diff --git a/Documentation/ABI/testing/configfs-usb-gadget-acm b/Documentation/ABI/testing/configfs-usb-gadget-acm index d21092d75a05..25e68be9eb66 100644 --- a/Documentation/ABI/testing/configfs-usb-gadget-acm +++ b/Documentation/ABI/testing/configfs-usb-gadget-acm @@ -6,3 +6,10 @@ Description: This item contains just one readonly attribute: port_num. It contains the port number of the /dev/ttyGS device associated with acm function's instance "name". + +What: /config/usb-gadget/gadget/functions/acm.name/protocol +Date: Aug 2024 +KernelVersion: 6.13 +Description: + Reported bInterfaceProtocol for the ACM device. For legacy + reasons, this defaults to 1 (USB_CDC_ACM_PROTO_AT_V25TER). diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uac1 b/Documentation/ABI/testing/configfs-usb-gadget-uac1 index c4ba92f004c3..64188a85592b 100644 --- a/Documentation/ABI/testing/configfs-usb-gadget-uac1 +++ b/Documentation/ABI/testing/configfs-usb-gadget-uac1 @@ -30,4 +30,12 @@ Description: req_number the number of pre-allocated requests for both capture and playback function_name name of the interface + p_it_name playback input terminal name + p_it_ch_name playback channels name + p_ot_name playback output terminal name + p_fu_vol_name playback mute/volume functional unit name + c_it_name capture input terminal name + c_it_ch_name capture channels name + c_ot_name capture output terminal name + c_fu_vol_name capture mute/volume functional unit name ===================== ======================================= diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uac2 b/Documentation/ABI/testing/configfs-usb-gadget-uac2 index a2bf4fd82a5b..133e995c3e92 100644 --- a/Documentation/ABI/testing/configfs-usb-gadget-uac2 +++ b/Documentation/ABI/testing/configfs-usb-gadget-uac2 @@ -35,6 +35,17 @@ Description: req_number the number of pre-allocated requests for both capture and playback function_name name of the interface + if_ctrl_name topology control name + clksrc_in_name input clock name + clksrc_out_name output clock name + p_it_name playback input terminal name + p_it_ch_name playback input first channel name + p_ot_name playback output terminal name + p_fu_vol_name playback mute/volume function unit name + c_it_name capture input terminal name + c_it_ch_name capture input first channel name + c_ot_name capture output terminal name + c_fu_vol_name capture mute/volume functional unit name c_terminal_type code of the capture terminal type p_terminal_type code of the playback terminal type ===================== ======================================= diff --git a/Documentation/devicetree/bindings/input/goodix,gt7986u.yaml b/Documentation/devicetree/bindings/input/goodix,gt7986u.yaml deleted file mode 100644 index a7d42a5d6128..000000000000 --- a/Documentation/devicetree/bindings/input/goodix,gt7986u.yaml +++ /dev/null @@ -1,71 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/input/goodix,gt7986u.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: GOODIX GT7986U SPI HID Touchscreen - -maintainers: - - Charles Wang - -description: Supports the Goodix GT7986U touchscreen. - This touch controller reports data packaged according to the HID protocol, - but is incompatible with Microsoft's HID-over-SPI protocol. - -allOf: - - $ref: /schemas/spi/spi-peripheral-props.yaml# - -properties: - compatible: - enum: - - goodix,gt7986u - - reg: - maxItems: 1 - - interrupts: - maxItems: 1 - - reset-gpios: - maxItems: 1 - - goodix,hid-report-addr: - $ref: /schemas/types.yaml#/definitions/uint32 - description: - The register address for retrieving HID report data. - This address is related to the device firmware and may - change after a firmware update. - - spi-max-frequency: true - -additionalProperties: false - -required: - - compatible - - reg - - interrupts - - reset-gpios - - goodix,hid-report-addr - -examples: - - | - #include - #include - - spi { - #address-cells = <1>; - #size-cells = <0>; - - touchscreen@0 { - compatible = "goodix,gt7986u"; - reg = <0>; - interrupt-parent = <&gpio>; - interrupts = <25 IRQ_TYPE_LEVEL_LOW>; - reset-gpios = <&gpio1 1 GPIO_ACTIVE_LOW>; - spi-max-frequency = <10000000>; - goodix,hid-report-addr = <0x22c8c>; - }; - }; - -... diff --git a/Documentation/devicetree/bindings/phy/fsl,mxs-usbphy.yaml b/Documentation/devicetree/bindings/phy/fsl,mxs-usbphy.yaml index f4b1ca2fb562..ce665a2779b7 100644 --- a/Documentation/devicetree/bindings/phy/fsl,mxs-usbphy.yaml +++ b/Documentation/devicetree/bindings/phy/fsl,mxs-usbphy.yaml @@ -87,6 +87,12 @@ properties: maximum: 119 default: 100 + nxp,sim: + description: + The system integration module (SIM) provides system control and chip + configuration registers. + $ref: /schemas/types.yaml#/definitions/phandle + required: - compatible - reg @@ -110,6 +116,17 @@ allOf: required: - fsl,anatop + - if: + properties: + compatible: + const: fsl,imx7ulp-usbphy + then: + required: + - nxp,sim + else: + properties: + nxp,sim: false + additionalProperties: false examples: diff --git a/Documentation/devicetree/bindings/rtc/microcrystal,rv3028.yaml b/Documentation/devicetree/bindings/rtc/microcrystal,rv3028.yaml index 5ade5dfad048..cda8ad7c1203 100644 --- a/Documentation/devicetree/bindings/rtc/microcrystal,rv3028.yaml +++ b/Documentation/devicetree/bindings/rtc/microcrystal,rv3028.yaml @@ -22,6 +22,9 @@ properties: interrupts: maxItems: 1 + "#clock-cells": + const: 0 + trickle-resistor-ohms: enum: - 3000 diff --git a/Documentation/devicetree/bindings/rtc/sprd,sc2731-rtc.yaml b/Documentation/devicetree/bindings/rtc/sprd,sc2731-rtc.yaml new file mode 100644 index 000000000000..f3d20e976965 --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/sprd,sc2731-rtc.yaml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/rtc/sprd,sc2731-rtc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Spreadtrum SC2731 Real Time Clock + +maintainers: + - Orson Zhai + - Baolin Wang + - Chunyan Zhang + +properties: + compatible: + const: sprd,sc2731-rtc + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + +allOf: + - $ref: rtc.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + + pmic { + #address-cells = <1>; + #size-cells = <0>; + + rtc@280 { + compatible = "sprd,sc2731-rtc"; + reg = <0x280>; + interrupt-parent = <&sc2731_pmic>; + interrupts = <2 IRQ_TYPE_LEVEL_HIGH>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/rtc/sprd,sc27xx-rtc.txt b/Documentation/devicetree/bindings/rtc/sprd,sc27xx-rtc.txt deleted file mode 100644 index 1f5754299d31..000000000000 --- a/Documentation/devicetree/bindings/rtc/sprd,sc27xx-rtc.txt +++ /dev/null @@ -1,26 +0,0 @@ -Spreadtrum SC27xx Real Time Clock - -Required properties: -- compatible: should be "sprd,sc2731-rtc". -- reg: address offset of rtc register. -- interrupts: rtc alarm interrupt. - -Example: - - sc2731_pmic: pmic@0 { - compatible = "sprd,sc2731"; - reg = <0>; - spi-max-frequency = <26000000>; - interrupts = ; - interrupt-controller; - #interrupt-cells = <2>; - #address-cells = <1>; - #size-cells = <0>; - - rtc@280 { - compatible = "sprd,sc2731-rtc"; - reg = <0x280>; - interrupt-parent = <&sc2731_pmic>; - interrupts = <2 IRQ_TYPE_LEVEL_HIGH>; - }; - }; diff --git a/Documentation/devicetree/bindings/rtc/st,stm32-rtc.yaml b/Documentation/devicetree/bindings/rtc/st,stm32-rtc.yaml index 7a0fab721cf1..aae06e570c22 100644 --- a/Documentation/devicetree/bindings/rtc/st,stm32-rtc.yaml +++ b/Documentation/devicetree/bindings/rtc/st,stm32-rtc.yaml @@ -53,6 +53,28 @@ properties: override default rtc_ck parent clock phandle of the new parent clock of rtc_ck maxItems: 1 +patternProperties: + "^rtc-[a-z]+-[0-9]+$": + type: object + $ref: /schemas/pinctrl/pinmux-node.yaml + description: | + Configuration of STM32 RTC pins description. STM32 RTC is able to output + some signals on specific pins: + - LSCO (Low Speed Clock Output) that allow to output LSE clock on a pin. + - Alarm out that allow to send a pulse on a pin when alarm A of the RTC + expires. + additionalProperties: false + properties: + function: + enum: + - lsco + - alarm-a + pins: + enum: + - out1 + - out2 + - out2_rmp + allOf: - if: properties: @@ -68,6 +90,9 @@ allOf: clock-names: false + patternProperties: + "^rtc-[a-z]+-[0-9]+$": false + required: - st,syscfg @@ -83,6 +108,9 @@ allOf: minItems: 2 maxItems: 2 + patternProperties: + "^rtc-[a-z]+-[0-9]+$": false + required: - clock-names - st,syscfg diff --git a/Documentation/devicetree/bindings/rtc/trivial-rtc.yaml b/Documentation/devicetree/bindings/rtc/trivial-rtc.yaml index fffd759c603f..7330a7200831 100644 --- a/Documentation/devicetree/bindings/rtc/trivial-rtc.yaml +++ b/Documentation/devicetree/bindings/rtc/trivial-rtc.yaml @@ -38,12 +38,13 @@ properties: - dallas,ds1672 # Extremely Accurate I²C RTC with Integrated Crystal and SRAM - dallas,ds3232 + # SD2405AL Real-Time Clock + - dfrobot,sd2405al # EM Microelectronic EM3027 RTC - emmicro,em3027 # I2C-BUS INTERFACE REAL TIME CLOCK MODULE - epson,rx8010 # I2C-BUS INTERFACE REAL TIME CLOCK MODULE - - epson,rx8025 - epson,rx8035 # I2C-BUS INTERFACE REAL TIME CLOCK MODULE with Battery Backed RAM - epson,rx8111 @@ -52,10 +53,6 @@ properties: - epson,rx8581 # Android Goldfish Real-time Clock - google,goldfish-rtc - # Intersil ISL1208 Low Power RTC with Battery Backed SRAM - - isil,isl1208 - # Intersil ISL1218 Low Power RTC with Battery Backed SRAM - - isil,isl1218 # Mvebu Real-time Clock - marvell,orion-rtc # Maxim DS1742/DS1743 Real-time Clock @@ -68,8 +65,6 @@ properties: - microcrystal,rv8523 # NXP LPC32xx SoC Real-time Clock - nxp,lpc3220-rtc - # Real-time Clock Module - - pericom,pt7c4338 # I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC - ricoh,r2025sd # I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC diff --git a/Documentation/devicetree/bindings/serial/8250_omap.yaml b/Documentation/devicetree/bindings/serial/8250_omap.yaml index 6a7be42da523..4b78de6b46a2 100644 --- a/Documentation/devicetree/bindings/serial/8250_omap.yaml +++ b/Documentation/devicetree/bindings/serial/8250_omap.yaml @@ -76,6 +76,7 @@ properties: clock-frequency: true current-speed: true overrun-throttle-ms: true + wakeup-source: true required: - compatible diff --git a/Documentation/devicetree/bindings/serial/atmel,at91-usart.yaml b/Documentation/devicetree/bindings/serial/atmel,at91-usart.yaml index eb2992a447d7..f466c38518c4 100644 --- a/Documentation/devicetree/bindings/serial/atmel,at91-usart.yaml +++ b/Documentation/devicetree/bindings/serial/atmel,at91-usart.yaml @@ -23,13 +23,20 @@ properties: - const: atmel,at91sam9260-dbgu - const: atmel,at91sam9260-usart - items: - - const: microchip,sam9x60-usart + - enum: + - microchip,sam9x60-usart + - microchip,sam9x7-usart - const: atmel,at91sam9260-usart - items: - const: microchip,sam9x60-dbgu - const: microchip,sam9x60-usart - const: atmel,at91sam9260-dbgu - const: atmel,at91sam9260-usart + - items: + - const: microchip,sam9x7-dbgu + - const: atmel,at91sam9260-dbgu + - const: microchip,sam9x7-usart + - const: atmel,at91sam9260-usart reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/serial/mediatek,uart.yaml b/Documentation/devicetree/bindings/serial/mediatek,uart.yaml index ff61ffdcad1d..1b02f0b197ff 100644 --- a/Documentation/devicetree/bindings/serial/mediatek,uart.yaml +++ b/Documentation/devicetree/bindings/serial/mediatek,uart.yaml @@ -36,6 +36,7 @@ properties: - mediatek,mt7622-uart - mediatek,mt7623-uart - mediatek,mt7629-uart + - mediatek,mt7981-uart - mediatek,mt7986-uart - mediatek,mt7988-uart - mediatek,mt8127-uart diff --git a/Documentation/devicetree/bindings/serial/renesas,scif.yaml b/Documentation/devicetree/bindings/serial/renesas,scif.yaml index afc7c05898a1..51d9fb0f4763 100644 --- a/Documentation/devicetree/bindings/serial/renesas,scif.yaml +++ b/Documentation/devicetree/bindings/serial/renesas,scif.yaml @@ -46,6 +46,7 @@ properties: - items: - enum: - renesas,scif-r8a774a1 # RZ/G2M + - renesas,scif-r8a774a3 # RZ/G2M v3.0 - renesas,scif-r8a774b1 # RZ/G2N - renesas,scif-r8a774c0 # RZ/G2E - renesas,scif-r8a774e1 # RZ/G2H diff --git a/Documentation/devicetree/bindings/serial/samsung_uart.yaml b/Documentation/devicetree/bindings/serial/samsung_uart.yaml index 0f0131026911..788c80e47831 100644 --- a/Documentation/devicetree/bindings/serial/samsung_uart.yaml +++ b/Documentation/devicetree/bindings/serial/samsung_uart.yaml @@ -56,14 +56,8 @@ properties: maxItems: 5 clock-names: - description: N = 0 is allowed for SoCs without internal baud clock mux. minItems: 2 - items: - - const: uart - - pattern: '^clk_uart_baud[0-3]$' - - pattern: '^clk_uart_baud[0-3]$' - - pattern: '^clk_uart_baud[0-3]$' - - pattern: '^clk_uart_baud[0-3]$' + maxItems: 5 dmas: items: @@ -103,18 +97,45 @@ allOf: compatible: contains: enum: - - samsung,s5pv210-uart + - samsung,s3c6400-uart then: properties: clocks: - minItems: 2 + minItems: 3 maxItems: 3 + + clock-names: + items: + - const: uart + - const: clk_uart_baud2 + - const: clk_uart_baud3 + + else: + properties: clock-names: minItems: 2 items: - const: uart - - pattern: '^clk_uart_baud[0-1]$' - - pattern: '^clk_uart_baud[0-1]$' + - const: clk_uart_baud0 + - const: clk_uart_baud1 + - const: clk_uart_baud2 + - const: clk_uart_baud3 + + - if: + properties: + compatible: + contains: + enum: + - samsung,s5pv210-uart + then: + properties: + clocks: + minItems: 3 + maxItems: 3 + + clock-names: + minItems: 3 + maxItems: 3 - if: properties: @@ -129,10 +150,9 @@ allOf: properties: clocks: maxItems: 2 + clock-names: - items: - - const: uart - - const: clk_uart_baud0 + maxItems: 2 - if: properties: @@ -146,6 +166,12 @@ allOf: properties: reg-io-width: false + clocks: + maxItems: 2 + + clock-names: + maxItems: 2 + unevaluatedProperties: false examples: @@ -163,3 +189,19 @@ examples: <&clocks SCLK_UART>; samsung,uart-fifosize = <16>; }; + - | + #include + #include + #include + + serial_0: serial@10a00000 { + compatible = "google,gs101-uart"; + reg = <0x10a00000 0xc0>; + clocks = <&cmu_peric0 CLK_GOUT_PERIC0_PERIC0_TOP1_PCLK_0>, + <&cmu_peric0 CLK_GOUT_PERIC0_PERIC0_TOP1_IPCLK_0>; + clock-names = "uart", "clk_uart_baud0"; + interrupts = ; + pinctrl-0 = <&uart0_bus>; + pinctrl-names = "default"; + samsung,uart-fifosize = <256>; + }; diff --git a/Documentation/devicetree/bindings/usb/fsl,ls1028a.yaml b/Documentation/devicetree/bindings/usb/fsl,ls1028a.yaml new file mode 100644 index 000000000000..a44bdf391887 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/fsl,ls1028a.yaml @@ -0,0 +1,52 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/fsl,ls1028a.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +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 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + +unevaluatedProperties: false + +required: + - compatible + - reg + - interrupts + +allOf: + - $ref: snps,dwc3.yaml# + +examples: + - | + #include + + usb@fe800000 { + compatible = "fsl,ls1028a-dwc3", "snps,dwc3"; + reg = <0xfe800000 0x100000>; + interrupts = ; + }; diff --git a/Documentation/devicetree/bindings/usb/msm-hsusb.txt b/Documentation/devicetree/bindings/usb/msm-hsusb.txt deleted file mode 100644 index afc30e98b123..000000000000 --- a/Documentation/devicetree/bindings/usb/msm-hsusb.txt +++ /dev/null @@ -1,110 +0,0 @@ -MSM SoC HSUSB controllers - -EHCI - -Required properties: -- compatible: Should contain "qcom,ehci-host" -- regs: offset and length of the register set in the memory map -- usb-phy: phandle for the PHY device - -Example EHCI controller device node: - - ehci: ehci@f9a55000 { - compatible = "qcom,ehci-host"; - reg = <0xf9a55000 0x400>; - usb-phy = <&usb_otg>; - }; - -USB PHY with optional OTG: - -Required properties: -- compatible: Should contain: - "qcom,usb-otg-ci" for chipsets with ChipIdea 45nm PHY - "qcom,usb-otg-snps" for chipsets with Synopsys 28nm PHY - -- regs: Offset and length of the register set in the memory map -- interrupts: interrupt-specifier for the OTG interrupt. - -- clocks: A list of phandle + clock-specifier pairs for the - clocks listed in clock-names -- clock-names: Should contain the following: - "phy" USB PHY reference clock - "core" Protocol engine clock - "iface" Interface bus clock - "alt_core" Protocol engine clock for targets with asynchronous - reset methodology. (optional) - -- vdccx-supply: phandle to the regulator for the vdd supply for - digital circuit operation. -- v1p8-supply: phandle to the regulator for the 1.8V supply -- v3p3-supply: phandle to the regulator for the 3.3V supply - -- resets: A list of phandle + reset-specifier pairs for the - resets listed in reset-names -- reset-names: Should contain the following: - "phy" USB PHY controller reset - "link" USB LINK controller reset - -- qcom,otg-control: OTG control (VBUS and ID notifications) can be one of - 1 - PHY control - 2 - PMIC control - -Optional properties: -- dr_mode: One of "host", "peripheral" or "otg". Defaults to "otg" - -- switch-gpio: A phandle + gpio-specifier pair. Some boards are using Dual - SPDT USB Switch, witch is controlled by GPIO to de/multiplex - D+/D- USB lines between connectors. - -- qcom,phy-init-sequence: PHY configuration sequence values. This is related to Device - Mode Eye Diagram test. Start address at which these values will be - written is ULPI_EXT_VENDOR_SPECIFIC. Value of -1 is reserved as - "do not overwrite default value at this address". - For example: qcom,phy-init-sequence = < -1 0x63 >; - Will update only value at address ULPI_EXT_VENDOR_SPECIFIC + 1. - -- qcom,phy-num: Select number of pyco-phy to use, can be one of - 0 - PHY one, default - 1 - Second PHY - Some platforms may have configuration to allow USB - controller work with any of the two HSPHYs present. - -- qcom,vdd-levels: This property must be a list of three integer values - (no, min, max) where each value represents either a voltage - in microvolts or a value corresponding to voltage corner. - -- qcom,manual-pullup: If present, vbus is not routed to USB controller/phy - and controller driver therefore enables pull-up explicitly - before starting controller using usbcmd run/stop bit. - -- extcon: phandles to external connector devices. First phandle - should point to external connector, which provide "USB" - cable events, the second should point to external connector - device, which provide "USB-HOST" cable events. If one of - the external connector devices is not required empty <0> - phandle should be specified. - -Example HSUSB OTG controller device node: - - usb@f9a55000 { - compatible = "qcom,usb-otg-snps"; - reg = <0xf9a55000 0x400>; - interrupts = <0 134 0>; - dr_mode = "peripheral"; - - clocks = <&gcc GCC_XO_CLK>, <&gcc GCC_USB_HS_SYSTEM_CLK>, - <&gcc GCC_USB_HS_AHB_CLK>; - - clock-names = "phy", "core", "iface"; - - vddcx-supply = <&pm8841_s2_corner>; - v1p8-supply = <&pm8941_l6>; - v3p3-supply = <&pm8941_l24>; - - resets = <&gcc GCC_USB2A_PHY_BCR>, <&gcc GCC_USB_HS_BCR>; - reset-names = "phy", "link"; - - qcom,otg-control = <1>; - qcom,phy-init-sequence = < -1 0x63 >; - qcom,vdd-levels = <1 5 7>; - }; diff --git a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml index aef42dacc202..18758efb8d29 100644 --- a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml @@ -52,6 +52,7 @@ properties: - qcom,sm8550-dwc3 - qcom,sm8650-dwc3 - qcom,x1e80100-dwc3 + - qcom,x1e80100-dwc3-mp - const: qcom,dwc3 reg: @@ -289,6 +290,7 @@ allOf: - qcom,sc8280xp-dwc3 - qcom,sc8280xp-dwc3-mp - qcom,x1e80100-dwc3 + - qcom,x1e80100-dwc3-mp then: properties: clocks: @@ -428,6 +430,21 @@ allOf: contains: enum: - qcom,ipq5332-dwc3 + then: + properties: + interrupts: + maxItems: 3 + interrupt-names: + items: + - const: pwr_event + - const: dp_hs_phy_irq + - const: dm_hs_phy_irq + + - if: + properties: + compatible: + contains: + enum: - qcom,x1e80100-dwc3 then: properties: @@ -486,6 +503,7 @@ allOf: contains: enum: - qcom,sc8180x-dwc3-mp + - qcom,x1e80100-dwc3-mp then: properties: interrupts: diff --git a/Documentation/devicetree/bindings/usb/ti,j721e-usb.yaml b/Documentation/devicetree/bindings/usb/ti,j721e-usb.yaml index 95ff9791baea..653a89586f4e 100644 --- a/Documentation/devicetree/bindings/usb/ti,j721e-usb.yaml +++ b/Documentation/devicetree/bindings/usb/ti,j721e-usb.yaml @@ -13,10 +13,9 @@ properties: compatible: oneOf: - const: ti,j721e-usb - - const: ti,am64-usb - items: - - const: ti,j721e-usb - const: ti,am64-usb + - const: ti,j721e-usb reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index 56d180f9c1cc..adb15f4c8f6c 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -368,6 +368,8 @@ patternProperties: description: Devantech, Ltd. "^dfi,.*": description: DFI Inc. + "^dfrobot,.*": + description: DFRobot Corporation "^dh,.*": description: DH electronics GmbH "^difrnce,.*": diff --git a/Documentation/filesystems/9p.rst b/Documentation/filesystems/9p.rst index d270a0aa8d55..2bbf68b56b0d 100644 --- a/Documentation/filesystems/9p.rst +++ b/Documentation/filesystems/9p.rst @@ -48,11 +48,66 @@ For server running on QEMU host with virtio transport:: mount -t 9p -o trans=virtio /mnt/9 -where mount_tag is the tag associated by the server to each of the exported +where mount_tag is the tag generated by the server to each of the exported mount points. Each 9P export is seen by the client as a virtio device with an associated "mount_tag" property. Available mount tags can be seen by reading /sys/bus/virtio/drivers/9pnet_virtio/virtio/mount_tag files. +USBG Usage +========== + +To mount a 9p FS on a USB Host accessible via the gadget at runtime:: + + mount -t 9p -o trans=usbg,aname=/path/to/fs /mnt/9 + +To mount a 9p FS on a USB Host accessible via the gadget as root filesystem:: + + root= rootfstype=9p rootflags=trans=usbg,cache=loose,uname=root,access=0,dfltuid=0,dfltgid=0,aname=/path/to/rootfs + +where is the tag associated by the usb gadget transport. +It is defined by the configfs instance name. + +USBG Example +============ + +The USB host exports a filesystem, while the gadget on the USB device +side makes it mountable. + +Diod (9pfs server) and the forwarder are on the development host, where +the root filesystem is actually stored. The gadget is initialized during +boot (or later) on the embedded board. Then the forwarder will find it +on the USB bus and start forwarding requests. + +In this case the 9p requests come from the device and are handled by the +host. The reason is that USB device ports are normally not available on +PCs, so a connection in the other direction would not work. + +When using the usbg transport, for now there is no native usb host +service capable to handle the requests from the gadget driver. For +this we have to use the extra python tool p9_fwd.py from tools/usb. + +Just start the 9pfs capable network server like diod/nfs-ganesha e.g.:: + + $ diod -f -n -d 0 -S -l 0.0.0.0:9999 -e $PWD + +Optionaly scan your bus if there are more then one usbg gadgets to find their path:: + + $ python $kernel_dir/tools/usb/p9_fwd.py list + + Bus | Addr | Manufacturer | Product | ID | Path + --- | ---- | ---------------- | ---------------- | --------- | ---- + 2 | 67 | unknown | unknown | 1d6b:0109 | 2-1.1.2 + 2 | 68 | unknown | unknown | 1d6b:0109 | 2-1.1.3 + +Then start the python transport:: + + $ python $kernel_dir/tools/usb/p9_fwd.py --path 2-1.1.2 connect -p 9999 + +After that the gadget driver can be used as described above. + +One use-case is to use it as an alternative to NFS root booting during +the development of embedded Linux devices. + Options ======= @@ -68,6 +123,7 @@ Options virtio connect to the next virtio channel available (from QEMU with trans_virtio module) rdma connect to a specified RDMA channel + usbg connect to a specified usb gadget channel ======== ============================================ uname=name user name to attempt mount as on the remote server. The diff --git a/Documentation/usb/functionfs-desc.rst b/Documentation/usb/functionfs-desc.rst new file mode 100644 index 000000000000..39649774da54 --- /dev/null +++ b/Documentation/usb/functionfs-desc.rst @@ -0,0 +1,39 @@ +====================== +FunctionFS Descriptors +====================== + +Some of the descriptors that can be written to the FFS gadget are +described below. Device and configuration descriptors are handled +by the composite gadget and are not written by the user to the +FFS gadget. + +Descriptors are written to the "ep0" file in the FFS gadget +following the descriptor header. + +.. kernel-doc:: include/uapi/linux/usb/functionfs.h + :doc: descriptors + +Interface Descriptors +--------------------- + +Standard USB interface descriptors may be written. The class/subclass of the +most recent interface descriptor determines what type of class-specific +descriptors are accepted. + +Class-Specific Descriptors +-------------------------- + +Class-specific descriptors are accepted only for the class/subclass of the +most recent interface descriptor. The following are some of the +class-specific descriptors that are supported. + +DFU Functional Descriptor +~~~~~~~~~~~~~~~~~~~~~~~~~ + +When the interface class is USB_CLASS_APP_SPEC and the interface subclass +is USB_SUBCLASS_DFU, a DFU functional descriptor can be provided. +The DFU functional descriptor is a described in the USB specification for +Device Firmware Upgrade (DFU), version 1.1 as of this writing. + +.. kernel-doc:: include/uapi/linux/usb/functionfs.h + :doc: usb_dfu_functional_descriptor diff --git a/Documentation/usb/functionfs.rst b/Documentation/usb/functionfs.rst index d05a775bc45b..f7487b0d8057 100644 --- a/Documentation/usb/functionfs.rst +++ b/Documentation/usb/functionfs.rst @@ -25,6 +25,8 @@ interface numbers starting from zero). The FunctionFS changes them as needed also handling situation when numbers differ in different configurations. +For more information about FunctionFS descriptors see :doc:`functionfs-desc` + When descriptors and strings are written "ep#" files appear (one for each declared endpoint) which handle communication on a single endpoint. Again, FunctionFS takes care of the real diff --git a/Documentation/usb/gadget-testing.rst b/Documentation/usb/gadget-testing.rst index b086c7ab72f0..bf555c2270f5 100644 --- a/Documentation/usb/gadget-testing.rst +++ b/Documentation/usb/gadget-testing.rst @@ -765,6 +765,17 @@ The uac2 function provides these attributes in its function directory: req_number the number of pre-allocated request for both capture and playback function_name name of the interface + if_ctrl_name topology control name + clksrc_in_name input clock name + clksrc_out_name output clock name + p_it_name playback input terminal name + p_it_ch_name playback input first channel name + p_ot_name playback output terminal name + p_fu_vol_name playback function unit name + c_it_name capture input terminal name + c_it_ch_name capture input first channel name + c_ot_name capture output terminal name + c_fu_vol_name capture functional unit name c_terminal_type code of the capture terminal type p_terminal_type code of the playback terminal type ================ ==================================================== @@ -957,6 +968,14 @@ The uac1 function provides these attributes in its function directory: req_number the number of pre-allocated requests for both capture and playback function_name name of the interface + p_it_name playback input terminal name + p_it_ch_name playback channels name + p_ot_name playback output terminal name + p_fu_vol_name playback mute/volume functional unit name + c_it_name capture input terminal name + c_it_ch_name capture channels name + c_ot_name capture output terminal name + c_fu_vol_name capture mute/volume functional unit name ================ ==================================================== The attributes have sane default values. diff --git a/Documentation/usb/index.rst b/Documentation/usb/index.rst index 27955dad95e1..826492c813ac 100644 --- a/Documentation/usb/index.rst +++ b/Documentation/usb/index.rst @@ -11,6 +11,7 @@ USB support dwc3 ehci functionfs + functionfs-desc gadget_configfs gadget_hid gadget_multi diff --git a/MAINTAINERS b/MAINTAINERS index 9f622c921893..4ef7fe5a209c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6557,6 +6557,12 @@ F: include/net/devlink.h F: include/uapi/linux/devlink.h F: net/devlink/ +DFROBOT SD2405AL RTC DRIVER +M: Tóth János +L: linux-rtc@vger.kernel.org +S: Maintained +F: drivers/rtc/rtc-sd2405al.c + DH ELECTRONICS IMX6 DHCOM/DHCOR BOARD SUPPORT M: Christoph Niedermaier L: kernel@dh-electronics.com @@ -24465,6 +24471,7 @@ F: include/linux/vdpa.h F: include/linux/virtio*.h F: include/linux/vringh.h F: include/uapi/linux/virtio_*.h +F: net/vmw_vsock/virtio* F: tools/virtio/ F: tools/testing/selftests/drivers/net/virtio_net/ diff --git a/arch/arm64/boot/dts/mediatek/mt7981b.dtsi b/arch/arm64/boot/dts/mediatek/mt7981b.dtsi index b096009ef99c..5cbea9cd411f 100644 --- a/arch/arm64/boot/dts/mediatek/mt7981b.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt7981b.dtsi @@ -94,6 +94,39 @@ #pwm-cells = <2>; }; + serial@11002000 { + compatible = "mediatek,mt7981-uart", "mediatek,mt6577-uart"; + reg = <0 0x11002000 0 0x100>; + interrupts = ; + interrupt-names = "uart", "wakeup"; + clocks = <&infracfg CLK_INFRA_UART0_SEL>, + <&infracfg CLK_INFRA_UART0_CK>; + clock-names = "baud", "bus"; + status = "disabled"; + }; + + serial@11003000 { + compatible = "mediatek,mt7981-uart", "mediatek,mt6577-uart"; + reg = <0 0x11003000 0 0x100>; + interrupts = ; + interrupt-names = "uart", "wakeup"; + clocks = <&infracfg CLK_INFRA_UART1_SEL>, + <&infracfg CLK_INFRA_UART1_CK>; + clock-names = "baud", "bus"; + status = "disabled"; + }; + + serial@11004000 { + compatible = "mediatek,mt7981-uart", "mediatek,mt6577-uart"; + reg = <0 0x11004000 0 0x100>; + interrupts = ; + interrupt-names = "uart", "wakeup"; + clocks = <&infracfg CLK_INFRA_UART2_SEL>, + <&infracfg CLK_INFRA_UART2_CK>; + clock-names = "baud", "bus"; + status = "disabled"; + }; + i2c@11007000 { compatible = "mediatek,mt7981-i2c"; reg = <0 0x11007000 0 0x1000>, diff --git a/block/bdev.c b/block/bdev.c index 33f9c4605e3a..738e3c8457e7 100644 --- a/block/bdev.c +++ b/block/bdev.c @@ -555,7 +555,7 @@ retry: /* if claiming is already in progress, wait for it to finish */ if (whole->bd_claiming) { - wait_queue_head_t *wq = bit_waitqueue(&whole->bd_claiming, 0); + wait_queue_head_t *wq = __var_waitqueue(&whole->bd_claiming); DEFINE_WAIT(wait); prepare_to_wait(wq, &wait, TASK_UNINTERRUPTIBLE); @@ -578,7 +578,7 @@ static void bd_clear_claiming(struct block_device *whole, void *holder) /* tell others that we're done */ BUG_ON(whole->bd_claiming != holder); whole->bd_claiming = NULL; - wake_up_bit(&whole->bd_claiming, 0); + wake_up_var(&whole->bd_claiming); } /** diff --git a/block/bio-integrity.c b/block/bio-integrity.c index 96a2653905ae..88e3ad73c385 100644 --- a/block/bio-integrity.c +++ b/block/bio-integrity.c @@ -367,7 +367,6 @@ free_bvec: kfree(bvec); return ret; } -EXPORT_SYMBOL_GPL(bio_integrity_map_user); /** * bio_integrity_prep - Prepare bio for integrity I/O diff --git a/block/blk-integrity.c b/block/blk-integrity.c index 010decc892ea..0a2b1c5d0ebf 100644 --- a/block/blk-integrity.c +++ b/block/blk-integrity.c @@ -53,7 +53,6 @@ new_segment: return segments; } -EXPORT_SYMBOL(blk_rq_count_integrity_sg); /** * blk_rq_map_integrity_sg - Map integrity metadata into a scatterlist @@ -63,19 +62,20 @@ EXPORT_SYMBOL(blk_rq_count_integrity_sg); * * Description: Map the integrity vectors in request into a * scatterlist. The scatterlist must be big enough to hold all - * elements. I.e. sized using blk_rq_count_integrity_sg(). + * elements. I.e. sized using blk_rq_count_integrity_sg() or + * rq->nr_integrity_segments. */ -int blk_rq_map_integrity_sg(struct request_queue *q, struct bio *bio, - struct scatterlist *sglist) +int blk_rq_map_integrity_sg(struct request *rq, struct scatterlist *sglist) { struct bio_vec iv, ivprv = { NULL }; + struct request_queue *q = rq->q; struct scatterlist *sg = NULL; + struct bio *bio = rq->bio; unsigned int segments = 0; struct bvec_iter iter; int prev = 0; bio_for_each_integrity_vec(iv, bio, iter) { - if (prev) { if (!biovec_phys_mergeable(q, &ivprv, &iv)) goto new_segment; @@ -103,10 +103,30 @@ new_segment: if (sg) sg_mark_end(sg); + /* + * Something must have been wrong if the figured number of segment + * is bigger than number of req's physical integrity segments + */ + BUG_ON(segments > rq->nr_integrity_segments); + BUG_ON(segments > queue_max_integrity_segments(q)); return segments; } EXPORT_SYMBOL(blk_rq_map_integrity_sg); +int blk_rq_integrity_map_user(struct request *rq, void __user *ubuf, + ssize_t bytes, u32 seed) +{ + int ret = bio_integrity_map_user(rq->bio, ubuf, bytes, seed); + + if (ret) + return ret; + + rq->nr_integrity_segments = blk_rq_count_integrity_sg(rq->q, rq->bio); + rq->cmd_flags |= REQ_INTEGRITY; + return 0; +} +EXPORT_SYMBOL_GPL(blk_rq_integrity_map_user); + bool blk_integrity_merge_rq(struct request_queue *q, struct request *req, struct request *next) { @@ -134,7 +154,6 @@ bool blk_integrity_merge_bio(struct request_queue *q, struct request *req, struct bio *bio) { int nr_integrity_segs; - struct bio *next = bio->bi_next; if (blk_integrity_rq(req) == 0 && bio_integrity(bio) == NULL) return true; @@ -145,16 +164,11 @@ bool blk_integrity_merge_bio(struct request_queue *q, struct request *req, if (bio_integrity(req->bio)->bip_flags != bio_integrity(bio)->bip_flags) return false; - bio->bi_next = NULL; nr_integrity_segs = blk_rq_count_integrity_sg(q, bio); - bio->bi_next = next; - if (req->nr_integrity_segments + nr_integrity_segs > q->limits.max_integrity_segments) return false; - req->nr_integrity_segments += nr_integrity_segs; - return true; } diff --git a/block/blk-merge.c b/block/blk-merge.c index 56769c4bcd79..ad763ec313b6 100644 --- a/block/blk-merge.c +++ b/block/blk-merge.c @@ -639,6 +639,9 @@ static inline int ll_new_hw_segment(struct request *req, struct bio *bio, * counters. */ req->nr_phys_segments += nr_phys_segs; + if (bio_integrity(bio)) + req->nr_integrity_segments += blk_rq_count_integrity_sg(req->q, + bio); return 1; no_merge: @@ -731,6 +734,7 @@ static int ll_merge_requests_fn(struct request_queue *q, struct request *req, /* Merge is OK... */ req->nr_phys_segments = total_phys_segments; + req->nr_integrity_segments += next->nr_integrity_segments; return 1; } diff --git a/block/blk-mq.c b/block/blk-mq.c index 831c5cf5d874..4b2c8e940f59 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -376,9 +376,7 @@ static struct request *blk_mq_rq_ctx_init(struct blk_mq_alloc_data *data, rq->io_start_time_ns = 0; rq->stats_sectors = 0; rq->nr_phys_segments = 0; -#if defined(CONFIG_BLK_DEV_INTEGRITY) rq->nr_integrity_segments = 0; -#endif rq->end_io = NULL; rq->end_io_data = NULL; @@ -2546,6 +2544,9 @@ static void blk_mq_bio_to_request(struct request *rq, struct bio *bio, rq->__sector = bio->bi_iter.bi_sector; rq->write_hint = bio->bi_write_hint; blk_rq_bio_prep(rq, bio, nr_segs); + if (bio_integrity(bio)) + rq->nr_integrity_segments = blk_rq_count_integrity_sg(rq->q, + bio); /* This can't fail, since GFP_NOIO includes __GFP_DIRECT_RECLAIM. */ err = blk_crypto_rq_bio_prep(rq, bio, GFP_NOIO); diff --git a/block/blk-settings.c b/block/blk-settings.c index cd8a8eabc9a5..a446654ddee5 100644 --- a/block/blk-settings.c +++ b/block/blk-settings.c @@ -437,48 +437,6 @@ int queue_limits_set(struct request_queue *q, struct queue_limits *lim) } EXPORT_SYMBOL_GPL(queue_limits_set); -/** - * blk_limits_io_min - set minimum request size for a device - * @limits: the queue limits - * @min: smallest I/O size in bytes - * - * Description: - * Some devices have an internal block size bigger than the reported - * hardware sector size. This function can be used to signal the - * smallest I/O the device can perform without incurring a performance - * penalty. - */ -void blk_limits_io_min(struct queue_limits *limits, unsigned int min) -{ - limits->io_min = min; - - if (limits->io_min < limits->logical_block_size) - limits->io_min = limits->logical_block_size; - - if (limits->io_min < limits->physical_block_size) - limits->io_min = limits->physical_block_size; -} -EXPORT_SYMBOL(blk_limits_io_min); - -/** - * blk_limits_io_opt - set optimal request size for a device - * @limits: the queue limits - * @opt: smallest I/O size in bytes - * - * Description: - * Storage devices may report an optimal I/O size, which is the - * device's preferred unit for sustained I/O. This is rarely reported - * for disk drives. For RAID arrays it is usually the stripe width or - * the internal track size. A properly aligned multiple of - * optimal_io_size is the preferred request size for workloads where - * sustained throughput is desired. - */ -void blk_limits_io_opt(struct queue_limits *limits, unsigned int opt) -{ - limits->io_opt = opt; -} -EXPORT_SYMBOL(blk_limits_io_opt); - static int queue_limit_alignment_offset(const struct queue_limits *lim, sector_t sector) { diff --git a/block/elevator.c b/block/elevator.c index c355b55d0107..4122026b11f1 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -715,7 +715,9 @@ int elv_iosched_load_module(struct gendisk *disk, const char *buf, strscpy(elevator_name, buf, sizeof(elevator_name)); - return request_module("%s-iosched", strstrip(elevator_name)); + request_module("%s-iosched", strstrip(elevator_name)); + + return 0; } ssize_t elv_iosched_store(struct gendisk *disk, const char *buf, diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c index 449123eb54bf..0d74d75260ef 100644 --- a/drivers/block/drbd/drbd_main.c +++ b/drivers/block/drbd/drbd_main.c @@ -3399,10 +3399,12 @@ void drbd_uuid_new_current(struct drbd_device *device) __must_hold(local) void drbd_uuid_set_bm(struct drbd_device *device, u64 val) __must_hold(local) { unsigned long flags; - if (device->ldev->md.uuid[UI_BITMAP] == 0 && val == 0) - return; - spin_lock_irqsave(&device->ldev->md.uuid_lock, flags); + if (device->ldev->md.uuid[UI_BITMAP] == 0 && val == 0) { + spin_unlock_irqrestore(&device->ldev->md.uuid_lock, flags); + return; + } + if (val == 0) { drbd_uuid_move_history(device); device->ldev->md.uuid[UI_HISTORY_START] = device->ldev->md.uuid[UI_BITMAP]; diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 5f43dfa22f79..85c525745b31 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -452,7 +452,7 @@ static void fw_cfg_sysfs_release_entry(struct kobject *kobj) } /* kobj_type: ties together all properties required to register an entry */ -static struct kobj_type fw_cfg_sysfs_entry_ktype = { +static const struct kobj_type fw_cfg_sysfs_entry_ktype = { .default_groups = fw_cfg_sysfs_entry_groups, .sysfs_ops = &fw_cfg_sysfs_attr_ops, .release = fw_cfg_sysfs_release_entry, diff --git a/drivers/hid/hid-goodix-spi.c b/drivers/hid/hid-goodix-spi.c index de655f745d3f..0e59663814dd 100644 --- a/drivers/hid/hid-goodix-spi.c +++ b/drivers/hid/hid-goodix-spi.c @@ -786,14 +786,6 @@ static const struct acpi_device_id goodix_spi_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, goodix_spi_acpi_match); #endif -#ifdef CONFIG_OF -static const struct of_device_id goodix_spi_of_match[] = { - { .compatible = "goodix,gt7986u", }, - { } -}; -MODULE_DEVICE_TABLE(of, goodix_spi_of_match); -#endif - static const struct spi_device_id goodix_spi_ids[] = { { "gt7986u" }, { }, @@ -804,7 +796,6 @@ static struct spi_driver goodix_spi_driver = { .driver = { .name = "goodix-spi-hid", .acpi_match_table = ACPI_PTR(goodix_spi_acpi_match), - .of_match_table = of_match_ptr(goodix_spi_of_match), .pm = pm_sleep_ptr(&goodix_spi_pm_ops), }, .probe = goodix_spi_probe, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c index 9af8ddb4a78f..a64d96effb9e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c @@ -1887,10 +1887,12 @@ static int cmd_exec(struct mlx5_core_dev *dev, void *in, int in_size, void *out, throttle_op = mlx5_cmd_is_throttle_opcode(opcode); if (throttle_op) { - /* atomic context may not sleep */ - if (callback) - return -EINVAL; - down(&dev->cmd.vars.throttle_sem); + if (callback) { + if (down_trylock(&dev->cmd.vars.throttle_sem)) + return -EBUSY; + } else { + down(&dev->cmd.vars.throttle_sem); + } } pages_queue = is_manage_pages(in); @@ -2096,10 +2098,19 @@ static void mlx5_cmd_exec_cb_handler(int status, void *_work) { struct mlx5_async_work *work = _work; struct mlx5_async_ctx *ctx; + struct mlx5_core_dev *dev; + u16 opcode; ctx = work->ctx; - status = cmd_status_err(ctx->dev, status, work->opcode, work->op_mod, work->out); + dev = ctx->dev; + opcode = work->opcode; + status = cmd_status_err(dev, status, work->opcode, work->op_mod, work->out); work->user_callback(status, work); + /* Can't access "work" from this point on. It could have been freed in + * the callback. + */ + if (mlx5_cmd_is_throttle_opcode(opcode)) + up(&dev->cmd.vars.throttle_sem); if (atomic_dec_and_test(&ctx->num_inflight)) complete(&ctx->inflight_done); } diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c index 6ed38a3cdd73..3bf6785f9057 100644 --- a/drivers/net/hamradio/6pack.c +++ b/drivers/net/hamradio/6pack.c @@ -37,8 +37,6 @@ #include #include -#define SIXPACK_VERSION "Revision: 0.3.0" - /* sixpack priority commands */ #define SIXP_SEOF 0x40 /* start and end of a 6pack frame */ #define SIXP_TX_URUN 0x48 /* transmit overrun */ @@ -88,22 +86,18 @@ struct sixpack { struct net_device *dev; /* easy for intr handling */ /* These are pointers to the malloc()ed frame buffers. */ - unsigned char *rbuff; /* receiver buffer */ int rcount; /* received chars counter */ unsigned char *xbuff; /* transmitter buffer */ unsigned char *xhead; /* next byte to XMIT */ int xleft; /* bytes left in XMIT queue */ - unsigned char raw_buf[4]; - unsigned char cooked_buf[400]; + u8 raw_buf[4]; + u8 cooked_buf[400]; unsigned int rx_count; unsigned int rx_count_cooked; spinlock_t rxlock; - int mtu; /* Our mtu (to spot changes!) */ - int buffsize; /* Max buffers sizes */ - unsigned long flags; /* Flag values/ mode etc */ unsigned char mode; /* 6pack mode */ @@ -113,8 +107,8 @@ struct sixpack { unsigned char slottime; unsigned char duplex; unsigned char led_state; - unsigned char status; - unsigned char status1; + u8 status; + u8 status1; unsigned char status2; unsigned char tx_enable; unsigned char tnc_state; @@ -128,7 +122,7 @@ struct sixpack { #define AX25_6PACK_HEADER_LEN 0 -static void sixpack_decode(struct sixpack *, const unsigned char[], int); +static void sixpack_decode(struct sixpack *, const u8 *, size_t); static int encode_sixpack(unsigned char *, unsigned char *, int, unsigned char); /* @@ -167,7 +161,7 @@ static void sp_encaps(struct sixpack *sp, unsigned char *icp, int len) unsigned char *msg, *p = icp; int actual, count; - if (len > sp->mtu) { /* sp->mtu = AX25_MTU = max. PACLEN = 256 */ + if (len > AX25_MTU + 73) { msg = "oversized transmit packet!"; goto out_drop; } @@ -333,7 +327,7 @@ static void sp_bump(struct sixpack *sp, char cmd) { struct sk_buff *skb; int count; - unsigned char *ptr; + u8 *ptr; count = sp->rcount + 1; @@ -431,7 +425,7 @@ static void sixpack_receive_buf(struct tty_struct *tty, const u8 *cp, const u8 *fp, size_t count) { struct sixpack *sp; - int count1; + size_t count1; if (!count) return; @@ -544,7 +538,7 @@ static inline int tnc_init(struct sixpack *sp) */ static int sixpack_open(struct tty_struct *tty) { - char *rbuff = NULL, *xbuff = NULL; + char *xbuff = NULL; struct net_device *dev; struct sixpack *sp; unsigned long len; @@ -574,10 +568,8 @@ static int sixpack_open(struct tty_struct *tty) len = dev->mtu * 2; - rbuff = kmalloc(len + 4, GFP_KERNEL); xbuff = kmalloc(len + 4, GFP_KERNEL); - - if (rbuff == NULL || xbuff == NULL) { + if (xbuff == NULL) { err = -ENOBUFS; goto out_free; } @@ -586,11 +578,8 @@ static int sixpack_open(struct tty_struct *tty) sp->tty = tty; - sp->rbuff = rbuff; sp->xbuff = xbuff; - sp->mtu = AX25_MTU + 73; - sp->buffsize = len; sp->rcount = 0; sp->rx_count = 0; sp->rx_count_cooked = 0; @@ -631,7 +620,6 @@ static int sixpack_open(struct tty_struct *tty) out_free: kfree(xbuff); - kfree(rbuff); free_netdev(dev); @@ -676,7 +664,6 @@ static void sixpack_close(struct tty_struct *tty) del_timer_sync(&sp->resync_t); /* Free all 6pack frame buffers after unreg. */ - kfree(sp->rbuff); kfree(sp->xbuff); free_netdev(sp->dev); @@ -756,21 +743,14 @@ static struct tty_ldisc_ops sp_ldisc = { /* Initialize 6pack control device -- register 6pack line discipline */ -static const char msg_banner[] __initconst = KERN_INFO \ - "AX.25: 6pack driver, " SIXPACK_VERSION "\n"; -static const char msg_regfail[] __initconst = KERN_ERR \ - "6pack: can't register line discipline (err = %d)\n"; - static int __init sixpack_init_driver(void) { int status; - printk(msg_banner); - /* Register the provided line protocol discipline */ status = tty_register_ldisc(&sp_ldisc); if (status) - printk(msg_regfail, status); + pr_err("6pack: can't register line discipline (err = %d)\n", status); return status; } @@ -820,9 +800,9 @@ static int encode_sixpack(unsigned char *tx_buf, unsigned char *tx_buf_raw, /* decode 4 sixpack-encoded bytes into 3 data bytes */ -static void decode_data(struct sixpack *sp, unsigned char inbyte) +static void decode_data(struct sixpack *sp, u8 inbyte) { - unsigned char *buf; + u8 *buf; if (sp->rx_count != 3) { sp->raw_buf[sp->rx_count++] = inbyte; @@ -848,9 +828,9 @@ static void decode_data(struct sixpack *sp, unsigned char inbyte) /* identify and execute a 6pack priority command byte */ -static void decode_prio_command(struct sixpack *sp, unsigned char cmd) +static void decode_prio_command(struct sixpack *sp, u8 cmd) { - int actual; + ssize_t actual; if ((cmd & SIXP_PRIO_DATA_MASK) != 0) { /* idle ? */ @@ -898,9 +878,9 @@ static void decode_prio_command(struct sixpack *sp, unsigned char cmd) /* identify and execute a standard 6pack command byte */ -static void decode_std_command(struct sixpack *sp, unsigned char cmd) +static void decode_std_command(struct sixpack *sp, u8 cmd) { - unsigned char checksum = 0, rest = 0; + u8 checksum = 0, rest = 0; short i; switch (cmd & SIXP_CMD_MASK) { /* normal command */ @@ -948,10 +928,10 @@ static void decode_std_command(struct sixpack *sp, unsigned char cmd) /* decode a 6pack packet */ static void -sixpack_decode(struct sixpack *sp, const unsigned char *pre_rbuff, int count) +sixpack_decode(struct sixpack *sp, const u8 *pre_rbuff, size_t count) { - unsigned char inbyte; - int count1; + size_t count1; + u8 inbyte; for (count1 = 0; count1 < count; count1++) { inbyte = pre_rbuff[count1]; diff --git a/drivers/net/mctp/mctp-serial.c b/drivers/net/mctp/mctp-serial.c index f39bbe255497..e63720ec3238 100644 --- a/drivers/net/mctp/mctp-serial.c +++ b/drivers/net/mctp/mctp-serial.c @@ -64,18 +64,18 @@ struct mctp_serial { u16 txfcs, rxfcs, rxfcs_rcvd; unsigned int txlen, rxlen; unsigned int txpos, rxpos; - unsigned char txbuf[BUFSIZE], + u8 txbuf[BUFSIZE], rxbuf[BUFSIZE]; }; -static bool needs_escape(unsigned char c) +static bool needs_escape(u8 c) { return c == BYTE_ESC || c == BYTE_FRAME; } -static int next_chunk_len(struct mctp_serial *dev) +static unsigned int next_chunk_len(struct mctp_serial *dev) { - int i; + unsigned int i; /* either we have no bytes to send ... */ if (dev->txpos == dev->txlen) @@ -99,7 +99,7 @@ static int next_chunk_len(struct mctp_serial *dev) return i; } -static int write_chunk(struct mctp_serial *dev, unsigned char *buf, int len) +static ssize_t write_chunk(struct mctp_serial *dev, u8 *buf, size_t len) { return dev->tty->ops->write(dev->tty, buf, len); } @@ -108,9 +108,10 @@ static void mctp_serial_tx_work(struct work_struct *work) { struct mctp_serial *dev = container_of(work, struct mctp_serial, tx_work); - unsigned char c, buf[3]; unsigned long flags; - int len, txlen; + ssize_t txlen; + unsigned int len; + u8 c, buf[3]; spin_lock_irqsave(&dev->lock, flags); @@ -293,7 +294,7 @@ static void mctp_serial_rx(struct mctp_serial *dev) dev->netdev->stats.rx_bytes += dev->rxlen; } -static void mctp_serial_push_header(struct mctp_serial *dev, unsigned char c) +static void mctp_serial_push_header(struct mctp_serial *dev, u8 c) { switch (dev->rxpos) { case 0: @@ -323,7 +324,7 @@ static void mctp_serial_push_header(struct mctp_serial *dev, unsigned char c) } } -static void mctp_serial_push_trailer(struct mctp_serial *dev, unsigned char c) +static void mctp_serial_push_trailer(struct mctp_serial *dev, u8 c) { switch (dev->rxpos) { case 0: @@ -347,7 +348,7 @@ static void mctp_serial_push_trailer(struct mctp_serial *dev, unsigned char c) } } -static void mctp_serial_push(struct mctp_serial *dev, unsigned char c) +static void mctp_serial_push(struct mctp_serial *dev, u8 c) { switch (dev->rxstate) { case STATE_IDLE: @@ -394,7 +395,7 @@ static void mctp_serial_tty_receive_buf(struct tty_struct *tty, const u8 *c, const u8 *f, size_t len) { struct mctp_serial *dev = tty->disc_data; - int i; + size_t i; if (!netif_running(dev->netdev)) return; diff --git a/drivers/nvdimm/nd_virtio.c b/drivers/nvdimm/nd_virtio.c index 35c8fbbba10e..f55d60922b87 100644 --- a/drivers/nvdimm/nd_virtio.c +++ b/drivers/nvdimm/nd_virtio.c @@ -44,6 +44,15 @@ static int virtio_pmem_flush(struct nd_region *nd_region) unsigned long flags; int err, err1; + /* + * Don't bother to submit the request to the device if the device is + * not activated. + */ + if (vdev->config->get_status(vdev) & VIRTIO_CONFIG_S_NEEDS_RESET) { + dev_info(&vdev->dev, "virtio pmem device needs a reset\n"); + return -EIO; + } + might_sleep(); req_data = kmalloc(sizeof(*req_data), GFP_KERNEL); if (!req_data) diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index ca9959a8fb9e..ba6508455e18 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -2468,11 +2468,6 @@ int nvme_enable_ctrl(struct nvme_ctrl *ctrl) if (ret) return ret; - /* Flush write to device (required if transport is PCI) */ - ret = ctrl->ops->reg_read32(ctrl, NVME_REG_CC, &ctrl->ctrl_config); - if (ret) - return ret; - /* CAP value may change after initial CC write */ ret = ctrl->ops->reg_read64(ctrl, NVME_REG_CAP, &ctrl->cap); if (ret) diff --git a/drivers/nvme/host/ioctl.c b/drivers/nvme/host/ioctl.c index 1d769c842fbf..b9b79ccfabf8 100644 --- a/drivers/nvme/host/ioctl.c +++ b/drivers/nvme/host/ioctl.c @@ -3,7 +3,6 @@ * Copyright (c) 2011-2014, Intel Corporation. * Copyright (c) 2017-2021 Christoph Hellwig. */ -#include #include #include /* for force_successful_syscall_return */ #include @@ -153,11 +152,10 @@ static int nvme_map_user_request(struct request *req, u64 ubuffer, bio_set_dev(bio, bdev); if (has_metadata) { - ret = bio_integrity_map_user(bio, meta_buffer, meta_len, - meta_seed); + ret = blk_rq_integrity_map_user(req, meta_buffer, meta_len, + meta_seed); if (ret) goto out_unmap; - req->cmd_flags |= REQ_INTEGRITY; } return ret; diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c index 518e22dd4f9b..48e7a8906d01 100644 --- a/drivers/nvme/host/multipath.c +++ b/drivers/nvme/host/multipath.c @@ -421,6 +421,9 @@ static bool nvme_available_path(struct nvme_ns_head *head) { struct nvme_ns *ns; + if (!test_bit(NVME_NSHEAD_DISK_LIVE, &head->flags)) + return NULL; + list_for_each_entry_rcu(ns, &head->list, siblings) { if (test_bit(NVME_CTRL_FAILFAST_EXPIRED, &ns->ctrl->flags)) continue; @@ -648,7 +651,7 @@ static void nvme_mpath_set_live(struct nvme_ns *ns) rc = device_add_disk(&head->subsys->dev, head->disk, nvme_ns_attr_groups); if (rc) { - clear_bit(NVME_NSHEAD_DISK_LIVE, &ns->flags); + clear_bit(NVME_NSHEAD_DISK_LIVE, &head->flags); return; } nvme_add_ns_head_cdev(head); @@ -969,11 +972,16 @@ void nvme_mpath_shutdown_disk(struct nvme_ns_head *head) { if (!head->disk) return; - kblockd_schedule_work(&head->requeue_work); - if (test_bit(NVME_NSHEAD_DISK_LIVE, &head->flags)) { + if (test_and_clear_bit(NVME_NSHEAD_DISK_LIVE, &head->flags)) { nvme_cdev_del(&head->cdev, &head->cdev_device); del_gendisk(head->disk); } + /* + * requeue I/O after NVME_NSHEAD_DISK_LIVE has been cleared + * to allow multipath to fail all I/O. + */ + synchronize_srcu(&head->srcu); + kblockd_schedule_work(&head->requeue_work); } void nvme_mpath_remove_disk(struct nvme_ns_head *head) diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c index 15b5e06039a5..c8fd0e8f0237 100644 --- a/drivers/nvme/host/rdma.c +++ b/drivers/nvme/host/rdma.c @@ -1496,7 +1496,7 @@ static int nvme_rdma_dma_map_req(struct ib_device *ibdev, struct request *rq, req->metadata_sgl->sg_table.sgl = (struct scatterlist *)(req->metadata_sgl + 1); ret = sg_alloc_table_chained(&req->metadata_sgl->sg_table, - blk_rq_count_integrity_sg(rq->q, rq->bio), + rq->nr_integrity_segments, req->metadata_sgl->sg_table.sgl, NVME_INLINE_METADATA_SG_CNT); if (unlikely(ret)) { @@ -1504,8 +1504,8 @@ static int nvme_rdma_dma_map_req(struct ib_device *ibdev, struct request *rq, goto out_unmap_sg; } - req->metadata_sgl->nents = blk_rq_map_integrity_sg(rq->q, - rq->bio, req->metadata_sgl->sg_table.sgl); + req->metadata_sgl->nents = blk_rq_map_integrity_sg(rq, + req->metadata_sgl->sg_table.sgl); *pi_count = ib_dma_map_sg(ibdev, req->metadata_sgl->sg_table.sgl, req->metadata_sgl->nents, diff --git a/drivers/nvme/host/sysfs.c b/drivers/nvme/host/sysfs.c index eb345551d6fe..b68a9e5f1ea3 100644 --- a/drivers/nvme/host/sysfs.c +++ b/drivers/nvme/host/sysfs.c @@ -767,6 +767,7 @@ static struct attribute *nvme_tls_attrs[] = { &dev_attr_tls_key.attr, &dev_attr_tls_configured_key.attr, &dev_attr_tls_keyring.attr, + NULL, }; static umode_t nvme_tls_attrs_are_visible(struct kobject *kobj, diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index e87c3d74565c..66eb1122248b 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -743,6 +743,16 @@ config RTC_DRV_S5M This driver can also be built as a module. If so, the module will be called rtc-s5m. +config RTC_DRV_SD2405AL + tristate "DFRobot SD2405AL" + select REGMAP_I2C + help + If you say yes here you will get support for the + DFRobot SD2405AL I2C RTC Module. + + This driver can also be built as a module. If so, the module + will be called rtc-sd2405al. + config RTC_DRV_SD3078 tristate "ZXW Shenzhen whwave SD3078" select REGMAP_I2C @@ -1934,6 +1944,12 @@ config RTC_DRV_STM32 tristate "STM32 RTC" select REGMAP_MMIO depends on ARCH_STM32 || COMPILE_TEST + depends on OF + depends on PINCTRL + select PINMUX + select PINCONF + select GENERIC_PINCONF + depends on COMMON_CLK help If you say yes here you get support for the STM32 On-Chip Real Time Clock. diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 8ee79cb18322..f62340ecc534 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -163,6 +163,7 @@ obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o obj-$(CONFIG_RTC_DRV_S5M) += rtc-s5m.o obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o obj-$(CONFIG_RTC_DRV_SC27XX) += rtc-sc27xx.o +obj-$(CONFIG_RTC_DRV_SD2405AL) += rtc-sd2405al.o obj-$(CONFIG_RTC_DRV_SD3078) += rtc-sd3078.o obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o obj-$(CONFIG_RTC_DRV_SNVS) += rtc-snvs.o diff --git a/drivers/rtc/rtc-at91sam9.c b/drivers/rtc/rtc-at91sam9.c index f93bee96e362..993c0878fb66 100644 --- a/drivers/rtc/rtc-at91sam9.c +++ b/drivers/rtc/rtc-at91sam9.c @@ -368,6 +368,7 @@ static int at91_rtc_probe(struct platform_device *pdev) return ret; rtc->gpbr = syscon_node_to_regmap(args.np); + of_node_put(args.np); rtc->gpbr_offset = args.args[0]; if (IS_ERR(rtc->gpbr)) { dev_err(&pdev->dev, "failed to retrieve gpbr regmap, aborting.\n"); diff --git a/drivers/rtc/rtc-m48t59.c b/drivers/rtc/rtc-m48t59.c index f0f6b9b6daec..5d30ce8e13ca 100644 --- a/drivers/rtc/rtc-m48t59.c +++ b/drivers/rtc/rtc-m48t59.c @@ -132,7 +132,7 @@ static int m48t59_rtc_set_time(struct device *dev, struct rtc_time *tm) M48T59_WRITE((bin2bcd(tm->tm_mon + 1) & 0x1F), M48T59_MONTH); M48T59_WRITE(bin2bcd(year % 100), M48T59_YEAR); - if (pdata->type == M48T59RTC_TYPE_M48T59 && (year / 100)) + if (pdata->type == M48T59RTC_TYPE_M48T59 && (year >= 100)) val = (M48T59_WDAY_CEB | M48T59_WDAY_CB); val |= (bin2bcd(tm->tm_wday) & 0x07); M48T59_WRITE(val, M48T59_WDAY); @@ -458,6 +458,8 @@ static int m48t59_rtc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, m48t59); m48t59->rtc->ops = &m48t59_rtc_ops; + m48t59->rtc->range_min = RTC_TIMESTAMP_BEGIN_1900; + m48t59->rtc->range_max = RTC_TIMESTAMP_END_2099; nvmem_cfg.size = pdata->offset; ret = devm_rtc_nvmem_register(m48t59->rtc, &nvmem_cfg); diff --git a/drivers/rtc/rtc-rc5t619.c b/drivers/rtc/rtc-rc5t619.c index e73102a39f1b..711f62eecd79 100644 --- a/drivers/rtc/rtc-rc5t619.c +++ b/drivers/rtc/rtc-rc5t619.c @@ -429,14 +429,23 @@ static int rc5t619_rtc_probe(struct platform_device *pdev) return devm_rtc_register_device(rtc->rtc); } +static const struct platform_device_id rc5t619_rtc_id[] = { + { + .name = "rc5t619-rtc", + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(platform, rc5t619_rtc_id); + static struct platform_driver rc5t619_rtc_driver = { .driver = { .name = "rc5t619-rtc", }, .probe = rc5t619_rtc_probe, + .id_table = rc5t619_rtc_id, }; - module_platform_driver(rc5t619_rtc_driver); -MODULE_ALIAS("platform:rc5t619-rtc"); + MODULE_DESCRIPTION("RICOH RC5T619 RTC driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-s35390a.c b/drivers/rtc/rtc-s35390a.c index 2d6b655a4b25..e3dc18882f41 100644 --- a/drivers/rtc/rtc-s35390a.c +++ b/drivers/rtc/rtc-s35390a.c @@ -56,7 +56,6 @@ static const struct i2c_device_id s35390a_id[] = { MODULE_DEVICE_TABLE(i2c, s35390a_id); static const __maybe_unused struct of_device_id s35390a_of_match[] = { - { .compatible = "s35390a" }, { .compatible = "sii,s35390a" }, { } }; diff --git a/drivers/rtc/rtc-sd2405al.c b/drivers/rtc/rtc-sd2405al.c new file mode 100644 index 000000000000..d2568c3e3876 --- /dev/null +++ b/drivers/rtc/rtc-sd2405al.c @@ -0,0 +1,227 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * RTC driver for the SD2405AL Real-Time Clock + * + * Datasheet: + * https://image.dfrobot.com/image/data/TOY0021/SD2405AL%20datasheet%20(Angelo%20v0.1).pdf + * + * Copyright (C) 2024 Tóth János + */ + +#include +#include +#include +#include + +/* Real time clock registers */ +#define SD2405AL_REG_T_SEC 0x00 +#define SD2405AL_REG_T_MIN 0x01 +#define SD2405AL_REG_T_HOUR 0x02 +# define SD2405AL_BIT_12H_PM BIT(5) +# define SD2405AL_BIT_24H BIT(7) +#define SD2405AL_REG_T_WEEK 0x03 +#define SD2405AL_REG_T_DAY 0x04 +#define SD2405AL_REG_T_MON 0x05 +#define SD2405AL_REG_T_YEAR 0x06 + +#define SD2405AL_NUM_T_REGS (SD2405AL_REG_T_YEAR - SD2405AL_REG_T_SEC + 1) + +/* Control registers */ +#define SD2405AL_REG_CTR1 0x0F +# define SD2405AL_BIT_WRTC2 BIT(2) +# define SD2405AL_BIT_WRTC3 BIT(7) +#define SD2405AL_REG_CTR2 0x10 +# define SD2405AL_BIT_WRTC1 BIT(7) +#define SD2405AL_REG_CTR3 0x11 +#define SD2405AL_REG_TTF 0x12 +#define SD2405AL_REG_CNTDWN 0x13 + +/* General RAM */ +#define SD2405AL_REG_M_START 0x14 +#define SD2405AL_REG_M_END 0x1F + +struct sd2405al { + struct device *dev; + struct rtc_device *rtc; + struct regmap *regmap; +}; + +static int sd2405al_enable_reg_write(struct sd2405al *sd2405al) +{ + int ret; + + /* order of writes is important */ + ret = regmap_update_bits(sd2405al->regmap, SD2405AL_REG_CTR2, + SD2405AL_BIT_WRTC1, SD2405AL_BIT_WRTC1); + if (ret < 0) + return ret; + + ret = regmap_update_bits(sd2405al->regmap, SD2405AL_REG_CTR1, + SD2405AL_BIT_WRTC2 | SD2405AL_BIT_WRTC3, + SD2405AL_BIT_WRTC2 | SD2405AL_BIT_WRTC3); + if (ret < 0) + return ret; + + return 0; +} + +static int sd2405al_disable_reg_write(struct sd2405al *sd2405al) +{ + int ret; + + /* order of writes is important */ + ret = regmap_update_bits(sd2405al->regmap, SD2405AL_REG_CTR1, + SD2405AL_BIT_WRTC2 | SD2405AL_BIT_WRTC3, 0x00); + if (ret < 0) + return ret; + + ret = regmap_update_bits(sd2405al->regmap, SD2405AL_REG_CTR2, + SD2405AL_BIT_WRTC1, 0x00); + if (ret < 0) + return ret; + + return 0; +} + +static int sd2405al_read_time(struct device *dev, struct rtc_time *time) +{ + u8 data[SD2405AL_NUM_T_REGS] = { 0 }; + struct sd2405al *sd2405al = dev_get_drvdata(dev); + int ret; + + ret = regmap_bulk_read(sd2405al->regmap, SD2405AL_REG_T_SEC, data, + SD2405AL_NUM_T_REGS); + if (ret < 0) + return ret; + + time->tm_sec = bcd2bin(data[SD2405AL_REG_T_SEC] & 0x7F); + time->tm_min = bcd2bin(data[SD2405AL_REG_T_MIN] & 0x7F); + + if (data[SD2405AL_REG_T_HOUR] & SD2405AL_BIT_24H) + time->tm_hour = bcd2bin(data[SD2405AL_REG_T_HOUR] & 0x3F); + else + if (data[SD2405AL_REG_T_HOUR] & SD2405AL_BIT_12H_PM) + time->tm_hour = bcd2bin(data[SD2405AL_REG_T_HOUR] + & 0x1F) + 12; + else /* 12 hour mode, AM */ + time->tm_hour = bcd2bin(data[SD2405AL_REG_T_HOUR] + & 0x1F); + + time->tm_wday = bcd2bin(data[SD2405AL_REG_T_WEEK] & 0x07); + time->tm_mday = bcd2bin(data[SD2405AL_REG_T_DAY] & 0x3F); + time->tm_mon = bcd2bin(data[SD2405AL_REG_T_MON] & 0x1F) - 1; + time->tm_year = bcd2bin(data[SD2405AL_REG_T_YEAR]) + 100; + + dev_dbg(sd2405al->dev, "read time: %ptR (%d)\n", time, time->tm_wday); + + return 0; +} + +static int sd2405al_set_time(struct device *dev, struct rtc_time *time) +{ + u8 data[SD2405AL_NUM_T_REGS]; + struct sd2405al *sd2405al = dev_get_drvdata(dev); + int ret; + + data[SD2405AL_REG_T_SEC] = bin2bcd(time->tm_sec); + data[SD2405AL_REG_T_MIN] = bin2bcd(time->tm_min); + data[SD2405AL_REG_T_HOUR] = bin2bcd(time->tm_hour) | SD2405AL_BIT_24H; + data[SD2405AL_REG_T_DAY] = bin2bcd(time->tm_mday); + data[SD2405AL_REG_T_WEEK] = bin2bcd(time->tm_wday); + data[SD2405AL_REG_T_MON] = bin2bcd(time->tm_mon) + 1; + data[SD2405AL_REG_T_YEAR] = bin2bcd(time->tm_year - 100); + + ret = sd2405al_enable_reg_write(sd2405al); + if (ret < 0) + return ret; + + ret = regmap_bulk_write(sd2405al->regmap, SD2405AL_REG_T_SEC, data, + SD2405AL_NUM_T_REGS); + if (ret < 0) + return ret; + + ret = regmap_write(sd2405al->regmap, SD2405AL_REG_TTF, 0x00); + if (ret < 0) + return ret; + + ret = sd2405al_disable_reg_write(sd2405al); + if (ret < 0) + return ret; + + dev_dbg(sd2405al->dev, "set time: %ptR (%d)\n", time, time->tm_wday); + + return 0; +} + +static const struct rtc_class_ops sd2405al_rtc_ops = { + .read_time = sd2405al_read_time, + .set_time = sd2405al_set_time, +}; + +static const struct regmap_config sd2405al_regmap_conf = { + .reg_bits = 8, + .val_bits = 8, + .max_register = SD2405AL_REG_M_END, +}; + +static int sd2405al_probe(struct i2c_client *client) +{ + struct sd2405al *sd2405al; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + sd2405al = devm_kzalloc(&client->dev, sizeof(*sd2405al), GFP_KERNEL); + if (!sd2405al) + return -ENOMEM; + + sd2405al->dev = &client->dev; + + sd2405al->regmap = devm_regmap_init_i2c(client, &sd2405al_regmap_conf); + if (IS_ERR(sd2405al->regmap)) + return PTR_ERR(sd2405al->regmap); + + sd2405al->rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(sd2405al->rtc)) + return PTR_ERR(sd2405al->rtc); + + sd2405al->rtc->ops = &sd2405al_rtc_ops; + sd2405al->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + sd2405al->rtc->range_max = RTC_TIMESTAMP_END_2099; + + dev_set_drvdata(&client->dev, sd2405al); + + ret = devm_rtc_register_device(sd2405al->rtc); + if (ret < 0) + return ret; + + return 0; +} + +static const struct i2c_device_id sd2405al_id[] = { + { "sd2405al" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(i2c, sd2405al_id); + +static const __maybe_unused struct of_device_id sd2405al_of_match[] = { + { .compatible = "dfrobot,sd2405al" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sd2405al_of_match); + +static struct i2c_driver sd2405al_driver = { + .driver = { + .name = "sd2405al", + .of_match_table = of_match_ptr(sd2405al_of_match), + }, + .probe = sd2405al_probe, + .id_table = sd2405al_id, +}; + +module_i2c_driver(sd2405al_driver); + +MODULE_AUTHOR("Tóth János "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SD2405AL RTC driver"); diff --git a/drivers/rtc/rtc-stm32.c b/drivers/rtc/rtc-stm32.c index 98b07969609d..3e4f2ee22b0b 100644 --- a/drivers/rtc/rtc-stm32.c +++ b/drivers/rtc/rtc-stm32.c @@ -7,12 +7,16 @@ #include #include #include +#include #include #include #include #include #include #include +#include +#include +#include #include #include #include @@ -42,6 +46,12 @@ #define STM32_RTC_CR_FMT BIT(6) #define STM32_RTC_CR_ALRAE BIT(8) #define STM32_RTC_CR_ALRAIE BIT(12) +#define STM32_RTC_CR_OSEL GENMASK(22, 21) +#define STM32_RTC_CR_OSEL_ALARM_A FIELD_PREP(STM32_RTC_CR_OSEL, 0x01) +#define STM32_RTC_CR_COE BIT(23) +#define STM32_RTC_CR_TAMPOE BIT(26) +#define STM32_RTC_CR_TAMPALRM_TYPE BIT(30) +#define STM32_RTC_CR_OUT2EN BIT(31) /* STM32_RTC_ISR/STM32_RTC_ICSR bit fields */ #define STM32_RTC_ISR_ALRAWF BIT(0) @@ -78,6 +88,12 @@ /* STM32_RTC_SR/_SCR bit fields */ #define STM32_RTC_SR_ALRA BIT(0) +/* STM32_RTC_CFGR bit fields */ +#define STM32_RTC_CFGR_OUT2_RMP BIT(0) +#define STM32_RTC_CFGR_LSCOEN GENMASK(2, 1) +#define STM32_RTC_CFGR_LSCOEN_OUT1 1 +#define STM32_RTC_CFGR_LSCOEN_OUT2_RMP 2 + /* STM32_RTC_VERR bit fields */ #define STM32_RTC_VERR_MINREV_SHIFT 0 #define STM32_RTC_VERR_MINREV GENMASK(3, 0) @@ -107,6 +123,14 @@ /* STM32 RTC driver time helpers */ #define SEC_PER_DAY (24 * 60 * 60) +/* STM32 RTC pinctrl helpers */ +#define STM32_RTC_PINMUX(_name, _action, ...) { \ + .name = (_name), \ + .action = (_action), \ + .groups = ((const char *[]){ __VA_ARGS__ }), \ + .num_groups = ARRAY_SIZE(((const char *[]){ __VA_ARGS__ })), \ +} + struct stm32_rtc; struct stm32_rtc_registers { @@ -119,6 +143,7 @@ struct stm32_rtc_registers { u16 wpr; u16 sr; u16 scr; + u16 cfgr; u16 verr; }; @@ -134,6 +159,8 @@ struct stm32_rtc_data { bool need_dbp; bool need_accuracy; bool rif_protected; + bool has_lsco; + bool has_alarm_out; }; struct stm32_rtc { @@ -146,6 +173,7 @@ struct stm32_rtc { struct clk *rtc_ck; const struct stm32_rtc_data *data; int irq_alarm; + struct clk *clk_lsco; }; struct stm32_rtc_rif_resource { @@ -171,6 +199,209 @@ static void stm32_rtc_wpr_lock(struct stm32_rtc *rtc) writel_relaxed(RTC_WPR_WRONG_KEY, rtc->base + regs->wpr); } +enum stm32_rtc_pin_name { + NONE, + OUT1, + OUT2, + OUT2_RMP +}; + +static const struct pinctrl_pin_desc stm32_rtc_pinctrl_pins[] = { + PINCTRL_PIN(OUT1, "out1"), + PINCTRL_PIN(OUT2, "out2"), + PINCTRL_PIN(OUT2_RMP, "out2_rmp"), +}; + +static int stm32_rtc_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) +{ + return ARRAY_SIZE(stm32_rtc_pinctrl_pins); +} + +static const char *stm32_rtc_pinctrl_get_group_name(struct pinctrl_dev *pctldev, + unsigned int selector) +{ + return stm32_rtc_pinctrl_pins[selector].name; +} + +static int stm32_rtc_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, + unsigned int selector, + const unsigned int **pins, + unsigned int *num_pins) +{ + *pins = &stm32_rtc_pinctrl_pins[selector].number; + *num_pins = 1; + return 0; +} + +static const struct pinctrl_ops stm32_rtc_pinctrl_ops = { + .dt_node_to_map = pinconf_generic_dt_node_to_map_all, + .dt_free_map = pinconf_generic_dt_free_map, + .get_groups_count = stm32_rtc_pinctrl_get_groups_count, + .get_group_name = stm32_rtc_pinctrl_get_group_name, + .get_group_pins = stm32_rtc_pinctrl_get_group_pins, +}; + +struct stm32_rtc_pinmux_func { + const char *name; + const char * const *groups; + const unsigned int num_groups; + int (*action)(struct pinctrl_dev *pctl_dev, unsigned int pin); +}; + +static int stm32_rtc_pinmux_action_alarm(struct pinctrl_dev *pctldev, unsigned int pin) +{ + struct stm32_rtc *rtc = pinctrl_dev_get_drvdata(pctldev); + struct stm32_rtc_registers regs = rtc->data->regs; + unsigned int cr = readl_relaxed(rtc->base + regs.cr); + unsigned int cfgr = readl_relaxed(rtc->base + regs.cfgr); + + if (!rtc->data->has_alarm_out) + return -EPERM; + + cr &= ~STM32_RTC_CR_OSEL; + cr |= STM32_RTC_CR_OSEL_ALARM_A; + cr &= ~STM32_RTC_CR_TAMPOE; + cr &= ~STM32_RTC_CR_COE; + cr &= ~STM32_RTC_CR_TAMPALRM_TYPE; + + switch (pin) { + case OUT1: + cr &= ~STM32_RTC_CR_OUT2EN; + cfgr &= ~STM32_RTC_CFGR_OUT2_RMP; + break; + case OUT2: + cr |= STM32_RTC_CR_OUT2EN; + cfgr &= ~STM32_RTC_CFGR_OUT2_RMP; + break; + case OUT2_RMP: + cr |= STM32_RTC_CR_OUT2EN; + cfgr |= STM32_RTC_CFGR_OUT2_RMP; + break; + default: + return -EINVAL; + } + + stm32_rtc_wpr_unlock(rtc); + writel_relaxed(cr, rtc->base + regs.cr); + writel_relaxed(cfgr, rtc->base + regs.cfgr); + stm32_rtc_wpr_lock(rtc); + + return 0; +} + +static int stm32_rtc_pinmux_lsco_available(struct pinctrl_dev *pctldev, unsigned int pin) +{ + struct stm32_rtc *rtc = pinctrl_dev_get_drvdata(pctldev); + struct stm32_rtc_registers regs = rtc->data->regs; + unsigned int cr = readl_relaxed(rtc->base + regs.cr); + unsigned int cfgr = readl_relaxed(rtc->base + regs.cfgr); + unsigned int calib = STM32_RTC_CR_COE; + unsigned int tampalrm = STM32_RTC_CR_TAMPOE | STM32_RTC_CR_OSEL; + + switch (pin) { + case OUT1: + if ((!(cr & STM32_RTC_CR_OUT2EN) && + ((cr & calib) || cr & tampalrm)) || + ((cr & calib) && (cr & tampalrm))) + return -EBUSY; + break; + case OUT2_RMP: + if ((cr & STM32_RTC_CR_OUT2EN) && + (cfgr & STM32_RTC_CFGR_OUT2_RMP) && + ((cr & calib) || (cr & tampalrm))) + return -EBUSY; + break; + default: + return -EINVAL; + } + + if (clk_get_rate(rtc->rtc_ck) != 32768) + return -ERANGE; + + return 0; +} + +static int stm32_rtc_pinmux_action_lsco(struct pinctrl_dev *pctldev, unsigned int pin) +{ + struct stm32_rtc *rtc = pinctrl_dev_get_drvdata(pctldev); + struct stm32_rtc_registers regs = rtc->data->regs; + struct device *dev = rtc->rtc_dev->dev.parent; + u8 lscoen; + int ret; + + if (!rtc->data->has_lsco) + return -EPERM; + + ret = stm32_rtc_pinmux_lsco_available(pctldev, pin); + if (ret) + return ret; + + lscoen = (pin == OUT1) ? STM32_RTC_CFGR_LSCOEN_OUT1 : STM32_RTC_CFGR_LSCOEN_OUT2_RMP; + + rtc->clk_lsco = clk_register_gate(dev, "rtc_lsco", __clk_get_name(rtc->rtc_ck), + CLK_IGNORE_UNUSED | CLK_IS_CRITICAL, + rtc->base + regs.cfgr, lscoen, 0, NULL); + if (IS_ERR(rtc->clk_lsco)) + return PTR_ERR(rtc->clk_lsco); + + of_clk_add_provider(dev->of_node, of_clk_src_simple_get, rtc->clk_lsco); + + return 0; +} + +static const struct stm32_rtc_pinmux_func stm32_rtc_pinmux_functions[] = { + STM32_RTC_PINMUX("lsco", &stm32_rtc_pinmux_action_lsco, "out1", "out2_rmp"), + STM32_RTC_PINMUX("alarm-a", &stm32_rtc_pinmux_action_alarm, "out1", "out2", "out2_rmp"), +}; + +static int stm32_rtc_pinmux_get_functions_count(struct pinctrl_dev *pctldev) +{ + return ARRAY_SIZE(stm32_rtc_pinmux_functions); +} + +static const char *stm32_rtc_pinmux_get_fname(struct pinctrl_dev *pctldev, unsigned int selector) +{ + return stm32_rtc_pinmux_functions[selector].name; +} + +static int stm32_rtc_pinmux_get_groups(struct pinctrl_dev *pctldev, unsigned int selector, + const char * const **groups, unsigned int * const num_groups) +{ + *groups = stm32_rtc_pinmux_functions[selector].groups; + *num_groups = stm32_rtc_pinmux_functions[selector].num_groups; + return 0; +} + +static int stm32_rtc_pinmux_set_mux(struct pinctrl_dev *pctldev, unsigned int selector, + unsigned int group) +{ + struct stm32_rtc_pinmux_func selected_func = stm32_rtc_pinmux_functions[selector]; + struct pinctrl_pin_desc pin = stm32_rtc_pinctrl_pins[group]; + + /* Call action */ + if (selected_func.action) + return selected_func.action(pctldev, pin.number); + + return -EINVAL; +} + +static const struct pinmux_ops stm32_rtc_pinmux_ops = { + .get_functions_count = stm32_rtc_pinmux_get_functions_count, + .get_function_name = stm32_rtc_pinmux_get_fname, + .get_function_groups = stm32_rtc_pinmux_get_groups, + .set_mux = stm32_rtc_pinmux_set_mux, + .strict = true, +}; + +static struct pinctrl_desc stm32_rtc_pdesc = { + .name = DRIVER_NAME, + .pins = stm32_rtc_pinctrl_pins, + .npins = ARRAY_SIZE(stm32_rtc_pinctrl_pins), + .owner = THIS_MODULE, + .pctlops = &stm32_rtc_pinctrl_ops, + .pmxops = &stm32_rtc_pinmux_ops, +}; + static int stm32_rtc_enter_init_mode(struct stm32_rtc *rtc) { const struct stm32_rtc_registers *regs = &rtc->data->regs; @@ -576,6 +807,8 @@ static const struct stm32_rtc_data stm32_rtc_data = { .need_dbp = true, .need_accuracy = false, .rif_protected = false, + .has_lsco = false, + .has_alarm_out = false, .regs = { .tr = 0x00, .dr = 0x04, @@ -586,6 +819,7 @@ static const struct stm32_rtc_data stm32_rtc_data = { .wpr = 0x24, .sr = 0x0C, /* set to ISR offset to ease alarm management */ .scr = UNDEF_REG, + .cfgr = UNDEF_REG, .verr = UNDEF_REG, }, .events = { @@ -599,6 +833,8 @@ static const struct stm32_rtc_data stm32h7_rtc_data = { .need_dbp = true, .need_accuracy = false, .rif_protected = false, + .has_lsco = false, + .has_alarm_out = false, .regs = { .tr = 0x00, .dr = 0x04, @@ -609,6 +845,7 @@ static const struct stm32_rtc_data stm32h7_rtc_data = { .wpr = 0x24, .sr = 0x0C, /* set to ISR offset to ease alarm management */ .scr = UNDEF_REG, + .cfgr = UNDEF_REG, .verr = UNDEF_REG, }, .events = { @@ -631,6 +868,8 @@ static const struct stm32_rtc_data stm32mp1_data = { .need_dbp = false, .need_accuracy = true, .rif_protected = false, + .has_lsco = true, + .has_alarm_out = true, .regs = { .tr = 0x00, .dr = 0x04, @@ -641,6 +880,7 @@ static const struct stm32_rtc_data stm32mp1_data = { .wpr = 0x24, .sr = 0x50, .scr = 0x5C, + .cfgr = 0x60, .verr = 0x3F4, }, .events = { @@ -654,6 +894,8 @@ static const struct stm32_rtc_data stm32mp25_data = { .need_dbp = false, .need_accuracy = true, .rif_protected = true, + .has_lsco = true, + .has_alarm_out = true, .regs = { .tr = 0x00, .dr = 0x04, @@ -664,6 +906,7 @@ static const struct stm32_rtc_data stm32mp25_data = { .wpr = 0x24, .sr = 0x50, .scr = 0x5C, + .cfgr = 0x60, .verr = 0x3F4, }, .events = { @@ -681,6 +924,30 @@ static const struct of_device_id stm32_rtc_of_match[] = { }; MODULE_DEVICE_TABLE(of, stm32_rtc_of_match); +static void stm32_rtc_clean_outs(struct stm32_rtc *rtc) +{ + struct stm32_rtc_registers regs = rtc->data->regs; + unsigned int cr = readl_relaxed(rtc->base + regs.cr); + + cr &= ~STM32_RTC_CR_OSEL; + cr &= ~STM32_RTC_CR_TAMPOE; + cr &= ~STM32_RTC_CR_COE; + cr &= ~STM32_RTC_CR_TAMPALRM_TYPE; + cr &= ~STM32_RTC_CR_OUT2EN; + + stm32_rtc_wpr_unlock(rtc); + writel_relaxed(cr, rtc->base + regs.cr); + stm32_rtc_wpr_lock(rtc); + + if (regs.cfgr != UNDEF_REG) { + unsigned int cfgr = readl_relaxed(rtc->base + regs.cfgr); + + cfgr &= ~STM32_RTC_CFGR_LSCOEN; + cfgr &= ~STM32_RTC_CFGR_OUT2_RMP; + writel_relaxed(cfgr, rtc->base + regs.cfgr); + } +} + static int stm32_rtc_check_rif(struct stm32_rtc *stm32_rtc, struct stm32_rtc_rif_resource res) { @@ -791,6 +1058,7 @@ static int stm32_rtc_probe(struct platform_device *pdev) { struct stm32_rtc *rtc; const struct stm32_rtc_registers *regs; + struct pinctrl_dev *pctl; int ret; rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); @@ -912,6 +1180,16 @@ static int stm32_rtc_probe(struct platform_device *pdev) goto err; } + stm32_rtc_clean_outs(rtc); + + ret = devm_pinctrl_register_and_init(&pdev->dev, &stm32_rtc_pdesc, rtc, &pctl); + if (ret) + return dev_err_probe(&pdev->dev, ret, "pinctrl register failed"); + + ret = pinctrl_enable(pctl); + if (ret) + return dev_err_probe(&pdev->dev, ret, "pinctrl enable failed"); + /* * If INITS flag is reset (calendar year field set to 0x00), calendar * must be initialized @@ -950,6 +1228,9 @@ static void stm32_rtc_remove(struct platform_device *pdev) const struct stm32_rtc_registers *regs = &rtc->data->regs; unsigned int cr; + if (!IS_ERR_OR_NULL(rtc->clk_lsco)) + clk_unregister_gate(rtc->clk_lsco); + /* Disable interrupts */ stm32_rtc_wpr_unlock(rtc); cr = readl_relaxed(rtc->base + regs->cr); diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c index 8e0c66906103..e681c1745866 100644 --- a/drivers/rtc/rtc-sun6i.c +++ b/drivers/rtc/rtc-sun6i.c @@ -402,6 +402,7 @@ CLK_OF_DECLARE_DRIVER(sun8i_r40_rtc_clk, "allwinner,sun8i-r40-rtc", static const struct sun6i_rtc_clk_data sun8i_v3_rtc_data = { .rc_osc_rate = 32000, .has_out_clk = 1, + .has_auto_swt = 1, }; static void __init sun8i_v3_rtc_clk_init(struct device_node *node) diff --git a/drivers/rtc/rtc-twl.c b/drivers/rtc/rtc-twl.c index 2cfacdd37e09..4e24c12004f1 100644 --- a/drivers/rtc/rtc-twl.c +++ b/drivers/rtc/rtc-twl.c @@ -591,8 +591,8 @@ static int twl_rtc_probe(struct platform_device *pdev) memset(&nvmem_cfg, 0, sizeof(nvmem_cfg)); nvmem_cfg.name = "twl-secured-"; nvmem_cfg.type = NVMEM_TYPE_BATTERY_BACKED; - nvmem_cfg.reg_read = twl_nvram_read, - nvmem_cfg.reg_write = twl_nvram_write, + nvmem_cfg.reg_read = twl_nvram_read; + nvmem_cfg.reg_write = twl_nvram_write; nvmem_cfg.word_size = 1; nvmem_cfg.stride = 1; if (twl_class_is_4030()) { diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 7f0394c44920..0561b318dade 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1163,7 +1163,6 @@ blk_status_t scsi_alloc_sgtables(struct scsi_cmnd *cmd) if (blk_integrity_rq(rq)) { struct scsi_data_buffer *prot_sdb = cmd->prot_sdb; - int ivecs; if (WARN_ON_ONCE(!prot_sdb)) { /* @@ -1175,20 +1174,15 @@ blk_status_t scsi_alloc_sgtables(struct scsi_cmnd *cmd) goto out_free_sgtables; } - ivecs = blk_rq_count_integrity_sg(rq->q, rq->bio); - - if (sg_alloc_table_chained(&prot_sdb->table, ivecs, + if (sg_alloc_table_chained(&prot_sdb->table, + rq->nr_integrity_segments, prot_sdb->table.sgl, SCSI_INLINE_PROT_SG_CNT)) { ret = BLK_STS_RESOURCE; goto out_free_sgtables; } - count = blk_rq_map_integrity_sg(rq->q, rq->bio, - prot_sdb->table.sgl); - BUG_ON(count > ivecs); - BUG_ON(count > queue_max_integrity_segments(rq->q)); - + count = blk_rq_map_integrity_sg(rq, prot_sdb->table.sgl); cmd->prot_sdb = prot_sdb; cmd->prot_sdb->table.nents = count; } diff --git a/drivers/spi/atmel-quadspi.c b/drivers/spi/atmel-quadspi.c index 936d57869493..4f288f07e38f 100644 --- a/drivers/spi/atmel-quadspi.c +++ b/drivers/spi/atmel-quadspi.c @@ -375,9 +375,9 @@ static int atmel_qspi_set_cfg(struct atmel_qspi *aq, * If the QSPI controller is set in regular SPI mode, set it in * Serial Memory Mode (SMM). */ - if (aq->mr != QSPI_MR_SMM) { - atmel_qspi_write(QSPI_MR_SMM, aq, QSPI_MR); - aq->mr = QSPI_MR_SMM; + if (!(aq->mr & QSPI_MR_SMM)) { + aq->mr |= QSPI_MR_SMM; + atmel_qspi_write(aq->scr, aq, QSPI_MR); } /* Clear pending interrupts */ @@ -501,7 +501,8 @@ static int atmel_qspi_setup(struct spi_device *spi) if (ret < 0) return ret; - aq->scr = QSPI_SCR_SCBR(scbr); + aq->scr &= ~QSPI_SCR_SCBR_MASK; + aq->scr |= QSPI_SCR_SCBR(scbr); atmel_qspi_write(aq->scr, aq, QSPI_SCR); pm_runtime_mark_last_busy(ctrl->dev.parent); @@ -534,6 +535,7 @@ static int atmel_qspi_set_cs_timing(struct spi_device *spi) if (ret < 0) return ret; + aq->scr &= ~QSPI_SCR_DLYBS_MASK; aq->scr |= QSPI_SCR_DLYBS(cs_setup); atmel_qspi_write(aq->scr, aq, QSPI_SCR); @@ -549,8 +551,8 @@ static void atmel_qspi_init(struct atmel_qspi *aq) atmel_qspi_write(QSPI_CR_SWRST, aq, QSPI_CR); /* Set the QSPI controller by default in Serial Memory Mode */ - atmel_qspi_write(QSPI_MR_SMM, aq, QSPI_MR); - aq->mr = QSPI_MR_SMM; + aq->mr |= QSPI_MR_SMM; + atmel_qspi_write(aq->mr, aq, QSPI_MR); /* Enable the QSPI controller */ atmel_qspi_write(QSPI_CR_QSPIEN, aq, QSPI_CR); @@ -721,6 +723,7 @@ static void atmel_qspi_remove(struct platform_device *pdev) clk_unprepare(aq->pclk); pm_runtime_disable(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); pm_runtime_put_noidle(&pdev->dev); } diff --git a/drivers/spi/spi-airoha-snfi.c b/drivers/spi/spi-airoha-snfi.c index 9d97ec98881c..94458df53eae 100644 --- a/drivers/spi/spi-airoha-snfi.c +++ b/drivers/spi/spi-airoha-snfi.c @@ -211,9 +211,6 @@ struct airoha_snand_dev { u8 *txrx_buf; dma_addr_t dma_addr; - - u64 cur_page_num; - bool data_need_update; }; struct airoha_snand_ctrl { @@ -405,7 +402,7 @@ static int airoha_snand_write_data(struct airoha_snand_ctrl *as_ctrl, u8 cmd, for (i = 0; i < len; i += data_len) { int err; - data_len = min(len, SPI_MAX_TRANSFER_SIZE); + data_len = min(len - i, SPI_MAX_TRANSFER_SIZE); err = airoha_snand_set_fifo_op(as_ctrl, cmd, data_len); if (err) return err; @@ -427,7 +424,7 @@ static int airoha_snand_read_data(struct airoha_snand_ctrl *as_ctrl, u8 *data, for (i = 0; i < len; i += data_len) { int err; - data_len = min(len, SPI_MAX_TRANSFER_SIZE); + data_len = min(len - i, SPI_MAX_TRANSFER_SIZE); err = airoha_snand_set_fifo_op(as_ctrl, 0xc, data_len); if (err) return err; @@ -644,11 +641,6 @@ static ssize_t airoha_snand_dirmap_read(struct spi_mem_dirmap_desc *desc, u32 val, rd_mode; int err; - if (!as_dev->data_need_update) - return len; - - as_dev->data_need_update = false; - switch (op->cmd.opcode) { case SPI_NAND_OP_READ_FROM_CACHE_DUAL: rd_mode = 1; @@ -739,8 +731,13 @@ static ssize_t airoha_snand_dirmap_read(struct spi_mem_dirmap_desc *desc, if (err) return err; - err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SNF_STA_CTL1, - SPI_NFI_READ_FROM_CACHE_DONE); + /* + * SPI_NFI_READ_FROM_CACHE_DONE bit must be written at the end + * of dirmap_read operation even if it is already set. + */ + err = regmap_write_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SNF_STA_CTL1, + SPI_NFI_READ_FROM_CACHE_DONE, + SPI_NFI_READ_FROM_CACHE_DONE); if (err) return err; @@ -870,8 +867,13 @@ static ssize_t airoha_snand_dirmap_write(struct spi_mem_dirmap_desc *desc, if (err) return err; - err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SNF_STA_CTL1, - SPI_NFI_LOAD_TO_CACHE_DONE); + /* + * SPI_NFI_LOAD_TO_CACHE_DONE bit must be written at the end + * of dirmap_write operation even if it is already set. + */ + err = regmap_write_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SNF_STA_CTL1, + SPI_NFI_LOAD_TO_CACHE_DONE, + SPI_NFI_LOAD_TO_CACHE_DONE); if (err) return err; @@ -885,23 +887,11 @@ static ssize_t airoha_snand_dirmap_write(struct spi_mem_dirmap_desc *desc, static int airoha_snand_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) { - struct airoha_snand_dev *as_dev = spi_get_ctldata(mem->spi); u8 data[8], cmd, opcode = op->cmd.opcode; struct airoha_snand_ctrl *as_ctrl; int i, err; as_ctrl = spi_controller_get_devdata(mem->spi->controller); - if (opcode == SPI_NAND_OP_PROGRAM_EXECUTE && - op->addr.val == as_dev->cur_page_num) { - as_dev->data_need_update = true; - } else if (opcode == SPI_NAND_OP_PAGE_READ) { - if (!as_dev->data_need_update && - op->addr.val == as_dev->cur_page_num) - return 0; - - as_dev->data_need_update = true; - as_dev->cur_page_num = op->addr.val; - } /* switch to manual mode */ err = airoha_snand_set_mode(as_ctrl, SPI_MODE_MANUAL); @@ -986,7 +976,6 @@ static int airoha_snand_setup(struct spi_device *spi) if (dma_mapping_error(as_ctrl->dev, as_dev->dma_addr)) return -ENOMEM; - as_dev->data_need_update = true; spi_set_ctldata(spi, as_dev); return 0; diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c index 8ecb426be45c..977e8b55c82b 100644 --- a/drivers/spi/spi-fsl-lpspi.c +++ b/drivers/spi/spi-fsl-lpspi.c @@ -986,6 +986,7 @@ static void fsl_lpspi_remove(struct platform_device *pdev) fsl_lpspi_dma_exit(controller); + pm_runtime_dont_use_autosuspend(fsl_lpspi->dev); pm_runtime_disable(fsl_lpspi->dev); } diff --git a/drivers/thunderbolt/acpi.c b/drivers/thunderbolt/acpi.c index c9b6bb46111c..d2a0054217da 100644 --- a/drivers/thunderbolt/acpi.c +++ b/drivers/thunderbolt/acpi.c @@ -32,40 +32,20 @@ static acpi_status tb_acpi_add_link(acpi_handle handle, u32 level, void *data, goto out_put; /* - * Try to find physical device walking upwards to the hierarcy. - * We need to do this because the xHCI driver might not yet be - * bound so the USB3 SuperSpeed ports are not yet created. + * Ignore USB3 ports here as USB core will set up device links between + * tunneled USB3 devices and NHI host during USB device creation. + * USB3 ports might not even have a physical device yet if xHCI driver + * isn't bound yet. */ - do { - dev = acpi_get_first_physical_node(adev); - if (dev) - break; - - adev = acpi_dev_parent(adev); - } while (adev); - - /* - * Check that the device is PCIe. This is because USB3 - * SuperSpeed ports have this property and they are not power - * managed with the xHCI and the SuperSpeed hub so we create the - * link from xHCI instead. - */ - while (dev && !dev_is_pci(dev)) - dev = dev->parent; - - if (!dev) + dev = acpi_get_first_physical_node(adev); + if (!dev || !dev_is_pci(dev)) goto out_put; - /* - * Check that this actually matches the type of device we - * expect. It should either be xHCI or PCIe root/downstream - * port. - */ + /* Check that this matches a PCIe root/downstream port. */ pdev = to_pci_dev(dev); - if (pdev->class == PCI_CLASS_SERIAL_USB_XHCI || - (pci_is_pcie(pdev) && - (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT || - pci_pcie_type(pdev) == PCI_EXP_TYPE_DOWNSTREAM))) { + if (pci_is_pcie(pdev) && + (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT || + pci_pcie_type(pdev) == PCI_EXP_TYPE_DOWNSTREAM)) { const struct device_link *link; /* diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c index 9ed4bb2e8d05..350310bd0fee 100644 --- a/drivers/thunderbolt/debugfs.c +++ b/drivers/thunderbolt/debugfs.c @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -34,6 +35,14 @@ #define COUNTER_SET_LEN 3 +/* + * USB4 spec doesn't specify dwell range, the range of 100 ms to 500 ms + * probed to give good results. + */ +#define MIN_DWELL_TIME 100 /* ms */ +#define MAX_DWELL_TIME 500 /* ms */ +#define DWELL_SAMPLE_INTERVAL 10 + /* Sideband registers and their sizes as defined in the USB4 spec */ struct sb_reg { unsigned int reg; @@ -394,8 +403,15 @@ out: * @ber_level: Current BER level contour value * @voltage_steps: Number of mandatory voltage steps * @max_voltage_offset: Maximum mandatory voltage offset (in mV) + * @voltage_steps_optional_range: Number of voltage steps for optional range + * @max_voltage_offset_optional_range: Maximum voltage offset for the optional + * range (in mV). * @time_steps: Number of time margin steps * @max_time_offset: Maximum time margin offset (in mUI) + * @voltage_time_offset: Offset for voltage / time for software margining + * @dwell_time: Dwell time for software margining (in ms) + * @error_counter: Error counter operation for software margining + * @optional_voltage_offset_range: Enable optional extended voltage range * @software: %true if software margining is used instead of hardware * @time: %true if time margining is used instead of voltage * @right_high: %false if left/low margin test is performed, %true if @@ -414,13 +430,37 @@ struct tb_margining { unsigned int ber_level; unsigned int voltage_steps; unsigned int max_voltage_offset; + unsigned int voltage_steps_optional_range; + unsigned int max_voltage_offset_optional_range; unsigned int time_steps; unsigned int max_time_offset; + unsigned int voltage_time_offset; + unsigned int dwell_time; + enum usb4_margin_sw_error_counter error_counter; + bool optional_voltage_offset_range; bool software; bool time; bool right_high; }; +static int margining_modify_error_counter(struct tb_margining *margining, + u32 lanes, enum usb4_margin_sw_error_counter error_counter) +{ + struct usb4_port_margining_params params = { 0 }; + struct tb_port *port = margining->port; + u32 result; + + if (error_counter != USB4_MARGIN_SW_ERROR_COUNTER_CLEAR && + error_counter != USB4_MARGIN_SW_ERROR_COUNTER_STOP) + return -EOPNOTSUPP; + + params.error_counter = error_counter; + params.lanes = lanes; + + return usb4_port_sw_margin(port, margining->target, margining->index, + ¶ms, &result); +} + static bool supports_software(const struct tb_margining *margining) { return margining->caps[0] & USB4_MARGIN_CAP_0_MODES_SW; @@ -454,6 +494,12 @@ independent_time_margins(const struct tb_margining *margining) return FIELD_GET(USB4_MARGIN_CAP_1_TIME_INDP_MASK, margining->caps[1]); } +static bool +supports_optional_voltage_offset_range(const struct tb_margining *margining) +{ + return margining->caps[0] & USB4_MARGIN_CAP_0_OPT_VOLTAGE_SUPPORT; +} + static ssize_t margining_ber_level_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) @@ -553,6 +599,14 @@ static int margining_caps_show(struct seq_file *s, void *not_used) margining->voltage_steps); seq_printf(s, "# maximum voltage offset: %u mV\n", margining->max_voltage_offset); + seq_printf(s, "# optional voltage offset range support: %s\n", + str_yes_no(supports_optional_voltage_offset_range(margining))); + if (supports_optional_voltage_offset_range(margining)) { + seq_printf(s, "# voltage margin steps, optional range: %u\n", + margining->voltage_steps_optional_range); + seq_printf(s, "# maximum voltage offset, optional range: %u mV\n", + margining->max_voltage_offset_optional_range); + } switch (independent_voltage_margins(margining)) { case USB4_MARGIN_CAP_0_VOLTAGE_MIN: @@ -667,6 +721,198 @@ static int margining_lanes_show(struct seq_file *s, void *not_used) } DEBUGFS_ATTR_RW(margining_lanes); +static ssize_t +margining_voltage_time_offset_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct tb_margining *margining = s->private; + struct tb *tb = margining->port->sw->tb; + unsigned int max_margin; + unsigned int val; + int ret; + + ret = kstrtouint_from_user(user_buf, count, 10, &val); + if (ret) + return ret; + + scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) { + if (!margining->software) + return -EOPNOTSUPP; + + if (margining->time) + max_margin = margining->time_steps; + else + if (margining->optional_voltage_offset_range) + max_margin = margining->voltage_steps_optional_range; + else + max_margin = margining->voltage_steps; + + margining->voltage_time_offset = clamp(val, 0, max_margin); + } + + return count; +} + +static int margining_voltage_time_offset_show(struct seq_file *s, + void *not_used) +{ + const struct tb_margining *margining = s->private; + struct tb *tb = margining->port->sw->tb; + + scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) { + if (!margining->software) + return -EOPNOTSUPP; + + seq_printf(s, "%d\n", margining->voltage_time_offset); + } + + return 0; +} +DEBUGFS_ATTR_RW(margining_voltage_time_offset); + +static ssize_t +margining_error_counter_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + enum usb4_margin_sw_error_counter error_counter; + struct seq_file *s = file->private_data; + struct tb_margining *margining = s->private; + struct tb *tb = margining->port->sw->tb; + char *buf; + + buf = validate_and_copy_from_user(user_buf, &count); + if (IS_ERR(buf)) + return PTR_ERR(buf); + + buf[count - 1] = '\0'; + + if (!strcmp(buf, "nop")) + error_counter = USB4_MARGIN_SW_ERROR_COUNTER_NOP; + else if (!strcmp(buf, "clear")) + error_counter = USB4_MARGIN_SW_ERROR_COUNTER_CLEAR; + else if (!strcmp(buf, "start")) + error_counter = USB4_MARGIN_SW_ERROR_COUNTER_START; + else if (!strcmp(buf, "stop")) + error_counter = USB4_MARGIN_SW_ERROR_COUNTER_STOP; + else + return -EINVAL; + + scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) { + if (!margining->software) + return -EOPNOTSUPP; + + margining->error_counter = error_counter; + } + + return count; +} + +static int margining_error_counter_show(struct seq_file *s, void *not_used) +{ + const struct tb_margining *margining = s->private; + struct tb *tb = margining->port->sw->tb; + + scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) { + if (!margining->software) + return -EOPNOTSUPP; + + switch (margining->error_counter) { + case USB4_MARGIN_SW_ERROR_COUNTER_NOP: + seq_puts(s, "[nop] clear start stop\n"); + break; + case USB4_MARGIN_SW_ERROR_COUNTER_CLEAR: + seq_puts(s, "nop [clear] start stop\n"); + break; + case USB4_MARGIN_SW_ERROR_COUNTER_START: + seq_puts(s, "nop clear [start] stop\n"); + break; + case USB4_MARGIN_SW_ERROR_COUNTER_STOP: + seq_puts(s, "nop clear start [stop]\n"); + break; + } + } + + return 0; +} +DEBUGFS_ATTR_RW(margining_error_counter); + +static ssize_t +margining_dwell_time_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct tb_margining *margining = s->private; + struct tb *tb = margining->port->sw->tb; + unsigned int val; + int ret; + + ret = kstrtouint_from_user(user_buf, count, 10, &val); + if (ret) + return ret; + + scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) { + if (!margining->software) + return -EOPNOTSUPP; + + margining->dwell_time = clamp(val, MIN_DWELL_TIME, MAX_DWELL_TIME); + } + + return count; +} + +static int margining_dwell_time_show(struct seq_file *s, void *not_used) +{ + struct tb_margining *margining = s->private; + struct tb *tb = margining->port->sw->tb; + + scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) { + if (!margining->software) + return -EOPNOTSUPP; + + seq_printf(s, "%d\n", margining->dwell_time); + } + + return 0; +} +DEBUGFS_ATTR_RW(margining_dwell_time); + +static ssize_t +margining_optional_voltage_offset_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct tb_margining *margining = s->private; + struct tb *tb = margining->port->sw->tb; + bool val; + int ret; + + ret = kstrtobool_from_user(user_buf, count, &val); + if (ret) + return ret; + + scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) { + margining->optional_voltage_offset_range = val; + } + + return count; +} + +static int margining_optional_voltage_offset_show(struct seq_file *s, + void *not_used) +{ + struct tb_margining *margining = s->private; + struct tb *tb = margining->port->sw->tb; + + scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) { + seq_printf(s, "%u\n", margining->optional_voltage_offset_range); + } + + return 0; +} +DEBUGFS_ATTR_RW(margining_optional_voltage_offset); + static ssize_t margining_mode_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) @@ -739,6 +985,51 @@ static int margining_mode_show(struct seq_file *s, void *not_used) } DEBUGFS_ATTR_RW(margining_mode); +static int margining_run_sw(struct tb_margining *margining, + struct usb4_port_margining_params *params) +{ + u32 nsamples = margining->dwell_time / DWELL_SAMPLE_INTERVAL; + int ret, i; + + ret = usb4_port_sw_margin(margining->port, margining->target, margining->index, + params, margining->results); + if (ret) + goto out_stop; + + for (i = 0; i <= nsamples; i++) { + u32 errors = 0; + + ret = usb4_port_sw_margin_errors(margining->port, margining->target, + margining->index, &margining->results[1]); + if (ret) + break; + + if (margining->lanes == USB4_MARGIN_SW_LANE_0) + errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_0_MASK, + margining->results[1]); + else if (margining->lanes == USB4_MARGIN_SW_LANE_1) + errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_1_MASK, + margining->results[1]); + else if (margining->lanes == USB4_MARGIN_SW_ALL_LANES) + errors = margining->results[1]; + + /* Any errors stop the test */ + if (errors) + break; + + fsleep(DWELL_SAMPLE_INTERVAL * USEC_PER_MSEC); + } + +out_stop: + /* + * Stop the counters but don't clear them to allow the + * different error counter configurations. + */ + margining_modify_error_counter(margining, margining->lanes, + USB4_MARGIN_SW_ERROR_COUNTER_STOP); + return ret; +} + static int margining_run_write(void *data, u64 val) { struct tb_margining *margining = data; @@ -779,36 +1070,43 @@ static int margining_run_write(void *data, u64 val) clx = ret; } + /* Clear the results */ + memset(margining->results, 0, sizeof(margining->results)); + if (margining->software) { + struct usb4_port_margining_params params = { + .error_counter = USB4_MARGIN_SW_ERROR_COUNTER_CLEAR, + .lanes = margining->lanes, + .time = margining->time, + .voltage_time_offset = margining->voltage_time_offset, + .right_high = margining->right_high, + .optional_voltage_offset_range = margining->optional_voltage_offset_range, + }; + tb_port_dbg(port, "running software %s lane margining for %s lanes %u\n", margining->time ? "time" : "voltage", dev_name(dev), margining->lanes); - ret = usb4_port_sw_margin(port, margining->target, margining->index, - margining->lanes, margining->time, - margining->right_high, - USB4_MARGIN_SW_COUNTER_CLEAR); - if (ret) - goto out_clx; - ret = usb4_port_sw_margin_errors(port, margining->target, - margining->index, - &margining->results[0]); + ret = margining_run_sw(margining, ¶ms); } else { + struct usb4_port_margining_params params = { + .ber_level = margining->ber_level, + .lanes = margining->lanes, + .time = margining->time, + .right_high = margining->right_high, + .optional_voltage_offset_range = margining->optional_voltage_offset_range, + }; + tb_port_dbg(port, "running hardware %s lane margining for %s lanes %u\n", margining->time ? "time" : "voltage", dev_name(dev), margining->lanes); - /* Clear the results */ - margining->results[0] = 0; - margining->results[1] = 0; - ret = usb4_port_hw_margin(port, margining->target, margining->index, - margining->lanes, margining->ber_level, - margining->time, margining->right_high, + + ret = usb4_port_hw_margin(port, margining->target, margining->index, ¶ms, margining->results); } -out_clx: if (down_sw) tb_switch_clx_enable(down_sw, clx); out_unlock: @@ -837,6 +1135,13 @@ static ssize_t margining_results_write(struct file *file, margining->results[0] = 0; margining->results[1] = 0; + if (margining->software) { + /* Clear the error counters */ + margining_modify_error_counter(margining, + USB4_MARGIN_SW_ALL_LANES, + USB4_MARGIN_SW_ERROR_COUNTER_CLEAR); + } + mutex_unlock(&tb->lock); return count; } @@ -852,6 +1157,8 @@ static void voltage_margin_show(struct seq_file *s, if (val & USB4_MARGIN_HW_RES_1_EXCEEDS) seq_puts(s, " exceeds maximum"); seq_puts(s, "\n"); + if (margining->optional_voltage_offset_range) + seq_puts(s, " optional voltage offset range enabled\n"); } static void time_margin_show(struct seq_file *s, @@ -924,6 +1231,24 @@ static int margining_results_show(struct seq_file *s, void *not_used) voltage_margin_show(s, margining, val); } } + } else { + u32 lane_errors, result; + + seq_printf(s, "0x%08x\n", margining->results[1]); + result = FIELD_GET(USB4_MARGIN_SW_LANES_MASK, margining->results[0]); + + if (result == USB4_MARGIN_SW_LANE_0 || + result == USB4_MARGIN_SW_ALL_LANES) { + lane_errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_0_MASK, + margining->results[1]); + seq_printf(s, "# lane 0 errors: %u\n", lane_errors); + } + if (result == USB4_MARGIN_SW_LANE_1 || + result == USB4_MARGIN_SW_ALL_LANES) { + lane_errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_1_MASK, + margining->results[1]); + seq_printf(s, "# lane 1 errors: %u\n", lane_errors); + } } mutex_unlock(&tb->lock); @@ -1091,6 +1416,15 @@ static struct tb_margining *margining_alloc(struct tb_port *port, val = FIELD_GET(USB4_MARGIN_CAP_0_MAX_VOLTAGE_OFFSET_MASK, margining->caps[0]); margining->max_voltage_offset = 74 + val * 2; + if (supports_optional_voltage_offset_range(margining)) { + val = FIELD_GET(USB4_MARGIN_CAP_0_VOLT_STEPS_OPT_MASK, + margining->caps[0]); + margining->voltage_steps_optional_range = val; + val = FIELD_GET(USB4_MARGIN_CAP_1_MAX_VOLT_OFS_OPT_MASK, + margining->caps[1]); + margining->max_voltage_offset_optional_range = 74 + val * 2; + } + if (supports_time(margining)) { val = FIELD_GET(USB4_MARGIN_CAP_1_TIME_STEPS_MASK, margining->caps[1]); margining->time_steps = val; @@ -1127,6 +1461,22 @@ static struct tb_margining *margining_alloc(struct tb_port *port, independent_time_margins(margining) == USB4_MARGIN_CAP_1_TIME_LR)) debugfs_create_file("margin", 0600, dir, margining, &margining_margin_fops); + + margining->error_counter = USB4_MARGIN_SW_ERROR_COUNTER_CLEAR; + margining->dwell_time = MIN_DWELL_TIME; + + if (supports_optional_voltage_offset_range(margining)) + debugfs_create_file("optional_voltage_offset", DEBUGFS_MODE, dir, margining, + &margining_optional_voltage_offset_fops); + + if (supports_software(margining)) { + debugfs_create_file("voltage_time_offset", DEBUGFS_MODE, dir, margining, + &margining_voltage_time_offset_fops); + debugfs_create_file("error_counter", DEBUGFS_MODE, dir, margining, + &margining_error_counter_fops); + debugfs_create_file("dwell_time", DEBUGFS_MODE, dir, margining, + &margining_dwell_time_fops); + } return margining; } diff --git a/drivers/thunderbolt/sb_regs.h b/drivers/thunderbolt/sb_regs.h index 2a88edfc97b2..dbcad25ead50 100644 --- a/drivers/thunderbolt/sb_regs.h +++ b/drivers/thunderbolt/sb_regs.h @@ -57,6 +57,9 @@ enum usb4_sb_opcode { #define USB4_MARGIN_CAP_0_TIME BIT(5) #define USB4_MARGIN_CAP_0_VOLTAGE_STEPS_MASK GENMASK(12, 6) #define USB4_MARGIN_CAP_0_MAX_VOLTAGE_OFFSET_MASK GENMASK(18, 13) +#define USB4_MARGIN_CAP_0_OPT_VOLTAGE_SUPPORT BIT(19) +#define USB4_MARGIN_CAP_0_VOLT_STEPS_OPT_MASK GENMASK(26, 20) +#define USB4_MARGIN_CAP_1_MAX_VOLT_OFS_OPT_MASK GENMASK(7, 0) #define USB4_MARGIN_CAP_1_TIME_DESTR BIT(8) #define USB4_MARGIN_CAP_1_TIME_INDP_MASK GENMASK(10, 9) #define USB4_MARGIN_CAP_1_TIME_MIN 0x0 @@ -72,6 +75,7 @@ enum usb4_sb_opcode { #define USB4_MARGIN_HW_RH BIT(4) #define USB4_MARGIN_HW_BER_MASK GENMASK(9, 5) #define USB4_MARGIN_HW_BER_SHIFT 5 +#define USB4_MARGIN_HW_OPT_VOLTAGE BIT(10) /* Applicable to all margin values */ #define USB4_MARGIN_HW_RES_1_MARGIN_MASK GENMASK(6, 0) @@ -82,13 +86,17 @@ enum usb4_sb_opcode { #define USB4_MARGIN_HW_RES_1_L1_LL_MARGIN_SHIFT 24 /* USB4_SB_OPCODE_RUN_SW_LANE_MARGINING */ +#define USB4_MARGIN_SW_LANES_MASK GENMASK(2, 0) +#define USB4_MARGIN_SW_LANE_0 0x0 +#define USB4_MARGIN_SW_LANE_1 0x1 +#define USB4_MARGIN_SW_ALL_LANES 0x7 #define USB4_MARGIN_SW_TIME BIT(3) #define USB4_MARGIN_SW_RH BIT(4) +#define USB4_MARGIN_SW_OPT_VOLTAGE BIT(5) +#define USB4_MARGIN_SW_VT_MASK GENMASK(12, 6) #define USB4_MARGIN_SW_COUNTER_MASK GENMASK(14, 13) -#define USB4_MARGIN_SW_COUNTER_SHIFT 13 -#define USB4_MARGIN_SW_COUNTER_NOP 0x0 -#define USB4_MARGIN_SW_COUNTER_CLEAR 0x1 -#define USB4_MARGIN_SW_COUNTER_START 0x2 -#define USB4_MARGIN_SW_COUNTER_STOP 0x3 + +#define USB4_MARGIN_SW_ERR_COUNTER_LANE_0_MASK GENMASK(3, 0) +#define USB4_MARGIN_SW_ERR_COUNTER_LANE_1_MASK GENMASK(7, 4) #endif diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index b47f7873c847..6737188f2581 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -1353,14 +1353,48 @@ int usb4_port_sb_read(struct tb_port *port, enum usb4_sb_target target, u8 index int usb4_port_sb_write(struct tb_port *port, enum usb4_sb_target target, u8 index, u8 reg, const void *buf, u8 size); +/** + * enum usb4_margin_sw_error_counter - Software margining error counter operation + * @USB4_MARGIN_SW_ERROR_COUNTER_NOP: No change in counter setup + * @USB4_MARGIN_SW_ERROR_COUNTER_CLEAR: Set the error counter to 0, enable counter + * @USB4_MARGIN_SW_ERROR_COUNTER_START: Start counter, count from last value + * @USB4_MARGIN_SW_ERROR_COUNTER_STOP: Stop counter, do not clear value + */ +enum usb4_margin_sw_error_counter { + USB4_MARGIN_SW_ERROR_COUNTER_NOP, + USB4_MARGIN_SW_ERROR_COUNTER_CLEAR, + USB4_MARGIN_SW_ERROR_COUNTER_START, + USB4_MARGIN_SW_ERROR_COUNTER_STOP, +}; + +/** + * struct usb4_port_margining_params - USB4 margining parameters + * @error_counter: Error counter operation for software margining + * @ber_level: Current BER level contour value + * @lanes: %0, %1 or %7 (all) + * @voltage_time_offset: Offset for voltage / time for software margining + * @optional_voltage_offset_range: Enable optional extended voltage range + * @right_high: %false if left/low margin test is performed, %true if right/high + * @time: %true if time margining is used instead of voltage + */ +struct usb4_port_margining_params { + enum usb4_margin_sw_error_counter error_counter; + u32 ber_level; + u32 lanes; + u32 voltage_time_offset; + bool optional_voltage_offset_range; + bool right_high; + bool time; +}; + int usb4_port_margining_caps(struct tb_port *port, enum usb4_sb_target target, u8 index, u32 *caps); int usb4_port_hw_margin(struct tb_port *port, enum usb4_sb_target target, - u8 index, unsigned int lanes, unsigned int ber_level, - bool timing, bool right_high, u32 *results); + u8 index, const struct usb4_port_margining_params *params, + u32 *results); int usb4_port_sw_margin(struct tb_port *port, enum usb4_sb_target target, - u8 index, unsigned int lanes, bool timing, - bool right_high, u32 counter); + u8 index, const struct usb4_port_margining_params *params, + u32 *results); int usb4_port_sw_margin_errors(struct tb_port *port, enum usb4_sb_target target, u8 index, u32 *errors); diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c index 4d83b65afb5b..0a9b4aeb3fa1 100644 --- a/drivers/thunderbolt/usb4.c +++ b/drivers/thunderbolt/usb4.c @@ -1653,31 +1653,31 @@ int usb4_port_margining_caps(struct tb_port *port, enum usb4_sb_target target, * @port: USB4 port * @target: Sideband target * @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER - * @lanes: Which lanes to run (must match the port capabilities). Can be - * %0, %1 or %7. - * @ber_level: BER level contour value - * @timing: Perform timing margining instead of voltage - * @right_high: Use Right/high margin instead of left/low + * @params: Parameters for USB4 hardware margining * @results: Array with at least two elements to hold the results * * Runs hardware lane margining on USB4 port and returns the result in * @results. */ int usb4_port_hw_margin(struct tb_port *port, enum usb4_sb_target target, - u8 index, unsigned int lanes, unsigned int ber_level, - bool timing, bool right_high, u32 *results) + u8 index, const struct usb4_port_margining_params *params, + u32 *results) { u32 val; int ret; - val = lanes; - if (timing) + if (WARN_ON_ONCE(!params)) + return -EINVAL; + + val = params->lanes; + if (params->time) val |= USB4_MARGIN_HW_TIME; - if (right_high) + if (params->right_high) val |= USB4_MARGIN_HW_RH; - if (ber_level) - val |= (ber_level << USB4_MARGIN_HW_BER_SHIFT) & - USB4_MARGIN_HW_BER_MASK; + if (params->ber_level) + val |= FIELD_PREP(USB4_MARGIN_HW_BER_MASK, params->ber_level); + if (params->optional_voltage_offset_range) + val |= USB4_MARGIN_HW_OPT_VOLTAGE; ret = usb4_port_sb_write(port, target, index, USB4_SB_METADATA, &val, sizeof(val)); @@ -1698,38 +1698,46 @@ int usb4_port_hw_margin(struct tb_port *port, enum usb4_sb_target target, * @port: USB4 port * @target: Sideband target * @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER - * @lanes: Which lanes to run (must match the port capabilities). Can be - * %0, %1 or %7. - * @timing: Perform timing margining instead of voltage - * @right_high: Use Right/high margin instead of left/low - * @counter: What to do with the error counter + * @params: Parameters for USB4 software margining + * @results: Data word for the operation completion data * * Runs software lane margining on USB4 port. Read back the error * counters by calling usb4_port_sw_margin_errors(). Returns %0 in * success and negative errno otherwise. */ int usb4_port_sw_margin(struct tb_port *port, enum usb4_sb_target target, - u8 index, unsigned int lanes, bool timing, - bool right_high, u32 counter) + u8 index, const struct usb4_port_margining_params *params, + u32 *results) { u32 val; int ret; - val = lanes; - if (timing) + if (WARN_ON_ONCE(!params)) + return -EINVAL; + + val = params->lanes; + if (params->time) val |= USB4_MARGIN_SW_TIME; - if (right_high) + if (params->optional_voltage_offset_range) + val |= USB4_MARGIN_SW_OPT_VOLTAGE; + if (params->right_high) val |= USB4_MARGIN_SW_RH; - val |= (counter << USB4_MARGIN_SW_COUNTER_SHIFT) & - USB4_MARGIN_SW_COUNTER_MASK; + val |= FIELD_PREP(USB4_MARGIN_SW_COUNTER_MASK, params->error_counter); + val |= FIELD_PREP(USB4_MARGIN_SW_VT_MASK, params->voltage_time_offset); ret = usb4_port_sb_write(port, target, index, USB4_SB_METADATA, &val, sizeof(val)); if (ret) return ret; - return usb4_port_sb_op(port, target, index, - USB4_SB_OPCODE_RUN_SW_LANE_MARGINING, 2500); + ret = usb4_port_sb_op(port, target, index, + USB4_SB_OPCODE_RUN_SW_LANE_MARGINING, 2500); + if (ret) + return ret; + + return usb4_port_sb_read(port, target, index, USB4_SB_DATA, results, + sizeof(*results)); + } /** diff --git a/drivers/tty/hvc/hvsi_lib.c b/drivers/tty/hvc/hvsi_lib.c index 22e1bc4d8a66..b35c44caf3d7 100644 --- a/drivers/tty/hvc/hvsi_lib.c +++ b/drivers/tty/hvc/hvsi_lib.c @@ -303,7 +303,7 @@ int hvsilib_write_mctrl(struct hvsi_priv *pv, int dtr) pr_devel("HVSI@%x: %s DTR...\n", pv->termno, dtr ? "Setting" : "Clearing"); - ctrl.hdr.type = VS_CONTROL_PACKET_HEADER, + ctrl.hdr.type = VS_CONTROL_PACKET_HEADER; ctrl.hdr.len = sizeof(struct hvsi_control); ctrl.verb = cpu_to_be16(VSV_SET_MODEM_CTL); ctrl.mask = cpu_to_be32(HVSI_TSDTR); diff --git a/drivers/tty/mxser.c b/drivers/tty/mxser.c index 5b97e420a95f..4d45eca4929a 100644 --- a/drivers/tty/mxser.c +++ b/drivers/tty/mxser.c @@ -208,9 +208,6 @@ static const struct { }; #define UART_INFO_NUM ARRAY_SIZE(Gpci_uart_info) - -/* driver_data correspond to the lines in the structure above - see also ISA probe function before you change something */ static const struct pci_device_id mxser_pcibrds[] = { { PCI_DEVICE_DATA(MOXA, C168, 8) }, { PCI_DEVICE_DATA(MOXA, C104, 4) }, @@ -986,7 +983,7 @@ static int mxser_get_serial_info(struct tty_struct *tty, ss->baud_base = MXSER_BAUD_BASE; ss->close_delay = close_delay; ss->closing_wait = closing_wait; - ss->custom_divisor = MXSER_CUSTOM_DIVISOR, + ss->custom_divisor = MXSER_CUSTOM_DIVISOR; mutex_unlock(&port->mutex); return 0; } @@ -1773,8 +1770,6 @@ static void mxser_initbrd(struct mxser_board *brd, bool high_baud) mxser_process_txrx_fifo(info); - info->port.close_delay = 5 * HZ / 10; - info->port.closing_wait = 30 * HZ; spin_lock_init(&info->slock); /* before set INT ISR, disable all int */ diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c index 8913cdd675f6..ebf0bbc2cff2 100644 --- a/drivers/tty/serdev/core.c +++ b/drivers/tty/serdev/core.c @@ -529,7 +529,7 @@ static int of_serdev_register_devices(struct serdev_controller *ctrl) bool found = false; for_each_available_child_of_node(ctrl->dev.of_node, node) { - if (!of_get_property(node, "compatible", NULL)) + if (!of_property_present(node, "compatible")) continue; dev_dbg(&ctrl->dev, "adding child %pOF\n", node); diff --git a/drivers/tty/serial/8250/8250_aspeed_vuart.c b/drivers/tty/serial/8250/8250_aspeed_vuart.c index 53d8eee9b1c8..25c201cfb91e 100644 --- a/drivers/tty/serial/8250/8250_aspeed_vuart.c +++ b/drivers/tty/serial/8250/8250_aspeed_vuart.c @@ -561,6 +561,7 @@ static const struct of_device_id aspeed_vuart_table[] = { { .compatible = "aspeed,ast2500-vuart" }, { }, }; +MODULE_DEVICE_TABLE(of, aspeed_vuart_table); static struct platform_driver aspeed_vuart_driver = { .driver = { diff --git a/drivers/tty/serial/8250/8250_bcm2835aux.c b/drivers/tty/serial/8250/8250_bcm2835aux.c index 121a5ce86050..d7a0f271263a 100644 --- a/drivers/tty/serial/8250/8250_bcm2835aux.c +++ b/drivers/tty/serial/8250/8250_bcm2835aux.c @@ -13,6 +13,7 @@ */ #include +#include #include #include #include @@ -213,11 +214,57 @@ static const struct acpi_device_id bcm2835aux_serial_acpi_match[] = { }; MODULE_DEVICE_TABLE(acpi, bcm2835aux_serial_acpi_match); +static bool bcm2835aux_can_disable_clock(struct device *dev) +{ + struct bcm2835aux_data *data = dev_get_drvdata(dev); + struct uart_8250_port *up = serial8250_get_port(data->line); + + if (device_may_wakeup(dev)) + return false; + + if (uart_console(&up->port) && !console_suspend_enabled) + return false; + + return true; +} + +static int bcm2835aux_suspend(struct device *dev) +{ + struct bcm2835aux_data *data = dev_get_drvdata(dev); + + serial8250_suspend_port(data->line); + + if (!bcm2835aux_can_disable_clock(dev)) + return 0; + + clk_disable_unprepare(data->clk); + return 0; +} + +static int bcm2835aux_resume(struct device *dev) +{ + struct bcm2835aux_data *data = dev_get_drvdata(dev); + int ret; + + if (bcm2835aux_can_disable_clock(dev)) { + ret = clk_prepare_enable(data->clk); + if (ret) + return ret; + } + + serial8250_resume_port(data->line); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(bcm2835aux_dev_pm_ops, bcm2835aux_suspend, bcm2835aux_resume); + static struct platform_driver bcm2835aux_serial_driver = { .driver = { .name = "bcm2835-aux-uart", .of_match_table = bcm2835aux_serial_match, .acpi_match_table = bcm2835aux_serial_acpi_match, + .pm = pm_ptr(&bcm2835aux_dev_pm_ops), }, .probe = bcm2835aux_serial_probe, .remove_new = bcm2835aux_serial_remove, diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c index 8a353e3cc3dd..d215c494ee24 100644 --- a/drivers/tty/serial/8250/8250_dma.c +++ b/drivers/tty/serial/8250/8250_dma.c @@ -89,7 +89,9 @@ int serial8250_tx_dma(struct uart_8250_port *p) struct tty_port *tport = &p->port.state->port; struct dma_async_tx_descriptor *desc; struct uart_port *up = &p->port; - struct scatterlist sg; + struct scatterlist *sg; + struct scatterlist sgl[2]; + int i; int ret; if (dma->tx_running) { @@ -110,18 +112,17 @@ int serial8250_tx_dma(struct uart_8250_port *p) serial8250_do_prepare_tx_dma(p); - sg_init_table(&sg, 1); - /* kfifo can do more than one sg, we don't (quite yet) */ - ret = kfifo_dma_out_prepare_mapped(&tport->xmit_fifo, &sg, 1, + sg_init_table(sgl, ARRAY_SIZE(sgl)); + + ret = kfifo_dma_out_prepare_mapped(&tport->xmit_fifo, sgl, ARRAY_SIZE(sgl), UART_XMIT_SIZE, dma->tx_addr); - /* we already checked empty fifo above, so there should be something */ - if (WARN_ON_ONCE(ret != 1)) - return 0; + dma->tx_size = 0; - dma->tx_size = sg_dma_len(&sg); + for_each_sg(sgl, sg, ret, i) + dma->tx_size += sg_dma_len(sg); - desc = dmaengine_prep_slave_sg(dma->txchan, &sg, 1, + desc = dmaengine_prep_slave_sg(dma->txchan, sgl, ret, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { diff --git a/drivers/tty/serial/8250/8250_dwlib.c b/drivers/tty/serial/8250/8250_dwlib.c index 5a2520943dfd..b055d89cfb39 100644 --- a/drivers/tty/serial/8250/8250_dwlib.c +++ b/drivers/tty/serial/8250/8250_dwlib.c @@ -89,7 +89,7 @@ static void dw8250_set_divisor(struct uart_port *p, unsigned int baud, unsigned int quot, unsigned int quot_frac) { dw8250_writel_ext(p, DW_UART_DLF, quot_frac); - serial8250_do_set_divisor(p, baud, quot, quot_frac); + serial8250_do_set_divisor(p, baud, quot); } void dw8250_do_set_termios(struct uart_port *p, struct ktermios *termios, diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c index e3f482fd3de4..6176083d0341 100644 --- a/drivers/tty/serial/8250/8250_early.c +++ b/drivers/tty/serial/8250/8250_early.c @@ -171,6 +171,17 @@ OF_EARLYCON_DECLARE(ns16550a, "ns16550a", early_serial8250_setup); OF_EARLYCON_DECLARE(uart, "nvidia,tegra20-uart", early_serial8250_setup); OF_EARLYCON_DECLARE(uart, "snps,dw-apb-uart", early_serial8250_setup); +static int __init early_serial8250_rs2_setup(struct earlycon_device *device, + const char *options) +{ + device->port.regshift = 2; + + return early_serial8250_setup(device, options); +} +OF_EARLYCON_DECLARE(uart, "intel,xscale-uart", early_serial8250_rs2_setup); +OF_EARLYCON_DECLARE(uart, "mrvl,mmp-uart", early_serial8250_rs2_setup); +OF_EARLYCON_DECLARE(uart, "mrvl,pxa-uart", early_serial8250_rs2_setup); + #ifdef CONFIG_SERIAL_8250_OMAP static int __init early_omap8250_setup(struct earlycon_device *device, diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 616128254bbd..b7a75db15249 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -500,7 +500,7 @@ static unsigned int xr17v35x_get_divisor(struct uart_port *p, unsigned int baud, static void xr17v35x_set_divisor(struct uart_port *p, unsigned int baud, unsigned int quot, unsigned int quot_frac) { - serial8250_do_set_divisor(p, baud, quot, quot_frac); + serial8250_do_set_divisor(p, baud, quot); /* Preserve bits not related to baudrate; DLD[7:4]. */ quot_frac |= serial_port_in(p, 0x2) & 0xf0; diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index afef1dd4ddf4..88b58f44e4e9 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -137,7 +137,6 @@ struct omap8250_priv { atomic_t active; bool is_suspending; int wakeirq; - int wakeups_enabled; u32 latency; u32 calc_latency; struct pm_qos_request pm_qos_request; @@ -1523,7 +1522,10 @@ static int omap8250_probe(struct platform_device *pdev) platform_set_drvdata(pdev, priv); - device_init_wakeup(&pdev->dev, true); + device_set_wakeup_capable(&pdev->dev, true); + if (of_property_read_bool(np, "wakeup-source")) + device_set_wakeup_enable(&pdev->dev, true); + pm_runtime_enable(&pdev->dev); pm_runtime_use_autosuspend(&pdev->dev); @@ -1581,7 +1583,7 @@ static int omap8250_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, up.port.irq, omap8250_irq, 0, dev_name(&pdev->dev), priv); if (ret < 0) - return ret; + goto err; priv->wakeirq = irq_of_parse_and_map(np, 1); @@ -1622,7 +1624,7 @@ static void omap8250_remove(struct platform_device *pdev) flush_work(&priv->qos_work); pm_runtime_disable(&pdev->dev); cpu_latency_qos_remove_request(&priv->pm_qos_request); - device_init_wakeup(&pdev->dev, false); + device_set_wakeup_capable(&pdev->dev, false); } static int omap8250_prepare(struct device *dev) diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index e1d7aa2fa347..6709b6a5f301 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1277,7 +1277,7 @@ static void pci_oxsemi_tornado_set_divisor(struct uart_port *port, serial_icr_write(up, UART_TCR, tcr); serial_icr_write(up, UART_CPR, cpr); serial_icr_write(up, UART_CKS, cpr2); - serial8250_do_set_divisor(port, baud, quot, 0); + serial8250_do_set_divisor(port, baud, quot); } /* diff --git a/drivers/tty/serial/8250/8250_platform.c b/drivers/tty/serial/8250/8250_platform.c index d5c8d851348d..be7ff07cbdd0 100644 --- a/drivers/tty/serial/8250/8250_platform.c +++ b/drivers/tty/serial/8250/8250_platform.c @@ -2,11 +2,15 @@ /* * Universal/legacy platform driver for 8250/16550-type serial ports * - * Supports: ISA-compatible 8250/16550 ports + * Supports: + * ISA-compatible 8250/16550 ports + * ACPI 8250/16550 ports * PNP 8250/16550 ports * "serial8250" platform devices */ +#include #include +#include #include #include #include @@ -22,9 +26,9 @@ /* * Configuration: - * share_irqs Whether we pass IRQF_SHARED to request_irq(). + * share_irqs: Whether we pass IRQF_SHARED to request_irq(). * This option is unsafe when used on edge-triggered interrupts. - * skip_txen_test Force skip of txen test at init time. + * skip_txen_test: Force skip of txen test at init time. */ unsigned int share_irqs = SERIAL8250_SHARE_IRQS; unsigned int skip_txen_test; @@ -61,9 +65,9 @@ static void __init __serial8250_isa_init_ports(void) nr_uarts = UART_NR; /* - * Set up initial isa ports based on nr_uart module param, or else + * Set up initial ISA ports based on nr_uart module param, or else * default to CONFIG_SERIAL_8250_RUNTIME_UARTS. Note that we do not - * need to increase nr_uarts when setting up the initial isa ports. + * need to increase nr_uarts when setting up the initial ISA ports. */ for (i = 0; i < nr_uarts; i++) serial8250_setup_port(i); @@ -101,13 +105,63 @@ void __init serial8250_isa_init_ports(void) } /* - * Register a set of serial devices attached to a platform device. The - * list is terminated with a zero flags entry, which means we expect - * all entries to have at least UPF_BOOT_AUTOCONF set. + * Generic 16550A platform devices */ -static int serial8250_probe(struct platform_device *dev) +static int serial8250_probe_acpi(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct uart_8250_port uart = { }; + struct resource *regs; + unsigned char iotype; + int ret, line; + + regs = platform_get_mem_or_io(pdev, 0); + if (!regs) + return dev_err_probe(dev, -EINVAL, "no registers defined\n"); + + switch (resource_type(regs)) { + case IORESOURCE_IO: + uart.port.iobase = regs->start; + iotype = UPIO_PORT; + break; + case IORESOURCE_MEM: + uart.port.mapbase = regs->start; + uart.port.mapsize = resource_size(regs); + uart.port.flags = UPF_IOREMAP; + iotype = UPIO_MEM; + break; + default: + return -EINVAL; + } + + /* default clock frequency */ + uart.port.uartclk = 1843200; + uart.port.type = PORT_16550A; + uart.port.dev = &pdev->dev; + uart.port.flags |= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF; + + ret = uart_read_and_validate_port_properties(&uart.port); + /* no interrupt -> fall back to polling */ + if (ret == -ENXIO) + ret = 0; + if (ret) + return ret; + + /* + * The previous call may not set iotype correctly when reg-io-width + * property is absent and it doesn't support IO port resource. + */ + uart.port.iotype = iotype; + + line = serial8250_register_8250_port(&uart); + if (line < 0) + return line; + + return 0; +} + +static int serial8250_probe_platform(struct platform_device *dev, struct plat_serial8250_port *p) { - struct plat_serial8250_port *p = dev_get_platdata(&dev->dev); struct uart_8250_port uart; int ret, i, irqflag = 0; @@ -155,6 +209,31 @@ static int serial8250_probe(struct platform_device *dev) return 0; } +/* + * Register a set of serial devices attached to a platform device. + * The list is terminated with a zero flags entry, which means we expect + * all entries to have at least UPF_BOOT_AUTOCONF set. + */ +static int serial8250_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct plat_serial8250_port *p; + + p = dev_get_platdata(dev); + if (p) + return serial8250_probe_platform(pdev, p); + + /* + * Probe platform UART devices defined using standard hardware + * discovery mechanism like ACPI or DT. Support only ACPI based + * serial device for now. + */ + if (has_acpi_companion(dev)) + return serial8250_probe_acpi(pdev); + + return 0; +} + /* * Remove serial ports registered against a platform device. */ @@ -198,6 +277,12 @@ static int serial8250_resume(struct platform_device *dev) return 0; } +static const struct acpi_device_id acpi_platform_serial_table[] = { + { "RSCV0003" }, /* RISC-V Generic 16550A UART */ + { } +}; +MODULE_DEVICE_TABLE(acpi, acpi_platform_serial_table); + static struct platform_driver serial8250_isa_driver = { .probe = serial8250_probe, .remove_new = serial8250_remove, @@ -205,12 +290,13 @@ static struct platform_driver serial8250_isa_driver = { .resume = serial8250_resume, .driver = { .name = "serial8250", + .acpi_match_table = acpi_platform_serial_table, }, }; /* * This "device" covers _all_ ISA 8250-compatible serial devices listed - * in the table in include/asm/serial.h + * in the table in include/asm/serial.h. */ struct platform_device *serial8250_isa_devs; @@ -239,8 +325,7 @@ static int __init serial8250_init(void) if (ret) goto unreg_uart_drv; - serial8250_isa_devs = platform_device_alloc("serial8250", - PLAT8250_DEV_LEGACY); + serial8250_isa_devs = platform_device_alloc("serial8250", PLAT8250_DEV_LEGACY); if (!serial8250_isa_devs) { ret = -ENOMEM; goto unreg_pnp; @@ -279,7 +364,7 @@ static void __exit serial8250_exit(void) /* * This tells serial8250_unregister_port() not to re-register * the ports (thereby making serial8250_isa_driver permanently - * in use.) + * in use). */ serial8250_isa_devs = NULL; @@ -312,12 +397,13 @@ MODULE_ALIAS_CHARDEV_MAJOR(TTY_MAJOR); #ifdef CONFIG_SERIAL_8250_DEPRECATED_OPTIONS #ifndef MODULE -/* This module was renamed to 8250_core in 3.7. Keep the old "8250" name - * working as well for the module options so we don't break people. We +/* + * This module was renamed to 8250_core in 3.7. Keep the old "8250" name + * working as well for the module options so we don't break people. We * need to keep the names identical and the convenient macros will happily * refuse to let us do that by failing the build with redefinition errors - * of global variables. So we stick them inside a dummy function to avoid - * those conflicts. The options still get parsed, and the redefined + * of global variables. So we stick them inside a dummy function to avoid + * those conflicts. The options still get parsed, and the redefined * MODULE_PARAM_PREFIX lets us keep the "8250." syntax alive. * * This is hacky. I'm sorry. diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 2786918aea98..3509af7dc52b 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -2609,7 +2609,7 @@ static unsigned char serial8250_compute_lcr(struct uart_8250_port *up, } void serial8250_do_set_divisor(struct uart_port *port, unsigned int baud, - unsigned int quot, unsigned int quot_frac) + unsigned int quot) { struct uart_8250_port *up = up_to_u8250p(port); @@ -2641,7 +2641,7 @@ static void serial8250_set_divisor(struct uart_port *port, unsigned int baud, if (port->set_divisor) port->set_divisor(port, baud, quot, quot_frac); else - serial8250_do_set_divisor(port, baud, quot, quot_frac); + serial8250_do_set_divisor(port, baud, quot); } static unsigned int serial8250_get_baud_rate(struct uart_port *port, diff --git a/drivers/tty/serial/8250/8250_pxa.c b/drivers/tty/serial/8250/8250_pxa.c index 1ac86b565374..96dd6126296c 100644 --- a/drivers/tty/serial/8250/8250_pxa.c +++ b/drivers/tty/serial/8250/8250_pxa.c @@ -165,22 +165,6 @@ static struct platform_driver serial_pxa_driver = { module_platform_driver(serial_pxa_driver); -#ifdef CONFIG_SERIAL_8250_CONSOLE -static int __init early_serial_pxa_setup(struct earlycon_device *device, - const char *options) -{ - struct uart_port *port = &device->port; - - if (!(device->port.membase || device->port.iobase)) - return -ENODEV; - - port->regshift = 2; - return early_serial8250_setup(device, NULL); -} -OF_EARLYCON_DECLARE(early_pxa, "mrvl,pxa-uart", early_serial_pxa_setup); -OF_EARLYCON_DECLARE(mmp, "mrvl,mmp-uart", early_serial_pxa_setup); -#endif - MODULE_AUTHOR("Sergei Ianovich"); MODULE_DESCRIPTION("driver for PXA on-board UARTS"); MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index 69a632fefc41..6f0db310cf69 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -124,13 +124,14 @@ struct qcom_geni_serial_port { dma_addr_t tx_dma_addr; dma_addr_t rx_dma_addr; bool setup; - unsigned int baud; + unsigned long poll_timeout_us; unsigned long clk_rate; void *rx_buf; u32 loopback; bool brk; unsigned int tx_remaining; + unsigned int tx_queued; int wakeup_irq; bool rx_tx_swap; bool cts_rts_swap; @@ -144,6 +145,9 @@ static const struct uart_ops qcom_geni_uart_pops; static struct uart_driver qcom_geni_console_driver; static struct uart_driver qcom_geni_uart_driver; +static void __qcom_geni_serial_cancel_tx_cmd(struct uart_port *uport); +static void qcom_geni_serial_cancel_tx_cmd(struct uart_port *uport); + static inline struct qcom_geni_serial_port *to_dev_port(struct uart_port *uport) { return container_of(uport, struct qcom_geni_serial_port, uport); @@ -265,27 +269,18 @@ static bool qcom_geni_serial_secondary_active(struct uart_port *uport) return readl(uport->membase + SE_GENI_STATUS) & S_GENI_CMD_ACTIVE; } -static bool qcom_geni_serial_poll_bit(struct uart_port *uport, - int offset, int field, bool set) +static bool qcom_geni_serial_poll_bitfield(struct uart_port *uport, + unsigned int offset, u32 field, u32 val) { u32 reg; struct qcom_geni_serial_port *port; - unsigned int baud; - unsigned int fifo_bits; unsigned long timeout_us = 20000; struct qcom_geni_private_data *private_data = uport->private_data; if (private_data->drv) { port = to_dev_port(uport); - baud = port->baud; - if (!baud) - baud = 115200; - fifo_bits = port->tx_fifo_depth * port->tx_fifo_width; - /* - * Total polling iterations based on FIFO worth of bytes to be - * sent at current baud. Add a little fluff to the wait. - */ - timeout_us = ((fifo_bits * USEC_PER_SEC) / baud) + 500; + if (port->poll_timeout_us) + timeout_us = port->poll_timeout_us; } /* @@ -295,7 +290,7 @@ static bool qcom_geni_serial_poll_bit(struct uart_port *uport, timeout_us = DIV_ROUND_UP(timeout_us, 10) * 10; while (timeout_us) { reg = readl(uport->membase + offset); - if ((bool)(reg & field) == set) + if ((reg & field) == val) return true; udelay(10); timeout_us -= 10; @@ -303,6 +298,12 @@ static bool qcom_geni_serial_poll_bit(struct uart_port *uport, return false; } +static bool qcom_geni_serial_poll_bit(struct uart_port *uport, + unsigned int offset, u32 field, bool set) +{ + return qcom_geni_serial_poll_bitfield(uport, offset, field, set ? field : 0); +} + static void qcom_geni_serial_setup_tx(struct uart_port *uport, u32 xmit_size) { u32 m_cmd; @@ -315,18 +316,16 @@ static void qcom_geni_serial_setup_tx(struct uart_port *uport, u32 xmit_size) static void qcom_geni_serial_poll_tx_done(struct uart_port *uport) { int done; - u32 irq_clear = M_CMD_DONE_EN; done = qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, M_CMD_DONE_EN, true); if (!done) { writel(M_GENI_CMD_ABORT, uport->membase + SE_GENI_M_CMD_CTRL_REG); - irq_clear |= M_CMD_ABORT_EN; qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, M_CMD_ABORT_EN, true); + writel(M_CMD_ABORT_EN, uport->membase + SE_GENI_M_IRQ_CLEAR); } - writel(irq_clear, uport->membase + SE_GENI_M_IRQ_CLEAR); } static void qcom_geni_serial_abort_rx(struct uart_port *uport) @@ -386,17 +385,27 @@ static int qcom_geni_serial_get_char(struct uart_port *uport) static void qcom_geni_serial_poll_put_char(struct uart_port *uport, unsigned char c) { - writel(DEF_TX_WM, uport->membase + SE_GENI_TX_WATERMARK_REG); + if (qcom_geni_serial_main_active(uport)) { + qcom_geni_serial_poll_tx_done(uport); + __qcom_geni_serial_cancel_tx_cmd(uport); + } + + writel(M_CMD_DONE_EN, uport->membase + SE_GENI_M_IRQ_CLEAR); qcom_geni_serial_setup_tx(uport, 1); - WARN_ON(!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, - M_TX_FIFO_WATERMARK_EN, true)); writel(c, uport->membase + SE_GENI_TX_FIFOn); - writel(M_TX_FIFO_WATERMARK_EN, uport->membase + SE_GENI_M_IRQ_CLEAR); qcom_geni_serial_poll_tx_done(uport); } #endif #ifdef CONFIG_SERIAL_QCOM_GENI_CONSOLE +static void qcom_geni_serial_drain_fifo(struct uart_port *uport) +{ + struct qcom_geni_serial_port *port = to_dev_port(uport); + + qcom_geni_serial_poll_bitfield(uport, SE_GENI_M_GP_LENGTH, GP_LENGTH, + port->tx_queued); +} + static void qcom_geni_serial_wr_char(struct uart_port *uport, unsigned char ch) { struct qcom_geni_private_data *private_data = uport->private_data; @@ -431,6 +440,7 @@ __qcom_geni_serial_console_write(struct uart_port *uport, const char *s, } writel(DEF_TX_WM, uport->membase + SE_GENI_TX_WATERMARK_REG); + writel(M_CMD_DONE_EN, uport->membase + SE_GENI_M_IRQ_CLEAR); qcom_geni_serial_setup_tx(uport, bytes_to_send); for (i = 0; i < count; ) { size_t chars_to_write = 0; @@ -469,10 +479,9 @@ static void qcom_geni_serial_console_write(struct console *co, const char *s, { struct uart_port *uport; struct qcom_geni_serial_port *port; + u32 m_irq_en, s_irq_en; bool locked = true; unsigned long flags; - u32 geni_status; - u32 irq_en; WARN_ON(co->index < 0 || co->index >= GENI_UART_CONS_PORTS); @@ -486,40 +495,28 @@ static void qcom_geni_serial_console_write(struct console *co, const char *s, else uart_port_lock_irqsave(uport, &flags); - geni_status = readl(uport->membase + SE_GENI_STATUS); + m_irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN); + s_irq_en = readl(uport->membase + SE_GENI_S_IRQ_EN); + writel(0, uport->membase + SE_GENI_M_IRQ_EN); + writel(0, uport->membase + SE_GENI_S_IRQ_EN); - if (!locked) { - /* - * We can only get here if an oops is in progress then we were - * unable to get the lock. This means we can't safely access - * our state variables like tx_remaining. About the best we - * can do is wait for the FIFO to be empty before we start our - * transfer, so we'll do that. - */ - qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, - M_TX_FIFO_NOT_EMPTY_EN, false); - } else if ((geni_status & M_GENI_CMD_ACTIVE) && !port->tx_remaining) { - /* - * It seems we can't interrupt existing transfers if all data - * has been sent, in which case we need to look for done first. - */ - qcom_geni_serial_poll_tx_done(uport); + if (qcom_geni_serial_main_active(uport)) { + /* Wait for completion or drain FIFO */ + if (!locked || port->tx_remaining == 0) + qcom_geni_serial_poll_tx_done(uport); + else + qcom_geni_serial_drain_fifo(uport); - if (!kfifo_is_empty(&uport->state->port.xmit_fifo)) { - irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN); - writel(irq_en | M_TX_FIFO_WATERMARK_EN, - uport->membase + SE_GENI_M_IRQ_EN); - } + qcom_geni_serial_cancel_tx_cmd(uport); } __qcom_geni_serial_console_write(uport, s, count); + writel(m_irq_en, uport->membase + SE_GENI_M_IRQ_EN); + writel(s_irq_en, uport->membase + SE_GENI_S_IRQ_EN); - if (locked) { - if (port->tx_remaining) - qcom_geni_serial_setup_tx(uport, port->tx_remaining); + if (locked) uart_port_unlock_irqrestore(uport, flags); - } } static void handle_rx_console(struct uart_port *uport, u32 bytes, bool drop) @@ -682,13 +679,10 @@ static void qcom_geni_serial_stop_tx_fifo(struct uart_port *uport) writel(irq_en, uport->membase + SE_GENI_M_IRQ_EN); } -static void qcom_geni_serial_cancel_tx_cmd(struct uart_port *uport) +static void __qcom_geni_serial_cancel_tx_cmd(struct uart_port *uport) { struct qcom_geni_serial_port *port = to_dev_port(uport); - if (!qcom_geni_serial_main_active(uport)) - return; - geni_se_cancel_m_cmd(&port->se); if (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, M_CMD_CANCEL_EN, true)) { @@ -698,8 +692,19 @@ static void qcom_geni_serial_cancel_tx_cmd(struct uart_port *uport) writel(M_CMD_ABORT_EN, uport->membase + SE_GENI_M_IRQ_CLEAR); } writel(M_CMD_CANCEL_EN, uport->membase + SE_GENI_M_IRQ_CLEAR); +} + +static void qcom_geni_serial_cancel_tx_cmd(struct uart_port *uport) +{ + struct qcom_geni_serial_port *port = to_dev_port(uport); + + if (!qcom_geni_serial_main_active(uport)) + return; + + __qcom_geni_serial_cancel_tx_cmd(uport); port->tx_remaining = 0; + port->tx_queued = 0; } static void qcom_geni_serial_handle_rx_fifo(struct uart_port *uport, bool drop) @@ -923,9 +928,10 @@ static void qcom_geni_serial_handle_tx_fifo(struct uart_port *uport, if (!chunk) goto out_write_wakeup; - if (!port->tx_remaining) { + if (!active) { qcom_geni_serial_setup_tx(uport, pending); port->tx_remaining = pending; + port->tx_queued = 0; irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN); if (!(irq_en & M_TX_FIFO_WATERMARK_EN)) @@ -934,6 +940,7 @@ static void qcom_geni_serial_handle_tx_fifo(struct uart_port *uport, } qcom_geni_serial_send_chunk_fifo(uport, chunk); + port->tx_queued += chunk; /* * The tx fifo watermark is level triggered and latched. Though we had @@ -1244,11 +1251,11 @@ static void qcom_geni_serial_set_termios(struct uart_port *uport, unsigned long clk_rate; u32 ver, sampling_rate; unsigned int avg_bw_core; + unsigned long timeout; qcom_geni_serial_stop_rx(uport); /* baud rate */ baud = uart_get_baud_rate(uport, termios, old, 300, 4000000); - port->baud = baud; sampling_rate = UART_OVERSAMPLING; /* Sampling rate is halved for IP versions >= 2.5 */ @@ -1326,9 +1333,21 @@ static void qcom_geni_serial_set_termios(struct uart_port *uport, else tx_trans_cfg |= UART_CTS_MASK; - if (baud) + if (baud) { uart_update_timeout(uport, termios->c_cflag, baud); + /* + * Make sure that qcom_geni_serial_poll_bitfield() waits for + * the FIFO, two-word intermediate transfer register and shift + * register to clear. + * + * Note that uart_fifo_timeout() also adds a 20 ms margin. + */ + timeout = jiffies_to_usecs(uart_fifo_timeout(uport)); + timeout += 3 * timeout / port->tx_fifo_depth; + WRITE_ONCE(port->poll_timeout_us, timeout); + } + if (!uart_console(uport)) writel(port->loopback, uport->membase + SE_UART_LOOPBACK_CFG); diff --git a/drivers/tty/serial/rp2.c b/drivers/tty/serial/rp2.c index 4132fcff7d4e..8bab2aedc499 100644 --- a/drivers/tty/serial/rp2.c +++ b/drivers/tty/serial/rp2.c @@ -577,8 +577,8 @@ static void rp2_reset_asic(struct rp2_card *card, unsigned int asic_id) u32 clk_cfg; writew(1, base + RP2_GLOBAL_CMD); - readw(base + RP2_GLOBAL_CMD); msleep(100); + readw(base + RP2_GLOBAL_CMD); writel(0, base + RP2_CLK_PRESCALER); /* TDM clock configuration */ diff --git a/drivers/tty/serial/samsung_tty.c b/drivers/tty/serial/samsung_tty.c index dc35eb77d2ef..0d184ee2f9ce 100644 --- a/drivers/tty/serial/samsung_tty.c +++ b/drivers/tty/serial/samsung_tty.c @@ -550,6 +550,7 @@ static void s3c24xx_serial_stop_rx(struct uart_port *port) case TYPE_APPLE_S5L: s3c24xx_clear_bit(port, APPLE_S5L_UCON_RXTHRESH_ENA, S3C2410_UCON); s3c24xx_clear_bit(port, APPLE_S5L_UCON_RXTO_ENA, S3C2410_UCON); + s3c24xx_clear_bit(port, APPLE_S5L_UCON_RXTO_LEGACY_ENA, S3C2410_UCON); break; default: disable_irq_nosync(ourport->rx_irq); @@ -707,9 +708,8 @@ static void enable_rx_pio(struct s3c24xx_uart_port *ourport) static void s3c24xx_serial_rx_drain_fifo(struct s3c24xx_uart_port *ourport); -static irqreturn_t s3c24xx_serial_rx_chars_dma(void *dev_id) +static irqreturn_t s3c24xx_serial_rx_chars_dma(struct s3c24xx_uart_port *ourport) { - struct s3c24xx_uart_port *ourport = dev_id; struct uart_port *port = &ourport->port; struct s3c24xx_uart_dma *dma = ourport->dma; struct tty_struct *tty = tty_port_tty_get(&ourport->port.state->port); @@ -843,9 +843,8 @@ static void s3c24xx_serial_rx_drain_fifo(struct s3c24xx_uart_port *ourport) tty_flip_buffer_push(&port->state->port); } -static irqreturn_t s3c24xx_serial_rx_chars_pio(void *dev_id) +static irqreturn_t s3c24xx_serial_rx_chars_pio(struct s3c24xx_uart_port *ourport) { - struct s3c24xx_uart_port *ourport = dev_id; struct uart_port *port = &ourport->port; uart_port_lock(port); @@ -855,13 +854,11 @@ static irqreturn_t s3c24xx_serial_rx_chars_pio(void *dev_id) return IRQ_HANDLED; } -static irqreturn_t s3c24xx_serial_rx_irq(int irq, void *dev_id) +static irqreturn_t s3c24xx_serial_rx_irq(struct s3c24xx_uart_port *ourport) { - struct s3c24xx_uart_port *ourport = dev_id; - if (ourport->dma && ourport->dma->rx_chan) - return s3c24xx_serial_rx_chars_dma(dev_id); - return s3c24xx_serial_rx_chars_pio(dev_id); + return s3c24xx_serial_rx_chars_dma(ourport); + return s3c24xx_serial_rx_chars_pio(ourport); } static void s3c24xx_serial_tx_chars(struct s3c24xx_uart_port *ourport) @@ -928,9 +925,8 @@ static void s3c24xx_serial_tx_chars(struct s3c24xx_uart_port *ourport) s3c24xx_serial_stop_tx(port); } -static irqreturn_t s3c24xx_serial_tx_irq(int irq, void *id) +static irqreturn_t s3c24xx_serial_tx_irq(struct s3c24xx_uart_port *ourport) { - struct s3c24xx_uart_port *ourport = id; struct uart_port *port = &ourport->port; uart_port_lock(port); @@ -944,17 +940,17 @@ static irqreturn_t s3c24xx_serial_tx_irq(int irq, void *id) /* interrupt handler for s3c64xx and later SoC's.*/ static irqreturn_t s3c64xx_serial_handle_irq(int irq, void *id) { - const struct s3c24xx_uart_port *ourport = id; + struct s3c24xx_uart_port *ourport = id; const struct uart_port *port = &ourport->port; u32 pend = rd_regl(port, S3C64XX_UINTP); irqreturn_t ret = IRQ_HANDLED; if (pend & S3C64XX_UINTM_RXD_MSK) { - ret = s3c24xx_serial_rx_irq(irq, id); + ret = s3c24xx_serial_rx_irq(ourport); wr_regl(port, S3C64XX_UINTP, S3C64XX_UINTM_RXD_MSK); } if (pend & S3C64XX_UINTM_TXD_MSK) { - ret = s3c24xx_serial_tx_irq(irq, id); + ret = s3c24xx_serial_tx_irq(ourport); wr_regl(port, S3C64XX_UINTP, S3C64XX_UINTM_TXD_MSK); } return ret; @@ -963,19 +959,21 @@ static irqreturn_t s3c64xx_serial_handle_irq(int irq, void *id) /* interrupt handler for Apple SoC's.*/ static irqreturn_t apple_serial_handle_irq(int irq, void *id) { - const struct s3c24xx_uart_port *ourport = id; + struct s3c24xx_uart_port *ourport = id; const struct uart_port *port = &ourport->port; u32 pend = rd_regl(port, S3C2410_UTRSTAT); irqreturn_t ret = IRQ_NONE; - if (pend & (APPLE_S5L_UTRSTAT_RXTHRESH | APPLE_S5L_UTRSTAT_RXTO)) { + if (pend & (APPLE_S5L_UTRSTAT_RXTHRESH | APPLE_S5L_UTRSTAT_RXTO | + APPLE_S5L_UTRSTAT_RXTO_LEGACY)) { wr_regl(port, S3C2410_UTRSTAT, - APPLE_S5L_UTRSTAT_RXTHRESH | APPLE_S5L_UTRSTAT_RXTO); - ret = s3c24xx_serial_rx_irq(irq, id); + APPLE_S5L_UTRSTAT_RXTHRESH | APPLE_S5L_UTRSTAT_RXTO | + APPLE_S5L_UTRSTAT_RXTO_LEGACY); + ret = s3c24xx_serial_rx_irq(ourport); } if (pend & APPLE_S5L_UTRSTAT_TXTHRESH) { wr_regl(port, S3C2410_UTRSTAT, APPLE_S5L_UTRSTAT_TXTHRESH); - ret = s3c24xx_serial_tx_irq(irq, id); + ret = s3c24xx_serial_tx_irq(ourport); } return ret; @@ -1195,7 +1193,8 @@ static void apple_s5l_serial_shutdown(struct uart_port *port) ucon = rd_regl(port, S3C2410_UCON); ucon &= ~(APPLE_S5L_UCON_TXTHRESH_ENA_MSK | APPLE_S5L_UCON_RXTHRESH_ENA_MSK | - APPLE_S5L_UCON_RXTO_ENA_MSK); + APPLE_S5L_UCON_RXTO_ENA_MSK | + APPLE_S5L_UCON_RXTO_LEGACY_ENA_MSK); wr_regl(port, S3C2410_UCON, ucon); wr_regl(port, S3C2410_UTRSTAT, APPLE_S5L_UTRSTAT_ALL_FLAGS); @@ -1292,6 +1291,7 @@ static int apple_s5l_serial_startup(struct uart_port *port) /* Enable Rx Interrupt */ s3c24xx_set_bit(port, APPLE_S5L_UCON_RXTHRESH_ENA, S3C2410_UCON); s3c24xx_set_bit(port, APPLE_S5L_UCON_RXTO_ENA, S3C2410_UCON); + s3c24xx_set_bit(port, APPLE_S5L_UCON_RXTO_LEGACY_ENA, S3C2410_UCON); return ret; } @@ -2148,13 +2148,15 @@ static int s3c24xx_serial_resume_noirq(struct device *dev) ucon &= ~(APPLE_S5L_UCON_TXTHRESH_ENA_MSK | APPLE_S5L_UCON_RXTHRESH_ENA_MSK | - APPLE_S5L_UCON_RXTO_ENA_MSK); + APPLE_S5L_UCON_RXTO_ENA_MSK | + APPLE_S5L_UCON_RXTO_LEGACY_ENA_MSK); if (ourport->tx_enabled) ucon |= APPLE_S5L_UCON_TXTHRESH_ENA_MSK; if (ourport->rx_enabled) ucon |= APPLE_S5L_UCON_RXTHRESH_ENA_MSK | - APPLE_S5L_UCON_RXTO_ENA_MSK; + APPLE_S5L_UCON_RXTO_ENA_MSK | + APPLE_S5L_UCON_RXTO_LEGACY_ENA_MSK; wr_regl(port, S3C2410_UCON, ucon); @@ -2541,7 +2543,7 @@ static const struct s3c24xx_serial_drv_data s5l_serial_drv_data = { .name = "Apple S5L UART", .type = TYPE_APPLE_S5L, .port_type = PORT_8250, - .iotype = UPIO_MEM, + .iotype = UPIO_MEM32, .fifosize = 16, .rx_fifomask = S3C2410_UFSTAT_RXMASK, .rx_fifoshift = S3C2410_UFSTAT_RXSHIFT, @@ -2827,6 +2829,9 @@ OF_EARLYCON_DECLARE(gs101, "google,gs101-uart", gs101_early_console_setup); static int __init apple_s5l_early_console_setup(struct earlycon_device *device, const char *opt) { + /* Apple A7-A11 requires MMIO32 register accesses. */ + device->port.iotype = UPIO_MEM32; + /* Close enough to S3C2410 for earlycon... */ device->port.private_data = &s3c2410_early_console_data; diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index b4c1798a1df2..ad88a33a504f 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -10,6 +10,7 @@ #undef DEFAULT_SYMBOL_NAMESPACE #define DEFAULT_SYMBOL_NAMESPACE SERIAL_NXP_SC16IS7XX +#include #include #include #include @@ -78,52 +79,52 @@ #define SC16IS7XX_XOFF2_REG (0x07) /* Xoff2 word */ /* IER register bits */ -#define SC16IS7XX_IER_RDI_BIT (1 << 0) /* Enable RX data interrupt */ -#define SC16IS7XX_IER_THRI_BIT (1 << 1) /* Enable TX holding register +#define SC16IS7XX_IER_RDI_BIT BIT(0) /* Enable RX data interrupt */ +#define SC16IS7XX_IER_THRI_BIT BIT(1) /* Enable TX holding register * interrupt */ -#define SC16IS7XX_IER_RLSI_BIT (1 << 2) /* Enable RX line status +#define SC16IS7XX_IER_RLSI_BIT BIT(2) /* Enable RX line status * interrupt */ -#define SC16IS7XX_IER_MSI_BIT (1 << 3) /* Enable Modem status +#define SC16IS7XX_IER_MSI_BIT BIT(3) /* Enable Modem status * interrupt */ /* IER register bits - write only if (EFR[4] == 1) */ -#define SC16IS7XX_IER_SLEEP_BIT (1 << 4) /* Enable Sleep mode */ -#define SC16IS7XX_IER_XOFFI_BIT (1 << 5) /* Enable Xoff interrupt */ -#define SC16IS7XX_IER_RTSI_BIT (1 << 6) /* Enable nRTS interrupt */ -#define SC16IS7XX_IER_CTSI_BIT (1 << 7) /* Enable nCTS interrupt */ +#define SC16IS7XX_IER_SLEEP_BIT BIT(4) /* Enable Sleep mode */ +#define SC16IS7XX_IER_XOFFI_BIT BIT(5) /* Enable Xoff interrupt */ +#define SC16IS7XX_IER_RTSI_BIT BIT(6) /* Enable nRTS interrupt */ +#define SC16IS7XX_IER_CTSI_BIT BIT(7) /* Enable nCTS interrupt */ /* FCR register bits */ -#define SC16IS7XX_FCR_FIFO_BIT (1 << 0) /* Enable FIFO */ -#define SC16IS7XX_FCR_RXRESET_BIT (1 << 1) /* Reset RX FIFO */ -#define SC16IS7XX_FCR_TXRESET_BIT (1 << 2) /* Reset TX FIFO */ -#define SC16IS7XX_FCR_RXLVLL_BIT (1 << 6) /* RX Trigger level LSB */ -#define SC16IS7XX_FCR_RXLVLH_BIT (1 << 7) /* RX Trigger level MSB */ +#define SC16IS7XX_FCR_FIFO_BIT BIT(0) /* Enable FIFO */ +#define SC16IS7XX_FCR_RXRESET_BIT BIT(1) /* Reset RX FIFO */ +#define SC16IS7XX_FCR_TXRESET_BIT BIT(2) /* Reset TX FIFO */ +#define SC16IS7XX_FCR_RXLVLL_BIT BIT(6) /* RX Trigger level LSB */ +#define SC16IS7XX_FCR_RXLVLH_BIT BIT(7) /* RX Trigger level MSB */ /* FCR register bits - write only if (EFR[4] == 1) */ -#define SC16IS7XX_FCR_TXLVLL_BIT (1 << 4) /* TX Trigger level LSB */ -#define SC16IS7XX_FCR_TXLVLH_BIT (1 << 5) /* TX Trigger level MSB */ +#define SC16IS7XX_FCR_TXLVLL_BIT BIT(4) /* TX Trigger level LSB */ +#define SC16IS7XX_FCR_TXLVLH_BIT BIT(5) /* TX Trigger level MSB */ /* IIR register bits */ -#define SC16IS7XX_IIR_NO_INT_BIT (1 << 0) /* No interrupts pending */ -#define SC16IS7XX_IIR_ID_MASK 0x3e /* Mask for the interrupt ID */ -#define SC16IS7XX_IIR_THRI_SRC 0x02 /* TX holding register empty */ -#define SC16IS7XX_IIR_RDI_SRC 0x04 /* RX data interrupt */ -#define SC16IS7XX_IIR_RLSE_SRC 0x06 /* RX line status error */ -#define SC16IS7XX_IIR_RTOI_SRC 0x0c /* RX time-out interrupt */ -#define SC16IS7XX_IIR_MSI_SRC 0x00 /* Modem status interrupt - * - only on 75x/76x - */ -#define SC16IS7XX_IIR_INPIN_SRC 0x30 /* Input pin change of state - * - only on 75x/76x - */ -#define SC16IS7XX_IIR_XOFFI_SRC 0x10 /* Received Xoff */ -#define SC16IS7XX_IIR_CTSRTS_SRC 0x20 /* nCTS,nRTS change of state - * from active (LOW) - * to inactive (HIGH) - */ +#define SC16IS7XX_IIR_NO_INT_BIT 0x01 /* No interrupts pending */ +#define SC16IS7XX_IIR_ID_MASK GENMASK(5, 1) /* Mask for the interrupt ID */ +#define SC16IS7XX_IIR_THRI_SRC 0x02 /* TX holding register empty */ +#define SC16IS7XX_IIR_RDI_SRC 0x04 /* RX data interrupt */ +#define SC16IS7XX_IIR_RLSE_SRC 0x06 /* RX line status error */ +#define SC16IS7XX_IIR_RTOI_SRC 0x0c /* RX time-out interrupt */ +#define SC16IS7XX_IIR_MSI_SRC 0x00 /* Modem status interrupt + * - only on 75x/76x + */ +#define SC16IS7XX_IIR_INPIN_SRC 0x30 /* Input pin change of state + * - only on 75x/76x + */ +#define SC16IS7XX_IIR_XOFFI_SRC 0x10 /* Received Xoff */ +#define SC16IS7XX_IIR_CTSRTS_SRC 0x20 /* nCTS,nRTS change of state + * from active (LOW) + * to inactive (HIGH) + */ /* LCR register bits */ -#define SC16IS7XX_LCR_LENGTH0_BIT (1 << 0) /* Word length bit 0 */ -#define SC16IS7XX_LCR_LENGTH1_BIT (1 << 1) /* Word length bit 1 +#define SC16IS7XX_LCR_LENGTH0_BIT BIT(0) /* Word length bit 0 */ +#define SC16IS7XX_LCR_LENGTH1_BIT BIT(1) /* Word length bit 1 * * Word length bits table: * 00 -> 5 bit words @@ -131,7 +132,7 @@ * 10 -> 7 bit words * 11 -> 8 bit words */ -#define SC16IS7XX_LCR_STOPLEN_BIT (1 << 2) /* STOP length bit +#define SC16IS7XX_LCR_STOPLEN_BIT BIT(2) /* STOP length bit * * STOP length bit table: * 0 -> 1 stop bit @@ -139,11 +140,11 @@ * word length is 5, * 2 stop bits otherwise */ -#define SC16IS7XX_LCR_PARITY_BIT (1 << 3) /* Parity bit enable */ -#define SC16IS7XX_LCR_EVENPARITY_BIT (1 << 4) /* Even parity bit enable */ -#define SC16IS7XX_LCR_FORCEPARITY_BIT (1 << 5) /* 9-bit multidrop parity */ -#define SC16IS7XX_LCR_TXBREAK_BIT (1 << 6) /* TX break enable */ -#define SC16IS7XX_LCR_DLAB_BIT (1 << 7) /* Divisor Latch enable */ +#define SC16IS7XX_LCR_PARITY_BIT BIT(3) /* Parity bit enable */ +#define SC16IS7XX_LCR_EVENPARITY_BIT BIT(4) /* Even parity bit enable */ +#define SC16IS7XX_LCR_FORCEPARITY_BIT BIT(5) /* 9-bit multidrop parity */ +#define SC16IS7XX_LCR_TXBREAK_BIT BIT(6) /* TX break enable */ +#define SC16IS7XX_LCR_DLAB_BIT BIT(7) /* Divisor Latch enable */ #define SC16IS7XX_LCR_WORD_LEN_5 (0x00) #define SC16IS7XX_LCR_WORD_LEN_6 (0x01) #define SC16IS7XX_LCR_WORD_LEN_7 (0x02) @@ -154,61 +155,65 @@ * reg set */ /* MCR register bits */ -#define SC16IS7XX_MCR_DTR_BIT (1 << 0) /* DTR complement +#define SC16IS7XX_MCR_DTR_BIT BIT(0) /* DTR complement * - only on 75x/76x */ -#define SC16IS7XX_MCR_RTS_BIT (1 << 1) /* RTS complement */ -#define SC16IS7XX_MCR_TCRTLR_BIT (1 << 2) /* TCR/TLR register enable */ -#define SC16IS7XX_MCR_LOOP_BIT (1 << 4) /* Enable loopback test mode */ -#define SC16IS7XX_MCR_XONANY_BIT (1 << 5) /* Enable Xon Any +#define SC16IS7XX_MCR_RTS_BIT BIT(1) /* RTS complement */ +#define SC16IS7XX_MCR_TCRTLR_BIT BIT(2) /* TCR/TLR register enable */ +#define SC16IS7XX_MCR_LOOP_BIT BIT(4) /* Enable loopback test mode */ +#define SC16IS7XX_MCR_XONANY_BIT BIT(5) /* Enable Xon Any * - write enabled * if (EFR[4] == 1) */ -#define SC16IS7XX_MCR_IRDA_BIT (1 << 6) /* Enable IrDA mode +#define SC16IS7XX_MCR_IRDA_BIT BIT(6) /* Enable IrDA mode * - write enabled * if (EFR[4] == 1) */ -#define SC16IS7XX_MCR_CLKSEL_BIT (1 << 7) /* Divide clock by 4 +#define SC16IS7XX_MCR_CLKSEL_BIT BIT(7) /* Divide clock by 4 * - write enabled * if (EFR[4] == 1) */ /* LSR register bits */ -#define SC16IS7XX_LSR_DR_BIT (1 << 0) /* Receiver data ready */ -#define SC16IS7XX_LSR_OE_BIT (1 << 1) /* Overrun Error */ -#define SC16IS7XX_LSR_PE_BIT (1 << 2) /* Parity Error */ -#define SC16IS7XX_LSR_FE_BIT (1 << 3) /* Frame Error */ -#define SC16IS7XX_LSR_BI_BIT (1 << 4) /* Break Interrupt */ -#define SC16IS7XX_LSR_BRK_ERROR_MASK 0x1E /* BI, FE, PE, OE bits */ -#define SC16IS7XX_LSR_THRE_BIT (1 << 5) /* TX holding register empty */ -#define SC16IS7XX_LSR_TEMT_BIT (1 << 6) /* Transmitter empty */ -#define SC16IS7XX_LSR_FIFOE_BIT (1 << 7) /* Fifo Error */ +#define SC16IS7XX_LSR_DR_BIT BIT(0) /* Receiver data ready */ +#define SC16IS7XX_LSR_OE_BIT BIT(1) /* Overrun Error */ +#define SC16IS7XX_LSR_PE_BIT BIT(2) /* Parity Error */ +#define SC16IS7XX_LSR_FE_BIT BIT(3) /* Frame Error */ +#define SC16IS7XX_LSR_BI_BIT BIT(4) /* Break Interrupt */ +#define SC16IS7XX_LSR_BRK_ERROR_MASK \ + (SC16IS7XX_LSR_OE_BIT | \ + SC16IS7XX_LSR_PE_BIT | \ + SC16IS7XX_LSR_FE_BIT | \ + SC16IS7XX_LSR_BI_BIT) + +#define SC16IS7XX_LSR_THRE_BIT BIT(5) /* TX holding register empty */ +#define SC16IS7XX_LSR_TEMT_BIT BIT(6) /* Transmitter empty */ +#define SC16IS7XX_LSR_FIFOE_BIT BIT(7) /* Fifo Error */ /* MSR register bits */ -#define SC16IS7XX_MSR_DCTS_BIT (1 << 0) /* Delta CTS Clear To Send */ -#define SC16IS7XX_MSR_DDSR_BIT (1 << 1) /* Delta DSR Data Set Ready +#define SC16IS7XX_MSR_DCTS_BIT BIT(0) /* Delta CTS Clear To Send */ +#define SC16IS7XX_MSR_DDSR_BIT BIT(1) /* Delta DSR Data Set Ready * or (IO4) * - only on 75x/76x */ -#define SC16IS7XX_MSR_DRI_BIT (1 << 2) /* Delta RI Ring Indicator +#define SC16IS7XX_MSR_DRI_BIT BIT(2) /* Delta RI Ring Indicator * or (IO7) * - only on 75x/76x */ -#define SC16IS7XX_MSR_DCD_BIT (1 << 3) /* Delta CD Carrier Detect +#define SC16IS7XX_MSR_DCD_BIT BIT(3) /* Delta CD Carrier Detect * or (IO6) * - only on 75x/76x */ -#define SC16IS7XX_MSR_CTS_BIT (1 << 4) /* CTS */ -#define SC16IS7XX_MSR_DSR_BIT (1 << 5) /* DSR (IO4) +#define SC16IS7XX_MSR_CTS_BIT BIT(4) /* CTS */ +#define SC16IS7XX_MSR_DSR_BIT BIT(5) /* DSR (IO4) * - only on 75x/76x */ -#define SC16IS7XX_MSR_RI_BIT (1 << 6) /* RI (IO7) +#define SC16IS7XX_MSR_RI_BIT BIT(6) /* RI (IO7) * - only on 75x/76x */ -#define SC16IS7XX_MSR_CD_BIT (1 << 7) /* CD (IO6) +#define SC16IS7XX_MSR_CD_BIT BIT(7) /* CD (IO6) * - only on 75x/76x */ -#define SC16IS7XX_MSR_DELTA_MASK 0x0F /* Any of the delta bits! */ /* * TCR register bits @@ -241,19 +246,19 @@ #define SC16IS7XX_TLR_RX_TRIGGER(words) ((((words) / 4) & 0x0f) << 4) /* IOControl register bits (Only 75x/76x) */ -#define SC16IS7XX_IOCONTROL_LATCH_BIT (1 << 0) /* Enable input latching */ -#define SC16IS7XX_IOCONTROL_MODEM_A_BIT (1 << 1) /* Enable GPIO[7:4] as modem A pins */ -#define SC16IS7XX_IOCONTROL_MODEM_B_BIT (1 << 2) /* Enable GPIO[3:0] as modem B pins */ -#define SC16IS7XX_IOCONTROL_SRESET_BIT (1 << 3) /* Software Reset */ +#define SC16IS7XX_IOCONTROL_LATCH_BIT BIT(0) /* Enable input latching */ +#define SC16IS7XX_IOCONTROL_MODEM_A_BIT BIT(1) /* Enable GPIO[7:4] as modem A pins */ +#define SC16IS7XX_IOCONTROL_MODEM_B_BIT BIT(2) /* Enable GPIO[3:0] as modem B pins */ +#define SC16IS7XX_IOCONTROL_SRESET_BIT BIT(3) /* Software Reset */ /* EFCR register bits */ -#define SC16IS7XX_EFCR_9BIT_MODE_BIT (1 << 0) /* Enable 9-bit or Multidrop +#define SC16IS7XX_EFCR_9BIT_MODE_BIT BIT(0) /* Enable 9-bit or Multidrop * mode (RS485) */ -#define SC16IS7XX_EFCR_RXDISABLE_BIT (1 << 1) /* Disable receiver */ -#define SC16IS7XX_EFCR_TXDISABLE_BIT (1 << 2) /* Disable transmitter */ -#define SC16IS7XX_EFCR_AUTO_RS485_BIT (1 << 4) /* Auto RS485 RTS direction */ -#define SC16IS7XX_EFCR_RTS_INVERT_BIT (1 << 5) /* RTS output inversion */ -#define SC16IS7XX_EFCR_IRDA_MODE_BIT (1 << 7) /* IrDA mode +#define SC16IS7XX_EFCR_RXDISABLE_BIT BIT(1) /* Disable receiver */ +#define SC16IS7XX_EFCR_TXDISABLE_BIT BIT(2) /* Disable transmitter */ +#define SC16IS7XX_EFCR_AUTO_RS485_BIT BIT(4) /* Auto RS485 RTS direction */ +#define SC16IS7XX_EFCR_RTS_INVERT_BIT BIT(5) /* RTS output inversion */ +#define SC16IS7XX_EFCR_IRDA_MODE_BIT BIT(7) /* IrDA mode * 0 = rate upto 115.2 kbit/s * - Only 75x/76x * 1 = rate upto 1.152 Mbit/s @@ -261,16 +266,16 @@ */ /* EFR register bits */ -#define SC16IS7XX_EFR_AUTORTS_BIT (1 << 6) /* Auto RTS flow ctrl enable */ -#define SC16IS7XX_EFR_AUTOCTS_BIT (1 << 7) /* Auto CTS flow ctrl enable */ -#define SC16IS7XX_EFR_XOFF2_DETECT_BIT (1 << 5) /* Enable Xoff2 detection */ -#define SC16IS7XX_EFR_ENABLE_BIT (1 << 4) /* Enable enhanced functions +#define SC16IS7XX_EFR_AUTORTS_BIT BIT(6) /* Auto RTS flow ctrl enable */ +#define SC16IS7XX_EFR_AUTOCTS_BIT BIT(7) /* Auto CTS flow ctrl enable */ +#define SC16IS7XX_EFR_XOFF2_DETECT_BIT BIT(5) /* Enable Xoff2 detection */ +#define SC16IS7XX_EFR_ENABLE_BIT BIT(4) /* Enable enhanced functions * and writing to IER[7:4], * FCR[5:4], MCR[7:5] */ -#define SC16IS7XX_EFR_SWFLOW3_BIT (1 << 3) /* SWFLOW bit 3 */ -#define SC16IS7XX_EFR_SWFLOW2_BIT (1 << 2) /* SWFLOW bit 2 - * +#define SC16IS7XX_EFR_SWFLOW3_BIT BIT(3) +#define SC16IS7XX_EFR_SWFLOW2_BIT BIT(2) + /* * SWFLOW bits 3 & 2 table: * 00 -> no transmitter flow * control @@ -282,10 +287,10 @@ * XON1, XON2, XOFF1 and * XOFF2 */ -#define SC16IS7XX_EFR_SWFLOW1_BIT (1 << 1) /* SWFLOW bit 2 */ -#define SC16IS7XX_EFR_SWFLOW0_BIT (1 << 0) /* SWFLOW bit 3 - * - * SWFLOW bits 3 & 2 table: +#define SC16IS7XX_EFR_SWFLOW1_BIT BIT(1) +#define SC16IS7XX_EFR_SWFLOW0_BIT BIT(0) + /* + * SWFLOW bits 1 & 0 table: * 00 -> no received flow * control * 01 -> receiver compares @@ -309,9 +314,9 @@ #define SC16IS7XX_FIFO_SIZE (64) #define SC16IS7XX_GPIOS_PER_BANK 4 -#define SC16IS7XX_RECONF_MD (1 << 0) -#define SC16IS7XX_RECONF_IER (1 << 1) -#define SC16IS7XX_RECONF_RS485 (1 << 2) +#define SC16IS7XX_RECONF_MD BIT(0) +#define SC16IS7XX_RECONF_IER BIT(1) +#define SC16IS7XX_RECONF_RS485 BIT(2) struct sc16is7xx_one_config { unsigned int flags; diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 1e3e28e364df..d94d73e45fb6 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -407,14 +407,16 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) /* * Turn off DTR and RTS early. */ - if (uport && uart_console(uport) && tty) { - uport->cons->cflag = tty->termios.c_cflag; - uport->cons->ispeed = tty->termios.c_ispeed; - uport->cons->ospeed = tty->termios.c_ospeed; - } + if (uport) { + if (uart_console(uport) && tty) { + uport->cons->cflag = tty->termios.c_cflag; + uport->cons->ispeed = tty->termios.c_ispeed; + uport->cons->ospeed = tty->termios.c_ospeed; + } - if (!tty || C_HUPCL(tty)) - uart_port_dtr_rts(uport, false); + if (!tty || C_HUPCL(tty)) + uart_port_dtr_rts(uport, false); + } uart_port_shutdown(port); } @@ -1102,21 +1104,19 @@ static int uart_tiocmget(struct tty_struct *tty) struct uart_state *state = tty->driver_data; struct tty_port *port = &state->port; struct uart_port *uport; - int result = -EIO; + int result; + + guard(mutex)(&port->mutex); - mutex_lock(&port->mutex); uport = uart_port_check(state); - if (!uport) - goto out; + if (!uport || tty_io_error(tty)) + return -EIO; + + uart_port_lock_irq(uport); + result = uport->mctrl; + result |= uport->ops->get_mctrl(uport); + uart_port_unlock_irq(uport); - if (!tty_io_error(tty)) { - uart_port_lock_irq(uport); - result = uport->mctrl; - result |= uport->ops->get_mctrl(uport); - uart_port_unlock_irq(uport); - } -out: - mutex_unlock(&port->mutex); return result; } @@ -1126,20 +1126,16 @@ uart_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) struct uart_state *state = tty->driver_data; struct tty_port *port = &state->port; struct uart_port *uport; - int ret = -EIO; - mutex_lock(&port->mutex); + guard(mutex)(&port->mutex); + uport = uart_port_check(state); - if (!uport) - goto out; + if (!uport || tty_io_error(tty)) + return -EIO; - if (!tty_io_error(tty)) { - uart_update_mctrl(uport, set, clear); - ret = 0; - } -out: - mutex_unlock(&port->mutex); - return ret; + uart_update_mctrl(uport, set, clear); + + return 0; } static int uart_break_ctl(struct tty_struct *tty, int break_state) @@ -1147,19 +1143,17 @@ static int uart_break_ctl(struct tty_struct *tty, int break_state) struct uart_state *state = tty->driver_data; struct tty_port *port = &state->port; struct uart_port *uport; - int ret = -EIO; - mutex_lock(&port->mutex); + guard(mutex)(&port->mutex); + uport = uart_port_check(state); if (!uport) - goto out; + return -EIO; if (uport->type != PORT_UNKNOWN && uport->ops->break_ctl) uport->ops->break_ctl(uport, break_state); - ret = 0; -out: - mutex_unlock(&port->mutex); - return ret; + + return 0; } static int uart_do_autoconfig(struct tty_struct *tty, struct uart_state *state) @@ -1176,17 +1170,14 @@ static int uart_do_autoconfig(struct tty_struct *tty, struct uart_state *state) * changing, and hence any extra opens of the port while * we're auto-configuring. */ - if (mutex_lock_interruptible(&port->mutex)) - return -ERESTARTSYS; + scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &port->mutex) { + uport = uart_port_check(state); + if (!uport) + return -EIO; - uport = uart_port_check(state); - if (!uport) { - ret = -EIO; - goto out; - } + if (tty_port_users(port) != 1) + return -EBUSY; - ret = -EBUSY; - if (tty_port_users(port) == 1) { uart_shutdown(tty, state); /* @@ -1207,14 +1198,15 @@ static int uart_do_autoconfig(struct tty_struct *tty, struct uart_state *state) uport->ops->config_port(uport, flags); ret = uart_startup(tty, state, true); - if (ret == 0) - tty_port_set_initialized(port, true); + if (ret < 0) + return ret; if (ret > 0) - ret = 0; + return 0; + + tty_port_set_initialized(port, true); } -out: - mutex_unlock(&port->mutex); - return ret; + + return 0; } static void uart_enable_ms(struct uart_port *uport) @@ -1709,10 +1701,11 @@ static void uart_set_termios(struct tty_struct *tty, unsigned int iflag_mask = IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK; bool sw_changed = false; - mutex_lock(&state->port.mutex); + guard(mutex)(&state->port.mutex); + uport = uart_port_check(state); if (!uport) - goto out; + return; /* * Drivers doing software flow control also need to know @@ -1735,9 +1728,8 @@ static void uart_set_termios(struct tty_struct *tty, tty->termios.c_ospeed == old_termios->c_ospeed && tty->termios.c_ispeed == old_termios->c_ispeed && ((tty->termios.c_iflag ^ old_termios->c_iflag) & iflag_mask) == 0 && - !sw_changed) { - goto out; - } + !sw_changed) + return; uart_change_line_settings(tty, state, old_termios); /* reload cflag from termios; port driver may have overridden flags */ @@ -1754,8 +1746,6 @@ static void uart_set_termios(struct tty_struct *tty, mask |= TIOCM_RTS; uart_set_mctrl(uport, mask); } -out: - mutex_unlock(&state->port.mutex); } /* @@ -2049,10 +2039,11 @@ static void uart_line_info(struct seq_file *m, struct uart_driver *drv, int i) unsigned int status; int mmio; - mutex_lock(&port->mutex); + guard(mutex)(&port->mutex); + uport = uart_port_check(state); if (!uport) - goto out; + return; mmio = uport->iotype >= UPIO_MEM; seq_printf(m, "%d: uart:%s %s%08llX irq:%d", @@ -2064,7 +2055,7 @@ static void uart_line_info(struct seq_file *m, struct uart_driver *drv, int i) if (uport->type == PORT_UNKNOWN) { seq_putc(m, '\n'); - goto out; + return; } if (capable(CAP_SYS_ADMIN)) { @@ -2115,8 +2106,6 @@ static void uart_line_info(struct seq_file *m, struct uart_driver *drv, int i) seq_putc(m, '\n'); #undef STATBIT #undef INFOBIT -out: - mutex_unlock(&port->mutex); } static int uart_proc_show(struct seq_file *m, void *v) @@ -2393,13 +2382,12 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) struct device *tty_dev; struct uart_match match = {uport, drv}; - mutex_lock(&port->mutex); + guard(mutex)(&port->mutex); tty_dev = device_find_child(&uport->port_dev->dev, &match, serial_match_port); if (tty_dev && device_may_wakeup(tty_dev)) { enable_irq_wake(uport->irq); put_device(tty_dev); - mutex_unlock(&port->mutex); return 0; } put_device(tty_dev); @@ -2417,7 +2405,7 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) uart_port_unlock_irq(uport); } device_set_awake_path(uport->dev); - goto unlock; + return 0; } uport->suspended = 1; @@ -2460,8 +2448,6 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) console_stop(uport->cons); uart_change_pm(state, UART_PM_STATE_OFF); -unlock: - mutex_unlock(&port->mutex); return 0; } @@ -2475,14 +2461,13 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) struct uart_match match = {uport, drv}; struct ktermios termios; - mutex_lock(&port->mutex); + guard(mutex)(&port->mutex); tty_dev = device_find_child(&uport->port_dev->dev, &match, serial_match_port); if (!uport->suspended && device_may_wakeup(tty_dev)) { if (irqd_is_wakeup_set(irq_get_irq_data((uport->irq)))) disable_irq_wake(uport->irq); put_device(tty_dev); - mutex_unlock(&port->mutex); return 0; } put_device(tty_dev); @@ -2555,8 +2540,6 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) tty_port_set_suspended(port, false); } - mutex_unlock(&port->mutex); - return 0; } EXPORT_SYMBOL(uart_resume_port); @@ -2696,14 +2679,13 @@ static int uart_poll_init(struct tty_driver *driver, int line, char *options) int ret = 0; tport = &state->port; - mutex_lock(&tport->mutex); + + guard(mutex)(&tport->mutex); port = uart_port_check(state); if (!port || port->type == PORT_UNKNOWN || - !(port->ops->poll_get_char && port->ops->poll_put_char)) { - ret = -1; - goto out; - } + !(port->ops->poll_get_char && port->ops->poll_put_char)) + return -1; pm_state = state->pm_state; uart_change_pm(state, UART_PM_STATE_ON); @@ -2723,10 +2705,10 @@ static int uart_poll_init(struct tty_driver *driver, int line, char *options) ret = uart_set_options(port, NULL, baud, parity, bits, flow); console_list_unlock(); } -out: + if (ret) uart_change_pm(state, pm_state); - mutex_unlock(&tport->mutex); + return ret; } diff --git a/drivers/tty/serial/st-asc.c b/drivers/tty/serial/st-asc.c index f91753a40a69..8aea59f8ca13 100644 --- a/drivers/tty/serial/st-asc.c +++ b/drivers/tty/serial/st-asc.c @@ -808,7 +808,6 @@ static void asc_serial_remove(struct platform_device *pdev) uart_remove_one_port(&asc_uart_driver, port); } -#ifdef CONFIG_PM_SLEEP static int asc_serial_suspend(struct device *dev) { struct uart_port *port = dev_get_drvdata(dev); @@ -823,8 +822,6 @@ static int asc_serial_resume(struct device *dev) return uart_resume_port(&asc_uart_driver, port); } -#endif /* CONFIG_PM_SLEEP */ - /*----------------------------------------------------------------------*/ #ifdef CONFIG_SERIAL_ST_ASC_CONSOLE @@ -932,16 +929,15 @@ static struct uart_driver asc_uart_driver = { .cons = ASC_SERIAL_CONSOLE, }; -static const struct dev_pm_ops asc_serial_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(asc_serial_suspend, asc_serial_resume) -}; +static DEFINE_SIMPLE_DEV_PM_OPS(asc_serial_pm_ops, asc_serial_suspend, + asc_serial_resume); static struct platform_driver asc_serial_driver = { .probe = asc_serial_probe, .remove_new = asc_serial_remove, .driver = { .name = DRIVER_NAME, - .pm = &asc_serial_pm_ops, + .pm = pm_sleep_ptr(&asc_serial_pm_ops), .of_match_table = of_match_ptr(asc_match), }, }; diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 2acfcea403ce..777392914819 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -219,7 +219,7 @@ struct cdns_platform_data { u32 quirks; }; -struct serial_rs485 cdns_rs485_supported = { +static struct serial_rs485 cdns_rs485_supported = { .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND, .delay_rts_before_send = 1, diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index abc2708d4ac5..6bd28a042dff 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -350,22 +350,19 @@ int tty_dev_name_to_number(const char *name, dev_t *number) return ret; prefix_length = str - name; - mutex_lock(&tty_mutex); + + guard(mutex)(&tty_mutex); list_for_each_entry(p, &tty_drivers, tty_drivers) if (prefix_length == strlen(p->name) && strncmp(name, p->name, prefix_length) == 0) { if (index < p->num) { *number = MKDEV(p->major, p->minor_start + index); - goto out; + return 0; } } - /* if here then driver wasn't found */ - ret = -ENODEV; -out: - mutex_unlock(&tty_mutex); - return ret; + return -ENODEV; } EXPORT_SYMBOL_GPL(tty_dev_name_to_number); diff --git a/drivers/usb/cdns3/cdns3-pci-wrap.c b/drivers/usb/cdns3/cdns3-pci-wrap.c index 1f6320d98a76..591d149de8f3 100644 --- a/drivers/usb/cdns3/cdns3-pci-wrap.c +++ b/drivers/usb/cdns3/cdns3-pci-wrap.c @@ -37,8 +37,7 @@ struct cdns3_wrap { #define PCI_DRIVER_NAME "cdns3-pci-usbss" #define PLAT_DRIVER_NAME "cdns-usb3" -#define CDNS_VENDOR_ID 0x17cd -#define CDNS_DEVICE_ID 0x0100 +#define PCI_DEVICE_ID_CDNS_USB3 0x0100 static struct pci_dev *cdns3_get_second_fun(struct pci_dev *pdev) { @@ -190,7 +189,7 @@ static void cdns3_pci_remove(struct pci_dev *pdev) } static const struct pci_device_id cdns3_pci_ids[] = { - { PCI_DEVICE(CDNS_VENDOR_ID, CDNS_DEVICE_ID), }, + { PCI_VDEVICE(CDNS, PCI_DEVICE_ID_CDNS_USB3) }, { 0, } }; diff --git a/drivers/usb/cdns3/cdnsp-pci.c b/drivers/usb/cdns3/cdnsp-pci.c index 225540fc81ba..2d05368a6745 100644 --- a/drivers/usb/cdns3/cdnsp-pci.c +++ b/drivers/usb/cdns3/cdnsp-pci.c @@ -28,10 +28,11 @@ #define PCI_DRIVER_NAME "cdns-pci-usbssp" #define PLAT_DRIVER_NAME "cdns-usbssp" -#define CDNS_VENDOR_ID 0x17cd -#define CDNS_DEVICE_ID 0x0200 -#define CDNS_DRD_ID 0x0100 -#define CDNS_DRD_IF (PCI_CLASS_SERIAL_USB << 8 | 0x80) +#define PCI_DEVICE_ID_CDNS_USB3 0x0100 +#define PCI_DEVICE_ID_CDNS_UDC 0x0200 + +#define PCI_CLASS_SERIAL_USB_CDNS_USB3 (PCI_CLASS_SERIAL_USB << 8 | 0x80) +#define PCI_CLASS_SERIAL_USB_CDNS_UDC PCI_CLASS_SERIAL_USB_DEVICE static struct pci_dev *cdnsp_get_second_fun(struct pci_dev *pdev) { @@ -40,10 +41,10 @@ static struct pci_dev *cdnsp_get_second_fun(struct pci_dev *pdev) * Platform has two function. The fist keeps resources for * Host/Device while the secon keeps resources for DRD/OTG. */ - if (pdev->device == CDNS_DEVICE_ID) - return pci_get_device(pdev->vendor, CDNS_DRD_ID, NULL); - else if (pdev->device == CDNS_DRD_ID) - return pci_get_device(pdev->vendor, CDNS_DEVICE_ID, NULL); + if (pdev->device == PCI_DEVICE_ID_CDNS_UDC) + return pci_get_device(pdev->vendor, PCI_DEVICE_ID_CDNS_USB3, NULL); + if (pdev->device == PCI_DEVICE_ID_CDNS_USB3) + return pci_get_device(pdev->vendor, PCI_DEVICE_ID_CDNS_UDC, NULL); return NULL; } @@ -220,12 +221,12 @@ static const struct dev_pm_ops cdnsp_pci_pm_ops = { }; static const struct pci_device_id cdnsp_pci_ids[] = { - { PCI_VENDOR_ID_CDNS, CDNS_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, - PCI_CLASS_SERIAL_USB_DEVICE, PCI_ANY_ID }, - { PCI_VENDOR_ID_CDNS, CDNS_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, - CDNS_DRD_IF, PCI_ANY_ID }, - { PCI_VENDOR_ID_CDNS, CDNS_DRD_ID, PCI_ANY_ID, PCI_ANY_ID, - CDNS_DRD_IF, PCI_ANY_ID }, + { PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_UDC), + .class = PCI_CLASS_SERIAL_USB_CDNS_UDC }, + { PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_UDC), + .class = PCI_CLASS_SERIAL_USB_CDNS_USB3 }, + { PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_USB3), + .class = PCI_CLASS_SERIAL_USB_CDNS_USB3 }, { 0, } }; diff --git a/drivers/usb/cdns3/cdnsp-ring.c b/drivers/usb/cdns3/cdnsp-ring.c index dbd83d321bca..46852529499d 100644 --- a/drivers/usb/cdns3/cdnsp-ring.c +++ b/drivers/usb/cdns3/cdnsp-ring.c @@ -718,7 +718,8 @@ int cdnsp_remove_request(struct cdnsp_device *pdev, seg = cdnsp_trb_in_td(pdev, cur_td->start_seg, cur_td->first_trb, cur_td->last_trb, hw_deq); - if (seg && (pep->ep_state & EP_ENABLED)) + if (seg && (pep->ep_state & EP_ENABLED) && + !(pep->ep_state & EP_DIS_IN_RROGRESS)) cdnsp_find_new_dequeue_state(pdev, pep, preq->request.stream_id, cur_td, &deq_state); else @@ -736,7 +737,8 @@ int cdnsp_remove_request(struct cdnsp_device *pdev, * During disconnecting all endpoint will be disabled so we don't * have to worry about updating dequeue pointer. */ - if (pdev->cdnsp_state & CDNSP_STATE_DISCONNECT_PENDING) { + if (pdev->cdnsp_state & CDNSP_STATE_DISCONNECT_PENDING || + pep->ep_state & EP_DIS_IN_RROGRESS) { status = -ESHUTDOWN; ret = cdnsp_cmd_set_deq(pdev, pep, &deq_state); } diff --git a/drivers/usb/cdns3/host.c b/drivers/usb/cdns3/host.c index ceca4d839dfd..7ba760ee62e3 100644 --- a/drivers/usb/cdns3/host.c +++ b/drivers/usb/cdns3/host.c @@ -62,7 +62,9 @@ static const struct xhci_plat_priv xhci_plat_cdns3_xhci = { .resume_quirk = xhci_cdns3_resume_quirk, }; -static const struct xhci_plat_priv xhci_plat_cdnsp_xhci; +static const struct xhci_plat_priv xhci_plat_cdnsp_xhci = { + .quirks = XHCI_CDNS_SCTX_QUIRK, +}; static int __cdns_host_init(struct cdns *cdns) { diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index bdc04ce919f7..c64ab0e07ea0 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -128,7 +128,7 @@ static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev) * In case the fsl,usbmisc property is not present this device doesn't * need usbmisc. Return NULL (which is no error here) */ - if (!of_get_property(np, "fsl,usbmisc", NULL)) + if (!of_property_present(np, "fsl,usbmisc")) return NULL; data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); diff --git a/drivers/usb/chipidea/ci_hdrc_npcm.c b/drivers/usb/chipidea/ci_hdrc_npcm.c index b14127873c55..3e5e05dbda89 100644 --- a/drivers/usb/chipidea/ci_hdrc_npcm.c +++ b/drivers/usb/chipidea/ci_hdrc_npcm.c @@ -18,7 +18,7 @@ struct npcm_udc_data { struct ci_hdrc_platform_data pdata; }; -static int npcm_udc_notify_event(struct ci_hdrc *ci, unsigned event) +static int npcm_udc_notify_event(struct ci_hdrc *ci, unsigned int event) { struct device *dev = ci->dev->parent; @@ -28,7 +28,7 @@ static int npcm_udc_notify_event(struct ci_hdrc *ci, unsigned event) hw_write(ci, OP_USBMODE, 0xffffffff, 0x0); break; default: - dev_dbg(dev, "unknown ci_hdrc event (%d)\n",event); + dev_dbg(dev, "unknown ci_hdrc event (%d)\n", event); break; } diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 2d7f616270c1..69ef3cd8d4f8 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -86,7 +86,7 @@ static int hw_device_state(struct ci_hdrc *ci, u32 dma) hw_write(ci, OP_ENDPTLISTADDR, ~0, dma); /* interrupt, error, port change, reset, sleep/suspend */ hw_write(ci, OP_USBINTR, ~0, - USBi_UI|USBi_UEI|USBi_PCI|USBi_URI|USBi_SLI); + USBi_UI|USBi_UEI|USBi_PCI|USBi_URI); } else { hw_write(ci, OP_USBINTR, ~0, 0); } @@ -877,6 +877,7 @@ __releases(ci->lock) __acquires(ci->lock) { int retval; + u32 intr; spin_unlock(&ci->lock); if (ci->gadget.speed != USB_SPEED_UNKNOWN) @@ -890,6 +891,11 @@ __acquires(ci->lock) if (retval) goto done; + /* clear SLI */ + hw_write(ci, OP_USBSTS, USBi_SLI, USBi_SLI); + intr = hw_read(ci, OP_USBINTR, ~0); + hw_write(ci, OP_USBINTR, ~0, intr | USBi_SLI); + ci->status = usb_ep_alloc_request(&ci->ep0in->ep, GFP_ATOMIC); if (ci->status == NULL) retval = -ENOMEM; diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 0c1b69d944ca..605fea461102 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -962,10 +962,12 @@ static int get_serial_info(struct tty_struct *tty, struct serial_struct *ss) struct acm *acm = tty->driver_data; ss->line = acm->minor; + mutex_lock(&acm->port.mutex); ss->close_delay = jiffies_to_msecs(acm->port.close_delay) / 10; ss->closing_wait = acm->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ? ASYNC_CLOSING_WAIT_NONE : jiffies_to_msecs(acm->port.closing_wait) / 10; + mutex_unlock(&acm->port.mutex); return 0; } diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 6bd9fe565385..34e46ef308ab 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -754,7 +754,7 @@ static struct urb *usbtmc_create_urb(void) if (!urb) return NULL; - dmabuf = kmalloc(bufsize, GFP_KERNEL); + dmabuf = kzalloc(bufsize, GFP_KERNEL); if (!dmabuf) { usb_free_urb(urb); return NULL; diff --git a/drivers/usb/common/common.c b/drivers/usb/common/common.c index 59b55d6cf490..b7bea1015d7c 100644 --- a/drivers/usb/common/common.c +++ b/drivers/usb/common/common.c @@ -107,19 +107,18 @@ EXPORT_SYMBOL_GPL(usb_speed_string); */ enum usb_device_speed usb_get_maximum_speed(struct device *dev) { - const char *maximum_speed; + const char *p = "maximum-speed"; int ret; - ret = device_property_read_string(dev, "maximum-speed", &maximum_speed); - if (ret < 0) - return USB_SPEED_UNKNOWN; - - ret = match_string(ssp_rate, ARRAY_SIZE(ssp_rate), maximum_speed); + ret = device_property_match_property_string(dev, p, ssp_rate, ARRAY_SIZE(ssp_rate)); if (ret > 0) return USB_SPEED_SUPER_PLUS; - ret = match_string(speed_names, ARRAY_SIZE(speed_names), maximum_speed); - return (ret < 0) ? USB_SPEED_UNKNOWN : ret; + ret = device_property_match_property_string(dev, p, speed_names, ARRAY_SIZE(speed_names)); + if (ret > 0) + return ret; + + return USB_SPEED_UNKNOWN; } EXPORT_SYMBOL_GPL(usb_get_maximum_speed); @@ -276,14 +275,13 @@ EXPORT_SYMBOL_GPL(usb_decode_interval); */ enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *np, int arg0) { - struct device_node *controller = NULL; + struct device_node *controller; struct of_phandle_args args; const char *dr_mode; int index; int err; - do { - controller = of_find_node_with_property(controller, "phys"); + for_each_node_with_property(controller, "phys") { if (!of_device_is_available(controller)) continue; index = 0; @@ -306,7 +304,7 @@ enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *np, int arg0) goto finish; index++; } while (args.np); - } while (controller); + } finish: err = of_property_read_string(controller, "dr_mode", &dr_mode); diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c index 7f8a912d4fe2..21585ed89ef8 100644 --- a/drivers/usb/core/usb-acpi.c +++ b/drivers/usb/core/usb-acpi.c @@ -142,6 +142,53 @@ int usb_acpi_set_power_state(struct usb_device *hdev, int index, bool enable) } EXPORT_SYMBOL_GPL(usb_acpi_set_power_state); +/** + * usb_acpi_add_usb4_devlink - add device link to USB4 Host Interface for tunneled USB3 devices + * + * @udev: Tunneled USB3 device connected to a roothub. + * + * Adds a device link between a tunneled USB3 device and the USB4 Host Interface + * device to ensure correct runtime PM suspend and resume order. This function + * should only be called for tunneled USB3 devices. + * The USB4 Host Interface this tunneled device depends on is found from the roothub + * port ACPI device specific data _DSD entry. + * + * Return: negative error code on failure, 0 otherwise + */ +static int usb_acpi_add_usb4_devlink(struct usb_device *udev) +{ + const struct device_link *link; + struct usb_port *port_dev; + struct usb_hub *hub; + + if (!udev->parent || udev->parent->parent) + return 0; + + hub = usb_hub_to_struct_hub(udev->parent); + port_dev = hub->ports[udev->portnum - 1]; + + struct fwnode_handle *nhi_fwnode __free(fwnode_handle) = + fwnode_find_reference(dev_fwnode(&port_dev->dev), "usb4-host-interface", 0); + + if (IS_ERR(nhi_fwnode)) + return 0; + + link = device_link_add(&port_dev->child->dev, nhi_fwnode->dev, + DL_FLAG_AUTOREMOVE_CONSUMER | + DL_FLAG_RPM_ACTIVE | + DL_FLAG_PM_RUNTIME); + if (!link) { + dev_err(&port_dev->dev, "Failed to created device link from %s to %s\n", + dev_name(&port_dev->child->dev), dev_name(nhi_fwnode->dev)); + return -EINVAL; + } + + dev_dbg(&port_dev->dev, "Created device link from %s to %s\n", + dev_name(&port_dev->child->dev), dev_name(nhi_fwnode->dev)); + + return 0; +} + /* * Private to usb-acpi, all the core needs to know is that * port_dev->location is non-zero when it has been set by the firmware. @@ -262,6 +309,12 @@ usb_acpi_find_companion_for_device(struct usb_device *udev) if (!hub) return NULL; + + /* Tunneled USB3 devices depend on USB4 Host Interface, set device link to it */ + if (udev->speed >= USB_SPEED_SUPER && + udev->tunnel_mode != USB_LINK_NATIVE) + usb_acpi_add_usb4_devlink(udev); + /* * This is an embedded USB device connected to a port and such * devices share port's ACPI companion. diff --git a/drivers/usb/dwc2/debugfs.c b/drivers/usb/dwc2/debugfs.c index 7c82ab590401..3116ac72747f 100644 --- a/drivers/usb/dwc2/debugfs.c +++ b/drivers/usb/dwc2/debugfs.c @@ -702,6 +702,7 @@ static int params_show(struct seq_file *seq, void *v) print_param(seq, p, uframe_sched); print_param(seq, p, external_id_pin_ctl); print_param(seq, p, power_down); + print_param(seq, p, no_clock_gating); print_param(seq, p, lpm); print_param(seq, p, lpm_clock_gating); print_param(seq, p, besl); diff --git a/drivers/usb/dwc2/drd.c b/drivers/usb/dwc2/drd.c index a8605b02115b..1ad8fa3f862a 100644 --- a/drivers/usb/dwc2/drd.c +++ b/drivers/usb/dwc2/drd.c @@ -127,6 +127,15 @@ static int dwc2_drd_role_sw_set(struct usb_role_switch *sw, enum usb_role role) role = USB_ROLE_DEVICE; } + if ((IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || + IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)) && + dwc2_is_device_mode(hsotg) && + hsotg->lx_state == DWC2_L2 && + hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_NONE && + hsotg->bus_suspended && + !hsotg->params.no_clock_gating) + dwc2_gadget_exit_clock_gating(hsotg, 0); + if (role == USB_ROLE_HOST) { already = dwc2_ovr_avalid(hsotg, true); } else if (role == USB_ROLE_DEVICE) { diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c index a937eadbc9b3..68226defdc60 100644 --- a/drivers/usb/dwc2/params.c +++ b/drivers/usb/dwc2/params.c @@ -23,6 +23,7 @@ static void dwc2_set_bcm_params(struct dwc2_hsotg *hsotg) p->max_transfer_size = 65535; p->max_packet_count = 511; p->ahbcfg = 0x10; + p->no_clock_gating = true; } static void dwc2_set_his_params(struct dwc2_hsotg *hsotg) @@ -352,6 +353,7 @@ const struct of_device_id dwc2_of_match_table[] = { MODULE_DEVICE_TABLE(of, dwc2_of_match_table); const struct acpi_device_id dwc2_acpi_match[] = { + /* This ID refers to the same USB IP as of_device_id brcm,bcm2835-usb */ { "BCM2848", (kernel_ulong_t)dwc2_set_bcm_params }, { }, }; diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 7b84416dfc2b..c1b7209b9483 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -469,18 +469,6 @@ static int dwc2_driver_probe(struct platform_device *dev) spin_lock_init(&hsotg->lock); - hsotg->irq = platform_get_irq(dev, 0); - if (hsotg->irq < 0) - return hsotg->irq; - - dev_dbg(hsotg->dev, "registering common handler for irq%d\n", - hsotg->irq); - retval = devm_request_irq(hsotg->dev, hsotg->irq, - dwc2_handle_common_intr, IRQF_SHARED, - dev_name(hsotg->dev), hsotg); - if (retval) - return retval; - hsotg->vbus_supply = devm_regulator_get_optional(hsotg->dev, "vbus"); if (IS_ERR(hsotg->vbus_supply)) { retval = PTR_ERR(hsotg->vbus_supply); @@ -524,6 +512,20 @@ static int dwc2_driver_probe(struct platform_device *dev) if (retval) goto error; + hsotg->irq = platform_get_irq(dev, 0); + if (hsotg->irq < 0) { + retval = hsotg->irq; + goto error; + } + + dev_dbg(hsotg->dev, "registering common handler for irq%d\n", + hsotg->irq); + retval = devm_request_irq(hsotg->dev, hsotg->irq, + dwc2_handle_common_intr, IRQF_SHARED, + dev_name(hsotg->dev), hsotg); + if (retval) + goto error; + /* * For OTG cores, set the force mode bits to reflect the value * of dr_mode. Force mode bits should not be touched at any diff --git a/drivers/usb/dwc3/dwc3-imx8mp.c b/drivers/usb/dwc3/dwc3-imx8mp.c index 8ee448068503..64c0cd1995aa 100644 --- a/drivers/usb/dwc3/dwc3-imx8mp.c +++ b/drivers/usb/dwc3/dwc3-imx8mp.c @@ -5,6 +5,7 @@ * Copyright (c) 2020 NXP. */ +#include #include #include #include @@ -96,7 +97,8 @@ static void imx8mp_configure_glue(struct dwc3_imx8mp *dwc3_imx) writel(value, dwc3_imx->glue_base + USB_CTRL1); } -static void dwc3_imx8mp_wakeup_enable(struct dwc3_imx8mp *dwc3_imx) +static void dwc3_imx8mp_wakeup_enable(struct dwc3_imx8mp *dwc3_imx, + pm_message_t msg) { struct dwc3 *dwc3 = platform_get_drvdata(dwc3_imx->dwc3); u32 val; @@ -106,12 +108,14 @@ static void dwc3_imx8mp_wakeup_enable(struct dwc3_imx8mp *dwc3_imx) val = readl(dwc3_imx->hsio_blk_base + USB_WAKEUP_CTRL); - if ((dwc3->current_dr_role == DWC3_GCTL_PRTCAP_HOST) && dwc3->xhci) - val |= USB_WAKEUP_EN | USB_WAKEUP_SS_CONN | - USB_WAKEUP_U3_EN | USB_WAKEUP_DPDM_EN; - else if (dwc3->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE) + if ((dwc3->current_dr_role == DWC3_GCTL_PRTCAP_HOST) && dwc3->xhci) { + val |= USB_WAKEUP_EN | USB_WAKEUP_DPDM_EN; + if (PMSG_IS_AUTO(msg)) + val |= USB_WAKEUP_SS_CONN | USB_WAKEUP_U3_EN; + } else { val |= USB_WAKEUP_EN | USB_WAKEUP_VBUS_EN | USB_WAKEUP_VBUS_SRC_SESS_VAL; + } writel(val, dwc3_imx->hsio_blk_base + USB_WAKEUP_CTRL); } @@ -144,10 +148,21 @@ static irqreturn_t dwc3_imx8mp_interrupt(int irq, void *_dwc3_imx) return IRQ_HANDLED; } +static int dwc3_imx8mp_set_software_node(struct device *dev) +{ + struct property_entry props[3] = { 0 }; + int prop_idx = 0; + + props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-missing-cas-quirk"); + props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-skip-phy-init-quirk"); + + return device_create_managed_software_node(dev, props, NULL); +} + static int dwc3_imx8mp_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct device_node *dwc3_np, *node = dev->of_node; + struct device_node *node = dev->of_node; struct dwc3_imx8mp *dwc3_imx; struct resource *res; int err, irq; @@ -178,39 +193,26 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev) return PTR_ERR(dwc3_imx->glue_base); } - dwc3_imx->hsio_clk = devm_clk_get(dev, "hsio"); - if (IS_ERR(dwc3_imx->hsio_clk)) { - err = PTR_ERR(dwc3_imx->hsio_clk); - dev_err(dev, "Failed to get hsio clk, err=%d\n", err); - return err; - } + dwc3_imx->hsio_clk = devm_clk_get_enabled(dev, "hsio"); + if (IS_ERR(dwc3_imx->hsio_clk)) + return dev_err_probe(dev, PTR_ERR(dwc3_imx->hsio_clk), + "Failed to get hsio clk\n"); - err = clk_prepare_enable(dwc3_imx->hsio_clk); - if (err) { - dev_err(dev, "Failed to enable hsio clk, err=%d\n", err); - return err; - } - - dwc3_imx->suspend_clk = devm_clk_get(dev, "suspend"); - if (IS_ERR(dwc3_imx->suspend_clk)) { - err = PTR_ERR(dwc3_imx->suspend_clk); - dev_err(dev, "Failed to get suspend clk, err=%d\n", err); - goto disable_hsio_clk; - } - - err = clk_prepare_enable(dwc3_imx->suspend_clk); - if (err) { - dev_err(dev, "Failed to enable suspend clk, err=%d\n", err); - goto disable_hsio_clk; - } + dwc3_imx->suspend_clk = devm_clk_get_enabled(dev, "suspend"); + if (IS_ERR(dwc3_imx->suspend_clk)) + return dev_err_probe(dev, PTR_ERR(dwc3_imx->suspend_clk), + "Failed to get suspend clk\n"); irq = platform_get_irq(pdev, 0); - if (irq < 0) { - err = irq; - goto disable_clks; - } + if (irq < 0) + return irq; dwc3_imx->irq = irq; + struct device_node *dwc3_np __free(device_node) = of_get_compatible_child(node, + "snps,dwc3"); + if (!dwc3_np) + return dev_err_probe(dev, -ENODEV, "failed to find dwc3 core child\n"); + imx8mp_configure_glue(dwc3_imx); pm_runtime_set_active(dev); @@ -219,17 +221,17 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev) if (err < 0) goto disable_rpm; - dwc3_np = of_get_compatible_child(node, "snps,dwc3"); - if (!dwc3_np) { + err = dwc3_imx8mp_set_software_node(dev); + if (err) { err = -ENODEV; - dev_err(dev, "failed to find dwc3 core child\n"); + dev_err(dev, "failed to create software node\n"); goto disable_rpm; } err = of_platform_populate(node, NULL, NULL, dev); if (err) { dev_err(&pdev->dev, "failed to create dwc3 core\n"); - goto err_node_put; + goto disable_rpm; } dwc3_imx->dwc3 = of_find_device_by_node(dwc3_np); @@ -238,7 +240,6 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev) err = -ENODEV; goto depopulate; } - of_node_put(dwc3_np); err = devm_request_threaded_irq(dev, irq, NULL, dwc3_imx8mp_interrupt, IRQF_ONESHOT, dev_name(dev), dwc3_imx); @@ -254,51 +255,39 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev) depopulate: of_platform_depopulate(dev); -err_node_put: - of_node_put(dwc3_np); disable_rpm: pm_runtime_disable(dev); pm_runtime_put_noidle(dev); -disable_clks: - clk_disable_unprepare(dwc3_imx->suspend_clk); -disable_hsio_clk: - clk_disable_unprepare(dwc3_imx->hsio_clk); return err; } static void dwc3_imx8mp_remove(struct platform_device *pdev) { - struct dwc3_imx8mp *dwc3_imx = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; pm_runtime_get_sync(dev); of_platform_depopulate(dev); - clk_disable_unprepare(dwc3_imx->suspend_clk); - clk_disable_unprepare(dwc3_imx->hsio_clk); - pm_runtime_disable(dev); pm_runtime_put_noidle(dev); } -static int __maybe_unused dwc3_imx8mp_suspend(struct dwc3_imx8mp *dwc3_imx, - pm_message_t msg) +static int dwc3_imx8mp_suspend(struct dwc3_imx8mp *dwc3_imx, pm_message_t msg) { if (dwc3_imx->pm_suspended) return 0; /* Wakeup enable */ if (PMSG_IS_AUTO(msg) || device_may_wakeup(dwc3_imx->dev)) - dwc3_imx8mp_wakeup_enable(dwc3_imx); + dwc3_imx8mp_wakeup_enable(dwc3_imx, msg); dwc3_imx->pm_suspended = true; return 0; } -static int __maybe_unused dwc3_imx8mp_resume(struct dwc3_imx8mp *dwc3_imx, - pm_message_t msg) +static int dwc3_imx8mp_resume(struct dwc3_imx8mp *dwc3_imx, pm_message_t msg) { struct dwc3 *dwc = platform_get_drvdata(dwc3_imx->dwc3); int ret = 0; @@ -331,7 +320,7 @@ static int __maybe_unused dwc3_imx8mp_resume(struct dwc3_imx8mp *dwc3_imx, return ret; } -static int __maybe_unused dwc3_imx8mp_pm_suspend(struct device *dev) +static int dwc3_imx8mp_pm_suspend(struct device *dev) { struct dwc3_imx8mp *dwc3_imx = dev_get_drvdata(dev); int ret; @@ -349,7 +338,7 @@ static int __maybe_unused dwc3_imx8mp_pm_suspend(struct device *dev) return ret; } -static int __maybe_unused dwc3_imx8mp_pm_resume(struct device *dev) +static int dwc3_imx8mp_pm_resume(struct device *dev) { struct dwc3_imx8mp *dwc3_imx = dev_get_drvdata(dev); int ret; @@ -379,7 +368,7 @@ static int __maybe_unused dwc3_imx8mp_pm_resume(struct device *dev) return ret; } -static int __maybe_unused dwc3_imx8mp_runtime_suspend(struct device *dev) +static int dwc3_imx8mp_runtime_suspend(struct device *dev) { struct dwc3_imx8mp *dwc3_imx = dev_get_drvdata(dev); @@ -388,7 +377,7 @@ static int __maybe_unused dwc3_imx8mp_runtime_suspend(struct device *dev) return dwc3_imx8mp_suspend(dwc3_imx, PMSG_AUTO_SUSPEND); } -static int __maybe_unused dwc3_imx8mp_runtime_resume(struct device *dev) +static int dwc3_imx8mp_runtime_resume(struct device *dev) { struct dwc3_imx8mp *dwc3_imx = dev_get_drvdata(dev); @@ -398,9 +387,9 @@ static int __maybe_unused dwc3_imx8mp_runtime_resume(struct device *dev) } static const struct dev_pm_ops dwc3_imx8mp_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(dwc3_imx8mp_pm_suspend, dwc3_imx8mp_pm_resume) - SET_RUNTIME_PM_OPS(dwc3_imx8mp_runtime_suspend, - dwc3_imx8mp_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(dwc3_imx8mp_pm_suspend, dwc3_imx8mp_pm_resume) + RUNTIME_PM_OPS(dwc3_imx8mp_runtime_suspend, dwc3_imx8mp_runtime_resume, + NULL) }; static const struct of_device_id dwc3_imx8mp_of_match[] = { @@ -414,7 +403,7 @@ static struct platform_driver dwc3_imx8mp_driver = { .remove_new = dwc3_imx8mp_remove, .driver = { .name = "imx8mp-dwc3", - .pm = &dwc3_imx8mp_dev_pm_ops, + .pm = pm_ptr(&dwc3_imx8mp_dev_pm_ops), .of_match_table = dwc3_imx8mp_of_match, }, }; diff --git a/drivers/usb/dwc3/dwc3-octeon.c b/drivers/usb/dwc3/dwc3-octeon.c index 6010135e1acc..1a3b205367fd 100644 --- a/drivers/usb/dwc3/dwc3-octeon.c +++ b/drivers/usb/dwc3/dwc3-octeon.c @@ -419,7 +419,7 @@ static int dwc3_octeon_probe(struct platform_device *pdev) int ref_clk_sel, ref_clk_fsel, mpll_mul; int power_active_low, power_gpio; int err, len; - u32 clock_rate; + u32 clock_rate, gpio_pwr[3]; if (of_property_read_u32(node, "refclk-frequency", &clock_rate)) { dev_err(dev, "No UCTL \"refclk-frequency\"\n"); @@ -476,21 +476,10 @@ static int dwc3_octeon_probe(struct platform_device *pdev) power_gpio = DWC3_GPIO_POWER_NONE; power_active_low = 0; - if (of_find_property(node, "power", &len)) { - u32 gpio_pwr[3]; - - switch (len) { - case 8: - of_property_read_u32_array(node, "power", gpio_pwr, 2); - break; - case 12: - of_property_read_u32_array(node, "power", gpio_pwr, 3); + len = of_property_read_variable_u32_array(node, "power", gpio_pwr, 2, 3); + if (len > 0) { + if (len == 3) power_active_low = gpio_pwr[2] & 0x01; - break; - default: - dev_err(dev, "invalid power configuration\n"); - return -EINVAL; - } power_gpio = gpio_pwr[1]; } diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c index 88fb6706a18d..c1d4b52f25b0 100644 --- a/drivers/usb/dwc3/dwc3-qcom.c +++ b/drivers/usb/dwc3/dwc3-qcom.c @@ -4,6 +4,7 @@ * Inspired by dwc3-of-simple.c */ +#include #include #include #include @@ -702,11 +703,12 @@ static int dwc3_qcom_clk_init(struct dwc3_qcom *qcom, int count) static int dwc3_qcom_of_register_core(struct platform_device *pdev) { struct dwc3_qcom *qcom = platform_get_drvdata(pdev); - struct device_node *np = pdev->dev.of_node, *dwc3_np; + struct device_node *np = pdev->dev.of_node; struct device *dev = &pdev->dev; int ret; - dwc3_np = of_get_compatible_child(np, "snps,dwc3"); + struct device_node *dwc3_np __free(device_node) = of_get_compatible_child(np, + "snps,dwc3"); if (!dwc3_np) { dev_err(dev, "failed to find dwc3 core child\n"); return -ENODEV; @@ -715,7 +717,7 @@ static int dwc3_qcom_of_register_core(struct platform_device *pdev) ret = of_platform_populate(np, NULL, NULL, dev); if (ret) { dev_err(dev, "failed to register dwc3 core - %d\n", ret); - goto node_put; + return ret; } qcom->dwc3 = of_find_device_by_node(dwc3_np); @@ -725,9 +727,6 @@ static int dwc3_qcom_of_register_core(struct platform_device *pdev) of_platform_depopulate(dev); } -node_put: - of_node_put(dwc3_np); - return ret; } @@ -736,7 +735,6 @@ static int dwc3_qcom_probe(struct platform_device *pdev) struct device_node *np = pdev->dev.of_node; struct device *dev = &pdev->dev; struct dwc3_qcom *qcom; - struct resource *res; int ret, i; bool ignore_pipe_clk; bool wakeup_source; @@ -774,9 +772,7 @@ static int dwc3_qcom_probe(struct platform_device *pdev) goto reset_assert; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - qcom->qscratch_base = devm_ioremap_resource(dev, res); + qcom->qscratch_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(qcom->qscratch_base)) { ret = PTR_ERR(qcom->qscratch_base); goto clk_disable; diff --git a/drivers/usb/dwc3/dwc3-rtk.c b/drivers/usb/dwc3/dwc3-rtk.c index 3cd6b184551c..e9c8b032c72c 100644 --- a/drivers/usb/dwc3/dwc3-rtk.c +++ b/drivers/usb/dwc3/dwc3-rtk.c @@ -6,6 +6,7 @@ * */ +#include #include #include #include @@ -173,23 +174,20 @@ static const char *const speed_names[] = { static enum usb_device_speed __get_dwc3_maximum_speed(struct device_node *np) { - struct device_node *dwc3_np; const char *maximum_speed; int ret; - dwc3_np = of_get_compatible_child(np, "snps,dwc3"); + struct device_node *dwc3_np __free(device_node) = of_get_compatible_child(np, + "snps,dwc3"); if (!dwc3_np) return USB_SPEED_UNKNOWN; ret = of_property_read_string(dwc3_np, "maximum-speed", &maximum_speed); if (ret < 0) - goto out; + return USB_SPEED_UNKNOWN; ret = match_string(speed_names, ARRAY_SIZE(speed_names), maximum_speed); -out: - of_node_put(dwc3_np); - return (ret < 0) ? USB_SPEED_UNKNOWN : ret; } @@ -276,7 +274,6 @@ static int dwc3_rtk_probe_dwc3_core(struct dwc3_rtk *rtk) struct device_node *node = dev->of_node; struct platform_device *dwc3_pdev; struct device *dwc3_dev; - struct device_node *dwc3_node; enum usb_dr_mode dr_mode; int ret = 0; @@ -290,7 +287,8 @@ static int dwc3_rtk_probe_dwc3_core(struct dwc3_rtk *rtk) return ret; } - dwc3_node = of_get_compatible_child(node, "snps,dwc3"); + struct device_node *dwc3_node __free(device_node) = of_get_compatible_child(node, + "snps,dwc3"); if (!dwc3_node) { dev_err(dev, "failed to find dwc3 core node\n"); ret = -ENODEV; @@ -301,7 +299,7 @@ static int dwc3_rtk_probe_dwc3_core(struct dwc3_rtk *rtk) if (!dwc3_pdev) { dev_err(dev, "failed to find dwc3 core platform_device\n"); ret = -ENODEV; - goto err_node_put; + goto depopulate; } dwc3_dev = &dwc3_pdev->dev; @@ -343,14 +341,11 @@ static int dwc3_rtk_probe_dwc3_core(struct dwc3_rtk *rtk) switch_usb2_role(rtk, rtk->cur_role); platform_device_put(dwc3_pdev); - of_node_put(dwc3_node); return 0; err_pdev_put: platform_device_put(dwc3_pdev); -err_node_put: - of_node_put(dwc3_node); depopulate: of_platform_depopulate(dev); @@ -363,30 +358,18 @@ static int dwc3_rtk_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct resource *res; void __iomem *regs; - int ret = 0; rtk = devm_kzalloc(dev, sizeof(*rtk), GFP_KERNEL); - if (!rtk) { - ret = -ENOMEM; - goto out; - } + if (!rtk) + return -ENOMEM; platform_set_drvdata(pdev, rtk); rtk->dev = dev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, "missing memory resource\n"); - ret = -ENODEV; - goto out; - } - - regs = devm_ioremap_resource(dev, res); - if (IS_ERR(regs)) { - ret = PTR_ERR(regs); - goto out; - } + regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(regs)) + return PTR_ERR(regs); rtk->regs = regs; rtk->regs_size = resource_size(res); @@ -394,16 +377,11 @@ static int dwc3_rtk_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (res) { rtk->pm_base = devm_ioremap_resource(dev, res); - if (IS_ERR(rtk->pm_base)) { - ret = PTR_ERR(rtk->pm_base); - goto out; - } + if (IS_ERR(rtk->pm_base)) + return PTR_ERR(rtk->pm_base); } - ret = dwc3_rtk_probe_dwc3_core(rtk); - -out: - return ret; + return dwc3_rtk_probe_dwc3_core(rtk); } static void dwc3_rtk_remove(struct platform_device *pdev) diff --git a/drivers/usb/dwc3/dwc3-st.c b/drivers/usb/dwc3/dwc3-st.c index c8c7cd0c1796..2841021f3557 100644 --- a/drivers/usb/dwc3/dwc3-st.c +++ b/drivers/usb/dwc3/dwc3-st.c @@ -14,6 +14,7 @@ * Inspired by dwc3-omap.c and dwc3-exynos.c. */ +#include #include #include #include @@ -197,7 +198,7 @@ static int st_dwc3_probe(struct platform_device *pdev) struct st_dwc3 *dwc3_data; struct resource *res; struct device *dev = &pdev->dev; - struct device_node *node = dev->of_node, *child; + struct device_node *node = dev->of_node; struct platform_device *child_pdev; struct regmap *regmap; int ret; @@ -224,15 +225,21 @@ static int st_dwc3_probe(struct platform_device *pdev) dwc3_data->syscfg_reg_off = res->start; - dev_vdbg(&pdev->dev, "glue-logic addr 0x%pK, syscfg-reg offset 0x%x\n", + dev_vdbg(dev, "glue-logic addr 0x%pK, syscfg-reg offset 0x%x\n", dwc3_data->glue_base, dwc3_data->syscfg_reg_off); + struct device_node *child __free(device_node) = of_get_compatible_child(node, + "snps,dwc3"); + if (!child) { + dev_err(dev, "failed to find dwc3 core node\n"); + return -ENODEV; + } + dwc3_data->rstc_pwrdn = devm_reset_control_get_exclusive(dev, "powerdown"); - if (IS_ERR(dwc3_data->rstc_pwrdn)) { - dev_err(&pdev->dev, "could not get power controller\n"); - return PTR_ERR(dwc3_data->rstc_pwrdn); - } + if (IS_ERR(dwc3_data->rstc_pwrdn)) + return dev_err_probe(dev, PTR_ERR(dwc3_data->rstc_pwrdn), + "could not get power controller\n"); /* Manage PowerDown */ reset_control_deassert(dwc3_data->rstc_pwrdn); @@ -240,26 +247,19 @@ static int st_dwc3_probe(struct platform_device *pdev) dwc3_data->rstc_rst = devm_reset_control_get_shared(dev, "softreset"); if (IS_ERR(dwc3_data->rstc_rst)) { - dev_err(&pdev->dev, "could not get reset controller\n"); - ret = PTR_ERR(dwc3_data->rstc_rst); + ret = dev_err_probe(dev, PTR_ERR(dwc3_data->rstc_rst), + "could not get reset controller\n"); goto undo_powerdown; } /* Manage SoftReset */ reset_control_deassert(dwc3_data->rstc_rst); - child = of_get_compatible_child(node, "snps,dwc3"); - if (!child) { - dev_err(&pdev->dev, "failed to find dwc3 core node\n"); - ret = -ENODEV; - goto err_node_put; - } - /* Allocate and initialize the core */ ret = of_platform_populate(node, NULL, NULL, dev); if (ret) { dev_err(dev, "failed to add dwc3 core\n"); - goto err_node_put; + goto undo_softreset; } child_pdev = of_find_device_by_node(child); @@ -270,7 +270,6 @@ static int st_dwc3_probe(struct platform_device *pdev) } dwc3_data->dr_mode = usb_get_dr_mode(&child_pdev->dev); - of_node_put(child); platform_device_put(child_pdev); /* @@ -282,8 +281,7 @@ static int st_dwc3_probe(struct platform_device *pdev) ret = st_dwc3_drd_init(dwc3_data); if (ret) { dev_err(dev, "drd initialisation failed\n"); - of_platform_depopulate(dev); - goto undo_softreset; + goto depopulate; } /* ST glue logic init */ @@ -294,8 +292,6 @@ static int st_dwc3_probe(struct platform_device *pdev) depopulate: of_platform_depopulate(dev); -err_node_put: - of_node_put(child); undo_softreset: reset_control_assert(dwc3_data->rstc_rst); undo_powerdown: diff --git a/drivers/usb/dwc3/dwc3-xilinx.c b/drivers/usb/dwc3/dwc3-xilinx.c index f1298b1b4f84..b5e5be424ce9 100644 --- a/drivers/usb/dwc3/dwc3-xilinx.c +++ b/drivers/usb/dwc3/dwc3-xilinx.c @@ -285,11 +285,8 @@ static int dwc3_xlnx_probe(struct platform_device *pdev) return -ENOMEM; regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(regs)) { - ret = PTR_ERR(regs); - dev_err_probe(dev, ret, "failed to map registers\n"); - return ret; - } + if (IS_ERR(regs)) + return dev_err_probe(dev, PTR_ERR(regs), "failed to map registers\n"); match = of_match_node(dwc3_xlnx_of_match, pdev->dev.of_node); diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index f261e6e157fd..9f3ad4f78a36 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -6,14 +6,14 @@ #include #include #include +#include #include #include #include "configfs.h" -#include "android_configfs_uevent.h" -#include "u_f.h" #include "u_os_desc.h" +#include "android_configfs_uevent.h" -int check_user_usb_string(const char *name, +static int check_user_usb_string(const char *name, struct usb_gadget_strings *stringtab_dev) { u16 num; @@ -907,7 +907,7 @@ static struct configfs_group_operations gadget_language_langid_group_ops = { .drop_item = gadget_language_string_drop, }; -static struct config_item_type gadget_language_type = { +static const struct config_item_type gadget_language_type = { .ct_item_ops = &gadget_language_langid_item_ops, .ct_group_ops = &gadget_language_langid_group_ops, .ct_attrs = gadget_language_langid_attrs, @@ -966,7 +966,7 @@ static struct configfs_group_operations gadget_language_group_ops = { .drop_item = &gadget_language_drop, }; -static struct config_item_type gadget_language_strings_type = { +static const struct config_item_type gadget_language_strings_type = { .ct_group_ops = &gadget_language_group_ops, .ct_owner = THIS_MODULE, }; @@ -1111,7 +1111,7 @@ static struct configfs_attribute *webusb_attrs[] = { NULL, }; -static struct config_item_type webusb_type = { +static const struct config_item_type webusb_type = { .ct_attrs = webusb_attrs, .ct_owner = THIS_MODULE, }; @@ -1268,7 +1268,7 @@ static struct configfs_item_operations os_desc_ops = { .drop_link = os_desc_unlink, }; -static struct config_item_type os_desc_type = { +static const struct config_item_type os_desc_type = { .ct_item_ops = &os_desc_ops, .ct_attrs = os_desc_attrs, .ct_owner = THIS_MODULE, diff --git a/drivers/usb/gadget/function/f_acm.c b/drivers/usb/gadget/function/f_acm.c index 724b2631f249..7061720b9732 100644 --- a/drivers/usb/gadget/function/f_acm.c +++ b/drivers/usb/gadget/function/f_acm.c @@ -41,6 +41,7 @@ struct f_acm { struct gserial port; u8 ctrl_id, data_id; u8 port_num; + u8 bInterfaceProtocol; u8 pending; @@ -89,7 +90,7 @@ acm_iad_descriptor = { .bInterfaceCount = 2, // control + data .bFunctionClass = USB_CLASS_COMM, .bFunctionSubClass = USB_CDC_SUBCLASS_ACM, - .bFunctionProtocol = USB_CDC_ACM_PROTO_AT_V25TER, + /* .bFunctionProtocol = DYNAMIC */ /* .iFunction = DYNAMIC */ }; @@ -101,7 +102,7 @@ static struct usb_interface_descriptor acm_control_interface_desc = { .bNumEndpoints = 1, .bInterfaceClass = USB_CLASS_COMM, .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, - .bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER, + /* .bInterfaceProtocol = DYNAMIC */ /* .iInterface = DYNAMIC */ }; @@ -663,6 +664,9 @@ acm_bind(struct usb_configuration *c, struct usb_function *f) goto fail; acm->notify = ep; + acm_iad_descriptor.bFunctionProtocol = acm->bInterfaceProtocol; + acm_control_interface_desc.bInterfaceProtocol = acm->bInterfaceProtocol; + /* allocate notification */ acm->notify_req = gs_alloc_req(ep, sizeof(struct usb_cdc_notification) + 2, @@ -719,8 +723,14 @@ static void acm_unbind(struct usb_configuration *c, struct usb_function *f) static void acm_free_func(struct usb_function *f) { struct f_acm *acm = func_to_acm(f); + struct f_serial_opts *opts; + + opts = container_of(f->fi, struct f_serial_opts, func_inst); kfree(acm); + mutex_lock(&opts->lock); + opts->instances--; + mutex_unlock(&opts->lock); } static void acm_resume(struct usb_function *f) @@ -761,7 +771,11 @@ static struct usb_function *acm_alloc_func(struct usb_function_instance *fi) acm->port.func.disable = acm_disable; opts = container_of(fi, struct f_serial_opts, func_inst); + mutex_lock(&opts->lock); acm->port_num = opts->port_num; + acm->bInterfaceProtocol = opts->protocol; + opts->instances++; + mutex_unlock(&opts->lock); acm->port.func.unbind = acm_unbind; acm->port.func.free_func = acm_free_func; acm->port.func.resume = acm_resume; @@ -812,11 +826,42 @@ static ssize_t f_acm_port_num_show(struct config_item *item, char *page) CONFIGFS_ATTR_RO(f_acm_, port_num); +static ssize_t f_acm_protocol_show(struct config_item *item, char *page) +{ + return sprintf(page, "%u\n", to_f_serial_opts(item)->protocol); +} + +static ssize_t f_acm_protocol_store(struct config_item *item, + const char *page, size_t count) +{ + struct f_serial_opts *opts = to_f_serial_opts(item); + int ret; + + mutex_lock(&opts->lock); + + if (opts->instances) { + ret = -EBUSY; + goto out; + } + + ret = kstrtou8(page, 0, &opts->protocol); + if (ret) + goto out; + ret = count; + +out: + mutex_unlock(&opts->lock); + return ret; +} + +CONFIGFS_ATTR(f_acm_, protocol); + static struct configfs_attribute *acm_attrs[] = { #ifdef CONFIG_U_SERIAL_CONSOLE &f_acm_attr_console, #endif &f_acm_attr_port_num, + &f_acm_attr_protocol, NULL, }; @@ -832,6 +877,7 @@ static void acm_free_instance(struct usb_function_instance *fi) opts = container_of(fi, struct f_serial_opts, func_inst); gserial_free_line(opts->port_num); + mutex_destroy(&opts->lock); kfree(opts); } @@ -843,7 +889,9 @@ static struct usb_function_instance *acm_alloc_instance(void) opts = kzalloc(sizeof(*opts), GFP_KERNEL); if (!opts) return ERR_PTR(-ENOMEM); + opts->protocol = USB_CDC_ACM_PROTO_AT_V25TER; opts->func_inst.free_func_inst = acm_free_instance; + mutex_init(&opts->lock); ret = gserial_alloc_line(&opts->port_num); if (ret) { kfree(opts); diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 7541718136c4..ea4d63f3910f 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -40,7 +41,6 @@ #include #include "u_fs.h" -#include "u_f.h" #include "u_os_desc.h" #include "configfs.h" @@ -2478,7 +2478,7 @@ typedef int (*ffs_os_desc_callback)(enum ffs_os_desc_type entity, static int __must_check ffs_do_single_desc(char *data, unsigned len, ffs_entity_callback entity, - void *priv, int *current_class) + void *priv, int *current_class, int *current_subclass) { struct usb_descriptor_header *_ds = (void *)data; u8 length; @@ -2535,6 +2535,7 @@ static int __must_check ffs_do_single_desc(char *data, unsigned len, if (ds->iInterface) __entity(STRING, ds->iInterface); *current_class = ds->bInterfaceClass; + *current_subclass = ds->bInterfaceSubClass; } break; @@ -2559,6 +2560,12 @@ static int __must_check ffs_do_single_desc(char *data, unsigned len, if (length != sizeof(struct ccid_descriptor)) goto inv_length; break; + } else if (*current_class == USB_CLASS_APP_SPEC && + *current_subclass == USB_SUBCLASS_DFU) { + pr_vdebug("dfu functional descriptor\n"); + if (length != sizeof(struct usb_dfu_functional_descriptor)) + goto inv_length; + break; } else { pr_vdebug("unknown descriptor: %d for class %d\n", _ds->bDescriptorType, *current_class); @@ -2621,6 +2628,7 @@ static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len, const unsigned _len = len; unsigned long num = 0; int current_class = -1; + int current_subclass = -1; for (;;) { int ret; @@ -2640,7 +2648,7 @@ static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len, return _len - len; ret = ffs_do_single_desc(data, len, entity, priv, - ¤t_class); + ¤t_class, ¤t_subclass); if (ret < 0) { pr_debug("%s returns %d\n", __func__, ret); return ret; diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c index 93dae017ae45..740311c4fa24 100644 --- a/drivers/usb/gadget/function/f_hid.c +++ b/drivers/usb/gadget/function/f_hid.c @@ -15,13 +15,21 @@ #include #include #include +#include +#include #include +#include -#include "u_f.h" #include "u_hid.h" #define HIDG_MINORS 4 +/* + * Most operating systems seem to allow for 5000ms timeout, we will allow + * userspace half that time to respond before we return an empty report. + */ +#define GET_REPORT_TIMEOUT_MS 2500 + static int major, minors; static const struct class hidg_class = { @@ -31,6 +39,11 @@ static const struct class hidg_class = { static DEFINE_IDA(hidg_ida); static DEFINE_MUTEX(hidg_ida_lock); /* protects access to hidg_ida */ +struct report_entry { + struct usb_hidg_report report_data; + struct list_head node; +}; + /*-------------------------------------------------------------------------*/ /* HID gadget struct */ @@ -75,6 +88,19 @@ struct f_hidg { wait_queue_head_t write_queue; struct usb_request *req; + /* get report */ + struct usb_request *get_req; + struct usb_hidg_report get_report; + bool get_report_returned; + int get_report_req_report_id; + int get_report_req_report_length; + spinlock_t get_report_spinlock; + wait_queue_head_t get_queue; /* Waiting for userspace response */ + wait_queue_head_t get_id_queue; /* Get ID came in */ + struct work_struct work; + struct workqueue_struct *workqueue; + struct list_head report_list; + struct device dev; struct cdev cdev; struct usb_function func; @@ -524,6 +550,174 @@ release_write_pending: return status; } +static struct report_entry *f_hidg_search_for_report(struct f_hidg *hidg, u8 report_id) +{ + struct list_head *ptr; + struct report_entry *entry; + + list_for_each(ptr, &hidg->report_list) { + entry = list_entry(ptr, struct report_entry, node); + if (entry->report_data.report_id == report_id) + return entry; + } + + return NULL; +} + +static void get_report_workqueue_handler(struct work_struct *work) +{ + struct f_hidg *hidg = container_of(work, struct f_hidg, work); + struct usb_composite_dev *cdev = hidg->func.config->cdev; + struct usb_request *req; + struct report_entry *ptr; + unsigned long flags; + + int status = 0; + + spin_lock_irqsave(&hidg->get_report_spinlock, flags); + req = hidg->get_req; + if (!req) { + spin_unlock_irqrestore(&hidg->get_report_spinlock, flags); + return; + } + + req->zero = 0; + req->length = min_t(unsigned int, min_t(unsigned int, hidg->get_report_req_report_length, + hidg->report_length), + MAX_REPORT_LENGTH); + + /* Check if there is a response available for immediate response */ + ptr = f_hidg_search_for_report(hidg, hidg->get_report_req_report_id); + if (ptr && !ptr->report_data.userspace_req) { + /* Report exists in list and it is to be used for immediate response */ + req->buf = ptr->report_data.data; + status = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + hidg->get_report_returned = true; + spin_unlock_irqrestore(&hidg->get_report_spinlock, flags); + } else { + /* + * Report does not exist in list or should not be immediately sent + * i.e. give userspace time to respond + */ + hidg->get_report_returned = false; + spin_unlock_irqrestore(&hidg->get_report_spinlock, flags); + wake_up(&hidg->get_id_queue); +#define GET_REPORT_COND (!hidg->get_report_returned) + /* Wait until userspace has responded or timeout */ + status = wait_event_interruptible_timeout(hidg->get_queue, !GET_REPORT_COND, + msecs_to_jiffies(GET_REPORT_TIMEOUT_MS)); + spin_lock_irqsave(&hidg->get_report_spinlock, flags); + req = hidg->get_req; + if (!req) { + spin_unlock_irqrestore(&hidg->get_report_spinlock, flags); + return; + } + if (status == 0 && !hidg->get_report_returned) { + /* GET_REPORT request was not serviced by userspace within timeout period */ + VDBG(cdev, "get_report : userspace timeout.\n"); + hidg->get_report_returned = true; + } + + /* Search again for report ID in list and respond to GET_REPORT request */ + ptr = f_hidg_search_for_report(hidg, hidg->get_report_req_report_id); + if (ptr) { + /* + * Either get an updated response just serviced by userspace + * or send the latest response in the list + */ + req->buf = ptr->report_data.data; + } else { + /* If there are no prevoiusly sent reports send empty report */ + req->buf = hidg->get_report.data; + memset(req->buf, 0x0, req->length); + } + + status = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + spin_unlock_irqrestore(&hidg->get_report_spinlock, flags); + } + + if (status < 0) + VDBG(cdev, "usb_ep_queue error on ep0 responding to GET_REPORT\n"); +} + +static int f_hidg_get_report_id(struct file *file, __u8 __user *buffer) +{ + struct f_hidg *hidg = file->private_data; + int ret = 0; + + ret = put_user(hidg->get_report_req_report_id, buffer); + + return ret; +} + +static int f_hidg_get_report(struct file *file, struct usb_hidg_report __user *buffer) +{ + struct f_hidg *hidg = file->private_data; + struct usb_composite_dev *cdev = hidg->func.config->cdev; + unsigned long flags; + struct report_entry *entry; + struct report_entry *ptr; + __u8 report_id; + + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + if (copy_from_user(&entry->report_data, buffer, + sizeof(struct usb_hidg_report))) { + ERROR(cdev, "copy_from_user error\n"); + kfree(entry); + return -EINVAL; + } + + report_id = entry->report_data.report_id; + + spin_lock_irqsave(&hidg->get_report_spinlock, flags); + ptr = f_hidg_search_for_report(hidg, report_id); + + if (ptr) { + /* Report already exists in list - update it */ + if (copy_from_user(&ptr->report_data, buffer, + sizeof(struct usb_hidg_report))) { + spin_unlock_irqrestore(&hidg->get_report_spinlock, flags); + ERROR(cdev, "copy_from_user error\n"); + kfree(entry); + return -EINVAL; + } + kfree(entry); + } else { + /* Report does not exist in list - add it */ + list_add_tail(&entry->node, &hidg->report_list); + } + + /* If there is no response pending then do nothing further */ + if (hidg->get_report_returned) { + spin_unlock_irqrestore(&hidg->get_report_spinlock, flags); + return 0; + } + + /* If this userspace response serves the current pending report */ + if (hidg->get_report_req_report_id == report_id) { + hidg->get_report_returned = true; + wake_up(&hidg->get_queue); + } + + spin_unlock_irqrestore(&hidg->get_report_spinlock, flags); + return 0; +} + +static long f_hidg_ioctl(struct file *file, unsigned int code, unsigned long arg) +{ + switch (code) { + case GADGET_HID_READ_GET_REPORT_ID: + return f_hidg_get_report_id(file, (__u8 __user *)arg); + case GADGET_HID_WRITE_GET_REPORT: + return f_hidg_get_report(file, (struct usb_hidg_report __user *)arg); + default: + return -ENOTTY; + } +} + static __poll_t f_hidg_poll(struct file *file, poll_table *wait) { struct f_hidg *hidg = file->private_data; @@ -531,6 +725,8 @@ static __poll_t f_hidg_poll(struct file *file, poll_table *wait) poll_wait(file, &hidg->read_queue, wait); poll_wait(file, &hidg->write_queue, wait); + poll_wait(file, &hidg->get_queue, wait); + poll_wait(file, &hidg->get_id_queue, wait); if (WRITE_COND) ret |= EPOLLOUT | EPOLLWRNORM; @@ -543,12 +739,16 @@ static __poll_t f_hidg_poll(struct file *file, poll_table *wait) ret |= EPOLLIN | EPOLLRDNORM; } + if (GET_REPORT_COND) + ret |= EPOLLPRI; + return ret; } #undef WRITE_COND #undef READ_COND_SSREPORT #undef READ_COND_INTOUT +#undef GET_REPORT_COND static int f_hidg_release(struct inode *inode, struct file *fd) { @@ -641,6 +841,10 @@ static void hidg_ssreport_complete(struct usb_ep *ep, struct usb_request *req) wake_up(&hidg->read_queue); } +static void hidg_get_report_complete(struct usb_ep *ep, struct usb_request *req) +{ +} + static int hidg_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) { @@ -649,6 +853,7 @@ static int hidg_setup(struct usb_function *f, struct usb_request *req = cdev->req; int status = 0; __u16 value, length; + unsigned long flags; value = __le16_to_cpu(ctrl->wValue); length = __le16_to_cpu(ctrl->wLength); @@ -660,14 +865,20 @@ static int hidg_setup(struct usb_function *f, switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 | HID_REQ_GET_REPORT): - VDBG(cdev, "get_report\n"); + VDBG(cdev, "get_report | wLength=%d\n", ctrl->wLength); - /* send an empty report */ - length = min_t(unsigned, length, hidg->report_length); - memset(req->buf, 0x0, length); + /* + * Update GET_REPORT ID so that an ioctl can be used to determine what + * GET_REPORT the request was actually for. + */ + spin_lock_irqsave(&hidg->get_report_spinlock, flags); + hidg->get_report_req_report_id = value & 0xff; + hidg->get_report_req_report_length = length; + spin_unlock_irqrestore(&hidg->get_report_spinlock, flags); - goto respond; - break; + queue_work(hidg->workqueue, &hidg->work); + + return status; case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 | HID_REQ_GET_PROTOCOL): @@ -793,6 +1004,14 @@ static void hidg_disable(struct usb_function *f) spin_unlock_irqrestore(&hidg->read_spinlock, flags); } + spin_lock_irqsave(&hidg->get_report_spinlock, flags); + if (!hidg->get_report_returned) { + usb_ep_free_request(f->config->cdev->gadget->ep0, hidg->get_req); + hidg->get_req = NULL; + hidg->get_report_returned = true; + } + spin_unlock_irqrestore(&hidg->get_report_spinlock, flags); + spin_lock_irqsave(&hidg->write_spinlock, flags); if (!hidg->write_pending) { free_ep_req(hidg->in_ep, hidg->req); @@ -902,6 +1121,14 @@ fail: return status; } +#ifdef CONFIG_COMPAT +static long f_hidg_compat_ioctl(struct file *file, unsigned int code, + unsigned long value) +{ + return f_hidg_ioctl(file, code, value); +} +#endif + static const struct file_operations f_hidg_fops = { .owner = THIS_MODULE, .open = f_hidg_open, @@ -909,6 +1136,10 @@ static const struct file_operations f_hidg_fops = { .write = f_hidg_write, .read = f_hidg_read, .poll = f_hidg_poll, + .unlocked_ioctl = f_hidg_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = f_hidg_compat_ioctl, +#endif .llseek = noop_llseek, }; @@ -919,6 +1150,15 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f) struct usb_string *us; int status; + hidg->get_req = usb_ep_alloc_request(c->cdev->gadget->ep0, GFP_ATOMIC); + if (!hidg->get_req) + return -ENOMEM; + + hidg->get_req->zero = 0; + hidg->get_req->complete = hidg_get_report_complete; + hidg->get_req->context = hidg; + hidg->get_report_returned = true; + /* maybe allocate device-global string IDs, and patch descriptors */ us = usb_gstrings_attach(c->cdev, ct_func_strings, ARRAY_SIZE(ct_func_string_defs)); @@ -1004,9 +1244,24 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f) hidg->write_pending = 1; hidg->req = NULL; spin_lock_init(&hidg->read_spinlock); + spin_lock_init(&hidg->get_report_spinlock); init_waitqueue_head(&hidg->write_queue); init_waitqueue_head(&hidg->read_queue); + init_waitqueue_head(&hidg->get_queue); + init_waitqueue_head(&hidg->get_id_queue); INIT_LIST_HEAD(&hidg->completed_out_req); + INIT_LIST_HEAD(&hidg->report_list); + + INIT_WORK(&hidg->work, get_report_workqueue_handler); + hidg->workqueue = alloc_workqueue("report_work", + WQ_FREEZABLE | + WQ_MEM_RECLAIM, + 1); + + if (!hidg->workqueue) { + status = -ENOMEM; + goto fail; + } /* create char device */ cdev_init(&hidg->cdev, &f_hidg_fops); @@ -1016,12 +1271,16 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f) return 0; fail_free_descs: + destroy_workqueue(hidg->workqueue); usb_free_all_descriptors(f); fail: ERROR(f->config->cdev, "hidg_bind FAILED\n"); if (hidg->req != NULL) free_ep_req(hidg->in_ep, hidg->req); + usb_ep_free_request(c->cdev->gadget->ep0, hidg->get_req); + hidg->get_req = NULL; + return status; } @@ -1256,7 +1515,7 @@ static void hidg_unbind(struct usb_configuration *c, struct usb_function *f) struct f_hidg *hidg = func_to_hidg(f); cdev_device_del(&hidg->cdev, &hidg->dev); - + destroy_workqueue(hidg->workqueue); usb_free_all_descriptors(f); } diff --git a/drivers/usb/gadget/function/f_loopback.c b/drivers/usb/gadget/function/f_loopback.c index 979b028edb99..49b009a7d5d7 100644 --- a/drivers/usb/gadget/function/f_loopback.c +++ b/drivers/usb/gadget/function/f_loopback.c @@ -14,9 +14,9 @@ #include #include #include +#include #include "g_zero.h" -#include "u_f.h" /* * LOOPBACK FUNCTION ... a testing vehicle for USB peripherals, diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index cfd712fd7452..e11d8c0edf06 100644 --- a/drivers/usb/gadget/function/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -3050,7 +3050,7 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f) if (!common->thread_task) { common->state = FSG_STATE_NORMAL; common->thread_task = - kthread_create(fsg_main_thread, common, "file-storage"); + kthread_run(fsg_main_thread, common, "file-storage"); if (IS_ERR(common->thread_task)) { ret = PTR_ERR(common->thread_task); common->thread_task = NULL; @@ -3059,7 +3059,6 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f) } DBG(common, "I/O thread pid: %d\n", task_pid_nr(common->thread_task)); - wake_up_process(common->thread_task); } fsg->gadget = gadget; diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index 67052a664e74..1067847cc079 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -30,11 +30,11 @@ #include #include +#include #include #include #include -#include "u_f.h" #include "u_midi.h" MODULE_AUTHOR("Ben Williamson"); diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c index 3f63253ad3e0..8285df9ed6fd 100644 --- a/drivers/usb/gadget/function/f_midi2.c +++ b/drivers/usb/gadget/function/f_midi2.c @@ -15,11 +15,11 @@ #include #include +#include #include #include #include -#include "u_f.h" #include "u_midi2.h" struct f_midi2; diff --git a/drivers/usb/gadget/function/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c index 6f3702210450..ec5fd25020fd 100644 --- a/drivers/usb/gadget/function/f_sourcesink.c +++ b/drivers/usb/gadget/function/f_sourcesink.c @@ -13,10 +13,10 @@ #include #include #include +#include #include #include "g_zero.h" -#include "u_f.h" /* * SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripheral diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index 2b9fb4daa806..c87e74afc881 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -377,24 +377,10 @@ enum { STR_AS_OUT_IF_ALT1, STR_AS_IN_IF_ALT0, STR_AS_IN_IF_ALT1, + NUM_STR_DESCRIPTORS, }; -static struct usb_string strings_uac1[] = { - /* [STR_AC_IF].s = DYNAMIC, */ - [STR_USB_OUT_IT].s = "Playback Input terminal", - [STR_USB_OUT_IT_CH_NAMES].s = "Playback Channels", - [STR_IO_OUT_OT].s = "Playback Output terminal", - [STR_IO_IN_IT].s = "Capture Input terminal", - [STR_IO_IN_IT_CH_NAMES].s = "Capture Channels", - [STR_USB_IN_OT].s = "Capture Output terminal", - [STR_FU_IN].s = "Capture Volume", - [STR_FU_OUT].s = "Playback Volume", - [STR_AS_OUT_IF_ALT0].s = "Playback Inactive", - [STR_AS_OUT_IF_ALT1].s = "Playback Active", - [STR_AS_IN_IF_ALT0].s = "Capture Inactive", - [STR_AS_IN_IF_ALT1].s = "Capture Active", - { }, -}; +static struct usb_string strings_uac1[NUM_STR_DESCRIPTORS + 1] = {}; static struct usb_gadget_strings str_uac1 = { .language = 0x0409, /* en-us */ @@ -1265,6 +1251,20 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) strings_uac1[STR_AC_IF].s = audio_opts->function_name; + strings_uac1[STR_USB_OUT_IT].s = audio_opts->c_it_name; + strings_uac1[STR_USB_OUT_IT_CH_NAMES].s = audio_opts->c_it_ch_name; + strings_uac1[STR_IO_OUT_OT].s = audio_opts->c_ot_name; + strings_uac1[STR_FU_OUT].s = audio_opts->c_fu_vol_name; + strings_uac1[STR_AS_OUT_IF_ALT0].s = "Playback Inactive"; + strings_uac1[STR_AS_OUT_IF_ALT1].s = "Playback Active"; + + strings_uac1[STR_IO_IN_IT].s = audio_opts->p_it_name; + strings_uac1[STR_IO_IN_IT_CH_NAMES].s = audio_opts->p_it_ch_name; + strings_uac1[STR_USB_IN_OT].s = audio_opts->p_ot_name; + strings_uac1[STR_FU_IN].s = audio_opts->p_fu_vol_name; + strings_uac1[STR_AS_IN_IF_ALT0].s = "Capture Inactive"; + strings_uac1[STR_AS_IN_IF_ALT1].s = "Capture Active"; + us = usb_gstrings_attach(cdev, uac1_strings, ARRAY_SIZE(strings_uac1)); if (IS_ERR(us)) return PTR_ERR(us); @@ -1681,8 +1681,19 @@ UAC1_ATTRIBUTE(bool, c_volume_present); UAC1_ATTRIBUTE(s16, c_volume_min); UAC1_ATTRIBUTE(s16, c_volume_max); UAC1_ATTRIBUTE(s16, c_volume_res); + UAC1_ATTRIBUTE_STRING(function_name); +UAC1_ATTRIBUTE_STRING(p_it_name); +UAC1_ATTRIBUTE_STRING(p_it_ch_name); +UAC1_ATTRIBUTE_STRING(p_ot_name); +UAC1_ATTRIBUTE_STRING(p_fu_vol_name); + +UAC1_ATTRIBUTE_STRING(c_it_name); +UAC1_ATTRIBUTE_STRING(c_it_ch_name); +UAC1_ATTRIBUTE_STRING(c_ot_name); +UAC1_ATTRIBUTE_STRING(c_fu_vol_name); + static struct configfs_attribute *f_uac1_attrs[] = { &f_uac1_opts_attr_c_chmask, &f_uac1_opts_attr_c_srate, @@ -1706,6 +1717,16 @@ static struct configfs_attribute *f_uac1_attrs[] = { &f_uac1_opts_attr_function_name, + &f_uac1_opts_attr_p_it_name, + &f_uac1_opts_attr_p_it_ch_name, + &f_uac1_opts_attr_p_ot_name, + &f_uac1_opts_attr_p_fu_vol_name, + + &f_uac1_opts_attr_c_it_name, + &f_uac1_opts_attr_c_it_ch_name, + &f_uac1_opts_attr_c_ot_name, + &f_uac1_opts_attr_c_fu_vol_name, + NULL, }; @@ -1760,6 +1781,16 @@ static struct usb_function_instance *f_audio_alloc_inst(void) scnprintf(opts->function_name, sizeof(opts->function_name), "AC Interface"); + scnprintf(opts->p_it_name, sizeof(opts->p_it_name), "Capture Input terminal"); + scnprintf(opts->p_it_ch_name, sizeof(opts->p_it_ch_name), "Capture Channels"); + scnprintf(opts->p_ot_name, sizeof(opts->p_ot_name), "Capture Output terminal"); + scnprintf(opts->p_fu_vol_name, sizeof(opts->p_fu_vol_name), "Capture Volume"); + + scnprintf(opts->c_it_name, sizeof(opts->c_it_name), "Playback Input terminal"); + scnprintf(opts->c_it_ch_name, sizeof(opts->c_it_ch_name), "Playback Channels"); + scnprintf(opts->c_ot_name, sizeof(opts->c_ot_name), "Playback Output terminal"); + scnprintf(opts->c_fu_vol_name, sizeof(opts->c_fu_vol_name), "Playback Volume"); + return &opts->func_inst; } diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 2d6d3286ffde..1cdda44455b3 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -95,7 +95,9 @@ enum { STR_CLKSRC_IN, STR_CLKSRC_OUT, STR_USB_IT, + STR_USB_IT_CH, STR_IO_IT, + STR_IO_IT_CH, STR_USB_OT, STR_IO_OT, STR_FU_IN, @@ -104,25 +106,10 @@ enum { STR_AS_OUT_ALT1, STR_AS_IN_ALT0, STR_AS_IN_ALT1, + NUM_STR_DESCRIPTORS, }; -static struct usb_string strings_fn[] = { - /* [STR_ASSOC].s = DYNAMIC, */ - [STR_IF_CTRL].s = "Topology Control", - [STR_CLKSRC_IN].s = "Input Clock", - [STR_CLKSRC_OUT].s = "Output Clock", - [STR_USB_IT].s = "USBH Out", - [STR_IO_IT].s = "USBD Out", - [STR_USB_OT].s = "USBH In", - [STR_IO_OT].s = "USBD In", - [STR_FU_IN].s = "Capture Volume", - [STR_FU_OUT].s = "Playback Volume", - [STR_AS_OUT_ALT0].s = "Playback Inactive", - [STR_AS_OUT_ALT1].s = "Playback Active", - [STR_AS_IN_ALT0].s = "Capture Inactive", - [STR_AS_IN_ALT1].s = "Capture Active", - { }, -}; +static struct usb_string strings_fn[NUM_STR_DESCRIPTORS + 1] = {}; static const char *const speed_names[] = { [USB_SPEED_UNKNOWN] = "UNKNOWN", @@ -1049,6 +1036,23 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) return ret; strings_fn[STR_ASSOC].s = uac2_opts->function_name; + strings_fn[STR_IF_CTRL].s = uac2_opts->if_ctrl_name; + strings_fn[STR_CLKSRC_IN].s = uac2_opts->clksrc_in_name; + strings_fn[STR_CLKSRC_OUT].s = uac2_opts->clksrc_out_name; + + strings_fn[STR_USB_IT].s = uac2_opts->c_it_name; + strings_fn[STR_USB_IT_CH].s = uac2_opts->c_it_ch_name; + strings_fn[STR_IO_OT].s = uac2_opts->c_ot_name; + strings_fn[STR_FU_OUT].s = uac2_opts->c_fu_vol_name; + strings_fn[STR_AS_OUT_ALT0].s = "Playback Inactive"; + strings_fn[STR_AS_OUT_ALT1].s = "Playback Active"; + + strings_fn[STR_IO_IT].s = uac2_opts->p_it_name; + strings_fn[STR_IO_IT_CH].s = uac2_opts->p_it_ch_name; + strings_fn[STR_USB_OT].s = uac2_opts->p_ot_name; + strings_fn[STR_FU_IN].s = uac2_opts->p_fu_vol_name; + strings_fn[STR_AS_IN_ALT0].s = "Capture Inactive"; + strings_fn[STR_AS_IN_ALT1].s = "Capture Active"; us = usb_gstrings_attach(cdev, fn_strings, ARRAY_SIZE(strings_fn)); if (IS_ERR(us)) @@ -1072,7 +1076,9 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) in_clk_src_desc.iClockSource = us[STR_CLKSRC_IN].id; out_clk_src_desc.iClockSource = us[STR_CLKSRC_OUT].id; usb_out_it_desc.iTerminal = us[STR_USB_IT].id; + usb_out_it_desc.iChannelNames = us[STR_USB_IT_CH].id; io_in_it_desc.iTerminal = us[STR_IO_IT].id; + io_in_it_desc.iChannelNames = us[STR_IO_IT_CH].id; usb_in_ot_desc.iTerminal = us[STR_USB_OT].id; io_out_ot_desc.iTerminal = us[STR_IO_OT].id; std_as_out_if0_desc.iInterface = us[STR_AS_OUT_ALT0].id; @@ -2100,10 +2106,24 @@ UAC2_ATTRIBUTE(s16, c_volume_max); UAC2_ATTRIBUTE(s16, c_volume_res); UAC2_ATTRIBUTE(u32, fb_max); UAC2_ATTRIBUTE_STRING(function_name); +UAC2_ATTRIBUTE_STRING(if_ctrl_name); +UAC2_ATTRIBUTE_STRING(clksrc_in_name); +UAC2_ATTRIBUTE_STRING(clksrc_out_name); + +UAC2_ATTRIBUTE_STRING(p_it_name); +UAC2_ATTRIBUTE_STRING(p_it_ch_name); +UAC2_ATTRIBUTE_STRING(p_ot_name); +UAC2_ATTRIBUTE_STRING(p_fu_vol_name); + +UAC2_ATTRIBUTE_STRING(c_it_name); +UAC2_ATTRIBUTE_STRING(c_it_ch_name); +UAC2_ATTRIBUTE_STRING(c_ot_name); +UAC2_ATTRIBUTE_STRING(c_fu_vol_name); UAC2_ATTRIBUTE(s16, p_terminal_type); UAC2_ATTRIBUTE(s16, c_terminal_type); + static struct configfs_attribute *f_uac2_attrs[] = { &f_uac2_opts_attr_p_chmask, &f_uac2_opts_attr_p_srate, @@ -2130,6 +2150,19 @@ static struct configfs_attribute *f_uac2_attrs[] = { &f_uac2_opts_attr_c_volume_res, &f_uac2_opts_attr_function_name, + &f_uac2_opts_attr_if_ctrl_name, + &f_uac2_opts_attr_clksrc_in_name, + &f_uac2_opts_attr_clksrc_out_name, + + &f_uac2_opts_attr_p_it_name, + &f_uac2_opts_attr_p_it_ch_name, + &f_uac2_opts_attr_p_ot_name, + &f_uac2_opts_attr_p_fu_vol_name, + + &f_uac2_opts_attr_c_it_name, + &f_uac2_opts_attr_c_it_ch_name, + &f_uac2_opts_attr_c_ot_name, + &f_uac2_opts_attr_c_fu_vol_name, &f_uac2_opts_attr_p_terminal_type, &f_uac2_opts_attr_c_terminal_type, @@ -2191,6 +2224,19 @@ static struct usb_function_instance *afunc_alloc_inst(void) opts->fb_max = FBACK_FAST_MAX; scnprintf(opts->function_name, sizeof(opts->function_name), "Source/Sink"); + scnprintf(opts->if_ctrl_name, sizeof(opts->if_ctrl_name), "Topology Control"); + scnprintf(opts->clksrc_in_name, sizeof(opts->clksrc_in_name), "Input Clock"); + scnprintf(opts->clksrc_out_name, sizeof(opts->clksrc_out_name), "Output Clock"); + + scnprintf(opts->p_it_name, sizeof(opts->p_it_name), "USBD Out"); + scnprintf(opts->p_it_ch_name, sizeof(opts->p_it_ch_name), "Capture Channels"); + scnprintf(opts->p_ot_name, sizeof(opts->p_ot_name), "USBH In"); + scnprintf(opts->p_fu_vol_name, sizeof(opts->p_fu_vol_name), "Capture Volume"); + + scnprintf(opts->c_it_name, sizeof(opts->c_it_name), "USBH Out"); + scnprintf(opts->c_it_ch_name, sizeof(opts->c_it_ch_name), "Playback Channels"); + scnprintf(opts->c_ot_name, sizeof(opts->c_ot_name), "USBD In"); + scnprintf(opts->c_fu_vol_name, sizeof(opts->c_fu_vol_name), "Playback Volume"); opts->p_terminal_type = UAC2_DEF_P_TERM_TYPE; opts->c_terminal_type = UAC2_DEF_C_TERM_TYPE; diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c index 24299576972f..ca8dbec65f73 100644 --- a/drivers/usb/gadget/function/u_audio.c +++ b/drivers/usb/gadget/function/u_audio.c @@ -1140,35 +1140,35 @@ static int u_audio_rate_get(struct snd_kcontrol *kcontrol, } static struct snd_kcontrol_new u_audio_controls[] = { - [UAC_FBACK_CTRL] { + [UAC_FBACK_CTRL] = { .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = "Capture Pitch 1000000", .info = u_audio_pitch_info, .get = u_audio_pitch_get, .put = u_audio_pitch_put, }, - [UAC_P_PITCH_CTRL] { + [UAC_P_PITCH_CTRL] = { .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = "Playback Pitch 1000000", .info = u_audio_pitch_info, .get = u_audio_pitch_get, .put = u_audio_pitch_put, }, - [UAC_MUTE_CTRL] { + [UAC_MUTE_CTRL] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "", /* will be filled later */ .info = u_audio_mute_info, .get = u_audio_mute_get, .put = u_audio_mute_put, }, - [UAC_VOLUME_CTRL] { + [UAC_VOLUME_CTRL] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "", /* will be filled later */ .info = u_audio_volume_info, .get = u_audio_volume_get, .put = u_audio_volume_put, }, - [UAC_RATE_CTRL] { + [UAC_RATE_CTRL] = { .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = "", /* will be filled later */ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c index b394105e55d6..0a8c05b2746b 100644 --- a/drivers/usb/gadget/function/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "u_serial.h" @@ -126,6 +127,7 @@ struct gs_port { wait_queue_head_t close_wait; bool suspended; /* port suspended */ bool start_delayed; /* delay start when suspended */ + struct async_icount icount; /* REVISIT this state ... */ struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */ @@ -257,6 +259,7 @@ __acquires(&port->port_lock) break; } do_tty_wake = true; + port->icount.tx += len; req->length = len; list_del(&req->list); @@ -408,6 +411,7 @@ static void gs_rx_push(struct work_struct *work) size -= n; } + port->icount.rx += size; count = tty_insert_flip_string(&port->port, packet, size); if (count) @@ -851,6 +855,23 @@ static int gs_break_ctl(struct tty_struct *tty, int duration) return status; } +static int gs_get_icount(struct tty_struct *tty, + struct serial_icounter_struct *icount) +{ + struct gs_port *port = tty->driver_data; + struct async_icount cnow; + unsigned long flags; + + spin_lock_irqsave(&port->port_lock, flags); + cnow = port->icount; + spin_unlock_irqrestore(&port->port_lock, flags); + + icount->rx = cnow.rx; + icount->tx = cnow.tx; + + return 0; +} + static const struct tty_operations gs_tty_ops = { .open = gs_open, .close = gs_close, @@ -861,6 +882,7 @@ static const struct tty_operations gs_tty_ops = { .chars_in_buffer = gs_chars_in_buffer, .unthrottle = gs_unthrottle, .break_ctl = gs_break_ctl, + .get_icount = gs_get_icount, }; /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/gadget/function/u_serial.h b/drivers/usb/gadget/function/u_serial.h index 901d99310bc4..e1274338ea61 100644 --- a/drivers/usb/gadget/function/u_serial.h +++ b/drivers/usb/gadget/function/u_serial.h @@ -17,6 +17,10 @@ struct f_serial_opts { struct usb_function_instance func_inst; u8 port_num; + u8 protocol; + + struct mutex lock; /* protect instances */ + int instances; }; /* diff --git a/drivers/usb/gadget/function/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h index f7a616760e31..feb6eb76462f 100644 --- a/drivers/usb/gadget/function/u_uac1.h +++ b/drivers/usb/gadget/function/u_uac1.h @@ -52,7 +52,17 @@ struct f_uac1_opts { int req_number; unsigned bound:1; - char function_name[32]; + char function_name[USB_MAX_STRING_LEN]; + + char p_it_name[USB_MAX_STRING_LEN]; + char p_it_ch_name[USB_MAX_STRING_LEN]; + char p_ot_name[USB_MAX_STRING_LEN]; + char p_fu_vol_name[USB_MAX_STRING_LEN]; + + char c_it_name[USB_MAX_STRING_LEN]; + char c_it_ch_name[USB_MAX_STRING_LEN]; + char c_ot_name[USB_MAX_STRING_LEN]; + char c_fu_vol_name[USB_MAX_STRING_LEN]; struct mutex lock; int refcnt; diff --git a/drivers/usb/gadget/function/u_uac2.h b/drivers/usb/gadget/function/u_uac2.h index 5e81bdd6c5fb..0df808289ded 100644 --- a/drivers/usb/gadget/function/u_uac2.h +++ b/drivers/usb/gadget/function/u_uac2.h @@ -68,7 +68,20 @@ struct f_uac2_opts { int fb_max; bool bound; - char function_name[32]; + char function_name[USB_MAX_STRING_LEN]; + char if_ctrl_name[USB_MAX_STRING_LEN]; + char clksrc_in_name[USB_MAX_STRING_LEN]; + char clksrc_out_name[USB_MAX_STRING_LEN]; + + char p_it_name[USB_MAX_STRING_LEN]; + char p_it_ch_name[USB_MAX_STRING_LEN]; + char p_ot_name[USB_MAX_STRING_LEN]; + char p_fu_vol_name[USB_MAX_STRING_LEN]; + + char c_it_name[USB_MAX_STRING_LEN]; + char c_it_ch_name[USB_MAX_STRING_LEN]; + char c_ot_name[USB_MAX_STRING_LEN]; + char c_fu_vol_name[USB_MAX_STRING_LEN]; s16 p_terminal_type; s16 c_terminal_type; diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index a024aecb76dc..de1736f834e6 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -121,6 +121,9 @@ static struct uvcg_format *find_format_by_pix(struct uvc_device *uvc, list_for_each_entry(format, &uvc->header->formats, entry) { const struct uvc_format_desc *fmtdesc = to_uvc_format(format->fmt); + if (IS_ERR(fmtdesc)) + continue; + if (fmtdesc->fcc == pixelformat) { uformat = format->fmt; break; @@ -240,6 +243,7 @@ uvc_v4l2_try_format(struct file *file, void *fh, struct v4l2_format *fmt) struct uvc_video *video = &uvc->video; struct uvcg_format *uformat; struct uvcg_frame *uframe; + const struct uvc_format_desc *fmtdesc; u8 *fcc; if (fmt->type != video->queue.queue.type) @@ -277,7 +281,10 @@ uvc_v4l2_try_format(struct file *file, void *fh, struct v4l2_format *fmt) fmt->fmt.pix.height = uframe->frame.w_height; fmt->fmt.pix.bytesperline = uvc_v4l2_get_bytesperline(uformat, uframe); fmt->fmt.pix.sizeimage = uvc_get_frame_size(uformat, uframe); - fmt->fmt.pix.pixelformat = to_uvc_format(uformat)->fcc; + fmtdesc = to_uvc_format(uformat); + if (IS_ERR(fmtdesc)) + return PTR_ERR(fmtdesc); + fmt->fmt.pix.pixelformat = fmtdesc->fcc; } fmt->fmt.pix.field = V4L2_FIELD_NONE; fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; @@ -389,6 +396,9 @@ uvc_v4l2_enum_format(struct file *file, void *fh, struct v4l2_fmtdesc *f) return -EINVAL; fmtdesc = to_uvc_format(uformat); + if (IS_ERR(fmtdesc)) + return PTR_ERR(fmtdesc); + f->pixelformat = fmtdesc->fcc; return 0; diff --git a/drivers/usb/gadget/u_f.c b/drivers/usb/gadget/u_f.c index 6aea1ecb3999..115d219c9c00 100644 --- a/drivers/usb/gadget/u_f.c +++ b/drivers/usb/gadget/u_f.c @@ -8,8 +8,8 @@ * Author: Andrzej Pietrasiewicz */ -#include "u_f.h" #include +#include struct usb_request *alloc_ep_req(struct usb_ep *ep, size_t len) { diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c index 35a652807fca..5149e2b7f050 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_core.c +++ b/drivers/usb/gadget/udc/bdc/bdc_core.c @@ -639,6 +639,7 @@ static const struct of_device_id bdc_of_match[] = { { .compatible = "brcm,bdc" }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, bdc_of_match); static struct platform_driver bdc_driver = { .driver = { diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c b/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c index d394affb7072..62fce42ef2da 100644 --- a/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c +++ b/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c @@ -2033,8 +2033,8 @@ static void cdns2_quiesce(struct cdns2_device *pdev) set_reg_bit_8(&pdev->usb_regs->usbcs, USBCS_DISCON); /* Disable interrupt. */ - writeb(0, &pdev->interrupt_regs->extien), - writeb(0, &pdev->interrupt_regs->usbien), + writeb(0, &pdev->interrupt_regs->extien); + writeb(0, &pdev->interrupt_regs->usbien); writew(0, &pdev->adma_regs->ep_ien); /* Clear interrupt line. */ diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-pci.c b/drivers/usb/gadget/udc/cdns2/cdns2-pci.c index 50c3d0974d9b..b1a8f772467c 100644 --- a/drivers/usb/gadget/udc/cdns2/cdns2-pci.c +++ b/drivers/usb/gadget/udc/cdns2/cdns2-pci.c @@ -15,8 +15,7 @@ #include "cdns2-gadget.h" #define PCI_DRIVER_NAME "cdns-pci-usbhs" -#define CDNS_VENDOR_ID 0x17cd -#define CDNS_DEVICE_ID 0x0120 +#define PCI_DEVICE_ID_CDNS_USB2 0x0120 #define PCI_BAR_DEV 0 #define PCI_DEV_FN_DEVICE 0 @@ -114,8 +113,8 @@ static const struct dev_pm_ops cdns2_pci_pm_ops = { }; static const struct pci_device_id cdns2_pci_ids[] = { - { PCI_VENDOR_ID_CDNS, CDNS_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, - PCI_CLASS_SERIAL_USB_DEVICE, PCI_ANY_ID }, + { PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_USB2), + .class = PCI_CLASS_SERIAL_USB_DEVICE }, { 0, } }; diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index f37b0d8386c1..ff7bee78bcc4 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -1304,7 +1304,8 @@ static int dummy_urb_enqueue( /* kick the scheduler, it'll do the rest */ if (!hrtimer_active(&dum_hcd->timer)) - hrtimer_start(&dum_hcd->timer, ns_to_ktime(DUMMY_TIMER_INT_NSECS), HRTIMER_MODE_REL); + hrtimer_start(&dum_hcd->timer, ns_to_ktime(DUMMY_TIMER_INT_NSECS), + HRTIMER_MODE_REL_SOFT); done: spin_unlock_irqrestore(&dum_hcd->dum->lock, flags); @@ -1325,7 +1326,7 @@ static int dummy_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) rc = usb_hcd_check_unlink_urb(hcd, urb, status); if (!rc && dum_hcd->rh_state != DUMMY_RH_RUNNING && !list_empty(&dum_hcd->urbp_list)) - hrtimer_start(&dum_hcd->timer, ns_to_ktime(0), HRTIMER_MODE_REL); + hrtimer_start(&dum_hcd->timer, ns_to_ktime(0), HRTIMER_MODE_REL_SOFT); spin_unlock_irqrestore(&dum_hcd->dum->lock, flags); return rc; @@ -1995,7 +1996,8 @@ return_urb: dum_hcd->udev = NULL; } else if (dum_hcd->rh_state == DUMMY_RH_RUNNING) { /* want a 1 msec delay here */ - hrtimer_start(&dum_hcd->timer, ns_to_ktime(DUMMY_TIMER_INT_NSECS), HRTIMER_MODE_REL); + hrtimer_start(&dum_hcd->timer, ns_to_ktime(DUMMY_TIMER_INT_NSECS), + HRTIMER_MODE_REL_SOFT); } spin_unlock_irqrestore(&dum->lock, flags); @@ -2389,7 +2391,7 @@ static int dummy_bus_resume(struct usb_hcd *hcd) dum_hcd->rh_state = DUMMY_RH_RUNNING; set_link_state(dum_hcd); if (!list_empty(&dum_hcd->urbp_list)) - hrtimer_start(&dum_hcd->timer, ns_to_ktime(0), HRTIMER_MODE_REL); + hrtimer_start(&dum_hcd->timer, ns_to_ktime(0), HRTIMER_MODE_REL_SOFT); hcd->state = HC_STATE_RUNNING; } spin_unlock_irq(&dum_hcd->dum->lock); @@ -2467,7 +2469,7 @@ static DEVICE_ATTR_RO(urbs); static int dummy_start_ss(struct dummy_hcd *dum_hcd) { - hrtimer_init(&dum_hcd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hrtimer_init(&dum_hcd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); dum_hcd->timer.function = dummy_timer; dum_hcd->rh_state = DUMMY_RH_RUNNING; dum_hcd->stream_en_ep = 0; @@ -2497,7 +2499,7 @@ static int dummy_start(struct usb_hcd *hcd) return dummy_start_ss(dum_hcd); spin_lock_init(&dum_hcd->dum->lock); - hrtimer_init(&dum_hcd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hrtimer_init(&dum_hcd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); dum_hcd->timer.function = dummy_timer; dum_hcd->rh_state = DUMMY_RH_RUNNING; diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c index d5f29f8fe481..3bfd889ed56a 100644 --- a/drivers/usb/gadget/udc/lpc32xx_udc.c +++ b/drivers/usb/gadget/udc/lpc32xx_udc.c @@ -1487,31 +1487,29 @@ static int udc_ep0_out_req(struct lpc32xx_udc *udc) req = list_entry(ep0->queue.next, struct lpc32xx_request, queue); - if (req) { - if (req->req.length == 0) { - /* Just dequeue request */ - done(ep0, req, 0); - udc->ep0state = WAIT_FOR_SETUP; - return 1; - } + if (req->req.length == 0) { + /* Just dequeue request */ + done(ep0, req, 0); + udc->ep0state = WAIT_FOR_SETUP; + return 1; + } - /* Get data from FIFO */ - bufferspace = req->req.length - req->req.actual; - if (bufferspace > ep0->ep.maxpacket) - bufferspace = ep0->ep.maxpacket; + /* Get data from FIFO */ + bufferspace = req->req.length - req->req.actual; + if (bufferspace > ep0->ep.maxpacket) + bufferspace = ep0->ep.maxpacket; - /* Copy data to buffer */ - prefetchw(req->req.buf + req->req.actual); - tr = udc_read_hwep(udc, EP_OUT, req->req.buf + req->req.actual, - bufferspace); - req->req.actual += bufferspace; + /* Copy data to buffer */ + prefetchw(req->req.buf + req->req.actual); + tr = udc_read_hwep(udc, EP_OUT, req->req.buf + req->req.actual, + bufferspace); + req->req.actual += bufferspace; - if (tr < ep0->ep.maxpacket) { - /* This is the last packet */ - done(ep0, req, 0); - udc->ep0state = WAIT_FOR_SETUP; - return 1; - } + if (tr < ep0->ep.maxpacket) { + /* This is the last packet */ + done(ep0, req, 0); + udc->ep0state = WAIT_FOR_SETUP; + return 1; } return 0; @@ -1962,18 +1960,17 @@ static void udc_handle_eps(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep) /* If there isn't a request waiting, something went wrong */ req = list_entry(ep->queue.next, struct lpc32xx_request, queue); - if (req) { - done(ep, req, 0); - /* Start another request if ready */ - if (!list_empty(&ep->queue)) { - if (ep->is_in) - udc_ep_in_req_dma(udc, ep); - else - udc_ep_out_req_dma(udc, ep); - } else - ep->req_pending = 0; - } + done(ep, req, 0); + + /* Start another request if ready */ + if (!list_empty(&ep->queue)) { + if (ep->is_in) + udc_ep_in_req_dma(udc, ep); + else + udc_ep_out_req_dma(udc, ep); + } else + ep->req_pending = 0; } @@ -1989,10 +1986,6 @@ static void udc_handle_dma_ep(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep) #endif req = list_entry(ep->queue.next, struct lpc32xx_request, queue); - if (!req) { - ep_err(ep, "DMA interrupt on no req!\n"); - return; - } dd = req->dd_desc_ptr; /* DMA descriptor should always be retired for this call */ diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c index 74590f93ea61..ebc45565c33e 100644 --- a/drivers/usb/gadget/udc/udc-xilinx.c +++ b/drivers/usb/gadget/udc/udc-xilinx.c @@ -947,7 +947,7 @@ static int xudc_ep_disable(struct usb_ep *_ep) ep->desc = NULL; ep->ep_usb.desc = NULL; - dev_dbg(udc->dev, "USB Ep %d disable\n ", ep->epnumber); + dev_dbg(udc->dev, "USB Ep %d disable\n", ep->epnumber); /* Disable the endpoint.*/ epcfg = udc->read_fn(udc->addr + ep->offset); epcfg &= ~XUSB_EP_CFG_VALID_MASK; diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 4448d0ab06f0..d011d6c753ed 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -40,11 +40,11 @@ config USB_XHCI_DBGCAP config USB_XHCI_PCI tristate depends on USB_PCI - depends on USB_XHCI_PCI_RENESAS || !USB_XHCI_PCI_RENESAS default y config USB_XHCI_PCI_RENESAS tristate "Support for additional Renesas xHCI controller with firmware" + depends on USB_XHCI_PCI help Say 'Y' to enable the support for the Renesas xHCI controller with firmware. Make sure you have the firmware for the device and diff --git a/drivers/usb/host/ehci-brcm.c b/drivers/usb/host/ehci-brcm.c index 77e42c739c58..68cad0620f1a 100644 --- a/drivers/usb/host/ehci-brcm.c +++ b/drivers/usb/host/ehci-brcm.c @@ -246,6 +246,7 @@ static const struct of_device_id brcm_ehci_of_match[] = { { .compatible = "brcm,bcm7445-ehci", }, {} }; +MODULE_DEVICE_TABLE(of, brcm_ehci_of_match); static struct platform_driver ehci_brcm_driver = { .probe = ehci_brcm_probe, diff --git a/drivers/usb/host/ehci-exynos.c b/drivers/usb/host/ehci-exynos.c index f40bc2a7a124..e3a961d3f5fc 100644 --- a/drivers/usb/host/ehci-exynos.c +++ b/drivers/usb/host/ehci-exynos.c @@ -48,7 +48,6 @@ struct exynos_ehci_hcd { static int exynos_ehci_get_phy(struct device *dev, struct exynos_ehci_hcd *exynos_ehci) { - struct device_node *child; struct phy *phy; int phy_number, num_phys; int ret; @@ -66,26 +65,22 @@ static int exynos_ehci_get_phy(struct device *dev, return 0; /* Get PHYs using legacy bindings */ - for_each_available_child_of_node(dev->of_node, child) { + for_each_available_child_of_node_scoped(dev->of_node, child) { ret = of_property_read_u32(child, "reg", &phy_number); if (ret) { dev_err(dev, "Failed to parse device tree\n"); - of_node_put(child); return ret; } if (phy_number >= PHY_NUMBER) { dev_err(dev, "Invalid number of PHYs\n"); - of_node_put(child); return -EINVAL; } phy = devm_of_phy_optional_get(dev, child, NULL); exynos_ehci->phy[phy_number] = phy; - if (IS_ERR(phy)) { - of_node_put(child); + if (IS_ERR(phy)) return PTR_ERR(phy); - } } exynos_ehci->legacy_phy = true; diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c index bfa2eba4e3a7..1379e03644b2 100644 --- a/drivers/usb/host/ohci-exynos.c +++ b/drivers/usb/host/ohci-exynos.c @@ -37,7 +37,6 @@ struct exynos_ohci_hcd { static int exynos_ohci_get_phy(struct device *dev, struct exynos_ohci_hcd *exynos_ohci) { - struct device_node *child; struct phy *phy; int phy_number, num_phys; int ret; @@ -55,26 +54,22 @@ static int exynos_ohci_get_phy(struct device *dev, return 0; /* Get PHYs using legacy bindings */ - for_each_available_child_of_node(dev->of_node, child) { + for_each_available_child_of_node_scoped(dev->of_node, child) { ret = of_property_read_u32(child, "reg", &phy_number); if (ret) { dev_err(dev, "Failed to parse device tree\n"); - of_node_put(child); return ret; } if (phy_number >= PHY_NUMBER) { dev_err(dev, "Invalid number of PHYs\n"); - of_node_put(child); return -EINVAL; } phy = devm_of_phy_optional_get(dev, child, NULL); exynos_ohci->phy[phy_number] = phy; - if (IS_ERR(phy)) { - of_node_put(child); + if (IS_ERR(phy)) return PTR_ERR(phy); - } } exynos_ohci->legacy_phy = true; diff --git a/drivers/usb/host/ohci-nxp.c b/drivers/usb/host/ohci-nxp.c index 8264c454f6bd..5b775e1ea527 100644 --- a/drivers/usb/host/ohci-nxp.c +++ b/drivers/usb/host/ohci-nxp.c @@ -51,8 +51,6 @@ static struct hc_driver __read_mostly ohci_nxp_hc_driver; static struct i2c_client *isp1301_i2c_client; -static struct clk *usb_host_clk; - static void isp1301_configure_lpc32xx(void) { /* LPC32XX only supports DAT_SE0 USB mode */ @@ -155,6 +153,7 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev) struct resource *res; int ret = 0, irq; struct device_node *isp1301_node; + struct clk *usb_host_clk; if (pdev->dev.of_node) { isp1301_node = of_parse_phandle(pdev->dev.of_node, @@ -180,26 +179,20 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev) } /* Enable USB host clock */ - usb_host_clk = devm_clk_get(&pdev->dev, NULL); + usb_host_clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(usb_host_clk)) { - dev_err(&pdev->dev, "failed to acquire USB OHCI clock\n"); + dev_err(&pdev->dev, "failed to acquire and start USB OHCI clock\n"); ret = PTR_ERR(usb_host_clk); goto fail_disable; } - ret = clk_prepare_enable(usb_host_clk); - if (ret < 0) { - dev_err(&pdev->dev, "failed to start USB OHCI clock\n"); - goto fail_disable; - } - isp1301_configure(); hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); if (!hcd) { dev_err(&pdev->dev, "Failed to allocate HC buffer\n"); ret = -ENOMEM; - goto fail_hcd; + goto fail_disable; } hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); @@ -229,8 +222,6 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev) ohci_nxp_stop_hc(); fail_resource: usb_put_hcd(hcd); -fail_hcd: - clk_disable_unprepare(usb_host_clk); fail_disable: isp1301_i2c_client = NULL; return ret; @@ -243,7 +234,6 @@ static void ohci_hcd_nxp_remove(struct platform_device *pdev) usb_remove_hcd(hcd); ohci_nxp_stop_hc(); usb_put_hcd(hcd); - clk_disable_unprepare(usb_host_clk); isp1301_i2c_client = NULL; } diff --git a/drivers/usb/host/ohci-ppc-of.c b/drivers/usb/host/ohci-ppc-of.c index f64bfe5f4d4d..a6be061f8653 100644 --- a/drivers/usb/host/ohci-ppc-of.c +++ b/drivers/usb/host/ohci-ppc-of.c @@ -202,10 +202,6 @@ static const struct of_device_id ohci_hcd_ppc_of_match[] = { }, #endif #ifdef CONFIG_USB_OHCI_HCD_PPC_OF_LE - { - .name = "usb", - .compatible = "ohci-littledian", - }, { .name = "usb", .compatible = "ohci-le", diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index 9f4bf8c5f8a5..6576515a29cd 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -297,9 +297,9 @@ static void put_child_connect_map(struct r8a66597 *r8a66597, int address) static void set_pipe_reg_addr(struct r8a66597_pipe *pipe, u8 dma_ch) { u16 pipenum = pipe->info.pipenum; - const unsigned long fifoaddr[] = {D0FIFO, D1FIFO, CFIFO}; - const unsigned long fifosel[] = {D0FIFOSEL, D1FIFOSEL, CFIFOSEL}; - const unsigned long fifoctr[] = {D0FIFOCTR, D1FIFOCTR, CFIFOCTR}; + static const unsigned long fifoaddr[] = {D0FIFO, D1FIFO, CFIFO}; + static const unsigned long fifosel[] = {D0FIFOSEL, D1FIFOSEL, CFIFOSEL}; + static const unsigned long fifoctr[] = {D0FIFOCTR, D1FIFOCTR, CFIFOCTR}; if (dma_ch > R8A66597_PIPE_NO_DMA) /* dma fifo not use? */ dma_ch = R8A66597_PIPE_NO_DMA; diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c index 161c09953c4e..241d7aa1fbc2 100644 --- a/drivers/usb/host/xhci-dbgcap.c +++ b/drivers/usb/host/xhci-dbgcap.c @@ -173,16 +173,18 @@ static void xhci_dbc_giveback(struct dbc_request *req, int status) spin_lock(&dbc->lock); } -static void xhci_dbc_flush_single_request(struct dbc_request *req) +static void trb_to_noop(union xhci_trb *trb) { - union xhci_trb *trb = req->trb; - trb->generic.field[0] = 0; trb->generic.field[1] = 0; trb->generic.field[2] = 0; trb->generic.field[3] &= cpu_to_le32(TRB_CYCLE); trb->generic.field[3] |= cpu_to_le32(TRB_TYPE(TRB_TR_NOOP)); +} +static void xhci_dbc_flush_single_request(struct dbc_request *req) +{ + trb_to_noop(req->trb); xhci_dbc_giveback(req, -ESHUTDOWN); } @@ -649,7 +651,6 @@ static void xhci_dbc_stop(struct xhci_dbc *dbc) case DS_DISABLED: return; case DS_CONFIGURED: - case DS_STALLED: if (dbc->driver->disconnect) dbc->driver->disconnect(dbc); break; @@ -669,6 +670,23 @@ static void xhci_dbc_stop(struct xhci_dbc *dbc) pm_runtime_put_sync(dbc->dev); /* note, was self.controller */ } +static void +handle_ep_halt_changes(struct xhci_dbc *dbc, struct dbc_ep *dep, bool halted) +{ + if (halted) { + dev_info(dbc->dev, "DbC Endpoint halted\n"); + dep->halted = 1; + + } else if (dep->halted) { + dev_info(dbc->dev, "DbC Endpoint halt cleared\n"); + dep->halted = 0; + + if (!list_empty(&dep->list_pending)) + writel(DBC_DOOR_BELL_TARGET(dep->direction), + &dbc->regs->doorbell); + } +} + static void dbc_handle_port_status(struct xhci_dbc *dbc, union xhci_trb *event) { @@ -697,6 +715,7 @@ static void dbc_handle_xfer_event(struct xhci_dbc *dbc, union xhci_trb *event) struct xhci_ring *ring; int ep_id; int status; + struct xhci_ep_ctx *ep_ctx; u32 comp_code; size_t remain_length; struct dbc_request *req = NULL, *r; @@ -706,8 +725,30 @@ static void dbc_handle_xfer_event(struct xhci_dbc *dbc, union xhci_trb *event) ep_id = TRB_TO_EP_ID(le32_to_cpu(event->generic.field[3])); dep = (ep_id == EPID_OUT) ? get_out_ep(dbc) : get_in_ep(dbc); + ep_ctx = (ep_id == EPID_OUT) ? + dbc_bulkout_ctx(dbc) : dbc_bulkin_ctx(dbc); ring = dep->ring; + /* Match the pending request: */ + list_for_each_entry(r, &dep->list_pending, list_pending) { + if (r->trb_dma == event->trans_event.buffer) { + req = r; + break; + } + if (r->status == -COMP_STALL_ERROR) { + dev_warn(dbc->dev, "Give back stale stalled req\n"); + ring->num_trbs_free++; + xhci_dbc_giveback(r, 0); + } + } + + if (!req) { + dev_warn(dbc->dev, "no matched request\n"); + return; + } + + trace_xhci_dbc_handle_transfer(ring, &req->trb->generic); + switch (comp_code) { case COMP_SUCCESS: remain_length = 0; @@ -718,31 +759,49 @@ static void dbc_handle_xfer_event(struct xhci_dbc *dbc, union xhci_trb *event) case COMP_TRB_ERROR: case COMP_BABBLE_DETECTED_ERROR: case COMP_USB_TRANSACTION_ERROR: - case COMP_STALL_ERROR: dev_warn(dbc->dev, "tx error %d detected\n", comp_code); status = -comp_code; break; + case COMP_STALL_ERROR: + dev_warn(dbc->dev, "Stall error at bulk TRB %llx, remaining %zu, ep deq %llx\n", + event->trans_event.buffer, remain_length, ep_ctx->deq); + status = 0; + dep->halted = 1; + + /* + * xHC DbC may trigger a STALL bulk xfer event when host sends a + * ClearFeature(ENDPOINT_HALT) request even if there wasn't an + * active bulk transfer. + * + * Don't give back this transfer request as hardware will later + * start processing TRBs starting from this 'STALLED' TRB, + * causing TRBs and requests to be out of sync. + * + * If STALL event shows some bytes were transferred then assume + * it's an actual transfer issue and give back the request. + * In this case mark the TRB as No-Op to avoid hw from using the + * TRB again. + */ + + if ((ep_ctx->deq & ~TRB_CYCLE) == event->trans_event.buffer) { + dev_dbg(dbc->dev, "Ep stopped on Stalled TRB\n"); + if (remain_length == req->length) { + dev_dbg(dbc->dev, "Spurious stall event, keep req\n"); + req->status = -COMP_STALL_ERROR; + req->actual = 0; + return; + } + dev_dbg(dbc->dev, "Give back stalled req, but turn TRB to No-op\n"); + trb_to_noop(req->trb); + } + break; + default: dev_err(dbc->dev, "unknown tx error %d\n", comp_code); status = -comp_code; break; } - /* Match the pending request: */ - list_for_each_entry(r, &dep->list_pending, list_pending) { - if (r->trb_dma == event->trans_event.buffer) { - req = r; - break; - } - } - - if (!req) { - dev_warn(dbc->dev, "no matched request\n"); - return; - } - - trace_xhci_dbc_handle_transfer(ring, &req->trb->generic); - ring->num_trbs_free++; req->actual = req->length - remain_length; xhci_dbc_giveback(req, status); @@ -762,7 +821,6 @@ static void inc_evt_deq(struct xhci_ring *ring) static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc) { dma_addr_t deq; - struct dbc_ep *dep; union xhci_trb *evt; u32 ctrl, portsc; bool update_erdp = false; @@ -814,43 +872,17 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc) return EVT_DISC; } - /* Handle endpoint stall event: */ + /* Check and handle changes in endpoint halt status */ ctrl = readl(&dbc->regs->control); - if ((ctrl & DBC_CTRL_HALT_IN_TR) || - (ctrl & DBC_CTRL_HALT_OUT_TR)) { - dev_info(dbc->dev, "DbC Endpoint stall\n"); - dbc->state = DS_STALLED; - - if (ctrl & DBC_CTRL_HALT_IN_TR) { - dep = get_in_ep(dbc); - xhci_dbc_flush_endpoint_requests(dep); - } - - if (ctrl & DBC_CTRL_HALT_OUT_TR) { - dep = get_out_ep(dbc); - xhci_dbc_flush_endpoint_requests(dep); - } - - return EVT_DONE; - } + handle_ep_halt_changes(dbc, get_in_ep(dbc), ctrl & DBC_CTRL_HALT_IN_TR); + handle_ep_halt_changes(dbc, get_out_ep(dbc), ctrl & DBC_CTRL_HALT_OUT_TR); /* Clear DbC run change bit: */ if (ctrl & DBC_CTRL_DBC_RUN_CHANGE) { writel(ctrl, &dbc->regs->control); ctrl = readl(&dbc->regs->control); } - break; - case DS_STALLED: - ctrl = readl(&dbc->regs->control); - if (!(ctrl & DBC_CTRL_HALT_IN_TR) && - !(ctrl & DBC_CTRL_HALT_OUT_TR) && - (ctrl & DBC_CTRL_DBC_RUN)) { - dbc->state = DS_CONFIGURED; - break; - } - - return EVT_DONE; default: dev_err(dbc->dev, "Unknown DbC state %d\n", dbc->state); break; @@ -939,7 +971,6 @@ static const char * const dbc_state_strings[DS_MAX] = { [DS_ENABLED] = "enabled", [DS_CONNECTED] = "connected", [DS_CONFIGURED] = "configured", - [DS_STALLED] = "stalled", }; static ssize_t dbc_show(struct device *dev, diff --git a/drivers/usb/host/xhci-dbgcap.h b/drivers/usb/host/xhci-dbgcap.h index 0118c6288a3c..8ec813b6e9fd 100644 --- a/drivers/usb/host/xhci-dbgcap.h +++ b/drivers/usb/host/xhci-dbgcap.h @@ -81,7 +81,6 @@ enum dbc_state { DS_ENABLED, DS_CONNECTED, DS_CONFIGURED, - DS_STALLED, DS_MAX }; @@ -90,6 +89,7 @@ struct dbc_ep { struct list_head list_pending; struct xhci_ring *ring; unsigned int direction:1; + unsigned int halted:1; }; #define DBC_QUEUE_SIZE 16 @@ -110,7 +110,6 @@ struct dbc_port { struct tasklet_struct push; struct list_head write_pool; - struct kfifo write_fifo; bool registered; }; diff --git a/drivers/usb/host/xhci-dbgtty.c b/drivers/usb/host/xhci-dbgtty.c index b74e98e94393..b8e78867e25a 100644 --- a/drivers/usb/host/xhci-dbgtty.c +++ b/drivers/usb/host/xhci-dbgtty.c @@ -24,19 +24,6 @@ static inline struct dbc_port *dbc_to_port(struct xhci_dbc *dbc) return dbc->priv; } -static unsigned int -dbc_send_packet(struct dbc_port *port, char *packet, unsigned int size) -{ - unsigned int len; - - len = kfifo_len(&port->write_fifo); - if (len < size) - size = len; - if (size != 0) - size = kfifo_out(&port->write_fifo, packet, size); - return size; -} - static int dbc_start_tx(struct dbc_port *port) __releases(&port->port_lock) __acquires(&port->port_lock) @@ -49,7 +36,7 @@ static int dbc_start_tx(struct dbc_port *port) while (!list_empty(pool)) { req = list_entry(pool->next, struct dbc_request, list_pool); - len = dbc_send_packet(port, req->buf, DBC_MAX_PACKET); + len = kfifo_out(&port->port.xmit_fifo, req->buf, DBC_MAX_PACKET); if (len == 0) break; do_tty_wake = true; @@ -216,7 +203,7 @@ static ssize_t dbc_tty_write(struct tty_struct *tty, const u8 *buf, spin_lock_irqsave(&port->port_lock, flags); if (count) - count = kfifo_in(&port->write_fifo, buf, count); + count = kfifo_in(&port->port.xmit_fifo, buf, count); dbc_start_tx(port); spin_unlock_irqrestore(&port->port_lock, flags); @@ -230,7 +217,7 @@ static int dbc_tty_put_char(struct tty_struct *tty, u8 ch) int status; spin_lock_irqsave(&port->port_lock, flags); - status = kfifo_put(&port->write_fifo, ch); + status = kfifo_put(&port->port.xmit_fifo, ch); spin_unlock_irqrestore(&port->port_lock, flags); return status; @@ -253,7 +240,7 @@ static unsigned int dbc_tty_write_room(struct tty_struct *tty) unsigned int room; spin_lock_irqsave(&port->port_lock, flags); - room = kfifo_avail(&port->write_fifo); + room = kfifo_avail(&port->port.xmit_fifo); spin_unlock_irqrestore(&port->port_lock, flags); return room; @@ -266,7 +253,7 @@ static unsigned int dbc_tty_chars_in_buffer(struct tty_struct *tty) unsigned int chars; spin_lock_irqsave(&port->port_lock, flags); - chars = kfifo_len(&port->write_fifo); + chars = kfifo_len(&port->port.xmit_fifo); spin_unlock_irqrestore(&port->port_lock, flags); return chars; @@ -346,7 +333,7 @@ static void dbc_rx_push(struct tasklet_struct *t) port->n_read = 0; } - list_move(&req->list_pool, &port->read_pool); + list_move_tail(&req->list_pool, &port->read_pool); } if (do_push) @@ -424,7 +411,8 @@ static int xhci_dbc_tty_register_device(struct xhci_dbc *dbc) goto err_idr; } - ret = kfifo_alloc(&port->write_fifo, DBC_WRITE_BUF_SIZE, GFP_KERNEL); + ret = kfifo_alloc(&port->port.xmit_fifo, DBC_WRITE_BUF_SIZE, + GFP_KERNEL); if (ret) goto err_exit_port; @@ -453,7 +441,7 @@ err_free_requests: xhci_dbc_free_requests(&port->read_pool); xhci_dbc_free_requests(&port->write_pool); err_free_fifo: - kfifo_free(&port->write_fifo); + kfifo_free(&port->port.xmit_fifo); err_exit_port: idr_remove(&dbc_tty_minors, port->minor); err_idr: @@ -478,7 +466,7 @@ static void xhci_dbc_tty_unregister_device(struct xhci_dbc *dbc) idr_remove(&dbc_tty_minors, port->minor); mutex_unlock(&dbc_tty_minors_lock); - kfifo_free(&port->write_fifo); + kfifo_free(&port->port.xmit_fifo); xhci_dbc_free_requests(&port->read_pool); xhci_dbc_free_requests(&port->read_queue); xhci_dbc_free_requests(&port->write_pool); diff --git a/drivers/usb/host/xhci-ext-caps.h b/drivers/usb/host/xhci-ext-caps.h index 96eb36a58738..67ecf7320c62 100644 --- a/drivers/usb/host/xhci-ext-caps.h +++ b/drivers/usb/host/xhci-ext-caps.h @@ -42,6 +42,7 @@ #define XHCI_EXT_CAPS_DEBUG 10 /* Vendor caps */ #define XHCI_EXT_CAPS_VENDOR_INTEL 192 +#define XHCI_EXT_CAPS_INTEL_SPR_SHADOW 206 /* USB Legacy Support Capability - section 7.1.1 */ #define XHCI_HC_BIOS_OWNED (1 << 16) #define XHCI_HC_OS_OWNED (1 << 24) @@ -64,6 +65,10 @@ #define XHCI_HLC (1 << 19) #define XHCI_BLC (1 << 20) +/* Intel SPR shadow capability */ +#define XHCI_INTEL_SPR_ESS_PORT_OFFSET 0x8ac4 /* SuperSpeed port control */ +#define XHCI_INTEL_SPR_TUNEN BIT(4) /* Tunnel mode enabled */ + /* command register values to disable interrupts and halt the HC */ /* start/stop HC execution - do not write unless HC is halted*/ #define XHCI_CMD_RUN (1 << 0) diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 61f083de6e19..d27c30ac17fd 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -752,6 +752,42 @@ static int xhci_exit_test_mode(struct xhci_hcd *xhci) return xhci_reset(xhci, XHCI_RESET_SHORT_USEC); } +/** + * xhci_port_is_tunneled() - Check if USB3 connection is tunneled over USB4 + * @xhci: xhci host controller + * @port: USB3 port to be checked. + * + * Some hosts can detect if a USB3 connection is native USB3 or tunneled over + * USB4. Intel hosts expose this via vendor specific extended capability 206 + * eSS PORT registers TUNEN (tunnel enabled) bit. + * + * A USB3 device must be connected to the port to detect the tunnel. + * + * Return: link tunnel mode enum, USB_LINK_UNKNOWN if host is incapable of + * detecting USB3 over USB4 tunnels. USB_LINK_NATIVE or USB_LINK_TUNNELED + * otherwise. + */ +enum usb_link_tunnel_mode xhci_port_is_tunneled(struct xhci_hcd *xhci, + struct xhci_port *port) +{ + void __iomem *base; + u32 offset; + + base = &xhci->cap_regs->hc_capbase; + offset = xhci_find_next_ext_cap(base, 0, XHCI_EXT_CAPS_INTEL_SPR_SHADOW); + + if (offset && offset <= XHCI_INTEL_SPR_ESS_PORT_OFFSET) { + offset = XHCI_INTEL_SPR_ESS_PORT_OFFSET + port->hcd_portnum * 0x20; + + if (readl(base + offset) & XHCI_INTEL_SPR_TUNEN) + return USB_LINK_TUNNELED; + else + return USB_LINK_NATIVE; + } + + return USB_LINK_UNKNOWN; +} + void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port, u32 link_state) { diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 937ce5fd5809..d2900197a49e 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -2332,7 +2332,8 @@ xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir, } struct xhci_interrupter * -xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs) +xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs, + u32 imod_interval) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); struct xhci_interrupter *ir; @@ -2365,6 +2366,11 @@ xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs) return NULL; } + err = xhci_set_interrupter_moderation(ir, imod_interval); + if (err) + xhci_warn(xhci, "Failed to set interrupter %d moderation to %uns\n", + i, imod_interval); + xhci_dbg(xhci, "Add secondary interrupter %d, max interrupters %d\n", i, xhci->max_interrupters); diff --git a/drivers/usb/host/xhci-pci-renesas.c b/drivers/usb/host/xhci-pci-renesas.c index 247cc7c2ce70..30cc5a1380a5 100644 --- a/drivers/usb/host/xhci-pci-renesas.c +++ b/drivers/usb/host/xhci-pci-renesas.c @@ -50,6 +50,8 @@ #define RENESAS_RETRY 10000 #define RENESAS_DELAY 10 +#define RENESAS_FW_NAME "renesas_usb_fw.mem" + static int renesas_fw_download_image(struct pci_dev *dev, const u32 *fw, size_t step, bool rom) { @@ -573,12 +575,10 @@ exit: return err; } -int renesas_xhci_check_request_fw(struct pci_dev *pdev, - const struct pci_device_id *id) +static int renesas_xhci_check_request_fw(struct pci_dev *pdev, + const struct pci_device_id *id) { - struct xhci_driver_data *driver_data = - (struct xhci_driver_data *)id->driver_data; - const char *fw_name = driver_data->firmware; + const char fw_name[] = RENESAS_FW_NAME; const struct firmware *fw; bool has_rom; int err; @@ -625,7 +625,41 @@ exit: release_firmware(fw); return err; } -EXPORT_SYMBOL_GPL(renesas_xhci_check_request_fw); -MODULE_DESCRIPTION("Support for Renesas xHCI controller with firmware"); +static int +xhci_pci_renesas_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + int retval; + + retval = renesas_xhci_check_request_fw(dev, id); + if (retval) + return retval; + + return xhci_pci_common_probe(dev, id); +} + +static const struct pci_device_id pci_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, 0x0014) }, + { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, 0x0015) }, + { /* end: all zeroes */ } +}; +MODULE_DEVICE_TABLE(pci, pci_ids); + +static struct pci_driver xhci_renesas_pci_driver = { + .name = "xhci-pci-renesas", + .id_table = pci_ids, + + .probe = xhci_pci_renesas_probe, + .remove = xhci_pci_remove, + + .shutdown = usb_hcd_pci_shutdown, + .driver = { + .pm = pm_ptr(&usb_hcd_pci_pm_ops), + }, +}; +module_pci_driver(xhci_renesas_pci_driver); + +MODULE_DESCRIPTION("Renesas xHCI PCI Host Controller Driver"); +MODULE_FIRMWARE(RENESAS_FW_NAME); +MODULE_IMPORT_NS(xhci); MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index dc1e345ab67e..91dccd25a551 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -55,6 +55,9 @@ #define PCI_DEVICE_ID_INTEL_ALDER_LAKE_PCH_XHCI 0x51ed #define PCI_DEVICE_ID_INTEL_ALDER_LAKE_N_PCH_XHCI 0x54ed +#define PCI_VENDOR_ID_PHYTIUM 0x1db7 +#define PCI_DEVICE_ID_PHYTIUM_XHCI 0xdc27 + /* Thunderbolt */ #define PCI_DEVICE_ID_INTEL_MAPLE_RIDGE_XHCI 0x1138 #define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_XHCI 0x15b5 @@ -78,6 +81,9 @@ #define PCI_DEVICE_ID_ASMEDIA_2142_XHCI 0x2142 #define PCI_DEVICE_ID_ASMEDIA_3242_XHCI 0x3242 +#define PCI_DEVICE_ID_CADENCE 0x17CD +#define PCI_DEVICE_ID_CADENCE_SSP 0x0200 + static const char hcd_name[] = "xhci_hcd"; static struct hc_driver __read_mostly xhci_pci_hc_driver; @@ -93,6 +99,10 @@ static const struct xhci_driver_overrides xhci_pci_overrides __initconst = { .update_hub_device = xhci_pci_update_hub_device, }; +/* + * Primary Legacy and MSI IRQ are synced in suspend_common(). + * All MSI-X IRQs and secondary MSI IRQs should be synced here. + */ static void xhci_msix_sync_irqs(struct xhci_hcd *xhci) { struct usb_hcd *hcd = xhci_to_hcd(xhci); @@ -105,13 +115,12 @@ static void xhci_msix_sync_irqs(struct xhci_hcd *xhci) } } -/* Free any IRQs and disable MSI-X */ +/* Legacy IRQ is freed by usb_remove_hcd() or usb_hcd_pci_shutdown() */ static void xhci_cleanup_msix(struct xhci_hcd *xhci) { struct usb_hcd *hcd = xhci_to_hcd(xhci); struct pci_dev *pdev = to_pci_dev(hcd->self.controller); - /* return if using legacy interrupt */ if (hcd->irq > 0) return; @@ -235,15 +244,6 @@ static int xhci_pci_reinit(struct xhci_hcd *xhci, struct pci_dev *pdev) static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) { struct pci_dev *pdev = to_pci_dev(dev); - struct xhci_driver_data *driver_data; - const struct pci_device_id *id; - - id = pci_match_id(to_pci_driver(pdev->dev.driver)->id_table, pdev); - - if (id && id->driver_data) { - driver_data = (struct xhci_driver_data *)id->driver_data; - xhci->quirks |= driver_data->quirks; - } /* Look for vendor-specific quirks */ if (pdev->vendor == PCI_VENDOR_ID_FRESCO_LOGIC && @@ -416,6 +416,10 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) if (pdev->vendor == PCI_VENDOR_ID_VIA) xhci->quirks |= XHCI_RESET_ON_RESUME; + if (pdev->vendor == PCI_VENDOR_ID_PHYTIUM && + pdev->device == PCI_DEVICE_ID_PHYTIUM_XHCI) + xhci->quirks |= XHCI_RESET_ON_RESUME; + /* See https://bugzilla.kernel.org/show_bug.cgi?id=79511 */ if (pdev->vendor == PCI_VENDOR_ID_VIA && pdev->device == 0x3432) @@ -473,6 +477,10 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) xhci->quirks |= XHCI_ZHAOXIN_TRB_FETCH; } + if (pdev->vendor == PCI_DEVICE_ID_CADENCE && + pdev->device == PCI_DEVICE_ID_CADENCE_SSP) + xhci->quirks |= XHCI_CDNS_SCTX_QUIRK; + /* xHC spec requires PCI devices to support D3hot and D3cold */ if (xhci->hci_version >= 0x120) xhci->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW; @@ -534,10 +542,9 @@ static int xhci_pci_setup(struct usb_hcd *hcd) struct xhci_hcd *xhci; struct pci_dev *pdev = to_pci_dev(hcd->self.controller); int retval; + u8 sbrn; xhci = hcd_to_xhci(hcd); - if (!xhci->sbrn) - pci_read_config_byte(pdev, XHCI_SBRN_OFFSET, &xhci->sbrn); /* imod_interval is the interrupt moderation value in nanoseconds. */ xhci->imod_interval = 40000; @@ -552,7 +559,8 @@ static int xhci_pci_setup(struct usb_hcd *hcd) if (xhci->quirks & XHCI_PME_STUCK_QUIRK) xhci_pme_acpi_rtd3_enable(pdev); - xhci_dbg(xhci, "Got SBRN %u\n", (unsigned int) xhci->sbrn); + pci_read_config_byte(pdev, XHCI_SBRN_OFFSET, &sbrn); + xhci_dbg(xhci, "Got SBRN %u\n", (unsigned int)sbrn); /* Find any debug ports */ return xhci_pci_reinit(xhci, pdev); @@ -572,21 +580,13 @@ static int xhci_pci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hd * We need to register our own PCI probe function (instead of the USB core's * function) in order to create a second roothub under xHCI. */ -static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) +int xhci_pci_common_probe(struct pci_dev *dev, const struct pci_device_id *id) { int retval; struct xhci_hcd *xhci; struct usb_hcd *hcd; - struct xhci_driver_data *driver_data; struct reset_control *reset; - driver_data = (struct xhci_driver_data *)id->driver_data; - if (driver_data && driver_data->quirks & XHCI_RENESAS_FW_QUIRK) { - retval = renesas_xhci_check_request_fw(dev, id); - if (retval) - return retval; - } - reset = devm_reset_control_get_optional_exclusive(&dev->dev, NULL); if (IS_ERR(reset)) return PTR_ERR(reset); @@ -651,12 +651,30 @@ put_runtime_pm: pm_runtime_put_noidle(&dev->dev); return retval; } +EXPORT_SYMBOL_NS_GPL(xhci_pci_common_probe, xhci); -static void xhci_pci_remove(struct pci_dev *dev) +static const struct pci_device_id pci_ids_reject[] = { + /* handled by xhci-pci-renesas */ + { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, 0x0014) }, + { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, 0x0015) }, + { /* end: all zeroes */ } +}; + +static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + if (pci_match_id(pci_ids_reject, dev)) + return -ENODEV; + + return xhci_pci_common_probe(dev, id); +} + +void xhci_pci_remove(struct pci_dev *dev) { struct xhci_hcd *xhci; + bool set_power_d3; xhci = hcd_to_xhci(pci_get_drvdata(dev)); + set_power_d3 = xhci->quirks & XHCI_SPURIOUS_WAKEUP; xhci->xhc_state |= XHCI_STATE_REMOVING; @@ -669,12 +687,13 @@ static void xhci_pci_remove(struct pci_dev *dev) xhci->shared_hcd = NULL; } - /* Workaround for spurious wakeups at shutdown with HSW */ - if (xhci->quirks & XHCI_SPURIOUS_WAKEUP) - pci_set_power_state(dev, PCI_D3hot); - usb_hcd_pci_remove(dev); + + /* Workaround for spurious wakeups at shutdown with HSW */ + if (set_power_d3) + pci_set_power_state(dev, PCI_D3hot); } +EXPORT_SYMBOL_NS_GPL(xhci_pci_remove, xhci); /* * In some Intel xHCI controllers, in order to get D3 working, @@ -783,7 +802,6 @@ static int xhci_pci_resume(struct usb_hcd *hcd, pm_message_t msg) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); struct pci_dev *pdev = to_pci_dev(hcd->self.controller); - int retval = 0; reset_control_reset(xhci->reset); @@ -814,8 +832,7 @@ static int xhci_pci_resume(struct usb_hcd *hcd, pm_message_t msg) if (xhci->quirks & XHCI_PME_STUCK_QUIRK) xhci_pme_quirk(hcd); - retval = xhci_resume(xhci, msg); - return retval; + return xhci_resume(xhci, msg); } static int xhci_pci_poweroff_late(struct usb_hcd *hcd, bool do_wakeup) @@ -882,19 +899,8 @@ static void xhci_pci_shutdown(struct usb_hcd *hcd) /*-------------------------------------------------------------------------*/ -static const struct xhci_driver_data reneses_data = { - .quirks = XHCI_RENESAS_FW_QUIRK, - .firmware = "renesas_usb_fw.mem", -}; - /* PCI driver selection metadata; PCI hotplugging uses this */ static const struct pci_device_id pci_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, 0x0014), - .driver_data = (unsigned long)&reneses_data, - }, - { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, 0x0015), - .driver_data = (unsigned long)&reneses_data, - }, /* handle any USB 3.0 xHCI controller */ { PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_XHCI, ~0), }, @@ -902,14 +908,6 @@ static const struct pci_device_id pci_ids[] = { }; MODULE_DEVICE_TABLE(pci, pci_ids); -/* - * Without CONFIG_USB_XHCI_PCI_RENESAS renesas_xhci_check_request_fw() won't - * load firmware, so don't encumber the xhci-pci driver with it. - */ -#if IS_ENABLED(CONFIG_USB_XHCI_PCI_RENESAS) -MODULE_FIRMWARE("renesas_usb_fw.mem"); -#endif - /* pci driver glue; this is a "new style" PCI driver module */ static struct pci_driver xhci_pci_driver = { .name = hcd_name, diff --git a/drivers/usb/host/xhci-pci.h b/drivers/usb/host/xhci-pci.h index cb9a8f331a44..e87c7d9d76b8 100644 --- a/drivers/usb/host/xhci-pci.h +++ b/drivers/usb/host/xhci-pci.h @@ -4,22 +4,7 @@ #ifndef XHCI_PCI_H #define XHCI_PCI_H -#if IS_ENABLED(CONFIG_USB_XHCI_PCI_RENESAS) -int renesas_xhci_check_request_fw(struct pci_dev *dev, - const struct pci_device_id *id); - -#else -static int renesas_xhci_check_request_fw(struct pci_dev *dev, - const struct pci_device_id *id) -{ - return 0; -} - -#endif - -struct xhci_driver_data { - u64 quirks; - const char *firmware; -}; +int xhci_pci_common_probe(struct pci_dev *dev, const struct pci_device_id *id); +void xhci_pci_remove(struct pci_dev *dev); #endif diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 31bdfa52eeb2..ecaa75718e59 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -259,6 +259,12 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s if (device_property_read_bool(tmpdev, "write-64-hi-lo-quirk")) xhci->quirks |= XHCI_WRITE_64_HI_LO; + if (device_property_read_bool(tmpdev, "xhci-missing-cas-quirk")) + xhci->quirks |= XHCI_MISSING_CAS; + + if (device_property_read_bool(tmpdev, "xhci-skip-phy-init-quirk")) + xhci->quirks |= XHCI_SKIP_PHY_INIT; + device_property_read_u32(tmpdev, "imod-interval-ns", &xhci->imod_interval); } diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 4ea2c3e072a9..4d664ba53fe9 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1399,6 +1399,20 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, struct xhci_stream_ctx *ctx = &ep->stream_info->stream_ctx_array[stream_id]; deq = le64_to_cpu(ctx->stream_ring) & SCTX_DEQ_MASK; + + /* + * Cadence xHCI controllers store some endpoint state + * information within Rsvd0 fields of Stream Endpoint + * context. This field is not cleared during Set TR + * Dequeue Pointer command which causes XDMA to skip + * over transfer ring and leads to data loss on stream + * pipe. + * To fix this issue driver must clear Rsvd0 field. + */ + if (xhci->quirks & XHCI_CDNS_SCTX_QUIRK) { + ctx->reserved[0] = 0; + ctx->reserved[1] = 0; + } } else { deq = le64_to_cpu(ep_ctx->deq) & ~EP_CTX_CYCLE_MASK; } @@ -2521,9 +2535,6 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, td->status = 0; break; case COMP_SHORT_PACKET: - xhci_dbg(xhci, "ep %#x - asked for %d bytes, %d bytes untransferred\n", - td->urb->ep->desc.bEndpointAddress, - requested, remaining); td->status = 0; break; case COMP_STOPPED_SHORT_PACKET: @@ -2764,35 +2775,25 @@ static int handle_tx_event(struct xhci_hcd *xhci, return 0; } - do { - /* This TRB should be in the TD at the head of this ring's - * TD list. + if (list_empty(&ep_ring->td_list)) { + /* + * Don't print wanings if ring is empty due to a stopped endpoint generating an + * extra completion event if the device was suspended. Or, a event for the last TRB + * of a short TD we already got a short event for. The short TD is already removed + * from the TD list. */ - if (list_empty(&ep_ring->td_list)) { - /* - * Don't print wanings if it's due to a stopped endpoint - * generating an extra completion event if the device - * was suspended. Or, a event for the last TRB of a - * short TD we already got a short event for. - * The short TD is already removed from the TD list. - */ - - if (!(trb_comp_code == COMP_STOPPED || - trb_comp_code == COMP_STOPPED_LENGTH_INVALID || - ep_ring->last_td_was_short)) { - xhci_warn(xhci, "WARN Event TRB for slot %u ep %d with no TDs queued?\n", - slot_id, ep_index); - } - if (ep->skip) { - ep->skip = false; - xhci_dbg(xhci, "td_list is empty while skip flag set. Clear skip flag for slot %u ep %u.\n", - slot_id, ep_index); - } - - td = NULL; - goto check_endpoint_halted; + if (trb_comp_code != COMP_STOPPED && + trb_comp_code != COMP_STOPPED_LENGTH_INVALID && + !ep_ring->last_td_was_short) { + xhci_warn(xhci, "Event TRB for slot %u ep %u with no TDs queued\n", + slot_id, ep_index); } + ep->skip = false; + goto check_endpoint_halted; + } + + do { td = list_first_entry(&ep_ring->td_list, struct xhci_td, td_list); @@ -2803,7 +2804,14 @@ static int handle_tx_event(struct xhci_hcd *xhci, if (ep->skip && usb_endpoint_xfer_isoc(&td->urb->ep->desc)) { skip_isoc_td(xhci, td, ep, status); - continue; + if (!list_empty(&ep_ring->td_list)) + continue; + + xhci_dbg(xhci, "All TDs skipped for slot %u ep %u. Clear skip flag.\n", + slot_id, ep_index); + ep->skip = false; + td = NULL; + goto check_endpoint_halted; } /* @@ -3941,10 +3949,6 @@ static int xhci_get_isoc_frame_id(struct xhci_hcd *xhci, start_frame_id = (start_frame_id >> 3) & 0x7ff; end_frame_id = (end_frame_id >> 3) & 0x7ff; - xhci_dbg(xhci, "%s: index %d, reg 0x%x start_frame_id 0x%x, end_frame_id 0x%x, start_frame 0x%x\n", - __func__, index, readl(&xhci->run_regs->microframe_index), - start_frame_id, end_frame_id, start_frame); - if (start_frame_id < end_frame_id) { if (start_frame > end_frame_id || start_frame < start_frame_id) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index efdf4c228b8c..899c0effb5d3 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -347,8 +347,8 @@ static int xhci_disable_interrupter(struct xhci_interrupter *ir) } /* interrupt moderation interval imod_interval in nanoseconds */ -static int xhci_set_interrupter_moderation(struct xhci_interrupter *ir, - u32 imod_interval) +int xhci_set_interrupter_moderation(struct xhci_interrupter *ir, + u32 imod_interval) { u32 imod; @@ -4525,6 +4525,20 @@ static int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev) struct xhci_port *port; u32 capability; + /* Check if USB3 device at root port is tunneled over USB4 */ + if (hcd->speed >= HCD_USB3 && !udev->parent->parent) { + port = xhci->usb3_rhub.ports[udev->portnum - 1]; + + udev->tunnel_mode = xhci_port_is_tunneled(xhci, port); + if (udev->tunnel_mode == USB_LINK_UNKNOWN) + dev_dbg(&udev->dev, "link tunnel state unknown\n"); + else if (udev->tunnel_mode == USB_LINK_TUNNELED) + dev_dbg(&udev->dev, "tunneled over USB4 link\n"); + else if (udev->tunnel_mode == USB_LINK_NATIVE) + dev_dbg(&udev->dev, "native USB 3.x link\n"); + return 0; + } + if (hcd->speed >= HCD_USB3 || !udev->lpm_capable || !xhci->hw_lpm_support) return 0; diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index ebd0afd59a60..620502de971a 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1498,15 +1498,10 @@ struct xhci_hcd { spinlock_t lock; /* packed release number */ - u8 sbrn; u16 hci_version; - u8 max_slots; u16 max_interrupters; - u8 max_ports; - u8 isoc_threshold; /* imod_interval in ns (I * 250ns) */ u32 imod_interval; - int event_ring_max; /* 4KB min, 128MB max */ int page_size; /* Valid values are 12 to 20, inclusive */ @@ -1616,7 +1611,7 @@ struct xhci_hcd { #define XHCI_DEFAULT_PM_RUNTIME_ALLOW BIT_ULL(33) #define XHCI_RESET_PLL_ON_DISCONNECT BIT_ULL(34) #define XHCI_SNPS_BROKEN_SUSPEND BIT_ULL(35) -#define XHCI_RENESAS_FW_QUIRK BIT_ULL(36) +/* Reserved. It was XHCI_RENESAS_FW_QUIRK */ #define XHCI_SKIP_PHY_INIT BIT_ULL(37) #define XHCI_DISABLE_SPARSE BIT_ULL(38) #define XHCI_SG_TRB_CACHE_SIZE_QUIRK BIT_ULL(39) @@ -1628,6 +1623,7 @@ struct xhci_hcd { #define XHCI_ZHAOXIN_TRB_FETCH BIT_ULL(45) #define XHCI_ZHAOXIN_HOST BIT_ULL(46) #define XHCI_WRITE_64_HI_LO BIT_ULL(47) +#define XHCI_CDNS_SCTX_QUIRK BIT_ULL(48) unsigned int num_active_eps; unsigned int limit_active_eps; @@ -1831,7 +1827,8 @@ struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci, void xhci_free_container_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx); struct xhci_interrupter * -xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs); +xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs, + u32 imod_interval); void xhci_remove_secondary_interrupter(struct usb_hcd *hcd, struct xhci_interrupter *ir); @@ -1871,6 +1868,8 @@ int xhci_alloc_tt_info(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev, struct usb_device *hdev, struct usb_tt *tt, gfp_t mem_flags); +int xhci_set_interrupter_moderation(struct xhci_interrupter *ir, + u32 imod_interval); /* xHCI ring, segment, TRB, and TD functions */ dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg, union xhci_trb *trb); @@ -1904,10 +1903,6 @@ int xhci_queue_reset_ep(struct xhci_hcd *xhci, struct xhci_command *cmd, enum xhci_ep_reset_type reset_type); int xhci_queue_reset_device(struct xhci_hcd *xhci, struct xhci_command *cmd, u32 slot_id); -void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci, unsigned int slot_id, - unsigned int ep_index, unsigned int stream_id, - struct xhci_td *td); -void xhci_stop_endpoint_command_watchdog(struct timer_list *t); void xhci_handle_command_timeout(struct work_struct *work); void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int slot_id, @@ -1929,7 +1924,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, int xhci_hub_status_data(struct usb_hcd *hcd, char *buf); int xhci_find_raw_port_number(struct usb_hcd *hcd, int port1); struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd); - +enum usb_link_tunnel_mode xhci_port_is_tunneled(struct xhci_hcd *xhci, + struct xhci_port *port); void xhci_hc_died(struct xhci_hcd *xhci); #ifdef CONFIG_PM diff --git a/drivers/usb/misc/appledisplay.c b/drivers/usb/misc/appledisplay.c index c8098e9b432e..62b5a30edc42 100644 --- a/drivers/usb/misc/appledisplay.c +++ b/drivers/usb/misc/appledisplay.c @@ -107,7 +107,12 @@ static void appledisplay_complete(struct urb *urb) case ACD_BTN_BRIGHT_UP: case ACD_BTN_BRIGHT_DOWN: pdata->button_pressed = 1; - schedule_delayed_work(&pdata->work, 0); + /* + * there is a window during which no device + * is registered + */ + if (pdata->bd ) + schedule_delayed_work(&pdata->work, 0); break; case ACD_BTN_NONE: default: @@ -202,6 +207,7 @@ static int appledisplay_probe(struct usb_interface *iface, const struct usb_device_id *id) { struct backlight_properties props; + struct backlight_device *backlight; struct appledisplay *pdata; struct usb_device *udev = interface_to_usbdev(iface); struct usb_endpoint_descriptor *endpoint; @@ -272,13 +278,14 @@ static int appledisplay_probe(struct usb_interface *iface, memset(&props, 0, sizeof(struct backlight_properties)); props.type = BACKLIGHT_RAW; props.max_brightness = 0xff; - pdata->bd = backlight_device_register(bl_name, NULL, pdata, + backlight = backlight_device_register(bl_name, NULL, pdata, &appledisplay_bl_data, &props); - if (IS_ERR(pdata->bd)) { + if (IS_ERR(backlight)) { dev_err(&iface->dev, "Backlight registration failed\n"); - retval = PTR_ERR(pdata->bd); + retval = PTR_ERR(backlight); goto error; } + pdata->bd = backlight; /* Try to get brightness */ brightness = appledisplay_bl_get_brightness(pdata->bd); diff --git a/drivers/usb/misc/brcmstb-usb-pinmap.c b/drivers/usb/misc/brcmstb-usb-pinmap.c index 2b2019c19cde..1ce885e4184c 100644 --- a/drivers/usb/misc/brcmstb-usb-pinmap.c +++ b/drivers/usb/misc/brcmstb-usb-pinmap.c @@ -335,6 +335,7 @@ static const struct of_device_id brcmstb_usb_pinmap_of_match[] = { { .compatible = "brcm,usb-pinmap" }, { }, }; +MODULE_DEVICE_TABLE(of, brcmstb_usb_pinmap_of_match); static struct platform_driver brcmstb_usb_pinmap_driver = { .driver = { diff --git a/drivers/usb/misc/cypress_cy7c63.c b/drivers/usb/misc/cypress_cy7c63.c index cecd7693b741..75f5a740cba3 100644 --- a/drivers/usb/misc/cypress_cy7c63.c +++ b/drivers/usb/misc/cypress_cy7c63.c @@ -88,6 +88,9 @@ static int vendor_command(struct cypress *dev, unsigned char request, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER, address, data, iobuf, CYPRESS_MAX_REQSIZE, USB_CTRL_GET_TIMEOUT); + /* we must not process garbage */ + if (retval < 2) + goto err_buf; /* store returned data (more READs to be added) */ switch (request) { @@ -107,6 +110,7 @@ static int vendor_command(struct cypress *dev, unsigned char request, break; } +err_buf: kfree(iobuf); error: return retval; diff --git a/drivers/usb/misc/onboard_usb_dev.c b/drivers/usb/misc/onboard_usb_dev.c index 56710e6b1653..560591e02d6a 100644 --- a/drivers/usb/misc/onboard_usb_dev.c +++ b/drivers/usb/misc/onboard_usb_dev.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -29,6 +30,17 @@ #include "onboard_usb_dev.h" +/* USB5744 register offset and mask */ +#define USB5744_CMD_ATTACH 0xAA +#define USB5744_CMD_ATTACH_LSB 0x56 +#define USB5744_CMD_CREG_ACCESS 0x99 +#define USB5744_CMD_CREG_ACCESS_LSB 0x37 +#define USB5744_CREG_MEM_ADDR 0x00 +#define USB5744_CREG_WRITE 0x00 +#define USB5744_CREG_RUNTIMEFLAGS2 0x41 +#define USB5744_CREG_RUNTIMEFLAGS2_LSB 0x1D +#define USB5744_CREG_BYPASS_UDC_SUSPEND BIT(3) + static void onboard_dev_attach_usb_driver(struct work_struct *work); static struct usb_device_driver onboard_dev_usbdev_driver; @@ -98,6 +110,7 @@ static int onboard_dev_power_on(struct onboard_dev *onboard_dev) fsleep(onboard_dev->pdata->reset_us); gpiod_set_value_cansleep(onboard_dev->reset_gpio, 0); + fsleep(onboard_dev->pdata->power_on_delay_us); onboard_dev->is_powered_on = true; @@ -296,10 +309,50 @@ static void onboard_dev_attach_usb_driver(struct work_struct *work) pr_err("Failed to attach USB driver: %pe\n", ERR_PTR(err)); } +static int onboard_dev_5744_i2c_init(struct i2c_client *client) +{ +#if IS_ENABLED(CONFIG_I2C) + struct device *dev = &client->dev; + int ret; + + /* + * Set BYPASS_UDC_SUSPEND bit to ensure MCU is always enabled + * and ready to respond to SMBus runtime commands. + * The command writes 5 bytes to memory and single data byte in + * configuration register. + */ + char wr_buf[7] = {USB5744_CREG_MEM_ADDR, 5, + USB5744_CREG_WRITE, 1, + USB5744_CREG_RUNTIMEFLAGS2, + USB5744_CREG_RUNTIMEFLAGS2_LSB, + USB5744_CREG_BYPASS_UDC_SUSPEND}; + + ret = i2c_smbus_write_block_data(client, 0, sizeof(wr_buf), wr_buf); + if (ret) + return dev_err_probe(dev, ret, "BYPASS_UDC_SUSPEND bit configuration failed\n"); + + ret = i2c_smbus_write_word_data(client, USB5744_CMD_CREG_ACCESS, + USB5744_CMD_CREG_ACCESS_LSB); + if (ret) + return dev_err_probe(dev, ret, "Configuration Register Access Command failed\n"); + + /* Send SMBus command to boot hub. */ + ret = i2c_smbus_write_word_data(client, USB5744_CMD_ATTACH, + USB5744_CMD_ATTACH_LSB); + if (ret < 0) + return dev_err_probe(dev, ret, "USB Attach with SMBus command failed\n"); + + return ret; +#else + return -ENODEV; +#endif +} + static int onboard_dev_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct onboard_dev *onboard_dev; + struct device_node *i2c_node; int err; onboard_dev = devm_kzalloc(dev, sizeof(*onboard_dev), GFP_KERNEL); @@ -339,6 +392,27 @@ static int onboard_dev_probe(struct platform_device *pdev) if (err) return err; + i2c_node = of_parse_phandle(pdev->dev.of_node, "i2c-bus", 0); + if (i2c_node) { + struct i2c_client *client; + + client = of_find_i2c_device_by_node(i2c_node); + of_node_put(i2c_node); + + if (!client) { + err = -EPROBE_DEFER; + goto err_power_off; + } + + if (of_device_is_compatible(pdev->dev.of_node, "usb424,2744") || + of_device_is_compatible(pdev->dev.of_node, "usb424,5744")) + err = onboard_dev_5744_i2c_init(client); + + put_device(&client->dev); + if (err < 0) + goto err_power_off; + } + /* * The USB driver might have been detached from the USB devices by * onboard_dev_remove() (e.g. through an 'unbind' by userspace), @@ -350,6 +424,10 @@ static int onboard_dev_probe(struct platform_device *pdev) schedule_work(&attach_usb_driver_work); return 0; + +err_power_off: + onboard_dev_power_off(onboard_dev); + return err; } static void onboard_dev_remove(struct platform_device *pdev) diff --git a/drivers/usb/misc/onboard_usb_dev.h b/drivers/usb/misc/onboard_usb_dev.h index fbba549c0f47..317b3eb99c02 100644 --- a/drivers/usb/misc/onboard_usb_dev.h +++ b/drivers/usb/misc/onboard_usb_dev.h @@ -10,6 +10,7 @@ struct onboard_dev_pdata { unsigned long reset_us; /* reset pulse width in us */ + unsigned long power_on_delay_us; /* power on delay in us */ unsigned int num_supplies; /* number of supplies */ const char * const supply_names[MAX_SUPPLIES]; bool is_hub; @@ -24,6 +25,7 @@ static const struct onboard_dev_pdata microchip_usb424_data = { static const struct onboard_dev_pdata microchip_usb5744_data = { .reset_us = 0, + .power_on_delay_us = 10000, .num_supplies = 2, .supply_names = { "vdd", "vdd2" }, .is_hub = true, diff --git a/drivers/usb/misc/qcom_eud.c b/drivers/usb/misc/qcom_eud.c index 26e9b8749d8a..19906301a4eb 100644 --- a/drivers/usb/misc/qcom_eud.c +++ b/drivers/usb/misc/qcom_eud.c @@ -232,7 +232,7 @@ static void eud_remove(struct platform_device *pdev) } static const struct of_device_id eud_dt_match[] = { - { .compatible = "qcom,sc7280-eud" }, + { .compatible = "qcom,eud" }, { } }; MODULE_DEVICE_TABLE(of, eud_dt_match); diff --git a/drivers/usb/misc/yurex.c b/drivers/usb/misc/yurex.c index 4745a320eae4..4a9859e03f6b 100644 --- a/drivers/usb/misc/yurex.c +++ b/drivers/usb/misc/yurex.c @@ -404,7 +404,6 @@ static ssize_t yurex_read(struct file *file, char __user *buffer, size_t count, struct usb_yurex *dev; int len = 0; char in_buffer[MAX_S64_STRLEN]; - unsigned long flags; dev = file->private_data; @@ -419,9 +418,9 @@ static ssize_t yurex_read(struct file *file, char __user *buffer, size_t count, return -EIO; } - spin_lock_irqsave(&dev->lock, flags); + spin_lock_irq(&dev->lock); scnprintf(in_buffer, MAX_S64_STRLEN, "%lld\n", dev->bbu); - spin_unlock_irqrestore(&dev->lock, flags); + spin_unlock_irq(&dev->lock); mutex_unlock(&dev->io_mutex); return simple_read_from_buffer(buffer, count, ppos, in_buffer, len); @@ -511,8 +510,11 @@ static ssize_t yurex_write(struct file *file, const char __user *user_buffer, __func__, retval); goto error; } - if (set && timeout) + if (set && timeout) { + spin_lock_irq(&dev->lock); dev->bbu = c2; + spin_unlock_irq(&dev->lock); + } return timeout ? count : -EIO; error: diff --git a/drivers/usb/musb/mediatek.c b/drivers/usb/musb/mediatek.c index 0a35aab3ab81..63c86c046b98 100644 --- a/drivers/usb/musb/mediatek.c +++ b/drivers/usb/musb/mediatek.c @@ -416,10 +416,9 @@ static int mtk_musb_probe(struct platform_device *pdev) return -ENOMEM; ret = of_platform_populate(np, NULL, NULL, dev); - if (ret) { - dev_err(dev, "failed to create child devices at %p\n", np); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, + "failed to create child devices at %p\n", np); ret = mtk_musb_clks_get(glue); if (ret) @@ -448,23 +447,19 @@ static int mtk_musb_probe(struct platform_device *pdev) glue->role = USB_ROLE_NONE; break; default: - dev_err(&pdev->dev, "Error 'dr_mode' property\n"); - return -EINVAL; + return dev_err_probe(&pdev->dev, -EINVAL, + "Error 'dr_mode' property\n"); } glue->phy = devm_of_phy_get_by_index(dev, np, 0); - if (IS_ERR(glue->phy)) { - dev_err(dev, "fail to getting phy %ld\n", - PTR_ERR(glue->phy)); - return PTR_ERR(glue->phy); - } + if (IS_ERR(glue->phy)) + return dev_err_probe(dev, PTR_ERR(glue->phy), + "fail to getting phy\n"); glue->usb_phy = usb_phy_generic_register(); - if (IS_ERR(glue->usb_phy)) { - dev_err(dev, "fail to registering usb-phy %ld\n", - PTR_ERR(glue->usb_phy)); - return PTR_ERR(glue->usb_phy); - } + if (IS_ERR(glue->usb_phy)) + return dev_err_probe(dev, PTR_ERR(glue->usb_phy), + "fail to registering usb-phy\n"); glue->xceiv = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); if (IS_ERR(glue->xceiv)) { diff --git a/drivers/usb/musb/mpfs.c b/drivers/usb/musb/mpfs.c index 29c7e5cdb230..00e13214aa76 100644 --- a/drivers/usb/musb/mpfs.c +++ b/drivers/usb/musb/mpfs.c @@ -49,30 +49,6 @@ static const struct musb_hdrc_config mpfs_musb_hdrc_config = { .ram_bits = MPFS_MUSB_RAM_BITS, }; -static irqreturn_t mpfs_musb_interrupt(int irq, void *__hci) -{ - unsigned long flags; - irqreturn_t ret = IRQ_NONE; - struct musb *musb = __hci; - - spin_lock_irqsave(&musb->lock, flags); - - musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB); - musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX); - musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX); - - if (musb->int_usb || musb->int_tx || musb->int_rx) { - musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb); - musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx); - musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx); - ret = musb_interrupt(musb); - } - - spin_unlock_irqrestore(&musb->lock, flags); - - return ret; -} - static void mpfs_musb_set_vbus(struct musb *musb, int is_on) { u8 devctl; @@ -111,6 +87,129 @@ static void mpfs_musb_set_vbus(struct musb *musb, int is_on) musb_readb(musb->mregs, MUSB_DEVCTL)); } +#define POLL_SECONDS 2 + +static void otg_timer(struct timer_list *t) +{ + struct musb *musb = from_timer(musb, t, dev_timer); + void __iomem *mregs = musb->mregs; + u8 devctl; + unsigned long flags; + + /* + * We poll because PolarFire SoC won't expose several OTG-critical + * status change events (from the transceiver) otherwise. + */ + devctl = musb_readb(mregs, MUSB_DEVCTL); + dev_dbg(musb->controller, "Poll devctl %02x (%s)\n", devctl, + usb_otg_state_string(musb->xceiv->otg->state)); + + spin_lock_irqsave(&musb->lock, flags); + switch (musb->xceiv->otg->state) { + case OTG_STATE_A_WAIT_BCON: + devctl &= ~MUSB_DEVCTL_SESSION; + musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); + + devctl = musb_readb(musb->mregs, MUSB_DEVCTL); + if (devctl & MUSB_DEVCTL_BDEVICE) { + musb->xceiv->otg->state = OTG_STATE_B_IDLE; + MUSB_DEV_MODE(musb); + mod_timer(&musb->dev_timer, jiffies + POLL_SECONDS * HZ); + } else { + musb->xceiv->otg->state = OTG_STATE_A_IDLE; + MUSB_HST_MODE(musb); + } + break; + case OTG_STATE_A_WAIT_VFALL: + if (devctl & MUSB_DEVCTL_VBUS) { + mod_timer(&musb->dev_timer, jiffies + POLL_SECONDS * HZ); + break; + } + musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE; + break; + case OTG_STATE_B_IDLE: + /* + * There's no ID-changed IRQ, so we have no good way to tell + * when to switch to the A-Default state machine (by setting + * the DEVCTL.Session bit). + * + * Workaround: whenever we're in B_IDLE, try setting the + * session flag every few seconds. If it works, ID was + * grounded and we're now in the A-Default state machine. + * + * NOTE: setting the session flag is _supposed_ to trigger + * SRP but clearly it doesn't. + */ + musb_writeb(mregs, MUSB_DEVCTL, devctl | MUSB_DEVCTL_SESSION); + devctl = musb_readb(mregs, MUSB_DEVCTL); + if (devctl & MUSB_DEVCTL_BDEVICE) + mod_timer(&musb->dev_timer, jiffies + POLL_SECONDS * HZ); + else + musb->xceiv->otg->state = OTG_STATE_A_IDLE; + break; + default: + break; + } + spin_unlock_irqrestore(&musb->lock, flags); +} + +static void __maybe_unused mpfs_musb_try_idle(struct musb *musb, unsigned long timeout) +{ + static unsigned long last_timer; + + if (timeout == 0) + timeout = jiffies + msecs_to_jiffies(3); + + /* Never idle if active, or when VBUS timeout is not set as host */ + if (musb->is_active || (musb->a_wait_bcon == 0 && + musb->xceiv->otg->state == OTG_STATE_A_WAIT_BCON)) { + dev_dbg(musb->controller, "%s active, deleting timer\n", + usb_otg_state_string(musb->xceiv->otg->state)); + del_timer(&musb->dev_timer); + last_timer = jiffies; + return; + } + + if (time_after(last_timer, timeout) && timer_pending(&musb->dev_timer)) { + dev_dbg(musb->controller, "Longer idle timer already pending, ignoring...\n"); + return; + } + last_timer = timeout; + + dev_dbg(musb->controller, "%s inactive, starting idle timer for %u ms\n", + usb_otg_state_string(musb->xceiv->otg->state), + jiffies_to_msecs(timeout - jiffies)); + mod_timer(&musb->dev_timer, timeout); +} + +static irqreturn_t mpfs_musb_interrupt(int irq, void *__hci) +{ + unsigned long flags; + irqreturn_t ret = IRQ_NONE; + struct musb *musb = __hci; + + spin_lock_irqsave(&musb->lock, flags); + + musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB); + musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX); + musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX); + + if (musb->int_usb || musb->int_tx || musb->int_rx) { + musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb); + musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx); + musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx); + ret = musb_interrupt(musb); + } + + /* Poll for ID change */ + if (musb->xceiv->otg->state == OTG_STATE_B_IDLE) + mod_timer(&musb->dev_timer, jiffies + POLL_SECONDS * HZ); + + spin_unlock_irqrestore(&musb->lock, flags); + + return ret; +} + static int mpfs_musb_init(struct musb *musb) { struct device *dev = musb->controller; @@ -121,6 +220,8 @@ static int mpfs_musb_init(struct musb *musb) return PTR_ERR(musb->xceiv); } + timer_setup(&musb->dev_timer, otg_timer, 0); + musb->dyn_fifo = true; musb->isr = mpfs_musb_interrupt; @@ -129,13 +230,24 @@ static int mpfs_musb_init(struct musb *musb) return 0; } +static int mpfs_musb_exit(struct musb *musb) +{ + del_timer_sync(&musb->dev_timer); + + return 0; +} + static const struct musb_platform_ops mpfs_ops = { .quirks = MUSB_DMA_INVENTRA, .init = mpfs_musb_init, + .exit = mpfs_musb_exit, .fifo_mode = 2, #ifdef CONFIG_USB_INVENTRA_DMA .dma_init = musbhs_dma_controller_create, .dma_exit = musbhs_dma_controller_destroy, +#endif +#ifndef CONFIG_USB_MUSB_HOST + .try_idle = mpfs_musb_try_idle, #endif .set_vbus = mpfs_musb_set_vbus }; diff --git a/drivers/usb/phy/phy-gpio-vbus-usb.c b/drivers/usb/phy/phy-gpio-vbus-usb.c index 817c242a76ca..5428b2b67de1 100644 --- a/drivers/usb/phy/phy-gpio-vbus-usb.c +++ b/drivers/usb/phy/phy-gpio-vbus-usb.c @@ -374,6 +374,7 @@ static const struct of_device_id gpio_vbus_of_match[] = { }, {}, }; +MODULE_DEVICE_TABLE(of, gpio_vbus_of_match); static struct platform_driver gpio_vbus_driver = { .driver = { diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index 920a32cd094d..cc4156c1b148 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -18,6 +18,7 @@ #include #include #include +#include #define DRIVER_NAME "mxs_phy" @@ -70,6 +71,9 @@ #define BM_USBPHY_PLL_EN_USB_CLKS BIT(6) /* Anatop Registers */ +#define ANADIG_REG_1P1_SET 0x114 +#define ANADIG_REG_1P1_CLR 0x118 + #define ANADIG_ANA_MISC0 0x150 #define ANADIG_ANA_MISC0_SET 0x154 #define ANADIG_ANA_MISC0_CLR 0x158 @@ -117,6 +121,14 @@ #define BM_ANADIG_USB2_MISC_RX_VPIN_FS BIT(29) #define BM_ANADIG_USB2_MISC_RX_VMIN_FS BIT(28) +/* System Integration Module (SIM) Registers */ +#define SIM_GPR1 0x30 + +#define USB_PHY_VLLS_WAKEUP_EN BIT(0) + +#define BM_ANADIG_REG_1P1_ENABLE_WEAK_LINREG BIT(18) +#define BM_ANADIG_REG_1P1_TRACK_VDD_SOC_CAP BIT(19) + #define to_mxs_phy(p) container_of((p), struct mxs_phy, phy) /* Do disconnection between PHY and controller without vbus */ @@ -149,6 +161,15 @@ #define MXS_PHY_TX_D_CAL_MIN 79 #define MXS_PHY_TX_D_CAL_MAX 119 +/* + * At imx6q/6sl/6sx, the PHY2's clock is controlled by hardware directly, + * eg, according to PHY's suspend status. In these PHYs, we only need to + * open the clock at the initialization and close it at its shutdown routine. + * These PHYs can send resume signal without software interfere if not + * gate clock. + */ +#define MXS_PHY_HARDWARE_CONTROL_PHY2_CLK BIT(4) + struct mxs_phy_data { unsigned int flags; }; @@ -160,12 +181,14 @@ static const struct mxs_phy_data imx23_phy_data = { static const struct mxs_phy_data imx6q_phy_data = { .flags = MXS_PHY_SENDING_SOF_TOO_FAST | MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS | - MXS_PHY_NEED_IP_FIX, + MXS_PHY_NEED_IP_FIX | + MXS_PHY_HARDWARE_CONTROL_PHY2_CLK, }; static const struct mxs_phy_data imx6sl_phy_data = { .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS | - MXS_PHY_NEED_IP_FIX, + MXS_PHY_NEED_IP_FIX | + MXS_PHY_HARDWARE_CONTROL_PHY2_CLK, }; static const struct mxs_phy_data vf610_phy_data = { @@ -174,11 +197,13 @@ static const struct mxs_phy_data vf610_phy_data = { }; static const struct mxs_phy_data imx6sx_phy_data = { - .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS, + .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS | + MXS_PHY_HARDWARE_CONTROL_PHY2_CLK, }; static const struct mxs_phy_data imx6ul_phy_data = { - .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS, + .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS | + MXS_PHY_HARDWARE_CONTROL_PHY2_CLK, }; static const struct mxs_phy_data imx7ulp_phy_data = { @@ -201,9 +226,11 @@ struct mxs_phy { struct clk *clk; const struct mxs_phy_data *data; struct regmap *regmap_anatop; + struct regmap *regmap_sim; int port_id; u32 tx_reg_set; u32 tx_reg_mask; + struct regulator *phy_3p0; }; static inline bool is_imx6q_phy(struct mxs_phy *mxs_phy) @@ -221,6 +248,11 @@ static inline bool is_imx7ulp_phy(struct mxs_phy *mxs_phy) return mxs_phy->data == &imx7ulp_phy_data; } +static inline bool is_imx6ul_phy(struct mxs_phy *mxs_phy) +{ + return mxs_phy->data == &imx6ul_phy_data; +} + /* * PHY needs some 32K cycles to switch from 32K clock to * bus (such as AHB/AXI, etc) clock. @@ -288,6 +320,16 @@ static int mxs_phy_hw_init(struct mxs_phy *mxs_phy) if (ret) goto disable_pll; + if (mxs_phy->phy_3p0) { + ret = regulator_enable(mxs_phy->phy_3p0); + if (ret) { + dev_err(mxs_phy->phy.dev, + "Failed to enable 3p0 regulator, ret=%d\n", + ret); + return ret; + } + } + /* Power up the PHY */ writel(0, base + HW_USBPHY_PWD); @@ -448,6 +490,9 @@ static void mxs_phy_shutdown(struct usb_phy *phy) if (is_imx7ulp_phy(mxs_phy)) mxs_phy_pll_enable(phy->io_priv, false); + if (mxs_phy->phy_3p0) + regulator_disable(mxs_phy->phy_3p0); + clk_disable_unprepare(mxs_phy->clk); } @@ -503,12 +548,19 @@ static int mxs_phy_suspend(struct usb_phy *x, int suspend) } writel(BM_USBPHY_CTRL_CLKGATE, x->io_priv + HW_USBPHY_CTRL_SET); - clk_disable_unprepare(mxs_phy->clk); + if (!(mxs_phy->port_id == 1 && + (mxs_phy->data->flags & + MXS_PHY_HARDWARE_CONTROL_PHY2_CLK))) + clk_disable_unprepare(mxs_phy->clk); } else { mxs_phy_clock_switch_delay(); - ret = clk_prepare_enable(mxs_phy->clk); - if (ret) - return ret; + if (!(mxs_phy->port_id == 1 && + (mxs_phy->data->flags & + MXS_PHY_HARDWARE_CONTROL_PHY2_CLK))) { + ret = clk_prepare_enable(mxs_phy->clk); + if (ret) + return ret; + } writel(BM_USBPHY_CTRL_CLKGATE, x->io_priv + HW_USBPHY_CTRL_CLR); writel(0, x->io_priv + HW_USBPHY_PWD); @@ -738,6 +790,17 @@ static int mxs_phy_probe(struct platform_device *pdev) } } + /* Currently, only imx7ulp has SIM module */ + if (of_get_property(np, "nxp,sim", NULL)) { + mxs_phy->regmap_sim = syscon_regmap_lookup_by_phandle + (np, "nxp,sim"); + if (IS_ERR(mxs_phy->regmap_sim)) { + dev_dbg(&pdev->dev, + "failed to find regmap for sim\n"); + return PTR_ERR(mxs_phy->regmap_sim); + } + } + /* Precompute which bits of the TX register are to be updated, if any */ if (!of_property_read_u32(np, "fsl,tx-cal-45-dn-ohms", &val) && val >= MXS_PHY_TX_CAL45_MIN && val <= MXS_PHY_TX_CAL45_MAX) { @@ -789,6 +852,17 @@ static int mxs_phy_probe(struct platform_device *pdev) mxs_phy->clk = clk; mxs_phy->data = of_device_get_match_data(&pdev->dev); + mxs_phy->phy_3p0 = devm_regulator_get(&pdev->dev, "phy-3p0"); + if (PTR_ERR(mxs_phy->phy_3p0) == -ENODEV) + /* not exist */ + mxs_phy->phy_3p0 = NULL; + else if (IS_ERR(mxs_phy->phy_3p0)) + return dev_err_probe(&pdev->dev, PTR_ERR(mxs_phy->phy_3p0), + "Getting regulator error\n"); + + if (mxs_phy->phy_3p0) + regulator_set_voltage(mxs_phy->phy_3p0, 3200000, 3200000); + platform_set_drvdata(pdev, mxs_phy); device_set_wakeup_capable(&pdev->dev, true); @@ -804,28 +878,58 @@ static void mxs_phy_remove(struct platform_device *pdev) } #ifdef CONFIG_PM_SLEEP +static void mxs_phy_wakeup_enable(struct mxs_phy *mxs_phy, bool on) +{ + u32 mask = USB_PHY_VLLS_WAKEUP_EN; + + /* If the SoCs don't have SIM, quit */ + if (!mxs_phy->regmap_sim) + return; + + if (on) { + regmap_update_bits(mxs_phy->regmap_sim, SIM_GPR1, mask, mask); + udelay(500); + } else { + regmap_update_bits(mxs_phy->regmap_sim, SIM_GPR1, mask, 0); + } +} + static void mxs_phy_enable_ldo_in_suspend(struct mxs_phy *mxs_phy, bool on) { - unsigned int reg = on ? ANADIG_ANA_MISC0_SET : ANADIG_ANA_MISC0_CLR; + unsigned int reg; + u32 value; /* If the SoCs don't have anatop, quit */ if (!mxs_phy->regmap_anatop) return; - if (is_imx6q_phy(mxs_phy)) + if (is_imx6q_phy(mxs_phy)) { + reg = on ? ANADIG_ANA_MISC0_SET : ANADIG_ANA_MISC0_CLR; regmap_write(mxs_phy->regmap_anatop, reg, BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG); - else if (is_imx6sl_phy(mxs_phy)) + } else if (is_imx6sl_phy(mxs_phy)) { + reg = on ? ANADIG_ANA_MISC0_SET : ANADIG_ANA_MISC0_CLR; regmap_write(mxs_phy->regmap_anatop, reg, BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG_SL); + } else if (is_imx6ul_phy(mxs_phy)) { + reg = on ? ANADIG_REG_1P1_SET : ANADIG_REG_1P1_CLR; + value = BM_ANADIG_REG_1P1_ENABLE_WEAK_LINREG | + BM_ANADIG_REG_1P1_TRACK_VDD_SOC_CAP; + if (mxs_phy_get_vbus_status(mxs_phy) && on) + regmap_write(mxs_phy->regmap_anatop, reg, value); + else if (!on) + regmap_write(mxs_phy->regmap_anatop, reg, value); + } } static int mxs_phy_system_suspend(struct device *dev) { struct mxs_phy *mxs_phy = dev_get_drvdata(dev); - if (device_may_wakeup(dev)) + if (device_may_wakeup(dev)) { mxs_phy_enable_ldo_in_suspend(mxs_phy, true); + mxs_phy_wakeup_enable(mxs_phy, true); + } return 0; } @@ -834,8 +938,10 @@ static int mxs_phy_system_resume(struct device *dev) { struct mxs_phy *mxs_phy = dev_get_drvdata(dev); - if (device_may_wakeup(dev)) + if (device_may_wakeup(dev)) { mxs_phy_enable_ldo_in_suspend(mxs_phy, false); + mxs_phy_wakeup_enable(mxs_phy, false); + } return 0; } diff --git a/drivers/usb/roles/class.c b/drivers/usb/roles/class.c index d7aa913ceb8a..c58a12c147f4 100644 --- a/drivers/usb/roles/class.c +++ b/drivers/usb/roles/class.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -21,6 +22,7 @@ static const struct class role_class = { struct usb_role_switch { struct device dev; + struct lock_class_key key; struct mutex lock; /* device lock*/ struct module *module; /* the module this device depends on */ enum usb_role role; @@ -326,6 +328,8 @@ static void usb_role_switch_release(struct device *dev) { struct usb_role_switch *sw = to_role_switch(dev); + mutex_destroy(&sw->lock); + lockdep_unregister_key(&sw->key); kfree(sw); } @@ -364,7 +368,8 @@ usb_role_switch_register(struct device *parent, if (!sw) return ERR_PTR(-ENOMEM); - mutex_init(&sw->lock); + lockdep_register_key(&sw->key); + mutex_init_with_key(&sw->lock, &sw->key); sw->allow_userspace_control = desc->allow_userspace_control; sw->usb2_port = desc->usb2_port; diff --git a/drivers/usb/serial/aircable.c b/drivers/usb/serial/aircable.c index a1df686c3066..aa517242d060 100644 --- a/drivers/usb/serial/aircable.c +++ b/drivers/usb/serial/aircable.c @@ -138,7 +138,6 @@ static void aircable_process_read_urb(struct urb *urb) static struct usb_serial_driver aircable_device = { .driver = { - .owner = THIS_MODULE, .name = "aircable", }, .id_table = id_table, diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index 67a07cc007f0..800b04fe37fa 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -599,7 +599,6 @@ static void ark3116_process_read_urb(struct urb *urb) static struct usb_serial_driver ark3116_device = { .driver = { - .owner = THIS_MODULE, .name = "ark3116", }, .id_table = id_table, diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index cf47ee4ae5d3..44f5b58beec9 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -66,7 +66,6 @@ MODULE_DEVICE_TABLE(usb, id_table); /* All of the device info needed for the serial converters */ static struct usb_serial_driver belkin_device = { .driver = { - .owner = THIS_MODULE, .name = "belkin", }, .description = "Belkin / Peracom / GoHubs USB Serial Adapter", diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index 0870c6533f80..02945ccf531d 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -837,7 +837,6 @@ static int ch341_reset_resume(struct usb_serial *serial) static struct usb_serial_driver ch341_device = { .driver = { - .owner = THIS_MODULE, .name = "ch341-uart", }, .id_table = id_table, diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 21fd26609252..c24101f0a07a 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -299,7 +299,6 @@ struct cp210x_port_private { static struct usb_serial_driver cp210x_device = { .driver = { - .owner = THIS_MODULE, .name = "cp210x", }, .id_table = id_table, diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index 51e5aac3bf4c..76dd8e7453b5 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -67,7 +67,6 @@ MODULE_DEVICE_TABLE(usb, id_table); static struct usb_serial_driver cyberjack_device = { .driver = { - .owner = THIS_MODULE, .name = "cyberjack", }, .description = "Reiner SCT Cyberjack USB card reader", diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index 1e0c028c5ec9..ce9134bb30f3 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -139,7 +139,6 @@ static void cypress_write_int_callback(struct urb *urb); static struct usb_serial_driver cypress_earthmate_device = { .driver = { - .owner = THIS_MODULE, .name = "earthmate", }, .description = "DeLorme Earthmate USB", @@ -166,7 +165,6 @@ static struct usb_serial_driver cypress_earthmate_device = { static struct usb_serial_driver cypress_hidcom_device = { .driver = { - .owner = THIS_MODULE, .name = "cyphidcom", }, .description = "HID->COM RS232 Adapter", @@ -192,7 +190,6 @@ static struct usb_serial_driver cypress_hidcom_device = { static struct usb_serial_driver cypress_ca42v2_device = { .driver = { - .owner = THIS_MODULE, .name = "nokiaca42v2", }, .description = "Nokia CA-42 V2 Adapter", diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index d1dea3850576..a06485965412 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -262,7 +262,6 @@ MODULE_DEVICE_TABLE(usb, id_table_combined); static struct usb_serial_driver digi_acceleport_2_device = { .driver = { - .owner = THIS_MODULE, .name = "digi_2", }, .description = "Digi 2 port USB adapter", @@ -293,7 +292,6 @@ static struct usb_serial_driver digi_acceleport_2_device = { static struct usb_serial_driver digi_acceleport_4_device = { .driver = { - .owner = THIS_MODULE, .name = "digi_4", }, .description = "Digi 4 port USB adapter", diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c index 405e835e93dd..aedcf7ebd269 100644 --- a/drivers/usb/serial/empeg.c +++ b/drivers/usb/serial/empeg.c @@ -43,7 +43,6 @@ MODULE_DEVICE_TABLE(usb, id_table); static struct usb_serial_driver empeg_device = { .driver = { - .owner = THIS_MODULE, .name = "empeg", }, .id_table = id_table, diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index 5f7a46bcace6..530b77fc2f78 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -967,7 +967,6 @@ static int f81232_resume(struct usb_serial *serial) static struct usb_serial_driver f81232_device = { .driver = { - .owner = THIS_MODULE, .name = "f81232", }, .id_table = f81232_id_table, @@ -994,7 +993,6 @@ static struct usb_serial_driver f81232_device = { static struct usb_serial_driver f81534a_device = { .driver = { - .owner = THIS_MODULE, .name = "f81534a", }, .id_table = f81534a_id_table, diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index ef126cd3d94f..685930ac8383 100644 --- a/drivers/usb/serial/f81534.c +++ b/drivers/usb/serial/f81534.c @@ -1538,7 +1538,6 @@ static int f81534_resume(struct usb_serial *serial) static struct usb_serial_driver f81534_device = { .driver = { - .owner = THIS_MODULE, .name = "f81534", }, .description = DRIVER_DESC, diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 76a04ab41100..c6f17d732b95 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -2871,7 +2871,6 @@ static int ftdi_ioctl(struct tty_struct *tty, static struct usb_serial_driver ftdi_device = { .driver = { - .owner = THIS_MODULE, .name = "ftdi_sio", .dev_groups = ftdi_groups, }, diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index 6d6ec7eed87c..b97ba8ed6801 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -1412,7 +1412,6 @@ static void garmin_port_remove(struct usb_serial_port *port) /* All of the device info needed */ static struct usb_serial_driver garmin_device = { .driver = { - .owner = THIS_MODULE, .name = "garmin_gps", }, .description = "Garmin GPS usb/tty", diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 15b6dee3a8e5..757f0a586ddb 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -63,7 +63,6 @@ static int usb_serial_generic_calc_num_ports(struct usb_serial *serial, static struct usb_serial_driver usb_serial_generic_device = { .driver = { - .owner = THIS_MODULE, .name = "generic", }, .id_table = generic_device_ids, diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index abe4bbb0ac65..c7d6b5e3f898 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -2978,7 +2978,6 @@ static void edge_port_remove(struct usb_serial_port *port) static struct usb_serial_driver edgeport_2port_device = { .driver = { - .owner = THIS_MODULE, .name = "edgeport_2", }, .description = "Edgeport 2 port adapter", @@ -3013,7 +3012,6 @@ static struct usb_serial_driver edgeport_2port_device = { static struct usb_serial_driver edgeport_4port_device = { .driver = { - .owner = THIS_MODULE, .name = "edgeport_4", }, .description = "Edgeport 4 port adapter", @@ -3048,7 +3046,6 @@ static struct usb_serial_driver edgeport_4port_device = { static struct usb_serial_driver edgeport_8port_device = { .driver = { - .owner = THIS_MODULE, .name = "edgeport_8", }, .description = "Edgeport 8 port adapter", @@ -3083,7 +3080,6 @@ static struct usb_serial_driver edgeport_8port_device = { static struct usb_serial_driver epic_device = { .driver = { - .owner = THIS_MODULE, .name = "epic", }, .description = "EPiC device", diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index 7a3a6e539456..7d0584b2a234 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -2670,7 +2670,6 @@ static int edge_resume(struct usb_serial *serial) static struct usb_serial_driver edgeport_1port_device = { .driver = { - .owner = THIS_MODULE, .name = "edgeport_ti_1", }, .description = "Edgeport TI 1 port adapter", @@ -2708,7 +2707,6 @@ static struct usb_serial_driver edgeport_1port_device = { static struct usb_serial_driver edgeport_2port_device = { .driver = { - .owner = THIS_MODULE, .name = "edgeport_ti_2", }, .description = "Edgeport TI 2 port adapter", diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c index e11441bac44f..3c6a9b9b9c2b 100644 --- a/drivers/usb/serial/ipaq.c +++ b/drivers/usb/serial/ipaq.c @@ -496,7 +496,6 @@ MODULE_DEVICE_TABLE(usb, ipaq_id_table); /* All of the device info needed for the Compaq iPAQ */ static struct usb_serial_driver ipaq_device = { .driver = { - .owner = THIS_MODULE, .name = "ipaq", }, .description = "PocketPC PDA", diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c index d04c7cc5c1c2..b1e672c2f423 100644 --- a/drivers/usb/serial/ipw.c +++ b/drivers/usb/serial/ipw.c @@ -285,7 +285,6 @@ static void ipw_close(struct usb_serial_port *port) static struct usb_serial_driver ipw_device = { .driver = { - .owner = THIS_MODULE, .name = "ipw", }, .description = "IPWireless converter", diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index 82f108134e6f..a106b71e698f 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -71,7 +71,6 @@ MODULE_DEVICE_TABLE(usb, ir_id_table); static struct usb_serial_driver ir_device = { .driver = { - .owner = THIS_MODULE, .name = "ir-usb", }, .description = "IR Dongle", diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index 77cba71bcccb..c21dcc9b6f05 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -1157,7 +1157,6 @@ static int iuu_remove_sysfs_attrs(struct usb_serial_port *port) static struct usb_serial_driver iuu_device = { .driver = { - .owner = THIS_MODULE, .name = "iuu_phoenix", }, .id_table = id_table, diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 0a783985197c..9129e0282c24 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -3001,7 +3001,6 @@ static void keyspan_port_remove(struct usb_serial_port *port) /* Structs for the devices, pre and post renumeration. */ static struct usb_serial_driver keyspan_pre_device = { .driver = { - .owner = THIS_MODULE, .name = "keyspan_no_firm", }, .description = "Keyspan - (without firmware)", @@ -3012,7 +3011,6 @@ static struct usb_serial_driver keyspan_pre_device = { static struct usb_serial_driver keyspan_1port_device = { .driver = { - .owner = THIS_MODULE, .name = "keyspan_1", }, .description = "Keyspan 1 port adapter", @@ -3036,7 +3034,6 @@ static struct usb_serial_driver keyspan_1port_device = { static struct usb_serial_driver keyspan_2port_device = { .driver = { - .owner = THIS_MODULE, .name = "keyspan_2", }, .description = "Keyspan 2 port adapter", @@ -3060,7 +3057,6 @@ static struct usb_serial_driver keyspan_2port_device = { static struct usb_serial_driver keyspan_4port_device = { .driver = { - .owner = THIS_MODULE, .name = "keyspan_4", }, .description = "Keyspan 4 port adapter", diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index 0eef358b314a..e98b479593d3 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -676,7 +676,6 @@ static void keyspan_pda_port_remove(struct usb_serial_port *port) static struct usb_serial_driver keyspan_pda_fake_device = { .driver = { - .owner = THIS_MODULE, .name = "keyspan_pda_pre", }, .description = "Keyspan PDA - (prerenumeration)", @@ -687,7 +686,6 @@ static struct usb_serial_driver keyspan_pda_fake_device = { static struct usb_serial_driver keyspan_pda_device = { .driver = { - .owner = THIS_MODULE, .name = "keyspan_pda", }, .description = "Keyspan PDA", diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index 394b3189e003..a2c0bebc041f 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -75,7 +75,6 @@ MODULE_DEVICE_TABLE(usb, id_table); static struct usb_serial_driver kl5kusb105d_device = { .driver = { - .owner = THIS_MODULE, .name = "kl5kusb105d", }, .description = "KL5KUSB105D / PalmConnect", diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index 5e775f68fcb8..464433be2034 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -77,7 +77,6 @@ MODULE_DEVICE_TABLE(usb, id_table); static struct usb_serial_driver kobil_device = { .driver = { - .owner = THIS_MODULE, .name = "kobil", }, .description = "KOBIL USB smart card terminal", @@ -156,8 +155,7 @@ static void kobil_init_termios(struct tty_struct *tty) { /* Default to echo off and other sane device settings */ tty->termios.c_lflag = 0; - tty->termios.c_iflag &= ~(ISIG | ICANON | ECHO | IEXTEN | XCASE); - tty->termios.c_iflag |= IGNBRK | IGNPAR | IXOFF; + tty->termios.c_iflag = IGNBRK | IGNPAR | IXOFF; /* do NOT translate CR to CR-NL (0x0A -> 0x0A 0x0D) */ tty->termios.c_oflag &= ~ONLCR; } diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 6570c8817a80..e5a139ed5d90 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -69,7 +69,6 @@ MODULE_DEVICE_TABLE(usb, id_table); static struct usb_serial_driver mct_u232_device = { .driver = { - .owner = THIS_MODULE, .name = "mct_u232", }, .description = "MCT U232", diff --git a/drivers/usb/serial/metro-usb.c b/drivers/usb/serial/metro-usb.c index 30ab565e0738..028878292901 100644 --- a/drivers/usb/serial/metro-usb.c +++ b/drivers/usb/serial/metro-usb.c @@ -341,7 +341,6 @@ static void metrousb_unthrottle(struct tty_struct *tty) static struct usb_serial_driver metrousb_device = { .driver = { - .owner = THIS_MODULE, .name = "metro-usb", }, .description = "Metrologic USB to Serial", diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index 23544074eb1c..e59bfa7c8030 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -1724,7 +1724,6 @@ static void mos7720_port_remove(struct usb_serial_port *port) static struct usb_serial_driver moschip7720_2port_driver = { .driver = { - .owner = THIS_MODULE, .name = "moschip7720", }, .description = "Moschip 2 port adapter", diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 85697466b147..ca3da79afd23 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -1782,7 +1782,6 @@ static int mos7840_resume(struct usb_serial *serial) static struct usb_serial_driver moschip7840_4port_device = { .driver = { - .owner = THIS_MODULE, .name = "mos7840", }, .description = DRIVER_DESC, diff --git a/drivers/usb/serial/mxuport.c b/drivers/usb/serial/mxuport.c index 942cb0153423..57e4f2b215d8 100644 --- a/drivers/usb/serial/mxuport.c +++ b/drivers/usb/serial/mxuport.c @@ -1278,7 +1278,6 @@ static int mxuport_resume(struct usb_serial *serial) static struct usb_serial_driver mxuport_device = { .driver = { - .owner = THIS_MODULE, .name = "mxuport", }, .description = "MOXA UPort", diff --git a/drivers/usb/serial/navman.c b/drivers/usb/serial/navman.c index 82791fd67c46..4d57a5902292 100644 --- a/drivers/usb/serial/navman.c +++ b/drivers/usb/serial/navman.c @@ -95,7 +95,6 @@ static int navman_write(struct tty_struct *tty, struct usb_serial_port *port, static struct usb_serial_driver navman_device = { .driver = { - .owner = THIS_MODULE, .name = "navman", }, .id_table = id_table, diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index 41f1b872d277..397ebd5a3e74 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -49,7 +49,6 @@ MODULE_DEVICE_TABLE(usb, id_table); static struct usb_serial_driver zyxel_omninet_device = { .driver = { - .owner = THIS_MODULE, .name = "omninet", }, .description = "ZyXEL - omni.net usb", diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c index e31a6d77da3a..1ee84ccc4bbd 100644 --- a/drivers/usb/serial/opticon.c +++ b/drivers/usb/serial/opticon.c @@ -375,7 +375,6 @@ static void opticon_port_remove(struct usb_serial_port *port) static struct usb_serial_driver opticon_device = { .driver = { - .owner = THIS_MODULE, .name = "opticon", }, .id_table = id_table, diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 176f38750ad5..eb0731992ca9 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -2381,7 +2381,6 @@ MODULE_DEVICE_TABLE(usb, option_ids); static struct usb_serial_driver option_1port_device = { .driver = { - .owner = THIS_MODULE, .name = "option1", }, .description = "GSM modem (1-port)", diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index fa07f6ff9ecc..24068368892c 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -138,7 +138,6 @@ static void oti6858_port_remove(struct usb_serial_port *port); /* device info */ static struct usb_serial_driver oti6858_device = { .driver = { - .owner = THIS_MODULE, .name = "oti6858", }, .id_table = id_table, diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index d93f5d584557..ab48f8875249 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -118,6 +118,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(SMART_VENDOR_ID, SMART_PRODUCT_ID) }, { USB_DEVICE(AT_VENDOR_ID, AT_VTKIT3_PRODUCT_ID) }, { USB_DEVICE(IBM_VENDOR_ID, IBM_PRODUCT_ID) }, + { USB_DEVICE(MACROSILICON_VENDOR_ID, MACROSILICON_MS3020_PRODUCT_ID) }, { } /* Terminating entry */ }; @@ -1234,7 +1235,6 @@ static void pl2303_process_read_urb(struct urb *urb) static struct usb_serial_driver pl2303_device = { .driver = { - .owner = THIS_MODULE, .name = "pl2303", }, .id_table = id_table, diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index 732f9b13ad5d..d60eda7f6eda 100644 --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -171,3 +171,7 @@ /* Allied Telesis VT-Kit3 */ #define AT_VENDOR_ID 0x0caa #define AT_VTKIT3_PRODUCT_ID 0x3001 + +/* Macrosilicon MS3020 */ +#define MACROSILICON_VENDOR_ID 0x345f +#define MACROSILICON_MS3020_PRODUCT_ID 0x3020 diff --git a/drivers/usb/serial/qcaux.c b/drivers/usb/serial/qcaux.c index 015bb7c5d19d..485ec5b07122 100644 --- a/drivers/usb/serial/qcaux.c +++ b/drivers/usb/serial/qcaux.c @@ -72,7 +72,6 @@ MODULE_DEVICE_TABLE(usb, id_table); static struct usb_serial_driver qcaux_device = { .driver = { - .owner = THIS_MODULE, .name = "qcaux", }, .id_table = id_table, diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index 703a9c563557..c7de9585feb2 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -454,7 +454,6 @@ static void qc_release(struct usb_serial *serial) static struct usb_serial_driver qcdevice = { .driver = { - .owner = THIS_MODULE, .name = "qcserial", }, .description = "Qualcomm USB modem", diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index 821f25e52ec2..4167a45d1be3 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -924,7 +924,6 @@ write_out: static struct usb_serial_driver qt2_device = { .driver = { - .owner = THIS_MODULE, .name = "quatech-serial", }, .description = DRIVER_DESC, diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c index 6accbecb6318..238b54993446 100644 --- a/drivers/usb/serial/safe_serial.c +++ b/drivers/usb/serial/safe_serial.c @@ -284,7 +284,6 @@ static int safe_startup(struct usb_serial *serial) static struct usb_serial_driver safe_device = { .driver = { - .owner = THIS_MODULE, .name = "safe_serial", }, .id_table = id_table, diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index 353b2549eaa8..64a2e0bb5723 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -1021,7 +1021,6 @@ static int sierra_resume(struct usb_serial *serial) static struct usb_serial_driver sierra_device = { .driver = { - .owner = THIS_MODULE, .name = "sierra", }, .description = "Sierra USB modem", diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index 6b294bf8bc43..11077beb7232 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -452,7 +452,6 @@ static int spcp8x5_tiocmget(struct tty_struct *tty) static struct usb_serial_driver spcp8x5_device = { .driver = { - .owner = THIS_MODULE, .name = "SPCP8x5", }, .id_table = id_table, diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c index 1e1888b66305..df21009bdf42 100644 --- a/drivers/usb/serial/ssu100.c +++ b/drivers/usb/serial/ssu100.c @@ -500,7 +500,6 @@ static void ssu100_process_read_urb(struct urb *urb) static struct usb_serial_driver ssu100_device = { .driver = { - .owner = THIS_MODULE, .name = "ssu100", }, .description = DRIVER_DESC, diff --git a/drivers/usb/serial/symbolserial.c b/drivers/usb/serial/symbolserial.c index 9aabb087f733..58962bcbf9ba 100644 --- a/drivers/usb/serial/symbolserial.c +++ b/drivers/usb/serial/symbolserial.c @@ -169,7 +169,6 @@ static void symbol_port_remove(struct usb_serial_port *port) static struct usb_serial_driver symbol_device = { .driver = { - .owner = THIS_MODULE, .name = "symbol", }, .id_table = id_table, diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 0fba25abf671..a0c244bc77c0 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -417,7 +417,6 @@ static const struct usb_device_id ti_id_table_combined[] = { static struct usb_serial_driver ti_1port_device = { .driver = { - .owner = THIS_MODULE, .name = "ti_usb_3410_5052_1", }, .description = "TI USB 3410 1 port adapter", @@ -450,7 +449,6 @@ static struct usb_serial_driver ti_1port_device = { static struct usb_serial_driver ti_2port_device = { .driver = { - .owner = THIS_MODULE, .name = "ti_usb_3410_5052_2", }, .description = "TI USB 5052 2 port adapter", diff --git a/drivers/usb/serial/upd78f0730.c b/drivers/usb/serial/upd78f0730.c index 46952182e04f..15a17bf111f1 100644 --- a/drivers/usb/serial/upd78f0730.c +++ b/drivers/usb/serial/upd78f0730.c @@ -407,7 +407,6 @@ static void upd78f0730_close(struct usb_serial_port *port) static struct usb_serial_driver upd78f0730_device = { .driver = { - .owner = THIS_MODULE, .name = "upd78f0730", }, .id_table = id_table, diff --git a/drivers/usb/serial/usb-serial-simple.c b/drivers/usb/serial/usb-serial-simple.c index 82f4f0b992aa..2c12449ff60c 100644 --- a/drivers/usb/serial/usb-serial-simple.c +++ b/drivers/usb/serial/usb-serial-simple.c @@ -24,7 +24,6 @@ static const struct usb_device_id vendor##_id_table[] = { \ }; \ static struct usb_serial_driver vendor##_device = { \ .driver = { \ - .owner = THIS_MODULE, \ .name = #vendor, \ }, \ .id_table = vendor##_id_table, \ diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index f1e91eb7f8a4..df6a2ae0bf42 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -1459,17 +1459,18 @@ static void usb_serial_deregister(struct usb_serial_driver *device) } /** - * usb_serial_register_drivers - register drivers for a usb-serial module + * __usb_serial_register_drivers - register drivers for a usb-serial module * @serial_drivers: NULL-terminated array of pointers to drivers to be registered + * @owner: owning module * @name: name of the usb_driver for this set of @serial_drivers * @id_table: list of all devices this @serial_drivers set binds to * * Registers all the drivers in the @serial_drivers array, and dynamically * creates a struct usb_driver with the name @name and id_table of @id_table. */ -int usb_serial_register_drivers(struct usb_serial_driver *const serial_drivers[], - const char *name, - const struct usb_device_id *id_table) +int __usb_serial_register_drivers(struct usb_serial_driver *const serial_drivers[], + struct module *owner, const char *name, + const struct usb_device_id *id_table) { int rc; struct usb_driver *udriver; @@ -1514,6 +1515,7 @@ int usb_serial_register_drivers(struct usb_serial_driver *const serial_drivers[] for (sd = serial_drivers; *sd; ++sd) { (*sd)->usb_driver = udriver; + (*sd)->driver.owner = owner; rc = usb_serial_register(*sd); if (rc) goto err_deregister_drivers; @@ -1532,7 +1534,7 @@ err_free_driver: kfree(udriver); return rc; } -EXPORT_SYMBOL_GPL(usb_serial_register_drivers); +EXPORT_SYMBOL_GPL(__usb_serial_register_drivers); /** * usb_serial_deregister_drivers - deregister drivers for a usb-serial module diff --git a/drivers/usb/serial/usb_debug.c b/drivers/usb/serial/usb_debug.c index 61a8425b7762..ec9fff794b36 100644 --- a/drivers/usb/serial/usb_debug.c +++ b/drivers/usb/serial/usb_debug.c @@ -83,7 +83,6 @@ static void usb_debug_init_termios(struct tty_struct *tty) static struct usb_serial_driver debug_device = { .driver = { - .owner = THIS_MODULE, .name = "debug", }, .id_table = id_table, @@ -96,7 +95,6 @@ static struct usb_serial_driver debug_device = { static struct usb_serial_driver dbc_device = { .driver = { - .owner = THIS_MODULE, .name = "xhci_dbc", }, .id_table = dbc_id_table, diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index 4412834db21c..062a38fe0c1c 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -161,7 +161,6 @@ MODULE_DEVICE_TABLE(usb, id_table_combined); and Palm 4.0 devices */ static struct usb_serial_driver handspring_device = { .driver = { - .owner = THIS_MODULE, .name = "visor", }, .description = "Handspring Visor / Palm OS", @@ -180,7 +179,6 @@ static struct usb_serial_driver handspring_device = { /* All of the device info needed for the Clie UX50, TH55 Palm 5.0 devices */ static struct usb_serial_driver clie_5_device = { .driver = { - .owner = THIS_MODULE, .name = "clie_5", }, .description = "Sony Clie 5.0", @@ -200,7 +198,6 @@ static struct usb_serial_driver clie_5_device = { /* device info for the Sony Clie OS version 3.5 */ static struct usb_serial_driver clie_3_5_device = { .driver = { - .owner = THIS_MODULE, .name = "clie_3.5", }, .description = "Sony Clie 3.5", diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index ca48e90a8e81..009faeb2ef55 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -91,7 +91,6 @@ static int whiteheat_break_ctl(struct tty_struct *tty, int break_state); static struct usb_serial_driver whiteheat_fake_device = { .driver = { - .owner = THIS_MODULE, .name = "whiteheatnofirm", }, .description = "Connect Tech - WhiteHEAT - (prerenumeration)", @@ -103,7 +102,6 @@ static struct usb_serial_driver whiteheat_fake_device = { static struct usb_serial_driver whiteheat_device = { .driver = { - .owner = THIS_MODULE, .name = "whiteheat", }, .description = "Connect Tech - WhiteHEAT", diff --git a/drivers/usb/serial/wishbone-serial.c b/drivers/usb/serial/wishbone-serial.c index ff4092f9b33c..670d573f6b63 100644 --- a/drivers/usb/serial/wishbone-serial.c +++ b/drivers/usb/serial/wishbone-serial.c @@ -70,7 +70,6 @@ static void wishbone_serial_close(struct usb_serial_port *port) static struct usb_serial_driver wishbone_serial_device = { .driver = { - .owner = THIS_MODULE, .name = "wishbone_serial", }, .id_table = id_table, diff --git a/drivers/usb/serial/xr_serial.c b/drivers/usb/serial/xr_serial.c index 1d9a12628f81..4186e791b384 100644 --- a/drivers/usb/serial/xr_serial.c +++ b/drivers/usb/serial/xr_serial.c @@ -1082,7 +1082,6 @@ MODULE_DEVICE_TABLE(usb, id_table); static struct usb_serial_driver xr_device = { .driver = { - .owner = THIS_MODULE, .name = "xr_serial", }, .id_table = id_table, diff --git a/drivers/usb/serial/xsens_mt.c b/drivers/usb/serial/xsens_mt.c index cf262c9a9638..382b3698c1d5 100644 --- a/drivers/usb/serial/xsens_mt.c +++ b/drivers/usb/serial/xsens_mt.c @@ -49,7 +49,6 @@ static int xsens_mt_probe(struct usb_serial *serial, static struct usb_serial_driver xsens_mt_device = { .driver = { - .owner = THIS_MODULE, .name = "xsens_mt", }, .id_table = id_table, diff --git a/drivers/usb/storage/alauda.c b/drivers/usb/storage/alauda.c index 40d34cc28344..a9d3c58ce7d9 100644 --- a/drivers/usb/storage/alauda.c +++ b/drivers/usb/storage/alauda.c @@ -132,7 +132,7 @@ static int init_alauda(struct us_data *us); { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ .driver_info = (flags) } -static struct usb_device_id alauda_usb_ids[] = { +static const struct usb_device_id alauda_usb_ids[] = { # include "unusual_alauda.h" { } /* Terminating entry */ }; @@ -154,7 +154,7 @@ MODULE_DEVICE_TABLE(usb, alauda_usb_ids); .initFunction = init_function, \ } -static struct us_unusual_dev alauda_unusual_dev_list[] = { +static const struct us_unusual_dev alauda_unusual_dev_list[] = { # include "unusual_alauda.h" { } /* Terminating entry */ }; diff --git a/drivers/usb/storage/cypress_atacb.c b/drivers/usb/storage/cypress_atacb.c index 98b3ec352a13..30dfd0082474 100644 --- a/drivers/usb/storage/cypress_atacb.c +++ b/drivers/usb/storage/cypress_atacb.c @@ -33,7 +33,7 @@ MODULE_IMPORT_NS(USB_STORAGE); { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ .driver_info = (flags) } -static struct usb_device_id cypress_usb_ids[] = { +static const struct usb_device_id cypress_usb_ids[] = { # include "unusual_cypress.h" { } /* Terminating entry */ }; @@ -55,7 +55,7 @@ MODULE_DEVICE_TABLE(usb, cypress_usb_ids); .initFunction = init_function, \ } -static struct us_unusual_dev cypress_unusual_dev_list[] = { +static const struct us_unusual_dev cypress_unusual_dev_list[] = { # include "unusual_cypress.h" { } /* Terminating entry */ }; diff --git a/drivers/usb/storage/datafab.c b/drivers/usb/storage/datafab.c index bcc4a2fad863..3ea5601d16b8 100644 --- a/drivers/usb/storage/datafab.c +++ b/drivers/usb/storage/datafab.c @@ -80,7 +80,7 @@ static int datafab_determine_lun(struct us_data *us, { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ .driver_info = (flags) } -static struct usb_device_id datafab_usb_ids[] = { +static const struct usb_device_id datafab_usb_ids[] = { # include "unusual_datafab.h" { } /* Terminating entry */ }; @@ -102,7 +102,7 @@ MODULE_DEVICE_TABLE(usb, datafab_usb_ids); .initFunction = init_function, \ } -static struct us_unusual_dev datafab_unusual_dev_list[] = { +static const struct us_unusual_dev datafab_unusual_dev_list[] = { # include "unusual_datafab.h" { } /* Terminating entry */ }; diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c index 97c66c0d91f4..a4bfbecbf16c 100644 --- a/drivers/usb/storage/ene_ub6250.c +++ b/drivers/usb/storage/ene_ub6250.c @@ -43,7 +43,7 @@ MODULE_FIRMWARE(MS_RW_FIRMWARE); { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ .driver_info = (flags)} -static struct usb_device_id ene_ub6250_usb_ids[] = { +static const struct usb_device_id ene_ub6250_usb_ids[] = { # include "unusual_ene_ub6250.h" { } /* Terminating entry */ }; @@ -65,7 +65,7 @@ MODULE_DEVICE_TABLE(usb, ene_ub6250_usb_ids); .initFunction = init_function, \ } -static struct us_unusual_dev ene_ub6250_unusual_dev_list[] = { +static const struct us_unusual_dev ene_ub6250_unusual_dev_list[] = { # include "unusual_ene_ub6250.h" { } /* Terminating entry */ }; @@ -1484,7 +1484,7 @@ static int ms_scsi_mode_sense(struct us_data *us, struct scsi_cmnd *srb) static int ms_scsi_read_capacity(struct us_data *us, struct scsi_cmnd *srb) { u32 bl_num; - u16 bl_len; + u32 bl_len; unsigned int offset = 0; unsigned char buf[8]; struct scatterlist *sg = NULL; diff --git a/drivers/usb/storage/freecom.c b/drivers/usb/storage/freecom.c index c3ce51c2dabd..cab27ba7a32a 100644 --- a/drivers/usb/storage/freecom.c +++ b/drivers/usb/storage/freecom.c @@ -119,7 +119,7 @@ static int init_freecom(struct us_data *us); { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ .driver_info = (flags) } -static struct usb_device_id freecom_usb_ids[] = { +static const struct usb_device_id freecom_usb_ids[] = { # include "unusual_freecom.h" { } /* Terminating entry */ }; @@ -141,7 +141,7 @@ MODULE_DEVICE_TABLE(usb, freecom_usb_ids); .initFunction = init_function, \ } -static struct us_unusual_dev freecom_unusual_dev_list[] = { +static const struct us_unusual_dev freecom_unusual_dev_list[] = { # include "unusual_freecom.h" { } /* Terminating entry */ }; diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c index 300aeef160e7..f2254eb3c0d7 100644 --- a/drivers/usb/storage/isd200.c +++ b/drivers/usb/storage/isd200.c @@ -67,7 +67,7 @@ static int isd200_Initialization(struct us_data *us); { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ .driver_info = (flags) } -static struct usb_device_id isd200_usb_ids[] = { +static const struct usb_device_id isd200_usb_ids[] = { # include "unusual_isd200.h" { } /* Terminating entry */ }; @@ -89,7 +89,7 @@ MODULE_DEVICE_TABLE(usb, isd200_usb_ids); .initFunction = init_function, \ } -static struct us_unusual_dev isd200_unusual_dev_list[] = { +static const struct us_unusual_dev isd200_unusual_dev_list[] = { # include "unusual_isd200.h" { } /* Terminating entry */ }; diff --git a/drivers/usb/storage/jumpshot.c b/drivers/usb/storage/jumpshot.c index 229bf0c1afc9..0e71a8f33c2b 100644 --- a/drivers/usb/storage/jumpshot.c +++ b/drivers/usb/storage/jumpshot.c @@ -62,7 +62,7 @@ MODULE_IMPORT_NS(USB_STORAGE); { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ .driver_info = (flags) } -static struct usb_device_id jumpshot_usb_ids[] = { +static const struct usb_device_id jumpshot_usb_ids[] = { # include "unusual_jumpshot.h" { } /* Terminating entry */ }; @@ -84,7 +84,7 @@ MODULE_DEVICE_TABLE(usb, jumpshot_usb_ids); .initFunction = init_function, \ } -static struct us_unusual_dev jumpshot_unusual_dev_list[] = { +static const struct us_unusual_dev jumpshot_unusual_dev_list[] = { # include "unusual_jumpshot.h" { } /* Terminating entry */ }; diff --git a/drivers/usb/storage/karma.c b/drivers/usb/storage/karma.c index 38ddfedef629..d6a5e54f2ca8 100644 --- a/drivers/usb/storage/karma.c +++ b/drivers/usb/storage/karma.c @@ -51,7 +51,7 @@ static int rio_karma_init(struct us_data *us); { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ .driver_info = (flags) } -static struct usb_device_id karma_usb_ids[] = { +static const struct usb_device_id karma_usb_ids[] = { # include "unusual_karma.h" { } /* Terminating entry */ }; @@ -73,7 +73,7 @@ MODULE_DEVICE_TABLE(usb, karma_usb_ids); .initFunction = init_function, \ } -static struct us_unusual_dev karma_unusual_dev_list[] = { +static const struct us_unusual_dev karma_unusual_dev_list[] = { # include "unusual_karma.h" { } /* Terminating entry */ }; diff --git a/drivers/usb/storage/onetouch.c b/drivers/usb/storage/onetouch.c index 01f3c2779ccf..f97cf6cadb8e 100644 --- a/drivers/usb/storage/onetouch.c +++ b/drivers/usb/storage/onetouch.c @@ -55,7 +55,7 @@ struct usb_onetouch { { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ .driver_info = (flags) } -static struct usb_device_id onetouch_usb_ids[] = { +static const struct usb_device_id onetouch_usb_ids[] = { # include "unusual_onetouch.h" { } /* Terminating entry */ }; @@ -77,7 +77,7 @@ MODULE_DEVICE_TABLE(usb, onetouch_usb_ids); .initFunction = init_function, \ } -static struct us_unusual_dev onetouch_unusual_dev_list[] = { +static const struct us_unusual_dev onetouch_unusual_dev_list[] = { # include "unusual_onetouch.h" { } /* Terminating entry */ }; diff --git a/drivers/usb/storage/sddr09.c b/drivers/usb/storage/sddr09.c index 51bcd4a43690..03d1b9c69ea1 100644 --- a/drivers/usb/storage/sddr09.c +++ b/drivers/usb/storage/sddr09.c @@ -63,7 +63,7 @@ static int usb_stor_sddr09_init(struct us_data *us); { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ .driver_info = (flags) } -static struct usb_device_id sddr09_usb_ids[] = { +static const struct usb_device_id sddr09_usb_ids[] = { # include "unusual_sddr09.h" { } /* Terminating entry */ }; @@ -85,7 +85,7 @@ MODULE_DEVICE_TABLE(usb, sddr09_usb_ids); .initFunction = init_function, \ } -static struct us_unusual_dev sddr09_unusual_dev_list[] = { +static const struct us_unusual_dev sddr09_unusual_dev_list[] = { # include "unusual_sddr09.h" { } /* Terminating entry */ }; diff --git a/drivers/usb/storage/sddr55.c b/drivers/usb/storage/sddr55.c index 0aa079405d23..b8227478a7ad 100644 --- a/drivers/usb/storage/sddr55.c +++ b/drivers/usb/storage/sddr55.c @@ -40,7 +40,7 @@ MODULE_IMPORT_NS(USB_STORAGE); { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ .driver_info = (flags) } -static struct usb_device_id sddr55_usb_ids[] = { +static const struct usb_device_id sddr55_usb_ids[] = { # include "unusual_sddr55.h" { } /* Terminating entry */ }; @@ -62,7 +62,7 @@ MODULE_DEVICE_TABLE(usb, sddr55_usb_ids); .initFunction = init_function, \ } -static struct us_unusual_dev sddr55_unusual_dev_list[] = { +static const struct us_unusual_dev sddr55_unusual_dev_list[] = { # include "unusual_sddr55.h" { } /* Terminating entry */ }; diff --git a/drivers/usb/storage/shuttle_usbat.c b/drivers/usb/storage/shuttle_usbat.c index f0d0ca37163d..e7c224b7c464 100644 --- a/drivers/usb/storage/shuttle_usbat.c +++ b/drivers/usb/storage/shuttle_usbat.c @@ -162,7 +162,7 @@ static int init_usbat_flash(struct us_data *us); { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ .driver_info = (flags) } -static struct usb_device_id usbat_usb_ids[] = { +static const struct usb_device_id usbat_usb_ids[] = { # include "unusual_usbat.h" { } /* Terminating entry */ }; @@ -184,7 +184,7 @@ MODULE_DEVICE_TABLE(usb, usbat_usb_ids); .initFunction = init_function, \ } -static struct us_unusual_dev usbat_unusual_dev_list[] = { +static const struct us_unusual_dev usbat_unusual_dev_list[] = { # include "unusual_usbat.h" { } /* Terminating entry */ }; diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index c223b4dc1b19..03043d567fa1 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -927,7 +927,7 @@ static const struct scsi_host_template uas_host_template = { { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ .driver_info = (flags) } -static struct usb_device_id uas_usb_ids[] = { +static const struct usb_device_id uas_usb_ids[] = { # include "unusual_uas.h" { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, USB_SC_SCSI, USB_PR_BULK) }, { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, USB_SC_SCSI, USB_PR_UAS) }, diff --git a/drivers/usb/typec/anx7411.c b/drivers/usb/typec/anx7411.c index 31e3e9544bc0..d1e7c487ddfb 100644 --- a/drivers/usb/typec/anx7411.c +++ b/drivers/usb/typec/anx7411.c @@ -6,6 +6,7 @@ * Copyright(c) 2022, Analogix Semiconductor. All rights reserved. * */ +#include #include #include #include @@ -884,8 +885,8 @@ static void anx7411_chip_standby(struct anx7411_data *ctx) OCM_RESET); ret |= anx7411_reg_write(ctx->tcpc_client, ANALOG_CTRL_10, 0x80); /* Set TCPC to RD and DRP enable */ - cc1 = TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT; - cc2 = TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT; + cc1 = FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RD); + cc2 = FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RD); ret |= anx7411_reg_write(ctx->tcpc_client, TCPC_ROLE_CTRL, TCPC_ROLE_CTRL_DRP | cc1 | cc2); @@ -1571,6 +1572,7 @@ static const struct of_device_id anx_match_table[] = { {.compatible = "analogix,anx7411",}, {}, }; +MODULE_DEVICE_TABLE(of, anx_match_table); static struct i2c_driver anx7411_driver = { .driver = { diff --git a/drivers/usb/typec/tcpm/maxim_contaminant.c b/drivers/usb/typec/tcpm/maxim_contaminant.c index f8504a90da26..22163d8f9eb0 100644 --- a/drivers/usb/typec/tcpm/maxim_contaminant.c +++ b/drivers/usb/typec/tcpm/maxim_contaminant.c @@ -5,6 +5,7 @@ * USB-C module to reduce wakeups due to contaminants. */ +#include #include #include #include @@ -45,14 +46,9 @@ enum fladc_select { #define READ1_SLEEP_MS 10 #define READ2_SLEEP_MS 5 -#define STATUS_CHECK(reg, mask, val) (((reg) & (mask)) == (val)) - #define IS_CC_OPEN(cc_status) \ - (STATUS_CHECK((cc_status), TCPC_CC_STATUS_CC1_MASK << TCPC_CC_STATUS_CC1_SHIFT, \ - TCPC_CC_STATE_SRC_OPEN) && STATUS_CHECK((cc_status), \ - TCPC_CC_STATUS_CC2_MASK << \ - TCPC_CC_STATUS_CC2_SHIFT, \ - TCPC_CC_STATE_SRC_OPEN)) + (FIELD_GET(TCPC_CC_STATUS_CC1, cc_status) == TCPC_CC_STATE_SRC_OPEN \ + && FIELD_GET(TCPC_CC_STATUS_CC2, cc_status) == TCPC_CC_STATE_SRC_OPEN) static int max_contaminant_adc_to_mv(struct max_tcpci_chip *chip, enum fladc_select channel, bool ua_src, u8 fladc) @@ -80,8 +76,8 @@ static int max_contaminant_read_adc_mv(struct max_tcpci_chip *chip, enum fladc_s int ret; /* Channel & scale select */ - ret = regmap_update_bits(regmap, TCPC_VENDOR_ADC_CTRL1, ADCINSEL_MASK, - channel << ADC_CHANNEL_OFFSET); + ret = regmap_update_bits(regmap, TCPC_VENDOR_ADC_CTRL1, ADCINSEL, + FIELD_PREP(ADCINSEL, channel)); if (ret < 0) return ret; @@ -100,7 +96,8 @@ static int max_contaminant_read_adc_mv(struct max_tcpci_chip *chip, enum fladc_s if (ret < 0) return ret; - ret = regmap_update_bits(regmap, TCPC_VENDOR_ADC_CTRL1, ADCINSEL_MASK, 0); + ret = regmap_update_bits(regmap, TCPC_VENDOR_ADC_CTRL1, ADCINSEL, + FIELD_PREP(ADCINSEL, 0)); if (ret < 0) return ret; @@ -120,13 +117,14 @@ static int max_contaminant_read_resistance_kohm(struct max_tcpci_chip *chip, if (channel == CC1_SCALE1 || channel == CC2_SCALE1 || channel == CC1_SCALE2 || channel == CC2_SCALE2) { /* Enable 1uA current source */ - ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCLPMODESEL_MASK, - ULTRA_LOW_POWER_MODE); + ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCLPMODESEL, + FIELD_PREP(CCLPMODESEL, ULTRA_LOW_POWER_MODE)); if (ret < 0) return ret; /* Enable 1uA current source */ - ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCRPCTRL_MASK, UA_1_SRC); + ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCRPCTRL, + FIELD_PREP(CCRPCTRL, UA_1_SRC)); if (ret < 0) return ret; @@ -180,7 +178,8 @@ static int max_contaminant_read_comparators(struct max_tcpci_chip *chip, u8 *ven int ret; /* Enable 80uA source */ - ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCRPCTRL_MASK, UA_80_SRC); + ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCRPCTRL, + FIELD_PREP(CCRPCTRL, UA_80_SRC)); if (ret < 0) return ret; @@ -213,7 +212,8 @@ static int max_contaminant_read_comparators(struct max_tcpci_chip *chip, u8 *ven if (ret < 0) return ret; - ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCRPCTRL_MASK, 0); + ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCRPCTRL, + FIELD_PREP(CCRPCTRL, 0)); if (ret < 0) return ret; @@ -284,10 +284,11 @@ static int max_contaminant_enable_dry_detection(struct max_tcpci_chip *chip) u8 temp; int ret; - ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL3, CCWTRDEB_MASK | CCWTRSEL_MASK - | WTRCYCLE_MASK, CCWTRDEB_1MS << CCWTRDEB_SHIFT | - CCWTRSEL_1V << CCWTRSEL_SHIFT | WTRCYCLE_4_8_S << - WTRCYCLE_SHIFT); + ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL3, + CCWTRDEB | CCWTRSEL | WTRCYCLE, + FIELD_PREP(CCWTRDEB, CCWTRDEB_1MS) + | FIELD_PREP(CCWTRSEL, CCWTRSEL_1V) + | FIELD_PREP(WTRCYCLE, WTRCYCLE_4_8_S)); if (ret < 0) return ret; @@ -302,8 +303,9 @@ static int max_contaminant_enable_dry_detection(struct max_tcpci_chip *chip) if (ret < 0) return ret; - ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCLPMODESEL_MASK, - ULTRA_LOW_POWER_MODE); + ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCLPMODESEL, + FIELD_PREP(CCLPMODESEL, + ULTRA_LOW_POWER_MODE)); if (ret < 0) return ret; ret = max_tcpci_read8(chip, TCPC_VENDOR_CC_CTRL2, &temp); @@ -322,11 +324,14 @@ static int max_contaminant_enable_dry_detection(struct max_tcpci_chip *chip) return 0; } -bool max_contaminant_is_contaminant(struct max_tcpci_chip *chip, bool disconnect_while_debounce) +bool max_contaminant_is_contaminant(struct max_tcpci_chip *chip, bool disconnect_while_debounce, + bool *cc_handled) { u8 cc_status, pwr_cntl; int ret; + *cc_handled = true; + ret = max_tcpci_read8(chip, TCPC_CC_STATUS, &cc_status); if (ret < 0) return false; @@ -368,9 +373,8 @@ bool max_contaminant_is_contaminant(struct max_tcpci_chip *chip, bool disconnect return true; } } - return false; } else if (chip->contaminant_state == DETECTED) { - if (STATUS_CHECK(cc_status, TCPC_CC_STATUS_TOGGLING, 0)) { + if (!(cc_status & TCPC_CC_STATUS_TOGGLING)) { chip->contaminant_state = max_contaminant_detect_contaminant(chip); if (chip->contaminant_state == DETECTED) { max_contaminant_enable_dry_detection(chip); @@ -379,6 +383,7 @@ bool max_contaminant_is_contaminant(struct max_tcpci_chip *chip, bool disconnect } } + *cc_handled = false; return false; } diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c index 3e3dcb983dde..ed32583829be 100644 --- a/drivers/usb/typec/tcpm/tcpci.c +++ b/drivers/usb/typec/tcpm/tcpci.c @@ -5,6 +5,7 @@ * USB Type-C Port Controller Interface. */ +#include #include #include #include @@ -103,45 +104,45 @@ static int tcpci_set_cc(struct tcpc_dev *tcpc, enum typec_cc_status cc) switch (cc) { case TYPEC_CC_RA: - reg = (TCPC_ROLE_CTRL_CC_RA << TCPC_ROLE_CTRL_CC1_SHIFT) | - (TCPC_ROLE_CTRL_CC_RA << TCPC_ROLE_CTRL_CC2_SHIFT); + reg = (FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RA) + | FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RA)); break; case TYPEC_CC_RD: - reg = (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT) | - (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT); + reg = (FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RD) + | FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RD)); break; case TYPEC_CC_RP_DEF: - reg = (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) | - (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT) | - (TCPC_ROLE_CTRL_RP_VAL_DEF << - TCPC_ROLE_CTRL_RP_VAL_SHIFT); + reg = (FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RP) + | FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RP) + | FIELD_PREP(TCPC_ROLE_CTRL_RP_VAL, + TCPC_ROLE_CTRL_RP_VAL_DEF)); break; case TYPEC_CC_RP_1_5: - reg = (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) | - (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT) | - (TCPC_ROLE_CTRL_RP_VAL_1_5 << - TCPC_ROLE_CTRL_RP_VAL_SHIFT); + reg = (FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RP) + | FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RP) + | FIELD_PREP(TCPC_ROLE_CTRL_RP_VAL, + TCPC_ROLE_CTRL_RP_VAL_1_5)); break; case TYPEC_CC_RP_3_0: - reg = (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) | - (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT) | - (TCPC_ROLE_CTRL_RP_VAL_3_0 << - TCPC_ROLE_CTRL_RP_VAL_SHIFT); + reg = (FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RP) + | FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RP) + | FIELD_PREP(TCPC_ROLE_CTRL_RP_VAL, + TCPC_ROLE_CTRL_RP_VAL_3_0)); break; case TYPEC_CC_OPEN: default: - reg = (TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC1_SHIFT) | - (TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC2_SHIFT); + reg = (FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_OPEN) + | FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_OPEN)); break; } if (vconn_pres) { if (polarity == TYPEC_POLARITY_CC2) { - reg &= ~(TCPC_ROLE_CTRL_CC1_MASK << TCPC_ROLE_CTRL_CC1_SHIFT); - reg |= (TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC1_SHIFT); + reg &= ~TCPC_ROLE_CTRL_CC1; + reg |= FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_OPEN); } else { - reg &= ~(TCPC_ROLE_CTRL_CC2_MASK << TCPC_ROLE_CTRL_CC2_SHIFT); - reg |= (TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC2_SHIFT); + reg &= ~TCPC_ROLE_CTRL_CC2; + reg |= FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_OPEN); } } @@ -167,15 +168,11 @@ static int tcpci_apply_rc(struct tcpc_dev *tcpc, enum typec_cc_status cc, * APPLY_RC state is when ROLE_CONTROL.CC1 != ROLE_CONTROL.CC2 and vbus autodischarge on * disconnect is disabled. Bail out when ROLE_CONTROL.CC1 != ROLE_CONTROL.CC2. */ - if (((reg & (TCPC_ROLE_CTRL_CC2_MASK << TCPC_ROLE_CTRL_CC2_SHIFT)) >> - TCPC_ROLE_CTRL_CC2_SHIFT) != - ((reg & (TCPC_ROLE_CTRL_CC1_MASK << TCPC_ROLE_CTRL_CC1_SHIFT)) >> - TCPC_ROLE_CTRL_CC1_SHIFT)) + if (FIELD_GET(TCPC_ROLE_CTRL_CC2, reg) != FIELD_GET(TCPC_ROLE_CTRL_CC1, reg)) return 0; return regmap_update_bits(tcpci->regmap, TCPC_ROLE_CTRL, polarity == TYPEC_POLARITY_CC1 ? - TCPC_ROLE_CTRL_CC2_MASK << TCPC_ROLE_CTRL_CC2_SHIFT : - TCPC_ROLE_CTRL_CC1_MASK << TCPC_ROLE_CTRL_CC1_SHIFT, + TCPC_ROLE_CTRL_CC2 : TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_OPEN); } @@ -200,25 +197,25 @@ static int tcpci_start_toggling(struct tcpc_dev *tcpc, switch (cc) { default: case TYPEC_CC_RP_DEF: - reg |= (TCPC_ROLE_CTRL_RP_VAL_DEF << - TCPC_ROLE_CTRL_RP_VAL_SHIFT); + reg |= FIELD_PREP(TCPC_ROLE_CTRL_RP_VAL, + TCPC_ROLE_CTRL_RP_VAL_DEF); break; case TYPEC_CC_RP_1_5: - reg |= (TCPC_ROLE_CTRL_RP_VAL_1_5 << - TCPC_ROLE_CTRL_RP_VAL_SHIFT); + reg |= FIELD_PREP(TCPC_ROLE_CTRL_RP_VAL, + TCPC_ROLE_CTRL_RP_VAL_1_5); break; case TYPEC_CC_RP_3_0: - reg |= (TCPC_ROLE_CTRL_RP_VAL_3_0 << - TCPC_ROLE_CTRL_RP_VAL_SHIFT); + reg |= FIELD_PREP(TCPC_ROLE_CTRL_RP_VAL, + TCPC_ROLE_CTRL_RP_VAL_3_0); break; } if (cc == TYPEC_CC_RD) - reg |= (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT) | - (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT); + reg |= (FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RD) + | FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RD)); else - reg |= (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) | - (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT); + reg |= (FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RP) + | FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RP)); ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg); if (ret < 0) return ret; @@ -241,12 +238,10 @@ static int tcpci_get_cc(struct tcpc_dev *tcpc, if (ret < 0) return ret; - *cc1 = tcpci_to_typec_cc((reg >> TCPC_CC_STATUS_CC1_SHIFT) & - TCPC_CC_STATUS_CC1_MASK, + *cc1 = tcpci_to_typec_cc(FIELD_GET(TCPC_CC_STATUS_CC1, reg), reg & TCPC_CC_STATUS_TERM || tcpc_presenting_rd(role_control, CC1)); - *cc2 = tcpci_to_typec_cc((reg >> TCPC_CC_STATUS_CC2_SHIFT) & - TCPC_CC_STATUS_CC2_MASK, + *cc2 = tcpci_to_typec_cc(FIELD_GET(TCPC_CC_STATUS_CC2, reg), reg & TCPC_CC_STATUS_TERM || tcpc_presenting_rd(role_control, CC2)); @@ -282,28 +277,28 @@ static int tcpci_set_polarity(struct tcpc_dev *tcpc, reg = reg & ~TCPC_ROLE_CTRL_DRP; if (polarity == TYPEC_POLARITY_CC2) { - reg &= ~(TCPC_ROLE_CTRL_CC2_MASK << TCPC_ROLE_CTRL_CC2_SHIFT); + reg &= ~TCPC_ROLE_CTRL_CC2; /* Local port is source */ if (cc2 == TYPEC_CC_RD) /* Role control would have the Rp setting when DRP was enabled */ - reg |= TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT; + reg |= FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RP); else - reg |= TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT; + reg |= FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RD); } else { - reg &= ~(TCPC_ROLE_CTRL_CC1_MASK << TCPC_ROLE_CTRL_CC1_SHIFT); + reg &= ~TCPC_ROLE_CTRL_CC1; /* Local port is source */ if (cc1 == TYPEC_CC_RD) /* Role control would have the Rp setting when DRP was enabled */ - reg |= TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT; + reg |= FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RP); else - reg |= TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT; + reg |= FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RD); } } if (polarity == TYPEC_POLARITY_CC2) - reg |= TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC1_SHIFT; + reg |= FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_OPEN); else - reg |= TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC2_SHIFT; + reg |= FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_OPEN); ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg); if (ret < 0) return ret; @@ -461,7 +456,7 @@ static int tcpci_set_roles(struct tcpc_dev *tcpc, bool attached, unsigned int reg; int ret; - reg = PD_REV20 << TCPC_MSG_HDR_INFO_REV_SHIFT; + reg = FIELD_PREP(TCPC_MSG_HDR_INFO_REV, PD_REV20); if (role == TYPEC_SOURCE) reg |= TCPC_MSG_HDR_INFO_PWR_ROLE; if (data == TYPEC_HOST) @@ -612,8 +607,11 @@ static int tcpci_pd_transmit(struct tcpc_dev *tcpc, enum tcpm_transmit_type type } /* nRetryCount is 3 in PD2.0 spec where 2 in PD3.0 spec */ - reg = ((negotiated_rev > PD_REV20 ? PD_RETRY_COUNT_3_0_OR_HIGHER : PD_RETRY_COUNT_DEFAULT) - << TCPC_TRANSMIT_RETRY_SHIFT) | (type << TCPC_TRANSMIT_TYPE_SHIFT); + reg = FIELD_PREP(TCPC_TRANSMIT_RETRY, + (negotiated_rev > PD_REV20 + ? PD_RETRY_COUNT_3_0_OR_HIGHER + : PD_RETRY_COUNT_DEFAULT)); + reg |= FIELD_PREP(TCPC_TRANSMIT_TYPE, type); ret = regmap_write(tcpci->regmap, TCPC_TRANSMIT, reg); if (ret < 0) return ret; @@ -709,10 +707,13 @@ irqreturn_t tcpci_irq(struct tcpci *tcpci) { u16 status; int ret; + int irq_ret; unsigned int raw; tcpci_read16(tcpci, TCPC_ALERT, &status); + irq_ret = status & tcpci->alert_mask; +process_status: /* * Clear alert status for everything except RX_STATUS, which shouldn't * be cleared until we have successfully retrieved message. @@ -785,7 +786,12 @@ irqreturn_t tcpci_irq(struct tcpci *tcpci) else if (status & TCPC_ALERT_TX_FAILED) tcpm_pd_transmit_complete(tcpci->port, TCPC_TX_FAILED); - return IRQ_RETVAL(status & tcpci->alert_mask); + tcpci_read16(tcpci, TCPC_ALERT, &status); + + if (status & tcpci->alert_mask) + goto process_status; + + return IRQ_RETVAL(irq_ret); } EXPORT_SYMBOL_GPL(tcpci_irq); @@ -917,20 +923,22 @@ static int tcpci_probe(struct i2c_client *client) chip->data.set_orientation = err; - chip->tcpci = tcpci_register_port(&client->dev, &chip->data); - if (IS_ERR(chip->tcpci)) - return PTR_ERR(chip->tcpci); - err = devm_request_threaded_irq(&client->dev, client->irq, NULL, _tcpci_irq, - IRQF_SHARED | IRQF_ONESHOT | IRQF_TRIGGER_LOW, + IRQF_SHARED | IRQF_ONESHOT, dev_name(&client->dev), chip); - if (err < 0) { - tcpci_unregister_port(chip->tcpci); + if (err < 0) return err; - } - return 0; + /* + * Disable irq while registering port. If irq is configured as an edge + * irq this allow to keep track and process the irq as soon as it is enabled. + */ + disable_irq(client->irq); + chip->tcpci = tcpci_register_port(&client->dev, &chip->data); + enable_irq(client->irq); + + return PTR_ERR_OR_ZERO(chip->tcpci); } static void tcpci_remove(struct i2c_client *client) diff --git a/drivers/usb/typec/tcpm/tcpci_maxim.h b/drivers/usb/typec/tcpm/tcpci_maxim.h index 78ff3b73ee7e..76270d5c2838 100644 --- a/drivers/usb/typec/tcpm/tcpci_maxim.h +++ b/drivers/usb/typec/tcpm/tcpci_maxim.h @@ -20,28 +20,24 @@ #define SBUOVPDIS BIT(7) #define CCOVPDIS BIT(6) #define SBURPCTRL BIT(5) -#define CCLPMODESEL_MASK GENMASK(4, 3) -#define ULTRA_LOW_POWER_MODE BIT(3) -#define CCRPCTRL_MASK GENMASK(2, 0) +#define CCLPMODESEL GENMASK(4, 3) +#define ULTRA_LOW_POWER_MODE 1 +#define CCRPCTRL GENMASK(2, 0) #define UA_1_SRC 1 #define UA_80_SRC 3 #define TCPC_VENDOR_CC_CTRL3 0x8e -#define CCWTRDEB_MASK GENMASK(7, 6) -#define CCWTRDEB_SHIFT 6 +#define CCWTRDEB GENMASK(7, 6) #define CCWTRDEB_1MS 1 -#define CCWTRSEL_MASK GENMASK(5, 3) -#define CCWTRSEL_SHIFT 3 +#define CCWTRSEL GENMASK(5, 3) #define CCWTRSEL_1V 0x4 #define CCLADDERDIS BIT(2) -#define WTRCYCLE_MASK BIT(0) -#define WTRCYCLE_SHIFT 0 +#define WTRCYCLE GENMASK(0, 0) #define WTRCYCLE_2_4_S 0 #define WTRCYCLE_4_8_S 1 #define TCPC_VENDOR_ADC_CTRL1 0x91 -#define ADCINSEL_MASK GENMASK(7, 5) -#define ADC_CHANNEL_OFFSET 5 +#define ADCINSEL GENMASK(7, 5) #define ADCEN BIT(0) enum contamiant_state { @@ -85,6 +81,20 @@ static inline int max_tcpci_write8(struct max_tcpci_chip *chip, unsigned int reg return regmap_raw_write(chip->data.regmap, reg, &val, sizeof(u8)); } -bool max_contaminant_is_contaminant(struct max_tcpci_chip *chip, bool disconnect_while_debounce); +/** + * max_contaminant_is_contaminant - Test if CC was toggled due to contaminant + * + * @chip: Handle to a struct max_tcpci_chip + * @disconnect_while_debounce: Whether the disconnect was detected when CC + * pins were debouncing + * @cc_handled: Returns whether or not update to CC status was handled here + * + * Determine if a contaminant was detected. + * + * Returns: true if a contaminant was detected, false otherwise. cc_handled + * is updated to reflect whether or not further CC handling is required. + */ +bool max_contaminant_is_contaminant(struct max_tcpci_chip *chip, bool disconnect_while_debounce, + bool *cc_handled); #endif // TCPCI_MAXIM_H_ diff --git a/drivers/usb/typec/tcpm/tcpci_maxim_core.c b/drivers/usb/typec/tcpm/tcpci_maxim_core.c index 760e2f92b958..fd1b80593367 100644 --- a/drivers/usb/typec/tcpm/tcpci_maxim_core.c +++ b/drivers/usb/typec/tcpm/tcpci_maxim_core.c @@ -97,11 +97,13 @@ static void max_tcpci_init_regs(struct max_tcpci_chip *chip) if (ret < 0) return; - alert_mask = TCPC_ALERT_TX_SUCCESS | TCPC_ALERT_TX_DISCARDED | TCPC_ALERT_TX_FAILED | - TCPC_ALERT_RX_HARD_RST | TCPC_ALERT_RX_STATUS | TCPC_ALERT_CC_STATUS | - TCPC_ALERT_VBUS_DISCNCT | TCPC_ALERT_RX_BUF_OVF | TCPC_ALERT_POWER_STATUS | - /* Enable Extended alert for detecting Fast Role Swap Signal */ - TCPC_ALERT_EXTND | TCPC_ALERT_EXTENDED_STATUS | TCPC_ALERT_FAULT; + alert_mask = (TCPC_ALERT_TX_SUCCESS | TCPC_ALERT_TX_DISCARDED | + TCPC_ALERT_TX_FAILED | TCPC_ALERT_RX_HARD_RST | + TCPC_ALERT_RX_STATUS | TCPC_ALERT_POWER_STATUS | + TCPC_ALERT_CC_STATUS | + TCPC_ALERT_EXTND | TCPC_ALERT_EXTENDED_STATUS | + TCPC_ALERT_VBUS_DISCNCT | TCPC_ALERT_RX_BUF_OVF | + TCPC_ALERT_FAULT); ret = max_tcpci_write16(chip, TCPC_ALERT_MASK, alert_mask); if (ret < 0) { @@ -191,9 +193,8 @@ static void process_rx(struct max_tcpci_chip *chip, u16 status) * Read complete, clear RX status alert bit. * Clear overflow as well if set. */ - ret = max_tcpci_write16(chip, TCPC_ALERT, status & TCPC_ALERT_RX_BUF_OVF ? - TCPC_ALERT_RX_STATUS | TCPC_ALERT_RX_BUF_OVF : - TCPC_ALERT_RX_STATUS); + ret = max_tcpci_write16(chip, TCPC_ALERT, + TCPC_ALERT_RX_STATUS | (status & TCPC_ALERT_RX_BUF_OVF)); if (ret < 0) return; @@ -295,9 +296,8 @@ static irqreturn_t _max_tcpci_irq(struct max_tcpci_chip *chip, u16 status) * be cleared until we have successfully retrieved message. */ if (status & ~TCPC_ALERT_RX_STATUS) { - mask = status & TCPC_ALERT_RX_BUF_OVF ? - status & ~(TCPC_ALERT_RX_STATUS | TCPC_ALERT_RX_BUF_OVF) : - status & ~TCPC_ALERT_RX_STATUS; + mask = status & ~(TCPC_ALERT_RX_STATUS + | (status & TCPC_ALERT_RX_BUF_OVF)); ret = max_tcpci_write16(chip, TCPC_ALERT, mask); if (ret < 0) { dev_err(chip->dev, "ALERT clear failed\n"); @@ -357,12 +357,14 @@ static irqreturn_t _max_tcpci_irq(struct max_tcpci_chip *chip, u16 status) tcpm_vbus_change(chip->port); if (status & TCPC_ALERT_CC_STATUS) { + bool cc_handled = false; + if (chip->contaminant_state == DETECTED || tcpm_port_is_toggling(chip->port)) { - if (!max_contaminant_is_contaminant(chip, false)) + if (!max_contaminant_is_contaminant(chip, false, &cc_handled)) tcpm_port_clean(chip->port); - } else { - tcpm_cc_change(chip->port); } + if (!cc_handled) + tcpm_cc_change(chip->port); } if (status & TCPC_ALERT_POWER_STATUS) @@ -397,7 +399,7 @@ static irqreturn_t max_tcpci_irq(int irq, void *dev_id) } while (status) { irq_return = _max_tcpci_irq(chip, status); - /* Do not return if the ALERT is already set. */ + /* Do not return if a (new) ALERT is set (again). */ ret = max_tcpci_read16(chip, TCPC_ALERT, &status); if (ret < 0) break; @@ -455,8 +457,9 @@ static int tcpci_init(struct tcpci *tcpci, struct tcpci_data *data) static void max_tcpci_check_contaminant(struct tcpci *tcpci, struct tcpci_data *tdata) { struct max_tcpci_chip *chip = tdata_to_max_tcpci(tdata); + bool cc_handled; - if (!max_contaminant_is_contaminant(chip, true)) + if (!max_contaminant_is_contaminant(chip, true, &cc_handled)) tcpm_port_clean(chip->port); } @@ -472,6 +475,11 @@ static bool max_tcpci_attempt_vconn_swap_discovery(struct tcpci *tcpci, struct t return true; } +static void max_tcpci_unregister_tcpci_port(void *tcpci) +{ + tcpci_unregister_port(tcpci); +} + static int max_tcpci_probe(struct i2c_client *client) { int ret; @@ -484,17 +492,17 @@ static int max_tcpci_probe(struct i2c_client *client) chip->client = client; chip->data.regmap = devm_regmap_init_i2c(client, &max_tcpci_regmap_config); - if (IS_ERR(chip->data.regmap)) { - dev_err(&client->dev, "Regmap init failed\n"); - return PTR_ERR(chip->data.regmap); - } + if (IS_ERR(chip->data.regmap)) + return dev_err_probe(&client->dev, PTR_ERR(chip->data.regmap), + "Regmap init failed\n"); chip->dev = &client->dev; i2c_set_clientdata(client, chip); ret = max_tcpci_read8(chip, TCPC_POWER_STATUS, &power_status); if (ret < 0) - return ret; + return dev_err_probe(&client->dev, ret, + "Failed to read TCPC_POWER_STATUS\n"); /* Chip level tcpci callbacks */ chip->data.set_vbus = max_tcpci_set_vbus; @@ -511,30 +519,25 @@ static int max_tcpci_probe(struct i2c_client *client) max_tcpci_init_regs(chip); chip->tcpci = tcpci_register_port(chip->dev, &chip->data); - if (IS_ERR(chip->tcpci)) { - dev_err(&client->dev, "TCPCI port registration failed\n"); - return PTR_ERR(chip->tcpci); - } + if (IS_ERR(chip->tcpci)) + return dev_err_probe(&client->dev, PTR_ERR(chip->tcpci), + "TCPCI port registration failed\n"); + + ret = devm_add_action_or_reset(&client->dev, + max_tcpci_unregister_tcpci_port, + chip->tcpci); + if (ret) + return ret; + chip->port = tcpci_get_tcpm_port(chip->tcpci); + ret = max_tcpci_init_alert(chip, client); if (ret < 0) - goto unreg_port; + return dev_err_probe(&client->dev, ret, + "IRQ initialization failed\n"); device_init_wakeup(chip->dev, true); return 0; - -unreg_port: - tcpci_unregister_port(chip->tcpci); - - return ret; -} - -static void max_tcpci_remove(struct i2c_client *client) -{ - struct max_tcpci_chip *chip = i2c_get_clientdata(client); - - if (!IS_ERR_OR_NULL(chip->tcpci)) - tcpci_unregister_port(chip->tcpci); } static const struct i2c_device_id max_tcpci_id[] = { @@ -557,7 +560,6 @@ static struct i2c_driver max_tcpci_i2c_driver = { .of_match_table = of_match_ptr(max_tcpci_of_match), }, .probe = max_tcpci_probe, - .remove = max_tcpci_remove, .id_table = max_tcpci_id, }; module_i2c_driver(max_tcpci_i2c_driver); diff --git a/drivers/usb/typec/tcpm/tcpci_rt1711h.c b/drivers/usb/typec/tcpm/tcpci_rt1711h.c index 67422d45eb54..64f6dd0dc660 100644 --- a/drivers/usb/typec/tcpm/tcpci_rt1711h.c +++ b/drivers/usb/typec/tcpm/tcpci_rt1711h.c @@ -5,6 +5,7 @@ * Richtek RT1711H Type-C Chip Driver */ +#include #include #include #include @@ -195,12 +196,10 @@ static inline int rt1711h_init_cc_params(struct rt1711h_chip *chip, u8 status) if (ret < 0) return ret; - cc1 = tcpci_to_typec_cc((status >> TCPC_CC_STATUS_CC1_SHIFT) & - TCPC_CC_STATUS_CC1_MASK, + cc1 = tcpci_to_typec_cc(FIELD_GET(TCPC_CC_STATUS_CC1, status), status & TCPC_CC_STATUS_TERM || tcpc_presenting_rd(role, CC1)); - cc2 = tcpci_to_typec_cc((status >> TCPC_CC_STATUS_CC2_SHIFT) & - TCPC_CC_STATUS_CC2_MASK, + cc2 = tcpci_to_typec_cc(FIELD_GET(TCPC_CC_STATUS_CC2, status), status & TCPC_CC_STATUS_TERM || tcpc_presenting_rd(role, CC2)); @@ -233,25 +232,25 @@ static int rt1711h_start_drp_toggling(struct tcpci *tcpci, switch (cc) { default: case TYPEC_CC_RP_DEF: - reg |= (TCPC_ROLE_CTRL_RP_VAL_DEF << - TCPC_ROLE_CTRL_RP_VAL_SHIFT); + reg |= FIELD_PREP(TCPC_ROLE_CTRL_RP_VAL, + TCPC_ROLE_CTRL_RP_VAL_DEF); break; case TYPEC_CC_RP_1_5: - reg |= (TCPC_ROLE_CTRL_RP_VAL_1_5 << - TCPC_ROLE_CTRL_RP_VAL_SHIFT); + reg |= FIELD_PREP(TCPC_ROLE_CTRL_RP_VAL, + TCPC_ROLE_CTRL_RP_VAL_1_5); break; case TYPEC_CC_RP_3_0: - reg |= (TCPC_ROLE_CTRL_RP_VAL_3_0 << - TCPC_ROLE_CTRL_RP_VAL_SHIFT); + reg |= FIELD_PREP(TCPC_ROLE_CTRL_RP_VAL, + TCPC_ROLE_CTRL_RP_VAL_3_0); break; } if (cc == TYPEC_CC_RD) - reg |= (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT) | - (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT); + reg |= (FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RD) + | FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RD)); else - reg |= (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) | - (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT); + reg |= (FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RP) + | FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RP)); ret = rt1711h_write8(chip, TCPC_ROLE_CTRL, reg); if (ret < 0) diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c index d532670885e4..7ee721a877c1 100644 --- a/drivers/usb/typec/tipd/core.c +++ b/drivers/usb/typec/tipd/core.c @@ -1460,8 +1460,9 @@ static void tps6598x_remove(struct i2c_client *client) if (!client->irq) cancel_delayed_work_sync(&tps->wq_poll); + else + devm_free_irq(tps->dev, client->irq, tps); - devm_free_irq(tps->dev, client->irq, tps); tps6598x_disconnect(tps, 0); typec_unregister_port(tps->port); usb_role_switch_put(tps->role_sw); diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 17155ed17fdf..e0f3925e401b 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -38,6 +38,10 @@ void ucsi_notify_common(struct ucsi *ucsi, u32 cci) { + /* Ignore bogus data in CCI if busy indicator is set. */ + if (cci & UCSI_CCI_BUSY) + return; + if (UCSI_CCI_CONNECTOR(cci)) ucsi_connector_change(ucsi, UCSI_CCI_CONNECTOR(cci)); @@ -99,23 +103,17 @@ static int ucsi_run_command(struct ucsi *ucsi, u64 command, u32 *cci, *cci = 0; - /* - * Below UCSI 2.0, MESSAGE_IN was limited to 16 bytes. Truncate the - * reads here. - */ - if (ucsi->version <= UCSI_VERSION_1_2) - size = clamp(size, 0, 16); + if (size > UCSI_MAX_DATA_LENGTH(ucsi)) + return -EINVAL; ret = ucsi->ops->sync_control(ucsi, command); - if (ret) - return ret; - - ret = ucsi->ops->read_cci(ucsi, cci); - if (ret) - return ret; + if (ucsi->ops->read_cci(ucsi, cci)) + return -EIO; if (*cci & UCSI_CCI_BUSY) - return -EBUSY; + return ucsi_run_command(ucsi, UCSI_CANCEL, cci, NULL, 0, false) ?: -EBUSY; + if (ret) + return ret; if (!(*cci & UCSI_CCI_COMMAND_COMPLETE)) return -EIO; @@ -148,21 +146,10 @@ 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); - - if (cci & UCSI_CCI_BUSY) { - ret = ucsi_run_command(ucsi, UCSI_CANCEL, &cci, NULL, 0, false); - - return ret ? ret : -EBUSY; - } - + ret = ucsi_run_command(ucsi, command, &cci, &error, sizeof(error), false); if (ret < 0) return ret; - if (cci & UCSI_CCI_ERROR) - return -EIO; - switch (error) { case UCSI_ERROR_INCOMPATIBLE_PARTNER: return -EOPNOTSUPP; @@ -238,9 +225,8 @@ 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); - if (cci & UCSI_CCI_BUSY) - ret = ucsi_run_command(ucsi, UCSI_CANCEL, &cci, NULL, 0, false) ?: -EBUSY; - else if (cci & UCSI_CCI_ERROR) + + if (cci & UCSI_CCI_ERROR) ret = ucsi_read_error(ucsi, connector_num); mutex_unlock(&ucsi->ppm_lock); @@ -752,104 +738,66 @@ static struct usb_power_delivery_capabilities *ucsi_get_pd_caps(struct ucsi_conn &pd_caps); } -static int ucsi_read_identity(struct ucsi_connector *con, u8 recipient, - u8 offset, u8 bytes, void *resp) +static int ucsi_get_pd_message(struct ucsi_connector *con, u8 recipient, + size_t bytes, void *data, u8 type) { - struct ucsi *ucsi = con->ucsi; + size_t len = min(bytes, UCSI_MAX_DATA_LENGTH(con->ucsi)); u64 command; + u8 offset; int ret; - command = UCSI_COMMAND(UCSI_GET_PD_MESSAGE) | - UCSI_CONNECTOR_NUMBER(con->num); - command |= UCSI_GET_PD_MESSAGE_RECIPIENT(recipient); - command |= UCSI_GET_PD_MESSAGE_OFFSET(offset); - command |= UCSI_GET_PD_MESSAGE_BYTES(bytes); - command |= UCSI_GET_PD_MESSAGE_TYPE(UCSI_GET_PD_MESSAGE_TYPE_IDENTITY); + for (offset = 0; offset < bytes; offset += len) { + len = min(len, bytes - offset); - ret = ucsi_send_command(ucsi, command, resp, bytes); - if (ret < 0) - dev_err(ucsi->dev, "UCSI_GET_PD_MESSAGE failed (%d)\n", ret); + command = UCSI_COMMAND(UCSI_GET_PD_MESSAGE) | UCSI_CONNECTOR_NUMBER(con->num); + command |= UCSI_GET_PD_MESSAGE_RECIPIENT(recipient); + command |= UCSI_GET_PD_MESSAGE_OFFSET(offset); + command |= UCSI_GET_PD_MESSAGE_BYTES(len); + command |= UCSI_GET_PD_MESSAGE_TYPE(type); - return ret; -} - -static int ucsi_get_identity(struct ucsi_connector *con, u8 recipient, - struct usb_pd_identity *id) -{ - struct ucsi *ucsi = con->ucsi; - struct ucsi_pd_message_disc_id resp = {}; - int ret; - - if (ucsi->version < UCSI_VERSION_2_0) { - /* - * Before UCSI v2.0, MESSAGE_IN is 16 bytes which cannot fit - * the 28 byte identity response including the VDM header. - * First request the VDM header, ID Header VDO, Cert Stat VDO - * and Product VDO. - */ - ret = ucsi_read_identity(con, recipient, 0, 0x10, &resp); - if (ret < 0) - return ret; - - - /* Then request Product Type VDO1 through Product Type VDO3. */ - ret = ucsi_read_identity(con, recipient, 0x10, 0xc, - &resp.vdo[0]); - if (ret < 0) - return ret; - - } else { - /* - * In UCSI v2.0 and after, MESSAGE_IN is large enough to request - * the large enough to request the full Discover Identity - * response at once. - */ - ret = ucsi_read_identity(con, recipient, 0x0, 0x1c, &resp); + ret = ucsi_send_command(con->ucsi, command, data + offset, len); if (ret < 0) return ret; } - id->id_header = resp.id_header; - id->cert_stat = resp.cert_stat; - id->product = resp.product; - id->vdo[0] = resp.vdo[0]; - id->vdo[1] = resp.vdo[1]; - id->vdo[2] = resp.vdo[2]; return 0; } static int ucsi_get_partner_identity(struct ucsi_connector *con) { + u32 vdo[7] = {}; int ret; - ret = ucsi_get_identity(con, UCSI_RECIPIENT_SOP, - &con->partner_identity); + ret = ucsi_get_pd_message(con, UCSI_RECIPIENT_SOP, sizeof(vdo), vdo, + UCSI_GET_PD_MESSAGE_TYPE_IDENTITY); if (ret < 0) return ret; + /* VDM Header is not part of struct usb_pd_identity, so dropping it. */ + con->partner_identity = *(struct usb_pd_identity *)&vdo[1]; + ret = typec_partner_set_identity(con->partner); - if (ret < 0) { - dev_err(con->ucsi->dev, "Failed to set partner identity (%d)\n", - ret); - } + if (ret < 0) + dev_err(con->ucsi->dev, "Failed to set partner identity (%d)\n", ret); return ret; } static int ucsi_get_cable_identity(struct ucsi_connector *con) { + u32 vdo[7] = {}; int ret; - ret = ucsi_get_identity(con, UCSI_RECIPIENT_SOP_P, - &con->cable_identity); + ret = ucsi_get_pd_message(con, UCSI_RECIPIENT_SOP_P, sizeof(vdo), vdo, + UCSI_GET_PD_MESSAGE_TYPE_IDENTITY); if (ret < 0) return ret; + con->cable_identity = *(struct usb_pd_identity *)&vdo[1]; + ret = typec_cable_set_identity(con->cable); - if (ret < 0) { - dev_err(con->ucsi->dev, "Failed to set cable identity (%d)\n", - ret); - } + if (ret < 0) + dev_err(con->ucsi->dev, "Failed to set cable identity (%d)\n", ret); return ret; } @@ -993,7 +941,8 @@ static int ucsi_register_cable(struct ucsi_connector *con) break; } - desc.identity = &con->cable_identity; + if (con->ucsi->cap.features & UCSI_CAP_GET_PD_MESSAGE) + desc.identity = &con->cable_identity; desc.active = !!(UCSI_CABLE_PROP_FLAG_ACTIVE_CABLE & cable_prop.flags); if (con->ucsi->version >= UCSI_VERSION_2_1) @@ -1094,7 +1043,8 @@ static int ucsi_register_partner(struct ucsi_connector *con) if (pwr_opmode == UCSI_CONSTAT_PWR_OPMODE_PD) ucsi_register_device_pdos(con); - desc.identity = &con->partner_identity; + if (con->ucsi->cap.features & UCSI_CAP_GET_PD_MESSAGE) + desc.identity = &con->partner_identity; desc.usb_pd = pwr_opmode == UCSI_CONSTAT_PWR_OPMODE_PD; partner = typec_register_partner(con->port, &desc); @@ -1249,6 +1199,10 @@ static void ucsi_handle_connector_change(struct work_struct *work) mutex_lock(&con->lock); + if (!test_and_set_bit(EVENT_PENDING, &ucsi->flags)) + dev_err_once(ucsi->dev, "%s entered without EVENT_PENDING\n", + __func__); + command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num); ret = ucsi_send_command_common(ucsi, command, &con->status, @@ -1341,12 +1295,26 @@ EXPORT_SYMBOL_GPL(ucsi_connector_change); /* -------------------------------------------------------------------------- */ +/* + * Hard Reset bit field was defined with value 1 in UCSI spec version 1.0. + * Starting with spec version 1.1, Hard Reset bit field was removed from the + * CONNECTOR_RESET command, until spec 2.0 reintroduced it with value 0, so, in effect, + * the value to pass in to the command for a Hard Reset is different depending + * on the supported UCSI version by the LPM. + * + * For performing a Data Reset on LPMs supporting version 2.0 and greater, + * this function needs to be called with the second argument set to 0. + */ static int ucsi_reset_connector(struct ucsi_connector *con, bool hard) { u64 command; command = UCSI_CONNECTOR_RESET | UCSI_CONNECTOR_NUMBER(con->num); - command |= hard ? UCSI_CONNECTOR_RESET_HARD : 0; + + if (con->ucsi->version < UCSI_VERSION_1_1) + command |= hard ? UCSI_CONNECTOR_RESET_HARD_VER_1_0 : 0; + 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); } diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index 5a3481d36d7a..4a017eb6a65b 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -29,6 +29,7 @@ struct dentry; #define UCSIv2_MESSAGE_OUT 272 /* UCSI versions */ +#define UCSI_VERSION_1_1 0x0110 #define UCSI_VERSION_1_2 0x0120 #define UCSI_VERSION_2_0 0x0200 #define UCSI_VERSION_2_1 0x0210 @@ -122,7 +123,9 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num); #define UCSI_DEFAULT_GET_CONNECTOR_NUMBER(_cmd_) (((_cmd_) >> 16) & GENMASK(6, 0)) /* CONNECTOR_RESET command bits */ -#define UCSI_CONNECTOR_RESET_HARD BIT(23) /* Deprecated in v1.1 */ +#define UCSI_CONNECTOR_RESET_HARD_VER_1_0 BIT(23) /* Deprecated in v1.1 */ +#define UCSI_CONNECTOR_RESET_DATA_VER_2_0 BIT(23) /* Redefined in v2.0 */ + /* ACK_CC_CI bits */ #define UCSI_ACK_CONNECTOR_CHANGE BIT(16) @@ -344,47 +347,12 @@ struct ucsi_connector_status { #define UCSI_CONSTAT_PARTNER_TYPE_AUDIO 6 u32 request_data_obj; - u8 pwr_status[3]; -#define UCSI_CONSTAT_BC_STATUS(_p_) ((_p_[0]) & GENMASK(1, 0)) + u8 pwr_status; +#define UCSI_CONSTAT_BC_STATUS(_p_) ((_p_) & GENMASK(1, 0)) #define UCSI_CONSTAT_BC_NOT_CHARGING 0 #define UCSI_CONSTAT_BC_NOMINAL_CHARGING 1 #define UCSI_CONSTAT_BC_SLOW_CHARGING 2 #define UCSI_CONSTAT_BC_TRICKLE_CHARGING 3 -#define UCSI_CONSTAT_PROVIDER_CAP_LIMIT(_p_) (((_p_[0]) & GENMASK(5, 2)) >> 2) -#define UCSI_CONSTAT_CAP_PWR_LOWERED 0 -#define UCSI_CONSTAT_CAP_PWR_BUDGET_LIMIT 1 -#define UCSI_CONSTAT_PROVIDER_PD_VERSION_OPER_MODE(_p_) \ - ((get_unaligned_le32(_p_) & GENMASK(21, 6)) >> 6) -#define UCSI_CONSTAT_ORIENTATION(_p_) (((_p_[2]) & GENMASK(6, 6)) >> 6) -#define UCSI_CONSTAT_ORIENTATION_DIRECT 0 -#define UCSI_CONSTAT_ORIENTATION_FLIPPED 1 -#define UCSI_CONSTAT_SINK_PATH_STATUS(_p_) (((_p_[2]) & GENMASK(7, 7)) >> 7) -#define UCSI_CONSTAT_SINK_PATH_DISABLED 0 -#define UCSI_CONSTAT_SINK_PATH_ENABLED 1 - u8 pwr_readings[9]; -#define UCSI_CONSTAT_REV_CURR_PROT_STATUS(_p_) ((_p_[0]) & 0x1) -#define UCSI_CONSTAT_PWR_READING_VALID(_p_) (((_p_[0]) & GENMASK(1, 1)) >> 1) -#define UCSI_CONSTAT_CURRENT_SCALE(_p_) (((_p_[0]) & GENMASK(4, 2)) >> 2) -#define UCSI_CONSTAT_PEAK_CURRENT(_p_) \ - ((get_unaligned_le32(_p_) & GENMASK(20, 5)) >> 5) -#define UCSI_CONSTAT_AVG_CURRENT(_p_) \ - ((get_unaligned_le32(&(_p_)[2]) & GENMASK(20, 5)) >> 5) -#define UCSI_CONSTAT_VOLTAGE_SCALE(_p_) \ - ((get_unaligned_le16(&(_p_)[4]) & GENMASK(8, 5)) >> 5) -#define UCSI_CONSTAT_VOLTAGE_READING(_p_) \ - ((get_unaligned_le32(&(_p_)[5]) & GENMASK(16, 1)) >> 1) -} __packed; - -/* - * Data structure filled by PPM in response to GET_PD_MESSAGE command with the - * Response Message Type set to Discover Identity Response. - */ -struct ucsi_pd_message_disc_id { - u32 vdm_header; - u32 id_header; - u32 cert_stat; - u32 product; - u32 vdo[3]; } __packed; /* -------------------------------------------------------------------------- */ @@ -435,6 +403,8 @@ struct ucsi { #define UCSI_DELAY_DEVICE_PDOS BIT(1) /* Reading PDOs fails until the parter is in PD mode */ }; +#define UCSI_MAX_DATA_LENGTH(u) (((u)->version < UCSI_VERSION_2_0) ? 0x10 : 0xff) + #define UCSI_MAX_SVID 5 #define UCSI_MAX_ALTMODES (UCSI_MAX_SVID * 6) diff --git a/drivers/usb/typec/ucsi/ucsi_glink.c b/drivers/usb/typec/ucsi/ucsi_glink.c index 6aace19d595b..03c0fa8edc8d 100644 --- a/drivers/usb/typec/ucsi/ucsi_glink.c +++ b/drivers/usb/typec/ucsi/ucsi_glink.c @@ -278,7 +278,7 @@ static void pmic_glink_ucsi_callback(const void *data, size_t len, void *priv) case UC_UCSI_USBC_NOTIFY_IND: schedule_work(&ucsi->notify_work); break; - }; + } } static void pmic_glink_ucsi_pdr_notify(void *priv, int state) diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c index 302a89aeb258..8dac1edc74d4 100644 --- a/drivers/usb/usbip/vhci_hcd.c +++ b/drivers/usb/usbip/vhci_hcd.c @@ -372,7 +372,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, } switch (wValue) { case USB_PORT_FEAT_SUSPEND: - if (hcd->speed == HCD_USB3) { + if (hcd->speed >= HCD_USB3) { pr_err(" ClearPortFeature: USB_PORT_FEAT_SUSPEND req not " "supported for USB 3.0 roothub\n"); goto error; @@ -388,7 +388,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, case USB_PORT_FEAT_POWER: usbip_dbg_vhci_rh( " ClearPortFeature: USB_PORT_FEAT_POWER\n"); - if (hcd->speed == HCD_USB3) + if (hcd->speed >= HCD_USB3) vhci_hcd->port_status[rhport] &= ~USB_SS_PORT_STAT_POWER; else vhci_hcd->port_status[rhport] &= ~USB_PORT_STAT_POWER; @@ -404,19 +404,19 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, break; case GetHubDescriptor: usbip_dbg_vhci_rh(" GetHubDescriptor\n"); - if (hcd->speed == HCD_USB3 && + 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"); goto error; } - if (hcd->speed == HCD_USB3) + if (hcd->speed >= HCD_USB3) ss_hub_descriptor((struct usb_hub_descriptor *) buf); else hub_descriptor((struct usb_hub_descriptor *) buf); break; case DeviceRequest | USB_REQ_GET_DESCRIPTOR: - if (hcd->speed != HCD_USB3) + if (hcd->speed < HCD_USB3) goto error; if ((wValue >> 8) != USB_DT_BOS) @@ -503,7 +503,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, case USB_PORT_FEAT_LINK_STATE: usbip_dbg_vhci_rh( " SetPortFeature: USB_PORT_FEAT_LINK_STATE\n"); - if (hcd->speed != HCD_USB3) { + if (hcd->speed < HCD_USB3) { pr_err("USB_PORT_FEAT_LINK_STATE req not " "supported for USB 2.0 roothub\n"); goto error; @@ -521,7 +521,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, usbip_dbg_vhci_rh( " SetPortFeature: USB_PORT_FEAT_U2_TIMEOUT\n"); /* TODO: add suspend/resume support! */ - if (hcd->speed != HCD_USB3) { + if (hcd->speed < HCD_USB3) { pr_err("USB_PORT_FEAT_U1/2_TIMEOUT req not " "supported for USB 2.0 roothub\n"); goto error; @@ -531,7 +531,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, usbip_dbg_vhci_rh( " SetPortFeature: USB_PORT_FEAT_SUSPEND\n"); /* Applicable only for USB2.0 hub */ - if (hcd->speed == HCD_USB3) { + if (hcd->speed >= HCD_USB3) { pr_err("USB_PORT_FEAT_SUSPEND req not " "supported for USB 3.0 roothub\n"); goto error; @@ -551,7 +551,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, pr_err("invalid port number %d\n", wIndex); goto error; } - if (hcd->speed == HCD_USB3) + if (hcd->speed >= HCD_USB3) vhci_hcd->port_status[rhport] |= USB_SS_PORT_STAT_POWER; else vhci_hcd->port_status[rhport] |= USB_PORT_STAT_POWER; @@ -564,7 +564,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, goto error; } /* Applicable only for USB3.0 hub */ - if (hcd->speed != HCD_USB3) { + if (hcd->speed < HCD_USB3) { pr_err("USB_PORT_FEAT_BH_PORT_RESET req not " "supported for USB 2.0 roothub\n"); goto error; @@ -578,7 +578,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, goto error; } /* if it's already enabled, disable */ - if (hcd->speed == HCD_USB3) { + if (hcd->speed >= HCD_USB3) { vhci_hcd->port_status[rhport] = 0; vhci_hcd->port_status[rhport] = (USB_SS_PORT_STAT_POWER | @@ -602,7 +602,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, } if (wValue >= 32) goto error; - if (hcd->speed == HCD_USB3) { + if (hcd->speed >= HCD_USB3) { if ((vhci_hcd->port_status[rhport] & USB_SS_PORT_STAT_POWER) != 0) { vhci_hcd->port_status[rhport] |= (1 << wValue); @@ -616,7 +616,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, break; case GetPortErrorCount: usbip_dbg_vhci_rh(" GetPortErrorCount\n"); - if (hcd->speed != HCD_USB3) { + if (hcd->speed < HCD_USB3) { pr_err("GetPortErrorCount req not " "supported for USB 2.0 roothub\n"); goto error; @@ -626,7 +626,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, break; case SetHubDepth: usbip_dbg_vhci_rh(" SetHubDepth\n"); - if (hcd->speed != HCD_USB3) { + if (hcd->speed < HCD_USB3) { pr_err("SetHubDepth req not supported for " "USB 2.0 roothub\n"); goto error; @@ -646,7 +646,7 @@ error: if (!invalid_rhport) { dump_port_status_diff(prev_port_status[rhport], vhci_hcd->port_status[rhport], - hcd->speed == HCD_USB3); + hcd->speed >= HCD_USB3); } } usbip_dbg_vhci_rh(" bye\n"); @@ -1157,8 +1157,8 @@ static int vhci_setup(struct usb_hcd *hcd) } else { vhci->vhci_hcd_ss = hcd_to_vhci_hcd(hcd); vhci->vhci_hcd_ss->vhci = vhci; - hcd->speed = HCD_USB3; - hcd->self.root_hub->speed = USB_SPEED_SUPER; + hcd->speed = HCD_USB31; + hcd->self.root_hub->speed = USB_SPEED_SUPER_PLUS; } /* @@ -1319,7 +1319,7 @@ static const struct hc_driver vhci_hc_driver = { .product_desc = driver_desc, .hcd_priv_size = sizeof(struct vhci_hcd), - .flags = HCD_USB3 | HCD_SHARED, + .flags = HCD_USB31 | HCD_SHARED, .reset = vhci_setup, .start = vhci_start, diff --git a/drivers/usb/usbip/vhci_sysfs.c b/drivers/usb/usbip/vhci_sysfs.c index e2847cd3e6e3..d5865460e82d 100644 --- a/drivers/usb/usbip/vhci_sysfs.c +++ b/drivers/usb/usbip/vhci_sysfs.c @@ -283,6 +283,7 @@ static int valid_args(__u32 *pdev_nr, __u32 *rhport, case USB_SPEED_HIGH: case USB_SPEED_WIRELESS: case USB_SPEED_SUPER: + case USB_SPEED_SUPER_PLUS: break; default: pr_err("Failed attach request for unsupported USB speed: %s\n", @@ -349,7 +350,7 @@ static ssize_t attach_store(struct device *dev, struct device_attribute *attr, vhci_hcd = hcd_to_vhci_hcd(hcd); vhci = vhci_hcd->vhci; - if (speed == USB_SPEED_SUPER) + if (speed >= USB_SPEED_SUPER) vdev = &vhci->vhci_hcd_ss->vdev[rhport]; else vdev = &vhci->vhci_hcd_hs->vdev[rhport]; diff --git a/drivers/vdpa/ifcvf/ifcvf_base.h b/drivers/vdpa/ifcvf/ifcvf_base.h index 0f347717021a..aa36de361c10 100644 --- a/drivers/vdpa/ifcvf/ifcvf_base.h +++ b/drivers/vdpa/ifcvf/ifcvf_base.h @@ -112,15 +112,12 @@ void ifcvf_write_dev_config(struct ifcvf_hw *hw, u64 offset, const void *src, int length); u8 ifcvf_get_status(struct ifcvf_hw *hw); void ifcvf_set_status(struct ifcvf_hw *hw, u8 status); -void io_write64_twopart(u64 val, u32 *lo, u32 *hi); void ifcvf_reset(struct ifcvf_hw *hw); u64 ifcvf_get_dev_features(struct ifcvf_hw *hw); u64 ifcvf_get_hw_features(struct ifcvf_hw *hw); int ifcvf_verify_min_features(struct ifcvf_hw *hw, u64 features); u16 ifcvf_get_vq_state(struct ifcvf_hw *hw, u16 qid); int ifcvf_set_vq_state(struct ifcvf_hw *hw, u16 qid, u16 num); -struct ifcvf_adapter *vf_to_adapter(struct ifcvf_hw *hw); -int ifcvf_probed_virtio_net(struct ifcvf_hw *hw); u32 ifcvf_get_config_size(struct ifcvf_hw *hw); u16 ifcvf_set_vq_vector(struct ifcvf_hw *hw, u16 qid, int vector); u16 ifcvf_set_config_vector(struct ifcvf_hw *hw, int vector); diff --git a/drivers/vdpa/mlx5/core/mlx5_vdpa.h b/drivers/vdpa/mlx5/core/mlx5_vdpa.h index 50aac8fe57ef..2cedf7e2dbc4 100644 --- a/drivers/vdpa/mlx5/core/mlx5_vdpa.h +++ b/drivers/vdpa/mlx5/core/mlx5_vdpa.h @@ -83,10 +83,28 @@ enum { MLX5_VDPA_NUM_AS = 2 }; +struct mlx5_vdpa_mr_resources { + struct mlx5_vdpa_mr *mr[MLX5_VDPA_NUM_AS]; + unsigned int group2asid[MLX5_VDPA_NUMVQ_GROUPS]; + + /* Pre-deletion mr list */ + struct list_head mr_list_head; + + /* Deferred mr list */ + struct list_head mr_gc_list_head; + struct workqueue_struct *wq_gc; + struct delayed_work gc_dwork_ent; + + struct mutex lock; + + atomic_t shutdown; +}; + struct mlx5_vdpa_dev { struct vdpa_device vdev; struct mlx5_core_dev *mdev; struct mlx5_vdpa_resources res; + struct mlx5_vdpa_mr_resources mres; u64 mlx_features; u64 actual_features; @@ -95,14 +113,23 @@ struct mlx5_vdpa_dev { u16 max_idx; u32 generation; - struct mlx5_vdpa_mr *mr[MLX5_VDPA_NUM_AS]; - struct list_head mr_list_head; - /* serialize mr access */ - struct mutex mr_mtx; struct mlx5_control_vq cvq; struct workqueue_struct *wq; - unsigned int group2asid[MLX5_VDPA_NUMVQ_GROUPS]; bool suspended; + + struct mlx5_async_ctx async_ctx; +}; + +struct mlx5_vdpa_async_cmd { + int err; + struct mlx5_async_work cb_work; + struct completion cmd_done; + + void *in; + size_t inlen; + + void *out; + size_t outlen; }; int mlx5_vdpa_create_tis(struct mlx5_vdpa_dev *mvdev, void *in, u32 *tisn); @@ -121,7 +148,9 @@ int mlx5_vdpa_create_mkey(struct mlx5_vdpa_dev *mvdev, u32 *mkey, u32 *in, int mlx5_vdpa_destroy_mkey(struct mlx5_vdpa_dev *mvdev, u32 mkey); struct mlx5_vdpa_mr *mlx5_vdpa_create_mr(struct mlx5_vdpa_dev *mvdev, struct vhost_iotlb *iotlb); +int mlx5_vdpa_init_mr_resources(struct mlx5_vdpa_dev *mvdev); void mlx5_vdpa_destroy_mr_resources(struct mlx5_vdpa_dev *mvdev); +void mlx5_vdpa_clean_mrs(struct mlx5_vdpa_dev *mvdev); void mlx5_vdpa_get_mr(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_mr *mr); void mlx5_vdpa_put_mr(struct mlx5_vdpa_dev *mvdev, @@ -134,6 +163,14 @@ int mlx5_vdpa_update_cvq_iotlb(struct mlx5_vdpa_dev *mvdev, unsigned int asid); int mlx5_vdpa_create_dma_mr(struct mlx5_vdpa_dev *mvdev); int mlx5_vdpa_reset_mr(struct mlx5_vdpa_dev *mvdev, unsigned int asid); +int mlx5_vdpa_exec_async_cmds(struct mlx5_vdpa_dev *mvdev, + struct mlx5_vdpa_async_cmd *cmds, + int num_cmds); + +#define mlx5_vdpa_err(__dev, format, ...) \ + dev_err((__dev)->mdev->device, "%s:%d:(pid %d) error: " format, __func__, __LINE__, \ + current->pid, ##__VA_ARGS__) + #define mlx5_vdpa_warn(__dev, format, ...) \ dev_warn((__dev)->mdev->device, "%s:%d:(pid %d) warning: " format, __func__, __LINE__, \ diff --git a/drivers/vdpa/mlx5/core/mr.c b/drivers/vdpa/mlx5/core/mr.c index 4758914ccf86..2dd21e0b399e 100644 --- a/drivers/vdpa/mlx5/core/mr.c +++ b/drivers/vdpa/mlx5/core/mr.c @@ -49,17 +49,23 @@ static void populate_mtts(struct mlx5_vdpa_direct_mr *mr, __be64 *mtt) } } -static int create_direct_mr(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_direct_mr *mr) -{ - int inlen; - void *mkc; - void *in; - int err; +struct mlx5_create_mkey_mem { + u8 out[MLX5_ST_SZ_BYTES(create_mkey_out)]; + u8 in[MLX5_ST_SZ_BYTES(create_mkey_in)]; + __be64 mtt[]; +}; - inlen = MLX5_ST_SZ_BYTES(create_mkey_in) + roundup(MLX5_ST_SZ_BYTES(mtt) * mr->nsg, 16); - in = kvzalloc(inlen, GFP_KERNEL); - if (!in) - return -ENOMEM; +struct mlx5_destroy_mkey_mem { + u8 out[MLX5_ST_SZ_BYTES(destroy_mkey_out)]; + u8 in[MLX5_ST_SZ_BYTES(destroy_mkey_in)]; +}; + +static void fill_create_direct_mr(struct mlx5_vdpa_dev *mvdev, + struct mlx5_vdpa_direct_mr *mr, + struct mlx5_create_mkey_mem *mem) +{ + void *in = &mem->in; + void *mkc; MLX5_SET(create_mkey_in, in, uid, mvdev->res.uid); mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry); @@ -76,18 +82,36 @@ static int create_direct_mr(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_direct MLX5_SET(create_mkey_in, in, translations_octword_actual_size, get_octo_len(mr->end - mr->start, mr->log_size)); populate_mtts(mr, MLX5_ADDR_OF(create_mkey_in, in, klm_pas_mtt)); - err = mlx5_vdpa_create_mkey(mvdev, &mr->mr, in, inlen); - kvfree(in); - if (err) { - mlx5_vdpa_warn(mvdev, "Failed to create direct MR\n"); - return err; - } - return 0; + MLX5_SET(create_mkey_in, in, opcode, MLX5_CMD_OP_CREATE_MKEY); + MLX5_SET(create_mkey_in, in, uid, mvdev->res.uid); +} + +static void create_direct_mr_end(struct mlx5_vdpa_dev *mvdev, + struct mlx5_vdpa_direct_mr *mr, + struct mlx5_create_mkey_mem *mem) +{ + u32 mkey_index = MLX5_GET(create_mkey_out, mem->out, mkey_index); + + mr->mr = mlx5_idx_to_mkey(mkey_index); +} + +static void fill_destroy_direct_mr(struct mlx5_vdpa_dev *mvdev, + struct mlx5_vdpa_direct_mr *mr, + struct mlx5_destroy_mkey_mem *mem) +{ + void *in = &mem->in; + + MLX5_SET(destroy_mkey_in, in, uid, mvdev->res.uid); + MLX5_SET(destroy_mkey_in, in, opcode, MLX5_CMD_OP_DESTROY_MKEY); + MLX5_SET(destroy_mkey_in, in, mkey_index, mlx5_mkey_to_idx(mr->mr)); } static void destroy_direct_mr(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_direct_mr *mr) { + if (!mr->mr) + return; + mlx5_vdpa_destroy_mkey(mvdev, mr->mr); } @@ -179,6 +203,123 @@ static int klm_byte_size(int nklms) return 16 * ALIGN(nklms, 4); } +#define MLX5_VDPA_MTT_ALIGN 16 + +static int create_direct_keys(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_mr *mr) +{ + struct mlx5_vdpa_async_cmd *cmds; + struct mlx5_vdpa_direct_mr *dmr; + int err = 0; + int i = 0; + + cmds = kvcalloc(mr->num_directs, sizeof(*cmds), GFP_KERNEL); + if (!cmds) + return -ENOMEM; + + list_for_each_entry(dmr, &mr->head, list) { + struct mlx5_create_mkey_mem *cmd_mem; + int mttlen, mttcount; + + mttlen = roundup(MLX5_ST_SZ_BYTES(mtt) * dmr->nsg, MLX5_VDPA_MTT_ALIGN); + mttcount = mttlen / sizeof(cmd_mem->mtt[0]); + cmd_mem = kvcalloc(1, struct_size(cmd_mem, mtt, mttcount), GFP_KERNEL); + if (!cmd_mem) { + err = -ENOMEM; + goto done; + } + + cmds[i].out = cmd_mem->out; + cmds[i].outlen = sizeof(cmd_mem->out); + cmds[i].in = cmd_mem->in; + cmds[i].inlen = struct_size(cmd_mem, mtt, mttcount); + + fill_create_direct_mr(mvdev, dmr, cmd_mem); + + i++; + } + + err = mlx5_vdpa_exec_async_cmds(mvdev, cmds, mr->num_directs); + if (err) { + + mlx5_vdpa_err(mvdev, "error issuing MTT mkey creation for direct mrs: %d\n", err); + goto done; + } + + i = 0; + list_for_each_entry(dmr, &mr->head, list) { + struct mlx5_vdpa_async_cmd *cmd = &cmds[i++]; + struct mlx5_create_mkey_mem *cmd_mem; + + cmd_mem = container_of(cmd->out, struct mlx5_create_mkey_mem, out); + + if (!cmd->err) { + create_direct_mr_end(mvdev, dmr, cmd_mem); + } else { + err = err ? err : cmd->err; + mlx5_vdpa_err(mvdev, "error creating MTT mkey [0x%llx, 0x%llx]: %d\n", + dmr->start, dmr->end, cmd->err); + } + } + +done: + for (i = i-1; i >= 0; i--) { + struct mlx5_create_mkey_mem *cmd_mem; + + cmd_mem = container_of(cmds[i].out, struct mlx5_create_mkey_mem, out); + kvfree(cmd_mem); + } + + kvfree(cmds); + return err; +} + +DEFINE_FREE(free_cmds, struct mlx5_vdpa_async_cmd *, kvfree(_T)) +DEFINE_FREE(free_cmd_mem, struct mlx5_destroy_mkey_mem *, kvfree(_T)) + +static int destroy_direct_keys(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_mr *mr) +{ + struct mlx5_destroy_mkey_mem *cmd_mem __free(free_cmd_mem) = NULL; + struct mlx5_vdpa_async_cmd *cmds __free(free_cmds) = NULL; + struct mlx5_vdpa_direct_mr *dmr; + int err = 0; + int i = 0; + + cmds = kvcalloc(mr->num_directs, sizeof(*cmds), GFP_KERNEL); + cmd_mem = kvcalloc(mr->num_directs, sizeof(*cmd_mem), GFP_KERNEL); + if (!cmds || !cmd_mem) + return -ENOMEM; + + list_for_each_entry(dmr, &mr->head, list) { + cmds[i].out = cmd_mem[i].out; + cmds[i].outlen = sizeof(cmd_mem[i].out); + cmds[i].in = cmd_mem[i].in; + cmds[i].inlen = sizeof(cmd_mem[i].in); + fill_destroy_direct_mr(mvdev, dmr, &cmd_mem[i]); + i++; + } + + err = mlx5_vdpa_exec_async_cmds(mvdev, cmds, mr->num_directs); + if (err) { + + mlx5_vdpa_err(mvdev, "error issuing MTT mkey deletion for direct mrs: %d\n", err); + return err; + } + + i = 0; + list_for_each_entry(dmr, &mr->head, list) { + struct mlx5_vdpa_async_cmd *cmd = &cmds[i++]; + + dmr->mr = 0; + if (cmd->err) { + err = err ? err : cmd->err; + mlx5_vdpa_err(mvdev, "error deleting MTT mkey [0x%llx, 0x%llx]: %d\n", + dmr->start, dmr->end, cmd->err); + } + } + + return err; +} + static int create_indirect_key(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_mr *mr) { int inlen; @@ -279,14 +420,8 @@ done: goto err_map; } - err = create_direct_mr(mvdev, mr); - if (err) - goto err_direct; - return 0; -err_direct: - dma_unmap_sg_attrs(dma, mr->sg_head.sgl, mr->nsg, DMA_BIDIRECTIONAL, 0); err_map: sg_free_table(&mr->sg_head); return err; @@ -401,6 +536,10 @@ static int create_user_mr(struct mlx5_vdpa_dev *mvdev, if (err) goto err_chain; + err = create_direct_keys(mvdev, mr); + if (err) + goto err_chain; + /* Create the memory key that defines the guests's address space. This * memory key refers to the direct keys that contain the MTT * translations @@ -489,6 +628,7 @@ static void destroy_user_mr(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_mr *mr struct mlx5_vdpa_direct_mr *n; destroy_indirect_key(mvdev, mr); + destroy_direct_keys(mvdev, mr); list_for_each_entry_safe_reverse(dmr, n, &mr->head, list) { list_del_init(&dmr->list); unmap_direct_mr(mvdev, dmr); @@ -513,22 +653,58 @@ static void _mlx5_vdpa_destroy_mr(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_ kfree(mr); } +/* There can be multiple .set_map() operations in quick succession. + * This large delay is a simple way to prevent the MR cleanup from blocking + * .set_map() MR creation in this scenario. + */ +#define MLX5_VDPA_MR_GC_TRIGGER_MS 2000 + +static void mlx5_vdpa_mr_gc_handler(struct work_struct *work) +{ + struct mlx5_vdpa_mr_resources *mres; + struct mlx5_vdpa_mr *mr, *tmp; + struct mlx5_vdpa_dev *mvdev; + + mres = container_of(work, struct mlx5_vdpa_mr_resources, gc_dwork_ent.work); + + if (atomic_read(&mres->shutdown)) { + mutex_lock(&mres->lock); + } else if (!mutex_trylock(&mres->lock)) { + queue_delayed_work(mres->wq_gc, &mres->gc_dwork_ent, + msecs_to_jiffies(MLX5_VDPA_MR_GC_TRIGGER_MS)); + return; + } + + mvdev = container_of(mres, struct mlx5_vdpa_dev, mres); + + list_for_each_entry_safe(mr, tmp, &mres->mr_gc_list_head, mr_list) { + _mlx5_vdpa_destroy_mr(mvdev, mr); + } + + mutex_unlock(&mres->lock); +} + static void _mlx5_vdpa_put_mr(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_mr *mr) { + struct mlx5_vdpa_mr_resources *mres = &mvdev->mres; + if (!mr) return; - if (refcount_dec_and_test(&mr->refcount)) - _mlx5_vdpa_destroy_mr(mvdev, mr); + if (refcount_dec_and_test(&mr->refcount)) { + list_move_tail(&mr->mr_list, &mres->mr_gc_list_head); + queue_delayed_work(mres->wq_gc, &mres->gc_dwork_ent, + msecs_to_jiffies(MLX5_VDPA_MR_GC_TRIGGER_MS)); + } } void mlx5_vdpa_put_mr(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_mr *mr) { - mutex_lock(&mvdev->mr_mtx); + mutex_lock(&mvdev->mres.lock); _mlx5_vdpa_put_mr(mvdev, mr); - mutex_unlock(&mvdev->mr_mtx); + mutex_unlock(&mvdev->mres.lock); } static void _mlx5_vdpa_get_mr(struct mlx5_vdpa_dev *mvdev, @@ -543,44 +719,47 @@ static void _mlx5_vdpa_get_mr(struct mlx5_vdpa_dev *mvdev, void mlx5_vdpa_get_mr(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_mr *mr) { - mutex_lock(&mvdev->mr_mtx); + mutex_lock(&mvdev->mres.lock); _mlx5_vdpa_get_mr(mvdev, mr); - mutex_unlock(&mvdev->mr_mtx); + mutex_unlock(&mvdev->mres.lock); } void mlx5_vdpa_update_mr(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_mr *new_mr, unsigned int asid) { - struct mlx5_vdpa_mr *old_mr = mvdev->mr[asid]; + struct mlx5_vdpa_mr *old_mr = mvdev->mres.mr[asid]; - mutex_lock(&mvdev->mr_mtx); + mutex_lock(&mvdev->mres.lock); _mlx5_vdpa_put_mr(mvdev, old_mr); - mvdev->mr[asid] = new_mr; + mvdev->mres.mr[asid] = new_mr; - mutex_unlock(&mvdev->mr_mtx); + mutex_unlock(&mvdev->mres.lock); } static void mlx5_vdpa_show_mr_leaks(struct mlx5_vdpa_dev *mvdev) { struct mlx5_vdpa_mr *mr; - mutex_lock(&mvdev->mr_mtx); + mutex_lock(&mvdev->mres.lock); - list_for_each_entry(mr, &mvdev->mr_list_head, mr_list) { + list_for_each_entry(mr, &mvdev->mres.mr_list_head, mr_list) { mlx5_vdpa_warn(mvdev, "mkey still alive after resource delete: " "mr: %p, mkey: 0x%x, refcount: %u\n", mr, mr->mkey, refcount_read(&mr->refcount)); } - mutex_unlock(&mvdev->mr_mtx); + mutex_unlock(&mvdev->mres.lock); } -void mlx5_vdpa_destroy_mr_resources(struct mlx5_vdpa_dev *mvdev) +void mlx5_vdpa_clean_mrs(struct mlx5_vdpa_dev *mvdev) { + if (!mvdev->res.valid) + return; + for (int i = 0; i < MLX5_VDPA_NUM_AS; i++) mlx5_vdpa_update_mr(mvdev, NULL, i); @@ -613,7 +792,7 @@ static int _mlx5_vdpa_create_mr(struct mlx5_vdpa_dev *mvdev, if (err) goto err_iotlb; - list_add_tail(&mr->mr_list, &mvdev->mr_list_head); + list_add_tail(&mr->mr_list, &mvdev->mres.mr_list_head); return 0; @@ -639,9 +818,9 @@ struct mlx5_vdpa_mr *mlx5_vdpa_create_mr(struct mlx5_vdpa_dev *mvdev, if (!mr) return ERR_PTR(-ENOMEM); - mutex_lock(&mvdev->mr_mtx); + mutex_lock(&mvdev->mres.lock); err = _mlx5_vdpa_create_mr(mvdev, mr, iotlb); - mutex_unlock(&mvdev->mr_mtx); + mutex_unlock(&mvdev->mres.lock); if (err) goto out_err; @@ -661,7 +840,7 @@ int mlx5_vdpa_update_cvq_iotlb(struct mlx5_vdpa_dev *mvdev, { int err; - if (mvdev->group2asid[MLX5_VDPA_CVQ_GROUP] != asid) + if (mvdev->mres.group2asid[MLX5_VDPA_CVQ_GROUP] != asid) return 0; spin_lock(&mvdev->cvq.iommu_lock); @@ -703,3 +882,33 @@ int mlx5_vdpa_reset_mr(struct mlx5_vdpa_dev *mvdev, unsigned int asid) return 0; } + +int mlx5_vdpa_init_mr_resources(struct mlx5_vdpa_dev *mvdev) +{ + struct mlx5_vdpa_mr_resources *mres = &mvdev->mres; + + mres->wq_gc = create_singlethread_workqueue("mlx5_vdpa_mr_gc"); + if (!mres->wq_gc) + return -ENOMEM; + + INIT_DELAYED_WORK(&mres->gc_dwork_ent, mlx5_vdpa_mr_gc_handler); + + mutex_init(&mres->lock); + + INIT_LIST_HEAD(&mres->mr_list_head); + INIT_LIST_HEAD(&mres->mr_gc_list_head); + + return 0; +} + +void mlx5_vdpa_destroy_mr_resources(struct mlx5_vdpa_dev *mvdev) +{ + struct mlx5_vdpa_mr_resources *mres = &mvdev->mres; + + atomic_set(&mres->shutdown, 1); + + flush_delayed_work(&mres->gc_dwork_ent); + destroy_workqueue(mres->wq_gc); + mres->wq_gc = NULL; + mutex_destroy(&mres->lock); +} diff --git a/drivers/vdpa/mlx5/core/resources.c b/drivers/vdpa/mlx5/core/resources.c index 5c5a41b64bfc..aeae31d0cefa 100644 --- a/drivers/vdpa/mlx5/core/resources.c +++ b/drivers/vdpa/mlx5/core/resources.c @@ -256,7 +256,6 @@ int mlx5_vdpa_alloc_resources(struct mlx5_vdpa_dev *mvdev) mlx5_vdpa_warn(mvdev, "resources already allocated\n"); return -EINVAL; } - mutex_init(&mvdev->mr_mtx); res->uar = mlx5_get_uars_page(mdev); if (IS_ERR(res->uar)) { err = PTR_ERR(res->uar); @@ -301,7 +300,6 @@ err_pd: err_uctx: mlx5_put_uars_page(mdev, res->uar); err_uars: - mutex_destroy(&mvdev->mr_mtx); return err; } @@ -318,6 +316,78 @@ void mlx5_vdpa_free_resources(struct mlx5_vdpa_dev *mvdev) dealloc_pd(mvdev, res->pdn, res->uid); destroy_uctx(mvdev, res->uid); mlx5_put_uars_page(mvdev->mdev, res->uar); - mutex_destroy(&mvdev->mr_mtx); res->valid = false; } + +static void virtqueue_cmd_callback(int status, struct mlx5_async_work *context) +{ + struct mlx5_vdpa_async_cmd *cmd = + container_of(context, struct mlx5_vdpa_async_cmd, cb_work); + + cmd->err = mlx5_cmd_check(context->ctx->dev, status, cmd->in, cmd->out); + complete(&cmd->cmd_done); +} + +static int issue_async_cmd(struct mlx5_vdpa_dev *mvdev, + struct mlx5_vdpa_async_cmd *cmds, + int issued, + int *completed) + +{ + struct mlx5_vdpa_async_cmd *cmd = &cmds[issued]; + int err; + +retry: + err = mlx5_cmd_exec_cb(&mvdev->async_ctx, + cmd->in, cmd->inlen, + cmd->out, cmd->outlen, + virtqueue_cmd_callback, + &cmd->cb_work); + if (err == -EBUSY) { + if (*completed < issued) { + /* Throttled by own commands: wait for oldest completion. */ + wait_for_completion(&cmds[*completed].cmd_done); + (*completed)++; + + goto retry; + } else { + /* Throttled by external commands: switch to sync api. */ + err = mlx5_cmd_exec(mvdev->mdev, + cmd->in, cmd->inlen, + cmd->out, cmd->outlen); + if (!err) + (*completed)++; + } + } + + return err; +} + +int mlx5_vdpa_exec_async_cmds(struct mlx5_vdpa_dev *mvdev, + struct mlx5_vdpa_async_cmd *cmds, + int num_cmds) +{ + int completed = 0; + int issued = 0; + int err = 0; + + for (int i = 0; i < num_cmds; i++) + init_completion(&cmds[i].cmd_done); + + while (issued < num_cmds) { + + err = issue_async_cmd(mvdev, cmds, issued, &completed); + if (err) { + mlx5_vdpa_err(mvdev, "error issuing command %d of %d: %d\n", + issued, num_cmds, err); + break; + } + + issued++; + } + + while (completed < issued) + wait_for_completion(&cmds[completed++].cmd_done); + + return err; +} diff --git a/drivers/vdpa/mlx5/net/mlx5_vnet.c b/drivers/vdpa/mlx5/net/mlx5_vnet.c index fa78e8288ebb..dee019977716 100644 --- a/drivers/vdpa/mlx5/net/mlx5_vnet.c +++ b/drivers/vdpa/mlx5/net/mlx5_vnet.c @@ -941,11 +941,11 @@ static int create_virtqueue(struct mlx5_vdpa_net *ndev, MLX5_SET64(virtio_q, vq_ctx, used_addr, mvq->device_addr); MLX5_SET64(virtio_q, vq_ctx, available_addr, mvq->driver_addr); - vq_mr = mvdev->mr[mvdev->group2asid[MLX5_VDPA_DATAVQ_GROUP]]; + vq_mr = mvdev->mres.mr[mvdev->mres.group2asid[MLX5_VDPA_DATAVQ_GROUP]]; if (vq_mr) MLX5_SET(virtio_q, vq_ctx, virtio_q_mkey, vq_mr->mkey); - vq_desc_mr = mvdev->mr[mvdev->group2asid[MLX5_VDPA_DATAVQ_DESC_GROUP]]; + vq_desc_mr = mvdev->mres.mr[mvdev->mres.group2asid[MLX5_VDPA_DATAVQ_DESC_GROUP]]; if (vq_desc_mr && MLX5_CAP_DEV_VDPA_EMULATION(mvdev->mdev, desc_group_mkey_supported)) MLX5_SET(virtio_q, vq_ctx, desc_group_mkey, vq_desc_mr->mkey); @@ -953,11 +953,11 @@ static int create_virtqueue(struct mlx5_vdpa_net *ndev, /* If there is no mr update, make sure that the existing ones are set * modify to ready. */ - vq_mr = mvdev->mr[mvdev->group2asid[MLX5_VDPA_DATAVQ_GROUP]]; + vq_mr = mvdev->mres.mr[mvdev->mres.group2asid[MLX5_VDPA_DATAVQ_GROUP]]; if (vq_mr) mvq->modified_fields |= MLX5_VIRTQ_MODIFY_MASK_VIRTIO_Q_MKEY; - vq_desc_mr = mvdev->mr[mvdev->group2asid[MLX5_VDPA_DATAVQ_DESC_GROUP]]; + vq_desc_mr = mvdev->mres.mr[mvdev->mres.group2asid[MLX5_VDPA_DATAVQ_DESC_GROUP]]; if (vq_desc_mr) mvq->modified_fields |= MLX5_VIRTQ_MODIFY_MASK_DESC_GROUP_MKEY; } @@ -1184,40 +1184,92 @@ struct mlx5_virtq_attr { u16 used_index; }; -static int query_virtqueue(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq, - struct mlx5_virtq_attr *attr) +struct mlx5_virtqueue_query_mem { + u8 in[MLX5_ST_SZ_BYTES(query_virtio_net_q_in)]; + u8 out[MLX5_ST_SZ_BYTES(query_virtio_net_q_out)]; +}; + +struct mlx5_virtqueue_modify_mem { + u8 in[MLX5_ST_SZ_BYTES(modify_virtio_net_q_in)]; + u8 out[MLX5_ST_SZ_BYTES(modify_virtio_net_q_out)]; +}; + +static void fill_query_virtqueue_cmd(struct mlx5_vdpa_net *ndev, + struct mlx5_vdpa_virtqueue *mvq, + struct mlx5_virtqueue_query_mem *cmd) { - int outlen = MLX5_ST_SZ_BYTES(query_virtio_net_q_out); - u32 in[MLX5_ST_SZ_DW(query_virtio_net_q_in)] = {}; - void *out; - void *obj_context; - void *cmd_hdr; - int err; - - out = kzalloc(outlen, GFP_KERNEL); - if (!out) - return -ENOMEM; - - cmd_hdr = MLX5_ADDR_OF(query_virtio_net_q_in, in, general_obj_in_cmd_hdr); + void *cmd_hdr = MLX5_ADDR_OF(query_virtio_net_q_in, cmd->in, general_obj_in_cmd_hdr); MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, opcode, MLX5_CMD_OP_QUERY_GENERAL_OBJECT); MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, obj_type, MLX5_OBJ_TYPE_VIRTIO_NET_Q); MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, obj_id, mvq->virtq_id); MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, uid, ndev->mvdev.res.uid); - err = mlx5_cmd_exec(ndev->mvdev.mdev, in, sizeof(in), out, outlen); - if (err) - goto err_cmd; +} + +static void query_virtqueue_end(struct mlx5_vdpa_net *ndev, + struct mlx5_virtqueue_query_mem *cmd, + struct mlx5_virtq_attr *attr) +{ + void *obj_context = MLX5_ADDR_OF(query_virtio_net_q_out, cmd->out, obj_context); - obj_context = MLX5_ADDR_OF(query_virtio_net_q_out, out, obj_context); memset(attr, 0, sizeof(*attr)); attr->state = MLX5_GET(virtio_net_q_object, obj_context, state); attr->available_index = MLX5_GET(virtio_net_q_object, obj_context, hw_available_index); attr->used_index = MLX5_GET(virtio_net_q_object, obj_context, hw_used_index); - kfree(out); - return 0; +} -err_cmd: - kfree(out); +static int query_virtqueues(struct mlx5_vdpa_net *ndev, + int start_vq, + int num_vqs, + struct mlx5_virtq_attr *attrs) +{ + struct mlx5_vdpa_dev *mvdev = &ndev->mvdev; + struct mlx5_virtqueue_query_mem *cmd_mem; + struct mlx5_vdpa_async_cmd *cmds; + int err = 0; + + WARN(start_vq + num_vqs > mvdev->max_vqs, "query vq range invalid [%d, %d), max_vqs: %u\n", + start_vq, start_vq + num_vqs, mvdev->max_vqs); + + cmds = kvcalloc(num_vqs, sizeof(*cmds), GFP_KERNEL); + cmd_mem = kvcalloc(num_vqs, sizeof(*cmd_mem), GFP_KERNEL); + if (!cmds || !cmd_mem) { + err = -ENOMEM; + goto done; + } + + for (int i = 0; i < num_vqs; i++) { + cmds[i].in = &cmd_mem[i].in; + cmds[i].inlen = sizeof(cmd_mem[i].in); + cmds[i].out = &cmd_mem[i].out; + cmds[i].outlen = sizeof(cmd_mem[i].out); + fill_query_virtqueue_cmd(ndev, &ndev->vqs[start_vq + i], &cmd_mem[i]); + } + + err = mlx5_vdpa_exec_async_cmds(&ndev->mvdev, cmds, num_vqs); + if (err) { + mlx5_vdpa_err(mvdev, "error issuing query cmd for vq range [%d, %d): %d\n", + start_vq, start_vq + num_vqs, err); + goto done; + } + + for (int i = 0; i < num_vqs; i++) { + struct mlx5_vdpa_async_cmd *cmd = &cmds[i]; + int vq_idx = start_vq + i; + + if (cmd->err) { + mlx5_vdpa_err(mvdev, "query vq %d failed, err: %d\n", vq_idx, err); + if (!err) + err = cmd->err; + continue; + } + + query_virtqueue_end(ndev, &cmd_mem[i], &attrs[i]); + } + +done: + kvfree(cmd_mem); + kvfree(cmds); return err; } @@ -1251,51 +1303,30 @@ static bool modifiable_virtqueue_fields(struct mlx5_vdpa_virtqueue *mvq) return true; } -static int modify_virtqueue(struct mlx5_vdpa_net *ndev, - struct mlx5_vdpa_virtqueue *mvq, - int state) +static void fill_modify_virtqueue_cmd(struct mlx5_vdpa_net *ndev, + struct mlx5_vdpa_virtqueue *mvq, + int state, + struct mlx5_virtqueue_modify_mem *cmd) { - int inlen = MLX5_ST_SZ_BYTES(modify_virtio_net_q_in); - u32 out[MLX5_ST_SZ_DW(modify_virtio_net_q_out)] = {}; struct mlx5_vdpa_dev *mvdev = &ndev->mvdev; struct mlx5_vdpa_mr *desc_mr = NULL; struct mlx5_vdpa_mr *vq_mr = NULL; - bool state_change = false; void *obj_context; void *cmd_hdr; void *vq_ctx; - void *in; - int err; - if (mvq->fw_state == MLX5_VIRTIO_NET_Q_OBJECT_NONE) - return 0; - - if (!modifiable_virtqueue_fields(mvq)) - return -EINVAL; - - in = kzalloc(inlen, GFP_KERNEL); - if (!in) - return -ENOMEM; - - cmd_hdr = MLX5_ADDR_OF(modify_virtio_net_q_in, in, general_obj_in_cmd_hdr); + cmd_hdr = MLX5_ADDR_OF(modify_virtio_net_q_in, cmd->in, general_obj_in_cmd_hdr); MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, opcode, MLX5_CMD_OP_MODIFY_GENERAL_OBJECT); MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, obj_type, MLX5_OBJ_TYPE_VIRTIO_NET_Q); MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, obj_id, mvq->virtq_id); MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, uid, ndev->mvdev.res.uid); - obj_context = MLX5_ADDR_OF(modify_virtio_net_q_in, in, obj_context); + obj_context = MLX5_ADDR_OF(modify_virtio_net_q_in, cmd->in, obj_context); vq_ctx = MLX5_ADDR_OF(virtio_net_q_object, obj_context, virtio_q_context); - if (mvq->modified_fields & MLX5_VIRTQ_MODIFY_MASK_STATE) { - if (!is_valid_state_change(mvq->fw_state, state, is_resumable(ndev))) { - err = -EINVAL; - goto done; - } - + if (mvq->modified_fields & MLX5_VIRTQ_MODIFY_MASK_STATE) MLX5_SET(virtio_net_q_object, obj_context, state, state); - state_change = true; - } if (mvq->modified_fields & MLX5_VIRTQ_MODIFY_MASK_VIRTIO_Q_ADDRS) { MLX5_SET64(virtio_q, vq_ctx, desc_addr, mvq->desc_addr); @@ -1323,7 +1354,7 @@ static int modify_virtqueue(struct mlx5_vdpa_net *ndev, } if (mvq->modified_fields & MLX5_VIRTQ_MODIFY_MASK_VIRTIO_Q_MKEY) { - vq_mr = mvdev->mr[mvdev->group2asid[MLX5_VDPA_DATAVQ_GROUP]]; + vq_mr = mvdev->mres.mr[mvdev->mres.group2asid[MLX5_VDPA_DATAVQ_GROUP]]; if (vq_mr) MLX5_SET(virtio_q, vq_ctx, virtio_q_mkey, vq_mr->mkey); @@ -1332,7 +1363,7 @@ static int modify_virtqueue(struct mlx5_vdpa_net *ndev, } if (mvq->modified_fields & MLX5_VIRTQ_MODIFY_MASK_DESC_GROUP_MKEY) { - desc_mr = mvdev->mr[mvdev->group2asid[MLX5_VDPA_DATAVQ_DESC_GROUP]]; + desc_mr = mvdev->mres.mr[mvdev->mres.group2asid[MLX5_VDPA_DATAVQ_DESC_GROUP]]; if (desc_mr && MLX5_CAP_DEV_VDPA_EMULATION(mvdev->mdev, desc_group_mkey_supported)) MLX5_SET(virtio_q, vq_ctx, desc_group_mkey, desc_mr->mkey); @@ -1341,38 +1372,36 @@ static int modify_virtqueue(struct mlx5_vdpa_net *ndev, } MLX5_SET64(virtio_net_q_object, obj_context, modify_field_select, mvq->modified_fields); - err = mlx5_cmd_exec(ndev->mvdev.mdev, in, inlen, out, sizeof(out)); - if (err) - goto done; +} - if (state_change) - mvq->fw_state = state; +static void modify_virtqueue_end(struct mlx5_vdpa_net *ndev, + struct mlx5_vdpa_virtqueue *mvq, + int state) +{ + struct mlx5_vdpa_dev *mvdev = &ndev->mvdev; if (mvq->modified_fields & MLX5_VIRTQ_MODIFY_MASK_VIRTIO_Q_MKEY) { + unsigned int asid = mvdev->mres.group2asid[MLX5_VDPA_DATAVQ_GROUP]; + struct mlx5_vdpa_mr *vq_mr = mvdev->mres.mr[asid]; + mlx5_vdpa_put_mr(mvdev, mvq->vq_mr); mlx5_vdpa_get_mr(mvdev, vq_mr); mvq->vq_mr = vq_mr; } if (mvq->modified_fields & MLX5_VIRTQ_MODIFY_MASK_DESC_GROUP_MKEY) { + unsigned int asid = mvdev->mres.group2asid[MLX5_VDPA_DATAVQ_DESC_GROUP]; + struct mlx5_vdpa_mr *desc_mr = mvdev->mres.mr[asid]; + mlx5_vdpa_put_mr(mvdev, mvq->desc_mr); mlx5_vdpa_get_mr(mvdev, desc_mr); mvq->desc_mr = desc_mr; } + if (mvq->modified_fields & MLX5_VIRTQ_MODIFY_MASK_STATE) + mvq->fw_state = state; + mvq->modified_fields = 0; - -done: - kfree(in); - return err; -} - -static int modify_virtqueue_state(struct mlx5_vdpa_net *ndev, - struct mlx5_vdpa_virtqueue *mvq, - unsigned int state) -{ - mvq->modified_fields |= MLX5_VIRTQ_MODIFY_MASK_STATE; - return modify_virtqueue(ndev, mvq, state); } static int counter_set_alloc(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq) @@ -1525,53 +1554,136 @@ err_fwqp: return err; } -static int suspend_vq(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq) +static int modify_virtqueues(struct mlx5_vdpa_net *ndev, int start_vq, int num_vqs, int state) { - struct mlx5_virtq_attr attr; + struct mlx5_vdpa_dev *mvdev = &ndev->mvdev; + struct mlx5_virtqueue_modify_mem *cmd_mem; + struct mlx5_vdpa_async_cmd *cmds; + int err = 0; + + WARN(start_vq + num_vqs > mvdev->max_vqs, "modify vq range invalid [%d, %d), max_vqs: %u\n", + start_vq, start_vq + num_vqs, mvdev->max_vqs); + + cmds = kvcalloc(num_vqs, sizeof(*cmds), GFP_KERNEL); + cmd_mem = kvcalloc(num_vqs, sizeof(*cmd_mem), GFP_KERNEL); + if (!cmds || !cmd_mem) { + err = -ENOMEM; + goto done; + } + + for (int i = 0; i < num_vqs; i++) { + struct mlx5_vdpa_async_cmd *cmd = &cmds[i]; + struct mlx5_vdpa_virtqueue *mvq; + int vq_idx = start_vq + i; + + mvq = &ndev->vqs[vq_idx]; + + if (!modifiable_virtqueue_fields(mvq)) { + err = -EINVAL; + goto done; + } + + if (mvq->fw_state != state) { + if (!is_valid_state_change(mvq->fw_state, state, is_resumable(ndev))) { + err = -EINVAL; + goto done; + } + + mvq->modified_fields |= MLX5_VIRTQ_MODIFY_MASK_STATE; + } + + cmd->in = &cmd_mem[i].in; + cmd->inlen = sizeof(cmd_mem[i].in); + cmd->out = &cmd_mem[i].out; + cmd->outlen = sizeof(cmd_mem[i].out); + fill_modify_virtqueue_cmd(ndev, mvq, state, &cmd_mem[i]); + } + + err = mlx5_vdpa_exec_async_cmds(&ndev->mvdev, cmds, num_vqs); + if (err) { + mlx5_vdpa_err(mvdev, "error issuing modify cmd for vq range [%d, %d)\n", + start_vq, start_vq + num_vqs); + goto done; + } + + for (int i = 0; i < num_vqs; i++) { + struct mlx5_vdpa_async_cmd *cmd = &cmds[i]; + struct mlx5_vdpa_virtqueue *mvq; + int vq_idx = start_vq + i; + + mvq = &ndev->vqs[vq_idx]; + + if (cmd->err) { + mlx5_vdpa_err(mvdev, "modify vq %d failed, state: %d -> %d, err: %d\n", + vq_idx, mvq->fw_state, state, err); + if (!err) + err = cmd->err; + continue; + } + + modify_virtqueue_end(ndev, mvq, state); + } + +done: + kvfree(cmd_mem); + kvfree(cmds); + return err; +} + +static int suspend_vqs(struct mlx5_vdpa_net *ndev, int start_vq, int num_vqs) +{ + struct mlx5_vdpa_virtqueue *mvq; + struct mlx5_virtq_attr *attrs; + int vq_idx, i; int err; + if (start_vq >= ndev->cur_num_vqs) + return -EINVAL; + + mvq = &ndev->vqs[start_vq]; if (!mvq->initialized) return 0; if (mvq->fw_state != MLX5_VIRTIO_NET_Q_OBJECT_STATE_RDY) return 0; - err = modify_virtqueue_state(ndev, mvq, MLX5_VIRTIO_NET_Q_OBJECT_STATE_SUSPEND); - if (err) { - mlx5_vdpa_warn(&ndev->mvdev, "modify to suspend failed, err: %d\n", err); + err = modify_virtqueues(ndev, start_vq, num_vqs, MLX5_VIRTIO_NET_Q_OBJECT_STATE_SUSPEND); + if (err) return err; + + attrs = kcalloc(num_vqs, sizeof(struct mlx5_virtq_attr), GFP_KERNEL); + if (!attrs) + return -ENOMEM; + + err = query_virtqueues(ndev, start_vq, num_vqs, attrs); + if (err) + goto done; + + for (i = 0, vq_idx = start_vq; i < num_vqs; i++, vq_idx++) { + mvq = &ndev->vqs[vq_idx]; + mvq->avail_idx = attrs[i].available_index; + mvq->used_idx = attrs[i].used_index; } - err = query_virtqueue(ndev, mvq, &attr); - if (err) { - mlx5_vdpa_warn(&ndev->mvdev, "failed to query virtqueue, err: %d\n", err); - return err; - } - - mvq->avail_idx = attr.available_index; - mvq->used_idx = attr.used_index; - - return 0; -} - -static int suspend_vqs(struct mlx5_vdpa_net *ndev) -{ - int err = 0; - int i; - - for (i = 0; i < ndev->cur_num_vqs; i++) { - int local_err = suspend_vq(ndev, &ndev->vqs[i]); - - err = local_err ? local_err : err; - } - +done: + kfree(attrs); return err; } -static int resume_vq(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq) +static int suspend_vq(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq) { + return suspend_vqs(ndev, mvq->index, 1); +} + +static int resume_vqs(struct mlx5_vdpa_net *ndev, int start_vq, int num_vqs) +{ + struct mlx5_vdpa_virtqueue *mvq; int err; + if (start_vq >= ndev->mvdev.max_vqs) + return -EINVAL; + + mvq = &ndev->vqs[start_vq]; if (!mvq->initialized) return 0; @@ -1583,13 +1695,9 @@ static int resume_vq(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq /* Due to a FW quirk we need to modify the VQ fields first then change state. * This should be fixed soon. After that, a single command can be used. */ - err = modify_virtqueue(ndev, mvq, 0); - if (err) { - mlx5_vdpa_warn(&ndev->mvdev, - "modify vq properties failed for vq %u, err: %d\n", - mvq->index, err); + err = modify_virtqueues(ndev, start_vq, num_vqs, mvq->fw_state); + if (err) return err; - } break; case MLX5_VIRTIO_NET_Q_OBJECT_STATE_SUSPEND: if (!is_resumable(ndev)) { @@ -1600,30 +1708,17 @@ static int resume_vq(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq case MLX5_VIRTIO_NET_Q_OBJECT_STATE_RDY: return 0; default: - mlx5_vdpa_warn(&ndev->mvdev, "resume vq %u called from bad state %d\n", + mlx5_vdpa_err(&ndev->mvdev, "resume vq %u called from bad state %d\n", mvq->index, mvq->fw_state); return -EINVAL; } - err = modify_virtqueue_state(ndev, mvq, MLX5_VIRTIO_NET_Q_OBJECT_STATE_RDY); - if (err) - mlx5_vdpa_warn(&ndev->mvdev, "modify to resume failed for vq %u, err: %d\n", - mvq->index, err); - - return err; + return modify_virtqueues(ndev, start_vq, num_vqs, MLX5_VIRTIO_NET_Q_OBJECT_STATE_RDY); } -static int resume_vqs(struct mlx5_vdpa_net *ndev) +static int resume_vq(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq) { - int err = 0; - - for (int i = 0; i < ndev->cur_num_vqs; i++) { - int local_err = resume_vq(ndev, &ndev->vqs[i]); - - err = local_err ? local_err : err; - } - - return err; + return resume_vqs(ndev, mvq->index, 1); } static void teardown_vq(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq) @@ -2002,13 +2097,13 @@ static int setup_steering(struct mlx5_vdpa_net *ndev) ns = mlx5_get_flow_namespace(ndev->mvdev.mdev, MLX5_FLOW_NAMESPACE_BYPASS); if (!ns) { - mlx5_vdpa_warn(&ndev->mvdev, "failed to get flow namespace\n"); + mlx5_vdpa_err(&ndev->mvdev, "failed to get flow namespace\n"); return -EOPNOTSUPP; } ndev->rxft = mlx5_create_auto_grouped_flow_table(ns, &ft_attr); if (IS_ERR(ndev->rxft)) { - mlx5_vdpa_warn(&ndev->mvdev, "failed to create flow table\n"); + mlx5_vdpa_err(&ndev->mvdev, "failed to create flow table\n"); return PTR_ERR(ndev->rxft); } mlx5_vdpa_add_rx_flow_table(ndev); @@ -2124,45 +2219,48 @@ static virtio_net_ctrl_ack handle_ctrl_mac(struct mlx5_vdpa_dev *mvdev, u8 cmd) static int change_num_qps(struct mlx5_vdpa_dev *mvdev, int newqps) { struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev); - int cur_qps = ndev->cur_num_vqs / 2; + int cur_vqs = ndev->cur_num_vqs; + int new_vqs = newqps * 2; int err; int i; - if (cur_qps > newqps) { - err = modify_rqt(ndev, 2 * newqps); + if (cur_vqs > new_vqs) { + err = modify_rqt(ndev, new_vqs); if (err) return err; - for (i = ndev->cur_num_vqs - 1; i >= 2 * newqps; i--) { - struct mlx5_vdpa_virtqueue *mvq = &ndev->vqs[i]; - - if (is_resumable(ndev)) - suspend_vq(ndev, mvq); - else - teardown_vq(ndev, mvq); + if (is_resumable(ndev)) { + suspend_vqs(ndev, new_vqs, cur_vqs - new_vqs); + } else { + for (i = new_vqs; i < cur_vqs; i++) + teardown_vq(ndev, &ndev->vqs[i]); } - ndev->cur_num_vqs = 2 * newqps; + ndev->cur_num_vqs = new_vqs; } else { - ndev->cur_num_vqs = 2 * newqps; - for (i = cur_qps * 2; i < 2 * newqps; i++) { - struct mlx5_vdpa_virtqueue *mvq = &ndev->vqs[i]; + ndev->cur_num_vqs = new_vqs; - err = mvq->initialized ? resume_vq(ndev, mvq) : setup_vq(ndev, mvq, true); + for (i = cur_vqs; i < new_vqs; i++) { + err = setup_vq(ndev, &ndev->vqs[i], false); if (err) goto clean_added; } - err = modify_rqt(ndev, 2 * newqps); + + err = resume_vqs(ndev, cur_vqs, new_vqs - cur_vqs); + if (err) + goto clean_added; + + err = modify_rqt(ndev, new_vqs); if (err) goto clean_added; } return 0; clean_added: - for (--i; i >= 2 * cur_qps; --i) + for (--i; i >= cur_vqs; --i) teardown_vq(ndev, &ndev->vqs[i]); - ndev->cur_num_vqs = 2 * cur_qps; + ndev->cur_num_vqs = cur_vqs; return err; } @@ -2528,9 +2626,9 @@ static int mlx5_vdpa_get_vq_state(struct vdpa_device *vdev, u16 idx, struct vdpa return 0; } - err = query_virtqueue(ndev, mvq, &attr); + err = query_virtqueues(ndev, mvq->index, 1, &attr); if (err) { - mlx5_vdpa_warn(mvdev, "failed to query virtqueue\n"); + mlx5_vdpa_err(mvdev, "failed to query virtqueue\n"); return err; } state->split.avail_index = attr.used_index; @@ -2755,6 +2853,9 @@ static int event_handler(struct notifier_block *nb, unsigned long event, void *p struct mlx5_eqe *eqe = param; int ret = NOTIFY_DONE; + if (ndev->mvdev.suspended) + return NOTIFY_DONE; + if (event == MLX5_EVENT_TYPE_PORT_CHANGE) { switch (eqe->sub_type) { case MLX5_PORT_CHANGE_SUBTYPE_DOWN: @@ -2879,7 +2980,7 @@ static int save_channel_info(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqu int err; if (mvq->initialized) { - err = query_virtqueue(ndev, mvq, &attr); + err = query_virtqueues(ndev, mvq->index, 1, &attr); if (err) return err; } @@ -2948,7 +3049,7 @@ static int mlx5_vdpa_change_map(struct mlx5_vdpa_dev *mvdev, bool teardown = !is_resumable(ndev); int err; - suspend_vqs(ndev); + suspend_vqs(ndev, 0, ndev->cur_num_vqs); if (teardown) { err = save_channels_info(ndev); if (err) @@ -2973,7 +3074,7 @@ static int mlx5_vdpa_change_map(struct mlx5_vdpa_dev *mvdev, return err; } - resume_vqs(ndev); + resume_vqs(ndev, 0, ndev->cur_num_vqs); return 0; } @@ -3097,7 +3198,7 @@ static void mlx5_vdpa_set_status(struct vdpa_device *vdev, u8 status) teardown_vq_resources(ndev); if (ndev->setup) { - err = resume_vqs(ndev); + err = resume_vqs(ndev, 0, ndev->cur_num_vqs); if (err) { mlx5_vdpa_warn(mvdev, "failed to resume VQs\n"); goto err_driver; @@ -3122,7 +3223,7 @@ static void mlx5_vdpa_set_status(struct vdpa_device *vdev, u8 status) err_driver: unregister_link_notifier(ndev); err_setup: - mlx5_vdpa_destroy_mr_resources(&ndev->mvdev); + mlx5_vdpa_clean_mrs(&ndev->mvdev); ndev->mvdev.status |= VIRTIO_CONFIG_S_FAILED; err_clear: up_write(&ndev->reslock); @@ -3134,7 +3235,7 @@ static void init_group_to_asid_map(struct mlx5_vdpa_dev *mvdev) /* default mapping all groups are mapped to asid 0 */ for (i = 0; i < MLX5_VDPA_NUMVQ_GROUPS; i++) - mvdev->group2asid[i] = 0; + mvdev->mres.group2asid[i] = 0; } static bool needs_vqs_reset(const struct mlx5_vdpa_dev *mvdev) @@ -3174,7 +3275,7 @@ static int mlx5_vdpa_compat_reset(struct vdpa_device *vdev, u32 flags) } if (flags & VDPA_RESET_F_CLEAN_MAP) - mlx5_vdpa_destroy_mr_resources(&ndev->mvdev); + mlx5_vdpa_clean_mrs(&ndev->mvdev); ndev->mvdev.status = 0; ndev->mvdev.suspended = false; ndev->cur_num_vqs = MLX5V_DEFAULT_VQ_COUNT; @@ -3189,7 +3290,7 @@ static int mlx5_vdpa_compat_reset(struct vdpa_device *vdev, u32 flags) if ((flags & VDPA_RESET_F_CLEAN_MAP) && MLX5_CAP_GEN(mvdev->mdev, umem_uid_0)) { if (mlx5_vdpa_create_dma_mr(mvdev)) - mlx5_vdpa_warn(mvdev, "create MR failed\n"); + mlx5_vdpa_err(mvdev, "create MR failed\n"); } if (vq_reset) setup_vq_resources(ndev, false); @@ -3244,7 +3345,7 @@ static int set_map_data(struct mlx5_vdpa_dev *mvdev, struct vhost_iotlb *iotlb, new_mr = mlx5_vdpa_create_mr(mvdev, iotlb); if (IS_ERR(new_mr)) { err = PTR_ERR(new_mr); - mlx5_vdpa_warn(mvdev, "create map failed(%d)\n", err); + mlx5_vdpa_err(mvdev, "create map failed(%d)\n", err); return err; } } else { @@ -3252,12 +3353,12 @@ static int set_map_data(struct mlx5_vdpa_dev *mvdev, struct vhost_iotlb *iotlb, new_mr = NULL; } - if (!mvdev->mr[asid]) { + if (!mvdev->mres.mr[asid]) { mlx5_vdpa_update_mr(mvdev, new_mr, asid); } else { err = mlx5_vdpa_change_map(mvdev, new_mr, asid); if (err) { - mlx5_vdpa_warn(mvdev, "change map failed(%d)\n", err); + mlx5_vdpa_err(mvdev, "change map failed(%d)\n", err); goto out_err; } } @@ -3332,7 +3433,10 @@ static void mlx5_vdpa_free(struct vdpa_device *vdev) ndev = to_mlx5_vdpa_ndev(mvdev); free_fixed_resources(ndev); - mlx5_vdpa_destroy_mr_resources(mvdev); + mlx5_vdpa_clean_mrs(mvdev); + mlx5_vdpa_destroy_mr_resources(&ndev->mvdev); + mlx5_cmd_cleanup_async_ctx(&mvdev->async_ctx); + if (!is_zero_ether_addr(ndev->config.mac)) { pfmdev = pci_get_drvdata(pci_physfn(mvdev->mdev->pdev)); mlx5_mpfs_del_mac(pfmdev, ndev->config.mac); @@ -3500,8 +3604,7 @@ static int mlx5_vdpa_suspend(struct vdpa_device *vdev) mlx5_vdpa_info(mvdev, "suspending device\n"); down_write(&ndev->reslock); - unregister_link_notifier(ndev); - err = suspend_vqs(ndev); + err = suspend_vqs(ndev, 0, ndev->cur_num_vqs); mlx5_vdpa_cvq_suspend(mvdev); mvdev->suspended = true; up_write(&ndev->reslock); @@ -3521,8 +3624,8 @@ static int mlx5_vdpa_resume(struct vdpa_device *vdev) down_write(&ndev->reslock); mvdev->suspended = false; - err = resume_vqs(ndev); - register_link_notifier(ndev); + err = resume_vqs(ndev, 0, ndev->cur_num_vqs); + queue_link_work(ndev); up_write(&ndev->reslock); return err; @@ -3537,12 +3640,12 @@ static int mlx5_set_group_asid(struct vdpa_device *vdev, u32 group, if (group >= MLX5_VDPA_NUMVQ_GROUPS) return -EINVAL; - mvdev->group2asid[group] = asid; + mvdev->mres.group2asid[group] = asid; - mutex_lock(&mvdev->mr_mtx); - if (group == MLX5_VDPA_CVQ_GROUP && mvdev->mr[asid]) - err = mlx5_vdpa_update_cvq_iotlb(mvdev, mvdev->mr[asid]->iotlb, asid); - mutex_unlock(&mvdev->mr_mtx); + mutex_lock(&mvdev->mres.lock); + if (group == MLX5_VDPA_CVQ_GROUP && mvdev->mres.mr[asid]) + err = mlx5_vdpa_update_cvq_iotlb(mvdev, mvdev->mres.mr[asid]->iotlb, asid); + mutex_unlock(&mvdev->mres.lock); return err; } @@ -3854,18 +3957,22 @@ static int mlx5_vdpa_dev_add(struct vdpa_mgmt_dev *v_mdev, const char *name, ndev->rqt_size = 1; } + mlx5_cmd_init_async_ctx(mdev, &mvdev->async_ctx); + ndev->mvdev.mlx_features = device_features; mvdev->vdev.dma_dev = &mdev->pdev->dev; err = mlx5_vdpa_alloc_resources(&ndev->mvdev); if (err) goto err_mpfs; - INIT_LIST_HEAD(&mvdev->mr_list_head); + err = mlx5_vdpa_init_mr_resources(mvdev); + if (err) + goto err_res; if (MLX5_CAP_GEN(mvdev->mdev, umem_uid_0)) { err = mlx5_vdpa_create_dma_mr(mvdev); if (err) - goto err_res; + goto err_mr_res; } err = alloc_fixed_resources(ndev); @@ -3906,6 +4013,8 @@ err_reg: err_res2: free_fixed_resources(ndev); err_mr: + mlx5_vdpa_clean_mrs(mvdev); +err_mr_res: mlx5_vdpa_destroy_mr_resources(mvdev); err_res: mlx5_vdpa_free_resources(&ndev->mvdev); @@ -3937,9 +4046,37 @@ static void mlx5_vdpa_dev_del(struct vdpa_mgmt_dev *v_mdev, struct vdpa_device * mgtdev->ndev = NULL; } +static int mlx5_vdpa_set_attr(struct vdpa_mgmt_dev *v_mdev, struct vdpa_device *dev, + const struct vdpa_dev_set_config *add_config) +{ + struct virtio_net_config *config; + struct mlx5_core_dev *pfmdev; + struct mlx5_vdpa_dev *mvdev; + struct mlx5_vdpa_net *ndev; + struct mlx5_core_dev *mdev; + int err = -EOPNOTSUPP; + + mvdev = to_mvdev(dev); + ndev = to_mlx5_vdpa_ndev(mvdev); + mdev = mvdev->mdev; + config = &ndev->config; + + down_write(&ndev->reslock); + if (add_config->mask & (1 << VDPA_ATTR_DEV_NET_CFG_MACADDR)) { + pfmdev = pci_get_drvdata(pci_physfn(mdev->pdev)); + err = mlx5_mpfs_add_mac(pfmdev, config->mac); + if (!err) + ether_addr_copy(config->mac, add_config->net.mac); + } + + up_write(&ndev->reslock); + return err; +} + static const struct vdpa_mgmtdev_ops mdev_ops = { .dev_add = mlx5_vdpa_dev_add, .dev_del = mlx5_vdpa_dev_del, + .dev_set_attr = mlx5_vdpa_set_attr, }; static struct virtio_device_id id_table[] = { diff --git a/drivers/vdpa/pds/cmds.h b/drivers/vdpa/pds/cmds.h index e24d85cb8f1c..6b1bc33356b0 100644 --- a/drivers/vdpa/pds/cmds.h +++ b/drivers/vdpa/pds/cmds.h @@ -14,5 +14,4 @@ int pds_vdpa_cmd_init_vq(struct pds_vdpa_device *pdsv, u16 qid, u16 invert_idx, struct pds_vdpa_vq_info *vq_info); int pds_vdpa_cmd_reset_vq(struct pds_vdpa_device *pdsv, u16 qid, u16 invert_idx, struct pds_vdpa_vq_info *vq_info); -int pds_vdpa_cmd_set_features(struct pds_vdpa_device *pdsv, u64 features); #endif /* _VDPA_CMDS_H_ */ diff --git a/drivers/vdpa/vdpa.c b/drivers/vdpa/vdpa.c index 4dbd2e55a288..8a372b51c21a 100644 --- a/drivers/vdpa/vdpa.c +++ b/drivers/vdpa/vdpa.c @@ -1361,6 +1361,80 @@ dev_err: return err; } +static int vdpa_dev_net_device_attr_set(struct vdpa_device *vdev, + struct genl_info *info) +{ + struct vdpa_dev_set_config set_config = {}; + struct vdpa_mgmt_dev *mdev = vdev->mdev; + struct nlattr **nl_attrs = info->attrs; + const u8 *macaddr; + int err = -EOPNOTSUPP; + + down_write(&vdev->cf_lock); + if (nl_attrs[VDPA_ATTR_DEV_NET_CFG_MACADDR]) { + set_config.mask |= BIT_ULL(VDPA_ATTR_DEV_NET_CFG_MACADDR); + macaddr = nla_data(nl_attrs[VDPA_ATTR_DEV_NET_CFG_MACADDR]); + + if (is_valid_ether_addr(macaddr)) { + ether_addr_copy(set_config.net.mac, macaddr); + if (mdev->ops->dev_set_attr) { + err = mdev->ops->dev_set_attr(mdev, vdev, + &set_config); + } else { + NL_SET_ERR_MSG_FMT_MOD(info->extack, + "Operation not supported by the device."); + } + } else { + NL_SET_ERR_MSG_FMT_MOD(info->extack, + "Invalid MAC address"); + } + } + up_write(&vdev->cf_lock); + return err; +} + +static int vdpa_nl_cmd_dev_attr_set_doit(struct sk_buff *skb, + struct genl_info *info) +{ + struct vdpa_device *vdev; + struct device *dev; + const char *name; + u64 classes; + int err = 0; + + if (!info->attrs[VDPA_ATTR_DEV_NAME]) + return -EINVAL; + + name = nla_data(info->attrs[VDPA_ATTR_DEV_NAME]); + + down_write(&vdpa_dev_lock); + dev = bus_find_device(&vdpa_bus, NULL, name, vdpa_name_match); + if (!dev) { + NL_SET_ERR_MSG_MOD(info->extack, "device not found"); + err = -ENODEV; + goto dev_err; + } + vdev = container_of(dev, struct vdpa_device, dev); + if (!vdev->mdev) { + NL_SET_ERR_MSG_MOD(info->extack, "unmanaged vdpa device"); + err = -EINVAL; + goto mdev_err; + } + classes = vdpa_mgmtdev_get_classes(vdev->mdev, NULL); + if (classes & BIT_ULL(VIRTIO_ID_NET)) { + err = vdpa_dev_net_device_attr_set(vdev, info); + } else { + NL_SET_ERR_MSG_FMT_MOD(info->extack, "%s device not supported", + name); + } + +mdev_err: + put_device(dev); +dev_err: + up_write(&vdpa_dev_lock); + return err; +} + static int vdpa_dev_config_dump(struct device *dev, void *data) { struct vdpa_device *vdev = container_of(dev, struct vdpa_device, dev); @@ -1497,6 +1571,11 @@ static const struct genl_ops vdpa_nl_ops[] = { .doit = vdpa_nl_cmd_dev_stats_get_doit, .flags = GENL_ADMIN_PERM, }, + { + .cmd = VDPA_CMD_DEV_ATTR_SET, + .doit = vdpa_nl_cmd_dev_attr_set_doit, + .flags = GENL_ADMIN_PERM, + }, }; static struct genl_family vdpa_nl_family __ro_after_init = { diff --git a/drivers/vdpa/vdpa_sim/vdpa_sim_net.c b/drivers/vdpa/vdpa_sim/vdpa_sim_net.c index cfe962911804..6caf09a1907b 100644 --- a/drivers/vdpa/vdpa_sim/vdpa_sim_net.c +++ b/drivers/vdpa/vdpa_sim/vdpa_sim_net.c @@ -414,6 +414,24 @@ static void vdpasim_net_get_config(struct vdpasim *vdpasim, void *config) net_config->status = cpu_to_vdpasim16(vdpasim, VIRTIO_NET_S_LINK_UP); } +static int vdpasim_net_set_attr(struct vdpa_mgmt_dev *mdev, struct vdpa_device *dev, + const struct vdpa_dev_set_config *config) +{ + struct vdpasim *vdpasim = container_of(dev, struct vdpasim, vdpa); + struct virtio_net_config *vio_config = vdpasim->config; + + mutex_lock(&vdpasim->mutex); + + if (config->mask & (1 << VDPA_ATTR_DEV_NET_CFG_MACADDR)) { + ether_addr_copy(vio_config->mac, config->net.mac); + mutex_unlock(&vdpasim->mutex); + return 0; + } + + mutex_unlock(&vdpasim->mutex); + return -EOPNOTSUPP; +} + static void vdpasim_net_setup_config(struct vdpasim *vdpasim, const struct vdpa_dev_set_config *config) { @@ -510,7 +528,8 @@ static void vdpasim_net_dev_del(struct vdpa_mgmt_dev *mdev, static const struct vdpa_mgmtdev_ops vdpasim_net_mgmtdev_ops = { .dev_add = vdpasim_net_dev_add, - .dev_del = vdpasim_net_dev_del + .dev_del = vdpasim_net_dev_del, + .dev_set_attr = vdpasim_net_set_attr }; static struct virtio_device_id id_table[] = { diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c index 478cd46a49ed..5a49b5a6d496 100644 --- a/drivers/vhost/vdpa.c +++ b/drivers/vhost/vdpa.c @@ -209,11 +209,9 @@ static void vhost_vdpa_setup_vq_irq(struct vhost_vdpa *v, u16 qid) if (irq < 0) return; - irq_bypass_unregister_producer(&vq->call_ctx.producer); if (!vq->call_ctx.ctx) return; - vq->call_ctx.producer.token = vq->call_ctx.ctx; vq->call_ctx.producer.irq = irq; ret = irq_bypass_register_producer(&vq->call_ctx.producer); if (unlikely(ret)) @@ -709,6 +707,14 @@ static long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd, vq->last_avail_idx = vq_state.split.avail_index; } break; + case VHOST_SET_VRING_CALL: + if (vq->call_ctx.ctx) { + if (ops->get_status(vdpa) & + VIRTIO_CONFIG_S_DRIVER_OK) + vhost_vdpa_unsetup_vq_irq(v, idx); + vq->call_ctx.producer.token = NULL; + } + break; } r = vhost_vring_ioctl(&v->vdev, cmd, argp); @@ -747,13 +753,16 @@ static long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd, cb.callback = vhost_vdpa_virtqueue_cb; cb.private = vq; cb.trigger = vq->call_ctx.ctx; + vq->call_ctx.producer.token = vq->call_ctx.ctx; + if (ops->get_status(vdpa) & + VIRTIO_CONFIG_S_DRIVER_OK) + vhost_vdpa_setup_vq_irq(v, idx); } else { cb.callback = NULL; cb.private = NULL; cb.trigger = NULL; } ops->set_vq_cb(vdpa, idx, &cb); - vhost_vdpa_setup_vq_irq(v, idx); break; case VHOST_SET_VRING_NUM: @@ -1419,6 +1428,7 @@ static int vhost_vdpa_open(struct inode *inode, struct file *filep) for (i = 0; i < nvqs; i++) { vqs[i] = &v->vqs[i]; vqs[i]->handle_kick = handle_vq_kick; + vqs[i]->call_ctx.ctx = NULL; } vhost_dev_init(dev, vqs, nvqs, 0, 0, 0, false, vhost_vdpa_process_iotlb_msg); diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index 54469277ca30..b36d2803674e 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c @@ -355,6 +355,8 @@ static inline unsigned int update_balloon_vm_stats(struct virtio_balloon *vb) { unsigned long events[NR_VM_EVENT_ITEMS]; unsigned int idx = 0; + unsigned int zid; + unsigned long stall = 0; all_vm_events(events); update_stat(vb, idx++, VIRTIO_BALLOON_S_SWAP_IN, @@ -363,6 +365,22 @@ static inline unsigned int update_balloon_vm_stats(struct virtio_balloon *vb) pages_to_bytes(events[PSWPOUT])); update_stat(vb, idx++, VIRTIO_BALLOON_S_MAJFLT, events[PGMAJFAULT]); update_stat(vb, idx++, VIRTIO_BALLOON_S_MINFLT, events[PGFAULT]); + update_stat(vb, idx++, VIRTIO_BALLOON_S_OOM_KILL, events[OOM_KILL]); + + /* sum all the stall events */ + for (zid = 0; zid < MAX_NR_ZONES; zid++) + stall += events[ALLOCSTALL_NORMAL - ZONE_NORMAL + zid]; + + update_stat(vb, idx++, VIRTIO_BALLOON_S_ALLOC_STALL, stall); + + update_stat(vb, idx++, VIRTIO_BALLOON_S_ASYNC_SCAN, + pages_to_bytes(events[PGSCAN_KSWAPD])); + update_stat(vb, idx++, VIRTIO_BALLOON_S_DIRECT_SCAN, + pages_to_bytes(events[PGSCAN_DIRECT])); + update_stat(vb, idx++, VIRTIO_BALLOON_S_ASYNC_RECLAIM, + pages_to_bytes(events[PGSTEAL_KSWAPD])); + update_stat(vb, idx++, VIRTIO_BALLOON_S_DIRECT_RECLAIM, + pages_to_bytes(events[PGSTEAL_DIRECT])); #ifdef CONFIG_HUGETLB_PAGE update_stat(vb, idx++, VIRTIO_BALLOON_S_HTLB_PGALLOC, diff --git a/fs/fuse/virtio_fs.c b/fs/fuse/virtio_fs.c index b6bd6ab71f5d..6404a189e989 100644 --- a/fs/fuse/virtio_fs.c +++ b/fs/fuse/virtio_fs.c @@ -56,12 +56,14 @@ struct virtio_fs_vq { bool connected; long in_flight; struct completion in_flight_zero; /* No inflight requests */ + struct kobject *kobj; char name[VQ_NAME_LEN]; } ____cacheline_aligned_in_smp; /* A virtio-fs device instance */ struct virtio_fs { struct kobject kobj; + struct kobject *mqs_kobj; struct list_head list; /* on virtio_fs_instances */ char *tag; struct virtio_fs_vq *vqs; @@ -200,19 +202,94 @@ static const struct kobj_type virtio_fs_ktype = { .default_groups = virtio_fs_groups, }; +static struct virtio_fs_vq *virtio_fs_kobj_to_vq(struct virtio_fs *fs, + struct kobject *kobj) +{ + int i; + + for (i = 0; i < fs->nvqs; i++) { + if (kobj == fs->vqs[i].kobj) + return &fs->vqs[i]; + } + return NULL; +} + +static ssize_t name_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct virtio_fs *fs = container_of(kobj->parent->parent, struct virtio_fs, kobj); + struct virtio_fs_vq *fsvq = virtio_fs_kobj_to_vq(fs, kobj); + + if (!fsvq) + return -EINVAL; + return sysfs_emit(buf, "%s\n", fsvq->name); +} + +static struct kobj_attribute virtio_fs_vq_name_attr = __ATTR_RO(name); + +static ssize_t cpu_list_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct virtio_fs *fs = container_of(kobj->parent->parent, struct virtio_fs, kobj); + struct virtio_fs_vq *fsvq = virtio_fs_kobj_to_vq(fs, kobj); + unsigned int cpu, qid; + const size_t size = PAGE_SIZE - 1; + bool first = true; + int ret = 0, pos = 0; + + if (!fsvq) + return -EINVAL; + + qid = fsvq->vq->index; + for (cpu = 0; cpu < nr_cpu_ids; cpu++) { + if (qid < VQ_REQUEST || (fs->mq_map[cpu] == qid - VQ_REQUEST)) { + if (first) + ret = snprintf(buf + pos, size - pos, "%u", cpu); + else + ret = snprintf(buf + pos, size - pos, ", %u", cpu); + + if (ret >= size - pos) + break; + first = false; + pos += ret; + } + } + ret = snprintf(buf + pos, size + 1 - pos, "\n"); + return pos + ret; +} + +static struct kobj_attribute virtio_fs_vq_cpu_list_attr = __ATTR_RO(cpu_list); + +static struct attribute *virtio_fs_vq_attrs[] = { + &virtio_fs_vq_name_attr.attr, + &virtio_fs_vq_cpu_list_attr.attr, + NULL +}; + +static struct attribute_group virtio_fs_vq_attr_group = { + .attrs = virtio_fs_vq_attrs, +}; + /* Make sure virtiofs_mutex is held */ +static void virtio_fs_put_locked(struct virtio_fs *fs) +{ + lockdep_assert_held(&virtio_fs_mutex); + + kobject_put(&fs->kobj); +} + static void virtio_fs_put(struct virtio_fs *fs) { - kobject_put(&fs->kobj); + mutex_lock(&virtio_fs_mutex); + virtio_fs_put_locked(fs); + mutex_unlock(&virtio_fs_mutex); } static void virtio_fs_fiq_release(struct fuse_iqueue *fiq) { struct virtio_fs *vfs = fiq->priv; - mutex_lock(&virtio_fs_mutex); virtio_fs_put(vfs); - mutex_unlock(&virtio_fs_mutex); } static void virtio_fs_drain_queue(struct virtio_fs_vq *fsvq) @@ -273,6 +350,50 @@ static void virtio_fs_start_all_queues(struct virtio_fs *fs) } } +static void virtio_fs_delete_queues_sysfs(struct virtio_fs *fs) +{ + struct virtio_fs_vq *fsvq; + int i; + + for (i = 0; i < fs->nvqs; i++) { + fsvq = &fs->vqs[i]; + kobject_put(fsvq->kobj); + } +} + +static int virtio_fs_add_queues_sysfs(struct virtio_fs *fs) +{ + struct virtio_fs_vq *fsvq; + char buff[12]; + int i, j, ret; + + for (i = 0; i < fs->nvqs; i++) { + fsvq = &fs->vqs[i]; + + sprintf(buff, "%d", i); + fsvq->kobj = kobject_create_and_add(buff, fs->mqs_kobj); + if (!fs->mqs_kobj) { + ret = -ENOMEM; + goto out_del; + } + + ret = sysfs_create_group(fsvq->kobj, &virtio_fs_vq_attr_group); + if (ret) { + kobject_put(fsvq->kobj); + goto out_del; + } + } + + return 0; + +out_del: + for (j = 0; j < i; j++) { + fsvq = &fs->vqs[j]; + kobject_put(fsvq->kobj); + } + return ret; +} + /* Add a new instance to the list or return -EEXIST if tag name exists*/ static int virtio_fs_add_instance(struct virtio_device *vdev, struct virtio_fs *fs) @@ -296,17 +417,22 @@ static int virtio_fs_add_instance(struct virtio_device *vdev, */ fs->kobj.kset = virtio_fs_kset; ret = kobject_add(&fs->kobj, NULL, "%d", vdev->index); - if (ret < 0) { - mutex_unlock(&virtio_fs_mutex); - return ret; + if (ret < 0) + goto out_unlock; + + fs->mqs_kobj = kobject_create_and_add("mqs", &fs->kobj); + if (!fs->mqs_kobj) { + ret = -ENOMEM; + goto out_del; } ret = sysfs_create_link(&fs->kobj, &vdev->dev.kobj, "device"); - if (ret < 0) { - kobject_del(&fs->kobj); - mutex_unlock(&virtio_fs_mutex); - return ret; - } + if (ret < 0) + goto out_put; + + ret = virtio_fs_add_queues_sysfs(fs); + if (ret) + goto out_remove; list_add_tail(&fs->list, &virtio_fs_instances); @@ -315,6 +441,16 @@ static int virtio_fs_add_instance(struct virtio_device *vdev, kobject_uevent(&fs->kobj, KOBJ_ADD); return 0; + +out_remove: + sysfs_remove_link(&fs->kobj, "device"); +out_put: + kobject_put(fs->mqs_kobj); +out_del: + kobject_del(&fs->kobj); +out_unlock: + mutex_unlock(&virtio_fs_mutex); + return ret; } /* Return the virtio_fs with a given tag, or NULL */ @@ -1043,7 +1179,9 @@ static void virtio_fs_remove(struct virtio_device *vdev) mutex_lock(&virtio_fs_mutex); /* This device is going away. No one should get new reference */ list_del_init(&fs->list); + virtio_fs_delete_queues_sysfs(fs); sysfs_remove_link(&fs->kobj, "device"); + kobject_put(fs->mqs_kobj); kobject_del(&fs->kobj); virtio_fs_stop_all_queues(fs); virtio_fs_drain_all_queues_locked(fs); @@ -1052,7 +1190,7 @@ static void virtio_fs_remove(struct virtio_device *vdev) vdev->priv = NULL; /* Put device reference on virtio_fs object */ - virtio_fs_put(fs); + virtio_fs_put_locked(fs); mutex_unlock(&virtio_fs_mutex); } @@ -1581,9 +1719,7 @@ static int virtio_fs_get_tree(struct fs_context *fsc) out_err: kfree(fc); - mutex_lock(&virtio_fs_mutex); virtio_fs_put(fs); - mutex_unlock(&virtio_fs_mutex); return err; } diff --git a/fs/netfs/buffered_write.c b/fs/netfs/buffered_write.c index d7eae597e54d..b3910dfcb56d 100644 --- a/fs/netfs/buffered_write.c +++ b/fs/netfs/buffered_write.c @@ -552,6 +552,7 @@ vm_fault_t netfs_page_mkwrite(struct vm_fault *vmf, struct netfs_group *netfs_gr trace_netfs_folio(folio, netfs_folio_trace_mkwrite); netfs_set_group(folio, netfs_group); file_update_time(file); + set_bit(NETFS_ICTX_MODIFIED_ATTR, &ictx->flags); if (ictx->ops->post_modify) ictx->ops->post_modify(inode); ret = VM_FAULT_LOCKED; diff --git a/fs/smb/client/cifsfs.h b/fs/smb/client/cifsfs.h index 61ded59b858f..71b720dbb2ce 100644 --- a/fs/smb/client/cifsfs.h +++ b/fs/smb/client/cifsfs.h @@ -146,6 +146,6 @@ extern const struct export_operations cifs_export_ops; #endif /* CONFIG_CIFS_NFSD_EXPORT */ /* when changing internal version - update following two lines at same time */ -#define SMB3_PRODUCT_BUILD 50 -#define CIFS_VERSION "2.50" +#define SMB3_PRODUCT_BUILD 51 +#define CIFS_VERSION "2.51" #endif /* _CIFSFS_H */ diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h index a71a988a92f9..15571cf0ba63 100644 --- a/fs/smb/client/cifsglob.h +++ b/fs/smb/client/cifsglob.h @@ -821,6 +821,7 @@ struct TCP_Server_Info { * format: \\HOST\SHARE[\OPTIONAL PATH] */ char *leaf_fullpath; + bool dfs_conn:1; }; static inline bool is_smb1(struct TCP_Server_Info *server) @@ -1059,6 +1060,7 @@ struct cifs_ses { struct list_head smb_ses_list; struct list_head rlist; /* reconnect list */ struct list_head tcon_list; + struct list_head dlist; /* dfs list */ struct cifs_tcon *tcon_ipc; spinlock_t ses_lock; /* protect anything here that is not protected */ struct mutex session_mutex; @@ -1287,6 +1289,7 @@ struct cifs_tcon { /* BB add field for back pointer to sb struct(s)? */ #ifdef CONFIG_CIFS_DFS_UPCALL struct delayed_work dfs_cache_work; + struct list_head dfs_ses_list; #endif struct delayed_work query_interfaces; /* query interfaces workqueue job */ char *origin_fullpath; /* canonical copy of smb3_fs_context::source */ diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h index c69e3f48a60c..68c716e6261b 100644 --- a/fs/smb/client/cifsproto.h +++ b/fs/smb/client/cifsproto.h @@ -724,15 +724,9 @@ static inline int cifs_create_options(struct cifs_sb_info *cifs_sb, int options) int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry); -/* Put references of @ses and its children */ static inline void cifs_put_smb_ses(struct cifs_ses *ses) { - struct cifs_ses *next; - - do { - next = ses->dfs_root_ses; - __cifs_put_smb_ses(ses); - } while ((ses = next)); + __cifs_put_smb_ses(ses); } /* Get an active reference of @ses and its children. @@ -746,9 +740,7 @@ static inline void cifs_put_smb_ses(struct cifs_ses *ses) static inline void cifs_smb_ses_inc_refcount(struct cifs_ses *ses) { lockdep_assert_held(&cifs_tcp_ses_lock); - - for (; ses; ses = ses->dfs_root_ses) - ses->ses_count++; + ses->ses_count++; } static inline bool dfs_src_pathname_equal(const char *s1, const char *s2) diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c index 08a41c7aaf72..adf8758847f6 100644 --- a/fs/smb/client/connect.c +++ b/fs/smb/client/connect.c @@ -811,13 +811,9 @@ cifs_read_iter_from_socket(struct TCP_Server_Info *server, struct iov_iter *iter unsigned int to_read) { struct msghdr smb_msg = { .msg_iter = *iter }; - int ret; iov_iter_truncate(&smb_msg.msg_iter, to_read); - ret = cifs_readv_from_socket(server, &smb_msg); - if (ret > 0) - iov_iter_advance(iter, ret); - return ret; + return cifs_readv_from_socket(server, &smb_msg); } static bool @@ -1530,6 +1526,9 @@ static int match_server(struct TCP_Server_Info *server, if (server->nosharesock) return 0; + if (!match_super && (ctx->dfs_conn || server->dfs_conn)) + return 0; + /* If multidialect negotiation see if existing sessions match one */ if (strcmp(ctx->vals->version_string, SMB3ANY_VERSION_STRING) == 0) { if (server->vals->protocol_id < SMB30_PROT_ID) @@ -1723,6 +1722,7 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx, if (ctx->nosharesock) tcp_ses->nosharesock = true; + tcp_ses->dfs_conn = ctx->dfs_conn; tcp_ses->ops = ctx->ops; tcp_ses->vals = ctx->vals; @@ -1873,13 +1873,15 @@ out_err: } /* this function must be called with ses_lock and chan_lock held */ -static int match_session(struct cifs_ses *ses, struct smb3_fs_context *ctx) +static int match_session(struct cifs_ses *ses, + struct smb3_fs_context *ctx, + bool match_super) { if (ctx->sectype != Unspecified && ctx->sectype != ses->sectype) return 0; - if (ctx->dfs_root_ses != ses->dfs_root_ses) + if (!match_super && ctx->dfs_root_ses != ses->dfs_root_ses) return 0; /* @@ -1998,7 +2000,7 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) continue; } spin_lock(&ses->chan_lock); - if (match_session(ses, ctx)) { + if (match_session(ses, ctx, false)) { spin_unlock(&ses->chan_lock); spin_unlock(&ses->ses_lock); ret = ses; @@ -2058,8 +2060,7 @@ void __cifs_put_smb_ses(struct cifs_ses *ses) if (do_logoff) { xid = get_xid(); rc = server->ops->logoff(xid, ses); - if (rc) - cifs_server_dbg(VFS, "%s: Session Logoff failure rc=%d\n", + cifs_server_dbg(FYI, "%s: Session Logoff: rc=%d\n", __func__, rc); _free_xid(xid); } @@ -2382,8 +2383,6 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) * need to lock before changing something in the session. */ spin_lock(&cifs_tcp_ses_lock); - if (ctx->dfs_root_ses) - cifs_smb_ses_inc_refcount(ctx->dfs_root_ses); ses->dfs_root_ses = ctx->dfs_root_ses; list_add(&ses->smb_ses_list, &server->smb_ses_list); spin_unlock(&cifs_tcp_ses_lock); @@ -2458,6 +2457,7 @@ cifs_put_tcon(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace) { unsigned int xid; struct cifs_ses *ses; + LIST_HEAD(ses_list); /* * IPC tcon share the lifetime of their session and are @@ -2482,6 +2482,9 @@ cifs_put_tcon(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace) list_del_init(&tcon->tcon_list); tcon->status = TID_EXITING; +#ifdef CONFIG_CIFS_DFS_UPCALL + list_replace_init(&tcon->dfs_ses_list, &ses_list); +#endif spin_unlock(&tcon->tc_lock); spin_unlock(&cifs_tcp_ses_lock); @@ -2509,6 +2512,9 @@ cifs_put_tcon(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace) cifs_fscache_release_super_cookie(tcon); tconInfoFree(tcon, netfs_trace_tcon_ref_free); cifs_put_smb_ses(ses); +#ifdef CONFIG_CIFS_DFS_UPCALL + dfs_put_root_smb_sessions(&ses_list); +#endif } /** @@ -2892,7 +2898,7 @@ cifs_match_super(struct super_block *sb, void *data) spin_lock(&ses->chan_lock); spin_lock(&tcon->tc_lock); if (!match_server(tcp_srv, ctx, true) || - !match_session(ses, ctx) || + !match_session(ses, ctx, true) || !match_tcon(tcon, ctx) || !match_prepath(sb, tcon, mnt_data)) { rc = 0; @@ -3623,13 +3629,12 @@ out: int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) { struct cifs_mount_ctx mnt_ctx = { .cifs_sb = cifs_sb, .fs_ctx = ctx, }; - bool isdfs; int rc; - rc = dfs_mount_share(&mnt_ctx, &isdfs); + rc = dfs_mount_share(&mnt_ctx); if (rc) goto error; - if (!isdfs) + if (!ctx->dfs_conn) goto out; /* @@ -4034,7 +4039,7 @@ cifs_set_vol_auth(struct smb3_fs_context *ctx, struct cifs_ses *ses) } static struct cifs_tcon * -__cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid) +cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid) { int rc; struct cifs_tcon *master_tcon = cifs_sb_master_tcon(cifs_sb); @@ -4132,17 +4137,6 @@ out: return tcon; } -static struct cifs_tcon * -cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid) -{ - struct cifs_tcon *ret; - - cifs_mount_lock(); - ret = __cifs_construct_tcon(cifs_sb, fsuid); - cifs_mount_unlock(); - return ret; -} - struct cifs_tcon * cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb) { @@ -4212,9 +4206,9 @@ tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink) struct tcon_link * cifs_sb_tlink(struct cifs_sb_info *cifs_sb) { - int ret; - kuid_t fsuid = current_fsuid(); struct tcon_link *tlink, *newtlink; + kuid_t fsuid = current_fsuid(); + int err; if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)) return cifs_get_tlink(cifs_sb_master_tlink(cifs_sb)); @@ -4249,9 +4243,9 @@ cifs_sb_tlink(struct cifs_sb_info *cifs_sb) spin_unlock(&cifs_sb->tlink_tree_lock); } else { wait_for_construction: - ret = wait_on_bit(&tlink->tl_flags, TCON_LINK_PENDING, + err = wait_on_bit(&tlink->tl_flags, TCON_LINK_PENDING, TASK_INTERRUPTIBLE); - if (ret) { + if (err) { cifs_put_tlink(tlink); return ERR_PTR(-ERESTARTSYS); } @@ -4262,8 +4256,9 @@ wait_for_construction: /* return error if we tried this already recently */ if (time_before(jiffies, tlink->tl_time + TLINK_ERROR_EXPIRE)) { + err = PTR_ERR(tlink->tl_tcon); cifs_put_tlink(tlink); - return ERR_PTR(-EACCES); + return ERR_PTR(err); } if (test_and_set_bit(TCON_LINK_PENDING, &tlink->tl_flags)) @@ -4275,8 +4270,11 @@ wait_for_construction: wake_up_bit(&tlink->tl_flags, TCON_LINK_PENDING); if (IS_ERR(tlink->tl_tcon)) { + err = PTR_ERR(tlink->tl_tcon); + if (err == -ENOKEY) + err = -EACCES; cifs_put_tlink(tlink); - return ERR_PTR(-EACCES); + return ERR_PTR(err); } return tlink; diff --git a/fs/smb/client/dfs.c b/fs/smb/client/dfs.c index 3ec965547e3d..3f6077c68d68 100644 --- a/fs/smb/client/dfs.c +++ b/fs/smb/client/dfs.c @@ -69,7 +69,7 @@ static int get_session(struct cifs_mount_ctx *mnt_ctx, const char *full_path) * Get an active reference of @ses so that next call to cifs_put_tcon() won't * release it as any new DFS referrals must go through its IPC tcon. */ -static void add_root_smb_session(struct cifs_mount_ctx *mnt_ctx) +static void set_root_smb_session(struct cifs_mount_ctx *mnt_ctx) { struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; struct cifs_ses *ses = mnt_ctx->ses; @@ -95,7 +95,7 @@ static inline int parse_dfs_target(struct smb3_fs_context *ctx, return rc; } -static int set_ref_paths(struct cifs_mount_ctx *mnt_ctx, +static int setup_dfs_ref(struct cifs_mount_ctx *mnt_ctx, struct dfs_info3_param *tgt, struct dfs_ref_walk *rw) { @@ -120,6 +120,7 @@ static int set_ref_paths(struct cifs_mount_ctx *mnt_ctx, } ref_walk_path(rw) = ref_path; ref_walk_fpath(rw) = full_path; + ref_walk_ses(rw) = ctx->dfs_root_ses; return 0; } @@ -128,11 +129,11 @@ static int __dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx, { struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; struct dfs_info3_param tgt = {}; - bool is_refsrv; int rc = -ENOENT; again: do { + ctx->dfs_root_ses = ref_walk_ses(rw); if (ref_walk_empty(rw)) { rc = dfs_get_referral(mnt_ctx, ref_walk_path(rw) + 1, NULL, ref_walk_tl(rw)); @@ -158,10 +159,7 @@ again: if (rc) continue; - is_refsrv = tgt.server_type == DFS_TYPE_ROOT || - DFS_INTERLINK(tgt.flags); ref_walk_set_tgt_hint(rw); - if (tgt.flags & DFSREF_STORAGE_SERVER) { rc = cifs_mount_get_tcon(mnt_ctx); if (!rc) @@ -172,12 +170,10 @@ again: continue; } - if (is_refsrv) - add_root_smb_session(mnt_ctx); - + set_root_smb_session(mnt_ctx); rc = ref_walk_advance(rw); if (!rc) { - rc = set_ref_paths(mnt_ctx, &tgt, rw); + rc = setup_dfs_ref(mnt_ctx, &tgt, rw); if (!rc) { rc = -EREMOTE; goto again; @@ -193,20 +189,22 @@ out: return rc; } -static int dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx) +static int dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx, + struct dfs_ref_walk **rw) { - struct dfs_ref_walk *rw; int rc; - rw = ref_walk_alloc(); - if (IS_ERR(rw)) - return PTR_ERR(rw); + *rw = ref_walk_alloc(); + if (IS_ERR(*rw)) { + rc = PTR_ERR(*rw); + *rw = NULL; + return rc; + } - ref_walk_init(rw); - rc = set_ref_paths(mnt_ctx, NULL, rw); + ref_walk_init(*rw); + rc = setup_dfs_ref(mnt_ctx, NULL, *rw); if (!rc) - rc = __dfs_referral_walk(mnt_ctx, rw); - ref_walk_free(rw); + rc = __dfs_referral_walk(mnt_ctx, *rw); return rc; } @@ -214,16 +212,16 @@ static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx) { struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; + struct dfs_ref_walk *rw = NULL; struct cifs_tcon *tcon; char *origin_fullpath; - bool new_tcon = true; int rc; origin_fullpath = dfs_get_path(cifs_sb, ctx->source); if (IS_ERR(origin_fullpath)) return PTR_ERR(origin_fullpath); - rc = dfs_referral_walk(mnt_ctx); + rc = dfs_referral_walk(mnt_ctx, &rw); if (!rc) { /* * Prevent superblock from being created with any missing @@ -241,21 +239,16 @@ static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx) tcon = mnt_ctx->tcon; spin_lock(&tcon->tc_lock); - if (!tcon->origin_fullpath) { - tcon->origin_fullpath = origin_fullpath; - origin_fullpath = NULL; - } else { - new_tcon = false; - } + tcon->origin_fullpath = origin_fullpath; + origin_fullpath = NULL; + ref_walk_set_tcon(rw, tcon); spin_unlock(&tcon->tc_lock); - - if (new_tcon) { - queue_delayed_work(dfscache_wq, &tcon->dfs_cache_work, - dfs_cache_get_ttl() * HZ); - } + queue_delayed_work(dfscache_wq, &tcon->dfs_cache_work, + dfs_cache_get_ttl() * HZ); out: kfree(origin_fullpath); + ref_walk_free(rw); return rc; } @@ -279,7 +272,7 @@ static int update_fs_context_dstaddr(struct smb3_fs_context *ctx) return rc; } -int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs) +int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx) { struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; bool nodfs = ctx->nodfs; @@ -289,7 +282,6 @@ int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs) if (rc) return rc; - *isdfs = false; rc = get_session(mnt_ctx, NULL); if (rc) return rc; @@ -317,10 +309,15 @@ int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs) return rc; } - *isdfs = true; - add_root_smb_session(mnt_ctx); - rc = __dfs_mount_share(mnt_ctx); - dfs_put_root_smb_sessions(mnt_ctx); + if (!ctx->dfs_conn) { + ctx->dfs_conn = true; + cifs_mount_put_conns(mnt_ctx); + rc = get_session(mnt_ctx, NULL); + } + if (!rc) { + set_root_smb_session(mnt_ctx); + rc = __dfs_mount_share(mnt_ctx); + } return rc; } diff --git a/fs/smb/client/dfs.h b/fs/smb/client/dfs.h index e5c4dcf83750..1aa2bc65b3bc 100644 --- a/fs/smb/client/dfs.h +++ b/fs/smb/client/dfs.h @@ -19,6 +19,7 @@ struct dfs_ref { char *path; char *full_path; + struct cifs_ses *ses; struct dfs_cache_tgt_list tl; struct dfs_cache_tgt_iterator *tit; }; @@ -38,6 +39,7 @@ struct dfs_ref_walk { #define ref_walk_path(w) (ref_walk_cur(w)->path) #define ref_walk_fpath(w) (ref_walk_cur(w)->full_path) #define ref_walk_tl(w) (&ref_walk_cur(w)->tl) +#define ref_walk_ses(w) (ref_walk_cur(w)->ses) static inline struct dfs_ref_walk *ref_walk_alloc(void) { @@ -60,14 +62,19 @@ static inline void __ref_walk_free(struct dfs_ref *ref) kfree(ref->path); kfree(ref->full_path); dfs_cache_free_tgts(&ref->tl); + if (ref->ses) + cifs_put_smb_ses(ref->ses); memset(ref, 0, sizeof(*ref)); } static inline void ref_walk_free(struct dfs_ref_walk *rw) { - struct dfs_ref *ref = ref_walk_start(rw); + struct dfs_ref *ref; - for (; ref <= ref_walk_end(rw); ref++) + if (!rw) + return; + + for (ref = ref_walk_start(rw); ref <= ref_walk_end(rw); ref++) __ref_walk_free(ref); kfree(rw); } @@ -116,9 +123,22 @@ static inline void ref_walk_set_tgt_hint(struct dfs_ref_walk *rw) ref_walk_tit(rw)); } +static inline void ref_walk_set_tcon(struct dfs_ref_walk *rw, + struct cifs_tcon *tcon) +{ + struct dfs_ref *ref = ref_walk_start(rw); + + for (; ref <= ref_walk_cur(rw); ref++) { + if (WARN_ON_ONCE(!ref->ses)) + continue; + list_add(&ref->ses->dlist, &tcon->dfs_ses_list); + ref->ses = NULL; + } +} + int dfs_parse_target_referral(const char *full_path, const struct dfs_info3_param *ref, struct smb3_fs_context *ctx); -int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs); +int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx); static inline char *dfs_get_path(struct cifs_sb_info *cifs_sb, const char *path) { @@ -142,20 +162,14 @@ static inline int dfs_get_referral(struct cifs_mount_ctx *mnt_ctx, const char *p * references of all DFS root sessions that were used across the mount process * in dfs_mount_share(). */ -static inline void dfs_put_root_smb_sessions(struct cifs_mount_ctx *mnt_ctx) +static inline void dfs_put_root_smb_sessions(struct list_head *head) { - const struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; - struct cifs_ses *ses = ctx->dfs_root_ses; - struct cifs_ses *cur; + struct cifs_ses *ses, *n; - if (!ses) - return; - - for (cur = ses; cur; cur = cur->dfs_root_ses) { - if (cur->dfs_root_ses) - cifs_put_smb_ses(cur->dfs_root_ses); + list_for_each_entry_safe(ses, n, head, dlist) { + list_del_init(&ses->dlist); + cifs_put_smb_ses(ses); } - cifs_put_smb_ses(ses); } #endif /* _CIFS_DFS_H */ diff --git a/fs/smb/client/dfs_cache.c b/fs/smb/client/dfs_cache.c index 11c8efecf7aa..110f03df012a 100644 --- a/fs/smb/client/dfs_cache.c +++ b/fs/smb/client/dfs_cache.c @@ -126,6 +126,7 @@ static inline void free_tgts(struct cache_entry *ce) static inline void flush_cache_ent(struct cache_entry *ce) { + cifs_dbg(FYI, "%s: %s\n", __func__, ce->path); hlist_del_init(&ce->hlist); kfree(ce->path); free_tgts(ce); @@ -441,34 +442,31 @@ static struct cache_entry *alloc_cache_entry(struct dfs_info3_param *refs, int n return ce; } -static void remove_oldest_entry_locked(void) +/* Remove all referrals that have a single target or oldest entry */ +static void purge_cache(void) { int i; struct cache_entry *ce; - struct cache_entry *to_del = NULL; - - WARN_ON(!rwsem_is_locked(&htable_rw_lock)); + struct cache_entry *oldest = NULL; for (i = 0; i < CACHE_HTABLE_SIZE; i++) { struct hlist_head *l = &cache_htable[i]; + struct hlist_node *n; - hlist_for_each_entry(ce, l, hlist) { + hlist_for_each_entry_safe(ce, n, l, hlist) { if (hlist_unhashed(&ce->hlist)) continue; - if (!to_del || timespec64_compare(&ce->etime, - &to_del->etime) < 0) - to_del = ce; + if (ce->numtgts == 1) + flush_cache_ent(ce); + else if (!oldest || + timespec64_compare(&ce->etime, + &oldest->etime) < 0) + oldest = ce; } } - if (!to_del) { - cifs_dbg(FYI, "%s: no entry to remove\n", __func__); - return; - } - - cifs_dbg(FYI, "%s: removing entry\n", __func__); - dump_ce(to_del); - flush_cache_ent(to_del); + if (atomic_read(&cache_count) >= CACHE_MAX_ENTRIES && oldest) + flush_cache_ent(oldest); } /* Add a new DFS cache entry */ @@ -484,7 +482,7 @@ static struct cache_entry *add_cache_entry_locked(struct dfs_info3_param *refs, if (atomic_read(&cache_count) >= CACHE_MAX_ENTRIES) { cifs_dbg(FYI, "%s: reached max cache size (%d)\n", __func__, CACHE_MAX_ENTRIES); - remove_oldest_entry_locked(); + purge_cache(); } rc = cache_entry_hash(refs[0].path_name, strlen(refs[0].path_name), &hash); @@ -1095,16 +1093,18 @@ int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it, return 0; } -static bool target_share_equal(struct TCP_Server_Info *server, const char *s1, const char *s2) +static bool target_share_equal(struct cifs_tcon *tcon, const char *s1) { - char unc[sizeof("\\\\") + SERVER_NAME_LENGTH] = {0}; - const char *host; - size_t hostlen; + struct TCP_Server_Info *server = tcon->ses->server; struct sockaddr_storage ss; + const char *host; + const char *s2 = &tcon->tree_name[1]; + size_t hostlen; + char unc[sizeof("\\\\") + SERVER_NAME_LENGTH] = {0}; bool match; int rc; - if (strcasecmp(s1, s2)) + if (strcasecmp(s2, s1)) return false; /* @@ -1128,34 +1128,6 @@ static bool target_share_equal(struct TCP_Server_Info *server, const char *s1, c return match; } -/* - * Mark dfs tcon for reconnecting when the currently connected tcon does not match any of the new - * target shares in @refs. - */ -static void mark_for_reconnect_if_needed(struct TCP_Server_Info *server, - const char *path, - struct dfs_cache_tgt_list *old_tl, - struct dfs_cache_tgt_list *new_tl) -{ - struct dfs_cache_tgt_iterator *oit, *nit; - - for (oit = dfs_cache_get_tgt_iterator(old_tl); oit; - oit = dfs_cache_get_next_tgt(old_tl, oit)) { - for (nit = dfs_cache_get_tgt_iterator(new_tl); nit; - nit = dfs_cache_get_next_tgt(new_tl, nit)) { - if (target_share_equal(server, - dfs_cache_get_tgt_name(oit), - dfs_cache_get_tgt_name(nit))) { - dfs_cache_noreq_update_tgthint(path, nit); - return; - } - } - } - - cifs_dbg(FYI, "%s: no cached or matched targets. mark dfs share for reconnect.\n", __func__); - cifs_signal_cifsd_for_reconnect(server, true); -} - static bool is_ses_good(struct cifs_ses *ses) { struct TCP_Server_Info *server = ses->server; @@ -1172,41 +1144,35 @@ static bool is_ses_good(struct cifs_ses *ses) return ret; } -/* Refresh dfs referral of @ses and mark it for reconnect if needed */ -static void __refresh_ses_referral(struct cifs_ses *ses, bool force_refresh) +static char *get_ses_refpath(struct cifs_ses *ses) { struct TCP_Server_Info *server = ses->server; - DFS_CACHE_TGT_LIST(old_tl); - DFS_CACHE_TGT_LIST(new_tl); - bool needs_refresh = false; - struct cache_entry *ce; - unsigned int xid; - char *path = NULL; - int rc = 0; - - xid = get_xid(); + char *path = ERR_PTR(-ENOENT); mutex_lock(&server->refpath_lock); if (server->leaf_fullpath) { path = kstrdup(server->leaf_fullpath + 1, GFP_ATOMIC); if (!path) - rc = -ENOMEM; + path = ERR_PTR(-ENOMEM); } mutex_unlock(&server->refpath_lock); - if (!path) - goto out; + return path; +} - down_read(&htable_rw_lock); - ce = lookup_cache_entry(path); - needs_refresh = force_refresh || IS_ERR(ce) || cache_entry_expired(ce); - if (!IS_ERR(ce)) { - rc = get_targets(ce, &old_tl); - cifs_dbg(FYI, "%s: get_targets: %d\n", __func__, rc); - } - up_read(&htable_rw_lock); +/* Refresh dfs referral of @ses */ +static void refresh_ses_referral(struct cifs_ses *ses) +{ + struct cache_entry *ce; + unsigned int xid; + char *path; + int rc = 0; - if (!needs_refresh) { - rc = 0; + xid = get_xid(); + + path = get_ses_refpath(ses); + if (IS_ERR(path)) { + rc = PTR_ERR(path); + path = NULL; goto out; } @@ -1217,29 +1183,106 @@ static void __refresh_ses_referral(struct cifs_ses *ses, bool force_refresh) goto out; } - ce = cache_refresh_path(xid, ses, path, true); - if (!IS_ERR(ce)) { - rc = get_targets(ce, &new_tl); + ce = cache_refresh_path(xid, ses, path, false); + if (!IS_ERR(ce)) up_read(&htable_rw_lock); - cifs_dbg(FYI, "%s: get_targets: %d\n", __func__, rc); - mark_for_reconnect_if_needed(server, path, &old_tl, &new_tl); + else + rc = PTR_ERR(ce); + +out: + free_xid(xid); + kfree(path); +} + +static int __refresh_tcon_referral(struct cifs_tcon *tcon, + const char *path, + struct dfs_info3_param *refs, + int numrefs, bool force_refresh) +{ + struct cache_entry *ce; + bool reconnect = force_refresh; + int rc = 0; + int i; + + if (unlikely(!numrefs)) + return 0; + + if (force_refresh) { + for (i = 0; i < numrefs; i++) { + /* TODO: include prefix paths in the matching */ + if (target_share_equal(tcon, refs[i].node_name)) { + reconnect = false; + break; + } + } + } + + down_write(&htable_rw_lock); + ce = lookup_cache_entry(path); + if (!IS_ERR(ce)) { + if (force_refresh || cache_entry_expired(ce)) + rc = update_cache_entry_locked(ce, refs, numrefs); + } else if (PTR_ERR(ce) == -ENOENT) { + ce = add_cache_entry_locked(refs, numrefs); + } + up_write(&htable_rw_lock); + + if (IS_ERR(ce)) + rc = PTR_ERR(ce); + if (reconnect) { + cifs_tcon_dbg(FYI, "%s: mark for reconnect\n", __func__); + cifs_signal_cifsd_for_reconnect(tcon->ses->server, true); + } + return rc; +} + +static void refresh_tcon_referral(struct cifs_tcon *tcon, bool force_refresh) +{ + struct dfs_info3_param *refs = NULL; + struct cache_entry *ce; + struct cifs_ses *ses; + unsigned int xid; + bool needs_refresh; + char *path; + int numrefs = 0; + int rc = 0; + + xid = get_xid(); + ses = tcon->ses; + + path = get_ses_refpath(ses); + if (IS_ERR(path)) { + rc = PTR_ERR(path); + path = NULL; + goto out; + } + + down_read(&htable_rw_lock); + ce = lookup_cache_entry(path); + needs_refresh = force_refresh || IS_ERR(ce) || cache_entry_expired(ce); + if (!needs_refresh) { + up_read(&htable_rw_lock); + goto out; + } + up_read(&htable_rw_lock); + + ses = CIFS_DFS_ROOT_SES(ses); + if (!is_ses_good(ses)) { + cifs_dbg(FYI, "%s: skip cache refresh due to disconnected ipc\n", + __func__); + goto out; + } + + rc = get_dfs_referral(xid, ses, path, &refs, &numrefs); + if (!rc) { + rc = __refresh_tcon_referral(tcon, path, refs, + numrefs, force_refresh); } out: free_xid(xid); - dfs_cache_free_tgts(&old_tl); - dfs_cache_free_tgts(&new_tl); kfree(path); -} - -static inline void refresh_ses_referral(struct cifs_ses *ses) -{ - __refresh_ses_referral(ses, false); -} - -static inline void force_refresh_ses_referral(struct cifs_ses *ses) -{ - __refresh_ses_referral(ses, true); + free_dfs_info_array(refs, numrefs); } /** @@ -1280,7 +1323,7 @@ int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb) */ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; - force_refresh_ses_referral(tcon->ses); + refresh_tcon_referral(tcon, true); return 0; } @@ -1292,8 +1335,9 @@ void dfs_cache_refresh(struct work_struct *work) tcon = container_of(work, struct cifs_tcon, dfs_cache_work.work); - for (ses = tcon->ses; ses; ses = ses->dfs_root_ses) + list_for_each_entry(ses, &tcon->dfs_ses_list, dlist) refresh_ses_referral(ses); + refresh_tcon_referral(tcon, false); queue_delayed_work(dfscache_wq, &tcon->dfs_cache_work, atomic_read(&dfs_cache_ttl) * HZ); diff --git a/fs/smb/client/fs_context.h b/fs/smb/client/fs_context.h index cf577ec0dd0a..69f9d938b336 100644 --- a/fs/smb/client/fs_context.h +++ b/fs/smb/client/fs_context.h @@ -284,6 +284,7 @@ struct smb3_fs_context { struct cifs_ses *dfs_root_ses; bool dfs_automount:1; /* set for dfs automount only */ enum cifs_reparse_type reparse_type; + bool dfs_conn:1; /* set for dfs mounts */ }; extern const struct fs_parameter_spec smb3_fs_parameters[]; diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c index 331a86074ae7..647f9bedd9fc 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -834,10 +834,6 @@ static void cifs_open_info_to_fattr(struct cifs_fattr *fattr, fattr->cf_mode = S_IFREG | cifs_sb->ctx->file_mode; fattr->cf_dtype = DT_REG; - /* clear write bits if ATTR_READONLY is set */ - if (fattr->cf_cifsattrs & ATTR_READONLY) - fattr->cf_mode &= ~(S_IWUGO); - /* * Don't accept zero nlink from non-unix servers unless * delete is pending. Instead mark it as unknown. @@ -850,6 +846,10 @@ static void cifs_open_info_to_fattr(struct cifs_fattr *fattr, } } + /* clear write bits if ATTR_READONLY is set */ + if (fattr->cf_cifsattrs & ATTR_READONLY) + fattr->cf_mode &= ~(S_IWUGO); + out_reparse: if (S_ISLNK(fattr->cf_mode)) { if (likely(data->symlink_target)) @@ -1267,11 +1267,14 @@ handle_mnt_opt: __func__, rc); goto out; } - } - - /* fill in remaining high mode bits e.g. SUID, VTX */ - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) + } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) + /* fill in remaining high mode bits e.g. SUID, VTX */ cifs_sfu_mode(fattr, full_path, cifs_sb, xid); + else if (!(tcon->posix_extensions)) + /* clear write bits if ATTR_READONLY is set */ + if (fattr->cf_cifsattrs & ATTR_READONLY) + fattr->cf_mode &= ~(S_IWUGO); + /* check for Minshall+French symlinks */ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) { diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c index dab526191b07..054f10ebf65a 100644 --- a/fs/smb/client/misc.c +++ b/fs/smb/client/misc.c @@ -145,6 +145,9 @@ tcon_info_alloc(bool dir_leases_enabled, enum smb3_tcon_ref_trace trace) mutex_init(&ret_buf->fscache_lock); #endif trace_smb3_tcon_ref(ret_buf->debug_id, ret_buf->tc_count, trace); +#ifdef CONFIG_CIFS_DFS_UPCALL + INIT_LIST_HEAD(&ret_buf->dfs_ses_list); +#endif return ret_buf; } @@ -1108,7 +1111,8 @@ static void tcon_super_cb(struct super_block *sb, void *arg) t2 = cifs_sb_master_tcon(cifs_sb); spin_lock(&t2->tc_lock); - if (t1->ses == t2->ses && + if ((t1->ses == t2->ses || + t1->ses->dfs_root_ses == t2->ses->dfs_root_ses) && t1->ses->server == t2->ses->server && t2->origin_fullpath && dfs_src_pathname_equal(t2->origin_fullpath, t1->origin_fullpath)) diff --git a/fs/smb/client/namespace.c b/fs/smb/client/namespace.c index 4a517b280f2b..0f788031b740 100644 --- a/fs/smb/client/namespace.c +++ b/fs/smb/client/namespace.c @@ -240,7 +240,7 @@ static struct vfsmount *cifs_do_automount(struct path *path) ctx->source = NULL; goto out; } - ctx->dfs_automount = is_dfs_mount(mntpt); + ctx->dfs_automount = ctx->dfs_conn = is_dfs_mount(mntpt); cifs_dbg(FYI, "%s: ctx: source=%s UNC=%s prepath=%s dfs_automount=%d\n", __func__, ctx->source, ctx->UNC, ctx->prepath, ctx->dfs_automount); diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c index 48c27581ec51..3b48a093cfb1 100644 --- a/fs/smb/client/reparse.c +++ b/fs/smb/client/reparse.c @@ -108,8 +108,8 @@ static int nfs_set_reparse_buf(struct reparse_posix_data *buf, buf->InodeType = cpu_to_le64(type); buf->ReparseDataLength = cpu_to_le16(len + dlen - sizeof(struct reparse_data_buffer)); - *(__le64 *)buf->DataBuffer = cpu_to_le64(((u64)MAJOR(dev) << 32) | - MINOR(dev)); + *(__le64 *)buf->DataBuffer = cpu_to_le64(((u64)MINOR(dev) << 32) | + MAJOR(dev)); iov->iov_base = buf; iov->iov_len = len + dlen; return 0; @@ -468,7 +468,7 @@ static void wsl_to_fattr(struct cifs_open_info_data *data, else if (!strncmp(name, SMB2_WSL_XATTR_MODE, nlen)) fattr->cf_mode = (umode_t)le32_to_cpu(*(__le32 *)v); else if (!strncmp(name, SMB2_WSL_XATTR_DEV, nlen)) - fattr->cf_rdev = wsl_mkdev(v); + fattr->cf_rdev = reparse_mkdev(v); } while (next); out: fattr->cf_dtype = S_DT(fattr->cf_mode); @@ -485,11 +485,11 @@ bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb, switch (le64_to_cpu(buf->InodeType)) { case NFS_SPECFILE_CHR: fattr->cf_mode |= S_IFCHR; - fattr->cf_rdev = reparse_nfs_mkdev(buf); + fattr->cf_rdev = reparse_mkdev(buf->DataBuffer); break; case NFS_SPECFILE_BLK: fattr->cf_mode |= S_IFBLK; - fattr->cf_rdev = reparse_nfs_mkdev(buf); + fattr->cf_rdev = reparse_mkdev(buf->DataBuffer); break; case NFS_SPECFILE_FIFO: fattr->cf_mode |= S_IFIFO; diff --git a/fs/smb/client/reparse.h b/fs/smb/client/reparse.h index 2c0644bc4e65..158e7b7aae64 100644 --- a/fs/smb/client/reparse.h +++ b/fs/smb/client/reparse.h @@ -18,14 +18,7 @@ */ #define IO_REPARSE_TAG_INTERNAL ((__u32)~0U) -static inline dev_t reparse_nfs_mkdev(struct reparse_posix_data *buf) -{ - u64 v = le64_to_cpu(*(__le64 *)buf->DataBuffer); - - return MKDEV(v >> 32, v & 0xffffffff); -} - -static inline dev_t wsl_mkdev(void *ptr) +static inline dev_t reparse_mkdev(void *ptr) { u64 v = le64_to_cpu(*(__le64 *)ptr); diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index 7381ec333c6d..1ee2dd4a1cae 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -4869,9 +4869,12 @@ receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid, goto discard_data; server->total_read += rc; - if (rc < len) - iov_iter_zero(len - rc, &iter); - iov_iter_revert(&iter, len); + if (rc < len) { + struct iov_iter tmp = iter; + + iov_iter_advance(&tmp, rc); + iov_iter_zero(len - rc, &tmp); + } iov_iter_truncate(&iter, dw->len); rc = cifs_discard_remaining_data(server); diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index 2cb1bf65a172..bb225758448a 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -4866,7 +4866,9 @@ smb2_writev_callback(struct mid_q_entry *mid) #endif if (result) { cifs_stats_fail_inc(tcon, SMB2_WRITE_HE); - trace_smb3_write_err(wdata->xid, + trace_smb3_write_err(wdata->rreq->debug_id, + wdata->subreq.debug_index, + wdata->xid, wdata->req->cfile->fid.persistent_fid, tcon->tid, tcon->ses->Suid, wdata->subreq.start, wdata->subreq.len, wdata->result); @@ -4874,7 +4876,9 @@ smb2_writev_callback(struct mid_q_entry *mid) pr_warn_once("Out of space writing to %s\n", tcon->tree_name); } else - trace_smb3_write_done(0 /* no xid */, + trace_smb3_write_done(wdata->rreq->debug_id, + wdata->subreq.debug_index, + wdata->xid, wdata->req->cfile->fid.persistent_fid, tcon->tid, tcon->ses->Suid, wdata->subreq.start, wdata->subreq.len); @@ -4952,7 +4956,9 @@ smb2_async_writev(struct cifs_io_subrequest *wdata) offsetof(struct smb2_write_req, Buffer)); req->RemainingBytes = 0; - trace_smb3_write_enter(wdata->xid, + trace_smb3_write_enter(wdata->rreq->debug_id, + wdata->subreq.debug_index, + wdata->xid, io_parms->persistent_fid, io_parms->tcon->tid, io_parms->tcon->ses->Suid, @@ -5032,7 +5038,9 @@ smb2_async_writev(struct cifs_io_subrequest *wdata) wdata, flags, &wdata->credits); /* Can't touch wdata if rc == 0 */ if (rc) { - trace_smb3_write_err(xid, + trace_smb3_write_err(wdata->rreq->debug_id, + wdata->subreq.debug_index, + xid, io_parms->persistent_fid, io_parms->tcon->tid, io_parms->tcon->ses->Suid, @@ -5112,7 +5120,7 @@ replay_again: offsetof(struct smb2_write_req, Buffer)); req->RemainingBytes = 0; - trace_smb3_write_enter(xid, io_parms->persistent_fid, + trace_smb3_write_enter(0, 0, xid, io_parms->persistent_fid, io_parms->tcon->tid, io_parms->tcon->ses->Suid, io_parms->offset, io_parms->length); @@ -5133,7 +5141,7 @@ replay_again: rsp = (struct smb2_write_rsp *)rsp_iov.iov_base; if (rc) { - trace_smb3_write_err(xid, + trace_smb3_write_err(0, 0, xid, req->PersistentFileId, io_parms->tcon->tid, io_parms->tcon->ses->Suid, @@ -5142,7 +5150,7 @@ replay_again: cifs_dbg(VFS, "Send error in write = %d\n", rc); } else { *nbytes = le32_to_cpu(rsp->DataLength); - trace_smb3_write_done(xid, + trace_smb3_write_done(0, 0, xid, req->PersistentFileId, io_parms->tcon->tid, io_parms->tcon->ses->Suid, diff --git a/fs/smb/client/trace.h b/fs/smb/client/trace.h index 8e9964001e2a..0b52d22a91a0 100644 --- a/fs/smb/client/trace.h +++ b/fs/smb/client/trace.h @@ -157,6 +157,7 @@ DEFINE_EVENT(smb3_rw_err_class, smb3_##name, \ TP_ARGS(rreq_debug_id, rreq_debug_index, xid, fid, tid, sesid, offset, len, rc)) DEFINE_SMB3_RW_ERR_EVENT(read_err); +DEFINE_SMB3_RW_ERR_EVENT(write_err); /* For logging errors in other file I/O ops */ DECLARE_EVENT_CLASS(smb3_other_err_class, @@ -202,7 +203,6 @@ DEFINE_EVENT(smb3_other_err_class, smb3_##name, \ int rc), \ TP_ARGS(xid, fid, tid, sesid, offset, len, rc)) -DEFINE_SMB3_OTHER_ERR_EVENT(write_err); DEFINE_SMB3_OTHER_ERR_EVENT(query_dir_err); DEFINE_SMB3_OTHER_ERR_EVENT(zero_err); DEFINE_SMB3_OTHER_ERR_EVENT(falloc_err); @@ -370,6 +370,8 @@ DEFINE_EVENT(smb3_rw_done_class, smb3_##name, \ DEFINE_SMB3_RW_DONE_EVENT(read_enter); DEFINE_SMB3_RW_DONE_EVENT(read_done); +DEFINE_SMB3_RW_DONE_EVENT(write_enter); +DEFINE_SMB3_RW_DONE_EVENT(write_done); /* For logging successful other op */ DECLARE_EVENT_CLASS(smb3_other_done_class, @@ -411,11 +413,9 @@ DEFINE_EVENT(smb3_other_done_class, smb3_##name, \ __u32 len), \ TP_ARGS(xid, fid, tid, sesid, offset, len)) -DEFINE_SMB3_OTHER_DONE_EVENT(write_enter); DEFINE_SMB3_OTHER_DONE_EVENT(query_dir_enter); DEFINE_SMB3_OTHER_DONE_EVENT(zero_enter); DEFINE_SMB3_OTHER_DONE_EVENT(falloc_enter); -DEFINE_SMB3_OTHER_DONE_EVENT(write_done); DEFINE_SMB3_OTHER_DONE_EVENT(query_dir_done); DEFINE_SMB3_OTHER_DONE_EVENT(zero_done); DEFINE_SMB3_OTHER_DONE_EVENT(falloc_done); diff --git a/fs/smb/client/transport.c b/fs/smb/client/transport.c index fd5a85d43759..91812150186c 100644 --- a/fs/smb/client/transport.c +++ b/fs/smb/client/transport.c @@ -1817,11 +1817,8 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) length = data_len; /* An RDMA read is already done. */ else #endif - { length = cifs_read_iter_from_socket(server, &rdata->subreq.io_iter, data_len); - iov_iter_revert(&rdata->subreq.io_iter, data_len); - } if (length > 0) rdata->got_bytes += length; server->total_read += length; diff --git a/include/linux/blk-integrity.h b/include/linux/blk-integrity.h index de98049b7ded..676f8f860c47 100644 --- a/include/linux/blk-integrity.h +++ b/include/linux/blk-integrity.h @@ -25,9 +25,10 @@ static inline bool queue_limits_stack_integrity_bdev(struct queue_limits *t, } #ifdef CONFIG_BLK_DEV_INTEGRITY -int blk_rq_map_integrity_sg(struct request_queue *, struct bio *, - struct scatterlist *); +int blk_rq_map_integrity_sg(struct request *, struct scatterlist *); int blk_rq_count_integrity_sg(struct request_queue *, struct bio *); +int blk_rq_integrity_map_user(struct request *rq, void __user *ubuf, + ssize_t bytes, u32 seed); static inline bool blk_integrity_queue_supports_integrity(struct request_queue *q) @@ -96,12 +97,18 @@ static inline int blk_rq_count_integrity_sg(struct request_queue *q, { return 0; } -static inline int blk_rq_map_integrity_sg(struct request_queue *q, - struct bio *b, +static inline int blk_rq_map_integrity_sg(struct request *q, struct scatterlist *s) { return 0; } +static inline int blk_rq_integrity_map_user(struct request *rq, + void __user *ubuf, + ssize_t bytes, + u32 seed) +{ + return -EINVAL; +} static inline struct blk_integrity *bdev_get_integrity(struct block_device *b) { return NULL; diff --git a/include/linux/blk-mq.h b/include/linux/blk-mq.h index 8d304b1d16b1..4fecf46ef681 100644 --- a/include/linux/blk-mq.h +++ b/include/linux/blk-mq.h @@ -149,10 +149,7 @@ struct request { * physical address coalescing is performed. */ unsigned short nr_phys_segments; - -#ifdef CONFIG_BLK_DEV_INTEGRITY unsigned short nr_integrity_segments; -#endif #ifdef CONFIG_BLK_INLINE_ENCRYPTION struct bio_crypt_ctx *crypt_ctx; diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h index d75d3e0c9e8d..2ee6a7e57079 100644 --- a/include/linux/blk_types.h +++ b/include/linux/blk_types.h @@ -254,11 +254,9 @@ struct bio { #endif #endif - union { #if defined(CONFIG_BLK_DEV_INTEGRITY) - struct bio_integrity_payload *bi_integrity; /* data integrity */ + struct bio_integrity_payload *bi_integrity; /* data integrity */ #endif - }; unsigned short bi_vcnt; /* how many bio_vec's */ diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 643c9020a35a..50c3b959da28 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -968,8 +968,6 @@ static inline void blk_queue_disable_write_zeroes(struct request_queue *q) /* * Access functions for manipulating queue properties */ -extern void blk_limits_io_min(struct queue_limits *limits, unsigned int min); -extern void blk_limits_io_opt(struct queue_limits *limits, unsigned int opt); extern void blk_set_queue_depth(struct request_queue *q, unsigned int depth); extern void blk_set_stacking_limits(struct queue_limits *lim); extern int blk_stack_limits(struct queue_limits *t, struct queue_limits *b, diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h index 5fcbc254d186..8c4f3bb24429 100644 --- a/include/linux/kprobes.h +++ b/include/linux/kprobes.h @@ -269,15 +269,6 @@ extern unsigned long __stop_kprobe_blacklist[]; extern struct kretprobe_blackpoint kretprobe_blacklist[]; -#ifdef CONFIG_KPROBES_SANITY_TEST -extern int init_test_probes(void); -#else /* !CONFIG_KPROBES_SANITY_TEST */ -static inline int init_test_probes(void) -{ - return 0; -} -#endif /* CONFIG_KPROBES_SANITY_TEST */ - extern int arch_prepare_kprobe(struct kprobe *p); extern void arch_arm_kprobe(struct kprobe *p); extern void arch_disarm_kprobe(struct kprobe *p); diff --git a/include/linux/mutex.h b/include/linux/mutex.h index a561c629d89f..2bf91b57591b 100644 --- a/include/linux/mutex.h +++ b/include/linux/mutex.h @@ -49,7 +49,6 @@ static inline void mutex_destroy(struct mutex *lock) {} #endif -#ifndef CONFIG_PREEMPT_RT /** * mutex_init - initialize the mutex * @mutex: the mutex to be initialized @@ -65,6 +64,18 @@ do { \ __mutex_init((mutex), #mutex, &__key); \ } while (0) +/** + * mutex_init_with_key - initialize a mutex with a given lockdep key + * @mutex: the mutex to be initialized + * @key: the lockdep key to be associated with the mutex + * + * Initialize the mutex to the unlocked state. + * + * It is not allowed to initialize an already locked mutex. + */ +#define mutex_init_with_key(mutex, key) __mutex_init((mutex), #mutex, (key)) + +#ifndef CONFIG_PREEMPT_RT #define __MUTEX_INITIALIZER(lockname) \ { .owner = ATOMIC_LONG_INIT(0) \ , .wait_lock = __RAW_SPIN_LOCK_UNLOCKED(lockname.wait_lock) \ @@ -111,12 +122,6 @@ do { \ __mutex_rt_init((mutex), name, key); \ } while (0) -#define mutex_init(mutex) \ -do { \ - static struct lock_class_key __key; \ - \ - __mutex_init((mutex), #mutex, &__key); \ -} while (0) #endif /* CONFIG_PREEMPT_RT */ #ifdef CONFIG_DEBUG_MUTEXES diff --git a/include/linux/sbitmap.h b/include/linux/sbitmap.h index c09cdcc99471..189140bf11fc 100644 --- a/include/linux/sbitmap.h +++ b/include/linux/sbitmap.h @@ -40,7 +40,7 @@ struct sbitmap_word { /** * @swap_lock: serializes simultaneous updates of ->word and ->cleared */ - spinlock_t swap_lock; + raw_spinlock_t swap_lock; } ____cacheline_aligned_in_smp; /** diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index fd59ed2cca53..e0717c8393d7 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -193,7 +193,7 @@ void serial8250_do_pm(struct uart_port *port, unsigned int state, unsigned int oldstate); void serial8250_do_set_mctrl(struct uart_port *port, unsigned int mctrl); void serial8250_do_set_divisor(struct uart_port *port, unsigned int baud, - unsigned int quot, unsigned int quot_frac); + unsigned int quot); int fsl8250_handle_irq(struct uart_port *port); int serial8250_handle_irq(struct uart_port *port, unsigned int iir); u16 serial8250_rx_chars(struct uart_8250_port *up, u16 lsr); diff --git a/include/linux/serial_s3c.h b/include/linux/serial_s3c.h index 1672cf0810ef..102aa33d956c 100644 --- a/include/linux/serial_s3c.h +++ b/include/linux/serial_s3c.h @@ -246,24 +246,28 @@ S5PV210_UFCON_TXTRIG4 | \ S5PV210_UFCON_RXTRIG4) -#define APPLE_S5L_UCON_RXTO_ENA 9 -#define APPLE_S5L_UCON_RXTHRESH_ENA 12 -#define APPLE_S5L_UCON_TXTHRESH_ENA 13 -#define APPLE_S5L_UCON_RXTO_ENA_MSK (1 << APPLE_S5L_UCON_RXTO_ENA) -#define APPLE_S5L_UCON_RXTHRESH_ENA_MSK (1 << APPLE_S5L_UCON_RXTHRESH_ENA) -#define APPLE_S5L_UCON_TXTHRESH_ENA_MSK (1 << APPLE_S5L_UCON_TXTHRESH_ENA) +#define APPLE_S5L_UCON_RXTO_ENA 9 +#define APPLE_S5L_UCON_RXTO_LEGACY_ENA 11 +#define APPLE_S5L_UCON_RXTHRESH_ENA 12 +#define APPLE_S5L_UCON_TXTHRESH_ENA 13 +#define APPLE_S5L_UCON_RXTO_ENA_MSK BIT(APPLE_S5L_UCON_RXTO_ENA) +#define APPLE_S5L_UCON_RXTO_LEGACY_ENA_MSK BIT(APPLE_S5L_UCON_RXTO_LEGACY_ENA) +#define APPLE_S5L_UCON_RXTHRESH_ENA_MSK BIT(APPLE_S5L_UCON_RXTHRESH_ENA) +#define APPLE_S5L_UCON_TXTHRESH_ENA_MSK BIT(APPLE_S5L_UCON_TXTHRESH_ENA) #define APPLE_S5L_UCON_DEFAULT (S3C2410_UCON_TXIRQMODE | \ S3C2410_UCON_RXIRQMODE | \ S3C2410_UCON_RXFIFO_TOI) #define APPLE_S5L_UCON_MASK (APPLE_S5L_UCON_RXTO_ENA_MSK | \ + APPLE_S5L_UCON_RXTO_LEGACY_ENA_MSK | \ APPLE_S5L_UCON_RXTHRESH_ENA_MSK | \ APPLE_S5L_UCON_TXTHRESH_ENA_MSK) -#define APPLE_S5L_UTRSTAT_RXTHRESH (1<<4) -#define APPLE_S5L_UTRSTAT_TXTHRESH (1<<5) -#define APPLE_S5L_UTRSTAT_RXTO (1<<9) -#define APPLE_S5L_UTRSTAT_ALL_FLAGS (0x3f0) +#define APPLE_S5L_UTRSTAT_RXTO_LEGACY BIT(3) +#define APPLE_S5L_UTRSTAT_RXTHRESH BIT(4) +#define APPLE_S5L_UTRSTAT_TXTHRESH BIT(5) +#define APPLE_S5L_UTRSTAT_RXTO BIT(9) +#define APPLE_S5L_UTRSTAT_ALL_FLAGS GENMASK(9, 3) #ifndef __ASSEMBLY__ diff --git a/include/linux/soc/qcom/geni-se.h b/include/linux/soc/qcom/geni-se.h index 0f038a1a0330..c3bca9c0bf2c 100644 --- a/include/linux/soc/qcom/geni-se.h +++ b/include/linux/soc/qcom/geni-se.h @@ -88,11 +88,15 @@ struct geni_se { #define SE_GENI_M_IRQ_STATUS 0x610 #define SE_GENI_M_IRQ_EN 0x614 #define SE_GENI_M_IRQ_CLEAR 0x618 +#define SE_GENI_M_IRQ_EN_SET 0x61c +#define SE_GENI_M_IRQ_EN_CLEAR 0x620 #define SE_GENI_S_CMD0 0x630 #define SE_GENI_S_CMD_CTRL_REG 0x634 #define SE_GENI_S_IRQ_STATUS 0x640 #define SE_GENI_S_IRQ_EN 0x644 #define SE_GENI_S_IRQ_CLEAR 0x648 +#define SE_GENI_S_IRQ_EN_SET 0x64c +#define SE_GENI_S_IRQ_EN_CLEAR 0x650 #define SE_GENI_TX_FIFOn 0x700 #define SE_GENI_RX_FIFOn 0x780 #define SE_GENI_TX_FIFO_STATUS 0x800 @@ -101,6 +105,8 @@ struct geni_se { #define SE_GENI_RX_WATERMARK_REG 0x810 #define SE_GENI_RX_RFR_WATERMARK_REG 0x814 #define SE_GENI_IOS 0x908 +#define SE_GENI_M_GP_LENGTH 0x910 +#define SE_GENI_S_GP_LENGTH 0x914 #define SE_DMA_TX_IRQ_STAT 0xc40 #define SE_DMA_TX_IRQ_CLR 0xc44 #define SE_DMA_TX_FSM_RST 0xc58 @@ -234,6 +240,9 @@ struct geni_se { #define IO2_DATA_IN BIT(1) #define RX_DATA_IN BIT(0) +/* SE_GENI_M_GP_LENGTH and SE_GENI_S_GP_LENGTH fields */ +#define GP_LENGTH GENMASK(31, 0) + /* SE_DMA_TX_IRQ_STAT Register fields */ #define TX_DMA_DONE BIT(0) #define TX_EOT BIT(1) diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h index 6be396bb4297..93a9f3070b48 100644 --- a/include/linux/tracepoint.h +++ b/include/linux/tracepoint.h @@ -64,6 +64,13 @@ struct tp_module { bool trace_module_has_bad_taint(struct module *mod); extern int register_tracepoint_module_notifier(struct notifier_block *nb); extern int unregister_tracepoint_module_notifier(struct notifier_block *nb); +void for_each_module_tracepoint(void (*fct)(struct tracepoint *, + struct module *, void *), + void *priv); +void for_each_tracepoint_in_module(struct module *, + void (*fct)(struct tracepoint *, + struct module *, void *), + void *priv); #else static inline bool trace_module_has_bad_taint(struct module *mod) { @@ -79,6 +86,19 @@ int unregister_tracepoint_module_notifier(struct notifier_block *nb) { return 0; } +static inline +void for_each_module_tracepoint(void (*fct)(struct tracepoint *, + struct module *, void *), + void *priv) +{ +} +static inline +void for_each_tracepoint_in_module(struct module *mod, + void (*fct)(struct tracepoint *, + struct module *, void *), + void *priv) +{ +} #endif /* CONFIG_MODULES */ /* diff --git a/include/linux/usb.h b/include/linux/usb.h index 832997a9da0a..672d8fc2abdb 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -495,6 +495,12 @@ struct usb_dev_state; struct usb_tt; +enum usb_link_tunnel_mode { + USB_LINK_UNKNOWN = 0, + USB_LINK_NATIVE, + USB_LINK_TUNNELED, +}; + enum usb_port_connect_type { USB_PORT_CONNECT_TYPE_UNKNOWN = 0, USB_PORT_CONNECT_TYPE_HOT_PLUG, @@ -605,6 +611,7 @@ struct usb3_lpm_parameters { * WUSB devices are not, until we authorize them from user space. * FIXME -- complete doc * @authenticated: Crypto authentication passed + * @tunnel_mode: Connection native or tunneled over USB4 * @lpm_capable: device supports LPM * @lpm_devinit_allow: Allow USB3 device initiated LPM, exit latency is in range * @usb2_hw_lpm_capable: device can perform USB2 hardware LPM @@ -714,6 +721,7 @@ struct usb_device { unsigned do_remote_wakeup:1; unsigned reset_resume:1; unsigned port_is_suspended:1; + enum usb_link_tunnel_mode tunnel_mode; int slot_id; struct usb2_lpm_parameters l1_params; diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 5d4666e5a4b9..27d10cea0bd7 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -257,7 +257,7 @@ int config_ep_by_speed(struct usb_gadget *g, struct usb_function *f, struct usb_ep *_ep); int usb_func_wakeup(struct usb_function *func); -#define MAX_CONFIG_INTERFACES 16 /* arbitrary; max 255 */ +#define MAX_CONFIG_INTERFACES 32 /** * struct usb_configuration - represents one gadget configuration diff --git a/drivers/usb/gadget/u_f.h b/include/linux/usb/func_utils.h similarity index 96% rename from drivers/usb/gadget/u_f.h rename to include/linux/usb/func_utils.h index e313c3b8dcb1..c8795c965109 100644 --- a/drivers/usb/gadget/u_f.h +++ b/include/linux/usb/func_utils.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * u_f.h + * func_utils.h * * Utility definitions for USB functions * @@ -10,8 +10,8 @@ * Author: Andrzej Pietrasiewicz */ -#ifndef __U_F_H__ -#define __U_F_H__ +#ifndef _FUNC_UTILS_H_ +#define _FUNC_UTILS_H_ #include #include @@ -83,4 +83,4 @@ static inline void free_ep_req(struct usb_ep *ep, struct usb_request *req) usb_ep_free_request(ep, req); } -#endif /* __U_F_H__ */ +#endif /* _FUNC_UTILS_H_ */ diff --git a/include/linux/usb/gadget_configfs.h b/include/linux/usb/gadget_configfs.h index d61aebd68128..6b5d6838f865 100644 --- a/include/linux/usb/gadget_configfs.h +++ b/include/linux/usb/gadget_configfs.h @@ -4,9 +4,6 @@ #include -int check_user_usb_string(const char *name, - struct usb_gadget_strings *stringtab_dev); - #define GS_STRINGS_W(__struct, __name) \ static ssize_t __struct##_##__name##_store(struct config_item *item, \ const char *page, size_t len) \ @@ -37,7 +34,7 @@ static struct configfs_item_operations struct_in##_langid_item_ops = { \ .release = struct_in##_attr_release, \ }; \ \ -static struct config_item_type struct_in##_langid_type = { \ +static const struct config_item_type struct_in##_langid_type = { \ .ct_item_ops = &struct_in##_langid_item_ops, \ .ct_attrs = struct_in##_langid_attrs, \ .ct_owner = THIS_MODULE, \ @@ -94,7 +91,7 @@ static struct configfs_group_operations struct_in##_strings_ops = { \ .drop_item = &struct_in##_strings_drop, \ }; \ \ -static struct config_item_type struct_in##_strings_type = { \ +static const struct config_item_type struct_in##_strings_type = { \ .ct_group_ops = &struct_in##_strings_ops, \ .ct_owner = THIS_MODULE, \ } diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 1a0a4dc87980..75b2b763f1ba 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -311,8 +311,11 @@ struct usb_serial_driver { #define to_usb_serial_driver(d) \ container_of(d, struct usb_serial_driver, driver) -int usb_serial_register_drivers(struct usb_serial_driver *const serial_drivers[], - const char *name, const struct usb_device_id *id_table); +#define usb_serial_register_drivers(serial_drivers, name, id_table) \ + __usb_serial_register_drivers(serial_drivers, THIS_MODULE, name, id_table) +int __usb_serial_register_drivers(struct usb_serial_driver *const serial_drivers[], + struct module *owner, const char *name, + const struct usb_device_id *id_table); void usb_serial_deregister_drivers(struct usb_serial_driver *const serial_drivers[]); void usb_serial_port_softint(struct usb_serial_port *port); diff --git a/include/linux/usb/tcpci.h b/include/linux/usb/tcpci.h index 0ab39b6ea205..f7f5cfbdef12 100644 --- a/include/linux/usb/tcpci.h +++ b/include/linux/usb/tcpci.h @@ -63,15 +63,12 @@ #define TCPC_ROLE_CTRL 0x1a #define TCPC_ROLE_CTRL_DRP BIT(6) -#define TCPC_ROLE_CTRL_RP_VAL_SHIFT 4 -#define TCPC_ROLE_CTRL_RP_VAL_MASK 0x3 +#define TCPC_ROLE_CTRL_RP_VAL GENMASK(5, 4) #define TCPC_ROLE_CTRL_RP_VAL_DEF 0x0 #define TCPC_ROLE_CTRL_RP_VAL_1_5 0x1 #define TCPC_ROLE_CTRL_RP_VAL_3_0 0x2 -#define TCPC_ROLE_CTRL_CC2_SHIFT 2 -#define TCPC_ROLE_CTRL_CC2_MASK 0x3 -#define TCPC_ROLE_CTRL_CC1_SHIFT 0 -#define TCPC_ROLE_CTRL_CC1_MASK 0x3 +#define TCPC_ROLE_CTRL_CC2 GENMASK(3, 2) +#define TCPC_ROLE_CTRL_CC1 GENMASK(1, 0) #define TCPC_ROLE_CTRL_CC_RA 0x0 #define TCPC_ROLE_CTRL_CC_RP 0x1 #define TCPC_ROLE_CTRL_CC_RD 0x2 @@ -92,11 +89,9 @@ #define TCPC_CC_STATUS_TERM BIT(4) #define TCPC_CC_STATUS_TERM_RP 0 #define TCPC_CC_STATUS_TERM_RD 1 +#define TCPC_CC_STATUS_CC2 GENMASK(3, 2) +#define TCPC_CC_STATUS_CC1 GENMASK(1, 0) #define TCPC_CC_STATE_SRC_OPEN 0 -#define TCPC_CC_STATUS_CC2_SHIFT 2 -#define TCPC_CC_STATUS_CC2_MASK 0x3 -#define TCPC_CC_STATUS_CC1_SHIFT 0 -#define TCPC_CC_STATUS_CC1_MASK 0x3 #define TCPC_POWER_STATUS 0x1e #define TCPC_POWER_STATUS_DBG_ACC_CON BIT(7) @@ -134,9 +129,8 @@ #define TCPC_MSG_HDR_INFO 0x2e #define TCPC_MSG_HDR_INFO_DATA_ROLE BIT(3) +#define TCPC_MSG_HDR_INFO_REV GENMASK(2, 1) #define TCPC_MSG_HDR_INFO_PWR_ROLE BIT(0) -#define TCPC_MSG_HDR_INFO_REV_SHIFT 1 -#define TCPC_MSG_HDR_INFO_REV_MASK 0x3 #define TCPC_RX_DETECT 0x2f #define TCPC_RX_DETECT_HARD_RESET BIT(5) @@ -154,10 +148,8 @@ #define TCPC_RX_DATA 0x34 /* through 0x4f */ #define TCPC_TRANSMIT 0x50 -#define TCPC_TRANSMIT_RETRY_SHIFT 4 -#define TCPC_TRANSMIT_RETRY_MASK 0x3 -#define TCPC_TRANSMIT_TYPE_SHIFT 0 -#define TCPC_TRANSMIT_TYPE_MASK 0x7 +#define TCPC_TRANSMIT_RETRY GENMASK(5, 4) +#define TCPC_TRANSMIT_TYPE GENMASK(2, 0) #define TCPC_TX_BYTE_CNT 0x51 #define TCPC_TX_HDR 0x52 @@ -178,8 +170,7 @@ #define tcpc_presenting_rd(reg, cc) \ (!(TCPC_ROLE_CTRL_DRP & (reg)) && \ - (((reg) & (TCPC_ROLE_CTRL_## cc ##_MASK << TCPC_ROLE_CTRL_## cc ##_SHIFT)) == \ - (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_## cc ##_SHIFT))) + FIELD_GET(TCPC_ROLE_CTRL_## cc, reg) == TCPC_ROLE_CTRL_CC_RD) struct tcpci; @@ -190,7 +181,7 @@ struct tcpci; * Optional; Callback to perform chip specific operations when FRS * is sourcing vbus. * @auto_discharge_disconnect: - * Optional; Enables TCPC to autonously discharge vbus on disconnect. + * Optional; Enables TCPC to autonomously discharge vbus on disconnect. * @vbus_vsafe0v: * optional; Set when TCPC can detect whether vbus is at VSAFE0V. * @set_partner_usb_comm_capable: @@ -256,7 +247,7 @@ static inline enum typec_cc_status tcpci_to_typec_cc(unsigned int cc, bool sink) if (sink) return TYPEC_CC_RP_3_0; fallthrough; - case 0x0: + case TCPC_CC_STATE_SRC_OPEN: default: return TYPEC_CC_OPEN; } diff --git a/include/linux/vdpa.h b/include/linux/vdpa.h index 7977ca03ac7a..2e7a30fe6b92 100644 --- a/include/linux/vdpa.h +++ b/include/linux/vdpa.h @@ -582,11 +582,20 @@ void vdpa_set_status(struct vdpa_device *vdev, u8 status); * @dev: vdpa device to remove * Driver need to remove the specified device by calling * _vdpa_unregister_device(). + * @dev_set_attr: change a vdpa device's attr after it was create + * @mdev: parent device to use for device + * @dev: vdpa device structure + * @config:Attributes to be set for the device. + * The driver needs to check the mask of the structure and then set + * the related information to the vdpa device. The driver must return 0 + * if set successfully. */ struct vdpa_mgmtdev_ops { int (*dev_add)(struct vdpa_mgmt_dev *mdev, const char *name, const struct vdpa_dev_set_config *config); void (*dev_del)(struct vdpa_mgmt_dev *mdev, struct vdpa_device *dev); + int (*dev_set_attr)(struct vdpa_mgmt_dev *mdev, struct vdpa_device *dev, + const struct vdpa_dev_set_config *config); }; /** diff --git a/include/uapi/linux/usb/ch9.h b/include/uapi/linux/usb/ch9.h index 44d73ba8788d..91f0f7e214a5 100644 --- a/include/uapi/linux/usb/ch9.h +++ b/include/uapi/linux/usb/ch9.h @@ -254,6 +254,9 @@ struct usb_ctrlrequest { #define USB_DT_DEVICE_CAPABILITY 0x10 #define USB_DT_WIRELESS_ENDPOINT_COMP 0x11 #define USB_DT_WIRE_ADAPTER 0x21 +/* From USB Device Firmware Upgrade Specification, Revision 1.1 */ +#define USB_DT_DFU_FUNCTIONAL 0x21 +/* these are from the Wireless USB spec */ #define USB_DT_RPIPE 0x22 #define USB_DT_CS_RADIO_CONTROL 0x23 /* From the T10 UAS specification */ @@ -329,9 +332,10 @@ struct usb_device_descriptor { #define USB_CLASS_USB_TYPE_C_BRIDGE 0x12 #define USB_CLASS_MISC 0xef #define USB_CLASS_APP_SPEC 0xfe -#define USB_CLASS_VENDOR_SPEC 0xff +#define USB_SUBCLASS_DFU 0x01 -#define USB_SUBCLASS_VENDOR_SPEC 0xff +#define USB_CLASS_VENDOR_SPEC 0xff +#define USB_SUBCLASS_VENDOR_SPEC 0xff /*-------------------------------------------------------------------------*/ diff --git a/include/uapi/linux/usb/functionfs.h b/include/uapi/linux/usb/functionfs.h index 9f88de9c3d66..2ebdba111a8f 100644 --- a/include/uapi/linux/usb/functionfs.h +++ b/include/uapi/linux/usb/functionfs.h @@ -3,6 +3,7 @@ #define _UAPI__LINUX_FUNCTIONFS_H__ +#include #include #include @@ -37,6 +38,31 @@ struct usb_endpoint_descriptor_no_audio { __u8 bInterval; } __attribute__((packed)); +/** + * struct usb_dfu_functional_descriptor - DFU Functional descriptor + * @bLength: Size of the descriptor (bytes) + * @bDescriptorType: USB_DT_DFU_FUNCTIONAL + * @bmAttributes: DFU attributes + * @wDetachTimeOut: Maximum time to wait after DFU_DETACH (ms, le16) + * @wTransferSize: Maximum number of bytes per control-write (le16) + * @bcdDFUVersion: DFU Spec version (BCD, le16) + */ +struct usb_dfu_functional_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bmAttributes; + __le16 wDetachTimeOut; + __le16 wTransferSize; + __le16 bcdDFUVersion; +} __attribute__ ((packed)); + +/* from DFU functional descriptor bmAttributes */ +#define DFU_FUNC_ATT_CAN_DOWNLOAD _BITUL(0) +#define DFU_FUNC_ATT_CAN_UPLOAD _BITUL(1) +#define DFU_FUNC_ATT_MANIFEST_TOLERANT _BITUL(2) +#define DFU_FUNC_ATT_WILL_DETACH _BITUL(3) + + struct usb_functionfs_descs_head_v2 { __le32 magic; __le32 length; @@ -104,23 +130,38 @@ struct usb_ffs_dmabuf_transfer_req { #ifndef __KERNEL__ -/* +/** + * DOC: descriptors + * * Descriptors format: * + * +-----+-----------+--------------+--------------------------------------+ * | off | name | type | description | - * |-----+-----------+--------------+--------------------------------------| + * +-----+-----------+--------------+--------------------------------------+ * | 0 | magic | LE32 | FUNCTIONFS_DESCRIPTORS_MAGIC_V2 | + * +-----+-----------+--------------+--------------------------------------+ * | 4 | length | LE32 | length of the whole data chunk | + * +-----+-----------+--------------+--------------------------------------+ * | 8 | flags | LE32 | combination of functionfs_flags | + * +-----+-----------+--------------+--------------------------------------+ * | | eventfd | LE32 | eventfd file descriptor | + * +-----+-----------+--------------+--------------------------------------+ * | | fs_count | LE32 | number of full-speed descriptors | + * +-----+-----------+--------------+--------------------------------------+ * | | hs_count | LE32 | number of high-speed descriptors | + * +-----+-----------+--------------+--------------------------------------+ * | | ss_count | LE32 | number of super-speed descriptors | + * +-----+-----------+--------------+--------------------------------------+ * | | os_count | LE32 | number of MS OS descriptors | + * +-----+-----------+--------------+--------------------------------------+ * | | fs_descrs | Descriptor[] | list of full-speed descriptors | + * +-----+-----------+--------------+--------------------------------------+ * | | hs_descrs | Descriptor[] | list of high-speed descriptors | + * +-----+-----------+--------------+--------------------------------------+ * | | ss_descrs | Descriptor[] | list of super-speed descriptors | + * +-----+-----------+--------------+--------------------------------------+ * | | os_descrs | OSDesc[] | list of MS OS descriptors | + * +-----+-----------+--------------+--------------------------------------+ * * Depending on which flags are set, various fields may be missing in the * structure. Any flags that are not recognised cause the whole block to be @@ -128,71 +169,111 @@ struct usb_ffs_dmabuf_transfer_req { * * Legacy descriptors format (deprecated as of 3.14): * + * +-----+-----------+--------------+--------------------------------------+ * | off | name | type | description | - * |-----+-----------+--------------+--------------------------------------| + * +-----+-----------+--------------+--------------------------------------+ * | 0 | magic | LE32 | FUNCTIONFS_DESCRIPTORS_MAGIC | + * +-----+-----------+--------------+--------------------------------------+ * | 4 | length | LE32 | length of the whole data chunk | + * +-----+-----------+--------------+--------------------------------------+ * | 8 | fs_count | LE32 | number of full-speed descriptors | + * +-----+-----------+--------------+--------------------------------------+ * | 12 | hs_count | LE32 | number of high-speed descriptors | + * +-----+-----------+--------------+--------------------------------------+ * | 16 | fs_descrs | Descriptor[] | list of full-speed descriptors | + * +-----+-----------+--------------+--------------------------------------+ * | | hs_descrs | Descriptor[] | list of high-speed descriptors | + * +-----+-----------+--------------+--------------------------------------+ * * All numbers must be in little endian order. * * Descriptor[] is an array of valid USB descriptors which have the following * format: * + * +-----+-----------------+------+--------------------------+ * | off | name | type | description | - * |-----+-----------------+------+--------------------------| + * +-----+-----------------+------+--------------------------+ * | 0 | bLength | U8 | length of the descriptor | + * +-----+-----------------+------+--------------------------+ * | 1 | bDescriptorType | U8 | descriptor type | + * +-----+-----------------+------+--------------------------+ * | 2 | payload | | descriptor's payload | + * +-----+-----------------+------+--------------------------+ * * OSDesc[] is an array of valid MS OS Feature Descriptors which have one of * the following formats: * + * +-----+-----------------+------+--------------------------+ * | off | name | type | description | - * |-----+-----------------+------+--------------------------| + * +-----+-----------------+------+--------------------------+ * | 0 | inteface | U8 | related interface number | + * +-----+-----------------+------+--------------------------+ * | 1 | dwLength | U32 | length of the descriptor | + * +-----+-----------------+------+--------------------------+ * | 5 | bcdVersion | U16 | currently supported: 1 | + * +-----+-----------------+------+--------------------------+ * | 7 | wIndex | U16 | currently supported: 4 | + * +-----+-----------------+------+--------------------------+ * | 9 | bCount | U8 | number of ext. compat. | + * +-----+-----------------+------+--------------------------+ * | 10 | Reserved | U8 | 0 | + * +-----+-----------------+------+--------------------------+ * | 11 | ExtCompat[] | | list of ext. compat. d. | + * +-----+-----------------+------+--------------------------+ * + * +-----+-----------------+------+--------------------------+ * | off | name | type | description | - * |-----+-----------------+------+--------------------------| + * +-----+-----------------+------+--------------------------+ * | 0 | inteface | U8 | related interface number | + * +-----+-----------------+------+--------------------------+ * | 1 | dwLength | U32 | length of the descriptor | + * +-----+-----------------+------+--------------------------+ * | 5 | bcdVersion | U16 | currently supported: 1 | + * +-----+-----------------+------+--------------------------+ * | 7 | wIndex | U16 | currently supported: 5 | + * +-----+-----------------+------+--------------------------+ * | 9 | wCount | U16 | number of ext. compat. | + * +-----+-----------------+------+--------------------------+ * | 11 | ExtProp[] | | list of ext. prop. d. | + * +-----+-----------------+------+--------------------------+ * * ExtCompat[] is an array of valid Extended Compatiblity descriptors * which have the following format: * + * +-----+-----------------------+------+-------------------------------------+ * | off | name | type | description | - * |-----+-----------------------+------+-------------------------------------| + * +-----+-----------------------+------+-------------------------------------+ * | 0 | bFirstInterfaceNumber | U8 | index of the interface or of the 1st| + * +-----+-----------------------+------+-------------------------------------+ * | | | | interface in an IAD group | + * +-----+-----------------------+------+-------------------------------------+ * | 1 | Reserved | U8 | 1 | + * +-----+-----------------------+------+-------------------------------------+ * | 2 | CompatibleID | U8[8]| compatible ID string | + * +-----+-----------------------+------+-------------------------------------+ * | 10 | SubCompatibleID | U8[8]| subcompatible ID string | + * +-----+-----------------------+------+-------------------------------------+ * | 18 | Reserved | U8[6]| 0 | + * +-----+-----------------------+------+-------------------------------------+ * * ExtProp[] is an array of valid Extended Properties descriptors * which have the following format: * + * +-----+-----------------------+------+-------------------------------------+ * | off | name | type | description | - * |-----+-----------------------+------+-------------------------------------| + * +-----+-----------------------+------+-------------------------------------+ * | 0 | dwSize | U32 | length of the descriptor | + * +-----+-----------------------+------+-------------------------------------+ * | 4 | dwPropertyDataType | U32 | 1..7 | + * +-----+-----------------------+------+-------------------------------------+ * | 8 | wPropertyNameLength | U16 | bPropertyName length (NL) | + * +-----+-----------------------+------+-------------------------------------+ * | 10 | bPropertyName |U8[NL]| name of this property | + * +-----+-----------------------+------+-------------------------------------+ * |10+NL| dwPropertyDataLength | U32 | bPropertyData length (DL) | + * +-----+-----------------------+------+-------------------------------------+ * |14+NL| bProperty |U8[DL]| payload of this property | + * +-----+-----------------------+------+-------------------------------------+ */ struct usb_functionfs_strings_head { diff --git a/include/uapi/linux/usb/g_hid.h b/include/uapi/linux/usb/g_hid.h new file mode 100644 index 000000000000..b965092db476 --- /dev/null +++ b/include/uapi/linux/usb/g_hid.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ + +#ifndef __UAPI_LINUX_USB_G_HID_H +#define __UAPI_LINUX_USB_G_HID_H + +#include + +/* Maximum HID report length for High-Speed USB (i.e. USB 2.0) */ +#define MAX_REPORT_LENGTH 64 + +/** + * struct usb_hidg_report - response to GET_REPORT + * @report_id: report ID that this is a response for + * @userspace_req: + * !0 this report is used for any pending GET_REPORT request + * but wait on userspace to issue a new report on future requests + * 0 this report is to be used for any future GET_REPORT requests + * @length: length of the report response + * @data: report response + * @padding: padding for 32/64 bit compatibility + * + * Structure used by GADGET_HID_WRITE_GET_REPORT ioctl on /dev/hidg*. + */ +struct usb_hidg_report { + __u8 report_id; + __u8 userspace_req; + __u16 length; + __u8 data[MAX_REPORT_LENGTH]; + __u8 padding[4]; +}; + +/* The 'g' code is used by gadgetfs and hid gadget ioctl requests. + * Don't add any colliding codes to either driver, and keep + * them in unique ranges. + */ + +#define GADGET_HID_READ_GET_REPORT_ID _IOR('g', 0x41, __u8) +#define GADGET_HID_WRITE_GET_REPORT _IOW('g', 0x42, struct usb_hidg_report) + +#endif /* __UAPI_LINUX_USB_G_HID_H */ diff --git a/include/uapi/linux/usb/gadgetfs.h b/include/uapi/linux/usb/gadgetfs.h index 835473910a49..9754822b2a40 100644 --- a/include/uapi/linux/usb/gadgetfs.h +++ b/include/uapi/linux/usb/gadgetfs.h @@ -62,7 +62,7 @@ struct usb_gadgetfs_event { }; -/* The 'g' code is also used by printer gadget ioctl requests. +/* The 'g' code is also used by printer and hid gadget ioctl requests. * Don't add any colliding codes to either driver, and keep * them in unique ranges (size 0x20 for now). */ diff --git a/include/uapi/linux/vdpa.h b/include/uapi/linux/vdpa.h index 842bf1201ac4..71edf2c70cc3 100644 --- a/include/uapi/linux/vdpa.h +++ b/include/uapi/linux/vdpa.h @@ -19,6 +19,7 @@ enum vdpa_command { VDPA_CMD_DEV_GET, /* can dump */ VDPA_CMD_DEV_CONFIG_GET, /* can dump */ VDPA_CMD_DEV_VSTATS_GET, + VDPA_CMD_DEV_ATTR_SET, }; enum vdpa_attr { diff --git a/include/uapi/linux/virtio_balloon.h b/include/uapi/linux/virtio_balloon.h index ddaa45e723c4..ee35a372805d 100644 --- a/include/uapi/linux/virtio_balloon.h +++ b/include/uapi/linux/virtio_balloon.h @@ -71,7 +71,13 @@ struct virtio_balloon_config { #define VIRTIO_BALLOON_S_CACHES 7 /* Disk caches */ #define VIRTIO_BALLOON_S_HTLB_PGALLOC 8 /* Hugetlb page allocations */ #define VIRTIO_BALLOON_S_HTLB_PGFAIL 9 /* Hugetlb page allocation failures */ -#define VIRTIO_BALLOON_S_NR 10 +#define VIRTIO_BALLOON_S_OOM_KILL 10 /* OOM killer invocations */ +#define VIRTIO_BALLOON_S_ALLOC_STALL 11 /* Stall count of memory allocatoin */ +#define VIRTIO_BALLOON_S_ASYNC_SCAN 12 /* Amount of memory scanned asynchronously */ +#define VIRTIO_BALLOON_S_DIRECT_SCAN 13 /* Amount of memory scanned directly */ +#define VIRTIO_BALLOON_S_ASYNC_RECLAIM 14 /* Amount of memory reclaimed asynchronously */ +#define VIRTIO_BALLOON_S_DIRECT_RECLAIM 15 /* Amount of memory reclaimed directly */ +#define VIRTIO_BALLOON_S_NR 16 #define VIRTIO_BALLOON_S_NAMES_WITH_PREFIX(VIRTIO_BALLOON_S_NAMES_prefix) { \ VIRTIO_BALLOON_S_NAMES_prefix "swap-in", \ @@ -83,7 +89,13 @@ struct virtio_balloon_config { VIRTIO_BALLOON_S_NAMES_prefix "available-memory", \ VIRTIO_BALLOON_S_NAMES_prefix "disk-caches", \ VIRTIO_BALLOON_S_NAMES_prefix "hugetlb-allocations", \ - VIRTIO_BALLOON_S_NAMES_prefix "hugetlb-failures" \ + VIRTIO_BALLOON_S_NAMES_prefix "hugetlb-failures", \ + VIRTIO_BALLOON_S_NAMES_prefix "oom-kills", \ + VIRTIO_BALLOON_S_NAMES_prefix "alloc-stalls", \ + VIRTIO_BALLOON_S_NAMES_prefix "async-scans", \ + VIRTIO_BALLOON_S_NAMES_prefix "direct-scans", \ + VIRTIO_BALLOON_S_NAMES_prefix "async-reclaims", \ + VIRTIO_BALLOON_S_NAMES_prefix "direct-reclaims" \ } #define VIRTIO_BALLOON_S_NAMES VIRTIO_BALLOON_S_NAMES_WITH_PREFIX("") diff --git a/kernel/trace/trace_fprobe.c b/kernel/trace/trace_fprobe.c index 62e6a8f4aae9..a079abd8955b 100644 --- a/kernel/trace/trace_fprobe.c +++ b/kernel/trace/trace_fprobe.c @@ -21,6 +21,7 @@ #define FPROBE_EVENT_SYSTEM "fprobes" #define TRACEPOINT_EVENT_SYSTEM "tracepoints" #define RETHOOK_MAXACTIVE_MAX 4096 +#define TRACEPOINT_STUB ERR_PTR(-ENOENT) static int trace_fprobe_create(const char *raw_command); static int trace_fprobe_show(struct seq_file *m, struct dyn_event *ev); @@ -385,6 +386,7 @@ static struct trace_fprobe *alloc_trace_fprobe(const char *group, const char *event, const char *symbol, struct tracepoint *tpoint, + struct module *mod, int maxactive, int nargs, bool is_return) { @@ -405,6 +407,7 @@ static struct trace_fprobe *alloc_trace_fprobe(const char *group, tf->fp.entry_handler = fentry_dispatcher; tf->tpoint = tpoint; + tf->mod = mod; tf->fp.nr_maxactive = maxactive; ret = trace_probe_init(&tf->tp, event, group, false, nargs); @@ -672,6 +675,24 @@ static int unregister_fprobe_event(struct trace_fprobe *tf) return trace_probe_unregister_event_call(&tf->tp); } +static int __regsiter_tracepoint_fprobe(struct trace_fprobe *tf) +{ + struct tracepoint *tpoint = tf->tpoint; + unsigned long ip = (unsigned long)tpoint->probestub; + int ret; + + /* + * Here, we do 2 steps to enable fprobe on a tracepoint. + * At first, put __probestub_##TP function on the tracepoint + * and put a fprobe on the stub function. + */ + ret = tracepoint_probe_register_prio_may_exist(tpoint, + tpoint->probestub, NULL, 0); + if (ret < 0) + return ret; + return register_fprobe_ips(&tf->fp, &ip, 1); +} + /* Internal register function - just handle fprobe and flags */ static int __register_trace_fprobe(struct trace_fprobe *tf) { @@ -698,18 +719,12 @@ static int __register_trace_fprobe(struct trace_fprobe *tf) tf->fp.flags |= FPROBE_FL_DISABLED; if (trace_fprobe_is_tracepoint(tf)) { - struct tracepoint *tpoint = tf->tpoint; - unsigned long ip = (unsigned long)tpoint->probestub; - /* - * Here, we do 2 steps to enable fprobe on a tracepoint. - * At first, put __probestub_##TP function on the tracepoint - * and put a fprobe on the stub function. - */ - ret = tracepoint_probe_register_prio_may_exist(tpoint, - tpoint->probestub, NULL, 0); - if (ret < 0) - return ret; - return register_fprobe_ips(&tf->fp, &ip, 1); + + /* This tracepoint is not loaded yet */ + if (tf->tpoint == TRACEPOINT_STUB) + return 0; + + return __regsiter_tracepoint_fprobe(tf); } /* TODO: handle filter, nofilter or symbol list */ @@ -862,20 +877,106 @@ end: return ret; } +struct __find_tracepoint_cb_data { + const char *tp_name; + struct tracepoint *tpoint; + struct module *mod; +}; + +static void __find_tracepoint_module_cb(struct tracepoint *tp, struct module *mod, void *priv) +{ + struct __find_tracepoint_cb_data *data = priv; + + if (!data->tpoint && !strcmp(data->tp_name, tp->name)) { + data->tpoint = tp; + if (!data->mod) { + data->mod = mod; + if (!try_module_get(data->mod)) { + data->tpoint = NULL; + data->mod = NULL; + } + } + } +} + +static void __find_tracepoint_cb(struct tracepoint *tp, void *priv) +{ + struct __find_tracepoint_cb_data *data = priv; + + if (!data->tpoint && !strcmp(data->tp_name, tp->name)) + data->tpoint = tp; +} + +/* + * Find a tracepoint from kernel and module. If the tracepoint is in a module, + * this increments the module refcount to prevent unloading until the + * trace_fprobe is registered to the list. After registering the trace_fprobe + * on the trace_fprobe list, the module refcount is decremented because + * tracepoint_probe_module_cb will handle it. + */ +static struct tracepoint *find_tracepoint(const char *tp_name, + struct module **tp_mod) +{ + struct __find_tracepoint_cb_data data = { + .tp_name = tp_name, + .mod = NULL, + }; + + for_each_kernel_tracepoint(__find_tracepoint_cb, &data); + + if (!data.tpoint && IS_ENABLED(CONFIG_MODULES)) { + for_each_module_tracepoint(__find_tracepoint_module_cb, &data); + *tp_mod = data.mod; + } + + return data.tpoint; +} + #ifdef CONFIG_MODULES +static void reenable_trace_fprobe(struct trace_fprobe *tf) +{ + struct trace_probe *tp = &tf->tp; + + list_for_each_entry(tf, trace_probe_probe_list(tp), tp.list) { + __enable_trace_fprobe(tf); + } +} + +static struct tracepoint *find_tracepoint_in_module(struct module *mod, + const char *tp_name) +{ + struct __find_tracepoint_cb_data data = { + .tp_name = tp_name, + .mod = mod, + }; + + for_each_tracepoint_in_module(mod, __find_tracepoint_module_cb, &data); + return data.tpoint; +} + static int __tracepoint_probe_module_cb(struct notifier_block *self, unsigned long val, void *data) { struct tp_module *tp_mod = data; + struct tracepoint *tpoint; struct trace_fprobe *tf; struct dyn_event *pos; - if (val != MODULE_STATE_GOING) + if (val != MODULE_STATE_GOING && val != MODULE_STATE_COMING) return NOTIFY_DONE; mutex_lock(&event_mutex); for_each_trace_fprobe(tf, pos) { - if (tp_mod->mod == tf->mod) { + if (val == MODULE_STATE_COMING && tf->tpoint == TRACEPOINT_STUB) { + tpoint = find_tracepoint_in_module(tp_mod->mod, tf->symbol); + if (tpoint) { + tf->tpoint = tpoint; + tf->mod = tp_mod->mod; + if (!WARN_ON_ONCE(__regsiter_tracepoint_fprobe(tf)) && + trace_probe_is_enabled(&tf->tp)) + reenable_trace_fprobe(tf); + } + } else if (val == MODULE_STATE_GOING && tp_mod->mod == tf->mod) { tracepoint_probe_unregister(tf->tpoint, tf->tpoint->probestub, NULL); tf->tpoint = NULL; @@ -892,30 +993,6 @@ static struct notifier_block tracepoint_module_nb = { }; #endif /* CONFIG_MODULES */ -struct __find_tracepoint_cb_data { - const char *tp_name; - struct tracepoint *tpoint; -}; - -static void __find_tracepoint_cb(struct tracepoint *tp, void *priv) -{ - struct __find_tracepoint_cb_data *data = priv; - - if (!data->tpoint && !strcmp(data->tp_name, tp->name)) - data->tpoint = tp; -} - -static struct tracepoint *find_tracepoint(const char *tp_name) -{ - struct __find_tracepoint_cb_data data = { - .tp_name = tp_name, - }; - - for_each_kernel_tracepoint(__find_tracepoint_cb, &data); - - return data.tpoint; -} - static int parse_symbol_and_return(int argc, const char *argv[], char **symbol, bool *is_return, bool is_tracepoint) @@ -996,6 +1073,7 @@ static int __trace_fprobe_create(int argc, const char *argv[]) char abuf[MAX_BTF_ARGS_LEN]; char *dbuf = NULL; bool is_tracepoint = false; + struct module *tp_mod = NULL; struct tracepoint *tpoint = NULL; struct traceprobe_parse_context ctx = { .flags = TPARG_FL_KERNEL | TPARG_FL_FPROBE, @@ -1080,15 +1158,20 @@ static int __trace_fprobe_create(int argc, const char *argv[]) if (is_tracepoint) { ctx.flags |= TPARG_FL_TPOINT; - tpoint = find_tracepoint(symbol); - if (!tpoint) { + tpoint = find_tracepoint(symbol, &tp_mod); + if (tpoint) { + ctx.funcname = kallsyms_lookup( + (unsigned long)tpoint->probestub, + NULL, NULL, NULL, sbuf); + } else if (IS_ENABLED(CONFIG_MODULES)) { + /* This *may* be loaded afterwards */ + tpoint = TRACEPOINT_STUB; + ctx.funcname = symbol; + } else { trace_probe_log_set_index(1); trace_probe_log_err(0, NO_TRACEPOINT); goto parse_error; } - ctx.funcname = kallsyms_lookup( - (unsigned long)tpoint->probestub, - NULL, NULL, NULL, sbuf); } else ctx.funcname = symbol; @@ -1110,8 +1193,8 @@ static int __trace_fprobe_create(int argc, const char *argv[]) goto out; /* setup a probe */ - tf = alloc_trace_fprobe(group, event, symbol, tpoint, maxactive, - argc, is_return); + tf = alloc_trace_fprobe(group, event, symbol, tpoint, tp_mod, + maxactive, argc, is_return); if (IS_ERR(tf)) { ret = PTR_ERR(tf); /* This must return -ENOMEM, else there is a bug */ @@ -1119,10 +1202,6 @@ static int __trace_fprobe_create(int argc, const char *argv[]) goto out; /* We know tf is not allocated */ } - if (is_tracepoint) - tf->mod = __module_text_address( - (unsigned long)tf->tpoint->probestub); - /* parse arguments */ for (i = 0; i < argc && i < MAX_TRACE_ARGS; i++) { trace_probe_log_set_index(i + 2); @@ -1155,6 +1234,8 @@ static int __trace_fprobe_create(int argc, const char *argv[]) } out: + if (tp_mod) + module_put(tp_mod); traceprobe_finish_parse(&ctx); trace_probe_log_clear(); kfree(new_argv); diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c index f7443e996b1b..c40531d2cbad 100644 --- a/kernel/trace/trace_uprobe.c +++ b/kernel/trace/trace_uprobe.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "trace_dynevent.h" #include "trace_probe.h" @@ -62,7 +63,7 @@ struct trace_uprobe { struct uprobe *uprobe; unsigned long offset; unsigned long ref_ctr_offset; - unsigned long nhit; + unsigned long __percpu *nhits; struct trace_probe tp; }; @@ -337,6 +338,12 @@ alloc_trace_uprobe(const char *group, const char *event, int nargs, bool is_ret) if (!tu) return ERR_PTR(-ENOMEM); + tu->nhits = alloc_percpu(unsigned long); + if (!tu->nhits) { + ret = -ENOMEM; + goto error; + } + ret = trace_probe_init(&tu->tp, event, group, true, nargs); if (ret < 0) goto error; @@ -349,6 +356,7 @@ alloc_trace_uprobe(const char *group, const char *event, int nargs, bool is_ret) return tu; error: + free_percpu(tu->nhits); kfree(tu); return ERR_PTR(ret); @@ -362,6 +370,7 @@ static void free_trace_uprobe(struct trace_uprobe *tu) path_put(&tu->path); trace_probe_cleanup(&tu->tp); kfree(tu->filename); + free_percpu(tu->nhits); kfree(tu); } @@ -815,13 +824,21 @@ static int probes_profile_seq_show(struct seq_file *m, void *v) { struct dyn_event *ev = v; struct trace_uprobe *tu; + unsigned long nhits; + int cpu; if (!is_trace_uprobe(ev)) return 0; tu = to_trace_uprobe(ev); + + nhits = 0; + for_each_possible_cpu(cpu) { + nhits += per_cpu(*tu->nhits, cpu); + } + seq_printf(m, " %s %-44s %15lu\n", tu->filename, - trace_probe_name(&tu->tp), tu->nhit); + trace_probe_name(&tu->tp), nhits); return 0; } @@ -1508,7 +1525,8 @@ static int uprobe_dispatcher(struct uprobe_consumer *con, struct pt_regs *regs) int ret = 0; tu = container_of(con, struct trace_uprobe, consumer); - tu->nhit++; + + this_cpu_inc(*tu->nhits); udd.tu = tu; udd.bp_addr = instruction_pointer(regs); diff --git a/kernel/tracepoint.c b/kernel/tracepoint.c index 78c730516aa6..af6e167614e5 100644 --- a/kernel/tracepoint.c +++ b/kernel/tracepoint.c @@ -736,6 +736,48 @@ static __init int init_tracepoints(void) return ret; } __initcall(init_tracepoints); + +/** + * for_each_tracepoint_in_module - iteration on all tracepoints in a module + * @mod: module + * @fct: callback + * @priv: private data + */ +void for_each_tracepoint_in_module(struct module *mod, + void (*fct)(struct tracepoint *tp, + struct module *mod, void *priv), + void *priv) +{ + tracepoint_ptr_t *begin, *end, *iter; + + lockdep_assert_held(&tracepoint_module_list_mutex); + + if (!mod) + return; + + begin = mod->tracepoints_ptrs; + end = mod->tracepoints_ptrs + mod->num_tracepoints; + + for (iter = begin; iter < end; iter++) + fct(tracepoint_ptr_deref(iter), mod, priv); +} + +/** + * for_each_module_tracepoint - iteration on all tracepoints in all modules + * @fct: callback + * @priv: private data + */ +void for_each_module_tracepoint(void (*fct)(struct tracepoint *tp, + struct module *mod, void *priv), + void *priv) +{ + struct tp_module *tp_mod; + + mutex_lock(&tracepoint_module_list_mutex); + list_for_each_entry(tp_mod, &tracepoint_module_list, list) + for_each_tracepoint_in_module(tp_mod->mod, fct, priv); + mutex_unlock(&tracepoint_module_list_mutex); +} #endif /* CONFIG_MODULES */ /** diff --git a/lib/sbitmap.c b/lib/sbitmap.c index 5e2e93307f0d..d3412984170c 100644 --- a/lib/sbitmap.c +++ b/lib/sbitmap.c @@ -65,7 +65,7 @@ static inline bool sbitmap_deferred_clear(struct sbitmap_word *map, { unsigned long mask, word_mask; - guard(spinlock_irqsave)(&map->swap_lock); + guard(raw_spinlock_irqsave)(&map->swap_lock); if (!map->cleared) { if (depth == 0) @@ -136,7 +136,7 @@ int sbitmap_init_node(struct sbitmap *sb, unsigned int depth, int shift, } for (i = 0; i < sb->map_nr; i++) - spin_lock_init(&sb->map[i].swap_lock); + raw_spin_lock_init(&sb->map[i].swap_lock); return 0; } diff --git a/net/9p/Kconfig b/net/9p/Kconfig index bcdab9c23b40..63f988f0c9e8 100644 --- a/net/9p/Kconfig +++ b/net/9p/Kconfig @@ -40,6 +40,12 @@ config NET_9P_XEN This builds support for a transport for 9pfs between two Xen domains. +config NET_9P_USBG + bool "9P USB Gadget Transport" + depends on USB_GADGET=y || USB_GADGET=NET_9P + help + This builds support for a transport for 9pfs over + usb gadget. config NET_9P_RDMA depends on INET && INFINIBAND && INFINIBAND_ADDR_TRANS diff --git a/net/9p/Makefile b/net/9p/Makefile index 1df9b344c30b..22794a451c3f 100644 --- a/net/9p/Makefile +++ b/net/9p/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_NET_9P_FD) += 9pnet_fd.o obj-$(CONFIG_NET_9P_XEN) += 9pnet_xen.o obj-$(CONFIG_NET_9P_VIRTIO) += 9pnet_virtio.o obj-$(CONFIG_NET_9P_RDMA) += 9pnet_rdma.o +obj-$(CONFIG_NET_9P_USBG) += 9pnet_usbg.o 9pnet-objs := \ mod.o \ @@ -23,3 +24,6 @@ obj-$(CONFIG_NET_9P_RDMA) += 9pnet_rdma.o 9pnet_rdma-objs := \ trans_rdma.o \ + +9pnet_usbg-objs := \ + trans_usbg.o \ diff --git a/net/9p/trans_usbg.c b/net/9p/trans_usbg.c new file mode 100644 index 000000000000..975b76839dca --- /dev/null +++ b/net/9p/trans_usbg.c @@ -0,0 +1,956 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * trans_usbg.c - USB peripheral usb9pfs configuration driver and transport. + * + * Copyright (C) 2024 Michael Grzeschik + */ + +/* Gadget usb9pfs only needs two bulk endpoints, and will use the usb9pfs + * transport to mount host exported filesystem via usb gadget. + */ + +/* +--------------------------+ | +--------------------------+ + * | 9PFS mounting client | | | 9PFS exporting server | + * SW | | | | | + * | (this:trans_usbg) | | |(e.g. diod or nfs-ganesha)| + * +-------------^------------+ | +-------------^------------+ + * | | | + * ------------------|------------------------------------|------------- + * | | | + * +-------------v------------+ | +-------------v------------+ + * | | | | | + * HW | USB Device Controller <---------> USB Host Controller | + * | | | | | + * +--------------------------+ | +--------------------------+ + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#define DEFAULT_BUFLEN 16384 + +struct f_usb9pfs { + struct p9_client *client; + + /* 9p request lock for en/dequeue */ + spinlock_t lock; + + struct usb_request *in_req; + struct usb_request *out_req; + + struct usb_ep *in_ep; + struct usb_ep *out_ep; + + struct completion send; + struct completion received; + + unsigned int buflen; + + struct usb_function function; +}; + +static inline struct f_usb9pfs *func_to_usb9pfs(struct usb_function *f) +{ + return container_of(f, struct f_usb9pfs, function); +} + +struct f_usb9pfs_opts { + struct usb_function_instance func_inst; + unsigned int buflen; + + struct f_usb9pfs_dev *dev; + + /* Read/write access to configfs attributes is handled by configfs. + * + * This is to protect the data from concurrent access by read/write + * and create symlink/remove symlink. + */ + struct mutex lock; + int refcnt; +}; + +struct f_usb9pfs_dev { + struct f_usb9pfs *usb9pfs; + struct f_usb9pfs_opts *opts; + char tag[41]; + bool inuse; + + struct list_head usb9pfs_instance; +}; + +static DEFINE_MUTEX(usb9pfs_lock); +static struct list_head usbg_instance_list; + +static int usb9pfs_queue_tx(struct f_usb9pfs *usb9pfs, struct p9_req_t *p9_tx_req, + gfp_t gfp_flags) +{ + struct usb_composite_dev *cdev = usb9pfs->function.config->cdev; + struct usb_request *req = usb9pfs->in_req; + int ret; + + if (!(p9_tx_req->tc.size % usb9pfs->in_ep->maxpacket)) + req->zero = 1; + + req->buf = p9_tx_req->tc.sdata; + req->length = p9_tx_req->tc.size; + req->context = p9_tx_req; + + dev_dbg(&cdev->gadget->dev, "%s usb9pfs send --> %d/%d, zero: %d\n", + usb9pfs->in_ep->name, req->actual, req->length, req->zero); + + ret = usb_ep_queue(usb9pfs->in_ep, req, gfp_flags); + if (ret) + req->context = NULL; + + dev_dbg(&cdev->gadget->dev, "tx submit --> %d\n", ret); + + return ret; +} + +static int usb9pfs_queue_rx(struct f_usb9pfs *usb9pfs, struct usb_request *req, + gfp_t gfp_flags) +{ + struct usb_composite_dev *cdev = usb9pfs->function.config->cdev; + int ret; + + ret = usb_ep_queue(usb9pfs->out_ep, req, gfp_flags); + + dev_dbg(&cdev->gadget->dev, "rx submit --> %d\n", ret); + + return ret; +} + +static int usb9pfs_transmit(struct f_usb9pfs *usb9pfs, struct p9_req_t *p9_req) +{ + int ret = 0; + + guard(spinlock_irqsave)(&usb9pfs->lock); + + ret = usb9pfs_queue_tx(usb9pfs, p9_req, GFP_ATOMIC); + if (ret) + return ret; + + list_del(&p9_req->req_list); + + p9_req_get(p9_req); + + return ret; +} + +static void usb9pfs_tx_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_usb9pfs *usb9pfs = ep->driver_data; + struct usb_composite_dev *cdev = usb9pfs->function.config->cdev; + struct p9_req_t *p9_tx_req = req->context; + unsigned long flags; + + /* reset zero packages */ + req->zero = 0; + + if (req->status) { + dev_err(&cdev->gadget->dev, "%s usb9pfs complete --> %d, %d/%d\n", + ep->name, req->status, req->actual, req->length); + return; + } + + dev_dbg(&cdev->gadget->dev, "%s usb9pfs complete --> %d, %d/%d\n", + ep->name, req->status, req->actual, req->length); + + spin_lock_irqsave(&usb9pfs->lock, flags); + WRITE_ONCE(p9_tx_req->status, REQ_STATUS_SENT); + + p9_req_put(usb9pfs->client, p9_tx_req); + + req->context = NULL; + + spin_unlock_irqrestore(&usb9pfs->lock, flags); + + complete(&usb9pfs->send); +} + +static struct p9_req_t *usb9pfs_rx_header(struct f_usb9pfs *usb9pfs, void *buf) +{ + struct p9_req_t *p9_rx_req; + struct p9_fcall rc; + int ret; + + /* start by reading header */ + rc.sdata = buf; + rc.offset = 0; + rc.capacity = P9_HDRSZ; + rc.size = P9_HDRSZ; + + p9_debug(P9_DEBUG_TRANS, "mux %p got %zu bytes\n", usb9pfs, + rc.capacity - rc.offset); + + ret = p9_parse_header(&rc, &rc.size, NULL, NULL, 0); + if (ret) { + p9_debug(P9_DEBUG_ERROR, + "error parsing header: %d\n", ret); + return NULL; + } + + p9_debug(P9_DEBUG_TRANS, + "mux %p pkt: size: %d bytes tag: %d\n", + usb9pfs, rc.size, rc.tag); + + p9_rx_req = p9_tag_lookup(usb9pfs->client, rc.tag); + if (!p9_rx_req || p9_rx_req->status != REQ_STATUS_SENT) { + p9_debug(P9_DEBUG_ERROR, "Unexpected packet tag %d\n", rc.tag); + return NULL; + } + + if (rc.size > p9_rx_req->rc.capacity) { + p9_debug(P9_DEBUG_ERROR, + "requested packet size too big: %d for tag %d with capacity %zd\n", + rc.size, rc.tag, p9_rx_req->rc.capacity); + p9_req_put(usb9pfs->client, p9_rx_req); + return NULL; + } + + if (!p9_rx_req->rc.sdata) { + p9_debug(P9_DEBUG_ERROR, + "No recv fcall for tag %d (req %p), disconnecting!\n", + rc.tag, p9_rx_req); + p9_req_put(usb9pfs->client, p9_rx_req); + return NULL; + } + + return p9_rx_req; +} + +static void usb9pfs_rx_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_usb9pfs *usb9pfs = ep->driver_data; + struct usb_composite_dev *cdev = usb9pfs->function.config->cdev; + struct p9_req_t *p9_rx_req; + + if (req->status) { + dev_err(&cdev->gadget->dev, "%s usb9pfs complete --> %d, %d/%d\n", + ep->name, req->status, req->actual, req->length); + return; + } + + p9_rx_req = usb9pfs_rx_header(usb9pfs, req->buf); + if (!p9_rx_req) + return; + + memcpy(p9_rx_req->rc.sdata, req->buf, req->actual); + + p9_rx_req->rc.size = req->actual; + + p9_client_cb(usb9pfs->client, p9_rx_req, REQ_STATUS_RCVD); + p9_req_put(usb9pfs->client, p9_rx_req); + + complete(&usb9pfs->received); +} + +static void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep) +{ + int value; + + value = usb_ep_disable(ep); + if (value < 0) + dev_info(&cdev->gadget->dev, + "disable %s --> %d\n", ep->name, value); +} + +static void disable_usb9pfs(struct f_usb9pfs *usb9pfs) +{ + struct usb_composite_dev *cdev = + usb9pfs->function.config->cdev; + + if (usb9pfs->in_req) { + usb_ep_free_request(usb9pfs->in_ep, usb9pfs->in_req); + usb9pfs->in_req = NULL; + } + + if (usb9pfs->out_req) { + usb_ep_free_request(usb9pfs->out_ep, usb9pfs->out_req); + usb9pfs->out_req = NULL; + } + + disable_ep(cdev, usb9pfs->in_ep); + disable_ep(cdev, usb9pfs->out_ep); + dev_dbg(&cdev->gadget->dev, "%s disabled\n", + usb9pfs->function.name); +} + +static int alloc_requests(struct usb_composite_dev *cdev, + struct f_usb9pfs *usb9pfs) +{ + int ret; + + usb9pfs->in_req = usb_ep_alloc_request(usb9pfs->in_ep, GFP_ATOMIC); + if (!usb9pfs->in_req) { + ret = -ENOENT; + goto fail; + } + + usb9pfs->out_req = alloc_ep_req(usb9pfs->out_ep, usb9pfs->buflen); + if (!usb9pfs->out_req) { + ret = -ENOENT; + goto fail_in; + } + + usb9pfs->in_req->complete = usb9pfs_tx_complete; + usb9pfs->out_req->complete = usb9pfs_rx_complete; + + /* length will be set in complete routine */ + usb9pfs->in_req->context = usb9pfs; + usb9pfs->out_req->context = usb9pfs; + + return 0; + +fail_in: + usb_ep_free_request(usb9pfs->in_ep, usb9pfs->in_req); +fail: + return ret; +} + +static int enable_endpoint(struct usb_composite_dev *cdev, + struct f_usb9pfs *usb9pfs, struct usb_ep *ep) +{ + int ret; + + ret = config_ep_by_speed(cdev->gadget, &usb9pfs->function, ep); + if (ret) + return ret; + + ret = usb_ep_enable(ep); + if (ret < 0) + return ret; + + ep->driver_data = usb9pfs; + + return 0; +} + +static int +enable_usb9pfs(struct usb_composite_dev *cdev, struct f_usb9pfs *usb9pfs) +{ + struct p9_client *client; + int ret = 0; + + ret = enable_endpoint(cdev, usb9pfs, usb9pfs->in_ep); + if (ret) + goto out; + + ret = enable_endpoint(cdev, usb9pfs, usb9pfs->out_ep); + if (ret) + goto disable_in; + + ret = alloc_requests(cdev, usb9pfs); + if (ret) + goto disable_out; + + client = usb9pfs->client; + if (client) + client->status = Connected; + + dev_dbg(&cdev->gadget->dev, "%s enabled\n", usb9pfs->function.name); + return 0; + +disable_out: + usb_ep_disable(usb9pfs->out_ep); +disable_in: + usb_ep_disable(usb9pfs->in_ep); +out: + return ret; +} + +static int p9_usbg_create(struct p9_client *client, const char *devname, char *args) +{ + struct f_usb9pfs_dev *dev; + struct f_usb9pfs *usb9pfs; + int ret = -ENOENT; + int found = 0; + + if (!devname) + return -EINVAL; + + guard(mutex)(&usb9pfs_lock); + + list_for_each_entry(dev, &usbg_instance_list, usb9pfs_instance) { + if (!strncmp(devname, dev->tag, strlen(devname))) { + if (!dev->inuse) { + dev->inuse = true; + found = 1; + break; + } + ret = -EBUSY; + break; + } + } + + if (!found) { + pr_err("no channels available for device %s\n", devname); + return ret; + } + + usb9pfs = dev->usb9pfs; + if (!usb9pfs) + return -EINVAL; + + client->trans = (void *)usb9pfs; + if (!usb9pfs->in_req) + client->status = Disconnected; + else + client->status = Connected; + usb9pfs->client = client; + + client->trans_mod->maxsize = usb9pfs->buflen; + + complete(&usb9pfs->received); + + return 0; +} + +static void usb9pfs_clear_tx(struct f_usb9pfs *usb9pfs) +{ + struct p9_req_t *req; + + guard(spinlock_irqsave)(&usb9pfs->lock); + + req = usb9pfs->in_req->context; + if (!req) + return; + + if (!req->t_err) + req->t_err = -ECONNRESET; + + p9_client_cb(usb9pfs->client, req, REQ_STATUS_ERROR); +} + +static void p9_usbg_close(struct p9_client *client) +{ + struct f_usb9pfs *usb9pfs; + struct f_usb9pfs_dev *dev; + struct f_usb9pfs_opts *opts; + + if (!client) + return; + + usb9pfs = client->trans; + if (!usb9pfs) + return; + + client->status = Disconnected; + + usb9pfs_clear_tx(usb9pfs); + + opts = container_of(usb9pfs->function.fi, + struct f_usb9pfs_opts, func_inst); + + dev = opts->dev; + + mutex_lock(&usb9pfs_lock); + dev->inuse = false; + mutex_unlock(&usb9pfs_lock); +} + +static int p9_usbg_request(struct p9_client *client, struct p9_req_t *p9_req) +{ + struct f_usb9pfs *usb9pfs = client->trans; + int ret; + + if (client->status != Connected) + return -EBUSY; + + ret = wait_for_completion_killable(&usb9pfs->received); + if (ret) + return ret; + + ret = usb9pfs_transmit(usb9pfs, p9_req); + if (ret) + return ret; + + ret = wait_for_completion_killable(&usb9pfs->send); + if (ret) + return ret; + + return usb9pfs_queue_rx(usb9pfs, usb9pfs->out_req, GFP_ATOMIC); +} + +static int p9_usbg_cancel(struct p9_client *client, struct p9_req_t *req) +{ + struct f_usb9pfs *usb9pfs = client->trans; + int ret = 1; + + p9_debug(P9_DEBUG_TRANS, "client %p req %p\n", client, req); + + guard(spinlock_irqsave)(&usb9pfs->lock); + + if (req->status == REQ_STATUS_UNSENT) { + list_del(&req->req_list); + WRITE_ONCE(req->status, REQ_STATUS_FLSHD); + p9_req_put(client, req); + ret = 0; + } + + return ret; +} + +static struct p9_trans_module p9_usbg_trans = { + .name = "usbg", + .create = p9_usbg_create, + .close = p9_usbg_close, + .request = p9_usbg_request, + .cancel = p9_usbg_cancel, + .owner = THIS_MODULE, +}; + +/*-------------------------------------------------------------------------*/ + +#define USB_PROTOCOL_9PFS 0x09 + +static struct usb_interface_descriptor usb9pfs_intf = { + .bLength = sizeof(usb9pfs_intf), + .bDescriptorType = USB_DT_INTERFACE, + + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = USB_SUBCLASS_VENDOR_SPEC, + .bInterfaceProtocol = USB_PROTOCOL_9PFS, + + /* .iInterface = DYNAMIC */ +}; + +/* full speed support: */ + +static struct usb_endpoint_descriptor fs_usb9pfs_source_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor fs_usb9pfs_sink_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_descriptor_header *fs_usb9pfs_descs[] = { + (struct usb_descriptor_header *)&usb9pfs_intf, + (struct usb_descriptor_header *)&fs_usb9pfs_sink_desc, + (struct usb_descriptor_header *)&fs_usb9pfs_source_desc, + NULL, +}; + +/* high speed support: */ + +static struct usb_endpoint_descriptor hs_usb9pfs_source_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor hs_usb9pfs_sink_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), +}; + +static struct usb_descriptor_header *hs_usb9pfs_descs[] = { + (struct usb_descriptor_header *)&usb9pfs_intf, + (struct usb_descriptor_header *)&hs_usb9pfs_source_desc, + (struct usb_descriptor_header *)&hs_usb9pfs_sink_desc, + NULL, +}; + +/* super speed support: */ + +static struct usb_endpoint_descriptor ss_usb9pfs_source_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +static struct usb_ss_ep_comp_descriptor ss_usb9pfs_source_comp_desc = { + .bLength = USB_DT_SS_EP_COMP_SIZE, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + .bMaxBurst = 0, + .bmAttributes = 0, + .wBytesPerInterval = 0, +}; + +static struct usb_endpoint_descriptor ss_usb9pfs_sink_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +static struct usb_ss_ep_comp_descriptor ss_usb9pfs_sink_comp_desc = { + .bLength = USB_DT_SS_EP_COMP_SIZE, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + .bMaxBurst = 0, + .bmAttributes = 0, + .wBytesPerInterval = 0, +}; + +static struct usb_descriptor_header *ss_usb9pfs_descs[] = { + (struct usb_descriptor_header *)&usb9pfs_intf, + (struct usb_descriptor_header *)&ss_usb9pfs_source_desc, + (struct usb_descriptor_header *)&ss_usb9pfs_source_comp_desc, + (struct usb_descriptor_header *)&ss_usb9pfs_sink_desc, + (struct usb_descriptor_header *)&ss_usb9pfs_sink_comp_desc, + NULL, +}; + +/* function-specific strings: */ +static struct usb_string strings_usb9pfs[] = { + [0].s = "usb9pfs input to output", + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_usb9pfs = { + .language = 0x0409, /* en-us */ + .strings = strings_usb9pfs, +}; + +static struct usb_gadget_strings *usb9pfs_strings[] = { + &stringtab_usb9pfs, + NULL, +}; + +/*-------------------------------------------------------------------------*/ + +static int usb9pfs_func_bind(struct usb_configuration *c, + struct usb_function *f) +{ + struct f_usb9pfs *usb9pfs = func_to_usb9pfs(f); + struct f_usb9pfs_opts *opts; + struct usb_composite_dev *cdev = c->cdev; + int ret; + int id; + + /* allocate interface ID(s) */ + id = usb_interface_id(c, f); + if (id < 0) + return id; + usb9pfs_intf.bInterfaceNumber = id; + + id = usb_string_id(cdev); + if (id < 0) + return id; + strings_usb9pfs[0].id = id; + usb9pfs_intf.iInterface = id; + + /* allocate endpoints */ + usb9pfs->in_ep = usb_ep_autoconfig(cdev->gadget, + &fs_usb9pfs_source_desc); + if (!usb9pfs->in_ep) + goto autoconf_fail; + + usb9pfs->out_ep = usb_ep_autoconfig(cdev->gadget, + &fs_usb9pfs_sink_desc); + if (!usb9pfs->out_ep) + goto autoconf_fail; + + /* support high speed hardware */ + hs_usb9pfs_source_desc.bEndpointAddress = + fs_usb9pfs_source_desc.bEndpointAddress; + hs_usb9pfs_sink_desc.bEndpointAddress = + fs_usb9pfs_sink_desc.bEndpointAddress; + + /* support super speed hardware */ + ss_usb9pfs_source_desc.bEndpointAddress = + fs_usb9pfs_source_desc.bEndpointAddress; + ss_usb9pfs_sink_desc.bEndpointAddress = + fs_usb9pfs_sink_desc.bEndpointAddress; + + ret = usb_assign_descriptors(f, fs_usb9pfs_descs, hs_usb9pfs_descs, + ss_usb9pfs_descs, ss_usb9pfs_descs); + if (ret) + return ret; + + opts = container_of(f->fi, struct f_usb9pfs_opts, func_inst); + opts->dev->usb9pfs = usb9pfs; + + dev_dbg(&cdev->gadget->dev, "%s speed %s: IN/%s, OUT/%s\n", + (gadget_is_superspeed(c->cdev->gadget) ? "super" : + (gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")), + f->name, usb9pfs->in_ep->name, usb9pfs->out_ep->name); + + return 0; + +autoconf_fail: + ERROR(cdev, "%s: can't autoconfigure on %s\n", + f->name, cdev->gadget->name); + return -ENODEV; +} + +static void usb9pfs_func_unbind(struct usb_configuration *c, + struct usb_function *f) +{ + struct f_usb9pfs *usb9pfs = func_to_usb9pfs(f); + + disable_usb9pfs(usb9pfs); +} + +static void usb9pfs_free_func(struct usb_function *f) +{ + struct f_usb9pfs *usb9pfs = func_to_usb9pfs(f); + struct f_usb9pfs_opts *opts; + + kfree(usb9pfs); + + opts = container_of(f->fi, struct f_usb9pfs_opts, func_inst); + + mutex_lock(&opts->lock); + opts->refcnt--; + mutex_unlock(&opts->lock); + + usb_free_all_descriptors(f); +} + +static int usb9pfs_set_alt(struct usb_function *f, + unsigned int intf, unsigned int alt) +{ + struct f_usb9pfs *usb9pfs = func_to_usb9pfs(f); + struct usb_composite_dev *cdev = f->config->cdev; + + return enable_usb9pfs(cdev, usb9pfs); +} + +static void usb9pfs_disable(struct usb_function *f) +{ + struct f_usb9pfs *usb9pfs = func_to_usb9pfs(f); + + usb9pfs_clear_tx(usb9pfs); +} + +static struct usb_function *usb9pfs_alloc(struct usb_function_instance *fi) +{ + struct f_usb9pfs_opts *usb9pfs_opts; + struct f_usb9pfs *usb9pfs; + + usb9pfs = kzalloc(sizeof(*usb9pfs), GFP_KERNEL); + if (!usb9pfs) + return ERR_PTR(-ENOMEM); + + spin_lock_init(&usb9pfs->lock); + + init_completion(&usb9pfs->send); + init_completion(&usb9pfs->received); + + usb9pfs_opts = container_of(fi, struct f_usb9pfs_opts, func_inst); + + mutex_lock(&usb9pfs_opts->lock); + usb9pfs_opts->refcnt++; + mutex_unlock(&usb9pfs_opts->lock); + + usb9pfs->buflen = usb9pfs_opts->buflen; + + usb9pfs->function.name = "usb9pfs"; + usb9pfs->function.bind = usb9pfs_func_bind; + usb9pfs->function.unbind = usb9pfs_func_unbind; + usb9pfs->function.set_alt = usb9pfs_set_alt; + usb9pfs->function.disable = usb9pfs_disable; + usb9pfs->function.strings = usb9pfs_strings; + + usb9pfs->function.free_func = usb9pfs_free_func; + + return &usb9pfs->function; +} + +static inline struct f_usb9pfs_opts *to_f_usb9pfs_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_usb9pfs_opts, + func_inst.group); +} + +static inline struct f_usb9pfs_opts *fi_to_f_usb9pfs_opts(struct usb_function_instance *fi) +{ + return container_of(fi, struct f_usb9pfs_opts, func_inst); +} + +static void usb9pfs_attr_release(struct config_item *item) +{ + struct f_usb9pfs_opts *usb9pfs_opts = to_f_usb9pfs_opts(item); + + usb_put_function_instance(&usb9pfs_opts->func_inst); +} + +static struct configfs_item_operations usb9pfs_item_ops = { + .release = usb9pfs_attr_release, +}; + +static ssize_t f_usb9pfs_opts_buflen_show(struct config_item *item, char *page) +{ + struct f_usb9pfs_opts *opts = to_f_usb9pfs_opts(item); + int ret; + + mutex_lock(&opts->lock); + ret = sysfs_emit(page, "%d\n", opts->buflen); + mutex_unlock(&opts->lock); + + return ret; +} + +static ssize_t f_usb9pfs_opts_buflen_store(struct config_item *item, + const char *page, size_t len) +{ + struct f_usb9pfs_opts *opts = to_f_usb9pfs_opts(item); + int ret; + u32 num; + + guard(mutex)(&opts->lock); + + if (opts->refcnt) + return -EBUSY; + + ret = kstrtou32(page, 0, &num); + if (ret) + return ret; + + opts->buflen = num; + + return len; +} + +CONFIGFS_ATTR(f_usb9pfs_opts_, buflen); + +static struct configfs_attribute *usb9pfs_attrs[] = { + &f_usb9pfs_opts_attr_buflen, + NULL, +}; + +static const struct config_item_type usb9pfs_func_type = { + .ct_item_ops = &usb9pfs_item_ops, + .ct_attrs = usb9pfs_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct f_usb9pfs_dev *_usb9pfs_do_find_dev(const char *tag) +{ + struct f_usb9pfs_dev *usb9pfs_dev; + + if (!tag) + return NULL; + + list_for_each_entry(usb9pfs_dev, &usbg_instance_list, usb9pfs_instance) { + if (strcmp(usb9pfs_dev->tag, tag) == 0) + return usb9pfs_dev; + } + + return NULL; +} + +static int usb9pfs_tag_instance(struct f_usb9pfs_dev *dev, const char *tag) +{ + struct f_usb9pfs_dev *existing; + int ret = 0; + + guard(mutex)(&usb9pfs_lock); + + existing = _usb9pfs_do_find_dev(tag); + if (!existing) + strscpy(dev->tag, tag, ARRAY_SIZE(dev->tag)); + else if (existing != dev) + ret = -EBUSY; + + return ret; +} + +static int usb9pfs_set_inst_tag(struct usb_function_instance *fi, const char *tag) +{ + if (strlen(tag) >= sizeof_field(struct f_usb9pfs_dev, tag)) + return -ENAMETOOLONG; + return usb9pfs_tag_instance(fi_to_f_usb9pfs_opts(fi)->dev, tag); +} + +static void usb9pfs_free_instance(struct usb_function_instance *fi) +{ + struct f_usb9pfs_opts *usb9pfs_opts = + container_of(fi, struct f_usb9pfs_opts, func_inst); + struct f_usb9pfs_dev *dev = usb9pfs_opts->dev; + + mutex_lock(&usb9pfs_lock); + list_del(&dev->usb9pfs_instance); + mutex_unlock(&usb9pfs_lock); + + kfree(usb9pfs_opts); +} + +static struct usb_function_instance *usb9pfs_alloc_instance(void) +{ + struct f_usb9pfs_opts *usb9pfs_opts; + struct f_usb9pfs_dev *dev; + + usb9pfs_opts = kzalloc(sizeof(*usb9pfs_opts), GFP_KERNEL); + if (!usb9pfs_opts) + return ERR_PTR(-ENOMEM); + + mutex_init(&usb9pfs_opts->lock); + + usb9pfs_opts->func_inst.set_inst_name = usb9pfs_set_inst_tag; + usb9pfs_opts->func_inst.free_func_inst = usb9pfs_free_instance; + + usb9pfs_opts->buflen = DEFAULT_BUFLEN; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (IS_ERR(dev)) { + kfree(usb9pfs_opts); + return ERR_CAST(dev); + } + + usb9pfs_opts->dev = dev; + dev->opts = usb9pfs_opts; + + config_group_init_type_name(&usb9pfs_opts->func_inst.group, "", + &usb9pfs_func_type); + + mutex_lock(&usb9pfs_lock); + list_add_tail(&dev->usb9pfs_instance, &usbg_instance_list); + mutex_unlock(&usb9pfs_lock); + + return &usb9pfs_opts->func_inst; +} +DECLARE_USB_FUNCTION(usb9pfs, usb9pfs_alloc_instance, usb9pfs_alloc); + +static int __init usb9pfs_modinit(void) +{ + int ret; + + INIT_LIST_HEAD(&usbg_instance_list); + + ret = usb_function_register(&usb9pfsusb_func); + if (!ret) + v9fs_register_trans(&p9_usbg_trans); + + return ret; +} + +static void __exit usb9pfs_modexit(void) +{ + usb_function_unregister(&usb9pfsusb_func); + v9fs_unregister_trans(&p9_usbg_trans); +} + +module_init(usb9pfs_modinit); +module_exit(usb9pfs_modexit); + +MODULE_ALIAS_9P("usbg"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("USB gadget 9pfs transport"); +MODULE_AUTHOR("Michael Grzeschik"); diff --git a/net/vmw_vsock/virtio_transport.c b/net/vmw_vsock/virtio_transport.c index e0160da4ef43..85e423921734 100644 --- a/net/vmw_vsock/virtio_transport.c +++ b/net/vmw_vsock/virtio_transport.c @@ -94,6 +94,63 @@ out_rcu: return ret; } +/* Caller need to hold vsock->tx_lock on vq */ +static int virtio_transport_send_skb(struct sk_buff *skb, struct virtqueue *vq, + struct virtio_vsock *vsock) +{ + int ret, in_sg = 0, out_sg = 0; + struct scatterlist **sgs; + + sgs = vsock->out_sgs; + sg_init_one(sgs[out_sg], virtio_vsock_hdr(skb), + sizeof(*virtio_vsock_hdr(skb))); + out_sg++; + + if (!skb_is_nonlinear(skb)) { + if (skb->len > 0) { + sg_init_one(sgs[out_sg], skb->data, skb->len); + out_sg++; + } + } else { + struct skb_shared_info *si; + int i; + + /* If skb is nonlinear, then its buffer must contain + * only header and nothing more. Data is stored in + * the fragged part. + */ + WARN_ON_ONCE(skb_headroom(skb) != sizeof(*virtio_vsock_hdr(skb))); + + si = skb_shinfo(skb); + + for (i = 0; i < si->nr_frags; i++) { + skb_frag_t *skb_frag = &si->frags[i]; + void *va; + + /* We will use 'page_to_virt()' for the userspace page + * here, because virtio or dma-mapping layers will call + * 'virt_to_phys()' later to fill the buffer descriptor. + * We don't touch memory at "virtual" address of this page. + */ + va = page_to_virt(skb_frag_page(skb_frag)); + sg_init_one(sgs[out_sg], + va + skb_frag_off(skb_frag), + skb_frag_size(skb_frag)); + out_sg++; + } + } + + ret = virtqueue_add_sgs(vq, sgs, out_sg, in_sg, skb, GFP_KERNEL); + /* Usually this means that there is no more space available in + * the vq + */ + if (ret < 0) + return ret; + + virtio_transport_deliver_tap_pkt(skb); + return 0; +} + static void virtio_transport_send_pkt_work(struct work_struct *work) { @@ -111,66 +168,22 @@ virtio_transport_send_pkt_work(struct work_struct *work) vq = vsock->vqs[VSOCK_VQ_TX]; for (;;) { - int ret, in_sg = 0, out_sg = 0; - struct scatterlist **sgs; struct sk_buff *skb; bool reply; + int ret; skb = virtio_vsock_skb_dequeue(&vsock->send_pkt_queue); if (!skb) break; reply = virtio_vsock_skb_reply(skb); - sgs = vsock->out_sgs; - sg_init_one(sgs[out_sg], virtio_vsock_hdr(skb), - sizeof(*virtio_vsock_hdr(skb))); - out_sg++; - if (!skb_is_nonlinear(skb)) { - if (skb->len > 0) { - sg_init_one(sgs[out_sg], skb->data, skb->len); - out_sg++; - } - } else { - struct skb_shared_info *si; - int i; - - /* If skb is nonlinear, then its buffer must contain - * only header and nothing more. Data is stored in - * the fragged part. - */ - WARN_ON_ONCE(skb_headroom(skb) != sizeof(*virtio_vsock_hdr(skb))); - - si = skb_shinfo(skb); - - for (i = 0; i < si->nr_frags; i++) { - skb_frag_t *skb_frag = &si->frags[i]; - void *va; - - /* We will use 'page_to_virt()' for the userspace page - * here, because virtio or dma-mapping layers will call - * 'virt_to_phys()' later to fill the buffer descriptor. - * We don't touch memory at "virtual" address of this page. - */ - va = page_to_virt(skb_frag_page(skb_frag)); - sg_init_one(sgs[out_sg], - va + skb_frag_off(skb_frag), - skb_frag_size(skb_frag)); - out_sg++; - } - } - - ret = virtqueue_add_sgs(vq, sgs, out_sg, in_sg, skb, GFP_KERNEL); - /* Usually this means that there is no more space available in - * the vq - */ + ret = virtio_transport_send_skb(skb, vq, vsock); if (ret < 0) { virtio_vsock_skb_queue_head(&vsock->send_pkt_queue, skb); break; } - virtio_transport_deliver_tap_pkt(skb); - if (reply) { struct virtqueue *rx_vq = vsock->vqs[VSOCK_VQ_RX]; int val; @@ -195,6 +208,28 @@ out: queue_work(virtio_vsock_workqueue, &vsock->rx_work); } +/* Caller need to hold RCU for vsock. + * Returns 0 if the packet is successfully put on the vq. + */ +static int virtio_transport_send_skb_fast_path(struct virtio_vsock *vsock, struct sk_buff *skb) +{ + struct virtqueue *vq = vsock->vqs[VSOCK_VQ_TX]; + int ret; + + /* Inside RCU, can't sleep! */ + ret = mutex_trylock(&vsock->tx_lock); + if (unlikely(ret == 0)) + return -EBUSY; + + ret = virtio_transport_send_skb(skb, vq, vsock); + if (ret == 0) + virtqueue_kick(vq); + + mutex_unlock(&vsock->tx_lock); + + return ret; +} + static int virtio_transport_send_pkt(struct sk_buff *skb) { @@ -218,11 +253,20 @@ virtio_transport_send_pkt(struct sk_buff *skb) goto out_rcu; } - if (virtio_vsock_skb_reply(skb)) - atomic_inc(&vsock->queued_replies); + /* If send_pkt_queue is empty, we can safely bypass this queue + * because packet order is maintained and (try) to put the packet + * on the virtqueue using virtio_transport_send_skb_fast_path. + * If this fails we simply put the packet on the intermediate + * queue and schedule the worker. + */ + if (!skb_queue_empty_lockless(&vsock->send_pkt_queue) || + virtio_transport_send_skb_fast_path(vsock, skb)) { + if (virtio_vsock_skb_reply(skb)) + atomic_inc(&vsock->queued_replies); - virtio_vsock_skb_queue_tail(&vsock->send_pkt_queue, skb); - queue_work(virtio_vsock_workqueue, &vsock->send_pkt_work); + virtio_vsock_skb_queue_tail(&vsock->send_pkt_queue, skb); + queue_work(virtio_vsock_workqueue, &vsock->send_pkt_work); + } out_rcu: rcu_read_unlock(); diff --git a/tools/testing/selftests/ftrace/config b/tools/testing/selftests/ftrace/config index 048a312abf40..544de0db5f58 100644 --- a/tools/testing/selftests/ftrace/config +++ b/tools/testing/selftests/ftrace/config @@ -20,6 +20,7 @@ CONFIG_PREEMPT_TRACER=y CONFIG_PROBE_EVENTS_BTF_ARGS=y CONFIG_SAMPLES=y CONFIG_SAMPLE_FTRACE_DIRECT=m +CONFIG_SAMPLE_TRACE_EVENTS=m CONFIG_SAMPLE_TRACE_PRINTK=m CONFIG_SCHED_TRACER=y CONFIG_STACK_TRACER=y diff --git a/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_tprobe_module.tc b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_tprobe_module.tc new file mode 100644 index 000000000000..d319d5ed4226 --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_tprobe_module.tc @@ -0,0 +1,61 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# description: Generic dynamic event - add/remove tracepoint probe events on module +# requires: dynamic_events "t[:[/][]] []":README + +rmmod trace-events-sample ||: +if ! modprobe trace-events-sample ; then + echo "No trace-events sample module - please make CONFIG_SAMPLE_TRACE_EVENTS=m" + exit_unresolved; +fi +trap "rmmod trace-events-sample" EXIT + +echo 0 > events/enable +echo > dynamic_events + +TRACEPOINT1=foo_bar +TRACEPOINT2=foo_bar_with_cond + +echo "t:myevent1 $TRACEPOINT1" >> dynamic_events +echo "t:myevent2 $TRACEPOINT2" >> dynamic_events + +grep -q myevent1 dynamic_events +grep -q myevent2 dynamic_events +test -d events/tracepoints/myevent1 +test -d events/tracepoints/myevent2 + +echo "-:myevent2" >> dynamic_events + +grep -q myevent1 dynamic_events +! grep -q myevent2 dynamic_events + +echo > dynamic_events + +clear_trace + +:;: "Try to put a probe on a tracepoint in non-loaded module" ;: +rmmod trace-events-sample + +echo "t:myevent1 $TRACEPOINT1" >> dynamic_events +echo "t:myevent2 $TRACEPOINT2" >> dynamic_events + +grep -q myevent1 dynamic_events +grep -q myevent2 dynamic_events +test -d events/tracepoints/myevent1 +test -d events/tracepoints/myevent2 + +echo 1 > events/tracepoints/enable + +modprobe trace-events-sample + +sleep 2 + +grep -q "myevent1" trace +grep -q "myevent2" trace + +rmmod trace-events-sample +trap "" EXIT + +echo 0 > events/tracepoints/enable +echo > dynamic_events +clear_trace diff --git a/tools/testing/selftests/ftrace/test.d/dynevent/tprobe_syntax_errors.tc b/tools/testing/selftests/ftrace/test.d/dynevent/tprobe_syntax_errors.tc index da117b8f1d12..ffe8ffef4027 100644 --- a/tools/testing/selftests/ftrace/test.d/dynevent/tprobe_syntax_errors.tc +++ b/tools/testing/selftests/ftrace/test.d/dynevent/tprobe_syntax_errors.tc @@ -9,7 +9,6 @@ check_error() { # command-with-error-pos-by-^ check_error 't^100 kfree' # BAD_MAXACT_TYPE -check_error 't ^non_exist_tracepoint' # NO_TRACEPOINT check_error 't:^/bar kfree' # NO_GROUP_NAME check_error 't:^12345678901234567890123456789012345678901234567890123456789012345/bar kfree' # GROUP_TOO_LONG diff --git a/tools/usb/p9_fwd.py b/tools/usb/p9_fwd.py new file mode 100755 index 000000000000..12c76cbb046b --- /dev/null +++ b/tools/usb/p9_fwd.py @@ -0,0 +1,243 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 + +import argparse +import errno +import logging +import socket +import struct +import time + +import usb.core +import usb.util + + +def path_from_usb_dev(dev): + """Takes a pyUSB device as argument and returns a string. + The string is a Path representation of the position of the USB device on the USB bus tree. + + This path is used to find a USB device on the bus or all devices connected to a HUB. + The path is made up of the number of the USB controller followed be the ports of the HUB tree.""" + if dev.port_numbers: + dev_path = ".".join(str(i) for i in dev.port_numbers) + return f"{dev.bus}-{dev_path}" + return "" + + +HEXDUMP_FILTER = "".join(chr(x).isprintable() and chr(x) or "." for x in range(128)) + "." * 128 + + +class Forwarder: + @staticmethod + def _log_hexdump(data): + if not logging.root.isEnabledFor(logging.TRACE): + return + L = 16 + for c in range(0, len(data), L): + chars = data[c : c + L] + dump = " ".join(f"{x:02x}" for x in chars) + printable = "".join(HEXDUMP_FILTER[x] for x in chars) + line = f"{c:08x} {dump:{L*3}s} |{printable:{L}s}|" + logging.root.log(logging.TRACE, "%s", line) + + def __init__(self, server, vid, pid, path): + self.stats = { + "c2s packets": 0, + "c2s bytes": 0, + "s2c packets": 0, + "s2c bytes": 0, + } + self.stats_logged = time.monotonic() + + def find_filter(dev): + dev_path = path_from_usb_dev(dev) + if path is not None: + return dev_path == path + return True + + dev = usb.core.find(idVendor=vid, idProduct=pid, custom_match=find_filter) + if dev is None: + raise ValueError("Device not found") + + logging.info(f"found device: {dev.bus}/{dev.address} located at {path_from_usb_dev(dev)}") + + # dev.set_configuration() is not necessary since g_multi has only one + usb9pfs = None + # g_multi adds 9pfs as last interface + cfg = dev.get_active_configuration() + for intf in cfg: + # we have to detach the usb-storage driver from multi gadget since + # stall option could be set, which will lead to spontaneous port + # resets and our transfers will run dead + if intf.bInterfaceClass == 0x08: + if dev.is_kernel_driver_active(intf.bInterfaceNumber): + dev.detach_kernel_driver(intf.bInterfaceNumber) + + if intf.bInterfaceClass == 0xFF and intf.bInterfaceSubClass == 0xFF and intf.bInterfaceProtocol == 0x09: + usb9pfs = intf + if usb9pfs is None: + raise ValueError("Interface not found") + + logging.info(f"claiming interface:\n{usb9pfs}") + usb.util.claim_interface(dev, usb9pfs.bInterfaceNumber) + ep_out = usb.util.find_descriptor( + usb9pfs, + custom_match=lambda e: usb.util.endpoint_direction(e.bEndpointAddress) == usb.util.ENDPOINT_OUT, + ) + assert ep_out is not None + ep_in = usb.util.find_descriptor( + usb9pfs, + custom_match=lambda e: usb.util.endpoint_direction(e.bEndpointAddress) == usb.util.ENDPOINT_IN, + ) + assert ep_in is not None + logging.info("interface claimed") + + self.ep_out = ep_out + self.ep_in = ep_in + self.dev = dev + + # create and connect socket + self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.s.connect(server) + + logging.info("connected to server") + + def c2s(self): + """forward a request from the USB client to the TCP server""" + data = None + while data is None: + try: + logging.log(logging.TRACE, "c2s: reading") + data = self.ep_in.read(self.ep_in.wMaxPacketSize) + except usb.core.USBTimeoutError: + logging.log(logging.TRACE, "c2s: reading timed out") + continue + except usb.core.USBError as e: + if e.errno == errno.EIO: + logging.debug("c2s: reading failed with %s, retrying", repr(e)) + time.sleep(0.5) + continue + logging.error("c2s: reading failed with %s, aborting", repr(e)) + raise + size = struct.unpack(" 0 + data = data[written:] + if size % self.ep_out.wMaxPacketSize == 0: + logging.log(logging.TRACE, "sending zero length packet") + self.ep_out.write(b"") + logging.debug("s2c: forwarded %i bytes", size) + self.stats["s2c packets"] += 1 + self.stats["s2c bytes"] += size + + def log_stats(self): + logging.info("statistics:") + for k, v in self.stats.items(): + logging.info(f" {k+':':14s} {v}") + + def log_stats_interval(self, interval=5): + if (time.monotonic() - self.stats_logged) < interval: + return + + self.log_stats() + self.stats_logged = time.monotonic() + + +def try_get_usb_str(dev, name): + try: + with open(f"/sys/bus/usb/devices/{dev.bus}-{dev.address}/{name}") as f: + return f.read().strip() + except FileNotFoundError: + return None + + +def list_usb(args): + vid, pid = [int(x, 16) for x in args.id.split(":", 1)] + + print("Bus | Addr | Manufacturer | Product | ID | Path") + print("--- | ---- | ---------------- | ---------------- | --------- | ----") + for dev in usb.core.find(find_all=True, idVendor=vid, idProduct=pid): + path = path_from_usb_dev(dev) or "" + manufacturer = try_get_usb_str(dev, "manufacturer") or "unknown" + product = try_get_usb_str(dev, "product") or "unknown" + print( + f"{dev.bus:3} | {dev.address:4} | {manufacturer:16} | {product:16} | {dev.idVendor:04x}:{dev.idProduct:04x} | {path:18}" + ) + + +def connect(args): + vid, pid = [int(x, 16) for x in args.id.split(":", 1)] + + f = Forwarder(server=(args.server, args.port), vid=vid, pid=pid, path=args.path) + + try: + while True: + f.c2s() + f.s2c() + f.log_stats_interval() + finally: + f.log_stats() + + +def main(): + parser = argparse.ArgumentParser( + description="Forward 9PFS requests from USB to TCP", + ) + + parser.add_argument("--id", type=str, default="1d6b:0109", help="vid:pid of target device") + parser.add_argument("--path", type=str, required=False, help="path of target device") + parser.add_argument("-v", "--verbose", action="count", default=0) + + subparsers = parser.add_subparsers() + subparsers.required = True + subparsers.dest = "command" + + parser_list = subparsers.add_parser("list", help="List all connected 9p gadgets") + parser_list.set_defaults(func=list_usb) + + parser_connect = subparsers.add_parser( + "connect", help="Forward messages between the usb9pfs gadget and the 9p server" + ) + parser_connect.set_defaults(func=connect) + connect_group = parser_connect.add_argument_group() + connect_group.required = True + parser_connect.add_argument("-s", "--server", type=str, default="127.0.0.1", help="server hostname") + parser_connect.add_argument("-p", "--port", type=int, default=564, help="server port") + + args = parser.parse_args() + + logging.TRACE = logging.DEBUG - 5 + logging.addLevelName(logging.TRACE, "TRACE") + + if args.verbose >= 2: + level = logging.TRACE + elif args.verbose: + level = logging.DEBUG + else: + level = logging.INFO + logging.basicConfig(level=level, format="%(asctime)-15s %(levelname)-8s %(message)s") + + args.func(args) + + +if __name__ == "__main__": + main() diff --git a/tools/virtio/ringtest/main.c b/tools/virtio/ringtest/main.c index 5a18b2301a63..e471d8e7cfaa 100644 --- a/tools/virtio/ringtest/main.c +++ b/tools/virtio/ringtest/main.c @@ -276,7 +276,7 @@ static void help(void) fprintf(stderr, "Usage: [--help]" " [--host-affinity H]" " [--guest-affinity G]" - " [--ring-size R (default: %d)]" + " [--ring-size R (default: %u)]" " [--run-cycles C (default: %d)]" " [--batch b]" " [--outstanding o]"