diff options
192 files changed, 3676 insertions, 1482 deletions
diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uvc b/Documentation/ABI/testing/configfs-usb-gadget-uvc index 889ed45be4ca..611b23e6488d 100644 --- a/Documentation/ABI/testing/configfs-usb-gadget-uvc +++ b/Documentation/ABI/testing/configfs-usb-gadget-uvc @@ -7,6 +7,7 @@ Description: UVC function directory streaming_maxburst 0..15 (ss only) streaming_maxpacket 1..1023 (fs), 1..3072 (hs/ss) streaming_interval 1..16 + function_name string [32] =================== ============================= What: /config/usb-gadget/gadget/functions/uvc.name/control diff --git a/Documentation/ABI/testing/sysfs-bus-thunderbolt b/Documentation/ABI/testing/sysfs-bus-thunderbolt index b7e87f6c7d47..f7570c240ce8 100644 --- a/Documentation/ABI/testing/sysfs-bus-thunderbolt +++ b/Documentation/ABI/testing/sysfs-bus-thunderbolt @@ -293,6 +293,16 @@ Contact: thunderbolt-software@lists.01.org Description: This contains XDomain service specific settings as bitmask. Format: %x +What: /sys/bus/thunderbolt/devices/usb4_portX/connector +Date: April 2022 +Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com> +Description: + Symlink to the USB Type-C connector. This link is only + created when USB Type-C Connector Class is enabled, + and only if the system firmware is capable of + describing the connection between a port and its + connector. + What: /sys/bus/thunderbolt/devices/usb4_portX/link Date: Sep 2021 KernelVersion: v5.14 diff --git a/Documentation/devicetree/bindings/usb/am33xx-usb.txt b/Documentation/devicetree/bindings/usb/am33xx-usb.txt index 7a198a30408a..654ffc62d013 100644 --- a/Documentation/devicetree/bindings/usb/am33xx-usb.txt +++ b/Documentation/devicetree/bindings/usb/am33xx-usb.txt @@ -61,8 +61,9 @@ DMA endpoint number (0 … 14 for endpoints 1 … 15 on instance 0 and 15 … 29 for endpoints 1 … 15 on instance 1). The second number is 0 for RX and 1 for TX transfers. -- #dma-channels: should be set to 30 representing the 15 endpoints for +- dma-channels: should be set to 30 representing the 15 endpoints for each USB instance. +- #dma-channels: deprecated Example: ~~~~~~~~ @@ -193,7 +194,7 @@ usb: usb@47400000 { interrupts = <17>; interrupt-names = "glue"; #dma-cells = <2>; - #dma-channels = <30>; - #dma-requests = <256>; + dma-channels = <30>; + dma-requests = <256>; }; }; diff --git a/Documentation/devicetree/bindings/usb/da8xx-usb.txt b/Documentation/devicetree/bindings/usb/da8xx-usb.txt index 9ce22551b2b3..fb2027a7d80d 100644 --- a/Documentation/devicetree/bindings/usb/da8xx-usb.txt +++ b/Documentation/devicetree/bindings/usb/da8xx-usb.txt @@ -36,7 +36,8 @@ DMA - #dma-cells: should be set to 2. The first number represents the channel number (0 … 3 for endpoints 1 … 4). The second number is 0 for RX and 1 for TX transfers. -- #dma-channels: should be set to 4 representing the 4 endpoints. +- dma-channels: should be set to 4 representing the 4 endpoints. +- #dma-channels: deprecated Example: usb_phy: usb-phy { @@ -74,7 +75,7 @@ Example: reg-names = "controller", "scheduler", "queuemgr"; interrupts = <58>; #dma-cells = <2>; - #dma-channels = <4>; + dma-channels = <4>; }; }; diff --git a/Documentation/devicetree/bindings/usb/dwc2.yaml b/Documentation/devicetree/bindings/usb/dwc2.yaml index 17fc471a12bd..8d22a9843ba5 100644 --- a/Documentation/devicetree/bindings/usb/dwc2.yaml +++ b/Documentation/devicetree/bindings/usb/dwc2.yaml @@ -17,6 +17,13 @@ properties: oneOf: - const: brcm,bcm2835-usb - const: hisilicon,hi6220-usb + - const: ingenic,jz4775-otg + - const: ingenic,jz4780-otg + - const: ingenic,x1000-otg + - const: ingenic,x1600-otg + - const: ingenic,x1700-otg + - const: ingenic,x1830-otg + - const: ingenic,x2000-otg - items: - const: rockchip,rk3066-usb - const: snps,dwc2 diff --git a/Documentation/devicetree/bindings/usb/dwc3-xilinx.yaml b/Documentation/devicetree/bindings/usb/dwc3-xilinx.yaml index f77c16e203d5..098b73134a1b 100644 --- a/Documentation/devicetree/bindings/usb/dwc3-xilinx.yaml +++ b/Documentation/devicetree/bindings/usb/dwc3-xilinx.yaml @@ -71,6 +71,10 @@ properties: - usb2-phy - usb3-phy + reset-gpios: + description: GPIO used for the reset ulpi-phy + maxItems: 1 + # Required child node: patternProperties: diff --git a/Documentation/devicetree/bindings/usb/fcs,fsa4480.yaml b/Documentation/devicetree/bindings/usb/fcs,fsa4480.yaml new file mode 100644 index 000000000000..9473f26b0621 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/fcs,fsa4480.yaml @@ -0,0 +1,72 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/usb/fcs,fsa4480.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: ON Semiconductor Analog Audio Switch + +maintainers: + - Bjorn Andersson <bjorn.andersson@linaro.org> + +properties: + compatible: + enum: + - fcs,fsa4480 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + vcc-supply: + description: power supply (2.7V-5.5V) + + mode-switch: + description: Flag the port as possible handle of altmode switching + type: boolean + + orientation-switch: + description: Flag the port as possible handler of orientation switching + type: boolean + + port: + $ref: /schemas/graph.yaml#/properties/port + description: + A port node to link the FSA4480 to a TypeC controller for the purpose of + handling altmode muxing and orientation switching. + +required: + - compatible + - reg + - port + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + i2c13 { + #address-cells = <1>; + #size-cells = <0>; + + fsa4480@42 { + compatible = "fcs,fsa4480"; + reg = <0x42>; + + interrupts-extended = <&tlmm 2 IRQ_TYPE_LEVEL_LOW>; + + vcc-supply = <&vreg_bob>; + + mode-switch; + orientation-switch; + + port { + fsa4480_ept: endpoint { + remote-endpoint = <&typec_controller>; + }; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/usb/generic-ehci.yaml b/Documentation/devicetree/bindings/usb/generic-ehci.yaml index 8913497624de..0b4524b6409e 100644 --- a/Documentation/devicetree/bindings/usb/generic-ehci.yaml +++ b/Documentation/devicetree/bindings/usb/generic-ehci.yaml @@ -55,6 +55,7 @@ properties: - brcm,bcm7420-ehci - brcm,bcm7425-ehci - brcm,bcm7435-ehci + - hpe,gxp-ehci - ibm,476gtr-ehci - nxp,lpc1850-ehci - qca,ar7100-ehci diff --git a/Documentation/devicetree/bindings/usb/generic-ohci.yaml b/Documentation/devicetree/bindings/usb/generic-ohci.yaml index acbf94fa5f74..e2ac84665316 100644 --- a/Documentation/devicetree/bindings/usb/generic-ohci.yaml +++ b/Documentation/devicetree/bindings/usb/generic-ohci.yaml @@ -42,6 +42,7 @@ properties: - brcm,bcm7420-ohci - brcm,bcm7425-ohci - brcm,bcm7435-ohci + - hpe,gxp-ohci - ibm,476gtr-ohci - ingenic,jz4740-ohci - snps,hsdk-v1.0-ohci diff --git a/Documentation/devicetree/bindings/usb/mediatek,mtu3.yaml b/Documentation/devicetree/bindings/usb/mediatek,mtu3.yaml index df766f8de872..37b02a841dc4 100644 --- a/Documentation/devicetree/bindings/usb/mediatek,mtu3.yaml +++ b/Documentation/devicetree/bindings/usb/mediatek,mtu3.yaml @@ -25,6 +25,7 @@ properties: - mediatek,mt8173-mtu3 - mediatek,mt8183-mtu3 - mediatek,mt8192-mtu3 + - mediatek,mt8195-mtu3 - const: mediatek,mtu3 reg: diff --git a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml index ce252db2aab3..e336fe2e03cc 100644 --- a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml @@ -16,16 +16,21 @@ properties: - qcom,ipq4019-dwc3 - qcom,ipq6018-dwc3 - qcom,ipq8064-dwc3 + - qcom,ipq8074-dwc3 - qcom,msm8953-dwc3 + - qcom,msm8994-dwc3 - qcom,msm8996-dwc3 - qcom,msm8998-dwc3 + - qcom,qcs404-dwc3 - qcom,sc7180-dwc3 - qcom,sc7280-dwc3 - qcom,sdm660-dwc3 - qcom,sdm845-dwc3 - qcom,sdx55-dwc3 + - qcom,sdx65-dwc3 - qcom,sm4250-dwc3 - qcom,sm6115-dwc3 + - qcom,sm6125-dwc3 - qcom,sm6350-dwc3 - qcom,sm8150-dwc3 - qcom,sm8250-dwc3 @@ -50,26 +55,22 @@ properties: maxItems: 1 clocks: - description: - A list of phandle and clock-specifier pairs for the clocks - listed in clock-names. - items: - - description: System Config NOC clock. - - description: Master/Core clock, has to be >= 125 MHz - for SS operation and >= 60MHz for HS operation. - - description: System bus AXI clock. - - description: Mock utmi clock needed for ITP/SOF generation - in host mode. Its frequency should be 19.2MHz. - - description: Sleep clock, used for wakeup when - USB3 core goes into low power mode (U3). + description: | + Several clocks are used, depending on the variant. Typical ones are:: + - cfg_noc:: System Config NOC clock. + - core:: Master/Core clock, has to be >= 125 MHz for SS operation and >= + 60MHz for HS operation. + - iface:: System bus AXI clock. + - sleep:: Sleep clock, used for wakeup when USB3 core goes into low + power mode (U3). + - mock_utmi:: Mock utmi clock needed for ITP/SOF generation in host + mode. Its frequency should be 19.2MHz. + minItems: 1 + maxItems: 6 clock-names: - items: - - const: cfg_noc - - const: core - - const: iface - - const: mock_utmi - - const: sleep + minItems: 1 + maxItems: 6 assigned-clocks: items: @@ -132,6 +133,185 @@ required: - interrupts - interrupt-names +allOf: + - if: + properties: + compatible: + contains: + enum: + - qcom,ipq4019-dwc3 + then: + properties: + clocks: + maxItems: 3 + clock-names: + items: + - const: core + - const: sleep + - const: mock_utmi + + - if: + properties: + compatible: + contains: + enum: + - qcom,ipq8064-dwc3 + then: + properties: + clocks: + items: + - description: Master/Core clock, has to be >= 125 MHz + for SS operation and >= 60MHz for HS operation. + clock-names: + items: + - const: core + + - if: + properties: + compatible: + contains: + enum: + - qcom,msm8953-dwc3 + - qcom,msm8996-dwc3 + - qcom,msm8998-dwc3 + - qcom,sc7180-dwc3 + - qcom,sc7280-dwc3 + - qcom,sdm845-dwc3 + - qcom,sdx55-dwc3 + - qcom,sm6350-dwc3 + then: + properties: + clocks: + maxItems: 5 + clock-names: + items: + - const: cfg_noc + - const: core + - const: iface + - const: sleep + - const: mock_utmi + + - if: + properties: + compatible: + contains: + enum: + - qcom,ipq6018-dwc3 + then: + properties: + clocks: + minItems: 3 + maxItems: 4 + clock-names: + oneOf: + - items: + - const: core + - const: sleep + - const: mock_utmi + - items: + - const: cfg_noc + - const: core + - const: sleep + - const: mock_utmi + + - if: + properties: + compatible: + contains: + enum: + - qcom,ipq8074-dwc3 + then: + properties: + clocks: + maxItems: 4 + clock-names: + items: + - const: cfg_noc + - const: core + - const: sleep + - const: mock_utmi + + - if: + properties: + compatible: + contains: + enum: + - qcom,msm8994-dwc3 + - qcom,qcs404-dwc3 + then: + properties: + clocks: + maxItems: 4 + clock-names: + items: + - const: core + - const: iface + - const: sleep + - const: mock_utmi + + - if: + properties: + compatible: + contains: + enum: + - qcom,sdm660-dwc3 + then: + properties: + clocks: + minItems: 6 + clock-names: + items: + - const: cfg_noc + - const: core + - const: iface + - const: sleep + - const: mock_utmi + - const: bus + + - if: + properties: + compatible: + contains: + enum: + - qcom,sm6125-dwc3 + - qcom,sm8150-dwc3 + - qcom,sm8250-dwc3 + - qcom,sm8450-dwc3 + then: + properties: + clocks: + minItems: 6 + clock-names: + items: + - const: cfg_noc + - const: core + - const: iface + - const: sleep + - const: mock_utmi + - const: xo + + - if: + properties: + compatible: + contains: + enum: + - qcom,sm8350-dwc3 + then: + properties: + clocks: + minItems: 5 + maxItems: 6 + clock-names: + minItems: 5 + items: + - const: cfg_noc + - const: core + - const: iface + - const: sleep + - const: mock_utmi + - const: xo + + additionalProperties: false examples: @@ -153,10 +333,13 @@ examples: clocks = <&gcc GCC_CFG_NOC_USB3_PRIM_AXI_CLK>, <&gcc GCC_USB30_PRIM_MASTER_CLK>, <&gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>, - <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>, - <&gcc GCC_USB30_PRIM_SLEEP_CLK>; - clock-names = "cfg_noc", "core", "iface", "mock_utmi", - "sleep"; + <&gcc GCC_USB30_PRIM_SLEEP_CLK>, + <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>; + clock-names = "cfg_noc", + "core", + "iface", + "sleep", + "mock_utmi"; assigned-clocks = <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>, <&gcc GCC_USB30_PRIM_MASTER_CLK>; diff --git a/Documentation/devicetree/bindings/usb/renesas,usbhs.yaml b/Documentation/devicetree/bindings/usb/renesas,usbhs.yaml index 0bb841b28003..bad55dfb2fa0 100644 --- a/Documentation/devicetree/bindings/usb/renesas,usbhs.yaml +++ b/Documentation/devicetree/bindings/usb/renesas,usbhs.yaml @@ -19,6 +19,7 @@ properties: - items: - enum: - renesas,usbhs-r7s9210 # RZ/A2 + - renesas,usbhs-r9a07g043 # RZ/G2UL - renesas,usbhs-r9a07g044 # RZ/G2{L,LC} - renesas,usbhs-r9a07g054 # RZ/V2L - const: renesas,rza2-usbhs @@ -118,6 +119,7 @@ allOf: compatible: contains: enum: + - renesas,usbhs-r9a07g043 - renesas,usbhs-r9a07g044 - renesas,usbhs-r9a07g054 then: @@ -128,6 +130,8 @@ allOf: - description: U2P_INT_DMA[0] - description: U2P_INT_DMA[1] - description: U2P_INT_DMAERR + required: + - resets else: properties: interrupts: diff --git a/Documentation/devicetree/bindings/usb/samsung,exynos-usb2.yaml b/Documentation/devicetree/bindings/usb/samsung,exynos-usb2.yaml index 9c92defbba01..caa572dcee02 100644 --- a/Documentation/devicetree/bindings/usb/samsung,exynos-usb2.yaml +++ b/Documentation/devicetree/bindings/usb/samsung,exynos-usb2.yaml @@ -15,9 +15,6 @@ properties: - samsung,exynos4210-ehci - samsung,exynos4210-ohci - '#address-cells': - const: 1 - clocks: maxItems: 1 @@ -46,15 +43,6 @@ properties: Only for controller in EHCI mode, if present, specifies the GPIO that needs to be pulled up for the bus to be powered. - '#size-cells': - const: 0 - -patternProperties: - "^.*@[0-9a-f]{1,2}$": - description: The hard wired USB devices - type: object - $ref: /usb/usb-device.yaml - required: - compatible - clocks @@ -65,6 +53,7 @@ required: - reg allOf: + - $ref: usb-hcd.yaml# - if: properties: compatible: @@ -74,7 +63,7 @@ allOf: properties: samsung,vbus-gpio: false -additionalProperties: false +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/usb/ti,am62-usb.yaml b/Documentation/devicetree/bindings/usb/ti,am62-usb.yaml new file mode 100644 index 000000000000..d25fc708e32c --- /dev/null +++ b/Documentation/devicetree/bindings/usb/ti,am62-usb.yaml @@ -0,0 +1,103 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/ti,am62-usb.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI's AM62 wrapper module for the Synopsys USBSS-DRD controller + +maintainers: + - Aswath Govindraju <a-govindraju@ti.com> + +properties: + compatible: + const: ti,am62-usb + + reg: + maxItems: 1 + + ranges: true + + power-domains: + description: + PM domain provider node and an args specifier containing + the USB ISO device id value. See, + Documentation/devicetree/bindings/soc/ti/sci-pm-domain.yaml + maxItems: 1 + + clocks: + description: Clock phandle to usb2_refclk + maxItems: 1 + + clock-names: + items: + - const: ref + + ti,vbus-divider: + description: + Should be present if USB VBUS line is connected to the + VBUS pin of the SoC via a 1/3 voltage divider. + type: boolean + + ti,syscon-phy-pll-refclk: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: Phandle to the SYSCON entry + - description: USB phy control register offset within SYSCON + description: + Specifier for conveying frequency of ref clock input, for the + operation of USB2PHY. + + '#address-cells': + const: 2 + + '#size-cells': + const: 2 + +patternProperties: + "^usb@[0-9a-f]+$": + $ref: snps,dwc3.yaml# + description: Required child node + +required: + - compatible + - reg + - power-domains + - clocks + - clock-names + - ti,syscon-phy-pll-refclk + +additionalProperties: false + +examples: + - | + #include <dt-bindings/soc/ti,sci_pm_domain.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/gpio/gpio.h> + + bus { + #address-cells = <2>; + #size-cells = <2>; + + usbss1: usb@f910000 { + compatible = "ti,am62-usb"; + reg = <0x00 0x0f910000 0x00 0x800>; + clocks = <&k3_clks 162 3>; + clock-names = "ref"; + ti,syscon-phy-pll-refclk = <&wkup_conf 0x4018>; + power-domains = <&k3_pds 179 TI_SCI_PD_EXCLUSIVE>; + #address-cells = <2>; + #size-cells = <2>; + + usb@31100000 { + compatible = "snps,dwc3"; + reg =<0x00 0x31100000 0x00 0x50000>; + interrupts = <GIC_SPI 226 IRQ_TYPE_LEVEL_HIGH>, /* irq.0 */ + <GIC_SPI 226 IRQ_TYPE_LEVEL_HIGH>; /* irq.0 */ + interrupt-names = "host", "peripheral"; + maximum-speed = "high-speed"; + dr_mode = "otg"; + }; + }; + }; diff --git a/Documentation/usb/gadget-testing.rst b/Documentation/usb/gadget-testing.rst index c6d034abce3a..1c37159fa171 100644 --- a/Documentation/usb/gadget-testing.rst +++ b/Documentation/usb/gadget-testing.rst @@ -787,6 +787,7 @@ The uvc function provides these attributes in its function directory: streaming_maxpacket maximum packet size this endpoint is capable of sending or receiving when this configuration is selected + function_name name of the interface =================== ================================================ There are also "control" and "streaming" subdirectories, each of which contain diff --git a/arch/arm/boot/dts/qcom-ipq4019.dtsi b/arch/arm/boot/dts/qcom-ipq4019.dtsi index 9d5e934f2272..c5da723f7674 100644 --- a/arch/arm/boot/dts/qcom-ipq4019.dtsi +++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi @@ -646,7 +646,7 @@ clocks = <&gcc GCC_USB3_MASTER_CLK>, <&gcc GCC_USB3_SLEEP_CLK>, <&gcc GCC_USB3_MOCK_UTMI_CLK>; - clock-names = "master", "sleep", "mock_utmi"; + clock-names = "core", "sleep", "mock_utmi"; ranges; status = "disabled"; diff --git a/arch/arm/boot/dts/qcom-sdx55.dtsi b/arch/arm/boot/dts/qcom-sdx55.dtsi index 123390721b7f..1c2b208a5670 100644 --- a/arch/arm/boot/dts/qcom-sdx55.dtsi +++ b/arch/arm/boot/dts/qcom-sdx55.dtsi @@ -483,10 +483,13 @@ clocks = <&gcc GCC_USB30_SLV_AHB_CLK>, <&gcc GCC_USB30_MASTER_CLK>, <&gcc GCC_USB30_MSTR_AXI_CLK>, - <&gcc GCC_USB30_MOCK_UTMI_CLK>, - <&gcc GCC_USB30_SLEEP_CLK>; - clock-names = "cfg_noc", "core", "iface", "mock_utmi", - "sleep"; + <&gcc GCC_USB30_SLEEP_CLK>, + <&gcc GCC_USB30_MOCK_UTMI_CLK>; + clock-names = "cfg_noc", + "core", + "iface", + "sleep", + "mock_utmi"; assigned-clocks = <&gcc GCC_USB30_MOCK_UTMI_CLK>, <&gcc GCC_USB30_MASTER_CLK>; diff --git a/arch/arm64/boot/dts/qcom/apq8096-db820c.dts b/arch/arm64/boot/dts/qcom/apq8096-db820c.dts index 56e54ce4d10e..49afbb1a066a 100644 --- a/arch/arm64/boot/dts/qcom/apq8096-db820c.dts +++ b/arch/arm64/boot/dts/qcom/apq8096-db820c.dts @@ -1052,22 +1052,22 @@ &usb2 { status = "okay"; extcon = <&usb2_id>; +}; - dwc3@7600000 { - extcon = <&usb2_id>; - dr_mode = "otg"; - maximum-speed = "high-speed"; - }; +&usb2_dwc3 { + extcon = <&usb2_id>; + dr_mode = "otg"; + maximum-speed = "high-speed"; }; &usb3 { status = "okay"; extcon = <&usb3_id>; +}; - dwc3@6a00000 { - extcon = <&usb3_id>; - dr_mode = "otg"; - }; +&usb3_dwc3 { + extcon = <&usb3_id>; + dr_mode = "otg"; }; &usb3phy { diff --git a/arch/arm64/boot/dts/qcom/ipq6018.dtsi b/arch/arm64/boot/dts/qcom/ipq6018.dtsi index a4d363c187fc..c89499e366d3 100644 --- a/arch/arm64/boot/dts/qcom/ipq6018.dtsi +++ b/arch/arm64/boot/dts/qcom/ipq6018.dtsi @@ -653,7 +653,7 @@ status = "disabled"; }; - usb2: usb2@7000000 { + usb2: usb@70f8800 { compatible = "qcom,ipq6018-dwc3", "qcom,dwc3"; reg = <0x0 0x070F8800 0x0 0x400>; #address-cells = <2>; @@ -662,7 +662,7 @@ clocks = <&gcc GCC_USB1_MASTER_CLK>, <&gcc GCC_USB1_SLEEP_CLK>, <&gcc GCC_USB1_MOCK_UTMI_CLK>; - clock-names = "master", + clock-names = "core", "sleep", "mock_utmi"; @@ -730,7 +730,7 @@ status = "disabled"; }; - usb3: usb3@8A00000 { + usb3: usb@8af8800 { compatible = "qcom,ipq6018-dwc3", "qcom,dwc3"; reg = <0x0 0x8AF8800 0x0 0x400>; #address-cells = <2>; @@ -741,8 +741,8 @@ <&gcc GCC_USB0_MASTER_CLK>, <&gcc GCC_USB0_SLEEP_CLK>, <&gcc GCC_USB0_MOCK_UTMI_CLK>; - clock-names = "sys_noc_axi", - "master", + clock-names = "cfg_noc", + "core", "sleep", "mock_utmi"; @@ -756,7 +756,7 @@ resets = <&gcc GCC_USB0_BCR>; status = "disabled"; - dwc_0: usb@8A00000 { + dwc_0: usb@8a00000 { compatible = "snps,dwc3"; reg = <0x0 0x8A00000 0x0 0xcd00>; interrupts = <GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>; diff --git a/arch/arm64/boot/dts/qcom/ipq8074.dtsi b/arch/arm64/boot/dts/qcom/ipq8074.dtsi index 943243d5515b..4c38b15c6fd4 100644 --- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi +++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi @@ -553,7 +553,7 @@ }; usb_0: usb@8af8800 { - compatible = "qcom,dwc3"; + compatible = "qcom,ipq8074-dwc3", "qcom,dwc3"; reg = <0x08af8800 0x400>; #address-cells = <1>; #size-cells = <1>; @@ -563,8 +563,8 @@ <&gcc GCC_USB0_MASTER_CLK>, <&gcc GCC_USB0_SLEEP_CLK>, <&gcc GCC_USB0_MOCK_UTMI_CLK>; - clock-names = "sys_noc_axi", - "master", + clock-names = "cfg_noc", + "core", "sleep", "mock_utmi"; @@ -578,7 +578,7 @@ resets = <&gcc GCC_USB0_BCR>; status = "disabled"; - dwc_0: dwc3@8a00000 { + dwc_0: usb@8a00000 { compatible = "snps,dwc3"; reg = <0x8a00000 0xcd00>; interrupts = <GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>; @@ -593,7 +593,7 @@ }; usb_1: usb@8cf8800 { - compatible = "qcom,dwc3"; + compatible = "qcom,ipq8074-dwc3", "qcom,dwc3"; reg = <0x08cf8800 0x400>; #address-cells = <1>; #size-cells = <1>; @@ -603,8 +603,8 @@ <&gcc GCC_USB1_MASTER_CLK>, <&gcc GCC_USB1_SLEEP_CLK>, <&gcc GCC_USB1_MOCK_UTMI_CLK>; - clock-names = "sys_noc_axi", - "master", + clock-names = "cfg_noc", + "core", "sleep", "mock_utmi"; @@ -618,7 +618,7 @@ resets = <&gcc GCC_USB1_BCR>; status = "disabled"; - dwc_1: dwc3@8c00000 { + dwc_1: usb@8c00000 { compatible = "snps,dwc3"; reg = <0x8c00000 0xcd00>; interrupts = <GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>; diff --git a/arch/arm64/boot/dts/qcom/msm8953.dtsi b/arch/arm64/boot/dts/qcom/msm8953.dtsi index 49903a6e9dfd..ffc3ec2cd3bc 100644 --- a/arch/arm64/boot/dts/qcom/msm8953.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8953.dtsi @@ -759,10 +759,13 @@ clocks = <&gcc GCC_USB_PHY_CFG_AHB_CLK>, <&gcc GCC_USB30_MASTER_CLK>, <&gcc GCC_PCNOC_USB3_AXI_CLK>, - <&gcc GCC_USB30_MOCK_UTMI_CLK>, - <&gcc GCC_USB30_SLEEP_CLK>; - clock-names = "cfg_noc", "core", "iface", - "mock_utmi", "sleep"; + <&gcc GCC_USB30_SLEEP_CLK>, + <&gcc GCC_USB30_MOCK_UTMI_CLK>; + clock-names = "cfg_noc", + "core", + "iface", + "sleep", + "mock_utmi"; assigned-clocks = <&gcc GCC_USB30_MOCK_UTMI_CLK>, <&gcc GCC_USB30_MASTER_CLK>; diff --git a/arch/arm64/boot/dts/qcom/msm8994.dtsi b/arch/arm64/boot/dts/qcom/msm8994.dtsi index 367ed913902c..0318d42c5736 100644 --- a/arch/arm64/boot/dts/qcom/msm8994.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8994.dtsi @@ -428,7 +428,7 @@ }; usb3: usb@f92f8800 { - compatible = "qcom,msm8996-dwc3", "qcom,dwc3"; + compatible = "qcom,msm8994-dwc3", "qcom,dwc3"; reg = <0xf92f8800 0x400>; #address-cells = <1>; #size-cells = <1>; @@ -438,7 +438,10 @@ <&gcc GCC_SYS_NOC_USB3_AXI_CLK>, <&gcc GCC_USB30_SLEEP_CLK>, <&gcc GCC_USB30_MOCK_UTMI_CLK>; - clock-names = "core", "iface", "sleep", "mock_utmi", "ref", "xo"; + clock-names = "core", + "iface", + "sleep", + "mock_utmi"; assigned-clocks = <&gcc GCC_USB30_MOCK_UTMI_CLK>, <&gcc GCC_USB30_MASTER_CLK>; diff --git a/arch/arm64/boot/dts/qcom/msm8996-xiaomi-common.dtsi b/arch/arm64/boot/dts/qcom/msm8996-xiaomi-common.dtsi index be4f643b1fd1..a7090befc16f 100644 --- a/arch/arm64/boot/dts/qcom/msm8996-xiaomi-common.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8996-xiaomi-common.dtsi @@ -308,19 +308,19 @@ extcon = <&typec>; qcom,select-utmi-as-pipe-clk; +}; - dwc3@6a00000 { - extcon = <&typec>; +&usb3_dwc3 { + extcon = <&typec>; - /* usb3-phy is not used on this device */ - phys = <&hsusb_phy1>; - phy-names = "usb2-phy"; + /* usb3-phy is not used on this device */ + phys = <&hsusb_phy1>; + phy-names = "usb2-phy"; - maximum-speed = "high-speed"; - snps,is-utmi-l1-suspend; - snps,usb2-gadget-lpm-disable; - snps,hird-threshold = /bits/ 8 <0>; - }; + maximum-speed = "high-speed"; + snps,is-utmi-l1-suspend; + snps,usb2-gadget-lpm-disable; + snps,hird-threshold = /bits/ 8 <0>; }; &hsusb_phy1 { diff --git a/arch/arm64/boot/dts/qcom/msm8996.dtsi b/arch/arm64/boot/dts/qcom/msm8996.dtsi index 205af7b479a8..9932186f7ceb 100644 --- a/arch/arm64/boot/dts/qcom/msm8996.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8996.dtsi @@ -2718,11 +2718,15 @@ interrupt-names = "hs_phy_irq", "ss_phy_irq"; clocks = <&gcc GCC_SYS_NOC_USB3_AXI_CLK>, - <&gcc GCC_USB30_MASTER_CLK>, - <&gcc GCC_AGGRE2_USB3_AXI_CLK>, - <&gcc GCC_USB30_MOCK_UTMI_CLK>, - <&gcc GCC_USB30_SLEEP_CLK>, - <&gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>; + <&gcc GCC_USB30_MASTER_CLK>, + <&gcc GCC_AGGRE2_USB3_AXI_CLK>, + <&gcc GCC_USB30_SLEEP_CLK>, + <&gcc GCC_USB30_MOCK_UTMI_CLK>; + clock-names = "cfg_noc", + "core", + "iface", + "sleep", + "mock_utmi"; assigned-clocks = <&gcc GCC_USB30_MOCK_UTMI_CLK>, <&gcc GCC_USB30_MASTER_CLK>; @@ -2731,7 +2735,7 @@ power-domains = <&gcc USB30_GDSC>; status = "disabled"; - usb3_dwc3: dwc3@6a00000 { + usb3_dwc3: usb@6a00000 { compatible = "snps,dwc3"; reg = <0x06a00000 0xcc00>; interrupts = <0 131 IRQ_TYPE_LEVEL_HIGH>; @@ -3050,6 +3054,11 @@ <&gcc GCC_USB20_MOCK_UTMI_CLK>, <&gcc GCC_USB20_SLEEP_CLK>, <&gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>; + clock-names = "cfg_noc", + "core", + "iface", + "sleep", + "mock_utmi"; assigned-clocks = <&gcc GCC_USB20_MOCK_UTMI_CLK>, <&gcc GCC_USB20_MASTER_CLK>; @@ -3059,7 +3068,7 @@ qcom,select-utmi-as-pipe-clk; status = "disabled"; - dwc3@7600000 { + usb2_dwc3: usb@7600000 { compatible = "snps,dwc3"; reg = <0x07600000 0xcc00>; interrupts = <0 138 IRQ_TYPE_LEVEL_HIGH>; diff --git a/arch/arm64/boot/dts/qcom/msm8998.dtsi b/arch/arm64/boot/dts/qcom/msm8998.dtsi index 4a84de6cee1e..758c45bbbe78 100644 --- a/arch/arm64/boot/dts/qcom/msm8998.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8998.dtsi @@ -2023,10 +2023,13 @@ clocks = <&gcc GCC_CFG_NOC_USB3_AXI_CLK>, <&gcc GCC_USB30_MASTER_CLK>, <&gcc GCC_AGGRE1_USB3_AXI_CLK>, - <&gcc GCC_USB30_MOCK_UTMI_CLK>, - <&gcc GCC_USB30_SLEEP_CLK>; - clock-names = "cfg_noc", "core", "iface", "mock_utmi", - "sleep"; + <&gcc GCC_USB30_SLEEP_CLK>, + <&gcc GCC_USB30_MOCK_UTMI_CLK>; + clock-names = "cfg_noc", + "core", + "iface", + "sleep", + "mock_utmi"; assigned-clocks = <&gcc GCC_USB30_MOCK_UTMI_CLK>, <&gcc GCC_USB30_MASTER_CLK>; @@ -2040,7 +2043,7 @@ resets = <&gcc GCC_USB_30_BCR>; - usb3_dwc3: dwc3@a800000 { + usb3_dwc3: usb@a800000 { compatible = "snps,dwc3"; reg = <0x0a800000 0xcd00>; interrupts = <GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH>; diff --git a/arch/arm64/boot/dts/qcom/qcs404-evb.dtsi b/arch/arm64/boot/dts/qcom/qcs404-evb.dtsi index a80c578484ba..2f3104a84417 100644 --- a/arch/arm64/boot/dts/qcom/qcs404-evb.dtsi +++ b/arch/arm64/boot/dts/qcom/qcs404-evb.dtsi @@ -337,9 +337,10 @@ &usb3 { status = "okay"; - dwc3@7580000 { - dr_mode = "host"; - }; +}; + +&usb3_dwc3 { + dr_mode = "host"; }; &usb2_phy_prim { diff --git a/arch/arm64/boot/dts/qcom/qcs404.dtsi b/arch/arm64/boot/dts/qcom/qcs404.dtsi index bc446c6002d0..d912166b7552 100644 --- a/arch/arm64/boot/dts/qcom/qcs404.dtsi +++ b/arch/arm64/boot/dts/qcom/qcs404.dtsi @@ -529,7 +529,7 @@ }; usb3: usb@7678800 { - compatible = "qcom,dwc3"; + compatible = "qcom,qcs404-dwc3", "qcom,dwc3"; reg = <0x07678800 0x400>; #address-cells = <1>; #size-cells = <1>; @@ -544,7 +544,7 @@ assigned-clock-rates = <19200000>, <200000000>; status = "disabled"; - dwc3@7580000 { + usb3_dwc3: usb@7580000 { compatible = "snps,dwc3"; reg = <0x07580000 0xcd00>; interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>; @@ -558,7 +558,7 @@ }; usb2: usb@79b8800 { - compatible = "qcom,dwc3"; + compatible = "qcom,qcs404-dwc3", "qcom,dwc3"; reg = <0x079b8800 0x400>; #address-cells = <1>; #size-cells = <1>; @@ -573,7 +573,7 @@ assigned-clock-rates = <19200000>, <133333333>; status = "disabled"; - dwc3@78c0000 { + usb@78c0000 { compatible = "snps,dwc3"; reg = <0x078c0000 0xcc00>; interrupts = <GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH>; diff --git a/arch/arm64/boot/dts/qcom/sc7180.dtsi b/arch/arm64/boot/dts/qcom/sc7180.dtsi index 82fa009e540f..5dcaac23a138 100644 --- a/arch/arm64/boot/dts/qcom/sc7180.dtsi +++ b/arch/arm64/boot/dts/qcom/sc7180.dtsi @@ -2755,10 +2755,13 @@ clocks = <&gcc GCC_CFG_NOC_USB3_PRIM_AXI_CLK>, <&gcc GCC_USB30_PRIM_MASTER_CLK>, <&gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>, - <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>, - <&gcc GCC_USB30_PRIM_SLEEP_CLK>; - clock-names = "cfg_noc", "core", "iface", "mock_utmi", - "sleep"; + <&gcc GCC_USB30_PRIM_SLEEP_CLK>, + <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>; + clock-names = "cfg_noc", + "core", + "iface", + "sleep", + "mock_utmi"; assigned-clocks = <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>, <&gcc GCC_USB30_PRIM_MASTER_CLK>; @@ -2779,7 +2782,7 @@ <&gem_noc MASTER_APPSS_PROC 0 &config_noc SLAVE_USB3 0>; interconnect-names = "usb-ddr", "apps-usb"; - usb_1_dwc3: dwc3@a600000 { + usb_1_dwc3: usb@a600000 { compatible = "snps,dwc3"; reg = <0 0x0a600000 0 0xe000>; interrupts = <GIC_SPI 133 IRQ_TYPE_LEVEL_HIGH>; diff --git a/arch/arm64/boot/dts/qcom/sc7280.dtsi b/arch/arm64/boot/dts/qcom/sc7280.dtsi index f72451f7f539..e66fc67de206 100644 --- a/arch/arm64/boot/dts/qcom/sc7280.dtsi +++ b/arch/arm64/boot/dts/qcom/sc7280.dtsi @@ -3069,10 +3069,13 @@ clocks = <&gcc GCC_CFG_NOC_USB3_SEC_AXI_CLK>, <&gcc GCC_USB30_SEC_MASTER_CLK>, <&gcc GCC_AGGRE_USB3_SEC_AXI_CLK>, - <&gcc GCC_USB30_SEC_MOCK_UTMI_CLK>, - <&gcc GCC_USB30_SEC_SLEEP_CLK>; - clock-names = "cfg_noc", "core", "iface","mock_utmi", - "sleep"; + <&gcc GCC_USB30_SEC_SLEEP_CLK>, + <&gcc GCC_USB30_SEC_MOCK_UTMI_CLK>; + clock-names = "cfg_noc", + "core", + "iface", + "sleep", + "mock_utmi"; assigned-clocks = <&gcc GCC_USB30_SEC_MOCK_UTMI_CLK>, <&gcc GCC_USB30_SEC_MASTER_CLK>; @@ -3102,6 +3105,12 @@ phys = <&usb_2_hsphy>; phy-names = "usb2-phy"; maximum-speed = "high-speed"; + usb-role-switch; + port { + usb2_role_switch: endpoint { + remote-endpoint = <&eud_ep>; + }; + }; }; }; @@ -3194,6 +3203,36 @@ interrupts = <GIC_SPI 582 IRQ_TYPE_LEVEL_HIGH>; }; + eud: eud@88e0000 { + compatible = "qcom,sc7280-eud","qcom,eud"; + reg = <0 0x88e0000 0 0x2000>, + <0 0x88e2000 0 0x1000>; + interrupts-extended = <&pdc 11 IRQ_TYPE_LEVEL_HIGH>; + ports { + port@0 { + eud_ep: endpoint { + remote-endpoint = <&usb2_role_switch>; + }; + }; + port@1 { + eud_con: endpoint { + remote-endpoint = <&con_eud>; + }; + }; + }; + }; + + eud_typec: connector { + compatible = "usb-c-connector"; + ports { + port@0 { + con_eud: endpoint { + remote-endpoint = <&eud_con>; + }; + }; + }; + }; + nsp_noc: interconnect@a0c0000 { reg = <0 0x0a0c0000 0 0x10000>; compatible = "qcom,sc7280-nsp-noc"; @@ -3213,21 +3252,26 @@ clocks = <&gcc GCC_CFG_NOC_USB3_PRIM_AXI_CLK>, <&gcc GCC_USB30_PRIM_MASTER_CLK>, <&gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>, - <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>, - <&gcc GCC_USB30_PRIM_SLEEP_CLK>; - clock-names = "cfg_noc", "core", "iface", "mock_utmi", - "sleep"; + <&gcc GCC_USB30_PRIM_SLEEP_CLK>, + <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>; + clock-names = "cfg_noc", + "core", + "iface", + "sleep", + "mock_utmi"; assigned-clocks = <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>, <&gcc GCC_USB30_PRIM_MASTER_CLK>; assigned-clock-rates = <19200000>, <200000000>; interrupts-extended = <&intc GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH>, - <&pdc 14 IRQ_TYPE_EDGE_BOTH>, + <&pdc 17 IRQ_TYPE_EDGE_BOTH>, <&pdc 15 IRQ_TYPE_EDGE_BOTH>, - <&pdc 17 IRQ_TYPE_LEVEL_HIGH>; - interrupt-names = "hs_phy_irq", "dp_hs_phy_irq", - "dm_hs_phy_irq", "ss_phy_irq"; + <&pdc 14 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "hs_phy_irq", + "ss_phy_irq", + "dm_hs_phy_irq", + "dp_hs_phy_irq"; power-domains = <&gcc GCC_USB30_PRIM_GDSC>; diff --git a/arch/arm64/boot/dts/qcom/sdm630.dtsi b/arch/arm64/boot/dts/qcom/sdm630.dtsi index 7f875bf9390a..b72e8e6c52f3 100644 --- a/arch/arm64/boot/dts/qcom/sdm630.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm630.dtsi @@ -1215,11 +1215,15 @@ clocks = <&gcc GCC_CFG_NOC_USB3_AXI_CLK>, <&gcc GCC_USB30_MASTER_CLK>, <&gcc GCC_AGGRE2_USB3_AXI_CLK>, - <&rpmcc RPM_SMD_AGGR2_NOC_CLK>, + <&gcc GCC_USB30_SLEEP_CLK>, <&gcc GCC_USB30_MOCK_UTMI_CLK>, - <&gcc GCC_USB30_SLEEP_CLK>; - clock-names = "cfg_noc", "core", "iface", "bus", - "mock_utmi", "sleep"; + <&rpmcc RPM_SMD_AGGR2_NOC_CLK>; + clock-names = "cfg_noc", + "core", + "iface", + "sleep", + "mock_utmi", + "bus"; assigned-clocks = <&gcc GCC_USB30_MOCK_UTMI_CLK>, <&gcc GCC_USB30_MASTER_CLK>, diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi index 692cf4be4eef..0692ae0e60a4 100644 --- a/arch/arm64/boot/dts/qcom/sdm845.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi @@ -3844,10 +3844,13 @@ clocks = <&gcc GCC_CFG_NOC_USB3_PRIM_AXI_CLK>, <&gcc GCC_USB30_PRIM_MASTER_CLK>, <&gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>, - <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>, - <&gcc GCC_USB30_PRIM_SLEEP_CLK>; - clock-names = "cfg_noc", "core", "iface", "mock_utmi", - "sleep"; + <&gcc GCC_USB30_PRIM_SLEEP_CLK>, + <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>; + clock-names = "cfg_noc", + "core", + "iface", + "sleep", + "mock_utmi"; assigned-clocks = <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>, <&gcc GCC_USB30_PRIM_MASTER_CLK>; @@ -3868,7 +3871,7 @@ <&gladiator_noc MASTER_APPSS_PROC 0 &config_noc SLAVE_USB3_0 0>; interconnect-names = "usb-ddr", "apps-usb"; - usb_1_dwc3: dwc3@a600000 { + usb_1_dwc3: usb@a600000 { compatible = "snps,dwc3"; reg = <0 0x0a600000 0 0xcd00>; interrupts = <GIC_SPI 133 IRQ_TYPE_LEVEL_HIGH>; @@ -3892,10 +3895,13 @@ clocks = <&gcc GCC_CFG_NOC_USB3_SEC_AXI_CLK>, <&gcc GCC_USB30_SEC_MASTER_CLK>, <&gcc GCC_AGGRE_USB3_SEC_AXI_CLK>, - <&gcc GCC_USB30_SEC_MOCK_UTMI_CLK>, - <&gcc GCC_USB30_SEC_SLEEP_CLK>; - clock-names = "cfg_noc", "core", "iface", "mock_utmi", - "sleep"; + <&gcc GCC_USB30_SEC_SLEEP_CLK>, + <&gcc GCC_USB30_SEC_MOCK_UTMI_CLK>; + clock-names = "cfg_noc", + "core", + "iface", + "sleep", + "mock_utmi"; assigned-clocks = <&gcc GCC_USB30_SEC_MOCK_UTMI_CLK>, <&gcc GCC_USB30_SEC_MASTER_CLK>; @@ -3916,7 +3922,7 @@ <&gladiator_noc MASTER_APPSS_PROC 0 &config_noc SLAVE_USB3_1 0>; interconnect-names = "usb-ddr", "apps-usb"; - usb_2_dwc3: dwc3@a800000 { + usb_2_dwc3: usb@a800000 { compatible = "snps,dwc3"; reg = <0 0x0a800000 0 0xcd00>; interrupts = <GIC_SPI 138 IRQ_TYPE_LEVEL_HIGH>; diff --git a/arch/arm64/boot/dts/qcom/sm6125.dtsi b/arch/arm64/boot/dts/qcom/sm6125.dtsi index e81b2a7794fb..135e6e0da27a 100644 --- a/arch/arm64/boot/dts/qcom/sm6125.dtsi +++ b/arch/arm64/boot/dts/qcom/sm6125.dtsi @@ -481,18 +481,24 @@ }; usb3: usb@4ef8800 { - compatible = "qcom,msm8996-dwc3", "qcom,dwc3"; + compatible = "qcom,sm6125-dwc3", "qcom,dwc3"; reg = <0x04ef8800 0x400>; #address-cells = <1>; #size-cells = <1>; ranges; - clocks = <&gcc GCC_USB30_PRIM_MASTER_CLK>, + clocks = <&gcc GCC_CFG_NOC_USB3_PRIM_AXI_CLK>, + <&gcc GCC_USB30_PRIM_MASTER_CLK>, <&gcc GCC_SYS_NOC_USB3_PRIM_AXI_CLK>, - <&gcc GCC_CFG_NOC_USB3_PRIM_AXI_CLK>, - <&gcc GCC_USB3_PRIM_CLKREF_CLK>, <&gcc GCC_USB30_PRIM_SLEEP_CLK>, - <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>; + <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>, + <&gcc GCC_USB3_PRIM_CLKREF_CLK>; + clock-names = "cfg_noc", + "core", + "iface", + "sleep", + "mock_utmi", + "xo"; assigned-clocks = <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>, <&gcc GCC_USB30_PRIM_MASTER_CLK>; diff --git a/arch/arm64/boot/dts/qcom/sm6350.dtsi b/arch/arm64/boot/dts/qcom/sm6350.dtsi index fb1a0f662575..d4f8f33f3f0c 100644 --- a/arch/arm64/boot/dts/qcom/sm6350.dtsi +++ b/arch/arm64/boot/dts/qcom/sm6350.dtsi @@ -1034,10 +1034,13 @@ clocks = <&gcc GCC_CFG_NOC_USB3_PRIM_AXI_CLK>, <&gcc GCC_USB30_PRIM_MASTER_CLK>, <&gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>, - <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>, - <&gcc GCC_USB30_PRIM_SLEEP_CLK>; - clock-names = "cfg_noc", "core", "iface", "mock_utmi", - "sleep"; + <&gcc GCC_USB30_PRIM_SLEEP_CLK>, + <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>; + clock-names = "cfg_noc", + "core", + "iface", + "sleep", + "mock_utmi"; interrupts-extended = <&intc GIC_SPI 130 IRQ_TYPE_LEVEL_HIGH>, <&pdc 17 IRQ_TYPE_LEVEL_HIGH>, diff --git a/arch/arm64/boot/dts/qcom/sm8150.dtsi b/arch/arm64/boot/dts/qcom/sm8150.dtsi index f70ae4c56762..8ea44c4b56b4 100644 --- a/arch/arm64/boot/dts/qcom/sm8150.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8150.dtsi @@ -3614,11 +3614,15 @@ clocks = <&gcc GCC_CFG_NOC_USB3_PRIM_AXI_CLK>, <&gcc GCC_USB30_PRIM_MASTER_CLK>, <&gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>, - <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>, <&gcc GCC_USB30_PRIM_SLEEP_CLK>, + <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>, <&gcc GCC_USB3_SEC_CLKREF_CLK>; - clock-names = "cfg_noc", "core", "iface", "mock_utmi", - "sleep", "xo"; + clock-names = "cfg_noc", + "core", + "iface", + "sleep", + "mock_utmi", + "xo"; assigned-clocks = <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>, <&gcc GCC_USB30_PRIM_MASTER_CLK>; @@ -3635,7 +3639,7 @@ resets = <&gcc GCC_USB30_PRIM_BCR>; - usb_1_dwc3: dwc3@a600000 { + usb_1_dwc3: usb@a600000 { compatible = "snps,dwc3"; reg = <0 0x0a600000 0 0xcd00>; interrupts = <GIC_SPI 133 IRQ_TYPE_LEVEL_HIGH>; @@ -3659,11 +3663,15 @@ clocks = <&gcc GCC_CFG_NOC_USB3_SEC_AXI_CLK>, <&gcc GCC_USB30_SEC_MASTER_CLK>, <&gcc GCC_AGGRE_USB3_SEC_AXI_CLK>, - <&gcc GCC_USB30_SEC_MOCK_UTMI_CLK>, <&gcc GCC_USB30_SEC_SLEEP_CLK>, + <&gcc GCC_USB30_SEC_MOCK_UTMI_CLK>, <&gcc GCC_USB3_SEC_CLKREF_CLK>; - clock-names = "cfg_noc", "core", "iface", "mock_utmi", - "sleep", "xo"; + clock-names = "cfg_noc", + "core", + "iface", + "sleep", + "mock_utmi", + "xo"; assigned-clocks = <&gcc GCC_USB30_SEC_MOCK_UTMI_CLK>, <&gcc GCC_USB30_SEC_MASTER_CLK>; diff --git a/arch/arm64/boot/dts/qcom/sm8250.dtsi b/arch/arm64/boot/dts/qcom/sm8250.dtsi index dc2562070336..cf0c97bd5ad3 100644 --- a/arch/arm64/boot/dts/qcom/sm8250.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8250.dtsi @@ -2995,11 +2995,15 @@ clocks = <&gcc GCC_CFG_NOC_USB3_PRIM_AXI_CLK>, <&gcc GCC_USB30_PRIM_MASTER_CLK>, <&gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>, - <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>, <&gcc GCC_USB30_PRIM_SLEEP_CLK>, + <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>, <&gcc GCC_USB3_SEC_CLKREF_EN>; - clock-names = "cfg_noc", "core", "iface", "mock_utmi", - "sleep", "xo"; + clock-names = "cfg_noc", + "core", + "iface", + "sleep", + "mock_utmi", + "xo"; assigned-clocks = <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>, <&gcc GCC_USB30_PRIM_MASTER_CLK>; @@ -3046,11 +3050,15 @@ clocks = <&gcc GCC_CFG_NOC_USB3_SEC_AXI_CLK>, <&gcc GCC_USB30_SEC_MASTER_CLK>, <&gcc GCC_AGGRE_USB3_SEC_AXI_CLK>, - <&gcc GCC_USB30_SEC_MOCK_UTMI_CLK>, <&gcc GCC_USB30_SEC_SLEEP_CLK>, + <&gcc GCC_USB30_SEC_MOCK_UTMI_CLK>, <&gcc GCC_USB3_SEC_CLKREF_EN>; - clock-names = "cfg_noc", "core", "iface", "mock_utmi", - "sleep", "xo"; + clock-names = "cfg_noc", + "core", + "iface", + "sleep", + "mock_utmi", + "xo"; assigned-clocks = <&gcc GCC_USB30_SEC_MOCK_UTMI_CLK>, <&gcc GCC_USB30_SEC_MASTER_CLK>; diff --git a/arch/arm64/boot/dts/qcom/sm8350.dtsi b/arch/arm64/boot/dts/qcom/sm8350.dtsi index c0137bdcf94b..743cba9b683c 100644 --- a/arch/arm64/boot/dts/qcom/sm8350.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8350.dtsi @@ -2449,10 +2449,13 @@ clocks = <&gcc GCC_CFG_NOC_USB3_PRIM_AXI_CLK>, <&gcc GCC_USB30_PRIM_MASTER_CLK>, <&gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>, - <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>, - <&gcc GCC_USB30_PRIM_SLEEP_CLK>; - clock-names = "cfg_noc", "core", "iface", "mock_utmi", - "sleep"; + <&gcc GCC_USB30_PRIM_SLEEP_CLK>, + <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>; + clock-names = "cfg_noc", + "core", + "iface", + "sleep", + "mock_utmi"; assigned-clocks = <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>, <&gcc GCC_USB30_PRIM_MASTER_CLK>; @@ -2492,11 +2495,15 @@ clocks = <&gcc GCC_CFG_NOC_USB3_SEC_AXI_CLK>, <&gcc GCC_USB30_SEC_MASTER_CLK>, <&gcc GCC_AGGRE_USB3_SEC_AXI_CLK>, - <&gcc GCC_USB30_SEC_MOCK_UTMI_CLK>, <&gcc GCC_USB30_SEC_SLEEP_CLK>, + <&gcc GCC_USB30_SEC_MOCK_UTMI_CLK>, <&gcc GCC_USB3_SEC_CLKREF_EN>; - clock-names = "cfg_noc", "core", "iface", "mock_utmi", - "sleep", "xo"; + clock-names = "cfg_noc", + "core", + "iface", + "sleep", + "mock_utmi", + "xo"; assigned-clocks = <&gcc GCC_USB30_SEC_MOCK_UTMI_CLK>, <&gcc GCC_USB30_SEC_MASTER_CLK>; diff --git a/arch/arm64/boot/dts/qcom/sm8450.dtsi b/arch/arm64/boot/dts/qcom/sm8450.dtsi index 7f52c3cfdfb7..7d08fad76371 100644 --- a/arch/arm64/boot/dts/qcom/sm8450.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8450.dtsi @@ -3107,22 +3107,28 @@ clocks = <&gcc GCC_CFG_NOC_USB3_PRIM_AXI_CLK>, <&gcc GCC_USB30_PRIM_MASTER_CLK>, <&gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>, - <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>, <&gcc GCC_USB30_PRIM_SLEEP_CLK>, + <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>, <&gcc GCC_USB3_0_CLKREF_EN>; - clock-names = "cfg_noc", "core", "iface", "mock_utmi", - "sleep", "xo"; + clock-names = "cfg_noc", + "core", + "iface", + "sleep", + "mock_utmi", + "xo"; assigned-clocks = <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>, <&gcc GCC_USB30_PRIM_MASTER_CLK>; assigned-clock-rates = <19200000>, <200000000>; interrupts-extended = <&intc GIC_SPI 130 IRQ_TYPE_LEVEL_HIGH>, - <&pdc 14 IRQ_TYPE_EDGE_BOTH>, + <&pdc 17 IRQ_TYPE_LEVEL_HIGH>, <&pdc 15 IRQ_TYPE_EDGE_BOTH>, - <&pdc 17 IRQ_TYPE_LEVEL_HIGH>; - interrupt-names = "hs_phy_irq", "dp_hs_phy_irq", - "dm_hs_phy_irq", "ss_phy_irq"; + <&pdc 14 IRQ_TYPE_EDGE_BOTH>; + interrupt-names = "hs_phy_irq", + "ss_phy_irq", + "dm_hs_phy_irq", + "dp_hs_phy_irq"; power-domains = <&gcc USB30_PRIM_GDSC>; diff --git a/arch/mips/boot/dts/ingenic/jz4780.dtsi b/arch/mips/boot/dts/ingenic/jz4780.dtsi index b998301f179c..c182a656d63b 100644 --- a/arch/mips/boot/dts/ingenic/jz4780.dtsi +++ b/arch/mips/boot/dts/ingenic/jz4780.dtsi @@ -577,7 +577,7 @@ }; otg: usb@13500000 { - compatible = "ingenic,jz4780-otg", "snps,dwc2"; + compatible = "ingenic,jz4780-otg"; reg = <0x13500000 0x40000>; interrupt-parent = <&intc>; diff --git a/arch/mips/boot/dts/ingenic/x1000.dtsi b/arch/mips/boot/dts/ingenic/x1000.dtsi index ecbfed49dc77..b0a034b468bb 100644 --- a/arch/mips/boot/dts/ingenic/x1000.dtsi +++ b/arch/mips/boot/dts/ingenic/x1000.dtsi @@ -398,7 +398,7 @@ }; otg: usb@13500000 { - compatible = "ingenic,x1000-otg", "snps,dwc2"; + compatible = "ingenic,x1000-otg"; reg = <0x13500000 0x40000>; interrupt-parent = <&intc>; diff --git a/arch/mips/boot/dts/ingenic/x1830.dtsi b/arch/mips/boot/dts/ingenic/x1830.dtsi index efd556902cfd..dbf21afaccb1 100644 --- a/arch/mips/boot/dts/ingenic/x1830.dtsi +++ b/arch/mips/boot/dts/ingenic/x1830.dtsi @@ -408,7 +408,7 @@ }; otg: usb@13500000 { - compatible = "ingenic,x1830-otg", "snps,dwc2"; + compatible = "ingenic,x1830-otg"; reg = <0x13500000 0x40000>; interrupt-parent = <&intc>; diff --git a/drivers/base/property.c b/drivers/base/property.c index 3adcac2c78fa..ed6f449f8e5c 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -1206,15 +1206,23 @@ const void *device_get_match_data(struct device *dev) } EXPORT_SYMBOL_GPL(device_get_match_data); -static void * -fwnode_graph_devcon_match(struct fwnode_handle *fwnode, const char *con_id, - void *data, devcon_match_fn_t match) +static unsigned int fwnode_graph_devcon_matches(struct fwnode_handle *fwnode, + const char *con_id, void *data, + devcon_match_fn_t match, + void **matches, + unsigned int matches_len) { struct fwnode_handle *node; struct fwnode_handle *ep; + unsigned int count = 0; void *ret; fwnode_graph_for_each_endpoint(fwnode, ep) { + if (matches && count >= matches_len) { + fwnode_handle_put(ep); + break; + } + node = fwnode_graph_get_remote_port_parent(ep); if (!fwnode_device_is_available(node)) { fwnode_handle_put(node); @@ -1224,33 +1232,43 @@ fwnode_graph_devcon_match(struct fwnode_handle *fwnode, const char *con_id, ret = match(node, con_id, data); fwnode_handle_put(node); if (ret) { - fwnode_handle_put(ep); - return ret; + if (matches) + matches[count] = ret; + count++; } } - return NULL; + return count; } -static void * -fwnode_devcon_match(struct fwnode_handle *fwnode, const char *con_id, - void *data, devcon_match_fn_t match) +static unsigned int fwnode_devcon_matches(struct fwnode_handle *fwnode, + const char *con_id, void *data, + devcon_match_fn_t match, + void **matches, + unsigned int matches_len) { struct fwnode_handle *node; + unsigned int count = 0; + unsigned int i; void *ret; - int i; for (i = 0; ; i++) { + if (matches && count >= matches_len) + break; + node = fwnode_find_reference(fwnode, con_id, i); if (IS_ERR(node)) break; ret = match(node, NULL, data); fwnode_handle_put(node); - if (ret) - return ret; + if (ret) { + if (matches) + matches[count] = ret; + count++; + } } - return NULL; + return count; } /** @@ -1268,15 +1286,61 @@ void *fwnode_connection_find_match(struct fwnode_handle *fwnode, const char *con_id, void *data, devcon_match_fn_t match) { + unsigned int count; void *ret; if (!fwnode || !match) return NULL; - ret = fwnode_graph_devcon_match(fwnode, con_id, data, match); - if (ret) + count = fwnode_graph_devcon_matches(fwnode, con_id, data, match, &ret, 1); + if (count) return ret; - return fwnode_devcon_match(fwnode, con_id, data, match); + count = fwnode_devcon_matches(fwnode, con_id, data, match, &ret, 1); + return count ? ret : NULL; } EXPORT_SYMBOL_GPL(fwnode_connection_find_match); + +/** + * fwnode_connection_find_matches - Find connections from a device node + * @fwnode: Device node with the connection + * @con_id: Identifier for the connection + * @data: Data for the match function + * @match: Function to check and convert the connection description + * @matches: (Optional) array of pointers to fill with matches + * @matches_len: Length of @matches + * + * Find up to @matches_len connections with unique identifier @con_id between + * @fwnode and other device nodes. @match will be used to convert the + * connection description to data the caller is expecting to be returned + * through the @matches array. + * If @matches is NULL @matches_len is ignored and the total number of resolved + * matches is returned. + * + * Return: Number of matches resolved, or negative errno. + */ +int fwnode_connection_find_matches(struct fwnode_handle *fwnode, + const char *con_id, void *data, + devcon_match_fn_t match, + void **matches, unsigned int matches_len) +{ + unsigned int count_graph; + unsigned int count_ref; + + if (!fwnode || !match) + return -EINVAL; + + count_graph = fwnode_graph_devcon_matches(fwnode, con_id, data, match, + matches, matches_len); + + if (matches) { + matches += count_graph; + matches_len -= count_graph; + } + + count_ref = fwnode_devcon_matches(fwnode, con_id, data, match, + matches, matches_len); + + return count_graph + count_ref; +} +EXPORT_SYMBOL_GPL(fwnode_connection_find_matches); diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 54752c85604b..4490e2f7252a 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -387,7 +387,7 @@ static int hid_submit_ctrl(struct hid_device *hid) usbhid->urbctrl->pipe = usb_rcvctrlpipe(hid_to_usb_dev(hid), 0); maxpacket = usb_maxpacket(hid_to_usb_dev(hid), - usbhid->urbctrl->pipe, 0); + usbhid->urbctrl->pipe); len += (len == 0); /* Don't allow 0-length reports */ len = round_up(len, maxpacket); if (len > usbhid->bufsize) diff --git a/drivers/hid/usbhid/usbkbd.c b/drivers/hid/usbhid/usbkbd.c index df02002066ce..b4b007c4beb6 100644 --- a/drivers/hid/usbhid/usbkbd.c +++ b/drivers/hid/usbhid/usbkbd.c @@ -279,7 +279,7 @@ static int usb_kbd_probe(struct usb_interface *iface, return -ENODEV; pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); - maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + maxp = usb_maxpacket(dev, pipe); kbd = kzalloc(sizeof(struct usb_kbd), GFP_KERNEL); input_dev = input_allocate_device(); diff --git a/drivers/hid/usbhid/usbmouse.c b/drivers/hid/usbhid/usbmouse.c index c89332017d5d..fb1d7d1f6999 100644 --- a/drivers/hid/usbhid/usbmouse.c +++ b/drivers/hid/usbhid/usbmouse.c @@ -123,7 +123,7 @@ static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_i return -ENODEV; pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); - maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + maxp = usb_maxpacket(dev, pipe); mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL); input_dev = input_allocate_device(); diff --git a/drivers/input/misc/ati_remote2.c b/drivers/input/misc/ati_remote2.c index 8a36d78fed63..946bf75aa106 100644 --- a/drivers/input/misc/ati_remote2.c +++ b/drivers/input/misc/ati_remote2.c @@ -639,7 +639,7 @@ static int ati_remote2_urb_init(struct ati_remote2 *ar2) return -ENOMEM; pipe = usb_rcvintpipe(udev, ar2->ep[i]->bEndpointAddress); - maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); + maxp = usb_maxpacket(udev, pipe); maxp = maxp > 4 ? 4 : maxp; usb_fill_int_urb(ar2->urb[i], udev, pipe, ar2->buf[i], maxp, diff --git a/drivers/input/misc/cm109.c b/drivers/input/misc/cm109.c index f515fae465c3..728325a2d574 100644 --- a/drivers/input/misc/cm109.c +++ b/drivers/input/misc/cm109.c @@ -745,7 +745,7 @@ static int cm109_usb_probe(struct usb_interface *intf, /* get a handle to the interrupt data pipe */ pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress); - ret = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); + ret = usb_maxpacket(udev, pipe); if (ret != USB_PKT_LEN) dev_err(&intf->dev, "invalid payload size %d, expected %d\n", ret, USB_PKT_LEN); diff --git a/drivers/input/misc/powermate.c b/drivers/input/misc/powermate.c index c4e0e1886061..c1c733a9cb89 100644 --- a/drivers/input/misc/powermate.c +++ b/drivers/input/misc/powermate.c @@ -374,7 +374,7 @@ static int powermate_probe(struct usb_interface *intf, const struct usb_device_i /* get a handle to the interrupt data pipe */ pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress); - maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); + maxp = usb_maxpacket(udev, pipe); if (maxp < POWERMATE_PAYLOAD_SIZE_MIN || maxp > POWERMATE_PAYLOAD_SIZE_MAX) { printk(KERN_WARNING "powermate: Expected payload of %d--%d bytes, found %d bytes!\n", diff --git a/drivers/input/misc/yealink.c b/drivers/input/misc/yealink.c index 8ab01c7601b1..69420781db30 100644 --- a/drivers/input/misc/yealink.c +++ b/drivers/input/misc/yealink.c @@ -905,7 +905,7 @@ static int usb_probe(struct usb_interface *intf, const struct usb_device_id *id) /* get a handle to the interrupt data pipe */ pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress); - ret = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); + ret = usb_maxpacket(udev, pipe); if (ret != USB_PKT_LEN) dev_err(&intf->dev, "invalid payload size %d, expected %zd\n", ret, USB_PKT_LEN); diff --git a/drivers/input/tablet/acecad.c b/drivers/input/tablet/acecad.c index a38d1fe97334..56c7e471ac32 100644 --- a/drivers/input/tablet/acecad.c +++ b/drivers/input/tablet/acecad.c @@ -130,7 +130,7 @@ static int usb_acecad_probe(struct usb_interface *intf, const struct usb_device_ return -ENODEV; pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); - maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + maxp = usb_maxpacket(dev, pipe); acecad = kzalloc(sizeof(struct usb_acecad), GFP_KERNEL); input_dev = input_allocate_device(); diff --git a/drivers/input/tablet/pegasus_notetaker.c b/drivers/input/tablet/pegasus_notetaker.c index 749edbdb7ffa..c608ac505d1b 100644 --- a/drivers/input/tablet/pegasus_notetaker.c +++ b/drivers/input/tablet/pegasus_notetaker.c @@ -296,7 +296,7 @@ static int pegasus_probe(struct usb_interface *intf, pegasus->intf = intf; pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); - pegasus->data_len = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + pegasus->data_len = usb_maxpacket(dev, pipe); pegasus->data = usb_alloc_coherent(dev, pegasus->data_len, GFP_KERNEL, &pegasus->data_dma); diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c index c12dda73cdd5..3155e876616d 100644 --- a/drivers/media/rc/ati_remote.c +++ b/drivers/media/rc/ati_remote.c @@ -773,7 +773,7 @@ static int ati_remote_initialize(struct ati_remote *ati_remote) /* Set up irq_urb */ pipe = usb_rcvintpipe(udev, ati_remote->endpoint_in->bEndpointAddress); - maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); + maxp = usb_maxpacket(udev, pipe); maxp = (maxp > DATA_BUFSIZE) ? DATA_BUFSIZE : maxp; usb_fill_int_urb(ati_remote->irq_urb, udev, pipe, ati_remote->inbuf, @@ -784,7 +784,7 @@ static int ati_remote_initialize(struct ati_remote *ati_remote) /* Set up out_urb */ pipe = usb_sndintpipe(udev, ati_remote->endpoint_out->bEndpointAddress); - maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); + maxp = usb_maxpacket(udev, pipe); maxp = (maxp > DATA_BUFSIZE) ? DATA_BUFSIZE : maxp; usb_fill_int_urb(ati_remote->out_urb, udev, pipe, ati_remote->outbuf, diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index 2dc810f5a73f..0834d5f866fd 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -1728,7 +1728,7 @@ static int mceusb_dev_probe(struct usb_interface *intf, pipe = usb_rcvintpipe(dev, ep_in->bEndpointAddress); else pipe = usb_rcvbulkpipe(dev, ep_in->bEndpointAddress); - maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + maxp = usb_maxpacket(dev, pipe); ir = kzalloc(sizeof(struct mceusb_dev), GFP_KERNEL); if (!ir) diff --git a/drivers/media/rc/streamzap.c b/drivers/media/rc/streamzap.c index 16ba85d7c090..deb85330c940 100644 --- a/drivers/media/rc/streamzap.c +++ b/drivers/media/rc/streamzap.c @@ -307,7 +307,7 @@ static int streamzap_probe(struct usb_interface *intf, } pipe = usb_rcvintpipe(usbdev, endpoint->bEndpointAddress); - maxp = usb_maxpacket(usbdev, pipe, usb_pipeout(pipe)); + maxp = usb_maxpacket(usbdev, pipe); if (maxp == 0) { dev_err(&intf->dev, "%s: endpoint Max Packet Size is 0!?!\n", diff --git a/drivers/media/rc/xbox_remote.c b/drivers/media/rc/xbox_remote.c index 98d0b43608ad..7424b2031152 100644 --- a/drivers/media/rc/xbox_remote.c +++ b/drivers/media/rc/xbox_remote.c @@ -171,7 +171,7 @@ static int xbox_remote_initialize(struct xbox_remote *xbox_remote, /* Set up irq_urb */ pipe = usb_rcvintpipe(udev, endpoint_in->bEndpointAddress); - maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); + maxp = usb_maxpacket(udev, pipe); maxp = (maxp > DATA_BUFSIZE) ? DATA_BUFSIZE : maxp; usb_fill_int_urb(xbox_remote->irq_urb, udev, pipe, xbox_remote->inbuf, diff --git a/drivers/media/usb/tm6000/tm6000-dvb.c b/drivers/media/usb/tm6000/tm6000-dvb.c index 8c2725e4105b..ee04973cbf93 100644 --- a/drivers/media/usb/tm6000/tm6000-dvb.c +++ b/drivers/media/usb/tm6000/tm6000-dvb.c @@ -120,7 +120,7 @@ static int tm6000_start_stream(struct tm6000_core *dev) pipe = usb_rcvbulkpipe(dev->udev, dev->bulk_in.endp->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); - size = usb_maxpacket(dev->udev, pipe, usb_pipeout(pipe)); + size = usb_maxpacket(dev->udev, pipe); size = size * 15; /* 512 x 8 or 12 or 15 */ dvb->bulk_urb->transfer_buffer = kzalloc(size, GFP_KERNEL); diff --git a/drivers/media/usb/tm6000/tm6000-input.c b/drivers/media/usb/tm6000/tm6000-input.c index 84602edf3fe8..5136e9e202f1 100644 --- a/drivers/media/usb/tm6000/tm6000-input.c +++ b/drivers/media/usb/tm6000/tm6000-input.c @@ -340,7 +340,7 @@ static int __tm6000_ir_int_start(struct rc_dev *rc) dev->int_in.endp->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); - size = usb_maxpacket(dev->udev, pipe, usb_pipeout(pipe)); + size = usb_maxpacket(dev->udev, pipe); dprintk(1, "IR max size: %d\n", size); ir->int_urb->transfer_buffer = kzalloc(size, GFP_ATOMIC); diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index e293f6f3d1bc..d855a19551f3 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -570,7 +570,7 @@ static int tm6000_prepare_isoc(struct tm6000_core *dev) dev->isoc_in.endp->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); - size = usb_maxpacket(dev->udev, pipe, usb_pipeout(pipe)); + size = usb_maxpacket(dev->udev, pipe); if (size > dev->isoc_in.maxsize) size = dev->isoc_in.maxsize; diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c index ec2a4fce8581..e889a8bd7ac8 100644 --- a/drivers/misc/mei/hdcp/mei_hdcp.c +++ b/drivers/misc/mei/hdcp/mei_hdcp.c @@ -784,7 +784,7 @@ static int mei_hdcp_component_match(struct device *dev, int subcomponent, { struct device *base = data; - if (strcmp(dev->driver->name, "i915") || + if (!dev->driver || strcmp(dev->driver->name, "i915") || subcomponent != I915_COMPONENT_HDCP) return 0; diff --git a/drivers/misc/mei/pxp/mei_pxp.c b/drivers/misc/mei/pxp/mei_pxp.c index f7380d387bab..5c39457e3f53 100644 --- a/drivers/misc/mei/pxp/mei_pxp.c +++ b/drivers/misc/mei/pxp/mei_pxp.c @@ -131,7 +131,7 @@ static int mei_pxp_component_match(struct device *dev, int subcomponent, { struct device *base = data; - if (strcmp(dev->driver->name, "i915") || + if (!dev->driver || strcmp(dev->driver->name, "i915") || subcomponent != I915_COMPONENT_PXP) return 0; diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index cdca00c0dc1f..d55f59ce4a31 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -441,7 +441,7 @@ static void cdc_ncm_update_rxtx_max(struct usbnet *dev, u32 new_rx, u32 new_tx) * .bind which is called before usbnet sets up dev->maxpacket */ if (val != le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize) && - val % usb_maxpacket(dev->udev, dev->out, 1) == 0) + val % usb_maxpacket(dev->udev, dev->out) == 0) val++; /* we might need to flush any pending tx buffers if running */ @@ -465,7 +465,7 @@ static void cdc_ncm_update_rxtx_max(struct usbnet *dev, u32 new_rx, u32 new_tx) usbnet_update_max_qlen(dev); /* never pad more than 3 full USB packets per transfer */ - ctx->min_tx_pkt = clamp_t(u16, ctx->tx_max - 3 * usb_maxpacket(dev->udev, dev->out, 1), + ctx->min_tx_pkt = clamp_t(u16, ctx->tx_max - 3 * usb_maxpacket(dev->udev, dev->out), CDC_NCM_MIN_TX_PKT, ctx->tx_max); } diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index 636a405844c5..3226ab33afae 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -4421,7 +4421,7 @@ static int lan78xx_probe(struct usb_interface *intf, goto out4; period = ep_intr->desc.bInterval; - maxp = usb_maxpacket(dev->udev, dev->pipe_intr, 0); + maxp = usb_maxpacket(dev->udev, dev->pipe_intr); buf = kmalloc(maxp, GFP_KERNEL); if (!buf) { ret = -ENOMEM; @@ -4439,7 +4439,7 @@ static int lan78xx_probe(struct usb_interface *intf, dev->urb_intr->transfer_flags |= URB_FREE_BUFFER; } - dev->maxpacket = usb_maxpacket(dev->udev, dev->pipe_out, 1); + dev->maxpacket = usb_maxpacket(dev->udev, dev->pipe_out); /* Reject broken descriptors. */ if (dev->maxpacket == 0) { diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c index 4e70dec30e5a..f79333fe1783 100644 --- a/drivers/net/usb/rndis_host.c +++ b/drivers/net/usb/rndis_host.c @@ -333,7 +333,7 @@ generic_rndis_bind(struct usbnet *dev, struct usb_interface *intf, int flags) net->hard_header_len += sizeof (struct rndis_data_hdr); dev->hard_mtu = net->mtu + net->hard_header_len; - dev->maxpacket = usb_maxpacket(dev->udev, dev->out, 1); + dev->maxpacket = usb_maxpacket(dev->udev, dev->out); if (dev->maxpacket == 0) { netif_dbg(dev, probe, dev->net, "dev->maxpacket can't be 0\n"); diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 36b24ec11650..1cb6dab3e2d0 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -229,7 +229,7 @@ static int init_status (struct usbnet *dev, struct usb_interface *intf) pipe = usb_rcvintpipe (dev->udev, dev->status->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); - maxp = usb_maxpacket (dev->udev, pipe, 0); + maxp = usb_maxpacket(dev->udev, pipe); /* avoid 1 msec chatter: min 8 msec poll rate */ period = max ((int) dev->status->desc.bInterval, @@ -1789,7 +1789,7 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) if (!dev->rx_urb_size) dev->rx_urb_size = dev->hard_mtu; - dev->maxpacket = usb_maxpacket (dev->udev, dev->out, 1); + dev->maxpacket = usb_maxpacket(dev->udev, dev->out); if (dev->maxpacket == 0) { /* that is a broken device */ status = -ENODEV; diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c index a85e192c9d59..1bb92ca7451b 100644 --- a/drivers/net/wireless/mediatek/mt76/usb.c +++ b/drivers/net/wireless/mediatek/mt76/usb.c @@ -1068,7 +1068,7 @@ int __mt76u_init(struct mt76_dev *dev, struct usb_interface *intf, INIT_WORK(&usb->stat_work, mt76u_tx_status_data); - usb->data_len = usb_maxpacket(udev, usb_sndctrlpipe(udev, 0), 1); + usb->data_len = usb_maxpacket(udev, usb_sndctrlpipe(udev, 0)); if (usb->data_len < 32) usb->data_len = 32; diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c b/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c index 74c3d8cb3100..0827bc860bf8 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c @@ -586,10 +586,10 @@ static void rt2x00usb_assign_endpoint(struct data_queue *queue, if (queue->qid == QID_RX) { pipe = usb_rcvbulkpipe(usb_dev, queue->usb_endpoint); - queue->usb_maxpacket = usb_maxpacket(usb_dev, pipe, 0); + queue->usb_maxpacket = usb_maxpacket(usb_dev, pipe); } else { pipe = usb_sndbulkpipe(usb_dev, queue->usb_endpoint); - queue->usb_maxpacket = usb_maxpacket(usb_dev, pipe, 1); + queue->usb_maxpacket = usb_maxpacket(usb_dev, pipe); } if (!queue->usb_maxpacket) diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c index 4986edfbdf67..e92c658dba1c 100644 --- a/drivers/thunderbolt/ctl.c +++ b/drivers/thunderbolt/ctl.c @@ -158,21 +158,20 @@ static bool tb_cfg_request_is_active(struct tb_cfg_request *req) static struct tb_cfg_request * tb_cfg_request_find(struct tb_ctl *ctl, struct ctl_pkg *pkg) { - struct tb_cfg_request *req; - bool found = false; + struct tb_cfg_request *req = NULL, *iter; mutex_lock(&pkg->ctl->request_queue_lock); - list_for_each_entry(req, &pkg->ctl->request_queue, list) { - tb_cfg_request_get(req); - if (req->match(req, pkg)) { - found = true; + list_for_each_entry(iter, &pkg->ctl->request_queue, list) { + tb_cfg_request_get(iter); + if (iter->match(iter, pkg)) { + req = iter; break; } - tb_cfg_request_put(req); + tb_cfg_request_put(iter); } mutex_unlock(&pkg->ctl->request_queue_lock); - return found ? req : NULL; + return req; } /* utility functions */ diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index 4bc87b0f003a..1333b158a95e 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -1250,7 +1250,7 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id) nhi->pdev = pdev; nhi->ops = (const struct tb_nhi_ops *)id->driver_data; - /* cannot fail - table is allocated bin pcim_iomap_regions */ + /* cannot fail - table is allocated in pcim_iomap_regions */ nhi->iobase = pcim_iomap_table(pdev)[0]; nhi->hop_count = ioread32(nhi->iobase + REG_HOP_COUNT) & 0x3ff; dev_dbg(&pdev->dev, "total paths: %d\n", nhi->hop_count); diff --git a/drivers/thunderbolt/path.c b/drivers/thunderbolt/path.c index 299712accfe9..ee03fd75a472 100644 --- a/drivers/thunderbolt/path.c +++ b/drivers/thunderbolt/path.c @@ -166,6 +166,9 @@ struct tb_path *tb_path_discover(struct tb_port *src, int src_hopid, return NULL; } + tb_dbg(path->tb, "discovering %s path starting from %llx:%u\n", + path->name, tb_route(src->sw), src->port); + p = src; h = src_hopid; @@ -198,10 +201,13 @@ struct tb_path *tb_path_discover(struct tb_port *src, int src_hopid, path->hops[i].out_port = out_port; path->hops[i].next_hop_index = next_hop; + tb_dump_hop(&path->hops[i], &hop); + h = next_hop; p = out_port->remote; } + tb_dbg(path->tb, "path discovery complete\n"); return path; err: diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index ac87e8b50e52..561e1d77240e 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -693,8 +693,14 @@ static int __tb_port_enable(struct tb_port *port, bool enable) else phy |= LANE_ADP_CS_1_LD; - return tb_port_write(port, &phy, TB_CFG_PORT, - port->cap_phy + LANE_ADP_CS_1, 1); + + ret = tb_port_write(port, &phy, TB_CFG_PORT, + port->cap_phy + LANE_ADP_CS_1, 1); + if (ret) + return ret; + + tb_port_dbg(port, "lane %sabled\n", enable ? "en" : "dis"); + return 0; } /** @@ -993,7 +999,17 @@ static bool tb_port_is_width_supported(struct tb_port *port, int width) return !!(widths & width); } -static int tb_port_set_link_width(struct tb_port *port, unsigned int width) +/** + * tb_port_set_link_width() - Set target link width of the lane adapter + * @port: Lane adapter + * @width: Target link width (%1 or %2) + * + * Sets the target link width of the lane adapter to @width. Does not + * enable/disable lane bonding. For that call tb_port_set_lane_bonding(). + * + * Return: %0 in case of success and negative errno in case of error + */ +int tb_port_set_link_width(struct tb_port *port, unsigned int width) { u32 val; int ret; @@ -1020,13 +1036,59 @@ static int tb_port_set_link_width(struct tb_port *port, unsigned int width) return -EINVAL; } - val |= LANE_ADP_CS_1_LB; - return tb_port_write(port, &val, TB_CFG_PORT, port->cap_phy + LANE_ADP_CS_1, 1); } /** + * tb_port_set_lane_bonding() - Enable/disable lane bonding + * @port: Lane adapter + * @bonding: enable/disable bonding + * + * Enables or disables lane bonding. This should be called after target + * link width has been set (tb_port_set_link_width()). Note in most + * cases one should use tb_port_lane_bonding_enable() instead to enable + * lane bonding. + * + * As a side effect sets @port->bonding accordingly (and does the same + * for lane 1 too). + * + * Return: %0 in case of success and negative errno in case of error + */ +int tb_port_set_lane_bonding(struct tb_port *port, bool bonding) +{ + u32 val; + int ret; + + if (!port->cap_phy) + return -EINVAL; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_phy + LANE_ADP_CS_1, 1); + if (ret) + return ret; + + if (bonding) + val |= LANE_ADP_CS_1_LB; + else + val &= ~LANE_ADP_CS_1_LB; + + ret = tb_port_write(port, &val, TB_CFG_PORT, + port->cap_phy + LANE_ADP_CS_1, 1); + if (ret) + return ret; + + /* + * When lane 0 bonding is set it will affect lane 1 too so + * update both. + */ + port->bonded = bonding; + port->dual_link_port->bonded = bonding; + + return 0; +} + +/** * tb_port_lane_bonding_enable() - Enable bonding on port * @port: port to enable * @@ -1050,22 +1112,27 @@ int tb_port_lane_bonding_enable(struct tb_port *port) if (ret == 1) { ret = tb_port_set_link_width(port, 2); if (ret) - return ret; + goto err_lane0; } ret = tb_port_get_link_width(port->dual_link_port); if (ret == 1) { ret = tb_port_set_link_width(port->dual_link_port, 2); - if (ret) { - tb_port_set_link_width(port, 1); - return ret; - } + if (ret) + goto err_lane0; } - port->bonded = true; - port->dual_link_port->bonded = true; + ret = tb_port_set_lane_bonding(port, true); + if (ret) + goto err_lane1; return 0; + +err_lane1: + tb_port_set_link_width(port->dual_link_port, 1); +err_lane0: + tb_port_set_link_width(port, 1); + return ret; } /** @@ -1074,13 +1141,10 @@ int tb_port_lane_bonding_enable(struct tb_port *port) * * Disable bonding by setting the link width of the port and the * other port in case of dual link port. - * */ void tb_port_lane_bonding_disable(struct tb_port *port) { - port->dual_link_port->bonded = false; - port->bonded = false; - + tb_port_set_lane_bonding(port, false); tb_port_set_link_width(port->dual_link_port, 1); tb_port_set_link_width(port, 1); } @@ -1104,10 +1168,17 @@ int tb_port_wait_for_link_width(struct tb_port *port, int width, do { ret = tb_port_get_link_width(port); - if (ret < 0) - return ret; - else if (ret == width) + if (ret < 0) { + /* + * Sometimes we get port locked error when + * polling the lanes so we can ignore it and + * retry. + */ + if (ret != -EACCES) + return ret; + } else if (ret == width) { return 0; + } usleep_range(1000, 2000); } while (ktime_before(ktime_get(), timeout)); diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 9beb47b31c75..9a3214fb5038 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -169,12 +169,6 @@ static void tb_discover_tunnels(struct tb *tb) static int tb_port_configure_xdomain(struct tb_port *port) { - /* - * XDomain paths currently only support single lane so we must - * disable the other lane according to USB4 spec. - */ - tb_port_disable(port->dual_link_port); - if (tb_switch_is_usb4(port->sw)) return usb4_port_configure_xdomain(port); return tb_lc_configure_xdomain(port); @@ -867,7 +861,7 @@ static struct tb_port *tb_find_dp_out(struct tb *tb, struct tb_port *in) static void tb_tunnel_dp(struct tb *tb) { - int available_up, available_down, ret; + int available_up, available_down, ret, link_nr; struct tb_cm *tcm = tb_priv(tb); struct tb_port *port, *in, *out; struct tb_tunnel *tunnel; @@ -913,6 +907,20 @@ static void tb_tunnel_dp(struct tb *tb) } /* + * This is only applicable to links that are not bonded (so + * when Thunderbolt 1 hardware is involved somewhere in the + * topology). For these try to share the DP bandwidth between + * the two lanes. + */ + link_nr = 1; + list_for_each_entry(tunnel, &tcm->tunnel_list, list) { + if (tb_tunnel_is_dp(tunnel)) { + link_nr = 0; + break; + } + } + + /* * DP stream needs the domain to be active so runtime resume * both ends of the tunnel. * @@ -943,7 +951,8 @@ static void tb_tunnel_dp(struct tb *tb) tb_dbg(tb, "available bandwidth for new DP tunnel %u/%u Mb/s\n", available_up, available_down); - tunnel = tb_tunnel_alloc_dp(tb, in, out, available_up, available_down); + tunnel = tb_tunnel_alloc_dp(tb, in, out, link_nr, available_up, + available_down); if (!tunnel) { tb_port_dbg(out, "could not allocate DP tunnel\n"); goto err_reclaim; diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index b6fcd8d45324..4602c69913fa 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -674,7 +674,7 @@ static inline int tb_port_write(struct tb_port *port, const void *buffer, #define __TB_PORT_PRINT(level, _port, fmt, arg...) \ do { \ const struct tb_port *__port = (_port); \ - level(__port->sw->tb, "%llx:%x: " fmt, \ + level(__port->sw->tb, "%llx:%u: " fmt, \ tb_route(__port->sw), __port->port, ## arg); \ } while (0) #define tb_port_WARN(port, fmt, arg...) \ @@ -991,6 +991,7 @@ int tb_switch_pcie_l1_enable(struct tb_switch *sw); int tb_switch_xhci_connect(struct tb_switch *sw); void tb_switch_xhci_disconnect(struct tb_switch *sw); +int tb_port_state(struct tb_port *port); int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged); int tb_port_add_nfc_credits(struct tb_port *port, int credits); int tb_port_clear_counter(struct tb_port *port, int counter); @@ -1023,7 +1024,8 @@ static inline bool tb_port_use_credit_allocation(const struct tb_port *port) int tb_port_get_link_speed(struct tb_port *port); int tb_port_get_link_width(struct tb_port *port); -int tb_port_state(struct tb_port *port); +int tb_port_set_link_width(struct tb_port *port, unsigned int width); +int tb_port_set_lane_bonding(struct tb_port *port, bool bonding); int tb_port_lane_bonding_enable(struct tb_port *port); void tb_port_lane_bonding_disable(struct tb_port *port); int tb_port_wait_for_link_width(struct tb_port *port, int width, diff --git a/drivers/thunderbolt/tb_msgs.h b/drivers/thunderbolt/tb_msgs.h index fe1afa44c56d..33c4c7aed56d 100644 --- a/drivers/thunderbolt/tb_msgs.h +++ b/drivers/thunderbolt/tb_msgs.h @@ -527,6 +527,10 @@ enum tb_xdp_type { PROPERTIES_CHANGED_RESPONSE, ERROR_RESPONSE, UUID_REQUEST = 12, + LINK_STATE_STATUS_REQUEST = 15, + LINK_STATE_STATUS_RESPONSE, + LINK_STATE_CHANGE_REQUEST, + LINK_STATE_CHANGE_RESPONSE, }; struct tb_xdp_header { @@ -540,6 +544,41 @@ struct tb_xdp_error_response { u32 error; }; +struct tb_xdp_link_state_status { + struct tb_xdp_header hdr; +}; + +struct tb_xdp_link_state_status_response { + union { + struct tb_xdp_error_response err; + struct { + struct tb_xdp_header hdr; + u32 status; + u8 slw; + u8 tlw; + u8 sls; + u8 tls; + }; + }; +}; + +struct tb_xdp_link_state_change { + struct tb_xdp_header hdr; + u8 tlw; + u8 tls; + u16 reserved; +}; + +struct tb_xdp_link_state_change_response { + union { + struct tb_xdp_error_response err; + struct { + struct tb_xdp_header hdr; + u32 status; + }; + }; +}; + struct tb_xdp_uuid { struct tb_xdp_header hdr; }; diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h index b301eeb0c89b..6a16f61a72a1 100644 --- a/drivers/thunderbolt/tb_regs.h +++ b/drivers/thunderbolt/tb_regs.h @@ -311,11 +311,16 @@ struct tb_regs_port_header { /* Lane adapter registers */ #define LANE_ADP_CS_0 0x00 +#define LANE_ADP_CS_0_SUPPORTED_SPEED_MASK GENMASK(19, 16) +#define LANE_ADP_CS_0_SUPPORTED_SPEED_SHIFT 16 #define LANE_ADP_CS_0_SUPPORTED_WIDTH_MASK GENMASK(25, 20) #define LANE_ADP_CS_0_SUPPORTED_WIDTH_SHIFT 20 +#define LANE_ADP_CS_0_SUPPORTED_WIDTH_DUAL 0x2 #define LANE_ADP_CS_0_CL0S_SUPPORT BIT(26) #define LANE_ADP_CS_0_CL1_SUPPORT BIT(27) #define LANE_ADP_CS_1 0x01 +#define LANE_ADP_CS_1_TARGET_SPEED_MASK GENMASK(3, 0) +#define LANE_ADP_CS_1_TARGET_SPEED_GEN3 0xc #define LANE_ADP_CS_1_TARGET_WIDTH_MASK GENMASK(9, 4) #define LANE_ADP_CS_1_TARGET_WIDTH_SHIFT 4 #define LANE_ADP_CS_1_TARGET_WIDTH_SINGLE 0x1 diff --git a/drivers/thunderbolt/test.c b/drivers/thunderbolt/test.c index be9b1d7e63d2..ee37f8b58f50 100644 --- a/drivers/thunderbolt/test.c +++ b/drivers/thunderbolt/test.c @@ -341,6 +341,47 @@ static struct tb_switch *alloc_dev_with_dpin(struct kunit *test, return sw; } +static struct tb_switch *alloc_dev_without_dp(struct kunit *test, + struct tb_switch *parent, + u64 route, bool bonded) +{ + struct tb_switch *sw; + int i; + + sw = alloc_dev_default(test, parent, route, bonded); + if (!sw) + return NULL; + /* + * Device with: + * 2x USB4 Adapters (adapters 1,2 and 3,4), + * 1x PCIe Upstream (adapter 9), + * 1x PCIe Downstream (adapter 10), + * 1x USB3 Upstream (adapter 16), + * 1x USB3 Downstream (adapter 17) + */ + for (i = 5; i <= 8; i++) + sw->ports[i].disabled = true; + + for (i = 11; i <= 14; i++) + sw->ports[i].disabled = true; + + sw->ports[13].cap_adap = 0; + sw->ports[14].cap_adap = 0; + + for (i = 18; i <= 19; i++) + sw->ports[i].disabled = true; + + sw->generation = 4; + sw->credit_allocation = true; + sw->max_usb3_credits = 109; + sw->min_dp_aux_credits = 0; + sw->min_dp_main_credits = 0; + sw->max_pcie_credits = 30; + sw->max_dma_credits = 1; + + return sw; +} + static struct tb_switch *alloc_dev_usb4(struct kunit *test, struct tb_switch *parent, u64 route, bool bonded) @@ -1348,7 +1389,7 @@ static void tb_test_tunnel_dp(struct kunit *test) in = &host->ports[5]; out = &dev->ports[13]; - tunnel = tb_tunnel_alloc_dp(NULL, in, out, 0, 0); + tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0); KUNIT_ASSERT_NOT_NULL(test, tunnel); KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP); KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in); @@ -1394,7 +1435,7 @@ static void tb_test_tunnel_dp_chain(struct kunit *test) in = &host->ports[5]; out = &dev4->ports[14]; - tunnel = tb_tunnel_alloc_dp(NULL, in, out, 0, 0); + tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0); KUNIT_ASSERT_NOT_NULL(test, tunnel); KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP); KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in); @@ -1444,7 +1485,7 @@ static void tb_test_tunnel_dp_tree(struct kunit *test) in = &dev2->ports[13]; out = &dev5->ports[13]; - tunnel = tb_tunnel_alloc_dp(NULL, in, out, 0, 0); + tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0); KUNIT_ASSERT_NOT_NULL(test, tunnel); KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP); KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in); @@ -1509,7 +1550,7 @@ static void tb_test_tunnel_dp_max_length(struct kunit *test) in = &dev6->ports[13]; out = &dev12->ports[13]; - tunnel = tb_tunnel_alloc_dp(NULL, in, out, 0, 0); + tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0); KUNIT_ASSERT_NOT_NULL(test, tunnel); KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP); KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in); @@ -1627,7 +1668,7 @@ static void tb_test_tunnel_port_on_path(struct kunit *test) in = &dev2->ports[13]; out = &dev5->ports[13]; - dp_tunnel = tb_tunnel_alloc_dp(NULL, in, out, 0, 0); + dp_tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0); KUNIT_ASSERT_NOT_NULL(test, dp_tunnel); KUNIT_EXPECT_TRUE(test, tb_tunnel_port_on_path(dp_tunnel, in)); @@ -1996,6 +2037,56 @@ static void tb_test_credit_alloc_pcie(struct kunit *test) tb_tunnel_free(tunnel); } +static void tb_test_credit_alloc_without_dp(struct kunit *test) +{ + struct tb_switch *host, *dev; + struct tb_port *up, *down; + struct tb_tunnel *tunnel; + struct tb_path *path; + + host = alloc_host_usb4(test); + dev = alloc_dev_without_dp(test, host, 0x1, true); + + /* + * The device has no DP therefore baMinDPmain = baMinDPaux = 0 + * + * Create PCIe path with buffers less than baMaxPCIe. + * + * For a device with buffers configurations: + * baMaxUSB3 = 109 + * baMinDPaux = 0 + * baMinDPmain = 0 + * baMaxPCIe = 30 + * baMaxHI = 1 + * Remaining Buffers = Total - (CP + DP) = 120 - (2 + 0) = 118 + * PCIe Credits = Max(6, Min(baMaxPCIe, Remaining Buffers - baMaxUSB3) + * = Max(6, Min(30, 9) = 9 + */ + down = &host->ports[8]; + up = &dev->ports[9]; + tunnel = tb_tunnel_alloc_pci(NULL, up, down); + KUNIT_ASSERT_TRUE(test, tunnel != NULL); + KUNIT_ASSERT_EQ(test, tunnel->npaths, (size_t)2); + + /* PCIe downstream path */ + path = tunnel->paths[0]; + KUNIT_ASSERT_EQ(test, path->path_length, 2); + KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U); + KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U); + KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U); + KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 9U); + + /* PCIe upstream path */ + path = tunnel->paths[1]; + KUNIT_ASSERT_EQ(test, path->path_length, 2); + KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U); + KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U); + KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U); + KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 64U); + + tb_tunnel_free(tunnel); +} + static void tb_test_credit_alloc_dp(struct kunit *test) { struct tb_switch *host, *dev; @@ -2009,7 +2100,7 @@ static void tb_test_credit_alloc_dp(struct kunit *test) in = &host->ports[5]; out = &dev->ports[14]; - tunnel = tb_tunnel_alloc_dp(NULL, in, out, 0, 0); + tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0); KUNIT_ASSERT_NOT_NULL(test, tunnel); KUNIT_ASSERT_EQ(test, tunnel->npaths, (size_t)3); @@ -2245,7 +2336,7 @@ static struct tb_tunnel *TB_TEST_DP_TUNNEL1(struct kunit *test, in = &host->ports[5]; out = &dev->ports[13]; - dp_tunnel1 = tb_tunnel_alloc_dp(NULL, in, out, 0, 0); + dp_tunnel1 = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0); KUNIT_ASSERT_NOT_NULL(test, dp_tunnel1); KUNIT_ASSERT_EQ(test, dp_tunnel1->npaths, (size_t)3); @@ -2282,7 +2373,7 @@ static struct tb_tunnel *TB_TEST_DP_TUNNEL2(struct kunit *test, in = &host->ports[6]; out = &dev->ports[14]; - dp_tunnel2 = tb_tunnel_alloc_dp(NULL, in, out, 0, 0); + dp_tunnel2 = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0); KUNIT_ASSERT_NOT_NULL(test, dp_tunnel2); KUNIT_ASSERT_EQ(test, dp_tunnel2->npaths, (size_t)3); @@ -2709,6 +2800,7 @@ static struct kunit_case tb_test_cases[] = { KUNIT_CASE(tb_test_credit_alloc_legacy_not_bonded), KUNIT_CASE(tb_test_credit_alloc_legacy_bonded), KUNIT_CASE(tb_test_credit_alloc_pcie), + KUNIT_CASE(tb_test_credit_alloc_without_dp), KUNIT_CASE(tb_test_credit_alloc_dp), KUNIT_CASE(tb_test_credit_alloc_usb3), KUNIT_CASE(tb_test_credit_alloc_dma), diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c index 118742ec93ed..2c3cf7fc3357 100644 --- a/drivers/thunderbolt/tunnel.c +++ b/drivers/thunderbolt/tunnel.c @@ -102,8 +102,11 @@ static unsigned int tb_available_credits(const struct tb_port *port, * Maximum number of DP streams possible through the * lane adapter. */ - ndp = (credits - (usb3 + pcie + spare)) / - (sw->min_dp_aux_credits + sw->min_dp_main_credits); + if (sw->min_dp_aux_credits + sw->min_dp_main_credits) + ndp = (credits - (usb3 + pcie + spare)) / + (sw->min_dp_aux_credits + sw->min_dp_main_credits); + else + ndp = 0; } else { ndp = 0; } @@ -858,6 +861,7 @@ err_free: * @tb: Pointer to the domain structure * @in: DP in adapter port * @out: DP out adapter port + * @link_nr: Preferred lane adapter when the link is not bonded * @max_up: Maximum available upstream bandwidth for the DP tunnel (%0 * if not limited) * @max_down: Maximum available downstream bandwidth for the DP tunnel @@ -869,8 +873,8 @@ err_free: * Return: Returns a tb_tunnel on success or NULL on failure. */ struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in, - struct tb_port *out, int max_up, - int max_down) + struct tb_port *out, int link_nr, + int max_up, int max_down) { struct tb_tunnel *tunnel; struct tb_path **paths; @@ -894,21 +898,21 @@ struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in, paths = tunnel->paths; path = tb_path_alloc(tb, in, TB_DP_VIDEO_HOPID, out, TB_DP_VIDEO_HOPID, - 1, "Video"); + link_nr, "Video"); if (!path) goto err_free; tb_dp_init_video_path(path); paths[TB_DP_VIDEO_PATH_OUT] = path; path = tb_path_alloc(tb, in, TB_DP_AUX_TX_HOPID, out, - TB_DP_AUX_TX_HOPID, 1, "AUX TX"); + TB_DP_AUX_TX_HOPID, link_nr, "AUX TX"); if (!path) goto err_free; tb_dp_init_aux_path(path); paths[TB_DP_AUX_PATH_OUT] = path; path = tb_path_alloc(tb, out, TB_DP_AUX_RX_HOPID, in, - TB_DP_AUX_RX_HOPID, 1, "AUX RX"); + TB_DP_AUX_RX_HOPID, link_nr, "AUX RX"); if (!path) goto err_free; tb_dp_init_aux_path(path); diff --git a/drivers/thunderbolt/tunnel.h b/drivers/thunderbolt/tunnel.h index 03e56076b5bc..bb4d1f1d6d0b 100644 --- a/drivers/thunderbolt/tunnel.h +++ b/drivers/thunderbolt/tunnel.h @@ -71,8 +71,8 @@ struct tb_tunnel *tb_tunnel_alloc_pci(struct tb *tb, struct tb_port *up, struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in, bool alloc_hopid); struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in, - struct tb_port *out, int max_up, - int max_down); + struct tb_port *out, int link_nr, + int max_up, int max_down); struct tb_tunnel *tb_tunnel_alloc_dma(struct tb *tb, struct tb_port *nhi, struct tb_port *dst, int transmit_path, int transmit_ring, int receive_path, diff --git a/drivers/thunderbolt/usb4_port.c b/drivers/thunderbolt/usb4_port.c index 29e2a4f9c9f5..6b02945624ee 100644 --- a/drivers/thunderbolt/usb4_port.c +++ b/drivers/thunderbolt/usb4_port.c @@ -7,9 +7,37 @@ */ #include <linux/pm_runtime.h> +#include <linux/component.h> +#include <linux/property.h> #include "tb.h" +static int connector_bind(struct device *dev, struct device *connector, void *data) +{ + int ret; + + ret = sysfs_create_link(&dev->kobj, &connector->kobj, "connector"); + if (ret) + return ret; + + ret = sysfs_create_link(&connector->kobj, &dev->kobj, dev_name(dev)); + if (ret) + sysfs_remove_link(&dev->kobj, "connector"); + + return ret; +} + +static void connector_unbind(struct device *dev, struct device *connector, void *data) +{ + sysfs_remove_link(&connector->kobj, dev_name(dev)); + sysfs_remove_link(&dev->kobj, "connector"); +} + +static const struct component_ops connector_ops = { + .bind = connector_bind, + .unbind = connector_unbind, +}; + static ssize_t link_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -246,6 +274,14 @@ struct usb4_port *usb4_port_device_add(struct tb_port *port) return ERR_PTR(ret); } + if (dev_fwnode(&usb4->dev)) { + ret = component_add(&usb4->dev, &connector_ops); + if (ret) { + dev_err(&usb4->dev, "failed to add component\n"); + device_unregister(&usb4->dev); + } + } + pm_runtime_no_callbacks(&usb4->dev); pm_runtime_set_active(&usb4->dev); pm_runtime_enable(&usb4->dev); @@ -265,6 +301,8 @@ struct usb4_port *usb4_port_device_add(struct tb_port *port) */ void usb4_port_device_remove(struct usb4_port *usb4) { + if (dev_fwnode(&usb4->dev)) + component_del(&usb4->dev, &connector_ops); device_unregister(&usb4->dev); } diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c index 01d6b724ca51..c31c0d94d8b3 100644 --- a/drivers/thunderbolt/xdomain.c +++ b/drivers/thunderbolt/xdomain.c @@ -19,13 +19,38 @@ #include "tb.h" -#define XDOMAIN_DEFAULT_TIMEOUT 1000 /* ms */ -#define XDOMAIN_UUID_RETRIES 10 -#define XDOMAIN_PROPERTIES_RETRIES 10 -#define XDOMAIN_PROPERTIES_CHANGED_RETRIES 10 -#define XDOMAIN_BONDING_WAIT 100 /* ms */ +#define XDOMAIN_SHORT_TIMEOUT 100 /* ms */ +#define XDOMAIN_DEFAULT_TIMEOUT 1000 /* ms */ +#define XDOMAIN_BONDING_TIMEOUT 10000 /* ms */ +#define XDOMAIN_RETRIES 10 #define XDOMAIN_DEFAULT_MAX_HOPID 15 +enum { + XDOMAIN_STATE_INIT, + XDOMAIN_STATE_UUID, + XDOMAIN_STATE_LINK_STATUS, + XDOMAIN_STATE_LINK_STATE_CHANGE, + XDOMAIN_STATE_LINK_STATUS2, + XDOMAIN_STATE_BONDING_UUID_LOW, + XDOMAIN_STATE_BONDING_UUID_HIGH, + XDOMAIN_STATE_PROPERTIES, + XDOMAIN_STATE_ENUMERATED, + XDOMAIN_STATE_ERROR, +}; + +static const char * const state_names[] = { + [XDOMAIN_STATE_INIT] = "INIT", + [XDOMAIN_STATE_UUID] = "UUID", + [XDOMAIN_STATE_LINK_STATUS] = "LINK_STATUS", + [XDOMAIN_STATE_LINK_STATE_CHANGE] = "LINK_STATE_CHANGE", + [XDOMAIN_STATE_LINK_STATUS2] = "LINK_STATUS2", + [XDOMAIN_STATE_BONDING_UUID_LOW] = "BONDING_UUID_LOW", + [XDOMAIN_STATE_BONDING_UUID_HIGH] = "BONDING_UUID_HIGH", + [XDOMAIN_STATE_PROPERTIES] = "PROPERTIES", + [XDOMAIN_STATE_ENUMERATED] = "ENUMERATED", + [XDOMAIN_STATE_ERROR] = "ERROR", +}; + struct xdomain_request_work { struct work_struct work; struct tb_xdp_header *pkg; @@ -235,7 +260,7 @@ static int tb_xdp_handle_error(const struct tb_xdp_error_response *res) } static int tb_xdp_uuid_request(struct tb_ctl *ctl, u64 route, int retry, - uuid_t *uuid) + uuid_t *uuid, u64 *remote_route) { struct tb_xdp_uuid_response res; struct tb_xdp_uuid req; @@ -258,6 +283,8 @@ static int tb_xdp_uuid_request(struct tb_ctl *ctl, u64 route, int retry, return ret; uuid_copy(uuid, &res.src_uuid); + *remote_route = (u64)res.src_route_hi << 32 | res.src_route_lo; + return 0; } @@ -473,6 +500,112 @@ tb_xdp_properties_changed_response(struct tb_ctl *ctl, u64 route, u8 sequence) TB_CFG_PKG_XDOMAIN_RESP); } +static int tb_xdp_link_state_status_request(struct tb_ctl *ctl, u64 route, + u8 sequence, u8 *slw, u8 *tlw, + u8 *sls, u8 *tls) +{ + struct tb_xdp_link_state_status_response res; + struct tb_xdp_link_state_status req; + int ret; + + memset(&req, 0, sizeof(req)); + tb_xdp_fill_header(&req.hdr, route, sequence, LINK_STATE_STATUS_REQUEST, + sizeof(req)); + + memset(&res, 0, sizeof(res)); + ret = __tb_xdomain_request(ctl, &req, sizeof(req), TB_CFG_PKG_XDOMAIN_REQ, + &res, sizeof(res), TB_CFG_PKG_XDOMAIN_RESP, + XDOMAIN_DEFAULT_TIMEOUT); + if (ret) + return ret; + + ret = tb_xdp_handle_error(&res.err); + if (ret) + return ret; + + if (res.status != 0) + return -EREMOTEIO; + + *slw = res.slw; + *tlw = res.tlw; + *sls = res.sls; + *tls = res.tls; + + return 0; +} + +static int tb_xdp_link_state_status_response(struct tb *tb, struct tb_ctl *ctl, + struct tb_xdomain *xd, u8 sequence) +{ + struct tb_switch *sw = tb_to_switch(xd->dev.parent); + struct tb_xdp_link_state_status_response res; + struct tb_port *port = tb_port_at(xd->route, sw); + u32 val[2]; + int ret; + + memset(&res, 0, sizeof(res)); + tb_xdp_fill_header(&res.hdr, xd->route, sequence, + LINK_STATE_STATUS_RESPONSE, sizeof(res)); + + ret = tb_port_read(port, val, TB_CFG_PORT, + port->cap_phy + LANE_ADP_CS_0, ARRAY_SIZE(val)); + if (ret) + return ret; + + res.slw = (val[0] & LANE_ADP_CS_0_SUPPORTED_WIDTH_MASK) >> + LANE_ADP_CS_0_SUPPORTED_WIDTH_SHIFT; + res.sls = (val[0] & LANE_ADP_CS_0_SUPPORTED_SPEED_MASK) >> + LANE_ADP_CS_0_SUPPORTED_SPEED_SHIFT; + res.tls = val[1] & LANE_ADP_CS_1_TARGET_SPEED_MASK; + res.tlw = (val[1] & LANE_ADP_CS_1_TARGET_WIDTH_MASK) >> + LANE_ADP_CS_1_TARGET_WIDTH_SHIFT; + + return __tb_xdomain_response(ctl, &res, sizeof(res), + TB_CFG_PKG_XDOMAIN_RESP); +} + +static int tb_xdp_link_state_change_request(struct tb_ctl *ctl, u64 route, + u8 sequence, u8 tlw, u8 tls) +{ + struct tb_xdp_link_state_change_response res; + struct tb_xdp_link_state_change req; + int ret; + + memset(&req, 0, sizeof(req)); + tb_xdp_fill_header(&req.hdr, route, sequence, LINK_STATE_CHANGE_REQUEST, + sizeof(req)); + req.tlw = tlw; + req.tls = tls; + + memset(&res, 0, sizeof(res)); + ret = __tb_xdomain_request(ctl, &req, sizeof(req), TB_CFG_PKG_XDOMAIN_REQ, + &res, sizeof(res), TB_CFG_PKG_XDOMAIN_RESP, + XDOMAIN_DEFAULT_TIMEOUT); + if (ret) + return ret; + + ret = tb_xdp_handle_error(&res.err); + if (ret) + return ret; + + return res.status != 0 ? -EREMOTEIO : 0; +} + +static int tb_xdp_link_state_change_response(struct tb_ctl *ctl, u64 route, + u8 sequence, u32 status) +{ + struct tb_xdp_link_state_change_response res; + + memset(&res, 0, sizeof(res)); + tb_xdp_fill_header(&res.hdr, route, sequence, LINK_STATE_CHANGE_RESPONSE, + sizeof(res)); + + res.status = status; + + return __tb_xdomain_response(ctl, &res, sizeof(res), + TB_CFG_PKG_XDOMAIN_RESP); +} + /** * tb_register_protocol_handler() - Register protocol handler * @handler: Handler to register @@ -600,14 +733,13 @@ static void tb_xdp_handle_request(struct work_struct *work) goto out; } - tb_dbg(tb, "%llx: received XDomain request %#x\n", route, pkg->type); - xd = tb_xdomain_find_by_route_locked(tb, route); if (xd) update_property_block(xd); switch (pkg->type) { case PROPERTIES_REQUEST: + tb_dbg(tb, "%llx: received XDomain properties request\n", route); if (xd) { ret = tb_xdp_properties_response(tb, ctl, xd, sequence, (const struct tb_xdp_properties *)pkg); @@ -615,6 +747,9 @@ static void tb_xdp_handle_request(struct work_struct *work) break; case PROPERTIES_CHANGED_REQUEST: + tb_dbg(tb, "%llx: received XDomain properties changed request\n", + route); + ret = tb_xdp_properties_changed_response(ctl, route, sequence); /* @@ -622,18 +757,51 @@ static void tb_xdp_handle_request(struct work_struct *work) * the xdomain related to this connection as well in * case there is a change in services it offers. */ - if (xd && device_is_registered(&xd->dev)) { - queue_delayed_work(tb->wq, &xd->get_properties_work, - msecs_to_jiffies(50)); - } + if (xd && device_is_registered(&xd->dev)) + queue_delayed_work(tb->wq, &xd->state_work, + msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT)); break; case UUID_REQUEST_OLD: case UUID_REQUEST: + tb_dbg(tb, "%llx: received XDomain UUID request\n", route); ret = tb_xdp_uuid_response(ctl, route, sequence, uuid); break; + case LINK_STATE_STATUS_REQUEST: + tb_dbg(tb, "%llx: received XDomain link state status request\n", + route); + + if (xd) { + ret = tb_xdp_link_state_status_response(tb, ctl, xd, + sequence); + } else { + tb_xdp_error_response(ctl, route, sequence, + ERROR_NOT_READY); + } + break; + + case LINK_STATE_CHANGE_REQUEST: + tb_dbg(tb, "%llx: received XDomain link state change request\n", + route); + + if (xd && xd->state == XDOMAIN_STATE_BONDING_UUID_HIGH) { + const struct tb_xdp_link_state_change *lsc = + (const struct tb_xdp_link_state_change *)pkg; + + ret = tb_xdp_link_state_change_response(ctl, route, + sequence, 0); + xd->target_link_width = lsc->tlw; + queue_delayed_work(tb->wq, &xd->state_work, + msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT)); + } else { + tb_xdp_error_response(ctl, route, sequence, + ERROR_NOT_READY); + } + break; + default: + tb_dbg(tb, "%llx: unknown XDomain request %#x\n", route, pkg->type); tb_xdp_error_response(ctl, route, sequence, ERROR_NOT_SUPPORTED); break; @@ -1000,32 +1168,38 @@ static int tb_xdomain_update_link_attributes(struct tb_xdomain *xd) return 0; } -static void tb_xdomain_get_uuid(struct work_struct *work) +static int tb_xdomain_get_uuid(struct tb_xdomain *xd) { - struct tb_xdomain *xd = container_of(work, typeof(*xd), - get_uuid_work.work); struct tb *tb = xd->tb; uuid_t uuid; + u64 route; int ret; dev_dbg(&xd->dev, "requesting remote UUID\n"); - ret = tb_xdp_uuid_request(tb->ctl, xd->route, xd->uuid_retries, &uuid); + ret = tb_xdp_uuid_request(tb->ctl, xd->route, xd->state_retries, &uuid, + &route); if (ret < 0) { - if (xd->uuid_retries-- > 0) { + if (xd->state_retries-- > 0) { dev_dbg(&xd->dev, "failed to request UUID, retrying\n"); - queue_delayed_work(xd->tb->wq, &xd->get_uuid_work, - msecs_to_jiffies(100)); + return -EAGAIN; } else { dev_dbg(&xd->dev, "failed to read remote UUID\n"); } - return; + return ret; } dev_dbg(&xd->dev, "got remote UUID %pUb\n", &uuid); - if (uuid_equal(&uuid, xd->local_uuid)) - dev_dbg(&xd->dev, "intra-domain loop detected\n"); + if (uuid_equal(&uuid, xd->local_uuid)) { + if (route == xd->route) + dev_dbg(&xd->dev, "loop back detected\n"); + else + dev_dbg(&xd->dev, "intra-domain loop detected\n"); + + /* Don't bond lanes automatically for loops */ + xd->bonding_possible = false; + } /* * If the UUID is different, there is another domain connected @@ -1035,27 +1209,152 @@ static void tb_xdomain_get_uuid(struct work_struct *work) if (xd->remote_uuid && !uuid_equal(&uuid, xd->remote_uuid)) { dev_dbg(&xd->dev, "remote UUID is different, unplugging\n"); xd->is_unplugged = true; - return; + return -ENODEV; } /* First time fill in the missing UUID */ if (!xd->remote_uuid) { xd->remote_uuid = kmemdup(&uuid, sizeof(uuid_t), GFP_KERNEL); if (!xd->remote_uuid) - return; + return -ENOMEM; } - /* Now we can start the normal properties exchange */ - queue_delayed_work(xd->tb->wq, &xd->properties_changed_work, - msecs_to_jiffies(100)); - queue_delayed_work(xd->tb->wq, &xd->get_properties_work, - msecs_to_jiffies(1000)); + return 0; } -static void tb_xdomain_get_properties(struct work_struct *work) +static int tb_xdomain_get_link_status(struct tb_xdomain *xd) +{ + struct tb *tb = xd->tb; + u8 slw, tlw, sls, tls; + int ret; + + dev_dbg(&xd->dev, "sending link state status request to %pUb\n", + xd->remote_uuid); + + ret = tb_xdp_link_state_status_request(tb->ctl, xd->route, + xd->state_retries, &slw, &tlw, &sls, + &tls); + if (ret) { + if (ret != -EOPNOTSUPP && xd->state_retries-- > 0) { + dev_dbg(&xd->dev, + "failed to request remote link status, retrying\n"); + return -EAGAIN; + } + dev_dbg(&xd->dev, "failed to receive remote link status\n"); + return ret; + } + + dev_dbg(&xd->dev, "remote link supports width %#x speed %#x\n", slw, sls); + + if (slw < LANE_ADP_CS_0_SUPPORTED_WIDTH_DUAL) { + dev_dbg(&xd->dev, "remote adapter is single lane only\n"); + return -EOPNOTSUPP; + } + + return 0; +} + +static int tb_xdomain_link_state_change(struct tb_xdomain *xd, + unsigned int width) +{ + struct tb_switch *sw = tb_to_switch(xd->dev.parent); + struct tb_port *port = tb_port_at(xd->route, sw); + struct tb *tb = xd->tb; + u8 tlw, tls; + u32 val; + int ret; + + if (width == 2) + tlw = LANE_ADP_CS_1_TARGET_WIDTH_DUAL; + else if (width == 1) + tlw = LANE_ADP_CS_1_TARGET_WIDTH_SINGLE; + else + return -EINVAL; + + /* Use the current target speed */ + ret = tb_port_read(port, &val, TB_CFG_PORT, port->cap_phy + LANE_ADP_CS_1, 1); + if (ret) + return ret; + tls = val & LANE_ADP_CS_1_TARGET_SPEED_MASK; + + dev_dbg(&xd->dev, "sending link state change request with width %#x speed %#x\n", + tlw, tls); + + ret = tb_xdp_link_state_change_request(tb->ctl, xd->route, + xd->state_retries, tlw, tls); + if (ret) { + if (ret != -EOPNOTSUPP && xd->state_retries-- > 0) { + dev_dbg(&xd->dev, + "failed to change remote link state, retrying\n"); + return -EAGAIN; + } + dev_err(&xd->dev, "failed request link state change, aborting\n"); + return ret; + } + + dev_dbg(&xd->dev, "received link state change response\n"); + return 0; +} + +static int tb_xdomain_bond_lanes_uuid_high(struct tb_xdomain *xd) +{ + struct tb_port *port; + int ret, width; + + if (xd->target_link_width == LANE_ADP_CS_1_TARGET_WIDTH_SINGLE) { + width = 1; + } else if (xd->target_link_width == LANE_ADP_CS_1_TARGET_WIDTH_DUAL) { + width = 2; + } else { + if (xd->state_retries-- > 0) { + dev_dbg(&xd->dev, + "link state change request not received yet, retrying\n"); + return -EAGAIN; + } + dev_dbg(&xd->dev, "timeout waiting for link change request\n"); + return -ETIMEDOUT; + } + + port = tb_port_at(xd->route, tb_xdomain_parent(xd)); + + /* + * We can't use tb_xdomain_lane_bonding_enable() here because it + * is the other side that initiates lane bonding. So here we + * just set the width to both lane adapters and wait for the + * link to transition bonded. + */ + ret = tb_port_set_link_width(port->dual_link_port, width); + if (ret) { + tb_port_warn(port->dual_link_port, + "failed to set link width to %d\n", width); + return ret; + } + + ret = tb_port_set_link_width(port, width); + if (ret) { + tb_port_warn(port, "failed to set link width to %d\n", width); + return ret; + } + + ret = tb_port_wait_for_link_width(port, width, XDOMAIN_BONDING_TIMEOUT); + if (ret) { + dev_warn(&xd->dev, "error waiting for link width to become %d\n", + width); + return ret; + } + + port->bonded = width == 2; + port->dual_link_port->bonded = width == 2; + + tb_port_update_credits(port); + tb_xdomain_update_link_attributes(xd); + + dev_dbg(&xd->dev, "lane bonding %sabled\n", width == 2 ? "en" : "dis"); + return 0; +} + +static int tb_xdomain_get_properties(struct tb_xdomain *xd) { - struct tb_xdomain *xd = container_of(work, typeof(*xd), - get_properties_work.work); struct tb_property_dir *dir; struct tb *tb = xd->tb; bool update = false; @@ -1066,34 +1365,35 @@ static void tb_xdomain_get_properties(struct work_struct *work) dev_dbg(&xd->dev, "requesting remote properties\n"); ret = tb_xdp_properties_request(tb->ctl, xd->route, xd->local_uuid, - xd->remote_uuid, xd->properties_retries, + xd->remote_uuid, xd->state_retries, &block, &gen); if (ret < 0) { - if (xd->properties_retries-- > 0) { + if (xd->state_retries-- > 0) { dev_dbg(&xd->dev, "failed to request remote properties, retrying\n"); - queue_delayed_work(xd->tb->wq, &xd->get_properties_work, - msecs_to_jiffies(1000)); + return -EAGAIN; } else { /* Give up now */ dev_err(&xd->dev, "failed read XDomain properties from %pUb\n", xd->remote_uuid); } - return; - } - xd->properties_retries = XDOMAIN_PROPERTIES_RETRIES; + return ret; + } mutex_lock(&xd->lock); /* Only accept newer generation properties */ - if (xd->remote_properties && gen <= xd->remote_property_block_gen) + if (xd->remote_properties && gen <= xd->remote_property_block_gen) { + ret = 0; goto err_free_block; + } dir = tb_property_parse_dir(block, ret); if (!dir) { dev_err(&xd->dev, "failed to parse XDomain properties\n"); + ret = -ENOMEM; goto err_free_block; } @@ -1124,9 +1424,16 @@ static void tb_xdomain_get_properties(struct work_struct *work) * registered, we notify the userspace that it has changed. */ if (!update) { + struct tb_port *port; + + /* Now disable lane 1 if bonding was not enabled */ + port = tb_port_at(xd->route, tb_xdomain_parent(xd)); + if (!port->bonded) + tb_port_disable(port->dual_link_port); + if (device_add(&xd->dev)) { dev_err(&xd->dev, "failed to add XDomain device\n"); - return; + return -ENODEV; } dev_info(&xd->dev, "new host found, vendor=%#x device=%#x\n", xd->vendor, xd->device); @@ -1138,13 +1445,193 @@ static void tb_xdomain_get_properties(struct work_struct *work) } enumerate_services(xd); - return; + return 0; err_free_dir: tb_property_free_dir(dir); err_free_block: kfree(block); mutex_unlock(&xd->lock); + + return ret; +} + +static void tb_xdomain_queue_uuid(struct tb_xdomain *xd) +{ + xd->state = XDOMAIN_STATE_UUID; + xd->state_retries = XDOMAIN_RETRIES; + queue_delayed_work(xd->tb->wq, &xd->state_work, + msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT)); +} + +static void tb_xdomain_queue_link_status(struct tb_xdomain *xd) +{ + xd->state = XDOMAIN_STATE_LINK_STATUS; + xd->state_retries = XDOMAIN_RETRIES; + queue_delayed_work(xd->tb->wq, &xd->state_work, + msecs_to_jiffies(XDOMAIN_DEFAULT_TIMEOUT)); +} + +static void tb_xdomain_queue_link_status2(struct tb_xdomain *xd) +{ + xd->state = XDOMAIN_STATE_LINK_STATUS2; + xd->state_retries = XDOMAIN_RETRIES; + queue_delayed_work(xd->tb->wq, &xd->state_work, + msecs_to_jiffies(XDOMAIN_DEFAULT_TIMEOUT)); +} + +static void tb_xdomain_queue_bonding(struct tb_xdomain *xd) +{ + if (memcmp(xd->local_uuid, xd->remote_uuid, UUID_SIZE) > 0) { + dev_dbg(&xd->dev, "we have higher UUID, other side bonds the lanes\n"); + xd->state = XDOMAIN_STATE_BONDING_UUID_HIGH; + } else { + dev_dbg(&xd->dev, "we have lower UUID, bonding lanes\n"); + xd->state = XDOMAIN_STATE_LINK_STATE_CHANGE; + } + + xd->state_retries = XDOMAIN_RETRIES; + queue_delayed_work(xd->tb->wq, &xd->state_work, + msecs_to_jiffies(XDOMAIN_DEFAULT_TIMEOUT)); +} + +static void tb_xdomain_queue_bonding_uuid_low(struct tb_xdomain *xd) +{ + xd->state = XDOMAIN_STATE_BONDING_UUID_LOW; + xd->state_retries = XDOMAIN_RETRIES; + queue_delayed_work(xd->tb->wq, &xd->state_work, + msecs_to_jiffies(XDOMAIN_DEFAULT_TIMEOUT)); +} + +static void tb_xdomain_queue_properties(struct tb_xdomain *xd) +{ + xd->state = XDOMAIN_STATE_PROPERTIES; + xd->state_retries = XDOMAIN_RETRIES; + queue_delayed_work(xd->tb->wq, &xd->state_work, + msecs_to_jiffies(XDOMAIN_DEFAULT_TIMEOUT)); +} + +static void tb_xdomain_queue_properties_changed(struct tb_xdomain *xd) +{ + xd->properties_changed_retries = XDOMAIN_RETRIES; + queue_delayed_work(xd->tb->wq, &xd->properties_changed_work, + msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT)); +} + +static void tb_xdomain_state_work(struct work_struct *work) +{ + struct tb_xdomain *xd = container_of(work, typeof(*xd), state_work.work); + int ret, state = xd->state; + + if (WARN_ON_ONCE(state < XDOMAIN_STATE_INIT || + state > XDOMAIN_STATE_ERROR)) + return; + + dev_dbg(&xd->dev, "running state %s\n", state_names[state]); + + switch (state) { + case XDOMAIN_STATE_INIT: + if (xd->needs_uuid) { + tb_xdomain_queue_uuid(xd); + } else { + tb_xdomain_queue_properties_changed(xd); + tb_xdomain_queue_properties(xd); + } + break; + + case XDOMAIN_STATE_UUID: + ret = tb_xdomain_get_uuid(xd); + if (ret) { + if (ret == -EAGAIN) + goto retry_state; + xd->state = XDOMAIN_STATE_ERROR; + } else { + tb_xdomain_queue_properties_changed(xd); + if (xd->bonding_possible) + tb_xdomain_queue_link_status(xd); + else + tb_xdomain_queue_properties(xd); + } + break; + + case XDOMAIN_STATE_LINK_STATUS: + ret = tb_xdomain_get_link_status(xd); + if (ret) { + if (ret == -EAGAIN) + goto retry_state; + + /* + * If any of the lane bonding states fail we skip + * bonding completely and try to continue from + * reading properties. + */ + tb_xdomain_queue_properties(xd); + } else { + tb_xdomain_queue_bonding(xd); + } + break; + + case XDOMAIN_STATE_LINK_STATE_CHANGE: + ret = tb_xdomain_link_state_change(xd, 2); + if (ret) { + if (ret == -EAGAIN) + goto retry_state; + tb_xdomain_queue_properties(xd); + } else { + tb_xdomain_queue_link_status2(xd); + } + break; + + case XDOMAIN_STATE_LINK_STATUS2: + ret = tb_xdomain_get_link_status(xd); + if (ret) { + if (ret == -EAGAIN) + goto retry_state; + tb_xdomain_queue_properties(xd); + } else { + tb_xdomain_queue_bonding_uuid_low(xd); + } + break; + + case XDOMAIN_STATE_BONDING_UUID_LOW: + tb_xdomain_lane_bonding_enable(xd); + tb_xdomain_queue_properties(xd); + break; + + case XDOMAIN_STATE_BONDING_UUID_HIGH: + if (tb_xdomain_bond_lanes_uuid_high(xd) == -EAGAIN) + goto retry_state; + tb_xdomain_queue_properties(xd); + break; + + case XDOMAIN_STATE_PROPERTIES: + ret = tb_xdomain_get_properties(xd); + if (ret) { + if (ret == -EAGAIN) + goto retry_state; + xd->state = XDOMAIN_STATE_ERROR; + } else { + xd->state = XDOMAIN_STATE_ENUMERATED; + } + break; + + case XDOMAIN_STATE_ENUMERATED: + tb_xdomain_queue_properties(xd); + break; + + case XDOMAIN_STATE_ERROR: + break; + + default: + dev_warn(&xd->dev, "unexpected state %d\n", state); + break; + } + + return; + +retry_state: + queue_delayed_work(xd->tb->wq, &xd->state_work, + msecs_to_jiffies(XDOMAIN_DEFAULT_TIMEOUT)); } static void tb_xdomain_properties_changed(struct work_struct *work) @@ -1163,13 +1650,13 @@ static void tb_xdomain_properties_changed(struct work_struct *work) "failed to send properties changed notification, retrying\n"); queue_delayed_work(xd->tb->wq, &xd->properties_changed_work, - msecs_to_jiffies(1000)); + msecs_to_jiffies(XDOMAIN_DEFAULT_TIMEOUT)); } dev_err(&xd->dev, "failed to send properties changed notification\n"); return; } - xd->properties_changed_retries = XDOMAIN_PROPERTIES_CHANGED_RETRIES; + xd->properties_changed_retries = XDOMAIN_RETRIES; } static ssize_t device_show(struct device *dev, struct device_attribute *attr, @@ -1304,31 +1791,17 @@ static void tb_xdomain_release(struct device *dev) static void start_handshake(struct tb_xdomain *xd) { - xd->uuid_retries = XDOMAIN_UUID_RETRIES; - xd->properties_retries = XDOMAIN_PROPERTIES_RETRIES; - xd->properties_changed_retries = XDOMAIN_PROPERTIES_CHANGED_RETRIES; - - if (xd->needs_uuid) { - queue_delayed_work(xd->tb->wq, &xd->get_uuid_work, - msecs_to_jiffies(100)); - } else { - /* Start exchanging properties with the other host */ - queue_delayed_work(xd->tb->wq, &xd->properties_changed_work, - msecs_to_jiffies(100)); - queue_delayed_work(xd->tb->wq, &xd->get_properties_work, - msecs_to_jiffies(1000)); - } + xd->state = XDOMAIN_STATE_INIT; + queue_delayed_work(xd->tb->wq, &xd->state_work, + msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT)); } static void stop_handshake(struct tb_xdomain *xd) { - xd->uuid_retries = 0; - xd->properties_retries = 0; - xd->properties_changed_retries = 0; - - cancel_delayed_work_sync(&xd->get_uuid_work); - cancel_delayed_work_sync(&xd->get_properties_work); cancel_delayed_work_sync(&xd->properties_changed_work); + cancel_delayed_work_sync(&xd->state_work); + xd->properties_changed_retries = 0; + xd->state_retries = 0; } static int __maybe_unused tb_xdomain_suspend(struct device *dev) @@ -1389,8 +1862,7 @@ struct tb_xdomain *tb_xdomain_alloc(struct tb *tb, struct device *parent, ida_init(&xd->in_hopids); ida_init(&xd->out_hopids); mutex_init(&xd->lock); - INIT_DELAYED_WORK(&xd->get_uuid_work, tb_xdomain_get_uuid); - INIT_DELAYED_WORK(&xd->get_properties_work, tb_xdomain_get_properties); + INIT_DELAYED_WORK(&xd->state_work, tb_xdomain_state_work); INIT_DELAYED_WORK(&xd->properties_changed_work, tb_xdomain_properties_changed); @@ -1405,6 +1877,7 @@ struct tb_xdomain *tb_xdomain_alloc(struct tb *tb, struct device *parent, goto err_free_local_uuid; } else { xd->needs_uuid = true; + xd->bonding_possible = !!down->dual_link_port; } device_initialize(&xd->dev); @@ -1523,9 +1996,9 @@ int tb_xdomain_lane_bonding_enable(struct tb_xdomain *xd) return ret; } - ret = tb_port_wait_for_link_width(port, 2, 100); + ret = tb_port_wait_for_link_width(port, 2, XDOMAIN_BONDING_TIMEOUT); if (ret) { - tb_port_warn(port, "timeout enabling lane bonding\n"); + tb_port_warn(port, "failed to enable lane bonding\n"); return ret; } diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c index e3a49d837609..362217189ef3 100644 --- a/drivers/usb/atm/usbatm.c +++ b/drivers/usb/atm/usbatm.c @@ -1091,7 +1091,7 @@ int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id, snd_buf_bytes - (snd_buf_bytes % instance->tx_channel.stride)); /* rx buffer size must be a positive multiple of the endpoint maxpacket */ - maxpacket = usb_maxpacket(usb_dev, instance->rx_channel.endpoint, 0); + maxpacket = usb_maxpacket(usb_dev, instance->rx_channel.endpoint); if ((maxpacket < 1) || (maxpacket > UDSL_MAX_BUF_SIZE)) { dev_err(dev, "%s: invalid endpoint %02x!\n", __func__, diff --git a/drivers/usb/c67x00/c67x00-drv.c b/drivers/usb/c67x00/c67x00-drv.c index 53838e7d4eef..6db5cb1b2dbb 100644 --- a/drivers/usb/c67x00/c67x00-drv.c +++ b/drivers/usb/c67x00/c67x00-drv.c @@ -189,14 +189,12 @@ static int c67x00_drv_remove(struct platform_device *pdev) c67x00_ll_release(c67x00); res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (res) - free_irq(res->start, c67x00); + free_irq(res->start, c67x00); iounmap(c67x00->hpi.base); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res) - release_mem_region(res->start, resource_size(res)); + release_mem_region(res->start, resource_size(res)); kfree(c67x00); diff --git a/drivers/usb/c67x00/c67x00-sched.c b/drivers/usb/c67x00/c67x00-sched.c index c7d3e907be81..a09fa68a6ce7 100644 --- a/drivers/usb/c67x00/c67x00-sched.c +++ b/drivers/usb/c67x00/c67x00-sched.c @@ -655,7 +655,7 @@ static int c67x00_add_data_urb(struct c67x00_hcd *c67x00, struct urb *urb) usb_pipeout(urb->pipe)); remaining = urb->transfer_buffer_length - urb->actual_length; - maxps = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); + maxps = usb_maxpacket(urb->dev, urb->pipe); need_empty = (urb->transfer_flags & URB_ZERO_PACKET) && usb_pipeout(urb->pipe) && !(remaining % maxps); @@ -866,7 +866,7 @@ static inline int c67x00_end_of_data(struct c67x00_td *td) if (unlikely(!act_bytes)) return 1; /* This was an empty packet */ - maxps = usb_maxpacket(td_udev(td), td->pipe, usb_pipeout(td->pipe)); + maxps = usb_maxpacket(td_udev(td), td->pipe); if (unlikely(act_bytes < maxps)) return 1; /* Smaller then full packet */ diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c index d6d515d598dc..5c15c48952a6 100644 --- a/drivers/usb/cdns3/cdns3-gadget.c +++ b/drivers/usb/cdns3/cdns3-gadget.c @@ -2038,7 +2038,7 @@ int cdns3_ep_config(struct cdns3_endpoint *priv_ep, bool enable) u8 mult = 0; int ret; - buffering = CDNS3_EP_BUF_SIZE - 1; + buffering = priv_dev->ep_buf_size - 1; cdns3_configure_dmult(priv_dev, priv_ep); @@ -2057,7 +2057,7 @@ int cdns3_ep_config(struct cdns3_endpoint *priv_ep, bool enable) break; default: ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_ISOC); - mult = CDNS3_EP_ISO_HS_MULT - 1; + mult = priv_dev->ep_iso_burst - 1; buffering = mult + 1; } @@ -2073,14 +2073,14 @@ int cdns3_ep_config(struct cdns3_endpoint *priv_ep, bool enable) mult = 0; max_packet_size = 1024; if (priv_ep->type == USB_ENDPOINT_XFER_ISOC) { - maxburst = CDNS3_EP_ISO_SS_BURST - 1; + maxburst = priv_dev->ep_iso_burst - 1; buffering = (mult + 1) * (maxburst + 1); if (priv_ep->interval > 1) buffering++; } else { - maxburst = CDNS3_EP_BUF_SIZE - 1; + maxburst = priv_dev->ep_buf_size - 1; } break; default: @@ -2095,6 +2095,10 @@ int cdns3_ep_config(struct cdns3_endpoint *priv_ep, bool enable) else priv_ep->trb_burst_size = 16; + mult = min_t(u8, mult, EP_CFG_MULT_MAX); + buffering = min_t(u8, buffering, EP_CFG_BUFFERING_MAX); + maxburst = min_t(u8, maxburst, EP_CFG_MAXBURST_MAX); + /* onchip buffer is only allocated before configuration */ if (!priv_dev->hw_configured_flag) { ret = cdns3_ep_onchip_buffer_reserve(priv_dev, buffering + 1, @@ -2961,6 +2965,40 @@ static int cdns3_gadget_udc_stop(struct usb_gadget *gadget) return 0; } +/** + * cdns3_gadget_check_config - ensure cdns3 can support the USB configuration + * @gadget: pointer to the USB gadget + * + * Used to record the maximum number of endpoints being used in a USB composite + * device. (across all configurations) This is to be used in the calculation + * of the TXFIFO sizes when resizing internal memory for individual endpoints. + * It will help ensured that the resizing logic reserves enough space for at + * least one max packet. + */ +static int cdns3_gadget_check_config(struct usb_gadget *gadget) +{ + struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget); + struct usb_ep *ep; + int n_in = 0; + int total; + + list_for_each_entry(ep, &gadget->ep_list, ep_list) { + if (ep->claimed && (ep->address & USB_DIR_IN)) + n_in++; + } + + /* 2KB are reserved for EP0, 1KB for out*/ + total = 2 + n_in + 1; + + if (total > priv_dev->onchip_buffers) + return -ENOMEM; + + priv_dev->ep_buf_size = priv_dev->ep_iso_burst = + (priv_dev->onchip_buffers - 2) / (n_in + 1); + + return 0; +} + static const struct usb_gadget_ops cdns3_gadget_ops = { .get_frame = cdns3_gadget_get_frame, .wakeup = cdns3_gadget_wakeup, @@ -2969,6 +3007,7 @@ static const struct usb_gadget_ops cdns3_gadget_ops = { .udc_start = cdns3_gadget_udc_start, .udc_stop = cdns3_gadget_udc_stop, .match_ep = cdns3_gadget_match_ep, + .check_config = cdns3_gadget_check_config, }; static void cdns3_free_all_eps(struct cdns3_device *priv_dev) diff --git a/drivers/usb/cdns3/cdns3-gadget.h b/drivers/usb/cdns3/cdns3-gadget.h index c5660f2c4293..fbe4a8e3aa89 100644 --- a/drivers/usb/cdns3/cdns3-gadget.h +++ b/drivers/usb/cdns3/cdns3-gadget.h @@ -562,15 +562,18 @@ struct cdns3_usb_regs { /* Max burst size (used only in SS mode). */ #define EP_CFG_MAXBURST_MASK GENMASK(11, 8) #define EP_CFG_MAXBURST(p) (((p) << 8) & EP_CFG_MAXBURST_MASK) +#define EP_CFG_MAXBURST_MAX 15 /* ISO max burst. */ #define EP_CFG_MULT_MASK GENMASK(15, 14) #define EP_CFG_MULT(p) (((p) << 14) & EP_CFG_MULT_MASK) +#define EP_CFG_MULT_MAX 2 /* ISO max burst. */ #define EP_CFG_MAXPKTSIZE_MASK GENMASK(26, 16) #define EP_CFG_MAXPKTSIZE(p) (((p) << 16) & EP_CFG_MAXPKTSIZE_MASK) /* Max number of buffered packets. */ #define EP_CFG_BUFFERING_MASK GENMASK(31, 27) #define EP_CFG_BUFFERING(p) (((p) << 27) & EP_CFG_BUFFERING_MASK) +#define EP_CFG_BUFFERING_MAX 15 /* EP_CMD - bitmasks */ /* Endpoint reset. */ @@ -1094,9 +1097,6 @@ struct cdns3_trb { #define CDNS3_ENDPOINTS_MAX_COUNT 32 #define CDNS3_EP_ZLP_BUF_SIZE 1024 -#define CDNS3_EP_BUF_SIZE 4 /* KB */ -#define CDNS3_EP_ISO_HS_MULT 3 -#define CDNS3_EP_ISO_SS_BURST 3 #define CDNS3_MAX_NUM_DESCMISS_BUF 32 #define CDNS3_DESCMIS_BUF_SIZE 2048 /* Bytes */ #define CDNS3_WA2_NUM_BUFFERS 128 @@ -1333,6 +1333,9 @@ struct cdns3_device { /*in KB */ u16 onchip_buffers; u16 onchip_used_size; + + u16 ep_buf_size; + u16 ep_iso_burst; }; void cdns3_set_register_bit(void __iomem *ptr, u32 mask); diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index d8b0041de612..2c14a9636056 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -228,8 +228,6 @@ static char *usb_dump_interface(int speed, char *start, char *end, start = usb_dump_interface_descriptor(start, end, intfc, iface, setno); for (i = 0; i < desc->desc.bNumEndpoints; i++) { - if (start > end) - return start; start = usb_dump_endpoint_descriptor(speed, start, end, &desc->endpoint[i].desc); } @@ -302,8 +300,6 @@ static char *usb_dump_config(int speed, char *start, char *end, intfc = config->intf_cache[i]; interface = config->interface[i]; for (j = 0; j < intfc->num_altsetting; j++) { - if (start > end) - return start; start = usb_dump_interface(speed, start, end, intfc, interface, j); } @@ -369,19 +365,11 @@ static char *usb_dump_desc(char *start, char *end, struct usb_device *dev) { int i; - if (start > end) - return start; - start = usb_dump_device_descriptor(start, end, &dev->descriptor); - if (start > end) - return start; - start = usb_dump_device_strings(start, end, dev); for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { - if (start > end) - return start; start = usb_dump_config(dev->speed, start, end, dev->config + i, /* active ? */ @@ -390,41 +378,6 @@ static char *usb_dump_desc(char *start, char *end, struct usb_device *dev) return start; } - -#ifdef PROC_EXTRA /* TBD: may want to add this code later */ - -static char *usb_dump_hub_descriptor(char *start, char *end, - const struct usb_hub_descriptor *desc) -{ - int leng = USB_DT_HUB_NONVAR_SIZE; - unsigned char *ptr = (unsigned char *)desc; - - if (start > end) - return start; - start += sprintf(start, "Interface:"); - while (leng && start <= end) { - start += sprintf(start, " %02x", *ptr); - ptr++; leng--; - } - *start++ = '\n'; - return start; -} - -static char *usb_dump_string(char *start, char *end, - const struct usb_device *dev, char *id, int index) -{ - if (start > end) - return start; - start += sprintf(start, "Interface:"); - if (index <= dev->maxstring && dev->stringindex && - dev->stringindex[index]) - start += sprintf(start, "%s: %.100s ", id, - dev->stringindex[index]); - return start; -} - -#endif /* PROC_EXTRA */ - /*****************************************************************/ /* This is a recursive function. Parameters: diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 355ed33a2179..b87452e22835 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1533,22 +1533,23 @@ static void choose_wakeup(struct usb_device *udev, pm_message_t msg) { int w; - /* Remote wakeup is needed only when we actually go to sleep. - * For things like FREEZE and QUIESCE, if the device is already - * autosuspended then its current wakeup setting is okay. + /* + * For FREEZE/QUIESCE, disable remote wakeups so no interrupts get + * generated. */ if (msg.event == PM_EVENT_FREEZE || msg.event == PM_EVENT_QUIESCE) { - if (udev->state != USB_STATE_SUSPENDED) - udev->do_remote_wakeup = 0; - return; - } + w = 0; - /* Enable remote wakeup if it is allowed, even if no interface drivers - * actually want it. - */ - w = device_may_wakeup(&udev->dev); + } else { + /* + * Enable remote wakeup if it is allowed, even if no interface + * drivers actually want it. + */ + w = device_may_wakeup(&udev->dev); + } - /* If the device is autosuspended with the wrong wakeup setting, + /* + * If the device is autosuspended with the wrong wakeup setting, * autoresume now so the setting can be changed. */ if (udev->state == USB_STATE_SUSPENDED && w != udev->do_remote_wakeup) diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 8176bc81a635..482dae72ef1c 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -15,7 +15,6 @@ #ifdef CONFIG_PPC_PMAC #include <asm/machdep.h> #include <asm/pmac_feature.h> -#include <asm/prom.h> #endif #include "usb.h" @@ -616,10 +615,10 @@ const struct dev_pm_ops usb_hcd_pci_pm_ops = { .suspend_noirq = hcd_pci_suspend_noirq, .resume_noirq = hcd_pci_resume_noirq, .resume = hcd_pci_resume, - .freeze = check_root_hub_suspended, + .freeze = hcd_pci_suspend, .freeze_noirq = check_root_hub_suspended, .thaw_noirq = NULL, - .thaw = NULL, + .thaw = hcd_pci_resume, .poweroff = hcd_pci_suspend, .poweroff_noirq = hcd_pci_suspend_noirq, .restore_noirq = hcd_pci_resume_noirq, diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index d9712c2602af..06eea8848ccc 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2816,6 +2816,7 @@ int usb_add_hcd(struct usb_hcd *hcd, { int retval; struct usb_device *rhdev; + struct usb_hcd *shared_hcd; if (!hcd->skip_phy_initialization && usb_hcd_is_primary_hcd(hcd)) { hcd->phy_roothub = usb_phy_roothub_alloc(hcd->self.sysdev); @@ -2976,13 +2977,26 @@ int usb_add_hcd(struct usb_hcd *hcd, goto err_hcd_driver_start; } + /* starting here, usbcore will pay attention to the shared HCD roothub */ + shared_hcd = hcd->shared_hcd; + if (!usb_hcd_is_primary_hcd(hcd) && shared_hcd && HCD_DEFER_RH_REGISTER(shared_hcd)) { + retval = register_root_hub(shared_hcd); + if (retval != 0) + goto err_register_root_hub; + + if (shared_hcd->uses_new_polling && HCD_POLL_RH(shared_hcd)) + usb_hcd_poll_rh_status(shared_hcd); + } + /* starting here, usbcore will pay attention to this root hub */ - retval = register_root_hub(hcd); - if (retval != 0) - goto err_register_root_hub; + if (!HCD_DEFER_RH_REGISTER(hcd)) { + retval = register_root_hub(hcd); + if (retval != 0) + goto err_register_root_hub; - if (hcd->uses_new_polling && HCD_POLL_RH(hcd)) - usb_hcd_poll_rh_status(hcd); + if (hcd->uses_new_polling && HCD_POLL_RH(hcd)) + usb_hcd_poll_rh_status(hcd); + } return retval; @@ -3020,6 +3034,7 @@ EXPORT_SYMBOL_GPL(usb_add_hcd); void usb_remove_hcd(struct usb_hcd *hcd) { struct usb_device *rhdev = hcd->self.root_hub; + bool rh_registered; dev_info(hcd->self.controller, "remove, state %x\n", hcd->state); @@ -3030,6 +3045,7 @@ void usb_remove_hcd(struct usb_hcd *hcd) dev_dbg(hcd->self.controller, "roothub graceful disconnect\n"); spin_lock_irq (&hcd_root_hub_lock); + rh_registered = hcd->rh_registered; hcd->rh_registered = 0; spin_unlock_irq (&hcd_root_hub_lock); @@ -3039,7 +3055,8 @@ void usb_remove_hcd(struct usb_hcd *hcd) cancel_work_sync(&hcd->died_work); mutex_lock(&usb_bus_idr_lock); - usb_disconnect(&rhdev); /* Sets rhdev to NULL */ + if (rh_registered) + usb_disconnect(&rhdev); /* Sets rhdev to NULL */ mutex_unlock(&usb_bus_idr_lock); /* diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 1460857026e0..68e9121c1878 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1635,7 +1635,7 @@ static int hub_configure(struct usb_hub *hub, * maxpktsize is defined in hcd.c's fake endpoint descriptors * to be big enough for at least USB_MAXCHILDREN ports. */ pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress); - maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe)); + maxp = usb_maxpacket(hdev, pipe); if (maxp > sizeof(*hub->buffer)) maxp = sizeof(*hub->buffer); @@ -5511,7 +5511,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, /* Handle notifying userspace about hub over-current events */ static void port_over_current_notify(struct usb_port *port_dev) { - char *envp[3]; + char *envp[3] = { NULL, NULL, NULL }; struct device *hub_dev; char *port_dev_path; @@ -5528,20 +5528,18 @@ static void port_over_current_notify(struct usb_port *port_dev) envp[0] = kasprintf(GFP_KERNEL, "OVER_CURRENT_PORT=%s", port_dev_path); if (!envp[0]) - goto exit_path; + goto exit; envp[1] = kasprintf(GFP_KERNEL, "OVER_CURRENT_COUNT=%u", port_dev->over_current_count); if (!envp[1]) goto exit; - envp[2] = NULL; kobject_uevent_env(&hub_dev->kobj, KOBJ_CHANGE, envp); - kfree(envp[1]); exit: + kfree(envp[1]); kfree(envp[0]); -exit_path: kfree(port_dev_path); } diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 97b44a68668a..f99a65a64588 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -510,6 +510,9 @@ static const struct usb_device_id usb_quirk_list[] = { /* DJI CineSSD */ { USB_DEVICE(0x2ca3, 0x0031), .driver_info = USB_QUIRK_NO_LPM }, + /* DELL USB GEN2 */ + { USB_DEVICE(0x413c, 0xb062), .driver_info = USB_QUIRK_NO_LPM | USB_QUIRK_RESET_RESUME }, + /* VCOM device */ { USB_DEVICE(0x4296, 0x7570), .driver_info = USB_QUIRK_CONFIG_INTF_STRINGS }, diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c index bb1da35eb891..d4dcaefd0ea4 100644 --- a/drivers/usb/core/usb-acpi.c +++ b/drivers/usb/core/usb-acpi.c @@ -205,8 +205,11 @@ usb_acpi_find_companion_for_device(struct usb_device *udev) struct usb_hub *hub; if (!udev->parent) { - /* root hub is only child (_ADR=0) under its parent, the HC */ - adev = ACPI_COMPANION(udev->dev.parent); + /* + * root hub is only child (_ADR=0) under its parent, the HC. + * sysdev pointer is the HC as seen from firmware. + */ + adev = ACPI_COMPANION(udev->bus->sysdev); return acpi_find_child_device(adev, 0, false); } diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index cf0bcd0dc320..dc4fc72ab1b6 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -1153,6 +1153,7 @@ static void dwc2_set_turnaround_time(struct dwc2_hsotg *hsotg) int dwc2_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) { u32 usbcfg; + u32 otgctl; int retval = 0; if ((hsotg->params.speed == DWC2_SPEED_PARAM_FULL || @@ -1187,6 +1188,14 @@ int dwc2_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) dwc2_writel(hsotg, usbcfg, GUSBCFG); } + if (!hsotg->params.activate_ingenic_overcurrent_detection) { + if (dwc2_is_host_mode(hsotg)) { + otgctl = readl(hsotg->regs + GOTGCTL); + otgctl |= GOTGCTL_VBVALOEN | GOTGCTL_VBVALOVAL; + writel(otgctl, hsotg->regs + GOTGCTL); + } + } + return retval; } diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 88c337bf564f..0683852e47e4 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -426,6 +426,10 @@ enum dwc2_ep0_state { * detection using GGPIO register. * 0 - Deactivate the external level detection (default) * 1 - Activate the external level detection + * @activate_ingenic_overcurrent_detection: Activate Ingenic overcurrent + * detection. + * 0 - Deactivate the overcurrent detection + * 1 - Activate the overcurrent detection (default) * @g_dma: Enables gadget dma usage (default: autodetect). * @g_dma_desc: Enables gadget descriptor DMA (default: autodetect). * @g_rx_fifo_size: The periodic rx fifo size for the device, in @@ -494,6 +498,7 @@ struct dwc2_core_params { u8 hird_threshold; bool activate_stm_fs_transceiver; bool activate_stm_id_vb_detection; + bool activate_ingenic_overcurrent_detection; bool ipg_isoc_en; u16 max_packet_count; u32 max_transfer_size; diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index eee3504397e6..fe2a58c75861 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -4544,7 +4544,6 @@ static int dwc2_hsotg_udc_start(struct usb_gadget *gadget, WARN_ON(hsotg->driver); - driver->driver.bus = NULL; hsotg->driver = driver; hsotg->gadget.dev.of_node = hsotg->dev->of_node; hsotg->gadget.speed = USB_SPEED_UNKNOWN; diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c index 1306f4ec788d..fdb8a42fff86 100644 --- a/drivers/usb/dwc2/params.c +++ b/drivers/usb/dwc2/params.c @@ -73,6 +73,47 @@ static void dwc2_set_his_params(struct dwc2_hsotg *hsotg) p->power_down = DWC2_POWER_DOWN_PARAM_NONE; } +static void dwc2_set_jz4775_params(struct dwc2_hsotg *hsotg) +{ + struct dwc2_core_params *p = &hsotg->params; + + p->otg_caps.hnp_support = false; + p->speed = DWC2_SPEED_PARAM_HIGH; + p->phy_type = DWC2_PHY_TYPE_PARAM_UTMI; + p->phy_utmi_width = 16; + p->activate_ingenic_overcurrent_detection = + !device_property_read_bool(hsotg->dev, "disable-over-current"); +} + +static void dwc2_set_x1600_params(struct dwc2_hsotg *hsotg) +{ + struct dwc2_core_params *p = &hsotg->params; + + p->otg_caps.hnp_support = false; + p->speed = DWC2_SPEED_PARAM_HIGH; + p->host_channels = 16; + p->phy_type = DWC2_PHY_TYPE_PARAM_UTMI; + p->phy_utmi_width = 16; + p->activate_ingenic_overcurrent_detection = + !device_property_read_bool(hsotg->dev, "disable-over-current"); +} + +static void dwc2_set_x2000_params(struct dwc2_hsotg *hsotg) +{ + struct dwc2_core_params *p = &hsotg->params; + + p->otg_caps.hnp_support = false; + p->speed = DWC2_SPEED_PARAM_HIGH; + p->host_rx_fifo_size = 1024; + p->host_nperio_tx_fifo_size = 1024; + p->host_perio_tx_fifo_size = 1024; + p->host_channels = 16; + p->phy_type = DWC2_PHY_TYPE_PARAM_UTMI; + p->phy_utmi_width = 16; + p->activate_ingenic_overcurrent_detection = + !device_property_read_bool(hsotg->dev, "disable-over-current"); +} + static void dwc2_set_s3c6400_params(struct dwc2_hsotg *hsotg) { struct dwc2_core_params *p = &hsotg->params; @@ -221,7 +262,14 @@ static void dwc2_set_stm32mp15_hsotg_params(struct dwc2_hsotg *hsotg) const struct of_device_id dwc2_of_match_table[] = { { .compatible = "brcm,bcm2835-usb", .data = dwc2_set_bcm_params }, - { .compatible = "hisilicon,hi6220-usb", .data = dwc2_set_his_params }, + { .compatible = "hisilicon,hi6220-usb", .data = dwc2_set_his_params }, + { .compatible = "ingenic,jz4775-otg", .data = dwc2_set_jz4775_params }, + { .compatible = "ingenic,jz4780-otg", .data = dwc2_set_jz4775_params }, + { .compatible = "ingenic,x1000-otg", .data = dwc2_set_jz4775_params }, + { .compatible = "ingenic,x1600-otg", .data = dwc2_set_x1600_params }, + { .compatible = "ingenic,x1700-otg", .data = dwc2_set_x1600_params }, + { .compatible = "ingenic,x1830-otg", .data = dwc2_set_x1600_params }, + { .compatible = "ingenic,x2000-otg", .data = dwc2_set_x2000_params }, { .compatible = "rockchip,rk3066-usb", .data = dwc2_set_rk_params }, { .compatible = "lantiq,arx100-usb", .data = dwc2_set_ltq_params }, { .compatible = "lantiq,xrx200-usb", .data = dwc2_set_ltq_params }, diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index c483f28b695d..cd9a734522a7 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -159,4 +159,13 @@ config USB_DWC3_XILINX This driver handles both ZynqMP and Versal SoC operations. Say 'Y' or 'M' if you have one such device. +config USB_DWC3_AM62 + tristate "Texas Instruments AM62 Platforms" + depends on ARCH_K3 || COMPILE_TEST + default USB_DWC3 + help + Support TI's AM62 platforms with DesignWare Core USB3 IP. + The Designware Core USB3 IP is progammed to operate in + in USB 2.0 mode only. + Say 'Y' or 'M' here if you have one such device endif diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 2d499de6f66a..9f66bd82b639 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -42,6 +42,7 @@ endif # and allyesconfig builds. ## +obj-$(CONFIG_USB_DWC3_AM62) += dwc3-am62.o obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.o obj-$(CONFIG_USB_DWC3_EXYNOS) += dwc3-exynos.o obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index d28cd1a6709b..e027c0420dc3 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -23,6 +23,7 @@ #include <linux/delay.h> #include <linux/dma-mapping.h> #include <linux/of.h> +#include <linux/of_graph.h> #include <linux/acpi.h> #include <linux/pinctrl/consumer.h> #include <linux/reset.h> @@ -85,7 +86,7 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc) * mode. If the controller supports DRD but the dr_mode is not * specified or set to OTG, then set the mode to peripheral. */ - if (mode == USB_DR_MODE_OTG && + if (mode == USB_DR_MODE_OTG && !dwc->edev && (!IS_ENABLED(CONFIG_USB_ROLE_SWITCH) || !device_property_read_bool(dwc->dev, "usb-role-switch")) && !DWC3_VER_IS_PRIOR(DWC3, 330A)) @@ -297,6 +298,7 @@ int dwc3_core_soft_reset(struct dwc3 *dwc) udelay(1); } while (--retries); + dev_warn(dwc->dev, "DWC3 controller soft reset failed.\n"); return -ETIMEDOUT; done: @@ -342,7 +344,6 @@ static void dwc3_frame_length_adjustment(struct dwc3 *dwc) * from the default, this will set clock period in DWC3_GUCTL * register. * @dwc: Pointer to our controller context structure - * @ref_clk_per: reference clock period in ns */ static void dwc3_ref_clk_period(struct dwc3 *dwc) { @@ -964,10 +965,8 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc) return; vals = kcalloc(ntype, sizeof(u32), GFP_KERNEL); - if (!vals) { - dev_err(dev, "Error to get memory\n"); + if (!vals) return; - } /* Get INCR burst type, and parse it */ ret = device_property_read_u32_array(dev, @@ -1268,40 +1267,36 @@ static int dwc3_core_get_phy(struct dwc3 *dwc) if (IS_ERR(dwc->usb2_phy)) { ret = PTR_ERR(dwc->usb2_phy); - if (ret == -ENXIO || ret == -ENODEV) { + if (ret == -ENXIO || ret == -ENODEV) dwc->usb2_phy = NULL; - } else { + else return dev_err_probe(dev, ret, "no usb2 phy configured\n"); - } } if (IS_ERR(dwc->usb3_phy)) { ret = PTR_ERR(dwc->usb3_phy); - if (ret == -ENXIO || ret == -ENODEV) { + if (ret == -ENXIO || ret == -ENODEV) dwc->usb3_phy = NULL; - } else { + else return dev_err_probe(dev, ret, "no usb3 phy configured\n"); - } } dwc->usb2_generic_phy = devm_phy_get(dev, "usb2-phy"); if (IS_ERR(dwc->usb2_generic_phy)) { ret = PTR_ERR(dwc->usb2_generic_phy); - if (ret == -ENOSYS || ret == -ENODEV) { + if (ret == -ENOSYS || ret == -ENODEV) dwc->usb2_generic_phy = NULL; - } else { + else return dev_err_probe(dev, ret, "no usb2 phy configured\n"); - } } dwc->usb3_generic_phy = devm_phy_get(dev, "usb3-phy"); if (IS_ERR(dwc->usb3_generic_phy)) { ret = PTR_ERR(dwc->usb3_generic_phy); - if (ret == -ENOSYS || ret == -ENODEV) { + if (ret == -ENOSYS || ret == -ENODEV) dwc->usb3_generic_phy = NULL; - } else { + else return dev_err_probe(dev, ret, "no usb3 phy configured\n"); - } } return 0; @@ -1633,6 +1628,51 @@ static void dwc3_check_params(struct dwc3 *dwc) } } +static struct extcon_dev *dwc3_get_extcon(struct dwc3 *dwc) +{ + struct device *dev = dwc->dev; + struct device_node *np_phy; + struct extcon_dev *edev = NULL; + const char *name; + + if (device_property_read_bool(dev, "extcon")) + return extcon_get_edev_by_phandle(dev, 0); + + /* + * Device tree platforms should get extcon via phandle. + * On ACPI platforms, we get the name from a device property. + * This device property is for kernel internal use only and + * is expected to be set by the glue code. + */ + if (device_property_read_string(dev, "linux,extcon-name", &name) == 0) { + edev = extcon_get_extcon_dev(name); + if (!edev) + return ERR_PTR(-EPROBE_DEFER); + + return edev; + } + + /* + * Try to get an extcon device from the USB PHY controller's "port" + * node. Check if it has the "port" node first, to avoid printing the + * error message from underlying code, as it's a valid case: extcon + * device (and "port" node) may be missing in case of "usb-role-switch" + * or OTG mode. + */ + np_phy = of_parse_phandle(dev->of_node, "phys", 0); + if (of_graph_is_present(np_phy)) { + struct device_node *np_conn; + + np_conn = of_graph_get_remote_node(np_phy, -1, -1); + if (np_conn) + edev = extcon_find_edev_by_node(np_conn); + of_node_put(np_conn); + } + of_node_put(np_phy); + + return edev; +} + static int dwc3_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1768,6 +1808,13 @@ static int dwc3_probe(struct platform_device *pdev) goto err2; } + dwc->edev = dwc3_get_extcon(dwc); + if (IS_ERR(dwc->edev)) { + ret = PTR_ERR(dwc->edev); + dev_err_probe(dwc->dev, ret, "failed to get extcon\n"); + goto err3; + } + ret = dwc3_get_dr_mode(dwc); if (ret) goto err3; diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 5c9d467195a6..81c486b3941c 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -1046,6 +1046,7 @@ struct dwc3_scratchpad_array { * @tx_thr_num_pkt_prd: periodic ESS transmit packet count * @tx_max_burst_prd: max periodic ESS transmit burst size * @tx_fifo_resize_max_num: max number of fifos allocated during txfifo resize + * @clear_stall_protocol: endpoint number that requires a delayed status phase * @hsphy_interface: "utmi" or "ulpi" * @connected: true when we're connected to a host, false otherwise * @softconnect: true when gadget connect is called, false when disconnect runs @@ -1266,6 +1267,7 @@ struct dwc3 { u8 tx_thr_num_pkt_prd; u8 tx_max_burst_prd; u8 tx_fifo_resize_max_num; + u8 clear_stall_protocol; const char *hsphy_interface; diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c index 8cad9e7d3368..039bf241769a 100644 --- a/drivers/usb/dwc3/drd.c +++ b/drivers/usb/dwc3/drd.c @@ -8,7 +8,6 @@ */ #include <linux/extcon.h> -#include <linux/of_graph.h> #include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/property.h> @@ -439,51 +438,6 @@ static int dwc3_drd_notifier(struct notifier_block *nb, return NOTIFY_DONE; } -static struct extcon_dev *dwc3_get_extcon(struct dwc3 *dwc) -{ - struct device *dev = dwc->dev; - struct device_node *np_phy; - struct extcon_dev *edev = NULL; - const char *name; - - if (device_property_read_bool(dev, "extcon")) - return extcon_get_edev_by_phandle(dev, 0); - - /* - * Device tree platforms should get extcon via phandle. - * On ACPI platforms, we get the name from a device property. - * This device property is for kernel internal use only and - * is expected to be set by the glue code. - */ - if (device_property_read_string(dev, "linux,extcon-name", &name) == 0) { - edev = extcon_get_extcon_dev(name); - if (!edev) - return ERR_PTR(-EPROBE_DEFER); - - return edev; - } - - /* - * Try to get an extcon device from the USB PHY controller's "port" - * node. Check if it has the "port" node first, to avoid printing the - * error message from underlying code, as it's a valid case: extcon - * device (and "port" node) may be missing in case of "usb-role-switch" - * or OTG mode. - */ - np_phy = of_parse_phandle(dev->of_node, "phys", 0); - if (of_graph_is_present(np_phy)) { - struct device_node *np_conn; - - np_conn = of_graph_get_remote_node(np_phy, -1, -1); - if (np_conn) - edev = extcon_find_edev_by_node(np_conn); - of_node_put(np_conn); - } - of_node_put(np_phy); - - return edev; -} - #if IS_ENABLED(CONFIG_USB_ROLE_SWITCH) #define ROLE_SWITCH 1 static int dwc3_usb_role_switch_set(struct usb_role_switch *sw, @@ -588,10 +542,6 @@ int dwc3_drd_init(struct dwc3 *dwc) device_property_read_bool(dwc->dev, "usb-role-switch")) return dwc3_setup_role_switch(dwc); - dwc->edev = dwc3_get_extcon(dwc); - if (IS_ERR(dwc->edev)) - return PTR_ERR(dwc->edev); - if (dwc->edev) { dwc->edev_nb.notifier_call = dwc3_drd_notifier; ret = extcon_register_notifier(dwc->edev, EXTCON_USB_HOST, diff --git a/drivers/usb/dwc3/dwc3-am62.c b/drivers/usb/dwc3/dwc3-am62.c new file mode 100644 index 000000000000..fea7aca35dc8 --- /dev/null +++ b/drivers/usb/dwc3/dwc3-am62.c @@ -0,0 +1,332 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * dwc3-am62.c - TI specific Glue layer for AM62 DWC3 USB Controller + * + * Copyright (C) 2022 Texas Instruments Incorporated - https://www.ti.com + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/mfd/syscon.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/pm_runtime.h> +#include <linux/clk.h> +#include <linux/regmap.h> +#include <linux/pinctrl/consumer.h> + +/* USB WRAPPER register offsets */ +#define USBSS_PID 0x0 +#define USBSS_OVERCURRENT_CTRL 0x4 +#define USBSS_PHY_CONFIG 0x8 +#define USBSS_PHY_TEST 0xc +#define USBSS_CORE_STAT 0x14 +#define USBSS_HOST_VBUS_CTRL 0x18 +#define USBSS_MODE_CONTROL 0x1c +#define USBSS_WAKEUP_CONFIG 0x30 +#define USBSS_WAKEUP_STAT 0x34 +#define USBSS_OVERRIDE_CONFIG 0x38 +#define USBSS_IRQ_MISC_STATUS_RAW 0x430 +#define USBSS_IRQ_MISC_STATUS 0x434 +#define USBSS_IRQ_MISC_ENABLE_SET 0x438 +#define USBSS_IRQ_MISC_ENABLE_CLR 0x43c +#define USBSS_IRQ_MISC_EOI 0x440 +#define USBSS_INTR_TEST 0x490 +#define USBSS_VBUS_FILTER 0x614 +#define USBSS_VBUS_STAT 0x618 +#define USBSS_DEBUG_CFG 0x708 +#define USBSS_DEBUG_DATA 0x70c +#define USBSS_HOST_HUB_CTRL 0x714 + +/* PHY CONFIG register bits */ +#define USBSS_PHY_VBUS_SEL_MASK GENMASK(2, 1) +#define USBSS_PHY_VBUS_SEL_SHIFT 1 +#define USBSS_PHY_LANE_REVERSE BIT(0) + +/* MODE CONTROL register bits */ +#define USBSS_MODE_VALID BIT(0) + +/* WAKEUP CONFIG register bits */ +#define USBSS_WAKEUP_CFG_OVERCURRENT_EN BIT(3) +#define USBSS_WAKEUP_CFG_LINESTATE_EN BIT(2) +#define USBSS_WAKEUP_CFG_SESSVALID_EN BIT(1) +#define USBSS_WAKEUP_CFG_VBUSVALID_EN BIT(0) + +/* WAKEUP STAT register bits */ +#define USBSS_WAKEUP_STAT_OVERCURRENT BIT(4) +#define USBSS_WAKEUP_STAT_LINESTATE BIT(3) +#define USBSS_WAKEUP_STAT_SESSVALID BIT(2) +#define USBSS_WAKEUP_STAT_VBUSVALID BIT(1) +#define USBSS_WAKEUP_STAT_CLR BIT(0) + +/* IRQ_MISC_STATUS_RAW register bits */ +#define USBSS_IRQ_MISC_RAW_VBUSVALID BIT(22) +#define USBSS_IRQ_MISC_RAW_SESSVALID BIT(20) + +/* IRQ_MISC_STATUS register bits */ +#define USBSS_IRQ_MISC_VBUSVALID BIT(22) +#define USBSS_IRQ_MISC_SESSVALID BIT(20) + +/* IRQ_MISC_ENABLE_SET register bits */ +#define USBSS_IRQ_MISC_ENABLE_SET_VBUSVALID BIT(22) +#define USBSS_IRQ_MISC_ENABLE_SET_SESSVALID BIT(20) + +/* IRQ_MISC_ENABLE_CLR register bits */ +#define USBSS_IRQ_MISC_ENABLE_CLR_VBUSVALID BIT(22) +#define USBSS_IRQ_MISC_ENABLE_CLR_SESSVALID BIT(20) + +/* IRQ_MISC_EOI register bits */ +#define USBSS_IRQ_MISC_EOI_VECTOR BIT(0) + +/* VBUS_STAT register bits */ +#define USBSS_VBUS_STAT_SESSVALID BIT(2) +#define USBSS_VBUS_STAT_VBUSVALID BIT(0) + +/* Mask for PHY PLL REFCLK */ +#define PHY_PLL_REFCLK_MASK GENMASK(3, 0) + +#define DWC3_AM62_AUTOSUSPEND_DELAY 100 + +struct dwc3_data { + struct device *dev; + void __iomem *usbss; + struct clk *usb2_refclk; + int rate_code; + struct regmap *syscon; + unsigned int offset; + unsigned int vbus_divider; +}; + +static const int dwc3_ti_rate_table[] = { /* in KHZ */ + 9600, + 10000, + 12000, + 19200, + 20000, + 24000, + 25000, + 26000, + 38400, + 40000, + 58000, + 50000, + 52000, +}; + +static inline u32 dwc3_ti_readl(struct dwc3_data *data, u32 offset) +{ + return readl((data->usbss) + offset); +} + +static inline void dwc3_ti_writel(struct dwc3_data *data, u32 offset, u32 value) +{ + writel(value, (data->usbss) + offset); +} + +static int phy_syscon_pll_refclk(struct dwc3_data *data) +{ + struct device *dev = data->dev; + struct device_node *node = dev->of_node; + struct of_phandle_args args; + struct regmap *syscon; + int ret; + + syscon = syscon_regmap_lookup_by_phandle(node, "ti,syscon-phy-pll-refclk"); + if (IS_ERR(syscon)) { + dev_err(dev, "unable to get ti,syscon-phy-pll-refclk regmap\n"); + return PTR_ERR(syscon); + } + + data->syscon = syscon; + + ret = of_parse_phandle_with_fixed_args(node, "ti,syscon-phy-pll-refclk", 1, + 0, &args); + if (ret) + return ret; + + data->offset = args.args[0]; + + ret = regmap_update_bits(data->syscon, data->offset, PHY_PLL_REFCLK_MASK, data->rate_code); + if (ret) { + dev_err(dev, "failed to set phy pll reference clock rate\n"); + return ret; + } + + return 0; +} + +static int dwc3_ti_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = pdev->dev.of_node; + struct dwc3_data *data; + int i, ret; + unsigned long rate; + u32 reg; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->dev = dev; + platform_set_drvdata(pdev, data); + + data->usbss = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(data->usbss)) { + dev_err(dev, "can't map IOMEM resource\n"); + return PTR_ERR(data->usbss); + } + + data->usb2_refclk = devm_clk_get(dev, "ref"); + if (IS_ERR(data->usb2_refclk)) { + dev_err(dev, "can't get usb2_refclk\n"); + return PTR_ERR(data->usb2_refclk); + } + + /* Calculate the rate code */ + rate = clk_get_rate(data->usb2_refclk); + rate /= 1000; // To KHz + for (i = 0; i < ARRAY_SIZE(dwc3_ti_rate_table); i++) { + if (dwc3_ti_rate_table[i] == rate) + break; + } + + if (i == ARRAY_SIZE(dwc3_ti_rate_table)) { + dev_err(dev, "unsupported usb2_refclk rate: %lu KHz\n", rate); + ret = -EINVAL; + goto err_clk_disable; + } + + data->rate_code = i; + + /* Read the syscon property and set the rate code */ + ret = phy_syscon_pll_refclk(data); + if (ret) + goto err_clk_disable; + + /* VBUS divider select */ + data->vbus_divider = device_property_read_bool(dev, "ti,vbus-divider"); + reg = dwc3_ti_readl(data, USBSS_PHY_CONFIG); + if (data->vbus_divider) + reg |= 1 << USBSS_PHY_VBUS_SEL_SHIFT; + + dwc3_ti_writel(data, USBSS_PHY_CONFIG, reg); + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + /* + * Don't ignore its dependencies with its children + */ + pm_suspend_ignore_children(dev, false); + clk_prepare_enable(data->usb2_refclk); + pm_runtime_get_noresume(dev); + + ret = of_platform_populate(node, NULL, NULL, dev); + if (ret) { + dev_err(dev, "failed to create dwc3 core: %d\n", ret); + goto err_pm_disable; + } + + /* Set mode valid bit to indicate role is valid */ + reg = dwc3_ti_readl(data, USBSS_MODE_CONTROL); + reg |= USBSS_MODE_VALID; + dwc3_ti_writel(data, USBSS_MODE_CONTROL, reg); + + /* Setting up autosuspend */ + pm_runtime_set_autosuspend_delay(dev, DWC3_AM62_AUTOSUSPEND_DELAY); + pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + return 0; + +err_pm_disable: + clk_disable_unprepare(data->usb2_refclk); + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); +err_clk_disable: + clk_put(data->usb2_refclk); + return ret; +} + +static int dwc3_ti_remove_core(struct device *dev, void *c) +{ + struct platform_device *pdev = to_platform_device(dev); + + platform_device_unregister(pdev); + return 0; +} + +static int dwc3_ti_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dwc3_data *data = platform_get_drvdata(pdev); + u32 reg; + + device_for_each_child(dev, NULL, dwc3_ti_remove_core); + + /* Clear mode valid bit */ + reg = dwc3_ti_readl(data, USBSS_MODE_CONTROL); + reg &= ~USBSS_MODE_VALID; + dwc3_ti_writel(data, USBSS_MODE_CONTROL, reg); + + pm_runtime_put_sync(dev); + clk_disable_unprepare(data->usb2_refclk); + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + + clk_put(data->usb2_refclk); + platform_set_drvdata(pdev, NULL); + return 0; +} + +#ifdef CONFIG_PM +static int dwc3_ti_suspend_common(struct device *dev) +{ + struct dwc3_data *data = dev_get_drvdata(dev); + + clk_disable_unprepare(data->usb2_refclk); + + return 0; +} + +static int dwc3_ti_resume_common(struct device *dev) +{ + struct dwc3_data *data = dev_get_drvdata(dev); + + clk_prepare_enable(data->usb2_refclk); + + return 0; +} + +static UNIVERSAL_DEV_PM_OPS(dwc3_ti_pm_ops, dwc3_ti_suspend_common, + dwc3_ti_resume_common, NULL); + +#define DEV_PM_OPS (&dwc3_ti_pm_ops) +#else +#define DEV_PM_OPS NULL +#endif /* CONFIG_PM */ + +static const struct of_device_id dwc3_ti_of_match[] = { + { .compatible = "ti,am62-usb"}, + {}, +}; +MODULE_DEVICE_TABLE(of, dwc3_ti_of_match); + +static struct platform_driver dwc3_ti_driver = { + .probe = dwc3_ti_probe, + .remove = dwc3_ti_remove, + .driver = { + .name = "dwc3-am62", + .pm = DEV_PM_OPS, + .of_match_table = dwc3_ti_of_match, + }, +}; + +module_platform_driver(dwc3_ti_driver); + +MODULE_ALIAS("platform:dwc3-am62"); +MODULE_AUTHOR("Aswath Govindraju <a-govindraju@ti.com>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("DesignWare USB3 TI Glue Layer"); diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 2e19e0e4ea53..ba51de7dd760 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -288,7 +288,7 @@ static void dwc3_pci_resume_work(struct work_struct *work) int ret; ret = pm_runtime_get_sync(&dwc3->dev); - if (ret) { + if (ret < 0) { pm_runtime_put_sync_autosuspend(&dwc3->dev); return; } diff --git a/drivers/usb/dwc3/dwc3-xilinx.c b/drivers/usb/dwc3/dwc3-xilinx.c index a6f3a9b38789..67b237c7a76a 100644 --- a/drivers/usb/dwc3/dwc3-xilinx.c +++ b/drivers/usb/dwc3/dwc3-xilinx.c @@ -13,6 +13,7 @@ #include <linux/of.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> +#include <linux/of_gpio.h> #include <linux/of_platform.h> #include <linux/pm_runtime.h> #include <linux/reset.h> @@ -98,6 +99,7 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data) { struct device *dev = priv_data->dev; struct reset_control *crst, *hibrst, *apbrst; + struct gpio_desc *reset_gpio; struct phy *usb3_phy; int ret = 0; u32 reg; @@ -201,6 +203,21 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data) } skip_usb3_phy: + /* ulpi reset via gpio-modepin or gpio-framework driver */ + reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(reset_gpio)) { + return dev_err_probe(dev, PTR_ERR(reset_gpio), + "Failed to request reset GPIO\n"); + } + + if (reset_gpio) { + /* Toggle ulpi to reset the phy. */ + gpiod_set_value_cansleep(reset_gpio, 1); + usleep_range(5000, 10000); + gpiod_set_value_cansleep(reset_gpio, 0); + usleep_range(5000, 10000); + } + /* * This routes the USB DMA traffic to go through FPD path instead * of reaching DDR directly. This traffic routing is needed to diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 1064be5518f6..5d642660fd15 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -218,7 +218,7 @@ out: return ret; } -static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc) +void dwc3_ep0_stall_and_restart(struct dwc3 *dwc) { struct dwc3_ep *dep; @@ -813,7 +813,7 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc, int ret = -EINVAL; u32 len; - if (!dwc->gadget_driver) + if (!dwc->gadget_driver || !dwc->connected) goto out; trace_dwc3_ctrl_req(ctrl); @@ -1080,6 +1080,7 @@ void dwc3_ep0_send_delayed_status(struct dwc3 *dwc) unsigned int direction = !dwc->ep0_expect_in; dwc->delayed_status = false; + dwc->clear_stall_protocol = 0; if (dwc->ep0state != EP0_STATUS_PHASE) return; @@ -1087,13 +1088,18 @@ void dwc3_ep0_send_delayed_status(struct dwc3 *dwc) __dwc3_ep0_do_control_status(dwc, dwc->eps[direction]); } -static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep) +void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep) { struct dwc3_gadget_ep_cmd_params params; u32 cmd; int ret; - if (!dep->resource_index) + /* + * For status/DATA OUT stage, TRB will be queued on ep0 out + * endpoint for which resource index is zero. Hence allow + * queuing ENDXFER command for ep0 out endpoint. + */ + if (!dep->resource_index && dep->number) return; cmd = DWC3_DEPCMD_ENDTRANSFER; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 0b9c2493844a..00427d108ab9 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -657,7 +657,6 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action) /** * dwc3_gadget_calc_tx_fifo_size - calculates the txfifo size value * @dwc: pointer to the DWC3 context - * @nfifos: number of fifos to calculate for * * Calculates the size value based on the equation below: * @@ -690,7 +689,7 @@ static int dwc3_gadget_calc_tx_fifo_size(struct dwc3 *dwc, int mult) } /** - * dwc3_gadget_clear_tx_fifo_size - Clears txfifo allocation + * dwc3_gadget_clear_tx_fifos - Clears txfifo allocation * @dwc: pointer to the DWC3 context * * Iterates through all the endpoint registers and clears the previous txfifo @@ -783,7 +782,8 @@ static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep) num_fifos = 3; if (dep->endpoint.maxburst > 6 && - usb_endpoint_xfer_bulk(dep->endpoint.desc) && DWC3_IP_IS(DWC31)) + (usb_endpoint_xfer_bulk(dep->endpoint.desc) || + usb_endpoint_xfer_isoc(dep->endpoint.desc)) && DWC3_IP_IS(DWC31)) num_fifos = dwc->tx_fifo_resize_max_num; /* FIFO size for a single buffer */ @@ -882,12 +882,13 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action) reg |= DWC3_DALEPENA_EP(dep->number); dwc3_writel(dwc->regs, DWC3_DALEPENA, reg); + dep->trb_dequeue = 0; + dep->trb_enqueue = 0; + if (usb_endpoint_xfer_control(desc)) goto out; /* Initialize the TRB ring */ - dep->trb_dequeue = 0; - dep->trb_enqueue = 0; memset(dep->trb_pool, 0, sizeof(struct dwc3_trb) * DWC3_TRB_NUM); @@ -2001,10 +2002,10 @@ static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep, struct dwc3_request *r static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep) { struct dwc3_request *req; - struct dwc3_request *tmp; struct dwc3 *dwc = dep->dwc; - list_for_each_entry_safe(req, tmp, &dep->cancelled_list, list) { + while (!list_empty(&dep->cancelled_list)) { + req = next_request(&dep->cancelled_list); dwc3_gadget_ep_skip_trbs(dep, req); switch (req->status) { case DWC3_REQUEST_STATUS_DISCONNECTED: @@ -2021,6 +2022,12 @@ static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep) dwc3_gadget_giveback(dep, req, -ECONNRESET); break; } + /* + * The endpoint is disabled, let the dwc3_remove_requests() + * handle the cleanup. + */ + if (!dep->endpoint.desc) + break; } } @@ -2056,16 +2063,6 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, if (r == req) { struct dwc3_request *t; - /* - * If a Setup packet is received but yet to DMA out, the controller will - * not process the End Transfer command of any endpoint. Polling of its - * DEPCMD.CmdAct may block setting up TRB for Setup packet, causing a - * timeout. Delay issuing the End Transfer command until the Setup TRB is - * prepared. - */ - if (dwc->ep0state != EP0_SETUP_PHASE && !dwc->delayed_status) - dep->flags |= DWC3_EP_DELAY_STOP; - /* wait until it is processed */ dwc3_stop_active_transfer(dep, true, true); @@ -2152,6 +2149,9 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol) if (dep->flags & DWC3_EP_END_TRANSFER_PENDING || (dep->flags & DWC3_EP_DELAY_STOP)) { dep->flags |= DWC3_EP_PENDING_CLEAR_STALL; + if (protocol) + dwc->clear_stall_protocol = dep->number; + return 0; } @@ -2498,28 +2498,64 @@ static void dwc3_gadget_disable_irq(struct dwc3 *dwc); static void __dwc3_gadget_stop(struct dwc3 *dwc); static int __dwc3_gadget_start(struct dwc3 *dwc); -static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) +static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc) { - struct dwc3 *dwc = gadget_to_dwc(g); - unsigned long flags; - int ret; + unsigned long flags; + + spin_lock_irqsave(&dwc->lock, flags); + dwc->connected = false; - is_on = !!is_on; - dwc->softconnect = is_on; /* * Per databook, when we want to stop the gadget, if a control transfer * is still in process, complete it and get the core into setup phase. */ - if (!is_on && dwc->ep0state != EP0_SETUP_PHASE) { + if (dwc->ep0state != EP0_SETUP_PHASE) { + int ret; + reinit_completion(&dwc->ep0_in_setup); + spin_unlock_irqrestore(&dwc->lock, flags); ret = wait_for_completion_timeout(&dwc->ep0_in_setup, msecs_to_jiffies(DWC3_PULL_UP_TIMEOUT)); + spin_lock_irqsave(&dwc->lock, flags); if (ret == 0) dev_warn(dwc->dev, "timed out waiting for SETUP phase\n"); } /* + * In the Synopsys DesignWare Cores USB3 Databook Rev. 3.30a + * Section 4.1.8 Table 4-7, it states that for a device-initiated + * disconnect, the SW needs to ensure that it sends "a DEPENDXFER + * command for any active transfers" before clearing the RunStop + * bit. + */ + dwc3_stop_active_transfers(dwc); + __dwc3_gadget_stop(dwc); + spin_unlock_irqrestore(&dwc->lock, flags); + + /* + * Note: if the GEVNTCOUNT indicates events in the event buffer, the + * driver needs to acknowledge them before the controller can halt. + * Simply let the interrupt handler acknowledges and handle the + * remaining event generated by the controller while polling for + * DSTS.DEVCTLHLT. + */ + return dwc3_gadget_run_stop(dwc, false, false); +} + +static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + int ret; + + is_on = !!is_on; + + if (dwc->pullups_connected == is_on) + return 0; + + dwc->softconnect = is_on; + + /* * Avoid issuing a runtime resume if the device is already in the * suspended state during gadget disconnect. DWC3 gadget was already * halted/stopped during runtime suspend. @@ -2541,42 +2577,8 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) return 0; } - /* - * Synchronize and disable any further event handling while controller - * is being enabled/disabled. - */ - disable_irq(dwc->irq_gadget); - - spin_lock_irqsave(&dwc->lock, flags); - if (!is_on) { - u32 count; - - dwc->connected = false; - /* - * In the Synopsis DesignWare Cores USB3 Databook Rev. 3.30a - * Section 4.1.8 Table 4-7, it states that for a device-initiated - * disconnect, the SW needs to ensure that it sends "a DEPENDXFER - * command for any active transfers" before clearing the RunStop - * bit. - */ - dwc3_stop_active_transfers(dwc); - __dwc3_gadget_stop(dwc); - - /* - * In the Synopsis DesignWare Cores USB3 Databook Rev. 3.30a - * Section 1.3.4, it mentions that for the DEVCTRLHLT bit, the - * "software needs to acknowledge the events that are generated - * (by writing to GEVNTCOUNTn) while it is waiting for this bit - * to be set to '1'." - */ - count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0)); - count &= DWC3_GEVNTCOUNT_MASK; - if (count > 0) { - dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), count); - dwc->ev_buf->lpos = (dwc->ev_buf->lpos + count) % - dwc->ev_buf->length; - } + ret = dwc3_gadget_soft_disconnect(dwc); } else { /* * In the Synopsys DWC_usb31 1.90a programming guide section @@ -2584,18 +2586,13 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) * device-initiated disconnect requires a core soft reset * (DCTL.CSftRst) before enabling the run/stop bit. */ - spin_unlock_irqrestore(&dwc->lock, flags); dwc3_core_soft_reset(dwc); - spin_lock_irqsave(&dwc->lock, flags); dwc3_event_buffers_setup(dwc); __dwc3_gadget_start(dwc); + ret = dwc3_gadget_run_stop(dwc, true, false); } - ret = dwc3_gadget_run_stop(dwc, is_on, false); - spin_unlock_irqrestore(&dwc->lock, flags); - enable_irq(dwc->irq_gadget); - pm_runtime_put(dwc->dev); return ret; @@ -2745,6 +2742,7 @@ static int __dwc3_gadget_start(struct dwc3 *dwc) /* begin to receive SETUP packets */ dwc->ep0state = EP0_SETUP_PHASE; + dwc->ep0_bounced = false; dwc->link_state = DWC3_LINK_STATE_SS_DIS; dwc->delayed_status = false; dwc3_ep0_out_start(dwc); @@ -3333,15 +3331,21 @@ static void dwc3_gadget_ep_cleanup_completed_requests(struct dwc3_ep *dep, const struct dwc3_event_depevt *event, int status) { struct dwc3_request *req; - struct dwc3_request *tmp; - list_for_each_entry_safe(req, tmp, &dep->started_list, list) { + while (!list_empty(&dep->started_list)) { int ret; + req = next_request(&dep->started_list); ret = dwc3_gadget_ep_cleanup_completed_request(dep, event, req, status); if (ret) break; + /* + * The endpoint is disabled, let the dwc3_remove_requests() + * handle the cleanup. + */ + if (!dep->endpoint.desc) + break; } } @@ -3380,14 +3384,14 @@ static bool dwc3_gadget_endpoint_trbs_complete(struct dwc3_ep *dep, struct dwc3 *dwc = dep->dwc; bool no_started_trb = true; - if (!dep->endpoint.desc) - return no_started_trb; - dwc3_gadget_ep_cleanup_completed_requests(dep, event, status); if (dep->flags & DWC3_EP_END_TRANSFER_PENDING) goto out; + if (!dep->endpoint.desc) + return no_started_trb; + if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && list_empty(&dep->started_list) && (list_empty(&dep->pending_list) || status == -EXDEV)) @@ -3512,7 +3516,7 @@ static void dwc3_gadget_endpoint_command_complete(struct dwc3_ep *dep, } dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE); - if (dwc->delayed_status) + if (dwc->clear_stall_protocol == dep->number) dwc3_ep0_send_delayed_status(dwc); } @@ -3673,12 +3677,35 @@ static void dwc3_reset_gadget(struct dwc3 *dwc) void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt) { + struct dwc3 *dwc = dep->dwc; + + /* + * Only issue End Transfer command to the control endpoint of a started + * Data Phase. Typically we should only do so in error cases such as + * invalid/unexpected direction as described in the control transfer + * flow of the programming guide. + */ + if (dep->number <= 1 && dwc->ep0state != EP0_DATA_PHASE) + return; + if (!(dep->flags & DWC3_EP_TRANSFER_STARTED) || (dep->flags & DWC3_EP_DELAY_STOP) || (dep->flags & DWC3_EP_END_TRANSFER_PENDING)) return; /* + * If a Setup packet is received but yet to DMA out, the controller will + * not process the End Transfer command of any endpoint. Polling of its + * DEPCMD.CmdAct may block setting up TRB for Setup packet, causing a + * timeout. Delay issuing the End Transfer command until the Setup TRB is + * prepared. + */ + if (dwc->ep0state != EP0_SETUP_PHASE && !dwc->delayed_status) { + dep->flags |= DWC3_EP_DELAY_STOP; + return; + } + + /* * NOTICE: We are violating what the Databook says about the * EndTransfer command. Ideally we would _always_ wait for the * EndTransfer Command Completion IRQ, but that's causing too @@ -3795,6 +3822,27 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) } dwc3_reset_gadget(dwc); + + /* + * From SNPS databook section 8.1.2, the EP0 should be in setup + * phase. So ensure that EP0 is in setup phase by issuing a stall + * and restart if EP0 is not in setup phase. + */ + if (dwc->ep0state != EP0_SETUP_PHASE) { + unsigned int dir; + + dir = !!dwc->ep0_expect_in; + if (dwc->ep0state == EP0_DATA_PHASE) + dwc3_ep0_end_control_data(dwc, dwc->eps[dir]); + else + dwc3_ep0_end_control_data(dwc, dwc->eps[!dir]); + + dwc->eps[0]->trb_enqueue = 0; + dwc->eps[1]->trb_enqueue = 0; + + dwc3_ep0_stall_and_restart(dwc); + } + /* * In the Synopsis DesignWare Cores USB3 Databook Rev. 3.30a * Section 4.1.2 Table 4-2, it states that during a USB reset, the SW diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index f763380e672e..55a56cf67d73 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -110,6 +110,8 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, void dwc3_ep0_interrupt(struct dwc3 *dwc, const struct dwc3_event_depevt *event); void dwc3_ep0_out_start(struct dwc3 *dwc); +void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep); +void dwc3_ep0_stall_and_restart(struct dwc3 *dwc); int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value); int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value); int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c index eda871973d6c..f56c30cf151e 100644 --- a/drivers/usb/dwc3/host.c +++ b/drivers/usb/dwc3/host.c @@ -7,7 +7,6 @@ * Authors: Felipe Balbi <balbi@ti.com>, */ -#include <linux/acpi.h> #include <linux/irq.h> #include <linux/of.h> #include <linux/platform_device.h> @@ -83,7 +82,6 @@ int dwc3_host_init(struct dwc3 *dwc) } xhci->dev.parent = dwc->dev; - ACPI_COMPANION_SET(&xhci->dev, ACPI_COMPANION(dwc->dev)); dwc->xhci = xhci; diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 2eaeaae96759..403563c06477 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -2505,7 +2505,7 @@ int usb_composite_probe(struct usb_composite_driver *driver) gadget_driver->driver.name = driver->name; gadget_driver->max_speed = driver->max_speed; - return usb_gadget_probe_driver(gadget_driver); + return usb_gadget_register_driver(gadget_driver); } EXPORT_SYMBOL_GPL(usb_composite_probe); diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 84b73cb03f87..3a6b4926193e 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -284,7 +284,7 @@ static ssize_t gadget_dev_desc_UDC_store(struct config_item *item, goto err; } gi->composite.gadget_driver.udc_name = name; - ret = usb_gadget_probe_driver(&gi->composite.gadget_driver); + ret = usb_gadget_register_driver(&gi->composite.gadget_driver); if (ret) { gi->composite.gadget_driver.udc_name = NULL; goto err; diff --git a/drivers/usb/gadget/function/f_acm.c b/drivers/usb/gadget/function/f_acm.c index 349945e064bb..411eb489e0ff 100644 --- a/drivers/usb/gadget/function/f_acm.c +++ b/drivers/usb/gadget/function/f_acm.c @@ -333,6 +333,8 @@ static void acm_complete_set_line_coding(struct usb_ep *ep, } } +static int acm_send_break(struct gserial *port, int duration); + static int acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) { struct f_acm *acm = func_to_acm(f); @@ -391,6 +393,14 @@ static int acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) acm->port_handshake_bits = w_value; break; + case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) + | USB_CDC_REQ_SEND_BREAK: + if (w_index != acm->ctrl_id) + goto invalid; + + acm_send_break(&acm->port, w_value); + break; + default: invalid: dev_vdbg(&cdev->gadget->dev, diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index d37965867b23..d3feeeb50841 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -24,7 +24,6 @@ #include <media/v4l2-dev.h> #include <media/v4l2-event.h> -#include "u_uvc.h" #include "uvc.h" #include "uvc_configfs.h" #include "uvc_v4l2.h" @@ -44,7 +43,7 @@ MODULE_PARM_DESC(trace, "Trace level bitmask"); #define UVC_STRING_STREAMING_IDX 1 static struct usb_string uvc_en_us_strings[] = { - [UVC_STRING_CONTROL_IDX].s = "UVC Camera", + /* [UVC_STRING_CONTROL_IDX].s = DYNAMIC, */ [UVC_STRING_STREAMING_IDX].s = "Video Streaming", { } }; @@ -676,6 +675,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) uvc_hs_streaming_ep.bEndpointAddress = uvc->video.ep->address; uvc_ss_streaming_ep.bEndpointAddress = uvc->video.ep->address; + uvc_en_us_strings[UVC_STRING_CONTROL_IDX].s = opts->function_name; us = usb_gstrings_attach(cdev, uvc_function_strings, ARRAY_SIZE(uvc_en_us_strings)); if (IS_ERR(us)) { @@ -866,6 +866,7 @@ static struct usb_function_instance *uvc_alloc_inst(void) opts->streaming_interval = 1; opts->streaming_maxpacket = 1024; + snprintf(opts->function_name, sizeof(opts->function_name), "UVC Camera"); ret = uvcg_attach_configfs(opts); if (ret < 0) { diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c index 2bb569895a90..c1f62e91b012 100644 --- a/drivers/usb/gadget/function/u_audio.c +++ b/drivers/usb/gadget/function/u_audio.c @@ -1179,8 +1179,8 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name, if (c_chmask) { struct uac_rtd_params *prm = &uac->c_prm; - spin_lock_init(&prm->lock); - uac->c_prm.uac = uac; + spin_lock_init(&prm->lock); + uac->c_prm.uac = uac; prm->max_psize = g_audio->out_ep_maxpsize; prm->srate = params->c_srates[0]; diff --git a/drivers/usb/gadget/function/u_uvc.h b/drivers/usb/gadget/function/u_uvc.h index 9a01a7d4f17f..24b8681b0d6f 100644 --- a/drivers/usb/gadget/function/u_uvc.h +++ b/drivers/usb/gadget/function/u_uvc.h @@ -27,6 +27,7 @@ struct f_uvc_opts { unsigned int control_interface; unsigned int streaming_interface; + char function_name[32]; /* * Control descriptors array pointers for full-/high-speed and diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index 886103a1fe9b..58e383afdd44 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -80,6 +80,7 @@ struct uvc_request { struct uvc_video *video; struct sg_table sgt; u8 header[UVCG_REQUEST_HEADER_LEN]; + struct uvc_buffer *last_buf; }; struct uvc_video { diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index 77d64031aa9c..e5a6b6e36b3d 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -10,17 +10,14 @@ * Author: Andrzej Pietrasiewicz <andrzejtp2010@gmail.com> */ -#include <linux/sort.h> - -#include "u_uvc.h" #include "uvc_configfs.h" +#include <linux/sort.h> + /* ----------------------------------------------------------------------------- * Global Utility Structures and Macros */ -#define UVCG_STREAMING_CONTROL_SIZE 1 - #define UVC_ATTR(prefix, cname, aname) \ static struct configfs_attribute prefix##attr_##cname = { \ .ca_name = __stringify(aname), \ @@ -49,12 +46,6 @@ static int uvcg_config_compare_u32(const void *l, const void *r) return li < ri ? -1 : li == ri ? 0 : 1; } -static inline struct f_uvc_opts *to_f_uvc_opts(struct config_item *item) -{ - return container_of(to_config_group(item), struct f_uvc_opts, - func_inst.group); -} - struct uvcg_config_group_type { struct config_item_type type; const char *name; @@ -125,19 +116,6 @@ static void uvcg_config_remove_children(struct config_group *group) * control/header */ -DECLARE_UVC_HEADER_DESCRIPTOR(1); - -struct uvcg_control_header { - struct config_item item; - struct UVC_HEADER_DESCRIPTOR(1) desc; - unsigned linked; -}; - -static struct uvcg_control_header *to_uvcg_control_header(struct config_item *item) -{ - return container_of(item, struct uvcg_control_header, item); -} - #define UVCG_CTRL_HDR_ATTR(cname, aname, bits, limit) \ static ssize_t uvcg_control_header_##cname##_show( \ struct config_item *item, char *page) \ @@ -769,24 +747,6 @@ static const char * const uvcg_format_names[] = { "mjpeg", }; -enum uvcg_format_type { - UVCG_UNCOMPRESSED = 0, - UVCG_MJPEG, -}; - -struct uvcg_format { - struct config_group group; - enum uvcg_format_type type; - unsigned linked; - unsigned num_frames; - __u8 bmaControls[UVCG_STREAMING_CONTROL_SIZE]; -}; - -static struct uvcg_format *to_uvcg_format(struct config_item *item) -{ - return container_of(to_config_group(item), struct uvcg_format, group); -} - static ssize_t uvcg_format_bma_controls_show(struct uvcg_format *f, char *page) { struct f_uvc_opts *opts; @@ -845,29 +805,11 @@ end: return ret; } -struct uvcg_format_ptr { - struct uvcg_format *fmt; - struct list_head entry; -}; - /* ----------------------------------------------------------------------------- * streaming/header/<NAME> * streaming/header */ -struct uvcg_streaming_header { - struct config_item item; - struct uvc_input_header_descriptor desc; - unsigned linked; - struct list_head formats; - unsigned num_fmt; -}; - -static struct uvcg_streaming_header *to_uvcg_streaming_header(struct config_item *item) -{ - return container_of(item, struct uvcg_streaming_header, item); -} - static void uvcg_format_set_indices(struct config_group *fmt); static int uvcg_streaming_header_allow_link(struct config_item *src, @@ -1059,31 +1001,6 @@ static const struct uvcg_config_group_type uvcg_streaming_header_grp_type = { * streaming/<mode>/<format>/<NAME> */ -struct uvcg_frame { - struct config_item item; - enum uvcg_format_type fmt_type; - struct { - u8 b_length; - u8 b_descriptor_type; - u8 b_descriptor_subtype; - u8 b_frame_index; - u8 bm_capabilities; - u16 w_width; - u16 w_height; - u32 dw_min_bit_rate; - u32 dw_max_bit_rate; - u32 dw_max_video_frame_buffer_size; - u32 dw_default_frame_interval; - u8 b_frame_interval_type; - } __attribute__((packed)) frame; - u32 *dw_frame_interval; -}; - -static struct uvcg_frame *to_uvcg_frame(struct config_item *item) -{ - return container_of(item, struct uvcg_frame, item); -} - #define UVCG_FRAME_ATTR(cname, aname, bits) \ static ssize_t uvcg_frame_##cname##_show(struct config_item *item, char *page)\ { \ @@ -1345,6 +1262,7 @@ static struct config_item *uvcg_frame_make(struct config_group *group, struct uvcg_format *fmt; struct f_uvc_opts *opts; struct config_item *opts_item; + struct uvcg_frame_ptr *frame_ptr; h = kzalloc(sizeof(*h), GFP_KERNEL); if (!h) @@ -1375,6 +1293,16 @@ static struct config_item *uvcg_frame_make(struct config_group *group, kfree(h); return ERR_PTR(-EINVAL); } + + frame_ptr = kzalloc(sizeof(*frame_ptr), GFP_KERNEL); + if (!frame_ptr) { + mutex_unlock(&opts->lock); + kfree(h); + return ERR_PTR(-ENOMEM); + } + + frame_ptr->frm = h; + list_add_tail(&frame_ptr->entry, &fmt->frames); ++fmt->num_frames; mutex_unlock(&opts->lock); @@ -1388,13 +1316,23 @@ static void uvcg_frame_drop(struct config_group *group, struct config_item *item struct uvcg_format *fmt; struct f_uvc_opts *opts; struct config_item *opts_item; + struct uvcg_frame *target_frm = NULL; + struct uvcg_frame_ptr *frame_ptr, *tmp; opts_item = group->cg_item.ci_parent->ci_parent->ci_parent; opts = to_f_uvc_opts(opts_item); mutex_lock(&opts->lock); + target_frm = container_of(item, struct uvcg_frame, item); fmt = to_uvcg_format(&group->cg_item); - --fmt->num_frames; + + list_for_each_entry_safe(frame_ptr, tmp, &fmt->frames, entry) + if (frame_ptr->frm == target_frm) { + list_del(&frame_ptr->entry); + kfree(frame_ptr); + --fmt->num_frames; + break; + } mutex_unlock(&opts->lock); config_item_put(item); @@ -1420,18 +1358,6 @@ static void uvcg_format_set_indices(struct config_group *fmt) * streaming/uncompressed/<NAME> */ -struct uvcg_uncompressed { - struct uvcg_format fmt; - struct uvc_format_uncompressed desc; -}; - -static struct uvcg_uncompressed *to_uvcg_uncompressed(struct config_item *item) -{ - return container_of( - container_of(to_config_group(item), struct uvcg_format, group), - struct uvcg_uncompressed, fmt); -} - static struct configfs_group_operations uvcg_uncompressed_group_ops = { .make_item = uvcg_frame_make, .drop_item = uvcg_frame_drop, @@ -1565,6 +1491,12 @@ uvcg_uncompressed_##cname##_store(struct config_item *item, \ if (ret) \ goto end; \ \ + /* index values in uvc are never 0 */ \ + if (!num) { \ + ret = -EINVAL; \ + goto end; \ + } \ + \ u->desc.aname = num; \ ret = len; \ end: \ @@ -1645,6 +1577,7 @@ static struct config_group *uvcg_uncompressed_make(struct config_group *group, h->desc.bmInterfaceFlags = 0; h->desc.bCopyProtect = 0; + INIT_LIST_HEAD(&h->fmt.frames); h->fmt.type = UVCG_UNCOMPRESSED; config_group_init_type_name(&h->fmt.group, name, &uvcg_uncompressed_type); @@ -1669,18 +1602,6 @@ static const struct uvcg_config_group_type uvcg_uncompressed_grp_type = { * streaming/mjpeg/<NAME> */ -struct uvcg_mjpeg { - struct uvcg_format fmt; - struct uvc_format_mjpeg desc; -}; - -static struct uvcg_mjpeg *to_uvcg_mjpeg(struct config_item *item) -{ - return container_of( - container_of(to_config_group(item), struct uvcg_format, group), - struct uvcg_mjpeg, fmt); -} - static struct configfs_group_operations uvcg_mjpeg_group_ops = { .make_item = uvcg_frame_make, .drop_item = uvcg_frame_drop, @@ -1758,6 +1679,12 @@ uvcg_mjpeg_##cname##_store(struct config_item *item, \ if (ret) \ goto end; \ \ + /* index values in uvc are never 0 */ \ + if (!num) { \ + ret = -EINVAL; \ + goto end; \ + } \ + \ u->desc.aname = num; \ ret = len; \ end: \ @@ -1831,6 +1758,7 @@ static struct config_group *uvcg_mjpeg_make(struct config_group *group, h->desc.bmInterfaceFlags = 0; h->desc.bCopyProtect = 0; + INIT_LIST_HEAD(&h->fmt.frames); h->fmt.type = UVCG_MJPEG; config_group_init_type_name(&h->fmt.group, name, &uvcg_mjpeg_type); @@ -2425,10 +2353,51 @@ UVCG_OPTS_ATTR(streaming_maxburst, streaming_maxburst, 15); #undef UVCG_OPTS_ATTR +#define UVCG_OPTS_STRING_ATTR(cname, aname) \ +static ssize_t f_uvc_opts_string_##cname##_show(struct config_item *item,\ + char *page) \ +{ \ + struct f_uvc_opts *opts = to_f_uvc_opts(item); \ + int result; \ + \ + mutex_lock(&opts->lock); \ + result = snprintf(page, sizeof(opts->aname), "%s", opts->aname);\ + mutex_unlock(&opts->lock); \ + \ + return result; \ +} \ + \ +static ssize_t f_uvc_opts_string_##cname##_store(struct config_item *item,\ + const char *page, size_t len) \ +{ \ + struct f_uvc_opts *opts = to_f_uvc_opts(item); \ + int ret = 0; \ + \ + mutex_lock(&opts->lock); \ + if (opts->refcnt) { \ + ret = -EBUSY; \ + goto end; \ + } \ + \ + ret = snprintf(opts->aname, min(sizeof(opts->aname), len), \ + "%s", page); \ + \ +end: \ + mutex_unlock(&opts->lock); \ + return ret; \ +} \ + \ +UVC_ATTR(f_uvc_opts_string_, cname, aname) + +UVCG_OPTS_STRING_ATTR(function_name, function_name); + +#undef UVCG_OPTS_STRING_ATTR + static struct configfs_attribute *uvc_attrs[] = { &f_uvc_opts_attr_streaming_interval, &f_uvc_opts_attr_streaming_maxpacket, &f_uvc_opts_attr_streaming_maxburst, + &f_uvc_opts_string_attr_function_name, NULL, }; diff --git a/drivers/usb/gadget/function/uvc_configfs.h b/drivers/usb/gadget/function/uvc_configfs.h index 7e1d7ca29bf2..ad2ec8c4c78c 100644 --- a/drivers/usb/gadget/function/uvc_configfs.h +++ b/drivers/usb/gadget/function/uvc_configfs.h @@ -12,7 +12,125 @@ #ifndef UVC_CONFIGFS_H #define UVC_CONFIGFS_H -struct f_uvc_opts; +#include <linux/configfs.h> + +#include "u_uvc.h" + +static inline struct f_uvc_opts *to_f_uvc_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_uvc_opts, + func_inst.group); +} + +#define UVCG_STREAMING_CONTROL_SIZE 1 + +DECLARE_UVC_HEADER_DESCRIPTOR(1); + +struct uvcg_control_header { + struct config_item item; + struct UVC_HEADER_DESCRIPTOR(1) desc; + unsigned linked; +}; + +static inline struct uvcg_control_header *to_uvcg_control_header(struct config_item *item) +{ + return container_of(item, struct uvcg_control_header, item); +} + +enum uvcg_format_type { + UVCG_UNCOMPRESSED = 0, + UVCG_MJPEG, +}; + +struct uvcg_format { + struct config_group group; + enum uvcg_format_type type; + unsigned linked; + struct list_head frames; + unsigned num_frames; + __u8 bmaControls[UVCG_STREAMING_CONTROL_SIZE]; +}; + +struct uvcg_format_ptr { + struct uvcg_format *fmt; + struct list_head entry; +}; + +static inline struct uvcg_format *to_uvcg_format(struct config_item *item) +{ + return container_of(to_config_group(item), struct uvcg_format, group); +} + +struct uvcg_streaming_header { + struct config_item item; + struct uvc_input_header_descriptor desc; + unsigned linked; + struct list_head formats; + unsigned num_fmt; +}; + +static inline struct uvcg_streaming_header *to_uvcg_streaming_header(struct config_item *item) +{ + return container_of(item, struct uvcg_streaming_header, item); +} + +struct uvcg_frame_ptr { + struct uvcg_frame *frm; + struct list_head entry; +}; + +struct uvcg_frame { + struct config_item item; + enum uvcg_format_type fmt_type; + struct { + u8 b_length; + u8 b_descriptor_type; + u8 b_descriptor_subtype; + u8 b_frame_index; + u8 bm_capabilities; + u16 w_width; + u16 w_height; + u32 dw_min_bit_rate; + u32 dw_max_bit_rate; + u32 dw_max_video_frame_buffer_size; + u32 dw_default_frame_interval; + u8 b_frame_interval_type; + } __attribute__((packed)) frame; + u32 *dw_frame_interval; +}; + +static inline struct uvcg_frame *to_uvcg_frame(struct config_item *item) +{ + return container_of(item, struct uvcg_frame, item); +} + +/* ----------------------------------------------------------------------------- + * streaming/uncompressed/<NAME> + */ + +struct uvcg_uncompressed { + struct uvcg_format fmt; + struct uvc_format_uncompressed desc; +}; + +static inline struct uvcg_uncompressed *to_uvcg_uncompressed(struct config_item *item) +{ + return container_of(to_uvcg_format(item), struct uvcg_uncompressed, fmt); +} + +/* ----------------------------------------------------------------------------- + * streaming/mjpeg/<NAME> + */ + +struct uvcg_mjpeg { + struct uvcg_format fmt; + struct uvc_format_mjpeg desc; +}; + +static inline struct uvcg_mjpeg *to_uvcg_mjpeg(struct config_item *item) +{ + return container_of(to_uvcg_format(item), struct uvcg_mjpeg, fmt); +} int uvcg_attach_configfs(struct f_uvc_opts *opts); diff --git a/drivers/usb/gadget/function/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c index 2cda982f3765..d25edc3d2174 100644 --- a/drivers/usb/gadget/function/uvc_queue.c +++ b/drivers/usb/gadget/function/uvc_queue.c @@ -185,18 +185,7 @@ int uvcg_query_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf) int uvcg_queue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf) { - unsigned long flags; - int ret; - - ret = vb2_qbuf(&queue->queue, NULL, buf); - if (ret < 0) - return ret; - - spin_lock_irqsave(&queue->irqlock, flags); - ret = (queue->flags & UVC_QUEUE_PAUSED) != 0; - queue->flags &= ~UVC_QUEUE_PAUSED; - spin_unlock_irqrestore(&queue->irqlock, flags); - return ret; + return vb2_qbuf(&queue->queue, NULL, buf); } /* @@ -328,33 +317,22 @@ int uvcg_queue_enable(struct uvc_video_queue *queue, int enable) } /* called with &queue_irqlock held.. */ -struct uvc_buffer *uvcg_queue_next_buffer(struct uvc_video_queue *queue, +void uvcg_complete_buffer(struct uvc_video_queue *queue, struct uvc_buffer *buf) { - struct uvc_buffer *nextbuf; - if ((queue->flags & UVC_QUEUE_DROP_INCOMPLETE) && buf->length != buf->bytesused) { buf->state = UVC_BUF_STATE_QUEUED; vb2_set_plane_payload(&buf->buf.vb2_buf, 0, 0); - return buf; + return; } - list_del(&buf->queue); - if (!list_empty(&queue->irqqueue)) - nextbuf = list_first_entry(&queue->irqqueue, struct uvc_buffer, - queue); - else - nextbuf = NULL; - buf->buf.field = V4L2_FIELD_NONE; buf->buf.sequence = queue->sequence++; buf->buf.vb2_buf.timestamp = ktime_get_ns(); vb2_set_plane_payload(&buf->buf.vb2_buf, 0, buf->bytesused); vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_DONE); - - return nextbuf; } struct uvc_buffer *uvcg_queue_head(struct uvc_video_queue *queue) @@ -364,8 +342,6 @@ struct uvc_buffer *uvcg_queue_head(struct uvc_video_queue *queue) if (!list_empty(&queue->irqqueue)) buf = list_first_entry(&queue->irqqueue, struct uvc_buffer, queue); - else - queue->flags |= UVC_QUEUE_PAUSED; return buf; } diff --git a/drivers/usb/gadget/function/uvc_queue.h b/drivers/usb/gadget/function/uvc_queue.h index 05360a0767f6..41f87b917f6b 100644 --- a/drivers/usb/gadget/function/uvc_queue.h +++ b/drivers/usb/gadget/function/uvc_queue.h @@ -43,7 +43,6 @@ struct uvc_buffer { #define UVC_QUEUE_DISCONNECTED (1 << 0) #define UVC_QUEUE_DROP_INCOMPLETE (1 << 1) -#define UVC_QUEUE_PAUSED (1 << 2) struct uvc_video_queue { struct vb2_queue queue; @@ -93,7 +92,7 @@ void uvcg_queue_cancel(struct uvc_video_queue *queue, int disconnect); int uvcg_queue_enable(struct uvc_video_queue *queue, int enable); -struct uvc_buffer *uvcg_queue_next_buffer(struct uvc_video_queue *queue, +void uvcg_complete_buffer(struct uvc_video_queue *queue, struct uvc_buffer *buf); struct uvc_buffer *uvcg_queue_head(struct uvc_video_queue *queue); diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index 7f59a0c47402..a9bb4553db84 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -112,7 +112,8 @@ uvc_video_encode_bulk(struct usb_request *req, struct uvc_video *video, if (buf->bytesused == video->queue.buf_used) { video->queue.buf_used = 0; buf->state = UVC_BUF_STATE_DONE; - uvcg_queue_next_buffer(&video->queue, buf); + list_del(&buf->queue); + uvcg_complete_buffer(&video->queue, buf); video->fid ^= UVC_STREAM_FID; video->payload_size = 0; @@ -154,7 +155,7 @@ uvc_video_encode_isoc_sg(struct usb_request *req, struct uvc_video *video, sg = sg_next(sg); for_each_sg(sg, iter, ureq->sgt.nents - 1, i) { - if (!len || !buf->sg) + if (!len || !buf->sg || !sg_dma_len(buf->sg)) break; sg_left = sg_dma_len(buf->sg) - buf->offset; @@ -183,8 +184,9 @@ uvc_video_encode_isoc_sg(struct usb_request *req, struct uvc_video *video, video->queue.buf_used = 0; buf->state = UVC_BUF_STATE_DONE; buf->offset = 0; - uvcg_queue_next_buffer(&video->queue, buf); + list_del(&buf->queue); video->fid ^= UVC_STREAM_FID; + ureq->last_buf = buf; } } @@ -210,7 +212,8 @@ uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video, if (buf->bytesused == video->queue.buf_used) { video->queue.buf_used = 0; buf->state = UVC_BUF_STATE_DONE; - uvcg_queue_next_buffer(&video->queue, buf); + list_del(&buf->queue); + uvcg_complete_buffer(&video->queue, buf); video->fid ^= UVC_STREAM_FID; } } @@ -264,6 +267,11 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req) uvcg_queue_cancel(queue, 0); } + if (ureq->last_buf) { + uvcg_complete_buffer(&video->queue, ureq->last_buf); + ureq->last_buf = NULL; + } + spin_lock_irqsave(&video->req_lock, flags); list_add_tail(&req->list, &video->req_free); spin_unlock_irqrestore(&video->req_lock, flags); @@ -332,6 +340,7 @@ uvc_video_alloc_requests(struct uvc_video *video) video->ureq[i].req->complete = uvc_video_complete; video->ureq[i].req->context = &video->ureq[i]; video->ureq[i].video = video; + video->ureq[i].last_buf = NULL; list_add_tail(&video->ureq[i].req->list, &video->req_free); /* req_size/PAGE_SIZE + 1 for overruns and + 1 for header */ diff --git a/drivers/usb/gadget/legacy/dbgp.c b/drivers/usb/gadget/legacy/dbgp.c index 6bcbad382580..b62e45235e8e 100644 --- a/drivers/usb/gadget/legacy/dbgp.c +++ b/drivers/usb/gadget/legacy/dbgp.c @@ -422,7 +422,7 @@ static struct usb_gadget_driver dbgp_driver = { static int __init dbgp_init(void) { - return usb_gadget_probe_driver(&dbgp_driver); + return usb_gadget_register_driver(&dbgp_driver); } static void __exit dbgp_exit(void) diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index 0c01e749f9ea..79990597c39f 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -1873,7 +1873,7 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) else gadgetfs_driver.max_speed = USB_SPEED_FULL; - value = usb_gadget_probe_driver(&gadgetfs_driver); + value = usb_gadget_register_driver(&gadgetfs_driver); if (value != 0) { spin_lock_irq(&dev->lock); goto fail; diff --git a/drivers/usb/gadget/legacy/raw_gadget.c b/drivers/usb/gadget/legacy/raw_gadget.c index e9440f7bf019..241740024c50 100644 --- a/drivers/usb/gadget/legacy/raw_gadget.c +++ b/drivers/usb/gadget/legacy/raw_gadget.c @@ -512,12 +512,12 @@ static int raw_ioctl_run(struct raw_dev *dev, unsigned long value) dev->state = STATE_DEV_REGISTERING; spin_unlock_irqrestore(&dev->lock, flags); - ret = usb_gadget_probe_driver(&dev->driver); + ret = usb_gadget_register_driver(&dev->driver); spin_lock_irqsave(&dev->lock, flags); if (ret) { dev_err(dev->dev, - "fail, usb_gadget_probe_driver returned %d\n", ret); + "fail, usb_gadget_register_driver returned %d\n", ret); dev->state = STATE_DEV_FAILED; goto out_unlock; } diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index 85b194011a16..7886497253cc 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -12,6 +12,7 @@ #include <linux/module.h> #include <linux/device.h> #include <linux/list.h> +#include <linux/idr.h> #include <linux/err.h> #include <linux/dma-mapping.h> #include <linux/sched/task_stack.h> @@ -23,6 +24,10 @@ #include "trace.h" +static DEFINE_IDA(gadget_id_numbers); + +static struct bus_type gadget_bus_type; + /** * struct usb_udc - describes one usb device controller * @driver: the gadget driver pointer. For use by the class code @@ -47,11 +52,9 @@ struct usb_udc { static struct class *udc_class; static LIST_HEAD(udc_list); -static LIST_HEAD(gadget_driver_pending_list); -static DEFINE_MUTEX(udc_lock); -static int udc_bind_to_driver(struct usb_udc *udc, - struct usb_gadget_driver *driver); +/* Protects udc_list, udc->driver, driver->is_bound, and related calls */ +static DEFINE_MUTEX(udc_lock); /* ------------------------------------------------------------------------- */ @@ -1238,38 +1241,16 @@ static void usb_udc_nop_release(struct device *dev) dev_vdbg(dev, "%s\n", __func__); } -/* should be called with udc_lock held */ -static int check_pending_gadget_drivers(struct usb_udc *udc) -{ - struct usb_gadget_driver *driver; - int ret = 0; - - list_for_each_entry(driver, &gadget_driver_pending_list, pending) - if (!driver->udc_name || strcmp(driver->udc_name, - dev_name(&udc->dev)) == 0) { - ret = udc_bind_to_driver(udc, driver); - if (ret != -EPROBE_DEFER) - list_del_init(&driver->pending); - break; - } - - return ret; -} - /** * usb_initialize_gadget - initialize a gadget and its embedded struct device * @parent: the parent device to this udc. Usually the controller driver's * device. * @gadget: the gadget to be initialized. * @release: a gadget release function. - * - * Returns zero on success, negative errno otherwise. - * Calls the gadget release function in the latter case. */ void usb_initialize_gadget(struct device *parent, struct usb_gadget *gadget, void (*release)(struct device *dev)) { - dev_set_name(&gadget->dev, "gadget"); INIT_WORK(&gadget->work, usb_gadget_state_work); gadget->dev.parent = parent; @@ -1279,6 +1260,7 @@ void usb_initialize_gadget(struct device *parent, struct usb_gadget *gadget, gadget->dev.release = usb_udc_nop_release; device_initialize(&gadget->dev); + gadget->dev.bus = &gadget_bus_type; } EXPORT_SYMBOL_GPL(usb_initialize_gadget); @@ -1308,10 +1290,6 @@ int usb_add_gadget(struct usb_gadget *gadget) if (ret) goto err_put_udc; - ret = device_add(&gadget->dev); - if (ret) - goto err_put_udc; - udc->gadget = gadget; gadget->udc = udc; @@ -1319,6 +1297,7 @@ int usb_add_gadget(struct usb_gadget *gadget) mutex_lock(&udc_lock); list_add_tail(&udc->list, &udc_list); + mutex_unlock(&udc_lock); ret = device_add(&udc->dev); if (ret) @@ -1327,25 +1306,30 @@ int usb_add_gadget(struct usb_gadget *gadget) usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED); udc->vbus = true; - /* pick up one of pending gadget drivers */ - ret = check_pending_gadget_drivers(udc); - if (ret) + ret = ida_alloc(&gadget_id_numbers, GFP_KERNEL); + if (ret < 0) goto err_del_udc; + gadget->id_number = ret; + dev_set_name(&gadget->dev, "gadget.%d", ret); - mutex_unlock(&udc_lock); + ret = device_add(&gadget->dev); + if (ret) + goto err_free_id; return 0; + err_free_id: + ida_free(&gadget_id_numbers, gadget->id_number); + err_del_udc: flush_work(&gadget->work); device_del(&udc->dev); err_unlist_udc: + mutex_lock(&udc_lock); list_del(&udc->list); mutex_unlock(&udc_lock); - device_del(&gadget->dev); - err_put_udc: put_device(&udc->dev); @@ -1421,30 +1405,11 @@ int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) } EXPORT_SYMBOL_GPL(usb_add_gadget_udc); -static void usb_gadget_remove_driver(struct usb_udc *udc) -{ - dev_dbg(&udc->dev, "unregistering UDC driver [%s]\n", - udc->driver->function); - - kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE); - - usb_gadget_disconnect(udc->gadget); - usb_gadget_disable_async_callbacks(udc); - if (udc->gadget->irq) - synchronize_irq(udc->gadget->irq); - udc->driver->unbind(udc->gadget); - usb_gadget_udc_stop(udc); - - udc->driver = NULL; - udc->gadget->dev.driver = NULL; -} - /** - * usb_del_gadget - deletes @udc from udc_list - * @gadget: the gadget to be removed. + * usb_del_gadget - deletes a gadget and unregisters its udc + * @gadget: the gadget to be deleted. * - * This will call usb_gadget_unregister_driver() if - * the @udc is still busy. + * This will unbind @gadget, if it is bound. * It will not do a final usb_put_gadget(). */ void usb_del_gadget(struct usb_gadget *gadget) @@ -1458,25 +1423,19 @@ void usb_del_gadget(struct usb_gadget *gadget) mutex_lock(&udc_lock); list_del(&udc->list); - - if (udc->driver) { - struct usb_gadget_driver *driver = udc->driver; - - usb_gadget_remove_driver(udc); - list_add(&driver->pending, &gadget_driver_pending_list); - } mutex_unlock(&udc_lock); kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE); flush_work(&gadget->work); - device_unregister(&udc->dev); device_del(&gadget->dev); + ida_free(&gadget_id_numbers, gadget->id_number); + device_unregister(&udc->dev); } EXPORT_SYMBOL_GPL(usb_del_gadget); /** - * usb_del_gadget_udc - deletes @udc from udc_list - * @gadget: the gadget to be removed. + * usb_del_gadget_udc - unregisters a gadget + * @gadget: the gadget to be unregistered. * * Calls usb_del_gadget() and does a final usb_put_gadget(). */ @@ -1489,123 +1448,147 @@ EXPORT_SYMBOL_GPL(usb_del_gadget_udc); /* ------------------------------------------------------------------------- */ -static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *driver) +static int gadget_match_driver(struct device *dev, struct device_driver *drv) { - int ret; + struct usb_gadget *gadget = dev_to_usb_gadget(dev); + struct usb_udc *udc = gadget->udc; + struct usb_gadget_driver *driver = container_of(drv, + struct usb_gadget_driver, driver); - dev_dbg(&udc->dev, "registering UDC driver [%s]\n", - driver->function); + /* If the driver specifies a udc_name, it must match the UDC's name */ + if (driver->udc_name && + strcmp(driver->udc_name, dev_name(&udc->dev)) != 0) + return 0; + + /* If the driver is already bound to a gadget, it doesn't match */ + if (driver->is_bound) + return 0; + + /* Otherwise any gadget driver matches any UDC */ + return 1; +} +static int gadget_bind_driver(struct device *dev) +{ + struct usb_gadget *gadget = dev_to_usb_gadget(dev); + struct usb_udc *udc = gadget->udc; + struct usb_gadget_driver *driver = container_of(dev->driver, + struct usb_gadget_driver, driver); + int ret = 0; + + mutex_lock(&udc_lock); + if (driver->is_bound) { + mutex_unlock(&udc_lock); + return -ENXIO; /* Driver binds to only one gadget */ + } + driver->is_bound = true; udc->driver = driver; - udc->gadget->dev.driver = &driver->driver; + mutex_unlock(&udc_lock); + + dev_dbg(&udc->dev, "binding gadget driver [%s]\n", driver->function); usb_gadget_udc_set_speed(udc, driver->max_speed); + mutex_lock(&udc_lock); ret = driver->bind(udc->gadget, driver); if (ret) - goto err1; + goto err_bind; + ret = usb_gadget_udc_start(udc); - if (ret) { - driver->unbind(udc->gadget); - goto err1; - } + if (ret) + goto err_start; usb_gadget_enable_async_callbacks(udc); usb_udc_connect_control(udc); + mutex_unlock(&udc_lock); kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE); return 0; -err1: + + err_start: + driver->unbind(udc->gadget); + + err_bind: if (ret != -EISNAM) dev_err(&udc->dev, "failed to start %s: %d\n", - udc->driver->function, ret); + driver->function, ret); + udc->driver = NULL; - udc->gadget->dev.driver = NULL; + driver->is_bound = false; + mutex_unlock(&udc_lock); + return ret; } -int usb_gadget_probe_driver(struct usb_gadget_driver *driver) +static void gadget_unbind_driver(struct device *dev) { - struct usb_udc *udc = NULL, *iter; - int ret = -ENODEV; + struct usb_gadget *gadget = dev_to_usb_gadget(dev); + struct usb_udc *udc = gadget->udc; + struct usb_gadget_driver *driver = udc->driver; + + dev_dbg(&udc->dev, "unbinding gadget driver [%s]\n", driver->function); + + kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE); + + mutex_lock(&udc_lock); + usb_gadget_disconnect(gadget); + usb_gadget_disable_async_callbacks(udc); + if (gadget->irq) + synchronize_irq(gadget->irq); + udc->driver->unbind(gadget); + usb_gadget_udc_stop(udc); + + driver->is_bound = false; + udc->driver = NULL; + mutex_unlock(&udc_lock); +} + +/* ------------------------------------------------------------------------- */ + +int usb_gadget_register_driver_owner(struct usb_gadget_driver *driver, + struct module *owner, const char *mod_name) +{ + int ret; if (!driver || !driver->bind || !driver->setup) return -EINVAL; + driver->driver.bus = &gadget_bus_type; + driver->driver.owner = owner; + driver->driver.mod_name = mod_name; + ret = driver_register(&driver->driver); + if (ret) { + pr_warn("%s: driver registration failed: %d\n", + driver->function, ret); + return ret; + } + mutex_lock(&udc_lock); - if (driver->udc_name) { - list_for_each_entry(iter, &udc_list, list) { - ret = strcmp(driver->udc_name, dev_name(&iter->dev)); - if (ret) - continue; - udc = iter; - break; - } - if (ret) - ret = -ENODEV; - else if (udc->driver) + if (!driver->is_bound) { + if (driver->match_existing_only) { + pr_warn("%s: couldn't find an available UDC or it's busy\n", + driver->function); ret = -EBUSY; - else - goto found; - } else { - list_for_each_entry(iter, &udc_list, list) { - /* For now we take the first one */ - if (iter->driver) - continue; - udc = iter; - goto found; + } else { + pr_info("%s: couldn't find an available UDC\n", + driver->function); + ret = 0; } } - - if (!driver->match_existing_only) { - list_add_tail(&driver->pending, &gadget_driver_pending_list); - pr_info("couldn't find an available UDC - added [%s] to list of pending drivers\n", - driver->function); - ret = 0; - } - mutex_unlock(&udc_lock); + if (ret) - pr_warn("couldn't find an available UDC or it's busy: %d\n", ret); - return ret; -found: - ret = udc_bind_to_driver(udc, driver); - mutex_unlock(&udc_lock); + driver_unregister(&driver->driver); return ret; } -EXPORT_SYMBOL_GPL(usb_gadget_probe_driver); +EXPORT_SYMBOL_GPL(usb_gadget_register_driver_owner); int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) { - struct usb_udc *udc = NULL; - int ret = -ENODEV; - if (!driver || !driver->unbind) return -EINVAL; - mutex_lock(&udc_lock); - list_for_each_entry(udc, &udc_list, list) { - if (udc->driver == driver) { - usb_gadget_remove_driver(udc); - usb_gadget_set_state(udc->gadget, - USB_STATE_NOTATTACHED); - - /* Maybe there is someone waiting for this UDC? */ - check_pending_gadget_drivers(udc); - /* - * For now we ignore bind errors as probably it's - * not a valid reason to fail other's gadget unbind - */ - ret = 0; - break; - } - } - - if (ret) { - list_del(&driver->pending); - ret = 0; - } - mutex_unlock(&udc_lock); - return ret; + driver_unregister(&driver->driver); + return 0; } EXPORT_SYMBOL_GPL(usb_gadget_unregister_driver); @@ -1757,8 +1740,17 @@ static int usb_udc_uevent(struct device *dev, struct kobj_uevent_env *env) return 0; } +static struct bus_type gadget_bus_type = { + .name = "gadget", + .probe = gadget_bind_driver, + .remove = gadget_unbind_driver, + .match = gadget_match_driver, +}; + static int __init usb_udc_init(void) { + int rc; + udc_class = class_create(THIS_MODULE, "udc"); if (IS_ERR(udc_class)) { pr_err("failed to create udc class --> %ld\n", @@ -1767,12 +1759,17 @@ static int __init usb_udc_init(void) } udc_class->dev_uevent = usb_udc_uevent; - return 0; + + rc = bus_register(&gadget_bus_type); + if (rc) + class_destroy(udc_class); + return rc; } subsys_initcall(usb_udc_init); static void __exit usb_udc_exit(void) { + bus_unregister(&gadget_bus_type); class_destroy(udc_class); } module_exit(usb_udc_exit); diff --git a/drivers/usb/gadget/udc/net2272.c b/drivers/usb/gadget/udc/net2272.c index 6a8884632273..c97cd4bc817c 100644 --- a/drivers/usb/gadget/udc/net2272.c +++ b/drivers/usb/gadget/udc/net2272.c @@ -71,7 +71,7 @@ static ushort dma_ep = 1; module_param(dma_ep, ushort, 0644); /* - * dma_mode: net2272 dma mode setting (see LOCCTL1 definiton): + * dma_mode: net2272 dma mode setting (see LOCCTL1 definition): * mode 0 == Slow DREQ mode * mode 1 == Fast DREQ mode * mode 2 == Burst mode @@ -97,7 +97,7 @@ module_param(fifo_mode, ushort, 0644); /* * enable_suspend: When enabled, the driver will respond to * USB suspend requests by powering down the NET2272. Otherwise, - * USB suspend requests will be ignored. This is acceptible for + * USB suspend requests will be ignored. This is acceptable for * self-powered devices. For bus powered devices set this to 1. */ static ushort enable_suspend = 0; @@ -288,7 +288,7 @@ static void net2272_ep_reset(struct net2272_ep *ep) | (1 << LOCAL_OUT_ZLP) | (1 << BUFFER_FLUSH)); - /* fifo size is handled seperately */ + /* fifo size is handled separately */ } static int net2272_disable(struct usb_ep *_ep) diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index 051d024b369e..d6a68631354a 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -932,19 +932,11 @@ static void start_dma(struct net2280_ep *ep, struct net2280_request *req) 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; + swap(ep->dummy, req->td); + swap(ep->td_dma, req->td_dma); - end->dmadesc = cpu_to_le32 (ep->td_dma); + req->td->dmadesc = cpu_to_le32 (ep->td_dma); fill_dma_desc(ep, req, valid); } diff --git a/drivers/usb/gadget/udc/omap_udc.c b/drivers/usb/gadget/udc/omap_udc.c index 5096d24915ce..61cabb9de6ae 100644 --- a/drivers/usb/gadget/udc/omap_udc.c +++ b/drivers/usb/gadget/udc/omap_udc.c @@ -1470,7 +1470,7 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src) if (!udc->ep0_in) { stat = 0; /* read next OUT packet of request, maybe - * reactiviting the fifo; stall on errors. + * reactivating the fifo; stall on errors. */ stat = read_fifo(ep0, req); if (!req || stat < 0) { @@ -2609,6 +2609,8 @@ static void omap_udc_release(struct device *dev) if (udc->dc_clk) { if (udc->clk_requested) omap_udc_enable_clock(0); + clk_unprepare(udc->hhc_clk); + clk_unprepare(udc->dc_clk); clk_put(udc->hhc_clk); clk_put(udc->dc_clk); } @@ -2773,8 +2775,8 @@ static int omap_udc_probe(struct platform_device *pdev) hhc_clk = clk_get(&pdev->dev, "usb_hhc_ck"); BUG_ON(IS_ERR(dc_clk) || IS_ERR(hhc_clk)); /* can't use omap_udc_enable_clock yet */ - clk_enable(dc_clk); - clk_enable(hhc_clk); + clk_prepare_enable(dc_clk); + clk_prepare_enable(hhc_clk); udelay(100); } @@ -2783,8 +2785,8 @@ static int omap_udc_probe(struct platform_device *pdev) hhc_clk = clk_get(&pdev->dev, "l3_ocpi_ck"); BUG_ON(IS_ERR(dc_clk) || IS_ERR(hhc_clk)); /* can't use omap_udc_enable_clock yet */ - clk_enable(dc_clk); - clk_enable(hhc_clk); + clk_prepare_enable(dc_clk); + clk_prepare_enable(hhc_clk); udelay(100); } @@ -2932,8 +2934,8 @@ cleanup0: usb_put_phy(xceiv); if (cpu_is_omap16xx() || cpu_is_omap7xx()) { - clk_disable(hhc_clk); - clk_disable(dc_clk); + clk_disable_unprepare(hhc_clk); + clk_disable_unprepare(dc_clk); clk_put(hhc_clk); clk_put(dc_clk); } diff --git a/drivers/usb/gadget/udc/pxa27x_udc.h b/drivers/usb/gadget/udc/pxa27x_udc.h index 0a6bc18a1264..31bf79ce931c 100644 --- a/drivers/usb/gadget/udc/pxa27x_udc.h +++ b/drivers/usb/gadget/udc/pxa27x_udc.h @@ -326,7 +326,7 @@ struct udc_usb_ep { * @addr: usb endpoint number * @config: configuration in which this endpoint is active * @interface: interface in which this endpoint is active - * @alternate: altsetting in which this endpoitn is active + * @alternate: altsetting in which this endpoint is active * @fifo_size: max packet size in the endpoint fifo * @type: endpoint type (bulk, iso, int, ...) * @udccsr_value: save register of UDCCSR0 for suspend/resume diff --git a/drivers/usb/gadget/udc/s3c-hsudc.c b/drivers/usb/gadget/udc/s3c-hsudc.c index bf803e013458..4b7eb7701470 100644 --- a/drivers/usb/gadget/udc/s3c-hsudc.c +++ b/drivers/usb/gadget/udc/s3c-hsudc.c @@ -126,7 +126,7 @@ struct s3c_hsudc_req { /** * struct s3c_hsudc - Driver's abstraction of the device controller. * @gadget: Instance of usb_gadget which is referenced by gadget driver. - * @driver: Reference to currenty active gadget driver. + * @driver: Reference to currently active gadget driver. * @dev: The device reference used by probe function. * @lock: Lock to synchronize the usage of Endpoints (EP's are indexed). * @regs: Remapped base address of controller's register space. @@ -633,7 +633,7 @@ static void s3c_hsudc_process_setup(struct s3c_hsudc *hsudc) } /** s3c_hsudc_handle_ep0_intr - Handle endpoint 0 interrupt. - * @hsudc: Device controller on which endpoint 0 interrupt has occured. + * @hsudc: Device controller on which endpoint 0 interrupt has occurred. * * Handle endpoint 0 interrupt when it occurs. EP0 interrupt could occur * when a stall handshake is sent to host or data is sent/received on diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c index d9c406bdb680..6d31ccf6aee5 100644 --- a/drivers/usb/gadget/udc/tegra-xudc.c +++ b/drivers/usb/gadget/udc/tegra-xudc.c @@ -1434,7 +1434,7 @@ __tegra_xudc_ep_dequeue(struct tegra_xudc_ep *ep, return 0; } - /* Halt DMA for this endpiont. */ + /* Halt DMA for this endpoint. */ if (ep_ctx_read_state(ep->context) == EP_STATE_RUNNING) { ep_pause(xudc, ep->index); ep_wait_for_inactive(xudc, ep->index); @@ -3423,7 +3423,7 @@ static void tegra_xudc_device_params_init(struct tegra_xudc *xudc) } /* - * Compliacne suite appears to be violating polling LFPS tBurst max + * Compliance suite appears to be violating polling LFPS tBurst max * of 1.4us. Send 1.45us instead. */ val = xudc_readl(xudc, SSPX_CORE_CNT32); diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c index 428c755cf2e1..4827e3cd3834 100644 --- a/drivers/usb/gadget/udc/udc-xilinx.c +++ b/drivers/usb/gadget/udc/udc-xilinx.c @@ -632,7 +632,7 @@ top: dev_dbg(udc->dev, "read %s, %d bytes%s req %p %d/%d\n", ep->ep_usb.name, count, is_short ? "/S" : "", req, req->usb_req.actual, req->usb_req.length); - bufferspace -= count; + /* Completion */ if ((req->usb_req.actual == req->usb_req.length) || is_short) { if (udc->dma_enabled && req->usb_req.length) diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 7f4a03e8647a..8c45bc17a580 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -61,11 +61,6 @@ static inline void ehci_write(void __iomem *base, u32 reg, u32 val) __raw_writel(val, base + reg); } -static inline u32 ehci_read(void __iomem *base, u32 reg) -{ - return __raw_readl(base + reg); -} - /* configure so an HC device and id are always provided */ /* always called with process context; sleeping is OK */ diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index 1115431a255d..f343967443e2 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -518,6 +518,7 @@ static struct platform_driver ehci_platform_driver = { .pm = pm_ptr(&ehci_platform_pm_ops), .of_match_table = vt8500_ehci_ids, .acpi_match_table = ACPI_PTR(ehci_acpi_match), + .probe_type = PROBE_PREFER_ASYNCHRONOUS, } }; diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index a2a5c2996350..1163af6fad77 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -645,7 +645,7 @@ qh_urb_transaction ( token |= (1 /* "in" */ << 8); /* else it's already initted to "out" pid (0 << 8) */ - maxpacket = usb_maxpacket(urb->dev, urb->pipe, !is_input); + maxpacket = usb_maxpacket(urb->dev, urb->pipe); /* * buffer gets wrapped in one or more qtds; @@ -1218,7 +1218,7 @@ static int ehci_submit_single_step_set_feature( token |= (1 /* "in" */ << 8); /*This is IN stage*/ - maxpacket = usb_maxpacket(urb->dev, urb->pipe, 0); + maxpacket = usb_maxpacket(urb->dev, urb->pipe); qtd_fill(ehci, qtd, buf, len, token, maxpacket); diff --git a/drivers/usb/host/ehci-xilinx-of.c b/drivers/usb/host/ehci-xilinx-of.c index 67a6ee8cb5d8..3d7893747835 100644 --- a/drivers/usb/host/ehci-xilinx-of.c +++ b/drivers/usb/host/ehci-xilinx-of.c @@ -32,6 +32,8 @@ * There are cases when the host controller fails to enable the port due to, * for example, insufficient power that can be supplied to the device from * the USB bus. In those cases, the messages printed here are not helpful. + * + * Return: Always return 0 */ static int ehci_xilinx_port_handed_over(struct usb_hcd *hcd, int portnum) { @@ -46,11 +48,9 @@ static int ehci_xilinx_port_handed_over(struct usb_hcd *hcd, int portnum) dev_warn(hcd->self.controller, "Maybe your device is not a high speed device?\n"); dev_warn(hcd->self.controller, - "The USB host controller does not support full speed " - "nor low speed devices\n"); + "The USB host controller does not support full speed nor low speed devices\n"); dev_warn(hcd->self.controller, - "You can reconfigure the host controller to have " - "full speed support\n"); + "You can reconfigure the host controller to have full speed support\n"); } return 0; @@ -112,6 +112,8 @@ static const struct hc_driver ehci_xilinx_of_hc_driver = { * host controller. Because the Xilinx USB host controller can be configured * as HS only or HS/FS only, it checks the configuration in the device tree * entry, and sets an appropriate value for hcd->has_tt. + * + * Return: zero on success, negative error code otherwise */ static int ehci_hcd_xilinx_of_probe(struct platform_device *op) { @@ -196,6 +198,8 @@ err_irq: * * Remove the hcd structure, and release resources that has been requested * during probe. + * + * Return: Always return 0 */ static int ehci_hcd_xilinx_of_remove(struct platform_device *op) { diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c index a8e1048278d0..2ba09c3fbc2f 100644 --- a/drivers/usb/host/fhci-hcd.c +++ b/drivers/usb/host/fhci-hcd.c @@ -408,8 +408,7 @@ static int fhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, size++; else if ((urb->transfer_flags & URB_ZERO_PACKET) != 0 && (urb->transfer_buffer_length - % usb_maxpacket(urb->dev, pipe, - usb_pipeout(pipe))) != 0) + % usb_maxpacket(urb->dev, pipe)) != 0) size++; break; case PIPE_ISOCHRONOUS: diff --git a/drivers/usb/host/fotg210-hcd.c b/drivers/usb/host/fotg210-hcd.c index c3fd375b4778..f8c111e08a0d 100644 --- a/drivers/usb/host/fotg210-hcd.c +++ b/drivers/usb/host/fotg210-hcd.c @@ -2596,7 +2596,7 @@ static struct list_head *qh_urb_transaction(struct fotg210_hcd *fotg210, token |= (1 /* "in" */ << 8); /* else it's already initted to "out" pid (0 << 8) */ - maxpacket = usb_maxpacket(urb->dev, urb->pipe, !is_input); + maxpacket = usb_maxpacket(urb->dev, urb->pipe); /* * buffer gets wrapped in one or more qtds; diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index 8835f6bd528e..4f564d71bb0b 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -726,7 +726,7 @@ static int isp116x_urb_enqueue(struct usb_hcd *hcd, INIT_LIST_HEAD(&ep->schedule); ep->udev = udev; ep->epnum = epnum; - ep->maxpacket = usb_maxpacket(udev, urb->pipe, is_out); + ep->maxpacket = usb_maxpacket(udev, urb->pipe); usb_settoggle(udev, epnum, is_out, 0); if (type == PIPE_CONTROL) { @@ -757,8 +757,7 @@ static int isp116x_urb_enqueue(struct usb_hcd *hcd, ep->load = usb_calc_bus_time(udev->speed, !is_out, (type == PIPE_ISOCHRONOUS), - usb_maxpacket(udev, pipe, - is_out)) / + usb_maxpacket(udev, pipe)) / 1000; } hep->hcpriv = ep; @@ -1541,10 +1540,12 @@ static int isp116x_remove(struct platform_device *pdev) iounmap(isp116x->data_reg); res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - release_mem_region(res->start, 2); + if (res) + release_mem_region(res->start, 2); iounmap(isp116x->addr_reg); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, 2); + if (res) + release_mem_region(res->start, 2); usb_put_hcd(hcd); return 0; diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c index d8610ce8f2ec..0e14d1d07709 100644 --- a/drivers/usb/host/isp1362-hcd.c +++ b/drivers/usb/host/isp1362-hcd.c @@ -1279,7 +1279,7 @@ static int isp1362_urb_enqueue(struct usb_hcd *hcd, ep->udev = usb_get_dev(udev); ep->hep = hep; ep->epnum = epnum; - ep->maxpacket = usb_maxpacket(udev, urb->pipe, is_out); + ep->maxpacket = usb_maxpacket(udev, urb->pipe); ep->ptd_offset = -EINVAL; ep->ptd_index = -EINVAL; usb_settoggle(udev, epnum, is_out, 0); @@ -1299,8 +1299,8 @@ static int isp1362_urb_enqueue(struct usb_hcd *hcd, ep->interval = urb->interval; ep->branch = PERIODIC_SIZE; ep->load = usb_calc_bus_time(udev->speed, !is_out, - (type == PIPE_ISOCHRONOUS), - usb_maxpacket(udev, pipe, is_out)) / 1000; + type == PIPE_ISOCHRONOUS, + usb_maxpacket(udev, pipe)) / 1000; break; } hep->hcpriv = ep; diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c index 99a5523a79fb..502a3ac5e35b 100644 --- a/drivers/usb/host/max3421-hcd.c +++ b/drivers/usb/host/max3421-hcd.c @@ -546,7 +546,7 @@ max3421_transfer_out(struct usb_hcd *hcd, struct urb *urb, int fast_retransmit) return MAX3421_HXFR_BULK_OUT(epnum); } - max_packet = usb_maxpacket(urb->dev, urb->pipe, 1); + max_packet = usb_maxpacket(urb->dev, urb->pipe); if (max_packet > MAX3421_FIFO_SIZE) { /* @@ -952,7 +952,7 @@ max3421_transfer_in_done(struct usb_hcd *hcd, struct urb *urb) * USB 2.0 Section 5.3.2 Pipes: packets must be full size * except for last one. */ - max_packet = usb_maxpacket(urb->dev, urb->pipe, 0); + max_packet = usb_maxpacket(urb->dev, urb->pipe); if (max_packet > MAX3421_FIFO_SIZE) { /* * We do not support isochronous transfers at this @@ -998,7 +998,7 @@ max3421_transfer_out_done(struct usb_hcd *hcd, struct urb *urb) * max_packet as an indicator that the end of the * packet has been reached). */ - u32 max_packet = usb_maxpacket(urb->dev, urb->pipe, 1); + u32 max_packet = usb_maxpacket(urb->dev, urb->pipe); if (max3421_hcd->curr_len == max_packet) return 0; diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 666b1c665188..c4c821c2288c 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -181,8 +181,7 @@ static int ohci_urb_enqueue ( size++; else if ((urb->transfer_flags & URB_ZERO_PACKET) != 0 && (urb->transfer_buffer_length - % usb_maxpacket (urb->dev, pipe, - usb_pipeout (pipe))) == 0) + % usb_maxpacket(urb->dev, pipe)) == 0) size++; break; case PIPE_ISOCHRONOUS: /* number of packets from URB */ diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index 069791d25abb..f5bc9c8bdc9a 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -259,6 +259,10 @@ static int ohci_hcd_omap_probe(struct platform_device *pdev) goto err_put_hcd; } + retval = clk_prepare(priv->usb_host_ck); + if (retval) + goto err_put_host_ck; + if (!cpu_is_omap15xx()) priv->usb_dc_ck = clk_get(&pdev->dev, "usb_dc_ck"); else @@ -266,13 +270,17 @@ static int ohci_hcd_omap_probe(struct platform_device *pdev) if (IS_ERR(priv->usb_dc_ck)) { retval = PTR_ERR(priv->usb_dc_ck); - goto err_put_host_ck; + goto err_unprepare_host_ck; } + retval = clk_prepare(priv->usb_dc_ck); + if (retval) + goto err_put_dc_ck; + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { dev_dbg(&pdev->dev, "request_mem_region failed\n"); retval = -EBUSY; - goto err_put_dc_ck; + goto err_unprepare_dc_ck; } hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); @@ -297,8 +305,12 @@ err3: iounmap(hcd->regs); err2: release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err_unprepare_dc_ck: + clk_unprepare(priv->usb_dc_ck); err_put_dc_ck: clk_put(priv->usb_dc_ck); +err_unprepare_host_ck: + clk_unprepare(priv->usb_host_ck); err_put_host_ck: clk_put(priv->usb_host_ck); err_put_hcd: @@ -333,7 +345,9 @@ static int ohci_hcd_omap_remove(struct platform_device *pdev) } iounmap(hcd->regs); release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + clk_unprepare(priv->usb_dc_ck); clk_put(priv->usb_dc_ck); + clk_unprepare(priv->usb_host_ck); clk_put(priv->usb_host_ck); usb_put_hcd(hcd); return 0; diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c index 4a8456f12a73..47dfbfe9e519 100644 --- a/drivers/usb/host/ohci-platform.c +++ b/drivers/usb/host/ohci-platform.c @@ -334,6 +334,7 @@ static struct platform_driver ohci_platform_driver = { .name = "ohci-platform", .pm = &ohci_platform_pm_ops, .of_match_table = ohci_platform_ids, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, } }; diff --git a/drivers/usb/host/ohci-ppc-of.c b/drivers/usb/host/ohci-ppc-of.c index 45f7cceb6df3..1960b8dfdba5 100644 --- a/drivers/usb/host/ohci-ppc-of.c +++ b/drivers/usb/host/ohci-ppc-of.c @@ -19,9 +19,6 @@ #include <linux/of_irq.h> #include <linux/of_platform.h> -#include <asm/prom.h> - - static int ohci_ppc_of_start(struct usb_hcd *hcd) { diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c index b741670525e3..3a441310c713 100644 --- a/drivers/usb/host/oxu210hp-hcd.c +++ b/drivers/usb/host/oxu210hp-hcd.c @@ -1685,7 +1685,7 @@ static struct list_head *qh_urb_transaction(struct oxu_hcd *oxu, token |= (1 /* "in" */ << 8); /* else it's already initted to "out" pid (0 << 8) */ - maxpacket = usb_maxpacket(urb->dev, urb->pipe, !is_input); + maxpacket = usb_maxpacket(urb->dev, urb->pipe); /* * buffer gets wrapped in one or more qtds; @@ -1796,7 +1796,7 @@ static struct ehci_qh *qh_make(struct oxu_hcd *oxu, is_input = usb_pipein(urb->pipe); type = usb_pipetype(urb->pipe); - maxp = usb_maxpacket(urb->dev, urb->pipe, !is_input); + maxp = usb_maxpacket(urb->dev, urb->pipe); /* Compute interrupt scheduling parameters just once, and save. * - allowing for high bandwidth, how many nsec/uframe are used? @@ -3909,8 +3909,10 @@ static int oxu_bus_suspend(struct usb_hcd *hcd) } } + spin_unlock_irq(&oxu->lock); /* turn off now-idle HC */ del_timer_sync(&oxu->watchdog); + spin_lock_irq(&oxu->lock); ehci_halt(oxu); hcd->state = HC_STATE_SUSPENDED; @@ -4223,13 +4225,9 @@ static int oxu_drv_probe(struct platform_device *pdev) /* * Get the platform resources */ - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res) { - dev_err(&pdev->dev, - "no IRQ! Check %s setup!\n", dev_name(&pdev->dev)); - return -ENODEV; - } - irq = res->start; + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; dev_dbg(&pdev->dev, "IRQ resource %d\n", irq); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index 63719cdf6a4e..abb88dd40d4e 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -1867,8 +1867,7 @@ static struct r8a66597_td *r8a66597_make_td(struct r8a66597 *r8a66597, td->pipe = hep->hcpriv; td->urb = urb; td->address = get_urb_to_r8a66597_addr(r8a66597, urb); - td->maxpacket = usb_maxpacket(urb->dev, urb->pipe, - !usb_pipein(urb->pipe)); + td->maxpacket = usb_maxpacket(urb->dev, urb->pipe); if (usb_pipecontrol(urb->pipe)) td->type = USB_PID_SETUP; else if (usb_pipein(urb->pipe)) diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index 85623731a516..d206bd95c7bb 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -842,7 +842,7 @@ static int sl811h_urb_enqueue( INIT_LIST_HEAD(&ep->schedule); ep->udev = udev; ep->epnum = epnum; - ep->maxpacket = usb_maxpacket(udev, urb->pipe, is_out); + ep->maxpacket = usb_maxpacket(udev, urb->pipe); ep->defctrl = SL11H_HCTLMASK_ARM | SL11H_HCTLMASK_ENABLE; usb_settoggle(udev, epnum, is_out, 0); @@ -878,8 +878,8 @@ static int sl811h_urb_enqueue( if (type == PIPE_ISOCHRONOUS) ep->defctrl |= SL11H_HCTLMASK_ISOCH; ep->load = usb_calc_bus_time(udev->speed, !is_out, - (type == PIPE_ISOCHRONOUS), - usb_maxpacket(udev, pipe, is_out)) + type == PIPE_ISOCHRONOUS, + usb_maxpacket(udev, pipe)) / 1000; break; } diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index f65f1ba2b592..c54f2bc23d3f 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -707,6 +707,7 @@ static int xhci_enter_test_mode(struct xhci_hcd *xhci, u16 test_mode, u16 wIndex, unsigned long *flags) __must_hold(&xhci->lock) { + struct usb_hcd *usb3_hcd = xhci_get_usb3_hcd(xhci); int i, retval; /* Disable all Device Slots */ @@ -727,7 +728,7 @@ static int xhci_enter_test_mode(struct xhci_hcd *xhci, xhci_dbg(xhci, "Disable all port (PP = 0)\n"); /* Power off USB3 ports*/ for (i = 0; i < xhci->usb3_rhub.num_ports; i++) - xhci_set_port_power(xhci, xhci->shared_hcd, i, false, flags); + xhci_set_port_power(xhci, usb3_hcd, i, false, flags); /* Power off USB2 ports*/ for (i = 0; i < xhci->usb2_rhub.num_ports; i++) xhci_set_port_power(xhci, xhci->main_hcd, i, false, flags); diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index bbb27ee2c6a3..8c19e151a945 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -782,14 +782,6 @@ void xhci_free_stream_info(struct xhci_hcd *xhci, /***************** Device context manipulation *************************/ -static void xhci_init_endpoint_timer(struct xhci_hcd *xhci, - struct xhci_virt_ep *ep) -{ - timer_setup(&ep->stop_cmd_timer, xhci_stop_endpoint_command_watchdog, - 0); - ep->xhci = xhci; -} - static void xhci_free_tt_info(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev, int slot_id) @@ -994,11 +986,11 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, xhci_dbg(xhci, "Slot %d input ctx = 0x%llx (dma)\n", slot_id, (unsigned long long)dev->in_ctx->dma); - /* Initialize the cancellation list and watchdog timers for each ep */ + /* Initialize the cancellation and bandwidth list for each ep */ for (i = 0; i < 31; i++) { dev->eps[i].ep_index = i; dev->eps[i].vdev = dev; - xhci_init_endpoint_timer(xhci, &dev->eps[i]); + dev->eps[i].xhci = xhci; INIT_LIST_HEAD(&dev->eps[i].cancelled_td_list); INIT_LIST_HEAD(&dev->eps[i].bw_endpoint_list); } @@ -1072,7 +1064,7 @@ static u32 xhci_find_real_port_number(struct xhci_hcd *xhci, struct usb_hcd *hcd; if (udev->speed >= USB_SPEED_SUPER) - hcd = xhci->shared_hcd; + hcd = xhci_get_usb3_hcd(xhci); else hcd = xhci->main_hcd; @@ -2362,10 +2354,11 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) xhci->usb2_rhub.num_ports = USB_MAXCHILDREN; } - /* - * Note we could have all USB 3.0 ports, or all USB 2.0 ports. - * Not sure how the USB core will handle a hub with no ports... - */ + if (!xhci->usb2_rhub.num_ports) + xhci_info(xhci, "USB2 root hub has no ports\n"); + + if (!xhci->usb3_rhub.num_ports) + xhci_info(xhci, "USB3 root hub has no ports\n"); xhci_create_rhub_port_array(xhci, &xhci->usb2_rhub, flags); xhci_create_rhub_port_array(xhci, &xhci->usb3_rhub, flags); diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index d7e0e6ebf080..fac9492a8bda 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -59,6 +59,7 @@ #define PCI_DEVICE_ID_INTEL_TIGER_LAKE_XHCI 0x9a13 #define PCI_DEVICE_ID_INTEL_MAPLE_RIDGE_XHCI 0x1138 #define PCI_DEVICE_ID_INTEL_ALDER_LAKE_XHCI 0x461e +#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_N_XHCI 0x464e #define PCI_DEVICE_ID_INTEL_ALDER_LAKE_PCH_XHCI 0x51ed #define PCI_DEVICE_ID_AMD_RENOIR_XHCI 0x1639 @@ -129,8 +130,7 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) pdev->revision == 0x0) { xhci->quirks |= XHCI_RESET_EP_QUIRK; xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, - "QUIRK: Fresco Logic xHC needs configure" - " endpoint cmd after reset endpoint"); + "XHCI_RESET_EP_QUIRK for this evaluation HW is deprecated"); } if (pdev->device == PCI_DEVICE_ID_FRESCO_LOGIC_PDK && pdev->revision == 0x4) { @@ -268,6 +268,7 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) pdev->device == PCI_DEVICE_ID_INTEL_TIGER_LAKE_XHCI || pdev->device == PCI_DEVICE_ID_INTEL_MAPLE_RIDGE_XHCI || pdev->device == PCI_DEVICE_ID_INTEL_ALDER_LAKE_XHCI || + pdev->device == PCI_DEVICE_ID_INTEL_ALDER_LAKE_N_XHCI || pdev->device == PCI_DEVICE_ID_INTEL_ALDER_LAKE_PCH_XHCI)) xhci->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW; diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 649ffd861b44..044855818cb1 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -180,7 +180,7 @@ static int xhci_plat_probe(struct platform_device *pdev) struct device *sysdev, *tmpdev; struct xhci_hcd *xhci; struct resource *res; - struct usb_hcd *hcd; + struct usb_hcd *hcd, *usb3_hcd; int ret; int irq; struct xhci_plat_priv *priv = NULL; @@ -245,6 +245,8 @@ static int xhci_plat_probe(struct platform_device *pdev) xhci = hcd_to_xhci(hcd); + xhci->allow_single_roothub = 1; + /* * Not all platforms have clks so it is not an error if the * clock do not exist. @@ -283,12 +285,6 @@ static int xhci_plat_probe(struct platform_device *pdev) device_set_wakeup_capable(&pdev->dev, true); xhci->main_hcd = hcd; - xhci->shared_hcd = __usb_create_hcd(driver, sysdev, &pdev->dev, - dev_name(&pdev->dev), hcd); - if (!xhci->shared_hcd) { - ret = -ENOMEM; - goto disable_clk; - } /* imod_interval is the interrupt moderation value in nanoseconds. */ xhci->imod_interval = 40000; @@ -313,16 +309,16 @@ static int xhci_plat_probe(struct platform_device *pdev) if (IS_ERR(hcd->usb_phy)) { ret = PTR_ERR(hcd->usb_phy); if (ret == -EPROBE_DEFER) - goto put_usb3_hcd; + goto disable_clk; hcd->usb_phy = NULL; } else { ret = usb_phy_init(hcd->usb_phy); if (ret) - goto put_usb3_hcd; + goto disable_clk; } hcd->tpl_support = of_usb_host_tpl_support(sysdev->of_node); - xhci->shared_hcd->tpl_support = hcd->tpl_support; + if (priv && (priv->quirks & XHCI_SKIP_PHY_INIT)) hcd->skip_phy_initialization = 1; @@ -333,12 +329,26 @@ static int xhci_plat_probe(struct platform_device *pdev) if (ret) goto disable_usb_phy; - if (HCC_MAX_PSA(xhci->hcc_params) >= 4) - xhci->shared_hcd->can_do_streams = 1; + if (!xhci_has_one_roothub(xhci)) { + xhci->shared_hcd = __usb_create_hcd(driver, sysdev, &pdev->dev, + dev_name(&pdev->dev), hcd); + if (!xhci->shared_hcd) { + ret = -ENOMEM; + goto dealloc_usb2_hcd; + } - ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED); - if (ret) - goto dealloc_usb2_hcd; + xhci->shared_hcd->tpl_support = hcd->tpl_support; + } + + usb3_hcd = xhci_get_usb3_hcd(xhci); + if (usb3_hcd && HCC_MAX_PSA(xhci->hcc_params) >= 4) + usb3_hcd->can_do_streams = 1; + + if (xhci->shared_hcd) { + ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED); + if (ret) + goto put_usb3_hcd; + } device_enable_async_suspend(&pdev->dev); pm_runtime_put_noidle(&pdev->dev); @@ -352,15 +362,15 @@ static int xhci_plat_probe(struct platform_device *pdev) return 0; +put_usb3_hcd: + usb_put_hcd(xhci->shared_hcd); + dealloc_usb2_hcd: usb_remove_hcd(hcd); disable_usb_phy: usb_phy_shutdown(hcd->usb_phy); -put_usb3_hcd: - usb_put_hcd(xhci->shared_hcd); - disable_clk: clk_disable_unprepare(xhci->clk); diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index f9707997969d..46d0b9ad6f74 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -740,14 +740,6 @@ static void td_to_noop(struct xhci_hcd *xhci, struct xhci_ring *ep_ring, } } -static void xhci_stop_watchdog_timer_in_irq(struct xhci_hcd *xhci, - struct xhci_virt_ep *ep) -{ - ep->ep_state &= ~EP_STOP_CMD_PENDING; - /* Can't del_timer_sync in interrupt */ - del_timer(&ep->stop_cmd_timer); -} - /* * Must be called with xhci->lock held in interrupt context, * releases and re-acquires xhci->lock @@ -1122,18 +1114,17 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id, reset_type); if (err) break; - xhci_stop_watchdog_timer_in_irq(xhci, ep); + ep->ep_state &= ~EP_STOP_CMD_PENDING; return; case EP_STATE_RUNNING: /* Race, HW handled stop ep cmd before ep was running */ xhci_dbg(xhci, "Stop ep completion ctx error, ep is running\n"); command = xhci_alloc_command(xhci, false, GFP_ATOMIC); - if (!command) - xhci_stop_watchdog_timer_in_irq(xhci, ep); - - mod_timer(&ep->stop_cmd_timer, - jiffies + XHCI_STOP_EP_CMD_TIMEOUT * HZ); + if (!command) { + ep->ep_state &= ~EP_STOP_CMD_PENDING; + return; + } xhci_queue_stop_endpoint(xhci, command, slot_id, ep_index, 0); xhci_ring_cmd_db(xhci); @@ -1142,9 +1133,10 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id, break; } } + /* will queue a set TR deq if stopped on a cancelled, uncleared TD */ xhci_invalidate_cancelled_tds(ep); - xhci_stop_watchdog_timer_in_irq(xhci, ep); + ep->ep_state &= ~EP_STOP_CMD_PENDING; /* Otherwise ring the doorbell(s) to restart queued transfers */ xhci_giveback_invalidated_tds(ep); @@ -1248,61 +1240,6 @@ void xhci_hc_died(struct xhci_hcd *xhci) usb_hc_died(xhci_to_hcd(xhci)); } -/* Watchdog timer function for when a stop endpoint command fails to complete. - * In this case, we assume the host controller is broken or dying or dead. The - * host may still be completing some other events, so we have to be careful to - * let the event ring handler and the URB dequeueing/enqueueing functions know - * through xhci->state. - * - * The timer may also fire if the host takes a very long time to respond to the - * command, and the stop endpoint command completion handler cannot delete the - * timer before the timer function is called. Another endpoint cancellation may - * sneak in before the timer function can grab the lock, and that may queue - * another stop endpoint command and add the timer back. So we cannot use a - * simple flag to say whether there is a pending stop endpoint command for a - * particular endpoint. - * - * Instead we use a combination of that flag and checking if a new timer is - * pending. - */ -void xhci_stop_endpoint_command_watchdog(struct timer_list *t) -{ - struct xhci_virt_ep *ep = from_timer(ep, t, stop_cmd_timer); - struct xhci_hcd *xhci = ep->xhci; - unsigned long flags; - u32 usbsts; - char str[XHCI_MSG_MAX]; - - spin_lock_irqsave(&xhci->lock, flags); - - /* bail out if cmd completed but raced with stop ep watchdog timer.*/ - if (!(ep->ep_state & EP_STOP_CMD_PENDING) || - timer_pending(&ep->stop_cmd_timer)) { - spin_unlock_irqrestore(&xhci->lock, flags); - xhci_dbg(xhci, "Stop EP timer raced with cmd completion, exit"); - return; - } - usbsts = readl(&xhci->op_regs->status); - - xhci_warn(xhci, "xHCI host not responding to stop endpoint command.\n"); - xhci_warn(xhci, "USBSTS:%s\n", xhci_decode_usbsts(str, usbsts)); - - ep->ep_state &= ~EP_STOP_CMD_PENDING; - - xhci_halt(xhci); - - /* - * handle a stop endpoint cmd timeout as if host died (-ENODEV). - * In the future we could distinguish between -ENODEV and -ETIMEDOUT - * and try to recover a -ETIMEDOUT with a host controller reset - */ - xhci_hc_died(xhci); - - spin_unlock_irqrestore(&xhci->lock, flags); - xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, - "xHCI host controller is dead."); -} - static void update_ring_for_set_deq_completion(struct xhci_hcd *xhci, struct xhci_virt_device *dev, struct xhci_ring *ep_ring, @@ -1489,8 +1426,6 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id, /* Cleanup cancelled TDs as ep is stopped. May queue a Set TR Deq cmd */ xhci_invalidate_cancelled_tds(ep); - if (xhci->quirks & XHCI_RESET_EP_QUIRK) - xhci_dbg(xhci, "Note: Removed workaround to queue config ep for this hw"); /* Clear our internal halted state */ ep->ep_state &= ~EP_HALTED; @@ -1534,17 +1469,13 @@ static void xhci_handle_cmd_config_ep(struct xhci_hcd *xhci, int slot_id, struct xhci_input_control_ctx *ctrl_ctx; struct xhci_ep_ctx *ep_ctx; unsigned int ep_index; - unsigned int ep_state; - u32 add_flags, drop_flags; + u32 add_flags; /* - * Configure endpoint commands can come from the USB core - * configuration or alt setting changes, or because the HW - * needed an extra configure endpoint command after a reset - * endpoint command or streams were being configured. - * If the command was for a halted endpoint, the xHCI driver - * is not waiting on the configure endpoint command. + * Configure endpoint commands can come from the USB core configuration + * or alt setting changes, or when streams were being configured. */ + virt_dev = xhci->devs[slot_id]; if (!virt_dev) return; @@ -1555,34 +1486,13 @@ static void xhci_handle_cmd_config_ep(struct xhci_hcd *xhci, int slot_id, } add_flags = le32_to_cpu(ctrl_ctx->add_flags); - drop_flags = le32_to_cpu(ctrl_ctx->drop_flags); + /* Input ctx add_flags are the endpoint index plus one */ ep_index = xhci_last_valid_endpoint(add_flags) - 1; ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->out_ctx, ep_index); trace_xhci_handle_cmd_config_ep(ep_ctx); - /* A usb_set_interface() call directly after clearing a halted - * condition may race on this quirky hardware. Not worth - * worrying about, since this is prototype hardware. Not sure - * if this will work for streams, but streams support was - * untested on this prototype. - */ - if (xhci->quirks & XHCI_RESET_EP_QUIRK && - ep_index != (unsigned int) -1 && - add_flags - SLOT_FLAG == drop_flags) { - ep_state = virt_dev->eps[ep_index].ep_state; - if (!(ep_state & EP_HALTED)) - return; - xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, - "Completed config ep cmd - " - "last ep index = %d, state = %d", - ep_index, ep_state); - /* Clear internal halted state and restart ring(s) */ - virt_dev->eps[ep_index].ep_state &= ~EP_HALTED; - ring_doorbell_for_active_rings(xhci, slot_id, ep_index); - return; - } return; } @@ -1650,9 +1560,12 @@ void xhci_cleanup_command_queue(struct xhci_hcd *xhci) void xhci_handle_command_timeout(struct work_struct *work) { - struct xhci_hcd *xhci; - unsigned long flags; - u64 hw_ring_state; + struct xhci_hcd *xhci; + unsigned long flags; + char str[XHCI_MSG_MAX]; + u64 hw_ring_state; + u32 cmd_field3; + u32 usbsts; xhci = container_of(to_delayed_work(work), struct xhci_hcd, cmd_timer); @@ -1666,6 +1579,27 @@ void xhci_handle_command_timeout(struct work_struct *work) spin_unlock_irqrestore(&xhci->lock, flags); return; } + + cmd_field3 = le32_to_cpu(xhci->current_cmd->command_trb->generic.field[3]); + usbsts = readl(&xhci->op_regs->status); + xhci_dbg(xhci, "Command timeout, USBSTS:%s\n", xhci_decode_usbsts(str, usbsts)); + + /* Bail out and tear down xhci if a stop endpoint command failed */ + if (TRB_FIELD_TO_TYPE(cmd_field3) == TRB_STOP_RING) { + struct xhci_virt_ep *ep; + + xhci_warn(xhci, "xHCI host not responding to stop endpoint command\n"); + + ep = xhci_get_virt_ep(xhci, TRB_TO_SLOT_ID(cmd_field3), + TRB_TO_EP_INDEX(cmd_field3)); + if (ep) + ep->ep_state &= ~EP_STOP_CMD_PENDING; + + xhci_halt(xhci); + xhci_hc_died(xhci); + goto time_out_completed; + } + /* mark this command to be cancelled */ xhci->current_cmd->status = COMP_COMMAND_ABORTED; diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 25b87e99b4dd..f0ab63138016 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -486,6 +486,10 @@ static void compliance_mode_recovery(struct timer_list *t) xhci = from_timer(xhci, t, comp_mode_recovery_timer); rhub = &xhci->usb3_rhub; + hcd = rhub->hcd; + + if (!hcd) + return; for (i = 0; i < rhub->num_ports; i++) { temp = readl(rhub->ports[i]->addr); @@ -499,7 +503,6 @@ static void compliance_mode_recovery(struct timer_list *t) i + 1); xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, "Attempting compliance mode recovery"); - hcd = xhci->shared_hcd; if (hcd->state == HC_STATE_SUSPENDED) usb_hcd_resume_root_hub(hcd); @@ -612,14 +615,11 @@ static int xhci_run_finished(struct xhci_hcd *xhci) xhci_halt(xhci); return -ENODEV; } - xhci->shared_hcd->state = HC_STATE_RUNNING; xhci->cmd_ring_state = CMD_RING_STATE_RUNNING; if (xhci->quirks & XHCI_NEC_HOST) xhci_ring_cmd_db(xhci); - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "Finished xhci_run for USB3 roothub"); return 0; } @@ -694,12 +694,17 @@ int xhci_run(struct usb_hcd *hcd) xhci_free_command(xhci, command); } xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "Finished xhci_run for USB2 roothub"); + "Finished %s for main hcd", __func__); xhci_create_dbc_dev(xhci); xhci_debugfs_init(xhci); + if (xhci_has_one_roothub(xhci)) + return xhci_run_finished(xhci); + + set_bit(HCD_FLAG_DEFER_RH_REGISTER, &hcd->flags); + return 0; } EXPORT_SYMBOL_GPL(xhci_run); @@ -992,7 +997,7 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup) return 0; if (hcd->state != HC_STATE_SUSPENDED || - xhci->shared_hcd->state != HC_STATE_SUSPENDED) + (xhci->shared_hcd && xhci->shared_hcd->state != HC_STATE_SUSPENDED)) return -EINVAL; /* Clear root port wake on bits if wakeup not allowed. */ @@ -1009,15 +1014,18 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup) __func__, hcd->self.busnum); clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); del_timer_sync(&hcd->rh_timer); - clear_bit(HCD_FLAG_POLL_RH, &xhci->shared_hcd->flags); - del_timer_sync(&xhci->shared_hcd->rh_timer); + if (xhci->shared_hcd) { + clear_bit(HCD_FLAG_POLL_RH, &xhci->shared_hcd->flags); + del_timer_sync(&xhci->shared_hcd->rh_timer); + } if (xhci->quirks & XHCI_SUSPEND_DELAY) usleep_range(1000, 1500); spin_lock_irq(&xhci->lock); clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - clear_bit(HCD_FLAG_HW_ACCESSIBLE, &xhci->shared_hcd->flags); + if (xhci->shared_hcd) + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &xhci->shared_hcd->flags); /* step 1: stop endpoint */ /* skipped assuming that port suspend has done */ @@ -1117,7 +1125,8 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) msleep(100); set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - set_bit(HCD_FLAG_HW_ACCESSIBLE, &xhci->shared_hcd->flags); + if (xhci->shared_hcd) + set_bit(HCD_FLAG_HW_ACCESSIBLE, &xhci->shared_hcd->flags); spin_lock_irq(&xhci->lock); @@ -1177,7 +1186,8 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) /* Let the USB core know _both_ roothubs lost power. */ usb_root_hub_lost_power(xhci->main_hcd->self.root_hub); - usb_root_hub_lost_power(xhci->shared_hcd->self.root_hub); + if (xhci->shared_hcd) + usb_root_hub_lost_power(xhci->shared_hcd->self.root_hub); xhci_dbg(xhci, "Stop HCD\n"); xhci_halt(xhci); @@ -1217,12 +1227,13 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) xhci_dbg(xhci, "Start the primary HCD\n"); retval = xhci_run(hcd->primary_hcd); - if (!retval) { + if (!retval && secondary_hcd) { xhci_dbg(xhci, "Start the secondary HCD\n"); retval = xhci_run(secondary_hcd); } hcd->state = HC_STATE_SUSPENDED; - xhci->shared_hcd->state = HC_STATE_SUSPENDED; + if (xhci->shared_hcd) + xhci->shared_hcd->state = HC_STATE_SUSPENDED; goto done; } @@ -1260,7 +1271,8 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) } if (pending_portevent) { - usb_hcd_resume_root_hub(xhci->shared_hcd); + if (xhci->shared_hcd) + usb_hcd_resume_root_hub(xhci->shared_hcd); usb_hcd_resume_root_hub(hcd); } } @@ -1279,8 +1291,10 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) /* Re-enable port polling. */ xhci_dbg(xhci, "%s: starting usb%d port polling.\n", __func__, hcd->self.busnum); - set_bit(HCD_FLAG_POLL_RH, &xhci->shared_hcd->flags); - usb_hcd_poll_rh_status(xhci->shared_hcd); + if (xhci->shared_hcd) { + set_bit(HCD_FLAG_POLL_RH, &xhci->shared_hcd->flags); + usb_hcd_poll_rh_status(xhci->shared_hcd); + } set_bit(HCD_FLAG_POLL_RH, &hcd->flags); usb_hcd_poll_rh_status(hcd); @@ -1860,9 +1874,6 @@ static int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) goto done; } ep->ep_state |= EP_STOP_CMD_PENDING; - ep->stop_cmd_timer.expires = jiffies + - XHCI_STOP_EP_CMD_TIMEOUT * HZ; - add_timer(&ep->stop_cmd_timer); xhci_queue_stop_endpoint(xhci, command, urb->dev->slot_id, ep_index, 0); xhci_ring_cmd_db(xhci); @@ -3972,10 +3983,8 @@ static void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev) trace_xhci_free_dev(slot_ctx); /* Stop any wayward timer functions (which may grab the lock) */ - for (i = 0; i < 31; i++) { + for (i = 0; i < 31; i++) virt_dev->eps[i].ep_state &= ~EP_STOP_CMD_PENDING; - del_timer_sync(&virt_dev->eps[i].stop_cmd_timer); - } virt_dev->udev = NULL; xhci_disable_slot(xhci, udev->slot_id); xhci_free_virt_device(xhci, udev->slot_id); @@ -4879,9 +4888,6 @@ static int xhci_check_intel_tier_policy(struct usb_device *udev, struct usb_device *parent; unsigned int num_hubs; - if (state == USB3_LPM_U2) - return 0; - /* Don't enable U1 if the device is on a 2nd tier hub or lower. */ for (parent = udev->parent, num_hubs = 0; parent->parent; parent = parent->parent) @@ -4890,7 +4896,7 @@ static int xhci_check_intel_tier_policy(struct usb_device *udev, if (num_hubs < 2) return 0; - dev_dbg(&udev->dev, "Disabling U1 link state for device" + dev_dbg(&udev->dev, "Disabling U1/U2 link state for device" " below second-tier hub.\n"); dev_dbg(&udev->dev, "Plug device into first-tier hub " "to decrease power consumption.\n"); @@ -4931,9 +4937,6 @@ static u16 xhci_calculate_lpm_timeout(struct usb_hcd *hcd, return timeout; } - if (xhci_check_tier_policy(xhci, udev, state) < 0) - return timeout; - /* Gather some information about the currently installed configuration * and alternate interface settings. */ @@ -5040,6 +5043,9 @@ static int xhci_enable_usb3_lpm_timeout(struct usb_hcd *hcd, !xhci->devs[udev->slot_id]) return USB3_LPM_DISABLED; + if (xhci_check_tier_policy(xhci, udev, state) < 0) + return USB3_LPM_DISABLED; + hub_encoded_timeout = xhci_calculate_lpm_timeout(hcd, udev, state); mel = calculate_max_exit_latency(udev, state, hub_encoded_timeout); if (mel < 0) { @@ -5207,6 +5213,57 @@ static int xhci_get_frame(struct usb_hcd *hcd) return readl(&xhci->run_regs->microframe_index) >> 3; } +static void xhci_hcd_init_usb2_data(struct xhci_hcd *xhci, struct usb_hcd *hcd) +{ + xhci->usb2_rhub.hcd = hcd; + hcd->speed = HCD_USB2; + hcd->self.root_hub->speed = USB_SPEED_HIGH; + /* + * USB 2.0 roothub under xHCI has an integrated TT, + * (rate matching hub) as opposed to having an OHCI/UHCI + * companion controller. + */ + hcd->has_tt = 1; +} + +static void xhci_hcd_init_usb3_data(struct xhci_hcd *xhci, struct usb_hcd *hcd) +{ + unsigned int minor_rev; + + /* + * Early xHCI 1.1 spec did not mention USB 3.1 capable hosts + * should return 0x31 for sbrn, or that the minor revision + * is a two digit BCD containig minor and sub-minor numbers. + * This was later clarified in xHCI 1.2. + * + * Some USB 3.1 capable hosts therefore have sbrn 0x30, and + * minor revision set to 0x1 instead of 0x10. + */ + if (xhci->usb3_rhub.min_rev == 0x1) + minor_rev = 1; + else + minor_rev = xhci->usb3_rhub.min_rev / 0x10; + + switch (minor_rev) { + case 2: + hcd->speed = HCD_USB32; + hcd->self.root_hub->speed = USB_SPEED_SUPER_PLUS; + hcd->self.root_hub->rx_lanes = 2; + hcd->self.root_hub->tx_lanes = 2; + hcd->self.root_hub->ssp_rate = USB_SSP_GEN_2x2; + break; + case 1: + hcd->speed = HCD_USB31; + hcd->self.root_hub->speed = USB_SPEED_SUPER_PLUS; + hcd->self.root_hub->ssp_rate = USB_SSP_GEN_2x1; + break; + } + xhci_info(xhci, "Host supports USB 3.%x %sSuperSpeed\n", + minor_rev, minor_rev ? "Enhanced " : ""); + + xhci->usb3_rhub.hcd = hcd; +} + int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) { struct xhci_hcd *xhci; @@ -5215,7 +5272,6 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) * quirks */ struct device *dev = hcd->self.sysdev; - unsigned int minor_rev; int retval; /* Accept arbitrarily long scatter-gather lists */ @@ -5229,61 +5285,13 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) xhci = hcd_to_xhci(hcd); - if (usb_hcd_is_primary_hcd(hcd)) { - xhci->main_hcd = hcd; - xhci->usb2_rhub.hcd = hcd; - /* Mark the first roothub as being USB 2.0. - * The xHCI driver will register the USB 3.0 roothub. - */ - hcd->speed = HCD_USB2; - hcd->self.root_hub->speed = USB_SPEED_HIGH; - /* - * USB 2.0 roothub under xHCI has an integrated TT, - * (rate matching hub) as opposed to having an OHCI/UHCI - * companion controller. - */ - hcd->has_tt = 1; - } else { - /* - * Early xHCI 1.1 spec did not mention USB 3.1 capable hosts - * should return 0x31 for sbrn, or that the minor revision - * is a two digit BCD containig minor and sub-minor numbers. - * This was later clarified in xHCI 1.2. - * - * Some USB 3.1 capable hosts therefore have sbrn 0x30, and - * minor revision set to 0x1 instead of 0x10. - */ - if (xhci->usb3_rhub.min_rev == 0x1) - minor_rev = 1; - else - minor_rev = xhci->usb3_rhub.min_rev / 0x10; - - switch (minor_rev) { - case 2: - hcd->speed = HCD_USB32; - hcd->self.root_hub->speed = USB_SPEED_SUPER_PLUS; - hcd->self.root_hub->rx_lanes = 2; - hcd->self.root_hub->tx_lanes = 2; - hcd->self.root_hub->ssp_rate = USB_SSP_GEN_2x2; - break; - case 1: - hcd->speed = HCD_USB31; - hcd->self.root_hub->speed = USB_SPEED_SUPER_PLUS; - hcd->self.root_hub->ssp_rate = USB_SSP_GEN_2x1; - break; - } - xhci_info(xhci, "Host supports USB 3.%x %sSuperSpeed\n", - minor_rev, - minor_rev ? "Enhanced " : ""); - - xhci->usb3_rhub.hcd = hcd; - /* xHCI private pointer was set in xhci_pci_probe for the second - * registered roothub. - */ + if (!usb_hcd_is_primary_hcd(hcd)) { + xhci_hcd_init_usb3_data(xhci, hcd); return 0; } mutex_init(&xhci->mutex); + xhci->main_hcd = hcd; xhci->cap_regs = hcd->regs; xhci->op_regs = hcd->regs + HC_LENGTH(readl(&xhci->cap_regs->hc_capbase)); @@ -5358,6 +5366,11 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) return retval; xhci_dbg(xhci, "Called HCD init\n"); + if (xhci_hcd_is_usb3(hcd)) + xhci_hcd_init_usb3_data(xhci, hcd); + else + xhci_hcd_init_usb2_data(xhci, hcd); + xhci_info(xhci, "hcc params 0x%08x hci version 0x%x quirks 0x%016llx\n", xhci->hcc_params, xhci->hci_version, xhci->quirks); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 473a33ce299e..0bd76c94a4b1 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -948,8 +948,6 @@ struct xhci_virt_ep { #define EP_CLEARING_TT (1 << 8) /* ---- Related to URB cancellation ---- */ struct list_head cancelled_td_list; - /* Watchdog timer for stop endpoint command to cancel URBs */ - struct timer_list stop_cmd_timer; struct xhci_hcd *xhci; /* Dequeue pointer and dequeue segment for a submitted Set TR Dequeue * command. We'll need to update the ring's dequeue segment and dequeue @@ -1848,7 +1846,7 @@ struct xhci_hcd { #define XHCI_STATE_REMOVING (1 << 2) unsigned long long quirks; #define XHCI_LINK_TRB_QUIRK BIT_ULL(0) -#define XHCI_RESET_EP_QUIRK BIT_ULL(1) +#define XHCI_RESET_EP_QUIRK BIT_ULL(1) /* Deprecated */ #define XHCI_NEC_HOST BIT_ULL(2) #define XHCI_AMD_PLL_FIX BIT_ULL(3) #define XHCI_SPURIOUS_SUCCESS BIT_ULL(4) @@ -1911,6 +1909,8 @@ struct xhci_hcd { unsigned hw_lpm_support:1; /* Broken Suspend flag for SNPS Suspend resume issue */ unsigned broken_suspend:1; + /* Indicates that omitting hcd is supported if root hub has no ports */ + unsigned allow_single_roothub:1; /* cached usb2 extened protocol capabilites */ u32 *ext_caps; unsigned int num_ext_caps; @@ -1966,6 +1966,30 @@ static inline struct usb_hcd *xhci_to_hcd(struct xhci_hcd *xhci) return xhci->main_hcd; } +static inline struct usb_hcd *xhci_get_usb3_hcd(struct xhci_hcd *xhci) +{ + if (xhci->shared_hcd) + return xhci->shared_hcd; + + if (!xhci->usb2_rhub.num_ports) + return xhci->main_hcd; + + return NULL; +} + +static inline bool xhci_hcd_is_usb3(struct usb_hcd *hcd) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + + return hcd == xhci_get_usb3_hcd(xhci); +} + +static inline bool xhci_has_one_roothub(struct xhci_hcd *xhci) +{ + return xhci->allow_single_roothub && + (!xhci->usb2_rhub.num_ports || !xhci->usb3_rhub.num_ports); +} + #define xhci_dbg(xhci, fmt, args...) \ dev_dbg(xhci_to_hcd(xhci)->self.controller , fmt , ## args) #define xhci_err(xhci, fmt, args...) \ diff --git a/drivers/usb/isp1760/isp1760-core.c b/drivers/usb/isp1760/isp1760-core.c index d1d9a7d5da17..af88f4fe00d2 100644 --- a/drivers/usb/isp1760/isp1760-core.c +++ b/drivers/usb/isp1760/isp1760-core.c @@ -251,6 +251,8 @@ static const struct reg_field isp1760_hc_reg_fields[] = { [HW_DM_PULLDOWN] = REG_FIELD(ISP176x_HC_OTG_CTRL, 2, 2), [HW_DP_PULLDOWN] = REG_FIELD(ISP176x_HC_OTG_CTRL, 1, 1), [HW_DP_PULLUP] = REG_FIELD(ISP176x_HC_OTG_CTRL, 0, 0), + /* Make sure the array is sized properly during compilation */ + [HC_FIELD_MAX] = {}, }; static const struct reg_field isp1763_hc_reg_fields[] = { @@ -321,6 +323,8 @@ static const struct reg_field isp1763_hc_reg_fields[] = { [HW_DM_PULLDOWN_CLEAR] = REG_FIELD(ISP1763_HC_OTG_CTRL_CLEAR, 2, 2), [HW_DP_PULLDOWN_CLEAR] = REG_FIELD(ISP1763_HC_OTG_CTRL_CLEAR, 1, 1), [HW_DP_PULLUP_CLEAR] = REG_FIELD(ISP1763_HC_OTG_CTRL_CLEAR, 0, 0), + /* Make sure the array is sized properly during compilation */ + [HC_FIELD_MAX] = {}, }; static const struct regmap_range isp1763_hc_volatile_ranges[] = { @@ -405,6 +409,8 @@ static const struct reg_field isp1761_dc_reg_fields[] = { [DC_CHIP_ID_HIGH] = REG_FIELD(ISP176x_DC_CHIPID, 16, 31), [DC_CHIP_ID_LOW] = REG_FIELD(ISP176x_DC_CHIPID, 0, 15), [DC_SCRATCH] = REG_FIELD(ISP176x_DC_SCRATCH, 0, 15), + /* Make sure the array is sized properly during compilation */ + [DC_FIELD_MAX] = {}, }; static const struct regmap_range isp1763_dc_volatile_ranges[] = { @@ -458,6 +464,8 @@ static const struct reg_field isp1763_dc_reg_fields[] = { [DC_CHIP_ID_HIGH] = REG_FIELD(ISP1763_DC_CHIPID_HIGH, 0, 15), [DC_CHIP_ID_LOW] = REG_FIELD(ISP1763_DC_CHIPID_LOW, 0, 15), [DC_SCRATCH] = REG_FIELD(ISP1763_DC_SCRATCH, 0, 15), + /* Make sure the array is sized properly during compilation */ + [DC_FIELD_MAX] = {}, }; static const struct regmap_config isp1763_dc_regmap_conf = { diff --git a/drivers/usb/isp1760/isp1760-hcd.c b/drivers/usb/isp1760/isp1760-hcd.c index 893becb077d3..76862ba40f35 100644 --- a/drivers/usb/isp1760/isp1760-hcd.c +++ b/drivers/usb/isp1760/isp1760-hcd.c @@ -825,8 +825,7 @@ static void create_ptd_atl(struct isp1760_qh *qh, memset(ptd, 0, sizeof(*ptd)); /* according to 3.6.2, max packet len can not be > 0x400 */ - maxpacket = usb_maxpacket(qtd->urb->dev, qtd->urb->pipe, - usb_pipeout(qtd->urb->pipe)); + maxpacket = usb_maxpacket(qtd->urb->dev, qtd->urb->pipe); multi = 1 + ((maxpacket >> 11) & 0x3); maxpacket &= 0x7ff; @@ -1808,8 +1807,7 @@ static void packetize_urb(struct usb_hcd *hcd, packet_type = IN_PID; } - maxpacketsize = usb_maxpacket(urb->dev, urb->pipe, - usb_pipeout(urb->pipe)); + maxpacketsize = usb_maxpacket(urb->dev, urb->pipe); /* * buffer gets wrapped in one or more qtds; diff --git a/drivers/usb/misc/ftdi-elan.c b/drivers/usb/misc/ftdi-elan.c index 6c38c62d29b2..b2f980409d0b 100644 --- a/drivers/usb/misc/ftdi-elan.c +++ b/drivers/usb/misc/ftdi-elan.c @@ -1449,8 +1449,7 @@ wait:if (ftdi->disconnected > 0) { command->length = 0x8007; command->address = (toggle_bits << 6) | (ep_number << 2) | (address << 0); - command->width = usb_maxpacket(urb->dev, urb->pipe, - usb_pipeout(urb->pipe)); + command->width = usb_maxpacket(urb->dev, urb->pipe); command->follows = 8; command->value = 0; command->buffer = urb->setup_packet; @@ -1514,8 +1513,7 @@ wait:if (ftdi->disconnected > 0) { 1); command->address = (toggle_bits << 6) | (ep_number << 2) | (address << 0); - command->width = usb_maxpacket(urb->dev, urb->pipe, - usb_pipeout(urb->pipe)); + command->width = usb_maxpacket(urb->dev, urb->pipe); command->follows = 0; command->value = 0; command->buffer = NULL; @@ -1571,8 +1569,7 @@ wait:if (ftdi->disconnected > 0) { command->length = 0x0000; command->address = (toggle_bits << 6) | (ep_number << 2) | (address << 0); - command->width = usb_maxpacket(urb->dev, urb->pipe, - usb_pipeout(urb->pipe)); + command->width = usb_maxpacket(urb->dev, urb->pipe); command->follows = 0; command->value = 0; command->buffer = NULL; @@ -1634,8 +1631,7 @@ wait:if (ftdi->disconnected > 0) { command->header = 0x81 | (ed << 5); command->address = (toggle_bits << 6) | (ep_number << 2) | (address << 0); - command->width = usb_maxpacket(urb->dev, urb->pipe, - usb_pipeout(urb->pipe)); + command->width = usb_maxpacket(urb->dev, urb->pipe); command->follows = min_t(u32, 1024, urb->transfer_buffer_length - urb->actual_length); @@ -1715,8 +1711,7 @@ wait:if (ftdi->disconnected > 0) { 1); command->address = (toggle_bits << 6) | (ep_number << 2) | (address << 0); - command->width = usb_maxpacket(urb->dev, urb->pipe, - usb_pipeout(urb->pipe)); + command->width = usb_maxpacket(urb->dev, urb->pipe); command->follows = 0; command->value = 0; command->buffer = NULL; diff --git a/drivers/usb/misc/lvstest.c b/drivers/usb/misc/lvstest.c index f8686139d6f3..25ec5666a75e 100644 --- a/drivers/usb/misc/lvstest.c +++ b/drivers/usb/misc/lvstest.c @@ -437,7 +437,7 @@ static int lvs_rh_probe(struct usb_interface *intf, INIT_WORK(&lvs->rh_work, lvs_rh_work); pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress); - maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe)); + maxp = usb_maxpacket(hdev, pipe); usb_fill_int_urb(lvs->urb, hdev, pipe, &lvs->buffer[0], maxp, lvs_rh_irq, lvs, endpoint->bInterval); diff --git a/drivers/usb/musb/mediatek.c b/drivers/usb/musb/mediatek.c index 1aeb34dbe24f..cad991380b0c 100644 --- a/drivers/usb/musb/mediatek.c +++ b/drivers/usb/musb/mediatek.c @@ -36,6 +36,8 @@ #define DMA_INTR_STATUS_MSK GENMASK(7, 0) #define DMA_INTR_UNMASK_SET_MSK GENMASK(31, 24) +#define MTK_MUSB_CLKS_NUM 3 + struct mtk_glue { struct device *dev; struct musb *musb; @@ -44,9 +46,7 @@ struct mtk_glue { struct phy *phy; struct usb_phy *xceiv; enum phy_mode phy_mode; - struct clk *main; - struct clk *mcu; - struct clk *univpll; + struct clk_bulk_data clks[MTK_MUSB_CLKS_NUM]; enum usb_role role; struct usb_role_switch *role_sw; }; @@ -55,64 +55,11 @@ static int mtk_musb_clks_get(struct mtk_glue *glue) { struct device *dev = glue->dev; - glue->main = devm_clk_get(dev, "main"); - if (IS_ERR(glue->main)) { - dev_err(dev, "fail to get main clock\n"); - return PTR_ERR(glue->main); - } - - glue->mcu = devm_clk_get(dev, "mcu"); - if (IS_ERR(glue->mcu)) { - dev_err(dev, "fail to get mcu clock\n"); - return PTR_ERR(glue->mcu); - } - - glue->univpll = devm_clk_get(dev, "univpll"); - if (IS_ERR(glue->univpll)) { - dev_err(dev, "fail to get univpll clock\n"); - return PTR_ERR(glue->univpll); - } - - return 0; -} + glue->clks[0].id = "main"; + glue->clks[1].id = "mcu"; + glue->clks[2].id = "univpll"; -static int mtk_musb_clks_enable(struct mtk_glue *glue) -{ - int ret; - - ret = clk_prepare_enable(glue->main); - if (ret) { - dev_err(glue->dev, "failed to enable main clock\n"); - goto err_main_clk; - } - - ret = clk_prepare_enable(glue->mcu); - if (ret) { - dev_err(glue->dev, "failed to enable mcu clock\n"); - goto err_mcu_clk; - } - - ret = clk_prepare_enable(glue->univpll); - if (ret) { - dev_err(glue->dev, "failed to enable univpll clock\n"); - goto err_univpll_clk; - } - - return 0; - -err_univpll_clk: - clk_disable_unprepare(glue->mcu); -err_mcu_clk: - clk_disable_unprepare(glue->main); -err_main_clk: - return ret; -} - -static void mtk_musb_clks_disable(struct mtk_glue *glue) -{ - clk_disable_unprepare(glue->univpll); - clk_disable_unprepare(glue->mcu); - clk_disable_unprepare(glue->main); + return devm_clk_bulk_get(dev, MTK_MUSB_CLKS_NUM, glue->clks); } static int mtk_otg_switch_set(struct mtk_glue *glue, enum usb_role role) @@ -390,7 +337,7 @@ static int mtk_musb_exit(struct musb *musb) mtk_otg_switch_exit(glue); phy_power_off(glue->phy); phy_exit(glue->phy); - mtk_musb_clks_disable(glue); + clk_bulk_disable_unprepare(MTK_MUSB_CLKS_NUM, glue->clks); pm_runtime_put_sync(dev); pm_runtime_disable(dev); @@ -528,7 +475,7 @@ static int mtk_musb_probe(struct platform_device *pdev) pm_runtime_enable(dev); pm_runtime_get_sync(dev); - ret = mtk_musb_clks_enable(glue); + ret = clk_bulk_prepare_enable(MTK_MUSB_CLKS_NUM, glue->clks); if (ret) goto err_enable_clk; @@ -551,7 +498,7 @@ static int mtk_musb_probe(struct platform_device *pdev) return 0; err_device_register: - mtk_musb_clks_disable(glue); + clk_bulk_disable_unprepare(MTK_MUSB_CLKS_NUM, glue->clks); err_enable_clk: pm_runtime_put_sync(dev); pm_runtime_disable(dev); diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index d2b7e613eb34..f571a65ae6ee 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -362,6 +362,7 @@ static int omap2430_probe(struct platform_device *pdev) control_node = of_parse_phandle(np, "ctrl-module", 0); if (control_node) { control_pdev = of_find_device_by_node(control_node); + of_node_put(control_node); if (!control_pdev) { dev_err(&pdev->dev, "Failed to get control device\n"); ret = -EINVAL; diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 49c08f07c969..b440d338a895 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1671,7 +1671,7 @@ static ssize_t latency_timer_show(struct device *dev, if (priv->flags & ASYNC_LOW_LATENCY) return sprintf(buf, "1\n"); else - return sprintf(buf, "%i\n", priv->latency); + return sprintf(buf, "%u\n", priv->latency); } /* Write a new value of the latency timer, in units of milliseconds. */ diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 152ad882657d..e60425bbf537 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -1137,6 +1137,8 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM12, 0xff, 0, 0) }, { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, 0x0620, 0xff, 0xff, 0x30) }, /* EM160R-GL */ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, 0x0620, 0xff, 0, 0) }, + { USB_DEVICE_INTERFACE_CLASS(QUECTEL_VENDOR_ID, 0x0700, 0xff), /* BG95 */ + .driver_info = RSVD(3) | ZLP }, { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RM500Q, 0xff, 0xff, 0x30) }, { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RM500Q, 0xff, 0, 0) }, { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RM500Q, 0xff, 0xff, 0x10), diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 1d878d05a658..3506c47e1eef 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -421,6 +421,9 @@ static int pl2303_detect_type(struct usb_serial *serial) bcdUSB = le16_to_cpu(desc->bcdUSB); switch (bcdUSB) { + case 0x101: + /* USB 1.0.1? Let's assume they meant 1.1... */ + fallthrough; case 0x110: switch (bcdDevice) { case 0x300: diff --git a/drivers/usb/storage/alauda.c b/drivers/usb/storage/alauda.c index 20b857e97e60..747be69e5e69 100644 --- a/drivers/usb/storage/alauda.c +++ b/drivers/usb/storage/alauda.c @@ -1104,7 +1104,7 @@ static int init_alauda(struct us_data *us) us->extra = kzalloc(sizeof(struct alauda_info), GFP_NOIO); if (!us->extra) - return USB_STOR_TRANSPORT_ERROR; + return -ENOMEM; info = (struct alauda_info *) us->extra; us->extra_destructor = alauda_info_destructor; @@ -1113,7 +1113,7 @@ static int init_alauda(struct us_data *us) altsetting->endpoint[0].desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); - return USB_STOR_TRANSPORT_GOOD; + return 0; } static int alauda_transport(struct scsi_cmnd *srb, struct us_data *us) diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c index 05429f1f69f9..4e0eef1440b7 100644 --- a/drivers/usb/storage/isd200.c +++ b/drivers/usb/storage/isd200.c @@ -1449,7 +1449,7 @@ static void isd200_free_info_ptrs(void *info_) * Allocates (if necessary) and initializes the driver structure. * * RETURNS: - * ISD status code + * error status code */ static int isd200_init_info(struct us_data *us) { @@ -1457,7 +1457,7 @@ static int isd200_init_info(struct us_data *us) info = kzalloc(sizeof(struct isd200_info), GFP_KERNEL); if (!info) - return ISD200_ERROR; + return -ENOMEM; info->id = kzalloc(ATA_ID_WORDS * 2, GFP_KERNEL); info->RegsBuf = kmalloc(sizeof(info->ATARegs), GFP_KERNEL); @@ -1466,13 +1466,13 @@ static int isd200_init_info(struct us_data *us) if (!info->id || !info->RegsBuf || !info->srb.sense_buffer) { isd200_free_info_ptrs(info); kfree(info); - return ISD200_ERROR; + return -ENOMEM; } us->extra = info; us->extra_destructor = isd200_free_info_ptrs; - return ISD200_GOOD; + return 0; } /************************************************************************** diff --git a/drivers/usb/storage/karma.c b/drivers/usb/storage/karma.c index 05cec81dcd3f..38ddfedef629 100644 --- a/drivers/usb/storage/karma.c +++ b/drivers/usb/storage/karma.c @@ -174,24 +174,25 @@ static void rio_karma_destructor(void *extra) static int rio_karma_init(struct us_data *us) { - int ret = 0; struct karma_data *data = kzalloc(sizeof(struct karma_data), GFP_NOIO); if (!data) - goto out; + return -ENOMEM; data->recv = kmalloc(RIO_RECV_LEN, GFP_NOIO); if (!data->recv) { kfree(data); - goto out; + return -ENOMEM; } us->extra = data; us->extra_destructor = rio_karma_destructor; - ret = rio_karma_send_command(RIO_ENTER_STORAGE, us); - data->in_storage = (ret == 0); -out: - return ret; + if (rio_karma_send_command(RIO_ENTER_STORAGE, us)) + return -EIO; + + data->in_storage = 1; + + return 0; } static struct scsi_host_template karma_host_template; diff --git a/drivers/usb/storage/onetouch.c b/drivers/usb/storage/onetouch.c index a989fe930e21..1db2eefeea22 100644 --- a/drivers/usb/storage/onetouch.c +++ b/drivers/usb/storage/onetouch.c @@ -180,7 +180,7 @@ static int onetouch_connect_input(struct us_data *ss) return -ENODEV; pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress); - maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); + maxp = usb_maxpacket(udev, pipe); maxp = min(maxp, ONETOUCH_PKT_LEN); onetouch = kzalloc(sizeof(struct usb_onetouch), GFP_KERNEL); diff --git a/drivers/usb/storage/shuttle_usbat.c b/drivers/usb/storage/shuttle_usbat.c index 54aa1392c9ca..f0d0ca37163d 100644 --- a/drivers/usb/storage/shuttle_usbat.c +++ b/drivers/usb/storage/shuttle_usbat.c @@ -1456,7 +1456,7 @@ static int init_usbat(struct us_data *us, int devicetype) us->extra = kzalloc(sizeof(struct usbat_info), GFP_NOIO); if (!us->extra) - return 1; + return -ENOMEM; info = (struct usbat_info *) (us->extra); @@ -1465,7 +1465,7 @@ static int init_usbat(struct us_data *us, int devicetype) USBAT_UIO_OE1 | USBAT_UIO_OE0, USBAT_UIO_EPAD | USBAT_UIO_1); if (rc != USB_STOR_XFER_GOOD) - return USB_STOR_TRANSPORT_ERROR; + return -EIO; usb_stor_dbg(us, "INIT 1\n"); @@ -1473,42 +1473,42 @@ static int init_usbat(struct us_data *us, int devicetype) rc = usbat_read_user_io(us, status); if (rc != USB_STOR_TRANSPORT_GOOD) - return rc; + return -EIO; usb_stor_dbg(us, "INIT 2\n"); rc = usbat_read_user_io(us, status); if (rc != USB_STOR_XFER_GOOD) - return USB_STOR_TRANSPORT_ERROR; + return -EIO; rc = usbat_read_user_io(us, status); if (rc != USB_STOR_XFER_GOOD) - return USB_STOR_TRANSPORT_ERROR; + return -EIO; usb_stor_dbg(us, "INIT 3\n"); rc = usbat_select_and_test_registers(us); if (rc != USB_STOR_TRANSPORT_GOOD) - return rc; + return -EIO; usb_stor_dbg(us, "INIT 4\n"); rc = usbat_read_user_io(us, status); if (rc != USB_STOR_XFER_GOOD) - return USB_STOR_TRANSPORT_ERROR; + return -EIO; usb_stor_dbg(us, "INIT 5\n"); /* Enable peripheral control signals and card detect */ rc = usbat_device_enable_cdt(us); if (rc != USB_STOR_TRANSPORT_GOOD) - return rc; + return -EIO; usb_stor_dbg(us, "INIT 6\n"); rc = usbat_read_user_io(us, status); if (rc != USB_STOR_XFER_GOOD) - return USB_STOR_TRANSPORT_ERROR; + return -EIO; usb_stor_dbg(us, "INIT 7\n"); @@ -1516,19 +1516,19 @@ static int init_usbat(struct us_data *us, int devicetype) rc = usbat_read_user_io(us, status); if (rc != USB_STOR_XFER_GOOD) - return USB_STOR_TRANSPORT_ERROR; + return -EIO; usb_stor_dbg(us, "INIT 8\n"); rc = usbat_select_and_test_registers(us); if (rc != USB_STOR_TRANSPORT_GOOD) - return rc; + return -EIO; usb_stor_dbg(us, "INIT 9\n"); /* At this point, we need to detect which device we are using */ if (usbat_set_transport(us, info, devicetype)) - return USB_STOR_TRANSPORT_ERROR; + return -EIO; usb_stor_dbg(us, "INIT 10\n"); @@ -1539,11 +1539,11 @@ static int init_usbat(struct us_data *us, int devicetype) rc = usbat_set_shuttle_features(us, (USBAT_FEAT_ETEN | USBAT_FEAT_ET2 | USBAT_FEAT_ET1), 0x00, 0x88, 0x08, subcountH, subcountL); if (rc != USB_STOR_XFER_GOOD) - return USB_STOR_TRANSPORT_ERROR; + return -EIO; usb_stor_dbg(us, "INIT 11\n"); - return USB_STOR_TRANSPORT_GOOD; + return 0; } /* diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index 1928b3918242..64d96d210e02 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -363,7 +363,7 @@ static int usb_stor_intr_transfer(struct us_data *us, void *buf, usb_stor_dbg(us, "xfer %u bytes\n", length); /* calculate the max packet size */ - maxp = usb_maxpacket(us->pusb_dev, pipe, usb_pipeout(pipe)); + maxp = usb_maxpacket(us->pusb_dev, pipe); if (maxp > length) maxp = length; diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c index 78e0e78954f2..26ea2fdec17d 100644 --- a/drivers/usb/typec/bus.c +++ b/drivers/usb/typec/bus.c @@ -24,7 +24,7 @@ typec_altmode_set_mux(struct altmode *alt, unsigned long conf, void *data) state.mode = conf; state.data = data; - return alt->mux->set(alt->mux, &state); + return typec_mux_set(alt->mux, &state); } static int typec_altmode_set_state(struct typec_altmode *adev, diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c index c8340de0ed49..fd55c2c516a5 100644 --- a/drivers/usb/typec/mux.c +++ b/drivers/usb/typec/mux.c @@ -17,9 +17,16 @@ #include "class.h" #include "mux.h" +#define TYPEC_MUX_MAX_DEVS 3 + +struct typec_switch { + struct typec_switch_dev *sw_devs[TYPEC_MUX_MAX_DEVS]; + unsigned int num_sw_devs; +}; + static int switch_fwnode_match(struct device *dev, const void *fwnode) { - if (!is_typec_switch(dev)) + if (!is_typec_switch_dev(dev)) return 0; return dev_fwnode(dev) == fwnode; @@ -49,7 +56,7 @@ static void *typec_switch_match(struct fwnode_handle *fwnode, const char *id, dev = class_find_device(&typec_mux_class, NULL, fwnode, switch_fwnode_match); - return dev ? to_typec_switch(dev) : ERR_PTR(-EPROBE_DEFER); + return dev ? to_typec_switch_dev(dev) : ERR_PTR(-EPROBE_DEFER); } /** @@ -63,14 +70,50 @@ static void *typec_switch_match(struct fwnode_handle *fwnode, const char *id, */ struct typec_switch *fwnode_typec_switch_get(struct fwnode_handle *fwnode) { + struct typec_switch_dev *sw_devs[TYPEC_MUX_MAX_DEVS]; struct typec_switch *sw; + int count; + int err; + int i; + + sw = kzalloc(sizeof(*sw), GFP_KERNEL); + if (!sw) + return ERR_PTR(-ENOMEM); + + count = fwnode_connection_find_matches(fwnode, "orientation-switch", NULL, + typec_switch_match, + (void **)sw_devs, + ARRAY_SIZE(sw_devs)); + if (count <= 0) { + kfree(sw); + return NULL; + } - sw = fwnode_connection_find_match(fwnode, "orientation-switch", NULL, - typec_switch_match); - if (!IS_ERR_OR_NULL(sw)) - WARN_ON(!try_module_get(sw->dev.parent->driver->owner)); + for (i = 0; i < count; i++) { + if (IS_ERR(sw_devs[i])) { + err = PTR_ERR(sw_devs[i]); + goto put_sw_devs; + } + } + + for (i = 0; i < count; i++) { + WARN_ON(!try_module_get(sw_devs[i]->dev.parent->driver->owner)); + sw->sw_devs[i] = sw_devs[i]; + } + + sw->num_sw_devs = count; return sw; + +put_sw_devs: + for (i = 0; i < count; i++) { + if (!IS_ERR(sw_devs[i])) + put_device(&sw_devs[i]->dev); + } + + kfree(sw); + + return ERR_PTR(err); } EXPORT_SYMBOL_GPL(fwnode_typec_switch_get); @@ -82,16 +125,25 @@ EXPORT_SYMBOL_GPL(fwnode_typec_switch_get); */ void typec_switch_put(struct typec_switch *sw) { - if (!IS_ERR_OR_NULL(sw)) { - module_put(sw->dev.parent->driver->owner); - put_device(&sw->dev); + struct typec_switch_dev *sw_dev; + unsigned int i; + + if (IS_ERR_OR_NULL(sw)) + return; + + for (i = 0; i < sw->num_sw_devs; i++) { + sw_dev = sw->sw_devs[i]; + + module_put(sw_dev->dev.parent->driver->owner); + put_device(&sw_dev->dev); } + kfree(sw); } EXPORT_SYMBOL_GPL(typec_switch_put); static void typec_switch_release(struct device *dev) { - kfree(to_typec_switch(dev)); + kfree(to_typec_switch_dev(dev)); } const struct device_type typec_switch_dev_type = { @@ -109,82 +161,102 @@ const struct device_type typec_switch_dev_type = { * connector to the USB controllers. USB Type-C plugs can be inserted * right-side-up or upside-down. */ -struct typec_switch * +struct typec_switch_dev * typec_switch_register(struct device *parent, const struct typec_switch_desc *desc) { - struct typec_switch *sw; + struct typec_switch_dev *sw_dev; int ret; if (!desc || !desc->set) return ERR_PTR(-EINVAL); - sw = kzalloc(sizeof(*sw), GFP_KERNEL); - if (!sw) + sw_dev = kzalloc(sizeof(*sw_dev), GFP_KERNEL); + if (!sw_dev) return ERR_PTR(-ENOMEM); - sw->set = desc->set; + sw_dev->set = desc->set; - device_initialize(&sw->dev); - sw->dev.parent = parent; - sw->dev.fwnode = desc->fwnode; - sw->dev.class = &typec_mux_class; - sw->dev.type = &typec_switch_dev_type; - sw->dev.driver_data = desc->drvdata; - dev_set_name(&sw->dev, "%s-switch", - desc->name ? desc->name : dev_name(parent)); + device_initialize(&sw_dev->dev); + sw_dev->dev.parent = parent; + sw_dev->dev.fwnode = desc->fwnode; + sw_dev->dev.class = &typec_mux_class; + sw_dev->dev.type = &typec_switch_dev_type; + sw_dev->dev.driver_data = desc->drvdata; + ret = dev_set_name(&sw_dev->dev, "%s-switch", desc->name ? desc->name : dev_name(parent)); + if (ret) { + put_device(&sw_dev->dev); + return ERR_PTR(ret); + } - ret = device_add(&sw->dev); + ret = device_add(&sw_dev->dev); if (ret) { dev_err(parent, "failed to register switch (%d)\n", ret); - put_device(&sw->dev); + put_device(&sw_dev->dev); return ERR_PTR(ret); } - return sw; + return sw_dev; } EXPORT_SYMBOL_GPL(typec_switch_register); int typec_switch_set(struct typec_switch *sw, enum typec_orientation orientation) { + struct typec_switch_dev *sw_dev; + unsigned int i; + int ret; + if (IS_ERR_OR_NULL(sw)) return 0; - return sw->set(sw, orientation); + for (i = 0; i < sw->num_sw_devs; i++) { + sw_dev = sw->sw_devs[i]; + + ret = sw_dev->set(sw_dev, orientation); + if (ret) + return ret; + } + + return 0; } EXPORT_SYMBOL_GPL(typec_switch_set); /** * typec_switch_unregister - Unregister USB Type-C orientation switch - * @sw: USB Type-C orientation switch + * @sw_dev: USB Type-C orientation switch * * Unregister switch that was registered with typec_switch_register(). */ -void typec_switch_unregister(struct typec_switch *sw) +void typec_switch_unregister(struct typec_switch_dev *sw_dev) { - if (!IS_ERR_OR_NULL(sw)) - device_unregister(&sw->dev); + if (!IS_ERR_OR_NULL(sw_dev)) + device_unregister(&sw_dev->dev); } EXPORT_SYMBOL_GPL(typec_switch_unregister); -void typec_switch_set_drvdata(struct typec_switch *sw, void *data) +void typec_switch_set_drvdata(struct typec_switch_dev *sw_dev, void *data) { - dev_set_drvdata(&sw->dev, data); + dev_set_drvdata(&sw_dev->dev, data); } EXPORT_SYMBOL_GPL(typec_switch_set_drvdata); -void *typec_switch_get_drvdata(struct typec_switch *sw) +void *typec_switch_get_drvdata(struct typec_switch_dev *sw_dev) { - return dev_get_drvdata(&sw->dev); + return dev_get_drvdata(&sw_dev->dev); } EXPORT_SYMBOL_GPL(typec_switch_get_drvdata); /* ------------------------------------------------------------------------- */ +struct typec_mux { + struct typec_mux_dev *mux_devs[TYPEC_MUX_MAX_DEVS]; + unsigned int num_mux_devs; +}; + static int mux_fwnode_match(struct device *dev, const void *fwnode) { - if (!is_typec_mux(dev)) + if (!is_typec_mux_dev(dev)) return 0; return dev_fwnode(dev) == fwnode; @@ -246,7 +318,7 @@ find_mux: dev = class_find_device(&typec_mux_class, NULL, fwnode, mux_fwnode_match); - return dev ? to_typec_mux(dev) : ERR_PTR(-EPROBE_DEFER); + return dev ? to_typec_mux_dev(dev) : ERR_PTR(-EPROBE_DEFER); } /** @@ -262,14 +334,50 @@ find_mux: struct typec_mux *fwnode_typec_mux_get(struct fwnode_handle *fwnode, const struct typec_altmode_desc *desc) { + struct typec_mux_dev *mux_devs[TYPEC_MUX_MAX_DEVS]; struct typec_mux *mux; + int count; + int err; + int i; + + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) + return ERR_PTR(-ENOMEM); + + count = fwnode_connection_find_matches(fwnode, "mode-switch", + (void *)desc, typec_mux_match, + (void **)mux_devs, + ARRAY_SIZE(mux_devs)); + if (count <= 0) { + kfree(mux); + return NULL; + } - mux = fwnode_connection_find_match(fwnode, "mode-switch", (void *)desc, - typec_mux_match); - if (!IS_ERR_OR_NULL(mux)) - WARN_ON(!try_module_get(mux->dev.parent->driver->owner)); + for (i = 0; i < count; i++) { + if (IS_ERR(mux_devs[i])) { + err = PTR_ERR(mux_devs[i]); + goto put_mux_devs; + } + } + + for (i = 0; i < count; i++) { + WARN_ON(!try_module_get(mux_devs[i]->dev.parent->driver->owner)); + mux->mux_devs[i] = mux_devs[i]; + } + + mux->num_mux_devs = count; return mux; + +put_mux_devs: + for (i = 0; i < count; i++) { + if (!IS_ERR(mux_devs[i])) + put_device(&mux_devs[i]->dev); + } + + kfree(mux); + + return ERR_PTR(err); } EXPORT_SYMBOL_GPL(fwnode_typec_mux_get); @@ -281,25 +389,45 @@ EXPORT_SYMBOL_GPL(fwnode_typec_mux_get); */ void typec_mux_put(struct typec_mux *mux) { - if (!IS_ERR_OR_NULL(mux)) { - module_put(mux->dev.parent->driver->owner); - put_device(&mux->dev); + struct typec_mux_dev *mux_dev; + unsigned int i; + + if (IS_ERR_OR_NULL(mux)) + return; + + for (i = 0; i < mux->num_mux_devs; i++) { + mux_dev = mux->mux_devs[i]; + module_put(mux_dev->dev.parent->driver->owner); + put_device(&mux_dev->dev); } + kfree(mux); } EXPORT_SYMBOL_GPL(typec_mux_put); int typec_mux_set(struct typec_mux *mux, struct typec_mux_state *state) { + struct typec_mux_dev *mux_dev; + unsigned int i; + int ret; + if (IS_ERR_OR_NULL(mux)) return 0; - return mux->set(mux, state); + for (i = 0; i < mux->num_mux_devs; i++) { + mux_dev = mux->mux_devs[i]; + + ret = mux_dev->set(mux_dev, state); + if (ret) + return ret; + } + + return 0; } EXPORT_SYMBOL_GPL(typec_mux_set); static void typec_mux_release(struct device *dev) { - kfree(to_typec_mux(dev)); + kfree(to_typec_mux_dev(dev)); } const struct device_type typec_mux_dev_type = { @@ -317,63 +445,66 @@ const struct device_type typec_mux_dev_type = { * the pins on the connector need to be reconfigured. This function registers * multiplexer switches routing the pins on the connector. */ -struct typec_mux * +struct typec_mux_dev * typec_mux_register(struct device *parent, const struct typec_mux_desc *desc) { - struct typec_mux *mux; + struct typec_mux_dev *mux_dev; int ret; if (!desc || !desc->set) return ERR_PTR(-EINVAL); - mux = kzalloc(sizeof(*mux), GFP_KERNEL); - if (!mux) + mux_dev = kzalloc(sizeof(*mux_dev), GFP_KERNEL); + if (!mux_dev) return ERR_PTR(-ENOMEM); - mux->set = desc->set; + mux_dev->set = desc->set; - device_initialize(&mux->dev); - mux->dev.parent = parent; - mux->dev.fwnode = desc->fwnode; - mux->dev.class = &typec_mux_class; - mux->dev.type = &typec_mux_dev_type; - mux->dev.driver_data = desc->drvdata; - dev_set_name(&mux->dev, "%s-mux", - desc->name ? desc->name : dev_name(parent)); + device_initialize(&mux_dev->dev); + mux_dev->dev.parent = parent; + mux_dev->dev.fwnode = desc->fwnode; + mux_dev->dev.class = &typec_mux_class; + mux_dev->dev.type = &typec_mux_dev_type; + mux_dev->dev.driver_data = desc->drvdata; + ret = dev_set_name(&mux_dev->dev, "%s-mux", desc->name ? desc->name : dev_name(parent)); + if (ret) { + put_device(&mux_dev->dev); + return ERR_PTR(ret); + } - ret = device_add(&mux->dev); + ret = device_add(&mux_dev->dev); if (ret) { dev_err(parent, "failed to register mux (%d)\n", ret); - put_device(&mux->dev); + put_device(&mux_dev->dev); return ERR_PTR(ret); } - return mux; + return mux_dev; } EXPORT_SYMBOL_GPL(typec_mux_register); /** * typec_mux_unregister - Unregister Multiplexer Switch - * @mux: USB Type-C Connector Multiplexer/DeMultiplexer + * @mux_dev: USB Type-C Connector Multiplexer/DeMultiplexer * * Unregister mux that was registered with typec_mux_register(). */ -void typec_mux_unregister(struct typec_mux *mux) +void typec_mux_unregister(struct typec_mux_dev *mux_dev) { - if (!IS_ERR_OR_NULL(mux)) - device_unregister(&mux->dev); + if (!IS_ERR_OR_NULL(mux_dev)) + device_unregister(&mux_dev->dev); } EXPORT_SYMBOL_GPL(typec_mux_unregister); -void typec_mux_set_drvdata(struct typec_mux *mux, void *data) +void typec_mux_set_drvdata(struct typec_mux_dev *mux_dev, void *data) { - dev_set_drvdata(&mux->dev, data); + dev_set_drvdata(&mux_dev->dev, data); } EXPORT_SYMBOL_GPL(typec_mux_set_drvdata); -void *typec_mux_get_drvdata(struct typec_mux *mux) +void *typec_mux_get_drvdata(struct typec_mux_dev *mux_dev) { - return dev_get_drvdata(&mux->dev); + return dev_get_drvdata(&mux_dev->dev); } EXPORT_SYMBOL_GPL(typec_mux_get_drvdata); diff --git a/drivers/usb/typec/mux.h b/drivers/usb/typec/mux.h index b1d6e837cb74..58f0f28b6dc8 100644 --- a/drivers/usb/typec/mux.h +++ b/drivers/usb/typec/mux.h @@ -5,23 +5,23 @@ #include <linux/usb/typec_mux.h> -struct typec_switch { +struct typec_switch_dev { struct device dev; typec_switch_set_fn_t set; }; -struct typec_mux { +struct typec_mux_dev { struct device dev; typec_mux_set_fn_t set; }; -#define to_typec_switch(_dev_) container_of(_dev_, struct typec_switch, dev) -#define to_typec_mux(_dev_) container_of(_dev_, struct typec_mux, dev) +#define to_typec_switch_dev(_dev_) container_of(_dev_, struct typec_switch_dev, dev) +#define to_typec_mux_dev(_dev_) container_of(_dev_, struct typec_mux_dev, dev) extern const struct device_type typec_switch_dev_type; extern const struct device_type typec_mux_dev_type; -#define is_typec_switch(dev) ((dev)->type == &typec_switch_dev_type) -#define is_typec_mux(dev) ((dev)->type == &typec_mux_dev_type) +#define is_typec_switch_dev(dev) ((dev)->type == &typec_switch_dev_type) +#define is_typec_mux_dev(dev) ((dev)->type == &typec_mux_dev_type) #endif /* __USB_TYPEC_MUX__ */ diff --git a/drivers/usb/typec/mux/Kconfig b/drivers/usb/typec/mux/Kconfig index edead555835e..5eb2c17d72c1 100644 --- a/drivers/usb/typec/mux/Kconfig +++ b/drivers/usb/typec/mux/Kconfig @@ -2,6 +2,16 @@ menu "USB Type-C Multiplexer/DeMultiplexer Switch support" +config TYPEC_MUX_FSA4480 + tristate "ON Semi FSA4480 Analog Audio Switch driver" + depends on I2C + select REGMAP_I2C + help + Driver for the ON Semiconductor FSA4480 Analog Audio Switch, which + provides support for muxing analog audio and sideband signals on a + common USB Type-C connector. + If compiled as a module, the module will be named fsa4480. + config TYPEC_MUX_PI3USB30532 tristate "Pericom PI3USB30532 Type-C cross switch driver" depends on I2C diff --git a/drivers/usb/typec/mux/Makefile b/drivers/usb/typec/mux/Makefile index 280a6f553115..e52a56c16bfb 100644 --- a/drivers/usb/typec/mux/Makefile +++ b/drivers/usb/typec/mux/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_TYPEC_MUX_FSA4480) += fsa4480.o obj-$(CONFIG_TYPEC_MUX_PI3USB30532) += pi3usb30532.o obj-$(CONFIG_TYPEC_MUX_INTEL_PMC) += intel_pmc_mux.o diff --git a/drivers/usb/typec/mux/fsa4480.c b/drivers/usb/typec/mux/fsa4480.c new file mode 100644 index 000000000000..6184f5367190 --- /dev/null +++ b/drivers/usb/typec/mux/fsa4480.c @@ -0,0 +1,218 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021-2022 Linaro Ltd. + * Copyright (C) 2018-2020 The Linux Foundation + */ + +#include <linux/bits.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/regmap.h> +#include <linux/usb/typec_dp.h> +#include <linux/usb/typec_mux.h> + +#define FSA4480_SWITCH_ENABLE 0x04 +#define FSA4480_SWITCH_SELECT 0x05 +#define FSA4480_SWITCH_STATUS1 0x07 +#define FSA4480_SLOW_L 0x08 +#define FSA4480_SLOW_R 0x09 +#define FSA4480_SLOW_MIC 0x0a +#define FSA4480_SLOW_SENSE 0x0b +#define FSA4480_SLOW_GND 0x0c +#define FSA4480_DELAY_L_R 0x0d +#define FSA4480_DELAY_L_MIC 0x0e +#define FSA4480_DELAY_L_SENSE 0x0f +#define FSA4480_DELAY_L_AGND 0x10 +#define FSA4480_RESET 0x1e +#define FSA4480_MAX_REGISTER 0x1f + +#define FSA4480_ENABLE_DEVICE BIT(7) +#define FSA4480_ENABLE_SBU GENMASK(6, 5) +#define FSA4480_ENABLE_USB GENMASK(4, 3) + +#define FSA4480_SEL_SBU_REVERSE GENMASK(6, 5) +#define FSA4480_SEL_USB GENMASK(4, 3) + +struct fsa4480 { + struct i2c_client *client; + + /* used to serialize concurrent change requests */ + struct mutex lock; + + struct typec_switch_dev *sw; + struct typec_mux_dev *mux; + + struct regmap *regmap; + + u8 cur_enable; + u8 cur_select; +}; + +static const struct regmap_config fsa4480_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = FSA4480_MAX_REGISTER, + /* Accesses only done under fsa4480->lock */ + .disable_locking = true, +}; + +static int fsa4480_switch_set(struct typec_switch_dev *sw, + enum typec_orientation orientation) +{ + struct fsa4480 *fsa = typec_switch_get_drvdata(sw); + u8 new_sel; + + mutex_lock(&fsa->lock); + new_sel = FSA4480_SEL_USB; + if (orientation == TYPEC_ORIENTATION_REVERSE) + new_sel |= FSA4480_SEL_SBU_REVERSE; + + if (new_sel == fsa->cur_select) + goto out_unlock; + + if (fsa->cur_enable & FSA4480_ENABLE_SBU) { + /* Disable SBU output while re-configuring the switch */ + regmap_write(fsa->regmap, FSA4480_SWITCH_ENABLE, + fsa->cur_enable & ~FSA4480_ENABLE_SBU); + + /* 35us to allow the SBU switch to turn off */ + usleep_range(35, 1000); + } + + regmap_write(fsa->regmap, FSA4480_SWITCH_SELECT, new_sel); + fsa->cur_select = new_sel; + + if (fsa->cur_enable & FSA4480_ENABLE_SBU) { + regmap_write(fsa->regmap, FSA4480_SWITCH_ENABLE, fsa->cur_enable); + + /* 15us to allow the SBU switch to turn on again */ + usleep_range(15, 1000); + } + +out_unlock: + mutex_unlock(&fsa->lock); + + return 0; +} + +static int fsa4480_mux_set(struct typec_mux_dev *mux, struct typec_mux_state *state) +{ + struct fsa4480 *fsa = typec_mux_get_drvdata(mux); + u8 new_enable; + + mutex_lock(&fsa->lock); + + new_enable = FSA4480_ENABLE_DEVICE | FSA4480_ENABLE_USB; + if (state->mode >= TYPEC_DP_STATE_A) + new_enable |= FSA4480_ENABLE_SBU; + + if (new_enable == fsa->cur_enable) + goto out_unlock; + + regmap_write(fsa->regmap, FSA4480_SWITCH_ENABLE, new_enable); + fsa->cur_enable = new_enable; + + if (new_enable & FSA4480_ENABLE_SBU) { + /* 15us to allow the SBU switch to turn off */ + usleep_range(15, 1000); + } + +out_unlock: + mutex_unlock(&fsa->lock); + + return 0; +} + +static int fsa4480_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct typec_switch_desc sw_desc = { }; + struct typec_mux_desc mux_desc = { }; + struct fsa4480 *fsa; + + fsa = devm_kzalloc(dev, sizeof(*fsa), GFP_KERNEL); + if (!fsa) + return -ENOMEM; + + fsa->client = client; + mutex_init(&fsa->lock); + + fsa->regmap = devm_regmap_init_i2c(client, &fsa4480_regmap_config); + if (IS_ERR(fsa->regmap)) + return dev_err_probe(dev, PTR_ERR(fsa->regmap), "failed to initialize regmap\n"); + + fsa->cur_enable = FSA4480_ENABLE_DEVICE | FSA4480_ENABLE_USB; + fsa->cur_select = FSA4480_SEL_USB; + + /* set default settings */ + regmap_write(fsa->regmap, FSA4480_SLOW_L, 0x00); + regmap_write(fsa->regmap, FSA4480_SLOW_R, 0x00); + regmap_write(fsa->regmap, FSA4480_SLOW_MIC, 0x00); + regmap_write(fsa->regmap, FSA4480_SLOW_SENSE, 0x00); + regmap_write(fsa->regmap, FSA4480_SLOW_GND, 0x00); + regmap_write(fsa->regmap, FSA4480_DELAY_L_R, 0x00); + regmap_write(fsa->regmap, FSA4480_DELAY_L_MIC, 0x00); + regmap_write(fsa->regmap, FSA4480_DELAY_L_SENSE, 0x00); + regmap_write(fsa->regmap, FSA4480_DELAY_L_AGND, 0x09); + regmap_write(fsa->regmap, FSA4480_SWITCH_SELECT, fsa->cur_select); + regmap_write(fsa->regmap, FSA4480_SWITCH_ENABLE, fsa->cur_enable); + + sw_desc.drvdata = fsa; + sw_desc.fwnode = dev_fwnode(dev); + sw_desc.set = fsa4480_switch_set; + + fsa->sw = typec_switch_register(dev, &sw_desc); + if (IS_ERR(fsa->sw)) + return dev_err_probe(dev, PTR_ERR(fsa->sw), "failed to register typec switch\n"); + + mux_desc.drvdata = fsa; + mux_desc.fwnode = dev_fwnode(dev); + mux_desc.set = fsa4480_mux_set; + + fsa->mux = typec_mux_register(dev, &mux_desc); + if (IS_ERR(fsa->mux)) { + typec_switch_unregister(fsa->sw); + return dev_err_probe(dev, PTR_ERR(fsa->mux), "failed to register typec mux\n"); + } + + i2c_set_clientdata(client, fsa); + return 0; +} + +static int fsa4480_remove(struct i2c_client *client) +{ + struct fsa4480 *fsa = i2c_get_clientdata(client); + + typec_mux_unregister(fsa->mux); + typec_switch_unregister(fsa->sw); + + return 0; +} + +static const struct i2c_device_id fsa4480_table[] = { + { "fsa4480" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, fsa4480_table); + +static const struct of_device_id fsa4480_of_table[] = { + { .compatible = "fcs,fsa4480" }, + { } +}; +MODULE_DEVICE_TABLE(of, fsa4480_of_table); + +static struct i2c_driver fsa4480_driver = { + .driver = { + .name = "fsa4480", + .of_match_table = fsa4480_of_table, + }, + .probe_new = fsa4480_probe, + .remove = fsa4480_remove, + .id_table = fsa4480_table, +}; +module_i2c_driver(fsa4480_driver); + +MODULE_DESCRIPTION("ON Semiconductor FSA4480 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/typec/mux/intel_pmc_mux.c b/drivers/usb/typec/mux/intel_pmc_mux.c index 2cdd22130834..47b733f78fb0 100644 --- a/drivers/usb/typec/mux/intel_pmc_mux.c +++ b/drivers/usb/typec/mux/intel_pmc_mux.c @@ -121,8 +121,8 @@ struct pmc_usb_port { int num; u32 iom_status; struct pmc_usb *pmc; - struct typec_mux *typec_mux; - struct typec_switch *typec_sw; + struct typec_mux_dev *typec_mux; + struct typec_switch_dev *typec_sw; struct usb_role_switch *usb_sw; enum typec_orientation orientation; @@ -173,7 +173,7 @@ static int hsl_orientation(struct pmc_usb_port *port) return port->orientation - 1; } -static int pmc_usb_command(struct pmc_usb_port *port, u8 *msg, u32 len) +static int pmc_usb_send_command(struct intel_scu_ipc_dev *ipc, u8 *msg, u32 len) { u8 response[4]; u8 status_res; @@ -184,7 +184,7 @@ static int pmc_usb_command(struct pmc_usb_port *port, u8 *msg, u32 len) * Status can be checked from the response message if the * function intel_scu_ipc_dev_command succeeds. */ - ret = intel_scu_ipc_dev_command(port->pmc->ipc, PMC_USBC_CMD, 0, msg, + ret = intel_scu_ipc_dev_command(ipc, PMC_USBC_CMD, 0, msg, len, response, sizeof(response)); if (ret) @@ -203,6 +203,23 @@ static int pmc_usb_command(struct pmc_usb_port *port, u8 *msg, u32 len) return 0; } +static int pmc_usb_command(struct pmc_usb_port *port, u8 *msg, u32 len) +{ + int retry_count = 3; + int ret; + + /* + * If PMC is busy then retry the command once again + */ + while (retry_count--) { + ret = pmc_usb_send_command(port->pmc->ipc, msg, len); + if (ret != -EBUSY) + break; + } + + return ret; +} + static int pmc_usb_mux_dp_hpd(struct pmc_usb_port *port, struct typec_displayport_data *dp) { @@ -416,7 +433,7 @@ static int pmc_usb_connect(struct pmc_usb_port *port, enum usb_role role) } static int -pmc_usb_mux_set(struct typec_mux *mux, struct typec_mux_state *state) +pmc_usb_mux_set(struct typec_mux_dev *mux, struct typec_mux_state *state) { struct pmc_usb_port *port = typec_mux_get_drvdata(mux); @@ -452,7 +469,7 @@ pmc_usb_mux_set(struct typec_mux *mux, struct typec_mux_state *state) return -EOPNOTSUPP; } -static int pmc_usb_set_orientation(struct typec_switch *sw, +static int pmc_usb_set_orientation(struct typec_switch_dev *sw, enum typec_orientation orientation) { struct pmc_usb_port *port = typec_switch_get_drvdata(sw); diff --git a/drivers/usb/typec/mux/pi3usb30532.c b/drivers/usb/typec/mux/pi3usb30532.c index 7afe275b17d0..6ce9f282594e 100644 --- a/drivers/usb/typec/mux/pi3usb30532.c +++ b/drivers/usb/typec/mux/pi3usb30532.c @@ -23,8 +23,8 @@ struct pi3usb30532 { struct i2c_client *client; struct mutex lock; /* protects the cached conf register */ - struct typec_switch *sw; - struct typec_mux *mux; + struct typec_switch_dev *sw; + struct typec_mux_dev *mux; u8 conf; }; @@ -45,7 +45,7 @@ static int pi3usb30532_set_conf(struct pi3usb30532 *pi, u8 new_conf) return 0; } -static int pi3usb30532_sw_set(struct typec_switch *sw, +static int pi3usb30532_sw_set(struct typec_switch_dev *sw, enum typec_orientation orientation) { struct pi3usb30532 *pi = typec_switch_get_drvdata(sw); @@ -74,7 +74,7 @@ static int pi3usb30532_sw_set(struct typec_switch *sw, } static int -pi3usb30532_mux_set(struct typec_mux *mux, struct typec_mux_state *state) +pi3usb30532_mux_set(struct typec_mux_dev *mux, struct typec_mux_state *state) { struct pi3usb30532 *pi = typec_mux_get_drvdata(mux); u8 new_conf; diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c index 16b4560216ba..dfbba5ae9487 100644 --- a/drivers/usb/typec/tipd/core.c +++ b/drivers/usb/typec/tipd/core.c @@ -93,6 +93,8 @@ struct tps6598x { struct power_supply *psy; struct power_supply_desc psy_desc; enum power_supply_usb_type usb_type; + + u16 pwr_status; }; static enum power_supply_property tps6598x_psy_props[] = { @@ -230,17 +232,12 @@ static int tps6598x_connect(struct tps6598x *tps, u32 status) { struct typec_partner_desc desc; enum typec_pwr_opmode mode; - u16 pwr_status; int ret; if (tps->partner) return 0; - ret = tps6598x_read16(tps, TPS_REG_POWER_STATUS, &pwr_status); - if (ret < 0) - return ret; - - mode = TPS_POWER_STATUS_PWROPMODE(pwr_status); + mode = TPS_POWER_STATUS_PWROPMODE(tps->pwr_status); desc.usb_pd = mode == TYPEC_PWR_MODE_PD; desc.accessory = TYPEC_ACCESSORY_NONE; /* XXX: handle accessories */ @@ -455,6 +452,7 @@ static bool tps6598x_read_power_status(struct tps6598x *tps) dev_err(tps->dev, "failed to read power status: %d\n", ret); return false; } + tps->pwr_status = pwr_status; trace_tps6598x_power_status(pwr_status); return true; @@ -601,15 +599,8 @@ static const struct regmap_config tps6598x_regmap_config = { static int tps6598x_psy_get_online(struct tps6598x *tps, union power_supply_propval *val) { - int ret; - u16 pwr_status; - - ret = tps6598x_read16(tps, TPS_REG_POWER_STATUS, &pwr_status); - if (ret < 0) - return ret; - - if (TPS_POWER_STATUS_CONNECTION(pwr_status) && - TPS_POWER_STATUS_SOURCESINK(pwr_status)) { + if (TPS_POWER_STATUS_CONNECTION(tps->pwr_status) && + TPS_POWER_STATUS_SOURCESINK(tps->pwr_status)) { val->intval = 1; } else { val->intval = 0; @@ -622,15 +613,11 @@ static int tps6598x_psy_get_prop(struct power_supply *psy, union power_supply_propval *val) { struct tps6598x *tps = power_supply_get_drvdata(psy); - u16 pwr_status; int ret = 0; switch (psp) { case POWER_SUPPLY_PROP_USB_TYPE: - ret = tps6598x_read16(tps, TPS_REG_POWER_STATUS, &pwr_status); - if (ret < 0) - return ret; - if (TPS_POWER_STATUS_PWROPMODE(pwr_status) == TYPEC_PWR_MODE_PD) + if (TPS_POWER_STATUS_PWROPMODE(tps->pwr_status) == TYPEC_PWR_MODE_PD) val->intval = POWER_SUPPLY_USB_TYPE_PD; else val->intval = POWER_SUPPLY_USB_TYPE_C; @@ -837,6 +824,11 @@ static int tps6598x_probe(struct i2c_client *client) fwnode_handle_put(fwnode); if (status & TPS_STATUS_PLUG_PRESENT) { + ret = tps6598x_read16(tps, TPS_REG_POWER_STATUS, &tps->pwr_status); + if (ret < 0) { + dev_err(tps->dev, "failed to read power status: %d\n", ret); + goto err_role_put; + } ret = tps6598x_connect(tps, status); if (ret) dev_err(&client->dev, "failed to register partner\n"); diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index a6045aef0d04..cbd862f9f2a1 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -1063,6 +1063,14 @@ static int ucsi_register_port(struct ucsi *ucsi, int index) con->num = index + 1; con->ucsi = ucsi; + cap->fwnode = ucsi_find_fwnode(con); + con->usb_role_sw = fwnode_usb_role_switch_get(cap->fwnode); + if (IS_ERR(con->usb_role_sw)) { + dev_err(ucsi->dev, "con%d: failed to get usb role switch\n", + con->num); + return PTR_ERR(con->usb_role_sw); + } + /* Delay other interactions with the con until registration is complete */ mutex_lock(&con->lock); @@ -1098,7 +1106,6 @@ static int ucsi_register_port(struct ucsi *ucsi, int index) if (con->cap.op_mode & UCSI_CONCAP_OPMODE_DEBUG_ACCESSORY) *accessory = TYPEC_ACCESSORY_DEBUG; - cap->fwnode = ucsi_find_fwnode(con); cap->driver_data = con; cap->ops = &ucsi_ops; @@ -1156,13 +1163,6 @@ static int ucsi_register_port(struct ucsi *ucsi, int index) ucsi_port_psy_changed(con); } - con->usb_role_sw = fwnode_usb_role_switch_get(cap->fwnode); - if (IS_ERR(con->usb_role_sw)) { - dev_err(ucsi->dev, "con%d: failed to get usb role switch\n", - con->num); - con->usb_role_sw = NULL; - } - /* Only notify USB controller if partner supports USB data */ if (!(UCSI_CONSTAT_PARTNER_FLAGS(con->status.flags) & UCSI_CONSTAT_PARTNER_FLAG_USB)) u_role = USB_ROLE_NONE; @@ -1196,6 +1196,32 @@ out_unlock: return ret; } +static void ucsi_unregister_connectors(struct ucsi *ucsi) +{ + struct ucsi_connector *con; + int i; + + if (!ucsi->connector) + return; + + for (i = 0; i < ucsi->cap.num_connectors; i++) { + con = &ucsi->connector[i]; + + if (!con->wq) + break; + + cancel_work_sync(&con->work); + ucsi_unregister_partner(con); + ucsi_unregister_altmodes(con, UCSI_RECIPIENT_CON); + ucsi_unregister_port_psy(con); + destroy_workqueue(con->wq); + typec_unregister_port(con->port); + } + + kfree(ucsi->connector); + ucsi->connector = NULL; +} + /** * ucsi_init - Initialize UCSI interface * @ucsi: UCSI to be initialized @@ -1204,7 +1230,6 @@ out_unlock: */ static int ucsi_init(struct ucsi *ucsi) { - struct ucsi_connector *con; u64 command; int ret; int i; @@ -1235,7 +1260,7 @@ static int ucsi_init(struct ucsi *ucsi) } /* Allocate the connectors. Released in ucsi_unregister() */ - ucsi->connector = kcalloc(ucsi->cap.num_connectors + 1, + ucsi->connector = kcalloc(ucsi->cap.num_connectors, sizeof(*ucsi->connector), GFP_KERNEL); if (!ucsi->connector) { ret = -ENOMEM; @@ -1259,15 +1284,7 @@ static int ucsi_init(struct ucsi *ucsi) return 0; err_unregister: - for (con = ucsi->connector; con->port; con++) { - ucsi_unregister_partner(con); - ucsi_unregister_altmodes(con, UCSI_RECIPIENT_CON); - ucsi_unregister_port_psy(con); - if (con->wq) - destroy_workqueue(con->wq); - typec_unregister_port(con->port); - con->port = NULL; - } + ucsi_unregister_connectors(ucsi); err_reset: memset(&ucsi->cap, 0, sizeof(ucsi->cap)); @@ -1278,12 +1295,20 @@ err: static void ucsi_init_work(struct work_struct *work) { - struct ucsi *ucsi = container_of(work, struct ucsi, work); + struct ucsi *ucsi = container_of(work, struct ucsi, work.work); int ret; ret = ucsi_init(ucsi); if (ret) dev_err(ucsi->dev, "PPM init failed (%d)\n", ret); + + if (ret == -EPROBE_DEFER) { + if (ucsi->work_count++ > UCSI_ROLE_SWITCH_WAIT_COUNT) + return; + + queue_delayed_work(system_long_wq, &ucsi->work, + UCSI_ROLE_SWITCH_INTERVAL); + } } /** @@ -1323,7 +1348,7 @@ struct ucsi *ucsi_create(struct device *dev, const struct ucsi_operations *ops) if (!ucsi) return ERR_PTR(-ENOMEM); - INIT_WORK(&ucsi->work, ucsi_init_work); + INIT_DELAYED_WORK(&ucsi->work, ucsi_init_work); mutex_init(&ucsi->ppm_lock); ucsi->dev = dev; ucsi->ops = ops; @@ -1358,7 +1383,7 @@ int ucsi_register(struct ucsi *ucsi) if (!ucsi->version) return -ENODEV; - queue_work(system_long_wq, &ucsi->work); + queue_delayed_work(system_long_wq, &ucsi->work, 0); return 0; } @@ -1373,26 +1398,14 @@ EXPORT_SYMBOL_GPL(ucsi_register); void ucsi_unregister(struct ucsi *ucsi) { u64 cmd = UCSI_SET_NOTIFICATION_ENABLE; - int i; /* Make sure that we are not in the middle of driver initialization */ - cancel_work_sync(&ucsi->work); + cancel_delayed_work_sync(&ucsi->work); /* Disable notifications */ ucsi->ops->async_write(ucsi, UCSI_CONTROL, &cmd, sizeof(cmd)); - for (i = 0; i < ucsi->cap.num_connectors; i++) { - cancel_work_sync(&ucsi->connector[i].work); - ucsi_unregister_partner(&ucsi->connector[i]); - ucsi_unregister_altmodes(&ucsi->connector[i], - UCSI_RECIPIENT_CON); - ucsi_unregister_port_psy(&ucsi->connector[i]); - if (ucsi->connector[i].wq) - destroy_workqueue(ucsi->connector[i].wq); - typec_unregister_port(ucsi->connector[i].port); - } - - kfree(ucsi->connector); + ucsi_unregister_connectors(ucsi); } EXPORT_SYMBOL_GPL(ucsi_unregister); diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index 280f1e1bda2c..8eb391e3e592 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -287,7 +287,11 @@ struct ucsi { struct ucsi_capability cap; struct ucsi_connector *connector; - struct work_struct work; + struct delayed_work work; + int work_count; +#define UCSI_ROLE_SWITCH_RETRY_PER_HZ 10 +#define UCSI_ROLE_SWITCH_INTERVAL (HZ / UCSI_ROLE_SWITCH_RETRY_PER_HZ) +#define UCSI_ROLE_SWITCH_WAIT_COUNT (10 * UCSI_ROLE_SWITCH_RETRY_PER_HZ) /* PPM Communication lock */ struct mutex ppm_lock; diff --git a/drivers/usb/usbip/stub_dev.c b/drivers/usb/usbip/stub_dev.c index d8d3892e5a69..3c6d452e3bf4 100644 --- a/drivers/usb/usbip/stub_dev.c +++ b/drivers/usb/usbip/stub_dev.c @@ -393,7 +393,6 @@ static int stub_probe(struct usb_device *udev) err_port: dev_set_drvdata(&udev->dev, NULL); - usb_put_dev(udev); /* we already have busid_priv, just lock busid_lock */ spin_lock(&busid_priv->busid_lock); @@ -408,6 +407,7 @@ call_put_busid_priv: put_busid_priv(busid_priv); sdev_free: + usb_put_dev(udev); stub_device_free(sdev); return rc; diff --git a/drivers/usb/usbip/stub_rx.c b/drivers/usb/usbip/stub_rx.c index 325c22008e53..5dd41e8215e0 100644 --- a/drivers/usb/usbip/stub_rx.c +++ b/drivers/usb/usbip/stub_rx.c @@ -138,7 +138,9 @@ static int tweak_set_configuration_cmd(struct urb *urb) req = (struct usb_ctrlrequest *) urb->setup_packet; config = le16_to_cpu(req->wValue); + usb_lock_device(sdev->udev); err = usb_set_configuration(sdev->udev, config); + usb_unlock_device(sdev->udev); if (err && err != -ENODEV) dev_err(&sdev->udev->dev, "can't set config #%d, error %d\n", config, err); diff --git a/include/linux/property.h b/include/linux/property.h index fc24d45632eb..a5b429d623f6 100644 --- a/include/linux/property.h +++ b/include/linux/property.h @@ -451,6 +451,11 @@ static inline void *device_connection_find_match(struct device *dev, return fwnode_connection_find_match(dev_fwnode(dev), con_id, data, match); } +int fwnode_connection_find_matches(struct fwnode_handle *fwnode, + const char *con_id, void *data, + devcon_match_fn_t match, + void **matches, unsigned int matches_len); + /* -------------------------------------------------------------------------- */ /* Software fwnode support - when HW description is incomplete or missing */ diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index 7a8ad984e651..9f442d73f3df 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -198,15 +198,15 @@ void tb_unregister_property_dir(const char *key, struct tb_property_dir *dir); * @local_property_block_len: Length of the @local_property_block in dwords * @remote_properties: Properties exported by the remote domain * @remote_property_block_gen: Generation of @remote_properties - * @get_uuid_work: Work used to retrieve @remote_uuid - * @uuid_retries: Number of times left @remote_uuid is requested before - * giving up - * @get_properties_work: Work used to get remote domain properties - * @properties_retries: Number of times left to read properties + * @state: Next XDomain discovery state to run + * @state_work: Work used to run the next state + * @state_retries: Number of retries remain for the state * @properties_changed_work: Work used to notify the remote domain that * our properties have changed * @properties_changed_retries: Number of times left to send properties * changed notification + * @bonding_possible: True if lane bonding is possible on local side + * @target_link_width: Target link width from the remote host * @link: Root switch link the remote domain is connected (ICM only) * @depth: Depth in the chain the remote domain is connected (ICM only) * @@ -244,12 +244,13 @@ struct tb_xdomain { u32 local_property_block_len; struct tb_property_dir *remote_properties; u32 remote_property_block_gen; - struct delayed_work get_uuid_work; - int uuid_retries; - struct delayed_work get_properties_work; - int properties_retries; + int state; + struct delayed_work state_work; + int state_retries; struct delayed_work properties_changed_work; int properties_changed_retries; + bool bonding_possible; + u8 target_link_width; u8 link; u8 depth; }; diff --git a/include/linux/usb.h b/include/linux/usb.h index 200b7b79acb5..60bee864d897 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -1969,21 +1969,10 @@ usb_pipe_endpoint(struct usb_device *dev, unsigned int pipe) return eps[usb_pipeendpoint(pipe)]; } -/*-------------------------------------------------------------------------*/ - -static inline __u16 -usb_maxpacket(struct usb_device *udev, int pipe, int is_out) +static inline u16 usb_maxpacket(struct usb_device *udev, int pipe) { - struct usb_host_endpoint *ep; - unsigned epnum = usb_pipeendpoint(pipe); + struct usb_host_endpoint *ep = usb_pipe_endpoint(udev, pipe); - if (is_out) { - WARN_ON(usb_pipein(pipe)); - ep = udev->ep_out[epnum]; - } else { - WARN_ON(usb_pipeout(pipe)); - ep = udev->ep_in[epnum]; - } if (!ep) return 0; @@ -1991,8 +1980,6 @@ usb_maxpacket(struct usb_device *udev, int pipe, int is_out) return usb_endpoint_maxp(&ep->desc); } -/* ----------------------------------------------------------------------- */ - /* translate USB error codes to codes user space understands */ static inline int usb_translate_errors(int error_code) { diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 10fe57cf40be..3ad58b7a0824 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -386,6 +386,7 @@ struct usb_gadget_ops { * @lpm_capable: If the gadget max_speed is FULL or HIGH, this flag * indicates that it supports LPM as per the LPM ECN & errata. * @irq: the interrupt number for device controller. + * @id_number: a unique ID number for ensuring that gadget names are distinct * * Gadgets have a mostly-portable "gadget driver" implementing device * functions, handling all usb configurations and interfaces. Gadget @@ -446,6 +447,7 @@ struct usb_gadget { unsigned connected:1; unsigned lpm_capable:1; int irq; + int id_number; }; #define work_to_gadget(w) (container_of((w), struct usb_gadget, work)) @@ -664,9 +666,9 @@ static inline int usb_gadget_check_config(struct usb_gadget *gadget) * @driver: Driver model state for this driver. * @udc_name: A name of UDC this driver should be bound to. If udc_name is NULL, * this driver will be bound to any available UDC. - * @pending: UDC core private data used for deferred probe of this driver. - * @match_existing_only: If udc is not found, return an error and don't add this - * gadget driver to list of pending driver + * @match_existing_only: If udc is not found, return an error and fail + * the driver registration + * @is_bound: Allow a driver to be bound to only one gadget * * Devices are disabled till a gadget driver successfully bind()s, which * means the driver will handle setup() requests needed to enumerate (and @@ -729,8 +731,8 @@ struct usb_gadget_driver { struct device_driver driver; char *udc_name; - struct list_head pending; unsigned match_existing_only:1; + bool is_bound:1; }; @@ -740,22 +742,30 @@ struct usb_gadget_driver { /* driver modules register and unregister, as usual. * these calls must be made in a context that can sleep. * - * these will usually be implemented directly by the hardware-dependent - * usb bus interface driver, which will only support a single driver. + * A gadget driver can be bound to only one gadget at a time. */ /** - * usb_gadget_probe_driver - probe a gadget driver + * usb_gadget_register_driver_owner - register a gadget driver * @driver: the driver being registered + * @owner: the driver module + * @mod_name: the driver module's build name * Context: can sleep * * Call this in your gadget driver's module initialization function, - * to tell the underlying usb controller driver about your driver. + * to tell the underlying UDC controller driver about your driver. * The @bind() function will be called to bind it to a gadget before this * registration call returns. It's expected that the @bind() function will * be in init sections. + * + * Use the macro defined below instead of calling this directly. */ -int usb_gadget_probe_driver(struct usb_gadget_driver *driver); +int usb_gadget_register_driver_owner(struct usb_gadget_driver *driver, + struct module *owner, const char *mod_name); + +/* use a define to avoid include chaining to get THIS_MODULE & friends */ +#define usb_gadget_register_driver(driver) \ + usb_gadget_register_driver_owner(driver, THIS_MODULE, KBUILD_MODNAME) /** * usb_gadget_unregister_driver - unregister a gadget driver diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 548a028f2dab..2c1fc9212cf2 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -124,6 +124,7 @@ struct usb_hcd { #define HCD_FLAG_RH_RUNNING 5 /* root hub is running? */ #define HCD_FLAG_DEAD 6 /* controller has died? */ #define HCD_FLAG_INTF_AUTHORIZED 7 /* authorize interfaces? */ +#define HCD_FLAG_DEFER_RH_REGISTER 8 /* Defer roothub registration */ /* The flags can be tested using these macros; they are likely to * be slightly faster than test_bit(). @@ -134,6 +135,7 @@ struct usb_hcd { #define HCD_WAKEUP_PENDING(hcd) ((hcd)->flags & (1U << HCD_FLAG_WAKEUP_PENDING)) #define HCD_RH_RUNNING(hcd) ((hcd)->flags & (1U << HCD_FLAG_RH_RUNNING)) #define HCD_DEAD(hcd) ((hcd)->flags & (1U << HCD_FLAG_DEAD)) +#define HCD_DEFER_RH_REGISTER(hcd) ((hcd)->flags & (1U << HCD_FLAG_DEFER_RH_REGISTER)) /* * Specifies if interfaces are authorized by default diff --git a/include/linux/usb/typec_mux.h b/include/linux/usb/typec_mux.h index a9d9957933dc..ee57781dcf28 100644 --- a/include/linux/usb/typec_mux.h +++ b/include/linux/usb/typec_mux.h @@ -8,11 +8,13 @@ struct device; struct typec_mux; +struct typec_mux_dev; struct typec_switch; +struct typec_switch_dev; struct typec_altmode; struct fwnode_handle; -typedef int (*typec_switch_set_fn_t)(struct typec_switch *sw, +typedef int (*typec_switch_set_fn_t)(struct typec_switch_dev *sw, enum typec_orientation orientation); struct typec_switch_desc { @@ -32,13 +34,13 @@ static inline struct typec_switch *typec_switch_get(struct device *dev) return fwnode_typec_switch_get(dev_fwnode(dev)); } -struct typec_switch * +struct typec_switch_dev * typec_switch_register(struct device *parent, const struct typec_switch_desc *desc); -void typec_switch_unregister(struct typec_switch *sw); +void typec_switch_unregister(struct typec_switch_dev *sw); -void typec_switch_set_drvdata(struct typec_switch *sw, void *data); -void *typec_switch_get_drvdata(struct typec_switch *sw); +void typec_switch_set_drvdata(struct typec_switch_dev *sw, void *data); +void *typec_switch_get_drvdata(struct typec_switch_dev *sw); struct typec_mux_state { struct typec_altmode *alt; @@ -46,7 +48,7 @@ struct typec_mux_state { void *data; }; -typedef int (*typec_mux_set_fn_t)(struct typec_mux *mux, +typedef int (*typec_mux_set_fn_t)(struct typec_mux_dev *mux, struct typec_mux_state *state); struct typec_mux_desc { @@ -67,11 +69,11 @@ typec_mux_get(struct device *dev, const struct typec_altmode_desc *desc) return fwnode_typec_mux_get(dev_fwnode(dev), desc); } -struct typec_mux * +struct typec_mux_dev * typec_mux_register(struct device *parent, const struct typec_mux_desc *desc); -void typec_mux_unregister(struct typec_mux *mux); +void typec_mux_unregister(struct typec_mux_dev *mux); -void typec_mux_set_drvdata(struct typec_mux *mux, void *data); -void *typec_mux_get_drvdata(struct typec_mux *mux); +void typec_mux_set_drvdata(struct typec_mux_dev *mux, void *data); +void *typec_mux_get_drvdata(struct typec_mux_dev *mux); #endif /* __USB_TYPEC_MUX */ diff --git a/sound/usb/line6/pcm.c b/sound/usb/line6/pcm.c index fdbdfb7bce92..6a4af725aedd 100644 --- a/sound/usb/line6/pcm.c +++ b/sound/usb/line6/pcm.c @@ -552,10 +552,10 @@ int line6_init_pcm(struct usb_line6 *line6, line6pcm->max_packet_size_in = usb_maxpacket(line6->usbdev, - usb_rcvisocpipe(line6->usbdev, ep_read), 0); + usb_rcvisocpipe(line6->usbdev, ep_read)); line6pcm->max_packet_size_out = usb_maxpacket(line6->usbdev, - usb_sndisocpipe(line6->usbdev, ep_write), 1); + usb_sndisocpipe(line6->usbdev, ep_write)); if (!line6pcm->max_packet_size_in || !line6pcm->max_packet_size_out) { dev_err(line6pcm->line6->ifcdev, "cannot get proper max packet size\n"); diff --git a/sound/usb/midi.c b/sound/usb/midi.c index 344fbeadf161..bbff0923d264 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -1289,7 +1289,7 @@ static int snd_usbmidi_in_endpoint_create(struct snd_usb_midi *umidi, pipe = usb_rcvintpipe(umidi->dev, ep_info->in_ep); else pipe = usb_rcvbulkpipe(umidi->dev, ep_info->in_ep); - length = usb_maxpacket(umidi->dev, pipe, 0); + length = usb_maxpacket(umidi->dev, pipe); for (i = 0; i < INPUT_URBS; ++i) { buffer = usb_alloc_coherent(umidi->dev, length, GFP_KERNEL, &ep->urbs[i]->transfer_dma); @@ -1378,7 +1378,7 @@ static int snd_usbmidi_out_endpoint_create(struct snd_usb_midi *umidi, pipe = usb_sndbulkpipe(umidi->dev, ep_info->out_ep); switch (umidi->usb_id) { default: - ep->max_transfer = usb_maxpacket(umidi->dev, pipe, 1); + ep->max_transfer = usb_maxpacket(umidi->dev, pipe); break; /* * Various chips declare a packet size larger than 4 bytes, but diff --git a/sound/usb/usx2y/usb_stream.c b/sound/usb/usx2y/usb_stream.c index 9d0e44793896..a4d32e8a1d36 100644 --- a/sound/usb/usx2y/usb_stream.c +++ b/sound/usb/usx2y/usb_stream.c @@ -51,7 +51,7 @@ static int init_pipe_urbs(struct usb_stream_kernel *sk, { int u, p; int maxpacket = use_packsize ? - use_packsize : usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + use_packsize : usb_maxpacket(dev, pipe); int transfer_length = maxpacket * sk->n_o_ps; for (u = 0; u < USB_STREAM_NURBS; @@ -171,7 +171,7 @@ struct usb_stream *usb_stream_new(struct usb_stream_kernel *sk, out_pipe = usb_sndisocpipe(dev, out_endpoint); max_packsize = use_packsize ? - use_packsize : usb_maxpacket(dev, in_pipe, 0); + use_packsize : usb_maxpacket(dev, in_pipe); /* t_period = period_frames / sample_rate @@ -187,7 +187,7 @@ struct usb_stream *usb_stream_new(struct usb_stream_kernel *sk, read_size += packets * USB_STREAM_URBDEPTH * (max_packsize + sizeof(struct usb_stream_packet)); - max_packsize = usb_maxpacket(dev, out_pipe, 1); + max_packsize = usb_maxpacket(dev, out_pipe); write_size = max_packsize * packets * USB_STREAM_URBDEPTH; if (read_size >= 256*PAGE_SIZE || write_size >= 256*PAGE_SIZE) { diff --git a/sound/usb/usx2y/usbusx2yaudio.c b/sound/usb/usx2y/usbusx2yaudio.c index cfc1ea53978d..9cd5e3aae4f7 100644 --- a/sound/usb/usx2y/usbusx2yaudio.c +++ b/sound/usb/usx2y/usbusx2yaudio.c @@ -421,7 +421,7 @@ static int usx2y_urbs_allocate(struct snd_usx2y_substream *subs) pipe = is_playback ? usb_sndisocpipe(dev, subs->endpoint) : usb_rcvisocpipe(dev, subs->endpoint); - subs->maxpacksize = usb_maxpacket(dev, pipe, is_playback); + subs->maxpacksize = usb_maxpacket(dev, pipe); if (!subs->maxpacksize) return -EINVAL; diff --git a/sound/usb/usx2y/usx2yhwdeppcm.c b/sound/usb/usx2y/usx2yhwdeppcm.c index db83522c1b49..240349b644f3 100644 --- a/sound/usb/usx2y/usx2yhwdeppcm.c +++ b/sound/usb/usx2y/usx2yhwdeppcm.c @@ -321,7 +321,7 @@ static int usx2y_usbpcm_urbs_allocate(struct snd_usx2y_substream *subs) pipe = is_playback ? usb_sndisocpipe(dev, subs->endpoint) : usb_rcvisocpipe(dev, subs->endpoint); - subs->maxpacksize = usb_maxpacket(dev, pipe, is_playback); + subs->maxpacksize = usb_maxpacket(dev, pipe); if (!subs->maxpacksize) return -EINVAL; diff --git a/tools/usb/testusb.c b/tools/usb/testusb.c index 69c3ead25313..474bae868b35 100644 --- a/tools/usb/testusb.c +++ b/tools/usb/testusb.c @@ -482,7 +482,7 @@ usage: } if (not) return 0; - if (testdevs && testdevs->next == 0 && !device) + if (testdevs && !testdevs->next && !device) device = testdevs->name; for (entry = testdevs; entry; entry = entry->next) { int status; |