diff --git a/Documentation/ABI/testing/sysfs-class-usb_power_delivery b/Documentation/ABI/testing/sysfs-class-usb_power_delivery index 61d233c320ea..c754458a527e 100644 --- a/Documentation/ABI/testing/sysfs-class-usb_power_delivery +++ b/Documentation/ABI/testing/sysfs-class-usb_power_delivery @@ -254,3 +254,31 @@ Contact: Heikki Krogerus Description: The PPS Power Limited bit indicates whether or not the source supply will exceed the rated output power if requested. + +Standard Power Range (SPR) Adjustable Voltage Supplies + +What: /sys/class/usb_power_delivery/...//:spr_adjustable_voltage_supply +Date: Oct 2025 +Contact: Badhri Jagan Sridharan +Description: + Adjustable Voltage Supply (AVS) Augmented PDO (APDO). + +What: /sys/class/usb_power_delivery/...//:spr_adjustable_voltage_supply/maximum_current_9V_to_15V +Date: Oct 2025 +Contact: Badhri Jagan Sridharan +Description: + Maximum Current for 9V to 15V range in milliamperes. + +What: /sys/class/usb_power_delivery/...//:spr_adjustable_voltage_supply/maximum_current_15V_to_20V +Date: Oct 2025 +Contact: Badhri Jagan Sridharan +Description: + Maximum Current for greater than 15V till 20V range in + milliamperes. + +What: /sys/class/usb_power_delivery/...//:spr_adjustable_voltage_supply/peak_current +Date: Oct 2025 +Contact: Badhri Jagan Sridharan +Description: + This file shows the value of the Adjustable Voltage Supply Peak Current + Capability field. diff --git a/Documentation/admin-guide/thunderbolt.rst b/Documentation/admin-guide/thunderbolt.rst index 102c693c8f81..07303c1346fb 100644 --- a/Documentation/admin-guide/thunderbolt.rst +++ b/Documentation/admin-guide/thunderbolt.rst @@ -203,10 +203,10 @@ host controller or a device, it is important that the firmware can be upgraded to the latest where possible bugs in it have been fixed. Typically OEMs provide this firmware from their support site. -There is also a central site which has links where to download firmware -for some machines: - - `Thunderbolt Updates `_ +Currently, recommended method of updating firmware is through "fwupd" tool. +It uses LVFS (Linux Vendor Firmware Service) portal by default to get the +latest firmware from hardware vendors and updates connected devices if found +compatible. For details refer to: https://github.com/fwupd/fwupd. Before you upgrade firmware on a device, host or retimer, please make sure it is a suitable upgrade. Failing to do that may render the device @@ -215,18 +215,40 @@ tools! Host NVM upgrade on Apple Macs is not supported. -Once the NVM image has been downloaded, you need to plug in a -Thunderbolt device so that the host controller appears. It does not -matter which device is connected (unless you are upgrading NVM on a -device - then you need to connect that particular device). +Fwupd is installed by default. If you don't have it on your system, simply +use your distro package manager to get it. + +To see possible updates through fwupd, you need to plug in a Thunderbolt +device so that the host controller appears. It does not matter which +device is connected (unless you are upgrading NVM on a device - then you +need to connect that particular device). Note an OEM-specific method to power the controller up ("force power") may be available for your system in which case there is no need to plug in a Thunderbolt device. -After that we can write the firmware to the non-active parts of the NVM -of the host or device. As an example here is how Intel NUC6i7KYK (Skull -Canyon) Thunderbolt controller NVM is upgraded:: +Updating firmware using fwupd is straightforward - refer to official +readme on fwupd github. + +If firmware image is written successfully, the device shortly disappears. +Once it comes back, the driver notices it and initiates a full power +cycle. After a while device appears again and this time it should be +fully functional. + +Device of interest should display new version under "Current version" +and "Update State: Success" in fwupd's interface. + +Upgrading firmware manually +--------------------------------------------------------------- +If possible, use fwupd to updated the firmware. However, if your device OEM +has not uploaded the firmware to LVFS, but it is available for download +from their side, you can use method below to directly upgrade the +firmware. + +Manual firmware update can be done with 'dd' tool. To update firmware +using this method, you need to write it to the non-active parts of NVM +of the host or device. Example on how to update Intel NUC6i7KYK +(Skull Canyon) Thunderbolt controller NVM:: # dd if=KYK_TBT_FW_0018.bin of=/sys/bus/thunderbolt/devices/0-0/nvm_non_active0/nvmem @@ -235,10 +257,8 @@ upgrade process as follows:: # echo 1 > /sys/bus/thunderbolt/devices/0-0/nvm_authenticate -If no errors are returned, the host controller shortly disappears. Once -it comes back the driver notices it and initiates a full power cycle. -After a while the host controller appears again and this time it should -be fully functional. +If no errors are returned, device should behave as described in previous +section. We can verify that the new NVM firmware is active by running the following commands:: diff --git a/Documentation/devicetree/bindings/usb/apple,dwc3.yaml b/Documentation/devicetree/bindings/usb/apple,dwc3.yaml new file mode 100644 index 000000000000..f70c33f32c5d --- /dev/null +++ b/Documentation/devicetree/bindings/usb/apple,dwc3.yaml @@ -0,0 +1,80 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/apple,dwc3.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Apple Silicon DWC3 USB controller + +maintainers: + - Sven Peter + +description: + Apple Silicon SoCs use a Synopsys DesignWare DWC3 based controller for each of + their Type-C ports. + +allOf: + - $ref: snps,dwc3-common.yaml# + +properties: + compatible: + oneOf: + - items: + - enum: + - apple,t6000-dwc3 + - apple,t6020-dwc3 + - apple,t8112-dwc3 + - const: apple,t8103-dwc3 + - const: apple,t8103-dwc3 + + reg: + items: + - description: Core DWC3 region + - description: Apple-specific DWC3 region + + reg-names: + items: + - const: dwc3-core + - const: dwc3-apple + + interrupts: + maxItems: 1 + + iommus: + maxItems: 2 + + resets: + maxItems: 1 + + power-domains: + maxItems: 1 + +required: + - compatible + - reg + - reg-names + - interrupts + - iommus + - resets + - power-domains + - usb-role-switch + +unevaluatedProperties: false + +examples: + - | + #include + #include + + usb@82280000 { + compatible = "apple,t8103-dwc3"; + reg = <0x82280000 0xcd00>, <0x8228cd00 0x3200>; + reg-names = "dwc3-core", "dwc3-apple"; + interrupts = ; + iommus = <&dwc3_0_dart_0 0>, <&dwc3_0_dart_1 1>; + + power-domains = <&ps_atc0_usb>; + resets = <&atcphy0>; + + usb-role-switch; + }; diff --git a/Documentation/devicetree/bindings/usb/dwc3-xilinx.yaml b/Documentation/devicetree/bindings/usb/dwc3-xilinx.yaml index 36f5c644d959..d6823ef5f9a7 100644 --- a/Documentation/devicetree/bindings/usb/dwc3-xilinx.yaml +++ b/Documentation/devicetree/bindings/usb/dwc3-xilinx.yaml @@ -47,6 +47,7 @@ properties: - const: ref_clk resets: + minItems: 1 description: A list of phandles for resets listed in reset-names. @@ -56,6 +57,7 @@ properties: - description: USB APB reset reset-names: + minItems: 1 items: - const: usb_crst - const: usb_hibrst @@ -95,6 +97,26 @@ required: - resets - reset-names +allOf: + - if: + properties: + compatible: + contains: + enum: + - xlnx,versal-dwc3 + then: + properties: + resets: + maxItems: 1 + reset-names: + maxItems: 1 + else: + properties: + resets: + minItems: 3 + reset-names: + minItems: 3 + additionalProperties: false examples: diff --git a/Documentation/devicetree/bindings/usb/eswin,eic7700-usb.yaml b/Documentation/devicetree/bindings/usb/eswin,eic7700-usb.yaml new file mode 100644 index 000000000000..41c3b1b98991 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/eswin,eic7700-usb.yaml @@ -0,0 +1,94 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/eswin,eic7700-usb.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ESWIN EIC7700 SoC Usb Controller + +maintainers: + - Wei Yang + - Senchuan Zhang + - Hang Cao + +description: + The Usb controller on EIC7700 SoC. + +allOf: + - $ref: snps,dwc3-common.yaml# + +properties: + compatible: + const: eswin,eic7700-dwc3 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + interrupt-names: + items: + - const: peripheral + + clocks: + maxItems: 3 + + clock-names: + items: + - const: aclk + - const: cfg + - const: usb_en + + resets: + maxItems: 2 + + reset-names: + items: + - const: vaux + - const: usb_rst + + eswin,hsp-sp-csr: + description: + HSP CSR is to control and get status of different high-speed peripherals + (such as Ethernet, USB, SATA, etc.) via register, which can tune + board-level's parameters of PHY, etc. + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: phandle to HSP Register Controller hsp_sp_csr node. + - description: USB bus register offset. + - description: AXI low power register offset. + +required: + - compatible + - reg + - clocks + - clock-names + - interrupts + - interrupt-names + - resets + - reset-names + - eswin,hsp-sp-csr + +unevaluatedProperties: false + +examples: + - | + usb@50480000 { + compatible = "eswin,eic7700-dwc3"; + reg = <0x50480000 0x10000>; + clocks = <&clock 135>, + <&clock 136>, + <&hspcrg 18>; + clock-names = "aclk", "cfg", "usb_en"; + interrupt-parent = <&plic>; + interrupts = <85>; + interrupt-names = "peripheral"; + resets = <&reset 84>, <&hspcrg 2>; + reset-names = "vaux", "usb_rst"; + dr_mode = "peripheral"; + maximum-speed = "high-speed"; + phy_type = "utmi"; + eswin,hsp-sp-csr = <&hsp_sp_csr 0x800 0x818>; + }; diff --git a/Documentation/devicetree/bindings/usb/fsl,ls1028a.yaml b/Documentation/devicetree/bindings/usb/fsl,ls1028a.yaml index a44bdf391887..4784f057264a 100644 --- a/Documentation/devicetree/bindings/usb/fsl,ls1028a.yaml +++ b/Documentation/devicetree/bindings/usb/fsl,ls1028a.yaml @@ -9,21 +9,19 @@ title: Freescale layerscape SuperSpeed DWC3 USB SoC controller maintainers: - Frank Li -select: - properties: - compatible: - contains: - enum: - - fsl,ls1028a-dwc3 - required: - - compatible - properties: compatible: - items: - - enum: - - fsl,ls1028a-dwc3 - - const: snps,dwc3 + oneOf: + - items: + - enum: + - fsl,ls1012a-dwc3 + - fsl,ls1043a-dwc3 + - fsl,ls1046a-dwc3 + - fsl,ls1088a-dwc3 + - fsl,ls208xa-dwc3 + - fsl,lx2160a-dwc3 + - const: fsl,ls1028a-dwc3 + - const: fsl,ls1028a-dwc3 reg: maxItems: 1 @@ -31,6 +29,11 @@ properties: interrupts: maxItems: 1 + iommus: + maxItems: 1 + + dma-coherent: true + unevaluatedProperties: false required: @@ -39,14 +42,14 @@ required: - interrupts allOf: - - $ref: snps,dwc3.yaml# + - $ref: snps,dwc3-common.yaml# examples: - | #include usb@fe800000 { - compatible = "fsl,ls1028a-dwc3", "snps,dwc3"; + compatible = "fsl,ls1028a-dwc3"; reg = <0xfe800000 0x100000>; interrupts = ; }; diff --git a/Documentation/devicetree/bindings/usb/fsl,usbmisc.yaml b/Documentation/devicetree/bindings/usb/fsl,usbmisc.yaml index ca677d1a8274..d06efe4dbb3b 100644 --- a/Documentation/devicetree/bindings/usb/fsl,usbmisc.yaml +++ b/Documentation/devicetree/bindings/usb/fsl,usbmisc.yaml @@ -36,6 +36,7 @@ properties: - fsl,imx8mm-usbmisc - fsl,imx8mn-usbmisc - fsl,imx8ulp-usbmisc + - fsl,imx94-usbmisc - fsl,imx95-usbmisc - const: fsl,imx7d-usbmisc - const: fsl,imx6q-usbmisc diff --git a/Documentation/devicetree/bindings/usb/generic-ehci.yaml b/Documentation/devicetree/bindings/usb/generic-ehci.yaml index 508d958e698c..4e84bead0232 100644 --- a/Documentation/devicetree/bindings/usb/generic-ehci.yaml +++ b/Documentation/devicetree/bindings/usb/generic-ehci.yaml @@ -46,6 +46,7 @@ properties: - aspeed,ast2400-ehci - aspeed,ast2500-ehci - aspeed,ast2600-ehci + - aspeed,ast2700-ehci - brcm,bcm3384-ehci - brcm,bcm63268-ehci - brcm,bcm6328-ehci diff --git a/Documentation/devicetree/bindings/usb/generic-xhci.yaml b/Documentation/devicetree/bindings/usb/generic-xhci.yaml index a2b94a138999..62678abd74b5 100644 --- a/Documentation/devicetree/bindings/usb/generic-xhci.yaml +++ b/Documentation/devicetree/bindings/usb/generic-xhci.yaml @@ -14,12 +14,15 @@ properties: oneOf: - description: Generic xHCI device const: generic-xhci - - description: Armada 37xx/375/38x/8k SoCs + - description: Armada 375/38x SoCs + items: + - enum: + - marvell,armada-375-xhci + - marvell,armada-380-xhci + - description: Armada 37xx/8k SoCs items: - enum: - marvell,armada3700-xhci - - marvell,armada-375-xhci - - marvell,armada-380-xhci - marvell,armada-8k-xhci - const: generic-xhci - description: Broadcom SoCs with power domains @@ -53,6 +56,14 @@ properties: dma-coherent: true + dr_mode: + enum: + - host + - otg + + iommus: + maxItems: 1 + power-domains: maxItems: 1 diff --git a/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.yaml b/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.yaml index 004d3ebec091..231e6f35a986 100644 --- a/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.yaml +++ b/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.yaml @@ -34,6 +34,7 @@ properties: - mediatek,mt8183-xhci - mediatek,mt8186-xhci - mediatek,mt8188-xhci + - mediatek,mt8189-xhci - mediatek,mt8192-xhci - mediatek,mt8195-xhci - mediatek,mt8365-xhci @@ -168,7 +169,8 @@ properties: 104 - used by mt8195, IP1, specific 1.04; 105 - used by mt8195, IP2, specific 1.05; 106 - used by mt8195, IP3, specific 1.06; - enum: [1, 2, 101, 102, 103, 104, 105, 106] + 110 - used by mt8189, IP4, specific 1.10; + enum: [1, 2, 101, 102, 103, 104, 105, 106, 110] mediatek,u3p-dis-msk: $ref: /schemas/types.yaml#/definitions/uint32 diff --git a/Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml b/Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml index d49a58d5478f..8cee7c5582f2 100644 --- a/Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml @@ -24,6 +24,8 @@ properties: compatible: items: - enum: + - qcom,glymur-dwc3 + - qcom,glymur-dwc3-mp - qcom,ipq4019-dwc3 - qcom,ipq5018-dwc3 - qcom,ipq5332-dwc3 @@ -32,6 +34,7 @@ properties: - qcom,ipq8064-dwc3 - qcom,ipq8074-dwc3 - qcom,ipq9574-dwc3 + - qcom,kaanapali-dwc3 - qcom,milos-dwc3 - qcom,msm8953-dwc3 - qcom,msm8994-dwc3 @@ -67,6 +70,7 @@ properties: - qcom,sm8450-dwc3 - qcom,sm8550-dwc3 - qcom,sm8650-dwc3 + - qcom,sm8750-dwc3 - qcom,x1e80100-dwc3 - qcom,x1e80100-dwc3-mp - const: qcom,snps-dwc3 @@ -200,6 +204,7 @@ allOf: contains: enum: - qcom,ipq9574-dwc3 + - qcom,kaanapali-dwc3 - qcom,msm8953-dwc3 - qcom,msm8996-dwc3 - qcom,msm8998-dwc3 @@ -213,6 +218,7 @@ allOf: - qcom,sdx65-dwc3 - qcom,sdx75-dwc3 - qcom,sm6350-dwc3 + - qcom,sm8750-dwc3 then: properties: clocks: @@ -387,6 +393,28 @@ allOf: - const: mock_utmi - const: xo + - if: + properties: + compatible: + contains: + enum: + - qcom,glymur-dwc3 + - qcom,glymur-dwc3-mp + + then: + properties: + clocks: + maxItems: 7 + clock-names: + items: + - const: cfg_noc + - const: core + - const: iface + - const: sleep + - const: mock_utmi + - const: noc_aggr_north + - const: noc_aggr_south + - if: properties: compatible: @@ -456,6 +484,7 @@ allOf: compatible: contains: enum: + - qcom,glymur-dwc3 - qcom,milos-dwc3 - qcom,x1e80100-dwc3 then: @@ -479,6 +508,7 @@ allOf: enum: - qcom,ipq4019-dwc3 - qcom,ipq8064-dwc3 + - qcom,kaanapali-dwc3 - qcom,msm8994-dwc3 - qcom,qcs615-dwc3 - qcom,qcs8300-dwc3 @@ -501,6 +531,7 @@ allOf: - qcom,sm8450-dwc3 - qcom,sm8550-dwc3 - qcom,sm8650-dwc3 + - qcom,sm8750-dwc3 then: properties: interrupts: @@ -521,6 +552,7 @@ allOf: compatible: contains: enum: + - qcom,glymur-dwc3-mp - qcom,sc8180x-dwc3-mp - qcom,x1e80100-dwc3-mp then: diff --git a/Documentation/devicetree/bindings/usb/renesas,rzg3e-xhci.yaml b/Documentation/devicetree/bindings/usb/renesas,rzg3e-xhci.yaml index 98260f9fb442..3f4b09e48ce0 100644 --- a/Documentation/devicetree/bindings/usb/renesas,rzg3e-xhci.yaml +++ b/Documentation/devicetree/bindings/usb/renesas,rzg3e-xhci.yaml @@ -4,14 +4,22 @@ $id: http://devicetree.org/schemas/usb/renesas,rzg3e-xhci.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Renesas RZ/G3E USB 3.2 Gen2 Host controller +title: Renesas USB 3.2 Gen2 Host controller maintainers: - Biju Das properties: compatible: - const: renesas,r9a09g047-xhci + oneOf: + - items: + - enum: + - renesas,r9a09g056-xhci # RZ/V2N + - renesas,r9a09g057-xhci # RZ/V2H(P) + - const: renesas,r9a09g047-xhci + + - items: + - const: renesas,r9a09g047-xhci # RZ/G3E reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/usb/samsung,exynos-dwc3.yaml b/Documentation/devicetree/bindings/usb/samsung,exynos-dwc3.yaml index 6d39e5066944..8af0143c3e47 100644 --- a/Documentation/devicetree/bindings/usb/samsung,exynos-dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/samsung,exynos-dwc3.yaml @@ -21,6 +21,9 @@ properties: - samsung,exynos7870-dwusb3 - samsung,exynos850-dwusb3 - samsung,exynosautov920-dwusb3 + - items: + - const: samsung,exynos8890-dwusb3 + - const: samsung,exynos7-dwusb3 - items: - const: samsung,exynos990-dwusb3 - const: samsung,exynos850-dwusb3 @@ -36,6 +39,9 @@ properties: minItems: 1 maxItems: 4 + power-domains: + maxItems: 1 + ranges: true '#size-cells': diff --git a/Documentation/devicetree/bindings/usb/ti,hd3ss3220.yaml b/Documentation/devicetree/bindings/usb/ti,hd3ss3220.yaml index bec1c8047bc0..06099e93c6c3 100644 --- a/Documentation/devicetree/bindings/usb/ti,hd3ss3220.yaml +++ b/Documentation/devicetree/bindings/usb/ti,hd3ss3220.yaml @@ -25,6 +25,14 @@ properties: interrupts: maxItems: 1 + id-gpios: + description: + An input gpio for USB ID pin. Upon detecting a UFP device, HD3SS3220 + will keep ID pin high if VBUS is not at VSafe0V. Once VBUS is at VSafe0V, + the HD3SS3220 will assert ID pin low. This is done to enforce Type-C + requirement that VBUS must be at VSafe0V before re-enabling VBUS. + maxItems: 1 + ports: $ref: /schemas/graph.yaml#/properties/ports description: OF graph bindings (specified in bindings/graph.txt) that model diff --git a/Documentation/devicetree/bindings/usb/usb-uhci.yaml b/Documentation/devicetree/bindings/usb/usb-uhci.yaml index d8336f72dc1f..e050ca203945 100644 --- a/Documentation/devicetree/bindings/usb/usb-uhci.yaml +++ b/Documentation/devicetree/bindings/usb/usb-uhci.yaml @@ -20,6 +20,7 @@ properties: - aspeed,ast2400-uhci - aspeed,ast2500-uhci - aspeed,ast2600-uhci + - aspeed,ast2700-uhci - const: generic-uhci reg: @@ -28,6 +29,9 @@ properties: interrupts: maxItems: 1 + resets: + maxItems: 1 + '#ports': $ref: /schemas/types.yaml#/definitions/uint32 @@ -50,6 +54,15 @@ allOf: required: - clocks + - if: + properties: + compatible: + contains: + const: aspeed,ast2700-uhci + then: + required: + - resets + unevaluatedProperties: false examples: diff --git a/MAINTAINERS b/MAINTAINERS index c7eb037dfac5..587f5fea8d6e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2477,6 +2477,7 @@ F: Documentation/devicetree/bindings/power/reset/apple,smc-reboot.yaml F: Documentation/devicetree/bindings/pwm/apple,s5l-fpwm.yaml F: Documentation/devicetree/bindings/spi/apple,spi.yaml F: Documentation/devicetree/bindings/spmi/apple,spmi.yaml +F: Documentation/devicetree/bindings/usb/apple,dwc3.yaml F: Documentation/devicetree/bindings/watchdog/apple,wdt.yaml F: Documentation/hwmon/macsmc-hwmon.rst F: arch/arm64/boot/dts/apple/ @@ -2503,6 +2504,7 @@ F: drivers/pwm/pwm-apple.c F: drivers/soc/apple/* F: drivers/spi/spi-apple.c F: drivers/spmi/spmi-apple-controller.c +F: drivers/usb/dwc3/dwc3-apple.c F: drivers/video/backlight/apple_dwi_bl.c F: drivers/watchdog/apple_wdt.c F: include/dt-bindings/interrupt-controller/apple-aic.h diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c index f92175ee3841..d7a535671404 100644 --- a/drivers/thunderbolt/ctl.c +++ b/drivers/thunderbolt/ctl.c @@ -412,7 +412,7 @@ static void tb_ctl_rx_submit(struct ctl_pkg *pkg) * We ignore failures during stop. * All rx packets are referenced * from ctl->rx_packets, so we do - * not loose them. + * not lose them. */ } diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c index 46a2a3550be7..45266ec72f88 100644 --- a/drivers/thunderbolt/debugfs.c +++ b/drivers/thunderbolt/debugfs.c @@ -201,7 +201,7 @@ static bool parse_line(char **line, u32 *offs, u32 *val, int short_fmt_len, #if IS_ENABLED(CONFIG_USB4_DEBUGFS_WRITE) /* * Path registers need to be written in double word pairs and they both must be - * read before written. This writes one double word in patch config space + * read before written. This writes one double word in path config space * following the spec flow. */ static int path_write_one(struct tb_port *port, u32 val, u32 offset) @@ -1196,7 +1196,7 @@ static int validate_margining(struct tb_margining *margining) { /* * For running on RX2 the link must be asymmetric with 3 - * receivers. Because this is can change dynamically, check it + * receivers. Because this can change dynamically, check it * here before we start the margining and report back error if * expectations are not met. */ diff --git a/drivers/thunderbolt/domain.c b/drivers/thunderbolt/domain.c index 83defc915d33..3ced37b4a869 100644 --- a/drivers/thunderbolt/domain.c +++ b/drivers/thunderbolt/domain.c @@ -376,7 +376,7 @@ struct tb *tb_domain_alloc(struct tb_nhi *nhi, int timeout_msec, size_t privsize struct tb *tb; /* - * Make sure the structure sizes map with that the hardware + * Make sure the structure sizes map with what the hardware * expects because bit-fields are being used. */ BUILD_BUG_ON(sizeof(struct tb_regs_switch_header) != 5 * 4); diff --git a/drivers/thunderbolt/eeprom.c b/drivers/thunderbolt/eeprom.c index 1af65fece495..5477b9437048 100644 --- a/drivers/thunderbolt/eeprom.c +++ b/drivers/thunderbolt/eeprom.c @@ -21,7 +21,7 @@ static int tb_eeprom_ctl_write(struct tb_switch *sw, struct tb_eeprom_ctl *ctl) } /* - * tb_eeprom_ctl_write() - read control word + * tb_eeprom_ctl_read() - read control word */ static int tb_eeprom_ctl_read(struct tb_switch *sw, struct tb_eeprom_ctl *ctl) { diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c index f213d9174dc5..d339ba835376 100644 --- a/drivers/thunderbolt/icm.c +++ b/drivers/thunderbolt/icm.c @@ -787,7 +787,7 @@ icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr) * information might have changed for example by the * fact that a switch on a dual-link connection might * have been enumerated using the other link now. Make - * sure our book keeping matches that. + * sure our bookkeeping matches that. */ if (sw->depth == depth && sw_phy_port == phy_port && !!sw->authorized == authorized) { @@ -969,7 +969,7 @@ icm_fr_xdomain_connected(struct tb *tb, const struct icm_pkg_header *hdr) /* * Look if there already exists an XDomain in the same place - * than the new one and in that case remove it because it is + * as the new one and in that case remove it because it is * most likely another host that got disconnected. */ xd = tb_xdomain_find_by_link_depth(tb, link, depth); @@ -2000,7 +2000,7 @@ static int icm_driver_ready(struct tb *tb) if (icm->safe_mode) { tb_info(tb, "Thunderbolt host controller is in safe mode.\n"); tb_info(tb, "You need to update NVM firmware of the controller before it can be used.\n"); - tb_info(tb, "For latest updates check https://thunderbolttechnology.net/updates.\n"); + tb_info(tb, "Use fwupd tool to apply update. Check Documentation/admin-guide/thunderbolt.rst for details.\n"); return 0; } @@ -2171,7 +2171,7 @@ static int icm_runtime_resume_switch(struct tb_switch *sw) static int icm_runtime_resume(struct tb *tb) { /* - * We can reuse the same resume functionality than with system + * We can reuse the same resume functionality as with system * suspend. */ icm_complete(tb); diff --git a/drivers/thunderbolt/lc.c b/drivers/thunderbolt/lc.c index 0891d51ac2e9..4449c28cc5f1 100644 --- a/drivers/thunderbolt/lc.c +++ b/drivers/thunderbolt/lc.c @@ -558,7 +558,7 @@ static int tb_lc_dp_sink_available(struct tb_switch *sw, int sink) return ret; /* - * Sink is available for CM/SW to use if the allocation valie is + * Sink is available for CM/SW to use if the allocation value is * either 0 or 1. */ if (!sink) { diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index addb4a20d5ea..6d0c9d37c55d 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -712,7 +712,7 @@ void tb_ring_start(struct tb_ring *ring) ring_iowrite64desc(ring, ring->descriptors_dma, 0); if (ring->is_tx) { ring_iowrite32desc(ring, ring->size, 12); - ring_iowrite32options(ring, 0, 4); /* time releated ? */ + ring_iowrite32options(ring, 0, 4); ring_iowrite32options(ring, flags, 0); } else { u32 sof_eof_mask = ring->sof_mask << 16 | ring->eof_mask; diff --git a/drivers/thunderbolt/retimer.c b/drivers/thunderbolt/retimer.c index 3a0f486a24d5..13d64dbd2bc5 100644 --- a/drivers/thunderbolt/retimer.c +++ b/drivers/thunderbolt/retimer.c @@ -501,7 +501,7 @@ static struct tb_retimer *tb_port_find_retimer(struct tb_port *port, u8 index) * @add: If true also registers found retimers * * Brings the sideband into a state where retimers can be accessed. - * Then Tries to enumerate on-board retimers connected to @port. Found + * Then tries to enumerate on-board retimers connected to @port. Found * retimers are registered as children of @port if @add is set. Does * not scan for cable retimers for now. * diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 0e07904aa73b..b3948aad0b95 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -736,9 +736,9 @@ static int tb_init_port(struct tb_port *port) port->cap_usb4 = cap; /* - * USB4 ports the buffers allocated for the control path + * USB4 port buffers allocated for the control path * can be read from the path config space. Legacy - * devices we use hard-coded value. + * devices use hard-coded value. */ if (port->cap_usb4) { struct tb_regs_hop hop; @@ -3221,7 +3221,7 @@ int tb_switch_configure_link(struct tb_switch *sw) * @sw: Switch whose link is unconfigured * * Sets the link unconfigured so the @sw will be disconnected if the - * domain exists sleep. + * domain exits sleep. */ void tb_switch_unconfigure_link(struct tb_switch *sw) { diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 4a94cb406bdf..4f5f1dfc0fbf 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -322,7 +322,7 @@ static int tb_enable_tmu(struct tb_switch *sw) /* * If both routers at the end of the link are v2 we simply - * enable the enhanched uni-directional mode. That covers all + * enable the enhanced uni-directional mode. That covers all * the CL states. For v1 and before we need to use the normal * rate to allow CL1 (when supported). Otherwise we keep the TMU * running at the highest accuracy. @@ -538,7 +538,7 @@ static struct tb_tunnel *tb_find_first_usb3_tunnel(struct tb *tb, * @src_port: Source protocol adapter * @dst_port: Destination protocol adapter * @port: USB4 port the consumed bandwidth is calculated - * @consumed_up: Consumed upsream bandwidth (Mb/s) + * @consumed_up: Consumed upstream bandwidth (Mb/s) * @consumed_down: Consumed downstream bandwidth (Mb/s) * * Calculates consumed USB3 and PCIe bandwidth at @port between path @@ -589,7 +589,7 @@ static int tb_consumed_usb3_pcie_bandwidth(struct tb *tb, * @src_port: Source protocol adapter * @dst_port: Destination protocol adapter * @port: USB4 port the consumed bandwidth is calculated - * @consumed_up: Consumed upsream bandwidth (Mb/s) + * @consumed_up: Consumed upstream bandwidth (Mb/s) * @consumed_down: Consumed downstream bandwidth (Mb/s) * * Calculates consumed DP bandwidth at @port between path from @src_port @@ -1115,7 +1115,7 @@ static int tb_configure_asym(struct tb *tb, struct tb_port *src_port, /* * Here requested + consumed > threshold so we need to - * transtion the link into asymmetric now. + * transition the link into asymmetric now. */ ret = tb_switch_set_link_width(up->sw, width_up); if (ret) { @@ -1936,7 +1936,7 @@ static void tb_dp_tunnel_active(struct tb_tunnel *tunnel, void *data) */ tb_recalc_estimated_bandwidth(tb); /* - * In case of DP tunnel exists, change host + * In case DP tunnel exists, change host * router's 1st children TMU mode to HiFi for * CL0s to work. */ @@ -2636,7 +2636,7 @@ static int tb_alloc_dp_bandwidth(struct tb_tunnel *tunnel, int *requested_up, * the 10s already expired and we should * give the reserved back to others). */ - mod_delayed_work(system_wq, &group->release_work, + mod_delayed_work(system_percpu_wq, &group->release_work, msecs_to_jiffies(TB_RELEASE_BW_TIMEOUT)); } } @@ -2786,8 +2786,8 @@ static void tb_handle_dp_bandwidth_request(struct work_struct *work) * There is no request active so this means the * BW allocation mode was enabled from graphics * side. At this point we know that the graphics - * driver has read the DRPX capabilities so we - * can offer an better bandwidth estimatation. + * driver has read the DPRX capabilities so we + * can offer better bandwidth estimation. */ tb_port_dbg(in, "DPTX enabled bandwidth allocation mode, updating estimated bandwidth\n"); tb_recalc_estimated_bandwidth(tb); diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 8e2762ff8d51..e96474f17067 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -308,7 +308,7 @@ struct tb_port { * struct usb4_port - USB4 port device * @dev: Device for the port * @port: Pointer to the lane 0 adapter - * @can_offline: Does the port have necessary platform support to moved + * @can_offline: Does the port have necessary platform support to move * it into offline mode and back * @offline: The port is currently in offline mode * @margining: Pointer to margining structure if enabled @@ -355,7 +355,7 @@ struct tb_retimer { * struct tb_path_hop - routing information for a tb_path * @in_port: Ingress port of a switch * @out_port: Egress port of a switch where the packet is routed out - * (must be on the same switch than @in_port) + * (must be on the same switch as @in_port) * @in_hop_index: HopID where the path configuration entry is placed in * the path config space of @in_port. * @in_counter_index: Used counter index (not used in the driver @@ -499,9 +499,9 @@ struct tb_path { * performed. If this returns %-EOPNOTSUPP then the * native USB4 router operation is called. * @usb4_switch_nvm_authenticate_status: Optional callback that the CM - * implementation can be used to - * return status of USB4 NVM_AUTH - * router operation. + * implementation can use to return + * status of USB4 NVM_AUTH router + * operation. */ struct tb_cm_ops { int (*driver_ready)(struct tb *tb); @@ -1109,7 +1109,7 @@ struct tb_port *tb_next_port_on_path(struct tb_port *start, struct tb_port *end, struct tb_port *prev); /** - * tb_port_path_direction_downstream() - Checks if path directed downstream + * tb_port_path_direction_downstream() - Checks if path is directed downstream * @src: Source adapter * @dst: Destination adapter * @@ -1141,7 +1141,7 @@ static inline bool tb_port_use_credit_allocation(const struct tb_port *port) (p) = tb_next_port_on_path((src), (dst), (p))) /** - * tb_for_each_upstream_port_on_path() - Iterate over each upstreamm port on path + * tb_for_each_upstream_port_on_path() - Iterate over each upstream port on path * @src: Source port * @dst: Destination port * @p: Port used as iterator diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h index 4e43b47f9f11..c0bf136236e6 100644 --- a/drivers/thunderbolt/tb_regs.h +++ b/drivers/thunderbolt/tb_regs.h @@ -99,7 +99,7 @@ struct tb_cap_extended_long { } __packed; /** - * struct tb_cap_any - Structure capable of hold every capability + * struct tb_cap_any - Structure capable of holding every capability * @basic: Basic capability * @extended_short: Vendor specific capability * @extended_long: Vendor specific extended capability @@ -534,8 +534,8 @@ struct tb_regs_hop { /* * Used for Titan Ridge only. Bits are part of the same register: TMU_ADP_CS_6 - * (see above) as in USB4 spec, but these specific bits used for Titan Ridge - * only and reserved in USB4 spec. + * (see above) as in USB4 spec, but these specific bits are used for Titan Ridge + * only and are reserved in USB4 spec. */ #define TMU_ADP_CS_6_DISABLE_TMU_OBJ_MASK GENMASK(3, 2) #define TMU_ADP_CS_6_DISABLE_TMU_OBJ_CL1 BIT(2) diff --git a/drivers/thunderbolt/tmu.c b/drivers/thunderbolt/tmu.c index b22831b41ec0..cf779874c675 100644 --- a/drivers/thunderbolt/tmu.c +++ b/drivers/thunderbolt/tmu.c @@ -400,10 +400,10 @@ static int tmu_mode_init(struct tb_switch *sw) /** * tb_switch_tmu_init() - Initialize switch TMU structures - * @sw: Switch to initialized + * @sw: Switch to be initialized * * This function must be called before other TMU related functions to - * makes the internal structures are filled in correctly. Does not + * make sure the internal structures are filled in correctly. Does not * change any hardware configuration. * * Return: %0 on success, negative errno otherwise. diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c index bfa0607b5574..9fa95c595ecc 100644 --- a/drivers/thunderbolt/tunnel.c +++ b/drivers/thunderbolt/tunnel.c @@ -301,7 +301,7 @@ static int tb_pci_set_ext_encapsulation(struct tb_tunnel *tunnel, bool enable) struct tb_port *port = tb_upstream_port(tunnel->dst_port->sw); int ret; - /* Only supported of both routers are at least USB4 v2 */ + /* Only supported if both routers are at least USB4 v2 */ if ((usb4_switch_version(tunnel->src_port->sw) < 2) || (usb4_switch_version(tunnel->dst_port->sw) < 2)) return 0; @@ -1170,8 +1170,8 @@ static int tb_dp_bandwidth_mode_maximum_bandwidth(struct tb_tunnel *tunnel, /* * DP IN adapter DP_LOCAL_CAP gets updated to the lowest AUX - * read parameter values so this so we can use this to determine - * the maximum possible bandwidth over this link. + * read parameter values so we can use this to determine the + * maximum possible bandwidth over this link. * * See USB4 v2 spec 1.0 10.4.4.5. */ @@ -1783,8 +1783,8 @@ static int tb_dma_init_rx_path(struct tb_path *path, unsigned int credits) /* * First lane adapter is the one connected to the remote host. - * We don't tunnel other traffic over this link so can use all - * the credits (except the ones reserved for control traffic). + * We don't tunnel other traffic over this link so we can use + * all the credits (except the ones reserved for control traffic). */ hop = &path->hops[0]; tmp = min(tb_usable_credits(hop->in_port), credits); @@ -2044,7 +2044,7 @@ static int tb_usb3_consumed_bandwidth(struct tb_tunnel *tunnel, /* * PCIe tunneling, if enabled, affects the USB3 bandwidth so - * take that it into account here. + * take that into account here. */ *consumed_up = tunnel->allocated_up * (TB_USB3_WEIGHT + pcie_weight) / TB_USB3_WEIGHT; @@ -2605,7 +2605,7 @@ int tb_tunnel_consumed_bandwidth(struct tb_tunnel *tunnel, int *consumed_up, * @tunnel: Tunnel whose unused bandwidth to release * * If tunnel supports dynamic bandwidth management (USB3 tunnels at the - * moment) this function makes it to release all the unused bandwidth. + * moment) this function makes it release all the unused bandwidth. * * Return: %0 on success, negative errno otherwise. */ diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c index 76f01713a875..9e810b2ae0b5 100644 --- a/drivers/thunderbolt/usb4.c +++ b/drivers/thunderbolt/usb4.c @@ -284,7 +284,7 @@ int usb4_switch_setup(struct tb_switch *sw) val |= ROUTER_CS_5_PTO; /* * xHCI can be enabled if PCIe tunneling is supported - * and the parent does not have any USB3 dowstream + * and the parent does not have any USB3 downstream * adapters (so we cannot do USB 3.x tunneling). */ if (xhci) @@ -1342,7 +1342,7 @@ static int usb4_port_write_data(struct tb_port *port, const void *data, * usb4_port_sb_read() - Read from sideband register * @port: USB4 port to read * @target: Sideband target - * @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER + * @index: Retimer index if target is %USB4_SB_TARGET_RETIMER * @reg: Sideband register index * @buf: Buffer where the sideband data is copied * @size: Size of @buf @@ -1395,7 +1395,7 @@ int usb4_port_sb_read(struct tb_port *port, enum usb4_sb_target target, u8 index * usb4_port_sb_write() - Write to sideband register * @port: USB4 port to write * @target: Sideband target - * @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER + * @index: Retimer index if target is %USB4_SB_TARGET_RETIMER * @reg: Sideband register index * @buf: Data to write * @size: Size of @buf @@ -1527,7 +1527,7 @@ int usb4_port_router_offline(struct tb_port *port) } /** - * usb4_port_router_online() - Put the USB4 port back to online + * usb4_port_router_online() - Put the USB4 port back online * @port: USB4 port * * Makes the USB4 port functional again. @@ -1692,10 +1692,10 @@ int usb4_port_asym_start(struct tb_port *port) } /** - * usb4_port_margining_caps() - Read USB4 port marginig capabilities + * usb4_port_margining_caps() - Read USB4 port margining capabilities * @port: USB4 port * @target: Sideband target - * @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER + * @index: Retimer index if target is %USB4_SB_TARGET_RETIMER * @caps: Array with at least two elements to hold the results * @ncaps: Number of elements in the caps array * @@ -1721,7 +1721,7 @@ int usb4_port_margining_caps(struct tb_port *port, enum usb4_sb_target target, * usb4_port_hw_margin() - Run hardware lane margining on port * @port: USB4 port * @target: Sideband target - * @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER + * @index: Retimer index if target is %USB4_SB_TARGET_RETIMER * @params: Parameters for USB4 hardware margining * @results: Array to hold the results * @nresults: Number of elements in the results array @@ -1769,7 +1769,7 @@ int usb4_port_hw_margin(struct tb_port *port, enum usb4_sb_target target, * usb4_port_sw_margin() - Run software lane margining on port * @port: USB4 port * @target: Sideband target - * @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER + * @index: Retimer index if target is %USB4_SB_TARGET_RETIMER * @params: Parameters for USB4 software margining * @results: Data word for the operation completion data * @@ -1819,7 +1819,7 @@ int usb4_port_sw_margin(struct tb_port *port, enum usb4_sb_target target, * usb4_port_sw_margin_errors() - Read the software margining error counters * @port: USB4 port * @target: Sideband target - * @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER + * @index: Retimer index if target is %USB4_SB_TARGET_RETIMER * @errors: Error metadata is copied here. * * This reads back the software margining error counters from the port. @@ -1853,7 +1853,7 @@ static inline int usb4_port_retimer_op(struct tb_port *port, u8 index, * @port: USB4 port * @index: Retimer index * - * Enables sideband channel transations on SBTX. Can be used when USB4 + * Enables sideband channel transactions on SBTX. Can be used when USB4 * link does not go up, for example if there is no device connected. * * Return: %0 on success, negative errno otherwise. @@ -1882,7 +1882,7 @@ int usb4_port_retimer_set_inbound_sbtx(struct tb_port *port, u8 index) * @port: USB4 port * @index: Retimer index * - * Disables sideband channel transations on SBTX. The reverse of + * Disables sideband channel transactions on SBTX. The reverse of * usb4_port_retimer_set_inbound_sbtx(). * * Return: %0 on success, negative errno otherwise. @@ -1981,7 +1981,7 @@ int usb4_port_retimer_nvm_sector_size(struct tb_port *port, u8 index) * @index: Retimer index * @address: Start offset * - * Exlicitly sets NVM write offset. Normally when writing to NVM this is + * Explicitly sets NVM write offset. Normally when writing to NVM this is * done automatically by usb4_port_retimer_nvm_write(). * * Return: %0 on success, negative errno otherwise. @@ -2190,7 +2190,7 @@ usb4_usb3_port_max_bandwidth(const struct tb_port *port, unsigned int bw) } /** - * usb4_usb3_port_max_link_rate() - Maximum support USB3 link rate + * usb4_usb3_port_max_link_rate() - Maximum supported USB3 link rate * @port: USB3 adapter port * * Return: Maximum supported link rate of a USB3 adapter in Mb/s. diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c index 9d220ba544ec..63c7be818b2c 100644 --- a/drivers/thunderbolt/xdomain.c +++ b/drivers/thunderbolt/xdomain.c @@ -1951,8 +1951,8 @@ static void tb_xdomain_link_exit(struct tb_xdomain *xd) /** * tb_xdomain_alloc() - Allocate new XDomain object * @tb: Domain where the XDomain belongs - * @parent: Parent device (the switch through the connection to the - * other domain is reached). + * @parent: Parent device (the switch through which the other domain + * is reached). * @route: Route string used to reach the other domain * @local_uuid: Our local domain UUID * @remote_uuid: UUID of the other domain (optional) diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c index d9d8dc05b235..168707213ed9 100644 --- a/drivers/usb/cdns3/cdns3-gadget.c +++ b/drivers/usb/cdns3/cdns3-gadget.c @@ -3251,7 +3251,6 @@ static void cdns3_gadget_exit(struct cdns *cdns) priv_dev = cdns->gadget_dev; - pm_runtime_mark_last_busy(cdns->dev); pm_runtime_put_autosuspend(cdns->dev); usb_del_gadget(&priv_dev->gadget); diff --git a/drivers/usb/cdns3/cdnsp-gadget.c b/drivers/usb/cdns3/cdnsp-gadget.c index 0252560cbc80..d37c29a253dd 100644 --- a/drivers/usb/cdns3/cdnsp-gadget.c +++ b/drivers/usb/cdns3/cdnsp-gadget.c @@ -1999,7 +1999,6 @@ static void cdnsp_gadget_exit(struct cdns *cdns) struct cdnsp_device *pdev = cdns->gadget_dev; devm_free_irq(pdev->dev, cdns->dev_irq, pdev); - pm_runtime_mark_last_busy(cdns->dev); pm_runtime_put_autosuspend(cdns->dev); usb_del_gadget(&pdev->gadget); cdnsp_gadget_free_endpoints(pdev); diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 70597f40b999..fac11f20cf0a 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -1375,7 +1375,6 @@ static int ci_controller_resume(struct device *dev) ci->in_lpm = false; if (ci->wakeup_int) { ci->wakeup_int = false; - pm_runtime_mark_last_busy(ci->dev); pm_runtime_put_autosuspend(ci->dev); enable_irq(ci->irq); if (ci_otg_is_fsm_mode(ci)) diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c index a093544482d5..929536dc96ec 100644 --- a/drivers/usb/chipidea/otg_fsm.c +++ b/drivers/usb/chipidea/otg_fsm.c @@ -629,7 +629,6 @@ int ci_otg_fsm_work(struct ci_hdrc *ci) ci_otg_queue_work(ci); } } else if (ci->fsm.otg->state == OTG_STATE_A_HOST) { - pm_runtime_mark_last_busy(ci->dev); pm_runtime_put_autosuspend(ci->dev); return 0; } diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index b1418885707c..bb027d2bd700 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -1224,6 +1224,14 @@ static const struct usbmisc_ops imx7ulp_usbmisc_ops = { .power_lost_check = usbmisc_imx7d_power_lost_check, }; +static const struct usbmisc_ops imx94_usbmisc_ops = { + .init = usbmisc_imx7d_init, + .set_wakeup = usbmisc_imx95_set_wakeup, + .charger_detection = imx7d_charger_detection, + .power_lost_check = usbmisc_imx7d_power_lost_check, + .vbus_comparator_on = usbmisc_imx7d_vbus_comparator_on, +}; + static const struct usbmisc_ops imx95_usbmisc_ops = { .init = usbmisc_imx7d_init, .set_wakeup = usbmisc_imx95_set_wakeup, @@ -1481,6 +1489,10 @@ static const struct of_device_id usbmisc_imx_dt_ids[] = { .compatible = "fsl,imx8ulp-usbmisc", .data = &imx7ulp_usbmisc_ops, }, + { + .compatible = "fsl,imx94-usbmisc", + .data = &imx94_usbmisc_ops, + }, { .compatible = "fsl,imx95-usbmisc", .data = &imx95_usbmisc_ops, diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 73f9476774ae..54be4aa1dcb2 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1475,7 +1475,7 @@ made_compressed_probe: if (!acm->country_codes) goto skip_countries; acm->country_code_size = cfd->bLength - 4; - memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0, + memcpy(acm->country_codes, cfd->wCountryCodes, cfd->bLength - 4); acm->country_rel_date = cfd->iCountryCodeRelDate; diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 75de29725a45..206f1b738ed3 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -1936,10 +1936,8 @@ static int usbtmc_ioctl_request(struct usbtmc_device_data *data, u8 *buffer = NULL; int rv; unsigned int is_in, pipe; - unsigned long res; - res = copy_from_user(&request, arg, sizeof(struct usbtmc_ctrlrequest)); - if (res) + if (copy_from_user(&request, arg, sizeof(struct usbtmc_ctrlrequest))) return -EFAULT; if (request.req.wLength > USBTMC_BUFSIZE) @@ -1956,9 +1954,8 @@ static int usbtmc_ioctl_request(struct usbtmc_device_data *data, if (!is_in) { /* Send control data to device */ - res = copy_from_user(buffer, request.data, - request.req.wLength); - if (res) { + if (copy_from_user(buffer, request.data, + request.req.wLength)) { rv = -EFAULT; goto exit; } @@ -1984,8 +1981,7 @@ static int usbtmc_ioctl_request(struct usbtmc_device_data *data, if (rv && is_in) { /* Read control data from device */ - res = copy_to_user(request.data, buffer, rv); - if (res) + if (copy_to_user(request.data, buffer, rv)) rv = -EFAULT; } diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile index 766000b4939e..60ea76160122 100644 --- a/drivers/usb/core/Makefile +++ b/drivers/usb/core/Makefile @@ -3,10 +3,13 @@ # Makefile for USB Core files and filesystem # +# define_trace.h needs to know how to find our header +CFLAGS_trace.o := -I$(src) + usbcore-y := usb.o hub.o hcd.o urb.o message.o driver.o usbcore-y += config.o file.o buffer.o sysfs.o endpoint.o usbcore-y += devio.o notify.o generic.o quirks.o devices.o -usbcore-y += phy.o port.o +usbcore-y += phy.o port.o trace.o usbcore-$(CONFIG_OF) += of.o usbcore-$(CONFIG_USB_XHCI_SIDEBAND) += offload.o diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 9dd79769cad1..24feb0de1c00 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2696,18 +2696,18 @@ static void hcd_release(struct kref *kref) kfree(hcd); } -struct usb_hcd *usb_get_hcd (struct usb_hcd *hcd) +struct usb_hcd *usb_get_hcd(struct usb_hcd *hcd) { if (hcd) - kref_get (&hcd->kref); + kref_get(&hcd->kref); return hcd; } EXPORT_SYMBOL_GPL(usb_get_hcd); -void usb_put_hcd (struct usb_hcd *hcd) +void usb_put_hcd(struct usb_hcd *hcd) { if (hcd) - kref_put (&hcd->kref, hcd_release); + kref_put(&hcd->kref, hcd_release); } EXPORT_SYMBOL_GPL(usb_put_hcd); diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 256fe8c86828..be50d03034a9 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -40,6 +41,7 @@ #include "hub.h" #include "phy.h" #include "otg_productlist.h" +#include "trace.h" #define USB_VENDOR_GENESYS_LOGIC 0x05e3 #define USB_VENDOR_SMSC 0x0424 @@ -277,10 +279,7 @@ static void usb_set_lpm_pel(struct usb_device *udev, * device and the parent hub into U0. The exit latency is the bigger of * the device exit latency or the hub exit latency. */ - if (udev_exit_latency > hub_exit_latency) - first_link_pel = udev_exit_latency * 1000; - else - first_link_pel = hub_exit_latency * 1000; + first_link_pel = max(udev_exit_latency, hub_exit_latency) * 1000; /* * When the hub starts to receive the LFPS, there is a slight delay for @@ -294,10 +293,7 @@ static void usb_set_lpm_pel(struct usb_device *udev, * According to figure C-7 in the USB 3.0 spec, the PEL for this device * is the greater of the two exit latencies. */ - if (first_link_pel > hub_pel) - udev_lpm_params->pel = first_link_pel; - else - udev_lpm_params->pel = hub_pel; + udev_lpm_params->pel = max(first_link_pel, hub_pel); } /* @@ -2147,6 +2143,21 @@ static void update_port_device_state(struct usb_device *udev) } } +static void update_usb_device_state(struct usb_device *udev, + enum usb_device_state new_state) +{ + if (udev->state == USB_STATE_SUSPENDED && + new_state != USB_STATE_SUSPENDED) + udev->active_duration -= jiffies; + else if (new_state == USB_STATE_SUSPENDED && + udev->state != USB_STATE_SUSPENDED) + udev->active_duration += jiffies; + + udev->state = new_state; + update_port_device_state(udev); + trace_usb_set_device_state(udev); +} + static void recursively_mark_NOTATTACHED(struct usb_device *udev) { struct usb_hub *hub = usb_hub_to_struct_hub(udev); @@ -2156,10 +2167,7 @@ static void recursively_mark_NOTATTACHED(struct usb_device *udev) if (hub->ports[i]->child) recursively_mark_NOTATTACHED(hub->ports[i]->child); } - if (udev->state == USB_STATE_SUSPENDED) - udev->active_duration -= jiffies; - udev->state = USB_STATE_NOTATTACHED; - update_port_device_state(udev); + update_usb_device_state(udev, USB_STATE_NOTATTACHED); } /** @@ -2209,14 +2217,7 @@ void usb_set_device_state(struct usb_device *udev, else wakeup = 0; } - if (udev->state == USB_STATE_SUSPENDED && - new_state != USB_STATE_SUSPENDED) - udev->active_duration -= jiffies; - else if (new_state == USB_STATE_SUSPENDED && - udev->state != USB_STATE_SUSPENDED) - udev->active_duration += jiffies; - udev->state = new_state; - update_port_device_state(udev); + update_usb_device_state(udev, new_state); } else recursively_mark_NOTATTACHED(udev); spin_unlock_irqrestore(&device_state_lock, flags); @@ -6077,7 +6078,7 @@ int usb_hub_init(void) * device was gone before the EHCI controller had handed its port * over to the companion full-speed controller. */ - hub_wq = alloc_workqueue("usb_hub_wq", WQ_FREEZABLE, 0); + hub_wq = alloc_workqueue("usb_hub_wq", WQ_FREEZABLE | WQ_PERCPU, 0); if (hub_wq) return 0; diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index d2b2787be409..6138468c67c4 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -2431,7 +2431,7 @@ int cdc_parse_cdc_header(struct usb_cdc_parsed_header *hdr, break; case USB_CDC_MBIM_EXTENDED_TYPE: if (elength < sizeof(struct usb_cdc_mbim_extended_desc)) - break; + goto next_desc; hdr->usb_cdc_mbim_extended_desc = (struct usb_cdc_mbim_extended_desc *)buffer; break; diff --git a/drivers/usb/core/trace.c b/drivers/usb/core/trace.c new file mode 100644 index 000000000000..607bcf639d27 --- /dev/null +++ b/drivers/usb/core/trace.c @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 Google LLC + */ +#define CREATE_TRACE_POINTS +#include "trace.h" diff --git a/drivers/usb/core/trace.h b/drivers/usb/core/trace.h new file mode 100644 index 000000000000..903e57dc273a --- /dev/null +++ b/drivers/usb/core/trace.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2025 Google LLC + */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM usbcore + +#if !defined(_USB_CORE_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _USB_CORE_TRACE_H + +#include +#include +#include + +DECLARE_EVENT_CLASS(usb_core_log_usb_device, + TP_PROTO(struct usb_device *udev), + TP_ARGS(udev), + TP_STRUCT__entry( + __string(name, dev_name(&udev->dev)) + __field(enum usb_device_speed, speed) + __field(enum usb_device_state, state) + __field(unsigned short, bus_mA) + __field(unsigned, authorized) + ), + TP_fast_assign( + __assign_str(name); + __entry->speed = udev->speed; + __entry->state = udev->state; + __entry->bus_mA = udev->bus_mA; + __entry->authorized = udev->authorized; + ), + TP_printk("usb %s speed %s state %s %dmA [%s]", + __get_str(name), + usb_speed_string(__entry->speed), + usb_state_string(__entry->state), + __entry->bus_mA, + __entry->authorized ? "authorized" : "unauthorized") +); + +DEFINE_EVENT(usb_core_log_usb_device, usb_set_device_state, + TP_PROTO(struct usb_device *udev), + TP_ARGS(udev) +); + +DEFINE_EVENT(usb_core_log_usb_device, usb_alloc_dev, + TP_PROTO(struct usb_device *udev), + TP_ARGS(udev) +); + + +#endif /* _USB_CORE_TRACE_H */ + +/* this part has to be here */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . + +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE trace + +#include diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index b6b0b8489523..e740f7852bcd 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -46,6 +46,7 @@ #include #include "hub.h" +#include "trace.h" const char *usbcore_name = "usbcore"; @@ -746,6 +747,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, #endif dev->authorized = usb_dev_authorized(dev, usb_hcd); + trace_usb_alloc_dev(dev); return dev; } EXPORT_SYMBOL_GPL(usb_alloc_dev); diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 3f83ecc9fc23..ef0d73077034 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -369,11 +369,11 @@ static void dwc2_driver_shutdown(struct platform_device *dev) { struct dwc2_hsotg *hsotg = platform_get_drvdata(dev); - dwc2_disable_global_interrupts(hsotg); - synchronize_irq(hsotg->irq); - - if (hsotg->ll_hw_enabled) + if (hsotg->ll_hw_enabled) { + dwc2_disable_global_interrupts(hsotg); + synchronize_irq(hsotg->irq); dwc2_lowlevel_hw_disable(hsotg); + } } /** @@ -649,9 +649,13 @@ error: static int __maybe_unused dwc2_suspend(struct device *dev) { struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev); - bool is_device_mode = dwc2_is_device_mode(dwc2); + bool is_device_mode; int ret = 0; + if (!dwc2->ll_hw_enabled) + return 0; + + is_device_mode = dwc2_is_device_mode(dwc2); if (is_device_mode) dwc2_hsotg_suspend(dwc2); @@ -728,6 +732,9 @@ static int __maybe_unused dwc2_resume(struct device *dev) struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev); int ret = 0; + if (!dwc2->ll_hw_enabled) + return 0; + if (dwc2->phy_off_for_suspend && dwc2->ll_hw_enabled) { ret = __dwc2_lowlevel_hw_enable(dwc2); if (ret) diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 4925d15084f8..bf3e04635131 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -200,4 +200,15 @@ config USB_DWC3_GENERIC_PLAT the dwc3 child node in the device tree. Say 'Y' or 'M' here if your platform integrates DWC3 in a similar way. +config USB_DWC3_APPLE + tristate "Apple Silicon DWC3 Platform Driver" + depends on OF && ARCH_APPLE + default USB_DWC3 + select USB_ROLE_SWITCH + help + Support Apple Silicon SoCs with DesignWare Core USB3 IP. + The DesignWare Core USB3 IP has to be used in dual-role + mode on these machines. + Say 'Y' or 'M' if you have such device. + endif diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 96469e48ff9d..89d46ab50068 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -43,6 +43,7 @@ endif ## obj-$(CONFIG_USB_DWC3_AM62) += dwc3-am62.o +obj-$(CONFIG_USB_DWC3_APPLE) += dwc3-apple.o obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.o obj-$(CONFIG_USB_DWC3_EXYNOS) += dwc3-exynos.o obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index c2ce2f5e60a1..ec8407972b9d 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -133,6 +133,7 @@ void dwc3_enable_susphy(struct dwc3 *dwc, bool enable) dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(i), reg); } } +EXPORT_SYMBOL_GPL(dwc3_enable_susphy); void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy) { @@ -159,6 +160,7 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy) dwc->current_dr_role = mode; trace_dwc3_set_prtcap(mode); } +EXPORT_SYMBOL_GPL(dwc3_set_prtcap); static void __dwc3_set_mode(struct work_struct *work) { @@ -281,7 +283,6 @@ static void __dwc3_set_mode(struct work_struct *work) } out: - pm_runtime_mark_last_busy(dwc->dev); pm_runtime_put_autosuspend(dwc->dev); mutex_unlock(&dwc->mutex); } @@ -976,7 +977,7 @@ static void dwc3_clk_disable(struct dwc3 *dwc) clk_disable_unprepare(dwc->bus_clk); } -static void dwc3_core_exit(struct dwc3 *dwc) +void dwc3_core_exit(struct dwc3 *dwc) { dwc3_event_buffers_cleanup(dwc); dwc3_phy_power_off(dwc); @@ -984,6 +985,7 @@ static void dwc3_core_exit(struct dwc3 *dwc) dwc3_clk_disable(dwc); reset_control_assert(dwc->reset); } +EXPORT_SYMBOL_GPL(dwc3_core_exit); static bool dwc3_core_is_valid(struct dwc3 *dwc) { @@ -1329,7 +1331,7 @@ static void dwc3_config_threshold(struct dwc3 *dwc) * * Returns 0 on success otherwise negative errno. */ -static int dwc3_core_init(struct dwc3 *dwc) +int dwc3_core_init(struct dwc3 *dwc) { unsigned int hw_mode; u32 reg; @@ -1482,10 +1484,6 @@ static int dwc3_core_init(struct dwc3 *dwc) dwc3_config_threshold(dwc); - /* - * Modify this for all supported Super Speed ports when - * multiport support is added. - */ if (hw_mode != DWC3_GHWPARAMS0_MODE_GADGET && (DWC3_IP_IS(DWC31)) && dwc->maximum_speed == USB_SPEED_SUPER) { @@ -1529,6 +1527,7 @@ err_exit_ulpi: return ret; } +EXPORT_SYMBOL_GPL(dwc3_core_init); static int dwc3_core_get_phy(struct dwc3 *dwc) { @@ -1667,7 +1666,8 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc) dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE, true); } -static void dwc3_get_software_properties(struct dwc3 *dwc) +static void dwc3_get_software_properties(struct dwc3 *dwc, + const struct dwc3_properties *properties) { struct device *tmpdev; u16 gsbuscfg0_reqinfo; @@ -1675,6 +1675,12 @@ static void dwc3_get_software_properties(struct dwc3 *dwc) dwc->gsbuscfg0_reqinfo = DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED; + if (properties->gsbuscfg0_reqinfo != + DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED) { + dwc->gsbuscfg0_reqinfo = properties->gsbuscfg0_reqinfo; + return; + } + /* * Iterate over all parent nodes for finding swnode properties * and non-DT (non-ABI) properties. @@ -2207,7 +2213,7 @@ int dwc3_core_probe(const struct dwc3_probe_data *data) dwc3_get_properties(dwc); - dwc3_get_software_properties(dwc); + dwc3_get_software_properties(dwc, &data->properties); dwc->usb_psy = dwc3_get_usb_power_supply(dwc); if (IS_ERR(dwc->usb_psy)) @@ -2300,9 +2306,11 @@ int dwc3_core_probe(const struct dwc3_probe_data *data) dwc3_check_params(dwc); dwc3_debugfs_init(dwc); - ret = dwc3_core_init_mode(dwc); - if (ret) - goto err_exit_debugfs; + if (!data->skip_core_init_mode) { + ret = dwc3_core_init_mode(dwc); + if (ret) + goto err_exit_debugfs; + } pm_runtime_put(dev); @@ -2357,6 +2365,7 @@ static int dwc3_probe(struct platform_device *pdev) probe_data.dwc = dwc; probe_data.res = res; + probe_data.properties = DWC3_DEFAULT_PROPERTIES; return dwc3_core_probe(&probe_data); } @@ -2645,7 +2654,6 @@ int dwc3_runtime_idle(struct dwc3 *dwc) break; } - pm_runtime_mark_last_busy(dev); pm_runtime_autosuspend(dev); return 0; diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c index 4c91240eb429..589bbeb27454 100644 --- a/drivers/usb/dwc3/drd.c +++ b/drivers/usb/dwc3/drd.c @@ -515,6 +515,7 @@ static int dwc3_setup_role_switch(struct dwc3 *dwc) dwc3_role_switch.set = dwc3_usb_role_switch_set; dwc3_role_switch.get = dwc3_usb_role_switch_get; dwc3_role_switch.driver_data = dwc; + dwc3_role_switch.allow_userspace_control = true; dwc->role_sw = usb_role_switch_register(dwc->dev, &dwc3_role_switch); if (IS_ERR(dwc->role_sw)) return PTR_ERR(dwc->role_sw); diff --git a/drivers/usb/dwc3/dwc3-am62.c b/drivers/usb/dwc3/dwc3-am62.c index 9db8f3ca493d..e11d7643f966 100644 --- a/drivers/usb/dwc3/dwc3-am62.c +++ b/drivers/usb/dwc3/dwc3-am62.c @@ -292,7 +292,6 @@ static int dwc3_ti_probe(struct platform_device *pdev) /* Setting up autosuspend */ pm_runtime_set_autosuspend_delay(dev, DWC3_AM62_AUTOSUSPEND_DELAY); pm_runtime_use_autosuspend(dev); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return 0; diff --git a/drivers/usb/dwc3/dwc3-apple.c b/drivers/usb/dwc3/dwc3-apple.c new file mode 100644 index 000000000000..cc47cad232e3 --- /dev/null +++ b/drivers/usb/dwc3/dwc3-apple.c @@ -0,0 +1,489 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Apple Silicon DWC3 Glue driver + * Copyright (C) The Asahi Linux Contributors + * + * Based on: + * - dwc3-qcom.c Copyright (c) 2018, The Linux Foundation. All rights reserved. + * - dwc3-of-simple.c Copyright (c) 2015 Texas Instruments Incorporated - https://www.ti.com + */ + +#include +#include +#include +#include +#include + +#include "glue.h" + +/* + * This platform requires a very specific sequence of operations to bring up dwc3 and its USB3 PHY: + * + * 1) The PHY itself has to be brought up; for this we need to know the mode (USB3, + * USB3+DisplayPort, USB4, etc) and the lane orientation. This happens through typec_mux_set. + * 2) DWC3 has to be brought up but we must not touch the gadget area or start xhci yet. + * 3) The PHY bring-up has to be finalized and dwc3's PIPE interface has to be switched to the + * USB3 PHY, this is done inside phy_set_mode. + * 4) We can now initialize xhci or gadget mode. + * + * We can switch 1 and 2 but 3 has to happen after (1 and 2) and 4 has to happen after 3. + * + * And then to bring this all down again: + * + * 1) DWC3 has to exit host or gadget mode and must no longer touch those registers + * 2) The PHY has to switch dwc3's PIPE interface back to the dummy backend + * 3) The PHY itself can be shut down, this happens from typec_mux_set + * + * We also can't transition the PHY from one mode to another while dwc3 is up and running (this is + * slightly wrong, some transitions are possible, others aren't but because we have no documentation + * for this I'd rather play it safe). + * + * After both the PHY and dwc3 are initialized we will only ever see a single "new device connected" + * event. If we just keep them running only the first device plugged in will ever work. XHCI's port + * status register actually does show the correct state but no interrupt ever comes in. In gadget + * mode we don't even get a USBDisconnected event and everything looks like there's still something + * connected on the other end. + * This can be partially explained because the USB2 D+/D- lines are connected through a stateful + * eUSB2 repeater which in turn is controlled by a variant of the TI TPS6598x USB PD chip which + * resets the repeater out-of-band everytime the CC lines are (dis)connected. This then requires a + * PHY reset to make sure the PHY and the eUSB2 repeater state are synchronized again. + * + * And to make this all extra fun: If we get the order of some of this wrong either the port is just + * broken until a phy+dwc3 reset, or it's broken until a full SoC reset (likely because we can't + * reset some parts of the PHY), or some watchdog kicks in after a few seconds and forces a full SoC + * reset (mostly seen this with USB4/Thunderbolt but there's clearly some watchdog that hates + * invalid states). + * + * Hence there's really no good way to keep dwc3 fully up and running after we disconnect a cable + * because then we can't shut down the PHY anymore. And if we kept the PHY running in whatever mode + * it was until the next cable is connected we'd need to tear it all down and bring it back up again + * anyway to detect and use the next device. + * + * Instead, we just shut down everything when a cable is disconnected and transition to + * DWC3_APPLE_NO_CABLE. + * During initial probe we don't have any information about the connected cable and can't bring up + * the PHY properly and thus also can't fully bring up dwc3. Instead, we just keep everything off + * and defer the first dwc3 probe until we get the first cable connected event. Until then we stay + * in DWC3_APPLE_PROBE_PENDING. + * Once a cable is connected we then keep track of the controller mode here by transitioning to + * DWC3_APPLE_HOST or DWC3_APPLE_DEVICE. + */ +enum dwc3_apple_state { + DWC3_APPLE_PROBE_PENDING, /* Before first cable connection, dwc3_core_probe not called */ + DWC3_APPLE_NO_CABLE, /* No cable connected, dwc3 suspended after dwc3_core_exit */ + DWC3_APPLE_HOST, /* Cable connected, dwc3 in host mode */ + DWC3_APPLE_DEVICE, /* Cable connected, dwc3 in device mode */ +}; + +/** + * struct dwc3_apple - Apple-specific DWC3 USB controller + * @dwc: Core DWC3 structure + * @dev: Pointer to the device structure + * @mmio_resource: Resource to be passed to dwc3_core_probe + * @apple_regs: Apple-specific DWC3 registers + * @reset: Reset control + * @role_sw: USB role switch + * @lock: Mutex for synchronizing access + * @state: Current state of the controller, see documentation for the enum for details + */ +struct dwc3_apple { + struct dwc3 dwc; + + struct device *dev; + struct resource *mmio_resource; + void __iomem *apple_regs; + + struct reset_control *reset; + struct usb_role_switch *role_sw; + + struct mutex lock; + + enum dwc3_apple_state state; +}; + +#define to_dwc3_apple(d) container_of((d), struct dwc3_apple, dwc) + +/* + * Apple Silicon dwc3 vendor-specific registers + * + * These registers were identified by tracing XNU's memory access patterns and correlating them with + * debug output over serial to determine their names. We don't exactly know what these do but + * without these USB3 devices sometimes don't work. + */ +#define APPLE_DWC3_REGS_START 0xcd00 +#define APPLE_DWC3_REGS_END 0xcdff + +#define APPLE_DWC3_CIO_LFPS_OFFSET 0xcd38 +#define APPLE_DWC3_CIO_LFPS_OFFSET_VALUE 0xf800f80 + +#define APPLE_DWC3_CIO_BW_NGT_OFFSET 0xcd3c +#define APPLE_DWC3_CIO_BW_NGT_OFFSET_VALUE 0xfc00fc0 + +#define APPLE_DWC3_CIO_LINK_TIMER 0xcd40 +#define APPLE_DWC3_CIO_PENDING_HP_TIMER GENMASK(23, 16) +#define APPLE_DWC3_CIO_PENDING_HP_TIMER_VALUE 0x14 +#define APPLE_DWC3_CIO_PM_LC_TIMER GENMASK(15, 8) +#define APPLE_DWC3_CIO_PM_LC_TIMER_VALUE 0xa +#define APPLE_DWC3_CIO_PM_ENTRY_TIMER GENMASK(7, 0) +#define APPLE_DWC3_CIO_PM_ENTRY_TIMER_VALUE 0x10 + +static inline void dwc3_apple_writel(struct dwc3_apple *appledwc, u32 offset, u32 value) +{ + writel(value, appledwc->apple_regs + offset - APPLE_DWC3_REGS_START); +} + +static inline u32 dwc3_apple_readl(struct dwc3_apple *appledwc, u32 offset) +{ + return readl(appledwc->apple_regs + offset - APPLE_DWC3_REGS_START); +} + +static inline void dwc3_apple_mask(struct dwc3_apple *appledwc, u32 offset, u32 mask, u32 value) +{ + u32 reg; + + reg = dwc3_apple_readl(appledwc, offset); + reg &= ~mask; + reg |= value; + dwc3_apple_writel(appledwc, offset, reg); +} + +static void dwc3_apple_setup_cio(struct dwc3_apple *appledwc) +{ + dwc3_apple_writel(appledwc, APPLE_DWC3_CIO_LFPS_OFFSET, APPLE_DWC3_CIO_LFPS_OFFSET_VALUE); + dwc3_apple_writel(appledwc, APPLE_DWC3_CIO_BW_NGT_OFFSET, + APPLE_DWC3_CIO_BW_NGT_OFFSET_VALUE); + dwc3_apple_mask(appledwc, APPLE_DWC3_CIO_LINK_TIMER, APPLE_DWC3_CIO_PENDING_HP_TIMER, + FIELD_PREP(APPLE_DWC3_CIO_PENDING_HP_TIMER, + APPLE_DWC3_CIO_PENDING_HP_TIMER_VALUE)); + dwc3_apple_mask(appledwc, APPLE_DWC3_CIO_LINK_TIMER, APPLE_DWC3_CIO_PM_LC_TIMER, + FIELD_PREP(APPLE_DWC3_CIO_PM_LC_TIMER, APPLE_DWC3_CIO_PM_LC_TIMER_VALUE)); + dwc3_apple_mask(appledwc, APPLE_DWC3_CIO_LINK_TIMER, APPLE_DWC3_CIO_PM_ENTRY_TIMER, + FIELD_PREP(APPLE_DWC3_CIO_PM_ENTRY_TIMER, + APPLE_DWC3_CIO_PM_ENTRY_TIMER_VALUE)); +} + +static void dwc3_apple_set_ptrcap(struct dwc3_apple *appledwc, u32 mode) +{ + guard(spinlock_irqsave)(&appledwc->dwc.lock); + dwc3_set_prtcap(&appledwc->dwc, mode, false); +} + +static int dwc3_apple_core_probe(struct dwc3_apple *appledwc) +{ + struct dwc3_probe_data probe_data = {}; + int ret; + + lockdep_assert_held(&appledwc->lock); + WARN_ON_ONCE(appledwc->state != DWC3_APPLE_PROBE_PENDING); + + appledwc->dwc.dev = appledwc->dev; + probe_data.dwc = &appledwc->dwc; + probe_data.res = appledwc->mmio_resource; + probe_data.ignore_clocks_and_resets = true; + probe_data.skip_core_init_mode = true; + probe_data.properties = DWC3_DEFAULT_PROPERTIES; + + ret = dwc3_core_probe(&probe_data); + if (ret) + return ret; + + appledwc->state = DWC3_APPLE_NO_CABLE; + return 0; +} + +static int dwc3_apple_core_init(struct dwc3_apple *appledwc) +{ + int ret; + + lockdep_assert_held(&appledwc->lock); + + switch (appledwc->state) { + case DWC3_APPLE_PROBE_PENDING: + ret = dwc3_apple_core_probe(appledwc); + if (ret) + dev_err(appledwc->dev, "Failed to probe DWC3 Core, err=%d\n", ret); + break; + case DWC3_APPLE_NO_CABLE: + ret = dwc3_core_init(&appledwc->dwc); + if (ret) + dev_err(appledwc->dev, "Failed to initialize DWC3 Core, err=%d\n", ret); + break; + default: + /* Unreachable unless there's a bug in this driver */ + WARN_ON_ONCE(1); + ret = -EINVAL; + break; + } + + return ret; +} + +static void dwc3_apple_phy_set_mode(struct dwc3_apple *appledwc, enum phy_mode mode) +{ + lockdep_assert_held(&appledwc->lock); + + /* + * This platform requires SUSPHY to be enabled here already in order to properly configure + * the PHY and switch dwc3's PIPE interface to USB3 PHY. + */ + dwc3_enable_susphy(&appledwc->dwc, true); + phy_set_mode(appledwc->dwc.usb2_generic_phy[0], mode); + phy_set_mode(appledwc->dwc.usb3_generic_phy[0], mode); +} + +static int dwc3_apple_init(struct dwc3_apple *appledwc, enum dwc3_apple_state state) +{ + int ret, ret_reset; + + lockdep_assert_held(&appledwc->lock); + + ret = reset_control_deassert(appledwc->reset); + if (ret) { + dev_err(appledwc->dev, "Failed to deassert reset, err=%d\n", ret); + return ret; + } + + ret = dwc3_apple_core_init(appledwc); + if (ret) + goto reset_assert; + + /* + * Now that the core is initialized and already went through dwc3_core_soft_reset we can + * configure some unknown Apple-specific settings and then bring up xhci or gadget mode. + */ + dwc3_apple_setup_cio(appledwc); + + switch (state) { + case DWC3_APPLE_HOST: + appledwc->dwc.dr_mode = USB_DR_MODE_HOST; + dwc3_apple_set_ptrcap(appledwc, DWC3_GCTL_PRTCAP_HOST); + dwc3_apple_phy_set_mode(appledwc, PHY_MODE_USB_HOST); + ret = dwc3_host_init(&appledwc->dwc); + if (ret) { + dev_err(appledwc->dev, "Failed to initialize host, ret=%d\n", ret); + goto core_exit; + } + + break; + case DWC3_APPLE_DEVICE: + appledwc->dwc.dr_mode = USB_DR_MODE_PERIPHERAL; + dwc3_apple_set_ptrcap(appledwc, DWC3_GCTL_PRTCAP_DEVICE); + dwc3_apple_phy_set_mode(appledwc, PHY_MODE_USB_DEVICE); + ret = dwc3_gadget_init(&appledwc->dwc); + if (ret) { + dev_err(appledwc->dev, "Failed to initialize gadget, ret=%d\n", ret); + goto core_exit; + } + break; + default: + /* Unreachable unless there's a bug in this driver */ + WARN_ON_ONCE(1); + ret = -EINVAL; + goto core_exit; + } + + appledwc->state = state; + return 0; + +core_exit: + dwc3_core_exit(&appledwc->dwc); +reset_assert: + ret_reset = reset_control_assert(appledwc->reset); + if (ret_reset) + dev_warn(appledwc->dev, "Failed to assert reset, err=%d\n", ret_reset); + + return ret; +} + +static int dwc3_apple_exit(struct dwc3_apple *appledwc) +{ + int ret = 0; + + lockdep_assert_held(&appledwc->lock); + + switch (appledwc->state) { + case DWC3_APPLE_PROBE_PENDING: + case DWC3_APPLE_NO_CABLE: + /* Nothing to do if we're already off */ + return 0; + case DWC3_APPLE_DEVICE: + dwc3_gadget_exit(&appledwc->dwc); + break; + case DWC3_APPLE_HOST: + dwc3_host_exit(&appledwc->dwc); + break; + } + + /* + * This platform requires SUSPHY to be enabled in order to properly power down the PHY + * and switch dwc3's PIPE interface back to a dummy PHY (i.e. no USB3 support and USB2 via + * a different PHY connected through ULPI). + */ + dwc3_enable_susphy(&appledwc->dwc, true); + dwc3_core_exit(&appledwc->dwc); + appledwc->state = DWC3_APPLE_NO_CABLE; + + ret = reset_control_assert(appledwc->reset); + if (ret) { + dev_err(appledwc->dev, "Failed to assert reset, err=%d\n", ret); + return ret; + } + + return 0; +} + +static int dwc3_usb_role_switch_set(struct usb_role_switch *sw, enum usb_role role) +{ + struct dwc3_apple *appledwc = usb_role_switch_get_drvdata(sw); + int ret; + + guard(mutex)(&appledwc->lock); + + /* + * We need to tear all of dwc3 down and re-initialize it every time a cable is + * connected or disconnected or when the mode changes. See the documentation for enum + * dwc3_apple_state for details. + */ + ret = dwc3_apple_exit(appledwc); + if (ret) + return ret; + + switch (role) { + case USB_ROLE_NONE: + /* Nothing to do if no cable is connected */ + return 0; + case USB_ROLE_HOST: + return dwc3_apple_init(appledwc, DWC3_APPLE_HOST); + case USB_ROLE_DEVICE: + return dwc3_apple_init(appledwc, DWC3_APPLE_DEVICE); + default: + dev_err(appledwc->dev, "Invalid target role: %d\n", role); + return -EINVAL; + } +} + +static enum usb_role dwc3_usb_role_switch_get(struct usb_role_switch *sw) +{ + struct dwc3_apple *appledwc = usb_role_switch_get_drvdata(sw); + + guard(mutex)(&appledwc->lock); + + switch (appledwc->state) { + case DWC3_APPLE_HOST: + return USB_ROLE_HOST; + case DWC3_APPLE_DEVICE: + return USB_ROLE_DEVICE; + case DWC3_APPLE_NO_CABLE: + case DWC3_APPLE_PROBE_PENDING: + return USB_ROLE_NONE; + default: + /* Unreachable unless there's a bug in this driver */ + dev_err(appledwc->dev, "Invalid internal state: %d\n", appledwc->state); + return USB_ROLE_NONE; + } +} + +static int dwc3_apple_setup_role_switch(struct dwc3_apple *appledwc) +{ + struct usb_role_switch_desc dwc3_role_switch = { NULL }; + + dwc3_role_switch.fwnode = dev_fwnode(appledwc->dev); + dwc3_role_switch.set = dwc3_usb_role_switch_set; + dwc3_role_switch.get = dwc3_usb_role_switch_get; + dwc3_role_switch.driver_data = appledwc; + appledwc->role_sw = usb_role_switch_register(appledwc->dev, &dwc3_role_switch); + if (IS_ERR(appledwc->role_sw)) + return PTR_ERR(appledwc->role_sw); + + return 0; +} + +static int dwc3_apple_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dwc3_apple *appledwc; + int ret; + + appledwc = devm_kzalloc(&pdev->dev, sizeof(*appledwc), GFP_KERNEL); + if (!appledwc) + return -ENOMEM; + + appledwc->dev = &pdev->dev; + mutex_init(&appledwc->lock); + + appledwc->reset = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(appledwc->reset)) + return dev_err_probe(&pdev->dev, PTR_ERR(appledwc->reset), + "Failed to get reset control\n"); + + ret = reset_control_assert(appledwc->reset); + if (ret) { + dev_err(&pdev->dev, "Failed to assert reset, err=%d\n", ret); + return ret; + } + + appledwc->mmio_resource = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dwc3-core"); + if (!appledwc->mmio_resource) { + dev_err(dev, "Failed to get DWC3 MMIO\n"); + return -EINVAL; + } + + appledwc->apple_regs = devm_platform_ioremap_resource_byname(pdev, "dwc3-apple"); + if (IS_ERR(appledwc->apple_regs)) + return dev_err_probe(dev, PTR_ERR(appledwc->apple_regs), + "Failed to map Apple-specific MMIO\n"); + + /* + * On this platform, DWC3 can only be brought up after parts of the PHY have been + * initialized with knowledge of the target mode and cable orientation from typec_set_mux. + * Since this has not happened here we cannot setup DWC3 yet and instead defer this until + * the first cable is connected. See the documentation for enum dwc3_apple_state for + * details. + */ + appledwc->state = DWC3_APPLE_PROBE_PENDING; + ret = dwc3_apple_setup_role_switch(appledwc); + if (ret) + return dev_err_probe(&pdev->dev, ret, "Failed to setup role switch\n"); + + return 0; +} + +static void dwc3_apple_remove(struct platform_device *pdev) +{ + struct dwc3 *dwc = platform_get_drvdata(pdev); + struct dwc3_apple *appledwc = to_dwc3_apple(dwc); + + guard(mutex)(&appledwc->lock); + + usb_role_switch_unregister(appledwc->role_sw); + + /* + * If we're still in DWC3_APPLE_PROBE_PENDING we never got any cable connected event and + * dwc3_core_probe was never called and there's hence no need to call dwc3_core_remove. + * dwc3_apple_exit can be called unconditionally because it checks the state itself. + */ + dwc3_apple_exit(appledwc); + if (appledwc->state != DWC3_APPLE_PROBE_PENDING) + dwc3_core_remove(&appledwc->dwc); +} + +static const struct of_device_id dwc3_apple_of_match[] = { + { .compatible = "apple,t8103-dwc3" }, + {} +}; +MODULE_DEVICE_TABLE(of, dwc3_apple_of_match); + +static struct platform_driver dwc3_apple_driver = { + .probe = dwc3_apple_probe, + .remove = dwc3_apple_remove, + .driver = { + .name = "dwc3-apple", + .of_match_table = dwc3_apple_of_match, + }, +}; + +module_platform_driver(dwc3_apple_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Sven Peter "); +MODULE_DESCRIPTION("DesignWare DWC3 Apple Silicon Glue Driver"); diff --git a/drivers/usb/dwc3/dwc3-generic-plat.c b/drivers/usb/dwc3/dwc3-generic-plat.c index f8ad79c08c4e..e846844e0023 100644 --- a/drivers/usb/dwc3/dwc3-generic-plat.c +++ b/drivers/usb/dwc3/dwc3-generic-plat.c @@ -10,8 +10,16 @@ #include #include #include +#include +#include #include "glue.h" +#define EIC7700_HSP_BUS_FILTER_EN BIT(0) +#define EIC7700_HSP_BUS_CLKEN_GM BIT(9) +#define EIC7700_HSP_BUS_CLKEN_GS BIT(16) +#define EIC7700_HSP_AXI_LP_XM_CSYSREQ BIT(0) +#define EIC7700_HSP_AXI_LP_XS_CSYSREQ BIT(16) + struct dwc3_generic { struct device *dev; struct dwc3 dwc; @@ -20,6 +28,11 @@ struct dwc3_generic { struct reset_control *resets; }; +struct dwc3_generic_config { + int (*init)(struct dwc3_generic *dwc3g); + struct dwc3_properties properties; +}; + #define to_dwc3_generic(d) container_of((d), struct dwc3_generic, dwc) static void dwc3_generic_reset_control_assert(void *data) @@ -27,8 +40,38 @@ static void dwc3_generic_reset_control_assert(void *data) reset_control_assert(data); } +static int dwc3_eic7700_init(struct dwc3_generic *dwc3g) +{ + struct device *dev = dwc3g->dev; + struct regmap *regmap; + u32 hsp_usb_axi_lp; + u32 hsp_usb_bus; + u32 args[2]; + u32 val; + + regmap = syscon_regmap_lookup_by_phandle_args(dev->of_node, + "eswin,hsp-sp-csr", + ARRAY_SIZE(args), args); + if (IS_ERR(regmap)) { + dev_err(dev, "No hsp-sp-csr phandle specified\n"); + return PTR_ERR(regmap); + } + + hsp_usb_bus = args[0]; + hsp_usb_axi_lp = args[1]; + + regmap_read(regmap, hsp_usb_bus, &val); + regmap_write(regmap, hsp_usb_bus, val | EIC7700_HSP_BUS_FILTER_EN | + EIC7700_HSP_BUS_CLKEN_GM | EIC7700_HSP_BUS_CLKEN_GS); + + regmap_write(regmap, hsp_usb_axi_lp, EIC7700_HSP_AXI_LP_XM_CSYSREQ | + EIC7700_HSP_AXI_LP_XS_CSYSREQ); + return 0; +} + static int dwc3_generic_probe(struct platform_device *pdev) { + const struct dwc3_generic_config *plat_config; struct dwc3_probe_data probe_data = {}; struct device *dev = &pdev->dev; struct dwc3_generic *dwc3g; @@ -75,6 +118,22 @@ static int dwc3_generic_probe(struct platform_device *pdev) probe_data.dwc = &dwc3g->dwc; probe_data.res = res; probe_data.ignore_clocks_and_resets = true; + + plat_config = of_device_get_match_data(dev); + if (!plat_config) { + probe_data.properties = DWC3_DEFAULT_PROPERTIES; + goto core_probe; + } + + probe_data.properties = plat_config->properties; + if (plat_config->init) { + ret = plat_config->init(dwc3g); + if (ret) + return dev_err_probe(dev, ret, + "failed to init platform\n"); + } + +core_probe: ret = dwc3_core_probe(&probe_data); if (ret) return dev_err_probe(dev, ret, "failed to register DWC3 Core\n"); @@ -142,8 +201,19 @@ static const struct dev_pm_ops dwc3_generic_dev_pm_ops = { dwc3_generic_runtime_idle) }; +static const struct dwc3_generic_config fsl_ls1028_dwc3 = { + .properties.gsbuscfg0_reqinfo = 0x2222, +}; + +static const struct dwc3_generic_config eic7700_dwc3 = { + .init = dwc3_eic7700_init, + .properties = DWC3_DEFAULT_PROPERTIES, +}; + static const struct of_device_id dwc3_generic_of_match[] = { { .compatible = "spacemit,k1-dwc3", }, + { .compatible = "fsl,ls1028a-dwc3", &fsl_ls1028_dwc3}, + { .compatible = "eswin,eic7700-dwc3", &eic7700_dwc3}, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, dwc3_generic_of_match); diff --git a/drivers/usb/dwc3/dwc3-imx8mp.c b/drivers/usb/dwc3/dwc3-imx8mp.c index 225d59e9c190..45c276a31d84 100644 --- a/drivers/usb/dwc3/dwc3-imx8mp.c +++ b/drivers/usb/dwc3/dwc3-imx8mp.c @@ -312,7 +312,6 @@ static int dwc3_imx8mp_resume(struct dwc3_imx8mp *dwc3_imx, pm_message_t msg) if (dwc3_imx->wakeup_pending) { dwc3_imx->wakeup_pending = false; if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE) { - pm_runtime_mark_last_busy(dwc->dev); pm_runtime_put_autosuspend(dwc->dev); } else { /* diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 8f5faf632a8b..6ecadc81bd6b 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -323,7 +323,6 @@ static void dwc3_pci_resume_work(struct work_struct *work) return; } - pm_runtime_mark_last_busy(&dwc3->dev); pm_runtime_put_sync_autosuspend(&dwc3->dev); } #endif diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c index ded2ca86670c..9ac75547820d 100644 --- a/drivers/usb/dwc3/dwc3-qcom.c +++ b/drivers/usb/dwc3/dwc3-qcom.c @@ -704,6 +704,7 @@ static int dwc3_qcom_probe(struct platform_device *pdev) probe_data.dwc = &qcom->dwc; probe_data.res = &res; probe_data.ignore_clocks_and_resets = true; + probe_data.properties = DWC3_DEFAULT_PROPERTIES; ret = dwc3_core_probe(&probe_data); if (ret) { ret = dev_err_probe(dev, ret, "failed to register DWC3 Core\n"); diff --git a/drivers/usb/dwc3/dwc3-xilinx.c b/drivers/usb/dwc3/dwc3-xilinx.c index 1e28d6f50ed0..0a8c47876ff9 100644 --- a/drivers/usb/dwc3/dwc3-xilinx.c +++ b/drivers/usb/dwc3/dwc3-xilinx.c @@ -383,7 +383,6 @@ static int __maybe_unused dwc3_xlnx_runtime_resume(struct device *dev) static int __maybe_unused dwc3_xlnx_runtime_idle(struct device *dev) { - pm_runtime_mark_last_busy(dev); pm_runtime_autosuspend(dev); return 0; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 5e4997f974dd..bc3fe31638b9 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -3879,7 +3879,7 @@ static void dwc3_gadget_endpoint_stream_event(struct dwc3_ep *dep, case DEPEVT_STREAM_NOSTREAM: dep->flags &= ~DWC3_EP_STREAM_PRIMED; if (dep->flags & DWC3_EP_FORCE_RESTART_STREAM) - queue_delayed_work(system_wq, &dep->nostream_work, + queue_delayed_work(system_percpu_wq, &dep->nostream_work, msecs_to_jiffies(100)); break; } @@ -4817,6 +4817,7 @@ err1: err0: return ret; } +EXPORT_SYMBOL_GPL(dwc3_gadget_init); /* -------------------------------------------------------------------------- */ @@ -4835,6 +4836,7 @@ void dwc3_gadget_exit(struct dwc3 *dwc) dma_free_coherent(dwc->sysdev, sizeof(*dwc->ep0_trb) * 2, dwc->ep0_trb, dwc->ep0_trb_addr); } +EXPORT_SYMBOL_GPL(dwc3_gadget_exit); int dwc3_gadget_suspend(struct dwc3 *dwc) { diff --git a/drivers/usb/dwc3/glue.h b/drivers/usb/dwc3/glue.h index 2efd00e763be..df86e14cb706 100644 --- a/drivers/usb/dwc3/glue.h +++ b/drivers/usb/dwc3/glue.h @@ -9,22 +9,66 @@ #include #include "core.h" +/** + * dwc3_properties: DWC3 core properties + * @gsbuscfg0_reqinfo: Value to be programmed in the GSBUSCFG0.REQINFO field + */ +struct dwc3_properties { + u32 gsbuscfg0_reqinfo; +}; + +#define DWC3_DEFAULT_PROPERTIES ((struct dwc3_properties){ \ + .gsbuscfg0_reqinfo = DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED, \ + }) + /** * dwc3_probe_data: Initialization parameters passed to dwc3_core_probe() * @dwc: Reference to dwc3 context structure * @res: resource for the DWC3 core mmio region * @ignore_clocks_and_resets: clocks and resets defined for the device should * be ignored by the DWC3 core, as they are managed by the glue + * @skip_core_init_mode: Skip the finial initialization of the target mode, as + * it must be managed by the glue + * @properties: dwc3 software manage properties */ struct dwc3_probe_data { struct dwc3 *dwc; struct resource *res; bool ignore_clocks_and_resets; + bool skip_core_init_mode; + struct dwc3_properties properties; }; +/** + * dwc3_core_probe - Initialize the core dwc3 driver + * @data: Initialization and configuration parameters for the controller + * + * Initializes the DesignWare USB3 core driver by setting up resources, + * registering interrupts, performing hardware setup, and preparing + * the controller for operation in the appropriate mode (host, gadget, + * or OTG). This is the main initialization function called by glue + * layer drivers to set up the core controller. + * + * Return: 0 on success, negative error code on failure + */ int dwc3_core_probe(const struct dwc3_probe_data *data); + +/** + * dwc3_core_remove - Deinitialize and remove the core dwc3 driver + * @dwc: Pointer to DWC3 controller context + * + * Cleans up resources and disables the dwc3 core driver. This should be called + * during driver removal or when the glue layer needs to shut down the + * controller completely. + */ void dwc3_core_remove(struct dwc3 *dwc); +/* + * The following callbacks are provided for glue drivers to call from their + * own pm callbacks provided in struct dev_pm_ops. Glue drivers can perform + * platform-specific work before or after calling these functions and delegate + * the core suspend/resume operations to the core driver. + */ int dwc3_runtime_suspend(struct dwc3 *dwc); int dwc3_runtime_resume(struct dwc3 *dwc); int dwc3_runtime_idle(struct dwc3 *dwc); @@ -33,4 +77,117 @@ int dwc3_pm_resume(struct dwc3 *dwc); void dwc3_pm_complete(struct dwc3 *dwc); int dwc3_pm_prepare(struct dwc3 *dwc); + +/* All of the following functions must only be used with skip_core_init_mode */ + +/** + * dwc3_core_init - Initialize DWC3 core hardware + * @dwc: Pointer to DWC3 controller context + * + * Configures and initializes the core hardware, usually done by dwc3_core_probe. + * This function is provided for platforms that use skip_core_init_mode and need + * to finalize the core initialization after some platform-specific setup. + * It must only be called when using skip_core_init_mode and before + * dwc3_host_init or dwc3_gadget_init. + * + * Return: 0 on success, negative error code on failure + */ +int dwc3_core_init(struct dwc3 *dwc); + +/** + * dwc3_core_exit - Shut down DWC3 core hardware + * @dwc: Pointer to DWC3 controller context + * + * Disables and cleans up the core hardware state. This is usually handled + * internally by dwc3 and must only be called when using skip_core_init_mode + * and only after dwc3_core_init. Afterwards, dwc3_core_init may be called + * again. + */ +void dwc3_core_exit(struct dwc3 *dwc); + +/** + * dwc3_host_init - Initialize host mode operation + * @dwc: Pointer to DWC3 controller context + * + * Initializes the controller for USB host mode operation, usually done by + * dwc3_core_probe or from within the dwc3 USB role switch callback. + * This function is provided for platforms that use skip_core_init_mode and need + * to finalize the host initialization after some platform-specific setup. + * It must not be called before dwc3_core_init or when skip_core_init_mode is + * not used. It must also not be called when gadget or host mode has already + * been initialized. + * + * Return: 0 on success, negative error code on failure + */ +int dwc3_host_init(struct dwc3 *dwc); + +/** + * dwc3_host_exit - Shut down host mode operation + * @dwc: Pointer to DWC3 controller context + * + * Disables and cleans up host mode resources, usually done by + * the dwc3 USB role switch callback before switching controller mode. + * It must only be called when skip_core_init_mode is used and only after + * dwc3_host_init. + */ +void dwc3_host_exit(struct dwc3 *dwc); + +/** + * dwc3_gadget_init - Initialize gadget mode operation + * @dwc: Pointer to DWC3 controller context + * + * Initializes the controller for USB gadget mode operation, usually done by + * dwc3_core_probe or from within the dwc3 USB role switch callback. This + * function is provided for platforms that use skip_core_init_mode and need to + * finalize the gadget initialization after some platform-specific setup. + * It must not be called before dwc3_core_init or when skip_core_init_mode is + * not used. It must also not be called when gadget or host mode has already + * been initialized. + * + * Return: 0 on success, negative error code on failure + */ +int dwc3_gadget_init(struct dwc3 *dwc); + +/** + * dwc3_gadget_exit - Shut down gadget mode operation + * @dwc: Pointer to DWC3 controller context + * + * Disables and cleans up gadget mode resources, usually done by + * the dwc3 USB role switch callback before switching controller mode. + * It must only be called when skip_core_init_mode is used and only after + * dwc3_gadget_init. + */ +void dwc3_gadget_exit(struct dwc3 *dwc); + +/** + * dwc3_enable_susphy - Control SUSPHY status for all USB ports + * @dwc: Pointer to DWC3 controller context + * @enable: True to enable SUSPHY, false to disable + * + * Enables or disables the USB3 PHY SUSPEND and USB2 PHY SUSPHY feature for + * all available ports. + * This is usually handled by the dwc3 core code and should only be used + * when skip_core_init_mode is used and the glue layer needs to manage SUSPHY + * settings itself, e.g., due to platform-specific requirements during mode + * switches. + */ +void dwc3_enable_susphy(struct dwc3 *dwc, bool enable); + +/** + * dwc3_set_prtcap - Set the USB controller PRTCAP mode + * @dwc: Pointer to DWC3 controller context + * @mode: Target mode, must be one of DWC3_GCTL_PRTCAP_{HOST,DEVICE,OTG} + * @ignore_susphy: If true, skip disabling the SUSPHY and keep the current state + * + * Updates PRTCAP of the controller and current_dr_role inside the dwc3 + * structure. For DRD controllers, this also disables SUSPHY unless explicitly + * told to skip via the ignore_susphy parameter. + * + * This is usually handled by the dwc3 core code and should only be used + * when skip_core_init_mode is used and the glue layer needs to manage mode + * transitions itself due to platform-specific requirements. It must be called + * with the correct mode before calling dwc3_host_init or dwc3_gadget_init. + */ +void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy); + #endif diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c index 1c513bf8002e..cf6512ed17a6 100644 --- a/drivers/usb/dwc3/host.c +++ b/drivers/usb/dwc3/host.c @@ -37,7 +37,10 @@ static void dwc3_power_off_all_roothub_ports(struct dwc3 *dwc) /* xhci regs are not mapped yet, do it temporarily here */ if (dwc->xhci_resources[0].start) { - xhci_regs = ioremap(dwc->xhci_resources[0].start, DWC3_XHCI_REGS_END); + if (dwc->xhci_resources[0].flags & IORESOURCE_MEM_NONPOSTED) + xhci_regs = ioremap_np(dwc->xhci_resources[0].start, DWC3_XHCI_REGS_END); + else + xhci_regs = ioremap(dwc->xhci_resources[0].start, DWC3_XHCI_REGS_END); if (!xhci_regs) { dev_err(dwc->dev, "Failed to ioremap xhci_regs\n"); return; @@ -217,6 +220,7 @@ err: platform_device_put(xhci); return ret; } +EXPORT_SYMBOL_GPL(dwc3_host_init); void dwc3_host_exit(struct dwc3 *dwc) { @@ -227,3 +231,4 @@ void dwc3_host_exit(struct dwc3 *dwc) platform_device_unregister(dwc->xhci); dwc->xhci = NULL; } +EXPORT_SYMBOL_GPL(dwc3_host_exit); diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 4bf61017b42d..05c6750702b6 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -1332,9 +1332,7 @@ static void ffs_dmabuf_release(struct kref *ref) struct dma_buf *dmabuf = attach->dmabuf; pr_vdebug("FFS DMABUF release\n"); - dma_resv_lock(dmabuf->resv, NULL); - dma_buf_unmap_attachment(attach, priv->sgt, priv->dir); - dma_resv_unlock(dmabuf->resv); + dma_buf_unmap_attachment_unlocked(attach, priv->sgt, priv->dir); dma_buf_detach(attach->dmabuf, attach); dma_buf_put(dmabuf); diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c index 307ea563af95..3ddfd4f66f0b 100644 --- a/drivers/usb/gadget/function/f_hid.c +++ b/drivers/usb/gadget/function/f_hid.c @@ -1272,8 +1272,7 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f) INIT_WORK(&hidg->work, get_report_workqueue_handler); hidg->workqueue = alloc_workqueue("report_work", - WQ_FREEZABLE | - WQ_MEM_RECLAIM, + WQ_FREEZABLE | WQ_MEM_RECLAIM | WQ_PERCPU, 1); if (!hidg->workqueue) { diff --git a/drivers/usb/gadget/legacy/raw_gadget.c b/drivers/usb/gadget/legacy/raw_gadget.c index b71680c58de6..46f343ba48b3 100644 --- a/drivers/usb/gadget/legacy/raw_gadget.c +++ b/drivers/usb/gadget/legacy/raw_gadget.c @@ -40,6 +40,7 @@ MODULE_LICENSE("GPL"); static DEFINE_IDA(driver_id_numbers); #define DRIVER_DRIVER_NAME_LENGTH_MAX 32 +#define USB_RAW_IO_LENGTH_MAX KMALLOC_MAX_SIZE #define RAW_EVENT_QUEUE_SIZE 16 @@ -667,6 +668,8 @@ static void *raw_alloc_io_data(struct usb_raw_ep_io *io, void __user *ptr, return ERR_PTR(-EINVAL); if (!usb_raw_io_flags_valid(io->flags)) return ERR_PTR(-EINVAL); + if (io->length > USB_RAW_IO_LENGTH_MAX) + return ERR_PTR(-EINVAL); if (get_from_user) data = memdup_user(ptr + sizeof(*io), io->length); else { diff --git a/drivers/usb/gadget/legacy/zero.c b/drivers/usb/gadget/legacy/zero.c index a05785bdeb30..08a21bd0c2ba 100644 --- a/drivers/usb/gadget/legacy/zero.c +++ b/drivers/usb/gadget/legacy/zero.c @@ -147,6 +147,12 @@ static struct usb_gadget_strings *dev_strings[] = { NULL, }; +static struct usb_function *func_lb; +static struct usb_function_instance *func_inst_lb; + +static struct usb_function *func_ss; +static struct usb_function_instance *func_inst_ss; + /*-------------------------------------------------------------------------*/ static struct timer_list autoresume_timer; @@ -156,6 +162,7 @@ static void zero_autoresume(struct timer_list *unused) { struct usb_composite_dev *cdev = autoresume_cdev; struct usb_gadget *g = cdev->gadget; + int status; /* unconfigured devices can't issue wakeups */ if (!cdev->config) @@ -165,10 +172,18 @@ static void zero_autoresume(struct timer_list *unused) * more significant than just a timer firing; likely * because of some direct user request. */ - if (g->speed != USB_SPEED_UNKNOWN) { - int status = usb_gadget_wakeup(g); - INFO(cdev, "%s --> %d\n", __func__, status); + if (g->speed == USB_SPEED_UNKNOWN) + return; + + if (g->speed >= USB_SPEED_SUPER) { + if (loopdefault) + status = usb_func_wakeup(func_lb); + else + status = usb_func_wakeup(func_ss); + } else { + status = usb_gadget_wakeup(g); } + INFO(cdev, "%s --> %d\n", __func__, status); } static void zero_suspend(struct usb_composite_dev *cdev) @@ -206,9 +221,6 @@ static struct usb_configuration loopback_driver = { /* .iConfiguration = DYNAMIC */ }; -static struct usb_function *func_ss; -static struct usb_function_instance *func_inst_ss; - static int ss_config_setup(struct usb_configuration *c, const struct usb_ctrlrequest *ctrl) { @@ -248,9 +260,6 @@ module_param_named(isoc_maxburst, gzero_options.isoc_maxburst, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(isoc_maxburst, "0 - 15 (ss only)"); -static struct usb_function *func_lb; -static struct usb_function_instance *func_inst_lb; - module_param_named(qlen, gzero_options.qlen, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(qlen, "depth of loopback queue"); diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c b/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c index 7e69944ef18a..9b53daf76583 100644 --- a/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c +++ b/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c @@ -2415,7 +2415,6 @@ int cdns2_gadget_resume(struct cdns2_device *pdev, bool hibernated) void cdns2_gadget_remove(struct cdns2_device *pdev) { - pm_runtime_mark_last_busy(pdev->dev); pm_runtime_put_autosuspend(pdev->dev); usb_del_gadget(&pdev->gadget); diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c index 0c38fc37b6e6..9d2007f448c0 100644 --- a/drivers/usb/gadget/udc/tegra-xudc.c +++ b/drivers/usb/gadget/udc/tegra-xudc.c @@ -1558,12 +1558,6 @@ static int __tegra_xudc_ep_set_halt(struct tegra_xudc_ep *ep, bool halt) return -ENOTSUPP; } - if (!!(xudc_readl(xudc, EP_HALT) & BIT(ep->index)) == halt) { - dev_dbg(xudc->dev, "EP %u already %s\n", ep->index, - halt ? "halted" : "not halted"); - return 0; - } - if (halt) { ep_halt(xudc, ep->index); } else { diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index 6aab45c8525c..f61f095cedab 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -111,8 +112,7 @@ static void ehci_platform_power_off(struct platform_device *dev) int clk; for (clk = EHCI_MAX_CLKS - 1; clk >= 0; clk--) - if (priv->clks[clk]) - clk_disable_unprepare(priv->clks[clk]); + clk_disable_unprepare(priv->clks[clk]); } static struct hc_driver __read_mostly ehci_platform_hc_driver; @@ -239,9 +239,11 @@ static int ehci_platform_probe(struct platform_device *dev) struct usb_hcd *hcd; struct resource *res_mem; struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev); + const struct of_device_id *match; struct ehci_platform_priv *priv; struct ehci_hcd *ehci; int err, irq, clk = 0; + bool dma_mask_64; if (usb_disabled()) return -ENODEV; @@ -253,8 +255,13 @@ static int ehci_platform_probe(struct platform_device *dev) if (!pdata) pdata = &ehci_platform_defaults; + dma_mask_64 = pdata->dma_mask_64; + match = of_match_device(dev->dev.driver->of_match_table, &dev->dev); + if (match && match->data) + dma_mask_64 = true; + err = dma_coerce_mask_and_coherent(&dev->dev, - pdata->dma_mask_64 ? DMA_BIT_MASK(64) : DMA_BIT_MASK(32)); + dma_mask_64 ? DMA_BIT_MASK(64) : DMA_BIT_MASK(32)); if (err) { dev_err(&dev->dev, "Error: DMA mask configuration failed\n"); return err; @@ -298,7 +305,9 @@ static int ehci_platform_probe(struct platform_device *dev) if (of_device_is_compatible(dev->dev.of_node, "aspeed,ast2500-ehci") || of_device_is_compatible(dev->dev.of_node, - "aspeed,ast2600-ehci")) + "aspeed,ast2600-ehci") || + of_device_is_compatible(dev->dev.of_node, + "aspeed,ast2700-ehci")) ehci->is_aspeed = 1; if (soc_device_match(quirk_poll_match)) @@ -445,6 +454,17 @@ static int __maybe_unused ehci_platform_suspend(struct device *dev) if (pdata->power_suspend) pdata->power_suspend(pdev); + ret = reset_control_assert(priv->rsts); + if (ret) { + if (pdata->power_on) + pdata->power_on(pdev); + + ehci_resume(hcd, false); + + if (priv->quirk_poll) + quirk_poll_init(priv); + } + return ret; } @@ -455,11 +475,18 @@ static int __maybe_unused ehci_platform_resume(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); struct device *companion_dev; + int err; + + err = reset_control_deassert(priv->rsts); + if (err) + return err; if (pdata->power_on) { - int err = pdata->power_on(pdev); - if (err < 0) + err = pdata->power_on(pdev); + if (err < 0) { + reset_control_assert(priv->rsts); return err; + } } companion_dev = usb_of_get_companion_dev(hcd->self.controller); @@ -485,6 +512,7 @@ static const struct of_device_id vt8500_ehci_ids[] = { { .compatible = "wm,prizm-ehci", }, { .compatible = "generic-ehci", }, { .compatible = "cavium,octeon-6335-ehci", }, + { .compatible = "aspeed,ast2700-ehci", .data = (void *)1 }, {} }; MODULE_DEVICE_TABLE(of, vt8500_ehci_ids); diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c index 3c5ca2d7c92e..0938c0e7a8b6 100644 --- a/drivers/usb/host/ohci-da8xx.c +++ b/drivers/usb/host/ohci-da8xx.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include @@ -166,17 +165,6 @@ static int ohci_da8xx_has_oci(struct usb_hcd *hcd) return 0; } -static int ohci_da8xx_has_potpgt(struct usb_hcd *hcd) -{ - struct device *dev = hcd->self.controller; - struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); - - if (hub && hub->potpgt) - return 1; - - return 0; -} - static int ohci_da8xx_regulator_event(struct notifier_block *nb, unsigned long event, void *data) { @@ -228,7 +216,6 @@ static int ohci_da8xx_register_notify(struct usb_hcd *hcd) static int ohci_da8xx_reset(struct usb_hcd *hcd) { struct device *dev = hcd->self.controller; - struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); struct ohci_hcd *ohci = hcd_to_ohci(hcd); int result; u32 rh_a; @@ -266,10 +253,6 @@ static int ohci_da8xx_reset(struct usb_hcd *hcd) rh_a &= ~RH_A_NOCP; rh_a |= RH_A_OCPM; } - if (ohci_da8xx_has_potpgt(hcd)) { - rh_a &= ~RH_A_POTPGT; - rh_a |= hub->potpgt << 24; - } ohci_writel(ohci, rh_a, &ohci->regs->roothub.a); return result; diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c index f47ae12cde6a..2e4bb5cc2165 100644 --- a/drivers/usb/host/ohci-platform.c +++ b/drivers/usb/host/ohci-platform.c @@ -69,8 +69,7 @@ static void ohci_platform_power_off(struct platform_device *dev) int clk; for (clk = OHCI_MAX_CLKS - 1; clk >= 0; clk--) - if (priv->clks[clk]) - clk_disable_unprepare(priv->clks[clk]); + clk_disable_unprepare(priv->clks[clk]); } static struct hc_driver __read_mostly ohci_platform_hc_driver; @@ -271,6 +270,7 @@ static int ohci_platform_suspend(struct device *dev) struct usb_hcd *hcd = dev_get_drvdata(dev); struct usb_ohci_pdata *pdata = dev->platform_data; struct platform_device *pdev = to_platform_device(dev); + struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); bool do_wakeup = device_may_wakeup(dev); int ret; @@ -281,6 +281,14 @@ static int ohci_platform_suspend(struct device *dev) if (pdata->power_suspend) pdata->power_suspend(pdev); + ret = reset_control_assert(priv->resets); + if (ret) { + if (pdata->power_on) + pdata->power_on(pdev); + + ohci_resume(hcd, false); + } + return ret; } @@ -289,11 +297,19 @@ static int ohci_platform_resume_common(struct device *dev, bool hibernated) struct usb_hcd *hcd = dev_get_drvdata(dev); struct usb_ohci_pdata *pdata = dev_get_platdata(dev); struct platform_device *pdev = to_platform_device(dev); + struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); + int err; + + err = reset_control_deassert(priv->resets); + if (err) + return err; if (pdata->power_on) { - int err = pdata->power_on(pdev); - if (err < 0) + err = pdata->power_on(pdev); + if (err < 0) { + reset_control_assert(priv->resets); return err; + } } ohci_resume(hcd, hibernated); diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h index 13ee2a6144b2..4326d1f3ca76 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h @@ -445,6 +445,7 @@ struct uhci_hcd { short load[MAX_PHASE]; /* Periodic allocations */ struct clk *clk; /* (optional) clock source */ + struct reset_control *rsts; /* (optional) clock reset */ /* Reset host controller */ void (*reset_hc) (struct uhci_hcd *uhci); diff --git a/drivers/usb/host/uhci-platform.c b/drivers/usb/host/uhci-platform.c index 62318291f566..5e02f2ceafb6 100644 --- a/drivers/usb/host/uhci-platform.c +++ b/drivers/usb/host/uhci-platform.c @@ -11,6 +11,7 @@ #include #include #include +#include static int uhci_platform_init(struct usb_hcd *hcd) { @@ -67,6 +68,7 @@ static const struct hc_driver uhci_platform_hc_driver = { static int uhci_hcd_platform_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; + bool dma_mask_64 = false; struct usb_hcd *hcd; struct uhci_hcd *uhci; struct resource *res; @@ -80,7 +82,11 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev) * Since shared usb code relies on it, set it here for now. * Once we have dma capability bindings this can go away. */ - ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (of_device_get_match_data(&pdev->dev)) + dma_mask_64 = true; + + ret = dma_coerce_mask_and_coherent(&pdev->dev, + dma_mask_64 ? DMA_BIT_MASK(64) : DMA_BIT_MASK(32)); if (ret) return ret; @@ -113,7 +119,8 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev) } if (of_device_is_compatible(np, "aspeed,ast2400-uhci") || of_device_is_compatible(np, "aspeed,ast2500-uhci") || - of_device_is_compatible(np, "aspeed,ast2600-uhci")) { + of_device_is_compatible(np, "aspeed,ast2600-uhci") || + of_device_is_compatible(np, "aspeed,ast2700-uhci")) { uhci->is_aspeed = 1; dev_info(&pdev->dev, "Enabled Aspeed implementation workarounds\n"); @@ -132,17 +139,28 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev) goto err_rmr; } + uhci->rsts = devm_reset_control_array_get_optional_shared(&pdev->dev); + if (IS_ERR(uhci->rsts)) { + ret = PTR_ERR(uhci->rsts); + goto err_clk; + } + ret = reset_control_deassert(uhci->rsts); + if (ret) + goto err_clk; + ret = platform_get_irq(pdev, 0); if (ret < 0) - goto err_clk; + goto err_reset; ret = usb_add_hcd(hcd, ret, IRQF_SHARED); if (ret) - goto err_clk; + goto err_reset; device_wakeup_enable(hcd->self.controller); return 0; +err_reset: + reset_control_assert(uhci->rsts); err_clk: clk_disable_unprepare(uhci->clk); err_rmr: @@ -156,6 +174,7 @@ static void uhci_hcd_platform_remove(struct platform_device *pdev) struct usb_hcd *hcd = platform_get_drvdata(pdev); struct uhci_hcd *uhci = hcd_to_uhci(hcd); + reset_control_assert(uhci->rsts); clk_disable_unprepare(uhci->clk); usb_remove_hcd(hcd); usb_put_hcd(hcd); @@ -178,6 +197,7 @@ static void uhci_hcd_platform_shutdown(struct platform_device *op) static const struct of_device_id platform_uhci_ids[] = { { .compatible = "generic-uhci", }, { .compatible = "platform-uhci", }, + { .compatible = "aspeed,ast2700-uhci", .data = (void *)1 }, {} }; MODULE_DEVICE_TABLE(of, platform_uhci_ids); diff --git a/drivers/usb/host/xen-hcd.c b/drivers/usb/host/xen-hcd.c index 1c2a95fe41e5..0a94d302911a 100644 --- a/drivers/usb/host/xen-hcd.c +++ b/drivers/usb/host/xen-hcd.c @@ -1388,7 +1388,7 @@ static int xenhcd_get_frame(struct usb_hcd *hcd) return 0; } -static struct hc_driver xenhcd_usb20_hc_driver = { +static const struct hc_driver xenhcd_usb20_hc_driver = { .description = "xen-hcd", .product_desc = "Xen USB2.0 Virtual Host Controller", .hcd_priv_size = sizeof(struct xenhcd_info), @@ -1413,7 +1413,7 @@ static struct hc_driver xenhcd_usb20_hc_driver = { #endif }; -static struct hc_driver xenhcd_usb11_hc_driver = { +static const struct hc_driver xenhcd_usb11_hc_driver = { .description = "xen-hcd", .product_desc = "Xen USB1.1 Virtual Host Controller", .hcd_priv_size = sizeof(struct xenhcd_info), diff --git a/drivers/usb/host/xhci-caps.h b/drivers/usb/host/xhci-caps.h index 89bc83e4f1eb..2f59b6ab1e45 100644 --- a/drivers/usb/host/xhci-caps.h +++ b/drivers/usb/host/xhci-caps.h @@ -1,93 +1,120 @@ /* SPDX-License-Identifier: GPL-2.0 */ +/* + * xHCI Host Controller Capability Registers. + * xHCI Specification Section 5.3, Revision 1.2. + */ -/* hc_capbase bitmasks */ -/* bits 7:0 - how long is the Capabilities register */ -#define HC_LENGTH(p) XHCI_HC_LENGTH(p) -/* bits 31:16 */ +#include + +/* hc_capbase - bitmasks */ +/* bits 7:0 - Capability Registers Length */ +#define HC_LENGTH(p) ((p) & 0xff) +/* bits 15:8 - Rsvd */ +/* bits 31:16 - Host Controller Interface Version Number */ #define HC_VERSION(p) (((p) >> 16) & 0xffff) /* HCSPARAMS1 - hcs_params1 - bitmasks */ -/* bits 0:7, Max Device Slots */ +/* bits 7:0 - Number of Device Slots */ #define HCS_MAX_SLOTS(p) (((p) >> 0) & 0xff) #define HCS_SLOTS_MASK 0xff -/* bits 8:18, Max Interrupters */ +/* bits 18:8 - Number of Interrupters, max values is 1024 */ #define HCS_MAX_INTRS(p) (((p) >> 8) & 0x7ff) -/* bits 24:31, Max Ports - max value is 0x7F = 127 ports */ -#define HCS_MAX_PORTS(p) (((p) >> 24) & 0x7f) +/* bits 31:24, Max Ports - max value is 255 */ +#define HCS_MAX_PORTS(p) (((p) >> 24) & 0xff) /* HCSPARAMS2 - hcs_params2 - bitmasks */ -/* bits 0:3, frames or uframes that SW needs to queue transactions - * ahead of the HW to meet periodic deadlines */ -#define HCS_IST(p) (((p) >> 0) & 0xf) -/* bits 4:7, max number of Event Ring segments */ +/* + * bits 3:0 - Isochronous Scheduling Threshold, frames or uframes that SW + * needs to queue transactions ahead of the HW to meet periodic deadlines. + * - Bits 2:0: Threshold value + * - Bit 3: Unit indicator + * - '1': Threshold in Frames + * - '0': Threshold in Microframes (uframes) + * Note: 1 Frame = 8 Microframes + * xHCI specification section 5.3.4. + */ +#define HCS_IST_VALUE(p) ((p) & 0x7) +#define HCS_IST_UNIT BIT(3) +/* bits 7:4 - Event Ring Segment Table Max, 2^(n) */ #define HCS_ERST_MAX(p) (((p) >> 4) & 0xf) -/* bits 21:25 Hi 5 bits of Scratchpad buffers SW must allocate for the HW */ -/* bit 26 Scratchpad restore - for save/restore HW state - not used yet */ -/* bits 27:31 Lo 5 bits of Scratchpad buffers SW must allocate for the HW */ -#define HCS_MAX_SCRATCHPAD(p) ((((p) >> 16) & 0x3e0) | (((p) >> 27) & 0x1f)) +/* bits 20:8 - Rsvd */ +/* bits 25:21 - Max Scratchpad Buffers (Hi), 5 Most significant bits */ +#define HCS_MAX_SP_HI(p) (((p) >> 21) & 0x1f) +/* bit 26 - Scratchpad restore, for save/restore HW state */ +/* bits 31:27 - Max Scratchpad Buffers (Lo), 5 Least significant bits */ +#define HCS_MAX_SP_LO(p) (((p) >> 27) & 0x1f) +#define HCS_MAX_SCRATCHPAD(p) (HCS_MAX_SP_HI(p) << 5 | HCS_MAX_SP_LO(p)) /* HCSPARAMS3 - hcs_params3 - bitmasks */ -/* bits 0:7, Max U1 to U0 latency for the roothub ports */ +/* bits 7:0 - U1 Device Exit Latency, Max U1 to U0 latency for the roothub ports */ #define HCS_U1_LATENCY(p) (((p) >> 0) & 0xff) -/* bits 16:31, Max U2 to U0 latency for the roothub ports */ +/* bits 15:8 - Rsvd */ +/* bits 31:16 - U2 Device Exit Latency, Max U2 to U0 latency for the roothub ports */ #define HCS_U2_LATENCY(p) (((p) >> 16) & 0xffff) -/* HCCPARAMS - hcc_params - bitmasks */ -/* true: HC can use 64-bit address pointers */ -#define HCC_64BIT_ADDR(p) ((p) & (1 << 0)) -/* true: HC can do bandwidth negotiation */ -#define HCC_BANDWIDTH_NEG(p) ((p) & (1 << 1)) -/* true: HC uses 64-byte Device Context structures - * FIXME 64-byte context structures aren't supported yet. - */ -#define HCC_64BYTE_CONTEXT(p) ((p) & (1 << 2)) -/* true: HC has port power switches */ -#define HCC_PPC(p) ((p) & (1 << 3)) -/* true: HC has port indicators */ -#define HCS_INDICATOR(p) ((p) & (1 << 4)) -/* true: HC has Light HC Reset Capability */ -#define HCC_LIGHT_RESET(p) ((p) & (1 << 5)) -/* true: HC supports latency tolerance messaging */ -#define HCC_LTC(p) ((p) & (1 << 6)) -/* true: no secondary Stream ID Support */ -#define HCC_NSS(p) ((p) & (1 << 7)) -/* true: HC supports Stopped - Short Packet */ -#define HCC_SPC(p) ((p) & (1 << 9)) -/* true: HC has Contiguous Frame ID Capability */ -#define HCC_CFC(p) ((p) & (1 << 11)) -/* Max size for Primary Stream Arrays - 2^(n+1), where n is bits 12:15 */ +/* HCCPARAMS1 - hcc_params - bitmasks */ +/* bit 0 - 64-bit Addressing Capability */ +#define HCC_64BIT_ADDR BIT(0) +/* bit 1 - BW Negotiation Capability */ +#define HCC_BANDWIDTH_NEG BIT(1) +/* bit 2 - Context Size */ +#define HCC_64BYTE_CONTEXT BIT(2) +#define CTX_SIZE(_hcc) (_hcc & HCC_64BYTE_CONTEXT ? 64 : 32) +/* bit 3 - Port Power Control */ +#define HCC_PPC BIT(3) +/* bit 4 - Port Indicators */ +#define HCS_INDICATOR BIT(4) +/* bit 5 - Light HC Reset Capability */ +#define HCC_LIGHT_RESET BIT(5) +/* bit 6 - Latency Tolerance Messaging Capability */ +#define HCC_LTC BIT(6) +/* bit 7 - No Secondary Stream ID Support */ +#define HCC_NSS BIT(7) +/* bit 8 - Parse All Event Data */ +/* bit 9 - Short Packet Capability */ +#define HCC_SPC BIT(9) +/* bit 10 - Stopped EDTLA Capability */ +/* bit 11 - Contiguous Frame ID Capability */ +#define HCC_CFC BIT(11) +/* bits 15:12 - Max size for Primary Stream Arrays, 2^(n+1) */ #define HCC_MAX_PSA(p) (1 << ((((p) >> 12) & 0xf) + 1)) -/* Extended Capabilities pointer from PCI base - section 5.3.6 */ -#define HCC_EXT_CAPS(p) XHCI_HCC_EXT_CAPS(p) +/* bits 31:16 - xHCI Extended Capabilities Pointer, from PCI base: 2^(n) */ +#define HCC_EXT_CAPS(p) (((p) >> 16) & 0xffff) -#define CTX_SIZE(_hcc) (HCC_64BYTE_CONTEXT(_hcc) ? 64 : 32) - -/* db_off bitmask - bits 31:2 Doorbell Array Offset */ +/* DBOFF - db_off - bitmasks */ +/* bits 1:0 - Rsvd */ +/* bits 31:2 - Doorbell Array Offset */ #define DBOFF_MASK (0xfffffffc) -/* run_regs_off bitmask - bits 0:4 reserved */ +/* RTSOFF - run_regs_off - bitmasks */ +/* bits 4:0 - Rsvd */ +/* bits 31:5 - Runtime Register Space Offse */ #define RTSOFF_MASK (~0x1f) /* HCCPARAMS2 - hcc_params2 - bitmasks */ -/* true: HC supports U3 entry Capability */ -#define HCC2_U3C(p) ((p) & (1 << 0)) -/* true: HC supports Configure endpoint command Max exit latency too large */ -#define HCC2_CMC(p) ((p) & (1 << 1)) -/* true: HC supports Force Save context Capability */ -#define HCC2_FSC(p) ((p) & (1 << 2)) -/* true: HC supports Compliance Transition Capability */ -#define HCC2_CTC(p) ((p) & (1 << 3)) -/* true: HC support Large ESIT payload Capability > 48k */ -#define HCC2_LEC(p) ((p) & (1 << 4)) -/* true: HC support Configuration Information Capability */ -#define HCC2_CIC(p) ((p) & (1 << 5)) -/* true: HC support Extended TBC Capability, Isoc burst count > 65535 */ -#define HCC2_ETC(p) ((p) & (1 << 6)) -/* true: HC support Extended TBC TRB Status Capability */ -#define HCC2_ETC_TSC(p) ((p) & (1 << 7)) -/* true: HC support Get/Set Extended Property Capability */ -#define HCC2_GSC(p) ((p) & (1 << 8)) -/* true: HC support Virtualization Based Trusted I/O Capability */ -#define HCC2_VTC(p) ((p) & (1 << 9)) -/* true: HC support Double BW on a eUSB2 HS ISOC EP */ -#define HCC2_EUSB2_DIC(p) ((p) & (1 << 11)) +/* bit 0 - U3 Entry Capability */ +#define HCC2_U3C BIT(0) +/* bit 1 - Configure Endpoint Command Max Exit Latency Too Large Capability */ +#define HCC2_CMC BIT(1) +/* bit 2 - Force Save Context Capabilitu */ +#define HCC2_FSC BIT(2) +/* bit 3 - Compliance Transition Capability, false: compliance is enabled by default */ +#define HCC2_CTC BIT(3) +/* bit 4 - Large ESIT Payload Capability, true: HC support ESIT payload > 48k */ +#define HCC2_LEC BIT(4) +/* bit 5 - Configuration Information Capability */ +#define HCC2_CIC BIT(5) +/* bit 6 - Extended TBC Capability, true: Isoc burst count > 65535 */ +#define HCC2_ETC BIT(6) +/* bit 7 - Extended TBC TRB Status Capability */ +#define HCC2_ETC_TSC BIT(7) +/* bit 8 - Get/Set Extended Property Capability */ +#define HCC2_GSC BIT(8) +/* bit 9 - Virtualization Based Trusted I/O Capability */ +#define HCC2_VTC BIT(9) +/* bit 10 - Rsvd */ +/* bit 11 - HC support Double BW on a eUSB2 HS ISOC EP */ +#define HCC2_EUSB2_DIC BIT(11) +/* bit 12 - HC support eUSB2V2 capability */ +#define HCC2_E2V2C BIT(12) +/* bits 31:13 - Rsvd */ diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c index ecda964e018a..9da4f3b452cb 100644 --- a/drivers/usb/host/xhci-dbgcap.c +++ b/drivers/usb/host/xhci-dbgcap.c @@ -374,7 +374,7 @@ int dbc_ep_queue(struct dbc_request *req) ret = dbc_ep_do_queue(req); spin_unlock_irqrestore(&dbc->lock, flags); - mod_delayed_work(system_wq, &dbc->event_work, 0); + mod_delayed_work(system_percpu_wq, &dbc->event_work, 0); trace_xhci_dbc_queue_request(req); @@ -677,7 +677,7 @@ static int xhci_dbc_start(struct xhci_dbc *dbc) return ret; } - return mod_delayed_work(system_wq, &dbc->event_work, + return mod_delayed_work(system_percpu_wq, &dbc->event_work, msecs_to_jiffies(dbc->poll_interval)); } @@ -1023,7 +1023,7 @@ static void xhci_dbc_handle_events(struct work_struct *work) return; } - mod_delayed_work(system_wq, &dbc->event_work, + mod_delayed_work(system_percpu_wq, &dbc->event_work, msecs_to_jiffies(poll_interval)); } @@ -1274,7 +1274,7 @@ static ssize_t dbc_poll_interval_ms_store(struct device *dev, dbc->poll_interval = value; - mod_delayed_work(system_wq, &dbc->event_work, 0); + mod_delayed_work(system_percpu_wq, &dbc->event_work, 0); return size; } diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c index c6d44977193f..c1eb1036ede9 100644 --- a/drivers/usb/host/xhci-debugfs.c +++ b/drivers/usb/host/xhci-debugfs.c @@ -329,7 +329,7 @@ static int xhci_portsc_show(struct seq_file *s, void *unused) u32 portsc; char str[XHCI_MSG_MAX]; - portsc = readl(port->addr); + portsc = xhci_portsc_readl(port); seq_printf(s, "%s\n", xhci_decode_portsc(str, portsc)); return 0; @@ -355,11 +355,11 @@ static ssize_t xhci_port_write(struct file *file, const char __user *ubuf, if (!strncmp(buf, "compliance", 10)) { /* If CTC is clear, compliance is enabled by default */ - if (!HCC2_CTC(xhci->hcc_params2)) + if (!(xhci->hcc_params2 & HCC2_CTC)) return count; spin_lock_irqsave(&xhci->lock, flags); /* compliance mode can only be enabled on ports in RxDetect */ - portsc = readl(port->addr); + portsc = xhci_portsc_readl(port); if ((portsc & PORT_PLS_MASK) != XDEV_RXDETECT) { spin_unlock_irqrestore(&xhci->lock, flags); return -EPERM; @@ -367,7 +367,7 @@ static ssize_t xhci_port_write(struct file *file, const char __user *ubuf, portsc = xhci_port_state_to_neutral(portsc); portsc &= ~PORT_PLS_MASK; portsc |= PORT_LINK_STROBE | XDEV_COMP_MODE; - writel(portsc, port->addr); + xhci_portsc_writel(port, portsc); spin_unlock_irqrestore(&xhci->lock, flags); } else { return -EINVAL; @@ -383,6 +383,39 @@ static const struct file_operations port_fops = { .release = single_release, }; +static int xhci_portli_show(struct seq_file *s, void *unused) +{ + struct xhci_port *port = s->private; + struct xhci_hcd *xhci = hcd_to_xhci(port->rhub->hcd); + u32 portli; + + portli = readl(&port->port_reg->portli); + + /* PORTLI fields are valid if port is a USB3 or eUSB2V2 port */ + if (port->rhub == &xhci->usb3_rhub) + seq_printf(s, "0x%08x LEC=%u RLC=%u TLC=%u\n", portli, + PORT_LEC(portli), PORT_RX_LANES(portli), PORT_TX_LANES(portli)); + else if (xhci->hcc_params2 & HCC2_E2V2C) + seq_printf(s, "0x%08x RDR=%u TDR=%u\n", portli, + PORTLI_RDR(portli), PORTLI_TDR(portli)); + else + seq_printf(s, "0x%08x RsvdP\n", portli); + + return 0; +} + +static int xhci_portli_open(struct inode *inode, struct file *file) +{ + return single_open(file, xhci_portli_show, inode->i_private); +} + +static const struct file_operations portli_fops = { + .open = xhci_portli_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + static void xhci_debugfs_create_files(struct xhci_hcd *xhci, struct xhci_file_map *files, size_t nentries, void *data, @@ -613,28 +646,24 @@ void xhci_debugfs_remove_slot(struct xhci_hcd *xhci, int slot_id) static void xhci_debugfs_create_ports(struct xhci_hcd *xhci, struct dentry *parent) { - unsigned int num_ports; char port_name[8]; struct xhci_port *port; struct dentry *dir; - num_ports = HCS_MAX_PORTS(xhci->hcs_params1); - parent = debugfs_create_dir("ports", parent); - while (num_ports--) { - scnprintf(port_name, sizeof(port_name), "port%02d", - num_ports + 1); + for (int i = 0; i < xhci->max_ports; i++) { + scnprintf(port_name, sizeof(port_name), "port%02d", i + 1); dir = debugfs_create_dir(port_name, parent); - port = &xhci->hw_ports[num_ports]; + port = &xhci->hw_ports[i]; debugfs_create_file("portsc", 0644, dir, port, &port_fops); + debugfs_create_file("portli", 0444, dir, port, &portli_fops); } } static int xhci_port_bw_show(struct xhci_hcd *xhci, u8 dev_speed, struct seq_file *s) { - unsigned int num_ports; unsigned int i; int ret; struct xhci_container_ctx *ctx; @@ -645,8 +674,6 @@ static int xhci_port_bw_show(struct xhci_hcd *xhci, u8 dev_speed, if (ret < 0) return ret; - num_ports = HCS_MAX_PORTS(xhci->hcs_params1); - ctx = xhci_alloc_port_bw_ctx(xhci, 0); if (!ctx) { pm_runtime_put_sync(dev); @@ -661,7 +688,7 @@ static int xhci_port_bw_show(struct xhci_hcd *xhci, u8 dev_speed, /* print all roothub ports available bandwidth * refer to xhci rev1_2 protocol 6.2.6 , byte 0 is reserved */ - for (i = 1; i < num_ports+1; i++) + for (i = 1; i <= xhci->max_ports; i++) seq_printf(s, "port[%d] available bw: %d%%.\n", i, ctx->bytes[i]); err_out: diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index b3a59ce1b3f4..04cc3d681495 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -110,7 +110,7 @@ static int xhci_create_usb3x_bos_desc(struct xhci_hcd *xhci, char *buf, ss_cap->bU2DevExitLat = 0; /* set later */ reg = readl(&xhci->cap_regs->hcc_params); - if (HCC_LTC(reg)) + if (reg & HCC_LTC) ss_cap->bmAttributes |= USB_LTM_SUPPORT; if ((xhci->quirks & XHCI_LPM_SUPPORT)) { @@ -263,7 +263,7 @@ static void xhci_common_hub_descriptor(struct xhci_hcd *xhci, desc->bNbrPorts = ports; temp = 0; /* Bits 1:0 - support per-port power switching, or power always on */ - if (HCC_PPC(xhci->hcc_params)) + if (xhci->hcc_params & HCC_PPC) temp |= HUB_CHAR_INDV_PORT_LPSM; else temp |= HUB_CHAR_NO_LPSM; @@ -299,7 +299,7 @@ static void xhci_usb2_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci, */ memset(port_removable, 0, sizeof(port_removable)); for (i = 0; i < ports; i++) { - portsc = readl(rhub->ports[i]->addr); + portsc = xhci_portsc_readl(rhub->ports[i]); /* If a device is removable, PORTSC reports a 0, same as in the * hub descriptor DeviceRemovable bits. */ @@ -356,7 +356,7 @@ static void xhci_usb3_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci, port_removable = 0; /* bit 0 is reserved, bit 1 is for port 1, etc. */ for (i = 0; i < ports; i++) { - portsc = readl(rhub->ports[i]->addr); + portsc = xhci_portsc_readl(rhub->ports[i]); if (portsc & PORT_DEV_REMOVE) port_removable |= 1 << (i + 1); } @@ -566,19 +566,19 @@ static void xhci_disable_port(struct xhci_hcd *xhci, struct xhci_port *port) return; } - portsc = readl(port->addr); + portsc = xhci_portsc_readl(port); portsc = xhci_port_state_to_neutral(portsc); /* Write 1 to disable the port */ - writel(portsc | PORT_PE, port->addr); + xhci_portsc_writel(port, portsc | PORT_PE); - portsc = readl(port->addr); + portsc = xhci_portsc_readl(port); xhci_dbg(xhci, "disable port %d-%d, portsc: 0x%x\n", hcd->self.busnum, port->hcd_portnum + 1, portsc); } static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue, - u16 wIndex, __le32 __iomem *addr, u32 port_status) + u16 wIndex, struct xhci_port *port, u32 port_status) { char *port_change_bit; u32 status; @@ -621,8 +621,8 @@ static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue, return; } /* Change bits are all write 1 to clear */ - writel(port_status | status, addr); - port_status = readl(addr); + xhci_portsc_writel(port, port_status | status); + port_status = xhci_portsc_readl(port); xhci_dbg(xhci, "clear port%d %s change, portsc: 0x%x\n", wIndex + 1, port_change_bit, port_status); @@ -650,7 +650,7 @@ static void xhci_set_port_power(struct xhci_hcd *xhci, struct xhci_port *port, u32 temp; hcd = port->rhub->hcd; - temp = readl(port->addr); + temp = xhci_portsc_readl(port); xhci_dbg(xhci, "set port power %d-%d %s, portsc: 0x%x\n", hcd->self.busnum, port->hcd_portnum + 1, on ? "ON" : "OFF", temp); @@ -659,11 +659,11 @@ static void xhci_set_port_power(struct xhci_hcd *xhci, struct xhci_port *port, if (on) { /* Power on */ - writel(temp | PORT_POWER, port->addr); - readl(port->addr); + xhci_portsc_writel(port, temp | PORT_POWER); + xhci_portsc_readl(port); } else { /* Power off */ - writel(temp & ~PORT_POWER, port->addr); + xhci_portsc_writel(port, temp & ~PORT_POWER); } spin_unlock_irqrestore(&xhci->lock, *flags); @@ -683,9 +683,9 @@ static void xhci_port_set_test_mode(struct xhci_hcd *xhci, /* xhci only supports test mode for usb2 ports */ port = xhci->usb2_rhub.ports[wIndex]; - temp = readl(port->addr + PORTPMSC); + temp = readl(&port->port_reg->portpmsc); temp |= test_mode << PORT_TEST_MODE_SHIFT; - writel(temp, port->addr + PORTPMSC); + writel(temp, &port->port_reg->portpmsc); xhci->test_mode = test_mode; if (test_mode == USB_TEST_FORCE_ENABLE) xhci_start(xhci); @@ -700,7 +700,7 @@ static int xhci_enter_test_mode(struct xhci_hcd *xhci, /* Disable all Device Slots */ xhci_dbg(xhci, "Disable all slots\n"); spin_unlock_irqrestore(&xhci->lock, *flags); - for (i = 1; i <= HCS_MAX_SLOTS(xhci->hcs_params1); i++) { + for (i = 1; i <= xhci->max_slots; i++) { if (!xhci->devs[i]) continue; @@ -801,11 +801,11 @@ void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port, u32 temp; u32 portsc; - portsc = readl(port->addr); + portsc = xhci_portsc_readl(port); temp = xhci_port_state_to_neutral(portsc); temp &= ~PORT_PLS_MASK; temp |= PORT_LINK_STROBE | link_state; - writel(temp, port->addr); + xhci_portsc_writel(port, temp); xhci_dbg(xhci, "Set port %d-%d link state, portsc: 0x%x, write 0x%x", port->rhub->hcd->self.busnum, port->hcd_portnum + 1, @@ -817,7 +817,7 @@ static void xhci_set_remote_wake_mask(struct xhci_hcd *xhci, { u32 temp; - temp = readl(port->addr); + temp = xhci_portsc_readl(port); temp = xhci_port_state_to_neutral(temp); if (wake_mask & USB_PORT_FEAT_REMOTE_WAKE_CONNECT) @@ -835,7 +835,7 @@ static void xhci_set_remote_wake_mask(struct xhci_hcd *xhci, else temp &= ~PORT_WKOC_E; - writel(temp, port->addr); + xhci_portsc_writel(port, temp); } /* Test and clear port RWC bit */ @@ -844,11 +844,11 @@ void xhci_test_and_clear_bit(struct xhci_hcd *xhci, struct xhci_port *port, { u32 temp; - temp = readl(port->addr); + temp = xhci_portsc_readl(port); if (temp & port_bit) { temp = xhci_port_state_to_neutral(temp); temp |= port_bit; - writel(temp, port->addr); + xhci_portsc_writel(port, temp); } } @@ -1002,7 +1002,7 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, } xhci_ring_device(xhci, port->slot_id); } else { - int port_status = readl(port->addr); + int port_status = xhci_portsc_readl(port); xhci_warn(xhci, "Port resume timed out, port %d-%d: 0x%x\n", hcd->self.busnum, wIndex + 1, port_status); @@ -1263,7 +1263,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, wIndex--; port = ports[portnum1 - 1]; - temp = readl(port->addr); + temp = xhci_portsc_readl(port); if (temp == ~(u32)0) { xhci_hc_died(xhci); retval = -ENODEV; @@ -1288,7 +1288,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, retval = -EINVAL; break; } - port_li = readl(port->addr + PORTLI); + port_li = readl(&port->port_reg->portli); status = xhci_get_ext_port_status(temp, port_li); put_unaligned_le32(status, &buf[4]); } @@ -1309,7 +1309,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, port = ports[portnum1 - 1]; wIndex--; - temp = readl(port->addr); + temp = xhci_portsc_readl(port); if (temp == ~(u32)0) { xhci_hc_died(xhci); retval = -ENODEV; @@ -1319,7 +1319,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, /* FIXME: What new port features do we need to support? */ switch (wValue) { case USB_PORT_FEAT_SUSPEND: - temp = readl(port->addr); + temp = xhci_portsc_readl(port); if ((temp & PORT_PLS_MASK) != XDEV_U0) { /* Resume the port to U0 first */ xhci_set_link_state(xhci, port, XDEV_U0); @@ -1331,7 +1331,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, * a port unless the port reports that it is in the * enabled (PED = ‘1’,PLS < ‘3’) state. */ - temp = readl(port->addr); + temp = xhci_portsc_readl(port); if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) || (temp & PORT_PLS_MASK) >= XDEV_U3) { xhci_warn(xhci, "USB core suspending port %d-%d not in U0/U1/U2\n", @@ -1354,11 +1354,11 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, msleep(10); /* wait device to enter */ spin_lock_irqsave(&xhci->lock, flags); - temp = readl(port->addr); + temp = xhci_portsc_readl(port); bus_state->suspended_ports |= 1 << wIndex; break; case USB_PORT_FEAT_LINK_STATE: - temp = readl(port->addr); + temp = xhci_portsc_readl(port); /* Disable port */ if (link_state == USB_SS_PORT_LS_SS_DISABLED) { xhci_dbg(xhci, "Disable port %d-%d\n", @@ -1371,8 +1371,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, temp |= PORT_CSC | PORT_PEC | PORT_WRC | PORT_OCC | PORT_RC | PORT_PLC | PORT_CEC; - writel(temp | PORT_PE, port->addr); - temp = readl(port->addr); + xhci_portsc_writel(port, temp | PORT_PE); + temp = xhci_portsc_readl(port); break; } @@ -1381,7 +1381,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, xhci_dbg(xhci, "Enable port %d-%d\n", hcd->self.busnum, portnum1); xhci_set_link_state(xhci, port, link_state); - temp = readl(port->addr); + temp = xhci_portsc_readl(port); break; } @@ -1400,7 +1400,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, * automatically entered as on 1.0 and prior. */ if (link_state == USB_SS_PORT_LS_COMP_MOD) { - if (!HCC2_CTC(xhci->hcc_params2)) { + if (!(xhci->hcc_params2 & HCC2_CTC)) { xhci_dbg(xhci, "CTC flag is 0, port already supports entering compliance mode\n"); break; } @@ -1414,7 +1414,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, hcd->self.busnum, portnum1); xhci_set_link_state(xhci, port, link_state); - temp = readl(port->addr); + temp = xhci_portsc_readl(port); break; } /* Port must be enabled */ @@ -1462,7 +1462,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, xhci_dbg(xhci, "missing U0 port change event for port %d-%d\n", hcd->self.busnum, portnum1); spin_lock_irqsave(&xhci->lock, flags); - temp = readl(port->addr); + temp = xhci_portsc_readl(port); break; } @@ -1480,12 +1480,12 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, spin_unlock_irqrestore(&xhci->lock, flags); while (retries--) { usleep_range(4000, 8000); - temp = readl(port->addr); + temp = xhci_portsc_readl(port); if ((temp & PORT_PLS_MASK) == XDEV_U3) break; } spin_lock_irqsave(&xhci->lock, flags); - temp = readl(port->addr); + temp = xhci_portsc_readl(port); bus_state->suspended_ports |= 1 << wIndex; } break; @@ -1500,38 +1500,38 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, break; case USB_PORT_FEAT_RESET: temp = (temp | PORT_RESET); - writel(temp, port->addr); + xhci_portsc_writel(port, temp); - temp = readl(port->addr); + temp = xhci_portsc_readl(port); xhci_dbg(xhci, "set port reset, actual port %d-%d status = 0x%x\n", hcd->self.busnum, portnum1, temp); break; case USB_PORT_FEAT_REMOTE_WAKE_MASK: xhci_set_remote_wake_mask(xhci, port, wake_mask); - temp = readl(port->addr); + temp = xhci_portsc_readl(port); xhci_dbg(xhci, "set port remote wake mask, actual port %d-%d status = 0x%x\n", hcd->self.busnum, portnum1, temp); break; case USB_PORT_FEAT_BH_PORT_RESET: temp |= PORT_WR; - writel(temp, port->addr); - temp = readl(port->addr); + xhci_portsc_writel(port, temp); + temp = xhci_portsc_readl(port); break; case USB_PORT_FEAT_U1_TIMEOUT: if (hcd->speed < HCD_USB3) goto error; - temp = readl(port->addr + PORTPMSC); + temp = readl(&port->port_reg->portpmsc); temp &= ~PORT_U1_TIMEOUT_MASK; temp |= PORT_U1_TIMEOUT(timeout); - writel(temp, port->addr + PORTPMSC); + writel(temp, &port->port_reg->portpmsc); break; case USB_PORT_FEAT_U2_TIMEOUT: if (hcd->speed < HCD_USB3) goto error; - temp = readl(port->addr + PORTPMSC); + temp = readl(&port->port_reg->portpmsc); temp &= ~PORT_U2_TIMEOUT_MASK; temp |= PORT_U2_TIMEOUT(timeout); - writel(temp, port->addr + PORTPMSC); + writel(temp, &port->port_reg->portpmsc); break; case USB_PORT_FEAT_TEST: /* 4.19.6 Port Test Modes (USB2 Test Mode) */ @@ -1547,7 +1547,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, goto error; } /* unblock any posted writes */ - temp = readl(port->addr); + temp = xhci_portsc_readl(port); break; case ClearPortFeature: if (!portnum1 || portnum1 > max_ports) @@ -1556,7 +1556,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, port = ports[portnum1 - 1]; wIndex--; - temp = readl(port->addr); + temp = xhci_portsc_readl(port); if (temp == ~(u32)0) { xhci_hc_died(xhci); retval = -ENODEV; @@ -1566,7 +1566,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, temp = xhci_port_state_to_neutral(temp); switch (wValue) { case USB_PORT_FEAT_SUSPEND: - temp = readl(port->addr); + temp = xhci_portsc_readl(port); xhci_dbg(xhci, "clear USB_PORT_FEAT_SUSPEND\n"); xhci_dbg(xhci, "PORTSC %04x\n", temp); if (temp & PORT_RESET) @@ -1603,8 +1603,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, case USB_PORT_FEAT_C_ENABLE: case USB_PORT_FEAT_C_PORT_LINK_STATE: case USB_PORT_FEAT_C_PORT_CONFIG_ERROR: - xhci_clear_port_change_bit(xhci, wValue, wIndex, - port->addr, temp); + xhci_clear_port_change_bit(xhci, wValue, wIndex, port, temp); break; case USB_PORT_FEAT_ENABLE: xhci_disable_port(xhci, port); @@ -1671,7 +1670,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) * SS devices are only visible to roothub after link training completes. * Keep polling roothubs for a grace period after xHC start */ - if (xhci->run_graceperiod) { + if (hcd->speed >= HCD_USB3 && xhci->run_graceperiod) { if (time_before(jiffies, xhci->run_graceperiod)) status = 1; else @@ -1682,7 +1681,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) /* For each port, did anything change? If so, set that bit in buf. */ for (i = 0; i < max_ports; i++) { - temp = readl(ports[i]->addr); + temp = xhci_portsc_readl(ports[i]); if (temp == ~(u32)0) { xhci_hc_died(xhci); retval = -ENODEV; @@ -1751,7 +1750,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd) u32 t1, t2; int retries = 10; retry: - t1 = readl(ports[port_index]->addr); + t1 = xhci_portsc_readl(ports[port_index]); t2 = xhci_port_state_to_neutral(t1); portsc_buf[port_index] = 0; @@ -1829,7 +1828,7 @@ retry: spin_lock_irqsave(&xhci->lock, flags); } } - writel(portsc_buf[port_index], ports[port_index]->addr); + xhci_portsc_writel(ports[port_index], portsc_buf[port_index]); } hcd->state = HC_STATE_SUSPENDED; bus_state->next_statechange = jiffies + msecs_to_jiffies(10); @@ -1850,7 +1849,7 @@ static bool xhci_port_missing_cas_quirk(struct xhci_port *port) { u32 portsc; - portsc = readl(port->addr); + portsc = xhci_portsc_readl(port); /* if any of these are set we are not stuck */ if (portsc & (PORT_CONNECT | PORT_CAS)) @@ -1863,9 +1862,9 @@ static bool xhci_port_missing_cas_quirk(struct xhci_port *port) /* clear wakeup/change bits, and do a warm port reset */ portsc &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS); portsc |= PORT_WR; - writel(portsc, port->addr); + xhci_portsc_writel(port, portsc); /* flush write */ - readl(port->addr); + xhci_portsc_readl(port); return true; } @@ -1912,7 +1911,7 @@ int xhci_bus_resume(struct usb_hcd *hcd) } port_index = max_ports; while (port_index--) { - portsc = readl(ports[port_index]->addr); + portsc = xhci_portsc_readl(ports[port_index]); /* warm reset CAS limited ports stuck in polling/compliance */ if ((xhci->quirks & XHCI_MISSING_CAS) && @@ -1942,7 +1941,7 @@ int xhci_bus_resume(struct usb_hcd *hcd) } /* disable wake for all ports, write new link state if needed */ portsc &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS); - writel(portsc, ports[port_index]->addr); + xhci_portsc_writel(ports[port_index], portsc); } /* USB2 specific resume signaling delay and U0 link state transition */ @@ -1963,7 +1962,7 @@ int xhci_bus_resume(struct usb_hcd *hcd) /* poll for U0 link state complete, both USB2 and USB3 */ for_each_set_bit(port_index, &bus_state->bus_suspended, BITS_PER_LONG) { - sret = xhci_handshake(ports[port_index]->addr, PORT_PLC, + sret = xhci_handshake(&ports[port_index]->port_reg->portsc, PORT_PLC, PORT_PLC, 10 * 1000); if (sret) { xhci_warn(xhci, "port %d-%d resume PLC timeout\n", diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 6e5b6057de79..c708bdd69f16 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -463,7 +463,7 @@ struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci, return NULL; ctx->type = type; - ctx->size = HCC_64BYTE_CONTEXT(xhci->hcc_params) ? 2048 : 1024; + ctx->size = xhci->hcc_params & HCC_64BYTE_CONTEXT ? 2048 : 1024; if (type == XHCI_CTX_TYPE_INPUT) ctx->size += CTX_SIZE(xhci->hcc_params); @@ -951,7 +951,7 @@ static void xhci_free_virt_devices_depth_first(struct xhci_hcd *xhci, int slot_i /* is this a hub device that added a tt_info to the tts list */ if (tt_info->slot_id == slot_id) { /* are any devices using this tt_info? */ - for (i = 1; i < HCS_MAX_SLOTS(xhci->hcs_params1); i++) { + for (i = 1; i < xhci->max_slots; i++) { vdev = xhci->devs[i]; if (vdev && (vdev->tt_info == tt_info)) xhci_free_virt_devices_depth_first( @@ -1344,7 +1344,7 @@ static u32 xhci_get_endpoint_mult(struct xhci_hcd *xhci, bool lec; /* xHCI 1.1 with LEC set does not use mult field, except intel eUSB2 */ - lec = xhci->hci_version > 0x100 && HCC2_LEC(xhci->hcc_params2); + lec = xhci->hci_version > 0x100 && (xhci->hcc_params2 & HCC2_LEC); /* eUSB2 double isoc bw devices are the only USB2 devices using mult */ if (usb_endpoint_is_hs_isoc_double(udev, ep) && @@ -1433,8 +1433,7 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, ring_type = usb_endpoint_type(&ep->desc); /* Ensure host supports double isoc bandwidth for eUSB2 devices */ - if (usb_endpoint_is_hs_isoc_double(udev, ep) && - !HCC2_EUSB2_DIC(xhci->hcc_params2)) { + if (usb_endpoint_is_hs_isoc_double(udev, ep) && !(xhci->hcc_params2 & HCC2_EUSB2_DIC)) { dev_dbg(&udev->dev, "Double Isoc Bandwidth not supported by xhci\n"); return -EINVAL; } @@ -1899,7 +1898,7 @@ EXPORT_SYMBOL_GPL(xhci_remove_secondary_interrupter); void xhci_mem_cleanup(struct xhci_hcd *xhci) { struct device *dev = xhci_to_hcd(xhci)->self.sysdev; - int i, j, num_ports; + int i, j; cancel_delayed_work_sync(&xhci->cmd_timer); @@ -1918,8 +1917,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed command ring"); xhci_cleanup_command_queue(xhci); - num_ports = HCS_MAX_PORTS(xhci->hcs_params1); - for (i = 0; i < num_ports && xhci->rh_bw; i++) { + for (i = 0; i < xhci->max_ports && xhci->rh_bw; i++) { struct xhci_interval_bw_table *bwt = &xhci->rh_bw[i].bw_table; for (j = 0; j < XHCI_MAX_INTERVAL; j++) { struct list_head *ep = &bwt->interval_bw[j].endpoints; @@ -1928,7 +1926,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) } } - for (i = HCS_MAX_SLOTS(xhci->hcs_params1); i > 0; i--) + for (i = xhci->max_slots; i > 0; i--) xhci_free_virt_devices_depth_first(xhci, i); dma_pool_destroy(xhci->segment_pool); @@ -1964,7 +1962,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) if (!xhci->rh_bw) goto no_bw; - for (i = 0; i < num_ports; i++) { + for (i = 0; i < xhci->max_ports; i++) { struct xhci_tt_bw_info *tt, *n; list_for_each_entry_safe(tt, n, &xhci->rh_bw[i].tts, tt_list) { list_del(&tt->tt_list); @@ -2165,7 +2163,7 @@ static void xhci_create_rhub_port_array(struct xhci_hcd *xhci, if (!rhub->ports) return; - for (i = 0; i < HCS_MAX_PORTS(xhci->hcs_params1); i++) { + for (i = 0; i < xhci->max_ports; i++) { if (xhci->hw_ports[i].rhub != rhub || xhci->hw_ports[i].hcd_portnum == DUPLICATE_ENTRY) continue; @@ -2188,32 +2186,28 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) { void __iomem *base; u32 offset; - unsigned int num_ports; int i, j; int cap_count = 0; u32 cap_start; struct device *dev = xhci_to_hcd(xhci)->self.sysdev; - num_ports = HCS_MAX_PORTS(xhci->hcs_params1); - xhci->hw_ports = kcalloc_node(num_ports, sizeof(*xhci->hw_ports), - flags, dev_to_node(dev)); + xhci->hw_ports = kcalloc_node(xhci->max_ports, sizeof(*xhci->hw_ports), + flags, dev_to_node(dev)); if (!xhci->hw_ports) return -ENOMEM; - for (i = 0; i < num_ports; i++) { - xhci->hw_ports[i].addr = &xhci->op_regs->port_status_base + - NUM_PORT_REGS * i; + for (i = 0; i < xhci->max_ports; i++) { + xhci->hw_ports[i].port_reg = &xhci->op_regs->port_regs[i]; xhci->hw_ports[i].hw_portnum = i; init_completion(&xhci->hw_ports[i].rexit_done); init_completion(&xhci->hw_ports[i].u3exit_done); } - xhci->rh_bw = kcalloc_node(num_ports, sizeof(*xhci->rh_bw), flags, - dev_to_node(dev)); + xhci->rh_bw = kcalloc_node(xhci->max_ports, sizeof(*xhci->rh_bw), flags, dev_to_node(dev)); if (!xhci->rh_bw) return -ENOMEM; - for (i = 0; i < num_ports; i++) { + for (i = 0; i < xhci->max_ports; i++) { struct xhci_interval_bw_table *bw_table; INIT_LIST_HEAD(&xhci->rh_bw[i].tts); @@ -2245,9 +2239,8 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) offset = cap_start; while (offset) { - xhci_add_in_port(xhci, num_ports, base + offset, cap_count); - if (xhci->usb2_rhub.num_ports + xhci->usb3_rhub.num_ports == - num_ports) + xhci_add_in_port(xhci, xhci->max_ports, base + offset, cap_count); + if (xhci->usb2_rhub.num_ports + xhci->usb3_rhub.num_ports == xhci->max_ports) break; offset = xhci_find_next_ext_cap(base, offset, XHCI_EXT_CAPS_PROTOCOL); diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c index 208558cf822d..06043c7c3100 100644 --- a/drivers/usb/host/xhci-mtk.c +++ b/drivers/usb/host/xhci-mtk.c @@ -670,7 +670,6 @@ static int xhci_mtk_probe(struct platform_device *pdev) } device_enable_async_suspend(dev); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); pm_runtime_forbid(dev); diff --git a/drivers/usb/host/xhci-mtk.h b/drivers/usb/host/xhci-mtk.h index f5e2bd66bb1b..2274f5995171 100644 --- a/drivers/usb/host/xhci-mtk.h +++ b/drivers/usb/host/xhci-mtk.h @@ -21,7 +21,7 @@ /* 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 @@ -34,6 +34,7 @@ #define XHCI_MTK_FRAMES_CNT (XHCI_MTK_MAX_ESIT / UFRAMES_PER_FRAME) /** + * struct mu3h_sch_tt - TT scheduling data * @fs_bus_bw_out: save bandwidth used by FS/LS OUT eps in each uframes * @fs_bus_bw_in: save bandwidth used by FS/LS IN eps in each uframes * @ls_bus_bw: save bandwidth used by LS eps in each uframes @@ -51,7 +52,7 @@ struct mu3h_sch_tt { }; /** - * struct mu3h_sch_bw_info: schedule information for bandwidth domain + * struct mu3h_sch_bw_info - schedule information for bandwidth domain * * @bus_bw: array to keep track of bandwidth already used at each uframes * @@ -63,7 +64,7 @@ struct mu3h_sch_bw_info { }; /** - * struct mu3h_sch_ep_info: schedule information for endpoint + * 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 @@ -77,6 +78,7 @@ struct mu3h_sch_bw_info { * @ep_type: endpoint type * @maxpkt: max packet size of endpoint * @ep: address of usb_host_endpoint struct + * @speed: usb device speed * @allocated: the bandwidth is aready allocated from bus_bw * @offset: which uframe of the interval that transfer should be * scheduled first time within the interval @@ -125,7 +127,7 @@ struct mu3h_sch_ep_info { #define MU3C_U2_PORT_MAX 5 /** - * struct mu3c_ippc_regs: MTK ssusb ip port control registers + * struct mu3c_ippc_regs - MTK ssusb ip port control registers * @ip_pw_ctr0~3: ip power and clock control registers * @ip_pw_sts1~2: ip power and clock status registers * @ip_xhci_cap: ip xHCI capability register diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index f67a4d956204..585b2f3117b0 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -896,9 +896,9 @@ static int xhci_pci_poweroff_late(struct usb_hcd *hcd, bool do_wakeup) if (!(xhci->quirks & XHCI_RESET_TO_DEFAULT)) return 0; - for (i = 0; i < HCS_MAX_PORTS(xhci->hcs_params1); i++) { + for (i = 0; i < xhci->max_ports; i++) { port = &xhci->hw_ports[i]; - portsc = readl(port->addr); + portsc = xhci_portsc_readl(port); if ((portsc & PORT_PLS_MASK) != XDEV_U3) continue; @@ -919,7 +919,7 @@ static int xhci_pci_poweroff_late(struct usb_hcd *hcd, bool do_wakeup) xhci_dbg(xhci, "port %d-%d in U3 without wakeup, disable it\n", port->rhub->hcd->self.busnum, port->hcd_portnum + 1); portsc = xhci_port_state_to_neutral(portsc); - writel(portsc | PORT_PE, port->addr); + xhci_portsc_writel(port, portsc | PORT_PE); } return 0; diff --git a/drivers/usb/host/xhci-port.h b/drivers/usb/host/xhci-port.h index f19efb966d18..889b5fb0fcd8 100644 --- a/drivers/usb/host/xhci-port.h +++ b/drivers/usb/host/xhci-port.h @@ -144,9 +144,14 @@ #define PORT_TEST_MODE_SHIFT 28 /* USB3 Protocol PORTLI Port Link Information */ +#define PORT_LEC(p) ((p) & 0xffff) #define PORT_RX_LANES(p) (((p) >> 16) & 0xf) #define PORT_TX_LANES(p) (((p) >> 20) & 0xf) +/* eUSB2v2 protocol PORTLI Port Link information, RsvdP for normal USB2 */ +#define PORTLI_RDR(p) ((p) & 0xf) +#define PORTLI_TDR(p) (((p) >> 4) & 0xf) + /* USB2 Protocol PORTHLPMC */ #define PORT_HIRDM(p)((p) & 3) #define PORT_L1_TIMEOUT(p)(((p) & 0xff) << 2) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 5bdcf9ab2b99..9315ba18310d 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -82,6 +82,23 @@ dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg, return seg->dma + (segment_offset * sizeof(*trb)); } +static union xhci_trb *xhci_dma_to_trb(struct xhci_segment *start_seg, + dma_addr_t dma, + struct xhci_segment **match_seg) +{ + struct xhci_segment *seg; + + xhci_for_each_ring_seg(start_seg, seg) { + if (in_range(dma, seg->dma, TRB_SEGMENT_SIZE)) { + if (match_seg) + *match_seg = seg; + return &seg->trbs[(dma - seg->dma) / sizeof(union xhci_trb)]; + } + } + + return NULL; +} + static bool trb_is_noop(union xhci_trb *trb) { return TRB_TYPE_NOOP_LE32(trb->generic.field[3]); @@ -128,11 +145,11 @@ static void inc_td_cnt(struct urb *urb) urb_priv->num_tds_done++; } -static void trb_to_noop(union xhci_trb *trb, u32 noop_type) +static void trb_to_noop(union xhci_trb *trb, u32 noop_type, bool unchain_links) { if (trb_is_link(trb)) { - /* unchain chained link TRBs */ - trb->link.control &= cpu_to_le32(~TRB_CHAIN); + if (unchain_links) + trb->link.control &= cpu_to_le32(~TRB_CHAIN); } else { trb->generic.field[0] = 0; trb->generic.field[1] = 0; @@ -143,6 +160,11 @@ static void trb_to_noop(union xhci_trb *trb, u32 noop_type) } } +static unsigned int trb_to_pos(struct xhci_segment *seg, union xhci_trb *trb) +{ + return seg->num * TRBS_PER_SEGMENT + (trb - seg->trbs); +} + /* Updates trb to point to the next TRB in the ring, and updates seg if the next * TRB is in a new segment. This does not skip over link TRBs, and it does not * effect the ring dequeue or enqueue pointers. @@ -282,55 +304,34 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring, inc_enq_past_link(xhci, ring, chain); } -/* - * If the suspect DMA address is a TRB in this TD, this function returns that - * TRB's segment. Otherwise it returns 0. - */ -static struct xhci_segment *trb_in_td(struct xhci_td *td, dma_addr_t suspect_dma) +static bool dma_in_range(dma_addr_t dma, + struct xhci_segment *start_seg, union xhci_trb *start_trb, + struct xhci_segment *end_seg, union xhci_trb *end_trb) { - dma_addr_t start_dma; - dma_addr_t end_seg_dma; - dma_addr_t end_trb_dma; - struct xhci_segment *cur_seg; + unsigned int pos, start, end; + struct xhci_segment *pos_seg; + union xhci_trb *pos_trb = xhci_dma_to_trb(start_seg, dma, &pos_seg); - start_dma = xhci_trb_virt_to_dma(td->start_seg, td->start_trb); - cur_seg = td->start_seg; + /* Is the trb dma address even part of the whole ring? */ + if (!pos_trb) + return false; - do { - if (start_dma == 0) - return NULL; - /* We may get an event for a Link TRB in the middle of a TD */ - end_seg_dma = xhci_trb_virt_to_dma(cur_seg, - &cur_seg->trbs[TRBS_PER_SEGMENT - 1]); - /* If the end TRB isn't in this segment, this is set to 0 */ - end_trb_dma = xhci_trb_virt_to_dma(cur_seg, td->end_trb); + pos = trb_to_pos(pos_seg, pos_trb); + start = trb_to_pos(start_seg, start_trb); + end = trb_to_pos(end_seg, end_trb); - if (end_trb_dma > 0) { - /* The end TRB is in this segment, so suspect should be here */ - if (start_dma <= end_trb_dma) { - if (suspect_dma >= start_dma && suspect_dma <= end_trb_dma) - return cur_seg; - } else { - /* Case for one segment with - * a TD wrapped around to the top - */ - if ((suspect_dma >= start_dma && - suspect_dma <= end_seg_dma) || - (suspect_dma >= cur_seg->dma && - suspect_dma <= end_trb_dma)) - return cur_seg; - } - return NULL; - } - /* Might still be somewhere in this segment */ - if (suspect_dma >= start_dma && suspect_dma <= end_seg_dma) - return cur_seg; + /* end position is smaller than start, search range wraps around */ + if (end < start) + return !(pos > end && pos < start); - cur_seg = cur_seg->next; - start_dma = xhci_trb_virt_to_dma(cur_seg, &cur_seg->trbs[0]); - } while (cur_seg != td->start_seg); + return (pos >= start && pos <= end); +} - return NULL; +/* If the suspect DMA address is a TRB in this TD, this function returns true */ +static bool trb_in_td(struct xhci_td *td, dma_addr_t suspect_dma) +{ + return dma_in_range(suspect_dma, td->start_seg, td->start_trb, + td->end_seg, td->end_trb); } /* @@ -434,7 +435,7 @@ void xhci_ring_cmd_db(struct xhci_hcd *xhci) static bool xhci_mod_cmd_timer(struct xhci_hcd *xhci) { - return mod_delayed_work(system_wq, &xhci->cmd_timer, + return mod_delayed_work(system_percpu_wq, &xhci->cmd_timer, msecs_to_jiffies(xhci->current_cmd->timeout_ms)); } @@ -465,7 +466,7 @@ static void xhci_handle_stopped_cmd_ring(struct xhci_hcd *xhci, xhci_dbg(xhci, "Turn aborted command %p to no-op\n", i_cmd->command_trb); - trb_to_noop(i_cmd->command_trb, TRB_CMD_NOOP); + trb_to_noop(i_cmd->command_trb, TRB_CMD_NOOP, false); /* * caller waiting for completion is called when command @@ -797,13 +798,18 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci, * (The last TRB actually points to the ring enqueue pointer, which is not part * of this TD.) This is used to remove partially enqueued isoc TDs from a ring. */ -static void td_to_noop(struct xhci_td *td, bool flip_cycle) +static void td_to_noop(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, + struct xhci_td *td, bool flip_cycle) { + bool unchain_links; struct xhci_segment *seg = td->start_seg; union xhci_trb *trb = td->start_trb; + /* link TRBs should now be unchained, but some old HCs expect otherwise */ + unchain_links = !xhci_link_chain_quirk(xhci, ep->ring ? ep->ring->type : TYPE_STREAM); + while (1) { - trb_to_noop(trb, TRB_TR_NOOP); + trb_to_noop(trb, TRB_TR_NOOP, unchain_links); /* flip cycle if asked to */ if (flip_cycle && trb != td->start_trb && trb != td->end_trb) @@ -1091,16 +1097,16 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep) "Found multiple active URBs %p and %p in stream %u?\n", td->urb, cached_td->urb, td->urb->stream_id); - td_to_noop(cached_td, false); + td_to_noop(xhci, ep, cached_td, false); cached_td->cancel_status = TD_CLEARED; } - td_to_noop(td, false); + td_to_noop(xhci, ep, td, false); td->cancel_status = TD_CLEARING_CACHE; cached_td = td; break; } } else { - td_to_noop(td, false); + td_to_noop(xhci, ep, td, false); td->cancel_status = TD_CLEARED; } } @@ -1125,7 +1131,7 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep) continue; xhci_warn(xhci, "Failed to clear cancelled cached URB %p, mark clear anyway\n", td->urb); - td_to_noop(td, false); + td_to_noop(xhci, ep, td, false); td->cancel_status = TD_CLEARED; } } @@ -1388,7 +1394,7 @@ void xhci_hc_died(struct xhci_hcd *xhci) xhci_cleanup_command_queue(xhci); /* return any pending urbs, remove may be waiting for them */ - for (i = 0; i <= HCS_MAX_SLOTS(xhci->hcs_params1); i++) { + for (i = 0; i <= xhci->max_slots; i++) { if (!xhci->devs[i]) continue; for (j = 0; j < 31; j++) @@ -1989,7 +1995,6 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event) struct usb_hcd *hcd; u32 port_id; u32 portsc, cmd_reg; - int max_ports; unsigned int hcd_portnum; struct xhci_bus_state *bus_state; bool bogus_port_status = false; @@ -2001,9 +2006,8 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event) "WARN: xHC returned failed port status event\n"); port_id = GET_PORT_ID(le32_to_cpu(event->generic.field[0])); - max_ports = HCS_MAX_PORTS(xhci->hcs_params1); - if ((port_id <= 0) || (port_id > max_ports)) { + if ((port_id <= 0) || (port_id > xhci->max_ports)) { xhci_warn(xhci, "Port change event with invalid port ID %d\n", port_id); return; @@ -2030,7 +2034,7 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event) hcd = port->rhub->hcd; bus_state = &port->rhub->bus_state; hcd_portnum = port->hcd_portnum; - portsc = readl(port->addr); + portsc = xhci_portsc_readl(port); xhci_dbg(xhci, "Port change event, %d-%d, id %d, portsc: 0x%x\n", hcd->self.busnum, hcd_portnum + 1, port_id, portsc); @@ -2184,24 +2188,31 @@ static void xhci_clear_hub_tt_buffer(struct xhci_hcd *xhci, struct xhci_td *td, * External device side is also halted in functional stall cases. Class driver * will clear the device halt with a CLEAR_FEATURE(ENDPOINT_HALT) request later. */ -static bool xhci_halted_host_endpoint(struct xhci_ep_ctx *ep_ctx, unsigned int comp_code) +static bool xhci_halted_host_endpoint(struct xhci_hcd *xhci, struct xhci_ep_ctx *ep_ctx, + unsigned int comp_code) { - /* Stall halts both internal and device side endpoint */ - if (comp_code == COMP_STALL_ERROR) - return true; + int ep_type = CTX_TO_EP_TYPE(le32_to_cpu(ep_ctx->ep_info2)); - /* TRB completion codes that may require internal halt cleanup */ - if (comp_code == COMP_USB_TRANSACTION_ERROR || - comp_code == COMP_BABBLE_DETECTED_ERROR || - comp_code == COMP_SPLIT_TRANSACTION_ERROR) + switch (comp_code) { + case COMP_STALL_ERROR: + /* on xHCI this always halts, including protocol stall */ + return true; + case COMP_BABBLE_DETECTED_ERROR: /* * The 0.95 spec says a babbling control endpoint is not halted. * The 0.96 spec says it is. Some HW claims to be 0.95 * compliant, but it halts the control endpoint anyway. * Check endpoint context if endpoint is halted. */ - if (GET_EP_CTX_STATE(ep_ctx) == EP_STATE_HALTED) - return true; + if (xhci->hci_version <= 0x95 && ep_type == CTRL_EP) + return GET_EP_CTX_STATE(ep_ctx) == EP_STATE_HALTED; + + fallthrough; + case COMP_USB_TRANSACTION_ERROR: + case COMP_SPLIT_TRANSACTION_ERROR: + /* these errors halt all non-isochronous endpoints */ + return ep_type != ISOC_IN_EP && ep_type != ISOC_OUT_EP; + } return false; } @@ -2238,41 +2249,9 @@ static void finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, * the ring dequeue pointer or take this TD off any lists yet. */ return; - case COMP_USB_TRANSACTION_ERROR: - case COMP_BABBLE_DETECTED_ERROR: - case COMP_SPLIT_TRANSACTION_ERROR: - /* - * If endpoint context state is not halted we might be - * racing with a reset endpoint command issued by a unsuccessful - * stop endpoint completion (context error). In that case the - * td should be on the cancelled list, and EP_HALTED flag set. - * - * Or then it's not halted due to the 0.95 spec stating that a - * babbling control endpoint should not halt. The 0.96 spec - * again says it should. Some HW claims to be 0.95 compliant, - * but it halts the control endpoint anyway. - */ - if (GET_EP_CTX_STATE(ep_ctx) != EP_STATE_HALTED) { - /* - * If EP_HALTED is set and TD is on the cancelled list - * the TD and dequeue pointer will be handled by reset - * ep command completion - */ - if ((ep->ep_state & EP_HALTED) && - !list_empty(&td->cancelled_td_list)) { - xhci_dbg(xhci, "Already resolving halted ep for 0x%llx\n", - (unsigned long long)xhci_trb_virt_to_dma( - td->start_seg, td->start_trb)); - return; - } - /* endpoint not halted, don't reset it */ - break; - } - /* Almost same procedure as for STALL_ERROR below */ - xhci_clear_hub_tt_buffer(xhci, td, ep); - xhci_handle_halted_endpoint(xhci, ep, td, EP_HARD_RESET); - return; - case COMP_STALL_ERROR: + } + + if (xhci_halted_host_endpoint(xhci, ep_ctx, trb_comp_code)) { /* * xhci internal endpoint state will go to a "halt" state for * any stall, including default control pipe protocol stall. @@ -2283,14 +2262,12 @@ static void finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, * stall later. Hub TT buffer should only be cleared for FS/LS * devices behind HS hubs for functional stalls. */ - if (ep->ep_index != 0) + if (!(ep->ep_index == 0 && trb_comp_code == COMP_STALL_ERROR)) xhci_clear_hub_tt_buffer(xhci, td, ep); xhci_handle_halted_endpoint(xhci, ep, td, EP_HARD_RESET); return; /* xhci_handle_halted_endpoint marked td cancelled */ - default: - break; } xhci_dequeue_td(xhci, td, ep_ring, td->status); @@ -2367,7 +2344,7 @@ static void process_ctrl_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, case COMP_STOPPED_LENGTH_INVALID: goto finish_td; default: - if (!xhci_halted_host_endpoint(ep_ctx, trb_comp_code)) + if (!xhci_halted_host_endpoint(xhci, ep_ctx, trb_comp_code)) break; xhci_dbg(xhci, "TRB error %u, halted endpoint index = %u\n", trb_comp_code, ep->ep_index); @@ -2663,7 +2640,6 @@ static int handle_tx_event(struct xhci_hcd *xhci, int ep_index; struct xhci_td *td = NULL; dma_addr_t ep_trb_dma; - struct xhci_segment *ep_seg; union xhci_trb *ep_trb; int status = -EINPROGRESS; struct xhci_ep_ctx *ep_ctx; @@ -2694,6 +2670,9 @@ static int handle_tx_event(struct xhci_hcd *xhci, if (!ep_ring) return handle_transferless_tx_event(xhci, ep, trb_comp_code); + /* find the transfer trb this events points to */ + ep_trb = xhci_dma_to_trb(ep_ring->deq_seg, ep_trb_dma, NULL); + /* Look for common error cases */ switch (trb_comp_code) { /* Skip codes that require special handling depending on @@ -2867,10 +2846,8 @@ static int handle_tx_event(struct xhci_hcd *xhci, td = list_first_entry(&ep_ring->td_list, struct xhci_td, td_list); - /* Is this a TRB in the currently executing TD? */ - ep_seg = trb_in_td(td, ep_trb_dma); - - if (!ep_seg) { + /* Is this TRB not part of the currently executing TD? */ + if (!trb_in_td(td, ep_trb_dma)) { if (ep->skip && usb_endpoint_xfer_isoc(&td->urb->ep->desc)) { /* this event is unlikely to match any TD, don't skip them all */ @@ -2953,7 +2930,6 @@ static int handle_tx_event(struct xhci_hcd *xhci, if (ring_xrun_event) return 0; - ep_trb = &ep_seg->trbs[(ep_trb_dma - ep_seg->dma) / sizeof(*ep_trb)]; trace_xhci_handle_transfer(ep_ring, (struct xhci_generic_trb *) ep_trb, ep_trb_dma); /* @@ -2978,7 +2954,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, return 0; check_endpoint_halted: - if (xhci_halted_host_endpoint(ep_ctx, trb_comp_code)) + if (xhci_halted_host_endpoint(xhci, ep_ctx, trb_comp_code)) xhci_handle_halted_endpoint(xhci, ep, td, EP_HARD_RESET); return 0; @@ -3990,6 +3966,16 @@ static unsigned int xhci_get_last_burst_packet_count(struct xhci_hcd *xhci, return total_packet_count - 1; } +/* Returns the Isochronous Scheduling Threshold in Microframes. 1 Frame is 8 Microframes. */ +static int xhci_ist_microframes(struct xhci_hcd *xhci) +{ + int ist = HCS_IST_VALUE(xhci->hcs_params2); + + if (xhci->hcs_params2 & HCS_IST_UNIT) + ist *= 8; + return ist; +} + /* * Calculates Frame ID field of the isochronous TRB identifies the * target frame that the Interval associated with this Isochronous @@ -4009,17 +3995,7 @@ static int xhci_get_isoc_frame_id(struct xhci_hcd *xhci, else start_frame = (urb->start_frame + index * urb->interval) >> 3; - /* Isochronous Scheduling Threshold (IST, bits 0~3 in HCSPARAMS2): - * - * If bit [3] of IST is cleared to '0', software can add a TRB no - * later than IST[2:0] Microframes before that TRB is scheduled to - * be executed. - * If bit [3] of IST is set to '1', software can add a TRB no later - * than IST[2:0] Frames before that TRB is scheduled to be executed. - */ - ist = HCS_IST(xhci->hcs_params2) & 0x7; - if (HCS_IST(xhci->hcs_params2) & (1 << 3)) - ist <<= 3; + ist = xhci_ist_microframes(xhci); /* Software shall not schedule an Isoch TD with a Frame ID value that * is less than the Start Frame ID or greater than the End Frame ID, @@ -4164,7 +4140,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, /* use SIA as default, if frame id is used overwrite it */ sia_frame_id = TRB_SIA; if (!(urb->transfer_flags & URB_ISO_ASAP) && - HCC_CFC(xhci->hcc_params)) { + (xhci->hcc_params & HCC_CFC)) { frame_id = xhci_get_isoc_frame_id(xhci, urb, i); if (frame_id >= 0) sia_frame_id = TRB_FRAME_ID(frame_id); @@ -4248,7 +4224,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, } /* store the next frame id */ - if (HCC_CFC(xhci->hcc_params)) + if (xhci->hcc_params & HCC_CFC) xep->next_frame_id = urb->start_frame + num_tds * urb->interval; if (xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs == 0) { @@ -4273,7 +4249,7 @@ cleanup: */ urb_priv->td[0].end_trb = ep_ring->enqueue; /* Every TRB except the first & last will have its cycle bit flipped. */ - td_to_noop(&urb_priv->td[0], true); + td_to_noop(xhci, xep, &urb_priv->td[0], true); /* Reset the ring enqueue back to the first TRB and its cycle bit. */ ep_ring->enqueue = urb_priv->td[0].start_trb; @@ -4327,7 +4303,7 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags, check_interval(urb, ep_ctx); /* Calculate the start frame and put it in urb->start_frame. */ - if (HCC_CFC(xhci->hcc_params) && !list_empty(&ep_ring->td_list)) { + if ((xhci->hcc_params & HCC_CFC) && !list_empty(&ep_ring->td_list)) { if (GET_EP_CTX_STATE(ep_ctx) == EP_STATE_RUNNING) { urb->start_frame = xep->next_frame_id; goto skip_start_over; @@ -4340,9 +4316,7 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags, * Round up to the next frame and consider the time before trb really * gets scheduled by hardare. */ - ist = HCS_IST(xhci->hcs_params2) & 0x7; - if (HCS_IST(xhci->hcs_params2) & (1 << 3)) - ist <<= 3; + ist = xhci_ist_microframes(xhci); start_frame += ist + XHCI_CFC_DELAY; start_frame = roundup(start_frame, 8); diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index 5255b1002893..31ccced5125e 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -1399,7 +1399,6 @@ static void tegra_xhci_id_work(struct work_struct *work) } tegra_xhci_set_port_power(tegra, true, true); - pm_runtime_mark_last_busy(tegra->dev); } else { if (tegra->otg_usb3_port >= 0) @@ -2036,7 +2035,7 @@ static bool xhci_hub_ports_suspended(struct xhci_hub *hub) u32 value; for (i = 0; i < hub->num_ports; i++) { - value = readl(hub->ports[i]->addr); + value = xhci_portsc_readl(hub->ports[i]); if ((value & PORT_PE) == 0) continue; @@ -2162,7 +2161,7 @@ static void tegra_xhci_enable_phy_sleepwalk_wake(struct tegra_xusb *tegra) if (!is_host_mode_phy(tegra, i, j)) continue; - portsc = readl(rhub->ports[index]->addr); + portsc = xhci_portsc_readl(rhub->ports[index]); speed = tegra_xhci_portsc_to_speed(tegra, portsc); tegra_xusb_padctl_enable_phy_sleepwalk(padctl, phy, speed); tegra_xusb_padctl_enable_phy_wake(padctl, phy); @@ -2257,7 +2256,7 @@ static int tegra_xusb_enter_elpg(struct tegra_xusb *tegra, bool is_auto_resume) for (i = 0; i < xhci->usb2_rhub.num_ports; i++) { if (!xhci->usb2_rhub.ports[i]) continue; - portsc = readl(xhci->usb2_rhub.ports[i]->addr); + portsc = xhci_portsc_readl(xhci->usb2_rhub.ports[i]); tegra->lp0_utmi_pad_mask &= ~BIT(i); if (((portsc & PORT_PLS_MASK) == XDEV_U3) || ((portsc & DEV_SPEED_MASK) == XDEV_FS)) tegra->lp0_utmi_pad_mask |= BIT(i); @@ -2790,7 +2789,7 @@ static int tegra_xhci_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, while (i--) { if (!test_bit(i, &bus_state->resuming_ports)) continue; - portsc = readl(ports[i]->addr); + portsc = xhci_portsc_readl(ports[i]); if ((portsc & PORT_PLS_MASK) == XDEV_RESUME) tegra_phy_xusb_utmi_pad_power_on( tegra_xusb_get_phy(tegra, "usb2", (int) i)); @@ -2808,7 +2807,7 @@ static int tegra_xhci_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, if (!index || index > rhub->num_ports) return -EPIPE; ports = rhub->ports; - portsc = readl(ports[port]->addr); + portsc = xhci_portsc_readl(ports[port]); if (portsc & PORT_CONNECT) tegra_phy_xusb_utmi_pad_power_on(phy); } @@ -2827,7 +2826,7 @@ static int tegra_xhci_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, if ((type_req == ClearPortFeature) && (value == USB_PORT_FEAT_C_CONNECTION)) { ports = rhub->ports; - portsc = readl(ports[port]->addr); + portsc = xhci_portsc_readl(ports[port]); if (!(portsc & PORT_CONNECT)) { /* We don't suspend the PAD while HNP role swap happens on the OTG * port diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h index 9abc904f1749..724cba2dbb78 100644 --- a/drivers/usb/host/xhci-trace.h +++ b/drivers/usb/host/xhci-trace.h @@ -71,29 +71,20 @@ DEFINE_EVENT(xhci_log_msg, xhci_dbg_ring_expansion, ); DECLARE_EVENT_CLASS(xhci_log_ctx, - TP_PROTO(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx, - unsigned int ep_num), - TP_ARGS(xhci, ctx, ep_num), + TP_PROTO(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx), + TP_ARGS(xhci, ctx), TP_STRUCT__entry( __field(int, ctx_64) __field(unsigned, ctx_type) __field(dma_addr_t, ctx_dma) __field(u8 *, ctx_va) - __field(unsigned, ctx_ep_num) - __dynamic_array(u32, ctx_data, - ((HCC_64BYTE_CONTEXT(xhci->hcc_params) + 1) * 8) * - ((ctx->type == XHCI_CTX_TYPE_INPUT) + ep_num + 1)) ), TP_fast_assign( - __entry->ctx_64 = HCC_64BYTE_CONTEXT(xhci->hcc_params); + __entry->ctx_64 = xhci->hcc_params & HCC_64BYTE_CONTEXT; __entry->ctx_type = ctx->type; __entry->ctx_dma = ctx->dma; __entry->ctx_va = ctx->bytes; - __entry->ctx_ep_num = ep_num; - memcpy(__get_dynamic_array(ctx_data), ctx->bytes, - ((HCC_64BYTE_CONTEXT(xhci->hcc_params) + 1) * 32) * - ((ctx->type == XHCI_CTX_TYPE_INPUT) + ep_num + 1)); ), TP_printk("ctx_64=%d, ctx_type=%u, ctx_dma=@%llx, ctx_va=@%p", __entry->ctx_64, __entry->ctx_type, @@ -102,9 +93,8 @@ DECLARE_EVENT_CLASS(xhci_log_ctx, ); DEFINE_EVENT(xhci_log_ctx, xhci_address_ctx, - TP_PROTO(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx, - unsigned int ep_num), - TP_ARGS(xhci, ctx, ep_num) + TP_PROTO(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx), + TP_ARGS(xhci, ctx) ); DECLARE_EVENT_CLASS(xhci_log_trb, @@ -575,6 +565,11 @@ DEFINE_EVENT(xhci_log_portsc, xhci_hub_status_data, TP_ARGS(port, portsc) ); +DEFINE_EVENT(xhci_log_portsc, xhci_portsc_writel, + TP_PROTO(struct xhci_port *port, u32 portsc), + TP_ARGS(port, portsc) +); + DECLARE_EVENT_CLASS(xhci_log_doorbell, TP_PROTO(u32 slot, u32 doorbell), TP_ARGS(slot, doorbell), diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index a148a1280126..02c9bfe21ae2 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -41,6 +41,19 @@ static unsigned long long quirks; module_param(quirks, ullong, S_IRUGO); MODULE_PARM_DESC(quirks, "Bit flags for quirks to be enabled as default"); +void xhci_portsc_writel(struct xhci_port *port, u32 val) +{ + trace_xhci_portsc_writel(port, val); + writel(val, &port->port_reg->portsc); +} +EXPORT_SYMBOL_GPL(xhci_portsc_writel); + +u32 xhci_portsc_readl(struct xhci_port *port) +{ + return readl(&port->port_reg->portsc); +} +EXPORT_SYMBOL_GPL(xhci_portsc_readl); + static bool td_on_ring(struct xhci_td *td, struct xhci_ring *ring) { struct xhci_segment *seg; @@ -237,7 +250,6 @@ static void xhci_zero_64b_regs(struct xhci_hcd *xhci) struct iommu_domain *domain; int err, i; u64 val; - u32 intrs; /* * Some Renesas controllers get into a weird state if they are @@ -278,10 +290,7 @@ static void xhci_zero_64b_regs(struct xhci_hcd *xhci) if (upper_32_bits(val)) xhci_write_64(xhci, 0, &xhci->op_regs->cmd_ring); - intrs = min_t(u32, HCS_MAX_INTRS(xhci->hcs_params1), - ARRAY_SIZE(xhci->run_regs->ir_set)); - - for (i = 0; i < intrs; i++) { + for (i = 0; i < xhci->max_interrupters; i++) { struct xhci_intr_reg __iomem *ir; ir = &xhci->run_regs->ir_set[i]; @@ -373,7 +382,7 @@ static void compliance_mode_recovery(struct timer_list *t) return; for (i = 0; i < rhub->num_ports; i++) { - temp = readl(rhub->ports[i]->addr); + temp = xhci_portsc_readl(rhub->ports[i]); if ((temp & PORT_PLS_MASK) == USB_SS_PORT_LS_COMP_MOD) { /* * Compliance Mode Detected. Letting USB Core @@ -471,15 +480,13 @@ static void xhci_hcd_page_size(struct xhci_hcd *xhci) static void xhci_enable_max_dev_slots(struct xhci_hcd *xhci) { u32 config_reg; - u32 max_slots; - max_slots = HCS_MAX_SLOTS(xhci->hcs_params1); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "xHC can handle at most %d device slots", - max_slots); + xhci->max_slots); config_reg = readl(&xhci->op_regs->config_reg); config_reg &= ~HCS_SLOTS_MASK; - config_reg |= max_slots; + config_reg |= xhci->max_slots; xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Setting Max device slots reg = 0x%x", config_reg); @@ -896,7 +903,7 @@ static void xhci_disable_hub_port_wake(struct xhci_hcd *xhci, spin_lock_irqsave(&xhci->lock, flags); for (i = 0; i < rhub->num_ports; i++) { - portsc = readl(rhub->ports[i]->addr); + portsc = xhci_portsc_readl(rhub->ports[i]); t1 = xhci_port_state_to_neutral(portsc); t2 = t1; @@ -909,7 +916,7 @@ static void xhci_disable_hub_port_wake(struct xhci_hcd *xhci, t2 |= PORT_CSC; if (t1 != t2) { - writel(t2, rhub->ports[i]->addr); + xhci_portsc_writel(rhub->ports[i], t2); xhci_dbg(xhci, "config port %d-%d wake bits, portsc: 0x%x, write: 0x%x\n", rhub->hcd->self.busnum, i + 1, portsc, t2); } @@ -936,7 +943,7 @@ static bool xhci_pending_portevent(struct xhci_hcd *xhci) port_index = xhci->usb2_rhub.num_ports; ports = xhci->usb2_rhub.ports; while (port_index--) { - portsc = readl(ports[port_index]->addr); + portsc = xhci_portsc_readl(ports[port_index]); if (portsc & PORT_CHANGE_MASK || (portsc & PORT_PLS_MASK) == XDEV_RESUME) return true; @@ -944,7 +951,7 @@ static bool xhci_pending_portevent(struct xhci_hcd *xhci) port_index = xhci->usb3_rhub.num_ports; ports = xhci->usb3_rhub.ports; while (port_index--) { - portsc = readl(ports[port_index]->addr); + portsc = xhci_portsc_readl(ports[port_index]); if (portsc & (PORT_CHANGE_MASK | PORT_CAS) || (portsc & PORT_PLS_MASK) == XDEV_RESUME) return true; @@ -4223,8 +4230,7 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev) xhci_err(xhci, "Error while assigning device slot ID: %s\n", xhci_trb_comp_code_string(command->status)); xhci_err(xhci, "Max number of devices this xHCI host supports is %u.\n", - HCS_MAX_SLOTS( - readl(&xhci->cap_regs->hcs_params1))); + xhci->max_slots); xhci_free_command(xhci, command); return 0; } @@ -4367,8 +4373,7 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, ctrl_ctx->add_flags = cpu_to_le32(SLOT_FLAG | EP0_FLAG); ctrl_ctx->drop_flags = 0; - trace_xhci_address_ctx(xhci, virt_dev->in_ctx, - le32_to_cpu(slot_ctx->dev_info) >> 27); + trace_xhci_address_ctx(xhci, virt_dev->in_ctx); trace_xhci_address_ctrl_ctx(ctrl_ctx); spin_lock_irqsave(&xhci->lock, flags); @@ -4428,7 +4433,7 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, xhci_err(xhci, "ERROR: unexpected setup %s command completion code 0x%x.\n", act, command->status); - trace_xhci_address_ctx(xhci, virt_dev->out_ctx, 1); + trace_xhci_address_ctx(xhci, virt_dev->out_ctx); ret = -EINVAL; break; } @@ -4446,14 +4451,12 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, xhci_dbg_trace(xhci, trace_xhci_dbg_address, "Output Context DMA address = %#08llx", (unsigned long long)virt_dev->out_ctx->dma); - trace_xhci_address_ctx(xhci, virt_dev->in_ctx, - le32_to_cpu(slot_ctx->dev_info) >> 27); + trace_xhci_address_ctx(xhci, virt_dev->in_ctx); /* * USB core uses address 1 for the roothubs, so we add one to the * address given back to us by the HC. */ - trace_xhci_address_ctx(xhci, virt_dev->out_ctx, - le32_to_cpu(slot_ctx->dev_info) >> 27); + trace_xhci_address_ctx(xhci, virt_dev->out_ctx); /* Zero the input context control for later use */ ctrl_ctx->add_flags = 0; ctrl_ctx->drop_flags = 0; @@ -4637,7 +4640,7 @@ static int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, { struct xhci_hcd *xhci = hcd_to_xhci(hcd); struct xhci_port **ports; - __le32 __iomem *pm_addr, *hlpm_addr; + struct xhci_port_regs __iomem *port_reg; u32 pm_val, hlpm_val, field; unsigned int port_num; unsigned long flags; @@ -4662,9 +4665,8 @@ static int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, ports = xhci->usb2_rhub.ports; port_num = udev->portnum - 1; - pm_addr = ports[port_num]->addr + PORTPMSC; - pm_val = readl(pm_addr); - hlpm_addr = ports[port_num]->addr + PORTHLPMC; + port_reg = ports[port_num]->port_reg; + pm_val = readl(&port_reg->portpmsc); xhci_dbg(xhci, "%s port %d USB2 hardware LPM\n", str_enable_disable(enable), port_num + 1); @@ -4693,30 +4695,30 @@ static int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, spin_lock_irqsave(&xhci->lock, flags); hlpm_val = xhci_calculate_usb2_hw_lpm_params(udev); - writel(hlpm_val, hlpm_addr); + writel(hlpm_val, &port_reg->porthlmpc); /* flush write */ - readl(hlpm_addr); + readl(&port_reg->porthlmpc); } else { hird = xhci_calculate_hird_besl(xhci, udev); } pm_val &= ~PORT_HIRD_MASK; pm_val |= PORT_HIRD(hird) | PORT_RWE | PORT_L1DS(udev->slot_id); - writel(pm_val, pm_addr); - pm_val = readl(pm_addr); + writel(pm_val, &port_reg->portpmsc); + pm_val = readl(&port_reg->portpmsc); pm_val |= PORT_HLE; - writel(pm_val, pm_addr); + writel(pm_val, &port_reg->portpmsc); /* flush write */ - readl(pm_addr); + readl(&port_reg->portpmsc); } else { pm_val &= ~(PORT_HLE | PORT_RWE | PORT_HIRD_MASK | PORT_L1DS_MASK); - writel(pm_val, pm_addr); + writel(pm_val, &port_reg->portpmsc); /* flush write */ - readl(pm_addr); + readl(&port_reg->portpmsc); if (udev->usb2_hw_lpm_besl_capable) { spin_unlock_irqrestore(&xhci->lock, flags); xhci_change_max_exit_latency(xhci, udev, 0); - readl_poll_timeout(ports[port_num]->addr, pm_val, + readl_poll_timeout(&ports[port_num]->port_reg->portsc, pm_val, (pm_val & PORT_PLS_MASK) == XDEV_U0, 100, 10000); return 0; @@ -5409,6 +5411,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) */ struct device *dev = hcd->self.sysdev; int retval; + u32 hcs_params1; /* Accept arbitrarily long scatter-gather lists */ hcd->self.sg_tablesize = ~0; @@ -5434,7 +5437,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) xhci->run_regs = hcd->regs + (readl(&xhci->cap_regs->run_regs_off) & RTSOFF_MASK); /* Cache read-only capability registers */ - xhci->hcs_params1 = readl(&xhci->cap_regs->hcs_params1); + hcs_params1 = readl(&xhci->cap_regs->hcs_params1); xhci->hcs_params2 = readl(&xhci->cap_regs->hcs_params2); xhci->hcs_params3 = readl(&xhci->cap_regs->hcs_params3); xhci->hci_version = HC_VERSION(readl(&xhci->cap_regs->hc_capbase)); @@ -5442,10 +5445,13 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) if (xhci->hci_version > 0x100) xhci->hcc_params2 = readl(&xhci->cap_regs->hcc_params2); + xhci->max_slots = HCS_MAX_SLOTS(hcs_params1); + xhci->max_ports = min(HCS_MAX_PORTS(hcs_params1), MAX_HC_PORTS); /* xhci-plat or xhci-pci might have set max_interrupters already */ - if ((!xhci->max_interrupters) || - xhci->max_interrupters > HCS_MAX_INTRS(xhci->hcs_params1)) - xhci->max_interrupters = HCS_MAX_INTRS(xhci->hcs_params1); + if (!xhci->max_interrupters) + xhci->max_interrupters = min(HCS_MAX_INTRS(hcs_params1), MAX_HC_INTRS); + else if (xhci->max_interrupters > HCS_MAX_INTRS(hcs_params1)) + xhci->max_interrupters = HCS_MAX_INTRS(hcs_params1); xhci->quirks |= quirks; @@ -5490,7 +5496,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) /* Set dma_mask and coherent_dma_mask to 64-bits, * if xHC supports 64-bit addressing */ - if (HCC_64BIT_ADDR(xhci->hcc_params) && + if ((xhci->hcc_params & HCC_64BIT_ADDR) && !dma_set_mask(dev, DMA_BIT_MASK(64))) { xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n"); dma_set_coherent_mask(dev, DMA_BIT_MASK(64)); @@ -5664,8 +5670,8 @@ static int __init xhci_hcd_init(void) BUILD_BUG_ON(sizeof(struct xhci_erst_entry) != 4*32/8); BUILD_BUG_ON(sizeof(struct xhci_cap_regs) != 8*32/8); BUILD_BUG_ON(sizeof(struct xhci_intr_reg) != 8*32/8); - /* xhci_run_regs has eight fields and embeds 128 xhci_intr_regs */ - BUILD_BUG_ON(sizeof(struct xhci_run_regs) != (8+8*128)*32/8); + /* xhci_run_regs has eight fields and embeds 1024 xhci_intr_regs */ + BUILD_BUG_ON(sizeof(struct xhci_run_regs) != (8+8*1024)*32/8); if (usb_disabled()) return -ENODEV; diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 58a51f09cceb..2b0796f6d00e 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -34,8 +34,16 @@ /* Max number of USB devices for any host controller - limit in section 6.1 */ #define MAX_HC_SLOTS 256 -/* Section 5.3.3 - MaxPorts */ +/* + * Max Number of Ports. xHCI specification section 5.3.3 + * Valid values are in the range of 1 to 255. + */ #define MAX_HC_PORTS 127 +/* + * Max number of Interrupter Register Sets. xHCI specification section 5.3.3 + * Valid values are in the range of 1 to 1024. + */ +#define MAX_HC_INTRS 128 /* * xHCI register interface. @@ -66,13 +74,19 @@ struct xhci_cap_regs { /* Reserved up to (CAPLENGTH - 0x1C) */ }; -/* Number of registers per port */ -#define NUM_PORT_REGS 4 - -#define PORTSC 0 -#define PORTPMSC 1 -#define PORTLI 2 -#define PORTHLPMC 3 +/* + * struct xhci_port_regs - Host Controller USB Port Register Set. xHCI spec 5.4.8 + * @portsc: Port Status and Control + * @portpmsc: Port Power Management Status and Control + * @portli: Port Link Info + * @porthlmpc: Port Hardware LPM Control + */ +struct xhci_port_regs { + __le32 portsc; + __le32 portpmsc; + __le32 portli; + __le32 porthlmpc; +}; /** * struct xhci_op_regs - xHCI Host Controller Operational Registers. @@ -85,16 +99,7 @@ struct xhci_cap_regs { * @cmd_ring: CRP - 64-bit Command Ring Pointer * @dcbaa_ptr: DCBAAP - 64-bit Device Context Base Address Array Pointer * @config_reg: CONFIG - Configure Register - * @port_status_base: PORTSCn - base address for Port Status and Control - * Each port has a Port Status and Control register, - * followed by a Port Power Management Status and Control - * register, a Port Link Info register, and a reserved - * register. - * @port_power_base: PORTPMSCn - base address for - * Port Power Management Status and Control - * @port_link_base: PORTLIn - base address for Port Link Info (current - * Link PM state and control) for USB 2.1 and USB 3.0 - * devices. + * @port_regs: Port Register Sets, from 1 to MaxPorts (defined by HCSPARAMS1). */ struct xhci_op_regs { __le32 command; @@ -110,13 +115,7 @@ struct xhci_op_regs { __le32 config_reg; /* rsvd: offset 0x3C-3FF */ __le32 reserved4[241]; - /* port 1 registers, which serve as a base address for other ports */ - __le32 port_status_base; - __le32 port_power_base; - __le32 port_link_base; - __le32 reserved5; - /* registers for ports 2-255 */ - __le32 reserved6[NUM_PORT_REGS*254]; + struct xhci_port_regs port_regs[]; }; /* USBCMD - USB command - command bitmasks */ @@ -284,7 +283,7 @@ struct xhci_intr_reg { struct xhci_run_regs { __le32 microframe_index; __le32 rsvd[7]; - struct xhci_intr_reg ir_set[128]; + struct xhci_intr_reg ir_set[1024]; }; /** @@ -800,7 +799,6 @@ struct xhci_device_context_array { /* private xHCD pointers */ dma_addr_t dma; }; -/* TODO: write function to set the 64-bit device DMA address */ /* * TODO: change this to be dynamically sized at HC mem init time since the HC * might not be able to handle the maximum number of devices possible. @@ -1474,7 +1472,7 @@ struct xhci_port_cap { }; struct xhci_port { - __le32 __iomem *addr; + struct xhci_port_regs __iomem *port_reg; int hw_portnum; int hcd_portnum; struct xhci_hub *rhub; @@ -1510,7 +1508,6 @@ struct xhci_hcd { struct xhci_doorbell_array __iomem *dba; /* Cached register copies of read-only HC data */ - __u32 hcs_params1; __u32 hcs_params2; __u32 hcs_params3; __u32 hcc_params; @@ -1521,6 +1518,8 @@ struct xhci_hcd { /* packed release number */ u16 hci_version; u16 max_interrupters; + u8 max_slots; + u8 max_ports; /* imod_interval in ns (I * 250ns) */ u32 imod_interval; u32 page_size; @@ -1961,6 +1960,8 @@ void xhci_update_erst_dequeue(struct xhci_hcd *xhci, void xhci_add_interrupter(struct xhci_hcd *xhci, unsigned int intr_num); int xhci_usb_endpoint_maxp(struct usb_device *udev, struct usb_host_endpoint *host_ep); +void xhci_portsc_writel(struct xhci_port *port, u32 val); +u32 xhci_portsc_readl(struct xhci_port *port); /* xHCI roothub code */ void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port, @@ -2399,25 +2400,48 @@ static inline const char *xhci_decode_portsc(char *str, u32 portsc) if (portsc == ~(u32)0) return str; - ret += sprintf(str + ret, "%s %s %s Link:%s PortSpeed:%d ", - portsc & PORT_POWER ? "Powered" : "Powered-off", - portsc & PORT_CONNECT ? "Connected" : "Not-connected", - portsc & PORT_PE ? "Enabled" : "Disabled", - xhci_portsc_link_state_string(portsc), - DEV_PORT_SPEED(portsc)); + ret += sprintf(str + ret, "Speed=%d ", DEV_PORT_SPEED(portsc)); + ret += sprintf(str + ret, "Link=%s ", xhci_portsc_link_state_string(portsc)); + /* RO/ROS: Read-only */ + if (portsc & PORT_CONNECT) + ret += sprintf(str + ret, "CCS "); if (portsc & PORT_OC) - ret += sprintf(str + ret, "OverCurrent "); - if (portsc & PORT_RESET) - ret += sprintf(str + ret, "In-Reset "); + ret += sprintf(str + ret, "OCA "); /* No set for USB2 ports */ + if (portsc & PORT_CAS) + ret += sprintf(str + ret, "CAS "); + if (portsc & PORT_DEV_REMOVE) + ret += sprintf(str + ret, "DR "); - ret += sprintf(str + ret, "Change: "); + /* RWS; writing 1 sets the bit, writing 0 clears the bit. */ + if (portsc & PORT_POWER) + ret += sprintf(str + ret, "PP "); + if (portsc & PORT_WKCONN_E) + ret += sprintf(str + ret, "WCE "); + if (portsc & PORT_WKDISC_E) + ret += sprintf(str + ret, "WDE "); + if (portsc & PORT_WKOC_E) + ret += sprintf(str + ret, "WOE "); + + /* RW; writing 1 sets the bit, writing 0 clears the bit */ + if (portsc & PORT_LINK_STROBE) + ret += sprintf(str + ret, "LWS "); /* LWS 0 write is ignored */ + + /* RW1S; writing 1 sets the bit, writing 0 has no effect */ + if (portsc & PORT_RESET) + ret += sprintf(str + ret, "PR "); + if (portsc & PORT_WR) + ret += sprintf(str + ret, "WPR "); /* RsvdZ for USB2 ports */ + + /* RW1CS; writing 1 clears the bit, writing 0 has no effect. */ + if (portsc & PORT_PE) + ret += sprintf(str + ret, "PED "); if (portsc & PORT_CSC) ret += sprintf(str + ret, "CSC "); if (portsc & PORT_PEC) - ret += sprintf(str + ret, "PEC "); + ret += sprintf(str + ret, "PEC "); /* No set for USB3 ports */ if (portsc & PORT_WRC) - ret += sprintf(str + ret, "WRC "); + ret += sprintf(str + ret, "WRC "); /* RsvdZ for USB2 ports */ if (portsc & PORT_OCC) ret += sprintf(str + ret, "OCC "); if (portsc & PORT_RC) @@ -2425,17 +2449,7 @@ static inline const char *xhci_decode_portsc(char *str, u32 portsc) if (portsc & PORT_PLC) ret += sprintf(str + ret, "PLC "); if (portsc & PORT_CEC) - ret += sprintf(str + ret, "CEC "); - if (portsc & PORT_CAS) - ret += sprintf(str + ret, "CAS "); - - ret += sprintf(str + ret, "Wake: "); - if (portsc & PORT_WKCONN_E) - ret += sprintf(str + ret, "WCE "); - if (portsc & PORT_WKDISC_E) - ret += sprintf(str + ret, "WDE "); - if (portsc & PORT_WKOC_E) - ret += sprintf(str + ret, "WOE "); + ret += sprintf(str + ret, "CEC "); /* RsvdZ for USB2 ports */ return str; } diff --git a/drivers/usb/misc/apple-mfi-fastcharge.c b/drivers/usb/misc/apple-mfi-fastcharge.c index 8e852f4b8262..47b38dcc2992 100644 --- a/drivers/usb/misc/apple-mfi-fastcharge.c +++ b/drivers/usb/misc/apple-mfi-fastcharge.c @@ -134,7 +134,6 @@ static int apple_mfi_fc_set_property(struct power_supply *psy, ret = -EINVAL; } - pm_runtime_mark_last_busy(&mfi->udev->dev); pm_runtime_put_autosuspend(&mfi->udev->dev); return ret; diff --git a/drivers/usb/misc/chaoskey.c b/drivers/usb/misc/chaoskey.c index 225863321dc4..45cff32656c6 100644 --- a/drivers/usb/misc/chaoskey.c +++ b/drivers/usb/misc/chaoskey.c @@ -444,9 +444,19 @@ static ssize_t chaoskey_read(struct file *file, goto bail; mutex_unlock(&dev->rng_lock); - result = mutex_lock_interruptible(&dev->lock); - if (result) - goto bail; + if (file->f_flags & O_NONBLOCK) { + result = mutex_trylock(&dev->lock); + if (result == 0) { + result = -EAGAIN; + goto bail; + } else { + result = 0; + } + } else { + result = mutex_lock_interruptible(&dev->lock); + if (result) + goto bail; + } if (dev->valid == dev->used) { result = _chaoskey_fill(dev); if (result < 0) { diff --git a/drivers/usb/misc/usb-ljca.c b/drivers/usb/misc/usb-ljca.c index c562630d862c..9e65bb9577ea 100644 --- a/drivers/usb/misc/usb-ljca.c +++ b/drivers/usb/misc/usb-ljca.c @@ -164,28 +164,39 @@ struct ljca_match_ids_walk_data { struct acpi_device *adev; }; +/* + * ACPI hardware IDs for LJCA client devices. + * + * [1] Some BIOS implementations use these IDs for denoting LJCA client devices + * even though the IDs have been allocated for USBIO. This isn't a problem + * as the usb-ljca driver is probed based on the USB device's vendor and + * product IDs and its client drivers are probed based on auxiliary device + * names, not these ACPI _HIDs. List of such systems: + * + * Dell Precision 5490 + */ static const struct acpi_device_id ljca_gpio_hids[] = { - { "INTC1074" }, - { "INTC1096" }, - { "INTC100B" }, - { "INTC10D1" }, - { "INTC10B5" }, + { "INTC100B" }, /* RPL LJCA GPIO */ + { "INTC1074" }, /* CVF LJCA GPIO */ + { "INTC1096" }, /* ADL LJCA GPIO */ + { "INTC10B5" }, /* LNL LJCA GPIO */ + { "INTC10D1" }, /* MTL (CVF VSC) USBIO GPIO [1] */ {}, }; static const struct acpi_device_id ljca_i2c_hids[] = { - { "INTC1075" }, - { "INTC1097" }, - { "INTC100C" }, - { "INTC10D2" }, + { "INTC100C" }, /* RPL LJCA I2C */ + { "INTC1075" }, /* CVF LJCA I2C */ + { "INTC1097" }, /* ADL LJCA I2C */ + { "INTC10D2" }, /* MTL (CVF VSC) USBIO I2C [1] */ {}, }; static const struct acpi_device_id ljca_spi_hids[] = { - { "INTC1091" }, - { "INTC1098" }, - { "INTC100D" }, - { "INTC10D3" }, + { "INTC100D" }, /* RPL LJCA SPI */ + { "INTC1091" }, /* TGL/ADL LJCA SPI */ + { "INTC1098" }, /* ADL LJCA SPI */ + { "INTC10D3" }, /* MTL (CVF VSC) USBIO SPI [1] */ {}, }; @@ -891,7 +902,7 @@ static struct usb_driver ljca_driver = { }; module_usb_driver(ljca_driver); -MODULE_AUTHOR("Wentong Wu "); +MODULE_AUTHOR("Wentong Wu"); MODULE_AUTHOR("Zhifeng Wang "); MODULE_AUTHOR("Lixu Zhang "); MODULE_DESCRIPTION("Intel La Jolla Cove Adapter USB driver"); diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h index c11840b9a6f1..ba5a63669e5f 100644 --- a/drivers/usb/mtu3/mtu3.h +++ b/drivers/usb/mtu3/mtu3.h @@ -65,7 +65,7 @@ struct mtu3_request; #define MTU3_U3_IP_SLOT_DEFAULT 2 #define MTU3_U2_IP_SLOT_DEFAULT 1 -/** +/* * IP TRUNK version * from 0x1003 version, USB3 Gen2 is supported, two changes affect driver: * 1. MAXPKT and MULTI bits layout of TXCSR1 and RXCSR1 are adjusted, @@ -74,9 +74,9 @@ struct mtu3_request; */ #define MTU3_TRUNK_VERS_1003 0x1003 -/** +/* * Normally the device works on HS or SS, to simplify fifo management, - * devide fifo into some 512B parts, use bitmap to manage it; And + * divide fifo into some 512B parts, use bitmap to manage it; And * 128 bits size of bitmap is large enough, that means it can manage * up to 64KB fifo size. * NOTE: MTU3_EP_FIFO_UNIT should be power of two @@ -85,7 +85,7 @@ struct mtu3_request; #define MTU3_FIFO_BIT_SIZE 128 #define MTU3_U2_IP_EP0_FIFO_SIZE 64 -/** +/* * Maximum size of ep0 response buffer for ch9 requests, * the SET_SEL request uses 6 so far, and GET_STATUS is 2 */ @@ -103,6 +103,7 @@ enum mtu3_speed { }; /** + * enum mtu3_g_ep0_state - endpoint 0 states * @MU3D_EP0_STATE_SETUP: waits for SETUP or received a SETUP * without data stage. * @MU3D_EP0_STATE_TX: IN data stage @@ -121,11 +122,12 @@ enum mtu3_g_ep0_state { }; /** - * MTU3_DR_FORCE_NONE: automatically switch host and periperal mode + * enum mtu3_dr_force_mode - indicates host/OTG operating mode + * @MTU3_DR_FORCE_NONE: automatically switch host and peripheral mode * by IDPIN signal. - * MTU3_DR_FORCE_HOST: force to enter host mode and override OTG + * @MTU3_DR_FORCE_HOST: force to enter host mode and override OTG * IDPIN signal. - * MTU3_DR_FORCE_DEVICE: force to enter peripheral mode. + * @MTU3_DR_FORCE_DEVICE: force to enter peripheral mode. */ enum mtu3_dr_force_mode { MTU3_DR_FORCE_NONE = 0, @@ -134,6 +136,7 @@ enum mtu3_dr_force_mode { }; /** + * struct mtu3_fifo_info - HW FIFO description and management data * @base: the base address of fifo * @limit: the bitmap size in bits * @bitmap: fifo bitmap in unit of @MTU3_EP_FIFO_UNIT @@ -145,7 +148,7 @@ struct mtu3_fifo_info { }; /** - * General Purpose Descriptor (GPD): + * struct qmu_gpd - General Purpose Descriptor (GPD): * The format of TX GPD is a little different from RX one. * And the size of GPD is 16 bytes. * @@ -179,11 +182,13 @@ struct qmu_gpd { } __packed; /** -* dma: physical base address of GPD segment -* start: virtual base address of GPD segment -* end: the last GPD element -* enqueue: the first empty GPD to use -* dequeue: the first completed GPD serviced by ISR +* struct mtu3_gpd_ring - GPD ring descriptor +* @dma: physical base address of GPD segment +* @start: virtual base address of GPD segment +* @end: the last GPD element +* @enqueue: the first empty GPD to use +* @dequeue: the first completed GPD serviced by ISR +* * NOTE: the size of GPD ring should be >= 2 */ struct mtu3_gpd_ring { @@ -195,6 +200,7 @@ struct mtu3_gpd_ring { }; /** +* struct otg_switch_mtk - OTG/dual-role switch management * @vbus: vbus 5V used by host mode * @edev: external connector used to detect vbus and iddig changes * @id_nb : notifier for iddig(idpin) detection @@ -222,6 +228,7 @@ struct otg_switch_mtk { }; /** + * struct ssusb_mtk - SuperSpeed USB descriptor (MTK) * @mac_base: register base address of device MAC, exclude xHCI's * @ippc_base: register base address of IP Power and Clock interface (IPPC) * @vusb33: usb3.3V shared by device/host IP @@ -268,6 +275,7 @@ struct ssusb_mtk { }; /** + * struct mtu3_ep - common mtu3 endpoint description * @fifo_size: it is (@slot + 1) * @fifo_seg_size * @fifo_seg_size: it is roundup_pow_of_two(@maxp) */ diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c index a3a6282893d0..3a25ee18f144 100644 --- a/drivers/usb/mtu3/mtu3_core.c +++ b/drivers/usb/mtu3/mtu3_core.c @@ -290,7 +290,7 @@ static void mtu3_csr_init(struct mtu3 *mtu) /* delay about 0.1us from detecting reset to send chirp-K */ mtu3_clrbits(mbase, U3D_LINK_RESET_INFO, WTCHRP_MSK); - /* enable automatical HWRW from L1 */ + /* enable automatic HWRW from L1 */ mtu3_setbits(mbase, U3D_POWER_MANAGEMENT, LPM_HRWE); } diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c index 7b5a431acb56..cc8a864dbd63 100644 --- a/drivers/usb/mtu3/mtu3_plat.c +++ b/drivers/usb/mtu3/mtu3_plat.c @@ -431,7 +431,6 @@ static int mtu3_probe(struct platform_device *pdev) } device_enable_async_suspend(dev); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); pm_runtime_forbid(dev); diff --git a/drivers/usb/mtu3/mtu3_qmu.c b/drivers/usb/mtu3/mtu3_qmu.c index 3d77408e3133..03f26589b056 100644 --- a/drivers/usb/mtu3/mtu3_qmu.c +++ b/drivers/usb/mtu3/mtu3_qmu.c @@ -221,7 +221,7 @@ static struct qmu_gpd *advance_deq_gpd(struct mtu3_gpd_ring *ring) return ring->dequeue; } -/* check if a ring is emtpy */ +/* check if a ring is empty */ static bool gpd_ring_empty(struct mtu3_gpd_ring *ring) { struct qmu_gpd *enq = ring->enqueue; diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index c7234b236971..0acc62569ae5 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -2031,7 +2031,6 @@ static void musb_pm_runtime_check_session(struct musb *musb) if (!musb->session) break; trace_musb_state(musb, devctl, "Allow PM on possible host mode disconnect"); - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); musb->session = false; return; @@ -2063,7 +2062,6 @@ static void musb_pm_runtime_check_session(struct musb *musb) msecs_to_jiffies(3000)); } else { trace_musb_state(musb, devctl, "Allow PM with no session"); - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); } @@ -2090,7 +2088,6 @@ static void musb_irq_work(struct work_struct *data) sysfs_notify(&musb->controller->kobj, NULL, "mode"); } - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); } @@ -2564,7 +2561,6 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) musb_init_debugfs(musb); musb->is_initialized = 1; - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); return 0; @@ -2887,7 +2883,6 @@ static int musb_resume(struct device *dev) error); spin_unlock_irqrestore(&musb->lock, flags); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return 0; diff --git a/drivers/usb/musb/musb_debugfs.c b/drivers/usb/musb/musb_debugfs.c index 2d623284edf6..5092d62c2062 100644 --- a/drivers/usb/musb/musb_debugfs.c +++ b/drivers/usb/musb/musb_debugfs.c @@ -106,7 +106,6 @@ static int musb_regdump_show(struct seq_file *s, void *unused) } } - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); return 0; } @@ -119,7 +118,6 @@ static int musb_test_mode_show(struct seq_file *s, void *unused) pm_runtime_get_sync(musb->controller); test = musb_readb(musb->mregs, MUSB_TESTMODE); - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); if (test == (MUSB_TEST_FORCE_HOST | MUSB_TEST_FORCE_FS)) @@ -216,7 +214,6 @@ static ssize_t musb_test_mode_write(struct file *file, musb_writeb(musb->mregs, MUSB_TESTMODE, test); ret: - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); return count; } @@ -243,7 +240,6 @@ static int musb_softconnect_show(struct seq_file *s, void *unused) reg = musb_readb(musb->mregs, MUSB_DEVCTL); connect = reg & MUSB_DEVCTL_SESSION ? 1 : 0; - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); break; default: @@ -304,7 +300,6 @@ static ssize_t musb_softconnect_write(struct file *file, } } - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); return count; } diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index a08ce96c08d3..e3935f18dd56 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -296,7 +296,6 @@ static void otg_timer(struct timer_list *t) if (err < 0) dev_err(dev, "%s resume work: %i\n", __func__, err); spin_unlock_irqrestore(&musb->lock, flags); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); } diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index caf4d4cd4b75..d666c4292753 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1258,7 +1258,6 @@ static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req, unlock: spin_unlock_irqrestore(&musb->lock, lockflags); - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); return status; @@ -1642,7 +1641,6 @@ static void musb_gadget_work(struct work_struct *work) spin_lock_irqsave(&musb->lock, flags); musb_pullup(musb, musb->softconnect); spin_unlock_irqrestore(&musb->lock, flags); - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); } @@ -1862,7 +1860,6 @@ static int musb_gadget_start(struct usb_gadget *g, if (musb->xceiv && musb->xceiv->last_event == USB_EVENT_ID) musb_platform_set_vbus(musb, 1); - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); return 0; @@ -1916,7 +1913,6 @@ static int musb_gadget_stop(struct usb_gadget *g) usb_gadget_set_state(g, USB_STATE_NOTATTACHED); /* Force check of devctl register for PM runtime */ - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); return 0; diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index c35c07b7488c..48bb9bfb2204 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -151,7 +151,6 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue) default: dev_dbg(musb->controller, "ID float\n"); } - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); atomic_notifier_call_chain(&musb->xceiv->notifier, musb->xceiv->last_event, NULL); diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c index e1435bc59662..5a9b9353f343 100644 --- a/drivers/usb/phy/phy.c +++ b/drivers/usb/phy/phy.c @@ -646,6 +646,8 @@ int usb_add_phy(struct usb_phy *x, enum usb_phy_type type) return -EINVAL; } + INIT_LIST_HEAD(&x->head); + usb_charger_init(x); ret = usb_add_extcon(x); if (ret) @@ -696,6 +698,8 @@ int usb_add_phy_dev(struct usb_phy *x) return -EINVAL; } + INIT_LIST_HEAD(&x->head); + usb_charger_init(x); ret = usb_add_extcon(x); if (ret) diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index dc2fec9168b7..cf4a0367d6d6 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -827,23 +827,7 @@ static void usbhs_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); } -static int usbhsc_suspend(struct device *dev) -{ - struct usbhs_priv *priv = dev_get_drvdata(dev); - struct usbhs_mod *mod = usbhs_mod_get_current(priv); - - if (mod) { - usbhs_mod_call(priv, stop, priv); - usbhs_mod_change(priv, -1); - } - - if (mod || !usbhs_get_dparam(priv, runtime_pwctrl)) - usbhsc_power_ctrl(priv, 0); - - return 0; -} - -static int usbhsc_resume(struct device *dev) +static void usbhsc_restore(struct device *dev) { struct usbhs_priv *priv = dev_get_drvdata(dev); struct platform_device *pdev = usbhs_priv_to_pdev(priv); @@ -856,6 +840,39 @@ static int usbhsc_resume(struct device *dev) usbhs_platform_call(priv, phy_reset, pdev); usbhsc_schedule_notify_hotplug(pdev); +} + +static int usbhsc_suspend(struct device *dev) +{ + struct usbhs_priv *priv = dev_get_drvdata(dev); + struct usbhs_mod *mod = usbhs_mod_get_current(priv); + int ret; + + if (mod) { + usbhs_mod_call(priv, stop, priv); + usbhs_mod_change(priv, -1); + } + + if (mod || !usbhs_get_dparam(priv, runtime_pwctrl)) + usbhsc_power_ctrl(priv, 0); + + ret = reset_control_assert(priv->rsts); + if (ret) + usbhsc_restore(dev); + + return ret; +} + +static int usbhsc_resume(struct device *dev) +{ + struct usbhs_priv *priv = dev_get_drvdata(dev); + int ret; + + ret = reset_control_deassert(priv->rsts); + if (ret) + return ret; + + usbhsc_restore(dev); return 0; } diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index 44f5b58beec9..5c41c1c82c3f 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -435,43 +435,39 @@ static int belkin_sa_tiocmset(struct tty_struct *tty, struct belkin_sa_private *priv = usb_get_serial_port_data(port); unsigned long control_state; unsigned long flags; - int retval; - int rts = 0; - int dtr = 0; + int retval = 0; spin_lock_irqsave(&priv->lock, flags); control_state = priv->control_state; - if (set & TIOCM_RTS) { + if (set & TIOCM_RTS) control_state |= TIOCM_RTS; - rts = 1; - } - if (set & TIOCM_DTR) { + if (set & TIOCM_DTR) control_state |= TIOCM_DTR; - dtr = 1; - } - if (clear & TIOCM_RTS) { + if (clear & TIOCM_RTS) control_state &= ~TIOCM_RTS; - rts = 0; - } - if (clear & TIOCM_DTR) { + if (clear & TIOCM_DTR) control_state &= ~TIOCM_DTR; - dtr = 0; - } priv->control_state = control_state; spin_unlock_irqrestore(&priv->lock, flags); - retval = BSA_USB_CMD(BELKIN_SA_SET_RTS_REQUEST, rts); - if (retval < 0) { - dev_err(&port->dev, "Set RTS error %d\n", retval); - goto exit; + if ((set | clear) & TIOCM_RTS) { + retval = BSA_USB_CMD(BELKIN_SA_SET_RTS_REQUEST, + !!(control_state & TIOCM_RTS)); + if (retval < 0) { + dev_err(&port->dev, "Set RTS error %d\n", retval); + goto exit; + } } - retval = BSA_USB_CMD(BELKIN_SA_SET_DTR_REQUEST, dtr); - if (retval < 0) { - dev_err(&port->dev, "Set DTR error %d\n", retval); - goto exit; + if ((set | clear) & TIOCM_DTR) { + retval = BSA_USB_CMD(BELKIN_SA_SET_DTR_REQUEST, + !!(control_state & TIOCM_DTR)); + if (retval < 0) { + dev_err(&port->dev, "Set DTR error %d\n", retval); + goto exit; + } } exit: return retval; diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index b37fa31f5694..fe2f21d85737 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -107,26 +107,24 @@ struct ftdi_quirk { }; static int ftdi_jtag_probe(struct usb_serial *serial); -static int ftdi_NDI_device_setup(struct usb_serial *serial); static int ftdi_stmclite_probe(struct usb_serial *serial); static int ftdi_8u2232c_probe(struct usb_serial *serial); -static void ftdi_USB_UIRT_setup(struct ftdi_private *priv); -static void ftdi_HE_TIRA1_setup(struct ftdi_private *priv); +static void ftdi_usb_uirt_setup(struct ftdi_private *priv); +static void ftdi_he_tira1_setup(struct ftdi_private *priv); static const struct ftdi_quirk ftdi_jtag_quirk = { .probe = ftdi_jtag_probe, }; -static const struct ftdi_quirk ftdi_NDI_device_quirk = { - .probe = ftdi_NDI_device_setup, +static const struct ftdi_quirk ftdi_ndi_quirk = { }; -static const struct ftdi_quirk ftdi_USB_UIRT_quirk = { - .port_probe = ftdi_USB_UIRT_setup, +static const struct ftdi_quirk ftdi_usb_uirt_quirk = { + .port_probe = ftdi_usb_uirt_setup, }; -static const struct ftdi_quirk ftdi_HE_TIRA1_quirk = { - .port_probe = ftdi_HE_TIRA1_setup, +static const struct ftdi_quirk ftdi_he_tira1_quirk = { + .port_probe = ftdi_he_tira1_setup, }; static const struct ftdi_quirk ftdi_stmclite_quirk = { @@ -590,9 +588,9 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(OCT_VID, OCT_US101_PID) }, { USB_DEVICE(OCT_VID, OCT_DK201_PID) }, { USB_DEVICE(FTDI_VID, FTDI_HE_TIRA1_PID), - .driver_info = (kernel_ulong_t)&ftdi_HE_TIRA1_quirk }, + .driver_info = (kernel_ulong_t)&ftdi_he_tira1_quirk }, { USB_DEVICE(FTDI_VID, FTDI_USB_UIRT_PID), - .driver_info = (kernel_ulong_t)&ftdi_USB_UIRT_quirk }, + .driver_info = (kernel_ulong_t)&ftdi_usb_uirt_quirk }, { USB_DEVICE(FTDI_VID, PROTEGO_SPECIAL_1) }, { USB_DEVICE(FTDI_VID, PROTEGO_R2X0) }, { USB_DEVICE(FTDI_VID, PROTEGO_SPECIAL_3) }, @@ -628,10 +626,8 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(FTDI_VID, FTDI_IBS_PEDO_PID) }, { USB_DEVICE(FTDI_VID, FTDI_IBS_PROD_PID) }, { USB_DEVICE(FTDI_VID, FTDI_TAVIR_STK500_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_TIAO_UMPA_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(FTDI_VID, FTDI_NT_ORIONLXM_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, FTDI_TIAO_UMPA_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, FTDI_NT_ORIONLXM_PID, 1) }, { USB_DEVICE(FTDI_VID, FTDI_NT_ORIONLX_PLUS_PID) }, { USB_DEVICE(FTDI_VID, FTDI_NT_ORION_IO_PID) }, { USB_DEVICE(FTDI_VID, FTDI_NT_ORIONMX_PID) }, @@ -794,17 +790,17 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13U_PID) }, { USB_DEVICE(ELEKTOR_VID, ELEKTOR_FT323R_PID) }, { USB_DEVICE(FTDI_VID, FTDI_NDI_HUC_PID), - .driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk }, + .driver_info = (kernel_ulong_t)&ftdi_ndi_quirk }, { USB_DEVICE(FTDI_VID, FTDI_NDI_SPECTRA_SCU_PID), - .driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk }, + .driver_info = (kernel_ulong_t)&ftdi_ndi_quirk }, { USB_DEVICE(FTDI_VID, FTDI_NDI_FUTURE_2_PID), - .driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk }, + .driver_info = (kernel_ulong_t)&ftdi_ndi_quirk }, { USB_DEVICE(FTDI_VID, FTDI_NDI_FUTURE_3_PID), - .driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk }, + .driver_info = (kernel_ulong_t)&ftdi_ndi_quirk }, { USB_DEVICE(FTDI_VID, FTDI_NDI_AURORA_SCU_PID), - .driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk }, + .driver_info = (kernel_ulong_t)&ftdi_ndi_quirk }, { USB_DEVICE(FTDI_NDI_VID, FTDI_NDI_EMGUIDE_GEMINI_PID), - .driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk }, + .driver_info = (kernel_ulong_t)&ftdi_ndi_quirk }, { USB_DEVICE(TELLDUS_VID, TELLDUS_TELLSTICK_PID) }, { USB_DEVICE(NOVITUS_VID, NOVITUS_BONO_E_PID) }, { USB_DEVICE(FTDI_VID, RTSYSTEMS_USB_VX8_PID) }, @@ -842,24 +838,17 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(FTDI_VID, FTDI_ELSTER_UNICOM_PID) }, { USB_DEVICE(FTDI_VID, FTDI_PROPOX_JTAGCABLEII_PID) }, { USB_DEVICE(FTDI_VID, FTDI_PROPOX_ISPCABLEIII_PID) }, - { USB_DEVICE(FTDI_VID, CYBER_CORTEX_AV_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, CYBER_CORTEX_AV_PID, 1) }, { USB_DEVICE_INTERFACE_NUMBER(OLIMEX_VID, OLIMEX_ARM_USB_OCD_PID, 1) }, { USB_DEVICE_INTERFACE_NUMBER(OLIMEX_VID, OLIMEX_ARM_USB_OCD_H_PID, 1) }, { USB_DEVICE_INTERFACE_NUMBER(OLIMEX_VID, OLIMEX_ARM_USB_TINY_PID, 1) }, { USB_DEVICE_INTERFACE_NUMBER(OLIMEX_VID, OLIMEX_ARM_USB_TINY_H_PID, 1) }, - { USB_DEVICE(FIC_VID, FIC_NEO1973_DEBUG_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(FTDI_VID, FTDI_OOCDLINK_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(FTDI_VID, LMI_LM3S_DEVEL_BOARD_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(FTDI_VID, LMI_LM3S_EVAL_BOARD_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(FTDI_VID, LMI_LM3S_ICDI_BOARD_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(FTDI_VID, FTDI_TURTELIZER_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE_INTERFACE_NUMBER(FIC_VID, FIC_NEO1973_DEBUG_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, FTDI_OOCDLINK_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, LMI_LM3S_DEVEL_BOARD_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, LMI_LM3S_EVAL_BOARD_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, LMI_LM3S_ICDI_BOARD_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, FTDI_TURTELIZER_PID, 1) }, { USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID_USB60F) }, { USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID_SCU18) }, { USB_DEVICE(FTDI_VID, FTDI_REU_TINY_PID) }, @@ -901,17 +890,14 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(ATMEL_VID, STK541_PID) }, { USB_DEVICE(DE_VID, STB_PID) }, { USB_DEVICE(DE_VID, WHT_PID) }, - { USB_DEVICE(ADI_VID, ADI_GNICE_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(ADI_VID, ADI_GNICEPLUS_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE_INTERFACE_NUMBER(ADI_VID, ADI_GNICE_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(ADI_VID, ADI_GNICEPLUS_PID, 1) }, { USB_DEVICE_AND_INTERFACE_INFO(MICROCHIP_VID, MICROCHIP_USB_BOARD_PID, USB_CLASS_VENDOR_SPEC, USB_SUBCLASS_VENDOR_SPEC, 0x00) }, { USB_DEVICE_INTERFACE_NUMBER(ACTEL_VID, MICROSEMI_ARROW_SF2PLUS_BOARD_PID, 2) }, { USB_DEVICE(JETI_VID, JETI_SPC1201_PID) }, - { USB_DEVICE(MARVELL_VID, MARVELL_SHEEVAPLUG_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE_INTERFACE_NUMBER(MARVELL_VID, MARVELL_SHEEVAPLUG_PID, 1) }, { USB_DEVICE(LARSENBRUSGAARD_VID, LB_ALTITRACK_PID) }, { USB_DEVICE(GN_OTOMETRICS_VID, AURICAL_USB_PID) }, { USB_DEVICE(FTDI_VID, PI_C865_PID) }, @@ -934,10 +920,8 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(PI_VID, PI_1016_PID) }, { USB_DEVICE(KONDO_VID, KONDO_USB_SERIAL_PID) }, { USB_DEVICE(BAYER_VID, BAYER_CONTOUR_CABLE_PID) }, - { USB_DEVICE(FTDI_VID, MARVELL_OPENRD_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(FTDI_VID, TI_XDS100V2_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, MARVELL_OPENRD_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, TI_XDS100V2_PID, 1) }, { USB_DEVICE(FTDI_VID, HAMEG_HO820_PID) }, { USB_DEVICE(FTDI_VID, HAMEG_HO720_PID) }, { USB_DEVICE(FTDI_VID, HAMEG_HO730_PID) }, @@ -946,18 +930,14 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(FTDI_VID, MJSG_SR_RADIO_PID) }, { USB_DEVICE(FTDI_VID, MJSG_HD_RADIO_PID) }, { USB_DEVICE(FTDI_VID, MJSG_XM_RADIO_PID) }, - { USB_DEVICE(FTDI_VID, XVERVE_SIGNALYZER_ST_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(FTDI_VID, XVERVE_SIGNALYZER_SLITE_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(FTDI_VID, XVERVE_SIGNALYZER_SH2_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, XVERVE_SIGNALYZER_ST_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, XVERVE_SIGNALYZER_SLITE_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, XVERVE_SIGNALYZER_SH2_PID, 1) }, { USB_DEVICE(FTDI_VID, XVERVE_SIGNALYZER_SH4_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { USB_DEVICE(FTDI_VID, SEGWAY_RMP200_PID) }, { USB_DEVICE(FTDI_VID, ACCESIO_COM4SM_PID) }, - { USB_DEVICE(IONICS_VID, IONICS_PLUGCOMPUTER_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE_INTERFACE_NUMBER(IONICS_VID, IONICS_PLUGCOMPUTER_PID, 1) }, { USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_24_MASTER_WING_PID) }, { USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_PC_WING_PID) }, { USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_USB_DMX_PID) }, @@ -972,15 +952,12 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(FTDI_VID, FTDI_CINTERION_MC55I_PID) }, { USB_DEVICE(FTDI_VID, FTDI_FHE_PID) }, { USB_DEVICE(FTDI_VID, FTDI_DOTEC_PID) }, - { USB_DEVICE(QIHARDWARE_VID, MILKYMISTONE_JTAGSERIAL_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(ST_VID, ST_STMCLT_2232_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE_INTERFACE_NUMBER(QIHARDWARE_VID, MILKYMISTONE_JTAGSERIAL_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(ST_VID, ST_STMCLT_2232_PID, 1) }, { USB_DEVICE(ST_VID, ST_STMCLT_4232_PID), .driver_info = (kernel_ulong_t)&ftdi_stmclite_quirk }, { USB_DEVICE(FTDI_VID, FTDI_RF_R106) }, - { USB_DEVICE(FTDI_VID, FTDI_DISTORTEC_JTAG_LOCK_PICK_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, FTDI_DISTORTEC_JTAG_LOCK_PICK_PID, 1) }, { USB_DEVICE(FTDI_VID, FTDI_LUMEL_PD12_PID) }, /* Crucible Devices */ { USB_DEVICE(FTDI_VID, FTDI_CT_COMET_PID) }, @@ -1055,8 +1032,7 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(ICPDAS_VID, ICPDAS_I7561U_PID) }, { USB_DEVICE(ICPDAS_VID, ICPDAS_I7563U_PID) }, { USB_DEVICE(WICED_VID, WICED_USB20706V2_PID) }, - { USB_DEVICE(TI_VID, TI_CC3200_LAUNCHPAD_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE_INTERFACE_NUMBER(TI_VID, TI_CC3200_LAUNCHPAD_PID, 1) }, { USB_DEVICE(CYPRESS_VID, CYPRESS_WICED_BT_USB_PID) }, { USB_DEVICE(CYPRESS_VID, CYPRESS_WICED_WL_USB_PID) }, { USB_DEVICE(AIRBUS_DS_VID, AIRBUS_DS_P8GR) }, @@ -1076,10 +1052,8 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(UBLOX_VID, UBLOX_C099F9P_ODIN_PID) }, { USB_DEVICE_INTERFACE_NUMBER(UBLOX_VID, UBLOX_EVK_M101_PID, 2) }, /* FreeCalypso USB adapters */ - { USB_DEVICE(FTDI_VID, FTDI_FALCONIA_JTAG_BUF_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(FTDI_VID, FTDI_FALCONIA_JTAG_UNBUF_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, FTDI_FALCONIA_JTAG_BUF_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, FTDI_FALCONIA_JTAG_UNBUF_PID, 1) }, /* GMC devices */ { USB_DEVICE(GMC_VID, GMC_Z216C_PID) }, /* Altera USB Blaster 3 */ @@ -1271,6 +1245,7 @@ static int update_mctrl(struct usb_serial_port *port, unsigned int set, static u32 get_ftdi_divisor(struct tty_struct *tty, struct usb_serial_port *port) { + const struct ftdi_quirk *quirk = usb_get_serial_data(port->serial); struct ftdi_private *priv = usb_get_serial_port_data(port); struct device *dev = &port->dev; u32 div_value = 0; @@ -1330,16 +1305,8 @@ static u32 get_ftdi_divisor(struct tty_struct *tty, case FT232R: case FTX: if (baud <= 3000000) { - u16 product_id = le16_to_cpu( - port->serial->dev->descriptor.idProduct); - if (((product_id == FTDI_NDI_HUC_PID) || - (product_id == FTDI_NDI_SPECTRA_SCU_PID) || - (product_id == FTDI_NDI_FUTURE_2_PID) || - (product_id == FTDI_NDI_FUTURE_3_PID) || - (product_id == FTDI_NDI_AURORA_SCU_PID)) && - (baud == 19200)) { + if (quirk == &ftdi_ndi_quirk && baud == 19200) baud = 1200000; - } div_value = ftdi_232bm_baud_to_divisor(baud); } else { dev_dbg(dev, "%s - Baud rate too high!\n", __func__); @@ -2236,7 +2203,9 @@ static int ftdi_port_probe(struct usb_serial_port *port) goto err_free; ftdi_set_max_packet_size(port); - if (read_latency_timer(port) < 0) + if (quirk == &ftdi_ndi_quirk) + priv->latency = 1; + else if (read_latency_timer(port) < 0) priv->latency = 16; write_latency_timer(port); @@ -2255,20 +2224,22 @@ err_free: return result; } -/* Setup for the USB-UIRT device, which requires hardwired - * baudrate (38400 gets mapped to 312500) */ -/* Called from usbserial:serial_probe */ -static void ftdi_USB_UIRT_setup(struct ftdi_private *priv) +/* + * Setup for the USB-UIRT device, which requires hardwired baudrate + * (38400 gets mapped to 312500). + */ +static void ftdi_usb_uirt_setup(struct ftdi_private *priv) { priv->flags |= ASYNC_SPD_CUST; priv->custom_divisor = 77; priv->force_baud = 38400; } -/* Setup for the HE-TIRA1 device, which requires hardwired - * baudrate (38400 gets mapped to 100000) and RTS-CTS enabled. */ - -static void ftdi_HE_TIRA1_setup(struct ftdi_private *priv) +/* + * Setup for the HE-TIRA1 device, which requires hardwired baudrate + * (38400 gets mapped to 100000) and RTS-CTS enabled. + */ +static void ftdi_he_tira1_setup(struct ftdi_private *priv) { priv->flags |= ASYNC_SPD_CUST; priv->custom_divisor = 240; @@ -2276,39 +2247,6 @@ static void ftdi_HE_TIRA1_setup(struct ftdi_private *priv) priv->force_rtscts = 1; } -/* - * Module parameter to control latency timer for NDI FTDI-based USB devices. - * If this value is not set in /etc/modprobe.d/ its value will be set - * to 1ms. - */ -static int ndi_latency_timer = 1; - -/* Setup for the NDI FTDI-based USB devices, which requires hardwired - * baudrate (19200 gets mapped to 1200000). - * - * Called from usbserial:serial_probe. - */ -static int ftdi_NDI_device_setup(struct usb_serial *serial) -{ - struct usb_device *udev = serial->dev; - int latency = ndi_latency_timer; - - if (latency == 0) - latency = 1; - if (latency > 99) - latency = 99; - - dev_dbg(&udev->dev, "%s setting NDI device latency to %d\n", __func__, latency); - dev_info(&udev->dev, "NDI device with a latency value of %d\n", latency); - - /* FIXME: errors are not returned */ - usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - FTDI_SIO_SET_LATENCY_TIMER_REQUEST, - FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE, - latency, 0, NULL, 0, WDR_TIMEOUT); - return 0; -} - /* * First port on JTAG adaptors such as Olimex arm-usb-ocd or the FIC/OpenMoko * Neo1973 Debug Board is reserved for JTAG interface and can be accessed from @@ -2319,26 +2257,29 @@ static int ftdi_jtag_probe(struct usb_serial *serial) struct usb_interface *intf = serial->interface; int ifnum = intf->cur_altsetting->desc.bInterfaceNumber; - if (ifnum == 0) { - dev_info(&intf->dev, "Ignoring interface reserved for JTAG\n"); + if (ifnum == 0) return -ENODEV; - } return 0; } static int ftdi_8u2232c_probe(struct usb_serial *serial) { + struct usb_interface *intf = serial->interface; struct usb_device *udev = serial->dev; + int ifnum = intf->cur_altsetting->desc.bInterfaceNumber; - if (udev->manufacturer && !strcmp(udev->manufacturer, "CALAO Systems")) - return ftdi_jtag_probe(serial); + if (ifnum == 0) { + if (udev->manufacturer && + !strcmp(udev->manufacturer, "CALAO Systems")) + return -ENODEV; - if (udev->product && - (!strcmp(udev->product, "Arrow USB Blaster") || - !strcmp(udev->product, "BeagleBone/XDS100V2") || - !strcmp(udev->product, "SNAP Connect E10"))) - return ftdi_jtag_probe(serial); + if (udev->product && + (!strcmp(udev->product, "Arrow USB Blaster") || + !strcmp(udev->product, "BeagleBone/XDS100V2") || + !strcmp(udev->product, "SNAP Connect E10"))) + return -ENODEV; + } return 0; } @@ -2355,10 +2296,8 @@ static int ftdi_stmclite_probe(struct usb_serial *serial) struct usb_interface *intf = serial->interface; int ifnum = intf->cur_altsetting->desc.bInterfaceNumber; - if (ifnum < 2) { - dev_info(&intf->dev, "Ignoring interface reserved for JTAG\n"); + if (ifnum < 2) return -ENODEV; - } return 0; } @@ -2935,6 +2874,3 @@ module_usb_serial_driver(serial_drivers, id_table_combined); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); - -module_param(ndi_latency_timer, int, 0644); -MODULE_PARM_DESC(ndi_latency_timer, "NDI device latency timer override"); diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index 464433be2034..3a1343d88386 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -109,6 +109,21 @@ struct kobil_private { __u16 device_type; }; +static int kobil_ctrl_send(struct usb_serial_port *port, u8 req, u16 val) +{ + return usb_control_msg(port->serial->dev, + usb_sndctrlpipe(port->serial->dev, 0), + req, USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, + val, 0, NULL, 0, KOBIL_TIMEOUT); +} + +static int kobil_ctrl_recv(struct usb_serial_port *port, u8 req, u16 val, void *buf, u16 size) +{ + return usb_control_msg(port->serial->dev, + usb_rcvctrlpipe(port->serial->dev, 0), + req, USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN, + val, 0, buf, size, KOBIL_TIMEOUT); +} static int kobil_port_probe(struct usb_serial_port *port) { @@ -163,10 +178,10 @@ static void kobil_init_termios(struct tty_struct *tty) static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port) { struct device *dev = &port->dev; - int result = 0; struct kobil_private *priv; unsigned char *transfer_buffer; int transfer_buffer_length = 8; + int result; priv = usb_get_serial_port_data(port); @@ -176,16 +191,8 @@ static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port) return -ENOMEM; /* get hardware version */ - result = usb_control_msg(port->serial->dev, - usb_rcvctrlpipe(port->serial->dev, 0), - SUSBCRequest_GetMisc, - USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN, - SUSBCR_MSC_GetHWVersion, - 0, - transfer_buffer, - transfer_buffer_length, - KOBIL_TIMEOUT - ); + result = kobil_ctrl_recv(port, SUSBCRequest_GetMisc, SUSBCR_MSC_GetHWVersion, + transfer_buffer, transfer_buffer_length); dev_dbg(dev, "%s - Send get_HW_version URB returns: %i\n", __func__, result); if (result >= 3) { dev_dbg(dev, "Hardware version: %i.%i.%i\n", transfer_buffer[0], @@ -193,16 +200,8 @@ static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port) } /* get firmware version */ - result = usb_control_msg(port->serial->dev, - usb_rcvctrlpipe(port->serial->dev, 0), - SUSBCRequest_GetMisc, - USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN, - SUSBCR_MSC_GetFWVersion, - 0, - transfer_buffer, - transfer_buffer_length, - KOBIL_TIMEOUT - ); + result = kobil_ctrl_recv(port, SUSBCRequest_GetMisc, SUSBCR_MSC_GetFWVersion, + transfer_buffer, transfer_buffer_length); dev_dbg(dev, "%s - Send get_FW_version URB returns: %i\n", __func__, result); if (result >= 3) { dev_dbg(dev, "Firmware version: %i.%i.%i\n", transfer_buffer[0], @@ -212,35 +211,17 @@ static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port) if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID || priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) { /* Setting Baudrate, Parity and Stopbits */ - result = usb_control_msg(port->serial->dev, - usb_sndctrlpipe(port->serial->dev, 0), - SUSBCRequest_SetBaudRateParityAndStopBits, - USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, - SUSBCR_SBR_9600 | SUSBCR_SPASB_EvenParity | - SUSBCR_SPASB_1StopBit, - 0, - NULL, - 0, - KOBIL_TIMEOUT - ); + result = kobil_ctrl_send(port, SUSBCRequest_SetBaudRateParityAndStopBits, + SUSBCR_SBR_9600 | SUSBCR_SPASB_EvenParity | SUSBCR_SPASB_1StopBit); dev_dbg(dev, "%s - Send set_baudrate URB returns: %i\n", __func__, result); /* reset all queues */ - result = usb_control_msg(port->serial->dev, - usb_sndctrlpipe(port->serial->dev, 0), - SUSBCRequest_Misc, - USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, - SUSBCR_MSC_ResetAllQueues, - 0, - NULL, - 0, - KOBIL_TIMEOUT - ); + result = kobil_ctrl_send(port, SUSBCRequest_Misc, SUSBCR_MSC_ResetAllQueues); dev_dbg(dev, "%s - Send reset_all_queues URB returns: %i\n", __func__, result); } if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID || - priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID || - priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) { + priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID || + priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) { /* start reading (Adapter B 'cause PNP string) */ result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); dev_dbg(dev, "%s - Send read URB returns: %i\n", __func__, result); @@ -291,10 +272,8 @@ static void kobil_write_int_callback(struct urb *urb) static int kobil_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) { - int length = 0; - int result = 0; - int todo = 0; struct kobil_private *priv; + int length, todo, result; if (count == 0) { dev_dbg(&port->dev, "%s - write request of 0 bytes\n", __func__); @@ -318,9 +297,10 @@ static int kobil_write(struct tty_struct *tty, struct usb_serial_port *port, if (((priv->device_type != KOBIL_ADAPTER_B_PRODUCT_ID) && (priv->filled > 2) && (priv->filled >= (priv->buf[1] + 3))) || ((priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) && (priv->filled > 3) && (priv->filled >= (priv->buf[2] + 4)))) { /* stop reading (except TWIN and KAAN SIM) */ - if ((priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) - || (priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID)) + if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID || + priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) { usb_kill_urb(port->interrupt_in_urb); + } todo = priv->filled - priv->cur_pos; @@ -347,7 +327,7 @@ static int kobil_write(struct tty_struct *tty, struct usb_serial_port *port, /* start reading (except TWIN and KAAN SIM) */ if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID || - priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) { + priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) { result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); dev_dbg(&port->dev, "%s - Send read URB returns: %i\n", __func__, result); @@ -373,8 +353,8 @@ static int kobil_tiocmget(struct tty_struct *tty) int transfer_buffer_length = 8; priv = usb_get_serial_port_data(port); - if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID - || priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) { + if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID || + priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) { /* This device doesn't support ioctl calls */ return -EINVAL; } @@ -384,16 +364,8 @@ static int kobil_tiocmget(struct tty_struct *tty) if (!transfer_buffer) return -ENOMEM; - result = usb_control_msg(port->serial->dev, - usb_rcvctrlpipe(port->serial->dev, 0), - SUSBCRequest_GetStatusLineState, - USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN, - 0, - 0, - transfer_buffer, - transfer_buffer_length, - KOBIL_TIMEOUT); - + result = kobil_ctrl_recv(port, SUSBCRequest_GetStatusLineState, 0, + transfer_buffer, transfer_buffer_length); dev_dbg(&port->dev, "Send get_status_line_state URB returns: %i\n", result); if (result < 1) { @@ -418,58 +390,41 @@ static int kobil_tiocmset(struct tty_struct *tty, struct usb_serial_port *port = tty->driver_data; struct device *dev = &port->dev; struct kobil_private *priv; + int dtr, rts; int result; - int dtr = 0; - int rts = 0; + u16 val = 0; - /* FIXME: locking ? */ priv = usb_get_serial_port_data(port); - if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID - || priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) { + if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID || + priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) { /* This device doesn't support ioctl calls */ return -EINVAL; } - if (set & TIOCM_RTS) - rts = 1; - if (set & TIOCM_DTR) - dtr = 1; - if (clear & TIOCM_RTS) - rts = 0; - if (clear & TIOCM_DTR) - dtr = 0; + dtr = (set | clear) & TIOCM_DTR; + rts = (set | clear) & TIOCM_RTS; - if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) { - if (dtr != 0) - dev_dbg(dev, "%s - Setting DTR\n", __func__); + if (dtr && priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) { + if (set & TIOCM_DTR) + val = SUSBCR_SSL_SETDTR; else - dev_dbg(dev, "%s - Clearing DTR\n", __func__); - result = usb_control_msg(port->serial->dev, - usb_sndctrlpipe(port->serial->dev, 0), - SUSBCRequest_SetStatusLinesOrQueues, - USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, - ((dtr != 0) ? SUSBCR_SSL_SETDTR : SUSBCR_SSL_CLRDTR), - 0, - NULL, - 0, - KOBIL_TIMEOUT); - } else { - if (rts != 0) - dev_dbg(dev, "%s - Setting RTS\n", __func__); + val = SUSBCR_SSL_CLRDTR; + } else if (rts) { + if (set & TIOCM_RTS) + val = SUSBCR_SSL_SETRTS; else - dev_dbg(dev, "%s - Clearing RTS\n", __func__); - result = usb_control_msg(port->serial->dev, - usb_sndctrlpipe(port->serial->dev, 0), - SUSBCRequest_SetStatusLinesOrQueues, - USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, - ((rts != 0) ? SUSBCR_SSL_SETRTS : SUSBCR_SSL_CLRRTS), - 0, - NULL, - 0, - KOBIL_TIMEOUT); + val = SUSBCR_SSL_CLRRTS; } - dev_dbg(dev, "%s - Send set_status_line URB returns: %i\n", __func__, result); - return (result < 0) ? result : 0; + + if (val) { + result = kobil_ctrl_send(port, SUSBCRequest_SetStatusLinesOrQueues, val); + if (result < 0) { + dev_err(dev, "failed to set status lines: %d\n", result); + return result; + } + } + + return 0; } static void kobil_set_termios(struct tty_struct *tty, @@ -478,9 +433,9 @@ static void kobil_set_termios(struct tty_struct *tty, { struct kobil_private *priv; int result; - unsigned short urb_val = 0; int c_cflag = tty->termios.c_cflag; speed_t speed; + u16 val; priv = usb_get_serial_port_data(port); if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID || @@ -493,37 +448,34 @@ static void kobil_set_termios(struct tty_struct *tty, speed = tty_get_baud_rate(tty); switch (speed) { case 1200: - urb_val = SUSBCR_SBR_1200; + val = SUSBCR_SBR_1200; break; default: speed = 9600; fallthrough; case 9600: - urb_val = SUSBCR_SBR_9600; + val = SUSBCR_SBR_9600; break; } - urb_val |= (c_cflag & CSTOPB) ? SUSBCR_SPASB_2StopBits : - SUSBCR_SPASB_1StopBit; + + if (c_cflag & CSTOPB) + val |= SUSBCR_SPASB_2StopBits; + else + val |= SUSBCR_SPASB_1StopBit; + if (c_cflag & PARENB) { if (c_cflag & PARODD) - urb_val |= SUSBCR_SPASB_OddParity; + val |= SUSBCR_SPASB_OddParity; else - urb_val |= SUSBCR_SPASB_EvenParity; - } else - urb_val |= SUSBCR_SPASB_NoParity; + val |= SUSBCR_SPASB_EvenParity; + } else { + val |= SUSBCR_SPASB_NoParity; + } + tty->termios.c_cflag &= ~CMSPAR; tty_encode_baud_rate(tty, speed, speed); - result = usb_control_msg(port->serial->dev, - usb_sndctrlpipe(port->serial->dev, 0), - SUSBCRequest_SetBaudRateParityAndStopBits, - USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, - urb_val, - 0, - NULL, - 0, - KOBIL_TIMEOUT - ); + result = kobil_ctrl_send(port, SUSBCRequest_SetBaudRateParityAndStopBits, val); if (result) { dev_err(&port->dev, "failed to update line settings: %d\n", result); @@ -544,17 +496,7 @@ static int kobil_ioctl(struct tty_struct *tty, switch (cmd) { case TCFLSH: - result = usb_control_msg(port->serial->dev, - usb_sndctrlpipe(port->serial->dev, 0), - SUSBCRequest_Misc, - USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, - SUSBCR_MSC_ResetAllQueues, - 0, - NULL, - 0, - KOBIL_TIMEOUT - ); - + result = kobil_ctrl_send(port, SUSBCRequest_Misc, SUSBCR_MSC_ResetAllQueues); dev_dbg(&port->dev, "%s - Send reset_all_queues (FLUSH) URB returns: %i\n", __func__, result); diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index e9400727ad36..4c0e5a3ab557 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -1433,17 +1433,31 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10b3, 0xff, 0xff, 0x60) }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c0, 0xff), /* Telit FE910C04 (rmnet) */ .driver_info = RSVD(0) | NCTRL(3) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c1, 0xff), /* Telit FE910C04 (RNDIS) */ + .driver_info = NCTRL(4) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c2, 0xff), /* Telit FE910C04 (MBIM) */ + .driver_info = NCTRL(4) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c3, 0xff), /* Telit FE910C04 (ECM) */ + .driver_info = NCTRL(4) }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c4, 0xff), /* Telit FE910C04 (rmnet) */ .driver_info = RSVD(0) | NCTRL(3) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c5, 0xff), /* Telit FE910C04 (RNDIS) */ + .driver_info = NCTRL(4) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c6, 0xff), /* Telit FE910C04 (MBIM) */ + .driver_info = NCTRL(4) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10c7, 0xff, 0xff, 0x30), /* Telit FE910C04 (ECM) */ + .driver_info = NCTRL(4) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10c7, 0xff, 0xff, 0x40) }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c8, 0xff), /* Telit FE910C04 (rmnet) */ .driver_info = RSVD(0) | NCTRL(2) | RSVD(3) | RSVD(4) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c9, 0xff), /* Telit FE910C04 (MBIM) */ + .driver_info = NCTRL(3) | RSVD(4) | RSVD(5) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10cb, 0xff), /* Telit FE910C04 (RNDIS) */ + .driver_info = NCTRL(3) | RSVD(4) | RSVD(5) }, { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10d0, 0xff, 0xff, 0x30), /* Telit FN990B (rmnet) */ .driver_info = NCTRL(5) }, { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10d0, 0xff, 0xff, 0x40) }, { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10d0, 0xff, 0xff, 0x60) }, - { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10c7, 0xff, 0xff, 0x30), /* Telit FE910C04 (ECM) */ - .driver_info = NCTRL(4) }, - { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10c7, 0xff, 0xff, 0x40) }, { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10d1, 0xff, 0xff, 0x30), /* Telit FN990B (MBIM) */ .driver_info = NCTRL(6) }, { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10d1, 0xff, 0xff, 0x40) }, @@ -2376,6 +2390,8 @@ static const struct usb_device_id option_ids[] = { .driver_info = RSVD(3) }, { USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe0f0, 0xff), /* Foxconn T99W373 MBIM */ .driver_info = RSVD(3) }, + { USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe123, 0xff), /* Foxconn T99W760 MBIM */ + .driver_info = RSVD(3) }, { USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe145, 0xff), /* Foxconn T99W651 RNDIS */ .driver_info = RSVD(5) | RSVD(6) }, { USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe15f, 0xff), /* Foxconn T99W709 */ diff --git a/drivers/usb/storage/protocol.c b/drivers/usb/storage/protocol.c index 9033e505db7f..0cff54ad90fa 100644 --- a/drivers/usb/storage/protocol.c +++ b/drivers/usb/storage/protocol.c @@ -139,8 +139,7 @@ unsigned int usb_stor_access_xfer_buf(unsigned char *buffer, return cnt; while (sg_miter_next(&miter) && cnt < buflen) { - unsigned int len = min_t(unsigned int, miter.length, - buflen - cnt); + unsigned int len = min(miter.length, buflen - cnt); if (dir == FROM_XFER_BUF) memcpy(buffer + cnt, miter.addr, len); diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 45b01df364f7..73b1981cb1d5 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -309,18 +309,18 @@ static void uas_stat_cmplt(struct urb *urb) int status = urb->status; bool success; + if (status) { + if (status != -ENOENT && status != -ECONNRESET && status != -ESHUTDOWN) + dev_err(&urb->dev->dev, "stat urb: status %d\n", status); + goto bail; + } + + idx = be16_to_cpup(&iu->tag) - 1; + spin_lock_irqsave(&devinfo->lock, flags); if (devinfo->resetting) goto out; - - if (status) { - if (status != -ENOENT && status != -ECONNRESET && status != -ESHUTDOWN) - dev_err(&urb->dev->dev, "stat urb: status %d\n", status); - goto out; - } - - idx = be16_to_cpup(&iu->tag) - 1; if (idx >= MAX_CMNDS || !devinfo->cmnd[idx]) { dev_err(&urb->dev->dev, "stat urb: no pending cmd for uas-tag %d\n", idx + 1); @@ -375,9 +375,8 @@ static void uas_stat_cmplt(struct urb *urb) default: uas_log_cmd_state(cmnd, "bogus IU", iu->iu_id); } -out: - usb_free_urb(urb); spin_unlock_irqrestore(&devinfo->lock, flags); + usb_free_urb(urb); /* Unlinking of data urbs must be done without holding the lock */ if (data_in_urb) { @@ -388,6 +387,12 @@ out: usb_unlink_urb(data_out_urb); usb_put_urb(data_out_urb); } + return; + +out: + spin_unlock_irqrestore(&devinfo->lock, flags); +bail: + usb_free_urb(urb); } static void uas_data_cmplt(struct urb *urb) @@ -429,8 +434,8 @@ static void uas_data_cmplt(struct urb *urb) } uas_try_complete(cmnd, __func__); out: - usb_free_urb(urb); spin_unlock_irqrestore(&devinfo->lock, flags); + usb_free_urb(urb); } static void uas_cmd_cmplt(struct urb *urb) @@ -1270,7 +1275,7 @@ static int __init uas_init(void) { int rv; - workqueue = alloc_workqueue("uas", WQ_MEM_RECLAIM, 0); + workqueue = alloc_workqueue("uas", WQ_MEM_RECLAIM | WQ_PERCPU, 0); if (!workqueue) return -ENOMEM; diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h index 1477e31d7763..b695f5ba9a40 100644 --- a/drivers/usb/storage/unusual_uas.h +++ b/drivers/usb/storage/unusual_uas.h @@ -98,7 +98,7 @@ UNUSUAL_DEV(0x125f, 0xa94a, 0x0160, 0x0160, US_FL_NO_ATA_1X), /* Reported-by: Benjamin Tissoires */ -UNUSUAL_DEV(0x13fd, 0x3940, 0x0000, 0x9999, +UNUSUAL_DEV(0x13fd, 0x3940, 0x0309, 0x0309, "Initio Corporation", "INIC-3069", USB_SC_DEVICE, USB_PR_DEVICE, NULL, diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c index 1dcb77faf85d..8d111ad3b71b 100644 --- a/drivers/usb/typec/altmodes/displayport.c +++ b/drivers/usb/typec/altmodes/displayport.c @@ -758,7 +758,9 @@ int dp_altmode_probe(struct typec_altmode *alt) struct fwnode_handle *fwnode; struct dp_altmode *dp; - /* FIXME: Port can only be DFP_U. */ + /* Port can only be DFP_U. */ + if (typec_altmode_get_data_role(alt) != TYPEC_HOST) + return -EPROTO; /* Make sure we have compatible pin configurations */ if (!(DP_CAP_PIN_ASSIGN_DFP_D(port->vdo) & diff --git a/drivers/usb/typec/anx7411.c b/drivers/usb/typec/anx7411.c index 0ae0a5ee3fae..2e8ae1d2faf9 100644 --- a/drivers/usb/typec/anx7411.c +++ b/drivers/usb/typec/anx7411.c @@ -1516,8 +1516,7 @@ static int anx7411_i2c_probe(struct i2c_client *client) INIT_WORK(&plat->work, anx7411_work_func); plat->workqueue = alloc_workqueue("anx7411_work", - WQ_FREEZABLE | - WQ_MEM_RECLAIM, + WQ_FREEZABLE | WQ_MEM_RECLAIM | WQ_PERCPU, 1); if (!plat->workqueue) { dev_err(dev, "fail to create work queue\n"); diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 67a533e35150..9b2647cb199b 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -2120,6 +2120,19 @@ void typec_set_data_role(struct typec_port *port, enum typec_data_role role) } EXPORT_SYMBOL_GPL(typec_set_data_role); +/** + * typec_get_data_role - Get port data role + * @port: The USB Type-C Port to query + * + * This routine is used by the altmode drivers to determine if the port is the + * DFP before issuing Enter Mode + */ +enum typec_data_role typec_get_data_role(struct typec_port *port) +{ + return port->data_role; +} +EXPORT_SYMBOL_GPL(typec_get_data_role); + /** * typec_set_pwr_role - Report power role change * @port: The USB Type-C Port where the role was changed diff --git a/drivers/usb/typec/hd3ss3220.c b/drivers/usb/typec/hd3ss3220.c index 3ecc688dda82..3876f4faead6 100644 --- a/drivers/usb/typec/hd3ss3220.c +++ b/drivers/usb/typec/hd3ss3220.c @@ -15,6 +15,9 @@ #include #include #include +#include +#include +#include #define HD3SS3220_REG_CN_STAT 0x08 #define HD3SS3220_REG_CN_STAT_CTRL 0x09 @@ -54,6 +57,11 @@ struct hd3ss3220 { struct delayed_work output_poll_work; enum usb_role role_state; bool poll; + + struct gpio_desc *id_gpiod; + int id_irq; + + struct regulator *vbus; }; static int hd3ss3220_set_power_opmode(struct hd3ss3220 *hd3ss3220, int power_opmode) @@ -319,13 +327,33 @@ static const struct regmap_config config = { .max_register = 0x0A, }; +static irqreturn_t hd3ss3220_id_isr(int irq, void *dev_id) +{ + struct hd3ss3220 *hd3ss3220 = dev_id; + int ret; + int id; + + id = gpiod_get_value_cansleep(hd3ss3220->id_gpiod); + if (!id) + ret = regulator_enable(hd3ss3220->vbus); + else + ret = regulator_disable(hd3ss3220->vbus); + + if (ret) + dev_err(hd3ss3220->dev, + "vbus regulator %s failed: %d\n", id ? "disable" : "enable", ret); + + return IRQ_HANDLED; +} + static int hd3ss3220_probe(struct i2c_client *client) { struct typec_capability typec_cap = { }; - struct hd3ss3220 *hd3ss3220; struct fwnode_handle *connector, *ep; - int ret; + struct hd3ss3220 *hd3ss3220; + struct regulator *vbus; unsigned int data; + int ret; hd3ss3220 = devm_kzalloc(&client->dev, sizeof(struct hd3ss3220), GFP_KERNEL); @@ -359,6 +387,49 @@ static int hd3ss3220_probe(struct i2c_client *client) goto err_put_fwnode; } + vbus = devm_of_regulator_get_optional(hd3ss3220->dev, + to_of_node(connector), + "vbus"); + if (IS_ERR(vbus) && vbus != ERR_PTR(-ENODEV)) { + ret = PTR_ERR(vbus); + dev_err(hd3ss3220->dev, "failed to get vbus: %d", ret); + goto err_put_fwnode; + } + + hd3ss3220->vbus = (vbus == ERR_PTR(-ENODEV) ? NULL : vbus); + + if (hd3ss3220->vbus) { + hd3ss3220->id_gpiod = devm_gpiod_get_optional(hd3ss3220->dev, + "id", + GPIOD_IN); + if (IS_ERR(hd3ss3220->id_gpiod)) { + ret = PTR_ERR(hd3ss3220->id_gpiod); + goto err_put_fwnode; + } + } + + if (hd3ss3220->id_gpiod) { + hd3ss3220->id_irq = gpiod_to_irq(hd3ss3220->id_gpiod); + if (hd3ss3220->id_irq < 0) { + ret = hd3ss3220->id_irq; + dev_err(hd3ss3220->dev, + "failed to get ID gpio: %d\n", + hd3ss3220->id_irq); + goto err_put_fwnode; + } + + ret = devm_request_threaded_irq(hd3ss3220->dev, + hd3ss3220->id_irq, NULL, + hd3ss3220_id_isr, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + dev_name(hd3ss3220->dev), hd3ss3220); + if (ret < 0) { + dev_err(hd3ss3220->dev, "failed to get ID irq: %d\n", ret); + goto err_put_fwnode; + } + } + typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE; typec_cap.driver_data = hd3ss3220; typec_cap.type = TYPEC_PORT_DRP; diff --git a/drivers/usb/typec/mux/ps883x.c b/drivers/usb/typec/mux/ps883x.c index ad59babf7cce..5f2879749769 100644 --- a/drivers/usb/typec/mux/ps883x.c +++ b/drivers/usb/typec/mux/ps883x.c @@ -14,15 +14,18 @@ #include #include #include +#include #include #include #include #include +#include #define REG_USB_PORT_CONN_STATUS_0 0x00 #define CONN_STATUS_0_CONNECTION_PRESENT BIT(0) #define CONN_STATUS_0_ORIENTATION_REVERSED BIT(1) +#define CONN_STATUS_0_ACTIVE_CABLE BIT(2) #define CONN_STATUS_0_USB_3_1_CONNECTED BIT(5) #define REG_USB_PORT_CONN_STATUS_1 0x01 @@ -34,6 +37,10 @@ #define REG_USB_PORT_CONN_STATUS_2 0x02 +#define CONN_STATUS_2_TBT_CONNECTED BIT(0) +#define CONN_STATUS_2_TBT_UNIDIR_LSRX_ACT_LT BIT(4) +#define CONN_STATUS_2_USB4_CONNECTED BIT(7) + struct ps883x_retimer { struct i2c_client *client; struct gpio_desc *reset_gpio; @@ -54,8 +61,9 @@ struct ps883x_retimer { struct mutex lock; /* protect non-concurrent retimer & switch */ enum typec_orientation orientation; - unsigned long mode; - unsigned int svid; + u8 cfg0; + u8 cfg1; + u8 cfg2; }; static int ps883x_configure(struct ps883x_retimer *retimer, int cfg0, @@ -64,6 +72,9 @@ static int ps883x_configure(struct ps883x_retimer *retimer, int cfg0, struct device *dev = &retimer->client->dev; int ret; + if (retimer->cfg0 == cfg0 && retimer->cfg1 == cfg1 && retimer->cfg2 == cfg2) + return 0; + ret = regmap_write(retimer->regmap, REG_USB_PORT_CONN_STATUS_0, cfg0); if (ret) { dev_err(dev, "failed to write conn_status_0: %d\n", ret); @@ -82,53 +93,82 @@ static int ps883x_configure(struct ps883x_retimer *retimer, int cfg0, return ret; } + retimer->cfg0 = cfg0; + retimer->cfg1 = cfg1; + retimer->cfg2 = cfg2; + return 0; } -static int ps883x_set(struct ps883x_retimer *retimer) +static int ps883x_set(struct ps883x_retimer *retimer, struct typec_retimer_state *state) { + struct typec_thunderbolt_data *tb_data; + const struct enter_usb_data *eudo_data; int cfg0 = CONN_STATUS_0_CONNECTION_PRESENT; int cfg1 = 0x00; int cfg2 = 0x00; - if (retimer->orientation == TYPEC_ORIENTATION_NONE || - retimer->mode == TYPEC_STATE_SAFE) { - return ps883x_configure(retimer, cfg0, cfg1, cfg2); - } - - if (retimer->mode != TYPEC_STATE_USB && retimer->svid != USB_TYPEC_DP_SID) - return -EINVAL; - if (retimer->orientation == TYPEC_ORIENTATION_REVERSE) cfg0 |= CONN_STATUS_0_ORIENTATION_REVERSED; - switch (retimer->mode) { - case TYPEC_STATE_USB: - cfg0 |= CONN_STATUS_0_USB_3_1_CONNECTED; - break; + if (state->alt) { + switch (state->alt->svid) { + case USB_TYPEC_DP_SID: + cfg1 |= CONN_STATUS_1_DP_CONNECTED | + CONN_STATUS_1_DP_HPD_LEVEL; - case TYPEC_DP_STATE_C: - cfg1 = CONN_STATUS_1_DP_CONNECTED | - CONN_STATUS_1_DP_SINK_REQUESTED | - CONN_STATUS_1_DP_PIN_ASSIGNMENT_C_D | - CONN_STATUS_1_DP_HPD_LEVEL; - break; + switch (state->mode) { + case TYPEC_DP_STATE_C: + cfg1 |= CONN_STATUS_1_DP_SINK_REQUESTED | + CONN_STATUS_1_DP_PIN_ASSIGNMENT_C_D; + fallthrough; + case TYPEC_DP_STATE_D: + cfg1 |= CONN_STATUS_0_USB_3_1_CONNECTED; + break; + default: /* MODE_E */ + break; + } + break; + case USB_TYPEC_TBT_SID: + tb_data = state->data; - case TYPEC_DP_STATE_D: - cfg0 |= CONN_STATUS_0_USB_3_1_CONNECTED; - cfg1 = CONN_STATUS_1_DP_CONNECTED | - CONN_STATUS_1_DP_SINK_REQUESTED | - CONN_STATUS_1_DP_PIN_ASSIGNMENT_C_D | - CONN_STATUS_1_DP_HPD_LEVEL; - break; + /* Unconditional */ + cfg2 |= CONN_STATUS_2_TBT_CONNECTED; - case TYPEC_DP_STATE_E: - cfg1 = CONN_STATUS_1_DP_CONNECTED | - CONN_STATUS_1_DP_HPD_LEVEL; - break; + if (tb_data->cable_mode & TBT_CABLE_ACTIVE_PASSIVE) + cfg0 |= CONN_STATUS_0_ACTIVE_CABLE; - default: - return -EOPNOTSUPP; + if (tb_data->enter_vdo & TBT_ENTER_MODE_UNI_DIR_LSRX) + cfg2 |= CONN_STATUS_2_TBT_UNIDIR_LSRX_ACT_LT; + break; + default: + dev_err(&retimer->client->dev, "Got unsupported SID: 0x%x\n", + state->alt->svid); + return -EOPNOTSUPP; + } + } else { + switch (state->mode) { + case TYPEC_STATE_SAFE: + /* USB2 pins don't even go through this chip */ + case TYPEC_MODE_USB2: + break; + case TYPEC_STATE_USB: + case TYPEC_MODE_USB3: + cfg0 |= CONN_STATUS_0_USB_3_1_CONNECTED; + break; + case TYPEC_MODE_USB4: + eudo_data = state->data; + + cfg2 |= CONN_STATUS_2_USB4_CONNECTED; + + if (FIELD_GET(EUDO_CABLE_TYPE_MASK, eudo_data->eudo) != EUDO_CABLE_TYPE_PASSIVE) + cfg0 |= CONN_STATUS_0_ACTIVE_CABLE; + break; + default: + dev_err(&retimer->client->dev, "Got unsupported mode: %lu\n", + state->mode); + return -EOPNOTSUPP; + } } return ps883x_configure(retimer, cfg0, cfg1, cfg2); @@ -149,7 +189,11 @@ static int ps883x_sw_set(struct typec_switch_dev *sw, if (retimer->orientation != orientation) { retimer->orientation = orientation; - ret = ps883x_set(retimer); + ret = regmap_assign_bits(retimer->regmap, REG_USB_PORT_CONN_STATUS_0, + CONN_STATUS_0_ORIENTATION_REVERSED, + orientation == TYPEC_ORIENTATION_REVERSE); + if (ret) + dev_err(&retimer->client->dev, "failed to set orientation: %d\n", ret); } mutex_unlock(&retimer->lock); @@ -165,18 +209,7 @@ static int ps883x_retimer_set(struct typec_retimer *rtmr, int ret = 0; mutex_lock(&retimer->lock); - - if (state->mode != retimer->mode) { - retimer->mode = state->mode; - - if (state->alt) - retimer->svid = state->alt->svid; - else - retimer->svid = 0; - - ret = ps883x_set(retimer); - } - + ret = ps883x_set(retimer, state); mutex_unlock(&retimer->lock); if (ret) diff --git a/drivers/usb/typec/pd.c b/drivers/usb/typec/pd.c index d78c04a421bc..67f20b5ffdf4 100644 --- a/drivers/usb/typec/pd.c +++ b/drivers/usb/typec/pd.c @@ -359,6 +359,84 @@ static const struct device_type sink_pps_type = { .groups = sink_pps_groups, }; +/* -------------------------------------------------------------------------- */ +/* Standard Power Range (SPR) Adjustable Voltage Supply (AVS) */ + +static ssize_t +spr_avs_9v_to_15v_max_current_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%umA\n", + pdo_spr_avs_apdo_9v_to_15v_max_current_ma(to_pdo(dev)->pdo)); +} + +static ssize_t +spr_avs_15v_to_20v_max_current_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%umA\n", + pdo_spr_avs_apdo_15v_to_20v_max_current_ma(to_pdo(dev)->pdo)); +} + +static ssize_t +spr_avs_src_peak_current_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%u\n", + pdo_spr_avs_apdo_src_peak_current(to_pdo(dev)->pdo)); +} + +static struct device_attribute spr_avs_9v_to_15v_max_current_attr = { + .attr = { + .name = "maximum_current_9V_to_15V", + .mode = 0444, + }, + .show = spr_avs_9v_to_15v_max_current_show, +}; + +static struct device_attribute spr_avs_15v_to_20v_max_current_attr = { + .attr = { + .name = "maximum_current_15V_to_20V", + .mode = 0444, + }, + .show = spr_avs_15v_to_20v_max_current_show, +}; + +static struct device_attribute spr_avs_src_peak_current_attr = { + .attr = { + .name = "peak_current", + .mode = 0444, + }, + .show = spr_avs_src_peak_current_show, +}; + +static struct attribute *source_spr_avs_attrs[] = { + &spr_avs_9v_to_15v_max_current_attr.attr, + &spr_avs_15v_to_20v_max_current_attr.attr, + &spr_avs_src_peak_current_attr.attr, + NULL +}; +ATTRIBUTE_GROUPS(source_spr_avs); + +static const struct device_type source_spr_avs_type = { + .name = "pdo", + .release = pdo_release, + .groups = source_spr_avs_groups, +}; + +static struct attribute *sink_spr_avs_attrs[] = { + &spr_avs_9v_to_15v_max_current_attr.attr, + &spr_avs_15v_to_20v_max_current_attr.attr, + NULL +}; +ATTRIBUTE_GROUPS(sink_spr_avs); + +static const struct device_type sink_spr_avs_type = { + .name = "pdo", + .release = pdo_release, + .groups = sink_spr_avs_groups, +}; + /* -------------------------------------------------------------------------- */ static const char * const supply_name[] = { @@ -368,7 +446,8 @@ static const char * const supply_name[] = { }; static const char * const apdo_supply_name[] = { - [APDO_TYPE_PPS] = "programmable_supply", + [APDO_TYPE_PPS] = "programmable_supply", + [APDO_TYPE_SPR_AVS] = "spr_adjustable_voltage_supply", }; static const struct device_type *source_type[] = { @@ -378,7 +457,8 @@ static const struct device_type *source_type[] = { }; static const struct device_type *source_apdo_type[] = { - [APDO_TYPE_PPS] = &source_pps_type, + [APDO_TYPE_PPS] = &source_pps_type, + [APDO_TYPE_SPR_AVS] = &source_spr_avs_type, }; static const struct device_type *sink_type[] = { @@ -388,7 +468,8 @@ static const struct device_type *sink_type[] = { }; static const struct device_type *sink_apdo_type[] = { - [APDO_TYPE_PPS] = &sink_pps_type, + [APDO_TYPE_PPS] = &sink_pps_type, + [APDO_TYPE_SPR_AVS] = &sink_spr_avs_type, }; /* REVISIT: Export when EPR_*_Capabilities need to be supported. */ @@ -407,8 +488,12 @@ static int add_pdo(struct usb_power_delivery_capabilities *cap, u32 pdo, int pos p->object_position = position; if (pdo_type(pdo) == PDO_TYPE_APDO) { - /* FIXME: Only PPS supported for now! Skipping others. */ - if (pdo_apdo_type(pdo) > APDO_TYPE_PPS) { + /* + * FIXME: Only PPS, SPR_AVS supported for now! + * Skipping others. + */ + if (pdo_apdo_type(pdo) != APDO_TYPE_PPS && + pdo_apdo_type(pdo) != APDO_TYPE_SPR_AVS) { dev_warn(&cap->dev, "Unknown APDO type. PDO 0x%08x\n", pdo); kfree(p); return 0; diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index cc78770509db..4ca2746ce16b 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -823,10 +823,23 @@ static void tcpm_log_source_caps(struct tcpm_port *port) case PDO_TYPE_APDO: if (pdo_apdo_type(pdo) == APDO_TYPE_PPS) scnprintf(msg, sizeof(msg), - "%u-%u mV, %u mA", + "PPS %u-%u mV, %u mA", pdo_pps_apdo_min_voltage(pdo), pdo_pps_apdo_max_voltage(pdo), pdo_pps_apdo_max_current(pdo)); + else if (pdo_apdo_type(pdo) == APDO_TYPE_EPR_AVS) + scnprintf(msg, sizeof(msg), + "EPR AVS %u-%u mV %u W peak_current: %u", + pdo_epr_avs_apdo_min_voltage_mv(pdo), + pdo_epr_avs_apdo_max_voltage_mv(pdo), + pdo_epr_avs_apdo_pdp_w(pdo), + pdo_epr_avs_apdo_src_peak_current(pdo)); + else if (pdo_apdo_type(pdo) == APDO_TYPE_SPR_AVS) + scnprintf(msg, sizeof(msg), + "SPR AVS 9-15 V: %u mA 15-20 V: %u mA peak_current: %u", + pdo_spr_avs_apdo_9v_to_15v_max_current_ma(pdo), + pdo_spr_avs_apdo_15v_to_20v_max_current_ma(pdo), + pdo_spr_avs_apdo_src_peak_current(pdo)); else strcpy(msg, "undefined APDO"); break; diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c index 2b1049c9a6f3..e2b26af2b84a 100644 --- a/drivers/usb/typec/tipd/core.c +++ b/drivers/usb/typec/tipd/core.c @@ -577,30 +577,36 @@ static bool cd321x_read_data_status(struct tps6598x *tps) int ret; ret = tps6598x_read_data_status(tps); - if (ret < 0) + if (!ret) return false; if (tps->data_status & TPS_DATA_STATUS_DP_CONNECTION) { ret = tps6598x_block_read(tps, TPS_REG_DP_SID_STATUS, &cd321x->dp_sid_status, sizeof(cd321x->dp_sid_status)); - if (ret) + if (ret) { dev_err(tps->dev, "Failed to read DP SID Status: %d\n", ret); + return false; + } } if (tps->data_status & TPS_DATA_STATUS_TBT_CONNECTION) { ret = tps6598x_block_read(tps, TPS_REG_INTEL_VID_STATUS, &cd321x->intel_vid_status, sizeof(cd321x->intel_vid_status)); - if (ret) + if (ret) { dev_err(tps->dev, "Failed to read Intel VID Status: %d\n", ret); + return false; + } } if (tps->data_status & CD321X_DATA_STATUS_USB4_CONNECTION) { ret = tps6598x_block_read(tps, TPS_REG_USB4_STATUS, &cd321x->usb4_status, sizeof(cd321x->usb4_status)); - if (ret) + if (ret) { dev_err(tps->dev, "Failed to read USB4 Status: %d\n", ret); + return false; + } } return true; @@ -1695,6 +1701,7 @@ tps25750_register_port(struct tps6598x *tps, struct fwnode_handle *fwnode) typec_cap.data = ret; typec_cap.revision = USB_TYPEC_REV_1_3; typec_cap.pd_revision = 0x300; + typec_cap.orientation_aware = true; typec_cap.driver_data = tps; typec_cap.ops = &tps6598x_ops; typec_cap.fwnode = fwnode; diff --git a/drivers/usb/typec/ucsi/cros_ec_ucsi.c b/drivers/usb/typec/ucsi/cros_ec_ucsi.c index eed2a7d0ebc6..d753f2188e25 100644 --- a/drivers/usb/typec/ucsi/cros_ec_ucsi.c +++ b/drivers/usb/typec/ucsi/cros_ec_ucsi.c @@ -105,13 +105,12 @@ static int cros_ucsi_async_control(struct ucsi *ucsi, u64 cmd) return 0; } -static int cros_ucsi_sync_control(struct ucsi *ucsi, u64 cmd, u32 *cci, - void *data, size_t size) +static int cros_ucsi_sync_control(struct ucsi *ucsi, u64 cmd, u32 *cci) { struct cros_ucsi_data *udata = ucsi_get_drvdata(ucsi); int ret; - ret = ucsi_sync_control_common(ucsi, cmd, cci, data, size); + ret = ucsi_sync_control_common(ucsi, cmd, cci); switch (ret) { case -EBUSY: /* EC may return -EBUSY if CCI.busy is set. diff --git a/drivers/usb/typec/ucsi/debugfs.c b/drivers/usb/typec/ucsi/debugfs.c index f73f2b54554e..174f4d53b777 100644 --- a/drivers/usb/typec/ucsi/debugfs.c +++ b/drivers/usb/typec/ucsi/debugfs.c @@ -35,8 +35,11 @@ static int ucsi_cmd(void *data, u64 val) case UCSI_SET_SINK_PATH: case UCSI_SET_NEW_CAM: case UCSI_SET_USB: + case UCSI_SET_POWER_LEVEL: case UCSI_READ_POWER_LEVEL: - ret = ucsi_send_command(ucsi, val, NULL, 0); + case UCSI_SET_PDOS: + ucsi->message_in_size = 0; + ret = ucsi_send_command(ucsi, val); break; case UCSI_GET_CAPABILITY: case UCSI_GET_CONNECTOR_CAPABILITY: @@ -51,9 +54,9 @@ static int ucsi_cmd(void *data, u64 val) case UCSI_GET_ATTENTION_VDO: case UCSI_GET_CAM_CS: case UCSI_GET_LPM_PPM_INFO: - ret = ucsi_send_command(ucsi, val, - &ucsi->debugfs->response, - sizeof(ucsi->debugfs->response)); + ucsi->message_in_size = sizeof(ucsi->debugfs->response); + ret = ucsi_send_command(ucsi, val); + memcpy(&ucsi->debugfs->response, ucsi->message_in, sizeof(ucsi->debugfs->response)); break; default: ret = -EOPNOTSUPP; @@ -108,6 +111,30 @@ static int ucsi_vbus_volt_show(struct seq_file *m, void *v) } DEFINE_SHOW_ATTRIBUTE(ucsi_vbus_volt); +static ssize_t ucsi_message_out_write(struct file *file, + const char __user *data, size_t count, loff_t *ppos) +{ + struct ucsi *ucsi = file->private_data; + int ret; + + char *buf __free(kfree) = memdup_user_nul(data, count); + if (IS_ERR(buf)) + return PTR_ERR(buf); + + ucsi->message_out_size = min(count / 2, UCSI_MAX_MESSAGE_OUT_LENGTH); + ret = hex2bin(ucsi->message_out, buf, ucsi->message_out_size); + if (ret) + return ret; + + return count; +} + +static const struct file_operations ucsi_message_out_fops = { + .open = simple_open, + .write = ucsi_message_out_write, + .llseek = generic_file_llseek, +}; + void ucsi_debugfs_register(struct ucsi *ucsi) { ucsi->debugfs = kzalloc(sizeof(*ucsi->debugfs), GFP_KERNEL); @@ -120,6 +147,8 @@ void ucsi_debugfs_register(struct ucsi *ucsi) debugfs_create_file("peak_current", 0400, ucsi->debugfs->dentry, ucsi, &ucsi_peak_curr_fops); debugfs_create_file("avg_current", 0400, ucsi->debugfs->dentry, ucsi, &ucsi_avg_curr_fops); debugfs_create_file("vbus_voltage", 0400, ucsi->debugfs->dentry, ucsi, &ucsi_vbus_volt_fops); + debugfs_create_file("message_out", 0200, ucsi->debugfs->dentry, ucsi, + &ucsi_message_out_fops); } void ucsi_debugfs_unregister(struct ucsi *ucsi) diff --git a/drivers/usb/typec/ucsi/displayport.c b/drivers/usb/typec/ucsi/displayport.c index 8aae80b457d7..a09b4900ec76 100644 --- a/drivers/usb/typec/ucsi/displayport.c +++ b/drivers/usb/typec/ucsi/displayport.c @@ -67,11 +67,14 @@ static int ucsi_displayport_enter(struct typec_altmode *alt, u32 *vdo) } command = UCSI_GET_CURRENT_CAM | UCSI_CONNECTOR_NUMBER(dp->con->num); - ret = ucsi_send_command(ucsi, command, &cur, sizeof(cur)); + ucsi->message_in_size = sizeof(cur); + ret = ucsi_send_command(ucsi, command); if (ret < 0) { if (ucsi->version > 0x0100) goto err_unlock; cur = 0xff; + } else { + memcpy(&cur, ucsi->message_in, ucsi->message_in_size); } if (cur != 0xff) { @@ -126,7 +129,8 @@ static int ucsi_displayport_exit(struct typec_altmode *alt) } command = UCSI_CMD_SET_NEW_CAM(dp->con->num, 0, dp->offset, 0); - ret = ucsi_send_command(dp->con->ucsi, command, NULL, 0); + dp->con->ucsi->message_in_size = 0; + ret = ucsi_send_command(dp->con->ucsi, command); if (ret < 0) goto out_unlock; @@ -193,7 +197,8 @@ static int ucsi_displayport_configure(struct ucsi_dp *dp) command = UCSI_CMD_SET_NEW_CAM(dp->con->num, 1, dp->offset, pins); - return ucsi_send_command(dp->con->ucsi, command, NULL, 0); + dp->con->ucsi->message_in_size = 0; + return ucsi_send_command(dp->con->ucsi, command); } static int ucsi_displayport_vdm(struct typec_altmode *alt, diff --git a/drivers/usb/typec/ucsi/psy.c b/drivers/usb/typec/ucsi/psy.c index 8ae900c8c132..3abe9370ffaa 100644 --- a/drivers/usb/typec/ucsi/psy.c +++ b/drivers/usb/typec/ucsi/psy.c @@ -29,6 +29,7 @@ static enum power_supply_property ucsi_psy_props[] = { POWER_SUPPLY_PROP_CURRENT_MAX, POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_SCOPE, + POWER_SUPPLY_PROP_STATUS, }; static int ucsi_psy_get_scope(struct ucsi_connector *con, @@ -51,6 +52,29 @@ static int ucsi_psy_get_scope(struct ucsi_connector *con, return 0; } +static int ucsi_psy_get_status(struct ucsi_connector *con, + union power_supply_propval *val) +{ + bool is_sink = UCSI_CONSTAT(con, PWR_DIR) == TYPEC_SINK; + bool sink_path_enabled = true; + + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + + if (con->ucsi->version >= UCSI_VERSION_2_0) + sink_path_enabled = + UCSI_CONSTAT(con, SINK_PATH_STATUS_V2_0) == + UCSI_CONSTAT_SINK_PATH_ENABLED; + + if (UCSI_CONSTAT(con, CONNECTED)) { + if (is_sink && sink_path_enabled) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else if (!is_sink) + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + } + + return 0; +} + static int ucsi_psy_get_online(struct ucsi_connector *con, union power_supply_propval *val) { @@ -250,6 +274,8 @@ static int ucsi_psy_get_prop(struct power_supply *psy, return ucsi_psy_get_current_now(con, val); case POWER_SUPPLY_PROP_SCOPE: return ucsi_psy_get_scope(con, val); + case POWER_SUPPLY_PROP_STATUS: + return ucsi_psy_get_status(con, val); default: return -EINVAL; } diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 3f568f790f39..9b3df776137a 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -55,8 +55,7 @@ void ucsi_notify_common(struct ucsi *ucsi, u32 cci) } EXPORT_SYMBOL_GPL(ucsi_notify_common); -int ucsi_sync_control_common(struct ucsi *ucsi, u64 command, u32 *cci, - void *data, size_t size) +int ucsi_sync_control_common(struct ucsi *ucsi, u64 command, u32 *cci) { bool ack = UCSI_COMMAND(command) == UCSI_ACK_CC_CI; int ret; @@ -68,6 +67,20 @@ int ucsi_sync_control_common(struct ucsi *ucsi, u64 command, u32 *cci, reinit_completion(&ucsi->complete); + if (ucsi->message_out_size > 0) { + if (!ucsi->ops->write_message_out) { + ucsi->message_out_size = 0; + ret = -EOPNOTSUPP; + goto out_clear_bit; + } + + ret = ucsi->ops->write_message_out(ucsi, ucsi->message_out, + ucsi->message_out_size); + ucsi->message_out_size = 0; + if (ret) + goto out_clear_bit; + } + ret = ucsi->ops->async_control(ucsi, command); if (ret) goto out_clear_bit; @@ -84,9 +97,10 @@ out_clear_bit: if (!ret && cci) ret = ucsi->ops->read_cci(ucsi, cci); - if (!ret && data && + if (!ret && ucsi->message_in_size > 0 && (*cci & UCSI_CCI_COMMAND_COMPLETE)) - ret = ucsi->ops->read_message_in(ucsi, data, size); + ret = ucsi->ops->read_message_in(ucsi, ucsi->message_in, + ucsi->message_in_size); return ret; } @@ -103,23 +117,25 @@ static int ucsi_acknowledge(struct ucsi *ucsi, bool conn_ack) ctrl |= UCSI_ACK_CONNECTOR_CHANGE; } - return ucsi->ops->sync_control(ucsi, ctrl, NULL, NULL, 0); + ucsi->message_in_size = 0; + return ucsi->ops->sync_control(ucsi, ctrl, NULL); } -static int ucsi_run_command(struct ucsi *ucsi, u64 command, u32 *cci, - void *data, size_t size, bool conn_ack) +static int ucsi_run_command(struct ucsi *ucsi, u64 command, u32 *cci, bool conn_ack) { int ret, err; *cci = 0; - if (size > UCSI_MAX_DATA_LENGTH(ucsi)) + if (ucsi->message_in_size > UCSI_MAX_DATA_LENGTH(ucsi)) return -EINVAL; - ret = ucsi->ops->sync_control(ucsi, command, cci, data, size); + ret = ucsi->ops->sync_control(ucsi, command, cci); - if (*cci & UCSI_CCI_BUSY) - return ucsi_run_command(ucsi, UCSI_CANCEL, cci, NULL, 0, false) ?: -EBUSY; + if (*cci & UCSI_CCI_BUSY) { + ucsi->message_in_size = 0; + return ucsi_run_command(ucsi, UCSI_CANCEL, cci, false) ?: -EBUSY; + } if (ret) return ret; @@ -151,10 +167,13 @@ static int ucsi_read_error(struct ucsi *ucsi, u8 connector_num) int ret; command = UCSI_GET_ERROR_STATUS | UCSI_CONNECTOR_NUMBER(connector_num); - ret = ucsi_run_command(ucsi, command, &cci, &error, sizeof(error), false); + ucsi->message_in_size = sizeof(error); + ret = ucsi_run_command(ucsi, command, &cci, false); if (ret < 0) return ret; + memcpy(&error, ucsi->message_in, sizeof(error)); + switch (error) { case UCSI_ERROR_INCOMPATIBLE_PARTNER: return -EOPNOTSUPP; @@ -200,8 +219,7 @@ static int ucsi_read_error(struct ucsi *ucsi, u8 connector_num) return -EIO; } -static int ucsi_send_command_common(struct ucsi *ucsi, u64 cmd, - void *data, size_t size, bool conn_ack) +static int ucsi_send_command_common(struct ucsi *ucsi, u64 cmd, bool conn_ack) { u8 connector_num; u32 cci; @@ -229,7 +247,7 @@ static int ucsi_send_command_common(struct ucsi *ucsi, u64 cmd, mutex_lock(&ucsi->ppm_lock); - ret = ucsi_run_command(ucsi, cmd, &cci, data, size, conn_ack); + ret = ucsi_run_command(ucsi, cmd, &cci, conn_ack); if (cci & UCSI_CCI_ERROR) ret = ucsi_read_error(ucsi, connector_num); @@ -238,10 +256,9 @@ static int ucsi_send_command_common(struct ucsi *ucsi, u64 cmd, return ret; } -int ucsi_send_command(struct ucsi *ucsi, u64 command, - void *data, size_t size) +int ucsi_send_command(struct ucsi *ucsi, u64 command) { - return ucsi_send_command_common(ucsi, command, data, size, false); + return ucsi_send_command_common(ucsi, command, false); } EXPORT_SYMBOL_GPL(ucsi_send_command); @@ -319,7 +336,8 @@ void ucsi_altmode_update_active(struct ucsi_connector *con) int i; command = UCSI_GET_CURRENT_CAM | UCSI_CONNECTOR_NUMBER(con->num); - ret = ucsi_send_command(con->ucsi, command, &cur, sizeof(cur)); + con->ucsi->message_in_size = sizeof(cur); + ret = ucsi_send_command(con->ucsi, command); if (ret < 0) { if (con->ucsi->version > 0x0100) { dev_err(con->ucsi->dev, @@ -327,6 +345,8 @@ void ucsi_altmode_update_active(struct ucsi_connector *con) return; } cur = 0xff; + } else { + memcpy(&cur, con->ucsi->message_in, sizeof(cur)); } if (cur < UCSI_MAX_ALTMODES) @@ -510,7 +530,8 @@ ucsi_register_altmodes_nvidia(struct ucsi_connector *con, u8 recipient) command |= UCSI_GET_ALTMODE_RECIPIENT(recipient); command |= UCSI_GET_ALTMODE_CONNECTOR_NUMBER(con->num); command |= UCSI_GET_ALTMODE_OFFSET(i); - len = ucsi_send_command(con->ucsi, command, &alt, sizeof(alt)); + ucsi->message_in_size = sizeof(alt); + len = ucsi_send_command(con->ucsi, command); /* * We are collecting all altmodes first and then registering. * Some type-C device will return zero length data beyond last @@ -519,6 +540,8 @@ ucsi_register_altmodes_nvidia(struct ucsi_connector *con, u8 recipient) if (len < 0) return len; + memcpy(&alt, ucsi->message_in, sizeof(alt)); + /* We got all altmodes, now break out and register them */ if (!len || !alt.svid) break; @@ -586,12 +609,15 @@ static int ucsi_register_altmodes(struct ucsi_connector *con, u8 recipient) command |= UCSI_GET_ALTMODE_RECIPIENT(recipient); command |= UCSI_GET_ALTMODE_CONNECTOR_NUMBER(con->num); command |= UCSI_GET_ALTMODE_OFFSET(i); - len = ucsi_send_command(con->ucsi, command, alt, sizeof(alt)); + con->ucsi->message_in_size = sizeof(alt); + len = ucsi_send_command(con->ucsi, command); if (len == -EBUSY) continue; if (len <= 0) return len; + memcpy(&alt, con->ucsi->message_in, sizeof(alt)); + /* * This code is requesting one alt mode at a time, but some PPMs * may still return two. If that happens both alt modes need be @@ -659,7 +685,9 @@ static int ucsi_get_connector_status(struct ucsi_connector *con, bool conn_ack) UCSI_MAX_DATA_LENGTH(con->ucsi)); int ret; - ret = ucsi_send_command_common(con->ucsi, command, &con->status, size, conn_ack); + con->ucsi->message_in_size = size; + ret = ucsi_send_command_common(con->ucsi, command, conn_ack); + memcpy(&con->status, con->ucsi->message_in, size); return ret < 0 ? ret : 0; } @@ -682,8 +710,9 @@ static int ucsi_read_pdos(struct ucsi_connector *con, command |= UCSI_GET_PDOS_PDO_OFFSET(offset); command |= UCSI_GET_PDOS_NUM_PDOS(num_pdos - 1); command |= is_source(role) ? UCSI_GET_PDOS_SRC_PDOS : 0; - ret = ucsi_send_command(ucsi, command, pdos + offset, - num_pdos * sizeof(u32)); + ucsi->message_in_size = num_pdos * sizeof(u32); + ret = ucsi_send_command(ucsi, command); + memcpy(pdos + offset, ucsi->message_in, num_pdos * sizeof(u32)); if (ret < 0 && ret != -ETIMEDOUT) dev_err(ucsi->dev, "UCSI_GET_PDOS failed (%d)\n", ret); @@ -770,7 +799,9 @@ static int ucsi_get_pd_message(struct ucsi_connector *con, u8 recipient, command |= UCSI_GET_PD_MESSAGE_BYTES(len); command |= UCSI_GET_PD_MESSAGE_TYPE(type); - ret = ucsi_send_command(con->ucsi, command, data + offset, len); + con->ucsi->message_in_size = len; + ret = ucsi_send_command(con->ucsi, command); + memcpy(data + offset, con->ucsi->message_in, len); if (ret < 0) return ret; } @@ -935,7 +966,9 @@ static int ucsi_register_cable(struct ucsi_connector *con) int ret; command = UCSI_GET_CABLE_PROPERTY | UCSI_CONNECTOR_NUMBER(con->num); - ret = ucsi_send_command(con->ucsi, command, &cable_prop, sizeof(cable_prop)); + con->ucsi->message_in_size = sizeof(cable_prop); + ret = ucsi_send_command(con->ucsi, command); + memcpy(&cable_prop, con->ucsi->message_in, sizeof(cable_prop)); if (ret < 0) { dev_err(con->ucsi->dev, "GET_CABLE_PROPERTY failed (%d)\n", ret); return ret; @@ -996,7 +1029,9 @@ static int ucsi_check_connector_capability(struct ucsi_connector *con) return 0; command = UCSI_GET_CONNECTOR_CAPABILITY | UCSI_CONNECTOR_NUMBER(con->num); - ret = ucsi_send_command(con->ucsi, command, &con->cap, sizeof(con->cap)); + con->ucsi->message_in_size = sizeof(con->cap); + ret = ucsi_send_command(con->ucsi, command); + memcpy(&con->cap, con->ucsi->message_in, sizeof(con->cap)); if (ret < 0) { dev_err(con->ucsi->dev, "GET_CONNECTOR_CAPABILITY failed (%d)\n", ret); return ret; @@ -1008,6 +1043,28 @@ static int ucsi_check_connector_capability(struct ucsi_connector *con) return ret; } +static void ucsi_orientation(struct ucsi_connector *con) +{ + if (con->ucsi->version < UCSI_VERSION_2_0) + return; + + if (!UCSI_CONSTAT(con, CONNECTED)) { + typec_set_orientation(con->port, TYPEC_ORIENTATION_NONE); + return; + } + + switch (UCSI_CONSTAT(con, ORIENTATION)) { + case UCSI_CONSTAT_ORIENTATION_NORMAL: + typec_set_orientation(con->port, TYPEC_ORIENTATION_NORMAL); + break; + case UCSI_CONSTAT_ORIENTATION_REVERSE: + typec_set_orientation(con->port, TYPEC_ORIENTATION_REVERSE); + break; + default: + break; + } +} + static void ucsi_pwr_opmode_change(struct ucsi_connector *con) { switch (UCSI_CONSTAT(con, PWR_OPMODE)) { @@ -1022,14 +1079,17 @@ static void ucsi_pwr_opmode_change(struct ucsi_connector *con) case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5: con->rdo = 0; typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_1_5A); + ucsi_port_psy_changed(con); break; case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0: con->rdo = 0; typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_3_0A); + ucsi_port_psy_changed(con); break; default: con->rdo = 0; typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_USB); + ucsi_port_psy_changed(con); break; } } @@ -1258,6 +1318,7 @@ static void ucsi_handle_connector_change(struct work_struct *work) typec_set_pwr_role(con->port, role); ucsi_port_psy_changed(con); ucsi_partner_change(con); + ucsi_orientation(con); if (UCSI_CONSTAT(con, CONNECTED)) { ucsi_register_partner(con); @@ -1290,7 +1351,7 @@ static void ucsi_handle_connector_change(struct work_struct *work) if (change & UCSI_CONSTAT_CAM_CHANGE) ucsi_partner_task(con, ucsi_check_altmodes, 1, HZ); - if (change & UCSI_CONSTAT_BC_CHANGE) + if (change & (UCSI_CONSTAT_BC_CHANGE | UCSI_CONSTAT_SINK_PATH_CHANGE)) ucsi_port_psy_changed(con); if (con->ucsi->version >= UCSI_VERSION_2_1 && @@ -1354,7 +1415,8 @@ static int ucsi_reset_connector(struct ucsi_connector *con, bool hard) else if (con->ucsi->version >= UCSI_VERSION_2_0) command |= hard ? 0 : UCSI_CONNECTOR_RESET_DATA_VER_2_0; - return ucsi_send_command(con->ucsi, command, NULL, 0); + con->ucsi->message_in_size = 0; + return ucsi_send_command(con->ucsi, command); } static int ucsi_reset_ppm(struct ucsi *ucsi) @@ -1435,7 +1497,8 @@ static int ucsi_role_cmd(struct ucsi_connector *con, u64 command) { int ret; - ret = ucsi_send_command(con->ucsi, command, NULL, 0); + con->ucsi->message_in_size = 0; + ret = ucsi_send_command(con->ucsi, command); if (ret == -ETIMEDOUT) { u64 c; @@ -1443,7 +1506,8 @@ static int ucsi_role_cmd(struct ucsi_connector *con, u64 command) ucsi_reset_ppm(con->ucsi); c = UCSI_SET_NOTIFICATION_ENABLE | con->ucsi->ntfy; - ucsi_send_command(con->ucsi, c, NULL, 0); + con->ucsi->message_in_size = 0; + ucsi_send_command(con->ucsi, c); ucsi_reset_connector(con, true); } @@ -1596,10 +1660,13 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) /* Get connector capability */ command = UCSI_GET_CONNECTOR_CAPABILITY; command |= UCSI_CONNECTOR_NUMBER(con->num); - ret = ucsi_send_command(ucsi, command, &con->cap, sizeof(con->cap)); + ucsi->message_in_size = sizeof(con->cap); + ret = ucsi_send_command(ucsi, command); if (ret < 0) goto out_unlock; + memcpy(&con->cap, ucsi->message_in, sizeof(con->cap)); + if (UCSI_CONCAP(con, OPMODE_DRP)) cap->data = TYPEC_PORT_DRD; else if (UCSI_CONCAP(con, OPMODE_DFP)) @@ -1634,6 +1701,9 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) cap->driver_data = con; cap->ops = &ucsi_ops; + if (ucsi->version >= UCSI_VERSION_2_0) + con->typec_cap.orientation_aware = true; + if (ucsi->ops->update_connector) ucsi->ops->update_connector(con); @@ -1690,6 +1760,7 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) typec_set_pwr_role(con->port, UCSI_CONSTAT(con, PWR_DIR)); ucsi_register_partner(con); ucsi_pwr_opmode_change(con); + ucsi_orientation(con); ucsi_port_psy_changed(con); if (con->ucsi->cap.features & UCSI_CAP_GET_PD_MESSAGE) ucsi_get_partner_identity(con); @@ -1792,21 +1863,30 @@ static int ucsi_init(struct ucsi *ucsi) /* Enable basic notifications */ ntfy = UCSI_ENABLE_NTFY_CMD_COMPLETE | UCSI_ENABLE_NTFY_ERROR; command = UCSI_SET_NOTIFICATION_ENABLE | ntfy; - ret = ucsi_send_command(ucsi, command, NULL, 0); + ucsi->message_in_size = 0; + ret = ucsi_send_command(ucsi, command); if (ret < 0) goto err_reset; /* Get PPM capabilities */ command = UCSI_GET_CAPABILITY; - ret = ucsi_send_command(ucsi, command, &ucsi->cap, - BITS_TO_BYTES(UCSI_GET_CAPABILITY_SIZE)); + ucsi->message_in_size = BITS_TO_BYTES(UCSI_GET_CAPABILITY_SIZE); + ret = ucsi_send_command(ucsi, command); if (ret < 0) goto err_reset; + memcpy(&ucsi->cap, ucsi->message_in, BITS_TO_BYTES(UCSI_GET_CAPABILITY_SIZE)); + if (!ucsi->cap.num_connectors) { ret = -ENODEV; goto err_reset; } + /* Check if reserved bit set. This is out of spec but happens in buggy FW */ + if (ucsi->cap.num_connectors & 0x80) { + dev_warn(ucsi->dev, "UCSI: Invalid num_connectors %d. Likely buggy FW\n", + ucsi->cap.num_connectors); + ucsi->cap.num_connectors &= 0x7f; // clear bit and carry on + } /* Allocate the connectors. Released in ucsi_unregister() */ connector = kcalloc(ucsi->cap.num_connectors + 1, sizeof(*connector), GFP_KERNEL); @@ -1826,7 +1906,8 @@ static int ucsi_init(struct ucsi *ucsi) /* Enable all supported notifications */ ntfy = ucsi_get_supported_notifications(ucsi); command = UCSI_SET_NOTIFICATION_ENABLE | ntfy; - ret = ucsi_send_command(ucsi, command, NULL, 0); + ucsi->message_in_size = 0; + ret = ucsi_send_command(ucsi, command); if (ret < 0) goto err_unregister; @@ -1877,7 +1958,8 @@ static void ucsi_resume_work(struct work_struct *work) /* Restore UCSI notification enable mask after system resume */ command = UCSI_SET_NOTIFICATION_ENABLE | ucsi->ntfy; - ret = ucsi_send_command(ucsi, command, NULL, 0); + ucsi->message_in_size = 0; + ret = ucsi_send_command(ucsi, command); if (ret < 0) { dev_err(ucsi->dev, "failed to re-enable notifications (%d)\n", ret); return; diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index e301d9012936..f946b728c373 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -29,6 +29,10 @@ struct dentry; #define UCSI_MESSAGE_OUT 32 #define UCSIv2_MESSAGE_OUT 272 +/* Define maximum lengths for message buffers */ +#define UCSI_MAX_MESSAGE_IN_LENGTH 256 +#define UCSI_MAX_MESSAGE_OUT_LENGTH 256 + /* UCSI versions */ #define UCSI_VERSION_1_0 0x0100 #define UCSI_VERSION_1_1 0x0110 @@ -65,6 +69,7 @@ struct dentry; * @read_cci: Read CCI register * @poll_cci: Read CCI register while polling with notifications disabled * @read_message_in: Read message data from UCSI + * @write_message_out: Write message data to UCSI * @sync_control: Blocking control operation * @async_control: Non-blocking control operation * @update_altmodes: Squashes duplicate DP altmodes @@ -80,8 +85,8 @@ struct ucsi_operations { int (*read_cci)(struct ucsi *ucsi, u32 *cci); int (*poll_cci)(struct ucsi *ucsi, u32 *cci); int (*read_message_in)(struct ucsi *ucsi, void *val, size_t val_len); - int (*sync_control)(struct ucsi *ucsi, u64 command, u32 *cci, - void *data, size_t size); + int (*write_message_out)(struct ucsi *ucsi, void *data, size_t data_len); + int (*sync_control)(struct ucsi *ucsi, u64 command, u32 *cci); int (*async_control)(struct ucsi *ucsi, u64 command); bool (*update_altmodes)(struct ucsi *ucsi, u8 recipient, struct ucsi_altmode *orig, @@ -127,10 +132,12 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num); #define UCSI_GET_CONNECTOR_STATUS 0x12 #define UCSI_GET_CONNECTOR_STATUS_SIZE 152 #define UCSI_GET_ERROR_STATUS 0x13 +#define UCSI_SET_POWER_LEVEL 0x14 #define UCSI_GET_ATTENTION_VDO 0x16 #define UCSI_GET_PD_MESSAGE 0x15 #define UCSI_GET_CAM_CS 0x18 #define UCSI_SET_SINK_PATH 0x1c +#define UCSI_SET_PDOS 0x1d #define UCSI_READ_POWER_LEVEL 0x1e #define UCSI_SET_USB 0x21 #define UCSI_GET_LPM_PPM_INFO 0x22 @@ -360,6 +367,12 @@ struct ucsi_cable_property { #define UCSI_CONSTAT_BC_SLOW_CHARGING 2 #define UCSI_CONSTAT_BC_TRICKLE_CHARGING 3 #define UCSI_CONSTAT_PD_VERSION_V1_2 UCSI_DECLARE_BITFIELD_V1_2(70, 16) +#define UCSI_CONSTAT_ORIENTATION UCSI_DECLARE_BITFIELD_V2_0(86, 1) +#define UCSI_CONSTAT_ORIENTATION_NORMAL 0 +#define UCSI_CONSTAT_ORIENTATION_REVERSE 1 +#define UCSI_CONSTAT_SINK_PATH_STATUS_V2_0 UCSI_DECLARE_BITFIELD_V2_0(87, 1) +#define UCSI_CONSTAT_SINK_PATH_DISABLED 0 +#define UCSI_CONSTAT_SINK_PATH_ENABLED 1 #define UCSI_CONSTAT_PWR_READING_READY_V2_1 UCSI_DECLARE_BITFIELD_V2_1(89, 1) #define UCSI_CONSTAT_CURRENT_SCALE_V2_1 UCSI_DECLARE_BITFIELD_V2_1(90, 3) #define UCSI_CONSTAT_PEAK_CURRENT_V2_1 UCSI_DECLARE_BITFIELD_V2_1(93, 16) @@ -379,6 +392,7 @@ struct ucsi_cable_property { #define UCSI_CONSTAT_BC_CHANGE BIT(9) #define UCSI_CONSTAT_PARTNER_CHANGE BIT(11) #define UCSI_CONSTAT_POWER_DIR_CHANGE BIT(12) +#define UCSI_CONSTAT_SINK_PATH_CHANGE BIT(13) #define UCSI_CONSTAT_CONNECT_CHANGE BIT(14) #define UCSI_CONSTAT_ERROR BIT(15) @@ -485,6 +499,12 @@ struct ucsi { unsigned long quirks; #define UCSI_NO_PARTNER_PDOS BIT(0) /* Don't read partner's PDOs */ #define UCSI_DELAY_DEVICE_PDOS BIT(1) /* Reading PDOs fails until the parter is in PD mode */ + + /* Fixed-size buffers for incoming and outgoing messages */ + u8 message_in[UCSI_MAX_MESSAGE_IN_LENGTH]; + size_t message_in_size; + u8 message_out[UCSI_MAX_MESSAGE_OUT_LENGTH]; + size_t message_out_size; }; #define UCSI_MAX_DATA_LENGTH(u) (((u)->version < UCSI_VERSION_2_0) ? 0x10 : 0xff) @@ -547,15 +567,13 @@ struct ucsi_connector { struct usb_pd_identity cable_identity; }; -int ucsi_send_command(struct ucsi *ucsi, u64 command, - void *retval, size_t size); +int ucsi_send_command(struct ucsi *ucsi, u64 command); void ucsi_altmode_update_active(struct ucsi_connector *con); int ucsi_resume(struct ucsi *ucsi); void ucsi_notify_common(struct ucsi *ucsi, u32 cci); -int ucsi_sync_control_common(struct ucsi *ucsi, u64 command, u32 *cci, - void *data, size_t size); +int ucsi_sync_control_common(struct ucsi *ucsi, u64 command, u32 *cci); #if IS_ENABLED(CONFIG_POWER_SUPPLY) int ucsi_register_port_psy(struct ucsi_connector *con); diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c index 6b92f296e985..f9beeb835238 100644 --- a/drivers/usb/typec/ucsi/ucsi_acpi.c +++ b/drivers/usb/typec/ucsi/ucsi_acpi.c @@ -86,6 +86,21 @@ static int ucsi_acpi_read_message_in(struct ucsi *ucsi, void *val, size_t val_le return 0; } +static int ucsi_acpi_write_message_out(struct ucsi *ucsi, void *data, size_t data_len) +{ + struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); + + if (!data || !data_len) + return -EINVAL; + + if (ucsi->version <= UCSI_VERSION_1_2) + memcpy(ua->base + UCSI_MESSAGE_OUT, data, data_len); + else + memcpy(ua->base + UCSIv2_MESSAGE_OUT, data, data_len); + + return 0; +} + static int ucsi_acpi_async_control(struct ucsi *ucsi, u64 command) { struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); @@ -101,19 +116,19 @@ static const struct ucsi_operations ucsi_acpi_ops = { .read_cci = ucsi_acpi_read_cci, .poll_cci = ucsi_acpi_poll_cci, .read_message_in = ucsi_acpi_read_message_in, + .write_message_out = ucsi_acpi_write_message_out, .sync_control = ucsi_sync_control_common, .async_control = ucsi_acpi_async_control }; -static int ucsi_gram_sync_control(struct ucsi *ucsi, u64 command, u32 *cci, - void *val, size_t len) +static int ucsi_gram_sync_control(struct ucsi *ucsi, u64 command, u32 *cci) { u16 bogus_change = UCSI_CONSTAT_POWER_LEVEL_CHANGE | UCSI_CONSTAT_PDOS_CHANGE; struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); int ret; - ret = ucsi_sync_control_common(ucsi, command, cci, val, len); + ret = ucsi_sync_control_common(ucsi, command, cci); if (ret < 0) return ret; @@ -125,8 +140,8 @@ static int ucsi_gram_sync_control(struct ucsi *ucsi, u64 command, u32 *cci, if (UCSI_COMMAND(ua->cmd) == UCSI_GET_CONNECTOR_STATUS && ua->check_bogus_event) { /* Clear the bogus change */ - if (*(u16 *)val == bogus_change) - *(u16 *)val = 0; + if (*(u16 *)ucsi->message_in == bogus_change) + *(u16 *)ucsi->message_in = 0; ua->check_bogus_event = false; } diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index d83a0051c737..ead1b2a25c79 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -606,8 +606,7 @@ static int ucsi_ccg_async_control(struct ucsi *ucsi, u64 command) return ccg_write(uc, reg, (u8 *)&command, sizeof(command)); } -static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command, u32 *cci, - void *data, size_t size) +static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command, u32 *cci) { struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi); struct ucsi_connector *con; @@ -629,16 +628,16 @@ static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command, u32 *cci, ucsi_ccg_update_set_new_cam_cmd(uc, con, &command); } - ret = ucsi_sync_control_common(ucsi, command, cci, data, size); + ret = ucsi_sync_control_common(ucsi, command, cci); switch (UCSI_COMMAND(command)) { case UCSI_GET_CURRENT_CAM: if (uc->has_multiple_dp) - ucsi_ccg_update_get_current_cam_cmd(uc, (u8 *)data); + ucsi_ccg_update_get_current_cam_cmd(uc, (u8 *)ucsi->message_in); break; case UCSI_GET_ALTERNATE_MODES: if (UCSI_ALTMODE_RECIPIENT(command) == UCSI_RECIPIENT_SOP) { - struct ucsi_altmode *alt = data; + struct ucsi_altmode *alt = (struct ucsi_altmode *)ucsi->message_in; if (alt[0].svid == USB_TYPEC_NVIDIA_VLINK_SID) ucsi_ccg_nvidia_altmode(uc, alt, command); @@ -646,7 +645,7 @@ static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command, u32 *cci, break; case UCSI_GET_CAPABILITY: if (uc->fw_build == CCG_FW_BUILD_NVIDIA_TEGRA) { - struct ucsi_capability *cap = data; + struct ucsi_capability *cap = (struct ucsi_capability *)ucsi->message_in; cap->features &= ~UCSI_CAP_ALT_MODE_DETAILS; } diff --git a/drivers/usb/typec/ucsi/ucsi_glink.c b/drivers/usb/typec/ucsi/ucsi_glink.c index 8af79101a2fc..11b3e24e34e2 100644 --- a/drivers/usb/typec/ucsi/ucsi_glink.c +++ b/drivers/usb/typec/ucsi/ucsi_glink.c @@ -16,10 +16,10 @@ #define PMIC_GLINK_MAX_PORTS 3 -#define UCSI_BUF_SIZE 48 +#define UCSI_BUF_V1_SIZE (UCSI_MESSAGE_OUT + (UCSI_MESSAGE_OUT - UCSI_MESSAGE_IN)) +#define UCSI_BUF_V2_SIZE (UCSIv2_MESSAGE_OUT + (UCSIv2_MESSAGE_OUT - UCSI_MESSAGE_IN)) #define MSG_TYPE_REQ_RESP 1 -#define UCSI_BUF_SIZE 48 #define UC_NOTIFY_RECEIVER_UCSI 0x0 #define UC_UCSI_READ_BUF_REQ 0x11 @@ -30,24 +30,30 @@ struct ucsi_read_buf_req_msg { struct pmic_glink_hdr hdr; }; -struct ucsi_read_buf_resp_msg { +struct __packed ucsi_read_buf_resp_msg { struct pmic_glink_hdr hdr; - u8 buf[UCSI_BUF_SIZE]; + union { + u8 v2_buf[UCSI_BUF_V2_SIZE]; + u8 v1_buf[UCSI_BUF_V1_SIZE]; + } buf; u32 ret_code; }; -struct ucsi_write_buf_req_msg { +struct __packed ucsi_write_buf_req_msg { struct pmic_glink_hdr hdr; - u8 buf[UCSI_BUF_SIZE]; + union { + u8 v2_buf[UCSI_BUF_V2_SIZE]; + u8 v1_buf[UCSI_BUF_V1_SIZE]; + } buf; u32 reserved; }; -struct ucsi_write_buf_resp_msg { +struct __packed ucsi_write_buf_resp_msg { struct pmic_glink_hdr hdr; u32 ret_code; }; -struct ucsi_notify_ind_msg { +struct __packed ucsi_notify_ind_msg { struct pmic_glink_hdr hdr; u32 notification; u32 receiver; @@ -72,7 +78,7 @@ struct pmic_glink_ucsi { bool ucsi_registered; bool pd_running; - u8 read_buf[UCSI_BUF_SIZE]; + u8 read_buf[UCSI_BUF_V2_SIZE]; }; static int pmic_glink_ucsi_read(struct ucsi *__ucsi, unsigned int offset, @@ -132,17 +138,35 @@ static int pmic_glink_ucsi_locked_write(struct pmic_glink_ucsi *ucsi, unsigned i const void *val, size_t val_len) { struct ucsi_write_buf_req_msg req = {}; + size_t req_len, buf_len; unsigned long left; int ret; + u8 *buf; req.hdr.owner = PMIC_GLINK_OWNER_USBC; req.hdr.type = MSG_TYPE_REQ_RESP; req.hdr.opcode = UC_UCSI_WRITE_BUF_REQ; - memcpy(&req.buf[offset], val, val_len); + + if (ucsi->ucsi->version >= UCSI_VERSION_2_0) { + buf_len = UCSI_BUF_V2_SIZE; + buf = req.buf.v2_buf; + } else if (ucsi->ucsi->version) { + buf_len = UCSI_BUF_V1_SIZE; + buf = req.buf.v1_buf; + } else { + dev_err(ucsi->dev, "UCSI version unknown\n"); + return -EINVAL; + } + req_len = sizeof(struct pmic_glink_hdr) + buf_len + sizeof(u32); + + if (offset + val_len > buf_len) + return -EINVAL; + + memcpy(&buf[offset], val, val_len); reinit_completion(&ucsi->write_ack); - ret = pmic_glink_send(ucsi->client, &req, sizeof(req)); + ret = pmic_glink_send(ucsi->client, &req, req_len); if (ret < 0) { dev_err(ucsi->dev, "failed to send UCSI write request: %d\n", ret); return ret; @@ -216,12 +240,48 @@ static const struct ucsi_operations pmic_glink_ucsi_ops = { static void pmic_glink_ucsi_read_ack(struct pmic_glink_ucsi *ucsi, const void *data, int len) { - const struct ucsi_read_buf_resp_msg *resp = data; + u32 ret_code, resp_len, buf_len = 0; + u8 *buf; - if (resp->ret_code) + if (ucsi->ucsi->version) { + if (ucsi->ucsi->version >= UCSI_VERSION_2_0) { + buf = ((struct ucsi_read_buf_resp_msg *)data)->buf.v2_buf; + buf_len = UCSI_BUF_V2_SIZE; + } else { + buf = ((struct ucsi_read_buf_resp_msg *)data)->buf.v1_buf; + buf_len = UCSI_BUF_V1_SIZE; + } + } else if (!ucsi->ucsi_registered) { + /* + * If UCSI version is not known yet because device is not registered, choose buffer + * size which best fits incoming data + */ + if (len > sizeof(struct pmic_glink_hdr) + UCSI_BUF_V2_SIZE) { + buf = ((struct ucsi_read_buf_resp_msg *)data)->buf.v2_buf; + buf_len = UCSI_BUF_V2_SIZE; + } else { + buf = ((struct ucsi_read_buf_resp_msg *)data)->buf.v1_buf; + buf_len = UCSI_BUF_V1_SIZE; + } + } else { + dev_err(ucsi->dev, "Device has been registered but UCSI version is still unknown\n"); + return; + } + + resp_len = sizeof(struct pmic_glink_hdr) + buf_len + sizeof(u32); + + if (len > resp_len) return; - memcpy(ucsi->read_buf, resp->buf, UCSI_BUF_SIZE); + /* Ensure that buffer_len leaves space for ret_code to be read back from memory */ + if (buf_len > len - sizeof(struct pmic_glink_hdr) - sizeof(u32)) + buf_len = len - sizeof(struct pmic_glink_hdr) - sizeof(u32); + + memcpy(&ret_code, buf + buf_len, sizeof(u32)); + if (ret_code) + return; + + memcpy(ucsi->read_buf, buf, buf_len); complete(&ucsi->read_ack); } diff --git a/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c b/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c index 7b5222081bbb..c5965656baba 100644 --- a/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c +++ b/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c @@ -196,6 +196,7 @@ static void gaokun_ucsi_connector_status(struct ucsi_connector *con) const struct ucsi_operations gaokun_ucsi_ops = { .read_version = gaokun_ucsi_read_version, .read_cci = gaokun_ucsi_read_cci, + .poll_cci = gaokun_ucsi_read_cci, .read_message_in = gaokun_ucsi_read_message_in, .sync_control = ucsi_sync_control_common, .async_control = gaokun_ucsi_async_control, @@ -502,6 +503,7 @@ static void gaokun_ucsi_remove(struct auxiliary_device *adev) { struct gaokun_ucsi *uec = auxiliary_get_drvdata(adev); + disable_delayed_work_sync(&uec->work); gaokun_ec_unregister_notify(uec->ec, &uec->nb); ucsi_unregister(uec->ucsi); ucsi_destroy(uec->ucsi); diff --git a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c index 0187c1c4b21a..299081444caa 100644 --- a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c +++ b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c @@ -88,8 +88,7 @@ static int yoga_c630_ucsi_async_control(struct ucsi *ucsi, u64 command) static int yoga_c630_ucsi_sync_control(struct ucsi *ucsi, u64 command, - u32 *cci, - void *data, size_t size) + u32 *cci) { int ret; @@ -107,8 +106,8 @@ static int yoga_c630_ucsi_sync_control(struct ucsi *ucsi, }; dev_dbg(ucsi->dev, "faking DP altmode for con1\n"); - memset(data, 0, size); - memcpy(data, &alt, min(sizeof(alt), size)); + memset(ucsi->message_in, 0, ucsi->message_in_size); + memcpy(ucsi->message_in, &alt, min(sizeof(alt), ucsi->message_in_size)); *cci = UCSI_CCI_COMMAND_COMPLETE | UCSI_SET_CCI_LENGTH(sizeof(alt)); return 0; } @@ -121,18 +120,18 @@ static int yoga_c630_ucsi_sync_control(struct ucsi *ucsi, if (UCSI_COMMAND(command) == UCSI_GET_ALTERNATE_MODES && UCSI_GET_ALTMODE_GET_CONNECTOR_NUMBER(command) == 2) { dev_dbg(ucsi->dev, "ignoring altmodes for con2\n"); - memset(data, 0, size); + memset(ucsi->message_in, 0, ucsi->message_in_size); *cci = UCSI_CCI_COMMAND_COMPLETE; return 0; } - ret = ucsi_sync_control_common(ucsi, command, cci, data, size); + ret = ucsi_sync_control_common(ucsi, command, cci); if (ret < 0) return ret; /* UCSI_GET_CURRENT_CAM is off-by-one on all ports */ - if (UCSI_COMMAND(command) == UCSI_GET_CURRENT_CAM && data) - ((u8 *)data)[0]--; + if (UCSI_COMMAND(command) == UCSI_GET_CURRENT_CAM && ucsi->message_in_size > 0) + ucsi->message_in[0]--; return ret; } diff --git a/drivers/usb/usbip/stub_tx.c b/drivers/usb/usbip/stub_tx.c index 7eb2e074012a..55919c3762ba 100644 --- a/drivers/usb/usbip/stub_tx.c +++ b/drivers/usb/usbip/stub_tx.c @@ -4,6 +4,7 @@ */ #include +#include #include #include @@ -239,17 +240,13 @@ static int stub_send_ret_submit(struct stub_device *sdev) urb->actual_length > 0) { if (urb->num_sgs) { unsigned int copy = urb->actual_length; - int size; + unsigned int size; for_each_sg(urb->sg, sg, urb->num_sgs, i) { if (copy == 0) break; - if (copy < sg->length) - size = copy; - else - size = sg->length; - + size = min(copy, sg->length); iov[iovnum].iov_base = sg_virt(sg); iov[iovnum].iov_len = size; diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c index 0d6c10a8490c..e55690da19e5 100644 --- a/drivers/usb/usbip/vhci_hcd.c +++ b/drivers/usb/usbip/vhci_hcd.c @@ -346,9 +346,10 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, if (wIndex < 1 || wIndex > VHCI_HC_PORTS) { invalid_rhport = true; if (wIndex > VHCI_HC_PORTS) - pr_err("invalid port number %d\n", wIndex); - } else + dev_err(hcd_dev(hcd), "invalid port number %d\n", wIndex); + } else { rhport = wIndex - 1; + } vhci_hcd = hcd_to_vhci_hcd(hcd); vhci = vhci_hcd->vhci; @@ -368,14 +369,14 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, break; case ClearPortFeature: if (invalid_rhport) { - pr_err("invalid port number %d\n", wIndex); + dev_err(hcd_dev(hcd), "invalid port number %d\n", wIndex); goto error; } switch (wValue) { case USB_PORT_FEAT_SUSPEND: if (hcd->speed >= HCD_USB3) { - pr_err(" ClearPortFeature: USB_PORT_FEAT_SUSPEND req not " - "supported for USB 3.0 roothub\n"); + dev_err(hcd_dev(hcd), + "ClearPortFeature: USB_PORT_FEAT_SUSPEND req not supported for USB 3.0 roothub\n"); goto error; } usbip_dbg_vhci_rh( @@ -408,7 +409,8 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, if (hcd->speed >= HCD_USB3 && (wLength < USB_DT_SS_HUB_SIZE || wValue != (USB_DT_SS_HUB << 8))) { - pr_err("Wrong hub descriptor type for USB 3.0 roothub.\n"); + dev_err(hcd_dev(hcd), + "Wrong hub descriptor type for USB 3.0 roothub.\n"); goto error; } if (hcd->speed >= HCD_USB3) @@ -433,7 +435,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, case GetPortStatus: usbip_dbg_vhci_rh(" GetPortStatus port %x\n", wIndex); if (invalid_rhport) { - pr_err("invalid port number %d\n", wIndex); + dev_err(hcd_dev(hcd), "invalid port number %d\n", wIndex); retval = -EPIPE; goto error; } @@ -483,7 +485,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, USB_PORT_STAT_LOW_SPEED; break; default: - pr_err("vhci_device speed not set\n"); + dev_err(hcd_dev(hcd), "vhci_device speed not set\n"); break; } } @@ -505,8 +507,8 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, usbip_dbg_vhci_rh( " SetPortFeature: USB_PORT_FEAT_LINK_STATE\n"); if (hcd->speed < HCD_USB3) { - pr_err("USB_PORT_FEAT_LINK_STATE req not " - "supported for USB 2.0 roothub\n"); + dev_err(hcd_dev(hcd), + "USB_PORT_FEAT_LINK_STATE req not supported for USB 2.0 roothub\n"); goto error; } /* @@ -523,8 +525,8 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, " SetPortFeature: USB_PORT_FEAT_U2_TIMEOUT\n"); /* TODO: add suspend/resume support! */ if (hcd->speed < HCD_USB3) { - pr_err("USB_PORT_FEAT_U1/2_TIMEOUT req not " - "supported for USB 2.0 roothub\n"); + dev_err(hcd_dev(hcd), + "USB_PORT_FEAT_U1/2_TIMEOUT req not supported for USB 2.0 roothub\n"); goto error; } break; @@ -533,13 +535,13 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, " SetPortFeature: USB_PORT_FEAT_SUSPEND\n"); /* Applicable only for USB2.0 hub */ if (hcd->speed >= HCD_USB3) { - pr_err("USB_PORT_FEAT_SUSPEND req not " - "supported for USB 3.0 roothub\n"); + dev_err(hcd_dev(hcd), + "USB_PORT_FEAT_SUSPEND req not supported for USB 3.0 roothub\n"); goto error; } if (invalid_rhport) { - pr_err("invalid port number %d\n", wIndex); + dev_err(hcd_dev(hcd), "invalid port number %d\n", wIndex); goto error; } @@ -549,7 +551,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, usbip_dbg_vhci_rh( " SetPortFeature: USB_PORT_FEAT_POWER\n"); if (invalid_rhport) { - pr_err("invalid port number %d\n", wIndex); + dev_err(hcd_dev(hcd), "invalid port number %d\n", wIndex); goto error; } if (hcd->speed >= HCD_USB3) @@ -561,13 +563,13 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, usbip_dbg_vhci_rh( " SetPortFeature: USB_PORT_FEAT_BH_PORT_RESET\n"); if (invalid_rhport) { - pr_err("invalid port number %d\n", wIndex); + dev_err(hcd_dev(hcd), "invalid port number %d\n", wIndex); goto error; } /* Applicable only for USB3.0 hub */ if (hcd->speed < HCD_USB3) { - pr_err("USB_PORT_FEAT_BH_PORT_RESET req not " - "supported for USB 2.0 roothub\n"); + dev_err(hcd_dev(hcd), + "USB_PORT_FEAT_BH_PORT_RESET req not supported for USB 2.0 roothub\n"); goto error; } fallthrough; @@ -575,7 +577,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, usbip_dbg_vhci_rh( " SetPortFeature: USB_PORT_FEAT_RESET\n"); if (invalid_rhport) { - pr_err("invalid port number %d\n", wIndex); + dev_err(hcd_dev(hcd), "invalid port number %d\n", wIndex); goto error; } /* if it's already enabled, disable */ @@ -598,7 +600,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, usbip_dbg_vhci_rh(" SetPortFeature: default %d\n", wValue); if (invalid_rhport) { - pr_err("invalid port number %d\n", wIndex); + dev_err(hcd_dev(hcd), "invalid port number %d\n", wIndex); goto error; } if (wValue >= 32) @@ -618,8 +620,8 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, case GetPortErrorCount: usbip_dbg_vhci_rh(" GetPortErrorCount\n"); if (hcd->speed < HCD_USB3) { - pr_err("GetPortErrorCount req not " - "supported for USB 2.0 roothub\n"); + dev_err(hcd_dev(hcd), + "GetPortErrorCount req not supported for USB 2.0 roothub\n"); goto error; } /* We'll always return 0 since this is a dummy hub */ @@ -628,13 +630,14 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, case SetHubDepth: usbip_dbg_vhci_rh(" SetHubDepth\n"); if (hcd->speed < HCD_USB3) { - pr_err("SetHubDepth req not supported for " - "USB 2.0 roothub\n"); + dev_err(hcd_dev(hcd), + "SetHubDepth req not supported for USB 2.0 roothub\n"); goto error; } break; default: - pr_err("default hub control req: %04x v%04x i%04x l%d\n", + dev_err(hcd_dev(hcd), + "default hub control req: %04x v%04x i%04x l%d\n", typeReq, wValue, wIndex, wLength); error: /* "protocol stall" on error */ @@ -642,7 +645,7 @@ error: } if (usbip_dbg_flag_vhci_rh) { - pr_debug("port %d\n", rhport); + dev_dbg(hcd_dev(hcd), "%s port %d\n", __func__, rhport); /* Only dump valid port status */ if (!invalid_rhport) { dump_port_status_diff(prev_port_status[rhport], @@ -702,7 +705,7 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag unsigned long flags; if (portnum > VHCI_HC_PORTS) { - pr_err("invalid port number %d\n", portnum); + dev_err(hcd_dev(hcd), "invalid port number %d\n", portnum); return -ENODEV; } vdev = &vhci_hcd->vdev[portnum-1]; @@ -831,15 +834,15 @@ out: no_need_xmit: usb_hcd_unlink_urb_from_ep(hcd, urb); no_need_unlink: - spin_unlock_irqrestore(&vhci->lock, flags); if (!ret) { /* usb_hcd_giveback_urb() should be called with * irqs disabled */ - local_irq_disable(); + spin_unlock(&vhci->lock); usb_hcd_giveback_urb(hcd, urb, urb->status); - local_irq_enable(); + spin_lock(&vhci->lock); } + spin_unlock_irqrestore(&vhci->lock, flags); return ret; } @@ -958,7 +961,7 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) unlink->seqnum = atomic_inc_return(&vhci_hcd->seqnum); if (unlink->seqnum == 0xffff) - pr_info("seqnum max\n"); + dev_info(hcd_dev(hcd), "seqnum max\n"); unlink->unlink_seqnum = priv->seqnum; @@ -1036,10 +1039,11 @@ static void vhci_device_unlink_cleanup(struct vhci_device *vdev) static void vhci_shutdown_connection(struct usbip_device *ud) { struct vhci_device *vdev = container_of(ud, struct vhci_device, ud); + struct usb_hcd *hcd = vhci_hcd_to_hcd(vdev_to_vhci_hcd(vdev)); /* need this? see stub_dev.c */ if (ud->tcp_socket) { - pr_debug("shutdown tcp_socket %d\n", ud->sockfd); + dev_dbg(hcd_dev(hcd), "shutdown tcp_socket %d\n", ud->sockfd); kernel_sock_shutdown(ud->tcp_socket, SHUT_RDWR); } @@ -1052,7 +1056,7 @@ static void vhci_shutdown_connection(struct usbip_device *ud) kthread_stop_put(vdev->ud.tcp_tx); vdev->ud.tcp_tx = NULL; } - pr_info("stop threads\n"); + dev_info(hcd_dev(hcd), "stop threads\n"); /* active connection is closed */ if (vdev->ud.tcp_socket) { @@ -1060,7 +1064,7 @@ static void vhci_shutdown_connection(struct usbip_device *ud) vdev->ud.tcp_socket = NULL; vdev->ud.sockfd = -1; } - pr_info("release socket\n"); + dev_info(hcd_dev(hcd), "release socket\n"); vhci_device_unlink_cleanup(vdev); @@ -1086,7 +1090,7 @@ static void vhci_shutdown_connection(struct usbip_device *ud) */ rh_port_disconnect(vdev); - pr_info("disconnect device\n"); + dev_info(hcd_dev(hcd), "disconnect device\n"); } static void vhci_device_reset(struct usbip_device *ud) @@ -1222,7 +1226,7 @@ static int vhci_start(struct usb_hcd *hcd) id = hcd_name_to_id(hcd_name(hcd)); if (id < 0) { - pr_err("invalid vhci name %s\n", hcd_name(hcd)); + dev_err(hcd_dev(hcd), "invalid vhci name %s\n", hcd_name(hcd)); return -EINVAL; } @@ -1239,7 +1243,7 @@ static int vhci_start(struct usb_hcd *hcd) vhci_finish_attr_group(); return err; } - pr_info("created sysfs %s\n", hcd_name(hcd)); + dev_info(hcd_dev(hcd), "created sysfs %s\n", hcd_name(hcd)); } return 0; @@ -1372,10 +1376,9 @@ static int vhci_hcd_probe(struct platform_device *pdev) * Our private data is also allocated automatically. */ hcd_hs = usb_create_hcd(&vhci_hc_driver, &pdev->dev, dev_name(&pdev->dev)); - if (!hcd_hs) { - pr_err("create primary hcd failed\n"); - return -ENOMEM; - } + if (!hcd_hs) + return dev_err_probe(&pdev->dev, -ENOMEM, "create primary hcd failed\n"); + hcd_hs->has_tt = 1; /* @@ -1383,22 +1386,21 @@ static int vhci_hcd_probe(struct platform_device *pdev) * Call the driver's reset() and start() routines. */ ret = usb_add_hcd(hcd_hs, 0, 0); - if (ret != 0) { - pr_err("usb_add_hcd hs failed %d\n", ret); + if (ret) { + dev_err_probe(&pdev->dev, ret, "usb_add_hcd hs failed\n"); goto put_usb2_hcd; } hcd_ss = usb_create_shared_hcd(&vhci_hc_driver, &pdev->dev, dev_name(&pdev->dev), hcd_hs); if (!hcd_ss) { - ret = -ENOMEM; - pr_err("create shared hcd failed\n"); + ret = dev_err_probe(&pdev->dev, -ENOMEM, "create shared hcd failed\n"); goto remove_usb2_hcd; } ret = usb_add_hcd(hcd_ss, 0, 0); if (ret) { - pr_err("usb_add_hcd ss failed %d\n", ret); + dev_err_probe(&pdev->dev, ret, "usb_add_hcd ss failed\n"); goto put_usb3_hcd; } diff --git a/include/linux/platform_data/usb-davinci.h b/include/linux/platform_data/usb-davinci.h deleted file mode 100644 index 879f5c78b91a..000000000000 --- a/include/linux/platform_data/usb-davinci.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * USB related definitions - * - * Copyright (C) 2009 MontaVista Software, Inc. - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ - -#ifndef __ASM_ARCH_USB_H -#define __ASM_ARCH_USB_H - -/* Passed as the platform data to the OHCI driver */ -struct da8xx_ohci_root_hub { - /* Time from power on to power good (in 2 ms units) */ - u8 potpgt; -}; - -void davinci_setup_usb(unsigned mA, unsigned potpgt_ms); - -#endif /* ifndef __ASM_ARCH_USB_H */ diff --git a/include/linux/usb/pd.h b/include/linux/usb/pd.h index 3068c3084eb6..6ccd1b2af993 100644 --- a/include/linux/usb/pd.h +++ b/include/linux/usb/pd.h @@ -6,6 +6,7 @@ #ifndef __LINUX_USB_PD_H #define __LINUX_USB_PD_H +#include #include #include #include @@ -271,9 +272,11 @@ enum pd_pdo_type { enum pd_apdo_type { APDO_TYPE_PPS = 0, + APDO_TYPE_EPR_AVS = 1, + APDO_TYPE_SPR_AVS = 2, }; -#define PDO_APDO_TYPE_SHIFT 28 /* Only valid value currently is 0x0 - PPS */ +#define PDO_APDO_TYPE_SHIFT 28 #define PDO_APDO_TYPE_MASK 0x3 #define PDO_APDO_TYPE(t) ((t) << PDO_APDO_TYPE_SHIFT) @@ -297,6 +300,35 @@ enum pd_apdo_type { PDO_PPS_APDO_MIN_VOLT(min_mv) | PDO_PPS_APDO_MAX_VOLT(max_mv) | \ PDO_PPS_APDO_MAX_CURR(max_ma)) +/* + * Applicable only to EPR AVS APDO source cap as per + * Table 6.15 EPR Adjustable Voltage Supply APDO – Source + */ +#define PDO_EPR_AVS_APDO_PEAK_CURRENT GENMASK(27, 26) + +/* + * Applicable to both EPR AVS APDO source and sink cap as per + * Table 6.15 EPR Adjustable Voltage Supply APDO – Source + * Table 6.22 EPR Adjustable Voltage Supply APDO – Sink + */ +#define PDO_EPR_AVS_APDO_MAX_VOLT GENMASK(25, 17) /* 100mV unit */ +#define PDO_EPR_AVS_APDO_MIN_VOLT GENMASK(15, 8) /* 100mV unit */ +#define PDO_EPR_AVS_APDO_PDP GENMASK(7, 0) /* 1W unit */ + +/* + * Applicable only SPR AVS APDO source cap as per + * Table 6.14 SPR Adjustable Voltage Supply APDO – Source + */ +#define PDO_SPR_AVS_APDO_PEAK_CURRENT GENMASK(27, 26) + +/* + * Applicable to both SPR AVS APDO source and sink cap as per + * Table 6.14 SPR Adjustable Voltage Supply APDO – Source + * Table 6.21 SPR Adjustable Voltage Supply APDO – Sink + */ +#define PDO_SPR_AVS_APDO_9V_TO_15V_MAX_CURR GENMASK(19, 10) /* 10mA unit */ +#define PDO_SPR_AVS_APDO_15V_TO_20V_MAX_CURR GENMASK(9, 0) /* 10mA unit */ + static inline enum pd_pdo_type pdo_type(u32 pdo) { return (pdo >> PDO_TYPE_SHIFT) & PDO_TYPE_MASK; @@ -350,6 +382,41 @@ static inline unsigned int pdo_pps_apdo_max_current(u32 pdo) PDO_PPS_APDO_CURR_MASK) * 50; } +static inline unsigned int pdo_epr_avs_apdo_src_peak_current(u32 pdo) +{ + return FIELD_GET(PDO_EPR_AVS_APDO_PEAK_CURRENT, pdo); +} + +static inline unsigned int pdo_epr_avs_apdo_min_voltage_mv(u32 pdo) +{ + return FIELD_GET(PDO_EPR_AVS_APDO_MIN_VOLT, pdo) * 100; +} + +static inline unsigned int pdo_epr_avs_apdo_max_voltage_mv(u32 pdo) +{ + return FIELD_GET(PDO_EPR_AVS_APDO_MIN_VOLT, pdo) * 100; +} + +static inline unsigned int pdo_epr_avs_apdo_pdp_w(u32 pdo) +{ + return FIELD_GET(PDO_EPR_AVS_APDO_PDP, pdo); +} + +static inline unsigned int pdo_spr_avs_apdo_src_peak_current(u32 pdo) +{ + return FIELD_GET(PDO_SPR_AVS_APDO_PEAK_CURRENT, pdo); +} + +static inline unsigned int pdo_spr_avs_apdo_9v_to_15v_max_current_ma(u32 pdo) +{ + return FIELD_GET(PDO_SPR_AVS_APDO_9V_TO_15V_MAX_CURR, pdo) * 10; +} + +static inline unsigned int pdo_spr_avs_apdo_15v_to_20v_max_current_ma(u32 pdo) +{ + return FIELD_GET(PDO_SPR_AVS_APDO_15V_TO_20V_MAX_CURR, pdo) * 10; +} + /* RDO: Request Data Object */ #define RDO_OBJ_POS_SHIFT 28 #define RDO_OBJ_POS_MASK 0x7 diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h index 252af3f77039..309251572e2e 100644 --- a/include/linux/usb/typec.h +++ b/include/linux/usb/typec.h @@ -337,6 +337,7 @@ struct typec_plug *typec_register_plug(struct typec_cable *cable, void typec_unregister_plug(struct typec_plug *plug); void typec_set_data_role(struct typec_port *port, enum typec_data_role role); +enum typec_data_role typec_get_data_role(struct typec_port *port); void typec_set_pwr_role(struct typec_port *port, enum typec_role role); void typec_set_vconn_role(struct typec_port *port, enum typec_role role); void typec_set_pwr_opmode(struct typec_port *port, enum typec_pwr_opmode mode); diff --git a/include/linux/usb/typec_altmode.h b/include/linux/usb/typec_altmode.h index b3c0866ea70f..f7db3bd4c90e 100644 --- a/include/linux/usb/typec_altmode.h +++ b/include/linux/usb/typec_altmode.h @@ -172,6 +172,19 @@ typec_altmode_get_svdm_version(struct typec_altmode *altmode) return typec_get_negotiated_svdm_version(typec_altmode2port(altmode)); } +/** + * typec_altmode_get_data_role - Get port data role + * @altmode: Handle to the alternate mode + * + * Alt Mode drivers should only issue Enter Mode through the port if they are + * the DFP. + */ +static inline enum typec_data_role +typec_altmode_get_data_role(struct typec_altmode *altmode) +{ + return typec_get_data_role(typec_altmode2port(altmode)); +} + /** * struct typec_altmode_driver - USB Type-C alternate mode device driver * @id_table: Null terminated array of SVIDs diff --git a/include/linux/usb/typec_tbt.h b/include/linux/usb/typec_tbt.h index 55dcea12082c..0b570f1b8bc8 100644 --- a/include/linux/usb/typec_tbt.h +++ b/include/linux/usb/typec_tbt.h @@ -55,6 +55,7 @@ struct typec_thunderbolt_data { /* TBT3 Device Enter Mode VDO bits */ #define TBT_ENTER_MODE_CABLE_SPEED(s) TBT_SET_CABLE_SPEED(s) +#define TBT_ENTER_MODE_UNI_DIR_LSRX BIT(23) #define TBT_ENTER_MODE_ACTIVE_CABLE BIT(24) #endif /* __USB_TYPEC_TBT_H */ diff --git a/include/uapi/linux/usb/cdc.h b/include/uapi/linux/usb/cdc.h index 1924cf665448..7bd5d12d8b26 100644 --- a/include/uapi/linux/usb/cdc.h +++ b/include/uapi/linux/usb/cdc.h @@ -104,8 +104,10 @@ struct usb_cdc_union_desc { __u8 bDescriptorSubType; __u8 bMasterInterface0; - __u8 bSlaveInterface0; - /* ... and there could be other slave interfaces */ + union { + __u8 bSlaveInterface0; + __DECLARE_FLEX_ARRAY(__u8, bSlaveInterfaces); + }; } __attribute__ ((packed)); /* "Country Selection Functional Descriptor" from CDC spec 5.2.3.9 */ @@ -115,8 +117,10 @@ struct usb_cdc_country_functional_desc { __u8 bDescriptorSubType; __u8 iCountryCodeRelDate; - __le16 wCountyCode0; - /* ... and there can be a lot of country codes */ + union { + __le16 wCountryCode0; + __DECLARE_FLEX_ARRAY(__le16, wCountryCodes); + }; } __attribute__ ((packed)); /* "Network Channel Terminal Functional Descriptor" from CDC spec 5.2.3.11 */ diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 5ea7b49f7dd9..a067038b4b42 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -82,6 +82,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index 014f20df9148..79c72762ad9c 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -57,6 +57,7 @@ #include "task.c" #include "time.c" #include "uaccess.c" +#include "usb.c" #include "vmalloc.c" #include "wait.c" #include "workqueue.c" diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 6083dec1f190..f812cf120042 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -149,6 +149,8 @@ pub mod tracepoint; pub mod transmute; pub mod types; pub mod uaccess; +#[cfg(CONFIG_USB = "y")] +pub mod usb; pub mod workqueue; pub mod xarray; diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig index cde1dc9451d3..3efa51bfc8ef 100644 --- a/samples/rust/Kconfig +++ b/samples/rust/Kconfig @@ -130,7 +130,7 @@ config SAMPLE_RUST_DRIVER_PLATFORM config SAMPLE_RUST_DRIVER_USB tristate "USB Driver" - depends on USB = y && BROKEN + depends on USB = y help This option builds the Rust USB driver sample.