diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-08-04 20:11:28 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-08-04 20:11:28 -0700 |
commit | ae9b475ebed96afe51d6bcf10dc7aee9c8d89ed7 (patch) | |
tree | 374bf5821a03d717b35f0b91dcbdeaa2428e649d | |
parent | 79eb238c76782a59d51adf8a3dd7f6444245b475 (diff) | |
parent | d310d05f1225d1f6f2bf505255fdf593bfbb3051 (diff) |
Merge tag 'usb-3.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
Pull USB updates from Greg KH:
"Here is the big USB driver update for 3.17-rc1.
Loads of gadget driver changes in here, including some big file
movements to make things easier to manage over time. There's also the
usual xhci and uas driver updates, and a handful of other changes in
here. The changelog has the full details.
All of these have been in linux-next for a while"
* tag 'usb-3.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (211 commits)
USB: devio: fix issue with log flooding
uas: Log a warning when we cannot use uas because the hcd lacks streams
uas: Only complain about missing sg if all other checks succeed
xhci: Add missing checks for xhci_alloc_command failure
xhci: Rename Asrock P67 pci product-id to EJ168
xhci: Blacklist using streams on the Etron EJ168 controller
uas: Limit qdepth to 32 when connected over usb-2
uwb/whci: use correct structure type name in sizeof
usb-core bInterval quirk
USB: serial: ftdi_sio: Add support for new Xsens devices
USB: serial: ftdi_sio: Annotate the current Xsens PID assignments
usb: chipidea: debug: fix sparse non static symbol warnings
usb: ci_hdrc_imx doc: fsl,usbphy is required
usb: ci_hdrc_imx: Return -EINVAL for missing USB PHY
usb: core: allow zero packet flag for interrupt urbs
usb: lvstest: Fix sparse warnings generated by kbuild test bot
USB: core: hcd-pci: free IRQ before disabling PCI device when shutting down
phy: miphy365x: Represent each PHY channel as a DT subnode
phy: miphy365x: Provide support for the MiPHY356x Generic PHY
phy: miphy365x: Add Device Tree bindings for the MiPHY365x
...
267 files changed, 11203 insertions, 5803 deletions
diff --git a/Documentation/ABI/stable/sysfs-bus-usb b/Documentation/ABI/stable/sysfs-bus-usb index a6b685724740..e2bc700a6f9c 100644 --- a/Documentation/ABI/stable/sysfs-bus-usb +++ b/Documentation/ABI/stable/sysfs-bus-usb @@ -3,13 +3,13 @@ Date: May 2007 KernelVersion: 2.6.23 Contact: Alan Stern <stern@rowland.harvard.edu> Description: - If CONFIG_USB_PERSIST is set, then each USB device directory - will contain a file named power/persist. The file holds a - boolean value (0 or 1) indicating whether or not the - "USB-Persist" facility is enabled for the device. Since the - facility is inherently dangerous, it is disabled by default - for all devices except hubs. For more information, see - Documentation/usb/persist.txt. + USB device directories can contain a file named power/persist. + The file holds a boolean value (0 or 1) indicating whether or + not the "USB-Persist" facility is enabled for the device. For + hubs this facility is always enabled and their device + directories will not contain this file. + + For more information, see Documentation/usb/persist.txt. What: /sys/bus/usb/devices/.../power/autosuspend Date: March 2007 diff --git a/Documentation/ABI/testing/sysfs-bus-usb-lvstest b/Documentation/ABI/testing/sysfs-bus-usb-lvstest new file mode 100644 index 000000000000..aae68fc2d842 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-usb-lvstest @@ -0,0 +1,47 @@ +Link Layer Validation Device is a standard device for testing of Super +Speed Link Layer tests. These nodes are available in sysfs only when lvs +driver is bound with root hub device. + +What: /sys/bus/usb/devices/.../get_dev_desc +Date: March 2014 +Contact: Pratyush Anand <pratyush.anand@st.com> +Description: + Write to this node to issue "Get Device Descriptor" + for Link Layer Validation device. It is needed for TD.7.06. + +What: /sys/bus/usb/devices/.../u1_timeout +Date: March 2014 +Contact: Pratyush Anand <pratyush.anand@st.com> +Description: + Set "U1 timeout" for the downstream port where Link Layer + Validation device is connected. Timeout value must be between 0 + and 127. It is needed for TD.7.18, TD.7.19, TD.7.20 and TD.7.21. + +What: /sys/bus/usb/devices/.../u2_timeout +Date: March 2014 +Contact: Pratyush Anand <pratyush.anand@st.com> +Description: + Set "U2 timeout" for the downstream port where Link Layer + Validation device is connected. Timeout value must be between 0 + and 127. It is needed for TD.7.18, TD.7.19, TD.7.20 and TD.7.21. + +What: /sys/bus/usb/devices/.../hot_reset +Date: March 2014 +Contact: Pratyush Anand <pratyush.anand@st.com> +Description: + Write to this node to issue "Reset" for Link Layer Validation + device. It is needed for TD.7.29, TD.7.31, TD.7.34 and TD.7.35. + +What: /sys/bus/usb/devices/.../u3_entry +Date: March 2014 +Contact: Pratyush Anand <pratyush.anand@st.com> +Description: + Write to this node to issue "U3 entry" for Link Layer + Validation device. It is needed for TD.7.35 and TD.7.36. + +What: /sys/bus/usb/devices/.../u3_exit +Date: March 2014 +Contact: Pratyush Anand <pratyush.anand@st.com> +Description: + Write to this node to issue "U3 exit" for Link Layer + Validation device. It is needed for TD.7.36. diff --git a/Documentation/DocBook/gadget.tmpl b/Documentation/DocBook/gadget.tmpl index 2c425d70f7e2..641629221176 100644 --- a/Documentation/DocBook/gadget.tmpl +++ b/Documentation/DocBook/gadget.tmpl @@ -556,11 +556,11 @@ been converted to this framework. Near-term plans include converting all of them, except for "gadgetfs". </para> -!Edrivers/usb/gadget/f_acm.c -!Edrivers/usb/gadget/f_ecm.c -!Edrivers/usb/gadget/f_subset.c -!Edrivers/usb/gadget/f_obex.c -!Edrivers/usb/gadget/f_serial.c +!Edrivers/usb/gadget/function/f_acm.c +!Edrivers/usb/gadget/function/f_ecm.c +!Edrivers/usb/gadget/function/f_subset.c +!Edrivers/usb/gadget/function/f_obex.c +!Edrivers/usb/gadget/function/f_serial.c </sect1> diff --git a/Documentation/devicetree/bindings/phy/berlin-sata-phy.txt b/Documentation/devicetree/bindings/phy/berlin-sata-phy.txt new file mode 100644 index 000000000000..88f8c23384c0 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/berlin-sata-phy.txt @@ -0,0 +1,34 @@ +Berlin SATA PHY +--------------- + +Required properties: +- compatible: should be "marvell,berlin2q-sata-phy" +- address-cells: should be 1 +- size-cells: should be 0 +- phy-cells: from the generic PHY bindings, must be 1 +- reg: address and length of the register +- clocks: reference to the clock entry + +Sub-nodes: +Each PHY should be represented as a sub-node. + +Sub-nodes required properties: +- reg: the PHY number + +Example: + sata_phy: phy@f7e900a0 { + compatible = "marvell,berlin2q-sata-phy"; + reg = <0xf7e900a0 0x200>; + clocks = <&chip CLKID_SATA>; + #address-cells = <1>; + #size-cells = <0>; + #phy-cells = <1>; + + sata-phy@0 { + reg = <0>; + }; + + sata-phy@1 { + reg = <1>; + }; + }; diff --git a/Documentation/devicetree/bindings/phy/hix5hd2-phy.txt b/Documentation/devicetree/bindings/phy/hix5hd2-phy.txt new file mode 100644 index 000000000000..296168b74d24 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/hix5hd2-phy.txt @@ -0,0 +1,22 @@ +Hisilicon hix5hd2 SATA PHY +----------------------- + +Required properties: +- compatible: should be "hisilicon,hix5hd2-sata-phy" +- reg: offset and length of the PHY registers +- #phy-cells: must be 0 +Refer to phy/phy-bindings.txt for the generic PHY binding properties + +Optional Properties: +- hisilicon,peripheral-syscon: phandle of syscon used to control peripheral. +- hisilicon,power-reg: offset and bit number within peripheral-syscon, + register of controlling sata power supply. + +Example: + sata_phy: phy@f9900000 { + compatible = "hisilicon,hix5hd2-sata-phy"; + reg = <0xf9900000 0x10000>; + #phy-cells = <0>; + hisilicon,peripheral-syscon = <&peripheral_ctrl>; + hisilicon,power-reg = <0x8 10>; + }; diff --git a/Documentation/devicetree/bindings/phy/phy-bindings.txt b/Documentation/devicetree/bindings/phy/phy-bindings.txt index 8ae844fc0c60..2aa1840200ed 100644 --- a/Documentation/devicetree/bindings/phy/phy-bindings.txt +++ b/Documentation/devicetree/bindings/phy/phy-bindings.txt @@ -10,6 +10,10 @@ Required Properties: provider can use the values in cells to find the appropriate PHY. +Optional Properties: +phy-supply: Phandle to a regulator that provides power to the PHY. This + regulator will be managed during the PHY power on/off sequence. + For example: phys: phy { diff --git a/Documentation/devicetree/bindings/phy/phy-miphy365x.txt b/Documentation/devicetree/bindings/phy/phy-miphy365x.txt new file mode 100644 index 000000000000..42c880886cf7 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/phy-miphy365x.txt @@ -0,0 +1,76 @@ +STMicroelectronics STi MIPHY365x PHY binding +============================================ + +This binding describes a miphy device that is used to control PHY hardware +for SATA and PCIe. + +Required properties (controller (parent) node): +- compatible : Should be "st,miphy365x-phy" +- st,syscfg : Should be a phandle of the system configuration register group + which contain the SATA, PCIe mode setting bits + +Required nodes : A sub-node is required for each channel the controller + provides. Address range information including the usual + 'reg' and 'reg-names' properties are used inside these + nodes to describe the controller's topology. These nodes + are translated by the driver's .xlate() function. + +Required properties (port (child) node): +- #phy-cells : Should be 1 (See second example) + Cell after port phandle is device type from: + - MIPHY_TYPE_SATA + - MIPHY_TYPE_PCI +- reg : Address and length of register sets for each device in + "reg-names" +- reg-names : The names of the register addresses corresponding to the + registers filled in "reg": + - sata: For SATA devices + - pcie: For PCIe devices + - syscfg: To specify the syscfg based config register + +Optional properties (port (child) node): +- st,sata-gen : Generation of locally attached SATA IP. Expected values + are {1,2,3). If not supplied generation 1 hardware will + be expected +- st,pcie-tx-pol-inv : Bool property to invert the polarity PCIe Tx (Txn/Txp) +- st,sata-tx-pol-inv : Bool property to invert the polarity SATA Tx (Txn/Txp) + +Example: + + miphy365x_phy: miphy365x@fe382000 { + compatible = "st,miphy365x-phy"; + st,syscfg = <&syscfg_rear>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + phy_port0: port@fe382000 { + reg = <0xfe382000 0x100>, <0xfe394000 0x100>, <0x824 0x4>; + reg-names = "sata", "pcie", "syscfg"; + #phy-cells = <1>; + st,sata-gen = <3>; + }; + + phy_port1: port@fe38a000 { + reg = <0xfe38a000 0x100>, <0xfe804000 0x100>, <0x828 0x4>;; + reg-names = "sata", "pcie", "syscfg"; + #phy-cells = <1>; + st,pcie-tx-pol-inv; + }; + }; + +Specifying phy control of devices +================================= + +Device nodes should specify the configuration required in their "phys" +property, containing a phandle to the phy port node and a device type. + +Example: + +#include <dt-bindings/phy/phy-miphy365x.h> + + sata0: sata@fe380000 { + ... + phys = <&phy_port0 MIPHY_TYPE_SATA>; + ... + }; diff --git a/Documentation/devicetree/bindings/phy/qcom-apq8064-sata-phy.txt b/Documentation/devicetree/bindings/phy/qcom-apq8064-sata-phy.txt new file mode 100644 index 000000000000..952f6c96bab9 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/qcom-apq8064-sata-phy.txt @@ -0,0 +1,24 @@ +Qualcomm APQ8064 SATA PHY Controller +------------------------------------ + +SATA PHY nodes are defined to describe on-chip SATA Physical layer controllers. +Each SATA PHY controller should have its own node. + +Required properties: +- compatible: compatible list, contains "qcom,apq8064-sata-phy". +- reg: offset and length of the SATA PHY register set; +- #phy-cells: must be zero +- clocks: a list of phandles and clock-specifier pairs, one for each entry in + clock-names. +- clock-names: must be "cfg" for phy config clock. + +Example: + sata_phy: sata-phy@1b400000 { + compatible = "qcom,apq8064-sata-phy"; + reg = <0x1b400000 0x200>; + + clocks = <&gcc SATA_PHY_CFG_CLK>; + clock-names = "cfg"; + + #phy-cells = <0>; + }; diff --git a/Documentation/devicetree/bindings/phy/qcom-ipq806x-sata-phy.txt b/Documentation/devicetree/bindings/phy/qcom-ipq806x-sata-phy.txt new file mode 100644 index 000000000000..76bfbd056202 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/qcom-ipq806x-sata-phy.txt @@ -0,0 +1,23 @@ +Qualcomm IPQ806x SATA PHY Controller +------------------------------------ + +SATA PHY nodes are defined to describe on-chip SATA Physical layer controllers. +Each SATA PHY controller should have its own node. + +Required properties: +- compatible: compatible list, contains "qcom,ipq806x-sata-phy" +- reg: offset and length of the SATA PHY register set; +- #phy-cells: must be zero +- clocks: must be exactly one entry +- clock-names: must be "cfg" + +Example: + sata_phy: sata-phy@1b400000 { + compatible = "qcom,ipq806x-sata-phy"; + reg = <0x1b400000 0x200>; + + clocks = <&gcc SATA_PHY_CFG_CLK>; + clock-names = "cfg"; + + #phy-cells = <0>; + }; diff --git a/Documentation/devicetree/bindings/phy/samsung-phy.txt b/Documentation/devicetree/bindings/phy/samsung-phy.txt index 2049261d8c31..6099a5c94283 100644 --- a/Documentation/devicetree/bindings/phy/samsung-phy.txt +++ b/Documentation/devicetree/bindings/phy/samsung-phy.txt @@ -26,6 +26,7 @@ Samsung S5P/EXYNOS SoC series USB PHY Required properties: - compatible : should be one of the listed compatibles: + - "samsung,exynos3250-usb2-phy" - "samsung,exynos4210-usb2-phy" - "samsung,exynos4x12-usb2-phy" - "samsung,exynos5250-usb2-phy" @@ -46,6 +47,7 @@ and Exynos 4212) it is as follows: 1 - USB host ("host"), 2 - HSIC0 ("hsic0"), 3 - HSIC1 ("hsic1"), +Exynos3250 has only USB device phy available as phy 0. Exynos 4210 and Exynos 4212 use mode switching and require that mode switch register is supplied. diff --git a/Documentation/devicetree/bindings/phy/ti-phy.txt b/Documentation/devicetree/bindings/phy/ti-phy.txt index 9ce458f32945..305e3df3d9b1 100644 --- a/Documentation/devicetree/bindings/phy/ti-phy.txt +++ b/Documentation/devicetree/bindings/phy/ti-phy.txt @@ -9,15 +9,17 @@ Required properties: 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-pcie" - for pcie to support external clock for pcie and to + set PCS delay value. + e.g. PCIE PHY in DRA7x "ti,control-phy-usb2-dra7" - if it has power down register like USB2 PHY on DRA7 platform. "ti,control-phy-usb2-am437" - if it has power down register like USB2 PHY on AM437 platform. - - reg : Address and length of the register set for the device. It contains - 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. + - reg : register ranges as listed in the reg-names property + - reg-names: "otghs_control" for control-phy-otghs + "power", "pcie_pcs" and "control_sma" for control-phy-pcie + "power" for all other types omap_control_usb: omap-control-usb@4a002300 { compatible = "ti,control-phy-otghs"; @@ -56,8 +58,8 @@ usb2phy@4a0ad080 { TI PIPE3 PHY Required properties: - - compatible: Should be "ti,phy-usb3" or "ti,phy-pipe3-sata". - "ti,omap-usb3" is deprecated. + - compatible: Should be "ti,phy-usb3", "ti,phy-pipe3-sata" or + "ti,phy-pipe3-pcie. "ti,omap-usb3" is deprecated. - 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". @@ -69,10 +71,17 @@ Required properties: * "wkupclk" - wakeup clock. * "sysclk" - system clock. * "refclk" - reference clock. + * "dpll_ref" - external dpll ref clk + * "dpll_ref_m2" - external dpll ref clk + * "phy-div" - divider for apll + * "div-clk" - apll clock Optional properties: - ctrl-module : phandle of the control module used by PHY driver to power on the PHY. + - id: If there are multiple instance of the same type, in order to + differentiate between each instance "id" can be used (e.g., multi-lane PCIe + PHY). If "id" is not provided, it is set to default value of '1'. This is usually a subnode of ocp2scp to which it is connected. diff --git a/Documentation/devicetree/bindings/usb/ci-hdrc-imx.txt b/Documentation/devicetree/bindings/usb/ci-hdrc-imx.txt index a6a32cb7f777..1bae71e9ad47 100644 --- a/Documentation/devicetree/bindings/usb/ci-hdrc-imx.txt +++ b/Documentation/devicetree/bindings/usb/ci-hdrc-imx.txt @@ -4,6 +4,7 @@ Required properties: - compatible: Should be "fsl,imx27-usb" - reg: Should contain registers location and length - interrupts: Should contain controller interrupt +- fsl,usbphy: phandle of usb phy that connects to the port Recommended properies: - phy_type: the type of the phy connected to the core. Should be one @@ -12,7 +13,6 @@ Recommended properies: - dr_mode: One of "host", "peripheral" or "otg". Defaults to "otg" Optional properties: -- fsl,usbphy: phandler of usb phy that connects to the only one port - fsl,usbmisc: phandler of non-core register device, with one argument that indicate usb controller index - vbus-supply: regulator for vbus diff --git a/Documentation/devicetree/bindings/usb/nvidia,tegra20-usb-phy.txt b/Documentation/devicetree/bindings/usb/nvidia,tegra20-usb-phy.txt index ba797d3e6326..c9205fbf26e2 100644 --- a/Documentation/devicetree/bindings/usb/nvidia,tegra20-usb-phy.txt +++ b/Documentation/devicetree/bindings/usb/nvidia,tegra20-usb-phy.txt @@ -20,6 +20,12 @@ Required properties : Present if phy_type == utmi. - ulpi-link: The clock Tegra provides to the ULPI PHY (cdev2). Present if phy_type == ulpi, and ULPI link mode is in use. + - resets : Must contain an entry for each entry in reset-names. + See ../reset/reset.txt for details. + - reset-names : Must include the following entries: + - usb: The PHY's own reset signal. + - utmi-pads: The reset of the PHY containing the chip-wide UTMI pad control + registers. Required even if phy_type == ulpi. Required properties for phy_type == ulpi: - nvidia,phy-reset-gpio : The GPIO used to reset the PHY. @@ -56,6 +62,8 @@ Optional properties: host means this is a host controller peripheral means it is device controller otg means it can operate as either ("on the go") + - nvidia,has-utmi-pad-registers : boolean indicates whether this controller + contains the UTMI pad control registers common to all USB controllers. VBUS control (required for dr_mode == otg, optional for dr_mode == host): - vbus-supply: regulator for VBUS diff --git a/Documentation/devicetree/bindings/usb/usb-xhci.txt b/Documentation/devicetree/bindings/usb/usb-xhci.txt index 5a79377c6a96..86f67f0886bc 100644 --- a/Documentation/devicetree/bindings/usb/usb-xhci.txt +++ b/Documentation/devicetree/bindings/usb/usb-xhci.txt @@ -9,8 +9,9 @@ Required properties: register set for the device. - interrupts: one XHCI interrupt should be described here. -Optional property: +Optional properties: - clocks: reference to a clock + - usb3-lpm-capable: determines if platform is USB3 LPM capable Example: usb@f0931000 { diff --git a/Documentation/phy.txt b/Documentation/phy.txt index ebff6ee52441..c6594af94d25 100644 --- a/Documentation/phy.txt +++ b/Documentation/phy.txt @@ -53,10 +53,12 @@ unregister 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); +struct phy *phy_create(struct device *dev, struct device_node *node, + const struct phy_ops *ops, + struct phy_init_data *init_data); +struct phy *devm_phy_create(struct device *dev, struct device_node *node, + 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. diff --git a/Documentation/usb/hotplug.txt b/Documentation/usb/hotplug.txt index 6424b130485c..a80b0e9a7a0b 100644 --- a/Documentation/usb/hotplug.txt +++ b/Documentation/usb/hotplug.txt @@ -105,13 +105,13 @@ macros such as these, and use driver_info to store more information. A short example, for a driver that supports several specific USB devices and their quirks, might have a MODULE_DEVICE_TABLE like this: - static const struct usb_device_id mydriver_id_table = { + static const struct usb_device_id mydriver_id_table[] = { { USB_DEVICE (0x9999, 0xaaaa), driver_info: QUIRK_X }, { USB_DEVICE (0xbbbb, 0x8888), driver_info: QUIRK_Y|QUIRK_Z }, ... { } /* end with an all-zeroes entry */ - } - MODULE_DEVICE_TABLE (usb, mydriver_id_table); + }; + MODULE_DEVICE_TABLE(usb, mydriver_id_table); Most USB device drivers should pass these tables to the USB subsystem as well as to the module management subsystem. Not all, though: some driver @@ -134,7 +134,7 @@ something like this: if exposing any operations through usbdevfs: .ioctl = my_ioctl, */ - } + }; When the USB subsystem knows about a driver's device ID table, it's used when choosing drivers to probe(). The thread doing new device processing checks diff --git a/Documentation/usb/power-management.txt b/Documentation/usb/power-management.txt index 1392b61d6ebe..7b90fe034c4b 100644 --- a/Documentation/usb/power-management.txt +++ b/Documentation/usb/power-management.txt @@ -2,8 +2,27 @@ Alan Stern <stern@rowland.harvard.edu> - October 28, 2010 - + Last-updated: February 2014 + + + Contents: + --------- + * What is Power Management? + * What is Remote Wakeup? + * When is a USB device idle? + * Forms of dynamic PM + * The user interface for dynamic PM + * Changing the default idle-delay time + * Warnings + * The driver interface for Power Management + * The driver interface for autosuspend and autoresume + * Other parts of the driver interface + * Mutual exclusion + * Interaction between dynamic PM and system PM + * xHCI hardware link PM + * USB Port Power Control + * User Interface for Port Power Control + * Suggested Userspace Port Power Policy What is Power Management? @@ -516,3 +535,225 @@ relevant attribute files is usb2_hardware_lpm. driver will enable hardware LPM for the device. You can write y/Y/1 or n/N/0 to the file to enable/disable USB2 hardware LPM manually. This is for test purpose mainly. + + + USB Port Power Control + ---------------------- + +In addition to suspending endpoint devices and enabling hardware +controlled link power management, the USB subsystem also has the +capability to disable power to ports under some conditions. Power is +controlled through Set/ClearPortFeature(PORT_POWER) requests to a hub. +In the case of a root or platform-internal hub the host controller +driver translates PORT_POWER requests into platform firmware (ACPI) +method calls to set the port power state. For more background see the +Linux Plumbers Conference 2012 slides [1] and video [2]: + +Upon receiving a ClearPortFeature(PORT_POWER) request a USB port is +logically off, and may trigger the actual loss of VBUS to the port [3]. +VBUS may be maintained in the case where a hub gangs multiple ports into +a shared power well causing power to remain until all ports in the gang +are turned off. VBUS may also be maintained by hub ports configured for +a charging application. In any event a logically off port will lose +connection with its device, not respond to hotplug events, and not +respond to remote wakeup events*. + +WARNING: turning off a port may result in the inability to hot add a device. +Please see "User Interface for Port Power Control" for details. + +As far as the effect on the device itself it is similar to what a device +goes through during system suspend, i.e. the power session is lost. Any +USB device or driver that misbehaves with system suspend will be +similarly affected by a port power cycle event. For this reason the +implementation shares the same device recovery path (and honors the same +quirks) as the system resume path for the hub. + +[1]: http://dl.dropbox.com/u/96820575/sarah-sharp-lpt-port-power-off2-mini.pdf +[2]: http://linuxplumbers.ubicast.tv/videos/usb-port-power-off-kerneluserspace-api/ +[3]: USB 3.1 Section 10.12 +* wakeup note: if a device is configured to send wakeup events the port + power control implementation will block poweroff attempts on that + port. + + + User Interface for Port Power Control + ------------------------------------- + +The port power control mechanism uses the PM runtime system. Poweroff is +requested by clearing the power/pm_qos_no_power_off flag of the port device +(defaults to 1). If the port is disconnected it will immediately receive a +ClearPortFeature(PORT_POWER) request. Otherwise, it will honor the pm runtime +rules and require the attached child device and all descendants to be suspended. +This mechanism is dependent on the hub advertising port power switching in its +hub descriptor (wHubCharacteristics logical power switching mode field). + +Note, some interface devices/drivers do not support autosuspend. Userspace may +need to unbind the interface drivers before the usb_device will suspend. An +unbound interface device is suspended by default. When unbinding, be careful +to unbind interface drivers, not the driver of the parent usb device. Also, +leave hub interface drivers bound. If the driver for the usb device (not +interface) is unbound the kernel is no longer able to resume the device. If a +hub interface driver is unbound, control of its child ports is lost and all +attached child-devices will disconnect. A good rule of thumb is that if the +'driver/module' link for a device points to /sys/module/usbcore then unbinding +it will interfere with port power control. + +Example of the relevant files for port power control. Note, in this example +these files are relative to a usb hub device (prefix). + + prefix=/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1 + + attached child device + + hub port device + | + hub interface device + | | + v v v + $prefix/3-1:1.0/3-1-port1/device + + $prefix/3-1:1.0/3-1-port1/power/pm_qos_no_power_off + $prefix/3-1:1.0/3-1-port1/device/power/control + $prefix/3-1:1.0/3-1-port1/device/3-1.1:<intf0>/driver/unbind + $prefix/3-1:1.0/3-1-port1/device/3-1.1:<intf1>/driver/unbind + ... + $prefix/3-1:1.0/3-1-port1/device/3-1.1:<intfN>/driver/unbind + +In addition to these files some ports may have a 'peer' link to a port on +another hub. The expectation is that all superspeed ports have a +hi-speed peer. + +$prefix/3-1:1.0/3-1-port1/peer -> ../../../../usb2/2-1/2-1:1.0/2-1-port1 +../../../../usb2/2-1/2-1:1.0/2-1-port1/peer -> ../../../../usb3/3-1/3-1:1.0/3-1-port1 + +Distinct from 'companion ports', or 'ehci/xhci shared switchover ports' +peer ports are simply the hi-speed and superspeed interface pins that +are combined into a single usb3 connector. Peer ports share the same +ancestor XHCI device. + +While a superspeed port is powered off a device may downgrade its +connection and attempt to connect to the hi-speed pins. The +implementation takes steps to prevent this: + +1/ Port suspend is sequenced to guarantee that hi-speed ports are powered-off + before their superspeed peer is permitted to power-off. The implication is + that the setting pm_qos_no_power_off to zero on a superspeed port may not cause + the port to power-off until its highspeed peer has gone to its runtime suspend + state. Userspace must take care to order the suspensions if it wants to + guarantee that a superspeed port will power-off. + +2/ Port resume is sequenced to force a superspeed port to power-on prior to its + highspeed peer. + +3/ Port resume always triggers an attached child device to resume. After a + power session is lost the device may have been removed, or need reset. + Resuming the child device when the parent port regains power resolves those + states and clamps the maximum port power cycle frequency at the rate the child + device can suspend (autosuspend-delay) and resume (reset-resume latency). + +Sysfs files relevant for port power control: + <hubdev-portX>/power/pm_qos_no_power_off: + This writable flag controls the state of an idle port. + Once all children and descendants have suspended the + port may suspend/poweroff provided that + pm_qos_no_power_off is '0'. If pm_qos_no_power_off is + '1' the port will remain active/powered regardless of + the stats of descendants. Defaults to 1. + + <hubdev-portX>/power/runtime_status: + This file reflects whether the port is 'active' (power is on) + or 'suspended' (logically off). There is no indication to + userspace whether VBUS is still supplied. + + <hubdev-portX>/connect_type: + An advisory read-only flag to userspace indicating the + location and connection type of the port. It returns + one of four values 'hotplug', 'hardwired', 'not used', + and 'unknown'. All values, besides unknown, are set by + platform firmware. + + "hotplug" indicates an externally connectable/visible + port on the platform. Typically userspace would choose + to keep such a port powered to handle new device + connection events. + + "hardwired" refers to a port that is not visible but + connectable. Examples are internal ports for USB + bluetooth that can be disconnected via an external + switch or a port with a hardwired USB camera. It is + expected to be safe to allow these ports to suspend + provided pm_qos_no_power_off is coordinated with any + switch that gates connections. Userspace must arrange + for the device to be connected prior to the port + powering off, or to activate the port prior to enabling + connection via a switch. + + "not used" refers to an internal port that is expected + to never have a device connected to it. These may be + empty internal ports, or ports that are not physically + exposed on a platform. Considered safe to be + powered-off at all times. + + "unknown" means platform firmware does not provide + information for this port. Most commonly refers to + external hub ports which should be considered 'hotplug' + for policy decisions. + + NOTE1: since we are relying on the BIOS to get this ACPI + information correct, the USB port descriptions may be + missing or wrong. + + NOTE2: Take care in clearing pm_qos_no_power_off. Once + power is off this port will + not respond to new connect events. + + Once a child device is attached additional constraints are + applied before the port is allowed to poweroff. + + <child>/power/control: + Must be 'auto', and the port will not + power down until <child>/power/runtime_status + reflects the 'suspended' state. Default + value is controlled by child device driver. + + <child>/power/persist: + This defaults to '1' for most devices and indicates if + kernel can persist the device's configuration across a + power session loss (suspend / port-power event). When + this value is '0' (quirky devices), port poweroff is + disabled. + + <child>/driver/unbind: + Wakeup capable devices will block port poweroff. At + this time the only mechanism to clear the usb-internal + wakeup-capability for an interface device is to unbind + its driver. + +Summary of poweroff pre-requisite settings relative to a port device: + + echo 0 > power/pm_qos_no_power_off + echo 0 > peer/power/pm_qos_no_power_off # if it exists + echo auto > power/control # this is the default value + echo auto > <child>/power/control + echo 1 > <child>/power/persist # this is the default value + + Suggested Userspace Port Power Policy + ------------------------------------- + +As noted above userspace needs to be careful and deliberate about what +ports are enabled for poweroff. + +The default configuration is that all ports start with +power/pm_qos_no_power_off set to '1' causing ports to always remain +active. + +Given confidence in the platform firmware's description of the ports +(ACPI _PLD record for a port populates 'connect_type') userspace can +clear pm_qos_no_power_off for all 'not used' ports. The same can be +done for 'hardwired' ports provided poweroff is coordinated with any +connection switch for the port. + +A more aggressive userspace policy is to enable USB port power off for +all ports (set <hubdev-portX>/power/pm_qos_no_power_off to '0') when +some external factor indicates the user has stopped interacting with the +system. For example, a distro may want to enable power off all USB +ports when the screen blanks, and re-power them when the screen becomes +active. Smart phones and tablets may want to power off USB ports when +the user pushes the power button. diff --git a/arch/arm/boot/dts/tegra114.dtsi b/arch/arm/boot/dts/tegra114.dtsi index fdc559ab2db3..7da20ca633dd 100644 --- a/arch/arm/boot/dts/tegra114.dtsi +++ b/arch/arm/boot/dts/tegra114.dtsi @@ -657,6 +657,8 @@ <&tegra_car TEGRA114_CLK_PLL_U>, <&tegra_car TEGRA114_CLK_USBD>; clock-names = "reg", "pll_u", "utmi-pads"; + resets = <&tegra_car 22>, <&tegra_car 22>; + reset-names = "usb", "utmi-pads"; nvidia,hssync-start-delay = <0>; nvidia,idle-wait-delay = <17>; nvidia,elastic-limit = <16>; @@ -667,6 +669,7 @@ nvidia,hssquelch-level = <2>; nvidia,hsdiscon-level = <5>; nvidia,xcvr-hsslew = <12>; + nvidia,has-utmi-pad-registers; status = "disabled"; }; @@ -690,6 +693,8 @@ <&tegra_car TEGRA114_CLK_PLL_U>, <&tegra_car TEGRA114_CLK_USBD>; clock-names = "reg", "pll_u", "utmi-pads"; + resets = <&tegra_car 59>, <&tegra_car 22>; + reset-names = "usb", "utmi-pads"; nvidia,hssync-start-delay = <0>; nvidia,idle-wait-delay = <17>; nvidia,elastic-limit = <16>; diff --git a/arch/arm/boot/dts/tegra124.dtsi b/arch/arm/boot/dts/tegra124.dtsi index 6e6bc4e8185c..aa8753a7c211 100644 --- a/arch/arm/boot/dts/tegra124.dtsi +++ b/arch/arm/boot/dts/tegra124.dtsi @@ -613,6 +613,8 @@ <&tegra_car TEGRA124_CLK_PLL_U>, <&tegra_car TEGRA124_CLK_USBD>; clock-names = "reg", "pll_u", "utmi-pads"; + resets = <&tegra_car 59>, <&tegra_car 22>; + reset-names = "usb", "utmi-pads"; nvidia,hssync-start-delay = <0>; nvidia,idle-wait-delay = <17>; nvidia,elastic-limit = <16>; @@ -647,6 +649,8 @@ <&tegra_car TEGRA124_CLK_PLL_U>, <&tegra_car TEGRA124_CLK_USBD>; clock-names = "reg", "pll_u", "utmi-pads"; + resets = <&tegra_car 22>, <&tegra_car 22>; + reset-names = "usb", "utmi-pads"; nvidia,hssync-start-delay = <0>; nvidia,idle-wait-delay = <17>; nvidia,elastic-limit = <16>; @@ -657,6 +661,7 @@ nvidia,hssquelch-level = <2>; nvidia,hsdiscon-level = <5>; nvidia,xcvr-hsslew = <12>; + nvidia,has-utmi-pad-registers; status = "disabled"; }; @@ -681,6 +686,8 @@ <&tegra_car TEGRA124_CLK_PLL_U>, <&tegra_car TEGRA124_CLK_USBD>; clock-names = "reg", "pll_u", "utmi-pads"; + resets = <&tegra_car 58>, <&tegra_car 22>; + reset-names = "usb", "utmi-pads"; nvidia,hssync-start-delay = <0>; nvidia,idle-wait-delay = <17>; nvidia,elastic-limit = <16>; diff --git a/arch/arm/boot/dts/tegra20.dtsi b/arch/arm/boot/dts/tegra20.dtsi index a7ddf70df50b..935df8906f25 100644 --- a/arch/arm/boot/dts/tegra20.dtsi +++ b/arch/arm/boot/dts/tegra20.dtsi @@ -630,6 +630,8 @@ <&tegra_car TEGRA20_CLK_CLK_M>, <&tegra_car TEGRA20_CLK_USBD>; clock-names = "reg", "pll_u", "timer", "utmi-pads"; + resets = <&tegra_car 22>, <&tegra_car 22>; + reset-names = "usb", "utmi-pads"; nvidia,has-legacy-mode; nvidia,hssync-start-delay = <9>; nvidia,idle-wait-delay = <17>; @@ -638,6 +640,7 @@ nvidia,xcvr-setup = <9>; nvidia,xcvr-lsfslew = <1>; nvidia,xcvr-lsrslew = <1>; + nvidia,has-utmi-pad-registers; status = "disabled"; }; @@ -661,6 +664,8 @@ <&tegra_car TEGRA20_CLK_PLL_U>, <&tegra_car TEGRA20_CLK_CDEV2>; clock-names = "reg", "pll_u", "ulpi-link"; + resets = <&tegra_car 58>, <&tegra_car 22>; + reset-names = "usb", "utmi-pads"; status = "disabled"; }; @@ -685,6 +690,8 @@ <&tegra_car TEGRA20_CLK_CLK_M>, <&tegra_car TEGRA20_CLK_USBD>; clock-names = "reg", "pll_u", "timer", "utmi-pads"; + resets = <&tegra_car 59>, <&tegra_car 22>; + reset-names = "usb", "utmi-pads"; nvidia,hssync-start-delay = <9>; nvidia,idle-wait-delay = <17>; nvidia,elastic-limit = <16>; diff --git a/arch/arm/boot/dts/tegra30.dtsi b/arch/arm/boot/dts/tegra30.dtsi index dec4fc823901..54805ce5efe0 100644 --- a/arch/arm/boot/dts/tegra30.dtsi +++ b/arch/arm/boot/dts/tegra30.dtsi @@ -775,6 +775,8 @@ <&tegra_car TEGRA30_CLK_PLL_U>, <&tegra_car TEGRA30_CLK_USBD>; clock-names = "reg", "pll_u", "utmi-pads"; + resets = <&tegra_car 22>, <&tegra_car 22>; + reset-names = "usb", "utmi-pads"; nvidia,hssync-start-delay = <9>; nvidia,idle-wait-delay = <17>; nvidia,elastic-limit = <16>; @@ -786,6 +788,7 @@ nvidia,xcvr-hsslew = <32>; nvidia,hssquelch-level = <2>; nvidia,hsdiscon-level = <5>; + nvidia,has-utmi-pad-registers; status = "disabled"; }; @@ -809,6 +812,8 @@ <&tegra_car TEGRA30_CLK_PLL_U>, <&tegra_car TEGRA30_CLK_USBD>; clock-names = "reg", "pll_u", "utmi-pads"; + resets = <&tegra_car 58>, <&tegra_car 22>; + reset-names = "usb", "utmi-pads"; nvidia,hssync-start-delay = <9>; nvidia,idle-wait-delay = <17>; nvidia,elastic-limit = <16>; @@ -843,6 +848,8 @@ <&tegra_car TEGRA30_CLK_PLL_U>, <&tegra_car TEGRA30_CLK_USBD>; clock-names = "reg", "pll_u", "utmi-pads"; + resets = <&tegra_car 59>, <&tegra_car 22>; + reset-names = "usb", "utmi-pads"; nvidia,hssync-start-delay = <0>; nvidia,idle-wait-delay = <17>; nvidia,elastic-limit = <16>; diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 64b98d242ea6..cc97c897945a 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -15,6 +15,13 @@ config GENERIC_PHY phy users can obtain reference to the PHY. All the users of this framework should select this config. +config PHY_BERLIN_SATA + tristate "Marvell Berlin SATA PHY driver" + depends on ARCH_BERLIN && HAS_IOMEM && OF + select GENERIC_PHY + help + Enable this to support the SATA PHY on Marvell Berlin SoCs. + config PHY_EXYNOS_MIPI_VIDEO tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver" depends on HAS_IOMEM @@ -27,10 +34,20 @@ config PHY_EXYNOS_MIPI_VIDEO config PHY_MVEBU_SATA def_bool y - depends on ARCH_KIRKWOOD || ARCH_DOVE || MACH_DOVE || MACH_KIRKWOOD + depends on ARCH_DOVE || MACH_DOVE || MACH_KIRKWOOD depends on OF select GENERIC_PHY +config PHY_MIPHY365X + tristate "STMicroelectronics MIPHY365X PHY driver for STiH41x series" + depends on ARCH_STI + depends on GENERIC_PHY + depends on HAS_IOMEM + depends on OF + help + Enable this to support the miphy transceiver (for SATA/PCIE) + that is part of STMicroelectronics STiH41x SoC series. + config OMAP_CONTROL_PHY tristate "OMAP CONTROL PHY Driver" depends on ARCH_OMAP2PLUS || COMPILE_TEST @@ -109,6 +126,14 @@ config PHY_EXYNOS5250_SATA SATA 3.0 Gb/s, SATA 6.0 Gb/s speeds. It supports one SATA host port to accept one SATA device. +config PHY_HIX5HD2_SATA + tristate "HIX5HD2 SATA PHY Driver" + depends on ARCH_HIX5HD2 && OF && HAS_IOMEM + select GENERIC_PHY + select MFD_SYSCON + help + Support for SATA PHY on Hisilicon hix5hd2 Soc. + config PHY_SUN4I_USB tristate "Allwinner sunxi SoC USB PHY driver" depends on ARCH_SUNXI && HAS_IOMEM && OF @@ -124,50 +149,39 @@ config PHY_SUN4I_USB config PHY_SAMSUNG_USB2 tristate "Samsung USB 2.0 PHY driver" depends on HAS_IOMEM + depends on USB_EHCI_EXYNOS || USB_OHCI_EXYNOS || USB_DWC2 select GENERIC_PHY select MFD_SYSCON + default ARCH_EXYNOS help Enable this to support the Samsung USB 2.0 PHY driver for Samsung - SoCs. This driver provides the interface for USB 2.0 PHY. Support for - particular SoCs has to be enabled in addition to this driver. Number - and type of supported phys depends on the SoC. + SoCs. This driver provides the interface for USB 2.0 PHY. Support + for particular PHYs will be enabled based on the SoC type in addition + to this driver. config PHY_EXYNOS4210_USB2 - bool "Support for Exynos 4210" + bool depends on PHY_SAMSUNG_USB2 - depends on CPU_EXYNOS4210 - help - Enable USB PHY support for Exynos 4210. This option requires that - Samsung USB 2.0 PHY driver is enabled and means that support for this - particular SoC is compiled in the driver. In case of Exynos 4210 four - phys are available - device, host, HSIC0 and HSIC1. + default CPU_EXYNOS4210 config PHY_EXYNOS4X12_USB2 - bool "Support for Exynos 4x12" + bool depends on PHY_SAMSUNG_USB2 - depends on (SOC_EXYNOS4212 || SOC_EXYNOS4412) - help - Enable USB PHY support for Exynos 4x12. This option requires that - Samsung USB 2.0 PHY driver is enabled and means that support for this - particular SoC is compiled in the driver. In case of Exynos 4x12 four - phys are available - device, host, HSIC0 and HSIC1. + default SOC_EXYNOS3250 || SOC_EXYNOS4212 || SOC_EXYNOS4412 config PHY_EXYNOS5250_USB2 - bool "Support for Exynos 5250" + bool depends on PHY_SAMSUNG_USB2 - depends on SOC_EXYNOS5250 - help - Enable USB PHY support for Exynos 5250. This option requires that - Samsung USB 2.0 PHY driver is enabled and means that support for this - particular SoC is compiled in the driver. In case of Exynos 5250 four - phys are available - device, host, HSIC0 and HSIC. + default SOC_EXYNOS5250 || SOC_EXYNOS5420 config PHY_EXYNOS5_USBDRD tristate "Exynos5 SoC series USB DRD PHY driver" depends on ARCH_EXYNOS5 && OF depends on HAS_IOMEM + depends on USB_DWC3_EXYNOS select GENERIC_PHY select MFD_SYSCON + default y help Enable USB DRD PHY support for Exynos 5 SoC series. This driver provides PHY interface for USB 3.0 DRD controller @@ -180,4 +194,18 @@ config PHY_XGENE help This option enables support for APM X-Gene SoC multi-purpose PHY. +config PHY_QCOM_APQ8064_SATA + tristate "Qualcomm APQ8064 SATA SerDes/PHY driver" + depends on ARCH_QCOM + depends on HAS_IOMEM + depends on OF + select GENERIC_PHY + +config PHY_QCOM_IPQ806X_SATA + tristate "Qualcomm IPQ806x SATA SerDes/PHY driver" + depends on ARCH_QCOM + depends on HAS_IOMEM + depends on OF + select GENERIC_PHY + endmenu diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index b4f1d5770601..971ad0aac388 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -3,15 +3,18 @@ # obj-$(CONFIG_GENERIC_PHY) += phy-core.o +obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o +obj-$(CONFIG_PHY_MIPHY365X) += phy-miphy365x.o obj-$(CONFIG_OMAP_CONTROL_PHY) += phy-omap-control.o obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o obj-$(CONFIG_PHY_EXYNOS5250_SATA) += phy-exynos5250-sata.o +obj-$(CONFIG_PHY_HIX5HD2_SATA) += phy-hix5hd2-sata.o obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o obj-$(CONFIG_PHY_SAMSUNG_USB2) += phy-exynos-usb2.o phy-exynos-usb2-y += phy-samsung-usb2.o @@ -20,3 +23,5 @@ phy-exynos-usb2-$(CONFIG_PHY_EXYNOS4X12_USB2) += phy-exynos4x12-usb2.o phy-exynos-usb2-$(CONFIG_PHY_EXYNOS5250_USB2) += phy-exynos5250-usb2.o obj-$(CONFIG_PHY_EXYNOS5_USBDRD) += phy-exynos5-usbdrd.o obj-$(CONFIG_PHY_XGENE) += phy-xgene.o +obj-$(CONFIG_PHY_QCOM_APQ8064_SATA) += phy-qcom-apq8064-sata.o +obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o diff --git a/drivers/phy/phy-bcm-kona-usb2.c b/drivers/phy/phy-bcm-kona-usb2.c index e94f5a6a5645..894fe74c1e44 100644 --- a/drivers/phy/phy-bcm-kona-usb2.c +++ b/drivers/phy/phy-bcm-kona-usb2.c @@ -117,7 +117,7 @@ static int bcm_kona_usb2_probe(struct platform_device *pdev) platform_set_drvdata(pdev, phy); - gphy = devm_phy_create(dev, &ops, NULL); + gphy = devm_phy_create(dev, NULL, &ops, NULL); if (IS_ERR(gphy)) return PTR_ERR(gphy); diff --git a/drivers/phy/phy-berlin-sata.c b/drivers/phy/phy-berlin-sata.c new file mode 100644 index 000000000000..5c3a0424aeb4 --- /dev/null +++ b/drivers/phy/phy-berlin-sata.c @@ -0,0 +1,284 @@ +/* + * Marvell Berlin SATA PHY driver + * + * Copyright (C) 2014 Marvell Technology Group Ltd. + * + * Antoine Ténart <antoine.tenart@free-electrons.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/phy/phy.h> +#include <linux/io.h> +#include <linux/platform_device.h> + +#define HOST_VSA_ADDR 0x0 +#define HOST_VSA_DATA 0x4 +#define PORT_SCR_CTL 0x2c +#define PORT_VSR_ADDR 0x78 +#define PORT_VSR_DATA 0x7c + +#define CONTROL_REGISTER 0x0 +#define MBUS_SIZE_CONTROL 0x4 + +#define POWER_DOWN_PHY0 BIT(6) +#define POWER_DOWN_PHY1 BIT(14) +#define MBUS_WRITE_REQUEST_SIZE_128 (BIT(2) << 16) +#define MBUS_READ_REQUEST_SIZE_128 (BIT(2) << 19) + +#define PHY_BASE 0x200 + +/* register 0x01 */ +#define REF_FREF_SEL_25 BIT(0) +#define PHY_MODE_SATA (0x0 << 5) + +/* register 0x02 */ +#define USE_MAX_PLL_RATE BIT(12) + +/* register 0x23 */ +#define DATA_BIT_WIDTH_10 (0x0 << 10) +#define DATA_BIT_WIDTH_20 (0x1 << 10) +#define DATA_BIT_WIDTH_40 (0x2 << 10) + +/* register 0x25 */ +#define PHY_GEN_MAX_1_5 (0x0 << 10) +#define PHY_GEN_MAX_3_0 (0x1 << 10) +#define PHY_GEN_MAX_6_0 (0x2 << 10) + +struct phy_berlin_desc { + struct phy *phy; + u32 power_bit; + unsigned index; +}; + +struct phy_berlin_priv { + void __iomem *base; + spinlock_t lock; + struct clk *clk; + struct phy_berlin_desc **phys; + unsigned nphys; +}; + +static inline void phy_berlin_sata_reg_setbits(void __iomem *ctrl_reg, u32 reg, + u32 mask, u32 val) +{ + u32 regval; + + /* select register */ + writel(PHY_BASE + reg, ctrl_reg + PORT_VSR_ADDR); + + /* set bits */ + regval = readl(ctrl_reg + PORT_VSR_DATA); + regval &= ~mask; + regval |= val; + writel(regval, ctrl_reg + PORT_VSR_DATA); +} + +static int phy_berlin_sata_power_on(struct phy *phy) +{ + struct phy_berlin_desc *desc = phy_get_drvdata(phy); + struct phy_berlin_priv *priv = dev_get_drvdata(phy->dev.parent); + void __iomem *ctrl_reg = priv->base + 0x60 + (desc->index * 0x80); + int ret = 0; + u32 regval; + + clk_prepare_enable(priv->clk); + + spin_lock(&priv->lock); + + /* Power on PHY */ + writel(CONTROL_REGISTER, priv->base + HOST_VSA_ADDR); + regval = readl(priv->base + HOST_VSA_DATA); + regval &= ~desc->power_bit; + writel(regval, priv->base + HOST_VSA_DATA); + + /* Configure MBus */ + writel(MBUS_SIZE_CONTROL, priv->base + HOST_VSA_ADDR); + regval = readl(priv->base + HOST_VSA_DATA); + regval |= MBUS_WRITE_REQUEST_SIZE_128 | MBUS_READ_REQUEST_SIZE_128; + writel(regval, priv->base + HOST_VSA_DATA); + + /* set PHY mode and ref freq to 25 MHz */ + phy_berlin_sata_reg_setbits(ctrl_reg, 0x1, 0xff, + REF_FREF_SEL_25 | PHY_MODE_SATA); + + /* set PHY up to 6 Gbps */ + phy_berlin_sata_reg_setbits(ctrl_reg, 0x25, 0xc00, PHY_GEN_MAX_6_0); + + /* set 40 bits width */ + phy_berlin_sata_reg_setbits(ctrl_reg, 0x23, 0xc00, DATA_BIT_WIDTH_40); + + /* use max pll rate */ + phy_berlin_sata_reg_setbits(ctrl_reg, 0x2, 0x0, USE_MAX_PLL_RATE); + + /* set Gen3 controller speed */ + regval = readl(ctrl_reg + PORT_SCR_CTL); + regval &= ~GENMASK(7, 4); + regval |= 0x30; + writel(regval, ctrl_reg + PORT_SCR_CTL); + + spin_unlock(&priv->lock); + + clk_disable_unprepare(priv->clk); + + return ret; +} + +static int phy_berlin_sata_power_off(struct phy *phy) +{ + struct phy_berlin_desc *desc = phy_get_drvdata(phy); + struct phy_berlin_priv *priv = dev_get_drvdata(phy->dev.parent); + u32 regval; + + clk_prepare_enable(priv->clk); + + spin_lock(&priv->lock); + + /* Power down PHY */ + writel(CONTROL_REGISTER, priv->base + HOST_VSA_ADDR); + regval = readl(priv->base + HOST_VSA_DATA); + regval |= desc->power_bit; + writel(regval, priv->base + HOST_VSA_DATA); + + spin_unlock(&priv->lock); + + clk_disable_unprepare(priv->clk); + + return 0; +} + +static struct phy *phy_berlin_sata_phy_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct phy_berlin_priv *priv = dev_get_drvdata(dev); + int i; + + if (WARN_ON(args->args[0] >= priv->nphys)) + return ERR_PTR(-ENODEV); + + for (i = 0; i < priv->nphys; i++) { + if (priv->phys[i]->index == args->args[0]) + break; + } + + if (i == priv->nphys) + return ERR_PTR(-ENODEV); + + return priv->phys[i]->phy; +} + +static struct phy_ops phy_berlin_sata_ops = { + .power_on = phy_berlin_sata_power_on, + .power_off = phy_berlin_sata_power_off, + .owner = THIS_MODULE, +}; + +static u32 phy_berlin_power_down_bits[] = { + POWER_DOWN_PHY0, + POWER_DOWN_PHY1, +}; + +static int phy_berlin_sata_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *child; + struct phy *phy; + struct phy_provider *phy_provider; + struct phy_berlin_priv *priv; + struct resource *res; + int i = 0; + u32 phy_id; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + + priv->base = devm_ioremap(dev, res->start, resource_size(res)); + if (!priv->base) + return -ENOMEM; + + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) + return PTR_ERR(priv->clk); + + priv->nphys = of_get_child_count(dev->of_node); + if (priv->nphys == 0) + return -ENODEV; + + priv->phys = devm_kzalloc(dev, priv->nphys * sizeof(*priv->phys), + GFP_KERNEL); + if (!priv->phys) + return -ENOMEM; + + dev_set_drvdata(dev, priv); + spin_lock_init(&priv->lock); + + for_each_available_child_of_node(dev->of_node, child) { + struct phy_berlin_desc *phy_desc; + + if (of_property_read_u32(child, "reg", &phy_id)) { + dev_err(dev, "missing reg property in node %s\n", + child->name); + return -EINVAL; + } + + if (phy_id >= ARRAY_SIZE(phy_berlin_power_down_bits)) { + dev_err(dev, "invalid reg in node %s\n", child->name); + return -EINVAL; + } + + phy_desc = devm_kzalloc(dev, sizeof(*phy_desc), GFP_KERNEL); + if (!phy_desc) + return -ENOMEM; + + phy = devm_phy_create(dev, NULL, &phy_berlin_sata_ops, NULL); + if (IS_ERR(phy)) { + dev_err(dev, "failed to create PHY %d\n", phy_id); + return PTR_ERR(phy); + } + + phy_desc->phy = phy; + phy_desc->power_bit = phy_berlin_power_down_bits[phy_id]; + phy_desc->index = phy_id; + phy_set_drvdata(phy, phy_desc); + + priv->phys[i++] = phy_desc; + + /* Make sure the PHY is off */ + phy_berlin_sata_power_off(phy); + } + + phy_provider = + devm_of_phy_provider_register(dev, phy_berlin_sata_phy_xlate); + if (IS_ERR(phy_provider)) + return PTR_ERR(phy_provider); + + return 0; +} + +static const struct of_device_id phy_berlin_sata_of_match[] = { + { .compatible = "marvell,berlin2q-sata-phy" }, + { }, +}; + +static struct platform_driver phy_berlin_sata_driver = { + .probe = phy_berlin_sata_probe, + .driver = { + .name = "phy-berlin-sata", + .owner = THIS_MODULE, + .of_match_table = phy_berlin_sata_of_match, + }, +}; +module_platform_driver(phy_berlin_sata_driver); + +MODULE_DESCRIPTION("Marvell Berlin SATA PHY driver"); +MODULE_AUTHOR("Antoine Ténart <antoine.tenart@free-electrons.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index 49c446530101..ff5eec5af817 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -21,6 +21,7 @@ #include <linux/phy/phy.h> #include <linux/idr.h> #include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> static struct class *phy_class; static DEFINE_MUTEX(phy_provider_mutex); @@ -86,10 +87,15 @@ static struct phy *phy_lookup(struct device *device, const char *port) static struct phy_provider *of_phy_provider_lookup(struct device_node *node) { struct phy_provider *phy_provider; + struct device_node *child; list_for_each_entry(phy_provider, &phy_provider_list, list) { if (phy_provider->dev->of_node == node) return phy_provider; + + for_each_child_of_node(phy_provider->dev->of_node, child) + if (child == node) + return phy_provider; } return ERR_PTR(-EPROBE_DEFER); @@ -226,6 +232,12 @@ int phy_power_on(struct phy *phy) if (!phy) return 0; + if (phy->pwr) { + ret = regulator_enable(phy->pwr); + if (ret) + return ret; + } + ret = phy_pm_runtime_get_sync(phy); if (ret < 0 && ret != -ENOTSUPP) return ret; @@ -247,6 +259,8 @@ int phy_power_on(struct phy *phy) out: mutex_unlock(&phy->mutex); phy_pm_runtime_put_sync(phy); + if (phy->pwr) + regulator_disable(phy->pwr); return ret; } @@ -272,6 +286,9 @@ int phy_power_off(struct phy *phy) mutex_unlock(&phy->mutex); phy_pm_runtime_put(phy); + if (phy->pwr) + regulator_disable(phy->pwr); + return 0; } EXPORT_SYMBOL_GPL(phy_power_off); @@ -398,13 +415,20 @@ struct phy *of_phy_simple_xlate(struct device *dev, struct of_phandle_args struct phy *phy; struct class_dev_iter iter; struct device_node *node = dev->of_node; + struct device_node *child; 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) + if (node != phy->dev.of_node) { + for_each_child_of_node(node, child) { + if (child == phy->dev.of_node) + goto phy_found; + } continue; + } +phy_found: class_dev_iter_exit(&iter); return phy; } @@ -562,13 +586,15 @@ EXPORT_SYMBOL_GPL(devm_of_phy_get); /** * phy_create() - create a new phy * @dev: device that is creating the new phy + * @node: device node of the 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) +struct phy *phy_create(struct device *dev, struct device_node *node, + const struct phy_ops *ops, + struct phy_init_data *init_data) { int ret; int id; @@ -588,12 +614,22 @@ struct phy *phy_create(struct device *dev, const struct phy_ops *ops, goto free_phy; } + /* phy-supply */ + phy->pwr = regulator_get_optional(dev, "phy"); + if (IS_ERR(phy->pwr)) { + if (PTR_ERR(phy->pwr) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto free_ida; + } + phy->pwr = NULL; + } + 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->dev.of_node = node ?: dev->of_node; phy->id = id; phy->ops = ops; phy->init_data = init_data; @@ -617,6 +653,9 @@ put_dev: put_device(&phy->dev); /* calls phy_release() which frees resources */ return ERR_PTR(ret); +free_ida: + ida_simple_remove(&phy_ida, phy->id); + free_phy: kfree(phy); return ERR_PTR(ret); @@ -626,6 +665,7 @@ EXPORT_SYMBOL_GPL(phy_create); /** * devm_phy_create() - create a new phy * @dev: device that is creating the new phy + * @node: device node of the phy * @ops: function pointers for performing phy operations * @init_data: contains the list of PHY consumers or NULL * @@ -634,8 +674,9 @@ EXPORT_SYMBOL_GPL(phy_create); * 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 *devm_phy_create(struct device *dev, struct device_node *node, + const struct phy_ops *ops, + struct phy_init_data *init_data) { struct phy **ptr, *phy; @@ -643,7 +684,7 @@ struct phy *devm_phy_create(struct device *dev, const struct phy_ops *ops, if (!ptr) return ERR_PTR(-ENOMEM); - phy = phy_create(dev, ops, init_data); + phy = phy_create(dev, node, ops, init_data); if (!IS_ERR(phy)) { *ptr = phy; devres_add(dev, ptr); @@ -800,6 +841,7 @@ static void phy_release(struct device *dev) phy = to_phy(dev); dev_vdbg(dev, "releasing '%s'\n", dev_name(dev)); + regulator_put(phy->pwr); ida_simple_remove(&phy_ida, phy->id); kfree(phy); } diff --git a/drivers/phy/phy-exynos-dp-video.c b/drivers/phy/phy-exynos-dp-video.c index 0786fef842e7..8b3026e2af7f 100644 --- a/drivers/phy/phy-exynos-dp-video.c +++ b/drivers/phy/phy-exynos-dp-video.c @@ -9,6 +9,7 @@ * published by the Free Software Foundation. */ +#include <linux/err.h> #include <linux/io.h> #include <linux/kernel.h> #include <linux/module.h> @@ -76,7 +77,7 @@ static int exynos_dp_video_phy_probe(struct platform_device *pdev) if (IS_ERR(state->regs)) return PTR_ERR(state->regs); - phy = devm_phy_create(dev, &exynos_dp_video_phy_ops, NULL); + phy = devm_phy_create(dev, NULL, &exynos_dp_video_phy_ops, NULL); if (IS_ERR(phy)) { dev_err(dev, "failed to create Display Port PHY\n"); return PTR_ERR(phy); @@ -84,10 +85,8 @@ static int exynos_dp_video_phy_probe(struct platform_device *pdev) phy_set_drvdata(phy, state); phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - if (IS_ERR(phy_provider)) - return PTR_ERR(phy_provider); - return 0; + return PTR_ERR_OR_ZERO(phy_provider); } static const struct of_device_id exynos_dp_video_phy_of_match[] = { diff --git a/drivers/phy/phy-exynos-mipi-video.c b/drivers/phy/phy-exynos-mipi-video.c index ff026689358c..b55a92e12496 100644 --- a/drivers/phy/phy-exynos-mipi-video.c +++ b/drivers/phy/phy-exynos-mipi-video.c @@ -9,6 +9,7 @@ * published by the Free Software Foundation. */ +#include <linux/err.h> #include <linux/io.h> #include <linux/kernel.h> #include <linux/module.h> @@ -135,7 +136,7 @@ static int exynos_mipi_video_phy_probe(struct platform_device *pdev) spin_lock_init(&state->slock); for (i = 0; i < EXYNOS_MIPI_PHYS_NUM; i++) { - struct phy *phy = devm_phy_create(dev, + struct phy *phy = devm_phy_create(dev, NULL, &exynos_mipi_video_phy_ops, NULL); if (IS_ERR(phy)) { dev_err(dev, "failed to create PHY %d\n", i); @@ -149,10 +150,8 @@ static int exynos_mipi_video_phy_probe(struct platform_device *pdev) phy_provider = devm_of_phy_provider_register(dev, exynos_mipi_video_phy_xlate); - if (IS_ERR(phy_provider)) - return PTR_ERR(phy_provider); - return 0; + return PTR_ERR_OR_ZERO(phy_provider); } static const struct of_device_id exynos_mipi_video_phy_of_match[] = { diff --git a/drivers/phy/phy-exynos4x12-usb2.c b/drivers/phy/phy-exynos4x12-usb2.c index d92a7cc5698a..0b9de88579b1 100644 --- a/drivers/phy/phy-exynos4x12-usb2.c +++ b/drivers/phy/phy-exynos4x12-usb2.c @@ -67,6 +67,8 @@ #define EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ (0x5 << 0) #define EXYNOS_4x12_UPHYCLK_PHYFSEL_50MHZ (0x7 << 0) +#define EXYNOS_3250_UPHYCLK_REFCLKSEL (0x2 << 8) + #define EXYNOS_4x12_UPHYCLK_PHY0_ID_PULLUP BIT(3) #define EXYNOS_4x12_UPHYCLK_PHY0_COMMON_ON BIT(4) #define EXYNOS_4x12_UPHYCLK_PHY1_COMMON_ON BIT(7) @@ -86,13 +88,23 @@ #define EXYNOS_4x12_URSTCON_OTG_HLINK BIT(1) #define EXYNOS_4x12_URSTCON_OTG_PHYLINK BIT(2) #define EXYNOS_4x12_URSTCON_HOST_PHY BIT(3) +/* The following bit defines are presented in the + * order taken from the Exynos4412 reference manual. + * + * During experiments with the hardware and debugging + * it was determined that the hardware behaves contrary + * to the manual. + * + * The following bit values were chaned accordingly to the + * results of real hardware experiments. + */ #define EXYNOS_4x12_URSTCON_PHY1 BIT(4) -#define EXYNOS_4x12_URSTCON_HSIC0 BIT(5) -#define EXYNOS_4x12_URSTCON_HSIC1 BIT(6) +#define EXYNOS_4x12_URSTCON_HSIC0 BIT(6) +#define EXYNOS_4x12_URSTCON_HSIC1 BIT(5) #define EXYNOS_4x12_URSTCON_HOST_LINK_ALL BIT(7) -#define EXYNOS_4x12_URSTCON_HOST_LINK_P0 BIT(8) +#define EXYNOS_4x12_URSTCON_HOST_LINK_P0 BIT(10) #define EXYNOS_4x12_URSTCON_HOST_LINK_P1 BIT(9) -#define EXYNOS_4x12_URSTCON_HOST_LINK_P2 BIT(10) +#define EXYNOS_4x12_URSTCON_HOST_LINK_P2 BIT(8) /* Isolation, configured in the power management unit */ #define EXYNOS_4x12_USB_ISOL_OFFSET 0x704 @@ -187,7 +199,12 @@ static void exynos4x12_setup_clk(struct samsung_usb2_phy_instance *inst) clk = readl(drv->reg_phy + EXYNOS_4x12_UPHYCLK); clk &= ~EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK; + + if (drv->cfg->has_refclk_sel) + clk = EXYNOS_3250_UPHYCLK_REFCLKSEL; + clk |= drv->ref_reg_val << EXYNOS_4x12_UPHYCLK_PHYFSEL_OFFSET; + clk |= EXYNOS_4x12_UPHYCLK_PHY1_COMMON_ON; writel(clk, drv->reg_phy + EXYNOS_4x12_UPHYCLK); } @@ -198,27 +215,22 @@ static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on) u32 phypwr = 0; u32 rst; u32 pwr; - u32 mode = 0; - u32 switch_mode = 0; switch (inst->cfg->id) { case EXYNOS4x12_DEVICE: phypwr = EXYNOS_4x12_UPHYPWR_PHY0; rstbits = EXYNOS_4x12_URSTCON_PHY0; - mode = EXYNOS_4x12_MODE_SWITCH_DEVICE; - switch_mode = 1; break; case EXYNOS4x12_HOST: phypwr = EXYNOS_4x12_UPHYPWR_PHY1; - rstbits = EXYNOS_4x12_URSTCON_HOST_PHY; - mode = EXYNOS_4x12_MODE_SWITCH_HOST; - switch_mode = 1; + rstbits = EXYNOS_4x12_URSTCON_HOST_PHY | + EXYNOS_4x12_URSTCON_PHY1 | + EXYNOS_4x12_URSTCON_HOST_LINK_P0; break; case EXYNOS4x12_HSIC0: phypwr = EXYNOS_4x12_UPHYPWR_HSIC0; - rstbits = EXYNOS_4x12_URSTCON_HSIC1 | - EXYNOS_4x12_URSTCON_HOST_LINK_P0 | - EXYNOS_4x12_URSTCON_HOST_PHY; + rstbits = EXYNOS_4x12_URSTCON_HSIC0 | + EXYNOS_4x12_URSTCON_HOST_LINK_P1; break; case EXYNOS4x12_HSIC1: phypwr = EXYNOS_4x12_UPHYPWR_HSIC1; @@ -228,11 +240,6 @@ static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on) }; if (on) { - if (switch_mode) - regmap_update_bits(drv->reg_sys, - EXYNOS_4x12_MODE_SWITCH_OFFSET, - EXYNOS_4x12_MODE_SWITCH_MASK, mode); - pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR); pwr &= ~phypwr; writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR); @@ -253,41 +260,78 @@ static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on) } } -static int exynos4x12_power_on(struct samsung_usb2_phy_instance *inst) +static void exynos4x12_power_on_int(struct samsung_usb2_phy_instance *inst) { - struct samsung_usb2_phy_driver *drv = inst->drv; + if (inst->int_cnt++ > 0) + return; - inst->enabled = 1; exynos4x12_setup_clk(inst); - exynos4x12_phy_pwr(inst, 1); exynos4x12_isol(inst, 0); + exynos4x12_phy_pwr(inst, 1); +} + +static int exynos4x12_power_on(struct samsung_usb2_phy_instance *inst) +{ + struct samsung_usb2_phy_driver *drv = inst->drv; + + if (inst->ext_cnt++ > 0) + return 0; - /* Power on the device, as it is necessary for HSIC to work */ - if (inst->cfg->id == EXYNOS4x12_HSIC0) { - struct samsung_usb2_phy_instance *device = - &drv->instances[EXYNOS4x12_DEVICE]; - exynos4x12_phy_pwr(device, 1); - exynos4x12_isol(device, 0); + if (inst->cfg->id == EXYNOS4x12_HOST) { + regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET, + EXYNOS_4x12_MODE_SWITCH_MASK, + EXYNOS_4x12_MODE_SWITCH_HOST); + exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_DEVICE]); } + if (inst->cfg->id == EXYNOS4x12_DEVICE && drv->cfg->has_mode_switch) + regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET, + EXYNOS_4x12_MODE_SWITCH_MASK, + EXYNOS_4x12_MODE_SWITCH_DEVICE); + + if (inst->cfg->id == EXYNOS4x12_HSIC0 || + inst->cfg->id == EXYNOS4x12_HSIC1) { + exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_DEVICE]); + exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_HOST]); + } + + exynos4x12_power_on_int(inst); + return 0; } -static int exynos4x12_power_off(struct samsung_usb2_phy_instance *inst) +static void exynos4x12_power_off_int(struct samsung_usb2_phy_instance *inst) { - struct samsung_usb2_phy_driver *drv = inst->drv; - struct samsung_usb2_phy_instance *device = - &drv->instances[EXYNOS4x12_DEVICE]; + if (inst->int_cnt-- > 1) + return; - inst->enabled = 0; exynos4x12_isol(inst, 1); exynos4x12_phy_pwr(inst, 0); +} + +static int exynos4x12_power_off(struct samsung_usb2_phy_instance *inst) +{ + struct samsung_usb2_phy_driver *drv = inst->drv; + + if (inst->ext_cnt-- > 1) + return 0; + + if (inst->cfg->id == EXYNOS4x12_DEVICE && drv->cfg->has_mode_switch) + regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET, + EXYNOS_4x12_MODE_SWITCH_MASK, + EXYNOS_4x12_MODE_SWITCH_HOST); + + if (inst->cfg->id == EXYNOS4x12_HOST) + exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_DEVICE]); - if (inst->cfg->id == EXYNOS4x12_HSIC0 && !device->enabled) { - exynos4x12_isol(device, 1); - exynos4x12_phy_pwr(device, 0); + if (inst->cfg->id == EXYNOS4x12_HSIC0 || + inst->cfg->id == EXYNOS4x12_HSIC1) { + exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_DEVICE]); + exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_HOST]); } + exynos4x12_power_off_int(inst); + return 0; } @@ -320,6 +364,13 @@ static const struct samsung_usb2_common_phy exynos4x12_phys[] = { {}, }; +const struct samsung_usb2_phy_config exynos3250_usb2_phy_config = { + .has_refclk_sel = 1, + .num_phys = 1, + .phys = exynos4x12_phys, + .rate_to_clk = exynos4x12_rate_to_clk, +}; + const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config = { .has_mode_switch = 1, .num_phys = EXYNOS4x12_NUM_PHYS, diff --git a/drivers/phy/phy-exynos5-usbdrd.c b/drivers/phy/phy-exynos5-usbdrd.c index 76d862b2202f..b05302b09c9f 100644 --- a/drivers/phy/phy-exynos5-usbdrd.c +++ b/drivers/phy/phy-exynos5-usbdrd.c @@ -506,7 +506,7 @@ static struct phy_ops exynos5_usbdrd_phy_ops = { .owner = THIS_MODULE, }; -const struct exynos5_usbdrd_phy_config phy_cfg_exynos5[] = { +static const struct exynos5_usbdrd_phy_config phy_cfg_exynos5[] = { { .id = EXYNOS5_DRDPHY_UTMI, .phy_isol = exynos5_usbdrd_phy_isol, @@ -521,13 +521,13 @@ const struct exynos5_usbdrd_phy_config phy_cfg_exynos5[] = { }, }; -const struct exynos5_usbdrd_phy_drvdata exynos5420_usbdrd_phy = { +static const struct exynos5_usbdrd_phy_drvdata exynos5420_usbdrd_phy = { .phy_cfg = phy_cfg_exynos5, .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, .pmu_offset_usbdrd1_phy = EXYNOS5420_USBDRD1_PHY_CONTROL, }; -const struct exynos5_usbdrd_phy_drvdata exynos5250_usbdrd_phy = { +static const struct exynos5_usbdrd_phy_drvdata exynos5250_usbdrd_phy = { .phy_cfg = phy_cfg_exynos5, .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, }; @@ -635,7 +635,8 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev) dev_vdbg(dev, "Creating usbdrd_phy phy\n"); for (i = 0; i < EXYNOS5_DRDPHYS_NUM; i++) { - struct phy *phy = devm_phy_create(dev, &exynos5_usbdrd_phy_ops, + struct phy *phy = devm_phy_create(dev, NULL, + &exynos5_usbdrd_phy_ops, NULL); if (IS_ERR(phy)) { dev_err(dev, "Failed to create usbdrd_phy phy\n"); diff --git a/drivers/phy/phy-exynos5250-sata.c b/drivers/phy/phy-exynos5250-sata.c index 05689450f93b..19a679aca4ac 100644 --- a/drivers/phy/phy-exynos5250-sata.c +++ b/drivers/phy/phy-exynos5250-sata.c @@ -210,7 +210,7 @@ static int exynos_sata_phy_probe(struct platform_device *pdev) return ret; } - sata_phy->phy = devm_phy_create(dev, &exynos_sata_phy_ops, NULL); + sata_phy->phy = devm_phy_create(dev, NULL, &exynos_sata_phy_ops, NULL); if (IS_ERR(sata_phy->phy)) { clk_disable_unprepare(sata_phy->phyclk); dev_err(dev, "failed to create PHY\n"); diff --git a/drivers/phy/phy-exynos5250-usb2.c b/drivers/phy/phy-exynos5250-usb2.c index 94179afda951..1c139aa0d074 100644 --- a/drivers/phy/phy-exynos5250-usb2.c +++ b/drivers/phy/phy-exynos5250-usb2.c @@ -318,7 +318,6 @@ static int exynos5250_power_on(struct samsung_usb2_phy_instance *inst) break; } - inst->enabled = 1; exynos5250_isol(inst, 0); return 0; @@ -331,7 +330,6 @@ static int exynos5250_power_off(struct samsung_usb2_phy_instance *inst) u32 otg; u32 hsic; - inst->enabled = 0; exynos5250_isol(inst, 1); switch (inst->cfg->id) { diff --git a/drivers/phy/phy-hix5hd2-sata.c b/drivers/phy/phy-hix5hd2-sata.c new file mode 100644 index 000000000000..6a08fa5f81eb --- /dev/null +++ b/drivers/phy/phy-hix5hd2-sata.c @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2014 Linaro Ltd. + * Copyright (c) 2014 Hisilicon Limited. + * + * 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/delay.h> +#include <linux/io.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#define SATA_PHY0_CTLL 0xa0 +#define MPLL_MULTIPLIER_SHIFT 1 +#define MPLL_MULTIPLIER_MASK 0xfe +#define MPLL_MULTIPLIER_50M 0x3c +#define MPLL_MULTIPLIER_100M 0x1e +#define PHY_RESET BIT(0) +#define REF_SSP_EN BIT(9) +#define SSC_EN BIT(10) +#define REF_USE_PAD BIT(23) + +#define SATA_PORT_PHYCTL 0x174 +#define SPEED_MODE_MASK 0x6f0000 +#define HALF_RATE_SHIFT 16 +#define PHY_CONFIG_SHIFT 18 +#define GEN2_EN_SHIFT 21 +#define SPEED_CTRL BIT(20) + +#define SATA_PORT_PHYCTL1 0x148 +#define AMPLITUDE_MASK 0x3ffffe +#define AMPLITUDE_GEN3 0x68 +#define AMPLITUDE_GEN3_SHIFT 15 +#define AMPLITUDE_GEN2 0x56 +#define AMPLITUDE_GEN2_SHIFT 8 +#define AMPLITUDE_GEN1 0x56 +#define AMPLITUDE_GEN1_SHIFT 1 + +#define SATA_PORT_PHYCTL2 0x14c +#define PREEMPH_MASK 0x3ffff +#define PREEMPH_GEN3 0x20 +#define PREEMPH_GEN3_SHIFT 12 +#define PREEMPH_GEN2 0x15 +#define PREEMPH_GEN2_SHIFT 6 +#define PREEMPH_GEN1 0x5 +#define PREEMPH_GEN1_SHIFT 0 + +struct hix5hd2_priv { + void __iomem *base; + struct regmap *peri_ctrl; +}; + +enum phy_speed_mode { + SPEED_MODE_GEN1 = 0, + SPEED_MODE_GEN2 = 1, + SPEED_MODE_GEN3 = 2, +}; + +static int hix5hd2_sata_phy_init(struct phy *phy) +{ + struct hix5hd2_priv *priv = phy_get_drvdata(phy); + u32 val, data[2]; + int ret; + + if (priv->peri_ctrl) { + ret = of_property_read_u32_array(phy->dev.of_node, + "hisilicon,power-reg", + &data[0], 2); + if (ret) { + dev_err(&phy->dev, "Fail read hisilicon,power-reg\n"); + return ret; + } + + regmap_update_bits(priv->peri_ctrl, data[0], + BIT(data[1]), BIT(data[1])); + } + + /* reset phy */ + val = readl_relaxed(priv->base + SATA_PHY0_CTLL); + val &= ~(MPLL_MULTIPLIER_MASK | REF_USE_PAD); + val |= MPLL_MULTIPLIER_50M << MPLL_MULTIPLIER_SHIFT | + REF_SSP_EN | PHY_RESET; + writel_relaxed(val, priv->base + SATA_PHY0_CTLL); + msleep(20); + val &= ~PHY_RESET; + writel_relaxed(val, priv->base + SATA_PHY0_CTLL); + + val = readl_relaxed(priv->base + SATA_PORT_PHYCTL1); + val &= ~AMPLITUDE_MASK; + val |= AMPLITUDE_GEN3 << AMPLITUDE_GEN3_SHIFT | + AMPLITUDE_GEN2 << AMPLITUDE_GEN2_SHIFT | + AMPLITUDE_GEN1 << AMPLITUDE_GEN1_SHIFT; + writel_relaxed(val, priv->base + SATA_PORT_PHYCTL1); + + val = readl_relaxed(priv->base + SATA_PORT_PHYCTL2); + val &= ~PREEMPH_MASK; + val |= PREEMPH_GEN3 << PREEMPH_GEN3_SHIFT | + PREEMPH_GEN2 << PREEMPH_GEN2_SHIFT | + PREEMPH_GEN1 << PREEMPH_GEN1_SHIFT; + writel_relaxed(val, priv->base + SATA_PORT_PHYCTL2); + + /* ensure PHYCTRL setting takes effect */ + val = readl_relaxed(priv->base + SATA_PORT_PHYCTL); + val &= ~SPEED_MODE_MASK; + val |= SPEED_MODE_GEN1 << HALF_RATE_SHIFT | + SPEED_MODE_GEN1 << PHY_CONFIG_SHIFT | + SPEED_MODE_GEN1 << GEN2_EN_SHIFT | SPEED_CTRL; + writel_relaxed(val, priv->base + SATA_PORT_PHYCTL); + + msleep(20); + val &= ~SPEED_MODE_MASK; + val |= SPEED_MODE_GEN3 << HALF_RATE_SHIFT | + SPEED_MODE_GEN3 << PHY_CONFIG_SHIFT | + SPEED_MODE_GEN3 << GEN2_EN_SHIFT | SPEED_CTRL; + writel_relaxed(val, priv->base + SATA_PORT_PHYCTL); + + val &= ~(SPEED_MODE_MASK | SPEED_CTRL); + val |= SPEED_MODE_GEN2 << HALF_RATE_SHIFT | + SPEED_MODE_GEN2 << PHY_CONFIG_SHIFT | + SPEED_MODE_GEN2 << GEN2_EN_SHIFT; + writel_relaxed(val, priv->base + SATA_PORT_PHYCTL); + + return 0; +} + +static struct phy_ops hix5hd2_sata_phy_ops = { + .init = hix5hd2_sata_phy_init, + .owner = THIS_MODULE, +}; + +static int hix5hd2_sata_phy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct device *dev = &pdev->dev; + struct resource *res; + struct phy *phy; + struct hix5hd2_priv *priv; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->base = devm_ioremap(dev, res->start, resource_size(res)); + if (!priv->base) + return -ENOMEM; + + priv->peri_ctrl = syscon_regmap_lookup_by_phandle(dev->of_node, + "hisilicon,peripheral-syscon"); + if (IS_ERR(priv->peri_ctrl)) + priv->peri_ctrl = NULL; + + phy = devm_phy_create(dev, NULL, &hix5hd2_sata_phy_ops, NULL); + if (IS_ERR(phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(phy); + } + + phy_set_drvdata(phy, priv); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(phy_provider)) + return PTR_ERR(phy_provider); + + return 0; +} + +static const struct of_device_id hix5hd2_sata_phy_of_match[] = { + {.compatible = "hisilicon,hix5hd2-sata-phy",}, + { }, +}; +MODULE_DEVICE_TABLE(of, hix5hd2_sata_phy_of_match); + +static struct platform_driver hix5hd2_sata_phy_driver = { + .probe = hix5hd2_sata_phy_probe, + .driver = { + .name = "hix5hd2-sata-phy", + .owner = THIS_MODULE, + .of_match_table = hix5hd2_sata_phy_of_match, + } +}; +module_platform_driver(hix5hd2_sata_phy_driver); + +MODULE_AUTHOR("Jiancheng Xue <xuejiancheng@huawei.com>"); +MODULE_DESCRIPTION("HISILICON HIX5HD2 SATA PHY driver"); +MODULE_ALIAS("platform:hix5hd2-sata-phy"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-miphy365x.c b/drivers/phy/phy-miphy365x.c new file mode 100644 index 000000000000..e111baf187ce --- /dev/null +++ b/drivers/phy/phy-miphy365x.c @@ -0,0 +1,636 @@ +/* + * Copyright (C) 2014 STMicroelectronics – All Rights Reserved + * + * STMicroelectronics PHY driver MiPHY365 (for SoC STiH416). + * + * Authors: Alexandre Torgue <alexandre.torgue@st.com> + * Lee Jones <lee.jones@linaro.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + */ + +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/of_address.h> +#include <linux/clk.h> +#include <linux/phy/phy.h> +#include <linux/delay.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> + +#include <dt-bindings/phy/phy-miphy365x.h> + +#define HFC_TIMEOUT 100 + +#define SYSCFG_SELECT_SATA_MASK BIT(1) +#define SYSCFG_SELECT_SATA_POS 1 + +/* MiPHY365x register definitions */ +#define RESET_REG 0x00 +#define RST_PLL BIT(1) +#define RST_PLL_CAL BIT(2) +#define RST_RX BIT(4) +#define RST_MACRO BIT(7) + +#define STATUS_REG 0x01 +#define IDLL_RDY BIT(0) +#define PLL_RDY BIT(1) +#define DES_BIT_LOCK BIT(2) +#define DES_SYMBOL_LOCK BIT(3) + +#define CTRL_REG 0x02 +#define TERM_EN BIT(0) +#define PCI_EN BIT(2) +#define DES_BIT_LOCK_EN BIT(3) +#define TX_POL BIT(5) + +#define INT_CTRL_REG 0x03 + +#define BOUNDARY1_REG 0x10 +#define SPDSEL_SEL BIT(0) + +#define BOUNDARY3_REG 0x12 +#define TX_SPDSEL_GEN1_VAL 0 +#define TX_SPDSEL_GEN2_VAL 0x01 +#define TX_SPDSEL_GEN3_VAL 0x02 +#define RX_SPDSEL_GEN1_VAL 0 +#define RX_SPDSEL_GEN2_VAL (0x01 << 3) +#define RX_SPDSEL_GEN3_VAL (0x02 << 3) + +#define PCIE_REG 0x16 + +#define BUF_SEL_REG 0x20 +#define CONF_GEN_SEL_GEN3 0x02 +#define CONF_GEN_SEL_GEN2 0x01 +#define PD_VDDTFILTER BIT(4) + +#define TXBUF1_REG 0x21 +#define SWING_VAL 0x04 +#define SWING_VAL_GEN1 0x03 +#define PREEMPH_VAL (0x3 << 5) + +#define TXBUF2_REG 0x22 +#define TXSLEW_VAL 0x2 +#define TXSLEW_VAL_GEN1 0x4 + +#define RXBUF_OFFSET_CTRL_REG 0x23 + +#define RXBUF_REG 0x25 +#define SDTHRES_VAL 0x01 +#define EQ_ON3 (0x03 << 4) +#define EQ_ON1 (0x01 << 4) + +#define COMP_CTRL1_REG 0x40 +#define START_COMSR BIT(0) +#define START_COMZC BIT(1) +#define COMSR_DONE BIT(2) +#define COMZC_DONE BIT(3) +#define COMP_AUTO_LOAD BIT(4) + +#define COMP_CTRL2_REG 0x41 +#define COMP_2MHZ_RAT_GEN1 0x1e +#define COMP_2MHZ_RAT 0xf + +#define COMP_CTRL3_REG 0x42 +#define COMSR_COMP_REF 0x33 + +#define COMP_IDLL_REG 0x47 +#define COMZC_IDLL 0x2a + +#define PLL_CTRL1_REG 0x50 +#define PLL_START_CAL BIT(0) +#define BUF_EN BIT(2) +#define SYNCHRO_TX BIT(3) +#define SSC_EN BIT(6) +#define CONFIG_PLL BIT(7) + +#define PLL_CTRL2_REG 0x51 +#define BYPASS_PLL_CAL BIT(1) + +#define PLL_RAT_REG 0x52 + +#define PLL_SSC_STEP_MSB_REG 0x56 +#define PLL_SSC_STEP_MSB_VAL 0x03 + +#define PLL_SSC_STEP_LSB_REG 0x57 +#define PLL_SSC_STEP_LSB_VAL 0x63 + +#define PLL_SSC_PER_MSB_REG 0x58 +#define PLL_SSC_PER_MSB_VAL 0 + +#define PLL_SSC_PER_LSB_REG 0x59 +#define PLL_SSC_PER_LSB_VAL 0xf1 + +#define IDLL_TEST_REG 0x72 +#define START_CLK_HF BIT(6) + +#define DES_BITLOCK_REG 0x86 +#define BIT_LOCK_LEVEL 0x01 +#define BIT_LOCK_CNT_512 (0x03 << 5) + +struct miphy365x_phy { + struct phy *phy; + void __iomem *base; + bool pcie_tx_pol_inv; + bool sata_tx_pol_inv; + u32 sata_gen; + u64 ctrlreg; + u8 type; +}; + +struct miphy365x_dev { + struct device *dev; + struct regmap *regmap; + struct mutex miphy_mutex; + struct miphy365x_phy **phys; +}; + +/* + * These values are represented in Device tree. They are considered to be ABI + * and although they can be extended any existing values must not change. + */ +enum miphy_sata_gen { + SATA_GEN1 = 1, + SATA_GEN2, + SATA_GEN3 +}; + +static u8 rx_tx_spd[] = { + TX_SPDSEL_GEN1_VAL | RX_SPDSEL_GEN1_VAL, + TX_SPDSEL_GEN2_VAL | RX_SPDSEL_GEN2_VAL, + TX_SPDSEL_GEN3_VAL | RX_SPDSEL_GEN3_VAL +}; + +/* + * This function selects the system configuration, + * either two SATA, one SATA and one PCIe, or two PCIe lanes. + */ +static int miphy365x_set_path(struct miphy365x_phy *miphy_phy, + struct miphy365x_dev *miphy_dev) +{ + bool sata = (miphy_phy->type == MIPHY_TYPE_SATA); + + return regmap_update_bits(miphy_dev->regmap, + (unsigned int)miphy_phy->ctrlreg, + SYSCFG_SELECT_SATA_MASK, + sata << SYSCFG_SELECT_SATA_POS); +} + +static int miphy365x_init_pcie_port(struct miphy365x_phy *miphy_phy, + struct miphy365x_dev *miphy_dev) +{ + u8 val; + + if (miphy_phy->pcie_tx_pol_inv) { + /* Invert Tx polarity and clear pci_txdetect_pol bit */ + val = TERM_EN | PCI_EN | DES_BIT_LOCK_EN | TX_POL; + writeb_relaxed(val, miphy_phy->base + CTRL_REG); + writeb_relaxed(0x00, miphy_phy->base + PCIE_REG); + } + + return 0; +} + +static inline int miphy365x_hfc_not_rdy(struct miphy365x_phy *miphy_phy, + struct miphy365x_dev *miphy_dev) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(HFC_TIMEOUT); + u8 mask = IDLL_RDY | PLL_RDY; + u8 regval; + + do { + regval = readb_relaxed(miphy_phy->base + STATUS_REG); + if (!(regval & mask)) + return 0; + + usleep_range(2000, 2500); + } while (time_before(jiffies, timeout)); + + dev_err(miphy_dev->dev, "HFC ready timeout!\n"); + return -EBUSY; +} + +static inline int miphy365x_rdy(struct miphy365x_phy *miphy_phy, + struct miphy365x_dev *miphy_dev) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(HFC_TIMEOUT); + u8 mask = IDLL_RDY | PLL_RDY; + u8 regval; + + do { + regval = readb_relaxed(miphy_phy->base + STATUS_REG); + if ((regval & mask) == mask) + return 0; + + usleep_range(2000, 2500); + } while (time_before(jiffies, timeout)); + + dev_err(miphy_dev->dev, "PHY not ready timeout!\n"); + return -EBUSY; +} + +static inline void miphy365x_set_comp(struct miphy365x_phy *miphy_phy, + struct miphy365x_dev *miphy_dev) +{ + u8 val, mask; + + if (miphy_phy->sata_gen == SATA_GEN1) + writeb_relaxed(COMP_2MHZ_RAT_GEN1, + miphy_phy->base + COMP_CTRL2_REG); + else + writeb_relaxed(COMP_2MHZ_RAT, + miphy_phy->base + COMP_CTRL2_REG); + + if (miphy_phy->sata_gen != SATA_GEN3) { + writeb_relaxed(COMSR_COMP_REF, + miphy_phy->base + COMP_CTRL3_REG); + /* + * Force VCO current to value defined by address 0x5A + * and disable PCIe100Mref bit + * Enable auto load compensation for pll_i_bias + */ + writeb_relaxed(BYPASS_PLL_CAL, miphy_phy->base + PLL_CTRL2_REG); + writeb_relaxed(COMZC_IDLL, miphy_phy->base + COMP_IDLL_REG); + } + + /* + * Force restart compensation and enable auto load + * for Comzc_Tx, Comzc_Rx and Comsr on macro + */ + val = START_COMSR | START_COMZC | COMP_AUTO_LOAD; + writeb_relaxed(val, miphy_phy->base + COMP_CTRL1_REG); + + mask = COMSR_DONE | COMZC_DONE; + while ((readb_relaxed(miphy_phy->base + COMP_CTRL1_REG) & mask) != mask) + cpu_relax(); +} + +static inline void miphy365x_set_ssc(struct miphy365x_phy *miphy_phy, + struct miphy365x_dev *miphy_dev) +{ + u8 val; + + /* + * SSC Settings. SSC will be enabled through Link + * SSC Ampl. = 0.4% + * SSC Freq = 31KHz + */ + writeb_relaxed(PLL_SSC_STEP_MSB_VAL, + miphy_phy->base + PLL_SSC_STEP_MSB_REG); + writeb_relaxed(PLL_SSC_STEP_LSB_VAL, + miphy_phy->base + PLL_SSC_STEP_LSB_REG); + writeb_relaxed(PLL_SSC_PER_MSB_VAL, + miphy_phy->base + PLL_SSC_PER_MSB_REG); + writeb_relaxed(PLL_SSC_PER_LSB_VAL, + miphy_phy->base + PLL_SSC_PER_LSB_REG); + + /* SSC Settings complete */ + if (miphy_phy->sata_gen == SATA_GEN1) { + val = PLL_START_CAL | BUF_EN | SYNCHRO_TX | CONFIG_PLL; + writeb_relaxed(val, miphy_phy->base + PLL_CTRL1_REG); + } else { + val = SSC_EN | PLL_START_CAL | BUF_EN | SYNCHRO_TX | CONFIG_PLL; + writeb_relaxed(val, miphy_phy->base + PLL_CTRL1_REG); + } +} + +static int miphy365x_init_sata_port(struct miphy365x_phy *miphy_phy, + struct miphy365x_dev *miphy_dev) +{ + int ret; + u8 val; + + /* + * Force PHY macro reset, PLL calibration reset, PLL reset + * and assert Deserializer Reset + */ + val = RST_PLL | RST_PLL_CAL | RST_RX | RST_MACRO; + writeb_relaxed(val, miphy_phy->base + RESET_REG); + + if (miphy_phy->sata_tx_pol_inv) + writeb_relaxed(TX_POL, miphy_phy->base + CTRL_REG); + + /* + * Force macro1 to use rx_lspd, tx_lspd + * Force Rx_Clock on first I-DLL phase + * Force Des in HP mode on macro, rx_lspd, tx_lspd for Gen2/3 + */ + writeb_relaxed(SPDSEL_SEL, miphy_phy->base + BOUNDARY1_REG); + writeb_relaxed(START_CLK_HF, miphy_phy->base + IDLL_TEST_REG); + val = rx_tx_spd[miphy_phy->sata_gen]; + writeb_relaxed(val, miphy_phy->base + BOUNDARY3_REG); + + /* Wait for HFC_READY = 0 */ + ret = miphy365x_hfc_not_rdy(miphy_phy, miphy_dev); + if (ret) + return ret; + + /* Compensation Recalibration */ + miphy365x_set_comp(miphy_phy, miphy_dev); + + switch (miphy_phy->sata_gen) { + case SATA_GEN3: + /* + * TX Swing target 550-600mv peak to peak diff + * Tx Slew target 90-110ps rising/falling time + * Rx Eq ON3, Sigdet threshold SDTH1 + */ + val = PD_VDDTFILTER | CONF_GEN_SEL_GEN3; + writeb_relaxed(val, miphy_phy->base + BUF_SEL_REG); + val = SWING_VAL | PREEMPH_VAL; + writeb_relaxed(val, miphy_phy->base + TXBUF1_REG); + writeb_relaxed(TXSLEW_VAL, miphy_phy->base + TXBUF2_REG); + writeb_relaxed(0x00, miphy_phy->base + RXBUF_OFFSET_CTRL_REG); + val = SDTHRES_VAL | EQ_ON3; + writeb_relaxed(val, miphy_phy->base + RXBUF_REG); + break; + case SATA_GEN2: + /* + * conf gen sel=0x1 to program Gen2 banked registers + * VDDT filter ON + * Tx Swing target 550-600mV peak-to-peak diff + * Tx Slew target 90-110 ps rising/falling time + * RX Equalization ON1, Sigdet threshold SDTH1 + */ + writeb_relaxed(CONF_GEN_SEL_GEN2, + miphy_phy->base + BUF_SEL_REG); + writeb_relaxed(SWING_VAL, miphy_phy->base + TXBUF1_REG); + writeb_relaxed(TXSLEW_VAL, miphy_phy->base + TXBUF2_REG); + val = SDTHRES_VAL | EQ_ON1; + writeb_relaxed(val, miphy_phy->base + RXBUF_REG); + break; + case SATA_GEN1: + /* + * conf gen sel = 00b to program Gen1 banked registers + * VDDT filter ON + * Tx Swing target 500-550mV peak-to-peak diff + * Tx Slew target120-140 ps rising/falling time + */ + writeb_relaxed(PD_VDDTFILTER, miphy_phy->base + BUF_SEL_REG); + writeb_relaxed(SWING_VAL_GEN1, miphy_phy->base + TXBUF1_REG); + writeb_relaxed(TXSLEW_VAL_GEN1, miphy_phy->base + TXBUF2_REG); + break; + default: + break; + } + + /* Force Macro1 in partial mode & release pll cal reset */ + writeb_relaxed(RST_RX, miphy_phy->base + RESET_REG); + usleep_range(100, 150); + + miphy365x_set_ssc(miphy_phy, miphy_dev); + + /* Wait for phy_ready */ + ret = miphy365x_rdy(miphy_phy, miphy_dev); + if (ret) + return ret; + + /* + * Enable macro1 to use rx_lspd & tx_lspd + * Release Rx_Clock on first I-DLL phase on macro1 + * Assert deserializer reset + * des_bit_lock_en is set + * bit lock detection strength + * Deassert deserializer reset + */ + writeb_relaxed(0x00, miphy_phy->base + BOUNDARY1_REG); + writeb_relaxed(0x00, miphy_phy->base + IDLL_TEST_REG); + writeb_relaxed(RST_RX, miphy_phy->base + RESET_REG); + val = miphy_phy->sata_tx_pol_inv ? + (TX_POL | DES_BIT_LOCK_EN) : DES_BIT_LOCK_EN; + writeb_relaxed(val, miphy_phy->base + CTRL_REG); + + val = BIT_LOCK_CNT_512 | BIT_LOCK_LEVEL; + writeb_relaxed(val, miphy_phy->base + DES_BITLOCK_REG); + writeb_relaxed(0x00, miphy_phy->base + RESET_REG); + + return 0; +} + +static int miphy365x_init(struct phy *phy) +{ + struct miphy365x_phy *miphy_phy = phy_get_drvdata(phy); + struct miphy365x_dev *miphy_dev = dev_get_drvdata(phy->dev.parent); + int ret = 0; + + mutex_lock(&miphy_dev->miphy_mutex); + + ret = miphy365x_set_path(miphy_phy, miphy_dev); + if (ret) { + mutex_unlock(&miphy_dev->miphy_mutex); + return ret; + } + + /* Initialise Miphy for PCIe or SATA */ + if (miphy_phy->type == MIPHY_TYPE_PCIE) + ret = miphy365x_init_pcie_port(miphy_phy, miphy_dev); + else + ret = miphy365x_init_sata_port(miphy_phy, miphy_dev); + + mutex_unlock(&miphy_dev->miphy_mutex); + + return ret; +} + +int miphy365x_get_addr(struct device *dev, struct miphy365x_phy *miphy_phy, + int index) +{ + struct device_node *phynode = miphy_phy->phy->dev.of_node; + const char *name; + const __be32 *taddr; + int type = miphy_phy->type; + int ret; + + ret = of_property_read_string_index(phynode, "reg-names", index, &name); + if (ret) { + dev_err(dev, "no reg-names property not found\n"); + return ret; + } + + if (!strncmp(name, "syscfg", 6)) { + taddr = of_get_address(phynode, index, NULL, NULL); + if (!taddr) { + dev_err(dev, "failed to fetch syscfg address\n"); + return -EINVAL; + } + + miphy_phy->ctrlreg = of_translate_address(phynode, taddr); + if (miphy_phy->ctrlreg == OF_BAD_ADDR) { + dev_err(dev, "failed to translate syscfg address\n"); + return -EINVAL; + } + + return 0; + } + + if (!((!strncmp(name, "sata", 4) && type == MIPHY_TYPE_SATA) || + (!strncmp(name, "pcie", 4) && type == MIPHY_TYPE_PCIE))) + return 0; + + miphy_phy->base = of_iomap(phynode, index); + if (!miphy_phy->base) { + dev_err(dev, "Failed to map %s\n", phynode->full_name); + return -EINVAL; + } + + return 0; +} + +static struct phy *miphy365x_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct miphy365x_dev *miphy_dev = dev_get_drvdata(dev); + struct miphy365x_phy *miphy_phy = NULL; + struct device_node *phynode = args->np; + int ret, index; + + if (!of_device_is_available(phynode)) { + dev_warn(dev, "Requested PHY is disabled\n"); + return ERR_PTR(-ENODEV); + } + + if (args->args_count != 1) { + dev_err(dev, "Invalid number of cells in 'phy' property\n"); + return ERR_PTR(-EINVAL); + } + + for (index = 0; index < of_get_child_count(dev->of_node); index++) + if (phynode == miphy_dev->phys[index]->phy->dev.of_node) { + miphy_phy = miphy_dev->phys[index]; + break; + } + + if (!miphy_phy) { + dev_err(dev, "Failed to find appropriate phy\n"); + return ERR_PTR(-EINVAL); + } + + miphy_phy->type = args->args[0]; + + if (!(miphy_phy->type == MIPHY_TYPE_SATA || + miphy_phy->type == MIPHY_TYPE_PCIE)) { + dev_err(dev, "Unsupported device type: %d\n", miphy_phy->type); + return ERR_PTR(-EINVAL); + } + + /* Each port handles SATA and PCIE - third entry is always sysconf. */ + for (index = 0; index < 3; index++) { + ret = miphy365x_get_addr(dev, miphy_phy, index); + if (ret < 0) + return ERR_PTR(ret); + } + + return miphy_phy->phy; +} + +static struct phy_ops miphy365x_ops = { + .init = miphy365x_init, + .owner = THIS_MODULE, +}; + +static int miphy365x_of_probe(struct device_node *phynode, + struct miphy365x_phy *miphy_phy) +{ + of_property_read_u32(phynode, "st,sata-gen", &miphy_phy->sata_gen); + if (!miphy_phy->sata_gen) + miphy_phy->sata_gen = SATA_GEN1; + + miphy_phy->pcie_tx_pol_inv = + of_property_read_bool(phynode, "st,pcie-tx-pol-inv"); + + miphy_phy->sata_tx_pol_inv = + of_property_read_bool(phynode, "st,sata-tx-pol-inv"); + + return 0; +} + +static int miphy365x_probe(struct platform_device *pdev) +{ + struct device_node *child, *np = pdev->dev.of_node; + struct miphy365x_dev *miphy_dev; + struct phy_provider *provider; + struct phy *phy; + int chancount, port = 0; + int ret; + + miphy_dev = devm_kzalloc(&pdev->dev, sizeof(*miphy_dev), GFP_KERNEL); + if (!miphy_dev) + return -ENOMEM; + + chancount = of_get_child_count(np); + miphy_dev->phys = devm_kzalloc(&pdev->dev, sizeof(phy) * chancount, + GFP_KERNEL); + if (!miphy_dev->phys) + return -ENOMEM; + + miphy_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); + if (IS_ERR(miphy_dev->regmap)) { + dev_err(miphy_dev->dev, "No syscfg phandle specified\n"); + return PTR_ERR(miphy_dev->regmap); + } + + miphy_dev->dev = &pdev->dev; + + dev_set_drvdata(&pdev->dev, miphy_dev); + + mutex_init(&miphy_dev->miphy_mutex); + + for_each_child_of_node(np, child) { + struct miphy365x_phy *miphy_phy; + + miphy_phy = devm_kzalloc(&pdev->dev, sizeof(*miphy_phy), + GFP_KERNEL); + if (!miphy_phy) + return -ENOMEM; + + miphy_dev->phys[port] = miphy_phy; + + phy = devm_phy_create(&pdev->dev, child, &miphy365x_ops, NULL); + if (IS_ERR(phy)) { + dev_err(&pdev->dev, "failed to create PHY\n"); + return PTR_ERR(phy); + } + + miphy_dev->phys[port]->phy = phy; + + ret = miphy365x_of_probe(child, miphy_phy); + if (ret) + return ret; + + phy_set_drvdata(phy, miphy_dev->phys[port]); + port++; + } + + provider = devm_of_phy_provider_register(&pdev->dev, miphy365x_xlate); + if (IS_ERR(provider)) + return PTR_ERR(provider); + + return 0; +} + +static const struct of_device_id miphy365x_of_match[] = { + { .compatible = "st,miphy365x-phy", }, + { }, +}; +MODULE_DEVICE_TABLE(of, miphy365x_of_match); + +static struct platform_driver miphy365x_driver = { + .probe = miphy365x_probe, + .driver = { + .name = "miphy365x-phy", + .owner = THIS_MODULE, + .of_match_table = miphy365x_of_match, + } +}; +module_platform_driver(miphy365x_driver); + +MODULE_AUTHOR("Alexandre Torgue <alexandre.torgue@st.com>"); +MODULE_DESCRIPTION("STMicroelectronics miphy365x driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-mvebu-sata.c b/drivers/phy/phy-mvebu-sata.c index d70ecd6a1b3f..cc3c0e166daf 100644 --- a/drivers/phy/phy-mvebu-sata.c +++ b/drivers/phy/phy-mvebu-sata.c @@ -99,7 +99,7 @@ static int phy_mvebu_sata_probe(struct platform_device *pdev) if (IS_ERR(priv->clk)) return PTR_ERR(priv->clk); - phy = devm_phy_create(&pdev->dev, &phy_mvebu_sata_ops, NULL); + phy = devm_phy_create(&pdev->dev, NULL, &phy_mvebu_sata_ops, NULL); if (IS_ERR(phy)) return PTR_ERR(phy); diff --git a/drivers/phy/phy-omap-control.c b/drivers/phy/phy-omap-control.c index 311b4f9a5132..9487bf112267 100644 --- a/drivers/phy/phy-omap-control.c +++ b/drivers/phy/phy-omap-control.c @@ -27,6 +27,41 @@ #include <linux/phy/omap_control_phy.h> /** + * omap_control_pcie_pcs - set the PCS delay count + * @dev: the control module device + * @id: index of the pcie PHY (should be 1 or 2) + * @delay: 8 bit delay value + */ +void omap_control_pcie_pcs(struct device *dev, u8 id, u8 delay) +{ + u32 val; + struct omap_control_phy *control_phy; + + if (IS_ERR(dev) || !dev) { + pr_err("%s: invalid device\n", __func__); + return; + } + + control_phy = dev_get_drvdata(dev); + if (!control_phy) { + dev_err(dev, "%s: invalid control phy device\n", __func__); + return; + } + + if (control_phy->type != OMAP_CTRL_TYPE_PCIE) { + dev_err(dev, "%s: unsupported operation\n", __func__); + return; + } + + val = readl(control_phy->pcie_pcs); + val &= ~(OMAP_CTRL_PCIE_PCS_MASK << + (id * OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT)); + val |= delay << (id * OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT); + writel(val, control_phy->pcie_pcs); +} +EXPORT_SYMBOL_GPL(omap_control_pcie_pcs); + +/** * omap_control_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 @@ -61,6 +96,7 @@ void omap_control_phy_power(struct device *dev, int on) val |= OMAP_CTRL_DEV_PHY_PD; break; + case OMAP_CTRL_TYPE_PCIE: case OMAP_CTRL_TYPE_PIPE3: rate = clk_get_rate(control_phy->sys_clk); rate = rate/1000000; @@ -211,6 +247,7 @@ EXPORT_SYMBOL_GPL(omap_control_usb_set_mode); static const enum omap_control_phy_type otghs_data = OMAP_CTRL_TYPE_OTGHS; static const enum omap_control_phy_type usb2_data = OMAP_CTRL_TYPE_USB2; static const enum omap_control_phy_type pipe3_data = OMAP_CTRL_TYPE_PIPE3; +static const enum omap_control_phy_type pcie_data = OMAP_CTRL_TYPE_PCIE; static const enum omap_control_phy_type dra7usb2_data = OMAP_CTRL_TYPE_DRA7USB2; static const enum omap_control_phy_type am437usb2_data = OMAP_CTRL_TYPE_AM437USB2; @@ -228,6 +265,10 @@ static const struct of_device_id omap_control_phy_id_table[] = { .data = &pipe3_data, }, { + .compatible = "ti,control-phy-pcie", + .data = &pcie_data, + }, + { .compatible = "ti,control-phy-usb2-dra7", .data = &dra7usb2_data, }, @@ -279,7 +320,8 @@ static int omap_control_phy_probe(struct platform_device *pdev) } } - if (control_phy->type == OMAP_CTRL_TYPE_PIPE3) { + if (control_phy->type == OMAP_CTRL_TYPE_PIPE3 || + control_phy->type == OMAP_CTRL_TYPE_PCIE) { control_phy->sys_clk = devm_clk_get(control_phy->dev, "sys_clkin"); if (IS_ERR(control_phy->sys_clk)) { @@ -288,6 +330,14 @@ static int omap_control_phy_probe(struct platform_device *pdev) } } + if (control_phy->type == OMAP_CTRL_TYPE_PCIE) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "pcie_pcs"); + control_phy->pcie_pcs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(control_phy->pcie_pcs)) + return PTR_ERR(control_phy->pcie_pcs); + } + dev_set_drvdata(control_phy->dev, control_phy); return 0; diff --git a/drivers/phy/phy-omap-usb2.c b/drivers/phy/phy-omap-usb2.c index 34b396146c8a..93d78359246c 100644 --- a/drivers/phy/phy-omap-usb2.c +++ b/drivers/phy/phy-omap-usb2.c @@ -263,7 +263,7 @@ static int omap_usb2_probe(struct platform_device *pdev) platform_set_drvdata(pdev, phy); - generic_phy = devm_phy_create(phy->dev, &ops, NULL); + generic_phy = devm_phy_create(phy->dev, NULL, &ops, NULL); if (IS_ERR(generic_phy)) return PTR_ERR(generic_phy); diff --git a/drivers/phy/phy-qcom-apq8064-sata.c b/drivers/phy/phy-qcom-apq8064-sata.c new file mode 100644 index 000000000000..b3ef7d805765 --- /dev/null +++ b/drivers/phy/phy-qcom-apq8064-sata.c @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/time.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/phy/phy.h> + +/* PHY registers */ +#define UNIPHY_PLL_REFCLK_CFG 0x000 +#define UNIPHY_PLL_PWRGEN_CFG 0x014 +#define UNIPHY_PLL_GLB_CFG 0x020 +#define UNIPHY_PLL_SDM_CFG0 0x038 +#define UNIPHY_PLL_SDM_CFG1 0x03C +#define UNIPHY_PLL_SDM_CFG2 0x040 +#define UNIPHY_PLL_SDM_CFG3 0x044 +#define UNIPHY_PLL_SDM_CFG4 0x048 +#define UNIPHY_PLL_SSC_CFG0 0x04C +#define UNIPHY_PLL_SSC_CFG1 0x050 +#define UNIPHY_PLL_SSC_CFG2 0x054 +#define UNIPHY_PLL_SSC_CFG3 0x058 +#define UNIPHY_PLL_LKDET_CFG0 0x05C +#define UNIPHY_PLL_LKDET_CFG1 0x060 +#define UNIPHY_PLL_LKDET_CFG2 0x064 +#define UNIPHY_PLL_CAL_CFG0 0x06C +#define UNIPHY_PLL_CAL_CFG8 0x08C +#define UNIPHY_PLL_CAL_CFG9 0x090 +#define UNIPHY_PLL_CAL_CFG10 0x094 +#define UNIPHY_PLL_CAL_CFG11 0x098 +#define UNIPHY_PLL_STATUS 0x0C0 + +#define SATA_PHY_SER_CTRL 0x100 +#define SATA_PHY_TX_DRIV_CTRL0 0x104 +#define SATA_PHY_TX_DRIV_CTRL1 0x108 +#define SATA_PHY_TX_IMCAL0 0x11C +#define SATA_PHY_TX_IMCAL2 0x124 +#define SATA_PHY_RX_IMCAL0 0x128 +#define SATA_PHY_EQUAL 0x13C +#define SATA_PHY_OOB_TERM 0x144 +#define SATA_PHY_CDR_CTRL0 0x148 +#define SATA_PHY_CDR_CTRL1 0x14C +#define SATA_PHY_CDR_CTRL2 0x150 +#define SATA_PHY_CDR_CTRL3 0x154 +#define SATA_PHY_PI_CTRL0 0x168 +#define SATA_PHY_POW_DWN_CTRL0 0x180 +#define SATA_PHY_POW_DWN_CTRL1 0x184 +#define SATA_PHY_TX_DATA_CTRL 0x188 +#define SATA_PHY_ALIGNP 0x1A4 +#define SATA_PHY_TX_IMCAL_STAT 0x1E4 +#define SATA_PHY_RX_IMCAL_STAT 0x1E8 + +#define UNIPHY_PLL_LOCK BIT(0) +#define SATA_PHY_TX_CAL BIT(0) +#define SATA_PHY_RX_CAL BIT(0) + +/* default timeout set to 1 sec */ +#define TIMEOUT_MS 10000 +#define DELAY_INTERVAL_US 100 + +struct qcom_apq8064_sata_phy { + void __iomem *mmio; + struct clk *cfg_clk; + struct device *dev; +}; + +/* Helper function to do poll and timeout */ +static int read_poll_timeout(void __iomem *addr, u32 mask) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(TIMEOUT_MS); + + do { + if (readl_relaxed(addr) & mask) + return 0; + + usleep_range(DELAY_INTERVAL_US, DELAY_INTERVAL_US + 50); + } while (!time_after(jiffies, timeout)); + + return (readl_relaxed(addr) & mask) ? 0 : -ETIMEDOUT; +} + +static int qcom_apq8064_sata_phy_init(struct phy *generic_phy) +{ + struct qcom_apq8064_sata_phy *phy = phy_get_drvdata(generic_phy); + void __iomem *base = phy->mmio; + int ret = 0; + + /* SATA phy initialization */ + writel_relaxed(0x01, base + SATA_PHY_SER_CTRL); + writel_relaxed(0xB1, base + SATA_PHY_POW_DWN_CTRL0); + /* Make sure the power down happens before power up */ + mb(); + usleep_range(10, 60); + + writel_relaxed(0x01, base + SATA_PHY_POW_DWN_CTRL0); + writel_relaxed(0x3E, base + SATA_PHY_POW_DWN_CTRL1); + writel_relaxed(0x01, base + SATA_PHY_RX_IMCAL0); + writel_relaxed(0x01, base + SATA_PHY_TX_IMCAL0); + writel_relaxed(0x02, base + SATA_PHY_TX_IMCAL2); + + /* Write UNIPHYPLL registers to configure PLL */ + writel_relaxed(0x04, base + UNIPHY_PLL_REFCLK_CFG); + writel_relaxed(0x00, base + UNIPHY_PLL_PWRGEN_CFG); + + writel_relaxed(0x0A, base + UNIPHY_PLL_CAL_CFG0); + writel_relaxed(0xF3, base + UNIPHY_PLL_CAL_CFG8); + writel_relaxed(0x01, base + UNIPHY_PLL_CAL_CFG9); + writel_relaxed(0xED, base + UNIPHY_PLL_CAL_CFG10); + writel_relaxed(0x02, base + UNIPHY_PLL_CAL_CFG11); + + writel_relaxed(0x36, base + UNIPHY_PLL_SDM_CFG0); + writel_relaxed(0x0D, base + UNIPHY_PLL_SDM_CFG1); + writel_relaxed(0xA3, base + UNIPHY_PLL_SDM_CFG2); + writel_relaxed(0xF0, base + UNIPHY_PLL_SDM_CFG3); + writel_relaxed(0x00, base + UNIPHY_PLL_SDM_CFG4); + + writel_relaxed(0x19, base + UNIPHY_PLL_SSC_CFG0); + writel_relaxed(0xE1, base + UNIPHY_PLL_SSC_CFG1); + writel_relaxed(0x00, base + UNIPHY_PLL_SSC_CFG2); + writel_relaxed(0x11, base + UNIPHY_PLL_SSC_CFG3); + + writel_relaxed(0x04, base + UNIPHY_PLL_LKDET_CFG0); + writel_relaxed(0xFF, base + UNIPHY_PLL_LKDET_CFG1); + + writel_relaxed(0x02, base + UNIPHY_PLL_GLB_CFG); + /* make sure global config LDO power down happens before power up */ + mb(); + + writel_relaxed(0x03, base + UNIPHY_PLL_GLB_CFG); + writel_relaxed(0x05, base + UNIPHY_PLL_LKDET_CFG2); + + /* PLL Lock wait */ + ret = read_poll_timeout(base + UNIPHY_PLL_STATUS, UNIPHY_PLL_LOCK); + if (ret) { + dev_err(phy->dev, "poll timeout UNIPHY_PLL_STATUS\n"); + return ret; + } + + /* TX Calibration */ + ret = read_poll_timeout(base + SATA_PHY_TX_IMCAL_STAT, SATA_PHY_TX_CAL); + if (ret) { + dev_err(phy->dev, "poll timeout SATA_PHY_TX_IMCAL_STAT\n"); + return ret; + } + + /* RX Calibration */ + ret = read_poll_timeout(base + SATA_PHY_RX_IMCAL_STAT, SATA_PHY_RX_CAL); + if (ret) { + dev_err(phy->dev, "poll timeout SATA_PHY_RX_IMCAL_STAT\n"); + return ret; + } + + /* SATA phy calibrated succesfully, power up to functional mode */ + writel_relaxed(0x3E, base + SATA_PHY_POW_DWN_CTRL1); + writel_relaxed(0x01, base + SATA_PHY_RX_IMCAL0); + writel_relaxed(0x01, base + SATA_PHY_TX_IMCAL0); + + writel_relaxed(0x00, base + SATA_PHY_POW_DWN_CTRL1); + writel_relaxed(0x59, base + SATA_PHY_CDR_CTRL0); + writel_relaxed(0x04, base + SATA_PHY_CDR_CTRL1); + writel_relaxed(0x00, base + SATA_PHY_CDR_CTRL2); + writel_relaxed(0x00, base + SATA_PHY_PI_CTRL0); + writel_relaxed(0x00, base + SATA_PHY_CDR_CTRL3); + writel_relaxed(0x01, base + SATA_PHY_POW_DWN_CTRL0); + + writel_relaxed(0x11, base + SATA_PHY_TX_DATA_CTRL); + writel_relaxed(0x43, base + SATA_PHY_ALIGNP); + writel_relaxed(0x04, base + SATA_PHY_OOB_TERM); + + writel_relaxed(0x01, base + SATA_PHY_EQUAL); + writel_relaxed(0x09, base + SATA_PHY_TX_DRIV_CTRL0); + writel_relaxed(0x09, base + SATA_PHY_TX_DRIV_CTRL1); + + return 0; +} + +static int qcom_apq8064_sata_phy_exit(struct phy *generic_phy) +{ + struct qcom_apq8064_sata_phy *phy = phy_get_drvdata(generic_phy); + void __iomem *base = phy->mmio; + + /* Power down PHY */ + writel_relaxed(0xF8, base + SATA_PHY_POW_DWN_CTRL0); + writel_relaxed(0xFE, base + SATA_PHY_POW_DWN_CTRL1); + + /* Power down PLL block */ + writel_relaxed(0x00, base + UNIPHY_PLL_GLB_CFG); + + return 0; +} + +static struct phy_ops qcom_apq8064_sata_phy_ops = { + .init = qcom_apq8064_sata_phy_init, + .exit = qcom_apq8064_sata_phy_exit, + .owner = THIS_MODULE, +}; + +static int qcom_apq8064_sata_phy_probe(struct platform_device *pdev) +{ + struct qcom_apq8064_sata_phy *phy; + struct device *dev = &pdev->dev; + struct resource *res; + struct phy_provider *phy_provider; + struct phy *generic_phy; + int ret; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + phy->mmio = devm_ioremap_resource(dev, res); + if (IS_ERR(phy->mmio)) + return PTR_ERR(phy->mmio); + + generic_phy = devm_phy_create(dev, NULL, &qcom_apq8064_sata_phy_ops, + NULL); + if (IS_ERR(generic_phy)) { + dev_err(dev, "%s: failed to create phy\n", __func__); + return PTR_ERR(generic_phy); + } + + phy->dev = dev; + phy_set_drvdata(generic_phy, phy); + platform_set_drvdata(pdev, phy); + + phy->cfg_clk = devm_clk_get(dev, "cfg"); + if (IS_ERR(phy->cfg_clk)) { + dev_err(dev, "Failed to get sata cfg clock\n"); + return PTR_ERR(phy->cfg_clk); + } + + ret = clk_prepare_enable(phy->cfg_clk); + if (ret) + return ret; + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(phy_provider)) { + clk_disable_unprepare(phy->cfg_clk); + dev_err(dev, "%s: failed to register phy\n", __func__); + return PTR_ERR(phy_provider); + } + + return 0; +} + +static int qcom_apq8064_sata_phy_remove(struct platform_device *pdev) +{ + struct qcom_apq8064_sata_phy *phy = platform_get_drvdata(pdev); + + clk_disable_unprepare(phy->cfg_clk); + + return 0; +} + +static const struct of_device_id qcom_apq8064_sata_phy_of_match[] = { + { .compatible = "qcom,apq8064-sata-phy" }, + { }, +}; +MODULE_DEVICE_TABLE(of, qcom_apq8064_sata_phy_of_match); + +static struct platform_driver qcom_apq8064_sata_phy_driver = { + .probe = qcom_apq8064_sata_phy_probe, + .remove = qcom_apq8064_sata_phy_remove, + .driver = { + .name = "qcom-apq8064-sata-phy", + .owner = THIS_MODULE, + .of_match_table = qcom_apq8064_sata_phy_of_match, + } +}; +module_platform_driver(qcom_apq8064_sata_phy_driver); + +MODULE_DESCRIPTION("QCOM apq8064 SATA PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-qcom-ipq806x-sata.c b/drivers/phy/phy-qcom-ipq806x-sata.c new file mode 100644 index 000000000000..909b5a87fc6a --- /dev/null +++ b/drivers/phy/phy-qcom-ipq806x-sata.c @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/time.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/phy/phy.h> + +struct qcom_ipq806x_sata_phy { + void __iomem *mmio; + struct clk *cfg_clk; + struct device *dev; +}; + +#define __set(v, a, b) (((v) << (b)) & GENMASK(a, b)) + +#define SATA_PHY_P0_PARAM0 0x200 +#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(x) __set(x, 17, 12) +#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK GENMASK(17, 12) +#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2(x) __set(x, 11, 6) +#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK GENMASK(11, 6) +#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1(x) __set(x, 5, 0) +#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK GENMASK(5, 0) + +#define SATA_PHY_P0_PARAM1 0x204 +#define SATA_PHY_P0_PARAM1_RESERVED_BITS31_21(x) __set(x, 31, 21) +#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(x) __set(x, 20, 14) +#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK GENMASK(20, 14) +#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(x) __set(x, 13, 7) +#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK GENMASK(13, 7) +#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(x) __set(x, 6, 0) +#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK GENMASK(6, 0) + +#define SATA_PHY_P0_PARAM2 0x208 +#define SATA_PHY_P0_PARAM2_RX_EQ(x) __set(x, 20, 18) +#define SATA_PHY_P0_PARAM2_RX_EQ_MASK GENMASK(20, 18) + +#define SATA_PHY_P0_PARAM3 0x20C +#define SATA_PHY_SSC_EN 0x8 +#define SATA_PHY_P0_PARAM4 0x210 +#define SATA_PHY_REF_SSP_EN 0x2 +#define SATA_PHY_RESET 0x1 + +static int qcom_ipq806x_sata_phy_init(struct phy *generic_phy) +{ + struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy); + u32 reg; + + /* Setting SSC_EN to 1 */ + reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM3); + reg = reg | SATA_PHY_SSC_EN; + writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM3); + + reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM0) & + ~(SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK | + SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK | + SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK); + reg |= SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(0xf); + writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM0); + + reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM1) & + ~(SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK | + SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK | + SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK); + reg |= SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(0x55) | + SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(0x55) | + SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(0x55); + writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM1); + + reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM2) & + ~SATA_PHY_P0_PARAM2_RX_EQ_MASK; + reg |= SATA_PHY_P0_PARAM2_RX_EQ(0x3); + writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM2); + + /* Setting PHY_RESET to 1 */ + reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4); + reg = reg | SATA_PHY_RESET; + writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4); + + /* Setting REF_SSP_EN to 1 */ + reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4); + reg = reg | SATA_PHY_REF_SSP_EN | SATA_PHY_RESET; + writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4); + + /* make sure all changes complete before we let the PHY out of reset */ + mb(); + + /* sleep for max. 50us more to combine processor wakeups */ + usleep_range(20, 20 + 50); + + /* Clearing PHY_RESET to 0 */ + reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4); + reg = reg & ~SATA_PHY_RESET; + writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4); + + return 0; +} + +static int qcom_ipq806x_sata_phy_exit(struct phy *generic_phy) +{ + struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy); + u32 reg; + + /* Setting PHY_RESET to 1 */ + reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4); + reg = reg | SATA_PHY_RESET; + writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4); + + return 0; +} + +static struct phy_ops qcom_ipq806x_sata_phy_ops = { + .init = qcom_ipq806x_sata_phy_init, + .exit = qcom_ipq806x_sata_phy_exit, + .owner = THIS_MODULE, +}; + +static int qcom_ipq806x_sata_phy_probe(struct platform_device *pdev) +{ + struct qcom_ipq806x_sata_phy *phy; + struct device *dev = &pdev->dev; + struct resource *res; + struct phy_provider *phy_provider; + struct phy *generic_phy; + int ret; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + phy->mmio = devm_ioremap_resource(dev, res); + if (IS_ERR(phy->mmio)) + return PTR_ERR(phy->mmio); + + generic_phy = devm_phy_create(dev, NULL, &qcom_ipq806x_sata_phy_ops, + NULL); + if (IS_ERR(generic_phy)) { + dev_err(dev, "%s: failed to create phy\n", __func__); + return PTR_ERR(generic_phy); + } + + phy->dev = dev; + phy_set_drvdata(generic_phy, phy); + platform_set_drvdata(pdev, phy); + + phy->cfg_clk = devm_clk_get(dev, "cfg"); + if (IS_ERR(phy->cfg_clk)) { + dev_err(dev, "Failed to get sata cfg clock\n"); + return PTR_ERR(phy->cfg_clk); + } + + ret = clk_prepare_enable(phy->cfg_clk); + if (ret) + return ret; + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(phy_provider)) { + clk_disable_unprepare(phy->cfg_clk); + dev_err(dev, "%s: failed to register phy\n", __func__); + return PTR_ERR(phy_provider); + } + + return 0; +} + +static int qcom_ipq806x_sata_phy_remove(struct platform_device *pdev) +{ + struct qcom_ipq806x_sata_phy *phy = platform_get_drvdata(pdev); + + clk_disable_unprepare(phy->cfg_clk); + + return 0; +} + +static const struct of_device_id qcom_ipq806x_sata_phy_of_match[] = { + { .compatible = "qcom,ipq806x-sata-phy" }, + { }, +}; +MODULE_DEVICE_TABLE(of, qcom_ipq806x_sata_phy_of_match); + +static struct platform_driver qcom_ipq806x_sata_phy_driver = { + .probe = qcom_ipq806x_sata_phy_probe, + .remove = qcom_ipq806x_sata_phy_remove, + .driver = { + .name = "qcom-ipq806x-sata-phy", + .owner = THIS_MODULE, + .of_match_table = qcom_ipq806x_sata_phy_of_match, + } +}; +module_platform_driver(qcom_ipq806x_sata_phy_driver); + +MODULE_DESCRIPTION("QCOM IPQ806x SATA PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-samsung-usb2.c b/drivers/phy/phy-samsung-usb2.c index 1e69a32c221d..ae30640a411d 100644 --- a/drivers/phy/phy-samsung-usb2.c +++ b/drivers/phy/phy-samsung-usb2.c @@ -87,6 +87,12 @@ static struct phy *samsung_usb2_phy_xlate(struct device *dev, } static const struct of_device_id samsung_usb2_phy_of_match[] = { +#ifdef CONFIG_PHY_EXYNOS4X12_USB2 + { + .compatible = "samsung,exynos3250-usb2-phy", + .data = &exynos3250_usb2_phy_config, + }, +#endif #ifdef CONFIG_PHY_EXYNOS4210_USB2 { .compatible = "samsung,exynos4210-usb2-phy", @@ -190,7 +196,8 @@ static int samsung_usb2_phy_probe(struct platform_device *pdev) struct samsung_usb2_phy_instance *p = &drv->instances[i]; dev_dbg(dev, "Creating phy \"%s\"\n", label); - p->phy = devm_phy_create(dev, &samsung_usb2_phy_ops, NULL); + p->phy = devm_phy_create(dev, NULL, &samsung_usb2_phy_ops, + NULL); if (IS_ERR(p->phy)) { dev_err(drv->dev, "Failed to create usb2_phy \"%s\"\n", label); diff --git a/drivers/phy/phy-samsung-usb2.h b/drivers/phy/phy-samsung-usb2.h index 45b3170652bd..b03da0ef39ac 100644 --- a/drivers/phy/phy-samsung-usb2.h +++ b/drivers/phy/phy-samsung-usb2.h @@ -29,7 +29,8 @@ struct samsung_usb2_phy_instance { const struct samsung_usb2_common_phy *cfg; struct phy *phy; struct samsung_usb2_phy_driver *drv; - bool enabled; + int int_cnt; + int ext_cnt; }; struct samsung_usb2_phy_driver { @@ -59,8 +60,10 @@ struct samsung_usb2_phy_config { int (*rate_to_clk)(unsigned long, u32 *); unsigned int num_phys; bool has_mode_switch; + bool has_refclk_sel; }; +extern const struct samsung_usb2_phy_config exynos3250_usb2_phy_config; extern const struct samsung_usb2_phy_config exynos4210_usb2_phy_config; extern const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config; extern const struct samsung_usb2_phy_config exynos5250_usb2_phy_config; diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c index 115d8d5190d5..61ebea49709b 100644 --- a/drivers/phy/phy-sun4i-usb.c +++ b/drivers/phy/phy-sun4i-usb.c @@ -22,6 +22,7 @@ */ #include <linux/clk.h> +#include <linux/err.h> #include <linux/io.h> #include <linux/kernel.h> #include <linux/module.h> @@ -294,7 +295,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) return PTR_ERR(phy->pmu); } - phy->phy = devm_phy_create(dev, &sun4i_usb_phy_ops, NULL); + phy->phy = devm_phy_create(dev, NULL, &sun4i_usb_phy_ops, NULL); if (IS_ERR(phy->phy)) { dev_err(dev, "failed to create PHY %d\n", i); return PTR_ERR(phy->phy); @@ -306,10 +307,8 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) dev_set_drvdata(dev, data); phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate); - if (IS_ERR(phy_provider)) - return PTR_ERR(phy_provider); - return 0; + return PTR_ERR_OR_ZERO(phy_provider); } static const struct of_device_id sun4i_usb_phy_of_match[] = { diff --git a/drivers/phy/phy-ti-pipe3.c b/drivers/phy/phy-ti-pipe3.c index 591367654613..b964aa967b46 100644 --- a/drivers/phy/phy-ti-pipe3.c +++ b/drivers/phy/phy-ti-pipe3.c @@ -80,7 +80,9 @@ struct ti_pipe3 { struct clk *wkupclk; struct clk *sys_clk; struct clk *refclk; + struct clk *div_clk; struct pipe3_dpll_map *dpll_map; + u8 id; }; static struct pipe3_dpll_map dpll_map_usb[] = { @@ -215,6 +217,11 @@ static int ti_pipe3_init(struct phy *x) u32 val; int ret = 0; + if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-pcie")) { + omap_control_pcie_pcs(phy->control_dev, phy->id, 0xF1); + return 0; + } + /* Bring it out of IDLE if it is IDLE */ val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); if (val & PLL_IDLE) { @@ -238,8 +245,11 @@ static int ti_pipe3_exit(struct phy *x) u32 val; unsigned long timeout; - /* SATA DPLL can't be powered down due to Errata i783 */ - if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-sata")) + /* SATA DPLL can't be powered down due to Errata i783 and PCIe + * does not have internal DPLL + */ + if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-sata") || + of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-pcie")) return 0; /* Put DPLL in IDLE mode */ @@ -286,32 +296,41 @@ static int ti_pipe3_probe(struct platform_device *pdev) struct device_node *control_node; struct platform_device *control_pdev; const struct of_device_id *match; - - match = of_match_device(of_match_ptr(ti_pipe3_id_table), &pdev->dev); - if (!match) - return -EINVAL; + struct clk *clk; phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); if (!phy) { dev_err(&pdev->dev, "unable to alloc mem for TI PIPE3 PHY\n"); return -ENOMEM; } + phy->dev = &pdev->dev; - phy->dpll_map = (struct pipe3_dpll_map *)match->data; - if (!phy->dpll_map) { - dev_err(&pdev->dev, "no DPLL data\n"); - return -EINVAL; - } + if (!of_device_is_compatible(node, "ti,phy-pipe3-pcie")) { + match = of_match_device(of_match_ptr(ti_pipe3_id_table), + &pdev->dev); + if (!match) + return -EINVAL; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pll_ctrl"); - phy->pll_ctrl_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(phy->pll_ctrl_base)) - return PTR_ERR(phy->pll_ctrl_base); + phy->dpll_map = (struct pipe3_dpll_map *)match->data; + if (!phy->dpll_map) { + dev_err(&pdev->dev, "no DPLL data\n"); + return -EINVAL; + } - phy->dev = &pdev->dev; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "pll_ctrl"); + phy->pll_ctrl_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(phy->pll_ctrl_base)) + return PTR_ERR(phy->pll_ctrl_base); - if (!of_device_is_compatible(node, "ti,phy-pipe3-sata")) { + phy->sys_clk = devm_clk_get(phy->dev, "sysclk"); + if (IS_ERR(phy->sys_clk)) { + dev_err(&pdev->dev, "unable to get sysclk\n"); + return -EINVAL; + } + } + if (!of_device_is_compatible(node, "ti,phy-pipe3-sata")) { phy->wkupclk = devm_clk_get(phy->dev, "wkupclk"); if (IS_ERR(phy->wkupclk)) { dev_err(&pdev->dev, "unable to get wkupclk\n"); @@ -328,10 +347,38 @@ static int ti_pipe3_probe(struct platform_device *pdev) phy->refclk = ERR_PTR(-ENODEV); } - phy->sys_clk = devm_clk_get(phy->dev, "sysclk"); - if (IS_ERR(phy->sys_clk)) { - dev_err(&pdev->dev, "unable to get sysclk\n"); - return -EINVAL; + if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) { + if (of_property_read_u8(node, "id", &phy->id) < 0) + phy->id = 1; + + clk = devm_clk_get(phy->dev, "dpll_ref"); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "unable to get dpll ref clk\n"); + return PTR_ERR(clk); + } + clk_set_rate(clk, 1500000000); + + clk = devm_clk_get(phy->dev, "dpll_ref_m2"); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "unable to get dpll ref m2 clk\n"); + return PTR_ERR(clk); + } + clk_set_rate(clk, 100000000); + + clk = devm_clk_get(phy->dev, "phy-div"); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "unable to get phy-div clk\n"); + return PTR_ERR(clk); + } + clk_set_rate(clk, 100000000); + + phy->div_clk = devm_clk_get(phy->dev, "div-clk"); + if (IS_ERR(phy->div_clk)) { + dev_err(&pdev->dev, "unable to get div-clk\n"); + return PTR_ERR(phy->div_clk); + } + } else { + phy->div_clk = ERR_PTR(-ENODEV); } control_node = of_parse_phandle(node, "ctrl-module", 0); @@ -353,7 +400,7 @@ static int ti_pipe3_probe(struct platform_device *pdev) platform_set_drvdata(pdev, phy); pm_runtime_enable(phy->dev); - generic_phy = devm_phy_create(phy->dev, &ops, NULL); + generic_phy = devm_phy_create(phy->dev, NULL, &ops, NULL); if (IS_ERR(generic_phy)) return PTR_ERR(generic_phy); @@ -387,6 +434,8 @@ static int ti_pipe3_runtime_suspend(struct device *dev) clk_disable_unprepare(phy->wkupclk); if (!IS_ERR(phy->refclk)) clk_disable_unprepare(phy->refclk); + if (!IS_ERR(phy->div_clk)) + clk_disable_unprepare(phy->div_clk); return 0; } @@ -412,8 +461,19 @@ static int ti_pipe3_runtime_resume(struct device *dev) } } + if (!IS_ERR(phy->div_clk)) { + ret = clk_prepare_enable(phy->div_clk); + if (ret) { + dev_err(phy->dev, "Failed to enable div_clk %d\n", ret); + goto err3; + } + } return 0; +err3: + if (!IS_ERR(phy->wkupclk)) + clk_disable_unprepare(phy->wkupclk); + err2: if (!IS_ERR(phy->refclk)) clk_disable_unprepare(phy->refclk); @@ -446,6 +506,9 @@ static const struct of_device_id ti_pipe3_id_table[] = { .compatible = "ti,phy-pipe3-sata", .data = dpll_map_sata, }, + { + .compatible = "ti,phy-pipe3-pcie", + }, {} }; MODULE_DEVICE_TABLE(of, ti_pipe3_id_table); diff --git a/drivers/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c index 2e0e9b3774c8..e1a6623d4696 100644 --- a/drivers/phy/phy-twl4030-usb.c +++ b/drivers/phy/phy-twl4030-usb.c @@ -695,7 +695,7 @@ static int twl4030_usb_probe(struct platform_device *pdev) otg->set_host = twl4030_set_host; otg->set_peripheral = twl4030_set_peripheral; - phy = devm_phy_create(twl->dev, &ops, init_data); + phy = devm_phy_create(twl->dev, NULL, &ops, init_data); if (IS_ERR(phy)) { dev_dbg(&pdev->dev, "Failed to create PHY\n"); return PTR_ERR(phy); diff --git a/drivers/phy/phy-xgene.c b/drivers/phy/phy-xgene.c index 4aa1ccd1511f..db809b97219e 100644 --- a/drivers/phy/phy-xgene.c +++ b/drivers/phy/phy-xgene.c @@ -1707,7 +1707,7 @@ static int xgene_phy_probe(struct platform_device *pdev) ctx->dev = &pdev->dev; platform_set_drvdata(pdev, ctx); - ctx->phy = devm_phy_create(ctx->dev, &xgene_phy_ops, NULL); + ctx->phy = devm_phy_create(ctx->dev, NULL, &xgene_phy_ops, NULL); if (IS_ERR(ctx->phy)) { dev_dbg(&pdev->dev, "Failed to create PHY\n"); rc = PTR_ERR(ctx->phy); diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 2e58f8dfd311..65444b02bd68 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -133,6 +133,9 @@ 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 = PTR_ERR(data->phy); + /* Return -EINVAL if no usbphy is available */ + if (ret == -ENODEV) + ret = -EINVAL; goto err_clk; } diff --git a/drivers/usb/chipidea/debug.c b/drivers/usb/chipidea/debug.c index 7cccab6ff308..795d6538d630 100644 --- a/drivers/usb/chipidea/debug.c +++ b/drivers/usb/chipidea/debug.c @@ -208,7 +208,7 @@ static const struct file_operations ci_requests_fops = { .release = single_release, }; -int ci_otg_show(struct seq_file *s, void *unused) +static int ci_otg_show(struct seq_file *s, void *unused) { struct ci_hdrc *ci = s->private; struct otg_fsm *fsm; @@ -331,7 +331,7 @@ static const struct file_operations ci_role_fops = { .release = single_release, }; -int ci_registers_show(struct seq_file *s, void *unused) +static int ci_registers_show(struct seq_file *s, void *unused) { struct ci_hdrc *ci = s->private; u32 tmp_reg; diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 103a6e9ee49d..ec978408a2ee 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -715,7 +715,7 @@ static int usbtmc_ioctl_clear(struct usbtmc_device_data *data) u8 *buffer; int rv; int n; - int actual; + int actual = 0; int max_size; dev = &data->intf->dev; diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 1ab4df1de2da..b2a540b43f97 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -199,6 +199,17 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, if (n == 0) n = 9; /* 32 ms = 2^(9-1) uframes */ j = 16; + + /* + * Adjust bInterval for quirked devices. + * This quirk fixes bIntervals reported in + * linear microframes. + */ + if (to_usb_device(ddev)->quirks & + USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL) { + n = clamp(fls(d->bInterval), i, j); + i = j = n; + } break; default: /* USB_SPEED_FULL or _LOW */ /* For low-speed, 10 ms is the official minimum. diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 257876ea03a1..0b59731c3021 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -1509,7 +1509,7 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb u = (is_in ? URB_DIR_IN : URB_DIR_OUT); if (uurb->flags & USBDEVFS_URB_ISO_ASAP) u |= URB_ISO_ASAP; - if (uurb->flags & USBDEVFS_URB_SHORT_NOT_OK) + if (uurb->flags & USBDEVFS_URB_SHORT_NOT_OK && is_in) u |= URB_SHORT_NOT_OK; if (uurb->flags & USBDEVFS_URB_NO_FSBR) u |= URB_NO_FSBR; diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 4aeb10034de7..9bffd26cea05 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -417,10 +417,11 @@ static int usb_unbind_interface(struct device *dev) */ lpm_disable_error = usb_unlocked_disable_lpm(udev); - /* Terminate all URBs for this interface unless the driver - * supports "soft" unbinding. + /* + * Terminate all URBs for this interface unless the driver + * supports "soft" unbinding and the device is still present. */ - if (!driver->soft_unbind) + if (!driver->soft_unbind || udev->state == USB_STATE_NOTATTACHED) usb_disable_interface(udev, intf, false); driver->disconnect(intf); diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 82044b5d6113..efc953119ce2 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -380,6 +380,8 @@ void usb_hcd_pci_shutdown(struct pci_dev *dev) if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) && hcd->driver->shutdown) { hcd->driver->shutdown(hcd); + if (usb_hcd_is_primary_hcd(hcd) && hcd->irq > 0) + free_irq(hcd->irq, hcd); pci_disable_device(dev); } } diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index bec31e2efb88..487abcfcccd8 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -855,8 +855,6 @@ static ssize_t authorized_default_show(struct device *dev, struct usb_bus *usb_bus = rh_usb_dev->bus; struct usb_hcd *usb_hcd; - if (usb_bus == NULL) /* FIXME: not sure if this case is possible */ - return -ENODEV; usb_hcd = bus_to_hcd(usb_bus); return snprintf(buf, PAGE_SIZE, "%u\n", usb_hcd->authorized_default); } @@ -871,8 +869,6 @@ static ssize_t authorized_default_store(struct device *dev, struct usb_bus *usb_bus = rh_usb_dev->bus; struct usb_hcd *usb_hcd; - if (usb_bus == NULL) /* FIXME: not sure if this case is possible */ - return -ENODEV; usb_hcd = bus_to_hcd(usb_bus); result = sscanf(buf, "%u\n", &val); if (result == 1) { diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 0e950ad8cb25..8a4dcbc7a75f 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2606,13 +2606,20 @@ static int hub_port_reset(struct usb_hub *hub, int port1, /* Is a USB 3.0 port in the Inactive or Compliance Mode state? * Port worm reset is required to recover */ -static bool hub_port_warm_reset_required(struct usb_hub *hub, u16 portstatus) +static bool hub_port_warm_reset_required(struct usb_hub *hub, int port1, + u16 portstatus) { - return hub_is_superspeed(hub->hdev) && - (((portstatus & USB_PORT_STAT_LINK_STATE) == - USB_SS_PORT_LS_SS_INACTIVE) || - ((portstatus & USB_PORT_STAT_LINK_STATE) == - USB_SS_PORT_LS_COMP_MOD)) ; + u16 link_state; + + if (!hub_is_superspeed(hub->hdev)) + return false; + + if (test_bit(port1, hub->warm_reset_bits)) + return true; + + link_state = portstatus & USB_PORT_STAT_LINK_STATE; + return link_state == USB_SS_PORT_LS_SS_INACTIVE + || link_state == USB_SS_PORT_LS_COMP_MOD; } static int hub_port_wait_reset(struct usb_hub *hub, int port1, @@ -2649,7 +2656,7 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, if ((portstatus & USB_PORT_STAT_RESET)) return -EBUSY; - if (hub_port_warm_reset_required(hub, portstatus)) + if (hub_port_warm_reset_required(hub, port1, portstatus)) return -ENOTCONN; /* Device went away? */ @@ -2749,9 +2756,10 @@ static int hub_port_reset(struct usb_hub *hub, int port1, if (status < 0) goto done; - if (hub_port_warm_reset_required(hub, portstatus)) + if (hub_port_warm_reset_required(hub, port1, portstatus)) warm = true; } + clear_bit(port1, hub->warm_reset_bits); /* Reset the port */ for (i = 0; i < PORT_RESET_TRIES; i++) { @@ -2788,7 +2796,8 @@ static int hub_port_reset(struct usb_hub *hub, int port1, &portstatus, &portchange) < 0) goto done; - if (!hub_port_warm_reset_required(hub, portstatus)) + if (!hub_port_warm_reset_required(hub, port1, + portstatus)) goto done; /* @@ -2875,8 +2884,13 @@ static int check_port_resume_type(struct usb_device *udev, { struct usb_port *port_dev = hub->ports[port1 - 1]; + /* Is a warm reset needed to recover the connection? */ + if (status == 0 && udev->reset_resume + && hub_port_warm_reset_required(hub, port1, portstatus)) { + /* pass */; + } /* Is the device still present? */ - if (status || port_is_suspended(hub, portstatus) || + else if (status || port_is_suspended(hub, portstatus) || !port_is_power_on(hub, portstatus) || !(portstatus & USB_PORT_STAT_CONNECTION)) { if (status >= 0) @@ -3264,6 +3278,43 @@ static int finish_port_resume(struct usb_device *udev) } /* + * There are some SS USB devices which take longer time for link training. + * XHCI specs 4.19.4 says that when Link training is successful, port + * sets CSC bit to 1. So if SW reads port status before successful link + * training, then it will not find device to be present. + * USB Analyzer log with such buggy devices show that in some cases + * device switch on the RX termination after long delay of host enabling + * the VBUS. In few other cases it has been seen that device fails to + * negotiate link training in first attempt. It has been + * reported till now that few devices take as long as 2000 ms to train + * the link after host enabling its VBUS and termination. Following + * routine implements a 2000 ms timeout for link training. If in a case + * link trains before timeout, loop will exit earlier. + * + * FIXME: If a device was connected before suspend, but was removed + * while system was asleep, then the loop in the following routine will + * only exit at timeout. + * + * This routine should only be called when persist is enabled for a SS + * device. + */ +static int wait_for_ss_port_enable(struct usb_device *udev, + struct usb_hub *hub, int *port1, + u16 *portchange, u16 *portstatus) +{ + int status = 0, delay_ms = 0; + + while (delay_ms < 2000) { + if (status || *portstatus & USB_PORT_STAT_CONNECTION) + break; + msleep(20); + delay_ms += 20; + status = hub_port_status(hub, *port1, portstatus, portchange); + } + return status; +} + +/* * usb_port_resume - re-activate a suspended usb device's upstream port * @udev: device to re-activate, not a root hub * Context: must be able to sleep; device not locked; pm locks held @@ -3359,6 +3410,10 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) } } + if (udev->persist_enabled && hub_is_superspeed(hub->hdev)) + status = wait_for_ss_port_enable(udev, hub, &port1, &portchange, + &portstatus); + status = check_port_resume_type(udev, hub, port1, status, portchange, portstatus); if (status == 0) @@ -3879,7 +3934,8 @@ int usb_disable_lpm(struct usb_device *udev) if (!udev || !udev->parent || udev->speed != USB_SPEED_SUPER || - !udev->lpm_capable) + !udev->lpm_capable || + udev->state < USB_STATE_DEFAULT) return 0; hcd = bus_to_hcd(udev->bus); @@ -3935,7 +3991,8 @@ void usb_enable_lpm(struct usb_device *udev) if (!udev || !udev->parent || udev->speed != USB_SPEED_SUPER || - !udev->lpm_capable) + !udev->lpm_capable || + udev->state < USB_STATE_DEFAULT) return; udev->lpm_disable_count--; @@ -4550,6 +4607,7 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus, struct usb_hcd *hcd = bus_to_hcd(hdev->bus); struct usb_port *port_dev = hub->ports[port1 - 1]; struct usb_device *udev = port_dev->child; + static int unreliable_port = -1; /* Disconnect any existing devices under this port */ if (udev) { @@ -4570,10 +4628,14 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus, USB_PORT_STAT_C_ENABLE)) { status = hub_port_debounce_be_stable(hub, port1); if (status < 0) { - if (status != -ENODEV && printk_ratelimit()) - dev_err(&port_dev->dev, - "connect-debounce failed\n"); + if (status != -ENODEV && + port1 != unreliable_port && + printk_ratelimit()) + dev_err(&udev->dev, "connect-debounce failed, port %d disabled\n", + port1); + portstatus &= ~USB_PORT_STAT_CONNECTION; + unreliable_port = port1; } else { portstatus = status; } @@ -4889,7 +4951,7 @@ static void port_event(struct usb_hub *hub, int port1) * Warm reset a USB3 protocol port if it's in * SS.Inactive state. */ - if (hub_port_warm_reset_required(hub, portstatus)) { + if (hub_port_warm_reset_required(hub, port1, portstatus)) { dev_dbg(&port_dev->dev, "do warm reset\n"); if (!udev || !(portstatus & USB_PORT_STAT_CONNECTION) || udev->state == USB_STATE_NOTATTACHED) { diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h index 326308e53961..c77d8778af4b 100644 --- a/drivers/usb/core/hub.h +++ b/drivers/usb/core/hub.h @@ -52,6 +52,8 @@ struct usb_hub { unsigned long power_bits[1]; /* ports that are powered */ unsigned long child_usage_bits[1]; /* ports powered on for children */ + unsigned long warm_reset_bits[1]; /* ports requesting warm + reset recovery */ #if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */ #error event_bits[] is too short! #endif diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c index fe1b6d0967e3..cd3f9dc24a06 100644 --- a/drivers/usb/core/port.c +++ b/drivers/usb/core/port.c @@ -103,16 +103,19 @@ static int usb_port_runtime_resume(struct device *dev) msleep(hub_power_on_good_delay(hub)); if (udev && !retval) { /* - * Attempt to wait for usb hub port to be reconnected in order - * to make the resume procedure successful. The device may have - * disconnected while the port was powered off, so ignore the - * return status. + * Our preference is to simply wait for the port to reconnect, + * as that is the lowest latency method to restart the port. + * However, there are cases where toggling port power results in + * the host port and the device port getting out of sync causing + * a link training live lock. Upon timeout, flag the port as + * needing warm reset recovery (to be performed later by + * usb_port_resume() as requested via usb_wakeup_notification()) */ - retval = hub_port_debounce_be_connected(hub, port1); - if (retval < 0) - dev_dbg(&port_dev->dev, "can't get reconnection after setting port power on, status %d\n", - retval); - retval = 0; + if (hub_port_debounce_be_connected(hub, port1) < 0) { + dev_dbg(&port_dev->dev, "reconnect timeout\n"); + if (hub_is_superspeed(hdev)) + set_bit(port1, hub->warm_reset_bits); + } /* Force the child awake to revalidate after the power loss. */ if (!test_and_set_bit(port1, hub->child_usage_bits)) { diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 739ee8e8bdfd..bae636e2a1a3 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -145,6 +145,10 @@ static const struct usb_device_id usb_quirk_list[] = { /* SKYMEDI USB_DRIVE */ { USB_DEVICE(0x1516, 0x8628), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Razer - Razer Blade Keyboard */ + { USB_DEVICE(0x1532, 0x0116), .driver_info = + USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL }, + /* BUILDWIN Photo Frame */ { USB_DEVICE(0x1908, 0x1315), .driver_info = USB_QUIRK_HONOR_BNUMINTERFACES }, @@ -152,6 +156,9 @@ static const struct usb_device_id usb_quirk_list[] = { /* INTEL VALUE SSD */ { USB_DEVICE(0x8086, 0xf1a5), .driver_info = USB_QUIRK_RESET_RESUME }, + /* USB3503 */ + { USB_DEVICE(0x0424, 0x3503), .driver_info = USB_QUIRK_RESET_RESUME }, + { } /* terminating entry must be last */ }; diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 991386ceb4ec..c9e8ee81b6b7 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -454,6 +454,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) URB_FREE_BUFFER); switch (xfertype) { case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: if (is_out) allowed |= URB_ZERO_PACKET; /* FALLTHROUGH */ diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 4d1144990d4c..2dd2362198d2 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -501,6 +501,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, } return dev; } +EXPORT_SYMBOL_GPL(usb_alloc_dev); /** * usb_get_dev - increments the reference count of the usb device structure diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index f3c56a2fed5b..0ba9c335b584 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -1,6 +1,4 @@ /** - * linux/drivers/usb/gadget/s3c-hsotg.c - * * Copyright (c) 2011 Samsung Electronics Co., Ltd. * http://www.samsung.com * @@ -1022,7 +1020,8 @@ static void s3c_hsotg_disconnect(struct s3c_hsotg *hsotg); * * Set stall for ep0 as response for setup request. */ -static void s3c_hsotg_stall_ep0(struct s3c_hsotg *hsotg) { +static void s3c_hsotg_stall_ep0(struct s3c_hsotg *hsotg) +{ struct s3c_hsotg_ep *ep0 = &hsotg->eps[0]; u32 reg; u32 ctrl; @@ -1994,7 +1993,7 @@ static void kill_all_requests(struct s3c_hsotg *hsotg, s3c_hsotg_complete_request(hsotg, ep, req, result); } - if(hsotg->dedicated_fifos) + if (hsotg->dedicated_fifos) if ((readl(hsotg->regs + DTXFSTS(ep->index)) & 0xffff) * 4 < 3072) s3c_hsotg_txfifo_flush(hsotg, ep->index); } @@ -3390,10 +3389,8 @@ static int s3c_hsotg_probe(struct platform_device *pdev) int i; hsotg = devm_kzalloc(&pdev->dev, sizeof(struct s3c_hsotg), GFP_KERNEL); - if (!hsotg) { - dev_err(dev, "cannot get memory\n"); + if (!hsotg) return -ENOMEM; - } /* * Attempt to find a generic PHY, then look for an old style @@ -3512,7 +3509,6 @@ static int s3c_hsotg_probe(struct platform_device *pdev) eps = kcalloc(hsotg->num_of_eps + 1, sizeof(struct s3c_hsotg_ep), GFP_KERNEL); if (!eps) { - dev_err(dev, "cannot get memory\n"); ret = -ENOMEM; goto err_supplies; } diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 261c3b428220..785510a0a0c3 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -93,4 +93,11 @@ config USB_DWC3_VERBOSE help Say Y here to enable verbose debugging messages on DWC3 Driver. +config DWC3_HOST_USB3_LPM_ENABLE + bool "Enable USB3 LPM Capability" + depends on USB_DWC3_HOST=y || USB_DWC3_DUAL_ROLE=y + default n + help + Select this when you want to enable USB3 LPM with dwc3 xhci host. + endif diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index eb69eb9f06c8..b769c1faaf03 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -386,6 +386,13 @@ static int dwc3_core_init(struct dwc3 *dwc) } dwc->revision = reg; + /* Handle USB2.0-only core configuration */ + if (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) == + DWC3_GHWPARAMS3_SSPHY_IFC_DIS) { + if (dwc->maximum_speed == USB_SPEED_SUPER) + dwc->maximum_speed = USB_SPEED_HIGH; + } + /* issue device SoftReset too */ timeout = jiffies + msecs_to_jiffies(500); dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST); @@ -656,6 +663,31 @@ static int dwc3_probe(struct platform_device *pdev) return -ENODEV; } + dwc->xhci_resources[0].start = res->start; + dwc->xhci_resources[0].end = dwc->xhci_resources[0].start + + DWC3_XHCI_REGS_END; + dwc->xhci_resources[0].flags = res->flags; + dwc->xhci_resources[0].name = res->name; + + res->start += DWC3_GLOBALS_REGS_START; + + /* + * Request memory region but exclude xHCI regs, + * since it will be requested by the xhci-plat driver. + */ + regs = devm_ioremap_resource(dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + dwc->regs = regs; + dwc->regs_size = resource_size(res); + /* + * restore res->start back to its original value so that, + * in case the probe is deferred, we don't end up getting error in + * request the memory region the next time probe is called. + */ + res->start -= DWC3_GLOBALS_REGS_START; + if (node) { dwc->maximum_speed = of_usb_get_maximum_speed(node); @@ -676,28 +708,9 @@ static int dwc3_probe(struct platform_device *pdev) if (ret) return ret; - dwc->xhci_resources[0].start = res->start; - dwc->xhci_resources[0].end = dwc->xhci_resources[0].start + - DWC3_XHCI_REGS_END; - dwc->xhci_resources[0].flags = res->flags; - dwc->xhci_resources[0].name = res->name; - - res->start += DWC3_GLOBALS_REGS_START; - - /* - * Request memory region but exclude xHCI regs, - * since it will be requested by the xhci-plat driver. - */ - regs = devm_ioremap_resource(dev, res); - if (IS_ERR(regs)) - return PTR_ERR(regs); - spin_lock_init(&dwc->lock); platform_set_drvdata(pdev, dwc); - dwc->regs = regs; - dwc->regs_size = resource_size(res); - dev->dma_mask = dev->parent->dma_mask; dev->dma_parms = dev->parent->dma_parms; dma_set_coherent_mask(dev, dev->parent->coherent_dma_mask); diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 57332e3768e4..48fb264065db 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -191,6 +191,19 @@ #define DWC3_GHWPARAMS1_PWROPT(n) ((n) << 24) #define DWC3_GHWPARAMS1_PWROPT_MASK DWC3_GHWPARAMS1_PWROPT(3) +/* Global HWPARAMS3 Register */ +#define DWC3_GHWPARAMS3_SSPHY_IFC(n) ((n) & 3) +#define DWC3_GHWPARAMS3_SSPHY_IFC_DIS 0 +#define DWC3_GHWPARAMS3_SSPHY_IFC_ENA 1 +#define DWC3_GHWPARAMS3_HSPHY_IFC(n) (((n) & (3 << 2)) >> 2) +#define DWC3_GHWPARAMS3_HSPHY_IFC_DIS 0 +#define DWC3_GHWPARAMS3_HSPHY_IFC_UTMI 1 +#define DWC3_GHWPARAMS3_HSPHY_IFC_ULPI 2 +#define DWC3_GHWPARAMS3_HSPHY_IFC_UTMI_ULPI 3 +#define DWC3_GHWPARAMS3_FSPHY_IFC(n) (((n) & (3 << 4)) >> 4) +#define DWC3_GHWPARAMS3_FSPHY_IFC_DIS 0 +#define DWC3_GHWPARAMS3_FSPHY_IFC_ENA 1 + /* Global HWPARAMS4 Register */ #define DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(n) (((n) & (0x0f << 13)) >> 13) #define DWC3_MAX_HIBER_SCRATCHBUFS 15 diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 07a736acd0f2..ef4936ff626c 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -77,10 +77,6 @@ #define USBOTGSS_DEV_EBC_EN 0x0110 #define USBOTGSS_DEBUG_OFFSET 0x0600 -/* REVISION REGISTER */ -#define USBOTGSS_REVISION_XMAJOR(reg) ((reg >> 8) & 0x7) -#define USBOTGSS_REVISION_XMAJOR1 1 -#define USBOTGSS_REVISION_XMAJOR2 2 /* SYSCONFIG REGISTER */ #define USBOTGSS_SYSCONFIG_DMADISABLE (1 << 16) @@ -129,7 +125,6 @@ struct dwc3_omap { u32 irq_eoi_offset; u32 debug_offset; u32 irq0_offset; - u32 revision; u32 dma_status:1; @@ -383,6 +378,87 @@ static int dwc3_omap_vbus_notifier(struct notifier_block *nb, return NOTIFY_DONE; } +static void dwc3_omap_map_offset(struct dwc3_omap *omap) +{ + struct device_node *node = omap->dev->of_node; + + /* + * Differentiate between OMAP5 and AM437x. + * + * For OMAP5(ES2.0) and AM437x wrapper revision is same, even + * though there are changes in wrapper register offsets. + * + * Using dt compatible to differentiate AM437x. + */ + if (of_device_is_compatible(node, "ti,am437x-dwc3")) { + omap->irq_eoi_offset = USBOTGSS_EOI_OFFSET; + omap->irq0_offset = USBOTGSS_IRQ0_OFFSET; + omap->irqmisc_offset = USBOTGSS_IRQMISC_OFFSET; + omap->utmi_otg_offset = USBOTGSS_UTMI_OTG_OFFSET; + omap->debug_offset = USBOTGSS_DEBUG_OFFSET; + } +} + +static void dwc3_omap_set_utmi_mode(struct dwc3_omap *omap) +{ + u32 reg; + struct device_node *node = omap->dev->of_node; + int utmi_mode = 0; + + reg = dwc3_omap_read_utmi_status(omap); + + of_property_read_u32(node, "utmi-mode", &utmi_mode); + + switch (utmi_mode) { + case DWC3_OMAP_UTMI_MODE_SW: + reg |= USBOTGSS_UTMI_OTG_STATUS_SW_MODE; + break; + case DWC3_OMAP_UTMI_MODE_HW: + reg &= ~USBOTGSS_UTMI_OTG_STATUS_SW_MODE; + break; + default: + dev_dbg(omap->dev, "UNKNOWN utmi mode %d\n", utmi_mode); + } + + dwc3_omap_write_utmi_status(omap, reg); +} + +static int dwc3_omap_extcon_register(struct dwc3_omap *omap) +{ + u32 ret; + struct device_node *node = omap->dev->of_node; + struct extcon_dev *edev; + + if (of_property_read_bool(node, "extcon")) { + edev = extcon_get_edev_by_phandle(omap->dev, 0); + if (IS_ERR(edev)) { + dev_vdbg(omap->dev, "couldn't get extcon device\n"); + return -EPROBE_DEFER; + } + + omap->vbus_nb.notifier_call = dwc3_omap_vbus_notifier; + ret = extcon_register_interest(&omap->extcon_vbus_dev, + edev->name, "USB", + &omap->vbus_nb); + if (ret < 0) + dev_vdbg(omap->dev, "failed to register notifier for USB\n"); + + omap->id_nb.notifier_call = dwc3_omap_id_notifier; + ret = extcon_register_interest(&omap->extcon_id_dev, + edev->name, "USB-HOST", + &omap->id_nb); + if (ret < 0) + dev_vdbg(omap->dev, "failed to register notifier for USB-HOST\n"); + + if (extcon_get_cable_state(edev, "USB") == true) + dwc3_omap_set_mailbox(omap, OMAP_DWC3_VBUS_VALID); + if (extcon_get_cable_state(edev, "USB-HOST") == true) + dwc3_omap_set_mailbox(omap, OMAP_DWC3_ID_GROUND); + } + + return 0; +} + static int dwc3_omap_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; @@ -390,15 +466,11 @@ static int dwc3_omap_probe(struct platform_device *pdev) struct dwc3_omap *omap; struct resource *res; struct device *dev = &pdev->dev; - struct extcon_dev *edev; struct regulator *vbus_reg = NULL; int ret; int irq; - int utmi_mode = 0; - int x_major; - u32 reg; void __iomem *base; @@ -448,58 +520,8 @@ static int dwc3_omap_probe(struct platform_device *pdev) goto err0; } - reg = dwc3_omap_readl(omap->base, USBOTGSS_REVISION); - omap->revision = reg; - x_major = USBOTGSS_REVISION_XMAJOR(reg); - - /* Differentiate between OMAP5 and AM437x */ - switch (x_major) { - case USBOTGSS_REVISION_XMAJOR1: - case USBOTGSS_REVISION_XMAJOR2: - omap->irq_eoi_offset = 0; - omap->irq0_offset = 0; - omap->irqmisc_offset = 0; - omap->utmi_otg_offset = 0; - omap->debug_offset = 0; - break; - default: - /* Default to the latest revision */ - omap->irq_eoi_offset = USBOTGSS_EOI_OFFSET; - omap->irq0_offset = USBOTGSS_IRQ0_OFFSET; - omap->irqmisc_offset = USBOTGSS_IRQMISC_OFFSET; - omap->utmi_otg_offset = USBOTGSS_UTMI_OTG_OFFSET; - omap->debug_offset = USBOTGSS_DEBUG_OFFSET; - break; - } - - /* For OMAP5(ES2.0) and AM437x x_major is 2 even though there are - * changes in wrapper registers, Using dt compatible for aegis - */ - - if (of_device_is_compatible(node, "ti,am437x-dwc3")) { - omap->irq_eoi_offset = USBOTGSS_EOI_OFFSET; - omap->irq0_offset = USBOTGSS_IRQ0_OFFSET; - omap->irqmisc_offset = USBOTGSS_IRQMISC_OFFSET; - omap->utmi_otg_offset = USBOTGSS_UTMI_OTG_OFFSET; - omap->debug_offset = USBOTGSS_DEBUG_OFFSET; - } - - reg = dwc3_omap_read_utmi_status(omap); - - of_property_read_u32(node, "utmi-mode", &utmi_mode); - - switch (utmi_mode) { - case DWC3_OMAP_UTMI_MODE_SW: - reg |= USBOTGSS_UTMI_OTG_STATUS_SW_MODE; - break; - case DWC3_OMAP_UTMI_MODE_HW: - reg &= ~USBOTGSS_UTMI_OTG_STATUS_SW_MODE; - break; - default: - dev_dbg(dev, "UNKNOWN utmi mode %d\n", utmi_mode); - } - - dwc3_omap_write_utmi_status(omap, reg); + dwc3_omap_map_offset(omap); + dwc3_omap_set_utmi_mode(omap); /* check the DMA Status */ reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG); @@ -515,31 +537,9 @@ static int dwc3_omap_probe(struct platform_device *pdev) dwc3_omap_enable_irqs(omap); - if (of_property_read_bool(node, "extcon")) { - edev = extcon_get_edev_by_phandle(dev, 0); - if (IS_ERR(edev)) { - dev_vdbg(dev, "couldn't get extcon device\n"); - ret = -EPROBE_DEFER; - goto err2; - } - - omap->vbus_nb.notifier_call = dwc3_omap_vbus_notifier; - ret = extcon_register_interest(&omap->extcon_vbus_dev, - edev->name, "USB", &omap->vbus_nb); - if (ret < 0) - dev_vdbg(dev, "failed to register notifier for USB\n"); - omap->id_nb.notifier_call = dwc3_omap_id_notifier; - ret = extcon_register_interest(&omap->extcon_id_dev, edev->name, - "USB-HOST", &omap->id_nb); - if (ret < 0) - dev_vdbg(dev, - "failed to register notifier for USB-HOST\n"); - - if (extcon_get_cable_state(edev, "USB") == true) - dwc3_omap_set_mailbox(omap, OMAP_DWC3_VBUS_VALID); - if (extcon_get_cable_state(edev, "USB-HOST") == true) - dwc3_omap_set_mailbox(omap, OMAP_DWC3_ID_GROUND); - } + ret = dwc3_omap_extcon_register(omap); + if (ret < 0) + goto err2; ret = of_platform_populate(node, NULL, NULL, dev); if (ret) { diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index dab7927d1009..349cacc577d8 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1971,8 +1971,7 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, } static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc, - struct dwc3_ep *dep, const struct dwc3_event_depevt *event, - int start_new) + struct dwc3_ep *dep, const struct dwc3_event_depevt *event) { unsigned status = 0; int clean_busy; @@ -2039,7 +2038,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, return; } - dwc3_endpoint_transfer_complete(dwc, dep, event, 1); + dwc3_endpoint_transfer_complete(dwc, dep, event); break; case DWC3_DEPEVT_XFERINPROGRESS: if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) { @@ -2048,7 +2047,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, return; } - dwc3_endpoint_transfer_complete(dwc, dep, event, 0); + dwc3_endpoint_transfer_complete(dwc, dep, event); break; case DWC3_DEPEVT_XFERNOTREADY: if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c index 32db328cc769..dcb8ca084598 100644 --- a/drivers/usb/dwc3/host.c +++ b/drivers/usb/dwc3/host.c @@ -16,12 +16,14 @@ */ #include <linux/platform_device.h> +#include <linux/usb/xhci_pdriver.h> #include "core.h" int dwc3_host_init(struct dwc3 *dwc) { struct platform_device *xhci; + struct usb_xhci_pdata pdata; int ret; xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO); @@ -46,6 +48,18 @@ int dwc3_host_init(struct dwc3 *dwc) goto err1; } + memset(&pdata, 0, sizeof(pdata)); + +#ifdef CONFIG_DWC3_HOST_USB3_LPM_ENABLE + pdata.usb3_lpm_capable = 1; +#endif + + ret = platform_device_add_data(xhci, &pdata, sizeof(pdata)); + if (ret) { + dev_err(dwc->dev, "couldn't add platform data to xHCI device\n"); + goto err1; + } + ret = platform_device_add(xhci); if (ret) { dev_err(dwc->dev, "failed to register xHCI device\n"); diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index ba18e9c110cc..5c822afb6d70 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -127,368 +127,7 @@ config USB_GADGET_STORAGE_NUM_BUFFERS a module parameter as well. If unsure, say 2. -# -# USB Peripheral Controller Support -# -# The order here is alphabetical, except that integrated controllers go -# before discrete ones so they will be the initial/default value: -# - integrated/SOC controllers first -# - licensed IP used in both SOC and discrete versions -# - discrete ones (including all PCI-only controllers) -# - debug/dummy gadget+hcd is last. -# -menu "USB Peripheral Controller" - -# -# Integrated controllers -# - -config USB_AT91 - tristate "Atmel AT91 USB Device Port" - depends on ARCH_AT91 - help - Many Atmel AT91 processors (such as the AT91RM2000) have a - full speed USB Device Port with support for five configurable - endpoints (plus endpoint zero). - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "at91_udc" and force all - gadget drivers to also be dynamically linked. - -config USB_LPC32XX - tristate "LPC32XX USB Peripheral Controller" - depends on ARCH_LPC32XX && I2C - select USB_ISP1301 - help - This option selects the USB device controller in the LPC32xx SoC. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "lpc32xx_udc" and force all - gadget drivers to also be dynamically linked. - -config USB_ATMEL_USBA - tristate "Atmel USBA" - depends on AVR32 || ARCH_AT91 - help - USBA is the integrated high-speed USB Device controller on - the AT32AP700x, some AT91SAM9 and AT91CAP9 processors from Atmel. - -config USB_BCM63XX_UDC - tristate "Broadcom BCM63xx Peripheral Controller" - depends on BCM63XX - help - Many Broadcom BCM63xx chipsets (such as the BCM6328) have a - high speed USB Device Port with support for four fixed endpoints - (plus endpoint zero). - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "bcm63xx_udc". - -config USB_FSL_USB2 - tristate "Freescale Highspeed USB DR Peripheral Controller" - depends on FSL_SOC || ARCH_MXC - select USB_FSL_MPH_DR_OF if OF - help - Some of Freescale PowerPC and i.MX processors have a High Speed - Dual-Role(DR) USB controller, which supports device mode. - - The number of programmable endpoints is different through - SOC revisions. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "fsl_usb2_udc" and force - all gadget drivers to also be dynamically linked. - -config USB_FUSB300 - tristate "Faraday FUSB300 USB Peripheral Controller" - depends on !PHYS_ADDR_T_64BIT && HAS_DMA - help - Faraday usb device controller FUSB300 driver - -config USB_FOTG210_UDC - depends on HAS_DMA - tristate "Faraday FOTG210 USB Peripheral Controller" - help - Faraday USB2.0 OTG controller which can be configured as - high speed or full speed USB device. This driver supppors - Bulk Transfer so far. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "fotg210_udc". - -config USB_GR_UDC - tristate "Aeroflex Gaisler GRUSBDC USB Peripheral Controller Driver" - depends on HAS_DMA - help - Select this to support Aeroflex Gaisler GRUSBDC cores from the GRLIB - VHDL IP core library. - -config USB_OMAP - tristate "OMAP USB Device Controller" - depends on ARCH_OMAP1 - depends on ISP1301_OMAP || !(MACH_OMAP_H2 || MACH_OMAP_H3) - help - Many Texas Instruments OMAP processors have flexible full - speed USB device controllers, with support for up to 30 - endpoints (plus endpoint zero). This driver supports the - controller in the OMAP 1611, and should work with controllers - in other OMAP processors too, given minor tweaks. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "omap_udc" and force all - gadget drivers to also be dynamically linked. - -config USB_PXA25X - tristate "PXA 25x or IXP 4xx" - depends on (ARCH_PXA && PXA25x) || ARCH_IXP4XX - help - Intel's PXA 25x series XScale ARM-5TE processors include - an integrated full speed USB 1.1 device controller. The - controller in the IXP 4xx series is register-compatible. - - It has fifteen fixed-function endpoints, as well as endpoint - zero (for control transfers). - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "pxa25x_udc" and force all - gadget drivers to also be dynamically linked. - -# if there's only one gadget driver, using only two bulk endpoints, -# don't waste memory for the other endpoints -config USB_PXA25X_SMALL - depends on USB_PXA25X - bool - default n if USB_ETH_RNDIS - default y if USB_ZERO - default y if USB_ETH - default y if USB_G_SERIAL - -config USB_R8A66597 - tristate "Renesas R8A66597 USB Peripheral Controller" - depends on HAS_DMA - help - R8A66597 is a discrete USB host and peripheral controller chip that - supports both full and high speed USB 2.0 data transfers. - It has nine configurable endpoints, and endpoint zero. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "r8a66597_udc" and force all - gadget drivers to also be dynamically linked. - -config USB_RENESAS_USBHS_UDC - tristate 'Renesas USBHS controller' - depends on USB_RENESAS_USBHS - help - Renesas USBHS is a discrete USB host and peripheral controller chip - that supports both full and high speed USB 2.0 data transfers. - It has nine or more configurable endpoints, and endpoint zero. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "renesas_usbhs" and force all - gadget drivers to also be dynamically linked. - -config USB_PXA27X - tristate "PXA 27x" - help - Intel's PXA 27x series XScale ARM v5TE processors include - an integrated full speed USB 1.1 device controller. - - It has up to 23 endpoints, as well as endpoint zero (for - control transfers). - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "pxa27x_udc" and force all - gadget drivers to also be dynamically linked. - -config USB_S3C2410 - tristate "S3C2410 USB Device Controller" - depends on ARCH_S3C24XX - help - Samsung's S3C2410 is an ARM-4 processor with an integrated - full speed USB 1.1 device controller. It has 4 configurable - endpoints, as well as endpoint zero (for control transfers). - - This driver has been tested on the S3C2410, S3C2412, and - S3C2440 processors. - -config USB_S3C2410_DEBUG - boolean "S3C2410 udc debug messages" - depends on USB_S3C2410 - -config USB_S3C_HSUDC - tristate "S3C2416, S3C2443 and S3C2450 USB Device Controller" - depends on ARCH_S3C24XX - help - Samsung's S3C2416, S3C2443 and S3C2450 is an ARM9 based SoC - integrated with dual speed USB 2.0 device controller. It has - 8 endpoints, as well as endpoint zero. - - This driver has been tested on S3C2416 and S3C2450 processors. - -config USB_MV_UDC - tristate "Marvell USB2.0 Device Controller" - depends on HAS_DMA - help - Marvell Socs (including PXA and MMP series) include a high speed - USB2.0 OTG controller, which can be configured as high speed or - full speed USB peripheral. - -config USB_MV_U3D - depends on HAS_DMA - tristate "MARVELL PXA2128 USB 3.0 controller" - help - MARVELL PXA2128 Processor series include a super speed USB3.0 device - controller, which support super speed USB peripheral. - -# -# Controllers available in both integrated and discrete versions -# - -config USB_M66592 - tristate "Renesas M66592 USB Peripheral Controller" - help - M66592 is a discrete USB peripheral controller chip that - supports both full and high speed USB 2.0 data transfers. - It has seven configurable endpoints, and endpoint zero. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "m66592_udc" and force all - gadget drivers to also be dynamically linked. - -# -# Controllers available only in discrete form (and all PCI controllers) -# - -config USB_AMD5536UDC - tristate "AMD5536 UDC" - depends on PCI - help - The AMD5536 UDC is part of the AMD Geode CS5536, an x86 southbridge. - It is a USB Highspeed DMA capable USB device controller. Beside ep0 - it provides 4 IN and 4 OUT endpoints (bulk or interrupt type). - The UDC port supports OTG operation, and may be used as a host port - if it's not being used to implement peripheral or OTG roles. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "amd5536udc" and force all - gadget drivers to also be dynamically linked. - -config USB_FSL_QE - tristate "Freescale QE/CPM USB Device Controller" - depends on FSL_SOC && (QUICC_ENGINE || CPM) - help - Some of Freescale PowerPC processors have a Full Speed - QE/CPM2 USB controller, which support device mode with 4 - programmable endpoints. This driver supports the - controller in the MPC8360 and MPC8272, and should work with - controllers having QE or CPM2, given minor tweaks. - - Set CONFIG_USB_GADGET to "m" to build this driver as a - dynamically linked module called "fsl_qe_udc". - -config USB_NET2272 - tristate "PLX NET2272" - help - PLX NET2272 is a USB peripheral controller which supports - both full and high speed USB 2.0 data transfers. - - It has three configurable endpoints, as well as endpoint zero - (for control transfer). - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "net2272" and force all - gadget drivers to also be dynamically linked. - -config USB_NET2272_DMA - boolean "Support external DMA controller" - depends on USB_NET2272 && HAS_DMA - help - The NET2272 part can optionally support an external DMA - controller, but your board has to have support in the - driver itself. - - If unsure, say "N" here. The driver works fine in PIO mode. - -config USB_NET2280 - tristate "NetChip 228x" - depends on PCI - help - NetChip 2280 / 2282 is a PCI based USB peripheral controller which - supports both full and high speed USB 2.0 data transfers. - - It has six configurable endpoints, as well as endpoint zero - (for control transfers) and several endpoints with dedicated - functions. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "net2280" and force all - gadget drivers to also be dynamically linked. - -config USB_GOKU - tristate "Toshiba TC86C001 'Goku-S'" - depends on PCI - help - The Toshiba TC86C001 is a PCI device which includes controllers - for full speed USB devices, IDE, I2C, SIO, plus a USB host (OHCI). - - The device controller has three configurable (bulk or interrupt) - endpoints, plus endpoint zero (for control transfers). - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "goku_udc" and to force all - gadget drivers to also be dynamically linked. - -config USB_EG20T - tristate "Intel EG20T PCH/LAPIS Semiconductor IOH(ML7213/ML7831) UDC" - depends on PCI - help - This is a USB device driver for EG20T PCH. - EG20T PCH is the platform controller hub that is used in Intel's - general embedded platform. EG20T PCH has USB device interface. - Using this interface, it is able to access system devices connected - to USB device. - This driver enables USB device function. - USB device is a USB peripheral controller which - supports both full and high speed USB 2.0 data transfers. - This driver supports both control transfer and bulk transfer modes. - This driver dose not support interrupt transfer or isochronous - transfer modes. - - This driver also can be used for LAPIS Semiconductor's ML7213 which is - for IVI(In-Vehicle Infotainment) use. - ML7831 is for general purpose use. - ML7213/ML7831 is companion chip for Intel Atom E6xx series. - ML7213/ML7831 is completely compatible for Intel EG20T PCH. - -# -# LAST -- dummy/emulated controller -# - -config USB_DUMMY_HCD - tristate "Dummy HCD (DEVELOPMENT)" - depends on USB=y || (USB=m && USB_GADGET=m) - help - This host controller driver emulates USB, looping all data transfer - requests back to a USB "gadget driver" in the same host. The host - side is the master; the gadget side is the slave. Gadget drivers - can be high, full, or low speed; and they have access to endpoints - like those from NET2280, PXA2xx, or SA1100 hardware. - - This may help in some stages of creating a driver to embed in a - Linux device, since it lets you debug several parts of the gadget - driver without its hardware or drivers being involved. - - Since such a gadget side driver needs to interoperate with a host - side Linux-USB device driver, this may help to debug both sides - of a USB protocol stack. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "dummy_hcd" and force all - gadget drivers to also be dynamically linked. - -# NOTE: Please keep dummy_hcd LAST so that "real hardware" appears -# first and will be selected by default. - -endmenu +source "drivers/usb/gadget/udc/Kconfig" # # USB Gadget Drivers @@ -714,466 +353,7 @@ config USB_CONFIGFS_F_FS implemented in kernel space (for instance Ethernet, serial or mass storage) and other are implemented in user space. -config USB_ZERO - tristate "Gadget Zero (DEVELOPMENT)" - select USB_LIBCOMPOSITE - select USB_F_SS_LB - help - Gadget Zero is a two-configuration device. It either sinks and - sources bulk data; or it loops back a configurable number of - transfers. It also implements control requests, for "chapter 9" - conformance. The driver needs only two bulk-capable endpoints, so - it can work on top of most device-side usb controllers. It's - useful for testing, and is also a working example showing how - USB "gadget drivers" can be written. - - Make this be the first driver you try using on top of any new - USB peripheral controller driver. Then you can use host-side - test software, like the "usbtest" driver, to put your hardware - and its driver through a basic set of functional tests. - - Gadget Zero also works with the host-side "usb-skeleton" driver, - and with many kinds of host-side test software. You may need - to tweak product and vendor IDs before host software knows about - this device, and arrange to select an appropriate configuration. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "g_zero". - -config USB_ZERO_HNPTEST - boolean "HNP Test Device" - depends on USB_ZERO && USB_OTG - help - You can configure this device to enumerate using the device - identifiers of the USB-OTG test device. That means that when - this gadget connects to another OTG device, with this one using - the "B-Peripheral" role, that device will use HNP to let this - one serve as the USB host instead (in the "B-Host" role). - -config USB_AUDIO - tristate "Audio Gadget" - depends on SND - select USB_LIBCOMPOSITE - select SND_PCM - help - This Gadget Audio driver is compatible with USB Audio Class - specification 2.0. It implements 1 AudioControl interface, - 1 AudioStreaming Interface each for USB-OUT and USB-IN. - Number of channels, sample rate and sample size can be - specified as module parameters. - This driver doesn't expect any real Audio codec to be present - on the device - the audio streams are simply sinked to and - sourced from a virtual ALSA sound card created. The user-space - application may choose to do whatever it wants with the data - received from the USB Host and choose to provide whatever it - wants as audio data to the USB Host. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "g_audio". - -config GADGET_UAC1 - bool "UAC 1.0 (Legacy)" - depends on USB_AUDIO - help - If you instead want older UAC Spec-1.0 driver that also has audio - paths hardwired to the Audio codec chip on-board and doesn't work - without one. - -config USB_ETH - tristate "Ethernet Gadget (with CDC Ethernet support)" - depends on NET - select USB_LIBCOMPOSITE - select USB_U_ETHER - select USB_F_ECM - select USB_F_SUBSET - select CRC32 - help - This driver implements Ethernet style communication, in one of - several ways: - - - The "Communication Device Class" (CDC) Ethernet Control Model. - That protocol is often avoided with pure Ethernet adapters, in - favor of simpler vendor-specific hardware, but is widely - supported by firmware for smart network devices. - - - On hardware can't implement that protocol, a simple CDC subset - is used, placing fewer demands on USB. - - - CDC Ethernet Emulation Model (EEM) is a newer standard that has - a simpler interface that can be used by more USB hardware. - - RNDIS support is an additional option, more demanding than than - subset. - - Within the USB device, this gadget driver exposes a network device - "usbX", where X depends on what other networking devices you have. - Treat it like a two-node Ethernet link: host, and gadget. - - The Linux-USB host-side "usbnet" driver interoperates with this - driver, so that deep I/O queues can be supported. On 2.4 kernels, - use "CDCEther" instead, if you're using the CDC option. That CDC - mode should also interoperate with standard CDC Ethernet class - drivers on other host operating systems. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "g_ether". - -config USB_ETH_RNDIS - bool "RNDIS support" - depends on USB_ETH - select USB_LIBCOMPOSITE - select USB_F_RNDIS - default y - help - Microsoft Windows XP bundles the "Remote NDIS" (RNDIS) protocol, - and Microsoft provides redistributable binary RNDIS drivers for - older versions of Windows. - - If you say "y" here, the Ethernet gadget driver will try to provide - a second device configuration, supporting RNDIS to talk to such - Microsoft USB hosts. - - To make MS-Windows work with this, use Documentation/usb/linux.inf - as the "driver info file". For versions of MS-Windows older than - XP, you'll need to download drivers from Microsoft's website; a URL - is given in comments found in that info file. - -config USB_ETH_EEM - bool "Ethernet Emulation Model (EEM) support" - depends on USB_ETH - select USB_LIBCOMPOSITE - select USB_F_EEM - default n - help - CDC EEM is a newer USB standard that is somewhat simpler than CDC ECM - and therefore can be supported by more hardware. Technically ECM and - EEM are designed for different applications. The ECM model extends - the network interface to the target (e.g. a USB cable modem), and the - EEM model is for mobile devices to communicate with hosts using - ethernet over USB. For Linux gadgets, however, the interface with - the host is the same (a usbX device), so the differences are minimal. - - If you say "y" here, the Ethernet gadget driver will use the EEM - protocol rather than ECM. If unsure, say "n". - -config USB_G_NCM - tristate "Network Control Model (NCM) support" - depends on NET - select USB_LIBCOMPOSITE - select USB_U_ETHER - select USB_F_NCM - select CRC32 - help - This driver implements USB CDC NCM subclass standard. NCM is - an advanced protocol for Ethernet encapsulation, allows grouping - of several ethernet frames into one USB transfer and different - alignment possibilities. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "g_ncm". - -config USB_GADGETFS - tristate "Gadget Filesystem" - help - This driver provides a filesystem based API that lets user mode - programs implement a single-configuration USB device, including - endpoint I/O and control requests that don't relate to enumeration. - All endpoints, transfer speeds, and transfer types supported by - the hardware are available, through read() and write() calls. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "gadgetfs". - -config USB_FUNCTIONFS - tristate "Function Filesystem" - select USB_LIBCOMPOSITE - select USB_F_FS - select USB_FUNCTIONFS_GENERIC if !(USB_FUNCTIONFS_ETH || USB_FUNCTIONFS_RNDIS) - help - The Function Filesystem (FunctionFS) lets one create USB - composite functions in user space in the same way GadgetFS - lets one create USB gadgets in user space. This allows creation - of composite gadgets such that some of the functions are - implemented in kernel space (for instance Ethernet, serial or - mass storage) and other are implemented in user space. - - If you say "y" or "m" here you will be able what kind of - configurations the gadget will provide. - - Say "y" to link the driver statically, or "m" to build - a dynamically linked module called "g_ffs". - -config USB_FUNCTIONFS_ETH - bool "Include configuration with CDC ECM (Ethernet)" - depends on USB_FUNCTIONFS && NET - select USB_U_ETHER - select USB_F_ECM - select USB_F_SUBSET - help - Include a configuration with CDC ECM function (Ethernet) and the - Function Filesystem. - -config USB_FUNCTIONFS_RNDIS - bool "Include configuration with RNDIS (Ethernet)" - depends on USB_FUNCTIONFS && NET - select USB_U_ETHER - select USB_F_RNDIS - help - Include a configuration with RNDIS function (Ethernet) and the Filesystem. - -config USB_FUNCTIONFS_GENERIC - bool "Include 'pure' configuration" - depends on USB_FUNCTIONFS - help - Include a configuration with the Function Filesystem alone with - no Ethernet interface. - -config USB_MASS_STORAGE - tristate "Mass Storage Gadget" - depends on BLOCK - select USB_LIBCOMPOSITE - select USB_F_MASS_STORAGE - help - The Mass Storage Gadget acts as a USB Mass Storage disk drive. - As its storage repository it can use a regular file or a block - device (in much the same way as the "loop" device driver), - specified as a module parameter or sysfs option. - - This driver is a replacement for now removed File-backed - Storage Gadget (g_file_storage). - - Say "y" to link the driver statically, or "m" to build - a dynamically linked module called "g_mass_storage". - -config USB_GADGET_TARGET - tristate "USB Gadget Target Fabric Module" - depends on TARGET_CORE - select USB_LIBCOMPOSITE - help - This fabric is an USB gadget. Two USB protocols are supported that is - BBB or BOT (Bulk Only Transport) and UAS (USB Attached SCSI). BOT is - advertised on alternative interface 0 (primary) and UAS is on - alternative interface 1. Both protocols can work on USB2.0 and USB3.0. - UAS utilizes the USB 3.0 feature called streams support. - -config USB_G_SERIAL - tristate "Serial Gadget (with CDC ACM and CDC OBEX support)" - depends on TTY - select USB_U_SERIAL - select USB_F_ACM - select USB_F_SERIAL - select USB_F_OBEX - select USB_LIBCOMPOSITE - help - The Serial Gadget talks to the Linux-USB generic serial driver. - This driver supports a CDC-ACM module option, which can be used - to interoperate with MS-Windows hosts or with the Linux-USB - "cdc-acm" driver. - - This driver also supports a CDC-OBEX option. You will need a - user space OBEX server talking to /dev/ttyGS*, since the kernel - itself doesn't implement the OBEX protocol. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "g_serial". - - For more information, see Documentation/usb/gadget_serial.txt - which includes instructions and a "driver info file" needed to - make MS-Windows work with CDC ACM. - -config USB_MIDI_GADGET - tristate "MIDI Gadget" - depends on SND - select USB_LIBCOMPOSITE - select SND_RAWMIDI - help - The MIDI Gadget acts as a USB Audio device, with one MIDI - input and one MIDI output. These MIDI jacks appear as - a sound "card" in the ALSA sound system. Other MIDI - connections can then be made on the gadget system, using - ALSA's aconnect utility etc. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "g_midi". - -config USB_G_PRINTER - tristate "Printer Gadget" - select USB_LIBCOMPOSITE - help - The Printer Gadget channels data between the USB host and a - userspace program driving the print engine. The user space - program reads and writes the device file /dev/g_printer to - receive or send printer data. It can use ioctl calls to - the device file to get or set printer status. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "g_printer". - - For more information, see Documentation/usb/gadget_printer.txt - which includes sample code for accessing the device file. - -if TTY - -config USB_CDC_COMPOSITE - tristate "CDC Composite Device (Ethernet and ACM)" - depends on NET - select USB_LIBCOMPOSITE - select USB_U_SERIAL - select USB_U_ETHER - select USB_F_ACM - select USB_F_ECM - help - This driver provides two functions in one configuration: - a CDC Ethernet (ECM) link, and a CDC ACM (serial port) link. - - This driver requires four bulk and two interrupt endpoints, - plus the ability to handle altsettings. Not all peripheral - controllers are that capable. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module. - -config USB_G_NOKIA - tristate "Nokia composite gadget" - depends on PHONET - select USB_LIBCOMPOSITE - select USB_U_SERIAL - select USB_U_ETHER - select USB_F_ACM - select USB_F_OBEX - select USB_F_PHONET - select USB_F_ECM - help - The Nokia composite gadget provides support for acm, obex - and phonet in only one composite gadget driver. - - It's only really useful for N900 hardware. If you're building - a kernel for N900, say Y or M here. If unsure, say N. - -config USB_G_ACM_MS - tristate "CDC Composite Device (ACM and mass storage)" - depends on BLOCK - select USB_LIBCOMPOSITE - select USB_U_SERIAL - select USB_F_ACM - select USB_F_MASS_STORAGE - help - This driver provides two functions in one configuration: - a mass storage, and a CDC ACM (serial port) link. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "g_acm_ms". - -config USB_G_MULTI - tristate "Multifunction Composite Gadget" - depends on BLOCK && NET - select USB_G_MULTI_CDC if !USB_G_MULTI_RNDIS - select USB_LIBCOMPOSITE - select USB_U_SERIAL - select USB_U_ETHER - select USB_F_ACM - select USB_F_MASS_STORAGE - help - The Multifunction Composite Gadget provides Ethernet (RNDIS - and/or CDC Ethernet), mass storage and ACM serial link - interfaces. - - You will be asked to choose which of the two configurations is - to be available in the gadget. At least one configuration must - be chosen to make the gadget usable. Selecting more than one - configuration will prevent Windows from automatically detecting - the gadget as a composite gadget, so an INF file will be needed to - use the gadget. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "g_multi". - -config USB_G_MULTI_RNDIS - bool "RNDIS + CDC Serial + Storage configuration" - depends on USB_G_MULTI - select USB_F_RNDIS - default y - help - This option enables a configuration with RNDIS, CDC Serial and - Mass Storage functions available in the Multifunction Composite - Gadget. This is the configuration dedicated for Windows since RNDIS - is Microsoft's protocol. - - If unsure, say "y". - -config USB_G_MULTI_CDC - bool "CDC Ethernet + CDC Serial + Storage configuration" - depends on USB_G_MULTI - default n - select USB_F_ECM - help - This option enables a configuration with CDC Ethernet (ECM), CDC - Serial and Mass Storage functions available in the Multifunction - Composite Gadget. - - If unsure, say "y". - -endif # TTY - -config USB_G_HID - tristate "HID Gadget" - select USB_LIBCOMPOSITE - help - The HID gadget driver provides generic emulation of USB - Human Interface Devices (HID). - - For more information, see Documentation/usb/gadget_hid.txt which - includes sample code for accessing the device files. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "g_hid". - -# Standalone / single function gadgets -config USB_G_DBGP - tristate "EHCI Debug Device Gadget" - depends on TTY - select USB_LIBCOMPOSITE - help - This gadget emulates an EHCI Debug device. This is useful when you want - to interact with an EHCI Debug Port. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "g_dbgp". - -if USB_G_DBGP -choice - prompt "EHCI Debug Device mode" - default USB_G_DBGP_SERIAL - -config USB_G_DBGP_PRINTK - depends on USB_G_DBGP - bool "printk" - help - Directly printk() received data. No interaction. - -config USB_G_DBGP_SERIAL - depends on USB_G_DBGP - select USB_U_SERIAL - bool "serial" - help - Userland can interact using /dev/ttyGSxxx. -endchoice -endif - -# put drivers that need isochronous transfer support (for audio -# or video class gadget drivers), or specific hardware, here. -config USB_G_WEBCAM - tristate "USB Webcam Gadget" - depends on VIDEO_DEV - select USB_LIBCOMPOSITE - select VIDEOBUF2_VMALLOC - help - The Webcam Gadget acts as a composite USB Audio and Video Class - device. It provides a userspace API to process UVC control requests - and stream video data to the host. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "g_webcam". +source "drivers/usb/gadget/legacy/Kconfig" endchoice diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 49514ea60a98..a186afeaa700 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -1,105 +1,12 @@ # # USB peripheral controller drivers # -ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG -ccflags-$(CONFIG_USB_GADGET_VERBOSE) += -DVERBOSE_DEBUG +subdir-ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG +subdir-ccflags-$(CONFIG_USB_GADGET_VERBOSE) += -DVERBOSE_DEBUG +ccflags-y += -I$(PWD)/drivers/usb/gadget/udc -obj-$(CONFIG_USB_GADGET) += udc-core.o obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o libcomposite-y := usbstring.o config.o epautoconf.o libcomposite-y += composite.o functions.o configfs.o u_f.o -obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o -obj-$(CONFIG_USB_NET2272) += net2272.o -obj-$(CONFIG_USB_NET2280) += net2280.o -obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc.o -obj-$(CONFIG_USB_PXA25X) += pxa25x_udc.o -obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o -obj-$(CONFIG_USB_GOKU) += goku_udc.o -obj-$(CONFIG_USB_OMAP) += omap_udc.o -obj-$(CONFIG_USB_S3C2410) += s3c2410_udc.o -obj-$(CONFIG_USB_AT91) += at91_udc.o -obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o -obj-$(CONFIG_USB_BCM63XX_UDC) += bcm63xx_udc.o -obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o -fsl_usb2_udc-y := fsl_udc_core.o -fsl_usb2_udc-$(CONFIG_ARCH_MXC) += fsl_mxc_udc.o -obj-$(CONFIG_USB_M66592) += m66592-udc.o -obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o -obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o -obj-$(CONFIG_USB_S3C_HSUDC) += s3c-hsudc.o -obj-$(CONFIG_USB_LPC32XX) += lpc32xx_udc.o -obj-$(CONFIG_USB_EG20T) += pch_udc.o -obj-$(CONFIG_USB_MV_UDC) += mv_udc.o -mv_udc-y := mv_udc_core.o -obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o -obj-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o -obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o -obj-$(CONFIG_USB_GR_UDC) += gr_udc.o -# USB Functions -usb_f_acm-y := f_acm.o -obj-$(CONFIG_USB_F_ACM) += usb_f_acm.o -usb_f_ss_lb-y := f_loopback.o f_sourcesink.o -obj-$(CONFIG_USB_F_SS_LB) += usb_f_ss_lb.o -obj-$(CONFIG_USB_U_SERIAL) += u_serial.o -usb_f_serial-y := f_serial.o -obj-$(CONFIG_USB_F_SERIAL) += usb_f_serial.o -usb_f_obex-y := f_obex.o -obj-$(CONFIG_USB_F_OBEX) += usb_f_obex.o -obj-$(CONFIG_USB_U_ETHER) += u_ether.o -usb_f_ncm-y := f_ncm.o -obj-$(CONFIG_USB_F_NCM) += usb_f_ncm.o -usb_f_ecm-y := f_ecm.o -obj-$(CONFIG_USB_F_ECM) += usb_f_ecm.o -usb_f_phonet-y := f_phonet.o -obj-$(CONFIG_USB_F_PHONET) += usb_f_phonet.o -usb_f_eem-y := f_eem.o -obj-$(CONFIG_USB_F_EEM) += usb_f_eem.o -usb_f_ecm_subset-y := f_subset.o -obj-$(CONFIG_USB_F_SUBSET) += usb_f_ecm_subset.o -usb_f_rndis-y := f_rndis.o rndis.o -obj-$(CONFIG_USB_F_RNDIS) += usb_f_rndis.o -usb_f_mass_storage-y := f_mass_storage.o storage_common.o -obj-$(CONFIG_USB_F_MASS_STORAGE)+= usb_f_mass_storage.o -usb_f_fs-y := f_fs.o -obj-$(CONFIG_USB_F_FS) += usb_f_fs.o - -# -# USB gadget drivers -# -g_zero-y := zero.o -g_audio-y := audio.o -g_ether-y := ether.o -g_serial-y := serial.o -g_midi-y := gmidi.o -gadgetfs-y := inode.o -g_mass_storage-y := mass_storage.o -g_printer-y := printer.o -g_cdc-y := cdc2.o -g_multi-y := multi.o -g_hid-y := hid.o -g_dbgp-y := dbgp.o -g_nokia-y := nokia.o -g_webcam-y := webcam.o -g_ncm-y := ncm.o -g_acm_ms-y := acm_ms.o -g_tcm_usb_gadget-y := tcm_usb_gadget.o - -obj-$(CONFIG_USB_ZERO) += g_zero.o -obj-$(CONFIG_USB_AUDIO) += g_audio.o -obj-$(CONFIG_USB_ETH) += g_ether.o -obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o -obj-$(CONFIG_USB_FUNCTIONFS) += g_ffs.o -obj-$(CONFIG_USB_MASS_STORAGE) += g_mass_storage.o -obj-$(CONFIG_USB_G_SERIAL) += g_serial.o -obj-$(CONFIG_USB_G_PRINTER) += g_printer.o -obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o -obj-$(CONFIG_USB_CDC_COMPOSITE) += g_cdc.o -obj-$(CONFIG_USB_G_HID) += g_hid.o -obj-$(CONFIG_USB_G_DBGP) += g_dbgp.o -obj-$(CONFIG_USB_G_MULTI) += g_multi.o -obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o -obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o -obj-$(CONFIG_USB_G_NCM) += g_ncm.o -obj-$(CONFIG_USB_G_ACM_MS) += g_acm_ms.o -obj-$(CONFIG_USB_GADGET_TARGET) += tcm_usb_gadget.o +obj-$(CONFIG_USB_GADGET) += udc/ function/ legacy/ diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index f80151932053..6935a822ce2b 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1956,6 +1956,7 @@ void composite_dev_cleanup(struct usb_composite_dev *cdev) } if (cdev->req) { kfree(cdev->req->buf); + usb_ep_dequeue(cdev->gadget->ep0, cdev->req); usb_ep_free_request(cdev->gadget->ep0, cdev->req); } cdev->next_string_id = 0; diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 97142146eead..811c2c7cc269 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -1021,12 +1021,10 @@ static ssize_t ext_prop_data_store(struct usb_os_desc_ext_prop *ext_prop, if (page[len - 1] == '\n' || page[len - 1] == '\0') --len; - new_data = kzalloc(len, GFP_KERNEL); + new_data = kmemdup(page, len, GFP_KERNEL); if (!new_data) return -ENOMEM; - memcpy(new_data, page, len); - if (desc->opts_mutex) mutex_lock(desc->opts_mutex); kfree(ext_prop->data); diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile new file mode 100644 index 000000000000..6d91f21b52a6 --- /dev/null +++ b/drivers/usb/gadget/function/Makefile @@ -0,0 +1,34 @@ +# +# USB peripheral controller drivers +# + +ccflags-y := -I$(PWD)/drivers/usb/gadget/ +ccflags-y += -I$(PWD)/drivers/usb/gadget/udc/ + +# USB Functions +usb_f_acm-y := f_acm.o +obj-$(CONFIG_USB_F_ACM) += usb_f_acm.o +usb_f_ss_lb-y := f_loopback.o f_sourcesink.o +obj-$(CONFIG_USB_F_SS_LB) += usb_f_ss_lb.o +obj-$(CONFIG_USB_U_SERIAL) += u_serial.o +usb_f_serial-y := f_serial.o +obj-$(CONFIG_USB_F_SERIAL) += usb_f_serial.o +usb_f_obex-y := f_obex.o +obj-$(CONFIG_USB_F_OBEX) += usb_f_obex.o +obj-$(CONFIG_USB_U_ETHER) += u_ether.o +usb_f_ncm-y := f_ncm.o +obj-$(CONFIG_USB_F_NCM) += usb_f_ncm.o +usb_f_ecm-y := f_ecm.o +obj-$(CONFIG_USB_F_ECM) += usb_f_ecm.o +usb_f_phonet-y := f_phonet.o +obj-$(CONFIG_USB_F_PHONET) += usb_f_phonet.o +usb_f_eem-y := f_eem.o +obj-$(CONFIG_USB_F_EEM) += usb_f_eem.o +usb_f_ecm_subset-y := f_subset.o +obj-$(CONFIG_USB_F_SUBSET) += usb_f_ecm_subset.o +usb_f_rndis-y := f_rndis.o rndis.o +obj-$(CONFIG_USB_F_RNDIS) += usb_f_rndis.o +usb_f_mass_storage-y := f_mass_storage.o storage_common.o +obj-$(CONFIG_USB_F_MASS_STORAGE)+= usb_f_mass_storage.o +usb_f_fs-y := f_fs.o +obj-$(CONFIG_USB_F_FS) += usb_f_fs.o diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/function/f_acm.c index ab1065afbbd0..ab1065afbbd0 100644 --- a/drivers/usb/gadget/f_acm.c +++ b/drivers/usb/gadget/function/f_acm.c diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/function/f_ecm.c index 798760fa7e70..798760fa7e70 100644 --- a/drivers/usb/gadget/f_ecm.c +++ b/drivers/usb/gadget/function/f_ecm.c diff --git a/drivers/usb/gadget/f_eem.c b/drivers/usb/gadget/function/f_eem.c index d61c11d765d0..4d8b236ea608 100644 --- a/drivers/usb/gadget/f_eem.c +++ b/drivers/usb/gadget/function/f_eem.c @@ -355,20 +355,18 @@ static struct sk_buff *eem_wrap(struct gether *port, struct sk_buff *skb) int padlen = 0; u16 len = skb->len; - if (!skb_cloned(skb)) { - int headroom = skb_headroom(skb); - int tailroom = skb_tailroom(skb); + int headroom = skb_headroom(skb); + int tailroom = skb_tailroom(skb); - /* When (len + EEM_HLEN + ETH_FCS_LEN) % in->maxpacket) is 0, - * stick two bytes of zero-length EEM packet on the end. - */ - if (((len + EEM_HLEN + ETH_FCS_LEN) % in->maxpacket) == 0) - padlen += 2; + /* When (len + EEM_HLEN + ETH_FCS_LEN) % in->maxpacket) is 0, + * stick two bytes of zero-length EEM packet on the end. + */ + if (((len + EEM_HLEN + ETH_FCS_LEN) % in->maxpacket) == 0) + padlen += 2; - if ((tailroom >= (ETH_FCS_LEN + padlen)) && - (headroom >= EEM_HLEN)) - goto done; - } + if ((tailroom >= (ETH_FCS_LEN + padlen)) && + (headroom >= EEM_HLEN) && !skb_cloned(skb)) + goto done; skb2 = skb_copy_expand(skb, EEM_HLEN, ETH_FCS_LEN + padlen, GFP_ATOMIC); dev_kfree_skb_any(skb); diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 8598c27c7d43..dc30adf15a01 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -34,6 +34,7 @@ #include "u_fs.h" #include "u_f.h" +#include "u_os_desc.h" #include "configfs.h" #define FUNCTIONFS_MAGIC 0xa647361 /* Chosen by a honest dice roll ;) */ @@ -1646,13 +1647,22 @@ enum ffs_entity_type { FFS_DESCRIPTOR, FFS_INTERFACE, FFS_STRING, FFS_ENDPOINT }; +enum ffs_os_desc_type { + FFS_OS_DESC, FFS_OS_DESC_EXT_COMPAT, FFS_OS_DESC_EXT_PROP +}; + typedef int (*ffs_entity_callback)(enum ffs_entity_type entity, u8 *valuep, struct usb_descriptor_header *desc, void *priv); -static int __must_check ffs_do_desc(char *data, unsigned len, - ffs_entity_callback entity, void *priv) +typedef int (*ffs_os_desc_callback)(enum ffs_os_desc_type entity, + struct usb_os_desc_header *h, void *data, + unsigned len, void *priv); + +static int __must_check ffs_do_single_desc(char *data, unsigned len, + ffs_entity_callback entity, + void *priv) { struct usb_descriptor_header *_ds = (void *)data; u8 length; @@ -1804,7 +1814,7 @@ static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len, if (!data) return _len - len; - ret = ffs_do_desc(data, len, entity, priv); + ret = ffs_do_single_desc(data, len, entity, priv); if (unlikely(ret < 0)) { pr_debug("%s returns %d\n", __func__, ret); return ret; @@ -1857,11 +1867,191 @@ static int __ffs_data_do_entity(enum ffs_entity_type type, return 0; } +static int __ffs_do_os_desc_header(enum ffs_os_desc_type *next_type, + struct usb_os_desc_header *desc) +{ + u16 bcd_version = le16_to_cpu(desc->bcdVersion); + u16 w_index = le16_to_cpu(desc->wIndex); + + if (bcd_version != 1) { + pr_vdebug("unsupported os descriptors version: %d", + bcd_version); + return -EINVAL; + } + switch (w_index) { + case 0x4: + *next_type = FFS_OS_DESC_EXT_COMPAT; + break; + case 0x5: + *next_type = FFS_OS_DESC_EXT_PROP; + break; + default: + pr_vdebug("unsupported os descriptor type: %d", w_index); + return -EINVAL; + } + + return sizeof(*desc); +} + +/* + * Process all extended compatibility/extended property descriptors + * of a feature descriptor + */ +static int __must_check ffs_do_single_os_desc(char *data, unsigned len, + enum ffs_os_desc_type type, + u16 feature_count, + ffs_os_desc_callback entity, + void *priv, + struct usb_os_desc_header *h) +{ + int ret; + const unsigned _len = len; + + ENTER(); + + /* loop over all ext compat/ext prop descriptors */ + while (feature_count--) { + ret = entity(type, h, data, len, priv); + if (unlikely(ret < 0)) { + pr_debug("bad OS descriptor, type: %d\n", type); + return ret; + } + data += ret; + len -= ret; + } + return _len - len; +} + +/* Process a number of complete Feature Descriptors (Ext Compat or Ext Prop) */ +static int __must_check ffs_do_os_descs(unsigned count, + char *data, unsigned len, + ffs_os_desc_callback entity, void *priv) +{ + const unsigned _len = len; + unsigned long num = 0; + + ENTER(); + + for (num = 0; num < count; ++num) { + int ret; + enum ffs_os_desc_type type; + u16 feature_count; + struct usb_os_desc_header *desc = (void *)data; + + if (len < sizeof(*desc)) + return -EINVAL; + + /* + * Record "descriptor" entity. + * Process dwLength, bcdVersion, wIndex, get b/wCount. + * Move the data pointer to the beginning of extended + * compatibilities proper or extended properties proper + * portions of the data + */ + if (le32_to_cpu(desc->dwLength) > len) + return -EINVAL; + + ret = __ffs_do_os_desc_header(&type, desc); + if (unlikely(ret < 0)) { + pr_debug("entity OS_DESCRIPTOR(%02lx); ret = %d\n", + num, ret); + return ret; + } + /* + * 16-bit hex "?? 00" Little Endian looks like 8-bit hex "??" + */ + feature_count = le16_to_cpu(desc->wCount); + if (type == FFS_OS_DESC_EXT_COMPAT && + (feature_count > 255 || desc->Reserved)) + return -EINVAL; + len -= ret; + data += ret; + + /* + * Process all function/property descriptors + * of this Feature Descriptor + */ + ret = ffs_do_single_os_desc(data, len, type, + feature_count, entity, priv, desc); + if (unlikely(ret < 0)) { + pr_debug("%s returns %d\n", __func__, ret); + return ret; + } + + len -= ret; + data += ret; + } + return _len - len; +} + +/** + * Validate contents of the buffer from userspace related to OS descriptors. + */ +static int __ffs_data_do_os_desc(enum ffs_os_desc_type type, + struct usb_os_desc_header *h, void *data, + unsigned len, void *priv) +{ + struct ffs_data *ffs = priv; + u8 length; + + ENTER(); + + switch (type) { + case FFS_OS_DESC_EXT_COMPAT: { + struct usb_ext_compat_desc *d = data; + int i; + + if (len < sizeof(*d) || + d->bFirstInterfaceNumber >= ffs->interfaces_count || + d->Reserved1) + return -EINVAL; + for (i = 0; i < ARRAY_SIZE(d->Reserved2); ++i) + if (d->Reserved2[i]) + return -EINVAL; + + length = sizeof(struct usb_ext_compat_desc); + } + break; + case FFS_OS_DESC_EXT_PROP: { + struct usb_ext_prop_desc *d = data; + u32 type, pdl; + u16 pnl; + + if (len < sizeof(*d) || h->interface >= ffs->interfaces_count) + return -EINVAL; + length = le32_to_cpu(d->dwSize); + type = le32_to_cpu(d->dwPropertyDataType); + if (type < USB_EXT_PROP_UNICODE || + type > USB_EXT_PROP_UNICODE_MULTI) { + pr_vdebug("unsupported os descriptor property type: %d", + type); + return -EINVAL; + } + pnl = le16_to_cpu(d->wPropertyNameLength); + pdl = le32_to_cpu(*(u32 *)((u8 *)data + 10 + pnl)); + if (length != 14 + pnl + pdl) { + pr_vdebug("invalid os descriptor length: %d pnl:%d pdl:%d (descriptor %d)\n", + length, pnl, pdl, type); + return -EINVAL; + } + ++ffs->ms_os_descs_ext_prop_count; + /* property name reported to the host as "WCHAR"s */ + ffs->ms_os_descs_ext_prop_name_len += pnl * 2; + ffs->ms_os_descs_ext_prop_data_len += pdl; + } + break; + default: + pr_vdebug("unknown descriptor: %d\n", type); + return -EINVAL; + } + return length; +} + static int __ffs_data_got_descs(struct ffs_data *ffs, char *const _data, size_t len) { char *data = _data, *raw_descs; - unsigned counts[3], flags; + unsigned os_descs_count = 0, counts[3], flags; int ret = -EINVAL, i; ENTER(); @@ -1879,7 +2069,8 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, flags = get_unaligned_le32(data + 8); if (flags & ~(FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC | - FUNCTIONFS_HAS_SS_DESC)) { + FUNCTIONFS_HAS_SS_DESC | + FUNCTIONFS_HAS_MS_OS_DESC)) { ret = -ENOSYS; goto error; } @@ -1902,6 +2093,11 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, len -= 4; } } + if (flags & (1 << i)) { + os_descs_count = get_unaligned_le32(data); + data += 4; + len -= 4; + }; /* Read descriptors */ raw_descs = data; @@ -1915,6 +2111,14 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, data += ret; len -= ret; } + if (os_descs_count) { + ret = ffs_do_os_descs(os_descs_count, data, len, + __ffs_data_do_os_desc, ffs); + if (ret < 0) + goto error; + data += ret; + len -= ret; + } if (raw_descs == data || len) { ret = -EINVAL; @@ -1927,6 +2131,7 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, ffs->fs_descs_count = counts[0]; ffs->hs_descs_count = counts[1]; ffs->ss_descs_count = counts[2]; + ffs->ms_os_descs_count = os_descs_count; return 0; @@ -2268,6 +2473,85 @@ static int __ffs_func_bind_do_nums(enum ffs_entity_type type, u8 *valuep, return 0; } +static int __ffs_func_bind_do_os_desc(enum ffs_os_desc_type type, + struct usb_os_desc_header *h, void *data, + unsigned len, void *priv) +{ + struct ffs_function *func = priv; + u8 length = 0; + + switch (type) { + case FFS_OS_DESC_EXT_COMPAT: { + struct usb_ext_compat_desc *desc = data; + struct usb_os_desc_table *t; + + t = &func->function.os_desc_table[desc->bFirstInterfaceNumber]; + t->if_id = func->interfaces_nums[desc->bFirstInterfaceNumber]; + memcpy(t->os_desc->ext_compat_id, &desc->CompatibleID, + ARRAY_SIZE(desc->CompatibleID) + + ARRAY_SIZE(desc->SubCompatibleID)); + length = sizeof(*desc); + } + break; + case FFS_OS_DESC_EXT_PROP: { + struct usb_ext_prop_desc *desc = data; + struct usb_os_desc_table *t; + struct usb_os_desc_ext_prop *ext_prop; + char *ext_prop_name; + char *ext_prop_data; + + t = &func->function.os_desc_table[h->interface]; + t->if_id = func->interfaces_nums[h->interface]; + + ext_prop = func->ffs->ms_os_descs_ext_prop_avail; + func->ffs->ms_os_descs_ext_prop_avail += sizeof(*ext_prop); + + ext_prop->type = le32_to_cpu(desc->dwPropertyDataType); + ext_prop->name_len = le16_to_cpu(desc->wPropertyNameLength); + ext_prop->data_len = le32_to_cpu(*(u32 *) + usb_ext_prop_data_len_ptr(data, ext_prop->name_len)); + length = ext_prop->name_len + ext_prop->data_len + 14; + + ext_prop_name = func->ffs->ms_os_descs_ext_prop_name_avail; + func->ffs->ms_os_descs_ext_prop_name_avail += + ext_prop->name_len; + + ext_prop_data = func->ffs->ms_os_descs_ext_prop_data_avail; + func->ffs->ms_os_descs_ext_prop_data_avail += + ext_prop->data_len; + memcpy(ext_prop_data, + usb_ext_prop_data_ptr(data, ext_prop->name_len), + ext_prop->data_len); + /* unicode data reported to the host as "WCHAR"s */ + switch (ext_prop->type) { + case USB_EXT_PROP_UNICODE: + case USB_EXT_PROP_UNICODE_ENV: + case USB_EXT_PROP_UNICODE_LINK: + case USB_EXT_PROP_UNICODE_MULTI: + ext_prop->data_len *= 2; + break; + } + ext_prop->data = ext_prop_data; + + memcpy(ext_prop_name, usb_ext_prop_name_ptr(data), + ext_prop->name_len); + /* property name reported to the host as "WCHAR"s */ + ext_prop->name_len *= 2; + ext_prop->name = ext_prop_name; + + t->os_desc->ext_prop_len += + ext_prop->name_len + ext_prop->data_len + 14; + ++t->os_desc->ext_prop_count; + list_add_tail(&ext_prop->entry, &t->os_desc->ext_prop); + } + break; + default: + pr_vdebug("unknown descriptor: %d\n", type); + } + + return length; +} + static inline struct f_fs_opts *ffs_do_functionfs_bind(struct usb_function *f, struct usb_configuration *c) { @@ -2329,7 +2613,7 @@ static int _ffs_func_bind(struct usb_configuration *c, const int super = gadget_is_superspeed(func->gadget) && func->ffs->ss_descs_count; - int fs_len, hs_len, ret; + int fs_len, hs_len, ss_len, ret, i; /* Make it a single chunk, less management later on */ vla_group(d); @@ -2341,6 +2625,18 @@ static int _ffs_func_bind(struct usb_configuration *c, vla_item_with_sz(d, struct usb_descriptor_header *, ss_descs, super ? ffs->ss_descs_count + 1 : 0); vla_item_with_sz(d, short, inums, ffs->interfaces_count); + vla_item_with_sz(d, struct usb_os_desc_table, os_desc_table, + c->cdev->use_os_string ? ffs->interfaces_count : 0); + vla_item_with_sz(d, char[16], ext_compat, + c->cdev->use_os_string ? ffs->interfaces_count : 0); + vla_item_with_sz(d, struct usb_os_desc, os_desc, + c->cdev->use_os_string ? ffs->interfaces_count : 0); + vla_item_with_sz(d, struct usb_os_desc_ext_prop, ext_prop, + ffs->ms_os_descs_ext_prop_count); + vla_item_with_sz(d, char, ext_prop_name, + ffs->ms_os_descs_ext_prop_name_len); + vla_item_with_sz(d, char, ext_prop_data, + ffs->ms_os_descs_ext_prop_data_len); vla_item_with_sz(d, char, raw_descs, ffs->raw_descs_length); char *vlabuf; @@ -2351,12 +2647,16 @@ static int _ffs_func_bind(struct usb_configuration *c, return -ENOTSUPP; /* Allocate a single chunk, less management later on */ - vlabuf = kmalloc(vla_group_size(d), GFP_KERNEL); + vlabuf = kzalloc(vla_group_size(d), GFP_KERNEL); if (unlikely(!vlabuf)) return -ENOMEM; - /* Zero */ - memset(vla_ptr(vlabuf, d, eps), 0, d_eps__sz); + ffs->ms_os_descs_ext_prop_avail = vla_ptr(vlabuf, d, ext_prop); + ffs->ms_os_descs_ext_prop_name_avail = + vla_ptr(vlabuf, d, ext_prop_name); + ffs->ms_os_descs_ext_prop_data_avail = + vla_ptr(vlabuf, d, ext_prop_data); + /* Copy descriptors */ memcpy(vla_ptr(vlabuf, d, raw_descs), ffs->raw_descs, ffs->raw_descs_length); @@ -2410,12 +2710,16 @@ static int _ffs_func_bind(struct usb_configuration *c, if (likely(super)) { func->function.ss_descriptors = vla_ptr(vlabuf, d, ss_descs); - ret = ffs_do_descs(ffs->ss_descs_count, + ss_len = ffs_do_descs(ffs->ss_descs_count, vla_ptr(vlabuf, d, raw_descs) + fs_len + hs_len, d_raw_descs__sz - fs_len - hs_len, __ffs_func_bind_do_descs, func); - if (unlikely(ret < 0)) + if (unlikely(ss_len < 0)) { + ret = ss_len; goto error; + } + } else { + ss_len = 0; } /* @@ -2431,6 +2735,28 @@ static int _ffs_func_bind(struct usb_configuration *c, if (unlikely(ret < 0)) goto error; + func->function.os_desc_table = vla_ptr(vlabuf, d, os_desc_table); + if (c->cdev->use_os_string) + for (i = 0; i < ffs->interfaces_count; ++i) { + struct usb_os_desc *desc; + + desc = func->function.os_desc_table[i].os_desc = + vla_ptr(vlabuf, d, os_desc) + + i * sizeof(struct usb_os_desc); + desc->ext_compat_id = + vla_ptr(vlabuf, d, ext_compat) + i * 16; + INIT_LIST_HEAD(&desc->ext_prop); + } + ret = ffs_do_os_descs(ffs->ms_os_descs_count, + vla_ptr(vlabuf, d, raw_descs) + + fs_len + hs_len + ss_len, + d_raw_descs__sz - fs_len - hs_len - ss_len, + __ffs_func_bind_do_os_desc, func); + if (unlikely(ret < 0)) + goto error; + func->function.os_desc_n = + c->cdev->use_os_string ? ffs->interfaces_count : 0; + /* And we're done */ ffs_event_add(ffs, FUNCTIONFS_BIND); return 0; @@ -2901,12 +3227,12 @@ static void *ffs_acquire_dev(const char *dev_name) ffs_dev = _ffs_find_dev(dev_name); if (!ffs_dev) - ffs_dev = ERR_PTR(-ENODEV); + ffs_dev = ERR_PTR(-ENOENT); else if (ffs_dev->mounted) ffs_dev = ERR_PTR(-EBUSY); else if (ffs_dev->ffs_acquire_dev_callback && ffs_dev->ffs_acquire_dev_callback(ffs_dev)) - ffs_dev = ERR_PTR(-ENODEV); + ffs_dev = ERR_PTR(-ENOENT); else ffs_dev->mounted = true; diff --git a/drivers/usb/gadget/f_hid.c b/drivers/usb/gadget/function/f_hid.c index a95290a1289f..a95290a1289f 100644 --- a/drivers/usb/gadget/f_hid.c +++ b/drivers/usb/gadget/function/f_hid.c diff --git a/drivers/usb/gadget/f_loopback.c b/drivers/usb/gadget/function/f_loopback.c index 4557cd03f0b1..4557cd03f0b1 100644 --- a/drivers/usb/gadget/f_loopback.c +++ b/drivers/usb/gadget/function/f_loopback.c diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index b96393908860..b96393908860 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c diff --git a/drivers/usb/gadget/f_mass_storage.h b/drivers/usb/gadget/function/f_mass_storage.h index b4866fcef30b..b4866fcef30b 100644 --- a/drivers/usb/gadget/f_mass_storage.h +++ b/drivers/usb/gadget/function/f_mass_storage.h diff --git a/drivers/usb/gadget/f_midi.c b/drivers/usb/gadget/function/f_midi.c index 807b31c0edc3..807b31c0edc3 100644 --- a/drivers/usb/gadget/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c diff --git a/drivers/usb/gadget/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index a9499fd30792..bcdc882cd415 100644 --- a/drivers/usb/gadget/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -68,6 +68,18 @@ struct f_ncm { * callback and ethernet open/close */ spinlock_t lock; + + struct net_device *netdev; + + /* For multi-frame NDP TX */ + struct sk_buff *skb_tx_data; + struct sk_buff *skb_tx_ndp; + u16 ndp_dgram_count; + bool timer_force_tx; + struct tasklet_struct tx_tasklet; + struct hrtimer task_timer; + + bool timer_stopping; }; static inline struct f_ncm *func_to_ncm(struct usb_function *f) @@ -92,15 +104,20 @@ static inline unsigned ncm_bitrate(struct usb_gadget *g) * If the host can group frames, allow it to do that, 16K is selected, * because it's used by default by the current linux host driver */ -#define NTB_DEFAULT_IN_SIZE USB_CDC_NCM_NTB_MIN_IN_SIZE +#define NTB_DEFAULT_IN_SIZE 16384 #define NTB_OUT_SIZE 16384 -/* - * skbs of size less than that will not be aligned - * to NCM's dwNtbInMaxSize to save bus bandwidth +/* Allocation for storing the NDP, 32 should suffice for a + * 16k packet. This allows a maximum of 32 * 507 Byte packets to + * be transmitted in a single 16kB skb, though when sending full size + * packets this limit will be plenty. + * Smaller packets are not likely to be trying to maximize the + * throughput and will be mstly sending smaller infrequent frames. */ +#define TX_MAX_NUM_DPE 32 -#define MAX_TX_NONFIXED (512 * 3) +/* Delay for the transmit to wait before sending an unfilled NTB frame. */ +#define TX_TIMEOUT_NSECS 300000 #define FORMATS_SUPPORTED (USB_CDC_NCM_NTB16_SUPPORTED | \ USB_CDC_NCM_NTB32_SUPPORTED) @@ -355,14 +372,15 @@ struct ndp_parser_opts { u32 ndp_sign; unsigned nth_size; unsigned ndp_size; + unsigned dpe_size; unsigned ndplen_align; /* sizes in u16 units */ unsigned dgram_item_len; /* index or length */ unsigned block_length; - unsigned fp_index; + unsigned ndp_index; unsigned reserved1; unsigned reserved2; - unsigned next_fp_index; + unsigned next_ndp_index; }; #define INIT_NDP16_OPTS { \ @@ -370,13 +388,14 @@ struct ndp_parser_opts { .ndp_sign = USB_CDC_NCM_NDP16_NOCRC_SIGN, \ .nth_size = sizeof(struct usb_cdc_ncm_nth16), \ .ndp_size = sizeof(struct usb_cdc_ncm_ndp16), \ + .dpe_size = sizeof(struct usb_cdc_ncm_dpe16), \ .ndplen_align = 4, \ .dgram_item_len = 1, \ .block_length = 1, \ - .fp_index = 1, \ + .ndp_index = 1, \ .reserved1 = 0, \ .reserved2 = 0, \ - .next_fp_index = 1, \ + .next_ndp_index = 1, \ } @@ -385,13 +404,14 @@ struct ndp_parser_opts { .ndp_sign = USB_CDC_NCM_NDP32_NOCRC_SIGN, \ .nth_size = sizeof(struct usb_cdc_ncm_nth32), \ .ndp_size = sizeof(struct usb_cdc_ncm_ndp32), \ + .dpe_size = sizeof(struct usb_cdc_ncm_dpe32), \ .ndplen_align = 8, \ .dgram_item_len = 2, \ .block_length = 2, \ - .fp_index = 2, \ + .ndp_index = 2, \ .reserved1 = 1, \ .reserved2 = 2, \ - .next_fp_index = 2, \ + .next_ndp_index = 2, \ } static const struct ndp_parser_opts ndp16_opts = INIT_NDP16_OPTS; @@ -803,6 +823,8 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) if (ncm->port.in_ep->driver_data) { DBG(cdev, "reset ncm\n"); + ncm->timer_stopping = true; + ncm->netdev = NULL; gether_disconnect(&ncm->port); ncm_reset_values(ncm); } @@ -839,6 +861,8 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) net = gether_connect(&ncm->port); if (IS_ERR(net)) return PTR_ERR(net); + ncm->netdev = net; + ncm->timer_stopping = false; } spin_lock(&ncm->lock); @@ -865,95 +889,232 @@ static int ncm_get_alt(struct usb_function *f, unsigned intf) return ncm->port.in_ep->driver_data ? 1 : 0; } +static struct sk_buff *package_for_tx(struct f_ncm *ncm) +{ + __le16 *ntb_iter; + struct sk_buff *skb2 = NULL; + unsigned ndp_pad; + unsigned ndp_index; + unsigned new_len; + + const struct ndp_parser_opts *opts = ncm->parser_opts; + const int ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment); + const int dgram_idx_len = 2 * 2 * opts->dgram_item_len; + + /* Stop the timer */ + hrtimer_try_to_cancel(&ncm->task_timer); + + ndp_pad = ALIGN(ncm->skb_tx_data->len, ndp_align) - + ncm->skb_tx_data->len; + ndp_index = ncm->skb_tx_data->len + ndp_pad; + new_len = ndp_index + dgram_idx_len + ncm->skb_tx_ndp->len; + + /* Set the final BlockLength and wNdpIndex */ + ntb_iter = (void *) ncm->skb_tx_data->data; + /* Increment pointer to BlockLength */ + ntb_iter += 2 + 1 + 1; + put_ncm(&ntb_iter, opts->block_length, new_len); + put_ncm(&ntb_iter, opts->ndp_index, ndp_index); + + /* Set the final NDP wLength */ + new_len = opts->ndp_size + + (ncm->ndp_dgram_count * dgram_idx_len); + ncm->ndp_dgram_count = 0; + /* Increment from start to wLength */ + ntb_iter = (void *) ncm->skb_tx_ndp->data; + ntb_iter += 2; + put_unaligned_le16(new_len, ntb_iter); + + /* Merge the skbs */ + swap(skb2, ncm->skb_tx_data); + if (ncm->skb_tx_data) { + dev_kfree_skb_any(ncm->skb_tx_data); + ncm->skb_tx_data = NULL; + } + + /* Insert NDP alignment. */ + ntb_iter = (void *) skb_put(skb2, ndp_pad); + memset(ntb_iter, 0, ndp_pad); + + /* Copy NTB across. */ + ntb_iter = (void *) skb_put(skb2, ncm->skb_tx_ndp->len); + memcpy(ntb_iter, ncm->skb_tx_ndp->data, ncm->skb_tx_ndp->len); + dev_kfree_skb_any(ncm->skb_tx_ndp); + ncm->skb_tx_ndp = NULL; + + /* Insert zero'd datagram. */ + ntb_iter = (void *) skb_put(skb2, dgram_idx_len); + memset(ntb_iter, 0, dgram_idx_len); + + return skb2; +} + static struct sk_buff *ncm_wrap_ntb(struct gether *port, struct sk_buff *skb) { struct f_ncm *ncm = func_to_ncm(&port->func); - struct sk_buff *skb2; + struct sk_buff *skb2 = NULL; int ncb_len = 0; - __le16 *tmp; - int div; - int rem; - int pad; - int ndp_align; - int ndp_pad; + __le16 *ntb_data; + __le16 *ntb_ndp; + int dgram_pad; + unsigned max_size = ncm->port.fixed_in_len; const struct ndp_parser_opts *opts = ncm->parser_opts; - unsigned crc_len = ncm->is_crc ? sizeof(uint32_t) : 0; - - div = le16_to_cpu(ntb_parameters.wNdpInDivisor); - rem = le16_to_cpu(ntb_parameters.wNdpInPayloadRemainder); - ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment); + const int ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment); + const int div = le16_to_cpu(ntb_parameters.wNdpInDivisor); + const int rem = le16_to_cpu(ntb_parameters.wNdpInPayloadRemainder); + const int dgram_idx_len = 2 * 2 * opts->dgram_item_len; - ncb_len += opts->nth_size; - ndp_pad = ALIGN(ncb_len, ndp_align) - ncb_len; - ncb_len += ndp_pad; - ncb_len += opts->ndp_size; - ncb_len += 2 * 2 * opts->dgram_item_len; /* Datagram entry */ - ncb_len += 2 * 2 * opts->dgram_item_len; /* Zero datagram entry */ - pad = ALIGN(ncb_len, div) + rem - ncb_len; - ncb_len += pad; - - if (ncb_len + skb->len + crc_len > max_size) { - dev_kfree_skb_any(skb); + if (!skb && !ncm->skb_tx_data) return NULL; - } - skb2 = skb_copy_expand(skb, ncb_len, - max_size - skb->len - ncb_len - crc_len, - GFP_ATOMIC); - dev_kfree_skb_any(skb); - if (!skb2) - return NULL; + if (skb) { + /* Add the CRC if required up front */ + if (ncm->is_crc) { + uint32_t crc; + __le16 *crc_pos; + + crc = ~crc32_le(~0, + skb->data, + skb->len); + crc_pos = (void *) skb_put(skb, sizeof(uint32_t)); + put_unaligned_le32(crc, crc_pos); + } - skb = skb2; + /* If the new skb is too big for the current NCM NTB then + * set the current stored skb to be sent now and clear it + * ready for new data. + * NOTE: Assume maximum align for speed of calculation. + */ + if (ncm->skb_tx_data + && (ncm->ndp_dgram_count >= TX_MAX_NUM_DPE + || (ncm->skb_tx_data->len + + div + rem + skb->len + + ncm->skb_tx_ndp->len + ndp_align + (2 * dgram_idx_len)) + > max_size)) { + skb2 = package_for_tx(ncm); + if (!skb2) + goto err; + } - tmp = (void *) skb_push(skb, ncb_len); - memset(tmp, 0, ncb_len); + if (!ncm->skb_tx_data) { + ncb_len = opts->nth_size; + dgram_pad = ALIGN(ncb_len, div) + rem - ncb_len; + ncb_len += dgram_pad; - put_unaligned_le32(opts->nth_sign, tmp); /* dwSignature */ - tmp += 2; - /* wHeaderLength */ - put_unaligned_le16(opts->nth_size, tmp++); - tmp++; /* skip wSequence */ - put_ncm(&tmp, opts->block_length, skb->len); /* (d)wBlockLength */ - /* (d)wFpIndex */ - /* the first pointer is right after the NTH + align */ - put_ncm(&tmp, opts->fp_index, opts->nth_size + ndp_pad); + /* Create a new skb for the NTH and datagrams. */ + ncm->skb_tx_data = alloc_skb(max_size, GFP_ATOMIC); + if (!ncm->skb_tx_data) + goto err; - tmp = (void *)tmp + ndp_pad; + ntb_data = (void *) skb_put(ncm->skb_tx_data, ncb_len); + memset(ntb_data, 0, ncb_len); + /* dwSignature */ + put_unaligned_le32(opts->nth_sign, ntb_data); + ntb_data += 2; + /* wHeaderLength */ + put_unaligned_le16(opts->nth_size, ntb_data++); + + /* Allocate an skb for storing the NDP, + * TX_MAX_NUM_DPE should easily suffice for a + * 16k packet. + */ + ncm->skb_tx_ndp = alloc_skb((int)(opts->ndp_size + + opts->dpe_size + * TX_MAX_NUM_DPE), + GFP_ATOMIC); + if (!ncm->skb_tx_ndp) + goto err; + ntb_ndp = (void *) skb_put(ncm->skb_tx_ndp, + opts->ndp_size); + memset(ntb_ndp, 0, ncb_len); + /* dwSignature */ + put_unaligned_le32(ncm->ndp_sign, ntb_ndp); + ntb_ndp += 2; - /* NDP */ - put_unaligned_le32(ncm->ndp_sign, tmp); /* dwSignature */ - tmp += 2; - /* wLength */ - put_unaligned_le16(ncb_len - opts->nth_size - pad, tmp++); + /* There is always a zeroed entry */ + ncm->ndp_dgram_count = 1; - tmp += opts->reserved1; - tmp += opts->next_fp_index; /* skip reserved (d)wNextFpIndex */ - tmp += opts->reserved2; + /* Note: we skip opts->next_ndp_index */ + } - if (ncm->is_crc) { - uint32_t crc; + /* Delay the timer. */ + hrtimer_start(&ncm->task_timer, + ktime_set(0, TX_TIMEOUT_NSECS), + HRTIMER_MODE_REL); + + /* Add the datagram position entries */ + ntb_ndp = (void *) skb_put(ncm->skb_tx_ndp, dgram_idx_len); + memset(ntb_ndp, 0, dgram_idx_len); + + ncb_len = ncm->skb_tx_data->len; + dgram_pad = ALIGN(ncb_len, div) + rem - ncb_len; + ncb_len += dgram_pad; + + /* (d)wDatagramIndex */ + put_ncm(&ntb_ndp, opts->dgram_item_len, ncb_len); + /* (d)wDatagramLength */ + put_ncm(&ntb_ndp, opts->dgram_item_len, skb->len); + ncm->ndp_dgram_count++; + + /* Add the new data to the skb */ + ntb_data = (void *) skb_put(ncm->skb_tx_data, dgram_pad); + memset(ntb_data, 0, dgram_pad); + ntb_data = (void *) skb_put(ncm->skb_tx_data, skb->len); + memcpy(ntb_data, skb->data, skb->len); + dev_kfree_skb_any(skb); + skb = NULL; - crc = ~crc32_le(~0, - skb->data + ncb_len, - skb->len - ncb_len); - put_unaligned_le32(crc, skb->data + skb->len); - skb_put(skb, crc_len); + } else if (ncm->skb_tx_data && ncm->timer_force_tx) { + /* If the tx was requested because of a timeout then send */ + skb2 = package_for_tx(ncm); + if (!skb2) + goto err; } - /* (d)wDatagramIndex[0] */ - put_ncm(&tmp, opts->dgram_item_len, ncb_len); - /* (d)wDatagramLength[0] */ - put_ncm(&tmp, opts->dgram_item_len, skb->len - ncb_len); - /* (d)wDatagramIndex[1] and (d)wDatagramLength[1] already zeroed */ + return skb2; - if (skb->len > MAX_TX_NONFIXED) - memset(skb_put(skb, max_size - skb->len), - 0, max_size - skb->len); +err: + ncm->netdev->stats.tx_dropped++; + + if (skb) + dev_kfree_skb_any(skb); + if (ncm->skb_tx_data) + dev_kfree_skb_any(ncm->skb_tx_data); + if (ncm->skb_tx_ndp) + dev_kfree_skb_any(ncm->skb_tx_ndp); - return skb; + return NULL; +} + +/* + * This transmits the NTB if there are frames waiting. + */ +static void ncm_tx_tasklet(unsigned long data) +{ + struct f_ncm *ncm = (void *)data; + + if (ncm->timer_stopping) + return; + + /* Only send if data is available. */ + if (ncm->skb_tx_data) { + ncm->timer_force_tx = true; + ncm->netdev->netdev_ops->ndo_start_xmit(NULL, ncm->netdev); + ncm->timer_force_tx = false; + } +} + +/* + * The transmit should only be run if no skb data has been sent + * for a certain duration. + */ +static enum hrtimer_restart ncm_tx_timeout(struct hrtimer *data) +{ + struct f_ncm *ncm = container_of(data, struct f_ncm, task_timer); + tasklet_schedule(&ncm->tx_tasklet); + return HRTIMER_NORESTART; } static int ncm_unwrap_ntb(struct gether *port, @@ -963,6 +1124,7 @@ static int ncm_unwrap_ntb(struct gether *port, struct f_ncm *ncm = func_to_ncm(&port->func); __le16 *tmp = (void *) skb->data; unsigned index, index2; + int ndp_index; unsigned dg_len, dg_len2; unsigned ndp_len; struct sk_buff *skb2; @@ -995,91 +1157,101 @@ static int ncm_unwrap_ntb(struct gether *port, goto err; } - index = get_ncm(&tmp, opts->fp_index); - /* NCM 3.2 */ - if (((index % 4) != 0) && (index < opts->nth_size)) { - INFO(port->func.config->cdev, "Bad index: %x\n", - index); - goto err; - } - - /* walk through NDP */ - tmp = ((void *)skb->data) + index; - if (get_unaligned_le32(tmp) != ncm->ndp_sign) { - INFO(port->func.config->cdev, "Wrong NDP SIGN\n"); - goto err; - } - tmp += 2; - - ndp_len = get_unaligned_le16(tmp++); - /* - * NCM 3.3.1 - * entry is 2 items - * item size is 16/32 bits, opts->dgram_item_len * 2 bytes - * minimal: struct usb_cdc_ncm_ndpX + normal entry + zero entry - */ - if ((ndp_len < opts->ndp_size + 2 * 2 * (opts->dgram_item_len * 2)) - || (ndp_len % opts->ndplen_align != 0)) { - INFO(port->func.config->cdev, "Bad NDP length: %x\n", ndp_len); - goto err; - } - tmp += opts->reserved1; - tmp += opts->next_fp_index; /* skip reserved (d)wNextFpIndex */ - tmp += opts->reserved2; - - ndp_len -= opts->ndp_size; - index2 = get_ncm(&tmp, opts->dgram_item_len); - dg_len2 = get_ncm(&tmp, opts->dgram_item_len); - dgram_counter = 0; + ndp_index = get_ncm(&tmp, opts->ndp_index); + /* Run through all the NDP's in the NTB */ do { - index = index2; - dg_len = dg_len2; - if (dg_len < 14 + crc_len) { /* ethernet header + crc */ - INFO(port->func.config->cdev, "Bad dgram length: %x\n", - dg_len); + /* NCM 3.2 */ + if (((ndp_index % 4) != 0) && + (ndp_index < opts->nth_size)) { + INFO(port->func.config->cdev, "Bad index: %#X\n", + ndp_index); goto err; } - if (ncm->is_crc) { - uint32_t crc, crc2; - - crc = get_unaligned_le32(skb->data + - index + dg_len - crc_len); - crc2 = ~crc32_le(~0, - skb->data + index, - dg_len - crc_len); - if (crc != crc2) { - INFO(port->func.config->cdev, "Bad CRC\n"); - goto err; - } + + /* walk through NDP */ + tmp = (void *)(skb->data + ndp_index); + if (get_unaligned_le32(tmp) != ncm->ndp_sign) { + INFO(port->func.config->cdev, "Wrong NDP SIGN\n"); + goto err; } + tmp += 2; + ndp_len = get_unaligned_le16(tmp++); + /* + * NCM 3.3.1 + * entry is 2 items + * item size is 16/32 bits, opts->dgram_item_len * 2 bytes + * minimal: struct usb_cdc_ncm_ndpX + normal entry + zero entry + * Each entry is a dgram index and a dgram length. + */ + if ((ndp_len < opts->ndp_size + + 2 * 2 * (opts->dgram_item_len * 2)) + || (ndp_len % opts->ndplen_align != 0)) { + INFO(port->func.config->cdev, "Bad NDP length: %#X\n", + ndp_len); + goto err; + } + tmp += opts->reserved1; + /* Check for another NDP (d)wNextNdpIndex */ + ndp_index = get_ncm(&tmp, opts->next_ndp_index); + tmp += opts->reserved2; + + ndp_len -= opts->ndp_size; index2 = get_ncm(&tmp, opts->dgram_item_len); dg_len2 = get_ncm(&tmp, opts->dgram_item_len); + dgram_counter = 0; + + do { + index = index2; + dg_len = dg_len2; + if (dg_len < 14 + crc_len) { /* ethernet hdr + crc */ + INFO(port->func.config->cdev, + "Bad dgram length: %#X\n", dg_len); + goto err; + } + if (ncm->is_crc) { + uint32_t crc, crc2; + + crc = get_unaligned_le32(skb->data + + index + dg_len - + crc_len); + crc2 = ~crc32_le(~0, + skb->data + index, + dg_len - crc_len); + if (crc != crc2) { + INFO(port->func.config->cdev, + "Bad CRC\n"); + goto err; + } + } + + index2 = get_ncm(&tmp, opts->dgram_item_len); + dg_len2 = get_ncm(&tmp, opts->dgram_item_len); - if (index2 == 0 || dg_len2 == 0) { - skb2 = skb; - } else { - skb2 = skb_clone(skb, GFP_ATOMIC); + /* + * Copy the data into a new skb. + * This ensures the truesize is correct + */ + skb2 = netdev_alloc_skb_ip_align(ncm->netdev, + dg_len - crc_len); if (skb2 == NULL) goto err; - } + memcpy(skb_put(skb2, dg_len - crc_len), + skb->data + index, dg_len - crc_len); - if (!skb_pull(skb2, index)) { - ret = -EOVERFLOW; - goto err; - } + skb_queue_tail(list, skb2); - skb_trim(skb2, dg_len - crc_len); - skb_queue_tail(list, skb2); + ndp_len -= 2 * (opts->dgram_item_len * 2); - ndp_len -= 2 * (opts->dgram_item_len * 2); + dgram_counter++; - dgram_counter++; + if (index2 == 0 || dg_len2 == 0) + break; + } while (ndp_len > 2 * (opts->dgram_item_len * 2)); + } while (ndp_index); - if (index2 == 0 || dg_len2 == 0) - break; - } while (ndp_len > 2 * (opts->dgram_item_len * 2)); /* zero entry */ + dev_kfree_skb_any(skb); VDBG(port->func.config->cdev, "Parsed NTB with %d frames\n", dgram_counter); @@ -1097,8 +1269,11 @@ static void ncm_disable(struct usb_function *f) DBG(cdev, "ncm deactivated\n"); - if (ncm->port.in_ep->driver_data) + if (ncm->port.in_ep->driver_data) { + ncm->timer_stopping = true; + ncm->netdev = NULL; gether_disconnect(&ncm->port); + } if (ncm->notify->driver_data) { usb_ep_disable(ncm->notify); @@ -1267,6 +1442,10 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) ncm->port.open = ncm_open; ncm->port.close = ncm_close; + tasklet_init(&ncm->tx_tasklet, ncm_tx_tasklet, (unsigned long) ncm); + hrtimer_init(&ncm->task_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ncm->task_timer.function = ncm_tx_timeout; + DBG(cdev, "CDC Network: %s speed IN/%s OUT/%s NOTIFY/%s\n", gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", ncm->port.in_ep->name, ncm->port.out_ep->name, @@ -1380,6 +1559,10 @@ static void ncm_unbind(struct usb_configuration *c, struct usb_function *f) DBG(c->cdev, "ncm unbind\n"); + hrtimer_cancel(&ncm->task_timer); + tasklet_kill(&ncm->tx_tasklet); + + ncm_string_defs[0].id = 0; usb_free_all_descriptors(f); kfree(ncm->notify_req->buf); @@ -1416,6 +1599,7 @@ static struct usb_function *ncm_alloc(struct usb_function_instance *fi) ncm->port.ioport = netdev_priv(opts->net); mutex_unlock(&opts->lock); ncm->port.is_fixed = true; + ncm->port.supports_multi_frame = true; ncm->port.func.name = "cdc_network"; /* descriptors are per-instance copies */ diff --git a/drivers/usb/gadget/f_obex.c b/drivers/usb/gadget/function/f_obex.c index aebae1853bce..aebae1853bce 100644 --- a/drivers/usb/gadget/f_obex.c +++ b/drivers/usb/gadget/function/f_obex.c diff --git a/drivers/usb/gadget/f_phonet.c b/drivers/usb/gadget/function/f_phonet.c index f2b781773eed..f2b781773eed 100644 --- a/drivers/usb/gadget/f_phonet.c +++ b/drivers/usb/gadget/function/f_phonet.c diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c index 9c41e9515b8e..ddb09dc6d1f2 100644 --- a/drivers/usb/gadget/f_rndis.c +++ b/drivers/usb/gadget/function/f_rndis.c @@ -727,6 +727,10 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) rndis_control_intf.bInterfaceNumber = status; rndis_union_desc.bMasterInterface0 = status; + if (cdev->use_os_string) + f->os_desc_table[0].if_id = + rndis_iad_descriptor.bFirstInterface; + status = usb_interface_id(c, f); if (status < 0) goto fail; diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/function/f_serial.c index 9ecbcbf36a45..9ecbcbf36a45 100644 --- a/drivers/usb/gadget/f_serial.c +++ b/drivers/usb/gadget/function/f_serial.c diff --git a/drivers/usb/gadget/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c index d3cd52db78fe..d3cd52db78fe 100644 --- a/drivers/usb/gadget/f_sourcesink.c +++ b/drivers/usb/gadget/function/f_sourcesink.c diff --git a/drivers/usb/gadget/f_subset.c b/drivers/usb/gadget/function/f_subset.c index 1ea8baf33333..1ea8baf33333 100644 --- a/drivers/usb/gadget/f_subset.c +++ b/drivers/usb/gadget/function/f_subset.c diff --git a/drivers/usb/gadget/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index 2b4c82d84bfc..2b4c82d84bfc 100644 --- a/drivers/usb/gadget/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c diff --git a/drivers/usb/gadget/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 6261db4a9910..3ed89ecc8d6d 100644 --- a/drivers/usb/gadget/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -348,14 +348,34 @@ static int uac2_pcm_open(struct snd_pcm_substream *substream) if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { spin_lock_init(&uac2->p_prm.lock); runtime->hw.rate_min = p_srate; - runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; /* ! p_ssize ! */ + switch (p_ssize) { + case 3: + runtime->hw.formats = SNDRV_PCM_FMTBIT_S24_3LE; + break; + case 4: + runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE; + break; + default: + runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; + break; + } runtime->hw.channels_min = num_channels(p_chmask); runtime->hw.period_bytes_min = 2 * uac2->p_prm.max_psize / runtime->hw.periods_min; } else { spin_lock_init(&uac2->c_prm.lock); runtime->hw.rate_min = c_srate; - runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; /* ! c_ssize ! */ + switch (c_ssize) { + case 3: + runtime->hw.formats = SNDRV_PCM_FMTBIT_S24_3LE; + break; + case 4: + runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE; + break; + default: + runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; + break; + } runtime->hw.channels_min = num_channels(c_chmask); runtime->hw.period_bytes_min = 2 * uac2->c_prm.max_psize / runtime->hw.periods_min; diff --git a/drivers/usb/gadget/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index e2a1f50bd93c..e2a1f50bd93c 100644 --- a/drivers/usb/gadget/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c diff --git a/drivers/usb/gadget/f_uvc.h b/drivers/usb/gadget/function/f_uvc.h index ec52752f7326..ec52752f7326 100644 --- a/drivers/usb/gadget/f_uvc.h +++ b/drivers/usb/gadget/function/f_uvc.h diff --git a/drivers/usb/gadget/g_zero.h b/drivers/usb/gadget/function/g_zero.h index 15f180904f8a..15f180904f8a 100644 --- a/drivers/usb/gadget/g_zero.h +++ b/drivers/usb/gadget/function/g_zero.h diff --git a/drivers/usb/gadget/ndis.h b/drivers/usb/gadget/function/ndis.h index a19f72dec0cd..a19f72dec0cd 100644 --- a/drivers/usb/gadget/ndis.h +++ b/drivers/usb/gadget/function/ndis.h diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/function/rndis.c index 95d2324f6977..95d2324f6977 100644 --- a/drivers/usb/gadget/rndis.c +++ b/drivers/usb/gadget/function/rndis.c diff --git a/drivers/usb/gadget/rndis.h b/drivers/usb/gadget/function/rndis.h index 0f4abb4c3775..0f4abb4c3775 100644 --- a/drivers/usb/gadget/rndis.h +++ b/drivers/usb/gadget/function/rndis.h diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/function/storage_common.c index 648f9e489b39..648f9e489b39 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/function/storage_common.c diff --git a/drivers/usb/gadget/storage_common.h b/drivers/usb/gadget/function/storage_common.h index 70c891469f57..70c891469f57 100644 --- a/drivers/usb/gadget/storage_common.h +++ b/drivers/usb/gadget/function/storage_common.h diff --git a/drivers/usb/gadget/u_ecm.h b/drivers/usb/gadget/function/u_ecm.h index 262cc03cc2c0..262cc03cc2c0 100644 --- a/drivers/usb/gadget/u_ecm.h +++ b/drivers/usb/gadget/function/u_ecm.h diff --git a/drivers/usb/gadget/u_eem.h b/drivers/usb/gadget/function/u_eem.h index e3ae97874c4f..e3ae97874c4f 100644 --- a/drivers/usb/gadget/u_eem.h +++ b/drivers/usb/gadget/function/u_eem.h diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/function/u_ether.c index 97b027724ee7..d50adda913cf 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -483,7 +483,7 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, struct net_device *net) { struct eth_dev *dev = netdev_priv(net); - int length = skb->len; + int length = 0; int retval; struct usb_request *req = NULL; unsigned long flags; @@ -500,13 +500,13 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, } spin_unlock_irqrestore(&dev->lock, flags); - if (!in) { + if (skb && !in) { dev_kfree_skb_any(skb); return NETDEV_TX_OK; } /* apply outgoing CDC or RNDIS filters */ - if (!is_promisc(cdc_filter)) { + if (skb && !is_promisc(cdc_filter)) { u8 *dest = skb->data; if (is_multicast_ether_addr(dest)) { @@ -557,11 +557,17 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, if (dev->port_usb) skb = dev->wrap(dev->port_usb, skb); spin_unlock_irqrestore(&dev->lock, flags); - if (!skb) + if (!skb) { + /* Multi frame CDC protocols may store the frame for + * later which is not a dropped frame. + */ + if (dev->port_usb->supports_multi_frame) + goto multiframe; goto drop; - - length = skb->len; + } } + + length = skb->len; req->buf = skb->data; req->context = skb; req->complete = tx_complete; @@ -604,6 +610,7 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, dev_kfree_skb_any(skb); drop: dev->net->stats.tx_dropped++; +multiframe: spin_lock_irqsave(&dev->req_lock, flags); if (list_empty(&dev->tx_reqs)) netif_start_queue(net); diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/function/u_ether.h index 0f0290acea7e..334b38947916 100644 --- a/drivers/usb/gadget/u_ether.h +++ b/drivers/usb/gadget/function/u_ether.h @@ -18,6 +18,7 @@ #include <linux/if_ether.h> #include <linux/usb/composite.h> #include <linux/usb/cdc.h> +#include <linux/netdevice.h> #include "gadget_chips.h" @@ -74,6 +75,7 @@ struct gether { bool is_fixed; u32 fixed_out_len; u32 fixed_in_len; + bool supports_multi_frame; struct sk_buff *(*wrap)(struct gether *port, struct sk_buff *skb); int (*unwrap)(struct gether *port, diff --git a/drivers/usb/gadget/u_ether_configfs.h b/drivers/usb/gadget/function/u_ether_configfs.h index bcbd30146cfd..bcbd30146cfd 100644 --- a/drivers/usb/gadget/u_ether_configfs.h +++ b/drivers/usb/gadget/function/u_ether_configfs.h diff --git a/drivers/usb/gadget/u_fs.h b/drivers/usb/gadget/function/u_fs.h index bf0ba375d459..63d6e71569c1 100644 --- a/drivers/usb/gadget/u_fs.h +++ b/drivers/usb/gadget/function/u_fs.h @@ -216,6 +216,13 @@ struct ffs_data { unsigned fs_descs_count; unsigned hs_descs_count; unsigned ss_descs_count; + unsigned ms_os_descs_count; + unsigned ms_os_descs_ext_prop_count; + unsigned ms_os_descs_ext_prop_name_len; + unsigned ms_os_descs_ext_prop_data_len; + void *ms_os_descs_ext_prop_avail; + void *ms_os_descs_ext_prop_name_avail; + void *ms_os_descs_ext_prop_data_avail; unsigned short strings_count; unsigned short interfaces_count; diff --git a/drivers/usb/gadget/u_gether.h b/drivers/usb/gadget/function/u_gether.h index d4078426ba5d..d4078426ba5d 100644 --- a/drivers/usb/gadget/u_gether.h +++ b/drivers/usb/gadget/function/u_gether.h diff --git a/drivers/usb/gadget/u_ncm.h b/drivers/usb/gadget/function/u_ncm.h index ce0f3a78ca13..ce0f3a78ca13 100644 --- a/drivers/usb/gadget/u_ncm.h +++ b/drivers/usb/gadget/function/u_ncm.h diff --git a/drivers/usb/gadget/u_phonet.h b/drivers/usb/gadget/function/u_phonet.h index 98ced18779ea..98ced18779ea 100644 --- a/drivers/usb/gadget/u_phonet.h +++ b/drivers/usb/gadget/function/u_phonet.h diff --git a/drivers/usb/gadget/u_rndis.h b/drivers/usb/gadget/function/u_rndis.h index e902aa42a297..e902aa42a297 100644 --- a/drivers/usb/gadget/u_rndis.h +++ b/drivers/usb/gadget/function/u_rndis.h diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/function/u_serial.c index ad0aca812002..ad0aca812002 100644 --- a/drivers/usb/gadget/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c diff --git a/drivers/usb/gadget/u_serial.h b/drivers/usb/gadget/function/u_serial.h index c20210c0babd..c20210c0babd 100644 --- a/drivers/usb/gadget/u_serial.h +++ b/drivers/usb/gadget/function/u_serial.h diff --git a/drivers/usb/gadget/u_uac1.c b/drivers/usb/gadget/function/u_uac1.c index 7a55fea43430..7a55fea43430 100644 --- a/drivers/usb/gadget/u_uac1.c +++ b/drivers/usb/gadget/function/u_uac1.c diff --git a/drivers/usb/gadget/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h index 18c2e729faf6..18c2e729faf6 100644 --- a/drivers/usb/gadget/u_uac1.h +++ b/drivers/usb/gadget/function/u_uac1.h diff --git a/drivers/usb/gadget/uvc.h b/drivers/usb/gadget/function/uvc.h index 7a9111de8054..7a9111de8054 100644 --- a/drivers/usb/gadget/uvc.h +++ b/drivers/usb/gadget/function/uvc.h diff --git a/drivers/usb/gadget/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c index 1c29bc954db9..1c29bc954db9 100644 --- a/drivers/usb/gadget/uvc_queue.c +++ b/drivers/usb/gadget/function/uvc_queue.c diff --git a/drivers/usb/gadget/uvc_queue.h b/drivers/usb/gadget/function/uvc_queue.h index 8e76ce982f1e..8e76ce982f1e 100644 --- a/drivers/usb/gadget/uvc_queue.h +++ b/drivers/usb/gadget/function/uvc_queue.h diff --git a/drivers/usb/gadget/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index ad48e81155e2..ad48e81155e2 100644 --- a/drivers/usb/gadget/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c diff --git a/drivers/usb/gadget/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index 71e896d4c5ae..71e896d4c5ae 100644 --- a/drivers/usb/gadget/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c diff --git a/drivers/usb/gadget/legacy/Kconfig b/drivers/usb/gadget/legacy/Kconfig new file mode 100644 index 000000000000..aa376f006333 --- /dev/null +++ b/drivers/usb/gadget/legacy/Kconfig @@ -0,0 +1,475 @@ +# +# USB Gadget support on a system involves +# (a) a peripheral controller, and +# (b) the gadget driver using it. +# +# NOTE: Gadget support ** DOES NOT ** depend on host-side CONFIG_USB !! +# +# - Host systems (like PCs) need CONFIG_USB (with "A" jacks). +# - Peripherals (like PDAs) need CONFIG_USB_GADGET (with "B" jacks). +# - Some systems have both kinds of controllers. +# +# With help from a special transceiver and a "Mini-AB" jack, systems with +# both kinds of controller can also support "USB On-the-Go" (CONFIG_USB_OTG). +# + +config USB_ZERO + tristate "Gadget Zero (DEVELOPMENT)" + select USB_LIBCOMPOSITE + select USB_F_SS_LB + help + Gadget Zero is a two-configuration device. It either sinks and + sources bulk data; or it loops back a configurable number of + transfers. It also implements control requests, for "chapter 9" + conformance. The driver needs only two bulk-capable endpoints, so + it can work on top of most device-side usb controllers. It's + useful for testing, and is also a working example showing how + USB "gadget drivers" can be written. + + Make this be the first driver you try using on top of any new + USB peripheral controller driver. Then you can use host-side + test software, like the "usbtest" driver, to put your hardware + and its driver through a basic set of functional tests. + + Gadget Zero also works with the host-side "usb-skeleton" driver, + and with many kinds of host-side test software. You may need + to tweak product and vendor IDs before host software knows about + this device, and arrange to select an appropriate configuration. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_zero". + +config USB_ZERO_HNPTEST + boolean "HNP Test Device" + depends on USB_ZERO && USB_OTG + help + You can configure this device to enumerate using the device + identifiers of the USB-OTG test device. That means that when + this gadget connects to another OTG device, with this one using + the "B-Peripheral" role, that device will use HNP to let this + one serve as the USB host instead (in the "B-Host" role). + +config USB_AUDIO + tristate "Audio Gadget" + depends on SND + select USB_LIBCOMPOSITE + select SND_PCM + help + This Gadget Audio driver is compatible with USB Audio Class + specification 2.0. It implements 1 AudioControl interface, + 1 AudioStreaming Interface each for USB-OUT and USB-IN. + Number of channels, sample rate and sample size can be + specified as module parameters. + This driver doesn't expect any real Audio codec to be present + on the device - the audio streams are simply sinked to and + sourced from a virtual ALSA sound card created. The user-space + application may choose to do whatever it wants with the data + received from the USB Host and choose to provide whatever it + wants as audio data to the USB Host. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_audio". + +config GADGET_UAC1 + bool "UAC 1.0 (Legacy)" + depends on USB_AUDIO + help + If you instead want older UAC Spec-1.0 driver that also has audio + paths hardwired to the Audio codec chip on-board and doesn't work + without one. + +config USB_ETH + tristate "Ethernet Gadget (with CDC Ethernet support)" + depends on NET + select USB_LIBCOMPOSITE + select USB_U_ETHER + select USB_F_ECM + select USB_F_SUBSET + select CRC32 + help + This driver implements Ethernet style communication, in one of + several ways: + + - The "Communication Device Class" (CDC) Ethernet Control Model. + That protocol is often avoided with pure Ethernet adapters, in + favor of simpler vendor-specific hardware, but is widely + supported by firmware for smart network devices. + + - On hardware can't implement that protocol, a simple CDC subset + is used, placing fewer demands on USB. + + - CDC Ethernet Emulation Model (EEM) is a newer standard that has + a simpler interface that can be used by more USB hardware. + + RNDIS support is an additional option, more demanding than than + subset. + + Within the USB device, this gadget driver exposes a network device + "usbX", where X depends on what other networking devices you have. + Treat it like a two-node Ethernet link: host, and gadget. + + The Linux-USB host-side "usbnet" driver interoperates with this + driver, so that deep I/O queues can be supported. On 2.4 kernels, + use "CDCEther" instead, if you're using the CDC option. That CDC + mode should also interoperate with standard CDC Ethernet class + drivers on other host operating systems. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_ether". + +config USB_ETH_RNDIS + bool "RNDIS support" + depends on USB_ETH + select USB_LIBCOMPOSITE + select USB_F_RNDIS + default y + help + Microsoft Windows XP bundles the "Remote NDIS" (RNDIS) protocol, + and Microsoft provides redistributable binary RNDIS drivers for + older versions of Windows. + + If you say "y" here, the Ethernet gadget driver will try to provide + a second device configuration, supporting RNDIS to talk to such + Microsoft USB hosts. + + To make MS-Windows work with this, use Documentation/usb/linux.inf + as the "driver info file". For versions of MS-Windows older than + XP, you'll need to download drivers from Microsoft's website; a URL + is given in comments found in that info file. + +config USB_ETH_EEM + bool "Ethernet Emulation Model (EEM) support" + depends on USB_ETH + select USB_LIBCOMPOSITE + select USB_F_EEM + default n + help + CDC EEM is a newer USB standard that is somewhat simpler than CDC ECM + and therefore can be supported by more hardware. Technically ECM and + EEM are designed for different applications. The ECM model extends + the network interface to the target (e.g. a USB cable modem), and the + EEM model is for mobile devices to communicate with hosts using + ethernet over USB. For Linux gadgets, however, the interface with + the host is the same (a usbX device), so the differences are minimal. + + If you say "y" here, the Ethernet gadget driver will use the EEM + protocol rather than ECM. If unsure, say "n". + +config USB_G_NCM + tristate "Network Control Model (NCM) support" + depends on NET + select USB_LIBCOMPOSITE + select USB_U_ETHER + select USB_F_NCM + select CRC32 + help + This driver implements USB CDC NCM subclass standard. NCM is + an advanced protocol for Ethernet encapsulation, allows grouping + of several ethernet frames into one USB transfer and different + alignment possibilities. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_ncm". + +config USB_GADGETFS + tristate "Gadget Filesystem" + help + This driver provides a filesystem based API that lets user mode + programs implement a single-configuration USB device, including + endpoint I/O and control requests that don't relate to enumeration. + All endpoints, transfer speeds, and transfer types supported by + the hardware are available, through read() and write() calls. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "gadgetfs". + +config USB_FUNCTIONFS + tristate "Function Filesystem" + select USB_LIBCOMPOSITE + select USB_F_FS + select USB_FUNCTIONFS_GENERIC if !(USB_FUNCTIONFS_ETH || USB_FUNCTIONFS_RNDIS) + help + The Function Filesystem (FunctionFS) lets one create USB + composite functions in user space in the same way GadgetFS + lets one create USB gadgets in user space. This allows creation + of composite gadgets such that some of the functions are + implemented in kernel space (for instance Ethernet, serial or + mass storage) and other are implemented in user space. + + If you say "y" or "m" here you will be able what kind of + configurations the gadget will provide. + + Say "y" to link the driver statically, or "m" to build + a dynamically linked module called "g_ffs". + +config USB_FUNCTIONFS_ETH + bool "Include configuration with CDC ECM (Ethernet)" + depends on USB_FUNCTIONFS && NET + select USB_U_ETHER + select USB_F_ECM + select USB_F_SUBSET + help + Include a configuration with CDC ECM function (Ethernet) and the + Function Filesystem. + +config USB_FUNCTIONFS_RNDIS + bool "Include configuration with RNDIS (Ethernet)" + depends on USB_FUNCTIONFS && NET + select USB_U_ETHER + select USB_F_RNDIS + help + Include a configuration with RNDIS function (Ethernet) and the Filesystem. + +config USB_FUNCTIONFS_GENERIC + bool "Include 'pure' configuration" + depends on USB_FUNCTIONFS + help + Include a configuration with the Function Filesystem alone with + no Ethernet interface. + +config USB_MASS_STORAGE + tristate "Mass Storage Gadget" + depends on BLOCK + select USB_LIBCOMPOSITE + select USB_F_MASS_STORAGE + help + The Mass Storage Gadget acts as a USB Mass Storage disk drive. + As its storage repository it can use a regular file or a block + device (in much the same way as the "loop" device driver), + specified as a module parameter or sysfs option. + + This driver is a replacement for now removed File-backed + Storage Gadget (g_file_storage). + + Say "y" to link the driver statically, or "m" to build + a dynamically linked module called "g_mass_storage". + +config USB_GADGET_TARGET + tristate "USB Gadget Target Fabric Module" + depends on TARGET_CORE + select USB_LIBCOMPOSITE + help + This fabric is an USB gadget. Two USB protocols are supported that is + BBB or BOT (Bulk Only Transport) and UAS (USB Attached SCSI). BOT is + advertised on alternative interface 0 (primary) and UAS is on + alternative interface 1. Both protocols can work on USB2.0 and USB3.0. + UAS utilizes the USB 3.0 feature called streams support. + +config USB_G_SERIAL + tristate "Serial Gadget (with CDC ACM and CDC OBEX support)" + depends on TTY + select USB_U_SERIAL + select USB_F_ACM + select USB_F_SERIAL + select USB_F_OBEX + select USB_LIBCOMPOSITE + help + The Serial Gadget talks to the Linux-USB generic serial driver. + This driver supports a CDC-ACM module option, which can be used + to interoperate with MS-Windows hosts or with the Linux-USB + "cdc-acm" driver. + + This driver also supports a CDC-OBEX option. You will need a + user space OBEX server talking to /dev/ttyGS*, since the kernel + itself doesn't implement the OBEX protocol. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_serial". + + For more information, see Documentation/usb/gadget_serial.txt + which includes instructions and a "driver info file" needed to + make MS-Windows work with CDC ACM. + +config USB_MIDI_GADGET + tristate "MIDI Gadget" + depends on SND + select USB_LIBCOMPOSITE + select SND_RAWMIDI + help + The MIDI Gadget acts as a USB Audio device, with one MIDI + input and one MIDI output. These MIDI jacks appear as + a sound "card" in the ALSA sound system. Other MIDI + connections can then be made on the gadget system, using + ALSA's aconnect utility etc. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_midi". + +config USB_G_PRINTER + tristate "Printer Gadget" + select USB_LIBCOMPOSITE + help + The Printer Gadget channels data between the USB host and a + userspace program driving the print engine. The user space + program reads and writes the device file /dev/g_printer to + receive or send printer data. It can use ioctl calls to + the device file to get or set printer status. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_printer". + + For more information, see Documentation/usb/gadget_printer.txt + which includes sample code for accessing the device file. + +if TTY + +config USB_CDC_COMPOSITE + tristate "CDC Composite Device (Ethernet and ACM)" + depends on NET + select USB_LIBCOMPOSITE + select USB_U_SERIAL + select USB_U_ETHER + select USB_F_ACM + select USB_F_ECM + help + This driver provides two functions in one configuration: + a CDC Ethernet (ECM) link, and a CDC ACM (serial port) link. + + This driver requires four bulk and two interrupt endpoints, + plus the ability to handle altsettings. Not all peripheral + controllers are that capable. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module. + +config USB_G_NOKIA + tristate "Nokia composite gadget" + depends on PHONET + select USB_LIBCOMPOSITE + select USB_U_SERIAL + select USB_U_ETHER + select USB_F_ACM + select USB_F_OBEX + select USB_F_PHONET + select USB_F_ECM + help + The Nokia composite gadget provides support for acm, obex + and phonet in only one composite gadget driver. + + It's only really useful for N900 hardware. If you're building + a kernel for N900, say Y or M here. If unsure, say N. + +config USB_G_ACM_MS + tristate "CDC Composite Device (ACM and mass storage)" + depends on BLOCK + select USB_LIBCOMPOSITE + select USB_U_SERIAL + select USB_F_ACM + select USB_F_MASS_STORAGE + help + This driver provides two functions in one configuration: + a mass storage, and a CDC ACM (serial port) link. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_acm_ms". + +config USB_G_MULTI + tristate "Multifunction Composite Gadget" + depends on BLOCK && NET + select USB_G_MULTI_CDC if !USB_G_MULTI_RNDIS + select USB_LIBCOMPOSITE + select USB_U_SERIAL + select USB_U_ETHER + select USB_F_ACM + select USB_F_MASS_STORAGE + help + The Multifunction Composite Gadget provides Ethernet (RNDIS + and/or CDC Ethernet), mass storage and ACM serial link + interfaces. + + You will be asked to choose which of the two configurations is + to be available in the gadget. At least one configuration must + be chosen to make the gadget usable. Selecting more than one + configuration will prevent Windows from automatically detecting + the gadget as a composite gadget, so an INF file will be needed to + use the gadget. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_multi". + +config USB_G_MULTI_RNDIS + bool "RNDIS + CDC Serial + Storage configuration" + depends on USB_G_MULTI + select USB_F_RNDIS + default y + help + This option enables a configuration with RNDIS, CDC Serial and + Mass Storage functions available in the Multifunction Composite + Gadget. This is the configuration dedicated for Windows since RNDIS + is Microsoft's protocol. + + If unsure, say "y". + +config USB_G_MULTI_CDC + bool "CDC Ethernet + CDC Serial + Storage configuration" + depends on USB_G_MULTI + default n + select USB_F_ECM + help + This option enables a configuration with CDC Ethernet (ECM), CDC + Serial and Mass Storage functions available in the Multifunction + Composite Gadget. + + If unsure, say "y". + +endif # TTY + +config USB_G_HID + tristate "HID Gadget" + select USB_LIBCOMPOSITE + help + The HID gadget driver provides generic emulation of USB + Human Interface Devices (HID). + + For more information, see Documentation/usb/gadget_hid.txt which + includes sample code for accessing the device files. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_hid". + +# Standalone / single function gadgets +config USB_G_DBGP + tristate "EHCI Debug Device Gadget" + depends on TTY + select USB_LIBCOMPOSITE + help + This gadget emulates an EHCI Debug device. This is useful when you want + to interact with an EHCI Debug Port. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_dbgp". + +if USB_G_DBGP +choice + prompt "EHCI Debug Device mode" + default USB_G_DBGP_SERIAL + +config USB_G_DBGP_PRINTK + depends on USB_G_DBGP + bool "printk" + help + Directly printk() received data. No interaction. + +config USB_G_DBGP_SERIAL + depends on USB_G_DBGP + select USB_U_SERIAL + bool "serial" + help + Userland can interact using /dev/ttyGSxxx. +endchoice +endif + +# put drivers that need isochronous transfer support (for audio +# or video class gadget drivers), or specific hardware, here. +config USB_G_WEBCAM + tristate "USB Webcam Gadget" + depends on VIDEO_DEV + select USB_LIBCOMPOSITE + select VIDEOBUF2_VMALLOC + help + The Webcam Gadget acts as a composite USB Audio and Video Class + device. It provides a userspace API to process UVC control requests + and stream video data to the host. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_webcam". diff --git a/drivers/usb/gadget/legacy/Makefile b/drivers/usb/gadget/legacy/Makefile new file mode 100644 index 000000000000..a11aad5635df --- /dev/null +++ b/drivers/usb/gadget/legacy/Makefile @@ -0,0 +1,44 @@ +# +# USB gadget drivers +# + +ccflags-y := -I$(PWD)/drivers/usb/gadget/ +ccflags-y += -I$(PWD)/drivers/usb/gadget/udc/ +ccflags-y += -I$(PWD)/drivers/usb/gadget/function/ + +g_zero-y := zero.o +g_audio-y := audio.o +g_ether-y := ether.o +g_serial-y := serial.o +g_midi-y := gmidi.o +gadgetfs-y := inode.o +g_mass_storage-y := mass_storage.o +g_printer-y := printer.o +g_cdc-y := cdc2.o +g_multi-y := multi.o +g_hid-y := hid.o +g_dbgp-y := dbgp.o +g_nokia-y := nokia.o +g_webcam-y := webcam.o +g_ncm-y := ncm.o +g_acm_ms-y := acm_ms.o +g_tcm_usb_gadget-y := tcm_usb_gadget.o + +obj-$(CONFIG_USB_ZERO) += g_zero.o +obj-$(CONFIG_USB_AUDIO) += g_audio.o +obj-$(CONFIG_USB_ETH) += g_ether.o +obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o +obj-$(CONFIG_USB_FUNCTIONFS) += g_ffs.o +obj-$(CONFIG_USB_MASS_STORAGE) += g_mass_storage.o +obj-$(CONFIG_USB_G_SERIAL) += g_serial.o +obj-$(CONFIG_USB_G_PRINTER) += g_printer.o +obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o +obj-$(CONFIG_USB_CDC_COMPOSITE) += g_cdc.o +obj-$(CONFIG_USB_G_HID) += g_hid.o +obj-$(CONFIG_USB_G_DBGP) += g_dbgp.o +obj-$(CONFIG_USB_G_MULTI) += g_multi.o +obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o +obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o +obj-$(CONFIG_USB_G_NCM) += g_ncm.o +obj-$(CONFIG_USB_G_ACM_MS) += g_acm_ms.o +obj-$(CONFIG_USB_GADGET_TARGET) += tcm_usb_gadget.o diff --git a/drivers/usb/gadget/acm_ms.c b/drivers/usb/gadget/legacy/acm_ms.c index a252444cc0a7..c30b7b572465 100644 --- a/drivers/usb/gadget/acm_ms.c +++ b/drivers/usb/gadget/legacy/acm_ms.c @@ -267,18 +267,8 @@ static __refdata struct usb_composite_driver acm_ms_driver = { .unbind = __exit_p(acm_ms_unbind), }; +module_usb_composite_driver(acm_ms_driver); + MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR("Klaus Schwarzkopf <schwarzkopf@sensortherm.de>"); MODULE_LICENSE("GPL v2"); - -static int __init init(void) -{ - return usb_composite_probe(&acm_ms_driver); -} -module_init(init); - -static void __exit cleanup(void) -{ - usb_composite_unregister(&acm_ms_driver); -} -module_exit(cleanup); diff --git a/drivers/usb/gadget/audio.c b/drivers/usb/gadget/legacy/audio.c index 231b0efe8fdc..6eb695e5e43a 100644 --- a/drivers/usb/gadget/audio.c +++ b/drivers/usb/gadget/legacy/audio.c @@ -172,17 +172,7 @@ static __refdata struct usb_composite_driver audio_driver = { .unbind = __exit_p(audio_unbind), }; -static int __init init(void) -{ - return usb_composite_probe(&audio_driver); -} -module_init(init); - -static void __exit cleanup(void) -{ - usb_composite_unregister(&audio_driver); -} -module_exit(cleanup); +module_usb_composite_driver(audio_driver); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR("Bryan Wu <cooloney@kernel.org>"); diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/legacy/cdc2.c index e126b6b248e6..2e85d9473478 100644 --- a/drivers/usb/gadget/cdc2.c +++ b/drivers/usb/gadget/legacy/cdc2.c @@ -231,18 +231,8 @@ static __refdata struct usb_composite_driver cdc_driver = { .unbind = __exit_p(cdc_unbind), }; +module_usb_composite_driver(cdc_driver); + MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR("David Brownell"); MODULE_LICENSE("GPL"); - -static int __init init(void) -{ - return usb_composite_probe(&cdc_driver); -} -module_init(init); - -static void __exit cleanup(void) -{ - usb_composite_unregister(&cdc_driver); -} -module_exit(cleanup); diff --git a/drivers/usb/gadget/dbgp.c b/drivers/usb/gadget/legacy/dbgp.c index 986fc511a2ed..986fc511a2ed 100644 --- a/drivers/usb/gadget/dbgp.c +++ b/drivers/usb/gadget/legacy/dbgp.c diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/legacy/ether.c index c1c113ef950c..c5fdc61cdc4a 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/legacy/ether.c @@ -475,18 +475,8 @@ static __refdata struct usb_composite_driver eth_driver = { .unbind = __exit_p(eth_unbind), }; +module_usb_composite_driver(eth_driver); + MODULE_DESCRIPTION(PREFIX DRIVER_DESC); MODULE_AUTHOR("David Brownell, Benedikt Spanger"); MODULE_LICENSE("GPL"); - -static int __init init(void) -{ - return usb_composite_probe(ð_driver); -} -module_init(init); - -static void __exit cleanup(void) -{ - usb_composite_unregister(ð_driver); -} -module_exit(cleanup); diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/legacy/g_ffs.c index fe12e6a27448..06acfa55864a 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/legacy/g_ffs.c @@ -276,7 +276,7 @@ module_exit(gfs_exit); static void *functionfs_acquire_dev(struct ffs_dev *dev) { if (!try_module_get(THIS_MODULE)) - return ERR_PTR(-ENODEV); + return ERR_PTR(-ENOENT); return 0; } diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/legacy/gmidi.c index e879e2c9f461..3d696b86ff76 100644 --- a/drivers/usb/gadget/gmidi.c +++ b/drivers/usb/gadget/legacy/gmidi.c @@ -163,15 +163,4 @@ static __refdata struct usb_composite_driver midi_driver = { .unbind = __exit_p(midi_unbind), }; -static int __init midi_init(void) -{ - return usb_composite_probe(&midi_driver); -} -module_init(midi_init); - -static void __exit midi_cleanup(void) -{ - usb_composite_unregister(&midi_driver); -} -module_exit(midi_cleanup); - +module_usb_composite_driver(midi_driver); diff --git a/drivers/usb/gadget/hid.c b/drivers/usb/gadget/legacy/hid.c index 778613eb37af..778613eb37af 100644 --- a/drivers/usb/gadget/hid.c +++ b/drivers/usb/gadget/legacy/hid.c diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/legacy/inode.c index 2e4ce7704908..2e4ce7704908 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/legacy/inode.c diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/legacy/mass_storage.c index 8e27a8c96444..8e27a8c96444 100644 --- a/drivers/usb/gadget/mass_storage.c +++ b/drivers/usb/gadget/legacy/mass_storage.c diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/legacy/multi.c index 940f6cde8e89..39d27bb343b4 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/legacy/multi.c @@ -507,15 +507,4 @@ static __refdata struct usb_composite_driver multi_driver = { .needs_serial = 1, }; - -static int __init multi_init(void) -{ - return usb_composite_probe(&multi_driver); -} -module_init(multi_init); - -static void __exit multi_exit(void) -{ - usb_composite_unregister(&multi_driver); -} -module_exit(multi_exit); +module_usb_composite_driver(multi_driver); diff --git a/drivers/usb/gadget/ncm.c b/drivers/usb/gadget/legacy/ncm.c index 81956feca1bd..e90e23db2acb 100644 --- a/drivers/usb/gadget/ncm.c +++ b/drivers/usb/gadget/legacy/ncm.c @@ -204,18 +204,8 @@ static __refdata struct usb_composite_driver ncm_driver = { .unbind = __exit_p(gncm_unbind), }; +module_usb_composite_driver(ncm_driver); + MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR("Yauheni Kaliuta"); MODULE_LICENSE("GPL"); - -static int __init init(void) -{ - return usb_composite_probe(&ncm_driver); -} -module_init(init); - -static void __exit cleanup(void) -{ - usb_composite_unregister(&ncm_driver); -} -module_exit(cleanup); diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/legacy/nokia.c index 3ab386167519..9b8fd701648c 100644 --- a/drivers/usb/gadget/nokia.c +++ b/drivers/usb/gadget/legacy/nokia.c @@ -347,14 +347,4 @@ static __refdata struct usb_composite_driver nokia_driver = { .unbind = __exit_p(nokia_unbind), }; -static int __init nokia_init(void) -{ - return usb_composite_probe(&nokia_driver); -} -module_init(nokia_init); - -static void __exit nokia_cleanup(void) -{ - usb_composite_unregister(&nokia_driver); -} -module_exit(nokia_cleanup); +module_usb_composite_driver(nokia_driver); diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/legacy/printer.c index 6474081dcbaf..6474081dcbaf 100644 --- a/drivers/usb/gadget/printer.c +++ b/drivers/usb/gadget/legacy/printer.c diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/legacy/serial.c index 1f5f978d35d5..1f5f978d35d5 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/legacy/serial.c diff --git a/drivers/usb/gadget/tcm_usb_gadget.c b/drivers/usb/gadget/legacy/tcm_usb_gadget.c index 6cdb7a534f23..6cdb7a534f23 100644 --- a/drivers/usb/gadget/tcm_usb_gadget.c +++ b/drivers/usb/gadget/legacy/tcm_usb_gadget.c diff --git a/drivers/usb/gadget/tcm_usb_gadget.h b/drivers/usb/gadget/legacy/tcm_usb_gadget.h index 8289219925b8..8289219925b8 100644 --- a/drivers/usb/gadget/tcm_usb_gadget.h +++ b/drivers/usb/gadget/legacy/tcm_usb_gadget.h diff --git a/drivers/usb/gadget/webcam.c b/drivers/usb/gadget/legacy/webcam.c index 8cef1e658c29..a11d8e420bfe 100644 --- a/drivers/usb/gadget/webcam.c +++ b/drivers/usb/gadget/legacy/webcam.c @@ -390,20 +390,7 @@ static __refdata struct usb_composite_driver webcam_driver = { .unbind = webcam_unbind, }; -static int __init -webcam_init(void) -{ - return usb_composite_probe(&webcam_driver); -} - -static void __exit -webcam_cleanup(void) -{ - usb_composite_unregister(&webcam_driver); -} - -module_init(webcam_init); -module_exit(webcam_cleanup); +module_usb_composite_driver(webcam_driver); MODULE_AUTHOR("Laurent Pinchart"); MODULE_DESCRIPTION("Webcam Video Gadget"); diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/legacy/zero.c index 134f354ede62..c3d496828b74 100644 --- a/drivers/usb/gadget/zero.c +++ b/drivers/usb/gadget/legacy/zero.c @@ -411,17 +411,7 @@ static __refdata struct usb_composite_driver zero_driver = { .resume = zero_resume, }; +module_usb_composite_driver(zero_driver); + MODULE_AUTHOR("David Brownell"); MODULE_LICENSE("GPL"); - -static int __init init(void) -{ - return usb_composite_probe(&zero_driver); -} -module_init(init); - -static void __exit cleanup(void) -{ - usb_composite_unregister(&zero_driver); -} -module_exit(cleanup); diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c deleted file mode 100644 index 300b3a71383b..000000000000 --- a/drivers/usb/gadget/net2280.c +++ /dev/null @@ -1,2905 +0,0 @@ -/* - * Driver for the PLX NET2280 USB device controller. - * Specs and errata are available from <http://www.plxtech.com>. - * - * PLX Technology Inc. (formerly NetChip Technology) supported the - * development of this driver. - * - * - * CODE STATUS HIGHLIGHTS - * - * This driver should work well with most "gadget" drivers, including - * the Mass Storage, Serial, and Ethernet/RNDIS gadget drivers - * as well as Gadget Zero and Gadgetfs. - * - * DMA is enabled by default. Drivers using transfer queues might use - * DMA chaining to remove IRQ latencies between transfers. (Except when - * short OUT transfers happen.) Drivers can use the req->no_interrupt - * hint to completely eliminate some IRQs, if a later IRQ is guaranteed - * and DMA chaining is enabled. - * - * Note that almost all the errata workarounds here are only needed for - * rev1 chips. Rev1a silicon (0110) fixes almost all of them. - */ - -/* - * Copyright (C) 2003 David Brownell - * Copyright (C) 2003-2005 PLX Technology, Inc. - * - * Modified Seth Levy 2005 PLX Technology, Inc. to provide compatibility - * with 2282 chip - * - * 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. - */ - -#undef DEBUG /* messages on error and most fault paths */ -#undef VERBOSE /* extra debug messages (success too) */ - -#include <linux/module.h> -#include <linux/pci.h> -#include <linux/dma-mapping.h> -#include <linux/kernel.h> -#include <linux/delay.h> -#include <linux/ioport.h> -#include <linux/slab.h> -#include <linux/errno.h> -#include <linux/init.h> -#include <linux/timer.h> -#include <linux/list.h> -#include <linux/interrupt.h> -#include <linux/moduleparam.h> -#include <linux/device.h> -#include <linux/usb/ch9.h> -#include <linux/usb/gadget.h> -#include <linux/prefetch.h> - -#include <asm/byteorder.h> -#include <asm/io.h> -#include <asm/irq.h> -#include <asm/unaligned.h> - - -#define DRIVER_DESC "PLX NET228x USB Peripheral Controller" -#define DRIVER_VERSION "2005 Sept 27" - -#define EP_DONTUSE 13 /* nonzero */ - -#define USE_RDK_LEDS /* GPIO pins control three LEDs */ - - -static const char driver_name [] = "net2280"; -static const char driver_desc [] = DRIVER_DESC; - -static const char ep0name [] = "ep0"; -static const char *const ep_name [] = { - ep0name, - "ep-a", "ep-b", "ep-c", "ep-d", - "ep-e", "ep-f", -}; - -/* use_dma -- general goodness, fewer interrupts, less cpu load (vs PIO) - * use_dma_chaining -- dma descriptor queueing gives even more irq reduction - * - * The net2280 DMA engines are not tightly integrated with their FIFOs; - * not all cases are (yet) handled well in this driver or the silicon. - * Some gadget drivers work better with the dma support here than others. - * These two parameters let you use PIO or more aggressive DMA. - */ -static bool use_dma = 1; -static bool use_dma_chaining = 0; - -/* "modprobe net2280 use_dma=n" etc */ -module_param (use_dma, bool, S_IRUGO); -module_param (use_dma_chaining, bool, S_IRUGO); - - -/* mode 0 == ep-{a,b,c,d} 1K fifo each - * mode 1 == ep-{a,b} 2K fifo each, ep-{c,d} unavailable - * mode 2 == ep-a 2K fifo, ep-{b,c} 1K each, ep-d unavailable - */ -static ushort fifo_mode = 0; - -/* "modprobe net2280 fifo_mode=1" etc */ -module_param (fifo_mode, ushort, 0644); - -/* enable_suspend -- When enabled, the driver will respond to - * USB suspend requests by powering down the NET2280. Otherwise, - * USB suspend requests will be ignored. This is acceptable for - * self-powered devices - */ -static bool enable_suspend = 0; - -/* "modprobe net2280 enable_suspend=1" etc */ -module_param (enable_suspend, bool, S_IRUGO); - -/* force full-speed operation */ -static bool full_speed; -module_param(full_speed, bool, 0444); -MODULE_PARM_DESC(full_speed, "force full-speed mode -- for testing only!"); - -#define DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out") - -#if defined(CONFIG_USB_GADGET_DEBUG_FILES) || defined (DEBUG) -static char *type_string (u8 bmAttributes) -{ - switch ((bmAttributes) & USB_ENDPOINT_XFERTYPE_MASK) { - case USB_ENDPOINT_XFER_BULK: return "bulk"; - case USB_ENDPOINT_XFER_ISOC: return "iso"; - case USB_ENDPOINT_XFER_INT: return "intr"; - } - return "control"; -} -#endif - -#include "net2280.h" - -#define valid_bit cpu_to_le32 (1 << VALID_BIT) -#define dma_done_ie cpu_to_le32 (1 << DMA_DONE_INTERRUPT_ENABLE) - -/*-------------------------------------------------------------------------*/ - -static int -net2280_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) -{ - struct net2280 *dev; - struct net2280_ep *ep; - u32 max, tmp; - unsigned long flags; - - ep = container_of (_ep, struct net2280_ep, ep); - if (!_ep || !desc || ep->desc || _ep->name == ep0name - || desc->bDescriptorType != USB_DT_ENDPOINT) - return -EINVAL; - dev = ep->dev; - if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) - return -ESHUTDOWN; - - /* erratum 0119 workaround ties up an endpoint number */ - if ((desc->bEndpointAddress & 0x0f) == EP_DONTUSE) - return -EDOM; - - /* sanity check ep-e/ep-f since their fifos are small */ - max = usb_endpoint_maxp (desc) & 0x1fff; - if (ep->num > 4 && max > 64) - return -ERANGE; - - spin_lock_irqsave (&dev->lock, flags); - _ep->maxpacket = max & 0x7ff; - ep->desc = desc; - - /* ep_reset() has already been called */ - ep->stopped = 0; - ep->wedged = 0; - ep->out_overflow = 0; - - /* set speed-dependent max packet; may kick in high bandwidth */ - set_idx_reg (dev->regs, REG_EP_MAXPKT (dev, ep->num), max); - - /* FIFO lines can't go to different packets. PIO is ok, so - * use it instead of troublesome (non-bulk) multi-packet DMA. - */ - if (ep->dma && (max % 4) != 0 && use_dma_chaining) { - DEBUG (ep->dev, "%s, no dma for maxpacket %d\n", - ep->ep.name, ep->ep.maxpacket); - ep->dma = NULL; - } - - /* set type, direction, address; reset fifo counters */ - writel ((1 << FIFO_FLUSH), &ep->regs->ep_stat); - tmp = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK); - if (tmp == USB_ENDPOINT_XFER_INT) { - /* erratum 0105 workaround prevents hs NYET */ - if (dev->chiprev == 0100 - && dev->gadget.speed == USB_SPEED_HIGH - && !(desc->bEndpointAddress & USB_DIR_IN)) - writel ((1 << CLEAR_NAK_OUT_PACKETS_MODE), - &ep->regs->ep_rsp); - } else if (tmp == USB_ENDPOINT_XFER_BULK) { - /* catch some particularly blatant driver bugs */ - if ((dev->gadget.speed == USB_SPEED_HIGH - && max != 512) - || (dev->gadget.speed == USB_SPEED_FULL - && max > 64)) { - spin_unlock_irqrestore (&dev->lock, flags); - return -ERANGE; - } - } - ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC) ? 1 : 0; - tmp <<= ENDPOINT_TYPE; - tmp |= desc->bEndpointAddress; - tmp |= (4 << ENDPOINT_BYTE_COUNT); /* default full fifo lines */ - tmp |= 1 << ENDPOINT_ENABLE; - wmb (); - - /* for OUT transfers, block the rx fifo until a read is posted */ - ep->is_in = (tmp & USB_DIR_IN) != 0; - if (!ep->is_in) - writel ((1 << SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp); - else if (dev->pdev->device != 0x2280) { - /* Added for 2282, Don't use nak packets on an in endpoint, - * this was ignored on 2280 - */ - writel ((1 << CLEAR_NAK_OUT_PACKETS) - | (1 << CLEAR_NAK_OUT_PACKETS_MODE), &ep->regs->ep_rsp); - } - - writel (tmp, &ep->regs->ep_cfg); - - /* enable irqs */ - if (!ep->dma) { /* pio, per-packet */ - tmp = (1 << ep->num) | readl (&dev->regs->pciirqenb0); - writel (tmp, &dev->regs->pciirqenb0); - - tmp = (1 << DATA_PACKET_RECEIVED_INTERRUPT_ENABLE) - | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE); - if (dev->pdev->device == 0x2280) - tmp |= readl (&ep->regs->ep_irqenb); - writel (tmp, &ep->regs->ep_irqenb); - } else { /* dma, per-request */ - tmp = (1 << (8 + ep->num)); /* completion */ - tmp |= readl (&dev->regs->pciirqenb1); - writel (tmp, &dev->regs->pciirqenb1); - - /* for short OUT transfers, dma completions can't - * advance the queue; do it pio-style, by hand. - * NOTE erratum 0112 workaround #2 - */ - if ((desc->bEndpointAddress & USB_DIR_IN) == 0) { - tmp = (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT_ENABLE); - writel (tmp, &ep->regs->ep_irqenb); - - tmp = (1 << ep->num) | readl (&dev->regs->pciirqenb0); - writel (tmp, &dev->regs->pciirqenb0); - } - } - - tmp = desc->bEndpointAddress; - DEBUG (dev, "enabled %s (ep%d%s-%s) %s max %04x\n", - _ep->name, tmp & 0x0f, DIR_STRING (tmp), - type_string (desc->bmAttributes), - ep->dma ? "dma" : "pio", max); - - /* pci writes may still be posted */ - spin_unlock_irqrestore (&dev->lock, flags); - return 0; -} - -static int handshake (u32 __iomem *ptr, u32 mask, u32 done, int usec) -{ - u32 result; - - do { - result = readl (ptr); - if (result == ~(u32)0) /* "device unplugged" */ - return -ENODEV; - result &= mask; - if (result == done) - return 0; - udelay (1); - usec--; - } while (usec > 0); - return -ETIMEDOUT; -} - -static const struct usb_ep_ops net2280_ep_ops; - -static void ep_reset (struct net2280_regs __iomem *regs, struct net2280_ep *ep) -{ - u32 tmp; - - ep->desc = NULL; - INIT_LIST_HEAD (&ep->queue); - - usb_ep_set_maxpacket_limit(&ep->ep, ~0); - ep->ep.ops = &net2280_ep_ops; - - /* disable the dma, irqs, endpoint... */ - if (ep->dma) { - writel (0, &ep->dma->dmactl); - writel ( (1 << DMA_SCATTER_GATHER_DONE_INTERRUPT) - | (1 << DMA_TRANSACTION_DONE_INTERRUPT) - | (1 << DMA_ABORT) - , &ep->dma->dmastat); - - tmp = readl (®s->pciirqenb0); - tmp &= ~(1 << ep->num); - writel (tmp, ®s->pciirqenb0); - } else { - tmp = readl (®s->pciirqenb1); - tmp &= ~(1 << (8 + ep->num)); /* completion */ - writel (tmp, ®s->pciirqenb1); - } - writel (0, &ep->regs->ep_irqenb); - - /* init to our chosen defaults, notably so that we NAK OUT - * packets until the driver queues a read (+note erratum 0112) - */ - if (!ep->is_in || ep->dev->pdev->device == 0x2280) { - tmp = (1 << SET_NAK_OUT_PACKETS_MODE) - | (1 << SET_NAK_OUT_PACKETS) - | (1 << CLEAR_EP_HIDE_STATUS_PHASE) - | (1 << CLEAR_INTERRUPT_MODE); - } else { - /* added for 2282 */ - tmp = (1 << CLEAR_NAK_OUT_PACKETS_MODE) - | (1 << CLEAR_NAK_OUT_PACKETS) - | (1 << CLEAR_EP_HIDE_STATUS_PHASE) - | (1 << CLEAR_INTERRUPT_MODE); - } - - if (ep->num != 0) { - tmp |= (1 << CLEAR_ENDPOINT_TOGGLE) - | (1 << CLEAR_ENDPOINT_HALT); - } - writel (tmp, &ep->regs->ep_rsp); - - /* scrub most status bits, and flush any fifo state */ - if (ep->dev->pdev->device == 0x2280) - tmp = (1 << FIFO_OVERFLOW) - | (1 << FIFO_UNDERFLOW); - else - tmp = 0; - - writel (tmp | (1 << TIMEOUT) - | (1 << USB_STALL_SENT) - | (1 << USB_IN_NAK_SENT) - | (1 << USB_IN_ACK_RCVD) - | (1 << USB_OUT_PING_NAK_SENT) - | (1 << USB_OUT_ACK_SENT) - | (1 << FIFO_FLUSH) - | (1 << SHORT_PACKET_OUT_DONE_INTERRUPT) - | (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT) - | (1 << DATA_PACKET_RECEIVED_INTERRUPT) - | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT) - | (1 << DATA_OUT_PING_TOKEN_INTERRUPT) - | (1 << DATA_IN_TOKEN_INTERRUPT) - , &ep->regs->ep_stat); - - /* fifo size is handled separately */ -} - -static void nuke (struct net2280_ep *); - -static int net2280_disable (struct usb_ep *_ep) -{ - struct net2280_ep *ep; - unsigned long flags; - - ep = container_of (_ep, struct net2280_ep, ep); - if (!_ep || !ep->desc || _ep->name == ep0name) - return -EINVAL; - - spin_lock_irqsave (&ep->dev->lock, flags); - nuke (ep); - ep_reset (ep->dev->regs, ep); - - VDEBUG (ep->dev, "disabled %s %s\n", - ep->dma ? "dma" : "pio", _ep->name); - - /* synch memory views with the device */ - (void) readl (&ep->regs->ep_cfg); - - if (use_dma && !ep->dma && ep->num >= 1 && ep->num <= 4) - ep->dma = &ep->dev->dma [ep->num - 1]; - - spin_unlock_irqrestore (&ep->dev->lock, flags); - return 0; -} - -/*-------------------------------------------------------------------------*/ - -static struct usb_request * -net2280_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags) -{ - struct net2280_ep *ep; - struct net2280_request *req; - - if (!_ep) - return NULL; - ep = container_of (_ep, struct net2280_ep, ep); - - req = kzalloc(sizeof(*req), gfp_flags); - if (!req) - return NULL; - - INIT_LIST_HEAD (&req->queue); - - /* this dma descriptor may be swapped with the previous dummy */ - if (ep->dma) { - struct net2280_dma *td; - - td = pci_pool_alloc (ep->dev->requests, gfp_flags, - &req->td_dma); - if (!td) { - kfree (req); - return NULL; - } - td->dmacount = 0; /* not VALID */ - td->dmadesc = td->dmaaddr; - req->td = td; - } - return &req->req; -} - -static void -net2280_free_request (struct usb_ep *_ep, struct usb_request *_req) -{ - struct net2280_ep *ep; - struct net2280_request *req; - - ep = container_of (_ep, struct net2280_ep, ep); - if (!_ep || !_req) - return; - - req = container_of (_req, struct net2280_request, req); - WARN_ON (!list_empty (&req->queue)); - if (req->td) - pci_pool_free (ep->dev->requests, req->td, req->td_dma); - kfree (req); -} - -/*-------------------------------------------------------------------------*/ - -/* load a packet into the fifo we use for usb IN transfers. - * works for all endpoints. - * - * NOTE: pio with ep-a..ep-d could stuff multiple packets into the fifo - * at a time, but this code is simpler because it knows it only writes - * one packet. ep-a..ep-d should use dma instead. - */ -static void -write_fifo (struct net2280_ep *ep, struct usb_request *req) -{ - struct net2280_ep_regs __iomem *regs = ep->regs; - u8 *buf; - u32 tmp; - unsigned count, total; - - /* INVARIANT: fifo is currently empty. (testable) */ - - if (req) { - buf = req->buf + req->actual; - prefetch (buf); - total = req->length - req->actual; - } else { - total = 0; - buf = NULL; - } - - /* write just one packet at a time */ - count = ep->ep.maxpacket; - if (count > total) /* min() cannot be used on a bitfield */ - count = total; - - VDEBUG (ep->dev, "write %s fifo (IN) %d bytes%s req %p\n", - ep->ep.name, count, - (count != ep->ep.maxpacket) ? " (short)" : "", - req); - while (count >= 4) { - /* NOTE be careful if you try to align these. fifo lines - * should normally be full (4 bytes) and successive partial - * lines are ok only in certain cases. - */ - tmp = get_unaligned ((u32 *)buf); - cpu_to_le32s (&tmp); - writel (tmp, ®s->ep_data); - buf += 4; - count -= 4; - } - - /* last fifo entry is "short" unless we wrote a full packet. - * also explicitly validate last word in (periodic) transfers - * when maxpacket is not a multiple of 4 bytes. - */ - if (count || total < ep->ep.maxpacket) { - tmp = count ? get_unaligned ((u32 *)buf) : count; - cpu_to_le32s (&tmp); - set_fifo_bytecount (ep, count & 0x03); - writel (tmp, ®s->ep_data); - } - - /* pci writes may still be posted */ -} - -/* work around erratum 0106: PCI and USB race over the OUT fifo. - * caller guarantees chiprev 0100, out endpoint is NAKing, and - * there's no real data in the fifo. - * - * NOTE: also used in cases where that erratum doesn't apply: - * where the host wrote "too much" data to us. - */ -static void out_flush (struct net2280_ep *ep) -{ - u32 __iomem *statp; - u32 tmp; - - ASSERT_OUT_NAKING (ep); - - statp = &ep->regs->ep_stat; - writel ( (1 << DATA_OUT_PING_TOKEN_INTERRUPT) - | (1 << DATA_PACKET_RECEIVED_INTERRUPT) - , statp); - writel ((1 << FIFO_FLUSH), statp); - mb (); - tmp = readl (statp); - if (tmp & (1 << DATA_OUT_PING_TOKEN_INTERRUPT) - /* high speed did bulk NYET; fifo isn't filling */ - && ep->dev->gadget.speed == USB_SPEED_FULL) { - unsigned usec; - - usec = 50; /* 64 byte bulk/interrupt */ - handshake (statp, (1 << USB_OUT_PING_NAK_SENT), - (1 << USB_OUT_PING_NAK_SENT), usec); - /* NAK done; now CLEAR_NAK_OUT_PACKETS is safe */ - } -} - -/* unload packet(s) from the fifo we use for usb OUT transfers. - * returns true iff the request completed, because of short packet - * or the request buffer having filled with full packets. - * - * for ep-a..ep-d this will read multiple packets out when they - * have been accepted. - */ -static int -read_fifo (struct net2280_ep *ep, struct net2280_request *req) -{ - struct net2280_ep_regs __iomem *regs = ep->regs; - u8 *buf = req->req.buf + req->req.actual; - unsigned count, tmp, is_short; - unsigned cleanup = 0, prevent = 0; - - /* erratum 0106 ... packets coming in during fifo reads might - * be incompletely rejected. not all cases have workarounds. - */ - if (ep->dev->chiprev == 0x0100 - && ep->dev->gadget.speed == USB_SPEED_FULL) { - udelay (1); - tmp = readl (&ep->regs->ep_stat); - if ((tmp & (1 << NAK_OUT_PACKETS))) - cleanup = 1; - else if ((tmp & (1 << FIFO_FULL))) { - start_out_naking (ep); - prevent = 1; - } - /* else: hope we don't see the problem */ - } - - /* never overflow the rx buffer. the fifo reads packets until - * it sees a short one; we might not be ready for them all. - */ - prefetchw (buf); - count = readl (®s->ep_avail); - if (unlikely (count == 0)) { - udelay (1); - tmp = readl (&ep->regs->ep_stat); - count = readl (®s->ep_avail); - /* handled that data already? */ - if (count == 0 && (tmp & (1 << NAK_OUT_PACKETS)) == 0) - return 0; - } - - tmp = req->req.length - req->req.actual; - if (count > tmp) { - /* as with DMA, data overflow gets flushed */ - if ((tmp % ep->ep.maxpacket) != 0) { - ERROR (ep->dev, - "%s out fifo %d bytes, expected %d\n", - ep->ep.name, count, tmp); - req->req.status = -EOVERFLOW; - cleanup = 1; - /* NAK_OUT_PACKETS will be set, so flushing is safe; - * the next read will start with the next packet - */ - } /* else it's a ZLP, no worries */ - count = tmp; - } - req->req.actual += count; - - is_short = (count == 0) || ((count % ep->ep.maxpacket) != 0); - - VDEBUG (ep->dev, "read %s fifo (OUT) %d bytes%s%s%s req %p %d/%d\n", - ep->ep.name, count, is_short ? " (short)" : "", - cleanup ? " flush" : "", prevent ? " nak" : "", - req, req->req.actual, req->req.length); - - while (count >= 4) { - tmp = readl (®s->ep_data); - cpu_to_le32s (&tmp); - put_unaligned (tmp, (u32 *)buf); - buf += 4; - count -= 4; - } - if (count) { - tmp = readl (®s->ep_data); - /* LE conversion is implicit here: */ - do { - *buf++ = (u8) tmp; - tmp >>= 8; - } while (--count); - } - if (cleanup) - out_flush (ep); - if (prevent) { - writel ((1 << CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp); - (void) readl (&ep->regs->ep_rsp); - } - - return is_short || ((req->req.actual == req->req.length) - && !req->req.zero); -} - -/* fill out dma descriptor to match a given request */ -static void -fill_dma_desc (struct net2280_ep *ep, struct net2280_request *req, int valid) -{ - struct net2280_dma *td = req->td; - u32 dmacount = req->req.length; - - /* don't let DMA continue after a short OUT packet, - * so overruns can't affect the next transfer. - * in case of overruns on max-size packets, we can't - * stop the fifo from filling but we can flush it. - */ - if (ep->is_in) - dmacount |= (1 << DMA_DIRECTION); - if ((!ep->is_in && (dmacount % ep->ep.maxpacket) != 0) - || ep->dev->pdev->device != 0x2280) - dmacount |= (1 << END_OF_CHAIN); - - req->valid = valid; - if (valid) - dmacount |= (1 << VALID_BIT); - if (likely(!req->req.no_interrupt || !use_dma_chaining)) - dmacount |= (1 << DMA_DONE_INTERRUPT_ENABLE); - - /* td->dmadesc = previously set by caller */ - td->dmaaddr = cpu_to_le32 (req->req.dma); - - /* 2280 may be polling VALID_BIT through ep->dma->dmadesc */ - wmb (); - td->dmacount = cpu_to_le32(dmacount); -} - -static const u32 dmactl_default = - (1 << DMA_SCATTER_GATHER_DONE_INTERRUPT) - | (1 << DMA_CLEAR_COUNT_ENABLE) - /* erratum 0116 workaround part 1 (use POLLING) */ - | (POLL_100_USEC << DESCRIPTOR_POLLING_RATE) - | (1 << DMA_VALID_BIT_POLLING_ENABLE) - | (1 << DMA_VALID_BIT_ENABLE) - | (1 << DMA_SCATTER_GATHER_ENABLE) - /* erratum 0116 workaround part 2 (no AUTOSTART) */ - | (1 << DMA_ENABLE); - -static inline void spin_stop_dma (struct net2280_dma_regs __iomem *dma) -{ - handshake (&dma->dmactl, (1 << DMA_ENABLE), 0, 50); -} - -static inline void stop_dma (struct net2280_dma_regs __iomem *dma) -{ - writel (readl (&dma->dmactl) & ~(1 << DMA_ENABLE), &dma->dmactl); - spin_stop_dma (dma); -} - -static void start_queue (struct net2280_ep *ep, u32 dmactl, u32 td_dma) -{ - struct net2280_dma_regs __iomem *dma = ep->dma; - unsigned int tmp = (1 << VALID_BIT) | (ep->is_in << DMA_DIRECTION); - - if (ep->dev->pdev->device != 0x2280) - tmp |= (1 << END_OF_CHAIN); - - writel (tmp, &dma->dmacount); - writel (readl (&dma->dmastat), &dma->dmastat); - - writel (td_dma, &dma->dmadesc); - writel (dmactl, &dma->dmactl); - - /* erratum 0116 workaround part 3: pci arbiter away from net2280 */ - (void) readl (&ep->dev->pci->pcimstctl); - - writel ((1 << DMA_START), &dma->dmastat); - - if (!ep->is_in) - stop_out_naking (ep); -} - -static void start_dma (struct net2280_ep *ep, struct net2280_request *req) -{ - u32 tmp; - struct net2280_dma_regs __iomem *dma = ep->dma; - - /* FIXME can't use DMA for ZLPs */ - - /* on this path we "know" there's no dma active (yet) */ - WARN_ON (readl (&dma->dmactl) & (1 << DMA_ENABLE)); - writel (0, &ep->dma->dmactl); - - /* previous OUT packet might have been short */ - if (!ep->is_in && ((tmp = readl (&ep->regs->ep_stat)) - & (1 << NAK_OUT_PACKETS)) != 0) { - writel ((1 << SHORT_PACKET_TRANSFERRED_INTERRUPT), - &ep->regs->ep_stat); - - tmp = readl (&ep->regs->ep_avail); - if (tmp) { - writel (readl (&dma->dmastat), &dma->dmastat); - - /* transfer all/some fifo data */ - writel (req->req.dma, &dma->dmaaddr); - tmp = min (tmp, req->req.length); - - /* dma irq, faking scatterlist status */ - req->td->dmacount = cpu_to_le32 (req->req.length - tmp); - writel ((1 << DMA_DONE_INTERRUPT_ENABLE) - | tmp, &dma->dmacount); - req->td->dmadesc = 0; - req->valid = 1; - - writel ((1 << DMA_ENABLE), &dma->dmactl); - writel ((1 << DMA_START), &dma->dmastat); - return; - } - } - - tmp = dmactl_default; - - /* force packet boundaries between dma requests, but prevent the - * controller from automagically writing a last "short" packet - * (zero length) unless the driver explicitly said to do that. - */ - if (ep->is_in) { - if (likely ((req->req.length % ep->ep.maxpacket) != 0 - || req->req.zero)) { - tmp |= (1 << DMA_FIFO_VALIDATE); - ep->in_fifo_validate = 1; - } else - ep->in_fifo_validate = 0; - } - - /* init req->td, pointing to the current dummy */ - req->td->dmadesc = cpu_to_le32 (ep->td_dma); - fill_dma_desc (ep, req, 1); - - if (!use_dma_chaining) - req->td->dmacount |= cpu_to_le32 (1 << END_OF_CHAIN); - - start_queue (ep, tmp, req->td_dma); -} - -static inline void -queue_dma (struct net2280_ep *ep, struct net2280_request *req, int valid) -{ - struct net2280_dma *end; - dma_addr_t tmp; - - /* swap new dummy for old, link; fill and maybe activate */ - end = ep->dummy; - ep->dummy = req->td; - req->td = end; - - tmp = ep->td_dma; - ep->td_dma = req->td_dma; - req->td_dma = tmp; - - end->dmadesc = cpu_to_le32 (ep->td_dma); - - fill_dma_desc (ep, req, valid); -} - -static void -done (struct net2280_ep *ep, struct net2280_request *req, int status) -{ - struct net2280 *dev; - unsigned stopped = ep->stopped; - - list_del_init (&req->queue); - - if (req->req.status == -EINPROGRESS) - req->req.status = status; - else - status = req->req.status; - - dev = ep->dev; - if (ep->dma) - usb_gadget_unmap_request(&dev->gadget, &req->req, ep->is_in); - - if (status && status != -ESHUTDOWN) - VDEBUG (dev, "complete %s req %p stat %d len %u/%u\n", - ep->ep.name, &req->req, status, - req->req.actual, req->req.length); - - /* don't modify queue heads during completion callback */ - ep->stopped = 1; - spin_unlock (&dev->lock); - req->req.complete (&ep->ep, &req->req); - spin_lock (&dev->lock); - ep->stopped = stopped; -} - -/*-------------------------------------------------------------------------*/ - -static int -net2280_queue (struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) -{ - struct net2280_request *req; - struct net2280_ep *ep; - struct net2280 *dev; - unsigned long flags; - - /* we always require a cpu-view buffer, so that we can - * always use pio (as fallback or whatever). - */ - req = container_of (_req, struct net2280_request, req); - if (!_req || !_req->complete || !_req->buf - || !list_empty (&req->queue)) - return -EINVAL; - if (_req->length > (~0 & DMA_BYTE_COUNT_MASK)) - return -EDOM; - ep = container_of (_ep, struct net2280_ep, ep); - if (!_ep || (!ep->desc && ep->num != 0)) - return -EINVAL; - dev = ep->dev; - if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) - return -ESHUTDOWN; - - /* FIXME implement PIO fallback for ZLPs with DMA */ - if (ep->dma && _req->length == 0) - return -EOPNOTSUPP; - - /* set up dma mapping in case the caller didn't */ - if (ep->dma) { - int ret; - - ret = usb_gadget_map_request(&dev->gadget, _req, - ep->is_in); - if (ret) - return ret; - } - -#if 0 - VDEBUG (dev, "%s queue req %p, len %d buf %p\n", - _ep->name, _req, _req->length, _req->buf); -#endif - - spin_lock_irqsave (&dev->lock, flags); - - _req->status = -EINPROGRESS; - _req->actual = 0; - - /* kickstart this i/o queue? */ - if (list_empty (&ep->queue) && !ep->stopped) { - /* use DMA if the endpoint supports it, else pio */ - if (ep->dma) - start_dma (ep, req); - else { - /* maybe there's no control data, just status ack */ - if (ep->num == 0 && _req->length == 0) { - allow_status (ep); - done (ep, req, 0); - VDEBUG (dev, "%s status ack\n", ep->ep.name); - goto done; - } - - /* PIO ... stuff the fifo, or unblock it. */ - if (ep->is_in) - write_fifo (ep, _req); - else if (list_empty (&ep->queue)) { - u32 s; - - /* OUT FIFO might have packet(s) buffered */ - s = readl (&ep->regs->ep_stat); - if ((s & (1 << FIFO_EMPTY)) == 0) { - /* note: _req->short_not_ok is - * ignored here since PIO _always_ - * stops queue advance here, and - * _req->status doesn't change for - * short reads (only _req->actual) - */ - if (read_fifo (ep, req)) { - done (ep, req, 0); - if (ep->num == 0) - allow_status (ep); - /* don't queue it */ - req = NULL; - } else - s = readl (&ep->regs->ep_stat); - } - - /* don't NAK, let the fifo fill */ - if (req && (s & (1 << NAK_OUT_PACKETS))) - writel ((1 << CLEAR_NAK_OUT_PACKETS), - &ep->regs->ep_rsp); - } - } - - } else if (ep->dma) { - int valid = 1; - - if (ep->is_in) { - int expect; - - /* preventing magic zlps is per-engine state, not - * per-transfer; irq logic must recover hiccups. - */ - expect = likely (req->req.zero - || (req->req.length % ep->ep.maxpacket) != 0); - if (expect != ep->in_fifo_validate) - valid = 0; - } - queue_dma (ep, req, valid); - - } /* else the irq handler advances the queue. */ - - ep->responded = 1; - if (req) - list_add_tail (&req->queue, &ep->queue); -done: - spin_unlock_irqrestore (&dev->lock, flags); - - /* pci writes may still be posted */ - return 0; -} - -static inline void -dma_done ( - struct net2280_ep *ep, - struct net2280_request *req, - u32 dmacount, - int status -) -{ - req->req.actual = req->req.length - (DMA_BYTE_COUNT_MASK & dmacount); - done (ep, req, status); -} - -static void restart_dma (struct net2280_ep *ep); - -static void scan_dma_completions (struct net2280_ep *ep) -{ - /* only look at descriptors that were "naturally" retired, - * so fifo and list head state won't matter - */ - while (!list_empty (&ep->queue)) { - struct net2280_request *req; - u32 tmp; - - req = list_entry (ep->queue.next, - struct net2280_request, queue); - if (!req->valid) - break; - rmb (); - tmp = le32_to_cpup (&req->td->dmacount); - if ((tmp & (1 << VALID_BIT)) != 0) - break; - - /* SHORT_PACKET_TRANSFERRED_INTERRUPT handles "usb-short" - * cases where DMA must be aborted; this code handles - * all non-abort DMA completions. - */ - if (unlikely (req->td->dmadesc == 0)) { - /* paranoia */ - tmp = readl (&ep->dma->dmacount); - if (tmp & DMA_BYTE_COUNT_MASK) - break; - /* single transfer mode */ - dma_done (ep, req, tmp, 0); - break; - } else if (!ep->is_in - && (req->req.length % ep->ep.maxpacket) != 0) { - tmp = readl (&ep->regs->ep_stat); - - /* AVOID TROUBLE HERE by not issuing short reads from - * your gadget driver. That helps avoids errata 0121, - * 0122, and 0124; not all cases trigger the warning. - */ - if ((tmp & (1 << NAK_OUT_PACKETS)) == 0) { - WARNING (ep->dev, "%s lost packet sync!\n", - ep->ep.name); - req->req.status = -EOVERFLOW; - } else if ((tmp = readl (&ep->regs->ep_avail)) != 0) { - /* fifo gets flushed later */ - ep->out_overflow = 1; - DEBUG (ep->dev, "%s dma, discard %d len %d\n", - ep->ep.name, tmp, - req->req.length); - req->req.status = -EOVERFLOW; - } - } - dma_done (ep, req, tmp, 0); - } -} - -static void restart_dma (struct net2280_ep *ep) -{ - struct net2280_request *req; - u32 dmactl = dmactl_default; - - if (ep->stopped) - return; - req = list_entry (ep->queue.next, struct net2280_request, queue); - - if (!use_dma_chaining) { - start_dma (ep, req); - return; - } - - /* the 2280 will be processing the queue unless queue hiccups after - * the previous transfer: - * IN: wanted automagic zlp, head doesn't (or vice versa) - * DMA_FIFO_VALIDATE doesn't init from dma descriptors. - * OUT: was "usb-short", we must restart. - */ - if (ep->is_in && !req->valid) { - struct net2280_request *entry, *prev = NULL; - int reqmode, done = 0; - - DEBUG (ep->dev, "%s dma hiccup td %p\n", ep->ep.name, req->td); - ep->in_fifo_validate = likely (req->req.zero - || (req->req.length % ep->ep.maxpacket) != 0); - if (ep->in_fifo_validate) - dmactl |= (1 << DMA_FIFO_VALIDATE); - list_for_each_entry (entry, &ep->queue, queue) { - __le32 dmacount; - - if (entry == req) - continue; - dmacount = entry->td->dmacount; - if (!done) { - reqmode = likely (entry->req.zero - || (entry->req.length - % ep->ep.maxpacket) != 0); - if (reqmode == ep->in_fifo_validate) { - entry->valid = 1; - dmacount |= valid_bit; - entry->td->dmacount = dmacount; - prev = entry; - continue; - } else { - /* force a hiccup */ - prev->td->dmacount |= dma_done_ie; - done = 1; - } - } - - /* walk the rest of the queue so unlinks behave */ - entry->valid = 0; - dmacount &= ~valid_bit; - entry->td->dmacount = dmacount; - prev = entry; - } - } - - writel (0, &ep->dma->dmactl); - start_queue (ep, dmactl, req->td_dma); -} - -static void abort_dma (struct net2280_ep *ep) -{ - /* abort the current transfer */ - if (likely (!list_empty (&ep->queue))) { - /* FIXME work around errata 0121, 0122, 0124 */ - writel ((1 << DMA_ABORT), &ep->dma->dmastat); - spin_stop_dma (ep->dma); - } else - stop_dma (ep->dma); - scan_dma_completions (ep); -} - -/* dequeue ALL requests */ -static void nuke (struct net2280_ep *ep) -{ - struct net2280_request *req; - - /* called with spinlock held */ - ep->stopped = 1; - if (ep->dma) - abort_dma (ep); - while (!list_empty (&ep->queue)) { - req = list_entry (ep->queue.next, - struct net2280_request, - queue); - done (ep, req, -ESHUTDOWN); - } -} - -/* dequeue JUST ONE request */ -static int net2280_dequeue (struct usb_ep *_ep, struct usb_request *_req) -{ - struct net2280_ep *ep; - struct net2280_request *req; - unsigned long flags; - u32 dmactl; - int stopped; - - ep = container_of (_ep, struct net2280_ep, ep); - if (!_ep || (!ep->desc && ep->num != 0) || !_req) - return -EINVAL; - - spin_lock_irqsave (&ep->dev->lock, flags); - stopped = ep->stopped; - - /* quiesce dma while we patch the queue */ - dmactl = 0; - ep->stopped = 1; - if (ep->dma) { - dmactl = readl (&ep->dma->dmactl); - /* WARNING erratum 0127 may kick in ... */ - stop_dma (ep->dma); - scan_dma_completions (ep); - } - - /* make sure it's still queued on this endpoint */ - list_for_each_entry (req, &ep->queue, queue) { - if (&req->req == _req) - break; - } - if (&req->req != _req) { - spin_unlock_irqrestore (&ep->dev->lock, flags); - return -EINVAL; - } - - /* queue head may be partially complete. */ - if (ep->queue.next == &req->queue) { - if (ep->dma) { - DEBUG (ep->dev, "unlink (%s) dma\n", _ep->name); - _req->status = -ECONNRESET; - abort_dma (ep); - if (likely (ep->queue.next == &req->queue)) { - // NOTE: misreports single-transfer mode - req->td->dmacount = 0; /* invalidate */ - dma_done (ep, req, - readl (&ep->dma->dmacount), - -ECONNRESET); - } - } else { - DEBUG (ep->dev, "unlink (%s) pio\n", _ep->name); - done (ep, req, -ECONNRESET); - } - req = NULL; - - /* patch up hardware chaining data */ - } else if (ep->dma && use_dma_chaining) { - if (req->queue.prev == ep->queue.next) { - writel (le32_to_cpu (req->td->dmadesc), - &ep->dma->dmadesc); - if (req->td->dmacount & dma_done_ie) - writel (readl (&ep->dma->dmacount) - | le32_to_cpu(dma_done_ie), - &ep->dma->dmacount); - } else { - struct net2280_request *prev; - - prev = list_entry (req->queue.prev, - struct net2280_request, queue); - prev->td->dmadesc = req->td->dmadesc; - if (req->td->dmacount & dma_done_ie) - prev->td->dmacount |= dma_done_ie; - } - } - - if (req) - done (ep, req, -ECONNRESET); - ep->stopped = stopped; - - if (ep->dma) { - /* turn off dma on inactive queues */ - if (list_empty (&ep->queue)) - stop_dma (ep->dma); - else if (!ep->stopped) { - /* resume current request, or start new one */ - if (req) - writel (dmactl, &ep->dma->dmactl); - else - start_dma (ep, list_entry (ep->queue.next, - struct net2280_request, queue)); - } - } - - spin_unlock_irqrestore (&ep->dev->lock, flags); - return 0; -} - -/*-------------------------------------------------------------------------*/ - -static int net2280_fifo_status (struct usb_ep *_ep); - -static int -net2280_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged) -{ - struct net2280_ep *ep; - unsigned long flags; - int retval = 0; - - ep = container_of (_ep, struct net2280_ep, ep); - if (!_ep || (!ep->desc && ep->num != 0)) - return -EINVAL; - if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) - return -ESHUTDOWN; - if (ep->desc /* not ep0 */ && (ep->desc->bmAttributes & 0x03) - == USB_ENDPOINT_XFER_ISOC) - return -EINVAL; - - spin_lock_irqsave (&ep->dev->lock, flags); - if (!list_empty (&ep->queue)) - retval = -EAGAIN; - else if (ep->is_in && value && net2280_fifo_status (_ep) != 0) - retval = -EAGAIN; - else { - VDEBUG (ep->dev, "%s %s %s\n", _ep->name, - value ? "set" : "clear", - wedged ? "wedge" : "halt"); - /* set/clear, then synch memory views with the device */ - if (value) { - if (ep->num == 0) - ep->dev->protocol_stall = 1; - else - set_halt (ep); - if (wedged) - ep->wedged = 1; - } else { - clear_halt (ep); - ep->wedged = 0; - } - (void) readl (&ep->regs->ep_rsp); - } - spin_unlock_irqrestore (&ep->dev->lock, flags); - - return retval; -} - -static int -net2280_set_halt(struct usb_ep *_ep, int value) -{ - return net2280_set_halt_and_wedge(_ep, value, 0); -} - -static int -net2280_set_wedge(struct usb_ep *_ep) -{ - if (!_ep || _ep->name == ep0name) - return -EINVAL; - return net2280_set_halt_and_wedge(_ep, 1, 1); -} - -static int -net2280_fifo_status (struct usb_ep *_ep) -{ - struct net2280_ep *ep; - u32 avail; - - ep = container_of (_ep, struct net2280_ep, ep); - if (!_ep || (!ep->desc && ep->num != 0)) - return -ENODEV; - if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) - return -ESHUTDOWN; - - avail = readl (&ep->regs->ep_avail) & ((1 << 12) - 1); - if (avail > ep->fifo_size) - return -EOVERFLOW; - if (ep->is_in) - avail = ep->fifo_size - avail; - return avail; -} - -static void -net2280_fifo_flush (struct usb_ep *_ep) -{ - struct net2280_ep *ep; - - ep = container_of (_ep, struct net2280_ep, ep); - if (!_ep || (!ep->desc && ep->num != 0)) - return; - if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) - return; - - writel ((1 << FIFO_FLUSH), &ep->regs->ep_stat); - (void) readl (&ep->regs->ep_rsp); -} - -static const struct usb_ep_ops net2280_ep_ops = { - .enable = net2280_enable, - .disable = net2280_disable, - - .alloc_request = net2280_alloc_request, - .free_request = net2280_free_request, - - .queue = net2280_queue, - .dequeue = net2280_dequeue, - - .set_halt = net2280_set_halt, - .set_wedge = net2280_set_wedge, - .fifo_status = net2280_fifo_status, - .fifo_flush = net2280_fifo_flush, -}; - -/*-------------------------------------------------------------------------*/ - -static int net2280_get_frame (struct usb_gadget *_gadget) -{ - struct net2280 *dev; - unsigned long flags; - u16 retval; - - if (!_gadget) - return -ENODEV; - dev = container_of (_gadget, struct net2280, gadget); - spin_lock_irqsave (&dev->lock, flags); - retval = get_idx_reg (dev->regs, REG_FRAME) & 0x03ff; - spin_unlock_irqrestore (&dev->lock, flags); - return retval; -} - -static int net2280_wakeup (struct usb_gadget *_gadget) -{ - struct net2280 *dev; - u32 tmp; - unsigned long flags; - - if (!_gadget) - return 0; - dev = container_of (_gadget, struct net2280, gadget); - - spin_lock_irqsave (&dev->lock, flags); - tmp = readl (&dev->usb->usbctl); - if (tmp & (1 << DEVICE_REMOTE_WAKEUP_ENABLE)) - writel (1 << GENERATE_RESUME, &dev->usb->usbstat); - spin_unlock_irqrestore (&dev->lock, flags); - - /* pci writes may still be posted */ - return 0; -} - -static int net2280_set_selfpowered (struct usb_gadget *_gadget, int value) -{ - struct net2280 *dev; - u32 tmp; - unsigned long flags; - - if (!_gadget) - return 0; - dev = container_of (_gadget, struct net2280, gadget); - - spin_lock_irqsave (&dev->lock, flags); - tmp = readl (&dev->usb->usbctl); - if (value) - tmp |= (1 << SELF_POWERED_STATUS); - else - tmp &= ~(1 << SELF_POWERED_STATUS); - writel (tmp, &dev->usb->usbctl); - spin_unlock_irqrestore (&dev->lock, flags); - - return 0; -} - -static int net2280_pullup(struct usb_gadget *_gadget, int is_on) -{ - struct net2280 *dev; - u32 tmp; - unsigned long flags; - - if (!_gadget) - return -ENODEV; - dev = container_of (_gadget, struct net2280, gadget); - - spin_lock_irqsave (&dev->lock, flags); - tmp = readl (&dev->usb->usbctl); - dev->softconnect = (is_on != 0); - if (is_on) - tmp |= (1 << USB_DETECT_ENABLE); - else - tmp &= ~(1 << USB_DETECT_ENABLE); - writel (tmp, &dev->usb->usbctl); - spin_unlock_irqrestore (&dev->lock, flags); - - return 0; -} - -static int net2280_start(struct usb_gadget *_gadget, - struct usb_gadget_driver *driver); -static int net2280_stop(struct usb_gadget *_gadget, - struct usb_gadget_driver *driver); - -static const struct usb_gadget_ops net2280_ops = { - .get_frame = net2280_get_frame, - .wakeup = net2280_wakeup, - .set_selfpowered = net2280_set_selfpowered, - .pullup = net2280_pullup, - .udc_start = net2280_start, - .udc_stop = net2280_stop, -}; - -/*-------------------------------------------------------------------------*/ - -#ifdef CONFIG_USB_GADGET_DEBUG_FILES - -/* FIXME move these into procfs, and use seq_file. - * Sysfs _still_ doesn't behave for arbitrarily sized files, - * and also doesn't help products using this with 2.4 kernels. - */ - -/* "function" sysfs attribute */ -static ssize_t function_show(struct device *_dev, struct device_attribute *attr, - char *buf) -{ - struct net2280 *dev = dev_get_drvdata (_dev); - - if (!dev->driver - || !dev->driver->function - || strlen (dev->driver->function) > PAGE_SIZE) - return 0; - return scnprintf (buf, PAGE_SIZE, "%s\n", dev->driver->function); -} -static DEVICE_ATTR_RO(function); - -static ssize_t registers_show(struct device *_dev, - struct device_attribute *attr, char *buf) -{ - struct net2280 *dev; - char *next; - unsigned size, t; - unsigned long flags; - int i; - u32 t1, t2; - const char *s; - - dev = dev_get_drvdata (_dev); - next = buf; - size = PAGE_SIZE; - spin_lock_irqsave (&dev->lock, flags); - - if (dev->driver) - s = dev->driver->driver.name; - else - s = "(none)"; - - /* Main Control Registers */ - t = scnprintf (next, size, "%s version " DRIVER_VERSION - ", chiprev %04x, dma %s\n\n" - "devinit %03x fifoctl %08x gadget '%s'\n" - "pci irqenb0 %02x irqenb1 %08x " - "irqstat0 %04x irqstat1 %08x\n", - driver_name, dev->chiprev, - use_dma - ? (use_dma_chaining ? "chaining" : "enabled") - : "disabled", - readl (&dev->regs->devinit), - readl (&dev->regs->fifoctl), - s, - readl (&dev->regs->pciirqenb0), - readl (&dev->regs->pciirqenb1), - readl (&dev->regs->irqstat0), - readl (&dev->regs->irqstat1)); - size -= t; - next += t; - - /* USB Control Registers */ - t1 = readl (&dev->usb->usbctl); - t2 = readl (&dev->usb->usbstat); - if (t1 & (1 << VBUS_PIN)) { - if (t2 & (1 << HIGH_SPEED)) - s = "high speed"; - else if (dev->gadget.speed == USB_SPEED_UNKNOWN) - s = "powered"; - else - s = "full speed"; - /* full speed bit (6) not working?? */ - } else - s = "not attached"; - t = scnprintf (next, size, - "stdrsp %08x usbctl %08x usbstat %08x " - "addr 0x%02x (%s)\n", - readl (&dev->usb->stdrsp), t1, t2, - readl (&dev->usb->ouraddr), s); - size -= t; - next += t; - - /* PCI Master Control Registers */ - - /* DMA Control Registers */ - - /* Configurable EP Control Registers */ - for (i = 0; i < 7; i++) { - struct net2280_ep *ep; - - ep = &dev->ep [i]; - if (i && !ep->desc) - continue; - - t1 = readl (&ep->regs->ep_cfg); - t2 = readl (&ep->regs->ep_rsp) & 0xff; - t = scnprintf (next, size, - "\n%s\tcfg %05x rsp (%02x) %s%s%s%s%s%s%s%s" - "irqenb %02x\n", - ep->ep.name, t1, t2, - (t2 & (1 << CLEAR_NAK_OUT_PACKETS)) - ? "NAK " : "", - (t2 & (1 << CLEAR_EP_HIDE_STATUS_PHASE)) - ? "hide " : "", - (t2 & (1 << CLEAR_EP_FORCE_CRC_ERROR)) - ? "CRC " : "", - (t2 & (1 << CLEAR_INTERRUPT_MODE)) - ? "interrupt " : "", - (t2 & (1<<CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE)) - ? "status " : "", - (t2 & (1 << CLEAR_NAK_OUT_PACKETS_MODE)) - ? "NAKmode " : "", - (t2 & (1 << CLEAR_ENDPOINT_TOGGLE)) - ? "DATA1 " : "DATA0 ", - (t2 & (1 << CLEAR_ENDPOINT_HALT)) - ? "HALT " : "", - readl (&ep->regs->ep_irqenb)); - size -= t; - next += t; - - t = scnprintf (next, size, - "\tstat %08x avail %04x " - "(ep%d%s-%s)%s\n", - readl (&ep->regs->ep_stat), - readl (&ep->regs->ep_avail), - t1 & 0x0f, DIR_STRING (t1), - type_string (t1 >> 8), - ep->stopped ? "*" : ""); - size -= t; - next += t; - - if (!ep->dma) - continue; - - t = scnprintf (next, size, - " dma\tctl %08x stat %08x count %08x\n" - "\taddr %08x desc %08x\n", - readl (&ep->dma->dmactl), - readl (&ep->dma->dmastat), - readl (&ep->dma->dmacount), - readl (&ep->dma->dmaaddr), - readl (&ep->dma->dmadesc)); - size -= t; - next += t; - - } - - /* Indexed Registers */ - // none yet - - /* Statistics */ - t = scnprintf (next, size, "\nirqs: "); - size -= t; - next += t; - for (i = 0; i < 7; i++) { - struct net2280_ep *ep; - - ep = &dev->ep [i]; - if (i && !ep->irqs) - continue; - t = scnprintf (next, size, " %s/%lu", ep->ep.name, ep->irqs); - size -= t; - next += t; - - } - t = scnprintf (next, size, "\n"); - size -= t; - next += t; - - spin_unlock_irqrestore (&dev->lock, flags); - - return PAGE_SIZE - size; -} -static DEVICE_ATTR_RO(registers); - -static ssize_t queues_show(struct device *_dev, struct device_attribute *attr, - char *buf) -{ - struct net2280 *dev; - char *next; - unsigned size; - unsigned long flags; - int i; - - dev = dev_get_drvdata (_dev); - next = buf; - size = PAGE_SIZE; - spin_lock_irqsave (&dev->lock, flags); - - for (i = 0; i < 7; i++) { - struct net2280_ep *ep = &dev->ep [i]; - struct net2280_request *req; - int t; - - if (i != 0) { - const struct usb_endpoint_descriptor *d; - - d = ep->desc; - if (!d) - continue; - t = d->bEndpointAddress; - t = scnprintf (next, size, - "\n%s (ep%d%s-%s) max %04x %s fifo %d\n", - ep->ep.name, t & USB_ENDPOINT_NUMBER_MASK, - (t & USB_DIR_IN) ? "in" : "out", - ({ char *val; - switch (d->bmAttributes & 0x03) { - case USB_ENDPOINT_XFER_BULK: - val = "bulk"; break; - case USB_ENDPOINT_XFER_INT: - val = "intr"; break; - default: - val = "iso"; break; - } val; }), - usb_endpoint_maxp (d) & 0x1fff, - ep->dma ? "dma" : "pio", ep->fifo_size - ); - } else /* ep0 should only have one transfer queued */ - t = scnprintf (next, size, "ep0 max 64 pio %s\n", - ep->is_in ? "in" : "out"); - if (t <= 0 || t > size) - goto done; - size -= t; - next += t; - - if (list_empty (&ep->queue)) { - t = scnprintf (next, size, "\t(nothing queued)\n"); - if (t <= 0 || t > size) - goto done; - size -= t; - next += t; - continue; - } - list_for_each_entry (req, &ep->queue, queue) { - if (ep->dma && req->td_dma == readl (&ep->dma->dmadesc)) - t = scnprintf (next, size, - "\treq %p len %d/%d " - "buf %p (dmacount %08x)\n", - &req->req, req->req.actual, - req->req.length, req->req.buf, - readl (&ep->dma->dmacount)); - else - t = scnprintf (next, size, - "\treq %p len %d/%d buf %p\n", - &req->req, req->req.actual, - req->req.length, req->req.buf); - if (t <= 0 || t > size) - goto done; - size -= t; - next += t; - - if (ep->dma) { - struct net2280_dma *td; - - td = req->td; - t = scnprintf (next, size, "\t td %08x " - " count %08x buf %08x desc %08x\n", - (u32) req->td_dma, - le32_to_cpu (td->dmacount), - le32_to_cpu (td->dmaaddr), - le32_to_cpu (td->dmadesc)); - if (t <= 0 || t > size) - goto done; - size -= t; - next += t; - } - } - } - -done: - spin_unlock_irqrestore (&dev->lock, flags); - return PAGE_SIZE - size; -} -static DEVICE_ATTR_RO(queues); - - -#else - -#define device_create_file(a,b) (0) -#define device_remove_file(a,b) do { } while (0) - -#endif - -/*-------------------------------------------------------------------------*/ - -/* another driver-specific mode might be a request type doing dma - * to/from another device fifo instead of to/from memory. - */ - -static void set_fifo_mode (struct net2280 *dev, int mode) -{ - /* keeping high bits preserves BAR2 */ - writel ((0xffff << PCI_BASE2_RANGE) | mode, &dev->regs->fifoctl); - - /* always ep-{a,b,e,f} ... maybe not ep-c or ep-d */ - INIT_LIST_HEAD (&dev->gadget.ep_list); - list_add_tail (&dev->ep [1].ep.ep_list, &dev->gadget.ep_list); - list_add_tail (&dev->ep [2].ep.ep_list, &dev->gadget.ep_list); - switch (mode) { - case 0: - list_add_tail (&dev->ep [3].ep.ep_list, &dev->gadget.ep_list); - list_add_tail (&dev->ep [4].ep.ep_list, &dev->gadget.ep_list); - dev->ep [1].fifo_size = dev->ep [2].fifo_size = 1024; - break; - case 1: - dev->ep [1].fifo_size = dev->ep [2].fifo_size = 2048; - break; - case 2: - list_add_tail (&dev->ep [3].ep.ep_list, &dev->gadget.ep_list); - dev->ep [1].fifo_size = 2048; - dev->ep [2].fifo_size = 1024; - break; - } - /* fifo sizes for ep0, ep-c, ep-d, ep-e, and ep-f never change */ - list_add_tail (&dev->ep [5].ep.ep_list, &dev->gadget.ep_list); - list_add_tail (&dev->ep [6].ep.ep_list, &dev->gadget.ep_list); -} - -/* keeping it simple: - * - one bus driver, initted first; - * - one function driver, initted second - * - * most of the work to support multiple net2280 controllers would - * be to associate this gadget driver (yes?) with all of them, or - * perhaps to bind specific drivers to specific devices. - */ - -static void usb_reset (struct net2280 *dev) -{ - u32 tmp; - - dev->gadget.speed = USB_SPEED_UNKNOWN; - (void) readl (&dev->usb->usbctl); - - net2280_led_init (dev); - - /* disable automatic responses, and irqs */ - writel (0, &dev->usb->stdrsp); - writel (0, &dev->regs->pciirqenb0); - writel (0, &dev->regs->pciirqenb1); - - /* clear old dma and irq state */ - for (tmp = 0; tmp < 4; tmp++) { - struct net2280_ep *ep = &dev->ep [tmp + 1]; - - if (ep->dma) - abort_dma (ep); - } - writel (~0, &dev->regs->irqstat0), - writel (~(1 << SUSPEND_REQUEST_INTERRUPT), &dev->regs->irqstat1), - - /* reset, and enable pci */ - tmp = readl (&dev->regs->devinit) - | (1 << PCI_ENABLE) - | (1 << FIFO_SOFT_RESET) - | (1 << USB_SOFT_RESET) - | (1 << M8051_RESET); - writel (tmp, &dev->regs->devinit); - - /* standard fifo and endpoint allocations */ - set_fifo_mode (dev, (fifo_mode <= 2) ? fifo_mode : 0); -} - -static void usb_reinit (struct net2280 *dev) -{ - u32 tmp; - int init_dma; - - /* use_dma changes are ignored till next device re-init */ - init_dma = use_dma; - - /* basic endpoint init */ - for (tmp = 0; tmp < 7; tmp++) { - struct net2280_ep *ep = &dev->ep [tmp]; - - ep->ep.name = ep_name [tmp]; - ep->dev = dev; - ep->num = tmp; - - if (tmp > 0 && tmp <= 4) { - ep->fifo_size = 1024; - if (init_dma) - ep->dma = &dev->dma [tmp - 1]; - } else - ep->fifo_size = 64; - ep->regs = &dev->epregs [tmp]; - ep_reset (dev->regs, ep); - } - usb_ep_set_maxpacket_limit(&dev->ep [0].ep, 64); - usb_ep_set_maxpacket_limit(&dev->ep [5].ep, 64); - usb_ep_set_maxpacket_limit(&dev->ep [6].ep, 64); - - dev->gadget.ep0 = &dev->ep [0].ep; - dev->ep [0].stopped = 0; - INIT_LIST_HEAD (&dev->gadget.ep0->ep_list); - - /* we want to prevent lowlevel/insecure access from the USB host, - * but erratum 0119 means this enable bit is ignored - */ - for (tmp = 0; tmp < 5; tmp++) - writel (EP_DONTUSE, &dev->dep [tmp].dep_cfg); -} - -static void ep0_start (struct net2280 *dev) -{ - writel ( (1 << CLEAR_EP_HIDE_STATUS_PHASE) - | (1 << CLEAR_NAK_OUT_PACKETS) - | (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) - , &dev->epregs [0].ep_rsp); - - /* - * hardware optionally handles a bunch of standard requests - * that the API hides from drivers anyway. have it do so. - * endpoint status/features are handled in software, to - * help pass tests for some dubious behavior. - */ - writel ( (1 << SET_TEST_MODE) - | (1 << SET_ADDRESS) - | (1 << DEVICE_SET_CLEAR_DEVICE_REMOTE_WAKEUP) - | (1 << GET_DEVICE_STATUS) - | (1 << GET_INTERFACE_STATUS) - , &dev->usb->stdrsp); - writel ( (1 << USB_ROOT_PORT_WAKEUP_ENABLE) - | (1 << SELF_POWERED_USB_DEVICE) - | (1 << REMOTE_WAKEUP_SUPPORT) - | (dev->softconnect << USB_DETECT_ENABLE) - | (1 << SELF_POWERED_STATUS) - , &dev->usb->usbctl); - - /* enable irqs so we can see ep0 and general operation */ - writel ( (1 << SETUP_PACKET_INTERRUPT_ENABLE) - | (1 << ENDPOINT_0_INTERRUPT_ENABLE) - , &dev->regs->pciirqenb0); - writel ( (1 << PCI_INTERRUPT_ENABLE) - | (1 << PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE) - | (1 << PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE) - | (1 << PCI_RETRY_ABORT_INTERRUPT_ENABLE) - | (1 << VBUS_INTERRUPT_ENABLE) - | (1 << ROOT_PORT_RESET_INTERRUPT_ENABLE) - | (1 << SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE) - , &dev->regs->pciirqenb1); - - /* don't leave any writes posted */ - (void) readl (&dev->usb->usbctl); -} - -/* when a driver is successfully registered, it will receive - * control requests including set_configuration(), which enables - * non-control requests. then usb traffic follows until a - * disconnect is reported. then a host may connect again, or - * the driver might get unbound. - */ -static int net2280_start(struct usb_gadget *_gadget, - struct usb_gadget_driver *driver) -{ - struct net2280 *dev; - int retval; - unsigned i; - - /* insist on high speed support from the driver, since - * (dev->usb->xcvrdiag & FORCE_FULL_SPEED_MODE) - * "must not be used in normal operation" - */ - if (!driver || driver->max_speed < USB_SPEED_HIGH - || !driver->setup) - return -EINVAL; - - dev = container_of (_gadget, struct net2280, gadget); - - for (i = 0; i < 7; i++) - dev->ep [i].irqs = 0; - - /* hook up the driver ... */ - dev->softconnect = 1; - driver->driver.bus = NULL; - dev->driver = driver; - - retval = device_create_file (&dev->pdev->dev, &dev_attr_function); - if (retval) goto err_unbind; - retval = device_create_file (&dev->pdev->dev, &dev_attr_queues); - if (retval) goto err_func; - - /* Enable force-full-speed testing mode, if desired */ - if (full_speed) - writel(1 << FORCE_FULL_SPEED_MODE, &dev->usb->xcvrdiag); - - /* ... then enable host detection and ep0; and we're ready - * for set_configuration as well as eventual disconnect. - */ - net2280_led_active (dev, 1); - ep0_start (dev); - - DEBUG (dev, "%s ready, usbctl %08x stdrsp %08x\n", - driver->driver.name, - readl (&dev->usb->usbctl), - readl (&dev->usb->stdrsp)); - - /* pci writes may still be posted */ - return 0; - -err_func: - device_remove_file (&dev->pdev->dev, &dev_attr_function); -err_unbind: - dev->driver = NULL; - return retval; -} - -static void -stop_activity (struct net2280 *dev, struct usb_gadget_driver *driver) -{ - int i; - - /* don't disconnect if it's not connected */ - if (dev->gadget.speed == USB_SPEED_UNKNOWN) - driver = NULL; - - /* stop hardware; prevent new request submissions; - * and kill any outstanding requests. - */ - usb_reset (dev); - for (i = 0; i < 7; i++) - nuke (&dev->ep [i]); - - /* report disconnect; the driver is already quiesced */ - if (driver) { - spin_unlock(&dev->lock); - driver->disconnect(&dev->gadget); - spin_lock(&dev->lock); - } - - usb_reinit (dev); -} - -static int net2280_stop(struct usb_gadget *_gadget, - struct usb_gadget_driver *driver) -{ - struct net2280 *dev; - unsigned long flags; - - dev = container_of (_gadget, struct net2280, gadget); - - spin_lock_irqsave (&dev->lock, flags); - stop_activity (dev, driver); - spin_unlock_irqrestore (&dev->lock, flags); - - dev->driver = NULL; - - net2280_led_active (dev, 0); - - /* Disable full-speed test mode */ - writel(0, &dev->usb->xcvrdiag); - - device_remove_file (&dev->pdev->dev, &dev_attr_function); - device_remove_file (&dev->pdev->dev, &dev_attr_queues); - - DEBUG(dev, "unregistered driver '%s'\n", - driver ? driver->driver.name : ""); - - return 0; -} - -/*-------------------------------------------------------------------------*/ - -/* handle ep0, ep-e, ep-f with 64 byte packets: packet per irq. - * also works for dma-capable endpoints, in pio mode or just - * to manually advance the queue after short OUT transfers. - */ -static void handle_ep_small (struct net2280_ep *ep) -{ - struct net2280_request *req; - u32 t; - /* 0 error, 1 mid-data, 2 done */ - int mode = 1; - - if (!list_empty (&ep->queue)) - req = list_entry (ep->queue.next, - struct net2280_request, queue); - else - req = NULL; - - /* ack all, and handle what we care about */ - t = readl (&ep->regs->ep_stat); - ep->irqs++; -#if 0 - VDEBUG (ep->dev, "%s ack ep_stat %08x, req %p\n", - ep->ep.name, t, req ? &req->req : 0); -#endif - if (!ep->is_in || ep->dev->pdev->device == 0x2280) - writel (t & ~(1 << NAK_OUT_PACKETS), &ep->regs->ep_stat); - else - /* Added for 2282 */ - writel (t, &ep->regs->ep_stat); - - /* for ep0, monitor token irqs to catch data stage length errors - * and to synchronize on status. - * - * also, to defer reporting of protocol stalls ... here's where - * data or status first appears, handling stalls here should never - * cause trouble on the host side.. - * - * control requests could be slightly faster without token synch for - * status, but status can jam up that way. - */ - if (unlikely (ep->num == 0)) { - if (ep->is_in) { - /* status; stop NAKing */ - if (t & (1 << DATA_OUT_PING_TOKEN_INTERRUPT)) { - if (ep->dev->protocol_stall) { - ep->stopped = 1; - set_halt (ep); - } - if (!req) - allow_status (ep); - mode = 2; - /* reply to extra IN data tokens with a zlp */ - } else if (t & (1 << DATA_IN_TOKEN_INTERRUPT)) { - if (ep->dev->protocol_stall) { - ep->stopped = 1; - set_halt (ep); - mode = 2; - } else if (ep->responded && - !req && !ep->stopped) - write_fifo (ep, NULL); - } - } else { - /* status; stop NAKing */ - if (t & (1 << DATA_IN_TOKEN_INTERRUPT)) { - if (ep->dev->protocol_stall) { - ep->stopped = 1; - set_halt (ep); - } - mode = 2; - /* an extra OUT token is an error */ - } else if (((t & (1 << DATA_OUT_PING_TOKEN_INTERRUPT)) - && req - && req->req.actual == req->req.length) - || (ep->responded && !req)) { - ep->dev->protocol_stall = 1; - set_halt (ep); - ep->stopped = 1; - if (req) - done (ep, req, -EOVERFLOW); - req = NULL; - } - } - } - - if (unlikely (!req)) - return; - - /* manual DMA queue advance after short OUT */ - if (likely (ep->dma)) { - if (t & (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)) { - u32 count; - int stopped = ep->stopped; - - /* TRANSFERRED works around OUT_DONE erratum 0112. - * we expect (N <= maxpacket) bytes; host wrote M. - * iff (M < N) we won't ever see a DMA interrupt. - */ - ep->stopped = 1; - for (count = 0; ; t = readl (&ep->regs->ep_stat)) { - - /* any preceding dma transfers must finish. - * dma handles (M >= N), may empty the queue - */ - scan_dma_completions (ep); - if (unlikely (list_empty (&ep->queue) - || ep->out_overflow)) { - req = NULL; - break; - } - req = list_entry (ep->queue.next, - struct net2280_request, queue); - - /* here either (M < N), a "real" short rx; - * or (M == N) and the queue didn't empty - */ - if (likely (t & (1 << FIFO_EMPTY))) { - count = readl (&ep->dma->dmacount); - count &= DMA_BYTE_COUNT_MASK; - if (readl (&ep->dma->dmadesc) - != req->td_dma) - req = NULL; - break; - } - udelay(1); - } - - /* stop DMA, leave ep NAKing */ - writel ((1 << DMA_ABORT), &ep->dma->dmastat); - spin_stop_dma (ep->dma); - - if (likely (req)) { - req->td->dmacount = 0; - t = readl (&ep->regs->ep_avail); - dma_done (ep, req, count, - (ep->out_overflow || t) - ? -EOVERFLOW : 0); - } - - /* also flush to prevent erratum 0106 trouble */ - if (unlikely (ep->out_overflow - || (ep->dev->chiprev == 0x0100 - && ep->dev->gadget.speed - == USB_SPEED_FULL))) { - out_flush (ep); - ep->out_overflow = 0; - } - - /* (re)start dma if needed, stop NAKing */ - ep->stopped = stopped; - if (!list_empty (&ep->queue)) - restart_dma (ep); - } else - DEBUG (ep->dev, "%s dma ep_stat %08x ??\n", - ep->ep.name, t); - return; - - /* data packet(s) received (in the fifo, OUT) */ - } else if (t & (1 << DATA_PACKET_RECEIVED_INTERRUPT)) { - if (read_fifo (ep, req) && ep->num != 0) - mode = 2; - - /* data packet(s) transmitted (IN) */ - } else if (t & (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)) { - unsigned len; - - len = req->req.length - req->req.actual; - if (len > ep->ep.maxpacket) - len = ep->ep.maxpacket; - req->req.actual += len; - - /* if we wrote it all, we're usually done */ - if (req->req.actual == req->req.length) { - if (ep->num == 0) { - /* send zlps until the status stage */ - } else if (!req->req.zero || len != ep->ep.maxpacket) - mode = 2; - } - - /* there was nothing to do ... */ - } else if (mode == 1) - return; - - /* done */ - if (mode == 2) { - /* stream endpoints often resubmit/unlink in completion */ - done (ep, req, 0); - - /* maybe advance queue to next request */ - if (ep->num == 0) { - /* NOTE: net2280 could let gadget driver start the - * status stage later. since not all controllers let - * them control that, the api doesn't (yet) allow it. - */ - if (!ep->stopped) - allow_status (ep); - req = NULL; - } else { - if (!list_empty (&ep->queue) && !ep->stopped) - req = list_entry (ep->queue.next, - struct net2280_request, queue); - else - req = NULL; - if (req && !ep->is_in) - stop_out_naking (ep); - } - } - - /* is there a buffer for the next packet? - * for best streaming performance, make sure there is one. - */ - if (req && !ep->stopped) { - - /* load IN fifo with next packet (may be zlp) */ - if (t & (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)) - write_fifo (ep, &req->req); - } -} - -static struct net2280_ep * -get_ep_by_addr (struct net2280 *dev, u16 wIndex) -{ - struct net2280_ep *ep; - - if ((wIndex & USB_ENDPOINT_NUMBER_MASK) == 0) - return &dev->ep [0]; - list_for_each_entry (ep, &dev->gadget.ep_list, ep.ep_list) { - u8 bEndpointAddress; - - if (!ep->desc) - continue; - bEndpointAddress = ep->desc->bEndpointAddress; - if ((wIndex ^ bEndpointAddress) & USB_DIR_IN) - continue; - if ((wIndex & 0x0f) == (bEndpointAddress & 0x0f)) - return ep; - } - return NULL; -} - -static void handle_stat0_irqs (struct net2280 *dev, u32 stat) -{ - struct net2280_ep *ep; - u32 num, scratch; - - /* most of these don't need individual acks */ - stat &= ~(1 << INTA_ASSERTED); - if (!stat) - return; - // DEBUG (dev, "irqstat0 %04x\n", stat); - - /* starting a control request? */ - if (unlikely (stat & (1 << SETUP_PACKET_INTERRUPT))) { - union { - u32 raw [2]; - struct usb_ctrlrequest r; - } u; - int tmp; - struct net2280_request *req; - - if (dev->gadget.speed == USB_SPEED_UNKNOWN) { - if (readl (&dev->usb->usbstat) & (1 << HIGH_SPEED)) - dev->gadget.speed = USB_SPEED_HIGH; - else - dev->gadget.speed = USB_SPEED_FULL; - net2280_led_speed (dev, dev->gadget.speed); - DEBUG(dev, "%s\n", usb_speed_string(dev->gadget.speed)); - } - - ep = &dev->ep [0]; - ep->irqs++; - - /* make sure any leftover request state is cleared */ - stat &= ~(1 << ENDPOINT_0_INTERRUPT); - while (!list_empty (&ep->queue)) { - req = list_entry (ep->queue.next, - struct net2280_request, queue); - done (ep, req, (req->req.actual == req->req.length) - ? 0 : -EPROTO); - } - ep->stopped = 0; - dev->protocol_stall = 0; - - if (ep->dev->pdev->device == 0x2280) - tmp = (1 << FIFO_OVERFLOW) - | (1 << FIFO_UNDERFLOW); - else - tmp = 0; - - writel (tmp | (1 << TIMEOUT) - | (1 << USB_STALL_SENT) - | (1 << USB_IN_NAK_SENT) - | (1 << USB_IN_ACK_RCVD) - | (1 << USB_OUT_PING_NAK_SENT) - | (1 << USB_OUT_ACK_SENT) - | (1 << SHORT_PACKET_OUT_DONE_INTERRUPT) - | (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT) - | (1 << DATA_PACKET_RECEIVED_INTERRUPT) - | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT) - | (1 << DATA_OUT_PING_TOKEN_INTERRUPT) - | (1 << DATA_IN_TOKEN_INTERRUPT) - , &ep->regs->ep_stat); - u.raw [0] = readl (&dev->usb->setup0123); - u.raw [1] = readl (&dev->usb->setup4567); - - cpu_to_le32s (&u.raw [0]); - cpu_to_le32s (&u.raw [1]); - - tmp = 0; - -#define w_value le16_to_cpu(u.r.wValue) -#define w_index le16_to_cpu(u.r.wIndex) -#define w_length le16_to_cpu(u.r.wLength) - - /* ack the irq */ - writel (1 << SETUP_PACKET_INTERRUPT, &dev->regs->irqstat0); - stat ^= (1 << SETUP_PACKET_INTERRUPT); - - /* watch control traffic at the token level, and force - * synchronization before letting the status stage happen. - * FIXME ignore tokens we'll NAK, until driver responds. - * that'll mean a lot less irqs for some drivers. - */ - ep->is_in = (u.r.bRequestType & USB_DIR_IN) != 0; - if (ep->is_in) { - scratch = (1 << DATA_PACKET_TRANSMITTED_INTERRUPT) - | (1 << DATA_OUT_PING_TOKEN_INTERRUPT) - | (1 << DATA_IN_TOKEN_INTERRUPT); - stop_out_naking (ep); - } else - scratch = (1 << DATA_PACKET_RECEIVED_INTERRUPT) - | (1 << DATA_OUT_PING_TOKEN_INTERRUPT) - | (1 << DATA_IN_TOKEN_INTERRUPT); - writel (scratch, &dev->epregs [0].ep_irqenb); - - /* we made the hardware handle most lowlevel requests; - * everything else goes uplevel to the gadget code. - */ - ep->responded = 1; - switch (u.r.bRequest) { - case USB_REQ_GET_STATUS: { - struct net2280_ep *e; - __le32 status; - - /* hw handles device and interface status */ - if (u.r.bRequestType != (USB_DIR_IN|USB_RECIP_ENDPOINT)) - goto delegate; - if ((e = get_ep_by_addr (dev, w_index)) == NULL - || w_length > 2) - goto do_stall; - - if (readl (&e->regs->ep_rsp) - & (1 << SET_ENDPOINT_HALT)) - status = cpu_to_le32 (1); - else - status = cpu_to_le32 (0); - - /* don't bother with a request object! */ - writel (0, &dev->epregs [0].ep_irqenb); - set_fifo_bytecount (ep, w_length); - writel ((__force u32)status, &dev->epregs [0].ep_data); - allow_status (ep); - VDEBUG (dev, "%s stat %02x\n", ep->ep.name, status); - goto next_endpoints; - } - break; - case USB_REQ_CLEAR_FEATURE: { - struct net2280_ep *e; - - /* hw handles device features */ - if (u.r.bRequestType != USB_RECIP_ENDPOINT) - goto delegate; - if (w_value != USB_ENDPOINT_HALT - || w_length != 0) - goto do_stall; - if ((e = get_ep_by_addr (dev, w_index)) == NULL) - goto do_stall; - if (e->wedged) { - VDEBUG(dev, "%s wedged, halt not cleared\n", - ep->ep.name); - } else { - VDEBUG(dev, "%s clear halt\n", ep->ep.name); - clear_halt(e); - } - allow_status (ep); - goto next_endpoints; - } - break; - case USB_REQ_SET_FEATURE: { - struct net2280_ep *e; - - /* hw handles device features */ - if (u.r.bRequestType != USB_RECIP_ENDPOINT) - goto delegate; - if (w_value != USB_ENDPOINT_HALT - || w_length != 0) - goto do_stall; - if ((e = get_ep_by_addr (dev, w_index)) == NULL) - goto do_stall; - if (e->ep.name == ep0name) - goto do_stall; - set_halt (e); - allow_status (ep); - VDEBUG (dev, "%s set halt\n", ep->ep.name); - goto next_endpoints; - } - break; - default: -delegate: - VDEBUG (dev, "setup %02x.%02x v%04x i%04x l%04x " - "ep_cfg %08x\n", - u.r.bRequestType, u.r.bRequest, - w_value, w_index, w_length, - readl (&ep->regs->ep_cfg)); - ep->responded = 0; - spin_unlock (&dev->lock); - tmp = dev->driver->setup (&dev->gadget, &u.r); - spin_lock (&dev->lock); - } - - /* stall ep0 on error */ - if (tmp < 0) { -do_stall: - VDEBUG (dev, "req %02x.%02x protocol STALL; stat %d\n", - u.r.bRequestType, u.r.bRequest, tmp); - dev->protocol_stall = 1; - } - - /* some in/out token irq should follow; maybe stall then. - * driver must queue a request (even zlp) or halt ep0 - * before the host times out. - */ - } - -#undef w_value -#undef w_index -#undef w_length - -next_endpoints: - /* endpoint data irq ? */ - scratch = stat & 0x7f; - stat &= ~0x7f; - for (num = 0; scratch; num++) { - u32 t; - - /* do this endpoint's FIFO and queue need tending? */ - t = 1 << num; - if ((scratch & t) == 0) - continue; - scratch ^= t; - - ep = &dev->ep [num]; - handle_ep_small (ep); - } - - if (stat) - DEBUG (dev, "unhandled irqstat0 %08x\n", stat); -} - -#define DMA_INTERRUPTS ( \ - (1 << DMA_D_INTERRUPT) \ - | (1 << DMA_C_INTERRUPT) \ - | (1 << DMA_B_INTERRUPT) \ - | (1 << DMA_A_INTERRUPT)) -#define PCI_ERROR_INTERRUPTS ( \ - (1 << PCI_MASTER_ABORT_RECEIVED_INTERRUPT) \ - | (1 << PCI_TARGET_ABORT_RECEIVED_INTERRUPT) \ - | (1 << PCI_RETRY_ABORT_INTERRUPT)) - -static void handle_stat1_irqs (struct net2280 *dev, u32 stat) -{ - struct net2280_ep *ep; - u32 tmp, num, mask, scratch; - - /* after disconnect there's nothing else to do! */ - tmp = (1 << VBUS_INTERRUPT) | (1 << ROOT_PORT_RESET_INTERRUPT); - mask = (1 << HIGH_SPEED) | (1 << FULL_SPEED); - - /* VBUS disconnect is indicated by VBUS_PIN and VBUS_INTERRUPT set. - * Root Port Reset is indicated by ROOT_PORT_RESET_INTERRUPT set and - * both HIGH_SPEED and FULL_SPEED clear (as ROOT_PORT_RESET_INTERRUPT - * only indicates a change in the reset state). - */ - if (stat & tmp) { - writel (tmp, &dev->regs->irqstat1); - if ((((stat & (1 << ROOT_PORT_RESET_INTERRUPT)) - && ((readl (&dev->usb->usbstat) & mask) - == 0)) - || ((readl (&dev->usb->usbctl) - & (1 << VBUS_PIN)) == 0) - ) && ( dev->gadget.speed != USB_SPEED_UNKNOWN)) { - DEBUG (dev, "disconnect %s\n", - dev->driver->driver.name); - stop_activity (dev, dev->driver); - ep0_start (dev); - return; - } - stat &= ~tmp; - - /* vBUS can bounce ... one of many reasons to ignore the - * notion of hotplug events on bus connect/disconnect! - */ - if (!stat) - return; - } - - /* NOTE: chip stays in PCI D0 state for now, but it could - * enter D1 to save more power - */ - tmp = (1 << SUSPEND_REQUEST_CHANGE_INTERRUPT); - if (stat & tmp) { - writel (tmp, &dev->regs->irqstat1); - if (stat & (1 << SUSPEND_REQUEST_INTERRUPT)) { - if (dev->driver->suspend) - dev->driver->suspend (&dev->gadget); - if (!enable_suspend) - stat &= ~(1 << SUSPEND_REQUEST_INTERRUPT); - } else { - if (dev->driver->resume) - dev->driver->resume (&dev->gadget); - /* at high speed, note erratum 0133 */ - } - stat &= ~tmp; - } - - /* clear any other status/irqs */ - if (stat) - writel (stat, &dev->regs->irqstat1); - - /* some status we can just ignore */ - if (dev->pdev->device == 0x2280) - stat &= ~((1 << CONTROL_STATUS_INTERRUPT) - | (1 << SUSPEND_REQUEST_INTERRUPT) - | (1 << RESUME_INTERRUPT) - | (1 << SOF_INTERRUPT)); - else - stat &= ~((1 << CONTROL_STATUS_INTERRUPT) - | (1 << RESUME_INTERRUPT) - | (1 << SOF_DOWN_INTERRUPT) - | (1 << SOF_INTERRUPT)); - - if (!stat) - return; - // DEBUG (dev, "irqstat1 %08x\n", stat); - - /* DMA status, for ep-{a,b,c,d} */ - scratch = stat & DMA_INTERRUPTS; - stat &= ~DMA_INTERRUPTS; - scratch >>= 9; - for (num = 0; scratch; num++) { - struct net2280_dma_regs __iomem *dma; - - tmp = 1 << num; - if ((tmp & scratch) == 0) - continue; - scratch ^= tmp; - - ep = &dev->ep [num + 1]; - dma = ep->dma; - - if (!dma) - continue; - - /* clear ep's dma status */ - tmp = readl (&dma->dmastat); - writel (tmp, &dma->dmastat); - - /* chaining should stop on abort, short OUT from fifo, - * or (stat0 codepath) short OUT transfer. - */ - if (!use_dma_chaining) { - if ((tmp & (1 << DMA_TRANSACTION_DONE_INTERRUPT)) - == 0) { - DEBUG (ep->dev, "%s no xact done? %08x\n", - ep->ep.name, tmp); - continue; - } - stop_dma (ep->dma); - } - - /* OUT transfers terminate when the data from the - * host is in our memory. Process whatever's done. - * On this path, we know transfer's last packet wasn't - * less than req->length. NAK_OUT_PACKETS may be set, - * or the FIFO may already be holding new packets. - * - * IN transfers can linger in the FIFO for a very - * long time ... we ignore that for now, accounting - * precisely (like PIO does) needs per-packet irqs - */ - scan_dma_completions (ep); - - /* disable dma on inactive queues; else maybe restart */ - if (list_empty (&ep->queue)) { - if (use_dma_chaining) - stop_dma (ep->dma); - } else { - tmp = readl (&dma->dmactl); - if (!use_dma_chaining - || (tmp & (1 << DMA_ENABLE)) == 0) - restart_dma (ep); - else if (ep->is_in && use_dma_chaining) { - struct net2280_request *req; - __le32 dmacount; - - /* the descriptor at the head of the chain - * may still have VALID_BIT clear; that's - * used to trigger changing DMA_FIFO_VALIDATE - * (affects automagic zlp writes). - */ - req = list_entry (ep->queue.next, - struct net2280_request, queue); - dmacount = req->td->dmacount; - dmacount &= cpu_to_le32 ( - (1 << VALID_BIT) - | DMA_BYTE_COUNT_MASK); - if (dmacount && (dmacount & valid_bit) == 0) - restart_dma (ep); - } - } - ep->irqs++; - } - - /* NOTE: there are other PCI errors we might usefully notice. - * if they appear very often, here's where to try recovering. - */ - if (stat & PCI_ERROR_INTERRUPTS) { - ERROR (dev, "pci dma error; stat %08x\n", stat); - stat &= ~PCI_ERROR_INTERRUPTS; - /* these are fatal errors, but "maybe" they won't - * happen again ... - */ - stop_activity (dev, dev->driver); - ep0_start (dev); - stat = 0; - } - - if (stat) - DEBUG (dev, "unhandled irqstat1 %08x\n", stat); -} - -static irqreturn_t net2280_irq (int irq, void *_dev) -{ - struct net2280 *dev = _dev; - - /* shared interrupt, not ours */ - if (!(readl(&dev->regs->irqstat0) & (1 << INTA_ASSERTED))) - return IRQ_NONE; - - spin_lock (&dev->lock); - - /* handle disconnect, dma, and more */ - handle_stat1_irqs (dev, readl (&dev->regs->irqstat1)); - - /* control requests and PIO */ - handle_stat0_irqs (dev, readl (&dev->regs->irqstat0)); - - spin_unlock (&dev->lock); - - return IRQ_HANDLED; -} - -/*-------------------------------------------------------------------------*/ - -static void gadget_release (struct device *_dev) -{ - struct net2280 *dev = dev_get_drvdata (_dev); - - kfree (dev); -} - -/* tear down the binding between this driver and the pci device */ - -static void net2280_remove (struct pci_dev *pdev) -{ - struct net2280 *dev = pci_get_drvdata (pdev); - - usb_del_gadget_udc(&dev->gadget); - - BUG_ON(dev->driver); - - /* then clean up the resources we allocated during probe() */ - net2280_led_shutdown (dev); - if (dev->requests) { - int i; - for (i = 1; i < 5; i++) { - if (!dev->ep [i].dummy) - continue; - pci_pool_free (dev->requests, dev->ep [i].dummy, - dev->ep [i].td_dma); - } - pci_pool_destroy (dev->requests); - } - if (dev->got_irq) - free_irq (pdev->irq, dev); - if (dev->regs) - iounmap (dev->regs); - if (dev->region) - release_mem_region (pci_resource_start (pdev, 0), - pci_resource_len (pdev, 0)); - if (dev->enabled) - pci_disable_device (pdev); - device_remove_file (&pdev->dev, &dev_attr_registers); - - INFO (dev, "unbind\n"); -} - -/* wrap this driver around the specified device, but - * don't respond over USB until a gadget driver binds to us. - */ - -static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) -{ - struct net2280 *dev; - unsigned long resource, len; - void __iomem *base = NULL; - int retval, i; - - /* alloc, and start init */ - dev = kzalloc (sizeof *dev, GFP_KERNEL); - if (dev == NULL){ - retval = -ENOMEM; - goto done; - } - - pci_set_drvdata (pdev, dev); - spin_lock_init (&dev->lock); - dev->pdev = pdev; - dev->gadget.ops = &net2280_ops; - dev->gadget.max_speed = USB_SPEED_HIGH; - - /* the "gadget" abstracts/virtualizes the controller */ - dev->gadget.name = driver_name; - - /* now all the pci goodies ... */ - if (pci_enable_device (pdev) < 0) { - retval = -ENODEV; - goto done; - } - dev->enabled = 1; - - /* BAR 0 holds all the registers - * BAR 1 is 8051 memory; unused here (note erratum 0103) - * BAR 2 is fifo memory; unused here - */ - resource = pci_resource_start (pdev, 0); - len = pci_resource_len (pdev, 0); - if (!request_mem_region (resource, len, driver_name)) { - DEBUG (dev, "controller already in use\n"); - retval = -EBUSY; - goto done; - } - dev->region = 1; - - /* FIXME provide firmware download interface to put - * 8051 code into the chip, e.g. to turn on PCI PM. - */ - - base = ioremap_nocache (resource, len); - if (base == NULL) { - DEBUG (dev, "can't map memory\n"); - retval = -EFAULT; - goto done; - } - dev->regs = (struct net2280_regs __iomem *) base; - dev->usb = (struct net2280_usb_regs __iomem *) (base + 0x0080); - dev->pci = (struct net2280_pci_regs __iomem *) (base + 0x0100); - dev->dma = (struct net2280_dma_regs __iomem *) (base + 0x0180); - dev->dep = (struct net2280_dep_regs __iomem *) (base + 0x0200); - dev->epregs = (struct net2280_ep_regs __iomem *) (base + 0x0300); - - /* put into initial config, link up all endpoints */ - writel (0, &dev->usb->usbctl); - usb_reset (dev); - usb_reinit (dev); - - /* irq setup after old hardware is cleaned up */ - if (!pdev->irq) { - ERROR (dev, "No IRQ. Check PCI setup!\n"); - retval = -ENODEV; - goto done; - } - - if (request_irq (pdev->irq, net2280_irq, IRQF_SHARED, driver_name, dev) - != 0) { - ERROR (dev, "request interrupt %d failed\n", pdev->irq); - retval = -EBUSY; - goto done; - } - dev->got_irq = 1; - - /* DMA setup */ - /* NOTE: we know only the 32 LSBs of dma addresses may be nonzero */ - dev->requests = pci_pool_create ("requests", pdev, - sizeof (struct net2280_dma), - 0 /* no alignment requirements */, - 0 /* or page-crossing issues */); - if (!dev->requests) { - DEBUG (dev, "can't get request pool\n"); - retval = -ENOMEM; - goto done; - } - for (i = 1; i < 5; i++) { - struct net2280_dma *td; - - td = pci_pool_alloc (dev->requests, GFP_KERNEL, - &dev->ep [i].td_dma); - if (!td) { - DEBUG (dev, "can't get dummy %d\n", i); - retval = -ENOMEM; - goto done; - } - td->dmacount = 0; /* not VALID */ - td->dmadesc = td->dmaaddr; - dev->ep [i].dummy = td; - } - - /* enable lower-overhead pci memory bursts during DMA */ - writel ( (1 << DMA_MEMORY_WRITE_AND_INVALIDATE_ENABLE) - // 256 write retries may not be enough... - // | (1 << PCI_RETRY_ABORT_ENABLE) - | (1 << DMA_READ_MULTIPLE_ENABLE) - | (1 << DMA_READ_LINE_ENABLE) - , &dev->pci->pcimstctl); - /* erratum 0115 shouldn't appear: Linux inits PCI_LATENCY_TIMER */ - pci_set_master (pdev); - pci_try_set_mwi (pdev); - - /* ... also flushes any posted pci writes */ - dev->chiprev = get_idx_reg (dev->regs, REG_CHIPREV) & 0xffff; - - /* done */ - INFO (dev, "%s\n", driver_desc); - INFO (dev, "irq %d, pci mem %p, chip rev %04x\n", - pdev->irq, base, dev->chiprev); - INFO (dev, "version: " DRIVER_VERSION "; dma %s\n", - use_dma - ? (use_dma_chaining ? "chaining" : "enabled") - : "disabled"); - retval = device_create_file (&pdev->dev, &dev_attr_registers); - if (retval) goto done; - - retval = usb_add_gadget_udc_release(&pdev->dev, &dev->gadget, - gadget_release); - if (retval) - goto done; - return 0; - -done: - if (dev) - net2280_remove (pdev); - return retval; -} - -/* make sure the board is quiescent; otherwise it will continue - * generating IRQs across the upcoming reboot. - */ - -static void net2280_shutdown (struct pci_dev *pdev) -{ - struct net2280 *dev = pci_get_drvdata (pdev); - - /* disable IRQs */ - writel (0, &dev->regs->pciirqenb0); - writel (0, &dev->regs->pciirqenb1); - - /* disable the pullup so the host will think we're gone */ - writel (0, &dev->usb->usbctl); - - /* Disable full-speed test mode */ - writel(0, &dev->usb->xcvrdiag); -} - - -/*-------------------------------------------------------------------------*/ - -static const struct pci_device_id pci_ids [] = { { - .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), - .class_mask = ~0, - .vendor = 0x17cc, - .device = 0x2280, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, -}, { - .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), - .class_mask = ~0, - .vendor = 0x17cc, - .device = 0x2282, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - -}, { /* end: all zeroes */ } -}; -MODULE_DEVICE_TABLE (pci, pci_ids); - -/* pci driver glue; this is a "new style" PCI driver module */ -static struct pci_driver net2280_pci_driver = { - .name = (char *) driver_name, - .id_table = pci_ids, - - .probe = net2280_probe, - .remove = net2280_remove, - .shutdown = net2280_shutdown, - - /* FIXME add power management support */ -}; - -MODULE_DESCRIPTION (DRIVER_DESC); -MODULE_AUTHOR ("David Brownell"); -MODULE_LICENSE ("GPL"); - -static int __init init (void) -{ - if (!use_dma) - use_dma_chaining = 0; - return pci_register_driver (&net2280_pci_driver); -} -module_init (init); - -static void __exit cleanup (void) -{ - pci_unregister_driver (&net2280_pci_driver); -} -module_exit (cleanup); diff --git a/drivers/usb/gadget/net2280.h b/drivers/usb/gadget/net2280.h deleted file mode 100644 index a844be0d683a..000000000000 --- a/drivers/usb/gadget/net2280.h +++ /dev/null @@ -1,308 +0,0 @@ -/* - * NetChip 2280 high/full speed USB device controller. - * Unlike many such controllers, this one talks PCI. - */ - -/* - * Copyright (C) 2002 NetChip Technology, Inc. (http://www.netchip.com) - * Copyright (C) 2003 David Brownell - * - * 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/usb/net2280.h> - -/*-------------------------------------------------------------------------*/ - -#ifdef __KERNEL__ - -/* indexed registers [11.10] are accessed indirectly - * caller must own the device lock. - */ - -static inline u32 -get_idx_reg (struct net2280_regs __iomem *regs, u32 index) -{ - writel (index, ®s->idxaddr); - /* NOTE: synchs device/cpu memory views */ - return readl (®s->idxdata); -} - -static inline void -set_idx_reg (struct net2280_regs __iomem *regs, u32 index, u32 value) -{ - writel (index, ®s->idxaddr); - writel (value, ®s->idxdata); - /* posted, may not be visible yet */ -} - -#endif /* __KERNEL__ */ - - -#define REG_DIAG 0x0 -#define RETRY_COUNTER 16 -#define FORCE_PCI_SERR 11 -#define FORCE_PCI_INTERRUPT 10 -#define FORCE_USB_INTERRUPT 9 -#define FORCE_CPU_INTERRUPT 8 -#define ILLEGAL_BYTE_ENABLES 5 -#define FAST_TIMES 4 -#define FORCE_RECEIVE_ERROR 2 -#define FORCE_TRANSMIT_CRC_ERROR 0 -#define REG_FRAME 0x02 /* from last sof */ -#define REG_CHIPREV 0x03 /* in bcd */ -#define REG_HS_NAK_RATE 0x0a /* NAK per N uframes */ - -#define CHIPREV_1 0x0100 -#define CHIPREV_1A 0x0110 - -#ifdef __KERNEL__ - -/* ep a-f highspeed and fullspeed maxpacket, addresses - * computed from ep->num - */ -#define REG_EP_MAXPKT(dev,num) (((num) + 1) * 0x10 + \ - (((dev)->gadget.speed == USB_SPEED_HIGH) ? 0 : 1)) - -/*-------------------------------------------------------------------------*/ - -/* [8.3] for scatter/gather i/o - * use struct net2280_dma_regs bitfields - */ -struct net2280_dma { - __le32 dmacount; - __le32 dmaaddr; /* the buffer */ - __le32 dmadesc; /* next dma descriptor */ - __le32 _reserved; -} __attribute__ ((aligned (16))); - -/*-------------------------------------------------------------------------*/ - -/* DRIVER DATA STRUCTURES and UTILITIES */ - -struct net2280_ep { - struct usb_ep ep; - struct net2280_ep_regs __iomem *regs; - struct net2280_dma_regs __iomem *dma; - struct net2280_dma *dummy; - dma_addr_t td_dma; /* of dummy */ - struct net2280 *dev; - unsigned long irqs; - - /* analogous to a host-side qh */ - struct list_head queue; - const struct usb_endpoint_descriptor *desc; - unsigned num : 8, - fifo_size : 12, - in_fifo_validate : 1, - out_overflow : 1, - stopped : 1, - wedged : 1, - is_in : 1, - is_iso : 1, - responded : 1; -}; - -static inline void allow_status (struct net2280_ep *ep) -{ - /* ep0 only */ - writel ( (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) - | (1 << CLEAR_NAK_OUT_PACKETS) - | (1 << CLEAR_NAK_OUT_PACKETS_MODE) - , &ep->regs->ep_rsp); - ep->stopped = 1; -} - -/* count (<= 4) bytes in the next fifo write will be valid */ -static inline void set_fifo_bytecount (struct net2280_ep *ep, unsigned count) -{ - writeb (count, 2 + (u8 __iomem *) &ep->regs->ep_cfg); -} - -struct net2280_request { - struct usb_request req; - struct net2280_dma *td; - dma_addr_t td_dma; - struct list_head queue; - unsigned mapped : 1, - valid : 1; -}; - -struct net2280 { - /* each pci device provides one gadget, several endpoints */ - struct usb_gadget gadget; - spinlock_t lock; - struct net2280_ep ep [7]; - struct usb_gadget_driver *driver; - unsigned enabled : 1, - protocol_stall : 1, - softconnect : 1, - got_irq : 1, - region : 1; - u16 chiprev; - - /* pci state used to access those endpoints */ - struct pci_dev *pdev; - struct net2280_regs __iomem *regs; - struct net2280_usb_regs __iomem *usb; - struct net2280_pci_regs __iomem *pci; - struct net2280_dma_regs __iomem *dma; - struct net2280_dep_regs __iomem *dep; - struct net2280_ep_regs __iomem *epregs; - - struct pci_pool *requests; - // statistics... -}; - -static inline void set_halt (struct net2280_ep *ep) -{ - /* ep0 and bulk/intr endpoints */ - writel ( (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) - /* set NAK_OUT for erratum 0114 */ - | ((ep->dev->chiprev == CHIPREV_1) << SET_NAK_OUT_PACKETS) - | (1 << SET_ENDPOINT_HALT) - , &ep->regs->ep_rsp); -} - -static inline void clear_halt (struct net2280_ep *ep) -{ - /* ep0 and bulk/intr endpoints */ - writel ( (1 << CLEAR_ENDPOINT_HALT) - | (1 << CLEAR_ENDPOINT_TOGGLE) - /* unless the gadget driver left a short packet in the - * fifo, this reverses the erratum 0114 workaround. - */ - | ((ep->dev->chiprev == CHIPREV_1) << CLEAR_NAK_OUT_PACKETS) - , &ep->regs->ep_rsp); -} - -#ifdef USE_RDK_LEDS - -static inline void net2280_led_init (struct net2280 *dev) -{ - /* LED3 (green) is on during USB activity. note erratum 0113. */ - writel ((1 << GPIO3_LED_SELECT) - | (1 << GPIO3_OUTPUT_ENABLE) - | (1 << GPIO2_OUTPUT_ENABLE) - | (1 << GPIO1_OUTPUT_ENABLE) - | (1 << GPIO0_OUTPUT_ENABLE) - , &dev->regs->gpioctl); -} - -/* indicate speed with bi-color LED 0/1 */ -static inline -void net2280_led_speed (struct net2280 *dev, enum usb_device_speed speed) -{ - u32 val = readl (&dev->regs->gpioctl); - switch (speed) { - case USB_SPEED_HIGH: /* green */ - val &= ~(1 << GPIO0_DATA); - val |= (1 << GPIO1_DATA); - break; - case USB_SPEED_FULL: /* red */ - val &= ~(1 << GPIO1_DATA); - val |= (1 << GPIO0_DATA); - break; - default: /* (off/black) */ - val &= ~((1 << GPIO1_DATA) | (1 << GPIO0_DATA)); - break; - } - writel (val, &dev->regs->gpioctl); -} - -/* indicate power with LED 2 */ -static inline void net2280_led_active (struct net2280 *dev, int is_active) -{ - u32 val = readl (&dev->regs->gpioctl); - - // FIXME this LED never seems to turn on. - if (is_active) - val |= GPIO2_DATA; - else - val &= ~GPIO2_DATA; - writel (val, &dev->regs->gpioctl); -} -static inline void net2280_led_shutdown (struct net2280 *dev) -{ - /* turn off all four GPIO*_DATA bits */ - writel (readl (&dev->regs->gpioctl) & ~0x0f, - &dev->regs->gpioctl); -} - -#else - -#define net2280_led_init(dev) do { } while (0) -#define net2280_led_speed(dev, speed) do { } while (0) -#define net2280_led_shutdown(dev) do { } while (0) - -#endif - -/*-------------------------------------------------------------------------*/ - -#define xprintk(dev,level,fmt,args...) \ - printk(level "%s %s: " fmt , driver_name , \ - pci_name(dev->pdev) , ## args) - -#ifdef DEBUG -#undef DEBUG -#define DEBUG(dev,fmt,args...) \ - xprintk(dev , KERN_DEBUG , fmt , ## args) -#else -#define DEBUG(dev,fmt,args...) \ - do { } while (0) -#endif /* DEBUG */ - -#ifdef VERBOSE -#define VDEBUG DEBUG -#else -#define VDEBUG(dev,fmt,args...) \ - do { } while (0) -#endif /* VERBOSE */ - -#define ERROR(dev,fmt,args...) \ - xprintk(dev , KERN_ERR , fmt , ## args) -#define WARNING(dev,fmt,args...) \ - xprintk(dev , KERN_WARNING , fmt , ## args) -#define INFO(dev,fmt,args...) \ - xprintk(dev , KERN_INFO , fmt , ## args) - -/*-------------------------------------------------------------------------*/ - -static inline void start_out_naking (struct net2280_ep *ep) -{ - /* NOTE: hardware races lurk here, and PING protocol issues */ - writel ((1 << SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp); - /* synch with device */ - readl (&ep->regs->ep_rsp); -} - -#ifdef DEBUG -static inline void assert_out_naking (struct net2280_ep *ep, const char *where) -{ - u32 tmp = readl (&ep->regs->ep_stat); - - if ((tmp & (1 << NAK_OUT_PACKETS)) == 0) { - DEBUG (ep->dev, "%s %s %08x !NAK\n", - ep->ep.name, where, tmp); - writel ((1 << SET_NAK_OUT_PACKETS), - &ep->regs->ep_rsp); - } -} -#define ASSERT_OUT_NAKING(ep) assert_out_naking(ep,__func__) -#else -#define ASSERT_OUT_NAKING(ep) do {} while (0) -#endif - -static inline void stop_out_naking (struct net2280_ep *ep) -{ - u32 tmp; - - tmp = readl (&ep->regs->ep_stat); - if ((tmp & (1 << NAK_OUT_PACKETS)) != 0) - writel ((1 << CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp); -} - -#endif /* __KERNEL__ */ diff --git a/drivers/usb/gadget/u_os_desc.h b/drivers/usb/gadget/u_os_desc.h index ea5cf8c2da28..947b7ddff691 100644 --- a/drivers/usb/gadget/u_os_desc.h +++ b/drivers/usb/gadget/u_os_desc.h @@ -35,27 +35,63 @@ #define USB_EXT_PROP_UNICODE_LINK 6 #define USB_EXT_PROP_UNICODE_MULTI 7 +static inline u8 *__usb_ext_prop_ptr(u8 *buf, size_t offset) +{ + return buf + offset; +} + +static inline u8 *usb_ext_prop_size_ptr(u8 *buf) +{ + return __usb_ext_prop_ptr(buf, USB_EXT_PROP_DW_SIZE); +} + +static inline u8 *usb_ext_prop_type_ptr(u8 *buf) +{ + return __usb_ext_prop_ptr(buf, USB_EXT_PROP_DW_PROPERTY_DATA_TYPE); +} + +static inline u8 *usb_ext_prop_name_len_ptr(u8 *buf) +{ + return __usb_ext_prop_ptr(buf, USB_EXT_PROP_W_PROPERTY_NAME_LENGTH); +} + +static inline u8 *usb_ext_prop_name_ptr(u8 *buf) +{ + return __usb_ext_prop_ptr(buf, USB_EXT_PROP_B_PROPERTY_NAME); +} + +static inline u8 *usb_ext_prop_data_len_ptr(u8 *buf, size_t off) +{ + return __usb_ext_prop_ptr(buf, + USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH + off); +} + +static inline u8 *usb_ext_prop_data_ptr(u8 *buf, size_t off) +{ + return __usb_ext_prop_ptr(buf, USB_EXT_PROP_B_PROPERTY_DATA + off); +} + static inline void usb_ext_prop_put_size(u8 *buf, int dw_size) { - put_unaligned_le32(dw_size, &buf[USB_EXT_PROP_DW_SIZE]); + put_unaligned_le32(dw_size, usb_ext_prop_size_ptr(buf)); } static inline void usb_ext_prop_put_type(u8 *buf, int type) { - put_unaligned_le32(type, &buf[USB_EXT_PROP_DW_PROPERTY_DATA_TYPE]); + put_unaligned_le32(type, usb_ext_prop_type_ptr(buf)); } static inline int usb_ext_prop_put_name(u8 *buf, const char *name, int pnl) { int result; - put_unaligned_le16(pnl, &buf[USB_EXT_PROP_W_PROPERTY_NAME_LENGTH]); + put_unaligned_le16(pnl, usb_ext_prop_name_len_ptr(buf)); result = utf8s_to_utf16s(name, strlen(name), UTF16_LITTLE_ENDIAN, - (wchar_t *) &buf[USB_EXT_PROP_B_PROPERTY_NAME], pnl - 2); + (wchar_t *) usb_ext_prop_name_ptr(buf), pnl - 2); if (result < 0) return result; - put_unaligned_le16(0, &buf[USB_EXT_PROP_B_PROPERTY_NAME + pnl]); + put_unaligned_le16(0, &buf[USB_EXT_PROP_B_PROPERTY_NAME + pnl - 2]); return pnl; } @@ -63,26 +99,23 @@ static inline int usb_ext_prop_put_name(u8 *buf, const char *name, int pnl) static inline void usb_ext_prop_put_binary(u8 *buf, int pnl, const u8 *data, int data_len) { - put_unaligned_le32(data_len, - &buf[USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH + pnl]); - memcpy(&buf[USB_EXT_PROP_B_PROPERTY_DATA + pnl], data, data_len); + put_unaligned_le32(data_len, usb_ext_prop_data_len_ptr(buf, pnl)); + memcpy(usb_ext_prop_data_ptr(buf, pnl), data, data_len); } static inline int usb_ext_prop_put_unicode(u8 *buf, int pnl, const char *string, int data_len) { int result; - put_unaligned_le32(data_len, - &buf[USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH + pnl]); - + put_unaligned_le32(data_len, usb_ext_prop_data_len_ptr(buf, pnl)); result = utf8s_to_utf16s(string, data_len >> 1, UTF16_LITTLE_ENDIAN, - (wchar_t *) &buf[USB_EXT_PROP_B_PROPERTY_DATA + pnl], + (wchar_t *) usb_ext_prop_data_ptr(buf, pnl), data_len - 2); if (result < 0) return result; put_unaligned_le16(0, - &buf[USB_EXT_PROP_B_PROPERTY_DATA + pnl + data_len]); + &buf[USB_EXT_PROP_B_PROPERTY_DATA + pnl + data_len - 2]); return data_len; } diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig new file mode 100644 index 000000000000..5151f947a4f5 --- /dev/null +++ b/drivers/usb/gadget/udc/Kconfig @@ -0,0 +1,385 @@ +# +# USB Gadget support on a system involves +# (a) a peripheral controller, and +# (b) the gadget driver using it. +# +# NOTE: Gadget support ** DOES NOT ** depend on host-side CONFIG_USB !! +# +# - Host systems (like PCs) need CONFIG_USB (with "A" jacks). +# - Peripherals (like PDAs) need CONFIG_USB_GADGET (with "B" jacks). +# - Some systems have both kinds of controllers. +# +# With help from a special transceiver and a "Mini-AB" jack, systems with +# both kinds of controller can also support "USB On-the-Go" (CONFIG_USB_OTG). +# + +# +# USB Peripheral Controller Support +# +# The order here is alphabetical, except that integrated controllers go +# before discrete ones so they will be the initial/default value: +# - integrated/SOC controllers first +# - licensed IP used in both SOC and discrete versions +# - discrete ones (including all PCI-only controllers) +# - debug/dummy gadget+hcd is last. +# +menu "USB Peripheral Controller" + +# +# Integrated controllers +# + +config USB_AT91 + tristate "Atmel AT91 USB Device Port" + depends on ARCH_AT91 + help + Many Atmel AT91 processors (such as the AT91RM2000) have a + full speed USB Device Port with support for five configurable + endpoints (plus endpoint zero). + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "at91_udc" and force all + gadget drivers to also be dynamically linked. + +config USB_LPC32XX + tristate "LPC32XX USB Peripheral Controller" + depends on ARCH_LPC32XX && I2C + select USB_ISP1301 + help + This option selects the USB device controller in the LPC32xx SoC. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "lpc32xx_udc" and force all + gadget drivers to also be dynamically linked. + +config USB_ATMEL_USBA + tristate "Atmel USBA" + depends on AVR32 || ARCH_AT91 + help + USBA is the integrated high-speed USB Device controller on + the AT32AP700x, some AT91SAM9 and AT91CAP9 processors from Atmel. + +config USB_BCM63XX_UDC + tristate "Broadcom BCM63xx Peripheral Controller" + depends on BCM63XX + help + Many Broadcom BCM63xx chipsets (such as the BCM6328) have a + high speed USB Device Port with support for four fixed endpoints + (plus endpoint zero). + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "bcm63xx_udc". + +config USB_FSL_USB2 + tristate "Freescale Highspeed USB DR Peripheral Controller" + depends on FSL_SOC || ARCH_MXC + select USB_FSL_MPH_DR_OF if OF + help + Some of Freescale PowerPC and i.MX processors have a High Speed + Dual-Role(DR) USB controller, which supports device mode. + + The number of programmable endpoints is different through + SOC revisions. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "fsl_usb2_udc" and force + all gadget drivers to also be dynamically linked. + +config USB_FUSB300 + tristate "Faraday FUSB300 USB Peripheral Controller" + depends on !PHYS_ADDR_T_64BIT && HAS_DMA + help + Faraday usb device controller FUSB300 driver + +config USB_FOTG210_UDC + depends on HAS_DMA + tristate "Faraday FOTG210 USB Peripheral Controller" + help + Faraday USB2.0 OTG controller which can be configured as + high speed or full speed USB device. This driver supppors + Bulk Transfer so far. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "fotg210_udc". + +config USB_GR_UDC + tristate "Aeroflex Gaisler GRUSBDC USB Peripheral Controller Driver" + depends on HAS_DMA + help + Select this to support Aeroflex Gaisler GRUSBDC cores from the GRLIB + VHDL IP core library. + +config USB_OMAP + tristate "OMAP USB Device Controller" + depends on ARCH_OMAP1 + depends on ISP1301_OMAP || !(MACH_OMAP_H2 || MACH_OMAP_H3) + help + Many Texas Instruments OMAP processors have flexible full + speed USB device controllers, with support for up to 30 + endpoints (plus endpoint zero). This driver supports the + controller in the OMAP 1611, and should work with controllers + in other OMAP processors too, given minor tweaks. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "omap_udc" and force all + gadget drivers to also be dynamically linked. + +config USB_PXA25X + tristate "PXA 25x or IXP 4xx" + depends on (ARCH_PXA && PXA25x) || ARCH_IXP4XX + help + Intel's PXA 25x series XScale ARM-5TE processors include + an integrated full speed USB 1.1 device controller. The + controller in the IXP 4xx series is register-compatible. + + It has fifteen fixed-function endpoints, as well as endpoint + zero (for control transfers). + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "pxa25x_udc" and force all + gadget drivers to also be dynamically linked. + +# if there's only one gadget driver, using only two bulk endpoints, +# don't waste memory for the other endpoints +config USB_PXA25X_SMALL + depends on USB_PXA25X + bool + default n if USB_ETH_RNDIS + default y if USB_ZERO + default y if USB_ETH + default y if USB_G_SERIAL + +config USB_R8A66597 + tristate "Renesas R8A66597 USB Peripheral Controller" + depends on HAS_DMA + help + R8A66597 is a discrete USB host and peripheral controller chip that + supports both full and high speed USB 2.0 data transfers. + It has nine configurable endpoints, and endpoint zero. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "r8a66597_udc" and force all + gadget drivers to also be dynamically linked. + +config USB_RENESAS_USBHS_UDC + tristate 'Renesas USBHS controller' + depends on USB_RENESAS_USBHS + help + Renesas USBHS is a discrete USB host and peripheral controller chip + that supports both full and high speed USB 2.0 data transfers. + It has nine or more configurable endpoints, and endpoint zero. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "renesas_usbhs" and force all + gadget drivers to also be dynamically linked. + +config USB_PXA27X + tristate "PXA 27x" + help + Intel's PXA 27x series XScale ARM v5TE processors include + an integrated full speed USB 1.1 device controller. + + It has up to 23 endpoints, as well as endpoint zero (for + control transfers). + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "pxa27x_udc" and force all + gadget drivers to also be dynamically linked. + +config USB_S3C2410 + tristate "S3C2410 USB Device Controller" + depends on ARCH_S3C24XX + help + Samsung's S3C2410 is an ARM-4 processor with an integrated + full speed USB 1.1 device controller. It has 4 configurable + endpoints, as well as endpoint zero (for control transfers). + + This driver has been tested on the S3C2410, S3C2412, and + S3C2440 processors. + +config USB_S3C2410_DEBUG + boolean "S3C2410 udc debug messages" + depends on USB_S3C2410 + +config USB_S3C_HSUDC + tristate "S3C2416, S3C2443 and S3C2450 USB Device Controller" + depends on ARCH_S3C24XX + help + Samsung's S3C2416, S3C2443 and S3C2450 is an ARM9 based SoC + integrated with dual speed USB 2.0 device controller. It has + 8 endpoints, as well as endpoint zero. + + This driver has been tested on S3C2416 and S3C2450 processors. + +config USB_MV_UDC + tristate "Marvell USB2.0 Device Controller" + depends on HAS_DMA + help + Marvell Socs (including PXA and MMP series) include a high speed + USB2.0 OTG controller, which can be configured as high speed or + full speed USB peripheral. + +config USB_MV_U3D + depends on HAS_DMA + tristate "MARVELL PXA2128 USB 3.0 controller" + help + MARVELL PXA2128 Processor series include a super speed USB3.0 device + controller, which support super speed USB peripheral. + +# +# Controllers available in both integrated and discrete versions +# + +config USB_M66592 + tristate "Renesas M66592 USB Peripheral Controller" + help + M66592 is a discrete USB peripheral controller chip that + supports both full and high speed USB 2.0 data transfers. + It has seven configurable endpoints, and endpoint zero. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "m66592_udc" and force all + gadget drivers to also be dynamically linked. + +# +# Controllers available only in discrete form (and all PCI controllers) +# + +config USB_AMD5536UDC + tristate "AMD5536 UDC" + depends on PCI + help + The AMD5536 UDC is part of the AMD Geode CS5536, an x86 southbridge. + It is a USB Highspeed DMA capable USB device controller. Beside ep0 + it provides 4 IN and 4 OUT endpoints (bulk or interrupt type). + The UDC port supports OTG operation, and may be used as a host port + if it's not being used to implement peripheral or OTG roles. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "amd5536udc" and force all + gadget drivers to also be dynamically linked. + +config USB_FSL_QE + tristate "Freescale QE/CPM USB Device Controller" + depends on FSL_SOC && (QUICC_ENGINE || CPM) + help + Some of Freescale PowerPC processors have a Full Speed + QE/CPM2 USB controller, which support device mode with 4 + programmable endpoints. This driver supports the + controller in the MPC8360 and MPC8272, and should work with + controllers having QE or CPM2, given minor tweaks. + + Set CONFIG_USB_GADGET to "m" to build this driver as a + dynamically linked module called "fsl_qe_udc". + +config USB_NET2272 + tristate "PLX NET2272" + help + PLX NET2272 is a USB peripheral controller which supports + both full and high speed USB 2.0 data transfers. + + It has three configurable endpoints, as well as endpoint zero + (for control transfer). + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "net2272" and force all + gadget drivers to also be dynamically linked. + +config USB_NET2272_DMA + boolean "Support external DMA controller" + depends on USB_NET2272 && HAS_DMA + help + The NET2272 part can optionally support an external DMA + controller, but your board has to have support in the + driver itself. + + If unsure, say "N" here. The driver works fine in PIO mode. + +config USB_NET2280 + tristate "NetChip 228x / PLX USB338x" + depends on PCI + help + NetChip 2280 / 2282 is a PCI based USB peripheral controller which + supports both full and high speed USB 2.0 data transfers. + + It has six configurable endpoints, as well as endpoint zero + (for control transfers) and several endpoints with dedicated + functions. + + PLX 3380 / 3382 is a PCIe based USB peripheral controller which + supports full, high speed USB 2.0 and super speed USB 3.0 + data transfers. + + It has eight configurable endpoints, as well as endpoint zero + (for control transfers) and several endpoints with dedicated + functions. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "net2280" and force all + gadget drivers to also be dynamically linked. + +config USB_GOKU + tristate "Toshiba TC86C001 'Goku-S'" + depends on PCI + help + The Toshiba TC86C001 is a PCI device which includes controllers + for full speed USB devices, IDE, I2C, SIO, plus a USB host (OHCI). + + The device controller has three configurable (bulk or interrupt) + endpoints, plus endpoint zero (for control transfers). + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "goku_udc" and to force all + gadget drivers to also be dynamically linked. + +config USB_EG20T + tristate "Intel EG20T PCH/LAPIS Semiconductor IOH(ML7213/ML7831) UDC" + depends on PCI + help + This is a USB device driver for EG20T PCH. + EG20T PCH is the platform controller hub that is used in Intel's + general embedded platform. EG20T PCH has USB device interface. + Using this interface, it is able to access system devices connected + to USB device. + This driver enables USB device function. + USB device is a USB peripheral controller which + supports both full and high speed USB 2.0 data transfers. + This driver supports both control transfer and bulk transfer modes. + This driver dose not support interrupt transfer or isochronous + transfer modes. + + This driver also can be used for LAPIS Semiconductor's ML7213 which is + for IVI(In-Vehicle Infotainment) use. + ML7831 is for general purpose use. + ML7213/ML7831 is companion chip for Intel Atom E6xx series. + ML7213/ML7831 is completely compatible for Intel EG20T PCH. + +# +# LAST -- dummy/emulated controller +# + +config USB_DUMMY_HCD + tristate "Dummy HCD (DEVELOPMENT)" + depends on USB=y || (USB=m && USB_GADGET=m) + help + This host controller driver emulates USB, looping all data transfer + requests back to a USB "gadget driver" in the same host. The host + side is the master; the gadget side is the slave. Gadget drivers + can be high, full, or low speed; and they have access to endpoints + like those from NET2280, PXA2xx, or SA1100 hardware. + + This may help in some stages of creating a driver to embed in a + Linux device, since it lets you debug several parts of the gadget + driver without its hardware or drivers being involved. + + Since such a gadget side driver needs to interoperate with a host + side Linux-USB device driver, this may help to debug both sides + of a USB protocol stack. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "dummy_hcd" and force all + gadget drivers to also be dynamically linked. + +# NOTE: Please keep dummy_hcd LAST so that "real hardware" appears +# first and will be selected by default. + +endmenu diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile new file mode 100644 index 000000000000..4096122bb283 --- /dev/null +++ b/drivers/usb/gadget/udc/Makefile @@ -0,0 +1,31 @@ +# +# USB peripheral controller drivers +# +obj-$(CONFIG_USB_GADGET) += udc-core.o +obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o +obj-$(CONFIG_USB_NET2272) += net2272.o +obj-$(CONFIG_USB_NET2280) += net2280.o +obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc.o +obj-$(CONFIG_USB_PXA25X) += pxa25x_udc.o +obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o +obj-$(CONFIG_USB_GOKU) += goku_udc.o +obj-$(CONFIG_USB_OMAP) += omap_udc.o +obj-$(CONFIG_USB_S3C2410) += s3c2410_udc.o +obj-$(CONFIG_USB_AT91) += at91_udc.o +obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o +obj-$(CONFIG_USB_BCM63XX_UDC) += bcm63xx_udc.o +obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o +fsl_usb2_udc-y := fsl_udc_core.o +fsl_usb2_udc-$(CONFIG_ARCH_MXC) += fsl_mxc_udc.o +obj-$(CONFIG_USB_M66592) += m66592-udc.o +obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o +obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o +obj-$(CONFIG_USB_S3C_HSUDC) += s3c-hsudc.o +obj-$(CONFIG_USB_LPC32XX) += lpc32xx_udc.o +obj-$(CONFIG_USB_EG20T) += pch_udc.o +obj-$(CONFIG_USB_MV_UDC) += mv_udc.o +mv_udc-y := mv_udc_core.o +obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o +obj-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o +obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o +obj-$(CONFIG_USB_GR_UDC) += gr_udc.o diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/udc/amd5536udc.c index 41b062eb4de0..41b062eb4de0 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/udc/amd5536udc.c diff --git a/drivers/usb/gadget/amd5536udc.h b/drivers/usb/gadget/udc/amd5536udc.h index 6744d3b83109..6744d3b83109 100644 --- a/drivers/usb/gadget/amd5536udc.h +++ b/drivers/usb/gadget/udc/amd5536udc.h diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c index cfd18bcca723..cfd18bcca723 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/udc/at91_udc.c diff --git a/drivers/usb/gadget/at91_udc.h b/drivers/usb/gadget/udc/at91_udc.h index 017524663381..017524663381 100644 --- a/drivers/usb/gadget/at91_udc.h +++ b/drivers/usb/gadget/udc/at91_udc.h diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 76023ce449a3..906e65f0e4fa 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -1979,7 +1979,7 @@ static struct usba_ep * usba_udc_pdata(struct platform_device *pdev, return eps; } -static int __init usba_udc_probe(struct platform_device *pdev) +static int usba_udc_probe(struct platform_device *pdev) { struct resource *regs, *fifo; struct clk *pclk, *hclk; diff --git a/drivers/usb/gadget/atmel_usba_udc.h b/drivers/usb/gadget/udc/atmel_usba_udc.h index a70706e8cb02..a70706e8cb02 100644 --- a/drivers/usb/gadget/atmel_usba_udc.h +++ b/drivers/usb/gadget/udc/atmel_usba_udc.h diff --git a/drivers/usb/gadget/bcm63xx_udc.c b/drivers/usb/gadget/udc/bcm63xx_udc.c index e969eb809a85..e969eb809a85 100644 --- a/drivers/usb/gadget/bcm63xx_udc.c +++ b/drivers/usb/gadget/udc/bcm63xx_udc.c diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index 2b54955d3166..2b54955d3166 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c diff --git a/drivers/usb/gadget/fotg210-udc.c b/drivers/usb/gadget/udc/fotg210-udc.c index e143d69f6017..e143d69f6017 100644 --- a/drivers/usb/gadget/fotg210-udc.c +++ b/drivers/usb/gadget/udc/fotg210-udc.c diff --git a/drivers/usb/gadget/fotg210.h b/drivers/usb/gadget/udc/fotg210.h index bbf991bcbe7c..bbf991bcbe7c 100644 --- a/drivers/usb/gadget/fotg210.h +++ b/drivers/usb/gadget/udc/fotg210.h diff --git a/drivers/usb/gadget/fsl_mxc_udc.c b/drivers/usb/gadget/udc/fsl_mxc_udc.c index 9b140fc4d3bc..f16e149c5b3e 100644 --- a/drivers/usb/gadget/fsl_mxc_udc.c +++ b/drivers/usb/gadget/udc/fsl_mxc_udc.c @@ -18,6 +18,8 @@ #include <linux/platform_device.h> #include <linux/io.h> +#include "fsl_usb2_udc.h" + static struct clk *mxc_ahb_clk; static struct clk *mxc_per_clk; static struct clk *mxc_ipg_clk; diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/udc/fsl_qe_udc.c index ad5483335167..732430804841 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/udc/fsl_qe_udc.c @@ -2539,7 +2539,7 @@ static int qe_udc_probe(struct platform_device *ofdev) goto err2; /* create a buf for ZLP send, need to remain zeroed */ - udc->nullbuf = kzalloc(256, GFP_KERNEL); + udc->nullbuf = devm_kzalloc(&ofdev->dev, 256, GFP_KERNEL); if (udc->nullbuf == NULL) { dev_err(udc->dev, "cannot alloc nullbuf\n"); ret = -ENOMEM; @@ -2547,10 +2547,10 @@ static int qe_udc_probe(struct platform_device *ofdev) } /* buffer for data of get_status request */ - udc->statusbuf = kzalloc(2, GFP_KERNEL); + udc->statusbuf = devm_kzalloc(&ofdev->dev, 2, GFP_KERNEL); if (udc->statusbuf == NULL) { ret = -ENOMEM; - goto err4; + goto err3; } udc->nullp = virt_to_phys((void *)udc->nullbuf); @@ -2581,13 +2581,13 @@ static int qe_udc_probe(struct platform_device *ofdev) if (ret) { dev_err(udc->dev, "cannot request irq %d err %d\n", udc->usb_irq, ret); - goto err5; + goto err4; } ret = usb_add_gadget_udc_release(&ofdev->dev, &udc->gadget, qe_udc_release); if (ret) - goto err6; + goto err5; platform_set_drvdata(ofdev, udc); dev_info(udc->dev, @@ -2595,9 +2595,9 @@ static int qe_udc_probe(struct platform_device *ofdev) (udc->soc_type == PORT_QE) ? "QE" : "CPM"); return 0; -err6: - free_irq(udc->usb_irq, udc); err5: + free_irq(udc->usb_irq, udc); +err4: irq_dispose_mapping(udc->usb_irq); err_noirq: if (udc->nullmap) { @@ -2610,9 +2610,6 @@ err_noirq: udc->nullp, 256, DMA_TO_DEVICE); } - kfree(udc->statusbuf); -err4: - kfree(udc->nullbuf); err3: ep = &udc->eps[0]; cpm_muram_free(cpm_muram_offset(ep->rxbase)); @@ -2660,8 +2657,6 @@ static int qe_udc_remove(struct platform_device *ofdev) udc->nullp, 256, DMA_TO_DEVICE); } - kfree(udc->statusbuf); - kfree(udc->nullbuf); ep = &udc->eps[0]; cpm_muram_free(cpm_muram_offset(ep->rxbase)); diff --git a/drivers/usb/gadget/fsl_qe_udc.h b/drivers/usb/gadget/udc/fsl_qe_udc.h index 7026919fc901..7026919fc901 100644 --- a/drivers/usb/gadget/fsl_qe_udc.h +++ b/drivers/usb/gadget/udc/fsl_qe_udc.h diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c index 28e4fc957026..75b23ea077a7 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/udc/fsl_udc_core.c @@ -59,9 +59,9 @@ static const char driver_name[] = "fsl-usb2-udc"; static const char driver_desc[] = DRIVER_DESC; -static struct usb_dr_device *dr_regs; +static struct usb_dr_device __iomem *dr_regs; -static struct usb_sys_interface *usb_sys_regs; +static struct usb_sys_interface __iomem *usb_sys_regs; /* it is initialized in probe() */ static struct fsl_udc *udc_controller = NULL; @@ -159,6 +159,8 @@ static inline void fsl_set_accessors(struct fsl_usb2_platform_data *pdata) {} * request is still in progress. *--------------------------------------------------------------*/ static void done(struct fsl_ep *ep, struct fsl_req *req, int status) +__releases(ep->udc->lock) +__acquires(ep->udc->lock) { struct fsl_udc *udc = NULL; unsigned char stopped = ep->stopped; @@ -1392,6 +1394,8 @@ stall: static void setup_received_irq(struct fsl_udc *udc, struct usb_ctrlrequest *setup) +__releases(udc->lock) +__acquires(udc->lock) { u16 wValue = le16_to_cpu(setup->wValue); u16 wIndex = le16_to_cpu(setup->wIndex); @@ -1957,8 +1961,7 @@ static int fsl_udc_start(struct usb_gadget *g, &udc_controller->gadget); if (retval < 0) { ERR("can't bind to transceiver\n"); - driver->unbind(&udc_controller->gadget); - udc_controller->driver = 0; + udc_controller->driver = NULL; return retval; } } @@ -2246,7 +2249,7 @@ static void fsl_udc_release(struct device *dev) * init resource for globle controller * Return the udc handle on success or NULL on failure ------------------------------------------------------------------*/ -static int __init struct_udc_setup(struct fsl_udc *udc, +static int struct_udc_setup(struct fsl_udc *udc, struct platform_device *pdev) { struct fsl_usb2_platform_data *pdata; @@ -2298,7 +2301,7 @@ static int __init struct_udc_setup(struct fsl_udc *udc, * ep0out is not used so do nothing here * ep0in should be taken care *--------------------------------------------------------------*/ -static int __init struct_ep_setup(struct fsl_udc *udc, unsigned char index, +static int struct_ep_setup(struct fsl_udc *udc, unsigned char index, char *name, int link) { struct fsl_ep *ep = &udc->eps[index]; @@ -2331,7 +2334,7 @@ static int __init struct_ep_setup(struct fsl_udc *udc, unsigned char index, * all intialization operations implemented here except enabling usb_intr reg * board setup should have been done in the platform code */ -static int __init fsl_udc_probe(struct platform_device *pdev) +static int fsl_udc_probe(struct platform_device *pdev) { struct fsl_usb2_platform_data *pdata; struct resource *res; @@ -2380,7 +2383,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev) goto err_release_mem_region; } - pdata->regs = (void *)dr_regs; + pdata->regs = (void __iomem *)dr_regs; /* * do platform specific init: check the clock, grab/config pins, etc. diff --git a/drivers/usb/gadget/fsl_usb2_udc.h b/drivers/usb/gadget/udc/fsl_usb2_udc.h index c6703bb07b23..84715625b2b3 100644 --- a/drivers/usb/gadget/fsl_usb2_udc.h +++ b/drivers/usb/gadget/udc/fsl_usb2_udc.h @@ -12,6 +12,9 @@ #ifndef __FSL_USB2_UDC_H #define __FSL_USB2_UDC_H +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + /* ### define USB registers here */ #define USB_MAX_CTRL_PAYLOAD 64 diff --git a/drivers/usb/gadget/fusb300_udc.c b/drivers/usb/gadget/udc/fusb300_udc.c index 3deb4e938071..d40255f784df 100644 --- a/drivers/usb/gadget/fusb300_udc.c +++ b/drivers/usb/gadget/udc/fusb300_udc.c @@ -1325,8 +1325,6 @@ static int fusb300_udc_stop(struct usb_gadget *g, { struct fusb300 *fusb300 = to_fusb300(g); - driver->unbind(&fusb300->gadget); - init_controller(fusb300); fusb300->driver = NULL; @@ -1359,7 +1357,7 @@ static int __exit fusb300_remove(struct platform_device *pdev) return 0; } -static int __init fusb300_probe(struct platform_device *pdev) +static int fusb300_probe(struct platform_device *pdev) { struct resource *res, *ires, *ires1; void __iomem *reg = NULL; diff --git a/drivers/usb/gadget/fusb300_udc.h b/drivers/usb/gadget/udc/fusb300_udc.h index ae811d8d38b4..ae811d8d38b4 100644 --- a/drivers/usb/gadget/fusb300_udc.h +++ b/drivers/usb/gadget/udc/fusb300_udc.h diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/udc/gadget_chips.h index bcd04bc66b98..bcd04bc66b98 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/udc/gadget_chips.h diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/udc/goku_udc.c index 6c85839e15ad..6c85839e15ad 100644 --- a/drivers/usb/gadget/goku_udc.c +++ b/drivers/usb/gadget/udc/goku_udc.c diff --git a/drivers/usb/gadget/goku_udc.h b/drivers/usb/gadget/udc/goku_udc.h index 86d2adafe149..86d2adafe149 100644 --- a/drivers/usb/gadget/goku_udc.h +++ b/drivers/usb/gadget/udc/goku_udc.h diff --git a/drivers/usb/gadget/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c index c7004ee89c90..08df5c4f46ce 100644 --- a/drivers/usb/gadget/gr_udc.c +++ b/drivers/usb/gadget/udc/gr_udc.c @@ -2213,7 +2213,7 @@ out: return retval; } -static struct of_device_id gr_match[] = { +static const struct of_device_id gr_match[] = { {.name = "GAISLER_USBDC"}, {.name = "01_021"}, {}, diff --git a/drivers/usb/gadget/gr_udc.h b/drivers/usb/gadget/udc/gr_udc.h index 8388897d9ec3..8388897d9ec3 100644 --- a/drivers/usb/gadget/gr_udc.h +++ b/drivers/usb/gadget/udc/gr_udc.h diff --git a/drivers/usb/gadget/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c index e471580a2a3b..1629ad7dcb80 100644 --- a/drivers/usb/gadget/lpc32xx_udc.c +++ b/drivers/usb/gadget/udc/lpc32xx_udc.c @@ -3036,7 +3036,7 @@ struct lpc32xx_usbd_cfg lpc32xx_usbddata = { static u64 lpc32xx_usbd_dmamask = ~(u32) 0x7F; -static int __init lpc32xx_udc_probe(struct platform_device *pdev) +static int lpc32xx_udc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct lpc32xx_udc *udc; @@ -3045,11 +3045,10 @@ static int __init lpc32xx_udc_probe(struct platform_device *pdev) dma_addr_t dma_handle; struct device_node *isp1301_node; - udc = kzalloc(sizeof(*udc), GFP_KERNEL); + udc = kmemdup(&controller_template, sizeof(*udc), GFP_KERNEL); if (!udc) return -ENOMEM; - memcpy(udc, &controller_template, sizeof(*udc)); for (i = 0; i <= 15; i++) udc->ep[i].udc = udc; udc->gadget.ep0 = &udc->ep[0].ep; @@ -3397,7 +3396,7 @@ static int lpc32xx_udc_resume(struct platform_device *pdev) #endif #ifdef CONFIG_OF -static struct of_device_id lpc32xx_udc_of_match[] = { +static const struct of_device_id lpc32xx_udc_of_match[] = { { .compatible = "nxp,lpc3220-udc", }, { }, }; diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/udc/m66592-udc.c index 0d17174b86f8..de88d33b44b2 100644 --- a/drivers/usb/gadget/m66592-udc.c +++ b/drivers/usb/gadget/udc/m66592-udc.c @@ -1492,8 +1492,6 @@ static int m66592_udc_stop(struct usb_gadget *g, m66592_bclr(m66592, M66592_VBSE | M66592_URST, M66592_INTENB0); - driver->unbind(&m66592->gadget); - init_controller(m66592); disable_controller(m66592); @@ -1553,7 +1551,7 @@ static void nop_completion(struct usb_ep *ep, struct usb_request *r) { } -static int __init m66592_probe(struct platform_device *pdev) +static int m66592_probe(struct platform_device *pdev) { struct resource *res, *ires; void __iomem *reg = NULL; diff --git a/drivers/usb/gadget/m66592-udc.h b/drivers/usb/gadget/udc/m66592-udc.h index 96d49d7bfb6b..96d49d7bfb6b 100644 --- a/drivers/usb/gadget/m66592-udc.h +++ b/drivers/usb/gadget/udc/m66592-udc.h diff --git a/drivers/usb/gadget/mv_u3d.h b/drivers/usb/gadget/udc/mv_u3d.h index e32a787ac373..e32a787ac373 100644 --- a/drivers/usb/gadget/mv_u3d.h +++ b/drivers/usb/gadget/udc/mv_u3d.h diff --git a/drivers/usb/gadget/mv_u3d_core.c b/drivers/usb/gadget/udc/mv_u3d_core.c index 16248711c152..16248711c152 100644 --- a/drivers/usb/gadget/mv_u3d_core.c +++ b/drivers/usb/gadget/udc/mv_u3d_core.c diff --git a/drivers/usb/gadget/mv_udc.h b/drivers/usb/gadget/udc/mv_udc.h index be77f207dbaf..be77f207dbaf 100644 --- a/drivers/usb/gadget/mv_udc.h +++ b/drivers/usb/gadget/udc/mv_udc.h diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/udc/mv_udc_core.c index fcff3a571b45..040fb169b162 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/udc/mv_udc_core.c @@ -332,7 +332,7 @@ static int queue_dtd(struct mv_ep *ep, struct mv_req *req) /* clear active and halt bit, in case set from a previous error */ dqh->size_ioc_int_sts &= ~(DTD_STATUS_ACTIVE | DTD_STATUS_HALTED); - /* Ensure that updates to the QH will occure before priming. */ + /* Ensure that updates to the QH will occur before priming. */ wmb(); /* Prime the Endpoint */ @@ -1656,7 +1656,7 @@ static void handle_setup_packet(struct mv_udc *udc, u8 ep_num, dev_dbg(&udc->dev->dev, "SETUP %02x.%02x v%04x i%04x l%04x\n", setup->bRequestType, setup->bRequest, setup->wValue, setup->wIndex, setup->wLength); - /* We process some stardard setup requests here */ + /* We process some standard setup requests here */ if ((setup->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { switch (setup->bRequest) { case USB_REQ_GET_STATUS: diff --git a/drivers/usb/gadget/net2272.c b/drivers/usb/gadget/udc/net2272.c index ca15405583e2..059cfe527982 100644 --- a/drivers/usb/gadget/net2272.c +++ b/drivers/usb/gadget/udc/net2272.c @@ -1453,7 +1453,7 @@ static int net2272_start(struct usb_gadget *_gadget, struct net2272 *dev; unsigned i; - if (!driver || !driver->unbind || !driver->setup || + if (!driver || !driver->setup || driver->max_speed != USB_SPEED_HIGH) return -EINVAL; diff --git a/drivers/usb/gadget/net2272.h b/drivers/usb/gadget/udc/net2272.h index e59505789359..e59505789359 100644 --- a/drivers/usb/gadget/net2272.h +++ b/drivers/usb/gadget/udc/net2272.h diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c new file mode 100644 index 000000000000..f4eac113690e --- /dev/null +++ b/drivers/usb/gadget/udc/net2280.c @@ -0,0 +1,3827 @@ +/* + * Driver for the PLX NET2280 USB device controller. + * Specs and errata are available from <http://www.plxtech.com>. + * + * PLX Technology Inc. (formerly NetChip Technology) supported the + * development of this driver. + * + * + * CODE STATUS HIGHLIGHTS + * + * This driver should work well with most "gadget" drivers, including + * the Mass Storage, Serial, and Ethernet/RNDIS gadget drivers + * as well as Gadget Zero and Gadgetfs. + * + * DMA is enabled by default. Drivers using transfer queues might use + * DMA chaining to remove IRQ latencies between transfers. (Except when + * short OUT transfers happen.) Drivers can use the req->no_interrupt + * hint to completely eliminate some IRQs, if a later IRQ is guaranteed + * and DMA chaining is enabled. + * + * MSI is enabled by default. The legacy IRQ is used if MSI couldn't + * be enabled. + * + * Note that almost all the errata workarounds here are only needed for + * rev1 chips. Rev1a silicon (0110) fixes almost all of them. + */ + +/* + * Copyright (C) 2003 David Brownell + * Copyright (C) 2003-2005 PLX Technology, Inc. + * Copyright (C) 2014 Ricardo Ribalda - Qtechnology/AS + * + * Modified Seth Levy 2005 PLX Technology, Inc. to provide compatibility + * with 2282 chip + * + * Modified Ricardo Ribalda Qtechnology AS to provide compatibility + * with usb 338x chip. Based on PLX driver + * + * 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/module.h> +#include <linux/pci.h> +#include <linux/dma-mapping.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/ioport.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/timer.h> +#include <linux/list.h> +#include <linux/interrupt.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/prefetch.h> +#include <linux/io.h> + +#include <asm/byteorder.h> +#include <asm/irq.h> +#include <asm/unaligned.h> + +#define DRIVER_DESC "PLX NET228x/USB338x USB Peripheral Controller" +#define DRIVER_VERSION "2005 Sept 27/v3.0" + +#define EP_DONTUSE 13 /* nonzero */ + +#define USE_RDK_LEDS /* GPIO pins control three LEDs */ + + +static const char driver_name[] = "net2280"; +static const char driver_desc[] = DRIVER_DESC; + +static const u32 ep_bit[9] = { 0, 17, 2, 19, 4, 1, 18, 3, 20 }; +static const char ep0name[] = "ep0"; +static const char *const ep_name[] = { + ep0name, + "ep-a", "ep-b", "ep-c", "ep-d", + "ep-e", "ep-f", "ep-g", "ep-h", +}; + +/* use_dma -- general goodness, fewer interrupts, less cpu load (vs PIO) + * use_dma_chaining -- dma descriptor queueing gives even more irq reduction + * + * The net2280 DMA engines are not tightly integrated with their FIFOs; + * not all cases are (yet) handled well in this driver or the silicon. + * Some gadget drivers work better with the dma support here than others. + * These two parameters let you use PIO or more aggressive DMA. + */ +static bool use_dma = true; +static bool use_dma_chaining; +static bool use_msi = true; + +/* "modprobe net2280 use_dma=n" etc */ +module_param(use_dma, bool, 0444); +module_param(use_dma_chaining, bool, 0444); +module_param(use_msi, bool, 0444); + +/* mode 0 == ep-{a,b,c,d} 1K fifo each + * mode 1 == ep-{a,b} 2K fifo each, ep-{c,d} unavailable + * mode 2 == ep-a 2K fifo, ep-{b,c} 1K each, ep-d unavailable + */ +static ushort fifo_mode; + +/* "modprobe net2280 fifo_mode=1" etc */ +module_param(fifo_mode, ushort, 0644); + +/* enable_suspend -- When enabled, the driver will respond to + * USB suspend requests by powering down the NET2280. Otherwise, + * USB suspend requests will be ignored. This is acceptable for + * self-powered devices + */ +static bool enable_suspend; + +/* "modprobe net2280 enable_suspend=1" etc */ +module_param(enable_suspend, bool, 0444); + +/* force full-speed operation */ +static bool full_speed; +module_param(full_speed, bool, 0444); +MODULE_PARM_DESC(full_speed, "force full-speed mode -- for testing only!"); + +#define DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out") + +static char *type_string(u8 bmAttributes) +{ + switch ((bmAttributes) & USB_ENDPOINT_XFERTYPE_MASK) { + case USB_ENDPOINT_XFER_BULK: return "bulk"; + case USB_ENDPOINT_XFER_ISOC: return "iso"; + case USB_ENDPOINT_XFER_INT: return "intr"; + } + return "control"; +} + +#include "net2280.h" + +#define valid_bit cpu_to_le32(BIT(VALID_BIT)) +#define dma_done_ie cpu_to_le32(BIT(DMA_DONE_INTERRUPT_ENABLE)) + +/*-------------------------------------------------------------------------*/ +static inline void enable_pciirqenb(struct net2280_ep *ep) +{ + u32 tmp = readl(&ep->dev->regs->pciirqenb0); + + if (ep->dev->quirks & PLX_LEGACY) + tmp |= BIT(ep->num); + else + tmp |= BIT(ep_bit[ep->num]); + writel(tmp, &ep->dev->regs->pciirqenb0); + + return; +} + +static int +net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) +{ + struct net2280 *dev; + struct net2280_ep *ep; + u32 max, tmp; + unsigned long flags; + static const u32 ep_key[9] = { 1, 0, 1, 0, 1, 1, 0, 1, 0 }; + + ep = container_of(_ep, struct net2280_ep, ep); + if (!_ep || !desc || ep->desc || _ep->name == ep0name || + desc->bDescriptorType != USB_DT_ENDPOINT) + return -EINVAL; + dev = ep->dev; + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + /* erratum 0119 workaround ties up an endpoint number */ + if ((desc->bEndpointAddress & 0x0f) == EP_DONTUSE) + return -EDOM; + + if (dev->quirks & PLX_SUPERSPEED) { + if ((desc->bEndpointAddress & 0x0f) >= 0x0c) + return -EDOM; + ep->is_in = !!usb_endpoint_dir_in(desc); + if (dev->enhanced_mode && ep->is_in && ep_key[ep->num]) + return -EINVAL; + } + + /* sanity check ep-e/ep-f since their fifos are small */ + max = usb_endpoint_maxp(desc) & 0x1fff; + if (ep->num > 4 && max > 64 && (dev->quirks & PLX_LEGACY)) + return -ERANGE; + + spin_lock_irqsave(&dev->lock, flags); + _ep->maxpacket = max & 0x7ff; + ep->desc = desc; + + /* ep_reset() has already been called */ + ep->stopped = 0; + ep->wedged = 0; + ep->out_overflow = 0; + + /* set speed-dependent max packet; may kick in high bandwidth */ + set_max_speed(ep, max); + + /* FIFO lines can't go to different packets. PIO is ok, so + * use it instead of troublesome (non-bulk) multi-packet DMA. + */ + if (ep->dma && (max % 4) != 0 && use_dma_chaining) { + ep_dbg(ep->dev, "%s, no dma for maxpacket %d\n", + ep->ep.name, ep->ep.maxpacket); + ep->dma = NULL; + } + + /* set type, direction, address; reset fifo counters */ + writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat); + tmp = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK); + if (tmp == USB_ENDPOINT_XFER_INT) { + /* erratum 0105 workaround prevents hs NYET */ + if (dev->chiprev == 0100 && + dev->gadget.speed == USB_SPEED_HIGH && + !(desc->bEndpointAddress & USB_DIR_IN)) + writel(BIT(CLEAR_NAK_OUT_PACKETS_MODE), + &ep->regs->ep_rsp); + } else if (tmp == USB_ENDPOINT_XFER_BULK) { + /* catch some particularly blatant driver bugs */ + if ((dev->gadget.speed == USB_SPEED_SUPER && max != 1024) || + (dev->gadget.speed == USB_SPEED_HIGH && max != 512) || + (dev->gadget.speed == USB_SPEED_FULL && max > 64)) { + spin_unlock_irqrestore(&dev->lock, flags); + return -ERANGE; + } + } + ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC); + /* Enable this endpoint */ + if (dev->quirks & PLX_LEGACY) { + tmp <<= ENDPOINT_TYPE; + tmp |= desc->bEndpointAddress; + /* default full fifo lines */ + tmp |= (4 << ENDPOINT_BYTE_COUNT); + tmp |= BIT(ENDPOINT_ENABLE); + ep->is_in = (tmp & USB_DIR_IN) != 0; + } else { + /* In Legacy mode, only OUT endpoints are used */ + if (dev->enhanced_mode && ep->is_in) { + tmp <<= IN_ENDPOINT_TYPE; + tmp |= BIT(IN_ENDPOINT_ENABLE); + /* Not applicable to Legacy */ + tmp |= BIT(ENDPOINT_DIRECTION); + } else { + tmp <<= OUT_ENDPOINT_TYPE; + tmp |= BIT(OUT_ENDPOINT_ENABLE); + tmp |= (ep->is_in << ENDPOINT_DIRECTION); + } + + tmp |= usb_endpoint_num(desc); + tmp |= (ep->ep.maxburst << MAX_BURST_SIZE); + } + + /* Make sure all the registers are written before ep_rsp*/ + wmb(); + + /* for OUT transfers, block the rx fifo until a read is posted */ + if (!ep->is_in) + writel(BIT(SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp); + else if (!(dev->quirks & PLX_2280)) { + /* Added for 2282, Don't use nak packets on an in endpoint, + * this was ignored on 2280 + */ + writel(BIT(CLEAR_NAK_OUT_PACKETS) | + BIT(CLEAR_NAK_OUT_PACKETS_MODE), &ep->regs->ep_rsp); + } + + writel(tmp, &ep->cfg->ep_cfg); + + /* enable irqs */ + if (!ep->dma) { /* pio, per-packet */ + enable_pciirqenb(ep); + + tmp = BIT(DATA_PACKET_RECEIVED_INTERRUPT_ENABLE) | + BIT(DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE); + if (dev->quirks & PLX_2280) + tmp |= readl(&ep->regs->ep_irqenb); + writel(tmp, &ep->regs->ep_irqenb); + } else { /* dma, per-request */ + tmp = BIT((8 + ep->num)); /* completion */ + tmp |= readl(&dev->regs->pciirqenb1); + writel(tmp, &dev->regs->pciirqenb1); + + /* for short OUT transfers, dma completions can't + * advance the queue; do it pio-style, by hand. + * NOTE erratum 0112 workaround #2 + */ + if ((desc->bEndpointAddress & USB_DIR_IN) == 0) { + tmp = BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT_ENABLE); + writel(tmp, &ep->regs->ep_irqenb); + + enable_pciirqenb(ep); + } + } + + tmp = desc->bEndpointAddress; + ep_dbg(dev, "enabled %s (ep%d%s-%s) %s max %04x\n", + _ep->name, tmp & 0x0f, DIR_STRING(tmp), + type_string(desc->bmAttributes), + ep->dma ? "dma" : "pio", max); + + /* pci writes may still be posted */ + spin_unlock_irqrestore(&dev->lock, flags); + return 0; +} + +static int handshake(u32 __iomem *ptr, u32 mask, u32 done, int usec) +{ + u32 result; + + do { + result = readl(ptr); + if (result == ~(u32)0) /* "device unplugged" */ + return -ENODEV; + result &= mask; + if (result == done) + return 0; + udelay(1); + usec--; + } while (usec > 0); + return -ETIMEDOUT; +} + +static const struct usb_ep_ops net2280_ep_ops; + +static void ep_reset_228x(struct net2280_regs __iomem *regs, + struct net2280_ep *ep) +{ + u32 tmp; + + ep->desc = NULL; + INIT_LIST_HEAD(&ep->queue); + + usb_ep_set_maxpacket_limit(&ep->ep, ~0); + ep->ep.ops = &net2280_ep_ops; + + /* disable the dma, irqs, endpoint... */ + if (ep->dma) { + writel(0, &ep->dma->dmactl); + writel(BIT(DMA_SCATTER_GATHER_DONE_INTERRUPT) | + BIT(DMA_TRANSACTION_DONE_INTERRUPT) | + BIT(DMA_ABORT), + &ep->dma->dmastat); + + tmp = readl(®s->pciirqenb0); + tmp &= ~BIT(ep->num); + writel(tmp, ®s->pciirqenb0); + } else { + tmp = readl(®s->pciirqenb1); + tmp &= ~BIT((8 + ep->num)); /* completion */ + writel(tmp, ®s->pciirqenb1); + } + writel(0, &ep->regs->ep_irqenb); + + /* init to our chosen defaults, notably so that we NAK OUT + * packets until the driver queues a read (+note erratum 0112) + */ + if (!ep->is_in || (ep->dev->quirks & PLX_2280)) { + tmp = BIT(SET_NAK_OUT_PACKETS_MODE) | + BIT(SET_NAK_OUT_PACKETS) | + BIT(CLEAR_EP_HIDE_STATUS_PHASE) | + BIT(CLEAR_INTERRUPT_MODE); + } else { + /* added for 2282 */ + tmp = BIT(CLEAR_NAK_OUT_PACKETS_MODE) | + BIT(CLEAR_NAK_OUT_PACKETS) | + BIT(CLEAR_EP_HIDE_STATUS_PHASE) | + BIT(CLEAR_INTERRUPT_MODE); + } + + if (ep->num != 0) { + tmp |= BIT(CLEAR_ENDPOINT_TOGGLE) | + BIT(CLEAR_ENDPOINT_HALT); + } + writel(tmp, &ep->regs->ep_rsp); + + /* scrub most status bits, and flush any fifo state */ + if (ep->dev->quirks & PLX_2280) + tmp = BIT(FIFO_OVERFLOW) | + BIT(FIFO_UNDERFLOW); + else + tmp = 0; + + writel(tmp | BIT(TIMEOUT) | + BIT(USB_STALL_SENT) | + BIT(USB_IN_NAK_SENT) | + BIT(USB_IN_ACK_RCVD) | + BIT(USB_OUT_PING_NAK_SENT) | + BIT(USB_OUT_ACK_SENT) | + BIT(FIFO_FLUSH) | + BIT(SHORT_PACKET_OUT_DONE_INTERRUPT) | + BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT) | + BIT(DATA_PACKET_RECEIVED_INTERRUPT) | + BIT(DATA_PACKET_TRANSMITTED_INTERRUPT) | + BIT(DATA_OUT_PING_TOKEN_INTERRUPT) | + BIT(DATA_IN_TOKEN_INTERRUPT), + &ep->regs->ep_stat); + + /* fifo size is handled separately */ +} + +static void ep_reset_338x(struct net2280_regs __iomem *regs, + struct net2280_ep *ep) +{ + u32 tmp, dmastat; + + ep->desc = NULL; + INIT_LIST_HEAD(&ep->queue); + + usb_ep_set_maxpacket_limit(&ep->ep, ~0); + ep->ep.ops = &net2280_ep_ops; + + /* disable the dma, irqs, endpoint... */ + if (ep->dma) { + writel(0, &ep->dma->dmactl); + writel(BIT(DMA_ABORT_DONE_INTERRUPT) | + BIT(DMA_PAUSE_DONE_INTERRUPT) | + BIT(DMA_SCATTER_GATHER_DONE_INTERRUPT) | + BIT(DMA_TRANSACTION_DONE_INTERRUPT), + /* | BIT(DMA_ABORT), */ + &ep->dma->dmastat); + + dmastat = readl(&ep->dma->dmastat); + if (dmastat == 0x5002) { + ep_warn(ep->dev, "The dmastat return = %x!!\n", + dmastat); + writel(0x5a, &ep->dma->dmastat); + } + + tmp = readl(®s->pciirqenb0); + tmp &= ~BIT(ep_bit[ep->num]); + writel(tmp, ®s->pciirqenb0); + } else { + if (ep->num < 5) { + tmp = readl(®s->pciirqenb1); + tmp &= ~BIT((8 + ep->num)); /* completion */ + writel(tmp, ®s->pciirqenb1); + } + } + writel(0, &ep->regs->ep_irqenb); + + writel(BIT(SHORT_PACKET_OUT_DONE_INTERRUPT) | + BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT) | + BIT(FIFO_OVERFLOW) | + BIT(DATA_PACKET_RECEIVED_INTERRUPT) | + BIT(DATA_PACKET_TRANSMITTED_INTERRUPT) | + BIT(DATA_OUT_PING_TOKEN_INTERRUPT) | + BIT(DATA_IN_TOKEN_INTERRUPT), &ep->regs->ep_stat); +} + +static void nuke(struct net2280_ep *); + +static int net2280_disable(struct usb_ep *_ep) +{ + struct net2280_ep *ep; + unsigned long flags; + + ep = container_of(_ep, struct net2280_ep, ep); + if (!_ep || !ep->desc || _ep->name == ep0name) + return -EINVAL; + + spin_lock_irqsave(&ep->dev->lock, flags); + nuke(ep); + + if (ep->dev->quirks & PLX_SUPERSPEED) + ep_reset_338x(ep->dev->regs, ep); + else + ep_reset_228x(ep->dev->regs, ep); + + ep_vdbg(ep->dev, "disabled %s %s\n", + ep->dma ? "dma" : "pio", _ep->name); + + /* synch memory views with the device */ + (void)readl(&ep->cfg->ep_cfg); + + if (use_dma && !ep->dma && ep->num >= 1 && ep->num <= 4) + ep->dma = &ep->dev->dma[ep->num - 1]; + + spin_unlock_irqrestore(&ep->dev->lock, flags); + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static struct usb_request +*net2280_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) +{ + struct net2280_ep *ep; + struct net2280_request *req; + + if (!_ep) + return NULL; + ep = container_of(_ep, struct net2280_ep, ep); + + req = kzalloc(sizeof(*req), gfp_flags); + if (!req) + return NULL; + + INIT_LIST_HEAD(&req->queue); + + /* this dma descriptor may be swapped with the previous dummy */ + if (ep->dma) { + struct net2280_dma *td; + + td = pci_pool_alloc(ep->dev->requests, gfp_flags, + &req->td_dma); + if (!td) { + kfree(req); + return NULL; + } + td->dmacount = 0; /* not VALID */ + td->dmadesc = td->dmaaddr; + req->td = td; + } + return &req->req; +} + +static void net2280_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + struct net2280_ep *ep; + struct net2280_request *req; + + ep = container_of(_ep, struct net2280_ep, ep); + if (!_ep || !_req) + return; + + req = container_of(_req, struct net2280_request, req); + WARN_ON(!list_empty(&req->queue)); + if (req->td) + pci_pool_free(ep->dev->requests, req->td, req->td_dma); + kfree(req); +} + +/*-------------------------------------------------------------------------*/ + +/* load a packet into the fifo we use for usb IN transfers. + * works for all endpoints. + * + * NOTE: pio with ep-a..ep-d could stuff multiple packets into the fifo + * at a time, but this code is simpler because it knows it only writes + * one packet. ep-a..ep-d should use dma instead. + */ +static void write_fifo(struct net2280_ep *ep, struct usb_request *req) +{ + struct net2280_ep_regs __iomem *regs = ep->regs; + u8 *buf; + u32 tmp; + unsigned count, total; + + /* INVARIANT: fifo is currently empty. (testable) */ + + if (req) { + buf = req->buf + req->actual; + prefetch(buf); + total = req->length - req->actual; + } else { + total = 0; + buf = NULL; + } + + /* write just one packet at a time */ + count = ep->ep.maxpacket; + if (count > total) /* min() cannot be used on a bitfield */ + count = total; + + ep_vdbg(ep->dev, "write %s fifo (IN) %d bytes%s req %p\n", + ep->ep.name, count, + (count != ep->ep.maxpacket) ? " (short)" : "", + req); + while (count >= 4) { + /* NOTE be careful if you try to align these. fifo lines + * should normally be full (4 bytes) and successive partial + * lines are ok only in certain cases. + */ + tmp = get_unaligned((u32 *)buf); + cpu_to_le32s(&tmp); + writel(tmp, ®s->ep_data); + buf += 4; + count -= 4; + } + + /* last fifo entry is "short" unless we wrote a full packet. + * also explicitly validate last word in (periodic) transfers + * when maxpacket is not a multiple of 4 bytes. + */ + if (count || total < ep->ep.maxpacket) { + tmp = count ? get_unaligned((u32 *)buf) : count; + cpu_to_le32s(&tmp); + set_fifo_bytecount(ep, count & 0x03); + writel(tmp, ®s->ep_data); + } + + /* pci writes may still be posted */ +} + +/* work around erratum 0106: PCI and USB race over the OUT fifo. + * caller guarantees chiprev 0100, out endpoint is NAKing, and + * there's no real data in the fifo. + * + * NOTE: also used in cases where that erratum doesn't apply: + * where the host wrote "too much" data to us. + */ +static void out_flush(struct net2280_ep *ep) +{ + u32 __iomem *statp; + u32 tmp; + + ASSERT_OUT_NAKING(ep); + + statp = &ep->regs->ep_stat; + writel(BIT(DATA_OUT_PING_TOKEN_INTERRUPT) | + BIT(DATA_PACKET_RECEIVED_INTERRUPT), + statp); + writel(BIT(FIFO_FLUSH), statp); + /* Make sure that stap is written */ + mb(); + tmp = readl(statp); + if (tmp & BIT(DATA_OUT_PING_TOKEN_INTERRUPT) && + /* high speed did bulk NYET; fifo isn't filling */ + ep->dev->gadget.speed == USB_SPEED_FULL) { + unsigned usec; + + usec = 50; /* 64 byte bulk/interrupt */ + handshake(statp, BIT(USB_OUT_PING_NAK_SENT), + BIT(USB_OUT_PING_NAK_SENT), usec); + /* NAK done; now CLEAR_NAK_OUT_PACKETS is safe */ + } +} + +/* unload packet(s) from the fifo we use for usb OUT transfers. + * returns true iff the request completed, because of short packet + * or the request buffer having filled with full packets. + * + * for ep-a..ep-d this will read multiple packets out when they + * have been accepted. + */ +static int read_fifo(struct net2280_ep *ep, struct net2280_request *req) +{ + struct net2280_ep_regs __iomem *regs = ep->regs; + u8 *buf = req->req.buf + req->req.actual; + unsigned count, tmp, is_short; + unsigned cleanup = 0, prevent = 0; + + /* erratum 0106 ... packets coming in during fifo reads might + * be incompletely rejected. not all cases have workarounds. + */ + if (ep->dev->chiprev == 0x0100 && + ep->dev->gadget.speed == USB_SPEED_FULL) { + udelay(1); + tmp = readl(&ep->regs->ep_stat); + if ((tmp & BIT(NAK_OUT_PACKETS))) + cleanup = 1; + else if ((tmp & BIT(FIFO_FULL))) { + start_out_naking(ep); + prevent = 1; + } + /* else: hope we don't see the problem */ + } + + /* never overflow the rx buffer. the fifo reads packets until + * it sees a short one; we might not be ready for them all. + */ + prefetchw(buf); + count = readl(®s->ep_avail); + if (unlikely(count == 0)) { + udelay(1); + tmp = readl(&ep->regs->ep_stat); + count = readl(®s->ep_avail); + /* handled that data already? */ + if (count == 0 && (tmp & BIT(NAK_OUT_PACKETS)) == 0) + return 0; + } + + tmp = req->req.length - req->req.actual; + if (count > tmp) { + /* as with DMA, data overflow gets flushed */ + if ((tmp % ep->ep.maxpacket) != 0) { + ep_err(ep->dev, + "%s out fifo %d bytes, expected %d\n", + ep->ep.name, count, tmp); + req->req.status = -EOVERFLOW; + cleanup = 1; + /* NAK_OUT_PACKETS will be set, so flushing is safe; + * the next read will start with the next packet + */ + } /* else it's a ZLP, no worries */ + count = tmp; + } + req->req.actual += count; + + is_short = (count == 0) || ((count % ep->ep.maxpacket) != 0); + + ep_vdbg(ep->dev, "read %s fifo (OUT) %d bytes%s%s%s req %p %d/%d\n", + ep->ep.name, count, is_short ? " (short)" : "", + cleanup ? " flush" : "", prevent ? " nak" : "", + req, req->req.actual, req->req.length); + + while (count >= 4) { + tmp = readl(®s->ep_data); + cpu_to_le32s(&tmp); + put_unaligned(tmp, (u32 *)buf); + buf += 4; + count -= 4; + } + if (count) { + tmp = readl(®s->ep_data); + /* LE conversion is implicit here: */ + do { + *buf++ = (u8) tmp; + tmp >>= 8; + } while (--count); + } + if (cleanup) + out_flush(ep); + if (prevent) { + writel(BIT(CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp); + (void) readl(&ep->regs->ep_rsp); + } + + return is_short || ((req->req.actual == req->req.length) && + !req->req.zero); +} + +/* fill out dma descriptor to match a given request */ +static void fill_dma_desc(struct net2280_ep *ep, + struct net2280_request *req, int valid) +{ + struct net2280_dma *td = req->td; + u32 dmacount = req->req.length; + + /* don't let DMA continue after a short OUT packet, + * so overruns can't affect the next transfer. + * in case of overruns on max-size packets, we can't + * stop the fifo from filling but we can flush it. + */ + if (ep->is_in) + dmacount |= BIT(DMA_DIRECTION); + if ((!ep->is_in && (dmacount % ep->ep.maxpacket) != 0) || + !(ep->dev->quirks & PLX_2280)) + dmacount |= BIT(END_OF_CHAIN); + + req->valid = valid; + if (valid) + dmacount |= BIT(VALID_BIT); + if (likely(!req->req.no_interrupt || !use_dma_chaining)) + dmacount |= BIT(DMA_DONE_INTERRUPT_ENABLE); + + /* td->dmadesc = previously set by caller */ + td->dmaaddr = cpu_to_le32 (req->req.dma); + + /* 2280 may be polling VALID_BIT through ep->dma->dmadesc */ + wmb(); + td->dmacount = cpu_to_le32(dmacount); +} + +static const u32 dmactl_default = + BIT(DMA_SCATTER_GATHER_DONE_INTERRUPT) | + BIT(DMA_CLEAR_COUNT_ENABLE) | + /* erratum 0116 workaround part 1 (use POLLING) */ + (POLL_100_USEC << DESCRIPTOR_POLLING_RATE) | + BIT(DMA_VALID_BIT_POLLING_ENABLE) | + BIT(DMA_VALID_BIT_ENABLE) | + BIT(DMA_SCATTER_GATHER_ENABLE) | + /* erratum 0116 workaround part 2 (no AUTOSTART) */ + BIT(DMA_ENABLE); + +static inline void spin_stop_dma(struct net2280_dma_regs __iomem *dma) +{ + handshake(&dma->dmactl, BIT(DMA_ENABLE), 0, 50); +} + +static inline void stop_dma(struct net2280_dma_regs __iomem *dma) +{ + writel(readl(&dma->dmactl) & ~BIT(DMA_ENABLE), &dma->dmactl); + spin_stop_dma(dma); +} + +static void start_queue(struct net2280_ep *ep, u32 dmactl, u32 td_dma) +{ + struct net2280_dma_regs __iomem *dma = ep->dma; + unsigned int tmp = BIT(VALID_BIT) | (ep->is_in << DMA_DIRECTION); + + if (!(ep->dev->quirks & PLX_2280)) + tmp |= BIT(END_OF_CHAIN); + + writel(tmp, &dma->dmacount); + writel(readl(&dma->dmastat), &dma->dmastat); + + writel(td_dma, &dma->dmadesc); + if (ep->dev->quirks & PLX_SUPERSPEED) + dmactl |= BIT(DMA_REQUEST_OUTSTANDING); + writel(dmactl, &dma->dmactl); + + /* erratum 0116 workaround part 3: pci arbiter away from net2280 */ + (void) readl(&ep->dev->pci->pcimstctl); + + writel(BIT(DMA_START), &dma->dmastat); + + if (!ep->is_in) + stop_out_naking(ep); +} + +static void start_dma(struct net2280_ep *ep, struct net2280_request *req) +{ + u32 tmp; + struct net2280_dma_regs __iomem *dma = ep->dma; + + /* FIXME can't use DMA for ZLPs */ + + /* on this path we "know" there's no dma active (yet) */ + WARN_ON(readl(&dma->dmactl) & BIT(DMA_ENABLE)); + writel(0, &ep->dma->dmactl); + + /* previous OUT packet might have been short */ + if (!ep->is_in && (readl(&ep->regs->ep_stat) & + BIT(NAK_OUT_PACKETS))) { + writel(BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT), + &ep->regs->ep_stat); + + tmp = readl(&ep->regs->ep_avail); + if (tmp) { + writel(readl(&dma->dmastat), &dma->dmastat); + + /* transfer all/some fifo data */ + writel(req->req.dma, &dma->dmaaddr); + tmp = min(tmp, req->req.length); + + /* dma irq, faking scatterlist status */ + req->td->dmacount = cpu_to_le32(req->req.length - tmp); + writel(BIT(DMA_DONE_INTERRUPT_ENABLE) | tmp, + &dma->dmacount); + req->td->dmadesc = 0; + req->valid = 1; + + writel(BIT(DMA_ENABLE), &dma->dmactl); + writel(BIT(DMA_START), &dma->dmastat); + return; + } + } + + tmp = dmactl_default; + + /* force packet boundaries between dma requests, but prevent the + * controller from automagically writing a last "short" packet + * (zero length) unless the driver explicitly said to do that. + */ + if (ep->is_in) { + if (likely((req->req.length % ep->ep.maxpacket) || + req->req.zero)){ + tmp |= BIT(DMA_FIFO_VALIDATE); + ep->in_fifo_validate = 1; + } else + ep->in_fifo_validate = 0; + } + + /* init req->td, pointing to the current dummy */ + req->td->dmadesc = cpu_to_le32 (ep->td_dma); + fill_dma_desc(ep, req, 1); + + if (!use_dma_chaining) + req->td->dmacount |= cpu_to_le32(BIT(END_OF_CHAIN)); + + start_queue(ep, tmp, req->td_dma); +} + +static inline void resume_dma(struct net2280_ep *ep) +{ + writel(readl(&ep->dma->dmactl) | BIT(DMA_ENABLE), &ep->dma->dmactl); + + ep->dma_started = true; +} + +static inline void ep_stop_dma(struct net2280_ep *ep) +{ + writel(readl(&ep->dma->dmactl) & ~BIT(DMA_ENABLE), &ep->dma->dmactl); + spin_stop_dma(ep->dma); + + ep->dma_started = false; +} + +static inline void +queue_dma(struct net2280_ep *ep, struct net2280_request *req, int valid) +{ + struct net2280_dma *end; + dma_addr_t tmp; + + /* swap new dummy for old, link; fill and maybe activate */ + end = ep->dummy; + ep->dummy = req->td; + req->td = end; + + tmp = ep->td_dma; + ep->td_dma = req->td_dma; + req->td_dma = tmp; + + end->dmadesc = cpu_to_le32 (ep->td_dma); + + fill_dma_desc(ep, req, valid); +} + +static void +done(struct net2280_ep *ep, struct net2280_request *req, int status) +{ + struct net2280 *dev; + unsigned stopped = ep->stopped; + + list_del_init(&req->queue); + + if (req->req.status == -EINPROGRESS) + req->req.status = status; + else + status = req->req.status; + + dev = ep->dev; + if (ep->dma) + usb_gadget_unmap_request(&dev->gadget, &req->req, ep->is_in); + + if (status && status != -ESHUTDOWN) + ep_vdbg(dev, "complete %s req %p stat %d len %u/%u\n", + ep->ep.name, &req->req, status, + req->req.actual, req->req.length); + + /* don't modify queue heads during completion callback */ + ep->stopped = 1; + spin_unlock(&dev->lock); + req->req.complete(&ep->ep, &req->req); + spin_lock(&dev->lock); + ep->stopped = stopped; +} + +/*-------------------------------------------------------------------------*/ + +static int +net2280_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) +{ + struct net2280_request *req; + struct net2280_ep *ep; + struct net2280 *dev; + unsigned long flags; + + /* we always require a cpu-view buffer, so that we can + * always use pio (as fallback or whatever). + */ + req = container_of(_req, struct net2280_request, req); + if (!_req || !_req->complete || !_req->buf || + !list_empty(&req->queue)) + return -EINVAL; + if (_req->length > (~0 & DMA_BYTE_COUNT_MASK)) + return -EDOM; + ep = container_of(_ep, struct net2280_ep, ep); + if (!_ep || (!ep->desc && ep->num != 0)) + return -EINVAL; + dev = ep->dev; + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + /* FIXME implement PIO fallback for ZLPs with DMA */ + if (ep->dma && _req->length == 0) + return -EOPNOTSUPP; + + /* set up dma mapping in case the caller didn't */ + if (ep->dma) { + int ret; + + ret = usb_gadget_map_request(&dev->gadget, _req, + ep->is_in); + if (ret) + return ret; + } + +#if 0 + ep_vdbg(dev, "%s queue req %p, len %d buf %p\n", + _ep->name, _req, _req->length, _req->buf); +#endif + + spin_lock_irqsave(&dev->lock, flags); + + _req->status = -EINPROGRESS; + _req->actual = 0; + + /* kickstart this i/o queue? */ + if (list_empty(&ep->queue) && !ep->stopped) { + /* DMA request while EP halted */ + if (ep->dma && + (readl(&ep->regs->ep_rsp) & BIT(CLEAR_ENDPOINT_HALT)) && + (dev->quirks & PLX_SUPERSPEED)) { + int valid = 1; + if (ep->is_in) { + int expect; + expect = likely(req->req.zero || + ((req->req.length % + ep->ep.maxpacket) != 0)); + if (expect != ep->in_fifo_validate) + valid = 0; + } + queue_dma(ep, req, valid); + } + /* use DMA if the endpoint supports it, else pio */ + else if (ep->dma) + start_dma(ep, req); + else { + /* maybe there's no control data, just status ack */ + if (ep->num == 0 && _req->length == 0) { + allow_status(ep); + done(ep, req, 0); + ep_vdbg(dev, "%s status ack\n", ep->ep.name); + goto done; + } + + /* PIO ... stuff the fifo, or unblock it. */ + if (ep->is_in) + write_fifo(ep, _req); + else if (list_empty(&ep->queue)) { + u32 s; + + /* OUT FIFO might have packet(s) buffered */ + s = readl(&ep->regs->ep_stat); + if ((s & BIT(FIFO_EMPTY)) == 0) { + /* note: _req->short_not_ok is + * ignored here since PIO _always_ + * stops queue advance here, and + * _req->status doesn't change for + * short reads (only _req->actual) + */ + if (read_fifo(ep, req) && + ep->num == 0) { + done(ep, req, 0); + allow_status(ep); + /* don't queue it */ + req = NULL; + } else if (read_fifo(ep, req) && + ep->num != 0) { + done(ep, req, 0); + req = NULL; + } else + s = readl(&ep->regs->ep_stat); + } + + /* don't NAK, let the fifo fill */ + if (req && (s & BIT(NAK_OUT_PACKETS))) + writel(BIT(CLEAR_NAK_OUT_PACKETS), + &ep->regs->ep_rsp); + } + } + + } else if (ep->dma) { + int valid = 1; + + if (ep->is_in) { + int expect; + + /* preventing magic zlps is per-engine state, not + * per-transfer; irq logic must recover hiccups. + */ + expect = likely(req->req.zero || + (req->req.length % ep->ep.maxpacket)); + if (expect != ep->in_fifo_validate) + valid = 0; + } + queue_dma(ep, req, valid); + + } /* else the irq handler advances the queue. */ + + ep->responded = 1; + if (req) + list_add_tail(&req->queue, &ep->queue); +done: + spin_unlock_irqrestore(&dev->lock, flags); + + /* pci writes may still be posted */ + return 0; +} + +static inline void +dma_done(struct net2280_ep *ep, struct net2280_request *req, u32 dmacount, + int status) +{ + req->req.actual = req->req.length - (DMA_BYTE_COUNT_MASK & dmacount); + done(ep, req, status); +} + +static void restart_dma(struct net2280_ep *ep); + +static void scan_dma_completions(struct net2280_ep *ep) +{ + /* only look at descriptors that were "naturally" retired, + * so fifo and list head state won't matter + */ + while (!list_empty(&ep->queue)) { + struct net2280_request *req; + u32 tmp; + + req = list_entry(ep->queue.next, + struct net2280_request, queue); + if (!req->valid) + break; + rmb(); + tmp = le32_to_cpup(&req->td->dmacount); + if ((tmp & BIT(VALID_BIT)) != 0) + break; + + /* SHORT_PACKET_TRANSFERRED_INTERRUPT handles "usb-short" + * cases where DMA must be aborted; this code handles + * all non-abort DMA completions. + */ + if (unlikely(req->td->dmadesc == 0)) { + /* paranoia */ + tmp = readl(&ep->dma->dmacount); + if (tmp & DMA_BYTE_COUNT_MASK) + break; + /* single transfer mode */ + dma_done(ep, req, tmp, 0); + break; + } else if (!ep->is_in && + (req->req.length % ep->ep.maxpacket) != 0) { + tmp = readl(&ep->regs->ep_stat); + if (ep->dev->quirks & PLX_SUPERSPEED) + return dma_done(ep, req, tmp, 0); + + /* AVOID TROUBLE HERE by not issuing short reads from + * your gadget driver. That helps avoids errata 0121, + * 0122, and 0124; not all cases trigger the warning. + */ + if ((tmp & BIT(NAK_OUT_PACKETS)) == 0) { + ep_warn(ep->dev, "%s lost packet sync!\n", + ep->ep.name); + req->req.status = -EOVERFLOW; + } else { + tmp = readl(&ep->regs->ep_avail); + if (tmp) { + /* fifo gets flushed later */ + ep->out_overflow = 1; + ep_dbg(ep->dev, + "%s dma, discard %d len %d\n", + ep->ep.name, tmp, + req->req.length); + req->req.status = -EOVERFLOW; + } + } + } + dma_done(ep, req, tmp, 0); + } +} + +static void restart_dma(struct net2280_ep *ep) +{ + struct net2280_request *req; + u32 dmactl = dmactl_default; + + if (ep->stopped) + return; + req = list_entry(ep->queue.next, struct net2280_request, queue); + + if (!use_dma_chaining) { + start_dma(ep, req); + return; + } + + /* the 2280 will be processing the queue unless queue hiccups after + * the previous transfer: + * IN: wanted automagic zlp, head doesn't (or vice versa) + * DMA_FIFO_VALIDATE doesn't init from dma descriptors. + * OUT: was "usb-short", we must restart. + */ + if (ep->is_in && !req->valid) { + struct net2280_request *entry, *prev = NULL; + int reqmode, done = 0; + + ep_dbg(ep->dev, "%s dma hiccup td %p\n", ep->ep.name, req->td); + ep->in_fifo_validate = likely(req->req.zero || + (req->req.length % ep->ep.maxpacket) != 0); + if (ep->in_fifo_validate) + dmactl |= BIT(DMA_FIFO_VALIDATE); + list_for_each_entry(entry, &ep->queue, queue) { + __le32 dmacount; + + if (entry == req) + continue; + dmacount = entry->td->dmacount; + if (!done) { + reqmode = likely(entry->req.zero || + (entry->req.length % ep->ep.maxpacket)); + if (reqmode == ep->in_fifo_validate) { + entry->valid = 1; + dmacount |= valid_bit; + entry->td->dmacount = dmacount; + prev = entry; + continue; + } else { + /* force a hiccup */ + prev->td->dmacount |= dma_done_ie; + done = 1; + } + } + + /* walk the rest of the queue so unlinks behave */ + entry->valid = 0; + dmacount &= ~valid_bit; + entry->td->dmacount = dmacount; + prev = entry; + } + } + + writel(0, &ep->dma->dmactl); + start_queue(ep, dmactl, req->td_dma); +} + +static void abort_dma_228x(struct net2280_ep *ep) +{ + /* abort the current transfer */ + if (likely(!list_empty(&ep->queue))) { + /* FIXME work around errata 0121, 0122, 0124 */ + writel(BIT(DMA_ABORT), &ep->dma->dmastat); + spin_stop_dma(ep->dma); + } else + stop_dma(ep->dma); + scan_dma_completions(ep); +} + +static void abort_dma_338x(struct net2280_ep *ep) +{ + writel(BIT(DMA_ABORT), &ep->dma->dmastat); + spin_stop_dma(ep->dma); +} + +static void abort_dma(struct net2280_ep *ep) +{ + if (ep->dev->quirks & PLX_LEGACY) + return abort_dma_228x(ep); + return abort_dma_338x(ep); +} + +/* dequeue ALL requests */ +static void nuke(struct net2280_ep *ep) +{ + struct net2280_request *req; + + /* called with spinlock held */ + ep->stopped = 1; + if (ep->dma) + abort_dma(ep); + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, + struct net2280_request, + queue); + done(ep, req, -ESHUTDOWN); + } +} + +/* dequeue JUST ONE request */ +static int net2280_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct net2280_ep *ep; + struct net2280_request *req; + unsigned long flags; + u32 dmactl; + int stopped; + + ep = container_of(_ep, struct net2280_ep, ep); + if (!_ep || (!ep->desc && ep->num != 0) || !_req) + return -EINVAL; + + spin_lock_irqsave(&ep->dev->lock, flags); + stopped = ep->stopped; + + /* quiesce dma while we patch the queue */ + dmactl = 0; + ep->stopped = 1; + if (ep->dma) { + dmactl = readl(&ep->dma->dmactl); + /* WARNING erratum 0127 may kick in ... */ + stop_dma(ep->dma); + scan_dma_completions(ep); + } + + /* make sure it's still queued on this endpoint */ + list_for_each_entry(req, &ep->queue, queue) { + if (&req->req == _req) + break; + } + if (&req->req != _req) { + spin_unlock_irqrestore(&ep->dev->lock, flags); + return -EINVAL; + } + + /* queue head may be partially complete. */ + if (ep->queue.next == &req->queue) { + if (ep->dma) { + ep_dbg(ep->dev, "unlink (%s) dma\n", _ep->name); + _req->status = -ECONNRESET; + abort_dma(ep); + if (likely(ep->queue.next == &req->queue)) { + /* NOTE: misreports single-transfer mode*/ + req->td->dmacount = 0; /* invalidate */ + dma_done(ep, req, + readl(&ep->dma->dmacount), + -ECONNRESET); + } + } else { + ep_dbg(ep->dev, "unlink (%s) pio\n", _ep->name); + done(ep, req, -ECONNRESET); + } + req = NULL; + + /* patch up hardware chaining data */ + } else if (ep->dma && use_dma_chaining) { + if (req->queue.prev == ep->queue.next) { + writel(le32_to_cpu(req->td->dmadesc), + &ep->dma->dmadesc); + if (req->td->dmacount & dma_done_ie) + writel(readl(&ep->dma->dmacount) | + le32_to_cpu(dma_done_ie), + &ep->dma->dmacount); + } else { + struct net2280_request *prev; + + prev = list_entry(req->queue.prev, + struct net2280_request, queue); + prev->td->dmadesc = req->td->dmadesc; + if (req->td->dmacount & dma_done_ie) + prev->td->dmacount |= dma_done_ie; + } + } + + if (req) + done(ep, req, -ECONNRESET); + ep->stopped = stopped; + + if (ep->dma) { + /* turn off dma on inactive queues */ + if (list_empty(&ep->queue)) + stop_dma(ep->dma); + else if (!ep->stopped) { + /* resume current request, or start new one */ + if (req) + writel(dmactl, &ep->dma->dmactl); + else + start_dma(ep, list_entry(ep->queue.next, + struct net2280_request, queue)); + } + } + + spin_unlock_irqrestore(&ep->dev->lock, flags); + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static int net2280_fifo_status(struct usb_ep *_ep); + +static int +net2280_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged) +{ + struct net2280_ep *ep; + unsigned long flags; + int retval = 0; + + ep = container_of(_ep, struct net2280_ep, ep); + if (!_ep || (!ep->desc && ep->num != 0)) + return -EINVAL; + if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + if (ep->desc /* not ep0 */ && (ep->desc->bmAttributes & 0x03) + == USB_ENDPOINT_XFER_ISOC) + return -EINVAL; + + spin_lock_irqsave(&ep->dev->lock, flags); + if (!list_empty(&ep->queue)) + retval = -EAGAIN; + else if (ep->is_in && value && net2280_fifo_status(_ep) != 0) + retval = -EAGAIN; + else { + ep_vdbg(ep->dev, "%s %s %s\n", _ep->name, + value ? "set" : "clear", + wedged ? "wedge" : "halt"); + /* set/clear, then synch memory views with the device */ + if (value) { + if (ep->num == 0) + ep->dev->protocol_stall = 1; + else + set_halt(ep); + if (wedged) + ep->wedged = 1; + } else { + clear_halt(ep); + if (ep->dev->quirks & PLX_SUPERSPEED && + !list_empty(&ep->queue) && ep->td_dma) + restart_dma(ep); + ep->wedged = 0; + } + (void) readl(&ep->regs->ep_rsp); + } + spin_unlock_irqrestore(&ep->dev->lock, flags); + + return retval; +} + +static int net2280_set_halt(struct usb_ep *_ep, int value) +{ + return net2280_set_halt_and_wedge(_ep, value, 0); +} + +static int net2280_set_wedge(struct usb_ep *_ep) +{ + if (!_ep || _ep->name == ep0name) + return -EINVAL; + return net2280_set_halt_and_wedge(_ep, 1, 1); +} + +static int net2280_fifo_status(struct usb_ep *_ep) +{ + struct net2280_ep *ep; + u32 avail; + + ep = container_of(_ep, struct net2280_ep, ep); + if (!_ep || (!ep->desc && ep->num != 0)) + return -ENODEV; + if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + avail = readl(&ep->regs->ep_avail) & (BIT(12) - 1); + if (avail > ep->fifo_size) + return -EOVERFLOW; + if (ep->is_in) + avail = ep->fifo_size - avail; + return avail; +} + +static void net2280_fifo_flush(struct usb_ep *_ep) +{ + struct net2280_ep *ep; + + ep = container_of(_ep, struct net2280_ep, ep); + if (!_ep || (!ep->desc && ep->num != 0)) + return; + if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) + return; + + writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat); + (void) readl(&ep->regs->ep_rsp); +} + +static const struct usb_ep_ops net2280_ep_ops = { + .enable = net2280_enable, + .disable = net2280_disable, + + .alloc_request = net2280_alloc_request, + .free_request = net2280_free_request, + + .queue = net2280_queue, + .dequeue = net2280_dequeue, + + .set_halt = net2280_set_halt, + .set_wedge = net2280_set_wedge, + .fifo_status = net2280_fifo_status, + .fifo_flush = net2280_fifo_flush, +}; + +/*-------------------------------------------------------------------------*/ + +static int net2280_get_frame(struct usb_gadget *_gadget) +{ + struct net2280 *dev; + unsigned long flags; + u16 retval; + + if (!_gadget) + return -ENODEV; + dev = container_of(_gadget, struct net2280, gadget); + spin_lock_irqsave(&dev->lock, flags); + retval = get_idx_reg(dev->regs, REG_FRAME) & 0x03ff; + spin_unlock_irqrestore(&dev->lock, flags); + return retval; +} + +static int net2280_wakeup(struct usb_gadget *_gadget) +{ + struct net2280 *dev; + u32 tmp; + unsigned long flags; + + if (!_gadget) + return 0; + dev = container_of(_gadget, struct net2280, gadget); + + spin_lock_irqsave(&dev->lock, flags); + tmp = readl(&dev->usb->usbctl); + if (tmp & BIT(DEVICE_REMOTE_WAKEUP_ENABLE)) + writel(BIT(GENERATE_RESUME), &dev->usb->usbstat); + spin_unlock_irqrestore(&dev->lock, flags); + + /* pci writes may still be posted */ + return 0; +} + +static int net2280_set_selfpowered(struct usb_gadget *_gadget, int value) +{ + struct net2280 *dev; + u32 tmp; + unsigned long flags; + + if (!_gadget) + return 0; + dev = container_of(_gadget, struct net2280, gadget); + + spin_lock_irqsave(&dev->lock, flags); + tmp = readl(&dev->usb->usbctl); + if (value) { + tmp |= BIT(SELF_POWERED_STATUS); + dev->selfpowered = 1; + } else { + tmp &= ~BIT(SELF_POWERED_STATUS); + dev->selfpowered = 0; + } + writel(tmp, &dev->usb->usbctl); + spin_unlock_irqrestore(&dev->lock, flags); + + return 0; +} + +static int net2280_pullup(struct usb_gadget *_gadget, int is_on) +{ + struct net2280 *dev; + u32 tmp; + unsigned long flags; + + if (!_gadget) + return -ENODEV; + dev = container_of(_gadget, struct net2280, gadget); + + spin_lock_irqsave(&dev->lock, flags); + tmp = readl(&dev->usb->usbctl); + dev->softconnect = (is_on != 0); + if (is_on) + tmp |= BIT(USB_DETECT_ENABLE); + else + tmp &= ~BIT(USB_DETECT_ENABLE); + writel(tmp, &dev->usb->usbctl); + spin_unlock_irqrestore(&dev->lock, flags); + + return 0; +} + +static int net2280_start(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver); +static int net2280_stop(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver); + +static const struct usb_gadget_ops net2280_ops = { + .get_frame = net2280_get_frame, + .wakeup = net2280_wakeup, + .set_selfpowered = net2280_set_selfpowered, + .pullup = net2280_pullup, + .udc_start = net2280_start, + .udc_stop = net2280_stop, +}; + +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + +/* FIXME move these into procfs, and use seq_file. + * Sysfs _still_ doesn't behave for arbitrarily sized files, + * and also doesn't help products using this with 2.4 kernels. + */ + +/* "function" sysfs attribute */ +static ssize_t function_show(struct device *_dev, struct device_attribute *attr, + char *buf) +{ + struct net2280 *dev = dev_get_drvdata(_dev); + + if (!dev->driver || !dev->driver->function || + strlen(dev->driver->function) > PAGE_SIZE) + return 0; + return scnprintf(buf, PAGE_SIZE, "%s\n", dev->driver->function); +} +static DEVICE_ATTR_RO(function); + +static ssize_t registers_show(struct device *_dev, + struct device_attribute *attr, char *buf) +{ + struct net2280 *dev; + char *next; + unsigned size, t; + unsigned long flags; + int i; + u32 t1, t2; + const char *s; + + dev = dev_get_drvdata(_dev); + next = buf; + size = PAGE_SIZE; + spin_lock_irqsave(&dev->lock, flags); + + if (dev->driver) + s = dev->driver->driver.name; + else + s = "(none)"; + + /* Main Control Registers */ + t = scnprintf(next, size, "%s version " DRIVER_VERSION + ", chiprev %04x, dma %s\n\n" + "devinit %03x fifoctl %08x gadget '%s'\n" + "pci irqenb0 %02x irqenb1 %08x " + "irqstat0 %04x irqstat1 %08x\n", + driver_name, dev->chiprev, + use_dma + ? (use_dma_chaining ? "chaining" : "enabled") + : "disabled", + readl(&dev->regs->devinit), + readl(&dev->regs->fifoctl), + s, + readl(&dev->regs->pciirqenb0), + readl(&dev->regs->pciirqenb1), + readl(&dev->regs->irqstat0), + readl(&dev->regs->irqstat1)); + size -= t; + next += t; + + /* USB Control Registers */ + t1 = readl(&dev->usb->usbctl); + t2 = readl(&dev->usb->usbstat); + if (t1 & BIT(VBUS_PIN)) { + if (t2 & BIT(HIGH_SPEED)) + s = "high speed"; + else if (dev->gadget.speed == USB_SPEED_UNKNOWN) + s = "powered"; + else + s = "full speed"; + /* full speed bit (6) not working?? */ + } else + s = "not attached"; + t = scnprintf(next, size, + "stdrsp %08x usbctl %08x usbstat %08x " + "addr 0x%02x (%s)\n", + readl(&dev->usb->stdrsp), t1, t2, + readl(&dev->usb->ouraddr), s); + size -= t; + next += t; + + /* PCI Master Control Registers */ + + /* DMA Control Registers */ + + /* Configurable EP Control Registers */ + for (i = 0; i < dev->n_ep; i++) { + struct net2280_ep *ep; + + ep = &dev->ep[i]; + if (i && !ep->desc) + continue; + + t1 = readl(&ep->cfg->ep_cfg); + t2 = readl(&ep->regs->ep_rsp) & 0xff; + t = scnprintf(next, size, + "\n%s\tcfg %05x rsp (%02x) %s%s%s%s%s%s%s%s" + "irqenb %02x\n", + ep->ep.name, t1, t2, + (t2 & BIT(CLEAR_NAK_OUT_PACKETS)) + ? "NAK " : "", + (t2 & BIT(CLEAR_EP_HIDE_STATUS_PHASE)) + ? "hide " : "", + (t2 & BIT(CLEAR_EP_FORCE_CRC_ERROR)) + ? "CRC " : "", + (t2 & BIT(CLEAR_INTERRUPT_MODE)) + ? "interrupt " : "", + (t2 & BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE)) + ? "status " : "", + (t2 & BIT(CLEAR_NAK_OUT_PACKETS_MODE)) + ? "NAKmode " : "", + (t2 & BIT(CLEAR_ENDPOINT_TOGGLE)) + ? "DATA1 " : "DATA0 ", + (t2 & BIT(CLEAR_ENDPOINT_HALT)) + ? "HALT " : "", + readl(&ep->regs->ep_irqenb)); + size -= t; + next += t; + + t = scnprintf(next, size, + "\tstat %08x avail %04x " + "(ep%d%s-%s)%s\n", + readl(&ep->regs->ep_stat), + readl(&ep->regs->ep_avail), + t1 & 0x0f, DIR_STRING(t1), + type_string(t1 >> 8), + ep->stopped ? "*" : ""); + size -= t; + next += t; + + if (!ep->dma) + continue; + + t = scnprintf(next, size, + " dma\tctl %08x stat %08x count %08x\n" + "\taddr %08x desc %08x\n", + readl(&ep->dma->dmactl), + readl(&ep->dma->dmastat), + readl(&ep->dma->dmacount), + readl(&ep->dma->dmaaddr), + readl(&ep->dma->dmadesc)); + size -= t; + next += t; + + } + + /* Indexed Registers (none yet) */ + + /* Statistics */ + t = scnprintf(next, size, "\nirqs: "); + size -= t; + next += t; + for (i = 0; i < dev->n_ep; i++) { + struct net2280_ep *ep; + + ep = &dev->ep[i]; + if (i && !ep->irqs) + continue; + t = scnprintf(next, size, " %s/%lu", ep->ep.name, ep->irqs); + size -= t; + next += t; + + } + t = scnprintf(next, size, "\n"); + size -= t; + next += t; + + spin_unlock_irqrestore(&dev->lock, flags); + + return PAGE_SIZE - size; +} +static DEVICE_ATTR_RO(registers); + +static ssize_t queues_show(struct device *_dev, struct device_attribute *attr, + char *buf) +{ + struct net2280 *dev; + char *next; + unsigned size; + unsigned long flags; + int i; + + dev = dev_get_drvdata(_dev); + next = buf; + size = PAGE_SIZE; + spin_lock_irqsave(&dev->lock, flags); + + for (i = 0; i < dev->n_ep; i++) { + struct net2280_ep *ep = &dev->ep[i]; + struct net2280_request *req; + int t; + + if (i != 0) { + const struct usb_endpoint_descriptor *d; + + d = ep->desc; + if (!d) + continue; + t = d->bEndpointAddress; + t = scnprintf(next, size, + "\n%s (ep%d%s-%s) max %04x %s fifo %d\n", + ep->ep.name, t & USB_ENDPOINT_NUMBER_MASK, + (t & USB_DIR_IN) ? "in" : "out", + type_string(d->bmAttributes), + usb_endpoint_maxp(d) & 0x1fff, + ep->dma ? "dma" : "pio", ep->fifo_size + ); + } else /* ep0 should only have one transfer queued */ + t = scnprintf(next, size, "ep0 max 64 pio %s\n", + ep->is_in ? "in" : "out"); + if (t <= 0 || t > size) + goto done; + size -= t; + next += t; + + if (list_empty(&ep->queue)) { + t = scnprintf(next, size, "\t(nothing queued)\n"); + if (t <= 0 || t > size) + goto done; + size -= t; + next += t; + continue; + } + list_for_each_entry(req, &ep->queue, queue) { + if (ep->dma && req->td_dma == readl(&ep->dma->dmadesc)) + t = scnprintf(next, size, + "\treq %p len %d/%d " + "buf %p (dmacount %08x)\n", + &req->req, req->req.actual, + req->req.length, req->req.buf, + readl(&ep->dma->dmacount)); + else + t = scnprintf(next, size, + "\treq %p len %d/%d buf %p\n", + &req->req, req->req.actual, + req->req.length, req->req.buf); + if (t <= 0 || t > size) + goto done; + size -= t; + next += t; + + if (ep->dma) { + struct net2280_dma *td; + + td = req->td; + t = scnprintf(next, size, "\t td %08x " + " count %08x buf %08x desc %08x\n", + (u32) req->td_dma, + le32_to_cpu(td->dmacount), + le32_to_cpu(td->dmaaddr), + le32_to_cpu(td->dmadesc)); + if (t <= 0 || t > size) + goto done; + size -= t; + next += t; + } + } + } + +done: + spin_unlock_irqrestore(&dev->lock, flags); + return PAGE_SIZE - size; +} +static DEVICE_ATTR_RO(queues); + + +#else + +#define device_create_file(a, b) (0) +#define device_remove_file(a, b) do { } while (0) + +#endif + +/*-------------------------------------------------------------------------*/ + +/* another driver-specific mode might be a request type doing dma + * to/from another device fifo instead of to/from memory. + */ + +static void set_fifo_mode(struct net2280 *dev, int mode) +{ + /* keeping high bits preserves BAR2 */ + writel((0xffff << PCI_BASE2_RANGE) | mode, &dev->regs->fifoctl); + + /* always ep-{a,b,e,f} ... maybe not ep-c or ep-d */ + INIT_LIST_HEAD(&dev->gadget.ep_list); + list_add_tail(&dev->ep[1].ep.ep_list, &dev->gadget.ep_list); + list_add_tail(&dev->ep[2].ep.ep_list, &dev->gadget.ep_list); + switch (mode) { + case 0: + list_add_tail(&dev->ep[3].ep.ep_list, &dev->gadget.ep_list); + list_add_tail(&dev->ep[4].ep.ep_list, &dev->gadget.ep_list); + dev->ep[1].fifo_size = dev->ep[2].fifo_size = 1024; + break; + case 1: + dev->ep[1].fifo_size = dev->ep[2].fifo_size = 2048; + break; + case 2: + list_add_tail(&dev->ep[3].ep.ep_list, &dev->gadget.ep_list); + dev->ep[1].fifo_size = 2048; + dev->ep[2].fifo_size = 1024; + break; + } + /* fifo sizes for ep0, ep-c, ep-d, ep-e, and ep-f never change */ + list_add_tail(&dev->ep[5].ep.ep_list, &dev->gadget.ep_list); + list_add_tail(&dev->ep[6].ep.ep_list, &dev->gadget.ep_list); +} + +static void defect7374_disable_data_eps(struct net2280 *dev) +{ + /* + * For Defect 7374, disable data EPs (and more): + * - This phase undoes the earlier phase of the Defect 7374 workaround, + * returing ep regs back to normal. + */ + struct net2280_ep *ep; + int i; + unsigned char ep_sel; + u32 tmp_reg; + + for (i = 1; i < 5; i++) { + ep = &dev->ep[i]; + writel(0, &ep->cfg->ep_cfg); + } + + /* CSROUT, CSRIN, PCIOUT, PCIIN, STATIN, RCIN */ + for (i = 0; i < 6; i++) + writel(0, &dev->dep[i].dep_cfg); + + for (ep_sel = 0; ep_sel <= 21; ep_sel++) { + /* Select an endpoint for subsequent operations: */ + tmp_reg = readl(&dev->plregs->pl_ep_ctrl); + writel(((tmp_reg & ~0x1f) | ep_sel), &dev->plregs->pl_ep_ctrl); + + if (ep_sel < 2 || (ep_sel > 9 && ep_sel < 14) || + ep_sel == 18 || ep_sel == 20) + continue; + + /* Change settings on some selected endpoints */ + tmp_reg = readl(&dev->plregs->pl_ep_cfg_4); + tmp_reg &= ~BIT(NON_CTRL_IN_TOLERATE_BAD_DIR); + writel(tmp_reg, &dev->plregs->pl_ep_cfg_4); + tmp_reg = readl(&dev->plregs->pl_ep_ctrl); + tmp_reg |= BIT(EP_INITIALIZED); + writel(tmp_reg, &dev->plregs->pl_ep_ctrl); + } +} + +static void defect7374_enable_data_eps_zero(struct net2280 *dev) +{ + u32 tmp = 0, tmp_reg; + u32 fsmvalue, scratch; + int i; + unsigned char ep_sel; + + scratch = get_idx_reg(dev->regs, SCRATCH); + fsmvalue = scratch & (0xf << DEFECT7374_FSM_FIELD); + scratch &= ~(0xf << DEFECT7374_FSM_FIELD); + + /*See if firmware needs to set up for workaround*/ + if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ) { + ep_warn(dev, "Operate Defect 7374 workaround soft this time"); + ep_warn(dev, "It will operate on cold-reboot and SS connect"); + + /*GPEPs:*/ + tmp = ((0 << ENDPOINT_NUMBER) | BIT(ENDPOINT_DIRECTION) | + (2 << OUT_ENDPOINT_TYPE) | (2 << IN_ENDPOINT_TYPE) | + ((dev->enhanced_mode) ? + BIT(OUT_ENDPOINT_ENABLE) : BIT(ENDPOINT_ENABLE)) | + BIT(IN_ENDPOINT_ENABLE)); + + for (i = 1; i < 5; i++) + writel(tmp, &dev->ep[i].cfg->ep_cfg); + + /* CSRIN, PCIIN, STATIN, RCIN*/ + tmp = ((0 << ENDPOINT_NUMBER) | BIT(ENDPOINT_ENABLE)); + writel(tmp, &dev->dep[1].dep_cfg); + writel(tmp, &dev->dep[3].dep_cfg); + writel(tmp, &dev->dep[4].dep_cfg); + writel(tmp, &dev->dep[5].dep_cfg); + + /*Implemented for development and debug. + * Can be refined/tuned later.*/ + for (ep_sel = 0; ep_sel <= 21; ep_sel++) { + /* Select an endpoint for subsequent operations: */ + tmp_reg = readl(&dev->plregs->pl_ep_ctrl); + writel(((tmp_reg & ~0x1f) | ep_sel), + &dev->plregs->pl_ep_ctrl); + + if (ep_sel == 1) { + tmp = + (readl(&dev->plregs->pl_ep_ctrl) | + BIT(CLEAR_ACK_ERROR_CODE) | 0); + writel(tmp, &dev->plregs->pl_ep_ctrl); + continue; + } + + if (ep_sel == 0 || (ep_sel > 9 && ep_sel < 14) || + ep_sel == 18 || ep_sel == 20) + continue; + + tmp = (readl(&dev->plregs->pl_ep_cfg_4) | + BIT(NON_CTRL_IN_TOLERATE_BAD_DIR) | 0); + writel(tmp, &dev->plregs->pl_ep_cfg_4); + + tmp = readl(&dev->plregs->pl_ep_ctrl) & + ~BIT(EP_INITIALIZED); + writel(tmp, &dev->plregs->pl_ep_ctrl); + + } + + /* Set FSM to focus on the first Control Read: + * - Tip: Connection speed is known upon the first + * setup request.*/ + scratch |= DEFECT7374_FSM_WAITING_FOR_CONTROL_READ; + set_idx_reg(dev->regs, SCRATCH, scratch); + + } else{ + ep_warn(dev, "Defect 7374 workaround soft will NOT operate"); + ep_warn(dev, "It will operate on cold-reboot and SS connect"); + } +} + +/* keeping it simple: + * - one bus driver, initted first; + * - one function driver, initted second + * + * most of the work to support multiple net2280 controllers would + * be to associate this gadget driver (yes?) with all of them, or + * perhaps to bind specific drivers to specific devices. + */ + +static void usb_reset_228x(struct net2280 *dev) +{ + u32 tmp; + + dev->gadget.speed = USB_SPEED_UNKNOWN; + (void) readl(&dev->usb->usbctl); + + net2280_led_init(dev); + + /* disable automatic responses, and irqs */ + writel(0, &dev->usb->stdrsp); + writel(0, &dev->regs->pciirqenb0); + writel(0, &dev->regs->pciirqenb1); + + /* clear old dma and irq state */ + for (tmp = 0; tmp < 4; tmp++) { + struct net2280_ep *ep = &dev->ep[tmp + 1]; + if (ep->dma) + abort_dma(ep); + } + + writel(~0, &dev->regs->irqstat0), + writel(~(u32)BIT(SUSPEND_REQUEST_INTERRUPT), &dev->regs->irqstat1), + + /* reset, and enable pci */ + tmp = readl(&dev->regs->devinit) | + BIT(PCI_ENABLE) | + BIT(FIFO_SOFT_RESET) | + BIT(USB_SOFT_RESET) | + BIT(M8051_RESET); + writel(tmp, &dev->regs->devinit); + + /* standard fifo and endpoint allocations */ + set_fifo_mode(dev, (fifo_mode <= 2) ? fifo_mode : 0); +} + +static void usb_reset_338x(struct net2280 *dev) +{ + u32 tmp; + u32 fsmvalue; + + dev->gadget.speed = USB_SPEED_UNKNOWN; + (void)readl(&dev->usb->usbctl); + + net2280_led_init(dev); + + fsmvalue = get_idx_reg(dev->regs, SCRATCH) & + (0xf << DEFECT7374_FSM_FIELD); + + /* See if firmware needs to set up for workaround: */ + if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ) { + ep_info(dev, "%s: Defect 7374 FsmValue 0x%08x\n", __func__, + fsmvalue); + } else { + /* disable automatic responses, and irqs */ + writel(0, &dev->usb->stdrsp); + writel(0, &dev->regs->pciirqenb0); + writel(0, &dev->regs->pciirqenb1); + } + + /* clear old dma and irq state */ + for (tmp = 0; tmp < 4; tmp++) { + struct net2280_ep *ep = &dev->ep[tmp + 1]; + + if (ep->dma) + abort_dma(ep); + } + + writel(~0, &dev->regs->irqstat0), writel(~0, &dev->regs->irqstat1); + + if (fsmvalue == DEFECT7374_FSM_SS_CONTROL_READ) { + /* reset, and enable pci */ + tmp = readl(&dev->regs->devinit) | + BIT(PCI_ENABLE) | + BIT(FIFO_SOFT_RESET) | + BIT(USB_SOFT_RESET) | + BIT(M8051_RESET); + + writel(tmp, &dev->regs->devinit); + } + + /* always ep-{1,2,3,4} ... maybe not ep-3 or ep-4 */ + INIT_LIST_HEAD(&dev->gadget.ep_list); + + for (tmp = 1; tmp < dev->n_ep; tmp++) + list_add_tail(&dev->ep[tmp].ep.ep_list, &dev->gadget.ep_list); + +} + +static void usb_reset(struct net2280 *dev) +{ + if (dev->quirks & PLX_LEGACY) + return usb_reset_228x(dev); + return usb_reset_338x(dev); +} + +static void usb_reinit_228x(struct net2280 *dev) +{ + u32 tmp; + int init_dma; + + /* use_dma changes are ignored till next device re-init */ + init_dma = use_dma; + + /* basic endpoint init */ + for (tmp = 0; tmp < 7; tmp++) { + struct net2280_ep *ep = &dev->ep[tmp]; + + ep->ep.name = ep_name[tmp]; + ep->dev = dev; + ep->num = tmp; + + if (tmp > 0 && tmp <= 4) { + ep->fifo_size = 1024; + if (init_dma) + ep->dma = &dev->dma[tmp - 1]; + } else + ep->fifo_size = 64; + ep->regs = &dev->epregs[tmp]; + ep->cfg = &dev->epregs[tmp]; + ep_reset_228x(dev->regs, ep); + } + usb_ep_set_maxpacket_limit(&dev->ep[0].ep, 64); + usb_ep_set_maxpacket_limit(&dev->ep[5].ep, 64); + usb_ep_set_maxpacket_limit(&dev->ep[6].ep, 64); + + dev->gadget.ep0 = &dev->ep[0].ep; + dev->ep[0].stopped = 0; + INIT_LIST_HEAD(&dev->gadget.ep0->ep_list); + + /* we want to prevent lowlevel/insecure access from the USB host, + * but erratum 0119 means this enable bit is ignored + */ + for (tmp = 0; tmp < 5; tmp++) + writel(EP_DONTUSE, &dev->dep[tmp].dep_cfg); +} + +static void usb_reinit_338x(struct net2280 *dev) +{ + int init_dma; + int i; + u32 tmp, val; + u32 fsmvalue; + static const u32 ne[9] = { 0, 1, 2, 3, 4, 1, 2, 3, 4 }; + static const u32 ep_reg_addr[9] = { 0x00, 0xC0, 0x00, 0xC0, 0x00, + 0x00, 0xC0, 0x00, 0xC0 }; + + /* use_dma changes are ignored till next device re-init */ + init_dma = use_dma; + + /* basic endpoint init */ + for (i = 0; i < dev->n_ep; i++) { + struct net2280_ep *ep = &dev->ep[i]; + + ep->ep.name = ep_name[i]; + ep->dev = dev; + ep->num = i; + + if (i > 0 && i <= 4 && init_dma) + ep->dma = &dev->dma[i - 1]; + + if (dev->enhanced_mode) { + ep->cfg = &dev->epregs[ne[i]]; + ep->regs = (struct net2280_ep_regs __iomem *) + (((void __iomem *)&dev->epregs[ne[i]]) + + ep_reg_addr[i]); + ep->fiforegs = &dev->fiforegs[i]; + } else { + ep->cfg = &dev->epregs[i]; + ep->regs = &dev->epregs[i]; + ep->fiforegs = &dev->fiforegs[i]; + } + + ep->fifo_size = (i != 0) ? 2048 : 512; + + ep_reset_338x(dev->regs, ep); + } + usb_ep_set_maxpacket_limit(&dev->ep[0].ep, 512); + + dev->gadget.ep0 = &dev->ep[0].ep; + dev->ep[0].stopped = 0; + + /* Link layer set up */ + fsmvalue = get_idx_reg(dev->regs, SCRATCH) & + (0xf << DEFECT7374_FSM_FIELD); + + /* See if driver needs to set up for workaround: */ + if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ) + ep_info(dev, "%s: Defect 7374 FsmValue %08x\n", + __func__, fsmvalue); + else { + tmp = readl(&dev->usb_ext->usbctl2) & + ~(BIT(U1_ENABLE) | BIT(U2_ENABLE) | BIT(LTM_ENABLE)); + writel(tmp, &dev->usb_ext->usbctl2); + } + + /* Hardware Defect and Workaround */ + val = readl(&dev->ll_lfps_regs->ll_lfps_5); + val &= ~(0xf << TIMER_LFPS_6US); + val |= 0x5 << TIMER_LFPS_6US; + writel(val, &dev->ll_lfps_regs->ll_lfps_5); + + val = readl(&dev->ll_lfps_regs->ll_lfps_6); + val &= ~(0xffff << TIMER_LFPS_80US); + val |= 0x0100 << TIMER_LFPS_80US; + writel(val, &dev->ll_lfps_regs->ll_lfps_6); + + /* + * AA_AB Errata. Issue 4. Workaround for SuperSpeed USB + * Hot Reset Exit Handshake may Fail in Specific Case using + * Default Register Settings. Workaround for Enumeration test. + */ + val = readl(&dev->ll_tsn_regs->ll_tsn_counters_2); + val &= ~(0x1f << HOT_TX_NORESET_TS2); + val |= 0x10 << HOT_TX_NORESET_TS2; + writel(val, &dev->ll_tsn_regs->ll_tsn_counters_2); + + val = readl(&dev->ll_tsn_regs->ll_tsn_counters_3); + val &= ~(0x1f << HOT_RX_RESET_TS2); + val |= 0x3 << HOT_RX_RESET_TS2; + writel(val, &dev->ll_tsn_regs->ll_tsn_counters_3); + + /* + * Set Recovery Idle to Recover bit: + * - On SS connections, setting Recovery Idle to Recover Fmw improves + * link robustness with various hosts and hubs. + * - It is safe to set for all connection speeds; all chip revisions. + * - R-M-W to leave other bits undisturbed. + * - Reference PLX TT-7372 + */ + val = readl(&dev->ll_chicken_reg->ll_tsn_chicken_bit); + val |= BIT(RECOVERY_IDLE_TO_RECOVER_FMW); + writel(val, &dev->ll_chicken_reg->ll_tsn_chicken_bit); + + INIT_LIST_HEAD(&dev->gadget.ep0->ep_list); + + /* disable dedicated endpoints */ + writel(0x0D, &dev->dep[0].dep_cfg); + writel(0x0D, &dev->dep[1].dep_cfg); + writel(0x0E, &dev->dep[2].dep_cfg); + writel(0x0E, &dev->dep[3].dep_cfg); + writel(0x0F, &dev->dep[4].dep_cfg); + writel(0x0C, &dev->dep[5].dep_cfg); +} + +static void usb_reinit(struct net2280 *dev) +{ + if (dev->quirks & PLX_LEGACY) + return usb_reinit_228x(dev); + return usb_reinit_338x(dev); +} + +static void ep0_start_228x(struct net2280 *dev) +{ + writel(BIT(CLEAR_EP_HIDE_STATUS_PHASE) | + BIT(CLEAR_NAK_OUT_PACKETS) | + BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE), + &dev->epregs[0].ep_rsp); + + /* + * hardware optionally handles a bunch of standard requests + * that the API hides from drivers anyway. have it do so. + * endpoint status/features are handled in software, to + * help pass tests for some dubious behavior. + */ + writel(BIT(SET_TEST_MODE) | + BIT(SET_ADDRESS) | + BIT(DEVICE_SET_CLEAR_DEVICE_REMOTE_WAKEUP) | + BIT(GET_DEVICE_STATUS) | + BIT(GET_INTERFACE_STATUS), + &dev->usb->stdrsp); + writel(BIT(USB_ROOT_PORT_WAKEUP_ENABLE) | + BIT(SELF_POWERED_USB_DEVICE) | + BIT(REMOTE_WAKEUP_SUPPORT) | + (dev->softconnect << USB_DETECT_ENABLE) | + BIT(SELF_POWERED_STATUS), + &dev->usb->usbctl); + + /* enable irqs so we can see ep0 and general operation */ + writel(BIT(SETUP_PACKET_INTERRUPT_ENABLE) | + BIT(ENDPOINT_0_INTERRUPT_ENABLE), + &dev->regs->pciirqenb0); + writel(BIT(PCI_INTERRUPT_ENABLE) | + BIT(PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE) | + BIT(PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE) | + BIT(PCI_RETRY_ABORT_INTERRUPT_ENABLE) | + BIT(VBUS_INTERRUPT_ENABLE) | + BIT(ROOT_PORT_RESET_INTERRUPT_ENABLE) | + BIT(SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE), + &dev->regs->pciirqenb1); + + /* don't leave any writes posted */ + (void) readl(&dev->usb->usbctl); +} + +static void ep0_start_338x(struct net2280 *dev) +{ + u32 fsmvalue; + + fsmvalue = get_idx_reg(dev->regs, SCRATCH) & + (0xf << DEFECT7374_FSM_FIELD); + + if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ) + ep_info(dev, "%s: Defect 7374 FsmValue %08x\n", __func__, + fsmvalue); + else + writel(BIT(CLEAR_NAK_OUT_PACKETS_MODE) | + BIT(SET_EP_HIDE_STATUS_PHASE), + &dev->epregs[0].ep_rsp); + + /* + * hardware optionally handles a bunch of standard requests + * that the API hides from drivers anyway. have it do so. + * endpoint status/features are handled in software, to + * help pass tests for some dubious behavior. + */ + writel(BIT(SET_ISOCHRONOUS_DELAY) | + BIT(SET_SEL) | + BIT(SET_TEST_MODE) | + BIT(SET_ADDRESS) | + BIT(GET_INTERFACE_STATUS) | + BIT(GET_DEVICE_STATUS), + &dev->usb->stdrsp); + dev->wakeup_enable = 1; + writel(BIT(USB_ROOT_PORT_WAKEUP_ENABLE) | + (dev->softconnect << USB_DETECT_ENABLE) | + BIT(DEVICE_REMOTE_WAKEUP_ENABLE), + &dev->usb->usbctl); + + /* enable irqs so we can see ep0 and general operation */ + writel(BIT(SETUP_PACKET_INTERRUPT_ENABLE) | + BIT(ENDPOINT_0_INTERRUPT_ENABLE), + &dev->regs->pciirqenb0); + writel(BIT(PCI_INTERRUPT_ENABLE) | + BIT(ROOT_PORT_RESET_INTERRUPT_ENABLE) | + BIT(SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE) | + BIT(VBUS_INTERRUPT_ENABLE), + &dev->regs->pciirqenb1); + + /* don't leave any writes posted */ + (void)readl(&dev->usb->usbctl); +} + +static void ep0_start(struct net2280 *dev) +{ + if (dev->quirks & PLX_LEGACY) + return ep0_start_228x(dev); + return ep0_start_338x(dev); +} + +/* when a driver is successfully registered, it will receive + * control requests including set_configuration(), which enables + * non-control requests. then usb traffic follows until a + * disconnect is reported. then a host may connect again, or + * the driver might get unbound. + */ +static int net2280_start(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver) +{ + struct net2280 *dev; + int retval; + unsigned i; + + /* insist on high speed support from the driver, since + * (dev->usb->xcvrdiag & FORCE_FULL_SPEED_MODE) + * "must not be used in normal operation" + */ + if (!driver || driver->max_speed < USB_SPEED_HIGH || + !driver->setup) + return -EINVAL; + + dev = container_of(_gadget, struct net2280, gadget); + + for (i = 0; i < dev->n_ep; i++) + dev->ep[i].irqs = 0; + + /* hook up the driver ... */ + dev->softconnect = 1; + driver->driver.bus = NULL; + dev->driver = driver; + + retval = device_create_file(&dev->pdev->dev, &dev_attr_function); + if (retval) + goto err_unbind; + retval = device_create_file(&dev->pdev->dev, &dev_attr_queues); + if (retval) + goto err_func; + + /* Enable force-full-speed testing mode, if desired */ + if (full_speed && (dev->quirks & PLX_LEGACY)) + writel(BIT(FORCE_FULL_SPEED_MODE), &dev->usb->xcvrdiag); + + /* ... then enable host detection and ep0; and we're ready + * for set_configuration as well as eventual disconnect. + */ + net2280_led_active(dev, 1); + + if (dev->quirks & PLX_SUPERSPEED) + defect7374_enable_data_eps_zero(dev); + + ep0_start(dev); + + ep_dbg(dev, "%s ready, usbctl %08x stdrsp %08x\n", + driver->driver.name, + readl(&dev->usb->usbctl), + readl(&dev->usb->stdrsp)); + + /* pci writes may still be posted */ + return 0; + +err_func: + device_remove_file(&dev->pdev->dev, &dev_attr_function); +err_unbind: + dev->driver = NULL; + return retval; +} + +static void stop_activity(struct net2280 *dev, struct usb_gadget_driver *driver) +{ + int i; + + /* don't disconnect if it's not connected */ + if (dev->gadget.speed == USB_SPEED_UNKNOWN) + driver = NULL; + + /* stop hardware; prevent new request submissions; + * and kill any outstanding requests. + */ + usb_reset(dev); + for (i = 0; i < dev->n_ep; i++) + nuke(&dev->ep[i]); + + /* report disconnect; the driver is already quiesced */ + if (driver) { + spin_unlock(&dev->lock); + driver->disconnect(&dev->gadget); + spin_lock(&dev->lock); + } + + usb_reinit(dev); +} + +static int net2280_stop(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver) +{ + struct net2280 *dev; + unsigned long flags; + + dev = container_of(_gadget, struct net2280, gadget); + + spin_lock_irqsave(&dev->lock, flags); + stop_activity(dev, driver); + spin_unlock_irqrestore(&dev->lock, flags); + + dev->driver = NULL; + + net2280_led_active(dev, 0); + + /* Disable full-speed test mode */ + if (dev->quirks & PLX_LEGACY) + writel(0, &dev->usb->xcvrdiag); + + device_remove_file(&dev->pdev->dev, &dev_attr_function); + device_remove_file(&dev->pdev->dev, &dev_attr_queues); + + ep_dbg(dev, "unregistered driver '%s'\n", + driver ? driver->driver.name : ""); + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* handle ep0, ep-e, ep-f with 64 byte packets: packet per irq. + * also works for dma-capable endpoints, in pio mode or just + * to manually advance the queue after short OUT transfers. + */ +static void handle_ep_small(struct net2280_ep *ep) +{ + struct net2280_request *req; + u32 t; + /* 0 error, 1 mid-data, 2 done */ + int mode = 1; + + if (!list_empty(&ep->queue)) + req = list_entry(ep->queue.next, + struct net2280_request, queue); + else + req = NULL; + + /* ack all, and handle what we care about */ + t = readl(&ep->regs->ep_stat); + ep->irqs++; +#if 0 + ep_vdbg(ep->dev, "%s ack ep_stat %08x, req %p\n", + ep->ep.name, t, req ? &req->req : 0); +#endif + if (!ep->is_in || (ep->dev->quirks & PLX_2280)) + writel(t & ~BIT(NAK_OUT_PACKETS), &ep->regs->ep_stat); + else + /* Added for 2282 */ + writel(t, &ep->regs->ep_stat); + + /* for ep0, monitor token irqs to catch data stage length errors + * and to synchronize on status. + * + * also, to defer reporting of protocol stalls ... here's where + * data or status first appears, handling stalls here should never + * cause trouble on the host side.. + * + * control requests could be slightly faster without token synch for + * status, but status can jam up that way. + */ + if (unlikely(ep->num == 0)) { + if (ep->is_in) { + /* status; stop NAKing */ + if (t & BIT(DATA_OUT_PING_TOKEN_INTERRUPT)) { + if (ep->dev->protocol_stall) { + ep->stopped = 1; + set_halt(ep); + } + if (!req) + allow_status(ep); + mode = 2; + /* reply to extra IN data tokens with a zlp */ + } else if (t & BIT(DATA_IN_TOKEN_INTERRUPT)) { + if (ep->dev->protocol_stall) { + ep->stopped = 1; + set_halt(ep); + mode = 2; + } else if (ep->responded && + !req && !ep->stopped) + write_fifo(ep, NULL); + } + } else { + /* status; stop NAKing */ + if (t & BIT(DATA_IN_TOKEN_INTERRUPT)) { + if (ep->dev->protocol_stall) { + ep->stopped = 1; + set_halt(ep); + } + mode = 2; + /* an extra OUT token is an error */ + } else if (((t & BIT(DATA_OUT_PING_TOKEN_INTERRUPT)) && + req && + req->req.actual == req->req.length) || + (ep->responded && !req)) { + ep->dev->protocol_stall = 1; + set_halt(ep); + ep->stopped = 1; + if (req) + done(ep, req, -EOVERFLOW); + req = NULL; + } + } + } + + if (unlikely(!req)) + return; + + /* manual DMA queue advance after short OUT */ + if (likely(ep->dma)) { + if (t & BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT)) { + u32 count; + int stopped = ep->stopped; + + /* TRANSFERRED works around OUT_DONE erratum 0112. + * we expect (N <= maxpacket) bytes; host wrote M. + * iff (M < N) we won't ever see a DMA interrupt. + */ + ep->stopped = 1; + for (count = 0; ; t = readl(&ep->regs->ep_stat)) { + + /* any preceding dma transfers must finish. + * dma handles (M >= N), may empty the queue + */ + scan_dma_completions(ep); + if (unlikely(list_empty(&ep->queue) || + ep->out_overflow)) { + req = NULL; + break; + } + req = list_entry(ep->queue.next, + struct net2280_request, queue); + + /* here either (M < N), a "real" short rx; + * or (M == N) and the queue didn't empty + */ + if (likely(t & BIT(FIFO_EMPTY))) { + count = readl(&ep->dma->dmacount); + count &= DMA_BYTE_COUNT_MASK; + if (readl(&ep->dma->dmadesc) + != req->td_dma) + req = NULL; + break; + } + udelay(1); + } + + /* stop DMA, leave ep NAKing */ + writel(BIT(DMA_ABORT), &ep->dma->dmastat); + spin_stop_dma(ep->dma); + + if (likely(req)) { + req->td->dmacount = 0; + t = readl(&ep->regs->ep_avail); + dma_done(ep, req, count, + (ep->out_overflow || t) + ? -EOVERFLOW : 0); + } + + /* also flush to prevent erratum 0106 trouble */ + if (unlikely(ep->out_overflow || + (ep->dev->chiprev == 0x0100 && + ep->dev->gadget.speed + == USB_SPEED_FULL))) { + out_flush(ep); + ep->out_overflow = 0; + } + + /* (re)start dma if needed, stop NAKing */ + ep->stopped = stopped; + if (!list_empty(&ep->queue)) + restart_dma(ep); + } else + ep_dbg(ep->dev, "%s dma ep_stat %08x ??\n", + ep->ep.name, t); + return; + + /* data packet(s) received (in the fifo, OUT) */ + } else if (t & BIT(DATA_PACKET_RECEIVED_INTERRUPT)) { + if (read_fifo(ep, req) && ep->num != 0) + mode = 2; + + /* data packet(s) transmitted (IN) */ + } else if (t & BIT(DATA_PACKET_TRANSMITTED_INTERRUPT)) { + unsigned len; + + len = req->req.length - req->req.actual; + if (len > ep->ep.maxpacket) + len = ep->ep.maxpacket; + req->req.actual += len; + + /* if we wrote it all, we're usually done */ + /* send zlps until the status stage */ + if ((req->req.actual == req->req.length) && + (!req->req.zero || len != ep->ep.maxpacket) && ep->num) + mode = 2; + + /* there was nothing to do ... */ + } else if (mode == 1) + return; + + /* done */ + if (mode == 2) { + /* stream endpoints often resubmit/unlink in completion */ + done(ep, req, 0); + + /* maybe advance queue to next request */ + if (ep->num == 0) { + /* NOTE: net2280 could let gadget driver start the + * status stage later. since not all controllers let + * them control that, the api doesn't (yet) allow it. + */ + if (!ep->stopped) + allow_status(ep); + req = NULL; + } else { + if (!list_empty(&ep->queue) && !ep->stopped) + req = list_entry(ep->queue.next, + struct net2280_request, queue); + else + req = NULL; + if (req && !ep->is_in) + stop_out_naking(ep); + } + } + + /* is there a buffer for the next packet? + * for best streaming performance, make sure there is one. + */ + if (req && !ep->stopped) { + + /* load IN fifo with next packet (may be zlp) */ + if (t & BIT(DATA_PACKET_TRANSMITTED_INTERRUPT)) + write_fifo(ep, &req->req); + } +} + +static struct net2280_ep *get_ep_by_addr(struct net2280 *dev, u16 wIndex) +{ + struct net2280_ep *ep; + + if ((wIndex & USB_ENDPOINT_NUMBER_MASK) == 0) + return &dev->ep[0]; + list_for_each_entry(ep, &dev->gadget.ep_list, ep.ep_list) { + u8 bEndpointAddress; + + if (!ep->desc) + continue; + bEndpointAddress = ep->desc->bEndpointAddress; + if ((wIndex ^ bEndpointAddress) & USB_DIR_IN) + continue; + if ((wIndex & 0x0f) == (bEndpointAddress & 0x0f)) + return ep; + } + return NULL; +} + +static void defect7374_workaround(struct net2280 *dev, struct usb_ctrlrequest r) +{ + u32 scratch, fsmvalue; + u32 ack_wait_timeout, state; + + /* Workaround for Defect 7374 (U1/U2 erroneously rejected): */ + scratch = get_idx_reg(dev->regs, SCRATCH); + fsmvalue = scratch & (0xf << DEFECT7374_FSM_FIELD); + scratch &= ~(0xf << DEFECT7374_FSM_FIELD); + + if (!((fsmvalue == DEFECT7374_FSM_WAITING_FOR_CONTROL_READ) && + (r.bRequestType & USB_DIR_IN))) + return; + + /* This is the first Control Read for this connection: */ + if (!(readl(&dev->usb->usbstat) & BIT(SUPER_SPEED_MODE))) { + /* + * Connection is NOT SS: + * - Connection must be FS or HS. + * - This FSM state should allow workaround software to + * run after the next USB connection. + */ + scratch |= DEFECT7374_FSM_NON_SS_CONTROL_READ; + goto restore_data_eps; + } + + /* Connection is SS: */ + for (ack_wait_timeout = 0; + ack_wait_timeout < DEFECT_7374_NUMBEROF_MAX_WAIT_LOOPS; + ack_wait_timeout++) { + + state = readl(&dev->plregs->pl_ep_status_1) + & (0xff << STATE); + if ((state >= (ACK_GOOD_NORMAL << STATE)) && + (state <= (ACK_GOOD_MORE_ACKS_TO_COME << STATE))) { + scratch |= DEFECT7374_FSM_SS_CONTROL_READ; + break; + } + + /* + * We have not yet received host's Data Phase ACK + * - Wait and try again. + */ + udelay(DEFECT_7374_PROCESSOR_WAIT_TIME); + + continue; + } + + + if (ack_wait_timeout >= DEFECT_7374_NUMBEROF_MAX_WAIT_LOOPS) { + ep_err(dev, "FAIL: Defect 7374 workaround waited but failed " + "to detect SS host's data phase ACK."); + ep_err(dev, "PL_EP_STATUS_1(23:16):.Expected from 0x11 to 0x16" + "got 0x%2.2x.\n", state >> STATE); + } else { + ep_warn(dev, "INFO: Defect 7374 workaround waited about\n" + "%duSec for Control Read Data Phase ACK\n", + DEFECT_7374_PROCESSOR_WAIT_TIME * ack_wait_timeout); + } + +restore_data_eps: + /* + * Restore data EPs to their pre-workaround settings (disabled, + * initialized, and other details). + */ + defect7374_disable_data_eps(dev); + + set_idx_reg(dev->regs, SCRATCH, scratch); + + return; +} + +static void ep_stall(struct net2280_ep *ep, int stall) +{ + struct net2280 *dev = ep->dev; + u32 val; + static const u32 ep_pl[9] = { 0, 3, 4, 7, 8, 2, 5, 6, 9 }; + + if (stall) { + writel(BIT(SET_ENDPOINT_HALT) | + /* BIT(SET_NAK_PACKETS) | */ + BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE), + &ep->regs->ep_rsp); + ep->is_halt = 1; + } else { + if (dev->gadget.speed == USB_SPEED_SUPER) { + /* + * Workaround for SS SeqNum not cleared via + * Endpoint Halt (Clear) bit. select endpoint + */ + val = readl(&dev->plregs->pl_ep_ctrl); + val = (val & ~0x1f) | ep_pl[ep->num]; + writel(val, &dev->plregs->pl_ep_ctrl); + + val |= BIT(SEQUENCE_NUMBER_RESET); + writel(val, &dev->plregs->pl_ep_ctrl); + } + val = readl(&ep->regs->ep_rsp); + val |= BIT(CLEAR_ENDPOINT_HALT) | + BIT(CLEAR_ENDPOINT_TOGGLE); + writel(val, + /* | BIT(CLEAR_NAK_PACKETS),*/ + &ep->regs->ep_rsp); + ep->is_halt = 0; + val = readl(&ep->regs->ep_rsp); + } +} + +static void ep_stdrsp(struct net2280_ep *ep, int value, int wedged) +{ + /* set/clear, then synch memory views with the device */ + if (value) { + ep->stopped = 1; + if (ep->num == 0) + ep->dev->protocol_stall = 1; + else { + if (ep->dma) + ep_stop_dma(ep); + ep_stall(ep, true); + } + + if (wedged) + ep->wedged = 1; + } else { + ep->stopped = 0; + ep->wedged = 0; + + ep_stall(ep, false); + + /* Flush the queue */ + if (!list_empty(&ep->queue)) { + struct net2280_request *req = + list_entry(ep->queue.next, struct net2280_request, + queue); + if (ep->dma) + resume_dma(ep); + else { + if (ep->is_in) + write_fifo(ep, &req->req); + else { + if (read_fifo(ep, req)) + done(ep, req, 0); + } + } + } + } +} + +static void handle_stat0_irqs_superspeed(struct net2280 *dev, + struct net2280_ep *ep, struct usb_ctrlrequest r) +{ + int tmp = 0; + +#define w_value le16_to_cpu(r.wValue) +#define w_index le16_to_cpu(r.wIndex) +#define w_length le16_to_cpu(r.wLength) + + switch (r.bRequest) { + struct net2280_ep *e; + u16 status; + + case USB_REQ_SET_CONFIGURATION: + dev->addressed_state = !w_value; + goto usb3_delegate; + + case USB_REQ_GET_STATUS: + switch (r.bRequestType) { + case (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE): + status = dev->wakeup_enable ? 0x02 : 0x00; + if (dev->selfpowered) + status |= BIT(0); + status |= (dev->u1_enable << 2 | dev->u2_enable << 3 | + dev->ltm_enable << 4); + writel(0, &dev->epregs[0].ep_irqenb); + set_fifo_bytecount(ep, sizeof(status)); + writel((__force u32) status, &dev->epregs[0].ep_data); + allow_status_338x(ep); + break; + + case (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT): + e = get_ep_by_addr(dev, w_index); + if (!e) + goto do_stall3; + status = readl(&e->regs->ep_rsp) & + BIT(CLEAR_ENDPOINT_HALT); + writel(0, &dev->epregs[0].ep_irqenb); + set_fifo_bytecount(ep, sizeof(status)); + writel((__force u32) status, &dev->epregs[0].ep_data); + allow_status_338x(ep); + break; + + default: + goto usb3_delegate; + } + break; + + case USB_REQ_CLEAR_FEATURE: + switch (r.bRequestType) { + case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE): + if (!dev->addressed_state) { + switch (w_value) { + case USB_DEVICE_U1_ENABLE: + dev->u1_enable = 0; + writel(readl(&dev->usb_ext->usbctl2) & + ~BIT(U1_ENABLE), + &dev->usb_ext->usbctl2); + allow_status_338x(ep); + goto next_endpoints3; + + case USB_DEVICE_U2_ENABLE: + dev->u2_enable = 0; + writel(readl(&dev->usb_ext->usbctl2) & + ~BIT(U2_ENABLE), + &dev->usb_ext->usbctl2); + allow_status_338x(ep); + goto next_endpoints3; + + case USB_DEVICE_LTM_ENABLE: + dev->ltm_enable = 0; + writel(readl(&dev->usb_ext->usbctl2) & + ~BIT(LTM_ENABLE), + &dev->usb_ext->usbctl2); + allow_status_338x(ep); + goto next_endpoints3; + + default: + break; + } + } + if (w_value == USB_DEVICE_REMOTE_WAKEUP) { + dev->wakeup_enable = 0; + writel(readl(&dev->usb->usbctl) & + ~BIT(DEVICE_REMOTE_WAKEUP_ENABLE), + &dev->usb->usbctl); + allow_status_338x(ep); + break; + } + goto usb3_delegate; + + case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT): + e = get_ep_by_addr(dev, w_index); + if (!e) + goto do_stall3; + if (w_value != USB_ENDPOINT_HALT) + goto do_stall3; + ep_vdbg(dev, "%s clear halt\n", e->ep.name); + ep_stall(e, false); + if (!list_empty(&e->queue) && e->td_dma) + restart_dma(e); + allow_status(ep); + ep->stopped = 1; + break; + + default: + goto usb3_delegate; + } + break; + case USB_REQ_SET_FEATURE: + switch (r.bRequestType) { + case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE): + if (!dev->addressed_state) { + switch (w_value) { + case USB_DEVICE_U1_ENABLE: + dev->u1_enable = 1; + writel(readl(&dev->usb_ext->usbctl2) | + BIT(U1_ENABLE), + &dev->usb_ext->usbctl2); + allow_status_338x(ep); + goto next_endpoints3; + + case USB_DEVICE_U2_ENABLE: + dev->u2_enable = 1; + writel(readl(&dev->usb_ext->usbctl2) | + BIT(U2_ENABLE), + &dev->usb_ext->usbctl2); + allow_status_338x(ep); + goto next_endpoints3; + + case USB_DEVICE_LTM_ENABLE: + dev->ltm_enable = 1; + writel(readl(&dev->usb_ext->usbctl2) | + BIT(LTM_ENABLE), + &dev->usb_ext->usbctl2); + allow_status_338x(ep); + goto next_endpoints3; + default: + break; + } + } + + if (w_value == USB_DEVICE_REMOTE_WAKEUP) { + dev->wakeup_enable = 1; + writel(readl(&dev->usb->usbctl) | + BIT(DEVICE_REMOTE_WAKEUP_ENABLE), + &dev->usb->usbctl); + allow_status_338x(ep); + break; + } + goto usb3_delegate; + + case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT): + e = get_ep_by_addr(dev, w_index); + if (!e || (w_value != USB_ENDPOINT_HALT)) + goto do_stall3; + ep_stdrsp(e, true, false); + allow_status_338x(ep); + break; + + default: + goto usb3_delegate; + } + + break; + default: + +usb3_delegate: + ep_vdbg(dev, "setup %02x.%02x v%04x i%04x l%04x ep_cfg %08x\n", + r.bRequestType, r.bRequest, + w_value, w_index, w_length, + readl(&ep->cfg->ep_cfg)); + + ep->responded = 0; + spin_unlock(&dev->lock); + tmp = dev->driver->setup(&dev->gadget, &r); + spin_lock(&dev->lock); + } +do_stall3: + if (tmp < 0) { + ep_vdbg(dev, "req %02x.%02x protocol STALL; stat %d\n", + r.bRequestType, r.bRequest, tmp); + dev->protocol_stall = 1; + /* TD 9.9 Halt Endpoint test. TD 9.22 Set feature test */ + ep_stall(ep, true); + } + +next_endpoints3: + +#undef w_value +#undef w_index +#undef w_length + + return; +} + +static void handle_stat0_irqs(struct net2280 *dev, u32 stat) +{ + struct net2280_ep *ep; + u32 num, scratch; + + /* most of these don't need individual acks */ + stat &= ~BIT(INTA_ASSERTED); + if (!stat) + return; + /* ep_dbg(dev, "irqstat0 %04x\n", stat); */ + + /* starting a control request? */ + if (unlikely(stat & BIT(SETUP_PACKET_INTERRUPT))) { + union { + u32 raw[2]; + struct usb_ctrlrequest r; + } u; + int tmp; + struct net2280_request *req; + + if (dev->gadget.speed == USB_SPEED_UNKNOWN) { + u32 val = readl(&dev->usb->usbstat); + if (val & BIT(SUPER_SPEED)) { + dev->gadget.speed = USB_SPEED_SUPER; + usb_ep_set_maxpacket_limit(&dev->ep[0].ep, + EP0_SS_MAX_PACKET_SIZE); + } else if (val & BIT(HIGH_SPEED)) { + dev->gadget.speed = USB_SPEED_HIGH; + usb_ep_set_maxpacket_limit(&dev->ep[0].ep, + EP0_HS_MAX_PACKET_SIZE); + } else { + dev->gadget.speed = USB_SPEED_FULL; + usb_ep_set_maxpacket_limit(&dev->ep[0].ep, + EP0_HS_MAX_PACKET_SIZE); + } + net2280_led_speed(dev, dev->gadget.speed); + ep_dbg(dev, "%s\n", + usb_speed_string(dev->gadget.speed)); + } + + ep = &dev->ep[0]; + ep->irqs++; + + /* make sure any leftover request state is cleared */ + stat &= ~BIT(ENDPOINT_0_INTERRUPT); + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, + struct net2280_request, queue); + done(ep, req, (req->req.actual == req->req.length) + ? 0 : -EPROTO); + } + ep->stopped = 0; + dev->protocol_stall = 0; + if (dev->quirks & PLX_SUPERSPEED) + ep->is_halt = 0; + else{ + if (ep->dev->quirks & PLX_2280) + tmp = BIT(FIFO_OVERFLOW) | + BIT(FIFO_UNDERFLOW); + else + tmp = 0; + + writel(tmp | BIT(TIMEOUT) | + BIT(USB_STALL_SENT) | + BIT(USB_IN_NAK_SENT) | + BIT(USB_IN_ACK_RCVD) | + BIT(USB_OUT_PING_NAK_SENT) | + BIT(USB_OUT_ACK_SENT) | + BIT(SHORT_PACKET_OUT_DONE_INTERRUPT) | + BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT) | + BIT(DATA_PACKET_RECEIVED_INTERRUPT) | + BIT(DATA_PACKET_TRANSMITTED_INTERRUPT) | + BIT(DATA_OUT_PING_TOKEN_INTERRUPT) | + BIT(DATA_IN_TOKEN_INTERRUPT), + &ep->regs->ep_stat); + } + u.raw[0] = readl(&dev->usb->setup0123); + u.raw[1] = readl(&dev->usb->setup4567); + + cpu_to_le32s(&u.raw[0]); + cpu_to_le32s(&u.raw[1]); + + if (dev->quirks & PLX_SUPERSPEED) + defect7374_workaround(dev, u.r); + + tmp = 0; + +#define w_value le16_to_cpu(u.r.wValue) +#define w_index le16_to_cpu(u.r.wIndex) +#define w_length le16_to_cpu(u.r.wLength) + + /* ack the irq */ + writel(BIT(SETUP_PACKET_INTERRUPT), &dev->regs->irqstat0); + stat ^= BIT(SETUP_PACKET_INTERRUPT); + + /* watch control traffic at the token level, and force + * synchronization before letting the status stage happen. + * FIXME ignore tokens we'll NAK, until driver responds. + * that'll mean a lot less irqs for some drivers. + */ + ep->is_in = (u.r.bRequestType & USB_DIR_IN) != 0; + if (ep->is_in) { + scratch = BIT(DATA_PACKET_TRANSMITTED_INTERRUPT) | + BIT(DATA_OUT_PING_TOKEN_INTERRUPT) | + BIT(DATA_IN_TOKEN_INTERRUPT); + stop_out_naking(ep); + } else + scratch = BIT(DATA_PACKET_RECEIVED_INTERRUPT) | + BIT(DATA_OUT_PING_TOKEN_INTERRUPT) | + BIT(DATA_IN_TOKEN_INTERRUPT); + writel(scratch, &dev->epregs[0].ep_irqenb); + + /* we made the hardware handle most lowlevel requests; + * everything else goes uplevel to the gadget code. + */ + ep->responded = 1; + + if (dev->gadget.speed == USB_SPEED_SUPER) { + handle_stat0_irqs_superspeed(dev, ep, u.r); + goto next_endpoints; + } + + switch (u.r.bRequest) { + case USB_REQ_GET_STATUS: { + struct net2280_ep *e; + __le32 status; + + /* hw handles device and interface status */ + if (u.r.bRequestType != (USB_DIR_IN|USB_RECIP_ENDPOINT)) + goto delegate; + e = get_ep_by_addr(dev, w_index); + if (!e || w_length > 2) + goto do_stall; + + if (readl(&e->regs->ep_rsp) & BIT(SET_ENDPOINT_HALT)) + status = cpu_to_le32(1); + else + status = cpu_to_le32(0); + + /* don't bother with a request object! */ + writel(0, &dev->epregs[0].ep_irqenb); + set_fifo_bytecount(ep, w_length); + writel((__force u32)status, &dev->epregs[0].ep_data); + allow_status(ep); + ep_vdbg(dev, "%s stat %02x\n", ep->ep.name, status); + goto next_endpoints; + } + break; + case USB_REQ_CLEAR_FEATURE: { + struct net2280_ep *e; + + /* hw handles device features */ + if (u.r.bRequestType != USB_RECIP_ENDPOINT) + goto delegate; + if (w_value != USB_ENDPOINT_HALT || w_length != 0) + goto do_stall; + e = get_ep_by_addr(dev, w_index); + if (!e) + goto do_stall; + if (e->wedged) { + ep_vdbg(dev, "%s wedged, halt not cleared\n", + ep->ep.name); + } else { + ep_vdbg(dev, "%s clear halt\n", e->ep.name); + clear_halt(e); + if ((ep->dev->quirks & PLX_SUPERSPEED) && + !list_empty(&e->queue) && e->td_dma) + restart_dma(e); + } + allow_status(ep); + goto next_endpoints; + } + break; + case USB_REQ_SET_FEATURE: { + struct net2280_ep *e; + + /* hw handles device features */ + if (u.r.bRequestType != USB_RECIP_ENDPOINT) + goto delegate; + if (w_value != USB_ENDPOINT_HALT || w_length != 0) + goto do_stall; + e = get_ep_by_addr(dev, w_index); + if (!e) + goto do_stall; + if (e->ep.name == ep0name) + goto do_stall; + set_halt(e); + if ((dev->quirks & PLX_SUPERSPEED) && e->dma) + abort_dma(e); + allow_status(ep); + ep_vdbg(dev, "%s set halt\n", ep->ep.name); + goto next_endpoints; + } + break; + default: +delegate: + ep_vdbg(dev, "setup %02x.%02x v%04x i%04x l%04x " + "ep_cfg %08x\n", + u.r.bRequestType, u.r.bRequest, + w_value, w_index, w_length, + readl(&ep->cfg->ep_cfg)); + ep->responded = 0; + spin_unlock(&dev->lock); + tmp = dev->driver->setup(&dev->gadget, &u.r); + spin_lock(&dev->lock); + } + + /* stall ep0 on error */ + if (tmp < 0) { +do_stall: + ep_vdbg(dev, "req %02x.%02x protocol STALL; stat %d\n", + u.r.bRequestType, u.r.bRequest, tmp); + dev->protocol_stall = 1; + } + + /* some in/out token irq should follow; maybe stall then. + * driver must queue a request (even zlp) or halt ep0 + * before the host times out. + */ + } + +#undef w_value +#undef w_index +#undef w_length + +next_endpoints: + /* endpoint data irq ? */ + scratch = stat & 0x7f; + stat &= ~0x7f; + for (num = 0; scratch; num++) { + u32 t; + + /* do this endpoint's FIFO and queue need tending? */ + t = BIT(num); + if ((scratch & t) == 0) + continue; + scratch ^= t; + + ep = &dev->ep[num]; + handle_ep_small(ep); + } + + if (stat) + ep_dbg(dev, "unhandled irqstat0 %08x\n", stat); +} + +#define DMA_INTERRUPTS (BIT(DMA_D_INTERRUPT) | \ + BIT(DMA_C_INTERRUPT) | \ + BIT(DMA_B_INTERRUPT) | \ + BIT(DMA_A_INTERRUPT)) +#define PCI_ERROR_INTERRUPTS ( \ + BIT(PCI_MASTER_ABORT_RECEIVED_INTERRUPT) | \ + BIT(PCI_TARGET_ABORT_RECEIVED_INTERRUPT) | \ + BIT(PCI_RETRY_ABORT_INTERRUPT)) + +static void handle_stat1_irqs(struct net2280 *dev, u32 stat) +{ + struct net2280_ep *ep; + u32 tmp, num, mask, scratch; + + /* after disconnect there's nothing else to do! */ + tmp = BIT(VBUS_INTERRUPT) | BIT(ROOT_PORT_RESET_INTERRUPT); + mask = BIT(SUPER_SPEED) | BIT(HIGH_SPEED) | BIT(FULL_SPEED); + + /* VBUS disconnect is indicated by VBUS_PIN and VBUS_INTERRUPT set. + * Root Port Reset is indicated by ROOT_PORT_RESET_INTERRUPT set and + * both HIGH_SPEED and FULL_SPEED clear (as ROOT_PORT_RESET_INTERRUPT + * only indicates a change in the reset state). + */ + if (stat & tmp) { + writel(tmp, &dev->regs->irqstat1); + if ((((stat & BIT(ROOT_PORT_RESET_INTERRUPT)) && + (readl(&dev->usb->usbstat) & mask)) || + ((readl(&dev->usb->usbctl) & + BIT(VBUS_PIN)) == 0)) && + (dev->gadget.speed != USB_SPEED_UNKNOWN)) { + ep_dbg(dev, "disconnect %s\n", + dev->driver->driver.name); + stop_activity(dev, dev->driver); + ep0_start(dev); + return; + } + stat &= ~tmp; + + /* vBUS can bounce ... one of many reasons to ignore the + * notion of hotplug events on bus connect/disconnect! + */ + if (!stat) + return; + } + + /* NOTE: chip stays in PCI D0 state for now, but it could + * enter D1 to save more power + */ + tmp = BIT(SUSPEND_REQUEST_CHANGE_INTERRUPT); + if (stat & tmp) { + writel(tmp, &dev->regs->irqstat1); + if (stat & BIT(SUSPEND_REQUEST_INTERRUPT)) { + if (dev->driver->suspend) + dev->driver->suspend(&dev->gadget); + if (!enable_suspend) + stat &= ~BIT(SUSPEND_REQUEST_INTERRUPT); + } else { + if (dev->driver->resume) + dev->driver->resume(&dev->gadget); + /* at high speed, note erratum 0133 */ + } + stat &= ~tmp; + } + + /* clear any other status/irqs */ + if (stat) + writel(stat, &dev->regs->irqstat1); + + /* some status we can just ignore */ + if (dev->quirks & PLX_2280) + stat &= ~(BIT(CONTROL_STATUS_INTERRUPT) | + BIT(SUSPEND_REQUEST_INTERRUPT) | + BIT(RESUME_INTERRUPT) | + BIT(SOF_INTERRUPT)); + else + stat &= ~(BIT(CONTROL_STATUS_INTERRUPT) | + BIT(RESUME_INTERRUPT) | + BIT(SOF_DOWN_INTERRUPT) | + BIT(SOF_INTERRUPT)); + + if (!stat) + return; + /* ep_dbg(dev, "irqstat1 %08x\n", stat);*/ + + /* DMA status, for ep-{a,b,c,d} */ + scratch = stat & DMA_INTERRUPTS; + stat &= ~DMA_INTERRUPTS; + scratch >>= 9; + for (num = 0; scratch; num++) { + struct net2280_dma_regs __iomem *dma; + + tmp = BIT(num); + if ((tmp & scratch) == 0) + continue; + scratch ^= tmp; + + ep = &dev->ep[num + 1]; + dma = ep->dma; + + if (!dma) + continue; + + /* clear ep's dma status */ + tmp = readl(&dma->dmastat); + writel(tmp, &dma->dmastat); + + /* dma sync*/ + if (dev->quirks & PLX_SUPERSPEED) { + u32 r_dmacount = readl(&dma->dmacount); + if (!ep->is_in && (r_dmacount & 0x00FFFFFF) && + (tmp & BIT(DMA_TRANSACTION_DONE_INTERRUPT))) + continue; + } + + /* chaining should stop on abort, short OUT from fifo, + * or (stat0 codepath) short OUT transfer. + */ + if (!use_dma_chaining) { + if (!(tmp & BIT(DMA_TRANSACTION_DONE_INTERRUPT))) { + ep_dbg(ep->dev, "%s no xact done? %08x\n", + ep->ep.name, tmp); + continue; + } + stop_dma(ep->dma); + } + + /* OUT transfers terminate when the data from the + * host is in our memory. Process whatever's done. + * On this path, we know transfer's last packet wasn't + * less than req->length. NAK_OUT_PACKETS may be set, + * or the FIFO may already be holding new packets. + * + * IN transfers can linger in the FIFO for a very + * long time ... we ignore that for now, accounting + * precisely (like PIO does) needs per-packet irqs + */ + scan_dma_completions(ep); + + /* disable dma on inactive queues; else maybe restart */ + if (list_empty(&ep->queue)) { + if (use_dma_chaining) + stop_dma(ep->dma); + } else { + tmp = readl(&dma->dmactl); + if (!use_dma_chaining || (tmp & BIT(DMA_ENABLE)) == 0) + restart_dma(ep); + else if (ep->is_in && use_dma_chaining) { + struct net2280_request *req; + __le32 dmacount; + + /* the descriptor at the head of the chain + * may still have VALID_BIT clear; that's + * used to trigger changing DMA_FIFO_VALIDATE + * (affects automagic zlp writes). + */ + req = list_entry(ep->queue.next, + struct net2280_request, queue); + dmacount = req->td->dmacount; + dmacount &= cpu_to_le32(BIT(VALID_BIT) | + DMA_BYTE_COUNT_MASK); + if (dmacount && (dmacount & valid_bit) == 0) + restart_dma(ep); + } + } + ep->irqs++; + } + + /* NOTE: there are other PCI errors we might usefully notice. + * if they appear very often, here's where to try recovering. + */ + if (stat & PCI_ERROR_INTERRUPTS) { + ep_err(dev, "pci dma error; stat %08x\n", stat); + stat &= ~PCI_ERROR_INTERRUPTS; + /* these are fatal errors, but "maybe" they won't + * happen again ... + */ + stop_activity(dev, dev->driver); + ep0_start(dev); + stat = 0; + } + + if (stat) + ep_dbg(dev, "unhandled irqstat1 %08x\n", stat); +} + +static irqreturn_t net2280_irq(int irq, void *_dev) +{ + struct net2280 *dev = _dev; + + /* shared interrupt, not ours */ + if ((dev->quirks & PLX_LEGACY) && + (!(readl(&dev->regs->irqstat0) & BIT(INTA_ASSERTED)))) + return IRQ_NONE; + + spin_lock(&dev->lock); + + /* handle disconnect, dma, and more */ + handle_stat1_irqs(dev, readl(&dev->regs->irqstat1)); + + /* control requests and PIO */ + handle_stat0_irqs(dev, readl(&dev->regs->irqstat0)); + + if (dev->quirks & PLX_SUPERSPEED) { + /* re-enable interrupt to trigger any possible new interrupt */ + u32 pciirqenb1 = readl(&dev->regs->pciirqenb1); + writel(pciirqenb1 & 0x7FFFFFFF, &dev->regs->pciirqenb1); + writel(pciirqenb1, &dev->regs->pciirqenb1); + } + + spin_unlock(&dev->lock); + + return IRQ_HANDLED; +} + +/*-------------------------------------------------------------------------*/ + +static void gadget_release(struct device *_dev) +{ + struct net2280 *dev = dev_get_drvdata(_dev); + + kfree(dev); +} + +/* tear down the binding between this driver and the pci device */ + +static void net2280_remove(struct pci_dev *pdev) +{ + struct net2280 *dev = pci_get_drvdata(pdev); + + usb_del_gadget_udc(&dev->gadget); + + BUG_ON(dev->driver); + + /* then clean up the resources we allocated during probe() */ + net2280_led_shutdown(dev); + if (dev->requests) { + int i; + for (i = 1; i < 5; i++) { + if (!dev->ep[i].dummy) + continue; + pci_pool_free(dev->requests, dev->ep[i].dummy, + dev->ep[i].td_dma); + } + pci_pool_destroy(dev->requests); + } + if (dev->got_irq) + free_irq(pdev->irq, dev); + if (use_msi && dev->quirks & PLX_SUPERSPEED) + pci_disable_msi(pdev); + if (dev->regs) + iounmap(dev->regs); + if (dev->region) + release_mem_region(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + if (dev->enabled) + pci_disable_device(pdev); + device_remove_file(&pdev->dev, &dev_attr_registers); + + ep_info(dev, "unbind\n"); +} + +/* wrap this driver around the specified device, but + * don't respond over USB until a gadget driver binds to us. + */ + +static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct net2280 *dev; + unsigned long resource, len; + void __iomem *base = NULL; + int retval, i; + + if (!use_dma) + use_dma_chaining = 0; + + /* alloc, and start init */ + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + retval = -ENOMEM; + goto done; + } + + pci_set_drvdata(pdev, dev); + spin_lock_init(&dev->lock); + dev->quirks = id->driver_data; + dev->pdev = pdev; + dev->gadget.ops = &net2280_ops; + dev->gadget.max_speed = (dev->quirks & PLX_SUPERSPEED) ? + USB_SPEED_SUPER : USB_SPEED_HIGH; + + /* the "gadget" abstracts/virtualizes the controller */ + dev->gadget.name = driver_name; + + /* now all the pci goodies ... */ + if (pci_enable_device(pdev) < 0) { + retval = -ENODEV; + goto done; + } + dev->enabled = 1; + + /* BAR 0 holds all the registers + * BAR 1 is 8051 memory; unused here (note erratum 0103) + * BAR 2 is fifo memory; unused here + */ + resource = pci_resource_start(pdev, 0); + len = pci_resource_len(pdev, 0); + if (!request_mem_region(resource, len, driver_name)) { + ep_dbg(dev, "controller already in use\n"); + retval = -EBUSY; + goto done; + } + dev->region = 1; + + /* FIXME provide firmware download interface to put + * 8051 code into the chip, e.g. to turn on PCI PM. + */ + + base = ioremap_nocache(resource, len); + if (base == NULL) { + ep_dbg(dev, "can't map memory\n"); + retval = -EFAULT; + goto done; + } + dev->regs = (struct net2280_regs __iomem *) base; + dev->usb = (struct net2280_usb_regs __iomem *) (base + 0x0080); + dev->pci = (struct net2280_pci_regs __iomem *) (base + 0x0100); + dev->dma = (struct net2280_dma_regs __iomem *) (base + 0x0180); + dev->dep = (struct net2280_dep_regs __iomem *) (base + 0x0200); + dev->epregs = (struct net2280_ep_regs __iomem *) (base + 0x0300); + + if (dev->quirks & PLX_SUPERSPEED) { + u32 fsmvalue; + u32 usbstat; + dev->usb_ext = (struct usb338x_usb_ext_regs __iomem *) + (base + 0x00b4); + dev->fiforegs = (struct usb338x_fifo_regs __iomem *) + (base + 0x0500); + dev->llregs = (struct usb338x_ll_regs __iomem *) + (base + 0x0700); + dev->ll_lfps_regs = (struct usb338x_ll_lfps_regs __iomem *) + (base + 0x0748); + dev->ll_tsn_regs = (struct usb338x_ll_tsn_regs __iomem *) + (base + 0x077c); + dev->ll_chicken_reg = (struct usb338x_ll_chi_regs __iomem *) + (base + 0x079c); + dev->plregs = (struct usb338x_pl_regs __iomem *) + (base + 0x0800); + usbstat = readl(&dev->usb->usbstat); + dev->enhanced_mode = !!(usbstat & BIT(11)); + dev->n_ep = (dev->enhanced_mode) ? 9 : 5; + /* put into initial config, link up all endpoints */ + fsmvalue = get_idx_reg(dev->regs, SCRATCH) & + (0xf << DEFECT7374_FSM_FIELD); + /* See if firmware needs to set up for workaround: */ + if (fsmvalue == DEFECT7374_FSM_SS_CONTROL_READ) + writel(0, &dev->usb->usbctl); + } else{ + dev->enhanced_mode = 0; + dev->n_ep = 7; + /* put into initial config, link up all endpoints */ + writel(0, &dev->usb->usbctl); + } + + usb_reset(dev); + usb_reinit(dev); + + /* irq setup after old hardware is cleaned up */ + if (!pdev->irq) { + ep_err(dev, "No IRQ. Check PCI setup!\n"); + retval = -ENODEV; + goto done; + } + + if (use_msi && (dev->quirks & PLX_SUPERSPEED)) + if (pci_enable_msi(pdev)) + ep_err(dev, "Failed to enable MSI mode\n"); + + if (request_irq(pdev->irq, net2280_irq, IRQF_SHARED, + driver_name, dev)) { + ep_err(dev, "request interrupt %d failed\n", pdev->irq); + retval = -EBUSY; + goto done; + } + dev->got_irq = 1; + + /* DMA setup */ + /* NOTE: we know only the 32 LSBs of dma addresses may be nonzero */ + dev->requests = pci_pool_create("requests", pdev, + sizeof(struct net2280_dma), + 0 /* no alignment requirements */, + 0 /* or page-crossing issues */); + if (!dev->requests) { + ep_dbg(dev, "can't get request pool\n"); + retval = -ENOMEM; + goto done; + } + for (i = 1; i < 5; i++) { + struct net2280_dma *td; + + td = pci_pool_alloc(dev->requests, GFP_KERNEL, + &dev->ep[i].td_dma); + if (!td) { + ep_dbg(dev, "can't get dummy %d\n", i); + retval = -ENOMEM; + goto done; + } + td->dmacount = 0; /* not VALID */ + td->dmadesc = td->dmaaddr; + dev->ep[i].dummy = td; + } + + /* enable lower-overhead pci memory bursts during DMA */ + if (dev->quirks & PLX_LEGACY) + writel(BIT(DMA_MEMORY_WRITE_AND_INVALIDATE_ENABLE) | + /* + * 256 write retries may not be enough... + BIT(PCI_RETRY_ABORT_ENABLE) | + */ + BIT(DMA_READ_MULTIPLE_ENABLE) | + BIT(DMA_READ_LINE_ENABLE), + &dev->pci->pcimstctl); + /* erratum 0115 shouldn't appear: Linux inits PCI_LATENCY_TIMER */ + pci_set_master(pdev); + pci_try_set_mwi(pdev); + + /* ... also flushes any posted pci writes */ + dev->chiprev = get_idx_reg(dev->regs, REG_CHIPREV) & 0xffff; + + /* done */ + ep_info(dev, "%s\n", driver_desc); + ep_info(dev, "irq %d, pci mem %p, chip rev %04x\n", + pdev->irq, base, dev->chiprev); + ep_info(dev, "version: " DRIVER_VERSION "; dma %s %s\n", + use_dma ? (use_dma_chaining ? "chaining" : "enabled") + : "disabled", + dev->enhanced_mode ? "enhanced mode" : "legacy mode"); + retval = device_create_file(&pdev->dev, &dev_attr_registers); + if (retval) + goto done; + + retval = usb_add_gadget_udc_release(&pdev->dev, &dev->gadget, + gadget_release); + if (retval) + goto done; + return 0; + +done: + if (dev) + net2280_remove(pdev); + return retval; +} + +/* make sure the board is quiescent; otherwise it will continue + * generating IRQs across the upcoming reboot. + */ + +static void net2280_shutdown(struct pci_dev *pdev) +{ + struct net2280 *dev = pci_get_drvdata(pdev); + + /* disable IRQs */ + writel(0, &dev->regs->pciirqenb0); + writel(0, &dev->regs->pciirqenb1); + + /* disable the pullup so the host will think we're gone */ + writel(0, &dev->usb->usbctl); + + /* Disable full-speed test mode */ + if (dev->quirks & PLX_LEGACY) + writel(0, &dev->usb->xcvrdiag); +} + + +/*-------------------------------------------------------------------------*/ + +static const struct pci_device_id pci_ids[] = { { + .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), + .class_mask = ~0, + .vendor = PCI_VENDOR_ID_PLX_LEGACY, + .device = 0x2280, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = PLX_LEGACY | PLX_2280, + }, { + .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), + .class_mask = ~0, + .vendor = PCI_VENDOR_ID_PLX_LEGACY, + .device = 0x2282, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = PLX_LEGACY, + }, + { + .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), + .class_mask = ~0, + .vendor = PCI_VENDOR_ID_PLX, + .device = 0x3380, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = PLX_SUPERSPEED, + }, + { + .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), + .class_mask = ~0, + .vendor = PCI_VENDOR_ID_PLX, + .device = 0x3382, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = PLX_SUPERSPEED, + }, +{ /* end: all zeroes */ } +}; +MODULE_DEVICE_TABLE(pci, pci_ids); + +/* pci driver glue; this is a "new style" PCI driver module */ +static struct pci_driver net2280_pci_driver = { + .name = (char *) driver_name, + .id_table = pci_ids, + + .probe = net2280_probe, + .remove = net2280_remove, + .shutdown = net2280_shutdown, + + /* FIXME add power management support */ +}; + +module_pci_driver(net2280_pci_driver); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("David Brownell"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/udc/net2280.h b/drivers/usb/gadget/udc/net2280.h new file mode 100644 index 000000000000..03f15242d794 --- /dev/null +++ b/drivers/usb/gadget/udc/net2280.h @@ -0,0 +1,403 @@ +/* + * NetChip 2280 high/full speed USB device controller. + * Unlike many such controllers, this one talks PCI. + */ + +/* + * Copyright (C) 2002 NetChip Technology, Inc. (http://www.netchip.com) + * Copyright (C) 2003 David Brownell + * Copyright (C) 2014 Ricardo Ribalda - Qtechnology/AS + * + * 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/usb/net2280.h> +#include <linux/usb/usb338x.h> + +/*-------------------------------------------------------------------------*/ + +#ifdef __KERNEL__ + +/* indexed registers [11.10] are accessed indirectly + * caller must own the device lock. + */ + +static inline u32 get_idx_reg(struct net2280_regs __iomem *regs, u32 index) +{ + writel(index, ®s->idxaddr); + /* NOTE: synchs device/cpu memory views */ + return readl(®s->idxdata); +} + +static inline void +set_idx_reg(struct net2280_regs __iomem *regs, u32 index, u32 value) +{ + writel(index, ®s->idxaddr); + writel(value, ®s->idxdata); + /* posted, may not be visible yet */ +} + +#endif /* __KERNEL__ */ + +#define PCI_VENDOR_ID_PLX_LEGACY 0x17cc + +#define PLX_LEGACY BIT(0) +#define PLX_2280 BIT(1) +#define PLX_SUPERSPEED BIT(2) + +#define REG_DIAG 0x0 +#define RETRY_COUNTER 16 +#define FORCE_PCI_SERR 11 +#define FORCE_PCI_INTERRUPT 10 +#define FORCE_USB_INTERRUPT 9 +#define FORCE_CPU_INTERRUPT 8 +#define ILLEGAL_BYTE_ENABLES 5 +#define FAST_TIMES 4 +#define FORCE_RECEIVE_ERROR 2 +#define FORCE_TRANSMIT_CRC_ERROR 0 +#define REG_FRAME 0x02 /* from last sof */ +#define REG_CHIPREV 0x03 /* in bcd */ +#define REG_HS_NAK_RATE 0x0a /* NAK per N uframes */ + +#define CHIPREV_1 0x0100 +#define CHIPREV_1A 0x0110 + +/* DEFECT 7374 */ +#define DEFECT_7374_NUMBEROF_MAX_WAIT_LOOPS 200 +#define DEFECT_7374_PROCESSOR_WAIT_TIME 10 + +/* ep0 max packet size */ +#define EP0_SS_MAX_PACKET_SIZE 0x200 +#define EP0_HS_MAX_PACKET_SIZE 0x40 +#ifdef __KERNEL__ + +/*-------------------------------------------------------------------------*/ + +/* [8.3] for scatter/gather i/o + * use struct net2280_dma_regs bitfields + */ +struct net2280_dma { + __le32 dmacount; + __le32 dmaaddr; /* the buffer */ + __le32 dmadesc; /* next dma descriptor */ + __le32 _reserved; +} __aligned(16); + +/*-------------------------------------------------------------------------*/ + +/* DRIVER DATA STRUCTURES and UTILITIES */ + +struct net2280_ep { + struct usb_ep ep; + struct net2280_ep_regs __iomem *cfg; + struct net2280_ep_regs __iomem *regs; + struct net2280_dma_regs __iomem *dma; + struct net2280_dma *dummy; + struct usb338x_fifo_regs __iomem *fiforegs; + dma_addr_t td_dma; /* of dummy */ + struct net2280 *dev; + unsigned long irqs; + unsigned is_halt:1, dma_started:1; + + /* analogous to a host-side qh */ + struct list_head queue; + const struct usb_endpoint_descriptor *desc; + unsigned num : 8, + fifo_size : 12, + in_fifo_validate : 1, + out_overflow : 1, + stopped : 1, + wedged : 1, + is_in : 1, + is_iso : 1, + responded : 1; +}; + +static inline void allow_status(struct net2280_ep *ep) +{ + /* ep0 only */ + writel(BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) | + BIT(CLEAR_NAK_OUT_PACKETS) | + BIT(CLEAR_NAK_OUT_PACKETS_MODE), + &ep->regs->ep_rsp); + ep->stopped = 1; +} + +static void allow_status_338x(struct net2280_ep *ep) +{ + /* + * Control Status Phase Handshake was set by the chip when the setup + * packet arrived. While set, the chip automatically NAKs the host's + * Status Phase tokens. + */ + writel(BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE), &ep->regs->ep_rsp); + + ep->stopped = 1; + + /* TD 9.9 Halt Endpoint test. TD 9.22 set feature test. */ + ep->responded = 0; +} + +struct net2280_request { + struct usb_request req; + struct net2280_dma *td; + dma_addr_t td_dma; + struct list_head queue; + unsigned mapped : 1, + valid : 1; +}; + +struct net2280 { + /* each pci device provides one gadget, several endpoints */ + struct usb_gadget gadget; + spinlock_t lock; + struct net2280_ep ep[9]; + struct usb_gadget_driver *driver; + unsigned enabled : 1, + protocol_stall : 1, + softconnect : 1, + got_irq : 1, + region:1, + u1_enable:1, + u2_enable:1, + ltm_enable:1, + wakeup_enable:1, + selfpowered:1, + addressed_state:1; + u16 chiprev; + int enhanced_mode; + int n_ep; + kernel_ulong_t quirks; + + + /* pci state used to access those endpoints */ + struct pci_dev *pdev; + struct net2280_regs __iomem *regs; + struct net2280_usb_regs __iomem *usb; + struct usb338x_usb_ext_regs __iomem *usb_ext; + struct net2280_pci_regs __iomem *pci; + struct net2280_dma_regs __iomem *dma; + struct net2280_dep_regs __iomem *dep; + struct net2280_ep_regs __iomem *epregs; + struct usb338x_fifo_regs __iomem *fiforegs; + struct usb338x_ll_regs __iomem *llregs; + struct usb338x_ll_lfps_regs __iomem *ll_lfps_regs; + struct usb338x_ll_tsn_regs __iomem *ll_tsn_regs; + struct usb338x_ll_chi_regs __iomem *ll_chicken_reg; + struct usb338x_pl_regs __iomem *plregs; + + struct pci_pool *requests; + /* statistics...*/ +}; + +static inline void set_halt(struct net2280_ep *ep) +{ + /* ep0 and bulk/intr endpoints */ + writel(BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) | + /* set NAK_OUT for erratum 0114 */ + ((ep->dev->chiprev == CHIPREV_1) << SET_NAK_OUT_PACKETS) | + BIT(SET_ENDPOINT_HALT), + &ep->regs->ep_rsp); +} + +static inline void clear_halt(struct net2280_ep *ep) +{ + /* ep0 and bulk/intr endpoints */ + writel(BIT(CLEAR_ENDPOINT_HALT) | + BIT(CLEAR_ENDPOINT_TOGGLE) | + /* + * unless the gadget driver left a short packet in the + * fifo, this reverses the erratum 0114 workaround. + */ + ((ep->dev->chiprev == CHIPREV_1) << CLEAR_NAK_OUT_PACKETS), + &ep->regs->ep_rsp); +} + +/* + * FSM value for Defect 7374 (U1U2 Test) is managed in + * chip's SCRATCH register: + */ +#define DEFECT7374_FSM_FIELD 28 + +/* Waiting for Control Read: + * - A transition to this state indicates a fresh USB connection, + * before the first Setup Packet. The connection speed is not + * known. Firmware is waiting for the first Control Read. + * - Starting state: This state can be thought of as the FSM's typical + * starting state. + * - Tip: Upon the first SS Control Read the FSM never + * returns to this state. + */ +#define DEFECT7374_FSM_WAITING_FOR_CONTROL_READ BIT(DEFECT7374_FSM_FIELD) + +/* Non-SS Control Read: + * - A transition to this state indicates detection of the first HS + * or FS Control Read. + * - Tip: Upon the first SS Control Read the FSM never + * returns to this state. + */ +#define DEFECT7374_FSM_NON_SS_CONTROL_READ (2 << DEFECT7374_FSM_FIELD) + +/* SS Control Read: + * - A transition to this state indicates detection of the + * first SS Control Read. + * - This state indicates workaround completion. Workarounds no longer + * need to be applied (as long as the chip remains powered up). + * - Tip: Once in this state the FSM state does not change (until + * the chip's power is lost and restored). + * - This can be thought of as the final state of the FSM; + * the FSM 'locks-up' in this state until the chip loses power. + */ +#define DEFECT7374_FSM_SS_CONTROL_READ (3 << DEFECT7374_FSM_FIELD) + +#ifdef USE_RDK_LEDS + +static inline void net2280_led_init(struct net2280 *dev) +{ + /* LED3 (green) is on during USB activity. note erratum 0113. */ + writel(BIT(GPIO3_LED_SELECT) | + BIT(GPIO3_OUTPUT_ENABLE) | + BIT(GPIO2_OUTPUT_ENABLE) | + BIT(GPIO1_OUTPUT_ENABLE) | + BIT(GPIO0_OUTPUT_ENABLE), + &dev->regs->gpioctl); +} + +/* indicate speed with bi-color LED 0/1 */ +static inline +void net2280_led_speed(struct net2280 *dev, enum usb_device_speed speed) +{ + u32 val = readl(&dev->regs->gpioctl); + switch (speed) { + case USB_SPEED_SUPER: /* green + red */ + val |= BIT(GPIO0_DATA) | BIT(GPIO1_DATA); + break; + case USB_SPEED_HIGH: /* green */ + val &= ~BIT(GPIO0_DATA); + val |= BIT(GPIO1_DATA); + break; + case USB_SPEED_FULL: /* red */ + val &= ~BIT(GPIO1_DATA); + val |= BIT(GPIO0_DATA); + break; + default: /* (off/black) */ + val &= ~(BIT(GPIO1_DATA) | BIT(GPIO0_DATA)); + break; + } + writel(val, &dev->regs->gpioctl); +} + +/* indicate power with LED 2 */ +static inline void net2280_led_active(struct net2280 *dev, int is_active) +{ + u32 val = readl(&dev->regs->gpioctl); + + /* FIXME this LED never seems to turn on.*/ + if (is_active) + val |= GPIO2_DATA; + else + val &= ~GPIO2_DATA; + writel(val, &dev->regs->gpioctl); +} + +static inline void net2280_led_shutdown(struct net2280 *dev) +{ + /* turn off all four GPIO*_DATA bits */ + writel(readl(&dev->regs->gpioctl) & ~0x0f, + &dev->regs->gpioctl); +} + +#else + +#define net2280_led_init(dev) do { } while (0) +#define net2280_led_speed(dev, speed) do { } while (0) +#define net2280_led_shutdown(dev) do { } while (0) + +#endif + +/*-------------------------------------------------------------------------*/ + +#define ep_dbg(ndev, fmt, args...) \ + dev_dbg((&((ndev)->pdev->dev)), fmt, ##args) + +#define ep_vdbg(ndev, fmt, args...) \ + dev_vdbg((&((ndev)->pdev->dev)), fmt, ##args) + +#define ep_info(ndev, fmt, args...) \ + dev_info((&((ndev)->pdev->dev)), fmt, ##args) + +#define ep_warn(ndev, fmt, args...) \ + dev_warn((&((ndev)->pdev->dev)), fmt, ##args) + +#define ep_err(ndev, fmt, args...) \ + dev_err((&((ndev)->pdev->dev)), fmt, ##args) + +/*-------------------------------------------------------------------------*/ + +static inline void set_fifo_bytecount(struct net2280_ep *ep, unsigned count) +{ + if (ep->dev->pdev->vendor == 0x17cc) + writeb(count, 2 + (u8 __iomem *) &ep->regs->ep_cfg); + else{ + u32 tmp = readl(&ep->cfg->ep_cfg) & + (~(0x07 << EP_FIFO_BYTE_COUNT)); + writel(tmp | (count << EP_FIFO_BYTE_COUNT), &ep->cfg->ep_cfg); + } +} + +static inline void start_out_naking(struct net2280_ep *ep) +{ + /* NOTE: hardware races lurk here, and PING protocol issues */ + writel(BIT(SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp); + /* synch with device */ + readl(&ep->regs->ep_rsp); +} + +#ifdef DEBUG +static inline void assert_out_naking(struct net2280_ep *ep, const char *where) +{ + u32 tmp = readl(&ep->regs->ep_stat); + + if ((tmp & BIT(NAK_OUT_PACKETS)) == 0) { + ep_dbg(ep->dev, "%s %s %08x !NAK\n", + ep->ep.name, where, tmp); + writel(BIT(SET_NAK_OUT_PACKETS), + &ep->regs->ep_rsp); + } +} +#define ASSERT_OUT_NAKING(ep) assert_out_naking(ep, __func__) +#else +#define ASSERT_OUT_NAKING(ep) do {} while (0) +#endif + +static inline void stop_out_naking(struct net2280_ep *ep) +{ + u32 tmp; + + tmp = readl(&ep->regs->ep_stat); + if ((tmp & BIT(NAK_OUT_PACKETS)) != 0) + writel(BIT(CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp); +} + + +static inline void set_max_speed(struct net2280_ep *ep, u32 max) +{ + u32 reg; + static const u32 ep_enhanced[9] = { 0x10, 0x60, 0x30, 0x80, + 0x50, 0x20, 0x70, 0x40, 0x90 }; + + if (ep->dev->enhanced_mode) + reg = ep_enhanced[ep->num]; + else{ + reg = (ep->num + 1) * 0x10; + if (ep->dev->gadget.speed != USB_SPEED_HIGH) + reg += 1; + } + + set_idx_reg(ep->dev->regs, reg, max); +} + +#endif /* __KERNEL__ */ diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/udc/omap_udc.c index 2ae4f6d69f74..e731373fd4d7 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/udc/omap_udc.c @@ -2079,10 +2079,7 @@ static int omap_udc_start(struct usb_gadget *g, &udc->gadget); if (status < 0) { ERR("can't bind to transceiver\n"); - if (driver->unbind) { - driver->unbind(&udc->gadget); - udc->driver = NULL; - } + udc->driver = NULL; goto done; } } else { diff --git a/drivers/usb/gadget/omap_udc.h b/drivers/usb/gadget/udc/omap_udc.h index cfadeb5fc5de..cfadeb5fc5de 100644 --- a/drivers/usb/gadget/omap_udc.h +++ b/drivers/usb/gadget/udc/omap_udc.h diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c index eb8c3bedb57a..eb8c3bedb57a 100644 --- a/drivers/usb/gadget/pch_udc.c +++ b/drivers/usb/gadget/udc/pch_udc.c diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/udc/pxa25x_udc.c index 9984437d2952..251e4d5ee152 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/udc/pxa25x_udc.c @@ -16,6 +16,7 @@ /* #define VERBOSE_DEBUG */ #include <linux/device.h> +#include <linux/gpio.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/ioport.h> @@ -40,7 +41,6 @@ #include <asm/byteorder.h> #include <asm/dma.h> -#include <asm/gpio.h> #include <asm/mach-types.h> #include <asm/unaligned.h> @@ -2105,11 +2105,9 @@ static int pxa25x_udc_probe(struct platform_device *pdev) if (irq < 0) return -ENODEV; - dev->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(dev->clk)) { - retval = PTR_ERR(dev->clk); - goto err_clk; - } + dev->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(dev->clk)) + return PTR_ERR(dev->clk); pr_debug("%s: IRQ %d%s%s\n", driver_name, irq, dev->has_cfr ? "" : " (!cfr)", @@ -2120,15 +2118,16 @@ static int pxa25x_udc_probe(struct platform_device *pdev) dev->dev = &pdev->dev; dev->mach = dev_get_platdata(&pdev->dev); - dev->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); + dev->transceiver = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); if (gpio_is_valid(dev->mach->gpio_pullup)) { - if ((retval = gpio_request(dev->mach->gpio_pullup, - "pca25x_udc GPIO PULLUP"))) { + retval = devm_gpio_request(&pdev->dev, dev->mach->gpio_pullup, + "pca25x_udc GPIO PULLUP"); + if (retval) { dev_dbg(&pdev->dev, "can't get pullup gpio %d, err: %d\n", dev->mach->gpio_pullup, retval); - goto err_gpio_pullup; + goto err; } gpio_direction_output(dev->mach->gpio_pullup, 0); } @@ -2146,30 +2145,32 @@ static int pxa25x_udc_probe(struct platform_device *pdev) dev->vbus = 0; /* irq setup after old hardware state is cleaned up */ - retval = request_irq(irq, pxa25x_udc_irq, - 0, driver_name, dev); + retval = devm_request_irq(&pdev->dev, irq, pxa25x_udc_irq, 0, + driver_name, dev); if (retval != 0) { pr_err("%s: can't get irq %d, err %d\n", driver_name, irq, retval); - goto err_irq1; + goto err; } dev->got_irq = 1; #ifdef CONFIG_ARCH_LUBBOCK if (machine_is_lubbock()) { - retval = request_irq(LUBBOCK_USB_DISC_IRQ, lubbock_vbus_irq, - 0, driver_name, dev); + retval = devm_request_irq(&pdev->dev, LUBBOCK_USB_DISC_IRQ, + lubbock_vbus_irq, 0, driver_name, + dev); if (retval != 0) { pr_err("%s: can't get irq %i, err %d\n", driver_name, LUBBOCK_USB_DISC_IRQ, retval); - goto err_irq_lub; + goto err; } - retval = request_irq(LUBBOCK_USB_IRQ, lubbock_vbus_irq, - 0, driver_name, dev); + retval = devm_request_irq(&pdev->dev, LUBBOCK_USB_IRQ, + lubbock_vbus_irq, 0, driver_name, + dev); if (retval != 0) { pr_err("%s: can't get irq %i, err %d\n", driver_name, LUBBOCK_USB_IRQ, retval); - goto lubbock_fail0; + goto err; } } else #endif @@ -2180,22 +2181,9 @@ static int pxa25x_udc_probe(struct platform_device *pdev) return retval; remove_debug_files(dev); -#ifdef CONFIG_ARCH_LUBBOCK -lubbock_fail0: - free_irq(LUBBOCK_USB_DISC_IRQ, dev); - err_irq_lub: - free_irq(irq, dev); -#endif - err_irq1: - if (gpio_is_valid(dev->mach->gpio_pullup)) - gpio_free(dev->mach->gpio_pullup); - err_gpio_pullup: - if (!IS_ERR_OR_NULL(dev->transceiver)) { - usb_put_phy(dev->transceiver); + err: + if (!IS_ERR_OR_NULL(dev->transceiver)) dev->transceiver = NULL; - } - clk_put(dev->clk); - err_clk: return retval; } @@ -2217,25 +2205,8 @@ static int pxa25x_udc_remove(struct platform_device *pdev) remove_debug_files(dev); - if (dev->got_irq) { - free_irq(platform_get_irq(pdev, 0), dev); - dev->got_irq = 0; - } -#ifdef CONFIG_ARCH_LUBBOCK - if (machine_is_lubbock()) { - free_irq(LUBBOCK_USB_DISC_IRQ, dev); - free_irq(LUBBOCK_USB_IRQ, dev); - } -#endif - if (gpio_is_valid(dev->mach->gpio_pullup)) - gpio_free(dev->mach->gpio_pullup); - - clk_put(dev->clk); - - if (!IS_ERR_OR_NULL(dev->transceiver)) { - usb_put_phy(dev->transceiver); + if (!IS_ERR_OR_NULL(dev->transceiver)) dev->transceiver = NULL; - } the_controller = NULL; return 0; diff --git a/drivers/usb/gadget/pxa25x_udc.h b/drivers/usb/gadget/udc/pxa25x_udc.h index 3fe5931dc21a..3fe5931dc21a 100644 --- a/drivers/usb/gadget/pxa25x_udc.h +++ b/drivers/usb/gadget/udc/pxa25x_udc.h diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c index cdf4d678be96..597d39f89420 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/udc/pxa27x_udc.c @@ -2446,6 +2446,9 @@ static int pxa_udc_probe(struct platform_device *pdev) retval = PTR_ERR(udc->clk); goto err_clk; } + retval = clk_prepare(udc->clk); + if (retval) + goto err_clk_prepare; retval = -ENOMEM; udc->regs = ioremap(regs->start, resource_size(regs)); @@ -2483,6 +2486,8 @@ err_add_udc: err_irq: iounmap(udc->regs); err_map: + clk_unprepare(udc->clk); +err_clk_prepare: clk_put(udc->clk); udc->clk = NULL; err_clk: @@ -2509,6 +2514,7 @@ static int pxa_udc_remove(struct platform_device *_dev) udc->transceiver = NULL; the_controller = NULL; + clk_unprepare(udc->clk); clk_put(udc->clk); iounmap(udc->regs); diff --git a/drivers/usb/gadget/pxa27x_udc.h b/drivers/usb/gadget/udc/pxa27x_udc.h index 28f2b53530f5..28f2b53530f5 100644 --- a/drivers/usb/gadget/pxa27x_udc.h +++ b/drivers/usb/gadget/udc/pxa27x_udc.h diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c index b698a490cc7d..46008421c1ec 100644 --- a/drivers/usb/gadget/r8a66597-udc.c +++ b/drivers/usb/gadget/udc/r8a66597-udc.c @@ -1826,18 +1826,12 @@ static int __exit r8a66597_remove(struct platform_device *pdev) usb_del_gadget_udc(&r8a66597->gadget); del_timer_sync(&r8a66597->timer); - iounmap(r8a66597->reg); - if (r8a66597->pdata->sudmac) - iounmap(r8a66597->sudmac_reg); - free_irq(platform_get_irq(pdev, 0), r8a66597); r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req); if (r8a66597->pdata->on_chip) { clk_disable_unprepare(r8a66597->clk); - clk_put(r8a66597->clk); } - kfree(r8a66597); return 0; } @@ -1845,28 +1839,24 @@ static void nop_completion(struct usb_ep *ep, struct usb_request *r) { } -static int __init r8a66597_sudmac_ioremap(struct r8a66597 *r8a66597, +static int r8a66597_sudmac_ioremap(struct r8a66597 *r8a66597, struct platform_device *pdev) { struct resource *res; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sudmac"); - if (!res) { - dev_err(&pdev->dev, "platform_get_resource error(sudmac).\n"); - return -ENODEV; - } - - r8a66597->sudmac_reg = ioremap(res->start, resource_size(res)); - if (r8a66597->sudmac_reg == NULL) { + r8a66597->sudmac_reg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(r8a66597->sudmac_reg)) { dev_err(&pdev->dev, "ioremap error(sudmac).\n"); - return -ENOMEM; + return PTR_ERR(r8a66597->sudmac_reg); } return 0; } -static int __init r8a66597_probe(struct platform_device *pdev) +static int r8a66597_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; char clk_name[8]; struct resource *res, *ires; int irq; @@ -1877,39 +1867,27 @@ static int __init r8a66597_probe(struct platform_device *pdev) unsigned long irq_trigger; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - ret = -ENODEV; - dev_err(&pdev->dev, "platform_get_resource error.\n"); - goto clean_up; - } + reg = devm_ioremap_resource(&pdev->dev, res); + if (!reg) + return -ENODEV; ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0); irq = ires->start; irq_trigger = ires->flags & IRQF_TRIGGER_MASK; if (irq < 0) { - ret = -ENODEV; - dev_err(&pdev->dev, "platform_get_irq error.\n"); - goto clean_up; - } - - reg = ioremap(res->start, resource_size(res)); - if (reg == NULL) { - ret = -ENOMEM; - dev_err(&pdev->dev, "ioremap error.\n"); - goto clean_up; + dev_err(dev, "platform_get_irq error.\n"); + return -ENODEV; } /* initialize ucd */ - r8a66597 = kzalloc(sizeof(struct r8a66597), GFP_KERNEL); - if (r8a66597 == NULL) { - ret = -ENOMEM; - goto clean_up; - } + r8a66597 = devm_kzalloc(dev, sizeof(struct r8a66597), GFP_KERNEL); + if (r8a66597 == NULL) + return -ENOMEM; spin_lock_init(&r8a66597->lock); platform_set_drvdata(pdev, r8a66597); - r8a66597->pdata = dev_get_platdata(&pdev->dev); + r8a66597->pdata = dev_get_platdata(dev); r8a66597->irq_sense_low = irq_trigger == IRQF_TRIGGER_LOW; r8a66597->gadget.ops = &r8a66597_gadget_ops; @@ -1923,12 +1901,10 @@ static int __init r8a66597_probe(struct platform_device *pdev) if (r8a66597->pdata->on_chip) { snprintf(clk_name, sizeof(clk_name), "usb%d", pdev->id); - r8a66597->clk = clk_get(&pdev->dev, clk_name); + r8a66597->clk = devm_clk_get(dev, clk_name); if (IS_ERR(r8a66597->clk)) { - dev_err(&pdev->dev, "cannot get clock \"%s\"\n", - clk_name); - ret = PTR_ERR(r8a66597->clk); - goto clean_up; + dev_err(dev, "cannot get clock \"%s\"\n", clk_name); + return PTR_ERR(r8a66597->clk); } clk_prepare_enable(r8a66597->clk); } @@ -1941,10 +1917,10 @@ static int __init r8a66597_probe(struct platform_device *pdev) disable_controller(r8a66597); /* make sure controller is disabled */ - ret = request_irq(irq, r8a66597_irq, IRQF_SHARED, - udc_name, r8a66597); + ret = devm_request_irq(dev, irq, r8a66597_irq, IRQF_SHARED, + udc_name, r8a66597); if (ret < 0) { - dev_err(&pdev->dev, "request_irq error (%d)\n", ret); + dev_err(dev, "request_irq error (%d)\n", ret); goto clean_up2; } @@ -1978,37 +1954,25 @@ static int __init r8a66597_probe(struct platform_device *pdev) GFP_KERNEL); if (r8a66597->ep0_req == NULL) { ret = -ENOMEM; - goto clean_up3; + goto clean_up2; } r8a66597->ep0_req->complete = nop_completion; - ret = usb_add_gadget_udc(&pdev->dev, &r8a66597->gadget); + ret = usb_add_gadget_udc(dev, &r8a66597->gadget); if (ret) goto err_add_udc; - dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION); + dev_info(dev, "version %s\n", DRIVER_VERSION); return 0; err_add_udc: r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req); -clean_up3: - free_irq(irq, r8a66597); clean_up2: - if (r8a66597->pdata->on_chip) { + if (r8a66597->pdata->on_chip) clk_disable_unprepare(r8a66597->clk); - clk_put(r8a66597->clk); - } -clean_up: - if (r8a66597) { - if (r8a66597->sudmac_reg) - iounmap(r8a66597->sudmac_reg); - if (r8a66597->ep0_req) - r8a66597_free_request(&r8a66597->ep[0].ep, - r8a66597->ep0_req); - kfree(r8a66597); - } - if (reg) - iounmap(reg); + + if (r8a66597->ep0_req) + r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req); return ret; } diff --git a/drivers/usb/gadget/r8a66597-udc.h b/drivers/usb/gadget/udc/r8a66597-udc.h index 45c4b2df1785..45c4b2df1785 100644 --- a/drivers/usb/gadget/r8a66597-udc.h +++ b/drivers/usb/gadget/udc/r8a66597-udc.h diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/udc/s3c-hsudc.c index 10c6a128250c..10c6a128250c 100644 --- a/drivers/usb/gadget/s3c-hsudc.c +++ b/drivers/usb/gadget/udc/s3c-hsudc.c diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/udc/s3c2410_udc.c index 7987aa049fab..357b58e0087b 100644 --- a/drivers/usb/gadget/s3c2410_udc.c +++ b/drivers/usb/gadget/udc/s3c2410_udc.c @@ -1788,7 +1788,7 @@ static int s3c2410_udc_probe(struct platform_device *pdev) return PTR_ERR(usb_bus_clock); } - clk_enable(usb_bus_clock); + clk_prepare_enable(usb_bus_clock); udc_clock = clk_get(NULL, "usb-device"); if (IS_ERR(udc_clock)) { @@ -1796,7 +1796,7 @@ static int s3c2410_udc_probe(struct platform_device *pdev) return PTR_ERR(udc_clock); } - clk_enable(udc_clock); + clk_prepare_enable(udc_clock); mdelay(10); @@ -1952,13 +1952,13 @@ static int s3c2410_udc_remove(struct platform_device *pdev) release_mem_region(rsrc_start, rsrc_len); if (!IS_ERR(udc_clock) && udc_clock != NULL) { - clk_disable(udc_clock); + clk_disable_unprepare(udc_clock); clk_put(udc_clock); udc_clock = NULL; } if (!IS_ERR(usb_bus_clock) && usb_bus_clock != NULL) { - clk_disable(usb_bus_clock); + clk_disable_unprepare(usb_bus_clock); clk_put(usb_bus_clock); usb_bus_clock = NULL; } diff --git a/drivers/usb/gadget/s3c2410_udc.h b/drivers/usb/gadget/udc/s3c2410_udc.h index 93bf225f1969..93bf225f1969 100644 --- a/drivers/usb/gadget/s3c2410_udc.h +++ b/drivers/usb/gadget/udc/s3c2410_udc.h diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc/udc-core.c index b0d98172bc07..b0d98172bc07 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc/udc-core.c diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 03314f861bee..82800a775501 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -37,6 +37,14 @@ config USB_XHCI_MVEBU Say 'Y' to enable the support for the xHCI host controller found in Marvell Armada 375/38x ARM SOCs. +config USB_XHCI_RCAR + tristate "xHCI support for Renesas R-Car SoCs" + select USB_XHCI_PLATFORM + depends on ARCH_SHMOBILE || COMPILE_TEST + ---help--- + Say 'Y' to enable the support for the xHCI host controller + found in Renesas R-Car ARM SoCs. + endif # USB_XHCI_HCD config USB_EHCI_HCD diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index af89a903d97e..144c038ef70f 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -22,6 +22,9 @@ ifneq ($(CONFIG_USB_XHCI_PLATFORM), ) ifneq ($(CONFIG_USB_XHCI_MVEBU), ) xhci-hcd-y += xhci-mvebu.o endif +ifneq ($(CONFIG_USB_XHCI_RCAR), ) + xhci-hcd-y += xhci-rcar.o +endif endif obj-$(CONFIG_USB_WHCI_HCD) += whci/ diff --git a/drivers/usb/host/ehci-exynos.c b/drivers/usb/host/ehci-exynos.c index d1c76216350f..cda0a2f5c467 100644 --- a/drivers/usb/host/ehci-exynos.c +++ b/drivers/usb/host/ehci-exynos.c @@ -88,7 +88,7 @@ static int exynos_ehci_get_phy(struct device *dev, return -EINVAL; } - phy = devm_of_phy_get(dev, child, 0); + phy = devm_of_phy_get(dev, child, NULL); of_node_put(child); if (IS_ERR(phy)) { ret = PTR_ERR(phy); diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c index c0fb6a8ae6a3..b6205fac3567 100644 --- a/drivers/usb/host/ehci-mem.c +++ b/drivers/usb/host/ehci-mem.c @@ -209,7 +209,7 @@ static int ehci_mem_init (struct ehci_hcd *ehci, gfp_t flags) ehci->periodic = (__le32 *) dma_alloc_coherent (ehci_to_hcd(ehci)->self.controller, ehci->periodic_size * sizeof(__le32), - &ehci->periodic_dma, 0); + &ehci->periodic_dma, flags); if (ehci->periodic == NULL) { goto fail; } diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c index 982c09bebe0f..934b39d5e44a 100644 --- a/drivers/usb/host/ehci-msm.c +++ b/drivers/usb/host/ehci-msm.c @@ -190,7 +190,7 @@ static const struct dev_pm_ops ehci_msm_dev_pm_ops = { .resume = ehci_msm_pm_resume, }; -static struct of_device_id msm_ehci_dt_match[] = { +static const struct of_device_id msm_ehci_dt_match[] = { { .compatible = "qcom,ehci-host", }, {} }; diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 3e86bf4371b3..ca7b964124af 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -35,6 +35,21 @@ static const char hcd_name[] = "ehci-pci"; #define PCI_DEVICE_ID_INTEL_CE4100_USB 0x2e70 /*-------------------------------------------------------------------------*/ +#define PCI_DEVICE_ID_INTEL_QUARK_X1000_SOC 0x0939 +static inline bool is_intel_quark_x1000(struct pci_dev *pdev) +{ + return pdev->vendor == PCI_VENDOR_ID_INTEL && + pdev->device == PCI_DEVICE_ID_INTEL_QUARK_X1000_SOC; +} + +/* + * 0x84 is the offset of in/out threshold register, + * and it is the same offset as the register of 'hostpc'. + */ +#define intel_quark_x1000_insnreg01 hostpc + +/* Maximum usable threshold value is 0x7f dwords for both IN and OUT */ +#define INTEL_QUARK_X1000_EHCI_MAX_THRESHOLD 0x007f007f /* called after powerup, by probe or system-pm "wakeup" */ static int ehci_pci_reinit(struct ehci_hcd *ehci, struct pci_dev *pdev) @@ -50,6 +65,16 @@ static int ehci_pci_reinit(struct ehci_hcd *ehci, struct pci_dev *pdev) if (!retval) ehci_dbg(ehci, "MWI active\n"); + /* Reset the threshold limit */ + if (is_intel_quark_x1000(pdev)) { + /* + * For the Intel QUARK X1000, raise the I/O threshold to the + * maximum usable value in order to improve performance. + */ + ehci_writel(ehci, INTEL_QUARK_X1000_EHCI_MAX_THRESHOLD, + ehci->regs->intel_quark_x1000_insnreg01); + } + return 0; } diff --git a/drivers/usb/host/ehci-spear.c b/drivers/usb/host/ehci-spear.c index 1d59958ad0ce..1355ff0946b9 100644 --- a/drivers/usb/host/ehci-spear.c +++ b/drivers/usb/host/ehci-spear.c @@ -150,7 +150,7 @@ static int spear_ehci_hcd_drv_remove(struct platform_device *pdev) return 0; } -static struct of_device_id spear_ehci_id_table[] = { +static const struct of_device_id spear_ehci_id_table[] = { { .compatible = "st,spear600-ehci", }, { }, }; diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 6fdcb8ad2296..7aafb05e7a40 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -46,6 +46,7 @@ #define DRV_NAME "tegra-ehci" static struct hc_driver __read_mostly tegra_ehci_hc_driver; +static bool usb1_reset_attempted; struct tegra_ehci_soc_config { bool has_hostpc; @@ -60,6 +61,61 @@ struct tegra_ehci_hcd { enum tegra_usb_phy_port_speed port_speed; }; +/* + * The 1st USB controller contains some UTMI pad registers that are global for + * all the controllers on the chip. Those registers are also cleared when + * reset is asserted to the 1st controller. This means that the 1st controller + * can only be reset when no other controlled has finished probing. So we'll + * reset the 1st controller before doing any other setup on any of the + * controllers, and then never again. + * + * Since this is a PHY issue, the Tegra PHY driver should probably be doing + * the resetting of the USB controllers. But to keep compatibility with old + * device trees that don't have reset phandles in the PHYs, do it here. + * Those old DTs will be vulnerable to total USB breakage if the 1st EHCI + * device isn't the first one to finish probing, so warn them. + */ +static int tegra_reset_usb_controller(struct platform_device *pdev) +{ + struct device_node *phy_np; + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct tegra_ehci_hcd *tegra = + (struct tegra_ehci_hcd *)hcd_to_ehci(hcd)->priv; + + phy_np = of_parse_phandle(pdev->dev.of_node, "nvidia,phy", 0); + if (!phy_np) + return -ENOENT; + + if (!usb1_reset_attempted) { + struct reset_control *usb1_reset; + + usb1_reset = of_reset_control_get(phy_np, "usb"); + if (IS_ERR(usb1_reset)) { + dev_warn(&pdev->dev, + "can't get utmi-pads reset from the PHY\n"); + dev_warn(&pdev->dev, + "continuing, but please update your DT\n"); + } else { + reset_control_assert(usb1_reset); + udelay(1); + reset_control_deassert(usb1_reset); + } + + reset_control_put(usb1_reset); + usb1_reset_attempted = true; + } + + if (!of_property_read_bool(phy_np, "nvidia,has-utmi-pad-registers")) { + reset_control_assert(tegra->rst); + udelay(1); + reset_control_deassert(tegra->rst); + } + + of_node_put(phy_np); + + return 0; +} + static int tegra_ehci_internal_port_reset( struct ehci_hcd *ehci, u32 __iomem *portsc_reg @@ -326,7 +382,7 @@ static const struct tegra_ehci_soc_config tegra20_soc_config = { .has_hostpc = false, }; -static struct of_device_id tegra_ehci_of_match[] = { +static const struct of_device_id tegra_ehci_of_match[] = { { .compatible = "nvidia,tegra30-ehci", .data = &tegra30_soc_config }, { .compatible = "nvidia,tegra20-ehci", .data = &tegra20_soc_config }, { }, @@ -389,9 +445,9 @@ static int tegra_ehci_probe(struct platform_device *pdev) if (err) goto cleanup_hcd_create; - reset_control_assert(tegra->rst); - udelay(1); - reset_control_deassert(tegra->rst); + err = tegra_reset_usb_controller(pdev); + if (err) + goto cleanup_clk_en; u_phy = devm_usb_get_phy_by_phandle(&pdev->dev, "nvidia,phy", 0); if (IS_ERR(u_phy)) { @@ -479,10 +535,11 @@ static int tegra_ehci_remove(struct platform_device *pdev) usb_phy_shutdown(hcd->phy); usb_remove_hcd(hcd); - usb_put_hcd(hcd); clk_disable_unprepare(tegra->clk); + usb_put_hcd(hcd); + return 0; } diff --git a/drivers/usb/host/fhci-dbg.c b/drivers/usb/host/fhci-dbg.c index f238cb37305c..b58e7a60913a 100644 --- a/drivers/usb/host/fhci-dbg.c +++ b/drivers/usb/host/fhci-dbg.c @@ -129,11 +129,7 @@ void fhci_dfs_destroy(struct fhci_hcd *fhci) if (!fhci->dfs_root) return; - if (fhci->dfs_irq_stat) - debugfs_remove(fhci->dfs_irq_stat); - - if (fhci->dfs_regs) - debugfs_remove(fhci->dfs_regs); - + debugfs_remove(fhci->dfs_irq_stat); + debugfs_remove(fhci->dfs_regs); debugfs_remove(fhci->dfs_root); } diff --git a/drivers/usb/host/fotg210-hcd.c b/drivers/usb/host/fotg210-hcd.c index 98a89d16cc3e..adcd2050dced 100644 --- a/drivers/usb/host/fotg210-hcd.c +++ b/drivers/usb/host/fotg210-hcd.c @@ -5838,41 +5838,17 @@ static int fotg210_hcd_probe(struct platform_device *pdev) goto fail_create_hcd; } + hcd->has_tt = 1; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, - "Found HC with no register addr. Check %s setup!\n", - dev_name(dev)); - retval = -ENODEV; - goto fail_request_resource; + hcd->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(hcd->regs)) { + retval = PTR_ERR(hcd->regs); + goto failed; } hcd->rsrc_start = res->start; hcd->rsrc_len = resource_size(res); - hcd->has_tt = 1; - - if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, - fotg210_fotg210_hc_driver.description)) { - dev_dbg(dev, "controller already in use\n"); - retval = -EBUSY; - goto fail_request_resource; - } - - res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (!res) { - dev_err(dev, - "Found HC with no register addr. Check %s setup!\n", - dev_name(dev)); - retval = -ENODEV; - goto fail_request_resource; - } - - hcd->regs = ioremap_nocache(res->start, resource_size(res)); - if (hcd->regs == NULL) { - dev_dbg(dev, "error mapping memory\n"); - retval = -EFAULT; - goto fail_ioremap; - } fotg210 = hcd_to_fotg210(hcd); @@ -5880,24 +5856,20 @@ static int fotg210_hcd_probe(struct platform_device *pdev) retval = fotg210_setup(hcd); if (retval) - goto fail_add_hcd; + goto failed; fotg210_init(fotg210); retval = usb_add_hcd(hcd, irq, IRQF_SHARED); if (retval) { dev_err(dev, "failed to add hcd with err %d\n", retval); - goto fail_add_hcd; + goto failed; } device_wakeup_enable(hcd->self.controller); return retval; -fail_add_hcd: - iounmap(hcd->regs); -fail_ioremap: - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); -fail_request_resource: +failed: usb_put_hcd(hcd); fail_create_hcd: dev_err(dev, "init %s fail, %d\n", dev_name(dev), retval); @@ -5918,8 +5890,6 @@ static int fotg210_hcd_remove(struct platform_device *pdev) return 0; usb_remove_hcd(hcd); - iounmap(hcd->regs); - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); return 0; diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c index 858efcfda50b..6234c75da33f 100644 --- a/drivers/usb/host/max3421-hcd.c +++ b/drivers/usb/host/max3421-hcd.c @@ -102,6 +102,15 @@ enum scheduling_pass { SCHED_PASS_DONE }; +/* Bit numbers for max3421_hcd->todo: */ +enum { + ENABLE_IRQ = 0, + RESET_HCD, + RESET_PORT, + CHECK_UNLINK, + IOPIN_UPDATE +}; + struct max3421_dma_buf { u8 data[2]; }; @@ -146,11 +155,7 @@ struct max3421_hcd { u8 hien; u8 mode; u8 iopins[2]; - unsigned int do_enable_irq:1; - unsigned int do_reset_hcd:1; - unsigned int do_reset_port:1; - unsigned int do_check_unlink:1; - unsigned int do_iopin_update:1; + unsigned long todo; #ifdef DEBUG unsigned long err_stat[16]; #endif @@ -1165,10 +1170,8 @@ max3421_irq_handler(int irq, void *dev_id) if (max3421_hcd->spi_thread && max3421_hcd->spi_thread->state != TASK_RUNNING) wake_up_process(max3421_hcd->spi_thread); - if (!max3421_hcd->do_enable_irq) { - max3421_hcd->do_enable_irq = 1; + if (!test_and_set_bit(ENABLE_IRQ, &max3421_hcd->todo)) disable_irq_nosync(spi->irq); - } return IRQ_HANDLED; } @@ -1423,10 +1426,8 @@ max3421_spi_thread(void *dev_id) spi_wr8(hcd, MAX3421_REG_HIEN, max3421_hcd->hien); set_current_state(TASK_INTERRUPTIBLE); - if (max3421_hcd->do_enable_irq) { - max3421_hcd->do_enable_irq = 0; + if (test_and_clear_bit(ENABLE_IRQ, &max3421_hcd->todo)) enable_irq(spi->irq); - } schedule(); __set_current_state(TASK_RUNNING); } @@ -1440,23 +1441,18 @@ max3421_spi_thread(void *dev_id) else if (!max3421_hcd->curr_urb) i_worked |= max3421_select_and_start_urb(hcd); - if (max3421_hcd->do_reset_hcd) { + if (test_and_clear_bit(RESET_HCD, &max3421_hcd->todo)) /* reset the HCD: */ - max3421_hcd->do_reset_hcd = 0; i_worked |= max3421_reset_hcd(hcd); - } - if (max3421_hcd->do_reset_port) { + if (test_and_clear_bit(RESET_PORT, &max3421_hcd->todo)) { /* perform a USB bus reset: */ - max3421_hcd->do_reset_port = 0; spi_wr8(hcd, MAX3421_REG_HCTL, BIT(MAX3421_HCTL_BUSRST_BIT)); i_worked = 1; } - if (max3421_hcd->do_check_unlink) { - max3421_hcd->do_check_unlink = 0; + if (test_and_clear_bit(CHECK_UNLINK, &max3421_hcd->todo)) i_worked |= max3421_check_unlink(hcd); - } - if (max3421_hcd->do_iopin_update) { + if (test_and_clear_bit(IOPIN_UPDATE, &max3421_hcd->todo)) { /* * IOPINS1/IOPINS2 do not auto-increment, so we can't * use spi_wr_buf(). @@ -1469,7 +1465,6 @@ max3421_spi_thread(void *dev_id) spi_wr8(hcd, MAX3421_REG_IOPINS1 + i, val); max3421_hcd->iopins[i] = val; } - max3421_hcd->do_iopin_update = 0; i_worked = 1; } } @@ -1485,7 +1480,8 @@ max3421_reset_port(struct usb_hcd *hcd) max3421_hcd->port_status &= ~(USB_PORT_STAT_ENABLE | USB_PORT_STAT_LOW_SPEED); - max3421_hcd->do_reset_port = 1; + max3421_hcd->port_status |= USB_PORT_STAT_RESET; + set_bit(RESET_PORT, &max3421_hcd->todo); wake_up_process(max3421_hcd->spi_thread); return 0; } @@ -1498,7 +1494,7 @@ max3421_reset(struct usb_hcd *hcd) hcd->self.sg_tablesize = 0; hcd->speed = HCD_USB2; hcd->self.root_hub->speed = USB_SPEED_FULL; - max3421_hcd->do_reset_hcd = 1; + set_bit(RESET_HCD, &max3421_hcd->todo); wake_up_process(max3421_hcd->spi_thread); return 0; } @@ -1551,7 +1547,7 @@ max3421_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) max3421_ep = urb->ep->hcpriv; if (!max3421_ep) { /* gets freed in max3421_endpoint_disable: */ - max3421_ep = kzalloc(sizeof(struct max3421_ep), mem_flags); + max3421_ep = kzalloc(sizeof(struct max3421_ep), GFP_ATOMIC); if (!max3421_ep) { retval = -ENOMEM; goto out; @@ -1590,7 +1586,7 @@ max3421_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) */ retval = usb_hcd_check_unlink_urb(hcd, urb, status); if (retval == 0) { - max3421_hcd->do_check_unlink = 1; + set_bit(CHECK_UNLINK, &max3421_hcd->todo); wake_up_process(max3421_hcd->spi_thread); } spin_unlock_irqrestore(&max3421_hcd->lock, flags); @@ -1690,7 +1686,7 @@ max3421_gpout_set_value(struct usb_hcd *hcd, u8 pin_number, u8 value) max3421_hcd->iopins[idx] |= mask; else max3421_hcd->iopins[idx] &= ~mask; - max3421_hcd->do_iopin_update = 1; + set_bit(IOPIN_UPDATE, &max3421_hcd->todo); wake_up_process(max3421_hcd->spi_thread); } diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c index 45032e933e18..04f2186939d2 100644 --- a/drivers/usb/host/ohci-dbg.c +++ b/drivers/usb/host/ohci-dbg.c @@ -236,7 +236,7 @@ ohci_dump_roothub ( } } -static void ohci_dump (struct ohci_hcd *controller, int verbose) +static void ohci_dump(struct ohci_hcd *controller) { ohci_dbg (controller, "OHCI controller state\n"); @@ -464,15 +464,16 @@ show_list (struct ohci_hcd *ohci, char *buf, size_t count, struct ed *ed) static ssize_t fill_async_buffer(struct debug_buffer *buf) { struct ohci_hcd *ohci; - size_t temp; + size_t temp, size; unsigned long flags; ohci = buf->ohci; + size = PAGE_SIZE; /* display control and bulk lists together, for simplicity */ spin_lock_irqsave (&ohci->lock, flags); - temp = show_list(ohci, buf->page, buf->count, ohci->ed_controltail); - temp += show_list(ohci, buf->page + temp, buf->count - temp, + temp = show_list(ohci, buf->page, size, ohci->ed_controltail); + temp += show_list(ohci, buf->page + temp, size - temp, ohci->ed_bulktail); spin_unlock_irqrestore (&ohci->lock, flags); diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c index 060a6a414750..a72ab8fe8cd3 100644 --- a/drivers/usb/host/ohci-exynos.c +++ b/drivers/usb/host/ohci-exynos.c @@ -87,7 +87,7 @@ static int exynos_ohci_get_phy(struct device *dev, return -EINVAL; } - phy = devm_of_phy_get(dev, child, 0); + phy = devm_of_phy_get(dev, child, NULL); of_node_put(child); if (IS_ERR(phy)) { ret = PTR_ERR(phy); diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index f98d03f3144c..46987735a2e3 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -72,12 +72,14 @@ static const char hcd_name [] = "ohci_hcd"; #define STATECHANGE_DELAY msecs_to_jiffies(300) +#define IO_WATCHDOG_DELAY msecs_to_jiffies(250) #include "ohci.h" #include "pci-quirks.h" -static void ohci_dump (struct ohci_hcd *ohci, int verbose); -static void ohci_stop (struct usb_hcd *hcd); +static void ohci_dump(struct ohci_hcd *ohci); +static void ohci_stop(struct usb_hcd *hcd); +static void io_watchdog_func(unsigned long _ohci); #include "ohci-hub.c" #include "ohci-dbg.c" @@ -109,6 +111,33 @@ MODULE_PARM_DESC (no_handshake, "true (not default) disables BIOS handshake"); /*-------------------------------------------------------------------------*/ +static int number_of_tds(struct urb *urb) +{ + int len, i, num, this_sg_len; + struct scatterlist *sg; + + len = urb->transfer_buffer_length; + i = urb->num_mapped_sgs; + + if (len > 0 && i > 0) { /* Scatter-gather transfer */ + num = 0; + sg = urb->sg; + for (;;) { + this_sg_len = min_t(int, sg_dma_len(sg), len); + num += DIV_ROUND_UP(this_sg_len, 4096); + len -= this_sg_len; + if (--i <= 0 || len <= 0) + break; + sg = sg_next(sg); + } + + } else { /* Non-SG transfer */ + /* one TD for every 4096 Bytes (could be up to 8K) */ + num = DIV_ROUND_UP(len, 4096); + } + return num; +} + /* * queue up an urb for anything except the root hub */ @@ -142,12 +171,8 @@ static int ohci_urb_enqueue ( // case PIPE_INTERRUPT: // case PIPE_BULK: default: - /* one TD for every 4096 Bytes (can be up to 8K) */ - size += urb->transfer_buffer_length / 4096; - /* ... and for any remaining bytes ... */ - if ((urb->transfer_buffer_length % 4096) != 0) - size++; - /* ... and maybe a zero length packet to wrap it up */ + size += number_of_tds(urb); + /* maybe a zero-length packet to wrap it up */ if (size == 0) size++; else if ((urb->transfer_flags & URB_ZERO_PACKET) != 0 @@ -202,6 +227,16 @@ static int ohci_urb_enqueue ( usb_hcd_unlink_urb_from_ep(hcd, urb); goto fail; } + + /* Start up the I/O watchdog timer, if it's not running */ + if (!timer_pending(&ohci->io_watchdog) && + list_empty(&ohci->eds_in_use)) { + ohci->prev_frame_no = ohci_frame_no(ohci); + mod_timer(&ohci->io_watchdog, + jiffies + IO_WATCHDOG_DELAY); + } + list_add(&ed->in_use_list, &ohci->eds_in_use); + if (ed->type == PIPE_ISOCHRONOUS) { u16 frame = ohci_frame_no(ohci); @@ -277,30 +312,24 @@ static int ohci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) struct ohci_hcd *ohci = hcd_to_ohci (hcd); unsigned long flags; int rc; + urb_priv_t *urb_priv; spin_lock_irqsave (&ohci->lock, flags); rc = usb_hcd_check_unlink_urb(hcd, urb, status); - if (rc) { - ; /* Do nothing */ - } else if (ohci->rh_state == OHCI_RH_RUNNING) { - urb_priv_t *urb_priv; + if (rc == 0) { /* Unless an IRQ completed the unlink while it was being * handed to us, flag it for unlink and giveback, and force * some upcoming INTR_SF to call finish_unlinks() */ urb_priv = urb->hcpriv; - if (urb_priv) { - if (urb_priv->ed->state == ED_OPER) - start_ed_unlink (ohci, urb_priv->ed); + if (urb_priv->ed->state == ED_OPER) + start_ed_unlink(ohci, urb_priv->ed); + + if (ohci->rh_state != OHCI_RH_RUNNING) { + /* With HC dead, we can clean up right away */ + ohci_work(ohci); } - } else { - /* - * with HC dead, we won't respect hc queue pointers - * any more ... just clean up every urb's memory. - */ - if (urb->hcpriv) - finish_urb(ohci, urb, status); } spin_unlock_irqrestore (&ohci->lock, flags); return rc; @@ -332,9 +361,7 @@ rescan: if (ohci->rh_state != OHCI_RH_RUNNING) { sanitize: ed->state = ED_IDLE; - if (quirk_zfmicro(ohci) && ed->type == PIPE_INTERRUPT) - ohci->eds_scheduled--; - finish_unlinks (ohci, 0); + ohci_work(ohci); } switch (ed->state) { @@ -342,11 +369,6 @@ sanitize: /* major IRQ delivery trouble loses INTR_SF too... */ if (limit-- == 0) { ohci_warn(ohci, "ED unlink timeout\n"); - if (quirk_zfmicro(ohci)) { - ohci_warn(ohci, "Attempting ZF TD recovery\n"); - ohci->ed_to_check = ed; - ohci->zf_delay = 2; - } goto sanitize; } spin_unlock_irqrestore (&ohci->lock, flags); @@ -406,93 +428,7 @@ ohci_shutdown (struct usb_hcd *hcd) udelay(10); ohci_writel(ohci, ohci->fminterval, &ohci->regs->fminterval); -} - -static int check_ed(struct ohci_hcd *ohci, struct ed *ed) -{ - return (hc32_to_cpu(ohci, ed->hwINFO) & ED_IN) != 0 - && (hc32_to_cpu(ohci, ed->hwHeadP) & TD_MASK) - == (hc32_to_cpu(ohci, ed->hwTailP) & TD_MASK) - && !list_empty(&ed->td_list); -} - -/* ZF Micro watchdog timer callback. The ZF Micro chipset sometimes completes - * an interrupt TD but neglects to add it to the donelist. On systems with - * this chipset, we need to periodically check the state of the queues to look - * for such "lost" TDs. - */ -static void unlink_watchdog_func(unsigned long _ohci) -{ - unsigned long flags; - unsigned max; - unsigned seen_count = 0; - unsigned i; - struct ed **seen = NULL; - struct ohci_hcd *ohci = (struct ohci_hcd *) _ohci; - - spin_lock_irqsave(&ohci->lock, flags); - max = ohci->eds_scheduled; - if (!max) - goto done; - - if (ohci->ed_to_check) - goto out; - - seen = kcalloc(max, sizeof *seen, GFP_ATOMIC); - if (!seen) - goto out; - - for (i = 0; i < NUM_INTS; i++) { - struct ed *ed = ohci->periodic[i]; - - while (ed) { - unsigned temp; - - /* scan this branch of the periodic schedule tree */ - for (temp = 0; temp < seen_count; temp++) { - if (seen[temp] == ed) { - /* we've checked it and what's after */ - ed = NULL; - break; - } - } - if (!ed) - break; - seen[seen_count++] = ed; - if (!check_ed(ohci, ed)) { - ed = ed->ed_next; - continue; - } - - /* HC's TD list is empty, but HCD sees at least one - * TD that's not been sent through the donelist. - */ - ohci->ed_to_check = ed; - ohci->zf_delay = 2; - - /* The HC may wait until the next frame to report the - * TD as done through the donelist and INTR_WDH. (We - * just *assume* it's not a multi-TD interrupt URB; - * those could defer the IRQ more than one frame, using - * DI...) Check again after the next INTR_SF. - */ - ohci_writel(ohci, OHCI_INTR_SF, - &ohci->regs->intrstatus); - ohci_writel(ohci, OHCI_INTR_SF, - &ohci->regs->intrenable); - - /* flush those writes */ - (void) ohci_readl(ohci, &ohci->regs->control); - - goto out; - } - } -out: - kfree(seen); - if (ohci->eds_scheduled) - mod_timer(&ohci->unlink_watchdog, round_jiffies(jiffies + HZ)); -done: - spin_unlock_irqrestore(&ohci->lock, flags); + ohci->rh_state = OHCI_RH_HALTED; } /*-------------------------------------------------------------------------* @@ -506,6 +442,9 @@ static int ohci_init (struct ohci_hcd *ohci) int ret; struct usb_hcd *hcd = ohci_to_hcd(ohci); + /* Accept arbitrarily long scatter-gather lists */ + hcd->self.sg_tablesize = ~0; + if (distrust_firmware) ohci->flags |= OHCI_QUIRK_HUB_POWER; @@ -558,8 +497,12 @@ static int ohci_init (struct ohci_hcd *ohci) if (ohci->hcca) return 0; + setup_timer(&ohci->io_watchdog, io_watchdog_func, + (unsigned long) ohci); + set_timer_slack(&ohci->io_watchdog, msecs_to_jiffies(20)); + ohci->hcca = dma_alloc_coherent (hcd->self.controller, - sizeof *ohci->hcca, &ohci->hcca_dma, 0); + sizeof(*ohci->hcca), &ohci->hcca_dma, GFP_KERNEL); if (!ohci->hcca) return -ENOMEM; @@ -735,16 +678,7 @@ retry: // POTPGT delay is bits 24-31, in 2 ms units. mdelay ((val >> 23) & 0x1fe); - if (quirk_zfmicro(ohci)) { - /* Create timer to watch for bad queue state on ZF Micro */ - setup_timer(&ohci->unlink_watchdog, unlink_watchdog_func, - (unsigned long) ohci); - - ohci->eds_scheduled = 0; - ohci->ed_to_check = NULL; - } - - ohci_dump (ohci, 1); + ohci_dump(ohci); return 0; } @@ -777,6 +711,142 @@ static int ohci_start(struct usb_hcd *hcd) /*-------------------------------------------------------------------------*/ +/* + * Some OHCI controllers are known to lose track of completed TDs. They + * don't add the TDs to the hardware done queue, which means we never see + * them as being completed. + * + * This watchdog routine checks for such problems. Without some way to + * tell when those TDs have completed, we would never take their EDs off + * the unlink list. As a result, URBs could never be dequeued and + * endpoints could never be released. + */ +static void io_watchdog_func(unsigned long _ohci) +{ + struct ohci_hcd *ohci = (struct ohci_hcd *) _ohci; + bool takeback_all_pending = false; + u32 status; + u32 head; + struct ed *ed; + struct td *td, *td_start, *td_next; + unsigned frame_no; + unsigned long flags; + + spin_lock_irqsave(&ohci->lock, flags); + + /* + * One way to lose track of completed TDs is if the controller + * never writes back the done queue head. If it hasn't been + * written back since the last time this function ran and if it + * was non-empty at that time, something is badly wrong with the + * hardware. + */ + status = ohci_readl(ohci, &ohci->regs->intrstatus); + if (!(status & OHCI_INTR_WDH) && ohci->wdh_cnt == ohci->prev_wdh_cnt) { + if (ohci->prev_donehead) { + ohci_err(ohci, "HcDoneHead not written back; disabled\n"); + died: + usb_hc_died(ohci_to_hcd(ohci)); + ohci_dump(ohci); + ohci_shutdown(ohci_to_hcd(ohci)); + goto done; + } else { + /* No write back because the done queue was empty */ + takeback_all_pending = true; + } + } + + /* Check every ED which might have pending TDs */ + list_for_each_entry(ed, &ohci->eds_in_use, in_use_list) { + if (ed->pending_td) { + if (takeback_all_pending || + OKAY_TO_TAKEBACK(ohci, ed)) { + unsigned tmp = hc32_to_cpu(ohci, ed->hwINFO); + + ohci_dbg(ohci, "takeback pending TD for dev %d ep 0x%x\n", + 0x007f & tmp, + (0x000f & (tmp >> 7)) + + ((tmp & ED_IN) >> 5)); + add_to_done_list(ohci, ed->pending_td); + } + } + + /* Starting from the latest pending TD, */ + td = ed->pending_td; + + /* or the last TD on the done list, */ + if (!td) { + list_for_each_entry(td_next, &ed->td_list, td_list) { + if (!td_next->next_dl_td) + break; + td = td_next; + } + } + + /* find the last TD processed by the controller. */ + head = hc32_to_cpu(ohci, ACCESS_ONCE(ed->hwHeadP)) & TD_MASK; + td_start = td; + td_next = list_prepare_entry(td, &ed->td_list, td_list); + list_for_each_entry_continue(td_next, &ed->td_list, td_list) { + if (head == (u32) td_next->td_dma) + break; + td = td_next; /* head pointer has passed this TD */ + } + if (td != td_start) { + /* + * In case a WDH cycle is in progress, we will wait + * for the next two cycles to complete before assuming + * this TD will never get on the done queue. + */ + ed->takeback_wdh_cnt = ohci->wdh_cnt + 2; + ed->pending_td = td; + } + } + + ohci_work(ohci); + + if (ohci->rh_state == OHCI_RH_RUNNING) { + + /* + * Sometimes a controller just stops working. We can tell + * by checking that the frame counter has advanced since + * the last time we ran. + * + * But be careful: Some controllers violate the spec by + * stopping their frame counter when no ports are active. + */ + frame_no = ohci_frame_no(ohci); + if (frame_no == ohci->prev_frame_no) { + int active_cnt = 0; + int i; + unsigned tmp; + + for (i = 0; i < ohci->num_ports; ++i) { + tmp = roothub_portstatus(ohci, i); + /* Enabled and not suspended? */ + if ((tmp & RH_PS_PES) && !(tmp & RH_PS_PSS)) + ++active_cnt; + } + + if (active_cnt > 0) { + ohci_err(ohci, "frame counter not updating; disabled\n"); + goto died; + } + } + if (!list_empty(&ohci->eds_in_use)) { + ohci->prev_frame_no = frame_no; + ohci->prev_wdh_cnt = ohci->wdh_cnt; + ohci->prev_donehead = ohci_readl(ohci, + &ohci->regs->donehead); + mod_timer(&ohci->io_watchdog, + jiffies + IO_WATCHDOG_DELAY); + } + } + + done: + spin_unlock_irqrestore(&ohci->lock, flags); +} + /* an interrupt happens */ static irqreturn_t ohci_irq (struct usb_hcd *hcd) @@ -825,7 +895,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd) usb_hc_died(hcd); } - ohci_dump (ohci, 1); + ohci_dump(ohci); ohci_usb_reset (ohci); } @@ -863,58 +933,30 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd) usb_hcd_resume_root_hub(hcd); } - if (ints & OHCI_INTR_WDH) { - spin_lock (&ohci->lock); - dl_done_list (ohci); - spin_unlock (&ohci->lock); - } - - if (quirk_zfmicro(ohci) && (ints & OHCI_INTR_SF)) { - spin_lock(&ohci->lock); - if (ohci->ed_to_check) { - struct ed *ed = ohci->ed_to_check; - - if (check_ed(ohci, ed)) { - /* HC thinks the TD list is empty; HCD knows - * at least one TD is outstanding - */ - if (--ohci->zf_delay == 0) { - struct td *td = list_entry( - ed->td_list.next, - struct td, td_list); - ohci_warn(ohci, - "Reclaiming orphan TD %p\n", - td); - takeback_td(ohci, td); - ohci->ed_to_check = NULL; - } - } else - ohci->ed_to_check = NULL; - } - spin_unlock(&ohci->lock); - } + spin_lock(&ohci->lock); + if (ints & OHCI_INTR_WDH) + update_done_list(ohci); /* could track INTR_SO to reduce available PCI/... bandwidth */ /* handle any pending URB/ED unlinks, leaving INTR_SF enabled * when there's still unlinking to be done (next frame). */ - spin_lock (&ohci->lock); - if (ohci->ed_rm_list) - finish_unlinks (ohci, ohci_frame_no(ohci)); - if ((ints & OHCI_INTR_SF) != 0 - && !ohci->ed_rm_list - && !ohci->ed_to_check + ohci_work(ohci); + if ((ints & OHCI_INTR_SF) != 0 && !ohci->ed_rm_list && ohci->rh_state == OHCI_RH_RUNNING) ohci_writel (ohci, OHCI_INTR_SF, ®s->intrdisable); - spin_unlock (&ohci->lock); if (ohci->rh_state == OHCI_RH_RUNNING) { ohci_writel (ohci, ints, ®s->intrstatus); + if (ints & OHCI_INTR_WDH) + ++ohci->wdh_cnt; + ohci_writel (ohci, OHCI_INTR_MIE, ®s->intrenable); // flush those writes (void) ohci_readl (ohci, &ohci->regs->control); } + spin_unlock(&ohci->lock); return IRQ_HANDLED; } @@ -925,18 +967,17 @@ static void ohci_stop (struct usb_hcd *hcd) { struct ohci_hcd *ohci = hcd_to_ohci (hcd); - ohci_dump (ohci, 1); + ohci_dump(ohci); if (quirk_nec(ohci)) flush_work(&ohci->nec_work); + del_timer_sync(&ohci->io_watchdog); ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable); ohci_usb_reset(ohci); free_irq(hcd->irq, hcd); hcd->irq = 0; - if (quirk_zfmicro(ohci)) - del_timer(&ohci->unlink_watchdog); if (quirk_amdiso(ohci)) usb_amd_dev_put(); @@ -993,7 +1034,7 @@ int ohci_restart(struct ohci_hcd *ohci) if (!urb->unlinked) urb->unlinked = -ESHUTDOWN; } - finish_unlinks (ohci, 0); + ohci_work(ohci); spin_unlock_irq(&ohci->lock); /* paranoia, in case that didn't work: */ diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index b4940de1eba3..17d32b0ea565 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -39,8 +39,8 @@ #define OHCI_SCHED_ENABLES \ (OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_PLE|OHCI_CTRL_IE) -static void dl_done_list (struct ohci_hcd *); -static void finish_unlinks (struct ohci_hcd *, u16); +static void update_done_list(struct ohci_hcd *); +static void ohci_work(struct ohci_hcd *); #ifdef CONFIG_PM static int ohci_rh_suspend (struct ohci_hcd *ohci, int autostop) @@ -87,8 +87,8 @@ __acquires(ohci->lock) msleep (8); spin_lock_irq (&ohci->lock); } - dl_done_list (ohci); - finish_unlinks (ohci, ohci_frame_no(ohci)); + update_done_list(ohci); + ohci_work(ohci); /* * Some controllers don't handle "global" suspend properly if @@ -309,6 +309,9 @@ static int ohci_bus_suspend (struct usb_hcd *hcd) else rc = ohci_rh_suspend (ohci, 0); spin_unlock_irq (&ohci->lock); + + if (rc == 0) + del_timer_sync(&ohci->io_watchdog); return rc; } diff --git a/drivers/usb/host/ohci-mem.c b/drivers/usb/host/ohci-mem.c index 2f20d3dc895b..c9e315c6808a 100644 --- a/drivers/usb/host/ohci-mem.c +++ b/drivers/usb/host/ohci-mem.c @@ -28,6 +28,7 @@ static void ohci_hcd_init (struct ohci_hcd *ohci) ohci->next_statechange = jiffies; spin_lock_init (&ohci->lock); INIT_LIST_HEAD (&ohci->pending); + INIT_LIST_HEAD(&ohci->eds_in_use); } /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c index d4253e319428..1463c398d322 100644 --- a/drivers/usb/host/ohci-q.c +++ b/drivers/usb/host/ohci-q.c @@ -187,10 +187,6 @@ static int ed_schedule (struct ohci_hcd *ohci, struct ed *ed) ed->ed_prev = NULL; ed->ed_next = NULL; ed->hwNextED = 0; - if (quirk_zfmicro(ohci) - && (ed->type == PIPE_INTERRUPT) - && !(ohci->eds_scheduled++)) - mod_timer(&ohci->unlink_watchdog, round_jiffies(jiffies + HZ)); wmb (); /* we care about rm_list when setting CLE/BLE in case the HC was at @@ -311,8 +307,7 @@ static void periodic_unlink (struct ohci_hcd *ohci, struct ed *ed) * - ED_OPER: when there's any request queued, the ED gets rescheduled * immediately. HC should be working on them. * - * - ED_IDLE: when there's no TD queue. there's no reason for the HC - * to care about this ED; safe to disable the endpoint. + * - ED_IDLE: when there's no TD queue or the HC isn't running. * * When finish_unlinks() runs later, after SOF interrupt, it will often * complete one or more URB unlinks before making that state change. @@ -602,6 +597,8 @@ static void td_submit_urb ( u32 info = 0; int is_out = usb_pipeout (urb->pipe); int periodic = 0; + int i, this_sg_len, n; + struct scatterlist *sg; /* OHCI handles the bulk/interrupt data toggles itself. We just * use the device toggle bits for resetting, and rely on the fact @@ -615,10 +612,24 @@ static void td_submit_urb ( list_add (&urb_priv->pending, &ohci->pending); - if (data_len) - data = urb->transfer_dma; - else - data = 0; + i = urb->num_mapped_sgs; + if (data_len > 0 && i > 0) { + sg = urb->sg; + data = sg_dma_address(sg); + + /* + * urb->transfer_buffer_length may be smaller than the + * size of the scatterlist (or vice versa) + */ + this_sg_len = min_t(int, sg_dma_len(sg), data_len); + } else { + sg = NULL; + if (data_len) + data = urb->transfer_dma; + else + data = 0; + this_sg_len = data_len; + } /* NOTE: TD_CC is set so we can tell which TDs the HC processed by * using TD_CC_GET, as well as by seeing them on the done list. @@ -639,17 +650,29 @@ static void td_submit_urb ( ? TD_T_TOGGLE | TD_CC | TD_DP_OUT : TD_T_TOGGLE | TD_CC | TD_DP_IN; /* TDs _could_ transfer up to 8K each */ - while (data_len > 4096) { - td_fill (ohci, info, data, 4096, urb, cnt); - data += 4096; - data_len -= 4096; + for (;;) { + n = min(this_sg_len, 4096); + + /* maybe avoid ED halt on final TD short read */ + if (n >= data_len || (i == 1 && n >= this_sg_len)) { + if (!(urb->transfer_flags & URB_SHORT_NOT_OK)) + info |= TD_R; + } + td_fill(ohci, info, data, n, urb, cnt); + this_sg_len -= n; + data_len -= n; + data += n; cnt++; + + if (this_sg_len <= 0) { + if (--i <= 0 || data_len <= 0) + break; + sg = sg_next(sg); + data = sg_dma_address(sg); + this_sg_len = min_t(int, sg_dma_len(sg), + data_len); + } } - /* maybe avoid ED halt on final TD short read */ - if (!(urb->transfer_flags & URB_SHORT_NOT_OK)) - info |= TD_R; - td_fill (ohci, info, data, data_len, urb, cnt); - cnt++; if ((urb->transfer_flags & URB_ZERO_PACKET) && cnt < urb_priv->length) { td_fill (ohci, info, 0, 0, urb, cnt); @@ -869,13 +892,46 @@ static void ed_halted(struct ohci_hcd *ohci, struct td *td, int cc) } } -/* replies to the request have to be on a FIFO basis so - * we unreverse the hc-reversed done-list - */ -static struct td *dl_reverse_done_list (struct ohci_hcd *ohci) +/* Add a TD to the done list */ +static void add_to_done_list(struct ohci_hcd *ohci, struct td *td) +{ + struct td *td2, *td_prev; + struct ed *ed; + + if (td->next_dl_td) + return; /* Already on the list */ + + /* Add all the TDs going back until we reach one that's on the list */ + ed = td->ed; + td2 = td_prev = td; + list_for_each_entry_continue_reverse(td2, &ed->td_list, td_list) { + if (td2->next_dl_td) + break; + td2->next_dl_td = td_prev; + td_prev = td2; + } + + if (ohci->dl_end) + ohci->dl_end->next_dl_td = td_prev; + else + ohci->dl_start = td_prev; + + /* + * Make td->next_dl_td point to td itself, to mark the fact + * that td is on the done list. + */ + ohci->dl_end = td->next_dl_td = td; + + /* Did we just add the latest pending TD? */ + td2 = ed->pending_td; + if (td2 && td2->next_dl_td) + ed->pending_td = NULL; +} + +/* Get the entries on the hardware done queue and put them on our list */ +static void update_done_list(struct ohci_hcd *ohci) { u32 td_dma; - struct td *td_rev = NULL; struct td *td = NULL; td_dma = hc32_to_cpup (ohci, &ohci->hcca->done_head); @@ -883,7 +939,7 @@ static struct td *dl_reverse_done_list (struct ohci_hcd *ohci) wmb(); /* get TD from hc's singly linked list, and - * prepend to ours. ed->td_list changes later. + * add to ours. ed->td_list changes later. */ while (td_dma) { int cc; @@ -905,19 +961,17 @@ static struct td *dl_reverse_done_list (struct ohci_hcd *ohci) && (td->ed->hwHeadP & cpu_to_hc32 (ohci, ED_H))) ed_halted(ohci, td, cc); - td->next_dl_td = td_rev; - td_rev = td; td_dma = hc32_to_cpup (ohci, &td->hwNextTD); + add_to_done_list(ohci, td); } - return td_rev; } /*-------------------------------------------------------------------------*/ /* there are some urbs/eds to unlink; called in_irq(), with HCD locked */ -static void -finish_unlinks (struct ohci_hcd *ohci, u16 tick) +static void finish_unlinks(struct ohci_hcd *ohci) { + unsigned tick = ohci_frame_no(ohci); struct ed *ed, **last; rescan_all: @@ -926,41 +980,48 @@ rescan_all: int completed, modified; __hc32 *prev; + /* Is this ED already invisible to the hardware? */ + if (ed->state == ED_IDLE) + goto ed_idle; + /* only take off EDs that the HC isn't using, accounting for * frame counter wraps and EDs with partially retired TDs */ - if (likely(ohci->rh_state == OHCI_RH_RUNNING)) { - if (tick_before (tick, ed->tick)) { + if (likely(ohci->rh_state == OHCI_RH_RUNNING) && + tick_before(tick, ed->tick)) { skip_ed: - last = &ed->ed_next; - continue; - } + last = &ed->ed_next; + continue; + } + if (!list_empty(&ed->td_list)) { + struct td *td; + u32 head; - if (!list_empty (&ed->td_list)) { - struct td *td; - u32 head; - - td = list_entry (ed->td_list.next, struct td, - td_list); - head = hc32_to_cpu (ohci, ed->hwHeadP) & - TD_MASK; - - /* INTR_WDH may need to clean up first */ - if (td->td_dma != head) { - if (ed == ohci->ed_to_check) - ohci->ed_to_check = NULL; - else - goto skip_ed; - } - } + td = list_first_entry(&ed->td_list, struct td, td_list); + + /* INTR_WDH may need to clean up first */ + head = hc32_to_cpu(ohci, ed->hwHeadP) & TD_MASK; + if (td->td_dma != head && + ohci->rh_state == OHCI_RH_RUNNING) + goto skip_ed; + + /* Don't mess up anything already on the done list */ + if (td->next_dl_td) + goto skip_ed; } + /* ED's now officially unlinked, hc doesn't see */ + ed->state = ED_IDLE; + ed->hwHeadP &= ~cpu_to_hc32(ohci, ED_H); + ed->hwNextED = 0; + wmb(); + ed->hwINFO &= ~cpu_to_hc32(ohci, ED_SKIP | ED_DEQUEUE); +ed_idle: + /* reentrancy: if we drop the schedule lock, someone might * have modified this list. normally it's just prepending * entries (which we'd ignore), but paranoia won't hurt. */ - *last = ed->ed_next; - ed->ed_next = NULL; modified = 0; /* unlink urbs as requested, but rescan the list after @@ -1018,19 +1079,21 @@ rescan_this: if (completed && !list_empty (&ed->td_list)) goto rescan_this; - /* ED's now officially unlinked, hc doesn't see */ - ed->state = ED_IDLE; - if (quirk_zfmicro(ohci) && ed->type == PIPE_INTERRUPT) - ohci->eds_scheduled--; - ed->hwHeadP &= ~cpu_to_hc32(ohci, ED_H); - ed->hwNextED = 0; - wmb (); - ed->hwINFO &= ~cpu_to_hc32 (ohci, ED_SKIP | ED_DEQUEUE); - - /* but if there's work queued, reschedule */ - if (!list_empty (&ed->td_list)) { - if (ohci->rh_state == OHCI_RH_RUNNING) - ed_schedule (ohci, ed); + /* + * If no TDs are queued, take ED off the ed_rm_list. + * Otherwise, if the HC is running, reschedule. + * If not, leave it on the list for further dequeues. + */ + if (list_empty(&ed->td_list)) { + *last = ed->ed_next; + ed->ed_next = NULL; + list_del(&ed->in_use_list); + } else if (ohci->rh_state == OHCI_RH_RUNNING) { + *last = ed->ed_next; + ed->ed_next = NULL; + ed_schedule(ohci, ed); + } else { + last = &ed->ed_next; } if (modified) @@ -1082,12 +1145,7 @@ rescan_this: /*-------------------------------------------------------------------------*/ -/* - * Used to take back a TD from the host controller. This would normally be - * called from within dl_done_list, however it may be called directly if the - * HC no longer sees the TD and it has not appeared on the donelist (after - * two frames). This bug has been observed on ZF Micro systems. - */ +/* Take back a TD from the host controller */ static void takeback_td(struct ohci_hcd *ohci, struct td *td) { struct urb *urb = td->urb; @@ -1134,37 +1192,43 @@ static void takeback_td(struct ohci_hcd *ohci, struct td *td) * * This is the main path for handing urbs back to drivers. The only other * normal path is finish_unlinks(), which unlinks URBs using ed_rm_list, - * instead of scanning the (re-reversed) donelist as this does. There's - * an abnormal path too, handling a quirk in some Compaq silicon: URBs - * with TDs that appear to be orphaned are directly reclaimed. + * instead of scanning the (re-reversed) donelist as this does. */ -static void -dl_done_list (struct ohci_hcd *ohci) +static void process_done_list(struct ohci_hcd *ohci) { - struct td *td = dl_reverse_done_list (ohci); + struct td *td; - while (td) { - struct td *td_next = td->next_dl_td; - struct ed *ed = td->ed; + while (ohci->dl_start) { + td = ohci->dl_start; + if (td == ohci->dl_end) + ohci->dl_start = ohci->dl_end = NULL; + else + ohci->dl_start = td->next_dl_td; - /* - * Some OHCI controllers (NVIDIA for sure, maybe others) - * occasionally forget to add TDs to the done queue. Since - * TDs for a given endpoint are always processed in order, - * if we find a TD on the donelist then all of its - * predecessors must be finished as well. - */ - for (;;) { - struct td *td2; + takeback_td(ohci, td); + } +} - td2 = list_first_entry(&ed->td_list, struct td, - td_list); - if (td2 == td) - break; - takeback_td(ohci, td2); - } +/* + * TD takeback and URB giveback must be single-threaded. + * This routine takes care of it all. + */ +static void ohci_work(struct ohci_hcd *ohci) +{ + if (ohci->working) { + ohci->restart_work = 1; + return; + } + ohci->working = 1; - takeback_td(ohci, td); - td = td_next; + restart: + process_done_list(ohci); + if (ohci->ed_rm_list) + finish_unlinks(ohci); + + if (ohci->restart_work) { + ohci->restart_work = 0; + goto restart; } + ohci->working = 0; } diff --git a/drivers/usb/host/ohci-spear.c b/drivers/usb/host/ohci-spear.c index 8b29a0c04c23..8d5876692e7c 100644 --- a/drivers/usb/host/ohci-spear.c +++ b/drivers/usb/host/ohci-spear.c @@ -162,7 +162,7 @@ static int spear_ohci_hcd_drv_resume(struct platform_device *dev) } #endif -static struct of_device_id spear_ohci_id_table[] = { +static const struct of_device_id spear_ohci_id_table[] = { { .compatible = "st,spear600-ohci", }, { }, }; diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index 05e02a709d4f..59f424567a8d 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -47,6 +47,7 @@ struct ed { struct ed *ed_next; /* on schedule or rm_list */ struct ed *ed_prev; /* for non-interrupt EDs */ struct list_head td_list; /* "shadow list" of our TDs */ + struct list_head in_use_list; /* create --> IDLE --> OPER --> ... --> IDLE --> destroy * usually: OPER --> UNLINK --> (IDLE | OPER) --> ... @@ -66,6 +67,13 @@ struct ed { /* HC may see EDs on rm_list until next frame (frame_no == tick) */ u16 tick; + + /* Detect TDs not added to the done queue */ + unsigned takeback_wdh_cnt; + struct td *pending_td; +#define OKAY_TO_TAKEBACK(ohci, ed) \ + ((int) (ohci->wdh_cnt - ed->takeback_wdh_cnt) >= 0) + } __attribute__ ((aligned(16))); #define ED_MASK ((u32)~0x0f) /* strip hw status in low addr bits */ @@ -380,7 +388,9 @@ struct ohci_hcd { struct dma_pool *td_cache; struct dma_pool *ed_cache; struct td *td_hash [TD_HASH_SIZE]; + struct td *dl_start, *dl_end; /* the done list */ struct list_head pending; + struct list_head eds_in_use; /* all EDs with at least 1 TD */ /* * driver state @@ -392,6 +402,8 @@ struct ohci_hcd { unsigned long next_statechange; /* suspend/resume */ u32 fminterval; /* saved register */ unsigned autostop:1; /* rh auto stopping/stopped */ + unsigned working:1; + unsigned restart_work:1; unsigned long flags; /* for HC bugs */ #define OHCI_QUIRK_AMD756 0x01 /* erratum #4 */ @@ -409,13 +421,12 @@ struct ohci_hcd { // there are also chip quirks/bugs in init logic - struct work_struct nec_work; /* Worker for NEC quirk */ + unsigned prev_frame_no; + unsigned wdh_cnt, prev_wdh_cnt; + u32 prev_donehead; + struct timer_list io_watchdog; - /* Needed for ZF Micro quirk */ - struct timer_list unlink_watchdog; - unsigned eds_scheduled; - struct ed *ed_to_check; - unsigned zf_delay; + struct work_struct nec_work; /* Worker for NEC quirk */ struct dentry *debug_dir; struct dentry *debug_async; diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c index e07248b6ab67..da5fb0e3c363 100644 --- a/drivers/usb/host/oxu210hp-hcd.c +++ b/drivers/usb/host/oxu210hp-hcd.c @@ -3826,49 +3826,36 @@ static int oxu_drv_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "IRQ resource %d\n", irq); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "no registers address! Check %s setup!\n", - dev_name(&pdev->dev)); - return -ENODEV; + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) { + ret = PTR_ERR(base); + goto error; } memstart = res->start; memlen = resource_size(res); - dev_dbg(&pdev->dev, "MEM resource %lx-%lx\n", memstart, memlen); - if (!request_mem_region(memstart, memlen, - oxu_hc_driver.description)) { - dev_dbg(&pdev->dev, "memory area already in use\n"); - return -EBUSY; - } ret = irq_set_irq_type(irq, IRQF_TRIGGER_FALLING); if (ret) { dev_err(&pdev->dev, "error setting irq type\n"); ret = -EFAULT; - goto error_set_irq_type; - } - - base = ioremap(memstart, memlen); - if (!base) { - dev_dbg(&pdev->dev, "error mapping memory\n"); - ret = -EFAULT; - goto error_ioremap; + goto error; } /* Allocate a driver data struct to hold useful info for both * SPH & OTG devices */ - info = kzalloc(sizeof(struct oxu_info), GFP_KERNEL); + info = devm_kzalloc(&pdev->dev, sizeof(struct oxu_info), GFP_KERNEL); if (!info) { dev_dbg(&pdev->dev, "error allocating memory\n"); ret = -EFAULT; - goto error_alloc; + goto error; } platform_set_drvdata(pdev, info); ret = oxu_init(pdev, memstart, memlen, base, irq); if (ret < 0) { dev_dbg(&pdev->dev, "cannot init USB devices\n"); - goto error_init; + goto error; } dev_info(&pdev->dev, "devices enabled and running\n"); @@ -3876,16 +3863,7 @@ static int oxu_drv_probe(struct platform_device *pdev) return 0; -error_init: - kfree(info); - -error_alloc: - iounmap(base); - -error_set_irq_type: -error_ioremap: - release_mem_region(memstart, memlen); - +error: dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), ret); return ret; } @@ -3899,18 +3877,10 @@ static void oxu_remove(struct platform_device *pdev, struct usb_hcd *hcd) static int oxu_drv_remove(struct platform_device *pdev) { struct oxu_info *info = platform_get_drvdata(pdev); - unsigned long memstart = info->hcd[0]->rsrc_start, - memlen = info->hcd[0]->rsrc_len; - void *base = info->hcd[0]->regs; oxu_remove(pdev, info->hcd[0]); oxu_remove(pdev, info->hcd[1]); - iounmap(base); - release_mem_region(memstart, memlen); - - kfree(info); - return 0; } diff --git a/drivers/usb/host/uhci-grlib.c b/drivers/usb/host/uhci-grlib.c index ab25dc397e8b..05f57ffdf9ab 100644 --- a/drivers/usb/host/uhci-grlib.c +++ b/drivers/usb/host/uhci-grlib.c @@ -17,6 +17,7 @@ * (C) Copyright 2004-2007 Alan Stern, stern@rowland.harvard.edu */ +#include <linux/device.h> #include <linux/of_irq.h> #include <linux/of_address.h> #include <linux/of_platform.h> @@ -113,24 +114,17 @@ static int uhci_hcd_grlib_probe(struct platform_device *op) hcd->rsrc_start = res.start; hcd->rsrc_len = resource_size(&res); - if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { - printk(KERN_ERR "%s: request_mem_region failed\n", __FILE__); - rv = -EBUSY; - goto err_rmr; - } - irq = irq_of_parse_and_map(dn, 0); if (irq == NO_IRQ) { printk(KERN_ERR "%s: irq_of_parse_and_map failed\n", __FILE__); rv = -EBUSY; - goto err_irq; + goto err_usb; } - hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); - if (!hcd->regs) { - printk(KERN_ERR "%s: ioremap failed\n", __FILE__); - rv = -ENOMEM; - goto err_ioremap; + hcd->regs = devm_ioremap_resource(&op->dev, &res); + if (IS_ERR(hcd->regs)) { + rv = PTR_ERR(hcd->regs); + goto err_irq; } uhci = hcd_to_uhci(hcd); @@ -139,18 +133,14 @@ static int uhci_hcd_grlib_probe(struct platform_device *op) rv = usb_add_hcd(hcd, irq, 0); if (rv) - goto err_uhci; + goto err_irq; device_wakeup_enable(hcd->self.controller); return 0; -err_uhci: - iounmap(hcd->regs); -err_ioremap: - irq_dispose_mapping(irq); err_irq: - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); -err_rmr: + irq_dispose_mapping(irq); +err_usb: usb_put_hcd(hcd); return rv; @@ -164,10 +154,7 @@ static int uhci_hcd_grlib_remove(struct platform_device *op) usb_remove_hcd(hcd); - iounmap(hcd->regs); irq_dispose_mapping(hcd->irq); - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); - usb_put_hcd(hcd); return 0; diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 27f35e8f161b..a7de8e8bb458 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -591,7 +591,7 @@ static int uhci_start(struct usb_hcd *hcd) uhci->frame = dma_alloc_coherent(uhci_dev(uhci), UHCI_NUMFRAMES * sizeof(*uhci->frame), - &uhci->frame_dma_handle, 0); + &uhci->frame_dma_handle, GFP_KERNEL); if (!uhci->frame) { dev_err(uhci_dev(uhci), "unable to allocate consistent memory for frame list\n"); diff --git a/drivers/usb/host/uhci-platform.c b/drivers/usb/host/uhci-platform.c index 01833ab2b5c3..b987f1d10058 100644 --- a/drivers/usb/host/uhci-platform.c +++ b/drivers/usb/host/uhci-platform.c @@ -8,6 +8,7 @@ */ #include <linux/of.h> +#include <linux/device.h> #include <linux/platform_device.h> static int uhci_platform_init(struct usb_hcd *hcd) @@ -88,33 +89,22 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev) hcd->rsrc_start = res->start; hcd->rsrc_len = resource_size(res); - if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { - pr_err("%s: request_mem_region failed\n", __func__); - ret = -EBUSY; + hcd->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(hcd->regs)) { + ret = PTR_ERR(hcd->regs); goto err_rmr; } - - hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); - if (!hcd->regs) { - pr_err("%s: ioremap failed\n", __func__); - ret = -ENOMEM; - goto err_irq; - } uhci = hcd_to_uhci(hcd); uhci->regs = hcd->regs; ret = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_SHARED); if (ret) - goto err_uhci; + goto err_rmr; device_wakeup_enable(hcd->self.controller); return 0; -err_uhci: - iounmap(hcd->regs); -err_irq: - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); err_rmr: usb_put_hcd(hcd); @@ -126,8 +116,6 @@ static int uhci_hcd_platform_remove(struct platform_device *pdev) struct usb_hcd *hcd = platform_get_drvdata(pdev); usb_remove_hcd(hcd); - iounmap(hcd->regs); - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); return 0; diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index e20520f42753..687d36608155 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -33,7 +33,7 @@ #define PCI_DEVICE_ID_FRESCO_LOGIC_FL1400 0x1400 #define PCI_VENDOR_ID_ETRON 0x1b6f -#define PCI_DEVICE_ID_ASROCK_P67 0x7023 +#define PCI_DEVICE_ID_EJ168 0x7023 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_XHCI 0x8c31 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31 @@ -140,9 +140,10 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) xhci->quirks |= XHCI_SPURIOUS_REBOOT; } if (pdev->vendor == PCI_VENDOR_ID_ETRON && - pdev->device == PCI_DEVICE_ID_ASROCK_P67) { + pdev->device == PCI_DEVICE_ID_EJ168) { xhci->quirks |= XHCI_RESET_ON_RESUME; xhci->quirks |= XHCI_TRUST_TX_LENGTH; + xhci->quirks |= XHCI_BROKEN_STREAMS; } if (pdev->vendor == PCI_VENDOR_ID_RENESAS && pdev->device == 0x0015) @@ -230,7 +231,8 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) goto put_usb3_hcd; /* Roothub already marked as USB 3.0 speed */ - if (HCC_MAX_PSA(xhci->hcc_params) >= 4) + if (!(xhci->quirks & XHCI_BROKEN_STREAMS) && + HCC_MAX_PSA(xhci->hcc_params) >= 4) xhci->shared_hcd->can_do_streams = 1; /* USB-2 and USB-3 roothubs initialized, allow runtime pm suspend */ diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 29d8adb5c8d1..1a0cf9f31e43 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -17,9 +17,11 @@ #include <linux/of.h> #include <linux/platform_device.h> #include <linux/slab.h> +#include <linux/usb/xhci_pdriver.h> #include "xhci.h" #include "xhci-mvebu.h" +#include "xhci-rcar.h" static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci) { @@ -34,11 +36,27 @@ static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci) /* called during probe() after chip reset completes */ static int xhci_plat_setup(struct usb_hcd *hcd) { + struct device_node *of_node = hcd->self.controller->of_node; + int ret; + + if (of_device_is_compatible(of_node, "renesas,xhci-r8a7790") || + of_device_is_compatible(of_node, "renesas,xhci-r8a7791")) { + ret = xhci_rcar_init_quirk(hcd); + if (ret) + return ret; + } + return xhci_gen_setup(hcd, xhci_plat_quirks); } static int xhci_plat_start(struct usb_hcd *hcd) { + struct device_node *of_node = hcd->self.controller->of_node; + + if (of_device_is_compatible(of_node, "renesas,xhci-r8a7790") || + of_device_is_compatible(of_node, "renesas,xhci-r8a7791")) + xhci_rcar_start(hcd); + return xhci_run(hcd); } @@ -90,10 +108,15 @@ static const struct hc_driver xhci_plat_xhci_driver = { .hub_status_data = xhci_hub_status_data, .bus_suspend = xhci_bus_suspend, .bus_resume = xhci_bus_resume, + + .enable_usb3_lpm_timeout = xhci_enable_usb3_lpm_timeout, + .disable_usb3_lpm_timeout = xhci_disable_usb3_lpm_timeout, }; static int xhci_plat_probe(struct platform_device *pdev) { + struct device_node *node = pdev->dev.of_node; + struct usb_xhci_pdata *pdata = dev_get_platdata(&pdev->dev); const struct hc_driver *driver; struct xhci_hcd *xhci; struct resource *res; @@ -140,20 +163,12 @@ static int xhci_plat_probe(struct platform_device *pdev) hcd->rsrc_start = res->start; hcd->rsrc_len = resource_size(res); - if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, - driver->description)) { - dev_dbg(&pdev->dev, "controller already in use\n"); - ret = -EBUSY; + hcd->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(hcd->regs)) { + ret = PTR_ERR(hcd->regs); goto put_hcd; } - hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len); - if (!hcd->regs) { - dev_dbg(&pdev->dev, "error mapping memory\n"); - ret = -EFAULT; - goto release_mem_region; - } - /* * Not all platforms have a clk so it is not an error if the * clock does not exists. @@ -162,7 +177,7 @@ static int xhci_plat_probe(struct platform_device *pdev) if (!IS_ERR(clk)) { ret = clk_prepare_enable(clk); if (ret) - goto unmap_registers; + goto put_hcd; } ret = usb_add_hcd(hcd, irq, IRQF_SHARED); @@ -182,6 +197,9 @@ static int xhci_plat_probe(struct platform_device *pdev) goto dealloc_usb2_hcd; } + if ((node && of_property_read_bool(node, "usb3-lpm-capable")) || + (pdata && pdata->usb3_lpm_capable)) + xhci->quirks |= XHCI_LPM_SUPPORT; /* * Set the xHCI pointer before xhci_plat_setup() (aka hcd_driver.reset) * is called by usb_add_hcd(). @@ -207,12 +225,6 @@ disable_clk: if (!IS_ERR(clk)) clk_disable_unprepare(clk); -unmap_registers: - iounmap(hcd->regs); - -release_mem_region: - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); - put_hcd: usb_put_hcd(hcd); @@ -231,8 +243,6 @@ static int xhci_plat_remove(struct platform_device *dev) usb_remove_hcd(hcd); if (!IS_ERR(clk)) clk_disable_unprepare(clk); - iounmap(hcd->regs); - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); kfree(xhci); @@ -270,6 +280,8 @@ static const struct of_device_id usb_xhci_of_match[] = { { .compatible = "xhci-platform" }, { .compatible = "marvell,armada-375-xhci"}, { .compatible = "marvell,armada-380-xhci"}, + { .compatible = "renesas,xhci-r8a7790"}, + { .compatible = "renesas,xhci-r8a7791"}, { }, }; MODULE_DEVICE_TABLE(of, usb_xhci_of_match); diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c new file mode 100644 index 000000000000..ff0d1b44ea58 --- /dev/null +++ b/drivers/usb/host/xhci-rcar.c @@ -0,0 +1,148 @@ +/* + * xHCI host controller driver for R-Car SoCs + * + * Copyright (C) 2014 Renesas Electronics Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#include <linux/firmware.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/usb/phy.h> + +#include "xhci.h" +#include "xhci-rcar.h" + +#define FIRMWARE_NAME "r8a779x_usb3_v1.dlmem" +MODULE_FIRMWARE(FIRMWARE_NAME); + +/*** Register Offset ***/ +#define RCAR_USB3_INT_ENA 0x224 /* Interrupt Enable */ +#define RCAR_USB3_DL_CTRL 0x250 /* FW Download Control & Status */ +#define RCAR_USB3_FW_DATA0 0x258 /* FW Data0 */ + +#define RCAR_USB3_LCLK 0xa44 /* LCLK Select */ +#define RCAR_USB3_CONF1 0xa48 /* USB3.0 Configuration1 */ +#define RCAR_USB3_CONF2 0xa5c /* USB3.0 Configuration2 */ +#define RCAR_USB3_CONF3 0xaa8 /* USB3.0 Configuration3 */ +#define RCAR_USB3_RX_POL 0xab0 /* USB3.0 RX Polarity */ +#define RCAR_USB3_TX_POL 0xab8 /* USB3.0 TX Polarity */ + +/*** Register Settings ***/ +/* Interrupt Enable */ +#define RCAR_USB3_INT_XHC_ENA 0x00000001 +#define RCAR_USB3_INT_PME_ENA 0x00000002 +#define RCAR_USB3_INT_HSE_ENA 0x00000004 +#define RCAR_USB3_INT_ENA_VAL (RCAR_USB3_INT_XHC_ENA | \ + RCAR_USB3_INT_PME_ENA | RCAR_USB3_INT_HSE_ENA) + +/* FW Download Control & Status */ +#define RCAR_USB3_DL_CTRL_ENABLE 0x00000001 +#define RCAR_USB3_DL_CTRL_FW_SUCCESS 0x00000010 +#define RCAR_USB3_DL_CTRL_FW_SET_DATA0 0x00000100 + +/* LCLK Select */ +#define RCAR_USB3_LCLK_ENA_VAL 0x01030001 + +/* USB3.0 Configuration */ +#define RCAR_USB3_CONF1_VAL 0x00030204 +#define RCAR_USB3_CONF2_VAL 0x00030300 +#define RCAR_USB3_CONF3_VAL 0x13802007 + +/* USB3.0 Polarity */ +#define RCAR_USB3_RX_POL_VAL BIT(21) +#define RCAR_USB3_TX_POL_VAL BIT(4) + +void xhci_rcar_start(struct usb_hcd *hcd) +{ + u32 temp; + + if (hcd->regs != NULL) { + /* Interrupt Enable */ + temp = readl(hcd->regs + RCAR_USB3_INT_ENA); + temp |= RCAR_USB3_INT_ENA_VAL; + writel(temp, hcd->regs + RCAR_USB3_INT_ENA); + /* LCLK Select */ + writel(RCAR_USB3_LCLK_ENA_VAL, hcd->regs + RCAR_USB3_LCLK); + /* USB3.0 Configuration */ + writel(RCAR_USB3_CONF1_VAL, hcd->regs + RCAR_USB3_CONF1); + writel(RCAR_USB3_CONF2_VAL, hcd->regs + RCAR_USB3_CONF2); + writel(RCAR_USB3_CONF3_VAL, hcd->regs + RCAR_USB3_CONF3); + /* USB3.0 Polarity */ + writel(RCAR_USB3_RX_POL_VAL, hcd->regs + RCAR_USB3_RX_POL); + writel(RCAR_USB3_TX_POL_VAL, hcd->regs + RCAR_USB3_TX_POL); + } +} + +static int xhci_rcar_download_firmware(struct device *dev, void __iomem *regs) +{ + const struct firmware *fw; + int retval, index, j, time; + int timeout = 10000; + u32 data, val, temp; + + /* request R-Car USB3.0 firmware */ + retval = request_firmware(&fw, FIRMWARE_NAME, dev); + if (retval) + return retval; + + /* download R-Car USB3.0 firmware */ + temp = readl(regs + RCAR_USB3_DL_CTRL); + temp |= RCAR_USB3_DL_CTRL_ENABLE; + writel(temp, regs + RCAR_USB3_DL_CTRL); + + for (index = 0; index < fw->size; index += 4) { + /* to avoid reading beyond the end of the buffer */ + for (data = 0, j = 3; j >= 0; j--) { + if ((j + index) < fw->size) + data |= fw->data[index + j] << (8 * j); + } + writel(data, regs + RCAR_USB3_FW_DATA0); + temp = readl(regs + RCAR_USB3_DL_CTRL); + temp |= RCAR_USB3_DL_CTRL_FW_SET_DATA0; + writel(temp, regs + RCAR_USB3_DL_CTRL); + + for (time = 0; time < timeout; time++) { + val = readl(regs + RCAR_USB3_DL_CTRL); + if ((val & RCAR_USB3_DL_CTRL_FW_SET_DATA0) == 0) + break; + udelay(1); + } + if (time == timeout) { + retval = -ETIMEDOUT; + break; + } + } + + temp = readl(regs + RCAR_USB3_DL_CTRL); + temp &= ~RCAR_USB3_DL_CTRL_ENABLE; + writel(temp, regs + RCAR_USB3_DL_CTRL); + + for (time = 0; time < timeout; time++) { + val = readl(regs + RCAR_USB3_DL_CTRL); + if (val & RCAR_USB3_DL_CTRL_FW_SUCCESS) { + retval = 0; + break; + } + udelay(1); + } + if (time == timeout) + retval = -ETIMEDOUT; + + release_firmware(fw); + + return retval; +} + +/* This function needs to initialize a "phy" of usb before */ +int xhci_rcar_init_quirk(struct usb_hcd *hcd) +{ + /* If hcd->regs is NULL, we don't just call the following function */ + if (!hcd->regs) + return 0; + + return xhci_rcar_download_firmware(hcd->self.controller, hcd->regs); +} diff --git a/drivers/usb/host/xhci-rcar.h b/drivers/usb/host/xhci-rcar.h new file mode 100644 index 000000000000..58501256715d --- /dev/null +++ b/drivers/usb/host/xhci-rcar.h @@ -0,0 +1,27 @@ +/* + * drivers/usb/host/xhci-rcar.h + * + * Copyright (C) 2014 Renesas Electronics Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#ifndef _XHCI_RCAR_H +#define _XHCI_RCAR_H + +#if IS_ENABLED(CONFIG_USB_XHCI_RCAR) +void xhci_rcar_start(struct usb_hcd *hcd); +int xhci_rcar_init_quirk(struct usb_hcd *hcd); +#else +static inline void xhci_rcar_start(struct usb_hcd *hcd) +{ +} + +static inline int xhci_rcar_init_quirk(struct usb_hcd *hcd) +{ + return 0; +} +#endif +#endif /* _XHCI_RCAR_H */ diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 749fc68eb5c1..60fb52ae864b 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1118,6 +1118,10 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id, if (xhci->quirks & XHCI_RESET_EP_QUIRK) { struct xhci_command *command; command = xhci_alloc_command(xhci, false, false, GFP_ATOMIC); + if (!command) { + xhci_warn(xhci, "WARN Cannot submit cfg ep: ENOMEM\n"); + return; + } xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, "Queueing configure endpoint command"); xhci_queue_configure_endpoint(xhci, command, diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 7436d5f5e67a..b6f21175b872 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1553,6 +1553,10 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) */ if (!(ep->ep_state & EP_HALT_PENDING)) { command = xhci_alloc_command(xhci, false, false, GFP_ATOMIC); + if (!command) { + ret = -ENOMEM; + goto done; + } ep->ep_state |= EP_HALT_PENDING; ep->stop_cmds_pending++; ep->stop_cmd_timer.expires = jiffies + @@ -1586,12 +1590,10 @@ int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev, struct xhci_hcd *xhci; struct xhci_container_ctx *in_ctx, *out_ctx; struct xhci_input_control_ctx *ctrl_ctx; - struct xhci_slot_ctx *slot_ctx; - unsigned int last_ctx; unsigned int ep_index; struct xhci_ep_ctx *ep_ctx; u32 drop_flag; - u32 new_add_flags, new_drop_flags, new_slot_info; + u32 new_add_flags, new_drop_flags; int ret; ret = xhci_check_args(hcd, udev, ep, 1, true, __func__); @@ -1638,24 +1640,13 @@ int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev, ctrl_ctx->add_flags &= cpu_to_le32(~drop_flag); new_add_flags = le32_to_cpu(ctrl_ctx->add_flags); - last_ctx = xhci_last_valid_endpoint(le32_to_cpu(ctrl_ctx->add_flags)); - slot_ctx = xhci_get_slot_ctx(xhci, in_ctx); - /* Update the last valid endpoint context, if we deleted the last one */ - if ((le32_to_cpu(slot_ctx->dev_info) & LAST_CTX_MASK) > - LAST_CTX(last_ctx)) { - slot_ctx->dev_info &= cpu_to_le32(~LAST_CTX_MASK); - slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(last_ctx)); - } - new_slot_info = le32_to_cpu(slot_ctx->dev_info); - xhci_endpoint_zero(xhci, xhci->devs[udev->slot_id], ep); - xhci_dbg(xhci, "drop ep 0x%x, slot id %d, new drop flags = %#x, new add flags = %#x, new slot info = %#x\n", + xhci_dbg(xhci, "drop ep 0x%x, slot id %d, new drop flags = %#x, new add flags = %#x\n", (unsigned int) ep->desc.bEndpointAddress, udev->slot_id, (unsigned int) new_drop_flags, - (unsigned int) new_add_flags, - (unsigned int) new_slot_info); + (unsigned int) new_add_flags); return 0; } @@ -1678,11 +1669,9 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, struct xhci_hcd *xhci; struct xhci_container_ctx *in_ctx, *out_ctx; unsigned int ep_index; - struct xhci_slot_ctx *slot_ctx; struct xhci_input_control_ctx *ctrl_ctx; u32 added_ctxs; - unsigned int last_ctx; - u32 new_add_flags, new_drop_flags, new_slot_info; + u32 new_add_flags, new_drop_flags; struct xhci_virt_device *virt_dev; int ret = 0; @@ -1697,7 +1686,6 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, return -ENODEV; added_ctxs = xhci_get_endpoint_flag(&ep->desc); - last_ctx = xhci_last_valid_endpoint(added_ctxs); if (added_ctxs == SLOT_FLAG || added_ctxs == EP0_FLAG) { /* FIXME when we have to issue an evaluate endpoint command to * deal with ep0 max packet size changing once we get the @@ -1763,24 +1751,14 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, */ new_drop_flags = le32_to_cpu(ctrl_ctx->drop_flags); - slot_ctx = xhci_get_slot_ctx(xhci, in_ctx); - /* Update the last valid endpoint context, if we just added one past */ - if ((le32_to_cpu(slot_ctx->dev_info) & LAST_CTX_MASK) < - LAST_CTX(last_ctx)) { - slot_ctx->dev_info &= cpu_to_le32(~LAST_CTX_MASK); - slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(last_ctx)); - } - new_slot_info = le32_to_cpu(slot_ctx->dev_info); - /* Store the usb_device pointer for later use */ ep->hcpriv = udev; - xhci_dbg(xhci, "add ep 0x%x, slot id %d, new drop flags = %#x, new add flags = %#x, new slot info = %#x\n", + xhci_dbg(xhci, "add ep 0x%x, slot id %d, new drop flags = %#x, new add flags = %#x\n", (unsigned int) ep->desc.bEndpointAddress, udev->slot_id, (unsigned int) new_drop_flags, - (unsigned int) new_add_flags, - (unsigned int) new_slot_info); + (unsigned int) new_add_flags); return 0; } @@ -1830,15 +1808,15 @@ static int xhci_configure_endpoint_result(struct xhci_hcd *xhci, ret = -ETIME; break; case COMP_ENOMEM: - dev_warn(&udev->dev, "Not enough host controller resources " - "for new device state.\n"); + dev_warn(&udev->dev, + "Not enough host controller resources for new device state.\n"); ret = -ENOMEM; /* FIXME: can we allocate more resources for the HC? */ break; case COMP_BW_ERR: case COMP_2ND_BW_ERR: - dev_warn(&udev->dev, "Not enough bandwidth " - "for new device state.\n"); + dev_warn(&udev->dev, + "Not enough bandwidth for new device state.\n"); ret = -ENOSPC; /* FIXME: can we go back to the old state? */ break; @@ -1850,8 +1828,8 @@ static int xhci_configure_endpoint_result(struct xhci_hcd *xhci, ret = -EINVAL; break; case COMP_DEV_ERR: - dev_warn(&udev->dev, "ERROR: Incompatible device for endpoint " - "configure command.\n"); + dev_warn(&udev->dev, + "ERROR: Incompatible device for endpoint configure command.\n"); ret = -ENODEV; break; case COMP_SUCCESS: @@ -1860,8 +1838,8 @@ static int xhci_configure_endpoint_result(struct xhci_hcd *xhci, ret = 0; break; default: - xhci_err(xhci, "ERROR: unexpected command completion " - "code 0x%x.\n", *cmd_status); + xhci_err(xhci, "ERROR: unexpected command completion code 0x%x.\n", + *cmd_status); ret = -EINVAL; break; } @@ -1881,24 +1859,24 @@ static int xhci_evaluate_context_result(struct xhci_hcd *xhci, ret = -ETIME; break; case COMP_EINVAL: - dev_warn(&udev->dev, "WARN: xHCI driver setup invalid evaluate " - "context command.\n"); + dev_warn(&udev->dev, + "WARN: xHCI driver setup invalid evaluate context command.\n"); ret = -EINVAL; break; case COMP_EBADSLT: - dev_warn(&udev->dev, "WARN: slot not enabled for" - "evaluate context command.\n"); + dev_warn(&udev->dev, + "WARN: slot not enabled for evaluate context command.\n"); ret = -EINVAL; break; case COMP_CTX_STATE: - dev_warn(&udev->dev, "WARN: invalid context state for " - "evaluate context command.\n"); + dev_warn(&udev->dev, + "WARN: invalid context state for evaluate context command.\n"); xhci_dbg_ctx(xhci, virt_dev->out_ctx, 1); ret = -EINVAL; break; case COMP_DEV_ERR: - dev_warn(&udev->dev, "ERROR: Incompatible device for evaluate " - "context command.\n"); + dev_warn(&udev->dev, + "ERROR: Incompatible device for evaluate context command.\n"); ret = -ENODEV; break; case COMP_MEL_ERR: @@ -1912,8 +1890,8 @@ static int xhci_evaluate_context_result(struct xhci_hcd *xhci, ret = 0; break; default: - xhci_err(xhci, "ERROR: unexpected command completion " - "code 0x%x.\n", *cmd_status); + xhci_err(xhci, "ERROR: unexpected command completion code 0x%x.\n", + *cmd_status); ret = -EINVAL; break; } @@ -2750,8 +2728,19 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) ret = 0; goto command_cleanup; } - xhci_dbg(xhci, "New Input Control Context:\n"); + /* Fix up Context Entries field. Minimum value is EP0 == BIT(1). */ slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx); + for (i = 31; i >= 1; i--) { + __le32 le32 = cpu_to_le32(BIT(i)); + + if ((virt_dev->eps[i-1].ring && !(ctrl_ctx->drop_flags & le32)) + || (ctrl_ctx->add_flags & le32) || i == 1) { + slot_ctx->dev_info &= cpu_to_le32(~LAST_CTX_MASK); + slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(i)); + break; + } + } + xhci_dbg(xhci, "New Input Control Context:\n"); xhci_dbg_ctx(xhci, virt_dev->in_ctx, LAST_CTX_TO_EP_NUM(le32_to_cpu(slot_ctx->dev_info))); @@ -3163,7 +3152,8 @@ int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev, num_streams); /* MaxPSASize value 0 (2 streams) means streams are not supported */ - if (HCC_MAX_PSA(xhci->hcc_params) < 4) { + if ((xhci->quirks & XHCI_BROKEN_STREAMS) || + HCC_MAX_PSA(xhci->hcc_params) < 4) { xhci_dbg(xhci, "xHCI controller does not support streams.\n"); return -ENOSYS; } @@ -4303,8 +4293,7 @@ static u16 xhci_get_timeout_no_hub_lpm(struct usb_device *udev, return USB3_LPM_DISABLED; } -/* Returns the hub-encoded U1 timeout value. - * The U1 timeout should be the maximum of the following values: +/* The U1 timeout should be the maximum of the following values: * - For control endpoints, U1 system exit latency (SEL) * 3 * - For bulk endpoints, U1 SEL * 5 * - For interrupt endpoints: @@ -4312,7 +4301,8 @@ static u16 xhci_get_timeout_no_hub_lpm(struct usb_device *udev, * - Periodic EPs, max(105% of bInterval, U1 SEL * 2) * - For isochronous endpoints, max(105% of bInterval, U1 SEL * 2) */ -static u16 xhci_calculate_intel_u1_timeout(struct usb_device *udev, +static unsigned long long xhci_calculate_intel_u1_timeout( + struct usb_device *udev, struct usb_endpoint_descriptor *desc) { unsigned long long timeout_ns; @@ -4344,11 +4334,28 @@ static u16 xhci_calculate_intel_u1_timeout(struct usb_device *udev, return 0; } - /* The U1 timeout is encoded in 1us intervals. */ - timeout_ns = DIV_ROUND_UP_ULL(timeout_ns, 1000); - /* Don't return a timeout of zero, because that's USB3_LPM_DISABLED. */ + return timeout_ns; +} + +/* Returns the hub-encoded U1 timeout value. */ +static u16 xhci_calculate_u1_timeout(struct xhci_hcd *xhci, + struct usb_device *udev, + struct usb_endpoint_descriptor *desc) +{ + unsigned long long timeout_ns; + + if (xhci->quirks & XHCI_INTEL_HOST) + timeout_ns = xhci_calculate_intel_u1_timeout(udev, desc); + else + timeout_ns = udev->u1_params.sel; + + /* The U1 timeout is encoded in 1us intervals. + * Don't return a timeout of zero, because that's USB3_LPM_DISABLED. + */ if (timeout_ns == USB3_LPM_DISABLED) - timeout_ns++; + timeout_ns = 1; + else + timeout_ns = DIV_ROUND_UP_ULL(timeout_ns, 1000); /* If the necessary timeout value is bigger than what we can set in the * USB 3.0 hub, we have to disable hub-initiated U1. @@ -4360,14 +4367,14 @@ static u16 xhci_calculate_intel_u1_timeout(struct usb_device *udev, return xhci_get_timeout_no_hub_lpm(udev, USB3_LPM_U1); } -/* Returns the hub-encoded U2 timeout value. - * The U2 timeout should be the maximum of: +/* The U2 timeout should be the maximum of: * - 10 ms (to avoid the bandwidth impact on the scheduler) * - largest bInterval of any active periodic endpoint (to avoid going * into lower power link states between intervals). * - the U2 Exit Latency of the device */ -static u16 xhci_calculate_intel_u2_timeout(struct usb_device *udev, +static unsigned long long xhci_calculate_intel_u2_timeout( + struct usb_device *udev, struct usb_endpoint_descriptor *desc) { unsigned long long timeout_ns; @@ -4383,6 +4390,21 @@ static u16 xhci_calculate_intel_u2_timeout(struct usb_device *udev, if (u2_del_ns > timeout_ns) timeout_ns = u2_del_ns; + return timeout_ns; +} + +/* Returns the hub-encoded U2 timeout value. */ +static u16 xhci_calculate_u2_timeout(struct xhci_hcd *xhci, + struct usb_device *udev, + struct usb_endpoint_descriptor *desc) +{ + unsigned long long timeout_ns; + + if (xhci->quirks & XHCI_INTEL_HOST) + timeout_ns = xhci_calculate_intel_u2_timeout(udev, desc); + else + timeout_ns = udev->u2_params.sel; + /* The U2 timeout is encoded in 256us intervals */ timeout_ns = DIV_ROUND_UP_ULL(timeout_ns, 256 * 1000); /* If the necessary timeout value is bigger than what we can set in the @@ -4401,13 +4423,10 @@ static u16 xhci_call_host_update_timeout_for_endpoint(struct xhci_hcd *xhci, enum usb3_link_state state, u16 *timeout) { - if (state == USB3_LPM_U1) { - if (xhci->quirks & XHCI_INTEL_HOST) - return xhci_calculate_intel_u1_timeout(udev, desc); - } else { - if (xhci->quirks & XHCI_INTEL_HOST) - return xhci_calculate_intel_u2_timeout(udev, desc); - } + if (state == USB3_LPM_U1) + return xhci_calculate_u1_timeout(xhci, udev, desc); + else if (state == USB3_LPM_U2) + return xhci_calculate_u2_timeout(xhci, udev, desc); return USB3_LPM_DISABLED; } @@ -4484,7 +4503,8 @@ static int xhci_check_tier_policy(struct xhci_hcd *xhci, { if (xhci->quirks & XHCI_INTEL_HOST) return xhci_check_intel_tier_policy(udev, state); - return -EINVAL; + else + return 0; } /* Returns the U1 or U2 timeout that should be enabled. diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 9ffecd56600d..dace5152e179 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1558,6 +1558,8 @@ struct xhci_hcd { #define XHCI_PLAT (1 << 16) #define XHCI_SLOW_SUSPEND (1 << 17) #define XHCI_SPURIOUS_WAKEUP (1 << 18) +/* For controllers with a broken beyond repair streams implementation */ +#define XHCI_BROKEN_STREAMS (1 << 19) unsigned int num_active_eps; unsigned int limit_active_eps; /* There are two roothubs to keep track of bus suspend info for */ diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index 1bca274dc3b5..76d77206e011 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -248,3 +248,10 @@ config USB_HSIC_USB3503 select REGMAP_I2C help This option enables support for SMSC USB3503 HSIC to USB 2.0 Driver. + +config USB_LINK_LAYER_TEST + tristate "USB Link Layer Test driver" + help + This driver is for generating specific traffic for Super Speed Link + Layer Test Device. Say Y only when you want to conduct USB Super Speed + Link Layer Test for host controllers. diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile index e748fd5dbe94..65b0402c1ca1 100644 --- a/drivers/usb/misc/Makefile +++ b/drivers/usb/misc/Makefile @@ -27,3 +27,4 @@ obj-$(CONFIG_USB_YUREX) += yurex.o obj-$(CONFIG_USB_HSIC_USB3503) += usb3503.o obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga/ +obj-$(CONFIG_USB_LINK_LAYER_TEST) += lvstest.o diff --git a/drivers/usb/misc/lvstest.c b/drivers/usb/misc/lvstest.c new file mode 100644 index 000000000000..7d589c156fb1 --- /dev/null +++ b/drivers/usb/misc/lvstest.c @@ -0,0 +1,460 @@ +/* + * drivers/usb/misc/lvstest.c + * + * Test pattern generation for Link Layer Validation System Tests + * + * Copyright (C) 2014 ST Microelectronics + * Pratyush Anand <pratyush.anand@st.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/usb.h> +#include <linux/usb/ch11.h> +#include <linux/usb/hcd.h> +#include <linux/usb/phy.h> + +struct lvs_rh { + /* root hub interface */ + struct usb_interface *intf; + /* if lvs device connected */ + bool present; + /* port no at which lvs device is present */ + int portnum; + /* urb buffer */ + u8 buffer[8]; + /* class descriptor */ + struct usb_hub_descriptor descriptor; + /* urb for polling interrupt pipe */ + struct urb *urb; + /* LVS RH work queue */ + struct workqueue_struct *rh_queue; + /* LVH RH work */ + struct work_struct rh_work; + /* RH port status */ + struct usb_port_status port_status; +}; + +static struct usb_device *create_lvs_device(struct usb_interface *intf) +{ + struct usb_device *udev, *hdev; + struct usb_hcd *hcd; + struct lvs_rh *lvs = usb_get_intfdata(intf); + + if (!lvs->present) { + dev_err(&intf->dev, "No LVS device is present\n"); + return NULL; + } + + hdev = interface_to_usbdev(intf); + hcd = bus_to_hcd(hdev->bus); + + udev = usb_alloc_dev(hdev, hdev->bus, lvs->portnum); + if (!udev) { + dev_err(&intf->dev, "Could not allocate lvs udev\n"); + return NULL; + } + udev->speed = USB_SPEED_SUPER; + udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512); + usb_set_device_state(udev, USB_STATE_DEFAULT); + + if (hcd->driver->enable_device) { + if (hcd->driver->enable_device(hcd, udev) < 0) { + dev_err(&intf->dev, "Failed to enable\n"); + usb_put_dev(udev); + return NULL; + } + } + + return udev; +} + +static void destroy_lvs_device(struct usb_device *udev) +{ + struct usb_device *hdev = udev->parent; + struct usb_hcd *hcd = bus_to_hcd(hdev->bus); + + if (hcd->driver->free_dev) + hcd->driver->free_dev(hcd, udev); + + usb_put_dev(udev); +} + +static int lvs_rh_clear_port_feature(struct usb_device *hdev, + int port1, int feature) +{ + return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0), + USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature, port1, + NULL, 0, 1000); +} + +static int lvs_rh_set_port_feature(struct usb_device *hdev, + int port1, int feature) +{ + return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0), + USB_REQ_SET_FEATURE, USB_RT_PORT, feature, port1, + NULL, 0, 1000); +} + +static ssize_t u3_entry_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usb_device *hdev = interface_to_usbdev(intf); + struct lvs_rh *lvs = usb_get_intfdata(intf); + struct usb_device *udev; + int ret; + + udev = create_lvs_device(intf); + if (!udev) { + dev_err(dev, "failed to create lvs device\n"); + return -ENOMEM; + } + + ret = lvs_rh_set_port_feature(hdev, lvs->portnum, + USB_PORT_FEAT_SUSPEND); + if (ret < 0) + dev_err(dev, "can't issue U3 entry %d\n", ret); + + destroy_lvs_device(udev); + + if (ret < 0) + return ret; + + return count; +} +static DEVICE_ATTR_WO(u3_entry); + +static ssize_t u3_exit_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usb_device *hdev = interface_to_usbdev(intf); + struct lvs_rh *lvs = usb_get_intfdata(intf); + struct usb_device *udev; + int ret; + + udev = create_lvs_device(intf); + if (!udev) { + dev_err(dev, "failed to create lvs device\n"); + return -ENOMEM; + } + + ret = lvs_rh_clear_port_feature(hdev, lvs->portnum, + USB_PORT_FEAT_SUSPEND); + if (ret < 0) + dev_err(dev, "can't issue U3 exit %d\n", ret); + + destroy_lvs_device(udev); + + if (ret < 0) + return ret; + + return count; +} +static DEVICE_ATTR_WO(u3_exit); + +static ssize_t hot_reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usb_device *hdev = interface_to_usbdev(intf); + struct lvs_rh *lvs = usb_get_intfdata(intf); + int ret; + + ret = lvs_rh_set_port_feature(hdev, lvs->portnum, + USB_PORT_FEAT_RESET); + if (ret < 0) { + dev_err(dev, "can't issue hot reset %d\n", ret); + return ret; + } + + return count; +} +static DEVICE_ATTR_WO(hot_reset); + +static ssize_t u2_timeout_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usb_device *hdev = interface_to_usbdev(intf); + struct lvs_rh *lvs = usb_get_intfdata(intf); + unsigned long val; + int ret; + + ret = kstrtoul(buf, 10, &val); + if (ret < 0) { + dev_err(dev, "couldn't parse string %d\n", ret); + return ret; + } + + if (val < 0 || val > 127) + return -EINVAL; + + ret = lvs_rh_set_port_feature(hdev, lvs->portnum | (val << 8), + USB_PORT_FEAT_U2_TIMEOUT); + if (ret < 0) { + dev_err(dev, "Error %d while setting U2 timeout %ld\n", ret, val); + return ret; + } + + return count; +} +static DEVICE_ATTR_WO(u2_timeout); + +static ssize_t u1_timeout_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usb_device *hdev = interface_to_usbdev(intf); + struct lvs_rh *lvs = usb_get_intfdata(intf); + unsigned long val; + int ret; + + ret = kstrtoul(buf, 10, &val); + if (ret < 0) { + dev_err(dev, "couldn't parse string %d\n", ret); + return ret; + } + + if (val < 0 || val > 127) + return -EINVAL; + + ret = lvs_rh_set_port_feature(hdev, lvs->portnum | (val << 8), + USB_PORT_FEAT_U1_TIMEOUT); + if (ret < 0) { + dev_err(dev, "Error %d while setting U1 timeout %ld\n", ret, val); + return ret; + } + + return count; +} +static DEVICE_ATTR_WO(u1_timeout); + +static ssize_t get_dev_desc_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usb_device *udev; + struct usb_device_descriptor *descriptor; + int ret; + + descriptor = kmalloc(sizeof(*descriptor), GFP_KERNEL); + if (!descriptor) { + dev_err(dev, "failed to allocate descriptor memory\n"); + return -ENOMEM; + } + + udev = create_lvs_device(intf); + if (!udev) { + dev_err(dev, "failed to create lvs device\n"); + ret = -ENOMEM; + goto free_desc; + } + + ret = usb_control_msg(udev, (PIPE_CONTROL << 30) | USB_DIR_IN, + USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, USB_DT_DEVICE << 8, + 0, descriptor, sizeof(*descriptor), + USB_CTRL_GET_TIMEOUT); + if (ret < 0) + dev_err(dev, "can't read device descriptor %d\n", ret); + + destroy_lvs_device(udev); + +free_desc: + kfree(descriptor); + + if (ret < 0) + return ret; + + return count; +} +static DEVICE_ATTR_WO(get_dev_desc); + +static struct attribute *lvs_attributes[] = { + &dev_attr_get_dev_desc.attr, + &dev_attr_u1_timeout.attr, + &dev_attr_u2_timeout.attr, + &dev_attr_hot_reset.attr, + &dev_attr_u3_entry.attr, + &dev_attr_u3_exit.attr, + NULL +}; + +static const struct attribute_group lvs_attr_group = { + .attrs = lvs_attributes, +}; + +static void lvs_rh_work(struct work_struct *work) +{ + struct lvs_rh *lvs = container_of(work, struct lvs_rh, rh_work); + struct usb_interface *intf = lvs->intf; + struct usb_device *hdev = interface_to_usbdev(intf); + struct usb_hcd *hcd = bus_to_hcd(hdev->bus); + struct usb_hub_descriptor *descriptor = &lvs->descriptor; + struct usb_port_status *port_status = &lvs->port_status; + int i, ret = 0; + u16 portchange; + + /* Examine each root port */ + for (i = 1; i <= descriptor->bNbrPorts; i++) { + ret = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0), + USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, i, + port_status, sizeof(*port_status), 1000); + if (ret < 4) + continue; + + portchange = le16_to_cpu(port_status->wPortChange); + + if (portchange & USB_PORT_STAT_C_LINK_STATE) + lvs_rh_clear_port_feature(hdev, i, + USB_PORT_FEAT_C_PORT_LINK_STATE); + if (portchange & USB_PORT_STAT_C_ENABLE) + lvs_rh_clear_port_feature(hdev, i, + USB_PORT_FEAT_C_ENABLE); + if (portchange & USB_PORT_STAT_C_RESET) + lvs_rh_clear_port_feature(hdev, i, + USB_PORT_FEAT_C_RESET); + if (portchange & USB_PORT_STAT_C_BH_RESET) + lvs_rh_clear_port_feature(hdev, i, + USB_PORT_FEAT_C_BH_PORT_RESET); + if (portchange & USB_PORT_STAT_C_CONNECTION) { + lvs_rh_clear_port_feature(hdev, i, + USB_PORT_FEAT_C_CONNECTION); + + if (le16_to_cpu(port_status->wPortStatus) & + USB_PORT_STAT_CONNECTION) { + lvs->present = true; + lvs->portnum = i; + if (hcd->phy) + usb_phy_notify_connect(hcd->phy, + USB_SPEED_SUPER); + } else { + lvs->present = false; + if (hcd->phy) + usb_phy_notify_disconnect(hcd->phy, + USB_SPEED_SUPER); + } + break; + } + } + + ret = usb_submit_urb(lvs->urb, GFP_KERNEL); + if (ret != 0 && ret != -ENODEV && ret != -EPERM) + dev_err(&intf->dev, "urb resubmit error %d\n", ret); +} + +static void lvs_rh_irq(struct urb *urb) +{ + struct lvs_rh *lvs = urb->context; + + queue_work(lvs->rh_queue, &lvs->rh_work); +} + +static int lvs_rh_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *hdev; + struct usb_host_interface *desc; + struct usb_endpoint_descriptor *endpoint; + struct lvs_rh *lvs; + unsigned int pipe; + int ret, maxp; + + hdev = interface_to_usbdev(intf); + desc = intf->cur_altsetting; + endpoint = &desc->endpoint[0].desc; + + /* valid only for SS root hub */ + if (hdev->descriptor.bDeviceProtocol != USB_HUB_PR_SS || hdev->parent) { + dev_err(&intf->dev, "Bind LVS driver with SS root Hub only\n"); + return -EINVAL; + } + + lvs = devm_kzalloc(&intf->dev, sizeof(*lvs), GFP_KERNEL); + if (!lvs) + return -ENOMEM; + + lvs->intf = intf; + usb_set_intfdata(intf, lvs); + + /* how many number of ports this root hub has */ + ret = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0), + USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB, + USB_DT_SS_HUB << 8, 0, &lvs->descriptor, + USB_DT_SS_HUB_SIZE, USB_CTRL_GET_TIMEOUT); + if (ret < (USB_DT_HUB_NONVAR_SIZE + 2)) { + dev_err(&hdev->dev, "wrong root hub descriptor read %d\n", ret); + return ret; + } + + /* submit urb to poll interrupt endpoint */ + lvs->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!lvs->urb) { + dev_err(&intf->dev, "couldn't allocate lvs urb\n"); + return -ENOMEM; + } + + lvs->rh_queue = create_singlethread_workqueue("lvs_rh_queue"); + if (!lvs->rh_queue) { + dev_err(&intf->dev, "couldn't create workqueue\n"); + ret = -ENOMEM; + goto free_urb; + } + + INIT_WORK(&lvs->rh_work, lvs_rh_work); + + ret = sysfs_create_group(&intf->dev.kobj, &lvs_attr_group); + if (ret < 0) { + dev_err(&intf->dev, "Failed to create sysfs node %d\n", ret); + goto destroy_queue; + } + + pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress); + maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe)); + usb_fill_int_urb(lvs->urb, hdev, pipe, &lvs->buffer[0], maxp, + lvs_rh_irq, lvs, endpoint->bInterval); + + ret = usb_submit_urb(lvs->urb, GFP_KERNEL); + if (ret < 0) { + dev_err(&intf->dev, "couldn't submit lvs urb %d\n", ret); + goto sysfs_remove; + } + + return ret; + +sysfs_remove: + sysfs_remove_group(&intf->dev.kobj, &lvs_attr_group); +destroy_queue: + destroy_workqueue(lvs->rh_queue); +free_urb: + usb_free_urb(lvs->urb); + return ret; +} + +static void lvs_rh_disconnect(struct usb_interface *intf) +{ + struct lvs_rh *lvs = usb_get_intfdata(intf); + + sysfs_remove_group(&intf->dev.kobj, &lvs_attr_group); + destroy_workqueue(lvs->rh_queue); + usb_free_urb(lvs->urb); +} + +static struct usb_driver lvs_driver = { + .name = "lvs", + .probe = lvs_rh_probe, + .disconnect = lvs_rh_disconnect, +}; + +module_usb_driver(lvs_driver); + +MODULE_DESCRIPTION("Link Layer Validation System Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c index f43c61989cef..47cb143716a1 100644 --- a/drivers/usb/misc/usb3503.c +++ b/drivers/usb/misc/usb3503.c @@ -149,8 +149,6 @@ static int usb3503_switch_mode(struct usb3503 *hub, enum usb3503_mode mode) case USB3503_MODE_STANDBY: usb3503_reset(hub, 0); - - hub->mode = mode; dev_info(dev, "switched to STANDBY mode\n"); break; @@ -192,7 +190,8 @@ static int usb3503_probe(struct usb3503 *hub) clk = devm_clk_get(dev, "refclk"); if (IS_ERR(clk) && PTR_ERR(clk) != -ENOENT) { - dev_err(dev, "unable to request refclk (%d)\n", err); + dev_err(dev, "unable to request refclk (%ld)\n", + PTR_ERR(clk)); return PTR_ERR(clk); } @@ -346,6 +345,37 @@ static int usb3503_platform_probe(struct platform_device *pdev) return usb3503_probe(hub); } +#ifdef CONFIG_PM_SLEEP +static int usb3503_i2c_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct usb3503 *hub = i2c_get_clientdata(client); + + usb3503_switch_mode(hub, USB3503_MODE_STANDBY); + + if (hub->clk) + clk_disable_unprepare(hub->clk); + + return 0; +} + +static int usb3503_i2c_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct usb3503 *hub = i2c_get_clientdata(client); + + if (hub->clk) + clk_prepare_enable(hub->clk); + + usb3503_switch_mode(hub, hub->mode); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(usb3503_i2c_pm_ops, usb3503_i2c_suspend, + usb3503_i2c_resume); + static const struct i2c_device_id usb3503_id[] = { { USB3503_I2C_NAME, 0 }, { } @@ -364,6 +394,7 @@ MODULE_DEVICE_TABLE(of, usb3503_of_match); static struct i2c_driver usb3503_i2c_driver = { .driver = { .name = USB3503_I2C_NAME, + .pm = &usb3503_i2c_pm_ops, .of_match_table = of_match_ptr(usb3503_of_match), }, .probe = usb3503_i2c_probe, diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index d40d5f0b5528..ac4422b33dcd 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -455,7 +455,7 @@ static int bfin_probe(struct platform_device *pdev) int ret = -ENOMEM; - glue = kzalloc(sizeof(*glue), GFP_KERNEL); + glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); if (!glue) { dev_err(&pdev->dev, "failed to allocate glue context\n"); goto err0; @@ -464,7 +464,7 @@ static int bfin_probe(struct platform_device *pdev) musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO); if (!musb) { dev_err(&pdev->dev, "failed to allocate musb device\n"); - goto err1; + goto err0; } musb->dev.parent = &pdev->dev; @@ -478,7 +478,7 @@ static int bfin_probe(struct platform_device *pdev) glue->phy = usb_phy_generic_register(); if (IS_ERR(glue->phy)) - goto err2; + goto err1; platform_set_drvdata(pdev, glue); memset(musb_resources, 0x00, sizeof(*musb_resources) * @@ -498,31 +498,28 @@ static int bfin_probe(struct platform_device *pdev) ARRAY_SIZE(musb_resources)); if (ret) { dev_err(&pdev->dev, "failed to add resources\n"); - goto err3; + goto err2; } ret = platform_device_add_data(musb, pdata, sizeof(*pdata)); if (ret) { dev_err(&pdev->dev, "failed to add platform_data\n"); - goto err3; + goto err2; } ret = platform_device_add(musb); if (ret) { dev_err(&pdev->dev, "failed to register musb device\n"); - goto err3; + goto err2; } return 0; -err3: - usb_phy_generic_unregister(glue->phy); - err2: - platform_device_put(musb); + usb_phy_generic_unregister(glue->phy); err1: - kfree(glue); + platform_device_put(musb); err0: return ret; @@ -534,7 +531,6 @@ static int bfin_remove(struct platform_device *pdev) platform_device_unregister(glue->musb); usb_phy_generic_unregister(glue->phy); - kfree(glue); return 0; } diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index de8492b06e46..110b78415bf0 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -519,23 +519,23 @@ static int davinci_probe(struct platform_device *pdev) int ret = -ENOMEM; - glue = kzalloc(sizeof(*glue), GFP_KERNEL); + glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); if (!glue) { dev_err(&pdev->dev, "failed to allocate glue context\n"); goto err0; } - clk = clk_get(&pdev->dev, "usb"); + clk = devm_clk_get(&pdev->dev, "usb"); if (IS_ERR(clk)) { dev_err(&pdev->dev, "failed to get clock\n"); ret = PTR_ERR(clk); - goto err3; + goto err0; } ret = clk_enable(clk); if (ret) { dev_err(&pdev->dev, "failed to enable clock\n"); - goto err4; + goto err0; } glue->dev = &pdev->dev; @@ -579,20 +579,14 @@ static int davinci_probe(struct platform_device *pdev) if (IS_ERR(musb)) { ret = PTR_ERR(musb); dev_err(&pdev->dev, "failed to register musb device: %d\n", ret); - goto err5; + goto err1; } return 0; -err5: +err1: clk_disable(clk); -err4: - clk_put(clk); - -err3: - kfree(glue); - err0: return ret; } @@ -604,8 +598,6 @@ static int davinci_remove(struct platform_device *pdev) platform_device_unregister(glue->musb); usb_phy_generic_unregister(); clk_disable(glue->clk); - clk_put(glue->clk); - kfree(glue); return 0; } diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c index 5f30537f1927..d1187290d4e3 100644 --- a/drivers/usb/musb/jz4740.c +++ b/drivers/usb/musb/jz4740.c @@ -19,6 +19,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/usb/usb_phy_generic.h> #include "musb_core.h" @@ -80,6 +81,7 @@ static struct musb_hdrc_platform_data jz4740_musb_platform_data = { static int jz4740_musb_init(struct musb *musb) { + usb_phy_generic_register(); musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (!musb->xceiv) { pr_err("HS UDC: no transceiver configured\n"); @@ -182,6 +184,7 @@ static int jz4740_remove(struct platform_device *pdev) struct jz4740_glue *glue = platform_get_drvdata(pdev); platform_device_unregister(glue->musb); + usb_phy_generic_unregister(pdev); clk_disable_unprepare(glue->clk); return 0; diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index eff3c5cf84f4..b841ee0bff06 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -850,7 +850,8 @@ b_host: /* handle babble condition */ if (int_usb & MUSB_INTR_BABBLE && is_host_active(musb)) - schedule_work(&musb->recover_work); + schedule_delayed_work(&musb->recover_work, + msecs_to_jiffies(100)); #if 0 /* REVISIT ... this would be for multiplexing periodic endpoints, or @@ -1517,7 +1518,7 @@ irqreturn_t musb_interrupt(struct musb *musb) devctl = musb_readb(musb->mregs, MUSB_DEVCTL); dev_dbg(musb->controller, "** IRQ %s usb%04x tx%04x rx%04x\n", - (devctl & MUSB_DEVCTL_HM) ? "host" : "peripheral", + is_host_active(musb) ? "host" : "peripheral", musb->int_usb, musb->int_tx, musb->int_rx); /* the core can interrupt us for multiple reasons; docs have @@ -1531,7 +1532,7 @@ irqreturn_t musb_interrupt(struct musb *musb) /* handle endpoint 0 first */ if (musb->int_tx & 1) { - if (devctl & MUSB_DEVCTL_HM) + if (is_host_active(musb)) retval |= musb_h_ep0_irq(musb); else retval |= musb_g_ep0_irq(musb); @@ -1545,7 +1546,7 @@ irqreturn_t musb_interrupt(struct musb *musb) /* musb_ep_select(musb->mregs, ep_num); */ /* REVISIT just retval = ep->rx_irq(...) */ retval = IRQ_HANDLED; - if (devctl & MUSB_DEVCTL_HM) + if (is_host_active(musb)) musb_host_rx(musb, ep_num); else musb_g_rx(musb, ep_num); @@ -1563,7 +1564,7 @@ irqreturn_t musb_interrupt(struct musb *musb) /* musb_ep_select(musb->mregs, ep_num); */ /* REVISIT just retval |= ep->tx_irq(...) */ retval = IRQ_HANDLED; - if (devctl & MUSB_DEVCTL_HM) + if (is_host_active(musb)) musb_host_tx(musb, ep_num); else musb_g_tx(musb, ep_num); @@ -1585,15 +1586,13 @@ MODULE_PARM_DESC(use_dma, "enable/disable use of DMA"); void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit) { - u8 devctl = musb_readb(musb->mregs, MUSB_DEVCTL); - /* called with controller lock already held */ if (!epnum) { #ifndef CONFIG_USB_TUSB_OMAP_DMA if (!is_cppi_enabled()) { /* endpoint 0 */ - if (devctl & MUSB_DEVCTL_HM) + if (is_host_active(musb)) musb_h_ep0_irq(musb); else musb_g_ep0_irq(musb); @@ -1602,13 +1601,13 @@ void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit) } else { /* endpoints 1..15 */ if (transmit) { - if (devctl & MUSB_DEVCTL_HM) + if (is_host_active(musb)) musb_host_tx(musb, epnum); else musb_g_tx(musb, epnum); } else { /* receive */ - if (devctl & MUSB_DEVCTL_HM) + if (is_host_active(musb)) musb_host_rx(musb, epnum); else musb_g_rx(musb, epnum); @@ -1753,20 +1752,22 @@ static void musb_irq_work(struct work_struct *data) /* Recover from babble interrupt conditions */ static void musb_recover_work(struct work_struct *data) { - struct musb *musb = container_of(data, struct musb, recover_work); - int status; + struct musb *musb = container_of(data, struct musb, recover_work.work); + int status, ret; - musb_platform_reset(musb); + ret = musb_platform_reset(musb); + if (ret) + return; usb_phy_vbus_off(musb->xceiv); - udelay(100); + usleep_range(100, 200); usb_phy_vbus_on(musb->xceiv); - udelay(100); + usleep_range(100, 200); /* - * When a babble condition occurs, the musb controller removes the - * session bit and the endpoint config is lost. + * When a babble condition occurs, the musb controller + * removes the session bit and the endpoint config is lost. */ if (musb->dyn_fifo) status = ep_config_from_table(musb); @@ -1945,7 +1946,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) /* Init IRQ workqueue before request_irq */ INIT_WORK(&musb->irq_work, musb_irq_work); - INIT_WORK(&musb->recover_work, musb_recover_work); + INIT_DELAYED_WORK(&musb->recover_work, musb_recover_work); INIT_DELAYED_WORK(&musb->deassert_reset_work, musb_deassert_reset); INIT_DELAYED_WORK(&musb->finish_resume_work, musb_host_finish_resume); @@ -2041,7 +2042,7 @@ fail4: fail3: cancel_work_sync(&musb->irq_work); - cancel_work_sync(&musb->recover_work); + cancel_delayed_work_sync(&musb->recover_work); cancel_delayed_work_sync(&musb->finish_resume_work); cancel_delayed_work_sync(&musb->deassert_reset_work); if (musb->dma_controller) @@ -2107,7 +2108,7 @@ static int musb_remove(struct platform_device *pdev) dma_controller_destroy(musb->dma_controller); cancel_work_sync(&musb->irq_work); - cancel_work_sync(&musb->recover_work); + cancel_delayed_work_sync(&musb->recover_work); cancel_delayed_work_sync(&musb->finish_resume_work); cancel_delayed_work_sync(&musb->deassert_reset_work); musb_free(musb); diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index d155a156f240..414e57a984bb 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -192,7 +192,7 @@ struct musb_platform_ops { int (*set_mode)(struct musb *musb, u8 mode); void (*try_idle)(struct musb *musb, unsigned long timeout); - void (*reset)(struct musb *musb); + int (*reset)(struct musb *musb); int (*vbus_status)(struct musb *musb); void (*set_vbus)(struct musb *musb, int on); @@ -297,7 +297,7 @@ struct musb { irqreturn_t (*isr)(int, void *); struct work_struct irq_work; - struct work_struct recover_work; + struct delayed_work recover_work; struct delayed_work deassert_reset_work; struct delayed_work finish_resume_work; u16 hwvers; @@ -555,10 +555,12 @@ static inline void musb_platform_try_idle(struct musb *musb, musb->ops->try_idle(musb, timeout); } -static inline void musb_platform_reset(struct musb *musb) +static inline int musb_platform_reset(struct musb *musb) { - if (musb->ops->reset) - musb->ops->reset(musb); + if (!musb->ops->reset) + return -EINVAL; + + return musb->ops->reset(musb); } static inline int musb_platform_get_vbus_status(struct musb *musb) diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c index 5341bb223b7c..47ae6455d073 100644 --- a/drivers/usb/musb/musb_cppi41.c +++ b/drivers/usb/musb/musb_cppi41.c @@ -39,7 +39,6 @@ struct cppi41_dma_channel { u32 transferred; u32 packet_sz; struct list_head tx_check; - struct work_struct dma_completion; }; #define MUSB_DMA_NUM_CHANNELS 15 @@ -74,15 +73,18 @@ static void save_rx_toggle(struct cppi41_dma_channel *cppi41_channel) static void update_rx_toggle(struct cppi41_dma_channel *cppi41_channel) { + struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep; + struct musb *musb = hw_ep->musb; u16 csr; u8 toggle; if (cppi41_channel->is_tx) return; - if (!is_host_active(cppi41_channel->controller->musb)) + if (!is_host_active(musb)) return; - csr = musb_readw(cppi41_channel->hw_ep->regs, MUSB_RXCSR); + musb_ep_select(musb->mregs, hw_ep->epnum); + csr = musb_readw(hw_ep->regs, MUSB_RXCSR); toggle = csr & MUSB_RXCSR_H_DATATOGGLE ? 1 : 0; /* @@ -107,24 +109,13 @@ static bool musb_is_tx_fifo_empty(struct musb_hw_ep *hw_ep) void __iomem *epio = musb->endpoints[epnum].regs; u16 csr; + musb_ep_select(musb->mregs, hw_ep->epnum); csr = musb_readw(epio, MUSB_TXCSR); if (csr & MUSB_TXCSR_TXPKTRDY) return false; return true; } -static bool is_isoc(struct musb_hw_ep *hw_ep, bool in) -{ - if (in && hw_ep->in_qh) { - if (hw_ep->in_qh->type == USB_ENDPOINT_XFER_ISOC) - return true; - } else if (hw_ep->out_qh) { - if (hw_ep->out_qh->type == USB_ENDPOINT_XFER_ISOC) - return true; - } - return false; -} - static void cppi41_dma_callback(void *private_data); static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel) @@ -139,6 +130,7 @@ static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel) cppi41_channel->channel.actual_len = cppi41_channel->transferred; cppi41_channel->channel.status = MUSB_DMA_STATUS_FREE; + cppi41_channel->channel.rx_packet_done = true; musb_dma_completion(musb, hw_ep->epnum, cppi41_channel->is_tx); } else { /* next iteration, reload */ @@ -172,6 +164,7 @@ static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel) dma_async_issue_pending(dc); if (!cppi41_channel->is_tx) { + musb_ep_select(musb->mregs, hw_ep->epnum); csr = musb_readw(epio, MUSB_RXCSR); csr |= MUSB_RXCSR_H_REQPKT; musb_writew(epio, MUSB_RXCSR, csr); @@ -179,32 +172,6 @@ static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel) } } -static void cppi_trans_done_work(struct work_struct *work) -{ - unsigned long flags; - struct cppi41_dma_channel *cppi41_channel = - container_of(work, struct cppi41_dma_channel, dma_completion); - struct cppi41_dma_controller *controller = cppi41_channel->controller; - struct musb *musb = controller->musb; - struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep; - bool empty; - - if (!cppi41_channel->is_tx && is_isoc(hw_ep, 1)) { - spin_lock_irqsave(&musb->lock, flags); - cppi41_trans_done(cppi41_channel); - spin_unlock_irqrestore(&musb->lock, flags); - } else { - empty = musb_is_tx_fifo_empty(hw_ep); - if (empty) { - spin_lock_irqsave(&musb->lock, flags); - cppi41_trans_done(cppi41_channel); - spin_unlock_irqrestore(&musb->lock, flags); - } else { - schedule_work(&cppi41_channel->dma_completion); - } - } -} - static enum hrtimer_restart cppi41_recheck_tx_req(struct hrtimer *timer) { struct cppi41_dma_controller *controller; @@ -233,7 +200,7 @@ static enum hrtimer_restart cppi41_recheck_tx_req(struct hrtimer *timer) if (!list_empty(&controller->early_tx_list)) { ret = HRTIMER_RESTART; hrtimer_forward_now(&controller->early_tx, - ktime_set(0, 150 * NSEC_PER_USEC)); + ktime_set(0, 50 * NSEC_PER_USEC)); } spin_unlock_irqrestore(&musb->lock, flags); @@ -268,14 +235,6 @@ static void cppi41_dma_callback(void *private_data) transferred < cppi41_channel->packet_sz) cppi41_channel->prog_len = 0; - if (!cppi41_channel->is_tx) { - if (is_isoc(hw_ep, 1)) - schedule_work(&cppi41_channel->dma_completion); - else - cppi41_trans_done(cppi41_channel); - goto out; - } - empty = musb_is_tx_fifo_empty(hw_ep); if (empty) { cppi41_trans_done(cppi41_channel); @@ -312,15 +271,13 @@ static void cppi41_dma_callback(void *private_data) goto out; } } - if (is_isoc(hw_ep, 0)) { - schedule_work(&cppi41_channel->dma_completion); - goto out; - } list_add_tail(&cppi41_channel->tx_check, &controller->early_tx_list); if (!hrtimer_is_queued(&controller->early_tx)) { + unsigned long usecs = cppi41_channel->total_len / 10; + hrtimer_start_range_ns(&controller->early_tx, - ktime_set(0, 140 * NSEC_PER_USEC), + ktime_set(0, usecs * NSEC_PER_USEC), 40 * NSEC_PER_USEC, HRTIMER_MODE_REL); } @@ -450,6 +407,7 @@ static bool cppi41_configure_channel(struct dma_channel *channel, dma_desc->callback = cppi41_dma_callback; dma_desc->callback_param = channel; cppi41_channel->cookie = dma_desc->tx_submit(dma_desc); + cppi41_channel->channel.rx_packet_done = false; save_rx_toggle(cppi41_channel); dma_async_issue_pending(dc); @@ -672,8 +630,6 @@ static int cppi41_dma_controller_start(struct cppi41_dma_controller *controller) cppi41_channel->port_num = port; cppi41_channel->is_tx = is_tx; INIT_LIST_HEAD(&cppi41_channel->tx_check); - INIT_WORK(&cppi41_channel->dma_completion, - cppi_trans_done_work); musb_dma = &cppi41_channel->channel; musb_dma->private_data = cppi41_channel; diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h index 1345a4ff041a..1d44faa86252 100644 --- a/drivers/usb/musb/musb_dma.h +++ b/drivers/usb/musb/musb_dma.h @@ -129,6 +129,7 @@ struct dma_channel { size_t actual_len; enum dma_channel_status status; bool desired_mode; + bool rx_packet_done; }; /* diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 09529f94e72d..c791ba5da91a 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -56,16 +56,24 @@ static const struct of_device_id musb_dsps_of_match[]; * dependent on musb core layer symbols. */ static inline u8 dsps_readb(const void __iomem *addr, unsigned offset) - { return __raw_readb(addr + offset); } +{ + return __raw_readb(addr + offset); +} static inline u32 dsps_readl(const void __iomem *addr, unsigned offset) - { return __raw_readl(addr + offset); } +{ + return __raw_readl(addr + offset); +} static inline void dsps_writeb(void __iomem *addr, unsigned offset, u8 data) - { __raw_writeb(data, addr + offset); } +{ + __raw_writeb(data, addr + offset); +} static inline void dsps_writel(void __iomem *addr, unsigned offset, u32 data) - { __raw_writel(data, addr + offset); } +{ + __raw_writel(data, addr + offset); +} /** * DSPS musb wrapper register offset. @@ -136,6 +144,7 @@ struct dsps_glue { const struct dsps_musb_wrapper *wrp; /* wrapper register offsets */ struct timer_list timer; /* otg_workaround timer */ unsigned long last_timer; /* last timer data for each instance */ + bool sw_babble_enabled; struct dsps_context context; struct debugfs_regset32 regset; @@ -469,6 +478,19 @@ static int dsps_musb_init(struct musb *musb) val &= ~(1 << wrp->otg_disable); dsps_writel(musb->ctrl_base, wrp->phy_utmi, val); + /* + * Check whether the dsps version has babble control enabled. + * In latest silicon revision the babble control logic is enabled. + * If MUSB_BABBLE_CTL returns 0x4 then we have the babble control + * logic enabled. + */ + val = dsps_readb(musb->mregs, MUSB_BABBLE_CTL); + if (val == MUSB_BABBLE_RCV_DISABLE) { + glue->sw_babble_enabled = true; + val |= MUSB_BABBLE_SW_SESSION_CTRL; + dsps_writeb(musb->mregs, MUSB_BABBLE_CTL, val); + } + ret = dsps_musb_dbg_init(musb, glue); if (ret) return ret; @@ -535,14 +557,82 @@ static int dsps_musb_set_mode(struct musb *musb, u8 mode) return 0; } -static void dsps_musb_reset(struct musb *musb) +static bool sw_babble_control(struct musb *musb) +{ + u8 babble_ctl; + bool session_restart = false; + + babble_ctl = dsps_readb(musb->mregs, MUSB_BABBLE_CTL); + dev_dbg(musb->controller, "babble: MUSB_BABBLE_CTL value %x\n", + babble_ctl); + /* + * check line monitor flag to check whether babble is + * due to noise + */ + dev_dbg(musb->controller, "STUCK_J is %s\n", + babble_ctl & MUSB_BABBLE_STUCK_J ? "set" : "reset"); + + if (babble_ctl & MUSB_BABBLE_STUCK_J) { + int timeout = 10; + + /* + * babble is due to noise, then set transmit idle (d7 bit) + * to resume normal operation + */ + babble_ctl = dsps_readb(musb->mregs, MUSB_BABBLE_CTL); + babble_ctl |= MUSB_BABBLE_FORCE_TXIDLE; + dsps_writeb(musb->mregs, MUSB_BABBLE_CTL, babble_ctl); + + /* wait till line monitor flag cleared */ + dev_dbg(musb->controller, "Set TXIDLE, wait J to clear\n"); + do { + babble_ctl = dsps_readb(musb->mregs, MUSB_BABBLE_CTL); + udelay(1); + } while ((babble_ctl & MUSB_BABBLE_STUCK_J) && timeout--); + + /* check whether stuck_at_j bit cleared */ + if (babble_ctl & MUSB_BABBLE_STUCK_J) { + /* + * real babble condition has occurred + * restart the controller to start the + * session again + */ + dev_dbg(musb->controller, "J not cleared, misc (%x)\n", + babble_ctl); + session_restart = true; + } + } else { + session_restart = true; + } + + return session_restart; +} + +static int dsps_musb_reset(struct musb *musb) { struct device *dev = musb->controller; struct dsps_glue *glue = dev_get_drvdata(dev->parent); const struct dsps_musb_wrapper *wrp = glue->wrp; + int session_restart = 0; + + if (glue->sw_babble_enabled) + session_restart = sw_babble_control(musb); + /* + * In case of new silicon version babble condition can be recovered + * without resetting the MUSB. But for older silicon versions, MUSB + * reset is needed + */ + if (session_restart || !glue->sw_babble_enabled) { + dev_info(musb->controller, "Restarting MUSB to recover from Babble\n"); + dsps_writel(musb->ctrl_base, wrp->control, (1 << wrp->reset)); + usleep_range(100, 200); + usb_phy_shutdown(musb->xceiv); + usleep_range(100, 200); + usb_phy_init(musb->xceiv); + session_restart = 1; + } - dsps_writel(musb->ctrl_base, wrp->control, (1 << wrp->reset)); - udelay(100); + return !session_restart; } static struct musb_platform_ops dsps_ops = { diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index eb06291a40c8..855793d701bb 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -120,7 +120,7 @@ static void musb_h_tx_flush_fifo(struct musb_hw_ep *ep) if (csr != lastcsr) dev_dbg(musb->controller, "Host TX FIFONOTEMPTY csr: %02x\n", csr); lastcsr = csr; - csr |= MUSB_TXCSR_FLUSHFIFO; + csr |= MUSB_TXCSR_FLUSHFIFO | MUSB_TXCSR_TXPKTRDY; musb_writew(epio, MUSB_TXCSR, csr); csr = musb_readw(epio, MUSB_TXCSR); if (WARN(retries-- < 1, @@ -1295,7 +1295,7 @@ done: if (status) { if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { dma->status = MUSB_DMA_STATUS_CORE_ABORT; - (void) musb->dma_controller->channel_abort(dma); + musb->dma_controller->channel_abort(dma); } /* do the proper sequence to abort the transfer in the @@ -1640,7 +1640,7 @@ void musb_host_rx(struct musb *musb, u8 epnum) /* clean up dma and collect transfer count */ if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { dma->status = MUSB_DMA_STATUS_CORE_ABORT; - (void) musb->dma_controller->channel_abort(dma); + musb->dma_controller->channel_abort(dma); xfer_len = dma->actual_len; } musb_h_flush_rxfifo(hw_ep, MUSB_RXCSR_CLRDATATOG); @@ -1671,7 +1671,7 @@ void musb_host_rx(struct musb *musb, u8 epnum) */ if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { dma->status = MUSB_DMA_STATUS_CORE_ABORT; - (void) musb->dma_controller->channel_abort(dma); + musb->dma_controller->channel_abort(dma); xfer_len = dma->actual_len; done = true; } @@ -1734,10 +1734,11 @@ void musb_host_rx(struct musb *musb, u8 epnum) } } else { - /* done if urb buffer is full or short packet is recd */ - done = (urb->actual_length + xfer_len >= - urb->transfer_buffer_length - || dma->actual_len < qh->maxpacket); + /* done if urb buffer is full or short packet is recd */ + done = (urb->actual_length + xfer_len >= + urb->transfer_buffer_length + || dma->actual_len < qh->maxpacket + || dma->rx_packet_done); } /* send IN token for next packet, without AUTOREQ */ @@ -1957,7 +1958,7 @@ static int musb_schedule( struct musb_qh *qh, int is_in) { - int idle; + int idle = 0; int best_diff; int best_end, epnum; struct musb_hw_ep *hw_ep = NULL; diff --git a/drivers/usb/musb/musb_regs.h b/drivers/usb/musb/musb_regs.h index 03f2655af290..b9bcda5e3945 100644 --- a/drivers/usb/musb/musb_regs.h +++ b/drivers/usb/musb/musb_regs.h @@ -72,6 +72,12 @@ #define MUSB_DEVCTL_HR 0x02 #define MUSB_DEVCTL_SESSION 0x01 +/* BABBLE_CTL */ +#define MUSB_BABBLE_FORCE_TXIDLE 0x80 +#define MUSB_BABBLE_SW_SESSION_CTRL 0x40 +#define MUSB_BABBLE_STUCK_J 0x20 +#define MUSB_BABBLE_RCV_DISABLE 0x04 + /* MUSB ULPI VBUSCONTROL */ #define MUSB_ULPI_USE_EXTVBUS 0x01 #define MUSB_ULPI_USE_EXTVBUSIND 0x02 @@ -246,6 +252,7 @@ */ #define MUSB_DEVCTL 0x60 /* 8 bit */ +#define MUSB_BABBLE_CTL 0x61 /* 8 bit */ /* These are always controlled through the INDEX register */ #define MUSB_TXFIFOSZ 0x62 /* 8-bit (see masks) */ diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index 159ef4be1ef2..7dfc6cb732c9 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -22,6 +22,7 @@ #include <linux/usb.h> #include <linux/irq.h> #include <linux/io.h> +#include <linux/device.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> #include <linux/usb/usb_phy_generic.h> @@ -1160,12 +1161,12 @@ static int tusb_probe(struct platform_device *pdev) struct platform_device *musb; struct tusb6010_glue *glue; struct platform_device_info pinfo; - int ret = -ENOMEM; + int ret; - glue = kzalloc(sizeof(*glue), GFP_KERNEL); + glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); if (!glue) { dev_err(&pdev->dev, "failed to allocate glue context\n"); - goto err0; + return -ENOMEM; } glue->dev = &pdev->dev; @@ -1204,16 +1205,10 @@ static int tusb_probe(struct platform_device *pdev) if (IS_ERR(musb)) { ret = PTR_ERR(musb); dev_err(&pdev->dev, "failed to register musb device: %d\n", ret); - goto err3; + return ret; } return 0; - -err3: - kfree(glue); - -err0: - return ret; } static int tusb_remove(struct platform_device *pdev) @@ -1222,7 +1217,6 @@ static int tusb_remove(struct platform_device *pdev) platform_device_unregister(glue->musb); usb_phy_generic_unregister(glue->phy); - kfree(glue); return 0; } diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c index f202e5088461..dc666e96f45f 100644 --- a/drivers/usb/musb/ux500.c +++ b/drivers/usb/musb/ux500.c @@ -246,7 +246,7 @@ static int ux500_probe(struct platform_device *pdev) } } - glue = kzalloc(sizeof(*glue), GFP_KERNEL); + glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); if (!glue) { dev_err(&pdev->dev, "failed to allocate glue context\n"); goto err0; @@ -255,20 +255,20 @@ static int ux500_probe(struct platform_device *pdev) musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO); if (!musb) { dev_err(&pdev->dev, "failed to allocate musb device\n"); - goto err1; + goto err0; } - clk = clk_get(&pdev->dev, NULL); + clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(clk)) { dev_err(&pdev->dev, "failed to get clock\n"); ret = PTR_ERR(clk); - goto err3; + goto err1; } ret = clk_prepare_enable(clk); if (ret) { dev_err(&pdev->dev, "failed to enable clock\n"); - goto err4; + goto err1; } musb->dev.parent = &pdev->dev; @@ -301,34 +301,28 @@ static int ux500_probe(struct platform_device *pdev) ARRAY_SIZE(musb_resources)); if (ret) { dev_err(&pdev->dev, "failed to add resources\n"); - goto err5; + goto err2; } ret = platform_device_add_data(musb, pdata, sizeof(*pdata)); if (ret) { dev_err(&pdev->dev, "failed to add platform_data\n"); - goto err5; + goto err2; } ret = platform_device_add(musb); if (ret) { dev_err(&pdev->dev, "failed to register musb device\n"); - goto err5; + goto err2; } return 0; -err5: +err2: clk_disable_unprepare(clk); -err4: - clk_put(clk); - -err3: - platform_device_put(musb); - err1: - kfree(glue); + platform_device_put(musb); err0: return ret; @@ -340,8 +334,6 @@ static int ux500_remove(struct platform_device *pdev) platform_device_unregister(glue->musb); clk_disable_unprepare(glue->clk); - clk_put(glue->clk); - kfree(glue); return 0; } diff --git a/drivers/usb/phy/phy-am335x.c b/drivers/usb/phy/phy-am335x.c index 585e50cb1980..b70e05537180 100644 --- a/drivers/usb/phy/phy-am335x.c +++ b/drivers/usb/phy/phy-am335x.c @@ -122,16 +122,10 @@ static int am335x_phy_resume(struct device *dev) return 0; } - -static const struct dev_pm_ops am335x_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(am335x_phy_suspend, am335x_phy_resume) -}; - -#define DEV_PM_OPS (&am335x_pm_ops) -#else -#define DEV_PM_OPS NULL #endif +static SIMPLE_DEV_PM_OPS(am335x_pm_ops, am335x_phy_suspend, am335x_phy_resume); + static const struct of_device_id am335x_phy_ids[] = { { .compatible = "ti,am335x-usb-phy" }, { } @@ -144,7 +138,7 @@ static struct platform_driver am335x_phy_driver = { .driver = { .name = "am335x-phy-driver", .owner = THIS_MODULE, - .pm = DEV_PM_OPS, + .pm = &am335x_pm_ops, .of_match_table = am335x_phy_ids, }, }; diff --git a/drivers/usb/phy/phy-gpio-vbus-usb.c b/drivers/usb/phy/phy-gpio-vbus-usb.c index 69462e09d014..ea9e705555df 100644 --- a/drivers/usb/phy/phy-gpio-vbus-usb.c +++ b/drivers/usb/phy/phy-gpio-vbus-usb.c @@ -253,11 +253,13 @@ static int gpio_vbus_probe(struct platform_device *pdev) return -EINVAL; gpio = pdata->gpio_vbus; - gpio_vbus = kzalloc(sizeof(struct gpio_vbus_data), GFP_KERNEL); + gpio_vbus = devm_kzalloc(&pdev->dev, sizeof(struct gpio_vbus_data), + GFP_KERNEL); if (!gpio_vbus) return -ENOMEM; - gpio_vbus->phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL); + gpio_vbus->phy.otg = devm_kzalloc(&pdev->dev, sizeof(struct usb_otg), + GFP_KERNEL); if (!gpio_vbus->phy.otg) { kfree(gpio_vbus); return -ENOMEM; @@ -274,11 +276,11 @@ static int gpio_vbus_probe(struct platform_device *pdev) gpio_vbus->phy.otg->phy = &gpio_vbus->phy; gpio_vbus->phy.otg->set_peripheral = gpio_vbus_set_peripheral; - err = gpio_request(gpio, "vbus_detect"); + err = devm_gpio_request(&pdev->dev, gpio, "vbus_detect"); if (err) { dev_err(&pdev->dev, "can't request vbus gpio %d, err: %d\n", gpio, err); - goto err_gpio; + return err; } gpio_direction_input(gpio); @@ -296,27 +298,27 @@ static int gpio_vbus_probe(struct platform_device *pdev) /* if data line pullup is in use, initialize it to "not pulling up" */ gpio = pdata->gpio_pullup; if (gpio_is_valid(gpio)) { - err = gpio_request(gpio, "udc_pullup"); + err = devm_gpio_request(&pdev->dev, gpio, "udc_pullup"); if (err) { dev_err(&pdev->dev, "can't request pullup gpio %d, err: %d\n", gpio, err); - gpio_free(pdata->gpio_vbus); - goto err_gpio; + return err; } gpio_direction_output(gpio, pdata->gpio_pullup_inverted); } - err = request_irq(irq, gpio_vbus_irq, irqflags, "vbus_detect", pdev); + err = devm_request_irq(&pdev->dev, irq, gpio_vbus_irq, irqflags, + "vbus_detect", pdev); if (err) { dev_err(&pdev->dev, "can't request irq %i, err: %d\n", irq, err); - goto err_irq; + return err; } INIT_DELAYED_WORK(&gpio_vbus->work, gpio_vbus_work); - gpio_vbus->vbus_draw = regulator_get(&pdev->dev, "vbus_draw"); + gpio_vbus->vbus_draw = devm_regulator_get(&pdev->dev, "vbus_draw"); if (IS_ERR(gpio_vbus->vbus_draw)) { dev_dbg(&pdev->dev, "can't get vbus_draw regulator, err: %ld\n", PTR_ERR(gpio_vbus->vbus_draw)); @@ -328,44 +330,23 @@ static int gpio_vbus_probe(struct platform_device *pdev) if (err) { dev_err(&pdev->dev, "can't register transceiver, err: %d\n", err); - goto err_otg; + return err; } device_init_wakeup(&pdev->dev, pdata->wakeup); return 0; -err_otg: - regulator_put(gpio_vbus->vbus_draw); - free_irq(irq, pdev); -err_irq: - if (gpio_is_valid(pdata->gpio_pullup)) - gpio_free(pdata->gpio_pullup); - gpio_free(pdata->gpio_vbus); -err_gpio: - kfree(gpio_vbus->phy.otg); - kfree(gpio_vbus); - return err; } static int gpio_vbus_remove(struct platform_device *pdev) { struct gpio_vbus_data *gpio_vbus = platform_get_drvdata(pdev); - struct gpio_vbus_mach_info *pdata = dev_get_platdata(&pdev->dev); - int gpio = pdata->gpio_vbus; device_init_wakeup(&pdev->dev, 0); cancel_delayed_work_sync(&gpio_vbus->work); - regulator_put(gpio_vbus->vbus_draw); usb_remove_phy(&gpio_vbus->phy); - free_irq(gpio_vbus->irq, pdev); - if (gpio_is_valid(pdata->gpio_pullup)) - gpio_free(pdata->gpio_pullup); - gpio_free(gpio); - kfree(gpio_vbus->phy.otg); - kfree(gpio_vbus); - return 0; } diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index c929370cdaa6..e4108eec5ef4 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -279,11 +279,11 @@ static int msm_otg_link_clk_reset(struct msm_otg *motg, bool assert) static int msm_otg_phy_clk_reset(struct msm_otg *motg) { - int ret; + int ret = 0; - if (motg->pdata->phy_clk_reset) + if (motg->pdata->phy_clk_reset && motg->phy_reset_clk) ret = motg->pdata->phy_clk_reset(motg->phy_reset_clk); - else + else if (motg->phy_rst) ret = reset_control_reset(motg->phy_rst); if (ret) @@ -1429,7 +1429,7 @@ static void msm_otg_debugfs_cleanup(void) debugfs_remove(msm_otg_dbg_root); } -static struct of_device_id msm_otg_dt_match[] = { +static const struct of_device_id msm_otg_dt_match[] = { { .compatible = "qcom,usb-otg-ci", .data = (void *) CI_45NM_INTEGRATED_PHY @@ -1466,7 +1466,7 @@ static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg) motg->phy_rst = devm_reset_control_get(&pdev->dev, "phy"); if (IS_ERR(motg->phy_rst)) - return PTR_ERR(motg->phy_rst); + motg->phy_rst = NULL; pdata->mode = of_usb_get_dr_mode(node); if (pdata->mode == USB_DR_MODE_UNKNOWN) @@ -1558,7 +1558,7 @@ static int msm_otg_probe(struct platform_device *pdev) np ? "phy" : "usb_phy_clk"); if (IS_ERR(motg->phy_reset_clk)) { dev_err(&pdev->dev, "failed to get usb_phy_clk\n"); - return PTR_ERR(motg->phy_reset_clk); + motg->phy_reset_clk = NULL; } motg->clk = devm_clk_get(&pdev->dev, np ? "core" : "usb_hs_clk"); diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index bbe4f8e6e8d7..13b4fa287da8 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -33,7 +33,6 @@ #include <linux/usb/otg.h> #include <linux/usb/ulpi.h> #include <linux/usb/of.h> -#include <asm/mach-types.h> #include <linux/usb/ehci_def.h> #include <linux/usb/tegra_usb_phy.h> #include <linux/regulator/consumer.h> @@ -686,10 +685,8 @@ static int ulpi_phy_power_off(struct tegra_usb_phy *phy) return gpio_direction_output(phy->reset_gpio, 0); } -static void tegra_usb_phy_close(struct usb_phy *x) +static void tegra_usb_phy_close(struct tegra_usb_phy *phy) { - struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy); - if (!IS_ERR(phy->vbus)) regulator_disable(phy->vbus); @@ -965,7 +962,7 @@ static const struct tegra_phy_soc_config tegra30_soc_config = { .requires_extra_tuning_parameters = true, }; -static struct of_device_id tegra_usb_phy_id_table[] = { +static const struct of_device_id tegra_usb_phy_id_table[] = { { .compatible = "nvidia,tegra30-usb-phy", .data = &tegra30_soc_config }, { .compatible = "nvidia,tegra20-usb-phy", .data = &tegra20_soc_config }, { }, @@ -1061,14 +1058,13 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) if (err < 0) return err; - tegra_phy->u_phy.shutdown = tegra_usb_phy_close; tegra_phy->u_phy.set_suspend = tegra_usb_phy_suspend; platform_set_drvdata(pdev, tegra_phy); err = usb_add_phy_dev(&tegra_phy->u_phy); if (err < 0) { - tegra_usb_phy_close(&tegra_phy->u_phy); + tegra_usb_phy_close(tegra_phy); return err; } @@ -1080,6 +1076,7 @@ static int tegra_usb_phy_remove(struct platform_device *pdev) struct tegra_usb_phy *tegra_phy = platform_get_drvdata(pdev); usb_remove_phy(&tegra_phy->u_phy); + tegra_usb_phy_close(tegra_phy); return 0; } diff --git a/drivers/usb/renesas_usbhs/Makefile b/drivers/usb/renesas_usbhs/Makefile index bc8aef4311a1..9e47f477b6d2 100644 --- a/drivers/usb/renesas_usbhs/Makefile +++ b/drivers/usb/renesas_usbhs/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs.o -renesas_usbhs-y := common.o mod.o pipe.o fifo.o +renesas_usbhs-y := common.o mod.o pipe.o fifo.o rcar2.o ifneq ($(CONFIG_USB_RENESAS_USBHS_HCD),) renesas_usbhs-y += mod_host.o diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 17267b0a2e95..1b9bf8d83235 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -15,12 +15,14 @@ * */ #include <linux/err.h> +#include <linux/gpio.h> #include <linux/io.h> #include <linux/module.h> #include <linux/pm_runtime.h> #include <linux/slab.h> #include <linux/sysfs.h> #include "common.h" +#include "rcar2.h" /* * image of renesas_usbhs @@ -284,6 +286,8 @@ static void usbhsc_set_buswait(struct usbhs_priv *priv) /* * platform default param */ + +/* commonly used on old SH-Mobile SoCs */ static u32 usbhsc_default_pipe_type[] = { USB_ENDPOINT_XFER_CONTROL, USB_ENDPOINT_XFER_ISOC, @@ -297,6 +301,26 @@ static u32 usbhsc_default_pipe_type[] = { USB_ENDPOINT_XFER_INT, }; +/* commonly used on newer SH-Mobile and R-Car SoCs */ +static u32 usbhsc_new_pipe_type[] = { + USB_ENDPOINT_XFER_CONTROL, + USB_ENDPOINT_XFER_ISOC, + USB_ENDPOINT_XFER_ISOC, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_INT, + USB_ENDPOINT_XFER_INT, + USB_ENDPOINT_XFER_INT, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, +}; + /* * power control */ @@ -423,8 +447,7 @@ static int usbhs_probe(struct platform_device *pdev) int ret; /* check platform information */ - if (!info || - !info->platform_callback.get_id) { + if (!info) { dev_err(&pdev->dev, "no platform information\n"); return -EINVAL; } @@ -451,13 +474,32 @@ static int usbhs_probe(struct platform_device *pdev) /* * care platform info */ - memcpy(&priv->pfunc, - &info->platform_callback, - sizeof(struct renesas_usbhs_platform_callback)); + memcpy(&priv->dparam, &info->driver_param, sizeof(struct renesas_usbhs_driver_param)); + switch (priv->dparam.type) { + case USBHS_TYPE_R8A7790: + case USBHS_TYPE_R8A7791: + priv->pfunc = usbhs_rcar2_ops; + if (!priv->dparam.pipe_type) { + priv->dparam.pipe_type = usbhsc_new_pipe_type; + priv->dparam.pipe_size = + ARRAY_SIZE(usbhsc_new_pipe_type); + } + break; + default: + if (!info->platform_callback.get_id) { + dev_err(&pdev->dev, "no platform callbacks"); + return -EINVAL; + } + memcpy(&priv->pfunc, + &info->platform_callback, + sizeof(struct renesas_usbhs_platform_callback)); + break; + } + /* set driver callback functions for platform */ dfunc = &info->driver_callback; dfunc->notify_hotplug = usbhsc_drvcllbck_notify_hotplug; @@ -507,6 +549,20 @@ static int usbhs_probe(struct platform_device *pdev) */ usbhs_sys_clock_ctrl(priv, 0); + /* check GPIO determining if USB function should be enabled */ + if (priv->dparam.enable_gpio) { + gpio_request_one(priv->dparam.enable_gpio, GPIOF_IN, NULL); + ret = !gpio_get_value(priv->dparam.enable_gpio); + gpio_free(priv->dparam.enable_gpio); + if (ret) { + dev_warn(&pdev->dev, + "USB function not selected (GPIO %d)\n", + priv->dparam.enable_gpio); + ret = -ENOTSUPP; + goto probe_end_mod_exit; + } + } + /* * platform call * diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h index c69dd2fba360..a7996da6a1bd 100644 --- a/drivers/usb/renesas_usbhs/common.h +++ b/drivers/usb/renesas_usbhs/common.h @@ -268,6 +268,8 @@ struct usbhs_priv { * fifo control */ struct usbhs_fifo_info fifo_info; + + struct usb_phy *phy; }; /* diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index 458f3766bef1..04e6505777d0 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -600,8 +600,10 @@ static int usbhsg_ep_enable(struct usb_ep *ep, static int usbhsg_ep_disable(struct usb_ep *ep) { struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep); + struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); usbhsg_pipe_disable(uep); + usbhs_pipe_free(pipe); uep->pipe->mod_private = NULL; uep->pipe = NULL; diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c index 7926e1c700f1..75fbcf6b102e 100644 --- a/drivers/usb/renesas_usbhs/pipe.c +++ b/drivers/usb/renesas_usbhs/pipe.c @@ -640,6 +640,11 @@ static struct usbhs_pipe *usbhsp_get_pipe(struct usbhs_priv *priv, u32 type) return pipe; } +static void usbhsp_put_pipe(struct usbhs_pipe *pipe) +{ + usbhsp_flags_init(pipe); +} + void usbhs_pipe_init(struct usbhs_priv *priv, int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map)) { @@ -710,6 +715,7 @@ struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv, usbhsp_pipe_select(pipe); usbhsp_pipe_cfg_set(pipe, 0xFFFF, pipecfg); usbhsp_pipe_buf_set(pipe, 0xFFFF, pipebuf); + usbhs_pipe_clear(pipe); usbhs_pipe_sequence_data0(pipe); @@ -726,6 +732,11 @@ struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv, return pipe; } +void usbhs_pipe_free(struct usbhs_pipe *pipe) +{ + usbhsp_put_pipe(pipe); +} + void usbhs_pipe_select_fifo(struct usbhs_pipe *pipe, struct usbhs_fifo *fifo) { if (pipe->fifo) diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h index 3e5349879838..406f36d050e4 100644 --- a/drivers/usb/renesas_usbhs/pipe.h +++ b/drivers/usb/renesas_usbhs/pipe.h @@ -75,6 +75,7 @@ struct usbhs_pipe_info { char *usbhs_pipe_name(struct usbhs_pipe *pipe); struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv, int endpoint_type, int dir_in); +void usbhs_pipe_free(struct usbhs_pipe *pipe); int usbhs_pipe_probe(struct usbhs_priv *priv); void usbhs_pipe_remove(struct usbhs_priv *priv); int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe); diff --git a/drivers/usb/renesas_usbhs/rcar2.c b/drivers/usb/renesas_usbhs/rcar2.c new file mode 100644 index 000000000000..e6b9dcc1c289 --- /dev/null +++ b/drivers/usb/renesas_usbhs/rcar2.c @@ -0,0 +1,77 @@ +/* + * Renesas USB driver R-Car Gen. 2 initialization and power control + * + * Copyright (C) 2014 Ulrich Hecht + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <linux/platform_data/gpio-rcar.h> +#include <linux/usb/phy.h> +#include "common.h" +#include "rcar2.h" + +static int usbhs_rcar2_hardware_init(struct platform_device *pdev) +{ + struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); + struct usb_phy *phy; + + phy = usb_get_phy_dev(&pdev->dev, 0); + if (IS_ERR(phy)) + return PTR_ERR(phy); + + priv->phy = phy; + return 0; +} + +static int usbhs_rcar2_hardware_exit(struct platform_device *pdev) +{ + struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); + + if (!priv->phy) + return 0; + + usb_put_phy(priv->phy); + priv->phy = NULL; + + return 0; +} + +static int usbhs_rcar2_power_ctrl(struct platform_device *pdev, + void __iomem *base, int enable) +{ + struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); + + if (!priv->phy) + return -ENODEV; + + if (enable) { + int retval = usb_phy_init(priv->phy); + + if (!retval) + retval = usb_phy_set_suspend(priv->phy, 0); + return retval; + } + + usb_phy_set_suspend(priv->phy, 1); + usb_phy_shutdown(priv->phy); + return 0; +} + +static int usbhs_rcar2_get_id(struct platform_device *pdev) +{ + return USBHS_GADGET; +} + +const struct renesas_usbhs_platform_callback usbhs_rcar2_ops = { + .hardware_init = usbhs_rcar2_hardware_init, + .hardware_exit = usbhs_rcar2_hardware_exit, + .power_ctrl = usbhs_rcar2_power_ctrl, + .get_id = usbhs_rcar2_get_id, +}; diff --git a/drivers/usb/renesas_usbhs/rcar2.h b/drivers/usb/renesas_usbhs/rcar2.h new file mode 100644 index 000000000000..f07f10d9b3b2 --- /dev/null +++ b/drivers/usb/renesas_usbhs/rcar2.h @@ -0,0 +1,4 @@ +#include "common.h" + +extern const struct renesas_usbhs_platform_callback + usbhs_rcar2_ops; diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 330df5ce435b..e4bb62225cb9 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -856,9 +856,6 @@ static int cp210x_startup(struct usb_serial *serial) struct usb_host_interface *cur_altsetting; struct cp210x_serial_private *spriv; - /* cp210x buffers behave strangely unless device is reset */ - usb_reset_device(serial->dev); - spriv = kzalloc(sizeof(*spriv), GFP_KERNEL); if (!spriv) return -ENOMEM; diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 8a3813be1b28..216ce3078270 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -87,7 +87,6 @@ struct ftdi_sio_quirk { }; static int ftdi_jtag_probe(struct usb_serial *serial); -static int ftdi_mtxorb_hack_setup(struct usb_serial *serial); static int ftdi_NDI_device_setup(struct usb_serial *serial); static int ftdi_stmclite_probe(struct usb_serial *serial); static int ftdi_8u2232c_probe(struct usb_serial *serial); @@ -98,10 +97,6 @@ static struct ftdi_sio_quirk ftdi_jtag_quirk = { .probe = ftdi_jtag_probe, }; -static struct ftdi_sio_quirk ftdi_mtxorb_hack_quirk = { - .probe = ftdi_mtxorb_hack_setup, -}; - static struct ftdi_sio_quirk ftdi_NDI_device_quirk = { .probe = ftdi_NDI_device_setup, }; @@ -256,14 +251,12 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0124_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0125_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0126_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0127_PID), - .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0127_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0128_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0129_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012A_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012B_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012C_PID), - .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012C_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012D_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012E_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012F_PID) }, @@ -302,18 +295,12 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0150_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0151_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0152_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0153_PID), - .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0154_PID), - .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0155_PID), - .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0156_PID), - .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0157_PID), - .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0158_PID), - .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0153_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0154_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0155_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0156_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0157_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0158_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0159_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_015A_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_015B_PID) }, @@ -673,6 +660,8 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_5_PID) }, { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_6_PID) }, { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_7_PID) }, + { USB_DEVICE(XSENS_VID, XSENS_CONVERTER_PID) }, + { USB_DEVICE(XSENS_VID, XSENS_MTW_PID) }, { USB_DEVICE(FTDI_VID, FTDI_OMNI1509) }, { USB_DEVICE(MOBILITY_VID, MOBILITY_USB_SERIAL_PID) }, { USB_DEVICE(FTDI_VID, FTDI_ACTIVE_ROBOTS_PID) }, @@ -1559,45 +1548,40 @@ static void ftdi_determine_type(struct usb_serial_port *port) } -/* Determine the maximum packet size for the device. This depends on the chip - * type and the USB host capabilities. The value should be obtained from the - * device descriptor as the chip will use the appropriate values for the host.*/ +/* + * Determine the maximum packet size for the device. This depends on the chip + * type and the USB host capabilities. The value should be obtained from the + * device descriptor as the chip will use the appropriate values for the host. + */ static void ftdi_set_max_packet_size(struct usb_serial_port *port) { struct ftdi_private *priv = usb_get_serial_port_data(port); - struct usb_serial *serial = port->serial; - struct usb_device *udev = serial->dev; - - struct usb_interface *interface = serial->interface; + struct usb_interface *interface = port->serial->interface; struct usb_endpoint_descriptor *ep_desc; - unsigned num_endpoints; unsigned i; num_endpoints = interface->cur_altsetting->desc.bNumEndpoints; - dev_info(&udev->dev, "Number of endpoints %d\n", num_endpoints); - if (!num_endpoints) return; - /* NOTE: some customers have programmed FT232R/FT245R devices - * with an endpoint size of 0 - not good. In this case, we + /* + * NOTE: Some customers have programmed FT232R/FT245R devices + * with an endpoint size of 0 - not good. In this case, we * want to override the endpoint descriptor setting and use a - * value of 64 for wMaxPacketSize */ + * value of 64 for wMaxPacketSize. + */ for (i = 0; i < num_endpoints; i++) { - dev_info(&udev->dev, "Endpoint %d MaxPacketSize %d\n", i+1, - interface->cur_altsetting->endpoint[i].desc.wMaxPacketSize); ep_desc = &interface->cur_altsetting->endpoint[i].desc; - if (ep_desc->wMaxPacketSize == 0) { + if (!ep_desc->wMaxPacketSize) { ep_desc->wMaxPacketSize = cpu_to_le16(0x40); - dev_info(&udev->dev, "Overriding wMaxPacketSize on endpoint %d\n", i); + dev_warn(&port->dev, "Overriding wMaxPacketSize on endpoint %d\n", + usb_endpoint_num(ep_desc)); } } - /* set max packet size based on descriptor */ + /* Set max packet size based on last descriptor. */ priv->max_packet_size = usb_endpoint_maxp(ep_desc); - - dev_info(&udev->dev, "Setting MaxPacketSize %d\n", priv->max_packet_size); } @@ -1866,24 +1850,6 @@ static int ftdi_stmclite_probe(struct usb_serial *serial) return 0; } -/* - * The Matrix Orbital VK204-25-USB has an invalid IN endpoint. - * We have to correct it if we want to read from it. - */ -static int ftdi_mtxorb_hack_setup(struct usb_serial *serial) -{ - struct usb_host_endpoint *ep = serial->dev->ep_in[1]; - struct usb_endpoint_descriptor *ep_desc = &ep->desc; - - if (ep->enabled && ep_desc->wMaxPacketSize == 0) { - ep_desc->wMaxPacketSize = cpu_to_le16(0x40); - dev_info(&serial->dev->dev, - "Fixing invalid wMaxPacketSize on read pipe\n"); - } - - return 0; -} - static int ftdi_sio_port_remove(struct usb_serial_port *port) { struct ftdi_private *priv = usb_get_serial_port_data(port); diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index c4777bc6aee0..1e58d90a0b6c 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -140,12 +140,15 @@ /* * Xsens Technologies BV products (http://www.xsens.com). */ -#define XSENS_CONVERTER_0_PID 0xD388 -#define XSENS_CONVERTER_1_PID 0xD389 +#define XSENS_VID 0x2639 +#define XSENS_CONVERTER_PID 0xD00D /* Xsens USB-serial converter */ +#define XSENS_MTW_PID 0x0200 /* Xsens MTw */ +#define XSENS_CONVERTER_0_PID 0xD388 /* Xsens USB converter */ +#define XSENS_CONVERTER_1_PID 0xD389 /* Xsens Wireless Receiver */ #define XSENS_CONVERTER_2_PID 0xD38A -#define XSENS_CONVERTER_3_PID 0xD38B -#define XSENS_CONVERTER_4_PID 0xD38C -#define XSENS_CONVERTER_5_PID 0xD38D +#define XSENS_CONVERTER_3_PID 0xD38B /* Xsens USB-serial converter */ +#define XSENS_CONVERTER_4_PID 0xD38C /* Xsens Wireless Receiver */ +#define XSENS_CONVERTER_5_PID 0xD38D /* Xsens Awinda Station */ #define XSENS_CONVERTER_6_PID 0xD38E #define XSENS_CONVERTER_7_PID 0xD38F diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index d7440b7557af..e020ad28a00c 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -62,8 +62,6 @@ static void klsi_105_close(struct usb_serial_port *port); static void klsi_105_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old); static int klsi_105_tiocmget(struct tty_struct *tty); -static int klsi_105_tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear); static void klsi_105_process_read_urb(struct urb *urb); static int klsi_105_prepare_write_buffer(struct usb_serial_port *port, void *dest, size_t size); @@ -93,7 +91,6 @@ static struct usb_serial_driver kl5kusb105d_device = { .set_termios = klsi_105_set_termios, /*.break_ctl = klsi_105_break_ctl,*/ .tiocmget = klsi_105_tiocmget, - .tiocmset = klsi_105_tiocmset, .port_probe = klsi_105_port_probe, .port_remove = klsi_105_port_remove, .throttle = usb_serial_generic_throttle, @@ -602,33 +599,6 @@ static int klsi_105_tiocmget(struct tty_struct *tty) return (int)line_state; } -static int klsi_105_tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear) -{ - int retval = -EINVAL; - -/* if this ever gets implemented, it should be done something like this: - struct usb_serial *serial = port->serial; - struct klsi_105_private *priv = usb_get_serial_port_data(port); - unsigned long flags; - int control; - - spin_lock_irqsave (&priv->lock, flags); - if (set & TIOCM_RTS) - priv->control_state |= TIOCM_RTS; - if (set & TIOCM_DTR) - priv->control_state |= TIOCM_DTR; - if (clear & TIOCM_RTS) - priv->control_state &= ~TIOCM_RTS; - if (clear & TIOCM_DTR) - priv->control_state &= ~TIOCM_DTR; - control = priv->control_state; - spin_unlock_irqrestore (&priv->lock, flags); - retval = mct_u232_set_modem_ctrl(serial, control); -*/ - return retval; -} - module_usb_serial_driver(serial_drivers, id_table); MODULE_AUTHOR(DRIVER_AUTHOR); diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 393be562d875..3d88eefdf1d1 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -1181,10 +1181,7 @@ static void mos7840_close(struct usb_serial_port *port) /* Freeing Write URBs */ for (j = 0; j < NUM_URBS; ++j) { if (mos7840_port->write_urb_pool[j]) { - if (mos7840_port->write_urb_pool[j]->transfer_buffer) - kfree(mos7840_port->write_urb_pool[j]-> - transfer_buffer); - + kfree(mos7840_port->write_urb_pool[j]->transfer_buffer); usb_free_urb(mos7840_port->write_urb_pool[j]); } } diff --git a/drivers/usb/storage/uas-detect.h b/drivers/usb/storage/uas-detect.h index bb05b984d5f6..503ac5c8d80f 100644 --- a/drivers/usb/storage/uas-detect.h +++ b/drivers/usb/storage/uas-detect.h @@ -9,32 +9,15 @@ static int uas_is_interface(struct usb_host_interface *intf) intf->desc.bInterfaceProtocol == USB_PR_UAS); } -static int uas_isnt_supported(struct usb_device *udev) -{ - struct usb_hcd *hcd = bus_to_hcd(udev->bus); - - dev_warn(&udev->dev, "The driver for the USB controller %s does not " - "support scatter-gather which is\n", - hcd->driver->description); - dev_warn(&udev->dev, "required by the UAS driver. Please try an" - "alternative USB controller if you wish to use UAS.\n"); - return -ENODEV; -} - static int uas_find_uas_alt_setting(struct usb_interface *intf) { int i; - struct usb_device *udev = interface_to_usbdev(intf); - int sg_supported = udev->bus->sg_tablesize != 0; for (i = 0; i < intf->num_altsetting; i++) { struct usb_host_interface *alt = &intf->altsetting[i]; - if (uas_is_interface(alt)) { - if (!sg_supported) - return uas_isnt_supported(udev); + if (uas_is_interface(alt)) return alt->desc.bAlternateSetting; - } } return -ENODEV; @@ -81,9 +64,6 @@ static int uas_use_uas_driver(struct usb_interface *intf, if (flags & US_FL_IGNORE_UAS) return 0; - if (udev->speed >= USB_SPEED_SUPER && !hcd->can_do_streams) - return 0; - alt = uas_find_uas_alt_setting(intf); if (alt < 0) return 0; @@ -92,5 +72,23 @@ static int uas_use_uas_driver(struct usb_interface *intf, if (r < 0) return 0; + if (udev->bus->sg_tablesize == 0) { + dev_warn(&udev->dev, + "The driver for the USB controller %s does not support scatter-gather which is\n", + hcd->driver->description); + dev_warn(&udev->dev, + "required by the UAS driver. Please try an other USB controller if you wish to use UAS.\n"); + return 0; + } + + if (udev->speed >= USB_SPEED_SUPER && !hcd->can_do_streams) { + dev_warn(&udev->dev, + "USB controller %s does not support streams, which are required by the UAS driver.\n", + hcd_to_bus(hcd)->bus_name); + dev_warn(&udev->dev, + "Please try an other USB controller if you wish to use UAS.\n"); + return 0; + } + return 1; } diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 511b22953167..3f42785f653c 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -1026,7 +1026,7 @@ static int uas_configure_endpoints(struct uas_dev_info *devinfo) usb_endpoint_num(&eps[3]->desc)); if (udev->speed != USB_SPEED_SUPER) { - devinfo->qdepth = 256; + devinfo->qdepth = 32; devinfo->use_streams = 0; } else { devinfo->qdepth = usb_alloc_streams(devinfo->intf, eps + 1, diff --git a/drivers/uwb/whci.c b/drivers/uwb/whci.c index c9df8ba97dae..46b7cfcdfbca 100644 --- a/drivers/uwb/whci.c +++ b/drivers/uwb/whci.c @@ -175,7 +175,7 @@ static int whci_probe(struct pci_dev *pci, const struct pci_device_id *id) err = -ENOMEM; card = kzalloc(sizeof(struct whci_card) - + sizeof(struct whci_dev *) * (n_caps + 1), + + sizeof(struct umc_dev *) * (n_caps + 1), GFP_KERNEL); if (card == NULL) goto error_kzalloc; diff --git a/include/dt-bindings/phy/phy-miphy365x.h b/include/dt-bindings/phy/phy-miphy365x.h new file mode 100644 index 000000000000..8ef8aba6edd6 --- /dev/null +++ b/include/dt-bindings/phy/phy-miphy365x.h @@ -0,0 +1,14 @@ +/* + * This header provides constants for the phy framework + * based on the STMicroelectronics MiPHY365x. + * + * Author: Lee Jones <lee.jones@linaro.org> + */ +#ifndef _DT_BINDINGS_PHY_MIPHY +#define _DT_BINDINGS_PHY_MIPHY + +#define MIPHY_TYPE_SATA 1 +#define MIPHY_TYPE_PCIE 2 +#define MIPHY_TYPE_USB 3 + +#endif /* _DT_BINDINGS_PHY_MIPHY */ diff --git a/include/linux/phy/omap_control_phy.h b/include/linux/phy/omap_control_phy.h index 5450403c7546..e9e6cfbfbb58 100644 --- a/include/linux/phy/omap_control_phy.h +++ b/include/linux/phy/omap_control_phy.h @@ -23,6 +23,7 @@ enum omap_control_phy_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_PCIE, /* RX TX control of ACSPCIE */ OMAP_CTRL_TYPE_DRA7USB2, /* USB2 PHY, power and power_aux e.g. DRA7 */ OMAP_CTRL_TYPE_AM437USB2, /* USB2 PHY, power e.g. AM437x */ }; @@ -33,6 +34,7 @@ struct omap_control_phy { u32 __iomem *otghs_control; u32 __iomem *power; u32 __iomem *power_aux; + u32 __iomem *pcie_pcs; struct clk *sys_clk; @@ -63,6 +65,9 @@ enum omap_control_usb_mode { #define OMAP_CTRL_PIPE3_PHY_TX_RX_POWERON 0x3 #define OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF 0x0 +#define OMAP_CTRL_PCIE_PCS_MASK 0xff +#define OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT 0x8 + #define OMAP_CTRL_USB2_PHY_PD BIT(28) #define AM437X_CTRL_USB2_PHY_PD BIT(0) @@ -74,6 +79,7 @@ enum omap_control_usb_mode { void omap_control_phy_power(struct device *dev, int on); void omap_control_usb_set_mode(struct device *dev, enum omap_control_usb_mode mode); +void omap_control_pcie_pcs(struct device *dev, u8 id, u8 delay); #else static inline void omap_control_phy_power(struct device *dev, int on) @@ -84,6 +90,10 @@ static inline void omap_control_usb_set_mode(struct device *dev, enum omap_control_usb_mode mode) { } + +static inline void omap_control_pcie_pcs(struct device *dev, u8 id, u8 delay) +{ +} #endif #endif /* __OMAP_CONTROL_PHY_H__ */ diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h index 2760744cb2a7..8cb6f815475b 100644 --- a/include/linux/phy/phy.h +++ b/include/linux/phy/phy.h @@ -18,6 +18,7 @@ #include <linux/of.h> #include <linux/device.h> #include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> struct phy; @@ -65,6 +66,7 @@ struct phy { int init_count; int power_count; struct phy_attrs attrs; + struct regulator *pwr; }; /** @@ -156,9 +158,10 @@ void devm_phy_put(struct device *dev, struct phy *phy); struct phy *of_phy_get(struct device_node *np, const char *con_id); 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, +struct phy *phy_create(struct device *dev, struct device_node *node, + const struct phy_ops *ops, + struct phy_init_data *init_data); +struct phy *devm_phy_create(struct device *dev, struct device_node *node, 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); @@ -297,13 +300,17 @@ static inline struct phy *of_phy_simple_xlate(struct device *dev, } static inline struct phy *phy_create(struct device *dev, - const struct phy_ops *ops, struct phy_init_data *init_data) + struct device_node *node, + 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) + struct device_node *node, + const struct phy_ops *ops, + struct phy_init_data *init_data) { return ERR_PTR(-ENOSYS); } diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 7373203140e7..c330f5ef42cf 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -386,6 +386,21 @@ struct usb_composite_driver { extern int usb_composite_probe(struct usb_composite_driver *driver); extern void usb_composite_unregister(struct usb_composite_driver *driver); + +/** + * module_usb_composite_driver() - Helper macro for registering a USB gadget + * composite driver + * @__usb_composite_driver: usb_composite_driver struct + * + * Helper macro for USB gadget composite drivers which do not do anything + * special in module init/exit. This eliminates a lot of boilerplate. Each + * module may only use this macro once, and calling it replaces module_init() + * and module_exit() + */ +#define module_usb_composite_driver(__usb_composite_driver) \ + module_driver(__usb_composite_driver, usb_composite_probe, \ + usb_composite_unregister) + extern void usb_composite_setup_continue(struct usb_composite_dev *cdev); extern int composite_dev_prepare(struct usb_composite_driver *composite, struct usb_composite_dev *cdev); diff --git a/include/linux/usb/quirks.h b/include/linux/usb/quirks.h index 52f944dfe2fd..55a17b188daa 100644 --- a/include/linux/usb/quirks.h +++ b/include/linux/usb/quirks.h @@ -30,4 +30,15 @@ descriptor */ #define USB_QUIRK_DELAY_INIT 0x00000040 +/* + * For high speed and super speed interupt endpoints, the USB 2.0 and + * USB 3.0 spec require the interval in microframes + * (1 microframe = 125 microseconds) to be calculated as + * interval = 2 ^ (bInterval-1). + * + * Devices with this quirk report their bInterval as the result of this + * calculation instead of the exponent variable used in the calculation. + */ +#define USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL 0x00000080 + #endif /* __LINUX_USB_QUIRKS_H */ diff --git a/include/linux/usb/renesas_usbhs.h b/include/linux/usb/renesas_usbhs.h index e452ba6ec6bd..d5952bb66752 100644 --- a/include/linux/usb/renesas_usbhs.h +++ b/include/linux/usb/renesas_usbhs.h @@ -153,6 +153,9 @@ struct renesas_usbhs_driver_param { */ int pio_dma_border; /* default is 64byte */ + u32 type; + u32 enable_gpio; + /* * option: */ @@ -160,6 +163,9 @@ struct renesas_usbhs_driver_param { u32 has_sudmac:1; /* for SUDMAC */ }; +#define USBHS_TYPE_R8A7790 1 +#define USBHS_TYPE_R8A7791 2 + /* * option: * diff --git a/include/linux/usb/usb338x.h b/include/linux/usb/usb338x.h new file mode 100644 index 000000000000..f92eb635b9d3 --- /dev/null +++ b/include/linux/usb/usb338x.h @@ -0,0 +1,199 @@ +/* + * USB 338x super/high/full speed USB device controller. + * Unlike many such controllers, this one talks PCI. + * + * Copyright (C) 2002 NetChip Technology, Inc. (http://www.netchip.com) + * Copyright (C) 2003 David Brownell + * Copyright (C) 2014 Ricardo Ribalda - Qtechnology/AS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __LINUX_USB_USB338X_H +#define __LINUX_USB_USB338X_H + +#include <linux/usb/net2280.h> + +/* + * Extra defined bits for net2280 registers + */ +#define SCRATCH 0x0b + +#define DEFECT7374_FSM_FIELD 28 +#define SUPER_SPEED 8 +#define DMA_REQUEST_OUTSTANDING 5 +#define DMA_PAUSE_DONE_INTERRUPT 26 +#define SET_ISOCHRONOUS_DELAY 24 +#define SET_SEL 22 +#define SUPER_SPEED_MODE 8 + +/*ep_cfg*/ +#define MAX_BURST_SIZE 24 +#define EP_FIFO_BYTE_COUNT 16 +#define IN_ENDPOINT_ENABLE 14 +#define IN_ENDPOINT_TYPE 12 +#define OUT_ENDPOINT_ENABLE 10 +#define OUT_ENDPOINT_TYPE 8 + +struct usb338x_usb_ext_regs { + u32 usbclass; +#define DEVICE_PROTOCOL 16 +#define DEVICE_SUB_CLASS 8 +#define DEVICE_CLASS 0 + u32 ss_sel; +#define U2_SYSTEM_EXIT_LATENCY 8 +#define U1_SYSTEM_EXIT_LATENCY 0 + u32 ss_del; +#define U2_DEVICE_EXIT_LATENCY 8 +#define U1_DEVICE_EXIT_LATENCY 0 + u32 usb2lpm; +#define USB_L1_LPM_HIRD 2 +#define USB_L1_LPM_REMOTE_WAKE 1 +#define USB_L1_LPM_SUPPORT 0 + u32 usb3belt; +#define BELT_MULTIPLIER 10 +#define BEST_EFFORT_LATENCY_TOLERANCE 0 + u32 usbctl2; +#define LTM_ENABLE 7 +#define U2_ENABLE 6 +#define U1_ENABLE 5 +#define FUNCTION_SUSPEND 4 +#define USB3_CORE_ENABLE 3 +#define USB2_CORE_ENABLE 2 +#define SERIAL_NUMBER_STRING_ENABLE 0 + u32 in_timeout; +#define GPEP3_TIMEOUT 19 +#define GPEP2_TIMEOUT 18 +#define GPEP1_TIMEOUT 17 +#define GPEP0_TIMEOUT 16 +#define GPEP3_TIMEOUT_VALUE 13 +#define GPEP3_TIMEOUT_ENABLE 12 +#define GPEP2_TIMEOUT_VALUE 9 +#define GPEP2_TIMEOUT_ENABLE 8 +#define GPEP1_TIMEOUT_VALUE 5 +#define GPEP1_TIMEOUT_ENABLE 4 +#define GPEP0_TIMEOUT_VALUE 1 +#define GPEP0_TIMEOUT_ENABLE 0 + u32 isodelay; +#define ISOCHRONOUS_DELAY 0 +} __packed; + +struct usb338x_fifo_regs { + /* offset 0x0500, 0x0520, 0x0540, 0x0560, 0x0580 */ + u32 ep_fifo_size_base; +#define IN_FIFO_BASE_ADDRESS 22 +#define IN_FIFO_SIZE 16 +#define OUT_FIFO_BASE_ADDRESS 6 +#define OUT_FIFO_SIZE 0 + u32 ep_fifo_out_wrptr; + u32 ep_fifo_out_rdptr; + u32 ep_fifo_in_wrptr; + u32 ep_fifo_in_rdptr; + u32 unused[3]; +} __packed; + + +/* Link layer */ +struct usb338x_ll_regs { + /* offset 0x700 */ + u32 ll_ltssm_ctrl1; + u32 ll_ltssm_ctrl2; + u32 ll_ltssm_ctrl3; + u32 unused[2]; + u32 ll_general_ctrl0; + u32 ll_general_ctrl1; +#define PM_U3_AUTO_EXIT 29 +#define PM_U2_AUTO_EXIT 28 +#define PM_U1_AUTO_EXIT 27 +#define PM_FORCE_U2_ENTRY 26 +#define PM_FORCE_U1_ENTRY 25 +#define PM_LGO_COLLISION_SEND_LAU 24 +#define PM_DIR_LINK_REJECT 23 +#define PM_FORCE_LINK_ACCEPT 22 +#define PM_DIR_ENTRY_U3 20 +#define PM_DIR_ENTRY_U2 19 +#define PM_DIR_ENTRY_U1 18 +#define PM_U2_ENABLE 17 +#define PM_U1_ENABLE 16 +#define SKP_THRESHOLD_ADJUST_FMW 8 +#define RESEND_DPP_ON_LRTY_FMW 7 +#define DL_BIT_VALUE_FMW 6 +#define FORCE_DL_BIT 5 + u32 ll_general_ctrl2; +#define SELECT_INVERT_LANE_POLARITY 7 +#define FORCE_INVERT_LANE_POLARITY 6 + u32 ll_general_ctrl3; + u32 ll_general_ctrl4; + u32 ll_error_gen; +} __packed; + +struct usb338x_ll_lfps_regs { + /* offset 0x748 */ + u32 ll_lfps_5; +#define TIMER_LFPS_6US 16 + u32 ll_lfps_6; +#define TIMER_LFPS_80US 0 +} __packed; + +struct usb338x_ll_tsn_regs { + /* offset 0x77C */ + u32 ll_tsn_counters_2; +#define HOT_TX_NORESET_TS2 24 + u32 ll_tsn_counters_3; +#define HOT_RX_RESET_TS2 0 +} __packed; + +struct usb338x_ll_chi_regs { + /* offset 0x79C */ + u32 ll_tsn_chicken_bit; +#define RECOVERY_IDLE_TO_RECOVER_FMW 3 +} __packed; + +/* protocol layer */ +struct usb338x_pl_regs { + /* offset 0x800 */ + u32 pl_reg_1; + u32 pl_reg_2; + u32 pl_reg_3; + u32 pl_reg_4; + u32 pl_ep_ctrl; + /* Protocol Layer Endpoint Control*/ +#define PL_EP_CTRL 0x810 +#define ENDPOINT_SELECT 0 + /* [4:0] */ +#define EP_INITIALIZED 16 +#define SEQUENCE_NUMBER_RESET 17 +#define CLEAR_ACK_ERROR_CODE 20 + u32 pl_reg_6; + u32 pl_reg_7; + u32 pl_reg_8; + u32 pl_ep_status_1; + /* Protocol Layer Endpoint Status 1*/ +#define PL_EP_STATUS_1 0x820 +#define STATE 16 +#define ACK_GOOD_NORMAL 0x11 +#define ACK_GOOD_MORE_ACKS_TO_COME 0x16 + u32 pl_ep_status_2; + u32 pl_ep_status_3; + /* Protocol Layer Endpoint Status 3*/ +#define PL_EP_STATUS_3 0x828 +#define SEQUENCE_NUMBER 0 + u32 pl_ep_status_4; + /* Protocol Layer Endpoint Status 4*/ +#define PL_EP_STATUS_4 0x82c + u32 pl_ep_cfg_4; + /* Protocol Layer Endpoint Configuration 4*/ +#define PL_EP_CFG_4 0x830 +#define NON_CTRL_IN_TOLERATE_BAD_DIR 6 +} __packed; + +#endif /* __LINUX_USB_USB338X_H */ diff --git a/include/linux/usb/xhci_pdriver.h b/include/linux/usb/xhci_pdriver.h new file mode 100644 index 000000000000..376654b5b0f7 --- /dev/null +++ b/include/linux/usb/xhci_pdriver.h @@ -0,0 +1,27 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef __USB_CORE_XHCI_PDRIVER_H +#define __USB_CORE_XHCI_PDRIVER_H + +/** + * struct usb_xhci_pdata - platform_data for generic xhci platform driver + * + * @usb3_lpm_capable: determines if this xhci platform supports USB3 + * LPM capability + * + */ +struct usb_xhci_pdata { + unsigned usb3_lpm_capable:1; +}; + +#endif /* __USB_CORE_XHCI_PDRIVER_H */ diff --git a/include/uapi/linux/usb/functionfs.h b/include/uapi/linux/usb/functionfs.h index 24b68c59dcf8..0154b2859fd7 100644 --- a/include/uapi/linux/usb/functionfs.h +++ b/include/uapi/linux/usb/functionfs.h @@ -18,10 +18,9 @@ enum functionfs_flags { FUNCTIONFS_HAS_FS_DESC = 1, FUNCTIONFS_HAS_HS_DESC = 2, FUNCTIONFS_HAS_SS_DESC = 4, + FUNCTIONFS_HAS_MS_OS_DESC = 8, }; -#ifndef __KERNEL__ - /* Descriptor of an non-audio endpoint */ struct usb_endpoint_descriptor_no_audio { __u8 bLength; @@ -41,6 +40,37 @@ struct usb_functionfs_descs_head { __le32 hs_count; } __attribute__((packed, deprecated)); +/* MS OS Descriptor header */ +struct usb_os_desc_header { + __u8 interface; + __le32 dwLength; + __le16 bcdVersion; + __le16 wIndex; + union { + struct { + __u8 bCount; + __u8 Reserved; + }; + __le16 wCount; + }; +} __attribute__((packed)); + +struct usb_ext_compat_desc { + __u8 bFirstInterfaceNumber; + __u8 Reserved1; + __u8 CompatibleID[8]; + __u8 SubCompatibleID[8]; + __u8 Reserved2[6]; +}; + +struct usb_ext_prop_desc { + __le32 dwSize; + __le32 dwPropertyDataType; + __le16 wPropertyNameLength; +} __attribute__((packed)); + +#ifndef __KERNEL__ + /* * Descriptors format: * @@ -52,9 +82,11 @@ struct usb_functionfs_descs_head { * | | fs_count | LE32 | number of full-speed descriptors | * | | hs_count | LE32 | number of high-speed descriptors | * | | ss_count | LE32 | number of super-speed descriptors | + * | | os_count | LE32 | number of MS OS descriptors | * | | fs_descrs | Descriptor[] | list of full-speed descriptors | * | | hs_descrs | Descriptor[] | list of high-speed descriptors | * | | ss_descrs | Descriptor[] | list of super-speed descriptors | + * | | os_descrs | OSDesc[] | list of MS OS descriptors | * * Depending on which flags are set, various fields may be missing in the * structure. Any flags that are not recognised cause the whole block to be @@ -81,6 +113,52 @@ struct usb_functionfs_descs_head { * | 0 | bLength | U8 | length of the descriptor | * | 1 | bDescriptorType | U8 | descriptor type | * | 2 | payload | | descriptor's payload | + * + * OSDesc[] is an array of valid MS OS Feature Descriptors which have one of + * the following formats: + * + * | off | name | type | description | + * |-----+-----------------+------+--------------------------| + * | 0 | inteface | U8 | related interface number | + * | 1 | dwLength | U32 | length of the descriptor | + * | 5 | bcdVersion | U16 | currently supported: 1 | + * | 7 | wIndex | U16 | currently supported: 4 | + * | 9 | bCount | U8 | number of ext. compat. | + * | 10 | Reserved | U8 | 0 | + * | 11 | ExtCompat[] | | list of ext. compat. d. | + * + * | off | name | type | description | + * |-----+-----------------+------+--------------------------| + * | 0 | inteface | U8 | related interface number | + * | 1 | dwLength | U32 | length of the descriptor | + * | 5 | bcdVersion | U16 | currently supported: 1 | + * | 7 | wIndex | U16 | currently supported: 5 | + * | 9 | wCount | U16 | number of ext. compat. | + * | 11 | ExtProp[] | | list of ext. prop. d. | + * + * ExtCompat[] is an array of valid Extended Compatiblity descriptors + * which have the following format: + * + * | off | name | type | description | + * |-----+-----------------------+------+-------------------------------------| + * | 0 | bFirstInterfaceNumber | U8 | index of the interface or of the 1st| + * | | | | interface in an IAD group | + * | 1 | Reserved | U8 | 0 | + * | 2 | CompatibleID | U8[8]| compatible ID string | + * | 10 | SubCompatibleID | U8[8]| subcompatible ID string | + * | 18 | Reserved | U8[6]| 0 | + * + * ExtProp[] is an array of valid Extended Properties descriptors + * which have the following format: + * + * | off | name | type | description | + * |-----+-----------------------+------+-------------------------------------| + * | 0 | dwSize | U32 | length of the descriptor | + * | 4 | dwPropertyDataType | U32 | 1..7 | + * | 8 | wPropertyNameLength | U16 | bPropertyName length (NL) | + * | 10 | bPropertyName |U8[NL]| name of this property | + * |10+NL| dwPropertyDataLength | U32 | bPropertyData length (DL) | + * |14+NL| bProperty |U8[DL]| payload of this property | */ struct usb_functionfs_strings_head { diff --git a/tools/usb/ffs-aio-example/multibuff/device_app/aio_multibuff.c b/tools/usb/ffs-aio-example/multibuff/device_app/aio_multibuff.c index 87216a0c4a8b..af4b0508be77 100644 --- a/tools/usb/ffs-aio-example/multibuff/device_app/aio_multibuff.c +++ b/tools/usb/ffs-aio-example/multibuff/device_app/aio_multibuff.c @@ -1,3 +1,30 @@ +/* + * This is free and unencumbered software released into the public domain. + * + * Anyone is free to copy, modify, publish, use, compile, sell, or + * distribute this software, either in source code form or as a compiled + * binary, for any purpose, commercial or non-commercial, and by any + * means. + * + * In jurisdictions that recognize copyright laws, the author or authors + * of this software dedicate any and all copyright interest in the + * software to the public domain. We make this dedication for the benefit + * of the public at large and to the detriment of our heirs and + * successors. We intend this dedication to be an overt act of + * relinquishment in perpetuity of all present and future rights to this + * software under copyright law. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * For more information, please refer to <http://unlicense.org/> + */ + #define _BSD_SOURCE /* for endian.h */ #include <endian.h> @@ -27,7 +54,9 @@ /******************** Descriptors and Strings *******************************/ static const struct { - struct usb_functionfs_descs_head header; + struct usb_functionfs_descs_head_v2 header; + __le32 fs_count; + __le32 hs_count; struct { struct usb_interface_descriptor intf; struct usb_endpoint_descriptor_no_audio bulk_sink; @@ -35,11 +64,12 @@ static const struct { } __attribute__ ((__packed__)) fs_descs, hs_descs; } __attribute__ ((__packed__)) descriptors = { .header = { - .magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC), + .magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2), + .flags = htole32(FUNCTIONFS_HAS_FS_DESC | + FUNCTIONFS_HAS_HS_DESC), .length = htole32(sizeof(descriptors)), - .fs_count = 3, - .hs_count = 3, }, + .fs_count = htole32(3), .fs_descs = { .intf = { .bLength = sizeof(descriptors.fs_descs.intf), @@ -61,6 +91,7 @@ static const struct { .bmAttributes = USB_ENDPOINT_XFER_BULK, }, }, + .hs_count = htole32(3), .hs_descs = { .intf = { .bLength = sizeof(descriptors.hs_descs.intf), diff --git a/tools/usb/ffs-aio-example/multibuff/host_app/test.c b/tools/usb/ffs-aio-example/multibuff/host_app/test.c index b0ad8747d03f..daa3abe6bebd 100644 --- a/tools/usb/ffs-aio-example/multibuff/host_app/test.c +++ b/tools/usb/ffs-aio-example/multibuff/host_app/test.c @@ -1,3 +1,30 @@ +/* + * This is free and unencumbered software released into the public domain. + * + * Anyone is free to copy, modify, publish, use, compile, sell, or + * distribute this software, either in source code form or as a compiled + * binary, for any purpose, commercial or non-commercial, and by any + * means. + * + * In jurisdictions that recognize copyright laws, the author or authors + * of this software dedicate any and all copyright interest in the + * software to the public domain. We make this dedication for the benefit + * of the public at large and to the detriment of our heirs and + * successors. We intend this dedication to be an overt act of + * relinquishment in perpetuity of all present and future rights to this + * software under copyright law. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * For more information, please refer to <http://unlicense.org/> + */ + #include <libusb.h> #include <stdio.h> #include <string.h> diff --git a/tools/usb/ffs-aio-example/simple/device_app/aio_simple.c b/tools/usb/ffs-aio-example/simple/device_app/aio_simple.c index f558664a3317..adc310a6d489 100644 --- a/tools/usb/ffs-aio-example/simple/device_app/aio_simple.c +++ b/tools/usb/ffs-aio-example/simple/device_app/aio_simple.c @@ -1,3 +1,30 @@ +/* + * This is free and unencumbered software released into the public domain. + * + * Anyone is free to copy, modify, publish, use, compile, sell, or + * distribute this software, either in source code form or as a compiled + * binary, for any purpose, commercial or non-commercial, and by any + * means. + * + * In jurisdictions that recognize copyright laws, the author or authors + * of this software dedicate any and all copyright interest in the + * software to the public domain. We make this dedication for the benefit + * of the public at large and to the detriment of our heirs and + * successors. We intend this dedication to be an overt act of + * relinquishment in perpetuity of all present and future rights to this + * software under copyright law. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * For more information, please refer to <http://unlicense.org/> + */ + #define _BSD_SOURCE /* for endian.h */ #include <endian.h> @@ -25,7 +52,9 @@ /******************** Descriptors and Strings *******************************/ static const struct { - struct usb_functionfs_descs_head header; + struct usb_functionfs_descs_head_v2 header; + __le32 fs_count; + __le32 hs_count; struct { struct usb_interface_descriptor intf; struct usb_endpoint_descriptor_no_audio bulk_sink; @@ -33,11 +62,12 @@ static const struct { } __attribute__ ((__packed__)) fs_descs, hs_descs; } __attribute__ ((__packed__)) descriptors = { .header = { - .magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC), + .magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2), + .flags = htole32(FUNCTIONFS_HAS_FS_DESC | + FUNCTIONFS_HAS_HS_DESC), .length = htole32(sizeof(descriptors)), - .fs_count = 3, - .hs_count = 3, }, + .fs_count = htole32(3), .fs_descs = { .intf = { .bLength = sizeof(descriptors.fs_descs.intf), @@ -59,6 +89,7 @@ static const struct { .bmAttributes = USB_ENDPOINT_XFER_BULK, }, }, + .hs_count = htole32(3), .hs_descs = { .intf = { .bLength = sizeof(descriptors.hs_descs.intf), diff --git a/tools/usb/ffs-aio-example/simple/host_app/test.c b/tools/usb/ffs-aio-example/simple/host_app/test.c index 64b6a57d8ca3..acd6332811f3 100644 --- a/tools/usb/ffs-aio-example/simple/host_app/test.c +++ b/tools/usb/ffs-aio-example/simple/host_app/test.c @@ -1,3 +1,30 @@ +/* + * This is free and unencumbered software released into the public domain. + * + * Anyone is free to copy, modify, publish, use, compile, sell, or + * distribute this software, either in source code form or as a compiled + * binary, for any purpose, commercial or non-commercial, and by any + * means. + * + * In jurisdictions that recognize copyright laws, the author or authors + * of this software dedicate any and all copyright interest in the + * software to the public domain. We make this dedication for the benefit + * of the public at large and to the detriment of our heirs and + * successors. We intend this dedication to be an overt act of + * relinquishment in perpetuity of all present and future rights to this + * software under copyright law. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * For more information, please refer to <http://unlicense.org/> + */ + #include <libusb.h> #include <stdio.h> #include <string.h> |