diff --git a/Documentation/ABI/testing/sysfs-bus-thunderbolt b/Documentation/ABI/testing/sysfs-bus-thunderbolt index d7f09d011b6d..c41c68f64693 100644 --- a/Documentation/ABI/testing/sysfs-bus-thunderbolt +++ b/Documentation/ABI/testing/sysfs-bus-thunderbolt @@ -1,31 +1,3 @@ -What: /sys/bus/thunderbolt/devices//rx_speed -Date: Feb 2021 -KernelVersion: 5.11 -Contact: Isaac Hazan -Description: This attribute reports the XDomain RX speed per lane. - All RX lanes run at the same speed. - -What: /sys/bus/thunderbolt/devices//rx_lanes -Date: Feb 2021 -KernelVersion: 5.11 -Contact: Isaac Hazan -Description: This attribute reports the number of RX lanes the XDomain - is using simultaneously through its upstream port. - -What: /sys/bus/thunderbolt/devices//tx_speed -Date: Feb 2021 -KernelVersion: 5.11 -Contact: Isaac Hazan -Description: This attribute reports the XDomain TX speed per lane. - All TX lanes run at the same speed. - -What: /sys/bus/thunderbolt/devices//tx_lanes -Date: Feb 2021 -KernelVersion: 5.11 -Contact: Isaac Hazan -Description: This attribute reports number of TX lanes the XDomain - is using simultaneously through its upstream port. - What: /sys/bus/thunderbolt/devices/.../domainX/boot_acl Date: Jun 2018 KernelVersion: 4.17 @@ -162,6 +134,13 @@ Contact: thunderbolt-software@lists.01.org Description: This attribute contains name of this device extracted from the device DROM. +What: /sys/bus/thunderbolt/devices/.../maxhopid +Date: Jul 2021 +KernelVersion: 5.13 +Contact: Mika Westerberg +Description: Only set for XDomains. The maximum HopID the other host + supports as its input HopID. + What: /sys/bus/thunderbolt/devices/.../rx_speed Date: Jan 2020 KernelVersion: 5.5 diff --git a/Documentation/devicetree/bindings/connector/usb-connector.yaml b/Documentation/devicetree/bindings/connector/usb-connector.yaml index b6daedd62516..32509b98142e 100644 --- a/Documentation/devicetree/bindings/connector/usb-connector.yaml +++ b/Documentation/devicetree/bindings/connector/usb-connector.yaml @@ -197,6 +197,16 @@ properties: $ref: /schemas/types.yaml#/definitions/uint32 enum: [1, 2, 3] + slow-charger-loop: + description: Allows PMIC charger loops which are slow(i.e. cannot meet the 15ms deadline) to + still comply to pSnkStby i.e Maximum power that can be consumed by sink while in Sink Standby + state as defined in 7.4.2 Sink Electrical Parameters of USB Power Delivery Specification + Revision 3.0, Version 1.2. When the property is set, the port requests pSnkStby(2.5W - + 5V@500mA) upon entering SNK_DISCOVERY(instead of 3A or the 1.5A, Rp current advertised, during + SNK_DISCOVERY) and the actual currrent limit after reception of PS_Ready for PD link or during + SNK_READY for non-pd link. + type: boolean + required: - compatible diff --git a/Documentation/devicetree/bindings/usb/dwc3-xilinx.txt b/Documentation/devicetree/bindings/usb/dwc3-xilinx.txt index a668f43bedf5..04813a46e5d0 100644 --- a/Documentation/devicetree/bindings/usb/dwc3-xilinx.txt +++ b/Documentation/devicetree/bindings/usb/dwc3-xilinx.txt @@ -1,32 +1,56 @@ Xilinx SuperSpeed DWC3 USB SoC controller Required properties: -- compatible: Should contain "xlnx,zynqmp-dwc3" +- compatible: May contain "xlnx,zynqmp-dwc3" or "xlnx,versal-dwc3" +- reg: Base address and length of the register control block - clocks: A list of phandles for the clocks listed in clock-names - clock-names: Should contain the following: "bus_clk" Master/Core clock, have to be >= 125 MHz for SS operation and >= 60MHz for HS operation "ref_clk" Clock source to core during PHY power down +- resets: A list of phandles for resets listed in reset-names +- reset-names: + "usb_crst" USB core reset + "usb_hibrst" USB hibernation reset + "usb_apbrst" USB APB reset Required child node: A child node must exist to represent the core DWC3 IP block. The name of the node is not important. The content of the node is defined in dwc3.txt. +Optional properties for snps,dwc3: +- dma-coherent: Enable this flag if CCI is enabled in design. Adding this + flag configures Global SoC bus Configuration Register and + Xilinx USB 3.0 IP - USB coherency register to enable CCI. +- interrupt-names: Should contain the following: + "dwc_usb3" USB gadget mode interrupts + "otg" USB OTG mode interrupts + "hiber" USB hibernation interrupts + Example device node: usb@0 { #address-cells = <0x2>; #size-cells = <0x1>; compatible = "xlnx,zynqmp-dwc3"; + reg = <0x0 0xff9d0000 0x0 0x100>; clock-names = "bus_clk", "ref_clk"; clocks = <&clk125>, <&clk125>; + resets = <&zynqmp_reset ZYNQMP_RESET_USB1_CORERESET>, + <&zynqmp_reset ZYNQMP_RESET_USB1_HIBERRESET>, + <&zynqmp_reset ZYNQMP_RESET_USB1_APB>; + reset-names = "usb_crst", "usb_hibrst", "usb_apbrst"; ranges; dwc3@fe200000 { compatible = "snps,dwc3"; reg = <0x0 0xfe200000 0x40000>; - interrupts = <0x0 0x41 0x4>; + interrupt-names = "dwc_usb3", "otg", "hiber"; + interrupts = <0 65 4>, <0 69 4>, <0 75 4>; + phys = <&psgtr 2 PHY_TYPE_USB3 0 2>; + phy-names = "usb3-phy"; dr_mode = "host"; + dma-coherent; }; }; diff --git a/Documentation/devicetree/bindings/usb/fsl,imx8mp-dwc3.yaml b/Documentation/devicetree/bindings/usb/fsl,imx8mp-dwc3.yaml index cb4c6f6d3a33..974032b1fda0 100644 --- a/Documentation/devicetree/bindings/usb/fsl,imx8mp-dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/fsl,imx8mp-dwc3.yaml @@ -52,11 +52,8 @@ properties: # Required child node: patternProperties: - "^dwc3@[0-9a-f]+$": - type: object - description: - A child node must exist to represent the core DWC3 IP block - The content of the node is defined in dwc3.txt. + "^usb@[0-9a-f]+$": + $ref: snps,dwc3.yaml# required: - compatible @@ -87,7 +84,7 @@ examples: dma-ranges = <0x40000000 0x40000000 0xc0000000>; ranges; - dwc3@38100000 { + usb@38100000 { compatible = "snps,dwc3"; reg = <0x38100000 0x10000>; clocks = <&clk IMX8MP_CLK_HSIO_AXI>, diff --git a/Documentation/devicetree/bindings/usb/generic-ehci.yaml b/Documentation/devicetree/bindings/usb/generic-ehci.yaml index cf83f2d9afac..8089dc956ba3 100644 --- a/Documentation/devicetree/bindings/usb/generic-ehci.yaml +++ b/Documentation/devicetree/bindings/usb/generic-ehci.yaml @@ -122,6 +122,12 @@ properties: description: Set this flag to force EHCI reset after resume. + spurious-oc: + $ref: /schemas/types.yaml#/definitions/flag + description: + Set this flag to indicate that the hardware sometimes turns on + the OC bit when an over-current isn't actually present. + companion: $ref: /schemas/types.yaml#/definitions/phandle description: diff --git a/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.yaml b/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.yaml index 14f40efb3b22..240882b12565 100644 --- a/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.yaml +++ b/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.yaml @@ -30,6 +30,7 @@ properties: - mediatek,mt7629-xhci - mediatek,mt8173-xhci - mediatek,mt8183-xhci + - mediatek,mt8192-xhci - const: mediatek,mtk-xhci reg: @@ -45,7 +46,18 @@ properties: - const: ippc # optional, only needed for case 1. interrupts: - maxItems: 1 + description: + use "interrupts-extended" when the interrupts are connected to the + separate interrupt controllers + minItems: 1 + items: + - description: xHCI host controller interrupt + - description: optional, wakeup interrupt used to support runtime PM + + interrupt-names: + items: + - const: host + - const: wakeup power-domains: description: A phandle to USB power domain node to control USB's MTCMOS @@ -99,9 +111,9 @@ properties: vbus-supply: description: Regulator of USB VBUS5v - usb3-lpm-capable: - description: supports USB3.0 LPM - type: boolean + usb3-lpm-capable: true + + usb2-lpm-disable: true imod-interval-ns: description: @@ -127,10 +139,13 @@ properties: - description: The second cell represents the register base address of the glue layer in syscon - - description: + - description: | The third cell represents the hardware version of the glue layer, - 1 is used by mt8173 etc, 2 is used by mt2712 etc - enum: [1, 2] + 1 - used by mt8173 etc, revision 1 without following IPM rule; + 2 - used by mt2712 etc, revision 2 following IPM rule; + 101 - used by mt8183, specific 1.01; + 102 - used by mt8192, specific 1.02; + enum: [1, 2, 101, 102] mediatek,u3p-dis-msk: $ref: /schemas/types.yaml#/definitions/uint32 diff --git a/Documentation/devicetree/bindings/usb/mediatek,mtu3.yaml b/Documentation/devicetree/bindings/usb/mediatek,mtu3.yaml index f5c04b9d2de9..dbc7876e0a0b 100644 --- a/Documentation/devicetree/bindings/usb/mediatek,mtu3.yaml +++ b/Documentation/devicetree/bindings/usb/mediatek,mtu3.yaml @@ -24,6 +24,7 @@ properties: - mediatek,mt2712-mtu3 - mediatek,mt8173-mtu3 - mediatek,mt8183-mtu3 + - mediatek,mt8192-mtu3 - const: mediatek,mtu3 reg: @@ -126,7 +127,7 @@ properties: Any connector to the data bus of this controller should be modelled using the OF graph bindings specified, if the "usb-role-switch" property is used. See graph.txt - type: object + $ref: /schemas/graph.yaml#/properties/port enable-manual-drd: $ref: /schemas/types.yaml#/definitions/flag @@ -152,10 +153,13 @@ properties: - description: The second cell represents the register base address of the glue layer in syscon - - description: + - description: | The third cell represents the hardware version of the glue layer, - 1 is used by mt8173 etc, 2 is used by mt2712 etc - enum: [1, 2] + 1 - used by mt8173 etc, revision 1 without following IPM rule; + 2 - used by mt2712 etc, revision 2 with following IPM rule; + 101 - used by mt8183, specific 1.01; + 102 - used by mt8192, specific 1.02; + enum: [1, 2, 101, 102] mediatek,u3p-dis-msk: $ref: /schemas/types.yaml#/definitions/uint32 diff --git a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml index c3cbd1fa9944..413299b5fe2b 100644 --- a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml @@ -16,6 +16,7 @@ properties: - qcom,msm8996-dwc3 - qcom,msm8998-dwc3 - qcom,sc7180-dwc3 + - qcom,sc7280-dwc3 - qcom,sdm845-dwc3 - qcom,sdx55-dwc3 - qcom,sm8150-dwc3 diff --git a/Documentation/devicetree/bindings/usb/snps,dwc3.yaml b/Documentation/devicetree/bindings/usb/snps,dwc3.yaml index 2247da77eac1..41416fbd92aa 100644 --- a/Documentation/devicetree/bindings/usb/snps,dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/snps,dwc3.yaml @@ -87,13 +87,19 @@ properties: minItems: 1 snps,usb2-lpm-disable: - description: Indicate if we don't want to enable USB2 HW LPM + description: Indicate if we don't want to enable USB2 HW LPM for host + mode. type: boolean snps,usb3_lpm_capable: description: Determines if platform is USB3 LPM capable type: boolean + snps,usb2-gadget-lpm-disable: + description: Indicate if we don't want to enable USB2 HW LPM for gadget + mode. + type: boolean + snps,dis-start-transfer-quirk: description: When set, disable isoc START TRANSFER command failure SW work-around diff --git a/Documentation/devicetree/bindings/usb/usb-device.yaml b/Documentation/devicetree/bindings/usb/usb-device.yaml index d4c99809ee9a..b77960a7a37b 100644 --- a/Documentation/devicetree/bindings/usb/usb-device.yaml +++ b/Documentation/devicetree/bindings/usb/usb-device.yaml @@ -82,9 +82,9 @@ required: additionalProperties: true examples: - #hub connected to port 1 - #device connected to port 2 - #device connected to port 3 + # hub connected to port 1 + # device connected to port 2 + # device connected to port 3 # interface 0 of configuration 1 # interface 0 of configuration 2 - | diff --git a/Documentation/devicetree/bindings/usb/usb-nop-xceiv.txt b/Documentation/devicetree/bindings/usb/usb-nop-xceiv.txt deleted file mode 100644 index 4dc6a8ee3071..000000000000 --- a/Documentation/devicetree/bindings/usb/usb-nop-xceiv.txt +++ /dev/null @@ -1,43 +0,0 @@ -USB NOP PHY - -Required properties: -- compatible: should be usb-nop-xceiv -- #phy-cells: Must be 0 - -Optional properties: -- clocks: phandle to the PHY clock. Use as per Documentation/devicetree - /bindings/clock/clock-bindings.txt - This property is required if clock-frequency is specified. - -- clock-names: Should be "main_clk" - -- clock-frequency: the clock frequency (in Hz) that the PHY clock must - be configured to. - -- vcc-supply: phandle to the regulator that provides power to the PHY. - -- reset-gpios: Should specify the GPIO for reset. - -- vbus-detect-gpio: should specify the GPIO detecting a VBus insertion - (see Documentation/devicetree/bindings/gpio/gpio.txt) -- vbus-regulator : should specifiy the regulator supplying current drawn from - the VBus line (see Documentation/devicetree/bindings/regulator/regulator.txt). - -Example: - - hsusb1_phy { - compatible = "usb-nop-xceiv"; - clock-frequency = <19200000>; - clocks = <&osc 0>; - clock-names = "main_clk"; - vcc-supply = <&hsusb1_vcc_regulator>; - reset-gpios = <&gpio1 7 GPIO_ACTIVE_LOW>; - vbus-detect-gpio = <&gpio2 13 GPIO_ACTIVE_HIGH>; - vbus-regulator = <&vbus_regulator>; - #phy-cells = <0>; - }; - -hsusb1_phy is a NOP USB PHY device that gets its clock from an oscillator -and expects that clock to be configured to 19.2MHz by the NOP PHY driver. -hsusb1_vcc_regulator provides power to the PHY and GPIO 7 controls RESET. -GPIO 13 detects VBus insertion, and accordingly notifies the vbus-regulator. diff --git a/Documentation/devicetree/bindings/usb/usb-nop-xceiv.yaml b/Documentation/devicetree/bindings/usb/usb-nop-xceiv.yaml new file mode 100644 index 000000000000..2824c17285ee --- /dev/null +++ b/Documentation/devicetree/bindings/usb/usb-nop-xceiv.yaml @@ -0,0 +1,64 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/usb-nop-xceiv.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: USB NOP PHY + +maintainers: + - Rob Herring + +properties: + compatible: + const: usb-nop-xceiv + + clocks: + maxItems: 1 + + clock-names: + const: main_clk + + clock-frequency: true + + '#phy-cells': + const: 0 + + vcc-supply: + description: phandle to the regulator that provides power to the PHY. + + reset-gpios: + maxItems: 1 + + vbus-detect-gpio: + description: Should specify the GPIO detecting a VBus insertion + maxItems: 1 + + vbus-regulator: + description: Should specifiy the regulator supplying current drawn from + the VBus line. + $ref: /schemas/types.yaml#/definitions/phandle + +required: + - compatible + - '#phy-cells' + +additionalProperties: false + +examples: + - | + #include + + hsusb1_phy { + compatible = "usb-nop-xceiv"; + clock-frequency = <19200000>; + clocks = <&osc 0>; + clock-names = "main_clk"; + vcc-supply = <&hsusb1_vcc_regulator>; + reset-gpios = <&gpio1 7 GPIO_ACTIVE_LOW>; + vbus-detect-gpio = <&gpio2 13 GPIO_ACTIVE_HIGH>; + vbus-regulator = <&vbus_regulator>; + #phy-cells = <0>; + }; + +... diff --git a/Documentation/driver-api/usb/usb.rst b/Documentation/driver-api/usb/usb.rst index 078e981e2b16..543e70434da2 100644 --- a/Documentation/driver-api/usb/usb.rst +++ b/Documentation/driver-api/usb/usb.rst @@ -109,15 +109,16 @@ well as to make sure they aren't relying on some HCD-specific behavior. USB-Standard Types ================== -In ```` you will find the USB data types defined in -chapter 9 of the USB specification. These data types are used throughout -USB, and in APIs including this host side API, gadget APIs, usb character -devices and debugfs interfaces. +In ``drivers/usb/common/common.c`` and ``drivers/usb/common/debug.c`` you +will find the USB data types defined in chapter 9 of the USB specification. +These data types are used throughout USB, and in APIs including this host +side API, gadget APIs, usb character devices and debugfs interfaces. -.. kernel-doc:: include/linux/usb/ch9.h - :internal: +.. kernel-doc:: drivers/usb/common/common.c + :export: -.. _usb_header: +.. kernel-doc:: drivers/usb/common/debug.c + :export: Host-Side Data Types and Macros =============================== diff --git a/Documentation/usb/usbip_protocol.rst b/Documentation/usb/usbip_protocol.rst index 988c832166cd..0b8541fda4d8 100644 --- a/Documentation/usb/usbip_protocol.rst +++ b/Documentation/usb/usbip_protocol.rst @@ -2,15 +2,15 @@ USB/IP protocol =============== -PRELIMINARY DRAFT, MAY CONTAIN MISTAKES! -28 Jun 2011 +Architecture +============ The USB/IP protocol follows a server/client architecture. The server exports the -USB devices and the clients imports them. The device driver for the exported +USB devices and the clients import them. The device driver for the exported USB device runs on the client machine. The client may ask for the list of the exported USB devices. To get the list the -client opens a TCP/IP connection towards the server, and sends an OP_REQ_DEVLIST +client opens a TCP/IP connection to the server, and sends an OP_REQ_DEVLIST packet on top of the TCP/IP connection (so the actual OP_REQ_DEVLIST may be sent in one or more pieces at the low level transport layer). The server sends back the OP_REP_DEVLIST packet which lists the exported USB devices. Finally the @@ -30,7 +30,7 @@ TCP/IP connection is closed. | | Once the client knows the list of exported USB devices it may decide to use one -of them. First the client opens a TCP/IP connection towards the server and +of them. First the client opens a TCP/IP connection to the server and sends an OP_REQ_IMPORT packet. The server replies with OP_REP_IMPORT. If the import was successful the TCP/IP connection remains open and will be used to transfer the URB traffic between the client and the server. The client may @@ -84,17 +84,61 @@ server may be USBIP_RET_SUBMIT and USBIP_RET_UNLINK respectively. | <---------------------------------------------- | | . | | : | + +For UNLINK, note that after a successful USBIP_RET_UNLINK, the unlinked URB +submission would not have a corresponding USBIP_RET_SUBMIT (this is explained in +function stub_recv_cmd_unlink of drivers/usb/usbip/stub_rx.c). + +:: + + virtual host controller usb host + "client" "server" + (imports USB devices) (exports USB devices) + | | + | USBIP_CMD_SUBMIT(seqnum = p) | + | ----------------------------------------------> | | | | USBIP_CMD_UNLINK | + | (seqnum = p+1, unlink_seqnum = p) | | ----------------------------------------------> | | | | USBIP_RET_UNLINK | + | (seqnum = p+1, status = -ECONNRESET) | + | <---------------------------------------------- | + | | + | Note: No USBIP_RET_SUBMIT(seqnum = p) | + | <--X---X---X---X---X---X---X---X---X---X---X--- | + | . | + | : | + | | + | USBIP_CMD_SUBMIT(seqnum = q) | + | ----------------------------------------------> | + | | + | USBIP_RET_SUBMIT(seqnum = q) | + | <---------------------------------------------- | + | | + | USBIP_CMD_UNLINK | + | (seqnum = q+1, unlink_seqnum = q) | + | ----------------------------------------------> | + | | + | USBIP_RET_UNLINK | + | (seqnum = q+1, status = 0) | | <---------------------------------------------- | | | The fields are in network (big endian) byte order meaning that the most significant byte (MSB) is stored at the lowest address. +Protocol Version +================ + +The documented USBIP version is v1.1.1. The binary representation of this +version in message headers is 0x0111. + +This is defined in tools/usb/usbip/configure.ac + +Message Format +============== OP_REQ_DEVLIST: Retrieve the list of exported USB devices. @@ -102,7 +146,7 @@ OP_REQ_DEVLIST: +-----------+--------+------------+---------------------------------------------------+ | Offset | Length | Value | Description | +===========+========+============+===================================================+ -| 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0 | +| 0 | 2 | | USBIP version | +-----------+--------+------------+---------------------------------------------------+ | 2 | 2 | 0x8005 | Command code: Retrieve the list of exported USB | | | | | devices. | @@ -116,7 +160,7 @@ OP_REP_DEVLIST: +-----------+--------+------------+---------------------------------------------------+ | Offset | Length | Value | Description | +===========+========+============+===================================================+ -| 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0.| +| 0 | 2 | | USBIP version | +-----------+--------+------------+---------------------------------------------------+ | 2 | 2 | 0x0005 | Reply code: The list of exported USB devices. | +-----------+--------+------------+---------------------------------------------------+ @@ -165,8 +209,8 @@ OP_REP_DEVLIST: | 0x143 | 1 | | bNumInterfaces | +-----------+--------+------------+---------------------------------------------------+ | 0x144 | | m_0 | From now on each interface is described, all | -| | | | together bNumInterfaces times, with the | -| | | | the following 4 fields: | +| | | | together bNumInterfaces times, with the following | +| | | | 4 fields: | +-----------+--------+------------+---------------------------------------------------+ | | 1 | | bInterfaceClass | +-----------+--------+------------+---------------------------------------------------+ @@ -177,7 +221,7 @@ OP_REP_DEVLIST: | 0x147 | 1 | | padding byte for alignment, shall be set to zero | +-----------+--------+------------+---------------------------------------------------+ | 0xC + | | | The second exported USB device starts at i=1 | -| i*0x138 + | | | with the busid field. | +| i*0x138 + | | | with the path field. | | m_(i-1)*4 | | | | +-----------+--------+------------+---------------------------------------------------+ @@ -187,7 +231,7 @@ OP_REQ_IMPORT: +-----------+--------+------------+---------------------------------------------------+ | Offset | Length | Value | Description | +===========+========+============+===================================================+ -| 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0 | +| 0 | 2 | | USBIP version | +-----------+--------+------------+---------------------------------------------------+ | 2 | 2 | 0x8003 | Command code: import a remote USB device. | +-----------+--------+------------+---------------------------------------------------+ @@ -206,7 +250,7 @@ OP_REP_IMPORT: +-----------+--------+------------+---------------------------------------------------+ | Offset | Length | Value | Description | +===========+========+============+===================================================+ -| 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0 | +| 0 | 2 | | USBIP version | +-----------+--------+------------+---------------------------------------------------+ | 2 | 2 | 0x0003 | Reply code: Reply to import. | +-----------+--------+------------+---------------------------------------------------+ @@ -254,158 +298,156 @@ OP_REP_IMPORT: | 0x13E | 1 | | bNumInterfaces | +-----------+--------+------------+---------------------------------------------------+ +The following four commands have a common basic header called +'usbip_header_basic', and their headers, called 'usbip_header' (before +transfer_buffer payload), have the same length, therefore paddings are needed. + +usbip_header_basic: + ++-----------+--------+---------------------------------------------------+ +| Offset | Length | Description | ++===========+========+===================================================+ +| 0 | 4 | command | ++-----------+--------+---------------------------------------------------+ +| 4 | 4 | seqnum: sequential number that identifies requests| +| | | and corresponding responses; | +| | | incremented per connection | ++-----------+--------+---------------------------------------------------+ +| 8 | 4 | devid: specifies a remote USB device uniquely | +| | | instead of busnum and devnum; | +| | | for client (request), this value is | +| | | ((busnum << 16) | devnum); | +| | | for server (response), this shall be set to 0 | ++-----------+--------+---------------------------------------------------+ +| 0xC | 4 | direction: | +| | | | +| | | - 0: USBIP_DIR_OUT | +| | | - 1: USBIP_DIR_IN | +| | | | +| | | only used by client, for server this shall be 0 | ++-----------+--------+---------------------------------------------------+ +| 0x10 | 4 | ep: endpoint number | +| | | only used by client, for server this shall be 0; | +| | | for UNLINK, this shall be 0 | ++-----------+--------+---------------------------------------------------+ + USBIP_CMD_SUBMIT: Submit an URB -+-----------+--------+------------+---------------------------------------------------+ -| Offset | Length | Value | Description | -+===========+========+============+===================================================+ -| 0 | 4 | 0x00000001 | command: Submit an URB | -+-----------+--------+------------+---------------------------------------------------+ -| 4 | 4 | | seqnum: the sequence number of the URB to submit | -+-----------+--------+------------+---------------------------------------------------+ -| 8 | 4 | | devid | -+-----------+--------+------------+---------------------------------------------------+ -| 0xC | 4 | | direction: | -| | | | | -| | | | - 0: USBIP_DIR_OUT | -| | | | - 1: USBIP_DIR_IN | -+-----------+--------+------------+---------------------------------------------------+ -| 0x10 | 4 | | ep: endpoint number, possible values are: 0...15 | -+-----------+--------+------------+---------------------------------------------------+ -| 0x14 | 4 | | transfer_flags: possible values depend on the | -| | | | URB transfer type, see below | -+-----------+--------+------------+---------------------------------------------------+ -| 0x18 | 4 | | transfer_buffer_length | -+-----------+--------+------------+---------------------------------------------------+ -| 0x1C | 4 | | start_frame: specify the selected frame to | -| | | | transmit an ISO frame, ignored if URB_ISO_ASAP | -| | | | is specified at transfer_flags | -+-----------+--------+------------+---------------------------------------------------+ -| 0x20 | 4 | | number_of_packets: number of ISO packets | -+-----------+--------+------------+---------------------------------------------------+ -| 0x24 | 4 | | interval: maximum time for the request on the | -| | | | server-side host controller | -+-----------+--------+------------+---------------------------------------------------+ -| 0x28 | 8 | | setup: data bytes for USB setup, filled with | -| | | | zeros if not used | -+-----------+--------+------------+---------------------------------------------------+ -| 0x30 | | | URB data. For ISO transfers the padding between | -| | | | each ISO packets is not transmitted. | -+-----------+--------+------------+---------------------------------------------------+ - - - +-------------------------+------------+---------+-----------+----------+-------------+ - | Allowed transfer_flags | value | control | interrupt | bulk | isochronous | - +=========================+============+=========+===========+==========+=============+ - | URB_SHORT_NOT_OK | 0x00000001 | only in | only in | only in | no | - +-------------------------+------------+---------+-----------+----------+-------------+ - | URB_ISO_ASAP | 0x00000002 | no | no | no | yes | - +-------------------------+------------+---------+-----------+----------+-------------+ - | URB_NO_TRANSFER_DMA_MAP | 0x00000004 | yes | yes | yes | yes | - +-------------------------+------------+---------+-----------+----------+-------------+ - | URB_ZERO_PACKET | 0x00000040 | no | no | only out | no | - +-------------------------+------------+---------+-----------+----------+-------------+ - | URB_NO_INTERRUPT | 0x00000080 | yes | yes | yes | yes | - +-------------------------+------------+---------+-----------+----------+-------------+ - | URB_FREE_BUFFER | 0x00000100 | yes | yes | yes | yes | - +-------------------------+------------+---------+-----------+----------+-------------+ - | URB_DIR_MASK | 0x00000200 | yes | yes | yes | yes | - +-------------------------+------------+---------+-----------+----------+-------------+ - ++-----------+--------+---------------------------------------------------+ +| Offset | Length | Description | ++===========+========+===================================================+ +| 0 | 20 | usbip_header_basic, 'command' shall be 0x00000001 | ++-----------+--------+---------------------------------------------------+ +| 0x14 | 4 | transfer_flags: possible values depend on the | +| | | URB transfer_flags (refer to URB doc in | +| | | Documentation/driver-api/usb/URB.rst) | +| | | but with URB_NO_TRANSFER_DMA_MAP masked. Refer to | +| | | function usbip_pack_cmd_submit and function | +| | | tweak_transfer_flags in drivers/usb/usbip/ | +| | | usbip_common.c. The following fields may also ref | +| | | to function usbip_pack_cmd_submit and URB doc | ++-----------+--------+---------------------------------------------------+ +| 0x18 | 4 | transfer_buffer_length: | +| | | use URB transfer_buffer_length | ++-----------+--------+---------------------------------------------------+ +| 0x1C | 4 | start_frame: use URB start_frame; | +| | | initial frame for ISO transfer; | +| | | shall be set to 0 if not ISO transfer | ++-----------+--------+---------------------------------------------------+ +| 0x20 | 4 | number_of_packets: number of ISO packets; | +| | | shall be set to 0xffffffff if not ISO transfer | ++-----------+--------+---------------------------------------------------+ +| 0x24 | 4 | interval: maximum time for the request on the | +| | | server-side host controller | ++-----------+--------+---------------------------------------------------+ +| 0x28 | 8 | setup: data bytes for USB setup, filled with | +| | | zeros if not used. | ++-----------+--------+---------------------------------------------------+ +| 0x30 | n | transfer_buffer. | +| | | If direction is USBIP_DIR_OUT then n equals | +| | | transfer_buffer_length; otherwise n equals 0. | +| | | For ISO transfers the padding between each ISO | +| | | packets is not transmitted. | ++-----------+--------+---------------------------------------------------+ +| 0x30+n | m | iso_packet_descriptor | ++-----------+--------+---------------------------------------------------+ USBIP_RET_SUBMIT: Reply for submitting an URB -+-----------+--------+------------+---------------------------------------------------+ -| Offset | Length | Value | Description | -+===========+========+============+===================================================+ -| 0 | 4 | 0x00000003 | command | -+-----------+--------+------------+---------------------------------------------------+ -| 4 | 4 | | seqnum: URB sequence number | -+-----------+--------+------------+---------------------------------------------------+ -| 8 | 4 | | devid | -+-----------+--------+------------+---------------------------------------------------+ -| 0xC | 4 | | direction: | -| | | | | -| | | | - 0: USBIP_DIR_OUT | -| | | | - 1: USBIP_DIR_IN | -+-----------+--------+------------+---------------------------------------------------+ -| 0x10 | 4 | | ep: endpoint number | -+-----------+--------+------------+---------------------------------------------------+ -| 0x14 | 4 | | status: zero for successful URB transaction, | -| | | | otherwise some kind of error happened. | -+-----------+--------+------------+---------------------------------------------------+ -| 0x18 | 4 | n | actual_length: number of URB data bytes | -+-----------+--------+------------+---------------------------------------------------+ -| 0x1C | 4 | | start_frame: for an ISO frame the actually | -| | | | selected frame for transmit. | -+-----------+--------+------------+---------------------------------------------------+ -| 0x20 | 4 | | number_of_packets | -+-----------+--------+------------+---------------------------------------------------+ -| 0x24 | 4 | | error_count | -+-----------+--------+------------+---------------------------------------------------+ -| 0x28 | 8 | | setup: data bytes for USB setup, filled with | -| | | | zeros if not used | -+-----------+--------+------------+---------------------------------------------------+ -| 0x30 | n | | URB data bytes. For ISO transfers the padding | -| | | | between each ISO packets is not transmitted. | -+-----------+--------+------------+---------------------------------------------------+ ++-----------+--------+---------------------------------------------------+ +| Offset | Length | Description | ++===========+========+===================================================+ +| 0 | 20 | usbip_header_basic, 'command' shall be 0x00000003 | ++-----------+--------+---------------------------------------------------+ +| 0x14 | 4 | status: zero for successful URB transaction, | +| | | otherwise some kind of error happened. | ++-----------+--------+---------------------------------------------------+ +| 0x18 | 4 | actual_length: number of URB data bytes; | +| | | use URB actual_length | ++-----------+--------+---------------------------------------------------+ +| 0x1C | 4 | start_frame: use URB start_frame; | +| | | initial frame for ISO transfer; | +| | | shall be set to 0 if not ISO transfer | ++-----------+--------+---------------------------------------------------+ +| 0x20 | 4 | number_of_packets: number of ISO packets; | +| | | shall be set to 0xffffffff if not ISO transfer | ++-----------+--------+---------------------------------------------------+ +| 0x24 | 4 | error_count | ++-----------+--------+---------------------------------------------------+ +| 0x28 | 8 | padding, shall be set to 0 | ++-----------+--------+---------------------------------------------------+ +| 0x30 | n | transfer_buffer. | +| | | If direction is USBIP_DIR_IN then n equals | +| | | actual_length; otherwise n equals 0. | +| | | For ISO transfers the padding between each ISO | +| | | packets is not transmitted. | ++-----------+--------+---------------------------------------------------+ +| 0x30+n | m | iso_packet_descriptor | ++-----------+--------+---------------------------------------------------+ USBIP_CMD_UNLINK: Unlink an URB -+-----------+--------+------------+---------------------------------------------------+ -| Offset | Length | Value | Description | -+===========+========+============+===================================================+ -| 0 | 4 | 0x00000002 | command: URB unlink command | -+-----------+--------+------------+---------------------------------------------------+ -| 4 | 4 | | seqnum: URB sequence number to unlink: | -| | | | | -| | | | FIXME: | -| | | | is this so? | -+-----------+--------+------------+---------------------------------------------------+ -| 8 | 4 | | devid | -+-----------+--------+------------+---------------------------------------------------+ -| 0xC | 4 | | direction: | -| | | | | -| | | | - 0: USBIP_DIR_OUT | -| | | | - 1: USBIP_DIR_IN | -+-----------+--------+------------+---------------------------------------------------+ -| 0x10 | 4 | | ep: endpoint number: zero | -+-----------+--------+------------+---------------------------------------------------+ -| 0x14 | 4 | | seqnum: the URB sequence number given previously | -| | | | at USBIP_CMD_SUBMIT.seqnum field | -+-----------+--------+------------+---------------------------------------------------+ -| 0x30 | n | | URB data bytes. For ISO transfers the padding | -| | | | between each ISO packets is not transmitted. | -+-----------+--------+------------+---------------------------------------------------+ ++-----------+--------+---------------------------------------------------+ +| Offset | Length | Description | ++===========+========+===================================================+ +| 0 | 20 | usbip_header_basic, 'command' shall be 0x00000002 | ++-----------+--------+---------------------------------------------------+ +| 0x14 | 4 | unlink_seqnum, of the SUBMIT request to unlink | ++-----------+--------+---------------------------------------------------+ +| 0x18 | 24 | padding, shall be set to 0 | ++-----------+--------+---------------------------------------------------+ USBIP_RET_UNLINK: Reply for URB unlink -+-----------+--------+------------+---------------------------------------------------+ -| Offset | Length | Value | Description | -+===========+========+============+===================================================+ -| 0 | 4 | 0x00000004 | command: reply for the URB unlink command | -+-----------+--------+------------+---------------------------------------------------+ -| 4 | 4 | | seqnum: the unlinked URB sequence number | -+-----------+--------+------------+---------------------------------------------------+ -| 8 | 4 | | devid | -+-----------+--------+------------+---------------------------------------------------+ -| 0xC | 4 | | direction: | -| | | | | -| | | | - 0: USBIP_DIR_OUT | -| | | | - 1: USBIP_DIR_IN | -+-----------+--------+------------+---------------------------------------------------+ -| 0x10 | 4 | | ep: endpoint number | -+-----------+--------+------------+---------------------------------------------------+ -| 0x14 | 4 | | status: This is the value contained in the | -| | | | urb->status in the URB completition handler. | -| | | | | -| | | | FIXME: | -| | | | a better explanation needed. | -+-----------+--------+------------+---------------------------------------------------+ -| 0x30 | n | | URB data bytes. For ISO transfers the padding | -| | | | between each ISO packets is not transmitted. | -+-----------+--------+------------+---------------------------------------------------+ ++-----------+--------+---------------------------------------------------+ +| Offset | Length | Description | ++===========+========+===================================================+ +| 0 | 20 | usbip_header_basic, 'command' shall be 0x00000004 | ++-----------+--------+---------------------------------------------------+ +| 0x14 | 4 | status: This is similar to the status of | +| | | USBIP_RET_SUBMIT (share the same memory offset). | +| | | When UNLINK is successful, status is -ECONNRESET; | +| | | when USBIP_CMD_UNLINK is after USBIP_RET_SUBMIT | +| | | status is 0 | ++-----------+--------+---------------------------------------------------+ +| 0x18 | 24 | padding, shall be set to 0 | ++-----------+--------+---------------------------------------------------+ + +EXAMPLE +======= + + The following data is captured from wire with Human Interface Devices (HID) + payload + +:: + + CmdIntrIN: 00000001 00000d05 0001000f 00000001 00000001 00000200 00000040 ffffffff 00000000 00000004 00000000 00000000 + CmdIntrOUT: 00000001 00000d06 0001000f 00000000 00000001 00000000 00000040 ffffffff 00000000 00000004 00000000 00000000 + ffffffff860008a784ce5ae212376300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + RetIntrOut: 00000003 00000d06 00000000 00000000 00000000 00000000 00000040 ffffffff 00000000 00000000 00000000 00000000 + RetIntrIn: 00000003 00000d05 00000000 00000000 00000000 00000000 00000040 ffffffff 00000000 00000000 00000000 00000000 + ffffffff860011a784ce5ae2123763612891b1020100000400000000000000000000000000000000000000000000000000000000000000000000000000000000 diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig index 3823da605430..d3242264514e 100644 --- a/arch/arm/configs/multi_v7_defconfig +++ b/arch/arm/configs/multi_v7_defconfig @@ -791,7 +791,6 @@ CONFIG_USB_XHCI_MVEBU=y CONFIG_USB_XHCI_TEGRA=m CONFIG_USB_EHCI_HCD=y CONFIG_USB_EHCI_HCD_STI=y -CONFIG_USB_EHCI_TEGRA=y CONFIG_USB_EHCI_EXYNOS=m CONFIG_USB_EHCI_MV=m CONFIG_USB_OHCI_HCD=y @@ -817,6 +816,7 @@ CONFIG_USB_DWC2=y CONFIG_USB_CHIPIDEA=y CONFIG_USB_CHIPIDEA_UDC=y CONFIG_USB_CHIPIDEA_HOST=y +CONFIG_USB_CHIPIDEA_TEGRA=y CONFIG_USB_ISP1760=y CONFIG_USB_HSIC_USB3503=y CONFIG_AB8500_USB=y diff --git a/arch/arm64/boot/dts/freescale/imx8mp.dtsi b/arch/arm64/boot/dts/freescale/imx8mp.dtsi index c7523fd4eae9..4967d72f41f6 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mp.dtsi @@ -828,7 +828,7 @@ usb3_0: usb@32f10100 { ranges; status = "disabled"; - usb_dwc3_0: dwc3@38100000 { + usb_dwc3_0: usb@38100000 { compatible = "snps,dwc3"; reg = <0x38100000 0x10000>; clocks = <&clk IMX8MP_CLK_HSIO_AXI>, @@ -869,7 +869,7 @@ usb3_1: usb@32f10108 { ranges; status = "disabled"; - usb_dwc3_1: dwc3@38200000 { + usb_dwc3_1: usb@38200000 { compatible = "snps,dwc3"; reg = <0x38200000 0x10000>; clocks = <&clk IMX8MP_CLK_HSIO_AXI>, diff --git a/arch/arm64/boot/dts/mediatek/mt8183.dtsi b/arch/arm64/boot/dts/mediatek/mt8183.dtsi index 80519a145f13..9ea84d636556 100644 --- a/arch/arm64/boot/dts/mediatek/mt8183.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8183.dtsi @@ -874,7 +874,7 @@ ssusb: usb@11201000 { clocks = <&infracfg CLK_INFRA_UNIPRO_SCK>, <&infracfg CLK_INFRA_USB>; clock-names = "sys_ck", "ref_ck"; - mediatek,syscon-wakeup = <&pericfg 0x400 0>; + mediatek,syscon-wakeup = <&pericfg 0x420 101>; #address-cells = <2>; #size-cells = <2>; ranges; diff --git a/drivers/net/thunderbolt.c b/drivers/net/thunderbolt.c index ed3743dc62b9..9a6a8353e192 100644 --- a/drivers/net/thunderbolt.c +++ b/drivers/net/thunderbolt.c @@ -25,13 +25,13 @@ /* Protocol timeouts in ms */ #define TBNET_LOGIN_DELAY 4500 #define TBNET_LOGIN_TIMEOUT 500 -#define TBNET_LOGOUT_TIMEOUT 100 +#define TBNET_LOGOUT_TIMEOUT 1000 #define TBNET_RING_SIZE 256 -#define TBNET_LOCAL_PATH 0xf #define TBNET_LOGIN_RETRIES 60 -#define TBNET_LOGOUT_RETRIES 5 +#define TBNET_LOGOUT_RETRIES 10 #define TBNET_MATCH_FRAGS_ID BIT(1) +#define TBNET_64K_FRAMES BIT(2) #define TBNET_MAX_MTU SZ_64K #define TBNET_FRAME_SIZE SZ_4K #define TBNET_MAX_PAYLOAD_SIZE \ @@ -154,8 +154,8 @@ struct tbnet_ring { * @login_sent: ThunderboltIP login message successfully sent * @login_received: ThunderboltIP login message received from the remote * host - * @transmit_path: HopID the other end needs to use building the - * opposite side path. + * @local_transmit_path: HopID we are using to send out packets + * @remote_transmit_path: HopID the other end is using to send packets to us * @connection_lock: Lock serializing access to @login_sent, * @login_received and @transmit_path. * @login_retries: Number of login retries currently done @@ -184,7 +184,8 @@ struct tbnet { atomic_t command_id; bool login_sent; bool login_received; - u32 transmit_path; + int local_transmit_path; + int remote_transmit_path; struct mutex connection_lock; int login_retries; struct delayed_work login_work; @@ -257,7 +258,7 @@ static int tbnet_login_request(struct tbnet *net, u8 sequence) atomic_inc_return(&net->command_id)); request.proto_version = TBIP_LOGIN_PROTO_VERSION; - request.transmit_path = TBNET_LOCAL_PATH; + request.transmit_path = net->local_transmit_path; return tb_xdomain_request(xd, &request, sizeof(request), TB_CFG_PKG_XDOMAIN_RESP, &reply, @@ -364,10 +365,10 @@ static void tbnet_tear_down(struct tbnet *net, bool send_logout) mutex_lock(&net->connection_lock); if (net->login_sent && net->login_received) { - int retries = TBNET_LOGOUT_RETRIES; + int ret, retries = TBNET_LOGOUT_RETRIES; while (send_logout && retries-- > 0) { - int ret = tbnet_logout_request(net); + ret = tbnet_logout_request(net); if (ret != -ETIMEDOUT) break; } @@ -377,8 +378,16 @@ static void tbnet_tear_down(struct tbnet *net, bool send_logout) tbnet_free_buffers(&net->rx_ring); tbnet_free_buffers(&net->tx_ring); - if (tb_xdomain_disable_paths(net->xd)) + ret = tb_xdomain_disable_paths(net->xd, + net->local_transmit_path, + net->rx_ring.ring->hop, + net->remote_transmit_path, + net->tx_ring.ring->hop); + if (ret) netdev_warn(net->dev, "failed to disable DMA paths\n"); + + tb_xdomain_release_in_hopid(net->xd, net->remote_transmit_path); + net->remote_transmit_path = 0; } net->login_retries = 0; @@ -424,7 +433,7 @@ static int tbnet_handle_packet(const void *buf, size_t size, void *data) if (!ret) { mutex_lock(&net->connection_lock); net->login_received = true; - net->transmit_path = pkg->transmit_path; + net->remote_transmit_path = pkg->transmit_path; /* If we reached the number of max retries or * previous logout, schedule another round of @@ -597,12 +606,18 @@ static void tbnet_connected_work(struct work_struct *work) if (!connected) return; + ret = tb_xdomain_alloc_in_hopid(net->xd, net->remote_transmit_path); + if (ret != net->remote_transmit_path) { + netdev_err(net->dev, "failed to allocate Rx HopID\n"); + return; + } + /* Both logins successful so enable the high-speed DMA paths and * start the network device queue. */ - ret = tb_xdomain_enable_paths(net->xd, TBNET_LOCAL_PATH, + ret = tb_xdomain_enable_paths(net->xd, net->local_transmit_path, net->rx_ring.ring->hop, - net->transmit_path, + net->remote_transmit_path, net->tx_ring.ring->hop); if (ret) { netdev_err(net->dev, "failed to enable DMA paths\n"); @@ -629,6 +644,7 @@ static void tbnet_connected_work(struct work_struct *work) err_stop_rings: tb_ring_stop(net->rx_ring.ring); tb_ring_stop(net->tx_ring.ring); + tb_xdomain_release_in_hopid(net->xd, net->remote_transmit_path); } static void tbnet_login_work(struct work_struct *work) @@ -851,6 +867,7 @@ static int tbnet_open(struct net_device *dev) struct tb_xdomain *xd = net->xd; u16 sof_mask, eof_mask; struct tb_ring *ring; + int hopid; netif_carrier_off(dev); @@ -862,6 +879,15 @@ static int tbnet_open(struct net_device *dev) } net->tx_ring.ring = ring; + hopid = tb_xdomain_alloc_out_hopid(xd, -1); + if (hopid < 0) { + netdev_err(dev, "failed to allocate Tx HopID\n"); + tb_ring_free(net->tx_ring.ring); + net->tx_ring.ring = NULL; + return hopid; + } + net->local_transmit_path = hopid; + sof_mask = BIT(TBIP_PDF_FRAME_START); eof_mask = BIT(TBIP_PDF_FRAME_END); @@ -893,6 +919,8 @@ static int tbnet_stop(struct net_device *dev) tb_ring_free(net->rx_ring.ring); net->rx_ring.ring = NULL; + + tb_xdomain_release_out_hopid(net->xd, net->local_transmit_path); tb_ring_free(net->tx_ring.ring); net->tx_ring.ring = NULL; @@ -1340,7 +1368,7 @@ static int __init tbnet_init(void) * the moment. */ tb_property_add_immediate(tbnet_dir, "prtcstns", - TBNET_MATCH_FRAGS_ID); + TBNET_MATCH_FRAGS_ID | TBNET_64K_FRAMES); ret = tb_register_property_dir("network", tbnet_dir); if (ret) { diff --git a/drivers/platform/x86/intel_cht_int33fe_typec.c b/drivers/platform/x86/intel_cht_int33fe_typec.c index 48638d1c56e5..b61bad9cc8d2 100644 --- a/drivers/platform/x86/intel_cht_int33fe_typec.c +++ b/drivers/platform/x86/intel_cht_int33fe_typec.c @@ -124,12 +124,31 @@ static const struct software_node usb_connector_node = { .properties = usb_connector_properties, }; +static const struct software_node altmodes_node = { + .name = "altmodes", + .parent = &usb_connector_node, +}; + +static const struct property_entry dp_altmode_properties[] = { + PROPERTY_ENTRY_U32("svid", 0xff01), + PROPERTY_ENTRY_U32("vdo", 0x0c0086), + { } +}; + +static const struct software_node dp_altmode_node = { + .name = "displayport-altmode", + .parent = &altmodes_node, + .properties = dp_altmode_properties, +}; + static const struct software_node *node_group[] = { &fusb302_node, &max17047_node, &pi3usb30532_node, &displayport_node, &usb_connector_node, + &altmodes_node, + &dp_altmode_node, NULL }; diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c index f1aeaff9f368..0fb5e04191e2 100644 --- a/drivers/thunderbolt/ctl.c +++ b/drivers/thunderbolt/ctl.c @@ -17,7 +17,7 @@ #define TB_CTL_RX_PKG_COUNT 10 -#define TB_CTL_RETRIES 4 +#define TB_CTL_RETRIES 1 /** * struct tb_ctl - Thunderbolt control channel @@ -29,6 +29,7 @@ * @request_queue_lock: Lock protecting @request_queue * @request_queue: List of outstanding requests * @running: Is the control channel running at the moment + * @timeout_msec: Default timeout for non-raw control messages * @callback: Callback called when hotplug message is received * @callback_data: Data passed to @callback */ @@ -43,6 +44,7 @@ struct tb_ctl { struct list_head request_queue; bool running; + int timeout_msec; event_cb callback; void *callback_data; }; @@ -613,6 +615,7 @@ struct tb_cfg_result tb_cfg_request_sync(struct tb_ctl *ctl, /** * tb_ctl_alloc() - allocate a control channel * @nhi: Pointer to NHI + * @timeout_msec: Default timeout used with non-raw control messages * @cb: Callback called for plug events * @cb_data: Data passed to @cb * @@ -620,13 +623,15 @@ struct tb_cfg_result tb_cfg_request_sync(struct tb_ctl *ctl, * * Return: Returns a pointer on success or NULL on failure. */ -struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, event_cb cb, void *cb_data) +struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, int timeout_msec, event_cb cb, + void *cb_data) { int i; struct tb_ctl *ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); if (!ctl) return NULL; ctl->nhi = nhi; + ctl->timeout_msec = timeout_msec; ctl->callback = cb; ctl->callback_data = cb_data; @@ -802,14 +807,12 @@ static bool tb_cfg_copy(struct tb_cfg_request *req, const struct ctl_pkg *pkg) * tb_cfg_reset() - send a reset packet and wait for a response * @ctl: Control channel pointer * @route: Router string for the router to send reset - * @timeout_msec: Timeout in ms how long to wait for the response * * If the switch at route is incorrectly configured then we will not receive a * reply (even though the switch will reset). The caller should check for * -ETIMEDOUT and attempt to reconfigure the switch. */ -struct tb_cfg_result tb_cfg_reset(struct tb_ctl *ctl, u64 route, - int timeout_msec) +struct tb_cfg_result tb_cfg_reset(struct tb_ctl *ctl, u64 route) { struct cfg_reset_pkg request = { .header = tb_cfg_make_header(route) }; struct tb_cfg_result res = { 0 }; @@ -831,7 +834,7 @@ struct tb_cfg_result tb_cfg_reset(struct tb_ctl *ctl, u64 route, req->response_size = sizeof(reply); req->response_type = TB_CFG_PKG_RESET; - res = tb_cfg_request_sync(ctl, req, timeout_msec); + res = tb_cfg_request_sync(ctl, req, ctl->timeout_msec); tb_cfg_request_put(req); @@ -1007,7 +1010,7 @@ int tb_cfg_read(struct tb_ctl *ctl, void *buffer, u64 route, u32 port, enum tb_cfg_space space, u32 offset, u32 length) { struct tb_cfg_result res = tb_cfg_read_raw(ctl, buffer, route, port, - space, offset, length, TB_CFG_DEFAULT_TIMEOUT); + space, offset, length, ctl->timeout_msec); switch (res.err) { case 0: /* Success */ @@ -1033,7 +1036,7 @@ int tb_cfg_write(struct tb_ctl *ctl, const void *buffer, u64 route, u32 port, enum tb_cfg_space space, u32 offset, u32 length) { struct tb_cfg_result res = tb_cfg_write_raw(ctl, buffer, route, port, - space, offset, length, TB_CFG_DEFAULT_TIMEOUT); + space, offset, length, ctl->timeout_msec); switch (res.err) { case 0: /* Success */ @@ -1071,7 +1074,7 @@ int tb_cfg_get_upstream_port(struct tb_ctl *ctl, u64 route) u32 dummy; struct tb_cfg_result res = tb_cfg_read_raw(ctl, &dummy, route, 0, TB_CFG_SWITCH, 0, 1, - TB_CFG_DEFAULT_TIMEOUT); + ctl->timeout_msec); if (res.err == 1) return -EIO; if (res.err) diff --git a/drivers/thunderbolt/ctl.h b/drivers/thunderbolt/ctl.h index 97cb03b38953..e8c64898dfce 100644 --- a/drivers/thunderbolt/ctl.h +++ b/drivers/thunderbolt/ctl.h @@ -21,15 +21,14 @@ struct tb_ctl; typedef bool (*event_cb)(void *data, enum tb_cfg_pkg_type type, const void *buf, size_t size); -struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, event_cb cb, void *cb_data); +struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, int timeout_msec, event_cb cb, + void *cb_data); void tb_ctl_start(struct tb_ctl *ctl); void tb_ctl_stop(struct tb_ctl *ctl); void tb_ctl_free(struct tb_ctl *ctl); /* configuration commands */ -#define TB_CFG_DEFAULT_TIMEOUT 5000 /* msec */ - struct tb_cfg_result { u64 response_route; u32 response_port; /* @@ -124,8 +123,7 @@ static inline struct tb_cfg_header tb_cfg_make_header(u64 route) } int tb_cfg_ack_plug(struct tb_ctl *ctl, u64 route, u32 port, bool unplug); -struct tb_cfg_result tb_cfg_reset(struct tb_ctl *ctl, u64 route, - int timeout_msec); +struct tb_cfg_result tb_cfg_reset(struct tb_ctl *ctl, u64 route); struct tb_cfg_result tb_cfg_read_raw(struct tb_ctl *ctl, void *buffer, u64 route, u32 port, enum tb_cfg_space space, u32 offset, diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c index 9541d7409ab1..c850b0ac098c 100644 --- a/drivers/thunderbolt/debugfs.c +++ b/drivers/thunderbolt/debugfs.c @@ -251,6 +251,29 @@ static ssize_t counters_write(struct file *file, const char __user *user_buf, return ret < 0 ? ret : count; } +static void cap_show_by_dw(struct seq_file *s, struct tb_switch *sw, + struct tb_port *port, unsigned int cap, + unsigned int offset, u8 cap_id, u8 vsec_id, + int dwords) +{ + int i, ret; + u32 data; + + for (i = 0; i < dwords; i++) { + if (port) + ret = tb_port_read(port, &data, TB_CFG_PORT, cap + offset + i, 1); + else + ret = tb_sw_read(sw, &data, TB_CFG_SWITCH, cap + offset + i, 1); + if (ret) { + seq_printf(s, "0x%04x \n", cap + offset + i); + continue; + } + + seq_printf(s, "0x%04x %4d 0x%02x 0x%02x 0x%08x\n", cap + offset + i, + offset + i, cap_id, vsec_id, data); + } +} + static void cap_show(struct seq_file *s, struct tb_switch *sw, struct tb_port *port, unsigned int cap, u8 cap_id, u8 vsec_id, int length) @@ -267,10 +290,7 @@ static void cap_show(struct seq_file *s, struct tb_switch *sw, else ret = tb_sw_read(sw, data, TB_CFG_SWITCH, cap + offset, dwords); if (ret) { - seq_printf(s, "0x%04x \n", - cap + offset); - if (dwords > 1) - seq_printf(s, "0x%04x ...\n", cap + offset + 1); + cap_show_by_dw(s, sw, port, cap, offset, cap_id, vsec_id, length); return; } @@ -341,15 +361,6 @@ static void port_cap_show(struct tb_port *port, struct seq_file *s, } else { length = header.extended_short.length; vsec_id = header.extended_short.vsec_id; - /* - * Ice Lake and Tiger Lake do not implement the - * full length of the capability, only first 32 - * dwords so hard-code it here. - */ - if (!vsec_id && - (tb_switch_is_ice_lake(port->sw) || - tb_switch_is_tiger_lake(port->sw))) - length = 32; } break; diff --git a/drivers/thunderbolt/dma_test.c b/drivers/thunderbolt/dma_test.c index 6debaf5a6604..3bedecb236e0 100644 --- a/drivers/thunderbolt/dma_test.c +++ b/drivers/thunderbolt/dma_test.c @@ -13,7 +13,6 @@ #include #include -#define DMA_TEST_HOPID 8 #define DMA_TEST_TX_RING_SIZE 64 #define DMA_TEST_RX_RING_SIZE 256 #define DMA_TEST_FRAME_SIZE SZ_4K @@ -72,7 +71,9 @@ static const char * const dma_test_result_names[] = { * @svc: XDomain service the driver is bound to * @xd: XDomain the service belongs to * @rx_ring: Software ring holding RX frames + * @rx_hopid: HopID used for receiving frames * @tx_ring: Software ring holding TX frames + * @tx_hopid: HopID used for sending fames * @packets_to_send: Number of packets to send * @packets_to_receive: Number of packets to receive * @packets_sent: Actual number of packets sent @@ -92,7 +93,9 @@ struct dma_test { const struct tb_service *svc; struct tb_xdomain *xd; struct tb_ring *rx_ring; + int rx_hopid; struct tb_ring *tx_ring; + int tx_hopid; unsigned int packets_to_send; unsigned int packets_to_receive; unsigned int packets_sent; @@ -119,10 +122,12 @@ static void *dma_test_pattern; static void dma_test_free_rings(struct dma_test *dt) { if (dt->rx_ring) { + tb_xdomain_release_in_hopid(dt->xd, dt->rx_hopid); tb_ring_free(dt->rx_ring); dt->rx_ring = NULL; } if (dt->tx_ring) { + tb_xdomain_release_out_hopid(dt->xd, dt->tx_hopid); tb_ring_free(dt->tx_ring); dt->tx_ring = NULL; } @@ -151,6 +156,14 @@ static int dma_test_start_rings(struct dma_test *dt) dt->tx_ring = ring; e2e_tx_hop = ring->hop; + + ret = tb_xdomain_alloc_out_hopid(xd, -1); + if (ret < 0) { + dma_test_free_rings(dt); + return ret; + } + + dt->tx_hopid = ret; } if (dt->packets_to_receive) { @@ -168,11 +181,19 @@ static int dma_test_start_rings(struct dma_test *dt) } dt->rx_ring = ring; + + ret = tb_xdomain_alloc_in_hopid(xd, -1); + if (ret < 0) { + dma_test_free_rings(dt); + return ret; + } + + dt->rx_hopid = ret; } - ret = tb_xdomain_enable_paths(dt->xd, DMA_TEST_HOPID, + ret = tb_xdomain_enable_paths(dt->xd, dt->tx_hopid, dt->tx_ring ? dt->tx_ring->hop : 0, - DMA_TEST_HOPID, + dt->rx_hopid, dt->rx_ring ? dt->rx_ring->hop : 0); if (ret) { dma_test_free_rings(dt); @@ -189,12 +210,18 @@ static int dma_test_start_rings(struct dma_test *dt) static void dma_test_stop_rings(struct dma_test *dt) { + int ret; + if (dt->rx_ring) tb_ring_stop(dt->rx_ring); if (dt->tx_ring) tb_ring_stop(dt->tx_ring); - if (tb_xdomain_disable_paths(dt->xd)) + ret = tb_xdomain_disable_paths(dt->xd, dt->tx_hopid, + dt->tx_ring ? dt->tx_ring->hop : 0, + dt->rx_hopid, + dt->rx_ring ? dt->rx_ring->hop : 0); + if (ret) dev_warn(&dt->svc->dev, "failed to disable DMA paths\n"); dma_test_free_rings(dt); diff --git a/drivers/thunderbolt/domain.c b/drivers/thunderbolt/domain.c index 89ae614eaba2..98f4056f89ff 100644 --- a/drivers/thunderbolt/domain.c +++ b/drivers/thunderbolt/domain.c @@ -341,9 +341,34 @@ struct device_type tb_domain_type = { .release = tb_domain_release, }; +static bool tb_domain_event_cb(void *data, enum tb_cfg_pkg_type type, + const void *buf, size_t size) +{ + struct tb *tb = data; + + if (!tb->cm_ops->handle_event) { + tb_warn(tb, "domain does not have event handler\n"); + return true; + } + + switch (type) { + case TB_CFG_PKG_XDOMAIN_REQ: + case TB_CFG_PKG_XDOMAIN_RESP: + if (tb_is_xdomain_enabled()) + return tb_xdomain_handle_request(tb, type, buf, size); + break; + + default: + tb->cm_ops->handle_event(tb, type, buf, size); + } + + return true; +} + /** * tb_domain_alloc() - Allocate a domain * @nhi: Pointer to the host controller + * @timeout_msec: Control channel timeout for non-raw messages * @privsize: Size of the connection manager private data * * Allocates and initializes a new Thunderbolt domain. Connection @@ -355,7 +380,7 @@ struct device_type tb_domain_type = { * * Return: allocated domain structure on %NULL in case of error */ -struct tb *tb_domain_alloc(struct tb_nhi *nhi, size_t privsize) +struct tb *tb_domain_alloc(struct tb_nhi *nhi, int timeout_msec, size_t privsize) { struct tb *tb; @@ -382,6 +407,10 @@ struct tb *tb_domain_alloc(struct tb_nhi *nhi, size_t privsize) if (!tb->wq) goto err_remove_ida; + tb->ctl = tb_ctl_alloc(nhi, timeout_msec, tb_domain_event_cb, tb); + if (!tb->ctl) + goto err_destroy_wq; + tb->dev.parent = &nhi->pdev->dev; tb->dev.bus = &tb_bus_type; tb->dev.type = &tb_domain_type; @@ -391,6 +420,8 @@ struct tb *tb_domain_alloc(struct tb_nhi *nhi, size_t privsize) return tb; +err_destroy_wq: + destroy_workqueue(tb->wq); err_remove_ida: ida_simple_remove(&tb_domain_ida, tb->index); err_free: @@ -399,30 +430,6 @@ struct tb *tb_domain_alloc(struct tb_nhi *nhi, size_t privsize) return NULL; } -static bool tb_domain_event_cb(void *data, enum tb_cfg_pkg_type type, - const void *buf, size_t size) -{ - struct tb *tb = data; - - if (!tb->cm_ops->handle_event) { - tb_warn(tb, "domain does not have event handler\n"); - return true; - } - - switch (type) { - case TB_CFG_PKG_XDOMAIN_REQ: - case TB_CFG_PKG_XDOMAIN_RESP: - if (tb_is_xdomain_enabled()) - return tb_xdomain_handle_request(tb, type, buf, size); - break; - - default: - tb->cm_ops->handle_event(tb, type, buf, size); - } - - return true; -} - /** * tb_domain_add() - Add domain to the system * @tb: Domain to add @@ -442,13 +449,6 @@ int tb_domain_add(struct tb *tb) return -EINVAL; mutex_lock(&tb->lock); - - tb->ctl = tb_ctl_alloc(tb->nhi, tb_domain_event_cb, tb); - if (!tb->ctl) { - ret = -ENOMEM; - goto err_unlock; - } - /* * tb_schedule_hotplug_handler may be called as soon as the config * channel is started. Thats why we have to hold the lock here. @@ -493,7 +493,6 @@ int tb_domain_add(struct tb *tb) device_del(&tb->dev); err_ctl_stop: tb_ctl_stop(tb->ctl); -err_unlock: mutex_unlock(&tb->lock); return ret; @@ -793,6 +792,10 @@ int tb_domain_disconnect_pcie_paths(struct tb *tb) * tb_domain_approve_xdomain_paths() - Enable DMA paths for XDomain * @tb: Domain enabling the DMA paths * @xd: XDomain DMA paths are created to + * @transmit_path: HopID we are using to send out packets + * @transmit_ring: DMA ring used to send out packets + * @receive_path: HopID the other end is using to send packets to us + * @receive_ring: DMA ring used to receive packets from @receive_path * * Calls connection manager specific method to enable DMA paths to the * XDomain in question. @@ -801,18 +804,25 @@ int tb_domain_disconnect_pcie_paths(struct tb *tb) * particular returns %-ENOTSUPP if the connection manager * implementation does not support XDomains. */ -int tb_domain_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) +int tb_domain_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd, + int transmit_path, int transmit_ring, + int receive_path, int receive_ring) { if (!tb->cm_ops->approve_xdomain_paths) return -ENOTSUPP; - return tb->cm_ops->approve_xdomain_paths(tb, xd); + return tb->cm_ops->approve_xdomain_paths(tb, xd, transmit_path, + transmit_ring, receive_path, receive_ring); } /** * tb_domain_disconnect_xdomain_paths() - Disable DMA paths for XDomain * @tb: Domain disabling the DMA paths * @xd: XDomain whose DMA paths are disconnected + * @transmit_path: HopID we are using to send out packets + * @transmit_ring: DMA ring used to send out packets + * @receive_path: HopID the other end is using to send packets to us + * @receive_ring: DMA ring used to receive packets from @receive_path * * Calls connection manager specific method to disconnect DMA paths to * the XDomain in question. @@ -821,12 +831,15 @@ int tb_domain_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) * particular returns %-ENOTSUPP if the connection manager * implementation does not support XDomains. */ -int tb_domain_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) +int tb_domain_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd, + int transmit_path, int transmit_ring, + int receive_path, int receive_ring) { if (!tb->cm_ops->disconnect_xdomain_paths) return -ENOTSUPP; - return tb->cm_ops->disconnect_xdomain_paths(tb, xd); + return tb->cm_ops->disconnect_xdomain_paths(tb, xd, transmit_path, + transmit_ring, receive_path, receive_ring); } static int disconnect_xdomain(struct device *dev, void *data) @@ -837,7 +850,7 @@ static int disconnect_xdomain(struct device *dev, void *data) xd = tb_to_xdomain(dev); if (xd && xd->tb == tb) - ret = tb_xdomain_disable_paths(xd); + ret = tb_xdomain_disable_all_paths(xd); return ret; } diff --git a/drivers/thunderbolt/eeprom.c b/drivers/thunderbolt/eeprom.c index dd03d3096653..46d0906a3070 100644 --- a/drivers/thunderbolt/eeprom.c +++ b/drivers/thunderbolt/eeprom.c @@ -277,6 +277,16 @@ struct tb_drom_entry_port { u8 unknown4:2; } __packed; +/* USB4 product descriptor */ +struct tb_drom_entry_desc { + struct tb_drom_entry_header header; + u16 bcdUSBSpec; + u16 idVendor; + u16 idProduct; + u16 bcdProductFWRevision; + u32 TID; + u8 productHWRevision; +}; /** * tb_drom_read_uid_only() - Read UID directly from DROM @@ -329,6 +339,16 @@ static int tb_drom_parse_entry_generic(struct tb_switch *sw, if (!sw->device_name) return -ENOMEM; break; + case 9: { + const struct tb_drom_entry_desc *desc = + (const struct tb_drom_entry_desc *)entry; + + if (!sw->vendor && !sw->device) { + sw->vendor = desc->idVendor; + sw->device = desc->idProduct; + } + break; + } } return 0; @@ -521,6 +541,51 @@ static int tb_drom_read_n(struct tb_switch *sw, u16 offset, u8 *val, return tb_eeprom_read_n(sw, offset, val, count); } +static int tb_drom_parse(struct tb_switch *sw) +{ + const struct tb_drom_header *header = + (const struct tb_drom_header *)sw->drom; + u32 crc; + + crc = tb_crc8((u8 *) &header->uid, 8); + if (crc != header->uid_crc8) { + tb_sw_warn(sw, + "DROM UID CRC8 mismatch (expected: %#x, got: %#x), aborting\n", + header->uid_crc8, crc); + return -EINVAL; + } + if (!sw->uid) + sw->uid = header->uid; + sw->vendor = header->vendor_id; + sw->device = header->model_id; + + crc = tb_crc32(sw->drom + TB_DROM_DATA_START, header->data_len); + if (crc != header->data_crc32) { + tb_sw_warn(sw, + "DROM data CRC32 mismatch (expected: %#x, got: %#x), continuing\n", + header->data_crc32, crc); + } + + return tb_drom_parse_entries(sw); +} + +static int usb4_drom_parse(struct tb_switch *sw) +{ + const struct tb_drom_header *header = + (const struct tb_drom_header *)sw->drom; + u32 crc; + + crc = tb_crc32(sw->drom + TB_DROM_DATA_START, header->data_len); + if (crc != header->data_crc32) { + tb_sw_warn(sw, + "DROM data CRC32 mismatch (expected: %#x, got: %#x), aborting\n", + header->data_crc32, crc); + return -EINVAL; + } + + return tb_drom_parse_entries(sw); +} + /** * tb_drom_read() - Copy DROM to sw->drom and parse it * @sw: Router whose DROM to read and parse @@ -534,7 +599,6 @@ static int tb_drom_read_n(struct tb_switch *sw, u16 offset, u8 *val, int tb_drom_read(struct tb_switch *sw) { u16 size; - u32 crc; struct tb_drom_header *header; int res, retries = 1; @@ -599,31 +663,21 @@ int tb_drom_read(struct tb_switch *sw) goto err; } - crc = tb_crc8((u8 *) &header->uid, 8); - if (crc != header->uid_crc8) { - tb_sw_warn(sw, - "drom uid crc8 mismatch (expected: %#x, got: %#x), aborting\n", - header->uid_crc8, crc); - goto err; - } - if (!sw->uid) - sw->uid = header->uid; - sw->vendor = header->vendor_id; - sw->device = header->model_id; - tb_check_quirks(sw); + tb_sw_dbg(sw, "DROM version: %d\n", header->device_rom_revision); - crc = tb_crc32(sw->drom + TB_DROM_DATA_START, header->data_len); - if (crc != header->data_crc32) { - tb_sw_warn(sw, - "drom data crc32 mismatch (expected: %#x, got: %#x), continuing\n", - header->data_crc32, crc); + switch (header->device_rom_revision) { + case 3: + res = usb4_drom_parse(sw); + break; + default: + tb_sw_warn(sw, "DROM device_rom_revision %#x unknown\n", + header->device_rom_revision); + fallthrough; + case 1: + res = tb_drom_parse(sw); + break; } - if (header->device_rom_revision > 2) - tb_sw_warn(sw, "drom device_rom_revision %#x unknown\n", - header->device_rom_revision); - - res = tb_drom_parse_entries(sw); /* If the DROM parsing fails, wait a moment and retry once */ if (res == -EILSEQ && retries--) { tb_sw_warn(sw, "parsing DROM failed, retrying\n"); @@ -633,10 +687,11 @@ int tb_drom_read(struct tb_switch *sw) goto parse; } - return res; + if (!res) + return 0; + err: kfree(sw->drom); sw->drom = NULL; return -EIO; - } diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c index f6f605d48371..2f30b816705a 100644 --- a/drivers/thunderbolt/icm.c +++ b/drivers/thunderbolt/icm.c @@ -557,7 +557,9 @@ static int icm_fr_challenge_switch_key(struct tb *tb, struct tb_switch *sw, return 0; } -static int icm_fr_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) +static int icm_fr_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd, + int transmit_path, int transmit_ring, + int receive_path, int receive_ring) { struct icm_fr_pkg_approve_xdomain_response reply; struct icm_fr_pkg_approve_xdomain request; @@ -568,10 +570,10 @@ static int icm_fr_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) request.link_info = xd->depth << ICM_LINK_INFO_DEPTH_SHIFT | xd->link; memcpy(&request.remote_uuid, xd->remote_uuid, sizeof(*xd->remote_uuid)); - request.transmit_path = xd->transmit_path; - request.transmit_ring = xd->transmit_ring; - request.receive_path = xd->receive_path; - request.receive_ring = xd->receive_ring; + request.transmit_path = transmit_path; + request.transmit_ring = transmit_ring; + request.receive_path = receive_path; + request.receive_ring = receive_ring; memset(&reply, 0, sizeof(reply)); ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), @@ -585,7 +587,9 @@ static int icm_fr_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) return 0; } -static int icm_fr_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) +static int icm_fr_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd, + int transmit_path, int transmit_ring, + int receive_path, int receive_ring) { u8 phy_port; u8 cmd; @@ -1122,7 +1126,9 @@ static int icm_tr_challenge_switch_key(struct tb *tb, struct tb_switch *sw, return 0; } -static int icm_tr_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) +static int icm_tr_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd, + int transmit_path, int transmit_ring, + int receive_path, int receive_ring) { struct icm_tr_pkg_approve_xdomain_response reply; struct icm_tr_pkg_approve_xdomain request; @@ -1132,10 +1138,10 @@ static int icm_tr_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) request.hdr.code = ICM_APPROVE_XDOMAIN; request.route_hi = upper_32_bits(xd->route); request.route_lo = lower_32_bits(xd->route); - request.transmit_path = xd->transmit_path; - request.transmit_ring = xd->transmit_ring; - request.receive_path = xd->receive_path; - request.receive_ring = xd->receive_ring; + request.transmit_path = transmit_path; + request.transmit_ring = transmit_ring; + request.receive_path = receive_path; + request.receive_ring = receive_ring; memcpy(&request.remote_uuid, xd->remote_uuid, sizeof(*xd->remote_uuid)); memset(&reply, 0, sizeof(reply)); @@ -1176,7 +1182,9 @@ static int icm_tr_xdomain_tear_down(struct tb *tb, struct tb_xdomain *xd, return 0; } -static int icm_tr_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) +static int icm_tr_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd, + int transmit_path, int transmit_ring, + int receive_path, int receive_ring) { int ret; @@ -2416,7 +2424,7 @@ struct tb *icm_probe(struct tb_nhi *nhi) struct icm *icm; struct tb *tb; - tb = tb_domain_alloc(nhi, sizeof(struct icm)); + tb = tb_domain_alloc(nhi, ICM_TIMEOUT, sizeof(struct icm)); if (!tb) return NULL; diff --git a/drivers/thunderbolt/property.c b/drivers/thunderbolt/property.c index d5b0cdb8f0b1..dc555cda98e6 100644 --- a/drivers/thunderbolt/property.c +++ b/drivers/thunderbolt/property.c @@ -501,6 +501,77 @@ ssize_t tb_property_format_dir(const struct tb_property_dir *dir, u32 *block, return ret < 0 ? ret : 0; } +/** + * tb_property_copy_dir() - Take a deep copy of directory + * @dir: Directory to copy + * + * This function takes a deep copy of @dir and returns back the copy. In + * case of error returns %NULL. The resulting directory needs to be + * released by calling tb_property_free_dir(). + */ +struct tb_property_dir *tb_property_copy_dir(const struct tb_property_dir *dir) +{ + struct tb_property *property, *p = NULL; + struct tb_property_dir *d; + + if (!dir) + return NULL; + + d = tb_property_create_dir(dir->uuid); + if (!d) + return NULL; + + list_for_each_entry(property, &dir->properties, list) { + struct tb_property *p; + + p = tb_property_alloc(property->key, property->type); + if (!p) + goto err_free; + + p->length = property->length; + + switch (property->type) { + case TB_PROPERTY_TYPE_DIRECTORY: + p->value.dir = tb_property_copy_dir(property->value.dir); + if (!p->value.dir) + goto err_free; + break; + + case TB_PROPERTY_TYPE_DATA: + p->value.data = kmemdup(property->value.data, + property->length * 4, + GFP_KERNEL); + if (!p->value.data) + goto err_free; + break; + + case TB_PROPERTY_TYPE_TEXT: + p->value.text = kzalloc(p->length * 4, GFP_KERNEL); + if (!p->value.text) + goto err_free; + strcpy(p->value.text, property->value.text); + break; + + case TB_PROPERTY_TYPE_VALUE: + p->value.immediate = property->value.immediate; + break; + + default: + break; + } + + list_add_tail(&p->list, &d->properties); + } + + return d; + +err_free: + kfree(p); + tb_property_free_dir(d); + + return NULL; +} + /** * tb_property_add_immediate() - Add immediate property to directory * @parent: Directory to add the property diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 2a95b4ce06c0..e73cd296db7e 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -626,28 +626,6 @@ int tb_port_add_nfc_credits(struct tb_port *port, int credits) TB_CFG_PORT, ADP_CS_4, 1); } -/** - * tb_port_set_initial_credits() - Set initial port link credits allocated - * @port: Port to set the initial credits - * @credits: Number of credits to to allocate - * - * Set initial credits value to be used for ingress shared buffering. - */ -int tb_port_set_initial_credits(struct tb_port *port, u32 credits) -{ - u32 data; - int ret; - - ret = tb_port_read(port, &data, TB_CFG_PORT, ADP_CS_5, 1); - if (ret) - return ret; - - data &= ~ADP_CS_5_LCA_MASK; - data |= (credits << ADP_CS_5_LCA_SHIFT) & ADP_CS_5_LCA_MASK; - - return tb_port_write(port, &data, TB_CFG_PORT, ADP_CS_5, 1); -} - /** * tb_port_clear_counter() - clear a counter in TB_CFG_COUNTER * @port: Port whose counters to clear @@ -1331,7 +1309,7 @@ int tb_switch_reset(struct tb_switch *sw) TB_CFG_SWITCH, 2, 2); if (res.err) return res.err; - res = tb_cfg_reset(sw->tb->ctl, tb_route(sw), TB_CFG_DEFAULT_TIMEOUT); + res = tb_cfg_reset(sw->tb->ctl, tb_route(sw)); if (res.err > 0) return -EIO; return res.err; @@ -1762,6 +1740,18 @@ static struct attribute *switch_attrs[] = { NULL, }; +static bool has_port(const struct tb_switch *sw, enum tb_port_type type) +{ + const struct tb_port *port; + + tb_switch_for_each_port(sw, port) { + if (!port->disabled && port->config.type == type) + return true; + } + + return false; +} + static umode_t switch_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n) { @@ -1770,7 +1760,8 @@ static umode_t switch_attr_is_visible(struct kobject *kobj, if (attr == &dev_attr_authorized.attr) { if (sw->tb->security_level == TB_SECURITY_NOPCIE || - sw->tb->security_level == TB_SECURITY_DPONLY) + sw->tb->security_level == TB_SECURITY_DPONLY || + !has_port(sw, TB_TYPE_PCIE_UP)) return 0; } else if (attr == &dev_attr_device.attr) { if (!sw->device) @@ -1849,6 +1840,39 @@ static void tb_switch_release(struct device *dev) kfree(sw); } +static int tb_switch_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct tb_switch *sw = tb_to_switch(dev); + const char *type; + + if (sw->config.thunderbolt_version == USB4_VERSION_1_0) { + if (add_uevent_var(env, "USB4_VERSION=1.0")) + return -ENOMEM; + } + + if (!tb_route(sw)) { + type = "host"; + } else { + const struct tb_port *port; + bool hub = false; + + /* Device is hub if it has any downstream ports */ + tb_switch_for_each_port(sw, port) { + if (!port->disabled && !tb_is_upstream_port(port) && + tb_port_is_null(port)) { + hub = true; + break; + } + } + + type = hub ? "hub" : "device"; + } + + if (add_uevent_var(env, "USB4_TYPE=%s", type)) + return -ENOMEM; + return 0; +} + /* * Currently only need to provide the callbacks. Everything else is handled * in the connection manager. @@ -1882,6 +1906,7 @@ static const struct dev_pm_ops tb_switch_pm_ops = { struct device_type tb_switch_type = { .name = "thunderbolt_device", .release = tb_switch_release, + .uevent = tb_switch_uevent, .pm = &tb_switch_pm_ops, }; @@ -2542,6 +2567,8 @@ int tb_switch_add(struct tb_switch *sw) } tb_sw_dbg(sw, "uid: %#llx\n", sw->uid); + tb_check_quirks(sw); + ret = tb_switch_set_uuid(sw); if (ret) { dev_err(&sw->dev, "failed to set UUID\n"); diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index c348b1fc0efc..7e6dc2b03bed 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -15,6 +15,8 @@ #include "tb_regs.h" #include "tunnel.h" +#define TB_TIMEOUT 100 /* ms */ + /** * struct tb_cm - Simple Thunderbolt connection manager * @tunnel_list: List of active tunnels @@ -1077,7 +1079,9 @@ static int tb_tunnel_pci(struct tb *tb, struct tb_switch *sw) return 0; } -static int tb_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) +static int tb_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd, + int transmit_path, int transmit_ring, + int receive_path, int receive_ring) { struct tb_cm *tcm = tb_priv(tb); struct tb_port *nhi_port, *dst_port; @@ -1089,9 +1093,8 @@ static int tb_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) nhi_port = tb_switch_find_port(tb->root_switch, TB_TYPE_NHI); mutex_lock(&tb->lock); - tunnel = tb_tunnel_alloc_dma(tb, nhi_port, dst_port, xd->transmit_ring, - xd->transmit_path, xd->receive_ring, - xd->receive_path); + tunnel = tb_tunnel_alloc_dma(tb, nhi_port, dst_port, transmit_path, + transmit_ring, receive_path, receive_ring); if (!tunnel) { mutex_unlock(&tb->lock); return -ENOMEM; @@ -1110,29 +1113,40 @@ static int tb_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) return 0; } -static void __tb_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) +static void __tb_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd, + int transmit_path, int transmit_ring, + int receive_path, int receive_ring) { - struct tb_port *dst_port; - struct tb_tunnel *tunnel; + struct tb_cm *tcm = tb_priv(tb); + struct tb_port *nhi_port, *dst_port; + struct tb_tunnel *tunnel, *n; struct tb_switch *sw; sw = tb_to_switch(xd->dev.parent); dst_port = tb_port_at(xd->route, sw); + nhi_port = tb_switch_find_port(tb->root_switch, TB_TYPE_NHI); - /* - * It is possible that the tunnel was already teared down (in - * case of cable disconnect) so it is fine if we cannot find it - * here anymore. - */ - tunnel = tb_find_tunnel(tb, TB_TUNNEL_DMA, NULL, dst_port); - tb_deactivate_and_free_tunnel(tunnel); + list_for_each_entry_safe(tunnel, n, &tcm->tunnel_list, list) { + if (!tb_tunnel_is_dma(tunnel)) + continue; + if (tunnel->src_port != nhi_port || tunnel->dst_port != dst_port) + continue; + + if (tb_tunnel_match_dma(tunnel, transmit_path, transmit_ring, + receive_path, receive_ring)) + tb_deactivate_and_free_tunnel(tunnel); + } } -static int tb_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) +static int tb_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd, + int transmit_path, int transmit_ring, + int receive_path, int receive_ring) { if (!xd->is_unplugged) { mutex_lock(&tb->lock); - __tb_disconnect_xdomain_paths(tb, xd); + __tb_disconnect_xdomain_paths(tb, xd, transmit_path, + transmit_ring, receive_path, + receive_ring); mutex_unlock(&tb->lock); } return 0; @@ -1208,12 +1222,12 @@ static void tb_handle_hotplug(struct work_struct *work) * tb_xdomain_remove() so setting XDomain as * unplugged here prevents deadlock if they call * tb_xdomain_disable_paths(). We will tear down - * the path below. + * all the tunnels below. */ xd->is_unplugged = true; tb_xdomain_remove(xd); port->xdomain = NULL; - __tb_disconnect_xdomain_paths(tb, xd); + __tb_disconnect_xdomain_paths(tb, xd, -1, -1, -1, -1); tb_xdomain_put(xd); tb_port_unconfigure_xdomain(port); } else if (tb_port_is_dpout(port) || tb_port_is_dpin(port)) { @@ -1562,7 +1576,7 @@ struct tb *tb_probe(struct tb_nhi *nhi) struct tb_cm *tcm; struct tb *tb; - tb = tb_domain_alloc(nhi, sizeof(*tcm)); + tb = tb_domain_alloc(nhi, TB_TIMEOUT, sizeof(*tcm)); if (!tb) return NULL; diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index beea88c34c0f..9790e9f13d2b 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -406,8 +406,12 @@ struct tb_cm_ops { int (*challenge_switch_key)(struct tb *tb, struct tb_switch *sw, const u8 *challenge, u8 *response); int (*disconnect_pcie_paths)(struct tb *tb); - int (*approve_xdomain_paths)(struct tb *tb, struct tb_xdomain *xd); - int (*disconnect_xdomain_paths)(struct tb *tb, struct tb_xdomain *xd); + int (*approve_xdomain_paths)(struct tb *tb, struct tb_xdomain *xd, + int transmit_path, int transmit_ring, + int receive_path, int receive_ring); + int (*disconnect_xdomain_paths)(struct tb *tb, struct tb_xdomain *xd, + int transmit_path, int transmit_ring, + int receive_path, int receive_ring); int (*usb4_switch_op)(struct tb_switch *sw, u16 opcode, u32 *metadata, u8 *status, const void *tx_data, size_t tx_data_len, void *rx_data, size_t rx_data_len); @@ -625,7 +629,7 @@ void tb_domain_exit(void); int tb_xdomain_init(void); void tb_xdomain_exit(void); -struct tb *tb_domain_alloc(struct tb_nhi *nhi, size_t privsize); +struct tb *tb_domain_alloc(struct tb_nhi *nhi, int timeout_msec, size_t privsize); int tb_domain_add(struct tb *tb); void tb_domain_remove(struct tb *tb); int tb_domain_suspend_noirq(struct tb *tb); @@ -641,8 +645,12 @@ int tb_domain_approve_switch(struct tb *tb, struct tb_switch *sw); int tb_domain_approve_switch_key(struct tb *tb, struct tb_switch *sw); int tb_domain_challenge_switch_key(struct tb *tb, struct tb_switch *sw); int tb_domain_disconnect_pcie_paths(struct tb *tb); -int tb_domain_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd); -int tb_domain_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd); +int tb_domain_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd, + int transmit_path, int transmit_ring, + int receive_path, int receive_ring); +int tb_domain_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd, + int transmit_path, int transmit_ring, + int receive_path, int receive_ring); int tb_domain_disconnect_all_paths(struct tb *tb); static inline struct tb *tb_domain_get(struct tb *tb) @@ -787,32 +795,6 @@ static inline bool tb_switch_is_titan_ridge(const struct tb_switch *sw) return false; } -static inline bool tb_switch_is_ice_lake(const struct tb_switch *sw) -{ - if (sw->config.vendor_id == PCI_VENDOR_ID_INTEL) { - switch (sw->config.device_id) { - case PCI_DEVICE_ID_INTEL_ICL_NHI0: - case PCI_DEVICE_ID_INTEL_ICL_NHI1: - return true; - } - } - return false; -} - -static inline bool tb_switch_is_tiger_lake(const struct tb_switch *sw) -{ - if (sw->config.vendor_id == PCI_VENDOR_ID_INTEL) { - switch (sw->config.device_id) { - case PCI_DEVICE_ID_INTEL_TGL_NHI0: - case PCI_DEVICE_ID_INTEL_TGL_NHI1: - case PCI_DEVICE_ID_INTEL_TGL_H_NHI0: - case PCI_DEVICE_ID_INTEL_TGL_H_NHI1: - return true; - } - } - return false; -} - /** * tb_switch_is_usb4() - Is the switch USB4 compliant * @sw: Switch to check @@ -860,7 +842,6 @@ static inline bool tb_switch_tmu_is_enabled(const struct tb_switch *sw) int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged); int tb_port_add_nfc_credits(struct tb_port *port, int credits); -int tb_port_set_initial_credits(struct tb_port *port, u32 credits); int tb_port_clear_counter(struct tb_port *port, int counter); int tb_port_unlock(struct tb_port *port); int tb_port_enable(struct tb_port *port); diff --git a/drivers/thunderbolt/test.c b/drivers/thunderbolt/test.c index 464c2d37b992..5ff5a03bc9ce 100644 --- a/drivers/thunderbolt/test.c +++ b/drivers/thunderbolt/test.c @@ -119,6 +119,7 @@ static struct tb_switch *alloc_host(struct kunit *test) sw->ports[7].config.type = TB_TYPE_NHI; sw->ports[7].config.max_in_hop_id = 11; sw->ports[7].config.max_out_hop_id = 11; + sw->ports[7].config.nfc_credits = 0x41800000; sw->ports[8].config.type = TB_TYPE_PCIE_DOWN; sw->ports[8].config.max_in_hop_id = 8; @@ -1594,6 +1595,489 @@ static void tb_test_tunnel_port_on_path(struct kunit *test) tb_tunnel_free(dp_tunnel); } +static void tb_test_tunnel_dma(struct kunit *test) +{ + struct tb_port *nhi, *port; + struct tb_tunnel *tunnel; + struct tb_switch *host; + + /* + * Create DMA tunnel from NHI to port 1 and back. + * + * [Host 1] + * 1 ^ In HopID 1 -> Out HopID 8 + * | + * v In HopID 8 -> Out HopID 1 + * ............ Domain border + * | + * [Host 2] + */ + host = alloc_host(test); + nhi = &host->ports[7]; + port = &host->ports[1]; + + tunnel = tb_tunnel_alloc_dma(NULL, nhi, port, 8, 1, 8, 1); + KUNIT_ASSERT_TRUE(test, tunnel != NULL); + KUNIT_EXPECT_EQ(test, tunnel->type, (enum tb_tunnel_type)TB_TUNNEL_DMA); + KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, nhi); + KUNIT_EXPECT_PTR_EQ(test, tunnel->dst_port, port); + KUNIT_ASSERT_EQ(test, tunnel->npaths, (size_t)2); + /* RX path */ + KUNIT_ASSERT_EQ(test, tunnel->paths[0]->path_length, 1); + KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[0].in_port, port); + KUNIT_EXPECT_EQ(test, tunnel->paths[0]->hops[0].in_hop_index, 8); + KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[0].out_port, nhi); + KUNIT_EXPECT_EQ(test, tunnel->paths[0]->hops[0].next_hop_index, 1); + /* TX path */ + KUNIT_ASSERT_EQ(test, tunnel->paths[1]->path_length, 1); + KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[0].in_port, nhi); + KUNIT_EXPECT_EQ(test, tunnel->paths[1]->hops[0].in_hop_index, 1); + KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[0].out_port, port); + KUNIT_EXPECT_EQ(test, tunnel->paths[1]->hops[0].next_hop_index, 8); + + tb_tunnel_free(tunnel); +} + +static void tb_test_tunnel_dma_rx(struct kunit *test) +{ + struct tb_port *nhi, *port; + struct tb_tunnel *tunnel; + struct tb_switch *host; + + /* + * Create DMA RX tunnel from port 1 to NHI. + * + * [Host 1] + * 1 ^ + * | + * | In HopID 15 -> Out HopID 2 + * ............ Domain border + * | + * [Host 2] + */ + host = alloc_host(test); + nhi = &host->ports[7]; + port = &host->ports[1]; + + tunnel = tb_tunnel_alloc_dma(NULL, nhi, port, -1, -1, 15, 2); + KUNIT_ASSERT_TRUE(test, tunnel != NULL); + KUNIT_EXPECT_EQ(test, tunnel->type, (enum tb_tunnel_type)TB_TUNNEL_DMA); + KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, nhi); + KUNIT_EXPECT_PTR_EQ(test, tunnel->dst_port, port); + KUNIT_ASSERT_EQ(test, tunnel->npaths, (size_t)1); + /* RX path */ + KUNIT_ASSERT_EQ(test, tunnel->paths[0]->path_length, 1); + KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[0].in_port, port); + KUNIT_EXPECT_EQ(test, tunnel->paths[0]->hops[0].in_hop_index, 15); + KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[0].out_port, nhi); + KUNIT_EXPECT_EQ(test, tunnel->paths[0]->hops[0].next_hop_index, 2); + + tb_tunnel_free(tunnel); +} + +static void tb_test_tunnel_dma_tx(struct kunit *test) +{ + struct tb_port *nhi, *port; + struct tb_tunnel *tunnel; + struct tb_switch *host; + + /* + * Create DMA TX tunnel from NHI to port 1. + * + * [Host 1] + * 1 | In HopID 2 -> Out HopID 15 + * | + * v + * ............ Domain border + * | + * [Host 2] + */ + host = alloc_host(test); + nhi = &host->ports[7]; + port = &host->ports[1]; + + tunnel = tb_tunnel_alloc_dma(NULL, nhi, port, 15, 2, -1, -1); + KUNIT_ASSERT_TRUE(test, tunnel != NULL); + KUNIT_EXPECT_EQ(test, tunnel->type, (enum tb_tunnel_type)TB_TUNNEL_DMA); + KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, nhi); + KUNIT_EXPECT_PTR_EQ(test, tunnel->dst_port, port); + KUNIT_ASSERT_EQ(test, tunnel->npaths, (size_t)1); + /* TX path */ + KUNIT_ASSERT_EQ(test, tunnel->paths[0]->path_length, 1); + KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[0].in_port, nhi); + KUNIT_EXPECT_EQ(test, tunnel->paths[0]->hops[0].in_hop_index, 2); + KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[0].out_port, port); + KUNIT_EXPECT_EQ(test, tunnel->paths[0]->hops[0].next_hop_index, 15); + + tb_tunnel_free(tunnel); +} + +static void tb_test_tunnel_dma_chain(struct kunit *test) +{ + struct tb_switch *host, *dev1, *dev2; + struct tb_port *nhi, *port; + struct tb_tunnel *tunnel; + + /* + * Create DMA tunnel from NHI to Device #2 port 3 and back. + * + * [Host 1] + * 1 ^ In HopID 1 -> Out HopID x + * | + * 1 | In HopID x -> Out HopID 1 + * [Device #1] + * 7 \ + * 1 \ + * [Device #2] + * 3 | In HopID x -> Out HopID 8 + * | + * v In HopID 8 -> Out HopID x + * ............ Domain border + * | + * [Host 2] + */ + host = alloc_host(test); + dev1 = alloc_dev_default(test, host, 0x1, true); + dev2 = alloc_dev_default(test, dev1, 0x701, true); + + nhi = &host->ports[7]; + port = &dev2->ports[3]; + tunnel = tb_tunnel_alloc_dma(NULL, nhi, port, 8, 1, 8, 1); + KUNIT_ASSERT_TRUE(test, tunnel != NULL); + KUNIT_EXPECT_EQ(test, tunnel->type, (enum tb_tunnel_type)TB_TUNNEL_DMA); + KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, nhi); + KUNIT_EXPECT_PTR_EQ(test, tunnel->dst_port, port); + KUNIT_ASSERT_EQ(test, tunnel->npaths, (size_t)2); + /* RX path */ + KUNIT_ASSERT_EQ(test, tunnel->paths[0]->path_length, 3); + KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[0].in_port, port); + KUNIT_EXPECT_EQ(test, tunnel->paths[0]->hops[0].in_hop_index, 8); + KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[0].out_port, + &dev2->ports[1]); + KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[1].in_port, + &dev1->ports[7]); + KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[1].out_port, + &dev1->ports[1]); + KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[2].in_port, + &host->ports[1]); + KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[2].out_port, nhi); + KUNIT_EXPECT_EQ(test, tunnel->paths[0]->hops[2].next_hop_index, 1); + /* TX path */ + KUNIT_ASSERT_EQ(test, tunnel->paths[1]->path_length, 3); + KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[0].in_port, nhi); + KUNIT_EXPECT_EQ(test, tunnel->paths[1]->hops[0].in_hop_index, 1); + KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[1].in_port, + &dev1->ports[1]); + KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[1].out_port, + &dev1->ports[7]); + KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[2].in_port, + &dev2->ports[1]); + KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[2].out_port, port); + KUNIT_EXPECT_EQ(test, tunnel->paths[1]->hops[2].next_hop_index, 8); + + tb_tunnel_free(tunnel); +} + +static void tb_test_tunnel_dma_match(struct kunit *test) +{ + struct tb_port *nhi, *port; + struct tb_tunnel *tunnel; + struct tb_switch *host; + + host = alloc_host(test); + nhi = &host->ports[7]; + port = &host->ports[1]; + + tunnel = tb_tunnel_alloc_dma(NULL, nhi, port, 15, 1, 15, 1); + KUNIT_ASSERT_TRUE(test, tunnel != NULL); + + KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, 15, 1, 15, 1)); + KUNIT_ASSERT_FALSE(test, tb_tunnel_match_dma(tunnel, 8, 1, 15, 1)); + KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, -1, -1, 15, 1)); + KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, 15, 1, -1, -1)); + KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, 15, -1, -1, -1)); + KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, -1, 1, -1, -1)); + KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, -1, -1, 15, -1)); + KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, -1, -1, -1, 1)); + KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, -1, -1, -1, -1)); + KUNIT_ASSERT_FALSE(test, tb_tunnel_match_dma(tunnel, 8, -1, 8, -1)); + + tb_tunnel_free(tunnel); + + tunnel = tb_tunnel_alloc_dma(NULL, nhi, port, 15, 1, -1, -1); + KUNIT_ASSERT_TRUE(test, tunnel != NULL); + KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, 15, 1, -1, -1)); + KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, 15, -1, -1, -1)); + KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, -1, 1, -1, -1)); + KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, -1, -1, -1, -1)); + KUNIT_ASSERT_FALSE(test, tb_tunnel_match_dma(tunnel, 15, 1, 15, 1)); + KUNIT_ASSERT_FALSE(test, tb_tunnel_match_dma(tunnel, -1, -1, 15, 1)); + KUNIT_ASSERT_FALSE(test, tb_tunnel_match_dma(tunnel, 15, 11, -1, -1)); + + tb_tunnel_free(tunnel); + + tunnel = tb_tunnel_alloc_dma(NULL, nhi, port, -1, -1, 15, 11); + KUNIT_ASSERT_TRUE(test, tunnel != NULL); + KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, -1, -1, 15, 11)); + KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, -1, -1, 15, -1)); + KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, -1, -1, -1, 11)); + KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, -1, -1, -1, -1)); + KUNIT_ASSERT_FALSE(test, tb_tunnel_match_dma(tunnel, -1, -1, 15, 1)); + KUNIT_ASSERT_FALSE(test, tb_tunnel_match_dma(tunnel, -1, -1, 10, 11)); + KUNIT_ASSERT_FALSE(test, tb_tunnel_match_dma(tunnel, 15, 11, -1, -1)); + + tb_tunnel_free(tunnel); +} + +static const u32 root_directory[] = { + 0x55584401, /* "UXD" v1 */ + 0x00000018, /* Root directory length */ + 0x76656e64, /* "vend" */ + 0x6f726964, /* "orid" */ + 0x76000001, /* "v" R 1 */ + 0x00000a27, /* Immediate value, ! Vendor ID */ + 0x76656e64, /* "vend" */ + 0x6f726964, /* "orid" */ + 0x74000003, /* "t" R 3 */ + 0x0000001a, /* Text leaf offset, (“Apple Inc.”) */ + 0x64657669, /* "devi" */ + 0x63656964, /* "ceid" */ + 0x76000001, /* "v" R 1 */ + 0x0000000a, /* Immediate value, ! Device ID */ + 0x64657669, /* "devi" */ + 0x63656964, /* "ceid" */ + 0x74000003, /* "t" R 3 */ + 0x0000001d, /* Text leaf offset, (“Macintosh”) */ + 0x64657669, /* "devi" */ + 0x63657276, /* "cerv" */ + 0x76000001, /* "v" R 1 */ + 0x80000100, /* Immediate value, Device Revision */ + 0x6e657477, /* "netw" */ + 0x6f726b00, /* "ork" */ + 0x44000014, /* "D" R 20 */ + 0x00000021, /* Directory data offset, (Network Directory) */ + 0x4170706c, /* "Appl" */ + 0x6520496e, /* "e In" */ + 0x632e0000, /* "c." ! */ + 0x4d616369, /* "Maci" */ + 0x6e746f73, /* "ntos" */ + 0x68000000, /* "h" */ + 0x00000000, /* padding */ + 0xca8961c6, /* Directory UUID, Network Directory */ + 0x9541ce1c, /* Directory UUID, Network Directory */ + 0x5949b8bd, /* Directory UUID, Network Directory */ + 0x4f5a5f2e, /* Directory UUID, Network Directory */ + 0x70727463, /* "prtc" */ + 0x69640000, /* "id" */ + 0x76000001, /* "v" R 1 */ + 0x00000001, /* Immediate value, Network Protocol ID */ + 0x70727463, /* "prtc" */ + 0x76657273, /* "vers" */ + 0x76000001, /* "v" R 1 */ + 0x00000001, /* Immediate value, Network Protocol Version */ + 0x70727463, /* "prtc" */ + 0x72657673, /* "revs" */ + 0x76000001, /* "v" R 1 */ + 0x00000001, /* Immediate value, Network Protocol Revision */ + 0x70727463, /* "prtc" */ + 0x73746e73, /* "stns" */ + 0x76000001, /* "v" R 1 */ + 0x00000000, /* Immediate value, Network Protocol Settings */ +}; + +static const uuid_t network_dir_uuid = + UUID_INIT(0xc66189ca, 0x1cce, 0x4195, + 0xbd, 0xb8, 0x49, 0x59, 0x2e, 0x5f, 0x5a, 0x4f); + +static void tb_test_property_parse(struct kunit *test) +{ + struct tb_property_dir *dir, *network_dir; + struct tb_property *p; + + dir = tb_property_parse_dir(root_directory, ARRAY_SIZE(root_directory)); + KUNIT_ASSERT_TRUE(test, dir != NULL); + + p = tb_property_find(dir, "foo", TB_PROPERTY_TYPE_TEXT); + KUNIT_ASSERT_TRUE(test, !p); + + p = tb_property_find(dir, "vendorid", TB_PROPERTY_TYPE_TEXT); + KUNIT_ASSERT_TRUE(test, p != NULL); + KUNIT_EXPECT_STREQ(test, p->value.text, "Apple Inc."); + + p = tb_property_find(dir, "vendorid", TB_PROPERTY_TYPE_VALUE); + KUNIT_ASSERT_TRUE(test, p != NULL); + KUNIT_EXPECT_EQ(test, p->value.immediate, (u32)0xa27); + + p = tb_property_find(dir, "deviceid", TB_PROPERTY_TYPE_TEXT); + KUNIT_ASSERT_TRUE(test, p != NULL); + KUNIT_EXPECT_STREQ(test, p->value.text, "Macintosh"); + + p = tb_property_find(dir, "deviceid", TB_PROPERTY_TYPE_VALUE); + KUNIT_ASSERT_TRUE(test, p != NULL); + KUNIT_EXPECT_EQ(test, p->value.immediate, (u32)0xa); + + p = tb_property_find(dir, "missing", TB_PROPERTY_TYPE_DIRECTORY); + KUNIT_ASSERT_TRUE(test, !p); + + p = tb_property_find(dir, "network", TB_PROPERTY_TYPE_DIRECTORY); + KUNIT_ASSERT_TRUE(test, p != NULL); + + network_dir = p->value.dir; + KUNIT_EXPECT_TRUE(test, uuid_equal(network_dir->uuid, &network_dir_uuid)); + + p = tb_property_find(network_dir, "prtcid", TB_PROPERTY_TYPE_VALUE); + KUNIT_ASSERT_TRUE(test, p != NULL); + KUNIT_EXPECT_EQ(test, p->value.immediate, (u32)0x1); + + p = tb_property_find(network_dir, "prtcvers", TB_PROPERTY_TYPE_VALUE); + KUNIT_ASSERT_TRUE(test, p != NULL); + KUNIT_EXPECT_EQ(test, p->value.immediate, (u32)0x1); + + p = tb_property_find(network_dir, "prtcrevs", TB_PROPERTY_TYPE_VALUE); + KUNIT_ASSERT_TRUE(test, p != NULL); + KUNIT_EXPECT_EQ(test, p->value.immediate, (u32)0x1); + + p = tb_property_find(network_dir, "prtcstns", TB_PROPERTY_TYPE_VALUE); + KUNIT_ASSERT_TRUE(test, p != NULL); + KUNIT_EXPECT_EQ(test, p->value.immediate, (u32)0x0); + + p = tb_property_find(network_dir, "deviceid", TB_PROPERTY_TYPE_VALUE); + KUNIT_EXPECT_TRUE(test, !p); + p = tb_property_find(network_dir, "deviceid", TB_PROPERTY_TYPE_TEXT); + KUNIT_EXPECT_TRUE(test, !p); + + tb_property_free_dir(dir); +} + +static void tb_test_property_format(struct kunit *test) +{ + struct tb_property_dir *dir; + ssize_t block_len; + u32 *block; + int ret, i; + + dir = tb_property_parse_dir(root_directory, ARRAY_SIZE(root_directory)); + KUNIT_ASSERT_TRUE(test, dir != NULL); + + ret = tb_property_format_dir(dir, NULL, 0); + KUNIT_ASSERT_EQ(test, ret, (int)ARRAY_SIZE(root_directory)); + + block_len = ret; + + block = kunit_kzalloc(test, block_len * sizeof(u32), GFP_KERNEL); + KUNIT_ASSERT_TRUE(test, block != NULL); + + ret = tb_property_format_dir(dir, block, block_len); + KUNIT_EXPECT_EQ(test, ret, 0); + + for (i = 0; i < ARRAY_SIZE(root_directory); i++) + KUNIT_EXPECT_EQ(test, root_directory[i], block[i]); + + tb_property_free_dir(dir); +} + +static void compare_dirs(struct kunit *test, struct tb_property_dir *d1, + struct tb_property_dir *d2) +{ + struct tb_property *p1, *p2, *tmp; + int n1, n2, i; + + if (d1->uuid) { + KUNIT_ASSERT_TRUE(test, d2->uuid != NULL); + KUNIT_ASSERT_TRUE(test, uuid_equal(d1->uuid, d2->uuid)); + } else { + KUNIT_ASSERT_TRUE(test, d2->uuid == NULL); + } + + n1 = 0; + tb_property_for_each(d1, tmp) + n1++; + KUNIT_ASSERT_NE(test, n1, 0); + + n2 = 0; + tb_property_for_each(d2, tmp) + n2++; + KUNIT_ASSERT_NE(test, n2, 0); + + KUNIT_ASSERT_EQ(test, n1, n2); + + p1 = NULL; + p2 = NULL; + for (i = 0; i < n1; i++) { + p1 = tb_property_get_next(d1, p1); + KUNIT_ASSERT_TRUE(test, p1 != NULL); + p2 = tb_property_get_next(d2, p2); + KUNIT_ASSERT_TRUE(test, p2 != NULL); + + KUNIT_ASSERT_STREQ(test, &p1->key[0], &p2->key[0]); + KUNIT_ASSERT_EQ(test, p1->type, p2->type); + KUNIT_ASSERT_EQ(test, p1->length, p2->length); + + switch (p1->type) { + case TB_PROPERTY_TYPE_DIRECTORY: + KUNIT_ASSERT_TRUE(test, p1->value.dir != NULL); + KUNIT_ASSERT_TRUE(test, p2->value.dir != NULL); + compare_dirs(test, p1->value.dir, p2->value.dir); + break; + + case TB_PROPERTY_TYPE_DATA: + KUNIT_ASSERT_TRUE(test, p1->value.data != NULL); + KUNIT_ASSERT_TRUE(test, p2->value.data != NULL); + KUNIT_ASSERT_TRUE(test, + !memcmp(p1->value.data, p2->value.data, + p1->length * 4) + ); + break; + + case TB_PROPERTY_TYPE_TEXT: + KUNIT_ASSERT_TRUE(test, p1->value.text != NULL); + KUNIT_ASSERT_TRUE(test, p2->value.text != NULL); + KUNIT_ASSERT_STREQ(test, p1->value.text, p2->value.text); + break; + + case TB_PROPERTY_TYPE_VALUE: + KUNIT_ASSERT_EQ(test, p1->value.immediate, + p2->value.immediate); + break; + default: + KUNIT_FAIL(test, "unexpected property type"); + break; + } + } +} + +static void tb_test_property_copy(struct kunit *test) +{ + struct tb_property_dir *src, *dst; + u32 *block; + int ret, i; + + src = tb_property_parse_dir(root_directory, ARRAY_SIZE(root_directory)); + KUNIT_ASSERT_TRUE(test, src != NULL); + + dst = tb_property_copy_dir(src); + KUNIT_ASSERT_TRUE(test, dst != NULL); + + /* Compare the structures */ + compare_dirs(test, src, dst); + + /* Compare the resulting property block */ + ret = tb_property_format_dir(dst, NULL, 0); + KUNIT_ASSERT_EQ(test, ret, (int)ARRAY_SIZE(root_directory)); + + block = kunit_kzalloc(test, sizeof(root_directory), GFP_KERNEL); + KUNIT_ASSERT_TRUE(test, block != NULL); + + ret = tb_property_format_dir(dst, block, ARRAY_SIZE(root_directory)); + KUNIT_EXPECT_TRUE(test, !ret); + + for (i = 0; i < ARRAY_SIZE(root_directory); i++) + KUNIT_EXPECT_EQ(test, root_directory[i], block[i]); + + tb_property_free_dir(dst); + tb_property_free_dir(src); +} + static struct kunit_case tb_test_cases[] = { KUNIT_CASE(tb_test_path_basic), KUNIT_CASE(tb_test_path_not_connected_walk), @@ -1616,6 +2100,14 @@ static struct kunit_case tb_test_cases[] = { KUNIT_CASE(tb_test_tunnel_dp_max_length), KUNIT_CASE(tb_test_tunnel_port_on_path), KUNIT_CASE(tb_test_tunnel_usb3), + KUNIT_CASE(tb_test_tunnel_dma), + KUNIT_CASE(tb_test_tunnel_dma_rx), + KUNIT_CASE(tb_test_tunnel_dma_tx), + KUNIT_CASE(tb_test_tunnel_dma_chain), + KUNIT_CASE(tb_test_tunnel_dma_match), + KUNIT_CASE(tb_test_property_parse), + KUNIT_CASE(tb_test_property_format), + KUNIT_CASE(tb_test_property_copy), { } }; diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c index 6557b6e07009..e1979bed7146 100644 --- a/drivers/thunderbolt/tunnel.c +++ b/drivers/thunderbolt/tunnel.c @@ -794,24 +794,14 @@ static u32 tb_dma_credits(struct tb_port *nhi) return min(max_credits, 13U); } -static int tb_dma_activate(struct tb_tunnel *tunnel, bool active) -{ - struct tb_port *nhi = tunnel->src_port; - u32 credits; - - credits = active ? tb_dma_credits(nhi) : 0; - return tb_port_set_initial_credits(nhi, credits); -} - -static void tb_dma_init_path(struct tb_path *path, unsigned int isb, - unsigned int efc, u32 credits) +static void tb_dma_init_path(struct tb_path *path, unsigned int efc, u32 credits) { int i; path->egress_fc_enable = efc; path->ingress_fc_enable = TB_PATH_ALL; path->egress_shared_buffer = TB_PATH_NONE; - path->ingress_shared_buffer = isb; + path->ingress_shared_buffer = TB_PATH_NONE; path->priority = 5; path->weight = 1; path->clear_fc = true; @@ -825,28 +815,28 @@ static void tb_dma_init_path(struct tb_path *path, unsigned int isb, * @tb: Pointer to the domain structure * @nhi: Host controller port * @dst: Destination null port which the other domain is connected to - * @transmit_ring: NHI ring number used to send packets towards the - * other domain. Set to %0 if TX path is not needed. * @transmit_path: HopID used for transmitting packets - * @receive_ring: NHI ring number used to receive packets from the - * other domain. Set to %0 if RX path is not needed. + * @transmit_ring: NHI ring number used to send packets towards the + * other domain. Set to %-1 if TX path is not needed. * @receive_path: HopID used for receiving packets + * @receive_ring: NHI ring number used to receive packets from the + * other domain. Set to %-1 if RX path is not needed. * * Return: Returns a tb_tunnel on success or NULL on failure. */ struct tb_tunnel *tb_tunnel_alloc_dma(struct tb *tb, struct tb_port *nhi, - struct tb_port *dst, int transmit_ring, - int transmit_path, int receive_ring, - int receive_path) + struct tb_port *dst, int transmit_path, + int transmit_ring, int receive_path, + int receive_ring) { struct tb_tunnel *tunnel; size_t npaths = 0, i = 0; struct tb_path *path; u32 credits; - if (receive_ring) + if (receive_ring > 0) npaths++; - if (transmit_ring) + if (transmit_ring > 0) npaths++; if (WARN_ON(!npaths)) @@ -856,38 +846,96 @@ struct tb_tunnel *tb_tunnel_alloc_dma(struct tb *tb, struct tb_port *nhi, if (!tunnel) return NULL; - tunnel->activate = tb_dma_activate; tunnel->src_port = nhi; tunnel->dst_port = dst; credits = tb_dma_credits(nhi); - if (receive_ring) { + if (receive_ring > 0) { path = tb_path_alloc(tb, dst, receive_path, nhi, receive_ring, 0, "DMA RX"); if (!path) { tb_tunnel_free(tunnel); return NULL; } - tb_dma_init_path(path, TB_PATH_NONE, TB_PATH_SOURCE | TB_PATH_INTERNAL, - credits); + tb_dma_init_path(path, TB_PATH_SOURCE | TB_PATH_INTERNAL, credits); tunnel->paths[i++] = path; } - if (transmit_ring) { + if (transmit_ring > 0) { path = tb_path_alloc(tb, nhi, transmit_ring, dst, transmit_path, 0, "DMA TX"); if (!path) { tb_tunnel_free(tunnel); return NULL; } - tb_dma_init_path(path, TB_PATH_SOURCE, TB_PATH_ALL, credits); + tb_dma_init_path(path, TB_PATH_ALL, credits); tunnel->paths[i++] = path; } return tunnel; } +/** + * tb_tunnel_match_dma() - Match DMA tunnel + * @tunnel: Tunnel to match + * @transmit_path: HopID used for transmitting packets. Pass %-1 to ignore. + * @transmit_ring: NHI ring number used to send packets towards the + * other domain. Pass %-1 to ignore. + * @receive_path: HopID used for receiving packets. Pass %-1 to ignore. + * @receive_ring: NHI ring number used to receive packets from the + * other domain. Pass %-1 to ignore. + * + * This function can be used to match specific DMA tunnel, if there are + * multiple DMA tunnels going through the same XDomain connection. + * Returns true if there is match and false otherwise. + */ +bool tb_tunnel_match_dma(const struct tb_tunnel *tunnel, int transmit_path, + int transmit_ring, int receive_path, int receive_ring) +{ + const struct tb_path *tx_path = NULL, *rx_path = NULL; + int i; + + if (!receive_ring || !transmit_ring) + return false; + + for (i = 0; i < tunnel->npaths; i++) { + const struct tb_path *path = tunnel->paths[i]; + + if (!path) + continue; + + if (tb_port_is_nhi(path->hops[0].in_port)) + tx_path = path; + else if (tb_port_is_nhi(path->hops[path->path_length - 1].out_port)) + rx_path = path; + } + + if (transmit_ring > 0 || transmit_path > 0) { + if (!tx_path) + return false; + if (transmit_ring > 0 && + (tx_path->hops[0].in_hop_index != transmit_ring)) + return false; + if (transmit_path > 0 && + (tx_path->hops[tx_path->path_length - 1].next_hop_index != transmit_path)) + return false; + } + + if (receive_ring > 0 || receive_path > 0) { + if (!rx_path) + return false; + if (receive_path > 0 && + (rx_path->hops[0].in_hop_index != receive_path)) + return false; + if (receive_ring > 0 && + (rx_path->hops[rx_path->path_length - 1].next_hop_index != receive_ring)) + return false; + } + + return true; +} + static int tb_usb3_max_link_rate(struct tb_port *up, struct tb_port *down) { int ret, up_max_rate, down_max_rate; diff --git a/drivers/thunderbolt/tunnel.h b/drivers/thunderbolt/tunnel.h index 1d2a64eb060d..a66994fb4e60 100644 --- a/drivers/thunderbolt/tunnel.h +++ b/drivers/thunderbolt/tunnel.h @@ -70,9 +70,11 @@ struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in, struct tb_port *out, int max_up, int max_down); struct tb_tunnel *tb_tunnel_alloc_dma(struct tb *tb, struct tb_port *nhi, - struct tb_port *dst, int transmit_ring, - int transmit_path, int receive_ring, - int receive_path); + struct tb_port *dst, int transmit_path, + int transmit_ring, int receive_path, + int receive_ring); +bool tb_tunnel_match_dma(const struct tb_tunnel *tunnel, int transmit_path, + int transmit_ring, int receive_path, int receive_ring); struct tb_tunnel *tb_tunnel_discover_usb3(struct tb *tb, struct tb_port *down); struct tb_tunnel *tb_tunnel_alloc_usb3(struct tb *tb, struct tb_port *up, struct tb_port *down, int max_up, diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c index 7cf8b9c85ab7..b21d99d59412 100644 --- a/drivers/thunderbolt/xdomain.c +++ b/drivers/thunderbolt/xdomain.c @@ -12,17 +12,19 @@ #include #include #include +#include #include #include #include #include "tb.h" -#define XDOMAIN_DEFAULT_TIMEOUT 5000 /* ms */ +#define XDOMAIN_DEFAULT_TIMEOUT 1000 /* ms */ #define XDOMAIN_UUID_RETRIES 10 -#define XDOMAIN_PROPERTIES_RETRIES 60 +#define XDOMAIN_PROPERTIES_RETRIES 10 #define XDOMAIN_PROPERTIES_CHANGED_RETRIES 10 #define XDOMAIN_BONDING_WAIT 100 /* ms */ +#define XDOMAIN_DEFAULT_MAX_HOPID 15 struct xdomain_request_work { struct work_struct work; @@ -34,13 +36,15 @@ static bool tb_xdomain_enabled = true; module_param_named(xdomain, tb_xdomain_enabled, bool, 0444); MODULE_PARM_DESC(xdomain, "allow XDomain protocol (default: true)"); -/* Serializes access to the properties and protocol handlers below */ +/* + * Serializes access to the properties and protocol handlers below. If + * you need to take both this lock and the struct tb_xdomain lock, take + * this one first. + */ static DEFINE_MUTEX(xdomain_lock); /* Properties exposed to the remote domains */ static struct tb_property_dir *xdomain_property_dir; -static u32 *xdomain_property_block; -static u32 xdomain_property_block_len; static u32 xdomain_property_block_gen; /* Additional protocol handlers */ @@ -385,8 +389,7 @@ static int tb_xdp_properties_request(struct tb_ctl *ctl, u64 route, } static int tb_xdp_properties_response(struct tb *tb, struct tb_ctl *ctl, - u64 route, u8 sequence, const uuid_t *src_uuid, - const struct tb_xdp_properties *req) + struct tb_xdomain *xd, u8 sequence, const struct tb_xdp_properties *req) { struct tb_xdp_properties_response *res; size_t total_size; @@ -398,39 +401,39 @@ static int tb_xdp_properties_response(struct tb *tb, struct tb_ctl *ctl, * protocol supports forwarding, though which we might add * support later on. */ - if (!uuid_equal(src_uuid, &req->dst_uuid)) { - tb_xdp_error_response(ctl, route, sequence, + if (!uuid_equal(xd->local_uuid, &req->dst_uuid)) { + tb_xdp_error_response(ctl, xd->route, sequence, ERROR_UNKNOWN_DOMAIN); return 0; } - mutex_lock(&xdomain_lock); + mutex_lock(&xd->lock); - if (req->offset >= xdomain_property_block_len) { - mutex_unlock(&xdomain_lock); + if (req->offset >= xd->local_property_block_len) { + mutex_unlock(&xd->lock); return -EINVAL; } - len = xdomain_property_block_len - req->offset; + len = xd->local_property_block_len - req->offset; len = min_t(u16, len, TB_XDP_PROPERTIES_MAX_DATA_LENGTH); total_size = sizeof(*res) + len * 4; res = kzalloc(total_size, GFP_KERNEL); if (!res) { - mutex_unlock(&xdomain_lock); + mutex_unlock(&xd->lock); return -ENOMEM; } - tb_xdp_fill_header(&res->hdr, route, sequence, PROPERTIES_RESPONSE, + tb_xdp_fill_header(&res->hdr, xd->route, sequence, PROPERTIES_RESPONSE, total_size); - res->generation = xdomain_property_block_gen; - res->data_length = xdomain_property_block_len; + res->generation = xd->local_property_block_gen; + res->data_length = xd->local_property_block_len; res->offset = req->offset; - uuid_copy(&res->src_uuid, src_uuid); + uuid_copy(&res->src_uuid, xd->local_uuid); uuid_copy(&res->dst_uuid, &req->src_uuid); - memcpy(res->data, &xdomain_property_block[req->offset], len * 4); + memcpy(res->data, &xd->local_property_block[req->offset], len * 4); - mutex_unlock(&xdomain_lock); + mutex_unlock(&xd->lock); ret = __tb_xdomain_response(ctl, res, total_size, TB_CFG_PKG_XDOMAIN_RESP); @@ -512,52 +515,63 @@ void tb_unregister_protocol_handler(struct tb_protocol_handler *handler) } EXPORT_SYMBOL_GPL(tb_unregister_protocol_handler); -static int rebuild_property_block(void) +static void update_property_block(struct tb_xdomain *xd) { - u32 *block, len; - int ret; - - ret = tb_property_format_dir(xdomain_property_dir, NULL, 0); - if (ret < 0) - return ret; - - len = ret; - - block = kcalloc(len, sizeof(u32), GFP_KERNEL); - if (!block) - return -ENOMEM; - - ret = tb_property_format_dir(xdomain_property_dir, block, len); - if (ret) { - kfree(block); - return ret; - } - - kfree(xdomain_property_block); - xdomain_property_block = block; - xdomain_property_block_len = len; - xdomain_property_block_gen++; - - return 0; -} - -static void finalize_property_block(void) -{ - const struct tb_property *nodename; - - /* - * On first XDomain connection we set up the the system - * nodename. This delayed here because userspace may not have it - * set when the driver is first probed. - */ mutex_lock(&xdomain_lock); - nodename = tb_property_find(xdomain_property_dir, "deviceid", - TB_PROPERTY_TYPE_TEXT); - if (!nodename) { - tb_property_add_text(xdomain_property_dir, "deviceid", - utsname()->nodename); - rebuild_property_block(); + mutex_lock(&xd->lock); + /* + * If the local property block is not up-to-date, rebuild it now + * based on the global property template. + */ + if (!xd->local_property_block || + xd->local_property_block_gen < xdomain_property_block_gen) { + struct tb_property_dir *dir; + int ret, block_len; + u32 *block; + + dir = tb_property_copy_dir(xdomain_property_dir); + if (!dir) { + dev_warn(&xd->dev, "failed to copy properties\n"); + goto out_unlock; + } + + /* Fill in non-static properties now */ + tb_property_add_text(dir, "deviceid", utsname()->nodename); + tb_property_add_immediate(dir, "maxhopid", xd->local_max_hopid); + + ret = tb_property_format_dir(dir, NULL, 0); + if (ret < 0) { + dev_warn(&xd->dev, "local property block creation failed\n"); + tb_property_free_dir(dir); + goto out_unlock; + } + + block_len = ret; + block = kcalloc(block_len, sizeof(*block), GFP_KERNEL); + if (!block) { + tb_property_free_dir(dir); + goto out_unlock; + } + + ret = tb_property_format_dir(dir, block, block_len); + if (ret) { + dev_warn(&xd->dev, "property block generation failed\n"); + tb_property_free_dir(dir); + kfree(block); + goto out_unlock; + } + + tb_property_free_dir(dir); + /* Release the previous block */ + kfree(xd->local_property_block); + /* Assign new one */ + xd->local_property_block = block; + xd->local_property_block_len = block_len; + xd->local_property_block_gen = xdomain_property_block_gen; } + +out_unlock: + mutex_unlock(&xd->lock); mutex_unlock(&xdomain_lock); } @@ -568,6 +582,7 @@ static void tb_xdp_handle_request(struct work_struct *work) const struct tb_xdomain_header *xhdr = &pkg->xd_hdr; struct tb *tb = xw->tb; struct tb_ctl *ctl = tb->ctl; + struct tb_xdomain *xd; const uuid_t *uuid; int ret = 0; u32 sequence; @@ -589,17 +604,21 @@ static void tb_xdp_handle_request(struct work_struct *work) goto out; } - finalize_property_block(); + tb_dbg(tb, "%llx: received XDomain request %#x\n", route, pkg->type); + + xd = tb_xdomain_find_by_route_locked(tb, route); + if (xd) + update_property_block(xd); switch (pkg->type) { case PROPERTIES_REQUEST: - ret = tb_xdp_properties_response(tb, ctl, route, sequence, uuid, - (const struct tb_xdp_properties *)pkg); + if (xd) { + ret = tb_xdp_properties_response(tb, ctl, xd, sequence, + (const struct tb_xdp_properties *)pkg); + } break; - case PROPERTIES_CHANGED_REQUEST: { - struct tb_xdomain *xd; - + case PROPERTIES_CHANGED_REQUEST: ret = tb_xdp_properties_changed_response(ctl, route, sequence); /* @@ -607,17 +626,11 @@ static void tb_xdp_handle_request(struct work_struct *work) * the xdomain related to this connection as well in * case there is a change in services it offers. */ - xd = tb_xdomain_find_by_route_locked(tb, route); - if (xd) { - if (device_is_registered(&xd->dev)) { - queue_delayed_work(tb->wq, &xd->get_properties_work, - msecs_to_jiffies(50)); - } - tb_xdomain_put(xd); + if (xd && device_is_registered(&xd->dev)) { + queue_delayed_work(tb->wq, &xd->get_properties_work, + msecs_to_jiffies(50)); } - break; - } case UUID_REQUEST_OLD: case UUID_REQUEST: @@ -630,6 +643,8 @@ static void tb_xdp_handle_request(struct work_struct *work) break; } + tb_xdomain_put(xd); + if (ret) { tb_warn(tb, "failed to send XDomain response for %#x\n", pkg->type); @@ -811,7 +826,7 @@ static int remove_missing_service(struct device *dev, void *data) if (!svc) return 0; - if (!tb_property_find(xd->properties, svc->key, + if (!tb_property_find(xd->remote_properties, svc->key, TB_PROPERTY_TYPE_DIRECTORY)) device_unregister(dev); @@ -871,7 +886,7 @@ static void enumerate_services(struct tb_xdomain *xd) device_for_each_child_reverse(&xd->dev, xd, remove_missing_service); /* Then re-enumerate properties creating new services as we go */ - tb_property_for_each(xd->properties, p) { + tb_property_for_each(xd->remote_properties, p) { if (p->type != TB_PROPERTY_TYPE_DIRECTORY) continue; @@ -928,6 +943,14 @@ static int populate_properties(struct tb_xdomain *xd, return -EINVAL; xd->vendor = p->value.immediate; + p = tb_property_find(dir, "maxhopid", TB_PROPERTY_TYPE_VALUE); + /* + * USB4 inter-domain spec suggests using 15 as HopID if the + * other end does not announce it in a property. This is for + * TBT3 compatibility. + */ + xd->remote_max_hopid = p ? p->value.immediate : XDOMAIN_DEFAULT_MAX_HOPID; + kfree(xd->device_name); xd->device_name = NULL; kfree(xd->vendor_name); @@ -944,19 +967,6 @@ static int populate_properties(struct tb_xdomain *xd, return 0; } -/* Called with @xd->lock held */ -static void tb_xdomain_restore_paths(struct tb_xdomain *xd) -{ - if (!xd->resume) - return; - - xd->resume = false; - if (xd->transmit_path) { - dev_dbg(&xd->dev, "re-establishing DMA path\n"); - tb_domain_approve_xdomain_paths(xd->tb, xd); - } -} - static inline struct tb_switch *tb_xdomain_parent(struct tb_xdomain *xd) { return tb_to_switch(xd->dev.parent); @@ -1002,9 +1012,12 @@ static void tb_xdomain_get_uuid(struct work_struct *work) uuid_t uuid; int ret; + dev_dbg(&xd->dev, "requesting remote UUID\n"); + ret = tb_xdp_uuid_request(tb->ctl, xd->route, xd->uuid_retries, &uuid); if (ret < 0) { if (xd->uuid_retries-- > 0) { + dev_dbg(&xd->dev, "failed to request UUID, retrying\n"); queue_delayed_work(xd->tb->wq, &xd->get_uuid_work, msecs_to_jiffies(100)); } else { @@ -1013,6 +1026,8 @@ static void tb_xdomain_get_uuid(struct work_struct *work) return; } + dev_dbg(&xd->dev, "got remote UUID %pUb\n", &uuid); + if (uuid_equal(&uuid, xd->local_uuid)) dev_dbg(&xd->dev, "intra-domain loop detected\n"); @@ -1052,11 +1067,15 @@ static void tb_xdomain_get_properties(struct work_struct *work) u32 gen = 0; int ret; + dev_dbg(&xd->dev, "requesting remote properties\n"); + ret = tb_xdp_properties_request(tb->ctl, xd->route, xd->local_uuid, xd->remote_uuid, xd->properties_retries, &block, &gen); if (ret < 0) { if (xd->properties_retries-- > 0) { + dev_dbg(&xd->dev, + "failed to request remote properties, retrying\n"); queue_delayed_work(xd->tb->wq, &xd->get_properties_work, msecs_to_jiffies(1000)); } else { @@ -1073,16 +1092,8 @@ static void tb_xdomain_get_properties(struct work_struct *work) mutex_lock(&xd->lock); /* Only accept newer generation properties */ - if (xd->properties && gen <= xd->property_block_gen) { - /* - * On resume it is likely that the properties block is - * not changed (unless the other end added or removed - * services). However, we need to make sure the existing - * DMA paths are restored properly. - */ - tb_xdomain_restore_paths(xd); + if (xd->remote_properties && gen <= xd->remote_property_block_gen) goto err_free_block; - } dir = tb_property_parse_dir(block, ret); if (!dir) { @@ -1097,18 +1108,16 @@ static void tb_xdomain_get_properties(struct work_struct *work) } /* Release the existing one */ - if (xd->properties) { - tb_property_free_dir(xd->properties); + if (xd->remote_properties) { + tb_property_free_dir(xd->remote_properties); update = true; } - xd->properties = dir; - xd->property_block_gen = gen; + xd->remote_properties = dir; + xd->remote_property_block_gen = gen; tb_xdomain_update_link_attributes(xd); - tb_xdomain_restore_paths(xd); - mutex_unlock(&xd->lock); kfree(block); @@ -1123,6 +1132,11 @@ static void tb_xdomain_get_properties(struct work_struct *work) dev_err(&xd->dev, "failed to add XDomain device\n"); return; } + dev_info(&xd->dev, "new host found, vendor=%#x device=%#x\n", + xd->vendor, xd->device); + if (xd->vendor_name && xd->device_name) + dev_info(&xd->dev, "%s %s\n", xd->vendor_name, + xd->device_name); } else { kobject_uevent(&xd->dev.kobj, KOBJ_CHANGE); } @@ -1143,13 +1157,19 @@ static void tb_xdomain_properties_changed(struct work_struct *work) properties_changed_work.work); int ret; + dev_dbg(&xd->dev, "sending properties changed notification\n"); + ret = tb_xdp_properties_changed_request(xd->tb->ctl, xd->route, xd->properties_changed_retries, xd->local_uuid); if (ret) { - if (xd->properties_changed_retries-- > 0) + if (xd->properties_changed_retries-- > 0) { + dev_dbg(&xd->dev, + "failed to send properties changed notification, retrying\n"); queue_delayed_work(xd->tb->wq, &xd->properties_changed_work, msecs_to_jiffies(1000)); + } + dev_err(&xd->dev, "failed to send properties changed notification\n"); return; } @@ -1180,6 +1200,15 @@ device_name_show(struct device *dev, struct device_attribute *attr, char *buf) } static DEVICE_ATTR_RO(device_name); +static ssize_t maxhopid_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev); + + return sprintf(buf, "%d\n", xd->remote_max_hopid); +} +static DEVICE_ATTR_RO(maxhopid); + static ssize_t vendor_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1238,6 +1267,7 @@ static DEVICE_ATTR(tx_lanes, 0444, lanes_show, NULL); static struct attribute *xdomain_attrs[] = { &dev_attr_device.attr, &dev_attr_device_name.attr, + &dev_attr_maxhopid.attr, &dev_attr_rx_lanes.attr, &dev_attr_rx_speed.attr, &dev_attr_tx_lanes.attr, @@ -1263,7 +1293,10 @@ static void tb_xdomain_release(struct device *dev) put_device(xd->dev.parent); - tb_property_free_dir(xd->properties); + kfree(xd->local_property_block); + tb_property_free_dir(xd->remote_properties); + ida_destroy(&xd->out_hopids); + ida_destroy(&xd->in_hopids); ida_destroy(&xd->service_ids); kfree(xd->local_uuid); @@ -1310,15 +1343,7 @@ static int __maybe_unused tb_xdomain_suspend(struct device *dev) static int __maybe_unused tb_xdomain_resume(struct device *dev) { - struct tb_xdomain *xd = tb_to_xdomain(dev); - - /* - * Ask tb_xdomain_get_properties() restore any existing DMA - * paths after properties are re-read. - */ - xd->resume = true; - start_handshake(xd); - + start_handshake(tb_to_xdomain(dev)); return 0; } @@ -1363,7 +1388,10 @@ struct tb_xdomain *tb_xdomain_alloc(struct tb *tb, struct device *parent, xd->tb = tb; xd->route = route; + xd->local_max_hopid = down->config.max_in_hop_id; ida_init(&xd->service_ids); + ida_init(&xd->in_hopids); + ida_init(&xd->out_hopids); mutex_init(&xd->lock); INIT_DELAYED_WORK(&xd->get_uuid_work, tb_xdomain_get_uuid); INIT_DELAYED_WORK(&xd->get_properties_work, tb_xdomain_get_properties); @@ -1390,6 +1418,10 @@ struct tb_xdomain *tb_xdomain_alloc(struct tb *tb, struct device *parent, xd->dev.groups = xdomain_attr_groups; dev_set_name(&xd->dev, "%u-%llx", tb->index, route); + dev_dbg(&xd->dev, "local UUID %pUb\n", local_uuid); + if (remote_uuid) + dev_dbg(&xd->dev, "remote UUID %pUb\n", remote_uuid); + /* * This keeps the DMA powered on as long as we have active * connection to another host. @@ -1452,10 +1484,12 @@ void tb_xdomain_remove(struct tb_xdomain *xd) pm_runtime_put_noidle(&xd->dev); pm_runtime_set_suspended(&xd->dev); - if (!device_is_registered(&xd->dev)) + if (!device_is_registered(&xd->dev)) { put_device(&xd->dev); - else + } else { + dev_info(&xd->dev, "host disconnected\n"); device_unregister(&xd->dev); + } } /** @@ -1522,74 +1556,119 @@ void tb_xdomain_lane_bonding_disable(struct tb_xdomain *xd) } EXPORT_SYMBOL_GPL(tb_xdomain_lane_bonding_disable); +/** + * tb_xdomain_alloc_in_hopid() - Allocate input HopID for tunneling + * @xd: XDomain connection + * @hopid: Preferred HopID or %-1 for next available + * + * Returns allocated HopID or negative errno. Specifically returns + * %-ENOSPC if there are no more available HopIDs. Returned HopID is + * guaranteed to be within range supported by the input lane adapter. + * Call tb_xdomain_release_in_hopid() to release the allocated HopID. + */ +int tb_xdomain_alloc_in_hopid(struct tb_xdomain *xd, int hopid) +{ + if (hopid < 0) + hopid = TB_PATH_MIN_HOPID; + if (hopid < TB_PATH_MIN_HOPID || hopid > xd->local_max_hopid) + return -EINVAL; + + return ida_alloc_range(&xd->in_hopids, hopid, xd->local_max_hopid, + GFP_KERNEL); +} +EXPORT_SYMBOL_GPL(tb_xdomain_alloc_in_hopid); + +/** + * tb_xdomain_alloc_out_hopid() - Allocate output HopID for tunneling + * @xd: XDomain connection + * @hopid: Preferred HopID or %-1 for next available + * + * Returns allocated HopID or negative errno. Specifically returns + * %-ENOSPC if there are no more available HopIDs. Returned HopID is + * guaranteed to be within range supported by the output lane adapter. + * Call tb_xdomain_release_in_hopid() to release the allocated HopID. + */ +int tb_xdomain_alloc_out_hopid(struct tb_xdomain *xd, int hopid) +{ + if (hopid < 0) + hopid = TB_PATH_MIN_HOPID; + if (hopid < TB_PATH_MIN_HOPID || hopid > xd->remote_max_hopid) + return -EINVAL; + + return ida_alloc_range(&xd->out_hopids, hopid, xd->remote_max_hopid, + GFP_KERNEL); +} +EXPORT_SYMBOL_GPL(tb_xdomain_alloc_out_hopid); + +/** + * tb_xdomain_release_in_hopid() - Release input HopID + * @xd: XDomain connection + * @hopid: HopID to release + */ +void tb_xdomain_release_in_hopid(struct tb_xdomain *xd, int hopid) +{ + ida_free(&xd->in_hopids, hopid); +} +EXPORT_SYMBOL_GPL(tb_xdomain_release_in_hopid); + +/** + * tb_xdomain_release_out_hopid() - Release output HopID + * @xd: XDomain connection + * @hopid: HopID to release + */ +void tb_xdomain_release_out_hopid(struct tb_xdomain *xd, int hopid) +{ + ida_free(&xd->out_hopids, hopid); +} +EXPORT_SYMBOL_GPL(tb_xdomain_release_out_hopid); + /** * tb_xdomain_enable_paths() - Enable DMA paths for XDomain connection * @xd: XDomain connection - * @transmit_path: HopID of the transmit path the other end is using to - * send packets - * @transmit_ring: DMA ring used to receive packets from the other end - * @receive_path: HopID of the receive path the other end is using to - * receive packets - * @receive_ring: DMA ring used to send packets to the other end + * @transmit_path: HopID we are using to send out packets + * @transmit_ring: DMA ring used to send out packets + * @receive_path: HopID the other end is using to send packets to us + * @receive_ring: DMA ring used to receive packets from @receive_path * * The function enables DMA paths accordingly so that after successful * return the caller can send and receive packets using high-speed DMA - * path. + * path. If a transmit or receive path is not needed, pass %-1 for those + * parameters. * * Return: %0 in case of success and negative errno in case of error */ -int tb_xdomain_enable_paths(struct tb_xdomain *xd, u16 transmit_path, - u16 transmit_ring, u16 receive_path, - u16 receive_ring) +int tb_xdomain_enable_paths(struct tb_xdomain *xd, int transmit_path, + int transmit_ring, int receive_path, + int receive_ring) { - int ret; - - mutex_lock(&xd->lock); - - if (xd->transmit_path) { - ret = xd->transmit_path == transmit_path ? 0 : -EBUSY; - goto exit_unlock; - } - - xd->transmit_path = transmit_path; - xd->transmit_ring = transmit_ring; - xd->receive_path = receive_path; - xd->receive_ring = receive_ring; - - ret = tb_domain_approve_xdomain_paths(xd->tb, xd); - -exit_unlock: - mutex_unlock(&xd->lock); - - return ret; + return tb_domain_approve_xdomain_paths(xd->tb, xd, transmit_path, + transmit_ring, receive_path, + receive_ring); } EXPORT_SYMBOL_GPL(tb_xdomain_enable_paths); /** * tb_xdomain_disable_paths() - Disable DMA paths for XDomain connection * @xd: XDomain connection + * @transmit_path: HopID we are using to send out packets + * @transmit_ring: DMA ring used to send out packets + * @receive_path: HopID the other end is using to send packets to us + * @receive_ring: DMA ring used to receive packets from @receive_path * * This does the opposite of tb_xdomain_enable_paths(). After call to - * this the caller is not expected to use the rings anymore. + * this the caller is not expected to use the rings anymore. Passing %-1 + * as path/ring parameter means don't care. Normally the callers should + * pass the same values here as they do when paths are enabled. * * Return: %0 in case of success and negative errno in case of error */ -int tb_xdomain_disable_paths(struct tb_xdomain *xd) +int tb_xdomain_disable_paths(struct tb_xdomain *xd, int transmit_path, + int transmit_ring, int receive_path, + int receive_ring) { - int ret = 0; - - mutex_lock(&xd->lock); - if (xd->transmit_path) { - xd->transmit_path = 0; - xd->transmit_ring = 0; - xd->receive_path = 0; - xd->receive_ring = 0; - - ret = tb_domain_disconnect_xdomain_paths(xd->tb, xd); - } - mutex_unlock(&xd->lock); - - return ret; + return tb_domain_disconnect_xdomain_paths(xd->tb, xd, transmit_path, + transmit_ring, receive_path, + receive_ring); } EXPORT_SYMBOL_GPL(tb_xdomain_disable_paths); @@ -1826,11 +1905,7 @@ int tb_register_property_dir(const char *key, struct tb_property_dir *dir) if (ret) goto err_unlock; - ret = rebuild_property_block(); - if (ret) { - remove_directory(key, dir); - goto err_unlock; - } + xdomain_property_block_gen++; mutex_unlock(&xdomain_lock); update_all_xdomains(); @@ -1856,7 +1931,7 @@ void tb_unregister_property_dir(const char *key, struct tb_property_dir *dir) mutex_lock(&xdomain_lock); if (remove_directory(key, dir)) - ret = rebuild_property_block(); + xdomain_property_block_gen++; mutex_unlock(&xdomain_lock); if (!ret) @@ -1875,7 +1950,8 @@ int tb_xdomain_init(void) * directories. Those will be added by service drivers * themselves when they are loaded. * - * We also add node name later when first connection is made. + * Rest of the properties are filled dynamically based on these + * when the P2P connection is made. */ tb_property_add_immediate(xdomain_property_dir, "vendorid", PCI_VENDOR_ID_INTEL); @@ -1883,11 +1959,11 @@ int tb_xdomain_init(void) tb_property_add_immediate(xdomain_property_dir, "deviceid", 0x1); tb_property_add_immediate(xdomain_property_dir, "devicerv", 0x80000100); + xdomain_property_block_gen = prandom_u32(); return 0; } void tb_xdomain_exit(void) { - kfree(xdomain_property_block); tb_property_free_dir(xdomain_property_dir); } diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c index 582bfeceedb4..9b1bd417cec0 100644 --- a/drivers/usb/cdns3/cdns3-gadget.c +++ b/drivers/usb/cdns3/cdns3-gadget.c @@ -59,6 +59,7 @@ #include #include #include +#include #include #include "core.h" @@ -190,29 +191,13 @@ dma_addr_t cdns3_trb_virt_to_dma(struct cdns3_endpoint *priv_ep, return priv_ep->trb_pool_dma + offset; } -static int cdns3_ring_size(struct cdns3_endpoint *priv_ep) -{ - switch (priv_ep->type) { - case USB_ENDPOINT_XFER_ISOC: - return TRB_ISO_RING_SIZE; - case USB_ENDPOINT_XFER_CONTROL: - return TRB_CTRL_RING_SIZE; - default: - if (priv_ep->use_streams) - return TRB_STREAM_RING_SIZE; - else - return TRB_RING_SIZE; - } -} - static void cdns3_free_trb_pool(struct cdns3_endpoint *priv_ep) { struct cdns3_device *priv_dev = priv_ep->cdns3_dev; if (priv_ep->trb_pool) { - dma_free_coherent(priv_dev->sysdev, - cdns3_ring_size(priv_ep), - priv_ep->trb_pool, priv_ep->trb_pool_dma); + dma_pool_free(priv_dev->eps_dma_pool, + priv_ep->trb_pool, priv_ep->trb_pool_dma); priv_ep->trb_pool = NULL; } } @@ -226,7 +211,7 @@ static void cdns3_free_trb_pool(struct cdns3_endpoint *priv_ep) int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep) { struct cdns3_device *priv_dev = priv_ep->cdns3_dev; - int ring_size = cdns3_ring_size(priv_ep); + int ring_size = TRB_RING_SIZE; int num_trbs = ring_size / TRB_SIZE; struct cdns3_trb *link_trb; @@ -234,10 +219,10 @@ int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep) cdns3_free_trb_pool(priv_ep); if (!priv_ep->trb_pool) { - priv_ep->trb_pool = dma_alloc_coherent(priv_dev->sysdev, - ring_size, - &priv_ep->trb_pool_dma, - GFP_DMA32 | GFP_ATOMIC); + priv_ep->trb_pool = dma_pool_alloc(priv_dev->eps_dma_pool, + GFP_DMA32 | GFP_ATOMIC, + &priv_ep->trb_pool_dma); + if (!priv_ep->trb_pool) return -ENOMEM; @@ -834,9 +819,15 @@ void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep, priv_ep->dir); if ((priv_req->flags & REQUEST_UNALIGNED) && - priv_ep->dir == USB_DIR_OUT && !request->status) + priv_ep->dir == USB_DIR_OUT && !request->status) { + /* Make DMA buffer CPU accessible */ + dma_sync_single_for_cpu(priv_dev->sysdev, + priv_req->aligned_buf->dma, + priv_req->aligned_buf->size, + priv_req->aligned_buf->dir); memcpy(request->buf, priv_req->aligned_buf->buf, request->length); + } priv_req->flags &= ~(REQUEST_PENDING | REQUEST_UNALIGNED); /* All TRBs have finished, clear the counter */ @@ -898,8 +889,8 @@ static void cdns3_free_aligned_request_buf(struct work_struct *work) * interrupts. */ spin_unlock_irqrestore(&priv_dev->lock, flags); - dma_free_coherent(priv_dev->sysdev, buf->size, - buf->buf, buf->dma); + dma_free_noncoherent(priv_dev->sysdev, buf->size, + buf->buf, buf->dma, buf->dir); kfree(buf); spin_lock_irqsave(&priv_dev->lock, flags); } @@ -926,10 +917,13 @@ static int cdns3_prepare_aligned_request_buf(struct cdns3_request *priv_req) return -ENOMEM; buf->size = priv_req->request.length; + buf->dir = usb_endpoint_dir_in(priv_ep->endpoint.desc) ? + DMA_TO_DEVICE : DMA_FROM_DEVICE; - buf->buf = dma_alloc_coherent(priv_dev->sysdev, + buf->buf = dma_alloc_noncoherent(priv_dev->sysdev, buf->size, &buf->dma, + buf->dir, GFP_ATOMIC); if (!buf->buf) { kfree(buf); @@ -951,10 +945,17 @@ static int cdns3_prepare_aligned_request_buf(struct cdns3_request *priv_req) } if (priv_ep->dir == USB_DIR_IN) { + /* Make DMA buffer CPU accessible */ + dma_sync_single_for_cpu(priv_dev->sysdev, + buf->dma, buf->size, buf->dir); memcpy(buf->buf, priv_req->request.buf, priv_req->request.length); } + /* Transfer DMA buffer ownership back to device */ + dma_sync_single_for_device(priv_dev->sysdev, + buf->dma, buf->size, buf->dir); + priv_req->flags |= REQUEST_UNALIGNED; trace_cdns3_prepare_aligned_request(priv_req); @@ -3103,9 +3104,10 @@ static void cdns3_gadget_exit(struct cdns *cdns) struct cdns3_aligned_buf *buf; buf = cdns3_next_align_buf(&priv_dev->aligned_buf_list); - dma_free_coherent(priv_dev->sysdev, buf->size, + dma_free_noncoherent(priv_dev->sysdev, buf->size, buf->buf, - buf->dma); + buf->dma, + buf->dir); list_del(&buf->list); kfree(buf); @@ -3113,6 +3115,7 @@ static void cdns3_gadget_exit(struct cdns *cdns) dma_free_coherent(priv_dev->sysdev, 8, priv_dev->setup_buf, priv_dev->setup_dma); + dma_pool_destroy(priv_dev->eps_dma_pool); kfree(priv_dev->zlp_buf); usb_put_gadget(&priv_dev->gadget); @@ -3185,6 +3188,14 @@ static int cdns3_gadget_start(struct cdns *cdns) /* initialize endpoint container */ INIT_LIST_HEAD(&priv_dev->gadget.ep_list); INIT_LIST_HEAD(&priv_dev->aligned_buf_list); + priv_dev->eps_dma_pool = dma_pool_create("cdns3_eps_dma_pool", + priv_dev->sysdev, + TRB_RING_SIZE, 8, 0); + if (!priv_dev->eps_dma_pool) { + dev_err(priv_dev->dev, "Failed to create TRB dma pool\n"); + ret = -ENOMEM; + goto err1; + } ret = cdns3_init_eps(priv_dev); if (ret) { @@ -3235,6 +3246,8 @@ static int cdns3_gadget_start(struct cdns *cdns) err2: cdns3_free_all_eps(priv_dev); err1: + dma_pool_destroy(priv_dev->eps_dma_pool); + usb_put_gadget(&priv_dev->gadget); cdns->gadget_dev = NULL; return ret; @@ -3304,6 +3317,8 @@ static int cdns3_gadget_resume(struct cdns *cdns, bool hibernated) return 0; cdns3_gadget_config(priv_dev); + if (hibernated) + writel(USB_CONF_DEVEN, &priv_dev->regs->usb_conf); return 0; } diff --git a/drivers/usb/cdns3/cdns3-gadget.h b/drivers/usb/cdns3/cdns3-gadget.h index 21fa461c518e..c5660f2c4293 100644 --- a/drivers/usb/cdns3/cdns3-gadget.h +++ b/drivers/usb/cdns3/cdns3-gadget.h @@ -12,6 +12,7 @@ #ifndef __LINUX_CDNS3_GADGET #define __LINUX_CDNS3_GADGET #include +#include /* * USBSS-DEV register interface. @@ -1205,6 +1206,7 @@ struct cdns3_aligned_buf { void *buf; dma_addr_t dma; u32 size; + enum dma_data_direction dir; unsigned in_use:1; struct list_head list; }; @@ -1298,6 +1300,7 @@ struct cdns3_device { struct cdns3_usb_regs __iomem *regs; + struct dma_pool *eps_dma_pool; struct usb_ctrlrequest *setup_buf; dma_addr_t setup_dma; void *zlp_buf; diff --git a/drivers/usb/cdns3/cdns3-imx.c b/drivers/usb/cdns3/cdns3-imx.c index 8f88eec0b0ea..74e758dc0895 100644 --- a/drivers/usb/cdns3/cdns3-imx.c +++ b/drivers/usb/cdns3/cdns3-imx.c @@ -361,6 +361,39 @@ static int cdns_imx_suspend(struct device *dev) return 0; } + + +/* Indicate if the controller was power lost before */ +static inline bool cdns_imx_is_power_lost(struct cdns_imx *data) +{ + u32 value; + + value = cdns_imx_readl(data, USB3_CORE_CTRL1); + if ((value & SW_RESET_MASK) == ALL_SW_RESET) + return true; + else + return false; +} + +static int __maybe_unused cdns_imx_system_resume(struct device *dev) +{ + struct cdns_imx *data = dev_get_drvdata(dev); + int ret; + + ret = cdns_imx_resume(dev); + if (ret) + return ret; + + if (cdns_imx_is_power_lost(data)) { + dev_dbg(dev, "resume from power lost\n"); + ret = cdns_imx_noncore_init(data); + if (ret) + cdns_imx_suspend(dev); + } + + return ret; +} + #else static int cdns_imx_platform_suspend(struct device *dev, bool suspend, bool wakeup) @@ -372,6 +405,7 @@ static int cdns_imx_platform_suspend(struct device *dev, static const struct dev_pm_ops cdns_imx_pm_ops = { SET_RUNTIME_PM_OPS(cdns_imx_suspend, cdns_imx_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(cdns_imx_suspend, cdns_imx_system_resume) }; static const struct of_device_id cdns_imx_of_match[] = { diff --git a/drivers/usb/cdns3/cdns3-plat.c b/drivers/usb/cdns3/cdns3-plat.c index 4b18e1c6a4bb..e1deeada425c 100644 --- a/drivers/usb/cdns3/cdns3-plat.c +++ b/drivers/usb/cdns3/cdns3-plat.c @@ -19,6 +19,7 @@ #include "core.h" #include "gadget-export.h" +#include "drd.h" static int set_phy_power_on(struct cdns *cdns) { @@ -236,6 +237,18 @@ static int cdns3_controller_resume(struct device *dev, pm_message_t msg) if (!cdns->in_lpm) return 0; + if (cdns_power_is_lost(cdns)) { + phy_exit(cdns->usb2_phy); + ret = phy_init(cdns->usb2_phy); + if (ret) + return ret; + + phy_exit(cdns->usb3_phy); + ret = phy_init(cdns->usb3_phy); + if (ret) + return ret; + } + ret = set_phy_power_on(cdns); if (ret) return ret; @@ -270,10 +283,18 @@ static int cdns3_plat_runtime_resume(struct device *dev) static int cdns3_plat_suspend(struct device *dev) { struct cdns *cdns = dev_get_drvdata(dev); + int ret; cdns_suspend(cdns); - return cdns3_controller_suspend(dev, PMSG_SUSPEND); + ret = cdns3_controller_suspend(dev, PMSG_SUSPEND); + if (ret) + return ret; + + if (device_may_wakeup(dev) && cdns->wakeup_irq) + enable_irq_wake(cdns->wakeup_irq); + + return ret; } static int cdns3_plat_resume(struct device *dev) diff --git a/drivers/usb/cdns3/cdns3-trace.h b/drivers/usb/cdns3/cdns3-trace.h index 8648c7a7a9dd..7574b4a62813 100644 --- a/drivers/usb/cdns3/cdns3-trace.h +++ b/drivers/usb/cdns3/cdns3-trace.h @@ -214,7 +214,6 @@ DECLARE_EVENT_CLASS(cdns3_log_request, __field(int, no_interrupt) __field(int, start_trb) __field(int, end_trb) - __field(struct cdns3_trb *, start_trb_addr) __field(int, flags) __field(unsigned int, stream_id) ), @@ -230,12 +229,11 @@ DECLARE_EVENT_CLASS(cdns3_log_request, __entry->no_interrupt = req->request.no_interrupt; __entry->start_trb = req->start_trb; __entry->end_trb = req->end_trb; - __entry->start_trb_addr = req->trb; __entry->flags = req->flags; __entry->stream_id = req->request.stream_id; ), TP_printk("%s: req: %p, req buff %p, length: %u/%u %s%s%s, status: %d," - " trb: [start:%d, end:%d: virt addr %pa], flags:%x SID: %u", + " trb: [start:%d, end:%d], flags:%x SID: %u", __get_str(name), __entry->req, __entry->buf, __entry->actual, __entry->length, __entry->zero ? "Z" : "z", @@ -244,7 +242,6 @@ DECLARE_EVENT_CLASS(cdns3_log_request, __entry->status, __entry->start_trb, __entry->end_trb, - __entry->start_trb_addr, __entry->flags, __entry->stream_id ) diff --git a/drivers/usb/cdns3/cdnsp-gadget.c b/drivers/usb/cdns3/cdnsp-gadget.c index d7d4bdd57f46..56707b6b0f57 100644 --- a/drivers/usb/cdns3/cdnsp-gadget.c +++ b/drivers/usb/cdns3/cdnsp-gadget.c @@ -727,7 +727,7 @@ int cdnsp_reset_device(struct cdnsp_device *pdev) * are in Disabled state. */ for (i = 1; i < CDNSP_ENDPOINTS_NUM; ++i) - pdev->eps[i].ep_state |= EP_STOPPED; + pdev->eps[i].ep_state |= EP_STOPPED | EP_UNCONFIGURED; trace_cdnsp_handle_cmd_reset_dev(slot_ctx); @@ -942,6 +942,7 @@ static int cdnsp_gadget_ep_enable(struct usb_ep *ep, pep = to_cdnsp_ep(ep); pdev = pep->pdev; + pep->ep_state &= ~EP_UNCONFIGURED; if (dev_WARN_ONCE(pdev->dev, pep->ep_state & EP_ENABLED, "%s is already enabled\n", pep->name)) @@ -1023,9 +1024,13 @@ static int cdnsp_gadget_ep_disable(struct usb_ep *ep) goto finish; } - cdnsp_cmd_stop_ep(pdev, pep); pep->ep_state |= EP_DIS_IN_RROGRESS; - cdnsp_cmd_flush_ep(pdev, pep); + + /* Endpoint was unconfigured by Reset Device command. */ + if (!(pep->ep_state & EP_UNCONFIGURED)) { + cdnsp_cmd_stop_ep(pdev, pep); + cdnsp_cmd_flush_ep(pdev, pep); + } /* Remove all queued USB requests. */ while (!list_empty(&pep->pending_list)) { @@ -1043,10 +1048,12 @@ static int cdnsp_gadget_ep_disable(struct usb_ep *ep) cdnsp_endpoint_zero(pdev, pep); - ret = cdnsp_update_eps_configuration(pdev, pep); + if (!(pep->ep_state & EP_UNCONFIGURED)) + ret = cdnsp_update_eps_configuration(pdev, pep); + cdnsp_free_endpoint_rings(pdev, pep); - pep->ep_state &= ~EP_ENABLED; + pep->ep_state &= ~(EP_ENABLED | EP_UNCONFIGURED); pep->ep_state |= EP_STOPPED; finish: diff --git a/drivers/usb/cdns3/cdnsp-gadget.h b/drivers/usb/cdns3/cdnsp-gadget.h index 6bbb26548c04..783ca8ffde00 100644 --- a/drivers/usb/cdns3/cdnsp-gadget.h +++ b/drivers/usb/cdns3/cdnsp-gadget.h @@ -835,6 +835,7 @@ struct cdnsp_ep { #define EP_WEDGE BIT(4) #define EP0_HALTED_STATUS BIT(5) #define EP_HAS_STREAMS BIT(6) +#define EP_UNCONFIGURED BIT(7) bool skip; }; diff --git a/drivers/usb/cdns3/cdnsp-mem.c b/drivers/usb/cdns3/cdnsp-mem.c index 7a84e928710e..5d4c4bfe15b7 100644 --- a/drivers/usb/cdns3/cdnsp-mem.c +++ b/drivers/usb/cdns3/cdnsp-mem.c @@ -686,7 +686,7 @@ static void cdnsp_free_priv_device(struct cdnsp_device *pdev) static int cdnsp_alloc_priv_device(struct cdnsp_device *pdev) { - int ret = -ENOMEM; + int ret; ret = cdnsp_init_device_ctx(pdev); if (ret) @@ -1231,7 +1231,6 @@ int cdnsp_mem_init(struct cdnsp_device *pdev) if (!pdev->dcbaa) return -ENOMEM; - memset(pdev->dcbaa, 0, sizeof(*pdev->dcbaa)); pdev->dcbaa->dma = dma; cdnsp_write_64(dma, &pdev->op_regs->dcbaa_ptr); diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c index 199713769289..bb739d88179f 100644 --- a/drivers/usb/cdns3/core.c +++ b/drivers/usb/cdns3/core.c @@ -525,9 +525,36 @@ EXPORT_SYMBOL_GPL(cdns_suspend); int cdns_resume(struct cdns *cdns, u8 set_active) { struct device *dev = cdns->dev; + enum usb_role real_role; + bool role_changed = false; + int ret = 0; + + if (cdns_power_is_lost(cdns)) { + if (cdns->role_sw) { + cdns->role = cdns_role_get(cdns->role_sw); + } else { + real_role = cdns_hw_role_state_machine(cdns); + if (real_role != cdns->role) { + ret = cdns_hw_role_switch(cdns); + if (ret) + return ret; + role_changed = true; + } + } + + if (!role_changed) { + if (cdns->role == USB_ROLE_HOST) + ret = cdns_drd_host_on(cdns); + else if (cdns->role == USB_ROLE_DEVICE) + ret = cdns_drd_gadget_on(cdns); + + if (ret) + return ret; + } + } if (cdns->roles[cdns->role]->resume) - cdns->roles[cdns->role]->resume(cdns, false); + cdns->roles[cdns->role]->resume(cdns, cdns_power_is_lost(cdns)); if (set_active) { pm_runtime_disable(dev); diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c index fa5318ade3e1..55c73b1d8704 100644 --- a/drivers/usb/cdns3/drd.c +++ b/drivers/usb/cdns3/drd.c @@ -478,3 +478,18 @@ int cdns_drd_exit(struct cdns *cdns) return 0; } + + +/* Indicate the cdns3 core was power lost before */ +bool cdns_power_is_lost(struct cdns *cdns) +{ + if (cdns->version == CDNS3_CONTROLLER_V1) { + if (!(readl(&cdns->otg_v1_regs->simulate) & BIT(0))) + return true; + } else { + if (!(readl(&cdns->otg_v0_regs->simulate) & BIT(0))) + return true; + } + return false; +} +EXPORT_SYMBOL_GPL(cdns_power_is_lost); diff --git a/drivers/usb/cdns3/drd.h b/drivers/usb/cdns3/drd.h index 9724acdecbbb..cbdf94f73ed9 100644 --- a/drivers/usb/cdns3/drd.h +++ b/drivers/usb/cdns3/drd.h @@ -215,5 +215,5 @@ int cdns_drd_gadget_on(struct cdns *cdns); void cdns_drd_gadget_off(struct cdns *cdns); int cdns_drd_host_on(struct cdns *cdns); void cdns_drd_host_off(struct cdns *cdns); - +bool cdns_power_is_lost(struct cdns *cdns); #endif /* __LINUX_CDNS3_DRD */ diff --git a/drivers/usb/chipidea/ci_hdrc_tegra.c b/drivers/usb/chipidea/ci_hdrc_tegra.c index 90f2a8b786be..60361141ac04 100644 --- a/drivers/usb/chipidea/ci_hdrc_tegra.c +++ b/drivers/usb/chipidea/ci_hdrc_tegra.c @@ -285,11 +285,9 @@ static int tegra_usb_probe(struct platform_device *pdev) } usb->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "nvidia,phy", 0); - if (IS_ERR(usb->phy)) { - err = PTR_ERR(usb->phy); - dev_err(&pdev->dev, "failed to get PHY: %d\n", err); - return err; - } + if (IS_ERR(usb->phy)) + return dev_err_probe(&pdev->dev, PTR_ERR(usb->phy), + "failed to get PHY\n"); usb->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(usb->clk)) { diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c index 67247d2ac07a..e86d13c04bdb 100644 --- a/drivers/usb/chipidea/host.c +++ b/drivers/usb/chipidea/host.c @@ -32,7 +32,7 @@ struct ehci_ci_priv { struct ci_hdrc_dma_aligned_buffer { void *kmalloc_ptr; void *old_xfer_buffer; - u8 data[0]; + u8 data[]; }; static int ehci_ci_portpower(struct usb_hcd *hcd, int portnum, bool enable) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 3fda1ec961d7..ca7a61190dd9 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -929,8 +929,7 @@ static int get_serial_info(struct tty_struct *tty, struct serial_struct *ss) { struct acm *acm = tty->driver_data; - ss->xmit_fifo_size = acm->writesize; - ss->baud_base = le32_to_cpu(acm->line.dwDTERate); + ss->line = acm->minor; 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 : @@ -942,7 +941,6 @@ static int set_serial_info(struct tty_struct *tty, struct serial_struct *ss) { struct acm *acm = tty->driver_data; unsigned int closing_wait, close_delay; - unsigned int old_closing_wait, old_close_delay; int retval = 0; close_delay = msecs_to_jiffies(ss->close_delay * 10); @@ -950,20 +948,12 @@ static int set_serial_info(struct tty_struct *tty, struct serial_struct *ss) ASYNC_CLOSING_WAIT_NONE : msecs_to_jiffies(ss->closing_wait * 10); - /* we must redo the rounding here, so that the values match */ - old_close_delay = jiffies_to_msecs(acm->port.close_delay) / 10; - old_closing_wait = acm->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ? - ASYNC_CLOSING_WAIT_NONE : - jiffies_to_msecs(acm->port.closing_wait) / 10; - mutex_lock(&acm->port.mutex); if (!capable(CAP_SYS_ADMIN)) { - if ((ss->close_delay != old_close_delay) || - (ss->closing_wait != old_closing_wait)) + if ((close_delay != acm->port.close_delay) || + (closing_wait != acm->port.closing_wait)) retval = -EPERM; - else - retval = -EOPNOTSUPP; } else { acm->port.close_delay = close_delay; acm->port.closing_wait = closing_wait; @@ -1634,12 +1624,13 @@ static int acm_resume(struct usb_interface *intf) struct urb *urb; int rv = 0; - acm_unpoison_urbs(acm); spin_lock_irq(&acm->write_lock); if (--acm->susp_count) goto out; + acm_unpoison_urbs(acm); + if (tty_port_initialized(&acm->port)) { rv = usb_submit_urb(acm->ctrlurb, GFP_ATOMIC); @@ -1922,9 +1913,17 @@ static const struct usb_device_id acm_ids[] = { #endif #if IS_ENABLED(CONFIG_USB_SERIAL_XR) - { USB_DEVICE(0x04e2, 0x1410), /* Ignore XR21V141X USB to Serial converter */ - .driver_info = IGNORE_DEVICE, - }, + { USB_DEVICE(0x04e2, 0x1400), .driver_info = IGNORE_DEVICE }, + { USB_DEVICE(0x04e2, 0x1401), .driver_info = IGNORE_DEVICE }, + { USB_DEVICE(0x04e2, 0x1402), .driver_info = IGNORE_DEVICE }, + { USB_DEVICE(0x04e2, 0x1403), .driver_info = IGNORE_DEVICE }, + { USB_DEVICE(0x04e2, 0x1410), .driver_info = IGNORE_DEVICE }, + { USB_DEVICE(0x04e2, 0x1411), .driver_info = IGNORE_DEVICE }, + { USB_DEVICE(0x04e2, 0x1412), .driver_info = IGNORE_DEVICE }, + { USB_DEVICE(0x04e2, 0x1414), .driver_info = IGNORE_DEVICE }, + { USB_DEVICE(0x04e2, 0x1420), .driver_info = IGNORE_DEVICE }, + { USB_DEVICE(0x04e2, 0x1422), .driver_info = IGNORE_DEVICE }, + { USB_DEVICE(0x04e2, 0x1424), .driver_info = IGNORE_DEVICE }, #endif /*Samsung phone in firmware update mode */ diff --git a/drivers/usb/common/common.c b/drivers/usb/common/common.c index fc21cf2d36f6..347fb3d3894a 100644 --- a/drivers/usb/common/common.c +++ b/drivers/usb/common/common.c @@ -25,6 +25,12 @@ static const char *const ep_type_names[] = { [USB_ENDPOINT_XFER_INT] = "intr", }; +/** + * usb_ep_type_string() - Returns human readable-name of the endpoint type. + * @ep_type: The endpoint type to return human-readable name for. If it's not + * any of the types: USB_ENDPOINT_XFER_{CONTROL, ISOC, BULK, INT}, + * usually got by usb_endpoint_type(), the string 'unknown' will be returned. + */ const char *usb_ep_type_string(int ep_type) { if (ep_type < 0 || ep_type >= ARRAY_SIZE(ep_type_names)) @@ -76,6 +82,12 @@ static const char *const ssp_rate[] = { [USB_SSP_GEN_2x2] = "super-speed-plus-gen2x2", }; +/** + * usb_speed_string() - Returns human readable-name of the speed. + * @speed: The speed to return human-readable name for. If it's not + * any of the speeds defined in usb_device_speed enum, string for + * USB_SPEED_UNKNOWN will be returned. + */ const char *usb_speed_string(enum usb_device_speed speed) { if (speed < 0 || speed >= ARRAY_SIZE(speed_names)) @@ -84,6 +96,14 @@ const char *usb_speed_string(enum usb_device_speed speed) } EXPORT_SYMBOL_GPL(usb_speed_string); +/** + * usb_get_maximum_speed - Get maximum requested speed for a given USB + * controller. + * @dev: Pointer to the given USB controller device + * + * The function gets the maximum speed string from property "maximum-speed", + * and returns the corresponding enum usb_device_speed. + */ enum usb_device_speed usb_get_maximum_speed(struct device *dev) { const char *maximum_speed; @@ -102,6 +122,15 @@ enum usb_device_speed usb_get_maximum_speed(struct device *dev) } EXPORT_SYMBOL_GPL(usb_get_maximum_speed); +/** + * usb_get_maximum_ssp_rate - Get the signaling rate generation and lane count + * of a SuperSpeed Plus capable device. + * @dev: Pointer to the given USB controller device + * + * If the string from "maximum-speed" property is super-speed-plus-genXxY where + * 'X' is the generation number and 'Y' is the number of lanes, then this + * function returns the corresponding enum usb_ssp_rate. + */ enum usb_ssp_rate usb_get_maximum_ssp_rate(struct device *dev) { const char *maximum_speed; @@ -116,6 +145,12 @@ enum usb_ssp_rate usb_get_maximum_ssp_rate(struct device *dev) } EXPORT_SYMBOL_GPL(usb_get_maximum_ssp_rate); +/** + * usb_state_string - Returns human readable name for the state. + * @state: The state to return a human-readable name for. If it's not + * any of the states devices in usb_device_state_string enum, + * the string UNKNOWN will be returned. + */ const char *usb_state_string(enum usb_device_state state) { static const char *const names[] = { @@ -165,6 +200,47 @@ enum usb_dr_mode usb_get_dr_mode(struct device *dev) } EXPORT_SYMBOL_GPL(usb_get_dr_mode); +/** + * usb_decode_interval - Decode bInterval into the time expressed in 1us unit + * @epd: The descriptor of the endpoint + * @speed: The speed that the endpoint works as + * + * Function returns the interval expressed in 1us unit for servicing + * endpoint for data transfers. + */ +unsigned int usb_decode_interval(const struct usb_endpoint_descriptor *epd, + enum usb_device_speed speed) +{ + unsigned int interval = 0; + + switch (usb_endpoint_type(epd)) { + case USB_ENDPOINT_XFER_CONTROL: + /* uframes per NAK */ + if (speed == USB_SPEED_HIGH) + interval = epd->bInterval; + break; + case USB_ENDPOINT_XFER_ISOC: + interval = 1 << (epd->bInterval - 1); + break; + case USB_ENDPOINT_XFER_BULK: + /* uframes per NAK */ + if (speed == USB_SPEED_HIGH && usb_endpoint_dir_out(epd)) + interval = epd->bInterval; + break; + case USB_ENDPOINT_XFER_INT: + if (speed >= USB_SPEED_HIGH) + interval = 1 << (epd->bInterval - 1); + else + interval = epd->bInterval; + break; + } + + interval *= (speed >= USB_SPEED_HIGH) ? 125 : 1000; + + return interval; +} +EXPORT_SYMBOL_GPL(usb_decode_interval); + #ifdef CONFIG_OF /** * of_usb_get_dr_mode_by_phy - Get dual role mode for the controller device diff --git a/drivers/usb/common/debug.c b/drivers/usb/common/debug.c index ba849c7bc5c7..a76a086b9c54 100644 --- a/drivers/usb/common/debug.c +++ b/drivers/usb/common/debug.c @@ -207,8 +207,26 @@ static void usb_decode_set_isoch_delay(__u8 wValue, char *str, size_t size) snprintf(str, size, "Set Isochronous Delay(Delay = %d ns)", wValue); } -/* - * usb_decode_ctrl - returns a string representation of ctrl request +/** + * usb_decode_ctrl - Returns human readable representation of control request. + * @str: buffer to return a human-readable representation of control request. + * This buffer should have about 200 bytes. + * @size: size of str buffer. + * @bRequestType: matches the USB bmRequestType field + * @bRequest: matches the USB bRequest field + * @wValue: matches the USB wValue field (CPU byte order) + * @wIndex: matches the USB wIndex field (CPU byte order) + * @wLength: matches the USB wLength field (CPU byte order) + * + * Function returns decoded, formatted and human-readable description of + * control request packet. + * + * The usage scenario for this is for tracepoints, so function as a return + * use the same value as in parameters. This approach allows to use this + * function in TP_printk + * + * Important: wValue, wIndex, wLength parameters before invoking this function + * should be processed by le16_to_cpu macro. */ const char *usb_decode_ctrl(char *str, size_t size, __u8 bRequestType, __u8 bRequest, __u16 wValue, __u16 wIndex, diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index 1ef2de6e375a..d8b0041de612 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -157,38 +157,25 @@ static char *usb_dump_endpoint_descriptor(int speed, char *start, char *end, switch (usb_endpoint_type(desc)) { case USB_ENDPOINT_XFER_CONTROL: type = "Ctrl"; - if (speed == USB_SPEED_HIGH) /* uframes per NAK */ - interval = desc->bInterval; - else - interval = 0; dir = 'B'; /* ctrl is bidirectional */ break; case USB_ENDPOINT_XFER_ISOC: type = "Isoc"; - interval = 1 << (desc->bInterval - 1); break; case USB_ENDPOINT_XFER_BULK: type = "Bulk"; - if (speed == USB_SPEED_HIGH && dir == 'O') /* uframes per NAK */ - interval = desc->bInterval; - else - interval = 0; break; case USB_ENDPOINT_XFER_INT: type = "Int."; - if (speed == USB_SPEED_HIGH || speed >= USB_SPEED_SUPER) - interval = 1 << (desc->bInterval - 1); - else - interval = desc->bInterval; break; default: /* "can't happen" */ return start; } - interval *= (speed == USB_SPEED_HIGH || - speed >= USB_SPEED_SUPER) ? 125 : 1000; - if (interval % 1000) + + interval = usb_decode_interval(desc, speed); + if (interval % 1000) { unit = 'u'; - else { + } else { unit = 'm'; interval /= 1000; } diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 4dfa44d6cc3c..072968c40ade 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -519,17 +519,13 @@ static int usb_unbind_interface(struct device *dev) * @driver: the driver to be bound * @iface: the interface to which it will be bound; must be in the * usb device's active configuration - * @priv: driver data associated with that interface + * @data: driver data associated with that interface * * This is used by usb device drivers that need to claim more than one * interface on a device when probing (audio and acm are current examples). * No device driver should directly modify internal usb_interface or * usb_device structure members. * - * Few drivers should need to use this routine, since the most natural - * way to bind to an interface is to return the private data from - * the driver's probe() method. - * * Callers must own the device lock, so driver probe() entries don't need * extra locking, but other call contexts may need to explicitly claim that * lock. @@ -537,7 +533,7 @@ static int usb_unbind_interface(struct device *dev) * Return: 0 on success. */ int usb_driver_claim_interface(struct usb_driver *driver, - struct usb_interface *iface, void *priv) + struct usb_interface *iface, void *data) { struct device *dev; int retval = 0; @@ -554,7 +550,7 @@ int usb_driver_claim_interface(struct usb_driver *driver, return -ENODEV; dev->driver = &driver->drvwrap.driver; - usb_set_intfdata(iface, priv); + usb_set_intfdata(iface, data); iface->needs_binding = 0; iface->condition = USB_INTERFACE_BOUND; diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c index 903426b6d305..a2530811cf7d 100644 --- a/drivers/usb/core/endpoint.c +++ b/drivers/usb/core/endpoint.c @@ -84,40 +84,13 @@ static ssize_t interval_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ep_device *ep = to_ep_device(dev); + unsigned int interval; char unit; - unsigned interval = 0; - unsigned in; - in = (ep->desc->bEndpointAddress & USB_DIR_IN); - - switch (usb_endpoint_type(ep->desc)) { - case USB_ENDPOINT_XFER_CONTROL: - if (ep->udev->speed == USB_SPEED_HIGH) - /* uframes per NAK */ - interval = ep->desc->bInterval; - break; - - case USB_ENDPOINT_XFER_ISOC: - interval = 1 << (ep->desc->bInterval - 1); - break; - - case USB_ENDPOINT_XFER_BULK: - if (ep->udev->speed == USB_SPEED_HIGH && !in) - /* uframes per NAK */ - interval = ep->desc->bInterval; - break; - - case USB_ENDPOINT_XFER_INT: - if (ep->udev->speed == USB_SPEED_HIGH) - interval = 1 << (ep->desc->bInterval - 1); - else - interval = ep->desc->bInterval; - break; - } - interval *= (ep->udev->speed == USB_SPEED_HIGH) ? 125 : 1000; - if (interval % 1000) + interval = usb_decode_interval(ep->desc, ep->udev->speed); + if (interval % 1000) { unit = 'u'; - else { + } else { unit = 'm'; interval /= 1000; } diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 3f0381344221..6119fb41d736 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2721,6 +2721,7 @@ int usb_add_hcd(struct usb_hcd *hcd, rhdev->rx_lanes = 1; rhdev->tx_lanes = 1; + rhdev->ssp_rate = USB_SSP_GEN_UNKNOWN; switch (hcd->speed) { case HCD_USB11: @@ -2738,8 +2739,11 @@ int usb_add_hcd(struct usb_hcd *hcd, case HCD_USB32: rhdev->rx_lanes = 2; rhdev->tx_lanes = 2; - fallthrough; + rhdev->ssp_rate = USB_SSP_GEN_2x2; + rhdev->speed = USB_SPEED_SUPER_PLUS; + break; case HCD_USB31: + rhdev->ssp_rate = USB_SSP_GEN_2x1; rhdev->speed = USB_SPEED_SUPER_PLUS; break; default: diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 7f71218cc1e5..b2bc4b7c4289 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -31,6 +31,7 @@ #include #include +#include #include #include @@ -2668,31 +2669,79 @@ int usb_authorize_device(struct usb_device *usb_dev) return result; } -/* - * Return 1 if port speed is SuperSpeedPlus, 0 otherwise - * check it from the link protocol field of the current speed ID attribute. - * current speed ID is got from ext port status request. Sublink speed attribute - * table is returned with the hub BOS SSP device capability descriptor +/** + * get_port_ssp_rate - Match the extended port status to SSP rate + * @hdev: The hub device + * @ext_portstatus: extended port status + * + * Match the extended port status speed id to the SuperSpeed Plus sublink speed + * capability attributes. Base on the number of connected lanes and speed, + * return the corresponding enum usb_ssp_rate. */ -static int port_speed_is_ssp(struct usb_device *hdev, int speed_id) +static enum usb_ssp_rate get_port_ssp_rate(struct usb_device *hdev, + u32 ext_portstatus) { - int ssa_count; - u32 ss_attr; - int i; struct usb_ssp_cap_descriptor *ssp_cap = hdev->bos->ssp_cap; + u32 attr; + u8 speed_id; + u8 ssac; + u8 lanes; + int i; if (!ssp_cap) - return 0; + goto out; - ssa_count = le32_to_cpu(ssp_cap->bmAttributes) & + speed_id = ext_portstatus & USB_EXT_PORT_STAT_RX_SPEED_ID; + lanes = USB_EXT_PORT_RX_LANES(ext_portstatus) + 1; + + ssac = le32_to_cpu(ssp_cap->bmAttributes) & USB_SSP_SUBLINK_SPEED_ATTRIBS; - for (i = 0; i <= ssa_count; i++) { - ss_attr = le32_to_cpu(ssp_cap->bmSublinkSpeedAttr[i]); - if (speed_id == (ss_attr & USB_SSP_SUBLINK_SPEED_SSID)) - return !!(ss_attr & USB_SSP_SUBLINK_SPEED_LP); + for (i = 0; i <= ssac; i++) { + u8 ssid; + + attr = le32_to_cpu(ssp_cap->bmSublinkSpeedAttr[i]); + ssid = FIELD_GET(USB_SSP_SUBLINK_SPEED_SSID, attr); + if (speed_id == ssid) { + u16 mantissa; + u8 lse; + u8 type; + + /* + * Note: currently asymmetric lane types are only + * applicable for SSIC operate in SuperSpeed protocol + */ + type = FIELD_GET(USB_SSP_SUBLINK_SPEED_ST, attr); + if (type == USB_SSP_SUBLINK_SPEED_ST_ASYM_RX || + type == USB_SSP_SUBLINK_SPEED_ST_ASYM_TX) + goto out; + + if (FIELD_GET(USB_SSP_SUBLINK_SPEED_LP, attr) != + USB_SSP_SUBLINK_SPEED_LP_SSP) + goto out; + + lse = FIELD_GET(USB_SSP_SUBLINK_SPEED_LSE, attr); + mantissa = FIELD_GET(USB_SSP_SUBLINK_SPEED_LSM, attr); + + /* Convert to Gbps */ + for (; lse < USB_SSP_SUBLINK_SPEED_LSE_GBPS; lse++) + mantissa /= 1000; + + if (mantissa >= 10 && lanes == 1) + return USB_SSP_GEN_2x1; + + if (mantissa >= 10 && lanes == 2) + return USB_SSP_GEN_2x2; + + if (mantissa >= 5 && lanes == 2) + return USB_SSP_GEN_1x2; + + goto out; + } } - return 0; + +out: + return USB_SSP_GEN_UNKNOWN; } /* Returns 1 if @hub is a WUSB root hub, 0 otherwise */ @@ -2850,15 +2899,15 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, /* extended portstatus Rx and Tx lane count are zero based */ udev->rx_lanes = USB_EXT_PORT_RX_LANES(ext_portstatus) + 1; udev->tx_lanes = USB_EXT_PORT_TX_LANES(ext_portstatus) + 1; + udev->ssp_rate = get_port_ssp_rate(hub->hdev, ext_portstatus); } else { udev->rx_lanes = 1; udev->tx_lanes = 1; + udev->ssp_rate = USB_SSP_GEN_UNKNOWN; } if (hub_is_wusb(hub)) udev->speed = USB_SPEED_WIRELESS; - else if (hub_is_superspeedplus(hub->hdev) && - port_speed_is_ssp(hub->hdev, ext_portstatus & - USB_EXT_PORT_STAT_RX_SPEED_ID)) + else if (udev->ssp_rate != USB_SSP_GEN_UNKNOWN) udev->speed = USB_SPEED_SUPER_PLUS; else if (hub_is_superspeed(hub->hdev)) udev->speed = USB_SPEED_SUPER; @@ -3556,7 +3605,7 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) u16 portchange, portstatus; if (!test_and_set_bit(port1, hub->child_usage_bits)) { - status = pm_runtime_get_sync(&port_dev->dev); + status = pm_runtime_resume_and_get(&port_dev->dev); if (status < 0) { dev_dbg(&udev->dev, "can't resume usb port, status %d\n", status); @@ -4781,9 +4830,13 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, "%s SuperSpeed%s%s USB device number %d using %s\n", (udev->config) ? "reset" : "new", (udev->speed == USB_SPEED_SUPER_PLUS) ? - "Plus Gen 2" : " Gen 1", - (udev->rx_lanes == 2 && udev->tx_lanes == 2) ? - "x2" : "", + " Plus" : "", + (udev->ssp_rate == USB_SSP_GEN_2x2) ? + " Gen 2x2" : + (udev->ssp_rate == USB_SSP_GEN_2x1) ? + " Gen 2x1" : + (udev->ssp_rate == USB_SSP_GEN_1x2) ? + " Gen 1x2" : "", devnum, driver_name); } diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h index 73f4482d833a..22ea1f4f2d66 100644 --- a/drivers/usb/core/hub.h +++ b/drivers/usb/core/hub.h @@ -148,8 +148,10 @@ static inline unsigned hub_power_on_good_delay(struct usb_hub *hub) { unsigned delay = hub->descriptor->bPwrOn2PwrGood * 2; - /* Wait at least 100 msec for power to become stable */ - return max(delay, 100U); + if (!hub->hdev->parent) /* root hub */ + return delay; + else /* Wait at least 100 msec for power to become stable */ + return max(delay, 100U); } static inline int hub_port_debounce_be_connected(struct usb_hub *hub, diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 76ac5d6555ae..21e7522655ac 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -406,6 +406,7 @@ static const struct usb_device_id usb_quirk_list[] = { /* Realtek hub in Dell WD19 (Type-C) */ { USB_DEVICE(0x0bda, 0x0487), .driver_info = USB_QUIRK_NO_LPM }, + { USB_DEVICE(0x0bda, 0x5487), .driver_info = USB_QUIRK_RESET_RESUME }, /* Generic RTL8153 based ethernet adapters */ { USB_DEVICE(0x0bda, 0x8153), .driver_info = USB_QUIRK_NO_LPM }, @@ -438,6 +439,9 @@ static const struct usb_device_id usb_quirk_list[] = { { USB_DEVICE(0x17ef, 0xa012), .driver_info = USB_QUIRK_DISCONNECT_SUSPEND }, + /* Lenovo ThinkPad USB-C Dock Gen2 Ethernet (RTL8153 GigE) */ + { USB_DEVICE(0x17ef, 0xa387), .driver_info = USB_QUIRK_NO_LPM }, + /* BUILDWIN Photo Frame */ { USB_DEVICE(0x1908, 0x1315), .driver_info = USB_QUIRK_HONOR_BNUMINTERFACES }, diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index d85699bee671..5a168ba9fc51 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -167,7 +167,10 @@ static ssize_t speed_show(struct device *dev, struct device_attribute *attr, speed = "5000"; break; case USB_SPEED_SUPER_PLUS: - speed = "10000"; + if (udev->ssp_rate == USB_SSP_GEN_2x2) + speed = "20000"; + else + speed = "10000"; break; default: speed = "unknown"; diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index a566bb494e24..62368c4ed37a 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -398,6 +398,52 @@ int usb_for_each_dev(void *data, int (*fn)(struct usb_device *, void *)) } EXPORT_SYMBOL_GPL(usb_for_each_dev); +struct each_hub_arg { + void *data; + int (*fn)(struct device *, void *); +}; + +static int __each_hub(struct usb_device *hdev, void *data) +{ + struct each_hub_arg *arg = (struct each_hub_arg *)data; + struct usb_hub *hub; + int ret = 0; + int i; + + hub = usb_hub_to_struct_hub(hdev); + if (!hub) + return 0; + + mutex_lock(&usb_port_peer_mutex); + + for (i = 0; i < hdev->maxchild; i++) { + ret = arg->fn(&hub->ports[i]->dev, arg->data); + if (ret) + break; + } + + mutex_unlock(&usb_port_peer_mutex); + + return ret; +} + +/** + * usb_for_each_port - interate over all USB ports in the system + * @data: data pointer that will be handed to the callback function + * @fn: callback function to be called for each USB port + * + * Iterate over all USB ports and call @fn for each, passing it @data. If it + * returns anything other than 0, we break the iteration prematurely and return + * that value. + */ +int usb_for_each_port(void *data, int (*fn)(struct device *, void *)) +{ + struct each_hub_arg arg = {data, fn}; + + return usb_for_each_dev(&arg, __each_hub); +} +EXPORT_SYMBOL_GPL(usb_for_each_port); + /** * usb_release_dev - free a usb device structure when all users of it are finished. * @dev: device that's been disconnected @@ -982,17 +1028,15 @@ static struct notifier_block usb_bus_nb = { .notifier_call = usb_bus_notify, }; -static struct dentry *usb_devices_root; - static void usb_debugfs_init(void) { - usb_devices_root = debugfs_create_file("devices", 0444, usb_debug_root, - NULL, &usbfs_devices_fops); + debugfs_create_file("devices", 0444, usb_debug_root, NULL, + &usbfs_devices_fops); } static void usb_debugfs_cleanup(void) { - debugfs_remove(usb_devices_root); + debugfs_remove(debugfs_lookup("devices", usb_debug_root)); } /* diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index fec17a2d2447..6f70ab9577b4 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -131,54 +131,26 @@ int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg) * dwc2_exit_partial_power_down() - Exit controller from Partial Power Down. * * @hsotg: Programming view of the DWC_otg controller + * @rem_wakeup: indicates whether resume is initiated by Reset. * @restore: Controller registers need to be restored */ -int dwc2_exit_partial_power_down(struct dwc2_hsotg *hsotg, bool restore) +int dwc2_exit_partial_power_down(struct dwc2_hsotg *hsotg, int rem_wakeup, + bool restore) { - u32 pcgcctl; - int ret = 0; + struct dwc2_gregs_backup *gr; - if (hsotg->params.power_down != DWC2_POWER_DOWN_PARAM_PARTIAL) - return -ENOTSUPP; + gr = &hsotg->gr_backup; - pcgcctl = dwc2_readl(hsotg, PCGCTL); - pcgcctl &= ~PCGCTL_STOPPCLK; - dwc2_writel(hsotg, pcgcctl, PCGCTL); - - pcgcctl = dwc2_readl(hsotg, PCGCTL); - pcgcctl &= ~PCGCTL_PWRCLMP; - dwc2_writel(hsotg, pcgcctl, PCGCTL); - - pcgcctl = dwc2_readl(hsotg, PCGCTL); - pcgcctl &= ~PCGCTL_RSTPDWNMODULE; - dwc2_writel(hsotg, pcgcctl, PCGCTL); - - udelay(100); - if (restore) { - ret = dwc2_restore_global_registers(hsotg); - if (ret) { - dev_err(hsotg->dev, "%s: failed to restore registers\n", - __func__); - return ret; - } - if (dwc2_is_host_mode(hsotg)) { - ret = dwc2_restore_host_registers(hsotg); - if (ret) { - dev_err(hsotg->dev, "%s: failed to restore host registers\n", - __func__); - return ret; - } - } else { - ret = dwc2_restore_device_registers(hsotg, 0); - if (ret) { - dev_err(hsotg->dev, "%s: failed to restore device registers\n", - __func__); - return ret; - } - } - } - - return ret; + /* + * Restore host or device regisers with the same mode core enterted + * to partial power down by checking "GOTGCTL_CURMODE_HOST" backup + * value of the "gotgctl" register. + */ + if (gr->gotgctl & GOTGCTL_CURMODE_HOST) + return dwc2_host_exit_partial_power_down(hsotg, rem_wakeup, + restore); + else + return dwc2_gadget_exit_partial_power_down(hsotg, restore); } /** @@ -188,57 +160,10 @@ int dwc2_exit_partial_power_down(struct dwc2_hsotg *hsotg, bool restore) */ int dwc2_enter_partial_power_down(struct dwc2_hsotg *hsotg) { - u32 pcgcctl; - int ret = 0; - - if (!hsotg->params.power_down) - return -ENOTSUPP; - - /* Backup all registers */ - ret = dwc2_backup_global_registers(hsotg); - if (ret) { - dev_err(hsotg->dev, "%s: failed to backup global registers\n", - __func__); - return ret; - } - - if (dwc2_is_host_mode(hsotg)) { - ret = dwc2_backup_host_registers(hsotg); - if (ret) { - dev_err(hsotg->dev, "%s: failed to backup host registers\n", - __func__); - return ret; - } - } else { - ret = dwc2_backup_device_registers(hsotg); - if (ret) { - dev_err(hsotg->dev, "%s: failed to backup device registers\n", - __func__); - return ret; - } - } - - /* - * Clear any pending interrupts since dwc2 will not be able to - * clear them after entering partial_power_down. - */ - dwc2_writel(hsotg, 0xffffffff, GINTSTS); - - /* Put the controller in low power state */ - pcgcctl = dwc2_readl(hsotg, PCGCTL); - - pcgcctl |= PCGCTL_PWRCLMP; - dwc2_writel(hsotg, pcgcctl, PCGCTL); - ndelay(20); - - pcgcctl |= PCGCTL_RSTPDWNMODULE; - dwc2_writel(hsotg, pcgcctl, PCGCTL); - ndelay(20); - - pcgcctl |= PCGCTL_STOPPCLK; - dwc2_writel(hsotg, pcgcctl, PCGCTL); - - return ret; + if (dwc2_is_host_mode(hsotg)) + return dwc2_host_enter_partial_power_down(hsotg); + else + return dwc2_gadget_enter_partial_power_down(hsotg); } /** @@ -374,6 +299,12 @@ void dwc2_hib_restore_common(struct dwc2_hsotg *hsotg, int rem_wakeup, __func__); } else { dev_dbg(hsotg->dev, "restore done generated here\n"); + + /* + * To avoid restore done interrupt storm after restore is + * generated clear GINTSTS_RESTOREDONE bit. + */ + dwc2_writel(hsotg, GINTSTS_RESTOREDONE, GINTSTS); } } @@ -460,9 +391,6 @@ static bool dwc2_iddig_filter_enabled(struct dwc2_hsotg *hsotg) */ int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg, int is_host) { - if (hsotg->params.power_down != DWC2_POWER_DOWN_PARAM_HIBERNATION) - return -ENOTSUPP; - if (is_host) return dwc2_host_enter_hibernation(hsotg); else @@ -545,6 +473,22 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait) dwc2_writel(hsotg, greset, GRSTCTL); } + /* + * Switching from device mode to host mode by disconnecting + * device cable core enters and exits form hibernation. + * However, the fifo map remains not cleared. It results + * to a WARNING (WARNING: CPU: 5 PID: 0 at drivers/usb/dwc2/ + * gadget.c:307 dwc2_hsotg_init_fifo+0x12/0x152 [dwc2]) + * if in host mode we disconnect the micro a to b host + * cable. Because core reset occurs. + * To avoid the WARNING, fifo_map should be cleared + * in dwc2_core_reset() function by taking into account configs. + * fifo_map must be cleared only if driver is configured in + * "CONFIG_USB_DWC2_PERIPHERAL" or "CONFIG_USB_DWC2_DUAL_ROLE" + * mode. + */ + dwc2_clear_fifo_map(hsotg); + /* Wait for AHB master IDLE state */ if (dwc2_hsotg_wait_bit_set(hsotg, GRSTCTL, GRSTCTL_AHBIDLE, 10000)) { dev_warn(hsotg->dev, "%s: HANG! AHB Idle timeout GRSTCTL GRSTCTL_AHBIDLE\n", diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 7161344c6522..da5ac4a4595b 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -38,6 +38,7 @@ #ifndef __DWC2_CORE_H__ #define __DWC2_CORE_H__ +#include #include #include #include @@ -426,7 +427,7 @@ enum dwc2_ep0_state { * @g_tx_fifo_size: An array of TX fifo sizes in dedicated fifo * mode. Each value corresponds to one EP * starting from EP1 (max 15 values). Sizes are - * in DWORDS with possible values from from + * in DWORDS with possible values from * 16-32768 (default: 256, 256, 256, 256, 768, * 768, 768, 768, 0, 0, 0, 0, 0, 0, 0). * @change_speed_quirk: Change speed configuration to DWC2_SPEED_PARAM_FULL @@ -865,6 +866,8 @@ struct dwc2_hregs_backup { * @gadget_enabled: Peripheral mode sub-driver initialization indicator. * @ll_hw_enabled: Status of low-level hardware resources. * @hibernated: True if core is hibernated + * @in_ppd: True if core is partial power down mode. + * @bus_suspended: True if bus is suspended * @reset_phy_on_wake: Quirk saying that we should assert PHY reset on a * remote wakeup. * @phy_off_for_suspend: Status of whether we turned the PHY off at suspend. @@ -1022,7 +1025,6 @@ struct dwc2_hregs_backup { * a pointer to an array of register definitions, the * array size and the base address where the register bank * is to be found. - * @bus_suspended: True if bus is suspended * @last_frame_num: Number of last frame. Range from 0 to 32768 * @frame_num_array: Used only if CONFIG_USB_DWC2_TRACK_MISSED_SOFS is * defined, for missed SOFs tracking. Array holds that @@ -1060,6 +1062,8 @@ struct dwc2_hsotg { unsigned int gadget_enabled:1; unsigned int ll_hw_enabled:1; unsigned int hibernated:1; + unsigned int in_ppd:1; + bool bus_suspended; unsigned int reset_phy_on_wake:1; unsigned int need_phy_for_wake:1; unsigned int phy_off_for_suspend:1; @@ -1143,7 +1147,6 @@ struct dwc2_hsotg { unsigned long hs_periodic_bitmap[ DIV_ROUND_UP(DWC2_HS_SCHEDULE_US, BITS_PER_LONG)]; u16 periodic_qh_count; - bool bus_suspended; bool new_connection; u16 last_frame_num; @@ -1301,7 +1304,8 @@ static inline bool dwc2_is_hs_iot(struct dwc2_hsotg *hsotg) */ int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait); int dwc2_enter_partial_power_down(struct dwc2_hsotg *hsotg); -int dwc2_exit_partial_power_down(struct dwc2_hsotg *hsotg, bool restore); +int dwc2_exit_partial_power_down(struct dwc2_hsotg *hsotg, int rem_wakeup, + bool restore); int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg, int is_host); int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup, int reset, int is_host); @@ -1339,6 +1343,7 @@ irqreturn_t dwc2_handle_common_intr(int irq, void *dev); /* The device ID match table */ extern const struct of_device_id dwc2_of_match_table[]; +extern const struct acpi_device_id dwc2_acpi_match[]; int dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg); int dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg); @@ -1409,11 +1414,19 @@ int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, int remote_wakeup); int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg); int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup, int reset); +int dwc2_gadget_enter_partial_power_down(struct dwc2_hsotg *hsotg); +int dwc2_gadget_exit_partial_power_down(struct dwc2_hsotg *hsotg, + bool restore); +void dwc2_gadget_enter_clock_gating(struct dwc2_hsotg *hsotg); +void dwc2_gadget_exit_clock_gating(struct dwc2_hsotg *hsotg, + int rem_wakeup); int dwc2_hsotg_tx_fifo_count(struct dwc2_hsotg *hsotg); int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg); int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg); void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg); void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg); +static inline void dwc2_clear_fifo_map(struct dwc2_hsotg *hsotg) +{ hsotg->fifo_map = 0; } #else static inline int dwc2_hsotg_remove(struct dwc2_hsotg *dwc2) { return 0; } @@ -1442,6 +1455,14 @@ static inline int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg) static inline int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup, int reset) { return 0; } +static inline int dwc2_gadget_enter_partial_power_down(struct dwc2_hsotg *hsotg) +{ return 0; } +static inline int dwc2_gadget_exit_partial_power_down(struct dwc2_hsotg *hsotg, + bool restore) +{ return 0; } +static inline void dwc2_gadget_enter_clock_gating(struct dwc2_hsotg *hsotg) {} +static inline void dwc2_gadget_exit_clock_gating(struct dwc2_hsotg *hsotg, + int rem_wakeup) {} static inline int dwc2_hsotg_tx_fifo_count(struct dwc2_hsotg *hsotg) { return 0; } static inline int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg) @@ -1450,6 +1471,7 @@ static inline int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg) { return 0; } static inline void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg) {} static inline void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg) {} +static inline void dwc2_clear_fifo_map(struct dwc2_hsotg *hsotg) {} #endif #if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) @@ -1459,11 +1481,18 @@ void dwc2_hcd_connect(struct dwc2_hsotg *hsotg); void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force); void dwc2_hcd_start(struct dwc2_hsotg *hsotg); int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup); +int dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex); +int dwc2_port_resume(struct dwc2_hsotg *hsotg); int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg); int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg); int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg); int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup, int reset); +int dwc2_host_enter_partial_power_down(struct dwc2_hsotg *hsotg); +int dwc2_host_exit_partial_power_down(struct dwc2_hsotg *hsotg, + int rem_wakeup, bool restore); +void dwc2_host_enter_clock_gating(struct dwc2_hsotg *hsotg); +void dwc2_host_exit_clock_gating(struct dwc2_hsotg *hsotg, int rem_wakeup); bool dwc2_host_can_poweroff_phy(struct dwc2_hsotg *dwc2); static inline void dwc2_host_schedule_phy_reset(struct dwc2_hsotg *hsotg) { schedule_work(&hsotg->phy_reset_work); } @@ -1479,6 +1508,10 @@ static inline void dwc2_hcd_start(struct dwc2_hsotg *hsotg) {} static inline void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) {} static inline int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup) { return 0; } +static inline int dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex) +{ return 0; } +static inline int dwc2_port_resume(struct dwc2_hsotg *hsotg) +{ return 0; } static inline int dwc2_hcd_init(struct dwc2_hsotg *hsotg) { return 0; } static inline int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg) @@ -1490,6 +1523,14 @@ static inline int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg) static inline int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup, int reset) { return 0; } +static inline int dwc2_host_enter_partial_power_down(struct dwc2_hsotg *hsotg) +{ return 0; } +static inline int dwc2_host_exit_partial_power_down(struct dwc2_hsotg *hsotg, + int rem_wakeup, bool restore) +{ return 0; } +static inline void dwc2_host_enter_clock_gating(struct dwc2_hsotg *hsotg) {} +static inline void dwc2_host_exit_clock_gating(struct dwc2_hsotg *hsotg, + int rem_wakeup) {} static inline bool dwc2_host_can_poweroff_phy(struct dwc2_hsotg *dwc2) { return false; } static inline void dwc2_host_schedule_phy_reset(struct dwc2_hsotg *hsotg) {} diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index 55f1d14fc414..a5ab03808da6 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -307,6 +307,7 @@ static void dwc2_handle_conn_id_status_change_intr(struct dwc2_hsotg *hsotg) static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg) { int ret; + u32 hprt0; /* Clear interrupt */ dwc2_writel(hsotg, GINTSTS_SESSREQINT, GINTSTS); @@ -316,10 +317,18 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg) if (dwc2_is_device_mode(hsotg)) { if (hsotg->lx_state == DWC2_L2) { - ret = dwc2_exit_partial_power_down(hsotg, true); - if (ret && (ret != -ENOTSUPP)) - dev_err(hsotg->dev, - "exit power_down failed\n"); + if (hsotg->in_ppd) { + ret = dwc2_exit_partial_power_down(hsotg, 0, + true); + if (ret) + dev_err(hsotg->dev, + "exit power_down failed\n"); + } + + /* Exit gadget mode clock gating. */ + if (hsotg->params.power_down == + DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended) + dwc2_gadget_exit_clock_gating(hsotg, 0); } /* @@ -327,6 +336,13 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg) * established */ dwc2_hsotg_disconnect(hsotg); + } else { + /* Turn on the port power bit. */ + hprt0 = dwc2_read_hprt0(hsotg); + hprt0 |= HPRT0_PWR; + dwc2_writel(hsotg, hprt0, HPRT0); + /* Connect hcd after port power is set. */ + dwc2_hcd_connect(hsotg); } } @@ -407,32 +423,40 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) dev_dbg(hsotg->dev, "DSTS=0x%0x\n", dwc2_readl(hsotg, DSTS)); if (hsotg->lx_state == DWC2_L2) { - u32 dctl = dwc2_readl(hsotg, DCTL); + if (hsotg->in_ppd) { + u32 dctl = dwc2_readl(hsotg, DCTL); + /* Clear Remote Wakeup Signaling */ + dctl &= ~DCTL_RMTWKUPSIG; + dwc2_writel(hsotg, dctl, DCTL); + ret = dwc2_exit_partial_power_down(hsotg, 1, + true); + if (ret) + dev_err(hsotg->dev, + "exit partial_power_down failed\n"); + call_gadget(hsotg, resume); + } - /* Clear Remote Wakeup Signaling */ - dctl &= ~DCTL_RMTWKUPSIG; - dwc2_writel(hsotg, dctl, DCTL); - ret = dwc2_exit_partial_power_down(hsotg, true); - if (ret && (ret != -ENOTSUPP)) - dev_err(hsotg->dev, "exit power_down failed\n"); - - /* Change to L0 state */ - hsotg->lx_state = DWC2_L0; - call_gadget(hsotg, resume); + /* Exit gadget mode clock gating. */ + if (hsotg->params.power_down == + DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended) + dwc2_gadget_exit_clock_gating(hsotg, 0); } else { /* Change to L0 state */ hsotg->lx_state = DWC2_L0; } } else { - if (hsotg->params.power_down) - return; + if (hsotg->lx_state == DWC2_L2) { + if (hsotg->in_ppd) { + ret = dwc2_exit_partial_power_down(hsotg, 1, + true); + if (ret) + dev_err(hsotg->dev, + "exit partial_power_down failed\n"); + } - if (hsotg->lx_state != DWC2_L1) { - u32 pcgcctl = dwc2_readl(hsotg, PCGCTL); - - /* Restart the Phy Clock */ - pcgcctl &= ~PCGCTL_STOPPCLK; - dwc2_writel(hsotg, pcgcctl, PCGCTL); + if (hsotg->params.power_down == + DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended) + dwc2_host_exit_clock_gating(hsotg, 1); /* * If we've got this quirk then the PHY is stuck upon @@ -508,31 +532,33 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) return; } if (dsts & DSTS_SUSPSTS) { - if (hsotg->hw_params.power_optimized) { + switch (hsotg->params.power_down) { + case DWC2_POWER_DOWN_PARAM_PARTIAL: ret = dwc2_enter_partial_power_down(hsotg); - if (ret) { - if (ret != -ENOTSUPP) - dev_err(hsotg->dev, - "%s: enter partial_power_down failed\n", - __func__); - goto skip_power_saving; - } + if (ret) + dev_err(hsotg->dev, + "enter partial_power_down failed\n"); udelay(100); /* Ask phy to be suspended */ if (!IS_ERR_OR_NULL(hsotg->uphy)) usb_phy_set_suspend(hsotg->uphy, true); + break; + case DWC2_POWER_DOWN_PARAM_HIBERNATION: + ret = dwc2_enter_hibernation(hsotg, 0); + if (ret) + dev_err(hsotg->dev, + "enter hibernation failed\n"); + break; + case DWC2_POWER_DOWN_PARAM_NONE: + /* + * If neither hibernation nor partial power down are supported, + * clock gating is used to save power. + */ + dwc2_gadget_enter_clock_gating(hsotg); } - if (hsotg->hw_params.hibernation) { - ret = dwc2_enter_hibernation(hsotg, 0); - if (ret && ret != -ENOTSUPP) - dev_err(hsotg->dev, - "%s: enter hibernation failed\n", - __func__); - } -skip_power_saving: /* * Change to L2 (suspend) state before releasing * spinlock @@ -652,16 +678,82 @@ static u32 dwc2_read_common_intr(struct dwc2_hsotg *hsotg) return 0; } +/** + * dwc_handle_gpwrdn_disc_det() - Handles the gpwrdn disconnect detect. + * Exits hibernation without restoring registers. + * + * @hsotg: Programming view of DWC_otg controller + * @gpwrdn: GPWRDN register + */ +static inline void dwc_handle_gpwrdn_disc_det(struct dwc2_hsotg *hsotg, + u32 gpwrdn) +{ + u32 gpwrdn_tmp; + + /* Switch-on voltage to the core */ + gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); + gpwrdn_tmp &= ~GPWRDN_PWRDNSWTCH; + dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); + udelay(5); + + /* Reset core */ + gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); + gpwrdn_tmp &= ~GPWRDN_PWRDNRSTN; + dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); + udelay(5); + + /* Disable Power Down Clamp */ + gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); + gpwrdn_tmp &= ~GPWRDN_PWRDNCLMP; + dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); + udelay(5); + + /* Deassert reset core */ + gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); + gpwrdn_tmp |= GPWRDN_PWRDNRSTN; + dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); + udelay(5); + + /* Disable PMU interrupt */ + gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); + gpwrdn_tmp &= ~GPWRDN_PMUINTSEL; + dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); + + /* De-assert Wakeup Logic */ + gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); + gpwrdn_tmp &= ~GPWRDN_PMUACTV; + dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); + + hsotg->hibernated = 0; + hsotg->bus_suspended = 0; + + if (gpwrdn & GPWRDN_IDSTS) { + hsotg->op_state = OTG_STATE_B_PERIPHERAL; + dwc2_core_init(hsotg, false); + dwc2_enable_global_interrupts(hsotg); + dwc2_hsotg_core_init_disconnected(hsotg, false); + dwc2_hsotg_core_connect(hsotg); + } else { + hsotg->op_state = OTG_STATE_A_HOST; + + /* Initialize the Core for Host mode */ + dwc2_core_init(hsotg, false); + dwc2_enable_global_interrupts(hsotg); + dwc2_hcd_start(hsotg); + } +} + /* * GPWRDN interrupt handler. * * The GPWRDN interrupts are those that occur in both Host and * Device mode while core is in hibernated state. */ -static void dwc2_handle_gpwrdn_intr(struct dwc2_hsotg *hsotg) +static int dwc2_handle_gpwrdn_intr(struct dwc2_hsotg *hsotg) { u32 gpwrdn; int linestate; + int ret = 0; gpwrdn = dwc2_readl(hsotg, GPWRDN); /* clear all interrupt */ @@ -673,93 +765,52 @@ static void dwc2_handle_gpwrdn_intr(struct dwc2_hsotg *hsotg) if ((gpwrdn & GPWRDN_DISCONN_DET) && (gpwrdn & GPWRDN_DISCONN_DET_MSK) && !linestate) { - u32 gpwrdn_tmp; - dev_dbg(hsotg->dev, "%s: GPWRDN_DISCONN_DET\n", __func__); - - /* Switch-on voltage to the core */ - gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); - gpwrdn_tmp &= ~GPWRDN_PWRDNSWTCH; - dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); - udelay(10); - - /* Reset core */ - gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); - gpwrdn_tmp &= ~GPWRDN_PWRDNRSTN; - dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); - udelay(10); - - /* Disable Power Down Clamp */ - gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); - gpwrdn_tmp &= ~GPWRDN_PWRDNCLMP; - dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); - udelay(10); - - /* Deassert reset core */ - gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); - gpwrdn_tmp |= GPWRDN_PWRDNRSTN; - dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); - udelay(10); - - /* Disable PMU interrupt */ - gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); - gpwrdn_tmp &= ~GPWRDN_PMUINTSEL; - dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); - - /* De-assert Wakeup Logic */ - gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); - gpwrdn_tmp &= ~GPWRDN_PMUACTV; - dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); - - hsotg->hibernated = 0; - - if (gpwrdn & GPWRDN_IDSTS) { - hsotg->op_state = OTG_STATE_B_PERIPHERAL; - dwc2_core_init(hsotg, false); - dwc2_enable_global_interrupts(hsotg); - dwc2_hsotg_core_init_disconnected(hsotg, false); - dwc2_hsotg_core_connect(hsotg); - } else { - hsotg->op_state = OTG_STATE_A_HOST; - - /* Initialize the Core for Host mode */ - dwc2_core_init(hsotg, false); - dwc2_enable_global_interrupts(hsotg); - dwc2_hcd_start(hsotg); - } - } - - if ((gpwrdn & GPWRDN_LNSTSCHG) && - (gpwrdn & GPWRDN_LNSTSCHG_MSK) && linestate) { + /* + * Call disconnect detect function to exit from + * hibernation + */ + dwc_handle_gpwrdn_disc_det(hsotg, gpwrdn); + } else if ((gpwrdn & GPWRDN_LNSTSCHG) && + (gpwrdn & GPWRDN_LNSTSCHG_MSK) && linestate) { dev_dbg(hsotg->dev, "%s: GPWRDN_LNSTSCHG\n", __func__); if (hsotg->hw_params.hibernation && hsotg->hibernated) { if (gpwrdn & GPWRDN_IDSTS) { - dwc2_exit_hibernation(hsotg, 0, 0, 0); + ret = dwc2_exit_hibernation(hsotg, 0, 0, 0); + if (ret) + dev_err(hsotg->dev, + "exit hibernation failed.\n"); call_gadget(hsotg, resume); } else { - dwc2_exit_hibernation(hsotg, 1, 0, 1); + ret = dwc2_exit_hibernation(hsotg, 1, 0, 1); + if (ret) + dev_err(hsotg->dev, + "exit hibernation failed.\n"); } } - } - if ((gpwrdn & GPWRDN_RST_DET) && (gpwrdn & GPWRDN_RST_DET_MSK)) { + } else if ((gpwrdn & GPWRDN_RST_DET) && + (gpwrdn & GPWRDN_RST_DET_MSK)) { dev_dbg(hsotg->dev, "%s: GPWRDN_RST_DET\n", __func__); - if (!linestate && (gpwrdn & GPWRDN_BSESSVLD)) - dwc2_exit_hibernation(hsotg, 0, 1, 0); - } - if ((gpwrdn & GPWRDN_STS_CHGINT) && - (gpwrdn & GPWRDN_STS_CHGINT_MSK) && linestate) { - dev_dbg(hsotg->dev, "%s: GPWRDN_STS_CHGINT\n", __func__); - if (hsotg->hw_params.hibernation && - hsotg->hibernated) { - if (gpwrdn & GPWRDN_IDSTS) { - dwc2_exit_hibernation(hsotg, 0, 0, 0); - call_gadget(hsotg, resume); - } else { - dwc2_exit_hibernation(hsotg, 1, 0, 1); - } + if (!linestate) { + ret = dwc2_exit_hibernation(hsotg, 0, 1, 0); + if (ret) + dev_err(hsotg->dev, + "exit hibernation failed.\n"); } + } else if ((gpwrdn & GPWRDN_STS_CHGINT) && + (gpwrdn & GPWRDN_STS_CHGINT_MSK)) { + dev_dbg(hsotg->dev, "%s: GPWRDN_STS_CHGINT\n", __func__); + /* + * As GPWRDN_STS_CHGINT exit from hibernation flow is + * the same as in GPWRDN_DISCONN_DET flow. Call + * disconnect detect helper function to exit from + * hibernation. + */ + dwc_handle_gpwrdn_disc_det(hsotg, gpwrdn); } + + return ret; } /* diff --git a/drivers/usb/dwc2/debugfs.c b/drivers/usb/dwc2/debugfs.c index aaafd463d72a..f13eed4231e1 100644 --- a/drivers/usb/dwc2/debugfs.c +++ b/drivers/usb/dwc2/debugfs.c @@ -691,6 +691,8 @@ static int params_show(struct seq_file *seq, void *v) print_param(seq, p, ulpi_fs_ls); print_param(seq, p, host_support_fs_ls_low_power); print_param(seq, p, host_ls_low_power_phy_clk); + print_param(seq, p, activate_stm_fs_transceiver); + print_param(seq, p, activate_stm_id_vb_detection); print_param(seq, p, ts_dline); print_param(seq, p, reload_ctl); print_param_hex(seq, p, ahbcfg); diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index ad4c94366dad..e6bb1bdb2760 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -3689,10 +3689,10 @@ static irqreturn_t dwc2_hsotg_irq(int irq, void *pw) dwc2_writel(hsotg, GINTSTS_RESETDET, GINTSTS); /* This event must be used only if controller is suspended */ - if (hsotg->lx_state == DWC2_L2) { - dwc2_exit_partial_power_down(hsotg, true); - hsotg->lx_state = DWC2_L0; - } + if (hsotg->in_ppd && hsotg->lx_state == DWC2_L2) + dwc2_exit_partial_power_down(hsotg, 0, true); + + hsotg->lx_state = DWC2_L0; } if (gintsts & (GINTSTS_USBRST | GINTSTS_RESETDET)) { @@ -4615,11 +4615,15 @@ static int dwc2_hsotg_vbus_session(struct usb_gadget *gadget, int is_active) spin_lock_irqsave(&hsotg->lock, flags); /* - * If controller is hibernated, it must exit from power_down - * before being initialized / de-initialized + * If controller is in partial power down state, it must exit from + * that state before being initialized / de-initialized */ - if (hsotg->lx_state == DWC2_L2) - dwc2_exit_partial_power_down(hsotg, false); + if (hsotg->lx_state == DWC2_L2 && hsotg->in_ppd) + /* + * No need to check the return value as + * registers are not being restored. + */ + dwc2_exit_partial_power_down(hsotg, 0, false); if (is_active) { hsotg->op_state = OTG_STATE_B_PERIPHERAL; @@ -5301,6 +5305,10 @@ int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg, dwc2_writel(hsotg, dr->dcfg, DCFG); dwc2_writel(hsotg, dr->dctl, DCTL); + /* On USB Reset, reset device address to zero */ + if (reset) + dwc2_clear_bit(hsotg, DCFG, DCFG_DEVADDR_MASK); + /* De-assert Wakeup Logic */ gpwrdn = dwc2_readl(hsotg, GPWRDN); gpwrdn &= ~GPWRDN_PMUACTV; @@ -5351,3 +5359,202 @@ int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg, return ret; } + +/** + * dwc2_gadget_enter_partial_power_down() - Put controller in partial + * power down. + * + * @hsotg: Programming view of the DWC_otg controller + * + * Return: non-zero if failed to enter device partial power down. + * + * This function is for entering device mode partial power down. + */ +int dwc2_gadget_enter_partial_power_down(struct dwc2_hsotg *hsotg) +{ + u32 pcgcctl; + int ret = 0; + + dev_dbg(hsotg->dev, "Entering device partial power down started.\n"); + + /* Backup all registers */ + ret = dwc2_backup_global_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to backup global registers\n", + __func__); + return ret; + } + + ret = dwc2_backup_device_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to backup device registers\n", + __func__); + return ret; + } + + /* + * Clear any pending interrupts since dwc2 will not be able to + * clear them after entering partial_power_down. + */ + dwc2_writel(hsotg, 0xffffffff, GINTSTS); + + /* Put the controller in low power state */ + pcgcctl = dwc2_readl(hsotg, PCGCTL); + + pcgcctl |= PCGCTL_PWRCLMP; + dwc2_writel(hsotg, pcgcctl, PCGCTL); + udelay(5); + + pcgcctl |= PCGCTL_RSTPDWNMODULE; + dwc2_writel(hsotg, pcgcctl, PCGCTL); + udelay(5); + + pcgcctl |= PCGCTL_STOPPCLK; + dwc2_writel(hsotg, pcgcctl, PCGCTL); + + /* Set in_ppd flag to 1 as here core enters suspend. */ + hsotg->in_ppd = 1; + hsotg->lx_state = DWC2_L2; + + dev_dbg(hsotg->dev, "Entering device partial power down completed.\n"); + + return ret; +} + +/* + * dwc2_gadget_exit_partial_power_down() - Exit controller from device partial + * power down. + * + * @hsotg: Programming view of the DWC_otg controller + * @restore: indicates whether need to restore the registers or not. + * + * Return: non-zero if failed to exit device partial power down. + * + * This function is for exiting from device mode partial power down. + */ +int dwc2_gadget_exit_partial_power_down(struct dwc2_hsotg *hsotg, + bool restore) +{ + u32 pcgcctl; + u32 dctl; + struct dwc2_dregs_backup *dr; + int ret = 0; + + dr = &hsotg->dr_backup; + + dev_dbg(hsotg->dev, "Exiting device partial Power Down started.\n"); + + pcgcctl = dwc2_readl(hsotg, PCGCTL); + pcgcctl &= ~PCGCTL_STOPPCLK; + dwc2_writel(hsotg, pcgcctl, PCGCTL); + + pcgcctl = dwc2_readl(hsotg, PCGCTL); + pcgcctl &= ~PCGCTL_PWRCLMP; + dwc2_writel(hsotg, pcgcctl, PCGCTL); + + pcgcctl = dwc2_readl(hsotg, PCGCTL); + pcgcctl &= ~PCGCTL_RSTPDWNMODULE; + dwc2_writel(hsotg, pcgcctl, PCGCTL); + + udelay(100); + if (restore) { + ret = dwc2_restore_global_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to restore registers\n", + __func__); + return ret; + } + /* Restore DCFG */ + dwc2_writel(hsotg, dr->dcfg, DCFG); + + ret = dwc2_restore_device_registers(hsotg, 0); + if (ret) { + dev_err(hsotg->dev, "%s: failed to restore device registers\n", + __func__); + return ret; + } + } + + /* Set the Power-On Programming done bit */ + dctl = dwc2_readl(hsotg, DCTL); + dctl |= DCTL_PWRONPRGDONE; + dwc2_writel(hsotg, dctl, DCTL); + + /* Set in_ppd flag to 0 as here core exits from suspend. */ + hsotg->in_ppd = 0; + hsotg->lx_state = DWC2_L0; + + dev_dbg(hsotg->dev, "Exiting device partial Power Down completed.\n"); + return ret; +} + +/** + * dwc2_gadget_enter_clock_gating() - Put controller in clock gating. + * + * @hsotg: Programming view of the DWC_otg controller + * + * Return: non-zero if failed to enter device partial power down. + * + * This function is for entering device mode clock gating. + */ +void dwc2_gadget_enter_clock_gating(struct dwc2_hsotg *hsotg) +{ + u32 pcgctl; + + dev_dbg(hsotg->dev, "Entering device clock gating.\n"); + + /* Set the Phy Clock bit as suspend is received. */ + pcgctl = dwc2_readl(hsotg, PCGCTL); + pcgctl |= PCGCTL_STOPPCLK; + dwc2_writel(hsotg, pcgctl, PCGCTL); + udelay(5); + + /* Set the Gate hclk as suspend is received. */ + pcgctl = dwc2_readl(hsotg, PCGCTL); + pcgctl |= PCGCTL_GATEHCLK; + dwc2_writel(hsotg, pcgctl, PCGCTL); + udelay(5); + + hsotg->lx_state = DWC2_L2; + hsotg->bus_suspended = true; +} + +/* + * dwc2_gadget_exit_clock_gating() - Exit controller from device clock gating. + * + * @hsotg: Programming view of the DWC_otg controller + * @rem_wakeup: indicates whether remote wake up is enabled. + * + * This function is for exiting from device mode clock gating. + */ +void dwc2_gadget_exit_clock_gating(struct dwc2_hsotg *hsotg, int rem_wakeup) +{ + u32 pcgctl; + u32 dctl; + + dev_dbg(hsotg->dev, "Exiting device clock gating.\n"); + + /* Clear the Gate hclk. */ + pcgctl = dwc2_readl(hsotg, PCGCTL); + pcgctl &= ~PCGCTL_GATEHCLK; + dwc2_writel(hsotg, pcgctl, PCGCTL); + udelay(5); + + /* Phy Clock bit. */ + pcgctl = dwc2_readl(hsotg, PCGCTL); + pcgctl &= ~PCGCTL_STOPPCLK; + dwc2_writel(hsotg, pcgctl, PCGCTL); + udelay(5); + + if (rem_wakeup) { + /* Set Remote Wakeup Signaling */ + dctl = dwc2_readl(hsotg, DCTL); + dctl |= DCTL_RMTWKUPSIG; + dwc2_writel(hsotg, dctl, DCTL); + } + + /* Change to L0 state */ + call_gadget(hsotg, resume); + hsotg->lx_state = DWC2_L0; + hsotg->bus_suspended = false; +} diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 1a9789ec5847..035d4911a3c3 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -56,8 +56,6 @@ #include "core.h" #include "hcd.h" -static void dwc2_port_resume(struct dwc2_hsotg *hsotg); - /* * ========================================================================= * Host Core Layer Functions @@ -3208,6 +3206,15 @@ static void dwc2_conn_id_status_change(struct work_struct *work) if (count > 250) dev_err(hsotg->dev, "Connection id status change timed out\n"); + + /* + * Exit Partial Power Down without restoring registers. + * No need to check the return value as registers + * are not being restored. + */ + if (hsotg->in_ppd && hsotg->lx_state == DWC2_L2) + dwc2_exit_partial_power_down(hsotg, 0, false); + hsotg->op_state = OTG_STATE_B_PERIPHERAL; dwc2_core_init(hsotg, false); dwc2_enable_global_interrupts(hsotg); @@ -3277,13 +3284,23 @@ static int dwc2_host_is_b_hnp_enabled(struct dwc2_hsotg *hsotg) return hcd->self.b_hnp_enable; } -/* Must NOT be called with interrupt disabled or spinlock held */ -static void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex) +/** + * dwc2_port_suspend() - Put controller in suspend mode for host. + * + * @hsotg: Programming view of the DWC_otg controller + * @windex: The control request wIndex field + * + * Return: non-zero if failed to enter suspend mode for host. + * + * This function is for entering Host mode suspend. + * Must NOT be called with interrupt disabled or spinlock held. + */ +int dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex) { unsigned long flags; - u32 hprt0; u32 pcgctl; u32 gotgctl; + int ret = 0; dev_dbg(hsotg->dev, "%s()\n", __func__); @@ -3296,22 +3313,33 @@ static void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex) hsotg->op_state = OTG_STATE_A_SUSPEND; } - hprt0 = dwc2_read_hprt0(hsotg); - hprt0 |= HPRT0_SUSP; - dwc2_writel(hsotg, hprt0, HPRT0); - - hsotg->bus_suspended = true; - - /* - * If power_down is supported, Phy clock will be suspended - * after registers are backuped. - */ - if (!hsotg->params.power_down) { - /* Suspend the Phy Clock */ - pcgctl = dwc2_readl(hsotg, PCGCTL); - pcgctl |= PCGCTL_STOPPCLK; - dwc2_writel(hsotg, pcgctl, PCGCTL); - udelay(10); + switch (hsotg->params.power_down) { + case DWC2_POWER_DOWN_PARAM_PARTIAL: + ret = dwc2_enter_partial_power_down(hsotg); + if (ret) + dev_err(hsotg->dev, + "enter partial_power_down failed.\n"); + break; + case DWC2_POWER_DOWN_PARAM_HIBERNATION: + /* + * Perform spin unlock and lock because in + * "dwc2_host_enter_hibernation()" function there is a spinlock + * logic which prevents servicing of any IRQ during entering + * hibernation. + */ + spin_unlock_irqrestore(&hsotg->lock, flags); + ret = dwc2_enter_hibernation(hsotg, 1); + if (ret) + dev_err(hsotg->dev, "enter hibernation failed.\n"); + spin_lock_irqsave(&hsotg->lock, flags); + break; + case DWC2_POWER_DOWN_PARAM_NONE: + /* + * If not hibernation nor partial power down are supported, + * clock gating is used to save power. + */ + dwc2_host_enter_clock_gating(hsotg); + break; } /* For HNP the bus must be suspended for at least 200ms */ @@ -3326,44 +3354,54 @@ static void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex) } else { spin_unlock_irqrestore(&hsotg->lock, flags); } + + return ret; } -/* Must NOT be called with interrupt disabled or spinlock held */ -static void dwc2_port_resume(struct dwc2_hsotg *hsotg) +/** + * dwc2_port_resume() - Exit controller from suspend mode for host. + * + * @hsotg: Programming view of the DWC_otg controller + * + * Return: non-zero if failed to exit suspend mode for host. + * + * This function is for exiting Host mode suspend. + * Must NOT be called with interrupt disabled or spinlock held. + */ +int dwc2_port_resume(struct dwc2_hsotg *hsotg) { unsigned long flags; - u32 hprt0; - u32 pcgctl; + int ret = 0; spin_lock_irqsave(&hsotg->lock, flags); - /* - * If power_down is supported, Phy clock is already resumed - * after registers restore. - */ - if (!hsotg->params.power_down) { - pcgctl = dwc2_readl(hsotg, PCGCTL); - pcgctl &= ~PCGCTL_STOPPCLK; - dwc2_writel(hsotg, pcgctl, PCGCTL); + switch (hsotg->params.power_down) { + case DWC2_POWER_DOWN_PARAM_PARTIAL: + ret = dwc2_exit_partial_power_down(hsotg, 0, true); + if (ret) + dev_err(hsotg->dev, + "exit partial_power_down failed.\n"); + break; + case DWC2_POWER_DOWN_PARAM_HIBERNATION: + /* Exit host hibernation. */ + ret = dwc2_exit_hibernation(hsotg, 0, 0, 1); + if (ret) + dev_err(hsotg->dev, "exit hibernation failed.\n"); + break; + case DWC2_POWER_DOWN_PARAM_NONE: + /* + * If not hibernation nor partial power down are supported, + * port resume is done using the clock gating programming flow. + */ spin_unlock_irqrestore(&hsotg->lock, flags); - msleep(20); + dwc2_host_exit_clock_gating(hsotg, 0); spin_lock_irqsave(&hsotg->lock, flags); + break; } - hprt0 = dwc2_read_hprt0(hsotg); - hprt0 |= HPRT0_RES; - hprt0 &= ~HPRT0_SUSP; - dwc2_writel(hsotg, hprt0, HPRT0); spin_unlock_irqrestore(&hsotg->lock, flags); - msleep(USB_RESUME_TIMEOUT); - - spin_lock_irqsave(&hsotg->lock, flags); - hprt0 = dwc2_read_hprt0(hsotg); - hprt0 &= ~(HPRT0_RES | HPRT0_SUSP); - dwc2_writel(hsotg, hprt0, HPRT0); - hsotg->bus_suspended = false; - spin_unlock_irqrestore(&hsotg->lock, flags); + return ret; } /* Handles hub class-specific requests */ @@ -3413,12 +3451,8 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, dev_dbg(hsotg->dev, "ClearPortFeature USB_PORT_FEAT_SUSPEND\n"); - if (hsotg->bus_suspended) { - if (hsotg->hibernated) - dwc2_exit_hibernation(hsotg, 0, 0, 1); - else - dwc2_port_resume(hsotg); - } + if (hsotg->bus_suspended) + retval = dwc2_port_resume(hsotg); break; case USB_PORT_FEAT_POWER: @@ -3629,10 +3663,8 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, "SetPortFeature - USB_PORT_FEAT_SUSPEND\n"); if (windex != hsotg->otg_port) goto error; - if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_HIBERNATION) - dwc2_enter_hibernation(hsotg, 1); - else - dwc2_port_suspend(hsotg, windex); + if (!hsotg->bus_suspended) + retval = dwc2_port_suspend(hsotg, windex); break; case USB_PORT_FEAT_POWER: @@ -3647,12 +3679,30 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, break; case USB_PORT_FEAT_RESET: - if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_HIBERNATION && - hsotg->hibernated) - dwc2_exit_hibernation(hsotg, 0, 1, 1); - hprt0 = dwc2_read_hprt0(hsotg); dev_dbg(hsotg->dev, "SetPortFeature - USB_PORT_FEAT_RESET\n"); + + hprt0 = dwc2_read_hprt0(hsotg); + + if (hsotg->hibernated) { + retval = dwc2_exit_hibernation(hsotg, 0, 1, 1); + if (retval) + dev_err(hsotg->dev, + "exit hibernation failed\n"); + } + + if (hsotg->in_ppd) { + retval = dwc2_exit_partial_power_down(hsotg, 1, + true); + if (retval) + dev_err(hsotg->dev, + "exit partial_power_down failed\n"); + } + + if (hsotg->params.power_down == + DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended) + dwc2_host_exit_clock_gating(hsotg, 0); + pcgctl = dwc2_readl(hsotg, PCGCTL); pcgctl &= ~(PCGCTL_ENBL_SLEEP_GATING | PCGCTL_STOPPCLK); dwc2_writel(hsotg, pcgctl, PCGCTL); @@ -4305,8 +4355,6 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd) struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); unsigned long flags; int ret = 0; - u32 hprt0; - u32 pcgctl; spin_lock_irqsave(&hsotg->lock, flags); @@ -4322,47 +4370,51 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd) if (hsotg->op_state == OTG_STATE_B_PERIPHERAL) goto unlock; - if (hsotg->params.power_down != DWC2_POWER_DOWN_PARAM_PARTIAL || - hsotg->flags.b.port_connect_status == 0) + if (hsotg->bus_suspended) goto skip_power_saving; - /* - * Drive USB suspend and disable port Power - * if usb bus is not suspended. - */ - if (!hsotg->bus_suspended) { - hprt0 = dwc2_read_hprt0(hsotg); - if (hprt0 & HPRT0_CONNSTS) { - hprt0 |= HPRT0_SUSP; - if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_PARTIAL) - hprt0 &= ~HPRT0_PWR; - dwc2_writel(hsotg, hprt0, HPRT0); - } - if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_PARTIAL) { - spin_unlock_irqrestore(&hsotg->lock, flags); - dwc2_vbus_supply_exit(hsotg); - spin_lock_irqsave(&hsotg->lock, flags); - } else { - pcgctl = readl(hsotg->regs + PCGCTL); - pcgctl |= PCGCTL_STOPPCLK; - writel(pcgctl, hsotg->regs + PCGCTL); - } - } + if (hsotg->flags.b.port_connect_status == 0) + goto skip_power_saving; - if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_PARTIAL) { + switch (hsotg->params.power_down) { + case DWC2_POWER_DOWN_PARAM_PARTIAL: /* Enter partial_power_down */ ret = dwc2_enter_partial_power_down(hsotg); - if (ret) { - if (ret != -ENOTSUPP) - dev_err(hsotg->dev, - "enter partial_power_down failed\n"); - goto skip_power_saving; - } - - /* After entering partial_power_down, hardware is no more accessible */ + if (ret) + dev_err(hsotg->dev, + "enter partial_power_down failed\n"); + /* After entering suspend, hardware is not accessible */ clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + break; + case DWC2_POWER_DOWN_PARAM_HIBERNATION: + /* Enter hibernation */ + spin_unlock_irqrestore(&hsotg->lock, flags); + ret = dwc2_enter_hibernation(hsotg, 1); + if (ret) + dev_err(hsotg->dev, "enter hibernation failed\n"); + spin_lock_irqsave(&hsotg->lock, flags); + + /* After entering suspend, hardware is not accessible */ + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + break; + case DWC2_POWER_DOWN_PARAM_NONE: + /* + * If not hibernation nor partial power down are supported, + * clock gating is used to save power. + */ + dwc2_host_enter_clock_gating(hsotg); + + /* After entering suspend, hardware is not accessible */ + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + break; + default: + goto skip_power_saving; } + spin_unlock_irqrestore(&hsotg->lock, flags); + dwc2_vbus_supply_exit(hsotg); + spin_lock_irqsave(&hsotg->lock, flags); + /* Ask phy to be suspended */ if (!IS_ERR_OR_NULL(hsotg->uphy)) { spin_unlock_irqrestore(&hsotg->lock, flags); @@ -4382,7 +4434,7 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) { struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); unsigned long flags; - u32 pcgctl; + u32 hprt0; int ret = 0; spin_lock_irqsave(&hsotg->lock, flags); @@ -4393,11 +4445,72 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) if (hsotg->lx_state != DWC2_L2) goto unlock; - if (hsotg->params.power_down > DWC2_POWER_DOWN_PARAM_PARTIAL) { + hprt0 = dwc2_read_hprt0(hsotg); + + /* + * Added port connection status checking which prevents exiting from + * Partial Power Down mode from _dwc2_hcd_resume() if not in Partial + * Power Down mode. + */ + if (hprt0 & HPRT0_CONNSTS) { hsotg->lx_state = DWC2_L0; goto unlock; } + switch (hsotg->params.power_down) { + case DWC2_POWER_DOWN_PARAM_PARTIAL: + ret = dwc2_exit_partial_power_down(hsotg, 0, true); + if (ret) + dev_err(hsotg->dev, + "exit partial_power_down failed\n"); + /* + * Set HW accessible bit before powering on the controller + * since an interrupt may rise. + */ + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + break; + case DWC2_POWER_DOWN_PARAM_HIBERNATION: + ret = dwc2_exit_hibernation(hsotg, 0, 0, 1); + if (ret) + dev_err(hsotg->dev, "exit hibernation failed.\n"); + + /* + * Set HW accessible bit before powering on the controller + * since an interrupt may rise. + */ + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + break; + case DWC2_POWER_DOWN_PARAM_NONE: + /* + * If not hibernation nor partial power down are supported, + * port resume is done using the clock gating programming flow. + */ + spin_unlock_irqrestore(&hsotg->lock, flags); + dwc2_host_exit_clock_gating(hsotg, 0); + + /* + * Initialize the Core for Host mode, as after system resume + * the global interrupts are disabled. + */ + dwc2_core_init(hsotg, false); + dwc2_enable_global_interrupts(hsotg); + dwc2_hcd_reinit(hsotg); + spin_lock_irqsave(&hsotg->lock, flags); + + /* + * Set HW accessible bit before powering on the controller + * since an interrupt may rise. + */ + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + break; + default: + hsotg->lx_state = DWC2_L0; + goto unlock; + } + + /* Change Root port status, as port status change occurred after resume.*/ + hsotg->flags.b.port_suspend_change = 1; + /* * Enable power if not already done. * This must not be spinlocked since duration @@ -4409,52 +4522,25 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) spin_lock_irqsave(&hsotg->lock, flags); } - if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_PARTIAL) { - /* - * Set HW accessible bit before powering on the controller - * since an interrupt may rise. - */ - set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - - - /* Exit partial_power_down */ - ret = dwc2_exit_partial_power_down(hsotg, true); - if (ret && (ret != -ENOTSUPP)) - dev_err(hsotg->dev, "exit partial_power_down failed\n"); - } else { - pcgctl = readl(hsotg->regs + PCGCTL); - pcgctl &= ~PCGCTL_STOPPCLK; - writel(pcgctl, hsotg->regs + PCGCTL); - } - - hsotg->lx_state = DWC2_L0; - + /* Enable external vbus supply after resuming the port. */ spin_unlock_irqrestore(&hsotg->lock, flags); + dwc2_vbus_supply_init(hsotg); - if (hsotg->bus_suspended) { - spin_lock_irqsave(&hsotg->lock, flags); - hsotg->flags.b.port_suspend_change = 1; - spin_unlock_irqrestore(&hsotg->lock, flags); - dwc2_port_resume(hsotg); - } else { - if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_PARTIAL) { - dwc2_vbus_supply_init(hsotg); + /* Wait for controller to correctly update D+/D- level */ + usleep_range(3000, 5000); + spin_lock_irqsave(&hsotg->lock, flags); - /* Wait for controller to correctly update D+/D- level */ - usleep_range(3000, 5000); - } + /* + * Clear Port Enable and Port Status changes. + * Enable Port Power. + */ + dwc2_writel(hsotg, HPRT0_PWR | HPRT0_CONNDET | + HPRT0_ENACHG, HPRT0); - /* - * Clear Port Enable and Port Status changes. - * Enable Port Power. - */ - dwc2_writel(hsotg, HPRT0_PWR | HPRT0_CONNDET | - HPRT0_ENACHG, HPRT0); - /* Wait for controller to detect Port Connect */ - usleep_range(5000, 7000); - } - - return ret; + /* Wait for controller to detect Port Connect */ + spin_unlock_irqrestore(&hsotg->lock, flags); + usleep_range(5000, 7000); + spin_lock_irqsave(&hsotg->lock, flags); unlock: spin_unlock_irqrestore(&hsotg->lock, flags); @@ -4565,12 +4651,41 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, struct dwc2_qh *qh; bool qh_allocated = false; struct dwc2_qtd *qtd; + struct dwc2_gregs_backup *gr; + + gr = &hsotg->gr_backup; if (dbg_urb(urb)) { dev_vdbg(hsotg->dev, "DWC OTG HCD URB Enqueue\n"); dwc2_dump_urb_info(hcd, urb, "urb_enqueue"); } + if (hsotg->hibernated) { + if (gr->gotgctl & GOTGCTL_CURMODE_HOST) + retval = dwc2_exit_hibernation(hsotg, 0, 0, 1); + else + retval = dwc2_exit_hibernation(hsotg, 0, 0, 0); + + if (retval) + dev_err(hsotg->dev, + "exit hibernation failed.\n"); + } + + if (hsotg->in_ppd) { + retval = dwc2_exit_partial_power_down(hsotg, 0, true); + if (retval) + dev_err(hsotg->dev, + "exit partial_power_down failed\n"); + } + + if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_NONE && + hsotg->bus_suspended) { + if (dwc2_is_device_mode(hsotg)) + dwc2_gadget_exit_clock_gating(hsotg, 0); + else + dwc2_host_exit_clock_gating(hsotg, 0); + } + if (!ep) return -EINVAL; @@ -5580,7 +5695,15 @@ int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup, return ret; } - dwc2_hcd_rem_wakeup(hsotg); + if (rem_wakeup) { + dwc2_hcd_rem_wakeup(hsotg); + /* + * Change "port_connect_status_change" flag to re-enumerate, + * because after exit from hibernation port connection status + * is not detected. + */ + hsotg->flags.b.port_connect_status_change = 1; + } hsotg->hibernated = 0; hsotg->bus_suspended = 0; @@ -5607,3 +5730,249 @@ bool dwc2_host_can_poweroff_phy(struct dwc2_hsotg *dwc2) /* No reason to keep the PHY powered, so allow poweroff */ return true; } + +/** + * dwc2_host_enter_partial_power_down() - Put controller in partial + * power down. + * + * @hsotg: Programming view of the DWC_otg controller + * + * Return: non-zero if failed to enter host partial power down. + * + * This function is for entering Host mode partial power down. + */ +int dwc2_host_enter_partial_power_down(struct dwc2_hsotg *hsotg) +{ + u32 pcgcctl; + u32 hprt0; + int ret = 0; + + dev_dbg(hsotg->dev, "Entering host partial power down started.\n"); + + /* Put this port in suspend mode. */ + hprt0 = dwc2_read_hprt0(hsotg); + hprt0 |= HPRT0_SUSP; + dwc2_writel(hsotg, hprt0, HPRT0); + udelay(5); + + /* Wait for the HPRT0.PrtSusp register field to be set */ + if (dwc2_hsotg_wait_bit_set(hsotg, HPRT0, HPRT0_SUSP, 3000)) + dev_warn(hsotg->dev, "Suspend wasn't generated\n"); + + /* Backup all registers */ + ret = dwc2_backup_global_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to backup global registers\n", + __func__); + return ret; + } + + ret = dwc2_backup_host_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to backup host registers\n", + __func__); + return ret; + } + + /* + * Clear any pending interrupts since dwc2 will not be able to + * clear them after entering partial_power_down. + */ + dwc2_writel(hsotg, 0xffffffff, GINTSTS); + + /* Put the controller in low power state */ + pcgcctl = dwc2_readl(hsotg, PCGCTL); + + pcgcctl |= PCGCTL_PWRCLMP; + dwc2_writel(hsotg, pcgcctl, PCGCTL); + udelay(5); + + pcgcctl |= PCGCTL_RSTPDWNMODULE; + dwc2_writel(hsotg, pcgcctl, PCGCTL); + udelay(5); + + pcgcctl |= PCGCTL_STOPPCLK; + dwc2_writel(hsotg, pcgcctl, PCGCTL); + + /* Set in_ppd flag to 1 as here core enters suspend. */ + hsotg->in_ppd = 1; + hsotg->lx_state = DWC2_L2; + hsotg->bus_suspended = true; + + dev_dbg(hsotg->dev, "Entering host partial power down completed.\n"); + + return ret; +} + +/* + * dwc2_host_exit_partial_power_down() - Exit controller from host partial + * power down. + * + * @hsotg: Programming view of the DWC_otg controller + * @rem_wakeup: indicates whether resume is initiated by Reset. + * @restore: indicates whether need to restore the registers or not. + * + * Return: non-zero if failed to exit host partial power down. + * + * This function is for exiting from Host mode partial power down. + */ +int dwc2_host_exit_partial_power_down(struct dwc2_hsotg *hsotg, + int rem_wakeup, bool restore) +{ + u32 pcgcctl; + int ret = 0; + u32 hprt0; + + dev_dbg(hsotg->dev, "Exiting host partial power down started.\n"); + + pcgcctl = dwc2_readl(hsotg, PCGCTL); + pcgcctl &= ~PCGCTL_STOPPCLK; + dwc2_writel(hsotg, pcgcctl, PCGCTL); + udelay(5); + + pcgcctl = dwc2_readl(hsotg, PCGCTL); + pcgcctl &= ~PCGCTL_PWRCLMP; + dwc2_writel(hsotg, pcgcctl, PCGCTL); + udelay(5); + + pcgcctl = dwc2_readl(hsotg, PCGCTL); + pcgcctl &= ~PCGCTL_RSTPDWNMODULE; + dwc2_writel(hsotg, pcgcctl, PCGCTL); + + udelay(100); + if (restore) { + ret = dwc2_restore_global_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to restore registers\n", + __func__); + return ret; + } + + ret = dwc2_restore_host_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to restore host registers\n", + __func__); + return ret; + } + } + + /* Drive resume signaling and exit suspend mode on the port. */ + hprt0 = dwc2_read_hprt0(hsotg); + hprt0 |= HPRT0_RES; + hprt0 &= ~HPRT0_SUSP; + dwc2_writel(hsotg, hprt0, HPRT0); + udelay(5); + + if (!rem_wakeup) { + /* Stop driveing resume signaling on the port. */ + hprt0 = dwc2_read_hprt0(hsotg); + hprt0 &= ~HPRT0_RES; + dwc2_writel(hsotg, hprt0, HPRT0); + + hsotg->bus_suspended = false; + } else { + /* Turn on the port power bit. */ + hprt0 = dwc2_read_hprt0(hsotg); + hprt0 |= HPRT0_PWR; + dwc2_writel(hsotg, hprt0, HPRT0); + + /* Connect hcd. */ + dwc2_hcd_connect(hsotg); + + mod_timer(&hsotg->wkp_timer, + jiffies + msecs_to_jiffies(71)); + } + + /* Set lx_state to and in_ppd to 0 as here core exits from suspend. */ + hsotg->in_ppd = 0; + hsotg->lx_state = DWC2_L0; + + dev_dbg(hsotg->dev, "Exiting host partial power down completed.\n"); + return ret; +} + +/** + * dwc2_host_enter_clock_gating() - Put controller in clock gating. + * + * @hsotg: Programming view of the DWC_otg controller + * + * This function is for entering Host mode clock gating. + */ +void dwc2_host_enter_clock_gating(struct dwc2_hsotg *hsotg) +{ + u32 hprt0; + u32 pcgctl; + + dev_dbg(hsotg->dev, "Entering host clock gating.\n"); + + /* Put this port in suspend mode. */ + hprt0 = dwc2_read_hprt0(hsotg); + hprt0 |= HPRT0_SUSP; + dwc2_writel(hsotg, hprt0, HPRT0); + + /* Set the Phy Clock bit as suspend is received. */ + pcgctl = dwc2_readl(hsotg, PCGCTL); + pcgctl |= PCGCTL_STOPPCLK; + dwc2_writel(hsotg, pcgctl, PCGCTL); + udelay(5); + + /* Set the Gate hclk as suspend is received. */ + pcgctl = dwc2_readl(hsotg, PCGCTL); + pcgctl |= PCGCTL_GATEHCLK; + dwc2_writel(hsotg, pcgctl, PCGCTL); + udelay(5); + + hsotg->bus_suspended = true; + hsotg->lx_state = DWC2_L2; +} + +/** + * dwc2_host_exit_clock_gating() - Exit controller from clock gating. + * + * @hsotg: Programming view of the DWC_otg controller + * @rem_wakeup: indicates whether resume is initiated by remote wakeup + * + * This function is for exiting Host mode clock gating. + */ +void dwc2_host_exit_clock_gating(struct dwc2_hsotg *hsotg, int rem_wakeup) +{ + u32 hprt0; + u32 pcgctl; + + dev_dbg(hsotg->dev, "Exiting host clock gating.\n"); + + /* Clear the Gate hclk. */ + pcgctl = dwc2_readl(hsotg, PCGCTL); + pcgctl &= ~PCGCTL_GATEHCLK; + dwc2_writel(hsotg, pcgctl, PCGCTL); + udelay(5); + + /* Phy Clock bit. */ + pcgctl = dwc2_readl(hsotg, PCGCTL); + pcgctl &= ~PCGCTL_STOPPCLK; + dwc2_writel(hsotg, pcgctl, PCGCTL); + udelay(5); + + /* Drive resume signaling and exit suspend mode on the port. */ + hprt0 = dwc2_read_hprt0(hsotg); + hprt0 |= HPRT0_RES; + hprt0 &= ~HPRT0_SUSP; + dwc2_writel(hsotg, hprt0, HPRT0); + udelay(5); + + if (!rem_wakeup) { + /* In case of port resume need to wait for 40 ms */ + msleep(USB_RESUME_TIMEOUT); + + /* Stop driveing resume signaling on the port. */ + hprt0 = dwc2_read_hprt0(hsotg); + hprt0 &= ~HPRT0_RES; + dwc2_writel(hsotg, hprt0, HPRT0); + + hsotg->bus_suspended = false; + hsotg->lx_state = DWC2_L0; + } else { + mod_timer(&hsotg->wkp_timer, + jiffies + msecs_to_jiffies(71)); + } +} diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c index 68bbac64b753..621a4846bd05 100644 --- a/drivers/usb/dwc2/hcd_queue.c +++ b/drivers/usb/dwc2/hcd_queue.c @@ -59,7 +59,7 @@ #define DWC2_UNRESERVE_DELAY (msecs_to_jiffies(5)) /* If we get a NAK, wait this long before retrying */ -#define DWC2_RETRY_WAIT_DELAY 1*1E6L +#define DWC2_RETRY_WAIT_DELAY (1 * 1E6L) /** * dwc2_periodic_channel_available() - Checks that a channel is available for a diff --git a/drivers/usb/dwc2/hw.h b/drivers/usb/dwc2/hw.h index c3d6dde2aca4..6b16fbf98bc6 100644 --- a/drivers/usb/dwc2/hw.h +++ b/drivers/usb/dwc2/hw.h @@ -44,6 +44,7 @@ #define GOTGCTL_CHIRPEN BIT(27) #define GOTGCTL_MULT_VALID_BC_MASK (0x1f << 22) #define GOTGCTL_MULT_VALID_BC_SHIFT 22 +#define GOTGCTL_CURMODE_HOST BIT(21) #define GOTGCTL_OTGVER BIT(20) #define GOTGCTL_BSESVLD BIT(19) #define GOTGCTL_ASESVLD BIT(18) diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c index 92df3d620f7d..7a6089fa81e1 100644 --- a/drivers/usb/dwc2/params.c +++ b/drivers/usb/dwc2/params.c @@ -232,6 +232,12 @@ 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[] = { + { "BCM2848", (kernel_ulong_t)dwc2_set_bcm_params }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, dwc2_acpi_match); + static void dwc2_set_param_otg_cap(struct dwc2_hsotg *hsotg) { u8 val; @@ -866,10 +872,12 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg) return 0; } +typedef void (*set_params_cb)(struct dwc2_hsotg *data); + int dwc2_init_params(struct dwc2_hsotg *hsotg) { const struct of_device_id *match; - void (*set_params)(struct dwc2_hsotg *data); + set_params_cb set_params; dwc2_set_default_params(hsotg); dwc2_get_device_properties(hsotg); @@ -878,6 +886,14 @@ int dwc2_init_params(struct dwc2_hsotg *hsotg) if (match && match->data) { set_params = match->data; set_params(hsotg); + } else { + const struct acpi_device_id *amatch; + + amatch = acpi_match_device(dwc2_acpi_match, hsotg->dev); + if (amatch && amatch->driver_data) { + set_params = (set_params_cb)amatch->driver_data; + set_params(hsotg); + } } dwc2_check_params(hsotg); diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 5f18acac7406..3024785d84cb 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -316,6 +316,39 @@ static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg) static int dwc2_driver_remove(struct platform_device *dev) { struct dwc2_hsotg *hsotg = platform_get_drvdata(dev); + struct dwc2_gregs_backup *gr; + int ret = 0; + + gr = &hsotg->gr_backup; + + /* Exit Hibernation when driver is removed. */ + if (hsotg->hibernated) { + if (gr->gotgctl & GOTGCTL_CURMODE_HOST) + ret = dwc2_exit_hibernation(hsotg, 0, 0, 1); + else + ret = dwc2_exit_hibernation(hsotg, 0, 0, 0); + + if (ret) + dev_err(hsotg->dev, + "exit hibernation failed.\n"); + } + + /* Exit Partial Power Down when driver is removed. */ + if (hsotg->in_ppd) { + ret = dwc2_exit_partial_power_down(hsotg, 0, true); + if (ret) + dev_err(hsotg->dev, + "exit partial_power_down failed\n"); + } + + /* Exit clock gating when driver is removed. */ + if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_NONE && + hsotg->bus_suspended) { + if (dwc2_is_device_mode(hsotg)) + dwc2_gadget_exit_clock_gating(hsotg, 0); + else + dwc2_host_exit_clock_gating(hsotg, 0); + } dwc2_debugfs_exit(hsotg); if (hsotg->hcd_enabled) @@ -334,7 +367,7 @@ static int dwc2_driver_remove(struct platform_device *dev) reset_control_assert(hsotg->reset); reset_control_assert(hsotg->reset_ecc); - return 0; + return ret; } /** @@ -734,6 +767,7 @@ static struct platform_driver dwc2_platform_driver = { .driver = { .name = dwc2_driver_name, .of_match_table = dwc2_of_match_table, + .acpi_match_table = ACPI_PTR(dwc2_acpi_match), .pm = &dwc2_dev_pm_ops, }, .probe = dwc2_driver_probe, diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 2133acf8ee69..66b1454c4db2 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -149,4 +149,13 @@ config USB_DWC3_IMX8MP functionality. Say 'Y' or 'M' if you have one such device. +config USB_DWC3_XILINX + tristate "Xilinx Platforms" + depends on (ARCH_ZYNQMP || ARCH_VERSAL) && OF + default USB_DWC3 + help + Support Xilinx SoCs with DesignWare Core USB3 IP. + This driver handles both ZynqMP and Versal SoC operations. + Say 'Y' or 'M' if you have one such device. + endif diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 2259f8876fb2..2d499de6f66a 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -52,3 +52,4 @@ obj-$(CONFIG_USB_DWC3_OF_SIMPLE) += dwc3-of-simple.o obj-$(CONFIG_USB_DWC3_ST) += dwc3-st.o obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom.o obj-$(CONFIG_USB_DWC3_IMX8MP) += dwc3-imx8mp.o +obj-$(CONFIG_USB_DWC3_XILINX) += dwc3-xilinx.o diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index f2448d0a9d39..b6e53d8212cd 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -114,6 +114,8 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode) dwc->current_dr_role = mode; } +static int dwc3_core_soft_reset(struct dwc3 *dwc); + static void __dwc3_set_mode(struct work_struct *work) { struct dwc3 *dwc = work_to_dwc(work); @@ -121,6 +123,8 @@ static void __dwc3_set_mode(struct work_struct *work) int ret; u32 reg; + mutex_lock(&dwc->mutex); + pm_runtime_get_sync(dwc->dev); if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_OTG) @@ -154,6 +158,25 @@ static void __dwc3_set_mode(struct work_struct *work) break; } + /* For DRD host or device mode only */ + if (dwc->desired_dr_role != DWC3_GCTL_PRTCAP_OTG) { + reg = dwc3_readl(dwc->regs, DWC3_GCTL); + reg |= DWC3_GCTL_CORESOFTRESET; + dwc3_writel(dwc->regs, DWC3_GCTL, reg); + + /* + * Wait for internal clocks to synchronized. DWC_usb31 and + * DWC_usb32 may need at least 50ms (less for DWC_usb3). To + * keep it consistent across different IPs, let's wait up to + * 100ms before clearing GCTL.CORESOFTRESET. + */ + msleep(100); + + reg = dwc3_readl(dwc->regs, DWC3_GCTL); + reg &= ~DWC3_GCTL_CORESOFTRESET; + dwc3_writel(dwc->regs, DWC3_GCTL, reg); + } + spin_lock_irqsave(&dwc->lock, flags); dwc3_set_prtcap(dwc, dwc->desired_dr_role); @@ -178,6 +201,8 @@ static void __dwc3_set_mode(struct work_struct *work) } break; case DWC3_GCTL_PRTCAP_DEVICE: + dwc3_core_soft_reset(dwc); + dwc3_event_buffers_setup(dwc); if (dwc->usb2_phy) @@ -200,6 +225,7 @@ static void __dwc3_set_mode(struct work_struct *work) out: pm_runtime_mark_last_busy(dwc->dev); pm_runtime_put_autosuspend(dwc->dev); + mutex_unlock(&dwc->mutex); } void dwc3_set_mode(struct dwc3 *dwc, u32 mode) @@ -544,6 +570,9 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc) parms->hwparams6 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS6); parms->hwparams7 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS7); parms->hwparams8 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS8); + + if (DWC3_IP_IS(DWC32)) + parms->hwparams9 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS9); } static int dwc3_core_ulpi_init(struct dwc3 *dwc) @@ -1238,6 +1267,8 @@ static void dwc3_get_properties(struct dwc3 *dwc) u8 rx_max_burst_prd; u8 tx_thr_num_pkt_prd; u8 tx_max_burst_prd; + const char *usb_psy_name; + int ret; /* default to highest possible threshold */ lpm_nyet_threshold = 0xf; @@ -1263,6 +1294,13 @@ static void dwc3_get_properties(struct dwc3 *dwc) else dwc->sysdev = dwc->dev; + ret = device_property_read_string(dev, "usb-psy-name", &usb_psy_name); + if (ret >= 0) { + dwc->usb_psy = power_supply_get_by_name(usb_psy_name); + if (!dwc->usb_psy) + dev_err(dev, "couldn't get usb power supply\n"); + } + dwc->has_lpm_erratum = device_property_read_bool(dev, "snps,has-lpm-erratum"); device_property_read_u8(dev, "snps,lpm-nyet-threshold", @@ -1277,6 +1315,8 @@ static void dwc3_get_properties(struct dwc3 *dwc) "snps,usb3_lpm_capable"); dwc->usb2_lpm_disable = device_property_read_bool(dev, "snps,usb2-lpm-disable"); + dwc->usb2_gadget_lpm_disable = device_property_read_bool(dev, + "snps,usb2-gadget-lpm-disable"); device_property_read_u8(dev, "snps,rx-thr-num-pkt-prd", &rx_thr_num_pkt_prd); device_property_read_u8(dev, "snps,rx-max-burst-prd", @@ -1385,7 +1425,6 @@ static void dwc3_check_params(struct dwc3 *dwc) /* Check the maximum_speed parameter */ switch (dwc->maximum_speed) { - case USB_SPEED_LOW: case USB_SPEED_FULL: case USB_SPEED_HIGH: break; @@ -1543,6 +1582,7 @@ static int dwc3_probe(struct platform_device *pdev) dwc3_cache_hwparams(dwc); spin_lock_init(&dwc->lock); + mutex_init(&dwc->mutex); pm_runtime_set_active(dev); pm_runtime_use_autosuspend(dev); @@ -1619,6 +1659,9 @@ static int dwc3_probe(struct platform_device *pdev) assert_reset: reset_control_assert(dwc->reset); + if (dwc->usb_psy) + power_supply_put(dwc->usb_psy); + return ret; } @@ -1641,9 +1684,17 @@ static int dwc3_remove(struct platform_device *pdev) dwc3_free_event_buffers(dwc); dwc3_free_scratch_buffers(dwc); + if (dwc->usb_psy) + power_supply_put(dwc->usb_psy); + return 0; } +static void dwc3_shutdown(struct platform_device *pdev) +{ + dwc3_remove(pdev); +} + #ifdef CONFIG_PM static int dwc3_core_init_for_resume(struct dwc3 *dwc) { @@ -1961,6 +2012,7 @@ MODULE_DEVICE_TABLE(acpi, dwc3_acpi_match); static struct platform_driver dwc3_driver = { .probe = dwc3_probe, .remove = dwc3_remove, + .shutdown = dwc3_shutdown, .driver = { .name = "dwc3", .of_match_table = of_match_ptr(of_dwc3_match), diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 052b20d52651..b1e875c58f20 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -30,6 +31,8 @@ #include +#include + #define DWC3_MSG_MAX 500 /* Global constants */ @@ -140,6 +143,7 @@ #define DWC3_GHWPARAMS8 0xc600 #define DWC3_GUCTL3 0xc60c #define DWC3_GFLADJ 0xc630 +#define DWC3_GHWPARAMS9 0xc680 /* Device Registers */ #define DWC3_DCFG 0xc700 @@ -375,6 +379,9 @@ #define DWC3_GHWPARAMS7_RAM1_DEPTH(n) ((n) & 0xffff) #define DWC3_GHWPARAMS7_RAM2_DEPTH(n) (((n) >> 16) & 0xffff) +/* Global HWPARAMS9 Register */ +#define DWC3_GHWPARAMS9_DEV_TXF_FLUSH_BYPASS BIT(0) + /* Global Frame Length Adjustment Register */ #define DWC3_GFLADJ_30MHZ_SDBND_SEL BIT(7) #define DWC3_GFLADJ_30MHZ_MASK 0x3f @@ -396,12 +403,12 @@ #define DWC3_DCFG_SUPERSPEED (4 << 0) #define DWC3_DCFG_HIGHSPEED (0 << 0) #define DWC3_DCFG_FULLSPEED BIT(0) -#define DWC3_DCFG_LOWSPEED (2 << 0) #define DWC3_DCFG_NUMP_SHIFT 17 #define DWC3_DCFG_NUMP(n) (((n) >> DWC3_DCFG_NUMP_SHIFT) & 0x1f) #define DWC3_DCFG_NUMP_MASK (0x1f << DWC3_DCFG_NUMP_SHIFT) #define DWC3_DCFG_LPM_CAP BIT(22) +#define DWC3_DCFG_IGNSTRMPP BIT(23) /* Device Control Register */ #define DWC3_DCTL_RUN_STOP BIT(31) @@ -490,7 +497,6 @@ #define DWC3_DSTS_SUPERSPEED (4 << 0) #define DWC3_DSTS_HIGHSPEED (0 << 0) #define DWC3_DSTS_FULLSPEED BIT(0) -#define DWC3_DSTS_LOWSPEED (2 << 0) /* Device Generic Command Register */ #define DWC3_DGCMD_SET_LMP 0x01 @@ -855,13 +861,12 @@ struct dwc3_hwparams { u32 hwparams6; u32 hwparams7; u32 hwparams8; + u32 hwparams9; }; /* HWPARAMS0 */ #define DWC3_MODE(n) ((n) & 0x7) -#define DWC3_MDWIDTH(n) (((n) & 0xff00) >> 8) - /* HWPARAMS1 */ #define DWC3_NUM_INT(n) (((n) & (0x3f << 15)) >> 15) @@ -908,11 +913,13 @@ struct dwc3_request { unsigned int remaining; unsigned int status; -#define DWC3_REQUEST_STATUS_QUEUED 0 -#define DWC3_REQUEST_STATUS_STARTED 1 -#define DWC3_REQUEST_STATUS_CANCELLED 2 -#define DWC3_REQUEST_STATUS_COMPLETED 3 -#define DWC3_REQUEST_STATUS_UNKNOWN -1 +#define DWC3_REQUEST_STATUS_QUEUED 0 +#define DWC3_REQUEST_STATUS_STARTED 1 +#define DWC3_REQUEST_STATUS_DISCONNECTED 2 +#define DWC3_REQUEST_STATUS_DEQUEUED 3 +#define DWC3_REQUEST_STATUS_STALLED 4 +#define DWC3_REQUEST_STATUS_COMPLETED 5 +#define DWC3_REQUEST_STATUS_UNKNOWN -1 u8 epnum; struct dwc3_trb *trb; @@ -946,6 +953,7 @@ struct dwc3_scratchpad_array { * @scratch_addr: dma address of scratchbuf * @ep0_in_setup: one control transfer is completed and enter setup phase * @lock: for synchronizing + * @mutex: for mode switching * @dev: pointer to our struct device * @sysdev: pointer to the DMA-capable device * @xhci: pointer to our xHCI child @@ -986,6 +994,7 @@ struct dwc3_scratchpad_array { * @role_sw: usb_role_switch handle * @role_switch_default_mode: default operation mode of controller while * usb role is USB_ROLE_NONE. + * @usb_psy: pointer to power supply interface. * @usb2_phy: pointer to USB2 PHY * @usb3_phy: pointer to USB3 PHY * @usb2_generic_phy: pointer to USB2 PHY @@ -1034,7 +1043,8 @@ struct dwc3_scratchpad_array { * @dis_start_transfer_quirk: set if start_transfer failure SW workaround is * not needed for DWC_usb31 version 1.70a-ea06 and below * @usb3_lpm_capable: set if hadrware supports Link Power Management - * @usb2_lpm_disable: set to disable usb2 lpm + * @usb2_lpm_disable: set to disable usb2 lpm for host + * @usb2_gadget_lpm_disable: set to disable usb2 lpm for gadget * @disable_scramble_quirk: set if we enable the disable scramble quirk * @u2exit_lfps_quirk: set if we enable u2exit lfps quirk * @u2ss_inp3_quirk: set if we enable P3 OK for U2/SS Inactive quirk @@ -1085,6 +1095,9 @@ struct dwc3 { /* device lock */ spinlock_t lock; + /* mode switching lock */ + struct mutex mutex; + struct device *dev; struct device *sysdev; @@ -1125,6 +1138,8 @@ struct dwc3 { struct usb_role_switch *role_sw; enum usb_dr_mode role_switch_default_mode; + struct power_supply *usb_psy; + u32 fladj; u32 irq_gadget; u32 otg_irq; @@ -1238,6 +1253,7 @@ struct dwc3 { unsigned dis_start_transfer_quirk:1; unsigned usb3_lpm_capable:1; unsigned usb2_lpm_disable:1; + unsigned usb2_gadget_lpm_disable:1; unsigned disable_scramble_quirk:1; unsigned u2exit_lfps_quirk:1; @@ -1455,6 +1471,23 @@ u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type); (!(_ip##_VERSIONTYPE_##_to) || \ dwc->version_type <= _ip##_VERSIONTYPE_##_to)) +/** + * dwc3_mdwidth - get MDWIDTH value in bits + * @dwc: pointer to our context structure + * + * Return MDWIDTH configuration value in bits. + */ +static inline u32 dwc3_mdwidth(struct dwc3 *dwc) +{ + u32 mdwidth; + + mdwidth = DWC3_GHWPARAMS0_MDWIDTH(dwc->hwparams.hwparams0); + if (DWC3_IP_IS(DWC32)) + mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6); + + return mdwidth; +} + bool dwc3_has_imod(struct dwc3 *dwc); int dwc3_event_buffers_setup(struct dwc3 *dwc); diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h index 8ab394942360..db231de46bb3 100644 --- a/drivers/usb/dwc3/debug.h +++ b/drivers/usb/dwc3/debug.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/** +/* * debug.h - DesignWare USB3 DRD Controller Debug Header * * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index 5da4f6082d93..7146ee2ac057 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/** +/* * debugfs.c - DesignWare USB3 DRD Controller DebugFS file * * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com @@ -638,16 +638,14 @@ static int dwc3_tx_fifo_size_show(struct seq_file *s, void *unused) struct dwc3_ep *dep = s->private; struct dwc3 *dwc = dep->dwc; unsigned long flags; - int mdwidth; + u32 mdwidth; u32 val; spin_lock_irqsave(&dwc->lock, flags); val = dwc3_core_fifo_space(dep, DWC3_TXFIFO); /* Convert to bytes */ - mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0); - if (DWC3_IP_IS(DWC32)) - mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6); + mdwidth = dwc3_mdwidth(dwc); val *= mdwidth; val >>= 3; @@ -662,16 +660,14 @@ static int dwc3_rx_fifo_size_show(struct seq_file *s, void *unused) struct dwc3_ep *dep = s->private; struct dwc3 *dwc = dep->dwc; unsigned long flags; - int mdwidth; + u32 mdwidth; u32 val; spin_lock_irqsave(&dwc->lock, flags); val = dwc3_core_fifo_space(dep, DWC3_RXFIFO); /* Convert to bytes */ - mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0); - if (DWC3_IP_IS(DWC32)) - mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6); + mdwidth = dwc3_mdwidth(dwc); val *= mdwidth; val >>= 3; diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c index 90bb022737da..0ecf20eeceee 100644 --- a/drivers/usb/dwc3/dwc3-exynos.c +++ b/drivers/usb/dwc3/dwc3-exynos.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/** +/* * dwc3-exynos.c - Samsung Exynos DWC3 Specific Glue layer * * Copyright (c) 2012 Samsung Electronics Co., Ltd. diff --git a/drivers/usb/dwc3/dwc3-imx8mp.c b/drivers/usb/dwc3/dwc3-imx8mp.c index 75f0042b998b..b13cfab89d53 100644 --- a/drivers/usb/dwc3/dwc3-imx8mp.c +++ b/drivers/usb/dwc3/dwc3-imx8mp.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/** +/* * dwc3-imx8mp.c - NXP imx8mp Specific Glue layer * * Copyright (c) 2020 NXP. diff --git a/drivers/usb/dwc3/dwc3-keystone.c b/drivers/usb/dwc3/dwc3-keystone.c index 057056c0975e..1317959294e6 100644 --- a/drivers/usb/dwc3/dwc3-keystone.c +++ b/drivers/usb/dwc3/dwc3-keystone.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/** +/* * dwc3-keystone.c - Keystone Specific Glue layer * * Copyright (C) 2010-2013 Texas Instruments Incorporated - https://www.ti.com diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c index e62ecd22b3ed..71fd620c5161 100644 --- a/drivers/usb/dwc3/dwc3-of-simple.c +++ b/drivers/usb/dwc3/dwc3-of-simple.c @@ -172,7 +172,6 @@ static const struct dev_pm_ops dwc3_of_simple_dev_pm_ops = { static const struct of_device_id of_dwc3_simple_match[] = { { .compatible = "rockchip,rk3399-dwc3" }, - { .compatible = "xlnx,zynqmp-dwc3" }, { .compatible = "cavium,octeon-7130-usb-uctl" }, { .compatible = "sprd,sc9860-dwc3" }, { .compatible = "allwinner,sun50i-h6-dwc3" }, diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 4c5c6972124a..e7b932dcbf82 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/** +/* * dwc3-pci.c - PCI Specific glue layer * * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com @@ -41,6 +41,7 @@ #define PCI_DEVICE_ID_INTEL_TGPH 0x43ee #define PCI_DEVICE_ID_INTEL_JSP 0x4dee #define PCI_DEVICE_ID_INTEL_ADLP 0x51ee +#define PCI_DEVICE_ID_INTEL_ADLM 0x54ee #define PCI_DEVICE_ID_INTEL_ADLS 0x7ae1 #define PCI_DEVICE_ID_INTEL_TGL 0x9a15 @@ -388,6 +389,9 @@ static const struct pci_device_id dwc3_pci_id_table[] = { { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ADLP), (kernel_ulong_t) &dwc3_pci_intel_swnode, }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ADLM), + (kernel_ulong_t) &dwc3_pci_intel_swnode, }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ADLS), (kernel_ulong_t) &dwc3_pci_intel_swnode, }, diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c index 3de291ab951a..49e6ca94486d 100644 --- a/drivers/usb/dwc3/dwc3-qcom.c +++ b/drivers/usb/dwc3/dwc3-qcom.c @@ -235,7 +235,7 @@ static int dwc3_qcom_interconnect_disable(struct dwc3_qcom *qcom) /** * dwc3_qcom_interconnect_init() - Get interconnect path handles - * and set bandwidhth. + * and set bandwidth. * @qcom: Pointer to the concerned usb core. * */ @@ -647,7 +647,7 @@ static int dwc3_qcom_of_register_core(struct platform_device *pdev) struct device *dev = &pdev->dev; int ret; - dwc3_np = of_get_child_by_name(np, "dwc3"); + dwc3_np = of_get_compatible_child(np, "snps,dwc3"); if (!dwc3_np) { dev_err(dev, "failed to find dwc3 core child\n"); return -ENODEV; @@ -774,7 +774,6 @@ static int dwc3_qcom_probe(struct platform_device *pdev) qcom->qscratch_base = devm_ioremap_resource(dev, parent_res); if (IS_ERR(qcom->qscratch_base)) { - dev_err(dev, "failed to map qscratch, err=%d\n", ret); ret = PTR_ERR(qcom->qscratch_base); goto clk_disable; } diff --git a/drivers/usb/dwc3/dwc3-st.c b/drivers/usb/dwc3/dwc3-st.c index b06b7092b1a2..166b5bde45cb 100644 --- a/drivers/usb/dwc3/dwc3-st.c +++ b/drivers/usb/dwc3/dwc3-st.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0+ -/** +/* * dwc3-st.c Support for dwc3 platform devices on ST Microelectronics platforms * * This is a small driver for the dwc3 to provide the glue logic diff --git a/drivers/usb/dwc3/dwc3-xilinx.c b/drivers/usb/dwc3/dwc3-xilinx.c new file mode 100644 index 000000000000..9cc3ad701a29 --- /dev/null +++ b/drivers/usb/dwc3/dwc3-xilinx.c @@ -0,0 +1,337 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * dwc3-xilinx.c - Xilinx DWC3 controller specific glue driver + * + * Authors: Manish Narani + * Anurag Kumar Vulisha + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* USB phy reset mask register */ +#define XLNX_USB_PHY_RST_EN 0x001C +#define XLNX_PHY_RST_MASK 0x1 + +/* Xilinx USB 3.0 IP Register */ +#define XLNX_USB_TRAFFIC_ROUTE_CONFIG 0x005C +#define XLNX_USB_TRAFFIC_ROUTE_FPD 0x1 + +/* Versal USB Reset ID */ +#define VERSAL_USB_RESET_ID 0xC104036 + +#define XLNX_USB_FPD_PIPE_CLK 0x7c +#define PIPE_CLK_DESELECT 1 +#define PIPE_CLK_SELECT 0 +#define XLNX_USB_FPD_POWER_PRSNT 0x80 +#define FPD_POWER_PRSNT_OPTION BIT(0) + +struct dwc3_xlnx { + int num_clocks; + struct clk_bulk_data *clks; + struct device *dev; + void __iomem *regs; + int (*pltfm_init)(struct dwc3_xlnx *data); +}; + +static void dwc3_xlnx_mask_phy_rst(struct dwc3_xlnx *priv_data, bool mask) +{ + u32 reg; + + /* + * Enable or disable ULPI PHY reset from USB Controller. + * This does not actually reset the phy, but just controls + * whether USB controller can or cannot reset ULPI PHY. + */ + reg = readl(priv_data->regs + XLNX_USB_PHY_RST_EN); + + if (mask) + reg &= ~XLNX_PHY_RST_MASK; + else + reg |= XLNX_PHY_RST_MASK; + + writel(reg, priv_data->regs + XLNX_USB_PHY_RST_EN); +} + +static int dwc3_xlnx_init_versal(struct dwc3_xlnx *priv_data) +{ + struct device *dev = priv_data->dev; + int ret; + + dwc3_xlnx_mask_phy_rst(priv_data, false); + + /* Assert and De-assert reset */ + ret = zynqmp_pm_reset_assert(VERSAL_USB_RESET_ID, + PM_RESET_ACTION_ASSERT); + if (ret < 0) { + dev_err_probe(dev, ret, "failed to assert Reset\n"); + return ret; + } + + ret = zynqmp_pm_reset_assert(VERSAL_USB_RESET_ID, + PM_RESET_ACTION_RELEASE); + if (ret < 0) { + dev_err_probe(dev, ret, "failed to De-assert Reset\n"); + return ret; + } + + dwc3_xlnx_mask_phy_rst(priv_data, true); + + return 0; +} + +static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data) +{ + struct device *dev = priv_data->dev; + struct reset_control *crst, *hibrst, *apbrst; + struct phy *usb3_phy; + int ret; + u32 reg; + + usb3_phy = devm_phy_get(dev, "usb3-phy"); + if (PTR_ERR(usb3_phy) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto err; + } else if (IS_ERR(usb3_phy)) { + usb3_phy = NULL; + } + + crst = devm_reset_control_get_exclusive(dev, "usb_crst"); + if (IS_ERR(crst)) { + ret = PTR_ERR(crst); + dev_err_probe(dev, ret, + "failed to get core reset signal\n"); + goto err; + } + + hibrst = devm_reset_control_get_exclusive(dev, "usb_hibrst"); + if (IS_ERR(hibrst)) { + ret = PTR_ERR(hibrst); + dev_err_probe(dev, ret, + "failed to get hibernation reset signal\n"); + goto err; + } + + apbrst = devm_reset_control_get_exclusive(dev, "usb_apbrst"); + if (IS_ERR(apbrst)) { + ret = PTR_ERR(apbrst); + dev_err_probe(dev, ret, + "failed to get APB reset signal\n"); + goto err; + } + + ret = reset_control_assert(crst); + if (ret < 0) { + dev_err(dev, "Failed to assert core reset\n"); + goto err; + } + + ret = reset_control_assert(hibrst); + if (ret < 0) { + dev_err(dev, "Failed to assert hibernation reset\n"); + goto err; + } + + ret = reset_control_assert(apbrst); + if (ret < 0) { + dev_err(dev, "Failed to assert APB reset\n"); + goto err; + } + + ret = phy_init(usb3_phy); + if (ret < 0) { + phy_exit(usb3_phy); + goto err; + } + + ret = reset_control_deassert(apbrst); + if (ret < 0) { + dev_err(dev, "Failed to release APB reset\n"); + goto err; + } + + /* Set PIPE Power Present signal in FPD Power Present Register*/ + writel(FPD_POWER_PRSNT_OPTION, priv_data->regs + XLNX_USB_FPD_POWER_PRSNT); + + /* Set the PIPE Clock Select bit in FPD PIPE Clock register */ + writel(PIPE_CLK_SELECT, priv_data->regs + XLNX_USB_FPD_PIPE_CLK); + + ret = reset_control_deassert(crst); + if (ret < 0) { + dev_err(dev, "Failed to release core reset\n"); + goto err; + } + + ret = reset_control_deassert(hibrst); + if (ret < 0) { + dev_err(dev, "Failed to release hibernation reset\n"); + goto err; + } + + ret = phy_power_on(usb3_phy); + if (ret < 0) { + phy_exit(usb3_phy); + goto err; + } + + /* + * This routes the USB DMA traffic to go through FPD path instead + * of reaching DDR directly. This traffic routing is needed to + * make SMMU and CCI work with USB DMA. + */ + if (of_dma_is_coherent(dev->of_node) || device_iommu_mapped(dev)) { + reg = readl(priv_data->regs + XLNX_USB_TRAFFIC_ROUTE_CONFIG); + reg |= XLNX_USB_TRAFFIC_ROUTE_FPD; + writel(reg, priv_data->regs + XLNX_USB_TRAFFIC_ROUTE_CONFIG); + } + +err: + return ret; +} + +static const struct of_device_id dwc3_xlnx_of_match[] = { + { + .compatible = "xlnx,zynqmp-dwc3", + .data = &dwc3_xlnx_init_zynqmp, + }, + { + .compatible = "xlnx,versal-dwc3", + .data = &dwc3_xlnx_init_versal, + }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dwc3_xlnx_of_match); + +static int dwc3_xlnx_probe(struct platform_device *pdev) +{ + struct dwc3_xlnx *priv_data; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + const struct of_device_id *match; + void __iomem *regs; + int ret; + + priv_data = devm_kzalloc(dev, sizeof(*priv_data), GFP_KERNEL); + if (!priv_data) + 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; + } + + match = of_match_node(dwc3_xlnx_of_match, pdev->dev.of_node); + + priv_data->pltfm_init = match->data; + priv_data->regs = regs; + priv_data->dev = dev; + + platform_set_drvdata(pdev, priv_data); + + ret = devm_clk_bulk_get_all(priv_data->dev, &priv_data->clks); + if (ret < 0) + return ret; + + priv_data->num_clocks = ret; + + ret = clk_bulk_prepare_enable(priv_data->num_clocks, priv_data->clks); + if (ret) + return ret; + + ret = priv_data->pltfm_init(priv_data); + if (ret) + goto err_clk_put; + + ret = of_platform_populate(np, NULL, NULL, dev); + if (ret) + goto err_clk_put; + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_suspend_ignore_children(dev, false); + pm_runtime_get_sync(dev); + + return 0; + +err_clk_put: + clk_bulk_disable_unprepare(priv_data->num_clocks, priv_data->clks); + + return ret; +} + +static int dwc3_xlnx_remove(struct platform_device *pdev) +{ + struct dwc3_xlnx *priv_data = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + + of_platform_depopulate(dev); + + clk_bulk_disable_unprepare(priv_data->num_clocks, priv_data->clks); + priv_data->num_clocks = 0; + + pm_runtime_disable(dev); + pm_runtime_put_noidle(dev); + pm_runtime_set_suspended(dev); + + return 0; +} + +static int __maybe_unused dwc3_xlnx_suspend_common(struct device *dev) +{ + struct dwc3_xlnx *priv_data = dev_get_drvdata(dev); + + clk_bulk_disable(priv_data->num_clocks, priv_data->clks); + + return 0; +} + +static int __maybe_unused dwc3_xlnx_resume_common(struct device *dev) +{ + struct dwc3_xlnx *priv_data = dev_get_drvdata(dev); + + return clk_bulk_enable(priv_data->num_clocks, priv_data->clks); +} + +static int __maybe_unused dwc3_xlnx_runtime_idle(struct device *dev) +{ + pm_runtime_mark_last_busy(dev); + pm_runtime_autosuspend(dev); + + return 0; +} + +static UNIVERSAL_DEV_PM_OPS(dwc3_xlnx_dev_pm_ops, dwc3_xlnx_suspend_common, + dwc3_xlnx_resume_common, dwc3_xlnx_runtime_idle); + +static struct platform_driver dwc3_xlnx_driver = { + .probe = dwc3_xlnx_probe, + .remove = dwc3_xlnx_remove, + .driver = { + .name = "dwc3-xilinx", + .of_match_table = dwc3_xlnx_of_match, + .pm = &dwc3_xlnx_dev_pm_ops, + }, +}; + +module_platform_driver(dwc3_xlnx_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Xilinx DWC3 controller specific glue driver"); +MODULE_AUTHOR("Manish Narani "); +MODULE_AUTHOR("Anurag Kumar Vulisha "); diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index c7ef218e7a8c..dd80e5ca8c78 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -308,13 +308,12 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd, } if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) { - int needs_wakeup; + int link_state; - needs_wakeup = (dwc->link_state == DWC3_LINK_STATE_U1 || - dwc->link_state == DWC3_LINK_STATE_U2 || - dwc->link_state == DWC3_LINK_STATE_U3); - - if (unlikely(needs_wakeup)) { + link_state = dwc3_gadget_get_link_state(dwc); + if (link_state == DWC3_LINK_STATE_U1 || + link_state == DWC3_LINK_STATE_U2 || + link_state == DWC3_LINK_STATE_U3) { ret = __dwc3_gadget_wakeup(dwc); dev_WARN_ONCE(dwc->dev, ret, "wakeup failed --> %d\n", ret); @@ -608,12 +607,14 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action) u8 bInterval_m1; /* - * Valid range for DEPCFG.bInterval_m1 is from 0 to 13, and it - * must be set to 0 when the controller operates in full-speed. + * Valid range for DEPCFG.bInterval_m1 is from 0 to 13. + * + * NOTE: The programming guide incorrectly stated bInterval_m1 + * must be set to 0 when operating in fullspeed. Internally the + * controller does not have this limitation. See DWC_usb3x + * programming guide section 3.2.2.1. */ bInterval_m1 = min_t(u8, desc->bInterval - 1, 13); - if (dwc->gadget->speed == USB_SPEED_FULL) - bInterval_m1 = 0; if (usb_endpoint_type(desc) == USB_ENDPOINT_XFER_INT && dwc->gadget->speed == USB_SPEED_FULL) @@ -729,8 +730,16 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action) * All stream eps will reinitiate stream on NoStream * rejection until we can determine that the host can * prime after the first transfer. + * + * However, if the controller is capable of + * TXF_FLUSH_BYPASS, then IN direction endpoints will + * automatically restart the stream without the driver + * initiation. */ - dep->flags |= DWC3_EP_FORCE_RESTART_STREAM; + if (!dep->direction || + !(dwc->hwparams.hwparams9 & + DWC3_GHWPARAMS9_DEV_TXF_FLUSH_BYPASS)) + dep->flags |= DWC3_EP_FORCE_RESTART_STREAM; } } @@ -1402,7 +1411,7 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep) dwc3_stop_active_transfer(dep, true, true); list_for_each_entry_safe(req, tmp, &dep->started_list, list) - dwc3_gadget_move_cancelled_request(req); + dwc3_gadget_move_cancelled_request(req, DWC3_REQUEST_STATUS_DEQUEUED); /* If ep isn't started, then there's no end transfer pending */ if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING)) @@ -1729,10 +1738,25 @@ static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep) { struct dwc3_request *req; struct dwc3_request *tmp; + struct dwc3 *dwc = dep->dwc; list_for_each_entry_safe(req, tmp, &dep->cancelled_list, list) { dwc3_gadget_ep_skip_trbs(dep, req); - dwc3_gadget_giveback(dep, req, -ECONNRESET); + switch (req->status) { + case DWC3_REQUEST_STATUS_DISCONNECTED: + dwc3_gadget_giveback(dep, req, -ESHUTDOWN); + break; + case DWC3_REQUEST_STATUS_DEQUEUED: + dwc3_gadget_giveback(dep, req, -ECONNRESET); + break; + case DWC3_REQUEST_STATUS_STALLED: + dwc3_gadget_giveback(dep, req, -EPIPE); + break; + default: + dev_err(dwc->dev, "request cancelled with wrong reason:%d\n", req->status); + dwc3_gadget_giveback(dep, req, -ECONNRESET); + break; + } } } @@ -1776,7 +1800,8 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, * cancelled. */ list_for_each_entry_safe(r, t, &dep->started_list, list) - dwc3_gadget_move_cancelled_request(r); + dwc3_gadget_move_cancelled_request(r, + DWC3_REQUEST_STATUS_DEQUEUED); dep->flags &= ~DWC3_EP_WAIT_TRANSFER_COMPLETE; @@ -1848,7 +1873,7 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol) dwc3_stop_active_transfer(dep, true, true); list_for_each_entry_safe(req, tmp, &dep->started_list, list) - dwc3_gadget_move_cancelled_request(req); + dwc3_gadget_move_cancelled_request(req, DWC3_REQUEST_STATUS_STALLED); if (dep->flags & DWC3_EP_END_TRANSFER_PENDING) { dep->flags |= DWC3_EP_PENDING_CLEAR_STALL; @@ -1973,6 +1998,8 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc) case DWC3_LINK_STATE_RESET: case DWC3_LINK_STATE_RX_DET: /* in HS, means Early Suspend */ case DWC3_LINK_STATE_U3: /* in HS, means SUSPEND */ + case DWC3_LINK_STATE_U2: /* in HS, means Sleep (L1) */ + case DWC3_LINK_STATE_U1: case DWC3_LINK_STATE_RESUME: break; default: @@ -2113,9 +2140,6 @@ static void __dwc3_gadget_set_speed(struct dwc3 *dwc) reg |= DWC3_DCFG_SUPERSPEED; } else { switch (speed) { - case USB_SPEED_LOW: - reg |= DWC3_DCFG_LOWSPEED; - break; case USB_SPEED_FULL: reg |= DWC3_DCFG_FULLSPEED; break; @@ -2340,9 +2364,7 @@ static void dwc3_gadget_setup_nump(struct dwc3 *dwc) u32 reg; ram2_depth = DWC3_GHWPARAMS7_RAM2_DEPTH(dwc->hwparams.hwparams7); - mdwidth = DWC3_GHWPARAMS0_MDWIDTH(dwc->hwparams.hwparams0); - if (DWC3_IP_IS(DWC32)) - mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6); + mdwidth = dwc3_mdwidth(dwc); nump = ((ram2_depth * mdwidth / 8) - 24 - 16) / 1024; nump = min_t(u32, nump, 16); @@ -2388,6 +2410,17 @@ static int __dwc3_gadget_start(struct dwc3 *dwc) dwc3_gadget_setup_nump(dwc); + /* + * Currently the controller handles single stream only. So, Ignore + * Packet Pending bit for stream selection and don't search for another + * stream if the host sends Data Packet with PP=0 (for OUT direction) or + * ACK with NumP=0 and PP=0 (for IN direction). This slightly improves + * the stream performance. + */ + reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg |= DWC3_DCFG_IGNSTRMPP; + dwc3_writel(dwc->regs, DWC3_DCFG, reg); + /* Start with SuperSpeed Default */ dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); @@ -2531,11 +2564,19 @@ static void dwc3_gadget_set_ssp_rate(struct usb_gadget *g, static int dwc3_gadget_vbus_draw(struct usb_gadget *g, unsigned int mA) { struct dwc3 *dwc = gadget_to_dwc(g); + union power_supply_propval val = {0}; + int ret; if (dwc->usb2_phy) return usb_phy_set_power(dwc->usb2_phy, mA); - return 0; + if (!dwc->usb_psy) + return -EOPNOTSUPP; + + val.intval = 1000 * mA; + ret = power_supply_set_property(dwc->usb_psy, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &val); + + return ret; } static const struct usb_gadget_ops dwc3_gadget_ops = { @@ -2571,12 +2612,10 @@ static int dwc3_gadget_init_control_endpoint(struct dwc3_ep *dep) static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep) { struct dwc3 *dwc = dep->dwc; - int mdwidth; + u32 mdwidth; int size; - mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0); - if (DWC3_IP_IS(DWC32)) - mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6); + mdwidth = dwc3_mdwidth(dwc); /* MDWIDTH is represented in bits, we need it in bytes */ mdwidth /= 8; @@ -2618,12 +2657,10 @@ static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep) static int dwc3_gadget_init_out_endpoint(struct dwc3_ep *dep) { struct dwc3 *dwc = dep->dwc; - int mdwidth; + u32 mdwidth; int size; - mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0); - if (DWC3_IP_IS(DWC32)) - mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6); + mdwidth = dwc3_mdwidth(dwc); /* MDWIDTH is represented in bits, convert to bytes */ mdwidth /= 8; @@ -2913,6 +2950,11 @@ static void dwc3_gadget_ep_cleanup_completed_requests(struct dwc3_ep *dep, static bool dwc3_gadget_ep_should_continue(struct dwc3_ep *dep) { struct dwc3_request *req; + struct dwc3 *dwc = dep->dwc; + + if (!dep->endpoint.desc || !dwc->pullups_connected || + !dwc->connected) + return false; if (!list_empty(&dep->pending_list)) return true; @@ -3322,6 +3364,15 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) { u32 reg; + /* + * Ideally, dwc3_reset_gadget() would trigger the function + * drivers to stop any active transfers through ep disable. + * However, for functions which defer ep disable, such as mass + * storage, we will need to rely on the call to stop active + * transfers here, and avoid allowing of request queuing. + */ + dwc->connected = false; + /* * WORKAROUND: DWC3 revisions <1.88a have an issue which * would cause a missing Disconnect Event if there's a @@ -3448,11 +3499,6 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) dwc->gadget->ep0->maxpacket = 64; dwc->gadget->speed = USB_SPEED_FULL; break; - case DWC3_DSTS_LOWSPEED: - dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(8); - dwc->gadget->ep0->maxpacket = 8; - dwc->gadget->speed = USB_SPEED_LOW; - break; } dwc->eps[1]->endpoint.maxpacket = dwc->gadget->ep0->maxpacket; @@ -3460,6 +3506,7 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) /* Enable USB2 LPM Capability */ if (!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A) && + !dwc->usb2_gadget_lpm_disable && (speed != DWC3_DSTS_SUPERSPEED) && (speed != DWC3_DSTS_SUPERSPEED_PLUS)) { reg = dwc3_readl(dwc->regs, DWC3_DCFG); @@ -3486,6 +3533,12 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) dwc3_gadget_dctl_write_safe(dwc, reg); } else { + if (dwc->usb2_gadget_lpm_disable) { + reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg &= ~DWC3_DCFG_LPM_CAP; + dwc3_writel(dwc->regs, DWC3_DCFG, reg); + } + reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg &= ~DWC3_DCTL_HIRD_THRES_MASK; dwc3_gadget_dctl_write_safe(dwc, reg); @@ -3934,7 +3987,7 @@ int dwc3_gadget_init(struct dwc3 *dwc) dwc->gadget->ssp_rate = USB_SSP_GEN_UNKNOWN; dwc->gadget->sg_supported = true; dwc->gadget->name = "dwc3-gadget"; - dwc->gadget->lpm_capable = true; + dwc->gadget->lpm_capable = !dwc->usb2_gadget_lpm_disable; /* * FIXME We might be setting max_speed to dep; - req->status = DWC3_REQUEST_STATUS_CANCELLED; + req->status = reason; list_move_tail(&req->list, &dep->cancelled_list); } diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h index 76b73b116862..1e96ea339d48 100644 --- a/drivers/usb/dwc3/io.h +++ b/drivers/usb/dwc3/io.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/** +/* * io.h - DesignWare USB3 DRD IO Header * * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com diff --git a/drivers/usb/dwc3/trace.c b/drivers/usb/dwc3/trace.c index 1b45a9723eeb..088995885678 100644 --- a/drivers/usb/dwc3/trace.c +++ b/drivers/usb/dwc3/trace.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/** +/* * trace.c - DesignWare USB3 DRD Controller Trace Support * * Copyright (C) 2014 Texas Instruments Incorporated - https://www.ti.com diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h index 97f4f1125a41..51d18e8d1602 100644 --- a/drivers/usb/dwc3/trace.h +++ b/drivers/usb/dwc3/trace.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/** +/* * trace.h - DesignWare USB3 DRD Controller Trace Support * * Copyright (C) 2014 Texas Instruments Incorporated - https://www.ti.com @@ -32,8 +32,10 @@ DECLARE_EVENT_CLASS(dwc3_log_io, __entry->offset = offset; __entry->value = value; ), - TP_printk("addr %p value %08x", __entry->base + __entry->offset, - __entry->value) + TP_printk("addr %p offset %04x value %08x", + __entry->base + __entry->offset, + __entry->offset, + __entry->value) ); DEFINE_EVENT(dwc3_log_io, dwc3_readl, diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c index 2d115353424c..8bb25773b61e 100644 --- a/drivers/usb/gadget/config.c +++ b/drivers/usb/gadget/config.c @@ -194,9 +194,13 @@ EXPORT_SYMBOL_GPL(usb_assign_descriptors); void usb_free_all_descriptors(struct usb_function *f) { usb_free_descriptors(f->fs_descriptors); + f->fs_descriptors = NULL; usb_free_descriptors(f->hs_descriptors); + f->hs_descriptors = NULL; usb_free_descriptors(f->ss_descriptors); + f->ss_descriptors = NULL; usb_free_descriptors(f->ssp_descriptors); + f->ssp_descriptors = NULL; } EXPORT_SYMBOL_GPL(usb_free_all_descriptors); diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 801a8b668a35..bf109191659a 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -2640,6 +2640,7 @@ static int __ffs_data_got_strings(struct ffs_data *ffs, do { /* lang_count > 0 so we can use do-while */ unsigned needed = needed_count; + u32 str_per_lang = str_count; if (len < 3) goto error_free; @@ -2675,7 +2676,7 @@ static int __ffs_data_got_strings(struct ffs_data *ffs, data += length + 1; len -= length + 1; - } while (--str_count); + } while (--str_per_lang); s->id = 0; /* terminator */ s->s = NULL; @@ -3826,14 +3827,9 @@ static char *ffs_prepare_buffer(const char __user *buf, size_t len) if (!len) return NULL; - data = kmalloc(len, GFP_KERNEL); - if (!data) - return ERR_PTR(-ENOMEM); - - if (copy_from_user(data, buf, len)) { - kfree(data); - return ERR_PTR(-EFAULT); - } + data = memdup_user(buf, len); + if (IS_ERR(data)) + return ERR_PTR(PTR_ERR(data)); pr_vdebug("Buffer from user space:\n"); ffs_dump_mem("", data, len); diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index 950c9435beec..4a4703634a2a 100644 --- a/drivers/usb/gadget/function/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -351,8 +351,6 @@ static inline struct fsg_dev *fsg_from_func(struct usb_function *f) return container_of(f, struct fsg_dev, function); } -typedef void (*fsg_routine_t)(struct fsg_dev *); - static int exception_in_progress(struct fsg_common *common) { return common->state > FSG_STATE_NORMAL; diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c index 61ce8e68f7a3..f47fdc1fa7f1 100644 --- a/drivers/usb/gadget/function/f_printer.c +++ b/drivers/usb/gadget/function/f_printer.c @@ -825,7 +825,7 @@ set_printer_interface(struct printer_dev *dev) result = usb_ep_enable(dev->out_ep); if (result != 0) { - DBG(dev, "enable %s --> %d\n", dev->in_ep->name, result); + DBG(dev, "enable %s --> %d\n", dev->out_ep->name, result); goto done; } diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index 560382e0a8f3..d04707580068 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -19,6 +19,12 @@ #include "u_audio.h" #include "u_uac1.h" +/* UAC1 spec: 3.7.2.3 Audio Channel Cluster Format */ +#define UAC1_CHANNEL_MASK 0x0FFF + +#define EPIN_EN(_opts) ((_opts)->p_chmask != 0) +#define EPOUT_EN(_opts) ((_opts)->c_chmask != 0) + struct f_uac1 { struct g_audio g_audio; u8 ac_intf, as_in_intf, as_out_intf; @@ -30,6 +36,11 @@ static inline struct f_uac1 *func_to_uac1(struct usb_function *f) return container_of(f, struct f_uac1, g_audio.func); } +static inline struct f_uac1_opts *g_audio_to_uac1_opts(struct g_audio *audio) +{ + return container_of(audio->func.fi, struct f_uac1_opts, func_inst); +} + /* * DESCRIPTORS ... most are static, but strings and full * configuration descriptors are built on demand. @@ -42,11 +53,6 @@ static inline struct f_uac1 *func_to_uac1(struct usb_function *f) * USB-OUT -> IT_1 -> OT_2 -> ALSA_Capture * ALSA_Playback -> IT_3 -> OT_4 -> USB-IN */ -#define F_AUDIO_AC_INTERFACE 0 -#define F_AUDIO_AS_OUT_INTERFACE 1 -#define F_AUDIO_AS_IN_INTERFACE 2 -/* Number of streaming interfaces */ -#define F_AUDIO_NUM_INTERFACES 2 /* B.3.1 Standard AC Interface Descriptor */ static struct usb_interface_descriptor ac_interface_desc = { @@ -57,73 +63,47 @@ static struct usb_interface_descriptor ac_interface_desc = { .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, }; -/* - * The number of AudioStreaming and MIDIStreaming interfaces - * in the Audio Interface Collection - */ -DECLARE_UAC_AC_HEADER_DESCRIPTOR(2); - -#define UAC_DT_AC_HEADER_LENGTH UAC_DT_AC_HEADER_SIZE(F_AUDIO_NUM_INTERFACES) -/* 2 input terminals and 2 output terminals */ -#define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH \ - + 2*UAC_DT_INPUT_TERMINAL_SIZE + 2*UAC_DT_OUTPUT_TERMINAL_SIZE) /* B.3.2 Class-Specific AC Interface Descriptor */ -static struct uac1_ac_header_descriptor_2 ac_header_desc = { - .bLength = UAC_DT_AC_HEADER_LENGTH, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = UAC_HEADER, - .bcdADC = cpu_to_le16(0x0100), - .wTotalLength = cpu_to_le16(UAC_DT_TOTAL_LENGTH), - .bInCollection = F_AUDIO_NUM_INTERFACES, - .baInterfaceNr = { - /* Interface number of the AudioStream interfaces */ - [0] = 1, - [1] = 2, - } -}; +static struct uac1_ac_header_descriptor *ac_header_desc; -#define USB_OUT_IT_ID 1 static struct uac_input_terminal_descriptor usb_out_it_desc = { .bLength = UAC_DT_INPUT_TERMINAL_SIZE, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubtype = UAC_INPUT_TERMINAL, - .bTerminalID = USB_OUT_IT_ID, + /* .bTerminalID = DYNAMIC */ .wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING), .bAssocTerminal = 0, .wChannelConfig = cpu_to_le16(0x3), }; -#define IO_OUT_OT_ID 2 static struct uac1_output_terminal_descriptor io_out_ot_desc = { .bLength = UAC_DT_OUTPUT_TERMINAL_SIZE, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, - .bTerminalID = IO_OUT_OT_ID, + /* .bTerminalID = DYNAMIC */ .wTerminalType = cpu_to_le16(UAC_OUTPUT_TERMINAL_SPEAKER), .bAssocTerminal = 0, - .bSourceID = USB_OUT_IT_ID, + /* .bSourceID = DYNAMIC */ }; -#define IO_IN_IT_ID 3 static struct uac_input_terminal_descriptor io_in_it_desc = { .bLength = UAC_DT_INPUT_TERMINAL_SIZE, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubtype = UAC_INPUT_TERMINAL, - .bTerminalID = IO_IN_IT_ID, + /* .bTerminalID = DYNAMIC */ .wTerminalType = cpu_to_le16(UAC_INPUT_TERMINAL_MICROPHONE), .bAssocTerminal = 0, .wChannelConfig = cpu_to_le16(0x3), }; -#define USB_IN_OT_ID 4 static struct uac1_output_terminal_descriptor usb_in_ot_desc = { .bLength = UAC_DT_OUTPUT_TERMINAL_SIZE, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, - .bTerminalID = USB_IN_OT_ID, + /* .bTerminalID = DYNAMIC */ .wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING), .bAssocTerminal = 0, - .bSourceID = IO_IN_IT_ID, + /* .bSourceID = DYNAMIC */ }; /* B.4.1 Standard AS Interface Descriptor */ @@ -168,7 +148,7 @@ static struct uac1_as_header_descriptor as_out_header_desc = { .bLength = UAC_DT_AS_HEADER_SIZE, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubtype = UAC_AS_GENERAL, - .bTerminalLink = USB_OUT_IT_ID, + /* .bTerminalLink = DYNAMIC */ .bDelay = 1, .wFormatTag = cpu_to_le16(UAC_FORMAT_TYPE_I_PCM), }; @@ -177,7 +157,7 @@ static struct uac1_as_header_descriptor as_in_header_desc = { .bLength = UAC_DT_AS_HEADER_SIZE, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubtype = UAC_AS_GENERAL, - .bTerminalLink = USB_IN_OT_ID, + /* .bTerminalLink = DYNAMIC */ .bDelay = 1, .wFormatTag = cpu_to_le16(UAC_FORMAT_TYPE_I_PCM), }; @@ -505,11 +485,144 @@ static void f_audio_disable(struct usb_function *f) /*-------------------------------------------------------------------------*/ +static struct +uac1_ac_header_descriptor *build_ac_header_desc(struct f_uac1_opts *opts) +{ + struct uac1_ac_header_descriptor *ac_desc; + int ac_header_desc_size; + int num_ifaces = 0; + + if (EPOUT_EN(opts)) + num_ifaces++; + if (EPIN_EN(opts)) + num_ifaces++; + + ac_header_desc_size = UAC_DT_AC_HEADER_SIZE(num_ifaces); + + ac_desc = kzalloc(ac_header_desc_size, GFP_KERNEL); + if (!ac_desc) + return NULL; + + ac_desc->bLength = ac_header_desc_size; + ac_desc->bDescriptorType = USB_DT_CS_INTERFACE; + ac_desc->bDescriptorSubtype = UAC_HEADER; + ac_desc->bcdADC = cpu_to_le16(0x0100); + ac_desc->bInCollection = num_ifaces; + + /* wTotalLength and baInterfaceNr will be defined later */ + + return ac_desc; +} + +/* Use macro to overcome line length limitation */ +#define USBDHDR(p) (struct usb_descriptor_header *)(p) + +static void setup_descriptor(struct f_uac1_opts *opts) +{ + /* patch descriptors */ + int i = 1; /* ID's start with 1 */ + + if (EPOUT_EN(opts)) + usb_out_it_desc.bTerminalID = i++; + if (EPIN_EN(opts)) + io_in_it_desc.bTerminalID = i++; + if (EPOUT_EN(opts)) + io_out_ot_desc.bTerminalID = i++; + if (EPIN_EN(opts)) + usb_in_ot_desc.bTerminalID = i++; + + usb_in_ot_desc.bSourceID = io_in_it_desc.bTerminalID; + io_out_ot_desc.bSourceID = usb_out_it_desc.bTerminalID; + + as_out_header_desc.bTerminalLink = usb_out_it_desc.bTerminalID; + as_in_header_desc.bTerminalLink = usb_in_ot_desc.bTerminalID; + + ac_header_desc->wTotalLength = cpu_to_le16(ac_header_desc->bLength); + + if (EPIN_EN(opts)) { + u16 len = le16_to_cpu(ac_header_desc->wTotalLength); + + len += sizeof(usb_in_ot_desc); + len += sizeof(io_in_it_desc); + ac_header_desc->wTotalLength = cpu_to_le16(len); + } + if (EPOUT_EN(opts)) { + u16 len = le16_to_cpu(ac_header_desc->wTotalLength); + + len += sizeof(usb_out_it_desc); + len += sizeof(io_out_ot_desc); + ac_header_desc->wTotalLength = cpu_to_le16(len); + } + + i = 0; + f_audio_desc[i++] = USBDHDR(&ac_interface_desc); + f_audio_desc[i++] = USBDHDR(ac_header_desc); + + if (EPOUT_EN(opts)) { + f_audio_desc[i++] = USBDHDR(&usb_out_it_desc); + f_audio_desc[i++] = USBDHDR(&io_out_ot_desc); + } + + if (EPIN_EN(opts)) { + f_audio_desc[i++] = USBDHDR(&io_in_it_desc); + f_audio_desc[i++] = USBDHDR(&usb_in_ot_desc); + } + + if (EPOUT_EN(opts)) { + f_audio_desc[i++] = USBDHDR(&as_out_interface_alt_0_desc); + f_audio_desc[i++] = USBDHDR(&as_out_interface_alt_1_desc); + f_audio_desc[i++] = USBDHDR(&as_out_header_desc); + f_audio_desc[i++] = USBDHDR(&as_out_type_i_desc); + f_audio_desc[i++] = USBDHDR(&as_out_ep_desc); + f_audio_desc[i++] = USBDHDR(&as_iso_out_desc); + } + if (EPIN_EN(opts)) { + f_audio_desc[i++] = USBDHDR(&as_in_interface_alt_0_desc); + f_audio_desc[i++] = USBDHDR(&as_in_interface_alt_1_desc); + f_audio_desc[i++] = USBDHDR(&as_in_header_desc); + f_audio_desc[i++] = USBDHDR(&as_in_type_i_desc); + f_audio_desc[i++] = USBDHDR(&as_in_ep_desc); + f_audio_desc[i++] = USBDHDR(&as_iso_in_desc); + } + f_audio_desc[i] = NULL; +} + +static int f_audio_validate_opts(struct g_audio *audio, struct device *dev) +{ + struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio); + + if (!opts->p_chmask && !opts->c_chmask) { + dev_err(dev, "Error: no playback and capture channels\n"); + return -EINVAL; + } else if (opts->p_chmask & ~UAC1_CHANNEL_MASK) { + dev_err(dev, "Error: unsupported playback channels mask\n"); + return -EINVAL; + } else if (opts->c_chmask & ~UAC1_CHANNEL_MASK) { + dev_err(dev, "Error: unsupported capture channels mask\n"); + return -EINVAL; + } else if ((opts->p_ssize < 1) || (opts->p_ssize > 4)) { + dev_err(dev, "Error: incorrect playback sample size\n"); + return -EINVAL; + } else if ((opts->c_ssize < 1) || (opts->c_ssize > 4)) { + dev_err(dev, "Error: incorrect capture sample size\n"); + return -EINVAL; + } else if (!opts->p_srate) { + dev_err(dev, "Error: incorrect playback sampling rate\n"); + return -EINVAL; + } else if (!opts->c_srate) { + dev_err(dev, "Error: incorrect capture sampling rate\n"); + return -EINVAL; + } + + return 0; +} + /* audio function driver setup/binding */ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_composite_dev *cdev = c->cdev; struct usb_gadget *gadget = cdev->gadget; + struct device *dev = &gadget->dev; struct f_uac1 *uac1 = func_to_uac1(f); struct g_audio *audio = func_to_g_audio(f); struct f_uac1_opts *audio_opts; @@ -517,13 +630,23 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) struct usb_string *us; u8 *sam_freq; int rate; + int ba_iface_id; int status; + status = f_audio_validate_opts(audio, dev); + if (status) + return status; + audio_opts = container_of(f->fi, struct f_uac1_opts, func_inst); us = usb_gstrings_attach(cdev, uac1_strings, ARRAY_SIZE(strings_uac1)); if (IS_ERR(us)) return PTR_ERR(us); + + ac_header_desc = build_ac_header_desc(audio_opts); + if (!ac_header_desc) + return -ENOMEM; + ac_interface_desc.iInterface = us[STR_AC_IF].id; usb_out_it_desc.iTerminal = us[STR_USB_OUT_IT].id; usb_out_it_desc.iChannelNames = us[STR_USB_OUT_IT_CH_NAMES].id; @@ -564,40 +687,52 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) uac1->ac_intf = status; uac1->ac_alt = 0; - status = usb_interface_id(c, f); - if (status < 0) - goto fail; - as_out_interface_alt_0_desc.bInterfaceNumber = status; - as_out_interface_alt_1_desc.bInterfaceNumber = status; - ac_header_desc.baInterfaceNr[0] = status; - uac1->as_out_intf = status; - uac1->as_out_alt = 0; + ba_iface_id = 0; - status = usb_interface_id(c, f); - if (status < 0) - goto fail; - as_in_interface_alt_0_desc.bInterfaceNumber = status; - as_in_interface_alt_1_desc.bInterfaceNumber = status; - ac_header_desc.baInterfaceNr[1] = status; - uac1->as_in_intf = status; - uac1->as_in_alt = 0; + if (EPOUT_EN(audio_opts)) { + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + as_out_interface_alt_0_desc.bInterfaceNumber = status; + as_out_interface_alt_1_desc.bInterfaceNumber = status; + ac_header_desc->baInterfaceNr[ba_iface_id++] = status; + uac1->as_out_intf = status; + uac1->as_out_alt = 0; + } + + if (EPIN_EN(audio_opts)) { + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + as_in_interface_alt_0_desc.bInterfaceNumber = status; + as_in_interface_alt_1_desc.bInterfaceNumber = status; + ac_header_desc->baInterfaceNr[ba_iface_id++] = status; + uac1->as_in_intf = status; + uac1->as_in_alt = 0; + } audio->gadget = gadget; status = -ENODEV; /* allocate instance-specific endpoints */ - ep = usb_ep_autoconfig(cdev->gadget, &as_out_ep_desc); - if (!ep) - goto fail; - audio->out_ep = ep; - audio->out_ep->desc = &as_out_ep_desc; + if (EPOUT_EN(audio_opts)) { + ep = usb_ep_autoconfig(cdev->gadget, &as_out_ep_desc); + if (!ep) + goto fail; + audio->out_ep = ep; + audio->out_ep->desc = &as_out_ep_desc; + } - ep = usb_ep_autoconfig(cdev->gadget, &as_in_ep_desc); - if (!ep) - goto fail; - audio->in_ep = ep; - audio->in_ep->desc = &as_in_ep_desc; + if (EPIN_EN(audio_opts)) { + ep = usb_ep_autoconfig(cdev->gadget, &as_in_ep_desc); + if (!ep) + goto fail; + audio->in_ep = ep; + audio->in_ep->desc = &as_in_ep_desc; + } + + setup_descriptor(audio_opts); /* copy descriptors, and track endpoint copies */ status = usb_assign_descriptors(f, f_audio_desc, f_audio_desc, NULL, @@ -624,6 +759,8 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) err_card_register: usb_free_all_descriptors(f); fail: + kfree(ac_header_desc); + ac_header_desc = NULL; return status; } @@ -766,6 +903,9 @@ static void f_audio_unbind(struct usb_configuration *c, struct usb_function *f) g_audio_cleanup(audio); usb_free_all_descriptors(f); + kfree(ac_header_desc); + ac_header_desc = NULL; + audio->gadget = NULL; } diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 6f03e944e0e3..7aa4c8bc5a1a 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -14,6 +14,9 @@ #include "u_audio.h" #include "u_uac2.h" +/* UAC2 spec: 4.1 Audio Channel Cluster Descriptor */ +#define UAC2_CHANNEL_MASK 0x07FFFFFF + /* * The driver implements a simple UAC_2 topology. * USB-OUT -> IT_1 -> OT_3 -> ALSA_Capture @@ -284,6 +287,24 @@ static struct usb_endpoint_descriptor hs_epout_desc = { .bInterval = 4, }; +static struct usb_endpoint_descriptor ss_epout_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, + /* .wMaxPacketSize = DYNAMIC */ + .bInterval = 4, +}; + +static struct usb_ss_ep_comp_descriptor ss_epout_desc_comp = { + .bLength = sizeof(ss_epout_desc_comp), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + .bMaxBurst = 0, + .bmAttributes = 0, + /* wBytesPerInterval = DYNAMIC */ +}; + /* CS AS ISO OUT Endpoint */ static struct uac2_iso_endpoint_descriptor as_iso_out_desc = { .bLength = sizeof as_iso_out_desc, @@ -361,6 +382,24 @@ static struct usb_endpoint_descriptor hs_epin_desc = { .bInterval = 4, }; +static struct usb_endpoint_descriptor ss_epin_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, + /* .wMaxPacketSize = DYNAMIC */ + .bInterval = 4, +}; + +static struct usb_ss_ep_comp_descriptor ss_epin_desc_comp = { + .bLength = sizeof(ss_epin_desc_comp), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + .bMaxBurst = 0, + .bmAttributes = 0, + /* wBytesPerInterval = DYNAMIC */ +}; + /* CS AS ISO IN Endpoint */ static struct uac2_iso_endpoint_descriptor as_iso_in_desc = { .bLength = sizeof as_iso_in_desc, @@ -433,6 +472,38 @@ static struct usb_descriptor_header *hs_audio_desc[] = { NULL, }; +static struct usb_descriptor_header *ss_audio_desc[] = { + (struct usb_descriptor_header *)&iad_desc, + (struct usb_descriptor_header *)&std_ac_if_desc, + + (struct usb_descriptor_header *)&ac_hdr_desc, + (struct usb_descriptor_header *)&in_clk_src_desc, + (struct usb_descriptor_header *)&out_clk_src_desc, + (struct usb_descriptor_header *)&usb_out_it_desc, + (struct usb_descriptor_header *)&io_in_it_desc, + (struct usb_descriptor_header *)&usb_in_ot_desc, + (struct usb_descriptor_header *)&io_out_ot_desc, + + (struct usb_descriptor_header *)&std_as_out_if0_desc, + (struct usb_descriptor_header *)&std_as_out_if1_desc, + + (struct usb_descriptor_header *)&as_out_hdr_desc, + (struct usb_descriptor_header *)&as_out_fmt1_desc, + (struct usb_descriptor_header *)&ss_epout_desc, + (struct usb_descriptor_header *)&ss_epout_desc_comp, + (struct usb_descriptor_header *)&as_iso_out_desc, + + (struct usb_descriptor_header *)&std_as_in_if0_desc, + (struct usb_descriptor_header *)&std_as_in_if1_desc, + + (struct usb_descriptor_header *)&as_in_hdr_desc, + (struct usb_descriptor_header *)&as_in_fmt1_desc, + (struct usb_descriptor_header *)&ss_epin_desc, + (struct usb_descriptor_header *)&ss_epin_desc_comp, + (struct usb_descriptor_header *)&as_iso_in_desc, + NULL, +}; + struct cntrl_cur_lay3 { __le32 dCUR; }; @@ -459,6 +530,7 @@ static int set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts, break; case USB_SPEED_HIGH: + case USB_SPEED_SUPER: max_size_ep = 1024; factor = 8000; break; @@ -488,6 +560,72 @@ static int set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts, /* Use macro to overcome line length limitation */ #define USBDHDR(p) (struct usb_descriptor_header *)(p) +static void setup_headers(struct f_uac2_opts *opts, + struct usb_descriptor_header **headers, + enum usb_device_speed speed) +{ + struct usb_ss_ep_comp_descriptor *epout_desc_comp = NULL; + struct usb_ss_ep_comp_descriptor *epin_desc_comp = NULL; + struct usb_endpoint_descriptor *epout_desc; + struct usb_endpoint_descriptor *epin_desc; + int i; + + switch (speed) { + case USB_SPEED_FULL: + epout_desc = &fs_epout_desc; + epin_desc = &fs_epin_desc; + break; + case USB_SPEED_HIGH: + epout_desc = &hs_epout_desc; + epin_desc = &hs_epin_desc; + break; + default: + epout_desc = &ss_epout_desc; + epin_desc = &ss_epin_desc; + epout_desc_comp = &ss_epout_desc_comp; + epin_desc_comp = &ss_epin_desc_comp; + } + + i = 0; + headers[i++] = USBDHDR(&iad_desc); + headers[i++] = USBDHDR(&std_ac_if_desc); + headers[i++] = USBDHDR(&ac_hdr_desc); + if (EPIN_EN(opts)) + headers[i++] = USBDHDR(&in_clk_src_desc); + if (EPOUT_EN(opts)) { + headers[i++] = USBDHDR(&out_clk_src_desc); + headers[i++] = USBDHDR(&usb_out_it_desc); + } + if (EPIN_EN(opts)) { + headers[i++] = USBDHDR(&io_in_it_desc); + headers[i++] = USBDHDR(&usb_in_ot_desc); + } + if (EPOUT_EN(opts)) { + headers[i++] = USBDHDR(&io_out_ot_desc); + headers[i++] = USBDHDR(&std_as_out_if0_desc); + headers[i++] = USBDHDR(&std_as_out_if1_desc); + headers[i++] = USBDHDR(&as_out_hdr_desc); + headers[i++] = USBDHDR(&as_out_fmt1_desc); + headers[i++] = USBDHDR(epout_desc); + if (epout_desc_comp) + headers[i++] = USBDHDR(epout_desc_comp); + + headers[i++] = USBDHDR(&as_iso_out_desc); + } + if (EPIN_EN(opts)) { + headers[i++] = USBDHDR(&std_as_in_if0_desc); + headers[i++] = USBDHDR(&std_as_in_if1_desc); + headers[i++] = USBDHDR(&as_in_hdr_desc); + headers[i++] = USBDHDR(&as_in_fmt1_desc); + headers[i++] = USBDHDR(epin_desc); + if (epin_desc_comp) + headers[i++] = USBDHDR(epin_desc_comp); + + headers[i++] = USBDHDR(&as_iso_in_desc); + } + headers[i] = NULL; +} + static void setup_descriptor(struct f_uac2_opts *opts) { /* patch descriptors */ @@ -537,71 +675,39 @@ static void setup_descriptor(struct f_uac2_opts *opts) iad_desc.bInterfaceCount++; } - i = 0; - fs_audio_desc[i++] = USBDHDR(&iad_desc); - fs_audio_desc[i++] = USBDHDR(&std_ac_if_desc); - fs_audio_desc[i++] = USBDHDR(&ac_hdr_desc); - if (EPIN_EN(opts)) - fs_audio_desc[i++] = USBDHDR(&in_clk_src_desc); - if (EPOUT_EN(opts)) { - fs_audio_desc[i++] = USBDHDR(&out_clk_src_desc); - fs_audio_desc[i++] = USBDHDR(&usb_out_it_desc); - } - if (EPIN_EN(opts)) { - fs_audio_desc[i++] = USBDHDR(&io_in_it_desc); - fs_audio_desc[i++] = USBDHDR(&usb_in_ot_desc); - } - if (EPOUT_EN(opts)) { - fs_audio_desc[i++] = USBDHDR(&io_out_ot_desc); - fs_audio_desc[i++] = USBDHDR(&std_as_out_if0_desc); - fs_audio_desc[i++] = USBDHDR(&std_as_out_if1_desc); - fs_audio_desc[i++] = USBDHDR(&as_out_hdr_desc); - fs_audio_desc[i++] = USBDHDR(&as_out_fmt1_desc); - fs_audio_desc[i++] = USBDHDR(&fs_epout_desc); - fs_audio_desc[i++] = USBDHDR(&as_iso_out_desc); - } - if (EPIN_EN(opts)) { - fs_audio_desc[i++] = USBDHDR(&std_as_in_if0_desc); - fs_audio_desc[i++] = USBDHDR(&std_as_in_if1_desc); - fs_audio_desc[i++] = USBDHDR(&as_in_hdr_desc); - fs_audio_desc[i++] = USBDHDR(&as_in_fmt1_desc); - fs_audio_desc[i++] = USBDHDR(&fs_epin_desc); - fs_audio_desc[i++] = USBDHDR(&as_iso_in_desc); - } - fs_audio_desc[i] = NULL; + setup_headers(opts, fs_audio_desc, USB_SPEED_FULL); + setup_headers(opts, hs_audio_desc, USB_SPEED_HIGH); + setup_headers(opts, ss_audio_desc, USB_SPEED_SUPER); +} - i = 0; - hs_audio_desc[i++] = USBDHDR(&iad_desc); - hs_audio_desc[i++] = USBDHDR(&std_ac_if_desc); - hs_audio_desc[i++] = USBDHDR(&ac_hdr_desc); - if (EPIN_EN(opts)) - hs_audio_desc[i++] = USBDHDR(&in_clk_src_desc); - if (EPOUT_EN(opts)) { - hs_audio_desc[i++] = USBDHDR(&out_clk_src_desc); - hs_audio_desc[i++] = USBDHDR(&usb_out_it_desc); +static int afunc_validate_opts(struct g_audio *agdev, struct device *dev) +{ + struct f_uac2_opts *opts = g_audio_to_uac2_opts(agdev); + + if (!opts->p_chmask && !opts->c_chmask) { + dev_err(dev, "Error: no playback and capture channels\n"); + return -EINVAL; + } else if (opts->p_chmask & ~UAC2_CHANNEL_MASK) { + dev_err(dev, "Error: unsupported playback channels mask\n"); + return -EINVAL; + } else if (opts->c_chmask & ~UAC2_CHANNEL_MASK) { + dev_err(dev, "Error: unsupported capture channels mask\n"); + return -EINVAL; + } else if ((opts->p_ssize < 1) || (opts->p_ssize > 4)) { + dev_err(dev, "Error: incorrect playback sample size\n"); + return -EINVAL; + } else if ((opts->c_ssize < 1) || (opts->c_ssize > 4)) { + dev_err(dev, "Error: incorrect capture sample size\n"); + return -EINVAL; + } else if (!opts->p_srate) { + dev_err(dev, "Error: incorrect playback sampling rate\n"); + return -EINVAL; + } else if (!opts->c_srate) { + dev_err(dev, "Error: incorrect capture sampling rate\n"); + return -EINVAL; } - if (EPIN_EN(opts)) { - hs_audio_desc[i++] = USBDHDR(&io_in_it_desc); - hs_audio_desc[i++] = USBDHDR(&usb_in_ot_desc); - } - if (EPOUT_EN(opts)) { - hs_audio_desc[i++] = USBDHDR(&io_out_ot_desc); - hs_audio_desc[i++] = USBDHDR(&std_as_out_if0_desc); - hs_audio_desc[i++] = USBDHDR(&std_as_out_if1_desc); - hs_audio_desc[i++] = USBDHDR(&as_out_hdr_desc); - hs_audio_desc[i++] = USBDHDR(&as_out_fmt1_desc); - hs_audio_desc[i++] = USBDHDR(&hs_epout_desc); - hs_audio_desc[i++] = USBDHDR(&as_iso_out_desc); - } - if (EPIN_EN(opts)) { - hs_audio_desc[i++] = USBDHDR(&std_as_in_if0_desc); - hs_audio_desc[i++] = USBDHDR(&std_as_in_if1_desc); - hs_audio_desc[i++] = USBDHDR(&as_in_hdr_desc); - hs_audio_desc[i++] = USBDHDR(&as_in_fmt1_desc); - hs_audio_desc[i++] = USBDHDR(&hs_epin_desc); - hs_audio_desc[i++] = USBDHDR(&as_iso_in_desc); - } - hs_audio_desc[i] = NULL; + + return 0; } static int @@ -612,11 +718,13 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) struct usb_composite_dev *cdev = cfg->cdev; struct usb_gadget *gadget = cdev->gadget; struct device *dev = &gadget->dev; - struct f_uac2_opts *uac2_opts; + struct f_uac2_opts *uac2_opts = g_audio_to_uac2_opts(agdev); struct usb_string *us; int ret; - uac2_opts = container_of(fn->fi, struct f_uac2_opts, func_inst); + ret = afunc_validate_opts(agdev, dev); + if (ret) + return ret; us = usb_gstrings_attach(cdev, fn_strings, ARRAY_SIZE(strings_fn)); if (IS_ERR(us)) @@ -716,6 +824,20 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) return ret; } + ret = set_ep_max_packet_size(uac2_opts, &ss_epin_desc, USB_SPEED_SUPER, + true); + if (ret < 0) { + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); + return ret; + } + + ret = set_ep_max_packet_size(uac2_opts, &ss_epout_desc, USB_SPEED_SUPER, + false); + if (ret < 0) { + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); + return ret; + } + if (EPOUT_EN(uac2_opts)) { agdev->out_ep = usb_ep_autoconfig(gadget, &fs_epout_desc); if (!agdev->out_ep) { @@ -739,13 +861,20 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) le16_to_cpu(fs_epout_desc.wMaxPacketSize), le16_to_cpu(hs_epout_desc.wMaxPacketSize)); + agdev->in_ep_maxpsize = max_t(u16, agdev->in_ep_maxpsize, + le16_to_cpu(ss_epin_desc.wMaxPacketSize)); + agdev->out_ep_maxpsize = max_t(u16, agdev->out_ep_maxpsize, + le16_to_cpu(ss_epout_desc.wMaxPacketSize)); + hs_epout_desc.bEndpointAddress = fs_epout_desc.bEndpointAddress; hs_epin_desc.bEndpointAddress = fs_epin_desc.bEndpointAddress; + ss_epout_desc.bEndpointAddress = fs_epout_desc.bEndpointAddress; + ss_epin_desc.bEndpointAddress = fs_epin_desc.bEndpointAddress; setup_descriptor(uac2_opts); - ret = usb_assign_descriptors(fn, fs_audio_desc, hs_audio_desc, NULL, - NULL); + ret = usb_assign_descriptors(fn, fs_audio_desc, hs_audio_desc, ss_audio_desc, + ss_audio_desc); if (ret) return ret; diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 44b4352a2676..f48a00e49794 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -633,7 +633,12 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) uvc_hs_streaming_ep.wMaxPacketSize = cpu_to_le16(max_packet_size | ((max_packet_mult - 1) << 11)); - uvc_hs_streaming_ep.bInterval = opts->streaming_interval; + + /* A high-bandwidth endpoint must specify a bInterval value of 1 */ + if (max_packet_mult > 1) + uvc_hs_streaming_ep.bInterval = 1; + else + uvc_hs_streaming_ep.bInterval = opts->streaming_interval; uvc_ss_streaming_ep.wMaxPacketSize = cpu_to_le16(max_packet_size); uvc_ss_streaming_ep.bInterval = opts->streaming_interval; @@ -817,6 +822,7 @@ static struct usb_function_instance *uvc_alloc_inst(void) pd->bmControls[0] = 1; pd->bmControls[1] = 0; pd->iProcessing = 0; + pd->bmVideoStandards = 0; od = &opts->uvc_output_terminal; od->bLength = UVC_DT_OUTPUT_TERMINAL_SIZE; diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c index 265c4d805f81..5fbceee897a3 100644 --- a/drivers/usb/gadget/function/u_audio.c +++ b/drivers/usb/gadget/function/u_audio.c @@ -549,15 +549,15 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name, if (err < 0) goto snd_fail; - strlcpy(pcm->name, pcm_name, sizeof(pcm->name)); + strscpy(pcm->name, pcm_name, sizeof(pcm->name)); pcm->private_data = uac; uac->pcm = pcm; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &uac_pcm_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &uac_pcm_ops); - strlcpy(card->driver, card_name, sizeof(card->driver)); - strlcpy(card->shortname, card_name, sizeof(card->shortname)); + strscpy(card->driver, card_name, sizeof(card->driver)); + strscpy(card->shortname, card_name, sizeof(card->shortname)); sprintf(card->longname, "%s %i", card_name, card->dev->id); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index 00fb58e50a15..cd28dec837dd 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -231,7 +231,7 @@ static struct config_item *uvcg_control_header_make(struct config_group *group, h->desc.bLength = UVC_DT_HEADER_SIZE(1); h->desc.bDescriptorType = USB_DT_CS_INTERFACE; h->desc.bDescriptorSubType = UVC_VC_HEADER; - h->desc.bcdUVC = cpu_to_le16(0x0100); + h->desc.bcdUVC = cpu_to_le16(0x0110); h->desc.dwClockFrequency = cpu_to_le32(48000000); config_item_init_type_name(&h->item, name, &uvcg_control_header_type); diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index 71e7d10dd76b..cd8e2737947b 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -498,7 +498,8 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req) iocb->private = NULL; /* aio_complete() reports bytes-transferred _and_ faults */ - iocb->ki_complete(iocb, req->actual ? req->actual : req->status, + iocb->ki_complete(iocb, + req->actual ? req->actual : (long)req->status, req->status); } else { /* ep_copy_to_user() won't report both; we hide some faults */ diff --git a/drivers/usb/gadget/legacy/mass_storage.c b/drivers/usb/gadget/legacy/mass_storage.c index 9ed22c5fb7fe..ac1741126619 100644 --- a/drivers/usb/gadget/legacy/mass_storage.c +++ b/drivers/usb/gadget/legacy/mass_storage.c @@ -175,8 +175,10 @@ static int msg_bind(struct usb_composite_dev *cdev) struct usb_descriptor_header *usb_desc; usb_desc = usb_otg_descriptor_alloc(cdev->gadget); - if (!usb_desc) + if (!usb_desc) { + status = -ENOMEM; goto fail_string_ids; + } usb_otg_descriptor_init(cdev->gadget, usb_desc); otg_desc[0] = usb_desc; otg_desc[1] = NULL; diff --git a/drivers/usb/gadget/legacy/multi.c b/drivers/usb/gadget/legacy/multi.c index ec9749845660..8db5c91ae47d 100644 --- a/drivers/usb/gadget/legacy/multi.c +++ b/drivers/usb/gadget/legacy/multi.c @@ -182,7 +182,7 @@ static int rndis_do_config(struct usb_configuration *c) return ret; } -static __ref int rndis_config_register(struct usb_composite_dev *cdev) +static int rndis_config_register(struct usb_composite_dev *cdev) { static struct usb_configuration config = { .bConfigurationValue = MULTI_RNDIS_CONFIG_NUM, @@ -197,7 +197,7 @@ static __ref int rndis_config_register(struct usb_composite_dev *cdev) #else -static __ref int rndis_config_register(struct usb_composite_dev *cdev) +static int rndis_config_register(struct usb_composite_dev *cdev) { return 0; } @@ -265,7 +265,7 @@ static int cdc_do_config(struct usb_configuration *c) return ret; } -static __ref int cdc_config_register(struct usb_composite_dev *cdev) +static int cdc_config_register(struct usb_composite_dev *cdev) { static struct usb_configuration config = { .bConfigurationValue = MULTI_CDC_CONFIG_NUM, @@ -280,7 +280,7 @@ static __ref int cdc_config_register(struct usb_composite_dev *cdev) #else -static __ref int cdc_config_register(struct usb_composite_dev *cdev) +static int cdc_config_register(struct usb_composite_dev *cdev) { return 0; } @@ -291,7 +291,7 @@ static __ref int cdc_config_register(struct usb_composite_dev *cdev) /****************************** Gadget Bind ******************************/ -static int __ref multi_bind(struct usb_composite_dev *cdev) +static int multi_bind(struct usb_composite_dev *cdev) { struct usb_gadget *gadget = cdev->gadget; #ifdef CONFIG_USB_G_MULTI_CDC @@ -399,8 +399,10 @@ static int __ref multi_bind(struct usb_composite_dev *cdev) struct usb_descriptor_header *usb_desc; usb_desc = usb_otg_descriptor_alloc(gadget); - if (!usb_desc) + if (!usb_desc) { + status = -ENOMEM; goto fail_string_ids; + } usb_otg_descriptor_init(gadget, usb_desc); otg_desc[0] = usb_desc; otg_desc[1] = NULL; diff --git a/drivers/usb/gadget/legacy/webcam.c b/drivers/usb/gadget/legacy/webcam.c index a9f8eb8e1c76..94e22867da1d 100644 --- a/drivers/usb/gadget/legacy/webcam.c +++ b/drivers/usb/gadget/legacy/webcam.c @@ -90,7 +90,7 @@ static const struct UVC_HEADER_DESCRIPTOR(1) uvc_control_header = { .bLength = UVC_DT_HEADER_SIZE(1), .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = UVC_VC_HEADER, - .bcdUVC = cpu_to_le16(0x0100), + .bcdUVC = cpu_to_le16(0x0110), .wTotalLength = 0, /* dynamic */ .dwClockFrequency = cpu_to_le32(48000000), .bInCollection = 0, /* dynamic */ @@ -125,6 +125,7 @@ static const struct uvc_processing_unit_descriptor uvc_processing = { .bmControls[0] = 1, .bmControls[1] = 0, .iProcessing = 0, + .bmVideoStandards = 0, }; static const struct uvc_output_terminal_descriptor uvc_output_terminal = { diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/aspeed-vhub/core.c index be7bb64e3594..d11d3d14313f 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/core.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/core.c @@ -36,6 +36,7 @@ void ast_vhub_done(struct ast_vhub_ep *ep, struct ast_vhub_req *req, int status) { bool internal = req->internal; + struct ast_vhub *vhub = ep->vhub; EPVDBG(ep, "completing request @%p, status %d\n", req, status); @@ -46,7 +47,7 @@ void ast_vhub_done(struct ast_vhub_ep *ep, struct ast_vhub_req *req, if (req->req.dma) { if (!WARN_ON(!ep->dev)) - usb_gadget_unmap_request(&ep->dev->gadget, + usb_gadget_unmap_request_by_dev(&vhub->pdev->dev, &req->req, ep->epn.is_in); req->req.dma = 0; } diff --git a/drivers/usb/gadget/udc/aspeed-vhub/epn.c b/drivers/usb/gadget/udc/aspeed-vhub/epn.c index 02d8bfae58fb..cb164c615e6f 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/epn.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/epn.c @@ -376,7 +376,7 @@ static int ast_vhub_epn_queue(struct usb_ep* u_ep, struct usb_request *u_req, if (ep->epn.desc_mode || ((((unsigned long)u_req->buf & 7) == 0) && (ep->epn.is_in || !(u_req->length & (u_ep->maxpacket - 1))))) { - rc = usb_gadget_map_request(&ep->dev->gadget, u_req, + rc = usb_gadget_map_request_by_dev(&vhub->pdev->dev, u_req, ep->epn.is_in); if (rc) { dev_warn(&vhub->pdev->dev, diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index 57067763b100..7db773c87379 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -903,6 +903,21 @@ static int dummy_pullup(struct usb_gadget *_gadget, int value) spin_lock_irqsave(&dum->lock, flags); dum->pullup = (value != 0); set_link_state(dum_hcd); + if (value == 0) { + /* + * Emulate synchronize_irq(): wait for callbacks to finish. + * This seems to be the best place to emulate the call to + * synchronize_irq() that's in usb_gadget_remove_driver(). + * Doing it in dummy_udc_stop() would be too late since it + * is called after the unbind callback and unbind shouldn't + * be invoked until all the other callbacks are finished. + */ + while (dum->callback_usage > 0) { + spin_unlock_irqrestore(&dum->lock, flags); + usleep_range(1000, 2000); + spin_lock_irqsave(&dum->lock, flags); + } + } spin_unlock_irqrestore(&dum->lock, flags); usb_hcd_poll_rh_status(dummy_hcd_to_hcd(dum_hcd)); @@ -1004,14 +1019,6 @@ static int dummy_udc_stop(struct usb_gadget *g) spin_lock_irq(&dum->lock); dum->ints_enabled = 0; stop_activity(dum); - - /* emulate synchronize_irq(): wait for callbacks to finish */ - while (dum->callback_usage > 0) { - spin_unlock_irq(&dum->lock); - usleep_range(1000, 2000); - spin_lock_irq(&dum->lock); - } - dum->driver = NULL; spin_unlock_irq(&dum->lock); @@ -1866,7 +1873,7 @@ static void dummy_timer(struct timer_list *t) /* handle control requests */ if (ep == &dum->ep[0] && ep->setup_stage) { struct usb_ctrlrequest setup; - int value = 1; + int value; setup = *(struct usb_ctrlrequest *) urb->setup_packet; /* paranoia, in case of stale queued data */ diff --git a/drivers/usb/gadget/udc/fotg210-udc.c b/drivers/usb/gadget/udc/fotg210-udc.c index d6ca50f01985..fdca28e72a3b 100644 --- a/drivers/usb/gadget/udc/fotg210-udc.c +++ b/drivers/usb/gadget/udc/fotg210-udc.c @@ -338,15 +338,16 @@ static void fotg210_start_dma(struct fotg210_ep *ep, } else { buffer = req->req.buf + req->req.actual; length = ioread32(ep->fotg210->reg + - FOTG210_FIBCR(ep->epnum - 1)); - length &= FIBCR_BCFX; + FOTG210_FIBCR(ep->epnum - 1)) & FIBCR_BCFX; + if (length > req->req.length - req->req.actual) + length = req->req.length - req->req.actual; } } else { buffer = req->req.buf + req->req.actual; if (req->req.length - req->req.actual > ep->ep.maxpacket) length = ep->ep.maxpacket; else - length = req->req.length; + length = req->req.length - req->req.actual; } d = dma_map_single(dev, buffer, length, @@ -379,8 +380,7 @@ static void fotg210_ep0_queue(struct fotg210_ep *ep, } if (ep->dir_in) { /* if IN */ fotg210_start_dma(ep, req); - if ((req->req.length == req->req.actual) || - (req->req.actual < ep->ep.maxpacket)) + if (req->req.length == req->req.actual) fotg210_done(ep, req, 0); } else { /* OUT */ u32 value = ioread32(ep->fotg210->reg + FOTG210_DMISGR0); @@ -820,7 +820,7 @@ static void fotg210_ep0in(struct fotg210_udc *fotg210) if (req->req.length) fotg210_start_dma(ep, req); - if ((req->req.length - req->req.actual) < ep->ep.maxpacket) + if (req->req.actual == req->req.length) fotg210_done(ep, req, 0); } else { fotg210_set_cxdone(fotg210); @@ -849,12 +849,16 @@ static void fotg210_out_fifo_handler(struct fotg210_ep *ep) { struct fotg210_request *req = list_entry(ep->queue.next, struct fotg210_request, queue); + int disgr1 = ioread32(ep->fotg210->reg + FOTG210_DISGR1); fotg210_start_dma(ep, req); - /* finish out transfer */ + /* Complete the request when it's full or a short packet arrived. + * Like other drivers, short_not_ok isn't handled. + */ + if (req->req.length == req->req.actual || - req->req.actual < ep->ep.maxpacket) + (disgr1 & DISGR1_SPK_INT(ep->epnum - 1))) fotg210_done(ep, req, 0); } @@ -877,6 +881,8 @@ static irqreturn_t fotg210_irq(int irq, void *_fotg210) int_grp2 &= ~int_msk2; if (int_grp2 & DISGR2_USBRST_INT) { + usb_gadget_udc_reset(&fotg210->gadget, + fotg210->driver); value = ioread32(reg); value &= ~DISGR2_USBRST_INT; iowrite32(value, reg); @@ -1027,6 +1033,12 @@ static void fotg210_init(struct fotg210_udc *fotg210) value &= ~DMCR_GLINT_EN; iowrite32(value, fotg210->reg + FOTG210_DMCR); + /* enable only grp2 irqs we handle */ + iowrite32(~(DISGR2_DMA_ERROR | DISGR2_RX0BYTE_INT | DISGR2_TX0BYTE_INT + | DISGR2_ISO_SEQ_ABORT_INT | DISGR2_ISO_SEQ_ERR_INT + | DISGR2_RESM_INT | DISGR2_SUSP_INT | DISGR2_USBRST_INT), + fotg210->reg + FOTG210_DMISGR2); + /* disable all fifo interrupt */ iowrite32(~(u32)0, fotg210->reg + FOTG210_DMISGR1); diff --git a/drivers/usb/gadget/udc/net2272.c b/drivers/usb/gadget/udc/net2272.c index 23a735641c3d..89f479b78d80 100644 --- a/drivers/usb/gadget/udc/net2272.c +++ b/drivers/usb/gadget/udc/net2272.c @@ -539,7 +539,6 @@ net2272_read_fifo(struct net2272_ep *ep, struct net2272_request *req) int count; int tmp; int cleanup = 0; - int status = -1; dev_vdbg(ep->dev->dev, "read_fifo %s actual %d len %d\n", ep->ep.name, req->req.actual, req->req.length); @@ -591,6 +590,8 @@ net2272_read_fifo(struct net2272_ep *ep, struct net2272_request *req) } if (!list_empty(&ep->queue)) { + int status; + req = list_entry(ep->queue.next, struct net2272_request, queue); status = net2272_kick_dma(ep, req); diff --git a/drivers/usb/gadget/udc/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c index a3c1fc924268..9bb7a9d7a2fb 100644 --- a/drivers/usb/gadget/udc/pch_udc.c +++ b/drivers/usb/gadget/udc/pch_udc.c @@ -8,11 +8,12 @@ #include #include #include +#include +#include #include #include #include #include -#include #include #define PCH_VBUS_PERIOD 3000 /* VBUS polling period (msec) */ @@ -331,6 +332,7 @@ struct pch_vbus_gpio_data { * @dma_addr: DMA pool for received * @setup_data: Received setup data * @base_addr: for mapped device memory + * @bar: PCI BAR used for mapped device memory * @cfg_data: current cfg, intf, and alt in use * @vbus_gpio: GPIO informaton for detecting VBUS */ @@ -353,6 +355,7 @@ struct pch_udc_dev { dma_addr_t dma_addr; struct usb_ctrlrequest setup_data; void __iomem *base_addr; + unsigned short bar; struct pch_udc_cfg_data cfg_data; struct pch_vbus_gpio_data vbus_gpio; }; @@ -381,11 +384,8 @@ MODULE_PARM_DESC(speed_fs, "true for Full speed operation"); * @td_data_last: last dma desc. of chain * @queue: associated queue * @dma_going: DMA in progress for request - * @dma_mapped: DMA memory mapped for request * @dma_done: DMA completed for request * @chain_len: chain length - * @buf: Buffer memory for align adjustment - * @dma: DMA memory for align adjustment */ struct pch_udc_request { struct usb_request req; @@ -394,11 +394,8 @@ struct pch_udc_request { struct pch_udc_data_dma_desc *td_data_last; struct list_head queue; unsigned dma_going:1, - dma_mapped:1, dma_done:1; unsigned chain_len; - void *buf; - dma_addr_t dma; }; static inline u32 pch_udc_readl(struct pch_udc_dev *dev, unsigned long reg) @@ -563,12 +560,13 @@ static void pch_udc_clear_disconnect(struct pch_udc_dev *dev) pch_udc_bit_clr(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_RES); } +static void pch_udc_init(struct pch_udc_dev *dev); + /** * pch_udc_reconnect() - This API initializes usb device controller, * and clear the disconnect status. * @dev: Reference to pch_udc_regs structure */ -static void pch_udc_init(struct pch_udc_dev *dev); static void pch_udc_reconnect(struct pch_udc_dev *dev) { pch_udc_init(dev); @@ -596,18 +594,22 @@ static void pch_udc_reconnect(struct pch_udc_dev *dev) static inline void pch_udc_vbus_session(struct pch_udc_dev *dev, int is_active) { + unsigned long iflags; + + spin_lock_irqsave(&dev->lock, iflags); if (is_active) { pch_udc_reconnect(dev); dev->vbus_session = 1; } else { if (dev->driver && dev->driver->disconnect) { - spin_lock(&dev->lock); + spin_unlock_irqrestore(&dev->lock, iflags); dev->driver->disconnect(&dev->gadget); - spin_unlock(&dev->lock); + spin_lock_irqsave(&dev->lock, iflags); } pch_udc_set_disconnect(dev); dev->vbus_session = 0; } + spin_unlock_irqrestore(&dev->lock, iflags); } /** @@ -1166,20 +1168,25 @@ static int pch_udc_pcd_selfpowered(struct usb_gadget *gadget, int value) static int pch_udc_pcd_pullup(struct usb_gadget *gadget, int is_on) { struct pch_udc_dev *dev; + unsigned long iflags; if (!gadget) return -EINVAL; + dev = container_of(gadget, struct pch_udc_dev, gadget); + + spin_lock_irqsave(&dev->lock, iflags); if (is_on) { pch_udc_reconnect(dev); } else { if (dev->driver && dev->driver->disconnect) { - spin_lock(&dev->lock); + spin_unlock_irqrestore(&dev->lock, iflags); dev->driver->disconnect(&dev->gadget); - spin_unlock(&dev->lock); + spin_lock_irqsave(&dev->lock, iflags); } pch_udc_set_disconnect(dev); } + spin_unlock_irqrestore(&dev->lock, iflags); return 0; } @@ -1360,6 +1367,7 @@ static irqreturn_t pch_vbus_gpio_irq(int irq, void *data) */ static int pch_vbus_gpio_init(struct pch_udc_dev *dev) { + struct device *d = &dev->pdev->dev; int err; int irq_num = 0; struct gpio_desc *gpiod; @@ -1368,7 +1376,7 @@ static int pch_vbus_gpio_init(struct pch_udc_dev *dev) dev->vbus_gpio.intr = 0; /* Retrieve the GPIO line from the USB gadget device */ - gpiod = devm_gpiod_get(dev->gadget.dev.parent, NULL, GPIOD_IN); + gpiod = devm_gpiod_get_optional(d, NULL, GPIOD_IN); if (IS_ERR(gpiod)) return PTR_ERR(gpiod); gpiod_set_consumer_name(gpiod, "pch_vbus"); @@ -1428,33 +1436,7 @@ static void complete_req(struct pch_udc_ep *ep, struct pch_udc_request *req, status = req->req.status; dev = ep->dev; - if (req->dma_mapped) { - if (req->dma == DMA_ADDR_INVALID) { - if (ep->in) - dma_unmap_single(&dev->pdev->dev, req->req.dma, - req->req.length, - DMA_TO_DEVICE); - else - dma_unmap_single(&dev->pdev->dev, req->req.dma, - req->req.length, - DMA_FROM_DEVICE); - req->req.dma = DMA_ADDR_INVALID; - } else { - if (ep->in) - dma_unmap_single(&dev->pdev->dev, req->dma, - req->req.length, - DMA_TO_DEVICE); - else { - dma_unmap_single(&dev->pdev->dev, req->dma, - req->req.length, - DMA_FROM_DEVICE); - memcpy(req->req.buf, req->buf, req->req.length); - } - kfree(req->buf); - req->dma = DMA_ADDR_INVALID; - } - req->dma_mapped = 0; - } + usb_gadget_unmap_request(&dev->gadget, &req->req, ep->in); ep->halted = 1; spin_unlock(&dev->lock); if (!ep->in) @@ -1532,12 +1514,9 @@ static int pch_udc_create_dma_chain(struct pch_udc_ep *ep, if (req->chain_len > 1) pch_udc_free_dma_chain(ep->dev, req); - if (req->dma == DMA_ADDR_INVALID) - td->dataptr = req->req.dma; - else - td->dataptr = req->dma; - + td->dataptr = req->req.dma; td->status = PCH_UDC_BS_HST_BSY; + for (; ; bytes -= buf_len, ++len) { td->status = PCH_UDC_BS_HST_BSY | min(buf_len, bytes); if (bytes <= buf_len) @@ -1743,7 +1722,6 @@ static struct usb_request *pch_udc_alloc_request(struct usb_ep *usbep, if (!req) return NULL; req->req.dma = DMA_ADDR_INVALID; - req->dma = DMA_ADDR_INVALID; INIT_LIST_HEAD(&req->queue); if (!ep->dev->dma_addr) return &req->req; @@ -1756,7 +1734,7 @@ static struct usb_request *pch_udc_alloc_request(struct usb_ep *usbep, } /* prevent from using desc. - set HOST BUSY */ dma_desc->status |= PCH_UDC_BS_HST_BSY; - dma_desc->dataptr = cpu_to_le32(DMA_ADDR_INVALID); + dma_desc->dataptr = lower_32_bits(DMA_ADDR_INVALID); req->td_data = dma_desc; req->td_data_last = dma_desc; req->chain_len = 1; @@ -1826,39 +1804,9 @@ static int pch_udc_pcd_queue(struct usb_ep *usbep, struct usb_request *usbreq, return -ESHUTDOWN; spin_lock_irqsave(&dev->lock, iflags); /* map the buffer for dma */ - if (usbreq->length && - ((usbreq->dma == DMA_ADDR_INVALID) || !usbreq->dma)) { - if (!((unsigned long)(usbreq->buf) & 0x03)) { - if (ep->in) - usbreq->dma = dma_map_single(&dev->pdev->dev, - usbreq->buf, - usbreq->length, - DMA_TO_DEVICE); - else - usbreq->dma = dma_map_single(&dev->pdev->dev, - usbreq->buf, - usbreq->length, - DMA_FROM_DEVICE); - } else { - req->buf = kzalloc(usbreq->length, GFP_ATOMIC); - if (!req->buf) { - retval = -ENOMEM; - goto probe_end; - } - if (ep->in) { - memcpy(req->buf, usbreq->buf, usbreq->length); - req->dma = dma_map_single(&dev->pdev->dev, - req->buf, - usbreq->length, - DMA_TO_DEVICE); - } else - req->dma = dma_map_single(&dev->pdev->dev, - req->buf, - usbreq->length, - DMA_FROM_DEVICE); - } - req->dma_mapped = 1; - } + retval = usb_gadget_map_request(&dev->gadget, usbreq, ep->in); + if (retval) + goto probe_end; if (usbreq->length > 0) { retval = prepare_dma(ep, req, GFP_ATOMIC); if (retval) @@ -2298,6 +2246,21 @@ static void pch_udc_svc_data_out(struct pch_udc_dev *dev, int ep_num) pch_udc_set_dma(dev, DMA_DIR_RX); } +static int pch_udc_gadget_setup(struct pch_udc_dev *dev) + __must_hold(&dev->lock) +{ + int rc; + + /* In some cases we can get an interrupt before driver gets setup */ + if (!dev->driver) + return -ESHUTDOWN; + + spin_unlock(&dev->lock); + rc = dev->driver->setup(&dev->gadget, &dev->setup_data); + spin_lock(&dev->lock); + return rc; +} + /** * pch_udc_svc_control_in() - Handle Control IN endpoint interrupts * @dev: Reference to the device structure @@ -2369,15 +2332,12 @@ static void pch_udc_svc_control_out(struct pch_udc_dev *dev) dev->gadget.ep0 = &dev->ep[UDC_EP0IN_IDX].ep; else /* OUT */ dev->gadget.ep0 = &ep->ep; - spin_lock(&dev->lock); /* If Mass storage Reset */ if ((dev->setup_data.bRequestType == 0x21) && (dev->setup_data.bRequest == 0xFF)) dev->prot_stall = 0; /* call gadget with setup data received */ - setup_supported = dev->driver->setup(&dev->gadget, - &dev->setup_data); - spin_unlock(&dev->lock); + setup_supported = pch_udc_gadget_setup(dev); if (dev->setup_data.bRequestType & USB_DIR_IN) { ep->td_data->status = (ep->td_data->status & @@ -2625,9 +2585,7 @@ static void pch_udc_svc_intf_interrupt(struct pch_udc_dev *dev) dev->ep[i].halted = 0; } dev->stall = 0; - spin_unlock(&dev->lock); - dev->driver->setup(&dev->gadget, &dev->setup_data); - spin_lock(&dev->lock); + pch_udc_gadget_setup(dev); } /** @@ -2662,9 +2620,7 @@ static void pch_udc_svc_cfg_interrupt(struct pch_udc_dev *dev) dev->stall = 0; /* call gadget zero with setup data received */ - spin_unlock(&dev->lock); - dev->driver->setup(&dev->gadget, &dev->setup_data); - spin_lock(&dev->lock); + pch_udc_gadget_setup(dev); } /** @@ -2870,14 +2826,20 @@ static void pch_udc_pcd_reinit(struct pch_udc_dev *dev) * @dev: Reference to the driver structure * * Return codes: - * 0: Success + * 0: Success + * -ERRNO: All kind of errors when retrieving VBUS GPIO */ static int pch_udc_pcd_init(struct pch_udc_dev *dev) { + int ret; + pch_udc_init(dev); pch_udc_pcd_reinit(dev); - pch_vbus_gpio_init(dev); - return 0; + + ret = pch_vbus_gpio_init(dev); + if (ret) + pch_udc_exit(dev); + return ret; } /** @@ -2938,7 +2900,7 @@ static int init_dma_pools(struct pch_udc_dev *dev) dev->dma_addr = dma_map_single(&dev->pdev->dev, ep0out_buf, UDC_EP0OUT_BUFF_SIZE * 4, DMA_FROM_DEVICE); - return 0; + return dma_mapping_error(&dev->pdev->dev, dev->dma_addr); } static int pch_udc_start(struct usb_gadget *g, @@ -2976,6 +2938,38 @@ static int pch_udc_stop(struct usb_gadget *g) return 0; } +static void pch_vbus_gpio_remove_table(void *table) +{ + gpiod_remove_lookup_table(table); +} + +static int pch_vbus_gpio_add_table(struct device *d, void *table) +{ + gpiod_add_lookup_table(table); + return devm_add_action_or_reset(d, pch_vbus_gpio_remove_table, table); +} + +static struct gpiod_lookup_table pch_udc_minnow_vbus_gpio_table = { + .dev_id = "0000:02:02.4", + .table = { + GPIO_LOOKUP("sch_gpio.33158", 12, NULL, GPIO_ACTIVE_HIGH), + {} + }, +}; + +static int pch_udc_minnow_platform_init(struct device *d) +{ + return pch_vbus_gpio_add_table(d, &pch_udc_minnow_vbus_gpio_table); +} + +static int pch_udc_quark_platform_init(struct device *d) +{ + struct pch_udc_dev *dev = dev_get_drvdata(d); + + dev->bar = PCH_UDC_PCI_BAR_QUARK_X1000; + return 0; +} + static void pch_udc_shutdown(struct pci_dev *pdev) { struct pch_udc_dev *dev = pci_get_drvdata(pdev); @@ -3024,8 +3018,7 @@ static void pch_udc_remove(struct pci_dev *pdev) pch_udc_exit(dev); } -#ifdef CONFIG_PM_SLEEP -static int pch_udc_suspend(struct device *d) +static int __maybe_unused pch_udc_suspend(struct device *d) { struct pch_udc_dev *dev = dev_get_drvdata(d); @@ -3035,21 +3028,18 @@ static int pch_udc_suspend(struct device *d) return 0; } -static int pch_udc_resume(struct device *d) +static int __maybe_unused pch_udc_resume(struct device *d) { return 0; } static SIMPLE_DEV_PM_OPS(pch_udc_pm, pch_udc_suspend, pch_udc_resume); -#define PCH_UDC_PM_OPS (&pch_udc_pm) -#else -#define PCH_UDC_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ -static int pch_udc_probe(struct pci_dev *pdev, - const struct pci_device_id *id) +typedef int (*platform_init_fn)(struct device *); + +static int pch_udc_probe(struct pci_dev *pdev, const struct pci_device_id *id) { - int bar; + platform_init_fn platform_init = (platform_init_fn)id->driver_data; int retval; struct pch_udc_dev *dev; @@ -3063,31 +3053,28 @@ static int pch_udc_probe(struct pci_dev *pdev, if (retval) return retval; + dev->bar = PCH_UDC_PCI_BAR; + dev->pdev = pdev; pci_set_drvdata(pdev, dev); - /* Determine BAR based on PCI ID */ - if (id->device == PCI_DEVICE_ID_INTEL_QUARK_X1000_UDC) - bar = PCH_UDC_PCI_BAR_QUARK_X1000; - else - bar = PCH_UDC_PCI_BAR; + /* Platform specific hook */ + if (platform_init) { + retval = platform_init(&pdev->dev); + if (retval) + return retval; + } /* PCI resource allocation */ - retval = pcim_iomap_regions(pdev, 1 << bar, pci_name(pdev)); + retval = pcim_iomap_regions(pdev, BIT(dev->bar), pci_name(pdev)); if (retval) return retval; - dev->base_addr = pcim_iomap_table(pdev)[bar]; - - /* - * FIXME: add a GPIO descriptor table to pdev.dev using - * gpiod_add_descriptor_table() from based on - * the PCI subsystem ID. The system-dependent GPIO is necessary for - * VBUS operation. - */ + dev->base_addr = pcim_iomap_table(pdev)[dev->bar]; /* initialize the hardware */ - if (pch_udc_pcd_init(dev)) - return -ENODEV; + retval = pch_udc_pcd_init(dev); + if (retval) + return retval; pci_enable_msi(pdev); @@ -3104,7 +3091,6 @@ static int pch_udc_probe(struct pci_dev *pdev, /* device struct setup */ spin_lock_init(&dev->lock); - dev->pdev = pdev; dev->gadget.ops = &pch_udc_ops; retval = init_dma_pools(dev); @@ -3128,10 +3114,17 @@ static int pch_udc_probe(struct pci_dev *pdev, static const struct pci_device_id pch_udc_pcidev_id[] = { { - PCI_DEVICE(PCI_VENDOR_ID_INTEL, - PCI_DEVICE_ID_INTEL_QUARK_X1000_UDC), + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_QUARK_X1000_UDC), .class = PCI_CLASS_SERIAL_USB_DEVICE, .class_mask = 0xffffffff, + .driver_data = (kernel_ulong_t)&pch_udc_quark_platform_init, + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EG20T_UDC, + PCI_VENDOR_ID_CIRCUITCO, PCI_SUBSYSTEM_ID_CIRCUITCO_MINNOWBOARD), + .class = PCI_CLASS_SERIAL_USB_DEVICE, + .class_mask = 0xffffffff, + .driver_data = (kernel_ulong_t)&pch_udc_minnow_platform_init, }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EG20T_UDC), @@ -3160,7 +3153,7 @@ static struct pci_driver pch_udc_driver = { .remove = pch_udc_remove, .shutdown = pch_udc_shutdown, .driver = { - .pm = PCH_UDC_PM_OPS, + .pm = &pch_udc_pm, }, }; diff --git a/drivers/usb/gadget/udc/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c index 896c1a016d55..65cae4883454 100644 --- a/drivers/usb/gadget/udc/r8a66597-udc.c +++ b/drivers/usb/gadget/udc/r8a66597-udc.c @@ -1849,6 +1849,8 @@ static int r8a66597_probe(struct platform_device *pdev) return PTR_ERR(reg); ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!ires) + return -EINVAL; irq = ires->start; irq_trigger = ires->flags & IRQF_TRIGGER_MASK; diff --git a/drivers/usb/gadget/udc/s3c2410_udc.c b/drivers/usb/gadget/udc/s3c2410_udc.c index 1d3ebb07ccd4..b154b62abefa 100644 --- a/drivers/usb/gadget/udc/s3c2410_udc.c +++ b/drivers/usb/gadget/udc/s3c2410_udc.c @@ -54,8 +54,6 @@ static struct clk *udc_clock; static struct clk *usb_bus_clock; static void __iomem *base_addr; static int irq_usbd; -static u64 rsrc_start; -static u64 rsrc_len; static struct dentry *s3c2410_udc_debugfs_root; static inline u32 udc_read(u32 reg) @@ -1752,7 +1750,8 @@ static int s3c2410_udc_probe(struct platform_device *pdev) udc_clock = clk_get(NULL, "usb-device"); if (IS_ERR(udc_clock)) { dev_err(dev, "failed to get udc clock source\n"); - return PTR_ERR(udc_clock); + retval = PTR_ERR(udc_clock); + goto err_usb_bus_clk; } clk_prepare_enable(udc_clock); @@ -1775,7 +1774,7 @@ static int s3c2410_udc_probe(struct platform_device *pdev) base_addr = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base_addr)) { retval = PTR_ERR(base_addr); - goto err_mem; + goto err_udc_clk; } the_controller = udc; @@ -1793,7 +1792,7 @@ static int s3c2410_udc_probe(struct platform_device *pdev) if (retval != 0) { dev_err(dev, "cannot get irq %i, err %d\n", irq_usbd, retval); retval = -EBUSY; - goto err_map; + goto err_udc_clk; } dev_dbg(dev, "got irq %i\n", irq_usbd); @@ -1864,10 +1863,14 @@ static int s3c2410_udc_probe(struct platform_device *pdev) gpio_free(udc_info->vbus_pin); err_int: free_irq(irq_usbd, udc); -err_map: - iounmap(base_addr); -err_mem: - release_mem_region(rsrc_start, rsrc_len); +err_udc_clk: + clk_disable_unprepare(udc_clock); + clk_put(udc_clock); + udc_clock = NULL; +err_usb_bus_clk: + clk_disable_unprepare(usb_bus_clock); + clk_put(usb_bus_clock); + usb_bus_clock = NULL; return retval; } @@ -1899,9 +1902,6 @@ static int s3c2410_udc_remove(struct platform_device *pdev) free_irq(irq_usbd, udc); - iounmap(base_addr); - release_mem_region(rsrc_start, rsrc_len); - if (!IS_ERR(udc_clock) && udc_clock != NULL) { clk_disable_unprepare(udc_clock); clk_put(udc_clock); diff --git a/drivers/usb/gadget/udc/snps_udc_plat.c b/drivers/usb/gadget/udc/snps_udc_plat.c index 32f1d3e90c26..99805d60a7ab 100644 --- a/drivers/usb/gadget/udc/snps_udc_plat.c +++ b/drivers/usb/gadget/udc/snps_udc_plat.c @@ -114,8 +114,8 @@ static int udc_plat_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); udc->virt_addr = devm_ioremap_resource(dev, res); - if (IS_ERR(udc->regs)) - return PTR_ERR(udc->regs); + if (IS_ERR(udc->virt_addr)) + return PTR_ERR(udc->virt_addr); /* udc csr registers base */ udc->csr = udc->virt_addr + UDC_CSR_ADDR; diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c index 580bef8eb4cb..2319c9737c2b 100644 --- a/drivers/usb/gadget/udc/tegra-xudc.c +++ b/drivers/usb/gadget/udc/tegra-xudc.c @@ -3883,7 +3883,7 @@ static int tegra_xudc_remove(struct platform_device *pdev) pm_runtime_get_sync(xudc->dev); - cancel_delayed_work(&xudc->plc_reset_work); + cancel_delayed_work_sync(&xudc->plc_reset_work); cancel_work_sync(&xudc->usb_role_sw_work); usb_del_gadget_udc(&xudc->gadget); diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index b94f2a070c05..df9428f1dc5e 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -272,6 +272,7 @@ config USB_EHCI_TEGRA select USB_CHIPIDEA select USB_CHIPIDEA_HOST select USB_CHIPIDEA_TEGRA + select USB_GADGET help This option is deprecated now and the driver was removed, use USB_CHIPIDEA_TEGRA instead. diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 3e4d298d851f..171de4df50bd 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -19,9 +19,7 @@ ifneq ($(CONFIG_USB_XHCI_DBGCAP), ) xhci-hcd-y += xhci-dbgcap.o xhci-dbgtty.o endif -ifneq ($(CONFIG_USB_XHCI_MTK), ) - xhci-hcd-y += xhci-mtk-sch.o -endif +xhci-mtk-hcd-y := xhci-mtk.o xhci-mtk-sch.o xhci-plat-hcd-y := xhci-plat.o ifneq ($(CONFIG_USB_XHCI_MVEBU), ) @@ -73,7 +71,7 @@ obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o obj-$(CONFIG_USB_XHCI_PCI_RENESAS) += xhci-pci-renesas.o obj-$(CONFIG_USB_XHCI_PLATFORM) += xhci-plat-hcd.o obj-$(CONFIG_USB_XHCI_HISTB) += xhci-histb.o -obj-$(CONFIG_USB_XHCI_MTK) += xhci-mtk.o +obj-$(CONFIG_USB_XHCI_MTK) += xhci-mtk-hcd.o obj-$(CONFIG_USB_XHCI_TEGRA) += xhci-tegra.o obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 1926b328b6aa..94b5e64ae9a2 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -651,7 +651,7 @@ static int ehci_run (struct usb_hcd *hcd) "USB %x.%x started, EHCI %x.%02x%s\n", ((ehci->sbrn & 0xf0)>>4), (ehci->sbrn & 0x0f), temp >> 8, temp & 0xff, - ignore_oc ? ", overcurrent ignored" : ""); + (ignore_oc || ehci->spurious_oc) ? ", overcurrent ignored" : ""); ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); /* Turn On Interrupts */ @@ -705,15 +705,8 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) struct ehci_hcd *ehci = hcd_to_ehci (hcd); u32 status, masked_status, pcd_status = 0, cmd; int bh; - unsigned long flags; - /* - * For threadirqs option we use spin_lock_irqsave() variant to prevent - * deadlock with ehci hrtimer callback, because hrtimer callbacks run - * in interrupt context even when threadirqs is specified. We can go - * back to spin_lock() variant when hrtimer callbacks become threaded. - */ - spin_lock_irqsave(&ehci->lock, flags); + spin_lock(&ehci->lock); status = ehci_readl(ehci, &ehci->regs->status); @@ -731,7 +724,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) /* Shared IRQ? */ if (!masked_status || unlikely(ehci->rh_state == EHCI_RH_HALTED)) { - spin_unlock_irqrestore(&ehci->lock, flags); + spin_unlock(&ehci->lock); return IRQ_NONE; } @@ -842,7 +835,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) if (bh) ehci_work (ehci); - spin_unlock_irqrestore(&ehci->lock, flags); + spin_unlock(&ehci->lock); if (pcd_status) usb_hcd_poll_rh_status(hcd); return IRQ_HANDLED; diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 9f9ab5ccea88..159cc27b1a36 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -643,7 +643,7 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf) * always set, seem to clear PORT_OCC and PORT_CSC when writing to * PORT_POWER; that's surprising, but maybe within-spec. */ - if (!ignore_oc) + if (!ignore_oc && !ehci->spurious_oc) mask = PORT_CSC | PORT_PEC | PORT_OCC; else mask = PORT_CSC | PORT_PEC; @@ -1013,7 +1013,7 @@ int ehci_hub_control( if (temp & PORT_PEC) status |= USB_PORT_STAT_C_ENABLE << 16; - if ((temp & PORT_OCC) && !ignore_oc){ + if ((temp & PORT_OCC) && (!ignore_oc && !ehci->spurious_oc)){ status |= USB_PORT_STAT_C_OVERCURRENT << 16; /* diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 71ec3025686f..e87cf3a00fa4 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -297,6 +297,9 @@ static int ehci_pci_setup(struct usb_hcd *hcd) if (pdev->vendor == PCI_VENDOR_ID_STMICRO && pdev->device == PCI_DEVICE_ID_STMICRO_USB_HOST) ; /* ConneXT has no sbrn register */ + else if (pdev->vendor == PCI_VENDOR_ID_HUAWEI + && pdev->device == 0xa239) + ; /* HUAWEI Kunpeng920 USB EHCI has no sbrn register */ else pci_read_config_byte(pdev, 0x60, &ehci->sbrn); diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index a48dd3fac153..c70f2d0b4aaf 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -286,6 +286,9 @@ static int ehci_platform_probe(struct platform_device *dev) if (of_property_read_bool(dev->dev.of_node, "big-endian")) ehci->big_endian_mmio = ehci->big_endian_desc = 1; + if (of_property_read_bool(dev->dev.of_node, "spurious-oc")) + ehci->spurious_oc = 1; + if (of_property_read_bool(dev->dev.of_node, "needs-reset-on-resume")) priv->reset_on_resume = true; @@ -327,6 +330,8 @@ static int ehci_platform_probe(struct platform_device *dev) hcd->has_tt = 1; if (pdata->reset_on_resume) priv->reset_on_resume = true; + if (pdata->spurious_oc) + ehci->spurious_oc = 1; #ifndef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO if (ehci->big_endian_mmio) { diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index eabf22a78eae..80bb823aa9fe 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -218,6 +218,7 @@ struct ehci_hcd { /* one per controller */ unsigned frame_index_bug:1; /* MosChip (AKA NetMos) */ unsigned need_oc_pp_cycle:1; /* MPC834X port power */ unsigned imx28_write_fix:1; /* For Freescale i.MX28 */ + unsigned spurious_oc:1; /* required for usb32 quirk */ #define OHCI_CTRL_HCFS (3 << 6) diff --git a/drivers/usb/host/fotg210-hcd.c b/drivers/usb/host/fotg210-hcd.c index 5617ef30530a..6cac642520fc 100644 --- a/drivers/usb/host/fotg210-hcd.c +++ b/drivers/usb/host/fotg210-hcd.c @@ -408,17 +408,17 @@ static void qh_lines(struct fotg210_hcd *fotg210, struct fotg210_qh *qh, temp = snprintf(next, size, "\n\t%p%c%s len=%d %08x urb %p", td, mark, ({ char *tmp; - switch ((scratch>>8)&0x03) { - case 0: + switch ((scratch>>8)&0x03) { + case 0: tmp = "out"; break; - case 1: + case 1: tmp = "in"; break; - case 2: + case 2: tmp = "setup"; break; - default: + default: tmp = "?"; break; } tmp; }), @@ -2699,7 +2699,7 @@ static struct list_head *qh_urb_transaction(struct fotg210_hcd *fotg210, * any previous qh and cancel its urbs first; endpoints are * implicitly reset then (data toggle too). * That'd mean updating how usbcore talks to HCDs. (2.7?) -*/ + */ /* Each QH holds a qtd list; a QH is used for everything except iso. diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index 8544a2a2c1e6..8835f6bd528e 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -1200,14 +1200,13 @@ DEFINE_SHOW_ATTRIBUTE(isp116x_debug); static void create_debug_file(struct isp116x *isp116x) { - isp116x->dentry = debugfs_create_file(hcd_name, - S_IRUGO, NULL, isp116x, - &isp116x_debug_fops); + debugfs_create_file(hcd_name, S_IRUGO, usb_debug_root, isp116x, + &isp116x_debug_fops); } static void remove_debug_file(struct isp116x *isp116x) { - debugfs_remove(isp116x->dentry); + debugfs_remove(debugfs_lookup(hcd_name, usb_debug_root)); } #else diff --git a/drivers/usb/host/isp116x.h b/drivers/usb/host/isp116x.h index a5e929c10d53..84904025fe7f 100644 --- a/drivers/usb/host/isp116x.h +++ b/drivers/usb/host/isp116x.h @@ -260,7 +260,6 @@ struct isp116x { struct isp116x_platform_data *board; - struct dentry *dentry; unsigned long stat1, stat2, stat4, stat8, stat16; /* HC registers */ diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c index 2cecb36d241b..d8610ce8f2ec 100644 --- a/drivers/usb/host/isp1362-hcd.c +++ b/drivers/usb/host/isp1362-hcd.c @@ -2164,15 +2164,13 @@ DEFINE_SHOW_ATTRIBUTE(isp1362); /* expect just one isp1362_hcd per system */ static void create_debug_file(struct isp1362_hcd *isp1362_hcd) { - isp1362_hcd->debug_file = debugfs_create_file("isp1362", S_IRUGO, - usb_debug_root, - isp1362_hcd, - &isp1362_fops); + debugfs_create_file("isp1362", S_IRUGO, usb_debug_root, isp1362_hcd, + &isp1362_fops); } static void remove_debug_file(struct isp1362_hcd *isp1362_hcd) { - debugfs_remove(isp1362_hcd->debug_file); + debugfs_remove(debugfs_lookup("isp1362", usb_debug_root)); } /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/isp1362.h b/drivers/usb/host/isp1362.h index 208705b08d37..74ca4be24723 100644 --- a/drivers/usb/host/isp1362.h +++ b/drivers/usb/host/isp1362.h @@ -435,7 +435,6 @@ struct isp1362_hcd { struct isp1362_platform_data *board; - struct dentry *debug_file; unsigned long stat1, stat2, stat4, stat8, stat16; /* HC registers */ diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index 115ced0d93e1..85623731a516 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -1287,11 +1287,10 @@ sl811h_hub_control( goto error; put_unaligned_le32(sl811->port1, buf); -#ifndef VERBOSE - if (*(u16*)(buf+2)) /* only if wPortChange is interesting */ -#endif - dev_dbg(hcd->self.controller, "GetPortStatus %08x\n", - sl811->port1); + if (__is_defined(VERBOSE) || + *(u16*)(buf+2)) /* only if wPortChange is interesting */ + dev_dbg(hcd->self.controller, "GetPortStatus %08x\n", + sl811->port1); break; case SetPortFeature: if (wIndex != 1 || wLength != 0) @@ -1496,14 +1495,13 @@ DEFINE_SHOW_ATTRIBUTE(sl811h_debug); /* expect just one sl811 per system */ static void create_debug_file(struct sl811 *sl811) { - sl811->debug_file = debugfs_create_file("sl811h", S_IRUGO, - usb_debug_root, sl811, - &sl811h_debug_fops); + debugfs_create_file("sl811h", S_IRUGO, usb_debug_root, sl811, + &sl811h_debug_fops); } static void remove_debug_file(struct sl811 *sl811) { - debugfs_remove(sl811->debug_file); + debugfs_remove(debugfs_lookup("sl811h", usb_debug_root)); } /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/sl811.h b/drivers/usb/host/sl811.h index 2abe51a5db44..ba8c9aa7dee8 100644 --- a/drivers/usb/host/sl811.h +++ b/drivers/usb/host/sl811.h @@ -123,7 +123,6 @@ struct sl811 { void __iomem *addr_reg; void __iomem *data_reg; struct sl811_platform_data *board; - struct dentry *debug_file; unsigned long stat_insrmv; unsigned long stat_wake; diff --git a/drivers/usb/host/sl811_cs.c b/drivers/usb/host/sl811_cs.c index 72136373ffab..16d157013018 100644 --- a/drivers/usb/host/sl811_cs.c +++ b/drivers/usb/host/sl811_cs.c @@ -94,7 +94,7 @@ static int sl811_hc_init(struct device *parent, resource_size_t base_addr, return -EBUSY; platform_dev.dev.parent = parent; - /* finish seting up the platform device */ + /* finish setting up the platform device */ resources[0].start = irq; resources[1].start = base_addr; diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c index eb96e1e15b71..5a783c423d8e 100644 --- a/drivers/usb/host/u132-hcd.c +++ b/drivers/usb/host/u132-hcd.c @@ -78,7 +78,7 @@ static DECLARE_WAIT_QUEUE_HEAD(u132_hcd_wait); * u132_module_lock exists to protect access to global variables * */ -static struct mutex u132_module_lock; +static DEFINE_MUTEX(u132_module_lock); static int u132_exiting; static int u132_instances; /* @@ -3190,7 +3190,6 @@ static int __init u132_hcd_init(void) int retval; u132_instances = 0; u132_exiting = 0; - mutex_init(&u132_module_lock); if (usb_disabled()) return -ENODEV; printk(KERN_INFO "driver %s\n", hcd_name); diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 03bc59755123..d90b869f5f40 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -536,7 +536,8 @@ static void release_uhci(struct uhci_hcd *uhci) uhci->is_initialized = 0; spin_unlock_irq(&uhci->lock); - debugfs_remove(uhci->dentry); + debugfs_remove(debugfs_lookup(uhci_to_hcd(uhci)->self.bus_name, + uhci_debugfs_root)); for (i = 0; i < UHCI_NUM_SKELQH; i++) uhci_free_qh(uhci, uhci->skelqh[i]); @@ -577,7 +578,6 @@ static int uhci_start(struct usb_hcd *hcd) struct uhci_hcd *uhci = hcd_to_uhci(hcd); int retval = -EBUSY; int i; - struct dentry __maybe_unused *dentry; hcd->uses_new_polling = 1; /* Accept arbitrarily long scatter-gather lists */ @@ -590,10 +590,8 @@ static int uhci_start(struct usb_hcd *hcd) init_waitqueue_head(&uhci->waitqh); #ifdef UHCI_DEBUG_OPS - uhci->dentry = debugfs_create_file(hcd->self.bus_name, - S_IFREG|S_IRUGO|S_IWUSR, - uhci_debugfs_root, uhci, - &uhci_debug_operations); + debugfs_create_file(hcd->self.bus_name, S_IFREG|S_IRUGO|S_IWUSR, + uhci_debugfs_root, uhci, &uhci_debug_operations); #endif uhci->frame = dma_alloc_coherent(uhci_dev(uhci), @@ -702,7 +700,7 @@ static int uhci_start(struct usb_hcd *hcd) uhci->frame, uhci->frame_dma_handle); err_alloc_frame: - debugfs_remove(uhci->dentry); + debugfs_remove(debugfs_lookup(hcd->self.bus_name, uhci_debugfs_root)); return retval; } diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h index 7f9f33c8c232..8ae5ccd26753 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h @@ -381,10 +381,6 @@ enum uhci_rh_state { * The full UHCI controller information: */ struct uhci_hcd { - - /* debugfs */ - struct dentry *dentry; - /* Grabbed from PCI */ unsigned long io_addr; diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 74c497fd3476..e9b18fc17617 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -11,6 +11,7 @@ #include #include +#include #include "xhci.h" #include "xhci-trace.h" @@ -19,151 +20,236 @@ #define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_WRC | PORT_OCC | \ PORT_RC | PORT_PLC | PORT_PE) -/* USB 3 BOS descriptor and a capability descriptors, combined. - * Fields will be adjusted and added later in xhci_create_usb3_bos_desc() - */ -static u8 usb_bos_descriptor [] = { - USB_DT_BOS_SIZE, /* __u8 bLength, 5 bytes */ - USB_DT_BOS, /* __u8 bDescriptorType */ - 0x0F, 0x00, /* __le16 wTotalLength, 15 bytes */ - 0x1, /* __u8 bNumDeviceCaps */ - /* First device capability, SuperSpeed */ - USB_DT_USB_SS_CAP_SIZE, /* __u8 bLength, 10 bytes */ - USB_DT_DEVICE_CAPABILITY, /* Device Capability */ - USB_SS_CAP_TYPE, /* bDevCapabilityType, SUPERSPEED_USB */ - 0x00, /* bmAttributes, LTM off by default */ - USB_5GBPS_OPERATION, 0x00, /* wSpeedsSupported, 5Gbps only */ - 0x03, /* bFunctionalitySupport, - USB 3.0 speed only */ - 0x00, /* bU1DevExitLat, set later. */ - 0x00, 0x00, /* __le16 bU2DevExitLat, set later. */ - /* Second device capability, SuperSpeedPlus */ - 0x1c, /* bLength 28, will be adjusted later */ - USB_DT_DEVICE_CAPABILITY, /* Device Capability */ - USB_SSP_CAP_TYPE, /* bDevCapabilityType SUPERSPEED_PLUS */ - 0x00, /* bReserved 0 */ - 0x23, 0x00, 0x00, 0x00, /* bmAttributes, SSAC=3 SSIC=1 */ - 0x01, 0x00, /* wFunctionalitySupport */ - 0x00, 0x00, /* wReserved 0 */ - /* Default Sublink Speed Attributes, overwrite if custom PSI exists */ - 0x34, 0x00, 0x05, 0x00, /* 5Gbps, symmetric, rx, ID = 4 */ - 0xb4, 0x00, 0x05, 0x00, /* 5Gbps, symmetric, tx, ID = 4 */ - 0x35, 0x40, 0x0a, 0x00, /* 10Gbps, SSP, symmetric, rx, ID = 5 */ - 0xb5, 0x40, 0x0a, 0x00, /* 10Gbps, SSP, symmetric, tx, ID = 5 */ +/* Default sublink speed attribute of each lane */ +static u32 ssp_cap_default_ssa[] = { + 0x00050034, /* USB 3.0 SS Gen1x1 id:4 symmetric rx 5Gbps */ + 0x000500b4, /* USB 3.0 SS Gen1x1 id:4 symmetric tx 5Gbps */ + 0x000a4035, /* USB 3.1 SSP Gen2x1 id:5 symmetric rx 10Gbps */ + 0x000a40b5, /* USB 3.1 SSP Gen2x1 id:5 symmetric tx 10Gbps */ + 0x00054036, /* USB 3.2 SSP Gen1x2 id:6 symmetric rx 5Gbps */ + 0x000540b6, /* USB 3.2 SSP Gen1x2 id:6 symmetric tx 5Gbps */ + 0x000a4037, /* USB 3.2 SSP Gen2x2 id:7 symmetric rx 10Gbps */ + 0x000a40b7, /* USB 3.2 SSP Gen2x2 id:7 symmetric tx 10Gbps */ }; -static int xhci_create_usb3_bos_desc(struct xhci_hcd *xhci, char *buf, - u16 wLength) +static int xhci_create_usb3x_bos_desc(struct xhci_hcd *xhci, char *buf, + u16 wLength) { - struct xhci_port_cap *port_cap = NULL; - int i, ssa_count; - u32 temp; - u16 desc_size, ssp_cap_size, ssa_size = 0; - bool usb3_1 = false; + struct usb_bos_descriptor *bos; + struct usb_ss_cap_descriptor *ss_cap; + struct usb_ssp_cap_descriptor *ssp_cap; + struct xhci_port_cap *port_cap = NULL; + u16 bcdUSB; + u32 reg; + u32 min_rate = 0; + u8 min_ssid; + u8 ssac; + u8 ssic; + int offset; + int i; - desc_size = USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE; - ssp_cap_size = sizeof(usb_bos_descriptor) - desc_size; + /* BOS descriptor */ + bos = (struct usb_bos_descriptor *)buf; + bos->bLength = USB_DT_BOS_SIZE; + bos->bDescriptorType = USB_DT_BOS; + bos->wTotalLength = cpu_to_le16(USB_DT_BOS_SIZE + + USB_DT_USB_SS_CAP_SIZE); + bos->bNumDeviceCaps = 1; - /* does xhci support USB 3.1 Enhanced SuperSpeed */ + /* Create the descriptor for port with the highest revision */ for (i = 0; i < xhci->num_port_caps; i++) { - if (xhci->port_caps[i].maj_rev == 0x03 && - xhci->port_caps[i].min_rev >= 0x01) { - usb3_1 = true; + u8 major = xhci->port_caps[i].maj_rev; + u8 minor = xhci->port_caps[i].min_rev; + u16 rev = (major << 8) | minor; + + if (i == 0 || bcdUSB < rev) { + bcdUSB = rev; port_cap = &xhci->port_caps[i]; - break; } } - if (usb3_1) { - /* does xhci provide a PSI table for SSA speed attributes? */ + if (bcdUSB >= 0x0310) { if (port_cap->psi_count) { - /* two SSA entries for each unique PSI ID, RX and TX */ - ssa_count = port_cap->psi_uid_count * 2; - ssa_size = ssa_count * sizeof(u32); - ssp_cap_size -= 16; /* skip copying the default SSA */ - } - desc_size += ssp_cap_size; - } - memcpy(buf, &usb_bos_descriptor, min(desc_size, wLength)); + u8 num_sym_ssa = 0; - if (usb3_1) { - /* modify bos descriptor bNumDeviceCaps and wTotalLength */ - buf[4] += 1; - put_unaligned_le16(desc_size + ssa_size, &buf[2]); + for (i = 0; i < port_cap->psi_count; i++) { + if ((port_cap->psi[i] & PLT_MASK) == PLT_SYM) + num_sym_ssa++; + } + + ssac = port_cap->psi_count + num_sym_ssa - 1; + ssic = port_cap->psi_uid_count - 1; + } else { + if (bcdUSB >= 0x0320) + ssac = 7; + else + ssac = 3; + + ssic = (ssac + 1) / 2 - 1; + } + + bos->bNumDeviceCaps++; + bos->wTotalLength = cpu_to_le16(USB_DT_BOS_SIZE + + USB_DT_USB_SS_CAP_SIZE + + USB_DT_USB_SSP_CAP_SIZE(ssac)); } if (wLength < USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE) return wLength; - /* Indicate whether the host has LTM support. */ - temp = readl(&xhci->cap_regs->hcc_params); - if (HCC_LTC(temp)) - buf[8] |= USB_LTM_SUPPORT; + /* SuperSpeed USB Device Capability */ + ss_cap = (struct usb_ss_cap_descriptor *)&buf[USB_DT_BOS_SIZE]; + ss_cap->bLength = USB_DT_USB_SS_CAP_SIZE; + ss_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY; + ss_cap->bDevCapabilityType = USB_SS_CAP_TYPE; + ss_cap->bmAttributes = 0; /* set later */ + ss_cap->wSpeedSupported = cpu_to_le16(USB_5GBPS_OPERATION); + ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION; + ss_cap->bU1devExitLat = 0; /* set later */ + ss_cap->bU2DevExitLat = 0; /* set later */ + + reg = readl(&xhci->cap_regs->hcc_params); + if (HCC_LTC(reg)) + ss_cap->bmAttributes |= USB_LTM_SUPPORT; - /* Set the U1 and U2 exit latencies. */ if ((xhci->quirks & XHCI_LPM_SUPPORT)) { - temp = readl(&xhci->cap_regs->hcs_params3); - buf[12] = HCS_U1_LATENCY(temp); - put_unaligned_le16(HCS_U2_LATENCY(temp), &buf[13]); + reg = readl(&xhci->cap_regs->hcs_params3); + ss_cap->bU1devExitLat = HCS_U1_LATENCY(reg); + ss_cap->bU2DevExitLat = cpu_to_le16(HCS_U2_LATENCY(reg)); } - /* If PSI table exists, add the custom speed attributes from it */ - if (usb3_1 && port_cap->psi_count) { - u32 ssp_cap_base, bm_attrib, psi, psi_mant, psi_exp; - int offset; + if (wLength < le16_to_cpu(bos->wTotalLength)) + return wLength; - ssp_cap_base = USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE; + if (bcdUSB < 0x0310) + return le16_to_cpu(bos->wTotalLength); - if (wLength < desc_size) - return wLength; - buf[ssp_cap_base] = ssp_cap_size + ssa_size; + ssp_cap = (struct usb_ssp_cap_descriptor *)&buf[USB_DT_BOS_SIZE + + USB_DT_USB_SS_CAP_SIZE]; + ssp_cap->bLength = USB_DT_USB_SSP_CAP_SIZE(ssac); + ssp_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY; + ssp_cap->bDevCapabilityType = USB_SSP_CAP_TYPE; + ssp_cap->bReserved = 0; + ssp_cap->wReserved = 0; + ssp_cap->bmAttributes = + cpu_to_le32(FIELD_PREP(USB_SSP_SUBLINK_SPEED_ATTRIBS, ssac) | + FIELD_PREP(USB_SSP_SUBLINK_SPEED_IDS, ssic)); - /* attribute count SSAC bits 4:0 and ID count SSIC bits 8:5 */ - bm_attrib = (ssa_count - 1) & 0x1f; - bm_attrib |= (port_cap->psi_uid_count - 1) << 5; - put_unaligned_le32(bm_attrib, &buf[ssp_cap_base + 4]); + if (!port_cap->psi_count) { + for (i = 0; i < ssac + 1; i++) + ssp_cap->bmSublinkSpeedAttr[i] = + cpu_to_le32(ssp_cap_default_ssa[i]); + + min_ssid = 4; + goto out; + } + + offset = 0; + for (i = 0; i < port_cap->psi_count; i++) { + u32 psi; + u32 attr; + u8 ssid; + u8 lp; + u8 lse; + u8 psie; + u16 lane_mantissa; + u16 psim; + u16 plt; + + psi = port_cap->psi[i]; + ssid = XHCI_EXT_PORT_PSIV(psi); + lp = XHCI_EXT_PORT_LP(psi); + psie = XHCI_EXT_PORT_PSIE(psi); + psim = XHCI_EXT_PORT_PSIM(psi); + plt = psi & PLT_MASK; + + lse = psie; + lane_mantissa = psim; + + /* Shift to Gbps and set SSP Link Protocol if 10Gpbs */ + for (; psie < USB_SSP_SUBLINK_SPEED_LSE_GBPS; psie++) + psim /= 1000; + + if (!min_rate || psim < min_rate) { + min_ssid = ssid; + min_rate = psim; + } + + /* Some host controllers don't set the link protocol for SSP */ + if (psim >= 10) + lp = USB_SSP_SUBLINK_SPEED_LP_SSP; - if (wLength < desc_size + ssa_size) - return wLength; /* - * Create the Sublink Speed Attributes (SSA) array. - * The xhci PSI field and USB 3.1 SSA fields are very similar, - * but link type bits 7:6 differ for values 01b and 10b. - * xhci has also only one PSI entry for a symmetric link when - * USB 3.1 requires two SSA entries (RX and TX) for every link + * PSIM and PSIE represent the total speed of PSI. The BOS + * descriptor SSP sublink speed attribute lane mantissa + * describes the lane speed. E.g. PSIM and PSIE for gen2x2 + * is 20Gbps, but the BOS descriptor lane speed mantissa is + * 10Gbps. Check and modify the mantissa value to match the + * lane speed. */ - offset = desc_size; - for (i = 0; i < port_cap->psi_count; i++) { - psi = port_cap->psi[i]; - psi &= ~USB_SSP_SUBLINK_SPEED_RSVD; - psi_exp = XHCI_EXT_PORT_PSIE(psi); - psi_mant = XHCI_EXT_PORT_PSIM(psi); + if (bcdUSB == 0x0320 && plt == PLT_SYM) { + /* + * The PSI dword for gen1x2 and gen2x1 share the same + * values. But the lane speed for gen1x2 is 5Gbps while + * gen2x1 is 10Gbps. If the previous PSI dword SSID is + * 5 and the PSIE and PSIM match with SSID 6, let's + * assume that the controller follows the default speed + * id with SSID 6 for gen1x2. + */ + if (ssid == 6 && psie == 3 && psim == 10 && i) { + u32 prev = port_cap->psi[i - 1]; - /* Shift to Gbps and set SSP Link BIT(14) if 10Gpbs */ - for (; psi_exp < 3; psi_exp++) - psi_mant /= 1000; - if (psi_mant >= 10) - psi |= BIT(14); - - if ((psi & PLT_MASK) == PLT_SYM) { - /* Symmetric, create SSA RX and TX from one PSI entry */ - put_unaligned_le32(psi, &buf[offset]); - psi |= 1 << 7; /* turn entry to TX */ - offset += 4; - if (offset >= desc_size + ssa_size) - return desc_size + ssa_size; - } else if ((psi & PLT_MASK) == PLT_ASYM_RX) { - /* Asymetric RX, flip bits 7:6 for SSA */ - psi ^= PLT_MASK; + if ((prev & PLT_MASK) == PLT_SYM && + XHCI_EXT_PORT_PSIV(prev) == 5 && + XHCI_EXT_PORT_PSIE(prev) == 3 && + XHCI_EXT_PORT_PSIM(prev) == 10) { + lse = USB_SSP_SUBLINK_SPEED_LSE_GBPS; + lane_mantissa = 5; + } } - put_unaligned_le32(psi, &buf[offset]); - offset += 4; - if (offset >= desc_size + ssa_size) - return desc_size + ssa_size; + + if (psie == 3 && psim > 10) { + lse = USB_SSP_SUBLINK_SPEED_LSE_GBPS; + lane_mantissa = 10; + } + } + + attr = (FIELD_PREP(USB_SSP_SUBLINK_SPEED_SSID, ssid) | + FIELD_PREP(USB_SSP_SUBLINK_SPEED_LP, lp) | + FIELD_PREP(USB_SSP_SUBLINK_SPEED_LSE, lse) | + FIELD_PREP(USB_SSP_SUBLINK_SPEED_LSM, lane_mantissa)); + + switch (plt) { + case PLT_SYM: + attr |= FIELD_PREP(USB_SSP_SUBLINK_SPEED_ST, + USB_SSP_SUBLINK_SPEED_ST_SYM_RX); + ssp_cap->bmSublinkSpeedAttr[offset++] = cpu_to_le32(attr); + + attr &= ~USB_SSP_SUBLINK_SPEED_ST; + attr |= FIELD_PREP(USB_SSP_SUBLINK_SPEED_ST, + USB_SSP_SUBLINK_SPEED_ST_SYM_TX); + ssp_cap->bmSublinkSpeedAttr[offset++] = cpu_to_le32(attr); + break; + case PLT_ASYM_RX: + attr |= FIELD_PREP(USB_SSP_SUBLINK_SPEED_ST, + USB_SSP_SUBLINK_SPEED_ST_ASYM_RX); + ssp_cap->bmSublinkSpeedAttr[offset++] = cpu_to_le32(attr); + break; + case PLT_ASYM_TX: + attr |= FIELD_PREP(USB_SSP_SUBLINK_SPEED_ST, + USB_SSP_SUBLINK_SPEED_ST_ASYM_TX); + ssp_cap->bmSublinkSpeedAttr[offset++] = cpu_to_le32(attr); + break; } } - /* ssa_size is 0 for other than usb 3.1 hosts */ - return desc_size + ssa_size; +out: + ssp_cap->wFunctionalitySupport = + cpu_to_le16(FIELD_PREP(USB_SSP_MIN_SUBLINK_SPEED_ATTRIBUTE_ID, + min_ssid) | + FIELD_PREP(USB_SSP_MIN_RX_LANE_COUNT, 1) | + FIELD_PREP(USB_SSP_MIN_TX_LANE_COUNT, 1)); + + return le16_to_cpu(bos->wTotalLength); } static void xhci_common_hub_descriptor(struct xhci_hcd *xhci, @@ -1137,7 +1223,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, if (hcd->speed < HCD_USB3) goto error; - retval = xhci_create_usb3_bos_desc(xhci, buf, wLength); + retval = xhci_create_usb3x_bos_desc(xhci, buf, wLength); spin_unlock_irqrestore(&xhci->lock, flags); return retval; case GetPortStatus: diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index f2c4ee7c4786..f66815fe8482 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -532,7 +532,7 @@ struct xhci_ep_ctx *xhci_get_ep_ctx(struct xhci_hcd *xhci, return (struct xhci_ep_ctx *) (ctx->bytes + (ep_index * CTX_SIZE(xhci->hcc_params))); } - +EXPORT_SYMBOL_GPL(xhci_get_ep_ctx); /***************** Streams structures manipulation *************************/ @@ -2129,6 +2129,15 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, if (major_revision == 0x03) { rhub = &xhci->usb3_rhub; + /* + * Some hosts incorrectly use sub-minor version for minor + * version (i.e. 0x02 instead of 0x20 for bcdUSB 0x320 and 0x01 + * for bcdUSB 0x310). Since there is no USB release with sub + * minor version 0x301 to 0x309, we can assume that they are + * incorrect and fix it here. + */ + if (minor_revision > 0x00 && minor_revision < 0x10) + minor_revision <<= 4; } else if (major_revision <= 0x02) { rhub = &xhci->usb2_rhub; } else { @@ -2240,6 +2249,9 @@ static void xhci_create_rhub_port_array(struct xhci_hcd *xhci, return; rhub->ports = kcalloc_node(rhub->num_ports, sizeof(*rhub->ports), flags, dev_to_node(dev)); + if (!rhub->ports) + return; + for (i = 0; i < HCS_MAX_PORTS(xhci->hcs_params1); i++) { if (xhci->hw_ports[i].rhub != rhub || xhci->hw_ports[i].hcd_portnum == DUPLICATE_ENTRY) diff --git a/drivers/usb/host/xhci-mtk-sch.c b/drivers/usb/host/xhci-mtk-sch.c index b45e5bf08997..8b90da5a6ed1 100644 --- a/drivers/usb/host/xhci-mtk-sch.c +++ b/drivers/usb/host/xhci-mtk-sch.c @@ -25,6 +25,15 @@ */ #define TT_MICROFRAMES_MAX 9 +#define DBG_BUF_EN 64 + +/* schedule error type */ +#define ESCH_SS_Y6 1001 +#define ESCH_SS_OVERLAP 1002 +#define ESCH_CS_OVERFLOW 1003 +#define ESCH_BW_OVERFLOW 1004 +#define ESCH_FIXME 1005 + /* mtk scheduler bitmasks */ #define EP_BPKTS(p) ((p) & 0x7f) #define EP_BCSCOUNT(p) (((p) & 0x7) << 8) @@ -32,13 +41,75 @@ #define EP_BOFFSET(p) ((p) & 0x3fff) #define EP_BREPEAT(p) (((p) & 0x7fff) << 16) +static char *sch_error_string(int err_num) +{ + switch (err_num) { + case ESCH_SS_Y6: + return "Can't schedule Start-Split in Y6"; + case ESCH_SS_OVERLAP: + return "Can't find a suitable Start-Split location"; + case ESCH_CS_OVERFLOW: + return "The last Complete-Split is greater than 7"; + case ESCH_BW_OVERFLOW: + return "Bandwidth exceeds the maximum limit"; + case ESCH_FIXME: + return "FIXME, to be resolved"; + default: + return "Unknown"; + } +} + static int is_fs_or_ls(enum usb_device_speed speed) { return speed == USB_SPEED_FULL || speed == USB_SPEED_LOW; } +static const char * +decode_ep(struct usb_host_endpoint *ep, enum usb_device_speed speed) +{ + static char buf[DBG_BUF_EN]; + struct usb_endpoint_descriptor *epd = &ep->desc; + unsigned int interval; + const char *unit; + + interval = usb_decode_interval(epd, speed); + if (interval % 1000) { + unit = "us"; + } else { + unit = "ms"; + interval /= 1000; + } + + snprintf(buf, DBG_BUF_EN, "%s ep%d%s %s, mpkt:%d, interval:%d/%d%s\n", + usb_speed_string(speed), usb_endpoint_num(epd), + usb_endpoint_dir_in(epd) ? "in" : "out", + usb_ep_type_string(usb_endpoint_type(epd)), + usb_endpoint_maxp(epd), epd->bInterval, interval, unit); + + return buf; +} + +static u32 get_bw_boundary(enum usb_device_speed speed) +{ + u32 boundary; + + switch (speed) { + case USB_SPEED_SUPER_PLUS: + boundary = SSP_BW_BOUNDARY; + break; + case USB_SPEED_SUPER: + boundary = SS_BW_BOUNDARY; + break; + default: + boundary = HS_BW_BOUNDARY; + break; + } + + return boundary; +} + /* -* get the index of bandwidth domains array which @ep belongs to. +* get the bandwidth domain which @ep belongs to. * * the bandwidth domain array is saved to @sch_array of struct xhci_hcd_mtk, * each HS root port is treated as a single bandwidth domain, @@ -49,9 +120,11 @@ static int is_fs_or_ls(enum usb_device_speed speed) * so the bandwidth domain array is organized as follow for simplification: * SSport0-OUT, SSport0-IN, ..., SSportX-OUT, SSportX-IN, HSport0, ..., HSportY */ -static int get_bw_index(struct xhci_hcd *xhci, struct usb_device *udev, - struct usb_host_endpoint *ep) +static struct mu3h_sch_bw_info * +get_bw_info(struct xhci_hcd_mtk *mtk, struct usb_device *udev, + struct usb_host_endpoint *ep) { + struct xhci_hcd *xhci = hcd_to_xhci(mtk->hcd); struct xhci_virt_device *virt_dev; int bw_index; @@ -67,7 +140,7 @@ static int get_bw_index(struct xhci_hcd *xhci, struct usb_device *udev, bw_index = virt_dev->real_port + xhci->usb3_rhub.num_ports - 1; } - return bw_index; + return &mtk->sch_array[bw_index]; } static u32 get_esit(struct xhci_ep_ctx *ep_ctx) @@ -85,7 +158,6 @@ static struct mu3h_sch_tt *find_tt(struct usb_device *udev) { struct usb_tt *utt = udev->tt; struct mu3h_sch_tt *tt, **tt_index, **ptt; - unsigned int port; bool allocated_index = false; if (!utt) @@ -107,10 +179,8 @@ static struct mu3h_sch_tt *find_tt(struct usb_device *udev) utt->hcpriv = tt_index; allocated_index = true; } - port = udev->ttport - 1; - ptt = &tt_index[port]; + ptt = &tt_index[udev->ttport - 1]; } else { - port = 0; ptt = (struct mu3h_sch_tt **) &utt->hcpriv; } @@ -125,8 +195,6 @@ static struct mu3h_sch_tt *find_tt(struct usb_device *udev) return ERR_PTR(-ENOMEM); } INIT_LIST_HEAD(&tt->ep_list); - tt->usb_tt = utt; - tt->tt_port = port; *ptt = tt; } @@ -200,14 +268,15 @@ static struct mu3h_sch_ep_info *create_sch_ep(struct usb_device *udev, sch_ep->sch_tt = tt; sch_ep->ep = ep; + sch_ep->speed = udev->speed; INIT_LIST_HEAD(&sch_ep->endpoint); INIT_LIST_HEAD(&sch_ep->tt_endpoint); return sch_ep; } -static void setup_sch_info(struct usb_device *udev, - struct xhci_ep_ctx *ep_ctx, struct mu3h_sch_ep_info *sch_ep) +static void setup_sch_info(struct xhci_ep_ctx *ep_ctx, + struct mu3h_sch_ep_info *sch_ep) { u32 ep_type; u32 maxpkt; @@ -234,7 +303,7 @@ static void setup_sch_info(struct usb_device *udev, sch_ep->burst_mode = 0; sch_ep->repeat = 0; - if (udev->speed == USB_SPEED_HIGH) { + if (sch_ep->speed == USB_SPEED_HIGH) { sch_ep->cs_count = 0; /* @@ -252,7 +321,7 @@ static void setup_sch_info(struct usb_device *udev, sch_ep->pkts = max_burst + 1; sch_ep->bw_cost_per_microframe = maxpkt * sch_ep->pkts; bwb_table[0] = sch_ep->bw_cost_per_microframe; - } else if (udev->speed >= USB_SPEED_SUPER) { + } else if (sch_ep->speed >= USB_SPEED_SUPER) { /* usb3_r1 spec section4.4.7 & 4.4.8 */ sch_ep->cs_count = 0; sch_ep->burst_mode = 1; @@ -272,7 +341,6 @@ static void setup_sch_info(struct usb_device *udev, } if (ep_type == ISOC_IN_EP || ep_type == ISOC_OUT_EP) { - u32 remainder; if (sch_ep->esit == 1) sch_ep->pkts = esit_pkts; @@ -288,16 +356,14 @@ static void setup_sch_info(struct usb_device *udev, sch_ep->repeat = !!(sch_ep->num_budget_microframes > 1); sch_ep->bw_cost_per_microframe = maxpkt * sch_ep->pkts; - remainder = sch_ep->bw_cost_per_microframe; - remainder *= sch_ep->num_budget_microframes; - remainder -= (maxpkt * esit_pkts); for (i = 0; i < sch_ep->num_budget_microframes - 1; i++) bwb_table[i] = sch_ep->bw_cost_per_microframe; /* last one <= bw_cost_per_microframe */ - bwb_table[i] = remainder; + bwb_table[i] = maxpkt * esit_pkts + - i * sch_ep->bw_cost_per_microframe; } - } else if (is_fs_or_ls(udev->speed)) { + } else if (is_fs_or_ls(sch_ep->speed)) { sch_ep->pkts = 1; /* at most one packet for each microframe */ /* @@ -375,21 +441,42 @@ static void update_bus_bw(struct mu3h_sch_bw_info *sch_bw, sch_ep->bw_budget_table[j]; } } - sch_ep->allocated = used; } -static int check_sch_tt(struct usb_device *udev, - struct mu3h_sch_ep_info *sch_ep, u32 offset) +static int check_fs_bus_bw(struct mu3h_sch_ep_info *sch_ep, int offset) +{ + struct mu3h_sch_tt *tt = sch_ep->sch_tt; + u32 num_esit, tmp; + int base; + int i, j; + + num_esit = XHCI_MTK_MAX_ESIT / sch_ep->esit; + for (i = 0; i < num_esit; i++) { + base = offset + i * sch_ep->esit; + + /* + * Compared with hs bus, no matter what ep type, + * the hub will always delay one uframe to send data + */ + for (j = 0; j < sch_ep->cs_count; j++) { + tmp = tt->fs_bus_bw[base + j] + sch_ep->bw_cost_per_microframe; + if (tmp > FS_PAYLOAD_MAX) + return -ESCH_BW_OVERFLOW; + } + } + + return 0; +} + +static int check_sch_tt(struct mu3h_sch_ep_info *sch_ep, u32 offset) { struct mu3h_sch_tt *tt = sch_ep->sch_tt; u32 extra_cs_count; - u32 fs_budget_start; u32 start_ss, last_ss; u32 start_cs, last_cs; int i; start_ss = offset % 8; - fs_budget_start = (start_ss + 1) % 8; if (sch_ep->ep_type == ISOC_OUT_EP) { last_ss = start_ss + sch_ep->cs_count - 1; @@ -399,11 +486,11 @@ static int check_sch_tt(struct usb_device *udev, * must never schedule Start-Split in Y6 */ if (!(start_ss == 7 || last_ss < 6)) - return -ERANGE; + return -ESCH_SS_Y6; for (i = 0; i < sch_ep->cs_count; i++) - if (test_bit(offset + i, tt->split_bit_map)) - return -ERANGE; + if (test_bit(offset + i, tt->ss_bit_map)) + return -ESCH_SS_OVERLAP; } else { u32 cs_count = DIV_ROUND_UP(sch_ep->maxpkt, FS_PAYLOAD_MAX); @@ -413,28 +500,26 @@ static int check_sch_tt(struct usb_device *udev, * must never schedule Start-Split in Y6 */ if (start_ss == 6) - return -ERANGE; + return -ESCH_SS_Y6; /* one uframe for ss + one uframe for idle */ start_cs = (start_ss + 2) % 8; last_cs = start_cs + cs_count - 1; if (last_cs > 7) - return -ERANGE; + return -ESCH_CS_OVERFLOW; if (sch_ep->ep_type == ISOC_IN_EP) extra_cs_count = (last_cs == 7) ? 1 : 2; else /* ep_type : INTR IN / INTR OUT */ - extra_cs_count = (fs_budget_start == 6) ? 1 : 2; + extra_cs_count = 1; cs_count += extra_cs_count; if (cs_count > 7) cs_count = 7; /* HW limit */ - for (i = 0; i < cs_count + 2; i++) { - if (test_bit(offset + i, tt->split_bit_map)) - return -ERANGE; - } + if (test_bit(offset, tt->ss_bit_map)) + return -ESCH_SS_OVERLAP; sch_ep->cs_count = cs_count; /* one for ss, the other for idle */ @@ -448,41 +533,85 @@ static int check_sch_tt(struct usb_device *udev, sch_ep->num_budget_microframes = sch_ep->esit; } - return 0; + return check_fs_bus_bw(sch_ep, offset); } -static void update_sch_tt(struct usb_device *udev, - struct mu3h_sch_ep_info *sch_ep) +static void update_sch_tt(struct mu3h_sch_ep_info *sch_ep, bool used) { struct mu3h_sch_tt *tt = sch_ep->sch_tt; u32 base, num_esit; + int bw_updated; + int bits; int i, j; num_esit = XHCI_MTK_MAX_ESIT / sch_ep->esit; + bits = (sch_ep->ep_type == ISOC_OUT_EP) ? sch_ep->cs_count : 1; + + if (used) + bw_updated = sch_ep->bw_cost_per_microframe; + else + bw_updated = -sch_ep->bw_cost_per_microframe; + for (i = 0; i < num_esit; i++) { base = sch_ep->offset + i * sch_ep->esit; - for (j = 0; j < sch_ep->num_budget_microframes; j++) - set_bit(base + j, tt->split_bit_map); + + for (j = 0; j < bits; j++) { + if (used) + set_bit(base + j, tt->ss_bit_map); + else + clear_bit(base + j, tt->ss_bit_map); + } + + for (j = 0; j < sch_ep->cs_count; j++) + tt->fs_bus_bw[base + j] += bw_updated; } - list_add_tail(&sch_ep->tt_endpoint, &tt->ep_list); + if (used) + list_add_tail(&sch_ep->tt_endpoint, &tt->ep_list); + else + list_del(&sch_ep->tt_endpoint); } -static int check_sch_bw(struct usb_device *udev, - struct mu3h_sch_bw_info *sch_bw, struct mu3h_sch_ep_info *sch_ep) +static int load_ep_bw(struct mu3h_sch_bw_info *sch_bw, + struct mu3h_sch_ep_info *sch_ep, bool loaded) +{ + if (sch_ep->sch_tt) + update_sch_tt(sch_ep, loaded); + + /* update bus bandwidth info */ + update_bus_bw(sch_bw, sch_ep, loaded); + sch_ep->allocated = loaded; + + return 0; +} + +static u32 get_esit_boundary(struct mu3h_sch_ep_info *sch_ep) +{ + u32 boundary = sch_ep->esit; + + if (sch_ep->sch_tt) { /* LS/FS with TT */ + /* tune for CS */ + if (sch_ep->ep_type != ISOC_OUT_EP) + boundary++; + else if (boundary > 1) /* normally esit >= 8 for FS/LS */ + boundary--; + } + + return boundary; +} + +static int check_sch_bw(struct mu3h_sch_bw_info *sch_bw, + struct mu3h_sch_ep_info *sch_ep) { u32 offset; - u32 esit; u32 min_bw; u32 min_index; u32 worst_bw; u32 bw_boundary; + u32 esit_boundary; u32 min_num_budget; u32 min_cs_count; - bool tt_offset_ok = false; - int ret; - - esit = sch_ep->esit; + int ret = 0; /* * Search through all possible schedule microframes. @@ -492,16 +621,15 @@ static int check_sch_bw(struct usb_device *udev, min_index = 0; min_cs_count = sch_ep->cs_count; min_num_budget = sch_ep->num_budget_microframes; - for (offset = 0; offset < esit; offset++) { - if (is_fs_or_ls(udev->speed)) { - ret = check_sch_tt(udev, sch_ep, offset); + esit_boundary = get_esit_boundary(sch_ep); + for (offset = 0; offset < sch_ep->esit; offset++) { + if (sch_ep->sch_tt) { + ret = check_sch_tt(sch_ep, offset); if (ret) continue; - else - tt_offset_ok = true; } - if ((offset + sch_ep->num_budget_microframes) > sch_ep->esit) + if ((offset + sch_ep->num_budget_microframes) > esit_boundary) break; worst_bw = get_max_bw(sch_bw, sch_ep, offset); @@ -515,33 +643,16 @@ static int check_sch_bw(struct usb_device *udev, break; } - if (udev->speed == USB_SPEED_SUPER_PLUS) - bw_boundary = SSP_BW_BOUNDARY; - else if (udev->speed == USB_SPEED_SUPER) - bw_boundary = SS_BW_BOUNDARY; - else - bw_boundary = HS_BW_BOUNDARY; - + bw_boundary = get_bw_boundary(sch_ep->speed); /* check bandwidth */ if (min_bw > bw_boundary) - return -ERANGE; + return ret ? ret : -ESCH_BW_OVERFLOW; sch_ep->offset = min_index; sch_ep->cs_count = min_cs_count; sch_ep->num_budget_microframes = min_num_budget; - if (is_fs_or_ls(udev->speed)) { - /* all offset for tt is not ok*/ - if (!tt_offset_ok) - return -ERANGE; - - update_sch_tt(udev, sch_ep); - } - - /* update bus bandwidth info */ - update_bus_bw(sch_bw, sch_ep, 1); - - return 0; + return load_ep_bw(sch_bw, sch_ep, true); } static void destroy_sch_ep(struct usb_device *udev, @@ -549,14 +660,12 @@ static void destroy_sch_ep(struct usb_device *udev, { /* only release ep bw check passed by check_sch_bw() */ if (sch_ep->allocated) - update_bus_bw(sch_bw, sch_ep, 0); + load_ep_bw(sch_bw, sch_ep, false); + + if (sch_ep->sch_tt) + drop_tt(udev); list_del(&sch_ep->endpoint); - - if (sch_ep->sch_tt) { - list_del(&sch_ep->tt_endpoint); - drop_tt(udev); - } kfree(sch_ep); } @@ -606,44 +715,36 @@ int xhci_mtk_sch_init(struct xhci_hcd_mtk *mtk) return 0; } -EXPORT_SYMBOL_GPL(xhci_mtk_sch_init); void xhci_mtk_sch_exit(struct xhci_hcd_mtk *mtk) { kfree(mtk->sch_array); } -EXPORT_SYMBOL_GPL(xhci_mtk_sch_exit); -int xhci_mtk_add_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev, - struct usb_host_endpoint *ep) +static int add_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev, + struct usb_host_endpoint *ep) { struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd); - struct xhci_hcd *xhci; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); struct xhci_ep_ctx *ep_ctx; - struct xhci_slot_ctx *slot_ctx; struct xhci_virt_device *virt_dev; struct mu3h_sch_ep_info *sch_ep; unsigned int ep_index; - xhci = hcd_to_xhci(hcd); virt_dev = xhci->devs[udev->slot_id]; ep_index = xhci_get_endpoint_index(&ep->desc); - slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx); ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->in_ctx, ep_index); - xhci_dbg(xhci, "%s() type:%d, speed:%d, mpkt:%d, dir:%d, ep:%p\n", - __func__, usb_endpoint_type(&ep->desc), udev->speed, - usb_endpoint_maxp(&ep->desc), - usb_endpoint_dir_in(&ep->desc), ep); + xhci_dbg(xhci, "%s %s\n", __func__, decode_ep(ep, udev->speed)); - if (!need_bw_sch(ep, udev->speed, slot_ctx->tt_info & TT_SLOT)) { + if (!need_bw_sch(ep, udev->speed, !!virt_dev->tt_info)) { /* * set @bpkts to 1 if it is LS or FS periodic endpoint, and its * device does not connected through an external HS hub */ if (usb_endpoint_xfer_int(&ep->desc) || usb_endpoint_xfer_isoc(&ep->desc)) - ep_ctx->reserved[0] |= cpu_to_le32(EP_BPKTS(1)); + ep_ctx->reserved[0] = cpu_to_le32(EP_BPKTS(1)); return 0; } @@ -652,41 +753,30 @@ int xhci_mtk_add_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev, if (IS_ERR_OR_NULL(sch_ep)) return -ENOMEM; - setup_sch_info(udev, ep_ctx, sch_ep); + setup_sch_info(ep_ctx, sch_ep); list_add_tail(&sch_ep->endpoint, &mtk->bw_ep_chk_list); return 0; } -EXPORT_SYMBOL_GPL(xhci_mtk_add_ep_quirk); -void xhci_mtk_drop_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev, - struct usb_host_endpoint *ep) +static void drop_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev, + struct usb_host_endpoint *ep) { struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd); - struct xhci_hcd *xhci; - struct xhci_slot_ctx *slot_ctx; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); struct xhci_virt_device *virt_dev; - struct mu3h_sch_bw_info *sch_array; struct mu3h_sch_bw_info *sch_bw; struct mu3h_sch_ep_info *sch_ep, *tmp; - int bw_index; - xhci = hcd_to_xhci(hcd); virt_dev = xhci->devs[udev->slot_id]; - slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx); - sch_array = mtk->sch_array; - xhci_dbg(xhci, "%s() type:%d, speed:%d, mpks:%d, dir:%d, ep:%p\n", - __func__, usb_endpoint_type(&ep->desc), udev->speed, - usb_endpoint_maxp(&ep->desc), - usb_endpoint_dir_in(&ep->desc), ep); + xhci_dbg(xhci, "%s %s\n", __func__, decode_ep(ep, udev->speed)); - if (!need_bw_sch(ep, udev->speed, slot_ctx->tt_info & TT_SLOT)) + if (!need_bw_sch(ep, udev->speed, !!virt_dev->tt_info)) return; - bw_index = get_bw_index(xhci, udev, ep); - sch_bw = &sch_array[bw_index]; + sch_bw = get_bw_info(mtk, udev, ep); list_for_each_entry_safe(sch_ep, tmp, &sch_bw->bw_ep_list, endpoint) { if (sch_ep->ep == ep) { @@ -695,7 +785,6 @@ void xhci_mtk_drop_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev, } } } -EXPORT_SYMBOL_GPL(xhci_mtk_drop_ep_quirk); int xhci_mtk_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) { @@ -704,17 +793,17 @@ int xhci_mtk_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) struct xhci_virt_device *virt_dev = xhci->devs[udev->slot_id]; struct mu3h_sch_bw_info *sch_bw; struct mu3h_sch_ep_info *sch_ep, *tmp; - int bw_index, ret; + int ret; xhci_dbg(xhci, "%s() udev %s\n", __func__, dev_name(&udev->dev)); list_for_each_entry(sch_ep, &mtk->bw_ep_chk_list, endpoint) { - bw_index = get_bw_index(xhci, udev, sch_ep->ep); - sch_bw = &mtk->sch_array[bw_index]; + sch_bw = get_bw_info(mtk, udev, sch_ep->ep); - ret = check_sch_bw(udev, sch_bw, sch_ep); + ret = check_sch_bw(sch_bw, sch_ep); if (ret) { - xhci_err(xhci, "Not enough bandwidth!\n"); + xhci_err(xhci, "Not enough bandwidth! (%s)\n", + sch_error_string(-ret)); return -ENOSPC; } } @@ -724,16 +813,14 @@ int xhci_mtk_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) struct usb_host_endpoint *ep = sch_ep->ep; unsigned int ep_index = xhci_get_endpoint_index(&ep->desc); - bw_index = get_bw_index(xhci, udev, ep); - sch_bw = &mtk->sch_array[bw_index]; - + sch_bw = get_bw_info(mtk, udev, ep); list_move_tail(&sch_ep->endpoint, &sch_bw->bw_ep_list); ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->in_ctx, ep_index); - ep_ctx->reserved[0] |= cpu_to_le32(EP_BPKTS(sch_ep->pkts) + ep_ctx->reserved[0] = cpu_to_le32(EP_BPKTS(sch_ep->pkts) | EP_BCSCOUNT(sch_ep->cs_count) | EP_BBM(sch_ep->burst_mode)); - ep_ctx->reserved[1] |= cpu_to_le32(EP_BOFFSET(sch_ep->offset) + ep_ctx->reserved[1] = cpu_to_le32(EP_BOFFSET(sch_ep->offset) | EP_BREPEAT(sch_ep->repeat)); xhci_dbg(xhci, " PKTS:%x, CSCOUNT:%x, BM:%x, OFFSET:%x, REPEAT:%x\n", @@ -743,7 +830,6 @@ int xhci_mtk_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) return xhci_check_bandwidth(hcd, udev); } -EXPORT_SYMBOL_GPL(xhci_mtk_check_bandwidth); void xhci_mtk_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) { @@ -751,16 +837,43 @@ void xhci_mtk_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) struct xhci_hcd *xhci = hcd_to_xhci(hcd); struct mu3h_sch_bw_info *sch_bw; struct mu3h_sch_ep_info *sch_ep, *tmp; - int bw_index; xhci_dbg(xhci, "%s() udev %s\n", __func__, dev_name(&udev->dev)); list_for_each_entry_safe(sch_ep, tmp, &mtk->bw_ep_chk_list, endpoint) { - bw_index = get_bw_index(xhci, udev, sch_ep->ep); - sch_bw = &mtk->sch_array[bw_index]; + sch_bw = get_bw_info(mtk, udev, sch_ep->ep); destroy_sch_ep(udev, sch_bw, sch_ep); } xhci_reset_bandwidth(hcd, udev); } -EXPORT_SYMBOL_GPL(xhci_mtk_reset_bandwidth); + +int xhci_mtk_add_ep(struct usb_hcd *hcd, struct usb_device *udev, + struct usb_host_endpoint *ep) +{ + int ret; + + ret = xhci_add_endpoint(hcd, udev, ep); + if (ret) + return ret; + + if (ep->hcpriv) + ret = add_ep_quirk(hcd, udev, ep); + + return ret; +} + +int xhci_mtk_drop_ep(struct usb_hcd *hcd, struct usb_device *udev, + struct usb_host_endpoint *ep) +{ + int ret; + + ret = xhci_drop_endpoint(hcd, udev, ep); + if (ret) + return ret; + + if (ep->hcpriv) + drop_ep_quirk(hcd, udev, ep); + + return 0; +} diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c index 2f27dc0d9c6b..b2058b3bc834 100644 --- a/drivers/usb/host/xhci-mtk.c +++ b/drivers/usb/host/xhci-mtk.c @@ -7,7 +7,6 @@ * Chunfeng Yun */ -#include #include #include #include @@ -16,6 +15,7 @@ #include #include #include +#include #include #include @@ -57,12 +57,23 @@ #define CTRL_U2_FORCE_PLL_STB BIT(28) /* usb remote wakeup registers in syscon */ + /* mt8173 etc */ #define PERI_WK_CTRL1 0x4 #define WC1_IS_C(x) (((x) & 0xf) << 26) /* cycle debounce */ #define WC1_IS_EN BIT(25) #define WC1_IS_P BIT(6) /* polarity for ip sleep */ +/* mt8183 */ +#define PERI_WK_CTRL0 0x0 +#define WC0_IS_C(x) ((u32)(((x) & 0xf) << 28)) /* cycle debounce */ +#define WC0_IS_P BIT(12) /* polarity */ +#define WC0_IS_EN BIT(6) + +/* mt8192 */ +#define WC0_SSUSB0_CDEN BIT(6) +#define WC0_IS_SPM_EN BIT(1) + /* mt2712 etc */ #define PERI_SSUSB_SPM_CTRL 0x0 #define SSC_IP_SLEEP_EN BIT(4) @@ -71,6 +82,8 @@ enum ssusb_uwk_vers { SSUSB_UWK_V1 = 1, SSUSB_UWK_V2, + SSUSB_UWK_V1_1 = 101, /* specific revision 1.01 */ + SSUSB_UWK_V1_2, /* specific revision 1.2 */ }; static int xhci_mtk_host_enable(struct xhci_hcd_mtk *mtk) @@ -206,89 +219,6 @@ static int xhci_mtk_ssusb_config(struct xhci_hcd_mtk *mtk) return xhci_mtk_host_enable(mtk); } -static int xhci_mtk_clks_get(struct xhci_hcd_mtk *mtk) -{ - struct device *dev = mtk->dev; - - mtk->sys_clk = devm_clk_get(dev, "sys_ck"); - if (IS_ERR(mtk->sys_clk)) { - dev_err(dev, "fail to get sys_ck\n"); - return PTR_ERR(mtk->sys_clk); - } - - mtk->xhci_clk = devm_clk_get_optional(dev, "xhci_ck"); - if (IS_ERR(mtk->xhci_clk)) - return PTR_ERR(mtk->xhci_clk); - - mtk->ref_clk = devm_clk_get_optional(dev, "ref_ck"); - if (IS_ERR(mtk->ref_clk)) - return PTR_ERR(mtk->ref_clk); - - mtk->mcu_clk = devm_clk_get_optional(dev, "mcu_ck"); - if (IS_ERR(mtk->mcu_clk)) - return PTR_ERR(mtk->mcu_clk); - - mtk->dma_clk = devm_clk_get_optional(dev, "dma_ck"); - return PTR_ERR_OR_ZERO(mtk->dma_clk); -} - -static int xhci_mtk_clks_enable(struct xhci_hcd_mtk *mtk) -{ - int ret; - - ret = clk_prepare_enable(mtk->ref_clk); - if (ret) { - dev_err(mtk->dev, "failed to enable ref_clk\n"); - goto ref_clk_err; - } - - ret = clk_prepare_enable(mtk->sys_clk); - if (ret) { - dev_err(mtk->dev, "failed to enable sys_clk\n"); - goto sys_clk_err; - } - - ret = clk_prepare_enable(mtk->xhci_clk); - if (ret) { - dev_err(mtk->dev, "failed to enable xhci_clk\n"); - goto xhci_clk_err; - } - - ret = clk_prepare_enable(mtk->mcu_clk); - if (ret) { - dev_err(mtk->dev, "failed to enable mcu_clk\n"); - goto mcu_clk_err; - } - - ret = clk_prepare_enable(mtk->dma_clk); - if (ret) { - dev_err(mtk->dev, "failed to enable dma_clk\n"); - goto dma_clk_err; - } - - return 0; - -dma_clk_err: - clk_disable_unprepare(mtk->mcu_clk); -mcu_clk_err: - clk_disable_unprepare(mtk->xhci_clk); -xhci_clk_err: - clk_disable_unprepare(mtk->sys_clk); -sys_clk_err: - clk_disable_unprepare(mtk->ref_clk); -ref_clk_err: - return ret; -} - -static void xhci_mtk_clks_disable(struct xhci_hcd_mtk *mtk) -{ - clk_disable_unprepare(mtk->dma_clk); - clk_disable_unprepare(mtk->mcu_clk); - clk_disable_unprepare(mtk->xhci_clk); - clk_disable_unprepare(mtk->sys_clk); - clk_disable_unprepare(mtk->ref_clk); -} - /* only clocks can be turn off for ip-sleep wakeup mode */ static void usb_wakeup_ip_sleep_set(struct xhci_hcd_mtk *mtk, bool enable) { @@ -300,6 +230,16 @@ static void usb_wakeup_ip_sleep_set(struct xhci_hcd_mtk *mtk, bool enable) msk = WC1_IS_EN | WC1_IS_C(0xf) | WC1_IS_P; val = enable ? (WC1_IS_EN | WC1_IS_C(0x8)) : 0; break; + case SSUSB_UWK_V1_1: + reg = mtk->uwk_reg_base + PERI_WK_CTRL0; + msk = WC0_IS_EN | WC0_IS_C(0xf) | WC0_IS_P; + val = enable ? (WC0_IS_EN | WC0_IS_C(0x8)) : 0; + break; + case SSUSB_UWK_V1_2: + reg = mtk->uwk_reg_base + PERI_WK_CTRL0; + msk = WC0_SSUSB0_CDEN | WC0_IS_SPM_EN; + val = enable ? msk : 0; + break; case SSUSB_UWK_V2: reg = mtk->uwk_reg_base + PERI_SSUSB_SPM_CTRL; msk = SSC_IP_SLEEP_EN | SSC_SPM_INT_EN; @@ -335,7 +275,6 @@ static int usb_wakeup_of_property_parse(struct xhci_hcd_mtk *mtk, mtk->uwk_reg_base, mtk->uwk_vers); return PTR_ERR_OR_ZERO(mtk->uwk); - } static void usb_wakeup_set(struct xhci_hcd_mtk *mtk, bool enable) @@ -344,14 +283,18 @@ static void usb_wakeup_set(struct xhci_hcd_mtk *mtk, bool enable) usb_wakeup_ip_sleep_set(mtk, enable); } -static int xhci_mtk_setup(struct usb_hcd *hcd); -static const struct xhci_driver_overrides xhci_mtk_overrides __initconst = { - .reset = xhci_mtk_setup, - .check_bandwidth = xhci_mtk_check_bandwidth, - .reset_bandwidth = xhci_mtk_reset_bandwidth, -}; +static int xhci_mtk_clks_get(struct xhci_hcd_mtk *mtk) +{ + struct clk_bulk_data *clks = mtk->clks; -static struct hc_driver __read_mostly xhci_mtk_hc_driver; + clks[0].id = "sys_ck"; + clks[1].id = "xhci_ck"; + clks[2].id = "ref_ck"; + clks[3].id = "mcu_ck"; + clks[4].id = "dma_ck"; + + return devm_clk_bulk_get_optional(mtk->dev, BULK_CLKS_NUM, clks); +} static int xhci_mtk_ldos_enable(struct xhci_hcd_mtk *mtk) { @@ -397,6 +340,8 @@ static void xhci_mtk_quirks(struct device *dev, struct xhci_hcd *xhci) xhci->quirks |= XHCI_SPURIOUS_SUCCESS; if (mtk->lpm_support) xhci->quirks |= XHCI_LPM_SUPPORT; + if (mtk->u2_lpm_disable) + xhci->quirks |= XHCI_HW_LPM_DISABLE; /* * MTK xHCI 0.96: PSA is 1 by default even if doesn't support stream, @@ -431,6 +376,16 @@ static int xhci_mtk_setup(struct usb_hcd *hcd) return ret; } +static const struct xhci_driver_overrides xhci_mtk_overrides __initconst = { + .reset = xhci_mtk_setup, + .add_endpoint = xhci_mtk_add_ep, + .drop_endpoint = xhci_mtk_drop_ep, + .check_bandwidth = xhci_mtk_check_bandwidth, + .reset_bandwidth = xhci_mtk_reset_bandwidth, +}; + +static struct hc_driver __read_mostly xhci_mtk_hc_driver; + static int xhci_mtk_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -441,6 +396,7 @@ static int xhci_mtk_probe(struct platform_device *pdev) struct resource *res; struct usb_hcd *hcd; int ret = -ENODEV; + int wakeup_irq; int irq; if (usb_disabled()) @@ -468,7 +424,23 @@ static int xhci_mtk_probe(struct platform_device *pdev) if (ret) return ret; + irq = platform_get_irq_byname_optional(pdev, "host"); + if (irq < 0) { + if (irq == -EPROBE_DEFER) + return irq; + + /* for backward compatibility */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + } + + wakeup_irq = platform_get_irq_byname_optional(pdev, "wakeup"); + if (wakeup_irq == -EPROBE_DEFER) + return wakeup_irq; + mtk->lpm_support = of_property_read_bool(node, "usb3-lpm-capable"); + mtk->u2_lpm_disable = of_property_read_bool(node, "usb2-lpm-disable"); /* optional property, ignore the error if it does not exist */ of_property_read_u32(node, "mediatek,u3p-dis-msk", &mtk->u3p_dis_msk); @@ -479,24 +451,20 @@ static int xhci_mtk_probe(struct platform_device *pdev) return ret; } + pm_runtime_set_active(dev); + pm_runtime_use_autosuspend(dev); + pm_runtime_set_autosuspend_delay(dev, 4000); pm_runtime_enable(dev); pm_runtime_get_sync(dev); - device_enable_async_suspend(dev); ret = xhci_mtk_ldos_enable(mtk); if (ret) goto disable_pm; - ret = xhci_mtk_clks_enable(mtk); + ret = clk_bulk_prepare_enable(BULK_CLKS_NUM, mtk->clks); if (ret) goto disable_ldos; - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - ret = irq; - goto disable_clk; - } - hcd = usb_create_hcd(driver, dev, dev_name(dev)); if (!hcd) { ret = -ENOMEM; @@ -563,8 +531,26 @@ static int xhci_mtk_probe(struct platform_device *pdev) if (ret) goto dealloc_usb2_hcd; + if (wakeup_irq > 0) { + ret = dev_pm_set_dedicated_wake_irq(dev, wakeup_irq); + if (ret) { + dev_err(dev, "set wakeup irq %d failed\n", wakeup_irq); + goto dealloc_usb3_hcd; + } + dev_info(dev, "wakeup irq %d\n", wakeup_irq); + } + + device_enable_async_suspend(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + pm_runtime_forbid(dev); + return 0; +dealloc_usb3_hcd: + usb_remove_hcd(xhci->shared_hcd); + xhci->shared_hcd = NULL; + dealloc_usb2_hcd: usb_remove_hcd(hcd); @@ -579,53 +565,52 @@ static int xhci_mtk_probe(struct platform_device *pdev) usb_put_hcd(hcd); disable_clk: - xhci_mtk_clks_disable(mtk); + clk_bulk_disable_unprepare(BULK_CLKS_NUM, mtk->clks); disable_ldos: xhci_mtk_ldos_disable(mtk); disable_pm: - pm_runtime_put_sync(dev); + pm_runtime_put_sync_autosuspend(dev); pm_runtime_disable(dev); return ret; } -static int xhci_mtk_remove(struct platform_device *dev) +static int xhci_mtk_remove(struct platform_device *pdev) { - struct xhci_hcd_mtk *mtk = platform_get_drvdata(dev); + struct xhci_hcd_mtk *mtk = platform_get_drvdata(pdev); struct usb_hcd *hcd = mtk->hcd; struct xhci_hcd *xhci = hcd_to_xhci(hcd); struct usb_hcd *shared_hcd = xhci->shared_hcd; + struct device *dev = &pdev->dev; - pm_runtime_put_noidle(&dev->dev); - pm_runtime_disable(&dev->dev); + pm_runtime_get_sync(dev); + xhci->xhc_state |= XHCI_STATE_REMOVING; + dev_pm_clear_wake_irq(dev); + device_init_wakeup(dev, false); usb_remove_hcd(shared_hcd); xhci->shared_hcd = NULL; - device_init_wakeup(&dev->dev, false); - usb_remove_hcd(hcd); usb_put_hcd(shared_hcd); usb_put_hcd(hcd); xhci_mtk_sch_exit(mtk); - xhci_mtk_clks_disable(mtk); + clk_bulk_disable_unprepare(BULK_CLKS_NUM, mtk->clks); xhci_mtk_ldos_disable(mtk); + pm_runtime_disable(dev); + pm_runtime_put_noidle(dev); + pm_runtime_set_suspended(dev); + return 0; } -/* - * if ip sleep fails, and all clocks are disabled, access register will hang - * AHB bus, so stop polling roothubs to avoid regs access on bus suspend. - * and no need to check whether ip sleep failed or not; this will cause SPM - * to wake up system immediately after system suspend complete if ip sleep - * fails, it is what we wanted. - */ static int __maybe_unused xhci_mtk_suspend(struct device *dev) { struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev); struct usb_hcd *hcd = mtk->hcd; struct xhci_hcd *xhci = hcd_to_xhci(hcd); + int ret; xhci_dbg(xhci, "%s: stop port polling\n", __func__); clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); @@ -633,10 +618,21 @@ static int __maybe_unused xhci_mtk_suspend(struct device *dev) clear_bit(HCD_FLAG_POLL_RH, &xhci->shared_hcd->flags); del_timer_sync(&xhci->shared_hcd->rh_timer); - xhci_mtk_host_disable(mtk); - xhci_mtk_clks_disable(mtk); + ret = xhci_mtk_host_disable(mtk); + if (ret) + goto restart_poll_rh; + + clk_bulk_disable_unprepare(BULK_CLKS_NUM, mtk->clks); usb_wakeup_set(mtk, true); return 0; + +restart_poll_rh: + xhci_dbg(xhci, "%s: restart port polling\n", __func__); + set_bit(HCD_FLAG_POLL_RH, &xhci->shared_hcd->flags); + usb_hcd_poll_rh_status(xhci->shared_hcd); + set_bit(HCD_FLAG_POLL_RH, &hcd->flags); + usb_hcd_poll_rh_status(hcd); + return ret; } static int __maybe_unused xhci_mtk_resume(struct device *dev) @@ -644,10 +640,16 @@ static int __maybe_unused xhci_mtk_resume(struct device *dev) struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev); struct usb_hcd *hcd = mtk->hcd; struct xhci_hcd *xhci = hcd_to_xhci(hcd); + int ret; usb_wakeup_set(mtk, false); - xhci_mtk_clks_enable(mtk); - xhci_mtk_host_enable(mtk); + ret = clk_bulk_prepare_enable(BULK_CLKS_NUM, mtk->clks); + if (ret) + goto enable_wakeup; + + ret = xhci_mtk_host_enable(mtk); + if (ret) + goto disable_clks; xhci_dbg(xhci, "%s: restart port polling\n", __func__); set_bit(HCD_FLAG_POLL_RH, &xhci->shared_hcd->flags); @@ -655,21 +657,59 @@ static int __maybe_unused xhci_mtk_resume(struct device *dev) set_bit(HCD_FLAG_POLL_RH, &hcd->flags); usb_hcd_poll_rh_status(hcd); return 0; + +disable_clks: + clk_bulk_disable_unprepare(BULK_CLKS_NUM, mtk->clks); +enable_wakeup: + usb_wakeup_set(mtk, true); + return ret; +} + +static int __maybe_unused xhci_mtk_runtime_suspend(struct device *dev) +{ + struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev); + struct xhci_hcd *xhci = hcd_to_xhci(mtk->hcd); + int ret = 0; + + if (xhci->xhc_state) + return -ESHUTDOWN; + + if (device_may_wakeup(dev)) + ret = xhci_mtk_suspend(dev); + + /* -EBUSY: let PM automatically reschedule another autosuspend */ + return ret ? -EBUSY : 0; +} + +static int __maybe_unused xhci_mtk_runtime_resume(struct device *dev) +{ + struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev); + struct xhci_hcd *xhci = hcd_to_xhci(mtk->hcd); + int ret = 0; + + if (xhci->xhc_state) + return -ESHUTDOWN; + + if (device_may_wakeup(dev)) + ret = xhci_mtk_resume(dev); + + return ret; } static const struct dev_pm_ops xhci_mtk_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(xhci_mtk_suspend, xhci_mtk_resume) + SET_RUNTIME_PM_OPS(xhci_mtk_runtime_suspend, + xhci_mtk_runtime_resume, NULL) }; -#define DEV_PM_OPS IS_ENABLED(CONFIG_PM) ? &xhci_mtk_pm_ops : NULL -#ifdef CONFIG_OF +#define DEV_PM_OPS (IS_ENABLED(CONFIG_PM) ? &xhci_mtk_pm_ops : NULL) + static const struct of_device_id mtk_xhci_of_match[] = { { .compatible = "mediatek,mt8173-xhci"}, { .compatible = "mediatek,mtk-xhci"}, { }, }; MODULE_DEVICE_TABLE(of, mtk_xhci_of_match); -#endif static struct platform_driver mtk_xhci_driver = { .probe = xhci_mtk_probe, @@ -677,10 +717,9 @@ static struct platform_driver mtk_xhci_driver = { .driver = { .name = "xhci-mtk", .pm = DEV_PM_OPS, - .of_match_table = of_match_ptr(mtk_xhci_of_match), + .of_match_table = mtk_xhci_of_match, }, }; -MODULE_ALIAS("platform:xhci-mtk"); static int __init xhci_mtk_init(void) { diff --git a/drivers/usb/host/xhci-mtk.h b/drivers/usb/host/xhci-mtk.h index cbb09dfea62e..cd3a37bb73e6 100644 --- a/drivers/usb/host/xhci-mtk.h +++ b/drivers/usb/host/xhci-mtk.h @@ -9,8 +9,12 @@ #ifndef _XHCI_MTK_H_ #define _XHCI_MTK_H_ +#include + #include "xhci.h" +#define BULK_CLKS_NUM 5 + /** * To simplify scheduler algorithm, set a upper limit for ESIT, * if a synchromous ep's ESIT is larger than @XHCI_MTK_MAX_ESIT, @@ -20,16 +24,14 @@ #define XHCI_MTK_MAX_ESIT 64 /** - * @split_bit_map: used to avoid split microframes overlay + * @ss_bit_map: used to avoid start split microframes overlay + * @fs_bus_bw: array to keep track of bandwidth already used for FS * @ep_list: Endpoints using this TT - * @usb_tt: usb TT related - * @tt_port: TT port number */ struct mu3h_sch_tt { - DECLARE_BITMAP(split_bit_map, XHCI_MTK_MAX_ESIT); + DECLARE_BITMAP(ss_bit_map, XHCI_MTK_MAX_ESIT); + u32 fs_bus_bw[XHCI_MTK_MAX_ESIT]; struct list_head ep_list; - struct usb_tt *usb_tt; - int tt_port; }; /** @@ -86,7 +88,8 @@ struct mu3h_sch_ep_info { struct mu3h_sch_tt *sch_tt; u32 ep_type; u32 maxpkt; - void *ep; + struct usb_host_endpoint *ep; + enum usb_device_speed speed; bool allocated; /* * mtk xHCI scheduling information put into reserved DWs @@ -141,15 +144,9 @@ struct xhci_hcd_mtk { int u3p_dis_msk; struct regulator *vusb33; struct regulator *vbus; - struct clk *sys_clk; /* sys and mac clock */ - struct clk *xhci_clk; - struct clk *ref_clk; - struct clk *mcu_clk; - struct clk *dma_clk; - struct regmap *pericfg; - struct phy **phys; - int num_phys; + struct clk_bulk_data clks[BULK_CLKS_NUM]; bool lpm_support; + bool u2_lpm_disable; /* usb remote wakeup */ bool uwk_en; struct regmap *uwk; @@ -162,38 +159,13 @@ static inline struct xhci_hcd_mtk *hcd_to_mtk(struct usb_hcd *hcd) return dev_get_drvdata(hcd->self.controller); } -#if IS_ENABLED(CONFIG_USB_XHCI_MTK) int xhci_mtk_sch_init(struct xhci_hcd_mtk *mtk); void xhci_mtk_sch_exit(struct xhci_hcd_mtk *mtk); -int xhci_mtk_add_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev, - struct usb_host_endpoint *ep); -void xhci_mtk_drop_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev, - struct usb_host_endpoint *ep); +int xhci_mtk_add_ep(struct usb_hcd *hcd, struct usb_device *udev, + struct usb_host_endpoint *ep); +int xhci_mtk_drop_ep(struct usb_hcd *hcd, struct usb_device *udev, + struct usb_host_endpoint *ep); int xhci_mtk_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev); void xhci_mtk_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev); -#else -static inline int xhci_mtk_add_ep_quirk(struct usb_hcd *hcd, - struct usb_device *udev, struct usb_host_endpoint *ep) -{ - return 0; -} - -static inline void xhci_mtk_drop_ep_quirk(struct usb_hcd *hcd, - struct usb_device *udev, struct usb_host_endpoint *ep) -{ -} - -static inline int xhci_mtk_check_bandwidth(struct usb_hcd *hcd, - struct usb_device *udev) -{ - return 0; -} - -static inline void xhci_mtk_reset_bandwidth(struct usb_hcd *hcd, - struct usb_device *udev) -{ -} -#endif - #endif /* _XHCI_MTK_H_ */ diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index ce38076901e2..05c38dd3ee36 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -57,7 +57,6 @@ #include #include "xhci.h" #include "xhci-trace.h" -#include "xhci-mtk.h" static int queue_command(struct xhci_hcd *xhci, struct xhci_command *cmd, u32 field1, u32 field2, @@ -2129,16 +2128,13 @@ int xhci_is_vendor_info_code(struct xhci_hcd *xhci, unsigned int trb_comp_code) return 0; } -static int finish_td(struct xhci_hcd *xhci, struct xhci_td *td, - struct xhci_transfer_event *event, struct xhci_virt_ep *ep) +static int finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, + struct xhci_ring *ep_ring, struct xhci_td *td, + u32 trb_comp_code) { struct xhci_ep_ctx *ep_ctx; - struct xhci_ring *ep_ring; - u32 trb_comp_code; - ep_ring = xhci_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer)); ep_ctx = xhci_get_ep_ctx(xhci, ep->vdev->out_ctx, ep->ep_index); - trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len)); switch (trb_comp_code) { case COMP_STOPPED_LENGTH_INVALID: @@ -2234,9 +2230,9 @@ static int sum_trb_lengths(struct xhci_hcd *xhci, struct xhci_ring *ring, /* * Process control tds, update urb status and actual_length. */ -static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, - union xhci_trb *ep_trb, struct xhci_transfer_event *event, - struct xhci_virt_ep *ep) +static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, + struct xhci_ring *ep_ring, struct xhci_td *td, + union xhci_trb *ep_trb, struct xhci_transfer_event *event) { struct xhci_ep_ctx *ep_ctx; u32 trb_comp_code; @@ -2324,15 +2320,15 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, td->urb->actual_length = requested; finish_td: - return finish_td(xhci, td, event, ep); + return finish_td(xhci, ep, ep_ring, td, trb_comp_code); } /* * Process isochronous tds, update urb packet status and actual_length. */ -static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td, - union xhci_trb *ep_trb, struct xhci_transfer_event *event, - struct xhci_virt_ep *ep) +static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, + struct xhci_ring *ep_ring, struct xhci_td *td, + union xhci_trb *ep_trb, struct xhci_transfer_event *event) { struct urb_priv *urb_priv; int idx; @@ -2409,7 +2405,7 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td, td->urb->actual_length += frame->actual_length; - return finish_td(xhci, td, event, ep); + return finish_td(xhci, ep, ep_ring, td, trb_comp_code); } static int skip_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td, @@ -2441,17 +2437,15 @@ static int skip_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td, /* * Process bulk and interrupt tds, update urb status and actual_length. */ -static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td, - union xhci_trb *ep_trb, struct xhci_transfer_event *event, - struct xhci_virt_ep *ep) +static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, + struct xhci_ring *ep_ring, struct xhci_td *td, + union xhci_trb *ep_trb, struct xhci_transfer_event *event) { struct xhci_slot_ctx *slot_ctx; - struct xhci_ring *ep_ring; u32 trb_comp_code; u32 remaining, requested, ep_trb_len; slot_ctx = xhci_get_slot_ctx(xhci, ep->vdev->out_ctx); - ep_ring = xhci_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer)); trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len)); remaining = EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)); ep_trb_len = TRB_LEN(le32_to_cpu(ep_trb->generic.field[2])); @@ -2511,7 +2505,8 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td, remaining); td->urb->actual_length = 0; } - return finish_td(xhci, td, event, ep); + + return finish_td(xhci, ep, ep_ring, td, trb_comp_code); } /* @@ -2854,11 +2849,11 @@ static int handle_tx_event(struct xhci_hcd *xhci, /* update the urb's actual_length and give back to the core */ if (usb_endpoint_xfer_control(&td->urb->ep->desc)) - process_ctrl_td(xhci, td, ep_trb, event, ep); + process_ctrl_td(xhci, ep, ep_ring, td, ep_trb, event); else if (usb_endpoint_xfer_isoc(&td->urb->ep->desc)) - process_isoc_td(xhci, td, ep_trb, event, ep); + process_isoc_td(xhci, ep, ep_ring, td, ep_trb, event); else - process_bulk_intr_td(xhci, td, ep_trb, event, ep); + process_bulk_intr_td(xhci, ep, ep_ring, td, ep_trb, event); cleanup: handling_skipped_tds = ep->skip && trb_comp_code != COMP_MISSED_SERVICE_ERROR && @@ -3015,12 +3010,11 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) struct xhci_hcd *xhci = hcd_to_xhci(hcd); union xhci_trb *event_ring_deq; irqreturn_t ret = IRQ_NONE; - unsigned long flags; u64 temp_64; u32 status; int event_loop = 0; - spin_lock_irqsave(&xhci->lock, flags); + spin_lock(&xhci->lock); /* Check if the xHC generated the interrupt, or the irq is shared */ status = readl(&xhci->op_regs->status); if (status == ~(u32)0) { @@ -3083,7 +3077,7 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) ret = IRQ_HANDLED; out: - spin_unlock_irqrestore(&xhci->lock, flags); + spin_unlock(&xhci->lock); return ret; } diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 1975016f46bf..ca9385d22f68 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -20,7 +20,6 @@ #include "xhci.h" #include "xhci-trace.h" -#include "xhci-mtk.h" #include "xhci-debugfs.h" #include "xhci-dbgcap.h" @@ -228,6 +227,7 @@ static void xhci_zero_64b_regs(struct xhci_hcd *xhci) struct device *dev = xhci_to_hcd(xhci)->self.sysdev; int err, i; u64 val; + u32 intrs; /* * Some Renesas controllers get into a weird state if they are @@ -266,7 +266,10 @@ static void xhci_zero_64b_regs(struct xhci_hcd *xhci) if (upper_32_bits(val)) xhci_write_64(xhci, 0, &xhci->op_regs->cmd_ring); - for (i = 0; i < HCS_MAX_INTRS(xhci->hcs_params1); i++) { + intrs = min_t(u32, HCS_MAX_INTRS(xhci->hcs_params1), + ARRAY_SIZE(xhci->run_regs->ir_set)); + + for (i = 0; i < intrs; i++) { struct xhci_intr_reg __iomem *ir; ir = &xhci->run_regs->ir_set[i]; @@ -1428,6 +1431,7 @@ unsigned int xhci_get_endpoint_index(struct usb_endpoint_descriptor *desc) (usb_endpoint_dir_in(desc) ? 1 : 0) - 1; return index; } +EXPORT_SYMBOL_GPL(xhci_get_endpoint_index); /* The reverse operation to xhci_get_endpoint_index. Calculate the USB endpoint * address from the XHCI endpoint index. @@ -1860,8 +1864,8 @@ static int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) * disabled, so there's no need for mutual exclusion to protect * the xhci->devs[slot_id] structure. */ -static int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev, - struct usb_host_endpoint *ep) +int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev, + struct usb_host_endpoint *ep) { struct xhci_hcd *xhci; struct xhci_container_ctx *in_ctx, *out_ctx; @@ -1921,9 +1925,6 @@ static int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev, xhci_endpoint_zero(xhci, xhci->devs[udev->slot_id], ep); - if (xhci->quirks & XHCI_MTK_HOST) - xhci_mtk_drop_ep_quirk(hcd, udev, ep); - xhci_dbg(xhci, "drop ep 0x%x, slot id %d, new drop flags = %#x, new add flags = %#x\n", (unsigned int) ep->desc.bEndpointAddress, udev->slot_id, @@ -1931,6 +1932,7 @@ static int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev, (unsigned int) new_add_flags); return 0; } +EXPORT_SYMBOL_GPL(xhci_drop_endpoint); /* Add an endpoint to a new possible bandwidth configuration for this device. * Only one call to this function is allowed per endpoint before @@ -1945,8 +1947,8 @@ static int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev, * configuration or alt setting is installed in the device, so there's no need * for mutual exclusion to protect the xhci->devs[slot_id] structure. */ -static int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, - struct usb_host_endpoint *ep) +int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, + struct usb_host_endpoint *ep) { struct xhci_hcd *xhci; struct xhci_container_ctx *in_ctx; @@ -2020,15 +2022,6 @@ static int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, return -ENOMEM; } - if (xhci->quirks & XHCI_MTK_HOST) { - ret = xhci_mtk_add_ep_quirk(hcd, udev, ep); - if (ret < 0) { - xhci_ring_free(xhci, virt_dev->eps[ep_index].new_ring); - virt_dev->eps[ep_index].new_ring = NULL; - return ret; - } - } - ctrl_ctx->add_flags |= cpu_to_le32(added_ctxs); new_add_flags = le32_to_cpu(ctrl_ctx->add_flags); @@ -2053,6 +2046,7 @@ static int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, (unsigned int) new_add_flags); return 0; } +EXPORT_SYMBOL_GPL(xhci_add_endpoint); static void xhci_zero_in_ctx(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev) { @@ -3086,6 +3080,7 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) return ret; } +EXPORT_SYMBOL_GPL(xhci_check_bandwidth); void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) { @@ -3110,6 +3105,7 @@ void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) } xhci_zero_in_ctx(xhci, virt_dev); } +EXPORT_SYMBOL_GPL(xhci_reset_bandwidth); static void xhci_setup_input_ctx_for_config_ep(struct xhci_hcd *xhci, struct xhci_container_ctx *in_ctx, @@ -3269,6 +3265,14 @@ static void xhci_endpoint_reset(struct usb_hcd *hcd, /* config ep command clears toggle if add and drop ep flags are set */ ctrl_ctx = xhci_get_input_control_ctx(cfg_cmd->in_ctx); + if (!ctrl_ctx) { + spin_unlock_irqrestore(&xhci->lock, flags); + xhci_free_command(xhci, cfg_cmd); + xhci_warn(xhci, "%s: Could not get input context, bad type.\n", + __func__); + goto cleanup; + } + xhci_setup_input_ctx_for_config_ep(xhci, cfg_cmd->in_ctx, vdev->out_ctx, ctrl_ctx, ep_flag, ep_flag); xhci_endpoint_copy(xhci, cfg_cmd->in_ctx, vdev->out_ctx, ep_index); @@ -5234,10 +5238,12 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) hcd->self.root_hub->speed = USB_SPEED_SUPER_PLUS; hcd->self.root_hub->rx_lanes = 2; hcd->self.root_hub->tx_lanes = 2; + hcd->self.root_hub->ssp_rate = USB_SSP_GEN_2x2; break; case 1: hcd->speed = HCD_USB31; hcd->self.root_hub->speed = USB_SPEED_SUPER_PLUS; + hcd->self.root_hub->ssp_rate = USB_SSP_GEN_2x1; break; } xhci_info(xhci, "Host supports USB 3.%x %sSuperSpeed\n", @@ -5436,6 +5442,10 @@ void xhci_init_driver(struct hc_driver *drv, drv->reset = over->reset; if (over->start) drv->start = over->start; + if (over->add_endpoint) + drv->add_endpoint = over->add_endpoint; + if (over->drop_endpoint) + drv->drop_endpoint = over->drop_endpoint; if (over->check_bandwidth) drv->check_bandwidth = over->check_bandwidth; if (over->reset_bandwidth) diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index ca822ad3b65b..2595a8f057c4 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1929,6 +1929,10 @@ struct xhci_driver_overrides { size_t extra_priv_size; int (*reset)(struct usb_hcd *hcd); int (*start)(struct usb_hcd *hcd); + int (*add_endpoint)(struct usb_hcd *hcd, struct usb_device *udev, + struct usb_host_endpoint *ep); + int (*drop_endpoint)(struct usb_hcd *hcd, struct usb_device *udev, + struct usb_host_endpoint *ep); int (*check_bandwidth)(struct usb_hcd *, struct usb_device *); void (*reset_bandwidth)(struct usb_hcd *, struct usb_device *); }; @@ -2081,6 +2085,10 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks); void xhci_shutdown(struct usb_hcd *hcd); void xhci_init_driver(struct hc_driver *drv, const struct xhci_driver_overrides *over); +int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, + struct usb_host_endpoint *ep); +int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev, + struct usb_host_endpoint *ep); int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev); void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev); int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id); diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c index 45a387979935..6d15a097b007 100644 --- a/drivers/usb/misc/adutux.c +++ b/drivers/usb/misc/adutux.c @@ -183,10 +183,10 @@ static void adu_interrupt_in_callback(struct urb *urb) dev->interrupt_in_buffer, urb->actual_length); dev->read_buffer_length += urb->actual_length; - dev_dbg(&dev->udev->dev,"%s reading %d\n", __func__, + dev_dbg(&dev->udev->dev, "%s reading %d\n", __func__, urb->actual_length); } else { - dev_dbg(&dev->udev->dev,"%s : read_buffer overflow\n", + dev_dbg(&dev->udev->dev, "%s : read_buffer overflow\n", __func__); } } @@ -726,7 +726,7 @@ static int adu_probe(struct usb_interface *interface, retval = -EIO; goto error; } - dev_dbg(&interface->dev,"serial_number=%s", dev->serial_number); + dev_dbg(&interface->dev, "serial_number=%s", dev->serial_number); /* we can register the device now, as it is ready */ usb_set_intfdata(interface, dev); diff --git a/drivers/usb/misc/ehset.c b/drivers/usb/misc/ehset.c index 2752e1f4f4d0..f87890f9cd26 100644 --- a/drivers/usb/misc/ehset.c +++ b/drivers/usb/misc/ehset.c @@ -24,68 +24,57 @@ static int ehset_probe(struct usb_interface *intf, int ret = -EINVAL; struct usb_device *dev = interface_to_usbdev(intf); struct usb_device *hub_udev = dev->parent; - struct usb_device_descriptor *buf; + struct usb_device_descriptor buf; u8 portnum = dev->portnum; u16 test_pid = le16_to_cpu(dev->descriptor.idProduct); switch (test_pid) { case TEST_SE0_NAK_PID: - ret = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0), - USB_REQ_SET_FEATURE, USB_RT_PORT, - USB_PORT_FEAT_TEST, - (USB_TEST_SE0_NAK << 8) | portnum, - NULL, 0, 1000); + ret = usb_control_msg_send(hub_udev, 0, USB_REQ_SET_FEATURE, + USB_RT_PORT, USB_PORT_FEAT_TEST, + (USB_TEST_SE0_NAK << 8) | portnum, + NULL, 0, 1000, GFP_KERNEL); break; case TEST_J_PID: - ret = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0), - USB_REQ_SET_FEATURE, USB_RT_PORT, - USB_PORT_FEAT_TEST, - (USB_TEST_J << 8) | portnum, - NULL, 0, 1000); + ret = usb_control_msg_send(hub_udev, 0, USB_REQ_SET_FEATURE, + USB_RT_PORT, USB_PORT_FEAT_TEST, + (USB_TEST_J << 8) | portnum, NULL, 0, + 1000, GFP_KERNEL); break; case TEST_K_PID: - ret = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0), - USB_REQ_SET_FEATURE, USB_RT_PORT, - USB_PORT_FEAT_TEST, - (USB_TEST_K << 8) | portnum, - NULL, 0, 1000); + ret = usb_control_msg_send(hub_udev, 0, USB_REQ_SET_FEATURE, + USB_RT_PORT, USB_PORT_FEAT_TEST, + (USB_TEST_K << 8) | portnum, NULL, 0, + 1000, GFP_KERNEL); break; case TEST_PACKET_PID: - ret = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0), - USB_REQ_SET_FEATURE, USB_RT_PORT, - USB_PORT_FEAT_TEST, - (USB_TEST_PACKET << 8) | portnum, - NULL, 0, 1000); + ret = usb_control_msg_send(hub_udev, 0, USB_REQ_SET_FEATURE, + USB_RT_PORT, USB_PORT_FEAT_TEST, + (USB_TEST_PACKET << 8) | portnum, + NULL, 0, 1000, GFP_KERNEL); break; case TEST_HS_HOST_PORT_SUSPEND_RESUME: /* Test: wait for 15secs -> suspend -> 15secs delay -> resume */ msleep(15 * 1000); - ret = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0), - USB_REQ_SET_FEATURE, USB_RT_PORT, - USB_PORT_FEAT_SUSPEND, portnum, - NULL, 0, 1000); + ret = usb_control_msg_send(hub_udev, 0, USB_REQ_SET_FEATURE, + USB_RT_PORT, USB_PORT_FEAT_SUSPEND, + portnum, NULL, 0, 1000, GFP_KERNEL); if (ret < 0) break; msleep(15 * 1000); - ret = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0), - USB_REQ_CLEAR_FEATURE, USB_RT_PORT, - USB_PORT_FEAT_SUSPEND, portnum, - NULL, 0, 1000); + ret = usb_control_msg_send(hub_udev, 0, USB_REQ_CLEAR_FEATURE, + USB_RT_PORT, USB_PORT_FEAT_SUSPEND, + portnum, NULL, 0, 1000, GFP_KERNEL); break; case TEST_SINGLE_STEP_GET_DEV_DESC: /* Test: wait for 15secs -> GetDescriptor request */ msleep(15 * 1000); - buf = kmalloc(USB_DT_DEVICE_SIZE, GFP_KERNEL); - if (!buf) - return -ENOMEM; - ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, - USB_DT_DEVICE << 8, 0, - buf, USB_DT_DEVICE_SIZE, - USB_CTRL_GET_TIMEOUT); - kfree(buf); + ret = usb_control_msg_recv(dev, 0, USB_REQ_GET_DESCRIPTOR, + USB_DIR_IN, USB_DT_DEVICE << 8, 0, + &buf, USB_DT_DEVICE_SIZE, + USB_CTRL_GET_TIMEOUT, GFP_KERNEL); break; case TEST_SINGLE_STEP_SET_FEATURE: /* @@ -100,11 +89,10 @@ static int ehset_probe(struct usb_interface *intf, break; } - ret = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0), - USB_REQ_SET_FEATURE, USB_RT_PORT, - USB_PORT_FEAT_TEST, - (6 << 8) | portnum, - NULL, 0, 60 * 1000); + ret = usb_control_msg_send(hub_udev, 0, USB_REQ_SET_FEATURE, + USB_RT_PORT, USB_PORT_FEAT_TEST, + (6 << 8) | portnum, NULL, 0, + 60 * 1000, GFP_KERNEL); break; default: @@ -112,7 +100,7 @@ static int ehset_probe(struct usb_interface *intf, __func__, test_pid); } - return (ret < 0) ? ret : 0; + return ret; } static void ehset_disconnect(struct usb_interface *intf) diff --git a/drivers/usb/misc/ezusb.c b/drivers/usb/misc/ezusb.c index f058d8029761..78aaee56c2b7 100644 --- a/drivers/usb/misc/ezusb.c +++ b/drivers/usb/misc/ezusb.c @@ -31,24 +31,12 @@ static const struct ezusb_fx_type ezusb_fx1 = { static int ezusb_writememory(struct usb_device *dev, int address, unsigned char *data, int length, __u8 request) { - int result; - unsigned char *transfer_buffer; - if (!dev) return -ENODEV; - transfer_buffer = kmemdup(data, length, GFP_KERNEL); - if (!transfer_buffer) { - dev_err(&dev->dev, "%s - kmalloc(%d) failed.\n", - __func__, length); - return -ENOMEM; - } - result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request, + return usb_control_msg_send(dev, 0, request, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - address, 0, transfer_buffer, length, 3000); - - kfree(transfer_buffer); - return result; + address, 0, data, length, 3000, GFP_KERNEL); } static int ezusb_set_reset(struct usb_device *dev, unsigned short cpucs_reg, diff --git a/drivers/usb/misc/usbsevseg.c b/drivers/usb/misc/usbsevseg.c index 551074f5b7ad..4bc816bb09bb 100644 --- a/drivers/usb/misc/usbsevseg.c +++ b/drivers/usb/misc/usbsevseg.c @@ -74,15 +74,10 @@ static void update_display_powered(struct usb_sevsegdev *mydev) if (mydev->shadow_power != 1) return; - rc = usb_control_msg(mydev->udev, - usb_sndctrlpipe(mydev->udev, 0), - 0x12, - 0x48, - (80 * 0x100) + 10, /* (power mode) */ - (0x00 * 0x100) + (mydev->powered ? 1 : 0), - NULL, - 0, - 2000); + rc = usb_control_msg_send(mydev->udev, 0, 0x12, 0x48, + (80 * 0x100) + 10, /* (power mode) */ + (0x00 * 0x100) + (mydev->powered ? 1 : 0), + NULL, 0, 2000, GFP_KERNEL); if (rc < 0) dev_dbg(&mydev->udev->dev, "power retval = %d\n", rc); @@ -99,15 +94,10 @@ static void update_display_mode(struct usb_sevsegdev *mydev) if(mydev->shadow_power != 1) return; - rc = usb_control_msg(mydev->udev, - usb_sndctrlpipe(mydev->udev, 0), - 0x12, - 0x48, - (82 * 0x100) + 10, /* (set mode) */ - (mydev->mode_msb * 0x100) + mydev->mode_lsb, - NULL, - 0, - 2000); + rc = usb_control_msg_send(mydev->udev, 0, 0x12, 0x48, + (82 * 0x100) + 10, /* (set mode) */ + (mydev->mode_msb * 0x100) + mydev->mode_lsb, + NULL, 0, 2000, GFP_NOIO); if (rc < 0) dev_dbg(&mydev->udev->dev, "mode retval = %d\n", rc); @@ -117,48 +107,32 @@ static void update_display_visual(struct usb_sevsegdev *mydev, gfp_t mf) { int rc; int i; - unsigned char *buffer; + unsigned char buffer[MAXLEN] = {0}; u8 decimals = 0; if(mydev->shadow_power != 1) return; - buffer = kzalloc(MAXLEN, mf); - if (!buffer) - return; - /* The device is right to left, where as you write left to right */ for (i = 0; i < mydev->textlength; i++) buffer[i] = mydev->text[mydev->textlength-1-i]; - rc = usb_control_msg(mydev->udev, - usb_sndctrlpipe(mydev->udev, 0), - 0x12, - 0x48, - (85 * 0x100) + 10, /* (write text) */ - (0 * 0x100) + mydev->textmode, /* mode */ - buffer, - mydev->textlength, - 2000); + rc = usb_control_msg_send(mydev->udev, 0, 0x12, 0x48, + (85 * 0x100) + 10, /* (write text) */ + (0 * 0x100) + mydev->textmode, /* mode */ + &buffer, mydev->textlength, 2000, mf); if (rc < 0) dev_dbg(&mydev->udev->dev, "write retval = %d\n", rc); - kfree(buffer); - /* The device is right to left, where as you write left to right */ for (i = 0; i < sizeof(mydev->decimals); i++) decimals |= mydev->decimals[i] << i; - rc = usb_control_msg(mydev->udev, - usb_sndctrlpipe(mydev->udev, 0), - 0x12, - 0x48, - (86 * 0x100) + 10, /* (set decimal) */ - (0 * 0x100) + decimals, /* decimals */ - NULL, - 0, - 2000); + rc = usb_control_msg_send(mydev->udev, 0, 0x12, 0x48, + (86 * 0x100) + 10, /* (set decimal) */ + (0 * 0x100) + decimals, /* decimals */ + NULL, 0, 2000, mf); if (rc < 0) dev_dbg(&mydev->udev->dev, "decimal retval = %d\n", rc); diff --git a/drivers/usb/mtu3/mtu3_host.c b/drivers/usb/mtu3/mtu3_host.c index c871b94f3e6f..0a8cd446cf1b 100644 --- a/drivers/usb/mtu3/mtu3_host.c +++ b/drivers/usb/mtu3/mtu3_host.c @@ -24,6 +24,16 @@ #define WC1_IS_EN BIT(25) #define WC1_IS_P BIT(6) /* polarity for ip sleep */ +/* mt8183 */ +#define PERI_WK_CTRL0 0x0 +#define WC0_IS_C(x) ((u32)(((x) & 0xf) << 28)) /* cycle debounce */ +#define WC0_IS_P BIT(12) /* polarity */ +#define WC0_IS_EN BIT(6) + +/* mt8192 */ +#define WC0_SSUSB0_CDEN BIT(6) +#define WC0_IS_SPM_EN BIT(1) + /* mt2712 etc */ #define PERI_SSUSB_SPM_CTRL 0x0 #define SSC_IP_SLEEP_EN BIT(4) @@ -32,6 +42,8 @@ enum ssusb_uwk_vers { SSUSB_UWK_V1 = 1, SSUSB_UWK_V2, + SSUSB_UWK_V1_1 = 101, /* specific revision 1.01 */ + SSUSB_UWK_V1_2, /* specific revision 1.02 */ }; /* @@ -48,6 +60,16 @@ static void ssusb_wakeup_ip_sleep_set(struct ssusb_mtk *ssusb, bool enable) msk = WC1_IS_EN | WC1_IS_C(0xf) | WC1_IS_P; val = enable ? (WC1_IS_EN | WC1_IS_C(0x8)) : 0; break; + case SSUSB_UWK_V1_1: + reg = ssusb->uwk_reg_base + PERI_WK_CTRL0; + msk = WC0_IS_EN | WC0_IS_C(0xf) | WC0_IS_P; + val = enable ? (WC0_IS_EN | WC0_IS_C(0x8)) : 0; + break; + case SSUSB_UWK_V1_2: + reg = ssusb->uwk_reg_base + PERI_WK_CTRL0; + msk = WC0_SSUSB0_CDEN | WC0_IS_SPM_EN; + val = enable ? msk : 0; + break; case SSUSB_UWK_V2: reg = ssusb->uwk_reg_base + PERI_SSUSB_SPM_CTRL; msk = SSC_IP_SLEEP_EN | SSC_SPM_INT_EN; @@ -109,7 +131,7 @@ int ssusb_host_enable(struct ssusb_mtk *ssusb) void __iomem *ibase = ssusb->ippc_base; int num_u3p = ssusb->u3_ports; int num_u2p = ssusb->u2_ports; - int u3_ports_disabed; + int u3_ports_disabled; u32 check_clk; u32 value; int i; @@ -118,10 +140,10 @@ int ssusb_host_enable(struct ssusb_mtk *ssusb) mtu3_clrbits(ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN); /* power on and enable u3 ports except skipped ones */ - u3_ports_disabed = 0; + u3_ports_disabled = 0; for (i = 0; i < num_u3p; i++) { if ((0x1 << i) & ssusb->u3p_dis_msk) { - u3_ports_disabed++; + u3_ports_disabled++; continue; } @@ -140,7 +162,7 @@ int ssusb_host_enable(struct ssusb_mtk *ssusb) } check_clk = SSUSB_XHCI_RST_B_STS; - if (num_u3p > u3_ports_disabed) + if (num_u3p > u3_ports_disabled) check_clk = SSUSB_U3_MAC_RST_B_STS; return ssusb_check_clocks(ssusb, check_clk); diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c index d44d5417438d..7786a95a874e 100644 --- a/drivers/usb/mtu3/mtu3_plat.c +++ b/drivers/usb/mtu3/mtu3_plat.c @@ -502,25 +502,20 @@ static const struct dev_pm_ops mtu3_pm_ops = { #define DEV_PM_OPS (IS_ENABLED(CONFIG_PM) ? &mtu3_pm_ops : NULL) -#ifdef CONFIG_OF - static const struct of_device_id mtu3_of_match[] = { {.compatible = "mediatek,mt8173-mtu3",}, {.compatible = "mediatek,mtu3",}, {}, }; - MODULE_DEVICE_TABLE(of, mtu3_of_match); -#endif - static struct platform_driver mtu3_driver = { .probe = mtu3_probe, .remove = mtu3_remove, .driver = { .name = MTU3_DRIVER_NAME, .pm = DEV_PM_OPS, - .of_match_table = of_match_ptr(mtu3_of_match), + .of_match_table = mtu3_of_match, }, }; module_platform_driver(mtu3_driver); diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index fc0457db62e1..8f09a387b773 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -2070,7 +2070,7 @@ static void musb_irq_work(struct work_struct *data) struct musb *musb = container_of(data, struct musb, irq_work.work); int error; - error = pm_runtime_get_sync(musb->controller); + error = pm_runtime_resume_and_get(musb->controller); if (error < 0) { dev_err(musb->controller, "Could not enable: %i\n", error); diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index dbe5623db1e0..a8a65effe68b 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -375,11 +375,11 @@ struct musb { unsigned dyn_fifo:1; /* dynamic FIFO supported? */ unsigned bulk_split:1; -#define can_bulk_split(musb,type) \ +#define can_bulk_split(musb, type) \ (((type) == USB_ENDPOINT_XFER_BULK) && (musb)->bulk_split) unsigned bulk_combine:1; -#define can_bulk_combine(musb,type) \ +#define can_bulk_combine(musb, type) \ (((type) == USB_ENDPOINT_XFER_BULK) && (musb)->bulk_combine) /* is_suspended means USB B_PERIPHERAL suspend */ diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index e6fa13701808..b5e7991dc7d9 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -160,8 +160,10 @@ static int usbhsf_pkt_handler(struct usbhs_pipe *pipe, int type) usbhs_lock(priv, flags); pkt = __usbhsf_pkt_get(pipe); - if (!pkt) + if (!pkt) { + ret = -EINVAL; goto __usbhs_pkt_handler_end; + } switch (type) { case USBHSF_PKT_PREPARE: diff --git a/drivers/usb/roles/class.c b/drivers/usb/roles/class.c index 97f37077b7f9..33b637d0d8d9 100644 --- a/drivers/usb/roles/class.c +++ b/drivers/usb/roles/class.c @@ -189,6 +189,8 @@ usb_role_switch_find_by_fwnode(const struct fwnode_handle *fwnode) return NULL; dev = class_find_device_by_fwnode(role_class, fwnode); + if (dev) + WARN_ON(!try_module_get(dev->parent->driver->owner)); return dev ? to_role_switch(dev) : NULL; } diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index b9bedfe9bd09..5dd710e9fe7d 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -385,18 +385,6 @@ static int ark3116_open(struct tty_struct *tty, struct usb_serial_port *port) return result; } -static int ark3116_get_serial_info(struct tty_struct *tty, - struct serial_struct *ss) -{ - struct usb_serial_port *port = tty->driver_data; - - ss->type = PORT_16654; - ss->line = port->minor; - ss->port = port->port_number; - ss->baud_base = 460800; - return 0; -} - static int ark3116_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; @@ -633,7 +621,6 @@ static struct usb_serial_driver ark3116_device = { .port_probe = ark3116_port_probe, .port_remove = ark3116_port_remove, .set_termios = ark3116_set_termios, - .get_serial = ark3116_get_serial_info, .tiocmget = ark3116_tiocmget, .tiocmset = ark3116_tiocmset, .tiocmiwait = usb_serial_generic_tiocmiwait, diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index a373cd63b3a4..ee595d1bea0a 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -1410,17 +1410,6 @@ static void cp210x_break_ctl(struct tty_struct *tty, int break_state) } #ifdef CONFIG_GPIOLIB -static int cp210x_gpio_request(struct gpio_chip *gc, unsigned int offset) -{ - struct usb_serial *serial = gpiochip_get_data(gc); - struct cp210x_serial_private *priv = usb_get_serial_data(serial); - - if (priv->gpio_altfunc & BIT(offset)) - return -ENODEV; - - return 0; -} - static int cp210x_gpio_get(struct gpio_chip *gc, unsigned int gpio) { struct usb_serial *serial = gpiochip_get_data(gc); @@ -1549,6 +1538,24 @@ static int cp210x_gpio_set_config(struct gpio_chip *gc, unsigned int gpio, return -ENOTSUPP; } +static int cp210x_gpio_init_valid_mask(struct gpio_chip *gc, + unsigned long *valid_mask, unsigned int ngpios) +{ + struct usb_serial *serial = gpiochip_get_data(gc); + struct cp210x_serial_private *priv = usb_get_serial_data(serial); + struct device *dev = &serial->interface->dev; + unsigned long altfunc_mask = priv->gpio_altfunc; + + bitmap_complement(valid_mask, &altfunc_mask, ngpios); + + if (bitmap_empty(valid_mask, ngpios)) + dev_dbg(dev, "no pin configured for GPIO\n"); + else + dev_dbg(dev, "GPIO.%*pbl configured for GPIO\n", ngpios, + valid_mask); + return 0; +} + /* * This function is for configuring GPIO using shared pins, where other signals * are made unavailable by configuring the use of GPIO. This is believed to be @@ -1786,13 +1793,13 @@ static int cp210x_gpio_init(struct usb_serial *serial) return result; priv->gc.label = "cp210x"; - priv->gc.request = cp210x_gpio_request; priv->gc.get_direction = cp210x_gpio_direction_get; priv->gc.direction_input = cp210x_gpio_direction_input; priv->gc.direction_output = cp210x_gpio_direction_output; priv->gc.get = cp210x_gpio_get; priv->gc.set = cp210x_gpio_set; priv->gc.set_config = cp210x_gpio_set_config; + priv->gc.init_valid_mask = cp210x_gpio_init_valid_mask; priv->gc.owner = THIS_MODULE; priv->gc.parent = &serial->interface->dev; priv->gc.base = -1; diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index 6a8f39147d8e..a7a7af8d05bf 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -820,17 +820,12 @@ static int f81232_carrier_raised(struct usb_serial_port *port) return 0; } -static int f81232_get_serial_info(struct tty_struct *tty, - struct serial_struct *ss) +static void f81232_get_serial(struct tty_struct *tty, struct serial_struct *ss) { struct usb_serial_port *port = tty->driver_data; struct f81232_private *priv = usb_get_serial_port_data(port); - ss->type = PORT_16550A; - ss->line = port->minor; - ss->port = port->port_number; ss->baud_base = priv->baud_base; - return 0; } static void f81232_interrupt_work(struct work_struct *work) @@ -953,7 +948,6 @@ static int f81232_port_probe(struct usb_serial_port *port) usb_set_serial_port_data(port, priv); - port->port.drain_delay = 256; priv->port = port; return 0; @@ -1021,7 +1015,7 @@ static struct usb_serial_driver f81232_device = { .close = f81232_close, .dtr_rts = f81232_dtr_rts, .carrier_raised = f81232_carrier_raised, - .get_serial = f81232_get_serial_info, + .get_serial = f81232_get_serial, .break_ctl = f81232_break_ctl, .set_termios = f81232_set_termios, .tiocmget = f81232_tiocmget, @@ -1046,7 +1040,7 @@ static struct usb_serial_driver f81534a_device = { .close = f81232_close, .dtr_rts = f81232_dtr_rts, .carrier_raised = f81232_carrier_raised, - .get_serial = f81232_get_serial_info, + .get_serial = f81232_get_serial, .break_ctl = f81232_break_ctl, .set_termios = f81232_set_termios, .tiocmget = f81232_tiocmget, diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index a763b362f081..c0bca52ef92a 100644 --- a/drivers/usb/serial/f81534.c +++ b/drivers/usb/serial/f81534.c @@ -1140,19 +1140,14 @@ static void f81534_close(struct usb_serial_port *port) mutex_unlock(&serial_priv->urb_mutex); } -static int f81534_get_serial_info(struct tty_struct *tty, - struct serial_struct *ss) +static void f81534_get_serial_info(struct tty_struct *tty, struct serial_struct *ss) { struct usb_serial_port *port = tty->driver_data; struct f81534_port_private *port_priv; port_priv = usb_get_serial_port_data(port); - ss->type = PORT_16550A; - ss->port = port->port_number; - ss->line = port->minor; ss->baud_base = port_priv->baud_base; - return 0; } static void f81534_process_per_serial_block(struct usb_serial_port *port, diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index c867592477c9..6f2659e59b2e 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1082,8 +1082,7 @@ static int ftdi_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); static int ftdi_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg); -static int get_serial_info(struct tty_struct *tty, - struct serial_struct *ss); +static void get_serial_info(struct tty_struct *tty, struct serial_struct *ss); static int set_serial_info(struct tty_struct *tty, struct serial_struct *ss); static void ftdi_break_ctl(struct tty_struct *tty, int break_state); @@ -1477,8 +1476,7 @@ static int read_latency_timer(struct usb_serial_port *port) return 0; } -static int get_serial_info(struct tty_struct *tty, - struct serial_struct *ss) +static void get_serial_info(struct tty_struct *tty, struct serial_struct *ss) { struct usb_serial_port *port = tty->driver_data; struct ftdi_private *priv = usb_get_serial_port_data(port); @@ -1486,49 +1484,34 @@ static int get_serial_info(struct tty_struct *tty, ss->flags = priv->flags; ss->baud_base = priv->baud_base; ss->custom_divisor = priv->custom_divisor; - return 0; } -static int set_serial_info(struct tty_struct *tty, - struct serial_struct *ss) +static int set_serial_info(struct tty_struct *tty, struct serial_struct *ss) { struct usb_serial_port *port = tty->driver_data; struct ftdi_private *priv = usb_get_serial_port_data(port); - struct ftdi_private old_priv; + int old_flags, old_divisor; mutex_lock(&priv->cfg_lock); - old_priv = *priv; - - /* Do error checking and permission checking */ if (!capable(CAP_SYS_ADMIN)) { if ((ss->flags ^ priv->flags) & ~ASYNC_USR_MASK) { mutex_unlock(&priv->cfg_lock); return -EPERM; } - priv->flags = ((priv->flags & ~ASYNC_USR_MASK) | - (ss->flags & ASYNC_USR_MASK)); - priv->custom_divisor = ss->custom_divisor; - goto check_and_exit; } - if (ss->baud_base != priv->baud_base) { - mutex_unlock(&priv->cfg_lock); - return -EINVAL; - } + old_flags = priv->flags; + old_divisor = priv->custom_divisor; - /* Make the changes - these are privileged changes! */ - - priv->flags = ((priv->flags & ~ASYNC_FLAGS) | - (ss->flags & ASYNC_FLAGS)); + priv->flags = ss->flags & ASYNC_FLAGS; priv->custom_divisor = ss->custom_divisor; -check_and_exit: write_latency_timer(port); - if ((priv->flags ^ old_priv.flags) & ASYNC_SPD_MASK || + if ((priv->flags ^ old_flags) & ASYNC_SPD_MASK || ((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST && - priv->custom_divisor != old_priv.custom_divisor)) { + priv->custom_divisor != old_divisor)) { /* warn about deprecation unless clearing */ if (priv->flags & ASYNC_SPD_MASK) diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 68401adcffde..e6fe3882bf69 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -263,39 +263,9 @@ static const struct divisor_table_entry divisor_table[] = { static atomic_t CmdUrbs = ATOMIC_INIT(0); -/* local function prototypes */ +/* function prototypes */ -/* function prototypes for all URB callbacks */ -static void edge_interrupt_callback(struct urb *urb); -static void edge_bulk_in_callback(struct urb *urb); -static void edge_bulk_out_data_callback(struct urb *urb); -static void edge_bulk_out_cmd_callback(struct urb *urb); - -/* function prototypes for the usbserial callbacks */ -static int edge_open(struct tty_struct *tty, struct usb_serial_port *port); static void edge_close(struct usb_serial_port *port); -static int edge_write(struct tty_struct *tty, struct usb_serial_port *port, - const unsigned char *buf, int count); -static int edge_write_room(struct tty_struct *tty); -static int edge_chars_in_buffer(struct tty_struct *tty); -static void edge_throttle(struct tty_struct *tty); -static void edge_unthrottle(struct tty_struct *tty); -static void edge_set_termios(struct tty_struct *tty, - struct usb_serial_port *port, - struct ktermios *old_termios); -static int edge_ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg); -static void edge_break(struct tty_struct *tty, int break_state); -static int edge_tiocmget(struct tty_struct *tty); -static int edge_tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear); -static int edge_startup(struct usb_serial *serial); -static void edge_disconnect(struct usb_serial *serial); -static void edge_release(struct usb_serial *serial); -static int edge_port_probe(struct usb_serial_port *port); -static void edge_port_remove(struct usb_serial_port *port); - -/* function prototypes for all of our local functions */ static void process_rcvd_data(struct edgeport_serial *edge_serial, unsigned char *buffer, __u16 bufferLength); @@ -309,8 +279,6 @@ static void handle_new_lsr(struct edgeport_port *edge_port, __u8 lsrData, static int send_iosp_ext_cmd(struct edgeport_port *edge_port, __u8 command, __u8 param); static int calc_baud_rate_divisor(struct device *dev, int baud_rate, int *divisor); -static int send_cmd_write_baud_rate(struct edgeport_port *edge_port, - int baudRate); static void change_port_settings(struct tty_struct *tty, struct edgeport_port *edge_port, struct ktermios *old_termios); @@ -321,19 +289,8 @@ static int write_cmd_usb(struct edgeport_port *edge_port, static void send_more_port_data(struct edgeport_serial *edge_serial, struct edgeport_port *edge_port); -static int sram_write(struct usb_serial *serial, __u16 extAddr, __u16 addr, - __u16 length, const __u8 *data); -static int rom_read(struct usb_serial *serial, __u16 extAddr, __u16 addr, - __u16 length, __u8 *data); static int rom_write(struct usb_serial *serial, __u16 extAddr, __u16 addr, __u16 length, const __u8 *data); -static void get_manufacturing_desc(struct edgeport_serial *edge_serial); -static void get_boot_desc(struct edgeport_serial *edge_serial); -static void load_application_firmware(struct edgeport_serial *edge_serial); - -static void unicode_to_ascii(char *string, int buflen, - __le16 *unicode, int unicode_size); - /* ************************************************************************ */ /* ************************************************************************ */ @@ -1637,24 +1594,6 @@ static int edge_tiocmget(struct tty_struct *tty) return result; } -static int get_serial_info(struct tty_struct *tty, - struct serial_struct *ss) -{ - struct usb_serial_port *port = tty->driver_data; - struct edgeport_port *edge_port = usb_get_serial_port_data(port); - - ss->type = PORT_16550A; - ss->line = edge_port->port->minor; - ss->port = edge_port->port->port_number; - ss->irq = 0; - ss->xmit_fifo_size = edge_port->maxTxCredits; - ss->baud_base = 9600; - ss->close_delay = 5*HZ; - ss->closing_wait = 30*HZ; - return 0; -} - - /***************************************************************************** * SerialIoctl * this function handles any ioctl calls to the driver @@ -3116,7 +3055,6 @@ static struct usb_serial_driver edgeport_2port_device = { .set_termios = edge_set_termios, .tiocmget = edge_tiocmget, .tiocmset = edge_tiocmset, - .get_serial = get_serial_info, .tiocmiwait = usb_serial_generic_tiocmiwait, .get_icount = usb_serial_generic_get_icount, .write = edge_write, @@ -3152,7 +3090,6 @@ static struct usb_serial_driver edgeport_4port_device = { .set_termios = edge_set_termios, .tiocmget = edge_tiocmget, .tiocmset = edge_tiocmset, - .get_serial = get_serial_info, .tiocmiwait = usb_serial_generic_tiocmiwait, .get_icount = usb_serial_generic_get_icount, .write = edge_write, @@ -3188,7 +3125,6 @@ static struct usb_serial_driver edgeport_8port_device = { .set_termios = edge_set_termios, .tiocmget = edge_tiocmget, .tiocmset = edge_tiocmset, - .get_serial = get_serial_info, .tiocmiwait = usb_serial_generic_tiocmiwait, .get_icount = usb_serial_generic_get_icount, .write = edge_write, @@ -3224,7 +3160,6 @@ static struct usb_serial_driver epic_device = { .set_termios = edge_set_termios, .tiocmget = edge_tiocmget, .tiocmset = edge_tiocmset, - .get_serial = get_serial_info, .tiocmiwait = usb_serial_generic_tiocmiwait, .get_icount = usb_serial_generic_get_icount, .write = edge_write, diff --git a/drivers/usb/serial/io_edgeport.h b/drivers/usb/serial/io_edgeport.h index 43ba53a3a6fa..7c9f62af5ed6 100644 --- a/drivers/usb/serial/io_edgeport.h +++ b/drivers/usb/serial/io_edgeport.h @@ -10,7 +10,6 @@ #if !defined(_IO_EDGEPORT_H_) #define _IO_EDGEPORT_H_ - #define MAX_RS232_PORTS 8 /* Max # of RS-232 ports per device */ /* typedefs that the insideout headers need */ @@ -21,57 +20,8 @@ #define HIGH8(a) ((unsigned char)((a & 0xff00) >> 8)) #endif -#ifndef __KERNEL__ -#define __KERNEL__ -#endif - #include "io_usbvend.h" - - -/* The following table is used to map the USBx port number to - * the device serial number (or physical USB path), */ -#define MAX_EDGEPORTS 64 - -struct comMapper { - char SerialNumber[MAX_SERIALNUMBER_LEN+1]; /* Serial number/usb path */ - int numPorts; /* Number of ports */ - int Original[MAX_RS232_PORTS]; /* Port numbers set by IOCTL */ - int Port[MAX_RS232_PORTS]; /* Actual used port numbers */ -}; - - -#define EDGEPORT_CONFIG_DEVICE "/proc/edgeport" - -/* /proc/edgeport Interface - * This interface uses read/write/lseek interface to talk to the edgeport driver - * the following read functions are supported: */ -#define PROC_GET_MAPPING_TO_PATH 1 -#define PROC_GET_COM_ENTRY 2 -#define PROC_GET_EDGE_MANUF_DESCRIPTOR 3 -#define PROC_GET_BOOT_DESCRIPTOR 4 -#define PROC_GET_PRODUCT_INFO 5 -#define PROC_GET_STRINGS 6 -#define PROC_GET_CURRENT_COM_MAPPING 7 - -/* The parameters to the lseek() for the read is: */ -#define PROC_READ_SETUP(Command, Argument) ((Command) + ((Argument)<<8)) - - -/* the following write functions are supported: */ -#define PROC_SET_COM_MAPPING 1 -#define PROC_SET_COM_ENTRY 2 - - -/* The following structure is passed to the write */ -struct procWrite { - int Command; - union { - struct comMapper Entry; - int ComMappingBasedOnUSBPort; /* Boolean value */ - } u; -}; - /* * Product information read from the Edgeport */ @@ -108,22 +58,4 @@ struct edgeport_product_info { struct edge_compatibility_bits Epic; }; -/* - * Edgeport Stringblock String locations - */ -#define EDGESTRING_MANUFNAME 1 /* Manufacture Name */ -#define EDGESTRING_PRODNAME 2 /* Product Name */ -#define EDGESTRING_SERIALNUM 3 /* Serial Number */ -#define EDGESTRING_ASSEMNUM 4 /* Assembly Number */ -#define EDGESTRING_OEMASSEMNUM 5 /* OEM Assembly Number */ -#define EDGESTRING_MANUFDATE 6 /* Manufacture Date */ -#define EDGESTRING_ORIGSERIALNUM 7 /* Serial Number */ - -struct string_block { - __u16 NumStrings; /* Number of strings in block */ - __u16 Strings[1]; /* Start of string block */ -}; - - - #endif diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index e800547be9e0..39503fdccebf 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -60,14 +60,12 @@ #define EDGE_READ_URB_STOPPING 1 #define EDGE_READ_URB_STOPPED 2 -#define EDGE_CLOSING_WAIT 4000 /* in .01 sec */ - /* Product information read from the Edgeport */ struct product_info { int TiMode; /* Current TI Mode */ - __u8 hardware_type; /* Type of hardware */ -} __attribute__((packed)); + u8 hardware_type; /* Type of hardware */ +} __packed; /* * Edgeport firmware header @@ -89,13 +87,13 @@ struct edgeport_fw_hdr { } __packed; struct edgeport_port { - __u16 uart_base; - __u16 dma_address; - __u8 shadow_msr; - __u8 shadow_mcr; - __u8 shadow_lsr; - __u8 lsr_mask; - __u32 ump_read_timeout; /* + u16 uart_base; + u16 dma_address; + u8 shadow_msr; + u8 shadow_mcr; + u8 shadow_lsr; + u8 lsr_mask; + u32 ump_read_timeout; /* * Number of milliseconds the UMP will * wait without data before completing * a read short @@ -106,7 +104,7 @@ struct edgeport_port { struct edgeport_serial *edge_serial; struct usb_serial_port *port; - __u8 bUartMode; /* Port type, 0: RS232, etc. */ + u8 bUartMode; /* Port type, 0: RS232, etc. */ spinlock_t ep_lock; int ep_read_urb_state; int ep_write_urb_in_use; @@ -211,7 +209,6 @@ static const struct usb_device_id id_table_combined[] = { MODULE_DEVICE_TABLE(usb, id_table_combined); -static int closing_wait = EDGE_CLOSING_WAIT; static bool ignore_cpu_rev; static int default_uart_mode; /* RS232 */ @@ -255,8 +252,8 @@ static int edge_remove_sysfs_attrs(struct usb_serial_port *port); #define TI_VSEND_TIMEOUT_DEFAULT 1000 #define TI_VSEND_TIMEOUT_FW_DOWNLOAD 10000 -static int ti_vread_sync(struct usb_device *dev, __u8 request, - __u16 value, __u16 index, u8 *data, int size) +static int ti_vread_sync(struct usb_device *dev, u8 request, u16 value, + u16 index, void *data, int size) { int status; @@ -274,7 +271,7 @@ static int ti_vread_sync(struct usb_device *dev, __u8 request, } static int ti_vsend_sync(struct usb_device *dev, u8 request, u16 value, - u16 index, u8 *data, int size, int timeout) + u16 index, void *data, int size, int timeout) { int status; @@ -287,27 +284,30 @@ static int ti_vsend_sync(struct usb_device *dev, u8 request, u16 value, return 0; } -static int send_cmd(struct usb_device *dev, __u8 command, - __u8 moduleid, __u16 value, u8 *data, - int size) +static int read_port_cmd(struct usb_serial_port *port, u8 command, u16 value, + void *data, int size) { - return ti_vsend_sync(dev, command, value, moduleid, data, size, - TI_VSEND_TIMEOUT_DEFAULT); + return ti_vread_sync(port->serial->dev, command, value, + UMPM_UART1_PORT + port->port_number, + data, size); +} + +static int send_port_cmd(struct usb_serial_port *port, u8 command, u16 value, + void *data, int size) +{ + return ti_vsend_sync(port->serial->dev, command, value, + UMPM_UART1_PORT + port->port_number, + data, size, TI_VSEND_TIMEOUT_DEFAULT); } /* clear tx/rx buffers and fifo in TI UMP */ -static int purge_port(struct usb_serial_port *port, __u16 mask) +static int purge_port(struct usb_serial_port *port, u16 mask) { int port_number = port->port_number; dev_dbg(&port->dev, "%s - port %d, mask %x\n", __func__, port_number, mask); - return send_cmd(port->serial->dev, - UMPC_PURGE_PORT, - (__u8)(UMPM_UART1_PORT + port_number), - mask, - NULL, - 0); + return send_port_cmd(port, UMPC_PURGE_PORT, mask, NULL, 0); } /** @@ -319,10 +319,10 @@ static int purge_port(struct usb_serial_port *port, __u16 mask) * @buffer: pointer to input data buffer */ static int read_download_mem(struct usb_device *dev, int start_address, - int length, __u8 address_type, __u8 *buffer) + int length, u8 address_type, u8 *buffer) { int status = 0; - __u8 read_length; + u8 read_length; u16 be_start_address; dev_dbg(&dev->dev, "%s - @ %x for %d\n", __func__, start_address, length); @@ -335,7 +335,7 @@ static int read_download_mem(struct usb_device *dev, int start_address, if (length > 64) read_length = 64; else - read_length = (__u8)length; + read_length = (u8)length; if (read_length > 1) { dev_dbg(&dev->dev, "%s - @ %x for %d\n", __func__, start_address, read_length); @@ -346,7 +346,7 @@ static int read_download_mem(struct usb_device *dev, int start_address, */ be_start_address = swab16((u16)start_address); status = ti_vread_sync(dev, UMPC_MEMORY_READ, - (__u16)address_type, + (u16)address_type, be_start_address, buffer, read_length); @@ -368,7 +368,7 @@ static int read_download_mem(struct usb_device *dev, int start_address, } static int read_ram(struct usb_device *dev, int start_address, - int length, __u8 *buffer) + int length, u8 *buffer) { return read_download_mem(dev, start_address, length, DTK_ADDR_SPACE_XDATA, buffer); @@ -376,7 +376,7 @@ static int read_ram(struct usb_device *dev, int start_address, /* Read edgeport memory to a given block */ static int read_boot_mem(struct edgeport_serial *serial, - int start_address, int length, __u8 *buffer) + int start_address, int length, u8 *buffer) { int status = 0; int i; @@ -384,7 +384,7 @@ static int read_boot_mem(struct edgeport_serial *serial, for (i = 0; i < length; i++) { status = ti_vread_sync(serial->serial->dev, UMPC_MEMORY_READ, serial->TI_I2C_Type, - (__u16)(start_address+i), &buffer[i], 0x01); + (u16)(start_address+i), &buffer[i], 0x01); if (status) { dev_dbg(&serial->serial->dev->dev, "%s - ERROR %x\n", __func__, status); return status; @@ -402,7 +402,7 @@ static int read_boot_mem(struct edgeport_serial *serial, /* Write given block to TI EPROM memory */ static int write_boot_mem(struct edgeport_serial *serial, - int start_address, int length, __u8 *buffer) + int start_address, int length, u8 *buffer) { int status = 0; int i; @@ -436,7 +436,7 @@ static int write_boot_mem(struct edgeport_serial *serial, /* Write edgeport I2C memory to TI chip */ static int write_i2c_mem(struct edgeport_serial *serial, - int start_address, int length, __u8 address_type, __u8 *buffer) + int start_address, int length, u8 address_type, u8 *buffer) { struct device *dev = &serial->serial->dev->dev; int status = 0; @@ -522,7 +522,7 @@ static int tx_active(struct edgeport_port *port) { int status; struct out_endpoint_desc_block *oedb; - __u8 *lsr; + u8 *lsr; int bytes_left = 0; oedb = kmalloc(sizeof(*oedb), GFP_KERNEL); @@ -593,7 +593,7 @@ static int choose_config(struct usb_device *dev) } static int read_rom(struct edgeport_serial *serial, - int start_address, int length, __u8 *buffer) + int start_address, int length, u8 *buffer) { int status; @@ -611,7 +611,7 @@ static int read_rom(struct edgeport_serial *serial, } static int write_rom(struct edgeport_serial *serial, int start_address, - int length, __u8 *buffer) + int length, u8 *buffer) { if (serial->product_info.TiMode == TI_MODE_BOOT) return write_boot_mem(serial, start_address, length, @@ -636,7 +636,7 @@ static int get_descriptor_addr(struct edgeport_serial *serial, status = read_rom(serial, start_address, sizeof(struct ti_i2c_desc), - (__u8 *)rom_desc); + (u8 *)rom_desc); if (status) return 0; @@ -652,13 +652,13 @@ static int get_descriptor_addr(struct edgeport_serial *serial, } /* Validate descriptor checksum */ -static int valid_csum(struct ti_i2c_desc *rom_desc, __u8 *buffer) +static int valid_csum(struct ti_i2c_desc *rom_desc, u8 *buffer) { - __u16 i; - __u8 cs = 0; + u16 i; + u8 cs = 0; for (i = 0; i < le16_to_cpu(rom_desc->Size); i++) - cs = (__u8)(cs + buffer[i]); + cs = (u8)(cs + buffer[i]); if (cs != rom_desc->CheckSum) { pr_debug("%s - Mismatch %x - %x", __func__, rom_desc->CheckSum, cs); @@ -674,8 +674,8 @@ static int check_i2c_image(struct edgeport_serial *serial) int status = 0; struct ti_i2c_desc *rom_desc; int start_address = 2; - __u8 *buffer; - __u16 ttype; + u8 *buffer; + u16 ttype; rom_desc = kmalloc(sizeof(*rom_desc), GFP_KERNEL); if (!rom_desc) @@ -703,7 +703,7 @@ static int check_i2c_image(struct edgeport_serial *serial) status = read_rom(serial, start_address, sizeof(struct ti_i2c_desc), - (__u8 *)rom_desc); + (u8 *)rom_desc); if (status) break; @@ -748,7 +748,7 @@ static int check_i2c_image(struct edgeport_serial *serial) return status; } -static int get_manuf_info(struct edgeport_serial *serial, __u8 *buffer) +static int get_manuf_info(struct edgeport_serial *serial, u8 *buffer) { int status; int start_address; @@ -793,10 +793,10 @@ static int get_manuf_info(struct edgeport_serial *serial, __u8 *buffer) /* Build firmware header used for firmware update */ static int build_i2c_fw_hdr(u8 *header, const struct firmware *fw) { - __u8 *buffer; + u8 *buffer; int buffer_size; int i; - __u8 cs = 0; + u8 cs = 0; struct ti_i2c_desc *i2c_header; struct ti_i2c_image_header *img_header; struct ti_i2c_firmware_rec *firmware_rec; @@ -840,7 +840,7 @@ static int build_i2c_fw_hdr(u8 *header, const struct firmware *fw) le16_to_cpu(img_header->Length)); for (i=0; i < buffer_size; i++) { - cs = (__u8)(cs + buffer[i]); + cs = (u8)(cs + buffer[i]); } kfree(buffer); @@ -916,7 +916,7 @@ static int bulk_xfer(struct usb_serial *serial, void *buffer, } /* Download given firmware image to the device (IN BOOT MODE) */ -static int download_code(struct edgeport_serial *serial, __u8 *image, +static int download_code(struct edgeport_serial *serial, u8 *image, int image_length) { int status = 0; @@ -1090,7 +1090,7 @@ static int do_download_mode(struct edgeport_serial *serial, if (!ti_manuf_desc) return -ENOMEM; - status = get_manuf_info(serial, (__u8 *)ti_manuf_desc); + status = get_manuf_info(serial, (u8 *)ti_manuf_desc); if (status) { kfree(ti_manuf_desc); return status; @@ -1135,7 +1135,7 @@ static int do_download_mode(struct edgeport_serial *serial, status = read_rom(serial, start_address + sizeof(struct ti_i2c_desc), sizeof(struct ti_i2c_firmware_rec), - (__u8 *)firmware_version); + (u8 *)firmware_version); if (status) { kfree(firmware_version); kfree(rom_desc); @@ -1261,8 +1261,8 @@ static int do_download_mode(struct edgeport_serial *serial, if (start_address != 0) { #define HEADER_SIZE (sizeof(struct ti_i2c_desc) + \ sizeof(struct ti_i2c_firmware_rec)) - __u8 *header; - __u8 *vheader; + u8 *header; + u8 *vheader; header = kmalloc(HEADER_SIZE, GFP_KERNEL); if (!header) { @@ -1408,8 +1408,8 @@ static int do_boot_mode(struct edgeport_serial *serial, if (!check_i2c_image(serial)) { struct ti_i2c_image_header *header; int i; - __u8 cs = 0; - __u8 *buffer; + u8 cs = 0; + u8 *buffer; int buffer_size; /* @@ -1420,7 +1420,7 @@ static int do_boot_mode(struct edgeport_serial *serial, if (!ti_manuf_desc) return -ENOMEM; - status = get_manuf_info(serial, (__u8 *)ti_manuf_desc); + status = get_manuf_info(serial, (u8 *)ti_manuf_desc); if (status) { kfree(ti_manuf_desc); goto stayinbootmode; @@ -1463,13 +1463,13 @@ static int do_boot_mode(struct edgeport_serial *serial, for (i = sizeof(struct ti_i2c_image_header); i < buffer_size; i++) { - cs = (__u8)(cs + buffer[i]); + cs = (u8)(cs + buffer[i]); } header = (struct ti_i2c_image_header *)buffer; /* update length and checksum after padding */ - header->Length = cpu_to_le16((__u16)(buffer_size - + header->Length = cpu_to_le16((u16)(buffer_size - sizeof(struct ti_i2c_image_header))); header->CheckSum = cs; @@ -1504,15 +1504,12 @@ static int do_boot_mode(struct edgeport_serial *serial, static int ti_do_config(struct edgeport_port *port, int feature, int on) { - int port_number = port->port->port_number; - on = !!on; /* 1 or 0 not bitmask */ - return send_cmd(port->port->serial->dev, - feature, (__u8)(UMPM_UART1_PORT + port_number), - on, NULL, 0); + + return send_port_cmd(port->port, feature, on, NULL, 0); } -static int restore_mcr(struct edgeport_port *port, __u8 mcr) +static int restore_mcr(struct edgeport_port *port, u8 mcr) { int status = 0; @@ -1528,9 +1525,9 @@ static int restore_mcr(struct edgeport_port *port, __u8 mcr) } /* Convert TI LSR to standard UART flags */ -static __u8 map_line_status(__u8 ti_lsr) +static u8 map_line_status(u8 ti_lsr) { - __u8 lsr = 0; + u8 lsr = 0; #define MAP_FLAG(flagUmp, flagUart) \ if (ti_lsr & flagUmp) \ @@ -1548,7 +1545,7 @@ static __u8 map_line_status(__u8 ti_lsr) return lsr; } -static void handle_new_msr(struct edgeport_port *edge_port, __u8 msr) +static void handle_new_msr(struct edgeport_port *edge_port, u8 msr) { struct async_icount *icount; struct tty_struct *tty; @@ -1584,10 +1581,10 @@ static void handle_new_msr(struct edgeport_port *edge_port, __u8 msr) } static void handle_new_lsr(struct edgeport_port *edge_port, int lsr_data, - __u8 lsr, __u8 data) + u8 lsr, u8 data) { struct async_icount *icount; - __u8 new_lsr = (__u8)(lsr & (__u8)(LSR_OVER_ERR | LSR_PAR_ERR | + u8 new_lsr = (u8)(lsr & (u8)(LSR_OVER_ERR | LSR_PAR_ERR | LSR_FRM_ERR | LSR_BREAK)); dev_dbg(&edge_port->port->dev, "%s - %02x\n", __func__, new_lsr); @@ -1599,7 +1596,7 @@ static void handle_new_lsr(struct edgeport_port *edge_port, int lsr_data, * Parity and Framing errors only count if they * occur exclusive of a break being received. */ - new_lsr &= (__u8)(LSR_OVER_ERR | LSR_BREAK); + new_lsr &= (u8)(LSR_OVER_ERR | LSR_BREAK); /* Place LSR data byte into Rx buffer */ if (lsr_data) @@ -1628,8 +1625,8 @@ static void edge_interrupt_callback(struct urb *urb) int port_number; int function; int retval; - __u8 lsr; - __u8 msr; + u8 lsr; + u8 msr; int status = urb->status; switch (status) { @@ -1837,7 +1834,6 @@ static int edge_open(struct tty_struct *tty, struct usb_serial_port *port) struct edgeport_serial *edge_serial; struct usb_device *dev; struct urb *urb; - int port_number; int status; u16 open_settings; u8 transaction_timeout; @@ -1845,8 +1841,6 @@ static int edge_open(struct tty_struct *tty, struct usb_serial_port *port) if (edge_port == NULL) return -ENODEV; - port_number = port->port_number; - dev = port->serial->dev; /* turn off loopback */ @@ -1878,8 +1872,7 @@ static int edge_open(struct tty_struct *tty, struct usb_serial_port *port) dev_dbg(&port->dev, "%s - Sending UMPC_OPEN_PORT\n", __func__); /* Tell TI to open and start the port */ - status = send_cmd(dev, UMPC_OPEN_PORT, - (u8)(UMPM_UART1_PORT + port_number), open_settings, NULL, 0); + status = send_port_cmd(port, UMPC_OPEN_PORT, open_settings, NULL, 0); if (status) { dev_err(&port->dev, "%s - cannot send open command, %d\n", __func__, status); @@ -1887,8 +1880,7 @@ static int edge_open(struct tty_struct *tty, struct usb_serial_port *port) } /* Start the DMA? */ - status = send_cmd(dev, UMPC_START_PORT, - (u8)(UMPM_UART1_PORT + port_number), 0, NULL, 0); + status = send_port_cmd(port, UMPC_START_PORT, 0, NULL, 0); if (status) { dev_err(&port->dev, "%s - cannot send start DMA command, %d\n", __func__, status); @@ -1905,9 +1897,7 @@ static int edge_open(struct tty_struct *tty, struct usb_serial_port *port) } /* Read Initial MSR */ - status = ti_vread_sync(dev, UMPC_READ_MSR, 0, - (__u16)(UMPM_UART1_PORT + port_number), - &edge_port->shadow_msr, 1); + status = read_port_cmd(port, UMPC_READ_MSR, 0, &edge_port->shadow_msr, 1); if (status) { dev_err(&port->dev, "%s - cannot send read MSR command, %d\n", __func__, status); @@ -1971,9 +1961,7 @@ static void edge_close(struct usb_serial_port *port) { struct edgeport_serial *edge_serial; struct edgeport_port *edge_port; - struct usb_serial *serial = port->serial; unsigned long flags; - int port_number; edge_serial = usb_get_serial_data(port->serial); edge_port = usb_get_serial_port_data(port); @@ -1994,9 +1982,7 @@ static void edge_close(struct usb_serial_port *port) spin_unlock_irqrestore(&edge_port->ep_lock, flags); dev_dbg(&port->dev, "%s - send umpc_close_port\n", __func__); - port_number = port->port_number; - send_cmd(serial->dev, UMPC_CLOSE_PORT, - (__u8)(UMPM_UART1_PORT + port_number), 0, NULL, 0); + send_port_cmd(port, UMPC_CLOSE_PORT, 0, NULL, 0); mutex_lock(&edge_serial->es_lock); --edge_port->edge_serial->num_ports_open; @@ -2229,7 +2215,6 @@ static void change_port_settings(struct tty_struct *tty, int baud; unsigned cflag; int status; - int port_number = edge_port->port->port_number; config = kmalloc (sizeof (*config), GFP_KERNEL); if (!config) { @@ -2244,7 +2229,7 @@ static void change_port_settings(struct tty_struct *tty, /* These flags must be set */ config->wFlags |= UMP_MASK_UART_FLAGS_RECEIVE_MS_INT; config->wFlags |= UMP_MASK_UART_FLAGS_AUTO_START_ON_ERR; - config->bUartMode = (__u8)(edge_port->bUartMode); + config->bUartMode = (u8)(edge_port->bUartMode); switch (cflag & CSIZE) { case CS5: @@ -2336,7 +2321,7 @@ static void change_port_settings(struct tty_struct *tty, } edge_port->baud_rate = baud; - config->wBaudRate = (__u16)((461550L + baud/2) / baud); + config->wBaudRate = (u16)((461550L + baud/2) / baud); /* FIXME: Recompute actual baud from divisor here */ @@ -2355,9 +2340,8 @@ static void change_port_settings(struct tty_struct *tty, cpu_to_be16s(&config->wFlags); cpu_to_be16s(&config->wBaudRate); - status = send_cmd(edge_port->port->serial->dev, UMPC_SET_CONFIG, - (__u8)(UMPM_UART1_PORT + port_number), - 0, (__u8 *)config, sizeof(*config)); + status = send_port_cmd(edge_port->port, UMPC_SET_CONFIG, 0, config, + sizeof(*config)); if (status) dev_dbg(dev, "%s - error %d when trying to write config to device\n", __func__, status); @@ -2433,28 +2417,6 @@ static int edge_tiocmget(struct tty_struct *tty) return result; } -static int get_serial_info(struct tty_struct *tty, - struct serial_struct *ss) -{ - struct usb_serial_port *port = tty->driver_data; - struct edgeport_port *edge_port = usb_get_serial_port_data(port); - unsigned cwait; - - cwait = edge_port->port->port.closing_wait; - if (cwait != ASYNC_CLOSING_WAIT_NONE) - cwait = jiffies_to_msecs(cwait) / 10; - - ss->type = PORT_16550A; - ss->line = edge_port->port->minor; - ss->port = edge_port->port->port_number; - ss->irq = 0; - ss->xmit_fifo_size = edge_port->port->bulk_out_size; - ss->baud_base = 9600; - ss->close_delay = 5*HZ; - ss->closing_wait = cwait; - return 0; -} - static void edge_break(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; @@ -2615,7 +2577,10 @@ static int edge_port_probe(struct usb_serial_port *port) if (ret) goto err; - port->port.closing_wait = msecs_to_jiffies(closing_wait * 10); + /* + * The LSR does not tell when the transmitter shift register has + * emptied so add a one-character drain delay. + */ port->port.drain_delay = 1; return 0; @@ -2713,7 +2678,6 @@ static struct usb_serial_driver edgeport_1port_device = { .release = edge_release, .port_probe = edge_port_probe, .port_remove = edge_port_remove, - .get_serial = get_serial_info, .set_termios = edge_set_termios, .tiocmget = edge_tiocmget, .tiocmset = edge_tiocmset, @@ -2752,7 +2716,6 @@ static struct usb_serial_driver edgeport_2port_device = { .release = edge_release, .port_probe = edge_port_probe, .port_remove = edge_port_remove, - .get_serial = get_serial_info, .set_termios = edge_set_termios, .tiocmget = edge_tiocmget, .tiocmset = edge_tiocmset, @@ -2783,9 +2746,6 @@ MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); MODULE_FIRMWARE("edgeport/down3.bin"); -module_param(closing_wait, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(closing_wait, "Maximum wait for data to drain, in .01 secs"); - module_param(ignore_cpu_rev, bool, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(ignore_cpu_rev, "Ignore the cpu revision when connecting to a device"); diff --git a/drivers/usb/serial/io_ti.h b/drivers/usb/serial/io_ti.h index 50b899d55ed0..24fe1312c84d 100644 --- a/drivers/usb/serial/io_ti.h +++ b/drivers/usb/serial/io_ti.h @@ -133,15 +133,15 @@ #define UMPD_OEDB2_ADDRESS 0xFF10 struct out_endpoint_desc_block { - __u8 Configuration; - __u8 XBufAddr; - __u8 XByteCount; - __u8 Unused1; - __u8 Unused2; - __u8 YBufAddr; - __u8 YByteCount; - __u8 BufferSize; -} __attribute__((packed)); + u8 Configuration; + u8 XBufAddr; + u8 XByteCount; + u8 Unused1; + u8 Unused2; + u8 YBufAddr; + u8 YByteCount; + u8 BufferSize; +}; /* @@ -150,16 +150,16 @@ struct out_endpoint_desc_block { */ /* UART settings */ struct ump_uart_config { - __u16 wBaudRate; /* Baud rate */ - __u16 wFlags; /* Bitmap mask of flags */ - __u8 bDataBits; /* 5..8 - data bits per character */ - __u8 bParity; /* Parity settings */ - __u8 bStopBits; /* Stop bits settings */ + u16 wBaudRate; /* Baud rate */ + u16 wFlags; /* Bitmap mask of flags */ + u8 bDataBits; /* 5..8 - data bits per character */ + u8 bParity; /* Parity settings */ + u8 bStopBits; /* Stop bits settings */ char cXon; /* XON character */ char cXoff; /* XOFF character */ - __u8 bUartMode; /* Will be updated when a user */ + u8 bUartMode; /* Will be updated when a user */ /* interface is defined */ -} __attribute__((packed)); +}; /* @@ -168,9 +168,9 @@ struct ump_uart_config { */ /* Interrupt packet structure */ struct ump_interrupt { - __u8 bICode; /* Interrupt code (interrupt num) */ - __u8 bIInfo; /* Interrupt information */ -} __attribute__((packed)); + u8 bICode; /* Interrupt code (interrupt num) */ + u8 bIInfo; /* Interrupt information */ +}; #define TIUMP_GET_PORT_FROM_CODE(c) (((c) >> 6) & 0x01) diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index 093afd67a664..19753611e7b0 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -643,7 +643,6 @@ static void iuu_uart_read_callback(struct urb *urb) struct iuu_private *priv = usb_get_serial_port_data(port); unsigned long flags; int status = urb->status; - int error = 0; int len = 0; unsigned char *data = urb->transfer_buffer; priv->poll++; @@ -660,12 +659,11 @@ static void iuu_uart_read_callback(struct urb *urb) if (urb->actual_length > 1) { dev_dbg(&port->dev, "%s - urb->actual_length = %i\n", __func__, urb->actual_length); - error = 1; return; } /* if len > 0 call readbuf */ - if (len > 0 && error == 0) { + if (len > 0) { dev_dbg(&port->dev, "%s - call read buf - len to read is %i\n", __func__, len); status = iuu_read_buf(port, len); diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 622077dcc344..b04a029e3657 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -41,27 +41,7 @@ #define DRIVER_AUTHOR "Hugh Blemings dev, "%s - set=%d, clear=%d\n", __func__, set, clear); + dev_dbg(&port->dev, "%s - set=%d, clear=%d\n", __func__, set, clear); spin_lock_irqsave(&metro_priv->lock, flags); control_state = metro_priv->control_state; @@ -334,7 +334,7 @@ static void metrousb_unthrottle(struct tty_struct *tty) /* Submit the urb to read from the port. */ result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); if (result) - dev_err(tty->dev, + dev_err(&port->dev, "failed submitting interrupt in urb error code=%d\n", result); } diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index bb3d39307d93..6ee83886e2c9 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -1634,23 +1634,6 @@ static int mos7720_tiocmset(struct tty_struct *tty, return 0; } -static int get_serial_info(struct tty_struct *tty, - struct serial_struct *ss) -{ - struct usb_serial_port *port = tty->driver_data; - struct moschip_port *mos7720_port = usb_get_serial_port_data(port); - - ss->type = PORT_16550A; - ss->line = mos7720_port->port->minor; - ss->port = mos7720_port->port->port_number; - ss->irq = 0; - ss->xmit_fifo_size = NUM_URBS * URB_TRANSFER_BUFFER_SIZE; - ss->baud_base = 9600; - ss->close_delay = 5*HZ; - ss->closing_wait = 30*HZ; - return 0; -} - static int mos7720_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { @@ -1790,7 +1773,6 @@ static struct usb_serial_driver moschip7720_2port_driver = { .ioctl = mos7720_ioctl, .tiocmget = mos7720_tiocmget, .tiocmset = mos7720_tiocmset, - .get_serial = get_serial_info, .set_termios = mos7720_set_termios, .write = mos7720_write, .write_room = mos7720_write_room, diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 1bf0d066f55a..28e4093794e0 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -1383,28 +1383,6 @@ static int mos7840_get_lsr_info(struct tty_struct *tty, return 0; } -/***************************************************************************** - * mos7840_get_serial_info - * function to get information about serial port - *****************************************************************************/ - -static int mos7840_get_serial_info(struct tty_struct *tty, - struct serial_struct *ss) -{ - struct usb_serial_port *port = tty->driver_data; - struct moschip_port *mos7840_port = usb_get_serial_port_data(port); - - ss->type = PORT_16550A; - ss->line = mos7840_port->port->minor; - ss->port = mos7840_port->port->port_number; - ss->irq = 0; - ss->xmit_fifo_size = NUM_URBS * URB_TRANSFER_BUFFER_SIZE; - ss->baud_base = 9600; - ss->close_delay = 5 * HZ; - ss->closing_wait = 30 * HZ; - return 0; -} - /***************************************************************************** * SerialIoctl * this function handles any ioctl calls to the driver @@ -1783,7 +1761,6 @@ static struct usb_serial_driver moschip7840_4port_device = { .probe = mos7840_probe, .attach = mos7840_attach, .ioctl = mos7840_ioctl, - .get_serial = mos7840_get_serial_info, .set_termios = mos7840_set_termios, .break_ctl = mos7840_break, .tiocmget = mos7840_tiocmget, diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c index eecb72aef83e..40c713fae0c3 100644 --- a/drivers/usb/serial/opticon.c +++ b/drivers/usb/serial/opticon.c @@ -352,23 +352,6 @@ static int opticon_tiocmset(struct tty_struct *tty, return 0; } -static int get_serial_info(struct tty_struct *tty, - struct serial_struct *ss) -{ - struct usb_serial_port *port = tty->driver_data; - - /* fake emulate a 16550 uart to make userspace code happy */ - ss->type = PORT_16550A; - ss->line = port->minor; - ss->port = 0; - ss->irq = 0; - ss->xmit_fifo_size = 1024; - ss->baud_base = 9600; - ss->close_delay = 5*HZ; - ss->closing_wait = 30*HZ; - return 0; -} - static int opticon_port_probe(struct usb_serial_port *port) { struct opticon_private *priv; @@ -410,7 +393,6 @@ static struct usb_serial_driver opticon_device = { .chars_in_buffer = opticon_chars_in_buffer, .throttle = usb_serial_generic_throttle, .unthrottle = usb_serial_generic_unthrottle, - .get_serial = get_serial_info, .tiocmget = opticon_tiocmget, .tiocmset = opticon_tiocmset, .process_read_urb = opticon_process_read_urb, diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index c6969ca72839..3e79a543d3e7 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -2095,8 +2095,6 @@ static struct usb_serial_driver option_1port_device = { .chars_in_buffer = usb_wwan_chars_in_buffer, .tiocmget = usb_wwan_tiocmget, .tiocmset = usb_wwan_tiocmset, - .get_serial = usb_wwan_get_serial_info, - .set_serial = usb_wwan_set_serial_info, .attach = option_attach, .release = option_release, .port_probe = usb_wwan_port_probe, diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index eed9acd1ae08..fd773d252691 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -173,17 +173,22 @@ MODULE_DEVICE_TABLE(usb, id_table); static void pl2303_set_break(struct usb_serial_port *port, bool enable); enum pl2303_type { - TYPE_01, /* Type 0 and 1 (difference unknown) */ - TYPE_HX, /* HX version of the pl2303 chip */ - TYPE_HXN, /* HXN version of the pl2303 chip */ + TYPE_H, + TYPE_HX, + TYPE_TA, + TYPE_TB, + TYPE_HXD, + TYPE_HXN, TYPE_COUNT }; struct pl2303_type_data { + const char *name; speed_t max_baud_rate; unsigned long quirks; unsigned int no_autoxonxoff:1; unsigned int no_divisors:1; + unsigned int alt_divisors:1; }; struct pl2303_serial_private { @@ -200,15 +205,32 @@ struct pl2303_private { }; static const struct pl2303_type_data pl2303_type_data[TYPE_COUNT] = { - [TYPE_01] = { + [TYPE_H] = { + .name = "H", .max_baud_rate = 1228800, .quirks = PL2303_QUIRK_LEGACY, .no_autoxonxoff = true, }, [TYPE_HX] = { + .name = "HX", + .max_baud_rate = 6000000, + }, + [TYPE_TA] = { + .name = "TA", + .max_baud_rate = 6000000, + .alt_divisors = true, + }, + [TYPE_TB] = { + .name = "TB", + .max_baud_rate = 12000000, + .alt_divisors = true, + }, + [TYPE_HXD] = { + .name = "HXD", .max_baud_rate = 12000000, }, [TYPE_HXN] = { + .name = "G", .max_baud_rate = 12000000, .no_divisors = true, }, @@ -362,42 +384,82 @@ static int pl2303_calc_num_ports(struct usb_serial *serial, return 1; } +static bool pl2303_supports_hx_status(struct usb_serial *serial) +{ + int ret; + u8 buf; + + ret = usb_control_msg_recv(serial->dev, 0, VENDOR_READ_REQUEST, + VENDOR_READ_REQUEST_TYPE, PL2303_READ_TYPE_HX_STATUS, + 0, &buf, 1, 100, GFP_KERNEL); + + return ret == 0; +} + +static int pl2303_detect_type(struct usb_serial *serial) +{ + struct usb_device_descriptor *desc = &serial->dev->descriptor; + u16 bcdDevice, bcdUSB; + + /* + * Legacy PL2303H, variants 0 and 1 (difference unknown). + */ + if (desc->bDeviceClass == 0x02) + return TYPE_H; /* variant 0 */ + + if (desc->bMaxPacketSize0 != 0x40) { + if (desc->bDeviceClass == 0x00 || desc->bDeviceClass == 0xff) + return TYPE_H; /* variant 1 */ + + return TYPE_H; /* variant 0 */ + } + + bcdDevice = le16_to_cpu(desc->bcdDevice); + bcdUSB = le16_to_cpu(desc->bcdUSB); + + switch (bcdDevice) { + case 0x100: + /* + * Assume it's an HXN-type if the device doesn't support the old read + * request value. + */ + if (bcdUSB == 0x200 && !pl2303_supports_hx_status(serial)) + return TYPE_HXN; + break; + case 0x300: + if (bcdUSB == 0x200) + return TYPE_TA; + + return TYPE_HX; + case 0x400: + return TYPE_HXD; + case 0x500: + return TYPE_TB; + } + + dev_err(&serial->interface->dev, + "unknown device type, please report to linux-usb@vger.kernel.org\n"); + return -ENODEV; +} + static int pl2303_startup(struct usb_serial *serial) { struct pl2303_serial_private *spriv; - enum pl2303_type type = TYPE_01; + enum pl2303_type type; unsigned char *buf; - int res; + int ret; + + ret = pl2303_detect_type(serial); + if (ret < 0) + return ret; + + type = ret; + dev_dbg(&serial->interface->dev, "device type: %s\n", pl2303_type_data[type].name); spriv = kzalloc(sizeof(*spriv), GFP_KERNEL); if (!spriv) return -ENOMEM; - buf = kmalloc(1, GFP_KERNEL); - if (!buf) { - kfree(spriv); - return -ENOMEM; - } - - if (serial->dev->descriptor.bDeviceClass == 0x02) - type = TYPE_01; /* type 0 */ - else if (serial->dev->descriptor.bMaxPacketSize0 == 0x40) - type = TYPE_HX; - else if (serial->dev->descriptor.bDeviceClass == 0x00) - type = TYPE_01; /* type 1 */ - else if (serial->dev->descriptor.bDeviceClass == 0xFF) - type = TYPE_01; /* type 1 */ - dev_dbg(&serial->interface->dev, "device type: %d\n", type); - - if (type == TYPE_HX) { - res = usb_control_msg(serial->dev, - usb_rcvctrlpipe(serial->dev, 0), - VENDOR_READ_REQUEST, VENDOR_READ_REQUEST_TYPE, - PL2303_READ_TYPE_HX_STATUS, 0, buf, 1, 100); - if (res != 1) - type = TYPE_HXN; - } - spriv->type = &pl2303_type_data[type]; spriv->quirks = (unsigned long)usb_get_serial_data(serial); spriv->quirks |= spriv->type->quirks; @@ -405,6 +467,12 @@ static int pl2303_startup(struct usb_serial *serial) usb_set_serial_data(serial, spriv); if (type != TYPE_HXN) { + buf = kmalloc(1, GFP_KERNEL); + if (!buf) { + kfree(spriv); + return -ENOMEM; + } + pl2303_vendor_read(serial, 0x8484, buf); pl2303_vendor_write(serial, 0x0404, 0); pl2303_vendor_read(serial, 0x8484, buf); @@ -419,9 +487,9 @@ static int pl2303_startup(struct usb_serial *serial) pl2303_vendor_write(serial, 2, 0x24); else pl2303_vendor_write(serial, 2, 0x44); - } - kfree(buf); + kfree(buf); + } return 0; } @@ -553,6 +621,45 @@ static speed_t pl2303_encode_baud_rate_divisor(unsigned char buf[4], return baud; } +static speed_t pl2303_encode_baud_rate_divisor_alt(unsigned char buf[4], + speed_t baud) +{ + unsigned int baseline, mantissa, exponent; + + /* + * Apparently, for the TA version the formula is: + * baudrate = 12M * 32 / (mantissa * 2^exponent) + * where + * mantissa = buf[10:0] + * exponent = buf[15:13 16] + */ + baseline = 12000000 * 32; + mantissa = baseline / baud; + if (mantissa == 0) + mantissa = 1; /* Avoid dividing by zero if baud > 32*12M. */ + exponent = 0; + while (mantissa >= 2048) { + if (exponent < 15) { + mantissa >>= 1; /* divide by 2 */ + exponent++; + } else { + /* Exponent is maxed. Trim mantissa and leave. */ + mantissa = 2047; + break; + } + } + + buf[3] = 0x80; + buf[2] = exponent & 0x01; + buf[1] = (exponent & ~0x01) << 4 | mantissa >> 8; + buf[0] = mantissa & 0xff; + + /* Calculate and return the exact baud rate. */ + baud = (baseline / mantissa) >> exponent; + + return baud; +} + static void pl2303_encode_baud_rate(struct tty_struct *tty, struct usb_serial_port *port, u8 buf[4]) @@ -580,6 +687,8 @@ static void pl2303_encode_baud_rate(struct tty_struct *tty, if (baud == baud_sup) baud = pl2303_encode_baud_rate_direct(buf, baud); + else if (spriv->type->alt_divisors) + baud = pl2303_encode_baud_rate_divisor_alt(buf, baud); else baud = pl2303_encode_baud_rate_divisor(buf, baud); @@ -939,18 +1048,6 @@ static int pl2303_carrier_raised(struct usb_serial_port *port) return 0; } -static int pl2303_get_serial(struct tty_struct *tty, - struct serial_struct *ss) -{ - struct usb_serial_port *port = tty->driver_data; - - ss->type = PORT_16654; - ss->line = port->minor; - ss->port = port->port_number; - ss->baud_base = 460800; - return 0; -} - static void pl2303_set_break(struct usb_serial_port *port, bool enable) { struct usb_serial *serial = port->serial; @@ -1134,7 +1231,6 @@ static struct usb_serial_driver pl2303_device = { .close = pl2303_close, .dtr_rts = pl2303_dtr_rts, .carrier_raised = pl2303_carrier_raised, - .get_serial = pl2303_get_serial, .break_ctl = pl2303_break_ctl, .set_termios = pl2303_set_termios, .tiocmget = pl2303_tiocmget, diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index 599dcb2e374d..5f2e7f668e68 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -453,21 +453,6 @@ static void qt2_disconnect(struct usb_serial *serial) usb_kill_urb(serial_priv->read_urb); } -static int get_serial_info(struct tty_struct *tty, - struct serial_struct *ss) -{ - struct usb_serial_port *port = tty->driver_data; - - ss->line = port->minor; - ss->port = 0; - ss->irq = 0; - ss->xmit_fifo_size = port->bulk_out_size; - ss->baud_base = 9600; - ss->close_delay = 5*HZ; - ss->closing_wait = 30*HZ; - return 0; -} - static void qt2_process_status(struct usb_serial_port *port, unsigned char *ch) { switch (*ch) { @@ -978,7 +963,6 @@ static struct usb_serial_driver qt2_device = { .tiocmset = qt2_tiocmset, .tiocmiwait = usb_serial_generic_tiocmiwait, .get_icount = usb_serial_generic_get_icount, - .get_serial = get_serial_info, .set_termios = qt2_set_termios, }; diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c index 89fdc5c19285..3baf7c0f5a98 100644 --- a/drivers/usb/serial/ssu100.c +++ b/drivers/usb/serial/ssu100.c @@ -331,21 +331,6 @@ static int ssu100_open(struct tty_struct *tty, struct usb_serial_port *port) return usb_serial_generic_open(tty, port); } -static int get_serial_info(struct tty_struct *tty, - struct serial_struct *ss) -{ - struct usb_serial_port *port = tty->driver_data; - - ss->line = port->minor; - ss->port = 0; - ss->irq = 0; - ss->xmit_fifo_size = port->bulk_out_size; - ss->baud_base = 9600; - ss->close_delay = 5*HZ; - ss->closing_wait = 30*HZ; - return 0; -} - static int ssu100_attach(struct usb_serial *serial) { return ssu100_initdevice(serial->dev); @@ -545,7 +530,6 @@ static struct usb_serial_driver ssu100_device = { .tiocmset = ssu100_tiocmset, .tiocmiwait = usb_serial_generic_tiocmiwait, .get_icount = usb_serial_generic_get_icount, - .get_serial = get_serial_info, .set_termios = ssu100_set_termios, }; diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 7252b0ce75a6..caa46ac23db9 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -121,6 +121,7 @@ #define TI_LSR_ERROR 0x0F #define TI_LSR_RX_FULL 0x10 #define TI_LSR_TX_EMPTY 0x20 +#define TI_LSR_TX_EMPTY_BOTH 0x40 /* Line control */ #define TI_LCR_BREAK 0x40 @@ -183,7 +184,7 @@ struct ti_uart_config { char cXon; char cXoff; u8 bUartMode; -} __packed; +}; /* Get port status */ struct ti_port_status { @@ -192,7 +193,7 @@ struct ti_port_status { u8 bErrorCode; u8 bMSR; u8 bLSR; -} __packed; +}; /* Purge modes */ #define TI_PURGE_OUTPUT 0x00 @@ -223,25 +224,25 @@ struct ti_write_data_bytes { } __packed; struct ti_read_data_request { - __u8 bAddrType; - __u8 bDataType; - __u8 bDataCounter; + u8 bAddrType; + u8 bDataType; + u8 bDataCounter; __be16 wBaseAddrHi; __be16 wBaseAddrLo; } __packed; struct ti_read_data_bytes { - __u8 bCmdCode; - __u8 bModuleId; - __u8 bErrorCode; - __u8 bData[]; -} __packed; + u8 bCmdCode; + u8 bModuleId; + u8 bErrorCode; + u8 bData[]; +}; /* Interrupt struct */ struct ti_interrupt { - __u8 bICode; - __u8 bIInfo; -} __packed; + u8 bICode; + u8 bIInfo; +}; /* Interrupt codes */ #define TI_CODE_HARDWARE_ERROR 0xFF @@ -270,8 +271,6 @@ struct ti_firmware_header { #define TI_TRANSFER_TIMEOUT 2 -#define TI_DEFAULT_CLOSING_WAIT 4000 /* in .01 secs */ - /* read urb states */ #define TI_READ_URB_RUNNING 0 #define TI_READ_URB_STOPPING 1 @@ -328,27 +327,26 @@ static void ti_recv(struct usb_serial_port *port, unsigned char *data, static void ti_send(struct ti_port *tport); static int ti_set_mcr(struct ti_port *tport, unsigned int mcr); static int ti_get_lsr(struct ti_port *tport, u8 *lsr); -static int ti_get_serial_info(struct tty_struct *tty, - struct serial_struct *ss); -static int ti_set_serial_info(struct tty_struct *tty, - struct serial_struct *ss); +static void ti_get_serial_info(struct tty_struct *tty, struct serial_struct *ss); static void ti_handle_new_msr(struct ti_port *tport, u8 msr); static void ti_stop_read(struct ti_port *tport, struct tty_struct *tty); static int ti_restart_read(struct ti_port *tport, struct tty_struct *tty); -static int ti_command_out_sync(struct ti_device *tdev, __u8 command, - __u16 moduleid, __u16 value, __u8 *data, int size); -static int ti_command_in_sync(struct ti_device *tdev, __u8 command, - __u16 moduleid, __u16 value, __u8 *data, int size); +static int ti_command_out_sync(struct usb_device *udev, u8 command, + u16 moduleid, u16 value, void *data, int size); +static int ti_command_in_sync(struct usb_device *udev, u8 command, + u16 moduleid, u16 value, void *data, int size); +static int ti_port_cmd_out(struct usb_serial_port *port, u8 command, + u16 value, void *data, int size); +static int ti_port_cmd_in(struct usb_serial_port *port, u8 command, + u16 value, void *data, int size); static int ti_write_byte(struct usb_serial_port *port, struct ti_device *tdev, unsigned long addr, u8 mask, u8 byte); static int ti_download_firmware(struct ti_device *tdev); -static int closing_wait = TI_DEFAULT_CLOSING_WAIT; - static const struct usb_device_id ti_id_table_3410[] = { { USB_DEVICE(TI_VENDOR_ID, TI_3410_PRODUCT_ID) }, { USB_DEVICE(TI_VENDOR_ID, TI_3410_EZ430_ID) }, @@ -435,7 +433,6 @@ static struct usb_serial_driver ti_1port_device = { .throttle = ti_throttle, .unthrottle = ti_unthrottle, .get_serial = ti_get_serial_info, - .set_serial = ti_set_serial_info, .set_termios = ti_set_termios, .tiocmget = ti_tiocmget, .tiocmset = ti_tiocmset, @@ -469,7 +466,6 @@ static struct usb_serial_driver ti_2port_device = { .throttle = ti_throttle, .unthrottle = ti_unthrottle, .get_serial = ti_get_serial_info, - .set_serial = ti_set_serial_info, .set_termios = ti_set_termios, .tiocmget = ti_tiocmget, .tiocmset = ti_tiocmset, @@ -502,10 +498,6 @@ MODULE_FIRMWARE("moxa/moxa-1131.fw"); MODULE_FIRMWARE("moxa/moxa-1150.fw"); MODULE_FIRMWARE("moxa/moxa-1151.fw"); -module_param(closing_wait, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(closing_wait, - "Maximum wait for data to drain in close, in .01 secs, default is 4000"); - MODULE_DEVICE_TABLE(usb, ti_id_table_combined); module_usb_serial_driver(serial_drivers, ti_id_table_combined); @@ -613,7 +605,6 @@ static int ti_port_probe(struct usb_serial_port *port) tport->tp_uart_base_addr = TI_UART1_BASE_ADDR; else tport->tp_uart_base_addr = TI_UART2_BASE_ADDR; - port->port.closing_wait = msecs_to_jiffies(10 * closing_wait); tport->tp_port = port; tport->tp_tdev = usb_get_serial_data(port->serial); @@ -624,7 +615,12 @@ static int ti_port_probe(struct usb_serial_port *port) usb_set_serial_port_data(port, tport); - port->port.drain_delay = 3; + /* + * The TUSB5052 LSR does not tell when the transmitter shift register + * has emptied so add a one-character drain delay. + */ + if (!tport->tp_tdev->td_is_3410) + port->port.drain_delay = 1; return 0; } @@ -643,7 +639,6 @@ static int ti_open(struct tty_struct *tty, struct usb_serial_port *port) struct ti_device *tdev; struct usb_device *dev; struct urb *urb; - int port_number; int status; u16 open_settings; @@ -658,8 +653,6 @@ static int ti_open(struct tty_struct *tty, struct usb_serial_port *port) if (mutex_lock_interruptible(&tdev->td_open_close_lock)) return -ERESTARTSYS; - port_number = port->port_number; - tport->tp_msr = 0; tport->tp_shadow_mcr |= (TI_MCR_RTS | TI_MCR_DTR); @@ -683,31 +676,27 @@ static int ti_open(struct tty_struct *tty, struct usb_serial_port *port) if (tty) ti_set_termios(tty, port, &tty->termios); - status = ti_command_out_sync(tdev, TI_OPEN_PORT, - (__u8)(TI_UART1_PORT + port_number), open_settings, NULL, 0); + status = ti_port_cmd_out(port, TI_OPEN_PORT, open_settings, NULL, 0); if (status) { dev_err(&port->dev, "%s - cannot send open command, %d\n", __func__, status); goto unlink_int_urb; } - status = ti_command_out_sync(tdev, TI_START_PORT, - (__u8)(TI_UART1_PORT + port_number), 0, NULL, 0); + status = ti_port_cmd_out(port, TI_START_PORT, 0, NULL, 0); if (status) { dev_err(&port->dev, "%s - cannot send start command, %d\n", __func__, status); goto unlink_int_urb; } - status = ti_command_out_sync(tdev, TI_PURGE_PORT, - (__u8)(TI_UART1_PORT + port_number), TI_PURGE_INPUT, NULL, 0); + status = ti_port_cmd_out(port, TI_PURGE_PORT, TI_PURGE_INPUT, NULL, 0); if (status) { dev_err(&port->dev, "%s - cannot clear input buffers, %d\n", __func__, status); goto unlink_int_urb; } - status = ti_command_out_sync(tdev, TI_PURGE_PORT, - (__u8)(TI_UART1_PORT + port_number), TI_PURGE_OUTPUT, NULL, 0); + status = ti_port_cmd_out(port, TI_PURGE_PORT, TI_PURGE_OUTPUT, NULL, 0); if (status) { dev_err(&port->dev, "%s - cannot clear output buffers, %d\n", __func__, status); @@ -722,16 +711,14 @@ static int ti_open(struct tty_struct *tty, struct usb_serial_port *port) if (tty) ti_set_termios(tty, port, &tty->termios); - status = ti_command_out_sync(tdev, TI_OPEN_PORT, - (__u8)(TI_UART1_PORT + port_number), open_settings, NULL, 0); + status = ti_port_cmd_out(port, TI_OPEN_PORT, open_settings, NULL, 0); if (status) { dev_err(&port->dev, "%s - cannot send open command (2), %d\n", __func__, status); goto unlink_int_urb; } - status = ti_command_out_sync(tdev, TI_START_PORT, - (__u8)(TI_UART1_PORT + port_number), 0, NULL, 0); + status = ti_port_cmd_out(port, TI_START_PORT, 0, NULL, 0); if (status) { dev_err(&port->dev, "%s - cannot send start command (2), %d\n", __func__, status); @@ -772,7 +759,6 @@ static void ti_close(struct usb_serial_port *port) { struct ti_device *tdev; struct ti_port *tport; - int port_number; int status; unsigned long flags; @@ -788,10 +774,7 @@ static void ti_close(struct usb_serial_port *port) kfifo_reset_out(&port->write_fifo); spin_unlock_irqrestore(&tport->tp_lock, flags); - port_number = port->port_number; - - status = ti_command_out_sync(tdev, TI_CLOSE_PORT, - (__u8)(TI_UART1_PORT + port_number), 0, NULL, 0); + status = ti_port_cmd_out(port, TI_CLOSE_PORT, 0, NULL, 0); if (status) dev_err(&port->dev, "%s - cannot send close port command, %d\n" @@ -861,11 +844,20 @@ static int ti_chars_in_buffer(struct tty_struct *tty) static bool ti_tx_empty(struct usb_serial_port *port) { struct ti_port *tport = usb_get_serial_port_data(port); + u8 lsr, mask; int ret; - u8 lsr; + + /* + * TUSB5052 does not have the TEMT bit to tell if the shift register + * is empty. + */ + if (tport->tp_tdev->td_is_3410) + mask = TI_LSR_TX_EMPTY_BOTH; + else + mask = TI_LSR_TX_EMPTY; ret = ti_get_lsr(tport, &lsr); - if (!ret && !(lsr & TI_LSR_TX_EMPTY)) + if (!ret && !(lsr & mask)) return false; return true; @@ -903,7 +895,6 @@ static void ti_set_termios(struct tty_struct *tty, struct ti_uart_config *config; int baud; int status; - int port_number = port->port_number; unsigned int mcr; u16 wbaudrate; u16 wflags = 0; @@ -919,18 +910,18 @@ static void ti_set_termios(struct tty_struct *tty, switch (C_CSIZE(tty)) { case CS5: - config->bDataBits = TI_UART_5_DATA_BITS; - break; + config->bDataBits = TI_UART_5_DATA_BITS; + break; case CS6: - config->bDataBits = TI_UART_6_DATA_BITS; - break; + config->bDataBits = TI_UART_6_DATA_BITS; + break; case CS7: - config->bDataBits = TI_UART_7_DATA_BITS; - break; + config->bDataBits = TI_UART_7_DATA_BITS; + break; default: case CS8: - config->bDataBits = TI_UART_8_DATA_BITS; - break; + config->bDataBits = TI_UART_8_DATA_BITS; + break; } /* CMSPAR isn't supported by this driver */ @@ -997,12 +988,11 @@ static void ti_set_termios(struct tty_struct *tty, config->wBaudRate = cpu_to_be16(wbaudrate); config->wFlags = cpu_to_be16(wflags); - status = ti_command_out_sync(tport->tp_tdev, TI_SET_CONFIG, - (__u8)(TI_UART1_PORT + port_number), 0, (__u8 *)config, - sizeof(*config)); + status = ti_port_cmd_out(port, TI_SET_CONFIG, 0, config, + sizeof(*config)); if (status) dev_err(&port->dev, "%s - cannot set config on port %d, %d\n", - __func__, port_number, status); + __func__, port->port_number, status); /* SET_CONFIG asserts RTS and DTR, reset them correctly */ mcr = tport->tp_shadow_mcr; @@ -1011,9 +1001,8 @@ static void ti_set_termios(struct tty_struct *tty, mcr &= ~(TI_MCR_DTR | TI_MCR_RTS); status = ti_set_mcr(tport, mcr); if (status) - dev_err(&port->dev, - "%s - cannot set modem control on port %d, %d\n", - __func__, port_number, status); + dev_err(&port->dev, "%s - cannot set modem control on port %d, %d\n", + __func__, port->port_number, status); kfree(config); } @@ -1364,9 +1353,7 @@ static int ti_set_mcr(struct ti_port *tport, unsigned int mcr) static int ti_get_lsr(struct ti_port *tport, u8 *lsr) { int size, status; - struct ti_device *tdev = tport->tp_tdev; struct usb_serial_port *port = tport->tp_port; - int port_number = port->port_number; struct ti_port_status *data; size = sizeof(struct ti_port_status); @@ -1374,8 +1361,7 @@ static int ti_get_lsr(struct ti_port *tport, u8 *lsr) if (!data) return -ENOMEM; - status = ti_command_in_sync(tdev, TI_GET_PORT_STATUS, - (__u8)(TI_UART1_PORT+port_number), 0, (__u8 *)data, size); + status = ti_port_cmd_in(port, TI_GET_PORT_STATUS, 0, data, size); if (status) { dev_err(&port->dev, "%s - get port status command failed, %d\n", @@ -1393,41 +1379,12 @@ static int ti_get_lsr(struct ti_port *tport, u8 *lsr) } -static int ti_get_serial_info(struct tty_struct *tty, - struct serial_struct *ss) +static void ti_get_serial_info(struct tty_struct *tty, struct serial_struct *ss) { struct usb_serial_port *port = tty->driver_data; struct ti_port *tport = usb_get_serial_port_data(port); - unsigned cwait; - cwait = port->port.closing_wait; - if (cwait != ASYNC_CLOSING_WAIT_NONE) - cwait = jiffies_to_msecs(cwait) / 10; - - ss->type = PORT_16550A; - ss->line = port->minor; - ss->port = port->port_number; - ss->xmit_fifo_size = kfifo_size(&port->write_fifo); ss->baud_base = tport->tp_tdev->td_is_3410 ? 921600 : 460800; - ss->closing_wait = cwait; - return 0; -} - - -static int ti_set_serial_info(struct tty_struct *tty, - struct serial_struct *ss) -{ - struct usb_serial_port *port = tty->driver_data; - struct ti_port *tport = usb_get_serial_port_data(port); - unsigned cwait; - - cwait = ss->closing_wait; - if (cwait != ASYNC_CLOSING_WAIT_NONE) - cwait = msecs_to_jiffies(10 * ss->closing_wait); - - tport->tp_port->port.closing_wait = cwait; - - return 0; } @@ -1501,34 +1458,28 @@ static int ti_restart_read(struct ti_port *tport, struct tty_struct *tty) return status; } - -static int ti_command_out_sync(struct ti_device *tdev, __u8 command, - __u16 moduleid, __u16 value, __u8 *data, int size) +static int ti_command_out_sync(struct usb_device *udev, u8 command, + u16 moduleid, u16 value, void *data, int size) { int status; - status = usb_control_msg(tdev->td_serial->dev, - usb_sndctrlpipe(tdev->td_serial->dev, 0), command, - (USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT), - value, moduleid, data, size, 1000); - + status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), command, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, + value, moduleid, data, size, 1000); if (status < 0) return status; return 0; } - -static int ti_command_in_sync(struct ti_device *tdev, __u8 command, - __u16 moduleid, __u16 value, __u8 *data, int size) +static int ti_command_in_sync(struct usb_device *udev, u8 command, + u16 moduleid, u16 value, void *data, int size) { int status; - status = usb_control_msg(tdev->td_serial->dev, - usb_rcvctrlpipe(tdev->td_serial->dev, 0), command, - (USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN), - value, moduleid, data, size, 1000); - + status = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), command, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + value, moduleid, data, size, 1000); if (status == size) status = 0; else if (status >= 0) @@ -1537,6 +1488,21 @@ static int ti_command_in_sync(struct ti_device *tdev, __u8 command, return status; } +static int ti_port_cmd_out(struct usb_serial_port *port, u8 command, + u16 value, void *data, int size) +{ + return ti_command_out_sync(port->serial->dev, command, + TI_UART1_PORT + port->port_number, + value, data, size); +} + +static int ti_port_cmd_in(struct usb_serial_port *port, u8 command, + u16 value, void *data, int size) +{ + return ti_command_in_sync(port->serial->dev, command, + TI_UART1_PORT + port->port_number, + value, data, size); +} static int ti_write_byte(struct usb_serial_port *port, struct ti_device *tdev, unsigned long addr, @@ -1562,9 +1528,8 @@ static int ti_write_byte(struct usb_serial_port *port, data->bData[0] = mask; data->bData[1] = byte; - status = ti_command_out_sync(tdev, TI_WRITE_DATA, TI_RAM_PORT, 0, - (__u8 *)data, size); - + status = ti_command_out_sync(port->serial->dev, TI_WRITE_DATA, + TI_RAM_PORT, 0, data, size); if (status < 0) dev_err(&port->dev, "%s - failed, %d\n", __func__, status); diff --git a/drivers/usb/serial/upd78f0730.c b/drivers/usb/serial/upd78f0730.c index 26d7b003b7e3..63d4a784ae45 100644 --- a/drivers/usb/serial/upd78f0730.c +++ b/drivers/usb/serial/upd78f0730.c @@ -182,7 +182,6 @@ static void upd78f0730_port_remove(struct usb_serial_port *port) static int upd78f0730_tiocmget(struct tty_struct *tty) { - struct device *dev = tty->dev; struct upd78f0730_port_private *private; struct usb_serial_port *port = tty->driver_data; int signals; @@ -197,7 +196,7 @@ static int upd78f0730_tiocmget(struct tty_struct *tty) res = ((signals & UPD78F0730_DTR) ? TIOCM_DTR : 0) | ((signals & UPD78F0730_RTS) ? TIOCM_RTS : 0); - dev_dbg(dev, "%s - res = %x\n", __func__, res); + dev_dbg(&port->dev, "%s - res = %x\n", __func__, res); return res; } @@ -205,10 +204,10 @@ static int upd78f0730_tiocmget(struct tty_struct *tty) static int upd78f0730_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { - struct device *dev = tty->dev; struct usb_serial_port *port = tty->driver_data; struct upd78f0730_port_private *private; struct upd78f0730_set_dtr_rts request; + struct device *dev = &port->dev; int res; private = usb_get_serial_port_data(port); @@ -241,10 +240,10 @@ static int upd78f0730_tiocmset(struct tty_struct *tty, static void upd78f0730_break_ctl(struct tty_struct *tty, int break_state) { - struct device *dev = tty->dev; struct upd78f0730_port_private *private; struct usb_serial_port *port = tty->driver_data; struct upd78f0730_set_dtr_rts request; + struct device *dev = &port->dev; private = usb_get_serial_port_data(port); diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 27e3bb58c872..98b33b1b5357 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -121,6 +121,44 @@ static void release_minors(struct usb_serial *serial) serial->minors_reserved = 0; } +int usb_serial_claim_interface(struct usb_serial *serial, struct usb_interface *intf) +{ + struct usb_driver *driver = serial->type->usb_driver; + int ret; + + if (serial->sibling) + return -EBUSY; + + ret = usb_driver_claim_interface(driver, intf, serial); + if (ret) { + dev_err(&serial->interface->dev, + "failed to claim sibling interface: %d\n", ret); + return ret; + } + + serial->sibling = intf; + + return 0; +} +EXPORT_SYMBOL_GPL(usb_serial_claim_interface); + +static void release_sibling(struct usb_serial *serial, struct usb_interface *intf) +{ + struct usb_driver *driver = serial->type->usb_driver; + struct usb_interface *sibling; + + if (!serial->sibling) + return; + + if (intf == serial->sibling) + sibling = serial->interface; + else + sibling = serial->sibling; + + usb_set_intfdata(sibling, NULL); + usb_driver_release_interface(driver, sibling); +} + static void destroy_serial(struct kref *kref) { struct usb_serial *serial; @@ -243,7 +281,7 @@ static int serial_open(struct tty_struct *tty, struct file *filp) { struct usb_serial_port *port = tty->driver_data; - dev_dbg(tty->dev, "%s\n", __func__); + dev_dbg(&port->dev, "%s\n", __func__); return tty_port_open(&port->port, tty, filp); } @@ -272,7 +310,7 @@ static void serial_hangup(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; - dev_dbg(tty->dev, "%s\n", __func__); + dev_dbg(&port->dev, "%s\n", __func__); tty_port_hangup(&port->port); } @@ -281,7 +319,7 @@ static void serial_close(struct tty_struct *tty, struct file *filp) { struct usb_serial_port *port = tty->driver_data; - dev_dbg(tty->dev, "%s\n", __func__); + dev_dbg(&port->dev, "%s\n", __func__); tty_port_close(&port->port, tty, filp); } @@ -301,7 +339,7 @@ static void serial_cleanup(struct tty_struct *tty) struct usb_serial *serial; struct module *owner; - dev_dbg(tty->dev, "%s\n", __func__); + dev_dbg(&port->dev, "%s\n", __func__); /* The console is magical. Do not hang up the console hardware * or there will be tears. @@ -329,7 +367,7 @@ static int serial_write(struct tty_struct *tty, const unsigned char *buf, if (port->serial->dev->state == USB_STATE_NOTATTACHED) goto exit; - dev_dbg(tty->dev, "%s - %d byte(s)\n", __func__, count); + dev_dbg(&port->dev, "%s - %d byte(s)\n", __func__, count); retval = port->serial->type->write(tty, port, buf, count); if (retval < 0) @@ -342,7 +380,7 @@ static int serial_write_room(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; - dev_dbg(tty->dev, "%s\n", __func__); + dev_dbg(&port->dev, "%s\n", __func__); return port->serial->type->write_room(tty); } @@ -352,7 +390,7 @@ static int serial_chars_in_buffer(struct tty_struct *tty) struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; - dev_dbg(tty->dev, "%s\n", __func__); + dev_dbg(&port->dev, "%s\n", __func__); if (serial->disconnected) return 0; @@ -365,7 +403,7 @@ static void serial_wait_until_sent(struct tty_struct *tty, int timeout) struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; - dev_dbg(tty->dev, "%s\n", __func__); + dev_dbg(&port->dev, "%s\n", __func__); if (!port->serial->type->wait_until_sent) return; @@ -380,7 +418,7 @@ static void serial_throttle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; - dev_dbg(tty->dev, "%s\n", __func__); + dev_dbg(&port->dev, "%s\n", __func__); if (port->serial->type->throttle) port->serial->type->throttle(tty); @@ -390,7 +428,7 @@ static void serial_unthrottle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; - dev_dbg(tty->dev, "%s\n", __func__); + dev_dbg(&port->dev, "%s\n", __func__); if (port->serial->type->unthrottle) port->serial->type->unthrottle(tty); @@ -399,19 +437,62 @@ static void serial_unthrottle(struct tty_struct *tty) static int serial_get_serial(struct tty_struct *tty, struct serial_struct *ss) { struct usb_serial_port *port = tty->driver_data; + struct tty_port *tport = &port->port; + unsigned int close_delay, closing_wait; + + mutex_lock(&tport->mutex); + + close_delay = jiffies_to_msecs(tport->close_delay) / 10; + closing_wait = tport->closing_wait; + if (closing_wait != ASYNC_CLOSING_WAIT_NONE) + closing_wait = jiffies_to_msecs(closing_wait) / 10; + + ss->line = port->minor; + ss->close_delay = close_delay; + ss->closing_wait = closing_wait; if (port->serial->type->get_serial) - return port->serial->type->get_serial(tty, ss); - return -ENOTTY; + port->serial->type->get_serial(tty, ss); + + mutex_unlock(&tport->mutex); + + return 0; } static int serial_set_serial(struct tty_struct *tty, struct serial_struct *ss) { struct usb_serial_port *port = tty->driver_data; + struct tty_port *tport = &port->port; + unsigned int close_delay, closing_wait; + int ret = 0; - if (port->serial->type->set_serial) - return port->serial->type->set_serial(tty, ss); - return -ENOTTY; + close_delay = msecs_to_jiffies(ss->close_delay * 10); + closing_wait = ss->closing_wait; + if (closing_wait != ASYNC_CLOSING_WAIT_NONE) + closing_wait = msecs_to_jiffies(closing_wait * 10); + + mutex_lock(&tport->mutex); + + if (!capable(CAP_SYS_ADMIN)) { + if (close_delay != tport->close_delay || + closing_wait != tport->closing_wait) { + ret = -EPERM; + goto out_unlock; + } + } + + if (port->serial->type->set_serial) { + ret = port->serial->type->set_serial(tty, ss); + if (ret) + goto out_unlock; + } + + tport->close_delay = close_delay; + tport->closing_wait = closing_wait; +out_unlock: + mutex_unlock(&tport->mutex); + + return ret; } static int serial_ioctl(struct tty_struct *tty, @@ -420,7 +501,7 @@ static int serial_ioctl(struct tty_struct *tty, struct usb_serial_port *port = tty->driver_data; int retval = -ENOIOCTLCMD; - dev_dbg(tty->dev, "%s - cmd 0x%04x\n", __func__, cmd); + dev_dbg(&port->dev, "%s - cmd 0x%04x\n", __func__, cmd); switch (cmd) { case TIOCMIWAIT: @@ -439,7 +520,7 @@ static void serial_set_termios(struct tty_struct *tty, struct ktermios *old) { struct usb_serial_port *port = tty->driver_data; - dev_dbg(tty->dev, "%s\n", __func__); + dev_dbg(&port->dev, "%s\n", __func__); if (port->serial->type->set_termios) port->serial->type->set_termios(tty, port, old); @@ -451,7 +532,7 @@ static int serial_break(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; - dev_dbg(tty->dev, "%s\n", __func__); + dev_dbg(&port->dev, "%s\n", __func__); if (port->serial->type->break_ctl) port->serial->type->break_ctl(tty, break_state); @@ -498,11 +579,11 @@ static int serial_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; - dev_dbg(tty->dev, "%s\n", __func__); + dev_dbg(&port->dev, "%s\n", __func__); if (port->serial->type->tiocmget) return port->serial->type->tiocmget(tty); - return -EINVAL; + return -ENOTTY; } static int serial_tiocmset(struct tty_struct *tty, @@ -510,11 +591,11 @@ static int serial_tiocmset(struct tty_struct *tty, { struct usb_serial_port *port = tty->driver_data; - dev_dbg(tty->dev, "%s\n", __func__); + dev_dbg(&port->dev, "%s\n", __func__); if (port->serial->type->tiocmset) return port->serial->type->tiocmset(tty, set, clear); - return -EINVAL; + return -ENOTTY; } static int serial_get_icount(struct tty_struct *tty, @@ -522,11 +603,11 @@ static int serial_get_icount(struct tty_struct *tty, { struct usb_serial_port *port = tty->driver_data; - dev_dbg(tty->dev, "%s\n", __func__); + dev_dbg(&port->dev, "%s\n", __func__); if (port->serial->type->get_icount) return port->serial->type->get_icount(tty, icount); - return -EINVAL; + return -ENOTTY; } /* @@ -711,36 +792,48 @@ static const struct tty_port_operations serial_port_ops = { .shutdown = serial_port_shutdown, }; -static void find_endpoints(struct usb_serial *serial, - struct usb_serial_endpoints *epds) +static void store_endpoint(struct usb_serial *serial, + struct usb_serial_endpoints *epds, + struct usb_endpoint_descriptor *epd) { struct device *dev = &serial->interface->dev; + u8 addr = epd->bEndpointAddress; + + if (usb_endpoint_is_bulk_in(epd)) { + if (epds->num_bulk_in == ARRAY_SIZE(epds->bulk_in)) + return; + dev_dbg(dev, "found bulk in endpoint %02x\n", addr); + epds->bulk_in[epds->num_bulk_in++] = epd; + } else if (usb_endpoint_is_bulk_out(epd)) { + if (epds->num_bulk_out == ARRAY_SIZE(epds->bulk_out)) + return; + dev_dbg(dev, "found bulk out endpoint %02x\n", addr); + epds->bulk_out[epds->num_bulk_out++] = epd; + } else if (usb_endpoint_is_int_in(epd)) { + if (epds->num_interrupt_in == ARRAY_SIZE(epds->interrupt_in)) + return; + dev_dbg(dev, "found interrupt in endpoint %02x\n", addr); + epds->interrupt_in[epds->num_interrupt_in++] = epd; + } else if (usb_endpoint_is_int_out(epd)) { + if (epds->num_interrupt_out == ARRAY_SIZE(epds->interrupt_out)) + return; + dev_dbg(dev, "found interrupt out endpoint %02x\n", addr); + epds->interrupt_out[epds->num_interrupt_out++] = epd; + } +} + +static void find_endpoints(struct usb_serial *serial, + struct usb_serial_endpoints *epds, + struct usb_interface *intf) +{ struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *epd; unsigned int i; - BUILD_BUG_ON(ARRAY_SIZE(epds->bulk_in) < USB_MAXENDPOINTS / 2); - BUILD_BUG_ON(ARRAY_SIZE(epds->bulk_out) < USB_MAXENDPOINTS / 2); - BUILD_BUG_ON(ARRAY_SIZE(epds->interrupt_in) < USB_MAXENDPOINTS / 2); - BUILD_BUG_ON(ARRAY_SIZE(epds->interrupt_out) < USB_MAXENDPOINTS / 2); - - iface_desc = serial->interface->cur_altsetting; + iface_desc = intf->cur_altsetting; for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { epd = &iface_desc->endpoint[i].desc; - - if (usb_endpoint_is_bulk_in(epd)) { - dev_dbg(dev, "found bulk in on endpoint %u\n", i); - epds->bulk_in[epds->num_bulk_in++] = epd; - } else if (usb_endpoint_is_bulk_out(epd)) { - dev_dbg(dev, "found bulk out on endpoint %u\n", i); - epds->bulk_out[epds->num_bulk_out++] = epd; - } else if (usb_endpoint_is_int_in(epd)) { - dev_dbg(dev, "found interrupt in on endpoint %u\n", i); - epds->interrupt_in[epds->num_interrupt_in++] = epd; - } else if (usb_endpoint_is_int_out(epd)) { - dev_dbg(dev, "found interrupt out on endpoint %u\n", i); - epds->interrupt_out[epds->num_interrupt_out++] = epd; - } + store_endpoint(serial, epds, epd); } } @@ -906,7 +999,7 @@ static int usb_serial_probe(struct usb_interface *interface, if (retval) { dev_dbg(ddev, "sub driver rejected device\n"); - goto err_put_serial; + goto err_release_sibling; } } @@ -914,10 +1007,12 @@ static int usb_serial_probe(struct usb_interface *interface, epds = kzalloc(sizeof(*epds), GFP_KERNEL); if (!epds) { retval = -ENOMEM; - goto err_put_serial; + goto err_release_sibling; } - find_endpoints(serial, epds); + find_endpoints(serial, epds, interface); + if (serial->sibling) + find_endpoints(serial, epds, serial->sibling); if (epds->num_bulk_in < type->num_bulk_in || epds->num_bulk_out < type->num_bulk_out || @@ -1065,7 +1160,8 @@ static int usb_serial_probe(struct usb_interface *interface, err_free_epds: kfree(epds); -err_put_serial: +err_release_sibling: + release_sibling(serial, interface); usb_serial_put(serial); err_put_module: module_put(type->driver.owner); @@ -1081,6 +1177,10 @@ static void usb_serial_disconnect(struct usb_interface *interface) struct usb_serial_port *port; struct tty_struct *tty; + /* sibling interface is cleaning up */ + if (!serial) + return; + usb_serial_console_disconnect(serial); mutex_lock(&serial->disc_mutex); @@ -1104,6 +1204,8 @@ static void usb_serial_disconnect(struct usb_interface *interface) if (serial->type->disconnect) serial->type->disconnect(serial); + release_sibling(serial, interface); + /* let the last holder of this object cause it to be cleaned up */ usb_serial_put(serial); dev_info(dev, "device disconnected\n"); @@ -1112,9 +1214,11 @@ static void usb_serial_disconnect(struct usb_interface *interface) int usb_serial_suspend(struct usb_interface *intf, pm_message_t message) { struct usb_serial *serial = usb_get_intfdata(intf); - int i, r = 0; + int i, r; - serial->suspending = 1; + /* suspend when called for first sibling interface */ + if (serial->suspend_count++) + return 0; /* * serial->type->suspend() MUST return 0 in system sleep context, @@ -1124,15 +1228,15 @@ int usb_serial_suspend(struct usb_interface *intf, pm_message_t message) if (serial->type->suspend) { r = serial->type->suspend(serial, message); if (r < 0) { - serial->suspending = 0; - goto err_out; + serial->suspend_count--; + return r; } } for (i = 0; i < serial->num_ports; ++i) usb_serial_port_poison_urbs(serial->port[i]); -err_out: - return r; + + return 0; } EXPORT_SYMBOL(usb_serial_suspend); @@ -1149,9 +1253,12 @@ int usb_serial_resume(struct usb_interface *intf) struct usb_serial *serial = usb_get_intfdata(intf); int rv; + /* resume when called for last sibling interface */ + if (--serial->suspend_count) + return 0; + usb_serial_unpoison_port_urbs(serial); - serial->suspending = 0; if (serial->type->resume) rv = serial->type->resume(serial); else @@ -1166,9 +1273,12 @@ static int usb_serial_reset_resume(struct usb_interface *intf) struct usb_serial *serial = usb_get_intfdata(intf); int rv; + /* resume when called for last sibling interface */ + if (--serial->suspend_count) + return 0; + usb_serial_unpoison_port_urbs(serial); - serial->suspending = 0; if (serial->type->reset_resume) { rv = serial->type->reset_resume(serial); } else { diff --git a/drivers/usb/serial/usb-wwan.h b/drivers/usb/serial/usb-wwan.h index 79dafd98e0a1..b5331d03092f 100644 --- a/drivers/usb/serial/usb-wwan.h +++ b/drivers/usb/serial/usb-wwan.h @@ -15,10 +15,6 @@ extern int usb_wwan_write_room(struct tty_struct *tty); extern int usb_wwan_tiocmget(struct tty_struct *tty); extern int usb_wwan_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); -extern int usb_wwan_get_serial_info(struct tty_struct *tty, - struct serial_struct *ss); -extern int usb_wwan_set_serial_info(struct tty_struct *tty, - struct serial_struct *ss); extern int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); extern int usb_wwan_chars_in_buffer(struct tty_struct *tty); diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c index 46d46a4f99c9..3eb72c59ede6 100644 --- a/drivers/usb/serial/usb_wwan.c +++ b/drivers/usb/serial/usb_wwan.c @@ -132,51 +132,6 @@ int usb_wwan_tiocmset(struct tty_struct *tty, } EXPORT_SYMBOL(usb_wwan_tiocmset); -int usb_wwan_get_serial_info(struct tty_struct *tty, - struct serial_struct *ss) -{ - struct usb_serial_port *port = tty->driver_data; - - ss->line = port->minor; - ss->port = port->port_number; - ss->baud_base = tty_get_baud_rate(port->port.tty); - ss->close_delay = port->port.close_delay / 10; - ss->closing_wait = port->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ? - ASYNC_CLOSING_WAIT_NONE : - port->port.closing_wait / 10; - return 0; -} -EXPORT_SYMBOL(usb_wwan_get_serial_info); - -int usb_wwan_set_serial_info(struct tty_struct *tty, - struct serial_struct *ss) -{ - struct usb_serial_port *port = tty->driver_data; - unsigned int closing_wait, close_delay; - int retval = 0; - - close_delay = ss->close_delay * 10; - closing_wait = ss->closing_wait == ASYNC_CLOSING_WAIT_NONE ? - ASYNC_CLOSING_WAIT_NONE : ss->closing_wait * 10; - - mutex_lock(&port->port.mutex); - - if (!capable(CAP_SYS_ADMIN)) { - if ((close_delay != port->port.close_delay) || - (closing_wait != port->port.closing_wait)) - retval = -EPERM; - else - retval = -EOPNOTSUPP; - } else { - port->port.close_delay = close_delay; - port->port.closing_wait = closing_wait; - } - - mutex_unlock(&port->port.mutex); - return retval; -} -EXPORT_SYMBOL(usb_wwan_set_serial_info); - int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) { diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index ccfd5ed652cd..5116ed9db3eb 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -83,7 +83,7 @@ static void whiteheat_port_remove(struct usb_serial_port *port); static int whiteheat_open(struct tty_struct *tty, struct usb_serial_port *port); static void whiteheat_close(struct usb_serial_port *port); -static int whiteheat_get_serial(struct tty_struct *tty, +static void whiteheat_get_serial(struct tty_struct *tty, struct serial_struct *ss); static void whiteheat_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old); @@ -170,7 +170,6 @@ static int firm_report_tx_done(struct usb_serial_port *port); #define COMMAND_PORT 4 #define COMMAND_TIMEOUT (2*HZ) /* 2 second timeout for a command */ #define COMMAND_TIMEOUT_MS 2000 -#define CLOSING_DELAY (30 * HZ) /***************************************************************************** @@ -440,21 +439,9 @@ static int whiteheat_tiocmset(struct tty_struct *tty, } -static int whiteheat_get_serial(struct tty_struct *tty, - struct serial_struct *ss) +static void whiteheat_get_serial(struct tty_struct *tty, struct serial_struct *ss) { - struct usb_serial_port *port = tty->driver_data; - - ss->type = PORT_16654; - ss->line = port->minor; - ss->port = port->port_number; - ss->xmit_fifo_size = kfifo_size(&port->write_fifo); - ss->custom_divisor = 0; ss->baud_base = 460800; - ss->close_delay = CLOSING_DELAY; - ss->closing_wait = CLOSING_DELAY; - - return 0; } diff --git a/drivers/usb/serial/xr_serial.c b/drivers/usb/serial/xr_serial.c index 0ca04906da4b..6853cd56d8dc 100644 --- a/drivers/usb/serial/xr_serial.c +++ b/drivers/usb/serial/xr_serial.c @@ -3,6 +3,7 @@ * MaxLinear/Exar USB to Serial driver * * Copyright (c) 2020 Manivannan Sadhasivam + * Copyright (c) 2021 Johan Hovold * * Based on the initial driver written by Patong Yang: * @@ -16,6 +17,7 @@ #include #include #include +#include #include struct xr_txrx_clk_mask { @@ -28,10 +30,12 @@ struct xr_txrx_clk_mask { #define XR21V141X_MIN_SPEED 46U #define XR21V141X_MAX_SPEED XR_INT_OSC_HZ -/* USB Requests */ -#define XR21V141X_SET_REQ 0 -#define XR21V141X_GET_REQ 1 +/* XR21V141X register blocks */ +#define XR21V141X_UART_REG_BLOCK 0 +#define XR21V141X_UM_REG_BLOCK 4 +#define XR21V141X_UART_CUSTOM_BLOCK 0x66 +/* XR21V141X UART registers */ #define XR21V141X_CLOCK_DIVISOR_0 0x04 #define XR21V141X_CLOCK_DIVISOR_1 0x05 #define XR21V141X_CLOCK_DIVISOR_2 0x06 @@ -39,13 +43,9 @@ struct xr_txrx_clk_mask { #define XR21V141X_TX_CLOCK_MASK_1 0x08 #define XR21V141X_RX_CLOCK_MASK_0 0x09 #define XR21V141X_RX_CLOCK_MASK_1 0x0a +#define XR21V141X_REG_FORMAT 0x0b -/* XR21V141X register blocks */ -#define XR21V141X_UART_REG_BLOCK 0 -#define XR21V141X_UM_REG_BLOCK 4 -#define XR21V141X_UART_CUSTOM_BLOCK 0x66 - -/* XR21V141X UART Manager Registers */ +/* XR21V141X UART Manager registers */ #define XR21V141X_UM_FIFO_ENABLE_REG 0x10 #define XR21V141X_UM_ENABLE_TX_FIFO 0x01 #define XR21V141X_UM_ENABLE_RX_FIFO 0x02 @@ -53,72 +53,203 @@ struct xr_txrx_clk_mask { #define XR21V141X_UM_RX_FIFO_RESET 0x18 #define XR21V141X_UM_TX_FIFO_RESET 0x1c -#define XR21V141X_UART_ENABLE_TX 0x1 -#define XR21V141X_UART_ENABLE_RX 0x2 +#define XR_UART_ENABLE_TX 0x1 +#define XR_UART_ENABLE_RX 0x2 -#define XR21V141X_UART_MODE_RI BIT(0) -#define XR21V141X_UART_MODE_CD BIT(1) -#define XR21V141X_UART_MODE_DSR BIT(2) -#define XR21V141X_UART_MODE_DTR BIT(3) -#define XR21V141X_UART_MODE_CTS BIT(4) -#define XR21V141X_UART_MODE_RTS BIT(5) +#define XR_GPIO_RI BIT(0) +#define XR_GPIO_CD BIT(1) +#define XR_GPIO_DSR BIT(2) +#define XR_GPIO_DTR BIT(3) +#define XR_GPIO_CTS BIT(4) +#define XR_GPIO_RTS BIT(5) +#define XR_GPIO_CLK BIT(6) +#define XR_GPIO_XEN BIT(7) +#define XR_GPIO_TXT BIT(8) +#define XR_GPIO_RXT BIT(9) -#define XR21V141X_UART_BREAK_ON 0xff -#define XR21V141X_UART_BREAK_OFF 0 +#define XR_UART_DATA_MASK GENMASK(3, 0) +#define XR_UART_DATA_7 0x7 +#define XR_UART_DATA_8 0x8 -#define XR21V141X_UART_DATA_MASK GENMASK(3, 0) -#define XR21V141X_UART_DATA_7 0x7 -#define XR21V141X_UART_DATA_8 0x8 +#define XR_UART_PARITY_MASK GENMASK(6, 4) +#define XR_UART_PARITY_SHIFT 4 +#define XR_UART_PARITY_NONE (0x0 << XR_UART_PARITY_SHIFT) +#define XR_UART_PARITY_ODD (0x1 << XR_UART_PARITY_SHIFT) +#define XR_UART_PARITY_EVEN (0x2 << XR_UART_PARITY_SHIFT) +#define XR_UART_PARITY_MARK (0x3 << XR_UART_PARITY_SHIFT) +#define XR_UART_PARITY_SPACE (0x4 << XR_UART_PARITY_SHIFT) -#define XR21V141X_UART_PARITY_MASK GENMASK(6, 4) -#define XR21V141X_UART_PARITY_SHIFT 4 -#define XR21V141X_UART_PARITY_NONE (0x0 << XR21V141X_UART_PARITY_SHIFT) -#define XR21V141X_UART_PARITY_ODD (0x1 << XR21V141X_UART_PARITY_SHIFT) -#define XR21V141X_UART_PARITY_EVEN (0x2 << XR21V141X_UART_PARITY_SHIFT) -#define XR21V141X_UART_PARITY_MARK (0x3 << XR21V141X_UART_PARITY_SHIFT) -#define XR21V141X_UART_PARITY_SPACE (0x4 << XR21V141X_UART_PARITY_SHIFT) +#define XR_UART_STOP_MASK BIT(7) +#define XR_UART_STOP_SHIFT 7 +#define XR_UART_STOP_1 (0x0 << XR_UART_STOP_SHIFT) +#define XR_UART_STOP_2 (0x1 << XR_UART_STOP_SHIFT) -#define XR21V141X_UART_STOP_MASK BIT(7) -#define XR21V141X_UART_STOP_SHIFT 7 -#define XR21V141X_UART_STOP_1 (0x0 << XR21V141X_UART_STOP_SHIFT) -#define XR21V141X_UART_STOP_2 (0x1 << XR21V141X_UART_STOP_SHIFT) +#define XR_UART_FLOW_MODE_NONE 0x0 +#define XR_UART_FLOW_MODE_HW 0x1 +#define XR_UART_FLOW_MODE_SW 0x2 -#define XR21V141X_UART_FLOW_MODE_NONE 0x0 -#define XR21V141X_UART_FLOW_MODE_HW 0x1 -#define XR21V141X_UART_FLOW_MODE_SW 0x2 +#define XR_GPIO_MODE_SEL_MASK GENMASK(2, 0) +#define XR_GPIO_MODE_SEL_RTS_CTS 0x1 +#define XR_GPIO_MODE_SEL_DTR_DSR 0x2 +#define XR_GPIO_MODE_SEL_RS485 0x3 +#define XR_GPIO_MODE_SEL_RS485_ADDR 0x4 +#define XR_GPIO_MODE_TX_TOGGLE 0x100 +#define XR_GPIO_MODE_RX_TOGGLE 0x200 -#define XR21V141X_UART_MODE_GPIO_MASK GENMASK(2, 0) -#define XR21V141X_UART_MODE_RTS_CTS 0x1 -#define XR21V141X_UART_MODE_DTR_DSR 0x2 -#define XR21V141X_UART_MODE_RS485 0x3 -#define XR21V141X_UART_MODE_RS485_ADDR 0x4 +#define XR_FIFO_RESET 0x1 -#define XR21V141X_REG_ENABLE 0x03 -#define XR21V141X_REG_FORMAT 0x0b -#define XR21V141X_REG_FLOW_CTRL 0x0c -#define XR21V141X_REG_XON_CHAR 0x10 -#define XR21V141X_REG_XOFF_CHAR 0x11 -#define XR21V141X_REG_LOOPBACK 0x12 -#define XR21V141X_REG_TX_BREAK 0x14 -#define XR21V141X_REG_RS845_DELAY 0x15 -#define XR21V141X_REG_GPIO_MODE 0x1a -#define XR21V141X_REG_GPIO_DIR 0x1b -#define XR21V141X_REG_GPIO_INT_MASK 0x1c -#define XR21V141X_REG_GPIO_SET 0x1d -#define XR21V141X_REG_GPIO_CLR 0x1e -#define XR21V141X_REG_GPIO_STATUS 0x1f +#define XR_CUSTOM_DRIVER_ACTIVE 0x1 -static int xr_set_reg(struct usb_serial_port *port, u8 block, u8 reg, u8 val) +static int xr21v141x_uart_enable(struct usb_serial_port *port); +static int xr21v141x_uart_disable(struct usb_serial_port *port); +static int xr21v141x_fifo_reset(struct usb_serial_port *port); +static void xr21v141x_set_line_settings(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios); + +struct xr_type { + int reg_width; + u8 reg_recipient; + u8 set_reg; + u8 get_reg; + + u16 uart_enable; + u16 flow_control; + u16 xon_char; + u16 xoff_char; + u16 tx_break; + u16 gpio_mode; + u16 gpio_direction; + u16 gpio_set; + u16 gpio_clear; + u16 gpio_status; + u16 tx_fifo_reset; + u16 rx_fifo_reset; + u16 custom_driver; + + bool have_5_6_bit_mode; + bool have_xmit_toggle; + + int (*enable)(struct usb_serial_port *port); + int (*disable)(struct usb_serial_port *port); + int (*fifo_reset)(struct usb_serial_port *port); + void (*set_line_settings)(struct tty_struct *tty, + struct usb_serial_port *port, + struct ktermios *old_termios); +}; + +enum xr_type_id { + XR21V141X, + XR21B142X, + XR21B1411, + XR2280X, + XR_TYPE_COUNT, +}; + +static const struct xr_type xr_types[] = { + [XR21V141X] = { + .reg_width = 8, + .reg_recipient = USB_RECIP_DEVICE, + .set_reg = 0x00, + .get_reg = 0x01, + + .uart_enable = 0x03, + .flow_control = 0x0c, + .xon_char = 0x10, + .xoff_char = 0x11, + .tx_break = 0x14, + .gpio_mode = 0x1a, + .gpio_direction = 0x1b, + .gpio_set = 0x1d, + .gpio_clear = 0x1e, + .gpio_status = 0x1f, + + .enable = xr21v141x_uart_enable, + .disable = xr21v141x_uart_disable, + .fifo_reset = xr21v141x_fifo_reset, + .set_line_settings = xr21v141x_set_line_settings, + }, + [XR21B142X] = { + .reg_width = 16, + .reg_recipient = USB_RECIP_INTERFACE, + .set_reg = 0x00, + .get_reg = 0x00, + + .uart_enable = 0x00, + .flow_control = 0x06, + .xon_char = 0x07, + .xoff_char = 0x08, + .tx_break = 0x0a, + .gpio_mode = 0x0c, + .gpio_direction = 0x0d, + .gpio_set = 0x0e, + .gpio_clear = 0x0f, + .gpio_status = 0x10, + .tx_fifo_reset = 0x40, + .rx_fifo_reset = 0x43, + .custom_driver = 0x60, + + .have_5_6_bit_mode = true, + .have_xmit_toggle = true, + }, + [XR21B1411] = { + .reg_width = 12, + .reg_recipient = USB_RECIP_DEVICE, + .set_reg = 0x00, + .get_reg = 0x01, + + .uart_enable = 0xc00, + .flow_control = 0xc06, + .xon_char = 0xc07, + .xoff_char = 0xc08, + .tx_break = 0xc0a, + .gpio_mode = 0xc0c, + .gpio_direction = 0xc0d, + .gpio_set = 0xc0e, + .gpio_clear = 0xc0f, + .gpio_status = 0xc10, + .tx_fifo_reset = 0xc80, + .rx_fifo_reset = 0xcc0, + .custom_driver = 0x20d, + }, + [XR2280X] = { + .reg_width = 16, + .reg_recipient = USB_RECIP_DEVICE, + .set_reg = 0x05, + .get_reg = 0x05, + + .uart_enable = 0x40, + .flow_control = 0x46, + .xon_char = 0x47, + .xoff_char = 0x48, + .tx_break = 0x4a, + .gpio_mode = 0x4c, + .gpio_direction = 0x4d, + .gpio_set = 0x4e, + .gpio_clear = 0x4f, + .gpio_status = 0x50, + .tx_fifo_reset = 0x60, + .rx_fifo_reset = 0x63, + .custom_driver = 0x81, + }, +}; + +struct xr_data { + const struct xr_type *type; + u8 channel; /* zero-based index or interface number */ +}; + +static int xr_set_reg(struct usb_serial_port *port, u8 channel, u16 reg, u16 val) { + struct xr_data *data = usb_get_serial_port_data(port); + const struct xr_type *type = data->type; struct usb_serial *serial = port->serial; int ret; - ret = usb_control_msg(serial->dev, - usb_sndctrlpipe(serial->dev, 0), - XR21V141X_SET_REQ, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - val, reg | (block << 8), NULL, 0, - USB_CTRL_SET_TIMEOUT); + ret = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + type->set_reg, + USB_DIR_OUT | USB_TYPE_VENDOR | type->reg_recipient, + val, (channel << 8) | reg, NULL, 0, + USB_CTRL_SET_TIMEOUT); if (ret < 0) { dev_err(&port->dev, "Failed to set reg 0x%02x: %d\n", reg, ret); return ret; @@ -127,24 +258,33 @@ static int xr_set_reg(struct usb_serial_port *port, u8 block, u8 reg, u8 val) return 0; } -static int xr_get_reg(struct usb_serial_port *port, u8 block, u8 reg, u8 *val) +static int xr_get_reg(struct usb_serial_port *port, u8 channel, u16 reg, u16 *val) { + struct xr_data *data = usb_get_serial_port_data(port); + const struct xr_type *type = data->type; struct usb_serial *serial = port->serial; u8 *dmabuf; - int ret; + int ret, len; - dmabuf = kmalloc(1, GFP_KERNEL); + if (type->reg_width == 8) + len = 1; + else + len = 2; + + dmabuf = kmalloc(len, GFP_KERNEL); if (!dmabuf) return -ENOMEM; - ret = usb_control_msg(serial->dev, - usb_rcvctrlpipe(serial->dev, 0), - XR21V141X_GET_REQ, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, reg | (block << 8), dmabuf, 1, - USB_CTRL_GET_TIMEOUT); - if (ret == 1) { - *val = *dmabuf; + ret = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), + type->get_reg, + USB_DIR_IN | USB_TYPE_VENDOR | type->reg_recipient, + 0, (channel << 8) | reg, dmabuf, len, + USB_CTRL_GET_TIMEOUT); + if (ret == len) { + if (len == 2) + *val = le16_to_cpup((__le16 *)dmabuf); + else + *val = *dmabuf; ret = 0; } else { dev_err(&port->dev, "Failed to get reg 0x%02x: %d\n", reg, ret); @@ -157,21 +297,45 @@ static int xr_get_reg(struct usb_serial_port *port, u8 block, u8 reg, u8 *val) return ret; } -static int xr_set_reg_uart(struct usb_serial_port *port, u8 reg, u8 val) +static int xr_set_reg_uart(struct usb_serial_port *port, u16 reg, u16 val) { - return xr_set_reg(port, XR21V141X_UART_REG_BLOCK, reg, val); + struct xr_data *data = usb_get_serial_port_data(port); + + return xr_set_reg(port, data->channel, reg, val); } -static int xr_get_reg_uart(struct usb_serial_port *port, u8 reg, u8 *val) +static int xr_get_reg_uart(struct usb_serial_port *port, u16 reg, u16 *val) { - return xr_get_reg(port, XR21V141X_UART_REG_BLOCK, reg, val); + struct xr_data *data = usb_get_serial_port_data(port); + + return xr_get_reg(port, data->channel, reg, val); } -static int xr_set_reg_um(struct usb_serial_port *port, u8 reg, u8 val) +static int xr_set_reg_um(struct usb_serial_port *port, u8 reg_base, u8 val) { + struct xr_data *data = usb_get_serial_port_data(port); + u8 reg; + + reg = reg_base + data->channel; + return xr_set_reg(port, XR21V141X_UM_REG_BLOCK, reg, val); } +static int __xr_uart_enable(struct usb_serial_port *port) +{ + struct xr_data *data = usb_get_serial_port_data(port); + + return xr_set_reg_uart(port, data->type->uart_enable, + XR_UART_ENABLE_TX | XR_UART_ENABLE_RX); +} + +static int __xr_uart_disable(struct usb_serial_port *port) +{ + struct xr_data *data = usb_get_serial_port_data(port); + + return xr_set_reg_uart(port, data->type->uart_enable, 0); +} + /* * According to datasheet, below is the recommended sequence for enabling UART * module in XR21V141X: @@ -180,7 +344,7 @@ static int xr_set_reg_um(struct usb_serial_port *port, u8 reg, u8 val) * Enable Tx and Rx * Enable Rx FIFO */ -static int xr_uart_enable(struct usb_serial_port *port) +static int xr21v141x_uart_enable(struct usb_serial_port *port) { int ret; @@ -189,25 +353,23 @@ static int xr_uart_enable(struct usb_serial_port *port) if (ret) return ret; - ret = xr_set_reg_uart(port, XR21V141X_REG_ENABLE, - XR21V141X_UART_ENABLE_TX | XR21V141X_UART_ENABLE_RX); + ret = __xr_uart_enable(port); if (ret) return ret; ret = xr_set_reg_um(port, XR21V141X_UM_FIFO_ENABLE_REG, XR21V141X_UM_ENABLE_TX_FIFO | XR21V141X_UM_ENABLE_RX_FIFO); - if (ret) - xr_set_reg_uart(port, XR21V141X_REG_ENABLE, 0); + __xr_uart_disable(port); return ret; } -static int xr_uart_disable(struct usb_serial_port *port) +static int xr21v141x_uart_disable(struct usb_serial_port *port) { int ret; - ret = xr_set_reg_uart(port, XR21V141X_REG_ENABLE, 0); + ret = __xr_uart_disable(port); if (ret) return ret; @@ -216,13 +378,68 @@ static int xr_uart_disable(struct usb_serial_port *port) return ret; } +static int xr_uart_enable(struct usb_serial_port *port) +{ + struct xr_data *data = usb_get_serial_port_data(port); + + if (data->type->enable) + return data->type->enable(port); + + return __xr_uart_enable(port); +} + +static int xr_uart_disable(struct usb_serial_port *port) +{ + struct xr_data *data = usb_get_serial_port_data(port); + + if (data->type->disable) + return data->type->disable(port); + + return __xr_uart_disable(port); +} + +static int xr21v141x_fifo_reset(struct usb_serial_port *port) +{ + int ret; + + ret = xr_set_reg_um(port, XR21V141X_UM_TX_FIFO_RESET, XR_FIFO_RESET); + if (ret) + return ret; + + ret = xr_set_reg_um(port, XR21V141X_UM_RX_FIFO_RESET, XR_FIFO_RESET); + if (ret) + return ret; + + return 0; +} + +static int xr_fifo_reset(struct usb_serial_port *port) +{ + struct xr_data *data = usb_get_serial_port_data(port); + int ret; + + if (data->type->fifo_reset) + return data->type->fifo_reset(port); + + ret = xr_set_reg_uart(port, data->type->tx_fifo_reset, XR_FIFO_RESET); + if (ret) + return ret; + + ret = xr_set_reg_uart(port, data->type->rx_fifo_reset, XR_FIFO_RESET); + if (ret) + return ret; + + return 0; +} + static int xr_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; - u8 status; + struct xr_data *data = usb_get_serial_port_data(port); + u16 status; int ret; - ret = xr_get_reg_uart(port, XR21V141X_REG_GPIO_STATUS, &status); + ret = xr_get_reg_uart(port, data->type->gpio_status, &status); if (ret) return ret; @@ -230,12 +447,12 @@ static int xr_tiocmget(struct tty_struct *tty) * Modem control pins are active low, so reading '0' means it is active * and '1' means not active. */ - ret = ((status & XR21V141X_UART_MODE_DTR) ? 0 : TIOCM_DTR) | - ((status & XR21V141X_UART_MODE_RTS) ? 0 : TIOCM_RTS) | - ((status & XR21V141X_UART_MODE_CTS) ? 0 : TIOCM_CTS) | - ((status & XR21V141X_UART_MODE_DSR) ? 0 : TIOCM_DSR) | - ((status & XR21V141X_UART_MODE_RI) ? 0 : TIOCM_RI) | - ((status & XR21V141X_UART_MODE_CD) ? 0 : TIOCM_CD); + ret = ((status & XR_GPIO_DTR) ? 0 : TIOCM_DTR) | + ((status & XR_GPIO_RTS) ? 0 : TIOCM_RTS) | + ((status & XR_GPIO_CTS) ? 0 : TIOCM_CTS) | + ((status & XR_GPIO_DSR) ? 0 : TIOCM_DSR) | + ((status & XR_GPIO_RI) ? 0 : TIOCM_RI) | + ((status & XR_GPIO_CD) ? 0 : TIOCM_CD); return ret; } @@ -243,26 +460,28 @@ static int xr_tiocmget(struct tty_struct *tty) static int xr_tiocmset_port(struct usb_serial_port *port, unsigned int set, unsigned int clear) { - u8 gpio_set = 0; - u8 gpio_clr = 0; + struct xr_data *data = usb_get_serial_port_data(port); + const struct xr_type *type = data->type; + u16 gpio_set = 0; + u16 gpio_clr = 0; int ret = 0; /* Modem control pins are active low, so set & clr are swapped */ if (set & TIOCM_RTS) - gpio_clr |= XR21V141X_UART_MODE_RTS; + gpio_clr |= XR_GPIO_RTS; if (set & TIOCM_DTR) - gpio_clr |= XR21V141X_UART_MODE_DTR; + gpio_clr |= XR_GPIO_DTR; if (clear & TIOCM_RTS) - gpio_set |= XR21V141X_UART_MODE_RTS; + gpio_set |= XR_GPIO_RTS; if (clear & TIOCM_DTR) - gpio_set |= XR21V141X_UART_MODE_DTR; + gpio_set |= XR_GPIO_DTR; /* Writing '0' to gpio_{set/clr} bits has no effect, so no need to do */ if (gpio_clr) - ret = xr_set_reg_uart(port, XR21V141X_REG_GPIO_CLR, gpio_clr); + ret = xr_set_reg_uart(port, type->gpio_clear, gpio_clr); if (gpio_set) - ret = xr_set_reg_uart(port, XR21V141X_REG_GPIO_SET, gpio_set); + ret = xr_set_reg_uart(port, type->gpio_set, gpio_set); return ret; } @@ -286,16 +505,18 @@ static void xr_dtr_rts(struct usb_serial_port *port, int on) static void xr_break_ctl(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; - u8 state; + struct xr_data *data = usb_get_serial_port_data(port); + const struct xr_type *type = data->type; + u16 state; if (break_state == 0) - state = XR21V141X_UART_BREAK_OFF; + state = 0; else - state = XR21V141X_UART_BREAK_ON; + state = GENMASK(type->reg_width - 1, 0); - dev_dbg(&port->dev, "Turning break %s\n", - state == XR21V141X_UART_BREAK_OFF ? "off" : "on"); - xr_set_reg_uart(port, XR21V141X_REG_TX_BREAK, state); + dev_dbg(&port->dev, "Turning break %s\n", state == 0 ? "off" : "on"); + + xr_set_reg_uart(port, type->tx_break, state); } /* Tx and Rx clock mask values obtained from section 3.3.4 of datasheet */ @@ -334,8 +555,7 @@ static const struct xr_txrx_clk_mask xr21v141x_txrx_clk_masks[] = { { 0xfff, 0xffe, 0xffd }, }; -static int xr_set_baudrate(struct tty_struct *tty, - struct usb_serial_port *port) +static int xr21v141x_set_baudrate(struct tty_struct *tty, struct usb_serial_port *port) { u32 divisor, baud, idx; u16 tx_mask, rx_mask; @@ -405,43 +625,47 @@ static void xr_set_flow_mode(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { - u8 flow, gpio_mode; + struct xr_data *data = usb_get_serial_port_data(port); + const struct xr_type *type = data->type; + u16 flow, gpio_mode; int ret; - ret = xr_get_reg_uart(port, XR21V141X_REG_GPIO_MODE, &gpio_mode); + ret = xr_get_reg_uart(port, type->gpio_mode, &gpio_mode); if (ret) return; + /* + * According to the datasheets, the UART needs to be disabled while + * writing to the FLOW_CONTROL register (XR21V141X), or any register + * but GPIO_SET, GPIO_CLEAR, TX_BREAK and ERROR_STATUS (XR21B142X). + */ + xr_uart_disable(port); + /* Set GPIO mode for controlling the pins manually by default. */ - gpio_mode &= ~XR21V141X_UART_MODE_GPIO_MASK; + gpio_mode &= ~XR_GPIO_MODE_SEL_MASK; if (C_CRTSCTS(tty) && C_BAUD(tty) != B0) { dev_dbg(&port->dev, "Enabling hardware flow ctrl\n"); - gpio_mode |= XR21V141X_UART_MODE_RTS_CTS; - flow = XR21V141X_UART_FLOW_MODE_HW; + gpio_mode |= XR_GPIO_MODE_SEL_RTS_CTS; + flow = XR_UART_FLOW_MODE_HW; } else if (I_IXON(tty)) { u8 start_char = START_CHAR(tty); u8 stop_char = STOP_CHAR(tty); dev_dbg(&port->dev, "Enabling sw flow ctrl\n"); - flow = XR21V141X_UART_FLOW_MODE_SW; + flow = XR_UART_FLOW_MODE_SW; - xr_set_reg_uart(port, XR21V141X_REG_XON_CHAR, start_char); - xr_set_reg_uart(port, XR21V141X_REG_XOFF_CHAR, stop_char); + xr_set_reg_uart(port, type->xon_char, start_char); + xr_set_reg_uart(port, type->xoff_char, stop_char); } else { dev_dbg(&port->dev, "Disabling flow ctrl\n"); - flow = XR21V141X_UART_FLOW_MODE_NONE; + flow = XR_UART_FLOW_MODE_NONE; } - /* - * As per the datasheet, UART needs to be disabled while writing to - * FLOW_CONTROL register. - */ - xr_uart_disable(port); - xr_set_reg_uart(port, XR21V141X_REG_FLOW_CTRL, flow); - xr_uart_enable(port); + xr_set_reg_uart(port, type->flow_control, flow); + xr_set_reg_uart(port, type->gpio_mode, gpio_mode); - xr_set_reg_uart(port, XR21V141X_REG_GPIO_MODE, gpio_mode); + xr_uart_enable(port); if (C_BAUD(tty) == B0) xr_dtr_rts(port, 0); @@ -449,16 +673,15 @@ static void xr_set_flow_mode(struct tty_struct *tty, xr_dtr_rts(port, 1); } -static void xr_set_termios(struct tty_struct *tty, - struct usb_serial_port *port, - struct ktermios *old_termios) +static void xr21v141x_set_line_settings(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { struct ktermios *termios = &tty->termios; u8 bits = 0; int ret; if (!old_termios || (tty->termios.c_ospeed != old_termios->c_ospeed)) - xr_set_baudrate(tty, port); + xr21v141x_set_baudrate(tty, port); switch (C_CSIZE(tty)) { case CS5: @@ -468,61 +691,154 @@ static void xr_set_termios(struct tty_struct *tty, if (old_termios) termios->c_cflag |= old_termios->c_cflag & CSIZE; else - bits |= XR21V141X_UART_DATA_8; + termios->c_cflag |= CS8; + + if (C_CSIZE(tty) == CS7) + bits |= XR_UART_DATA_7; + else + bits |= XR_UART_DATA_8; break; case CS7: - bits |= XR21V141X_UART_DATA_7; + bits |= XR_UART_DATA_7; break; case CS8: default: - bits |= XR21V141X_UART_DATA_8; + bits |= XR_UART_DATA_8; break; } if (C_PARENB(tty)) { if (C_CMSPAR(tty)) { if (C_PARODD(tty)) - bits |= XR21V141X_UART_PARITY_MARK; + bits |= XR_UART_PARITY_MARK; else - bits |= XR21V141X_UART_PARITY_SPACE; + bits |= XR_UART_PARITY_SPACE; } else { if (C_PARODD(tty)) - bits |= XR21V141X_UART_PARITY_ODD; + bits |= XR_UART_PARITY_ODD; else - bits |= XR21V141X_UART_PARITY_EVEN; + bits |= XR_UART_PARITY_EVEN; } } if (C_CSTOPB(tty)) - bits |= XR21V141X_UART_STOP_2; + bits |= XR_UART_STOP_2; else - bits |= XR21V141X_UART_STOP_1; + bits |= XR_UART_STOP_1; ret = xr_set_reg_uart(port, XR21V141X_REG_FORMAT, bits); if (ret) return; +} + +static void xr_cdc_set_line_coding(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) +{ + struct xr_data *data = usb_get_serial_port_data(port); + struct usb_host_interface *alt = port->serial->interface->cur_altsetting; + struct usb_device *udev = port->serial->dev; + struct usb_cdc_line_coding *lc; + int ret; + + lc = kzalloc(sizeof(*lc), GFP_KERNEL); + if (!lc) + return; + + if (tty->termios.c_ospeed) + lc->dwDTERate = cpu_to_le32(tty->termios.c_ospeed); + else if (old_termios) + lc->dwDTERate = cpu_to_le32(old_termios->c_ospeed); + else + lc->dwDTERate = cpu_to_le32(9600); + + if (C_CSTOPB(tty)) + lc->bCharFormat = USB_CDC_2_STOP_BITS; + else + lc->bCharFormat = USB_CDC_1_STOP_BITS; + + if (C_PARENB(tty)) { + if (C_CMSPAR(tty)) { + if (C_PARODD(tty)) + lc->bParityType = USB_CDC_MARK_PARITY; + else + lc->bParityType = USB_CDC_SPACE_PARITY; + } else { + if (C_PARODD(tty)) + lc->bParityType = USB_CDC_ODD_PARITY; + else + lc->bParityType = USB_CDC_EVEN_PARITY; + } + } else { + lc->bParityType = USB_CDC_NO_PARITY; + } + + if (!data->type->have_5_6_bit_mode && + (C_CSIZE(tty) == CS5 || C_CSIZE(tty) == CS6)) { + tty->termios.c_cflag &= ~CSIZE; + if (old_termios) + tty->termios.c_cflag |= old_termios->c_cflag & CSIZE; + else + tty->termios.c_cflag |= CS8; + } + + switch (C_CSIZE(tty)) { + case CS5: + lc->bDataBits = 5; + break; + case CS6: + lc->bDataBits = 6; + break; + case CS7: + lc->bDataBits = 7; + break; + case CS8: + default: + lc->bDataBits = 8; + break; + } + + ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_CDC_REQ_SET_LINE_CODING, + USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0, alt->desc.bInterfaceNumber, + lc, sizeof(*lc), USB_CTRL_SET_TIMEOUT); + if (ret < 0) + dev_err(&port->dev, "Failed to set line coding: %d\n", ret); + + kfree(lc); +} + +static void xr_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) +{ + struct xr_data *data = usb_get_serial_port_data(port); + + /* + * XR21V141X does not have a CUSTOM_DRIVER flag and always enters CDC + * mode upon receiving CDC requests. + */ + if (data->type->set_line_settings) + data->type->set_line_settings(tty, port, old_termios); + else + xr_cdc_set_line_coding(tty, port, old_termios); xr_set_flow_mode(tty, port, old_termios); } static int xr_open(struct tty_struct *tty, struct usb_serial_port *port) { - u8 gpio_dir; int ret; + ret = xr_fifo_reset(port); + if (ret) + return ret; + ret = xr_uart_enable(port); if (ret) { dev_err(&port->dev, "Failed to enable UART\n"); return ret; } - /* - * Configure DTR and RTS as outputs and RI, CD, DSR and CTS as - * inputs. - */ - gpio_dir = XR21V141X_UART_MODE_DTR | XR21V141X_UART_MODE_RTS; - xr_set_reg_uart(port, XR21V141X_REG_GPIO_DIR, gpio_dir); - /* Setup termios */ if (tty) xr_set_termios(tty, port, NULL); @@ -545,15 +861,133 @@ static void xr_close(struct usb_serial_port *port) static int xr_probe(struct usb_serial *serial, const struct usb_device_id *id) { - /* Don't bind to control interface */ - if (serial->interface->cur_altsetting->desc.bInterfaceNumber == 0) + struct usb_interface *control = serial->interface; + struct usb_host_interface *alt = control->cur_altsetting; + struct usb_cdc_parsed_header hdrs; + struct usb_cdc_union_desc *desc; + struct usb_interface *data; + int ret; + + ret = cdc_parse_cdc_header(&hdrs, control, alt->extra, alt->extralen); + if (ret < 0) return -ENODEV; + desc = hdrs.usb_cdc_union_desc; + if (!desc) + return -ENODEV; + + data = usb_ifnum_to_if(serial->dev, desc->bSlaveInterface0); + if (!data) + return -ENODEV; + + ret = usb_serial_claim_interface(serial, data); + if (ret) + return ret; + + usb_set_serial_data(serial, (void *)id->driver_info); + return 0; } +static int xr_gpio_init(struct usb_serial_port *port, const struct xr_type *type) +{ + u16 mask, mode; + int ret; + + /* + * Configure all pins as GPIO except for Receive and Transmit Toggle. + */ + mode = 0; + if (type->have_xmit_toggle) + mode |= XR_GPIO_MODE_RX_TOGGLE | XR_GPIO_MODE_TX_TOGGLE; + + ret = xr_set_reg_uart(port, type->gpio_mode, mode); + if (ret) + return ret; + + /* + * Configure DTR and RTS as outputs and make sure they are deasserted + * (active low), and configure RI, CD, DSR and CTS as inputs. + */ + mask = XR_GPIO_DTR | XR_GPIO_RTS; + ret = xr_set_reg_uart(port, type->gpio_direction, mask); + if (ret) + return ret; + + ret = xr_set_reg_uart(port, type->gpio_set, mask); + if (ret) + return ret; + + return 0; +} + +static int xr_port_probe(struct usb_serial_port *port) +{ + struct usb_interface_descriptor *desc; + const struct xr_type *type; + struct xr_data *data; + enum xr_type_id type_id; + int ret; + + type_id = (int)(unsigned long)usb_get_serial_data(port->serial); + type = &xr_types[type_id]; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->type = type; + + desc = &port->serial->interface->cur_altsetting->desc; + if (type_id == XR21V141X) + data->channel = desc->bInterfaceNumber / 2; + else + data->channel = desc->bInterfaceNumber; + + usb_set_serial_port_data(port, data); + + if (type->custom_driver) { + ret = xr_set_reg_uart(port, type->custom_driver, + XR_CUSTOM_DRIVER_ACTIVE); + if (ret) + goto err_free; + } + + ret = xr_gpio_init(port, type); + if (ret) + goto err_free; + + return 0; + +err_free: + kfree(data); + + return ret; +} + +static void xr_port_remove(struct usb_serial_port *port) +{ + struct xr_data *data = usb_get_serial_port_data(port); + + kfree(data); +} + +#define XR_DEVICE(vid, pid, type) \ + USB_DEVICE_INTERFACE_CLASS((vid), (pid), USB_CLASS_COMM), \ + .driver_info = (type) + static const struct usb_device_id id_table[] = { - { USB_DEVICE(0x04e2, 0x1410) }, /* XR21V141X */ + { XR_DEVICE(0x04e2, 0x1400, XR2280X) }, + { XR_DEVICE(0x04e2, 0x1401, XR2280X) }, + { XR_DEVICE(0x04e2, 0x1402, XR2280X) }, + { XR_DEVICE(0x04e2, 0x1403, XR2280X) }, + { XR_DEVICE(0x04e2, 0x1410, XR21V141X) }, + { XR_DEVICE(0x04e2, 0x1411, XR21B1411) }, + { XR_DEVICE(0x04e2, 0x1412, XR21V141X) }, + { XR_DEVICE(0x04e2, 0x1414, XR21V141X) }, + { XR_DEVICE(0x04e2, 0x1420, XR21B142X) }, + { XR_DEVICE(0x04e2, 0x1422, XR21B142X) }, + { XR_DEVICE(0x04e2, 0x1424, XR21B142X) }, { } }; MODULE_DEVICE_TABLE(usb, id_table); @@ -566,6 +1000,8 @@ static struct usb_serial_driver xr_device = { .id_table = id_table, .num_ports = 1, .probe = xr_probe, + .port_probe = xr_port_probe, + .port_remove = xr_port_remove, .open = xr_open, .close = xr_close, .break_ctl = xr_break_ctl, diff --git a/drivers/usb/storage/datafab.c b/drivers/usb/storage/datafab.c index 588818483f4b..bcc4a2fad863 100644 --- a/drivers/usb/storage/datafab.c +++ b/drivers/usb/storage/datafab.c @@ -294,7 +294,6 @@ static int datafab_write_data(struct us_data *us, if (reply[0] != 0x50 && reply[1] != 0) { usb_stor_dbg(us, "Gah! write return code: %02x %02x\n", reply[0], reply[1]); - result = USB_STOR_TRANSPORT_ERROR; goto leave; } diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig index 270e81c087e9..a0418f23b4aa 100644 --- a/drivers/usb/typec/Kconfig +++ b/drivers/usb/typec/Kconfig @@ -50,6 +50,8 @@ source "drivers/usb/typec/tcpm/Kconfig" source "drivers/usb/typec/ucsi/Kconfig" +source "drivers/usb/typec/tipd/Kconfig" + config TYPEC_HD3SS3220 tristate "TI HD3SS3220 Type-C DRP Port controller driver" depends on I2C @@ -61,19 +63,6 @@ config TYPEC_HD3SS3220 If you choose to build this driver as a dynamically linked module, the module will be called hd3ss3220.ko. -config TYPEC_TPS6598X - tristate "TI TPS6598x USB Power Delivery controller driver" - depends on I2C - select POWER_SUPPLY - select REGMAP_I2C - select USB_ROLE_SWITCH - help - Say Y or M here if your system has TI TPS65982 or TPS65983 USB Power - Delivery controller. - - If you choose to build this driver as a dynamically linked module, the - module will be called tps6598x.ko. - config TYPEC_STUSB160X tristate "STMicroelectronics STUSB160x Type-C controller driver" depends on I2C diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile index d03b48c4b864..a0adb8947a30 100644 --- a/drivers/usb/typec/Makefile +++ b/drivers/usb/typec/Makefile @@ -1,11 +1,11 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_TYPEC) += typec.o -typec-y := class.o mux.o bus.o +typec-y := class.o mux.o bus.o port-mapper.o obj-$(CONFIG_TYPEC) += altmodes/ obj-$(CONFIG_TYPEC_TCPM) += tcpm/ obj-$(CONFIG_TYPEC_UCSI) += ucsi/ +obj-$(CONFIG_TYPEC_TPS6598X) += tipd/ obj-$(CONFIG_TYPEC_HD3SS3220) += hd3ss3220.o -obj-$(CONFIG_TYPEC_TPS6598X) += tps6598x.o obj-$(CONFIG_TYPEC_QCOM_PMIC) += qcom-pmic-typec.o obj-$(CONFIG_TYPEC_STUSB160X) += stusb160x.o obj-$(CONFIG_TYPEC) += mux/ diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c index e8ddb81cb6df..7f3c9a8e2bf0 100644 --- a/drivers/usb/typec/bus.c +++ b/drivers/usb/typec/bus.c @@ -9,6 +9,8 @@ #include #include "bus.h" +#include "class.h" +#include "mux.h" static inline int typec_altmode_set_mux(struct altmode *alt, unsigned long conf, void *data) diff --git a/drivers/usb/typec/bus.h b/drivers/usb/typec/bus.h index 8ba8112d2740..56dec268d4dd 100644 --- a/drivers/usb/typec/bus.h +++ b/drivers/usb/typec/bus.h @@ -4,9 +4,9 @@ #define __USB_TYPEC_ALTMODE_H__ #include -#include struct bus_type; +struct typec_mux; struct altmode { unsigned int id; @@ -28,24 +28,7 @@ struct altmode { extern struct bus_type typec_bus; extern const struct device_type typec_altmode_dev_type; -extern const struct device_type typec_port_dev_type; #define is_typec_altmode(_dev_) (_dev_->type == &typec_altmode_dev_type) -#define is_typec_port(_dev_) (_dev_->type == &typec_port_dev_type) - -extern struct class typec_mux_class; - -struct typec_switch { - struct device dev; - typec_switch_set_fn_t set; -}; - -struct typec_mux { - struct device dev; - typec_mux_set_fn_t set; -}; - -#define to_typec_switch(_dev_) container_of(_dev_, struct typec_switch, dev) -#define to_typec_mux(_dev_) container_of(_dev_, struct typec_mux, dev) #endif /* __USB_TYPEC_ALTMODE_H__ */ diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 45f0bf65e9ab..b9429c9f65f6 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -6,77 +6,22 @@ * Author: Heikki Krogerus */ -#include #include #include #include #include #include +#include #include "bus.h" - -struct typec_plug { - struct device dev; - enum typec_plug_index index; - struct ida mode_ids; - int num_altmodes; -}; - -struct typec_cable { - struct device dev; - enum typec_plug_type type; - struct usb_pd_identity *identity; - unsigned int active:1; - u16 pd_revision; /* 0300H = "3.0" */ -}; - -struct typec_partner { - struct device dev; - unsigned int usb_pd:1; - struct usb_pd_identity *identity; - enum typec_accessory accessory; - struct ida mode_ids; - int num_altmodes; - u16 pd_revision; /* 0300H = "3.0" */ - enum usb_pd_svdm_ver svdm_version; -}; - -struct typec_port { - unsigned int id; - struct device dev; - struct ida mode_ids; - - int prefer_role; - enum typec_data_role data_role; - enum typec_role pwr_role; - enum typec_role vconn_role; - enum typec_pwr_opmode pwr_opmode; - enum typec_port_type port_type; - struct mutex port_type_lock; - - enum typec_orientation orientation; - struct typec_switch *sw; - struct typec_mux *mux; - - const struct typec_capability *cap; - const struct typec_operations *ops; -}; - -#define to_typec_port(_dev_) container_of(_dev_, struct typec_port, dev) -#define to_typec_plug(_dev_) container_of(_dev_, struct typec_plug, dev) -#define to_typec_cable(_dev_) container_of(_dev_, struct typec_cable, dev) -#define to_typec_partner(_dev_) container_of(_dev_, struct typec_partner, dev) - -static const struct device_type typec_partner_dev_type; -static const struct device_type typec_cable_dev_type; -static const struct device_type typec_plug_dev_type; - -#define is_typec_partner(_dev_) (_dev_->type == &typec_partner_dev_type) -#define is_typec_cable(_dev_) (_dev_->type == &typec_cable_dev_type) -#define is_typec_plug(_dev_) (_dev_->type == &typec_plug_dev_type) +#include "class.h" static DEFINE_IDA(typec_index_ida); -static struct class *typec_class; + +struct class typec_class = { + .name = "typec", + .owner = THIS_MODULE, +}; /* ------------------------------------------------------------------------- */ /* Common attributes */ @@ -610,7 +555,7 @@ typec_register_altmode(struct device *parent, /* Plug alt modes need a class to generate udev events. */ if (is_typec_plug(parent)) - alt->adev.dev.class = typec_class; + alt->adev.dev.class = &typec_class; ret = device_register(&alt->adev.dev); if (ret) { @@ -726,7 +671,7 @@ static void typec_partner_release(struct device *dev) kfree(partner); } -static const struct device_type typec_partner_dev_type = { +const struct device_type typec_partner_dev_type = { .name = "typec_partner", .groups = typec_partner_groups, .release = typec_partner_release, @@ -874,7 +819,7 @@ struct typec_partner *typec_register_partner(struct typec_port *port, partner->identity = desc->identity; } - partner->dev.class = typec_class; + partner->dev.class = &typec_class; partner->dev.parent = &port->dev; partner->dev.type = &typec_partner_dev_type; dev_set_name(&partner->dev, "%s-partner", dev_name(&port->dev)); @@ -941,7 +886,7 @@ static const struct attribute_group *typec_plug_groups[] = { NULL }; -static const struct device_type typec_plug_dev_type = { +const struct device_type typec_plug_dev_type = { .name = "typec_plug", .groups = typec_plug_groups, .release = typec_plug_release, @@ -1026,7 +971,7 @@ struct typec_plug *typec_register_plug(struct typec_cable *cable, ida_init(&plug->mode_ids); plug->num_altmodes = -1; plug->index = desc->index; - plug->dev.class = typec_class; + plug->dev.class = &typec_class; plug->dev.parent = &cable->dev; plug->dev.type = &typec_plug_dev_type; dev_set_name(&plug->dev, "%s-%s", dev_name(cable->dev.parent), name); @@ -1089,7 +1034,7 @@ static void typec_cable_release(struct device *dev) kfree(cable); } -static const struct device_type typec_cable_dev_type = { +const struct device_type typec_cable_dev_type = { .name = "typec_cable", .groups = typec_cable_groups, .release = typec_cable_release, @@ -1191,7 +1136,7 @@ struct typec_cable *typec_register_cable(struct typec_port *port, cable->identity = desc->identity; } - cable->dev.class = typec_class; + cable->dev.class = &typec_class; cable->dev.parent = &port->dev; cable->dev.type = &typec_cable_dev_type; dev_set_name(&cable->dev, "%s-cable", dev_name(&port->dev)); @@ -1978,6 +1923,60 @@ typec_port_register_altmode(struct typec_port *port, } EXPORT_SYMBOL_GPL(typec_port_register_altmode); +void typec_port_register_altmodes(struct typec_port *port, + const struct typec_altmode_ops *ops, void *drvdata, + struct typec_altmode **altmodes, size_t n) +{ + struct fwnode_handle *altmodes_node, *child; + struct typec_altmode_desc desc; + struct typec_altmode *alt; + size_t index = 0; + u32 svid, vdo; + int ret; + + altmodes_node = device_get_named_child_node(&port->dev, "altmodes"); + if (!altmodes_node) + return; /* No altmodes specified */ + + fwnode_for_each_child_node(altmodes_node, child) { + ret = fwnode_property_read_u32(child, "svid", &svid); + if (ret) { + dev_err(&port->dev, "Error reading svid for altmode %s\n", + fwnode_get_name(child)); + continue; + } + + ret = fwnode_property_read_u32(child, "vdo", &vdo); + if (ret) { + dev_err(&port->dev, "Error reading vdo for altmode %s\n", + fwnode_get_name(child)); + continue; + } + + if (index >= n) { + dev_err(&port->dev, "Error not enough space for altmode %s\n", + fwnode_get_name(child)); + continue; + } + + desc.svid = svid; + desc.vdo = vdo; + desc.mode = index + 1; + alt = typec_port_register_altmode(port, &desc); + if (IS_ERR(alt)) { + dev_err(&port->dev, "Error registering altmode %s\n", + fwnode_get_name(child)); + continue; + } + + alt->ops = ops; + typec_altmode_set_drvdata(alt, drvdata); + altmodes[index] = alt; + index++; + } +} +EXPORT_SYMBOL_GPL(typec_port_register_altmodes); + /** * typec_register_port - Register a USB Type-C Port * @parent: Parent device @@ -2038,6 +2037,8 @@ struct typec_port *typec_register_port(struct device *parent, ida_init(&port->mode_ids); mutex_init(&port->port_type_lock); + mutex_init(&port->port_list_lock); + INIT_LIST_HEAD(&port->port_list); port->id = id; port->ops = cap->ops; @@ -2045,7 +2046,7 @@ struct typec_port *typec_register_port(struct device *parent, port->prefer_role = cap->prefer_role; device_initialize(&port->dev); - port->dev.class = typec_class; + port->dev.class = &typec_class; port->dev.parent = parent; port->dev.fwnode = cap->fwnode; port->dev.type = &typec_port_dev_type; @@ -2079,6 +2080,10 @@ struct typec_port *typec_register_port(struct device *parent, return ERR_PTR(ret); } + ret = typec_link_ports(port); + if (ret) + dev_warn(&port->dev, "failed to create symlinks (%d)\n", ret); + return port; } EXPORT_SYMBOL_GPL(typec_register_port); @@ -2091,8 +2096,10 @@ EXPORT_SYMBOL_GPL(typec_register_port); */ void typec_unregister_port(struct typec_port *port) { - if (!IS_ERR_OR_NULL(port)) + if (!IS_ERR_OR_NULL(port)) { + typec_unlink_ports(port); device_unregister(&port->dev); + } } EXPORT_SYMBOL_GPL(typec_unregister_port); @@ -2108,11 +2115,9 @@ static int __init typec_init(void) if (ret) goto err_unregister_bus; - typec_class = class_create(THIS_MODULE, "typec"); - if (IS_ERR(typec_class)) { - ret = PTR_ERR(typec_class); + ret = class_register(&typec_class); + if (ret) goto err_unregister_mux_class; - } return 0; @@ -2128,7 +2133,7 @@ subsys_initcall(typec_init); static void __exit typec_exit(void) { - class_destroy(typec_class); + class_unregister(&typec_class); ida_destroy(&typec_index_ida); bus_unregister(&typec_bus); class_unregister(&typec_mux_class); diff --git a/drivers/usb/typec/class.h b/drivers/usb/typec/class.h new file mode 100644 index 000000000000..aef03eb7e152 --- /dev/null +++ b/drivers/usb/typec/class.h @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __USB_TYPEC_CLASS__ +#define __USB_TYPEC_CLASS__ + +#include +#include + +struct typec_mux; +struct typec_switch; + +struct typec_plug { + struct device dev; + enum typec_plug_index index; + struct ida mode_ids; + int num_altmodes; +}; + +struct typec_cable { + struct device dev; + enum typec_plug_type type; + struct usb_pd_identity *identity; + unsigned int active:1; + u16 pd_revision; /* 0300H = "3.0" */ +}; + +struct typec_partner { + struct device dev; + unsigned int usb_pd:1; + struct usb_pd_identity *identity; + enum typec_accessory accessory; + struct ida mode_ids; + int num_altmodes; + u16 pd_revision; /* 0300H = "3.0" */ + enum usb_pd_svdm_ver svdm_version; +}; + +struct typec_port { + unsigned int id; + struct device dev; + struct ida mode_ids; + + int prefer_role; + enum typec_data_role data_role; + enum typec_role pwr_role; + enum typec_role vconn_role; + enum typec_pwr_opmode pwr_opmode; + enum typec_port_type port_type; + struct mutex port_type_lock; + + enum typec_orientation orientation; + struct typec_switch *sw; + struct typec_mux *mux; + + const struct typec_capability *cap; + const struct typec_operations *ops; + + struct list_head port_list; + struct mutex port_list_lock; /* Port list lock */ + + void *pld; +}; + +#define to_typec_port(_dev_) container_of(_dev_, struct typec_port, dev) +#define to_typec_plug(_dev_) container_of(_dev_, struct typec_plug, dev) +#define to_typec_cable(_dev_) container_of(_dev_, struct typec_cable, dev) +#define to_typec_partner(_dev_) container_of(_dev_, struct typec_partner, dev) + +extern const struct device_type typec_partner_dev_type; +extern const struct device_type typec_cable_dev_type; +extern const struct device_type typec_plug_dev_type; +extern const struct device_type typec_port_dev_type; + +#define is_typec_partner(dev) ((dev)->type == &typec_partner_dev_type) +#define is_typec_cable(dev) ((dev)->type == &typec_cable_dev_type) +#define is_typec_plug(dev) ((dev)->type == &typec_plug_dev_type) +#define is_typec_port(dev) ((dev)->type == &typec_port_dev_type) + +extern struct class typec_mux_class; +extern struct class typec_class; + +int typec_link_ports(struct typec_port *connector); +void typec_unlink_ports(struct typec_port *connector); + +#endif /* __USB_TYPEC_CLASS__ */ diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c index cf720e944aaa..9da22ae3006c 100644 --- a/drivers/usb/typec/mux.c +++ b/drivers/usb/typec/mux.c @@ -13,9 +13,9 @@ #include #include #include -#include -#include "bus.h" +#include "class.h" +#include "mux.h" static bool dev_name_ends_with(struct device *dev, const char *suffix) { diff --git a/drivers/usb/typec/mux.h b/drivers/usb/typec/mux.h new file mode 100644 index 000000000000..4fd9426ee44f --- /dev/null +++ b/drivers/usb/typec/mux.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __USB_TYPEC_MUX__ +#define __USB_TYPEC_MUX__ + +#include + +struct typec_switch { + struct device dev; + typec_switch_set_fn_t set; +}; + +struct typec_mux { + struct device dev; + typec_mux_set_fn_t set; +}; + +#define to_typec_switch(_dev_) container_of(_dev_, struct typec_switch, dev) +#define to_typec_mux(_dev_) container_of(_dev_, struct typec_mux, dev) + +#endif /* __USB_TYPEC_MUX__ */ diff --git a/drivers/usb/typec/port-mapper.c b/drivers/usb/typec/port-mapper.c new file mode 100644 index 000000000000..9b0991bdf391 --- /dev/null +++ b/drivers/usb/typec/port-mapper.c @@ -0,0 +1,279 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * USB Type-C Connector Class Port Mapping Utility + * + * Copyright (C) 2021, Intel Corporation + * Author: Heikki Krogerus + */ + +#include +#include +#include + +#include "class.h" + +struct port_node { + struct list_head list; + struct device *dev; + void *pld; +}; + +static int acpi_pld_match(const struct acpi_pld_info *pld1, + const struct acpi_pld_info *pld2) +{ + if (!pld1 || !pld2) + return 0; + + /* + * To speed things up, first checking only the group_position. It seems + * to often have the first unique value in the _PLD. + */ + if (pld1->group_position == pld2->group_position) + return !memcmp(pld1, pld2, sizeof(struct acpi_pld_info)); + + return 0; +} + +static void *get_pld(struct device *dev) +{ +#ifdef CONFIG_ACPI + struct acpi_pld_info *pld; + acpi_status status; + + if (!has_acpi_companion(dev)) + return NULL; + + status = acpi_get_physical_device_location(ACPI_HANDLE(dev), &pld); + if (ACPI_FAILURE(status)) + return NULL; + + return pld; +#else + return NULL; +#endif +} + +static void free_pld(void *pld) +{ +#ifdef CONFIG_ACPI + ACPI_FREE(pld); +#endif +} + +static int __link_port(struct typec_port *con, struct port_node *node) +{ + int ret; + + ret = sysfs_create_link(&node->dev->kobj, &con->dev.kobj, "connector"); + if (ret) + return ret; + + ret = sysfs_create_link(&con->dev.kobj, &node->dev->kobj, + dev_name(node->dev)); + if (ret) { + sysfs_remove_link(&node->dev->kobj, "connector"); + return ret; + } + + list_add_tail(&node->list, &con->port_list); + + return 0; +} + +static int link_port(struct typec_port *con, struct port_node *node) +{ + int ret; + + mutex_lock(&con->port_list_lock); + ret = __link_port(con, node); + mutex_unlock(&con->port_list_lock); + + return ret; +} + +static void __unlink_port(struct typec_port *con, struct port_node *node) +{ + sysfs_remove_link(&con->dev.kobj, dev_name(node->dev)); + sysfs_remove_link(&node->dev->kobj, "connector"); + list_del(&node->list); +} + +static void unlink_port(struct typec_port *con, struct port_node *node) +{ + mutex_lock(&con->port_list_lock); + __unlink_port(con, node); + mutex_unlock(&con->port_list_lock); +} + +static struct port_node *create_port_node(struct device *port) +{ + struct port_node *node; + + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) + return ERR_PTR(-ENOMEM); + + node->dev = get_device(port); + node->pld = get_pld(port); + + return node; +} + +static void remove_port_node(struct port_node *node) +{ + put_device(node->dev); + free_pld(node->pld); + kfree(node); +} + +static int connector_match(struct device *dev, const void *data) +{ + const struct port_node *node = data; + + if (!is_typec_port(dev)) + return 0; + + return acpi_pld_match(to_typec_port(dev)->pld, node->pld); +} + +static struct device *find_connector(struct port_node *node) +{ + if (!node->pld) + return NULL; + + return class_find_device(&typec_class, NULL, node, connector_match); +} + +/** + * typec_link_port - Link a port to its connector + * @port: The port device + * + * Find the connector of @port and create symlink named "connector" for it. + * Returns 0 on success, or errno in case of a failure. + * + * NOTE. The function increments the reference count of @port on success. + */ +int typec_link_port(struct device *port) +{ + struct device *connector; + struct port_node *node; + int ret; + + node = create_port_node(port); + if (IS_ERR(node)) + return PTR_ERR(node); + + connector = find_connector(node); + if (!connector) { + ret = 0; + goto remove_node; + } + + ret = link_port(to_typec_port(connector), node); + if (ret) + goto put_connector; + + return 0; + +put_connector: + put_device(connector); +remove_node: + remove_port_node(node); + + return ret; +} +EXPORT_SYMBOL_GPL(typec_link_port); + +static int port_match_and_unlink(struct device *connector, void *port) +{ + struct port_node *node; + struct port_node *tmp; + int ret = 0; + + if (!is_typec_port(connector)) + return 0; + + mutex_lock(&to_typec_port(connector)->port_list_lock); + list_for_each_entry_safe(node, tmp, &to_typec_port(connector)->port_list, list) { + ret = node->dev == port; + if (ret) { + unlink_port(to_typec_port(connector), node); + remove_port_node(node); + put_device(connector); + break; + } + } + mutex_unlock(&to_typec_port(connector)->port_list_lock); + + return ret; +} + +/** + * typec_unlink_port - Unlink port from its connector + * @port: The port device + * + * Removes the symlink "connector" and decrements the reference count of @port. + */ +void typec_unlink_port(struct device *port) +{ + class_for_each_device(&typec_class, NULL, port, port_match_and_unlink); +} +EXPORT_SYMBOL_GPL(typec_unlink_port); + +static int each_port(struct device *port, void *connector) +{ + struct port_node *node; + int ret; + + node = create_port_node(port); + if (IS_ERR(node)) + return PTR_ERR(node); + + if (!connector_match(connector, node)) { + remove_port_node(node); + return 0; + } + + ret = link_port(to_typec_port(connector), node); + if (ret) { + remove_port_node(node->pld); + return ret; + } + + get_device(connector); + + return 0; +} + +int typec_link_ports(struct typec_port *con) +{ + int ret = 0; + + con->pld = get_pld(&con->dev); + if (!con->pld) + return 0; + + ret = usb_for_each_port(&con->dev, each_port); + if (ret) + typec_unlink_ports(con); + + return ret; +} + +void typec_unlink_ports(struct typec_port *con) +{ + struct port_node *node; + struct port_node *tmp; + + mutex_lock(&con->port_list_lock); + + list_for_each_entry_safe(node, tmp, &con->port_list, list) { + __unlink_port(con, node); + remove_port_node(node); + put_device(&con->dev); + } + + mutex_unlock(&con->port_list_lock); + + free_pld(con->pld); +} diff --git a/drivers/usb/typec/stusb160x.c b/drivers/usb/typec/stusb160x.c index d21750bbbb44..6eaeba9b096e 100644 --- a/drivers/usb/typec/stusb160x.c +++ b/drivers/usb/typec/stusb160x.c @@ -682,8 +682,8 @@ static int stusb160x_probe(struct i2c_client *client) } fwnode = device_get_named_child_node(chip->dev, "connector"); - if (IS_ERR(fwnode)) - return PTR_ERR(fwnode); + if (!fwnode) + return -ENODEV; /* * When both VDD and VSYS power supplies are present, the low power diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c index ebc46b9f776c..7a2a17866a82 100644 --- a/drivers/usb/typec/tcpm/fusb302.c +++ b/drivers/usb/typec/tcpm/fusb302.c @@ -213,8 +213,9 @@ static void fusb302_debugfs_init(struct fusb302_chip *chip) mutex_init(&chip->logbuffer_lock); snprintf(name, NAME_MAX, "fusb302-%s", dev_name(chip->dev)); - chip->dentry = debugfs_create_file(name, S_IFREG | 0444, usb_debug_root, - chip, &fusb302_debug_fops); + chip->dentry = debugfs_create_dir(name, usb_debug_root); + debugfs_create_file("log", S_IFREG | 0444, chip->dentry, chip, + &fusb302_debug_fops); } static void fusb302_debugfs_exit(struct fusb302_chip *chip) diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c index a27deb0b5f03..25b480752266 100644 --- a/drivers/usb/typec/tcpm/tcpci.c +++ b/drivers/usb/typec/tcpm/tcpci.c @@ -24,6 +24,11 @@ #define AUTO_DISCHARGE_PD_HEADROOM_MV 850 #define AUTO_DISCHARGE_PPS_HEADROOM_MV 1250 +#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))) + struct tcpci { struct device *dev; @@ -178,19 +183,25 @@ static int tcpci_get_cc(struct tcpc_dev *tcpc, enum typec_cc_status *cc1, enum typec_cc_status *cc2) { struct tcpci *tcpci = tcpc_to_tcpci(tcpc); - unsigned int reg; + unsigned int reg, role_control; int ret; + ret = regmap_read(tcpci->regmap, TCPC_ROLE_CTRL, &role_control); + if (ret < 0) + return ret; + ret = regmap_read(tcpci->regmap, TCPC_CC_STATUS, ®); if (ret < 0) return ret; *cc1 = tcpci_to_typec_cc((reg >> TCPC_CC_STATUS_CC1_SHIFT) & TCPC_CC_STATUS_CC1_MASK, - reg & TCPC_CC_STATUS_TERM); + 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, - reg & TCPC_CC_STATUS_TERM); + reg & TCPC_CC_STATUS_TERM || + tcpc_presenting_rd(role_control, CC2)); return 0; } diff --git a/drivers/usb/typec/tcpm/tcpci.h b/drivers/usb/typec/tcpm/tcpci.h index 57b6e24e0a0c..2be7a77d400e 100644 --- a/drivers/usb/typec/tcpm/tcpci.h +++ b/drivers/usb/typec/tcpm/tcpci.h @@ -47,7 +47,10 @@ #define TCPC_TCPC_CTRL 0x19 #define TCPC_TCPC_CTRL_ORIENTATION BIT(0) +#define PLUG_ORNT_CC1 0 +#define PLUG_ORNT_CC2 1 #define TCPC_TCPC_CTRL_BIST_TM BIT(1) +#define TCPC_TCPC_CTRL_EN_LK4CONN_ALRT BIT(6) #define TCPC_EXTENDED_STATUS 0x20 #define TCPC_EXTENDED_STATUS_VSAFE0V BIT(0) @@ -74,21 +77,28 @@ #define TCPC_POWER_CTRL_VCONN_ENABLE BIT(0) #define TCPC_POWER_CTRL_BLEED_DISCHARGE BIT(3) #define TCPC_POWER_CTRL_AUTO_DISCHARGE BIT(4) +#define TCPC_DIS_VOLT_ALRM BIT(5) +#define TCPC_POWER_CTRL_VBUS_VOLT_MON BIT(6) #define TCPC_FAST_ROLE_SWAP_EN BIT(7) #define TCPC_CC_STATUS 0x1d #define TCPC_CC_STATUS_TOGGLING BIT(5) #define TCPC_CC_STATUS_TERM BIT(4) +#define TCPC_CC_STATUS_TERM_RP 0 +#define TCPC_CC_STATUS_TERM_RD 1 +#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) #define TCPC_POWER_STATUS_UNINIT BIT(6) #define TCPC_POWER_STATUS_SOURCING_VBUS BIT(4) #define TCPC_POWER_STATUS_VBUS_DET BIT(3) #define TCPC_POWER_STATUS_VBUS_PRES BIT(2) +#define TCPC_POWER_STATUS_SINKING_VBUS BIT(0) #define TCPC_FAULT_STATUS 0x1f @@ -121,6 +131,10 @@ #define TCPC_RX_DETECT 0x2f #define TCPC_RX_DETECT_HARD_RESET BIT(5) #define TCPC_RX_DETECT_SOP BIT(0) +#define TCPC_RX_DETECT_SOP1 BIT(1) +#define TCPC_RX_DETECT_SOP2 BIT(2) +#define TCPC_RX_DETECT_DBG1 BIT(3) +#define TCPC_RX_DETECT_DBG2 BIT(4) #define TCPC_RX_BYTE_CNT 0x30 #define TCPC_RX_BUF_FRAME_TYPE 0x31 @@ -139,6 +153,8 @@ #define TCPC_TX_DATA 0x54 /* through 0x6f */ #define TCPC_VBUS_VOLTAGE 0x70 +#define TCPC_VBUS_VOLTAGE_MASK 0x3ff +#define TCPC_VBUS_VOLTAGE_LSB_MV 25 #define TCPC_VBUS_SINK_DISCONNECT_THRESH 0x72 #define TCPC_VBUS_SINK_DISCONNECT_THRESH_LSB_MV 25 #define TCPC_VBUS_SINK_DISCONNECT_THRESH_MAX 0x3ff diff --git a/drivers/usb/typec/tcpm/tcpci_maxim.c b/drivers/usb/typec/tcpm/tcpci_maxim.c index 041a1c393594..df2505570f07 100644 --- a/drivers/usb/typec/tcpm/tcpci_maxim.c +++ b/drivers/usb/typec/tcpm/tcpci_maxim.c @@ -52,7 +52,7 @@ static const struct regmap_range max_tcpci_tcpci_range[] = { regmap_reg_range(0x00, 0x95) }; -const struct regmap_access_table max_tcpci_tcpci_write_table = { +static const struct regmap_access_table max_tcpci_tcpci_write_table = { .yes_ranges = max_tcpci_tcpci_range, .n_yes_ranges = ARRAY_SIZE(max_tcpci_tcpci_range), }; diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index ce7af398c7c1..c4fdc00a3bc8 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -268,12 +268,27 @@ struct pd_mode_data { struct typec_altmode_desc altmode_desc[ALTMODE_DISCOVERY_MAX]; }; +/* + * @min_volt: Actual min voltage at the local port + * @req_min_volt: Requested min voltage to the port partner + * @max_volt: Actual max voltage at the local port + * @req_max_volt: Requested max voltage to the port partner + * @max_curr: Actual max current at the local port + * @req_max_curr: Requested max current of the port partner + * @req_out_volt: Requested output voltage to the port partner + * @req_op_curr: Requested operating current to the port partner + * @supported: Parter has atleast one APDO hence supports PPS + * @active: PPS mode is active + */ struct pd_pps_data { u32 min_volt; + u32 req_min_volt; u32 max_volt; + u32 req_max_volt; u32 max_curr; - u32 out_volt; - u32 op_curr; + u32 req_max_curr; + u32 req_out_volt; + u32 req_op_curr; bool supported; bool active; }; @@ -389,7 +404,10 @@ struct tcpm_port { unsigned int operating_snk_mw; bool update_sink_caps; - /* Requested current / voltage */ + /* Requested current / voltage to the port partner */ + u32 req_current_limit; + u32 req_supply_voltage; + /* Actual current / voltage limit of the local port */ u32 current_limit; u32 supply_voltage; @@ -438,6 +456,15 @@ struct tcpm_port { enum tcpm_ams next_ams; bool in_ams; + /* Auto vbus discharge status */ + bool auto_vbus_discharge_enabled; + + /* + * When set, port requests PD_P_SNK_STDBY_MW upon entering SNK_DISCOVERY and + * the actual currrent limit after RX of PD_CTRL_PSRDY for PD link, + * SNK_READY for non-pd link. + */ + bool slow_charger_loop; #ifdef CONFIG_DEBUG_FS struct dentry *dentry; struct mutex logbuffer_lock; /* log buffer access lock */ @@ -507,6 +534,9 @@ static const char * const pd_rev[] = { (tcpm_port_is_sink(port) && \ ((port)->cc1 == TYPEC_CC_RP_3_0 || (port)->cc2 == TYPEC_CC_RP_3_0)) +#define tcpm_wait_for_discharge(port) \ + (((port)->auto_vbus_discharge_enabled && !(port)->vbus_vsafe0v) ? PD_T_SAFE_0V : 0) + static enum tcpm_state tcpm_default_state(struct tcpm_port *port) { if (port->port_type == TYPEC_PORT_DRP) { @@ -703,8 +733,9 @@ static void tcpm_debugfs_init(struct tcpm_port *port) mutex_init(&port->logbuffer_lock); snprintf(name, NAME_MAX, "tcpm-%s", dev_name(port->dev)); - port->dentry = debugfs_create_file(name, S_IFREG | 0444, usb_debug_root, - port, &tcpm_debug_fops); + port->dentry = debugfs_create_dir(name, usb_debug_root); + debugfs_create_file("log", S_IFREG | 0444, port->dentry, port, + &tcpm_debug_fops); } static void tcpm_debugfs_exit(struct tcpm_port *port) @@ -773,10 +804,8 @@ static enum typec_cc_status tcpm_rp_cc(struct tcpm_port *port) return TYPEC_CC_RP_DEF; } -static int tcpm_ams_finish(struct tcpm_port *port) +static void tcpm_ams_finish(struct tcpm_port *port) { - int ret = 0; - tcpm_log(port, "AMS %s finished", tcpm_ams_str[port->ams]); if (port->pd_capable && port->pwr_role == TYPEC_SOURCE) { @@ -790,8 +819,6 @@ static int tcpm_ams_finish(struct tcpm_port *port) port->in_ams = false; port->ams = NONE_AMS; - - return ret; } static int tcpm_pd_transmit(struct tcpm_port *port, @@ -2432,8 +2459,8 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, case SNK_TRANSITION_SINK: if (port->vbus_present) { tcpm_set_current_limit(port, - port->current_limit, - port->supply_voltage); + port->req_current_limit, + port->req_supply_voltage); port->explicit_contract = true; tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_PD, @@ -2492,8 +2519,8 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, break; case SNK_NEGOTIATE_PPS_CAPABILITIES: /* Revert data back from any requested PPS updates */ - port->pps_data.out_volt = port->supply_voltage; - port->pps_data.op_curr = port->current_limit; + port->pps_data.req_out_volt = port->supply_voltage; + port->pps_data.req_op_curr = port->current_limit; port->pps_status = (type == PD_CTRL_WAIT ? -EAGAIN : -EOPNOTSUPP); @@ -2542,8 +2569,12 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, break; case SNK_NEGOTIATE_PPS_CAPABILITIES: port->pps_data.active = true; - port->supply_voltage = port->pps_data.out_volt; - port->current_limit = port->pps_data.op_curr; + port->pps_data.min_volt = port->pps_data.req_min_volt; + port->pps_data.max_volt = port->pps_data.req_max_volt; + port->pps_data.max_curr = port->pps_data.req_max_curr; + port->req_supply_voltage = port->pps_data.req_out_volt; + port->req_current_limit = port->pps_data.req_op_curr; + power_supply_changed(port->psy); tcpm_set_state(port, SNK_TRANSITION_SINK, 0); break; case SOFT_RESET_SEND: @@ -3102,17 +3133,16 @@ static unsigned int tcpm_pd_select_pps_apdo(struct tcpm_port *port) src = port->source_caps[src_pdo]; snk = port->snk_pdo[snk_pdo]; - port->pps_data.min_volt = max(pdo_pps_apdo_min_voltage(src), - pdo_pps_apdo_min_voltage(snk)); - port->pps_data.max_volt = min(pdo_pps_apdo_max_voltage(src), - pdo_pps_apdo_max_voltage(snk)); - port->pps_data.max_curr = min_pps_apdo_current(src, snk); - port->pps_data.out_volt = min(port->pps_data.max_volt, - max(port->pps_data.min_volt, - port->pps_data.out_volt)); - port->pps_data.op_curr = min(port->pps_data.max_curr, - port->pps_data.op_curr); - power_supply_changed(port->psy); + port->pps_data.req_min_volt = max(pdo_pps_apdo_min_voltage(src), + pdo_pps_apdo_min_voltage(snk)); + port->pps_data.req_max_volt = min(pdo_pps_apdo_max_voltage(src), + pdo_pps_apdo_max_voltage(snk)); + port->pps_data.req_max_curr = min_pps_apdo_current(src, snk); + port->pps_data.req_out_volt = min(port->pps_data.req_max_volt, + max(port->pps_data.req_min_volt, + port->pps_data.req_out_volt)); + port->pps_data.req_op_curr = min(port->pps_data.req_max_curr, + port->pps_data.req_op_curr); } return src_pdo; @@ -3192,8 +3222,8 @@ static int tcpm_pd_build_request(struct tcpm_port *port, u32 *rdo) flags & RDO_CAP_MISMATCH ? " [mismatch]" : ""); } - port->current_limit = ma; - port->supply_voltage = mv; + port->req_current_limit = ma; + port->req_supply_voltage = mv; return 0; } @@ -3239,10 +3269,10 @@ static int tcpm_pd_build_pps_request(struct tcpm_port *port, u32 *rdo) tcpm_log(port, "Invalid APDO selected!"); return -EINVAL; } - max_mv = port->pps_data.max_volt; - max_ma = port->pps_data.max_curr; - out_mv = port->pps_data.out_volt; - op_ma = port->pps_data.op_curr; + max_mv = port->pps_data.req_max_volt; + max_ma = port->pps_data.req_max_curr; + out_mv = port->pps_data.req_out_volt; + op_ma = port->pps_data.req_op_curr; break; default: tcpm_log(port, "Invalid PDO selected!"); @@ -3289,8 +3319,8 @@ static int tcpm_pd_build_pps_request(struct tcpm_port *port, u32 *rdo) tcpm_log(port, "Requesting APDO %d: %u mV, %u mA", src_pdo_index, out_mv, op_ma); - port->pps_data.op_curr = op_ma; - port->pps_data.out_volt = out_mv; + port->pps_data.req_op_curr = op_ma; + port->pps_data.req_out_volt = out_mv; return 0; } @@ -3418,6 +3448,8 @@ static int tcpm_src_attach(struct tcpm_port *port) if (port->tcpc->enable_auto_vbus_discharge) { ret = port->tcpc->enable_auto_vbus_discharge(port->tcpc, true); tcpm_log_force(port, "enable vbus discharge ret:%d", ret); + if (!ret) + port->auto_vbus_discharge_enabled = true; } ret = tcpm_set_roles(port, true, TYPEC_SOURCE, tcpm_data_role_for_source(port)); @@ -3500,6 +3532,8 @@ static void tcpm_reset_port(struct tcpm_port *port) if (port->tcpc->enable_auto_vbus_discharge) { ret = port->tcpc->enable_auto_vbus_discharge(port->tcpc, false); tcpm_log_force(port, "Disable vbus discharge ret:%d", ret); + if (!ret) + port->auto_vbus_discharge_enabled = false; } port->in_ams = false; port->ams = NONE_AMS; @@ -3533,8 +3567,6 @@ static void tcpm_reset_port(struct tcpm_port *port) port->sink_cap_done = false; if (port->tcpc->enable_frs) port->tcpc->enable_frs(port->tcpc, false); - - power_supply_changed(port->psy); } static void tcpm_detach(struct tcpm_port *port) @@ -3574,6 +3606,8 @@ static int tcpm_snk_attach(struct tcpm_port *port) tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_USB, false, VSAFE5V); ret = port->tcpc->enable_auto_vbus_discharge(port->tcpc, true); tcpm_log_force(port, "enable vbus discharge ret:%d", ret); + if (!ret) + port->auto_vbus_discharge_enabled = true; } ret = tcpm_set_roles(port, true, TYPEC_SINK, tcpm_data_role_for_sink(port)); @@ -3650,8 +3684,8 @@ static inline enum tcpm_state unattached_state(struct tcpm_port *port) static void tcpm_check_send_discover(struct tcpm_port *port) { - if (port->data_role == TYPEC_HOST && port->send_discover && - port->pd_capable) + if ((port->data_role == TYPEC_HOST || port->negotiated_rev > PD_REV20) && + port->send_discover && port->pd_capable) tcpm_send_vdm(port, USB_SID_PD, CMD_DISCOVER_IDENT, NULL, 0); port->send_discover = false; } @@ -4019,9 +4053,11 @@ static void run_state_machine(struct tcpm_port *port) break; case SNK_DISCOVERY: if (port->vbus_present) { - tcpm_set_current_limit(port, - tcpm_get_current_limit(port), - 5000); + u32 current_lim = tcpm_get_current_limit(port); + + if (port->slow_charger_loop || (current_lim > PD_P_SNK_STDBY_MW / 5)) + current_lim = PD_P_SNK_STDBY_MW / 5; + tcpm_set_current_limit(port, current_lim, 5000); tcpm_set_charge(port, true); tcpm_set_state(port, SNK_WAIT_CAPABILITIES, 0); break; @@ -4103,6 +4139,23 @@ static void run_state_machine(struct tcpm_port *port) } break; case SNK_TRANSITION_SINK: + /* From the USB PD spec: + * "The Sink Shall transition to Sink Standby before a positive or + * negative voltage transition of VBUS. During Sink Standby + * the Sink Shall reduce its power draw to pSnkStdby." + * + * This is not applicable to PPS though as the port can continue + * to draw negotiated power without switching to standby. + */ + if (port->supply_voltage != port->req_supply_voltage && !port->pps_data.active && + port->current_limit * port->supply_voltage / 1000 > PD_P_SNK_STDBY_MW) { + u32 stdby_ma = PD_P_SNK_STDBY_MW * 1000 / port->supply_voltage; + + tcpm_log(port, "Setting standby current %u mV @ %u mA", + port->supply_voltage, stdby_ma); + tcpm_set_current_limit(port, stdby_ma, port->supply_voltage); + } + fallthrough; case SNK_TRANSITION_SINK_VBUS: tcpm_set_state(port, hard_reset_state(port), PD_T_PS_TRANSITION); @@ -4116,6 +4169,8 @@ static void run_state_machine(struct tcpm_port *port) port->pwr_opmode = TYPEC_PWR_MODE_PD; } + if (!port->pd_capable && port->slow_charger_loop) + tcpm_set_current_limit(port, tcpm_get_current_limit(port), 5000); tcpm_swap_complete(port, 0); tcpm_typec_connect(port); mod_enable_frs_delayed_work(port, 0); @@ -4676,9 +4731,9 @@ static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1, if (tcpm_port_is_disconnected(port) || !tcpm_port_is_source(port)) { if (port->port_type == TYPEC_PORT_SRC) - tcpm_set_state(port, SRC_UNATTACHED, 0); + tcpm_set_state(port, SRC_UNATTACHED, tcpm_wait_for_discharge(port)); else - tcpm_set_state(port, SNK_UNATTACHED, 0); + tcpm_set_state(port, SNK_UNATTACHED, tcpm_wait_for_discharge(port)); } break; case SNK_UNATTACHED: @@ -4709,7 +4764,23 @@ static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1, tcpm_set_state(port, SNK_DEBOUNCED, 0); break; case SNK_READY: - if (tcpm_port_is_disconnected(port)) + /* + * EXIT condition is based primarily on vbus disconnect and CC is secondary. + * "A port that has entered into USB PD communications with the Source and + * has seen the CC voltage exceed vRd-USB may monitor the CC pin to detect + * cable disconnect in addition to monitoring VBUS. + * + * A port that is monitoring the CC voltage for disconnect (but is not in + * the process of a USB PD PR_Swap or USB PD FR_Swap) shall transition to + * Unattached.SNK within tSinkDisconnect after the CC voltage remains below + * vRd-USB for tPDDebounce." + * + * When set_auto_vbus_discharge_threshold is enabled, CC pins go + * away before vbus decays to disconnect threshold. Allow + * disconnect to be driven by vbus disconnect when auto vbus + * discharge is enabled. + */ + if (!port->auto_vbus_discharge_enabled && tcpm_port_is_disconnected(port)) tcpm_set_state(port, unattached_state(port), 0); else if (!port->pd_capable && (cc1 != old_cc1 || cc2 != old_cc2)) @@ -4808,9 +4879,13 @@ static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1, * Ignore CC changes here. */ break; - default: - if (tcpm_port_is_disconnected(port)) + /* + * While acting as sink and auto vbus discharge is enabled, Allow disconnect + * to be driven by vbus disconnect. + */ + if (tcpm_port_is_disconnected(port) && !(port->pwr_role == TYPEC_SINK && + port->auto_vbus_discharge_enabled)) tcpm_set_state(port, unattached_state(port), 0); break; } @@ -4974,8 +5049,16 @@ static void _tcpm_pd_vbus_off(struct tcpm_port *port) case SRC_TRANSITION_SUPPLY: case SRC_READY: case SRC_WAIT_NEW_CAPABILITIES: - /* Force to unattached state to re-initiate connection */ - tcpm_set_state(port, SRC_UNATTACHED, 0); + /* + * Force to unattached state to re-initiate connection. + * DRP port should move to Unattached.SNK instead of Unattached.SRC if + * sink removed. Although sink removal here is due to source's vbus collapse, + * treat it the same way for consistency. + */ + if (port->port_type == TYPEC_PORT_SRC) + tcpm_set_state(port, SRC_UNATTACHED, tcpm_wait_for_discharge(port)); + else + tcpm_set_state(port, SNK_UNATTACHED, tcpm_wait_for_discharge(port)); break; case PORT_RESET: @@ -4994,9 +5077,8 @@ static void _tcpm_pd_vbus_off(struct tcpm_port *port) break; default: - if (port->pwr_role == TYPEC_SINK && - port->attached) - tcpm_set_state(port, SNK_UNATTACHED, 0); + if (port->pwr_role == TYPEC_SINK && port->attached) + tcpm_set_state(port, SNK_UNATTACHED, tcpm_wait_for_discharge(port)); break; } } @@ -5018,7 +5100,23 @@ static void _tcpm_pd_vbus_vsafe0v(struct tcpm_port *port) tcpm_set_state(port, tcpm_try_snk(port) ? SNK_TRY : SRC_ATTACHED, PD_T_CC_DEBOUNCE); break; + case SRC_STARTUP: + case SRC_SEND_CAPABILITIES: + case SRC_SEND_CAPABILITIES_TIMEOUT: + case SRC_NEGOTIATE_CAPABILITIES: + case SRC_TRANSITION_SUPPLY: + case SRC_READY: + case SRC_WAIT_NEW_CAPABILITIES: + if (port->auto_vbus_discharge_enabled) { + if (port->port_type == TYPEC_PORT_SRC) + tcpm_set_state(port, SRC_UNATTACHED, 0); + else + tcpm_set_state(port, SNK_UNATTACHED, 0); + } + break; default: + if (port->pwr_role == TYPEC_SINK && port->auto_vbus_discharge_enabled) + tcpm_set_state(port, SNK_UNATTACHED, 0); break; } } @@ -5374,7 +5472,7 @@ static int tcpm_try_role(struct typec_port *p, int role) return ret; } -static int tcpm_pps_set_op_curr(struct tcpm_port *port, u16 op_curr) +static int tcpm_pps_set_op_curr(struct tcpm_port *port, u16 req_op_curr) { unsigned int target_mw; int ret; @@ -5392,12 +5490,12 @@ static int tcpm_pps_set_op_curr(struct tcpm_port *port, u16 op_curr) goto port_unlock; } - if (op_curr > port->pps_data.max_curr) { + if (req_op_curr > port->pps_data.max_curr) { ret = -EINVAL; goto port_unlock; } - target_mw = (op_curr * port->pps_data.out_volt) / 1000; + target_mw = (req_op_curr * port->supply_voltage) / 1000; if (target_mw < port->operating_snk_mw) { ret = -EINVAL; goto port_unlock; @@ -5411,10 +5509,10 @@ static int tcpm_pps_set_op_curr(struct tcpm_port *port, u16 op_curr) } /* Round down operating current to align with PPS valid steps */ - op_curr = op_curr - (op_curr % RDO_PROG_CURR_MA_STEP); + req_op_curr = req_op_curr - (req_op_curr % RDO_PROG_CURR_MA_STEP); reinit_completion(&port->pps_complete); - port->pps_data.op_curr = op_curr; + port->pps_data.req_op_curr = req_op_curr; port->pps_status = 0; port->pps_pending = true; mutex_unlock(&port->lock); @@ -5435,7 +5533,7 @@ static int tcpm_pps_set_op_curr(struct tcpm_port *port, u16 op_curr) return ret; } -static int tcpm_pps_set_out_volt(struct tcpm_port *port, u16 out_volt) +static int tcpm_pps_set_out_volt(struct tcpm_port *port, u16 req_out_volt) { unsigned int target_mw; int ret; @@ -5453,13 +5551,13 @@ static int tcpm_pps_set_out_volt(struct tcpm_port *port, u16 out_volt) goto port_unlock; } - if (out_volt < port->pps_data.min_volt || - out_volt > port->pps_data.max_volt) { + if (req_out_volt < port->pps_data.min_volt || + req_out_volt > port->pps_data.max_volt) { ret = -EINVAL; goto port_unlock; } - target_mw = (port->pps_data.op_curr * out_volt) / 1000; + target_mw = (port->current_limit * req_out_volt) / 1000; if (target_mw < port->operating_snk_mw) { ret = -EINVAL; goto port_unlock; @@ -5473,10 +5571,10 @@ static int tcpm_pps_set_out_volt(struct tcpm_port *port, u16 out_volt) } /* Round down output voltage to align with PPS valid steps */ - out_volt = out_volt - (out_volt % RDO_PROG_VOLT_MV_STEP); + req_out_volt = req_out_volt - (req_out_volt % RDO_PROG_VOLT_MV_STEP); reinit_completion(&port->pps_complete); - port->pps_data.out_volt = out_volt; + port->pps_data.req_out_volt = req_out_volt; port->pps_status = 0; port->pps_pending = true; mutex_unlock(&port->lock); @@ -5534,8 +5632,8 @@ static int tcpm_pps_activate(struct tcpm_port *port, bool activate) /* Trigger PPS request or move back to standard PDO contract */ if (activate) { - port->pps_data.out_volt = port->supply_voltage; - port->pps_data.op_curr = port->current_limit; + port->pps_data.req_out_volt = port->supply_voltage; + port->pps_data.req_op_curr = port->current_limit; } mutex_unlock(&port->lock); @@ -5675,6 +5773,7 @@ static int tcpm_fw_get_caps(struct tcpm_port *port, port->typec_caps.type = ret; port->port_type = port->typec_caps.type; + port->slow_charger_loop = fwnode_property_read_bool(fwnode, "slow-charger-loop"); if (port->port_type == TYPEC_PORT_SNK) goto sink; @@ -6072,6 +6171,10 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) goto out_role_sw_put; } + typec_port_register_altmodes(port->typec_port, + &tcpm_altmode_ops, port, + port->port_altmode, ALTMODE_DISCOVERY_MAX); + mutex_lock(&port->lock); tcpm_init(port); mutex_unlock(&port->lock); diff --git a/drivers/usb/typec/tipd/Kconfig b/drivers/usb/typec/tipd/Kconfig new file mode 100644 index 000000000000..b82715293072 --- /dev/null +++ b/drivers/usb/typec/tipd/Kconfig @@ -0,0 +1,12 @@ +config TYPEC_TPS6598X + tristate "TI TPS6598x USB Power Delivery controller driver" + depends on I2C + select POWER_SUPPLY + select REGMAP_I2C + select USB_ROLE_SWITCH + help + Say Y or M here if your system has TI TPS65982 or TPS65983 USB Power + Delivery controller. + + If you choose to build this driver as a dynamically linked module, the + module will be called tps6598x.ko. diff --git a/drivers/usb/typec/tipd/Makefile b/drivers/usb/typec/tipd/Makefile new file mode 100644 index 000000000000..aa439f80a889 --- /dev/null +++ b/drivers/usb/typec/tipd/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +CFLAGS_trace.o := -I$(src) + +obj-$(CONFIG_TYPEC_TPS6598X) += tps6598x.o +tps6598x-y := core.o +tps6598x-$(CONFIG_TRACING) += trace.o diff --git a/drivers/usb/typec/tps6598x.c b/drivers/usb/typec/tipd/core.c similarity index 91% rename from drivers/usb/typec/tps6598x.c rename to drivers/usb/typec/tipd/core.c index 29bd1c5a283c..938219bc1b4b 100644 --- a/drivers/usb/typec/tps6598x.c +++ b/drivers/usb/typec/tipd/core.c @@ -15,6 +15,9 @@ #include #include +#include "tps6598x.h" +#include "trace.h" + /* Register offsets */ #define TPS_REG_VID 0x00 #define TPS_REG_MODE 0x03 @@ -31,16 +34,7 @@ #define TPS_REG_CTRL_CONF 0x29 #define TPS_REG_POWER_STATUS 0x3f #define TPS_REG_RX_IDENTITY_SOP 0x48 - -/* TPS_REG_INT_* bits */ -#define TPS_REG_INT_PLUG_EVENT BIT(3) - -/* TPS_REG_STATUS bits */ -#define TPS_STATUS_PLUG_PRESENT BIT(0) -#define TPS_STATUS_ORIENTATION BIT(4) -#define TPS_STATUS_PORTROLE(s) (!!((s) & BIT(5))) -#define TPS_STATUS_DATAROLE(s) (!!((s) & BIT(6))) -#define TPS_STATUS_VCONN(s) (!!((s) & BIT(7))) +#define TPS_REG_DATA_STATUS 0x5f /* TPS_REG_SYSTEM_CONF bits */ #define TPS_SYSCONF_PORTINFO(c) ((c) & 7) @@ -55,11 +49,6 @@ enum { TPS_PORTINFO_SOURCE, }; -/* TPS_REG_POWER_STATUS bits */ -#define TPS_POWER_STATUS_CONNECTION BIT(0) -#define TPS_POWER_STATUS_SOURCESINK BIT(1) -#define TPS_POWER_STATUS_PWROPMODE(p) (((p) & GENMASK(3, 2)) >> 2) - /* TPS_REG_RX_IDENTITY_SOP */ struct tps6598x_rx_identity_reg { u8 status; @@ -255,9 +244,9 @@ static int tps6598x_connect(struct tps6598x *tps, u32 status) } typec_set_pwr_opmode(tps->port, mode); - typec_set_pwr_role(tps->port, TPS_STATUS_PORTROLE(status)); - typec_set_vconn_role(tps->port, TPS_STATUS_VCONN(status)); - tps6598x_set_data_role(tps, TPS_STATUS_DATAROLE(status), true); + typec_set_pwr_role(tps->port, TPS_STATUS_TO_TYPEC_PORTROLE(status)); + typec_set_vconn_role(tps->port, TPS_STATUS_TO_TYPEC_VCONN(status)); + tps6598x_set_data_role(tps, TPS_STATUS_TO_TYPEC_DATAROLE(status), true); tps->partner = typec_register_partner(tps->port, &desc); if (IS_ERR(tps->partner)) @@ -277,9 +266,10 @@ static void tps6598x_disconnect(struct tps6598x *tps, u32 status) typec_unregister_partner(tps->partner); tps->partner = NULL; typec_set_pwr_opmode(tps->port, TYPEC_PWR_MODE_USB); - typec_set_pwr_role(tps->port, TPS_STATUS_PORTROLE(status)); - typec_set_vconn_role(tps->port, TPS_STATUS_VCONN(status)); - tps6598x_set_data_role(tps, TPS_STATUS_DATAROLE(status), false); + typec_set_pwr_role(tps->port, TPS_STATUS_TO_TYPEC_PORTROLE(status)); + typec_set_vconn_role(tps->port, TPS_STATUS_TO_TYPEC_VCONN(status)); + tps6598x_set_data_role(tps, TPS_STATUS_TO_TYPEC_DATAROLE(status), false); + power_supply_changed(tps->psy); } @@ -363,7 +353,7 @@ static int tps6598x_dr_set(struct typec_port *port, enum typec_data_role role) if (ret) goto out_unlock; - if (role != TPS_STATUS_DATAROLE(status)) { + if (role != TPS_STATUS_TO_TYPEC_DATAROLE(status)) { ret = -EPROTO; goto out_unlock; } @@ -393,7 +383,7 @@ static int tps6598x_pr_set(struct typec_port *port, enum typec_role role) if (ret) goto out_unlock; - if (role != TPS_STATUS_PORTROLE(status)) { + if (role != TPS_STATUS_TO_TYPEC_PORTROLE(status)) { ret = -EPROTO; goto out_unlock; } @@ -416,7 +406,8 @@ static irqreturn_t tps6598x_interrupt(int irq, void *data) struct tps6598x *tps = data; u64 event1; u64 event2; - u32 status; + u32 status, data_status; + u16 pwr_status; int ret; mutex_lock(&tps->lock); @@ -427,12 +418,32 @@ static irqreturn_t tps6598x_interrupt(int irq, void *data) dev_err(tps->dev, "%s: failed to read events\n", __func__); goto err_unlock; } + trace_tps6598x_irq(event1, event2); ret = tps6598x_read32(tps, TPS_REG_STATUS, &status); if (ret) { dev_err(tps->dev, "%s: failed to read status\n", __func__); goto err_clear_ints; } + trace_tps6598x_status(status); + + if ((event1 | event2) & TPS_REG_INT_POWER_STATUS_UPDATE) { + ret = tps6598x_read16(tps, TPS_REG_POWER_STATUS, &pwr_status); + if (ret < 0) { + dev_err(tps->dev, "failed to read power status: %d\n", ret); + goto err_clear_ints; + } + trace_tps6598x_power_status(pwr_status); + } + + if ((event1 | event2) & TPS_REG_INT_DATA_STATUS_UPDATE) { + ret = tps6598x_read32(tps, TPS_REG_DATA_STATUS, &data_status); + if (ret < 0) { + dev_err(tps->dev, "failed to read data status: %d\n", ret); + goto err_clear_ints; + } + trace_tps6598x_data_status(data_status); + } /* Handle plug insert or removal */ if ((event1 | event2) & TPS_REG_INT_PLUG_EVENT) { @@ -498,8 +509,8 @@ static int tps6598x_psy_get_online(struct tps6598x *tps, if (ret < 0) return ret; - if ((pwr_status & TPS_POWER_STATUS_CONNECTION) && - (pwr_status & TPS_POWER_STATUS_SOURCESINK)) { + if (TPS_POWER_STATUS_CONNECTION(pwr_status) && + TPS_POWER_STATUS_SOURCESINK(pwr_status)) { val->intval = 1; } else { val->intval = 0; @@ -608,14 +619,15 @@ static int tps6598x_probe(struct i2c_client *client) ret = tps6598x_read32(tps, TPS_REG_STATUS, &status); if (ret < 0) return ret; + trace_tps6598x_status(status); ret = tps6598x_read32(tps, TPS_REG_SYSTEM_CONF, &conf); if (ret < 0) return ret; fwnode = device_get_named_child_node(&client->dev, "connector"); - if (IS_ERR(fwnode)) - return PTR_ERR(fwnode); + if (!fwnode) + return -ENODEV; tps->role_sw = fwnode_usb_role_switch_get(fwnode); if (IS_ERR(tps->role_sw)) { diff --git a/drivers/usb/typec/tipd/tps6598x.h b/drivers/usb/typec/tipd/tps6598x.h new file mode 100644 index 000000000000..003a577be216 --- /dev/null +++ b/drivers/usb/typec/tipd/tps6598x.h @@ -0,0 +1,189 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Driver for TI TPS6598x USB Power Delivery controller family + * + * Copyright (C) 2017, Intel Corporation + * Author: Heikki Krogerus + */ + +#include +#include + +#ifndef __TPS6598X_H__ +#define __TPS6598X_H__ + +#define TPS_FIELD_GET(_mask, _reg) ((typeof(_mask))(((_reg) & (_mask)) >> __bf_shf(_mask))) + +/* TPS_REG_STATUS bits */ +#define TPS_STATUS_PLUG_PRESENT BIT(0) +#define TPS_STATUS_PLUG_UPSIDE_DOWN BIT(4) +#define TPS_STATUS_PORTROLE BIT(5) +#define TPS_STATUS_TO_TYPEC_PORTROLE(s) (!!((s) & TPS_STATUS_PORTROLE)) +#define TPS_STATUS_DATAROLE BIT(6) +#define TPS_STATUS_TO_TYPEC_DATAROLE(s) (!!((s) & TPS_STATUS_DATAROLE)) +#define TPS_STATUS_VCONN BIT(7) +#define TPS_STATUS_TO_TYPEC_VCONN(s) (!!((s) & TPS_STATUS_VCONN)) +#define TPS_STATUS_OVERCURRENT BIT(16) +#define TPS_STATUS_GOTO_MIN_ACTIVE BIT(26) +#define TPS_STATUS_BIST BIT(27) +#define TPS_STATUS_HIGH_VOLAGE_WARNING BIT(28) +#define TPS_STATUS_HIGH_LOW_VOLTAGE_WARNING BIT(29) + +#define TPS_STATUS_CONN_STATE_MASK GENMASK(3, 1) +#define TPS_STATUS_CONN_STATE(x) TPS_FIELD_GET(TPS_STATUS_CONN_STATE_MASK, (x)) +#define TPS_STATUS_PP_5V0_SWITCH_MASK GENMASK(9, 8) +#define TPS_STATUS_PP_5V0_SWITCH(x) TPS_FIELD_GET(TPS_STATUS_PP_5V0_SWITCH_MASK, (x)) +#define TPS_STATUS_PP_HV_SWITCH_MASK GENMASK(11, 10) +#define TPS_STATUS_PP_HV_SWITCH(x) TPS_FIELD_GET(TPS_STATUS_PP_HV_SWITCH_MASK, (x)) +#define TPS_STATUS_PP_EXT_SWITCH_MASK GENMASK(13, 12) +#define TPS_STATUS_PP_EXT_SWITCH(x) TPS_FIELD_GET(TPS_STATUS_PP_EXT_SWITCH_MASK, (x)) +#define TPS_STATUS_PP_CABLE_SWITCH_MASK GENMASK(15, 14) +#define TPS_STATUS_PP_CABLE_SWITCH(x) TPS_FIELD_GET(TPS_STATUS_PP_CABLE_SWITCH_MASK, (x)) +#define TPS_STATUS_POWER_SOURCE_MASK GENMASK(19, 18) +#define TPS_STATUS_POWER_SOURCE(x) TPS_FIELD_GET(TPS_STATUS_POWER_SOURCE_MASK, (x)) +#define TPS_STATUS_VBUS_STATUS_MASK GENMASK(21, 20) +#define TPS_STATUS_VBUS_STATUS(x) TPS_FIELD_GET(TPS_STATUS_VBUS_STATUS_MASK, (x)) +#define TPS_STATUS_USB_HOST_PRESENT_MASK GENMASK(23, 22) +#define TPS_STATUS_USB_HOST_PRESENT(x) TPS_FIELD_GET(TPS_STATUS_USB_HOST_PRESENT_MASK, (x)) +#define TPS_STATUS_LEGACY_MASK GENMASK(25, 24) +#define TPS_STATUS_LEGACY(x) TPS_FIELD_GET(TPS_STATUS_LEGACY_MASK, (x)) + +#define TPS_STATUS_CONN_STATE_NO_CONN 0 +#define TPS_STATUS_CONN_STATE_DISABLED 1 +#define TPS_STATUS_CONN_STATE_AUDIO_CONN 2 +#define TPS_STATUS_CONN_STATE_DEBUG_CONN 3 +#define TPS_STATUS_CONN_STATE_NO_CONN_R_A 4 +#define TPS_STATUS_CONN_STATE_RESERVED 5 +#define TPS_STATUS_CONN_STATE_CONN_NO_R_A 6 +#define TPS_STATUS_CONN_STATE_CONN_WITH_R_A 7 + +#define TPS_STATUS_PP_SWITCH_STATE_DISABLED 0 +#define TPS_STATUS_PP_SWITCH_STATE_FAULT 1 +#define TPS_STATUS_PP_SWITCH_STATE_OUT 2 +#define TPS_STATUS_PP_SWITCH_STATE_IN 3 + +#define TPS_STATUS_POWER_SOURCE_UNKNOWN 0 +#define TPS_STATUS_POWER_SOURCE_VIN_3P3 1 +#define TPS_STATUS_POWER_SOURCE_DEAD_BAT 2 +#define TPS_STATUS_POWER_SOURCE_VBUS 3 + +#define TPS_STATUS_VBUS_STATUS_VSAFE0V 0 +#define TPS_STATUS_VBUS_STATUS_VSAFE5V 1 +#define TPS_STATUS_VBUS_STATUS_PD 2 +#define TPS_STATUS_VBUS_STATUS_FAULT 3 + +#define TPS_STATUS_USB_HOST_PRESENT_NO 0 +#define TPS_STATUS_USB_HOST_PRESENT_PD_NO_USB 1 +#define TPS_STATUS_USB_HOST_PRESENT_NO_PD 2 +#define TPS_STATUS_USB_HOST_PRESENT_PD_USB 3 + +#define TPS_STATUS_LEGACY_NO 0 +#define TPS_STATUS_LEGACY_SINK 1 +#define TPS_STATUS_LEGACY_SOURCE 2 + +/* TPS_REG_INT_* bits */ +#define TPS_REG_INT_USER_VID_ALT_MODE_OTHER_VDM BIT_ULL(27+32) +#define TPS_REG_INT_USER_VID_ALT_MODE_ATTN_VDM BIT_ULL(26+32) +#define TPS_REG_INT_USER_VID_ALT_MODE_EXIT BIT_ULL(25+32) +#define TPS_REG_INT_USER_VID_ALT_MODE_ENTERED BIT_ULL(24+32) +#define TPS_REG_INT_EXIT_MODES_COMPLETE BIT_ULL(20+32) +#define TPS_REG_INT_DISCOVER_MODES_COMPLETE BIT_ULL(19+32) +#define TPS_REG_INT_VDM_MSG_SENT BIT_ULL(18+32) +#define TPS_REG_INT_VDM_ENTERED_MODE BIT_ULL(17+32) +#define TPS_REG_INT_ERROR_UNABLE_TO_SOURCE BIT_ULL(14+32) +#define TPS_REG_INT_SRC_TRANSITION BIT_ULL(10+32) +#define TPS_REG_INT_ERROR_DISCHARGE_FAILED BIT_ULL(9+32) +#define TPS_REG_INT_ERROR_MESSAGE_DATA BIT_ULL(7+32) +#define TPS_REG_INT_ERROR_PROTOCOL_ERROR BIT_ULL(6+32) +#define TPS_REG_INT_ERROR_MISSING_GET_CAP_MESSAGE BIT_ULL(4+32) +#define TPS_REG_INT_ERROR_POWER_EVENT_OCCURRED BIT_ULL(3+32) +#define TPS_REG_INT_ERROR_CAN_PROVIDE_PWR_LATER BIT_ULL(2+32) +#define TPS_REG_INT_ERROR_CANNOT_PROVIDE_PWR BIT_ULL(1+32) +#define TPS_REG_INT_ERROR_DEVICE_INCOMPATIBLE BIT_ULL(0+32) +#define TPS_REG_INT_CMD2_COMPLETE BIT(31) +#define TPS_REG_INT_CMD1_COMPLETE BIT(30) +#define TPS_REG_INT_ADC_HIGH_THRESHOLD BIT(29) +#define TPS_REG_INT_ADC_LOW_THRESHOLD BIT(28) +#define TPS_REG_INT_PD_STATUS_UPDATE BIT(27) +#define TPS_REG_INT_STATUS_UPDATE BIT(26) +#define TPS_REG_INT_DATA_STATUS_UPDATE BIT(25) +#define TPS_REG_INT_POWER_STATUS_UPDATE BIT(24) +#define TPS_REG_INT_PP_SWITCH_CHANGED BIT(23) +#define TPS_REG_INT_HIGH_VOLTAGE_WARNING BIT(22) +#define TPS_REG_INT_USB_HOST_PRESENT_NO_LONGER BIT(21) +#define TPS_REG_INT_USB_HOST_PRESENT BIT(20) +#define TPS_REG_INT_GOTO_MIN_RECEIVED BIT(19) +#define TPS_REG_INT_PR_SWAP_REQUESTED BIT(17) +#define TPS_REG_INT_SINK_CAP_MESSAGE_READY BIT(15) +#define TPS_REG_INT_SOURCE_CAP_MESSAGE_READY BIT(14) +#define TPS_REG_INT_NEW_CONTRACT_AS_PROVIDER BIT(13) +#define TPS_REG_INT_NEW_CONTRACT_AS_CONSUMER BIT(12) +#define TPS_REG_INT_VDM_RECEIVED BIT(11) +#define TPS_REG_INT_ATTENTION_RECEIVED BIT(10) +#define TPS_REG_INT_OVERCURRENT BIT(9) +#define TPS_REG_INT_BIST BIT(8) +#define TPS_REG_INT_RDO_RECEIVED_FROM_SINK BIT(7) +#define TPS_REG_INT_DR_SWAP_COMPLETE BIT(5) +#define TPS_REG_INT_PR_SWAP_COMPLETE BIT(4) +#define TPS_REG_INT_PLUG_EVENT BIT(3) +#define TPS_REG_INT_HARD_RESET BIT(1) +#define TPS_REG_INT_PD_SOFT_RESET BIT(0) + +/* TPS_REG_POWER_STATUS bits */ +#define TPS_POWER_STATUS_CONNECTION(x) TPS_FIELD_GET(BIT(0), (x)) +#define TPS_POWER_STATUS_SOURCESINK(x) TPS_FIELD_GET(BIT(1), (x)) +#define TPS_POWER_STATUS_BC12_DET(x) TPS_FIELD_GET(BIT(2), (x)) + +#define TPS_POWER_STATUS_TYPEC_CURRENT_MASK GENMASK(3, 2) +#define TPS_POWER_STATUS_PWROPMODE(p) TPS_FIELD_GET(TPS_POWER_STATUS_TYPEC_CURRENT_MASK, (p)) +#define TPS_POWER_STATUS_BC12_STATUS_MASK GENMASK(6, 5) +#define TPS_POWER_STATUS_BC12_STATUS(p) TPS_FIELD_GET(TPS_POWER_STATUS_BC12_STATUS_MASK, (p)) + +#define TPS_POWER_STATUS_TYPEC_CURRENT_USB 0 +#define TPS_POWER_STATUS_TYPEC_CURRENT_1A5 1 +#define TPS_POWER_STATUS_TYPEC_CURRENT_3A0 2 +#define TPS_POWER_STATUS_TYPEC_CURRENT_PD 3 + +#define TPS_POWER_STATUS_BC12_STATUS_SDP 0 +#define TPS_POWER_STATUS_BC12_STATUS_CDP 2 +#define TPS_POWER_STATUS_BC12_STATUS_DCP 3 + +/* TPS_REG_DATA_STATUS bits */ +#define TPS_DATA_STATUS_DATA_CONNECTION BIT(0) +#define TPS_DATA_STATUS_UPSIDE_DOWN BIT(1) +#define TPS_DATA_STATUS_ACTIVE_CABLE BIT(2) +#define TPS_DATA_STATUS_USB2_CONNECTION BIT(4) +#define TPS_DATA_STATUS_USB3_CONNECTION BIT(5) +#define TPS_DATA_STATUS_USB3_GEN2 BIT(6) +#define TPS_DATA_STATUS_USB_DATA_ROLE BIT(7) +#define TPS_DATA_STATUS_DP_CONNECTION BIT(8) +#define TPS_DATA_STATUS_DP_SINK BIT(9) +#define TPS_DATA_STATUS_TBT_CONNECTION BIT(16) +#define TPS_DATA_STATUS_TBT_TYPE BIT(17) +#define TPS_DATA_STATUS_OPTICAL_CABLE BIT(18) +#define TPS_DATA_STATUS_ACTIVE_LINK_TRAIN BIT(20) +#define TPS_DATA_STATUS_FORCE_LSX BIT(23) +#define TPS_DATA_STATUS_POWER_MISMATCH BIT(24) + +#define TPS_DATA_STATUS_DP_PIN_ASSIGNMENT_MASK GENMASK(11, 10) +#define TPS_DATA_STATUS_DP_PIN_ASSIGNMENT(x) \ + TPS_FIELD_GET(TPS_DATA_STATUS_DP_PIN_ASSIGNMENT_MASK, (x)) +#define TPS_DATA_STATUS_TBT_CABLE_SPEED_MASK GENMASK(27, 25) +#define TPS_DATA_STATUS_TBT_CABLE_SPEED \ + TPS_FIELD_GET(TPS_DATA_STATUS_TBT_CABLE_SPEED_MASK, (x)) +#define TPS_DATA_STATUS_TBT_CABLE_GEN_MASK GENMASK(29, 28) +#define TPS_DATA_STATUS_TBT_CABLE_GEN \ + TPS_FIELD_GET(TPS_DATA_STATUS_TBT_CABLE_GEN_MASK, (x)) + +/* Map data status to DP spec assignments */ +#define TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT(x) \ + ((TPS_DATA_STATUS_DP_PIN_ASSIGNMENT(x) << 1) | \ + TPS_FIELD_GET(TPS_DATA_STATUS_USB3_CONNECTION, (x))) +#define TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT_E 0 +#define TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT_F BIT(0) +#define TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT_C BIT(1) +#define TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT_D (BIT(1) | BIT(0)) +#define TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT_A BIT(2) +#define TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT_B (BIT(2) | BIT(1)) + +#endif /* __TPS6598X_H__ */ diff --git a/drivers/usb/typec/tipd/trace.c b/drivers/usb/typec/tipd/trace.c new file mode 100644 index 000000000000..016e68048dc2 --- /dev/null +++ b/drivers/usb/typec/tipd/trace.c @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * TI TPS6598x USB Power Delivery Controller Trace Support + * + * Copyright (C) 2021, Intel Corporation + * Author: Heikki Krogerus + */ +#define CREATE_TRACE_POINTS +#include "trace.h" diff --git a/drivers/usb/typec/tipd/trace.h b/drivers/usb/typec/tipd/trace.h new file mode 100644 index 000000000000..5d09d6f78930 --- /dev/null +++ b/drivers/usb/typec/tipd/trace.h @@ -0,0 +1,283 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Driver for TI TPS6598x USB Power Delivery controller family + * + * Copyright (C) 2020 Purism SPC + * Author: Guido Günther + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM tps6598x + +#if !defined(_TPS6598X_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ) +#define _TPS6598X_TRACE_H_ + +#include "tps6598x.h" + +#include +#include +#include + +#define show_irq_flags(flags) \ + __print_flags_u64(flags, "|", \ + { TPS_REG_INT_PD_SOFT_RESET, "PD_SOFT_RESET" }, \ + { TPS_REG_INT_HARD_RESET, "HARD_RESET" }, \ + { TPS_REG_INT_PLUG_EVENT, "PLUG_EVENT" }, \ + { TPS_REG_INT_PR_SWAP_COMPLETE, "PR_SWAP_COMPLETE" }, \ + { TPS_REG_INT_DR_SWAP_COMPLETE, "DR_SWAP_COMPLETE" }, \ + { TPS_REG_INT_RDO_RECEIVED_FROM_SINK, "RDO_RECEIVED_FROM_SINK" }, \ + { TPS_REG_INT_BIST, "BIST" }, \ + { TPS_REG_INT_OVERCURRENT, "OVERCURRENT" }, \ + { TPS_REG_INT_ATTENTION_RECEIVED, "ATTENTION_RECEIVED" }, \ + { TPS_REG_INT_VDM_RECEIVED, "VDM_RECEIVED" }, \ + { TPS_REG_INT_NEW_CONTRACT_AS_CONSUMER, "NEW_CONTRACT_AS_CONSUMER" }, \ + { TPS_REG_INT_NEW_CONTRACT_AS_PROVIDER, "NEW_CONTRACT_AS_PROVIDER" }, \ + { TPS_REG_INT_SOURCE_CAP_MESSAGE_READY, "SOURCE_CAP_MESSAGE_READY" }, \ + { TPS_REG_INT_SINK_CAP_MESSAGE_READY, "SINK_CAP_MESSAGE_READY" }, \ + { TPS_REG_INT_PR_SWAP_REQUESTED, "PR_SWAP_REQUESTED" }, \ + { TPS_REG_INT_GOTO_MIN_RECEIVED, "GOTO_MIN_RECEIVED" }, \ + { TPS_REG_INT_USB_HOST_PRESENT, "USB_HOST_PRESENT" }, \ + { TPS_REG_INT_USB_HOST_PRESENT_NO_LONGER, "USB_HOST_PRESENT_NO_LONGER" }, \ + { TPS_REG_INT_HIGH_VOLTAGE_WARNING, "HIGH_VOLTAGE_WARNING" }, \ + { TPS_REG_INT_PP_SWITCH_CHANGED, "PP_SWITCH_CHANGED" }, \ + { TPS_REG_INT_POWER_STATUS_UPDATE, "POWER_STATUS_UPDATE" }, \ + { TPS_REG_INT_DATA_STATUS_UPDATE, "DATA_STATUS_UPDATE" }, \ + { TPS_REG_INT_STATUS_UPDATE, "STATUS_UPDATE" }, \ + { TPS_REG_INT_PD_STATUS_UPDATE, "PD_STATUS_UPDATE" }, \ + { TPS_REG_INT_ADC_LOW_THRESHOLD, "ADC_LOW_THRESHOLD" }, \ + { TPS_REG_INT_ADC_HIGH_THRESHOLD, "ADC_HIGH_THRESHOLD" }, \ + { TPS_REG_INT_CMD1_COMPLETE, "CMD1_COMPLETE" }, \ + { TPS_REG_INT_CMD2_COMPLETE, "CMD2_COMPLETE" }, \ + { TPS_REG_INT_ERROR_DEVICE_INCOMPATIBLE, "ERROR_DEVICE_INCOMPATIBLE" }, \ + { TPS_REG_INT_ERROR_CANNOT_PROVIDE_PWR, "ERROR_CANNOT_PROVIDE_PWR" }, \ + { TPS_REG_INT_ERROR_CAN_PROVIDE_PWR_LATER, "ERROR_CAN_PROVIDE_PWR_LATER" }, \ + { TPS_REG_INT_ERROR_POWER_EVENT_OCCURRED, "ERROR_POWER_EVENT_OCCURRED" }, \ + { TPS_REG_INT_ERROR_MISSING_GET_CAP_MESSAGE, "ERROR_MISSING_GET_CAP_MESSAGE" }, \ + { TPS_REG_INT_ERROR_PROTOCOL_ERROR, "ERROR_PROTOCOL_ERROR" }, \ + { TPS_REG_INT_ERROR_MESSAGE_DATA, "ERROR_MESSAGE_DATA" }, \ + { TPS_REG_INT_ERROR_DISCHARGE_FAILED, "ERROR_DISCHARGE_FAILED" }, \ + { TPS_REG_INT_SRC_TRANSITION, "SRC_TRANSITION" }, \ + { TPS_REG_INT_ERROR_UNABLE_TO_SOURCE, "ERROR_UNABLE_TO_SOURCE" }, \ + { TPS_REG_INT_VDM_ENTERED_MODE, "VDM_ENTERED_MODE" }, \ + { TPS_REG_INT_VDM_MSG_SENT, "VDM_MSG_SENT" }, \ + { TPS_REG_INT_DISCOVER_MODES_COMPLETE, "DISCOVER_MODES_COMPLETE" }, \ + { TPS_REG_INT_EXIT_MODES_COMPLETE, "EXIT_MODES_COMPLETE" }, \ + { TPS_REG_INT_USER_VID_ALT_MODE_ENTERED, "USER_VID_ALT_MODE_ENTERED" }, \ + { TPS_REG_INT_USER_VID_ALT_MODE_EXIT, "USER_VID_ALT_MODE_EXIT" }, \ + { TPS_REG_INT_USER_VID_ALT_MODE_ATTN_VDM, "USER_VID_ALT_MODE_ATTN_VDM" }, \ + { TPS_REG_INT_USER_VID_ALT_MODE_OTHER_VDM, "USER_VID_ALT_MODE_OTHER_VDM" }) + +#define TPS6598X_STATUS_FLAGS_MASK (GENMASK(31, 0) ^ (TPS_STATUS_CONN_STATE_MASK | \ + TPS_STATUS_PP_5V0_SWITCH_MASK | \ + TPS_STATUS_PP_HV_SWITCH_MASK | \ + TPS_STATUS_PP_EXT_SWITCH_MASK | \ + TPS_STATUS_PP_CABLE_SWITCH_MASK | \ + TPS_STATUS_POWER_SOURCE_MASK | \ + TPS_STATUS_VBUS_STATUS_MASK | \ + TPS_STATUS_USB_HOST_PRESENT_MASK | \ + TPS_STATUS_LEGACY_MASK)) + +#define show_status_conn_state(status) \ + __print_symbolic(TPS_STATUS_CONN_STATE((status)), \ + { TPS_STATUS_CONN_STATE_CONN_WITH_R_A, "conn-Ra" }, \ + { TPS_STATUS_CONN_STATE_CONN_NO_R_A, "conn-no-Ra" }, \ + { TPS_STATUS_CONN_STATE_NO_CONN_R_A, "no-conn-Ra" }, \ + { TPS_STATUS_CONN_STATE_DEBUG_CONN, "debug" }, \ + { TPS_STATUS_CONN_STATE_AUDIO_CONN, "audio" }, \ + { TPS_STATUS_CONN_STATE_DISABLED, "disabled" }, \ + { TPS_STATUS_CONN_STATE_NO_CONN, "no-conn" }) + +#define show_status_pp_switch_state(status) \ + __print_symbolic(status, \ + { TPS_STATUS_PP_SWITCH_STATE_IN, "in" }, \ + { TPS_STATUS_PP_SWITCH_STATE_OUT, "out" }, \ + { TPS_STATUS_PP_SWITCH_STATE_FAULT, "fault" }, \ + { TPS_STATUS_PP_SWITCH_STATE_DISABLED, "off" }) + +#define show_status_power_sources(status) \ + __print_symbolic(TPS_STATUS_POWER_SOURCE(status), \ + { TPS_STATUS_POWER_SOURCE_VBUS, "vbus" }, \ + { TPS_STATUS_POWER_SOURCE_VIN_3P3, "vin-3p3" }, \ + { TPS_STATUS_POWER_SOURCE_DEAD_BAT, "dead-battery" }, \ + { TPS_STATUS_POWER_SOURCE_UNKNOWN, "unknown" }) + +#define show_status_vbus_status(status) \ + __print_symbolic(TPS_STATUS_VBUS_STATUS(status), \ + { TPS_STATUS_VBUS_STATUS_VSAFE0V, "vSafe0V" }, \ + { TPS_STATUS_VBUS_STATUS_VSAFE5V, "vSafe5V" }, \ + { TPS_STATUS_VBUS_STATUS_PD, "pd" }, \ + { TPS_STATUS_VBUS_STATUS_FAULT, "fault" }) + +#define show_status_usb_host_present(status) \ + __print_symbolic(TPS_STATUS_USB_HOST_PRESENT(status), \ + { TPS_STATUS_USB_HOST_PRESENT_PD_USB, "pd-usb" }, \ + { TPS_STATUS_USB_HOST_PRESENT_NO_PD, "no-pd" }, \ + { TPS_STATUS_USB_HOST_PRESENT_PD_NO_USB, "pd-no-usb" }, \ + { TPS_STATUS_USB_HOST_PRESENT_NO, "no" }) + +#define show_status_legacy(status) \ + __print_symbolic(TPS_STATUS_LEGACY(status), \ + { TPS_STATUS_LEGACY_SOURCE, "source" }, \ + { TPS_STATUS_LEGACY_SINK, "sink" }, \ + { TPS_STATUS_LEGACY_NO, "no" }) + +#define show_status_flags(flags) \ + __print_flags((flags & TPS6598X_STATUS_FLAGS_MASK), "|", \ + { TPS_STATUS_PLUG_PRESENT, "PLUG_PRESENT" }, \ + { TPS_STATUS_PLUG_UPSIDE_DOWN, "UPSIDE_DOWN" }, \ + { TPS_STATUS_PORTROLE, "PORTROLE" }, \ + { TPS_STATUS_DATAROLE, "DATAROLE" }, \ + { TPS_STATUS_VCONN, "VCONN" }, \ + { TPS_STATUS_OVERCURRENT, "OVERCURRENT" }, \ + { TPS_STATUS_GOTO_MIN_ACTIVE, "GOTO_MIN_ACTIVE" }, \ + { TPS_STATUS_BIST, "BIST" }, \ + { TPS_STATUS_HIGH_VOLAGE_WARNING, "HIGH_VOLAGE_WARNING" }, \ + { TPS_STATUS_HIGH_LOW_VOLTAGE_WARNING, "HIGH_LOW_VOLTAGE_WARNING" }) + +#define show_power_status_source_sink(power_status) \ + __print_symbolic(TPS_POWER_STATUS_SOURCESINK(power_status), \ + { 1, "sink" }, \ + { 0, "source" }) + +#define show_power_status_typec_status(power_status) \ + __print_symbolic(TPS_POWER_STATUS_PWROPMODE(power_status), \ + { TPS_POWER_STATUS_TYPEC_CURRENT_PD, "pd" }, \ + { TPS_POWER_STATUS_TYPEC_CURRENT_3A0, "3.0A" }, \ + { TPS_POWER_STATUS_TYPEC_CURRENT_1A5, "1.5A" }, \ + { TPS_POWER_STATUS_TYPEC_CURRENT_USB, "usb" }) + +#define show_power_status_bc12_status(power_status) \ + __print_symbolic(TPS_POWER_STATUS_BC12_STATUS(power_status), \ + { TPS_POWER_STATUS_BC12_STATUS_DCP, "dcp" }, \ + { TPS_POWER_STATUS_BC12_STATUS_CDP, "cdp" }, \ + { TPS_POWER_STATUS_BC12_STATUS_SDP, "sdp" }) + +#define TPS_DATA_STATUS_FLAGS_MASK (GENMASK(31, 0) ^ (TPS_DATA_STATUS_DP_PIN_ASSIGNMENT_MASK | \ + TPS_DATA_STATUS_TBT_CABLE_SPEED_MASK | \ + TPS_DATA_STATUS_TBT_CABLE_GEN_MASK)) + +#define show_data_status_flags(data_status) \ + __print_flags(data_status & TPS_DATA_STATUS_FLAGS_MASK, "|", \ + { TPS_DATA_STATUS_DATA_CONNECTION, "DATA_CONNECTION" }, \ + { TPS_DATA_STATUS_UPSIDE_DOWN, "DATA_UPSIDE_DOWN" }, \ + { TPS_DATA_STATUS_ACTIVE_CABLE, "ACTIVE_CABLE" }, \ + { TPS_DATA_STATUS_USB2_CONNECTION, "USB2_CONNECTION" }, \ + { TPS_DATA_STATUS_USB3_CONNECTION, "USB3_CONNECTION" }, \ + { TPS_DATA_STATUS_USB3_GEN2, "USB3_GEN2" }, \ + { TPS_DATA_STATUS_USB_DATA_ROLE, "USB_DATA_ROLE" }, \ + { TPS_DATA_STATUS_DP_CONNECTION, "DP_CONNECTION" }, \ + { TPS_DATA_STATUS_DP_SINK, "DP_SINK" }, \ + { TPS_DATA_STATUS_TBT_CONNECTION, "TBT_CONNECTION" }, \ + { TPS_DATA_STATUS_TBT_TYPE, "TBT_TYPE" }, \ + { TPS_DATA_STATUS_OPTICAL_CABLE, "OPTICAL_CABLE" }, \ + { TPS_DATA_STATUS_ACTIVE_LINK_TRAIN, "ACTIVE_LINK_TRAIN" }, \ + { TPS_DATA_STATUS_FORCE_LSX, "FORCE_LSX" }, \ + { TPS_DATA_STATUS_POWER_MISMATCH, "POWER_MISMATCH" }) + +#define show_data_status_dp_pin_assignment(data_status) \ + __print_symbolic(TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT(data_status), \ + { TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT_E, "E" }, \ + { TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT_F, "F" }, \ + { TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT_C, "C" }, \ + { TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT_D, "D" }, \ + { TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT_A, "A" }, \ + { TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT_B, "B" }) + +#define maybe_show_data_status_dp_pin_assignment(data_status) \ + (data_status & TPS_DATA_STATUS_DP_CONNECTION ? \ + show_data_status_dp_pin_assignment(data_status) : "") + +TRACE_EVENT(tps6598x_irq, + TP_PROTO(u64 event1, + u64 event2), + TP_ARGS(event1, event2), + + TP_STRUCT__entry( + __field(u64, event1) + __field(u64, event2) + ), + + TP_fast_assign( + __entry->event1 = event1; + __entry->event2 = event2; + ), + + TP_printk("event1=%s, event2=%s", + show_irq_flags(__entry->event1), + show_irq_flags(__entry->event2)) +); + +TRACE_EVENT(tps6598x_status, + TP_PROTO(u32 status), + TP_ARGS(status), + + TP_STRUCT__entry( + __field(u32, status) + ), + + TP_fast_assign( + __entry->status = status; + ), + + TP_printk("conn: %s, pp_5v0: %s, pp_hv: %s, pp_ext: %s, pp_cable: %s, " + "pwr-src: %s, vbus: %s, usb-host: %s, legacy: %s, flags: %s", + show_status_conn_state(__entry->status), + show_status_pp_switch_state(TPS_STATUS_PP_5V0_SWITCH(__entry->status)), + show_status_pp_switch_state(TPS_STATUS_PP_HV_SWITCH(__entry->status)), + show_status_pp_switch_state(TPS_STATUS_PP_EXT_SWITCH(__entry->status)), + show_status_pp_switch_state(TPS_STATUS_PP_CABLE_SWITCH(__entry->status)), + show_status_power_sources(__entry->status), + show_status_vbus_status(__entry->status), + show_status_usb_host_present(__entry->status), + show_status_legacy(__entry->status), + show_status_flags(__entry->status) + ) +); + +TRACE_EVENT(tps6598x_power_status, + TP_PROTO(u16 power_status), + TP_ARGS(power_status), + + TP_STRUCT__entry( + __field(u16, power_status) + ), + + TP_fast_assign( + __entry->power_status = power_status; + ), + + TP_printk("conn: %d, pwr-role: %s, typec: %s, bc: %s", + !!TPS_POWER_STATUS_CONNECTION(__entry->power_status), + show_power_status_source_sink(__entry->power_status), + show_power_status_typec_status(__entry->power_status), + show_power_status_bc12_status(__entry->power_status) + ) +); + +TRACE_EVENT(tps6598x_data_status, + TP_PROTO(u32 data_status), + TP_ARGS(data_status), + + TP_STRUCT__entry( + __field(u32, data_status) + ), + + TP_fast_assign( + __entry->data_status = data_status; + ), + + TP_printk("%s%s%s", + show_data_status_flags(__entry->data_status), + __entry->data_status & TPS_DATA_STATUS_DP_CONNECTION ? ", DP pinout " : "", + maybe_show_data_status_dp_pin_assignment(__entry->data_status) + ) +); + +#endif /* _TPS6598X_TRACE_H_ */ + +/* This part must be outside protection */ +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE trace +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#include diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 244270755ae6..282c3c825c13 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -63,7 +63,7 @@ static int ucsi_read_error(struct ucsi *ucsi) u16 error; int ret; - /* Acknowlege the command that failed */ + /* Acknowledge the command that failed */ ret = ucsi_acknowledge_command(ucsi); if (ret) return ret; diff --git a/drivers/usb/usbip/vudc_sysfs.c b/drivers/usb/usbip/vudc_sysfs.c index f7633ee655a1..d1cf6b51bf85 100644 --- a/drivers/usb/usbip/vudc_sysfs.c +++ b/drivers/usb/usbip/vudc_sysfs.c @@ -156,12 +156,14 @@ static ssize_t usbip_sockfd_store(struct device *dev, tcp_rx = kthread_create(&v_rx_loop, &udc->ud, "vudc_rx"); if (IS_ERR(tcp_rx)) { sockfd_put(socket); + mutex_unlock(&udc->ud.sysfs_lock); return -EINVAL; } tcp_tx = kthread_create(&v_tx_loop, &udc->ud, "vudc_tx"); if (IS_ERR(tcp_tx)) { kthread_stop(tcp_rx); sockfd_put(socket); + mutex_unlock(&udc->ud.sysfs_lock); return -EINVAL; } diff --git a/include/linux/platform_data/usb-mx2.h b/include/linux/platform_data/usb-mx2.h deleted file mode 100644 index 97a670f3d8fb..000000000000 --- a/include/linux/platform_data/usb-mx2.h +++ /dev/null @@ -1,29 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2009 Martin Fuzzey - */ - -#ifndef __ASM_ARCH_MX21_USBH -#define __ASM_ARCH_MX21_USBH - -enum mx21_usbh_xcvr { - /* Values below as used by hardware (HWMODE register) */ - MX21_USBXCVR_TXDIF_RXDIF = 0, - MX21_USBXCVR_TXDIF_RXSE = 1, - MX21_USBXCVR_TXSE_RXDIF = 2, - MX21_USBXCVR_TXSE_RXSE = 3, -}; - -struct mx21_usbh_platform_data { - enum mx21_usbh_xcvr host_xcvr; /* tranceiver mode host 1,2 ports */ - enum mx21_usbh_xcvr otg_xcvr; /* tranceiver mode otg (as host) port */ - u16 enable_host1:1, - enable_host2:1, - enable_otg_host:1, /* enable "OTG" port (as host) */ - host1_xcverless:1, /* traceiverless host1 port */ - host1_txenoe:1, /* output enable host1 transmit enable */ - otg_ext_xcvr:1, /* external tranceiver for OTG port */ - unused:10; -}; - -#endif /* __ASM_ARCH_MX21_USBH */ diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 81a55e974feb..b495b4374cd0 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -426,9 +426,16 @@ static inline int power_supply_is_system_supplied(void) { return -ENOSYS; } extern int power_supply_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val); +#if IS_ENABLED(CONFIG_POWER_SUPPLY) extern int power_supply_set_property(struct power_supply *psy, enum power_supply_property psp, const union power_supply_propval *val); +#else +static inline int power_supply_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ return 0; } +#endif extern int power_supply_property_is_writeable(struct power_supply *psy, enum power_supply_property psp); extern void power_supply_external_power_changed(struct power_supply *psy); diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index 659a0a810fa1..e7c96c37174f 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -146,6 +146,7 @@ struct tb_property_dir *tb_property_parse_dir(const u32 *block, size_t block_len); ssize_t tb_property_format_dir(const struct tb_property_dir *dir, u32 *block, size_t block_len); +struct tb_property_dir *tb_property_copy_dir(const struct tb_property_dir *dir); struct tb_property_dir *tb_property_create_dir(const uuid_t *uuid); void tb_property_free_dir(struct tb_property_dir *dir); int tb_property_add_immediate(struct tb_property_dir *parent, const char *key, @@ -179,23 +180,24 @@ void tb_unregister_property_dir(const char *key, struct tb_property_dir *dir); * @route: Route string the other domain can be reached * @vendor: Vendor ID of the remote domain * @device: Device ID of the demote domain + * @local_max_hopid: Maximum input HopID of this host + * @remote_max_hopid: Maximum input HopID of the remote host * @lock: Lock to serialize access to the following fields of this structure * @vendor_name: Name of the vendor (or %NULL if not known) * @device_name: Name of the device (or %NULL if not known) * @link_speed: Speed of the link in Gb/s * @link_width: Width of the link (1 or 2) * @is_unplugged: The XDomain is unplugged - * @resume: The XDomain is being resumed * @needs_uuid: If the XDomain does not have @remote_uuid it will be * queried first - * @transmit_path: HopID which the remote end expects us to transmit - * @transmit_ring: Local ring (hop) where outgoing packets are pushed - * @receive_path: HopID which we expect the remote end to transmit - * @receive_ring: Local ring (hop) where incoming packets arrive * @service_ids: Used to generate IDs for the services - * @properties: Properties exported by the remote domain - * @property_block_gen: Generation of @properties - * @properties_lock: Lock protecting @properties. + * @in_hopids: Input HopIDs for DMA tunneling + * @out_hopids; Output HopIDs for DMA tunneling + * @local_property_block: Local block of properties + * @local_property_block_gen: Generation of @local_property_block + * @local_property_block_len: Length of the @local_property_block in dwords + * @remote_properties: Properties exported by the remote domain + * @remote_property_block_gen: Generation of @remote_properties * @get_uuid_work: Work used to retrieve @remote_uuid * @uuid_retries: Number of times left @remote_uuid is requested before * giving up @@ -225,21 +227,23 @@ struct tb_xdomain { u64 route; u16 vendor; u16 device; + unsigned int local_max_hopid; + unsigned int remote_max_hopid; struct mutex lock; const char *vendor_name; const char *device_name; unsigned int link_speed; unsigned int link_width; bool is_unplugged; - bool resume; bool needs_uuid; - u16 transmit_path; - u16 transmit_ring; - u16 receive_path; - u16 receive_ring; struct ida service_ids; - struct tb_property_dir *properties; - u32 property_block_gen; + struct ida in_hopids; + struct ida out_hopids; + u32 *local_property_block; + u32 local_property_block_gen; + u32 local_property_block_len; + struct tb_property_dir *remote_properties; + u32 remote_property_block_gen; struct delayed_work get_uuid_work; int uuid_retries; struct delayed_work get_properties_work; @@ -252,10 +256,22 @@ struct tb_xdomain { int tb_xdomain_lane_bonding_enable(struct tb_xdomain *xd); void tb_xdomain_lane_bonding_disable(struct tb_xdomain *xd); -int tb_xdomain_enable_paths(struct tb_xdomain *xd, u16 transmit_path, - u16 transmit_ring, u16 receive_path, - u16 receive_ring); -int tb_xdomain_disable_paths(struct tb_xdomain *xd); +int tb_xdomain_alloc_in_hopid(struct tb_xdomain *xd, int hopid); +void tb_xdomain_release_in_hopid(struct tb_xdomain *xd, int hopid); +int tb_xdomain_alloc_out_hopid(struct tb_xdomain *xd, int hopid); +void tb_xdomain_release_out_hopid(struct tb_xdomain *xd, int hopid); +int tb_xdomain_enable_paths(struct tb_xdomain *xd, int transmit_path, + int transmit_ring, int receive_path, + int receive_ring); +int tb_xdomain_disable_paths(struct tb_xdomain *xd, int transmit_path, + int transmit_ring, int receive_path, + int receive_ring); + +static inline int tb_xdomain_disable_all_paths(struct tb_xdomain *xd) +{ + return tb_xdomain_disable_paths(xd, -1, -1, -1, -1); +} + struct tb_xdomain *tb_xdomain_find_by_uuid(struct tb *tb, const uuid_t *uuid); struct tb_xdomain *tb_xdomain_find_by_route(struct tb *tb, u64 route); diff --git a/include/linux/usb.h b/include/linux/usb.h index d6a41841b93e..eaae24217e8a 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -560,6 +560,7 @@ struct usb3_lpm_parameters { * @speed: device speed: high/full/low (or error) * @rx_lanes: number of rx lanes in use, USB 3.2 adds dual-lane support * @tx_lanes: number of tx lanes in use, USB 3.2 adds dual-lane support + * @ssp_rate: SuperSpeed Plus phy signaling rate and lane count * @tt: Transaction Translator info; used with low/full speed dev, highspeed hub * @ttport: device port on that tt hub * @toggle: one bit for each endpoint, with ([0] = IN, [1] = OUT) endpoints @@ -636,6 +637,7 @@ struct usb_device { enum usb_device_speed speed; unsigned int rx_lanes; unsigned int tx_lanes; + enum usb_ssp_rate ssp_rate; struct usb_tt *tt; int ttport; @@ -841,7 +843,7 @@ extern int usb_free_streams(struct usb_interface *interface, /* used these for multi-interface device registration */ extern int usb_driver_claim_interface(struct usb_driver *driver, - struct usb_interface *iface, void *priv); + struct usb_interface *iface, void *data); /** * usb_interface_claimed - returns true iff an interface is claimed @@ -880,6 +882,15 @@ extern struct usb_host_interface *usb_find_alt_setting( unsigned int iface_num, unsigned int alt_num); +#if IS_REACHABLE(CONFIG_USB) +int usb_for_each_port(void *data, int (*fn)(struct device *, void *)); +#else +static inline int usb_for_each_port(void *data, int (*fn)(struct device *, void *)) +{ + return 0; +} +#endif + /* port claiming functions */ int usb_hub_claim_port(struct usb_device *hdev, unsigned port1, struct usb_dev_state *owner); @@ -1259,8 +1270,6 @@ struct usb_device_driver { #define to_usb_device_driver(d) container_of(d, struct usb_device_driver, \ drvwrap.driver) -extern struct bus_type usb_bus_type; - /** * struct usb_class_driver - identifies a USB driver that wants to use the USB major number * @name: the usb class device name for this driver. Will show up in sysfs. diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h index abdd310c77f0..1cffa34740b0 100644 --- a/include/linux/usb/ch9.h +++ b/include/linux/usb/ch9.h @@ -45,73 +45,15 @@ enum usb_ssp_rate { USB_SSP_GEN_2x2, }; -/** - * usb_ep_type_string() - Returns human readable-name of the endpoint type. - * @ep_type: The endpoint type to return human-readable name for. If it's not - * any of the types: USB_ENDPOINT_XFER_{CONTROL, ISOC, BULK, INT}, - * usually got by usb_endpoint_type(), the string 'unknown' will be returned. - */ extern const char *usb_ep_type_string(int ep_type); - -/** - * usb_speed_string() - Returns human readable-name of the speed. - * @speed: The speed to return human-readable name for. If it's not - * any of the speeds defined in usb_device_speed enum, string for - * USB_SPEED_UNKNOWN will be returned. - */ extern const char *usb_speed_string(enum usb_device_speed speed); - -/** - * usb_get_maximum_speed - Get maximum requested speed for a given USB - * controller. - * @dev: Pointer to the given USB controller device - * - * The function gets the maximum speed string from property "maximum-speed", - * and returns the corresponding enum usb_device_speed. - */ extern enum usb_device_speed usb_get_maximum_speed(struct device *dev); - -/** - * usb_get_maximum_ssp_rate - Get the signaling rate generation and lane count - * of a SuperSpeed Plus capable device. - * @dev: Pointer to the given USB controller device - * - * If the string from "maximum-speed" property is super-speed-plus-genXxY where - * 'X' is the generation number and 'Y' is the number of lanes, then this - * function returns the corresponding enum usb_ssp_rate. - */ extern enum usb_ssp_rate usb_get_maximum_ssp_rate(struct device *dev); - -/** - * usb_state_string - Returns human readable name for the state. - * @state: The state to return a human-readable name for. If it's not - * any of the states devices in usb_device_state_string enum, - * the string UNKNOWN will be returned. - */ extern const char *usb_state_string(enum usb_device_state state); +unsigned int usb_decode_interval(const struct usb_endpoint_descriptor *epd, + enum usb_device_speed speed); #ifdef CONFIG_TRACING -/** - * usb_decode_ctrl - Returns human readable representation of control request. - * @str: buffer to return a human-readable representation of control request. - * This buffer should have about 200 bytes. - * @size: size of str buffer. - * @bRequestType: matches the USB bmRequestType field - * @bRequest: matches the USB bRequest field - * @wValue: matches the USB wValue field (CPU byte order) - * @wIndex: matches the USB wIndex field (CPU byte order) - * @wLength: matches the USB wLength field (CPU byte order) - * - * Function returns decoded, formatted and human-readable description of - * control request packet. - * - * The usage scenario for this is for tracepoints, so function as a return - * use the same value as in parameters. This approach allows to use this - * function in TP_printk - * - * Important: wValue, wIndex, wLength parameters before invoking this function - * should be processed by le16_to_cpu macro. - */ extern const char *usb_decode_ctrl(char *str, size_t size, __u8 bRequestType, __u8 bRequest, __u16 wValue, __u16 wIndex, __u16 wLength); diff --git a/include/linux/usb/ehci_pdriver.h b/include/linux/usb/ehci_pdriver.h index dd742afdc03f..89fc901e778f 100644 --- a/include/linux/usb/ehci_pdriver.h +++ b/include/linux/usb/ehci_pdriver.h @@ -50,6 +50,7 @@ struct usb_ehci_pdata { unsigned no_io_watchdog:1; unsigned reset_on_resume:1; unsigned dma_mask_64:1; + unsigned spurious_oc:1; /* Turn on all power and clocks */ int (*power_on)(struct platform_device *pdev); diff --git a/include/linux/usb/pd.h b/include/linux/usb/pd.h index 70d681918d01..bf00259493e0 100644 --- a/include/linux/usb/pd.h +++ b/include/linux/usb/pd.h @@ -493,4 +493,6 @@ static inline unsigned int rdo_max_power(u32 rdo) #define PD_N_CAPS_COUNT (PD_T_NO_RESPONSE / PD_T_SEND_SOURCE_CAP) #define PD_N_HARD_RESET_COUNT 2 +#define PD_P_SNK_STDBY_MW 2500 /* 2500 mW */ + #endif /* __LINUX_USB_PD_H */ diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 952272002e48..8c63fa9bfc74 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -130,6 +130,8 @@ static inline void usb_set_serial_port_data(struct usb_serial_port *port, * @dev: pointer to the struct usb_device for this device * @type: pointer to the struct usb_serial_driver for this device * @interface: pointer to the struct usb_interface for this device + * @sibling: pointer to the struct usb_interface of any sibling interface + * @suspend_count: number of suspended (sibling) interfaces * @num_ports: the number of ports this device has * @num_interrupt_in: number of interrupt in endpoints we have * @num_interrupt_out: number of interrupt out endpoints we have @@ -145,8 +147,9 @@ struct usb_serial { struct usb_device *dev; struct usb_serial_driver *type; struct usb_interface *interface; + struct usb_interface *sibling; + unsigned int suspend_count; unsigned char disconnected:1; - unsigned char suspending:1; unsigned char attached:1; unsigned char minors_reserved:1; unsigned char num_ports; @@ -276,7 +279,7 @@ struct usb_serial_driver { int (*write_room)(struct tty_struct *tty); int (*ioctl)(struct tty_struct *tty, unsigned int cmd, unsigned long arg); - int (*get_serial)(struct tty_struct *tty, struct serial_struct *ss); + void (*get_serial)(struct tty_struct *tty, struct serial_struct *ss); int (*set_serial)(struct tty_struct *tty, struct serial_struct *ss); void (*set_termios)(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old); @@ -335,6 +338,9 @@ static inline void usb_serial_console_disconnect(struct usb_serial *serial) {} /* Functions needed by other parts of the usbserial core */ struct usb_serial_port *usb_serial_port_get_by_minor(unsigned int minor); void usb_serial_put(struct usb_serial *serial); + +int usb_serial_claim_interface(struct usb_serial *serial, struct usb_interface *intf); + int usb_serial_generic_open(struct tty_struct *tty, struct usb_serial_port *port); int usb_serial_generic_write_start(struct usb_serial_port *port, gfp_t mem_flags); int usb_serial_generic_write(struct tty_struct *tty, struct usb_serial_port *port, diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h index 91b4303ca305..e2e44bb1dad8 100644 --- a/include/linux/usb/typec.h +++ b/include/linux/usb/typec.h @@ -17,6 +17,7 @@ struct typec_partner; struct typec_cable; struct typec_plug; struct typec_port; +struct typec_altmode_ops; struct fwnode_handle; struct device; @@ -138,6 +139,11 @@ struct typec_altmode struct typec_altmode *typec_port_register_altmode(struct typec_port *port, const struct typec_altmode_desc *desc); + +void typec_port_register_altmodes(struct typec_port *port, + const struct typec_altmode_ops *ops, void *drvdata, + struct typec_altmode **altmodes, size_t n); + void typec_unregister_altmode(struct typec_altmode *altmode); struct typec_port *typec_altmode2port(struct typec_altmode *alt); @@ -298,4 +304,17 @@ int typec_find_port_data_role(const char *name); void typec_partner_set_svdm_version(struct typec_partner *partner, enum usb_pd_svdm_ver svdm_version); int typec_get_negotiated_svdm_version(struct typec_port *port); + +#if IS_REACHABLE(CONFIG_TYPEC) +int typec_link_port(struct device *port); +void typec_unlink_port(struct device *port); +#else +static inline int typec_link_port(struct device *port) +{ + return 0; +} + +static inline void typec_unlink_port(struct device *port) { } +#endif + #endif /* __LINUX_USB_TYPEC_H */ diff --git a/include/uapi/linux/usb/video.h b/include/uapi/linux/usb/video.h index d854cb19c42c..bfdae12cdacf 100644 --- a/include/uapi/linux/usb/video.h +++ b/include/uapi/linux/usb/video.h @@ -302,9 +302,10 @@ struct uvc_processing_unit_descriptor { __u8 bControlSize; __u8 bmControls[2]; __u8 iProcessing; + __u8 bmVideoStandards; } __attribute__((__packed__)); -#define UVC_DT_PROCESSING_UNIT_SIZE(n) (9+(n)) +#define UVC_DT_PROCESSING_UNIT_SIZE(n) (10+(n)) /* 3.7.2.6. Extension Unit Descriptor */ struct uvc_extension_unit_descriptor { diff --git a/tools/usb/usbip/doc/usbip.8 b/tools/usb/usbip/doc/usbip.8 index a15d20063b98..1f26e4a00638 100644 --- a/tools/usb/usbip/doc/usbip.8 +++ b/tools/usb/usbip/doc/usbip.8 @@ -49,10 +49,17 @@ then exit. Attach a remote USB device. .PP +.HP +\fBattach\fR \-\-remote=<\fIhost\fR> \-\-device=<\fIdev_id\fR> +.IP +Attach a remote USB gadget. +Only used when the remote usbipd is in device mode. +.PP + .HP \fBdetach\fR \-\-port=<\fIport\fR> .IP -Detach an imported USB device. +Detach an imported USB device/gadget. .PP .HP @@ -73,12 +80,26 @@ Stop exporting a device so it can be used by a local driver. List USB devices exported by a remote host. .PP +.HP +\fBlist\fR \-\-device +.IP +List USB gadgets of local usbip-vudc. +Only used when the local usbipd is in device mode. +Note that this can not list usbip-vudc USB gadgets of the remote device mode usbipd. +.PP + .HP \fBlist\fR \-\-local .IP List local USB devices. .PP +.HP +\fBport\fR +.IP +List imported devices/gadgets. +.PP + .SH EXAMPLES @@ -90,8 +111,27 @@ List local USB devices. client:# usbip attach --remote=server --busid=1-2 - Connect the remote USB device. + client:# usbip port + - List imported devices/gadgets. + client:# usbip detach --port=0 - Detach the usb device. +The following example shows the usage of device mode + + server:# usbip list --device + - List gadgets exported by local usbipd server. + + client:# modprobe vhci-hcd + + client:# usbip attach --remote=server --device=usbip-vudc.0 + - Connect the remote USB gadget. + + client:# usbip port + - List imported devices/gadgets. + + client:# usbip detach --port=0 + - Detach the usb gadget. + .SH "SEE ALSO" \fBusbipd\fP\fB(8)\fB\fP diff --git a/tools/usb/usbip/doc/usbipd.8 b/tools/usb/usbip/doc/usbipd.8 index fb62a756893b..d974394f86a1 100644 --- a/tools/usb/usbip/doc/usbipd.8 +++ b/tools/usb/usbip/doc/usbipd.8 @@ -29,6 +29,12 @@ Bind to IPv4. Default is both. Bind to IPv6. Default is both. .PP +.HP +\fB\-e\fR, \fB\-\-device\fR +.IP +Run in device mode. Rather than drive an attached device, create a virtual UDC to bind gadgets to. +.PP + .HP \fB\-D\fR, \fB\-\-daemon\fR .IP @@ -86,6 +92,26 @@ USB/IP client can connect and use exported devices. - A usb device 1-2 is now exportable to other hosts! - Use 'usbip unbind --busid=1-2' when you want to shutdown exporting and use the device locally. +The following example shows the usage of device mode + + server:# modprobe usbip-vudc + - Use /sys/class/udc/ interface. + - usbip-host is independent of this module. + + server:# usbipd -e -D + - Start usbip daemon in device mode. + + server:# modprobe g_mass_storage file=/tmp/tmp.img + - Bind a gadget to usbip-vudc. + - in this example, a mass storage gadget is bound. + + server:# usbip list --device + - List gadgets exported by local usbipd server. + + server:# modprobe -r g_mass_storage + - Unbind a gadget from usbip-vudc. + - in this example, the previous mass storage gadget is unbound. + .SH "SEE ALSO" \fBusbip\fP\fB(8)\fB\fP diff --git a/tools/usb/usbip/libsrc/list.h b/tools/usb/usbip/libsrc/list.h index a941671e4900..9cca2425587b 100644 --- a/tools/usb/usbip/libsrc/list.h +++ b/tools/usb/usbip/libsrc/list.h @@ -77,17 +77,17 @@ static inline void __list_del(struct list_head * prev, struct list_head * next) #define LIST_POISON1 ((void *) 0x00100100 + POISON_POINTER_DELTA) #define LIST_POISON2 ((void *) 0x00200200 + POISON_POINTER_DELTA) +static inline void __list_del_entry(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + /** * list_del - deletes entry from list. * @entry: the element to delete from the list. * Note: list_empty() on entry does not return true after this, the entry is * in an undefined state. */ -static inline void __list_del_entry(struct list_head *entry) -{ - __list_del(entry->prev, entry->next); -} - static inline void list_del(struct list_head *entry) { __list_del(entry->prev, entry->next); diff --git a/tools/usb/usbip/src/usbip_list.c b/tools/usb/usbip/src/usbip_list.c index 8625b0f514ee..3d810bcca02f 100644 --- a/tools/usb/usbip/src/usbip_list.c +++ b/tools/usb/usbip/src/usbip_list.c @@ -33,7 +33,8 @@ static const char usbip_list_usage_string[] = "usbip list [-p|--parsable] \n" " -p, --parsable Parsable list format\n" " -r, --remote= List the exportable USB devices on \n" - " -l, --local List the local USB devices\n"; + " -l, --local List the local USB devices\n" + " -d, --device List the local USB gadgets bound to usbip-vudc\n"; void usbip_list_usage(void) {