diff options
123 files changed, 3994 insertions, 1181 deletions
diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uac1 b/Documentation/ABI/testing/configfs-usb-gadget-uac1 index dc23fd776943..dd647d44d975 100644 --- a/Documentation/ABI/testing/configfs-usb-gadget-uac1 +++ b/Documentation/ABI/testing/configfs-usb-gadget-uac1 @@ -8,9 +8,19 @@ Description: c_chmask capture channel mask c_srate capture sampling rate c_ssize capture sample size (bytes) + c_mute_present capture mute control enable + c_volume_present capture volume control enable + c_volume_min capture volume control min value (in 1/256 dB) + c_volume_max capture volume control max value (in 1/256 dB) + c_volume_res capture volume control resolution (in 1/256 dB) p_chmask playback channel mask p_srate playback sampling rate p_ssize playback sample size (bytes) + p_mute_present playback mute control enable + p_volume_present playback volume control enable + p_volume_min playback volume control min value (in 1/256 dB) + p_volume_max playback volume control max value (in 1/256 dB) + p_volume_res playback volume control resolution (in 1/256 dB) req_number the number of pre-allocated request for both capture and playback ========== =================================== diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uac2 b/Documentation/ABI/testing/configfs-usb-gadget-uac2 index 26fb8e9b4e61..cfd160ff8b56 100644 --- a/Documentation/ABI/testing/configfs-usb-gadget-uac2 +++ b/Documentation/ABI/testing/configfs-usb-gadget-uac2 @@ -9,8 +9,18 @@ Description: c_srate capture sampling rate c_ssize capture sample size (bytes) c_sync capture synchronization type (async/adaptive) + c_mute_present capture mute control enable + c_volume_present capture volume control enable + c_volume_min capture volume control min value (in 1/256 dB) + c_volume_max capture volume control max value (in 1/256 dB) + c_volume_res capture volume control resolution (in 1/256 dB) fb_max maximum extra bandwidth in async mode p_chmask playback channel mask p_srate playback sampling rate p_ssize playback sample size (bytes) + p_mute_present playback mute control enable + p_volume_present playback volume control enable + p_volume_min playback volume control min value (in 1/256 dB) + p_volume_max playback volume control max value (in 1/256 dB) + p_volume_res playback volume control resolution (in 1/256 dB) ========= ============================ diff --git a/Documentation/devicetree/bindings/connector/usb-connector.yaml b/Documentation/devicetree/bindings/connector/usb-connector.yaml index 92b49bc37939..7eb8659fa610 100644 --- a/Documentation/devicetree/bindings/connector/usb-connector.yaml +++ b/Documentation/devicetree/bindings/connector/usb-connector.yaml @@ -111,6 +111,10 @@ properties: - 1.5A - 3.0A + pd-disable: + description: Set this property if the Type-C connector has no power delivery support. + type: boolean + # The following are optional properties for "usb-c-connector" with power # delivery support. source-pdos: diff --git a/Documentation/devicetree/bindings/phy/qcom,qmp-usb3-dp-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,qmp-usb3-dp-phy.yaml index 1d49cc3d4eae..60dc27834e1d 100644 --- a/Documentation/devicetree/bindings/phy/qcom,qmp-usb3-dp-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,qmp-usb3-dp-phy.yaml @@ -14,6 +14,7 @@ properties: compatible: enum: - qcom,sc7180-qmp-usb3-dp-phy + - qcom,sc7280-qmp-usb3-dp-phy - qcom,sc8180x-qmp-usb3-dp-phy - qcom,sdm845-qmp-usb3-dp-phy - qcom,sm8250-qmp-usb3-dp-phy diff --git a/Documentation/devicetree/bindings/usb/generic-ehci.yaml b/Documentation/devicetree/bindings/usb/generic-ehci.yaml index 8089dc956ba3..8913497624de 100644 --- a/Documentation/devicetree/bindings/usb/generic-ehci.yaml +++ b/Documentation/devicetree/bindings/usb/generic-ehci.yaml @@ -34,6 +34,7 @@ properties: - allwinner,sun6i-a31-ehci - allwinner,sun7i-a20-ehci - allwinner,sun8i-a23-ehci + - allwinner,sun8i-a83t-ehci - allwinner,sun8i-h3-ehci - allwinner,sun8i-r40-ehci - allwinner,sun9i-a80-ehci @@ -142,6 +143,11 @@ properties: iommus: maxItems: 1 + dr_mode: + enum: + - host + - otg + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/usb/generic-ohci.yaml b/Documentation/devicetree/bindings/usb/generic-ohci.yaml index 0f5f6ea702d0..acbf94fa5f74 100644 --- a/Documentation/devicetree/bindings/usb/generic-ohci.yaml +++ b/Documentation/devicetree/bindings/usb/generic-ohci.yaml @@ -24,6 +24,7 @@ properties: - allwinner,sun6i-a31-ohci - allwinner,sun7i-a20-ohci - allwinner,sun8i-a23-ohci + - allwinner,sun8i-a83t-ohci - allwinner,sun8i-h3-ohci - allwinner,sun8i-r40-ohci - allwinner,sun9i-a80-ohci @@ -109,6 +110,11 @@ properties: iommus: maxItems: 1 + dr_mode: + enum: + - host + - otg + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.yaml b/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.yaml index 240882b12565..11f7bacd4e2b 100644 --- a/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.yaml +++ b/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.yaml @@ -31,6 +31,7 @@ properties: - mediatek,mt8173-xhci - mediatek,mt8183-xhci - mediatek,mt8192-xhci + - mediatek,mt8195-xhci - const: mediatek,mtk-xhci reg: @@ -152,6 +153,11 @@ properties: description: The mask to disable u3ports, bit0 for u3port0, bit1 for u3port1, ... etc + mediatek,u2p-dis-msk: + $ref: /schemas/types.yaml#/definitions/uint32 + description: The mask to disable u2ports, bit0 for u2port0, + bit1 for u2port1, ... etc + "#address-cells": const: 1 diff --git a/Documentation/devicetree/bindings/usb/mediatek,mtu3.yaml b/Documentation/devicetree/bindings/usb/mediatek,mtu3.yaml index dbc7876e0a0b..77db1233516e 100644 --- a/Documentation/devicetree/bindings/usb/mediatek,mtu3.yaml +++ b/Documentation/devicetree/bindings/usb/mediatek,mtu3.yaml @@ -38,7 +38,18 @@ properties: - const: ippc interrupts: - maxItems: 1 + description: + use "interrupts-extended" when the interrupts are connected to the + separate interrupt controllers + minItems: 1 + items: + - description: SSUSB device controller interrupt + - description: optional, wakeup interrupt used to support runtime PM + + interrupt-names: + items: + - const: device + - const: wakeup power-domains: description: A phandle to USB power domain node to control USB's MTCMOS @@ -106,7 +117,7 @@ properties: extcon: deprecated: true description: | - Phandle to the extcon device detecting the IDDIG/VBUS state, neede + Phandle to the extcon device detecting the IDDIG state, needed when supports dual-role mode. It's considered valid for compatibility reasons, not allowed for new bindings, and use "usb-role-switch" property instead. @@ -116,6 +127,10 @@ properties: description: Support role switch. type: boolean + role-switch-default-mode: + enum: [host, peripheral] + default: host + connector: $ref: /connector/usb-connector.yaml# description: @@ -166,6 +181,12 @@ properties: description: The mask to disable u3ports, bit0 for u3port0, bit1 for u3port1, ... etc + mediatek,u2p-dis-msk: + $ref: /schemas/types.yaml#/definitions/uint32 + description: The mask to disable u2ports, bit0 for u2port0, + bit1 for u2port1, ... etc; but can't disable u2port0 if dual role mode + is enabled, so will be skipped in this case. + # Required child node when support dual-role patternProperties: "^usb@[0-9a-f]+$": @@ -178,6 +199,7 @@ patternProperties: dependencies: connector: [ 'usb-role-switch' ] port: [ 'usb-role-switch' ] + role-switch-default-mode: [ 'usb-role-switch' ] wakeup-source: [ 'mediatek,syscon-wakeup' ] required: @@ -230,7 +252,7 @@ examples: }; }; - # Enable/disable device by an input gpio for VBUS pin + # Dual role switch by gpio-usb-b-connector - | #include <dt-bindings/gpio/gpio.h> #include <dt-bindings/power/mt2712-power.h> @@ -244,13 +266,27 @@ examples: power-domains = <&scpsys MT2712_POWER_DOMAIN_USB2>; clocks = <&topckgen CLK_TOP_USB30_SEL>; clock-names = "sys_ck"; - dr_mode = "peripheral"; + dr_mode = "otg"; usb-role-switch; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + host0: usb@11270000 { + compatible = "mediatek,mt2712-xhci", "mediatek,mtk-xhci"; + reg = <0x11270000 0x1000>; + reg-names = "mac"; + interrupts = <GIC_SPI 123 IRQ_TYPE_LEVEL_LOW>; + power-domains = <&scpsys MT2712_POWER_DOMAIN_USB>; + clocks = <&topckgen CLK_TOP_USB30_SEL>, <&clk26m>; + clock-names = "sys_ck", "ref_ck"; + }; connector { compatible = "gpio-usb-b-connector", "usb-b-connector"; type = "micro"; - vbus-gpios = <&pio 13 GPIO_ACTIVE_HIGH>; + id-gpios = <&pio 12 GPIO_ACTIVE_HIGH>; + vbus-supply = <&usb_p0_vbus>; }; }; @@ -268,6 +304,7 @@ examples: wakeup-source; dr_mode = "otg"; usb-role-switch; + role-switch-default-mode = "host"; #address-cells = <1>; #size-cells = <1>; ranges; diff --git a/Documentation/devicetree/bindings/usb/mediatek,musb.yaml b/Documentation/devicetree/bindings/usb/mediatek,musb.yaml index 84ddacfdbe9b..03d62d60ce5f 100644 --- a/Documentation/devicetree/bindings/usb/mediatek,musb.yaml +++ b/Documentation/devicetree/bindings/usb/mediatek,musb.yaml @@ -19,6 +19,7 @@ properties: - enum: - mediatek,mt8516-musb - mediatek,mt2701-musb + - mediatek,mt7623-musb - const: mediatek,mtk-musb reg: diff --git a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml index 4e6451789806..e70afc40edb2 100644 --- a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml @@ -17,6 +17,7 @@ properties: - qcom,msm8998-dwc3 - qcom,sc7180-dwc3 - qcom,sc7280-dwc3 + - qcom,sdm660-dwc3 - qcom,sdm845-dwc3 - qcom,sdx55-dwc3 - qcom,sm4250-dwc3 diff --git a/Documentation/devicetree/bindings/usb/renesas,usbhs.yaml b/Documentation/devicetree/bindings/usb/renesas,usbhs.yaml index ad73339ffe1d..012fe80a7611 100644 --- a/Documentation/devicetree/bindings/usb/renesas,usbhs.yaml +++ b/Documentation/devicetree/bindings/usb/renesas,usbhs.yaml @@ -17,7 +17,9 @@ properties: - const: renesas,rza1-usbhs - items: - - const: renesas,usbhs-r7s9210 # RZ/A2 + - enum: + - renesas,usbhs-r7s9210 # RZ/A2 + - renesas,usbhs-r9a07g044 # RZ/G2{L,LC} - const: renesas,rza2-usbhs - items: @@ -59,7 +61,8 @@ properties: - description: USB 2.0 clock selector interrupts: - maxItems: 1 + minItems: 1 + maxItems: 4 renesas,buswait: $ref: /schemas/types.yaml#/definitions/uint32 @@ -108,6 +111,25 @@ required: - clocks - interrupts +allOf: + - if: + properties: + compatible: + contains: + const: renesas,usbhs-r9a07g044 + then: + properties: + interrupts: + items: + - description: U2P_IXL_INT + - description: U2P_INT_DMA[0] + - description: U2P_INT_DMA[1] + - description: U2P_INT_DMAERR + else: + properties: + interrupts: + maxItems: 1 + additionalProperties: false examples: diff --git a/Documentation/devicetree/bindings/usb/snps,dwc3.yaml b/Documentation/devicetree/bindings/usb/snps,dwc3.yaml index 41416fbd92aa..078fb7889593 100644 --- a/Documentation/devicetree/bindings/usb/snps,dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/snps,dwc3.yaml @@ -289,10 +289,21 @@ properties: maximum: 16 tx-fifo-resize: - description: Determines if the FIFO *has* to be reallocated - deprecated: true + description: Determines if the TX fifos can be dynamically resized depending + on the number of IN endpoints used and if bursting is supported. This + may help improve bandwidth on platforms with higher system latencies, as + increased fifo space allows for the controller to prefetch data into its + internal memory. type: boolean + tx-fifo-max-num: + description: Specifies the max number of packets the txfifo resizing logic + can account for when higher endpoint bursting is used. (bMaxBurst > 6) The + higher the number, the more fifo space the txfifo resizing logic will + allocate for that endpoint. + $ref: /schemas/types.yaml#/definitions/uint8 + minimum: 3 + snps,incr-burst-type-adjustment: description: Value for INCR burst type of GSBUSCFG0 register, undefined length INCR diff --git a/Documentation/usb/gadget-testing.rst b/Documentation/usb/gadget-testing.rst index 9d6276f82774..c18113077889 100644 --- a/Documentation/usb/gadget-testing.rst +++ b/Documentation/usb/gadget-testing.rst @@ -724,18 +724,28 @@ Function-specific configfs interface The function name to use when creating the function directory is "uac2". The uac2 function provides these attributes in its function directory: - =============== ==================================================== - c_chmask capture channel mask - c_srate capture sampling rate - c_ssize capture sample size (bytes) - c_sync capture synchronization type (async/adaptive) - fb_max maximum extra bandwidth in async mode - p_chmask playback channel mask - p_srate playback sampling rate - p_ssize playback sample size (bytes) - req_number the number of pre-allocated request for both capture - and playback - =============== ==================================================== + ================ ==================================================== + c_chmask capture channel mask + c_srate capture sampling rate + c_ssize capture sample size (bytes) + c_sync capture synchronization type (async/adaptive) + c_mute_present capture mute control enable + c_volume_present capture volume control enable + c_volume_min capture volume control min value (in 1/256 dB) + c_volume_max capture volume control max value (in 1/256 dB) + c_volume_res capture volume control resolution (in 1/256 dB) + fb_max maximum extra bandwidth in async mode + p_chmask playback channel mask + p_srate playback sampling rate + p_ssize playback sample size (bytes) + p_mute_present playback mute control enable + p_volume_present playback volume control enable + p_volume_min playback volume control min value (in 1/256 dB) + p_volume_max playback volume control max value (in 1/256 dB) + p_volume_res playback volume control resolution (in 1/256 dB) + req_number the number of pre-allocated request for both capture + and playback + ================ ==================================================== The attributes have sane default values. @@ -904,16 +914,26 @@ Function-specific configfs interface The function name to use when creating the function directory is "uac1". The uac1 function provides these attributes in its function directory: - ========== ==================================================== - c_chmask capture channel mask - c_srate capture sampling rate - c_ssize capture sample size (bytes) - p_chmask playback channel mask - p_srate playback sampling rate - p_ssize playback sample size (bytes) - req_number the number of pre-allocated request for both capture - and playback - ========== ==================================================== + ================ ==================================================== + c_chmask capture channel mask + c_srate capture sampling rate + c_ssize capture sample size (bytes) + c_mute_present capture mute control enable + c_volume_present capture volume control enable + c_volume_min capture volume control min value (in 1/256 dB) + c_volume_max capture volume control max value (in 1/256 dB) + c_volume_res capture volume control resolution (in 1/256 dB) + p_chmask playback channel mask + p_srate playback sampling rate + p_ssize playback sample size (bytes) + p_mute_present playback mute control enable + p_volume_present playback volume control enable + p_volume_min playback volume control min value (in 1/256 dB) + p_volume_max playback volume control max value (in 1/256 dB) + p_volume_res playback volume control resolution (in 1/256 dB) + req_number the number of pre-allocated request for both capture + and playback + ================ ==================================================== The attributes have sane default values. diff --git a/arch/arm64/boot/dts/qcom/sc7280-idp.dts b/arch/arm64/boot/dts/qcom/sc7280-idp.dts index 3900cfc09562..44326d816396 100644 --- a/arch/arm64/boot/dts/qcom/sc7280-idp.dts +++ b/arch/arm64/boot/dts/qcom/sc7280-idp.dts @@ -276,6 +276,45 @@ status = "okay"; }; +&usb_1 { + status = "okay"; +}; + +&usb_1_dwc3 { + dr_mode = "host"; +}; + +&usb_1_hsphy { + status = "okay"; + + vdda-pll-supply = <&vreg_l10c_0p8>; + vdda33-supply = <&vreg_l2b_3p0>; + vdda18-supply = <&vreg_l1c_1p8>; +}; + +&usb_1_qmpphy { + status = "okay"; + + vdda-phy-supply = <&vreg_l6b_1p2>; + vdda-pll-supply = <&vreg_l1b_0p8>; +}; + +&usb_2 { + status = "okay"; +}; + +&usb_2_dwc3 { + dr_mode = "peripheral"; +}; + +&usb_2_hsphy { + status = "okay"; + + vdda-pll-supply = <&vreg_l10c_0p8>; + vdda33-supply = <&vreg_l2b_3p0>; + vdda18-supply = <&vreg_l1c_1p8>; +}; + /* PINCTRL - additions to nodes defined in sc7280.dtsi */ &qup_uart5_default { diff --git a/arch/arm64/boot/dts/qcom/sc7280.dtsi b/arch/arm64/boot/dts/qcom/sc7280.dtsi index 188c5768a55a..903f56ae1185 100644 --- a/arch/arm64/boot/dts/qcom/sc7280.dtsi +++ b/arch/arm64/boot/dts/qcom/sc7280.dtsi @@ -1035,6 +1035,125 @@ }; }; + usb_1_hsphy: phy@88e3000 { + compatible = "qcom,sc7280-usb-hs-phy", + "qcom,usb-snps-hs-7nm-phy"; + reg = <0 0x088e3000 0 0x400>; + status = "disabled"; + #phy-cells = <0>; + + clocks = <&rpmhcc RPMH_CXO_CLK>; + clock-names = "ref"; + + resets = <&gcc GCC_QUSB2PHY_PRIM_BCR>; + }; + + usb_2_hsphy: phy@88e4000 { + compatible = "qcom,sc7280-usb-hs-phy", + "qcom,usb-snps-hs-7nm-phy"; + reg = <0 0x088e4000 0 0x400>; + status = "disabled"; + #phy-cells = <0>; + + clocks = <&rpmhcc RPMH_CXO_CLK>; + clock-names = "ref"; + + resets = <&gcc GCC_QUSB2PHY_SEC_BCR>; + }; + + usb_1_qmpphy: phy-wrapper@88e9000 { + compatible = "qcom,sc7280-qmp-usb3-dp-phy", + "qcom,sm8250-qmp-usb3-dp-phy"; + reg = <0 0x088e9000 0 0x200>, + <0 0x088e8000 0 0x40>, + <0 0x088ea000 0 0x200>; + status = "disabled"; + #address-cells = <2>; + #size-cells = <2>; + ranges; + + clocks = <&gcc GCC_USB3_PRIM_PHY_AUX_CLK>, + <&rpmhcc RPMH_CXO_CLK>, + <&gcc GCC_USB3_PRIM_PHY_COM_AUX_CLK>; + clock-names = "aux", "ref_clk_src", "com_aux"; + + resets = <&gcc GCC_USB3_DP_PHY_PRIM_BCR>, + <&gcc GCC_USB3_PHY_PRIM_BCR>; + reset-names = "phy", "common"; + + usb_1_ssphy: usb3-phy@88e9200 { + reg = <0 0x088e9200 0 0x200>, + <0 0x088e9400 0 0x200>, + <0 0x088e9c00 0 0x400>, + <0 0x088e9600 0 0x200>, + <0 0x088e9800 0 0x200>, + <0 0x088e9a00 0 0x100>; + #clock-cells = <0>; + #phy-cells = <0>; + clocks = <&gcc GCC_USB3_PRIM_PHY_PIPE_CLK>; + clock-names = "pipe0"; + clock-output-names = "usb3_phy_pipe_clk_src"; + }; + + dp_phy: dp-phy@88ea200 { + reg = <0 0x088ea200 0 0x200>, + <0 0x088ea400 0 0x200>, + <0 0x088eac00 0 0x400>, + <0 0x088ea600 0 0x200>, + <0 0x088ea800 0 0x200>, + <0 0x088eaa00 0 0x100>; + #phy-cells = <0>; + #clock-cells = <1>; + clocks = <&gcc GCC_USB3_PRIM_PHY_PIPE_CLK>; + clock-names = "pipe0"; + clock-output-names = "usb3_phy_pipe_clk_src"; + }; + }; + + usb_2: usb@8cf8800 { + compatible = "qcom,sc7280-dwc3", "qcom,dwc3"; + reg = <0 0x08cf8800 0 0x400>; + status = "disabled"; + #address-cells = <2>; + #size-cells = <2>; + ranges; + dma-ranges; + + 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"; + + assigned-clocks = <&gcc GCC_USB30_SEC_MOCK_UTMI_CLK>, + <&gcc GCC_USB30_SEC_MASTER_CLK>; + assigned-clock-rates = <19200000>, <200000000>; + + interrupts-extended = <&intc GIC_SPI 240 IRQ_TYPE_LEVEL_HIGH>, + <&pdc 13 IRQ_TYPE_EDGE_RISING>, + <&pdc 12 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "hs_phy_irq", + "dm_hs_phy_irq", "dp_hs_phy_irq"; + + power-domains = <&gcc GCC_USB30_SEC_GDSC>; + + resets = <&gcc GCC_USB30_SEC_BCR>; + + usb_2_dwc3: usb@8c00000 { + compatible = "snps,dwc3"; + reg = <0 0x08c00000 0 0xe000>; + interrupts = <GIC_SPI 242 IRQ_TYPE_LEVEL_HIGH>; + iommus = <&apps_smmu 0xa0 0x0>; + snps,dis_u2_susphy_quirk; + snps,dis_enblslpm_quirk; + phys = <&usb_2_hsphy>; + phy-names = "usb2-phy"; + maximum-speed = "high-speed"; + }; + }; + dc_noc: interconnect@90e0000 { reg = <0 0x090e0000 0 0x5080>; compatible = "qcom,sc7280-dc-noc"; @@ -1063,6 +1182,51 @@ qcom,bcm-voters = <&apps_bcm_voter>; }; + usb_1: usb@a6f8800 { + compatible = "qcom,sc7280-dwc3", "qcom,dwc3"; + reg = <0 0x0a6f8800 0 0x400>; + status = "disabled"; + #address-cells = <2>; + #size-cells = <2>; + ranges; + dma-ranges; + + 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"; + + 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 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"; + + power-domains = <&gcc GCC_USB30_PRIM_GDSC>; + + resets = <&gcc GCC_USB30_PRIM_BCR>; + + usb_1_dwc3: usb@a600000 { + compatible = "snps,dwc3"; + reg = <0 0x0a600000 0 0xe000>; + interrupts = <GIC_SPI 133 IRQ_TYPE_LEVEL_HIGH>; + iommus = <&apps_smmu 0xe0 0x0>; + snps,dis_u2_susphy_quirk; + snps,dis_enblslpm_quirk; + phys = <&usb_1_hsphy>, <&usb_1_ssphy>; + phy-names = "usb2-phy", "usb3-phy"; + maximum-speed = "super-speed"; + }; + }; + videocc: clock-controller@aaf0000 { compatible = "qcom,sc7280-videocc"; reg = <0 0xaaf0000 0 0x10000>; diff --git a/drivers/of/base.c b/drivers/of/base.c index 48e941f99558..5883d63c7714 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1821,6 +1821,7 @@ int of_add_property(struct device_node *np, struct property *prop) return rc; } +EXPORT_SYMBOL_GPL(of_add_property); int __of_remove_property(struct device_node *np, struct property *prop) { diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index fa44332845a1..c73da0532be4 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -35,6 +35,8 @@ #define NHI_MAILBOX_TIMEOUT 500 /* ms */ +#define QUIRK_AUTO_CLEAR_INT BIT(0) + static int ring_interrupt_index(struct tb_ring *ring) { int bit = ring->hop; @@ -66,14 +68,17 @@ static void ring_interrupt_active(struct tb_ring *ring, bool active) else index = ring->hop + ring->nhi->hop_count; - /* - * Ask the hardware to clear interrupt status bits automatically - * since we already know which interrupt was triggered. - */ - misc = ioread32(ring->nhi->iobase + REG_DMA_MISC); - if (!(misc & REG_DMA_MISC_INT_AUTO_CLEAR)) { - misc |= REG_DMA_MISC_INT_AUTO_CLEAR; - iowrite32(misc, ring->nhi->iobase + REG_DMA_MISC); + if (ring->nhi->quirks & QUIRK_AUTO_CLEAR_INT) { + /* + * Ask the hardware to clear interrupt status + * bits automatically since we already know + * which interrupt was triggered. + */ + misc = ioread32(ring->nhi->iobase + REG_DMA_MISC); + if (!(misc & REG_DMA_MISC_INT_AUTO_CLEAR)) { + misc |= REG_DMA_MISC_INT_AUTO_CLEAR; + iowrite32(misc, ring->nhi->iobase + REG_DMA_MISC); + } } ivr_base = ring->nhi->iobase + REG_INT_VEC_ALLOC_BASE; @@ -377,11 +382,24 @@ void tb_ring_poll_complete(struct tb_ring *ring) } EXPORT_SYMBOL_GPL(tb_ring_poll_complete); +static void ring_clear_msix(const struct tb_ring *ring) +{ + if (ring->nhi->quirks & QUIRK_AUTO_CLEAR_INT) + return; + + if (ring->is_tx) + ioread32(ring->nhi->iobase + REG_RING_NOTIFY_BASE); + else + ioread32(ring->nhi->iobase + REG_RING_NOTIFY_BASE + + 4 * (ring->nhi->hop_count / 32)); +} + static irqreturn_t ring_msix(int irq, void *data) { struct tb_ring *ring = data; spin_lock(&ring->nhi->lock); + ring_clear_msix(ring); spin_lock(&ring->lock); __ring_interrupt(ring); spin_unlock(&ring->lock); @@ -1074,6 +1092,16 @@ static void nhi_shutdown(struct tb_nhi *nhi) nhi->ops->shutdown(nhi); } +static void nhi_check_quirks(struct tb_nhi *nhi) +{ + /* + * Intel hardware supports auto clear of the interrupt status + * reqister right after interrupt is being issued. + */ + if (nhi->pdev->vendor == PCI_VENDOR_ID_INTEL) + nhi->quirks |= QUIRK_AUTO_CLEAR_INT; +} + static int nhi_init_msi(struct tb_nhi *nhi) { struct pci_dev *pdev = nhi->pdev; @@ -1190,6 +1218,8 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (!nhi->tx_rings || !nhi->rx_rings) return -ENOMEM; + nhi_check_quirks(nhi); + res = nhi_init_msi(nhi); if (res) { dev_err(&pdev->dev, "cannot enable MSI, aborting\n"); diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 10d6b228cc94..3014146081c1 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -724,6 +724,12 @@ static int tb_init_port(struct tb_port *port) int res; int cap; + INIT_LIST_HEAD(&port->list); + + /* Control adapter does not have configuration space */ + if (!port->port) + return 0; + res = tb_port_read(port, &port->config, TB_CFG_PORT, 0, 8); if (res) { if (res == -ENODEV) { @@ -736,7 +742,7 @@ static int tb_init_port(struct tb_port *port) } /* Port 0 is the switch itself and has no PHY. */ - if (port->config.type == TB_TYPE_PORT && port->port != 0) { + if (port->config.type == TB_TYPE_PORT) { cap = tb_port_find_cap(port, TB_PORT_CAP_PHY); if (cap > 0) @@ -762,7 +768,7 @@ static int tb_init_port(struct tb_port *port) if (!port->ctl_credits) port->ctl_credits = 2; - } else if (port->port != 0) { + } else { cap = tb_port_find_cap(port, TB_PORT_CAP_ADAP); if (cap > 0) port->cap_adap = cap; @@ -773,10 +779,7 @@ static int tb_init_port(struct tb_port *port) ADP_CS_4_TOTAL_BUFFERS_SHIFT; tb_dump_port(port->sw->tb, port); - - INIT_LIST_HEAD(&port->list); return 0; - } static int tb_port_alloc_hopid(struct tb_port *port, bool in, int min_hopid, @@ -1498,6 +1501,7 @@ static ssize_t authorized_show(struct device *dev, static int disapprove_switch(struct device *dev, void *not_used) { + char *envp[] = { "AUTHORIZED=0", NULL }; struct tb_switch *sw; sw = tb_to_switch(dev); @@ -1514,7 +1518,7 @@ static int disapprove_switch(struct device *dev, void *not_used) return ret; sw->authorized = 0; - kobject_uevent(&sw->dev.kobj, KOBJ_CHANGE); + kobject_uevent_env(&sw->dev.kobj, KOBJ_CHANGE, envp); } return 0; @@ -1522,7 +1526,9 @@ static int disapprove_switch(struct device *dev, void *not_used) static int tb_switch_set_authorized(struct tb_switch *sw, unsigned int val) { + char envp_string[13]; int ret = -EINVAL; + char *envp[] = { envp_string, NULL }; if (!mutex_trylock(&sw->tb->lock)) return restart_syscall(); @@ -1559,8 +1565,12 @@ static int tb_switch_set_authorized(struct tb_switch *sw, unsigned int val) if (!ret) { sw->authorized = val; - /* Notify status change to the userspace */ - kobject_uevent(&sw->dev.kobj, KOBJ_CHANGE); + /* + * Notify status change to the userspace, informing the new + * value of /sys/bus/thunderbolt/devices/.../authorized. + */ + sprintf(envp_string, "AUTHORIZED=%u", sw->authorized); + kobject_uevent_env(&sw->dev.kobj, KOBJ_CHANGE, envp); } unlock: @@ -2443,7 +2453,7 @@ static void tb_switch_default_link_ports(struct tb_switch *sw) { int i; - for (i = 1; i <= sw->config.max_port_number; i += 2) { + for (i = 1; i <= sw->config.max_port_number; i++) { struct tb_port *port = &sw->ports[i]; struct tb_port *subordinate; diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 26475b409b53..578a439e71b5 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -11,7 +11,7 @@ config USB_OHCI_BIG_ENDIAN_MMIO config USB_OHCI_LITTLE_ENDIAN bool - default n if STB03xxx || PPC_MPC52xx + default n if PPC_MPC52xx default y config USB_EHCI_BIG_ENDIAN_MMIO diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 3e2cc95b7b0b..643edf5fe18c 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -31,7 +31,6 @@ obj-$(CONFIG_USB_SL811_HCD) += host/ obj-$(CONFIG_USB_ISP1362_HCD) += host/ obj-$(CONFIG_USB_U132_HCD) += host/ obj-$(CONFIG_USB_R8A66597_HCD) += host/ -obj-$(CONFIG_USB_HWA_HCD) += host/ obj-$(CONFIG_USB_FSL_USB2) += host/ obj-$(CONFIG_USB_FOTG210_HCD) += host/ obj-$(CONFIG_USB_MAX3421_HCD) += host/ diff --git a/drivers/usb/cdns3/cdnsp-mem.c b/drivers/usb/cdns3/cdnsp-mem.c index a47948a1623f..ad9aee3f1e39 100644 --- a/drivers/usb/cdns3/cdnsp-mem.c +++ b/drivers/usb/cdns3/cdnsp-mem.c @@ -882,7 +882,7 @@ static u32 cdnsp_get_endpoint_max_burst(struct usb_gadget *g, if (g->speed == USB_SPEED_HIGH && (usb_endpoint_xfer_isoc(pep->endpoint.desc) || usb_endpoint_xfer_int(pep->endpoint.desc))) - return (usb_endpoint_maxp(pep->endpoint.desc) & 0x1800) >> 11; + return usb_endpoint_maxp_mult(pep->endpoint.desc) - 1; return 0; } diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c index e86d13c04bdb..bdc3885c0d49 100644 --- a/drivers/usb/chipidea/host.c +++ b/drivers/usb/chipidea/host.c @@ -240,15 +240,18 @@ static int ci_ehci_hub_control( ) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); + unsigned int ports = HCS_N_PORTS(ehci->hcs_params); u32 __iomem *status_reg; - u32 temp; + u32 temp, port_index; unsigned long flags; int retval = 0; bool done = false; struct device *dev = hcd->self.controller; struct ci_hdrc *ci = dev_get_drvdata(dev); - status_reg = &ehci->regs->port_status[(wIndex & 0xff) - 1]; + port_index = wIndex & 0xff; + port_index -= (port_index > 0); + status_reg = &ehci->regs->port_status[port_index]; spin_lock_irqsave(&ehci->lock, flags); @@ -260,6 +263,11 @@ static int ci_ehci_hub_control( } if (typeReq == SetPortFeature && wValue == USB_PORT_FEAT_SUSPEND) { + if (!wIndex || wIndex > ports) { + retval = -EPIPE; + goto done; + } + temp = ehci_readl(ehci, status_reg); if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) != 0) { retval = -EPIPE; @@ -288,7 +296,7 @@ static int ci_ehci_hub_control( ehci_writel(ehci, temp, status_reg); } - set_bit((wIndex & 0xff) - 1, &ehci->suspended_ports); + set_bit(port_index, &ehci->suspended_ports); goto done; } diff --git a/drivers/usb/common/common.c b/drivers/usb/common/common.c index 347fb3d3894a..c9bdeb4ddcb5 100644 --- a/drivers/usb/common/common.c +++ b/drivers/usb/common/common.c @@ -201,6 +201,26 @@ enum usb_dr_mode usb_get_dr_mode(struct device *dev) EXPORT_SYMBOL_GPL(usb_get_dr_mode); /** + * usb_get_role_switch_default_mode - Get default mode for given device + * @dev: Pointer to the given device + * + * The function gets string from property 'role-switch-default-mode', + * and returns the corresponding enum usb_dr_mode. + */ +enum usb_dr_mode usb_get_role_switch_default_mode(struct device *dev) +{ + const char *str; + int ret; + + ret = device_property_read_string(dev, "role-switch-default-mode", &str); + if (ret < 0) + return USB_DR_MODE_UNKNOWN; + + return usb_get_dr_mode_from_string(str); +} +EXPORT_SYMBOL_GPL(usb_get_role_switch_default_mode); + +/** * usb_decode_interval - Decode bInterval into the time expressed in 1us unit * @epd: The descriptor of the endpoint * @speed: The speed that the endpoint works as diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index 272ae5722c86..cf0bcd0dc320 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -295,7 +295,7 @@ void dwc2_hib_restore_common(struct dwc2_hsotg *hsotg, int rem_wakeup, if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS, GINTSTS_RESTOREDONE, 20000)) { dev_dbg(hsotg->dev, - "%s: Restore Done wan't generated here\n", + "%s: Restore Done wasn't generated here\n", __func__); } else { dev_dbg(hsotg->dev, "restore done generated here\n"); diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 483de2bbfaab..cb9059a8444b 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -122,6 +122,7 @@ struct dwc2_hsotg_req; * @periodic: Set if this is a periodic ep, such as Interrupt * @isochronous: Set if this is a isochronous ep * @send_zlp: Set if we need to send a zero-length packet. + * @wedged: Set if ep is wedged. * @desc_list_dma: The DMA address of descriptor chain currently in use. * @desc_list: Pointer to descriptor DMA chain head currently in use. * @desc_count: Count of entries within the DMA descriptor chain of EP. @@ -172,6 +173,7 @@ struct dwc2_hsotg_ep { unsigned int periodic:1; unsigned int isochronous:1; unsigned int send_zlp:1; + unsigned int wedged:1; unsigned int target_frame; #define TARGET_FRAME_INITIAL 0xFFFFFFFF bool frame_overrun; diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 3146df6e6510..837237e4bc96 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -1806,7 +1806,8 @@ static int dwc2_hsotg_process_req_feature(struct dwc2_hsotg *hsotg, case USB_ENDPOINT_HALT: halted = ep->halted; - dwc2_hsotg_ep_sethalt(&ep->ep, set, true); + if (!ep->wedged) + dwc2_hsotg_ep_sethalt(&ep->ep, set, true); ret = dwc2_hsotg_send_reply(hsotg, ep0, NULL, 0); if (ret) { @@ -4066,6 +4067,7 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep, hs_ep->isochronous = 0; hs_ep->periodic = 0; hs_ep->halted = 0; + hs_ep->wedged = 0; hs_ep->interval = desc->bInterval; switch (ep_type) { @@ -4307,6 +4309,27 @@ static int dwc2_hsotg_ep_dequeue(struct usb_ep *ep, struct usb_request *req) } /** + * dwc2_gadget_ep_set_wedge - set wedge on a given endpoint + * @ep: The endpoint to be wedged. + * + */ +static int dwc2_gadget_ep_set_wedge(struct usb_ep *ep) +{ + struct dwc2_hsotg_ep *hs_ep = our_ep(ep); + struct dwc2_hsotg *hs = hs_ep->parent; + + unsigned long flags; + int ret; + + spin_lock_irqsave(&hs->lock, flags); + hs_ep->wedged = 1; + ret = dwc2_hsotg_ep_sethalt(ep, 1, false); + spin_unlock_irqrestore(&hs->lock, flags); + + return ret; +} + +/** * dwc2_hsotg_ep_sethalt - set halt on a given endpoint * @ep: The endpoint to set halt. * @value: Set or unset the halt. @@ -4357,6 +4380,7 @@ static int dwc2_hsotg_ep_sethalt(struct usb_ep *ep, int value, bool now) epctl |= DXEPCTL_EPDIS; } else { epctl &= ~DXEPCTL_STALL; + hs_ep->wedged = 0; xfertype = epctl & DXEPCTL_EPTYPE_MASK; if (xfertype == DXEPCTL_EPTYPE_BULK || xfertype == DXEPCTL_EPTYPE_INTERRUPT) @@ -4376,6 +4400,7 @@ static int dwc2_hsotg_ep_sethalt(struct usb_ep *ep, int value, bool now) // STALL bit will be set in GOUTNAKEFF interrupt handler } else { epctl &= ~DXEPCTL_STALL; + hs_ep->wedged = 0; xfertype = epctl & DXEPCTL_EPTYPE_MASK; if (xfertype == DXEPCTL_EPTYPE_BULK || xfertype == DXEPCTL_EPTYPE_INTERRUPT) @@ -4415,6 +4440,7 @@ static const struct usb_ep_ops dwc2_hsotg_ep_ops = { .queue = dwc2_hsotg_ep_queue_lock, .dequeue = dwc2_hsotg_ep_dequeue, .set_halt = dwc2_hsotg_ep_sethalt_lock, + .set_wedge = dwc2_gadget_ep_set_wedge, /* note, don't believe we have any call for the fifo routines */ }; @@ -4683,12 +4709,35 @@ static int dwc2_hsotg_vbus_draw(struct usb_gadget *gadget, unsigned int mA) return usb_phy_set_power(hsotg->uphy, mA); } +static void dwc2_gadget_set_speed(struct usb_gadget *g, enum usb_device_speed speed) +{ + struct dwc2_hsotg *hsotg = to_hsotg(g); + unsigned long flags; + + spin_lock_irqsave(&hsotg->lock, flags); + switch (speed) { + case USB_SPEED_HIGH: + hsotg->params.speed = DWC2_SPEED_PARAM_HIGH; + break; + case USB_SPEED_FULL: + hsotg->params.speed = DWC2_SPEED_PARAM_FULL; + break; + case USB_SPEED_LOW: + hsotg->params.speed = DWC2_SPEED_PARAM_LOW; + break; + default: + dev_err(hsotg->dev, "invalid speed (%d)\n", speed); + } + spin_unlock_irqrestore(&hsotg->lock, flags); +} + static const struct usb_gadget_ops dwc2_hsotg_gadget_ops = { .get_frame = dwc2_hsotg_gadget_getframe, .set_selfpowered = dwc2_hsotg_set_selfpowered, .udc_start = dwc2_hsotg_udc_start, .udc_stop = dwc2_hsotg_udc_stop, .pullup = dwc2_hsotg_pullup, + .udc_set_speed = dwc2_gadget_set_speed, .vbus_session = dwc2_hsotg_vbus_session, .vbus_draw = dwc2_hsotg_vbus_draw, }; diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index ba74ad7f6995..01866dcb953b 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1050,6 +1050,15 @@ static int dwc3_core_init(struct dwc3 *dwc) if (!DWC3_VER_IS_PRIOR(DWC3, 290A)) reg |= DWC3_GUCTL1_DEV_L1_EXIT_BY_HW; + /* + * Decouple USB 2.0 L1 & L2 events which will allow for + * gadget driver to only receive U3/L2 suspend & wakeup + * events and prevent the more frequent L1 LPM transitions + * from interrupting the driver. + */ + if (!DWC3_VER_IS_PRIOR(DWC3, 300A)) + reg |= DWC3_GUCTL1_DEV_DECOUPLE_L1L2_EVT; + if (dwc->dis_tx_ipgap_linecheck_quirk) reg |= DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS; @@ -1267,6 +1276,7 @@ static void dwc3_get_properties(struct dwc3 *dwc) u8 rx_max_burst_prd; u8 tx_thr_num_pkt_prd; u8 tx_max_burst_prd; + u8 tx_fifo_resize_max_num; const char *usb_psy_name; int ret; @@ -1282,6 +1292,13 @@ static void dwc3_get_properties(struct dwc3 *dwc) */ hird_threshold = 12; + /* + * default to a TXFIFO size large enough to fit 6 max packets. This + * allows for systems with larger bus latencies to have some headroom + * for endpoints that have a large bMaxBurst value. + */ + tx_fifo_resize_max_num = 6; + dwc->maximum_speed = usb_get_maximum_speed(dev); dwc->max_ssp_rate = usb_get_maximum_ssp_rate(dev); dwc->dr_mode = usb_get_dr_mode(dev); @@ -1325,6 +1342,11 @@ static void dwc3_get_properties(struct dwc3 *dwc) &tx_thr_num_pkt_prd); device_property_read_u8(dev, "snps,tx-max-burst-prd", &tx_max_burst_prd); + dwc->do_fifo_resize = device_property_read_bool(dev, + "tx-fifo-resize"); + if (dwc->do_fifo_resize) + device_property_read_u8(dev, "tx-fifo-max-num", + &tx_fifo_resize_max_num); dwc->disable_scramble_quirk = device_property_read_bool(dev, "snps,disable_scramble_quirk"); @@ -1390,6 +1412,8 @@ static void dwc3_get_properties(struct dwc3 *dwc) dwc->tx_max_burst_prd = tx_max_burst_prd; dwc->imod_interval = 0; + + dwc->tx_fifo_resize_max_num = tx_fifo_resize_max_num; } /* check whether the core supports IMOD */ diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 5991766239ba..5612bfdf37da 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -256,9 +256,10 @@ #define DWC3_GUCTL_HSTINAUTORETRY BIT(14) /* Global User Control 1 Register */ -#define DWC3_GUCTL1_PARKMODE_DISABLE_SS BIT(17) +#define DWC3_GUCTL1_DEV_DECOUPLE_L1L2_EVT BIT(31) #define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS BIT(28) -#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24) +#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24) +#define DWC3_GUCTL1_PARKMODE_DISABLE_SS BIT(17) /* Global Status Register */ #define DWC3_GSTS_OTG_IP BIT(10) @@ -1023,6 +1024,7 @@ struct dwc3_scratchpad_array { * @rx_max_burst_prd: max periodic ESS receive burst size * @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 * @hsphy_interface: "utmi" or "ulpi" * @connected: true when we're connected to a host, false otherwise * @delayed_status: true when gadget driver asks for delayed status @@ -1037,6 +1039,7 @@ struct dwc3_scratchpad_array { * 1 - utmi_l1_suspend_n * @is_fpga: true when we are using the FPGA board * @pending_events: true when we have pending IRQs to be handled + * @do_fifo_resize: true when txfifo resizing is enabled for dwc3 endpoints * @pullups_connected: true when Run/Stop bit is set * @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround * @three_stage_setup: set if we perform a three phase setup @@ -1079,6 +1082,11 @@ struct dwc3_scratchpad_array { * @dis_split_quirk: set to disable split boundary. * @imod_interval: set the interrupt moderation interval in 250ns * increments or 0 to disable. + * @max_cfg_eps: current max number of IN eps used across all USB configs. + * @last_fifo_depth: last fifo depth used to determine next fifo ram start + * address. + * @num_ep_resized: carries the current number endpoints which have had its tx + * fifo resized. */ struct dwc3 { struct work_struct drd_work; @@ -1233,6 +1241,7 @@ struct dwc3 { u8 rx_max_burst_prd; u8 tx_thr_num_pkt_prd; u8 tx_max_burst_prd; + u8 tx_fifo_resize_max_num; const char *hsphy_interface; @@ -1246,6 +1255,7 @@ struct dwc3 { unsigned is_utmi_l1_suspend:1; unsigned is_fpga:1; unsigned pending_events:1; + unsigned do_fifo_resize:1; unsigned pullups_connected:1; unsigned setup_packet_pending:1; unsigned three_stage_setup:1; @@ -1282,6 +1292,10 @@ struct dwc3 { unsigned async_callbacks:1; u16 imod_interval; + + int max_cfg_eps; + int last_fifo_depth; + int num_ep_resized; }; #define INCRX_BURST_MODE 0 @@ -1513,6 +1527,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd, struct dwc3_gadget_ep_cmd_params *params); int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd, u32 param); +void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc); #else static inline int dwc3_gadget_init(struct dwc3 *dwc) { return 0; } @@ -1532,6 +1547,8 @@ static inline int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd, static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param) { return 0; } +static inline void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc) +{ } #endif #if IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c index 8fcbac10510c..d7f76835137f 100644 --- a/drivers/usb/dwc3/drd.c +++ b/drivers/usb/dwc3/drd.c @@ -541,14 +541,10 @@ static enum usb_role dwc3_usb_role_switch_get(struct usb_role_switch *sw) static int dwc3_setup_role_switch(struct dwc3 *dwc) { struct usb_role_switch_desc dwc3_role_switch = {NULL}; - const char *str; u32 mode; - int ret; - ret = device_property_read_string(dwc->dev, "role-switch-default-mode", - &str); - if (ret >= 0 && !strncmp(str, "host", strlen("host"))) { - dwc->role_switch_default_mode = USB_DR_MODE_HOST; + dwc->role_switch_default_mode = usb_get_role_switch_default_mode(dwc->dev); + if (dwc->role_switch_default_mode == USB_DR_MODE_HOST) { mode = DWC3_GCTL_PRTCAP_HOST; } else { dwc->role_switch_default_mode = USB_DR_MODE_PERIPHERAL; diff --git a/drivers/usb/dwc3/dwc3-imx8mp.c b/drivers/usb/dwc3/dwc3-imx8mp.c index 756faa46d33a..d328d20abfbc 100644 --- a/drivers/usb/dwc3/dwc3-imx8mp.c +++ b/drivers/usb/dwc3/dwc3-imx8mp.c @@ -152,13 +152,6 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev) } dwc3_imx->irq = irq; - err = devm_request_threaded_irq(dev, irq, NULL, dwc3_imx8mp_interrupt, - IRQF_ONESHOT, dev_name(dev), dwc3_imx); - if (err) { - dev_err(dev, "failed to request IRQ #%d --> %d\n", irq, err); - goto disable_clks; - } - pm_runtime_set_active(dev); pm_runtime_enable(dev); err = pm_runtime_get_sync(dev); @@ -186,6 +179,13 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev) } of_node_put(dwc3_np); + err = devm_request_threaded_irq(dev, irq, NULL, dwc3_imx8mp_interrupt, + IRQF_ONESHOT, dev_name(dev), dwc3_imx); + if (err) { + dev_err(dev, "failed to request IRQ #%d --> %d\n", irq, err); + goto depopulate; + } + device_set_wakeup_capable(dev, true); pm_runtime_put(dev); diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c index ffe301d6ea35..d0f9b7c296b0 100644 --- a/drivers/usb/dwc3/dwc3-meson-g12a.c +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c @@ -598,6 +598,8 @@ static int dwc3_meson_g12a_otg_init(struct platform_device *pdev, USB_R5_ID_DIG_IRQ, 0); irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, dwc3_meson_g12a_irq_thread, IRQF_ONESHOT, pdev->name, priv); diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 2b37bdd39a74..7ff8fc8f79a9 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -44,6 +44,7 @@ #define PCI_DEVICE_ID_INTEL_ADLM 0x54ee #define PCI_DEVICE_ID_INTEL_ADLS 0x7ae1 #define PCI_DEVICE_ID_INTEL_TGL 0x9a15 +#define PCI_DEVICE_ID_AMD_MR 0x163a #define PCI_INTEL_BXT_DSM_GUID "732b85d5-b7a7-4a1b-9ba0-4bbd00ffd511" #define PCI_INTEL_BXT_FUNC_PMU_PWR 4 @@ -148,6 +149,14 @@ static const struct property_entry dwc3_pci_amd_properties[] = { {} }; +static const struct property_entry dwc3_pci_mr_properties[] = { + PROPERTY_ENTRY_STRING("dr_mode", "otg"), + PROPERTY_ENTRY_BOOL("usb-role-switch"), + PROPERTY_ENTRY_STRING("role-switch-default-mode", "host"), + PROPERTY_ENTRY_BOOL("linux,sysdev_is_parent"), + {} +}; + static const struct software_node dwc3_pci_intel_swnode = { .properties = dwc3_pci_intel_properties, }; @@ -160,6 +169,10 @@ static const struct software_node dwc3_pci_amd_swnode = { .properties = dwc3_pci_amd_properties, }; +static const struct software_node dwc3_pci_amd_mr_swnode = { + .properties = dwc3_pci_mr_properties, +}; + static int dwc3_pci_quirks(struct dwc3_pci *dwc) { struct pci_dev *pdev = dwc->pci; @@ -401,6 +414,10 @@ static const struct pci_device_id dwc3_pci_id_table[] = { { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_NL_USB), (kernel_ulong_t) &dwc3_pci_amd_swnode, }, + + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_MR), + (kernel_ulong_t)&dwc3_pci_amd_mr_swnode, }, + { } /* Terminating Entry */ }; MODULE_DEVICE_TABLE(pci, dwc3_pci_id_table); diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c index 49e6ca94486d..9abbd01028c5 100644 --- a/drivers/usb/dwc3/dwc3-qcom.c +++ b/drivers/usb/dwc3/dwc3-qcom.c @@ -115,7 +115,7 @@ static inline void dwc3_qcom_clrbits(void __iomem *base, u32 offset, u32 val) readl(base + offset); } -static void dwc3_qcom_vbus_overrride_enable(struct dwc3_qcom *qcom, bool enable) +static void dwc3_qcom_vbus_override_enable(struct dwc3_qcom *qcom, bool enable) { if (enable) { dwc3_qcom_setbits(qcom->qscratch_base, QSCRATCH_SS_PHY_CTRL, @@ -136,7 +136,7 @@ static int dwc3_qcom_vbus_notifier(struct notifier_block *nb, struct dwc3_qcom *qcom = container_of(nb, struct dwc3_qcom, vbus_nb); /* enable vbus override for device mode */ - dwc3_qcom_vbus_overrride_enable(qcom, event); + dwc3_qcom_vbus_override_enable(qcom, event); qcom->mode = event ? USB_DR_MODE_PERIPHERAL : USB_DR_MODE_HOST; return NOTIFY_DONE; @@ -148,7 +148,7 @@ static int dwc3_qcom_host_notifier(struct notifier_block *nb, struct dwc3_qcom *qcom = container_of(nb, struct dwc3_qcom, host_nb); /* disable vbus override in host mode */ - dwc3_qcom_vbus_overrride_enable(qcom, !event); + dwc3_qcom_vbus_override_enable(qcom, !event); qcom->mode = event ? USB_DR_MODE_HOST : USB_DR_MODE_PERIPHERAL; return NOTIFY_DONE; @@ -614,6 +614,10 @@ static int dwc3_qcom_acpi_register_core(struct platform_device *pdev) qcom->acpi_pdata->dwc3_core_base_size; irq = platform_get_irq(pdev_irq, 0); + if (irq < 0) { + ret = irq; + goto out; + } child_res[1].flags = IORESOURCE_IRQ; child_res[1].start = child_res[1].end = irq; @@ -645,6 +649,7 @@ static int dwc3_qcom_of_register_core(struct platform_device *pdev) struct dwc3_qcom *qcom = platform_get_drvdata(pdev); struct device_node *np = pdev->dev.of_node, *dwc3_np; struct device *dev = &pdev->dev; + struct property *prop; int ret; dwc3_np = of_get_compatible_child(np, "snps,dwc3"); @@ -653,6 +658,20 @@ static int dwc3_qcom_of_register_core(struct platform_device *pdev) return -ENODEV; } + prop = devm_kzalloc(dev, sizeof(*prop), GFP_KERNEL); + if (!prop) { + ret = -ENOMEM; + dev_err(dev, "unable to allocate memory for property\n"); + goto node_put; + } + + prop->name = "tx-fifo-resize"; + ret = of_add_property(dwc3_np, prop); + if (ret) { + dev_err(dev, "unable to add property\n"); + goto node_put; + } + ret = of_platform_populate(np, NULL, NULL, dev); if (ret) { dev_err(dev, "failed to register dwc3 core - %d\n", ret); @@ -811,7 +830,7 @@ static int dwc3_qcom_probe(struct platform_device *pdev) /* enable vbus override for device mode */ if (qcom->mode == USB_DR_MODE_PERIPHERAL) - dwc3_qcom_vbus_overrride_enable(qcom, true); + dwc3_qcom_vbus_override_enable(qcom, true); /* register extcon to override sw_vbus on Vbus change later */ ret = dwc3_qcom_register_extcon(qcom); @@ -916,6 +935,7 @@ static const struct of_device_id dwc3_qcom_of_match[] = { { .compatible = "qcom,dwc3" }, { .compatible = "qcom,msm8996-dwc3" }, { .compatible = "qcom,msm8998-dwc3" }, + { .compatible = "qcom,sdm660-dwc3" }, { .compatible = "qcom,sdm845-dwc3" }, { } }; diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 2f9e45eed228..658739410992 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -621,6 +621,8 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) return -EINVAL; case USB_STATE_ADDRESS: + dwc3_gadget_clear_tx_fifos(dwc); + ret = dwc3_ep0_delegate_req(dwc, ctrl); /* if the cfg matches and the cfg is non zero */ if (cfg && (!ret || (ret == USB_GADGET_DELAYED_STATUS))) { diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index ccb68fe6202e..804b50548163 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -632,6 +632,187 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt); /** + * 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: + * + * DWC3 revision 280A and prior: + * fifo_size = mult * (max_packet / mdwidth) + 1; + * + * DWC3 revision 290A and onwards: + * fifo_size = mult * ((max_packet + mdwidth)/mdwidth + 1) + 1 + * + * The max packet size is set to 1024, as the txfifo requirements mainly apply + * to super speed USB use cases. However, it is safe to overestimate the fifo + * allocations for other scenarios, i.e. high speed USB. + */ +static int dwc3_gadget_calc_tx_fifo_size(struct dwc3 *dwc, int mult) +{ + int max_packet = 1024; + int fifo_size; + int mdwidth; + + mdwidth = dwc3_mdwidth(dwc); + + /* MDWIDTH is represented in bits, we need it in bytes */ + mdwidth >>= 3; + + if (DWC3_VER_IS_PRIOR(DWC3, 290A)) + fifo_size = mult * (max_packet / mdwidth) + 1; + else + fifo_size = mult * ((max_packet + mdwidth) / mdwidth) + 1; + return fifo_size; +} + +/** + * dwc3_gadget_clear_tx_fifo_size - Clears txfifo allocation + * @dwc: pointer to the DWC3 context + * + * Iterates through all the endpoint registers and clears the previous txfifo + * allocations. + */ +void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc) +{ + struct dwc3_ep *dep; + int fifo_depth; + int size; + int num; + + if (!dwc->do_fifo_resize) + return; + + /* Read ep0IN related TXFIFO size */ + dep = dwc->eps[1]; + size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0)); + if (DWC3_IP_IS(DWC3)) + fifo_depth = DWC3_GTXFIFOSIZ_TXFDEP(size); + else + fifo_depth = DWC31_GTXFIFOSIZ_TXFDEP(size); + + dwc->last_fifo_depth = fifo_depth; + /* Clear existing TXFIFO for all IN eps except ep0 */ + for (num = 3; num < min_t(int, dwc->num_eps, DWC3_ENDPOINTS_NUM); + num += 2) { + dep = dwc->eps[num]; + /* Don't change TXFRAMNUM on usb31 version */ + size = DWC3_IP_IS(DWC3) ? 0 : + dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(num >> 1)) & + DWC31_GTXFIFOSIZ_TXFRAMNUM; + + dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(num >> 1), size); + } + dwc->num_ep_resized = 0; +} + +/* + * dwc3_gadget_resize_tx_fifos - reallocate fifo spaces for current use-case + * @dwc: pointer to our context structure + * + * This function will a best effort FIFO allocation in order + * to improve FIFO usage and throughput, while still allowing + * us to enable as many endpoints as possible. + * + * Keep in mind that this operation will be highly dependent + * on the configured size for RAM1 - which contains TxFifo -, + * the amount of endpoints enabled on coreConsultant tool, and + * the width of the Master Bus. + * + * In general, FIFO depths are represented with the following equation: + * + * fifo_size = mult * ((max_packet + mdwidth)/mdwidth + 1) + 1 + * + * In conjunction with dwc3_gadget_check_config(), this resizing logic will + * ensure that all endpoints will have enough internal memory for one max + * packet per endpoint. + */ +static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep) +{ + struct dwc3 *dwc = dep->dwc; + int fifo_0_start; + int ram1_depth; + int fifo_size; + int min_depth; + int num_in_ep; + int remaining; + int num_fifos = 1; + int fifo; + int tmp; + + if (!dwc->do_fifo_resize) + return 0; + + /* resize IN endpoints except ep0 */ + if (!usb_endpoint_dir_in(dep->endpoint.desc) || dep->number <= 1) + return 0; + + ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7); + + if ((dep->endpoint.maxburst > 1 && + usb_endpoint_xfer_bulk(dep->endpoint.desc)) || + usb_endpoint_xfer_isoc(dep->endpoint.desc)) + num_fifos = 3; + + if (dep->endpoint.maxburst > 6 && + usb_endpoint_xfer_bulk(dep->endpoint.desc) && DWC3_IP_IS(DWC31)) + num_fifos = dwc->tx_fifo_resize_max_num; + + /* FIFO size for a single buffer */ + fifo = dwc3_gadget_calc_tx_fifo_size(dwc, 1); + + /* Calculate the number of remaining EPs w/o any FIFO */ + num_in_ep = dwc->max_cfg_eps; + num_in_ep -= dwc->num_ep_resized; + + /* Reserve at least one FIFO for the number of IN EPs */ + min_depth = num_in_ep * (fifo + 1); + remaining = ram1_depth - min_depth - dwc->last_fifo_depth; + remaining = max_t(int, 0, remaining); + /* + * We've already reserved 1 FIFO per EP, so check what we can fit in + * addition to it. If there is not enough remaining space, allocate + * all the remaining space to the EP. + */ + fifo_size = (num_fifos - 1) * fifo; + if (remaining < fifo_size) + fifo_size = remaining; + + fifo_size += fifo; + /* Last increment according to the TX FIFO size equation */ + fifo_size++; + + /* Check if TXFIFOs start at non-zero addr */ + tmp = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0)); + fifo_0_start = DWC3_GTXFIFOSIZ_TXFSTADDR(tmp); + + fifo_size |= (fifo_0_start + (dwc->last_fifo_depth << 16)); + if (DWC3_IP_IS(DWC3)) + dwc->last_fifo_depth += DWC3_GTXFIFOSIZ_TXFDEP(fifo_size); + else + dwc->last_fifo_depth += DWC31_GTXFIFOSIZ_TXFDEP(fifo_size); + + /* Check fifo size allocation doesn't exceed available RAM size. */ + if (dwc->last_fifo_depth >= ram1_depth) { + dev_err(dwc->dev, "Fifosize(%d) > RAM size(%d) %s depth:%d\n", + dwc->last_fifo_depth, ram1_depth, + dep->endpoint.name, fifo_size); + if (DWC3_IP_IS(DWC3)) + fifo_size = DWC3_GTXFIFOSIZ_TXFDEP(fifo_size); + else + fifo_size = DWC31_GTXFIFOSIZ_TXFDEP(fifo_size); + + dwc->last_fifo_depth -= fifo_size; + return -ENOMEM; + } + + dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(dep->number >> 1), fifo_size); + dwc->num_ep_resized++; + + return 0; +} + +/** * __dwc3_gadget_ep_enable - initializes a hw endpoint * @dep: endpoint to be initialized * @action: one of INIT, MODIFY or RESTORE @@ -648,6 +829,10 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action) int ret; if (!(dep->flags & DWC3_EP_ENABLED)) { + ret = dwc3_gadget_resize_tx_fifos(dep); + if (ret) + return ret; + ret = dwc3_gadget_start_config(dep); if (ret) return ret; @@ -2508,6 +2693,7 @@ static int dwc3_gadget_stop(struct usb_gadget *g) spin_lock_irqsave(&dwc->lock, flags); dwc->gadget_driver = NULL; + dwc->max_cfg_eps = 0; spin_unlock_irqrestore(&dwc->lock, flags); free_irq(dwc->irq_gadget, dwc->ev_buf); @@ -2595,6 +2781,51 @@ static int dwc3_gadget_vbus_draw(struct usb_gadget *g, unsigned int mA) return ret; } +/** + * dwc3_gadget_check_config - ensure dwc3 can support the USB configuration + * @g: 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 dwc3_gadget_check_config(struct usb_gadget *g) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + struct usb_ep *ep; + int fifo_size = 0; + int ram1_depth; + int ep_num = 0; + + if (!dwc->do_fifo_resize) + return 0; + + list_for_each_entry(ep, &g->ep_list, ep_list) { + /* Only interested in the IN endpoints */ + if (ep->claimed && (ep->address & USB_DIR_IN)) + ep_num++; + } + + if (ep_num <= dwc->max_cfg_eps) + return 0; + + /* Update the max number of eps in the composition */ + dwc->max_cfg_eps = ep_num; + + fifo_size = dwc3_gadget_calc_tx_fifo_size(dwc, dwc->max_cfg_eps); + /* Based on the equation, increment by one for every ep */ + fifo_size += dwc->max_cfg_eps; + + /* Check if we can fit a single fifo per endpoint */ + ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7); + if (fifo_size > ram1_depth) + return -ENOMEM; + + return 0; +} + static void dwc3_gadget_async_callbacks(struct usb_gadget *g, bool enable) { struct dwc3 *dwc = gadget_to_dwc(g); @@ -2616,6 +2847,7 @@ static const struct usb_gadget_ops dwc3_gadget_ops = { .udc_set_ssp_rate = dwc3_gadget_set_ssp_rate, .get_config_params = dwc3_gadget_config_params, .vbus_draw = dwc3_gadget_vbus_draw, + .check_config = dwc3_gadget_check_config, .udc_async_callbacks = dwc3_gadget_async_callbacks, }; @@ -4011,7 +4243,7 @@ int dwc3_gadget_init(struct dwc3 *dwc) } - usb_initialize_gadget(dwc->dev, dwc->gadget, dwc_gadget_release); + usb_initialize_gadget(dwc->sysdev, dwc->gadget, dwc_gadget_release); dev = &dwc->gadget->dev; dev->platform_data = dwc; dwc->gadget->ops = &dwc3_gadget_ops; diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 2d152571a7de..dd58094f0b85 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -450,6 +450,7 @@ config USB_CONFIGFS_F_UVC depends on USB_CONFIGFS depends on VIDEO_V4L2 depends on VIDEO_DEV + select VIDEOBUF2_DMA_SG select VIDEOBUF2_VMALLOC select USB_F_UVC help diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 72a9797dbbae..504c1cbc255d 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -482,7 +482,7 @@ static u8 encode_bMaxPower(enum usb_device_speed speed, { unsigned val; - if (c->MaxPower) + if (c->MaxPower || (c->bmAttributes & USB_CONFIG_ATT_SELFPOWER)) val = c->MaxPower; else val = CONFIG_USB_GADGET_VBUS_DRAW; @@ -936,7 +936,11 @@ static int set_config(struct usb_composite_dev *cdev, } /* when we return, be sure our power usage is valid */ - power = c->MaxPower ? c->MaxPower : CONFIG_USB_GADGET_VBUS_DRAW; + if (c->MaxPower || (c->bmAttributes & USB_CONFIG_ATT_SELFPOWER)) + power = c->MaxPower; + else + power = CONFIG_USB_GADGET_VBUS_DRAW; + if (gadget->speed < USB_SPEED_SUPER) power = min(power, 500U); else diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 15a607ccef8a..477e72a1d11e 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -55,7 +55,7 @@ struct gadget_info { static inline struct gadget_info *to_gadget_info(struct config_item *item) { - return container_of(to_config_group(item), struct gadget_info, group); + return container_of(to_config_group(item), struct gadget_info, group); } struct config_usb_cfg { @@ -365,21 +365,21 @@ static struct configfs_attribute *gadget_root_attrs[] = { static inline struct gadget_strings *to_gadget_strings(struct config_item *item) { - return container_of(to_config_group(item), struct gadget_strings, + return container_of(to_config_group(item), struct gadget_strings, group); } static inline struct gadget_config_name *to_gadget_config_name( struct config_item *item) { - return container_of(to_config_group(item), struct gadget_config_name, + return container_of(to_config_group(item), struct gadget_config_name, group); } static inline struct usb_function_instance *to_usb_function_instance( struct config_item *item) { - return container_of(to_config_group(item), + return container_of(to_config_group(item), struct usb_function_instance, group); } @@ -1404,6 +1404,10 @@ static int configfs_composite_bind(struct usb_gadget *gadget, goto err_purge_funcs; } } + ret = usb_gadget_check_config(cdev->gadget); + if (ret) + goto err_purge_funcs; + usb_ep_autoconfig_reset(cdev->gadget); } if (cdev->use_os_string) { diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 9c0c393abb39..8260f38025b7 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -2081,7 +2081,7 @@ static int __must_check ffs_do_single_desc(char *data, unsigned len, break; case USB_TYPE_CLASS | 0x01: - if (*current_class == USB_INTERFACE_CLASS_HID) { + if (*current_class == USB_INTERFACE_CLASS_HID) { pr_vdebug("hid descriptor\n"); if (length != sizeof(struct hid_descriptor)) goto inv_length; @@ -3831,7 +3831,7 @@ static char *ffs_prepare_buffer(const char __user *buf, size_t len) data = memdup_user(buf, len); if (IS_ERR(data)) - return ERR_PTR(PTR_ERR(data)); + return data; pr_vdebug("Buffer from user space:\n"); ffs_dump_mem("", data, len); diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c index bb476e121eae..ca0a7d9eaa34 100644 --- a/drivers/usb/gadget/function/f_hid.c +++ b/drivers/usb/gadget/function/f_hid.c @@ -45,12 +45,25 @@ struct f_hidg { unsigned short report_desc_length; char *report_desc; unsigned short report_length; + /* + * use_out_ep - if true, the OUT Endpoint (interrupt out method) + * will be used to receive reports from the host + * using functions with the "intout" suffix. + * Otherwise, the OUT Endpoint will not be configured + * and the SETUP/SET_REPORT method ("ssreport" suffix) + * will be used to receive reports. + */ + bool use_out_ep; /* recv report */ - struct list_head completed_out_req; spinlock_t read_spinlock; wait_queue_head_t read_queue; + /* recv report - interrupt out only (use_out_ep == 1) */ + struct list_head completed_out_req; unsigned int qlen; + /* recv report - setup set_report only (use_out_ep == 0) */ + char *set_report_buf; + unsigned int set_report_length; /* send report */ spinlock_t write_spinlock; @@ -79,7 +92,7 @@ static struct usb_interface_descriptor hidg_interface_desc = { .bDescriptorType = USB_DT_INTERFACE, /* .bInterfaceNumber = DYNAMIC */ .bAlternateSetting = 0, - .bNumEndpoints = 2, + /* .bNumEndpoints = DYNAMIC (depends on use_out_ep) */ .bInterfaceClass = USB_CLASS_HID, /* .bInterfaceSubClass = DYNAMIC */ /* .bInterfaceProtocol = DYNAMIC */ @@ -140,7 +153,7 @@ static struct usb_ss_ep_comp_descriptor hidg_ss_out_comp_desc = { /* .wBytesPerInterval = DYNAMIC */ }; -static struct usb_descriptor_header *hidg_ss_descriptors[] = { +static struct usb_descriptor_header *hidg_ss_descriptors_intout[] = { (struct usb_descriptor_header *)&hidg_interface_desc, (struct usb_descriptor_header *)&hidg_desc, (struct usb_descriptor_header *)&hidg_ss_in_ep_desc, @@ -150,6 +163,14 @@ static struct usb_descriptor_header *hidg_ss_descriptors[] = { NULL, }; +static struct usb_descriptor_header *hidg_ss_descriptors_ssreport[] = { + (struct usb_descriptor_header *)&hidg_interface_desc, + (struct usb_descriptor_header *)&hidg_desc, + (struct usb_descriptor_header *)&hidg_ss_in_ep_desc, + (struct usb_descriptor_header *)&hidg_ss_in_comp_desc, + NULL, +}; + /* High-Speed Support */ static struct usb_endpoint_descriptor hidg_hs_in_ep_desc = { @@ -176,7 +197,7 @@ static struct usb_endpoint_descriptor hidg_hs_out_ep_desc = { */ }; -static struct usb_descriptor_header *hidg_hs_descriptors[] = { +static struct usb_descriptor_header *hidg_hs_descriptors_intout[] = { (struct usb_descriptor_header *)&hidg_interface_desc, (struct usb_descriptor_header *)&hidg_desc, (struct usb_descriptor_header *)&hidg_hs_in_ep_desc, @@ -184,6 +205,13 @@ static struct usb_descriptor_header *hidg_hs_descriptors[] = { NULL, }; +static struct usb_descriptor_header *hidg_hs_descriptors_ssreport[] = { + (struct usb_descriptor_header *)&hidg_interface_desc, + (struct usb_descriptor_header *)&hidg_desc, + (struct usb_descriptor_header *)&hidg_hs_in_ep_desc, + NULL, +}; + /* Full-Speed Support */ static struct usb_endpoint_descriptor hidg_fs_in_ep_desc = { @@ -210,7 +238,7 @@ static struct usb_endpoint_descriptor hidg_fs_out_ep_desc = { */ }; -static struct usb_descriptor_header *hidg_fs_descriptors[] = { +static struct usb_descriptor_header *hidg_fs_descriptors_intout[] = { (struct usb_descriptor_header *)&hidg_interface_desc, (struct usb_descriptor_header *)&hidg_desc, (struct usb_descriptor_header *)&hidg_fs_in_ep_desc, @@ -218,6 +246,13 @@ static struct usb_descriptor_header *hidg_fs_descriptors[] = { NULL, }; +static struct usb_descriptor_header *hidg_fs_descriptors_ssreport[] = { + (struct usb_descriptor_header *)&hidg_interface_desc, + (struct usb_descriptor_header *)&hidg_desc, + (struct usb_descriptor_header *)&hidg_fs_in_ep_desc, + NULL, +}; + /*-------------------------------------------------------------------------*/ /* Strings */ @@ -241,8 +276,8 @@ static struct usb_gadget_strings *ct_func_strings[] = { /*-------------------------------------------------------------------------*/ /* Char Device */ -static ssize_t f_hidg_read(struct file *file, char __user *buffer, - size_t count, loff_t *ptr) +static ssize_t f_hidg_intout_read(struct file *file, char __user *buffer, + size_t count, loff_t *ptr) { struct f_hidg *hidg = file->private_data; struct f_hidg_req_list *list; @@ -255,15 +290,15 @@ static ssize_t f_hidg_read(struct file *file, char __user *buffer, spin_lock_irqsave(&hidg->read_spinlock, flags); -#define READ_COND (!list_empty(&hidg->completed_out_req)) +#define READ_COND_INTOUT (!list_empty(&hidg->completed_out_req)) /* wait for at least one buffer to complete */ - while (!READ_COND) { + while (!READ_COND_INTOUT) { spin_unlock_irqrestore(&hidg->read_spinlock, flags); if (file->f_flags & O_NONBLOCK) return -EAGAIN; - if (wait_event_interruptible(hidg->read_queue, READ_COND)) + if (wait_event_interruptible(hidg->read_queue, READ_COND_INTOUT)) return -ERESTARTSYS; spin_lock_irqsave(&hidg->read_spinlock, flags); @@ -313,6 +348,60 @@ static ssize_t f_hidg_read(struct file *file, char __user *buffer, return count; } +#define READ_COND_SSREPORT (hidg->set_report_buf != NULL) + +static ssize_t f_hidg_ssreport_read(struct file *file, char __user *buffer, + size_t count, loff_t *ptr) +{ + struct f_hidg *hidg = file->private_data; + char *tmp_buf = NULL; + unsigned long flags; + + if (!count) + return 0; + + spin_lock_irqsave(&hidg->read_spinlock, flags); + + while (!READ_COND_SSREPORT) { + spin_unlock_irqrestore(&hidg->read_spinlock, flags); + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + if (wait_event_interruptible(hidg->read_queue, READ_COND_SSREPORT)) + return -ERESTARTSYS; + + spin_lock_irqsave(&hidg->read_spinlock, flags); + } + + count = min_t(unsigned int, count, hidg->set_report_length); + tmp_buf = hidg->set_report_buf; + hidg->set_report_buf = NULL; + + spin_unlock_irqrestore(&hidg->read_spinlock, flags); + + if (tmp_buf != NULL) { + count -= copy_to_user(buffer, tmp_buf, count); + kfree(tmp_buf); + } else { + count = -ENOMEM; + } + + wake_up(&hidg->read_queue); + + return count; +} + +static ssize_t f_hidg_read(struct file *file, char __user *buffer, + size_t count, loff_t *ptr) +{ + struct f_hidg *hidg = file->private_data; + + if (hidg->use_out_ep) + return f_hidg_intout_read(file, buffer, count, ptr); + else + return f_hidg_ssreport_read(file, buffer, count, ptr); +} + static void f_hidg_req_complete(struct usb_ep *ep, struct usb_request *req) { struct f_hidg *hidg = (struct f_hidg *)ep->driver_data; @@ -433,14 +522,20 @@ static __poll_t f_hidg_poll(struct file *file, poll_table *wait) if (WRITE_COND) ret |= EPOLLOUT | EPOLLWRNORM; - if (READ_COND) - ret |= EPOLLIN | EPOLLRDNORM; + if (hidg->use_out_ep) { + if (READ_COND_INTOUT) + ret |= EPOLLIN | EPOLLRDNORM; + } else { + if (READ_COND_SSREPORT) + ret |= EPOLLIN | EPOLLRDNORM; + } return ret; } #undef WRITE_COND -#undef READ_COND +#undef READ_COND_SSREPORT +#undef READ_COND_INTOUT static int f_hidg_release(struct inode *inode, struct file *fd) { @@ -467,7 +562,7 @@ static inline struct usb_request *hidg_alloc_ep_req(struct usb_ep *ep, return alloc_ep_req(ep, length); } -static void hidg_set_report_complete(struct usb_ep *ep, struct usb_request *req) +static void hidg_intout_complete(struct usb_ep *ep, struct usb_request *req) { struct f_hidg *hidg = (struct f_hidg *) req->context; struct usb_composite_dev *cdev = hidg->func.config->cdev; @@ -502,6 +597,37 @@ free_req: } } +static void hidg_ssreport_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_hidg *hidg = (struct f_hidg *)req->context; + struct usb_composite_dev *cdev = hidg->func.config->cdev; + char *new_buf = NULL; + unsigned long flags; + + if (req->status != 0 || req->buf == NULL || req->actual == 0) { + ERROR(cdev, + "%s FAILED: status=%d, buf=%p, actual=%d\n", + __func__, req->status, req->buf, req->actual); + return; + } + + spin_lock_irqsave(&hidg->read_spinlock, flags); + + new_buf = krealloc(hidg->set_report_buf, req->actual, GFP_ATOMIC); + if (new_buf == NULL) { + spin_unlock_irqrestore(&hidg->read_spinlock, flags); + return; + } + hidg->set_report_buf = new_buf; + + hidg->set_report_length = req->actual; + memcpy(hidg->set_report_buf, req->buf, req->actual); + + spin_unlock_irqrestore(&hidg->read_spinlock, flags); + + wake_up(&hidg->read_queue); +} + static int hidg_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) { @@ -549,7 +675,11 @@ static int hidg_setup(struct usb_function *f, case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 | HID_REQ_SET_REPORT): VDBG(cdev, "set_report | wLength=%d\n", ctrl->wLength); - goto stall; + if (hidg->use_out_ep) + goto stall; + req->complete = hidg_ssreport_complete; + req->context = hidg; + goto respond; break; case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 @@ -637,15 +767,18 @@ static void hidg_disable(struct usb_function *f) unsigned long flags; usb_ep_disable(hidg->in_ep); - usb_ep_disable(hidg->out_ep); - spin_lock_irqsave(&hidg->read_spinlock, flags); - list_for_each_entry_safe(list, next, &hidg->completed_out_req, list) { - free_ep_req(hidg->out_ep, list->req); - list_del(&list->list); - kfree(list); + if (hidg->out_ep) { + usb_ep_disable(hidg->out_ep); + + spin_lock_irqsave(&hidg->read_spinlock, flags); + list_for_each_entry_safe(list, next, &hidg->completed_out_req, list) { + free_ep_req(hidg->out_ep, list->req); + list_del(&list->list); + kfree(list); + } + spin_unlock_irqrestore(&hidg->read_spinlock, flags); } - spin_unlock_irqrestore(&hidg->read_spinlock, flags); spin_lock_irqsave(&hidg->write_spinlock, flags); if (!hidg->write_pending) { @@ -691,8 +824,7 @@ static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) } } - - if (hidg->out_ep != NULL) { + if (hidg->use_out_ep && hidg->out_ep != NULL) { /* restart endpoint */ usb_ep_disable(hidg->out_ep); @@ -717,7 +849,7 @@ static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) hidg_alloc_ep_req(hidg->out_ep, hidg->report_length); if (req) { - req->complete = hidg_set_report_complete; + req->complete = hidg_intout_complete; req->context = hidg; status = usb_ep_queue(hidg->out_ep, req, GFP_ATOMIC); @@ -743,7 +875,8 @@ static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) } return 0; disable_out_ep: - usb_ep_disable(hidg->out_ep); + if (hidg->out_ep) + usb_ep_disable(hidg->out_ep); free_req_in: if (req_in) free_ep_req(hidg->in_ep, req_in); @@ -795,14 +928,21 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f) goto fail; hidg->in_ep = ep; - ep = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_out_ep_desc); - if (!ep) - goto fail; - hidg->out_ep = ep; + hidg->out_ep = NULL; + if (hidg->use_out_ep) { + ep = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_out_ep_desc); + if (!ep) + goto fail; + hidg->out_ep = ep; + } + + /* used only if use_out_ep == 1 */ + hidg->set_report_buf = NULL; /* set descriptor dynamic values */ hidg_interface_desc.bInterfaceSubClass = hidg->bInterfaceSubClass; hidg_interface_desc.bInterfaceProtocol = hidg->bInterfaceProtocol; + hidg_interface_desc.bNumEndpoints = hidg->use_out_ep ? 2 : 1; hidg->protocol = HID_REPORT_PROTOCOL; hidg->idle = 1; hidg_ss_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); @@ -833,9 +973,19 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f) hidg_ss_out_ep_desc.bEndpointAddress = hidg_fs_out_ep_desc.bEndpointAddress; - status = usb_assign_descriptors(f, hidg_fs_descriptors, - hidg_hs_descriptors, hidg_ss_descriptors, - hidg_ss_descriptors); + if (hidg->use_out_ep) + status = usb_assign_descriptors(f, + hidg_fs_descriptors_intout, + hidg_hs_descriptors_intout, + hidg_ss_descriptors_intout, + hidg_ss_descriptors_intout); + else + status = usb_assign_descriptors(f, + hidg_fs_descriptors_ssreport, + hidg_hs_descriptors_ssreport, + hidg_ss_descriptors_ssreport, + hidg_ss_descriptors_ssreport); + if (status) goto fail; @@ -950,6 +1100,7 @@ CONFIGFS_ATTR(f_hid_opts_, name) F_HID_OPT(subclass, 8, 255); F_HID_OPT(protocol, 8, 255); +F_HID_OPT(no_out_endpoint, 8, 1); F_HID_OPT(report_length, 16, 65535); static ssize_t f_hid_opts_report_desc_show(struct config_item *item, char *page) @@ -1009,6 +1160,7 @@ CONFIGFS_ATTR_RO(f_hid_opts_, dev); static struct configfs_attribute *hid_attrs[] = { &f_hid_opts_attr_subclass, &f_hid_opts_attr_protocol, + &f_hid_opts_attr_no_out_endpoint, &f_hid_opts_attr_report_length, &f_hid_opts_attr_report_desc, &f_hid_opts_attr_dev, @@ -1093,6 +1245,7 @@ static void hidg_free(struct usb_function *f) hidg = func_to_hidg(f); opts = container_of(f->fi, struct f_hid_opts, func_inst); kfree(hidg->report_desc); + kfree(hidg->set_report_buf); kfree(hidg); mutex_lock(&opts->lock); --opts->refcnt; @@ -1139,6 +1292,7 @@ static struct usb_function *hidg_alloc(struct usb_function_instance *fi) return ERR_PTR(-ENOMEM); } } + hidg->use_out_ep = !opts->no_out_endpoint; mutex_unlock(&opts->lock); diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index 4a4703634a2a..6ad669dde41c 100644 --- a/drivers/usb/gadget/function/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -6,36 +6,6 @@ * Copyright (C) 2009 Samsung Electronics * Author: Michal Nazarewicz <mina86@mina86.com> * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The names of the above-listed copyright holders may not be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") as published by the Free Software - * Foundation, either version 2 of that License or (at your option) any - * later version. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index 855127249f24..dc8f078f918c 100644 --- a/drivers/usb/gadget/function/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -72,9 +72,7 @@ struct f_ncm { struct sk_buff *skb_tx_data; struct sk_buff *skb_tx_ndp; u16 ndp_dgram_count; - bool timer_force_tx; struct hrtimer task_timer; - bool timer_stopping; }; static inline struct f_ncm *func_to_ncm(struct usb_function *f) @@ -890,7 +888,6 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) if (ncm->port.in_ep->enabled) { DBG(cdev, "reset ncm\n"); - ncm->timer_stopping = true; ncm->netdev = NULL; gether_disconnect(&ncm->port); ncm_reset_values(ncm); @@ -928,7 +925,6 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) if (IS_ERR(net)) return PTR_ERR(net); ncm->netdev = net; - ncm->timer_stopping = false; } spin_lock(&ncm->lock); @@ -1017,22 +1013,20 @@ static struct sk_buff *ncm_wrap_ntb(struct gether *port, { struct f_ncm *ncm = func_to_ncm(&port->func); struct sk_buff *skb2 = NULL; - int ncb_len = 0; - __le16 *ntb_data; - __le16 *ntb_ndp; - int dgram_pad; - - unsigned max_size = ncm->port.fixed_in_len; - const struct ndp_parser_opts *opts = ncm->parser_opts; - const int ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment); - const int div = le16_to_cpu(ntb_parameters.wNdpInDivisor); - const int rem = le16_to_cpu(ntb_parameters.wNdpInPayloadRemainder); - const int dgram_idx_len = 2 * 2 * opts->dgram_item_len; - - if (!skb && !ncm->skb_tx_data) - return NULL; if (skb) { + int ncb_len = 0; + __le16 *ntb_data; + __le16 *ntb_ndp; + int dgram_pad; + + unsigned max_size = ncm->port.fixed_in_len; + const struct ndp_parser_opts *opts = ncm->parser_opts; + const int ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment); + const int div = le16_to_cpu(ntb_parameters.wNdpInDivisor); + const int rem = le16_to_cpu(ntb_parameters.wNdpInPayloadRemainder); + const int dgram_idx_len = 2 * 2 * opts->dgram_item_len; + /* Add the CRC if required up front */ if (ncm->is_crc) { uint32_t crc; @@ -1126,8 +1120,11 @@ static struct sk_buff *ncm_wrap_ntb(struct gether *port, dev_consume_skb_any(skb); skb = NULL; - } else if (ncm->skb_tx_data && ncm->timer_force_tx) { - /* If the tx was requested because of a timeout then send */ + } else if (ncm->skb_tx_data) { + /* If we get here ncm_wrap_ntb() was called with NULL skb, + * because eth_start_xmit() was called with NULL skb by + * ncm_tx_timeout() - hence, this is our signal to flush/send. + */ skb2 = package_for_tx(ncm); if (!skb2) goto err; @@ -1155,20 +1152,18 @@ err: static enum hrtimer_restart ncm_tx_timeout(struct hrtimer *data) { struct f_ncm *ncm = container_of(data, struct f_ncm, task_timer); + struct net_device *netdev = READ_ONCE(ncm->netdev); - /* Only send if data is available. */ - if (!ncm->timer_stopping && ncm->skb_tx_data) { - ncm->timer_force_tx = true; - + if (netdev) { /* XXX This allowance of a NULL skb argument to ndo_start_xmit * XXX is not sane. The gadget layer should be redesigned so * XXX that the dev->wrap() invocations to build SKBs is transparent * XXX and performed in some way outside of the ndo_start_xmit * XXX interface. + * + * This will call directly into u_ether's eth_start_xmit() */ - ncm->netdev->netdev_ops->ndo_start_xmit(NULL, ncm->netdev); - - ncm->timer_force_tx = false; + netdev->netdev_ops->ndo_start_xmit(NULL, netdev); } return HRTIMER_NORESTART; } @@ -1357,7 +1352,6 @@ static void ncm_disable(struct usb_function *f) DBG(cdev, "ncm deactivated\n"); if (ncm->port.in_ep->enabled) { - ncm->timer_stopping = true; ncm->netdev = NULL; gether_disconnect(&ncm->port); } diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index d04707580068..5b3502df4e13 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -22,13 +22,26 @@ /* UAC1 spec: 3.7.2.3 Audio Channel Cluster Format */ #define UAC1_CHANNEL_MASK 0x0FFF +#define USB_OUT_FU_ID (out_feature_unit_desc->bUnitID) +#define USB_IN_FU_ID (in_feature_unit_desc->bUnitID) + #define EPIN_EN(_opts) ((_opts)->p_chmask != 0) #define EPOUT_EN(_opts) ((_opts)->c_chmask != 0) +#define FUIN_EN(_opts) ((_opts)->p_mute_present \ + || (_opts)->p_volume_present) +#define FUOUT_EN(_opts) ((_opts)->c_mute_present \ + || (_opts)->c_volume_present) struct f_uac1 { struct g_audio g_audio; u8 ac_intf, as_in_intf, as_out_intf; u8 ac_alt, as_in_alt, as_out_alt; /* needed for get_alt() */ + + struct usb_ctrlrequest setup_cr; /* will be used in data stage */ + + /* Interrupt IN endpoint of AC interface */ + struct usb_ep *int_ep; + atomic_t int_count; }; static inline struct f_uac1 *func_to_uac1(struct usb_function *f) @@ -58,7 +71,7 @@ static inline struct f_uac1_opts *g_audio_to_uac1_opts(struct g_audio *audio) static struct usb_interface_descriptor ac_interface_desc = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, - .bNumEndpoints = 0, + /* .bNumEndpoints = DYNAMIC */ .bInterfaceClass = USB_CLASS_AUDIO, .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, }; @@ -106,6 +119,19 @@ static struct uac1_output_terminal_descriptor usb_in_ot_desc = { /* .bSourceID = DYNAMIC */ }; +static struct uac_feature_unit_descriptor *in_feature_unit_desc; +static struct uac_feature_unit_descriptor *out_feature_unit_desc; + +/* AC IN Interrupt Endpoint */ +static struct usb_endpoint_descriptor ac_int_ep_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(2), + .bInterval = 4, +}; + /* B.4.1 Standard AS Interface Descriptor */ static struct usb_interface_descriptor as_out_interface_alt_0_desc = { .bLength = USB_DT_INTERFACE_SIZE, @@ -232,8 +258,13 @@ static struct usb_descriptor_header *f_audio_desc[] = { (struct usb_descriptor_header *)&usb_out_it_desc, (struct usb_descriptor_header *)&io_out_ot_desc, + (struct usb_descriptor_header *)&out_feature_unit_desc, + (struct usb_descriptor_header *)&io_in_it_desc, (struct usb_descriptor_header *)&usb_in_ot_desc, + (struct usb_descriptor_header *)&in_feature_unit_desc, + + (struct usb_descriptor_header *)&ac_int_ep_desc, (struct usb_descriptor_header *)&as_out_interface_alt_0_desc, (struct usb_descriptor_header *)&as_out_interface_alt_1_desc, @@ -263,6 +294,8 @@ enum { STR_IO_IN_IT, STR_IO_IN_IT_CH_NAMES, STR_USB_IN_OT, + STR_FU_IN, + STR_FU_OUT, STR_AS_OUT_IF_ALT0, STR_AS_OUT_IF_ALT1, STR_AS_IN_IF_ALT0, @@ -277,6 +310,8 @@ static struct usb_string strings_uac1[] = { [STR_IO_IN_IT].s = "Capture Input terminal", [STR_IO_IN_IT_CH_NAMES].s = "Capture Channels", [STR_USB_IN_OT].s = "Capture Output terminal", + [STR_FU_IN].s = "Capture Volume", + [STR_FU_OUT].s = "Playback Volume", [STR_AS_OUT_IF_ALT0].s = "Playback Inactive", [STR_AS_OUT_IF_ALT1].s = "Playback Active", [STR_AS_IN_IF_ALT0].s = "Capture Inactive", @@ -298,6 +333,376 @@ static struct usb_gadget_strings *uac1_strings[] = { * This function is an ALSA sound card following USB Audio Class Spec 1.0. */ +static void audio_notify_complete(struct usb_ep *_ep, struct usb_request *req) +{ + struct g_audio *audio = req->context; + struct f_uac1 *uac1 = func_to_uac1(&audio->func); + + atomic_dec(&uac1->int_count); + kfree(req->buf); + usb_ep_free_request(_ep, req); +} + +static int audio_notify(struct g_audio *audio, int unit_id, int cs) +{ + struct f_uac1 *uac1 = func_to_uac1(&audio->func); + struct usb_request *req; + struct uac1_status_word *msg; + int ret; + + if (!uac1->int_ep->enabled) + return 0; + + if (atomic_inc_return(&uac1->int_count) > UAC1_DEF_INT_REQ_NUM) { + atomic_dec(&uac1->int_count); + return 0; + } + + req = usb_ep_alloc_request(uac1->int_ep, GFP_ATOMIC); + if (req == NULL) { + ret = -ENOMEM; + goto err_dec_int_count; + } + + msg = kmalloc(sizeof(*msg), GFP_ATOMIC); + if (msg == NULL) { + ret = -ENOMEM; + goto err_free_request; + } + + msg->bStatusType = UAC1_STATUS_TYPE_IRQ_PENDING + | UAC1_STATUS_TYPE_ORIG_AUDIO_CONTROL_IF; + msg->bOriginator = unit_id; + + req->length = sizeof(*msg); + req->buf = msg; + req->context = audio; + req->complete = audio_notify_complete; + + ret = usb_ep_queue(uac1->int_ep, req, GFP_ATOMIC); + + if (ret) + goto err_free_msg; + + return 0; + +err_free_msg: + kfree(msg); +err_free_request: + usb_ep_free_request(uac1->int_ep, req); +err_dec_int_count: + atomic_dec(&uac1->int_count); + + return ret; +} + +static int +in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr) +{ + struct usb_request *req = fn->config->cdev->req; + struct g_audio *audio = func_to_g_audio(fn); + struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio); + u16 w_length = le16_to_cpu(cr->wLength); + u16 w_index = le16_to_cpu(cr->wIndex); + u16 w_value = le16_to_cpu(cr->wValue); + u8 entity_id = (w_index >> 8) & 0xff; + u8 control_selector = w_value >> 8; + int value = -EOPNOTSUPP; + + if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) || + (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) { + unsigned int is_playback = 0; + + if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) + is_playback = 1; + + if (control_selector == UAC_FU_MUTE) { + unsigned int mute; + + u_audio_get_mute(audio, is_playback, &mute); + + *(u8 *)req->buf = mute; + value = min_t(unsigned int, w_length, 1); + } else if (control_selector == UAC_FU_VOLUME) { + __le16 c; + s16 volume; + + u_audio_get_volume(audio, is_playback, &volume); + + c = cpu_to_le16(volume); + + value = min_t(unsigned int, w_length, sizeof(c)); + memcpy(req->buf, &c, value); + } else { + dev_err(&audio->gadget->dev, + "%s:%d control_selector=%d TODO!\n", + __func__, __LINE__, control_selector); + } + } else { + dev_err(&audio->gadget->dev, + "%s:%d entity_id=%d control_selector=%d TODO!\n", + __func__, __LINE__, entity_id, control_selector); + } + + return value; +} + +static int +in_rq_min(struct usb_function *fn, const struct usb_ctrlrequest *cr) +{ + struct usb_request *req = fn->config->cdev->req; + struct g_audio *audio = func_to_g_audio(fn); + struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio); + u16 w_length = le16_to_cpu(cr->wLength); + u16 w_index = le16_to_cpu(cr->wIndex); + u16 w_value = le16_to_cpu(cr->wValue); + u8 entity_id = (w_index >> 8) & 0xff; + u8 control_selector = w_value >> 8; + int value = -EOPNOTSUPP; + + if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) || + (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) { + unsigned int is_playback = 0; + + if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) + is_playback = 1; + + if (control_selector == UAC_FU_VOLUME) { + __le16 r; + s16 min_db; + + if (is_playback) + min_db = opts->p_volume_min; + else + min_db = opts->c_volume_min; + + r = cpu_to_le16(min_db); + + value = min_t(unsigned int, w_length, sizeof(r)); + memcpy(req->buf, &r, value); + } else { + dev_err(&audio->gadget->dev, + "%s:%d control_selector=%d TODO!\n", + __func__, __LINE__, control_selector); + } + } else { + dev_err(&audio->gadget->dev, + "%s:%d entity_id=%d control_selector=%d TODO!\n", + __func__, __LINE__, entity_id, control_selector); + } + + return value; +} + +static int +in_rq_max(struct usb_function *fn, const struct usb_ctrlrequest *cr) +{ + struct usb_request *req = fn->config->cdev->req; + struct g_audio *audio = func_to_g_audio(fn); + struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio); + u16 w_length = le16_to_cpu(cr->wLength); + u16 w_index = le16_to_cpu(cr->wIndex); + u16 w_value = le16_to_cpu(cr->wValue); + u8 entity_id = (w_index >> 8) & 0xff; + u8 control_selector = w_value >> 8; + int value = -EOPNOTSUPP; + + if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) || + (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) { + unsigned int is_playback = 0; + + if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) + is_playback = 1; + + if (control_selector == UAC_FU_VOLUME) { + __le16 r; + s16 max_db; + + if (is_playback) + max_db = opts->p_volume_max; + else + max_db = opts->c_volume_max; + + r = cpu_to_le16(max_db); + + value = min_t(unsigned int, w_length, sizeof(r)); + memcpy(req->buf, &r, value); + } else { + dev_err(&audio->gadget->dev, + "%s:%d control_selector=%d TODO!\n", + __func__, __LINE__, control_selector); + } + } else { + dev_err(&audio->gadget->dev, + "%s:%d entity_id=%d control_selector=%d TODO!\n", + __func__, __LINE__, entity_id, control_selector); + } + + return value; +} + +static int +in_rq_res(struct usb_function *fn, const struct usb_ctrlrequest *cr) +{ + struct usb_request *req = fn->config->cdev->req; + struct g_audio *audio = func_to_g_audio(fn); + struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio); + u16 w_length = le16_to_cpu(cr->wLength); + u16 w_index = le16_to_cpu(cr->wIndex); + u16 w_value = le16_to_cpu(cr->wValue); + u8 entity_id = (w_index >> 8) & 0xff; + u8 control_selector = w_value >> 8; + int value = -EOPNOTSUPP; + + if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) || + (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) { + unsigned int is_playback = 0; + + if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) + is_playback = 1; + + if (control_selector == UAC_FU_VOLUME) { + __le16 r; + s16 res_db; + + if (is_playback) + res_db = opts->p_volume_res; + else + res_db = opts->c_volume_res; + + r = cpu_to_le16(res_db); + + value = min_t(unsigned int, w_length, sizeof(r)); + memcpy(req->buf, &r, value); + } else { + dev_err(&audio->gadget->dev, + "%s:%d control_selector=%d TODO!\n", + __func__, __LINE__, control_selector); + } + } else { + dev_err(&audio->gadget->dev, + "%s:%d entity_id=%d control_selector=%d TODO!\n", + __func__, __LINE__, entity_id, control_selector); + } + + return value; +} + +static void +out_rq_cur_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct g_audio *audio = req->context; + struct usb_composite_dev *cdev = audio->func.config->cdev; + struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio); + struct f_uac1 *uac1 = func_to_uac1(&audio->func); + struct usb_ctrlrequest *cr = &uac1->setup_cr; + u16 w_index = le16_to_cpu(cr->wIndex); + u16 w_value = le16_to_cpu(cr->wValue); + u8 entity_id = (w_index >> 8) & 0xff; + u8 control_selector = w_value >> 8; + + if (req->status != 0) { + dev_dbg(&cdev->gadget->dev, "completion err %d\n", req->status); + return; + } + + if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) || + (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) { + unsigned int is_playback = 0; + + if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) + is_playback = 1; + + if (control_selector == UAC_FU_MUTE) { + u8 mute = *(u8 *)req->buf; + + u_audio_set_mute(audio, is_playback, mute); + + return; + } else if (control_selector == UAC_FU_VOLUME) { + __le16 *c = req->buf; + s16 volume; + + volume = le16_to_cpu(*c); + u_audio_set_volume(audio, is_playback, volume); + + return; + } else { + dev_err(&audio->gadget->dev, + "%s:%d control_selector=%d TODO!\n", + __func__, __LINE__, control_selector); + usb_ep_set_halt(ep); + } + } else { + dev_err(&audio->gadget->dev, + "%s:%d entity_id=%d control_selector=%d TODO!\n", + __func__, __LINE__, entity_id, control_selector); + usb_ep_set_halt(ep); + + } +} + +static int +out_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr) +{ + struct usb_request *req = fn->config->cdev->req; + struct g_audio *audio = func_to_g_audio(fn); + struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio); + struct f_uac1 *uac1 = func_to_uac1(&audio->func); + u16 w_length = le16_to_cpu(cr->wLength); + u16 w_index = le16_to_cpu(cr->wIndex); + u16 w_value = le16_to_cpu(cr->wValue); + u8 entity_id = (w_index >> 8) & 0xff; + u8 control_selector = w_value >> 8; + + if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) || + (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) { + memcpy(&uac1->setup_cr, cr, sizeof(*cr)); + req->context = audio; + req->complete = out_rq_cur_complete; + + return w_length; + } else { + dev_err(&audio->gadget->dev, + "%s:%d entity_id=%d control_selector=%d TODO!\n", + __func__, __LINE__, entity_id, control_selector); + } + return -EOPNOTSUPP; +} + +static int ac_rq_in(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct usb_composite_dev *cdev = f->config->cdev; + int value = -EOPNOTSUPP; + u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF); + u16 len = le16_to_cpu(ctrl->wLength); + u16 w_value = le16_to_cpu(ctrl->wValue); + + DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", + ctrl->bRequest, w_value, len, ep); + + switch (ctrl->bRequest) { + case UAC_GET_CUR: + return in_rq_cur(f, ctrl); + case UAC_GET_MIN: + return in_rq_min(f, ctrl); + case UAC_GET_MAX: + return in_rq_max(f, ctrl); + case UAC_GET_RES: + return in_rq_res(f, ctrl); + case UAC_GET_MEM: + break; + case UAC_GET_STAT: + value = len; + break; + default: + break; + } + + return value; +} + static int audio_set_endpoint_req(struct usb_function *f, const struct usb_ctrlrequest *ctrl) { @@ -383,7 +788,13 @@ f_audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: value = audio_get_endpoint_req(f, ctrl); break; - + case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE: + if (ctrl->bRequest == UAC_SET_CUR) + value = out_rq_cur(f, ctrl); + break; + case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE: + value = ac_rq_in(f, ctrl); + break; default: ERROR(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n", ctrl->bRequestType, ctrl->bRequest, @@ -411,6 +822,7 @@ static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt) struct usb_composite_dev *cdev = f->config->cdev; struct usb_gadget *gadget = cdev->gadget; struct device *dev = &gadget->dev; + struct g_audio *audio = func_to_g_audio(f); struct f_uac1 *uac1 = func_to_uac1(f); int ret = 0; @@ -426,6 +838,14 @@ static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt) dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); return -EINVAL; } + + /* restart interrupt endpoint */ + if (uac1->int_ep) { + usb_ep_disable(uac1->int_ep); + config_ep_by_speed(gadget, &audio->func, uac1->int_ep); + usb_ep_enable(uac1->int_ep); + } + return 0; } @@ -481,10 +901,33 @@ static void f_audio_disable(struct usb_function *f) u_audio_stop_playback(&uac1->g_audio); u_audio_stop_capture(&uac1->g_audio); + if (uac1->int_ep) + usb_ep_disable(uac1->int_ep); } /*-------------------------------------------------------------------------*/ +static struct uac_feature_unit_descriptor *build_fu_desc(int chmask) +{ + struct uac_feature_unit_descriptor *fu_desc; + int channels = num_channels(chmask); + int fu_desc_size = UAC_DT_FEATURE_UNIT_SIZE(channels); + + fu_desc = kzalloc(fu_desc_size, GFP_KERNEL); + if (!fu_desc) + return NULL; + + fu_desc->bLength = fu_desc_size; + fu_desc->bDescriptorType = USB_DT_CS_INTERFACE; + + fu_desc->bDescriptorSubtype = UAC_FEATURE_UNIT; + fu_desc->bControlSize = 2; + /* bUnitID, bSourceID and bmaControls will be defined later */ + + return fu_desc; +} + +/* B.3.2 Class-Specific AC Interface Descriptor */ static struct uac1_ac_header_descriptor *build_ac_header_desc(struct f_uac1_opts *opts) { @@ -530,9 +973,23 @@ static void setup_descriptor(struct f_uac1_opts *opts) io_out_ot_desc.bTerminalID = i++; if (EPIN_EN(opts)) usb_in_ot_desc.bTerminalID = i++; - - usb_in_ot_desc.bSourceID = io_in_it_desc.bTerminalID; - io_out_ot_desc.bSourceID = usb_out_it_desc.bTerminalID; + if (FUOUT_EN(opts)) + out_feature_unit_desc->bUnitID = i++; + if (FUIN_EN(opts)) + in_feature_unit_desc->bUnitID = i++; + + if (FUIN_EN(opts)) { + usb_in_ot_desc.bSourceID = in_feature_unit_desc->bUnitID; + in_feature_unit_desc->bSourceID = io_in_it_desc.bTerminalID; + } else { + usb_in_ot_desc.bSourceID = io_in_it_desc.bTerminalID; + } + if (FUOUT_EN(opts)) { + io_out_ot_desc.bSourceID = out_feature_unit_desc->bUnitID; + out_feature_unit_desc->bSourceID = usb_out_it_desc.bTerminalID; + } else { + io_out_ot_desc.bSourceID = usb_out_it_desc.bTerminalID; + } as_out_header_desc.bTerminalLink = usb_out_it_desc.bTerminalID; as_in_header_desc.bTerminalLink = usb_in_ot_desc.bTerminalID; @@ -544,6 +1001,8 @@ static void setup_descriptor(struct f_uac1_opts *opts) len += sizeof(usb_in_ot_desc); len += sizeof(io_in_it_desc); + if (FUIN_EN(opts)) + len += in_feature_unit_desc->bLength; ac_header_desc->wTotalLength = cpu_to_le16(len); } if (EPOUT_EN(opts)) { @@ -551,6 +1010,8 @@ static void setup_descriptor(struct f_uac1_opts *opts) len += sizeof(usb_out_it_desc); len += sizeof(io_out_ot_desc); + if (FUOUT_EN(opts)) + len += out_feature_unit_desc->bLength; ac_header_desc->wTotalLength = cpu_to_le16(len); } @@ -561,13 +1022,20 @@ static void setup_descriptor(struct f_uac1_opts *opts) if (EPOUT_EN(opts)) { f_audio_desc[i++] = USBDHDR(&usb_out_it_desc); f_audio_desc[i++] = USBDHDR(&io_out_ot_desc); + if (FUOUT_EN(opts)) + f_audio_desc[i++] = USBDHDR(out_feature_unit_desc); } if (EPIN_EN(opts)) { f_audio_desc[i++] = USBDHDR(&io_in_it_desc); f_audio_desc[i++] = USBDHDR(&usb_in_ot_desc); + if (FUIN_EN(opts)) + f_audio_desc[i++] = USBDHDR(in_feature_unit_desc); } + if (FUOUT_EN(opts) || FUIN_EN(opts)) + f_audio_desc[i++] = USBDHDR(&ac_int_ep_desc); + if (EPOUT_EN(opts)) { f_audio_desc[i++] = USBDHDR(&as_out_interface_alt_0_desc); f_audio_desc[i++] = USBDHDR(&as_out_interface_alt_1_desc); @@ -614,6 +1082,28 @@ static int f_audio_validate_opts(struct g_audio *audio, struct device *dev) return -EINVAL; } + if (opts->p_volume_max <= opts->p_volume_min) { + dev_err(dev, "Error: incorrect playback volume max/min\n"); + return -EINVAL; + } else if (opts->c_volume_max <= opts->c_volume_min) { + dev_err(dev, "Error: incorrect capture volume max/min\n"); + return -EINVAL; + } else if (opts->p_volume_res <= 0) { + dev_err(dev, "Error: negative/zero playback volume resolution\n"); + return -EINVAL; + } else if (opts->c_volume_res <= 0) { + dev_err(dev, "Error: negative/zero capture volume resolution\n"); + return -EINVAL; + } + + if ((opts->p_volume_max - opts->p_volume_min) % opts->p_volume_res) { + dev_err(dev, "Error: incorrect playback volume resolution\n"); + return -EINVAL; + } else if ((opts->c_volume_max - opts->c_volume_min) % opts->c_volume_res) { + dev_err(dev, "Error: incorrect capture volume resolution\n"); + return -EINVAL; + } + return 0; } @@ -647,6 +1137,21 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) if (!ac_header_desc) return -ENOMEM; + if (FUOUT_EN(audio_opts)) { + out_feature_unit_desc = build_fu_desc(audio_opts->c_chmask); + if (!out_feature_unit_desc) { + status = -ENOMEM; + goto fail; + } + } + if (FUIN_EN(audio_opts)) { + in_feature_unit_desc = build_fu_desc(audio_opts->p_chmask); + if (!in_feature_unit_desc) { + status = -ENOMEM; + goto err_free_fu; + } + } + ac_interface_desc.iInterface = us[STR_AC_IF].id; usb_out_it_desc.iTerminal = us[STR_USB_OUT_IT].id; usb_out_it_desc.iChannelNames = us[STR_USB_OUT_IT_CH_NAMES].id; @@ -659,6 +1164,21 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) as_in_interface_alt_0_desc.iInterface = us[STR_AS_IN_IF_ALT0].id; as_in_interface_alt_1_desc.iInterface = us[STR_AS_IN_IF_ALT1].id; + if (FUOUT_EN(audio_opts)) { + u8 *i_feature; + + i_feature = (u8 *)out_feature_unit_desc + + out_feature_unit_desc->bLength - 1; + *i_feature = us[STR_FU_OUT].id; + } + if (FUIN_EN(audio_opts)) { + u8 *i_feature; + + i_feature = (u8 *)in_feature_unit_desc + + in_feature_unit_desc->bLength - 1; + *i_feature = us[STR_FU_IN].id; + } + /* Set channel numbers */ usb_out_it_desc.bNrChannels = num_channels(audio_opts->c_chmask); usb_out_it_desc.wChannelConfig = cpu_to_le16(audio_opts->c_chmask); @@ -671,6 +1191,27 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) as_in_type_i_desc.bSubframeSize = audio_opts->p_ssize; as_in_type_i_desc.bBitResolution = audio_opts->p_ssize * 8; + if (FUOUT_EN(audio_opts)) { + __le16 *bma = (__le16 *)&out_feature_unit_desc->bmaControls[0]; + u32 control = 0; + + if (audio_opts->c_mute_present) + control |= UAC_FU_MUTE; + if (audio_opts->c_volume_present) + control |= UAC_FU_VOLUME; + *bma = cpu_to_le16(control); + } + if (FUIN_EN(audio_opts)) { + __le16 *bma = (__le16 *)&in_feature_unit_desc->bmaControls[0]; + u32 control = 0; + + if (audio_opts->p_mute_present) + control |= UAC_FU_MUTE; + if (audio_opts->p_volume_present) + control |= UAC_FU_VOLUME; + *bma = cpu_to_le16(control); + } + /* Set sample rates */ rate = audio_opts->c_srate; sam_freq = as_out_type_i_desc.tSamFreq[0]; @@ -682,7 +1223,7 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) /* allocate instance-specific interface IDs, and patch descriptors */ status = usb_interface_id(c, f); if (status < 0) - goto fail; + goto err_free_fu; ac_interface_desc.bInterfaceNumber = status; uac1->ac_intf = status; uac1->ac_alt = 0; @@ -692,7 +1233,7 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) if (EPOUT_EN(audio_opts)) { status = usb_interface_id(c, f); if (status < 0) - goto fail; + goto err_free_fu; as_out_interface_alt_0_desc.bInterfaceNumber = status; as_out_interface_alt_1_desc.bInterfaceNumber = status; ac_header_desc->baInterfaceNr[ba_iface_id++] = status; @@ -703,7 +1244,7 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) if (EPIN_EN(audio_opts)) { status = usb_interface_id(c, f); if (status < 0) - goto fail; + goto err_free_fu; as_in_interface_alt_0_desc.bInterfaceNumber = status; as_in_interface_alt_1_desc.bInterfaceNumber = status; ac_header_desc->baInterfaceNr[ba_iface_id++] = status; @@ -715,11 +1256,24 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) status = -ENODEV; + ac_interface_desc.bNumEndpoints = 0; + + /* allocate AC interrupt endpoint */ + if (FUOUT_EN(audio_opts) || FUIN_EN(audio_opts)) { + ep = usb_ep_autoconfig(cdev->gadget, &ac_int_ep_desc); + if (!ep) + goto err_free_fu; + uac1->int_ep = ep; + uac1->int_ep->desc = &ac_int_ep_desc; + + ac_interface_desc.bNumEndpoints = 1; + } + /* allocate instance-specific endpoints */ if (EPOUT_EN(audio_opts)) { ep = usb_ep_autoconfig(cdev->gadget, &as_out_ep_desc); if (!ep) - goto fail; + goto err_free_fu; audio->out_ep = ep; audio->out_ep->desc = &as_out_ep_desc; } @@ -727,7 +1281,7 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) if (EPIN_EN(audio_opts)) { ep = usb_ep_autoconfig(cdev->gadget, &as_in_ep_desc); if (!ep) - goto fail; + goto err_free_fu; audio->in_ep = ep; audio->in_ep->desc = &as_in_ep_desc; } @@ -738,17 +1292,37 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) status = usb_assign_descriptors(f, f_audio_desc, f_audio_desc, NULL, NULL); if (status) - goto fail; + goto err_free_fu; audio->out_ep_maxpsize = le16_to_cpu(as_out_ep_desc.wMaxPacketSize); audio->in_ep_maxpsize = le16_to_cpu(as_in_ep_desc.wMaxPacketSize); audio->params.c_chmask = audio_opts->c_chmask; audio->params.c_srate = audio_opts->c_srate; audio->params.c_ssize = audio_opts->c_ssize; + if (FUIN_EN(audio_opts)) { + audio->params.p_fu.id = USB_IN_FU_ID; + audio->params.p_fu.mute_present = audio_opts->p_mute_present; + audio->params.p_fu.volume_present = + audio_opts->p_volume_present; + audio->params.p_fu.volume_min = audio_opts->p_volume_min; + audio->params.p_fu.volume_max = audio_opts->p_volume_max; + audio->params.p_fu.volume_res = audio_opts->p_volume_res; + } audio->params.p_chmask = audio_opts->p_chmask; audio->params.p_srate = audio_opts->p_srate; audio->params.p_ssize = audio_opts->p_ssize; + if (FUOUT_EN(audio_opts)) { + audio->params.c_fu.id = USB_OUT_FU_ID; + audio->params.c_fu.mute_present = audio_opts->c_mute_present; + audio->params.c_fu.volume_present = + audio_opts->c_volume_present; + audio->params.c_fu.volume_min = audio_opts->c_volume_min; + audio->params.c_fu.volume_max = audio_opts->c_volume_max; + audio->params.c_fu.volume_res = audio_opts->c_volume_res; + } audio->params.req_number = audio_opts->req_number; + if (FUOUT_EN(audio_opts) || FUIN_EN(audio_opts)) + audio->notify = audio_notify; status = g_audio_setup(audio, "UAC1_PCM", "UAC1_Gadget"); if (status) @@ -758,6 +1332,11 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) err_card_register: usb_free_all_descriptors(f); +err_free_fu: + kfree(out_feature_unit_desc); + out_feature_unit_desc = NULL; + kfree(in_feature_unit_desc); + in_feature_unit_desc = NULL; fail: kfree(ac_header_desc); ac_header_desc = NULL; @@ -783,7 +1362,15 @@ static struct configfs_item_operations f_uac1_item_ops = { .release = f_uac1_attr_release, }; -#define UAC1_ATTRIBUTE(name) \ +#define uac1_kstrtou32 kstrtou32 +#define uac1_kstrtos16 kstrtos16 +#define uac1_kstrtobool(s, base, res) kstrtobool((s), (res)) + +static const char *u32_fmt = "%u\n"; +static const char *s16_fmt = "%hd\n"; +static const char *bool_fmt = "%u\n"; + +#define UAC1_ATTRIBUTE(type, name) \ static ssize_t f_uac1_opts_##name##_show( \ struct config_item *item, \ char *page) \ @@ -792,7 +1379,7 @@ static ssize_t f_uac1_opts_##name##_show( \ int result; \ \ mutex_lock(&opts->lock); \ - result = sprintf(page, "%u\n", opts->name); \ + result = sprintf(page, type##_fmt, opts->name); \ mutex_unlock(&opts->lock); \ \ return result; \ @@ -804,7 +1391,7 @@ static ssize_t f_uac1_opts_##name##_store( \ { \ struct f_uac1_opts *opts = to_f_uac1_opts(item); \ int ret; \ - u32 num; \ + type num; \ \ mutex_lock(&opts->lock); \ if (opts->refcnt) { \ @@ -812,7 +1399,7 @@ static ssize_t f_uac1_opts_##name##_store( \ goto end; \ } \ \ - ret = kstrtou32(page, 0, &num); \ + ret = uac1_kstrto##type(page, 0, &num); \ if (ret) \ goto end; \ \ @@ -826,13 +1413,25 @@ end: \ \ CONFIGFS_ATTR(f_uac1_opts_, name) -UAC1_ATTRIBUTE(c_chmask); -UAC1_ATTRIBUTE(c_srate); -UAC1_ATTRIBUTE(c_ssize); -UAC1_ATTRIBUTE(p_chmask); -UAC1_ATTRIBUTE(p_srate); -UAC1_ATTRIBUTE(p_ssize); -UAC1_ATTRIBUTE(req_number); +UAC1_ATTRIBUTE(u32, c_chmask); +UAC1_ATTRIBUTE(u32, c_srate); +UAC1_ATTRIBUTE(u32, c_ssize); +UAC1_ATTRIBUTE(u32, p_chmask); +UAC1_ATTRIBUTE(u32, p_srate); +UAC1_ATTRIBUTE(u32, p_ssize); +UAC1_ATTRIBUTE(u32, req_number); + +UAC1_ATTRIBUTE(bool, p_mute_present); +UAC1_ATTRIBUTE(bool, p_volume_present); +UAC1_ATTRIBUTE(s16, p_volume_min); +UAC1_ATTRIBUTE(s16, p_volume_max); +UAC1_ATTRIBUTE(s16, p_volume_res); + +UAC1_ATTRIBUTE(bool, c_mute_present); +UAC1_ATTRIBUTE(bool, c_volume_present); +UAC1_ATTRIBUTE(s16, c_volume_min); +UAC1_ATTRIBUTE(s16, c_volume_max); +UAC1_ATTRIBUTE(s16, c_volume_res); static struct configfs_attribute *f_uac1_attrs[] = { &f_uac1_opts_attr_c_chmask, @@ -842,6 +1441,19 @@ static struct configfs_attribute *f_uac1_attrs[] = { &f_uac1_opts_attr_p_srate, &f_uac1_opts_attr_p_ssize, &f_uac1_opts_attr_req_number, + + &f_uac1_opts_attr_p_mute_present, + &f_uac1_opts_attr_p_volume_present, + &f_uac1_opts_attr_p_volume_min, + &f_uac1_opts_attr_p_volume_max, + &f_uac1_opts_attr_p_volume_res, + + &f_uac1_opts_attr_c_mute_present, + &f_uac1_opts_attr_c_volume_present, + &f_uac1_opts_attr_c_volume_min, + &f_uac1_opts_attr_c_volume_max, + &f_uac1_opts_attr_c_volume_res, + NULL, }; @@ -879,6 +1491,19 @@ static struct usb_function_instance *f_audio_alloc_inst(void) opts->p_chmask = UAC1_DEF_PCHMASK; opts->p_srate = UAC1_DEF_PSRATE; opts->p_ssize = UAC1_DEF_PSSIZE; + + opts->p_mute_present = UAC1_DEF_MUTE_PRESENT; + opts->p_volume_present = UAC1_DEF_VOLUME_PRESENT; + opts->p_volume_min = UAC1_DEF_MIN_DB; + opts->p_volume_max = UAC1_DEF_MAX_DB; + opts->p_volume_res = UAC1_DEF_RES_DB; + + opts->c_mute_present = UAC1_DEF_MUTE_PRESENT; + opts->c_volume_present = UAC1_DEF_VOLUME_PRESENT; + opts->c_volume_min = UAC1_DEF_MIN_DB; + opts->c_volume_max = UAC1_DEF_MAX_DB; + opts->c_volume_res = UAC1_DEF_RES_DB; + opts->req_number = UAC1_DEF_REQ_NUM; return &opts->func_inst; } @@ -903,6 +1528,11 @@ static void f_audio_unbind(struct usb_configuration *c, struct usb_function *f) g_audio_cleanup(audio); usb_free_all_descriptors(f); + kfree(out_feature_unit_desc); + out_feature_unit_desc = NULL; + kfree(in_feature_unit_desc); + in_feature_unit_desc = NULL; + kfree(ac_header_desc); ac_header_desc = NULL; diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index ae29ff2b2b68..3c34995276e7 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -5,6 +5,9 @@ * Copyright (C) 2011 * Yadwinder Singh (yadi.brar01@gmail.com) * Jaswinder Singh (jaswinder.singh@linaro.org) + * + * Copyright (C) 2020 + * Ruslan Bilovol (ruslan.bilovol@gmail.com) */ #include <linux/usb/audio.h> @@ -19,14 +22,16 @@ /* * The driver implements a simple UAC_2 topology. - * USB-OUT -> IT_1 -> OT_3 -> ALSA_Capture - * ALSA_Playback -> IT_2 -> OT_4 -> USB-IN + * USB-OUT -> IT_1 -> FU -> OT_3 -> ALSA_Capture + * ALSA_Playback -> IT_2 -> FU -> OT_4 -> USB-IN * Capture and Playback sampling rates are independently * controlled by two clock sources : * CLK_5 := c_srate, and CLK_6 := p_srate */ #define USB_OUT_CLK_ID (out_clk_src_desc.bClockID) #define USB_IN_CLK_ID (in_clk_src_desc.bClockID) +#define USB_OUT_FU_ID (out_feature_unit_desc->bUnitID) +#define USB_IN_FU_ID (in_feature_unit_desc->bUnitID) #define CONTROL_ABSENT 0 #define CONTROL_RDONLY 1 @@ -34,6 +39,8 @@ #define CLK_FREQ_CTRL 0 #define CLK_VLD_CTRL 2 +#define FU_MUTE_CTRL 0 +#define FU_VOL_CTRL 2 #define COPY_CTRL 0 #define CONN_CTRL 2 @@ -44,12 +51,24 @@ #define EPIN_EN(_opts) ((_opts)->p_chmask != 0) #define EPOUT_EN(_opts) ((_opts)->c_chmask != 0) +#define FUIN_EN(_opts) (EPIN_EN(_opts) \ + && ((_opts)->p_mute_present \ + || (_opts)->p_volume_present)) +#define FUOUT_EN(_opts) (EPOUT_EN(_opts) \ + && ((_opts)->c_mute_present \ + || (_opts)->c_volume_present)) #define EPOUT_FBACK_IN_EN(_opts) ((_opts)->c_sync == USB_ENDPOINT_SYNC_ASYNC) struct f_uac2 { struct g_audio g_audio; u8 ac_intf, as_in_intf, as_out_intf; u8 ac_alt, as_in_alt, as_out_alt; /* needed for get_alt() */ + + struct usb_ctrlrequest setup_cr; /* will be used in data stage */ + + /* Interrupt IN endpoint of AC interface */ + struct usb_ep *int_ep; + atomic_t int_count; }; static inline struct f_uac2 *func_to_uac2(struct usb_function *f) @@ -63,6 +82,8 @@ struct f_uac2_opts *g_audio_to_uac2_opts(struct g_audio *agdev) return container_of(agdev->func.fi, struct f_uac2_opts, func_inst); } +static int afunc_notify(struct g_audio *agdev, int unit_id, int cs); + /* --------- USB Function Interface ------------- */ enum { @@ -74,6 +95,8 @@ enum { STR_IO_IT, STR_USB_OT, STR_IO_OT, + STR_FU_IN, + STR_FU_OUT, STR_AS_OUT_ALT0, STR_AS_OUT_ALT1, STR_AS_IN_ALT0, @@ -92,6 +115,8 @@ static struct usb_string strings_fn[] = { [STR_IO_IT].s = "USBD Out", [STR_USB_OT].s = "USBH In", [STR_IO_OT].s = "USBD In", + [STR_FU_IN].s = "Capture Volume", + [STR_FU_OUT].s = "Playback Volume", [STR_AS_OUT_ALT0].s = "Playback Inactive", [STR_AS_OUT_ALT1].s = "Playback Active", [STR_AS_IN_ALT0].s = "Capture Inactive", @@ -126,7 +151,7 @@ static struct usb_interface_descriptor std_ac_if_desc = { .bDescriptorType = USB_DT_INTERFACE, .bAlternateSetting = 0, - .bNumEndpoints = 0, + /* .bNumEndpoints = DYNAMIC */ .bInterfaceClass = USB_CLASS_AUDIO, .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, .bInterfaceProtocol = UAC_VERSION_2, @@ -212,6 +237,9 @@ static struct uac2_output_terminal_descriptor io_out_ot_desc = { .bmControls = cpu_to_le16(CONTROL_RDWR << COPY_CTRL), }; +static struct uac2_feature_unit_descriptor *in_feature_unit_desc; +static struct uac2_feature_unit_descriptor *out_feature_unit_desc; + static struct uac2_ac_header_descriptor ac_hdr_desc = { .bLength = sizeof ac_hdr_desc, .bDescriptorType = USB_DT_CS_INTERFACE, @@ -223,6 +251,36 @@ static struct uac2_ac_header_descriptor ac_hdr_desc = { .bmControls = 0, }; +/* AC IN Interrupt Endpoint */ +static struct usb_endpoint_descriptor fs_ep_int_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(6), + .bInterval = 1, +}; + +static struct usb_endpoint_descriptor hs_ep_int_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(6), + .bInterval = 4, +}; + +static struct usb_endpoint_descriptor ss_ep_int_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(6), + .bInterval = 4, +}; + /* Audio Streaming OUT Interface - Alt0 */ static struct usb_interface_descriptor std_as_out_if0_desc = { .bLength = sizeof std_as_out_if0_desc, @@ -452,10 +510,14 @@ static struct usb_descriptor_header *fs_audio_desc[] = { (struct usb_descriptor_header *)&in_clk_src_desc, (struct usb_descriptor_header *)&out_clk_src_desc, (struct usb_descriptor_header *)&usb_out_it_desc, + (struct usb_descriptor_header *)&out_feature_unit_desc, (struct usb_descriptor_header *)&io_in_it_desc, (struct usb_descriptor_header *)&usb_in_ot_desc, + (struct usb_descriptor_header *)&in_feature_unit_desc, (struct usb_descriptor_header *)&io_out_ot_desc, + (struct usb_descriptor_header *)&fs_ep_int_desc, + (struct usb_descriptor_header *)&std_as_out_if0_desc, (struct usb_descriptor_header *)&std_as_out_if1_desc, @@ -483,10 +545,14 @@ static struct usb_descriptor_header *hs_audio_desc[] = { (struct usb_descriptor_header *)&in_clk_src_desc, (struct usb_descriptor_header *)&out_clk_src_desc, (struct usb_descriptor_header *)&usb_out_it_desc, + (struct usb_descriptor_header *)&out_feature_unit_desc, (struct usb_descriptor_header *)&io_in_it_desc, (struct usb_descriptor_header *)&usb_in_ot_desc, + (struct usb_descriptor_header *)&in_feature_unit_desc, (struct usb_descriptor_header *)&io_out_ot_desc, + (struct usb_descriptor_header *)&hs_ep_int_desc, + (struct usb_descriptor_header *)&std_as_out_if0_desc, (struct usb_descriptor_header *)&std_as_out_if1_desc, @@ -514,10 +580,14 @@ static struct usb_descriptor_header *ss_audio_desc[] = { (struct usb_descriptor_header *)&in_clk_src_desc, (struct usb_descriptor_header *)&out_clk_src_desc, (struct usb_descriptor_header *)&usb_out_it_desc, + (struct usb_descriptor_header *)&out_feature_unit_desc, (struct usb_descriptor_header *)&io_in_it_desc, (struct usb_descriptor_header *)&usb_in_ot_desc, + (struct usb_descriptor_header *)&in_feature_unit_desc, (struct usb_descriptor_header *)&io_out_ot_desc, + (struct usb_descriptor_header *)&ss_ep_int_desc, + (struct usb_descriptor_header *)&std_as_out_if0_desc, (struct usb_descriptor_header *)&std_as_out_if1_desc, @@ -539,6 +609,17 @@ static struct usb_descriptor_header *ss_audio_desc[] = { NULL, }; +struct cntrl_cur_lay2 { + __le16 wCUR; +}; + +struct cntrl_range_lay2 { + __le16 wNumSubRanges; + __le16 wMIN; + __le16 wMAX; + __le16 wRES; +} __packed; + struct cntrl_cur_lay3 { __le32 dCUR; }; @@ -595,6 +676,26 @@ static int set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts, return 0; } +static struct uac2_feature_unit_descriptor *build_fu_desc(int chmask) +{ + struct uac2_feature_unit_descriptor *fu_desc; + int channels = num_channels(chmask); + int fu_desc_size = UAC2_DT_FEATURE_UNIT_SIZE(channels); + + fu_desc = kzalloc(fu_desc_size, GFP_KERNEL); + if (!fu_desc) + return NULL; + + fu_desc->bLength = fu_desc_size; + fu_desc->bDescriptorType = USB_DT_CS_INTERFACE; + + fu_desc->bDescriptorSubtype = UAC_FEATURE_UNIT; + + /* bUnitID, bSourceID and bmaControls will be defined later */ + + return fu_desc; +} + /* Use macro to overcome line length limitation */ #define USBDHDR(p) (struct usb_descriptor_header *)(p) @@ -607,6 +708,7 @@ static void setup_headers(struct f_uac2_opts *opts, struct usb_endpoint_descriptor *epout_desc; struct usb_endpoint_descriptor *epin_desc; struct usb_endpoint_descriptor *epin_fback_desc; + struct usb_endpoint_descriptor *ep_int_desc; int i; switch (speed) { @@ -614,11 +716,13 @@ static void setup_headers(struct f_uac2_opts *opts, epout_desc = &fs_epout_desc; epin_desc = &fs_epin_desc; epin_fback_desc = &fs_epin_fback_desc; + ep_int_desc = &fs_ep_int_desc; break; case USB_SPEED_HIGH: epout_desc = &hs_epout_desc; epin_desc = &hs_epin_desc; epin_fback_desc = &hs_epin_fback_desc; + ep_int_desc = &hs_ep_int_desc; break; default: epout_desc = &ss_epout_desc; @@ -626,6 +730,7 @@ static void setup_headers(struct f_uac2_opts *opts, epout_desc_comp = &ss_epout_desc_comp; epin_desc_comp = &ss_epin_desc_comp; epin_fback_desc = &ss_epin_fback_desc; + ep_int_desc = &ss_ep_int_desc; } i = 0; @@ -637,13 +742,27 @@ static void setup_headers(struct f_uac2_opts *opts, if (EPOUT_EN(opts)) { headers[i++] = USBDHDR(&out_clk_src_desc); headers[i++] = USBDHDR(&usb_out_it_desc); - } + + if (FUOUT_EN(opts)) + headers[i++] = USBDHDR(out_feature_unit_desc); + } + if (EPIN_EN(opts)) { headers[i++] = USBDHDR(&io_in_it_desc); + + if (FUIN_EN(opts)) + headers[i++] = USBDHDR(in_feature_unit_desc); + headers[i++] = USBDHDR(&usb_in_ot_desc); } - if (EPOUT_EN(opts)) { + + if (EPOUT_EN(opts)) headers[i++] = USBDHDR(&io_out_ot_desc); + + if (FUOUT_EN(opts) || FUIN_EN(opts)) + headers[i++] = USBDHDR(ep_int_desc); + + if (EPOUT_EN(opts)) { headers[i++] = USBDHDR(&std_as_out_if0_desc); headers[i++] = USBDHDR(&std_as_out_if1_desc); headers[i++] = USBDHDR(&as_out_hdr_desc); @@ -657,6 +776,7 @@ static void setup_headers(struct f_uac2_opts *opts, if (EPOUT_FBACK_IN_EN(opts)) headers[i++] = USBDHDR(epin_fback_desc); } + if (EPIN_EN(opts)) { headers[i++] = USBDHDR(&std_as_in_if0_desc); headers[i++] = USBDHDR(&std_as_in_if1_desc); @@ -684,17 +804,35 @@ static void setup_descriptor(struct f_uac2_opts *opts) io_out_ot_desc.bTerminalID = i++; if (EPIN_EN(opts)) usb_in_ot_desc.bTerminalID = i++; + if (FUOUT_EN(opts)) + out_feature_unit_desc->bUnitID = i++; + if (FUIN_EN(opts)) + in_feature_unit_desc->bUnitID = i++; if (EPOUT_EN(opts)) out_clk_src_desc.bClockID = i++; if (EPIN_EN(opts)) in_clk_src_desc.bClockID = i++; usb_out_it_desc.bCSourceID = out_clk_src_desc.bClockID; - usb_in_ot_desc.bSourceID = io_in_it_desc.bTerminalID; + + if (FUIN_EN(opts)) { + usb_in_ot_desc.bSourceID = in_feature_unit_desc->bUnitID; + in_feature_unit_desc->bSourceID = io_in_it_desc.bTerminalID; + } else { + usb_in_ot_desc.bSourceID = io_in_it_desc.bTerminalID; + } + usb_in_ot_desc.bCSourceID = in_clk_src_desc.bClockID; io_in_it_desc.bCSourceID = in_clk_src_desc.bClockID; io_out_ot_desc.bCSourceID = out_clk_src_desc.bClockID; - io_out_ot_desc.bSourceID = usb_out_it_desc.bTerminalID; + + if (FUOUT_EN(opts)) { + io_out_ot_desc.bSourceID = out_feature_unit_desc->bUnitID; + out_feature_unit_desc->bSourceID = usb_out_it_desc.bTerminalID; + } else { + io_out_ot_desc.bSourceID = usb_out_it_desc.bTerminalID; + } + as_out_hdr_desc.bTerminalLink = usb_out_it_desc.bTerminalID; as_in_hdr_desc.bTerminalLink = usb_in_ot_desc.bTerminalID; @@ -706,6 +844,10 @@ static void setup_descriptor(struct f_uac2_opts *opts) len += sizeof(in_clk_src_desc); len += sizeof(usb_in_ot_desc); + + if (FUIN_EN(opts)) + len += in_feature_unit_desc->bLength; + len += sizeof(io_in_it_desc); ac_hdr_desc.wTotalLength = cpu_to_le16(len); iad_desc.bInterfaceCount++; @@ -715,6 +857,10 @@ static void setup_descriptor(struct f_uac2_opts *opts) len += sizeof(out_clk_src_desc); len += sizeof(usb_out_it_desc); + + if (FUOUT_EN(opts)) + len += out_feature_unit_desc->bLength; + len += sizeof(io_out_ot_desc); ac_hdr_desc.wTotalLength = cpu_to_le16(len); iad_desc.bInterfaceCount++; @@ -752,6 +898,28 @@ static int afunc_validate_opts(struct g_audio *agdev, struct device *dev) return -EINVAL; } + if (opts->p_volume_max <= opts->p_volume_min) { + dev_err(dev, "Error: incorrect playback volume max/min\n"); + return -EINVAL; + } else if (opts->c_volume_max <= opts->c_volume_min) { + dev_err(dev, "Error: incorrect capture volume max/min\n"); + return -EINVAL; + } else if (opts->p_volume_res <= 0) { + dev_err(dev, "Error: negative/zero playback volume resolution\n"); + return -EINVAL; + } else if (opts->c_volume_res <= 0) { + dev_err(dev, "Error: negative/zero capture volume resolution\n"); + return -EINVAL; + } + + if ((opts->p_volume_max - opts->p_volume_min) % opts->p_volume_res) { + dev_err(dev, "Error: incorrect playback volume resolution\n"); + return -EINVAL; + } else if ((opts->c_volume_max - opts->c_volume_min) % opts->c_volume_res) { + dev_err(dev, "Error: incorrect capture volume resolution\n"); + return -EINVAL; + } + return 0; } @@ -774,6 +942,20 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) us = usb_gstrings_attach(cdev, fn_strings, ARRAY_SIZE(strings_fn)); if (IS_ERR(us)) return PTR_ERR(us); + + if (FUOUT_EN(uac2_opts)) { + out_feature_unit_desc = build_fu_desc(uac2_opts->c_chmask); + if (!out_feature_unit_desc) + return -ENOMEM; + } + if (FUIN_EN(uac2_opts)) { + in_feature_unit_desc = build_fu_desc(uac2_opts->p_chmask); + if (!in_feature_unit_desc) { + ret = -ENOMEM; + goto err_free_fu; + } + } + iad_desc.iFunction = us[STR_ASSOC].id; std_ac_if_desc.iInterface = us[STR_IF_CTRL].id; in_clk_src_desc.iClockSource = us[STR_CLKSRC_IN].id; @@ -787,6 +969,17 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) std_as_in_if0_desc.iInterface = us[STR_AS_IN_ALT0].id; std_as_in_if1_desc.iInterface = us[STR_AS_IN_ALT1].id; + if (FUOUT_EN(uac2_opts)) { + u8 *i_feature = (u8 *)out_feature_unit_desc + + out_feature_unit_desc->bLength - 1; + *i_feature = us[STR_FU_OUT].id; + } + if (FUIN_EN(uac2_opts)) { + u8 *i_feature = (u8 *)in_feature_unit_desc + + in_feature_unit_desc->bLength - 1; + *i_feature = us[STR_FU_IN].id; + } + /* Initialize the configurable parameters */ usb_out_it_desc.bNrChannels = num_channels(uac2_opts->c_chmask); @@ -801,6 +994,26 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) as_out_fmt1_desc.bBitResolution = uac2_opts->c_ssize * 8; as_in_fmt1_desc.bSubslotSize = uac2_opts->p_ssize; as_in_fmt1_desc.bBitResolution = uac2_opts->p_ssize * 8; + if (FUOUT_EN(uac2_opts)) { + __le32 *bma = (__le32 *)&out_feature_unit_desc->bmaControls[0]; + u32 control = 0; + + if (uac2_opts->c_mute_present) + control |= CONTROL_RDWR << FU_MUTE_CTRL; + if (uac2_opts->c_volume_present) + control |= CONTROL_RDWR << FU_VOL_CTRL; + *bma = cpu_to_le32(control); + } + if (FUIN_EN(uac2_opts)) { + __le32 *bma = (__le32 *)&in_feature_unit_desc->bmaControls[0]; + u32 control = 0; + + if (uac2_opts->p_mute_present) + control |= CONTROL_RDWR << FU_MUTE_CTRL; + if (uac2_opts->p_volume_present) + control |= CONTROL_RDWR << FU_VOL_CTRL; + *bma = cpu_to_le32(control); + } snprintf(clksrc_in, sizeof(clksrc_in), "%uHz", uac2_opts->p_srate); snprintf(clksrc_out, sizeof(clksrc_out), "%uHz", uac2_opts->c_srate); @@ -808,7 +1021,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) ret = usb_interface_id(cfg, fn); if (ret < 0) { dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); - return ret; + goto err_free_fu; } iad_desc.bFirstInterface = ret; @@ -820,7 +1033,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) ret = usb_interface_id(cfg, fn); if (ret < 0) { dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); - return ret; + goto err_free_fu; } std_as_out_if0_desc.bInterfaceNumber = ret; std_as_out_if1_desc.bInterfaceNumber = ret; @@ -849,7 +1062,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) ret = usb_interface_id(cfg, fn); if (ret < 0) { dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); - return ret; + goto err_free_fu; } std_as_in_if0_desc.bInterfaceNumber = ret; std_as_in_if1_desc.bInterfaceNumber = ret; @@ -857,6 +1070,17 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) uac2->as_in_alt = 0; } + if (FUOUT_EN(uac2_opts) || FUIN_EN(uac2_opts)) { + uac2->int_ep = usb_ep_autoconfig(gadget, &fs_ep_int_desc); + if (!uac2->int_ep) { + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); + ret = -ENODEV; + goto err_free_fu; + } + + std_ac_if_desc.bNumEndpoints = 1; + } + /* Calculate wMaxPacketSize according to audio bandwidth */ ret = set_ep_max_packet_size(uac2_opts, &fs_epin_desc, USB_SPEED_FULL, true); @@ -904,7 +1128,8 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) agdev->out_ep = usb_ep_autoconfig(gadget, &fs_epout_desc); if (!agdev->out_ep) { dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); - return -ENODEV; + ret = -ENODEV; + goto err_free_fu; } if (EPOUT_FBACK_IN_EN(uac2_opts)) { agdev->in_ep_fback = usb_ep_autoconfig(gadget, @@ -912,7 +1137,8 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) if (!agdev->in_ep_fback) { dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); - return -ENODEV; + ret = -ENODEV; + goto err_free_fu; } } } @@ -921,7 +1147,8 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) agdev->in_ep = usb_ep_autoconfig(gadget, &fs_epin_desc); if (!agdev->in_ep) { dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); - return -ENODEV; + ret = -ENODEV; + goto err_free_fu; } } @@ -937,38 +1164,137 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) agdev->out_ep_maxpsize = max_t(u16, agdev->out_ep_maxpsize, le16_to_cpu(ss_epout_desc.wMaxPacketSize)); + // HS and SS endpoint addresses are copied from autoconfigured FS descriptors + hs_ep_int_desc.bEndpointAddress = fs_ep_int_desc.bEndpointAddress; hs_epout_desc.bEndpointAddress = fs_epout_desc.bEndpointAddress; hs_epin_fback_desc.bEndpointAddress = fs_epin_fback_desc.bEndpointAddress; hs_epin_desc.bEndpointAddress = fs_epin_desc.bEndpointAddress; ss_epout_desc.bEndpointAddress = fs_epout_desc.bEndpointAddress; ss_epin_fback_desc.bEndpointAddress = fs_epin_fback_desc.bEndpointAddress; ss_epin_desc.bEndpointAddress = fs_epin_desc.bEndpointAddress; + ss_ep_int_desc.bEndpointAddress = fs_ep_int_desc.bEndpointAddress; setup_descriptor(uac2_opts); ret = usb_assign_descriptors(fn, fs_audio_desc, hs_audio_desc, ss_audio_desc, ss_audio_desc); if (ret) - return ret; + goto err_free_fu; agdev->gadget = gadget; agdev->params.p_chmask = uac2_opts->p_chmask; agdev->params.p_srate = uac2_opts->p_srate; agdev->params.p_ssize = uac2_opts->p_ssize; + if (FUIN_EN(uac2_opts)) { + agdev->params.p_fu.id = USB_IN_FU_ID; + agdev->params.p_fu.mute_present = uac2_opts->p_mute_present; + agdev->params.p_fu.volume_present = uac2_opts->p_volume_present; + agdev->params.p_fu.volume_min = uac2_opts->p_volume_min; + agdev->params.p_fu.volume_max = uac2_opts->p_volume_max; + agdev->params.p_fu.volume_res = uac2_opts->p_volume_res; + } agdev->params.c_chmask = uac2_opts->c_chmask; agdev->params.c_srate = uac2_opts->c_srate; agdev->params.c_ssize = uac2_opts->c_ssize; + if (FUOUT_EN(uac2_opts)) { + agdev->params.c_fu.id = USB_OUT_FU_ID; + agdev->params.c_fu.mute_present = uac2_opts->c_mute_present; + agdev->params.c_fu.volume_present = uac2_opts->c_volume_present; + agdev->params.c_fu.volume_min = uac2_opts->c_volume_min; + agdev->params.c_fu.volume_max = uac2_opts->c_volume_max; + agdev->params.c_fu.volume_res = uac2_opts->c_volume_res; + } agdev->params.req_number = uac2_opts->req_number; agdev->params.fb_max = uac2_opts->fb_max; + + if (FUOUT_EN(uac2_opts) || FUIN_EN(uac2_opts)) + agdev->notify = afunc_notify; + ret = g_audio_setup(agdev, "UAC2 PCM", "UAC2_Gadget"); if (ret) goto err_free_descs; + return 0; err_free_descs: usb_free_all_descriptors(fn); agdev->gadget = NULL; +err_free_fu: + kfree(out_feature_unit_desc); + out_feature_unit_desc = NULL; + kfree(in_feature_unit_desc); + in_feature_unit_desc = NULL; + return ret; +} + +static void +afunc_notify_complete(struct usb_ep *_ep, struct usb_request *req) +{ + struct g_audio *agdev = req->context; + struct f_uac2 *uac2 = func_to_uac2(&agdev->func); + + atomic_dec(&uac2->int_count); + kfree(req->buf); + usb_ep_free_request(_ep, req); +} + +static int +afunc_notify(struct g_audio *agdev, int unit_id, int cs) +{ + struct f_uac2 *uac2 = func_to_uac2(&agdev->func); + struct usb_request *req; + struct uac2_interrupt_data_msg *msg; + u16 w_index, w_value; + int ret; + + if (!uac2->int_ep->enabled) + return 0; + + if (atomic_inc_return(&uac2->int_count) > UAC2_DEF_INT_REQ_NUM) { + atomic_dec(&uac2->int_count); + return 0; + } + + req = usb_ep_alloc_request(uac2->int_ep, GFP_ATOMIC); + if (req == NULL) { + ret = -ENOMEM; + goto err_dec_int_count; + } + + msg = kzalloc(sizeof(*msg), GFP_ATOMIC); + if (msg == NULL) { + ret = -ENOMEM; + goto err_free_request; + } + + w_index = unit_id << 8 | uac2->ac_intf; + w_value = cs << 8; + + msg->bInfo = 0; /* Non-vendor, interface interrupt */ + msg->bAttribute = UAC2_CS_CUR; + msg->wIndex = cpu_to_le16(w_index); + msg->wValue = cpu_to_le16(w_value); + + req->length = sizeof(*msg); + req->buf = msg; + req->context = agdev; + req->complete = afunc_notify_complete; + + ret = usb_ep_queue(uac2->int_ep, req, GFP_ATOMIC); + + if (ret) + goto err_free_msg; + + return 0; + +err_free_msg: + kfree(msg); +err_free_request: + usb_ep_free_request(uac2->int_ep, req); +err_dec_int_count: + atomic_dec(&uac2->int_count); + return ret; } @@ -977,6 +1303,7 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt) { struct usb_composite_dev *cdev = fn->config->cdev; struct f_uac2 *uac2 = func_to_uac2(fn); + struct g_audio *agdev = func_to_g_audio(fn); struct usb_gadget *gadget = cdev->gadget; struct device *dev = &gadget->dev; int ret = 0; @@ -993,6 +1320,14 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt) dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); return -EINVAL; } + + /* restart interrupt endpoint */ + if (uac2->int_ep) { + usb_ep_disable(uac2->int_ep); + config_ep_by_speed(gadget, &agdev->func, uac2->int_ep); + usb_ep_enable(uac2->int_ep); + } + return 0; } @@ -1047,6 +1382,8 @@ afunc_disable(struct usb_function *fn) uac2->as_out_alt = 0; u_audio_stop_capture(&uac2->g_audio); u_audio_stop_playback(&uac2->g_audio); + if (uac2->int_ep) + usb_ep_disable(uac2->int_ep); } static int @@ -1054,7 +1391,7 @@ in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr) { struct usb_request *req = fn->config->cdev->req; struct g_audio *agdev = func_to_g_audio(fn); - struct f_uac2_opts *opts; + struct f_uac2_opts *opts = g_audio_to_uac2_opts(agdev); u16 w_length = le16_to_cpu(cr->wLength); u16 w_index = le16_to_cpu(cr->wIndex); u16 w_value = le16_to_cpu(cr->wValue); @@ -1063,28 +1400,64 @@ in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr) int value = -EOPNOTSUPP; int p_srate, c_srate; - opts = g_audio_to_uac2_opts(agdev); p_srate = opts->p_srate; c_srate = opts->c_srate; - if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) { - struct cntrl_cur_lay3 c; - memset(&c, 0, sizeof(struct cntrl_cur_lay3)); + if ((entity_id == USB_IN_CLK_ID) || (entity_id == USB_OUT_CLK_ID)) { + if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) { + struct cntrl_cur_lay3 c; + + memset(&c, 0, sizeof(struct cntrl_cur_lay3)); + + if (entity_id == USB_IN_CLK_ID) + c.dCUR = cpu_to_le32(p_srate); + else if (entity_id == USB_OUT_CLK_ID) + c.dCUR = cpu_to_le32(c_srate); + + value = min_t(unsigned int, w_length, sizeof(c)); + memcpy(req->buf, &c, value); + } else if (control_selector == UAC2_CS_CONTROL_CLOCK_VALID) { + *(u8 *)req->buf = 1; + value = min_t(unsigned int, w_length, 1); + } else { + dev_err(&agdev->gadget->dev, + "%s:%d control_selector=%d TODO!\n", + __func__, __LINE__, control_selector); + } + } else if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) || + (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) { + unsigned int is_playback = 0; + + if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) + is_playback = 1; - if (entity_id == USB_IN_CLK_ID) - c.dCUR = cpu_to_le32(p_srate); - else if (entity_id == USB_OUT_CLK_ID) - c.dCUR = cpu_to_le32(c_srate); + if (control_selector == UAC_FU_MUTE) { + unsigned int mute; - value = min_t(unsigned, w_length, sizeof c); - memcpy(req->buf, &c, value); - } else if (control_selector == UAC2_CS_CONTROL_CLOCK_VALID) { - *(u8 *)req->buf = 1; - value = min_t(unsigned, w_length, 1); + u_audio_get_mute(agdev, is_playback, &mute); + + *(u8 *)req->buf = mute; + value = min_t(unsigned int, w_length, 1); + } else if (control_selector == UAC_FU_VOLUME) { + struct cntrl_cur_lay2 c; + s16 volume; + + memset(&c, 0, sizeof(struct cntrl_cur_lay2)); + + u_audio_get_volume(agdev, is_playback, &volume); + c.wCUR = cpu_to_le16(volume); + + value = min_t(unsigned int, w_length, sizeof(c)); + memcpy(req->buf, &c, value); + } else { + dev_err(&agdev->gadget->dev, + "%s:%d control_selector=%d TODO!\n", + __func__, __LINE__, control_selector); + } } else { dev_err(&agdev->gadget->dev, - "%s:%d control_selector=%d TODO!\n", - __func__, __LINE__, control_selector); + "%s:%d entity_id=%d control_selector=%d TODO!\n", + __func__, __LINE__, entity_id, control_selector); } return value; @@ -1095,38 +1468,77 @@ in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr) { struct usb_request *req = fn->config->cdev->req; struct g_audio *agdev = func_to_g_audio(fn); - struct f_uac2_opts *opts; + struct f_uac2_opts *opts = g_audio_to_uac2_opts(agdev); u16 w_length = le16_to_cpu(cr->wLength); u16 w_index = le16_to_cpu(cr->wIndex); u16 w_value = le16_to_cpu(cr->wValue); u8 entity_id = (w_index >> 8) & 0xff; u8 control_selector = w_value >> 8; - struct cntrl_range_lay3 r; int value = -EOPNOTSUPP; int p_srate, c_srate; - opts = g_audio_to_uac2_opts(agdev); p_srate = opts->p_srate; c_srate = opts->c_srate; - if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) { - if (entity_id == USB_IN_CLK_ID) - r.dMIN = cpu_to_le32(p_srate); - else if (entity_id == USB_OUT_CLK_ID) - r.dMIN = cpu_to_le32(c_srate); - else - return -EOPNOTSUPP; + if ((entity_id == USB_IN_CLK_ID) || (entity_id == USB_OUT_CLK_ID)) { + if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) { + struct cntrl_range_lay3 r; + + if (entity_id == USB_IN_CLK_ID) + r.dMIN = cpu_to_le32(p_srate); + else if (entity_id == USB_OUT_CLK_ID) + r.dMIN = cpu_to_le32(c_srate); + else + return -EOPNOTSUPP; - r.dMAX = r.dMIN; - r.dRES = 0; - r.wNumSubRanges = cpu_to_le16(1); + r.dMAX = r.dMIN; + r.dRES = 0; + r.wNumSubRanges = cpu_to_le16(1); - value = min_t(unsigned, w_length, sizeof r); - memcpy(req->buf, &r, value); + value = min_t(unsigned int, w_length, sizeof(r)); + memcpy(req->buf, &r, value); + } else { + dev_err(&agdev->gadget->dev, + "%s:%d control_selector=%d TODO!\n", + __func__, __LINE__, control_selector); + } + } else if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) || + (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) { + unsigned int is_playback = 0; + + if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) + is_playback = 1; + + if (control_selector == UAC_FU_VOLUME) { + struct cntrl_range_lay2 r; + s16 max_db, min_db, res_db; + + if (is_playback) { + max_db = opts->p_volume_max; + min_db = opts->p_volume_min; + res_db = opts->p_volume_res; + } else { + max_db = opts->c_volume_max; + min_db = opts->c_volume_min; + res_db = opts->c_volume_res; + } + + r.wMAX = cpu_to_le16(max_db); + r.wMIN = cpu_to_le16(min_db); + r.wRES = cpu_to_le16(res_db); + r.wNumSubRanges = cpu_to_le16(1); + + value = min_t(unsigned int, w_length, sizeof(r)); + memcpy(req->buf, &r, value); + } else { + dev_err(&agdev->gadget->dev, + "%s:%d control_selector=%d TODO!\n", + __func__, __LINE__, control_selector); + } } else { dev_err(&agdev->gadget->dev, - "%s:%d control_selector=%d TODO!\n", - __func__, __LINE__, control_selector); + "%s:%d entity_id=%d control_selector=%d TODO!\n", + __func__, __LINE__, entity_id, control_selector); } return value; @@ -1143,16 +1555,82 @@ ac_rq_in(struct usb_function *fn, const struct usb_ctrlrequest *cr) return -EOPNOTSUPP; } +static void +out_rq_cur_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct g_audio *agdev = req->context; + struct usb_composite_dev *cdev = agdev->func.config->cdev; + struct f_uac2_opts *opts = g_audio_to_uac2_opts(agdev); + struct f_uac2 *uac2 = func_to_uac2(&agdev->func); + struct usb_ctrlrequest *cr = &uac2->setup_cr; + u16 w_index = le16_to_cpu(cr->wIndex); + u16 w_value = le16_to_cpu(cr->wValue); + u8 entity_id = (w_index >> 8) & 0xff; + u8 control_selector = w_value >> 8; + + if (req->status != 0) { + dev_dbg(&cdev->gadget->dev, "completion err %d\n", req->status); + return; + } + + if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) || + (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) { + unsigned int is_playback = 0; + + if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) + is_playback = 1; + + if (control_selector == UAC_FU_MUTE) { + u8 mute = *(u8 *)req->buf; + + u_audio_set_mute(agdev, is_playback, mute); + + return; + } else if (control_selector == UAC_FU_VOLUME) { + struct cntrl_cur_lay2 *c = req->buf; + s16 volume; + + volume = le16_to_cpu(c->wCUR); + u_audio_set_volume(agdev, is_playback, volume); + + return; + } else { + dev_err(&agdev->gadget->dev, + "%s:%d control_selector=%d TODO!\n", + __func__, __LINE__, control_selector); + usb_ep_set_halt(ep); + } + } +} + static int out_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr) { + struct usb_request *req = fn->config->cdev->req; + struct g_audio *agdev = func_to_g_audio(fn); + struct f_uac2_opts *opts = g_audio_to_uac2_opts(agdev); + struct f_uac2 *uac2 = func_to_uac2(fn); u16 w_length = le16_to_cpu(cr->wLength); + u16 w_index = le16_to_cpu(cr->wIndex); u16 w_value = le16_to_cpu(cr->wValue); + u8 entity_id = (w_index >> 8) & 0xff; u8 control_selector = w_value >> 8; - if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) - return w_length; + if ((entity_id == USB_IN_CLK_ID) || (entity_id == USB_OUT_CLK_ID)) { + if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) + return w_length; + } else if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) || + (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) { + memcpy(&uac2->setup_cr, cr, sizeof(*cr)); + req->context = agdev; + req->complete = out_rq_cur_complete; + return w_length; + } else { + dev_err(&agdev->gadget->dev, + "%s:%d entity_id=%d control_selector=%d TODO!\n", + __func__, __LINE__, entity_id, control_selector); + } return -EOPNOTSUPP; } @@ -1228,7 +1706,15 @@ static struct configfs_item_operations f_uac2_item_ops = { .release = f_uac2_attr_release, }; -#define UAC2_ATTRIBUTE(name) \ +#define uac2_kstrtou32 kstrtou32 +#define uac2_kstrtos16 kstrtos16 +#define uac2_kstrtobool(s, base, res) kstrtobool((s), (res)) + +static const char *u32_fmt = "%u\n"; +static const char *s16_fmt = "%hd\n"; +static const char *bool_fmt = "%u\n"; + +#define UAC2_ATTRIBUTE(type, name) \ static ssize_t f_uac2_opts_##name##_show(struct config_item *item, \ char *page) \ { \ @@ -1236,7 +1722,7 @@ static ssize_t f_uac2_opts_##name##_show(struct config_item *item, \ int result; \ \ mutex_lock(&opts->lock); \ - result = sprintf(page, "%u\n", opts->name); \ + result = sprintf(page, type##_fmt, opts->name); \ mutex_unlock(&opts->lock); \ \ return result; \ @@ -1247,7 +1733,7 @@ static ssize_t f_uac2_opts_##name##_store(struct config_item *item, \ { \ struct f_uac2_opts *opts = to_f_uac2_opts(item); \ int ret; \ - u32 num; \ + type num; \ \ mutex_lock(&opts->lock); \ if (opts->refcnt) { \ @@ -1255,7 +1741,7 @@ static ssize_t f_uac2_opts_##name##_store(struct config_item *item, \ goto end; \ } \ \ - ret = kstrtou32(page, 0, &num); \ + ret = uac2_kstrto##type(page, 0, &num); \ if (ret) \ goto end; \ \ @@ -1325,15 +1811,27 @@ end: \ \ CONFIGFS_ATTR(f_uac2_opts_, name) -UAC2_ATTRIBUTE(p_chmask); -UAC2_ATTRIBUTE(p_srate); -UAC2_ATTRIBUTE(p_ssize); -UAC2_ATTRIBUTE(c_chmask); -UAC2_ATTRIBUTE(c_srate); +UAC2_ATTRIBUTE(u32, p_chmask); +UAC2_ATTRIBUTE(u32, p_srate); +UAC2_ATTRIBUTE(u32, p_ssize); +UAC2_ATTRIBUTE(u32, c_chmask); +UAC2_ATTRIBUTE(u32, c_srate); UAC2_ATTRIBUTE_SYNC(c_sync); -UAC2_ATTRIBUTE(c_ssize); -UAC2_ATTRIBUTE(req_number); -UAC2_ATTRIBUTE(fb_max); +UAC2_ATTRIBUTE(u32, c_ssize); +UAC2_ATTRIBUTE(u32, req_number); + +UAC2_ATTRIBUTE(bool, p_mute_present); +UAC2_ATTRIBUTE(bool, p_volume_present); +UAC2_ATTRIBUTE(s16, p_volume_min); +UAC2_ATTRIBUTE(s16, p_volume_max); +UAC2_ATTRIBUTE(s16, p_volume_res); + +UAC2_ATTRIBUTE(bool, c_mute_present); +UAC2_ATTRIBUTE(bool, c_volume_present); +UAC2_ATTRIBUTE(s16, c_volume_min); +UAC2_ATTRIBUTE(s16, c_volume_max); +UAC2_ATTRIBUTE(s16, c_volume_res); +UAC2_ATTRIBUTE(u32, fb_max); static struct configfs_attribute *f_uac2_attrs[] = { &f_uac2_opts_attr_p_chmask, @@ -1345,6 +1843,19 @@ static struct configfs_attribute *f_uac2_attrs[] = { &f_uac2_opts_attr_c_sync, &f_uac2_opts_attr_req_number, &f_uac2_opts_attr_fb_max, + + &f_uac2_opts_attr_p_mute_present, + &f_uac2_opts_attr_p_volume_present, + &f_uac2_opts_attr_p_volume_min, + &f_uac2_opts_attr_p_volume_max, + &f_uac2_opts_attr_p_volume_res, + + &f_uac2_opts_attr_c_mute_present, + &f_uac2_opts_attr_c_volume_present, + &f_uac2_opts_attr_c_volume_min, + &f_uac2_opts_attr_c_volume_max, + &f_uac2_opts_attr_c_volume_res, + NULL, }; @@ -1383,6 +1894,19 @@ static struct usb_function_instance *afunc_alloc_inst(void) opts->c_srate = UAC2_DEF_CSRATE; opts->c_ssize = UAC2_DEF_CSSIZE; opts->c_sync = UAC2_DEF_CSYNC; + + opts->p_mute_present = UAC2_DEF_MUTE_PRESENT; + opts->p_volume_present = UAC2_DEF_VOLUME_PRESENT; + opts->p_volume_min = UAC2_DEF_MIN_DB; + opts->p_volume_max = UAC2_DEF_MAX_DB; + opts->p_volume_res = UAC2_DEF_RES_DB; + + opts->c_mute_present = UAC2_DEF_MUTE_PRESENT; + opts->c_volume_present = UAC2_DEF_VOLUME_PRESENT; + opts->c_volume_min = UAC2_DEF_MIN_DB; + opts->c_volume_max = UAC2_DEF_MAX_DB; + opts->c_volume_res = UAC2_DEF_RES_DB; + opts->req_number = UAC2_DEF_REQ_NUM; opts->fb_max = UAC2_DEF_FB_MAX; return &opts->func_inst; @@ -1409,6 +1933,11 @@ static void afunc_unbind(struct usb_configuration *c, struct usb_function *f) usb_free_all_descriptors(f); agdev->gadget = NULL; + + kfree(out_feature_unit_desc); + out_feature_unit_desc = NULL; + kfree(in_feature_unit_desc); + in_feature_unit_desc = NULL; } static struct usb_function *afunc_alloc(struct usb_function_instance *fi) @@ -1441,3 +1970,4 @@ DECLARE_USB_FUNCTION_INIT(uac2, afunc_alloc_inst, afunc_alloc); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Yadwinder Singh"); MODULE_AUTHOR("Jaswinder Singh"); +MODULE_AUTHOR("Ruslan Bilovol"); diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index f48a00e49794..9d87c0fb8f92 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -418,6 +418,7 @@ uvc_register_video(struct uvc_device *uvc) /* TODO reference counting. */ uvc->vdev.v4l2_dev = &uvc->v4l2_dev; + uvc->vdev.v4l2_dev->dev = &cdev->gadget->dev; uvc->vdev.fops = &uvc_v4l2_fops; uvc->vdev.ioctl_ops = &uvc_v4l2_ioctl_ops; uvc->vdev.release = video_device_release_empty; diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c index 9e5c950612d0..32ef22857083 100644 --- a/drivers/usb/gadget/function/u_audio.c +++ b/drivers/usb/gadget/function/u_audio.c @@ -12,11 +12,14 @@ * Jaswinder Singh (jaswinder.singh@linaro.org) */ +#include <linux/kernel.h> #include <linux/module.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/control.h> +#include <sound/tlv.h> +#include <linux/usb/audio.h> #include "u_audio.h" @@ -24,6 +27,12 @@ #define PRD_SIZE_MAX PAGE_SIZE #define MIN_PERIODS 4 +enum { + UAC_FBACK_CTRL, + UAC_MUTE_CTRL, + UAC_VOLUME_CTRL, +}; + /* Runtime data params for one stream */ struct uac_rtd_params { struct snd_uac_chip *uac; /* parent chip */ @@ -43,6 +52,17 @@ struct uac_rtd_params { struct usb_request *req_fback; /* Feedback endpoint request */ bool fb_ep_enabled; /* if the ep is enabled */ + + /* Volume/Mute controls and their state */ + int fu_id; /* Feature Unit ID */ + struct snd_kcontrol *snd_kctl_volume; + struct snd_kcontrol *snd_kctl_mute; + s16 volume_min, volume_max, volume_res; + s16 volume; + int mute; + + spinlock_t lock; /* lock for control transfers */ + }; struct snd_uac_chip { @@ -604,6 +624,103 @@ void u_audio_stop_playback(struct g_audio *audio_dev) } EXPORT_SYMBOL_GPL(u_audio_stop_playback); +int u_audio_get_volume(struct g_audio *audio_dev, int playback, s16 *val) +{ + struct snd_uac_chip *uac = audio_dev->uac; + struct uac_rtd_params *prm; + unsigned long flags; + + if (playback) + prm = &uac->p_prm; + else + prm = &uac->c_prm; + + spin_lock_irqsave(&prm->lock, flags); + *val = prm->volume; + spin_unlock_irqrestore(&prm->lock, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(u_audio_get_volume); + +int u_audio_set_volume(struct g_audio *audio_dev, int playback, s16 val) +{ + struct snd_uac_chip *uac = audio_dev->uac; + struct uac_rtd_params *prm; + unsigned long flags; + int change = 0; + + if (playback) + prm = &uac->p_prm; + else + prm = &uac->c_prm; + + spin_lock_irqsave(&prm->lock, flags); + val = clamp(val, prm->volume_min, prm->volume_max); + if (prm->volume != val) { + prm->volume = val; + change = 1; + } + spin_unlock_irqrestore(&prm->lock, flags); + + if (change) + snd_ctl_notify(uac->card, SNDRV_CTL_EVENT_MASK_VALUE, + &prm->snd_kctl_volume->id); + + return 0; +} +EXPORT_SYMBOL_GPL(u_audio_set_volume); + +int u_audio_get_mute(struct g_audio *audio_dev, int playback, int *val) +{ + struct snd_uac_chip *uac = audio_dev->uac; + struct uac_rtd_params *prm; + unsigned long flags; + + if (playback) + prm = &uac->p_prm; + else + prm = &uac->c_prm; + + spin_lock_irqsave(&prm->lock, flags); + *val = prm->mute; + spin_unlock_irqrestore(&prm->lock, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(u_audio_get_mute); + +int u_audio_set_mute(struct g_audio *audio_dev, int playback, int val) +{ + struct snd_uac_chip *uac = audio_dev->uac; + struct uac_rtd_params *prm; + unsigned long flags; + int change = 0; + int mute; + + if (playback) + prm = &uac->p_prm; + else + prm = &uac->c_prm; + + mute = val ? 1 : 0; + + spin_lock_irqsave(&prm->lock, flags); + if (prm->mute != mute) { + prm->mute = mute; + change = 1; + } + spin_unlock_irqrestore(&prm->lock, flags); + + if (change) + snd_ctl_notify(uac->card, SNDRV_CTL_EVENT_MASK_VALUE, + &prm->snd_kctl_mute->id); + + return 0; +} +EXPORT_SYMBOL_GPL(u_audio_set_mute); + + static int u_audio_pitch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -663,14 +780,158 @@ static int u_audio_pitch_put(struct snd_kcontrol *kcontrol, return change; } -static const struct snd_kcontrol_new u_audio_controls[] = { +static int u_audio_mute_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { - .iface = SNDRV_CTL_ELEM_IFACE_PCM, - .name = "Capture Pitch 1000000", - .info = u_audio_pitch_info, - .get = u_audio_pitch_get, - .put = u_audio_pitch_put, -}, + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + uinfo->value.integer.step = 1; + + return 0; +} + +static int u_audio_mute_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&prm->lock, flags); + ucontrol->value.integer.value[0] = !prm->mute; + spin_unlock_irqrestore(&prm->lock, flags); + + return 0; +} + +static int u_audio_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); + struct snd_uac_chip *uac = prm->uac; + struct g_audio *audio_dev = uac->audio_dev; + unsigned int val; + unsigned long flags; + int change = 0; + + val = !ucontrol->value.integer.value[0]; + + spin_lock_irqsave(&prm->lock, flags); + if (val != prm->mute) { + prm->mute = val; + change = 1; + } + spin_unlock_irqrestore(&prm->lock, flags); + + if (change && audio_dev->notify) + audio_dev->notify(audio_dev, prm->fu_id, UAC_FU_MUTE); + + return change; +} + +/* + * TLV callback for mixer volume controls + */ +static int u_audio_volume_tlv(struct snd_kcontrol *kcontrol, int op_flag, + unsigned int size, unsigned int __user *_tlv) +{ + struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); + DECLARE_TLV_DB_MINMAX(scale, 0, 0); + + if (size < sizeof(scale)) + return -ENOMEM; + + /* UAC volume resolution is 1/256 dB, TLV is 1/100 dB */ + scale[2] = (prm->volume_min * 100) / 256; + scale[3] = (prm->volume_max * 100) / 256; + if (copy_to_user(_tlv, scale, sizeof(scale))) + return -EFAULT; + + return 0; +} + +static int u_audio_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = + (prm->volume_max - prm->volume_min + prm->volume_res - 1) + / prm->volume_res; + uinfo->value.integer.step = 1; + + return 0; +} + +static int u_audio_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&prm->lock, flags); + ucontrol->value.integer.value[0] = + (prm->volume - prm->volume_min) / prm->volume_res; + spin_unlock_irqrestore(&prm->lock, flags); + + return 0; +} + +static int u_audio_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); + struct snd_uac_chip *uac = prm->uac; + struct g_audio *audio_dev = uac->audio_dev; + unsigned int val; + s16 volume; + unsigned long flags; + int change = 0; + + val = ucontrol->value.integer.value[0]; + + spin_lock_irqsave(&prm->lock, flags); + volume = (val * prm->volume_res) + prm->volume_min; + volume = clamp(volume, prm->volume_min, prm->volume_max); + if (volume != prm->volume) { + prm->volume = volume; + change = 1; + } + spin_unlock_irqrestore(&prm->lock, flags); + + if (change && audio_dev->notify) + audio_dev->notify(audio_dev, prm->fu_id, UAC_FU_VOLUME); + + return change; +} + + +static struct snd_kcontrol_new u_audio_controls[] = { + [UAC_FBACK_CTRL] { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "Capture Pitch 1000000", + .info = u_audio_pitch_info, + .get = u_audio_pitch_get, + .put = u_audio_pitch_put, + }, + [UAC_MUTE_CTRL] { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", /* will be filled later */ + .info = u_audio_mute_info, + .get = u_audio_mute_get, + .put = u_audio_mute_put, + }, + [UAC_VOLUME_CTRL] { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", /* will be filled later */ + .info = u_audio_volume_info, + .get = u_audio_volume_get, + .put = u_audio_volume_put, + }, }; int g_audio_setup(struct g_audio *g_audio, const char *pcm_name, @@ -682,7 +943,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name, struct snd_kcontrol *kctl; struct uac_params *params; int p_chmask, c_chmask; - int err; + int i, err; if (!g_audio) return -EINVAL; @@ -700,7 +961,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; - uac->c_prm.uac = uac; + spin_lock_init(&prm->lock); + uac->c_prm.uac = uac; prm->max_psize = g_audio->out_ep_maxpsize; prm->reqs = kcalloc(params->req_number, @@ -723,6 +985,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name, if (p_chmask) { struct uac_rtd_params *prm = &uac->p_prm; + spin_lock_init(&prm->lock); uac->p_prm.uac = uac; prm->max_psize = g_audio->in_ep_maxpsize; @@ -767,10 +1030,18 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name, snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &uac_pcm_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &uac_pcm_ops); - if (c_chmask && g_audio->in_ep_fback) { + /* + * Create mixer and controls + * Create only if it's required on USB side + */ + if ((c_chmask && g_audio->in_ep_fback) + || (p_chmask && params->p_fu.id) + || (c_chmask && params->c_fu.id)) strscpy(card->mixername, card_name, sizeof(card->driver)); - kctl = snd_ctl_new1(&u_audio_controls[0], &uac->c_prm); + if (c_chmask && g_audio->in_ep_fback) { + kctl = snd_ctl_new1(&u_audio_controls[UAC_FBACK_CTRL], + &uac->c_prm); if (!kctl) { err = -ENOMEM; goto snd_fail; @@ -784,6 +1055,82 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name, goto snd_fail; } + for (i = 0; i <= SNDRV_PCM_STREAM_LAST; i++) { + struct uac_rtd_params *prm; + struct uac_fu_params *fu; + char ctrl_name[24]; + char *direction; + + if (!pcm->streams[i].substream_count) + continue; + + if (i == SNDRV_PCM_STREAM_PLAYBACK) { + prm = &uac->p_prm; + fu = ¶ms->p_fu; + direction = "Playback"; + } else { + prm = &uac->c_prm; + fu = ¶ms->c_fu; + direction = "Capture"; + } + + prm->fu_id = fu->id; + + if (fu->mute_present) { + snprintf(ctrl_name, sizeof(ctrl_name), + "PCM %s Switch", direction); + + u_audio_controls[UAC_MUTE_CTRL].name = ctrl_name; + + kctl = snd_ctl_new1(&u_audio_controls[UAC_MUTE_CTRL], + prm); + if (!kctl) { + err = -ENOMEM; + goto snd_fail; + } + + kctl->id.device = pcm->device; + kctl->id.subdevice = i; + + err = snd_ctl_add(card, kctl); + if (err < 0) + goto snd_fail; + prm->snd_kctl_mute = kctl; + prm->mute = 0; + } + + if (fu->volume_present) { + snprintf(ctrl_name, sizeof(ctrl_name), + "PCM %s Volume", direction); + + u_audio_controls[UAC_VOLUME_CTRL].name = ctrl_name; + + kctl = snd_ctl_new1(&u_audio_controls[UAC_VOLUME_CTRL], + prm); + if (!kctl) { + err = -ENOMEM; + goto snd_fail; + } + + kctl->id.device = pcm->device; + kctl->id.subdevice = i; + + + kctl->tlv.c = u_audio_volume_tlv; + kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; + + err = snd_ctl_add(card, kctl); + if (err < 0) + goto snd_fail; + prm->snd_kctl_volume = kctl; + prm->volume = fu->volume_max; + prm->volume_max = fu->volume_max; + prm->volume_min = fu->volume_min; + prm->volume_res = fu->volume_res; + } + } + strscpy(card->driver, card_name, sizeof(card->driver)); strscpy(card->shortname, card_name, sizeof(card->shortname)); sprintf(card->longname, "%s %i", card_name, card->dev->id); diff --git a/drivers/usb/gadget/function/u_audio.h b/drivers/usb/gadget/function/u_audio.h index a218cdf771fe..001a79a46022 100644 --- a/drivers/usb/gadget/function/u_audio.h +++ b/drivers/usb/gadget/function/u_audio.h @@ -19,16 +19,30 @@ */ #define FBACK_SLOW_MAX 250 +/* Feature Unit parameters */ +struct uac_fu_params { + int id; /* Feature Unit ID */ + + bool mute_present; /* mute control enable */ + + bool volume_present; /* volume control enable */ + s16 volume_min; /* min volume in 1/256 dB */ + s16 volume_max; /* max volume in 1/256 dB */ + s16 volume_res; /* volume resolution in 1/256 dB */ +}; + struct uac_params { /* playback */ int p_chmask; /* channel mask */ int p_srate; /* rate in Hz */ int p_ssize; /* sample size */ + struct uac_fu_params p_fu; /* Feature Unit parameters */ /* capture */ int c_chmask; /* channel mask */ int c_srate; /* rate in Hz */ int c_ssize; /* sample size */ + struct uac_fu_params c_fu; /* Feature Unit parameters */ int req_number; /* number of preallocated requests */ int fb_max; /* upper frequency drift feedback limit per-mil */ @@ -49,6 +63,9 @@ struct g_audio { /* Max packet size for all out_ep possible speeds */ unsigned int out_ep_maxpsize; + /* Notify UAC driver about control change */ + int (*notify)(struct g_audio *g_audio, int unit_id, int cs); + /* The ALSA Sound Card it represents on the USB-Client side */ struct snd_uac_chip *uac; @@ -94,4 +111,9 @@ void u_audio_stop_capture(struct g_audio *g_audio); int u_audio_start_playback(struct g_audio *g_audio); void u_audio_stop_playback(struct g_audio *g_audio); +int u_audio_get_volume(struct g_audio *g_audio, int playback, s16 *val); +int u_audio_set_volume(struct g_audio *g_audio, int playback, s16 val); +int u_audio_get_mute(struct g_audio *g_audio, int playback, int *val); +int u_audio_set_mute(struct g_audio *g_audio, int playback, int val); + #endif /* __U_AUDIO_H */ diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index d1d044d9f859..85a3f6d4b5af 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -492,8 +492,9 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, } spin_unlock_irqrestore(&dev->lock, flags); - if (skb && !in) { - dev_kfree_skb_any(skb); + if (!in) { + if (skb) + dev_kfree_skb_any(skb); return NETDEV_TX_OK; } diff --git a/drivers/usb/gadget/function/u_hid.h b/drivers/usb/gadget/function/u_hid.h index 98d6af558c03..84bb70292855 100644 --- a/drivers/usb/gadget/function/u_hid.h +++ b/drivers/usb/gadget/function/u_hid.h @@ -20,6 +20,7 @@ struct f_hid_opts { int minor; unsigned char subclass; unsigned char protocol; + unsigned char no_out_endpoint; unsigned short report_length; unsigned short report_desc_length; unsigned char *report_desc; diff --git a/drivers/usb/gadget/function/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h index 39c0e29e1b46..589fae861141 100644 --- a/drivers/usb/gadget/function/u_uac1.h +++ b/drivers/usb/gadget/function/u_uac1.h @@ -18,6 +18,13 @@ #define UAC1_DEF_PSRATE 48000 #define UAC1_DEF_PSSIZE 2 #define UAC1_DEF_REQ_NUM 2 +#define UAC1_DEF_INT_REQ_NUM 10 + +#define UAC1_DEF_MUTE_PRESENT 1 +#define UAC1_DEF_VOLUME_PRESENT 1 +#define UAC1_DEF_MIN_DB (-100*256) /* -100 dB */ +#define UAC1_DEF_MAX_DB 0 /* 0 dB */ +#define UAC1_DEF_RES_DB (1*256) /* 1 dB */ struct f_uac1_opts { @@ -28,6 +35,19 @@ struct f_uac1_opts { int p_chmask; int p_srate; int p_ssize; + + bool p_mute_present; + bool p_volume_present; + s16 p_volume_min; + s16 p_volume_max; + s16 p_volume_res; + + bool c_mute_present; + bool c_volume_present; + s16 c_volume_min; + s16 c_volume_max; + s16 c_volume_res; + int req_number; unsigned bound:1; diff --git a/drivers/usb/gadget/function/u_uac2.h b/drivers/usb/gadget/function/u_uac2.h index 179d3ef6a195..a73b35774c44 100644 --- a/drivers/usb/gadget/function/u_uac2.h +++ b/drivers/usb/gadget/function/u_uac2.h @@ -22,8 +22,16 @@ #define UAC2_DEF_CSRATE 64000 #define UAC2_DEF_CSSIZE 2 #define UAC2_DEF_CSYNC USB_ENDPOINT_SYNC_ASYNC + +#define UAC2_DEF_MUTE_PRESENT 1 +#define UAC2_DEF_VOLUME_PRESENT 1 +#define UAC2_DEF_MIN_DB (-100*256) /* -100 dB */ +#define UAC2_DEF_MAX_DB 0 /* 0 dB */ +#define UAC2_DEF_RES_DB (1*256) /* 1 dB */ + #define UAC2_DEF_REQ_NUM 2 #define UAC2_DEF_FB_MAX 5 +#define UAC2_DEF_INT_REQ_NUM 10 struct f_uac2_opts { struct usb_function_instance func_inst; @@ -34,9 +42,22 @@ struct f_uac2_opts { int c_srate; int c_ssize; int c_sync; + + bool p_mute_present; + bool p_volume_present; + s16 p_volume_min; + s16 p_volume_max; + s16 p_volume_res; + + bool c_mute_present; + bool c_volume_present; + s16 c_volume_min; + s16 c_volume_max; + s16 c_volume_res; + int req_number; int fb_max; - bool bound; + bool bound; struct mutex lock; int refcnt; diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index 23ee25383c1f..255a61bd6a6a 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -65,13 +65,19 @@ extern unsigned int uvc_gadget_trace_param; * Driver specific constants */ -#define UVC_NUM_REQUESTS 4 #define UVC_MAX_REQUEST_SIZE 64 #define UVC_MAX_EVENTS 4 /* ------------------------------------------------------------------------ * Structures */ +struct uvc_request { + struct usb_request *req; + u8 *req_buffer; + struct uvc_video *video; + struct sg_table sgt; + u8 header[2]; +}; struct uvc_video { struct uvc_device *uvc; @@ -87,13 +93,16 @@ struct uvc_video { unsigned int imagesize; struct mutex mutex; /* protects frame parameters */ + unsigned int uvc_num_requests; + /* Requests */ unsigned int req_size; - struct usb_request *req[UVC_NUM_REQUESTS]; - __u8 *req_buffer[UVC_NUM_REQUESTS]; + struct uvc_request *ureq; struct list_head req_free; spinlock_t req_lock; + unsigned int req_int_count; + void (*encode) (struct usb_request *req, struct uvc_video *video, struct uvc_buffer *buf); diff --git a/drivers/usb/gadget/function/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c index 61e2c94cc0b0..7d00ad7c154c 100644 --- a/drivers/usb/gadget/function/uvc_queue.c +++ b/drivers/usb/gadget/function/uvc_queue.c @@ -17,6 +17,7 @@ #include <linux/wait.h> #include <media/v4l2-common.h> +#include <media/videobuf2-dma-sg.h> #include <media/videobuf2-vmalloc.h> #include "uvc.h" @@ -43,6 +44,7 @@ static int uvc_queue_setup(struct vb2_queue *vq, { struct uvc_video_queue *queue = vb2_get_drv_priv(vq); struct uvc_video *video = container_of(queue, struct uvc_video, queue); + struct usb_composite_dev *cdev = video->uvc->func.config->cdev; if (*nbuffers > UVC_MAX_VIDEO_BUFFERS) *nbuffers = UVC_MAX_VIDEO_BUFFERS; @@ -51,6 +53,11 @@ static int uvc_queue_setup(struct vb2_queue *vq, sizes[0] = video->imagesize; + if (cdev->gadget->speed < USB_SPEED_SUPER) + video->uvc_num_requests = 4; + else + video->uvc_num_requests = 64; + return 0; } @@ -70,7 +77,12 @@ static int uvc_buffer_prepare(struct vb2_buffer *vb) return -ENODEV; buf->state = UVC_BUF_STATE_QUEUED; - buf->mem = vb2_plane_vaddr(vb, 0); + if (queue->use_sg) { + buf->sgt = vb2_dma_sg_plane_desc(vb, 0); + buf->sg = buf->sgt->sgl; + } else { + buf->mem = vb2_plane_vaddr(vb, 0); + } buf->length = vb2_plane_size(vb, 0); if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) buf->bytesused = 0; @@ -110,9 +122,11 @@ static const struct vb2_ops uvc_queue_qops = { .wait_finish = vb2_ops_wait_finish, }; -int uvcg_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type, +int uvcg_queue_init(struct uvc_video_queue *queue, struct device *dev, enum v4l2_buf_type type, struct mutex *lock) { + struct uvc_video *video = container_of(queue, struct uvc_video, queue); + struct usb_composite_dev *cdev = video->uvc->func.config->cdev; int ret; queue->queue.type = type; @@ -121,9 +135,17 @@ int uvcg_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type, queue->queue.buf_struct_size = sizeof(struct uvc_buffer); queue->queue.ops = &uvc_queue_qops; queue->queue.lock = lock; - queue->queue.mem_ops = &vb2_vmalloc_memops; + if (cdev->gadget->sg_supported) { + queue->queue.mem_ops = &vb2_dma_sg_memops; + queue->use_sg = 1; + } else { + queue->queue.mem_ops = &vb2_vmalloc_memops; + } + queue->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC | V4L2_BUF_FLAG_TSTAMP_SRC_EOF; + queue->queue.dev = dev; + ret = vb2_queue_init(&queue->queue); if (ret) return ret; diff --git a/drivers/usb/gadget/function/uvc_queue.h b/drivers/usb/gadget/function/uvc_queue.h index 2f0fff769843..05360a0767f6 100644 --- a/drivers/usb/gadget/function/uvc_queue.h +++ b/drivers/usb/gadget/function/uvc_queue.h @@ -34,6 +34,9 @@ struct uvc_buffer { enum uvc_buffer_state state; void *mem; + struct sg_table *sgt; + struct scatterlist *sg; + unsigned int offset; unsigned int length; unsigned int bytesused; }; @@ -50,6 +53,8 @@ struct uvc_video_queue { unsigned int buf_used; + bool use_sg; + spinlock_t irqlock; /* Protects flags and irqqueue */ struct list_head irqqueue; }; @@ -59,7 +64,7 @@ static inline int uvc_queue_streaming(struct uvc_video_queue *queue) return vb2_is_streaming(&queue->queue); } -int uvcg_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type, +int uvcg_queue_init(struct uvc_video_queue *queue, struct device *dev, enum v4l2_buf_type type, struct mutex *lock); void uvcg_free_buffers(struct uvc_video_queue *queue); diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index 633e23d58d86..b4a763e5f70e 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -27,10 +27,10 @@ static int uvc_video_encode_header(struct uvc_video *video, struct uvc_buffer *buf, u8 *data, int len) { - data[0] = 2; + data[0] = UVCG_REQUEST_HEADER_LEN; data[1] = UVC_STREAM_EOH | video->fid; - if (buf->bytesused - video->queue.buf_used <= len - 2) + if (buf->bytesused - video->queue.buf_used <= len - UVCG_REQUEST_HEADER_LEN) data[1] |= UVC_STREAM_EOF; return 2; @@ -95,6 +95,71 @@ uvc_video_encode_bulk(struct usb_request *req, struct uvc_video *video, } static void +uvc_video_encode_isoc_sg(struct usb_request *req, struct uvc_video *video, + struct uvc_buffer *buf) +{ + unsigned int pending = buf->bytesused - video->queue.buf_used; + struct uvc_request *ureq = req->context; + struct scatterlist *sg, *iter; + unsigned int len = video->req_size; + unsigned int sg_left, part = 0; + unsigned int i; + int ret; + + sg = ureq->sgt.sgl; + sg_init_table(sg, ureq->sgt.nents); + + /* Init the header. */ + ret = uvc_video_encode_header(video, buf, ureq->header, + video->req_size); + sg_set_buf(sg, ureq->header, UVCG_REQUEST_HEADER_LEN); + len -= ret; + + if (pending <= len) + len = pending; + + req->length = (len == pending) ? + len + UVCG_REQUEST_HEADER_LEN : video->req_size; + + /* Init the pending sgs with payload */ + sg = sg_next(sg); + + for_each_sg(sg, iter, ureq->sgt.nents - 1, i) { + if (!len || !buf->sg) + break; + + sg_left = sg_dma_len(buf->sg) - buf->offset; + part = min_t(unsigned int, len, sg_left); + + sg_set_page(iter, sg_page(buf->sg), part, buf->offset); + + if (part == sg_left) { + buf->offset = 0; + buf->sg = sg_next(buf->sg); + } else { + buf->offset += part; + } + len -= part; + } + + /* Assign the video data with header. */ + req->buf = NULL; + req->sg = ureq->sgt.sgl; + req->num_sgs = i + 1; + + req->length -= len; + video->queue.buf_used += req->length - UVCG_REQUEST_HEADER_LEN; + + if (buf->bytesused == video->queue.buf_used || !buf->sg) { + video->queue.buf_used = 0; + buf->state = UVC_BUF_STATE_DONE; + buf->offset = 0; + uvcg_queue_next_buffer(&video->queue, buf); + video->fid ^= UVC_STREAM_FID; + } +} + +static void uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video, struct uvc_buffer *buf) { @@ -145,7 +210,8 @@ static int uvcg_video_ep_queue(struct uvc_video *video, struct usb_request *req) static void uvc_video_complete(struct usb_ep *ep, struct usb_request *req) { - struct uvc_video *video = req->context; + struct uvc_request *ureq = req->context; + struct uvc_video *video = ureq->video; struct uvc_video_queue *queue = &video->queue; unsigned long flags; @@ -177,16 +243,23 @@ uvc_video_free_requests(struct uvc_video *video) { unsigned int i; - for (i = 0; i < UVC_NUM_REQUESTS; ++i) { - if (video->req[i]) { - usb_ep_free_request(video->ep, video->req[i]); - video->req[i] = NULL; - } + if (video->ureq) { + for (i = 0; i < video->uvc_num_requests; ++i) { + sg_free_table(&video->ureq[i].sgt); + + if (video->ureq[i].req) { + usb_ep_free_request(video->ep, video->ureq[i].req); + video->ureq[i].req = NULL; + } - if (video->req_buffer[i]) { - kfree(video->req_buffer[i]); - video->req_buffer[i] = NULL; + if (video->ureq[i].req_buffer) { + kfree(video->ureq[i].req_buffer); + video->ureq[i].req_buffer = NULL; + } } + + kfree(video->ureq); + video->ureq = NULL; } INIT_LIST_HEAD(&video->req_free); @@ -207,21 +280,30 @@ uvc_video_alloc_requests(struct uvc_video *video) * max_t(unsigned int, video->ep->maxburst, 1) * (video->ep->mult); - for (i = 0; i < UVC_NUM_REQUESTS; ++i) { - video->req_buffer[i] = kmalloc(req_size, GFP_KERNEL); - if (video->req_buffer[i] == NULL) - goto error; + video->ureq = kcalloc(video->uvc_num_requests, sizeof(struct uvc_request), GFP_KERNEL); + if (video->ureq == NULL) + return -ENOMEM; - video->req[i] = usb_ep_alloc_request(video->ep, GFP_KERNEL); - if (video->req[i] == NULL) + for (i = 0; i < video->uvc_num_requests; ++i) { + video->ureq[i].req_buffer = kmalloc(req_size, GFP_KERNEL); + if (video->ureq[i].req_buffer == NULL) goto error; - video->req[i]->buf = video->req_buffer[i]; - video->req[i]->length = 0; - video->req[i]->complete = uvc_video_complete; - video->req[i]->context = video; + video->ureq[i].req = usb_ep_alloc_request(video->ep, GFP_KERNEL); + if (video->ureq[i].req == NULL) + goto error; - list_add_tail(&video->req[i]->list, &video->req_free); + video->ureq[i].req->buf = video->ureq[i].req_buffer; + video->ureq[i].req->length = 0; + video->ureq[i].req->complete = uvc_video_complete; + video->ureq[i].req->context = &video->ureq[i]; + video->ureq[i].video = video; + + list_add_tail(&video->ureq[i].req->list, &video->req_free); + /* req_size/PAGE_SIZE + 1 for overruns and + 1 for header */ + sg_alloc_table(&video->ureq[i].sgt, + DIV_ROUND_UP(req_size - 2, PAGE_SIZE) + 2, + GFP_KERNEL); } video->req_size = req_size; @@ -278,6 +360,19 @@ static void uvcg_video_pump(struct work_struct *work) video->encode(req, video, buf); + /* With usb3 we have more requests. This will decrease the + * interrupt load to a quarter but also catches the corner + * cases, which needs to be handled */ + if (list_empty(&video->req_free) || + buf->state == UVC_BUF_STATE_DONE || + !(video->req_int_count % + DIV_ROUND_UP(video->uvc_num_requests, 4))) { + video->req_int_count = 0; + req->no_interrupt = 0; + } else { + req->no_interrupt = 1; + } + /* Queue the USB request */ ret = uvcg_video_ep_queue(video, req); spin_unlock_irqrestore(&queue->irqlock, flags); @@ -286,6 +381,7 @@ static void uvcg_video_pump(struct work_struct *work) uvcg_queue_cancel(queue, 0); break; } + video->req_int_count++; } spin_lock_irqsave(&video->req_lock, flags); @@ -312,9 +408,9 @@ int uvcg_video_enable(struct uvc_video *video, int enable) cancel_work_sync(&video->pump); uvcg_queue_cancel(&video->queue, 0); - for (i = 0; i < UVC_NUM_REQUESTS; ++i) - if (video->req[i]) - usb_ep_dequeue(video->ep, video->req[i]); + for (i = 0; i < video->uvc_num_requests; ++i) + if (video->ureq && video->ureq[i].req) + usb_ep_dequeue(video->ep, video->ureq[i].req); uvc_video_free_requests(video); uvcg_queue_enable(&video->queue, 0); @@ -331,7 +427,10 @@ int uvcg_video_enable(struct uvc_video *video, int enable) video->encode = uvc_video_encode_bulk; video->payload_size = 0; } else - video->encode = uvc_video_encode_isoc; + video->encode = video->queue.use_sg ? + uvc_video_encode_isoc_sg : uvc_video_encode_isoc; + + video->req_int_count = 0; schedule_work(&video->pump); @@ -355,8 +454,8 @@ int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc) video->imagesize = 320 * 240 * 2; /* Initialize the video buffers queue. */ - uvcg_queue_init(&video->queue, V4L2_BUF_TYPE_VIDEO_OUTPUT, - &video->mutex); + uvcg_queue_init(&video->queue, uvc->v4l2_dev.dev->parent, + V4L2_BUF_TYPE_VIDEO_OUTPUT, &video->mutex); return 0; } diff --git a/drivers/usb/gadget/function/uvc_video.h b/drivers/usb/gadget/function/uvc_video.h index 03adeefa343b..9bf19475f6f9 100644 --- a/drivers/usb/gadget/function/uvc_video.h +++ b/drivers/usb/gadget/function/uvc_video.h @@ -12,6 +12,8 @@ #ifndef __UVC_VIDEO_H__ #define __UVC_VIDEO_H__ +#define UVCG_REQUEST_HEADER_LEN 2 + struct uvc_video; int uvcg_video_enable(struct uvc_video *video, int enable); diff --git a/drivers/usb/gadget/legacy/Kconfig b/drivers/usb/gadget/legacy/Kconfig index 11dd6e8adc8a..de6668e58481 100644 --- a/drivers/usb/gadget/legacy/Kconfig +++ b/drivers/usb/gadget/legacy/Kconfig @@ -502,6 +502,7 @@ config USB_G_WEBCAM tristate "USB Webcam Gadget" depends on VIDEO_V4L2 select USB_LIBCOMPOSITE + select VIDEOBUF2_DMA_SG select VIDEOBUF2_VMALLOC select USB_F_UVC help diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index cd8e2737947b..539220d7f5b6 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -1214,8 +1214,8 @@ dev_release (struct inode *inode, struct file *fd) static __poll_t ep0_poll (struct file *fd, poll_table *wait) { - struct dev_data *dev = fd->private_data; - __poll_t mask = 0; + struct dev_data *dev = fd->private_data; + __poll_t mask = 0; if (dev->state <= STATE_DEV_OPENED) return DEFAULT_POLLMASK; diff --git a/drivers/usb/gadget/legacy/printer.c b/drivers/usb/gadget/legacy/printer.c index 2cd389575084..ed762ba9b629 100644 --- a/drivers/usb/gadget/legacy/printer.c +++ b/drivers/usb/gadget/legacy/printer.c @@ -50,6 +50,7 @@ MODULE_PARM_DESC(iPNPstring, "MFG:linux;MDL:g_printer;CLS:PRINTER;SN:1;"); /* Number of requests to allocate per endpoint, not used for ep0. */ static unsigned qlen = 10; module_param(qlen, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(qlen, "The number of 8k buffers to use per endpoint"); #define QLEN qlen diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/aspeed-vhub/core.c index d11d3d14313f..7a635c499777 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/core.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/core.c @@ -5,11 +5,6 @@ * core.c - Top level support * * Copyright 2017 IBM Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/kernel.h> diff --git a/drivers/usb/gadget/udc/aspeed-vhub/dev.c b/drivers/usb/gadget/udc/aspeed-vhub/dev.c index d268306a7bfe..d918e8b2af3c 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/dev.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/dev.c @@ -5,11 +5,6 @@ * dev.c - Individual device/gadget management (ie, a port = a gadget) * * Copyright 2017 IBM Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/kernel.h> diff --git a/drivers/usb/gadget/udc/aspeed-vhub/ep0.c b/drivers/usb/gadget/udc/aspeed-vhub/ep0.c index 022b777b85f8..74ea36c19b1e 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/ep0.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/ep0.c @@ -5,11 +5,6 @@ * ep0.c - Endpoint 0 handling * * Copyright 2017 IBM Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/kernel.h> diff --git a/drivers/usb/gadget/udc/aspeed-vhub/epn.c b/drivers/usb/gadget/udc/aspeed-vhub/epn.c index cb164c615e6f..917892ca8753 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/epn.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/epn.c @@ -5,11 +5,6 @@ * epn.c - Generic endpoints management * * Copyright 2017 IBM Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/kernel.h> diff --git a/drivers/usb/gadget/udc/aspeed-vhub/hub.c b/drivers/usb/gadget/udc/aspeed-vhub/hub.c index 5c7dea5e0ff1..b9960fdd8a51 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/hub.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/hub.c @@ -5,11 +5,6 @@ * hub.c - virtual hub handling * * Copyright 2017 IBM Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/kernel.h> diff --git a/drivers/usb/gadget/udc/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c index eede5cedacb4..d9ad9adf7348 100644 --- a/drivers/usb/gadget/udc/at91_udc.c +++ b/drivers/usb/gadget/udc/at91_udc.c @@ -1876,7 +1876,9 @@ static int at91udc_probe(struct platform_device *pdev) clk_disable(udc->iclk); /* request UDC and maybe VBUS irqs */ - udc->udp_irq = platform_get_irq(pdev, 0); + udc->udp_irq = retval = platform_get_irq(pdev, 0); + if (retval < 0) + goto err_unprepare_iclk; retval = devm_request_irq(dev, udc->udp_irq, at91_udc_irq, 0, driver_name, udc); if (retval) { diff --git a/drivers/usb/gadget/udc/bdc/bdc_cmd.c b/drivers/usb/gadget/udc/bdc/bdc_cmd.c index 995f79c79f96..67887316a1a6 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_cmd.c +++ b/drivers/usb/gadget/udc/bdc/bdc_cmd.c @@ -153,7 +153,6 @@ int bdc_config_ep(struct bdc *bdc, struct bdc_ep *ep) si = clamp_val(si, 1, 16) - 1; mps = usb_endpoint_maxp(desc); - mps &= 0x7ff; param2 |= mps << MP_SHIFT; param2 |= usb_endpoint_type(desc) << EPT_SHIFT; diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c index 0bef6b3f049b..fa1a3908ec3b 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_core.c +++ b/drivers/usb/gadget/udc/bdc/bdc_core.c @@ -488,27 +488,14 @@ static int bdc_probe(struct platform_device *pdev) int irq; u32 temp; struct device *dev = &pdev->dev; - struct clk *clk; int phy_num; dev_dbg(dev, "%s()\n", __func__); - clk = devm_clk_get_optional(dev, "sw_usbd"); - if (IS_ERR(clk)) - return PTR_ERR(clk); - - ret = clk_prepare_enable(clk); - if (ret) { - dev_err(dev, "could not enable clock\n"); - return ret; - } - bdc = devm_kzalloc(dev, sizeof(*bdc), GFP_KERNEL); if (!bdc) return -ENOMEM; - bdc->clk = clk; - bdc->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(bdc->regs)) return PTR_ERR(bdc->regs); @@ -545,10 +532,20 @@ static int bdc_probe(struct platform_device *pdev) } } + bdc->clk = devm_clk_get_optional(dev, "sw_usbd"); + if (IS_ERR(bdc->clk)) + return PTR_ERR(bdc->clk); + + ret = clk_prepare_enable(bdc->clk); + if (ret) { + dev_err(dev, "could not enable clock\n"); + return ret; + } + ret = bdc_phy_init(bdc); if (ret) { dev_err(bdc->dev, "BDC phy init failure:%d\n", ret); - return ret; + goto disable_clk; } temp = bdc_readl(bdc->regs, BDC_BDCCAP1); @@ -560,7 +557,8 @@ static int bdc_probe(struct platform_device *pdev) if (ret) { dev_err(dev, "No suitable DMA config available, abort\n"); - return -ENOTSUPP; + ret = -ENOTSUPP; + goto phycleanup; } dev_dbg(dev, "Using 32-bit address\n"); } @@ -580,6 +578,8 @@ cleanup: bdc_hw_exit(bdc); phycleanup: bdc_phy_exit(bdc); +disable_clk: + clk_disable_unprepare(bdc->clk); return ret; } diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index b7f0b1ebaaa8..14fdf918ecfe 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -1003,6 +1003,25 @@ int usb_gadget_ep_match_desc(struct usb_gadget *gadget, } EXPORT_SYMBOL_GPL(usb_gadget_ep_match_desc); +/** + * usb_gadget_check_config - checks if the UDC can support the binded + * configuration + * @gadget: controller to check the USB configuration + * + * Ensure that a UDC is able to support the requested resources by a + * configuration, and that there are no resource limitations, such as + * internal memory allocated to all requested endpoints. + * + * Returns zero on success, else a negative errno. + */ +int usb_gadget_check_config(struct usb_gadget *gadget) +{ + if (gadget->ops->check_config) + return gadget->ops->check_config(gadget); + return 0; +} +EXPORT_SYMBOL_GPL(usb_gadget_check_config); + /* ------------------------------------------------------------------------- */ static void usb_gadget_state_work(struct work_struct *work) diff --git a/drivers/usb/gadget/udc/mv_u3d_core.c b/drivers/usb/gadget/udc/mv_u3d_core.c index ce3d7a3eb7e3..a1057ddfbda3 100644 --- a/drivers/usb/gadget/udc/mv_u3d_core.c +++ b/drivers/usb/gadget/udc/mv_u3d_core.c @@ -1921,14 +1921,6 @@ static int mv_u3d_probe(struct platform_device *dev) goto err_get_irq; } u3d->irq = r->start; - if (request_irq(u3d->irq, mv_u3d_irq, - IRQF_SHARED, driver_name, u3d)) { - u3d->irq = 0; - dev_err(&dev->dev, "Request irq %d for u3d failed\n", - u3d->irq); - retval = -ENODEV; - goto err_request_irq; - } /* initialize gadget structure */ u3d->gadget.ops = &mv_u3d_ops; /* usb_gadget_ops */ @@ -1941,6 +1933,15 @@ static int mv_u3d_probe(struct platform_device *dev) mv_u3d_eps_init(u3d); + if (request_irq(u3d->irq, mv_u3d_irq, + IRQF_SHARED, driver_name, u3d)) { + u3d->irq = 0; + dev_err(&dev->dev, "Request irq %d for u3d failed\n", + u3d->irq); + retval = -ENODEV; + goto err_request_irq; + } + /* external vbus detection */ if (u3d->vbus) { u3d->clock_gating = 1; @@ -1964,8 +1965,8 @@ static int mv_u3d_probe(struct platform_device *dev) err_unregister: free_irq(u3d->irq, u3d); -err_request_irq: err_get_irq: +err_request_irq: kfree(u3d->status_req); err_alloc_status_req: kfree(u3d->eps); diff --git a/drivers/usb/gadget/udc/pxa25x_udc.c b/drivers/usb/gadget/udc/pxa25x_udc.c index 69ef1e669d0c..a09ec1d826b2 100644 --- a/drivers/usb/gadget/udc/pxa25x_udc.c +++ b/drivers/usb/gadget/udc/pxa25x_udc.c @@ -1093,7 +1093,7 @@ static void pxa25x_ep_fifo_flush(struct usb_ep *_ep) } -static struct usb_ep_ops pxa25x_ep_ops = { +static const struct usb_ep_ops pxa25x_ep_ops = { .enable = pxa25x_ep_enable, .disable = pxa25x_ep_disable, diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c index f1b35a39d1ba..57d417a7c3e0 100644 --- a/drivers/usb/gadget/udc/renesas_usb3.c +++ b/drivers/usb/gadget/udc/renesas_usb3.c @@ -2707,10 +2707,15 @@ static const struct renesas_usb3_priv renesas_usb3_priv_r8a77990 = { static const struct of_device_id usb3_of_match[] = { { + .compatible = "renesas,r8a774c0-usb3-peri", + .data = &renesas_usb3_priv_r8a77990, + }, { .compatible = "renesas,r8a7795-usb3-peri", .data = &renesas_usb3_priv_gen3, - }, - { + }, { + .compatible = "renesas,r8a77990-usb3-peri", + .data = &renesas_usb3_priv_r8a77990, + }, { .compatible = "renesas,rcar-gen3-usb3-peri", .data = &renesas_usb3_priv_gen3, }, @@ -2720,17 +2725,9 @@ MODULE_DEVICE_TABLE(of, usb3_of_match); static const struct soc_device_attribute renesas_usb3_quirks_match[] = { { - .soc_id = "r8a774c0", - .data = &renesas_usb3_priv_r8a77990, - }, - { .soc_id = "r8a7795", .revision = "ES1.*", .data = &renesas_usb3_priv_r8a7795_es1, }, - { - .soc_id = "r8a77990", - .data = &renesas_usb3_priv_r8a77990, - }, { /* sentinel */ }, }; diff --git a/drivers/usb/gadget/udc/s3c2410_udc.c b/drivers/usb/gadget/udc/s3c2410_udc.c index 179777cb699f..e3931da24277 100644 --- a/drivers/usb/gadget/udc/s3c2410_udc.c +++ b/drivers/usb/gadget/udc/s3c2410_udc.c @@ -1784,6 +1784,10 @@ static int s3c2410_udc_probe(struct platform_device *pdev) s3c2410_udc_reinit(udc); irq_usbd = platform_get_irq(pdev, 0); + if (irq_usbd < 0) { + retval = irq_usbd; + goto err_udc_clk; + } /* irq setup after old hardware state is cleaned up */ retval = request_irq(irq_usbd, s3c2410_udc_irq, diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c index c0ca7144e512..43f1b0d461c1 100644 --- a/drivers/usb/gadget/udc/tegra-xudc.c +++ b/drivers/usb/gadget/udc/tegra-xudc.c @@ -1610,7 +1610,7 @@ static void tegra_xudc_ep_context_setup(struct tegra_xudc_ep *ep) u16 maxpacket, maxburst = 0, esit = 0; u32 val; - maxpacket = usb_endpoint_maxp(desc) & 0x7ff; + maxpacket = usb_endpoint_maxp(desc); if (xudc->gadget.speed == USB_SPEED_SUPER) { if (!usb_endpoint_xfer_control(desc)) maxburst = comp_desc->bMaxBurst; @@ -1621,7 +1621,7 @@ static void tegra_xudc_ep_context_setup(struct tegra_xudc_ep *ep) (usb_endpoint_xfer_int(desc) || usb_endpoint_xfer_isoc(desc))) { if (xudc->gadget.speed == USB_SPEED_HIGH) { - maxburst = (usb_endpoint_maxp(desc) >> 11) & 0x3; + maxburst = usb_endpoint_maxp_mult(desc) - 1; if (maxburst == 0x3) { dev_warn(xudc->dev, "invalid endpoint maxburst\n"); diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index df9428f1dc5e..c4736d1d020c 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -187,15 +187,6 @@ config USB_EHCI_PCI depends on USB_PCI default y -config USB_EHCI_HCD_PMC_MSP - tristate "EHCI support for on-chip PMC MSP71xx USB controller" - depends on MSP_HAS_USB - select USB_EHCI_BIG_ENDIAN_DESC - select USB_EHCI_BIG_ENDIAN_MMIO - help - Enables support for the onchip USB controller on the PMC_MSP7100 Family SoC's. - If unsure, say N. - config XPS_USB_HCD_XILINX bool "Use Xilinx usb host EHCI controller core" depends on (PPC32 || MICROBLAZE) diff --git a/drivers/usb/host/ehci-brcm.c b/drivers/usb/host/ehci-brcm.c index 3e0ebe8cc649..d3626bfa966b 100644 --- a/drivers/usb/host/ehci-brcm.c +++ b/drivers/usb/host/ehci-brcm.c @@ -108,10 +108,9 @@ static int ehci_brcm_reset(struct usb_hcd *hcd) /* * SWLINUX-1705: Avoid OUT packet underflows during high memory * bus usage - * port_status[0x0f] = Broadcom-proprietary USB_EHCI_INSNREG00 @ 0x90 */ - ehci_writel(ehci, 0x00800040, &ehci->regs->port_status[0x10]); - ehci_writel(ehci, 0x00000001, &ehci->regs->port_status[0x12]); + ehci_writel(ehci, 0x00800040, &ehci->regs->brcm_insnreg[1]); + ehci_writel(ehci, 0x00000001, &ehci->regs->brcm_insnreg[3]); return ehci_setup(hcd); } @@ -223,11 +222,9 @@ static int __maybe_unused ehci_brcm_resume(struct device *dev) /* * SWLINUX-1705: Avoid OUT packet underflows during high memory * bus usage - * port_status[0x0f] = Broadcom-proprietary USB_EHCI_INSNREG00 - * @ 0x90 */ - ehci_writel(ehci, 0x00800040, &ehci->regs->port_status[0x10]); - ehci_writel(ehci, 0x00000001, &ehci->regs->port_status[0x12]); + ehci_writel(ehci, 0x00800040, &ehci->regs->brcm_insnreg[1]); + ehci_writel(ehci, 0x00000001, &ehci->regs->brcm_insnreg[3]); ehci_resume(hcd, false); diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 10b0365f3439..6bdc6d6bf74d 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1296,11 +1296,6 @@ MODULE_LICENSE ("GPL"); #define XILINX_OF_PLATFORM_DRIVER ehci_hcd_xilinx_of_driver #endif -#ifdef CONFIG_USB_EHCI_HCD_PMC_MSP -#include "ehci-pmcmsp.c" -#define PLATFORM_DRIVER ehci_hcd_msp_driver -#endif - #ifdef CONFIG_SPARC_LEON #include "ehci-grlib.c" #define PLATFORM_DRIVER ehci_grlib_driver diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c index cffdc8d01b2a..8fd27249ad25 100644 --- a/drivers/usb/host/ehci-mv.c +++ b/drivers/usb/host/ehci-mv.c @@ -42,26 +42,25 @@ struct ehci_hcd_mv { int (*set_vbus)(unsigned int vbus); }; -static void ehci_clock_enable(struct ehci_hcd_mv *ehci_mv) +static int mv_ehci_enable(struct ehci_hcd_mv *ehci_mv) { - clk_prepare_enable(ehci_mv->clk); -} + int retval; -static void ehci_clock_disable(struct ehci_hcd_mv *ehci_mv) -{ - clk_disable_unprepare(ehci_mv->clk); -} + retval = clk_prepare_enable(ehci_mv->clk); + if (retval) + return retval; -static int mv_ehci_enable(struct ehci_hcd_mv *ehci_mv) -{ - ehci_clock_enable(ehci_mv); - return phy_init(ehci_mv->phy); + retval = phy_init(ehci_mv->phy); + if (retval) + clk_disable_unprepare(ehci_mv->clk); + + return retval; } static void mv_ehci_disable(struct ehci_hcd_mv *ehci_mv) { phy_exit(ehci_mv->phy); - ehci_clock_disable(ehci_mv); + clk_disable_unprepare(ehci_mv->clk); } static int mv_ehci_reset(struct usb_hcd *hcd) diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index a319b1df3011..3626758b3e2a 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -264,8 +264,11 @@ static int ehci_orion_drv_probe(struct platform_device *pdev) * the clock does not exists. */ priv->clk = devm_clk_get(&pdev->dev, NULL); - if (!IS_ERR(priv->clk)) - clk_prepare_enable(priv->clk); + if (!IS_ERR(priv->clk)) { + err = clk_prepare_enable(priv->clk); + if (err) + goto err_put_hcd; + } priv->phy = devm_phy_optional_get(&pdev->dev, "usb"); if (IS_ERR(priv->phy)) { @@ -311,6 +314,7 @@ static int ehci_orion_drv_probe(struct platform_device *pdev) err_dis_clk: if (!IS_ERR(priv->clk)) clk_disable_unprepare(priv->clk); +err_put_hcd: usb_put_hcd(hcd); err: dev_err(&pdev->dev, "init %s fail, %d\n", diff --git a/drivers/usb/host/ehci-pmcmsp.c b/drivers/usb/host/ehci-pmcmsp.c deleted file mode 100644 index 5fb92b956cc7..000000000000 --- a/drivers/usb/host/ehci-pmcmsp.c +++ /dev/null @@ -1,328 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * PMC MSP EHCI (Host Controller Driver) for USB. - * - * (C) Copyright 2006-2010 PMC-Sierra Inc - */ - -/* includes */ -#include <linux/platform_device.h> -#include <linux/gpio.h> -#include <linux/usb.h> -#include <msp_usb.h> - -/* stream disable*/ -#define USB_CTRL_MODE_STREAM_DISABLE 0x10 - -/* threshold */ -#define USB_CTRL_FIFO_THRESH 0x00300000 - -/* register offset for usb_mode */ -#define USB_EHCI_REG_USB_MODE 0x68 - -/* register offset for usb fifo */ -#define USB_EHCI_REG_USB_FIFO 0x24 - -/* register offset for usb status */ -#define USB_EHCI_REG_USB_STATUS 0x44 - -/* serial/parallel transceiver */ -#define USB_EHCI_REG_BIT_STAT_STS (1<<29) - -/* TWI USB0 host device pin */ -#define MSP_PIN_USB0_HOST_DEV 49 - -/* TWI USB1 host device pin */ -#define MSP_PIN_USB1_HOST_DEV 50 - - -static void usb_hcd_tdi_set_mode(struct ehci_hcd *ehci) -{ - u8 *base; - u8 *statreg; - u8 *fiforeg; - u32 val; - struct ehci_regs *reg_base = ehci->regs; - - /* get register base */ - base = (u8 *)reg_base + USB_EHCI_REG_USB_MODE; - statreg = (u8 *)reg_base + USB_EHCI_REG_USB_STATUS; - fiforeg = (u8 *)reg_base + USB_EHCI_REG_USB_FIFO; - - /* Disable controller mode stream */ - val = ehci_readl(ehci, (u32 *)base); - ehci_writel(ehci, (val | USB_CTRL_MODE_STREAM_DISABLE), - (u32 *)base); - - /* clear STS to select parallel transceiver interface */ - val = ehci_readl(ehci, (u32 *)statreg); - val = val & ~USB_EHCI_REG_BIT_STAT_STS; - ehci_writel(ehci, val, (u32 *)statreg); - - /* write to set the proper fifo threshold */ - ehci_writel(ehci, USB_CTRL_FIFO_THRESH, (u32 *)fiforeg); - - /* set TWI GPIO USB_HOST_DEV pin high */ - gpio_direction_output(MSP_PIN_USB0_HOST_DEV, 1); -} - -/* called during probe() after chip reset completes */ -static int ehci_msp_setup(struct usb_hcd *hcd) -{ - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - int retval; - - ehci->big_endian_mmio = 1; - ehci->big_endian_desc = 1; - - ehci->caps = hcd->regs; - hcd->has_tt = 1; - - retval = ehci_setup(hcd); - if (retval) - return retval; - - usb_hcd_tdi_set_mode(ehci); - - return retval; -} - - -/* configure so an HC device and id are always provided - * always called with process context; sleeping is OK - */ - -static int usb_hcd_msp_map_regs(struct mspusb_device *dev) -{ - struct resource *res; - struct platform_device *pdev = &dev->dev; - u32 res_len; - int retval; - - /* MAB register space */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (res == NULL) - return -ENOMEM; - res_len = resource_size(res); - if (!request_mem_region(res->start, res_len, "mab regs")) - return -EBUSY; - - dev->mab_regs = ioremap(res->start, res_len); - if (dev->mab_regs == NULL) { - retval = -ENOMEM; - goto err1; - } - - /* MSP USB register space */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 2); - if (res == NULL) { - retval = -ENOMEM; - goto err2; - } - res_len = resource_size(res); - if (!request_mem_region(res->start, res_len, "usbid regs")) { - retval = -EBUSY; - goto err2; - } - dev->usbid_regs = ioremap(res->start, res_len); - if (dev->usbid_regs == NULL) { - retval = -ENOMEM; - goto err3; - } - - return 0; -err3: - res = platform_get_resource(pdev, IORESOURCE_MEM, 2); - res_len = resource_size(res); - release_mem_region(res->start, res_len); -err2: - iounmap(dev->mab_regs); -err1: - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - res_len = resource_size(res); - release_mem_region(res->start, res_len); - dev_err(&pdev->dev, "Failed to map non-EHCI regs.\n"); - return retval; -} - -/** - * usb_hcd_msp_probe - initialize PMC MSP-based HCDs - * @driver: Pointer to hc driver instance - * @dev: USB controller to probe - * - * Context: task context, might sleep - * - * Allocates basic resources for this USB host controller, and - * then invokes the start() method for the HCD associated with it - * through the hotplug entry's driver_data. - */ -int usb_hcd_msp_probe(const struct hc_driver *driver, - struct platform_device *dev) -{ - int retval; - struct usb_hcd *hcd; - struct resource *res; - struct ehci_hcd *ehci ; - - hcd = usb_create_hcd(driver, &dev->dev, "pmcmsp"); - if (!hcd) - return -ENOMEM; - - res = platform_get_resource(dev, IORESOURCE_MEM, 0); - if (res == NULL) { - pr_debug("No IOMEM resource info for %s.\n", dev->name); - retval = -ENOMEM; - goto err1; - } - hcd->rsrc_start = res->start; - hcd->rsrc_len = resource_size(res); - if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, dev->name)) { - retval = -EBUSY; - goto err1; - } - hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); - if (!hcd->regs) { - pr_debug("ioremap failed"); - retval = -ENOMEM; - goto err2; - } - - res = platform_get_resource(dev, IORESOURCE_IRQ, 0); - if (res == NULL) { - dev_err(&dev->dev, "No IRQ resource info for %s.\n", dev->name); - retval = -ENOMEM; - goto err3; - } - - /* Map non-EHCI register spaces */ - retval = usb_hcd_msp_map_regs(to_mspusb_device(dev)); - if (retval != 0) - goto err3; - - ehci = hcd_to_ehci(hcd); - ehci->big_endian_mmio = 1; - ehci->big_endian_desc = 1; - - - retval = usb_add_hcd(hcd, res->start, IRQF_SHARED); - if (retval == 0) { - device_wakeup_enable(hcd->self.controller); - return 0; - } - - usb_remove_hcd(hcd); -err3: - iounmap(hcd->regs); -err2: - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); -err1: - usb_put_hcd(hcd); - - return retval; -} - - - -/** - * usb_hcd_msp_remove - shutdown processing for PMC MSP-based HCDs - * @hcd: USB Host Controller being removed - * - * Context: task context, might sleep - * - * Reverses the effect of usb_hcd_msp_probe(), first invoking - * the HCD's stop() method. It is always called from a thread - * context, normally "rmmod", "apmd", or something similar. - * - * may be called without controller electrically present - * may be called with controller, bus, and devices active - */ -static void usb_hcd_msp_remove(struct usb_hcd *hcd) -{ - usb_remove_hcd(hcd); - iounmap(hcd->regs); - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); - usb_put_hcd(hcd); -} - -static const struct hc_driver ehci_msp_hc_driver = { - .description = hcd_name, - .product_desc = "PMC MSP EHCI", - .hcd_priv_size = sizeof(struct ehci_hcd), - - /* - * generic hardware linkage - */ - .irq = ehci_irq, - .flags = HCD_MEMORY | HCD_DMA | HCD_USB2 | HCD_BH, - - /* - * basic lifecycle operations - */ - .reset = ehci_msp_setup, - .shutdown = ehci_shutdown, - .start = ehci_run, - .stop = ehci_stop, - - /* - * managing i/o requests and associated device resources - */ - .urb_enqueue = ehci_urb_enqueue, - .urb_dequeue = ehci_urb_dequeue, - .endpoint_disable = ehci_endpoint_disable, - .endpoint_reset = ehci_endpoint_reset, - - /* - * scheduling support - */ - .get_frame_number = ehci_get_frame, - - /* - * root hub support - */ - .hub_status_data = ehci_hub_status_data, - .hub_control = ehci_hub_control, - .bus_suspend = ehci_bus_suspend, - .bus_resume = ehci_bus_resume, - .relinquish_port = ehci_relinquish_port, - .port_handed_over = ehci_port_handed_over, - - .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, -}; - -static int ehci_hcd_msp_drv_probe(struct platform_device *pdev) -{ - int ret; - - pr_debug("In ehci_hcd_msp_drv_probe"); - - if (usb_disabled()) - return -ENODEV; - - gpio_request(MSP_PIN_USB0_HOST_DEV, "USB0_HOST_DEV_GPIO"); - - ret = usb_hcd_msp_probe(&ehci_msp_hc_driver, pdev); - - return ret; -} - -static int ehci_hcd_msp_drv_remove(struct platform_device *pdev) -{ - struct usb_hcd *hcd = platform_get_drvdata(pdev); - - usb_hcd_msp_remove(hcd); - - /* free TWI GPIO USB_HOST_DEV pin */ - gpio_free(MSP_PIN_USB0_HOST_DEV); - - return 0; -} - -MODULE_ALIAS("pmcmsp-ehci"); - -static struct platform_driver ehci_hcd_msp_driver = { - .probe = ehci_hcd_msp_drv_probe, - .remove = ehci_hcd_msp_drv_remove, - .driver = { - .name = "pmcmsp-ehci", - }, -}; diff --git a/drivers/usb/host/fotg210-hcd.c b/drivers/usb/host/fotg210-hcd.c index 05fb8d97cf02..4b02ace09f3d 100644 --- a/drivers/usb/host/fotg210-hcd.c +++ b/drivers/usb/host/fotg210-hcd.c @@ -1858,9 +1858,11 @@ static struct fotg210_qh *fotg210_qh_alloc(struct fotg210_hcd *fotg210, qh = kzalloc(sizeof(*qh), GFP_ATOMIC); if (!qh) goto done; - qh->hw = dma_pool_zalloc(fotg210->qh_pool, flags, &dma); + qh->hw = (struct fotg210_qh_hw *) + dma_pool_alloc(fotg210->qh_pool, flags, &dma); if (!qh->hw) goto fail; + memset(qh->hw, 0, sizeof(*qh->hw)); qh->qh_dma = dma; INIT_LIST_HEAD(&qh->qtd_list); @@ -2510,11 +2512,6 @@ retry_xacterr: return count; } -/* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */ -#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03)) -/* ... and packet size, for any kind of endpoint descriptor */ -#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff) - /* reverse of qh_urb_transaction: free a list of TDs. * used for cleanup after errors, before HC sees an URB's TDs. */ @@ -2600,7 +2597,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 = max_packet(usb_maxpacket(urb->dev, urb->pipe, !is_input)); + maxpacket = usb_maxpacket(urb->dev, urb->pipe, !is_input); /* * buffer gets wrapped in one or more qtds; @@ -2714,9 +2711,11 @@ static struct fotg210_qh *qh_make(struct fotg210_hcd *fotg210, struct urb *urb, gfp_t flags) { struct fotg210_qh *qh = fotg210_qh_alloc(fotg210, flags); + struct usb_host_endpoint *ep; u32 info1 = 0, info2 = 0; int is_input, type; int maxp = 0; + int mult; struct usb_tt *tt = urb->dev->tt; struct fotg210_qh_hw *hw; @@ -2731,14 +2730,15 @@ static struct fotg210_qh *qh_make(struct fotg210_hcd *fotg210, struct urb *urb, is_input = usb_pipein(urb->pipe); type = usb_pipetype(urb->pipe); - maxp = usb_maxpacket(urb->dev, urb->pipe, !is_input); + ep = usb_pipe_endpoint(urb->dev, urb->pipe); + maxp = usb_endpoint_maxp(&ep->desc); + mult = usb_endpoint_maxp_mult(&ep->desc); /* 1024 byte maxpacket is a hardware ceiling. High bandwidth * acts like up to 3KB, but is built from smaller packets. */ - if (max_packet(maxp) > 1024) { - fotg210_dbg(fotg210, "bogus qh maxpacket %d\n", - max_packet(maxp)); + if (maxp > 1024) { + fotg210_dbg(fotg210, "bogus qh maxpacket %d\n", maxp); goto done; } @@ -2752,8 +2752,7 @@ static struct fotg210_qh *qh_make(struct fotg210_hcd *fotg210, struct urb *urb, */ if (type == PIPE_INTERRUPT) { qh->usecs = NS_TO_US(usb_calc_bus_time(USB_SPEED_HIGH, - is_input, 0, - hb_mult(maxp) * max_packet(maxp))); + is_input, 0, mult * maxp)); qh->start = NO_FRAME; if (urb->dev->speed == USB_SPEED_HIGH) { @@ -2790,7 +2789,7 @@ static struct fotg210_qh *qh_make(struct fotg210_hcd *fotg210, struct urb *urb, think_time = tt ? tt->think_time : 0; qh->tt_usecs = NS_TO_US(think_time + usb_calc_bus_time(urb->dev->speed, - is_input, 0, max_packet(maxp))); + is_input, 0, maxp)); qh->period = urb->interval; if (qh->period > fotg210->periodic_size) { qh->period = fotg210->periodic_size; @@ -2853,11 +2852,11 @@ static struct fotg210_qh *qh_make(struct fotg210_hcd *fotg210, struct urb *urb, * to help them do so. So now people expect to use * such nonconformant devices with Linux too; sigh. */ - info1 |= max_packet(maxp) << 16; + info1 |= maxp << 16; info2 |= (FOTG210_TUNE_MULT_HS << 30); } else { /* PIPE_INTERRUPT */ - info1 |= max_packet(maxp) << 16; - info2 |= hb_mult(maxp) << 30; + info1 |= maxp << 16; + info2 |= mult << 30; } break; default: @@ -3927,6 +3926,7 @@ static void iso_stream_init(struct fotg210_hcd *fotg210, int is_input; long bandwidth; unsigned multi; + struct usb_host_endpoint *ep; /* * this might be a "high bandwidth" highspeed endpoint, @@ -3934,14 +3934,14 @@ static void iso_stream_init(struct fotg210_hcd *fotg210, */ epnum = usb_pipeendpoint(pipe); is_input = usb_pipein(pipe) ? USB_DIR_IN : 0; - maxp = usb_maxpacket(dev, pipe, !is_input); + ep = usb_pipe_endpoint(dev, pipe); + maxp = usb_endpoint_maxp(&ep->desc); if (is_input) buf1 = (1 << 11); else buf1 = 0; - maxp = max_packet(maxp); - multi = hb_mult(maxp); + multi = usb_endpoint_maxp_mult(&ep->desc); buf1 |= maxp; maxp *= multi; @@ -4112,7 +4112,7 @@ static int itd_urb_transaction(struct fotg210_iso_stream *stream, } else { alloc_itd: spin_unlock_irqrestore(&fotg210->lock, flags); - itd = dma_pool_zalloc(fotg210->itd_pool, mem_flags, + itd = dma_pool_alloc(fotg210->itd_pool, mem_flags, &itd_dma); spin_lock_irqsave(&fotg210->lock, flags); if (!itd) { @@ -4122,6 +4122,7 @@ alloc_itd: } } + memset(itd, 0, sizeof(*itd)); itd->itd_dma = itd_dma; list_add(&itd->itd_list, &sched->td_list); } @@ -4462,13 +4463,12 @@ static bool itd_complete(struct fotg210_hcd *fotg210, struct fotg210_itd *itd) /* HC need not update length with this error */ if (!(t & FOTG210_ISOC_BABBLE)) { - desc->actual_length = - fotg210_itdlen(urb, desc, t); + desc->actual_length = FOTG210_ITD_LENGTH(t); urb->actual_length += desc->actual_length; } } else if (likely((t & FOTG210_ISOC_ACTIVE) == 0)) { desc->status = 0; - desc->actual_length = fotg210_itdlen(urb, desc, t); + desc->actual_length = FOTG210_ITD_LENGTH(t); urb->actual_length += desc->actual_length; } else { /* URB was too late */ diff --git a/drivers/usb/host/fotg210.h b/drivers/usb/host/fotg210.h index 0a91061a0551..0781442b7a24 100644 --- a/drivers/usb/host/fotg210.h +++ b/drivers/usb/host/fotg210.h @@ -683,11 +683,6 @@ static inline unsigned fotg210_read_frame_index(struct fotg210_hcd *fotg210) return fotg210_readl(fotg210, &fotg210->regs->frame_index); } -#define fotg210_itdlen(urb, desc, t) ({ \ - usb_pipein((urb)->pipe) ? \ - (desc)->length - FOTG210_ITD_LENGTH(t) : \ - FOTG210_ITD_LENGTH(t); \ -}) /*-------------------------------------------------------------------------*/ #endif /* __LINUX_FOTG210_H */ diff --git a/drivers/usb/host/ohci-spear.c b/drivers/usb/host/ohci-spear.c index 5cc05449281c..b4cd9e6c72fd 100644 --- a/drivers/usb/host/ohci-spear.c +++ b/drivers/usb/host/ohci-spear.c @@ -84,7 +84,7 @@ static int spear_ohci_hcd_drv_probe(struct platform_device *pdev) clk_prepare_enable(sohci_p->clk); - retval = usb_add_hcd(hcd, platform_get_irq(pdev, 0), 0); + retval = usb_add_hcd(hcd, irq, 0); if (retval == 0) { device_wakeup_enable(hcd->self.controller); return retval; diff --git a/drivers/usb/host/ohci-tmio.c b/drivers/usb/host/ohci-tmio.c index 7f857bad9e95..08ec2ab0d95a 100644 --- a/drivers/usb/host/ohci-tmio.c +++ b/drivers/usb/host/ohci-tmio.c @@ -202,6 +202,9 @@ static int ohci_hcd_tmio_drv_probe(struct platform_device *dev) if (!cell) return -EINVAL; + if (irq < 0) + return irq; + hcd = usb_create_hcd(&ohci_tmio_hc_driver, &dev->dev, dev_name(&dev->dev)); if (!hcd) { ret = -ENOMEM; diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c index 2c0fda57869e..dc832ddf7033 100644 --- a/drivers/usb/host/xhci-debugfs.c +++ b/drivers/usb/host/xhci-debugfs.c @@ -198,12 +198,13 @@ static void xhci_ring_dump_segment(struct seq_file *s, int i; dma_addr_t dma; union xhci_trb *trb; + char str[XHCI_MSG_MAX]; for (i = 0; i < TRBS_PER_SEGMENT; i++) { trb = &seg->trbs[i]; dma = seg->dma + i * sizeof(*trb); seq_printf(s, "%pad: %s\n", &dma, - xhci_decode_trb(le32_to_cpu(trb->generic.field[0]), + xhci_decode_trb(str, XHCI_MSG_MAX, le32_to_cpu(trb->generic.field[0]), le32_to_cpu(trb->generic.field[1]), le32_to_cpu(trb->generic.field[2]), le32_to_cpu(trb->generic.field[3]))); @@ -260,11 +261,13 @@ static int xhci_slot_context_show(struct seq_file *s, void *unused) struct xhci_slot_ctx *slot_ctx; struct xhci_slot_priv *priv = s->private; struct xhci_virt_device *dev = priv->dev; + char str[XHCI_MSG_MAX]; xhci = hcd_to_xhci(bus_to_hcd(dev->udev->bus)); slot_ctx = xhci_get_slot_ctx(xhci, dev->out_ctx); seq_printf(s, "%pad: %s\n", &dev->out_ctx->dma, - xhci_decode_slot_context(le32_to_cpu(slot_ctx->dev_info), + xhci_decode_slot_context(str, + le32_to_cpu(slot_ctx->dev_info), le32_to_cpu(slot_ctx->dev_info2), le32_to_cpu(slot_ctx->tt_info), le32_to_cpu(slot_ctx->dev_state))); @@ -280,6 +283,7 @@ static int xhci_endpoint_context_show(struct seq_file *s, void *unused) struct xhci_ep_ctx *ep_ctx; struct xhci_slot_priv *priv = s->private; struct xhci_virt_device *dev = priv->dev; + char str[XHCI_MSG_MAX]; xhci = hcd_to_xhci(bus_to_hcd(dev->udev->bus)); @@ -287,7 +291,8 @@ static int xhci_endpoint_context_show(struct seq_file *s, void *unused) ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, ep_index); dma = dev->out_ctx->dma + (ep_index + 1) * CTX_SIZE(xhci->hcc_params); seq_printf(s, "%pad: %s\n", &dma, - xhci_decode_ep_context(le32_to_cpu(ep_ctx->ep_info), + xhci_decode_ep_context(str, + le32_to_cpu(ep_ctx->ep_info), le32_to_cpu(ep_ctx->ep_info2), le64_to_cpu(ep_ctx->deq), le32_to_cpu(ep_ctx->tx_info))); @@ -341,9 +346,10 @@ static int xhci_portsc_show(struct seq_file *s, void *unused) { struct xhci_port *port = s->private; u32 portsc; + char str[XHCI_MSG_MAX]; portsc = readl(port->addr); - seq_printf(s, "%s\n", xhci_decode_portsc(portsc)); + seq_printf(s, "%s\n", xhci_decode_portsc(str, portsc)); return 0; } diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 151e93c4bd57..a3f875eea751 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -1667,7 +1667,8 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) status = 1; } if (!status && !reset_change) { - xhci_dbg(xhci, "%s: stopping port polling.\n", __func__); + xhci_dbg(xhci, "%s: stopping usb%d port polling\n", + __func__, hcd->self.busnum); clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); } spin_unlock_irqrestore(&xhci->lock, flags); @@ -1699,7 +1700,8 @@ int xhci_bus_suspend(struct usb_hcd *hcd) if (bus_state->resuming_ports || /* USB2 */ bus_state->port_remote_wakeup) { /* USB3 */ spin_unlock_irqrestore(&xhci->lock, flags); - xhci_dbg(xhci, "suspend failed because a port is resuming\n"); + xhci_dbg(xhci, "usb%d bus suspend to fail because a port is resuming\n", + hcd->self.busnum); return -EBUSY; } } diff --git a/drivers/usb/host/xhci-mtk-sch.c b/drivers/usb/host/xhci-mtk-sch.c index cffcaf4dfa9f..134f4789bd89 100644 --- a/drivers/usb/host/xhci-mtk-sch.c +++ b/drivers/usb/host/xhci-mtk-sch.c @@ -80,7 +80,7 @@ decode_ep(struct usb_host_endpoint *ep, enum usb_device_speed speed) interval /= 1000; } - snprintf(buf, DBG_BUF_EN, "%s ep%d%s %s, mpkt:%d, interval:%d/%d%s\n", + snprintf(buf, DBG_BUF_EN, "%s ep%d%s %s, mpkt:%d, interval:%d/%d%s", usb_speed_string(speed), usb_endpoint_num(epd), usb_endpoint_dir_in(epd) ? "in" : "out", usb_ep_type_string(usb_endpoint_type(epd)), @@ -129,6 +129,10 @@ get_bw_info(struct xhci_hcd_mtk *mtk, struct usb_device *udev, int bw_index; virt_dev = xhci->devs[udev->slot_id]; + if (!virt_dev->real_port) { + WARN_ONCE(1, "%s invalid real_port\n", dev_name(&udev->dev)); + return NULL; + } if (udev->speed >= USB_SPEED_SUPER) { if (usb_endpoint_dir_out(&ep->desc)) @@ -236,14 +240,20 @@ static void drop_tt(struct usb_device *udev) } } -static struct mu3h_sch_ep_info *create_sch_ep(struct usb_device *udev, - struct usb_host_endpoint *ep, struct xhci_ep_ctx *ep_ctx) +static struct mu3h_sch_ep_info * +create_sch_ep(struct xhci_hcd_mtk *mtk, struct usb_device *udev, + struct usb_host_endpoint *ep, struct xhci_ep_ctx *ep_ctx) { struct mu3h_sch_ep_info *sch_ep; + struct mu3h_sch_bw_info *bw_info; struct mu3h_sch_tt *tt = NULL; u32 len_bw_budget_table; size_t mem_size; + bw_info = get_bw_info(mtk, udev, ep); + if (!bw_info) + return ERR_PTR(-ENODEV); + if (is_fs_or_ls(udev->speed)) len_bw_budget_table = TT_MICROFRAMES_MAX; else if ((udev->speed >= USB_SPEED_SUPER) @@ -266,11 +276,13 @@ static struct mu3h_sch_ep_info *create_sch_ep(struct usb_device *udev, } } + sch_ep->bw_info = bw_info; sch_ep->sch_tt = tt; sch_ep->ep = ep; sch_ep->speed = udev->speed; INIT_LIST_HEAD(&sch_ep->endpoint); INIT_LIST_HEAD(&sch_ep->tt_endpoint); + INIT_HLIST_NODE(&sch_ep->hentry); return sch_ep; } @@ -297,6 +309,7 @@ static void setup_sch_info(struct xhci_ep_ctx *ep_ctx, CTX_TO_MAX_ESIT_PAYLOAD(le32_to_cpu(ep_ctx->tx_info)); sch_ep->esit = get_esit(ep_ctx); + sch_ep->num_esit = XHCI_MTK_MAX_ESIT / sch_ep->esit; sch_ep->ep_type = ep_type; sch_ep->maxpkt = maxpkt; sch_ep->offset = 0; @@ -401,19 +414,16 @@ static void setup_sch_info(struct xhci_ep_ctx *ep_ctx, static u32 get_max_bw(struct mu3h_sch_bw_info *sch_bw, struct mu3h_sch_ep_info *sch_ep, u32 offset) { - u32 num_esit; u32 max_bw = 0; u32 bw; - int i; - int j; + int i, j, k; - num_esit = XHCI_MTK_MAX_ESIT / sch_ep->esit; - for (i = 0; i < num_esit; i++) { + for (i = 0; i < sch_ep->num_esit; i++) { u32 base = offset + i * sch_ep->esit; for (j = 0; j < sch_ep->num_budget_microframes; j++) { - bw = sch_bw->bus_bw[base + j] + - sch_ep->bw_budget_table[j]; + k = XHCI_MTK_BW_INDEX(base + j); + bw = sch_bw->bus_bw[k] + sch_ep->bw_budget_table[j]; if (bw > max_bw) max_bw = bw; } @@ -424,21 +434,17 @@ static u32 get_max_bw(struct mu3h_sch_bw_info *sch_bw, static void update_bus_bw(struct mu3h_sch_bw_info *sch_bw, struct mu3h_sch_ep_info *sch_ep, bool used) { - u32 num_esit; u32 base; - int i; - int j; + int i, j, k; - num_esit = XHCI_MTK_MAX_ESIT / sch_ep->esit; - for (i = 0; i < num_esit; i++) { + for (i = 0; i < sch_ep->num_esit; i++) { base = sch_ep->offset + i * sch_ep->esit; for (j = 0; j < sch_ep->num_budget_microframes; j++) { + k = XHCI_MTK_BW_INDEX(base + j); if (used) - sch_bw->bus_bw[base + j] += - sch_ep->bw_budget_table[j]; + sch_bw->bus_bw[k] += sch_ep->bw_budget_table[j]; else - sch_bw->bus_bw[base + j] -= - sch_ep->bw_budget_table[j]; + sch_bw->bus_bw[k] -= sch_ep->bw_budget_table[j]; } } } @@ -446,20 +452,20 @@ static void update_bus_bw(struct mu3h_sch_bw_info *sch_bw, static int check_fs_bus_bw(struct mu3h_sch_ep_info *sch_ep, int offset) { struct mu3h_sch_tt *tt = sch_ep->sch_tt; - u32 num_esit, tmp; + u32 tmp; int base; - int i, j; + int i, j, k; - num_esit = XHCI_MTK_MAX_ESIT / sch_ep->esit; - for (i = 0; i < num_esit; i++) { + for (i = 0; i < sch_ep->num_esit; i++) { base = offset + i * sch_ep->esit; /* * Compared with hs bus, no matter what ep type, * the hub will always delay one uframe to send data */ - for (j = 0; j < sch_ep->cs_count; j++) { - tmp = tt->fs_bus_bw[base + j] + sch_ep->bw_cost_per_microframe; + for (j = 0; j < sch_ep->num_budget_microframes; j++) { + k = XHCI_MTK_BW_INDEX(base + j); + tmp = tt->fs_bus_bw[k] + sch_ep->bw_budget_table[j]; if (tmp > FS_PAYLOAD_MAX) return -ESCH_BW_OVERFLOW; } @@ -533,22 +539,19 @@ static int check_sch_tt(struct mu3h_sch_ep_info *sch_ep, u32 offset) static void update_sch_tt(struct mu3h_sch_ep_info *sch_ep, bool used) { struct mu3h_sch_tt *tt = sch_ep->sch_tt; - u32 base, num_esit; - int bw_updated; - int i, j; - - num_esit = XHCI_MTK_MAX_ESIT / sch_ep->esit; - - if (used) - bw_updated = sch_ep->bw_cost_per_microframe; - else - bw_updated = -sch_ep->bw_cost_per_microframe; + u32 base; + int i, j, k; - for (i = 0; i < num_esit; i++) { + for (i = 0; i < sch_ep->num_esit; i++) { base = sch_ep->offset + i * sch_ep->esit; - for (j = 0; j < sch_ep->cs_count; j++) - tt->fs_bus_bw[base + j] += bw_updated; + for (j = 0; j < sch_ep->num_budget_microframes; j++) { + k = XHCI_MTK_BW_INDEX(base + j); + if (used) + tt->fs_bus_bw[k] += sch_ep->bw_budget_table[j]; + else + tt->fs_bus_bw[k] -= sch_ep->bw_budget_table[j]; + } } if (used) @@ -570,25 +573,9 @@ static int load_ep_bw(struct mu3h_sch_bw_info *sch_bw, return 0; } -static u32 get_esit_boundary(struct mu3h_sch_ep_info *sch_ep) -{ - u32 boundary = sch_ep->esit; - - if (sch_ep->sch_tt) { /* LS/FS with TT */ - /* tune for CS */ - if (sch_ep->ep_type != ISOC_OUT_EP) - boundary++; - else if (boundary > 1) /* normally esit >= 8 for FS/LS */ - boundary--; - } - - return boundary; -} - -static int check_sch_bw(struct mu3h_sch_bw_info *sch_bw, - struct mu3h_sch_ep_info *sch_ep) +static int check_sch_bw(struct mu3h_sch_ep_info *sch_ep) { - const u32 esit_boundary = get_esit_boundary(sch_ep); + struct mu3h_sch_bw_info *sch_bw = sch_ep->bw_info; const u32 bw_boundary = get_bw_boundary(sch_ep->speed); u32 offset; u32 worst_bw; @@ -605,9 +592,6 @@ static int check_sch_bw(struct mu3h_sch_bw_info *sch_bw, if (ret) continue; - if ((offset + sch_ep->num_budget_microframes) > esit_boundary) - break; - worst_bw = get_max_bw(sch_bw, sch_ep, offset); if (worst_bw > bw_boundary) continue; @@ -633,23 +617,26 @@ static int check_sch_bw(struct mu3h_sch_bw_info *sch_bw, return load_ep_bw(sch_bw, sch_ep, true); } -static void destroy_sch_ep(struct usb_device *udev, - struct mu3h_sch_bw_info *sch_bw, struct mu3h_sch_ep_info *sch_ep) +static void destroy_sch_ep(struct xhci_hcd_mtk *mtk, struct usb_device *udev, + struct mu3h_sch_ep_info *sch_ep) { /* only release ep bw check passed by check_sch_bw() */ if (sch_ep->allocated) - load_ep_bw(sch_bw, sch_ep, false); + load_ep_bw(sch_ep->bw_info, sch_ep, false); if (sch_ep->sch_tt) drop_tt(udev); list_del(&sch_ep->endpoint); + hlist_del(&sch_ep->hentry); kfree(sch_ep); } -static bool need_bw_sch(struct usb_host_endpoint *ep, - enum usb_device_speed speed, int has_tt) +static bool need_bw_sch(struct usb_device *udev, + struct usb_host_endpoint *ep) { + bool has_tt = udev->tt && udev->tt->hub->parent; + /* only for periodic endpoints */ if (usb_endpoint_xfer_control(&ep->desc) || usb_endpoint_xfer_bulk(&ep->desc)) @@ -660,7 +647,7 @@ static bool need_bw_sch(struct usb_host_endpoint *ep, * a TT are also ignored, root-hub will schedule them directly, * but need set @bpkts field of endpoint context to 1. */ - if (is_fs_or_ls(speed) && !has_tt) + if (is_fs_or_ls(udev->speed) && !has_tt) return false; /* skip endpoint with zero maxpkt */ @@ -675,7 +662,6 @@ int xhci_mtk_sch_init(struct xhci_hcd_mtk *mtk) struct xhci_hcd *xhci = hcd_to_xhci(mtk->hcd); struct mu3h_sch_bw_info *sch_array; int num_usb_bus; - int i; /* ss IN and OUT are separated */ num_usb_bus = xhci->usb3_rhub.num_ports * 2 + xhci->usb2_rhub.num_ports; @@ -684,12 +670,10 @@ int xhci_mtk_sch_init(struct xhci_hcd_mtk *mtk) if (sch_array == NULL) return -ENOMEM; - for (i = 0; i < num_usb_bus; i++) - INIT_LIST_HEAD(&sch_array[i].bw_ep_list); - mtk->sch_array = sch_array; INIT_LIST_HEAD(&mtk->bw_ep_chk_list); + hash_init(mtk->sch_ep_hash); return 0; } @@ -713,9 +697,7 @@ static int add_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev, ep_index = xhci_get_endpoint_index(&ep->desc); ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->in_ctx, ep_index); - xhci_dbg(xhci, "%s %s\n", __func__, decode_ep(ep, udev->speed)); - - if (!need_bw_sch(ep, udev->speed, !!virt_dev->tt_info)) { + if (!need_bw_sch(udev, ep)) { /* * set @bpkts to 1 if it is LS or FS periodic endpoint, and its * device does not connected through an external HS hub @@ -727,13 +709,16 @@ static int add_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev, return 0; } - sch_ep = create_sch_ep(udev, ep, ep_ctx); + xhci_dbg(xhci, "%s %s\n", __func__, decode_ep(ep, udev->speed)); + + sch_ep = create_sch_ep(mtk, udev, ep, ep_ctx); if (IS_ERR_OR_NULL(sch_ep)) return -ENOMEM; setup_sch_info(ep_ctx, sch_ep); list_add_tail(&sch_ep->endpoint, &mtk->bw_ep_chk_list); + hash_add(mtk->sch_ep_hash, &sch_ep->hentry, (unsigned long)ep); return 0; } @@ -743,22 +728,18 @@ static void drop_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev, { struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd); struct xhci_hcd *xhci = hcd_to_xhci(hcd); - struct xhci_virt_device *virt_dev; - struct mu3h_sch_bw_info *sch_bw; - struct mu3h_sch_ep_info *sch_ep, *tmp; - - virt_dev = xhci->devs[udev->slot_id]; - - xhci_dbg(xhci, "%s %s\n", __func__, decode_ep(ep, udev->speed)); + struct mu3h_sch_ep_info *sch_ep; + struct hlist_node *hn; - if (!need_bw_sch(ep, udev->speed, !!virt_dev->tt_info)) + if (!need_bw_sch(udev, ep)) return; - sch_bw = get_bw_info(mtk, udev, ep); + xhci_err(xhci, "%s %s\n", __func__, decode_ep(ep, udev->speed)); - list_for_each_entry_safe(sch_ep, tmp, &sch_bw->bw_ep_list, endpoint) { + hash_for_each_possible_safe(mtk->sch_ep_hash, sch_ep, + hn, hentry, (unsigned long)ep) { if (sch_ep->ep == ep) { - destroy_sch_ep(udev, sch_bw, sch_ep); + destroy_sch_ep(mtk, udev, sch_ep); break; } } @@ -769,30 +750,22 @@ int xhci_mtk_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd); struct xhci_hcd *xhci = hcd_to_xhci(hcd); struct xhci_virt_device *virt_dev = xhci->devs[udev->slot_id]; - struct mu3h_sch_bw_info *sch_bw; - struct mu3h_sch_ep_info *sch_ep, *tmp; + struct mu3h_sch_ep_info *sch_ep; int ret; xhci_dbg(xhci, "%s() udev %s\n", __func__, dev_name(&udev->dev)); list_for_each_entry(sch_ep, &mtk->bw_ep_chk_list, endpoint) { - sch_bw = get_bw_info(mtk, udev, sch_ep->ep); + struct xhci_ep_ctx *ep_ctx; + struct usb_host_endpoint *ep = sch_ep->ep; + unsigned int ep_index = xhci_get_endpoint_index(&ep->desc); - ret = check_sch_bw(sch_bw, sch_ep); + ret = check_sch_bw(sch_ep); if (ret) { xhci_err(xhci, "Not enough bandwidth! (%s)\n", sch_error_string(-ret)); return -ENOSPC; } - } - - list_for_each_entry_safe(sch_ep, tmp, &mtk->bw_ep_chk_list, endpoint) { - struct xhci_ep_ctx *ep_ctx; - struct usb_host_endpoint *ep = sch_ep->ep; - unsigned int ep_index = xhci_get_endpoint_index(&ep->desc); - - sch_bw = get_bw_info(mtk, udev, ep); - list_move_tail(&sch_ep->endpoint, &sch_bw->bw_ep_list); ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->in_ctx, ep_index); ep_ctx->reserved[0] = cpu_to_le32(EP_BPKTS(sch_ep->pkts) @@ -806,22 +779,23 @@ int xhci_mtk_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) sch_ep->offset, sch_ep->repeat); } - return xhci_check_bandwidth(hcd, udev); + ret = xhci_check_bandwidth(hcd, udev); + if (!ret) + INIT_LIST_HEAD(&mtk->bw_ep_chk_list); + + return ret; } void xhci_mtk_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) { struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd); struct xhci_hcd *xhci = hcd_to_xhci(hcd); - struct mu3h_sch_bw_info *sch_bw; struct mu3h_sch_ep_info *sch_ep, *tmp; xhci_dbg(xhci, "%s() udev %s\n", __func__, dev_name(&udev->dev)); - list_for_each_entry_safe(sch_ep, tmp, &mtk->bw_ep_chk_list, endpoint) { - sch_bw = get_bw_info(mtk, udev, sch_ep->ep); - destroy_sch_ep(udev, sch_bw, sch_ep); - } + list_for_each_entry_safe(sch_ep, tmp, &mtk->bw_ep_chk_list, endpoint) + destroy_sch_ep(mtk, udev, sch_ep); xhci_reset_bandwidth(hcd, udev); } diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c index 2548976bcf05..c53f6f276d5c 100644 --- a/drivers/usb/host/xhci-mtk.c +++ b/drivers/usb/host/xhci-mtk.c @@ -56,6 +56,27 @@ /* u2_phy_pll register */ #define CTRL_U2_FORCE_PLL_STB BIT(28) +/* xHCI CSR */ +#define LS_EOF_CFG 0x930 +#define LSEOF_OFFSET 0x89 + +#define FS_EOF_CFG 0x934 +#define FSEOF_OFFSET 0x2e + +#define SS_GEN1_EOF_CFG 0x93c +#define SSG1EOF_OFFSET 0x78 + +#define HFCNTR_CFG 0x944 +#define ITP_DELTA_CLK (0xa << 1) +#define ITP_DELTA_CLK_MASK GENMASK(5, 1) +#define FRMCNT_LEV1_RANG (0x12b << 8) +#define FRMCNT_LEV1_RANG_MASK GENMASK(19, 8) + +#define SS_GEN2_EOF_CFG 0x990 +#define SSG2EOF_OFFSET 0x3c + +#define XSEOF_OFFSET_MASK GENMASK(11, 0) + /* usb remote wakeup registers in syscon */ /* mt8173 etc */ @@ -86,6 +107,46 @@ enum ssusb_uwk_vers { SSUSB_UWK_V1_2, /* specific revision 1.2 */ }; +/* + * MT8195 has 4 controllers, the controller1~3's default SOF/ITP interval + * is calculated from the frame counter clock 24M, but in fact, the clock + * is 48M, add workaround for it. + */ +static void xhci_mtk_set_frame_interval(struct xhci_hcd_mtk *mtk) +{ + struct device *dev = mtk->dev; + struct usb_hcd *hcd = mtk->hcd; + u32 value; + + if (!of_device_is_compatible(dev->of_node, "mediatek,mt8195-xhci")) + return; + + value = readl(hcd->regs + HFCNTR_CFG); + value &= ~(ITP_DELTA_CLK_MASK | FRMCNT_LEV1_RANG_MASK); + value |= (ITP_DELTA_CLK | FRMCNT_LEV1_RANG); + writel(value, hcd->regs + HFCNTR_CFG); + + value = readl(hcd->regs + LS_EOF_CFG); + value &= ~XSEOF_OFFSET_MASK; + value |= LSEOF_OFFSET; + writel(value, hcd->regs + LS_EOF_CFG); + + value = readl(hcd->regs + FS_EOF_CFG); + value &= ~XSEOF_OFFSET_MASK; + value |= FSEOF_OFFSET; + writel(value, hcd->regs + FS_EOF_CFG); + + value = readl(hcd->regs + SS_GEN1_EOF_CFG); + value &= ~XSEOF_OFFSET_MASK; + value |= SSG1EOF_OFFSET; + writel(value, hcd->regs + SS_GEN1_EOF_CFG); + + value = readl(hcd->regs + SS_GEN2_EOF_CFG); + value &= ~XSEOF_OFFSET_MASK; + value |= SSG2EOF_OFFSET; + writel(value, hcd->regs + SS_GEN2_EOF_CFG); +} + static int xhci_mtk_host_enable(struct xhci_hcd_mtk *mtk) { struct mu3c_ippc_regs __iomem *ippc = mtk->ippc_regs; @@ -115,8 +176,11 @@ static int xhci_mtk_host_enable(struct xhci_hcd_mtk *mtk) writel(value, &ippc->u3_ctrl_p[i]); } - /* power on and enable all u2 ports */ + /* power on and enable all u2 ports except skipped ones */ for (i = 0; i < mtk->num_u2_ports; i++) { + if (BIT(i) & mtk->u2p_dis_msk) + continue; + value = readl(&ippc->u2_ctrl_p[i]); value &= ~(CTRL_U2_PORT_PDN | CTRL_U2_PORT_DIS); value |= CTRL_U2_PORT_HOST_SEL; @@ -163,8 +227,11 @@ static int xhci_mtk_host_disable(struct xhci_hcd_mtk *mtk) writel(value, &ippc->u3_ctrl_p[i]); } - /* power down all u2 ports */ + /* power down all u2 ports except skipped ones */ for (i = 0; i < mtk->num_u2_ports; i++) { + if (BIT(i) & mtk->u2p_dis_msk) + continue; + value = readl(&ippc->u2_ctrl_p[i]); value |= CTRL_U2_PORT_PDN; writel(value, &ippc->u2_ctrl_p[i]); @@ -361,6 +428,9 @@ static int xhci_mtk_setup(struct usb_hcd *hcd) ret = xhci_mtk_ssusb_config(mtk); if (ret) return ret; + + /* workaround only for mt8195 */ + xhci_mtk_set_frame_interval(mtk); } ret = xhci_gen_setup(hcd, xhci_mtk_quirks); @@ -444,6 +514,8 @@ static int xhci_mtk_probe(struct platform_device *pdev) /* optional property, ignore the error if it does not exist */ of_property_read_u32(node, "mediatek,u3p-dis-msk", &mtk->u3p_dis_msk); + of_property_read_u32(node, "mediatek,u2p-dis-msk", + &mtk->u2p_dis_msk); ret = usb_wakeup_of_property_parse(mtk, node); if (ret) { @@ -569,7 +641,7 @@ disable_ldos: xhci_mtk_ldos_disable(mtk); disable_pm: - pm_runtime_put_sync_autosuspend(dev); + pm_runtime_put_noidle(dev); pm_runtime_disable(dev); return ret; } @@ -704,6 +776,7 @@ static const struct dev_pm_ops xhci_mtk_pm_ops = { static const struct of_device_id mtk_xhci_of_match[] = { { .compatible = "mediatek,mt8173-xhci"}, + { .compatible = "mediatek,mt8195-xhci"}, { .compatible = "mediatek,mtk-xhci"}, { }, }; diff --git a/drivers/usb/host/xhci-mtk.h b/drivers/usb/host/xhci-mtk.h index ace432356c41..4b1ea89f959a 100644 --- a/drivers/usb/host/xhci-mtk.h +++ b/drivers/usb/host/xhci-mtk.h @@ -10,18 +10,23 @@ #define _XHCI_MTK_H_ #include <linux/clk.h> +#include <linux/hashtable.h> #include "xhci.h" #define BULK_CLKS_NUM 5 +/* support at most 64 ep, use 32 size hash table */ +#define SCH_EP_HASH_BITS 5 + /** * To simplify scheduler algorithm, set a upper limit for ESIT, * if a synchromous ep's ESIT is larger than @XHCI_MTK_MAX_ESIT, * round down to the limit value, that means allocating more * bandwidth to it. */ -#define XHCI_MTK_MAX_ESIT 64 +#define XHCI_MTK_MAX_ESIT (1 << 6) +#define XHCI_MTK_BW_INDEX(x) ((x) & (XHCI_MTK_MAX_ESIT - 1)) /** * @fs_bus_bw: array to keep track of bandwidth already used for FS @@ -36,25 +41,26 @@ struct mu3h_sch_tt { * struct mu3h_sch_bw_info: schedule information for bandwidth domain * * @bus_bw: array to keep track of bandwidth already used at each uframes - * @bw_ep_list: eps in the bandwidth domain * * treat a HS root port as a bandwidth domain, but treat a SS root port as * two bandwidth domains, one for IN eps and another for OUT eps. */ struct mu3h_sch_bw_info { u32 bus_bw[XHCI_MTK_MAX_ESIT]; - struct list_head bw_ep_list; }; /** * struct mu3h_sch_ep_info: schedule information for endpoint * * @esit: unit is 125us, equal to 2 << Interval field in ep-context + * @num_esit: number of @esit in a period * @num_budget_microframes: number of continuous uframes * (@repeat==1) scheduled within the interval * @bw_cost_per_microframe: bandwidth cost per microframe + * @hentry: hash table entry * @endpoint: linked into bandwidth domain which it belongs to * @tt_endpoint: linked into mu3h_sch_tt's list which it belongs to + * @bw_info: bandwidth domain which this endpoint belongs * @sch_tt: mu3h_sch_tt linked into * @ep_type: endpoint type * @maxpkt: max packet size of endpoint @@ -79,10 +85,13 @@ struct mu3h_sch_bw_info { */ struct mu3h_sch_ep_info { u32 esit; + u32 num_esit; u32 num_budget_microframes; u32 bw_cost_per_microframe; struct list_head endpoint; + struct hlist_node hentry; struct list_head tt_endpoint; + struct mu3h_sch_bw_info *bw_info; struct mu3h_sch_tt *sch_tt; u32 ep_type; u32 maxpkt; @@ -135,9 +144,11 @@ struct xhci_hcd_mtk { struct usb_hcd *hcd; struct mu3h_sch_bw_info *sch_array; struct list_head bw_ep_chk_list; + DECLARE_HASHTABLE(sch_ep_hash, SCH_EP_HASH_BITS); struct mu3c_ippc_regs __iomem *ippc_regs; int num_u2_ports; int num_u3_ports; + int u2p_dis_msk; int u3p_dis_msk; struct regulator *vusb33; struct regulator *vbus; diff --git a/drivers/usb/host/xhci-pci-renesas.c b/drivers/usb/host/xhci-pci-renesas.c index ef5e91a5542d..52599d96634f 100644 --- a/drivers/usb/host/xhci-pci-renesas.c +++ b/drivers/usb/host/xhci-pci-renesas.c @@ -599,7 +599,7 @@ int renesas_xhci_check_request_fw(struct pci_dev *pdev, err = renesas_fw_check_running(pdev); /* Continue ahead, if the firmware is already running. */ - if (err == 0) + if (!err) return 0; /* no firmware interface available */ @@ -631,9 +631,4 @@ exit: } EXPORT_SYMBOL_GPL(renesas_xhci_check_request_fw); -void renesas_xhci_pci_exit(struct pci_dev *dev) -{ -} -EXPORT_SYMBOL_GPL(renesas_xhci_pci_exit); - MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 1c9a7957c45c..2c9f25ca8edd 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -449,8 +449,6 @@ static void xhci_pci_remove(struct pci_dev *dev) struct xhci_hcd *xhci; xhci = hcd_to_xhci(pci_get_drvdata(dev)); - if (xhci->quirks & XHCI_RENESAS_FW_QUIRK) - renesas_xhci_pci_exit(dev); xhci->xhc_state |= XHCI_STATE_REMOVING; diff --git a/drivers/usb/host/xhci-pci.h b/drivers/usb/host/xhci-pci.h index acd7cf0a1706..cb9a8f331a44 100644 --- a/drivers/usb/host/xhci-pci.h +++ b/drivers/usb/host/xhci-pci.h @@ -7,7 +7,6 @@ #if IS_ENABLED(CONFIG_USB_XHCI_PCI_RENESAS) int renesas_xhci_check_request_fw(struct pci_dev *dev, const struct pci_device_id *id); -void renesas_xhci_pci_exit(struct pci_dev *dev); #else static int renesas_xhci_check_request_fw(struct pci_dev *dev, @@ -16,8 +15,6 @@ static int renesas_xhci_check_request_fw(struct pci_dev *dev, return 0; } -static void renesas_xhci_pci_exit(struct pci_dev *dev) { }; - #endif struct xhci_driver_data { diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c index 1bc4fe7b8c75..9888ba7d85b6 100644 --- a/drivers/usb/host/xhci-rcar.c +++ b/drivers/usb/host/xhci-rcar.c @@ -134,6 +134,13 @@ static int xhci_rcar_download_firmware(struct usb_hcd *hcd) const struct soc_device_attribute *attr; const char *firmware_name; + /* + * According to the datasheet, "Upon the completion of FW Download, + * there is no need to write or reload FW". + */ + if (readl(regs + RCAR_USB3_DL_CTRL) & RCAR_USB3_DL_CTRL_FW_SUCCESS) + return 0; + attr = soc_device_match(rcar_quirks_match); if (attr) quirks = (uintptr_t)attr->data; diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 8fea44bbc266..e676749f543b 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -830,9 +830,14 @@ static void xhci_giveback_invalidated_tds(struct xhci_virt_ep *ep) ring = xhci_urb_to_transfer_ring(ep->xhci, td->urb); - if (td->cancel_status == TD_CLEARED) + if (td->cancel_status == TD_CLEARED) { + xhci_dbg(ep->xhci, "%s: Giveback cancelled URB %p TD\n", + __func__, td->urb); xhci_td_cleanup(ep->xhci, td, ring, td->status); - + } else { + xhci_dbg(ep->xhci, "%s: Keep cancelled URB %p TD as cancel_status is %d\n", + __func__, td->urb, td->cancel_status); + } if (ep->xhci->xhc_state & XHCI_STATE_DYING) return; } @@ -850,6 +855,10 @@ static int xhci_reset_halted_ep(struct xhci_hcd *xhci, unsigned int slot_id, goto done; } + xhci_dbg(xhci, "%s-reset ep %u, slot %u\n", + (reset_type == EP_HARD_RESET) ? "Hard" : "Soft", + ep_index, slot_id); + ret = xhci_queue_reset_ep(xhci, command, slot_id, ep_index, reset_type); done: if (ret) @@ -883,7 +892,8 @@ static int xhci_handle_halted_endpoint(struct xhci_hcd *xhci, } if (ep->ep_state & EP_HALTED) { - xhci_dbg(xhci, "Reset ep command already pending\n"); + xhci_dbg(xhci, "Reset ep command for ep_index %d already pending\n", + ep->ep_index); return 0; } @@ -922,9 +932,10 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep) list_for_each_entry_safe(td, tmp_td, &ep->cancelled_td_list, cancelled_td_list) { xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, - "Removing canceled TD starting at 0x%llx (dma).", - (unsigned long long)xhci_trb_virt_to_dma( - td->start_seg, td->first_trb)); + "Removing canceled TD starting at 0x%llx (dma) in stream %u URB %p", + (unsigned long long)xhci_trb_virt_to_dma( + td->start_seg, td->first_trb), + td->urb->stream_id, td->urb); list_del_init(&td->td_list); ring = xhci_urb_to_transfer_ring(xhci, td->urb); if (!ring) { @@ -942,17 +953,21 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep) td->urb->stream_id); hw_deq &= ~0xf; - if (td->cancel_status == TD_HALTED) { - cached_td = td; - } else if (trb_in_td(xhci, td->start_seg, td->first_trb, - td->last_trb, hw_deq, false)) { + if (td->cancel_status == TD_HALTED || + trb_in_td(xhci, td->start_seg, td->first_trb, td->last_trb, hw_deq, false)) { switch (td->cancel_status) { case TD_CLEARED: /* TD is already no-op */ case TD_CLEARING_CACHE: /* set TR deq command already queued */ break; case TD_DIRTY: /* TD is cached, clear it */ case TD_HALTED: - /* FIXME stream case, several stopped rings */ + td->cancel_status = TD_CLEARING_CACHE; + if (cached_td) + /* FIXME stream case, several stopped rings */ + xhci_dbg(xhci, + "Move dq past stream %u URB %p instead of stream %u URB %p\n", + td->urb->stream_id, td->urb, + cached_td->urb->stream_id, cached_td->urb); cached_td = td; break; } @@ -961,18 +976,24 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep) td->cancel_status = TD_CLEARED; } } - if (cached_td) { - cached_td->cancel_status = TD_CLEARING_CACHE; - err = xhci_move_dequeue_past_td(xhci, slot_id, ep->ep_index, - cached_td->urb->stream_id, - cached_td); - /* Failed to move past cached td, try just setting it noop */ - if (err) { - td_to_noop(xhci, ring, cached_td, false); - cached_td->cancel_status = TD_CLEARED; + /* If there's no need to move the dequeue pointer then we're done */ + if (!cached_td) + return 0; + + err = xhci_move_dequeue_past_td(xhci, slot_id, ep->ep_index, + cached_td->urb->stream_id, + cached_td); + if (err) { + /* Failed to move past cached td, just set cached TDs to no-op */ + list_for_each_entry_safe(td, tmp_td, &ep->cancelled_td_list, cancelled_td_list) { + if (td->cancel_status != TD_CLEARING_CACHE) + continue; + xhci_dbg(xhci, "Failed to clear cancelled cached URB %p, mark clear anyway\n", + td->urb); + td_to_noop(xhci, ring, td, false); + td->cancel_status = TD_CLEARED; } - cached_td = NULL; } return 0; } @@ -1069,6 +1090,8 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id, 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); @@ -1212,6 +1235,7 @@ void xhci_stop_endpoint_command_watchdog(struct timer_list *t) struct xhci_hcd *xhci = ep->xhci; unsigned long flags; u32 usbsts; + char str[XHCI_MSG_MAX]; spin_lock_irqsave(&xhci->lock, flags); @@ -1225,7 +1249,7 @@ void xhci_stop_endpoint_command_watchdog(struct timer_list *t) 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(usbsts)); + xhci_warn(xhci, "USBSTS:%s\n", xhci_decode_usbsts(str, usbsts)); ep->ep_state &= ~EP_STOP_CMD_PENDING; @@ -1389,7 +1413,12 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, ep_ring = xhci_urb_to_transfer_ring(ep->xhci, td->urb); if (td->cancel_status == TD_CLEARING_CACHE) { td->cancel_status = TD_CLEARED; + xhci_dbg(ep->xhci, "%s: Giveback cancelled URB %p TD\n", + __func__, td->urb); xhci_td_cleanup(ep->xhci, td, ep_ring, td->status); + } else { + xhci_dbg(ep->xhci, "%s: Keep cancelled URB %p TD as cancel_status is %d\n", + __func__, td->urb, td->cancel_status); } } cleanup: @@ -2002,7 +2031,8 @@ cleanup: * bits are still set. When an event occurs, switch over to * polling to avoid losing status changes. */ - xhci_dbg(xhci, "%s: starting port polling.\n", __func__); + xhci_dbg(xhci, "%s: starting usb%d port polling.\n", + __func__, hcd->self.busnum); set_bit(HCD_FLAG_POLL_RH, &hcd->flags); spin_unlock(&xhci->lock); /* Pass this up to the core */ diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h index 627abd236dbe..a5da02077297 100644 --- a/drivers/usb/host/xhci-trace.h +++ b/drivers/usb/host/xhci-trace.h @@ -25,8 +25,6 @@ #include "xhci.h" #include "xhci-dbgcap.h" -#define XHCI_MSG_MAX 500 - DECLARE_EVENT_CLASS(xhci_log_msg, TP_PROTO(struct va_format *vaf), TP_ARGS(vaf), @@ -122,6 +120,7 @@ DECLARE_EVENT_CLASS(xhci_log_trb, __field(u32, field1) __field(u32, field2) __field(u32, field3) + __dynamic_array(char, str, XHCI_MSG_MAX) ), TP_fast_assign( __entry->type = ring->type; @@ -131,7 +130,7 @@ DECLARE_EVENT_CLASS(xhci_log_trb, __entry->field3 = le32_to_cpu(trb->field[3]); ), TP_printk("%s: %s", xhci_ring_type_string(__entry->type), - xhci_decode_trb(__entry->field0, __entry->field1, + xhci_decode_trb(__get_str(str), XHCI_MSG_MAX, __entry->field0, __entry->field1, __entry->field2, __entry->field3) ) ); @@ -323,6 +322,7 @@ DECLARE_EVENT_CLASS(xhci_log_ep_ctx, __field(u32, info2) __field(u64, deq) __field(u32, tx_info) + __dynamic_array(char, str, XHCI_MSG_MAX) ), TP_fast_assign( __entry->info = le32_to_cpu(ctx->ep_info); @@ -330,8 +330,8 @@ DECLARE_EVENT_CLASS(xhci_log_ep_ctx, __entry->deq = le64_to_cpu(ctx->deq); __entry->tx_info = le32_to_cpu(ctx->tx_info); ), - TP_printk("%s", xhci_decode_ep_context(__entry->info, - __entry->info2, __entry->deq, __entry->tx_info) + TP_printk("%s", xhci_decode_ep_context(__get_str(str), + __entry->info, __entry->info2, __entry->deq, __entry->tx_info) ) ); @@ -368,6 +368,7 @@ DECLARE_EVENT_CLASS(xhci_log_slot_ctx, __field(u32, info2) __field(u32, tt_info) __field(u32, state) + __dynamic_array(char, str, XHCI_MSG_MAX) ), TP_fast_assign( __entry->info = le32_to_cpu(ctx->dev_info); @@ -375,9 +376,9 @@ DECLARE_EVENT_CLASS(xhci_log_slot_ctx, __entry->tt_info = le64_to_cpu(ctx->tt_info); __entry->state = le32_to_cpu(ctx->dev_state); ), - TP_printk("%s", xhci_decode_slot_context(__entry->info, - __entry->info2, __entry->tt_info, - __entry->state) + TP_printk("%s", xhci_decode_slot_context(__get_str(str), + __entry->info, __entry->info2, + __entry->tt_info, __entry->state) ) ); @@ -432,12 +433,13 @@ DECLARE_EVENT_CLASS(xhci_log_ctrl_ctx, TP_STRUCT__entry( __field(u32, drop) __field(u32, add) + __dynamic_array(char, str, XHCI_MSG_MAX) ), TP_fast_assign( __entry->drop = le32_to_cpu(ctrl_ctx->drop_flags); __entry->add = le32_to_cpu(ctrl_ctx->add_flags); ), - TP_printk("%s", xhci_decode_ctrl_ctx(__entry->drop, __entry->add) + TP_printk("%s", xhci_decode_ctrl_ctx(__get_str(str), __entry->drop, __entry->add) ) ); @@ -523,6 +525,7 @@ DECLARE_EVENT_CLASS(xhci_log_portsc, TP_STRUCT__entry( __field(u32, portnum) __field(u32, portsc) + __dynamic_array(char, str, XHCI_MSG_MAX) ), TP_fast_assign( __entry->portnum = portnum; @@ -530,7 +533,7 @@ DECLARE_EVENT_CLASS(xhci_log_portsc, ), TP_printk("port-%d: %s", __entry->portnum, - xhci_decode_portsc(__entry->portsc) + xhci_decode_portsc(__get_str(str), __entry->portsc) ) ); @@ -555,13 +558,14 @@ DECLARE_EVENT_CLASS(xhci_log_doorbell, TP_STRUCT__entry( __field(u32, slot) __field(u32, doorbell) + __dynamic_array(char, str, XHCI_MSG_MAX) ), TP_fast_assign( __entry->slot = slot; __entry->doorbell = doorbell; ), TP_printk("Ring doorbell for %s", - xhci_decode_doorbell(__entry->slot, __entry->doorbell) + xhci_decode_doorbell(__get_str(str), __entry->slot, __entry->doorbell) ) ); diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 3618070eba78..f3dabd02382c 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -993,7 +993,8 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup) xhci_dbc_suspend(xhci); /* Don't poll the roothubs on bus suspend. */ - xhci_dbg(xhci, "%s: stopping port polling.\n", __func__); + xhci_dbg(xhci, "%s: stopping usb%d port polling.\n", + __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); @@ -1257,7 +1258,8 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) usb_asmedia_modifyflowcontrol(to_pci_dev(hcd->self.controller)); /* Re-enable port polling. */ - xhci_dbg(xhci, "%s: starting port polling.\n", __func__); + 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); set_bit(HCD_FLAG_POLL_RH, &hcd->flags); @@ -4705,19 +4707,19 @@ static u16 xhci_calculate_u1_timeout(struct xhci_hcd *xhci, { unsigned long long timeout_ns; - if (xhci->quirks & XHCI_INTEL_HOST) - timeout_ns = xhci_calculate_intel_u1_timeout(udev, desc); - else - timeout_ns = udev->u1_params.sel; - /* Prevent U1 if service interval is shorter than U1 exit latency */ if (usb_endpoint_xfer_int(desc) || usb_endpoint_xfer_isoc(desc)) { - if (xhci_service_interval_to_ns(desc) <= timeout_ns) { + if (xhci_service_interval_to_ns(desc) <= udev->u1_params.mel) { dev_dbg(&udev->dev, "Disable U1, ESIT shorter than exit latency\n"); return USB3_LPM_DISABLED; } } + if (xhci->quirks & XHCI_INTEL_HOST) + timeout_ns = xhci_calculate_intel_u1_timeout(udev, desc); + else + timeout_ns = udev->u1_params.sel; + /* The U1 timeout is encoded in 1us intervals. * Don't return a timeout of zero, because that's USB3_LPM_DISABLED. */ @@ -4769,19 +4771,19 @@ static u16 xhci_calculate_u2_timeout(struct xhci_hcd *xhci, { unsigned long long timeout_ns; - if (xhci->quirks & XHCI_INTEL_HOST) - timeout_ns = xhci_calculate_intel_u2_timeout(udev, desc); - else - timeout_ns = udev->u2_params.sel; - /* Prevent U2 if service interval is shorter than U2 exit latency */ if (usb_endpoint_xfer_int(desc) || usb_endpoint_xfer_isoc(desc)) { - if (xhci_service_interval_to_ns(desc) <= timeout_ns) { + if (xhci_service_interval_to_ns(desc) <= udev->u2_params.mel) { dev_dbg(&udev->dev, "Disable U2, ESIT shorter than exit latency\n"); return USB3_LPM_DISABLED; } } + if (xhci->quirks & XHCI_INTEL_HOST) + timeout_ns = xhci_calculate_intel_u2_timeout(udev, desc); + else + timeout_ns = udev->u2_params.sel; + /* The U2 timeout is encoded in 256us intervals */ timeout_ns = DIV_ROUND_UP_ULL(timeout_ns, 256 * 1000); /* If the necessary timeout value is bigger than what we can set in the diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 3c7d281672ae..dca6181c33fd 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -22,6 +22,9 @@ #include "xhci-ext-caps.h" #include "pci-quirks.h" +/* max buffer size for trace and debug messages */ +#define XHCI_MSG_MAX 500 + /* xHCI PCI Configuration Registers */ #define XHCI_SBRN_OFFSET (0x60) @@ -2235,15 +2238,14 @@ static inline char *xhci_slot_state_string(u32 state) } } -static inline const char *xhci_decode_trb(u32 field0, u32 field1, u32 field2, - u32 field3) +static inline const char *xhci_decode_trb(char *str, size_t size, + u32 field0, u32 field1, u32 field2, u32 field3) { - static char str[256]; int type = TRB_FIELD_TO_TYPE(field3); switch (type) { case TRB_LINK: - sprintf(str, + snprintf(str, size, "LINK %08x%08x intr %d type '%s' flags %c:%c:%c:%c", field1, field0, GET_INTR_TARGET(field2), xhci_trb_type_string(type), @@ -2260,7 +2262,7 @@ static inline const char *xhci_decode_trb(u32 field0, u32 field1, u32 field2, case TRB_HC_EVENT: case TRB_DEV_NOTE: case TRB_MFINDEX_WRAP: - sprintf(str, + snprintf(str, size, "TRB %08x%08x status '%s' len %d slot %d ep %d type '%s' flags %c:%c", field1, field0, xhci_trb_comp_code_string(GET_COMP_CODE(field2)), @@ -2273,7 +2275,8 @@ static inline const char *xhci_decode_trb(u32 field0, u32 field1, u32 field2, break; case TRB_SETUP: - sprintf(str, "bRequestType %02x bRequest %02x wValue %02x%02x wIndex %02x%02x wLength %d length %d TD size %d intr %d type '%s' flags %c:%c:%c", + snprintf(str, size, + "bRequestType %02x bRequest %02x wValue %02x%02x wIndex %02x%02x wLength %d length %d TD size %d intr %d type '%s' flags %c:%c:%c", field0 & 0xff, (field0 & 0xff00) >> 8, (field0 & 0xff000000) >> 24, @@ -2290,7 +2293,8 @@ static inline const char *xhci_decode_trb(u32 field0, u32 field1, u32 field2, field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_DATA: - sprintf(str, "Buffer %08x%08x length %d TD size %d intr %d type '%s' flags %c:%c:%c:%c:%c:%c:%c", + snprintf(str, size, + "Buffer %08x%08x length %d TD size %d intr %d type '%s' flags %c:%c:%c:%c:%c:%c:%c", field1, field0, TRB_LEN(field2), GET_TD_SIZE(field2), GET_INTR_TARGET(field2), xhci_trb_type_string(type), @@ -2303,7 +2307,8 @@ static inline const char *xhci_decode_trb(u32 field0, u32 field1, u32 field2, field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_STATUS: - sprintf(str, "Buffer %08x%08x length %d TD size %d intr %d type '%s' flags %c:%c:%c:%c", + snprintf(str, size, + "Buffer %08x%08x length %d TD size %d intr %d type '%s' flags %c:%c:%c:%c", field1, field0, TRB_LEN(field2), GET_TD_SIZE(field2), GET_INTR_TARGET(field2), xhci_trb_type_string(type), @@ -2316,7 +2321,7 @@ static inline const char *xhci_decode_trb(u32 field0, u32 field1, u32 field2, case TRB_ISOC: case TRB_EVENT_DATA: case TRB_TR_NOOP: - sprintf(str, + snprintf(str, size, "Buffer %08x%08x length %d TD size %d intr %d type '%s' flags %c:%c:%c:%c:%c:%c:%c:%c", field1, field0, TRB_LEN(field2), GET_TD_SIZE(field2), GET_INTR_TARGET(field2), @@ -2333,21 +2338,21 @@ static inline const char *xhci_decode_trb(u32 field0, u32 field1, u32 field2, case TRB_CMD_NOOP: case TRB_ENABLE_SLOT: - sprintf(str, + snprintf(str, size, "%s: flags %c", xhci_trb_type_string(type), field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_DISABLE_SLOT: case TRB_NEG_BANDWIDTH: - sprintf(str, + snprintf(str, size, "%s: slot %d flags %c", xhci_trb_type_string(type), TRB_TO_SLOT_ID(field3), field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_ADDR_DEV: - sprintf(str, + snprintf(str, size, "%s: ctx %08x%08x slot %d flags %c:%c", xhci_trb_type_string(type), field1, field0, @@ -2356,7 +2361,7 @@ static inline const char *xhci_decode_trb(u32 field0, u32 field1, u32 field2, field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_CONFIG_EP: - sprintf(str, + snprintf(str, size, "%s: ctx %08x%08x slot %d flags %c:%c", xhci_trb_type_string(type), field1, field0, @@ -2365,7 +2370,7 @@ static inline const char *xhci_decode_trb(u32 field0, u32 field1, u32 field2, field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_EVAL_CONTEXT: - sprintf(str, + snprintf(str, size, "%s: ctx %08x%08x slot %d flags %c", xhci_trb_type_string(type), field1, field0, @@ -2373,7 +2378,7 @@ static inline const char *xhci_decode_trb(u32 field0, u32 field1, u32 field2, field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_RESET_EP: - sprintf(str, + snprintf(str, size, "%s: ctx %08x%08x slot %d ep %d flags %c:%c", xhci_trb_type_string(type), field1, field0, @@ -2394,7 +2399,7 @@ static inline const char *xhci_decode_trb(u32 field0, u32 field1, u32 field2, field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_SET_DEQ: - sprintf(str, + snprintf(str, size, "%s: deq %08x%08x stream %d slot %d ep %d flags %c", xhci_trb_type_string(type), field1, field0, @@ -2405,14 +2410,14 @@ static inline const char *xhci_decode_trb(u32 field0, u32 field1, u32 field2, field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_RESET_DEV: - sprintf(str, + snprintf(str, size, "%s: slot %d flags %c", xhci_trb_type_string(type), TRB_TO_SLOT_ID(field3), field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_FORCE_EVENT: - sprintf(str, + snprintf(str, size, "%s: event %08x%08x vf intr %d vf id %d flags %c", xhci_trb_type_string(type), field1, field0, @@ -2421,14 +2426,14 @@ static inline const char *xhci_decode_trb(u32 field0, u32 field1, u32 field2, field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_SET_LT: - sprintf(str, + snprintf(str, size, "%s: belt %d flags %c", xhci_trb_type_string(type), TRB_TO_BELT(field3), field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_GET_BW: - sprintf(str, + snprintf(str, size, "%s: ctx %08x%08x slot %d speed %d flags %c", xhci_trb_type_string(type), field1, field0, @@ -2437,7 +2442,7 @@ static inline const char *xhci_decode_trb(u32 field0, u32 field1, u32 field2, field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_FORCE_HEADER: - sprintf(str, + snprintf(str, size, "%s: info %08x%08x%08x pkt type %d roothub port %d flags %c", xhci_trb_type_string(type), field2, field1, field0 & 0xffffffe0, @@ -2446,7 +2451,7 @@ static inline const char *xhci_decode_trb(u32 field0, u32 field1, u32 field2, field3 & TRB_CYCLE ? 'C' : 'c'); break; default: - sprintf(str, + snprintf(str, size, "type '%s' -> raw %08x %08x %08x %08x", xhci_trb_type_string(type), field0, field1, field2, field3); @@ -2455,10 +2460,9 @@ static inline const char *xhci_decode_trb(u32 field0, u32 field1, u32 field2, return str; } -static inline const char *xhci_decode_ctrl_ctx(unsigned long drop, - unsigned long add) +static inline const char *xhci_decode_ctrl_ctx(char *str, + unsigned long drop, unsigned long add) { - static char str[1024]; unsigned int bit; int ret = 0; @@ -2484,10 +2488,9 @@ static inline const char *xhci_decode_ctrl_ctx(unsigned long drop, return str; } -static inline const char *xhci_decode_slot_context(u32 info, u32 info2, - u32 tt_info, u32 state) +static inline const char *xhci_decode_slot_context(char *str, + u32 info, u32 info2, u32 tt_info, u32 state) { - static char str[1024]; u32 speed; u32 hub; u32 mtt; @@ -2571,9 +2574,8 @@ static inline const char *xhci_portsc_link_state_string(u32 portsc) return "Unknown"; } -static inline const char *xhci_decode_portsc(u32 portsc) +static inline const char *xhci_decode_portsc(char *str, u32 portsc) { - static char str[256]; int ret; ret = sprintf(str, "%s %s %s Link:%s PortSpeed:%d ", @@ -2617,9 +2619,8 @@ static inline const char *xhci_decode_portsc(u32 portsc) return str; } -static inline const char *xhci_decode_usbsts(u32 usbsts) +static inline const char *xhci_decode_usbsts(char *str, u32 usbsts) { - static char str[256]; int ret = 0; if (usbsts == ~(u32)0) @@ -2646,9 +2647,8 @@ static inline const char *xhci_decode_usbsts(u32 usbsts) return str; } -static inline const char *xhci_decode_doorbell(u32 slot, u32 doorbell) +static inline const char *xhci_decode_doorbell(char *str, u32 slot, u32 doorbell) { - static char str[256]; u8 ep; u16 stream; int ret; @@ -2715,10 +2715,9 @@ static inline const char *xhci_ep_type_string(u8 type) } } -static inline const char *xhci_decode_ep_context(u32 info, u32 info2, u64 deq, - u32 tx_info) +static inline const char *xhci_decode_ep_context(char *str, u32 info, + u32 info2, u64 deq, u32 tx_info) { - static char str[1024]; int ret; u32 esit; diff --git a/drivers/usb/isp1760/isp1760-core.c b/drivers/usb/isp1760/isp1760-core.c index ff07e2890692..d1d9a7d5da17 100644 --- a/drivers/usb/isp1760/isp1760-core.c +++ b/drivers/usb/isp1760/isp1760-core.c @@ -30,6 +30,7 @@ static int isp1760_init_core(struct isp1760_device *isp) { struct isp1760_hcd *hcd = &isp->hcd; struct isp1760_udc *udc = &isp->udc; + u32 otg_ctrl; /* Low-level chip reset */ if (isp->rst_gpio) { @@ -83,16 +84,17 @@ static int isp1760_init_core(struct isp1760_device *isp) * * TODO: Really support OTG. For now we configure port 1 in device mode */ - if (((isp->devflags & ISP1760_FLAG_ISP1761) || - (isp->devflags & ISP1760_FLAG_ISP1763)) && - (isp->devflags & ISP1760_FLAG_PERIPHERAL_EN)) { - isp1760_field_set(hcd->fields, HW_DM_PULLDOWN); - isp1760_field_set(hcd->fields, HW_DP_PULLDOWN); - isp1760_field_set(hcd->fields, HW_OTG_DISABLE); - } else { - isp1760_field_set(hcd->fields, HW_SW_SEL_HC_DC); - isp1760_field_set(hcd->fields, HW_VBUS_DRV); - isp1760_field_set(hcd->fields, HW_SEL_CP_EXT); + if (isp->devflags & ISP1760_FLAG_ISP1761) { + if (isp->devflags & ISP1760_FLAG_PERIPHERAL_EN) { + otg_ctrl = (ISP176x_HW_DM_PULLDOWN_CLEAR | + ISP176x_HW_DP_PULLDOWN_CLEAR | + ISP176x_HW_OTG_DISABLE); + } else { + otg_ctrl = (ISP176x_HW_SW_SEL_HC_DC_CLEAR | + ISP176x_HW_VBUS_DRV | + ISP176x_HW_SEL_CP_EXT); + } + isp1760_reg_write(hcd->regs, ISP176x_HC_OTG_CTRL, otg_ctrl); } dev_info(isp->dev, "%s bus width: %u, oc: %s\n", @@ -235,20 +237,20 @@ static const struct reg_field isp1760_hc_reg_fields[] = { [HC_ISO_IRQ_MASK_AND] = REG_FIELD(ISP176x_HC_ISO_IRQ_MASK_AND, 0, 31), [HC_INT_IRQ_MASK_AND] = REG_FIELD(ISP176x_HC_INT_IRQ_MASK_AND, 0, 31), [HC_ATL_IRQ_MASK_AND] = REG_FIELD(ISP176x_HC_ATL_IRQ_MASK_AND, 0, 31), - [HW_OTG_DISABLE] = REG_FIELD(ISP176x_HC_OTG_CTRL_SET, 10, 10), - [HW_SW_SEL_HC_DC] = REG_FIELD(ISP176x_HC_OTG_CTRL_SET, 7, 7), - [HW_VBUS_DRV] = REG_FIELD(ISP176x_HC_OTG_CTRL_SET, 4, 4), - [HW_SEL_CP_EXT] = REG_FIELD(ISP176x_HC_OTG_CTRL_SET, 3, 3), - [HW_DM_PULLDOWN] = REG_FIELD(ISP176x_HC_OTG_CTRL_SET, 2, 2), - [HW_DP_PULLDOWN] = REG_FIELD(ISP176x_HC_OTG_CTRL_SET, 1, 1), - [HW_DP_PULLUP] = REG_FIELD(ISP176x_HC_OTG_CTRL_SET, 0, 0), - [HW_OTG_DISABLE_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL_CLEAR, 10, 10), - [HW_SW_SEL_HC_DC_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL_CLEAR, 7, 7), - [HW_VBUS_DRV_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL_CLEAR, 4, 4), - [HW_SEL_CP_EXT_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL_CLEAR, 3, 3), - [HW_DM_PULLDOWN_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL_CLEAR, 2, 2), - [HW_DP_PULLDOWN_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL_CLEAR, 1, 1), - [HW_DP_PULLUP_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL_CLEAR, 0, 0), + [HW_OTG_DISABLE_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL, 26, 26), + [HW_SW_SEL_HC_DC_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL, 23, 23), + [HW_VBUS_DRV_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL, 20, 20), + [HW_SEL_CP_EXT_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL, 19, 19), + [HW_DM_PULLDOWN_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL, 18, 18), + [HW_DP_PULLDOWN_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL, 17, 17), + [HW_DP_PULLUP_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL, 16, 16), + [HW_OTG_DISABLE] = REG_FIELD(ISP176x_HC_OTG_CTRL, 10, 10), + [HW_SW_SEL_HC_DC] = REG_FIELD(ISP176x_HC_OTG_CTRL, 7, 7), + [HW_VBUS_DRV] = REG_FIELD(ISP176x_HC_OTG_CTRL, 4, 4), + [HW_SEL_CP_EXT] = REG_FIELD(ISP176x_HC_OTG_CTRL, 3, 3), + [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), }; static const struct reg_field isp1763_hc_reg_fields[] = { @@ -491,7 +493,7 @@ int isp1760_register(struct resource *mem, int irq, unsigned long irqflags, (devflags & ISP1760_FLAG_ISP1761)); if ((!IS_ENABLED(CONFIG_USB_ISP1760_HCD) || usb_disabled()) && - (!IS_ENABLED(CONFIG_USB_ISP1761_UDC) || !udc_enabled)) + (!udc_enabled || !IS_ENABLED(CONFIG_USB_ISP1761_UDC))) return -ENODEV; isp = devm_kzalloc(dev, sizeof(*isp), GFP_KERNEL); @@ -571,7 +573,7 @@ int isp1760_register(struct resource *mem, int irq, unsigned long irqflags, return ret; } - if (IS_ENABLED(CONFIG_USB_ISP1761_UDC) && udc_enabled) { + if (udc_enabled && IS_ENABLED(CONFIG_USB_ISP1761_UDC)) { ret = isp1760_udc_register(isp, irq, irqflags); if (ret < 0) { isp1760_hcd_unregister(hcd); diff --git a/drivers/usb/isp1760/isp1760-hcd.c b/drivers/usb/isp1760/isp1760-hcd.c index 27168b4a4ef2..79d571f1429b 100644 --- a/drivers/usb/isp1760/isp1760-hcd.c +++ b/drivers/usb/isp1760/isp1760-hcd.c @@ -182,7 +182,7 @@ struct urb_listitem { struct urb *urb; }; -static const u32 isp1763_hc_portsc1_fields[] = { +static const u32 isp176x_hc_portsc1_fields[] = { [PORT_OWNER] = BIT(13), [PORT_POWER] = BIT(12), [PORT_LSTATUS] = BIT(10), @@ -205,27 +205,28 @@ static u32 isp1760_hcd_read(struct usb_hcd *hcd, u32 field) } /* - * We need, in isp1763, to write directly the values to the portsc1 + * We need, in isp176x, to write directly the values to the portsc1 * register so it will make the other values to trigger. */ static void isp1760_hcd_portsc1_set_clear(struct isp1760_hcd *priv, u32 field, u32 val) { - u32 bit = isp1763_hc_portsc1_fields[field]; - u32 port_status = readl(priv->base + ISP1763_HC_PORTSC1); + u32 bit = isp176x_hc_portsc1_fields[field]; + u16 portsc1_reg = priv->is_isp1763 ? ISP1763_HC_PORTSC1 : + ISP176x_HC_PORTSC1; + u32 port_status = readl(priv->base + portsc1_reg); if (val) - writel(port_status | bit, priv->base + ISP1763_HC_PORTSC1); + writel(port_status | bit, priv->base + portsc1_reg); else - writel(port_status & ~bit, priv->base + ISP1763_HC_PORTSC1); + writel(port_status & ~bit, priv->base + portsc1_reg); } static void isp1760_hcd_write(struct usb_hcd *hcd, u32 field, u32 val) { struct isp1760_hcd *priv = hcd_to_priv(hcd); - if (unlikely(priv->is_isp1763 && - (field >= PORT_OWNER && field <= PORT_CONNECT))) + if (unlikely((field >= PORT_OWNER && field <= PORT_CONNECT))) return isp1760_hcd_portsc1_set_clear(priv, field, val); isp1760_field_write(priv->fields, field, val); @@ -250,7 +251,7 @@ static int isp1760_hcd_set_and_wait(struct usb_hcd *hcd, u32 field, isp1760_hcd_set(hcd, field); return regmap_field_read_poll_timeout(priv->fields[field], val, - val, 10, timeout_us); + val, 0, timeout_us); } static int isp1760_hcd_set_and_wait_swap(struct usb_hcd *hcd, u32 field, @@ -262,7 +263,7 @@ static int isp1760_hcd_set_and_wait_swap(struct usb_hcd *hcd, u32 field, isp1760_hcd_set(hcd, field); return regmap_field_read_poll_timeout(priv->fields[field], val, - !val, 10, timeout_us); + !val, 0, timeout_us); } static int isp1760_hcd_clear_and_wait(struct usb_hcd *hcd, u32 field, @@ -274,7 +275,7 @@ static int isp1760_hcd_clear_and_wait(struct usb_hcd *hcd, u32 field, isp1760_hcd_clear(hcd, field); return regmap_field_read_poll_timeout(priv->fields[field], val, - !val, 10, timeout_us); + !val, 0, timeout_us); } static bool isp1760_hcd_is_set(struct usb_hcd *hcd, u32 field) @@ -367,8 +368,7 @@ static void isp1760_mem_read(struct usb_hcd *hcd, u32 src_offset, void *dst, { struct isp1760_hcd *priv = hcd_to_priv(hcd); - isp1760_hcd_write(hcd, MEM_BANK_SEL, ISP_BANK_0); - isp1760_hcd_write(hcd, MEM_START_ADDR, src_offset); + isp1760_reg_write(priv->regs, ISP176x_HC_MEMORY, src_offset); ndelay(100); bank_reads8(priv->base, src_offset, ISP_BANK_0, dst, bytes); @@ -496,8 +496,7 @@ static void isp1760_ptd_read(struct usb_hcd *hcd, u32 ptd_offset, u32 slot, u16 src_offset = ptd_offset + slot * sizeof(*ptd); struct isp1760_hcd *priv = hcd_to_priv(hcd); - isp1760_hcd_write(hcd, MEM_BANK_SEL, ISP_BANK_0); - isp1760_hcd_write(hcd, MEM_START_ADDR, src_offset); + isp1760_reg_write(priv->regs, ISP176x_HC_MEMORY, src_offset); ndelay(90); bank_reads8(priv->base, src_offset, ISP_BANK_0, (void *)ptd, @@ -588,8 +587,8 @@ static void init_memory(struct isp1760_hcd *priv) payload_addr = PAYLOAD_OFFSET; - for (i = 0, curr = 0; i < ARRAY_SIZE(mem->blocks); i++) { - for (j = 0; j < mem->blocks[i]; j++, curr++) { + for (i = 0, curr = 0; i < ARRAY_SIZE(mem->blocks); i++, curr += j) { + for (j = 0; j < mem->blocks[i]; j++) { priv->memory_pool[curr + j].start = payload_addr; priv->memory_pool[curr + j].size = mem->blocks_size[i]; priv->memory_pool[curr + j].free = 1; @@ -731,12 +730,15 @@ static int isp1760_hc_setup(struct usb_hcd *hcd) isp1760_hcd_write(hcd, HC_SCRATCH, pattern); - /* Change bus pattern */ - scratch = isp1760_hcd_read(hcd, HC_CHIP_ID_HIGH); - dev_err(hcd->self.controller, "Scratch test 0x%08x\n", scratch); + /* + * we do not care about the read value here we just want to + * change bus pattern. + */ + isp1760_hcd_read(hcd, HC_CHIP_ID_HIGH); scratch = isp1760_hcd_read(hcd, HC_SCRATCH); if (scratch != pattern) { - dev_err(hcd->self.controller, "Scratch test failed. 0x%08x\n", scratch); + dev_err(hcd->self.controller, "Scratch test failed. 0x%08x\n", + scratch); return -ENODEV; } @@ -1826,9 +1828,11 @@ static void packetize_urb(struct usb_hcd *hcd, goto cleanup; if (len > mem->blocks_size[ISP176x_BLOCK_NUM - 1]) - len = mem->blocks_size[ISP176x_BLOCK_NUM - 1]; + this_qtd_len = mem->blocks_size[ISP176x_BLOCK_NUM - 1]; + else + this_qtd_len = len; - this_qtd_len = qtd_fill(qtd, buf, len); + this_qtd_len = qtd_fill(qtd, buf, this_qtd_len); list_add_tail(&qtd->qtd_list, head); len -= this_qtd_len; @@ -1851,7 +1855,7 @@ static void packetize_urb(struct usb_hcd *hcd, packet_type = OUT_PID; else packet_type = IN_PID; - } else if (usb_pipebulk(urb->pipe) + } else if (usb_pipebulk(urb->pipe) && maxpacketsize && (urb->transfer_flags & URB_ZERO_PACKET) && !(urb->transfer_buffer_length % maxpacketsize)) { @@ -1916,7 +1920,6 @@ static int isp1760_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, if (list_empty(&new_qtds)) return -ENOMEM; - retval = 0; spin_lock_irqsave(&priv->lock, spinflags); if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { @@ -1972,16 +1975,20 @@ static void kill_transfer(struct usb_hcd *hcd, struct urb *urb, /* We need to forcefully reclaim the slot since some transfers never return, e.g. interrupt transfers and NAKed bulk transfers. */ if (usb_pipecontrol(urb->pipe) || usb_pipebulk(urb->pipe)) { - skip_map = isp1760_hcd_read(hcd, HC_ATL_PTD_SKIPMAP); - skip_map |= (1 << qh->slot); - isp1760_hcd_write(hcd, HC_ATL_PTD_SKIPMAP, skip_map); - ndelay(100); + if (qh->slot != -1) { + skip_map = isp1760_hcd_read(hcd, HC_ATL_PTD_SKIPMAP); + skip_map |= (1 << qh->slot); + isp1760_hcd_write(hcd, HC_ATL_PTD_SKIPMAP, skip_map); + ndelay(100); + } priv->atl_slots[qh->slot].qh = NULL; priv->atl_slots[qh->slot].qtd = NULL; } else { - skip_map = isp1760_hcd_read(hcd, HC_INT_PTD_SKIPMAP); - skip_map |= (1 << qh->slot); - isp1760_hcd_write(hcd, HC_INT_PTD_SKIPMAP, skip_map); + if (qh->slot != -1) { + skip_map = isp1760_hcd_read(hcd, HC_INT_PTD_SKIPMAP); + skip_map |= (1 << qh->slot); + isp1760_hcd_write(hcd, HC_INT_PTD_SKIPMAP, skip_map); + } priv->int_slots[qh->slot].qh = NULL; priv->int_slots[qh->slot].qtd = NULL; } @@ -2528,17 +2535,23 @@ int __init isp1760_init_kmem_once(void) SLAB_MEM_SPREAD, NULL); if (!qtd_cachep) - return -ENOMEM; + goto destroy_urb_listitem; qh_cachep = kmem_cache_create("isp1760_qh", sizeof(struct isp1760_qh), 0, SLAB_TEMPORARY | SLAB_MEM_SPREAD, NULL); - if (!qh_cachep) { - kmem_cache_destroy(qtd_cachep); - return -ENOMEM; - } + if (!qh_cachep) + goto destroy_qtd; return 0; + +destroy_qtd: + kmem_cache_destroy(qtd_cachep); + +destroy_urb_listitem: + kmem_cache_destroy(urb_listitem_cachep); + + return -ENOMEM; } void isp1760_deinit_kmem_cache(void) diff --git a/drivers/usb/isp1760/isp1760-regs.h b/drivers/usb/isp1760/isp1760-regs.h index 94ea60c20b2a..3a6751197e97 100644 --- a/drivers/usb/isp1760/isp1760-regs.h +++ b/drivers/usb/isp1760/isp1760-regs.h @@ -61,6 +61,7 @@ #define ISP176x_HC_INT_IRQ_MASK_AND 0x328 #define ISP176x_HC_ATL_IRQ_MASK_AND 0x32c +#define ISP176x_HC_OTG_CTRL 0x374 #define ISP176x_HC_OTG_CTRL_SET 0x374 #define ISP176x_HC_OTG_CTRL_CLEAR 0x376 @@ -179,6 +180,21 @@ enum isp176x_host_controller_fields { #define ISP176x_DC_IESUSP BIT(3) #define ISP176x_DC_IEBRST BIT(0) +#define ISP176x_HW_OTG_DISABLE_CLEAR BIT(26) +#define ISP176x_HW_SW_SEL_HC_DC_CLEAR BIT(23) +#define ISP176x_HW_VBUS_DRV_CLEAR BIT(20) +#define ISP176x_HW_SEL_CP_EXT_CLEAR BIT(19) +#define ISP176x_HW_DM_PULLDOWN_CLEAR BIT(18) +#define ISP176x_HW_DP_PULLDOWN_CLEAR BIT(17) +#define ISP176x_HW_DP_PULLUP_CLEAR BIT(16) +#define ISP176x_HW_OTG_DISABLE BIT(10) +#define ISP176x_HW_SW_SEL_HC_DC BIT(7) +#define ISP176x_HW_VBUS_DRV BIT(4) +#define ISP176x_HW_SEL_CP_EXT BIT(3) +#define ISP176x_HW_DM_PULLDOWN BIT(2) +#define ISP176x_HW_DP_PULLDOWN BIT(1) +#define ISP176x_HW_DP_PULLUP BIT(0) + #define ISP176x_DC_ENDPTYP_ISOC 0x01 #define ISP176x_DC_ENDPTYP_BULK 0x02 #define ISP176x_DC_ENDPTYP_INTERRUPT 0x03 diff --git a/drivers/usb/isp1760/isp1760-udc.c b/drivers/usb/isp1760/isp1760-udc.c index a78da59d6417..5cafd23345ca 100644 --- a/drivers/usb/isp1760/isp1760-udc.c +++ b/drivers/usb/isp1760/isp1760-udc.c @@ -1363,7 +1363,7 @@ static irqreturn_t isp1760_udc_irq(int irq, void *dev) status = isp1760_udc_irq_get_status(udc); - if (status & DC_IEVBUS) { + if (status & ISP176x_DC_IEVBUS) { dev_dbg(udc->isp->dev, "%s(VBUS)\n", __func__); /* The VBUS interrupt is only triggered when VBUS appears. */ spin_lock(&udc->lock); @@ -1371,7 +1371,7 @@ static irqreturn_t isp1760_udc_irq(int irq, void *dev) spin_unlock(&udc->lock); } - if (status & DC_IEBRST) { + if (status & ISP176x_DC_IEBRST) { dev_dbg(udc->isp->dev, "%s(BRST)\n", __func__); isp1760_udc_reset(udc); @@ -1391,18 +1391,18 @@ static irqreturn_t isp1760_udc_irq(int irq, void *dev) } } - if (status & DC_IEP0SETUP) { + if (status & ISP176x_DC_IEP0SETUP) { dev_dbg(udc->isp->dev, "%s(EP0SETUP)\n", __func__); isp1760_ep0_setup(udc); } - if (status & DC_IERESM) { + if (status & ISP176x_DC_IERESM) { dev_dbg(udc->isp->dev, "%s(RESM)\n", __func__); isp1760_udc_resume(udc); } - if (status & DC_IESUSP) { + if (status & ISP176x_DC_IESUSP) { dev_dbg(udc->isp->dev, "%s(SUSP)\n", __func__); spin_lock(&udc->lock); @@ -1413,7 +1413,7 @@ static irqreturn_t isp1760_udc_irq(int irq, void *dev) spin_unlock(&udc->lock); } - if (status & DC_IEHS_STA) { + if (status & ISP176x_DC_IEHS_STA) { dev_dbg(udc->isp->dev, "%s(HS_STA)\n", __func__); udc->gadget.speed = USB_SPEED_HIGH; } diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c index 6d15a097b007..ed6a19254d2f 100644 --- a/drivers/usb/misc/adutux.c +++ b/drivers/usb/misc/adutux.c @@ -394,13 +394,10 @@ static ssize_t adu_read(struct file *file, __user char *buffer, size_t count, spin_lock_irqsave (&dev->buflock, flags); if (dev->read_buffer_length) { /* we secure access to the primary */ - char *tmp; dev_dbg(&dev->udev->dev, "%s : swap, read_buffer_length = %d\n", __func__, dev->read_buffer_length); - tmp = dev->read_buffer_secondary; - dev->read_buffer_secondary = dev->read_buffer_primary; - dev->read_buffer_primary = tmp; + swap(dev->read_buffer_primary, dev->read_buffer_secondary); dev->secondary_head = 0; dev->secondary_tail = dev->read_buffer_length; dev->read_buffer_length = 0; diff --git a/drivers/usb/misc/brcmstb-usb-pinmap.c b/drivers/usb/misc/brcmstb-usb-pinmap.c index 336653091e3b..2b2019c19cde 100644 --- a/drivers/usb/misc/brcmstb-usb-pinmap.c +++ b/drivers/usb/misc/brcmstb-usb-pinmap.c @@ -293,6 +293,8 @@ static int __init brcmstb_usb_pinmap_probe(struct platform_device *pdev) /* Enable interrupt for out pins */ irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; err = devm_request_irq(&pdev->dev, irq, brcmstb_usb_pinmap_ovr_isr, IRQF_TRIGGER_RISING, diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h index 5546b868b08b..022bbdc54e68 100644 --- a/drivers/usb/mtu3/mtu3.h +++ b/drivers/usb/mtu3/mtu3.h @@ -199,6 +199,7 @@ struct mtu3_gpd_ring { * @id_nb : notifier for iddig(idpin) detection * @dr_work : work for drd mode switch, used to avoid sleep in atomic context * @desired_role : role desired to switch +* @default_role : default mode while usb role is USB_ROLE_NONE * @role_sw : use USB Role Switch to support dual-role switch, can't use * extcon at the same time, and extcon is deprecated. * @role_sw_used : true when the USB Role Switch is used. @@ -212,6 +213,7 @@ struct otg_switch_mtk { struct notifier_block id_nb; struct work_struct dr_work; enum usb_role desired_role; + enum usb_role default_role; struct usb_role_switch *role_sw; bool role_sw_used; bool is_u3_drd; @@ -226,6 +228,9 @@ struct otg_switch_mtk { * host only, device only or dual-role mode * @u2_ports: number of usb2.0 host ports * @u3_ports: number of usb3.0 host ports + * @u2p_dis_msk: mask of disabling usb2 ports, e.g. bit0==1 to + * disable u2port0, bit1==1 to disable u2port1,... etc, + * but when use dual-role mode, can't disable u2port0 * @u3p_dis_msk: mask of disabling usb3 ports, for example, bit0==1 to * disable u3port0, bit1==1 to disable u3port1,... etc * @dbgfs_root: only used when supports manual dual-role switch via debugfs @@ -241,6 +246,7 @@ struct ssusb_mtk { void __iomem *ippc_base; struct phy **phys; int num_phys; + int wakeup_irq; /* common power & clock */ struct regulator *vusb33; struct clk_bulk_data clks[BULK_CLKS_CNT]; @@ -250,6 +256,7 @@ struct ssusb_mtk { bool is_host; int u2_ports; int u3_ports; + int u2p_dis_msk; int u3p_dis_msk; struct dentry *dbgfs_root; /* usb wakeup for host mode */ @@ -349,6 +356,7 @@ struct mtu3 { unsigned is_u3_ip:1; unsigned delayed_status:1; unsigned gen2cp:1; + unsigned connected:1; u8 address; u8 test_mode_nr; diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c index 562f4357831e..c4a2c37abf62 100644 --- a/drivers/usb/mtu3/mtu3_core.c +++ b/drivers/usb/mtu3/mtu3_core.c @@ -141,6 +141,28 @@ static void mtu3_device_disable(struct mtu3 *mtu) mtu3_setbits(ibase, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN); } +static void mtu3_dev_power_on(struct mtu3 *mtu) +{ + void __iomem *ibase = mtu->ippc_base; + + mtu3_clrbits(ibase, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN); + if (mtu->is_u3_ip) + mtu3_clrbits(ibase, SSUSB_U3_CTRL(0), SSUSB_U3_PORT_PDN); + + mtu3_clrbits(ibase, SSUSB_U2_CTRL(0), SSUSB_U2_PORT_PDN); +} + +static void mtu3_dev_power_down(struct mtu3 *mtu) +{ + void __iomem *ibase = mtu->ippc_base; + + if (mtu->is_u3_ip) + mtu3_setbits(ibase, SSUSB_U3_CTRL(0), SSUSB_U3_PORT_PDN); + + mtu3_setbits(ibase, SSUSB_U2_CTRL(0), SSUSB_U2_PORT_PDN); + mtu3_setbits(ibase, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN); +} + /* reset U3D's device module. */ static void mtu3_device_reset(struct mtu3 *mtu) { @@ -227,11 +249,13 @@ static void mtu3_set_speed(struct mtu3 *mtu, enum usb_device_speed speed) mtu3_setbits(mbase, U3D_POWER_MANAGEMENT, HS_ENABLE); break; case USB_SPEED_SUPER: + mtu3_setbits(mbase, U3D_POWER_MANAGEMENT, HS_ENABLE); mtu3_clrbits(mtu->ippc_base, SSUSB_U3_CTRL(0), SSUSB_U3_PORT_SSP_SPEED); break; case USB_SPEED_SUPER_PLUS: - mtu3_setbits(mtu->ippc_base, SSUSB_U3_CTRL(0), + mtu3_setbits(mbase, U3D_POWER_MANAGEMENT, HS_ENABLE); + mtu3_setbits(mtu->ippc_base, SSUSB_U3_CTRL(0), SSUSB_U3_PORT_SSP_SPEED); break; default: @@ -333,12 +357,7 @@ void mtu3_start(struct mtu3 *mtu) dev_dbg(mtu->dev, "%s devctl 0x%x\n", __func__, mtu3_readl(mbase, U3D_DEVICE_CONTROL)); - mtu3_clrbits(mtu->ippc_base, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN); - if (mtu->is_u3_ip) - mtu3_clrbits(mtu->ippc_base, SSUSB_U3_CTRL(0), SSUSB_U3_PORT_PDN); - - mtu3_clrbits(mtu->ippc_base, SSUSB_U2_CTRL(0), SSUSB_U2_PORT_PDN); - + mtu3_dev_power_on(mtu); mtu3_csr_init(mtu); mtu3_set_speed(mtu, mtu->speed); @@ -360,12 +379,25 @@ void mtu3_stop(struct mtu3 *mtu) mtu3_dev_on_off(mtu, 0); mtu->is_active = 0; + mtu3_dev_power_down(mtu); +} - if (mtu->is_u3_ip) - mtu3_setbits(mtu->ippc_base, SSUSB_U3_CTRL(0), SSUSB_U3_PORT_PDN); +static void mtu3_dev_suspend(struct mtu3 *mtu) +{ + if (!mtu->is_active) + return; - mtu3_setbits(mtu->ippc_base, SSUSB_U2_CTRL(0), SSUSB_U2_PORT_PDN); - mtu3_setbits(mtu->ippc_base, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN); + mtu3_intr_disable(mtu); + mtu3_dev_power_down(mtu); +} + +static void mtu3_dev_resume(struct mtu3 *mtu) +{ + if (!mtu->is_active) + return; + + mtu3_dev_power_on(mtu); + mtu3_intr_enable(mtu); } /* for non-ep0 */ @@ -688,11 +720,15 @@ static irqreturn_t mtu3_link_isr(struct mtu3 *mtu) mtu->g.speed = udev_speed; mtu->g.ep0->maxpacket = maxpkt; mtu->ep0_state = MU3D_EP0_STATE_SETUP; + mtu->connected = !!(udev_speed != USB_SPEED_UNKNOWN); - if (udev_speed == USB_SPEED_UNKNOWN) + if (udev_speed == USB_SPEED_UNKNOWN) { mtu3_gadget_disconnect(mtu); - else + pm_runtime_put(mtu->dev); + } else { + pm_runtime_get(mtu->dev); mtu3_ep0_setup(mtu); + } return IRQ_HANDLED; } @@ -888,9 +924,16 @@ int ssusb_gadget_init(struct ssusb_mtk *ssusb) if (mtu == NULL) return -ENOMEM; - mtu->irq = platform_get_irq(pdev, 0); - if (mtu->irq < 0) - return mtu->irq; + mtu->irq = platform_get_irq_byname_optional(pdev, "device"); + if (mtu->irq < 0) { + if (mtu->irq == -EPROBE_DEFER) + return mtu->irq; + + /* for backward compatibility */ + mtu->irq = platform_get_irq(pdev, 0); + if (mtu->irq < 0) + return mtu->irq; + } dev_info(dev, "irq %d\n", mtu->irq); mtu->mac_base = devm_platform_ioremap_resource_byname(pdev, "mac"); @@ -965,3 +1008,47 @@ void ssusb_gadget_exit(struct ssusb_mtk *ssusb) device_init_wakeup(ssusb->dev, false); mtu3_hw_exit(mtu); } + +bool ssusb_gadget_ip_sleep_check(struct ssusb_mtk *ssusb) +{ + struct mtu3 *mtu = ssusb->u3d; + + /* host only, should wait for ip sleep */ + if (!mtu) + return true; + + /* device is started and pullup D+, ip can sleep */ + if (mtu->is_active && mtu->softconnect) + return true; + + /* ip can't sleep if not pullup D+ when support device mode */ + return false; +} + +int ssusb_gadget_suspend(struct ssusb_mtk *ssusb, pm_message_t msg) +{ + struct mtu3 *mtu = ssusb->u3d; + + if (!mtu->gadget_driver) + return 0; + + if (mtu->connected) + return -EBUSY; + + mtu3_dev_suspend(mtu); + synchronize_irq(mtu->irq); + + return 0; +} + +int ssusb_gadget_resume(struct ssusb_mtk *ssusb, pm_message_t msg) +{ + struct mtu3 *mtu = ssusb->u3d; + + if (!mtu->gadget_driver) + return 0; + + mtu3_dev_resume(mtu); + + return 0; +} diff --git a/drivers/usb/mtu3/mtu3_dr.c b/drivers/usb/mtu3/mtu3_dr.c index 318fbc618137..a6b04831b20b 100644 --- a/drivers/usb/mtu3/mtu3_dr.c +++ b/drivers/usb/mtu3/mtu3_dr.c @@ -137,14 +137,19 @@ static void ssusb_mode_sw_work(struct work_struct *work) current_role = ssusb->is_host ? USB_ROLE_HOST : USB_ROLE_DEVICE; - if (desired_role == USB_ROLE_NONE) + if (desired_role == USB_ROLE_NONE) { + /* the default mode is host as probe does */ desired_role = USB_ROLE_HOST; + if (otg_sx->default_role == USB_ROLE_DEVICE) + desired_role = USB_ROLE_DEVICE; + } if (current_role == desired_role) return; dev_dbg(ssusb->dev, "set role : %s\n", usb_role_string(desired_role)); mtu3_dbg_trace(ssusb->dev, "set role : %s", usb_role_string(desired_role)); + pm_runtime_get_sync(ssusb->dev); switch (desired_role) { case USB_ROLE_HOST: @@ -165,6 +170,7 @@ static void ssusb_mode_sw_work(struct work_struct *work) default: dev_err(ssusb->dev, "invalid role\n"); } + pm_runtime_put(ssusb->dev); } static void ssusb_set_mode(struct otg_switch_mtk *otg_sx, enum usb_role role) @@ -274,17 +280,29 @@ static int ssusb_role_sw_register(struct otg_switch_mtk *otg_sx) { struct usb_role_switch_desc role_sx_desc = { 0 }; struct ssusb_mtk *ssusb = otg_sx_to_ssusb(otg_sx); + struct device *dev = ssusb->dev; + enum usb_dr_mode mode; if (!otg_sx->role_sw_used) return 0; + mode = usb_get_role_switch_default_mode(dev); + if (mode == USB_DR_MODE_PERIPHERAL) + otg_sx->default_role = USB_ROLE_DEVICE; + else + otg_sx->default_role = USB_ROLE_HOST; + role_sx_desc.set = ssusb_role_sw_set; role_sx_desc.get = ssusb_role_sw_get; - role_sx_desc.fwnode = dev_fwnode(ssusb->dev); + role_sx_desc.fwnode = dev_fwnode(dev); role_sx_desc.driver_data = ssusb; - otg_sx->role_sw = usb_role_switch_register(ssusb->dev, &role_sx_desc); + otg_sx->role_sw = usb_role_switch_register(dev, &role_sx_desc); + if (IS_ERR(otg_sx->role_sw)) + return PTR_ERR(otg_sx->role_sw); - return PTR_ERR_OR_ZERO(otg_sx->role_sw); + ssusb_set_mode(otg_sx, otg_sx->default_role); + + return 0; } int ssusb_otg_switch_init(struct ssusb_mtk *ssusb) diff --git a/drivers/usb/mtu3/mtu3_dr.h b/drivers/usb/mtu3/mtu3_dr.h index 760fe7d69c6b..e325508bddf4 100644 --- a/drivers/usb/mtu3/mtu3_dr.h +++ b/drivers/usb/mtu3/mtu3_dr.h @@ -16,8 +16,8 @@ int ssusb_host_init(struct ssusb_mtk *ssusb, struct device_node *parent_dn); void ssusb_host_exit(struct ssusb_mtk *ssusb); int ssusb_wakeup_of_property_parse(struct ssusb_mtk *ssusb, struct device_node *dn); -int ssusb_host_enable(struct ssusb_mtk *ssusb); -int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend); +int ssusb_host_resume(struct ssusb_mtk *ssusb, bool p0_skipped); +int ssusb_host_suspend(struct ssusb_mtk *ssusb); void ssusb_wakeup_set(struct ssusb_mtk *ssusb, bool enable); #else @@ -38,12 +38,12 @@ static inline int ssusb_wakeup_of_property_parse( return 0; } -static inline int ssusb_host_enable(struct ssusb_mtk *ssusb) +static inline int ssusb_host_resume(struct ssusb_mtk *ssusb, bool p0_skipped) { return 0; } -static inline int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend) +static inline int ssusb_host_suspend(struct ssusb_mtk *ssusb) { return 0; } @@ -57,6 +57,10 @@ static inline void ssusb_wakeup_set(struct ssusb_mtk *ssusb, bool enable) #if IS_ENABLED(CONFIG_USB_MTU3_GADGET) || IS_ENABLED(CONFIG_USB_MTU3_DUAL_ROLE) int ssusb_gadget_init(struct ssusb_mtk *ssusb); void ssusb_gadget_exit(struct ssusb_mtk *ssusb); +int ssusb_gadget_suspend(struct ssusb_mtk *ssusb, pm_message_t msg); +int ssusb_gadget_resume(struct ssusb_mtk *ssusb, pm_message_t msg); +bool ssusb_gadget_ip_sleep_check(struct ssusb_mtk *ssusb); + #else static inline int ssusb_gadget_init(struct ssusb_mtk *ssusb) { @@ -65,6 +69,24 @@ static inline int ssusb_gadget_init(struct ssusb_mtk *ssusb) static inline void ssusb_gadget_exit(struct ssusb_mtk *ssusb) {} + +static inline int +ssusb_gadget_suspend(struct ssusb_mtk *ssusb, pm_message_t msg) +{ + return 0; +} + +static inline int +ssusb_gadget_resume(struct ssusb_mtk *ssusb, pm_message_t msg) +{ + return 0; +} + +static inline bool ssusb_gadget_ip_sleep_check(struct ssusb_mtk *ssusb) +{ + return true; +} + #endif diff --git a/drivers/usb/mtu3/mtu3_gadget.c b/drivers/usb/mtu3/mtu3_gadget.c index 5e21ba05ebf0..a9a65b4bbfed 100644 --- a/drivers/usb/mtu3/mtu3_gadget.c +++ b/drivers/usb/mtu3/mtu3_gadget.c @@ -64,14 +64,12 @@ static int mtu3_ep_enable(struct mtu3_ep *mep) u32 interval = 0; u32 mult = 0; u32 burst = 0; - int max_packet; int ret; desc = mep->desc; comp_desc = mep->comp_desc; mep->type = usb_endpoint_type(desc); - max_packet = usb_endpoint_maxp(desc); - mep->maxp = max_packet & GENMASK(10, 0); + mep->maxp = usb_endpoint_maxp(desc); switch (mtu->g.speed) { case USB_SPEED_SUPER: @@ -92,7 +90,7 @@ static int mtu3_ep_enable(struct mtu3_ep *mep) usb_endpoint_xfer_int(desc)) { interval = desc->bInterval; interval = clamp_val(interval, 1, 16) - 1; - burst = (max_packet & GENMASK(12, 11)) >> 11; + mult = usb_endpoint_maxp_mult(desc) - 1; } break; default: @@ -469,6 +467,8 @@ static int mtu3_gadget_pullup(struct usb_gadget *gadget, int is_on) dev_dbg(mtu->dev, "%s (%s) for %sactive device\n", __func__, is_on ? "on" : "off", mtu->is_active ? "" : "in"); + pm_runtime_get_sync(mtu->dev); + /* we'd rather not pullup unless the device is active. */ spin_lock_irqsave(&mtu->lock, flags); @@ -482,6 +482,7 @@ static int mtu3_gadget_pullup(struct usb_gadget *gadget, int is_on) } spin_unlock_irqrestore(&mtu->lock, flags); + pm_runtime_put(mtu->dev); return 0; } @@ -499,6 +500,7 @@ static int mtu3_gadget_start(struct usb_gadget *gadget, } dev_dbg(mtu->dev, "bind driver %s\n", driver->function); + pm_runtime_get_sync(mtu->dev); spin_lock_irqsave(&mtu->lock, flags); @@ -509,6 +511,7 @@ static int mtu3_gadget_start(struct usb_gadget *gadget, mtu3_start(mtu); spin_unlock_irqrestore(&mtu->lock, flags); + pm_runtime_put(mtu->dev); return 0; } diff --git a/drivers/usb/mtu3/mtu3_host.c b/drivers/usb/mtu3/mtu3_host.c index 93a1a4c11e1e..f3903367a6a0 100644 --- a/drivers/usb/mtu3/mtu3_host.c +++ b/drivers/usb/mtu3/mtu3_host.c @@ -8,7 +8,6 @@ */ #include <linux/clk.h> -#include <linux/iopoll.h> #include <linux/irq.h> #include <linux/kernel.h> #include <linux/mfd/syscon.h> @@ -63,7 +62,7 @@ static void ssusb_wakeup_ip_sleep_set(struct ssusb_mtk *ssusb, bool enable) case SSUSB_UWK_V1_1: reg = ssusb->uwk_reg_base + PERI_WK_CTRL0; msk = WC0_IS_EN | WC0_IS_C(0xf) | WC0_IS_P; - val = enable ? (WC0_IS_EN | WC0_IS_C(0x8)) : 0; + val = enable ? (WC0_IS_EN | WC0_IS_C(0x1)) : 0; break; case SSUSB_UWK_V1_2: reg = ssusb->uwk_reg_base + PERI_WK_CTRL0; @@ -126,7 +125,7 @@ static void host_ports_num_get(struct ssusb_mtk *ssusb) } /* only configure ports will be used later */ -int ssusb_host_enable(struct ssusb_mtk *ssusb) +static int ssusb_host_enable(struct ssusb_mtk *ssusb) { void __iomem *ibase = ssusb->ippc_base; int num_u3p = ssusb->u3_ports; @@ -155,6 +154,9 @@ int ssusb_host_enable(struct ssusb_mtk *ssusb) /* power on and enable all u2 ports */ for (i = 0; i < num_u2p; i++) { + if ((0x1 << i) & ssusb->u2p_dis_msk) + continue; + value = mtu3_readl(ibase, SSUSB_U2_CTRL(i)); value &= ~(SSUSB_U2_PORT_PDN | SSUSB_U2_PORT_DIS); value |= SSUSB_U2_PORT_HOST_SEL; @@ -168,13 +170,12 @@ int ssusb_host_enable(struct ssusb_mtk *ssusb) return ssusb_check_clocks(ssusb, check_clk); } -int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend) +static int ssusb_host_disable(struct ssusb_mtk *ssusb) { void __iomem *ibase = ssusb->ippc_base; int num_u3p = ssusb->u3_ports; int num_u2p = ssusb->u2_ports; u32 value; - int ret; int i; /* power down and disable u3 ports except skipped ones */ @@ -183,32 +184,101 @@ int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend) continue; value = mtu3_readl(ibase, SSUSB_U3_CTRL(i)); - value |= SSUSB_U3_PORT_PDN; - value |= suspend ? 0 : SSUSB_U3_PORT_DIS; + value |= SSUSB_U3_PORT_PDN | SSUSB_U3_PORT_DIS; mtu3_writel(ibase, SSUSB_U3_CTRL(i), value); } - /* power down and disable all u2 ports */ + /* power down and disable u2 ports except skipped ones */ for (i = 0; i < num_u2p; i++) { + if ((0x1 << i) & ssusb->u2p_dis_msk) + continue; + value = mtu3_readl(ibase, SSUSB_U2_CTRL(i)); - value |= SSUSB_U2_PORT_PDN; - value |= suspend ? 0 : SSUSB_U2_PORT_DIS; + value |= SSUSB_U2_PORT_PDN | SSUSB_U2_PORT_DIS; mtu3_writel(ibase, SSUSB_U2_CTRL(i), value); } /* power down host ip */ mtu3_setbits(ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN); - if (!suspend) - return 0; + return 0; +} - /* wait for host ip to sleep */ - ret = readl_poll_timeout(ibase + U3D_SSUSB_IP_PW_STS1, value, - (value & SSUSB_IP_SLEEP_STS), 100, 100000); - if (ret) - dev_err(ssusb->dev, "ip sleep failed!!!\n"); +int ssusb_host_resume(struct ssusb_mtk *ssusb, bool p0_skipped) +{ + void __iomem *ibase = ssusb->ippc_base; + int u3p_skip_msk = ssusb->u3p_dis_msk; + int u2p_skip_msk = ssusb->u2p_dis_msk; + int num_u3p = ssusb->u3_ports; + int num_u2p = ssusb->u2_ports; + u32 value; + int i; - return ret; + if (p0_skipped) { + u2p_skip_msk |= 0x1; + if (ssusb->otg_switch.is_u3_drd) + u3p_skip_msk |= 0x1; + } + + /* power on host ip */ + mtu3_clrbits(ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN); + + /* power on u3 ports except skipped ones */ + for (i = 0; i < num_u3p; i++) { + if ((0x1 << i) & u3p_skip_msk) + continue; + + value = mtu3_readl(ibase, SSUSB_U3_CTRL(i)); + value &= ~SSUSB_U3_PORT_PDN; + mtu3_writel(ibase, SSUSB_U3_CTRL(i), value); + } + + /* power on all u2 ports except skipped ones */ + for (i = 0; i < num_u2p; i++) { + if ((0x1 << i) & u2p_skip_msk) + continue; + + value = mtu3_readl(ibase, SSUSB_U2_CTRL(i)); + value &= ~SSUSB_U2_PORT_PDN; + mtu3_writel(ibase, SSUSB_U2_CTRL(i), value); + } + + return 0; +} + +/* here not skip port0 due to PDN can be set repeatedly */ +int ssusb_host_suspend(struct ssusb_mtk *ssusb) +{ + void __iomem *ibase = ssusb->ippc_base; + int num_u3p = ssusb->u3_ports; + int num_u2p = ssusb->u2_ports; + u32 value; + int i; + + /* power down u3 ports except skipped ones */ + for (i = 0; i < num_u3p; i++) { + if ((0x1 << i) & ssusb->u3p_dis_msk) + continue; + + value = mtu3_readl(ibase, SSUSB_U3_CTRL(i)); + value |= SSUSB_U3_PORT_PDN; + mtu3_writel(ibase, SSUSB_U3_CTRL(i), value); + } + + /* power down u2 ports except skipped ones */ + for (i = 0; i < num_u2p; i++) { + if ((0x1 << i) & ssusb->u2p_dis_msk) + continue; + + value = mtu3_readl(ibase, SSUSB_U2_CTRL(i)); + value |= SSUSB_U2_PORT_PDN; + mtu3_writel(ibase, SSUSB_U2_CTRL(i), value); + } + + /* power down host ip */ + mtu3_setbits(ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN); + + return 0; } static void ssusb_host_setup(struct ssusb_mtk *ssusb) @@ -231,7 +301,7 @@ static void ssusb_host_cleanup(struct ssusb_mtk *ssusb) if (ssusb->is_host) ssusb_set_vbus(&ssusb->otg_switch, 0); - ssusb_host_disable(ssusb, false); + ssusb_host_disable(ssusb); } /* diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c index c0615f6e5cce..f13531022f4a 100644 --- a/drivers/usb/mtu3/mtu3_plat.c +++ b/drivers/usb/mtu3/mtu3_plat.c @@ -12,6 +12,7 @@ #include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/platform_device.h> +#include <linux/pm_wakeirq.h> #include "mtu3.h" #include "mtu3_dr.h" @@ -44,6 +45,32 @@ int ssusb_check_clocks(struct ssusb_mtk *ssusb, u32 ex_clks) return 0; } +static int wait_for_ip_sleep(struct ssusb_mtk *ssusb) +{ + bool sleep_check = true; + u32 value; + int ret; + + if (!ssusb->is_host) + sleep_check = ssusb_gadget_ip_sleep_check(ssusb); + + if (!sleep_check) + return 0; + + /* wait for ip enter sleep mode */ + ret = readl_poll_timeout(ssusb->ippc_base + U3D_SSUSB_IP_PW_STS1, value, + (value & SSUSB_IP_SLEEP_STS), 100, 100000); + if (ret) { + dev_err(ssusb->dev, "ip sleep failed!!!\n"); + ret = -EBUSY; + } else { + /* workaround: avoid wrong wakeup signal latch for some soc */ + usleep_range(100, 200); + } + + return ret; +} + static int ssusb_phy_init(struct ssusb_mtk *ssusb) { int i; @@ -208,6 +235,10 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb) if (IS_ERR(ssusb->ippc_base)) return PTR_ERR(ssusb->ippc_base); + ssusb->wakeup_irq = platform_get_irq_byname_optional(pdev, "wakeup"); + if (ssusb->wakeup_irq == -EPROBE_DEFER) + return ssusb->wakeup_irq; + ssusb->dr_mode = usb_get_dr_mode(dev); if (ssusb->dr_mode == USB_DR_MODE_UNKNOWN) ssusb->dr_mode = USB_DR_MODE_OTG; @@ -225,6 +256,8 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb) /* optional property, ignore the error if it does not exist */ of_property_read_u32(node, "mediatek,u3p-dis-msk", &ssusb->u3p_dis_msk); + of_property_read_u32(node, "mediatek,u2p-dis-msk", + &ssusb->u2p_dis_msk); otg_sx->vbus = devm_regulator_get(dev, "vbus"); if (IS_ERR(otg_sx->vbus)) { @@ -241,6 +274,9 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb) of_property_read_bool(node, "enable-manual-drd"); otg_sx->role_sw_used = of_property_read_bool(node, "usb-role-switch"); + /* can't disable port0 when use dual-role mode */ + ssusb->u2p_dis_msk &= ~0x1; + if (otg_sx->role_sw_used || otg_sx->manual_drd_enabled) goto out; @@ -253,9 +289,11 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb) } out: - dev_info(dev, "dr_mode: %d, is_u3_dr: %d, u3p_dis_msk: %x, drd: %s\n", - ssusb->dr_mode, otg_sx->is_u3_drd, ssusb->u3p_dis_msk, + dev_info(dev, "dr_mode: %d, is_u3_dr: %d, drd: %s\n", + ssusb->dr_mode, otg_sx->is_u3_drd, otg_sx->manual_drd_enabled ? "manual" : "auto"); + dev_info(dev, "u2p_dis_msk: %x, u3p_dis_msk: %x\n", + ssusb->u2p_dis_msk, ssusb->u3p_dis_msk); return 0; } @@ -288,14 +326,25 @@ static int mtu3_probe(struct platform_device *pdev) ssusb_debugfs_create_root(ssusb); /* enable power domain */ + pm_runtime_set_active(dev); + pm_runtime_use_autosuspend(dev); + pm_runtime_set_autosuspend_delay(dev, 4000); pm_runtime_enable(dev); pm_runtime_get_sync(dev); - device_enable_async_suspend(dev); ret = ssusb_rscs_init(ssusb); if (ret) goto comm_init_err; + if (ssusb->wakeup_irq > 0) { + ret = dev_pm_set_dedicated_wake_irq(dev, ssusb->wakeup_irq); + if (ret) { + dev_err(dev, "failed to set wakeup irq %d\n", ssusb->wakeup_irq); + goto comm_exit; + } + dev_info(dev, "wakeup irq %d\n", ssusb->wakeup_irq); + } + ssusb_ip_sw_reset(ssusb); if (IS_ENABLED(CONFIG_USB_MTU3_HOST)) @@ -346,6 +395,11 @@ static int mtu3_probe(struct platform_device *pdev) goto comm_exit; } + device_enable_async_suspend(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + pm_runtime_forbid(dev); + return 0; host_exit: @@ -355,7 +409,7 @@ gadget_exit: comm_exit: ssusb_rscs_exit(ssusb); comm_init_err: - pm_runtime_put_sync(dev); + pm_runtime_put_noidle(dev); pm_runtime_disable(dev); ssusb_debugfs_remove_root(ssusb); @@ -366,6 +420,8 @@ static int mtu3_remove(struct platform_device *pdev) { struct ssusb_mtk *ssusb = platform_get_drvdata(pdev); + pm_runtime_get_sync(&pdev->dev); + switch (ssusb->dr_mode) { case USB_DR_MODE_PERIPHERAL: ssusb_gadget_exit(ssusb); @@ -383,45 +439,87 @@ static int mtu3_remove(struct platform_device *pdev) } ssusb_rscs_exit(ssusb); - pm_runtime_put_sync(&pdev->dev); - pm_runtime_disable(&pdev->dev); ssusb_debugfs_remove_root(ssusb); + pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); return 0; } -/* - * when support dual-role mode, we reject suspend when - * it works as device mode; - */ -static int __maybe_unused mtu3_suspend(struct device *dev) +static int resume_ip_and_ports(struct ssusb_mtk *ssusb, pm_message_t msg) +{ + switch (ssusb->dr_mode) { + case USB_DR_MODE_PERIPHERAL: + ssusb_gadget_resume(ssusb, msg); + break; + case USB_DR_MODE_HOST: + ssusb_host_resume(ssusb, false); + break; + case USB_DR_MODE_OTG: + ssusb_host_resume(ssusb, !ssusb->is_host); + if (!ssusb->is_host) + ssusb_gadget_resume(ssusb, msg); + + break; + default: + return -EINVAL; + } + + return 0; +} + +static int mtu3_suspend_common(struct device *dev, pm_message_t msg) { struct ssusb_mtk *ssusb = dev_get_drvdata(dev); + int ret = 0; dev_dbg(dev, "%s\n", __func__); - /* REVISIT: disconnect it for only device mode? */ - if (!ssusb->is_host) - return 0; + switch (ssusb->dr_mode) { + case USB_DR_MODE_PERIPHERAL: + ret = ssusb_gadget_suspend(ssusb, msg); + if (ret) + goto err; + + break; + case USB_DR_MODE_HOST: + ssusb_host_suspend(ssusb); + break; + case USB_DR_MODE_OTG: + if (!ssusb->is_host) { + ret = ssusb_gadget_suspend(ssusb, msg); + if (ret) + goto err; + } + ssusb_host_suspend(ssusb); + break; + default: + return -EINVAL; + } + + ret = wait_for_ip_sleep(ssusb); + if (ret) + goto sleep_err; - ssusb_host_disable(ssusb, true); ssusb_phy_power_off(ssusb); clk_bulk_disable_unprepare(BULK_CLKS_CNT, ssusb->clks); ssusb_wakeup_set(ssusb, true); - return 0; + +sleep_err: + resume_ip_and_ports(ssusb, msg); +err: + return ret; } -static int __maybe_unused mtu3_resume(struct device *dev) +static int mtu3_resume_common(struct device *dev, pm_message_t msg) { struct ssusb_mtk *ssusb = dev_get_drvdata(dev); int ret; dev_dbg(dev, "%s\n", __func__); - if (!ssusb->is_host) - return 0; - ssusb_wakeup_set(ssusb, false); ret = clk_bulk_prepare_enable(BULK_CLKS_CNT, ssusb->clks); if (ret) @@ -431,9 +529,7 @@ static int __maybe_unused mtu3_resume(struct device *dev) if (ret) goto phy_err; - ssusb_host_enable(ssusb); - - return 0; + return resume_ip_and_ports(ssusb, msg); phy_err: clk_bulk_disable_unprepare(BULK_CLKS_CNT, ssusb->clks); @@ -441,8 +537,36 @@ clks_err: return ret; } +static int __maybe_unused mtu3_suspend(struct device *dev) +{ + return mtu3_suspend_common(dev, PMSG_SUSPEND); +} + +static int __maybe_unused mtu3_resume(struct device *dev) +{ + return mtu3_resume_common(dev, PMSG_SUSPEND); +} + +static int __maybe_unused mtu3_runtime_suspend(struct device *dev) +{ + if (!device_may_wakeup(dev)) + return 0; + + return mtu3_suspend_common(dev, PMSG_AUTO_SUSPEND); +} + +static int __maybe_unused mtu3_runtime_resume(struct device *dev) +{ + if (!device_may_wakeup(dev)) + return 0; + + return mtu3_resume_common(dev, PMSG_AUTO_SUSPEND); +} + static const struct dev_pm_ops mtu3_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(mtu3_suspend, mtu3_resume) + SET_RUNTIME_PM_OPS(mtu3_runtime_suspend, + mtu3_runtime_resume, NULL) }; #define DEV_PM_OPS (IS_ENABLED(CONFIG_PM) ? &mtu3_pm_ops : NULL) diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 5892f3ce0cdc..ce9fc46c9266 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -890,23 +890,22 @@ static int dsps_probe(struct platform_device *pdev) if (!glue->usbss_base) return -ENXIO; - if (usb_get_dr_mode(&pdev->dev) == USB_DR_MODE_PERIPHERAL) { - ret = dsps_setup_optional_vbus_irq(pdev, glue); - if (ret) - goto err_iounmap; - } - platform_set_drvdata(pdev, glue); pm_runtime_enable(&pdev->dev); ret = dsps_create_musb_pdev(glue, pdev); if (ret) goto err; + if (usb_get_dr_mode(&pdev->dev) == USB_DR_MODE_PERIPHERAL) { + ret = dsps_setup_optional_vbus_irq(pdev, glue); + if (ret) + goto err; + } + return 0; err: pm_runtime_disable(&pdev->dev); -err_iounmap: iounmap(glue->usbss_base); return ret; } diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c index f34c9437a182..972704262b02 100644 --- a/drivers/usb/phy/phy-fsl-usb.c +++ b/drivers/usb/phy/phy-fsl-usb.c @@ -873,6 +873,8 @@ int usb_otg_start(struct platform_device *pdev) /* request irq */ p_otg->irq = platform_get_irq(pdev, 0); + if (p_otg->irq < 0) + return p_otg->irq; status = request_irq(p_otg->irq, fsl_otg_isr, IRQF_SHARED, driver_name, p_otg); if (status) { diff --git a/drivers/usb/phy/phy-isp1301-omap.c b/drivers/usb/phy/phy-isp1301-omap.c index f3e9b3b6ac3e..190699b38b41 100644 --- a/drivers/usb/phy/phy-isp1301-omap.c +++ b/drivers/usb/phy/phy-isp1301-omap.c @@ -12,7 +12,7 @@ #include <linux/slab.h> #include <linux/interrupt.h> #include <linux/platform_device.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> #include <linux/usb.h> diff --git a/drivers/usb/phy/phy-tahvo.c b/drivers/usb/phy/phy-tahvo.c index baebb1f5a973..a3e043e3e4aa 100644 --- a/drivers/usb/phy/phy-tahvo.c +++ b/drivers/usb/phy/phy-tahvo.c @@ -393,7 +393,9 @@ static int tahvo_usb_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, tu); - tu->irq = platform_get_irq(pdev, 0); + tu->irq = ret = platform_get_irq(pdev, 0); + if (ret < 0) + return ret; ret = request_threaded_irq(tu->irq, NULL, tahvo_usb_vbus_interrupt, IRQF_ONESHOT, "tahvo-vbus", tu); diff --git a/drivers/usb/phy/phy-twl6030-usb.c b/drivers/usb/phy/phy-twl6030-usb.c index 8ba6c5a91557..ab3c38a7d8ac 100644 --- a/drivers/usb/phy/phy-twl6030-usb.c +++ b/drivers/usb/phy/phy-twl6030-usb.c @@ -348,6 +348,11 @@ static int twl6030_usb_probe(struct platform_device *pdev) twl->irq2 = platform_get_irq(pdev, 1); twl->linkstat = MUSB_UNKNOWN; + if (twl->irq1 < 0) + return twl->irq1; + if (twl->irq2 < 0) + return twl->irq2; + twl->comparator.set_vbus = twl6030_set_vbus; twl->comparator.start_srp = twl6030_start_srp; diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index a3c2b01ccf7b..10607e273879 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -366,7 +366,7 @@ static int usbhs_dcp_dir_switch_to_write(struct usbhs_pkt *pkt, int *is_done) ret = usbhsf_fifo_select(pipe, fifo, 1); if (ret < 0) { - dev_err(dev, "%s() faile\n", __func__); + dev_err(dev, "%s() failed\n", __func__); return ret; } diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 5d05de666597..a4d37205df54 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -278,7 +278,7 @@ struct pd_mode_data { * @req_max_curr: Requested max current of the port partner * @req_out_volt: Requested output voltage to the port partner * @req_op_curr: Requested operating current to the port partner - * @supported: Parter has atleast one APDO hence supports PPS + * @supported: Parter has at least one APDO hence supports PPS * @active: PPS mode is active */ struct pd_pps_data { @@ -316,6 +316,7 @@ struct tcpm_port { struct typec_partner *partner; enum typec_cc_status cc_req; + enum typec_cc_status src_rp; /* work only if pd_supported == false */ enum typec_cc_status cc1; enum typec_cc_status cc2; @@ -323,6 +324,7 @@ struct tcpm_port { bool attached; bool connected; + bool pd_supported; enum typec_port_type port_type; /* @@ -817,6 +819,9 @@ static enum typec_cc_status tcpm_rp_cc(struct tcpm_port *port) int nr_pdo = port->nr_src_pdo; int i; + if (!port->pd_supported) + return port->src_rp; + /* * Search for first entry with matching voltage. * It should report the maximum supported current. @@ -2063,7 +2068,7 @@ enum pdo_err { static const char * const pdo_err_msg[] = { [PDO_ERR_NO_VSAFE5V] = - " err: source/sink caps should atleast have vSafe5V", + " err: source/sink caps should at least have vSafe5V", [PDO_ERR_VSAFE5V_NOT_FIRST] = " err: vSafe5V Fixed Supply Object Shall always be the first object", [PDO_ERR_PDO_TYPE_NOT_IN_ORDER] = @@ -3571,9 +3576,11 @@ static int tcpm_src_attach(struct tcpm_port *port) if (ret < 0) return ret; - ret = port->tcpc->set_pd_rx(port->tcpc, true); - if (ret < 0) - goto out_disable_mux; + if (port->pd_supported) { + ret = port->tcpc->set_pd_rx(port->tcpc, true); + if (ret < 0) + goto out_disable_mux; + } /* * USB Type-C specification, version 1.2, @@ -3603,7 +3610,8 @@ static int tcpm_src_attach(struct tcpm_port *port) out_disable_vconn: tcpm_set_vconn(port, false); out_disable_pd: - port->tcpc->set_pd_rx(port->tcpc, false); + if (port->pd_supported) + port->tcpc->set_pd_rx(port->tcpc, false); out_disable_mux: tcpm_mux_set(port, TYPEC_STATE_SAFE, USB_ROLE_NONE, TYPEC_ORIENTATION_NONE); @@ -3807,6 +3815,20 @@ static enum typec_pwr_opmode tcpm_get_pwr_opmode(enum typec_cc_status cc) } } +static enum typec_cc_status tcpm_pwr_opmode_to_rp(enum typec_pwr_opmode opmode) +{ + switch (opmode) { + case TYPEC_PWR_MODE_USB: + return TYPEC_CC_RP_DEF; + case TYPEC_PWR_MODE_1_5A: + return TYPEC_CC_RP_1_5; + case TYPEC_PWR_MODE_3_0A: + case TYPEC_PWR_MODE_PD: + default: + return TYPEC_CC_RP_3_0; + } +} + static void run_state_machine(struct tcpm_port *port) { int ret; @@ -3917,6 +3939,10 @@ static void run_state_machine(struct tcpm_port *port) if (port->ams == POWER_ROLE_SWAP || port->ams == FAST_ROLE_SWAP) tcpm_ams_finish(port); + if (!port->pd_supported) { + tcpm_set_state(port, SRC_READY, 0); + break; + } port->upcoming_state = SRC_SEND_CAPABILITIES; tcpm_ams_start(port, POWER_NEGOTIATION); break; @@ -4164,7 +4190,10 @@ static void run_state_machine(struct tcpm_port *port) current_lim = PD_P_SNK_STDBY_MW / 5; tcpm_set_current_limit(port, current_lim, 5000); tcpm_set_charge(port, true); - tcpm_set_state(port, SNK_WAIT_CAPABILITIES, 0); + if (!port->pd_supported) + tcpm_set_state(port, SNK_READY, 0); + else + tcpm_set_state(port, SNK_WAIT_CAPABILITIES, 0); break; } /* @@ -4392,7 +4421,8 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_vbus(port, true); if (port->ams == HARD_RESET) tcpm_ams_finish(port); - port->tcpc->set_pd_rx(port->tcpc, true); + if (port->pd_supported) + port->tcpc->set_pd_rx(port->tcpc, true); tcpm_set_attached_state(port, true); tcpm_set_state(port, SRC_UNATTACHED, PD_T_PS_SOURCE_ON); break; @@ -5894,6 +5924,7 @@ EXPORT_SYMBOL_GPL(tcpm_tcpc_reset); static int tcpm_fw_get_caps(struct tcpm_port *port, struct fwnode_handle *fwnode) { + const char *opmode_str; const char *cap_str; int ret; u32 mw, frs_current; @@ -5928,22 +5959,37 @@ static int tcpm_fw_get_caps(struct tcpm_port *port, return ret; port->typec_caps.type = ret; port->port_type = port->typec_caps.type; + port->pd_supported = !fwnode_property_read_bool(fwnode, "pd-disable"); port->slow_charger_loop = fwnode_property_read_bool(fwnode, "slow-charger-loop"); if (port->port_type == TYPEC_PORT_SNK) goto sink; - /* Get source pdos */ - ret = fwnode_property_count_u32(fwnode, "source-pdos"); - if (ret <= 0) - return -EINVAL; + /* Get Source PDOs for the PD port or Source Rp value for the non-PD port */ + if (port->pd_supported) { + ret = fwnode_property_count_u32(fwnode, "source-pdos"); + if (ret == 0) + return -EINVAL; + else if (ret < 0) + return ret; - port->nr_src_pdo = min(ret, PDO_MAX_OBJECTS); - ret = fwnode_property_read_u32_array(fwnode, "source-pdos", - port->src_pdo, port->nr_src_pdo); - if ((ret < 0) || tcpm_validate_caps(port, port->src_pdo, - port->nr_src_pdo)) - return -EINVAL; + port->nr_src_pdo = min(ret, PDO_MAX_OBJECTS); + ret = fwnode_property_read_u32_array(fwnode, "source-pdos", + port->src_pdo, port->nr_src_pdo); + if (ret) + return ret; + ret = tcpm_validate_caps(port, port->src_pdo, port->nr_src_pdo); + if (ret) + return ret; + } else { + ret = fwnode_property_read_string(fwnode, "typec-power-opmode", &opmode_str); + if (ret) + return ret; + ret = typec_find_pwr_opmode(opmode_str); + if (ret < 0) + return ret; + port->src_rp = tcpm_pwr_opmode_to_rp(ret); + } if (port->port_type == TYPEC_PORT_SRC) return 0; @@ -5957,6 +6003,11 @@ static int tcpm_fw_get_caps(struct tcpm_port *port, if (port->typec_caps.prefer_role < 0) return -EINVAL; sink: + port->self_powered = fwnode_property_read_bool(fwnode, "self-powered"); + + if (!port->pd_supported) + return 0; + /* Get sink pdos */ ret = fwnode_property_count_u32(fwnode, "sink-pdos"); if (ret <= 0) @@ -5973,9 +6024,7 @@ sink: return -EINVAL; port->operating_snk_mw = mw / 1000; - port->self_powered = fwnode_property_read_bool(fwnode, "self-powered"); - - /* FRS can only be supported byb DRP ports */ + /* FRS can only be supported by DRP ports */ if (port->port_type == TYPEC_PORT_DRP) { ret = fwnode_property_read_u32(fwnode, "new-source-frs-typec-current", &frs_current); diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c index 4ba6bcdaa8e9..233265550fc6 100644 --- a/drivers/usb/usbip/vhci_hcd.c +++ b/drivers/usb/usbip/vhci_hcd.c @@ -455,8 +455,14 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, vhci_hcd->port_status[rhport] &= ~(1 << USB_PORT_FEAT_RESET); vhci_hcd->re_timeout = 0; + /* + * A few drivers do usb reset during probe when + * the device could be in VDEV_ST_USED state + */ if (vhci_hcd->vdev[rhport].ud.status == - VDEV_ST_NOTASSIGNED) { + VDEV_ST_NOTASSIGNED || + vhci_hcd->vdev[rhport].ud.status == + VDEV_ST_USED) { usbip_dbg_vhci_rh( " enable rhport %d (status %u)\n", rhport, @@ -945,7 +951,8 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) return 0; } -static void vhci_device_unlink_cleanup(struct vhci_device *vdev) +static void vhci_cleanup_unlink_list(struct vhci_device *vdev, + struct list_head *unlink_list) { struct vhci_hcd *vhci_hcd = vdev_to_vhci_hcd(vdev); struct usb_hcd *hcd = vhci_hcd_to_hcd(vhci_hcd); @@ -956,25 +963,11 @@ static void vhci_device_unlink_cleanup(struct vhci_device *vdev) spin_lock_irqsave(&vhci->lock, flags); spin_lock(&vdev->priv_lock); - list_for_each_entry_safe(unlink, tmp, &vdev->unlink_tx, list) { - pr_info("unlink cleanup tx %lu\n", unlink->unlink_seqnum); - list_del(&unlink->list); - kfree(unlink); - } - - while (!list_empty(&vdev->unlink_rx)) { + list_for_each_entry_safe(unlink, tmp, unlink_list, list) { struct urb *urb; - unlink = list_first_entry(&vdev->unlink_rx, struct vhci_unlink, - list); - - /* give back URB of unanswered unlink request */ - pr_info("unlink cleanup rx %lu\n", unlink->unlink_seqnum); - urb = pickup_urb_and_free_priv(vdev, unlink->unlink_seqnum); if (!urb) { - pr_info("the urb (seqnum %lu) was already given back\n", - unlink->unlink_seqnum); list_del(&unlink->list); kfree(unlink); continue; @@ -1001,6 +994,15 @@ static void vhci_device_unlink_cleanup(struct vhci_device *vdev) spin_unlock_irqrestore(&vhci->lock, flags); } +static void vhci_device_unlink_cleanup(struct vhci_device *vdev) +{ + /* give back URB of unsent unlink request */ + vhci_cleanup_unlink_list(vdev, &vdev->unlink_tx); + + /* give back URB of unanswered unlink request */ + vhci_cleanup_unlink_list(vdev, &vdev->unlink_rx); +} + /* * The important thing is that only one context begins cleanup. * This is why error handling and cleanup become simple. diff --git a/include/linux/of.h b/include/linux/of.h index 9c2e71e202d1..0e786b60bd5d 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -946,6 +946,11 @@ static inline int of_machine_is_compatible(const char *compat) return 0; } +static inline int of_add_property(struct device_node *np, struct property *prop) +{ + return 0; +} + static inline int of_remove_property(struct device_node *np, struct property *prop) { return 0; diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index e7c96c37174f..124e13cb1469 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -468,6 +468,7 @@ static inline struct tb_xdomain *tb_service_parent(struct tb_service *svc) * @interrupt_work: Work scheduled to handle ring interrupt when no * MSI-X is used. * @hop_count: Number of rings (end point hops) supported by NHI. + * @quirks: NHI specific quirks if any */ struct tb_nhi { spinlock_t lock; @@ -480,6 +481,7 @@ struct tb_nhi { bool going_away; struct work_struct interrupt_work; u32 hop_count; + unsigned long quirks; }; /** diff --git a/include/linux/usb/audio-v2.h b/include/linux/usb/audio-v2.h index ead8c9a47c6a..8fc2abd7aecb 100644 --- a/include/linux/usb/audio-v2.h +++ b/include/linux/usb/audio-v2.h @@ -156,6 +156,20 @@ struct uac2_feature_unit_descriptor { __u8 bmaControls[]; /* variable length */ } __attribute__((packed)); +#define UAC2_DT_FEATURE_UNIT_SIZE(ch) (6 + ((ch) + 1) * 4) + +/* As above, but more useful for defining your own descriptors: */ +#define DECLARE_UAC2_FEATURE_UNIT_DESCRIPTOR(ch) \ +struct uac2_feature_unit_descriptor_##ch { \ + __u8 bLength; \ + __u8 bDescriptorType; \ + __u8 bDescriptorSubtype; \ + __u8 bUnitID; \ + __u8 bSourceID; \ + __le32 bmaControls[ch + 1]; \ + __u8 iFeature; \ +} __packed + /* 4.7.2.10 Effect Unit Descriptor */ struct uac2_effect_unit_descriptor { diff --git a/include/linux/usb/ehci_def.h b/include/linux/usb/ehci_def.h index 78e006355557..c892c5bc6638 100644 --- a/include/linux/usb/ehci_def.h +++ b/include/linux/usb/ehci_def.h @@ -45,6 +45,7 @@ struct ehci_caps { #define HCS_PORTROUTED(p) ((p)&(1 << 7)) /* true: port routing */ #define HCS_PPC(p) ((p)&(1 << 4)) /* true: port power control */ #define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */ +#define HCS_N_PORTS_MAX 15 /* N_PORTS valid 0x1-0xF */ u32 hcc_params; /* HCCPARAMS - offset 0x8 */ /* EHCI 1.1 addendum */ @@ -126,8 +127,9 @@ struct ehci_regs { u32 configured_flag; #define FLAG_CF (1<<0) /* true: we'll support "high speed" */ - /* PORTSC: offset 0x44 */ - u32 port_status[0]; /* up to N_PORTS */ + union { + /* PORTSC: offset 0x44 */ + u32 port_status[HCS_N_PORTS_MAX]; /* up to N_PORTS */ /* EHCI 1.1 addendum */ #define PORTSC_SUSPEND_STS_ACK 0 #define PORTSC_SUSPEND_STS_NYET 1 @@ -164,28 +166,35 @@ struct ehci_regs { #define PORT_CSC (1<<1) /* connect status change */ #define PORT_CONNECT (1<<0) /* device connected */ #define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC) - - u32 reserved3[9]; - - /* USBMODE: offset 0x68 */ - u32 usbmode; /* USB Device mode */ + struct { + u32 reserved3[9]; + /* USBMODE: offset 0x68 */ + u32 usbmode; /* USB Device mode */ + }; #define USBMODE_SDIS (1<<3) /* Stream disable */ #define USBMODE_BE (1<<2) /* BE/LE endianness select */ #define USBMODE_CM_HC (3<<0) /* host controller mode */ #define USBMODE_CM_IDLE (0<<0) /* idle state */ - - u32 reserved4[6]; + }; /* Moorestown has some non-standard registers, partially due to the fact that * its EHCI controller has both TT and LPM support. HOSTPCx are extensions to * PORTSCx */ - /* HOSTPC: offset 0x84 */ - u32 hostpc[0]; /* HOSTPC extension */ + union { + struct { + u32 reserved4; + /* HOSTPC: offset 0x84 */ + u32 hostpc[HCS_N_PORTS_MAX]; #define HOSTPC_PHCD (1<<22) /* Phy clock disable */ #define HOSTPC_PSPD (3<<25) /* Port speed detection */ + }; + + /* Broadcom-proprietary USB_EHCI_INSNREG00 @ 0x80 */ + u32 brcm_insnreg[4]; + }; - u32 reserved5[17]; + u32 reserved5[2]; /* USBMODE_EX: offset 0xc8 */ u32 usbmode_ex; /* USB Device mode extension */ diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 75c7538e350a..10fe57cf40be 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -329,6 +329,7 @@ struct usb_gadget_ops { struct usb_ep *(*match_ep)(struct usb_gadget *, struct usb_endpoint_descriptor *, struct usb_ss_ep_comp_descriptor *); + int (*check_config)(struct usb_gadget *gadget); }; /** @@ -491,7 +492,7 @@ extern char *usb_get_gadget_udc_name(void); */ static inline size_t usb_ep_align(struct usb_ep *ep, size_t len) { - int max_packet_size = (size_t)usb_endpoint_maxp(ep->desc) & 0x7ff; + int max_packet_size = (size_t)usb_endpoint_maxp(ep->desc); return round_up(len, max_packet_size); } @@ -608,6 +609,7 @@ int usb_gadget_connect(struct usb_gadget *gadget); int usb_gadget_disconnect(struct usb_gadget *gadget); int usb_gadget_deactivate(struct usb_gadget *gadget); int usb_gadget_activate(struct usb_gadget *gadget); +int usb_gadget_check_config(struct usb_gadget *gadget); #else static inline int usb_gadget_frame_number(struct usb_gadget *gadget) { return 0; } @@ -631,6 +633,8 @@ static inline int usb_gadget_deactivate(struct usb_gadget *gadget) { return 0; } static inline int usb_gadget_activate(struct usb_gadget *gadget) { return 0; } +static inline int usb_gadget_check_config(struct usb_gadget *gadget) +{ return 0; } #endif /* CONFIG_USB_GADGET */ /*-------------------------------------------------------------------------*/ diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index 7ceeecbb9e02..6475f880be37 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -128,5 +128,6 @@ enum usb_dr_mode { * and returns the corresponding enum usb_dr_mode */ extern enum usb_dr_mode usb_get_dr_mode(struct device *dev); +extern enum usb_dr_mode usb_get_role_switch_default_mode(struct device *dev); #endif /* __LINUX_USB_OTG_H */ |