diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2020-12-15 22:54:56 +0100 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2020-12-15 22:54:56 +0100 |
commit | 0cee54c890a40051928991072e5d1cd279611dfd (patch) | |
tree | e4e24223c697b8935a1b24b906f60831d1dc7c71 | |
parent | Merge tag 'sound-5.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/t... (diff) | |
parent | usb: phy: convert comma to semicolon (diff) | |
download | linux-0cee54c890a40051928991072e5d1cd279611dfd.tar.xz linux-0cee54c890a40051928991072e5d1cd279611dfd.zip |
Merge tag 'usb-5.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
Pull USB / Thunderbolt updates from Greg KH:
"Here is the big USB and thunderbolt pull request for 5.11-rc1.
Nothing major in here, just the grind of constant development to
support new hardware and fix old issues:
- thunderbolt updates for new USB4 hardware
- cdns3 major driver updates
- lots of typec updates and additions as more hardware is available
- usb serial driver updates and fixes
- other tiny USB driver updates
All have been in linux-next with no reported issues"
* tag 'usb-5.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (172 commits)
usb: phy: convert comma to semicolon
usb: ucsi: convert comma to semicolon
usb: typec: tcpm: convert comma to semicolon
usb: typec: tcpm: Update vbus_vsafe0v on init
usb: typec: tcpci: Enable bleed discharge when auto discharge is enabled
usb: typec: Add class for plug alt mode device
USB: typec: tcpci: Add Bleed discharge to POWER_CONTROL definition
USB: typec: tcpm: Add a 30ms room for tPSSourceOn in PR_SWAP
USB: typec: tcpm: Fix PR_SWAP error handling
USB: typec: tcpm: Hard Reset after not receiving a Request
USB: gadget: f_fs: remove likely/unlikely
usb: gadget: f_fs: Re-use SS descriptors for SuperSpeedPlus
USB: gadget: f_midi: setup SuperSpeed Plus descriptors
USB: gadget: f_acm: add support for SuperSpeed Plus
USB: gadget: f_rndis: fix bitrate for SuperSpeed and above
usb: typec: intel_pmc_mux: Configure cable generation value for USB4
MAINTAINERS: Add myself as a reviewer for CADENCE USB3 DRD IP DRIVER
usb: chipidea: ci_hdrc_imx: Use of_device_get_match_data()
usb: chipidea: usbmisc_imx: Use of_device_get_match_data()
usb: cdns3: fix NULL pointer dereference on no platform data
...
158 files changed, 4292 insertions, 4702 deletions
diff --git a/Documentation/ABI/testing/sysfs-bus-thunderbolt b/Documentation/ABI/testing/sysfs-bus-thunderbolt index 0b4ab9e4b8f4..a91b4b24496e 100644 --- a/Documentation/ABI/testing/sysfs-bus-thunderbolt +++ b/Documentation/ABI/testing/sysfs-bus-thunderbolt @@ -1,3 +1,31 @@ +What: /sys/bus/thunderbolt/devices/<xdomain>/rx_speed +Date: Feb 2021 +KernelVersion: 5.11 +Contact: Isaac Hazan <isaac.hazan@intel.com> +Description: This attribute reports the XDomain RX speed per lane. + All RX lanes run at the same speed. + +What: /sys/bus/thunderbolt/devices/<xdomain>/rx_lanes +Date: Feb 2021 +KernelVersion: 5.11 +Contact: Isaac Hazan <isaac.hazan@intel.com> +Description: This attribute reports the number of RX lanes the XDomain + is using simultaneously through its upstream port. + +What: /sys/bus/thunderbolt/devices/<xdomain>/tx_speed +Date: Feb 2021 +KernelVersion: 5.11 +Contact: Isaac Hazan <isaac.hazan@intel.com> +Description: This attribute reports the XDomain TX speed per lane. + All TX lanes run at the same speed. + +What: /sys/bus/thunderbolt/devices/<xdomain>/tx_lanes +Date: Feb 2021 +KernelVersion: 5.11 +Contact: Isaac Hazan <isaac.hazan@intel.com> +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 diff --git a/Documentation/ABI/testing/sysfs-class-typec b/Documentation/ABI/testing/sysfs-class-typec index b7794e02ad20..8eab41e79ce6 100644 --- a/Documentation/ABI/testing/sysfs-class-typec +++ b/Documentation/ABI/testing/sysfs-class-typec @@ -139,6 +139,49 @@ Description: Shows if the partner supports USB Power Delivery communication: Valid values: yes, no +What: /sys/class/typec/<port>-partner/number_of_alternate_modes +Date: November 2020 +Contact: Prashant Malani <pmalani@chromium.org> +Description: + Shows the number of alternate modes which are advertised by the partner + during Power Delivery discovery. This file remains hidden until a value + greater than or equal to 0 is set by Type C port driver. + +What: /sys/class/typec/<port>-partner/type +Date: December 2020 +Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com> +Description: USB Power Delivery Specification defines a set of product types + for the partner devices. This file will show the product type of + the partner if it is known. Dual-role capable partners will have + both UFP and DFP product types defined, but only one that + matches the current role will be active at the time. If the + product type of the partner is not visible to the device driver, + this file will not exist. + + When the partner product type is detected, or changed with role + swap, uvevent is also raised that contains PRODUCT_TYPE=<product + type> (for example PRODUCT_TYPE=hub). + + Valid values: + + UFP / device role + ====================== ========================== + undefined - + hub PDUSB Hub + peripheral PDUSB Peripheral + psd Power Bank + ama Alternate Mode Adapter + ====================== ========================== + + DFP / host role + ====================== ========================== + undefined - + hub PDUSB Hub + host PDUSB Host + power_brick Power Brick + amc Alternate Mode Controller + ====================== ========================== + What: /sys/class/typec/<port>-partner>/identity/ Date: April 2017 Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com> @@ -151,31 +194,6 @@ Description: directory exists, it will have an attribute file for every VDO in Discover Identity command result. -What: /sys/class/typec/<port>-partner/identity/id_header -Date: April 2017 -Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com> -Description: - ID Header VDO part of Discover Identity command result. The - value will show 0 until Discover Identity command result becomes - available. The value can be polled. - -What: /sys/class/typec/<port>-partner/identity/cert_stat -Date: April 2017 -Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com> -Description: - Cert Stat VDO part of Discover Identity command result. The - value will show 0 until Discover Identity command result becomes - available. The value can be polled. - -What: /sys/class/typec/<port>-partner/identity/product -Date: April 2017 -Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com> -Description: - Product VDO part of Discover Identity command result. The value - will show 0 until Discover Identity command result becomes - available. The value can be polled. - - USB Type-C cable devices (eg. /sys/class/typec/port0-cable/) Note: Electronically Marked Cables will have a device also for one cable plug @@ -187,9 +205,21 @@ described in USB Type-C and USB Power Delivery specifications. What: /sys/class/typec/<port>-cable/type Date: April 2017 Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com> -Description: - Shows if the cable is active. - Valid values: active, passive +Description: USB Power Delivery Specification defines a set of product types + for the cables. This file will show the product type of the + cable if it is known. If the product type of the cable is not + visible to the device driver, this file will not exist. + + When the cable product type is detected, uvevent is also raised + with PRODUCT_TYPE showing the product type of the cable. + + Valid values: + + ====================== ========================== + undefined - + active Active Cable + passive Passive Cable + ====================== ========================== What: /sys/class/typec/<port>-cable/plug_type Date: April 2017 @@ -202,17 +232,37 @@ Description: - type-c - captive -What: /sys/class/typec/<port>-cable/identity/ +What: /sys/class/typec/<port>-<plug>/number_of_alternate_modes +Date: November 2020 +Contact: Prashant Malani <pmalani@chromium.org> +Description: + Shows the number of alternate modes which are advertised by the plug + associated with a particular cable during Power Delivery discovery. + This file remains hidden until a value greater than or equal to 0 + is set by Type C port driver. + + +USB Type-C partner/cable Power Delivery Identity objects + +NOTE: The following attributes will be applicable to both +partner (e.g /sys/class/typec/port0-partner/) and +cable (e.g /sys/class/typec/port0-cable/) devices. Consequently, the example file +paths below are prefixed with "/sys/class/typec/<port>-{partner|cable}/" to +reflect this. + +What: /sys/class/typec/<port>-{partner|cable}/identity/ Date: April 2017 Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com> Description: This directory appears only if the port device driver is capable of showing the result of Discover Identity USB power delivery command. That will not always be possible even when USB power - delivery is supported. If the directory exists, it will have an - attribute for every VDO returned by Discover Identity command. + delivery is supported, for example when USB power delivery + communication for the port is mostly handled in firmware. If the + directory exists, it will have an attribute file for every VDO + in Discover Identity command result. -What: /sys/class/typec/<port>-cable/identity/id_header +What: /sys/class/typec/<port>-{partner|cable}/identity/id_header Date: April 2017 Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com> Description: @@ -220,7 +270,7 @@ Description: value will show 0 until Discover Identity command result becomes available. The value can be polled. -What: /sys/class/typec/<port>-cable/identity/cert_stat +What: /sys/class/typec/<port>-{partner|cable}/identity/cert_stat Date: April 2017 Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com> Description: @@ -228,7 +278,7 @@ Description: value will show 0 until Discover Identity command result becomes available. The value can be polled. -What: /sys/class/typec/<port>-cable/identity/product +What: /sys/class/typec/<port>-{partner|cable}/identity/product Date: April 2017 Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com> Description: @@ -236,6 +286,30 @@ Description: will show 0 until Discover Identity command result becomes available. The value can be polled. +What: /sys/class/typec/<port>-{partner|cable}/identity/product_type_vdo1 +Date: October 2020 +Contact: Prashant Malani <pmalani@chromium.org> +Description: + 1st Product Type VDO of Discover Identity command result. + The value will show 0 until Discover Identity command result becomes + available and a valid Product Type VDO is returned. + +What: /sys/class/typec/<port>-{partner|cable}/identity/product_type_vdo2 +Date: October 2020 +Contact: Prashant Malani <pmalani@chromium.org> +Description: + 2nd Product Type VDO of Discover Identity command result. + The value will show 0 until Discover Identity command result becomes + available and a valid Product Type VDO is returned. + +What: /sys/class/typec/<port>-{partner|cable}/identity/product_type_vdo3 +Date: October 2020 +Contact: Prashant Malani <pmalani@chromium.org> +Description: + 3rd Product Type VDO of Discover Identity command result. + The value will show 0 until Discover Identity command result becomes + available and a valid Product Type VDO is returned. + USB Type-C port alternate mode devices. diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 23f209c8c19a..c0ca130fe3c9 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -5665,6 +5665,7 @@ device); j = NO_REPORT_LUNS (don't use report luns command, uas only); + k = NO_SAME (do not use WRITE_SAME, uas only) l = NOT_LOCKABLE (don't try to lock and unlock ejectable media, not on uas); m = MAX_SECTORS_64 (don't transfer more diff --git a/Documentation/devicetree/bindings/connector/usb-connector.yaml b/Documentation/devicetree/bindings/connector/usb-connector.yaml index 728f82db073d..62781518aefc 100644 --- a/Documentation/devicetree/bindings/connector/usb-connector.yaml +++ b/Documentation/devicetree/bindings/connector/usb-connector.yaml @@ -147,6 +147,25 @@ properties: required: - port@0 + new-source-frs-typec-current: + description: Initial current capability of the new source when vSafe5V + is applied during PD3.0 Fast Role Swap. "Table 6-14 Fixed Supply PDO - Sink" + of "USB Power Delivery Specification Revision 3.0, Version 1.2" provides the + different power levels and "6.4.1.3.1.6 Fast Role Swap USB Type-C Current" + provides a detailed description of the field. The sink PDO from current source + reflects the current source's(i.e. transmitter of the FRS signal) power + requirement during fr swap. The current sink (i.e. receiver of the FRS signal), + a.k.a new source, should check if it will be able to satisfy the current source's, + new sink's, requirement during frswap before enabling the frs signal reception. + This property refers to maximum current capability that the current sink can + satisfy. During FRS, VBUS voltage is at 5V, as the partners are in implicit + contract, hence, the power level is only a function of the current capability. + "1" refers to default USB power level as described by "Table 6-14 Fixed Supply PDO - Sink". + "2" refers to 1.5A@5V. + "3" refers to 3.0A@5V. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [1, 2, 3] + required: - compatible diff --git a/Documentation/devicetree/bindings/usb/brcm,usb-pinmap.yaml b/Documentation/devicetree/bindings/usb/brcm,usb-pinmap.yaml new file mode 100644 index 000000000000..ffa148b9eaa8 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/brcm,usb-pinmap.yaml @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/brcm,usb-pinmap.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Broadcom USB pin map Controller Device Tree Bindings + +maintainers: + - Al Cooper <alcooperx@gmail.com> + +properties: + compatible: + items: + - const: brcm,usb-pinmap + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + description: Interrupt for signals mirrored to out-gpios. + + in-gpios: + description: Array of one or two GPIO pins used for input signals. + + brcm,in-functions: + $ref: /schemas/types.yaml#/definitions/string-array + description: Array of input signal names, one per gpio in in-gpios. + + brcm,in-masks: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: Array of enable and mask pairs, one per gpio in-gpios. + + out-gpios: + description: Array of one GPIO pin used for output signals. + + brcm,out-functions: + $ref: /schemas/types.yaml#/definitions/string-array + description: Array of output signal names, one per gpio in out-gpios. + + brcm,out-masks: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: Array of enable, value, changed and clear masks, one + per gpio in out-gpios. + +required: + - compatible + - reg + +additionalProperties: false + +dependencies: + in-gpios: [ interrupts ] + +examples: + - | + usb_pinmap: usb-pinmap@22000d0 { + compatible = "brcm,usb-pinmap"; + reg = <0x22000d0 0x4>; + in-gpios = <&gpio 18 0>, <&gpio 19 0>; + brcm,in-functions = "VBUS", "PWRFLT"; + brcm,in-masks = <0x8000 0x40000 0x10000 0x80000>; + out-gpios = <&gpio 20 0>; + brcm,out-functions = "PWRON"; + brcm,out-masks = <0x20000 0x800000 0x400000 0x200000>; + interrupts = <0x0 0xb2 0x4>; + }; + +... diff --git a/Documentation/devicetree/bindings/usb/cdns,usb3.yaml b/Documentation/devicetree/bindings/usb/cdns,usb3.yaml index d6af2794d444..a407e1143cf4 100644 --- a/Documentation/devicetree/bindings/usb/cdns,usb3.yaml +++ b/Documentation/devicetree/bindings/usb/cdns,usb3.yaml @@ -26,16 +26,21 @@ properties: - const: dev interrupts: + minItems: 3 items: - description: OTG/DRD controller interrupt - description: XHCI host controller interrupt - description: Device controller interrupt + - description: interrupt used to wake up core, e.g when usbcmd.rs is + cleared by xhci core, this interrupt is optional interrupt-names: + minItems: 3 items: - const: host - const: peripheral - const: otg + - const: wakeup dr_mode: enum: [host, otg, peripheral] diff --git a/Documentation/devicetree/bindings/usb/maxim,max33359.yaml b/Documentation/devicetree/bindings/usb/maxim,max33359.yaml new file mode 100644 index 000000000000..93a19eda610b --- /dev/null +++ b/Documentation/devicetree/bindings/usb/maxim,max33359.yaml @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/usb/maxim,max33359.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Maxim TCPCI Type-C PD controller DT bindings + +maintainers: + - Badhri Jagan Sridharan <badhri@google.com> + +description: Maxim TCPCI Type-C PD controller + +properties: + compatible: + enum: + - maxim,max33359 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + connector: + type: object + $ref: ../connector/usb-connector.yaml# + description: + Properties for usb c connector. + +required: + - compatible + - reg + - interrupts + - connector + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + #include <dt-bindings/usb/pd.h> + i2c0 { + #address-cells = <1>; + #size-cells = <0>; + + maxtcpc@25 { + compatible = "maxim,max33359"; + reg = <0x25>; + interrupt-parent = <&gpa8>; + interrupts = <2 IRQ_TYPE_LEVEL_LOW>; + + connector { + compatible = "usb-c-connector"; + label = "USB-C"; + data-role = "dual"; + power-role = "dual"; + try-power-role = "sink"; + self-powered; + op-sink-microwatt = <2600000>; + new-source-frs-typec-current = <FRS_5V_1P5A>; + source-pdos = <PDO_FIXED(5000, 900, + PDO_FIXED_SUSPEND | + PDO_FIXED_USB_COMM | + PDO_FIXED_DATA_SWAP | + PDO_FIXED_DUAL_ROLE)>; + sink-pdos = <PDO_FIXED(5000, 3000, + PDO_FIXED_USB_COMM | + PDO_FIXED_DATA_SWAP | + PDO_FIXED_DUAL_ROLE) + PDO_FIXED(9000, 2000, 0)>; + }; + }; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index 9a34f5b2d0ed..2147671b4e53 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3583,6 +3583,14 @@ S: Maintained F: Documentation/devicetree/bindings/usb/brcm,bcm7445-ehci.yaml F: drivers/usb/host/ehci-brcm.* +BROADCOM BRCMSTB USB PIN MAP DRIVER +M: Al Cooper <alcooperx@gmail.com> +L: linux-usb@vger.kernel.org +L: bcm-kernel-feedback-list@broadcom.com +S: Maintained +F: Documentation/devicetree/bindings/usb/brcm,usb-pinmap.yaml +F: drivers/usb/misc/brcmstb-usb-pinmap.c + BROADCOM BRCMSTB USB2 and USB3 PHY DRIVER M: Al Cooper <alcooperx@gmail.com> L: linux-kernel@vger.kernel.org @@ -3868,6 +3876,7 @@ CADENCE USB3 DRD IP DRIVER M: Peter Chen <peter.chen@nxp.com> M: Pawel Laszczak <pawell@cadence.com> M: Roger Quadros <rogerq@ti.com> +R: Aswath Govindraju <a-govindraju@ti.com> L: linux-usb@vger.kernel.org S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb.git @@ -17476,6 +17485,12 @@ W: http://thinkwiki.org/wiki/Ibm-acpi T: git git://repo.or.cz/linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git F: drivers/platform/x86/thinkpad_acpi.c +THUNDERBOLT DMA TRAFFIC TEST DRIVER +M: Isaac Hazan <isaac.hazan@intel.com> +L: linux-usb@vger.kernel.org +S: Maintained +F: drivers/thunderbolt/dma_test.c + THUNDERBOLT DRIVER M: Andreas Noever <andreas.noever@gmail.com> M: Michael Jamet <michael.jamet@intel.com> diff --git a/arch/arm/configs/badge4_defconfig b/arch/arm/configs/badge4_defconfig index ef484c4cfd1a..d9119da65f48 100644 --- a/arch/arm/configs/badge4_defconfig +++ b/arch/arm/configs/badge4_defconfig @@ -89,7 +89,6 @@ CONFIG_USB_SERIAL_KEYSPAN=m CONFIG_USB_SERIAL_MCT_U232=m CONFIG_USB_SERIAL_PL2303=m CONFIG_USB_SERIAL_CYBERJACK=m -CONFIG_USB_SERIAL_XIRCOM=m CONFIG_USB_SERIAL_OMNINET=m CONFIG_EXT2_FS=m CONFIG_EXT3_FS=m diff --git a/arch/arm/configs/corgi_defconfig b/arch/arm/configs/corgi_defconfig index 4fec2ec379ad..911e880f06ed 100644 --- a/arch/arm/configs/corgi_defconfig +++ b/arch/arm/configs/corgi_defconfig @@ -191,7 +191,6 @@ CONFIG_USB_SERIAL_PL2303=m CONFIG_USB_SERIAL_SAFE=m CONFIG_USB_SERIAL_TI=m CONFIG_USB_SERIAL_CYBERJACK=m -CONFIG_USB_SERIAL_XIRCOM=m CONFIG_USB_SERIAL_OMNINET=m CONFIG_USB_EMI62=m CONFIG_USB_EMI26=m diff --git a/arch/arm/configs/pxa_defconfig b/arch/arm/configs/pxa_defconfig index d7b9eaf4783c..8654ece13004 100644 --- a/arch/arm/configs/pxa_defconfig +++ b/arch/arm/configs/pxa_defconfig @@ -574,7 +574,6 @@ CONFIG_USB_SERIAL_PL2303=m CONFIG_USB_SERIAL_SAFE=m CONFIG_USB_SERIAL_TI=m CONFIG_USB_SERIAL_CYBERJACK=m -CONFIG_USB_SERIAL_XIRCOM=m CONFIG_USB_SERIAL_OMNINET=m CONFIG_USB_EMI62=m CONFIG_USB_EMI26=m diff --git a/arch/arm/configs/spitz_defconfig b/arch/arm/configs/spitz_defconfig index a1cdbfa064c5..8b2c14424927 100644 --- a/arch/arm/configs/spitz_defconfig +++ b/arch/arm/configs/spitz_defconfig @@ -185,7 +185,6 @@ CONFIG_USB_SERIAL_PL2303=m CONFIG_USB_SERIAL_SAFE=m CONFIG_USB_SERIAL_TI=m CONFIG_USB_SERIAL_CYBERJACK=m -CONFIG_USB_SERIAL_XIRCOM=m CONFIG_USB_SERIAL_OMNINET=m CONFIG_USB_EMI62=m CONFIG_USB_EMI26=m diff --git a/arch/arm/mach-omap1/board-h2.c b/arch/arm/mach-omap1/board-h2.c index cb7ce627ffe8..c40cf5ef8607 100644 --- a/arch/arm/mach-omap1/board-h2.c +++ b/arch/arm/mach-omap1/board-h2.c @@ -16,6 +16,7 @@ * Copyright (C) 2004 Nokia Corporation by Imre Deak <imre.deak@nokia.com> */ #include <linux/gpio.h> +#include <linux/gpio/machine.h> #include <linux/kernel.h> #include <linux/platform_device.h> #include <linux/delay.h> @@ -46,6 +47,9 @@ #include "common.h" #include "board-h2.h" +/* The first 16 SoC GPIO lines are on this GPIO chip */ +#define OMAP_GPIO_LABEL "gpio-0-15" + /* At OMAP1610 Innovator the Ethernet is directly connected to CS1 */ #define OMAP1610_ETHR_START 0x04000300 @@ -334,7 +338,19 @@ static struct i2c_board_info __initdata h2_i2c_board_info[] = { I2C_BOARD_INFO("tps65010", 0x48), .platform_data = &tps_board, }, { - I2C_BOARD_INFO("isp1301_omap", 0x2d), + .type = "isp1301_omap", + .addr = 0x2d, + .dev_name = "isp1301", + }, +}; + +static struct gpiod_lookup_table isp1301_gpiod_table = { + .dev_id = "isp1301", + .table = { + /* Active low since the irq triggers on falling edge */ + GPIO_LOOKUP(OMAP_GPIO_LABEL, 2, + NULL, GPIO_ACTIVE_LOW), + { }, }, }; @@ -406,8 +422,10 @@ static void __init h2_init(void) h2_smc91x_resources[1].end = gpio_to_irq(0); platform_add_devices(h2_devices, ARRAY_SIZE(h2_devices)); omap_serial_init(); + + /* ISP1301 IRQ wired at M14 */ + omap_cfg_reg(M14_1510_GPIO2); h2_i2c_board_info[0].irq = gpio_to_irq(58); - h2_i2c_board_info[1].irq = gpio_to_irq(2); omap_register_i2c_bus(1, 100, h2_i2c_board_info, ARRAY_SIZE(h2_i2c_board_info)); omap1_usb_init(&h2_usb_config); diff --git a/arch/mips/configs/mtx1_defconfig b/arch/mips/configs/mtx1_defconfig index 30dacce94198..d1c3ff7f00bf 100644 --- a/arch/mips/configs/mtx1_defconfig +++ b/arch/mips/configs/mtx1_defconfig @@ -563,7 +563,6 @@ CONFIG_USB_SERIAL_SAFE=m CONFIG_USB_SERIAL_SIERRAWIRELESS=m CONFIG_USB_SERIAL_TI=m CONFIG_USB_SERIAL_CYBERJACK=m -CONFIG_USB_SERIAL_XIRCOM=m CONFIG_USB_SERIAL_OPTION=m CONFIG_USB_SERIAL_OMNINET=m CONFIG_USB_EMI62=m diff --git a/arch/mips/configs/rm200_defconfig b/arch/mips/configs/rm200_defconfig index 30d7c3db884e..3dc2da2bee0d 100644 --- a/arch/mips/configs/rm200_defconfig +++ b/arch/mips/configs/rm200_defconfig @@ -311,7 +311,6 @@ CONFIG_USB_SERIAL_PL2303=m CONFIG_USB_SERIAL_SAFE=m CONFIG_USB_SERIAL_SAFE_PADDED=y CONFIG_USB_SERIAL_CYBERJACK=m -CONFIG_USB_SERIAL_XIRCOM=m CONFIG_USB_SERIAL_OMNINET=m CONFIG_USB_LEGOTOWER=m CONFIG_USB_LCD=m diff --git a/arch/powerpc/configs/g5_defconfig b/arch/powerpc/configs/g5_defconfig index 1c674c4c1d86..1de0dbf6cbba 100644 --- a/arch/powerpc/configs/g5_defconfig +++ b/arch/powerpc/configs/g5_defconfig @@ -194,7 +194,6 @@ CONFIG_USB_SERIAL_SAFE=m CONFIG_USB_SERIAL_SAFE_PADDED=y CONFIG_USB_SERIAL_TI=m CONFIG_USB_SERIAL_CYBERJACK=m -CONFIG_USB_SERIAL_XIRCOM=m CONFIG_USB_SERIAL_OMNINET=m CONFIG_USB_APPLEDISPLAY=m CONFIG_EXT2_FS=y diff --git a/arch/powerpc/configs/ppc6xx_defconfig b/arch/powerpc/configs/ppc6xx_defconfig index 66e9a0fd64ff..ac92cbe1f581 100644 --- a/arch/powerpc/configs/ppc6xx_defconfig +++ b/arch/powerpc/configs/ppc6xx_defconfig @@ -911,7 +911,6 @@ CONFIG_USB_SERIAL_SAFE_PADDED=y CONFIG_USB_SERIAL_SIERRAWIRELESS=m CONFIG_USB_SERIAL_TI=m CONFIG_USB_SERIAL_CYBERJACK=m -CONFIG_USB_SERIAL_XIRCOM=m CONFIG_USB_SERIAL_OPTION=m CONFIG_USB_SERIAL_OMNINET=m CONFIG_USB_SERIAL_DEBUG=m diff --git a/drivers/net/thunderbolt.c b/drivers/net/thunderbolt.c index ae83d66195a5..ed3743dc62b9 100644 --- a/drivers/net/thunderbolt.c +++ b/drivers/net/thunderbolt.c @@ -866,7 +866,7 @@ static int tbnet_open(struct net_device *dev) eof_mask = BIT(TBIP_PDF_FRAME_END); ring = tb_ring_alloc_rx(xd->tb->nhi, -1, TBNET_RING_SIZE, - RING_FLAG_FRAME, sof_mask, eof_mask, + RING_FLAG_FRAME, 0, sof_mask, eof_mask, tbnet_start_poll, net); if (!ring) { netdev_err(dev, "failed to allocate Rx ring\n"); diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c index 31be31161350..8111ed1fc574 100644 --- a/drivers/platform/chrome/cros_ec_typec.c +++ b/drivers/platform/chrome/cros_ec_typec.c @@ -438,8 +438,7 @@ static int cros_typec_enable_tbt(struct cros_typec_data *typec, if (pd_ctrl->control_flags & USB_PD_CTRL_ACTIVE_LINK_UNIDIR) data.cable_mode |= TBT_CABLE_LINK_TRAINING; - if (pd_ctrl->cable_gen) - data.cable_mode |= TBT_CABLE_ROUNDED; + data.cable_mode |= TBT_SET_CABLE_ROUNDED(pd_ctrl->cable_gen); /* Enter Mode VDO */ data.enter_vdo = TBT_SET_CABLE_SPEED(pd_ctrl->cable_speed); diff --git a/drivers/thunderbolt/Kconfig b/drivers/thunderbolt/Kconfig index 7fc058f81d00..4bfec8a28064 100644 --- a/drivers/thunderbolt/Kconfig +++ b/drivers/thunderbolt/Kconfig @@ -31,4 +31,17 @@ config USB4_KUNIT_TEST bool "KUnit tests" depends on KUNIT=y +config USB4_DMA_TEST + tristate "DMA traffic test driver" + depends on DEBUG_FS + help + This allows sending and receiving DMA traffic through loopback + connection. Loopback connection can be done by either special + dongle that has TX/RX lines crossed, or by simply connecting a + cable back to the host. Only enable this if you know what you + are doing. Normal users and distro kernels should say N here. + + To compile this driver a module, choose M here. The module will be + called thunderbolt_dma_test. + endif # USB4 diff --git a/drivers/thunderbolt/Makefile b/drivers/thunderbolt/Makefile index 571537371072..7aa48f6c41d9 100644 --- a/drivers/thunderbolt/Makefile +++ b/drivers/thunderbolt/Makefile @@ -7,3 +7,6 @@ thunderbolt-objs += nvm.o retimer.o quirks.o thunderbolt-${CONFIG_ACPI} += acpi.o thunderbolt-$(CONFIG_DEBUG_FS) += debugfs.o thunderbolt-${CONFIG_USB4_KUNIT_TEST} += test.o + +thunderbolt_dma_test-${CONFIG_USB4_DMA_TEST} += dma_test.o +obj-$(CONFIG_USB4_DMA_TEST) += thunderbolt_dma_test.o diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c index 9894b8f63064..bac08b820015 100644 --- a/drivers/thunderbolt/ctl.c +++ b/drivers/thunderbolt/ctl.c @@ -628,8 +628,8 @@ struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, event_cb cb, void *cb_data) if (!ctl->tx) goto err; - ctl->rx = tb_ring_alloc_rx(nhi, 0, 10, RING_FLAG_NO_SUSPEND, 0xffff, - 0xffff, NULL, NULL); + ctl->rx = tb_ring_alloc_rx(nhi, 0, 10, RING_FLAG_NO_SUSPEND, 0, 0xffff, + 0xffff, NULL, NULL); if (!ctl->rx) goto err; @@ -962,6 +962,9 @@ static int tb_cfg_get_error(struct tb_ctl *ctl, enum tb_cfg_space space, if (res->tb_error == TB_CFG_ERROR_LOCK) return -EACCES; + else if (res->tb_error == TB_CFG_ERROR_PORT_NOT_CONNECTED) + return -ENOTCONN; + return -EIO; } diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c index ed65d2b13964..9541d7409ab1 100644 --- a/drivers/thunderbolt/debugfs.c +++ b/drivers/thunderbolt/debugfs.c @@ -691,6 +691,30 @@ void tb_switch_debugfs_remove(struct tb_switch *sw) debugfs_remove_recursive(sw->debugfs_dir); } +/** + * tb_service_debugfs_init() - Add debugfs directory for service + * @svc: Thunderbolt service pointer + * + * Adds debugfs directory for service. + */ +void tb_service_debugfs_init(struct tb_service *svc) +{ + svc->debugfs_dir = debugfs_create_dir(dev_name(&svc->dev), + tb_debugfs_root); +} + +/** + * tb_service_debugfs_remove() - Remove service debugfs directory + * @svc: Thunderbolt service pointer + * + * Removes the previously created debugfs directory for @svc. + */ +void tb_service_debugfs_remove(struct tb_service *svc) +{ + debugfs_remove_recursive(svc->debugfs_dir); + svc->debugfs_dir = NULL; +} + void tb_debugfs_init(void) { tb_debugfs_root = debugfs_create_dir("thunderbolt", NULL); diff --git a/drivers/thunderbolt/dma_test.c b/drivers/thunderbolt/dma_test.c new file mode 100644 index 000000000000..f924423fa180 --- /dev/null +++ b/drivers/thunderbolt/dma_test.c @@ -0,0 +1,736 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * DMA traffic test driver + * + * Copyright (C) 2020, Intel Corporation + * Authors: Isaac Hazan <isaac.hazan@intel.com> + * Mika Westerberg <mika.westerberg@linux.intel.com> + */ + +#include <linux/acpi.h> +#include <linux/completion.h> +#include <linux/debugfs.h> +#include <linux/module.h> +#include <linux/sizes.h> +#include <linux/thunderbolt.h> + +#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 +#define DMA_TEST_DATA_PATTERN 0x0123456789abcdefLL +#define DMA_TEST_MAX_PACKETS 1000 + +enum dma_test_frame_pdf { + DMA_TEST_PDF_FRAME_START = 1, + DMA_TEST_PDF_FRAME_END, +}; + +struct dma_test_frame { + struct dma_test *dma_test; + void *data; + struct ring_frame frame; +}; + +enum dma_test_test_error { + DMA_TEST_NO_ERROR, + DMA_TEST_INTERRUPTED, + DMA_TEST_BUFFER_ERROR, + DMA_TEST_DMA_ERROR, + DMA_TEST_CONFIG_ERROR, + DMA_TEST_SPEED_ERROR, + DMA_TEST_WIDTH_ERROR, + DMA_TEST_BONDING_ERROR, + DMA_TEST_PACKET_ERROR, +}; + +static const char * const dma_test_error_names[] = { + [DMA_TEST_NO_ERROR] = "no errors", + [DMA_TEST_INTERRUPTED] = "interrupted by signal", + [DMA_TEST_BUFFER_ERROR] = "no memory for packet buffers", + [DMA_TEST_DMA_ERROR] = "DMA ring setup failed", + [DMA_TEST_CONFIG_ERROR] = "configuration is not valid", + [DMA_TEST_SPEED_ERROR] = "unexpected link speed", + [DMA_TEST_WIDTH_ERROR] = "unexpected link width", + [DMA_TEST_BONDING_ERROR] = "lane bonding configuration error", + [DMA_TEST_PACKET_ERROR] = "packet check failed", +}; + +enum dma_test_result { + DMA_TEST_NOT_RUN, + DMA_TEST_SUCCESS, + DMA_TEST_FAIL, +}; + +static const char * const dma_test_result_names[] = { + [DMA_TEST_NOT_RUN] = "not run", + [DMA_TEST_SUCCESS] = "success", + [DMA_TEST_FAIL] = "failed", +}; + +/** + * struct dma_test - DMA test device driver private data + * @svc: XDomain service the driver is bound to + * @xd: XDomain the service belongs to + * @rx_ring: Software ring holding RX frames + * @tx_ring: Software ring holding TX frames + * @packets_to_send: Number of packets to send + * @packets_to_receive: Number of packets to receive + * @packets_sent: Actual number of packets sent + * @packets_received: Actual number of packets received + * @link_speed: Expected link speed (Gb/s), %0 to use whatever is negotiated + * @link_width: Expected link width (Gb/s), %0 to use whatever is negotiated + * @crc_errors: Number of CRC errors during the test run + * @buffer_overflow_errors: Number of buffer overflow errors during the test + * run + * @result: Result of the last run + * @error_code: Error code of the last run + * @complete: Used to wait for the Rx to complete + * @lock: Lock serializing access to this structure + * @debugfs_dir: dentry of this dma_test + */ +struct dma_test { + const struct tb_service *svc; + struct tb_xdomain *xd; + struct tb_ring *rx_ring; + struct tb_ring *tx_ring; + unsigned int packets_to_send; + unsigned int packets_to_receive; + unsigned int packets_sent; + unsigned int packets_received; + unsigned int link_speed; + unsigned int link_width; + unsigned int crc_errors; + unsigned int buffer_overflow_errors; + enum dma_test_result result; + enum dma_test_test_error error_code; + struct completion complete; + struct mutex lock; + struct dentry *debugfs_dir; +}; + +/* DMA test property directory UUID: 3188cd10-6523-4a5a-a682-fdca07a248d8 */ +static const uuid_t dma_test_dir_uuid = + UUID_INIT(0x3188cd10, 0x6523, 0x4a5a, + 0xa6, 0x82, 0xfd, 0xca, 0x07, 0xa2, 0x48, 0xd8); + +static struct tb_property_dir *dma_test_dir; +static void *dma_test_pattern; + +static void dma_test_free_rings(struct dma_test *dt) +{ + if (dt->rx_ring) { + tb_ring_free(dt->rx_ring); + dt->rx_ring = NULL; + } + if (dt->tx_ring) { + tb_ring_free(dt->tx_ring); + dt->tx_ring = NULL; + } +} + +static int dma_test_start_rings(struct dma_test *dt) +{ + unsigned int flags = RING_FLAG_FRAME; + struct tb_xdomain *xd = dt->xd; + int ret, e2e_tx_hop = 0; + struct tb_ring *ring; + + /* + * If we are both sender and receiver (traffic goes over a + * special loopback dongle) enable E2E flow control. This avoids + * losing packets. + */ + if (dt->packets_to_send && dt->packets_to_receive) + flags |= RING_FLAG_E2E; + + if (dt->packets_to_send) { + ring = tb_ring_alloc_tx(xd->tb->nhi, -1, DMA_TEST_TX_RING_SIZE, + flags); + if (!ring) + return -ENOMEM; + + dt->tx_ring = ring; + e2e_tx_hop = ring->hop; + } + + if (dt->packets_to_receive) { + u16 sof_mask, eof_mask; + + sof_mask = BIT(DMA_TEST_PDF_FRAME_START); + eof_mask = BIT(DMA_TEST_PDF_FRAME_END); + + ring = tb_ring_alloc_rx(xd->tb->nhi, -1, DMA_TEST_RX_RING_SIZE, + flags, e2e_tx_hop, sof_mask, eof_mask, + NULL, NULL); + if (!ring) { + dma_test_free_rings(dt); + return -ENOMEM; + } + + dt->rx_ring = ring; + } + + ret = tb_xdomain_enable_paths(dt->xd, DMA_TEST_HOPID, + dt->tx_ring ? dt->tx_ring->hop : 0, + DMA_TEST_HOPID, + dt->rx_ring ? dt->rx_ring->hop : 0); + if (ret) { + dma_test_free_rings(dt); + return ret; + } + + if (dt->tx_ring) + tb_ring_start(dt->tx_ring); + if (dt->rx_ring) + tb_ring_start(dt->rx_ring); + + return 0; +} + +static void dma_test_stop_rings(struct dma_test *dt) +{ + 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)) + dev_warn(&dt->svc->dev, "failed to disable DMA paths\n"); + + dma_test_free_rings(dt); +} + +static void dma_test_rx_callback(struct tb_ring *ring, struct ring_frame *frame, + bool canceled) +{ + struct dma_test_frame *tf = container_of(frame, typeof(*tf), frame); + struct dma_test *dt = tf->dma_test; + struct device *dma_dev = tb_ring_dma_device(dt->rx_ring); + + dma_unmap_single(dma_dev, tf->frame.buffer_phy, DMA_TEST_FRAME_SIZE, + DMA_FROM_DEVICE); + kfree(tf->data); + + if (canceled) { + kfree(tf); + return; + } + + dt->packets_received++; + dev_dbg(&dt->svc->dev, "packet %u/%u received\n", dt->packets_received, + dt->packets_to_receive); + + if (tf->frame.flags & RING_DESC_CRC_ERROR) + dt->crc_errors++; + if (tf->frame.flags & RING_DESC_BUFFER_OVERRUN) + dt->buffer_overflow_errors++; + + kfree(tf); + + if (dt->packets_received == dt->packets_to_receive) + complete(&dt->complete); +} + +static int dma_test_submit_rx(struct dma_test *dt, size_t npackets) +{ + struct device *dma_dev = tb_ring_dma_device(dt->rx_ring); + int i; + + for (i = 0; i < npackets; i++) { + struct dma_test_frame *tf; + dma_addr_t dma_addr; + + tf = kzalloc(sizeof(*tf), GFP_KERNEL); + if (!tf) + return -ENOMEM; + + tf->data = kzalloc(DMA_TEST_FRAME_SIZE, GFP_KERNEL); + if (!tf->data) { + kfree(tf); + return -ENOMEM; + } + + dma_addr = dma_map_single(dma_dev, tf->data, DMA_TEST_FRAME_SIZE, + DMA_FROM_DEVICE); + if (dma_mapping_error(dma_dev, dma_addr)) { + kfree(tf->data); + kfree(tf); + return -ENOMEM; + } + + tf->frame.buffer_phy = dma_addr; + tf->frame.callback = dma_test_rx_callback; + tf->dma_test = dt; + INIT_LIST_HEAD(&tf->frame.list); + + tb_ring_rx(dt->rx_ring, &tf->frame); + } + + return 0; +} + +static void dma_test_tx_callback(struct tb_ring *ring, struct ring_frame *frame, + bool canceled) +{ + struct dma_test_frame *tf = container_of(frame, typeof(*tf), frame); + struct dma_test *dt = tf->dma_test; + struct device *dma_dev = tb_ring_dma_device(dt->tx_ring); + + dma_unmap_single(dma_dev, tf->frame.buffer_phy, DMA_TEST_FRAME_SIZE, + DMA_TO_DEVICE); + kfree(tf->data); + kfree(tf); +} + +static int dma_test_submit_tx(struct dma_test *dt, size_t npackets) +{ + struct device *dma_dev = tb_ring_dma_device(dt->tx_ring); + int i; + + for (i = 0; i < npackets; i++) { + struct dma_test_frame *tf; + dma_addr_t dma_addr; + + tf = kzalloc(sizeof(*tf), GFP_KERNEL); + if (!tf) + return -ENOMEM; + + tf->frame.size = 0; /* means 4096 */ + tf->dma_test = dt; + + tf->data = kzalloc(DMA_TEST_FRAME_SIZE, GFP_KERNEL); + if (!tf->data) { + kfree(tf); + return -ENOMEM; + } + + memcpy(tf->data, dma_test_pattern, DMA_TEST_FRAME_SIZE); + + dma_addr = dma_map_single(dma_dev, tf->data, DMA_TEST_FRAME_SIZE, + DMA_TO_DEVICE); + if (dma_mapping_error(dma_dev, dma_addr)) { + kfree(tf->data); + kfree(tf); + return -ENOMEM; + } + + tf->frame.buffer_phy = dma_addr; + tf->frame.callback = dma_test_tx_callback; + tf->frame.sof = DMA_TEST_PDF_FRAME_START; + tf->frame.eof = DMA_TEST_PDF_FRAME_END; + INIT_LIST_HEAD(&tf->frame.list); + + dt->packets_sent++; + dev_dbg(&dt->svc->dev, "packet %u/%u sent\n", dt->packets_sent, + dt->packets_to_send); + + tb_ring_tx(dt->tx_ring, &tf->frame); + } + + return 0; +} + +#define DMA_TEST_DEBUGFS_ATTR(__fops, __get, __validate, __set) \ +static int __fops ## _show(void *data, u64 *val) \ +{ \ + struct tb_service *svc = data; \ + struct dma_test *dt = tb_service_get_drvdata(svc); \ + int ret; \ + \ + ret = mutex_lock_interruptible(&dt->lock); \ + if (ret) \ + return ret; \ + __get(dt, val); \ + mutex_unlock(&dt->lock); \ + return 0; \ +} \ +static int __fops ## _store(void *data, u64 val) \ +{ \ + struct tb_service *svc = data; \ + struct dma_test *dt = tb_service_get_drvdata(svc); \ + int ret; \ + \ + ret = __validate(val); \ + if (ret) \ + return ret; \ + ret = mutex_lock_interruptible(&dt->lock); \ + if (ret) \ + return ret; \ + __set(dt, val); \ + mutex_unlock(&dt->lock); \ + return 0; \ +} \ +DEFINE_DEBUGFS_ATTRIBUTE(__fops ## _fops, __fops ## _show, \ + __fops ## _store, "%llu\n") + +static void lanes_get(const struct dma_test *dt, u64 *val) +{ + *val = dt->link_width; +} + +static int lanes_validate(u64 val) +{ + return val > 2 ? -EINVAL : 0; +} + +static void lanes_set(struct dma_test *dt, u64 val) +{ + dt->link_width = val; +} +DMA_TEST_DEBUGFS_ATTR(lanes, lanes_get, lanes_validate, lanes_set); + +static void speed_get(const struct dma_test *dt, u64 *val) +{ + *val = dt->link_speed; +} + +static int speed_validate(u64 val) +{ + switch (val) { + case 20: + case 10: + case 0: + return 0; + default: + return -EINVAL; + } +} + +static void speed_set(struct dma_test *dt, u64 val) +{ + dt->link_speed = val; +} +DMA_TEST_DEBUGFS_ATTR(speed, speed_get, speed_validate, speed_set); + +static void packets_to_receive_get(const struct dma_test *dt, u64 *val) +{ + *val = dt->packets_to_receive; +} + +static int packets_to_receive_validate(u64 val) +{ + return val > DMA_TEST_MAX_PACKETS ? -EINVAL : 0; +} + +static void packets_to_receive_set(struct dma_test *dt, u64 val) +{ + dt->packets_to_receive = val; +} +DMA_TEST_DEBUGFS_ATTR(packets_to_receive, packets_to_receive_get, + packets_to_receive_validate, packets_to_receive_set); + +static void packets_to_send_get(const struct dma_test *dt, u64 *val) +{ + *val = dt->packets_to_send; +} + +static int packets_to_send_validate(u64 val) +{ + return val > DMA_TEST_MAX_PACKETS ? -EINVAL : 0; +} + +static void packets_to_send_set(struct dma_test *dt, u64 val) +{ + dt->packets_to_send = val; +} +DMA_TEST_DEBUGFS_ATTR(packets_to_send, packets_to_send_get, + packets_to_send_validate, packets_to_send_set); + +static int dma_test_set_bonding(struct dma_test *dt) +{ + switch (dt->link_width) { + case 2: + return tb_xdomain_lane_bonding_enable(dt->xd); + case 1: + tb_xdomain_lane_bonding_disable(dt->xd); + fallthrough; + default: + return 0; + } +} + +static bool dma_test_validate_config(struct dma_test *dt) +{ + if (!dt->packets_to_send && !dt->packets_to_receive) + return false; + if (dt->packets_to_send && dt->packets_to_receive && + dt->packets_to_send != dt->packets_to_receive) + return false; + return true; +} + +static void dma_test_check_errors(struct dma_test *dt, int ret) +{ + if (!dt->error_code) { + if (dt->link_speed && dt->xd->link_speed != dt->link_speed) { + dt->error_code = DMA_TEST_SPEED_ERROR; + } else if (dt->link_width && + dt->xd->link_width != dt->link_width) { + dt->error_code = DMA_TEST_WIDTH_ERROR; + } else if (dt->packets_to_send != dt->packets_sent || + dt->packets_to_receive != dt->packets_received || + dt->crc_errors || dt->buffer_overflow_errors) { + dt->error_code = DMA_TEST_PACKET_ERROR; + } else { + return; + } + } + + dt->result = DMA_TEST_FAIL; +} + +static int test_store(void *data, u64 val) +{ + struct tb_service *svc = data; + struct dma_test *dt = tb_service_get_drvdata(svc); + int ret; + + if (val != 1) + return -EINVAL; + + ret = mutex_lock_interruptible(&dt->lock); + if (ret) + return ret; + + dt->packets_sent = 0; + dt->packets_received = 0; + dt->crc_errors = 0; + dt->buffer_overflow_errors = 0; + dt->result = DMA_TEST_SUCCESS; + dt->error_code = DMA_TEST_NO_ERROR; + + dev_dbg(&svc->dev, "DMA test starting\n"); + if (dt->link_speed) + dev_dbg(&svc->dev, "link_speed: %u Gb/s\n", dt->link_speed); + if (dt->link_width) + dev_dbg(&svc->dev, "link_width: %u\n", dt->link_width); + dev_dbg(&svc->dev, "packets_to_send: %u\n", dt->packets_to_send); + dev_dbg(&svc->dev, "packets_to_receive: %u\n", dt->packets_to_receive); + + if (!dma_test_validate_config(dt)) { + dev_err(&svc->dev, "invalid test configuration\n"); + dt->error_code = DMA_TEST_CONFIG_ERROR; + goto out_unlock; + } + + ret = dma_test_set_bonding(dt); + if (ret) { + dev_err(&svc->dev, "failed to set lanes\n"); + dt->error_code = DMA_TEST_BONDING_ERROR; + goto out_unlock; + } + + ret = dma_test_start_rings(dt); + if (ret) { + dev_err(&svc->dev, "failed to enable DMA rings\n"); + dt->error_code = DMA_TEST_DMA_ERROR; + goto out_unlock; + } + + if (dt->packets_to_receive) { + reinit_completion(&dt->complete); + ret = dma_test_submit_rx(dt, dt->packets_to_receive); + if (ret) { + dev_err(&svc->dev, "failed to submit receive buffers\n"); + dt->error_code = DMA_TEST_BUFFER_ERROR; + goto out_stop; + } + } + + if (dt->packets_to_send) { + ret = dma_test_submit_tx(dt, dt->packets_to_send); + if (ret) { + dev_err(&svc->dev, "failed to submit transmit buffers\n"); + dt->error_code = DMA_TEST_BUFFER_ERROR; + goto out_stop; + } + } + + if (dt->packets_to_receive) { + ret = wait_for_completion_interruptible(&dt->complete); + if (ret) { + dt->error_code = DMA_TEST_INTERRUPTED; + goto out_stop; + } + } + +out_stop: + dma_test_stop_rings(dt); +out_unlock: + dma_test_check_errors(dt, ret); + mutex_unlock(&dt->lock); + + dev_dbg(&svc->dev, "DMA test %s\n", dma_test_result_names[dt->result]); + return ret; +} +DEFINE_DEBUGFS_ATTRIBUTE(test_fops, NULL, test_store, "%llu\n"); + +static int status_show(struct seq_file *s, void *not_used) +{ + struct tb_service *svc = s->private; + struct dma_test *dt = tb_service_get_drvdata(svc); + int ret; + + ret = mutex_lock_interruptible(&dt->lock); + if (ret) + return ret; + + seq_printf(s, "result: %s\n", dma_test_result_names[dt->result]); + if (dt->result == DMA_TEST_NOT_RUN) + goto out_unlock; + + seq_printf(s, "packets received: %u\n", dt->packets_received); + seq_printf(s, "packets sent: %u\n", dt->packets_sent); + seq_printf(s, "CRC errors: %u\n", dt->crc_errors); + seq_printf(s, "buffer overflow errors: %u\n", + dt->buffer_overflow_errors); + seq_printf(s, "error: %s\n", dma_test_error_names[dt->error_code]); + +out_unlock: + mutex_unlock(&dt->lock); + return 0; +} +DEFINE_SHOW_ATTRIBUTE(status); + +static void dma_test_debugfs_init(struct tb_service *svc) +{ + struct dma_test *dt = tb_service_get_drvdata(svc); + + dt->debugfs_dir = debugfs_create_dir("dma_test", svc->debugfs_dir); + + debugfs_create_file("lanes", 0600, dt->debugfs_dir, svc, &lanes_fops); + debugfs_create_file("speed", 0600, dt->debugfs_dir, svc, &speed_fops); + debugfs_create_file("packets_to_receive", 0600, dt->debugfs_dir, svc, + &packets_to_receive_fops); + debugfs_create_file("packets_to_send", 0600, dt->debugfs_dir, svc, + &packets_to_send_fops); + debugfs_create_file("status", 0400, dt->debugfs_dir, svc, &status_fops); + debugfs_create_file("test", 0200, dt->debugfs_dir, svc, &test_fops); +} + +static int dma_test_probe(struct tb_service *svc, const struct tb_service_id *id) +{ + struct tb_xdomain *xd = tb_service_parent(svc); + struct dma_test *dt; + + dt = devm_kzalloc(&svc->dev, sizeof(*dt), GFP_KERNEL); + if (!dt) + return -ENOMEM; + + dt->svc = svc; + dt->xd = xd; + mutex_init(&dt->lock); + init_completion(&dt->complete); + + tb_service_set_drvdata(svc, dt); + dma_test_debugfs_init(svc); + + return 0; +} + +static void dma_test_remove(struct tb_service *svc) +{ + struct dma_test *dt = tb_service_get_drvdata(svc); + + mutex_lock(&dt->lock); + debugfs_remove_recursive(dt->debugfs_dir); + mutex_unlock(&dt->lock); +} + +static int __maybe_unused dma_test_suspend(struct device *dev) +{ + /* + * No need to do anything special here. If userspace is writing + * to the test attribute when suspend started, it comes out from + * wait_for_completion_interruptible() with -ERESTARTSYS and the + * DMA test fails tearing down the rings. Once userspace is + * thawed the kernel restarts the write syscall effectively + * re-running the test. + */ + return 0; +} + +static int __maybe_unused dma_test_resume(struct device *dev) +{ + return 0; +} + +static const struct dev_pm_ops dma_test_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(dma_test_suspend, dma_test_resume) +}; + +static const struct tb_service_id dma_test_ids[] = { + { TB_SERVICE("dma_test", 1) }, + { }, +}; +MODULE_DEVICE_TABLE(tbsvc, dma_test_ids); + +static struct tb_service_driver dma_test_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "thunderbolt_dma_test", + .pm = &dma_test_pm_ops, + }, + .probe = dma_test_probe, + .remove = dma_test_remove, + .id_table = dma_test_ids, +}; + +static int __init dma_test_init(void) +{ + u64 data_value = DMA_TEST_DATA_PATTERN; + int i, ret; + + dma_test_pattern = kmalloc(DMA_TEST_FRAME_SIZE, GFP_KERNEL); + if (!dma_test_pattern) + return -ENOMEM; + + for (i = 0; i < DMA_TEST_FRAME_SIZE / sizeof(data_value); i++) + ((u32 *)dma_test_pattern)[i] = data_value++; + + dma_test_dir = tb_property_create_dir(&dma_test_dir_uuid); + if (!dma_test_dir) { + ret = -ENOMEM; + goto err_free_pattern; + } + + tb_property_add_immediate(dma_test_dir, "prtcid", 1); + tb_property_add_immediate(dma_test_dir, "prtcvers", 1); + tb_property_add_immediate(dma_test_dir, "prtcrevs", 0); + tb_property_add_immediate(dma_test_dir, "prtcstns", 0); + + ret = tb_register_property_dir("dma_test", dma_test_dir); + if (ret) + goto err_free_dir; + + ret = tb_register_service_driver(&dma_test_driver); + if (ret) + goto err_unregister_dir; + + return 0; + +err_unregister_dir: + tb_unregister_property_dir("dma_test", dma_test_dir); +err_free_dir: + tb_property_free_dir(dma_test_dir); +err_free_pattern: + kfree(dma_test_pattern); + + return ret; +} +module_init(dma_test_init); + +static void __exit dma_test_exit(void) +{ + tb_unregister_service_driver(&dma_test_driver); + tb_unregister_property_dir("dma_test", dma_test_dir); + tb_property_free_dir(dma_test_dir); + kfree(dma_test_pattern); +} +module_exit(dma_test_exit); + +MODULE_AUTHOR("Isaac Hazan <isaac.hazan@intel.com>"); +MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>"); +MODULE_DESCRIPTION("DMA traffic test driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c index 82c46b200c34..8b7f941a9bb7 100644 --- a/drivers/thunderbolt/icm.c +++ b/drivers/thunderbolt/icm.c @@ -49,6 +49,18 @@ module_param(start_icm, bool, 0444); MODULE_PARM_DESC(start_icm, "start ICM firmware if it is not running (default: false)"); /** + * struct usb4_switch_nvm_auth - Holds USB4 NVM_AUTH status + * @reply: Reply from ICM firmware is placed here + * @request: Request that is sent to ICM firmware + * @icm: Pointer to ICM private data + */ +struct usb4_switch_nvm_auth { + struct icm_usb4_switch_op_response reply; + struct icm_usb4_switch_op request; + struct icm *icm; +}; + +/** * struct icm - Internal connection manager private data * @request_lock: Makes sure only one message is send to ICM at time * @rescan_work: Work used to rescan the surviving switches after resume @@ -61,6 +73,8 @@ MODULE_PARM_DESC(start_icm, "start ICM firmware if it is not running (default: f * @max_boot_acl: Maximum number of preboot ACL entries (%0 if not supported) * @rpm: Does the controller support runtime PM (RTD3) * @can_upgrade_nvm: Can the NVM firmware be upgrade on this controller + * @proto_version: Firmware protocol version + * @last_nvm_auth: Last USB4 router NVM_AUTH result (or %NULL if not set) * @veto: Is RTD3 veto in effect * @is_supported: Checks if we can support ICM on this controller * @cio_reset: Trigger CIO reset @@ -79,11 +93,13 @@ struct icm { struct mutex request_lock; struct delayed_work rescan_work; struct pci_dev *upstream_port; - size_t max_boot_acl; int vnd_cap; bool safe_mode; + size_t max_boot_acl; bool rpm; bool can_upgrade_nvm; + u8 proto_version; + struct usb4_switch_nvm_auth *last_nvm_auth; bool veto; bool (*is_supported)(struct tb *tb); int (*cio_reset)(struct tb *tb); @@ -92,7 +108,7 @@ struct icm { void (*save_devices)(struct tb *tb); int (*driver_ready)(struct tb *tb, enum tb_security_level *security_level, - size_t *nboot_acl, bool *rpm); + u8 *proto_version, size_t *nboot_acl, bool *rpm); void (*set_uuid)(struct tb *tb); void (*device_connected)(struct tb *tb, const struct icm_pkg_header *hdr); @@ -437,7 +453,7 @@ static void icm_fr_save_devices(struct tb *tb) static int icm_fr_driver_ready(struct tb *tb, enum tb_security_level *security_level, - size_t *nboot_acl, bool *rpm) + u8 *proto_version, size_t *nboot_acl, bool *rpm) { struct icm_fr_pkg_driver_ready_response reply; struct icm_pkg_driver_ready request = { @@ -870,7 +886,13 @@ icm_fr_device_disconnected(struct tb *tb, const struct icm_pkg_header *hdr) return; } + pm_runtime_get_sync(sw->dev.parent); + remove_switch(sw); + + pm_runtime_mark_last_busy(sw->dev.parent); + pm_runtime_put_autosuspend(sw->dev.parent); + tb_switch_put(sw); } @@ -986,7 +1008,7 @@ static int icm_tr_cio_reset(struct tb *tb) static int icm_tr_driver_ready(struct tb *tb, enum tb_security_level *security_level, - size_t *nboot_acl, bool *rpm) + u8 *proto_version, size_t *nboot_acl, bool *rpm) { struct icm_tr_pkg_driver_ready_response reply; struct icm_pkg_driver_ready request = { @@ -1002,6 +1024,9 @@ icm_tr_driver_ready(struct tb *tb, enum tb_security_level *security_level, if (security_level) *security_level = reply.info & ICM_TR_INFO_SLEVEL_MASK; + if (proto_version) + *proto_version = (reply.info & ICM_TR_INFO_PROTO_VERSION_MASK) >> + ICM_TR_INFO_PROTO_VERSION_SHIFT; if (nboot_acl) *nboot_acl = (reply.info & ICM_TR_INFO_BOOT_ACL_MASK) >> ICM_TR_INFO_BOOT_ACL_SHIFT; @@ -1280,8 +1305,13 @@ icm_tr_device_disconnected(struct tb *tb, const struct icm_pkg_header *hdr) tb_warn(tb, "no switch exists at %llx, ignoring\n", route); return; } + pm_runtime_get_sync(sw->dev.parent); remove_switch(sw); + + pm_runtime_mark_last_busy(sw->dev.parent); + pm_runtime_put_autosuspend(sw->dev.parent); + tb_switch_put(sw); } @@ -1450,7 +1480,7 @@ static int icm_ar_get_mode(struct tb *tb) static int icm_ar_driver_ready(struct tb *tb, enum tb_security_level *security_level, - size_t *nboot_acl, bool *rpm) + u8 *proto_version, size_t *nboot_acl, bool *rpm) { struct icm_ar_pkg_driver_ready_response reply; struct icm_pkg_driver_ready request = { @@ -1580,7 +1610,7 @@ static int icm_ar_set_boot_acl(struct tb *tb, const uuid_t *uuids, static int icm_icl_driver_ready(struct tb *tb, enum tb_security_level *security_level, - size_t *nboot_acl, bool *rpm) + u8 *proto_version, size_t *nboot_acl, bool *rpm) { struct icm_tr_pkg_driver_ready_response reply; struct icm_pkg_driver_ready request = { @@ -1594,6 +1624,10 @@ icm_icl_driver_ready(struct tb *tb, enum tb_security_level *security_level, if (ret) return ret; + if (proto_version) + *proto_version = (reply.info & ICM_TR_INFO_PROTO_VERSION_MASK) >> + ICM_TR_INFO_PROTO_VERSION_SHIFT; + /* Ice Lake always supports RTD3 */ if (rpm) *rpm = true; @@ -1702,13 +1736,14 @@ static void icm_handle_event(struct tb *tb, enum tb_cfg_pkg_type type, static int __icm_driver_ready(struct tb *tb, enum tb_security_level *security_level, - size_t *nboot_acl, bool *rpm) + u8 *proto_version, size_t *nboot_acl, bool *rpm) { struct icm *icm = tb_priv(tb); unsigned int retries = 50; int ret; - ret = icm->driver_ready(tb, security_level, nboot_acl, rpm); + ret = icm->driver_ready(tb, security_level, proto_version, nboot_acl, + rpm); if (ret) { tb_err(tb, "failed to send driver ready to ICM\n"); return ret; @@ -1918,8 +1953,8 @@ static int icm_driver_ready(struct tb *tb) return 0; } - ret = __icm_driver_ready(tb, &tb->security_level, &tb->nboot_acl, - &icm->rpm); + ret = __icm_driver_ready(tb, &tb->security_level, &icm->proto_version, + &tb->nboot_acl, &icm->rpm); if (ret) return ret; @@ -1930,6 +1965,9 @@ static int icm_driver_ready(struct tb *tb) if (tb->nboot_acl > icm->max_boot_acl) tb->nboot_acl = 0; + if (icm->proto_version >= 3) + tb_dbg(tb, "USB4 proxy operations supported\n"); + return 0; } @@ -2045,7 +2083,7 @@ static void icm_complete(struct tb *tb) * Now all existing children should be resumed, start events * from ICM to get updated status. */ - __icm_driver_ready(tb, NULL, NULL, NULL); + __icm_driver_ready(tb, NULL, NULL, NULL, NULL); /* * We do not get notifications of devices that have been @@ -2124,6 +2162,8 @@ static void icm_stop(struct tb *tb) tb_switch_remove(tb->root_switch); tb->root_switch = NULL; nhi_mailbox_cmd(tb->nhi, NHI_MAILBOX_DRV_UNLOADS, 0); + kfree(icm->last_nvm_auth); + icm->last_nvm_auth = NULL; } static int icm_disconnect_pcie_paths(struct tb *tb) @@ -2131,6 +2171,165 @@ static int icm_disconnect_pcie_paths(struct tb *tb) return nhi_mailbox_cmd(tb->nhi, NHI_MAILBOX_DISCONNECT_PCIE_PATHS, 0); } +static void icm_usb4_switch_nvm_auth_complete(void *data) +{ + struct usb4_switch_nvm_auth *auth = data; + struct icm *icm = auth->icm; + struct tb *tb = icm_to_tb(icm); + + tb_dbg(tb, "NVM_AUTH response for %llx flags %#x status %#x\n", + get_route(auth->reply.route_hi, auth->reply.route_lo), + auth->reply.hdr.flags, auth->reply.status); + + mutex_lock(&tb->lock); + if (WARN_ON(icm->last_nvm_auth)) + kfree(icm->last_nvm_auth); + icm->last_nvm_auth = auth; + mutex_unlock(&tb->lock); +} + +static int icm_usb4_switch_nvm_authenticate(struct tb *tb, u64 route) +{ + struct usb4_switch_nvm_auth *auth; + struct icm *icm = tb_priv(tb); + struct tb_cfg_request *req; + int ret; + + auth = kzalloc(sizeof(*auth), GFP_KERNEL); + if (!auth) + return -ENOMEM; + + auth->icm = icm; + auth->request.hdr.code = ICM_USB4_SWITCH_OP; + auth->request.route_hi = upper_32_bits(route); + auth->request.route_lo = lower_32_bits(route); + auth->request.opcode = USB4_SWITCH_OP_NVM_AUTH; + + req = tb_cfg_request_alloc(); + if (!req) { + ret = -ENOMEM; + goto err_free_auth; + } + + req->match = icm_match; + req->copy = icm_copy; + req->request = &auth->request; + req->request_size = sizeof(auth->request); + req->request_type = TB_CFG_PKG_ICM_CMD; + req->response = &auth->reply; + req->npackets = 1; + req->response_size = sizeof(auth->reply); + req->response_type = TB_CFG_PKG_ICM_RESP; + + tb_dbg(tb, "NVM_AUTH request for %llx\n", route); + + mutex_lock(&icm->request_lock); + ret = tb_cfg_request(tb->ctl, req, icm_usb4_switch_nvm_auth_complete, + auth); + mutex_unlock(&icm->request_lock); + + tb_cfg_request_put(req); + if (ret) + goto err_free_auth; + return 0; + +err_free_auth: + kfree(auth); + return ret; +} + +static int icm_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) +{ + struct icm_usb4_switch_op_response reply; + struct icm_usb4_switch_op request; + struct tb *tb = sw->tb; + struct icm *icm = tb_priv(tb); + u64 route = tb_route(sw); + int ret; + + /* + * USB4 router operation proxy is supported in firmware if the + * protocol version is 3 or higher. + */ + if (icm->proto_version < 3) + return -EOPNOTSUPP; + + /* + * NVM_AUTH is a special USB4 proxy operation that does not + * return immediately so handle it separately. + */ + if (opcode == USB4_SWITCH_OP_NVM_AUTH) + return icm_usb4_switch_nvm_authenticate(tb, route); + + memset(&request, 0, sizeof(request)); + request.hdr.code = ICM_USB4_SWITCH_OP; + request.route_hi = upper_32_bits(route); + request.route_lo = lower_32_bits(route); + request.opcode = opcode; + if (metadata) + request.metadata = *metadata; + + if (tx_data_len) { + request.data_len_valid |= ICM_USB4_SWITCH_DATA_VALID; + if (tx_data_len < ARRAY_SIZE(request.data)) + request.data_len_valid = + tx_data_len & ICM_USB4_SWITCH_DATA_LEN_MASK; + memcpy(request.data, tx_data, tx_data_len * sizeof(u32)); + } + + memset(&reply, 0, sizeof(reply)); + ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), + 1, ICM_TIMEOUT); + if (ret) + return ret; + + if (reply.hdr.flags & ICM_FLAGS_ERROR) + return -EIO; + + if (status) + *status = reply.status; + + if (metadata) + *metadata = reply.metadata; + + if (rx_data_len) + memcpy(rx_data, reply.data, rx_data_len * sizeof(u32)); + + return 0; +} + +static int icm_usb4_switch_nvm_authenticate_status(struct tb_switch *sw, + u32 *status) +{ + struct usb4_switch_nvm_auth *auth; + struct tb *tb = sw->tb; + struct icm *icm = tb_priv(tb); + int ret = 0; + + if (icm->proto_version < 3) + return -EOPNOTSUPP; + + auth = icm->last_nvm_auth; + icm->last_nvm_auth = NULL; + + if (auth && auth->reply.route_hi == sw->config.route_hi && + auth->reply.route_lo == sw->config.route_lo) { + tb_dbg(tb, "NVM_AUTH found for %llx flags 0x%#x status %#x\n", + tb_route(sw), auth->reply.hdr.flags, auth->reply.status); + if (auth->reply.hdr.flags & ICM_FLAGS_ERROR) + ret = -EIO; + else + *status = auth->reply.status; + } else { + *status = 0; + } + + kfree(auth); + return ret; +} + /* Falcon Ridge */ static const struct tb_cm_ops icm_fr_ops = { .driver_ready = icm_driver_ready, @@ -2189,6 +2388,9 @@ static const struct tb_cm_ops icm_tr_ops = { .disconnect_pcie_paths = icm_disconnect_pcie_paths, .approve_xdomain_paths = icm_tr_approve_xdomain_paths, .disconnect_xdomain_paths = icm_tr_disconnect_xdomain_paths, + .usb4_switch_op = icm_usb4_switch_op, + .usb4_switch_nvm_authenticate_status = + icm_usb4_switch_nvm_authenticate_status, }; /* Ice Lake */ @@ -2202,6 +2404,9 @@ static const struct tb_cm_ops icm_icl_ops = { .handle_event = icm_handle_event, .approve_xdomain_paths = icm_tr_approve_xdomain_paths, .disconnect_xdomain_paths = icm_tr_disconnect_xdomain_paths, + .usb4_switch_op = icm_usb4_switch_op, + .usb4_switch_nvm_authenticate_status = + icm_usb4_switch_nvm_authenticate_status, }; struct tb *icm_probe(struct tb_nhi *nhi) @@ -2300,6 +2505,17 @@ struct tb *icm_probe(struct tb_nhi *nhi) icm->rtd3_veto = icm_icl_rtd3_veto; tb->cm_ops = &icm_icl_ops; break; + + case PCI_DEVICE_ID_INTEL_MAPLE_RIDGE_4C_NHI: + icm->is_supported = icm_tgl_is_supported; + icm->get_mode = icm_ar_get_mode; + icm->driver_ready = icm_tr_driver_ready; + icm->device_connected = icm_tr_device_connected; + icm->device_disconnected = icm_tr_device_disconnected; + icm->xdomain_connected = icm_tr_xdomain_connected; + icm->xdomain_disconnected = icm_tr_xdomain_disconnected; + tb->cm_ops = &icm_tr_ops; + break; } if (!icm->is_supported || !icm->is_supported(tb)) { @@ -2308,5 +2524,7 @@ struct tb *icm_probe(struct tb_nhi *nhi) return NULL; } + tb_dbg(tb, "using firmware connection manager\n"); + return tb; } diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index db80dc5dfeba..cfc622da4f83 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -494,7 +494,7 @@ err_unlock: static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size, bool transmit, unsigned int flags, - u16 sof_mask, u16 eof_mask, + int e2e_tx_hop, u16 sof_mask, u16 eof_mask, void (*start_poll)(void *), void *poll_data) { @@ -517,6 +517,7 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size, ring->is_tx = transmit; ring->size = size; ring->flags = flags; + ring->e2e_tx_hop = e2e_tx_hop; ring->sof_mask = sof_mask; ring->eof_mask = eof_mask; ring->head = 0; @@ -561,7 +562,7 @@ err_free_ring: struct tb_ring *tb_ring_alloc_tx(struct tb_nhi *nhi, int hop, int size, unsigned int flags) { - return tb_ring_alloc(nhi, hop, size, true, flags, 0, 0, NULL, NULL); + return tb_ring_alloc(nhi, hop, size, true, flags, 0, 0, 0, NULL, NULL); } EXPORT_SYMBOL_GPL(tb_ring_alloc_tx); @@ -571,6 +572,7 @@ EXPORT_SYMBOL_GPL(tb_ring_alloc_tx); * @hop: HopID (ring) to allocate. Pass %-1 for automatic allocation. * @size: Number of entries in the ring * @flags: Flags for the ring + * @e2e_tx_hop: Transmit HopID when E2E is enabled in @flags * @sof_mask: Mask of PDF values that start a frame * @eof_mask: Mask of PDF values that end a frame * @start_poll: If not %NULL the ring will call this function when an @@ -579,10 +581,11 @@ EXPORT_SYMBOL_GPL(tb_ring_alloc_tx); * @poll_data: Optional data passed to @start_poll */ struct tb_ring *tb_ring_alloc_rx(struct tb_nhi *nhi, int hop, int size, - unsigned int flags, u16 sof_mask, u16 eof_mask, + unsigned int flags, int e2e_tx_hop, + u16 sof_mask, u16 eof_mask, void (*start_poll)(void *), void *poll_data) { - return tb_ring_alloc(nhi, hop, size, false, flags, sof_mask, eof_mask, + return tb_ring_alloc(nhi, hop, size, false, flags, e2e_tx_hop, sof_mask, eof_mask, start_poll, poll_data); } EXPORT_SYMBOL_GPL(tb_ring_alloc_rx); @@ -629,6 +632,31 @@ void tb_ring_start(struct tb_ring *ring) ring_iowrite32options(ring, sof_eof_mask, 4); ring_iowrite32options(ring, flags, 0); } + + /* + * Now that the ring valid bit is set we can configure E2E if + * enabled for the ring. + */ + if (ring->flags & RING_FLAG_E2E) { + if (!ring->is_tx) { + u32 hop; + + hop = ring->e2e_tx_hop << REG_RX_OPTIONS_E2E_HOP_SHIFT; + hop &= REG_RX_OPTIONS_E2E_HOP_MASK; + flags |= hop; + + dev_dbg(&ring->nhi->pdev->dev, + "enabling E2E for %s %d with TX HopID %d\n", + RING_TYPE(ring), ring->hop, ring->e2e_tx_hop); + } else { + dev_dbg(&ring->nhi->pdev->dev, "enabling E2E for %s %d\n", + RING_TYPE(ring), ring->hop); + } + + flags |= RING_FLAG_E2E_FLOW_CONTROL; + ring_iowrite32options(ring, flags, 0); + } + ring_interrupt_active(ring, true); ring->running = true; err: diff --git a/drivers/thunderbolt/nhi.h b/drivers/thunderbolt/nhi.h index 4e0861d75072..69770beca792 100644 --- a/drivers/thunderbolt/nhi.h +++ b/drivers/thunderbolt/nhi.h @@ -55,6 +55,7 @@ extern const struct tb_nhi_ops icl_nhi_ops; * need for the PCI quirk anymore as we will use ICM also on Apple * hardware. */ +#define PCI_DEVICE_ID_INTEL_MAPLE_RIDGE_4C_NHI 0x1137 #define PCI_DEVICE_ID_INTEL_WIN_RIDGE_2C_NHI 0x157d #define PCI_DEVICE_ID_INTEL_WIN_RIDGE_2C_BRIDGE 0x157e #define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_NHI 0x15bf diff --git a/drivers/thunderbolt/path.c b/drivers/thunderbolt/path.c index 03e7b714deab..ca7d738d66de 100644 --- a/drivers/thunderbolt/path.c +++ b/drivers/thunderbolt/path.c @@ -406,10 +406,17 @@ static int __tb_path_deactivate_hop(struct tb_port *port, int hop_index, if (!hop.pending) { if (clear_fc) { - /* Clear flow control */ - hop.ingress_fc = 0; + /* + * Clear flow control. Protocol adapters + * IFC and ISE bits are vendor defined + * in the USB4 spec so we clear them + * only for pre-USB4 adapters. + */ + if (!tb_switch_is_usb4(port->sw)) { + hop.ingress_fc = 0; + hop.ingress_shared_buffer = 0; + } hop.egress_fc = 0; - hop.ingress_shared_buffer = 0; hop.egress_shared_buffer = 0; return tb_port_write(port, &hop, TB_CFG_HOPS, @@ -447,7 +454,7 @@ void tb_path_deactivate(struct tb_path *path) return; } tb_dbg(path->tb, - "deactivating %s path from %llx:%x to %llx:%x\n", + "deactivating %s path from %llx:%u to %llx:%u\n", path->name, tb_route(path->hops[0].in_port->sw), path->hops[0].in_port->port, tb_route(path->hops[path->path_length - 1].out_port->sw), @@ -475,7 +482,7 @@ int tb_path_activate(struct tb_path *path) } tb_dbg(path->tb, - "activating %s path from %llx:%x to %llx:%x\n", + "activating %s path from %llx:%u to %llx:%u\n", path->name, tb_route(path->hops[0].in_port->sw), path->hops[0].in_port->port, tb_route(path->hops[path->path_length - 1].out_port->sw), diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index c73bbfe69ba1..a8572f49d3ad 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -503,12 +503,13 @@ static void tb_dump_port(struct tb *tb, struct tb_regs_port_header *port) /** * tb_port_state() - get connectedness state of a port + * @port: the port to check * * The port must have a TB_CAP_PHY (i.e. it should be a real port). * * Return: Returns an enum tb_port_state on success or an error code on failure. */ -static int tb_port_state(struct tb_port *port) +int tb_port_state(struct tb_port *port) { struct tb_cap_phy phy; int res; @@ -932,7 +933,14 @@ int tb_port_get_link_speed(struct tb_port *port) return speed == LANE_ADP_CS_1_CURRENT_SPEED_GEN3 ? 20 : 10; } -static int tb_port_get_link_width(struct tb_port *port) +/** + * tb_port_get_link_width() - Get current link width + * @port: Port to check (USB4 or CIO) + * + * Returns link width. Return values can be 1 (Single-Lane), 2 (Dual-Lane) + * or negative errno in case of failure. + */ +int tb_port_get_link_width(struct tb_port *port) { u32 val; int ret; @@ -1001,7 +1009,16 @@ static int tb_port_set_link_width(struct tb_port *port, unsigned int width) port->cap_phy + LANE_ADP_CS_1, 1); } -static int tb_port_lane_bonding_enable(struct tb_port *port) +/** + * tb_port_lane_bonding_enable() - Enable bonding on port + * @port: port to enable + * + * Enable bonding by setting the link width of the port and the + * other port in case of dual link port. + * + * Return: %0 in case of success and negative errno in case of error + */ +int tb_port_lane_bonding_enable(struct tb_port *port) { int ret; @@ -1031,7 +1048,15 @@ static int tb_port_lane_bonding_enable(struct tb_port *port) return 0; } -static void tb_port_lane_bonding_disable(struct tb_port *port) +/** + * tb_port_lane_bonding_disable() - Disable bonding on port + * @port: port to disable + * + * Disable bonding by setting the link width of the port and the + * other port in case of dual link port. + * + */ +void tb_port_lane_bonding_disable(struct tb_port *port) { port->dual_link_port->bonded = false; port->bonded = false; @@ -2135,6 +2160,7 @@ static int tb_switch_add_dma_port(struct tb_switch *sw) fallthrough; case 3: + case 4: ret = tb_switch_set_uuid(sw); if (ret) return ret; @@ -2150,6 +2176,22 @@ static int tb_switch_add_dma_port(struct tb_switch *sw) break; } + if (sw->no_nvm_upgrade) + return 0; + + if (tb_switch_is_usb4(sw)) { + ret = usb4_switch_nvm_authenticate_status(sw, &status); + if (ret) + return ret; + + if (status) { + tb_sw_info(sw, "switch flash authentication failed\n"); + nvm_set_auth_status(sw, status); + } + + return 0; + } + /* Root switch DMA port requires running firmware */ if (!tb_route(sw) && !tb_switch_is_icm(sw)) return 0; @@ -2158,9 +2200,6 @@ static int tb_switch_add_dma_port(struct tb_switch *sw) if (!sw->dma_port) return 0; - if (sw->no_nvm_upgrade) - return 0; - /* * If there is status already set then authentication failed * when the dma_port_flash_update_auth() returned. Power cycling diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 214fbc92c1b7..51d5b031cada 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -1534,5 +1534,7 @@ struct tb *tb_probe(struct tb_nhi *nhi) INIT_LIST_HEAD(&tcm->dp_resources); INIT_DELAYED_WORK(&tcm->remove_work, tb_remove_work); + tb_dbg(tb, "using software connection manager\n"); + return tb; } diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 8ea360b0ff77..554feda1e359 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -367,6 +367,14 @@ struct tb_path { * @disconnect_pcie_paths: Disconnects PCIe paths before NVM update * @approve_xdomain_paths: Approve (establish) XDomain DMA paths * @disconnect_xdomain_paths: Disconnect XDomain DMA paths + * @usb4_switch_op: Optional proxy for USB4 router operations. If set + * this will be called whenever USB4 router operation is + * performed. If this returns %-EOPNOTSUPP then the + * native USB4 router operation is called. + * @usb4_switch_nvm_authenticate_status: Optional callback that the CM + * implementation can be used to + * return status of USB4 NVM_AUTH + * router operation. */ struct tb_cm_ops { int (*driver_ready)(struct tb *tb); @@ -393,6 +401,11 @@ struct tb_cm_ops { 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 (*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); + int (*usb4_switch_nvm_authenticate_status)(struct tb_switch *sw, + u32 *status); }; static inline void *tb_priv(struct tb *tb) @@ -864,6 +877,10 @@ struct tb_port *tb_next_port_on_path(struct tb_port *start, struct tb_port *end, (p) = tb_next_port_on_path((src), (dst), (p))) int tb_port_get_link_speed(struct tb_port *port); +int tb_port_get_link_width(struct tb_port *port); +int tb_port_state(struct tb_port *port); +int tb_port_lane_bonding_enable(struct tb_port *port); +void tb_port_lane_bonding_disable(struct tb_port *port); int tb_switch_find_vse_cap(struct tb_switch *sw, enum tb_switch_vse_cap vsec); int tb_switch_find_cap(struct tb_switch *sw, enum tb_switch_cap cap); @@ -970,6 +987,7 @@ int usb4_switch_nvm_read(struct tb_switch *sw, unsigned int address, void *buf, int usb4_switch_nvm_write(struct tb_switch *sw, unsigned int address, const void *buf, size_t size); int usb4_switch_nvm_authenticate(struct tb_switch *sw); +int usb4_switch_nvm_authenticate_status(struct tb_switch *sw, u32 *status); bool usb4_switch_query_dp_resource(struct tb_switch *sw, struct tb_port *in); int usb4_switch_alloc_dp_resource(struct tb_switch *sw, struct tb_port *in); int usb4_switch_dealloc_dp_resource(struct tb_switch *sw, struct tb_port *in); @@ -1025,11 +1043,15 @@ void tb_debugfs_init(void); void tb_debugfs_exit(void); void tb_switch_debugfs_init(struct tb_switch *sw); void tb_switch_debugfs_remove(struct tb_switch *sw); +void tb_service_debugfs_init(struct tb_service *svc); +void tb_service_debugfs_remove(struct tb_service *svc); #else static inline void tb_debugfs_init(void) { } static inline void tb_debugfs_exit(void) { } static inline void tb_switch_debugfs_init(struct tb_switch *sw) { } static inline void tb_switch_debugfs_remove(struct tb_switch *sw) { } +static inline void tb_service_debugfs_init(struct tb_service *svc) { } +static inline void tb_service_debugfs_remove(struct tb_service *svc) { } #endif #ifdef CONFIG_USB4_KUNIT_TEST diff --git a/drivers/thunderbolt/tb_msgs.h b/drivers/thunderbolt/tb_msgs.h index 0e01dbc63e72..bcabfcb2fd03 100644 --- a/drivers/thunderbolt/tb_msgs.h +++ b/drivers/thunderbolt/tb_msgs.h @@ -106,6 +106,7 @@ enum icm_pkg_code { ICM_APPROVE_XDOMAIN = 0x10, ICM_DISCONNECT_XDOMAIN = 0x11, ICM_PREBOOT_ACL = 0x18, + ICM_USB4_SWITCH_OP = 0x20, }; enum icm_event_code { @@ -343,6 +344,8 @@ struct icm_tr_pkg_driver_ready_response { #define ICM_TR_FLAGS_RTD3 BIT(6) #define ICM_TR_INFO_SLEVEL_MASK GENMASK(2, 0) +#define ICM_TR_INFO_PROTO_VERSION_MASK GENMASK(6, 4) +#define ICM_TR_INFO_PROTO_VERSION_SHIFT 4 #define ICM_TR_INFO_BOOT_ACL_SHIFT 7 #define ICM_TR_INFO_BOOT_ACL_MASK GENMASK(12, 7) @@ -478,6 +481,31 @@ struct icm_icl_event_rtd3_veto { u32 veto_reason; }; +/* USB4 ICM messages */ + +struct icm_usb4_switch_op { + struct icm_pkg_header hdr; + u32 route_hi; + u32 route_lo; + u32 metadata; + u16 opcode; + u16 data_len_valid; + u32 data[16]; +}; + +#define ICM_USB4_SWITCH_DATA_LEN_MASK GENMASK(3, 0) +#define ICM_USB4_SWITCH_DATA_VALID BIT(4) + +struct icm_usb4_switch_op_response { + struct icm_pkg_header hdr; + u32 route_hi; + u32 route_lo; + u32 metadata; + u16 opcode; + u16 status; + u32 data[16]; +}; + /* XDomain messages */ struct tb_xdomain_header { diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h index e7d9529822fa..ae427a953489 100644 --- a/drivers/thunderbolt/tb_regs.h +++ b/drivers/thunderbolt/tb_regs.h @@ -211,11 +211,25 @@ struct tb_regs_switch_header { #define ROUTER_CS_9 0x09 #define ROUTER_CS_25 0x19 #define ROUTER_CS_26 0x1a +#define ROUTER_CS_26_OPCODE_MASK GENMASK(15, 0) #define ROUTER_CS_26_STATUS_MASK GENMASK(29, 24) #define ROUTER_CS_26_STATUS_SHIFT 24 #define ROUTER_CS_26_ONS BIT(30) #define ROUTER_CS_26_OV BIT(31) +/* USB4 router operations opcodes */ +enum usb4_switch_op { + USB4_SWITCH_OP_QUERY_DP_RESOURCE = 0x10, + USB4_SWITCH_OP_ALLOC_DP_RESOURCE = 0x11, + USB4_SWITCH_OP_DEALLOC_DP_RESOURCE = 0x12, + USB4_SWITCH_OP_NVM_WRITE = 0x20, + USB4_SWITCH_OP_NVM_AUTH = 0x21, + USB4_SWITCH_OP_NVM_READ = 0x22, + USB4_SWITCH_OP_NVM_SET_OFFSET = 0x23, + USB4_SWITCH_OP_DROM_READ = 0x24, + USB4_SWITCH_OP_NVM_SECTOR_SIZE = 0x25, +}; + /* Router TMU configuration */ #define TMU_RTR_CS_0 0x00 #define TMU_RTR_CS_0_TD BIT(27) diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c index 829b6ccdd5d4..dcdf9c7a9cae 100644 --- a/drivers/thunderbolt/tunnel.c +++ b/drivers/thunderbolt/tunnel.c @@ -34,9 +34,6 @@ #define TB_DP_AUX_PATH_OUT 1 #define TB_DP_AUX_PATH_IN 2 -#define TB_DMA_PATH_OUT 0 -#define TB_DMA_PATH_IN 1 - static const char * const tb_tunnel_names[] = { "PCI", "DP", "DMA", "USB3" }; #define __TB_TUNNEL_PRINT(level, tunnel, fmt, arg...) \ @@ -829,10 +826,10 @@ static void tb_dma_init_path(struct tb_path *path, unsigned int isb, * @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 + * 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 + * other domain. Set to %0 if RX path is not needed. * @reveive_path: HopID used for receiving packets * * Return: Returns a tb_tunnel on success or NULL on failure. @@ -843,10 +840,19 @@ struct tb_tunnel *tb_tunnel_alloc_dma(struct tb *tb, struct tb_port *nhi, int receive_path) { struct tb_tunnel *tunnel; + size_t npaths = 0, i = 0; struct tb_path *path; u32 credits; - tunnel = tb_tunnel_alloc(tb, 2, TB_TUNNEL_DMA); + if (receive_ring) + npaths++; + if (transmit_ring) + npaths++; + + if (WARN_ON(!npaths)) + return NULL; + + tunnel = tb_tunnel_alloc(tb, npaths, TB_TUNNEL_DMA); if (!tunnel) return NULL; @@ -856,22 +862,28 @@ struct tb_tunnel *tb_tunnel_alloc_dma(struct tb *tb, struct tb_port *nhi, credits = tb_dma_credits(nhi); - path = tb_path_alloc(tb, dst, receive_path, nhi, receive_ring, 0, "DMA RX"); - if (!path) { - tb_tunnel_free(tunnel); - return NULL; + if (receive_ring) { + 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); + tunnel->paths[i++] = path; } - tb_dma_init_path(path, TB_PATH_NONE, TB_PATH_SOURCE | TB_PATH_INTERNAL, - credits); - tunnel->paths[TB_DMA_PATH_IN] = path; - path = tb_path_alloc(tb, nhi, transmit_ring, dst, transmit_path, 0, "DMA TX"); - if (!path) { - tb_tunnel_free(tunnel); - return NULL; + if (transmit_ring) { + 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); + tunnel->paths[i++] = path; } - tb_dma_init_path(path, TB_PATH_SOURCE, TB_PATH_ALL, credits); - tunnel->paths[TB_DMA_PATH_OUT] = path; return tunnel; } diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c index f2583b4053e4..67a2867382ed 100644 --- a/drivers/thunderbolt/usb4.c +++ b/drivers/thunderbolt/usb4.c @@ -16,18 +16,6 @@ #define USB4_DATA_DWORDS 16 #define USB4_DATA_RETRIES 3 -enum usb4_switch_op { - USB4_SWITCH_OP_QUERY_DP_RESOURCE = 0x10, - USB4_SWITCH_OP_ALLOC_DP_RESOURCE = 0x11, - USB4_SWITCH_OP_DEALLOC_DP_RESOURCE = 0x12, - USB4_SWITCH_OP_NVM_WRITE = 0x20, - USB4_SWITCH_OP_NVM_AUTH = 0x21, - USB4_SWITCH_OP_NVM_READ = 0x22, - USB4_SWITCH_OP_NVM_SET_OFFSET = 0x23, - USB4_SWITCH_OP_DROM_READ = 0x24, - USB4_SWITCH_OP_NVM_SECTOR_SIZE = 0x25, -}; - enum usb4_sb_target { USB4_SB_TARGET_ROUTER, USB4_SB_TARGET_PARTNER, @@ -74,34 +62,6 @@ static int usb4_switch_wait_for_bit(struct tb_switch *sw, u32 offset, u32 bit, return -ETIMEDOUT; } -static int usb4_switch_op_read_data(struct tb_switch *sw, void *data, - size_t dwords) -{ - if (dwords > USB4_DATA_DWORDS) - return -EINVAL; - - return tb_sw_read(sw, data, TB_CFG_SWITCH, ROUTER_CS_9, dwords); -} - -static int usb4_switch_op_write_data(struct tb_switch *sw, const void *data, - size_t dwords) -{ - if (dwords > USB4_DATA_DWORDS) - return -EINVAL; - - return tb_sw_write(sw, data, TB_CFG_SWITCH, ROUTER_CS_9, dwords); -} - -static int usb4_switch_op_read_metadata(struct tb_switch *sw, u32 *metadata) -{ - return tb_sw_read(sw, metadata, TB_CFG_SWITCH, ROUTER_CS_25, 1); -} - -static int usb4_switch_op_write_metadata(struct tb_switch *sw, u32 metadata) -{ - return tb_sw_write(sw, &metadata, TB_CFG_SWITCH, ROUTER_CS_25, 1); -} - static int usb4_do_read_data(u16 address, void *buf, size_t size, read_block_fn read_block, void *read_block_data) { @@ -171,11 +131,26 @@ static int usb4_do_write_data(unsigned int address, const void *buf, size_t size return 0; } -static int usb4_switch_op(struct tb_switch *sw, u16 opcode, u8 *status) +static int usb4_native_switch_op(struct tb_switch *sw, u16 opcode, + u32 *metadata, u8 *status, + const void *tx_data, size_t tx_dwords, + void *rx_data, size_t rx_dwords) { u32 val; int ret; + if (metadata) { + ret = tb_sw_write(sw, metadata, TB_CFG_SWITCH, ROUTER_CS_25, 1); + if (ret) + return ret; + } + if (tx_dwords) { + ret = tb_sw_write(sw, tx_data, TB_CFG_SWITCH, ROUTER_CS_9, + tx_dwords); + if (ret) + return ret; + } + val = opcode | ROUTER_CS_26_OV; ret = tb_sw_write(sw, &val, TB_CFG_SWITCH, ROUTER_CS_26, 1); if (ret) @@ -192,10 +167,73 @@ static int usb4_switch_op(struct tb_switch *sw, u16 opcode, u8 *status) if (val & ROUTER_CS_26_ONS) return -EOPNOTSUPP; - *status = (val & ROUTER_CS_26_STATUS_MASK) >> ROUTER_CS_26_STATUS_SHIFT; + if (status) + *status = (val & ROUTER_CS_26_STATUS_MASK) >> + ROUTER_CS_26_STATUS_SHIFT; + + if (metadata) { + ret = tb_sw_read(sw, metadata, TB_CFG_SWITCH, ROUTER_CS_25, 1); + if (ret) + return ret; + } + if (rx_dwords) { + ret = tb_sw_read(sw, rx_data, TB_CFG_SWITCH, ROUTER_CS_9, + rx_dwords); + if (ret) + return ret; + } + return 0; } +static int __usb4_switch_op(struct tb_switch *sw, u16 opcode, u32 *metadata, + u8 *status, const void *tx_data, size_t tx_dwords, + void *rx_data, size_t rx_dwords) +{ + const struct tb_cm_ops *cm_ops = sw->tb->cm_ops; + + if (tx_dwords > USB4_DATA_DWORDS || rx_dwords > USB4_DATA_DWORDS) + return -EINVAL; + + /* + * If the connection manager implementation provides USB4 router + * operation proxy callback, call it here instead of running the + * operation natively. + */ + if (cm_ops->usb4_switch_op) { + int ret; + + ret = cm_ops->usb4_switch_op(sw, opcode, metadata, status, + tx_data, tx_dwords, rx_data, + rx_dwords); + if (ret != -EOPNOTSUPP) + return ret; + + /* + * If the proxy was not supported then run the native + * router operation instead. + */ + } + + return usb4_native_switch_op(sw, opcode, metadata, status, tx_data, + tx_dwords, rx_data, rx_dwords); +} + +static inline int usb4_switch_op(struct tb_switch *sw, u16 opcode, + u32 *metadata, u8 *status) +{ + return __usb4_switch_op(sw, opcode, metadata, status, NULL, 0, NULL, 0); +} + +static inline int usb4_switch_op_data(struct tb_switch *sw, u16 opcode, + u32 *metadata, u8 *status, + const void *tx_data, size_t tx_dwords, + void *rx_data, size_t rx_dwords) +{ + return __usb4_switch_op(sw, opcode, metadata, status, tx_data, + tx_dwords, rx_data, rx_dwords); +} + static void usb4_switch_check_wakes(struct tb_switch *sw) { struct tb_port *port; @@ -348,18 +386,12 @@ static int usb4_switch_drom_read_block(void *data, metadata |= (dwaddress << USB4_DROM_ADDRESS_SHIFT) & USB4_DROM_ADDRESS_MASK; - ret = usb4_switch_op_write_metadata(sw, metadata); + ret = usb4_switch_op_data(sw, USB4_SWITCH_OP_DROM_READ, &metadata, + &status, NULL, 0, buf, dwords); if (ret) return ret; - ret = usb4_switch_op(sw, USB4_SWITCH_OP_DROM_READ, &status); - if (ret) - return ret; - - if (status) - return -EIO; - - return usb4_switch_op_read_data(sw, buf, dwords); + return status ? -EIO : 0; } /** @@ -512,17 +544,14 @@ int usb4_switch_nvm_sector_size(struct tb_switch *sw) u8 status; int ret; - ret = usb4_switch_op(sw, USB4_SWITCH_OP_NVM_SECTOR_SIZE, &status); + ret = usb4_switch_op(sw, USB4_SWITCH_OP_NVM_SECTOR_SIZE, &metadata, + &status); if (ret) return ret; if (status) return status == 0x2 ? -EOPNOTSUPP : -EIO; - ret = usb4_switch_op_read_metadata(sw, &metadata); - if (ret) - return ret; - return metadata & USB4_NVM_SECTOR_SIZE_MASK; } @@ -539,18 +568,12 @@ static int usb4_switch_nvm_read_block(void *data, metadata |= (dwaddress << USB4_NVM_READ_OFFSET_SHIFT) & USB4_NVM_READ_OFFSET_MASK; - ret = usb4_switch_op_write_metadata(sw, metadata); - if (ret) - return ret; - - ret = usb4_switch_op(sw, USB4_SWITCH_OP_NVM_READ, &status); + ret = usb4_switch_op_data(sw, USB4_SWITCH_OP_NVM_READ, &metadata, + &status, NULL, 0, buf, dwords); if (ret) return ret; - if (status) - return -EIO; - - return usb4_switch_op_read_data(sw, buf, dwords); + return status ? -EIO : 0; } /** @@ -581,11 +604,8 @@ static int usb4_switch_nvm_set_offset(struct tb_switch *sw, metadata = (dwaddress << USB4_NVM_SET_OFFSET_SHIFT) & USB4_NVM_SET_OFFSET_MASK; - ret = usb4_switch_op_write_metadata(sw, metadata); - if (ret) - return ret; - - ret = usb4_switch_op(sw, USB4_SWITCH_OP_NVM_SET_OFFSET, &status); + ret = usb4_switch_op(sw, USB4_SWITCH_OP_NVM_SET_OFFSET, &metadata, + &status); if (ret) return ret; @@ -599,11 +619,8 @@ static int usb4_switch_nvm_write_next_block(void *data, const void *buf, u8 status; int ret; - ret = usb4_switch_op_write_data(sw, buf, dwords); - if (ret) - return ret; - - ret = usb4_switch_op(sw, USB4_SWITCH_OP_NVM_WRITE, &status); + ret = usb4_switch_op_data(sw, USB4_SWITCH_OP_NVM_WRITE, NULL, &status, + buf, dwords, NULL, 0); if (ret) return ret; @@ -638,32 +655,78 @@ int usb4_switch_nvm_write(struct tb_switch *sw, unsigned int address, * @sw: USB4 router * * After the new NVM has been written via usb4_switch_nvm_write(), this - * function triggers NVM authentication process. If the authentication - * is successful the router is power cycled and the new NVM starts + * function triggers NVM authentication process. The router gets power + * cycled and if the authentication is successful the new NVM starts * running. In case of failure returns negative errno. + * + * The caller should call usb4_switch_nvm_authenticate_status() to read + * the status of the authentication after power cycle. It should be the + * first router operation to avoid the status being lost. */ int usb4_switch_nvm_authenticate(struct tb_switch *sw) { - u8 status = 0; int ret; - ret = usb4_switch_op(sw, USB4_SWITCH_OP_NVM_AUTH, &status); + ret = usb4_switch_op(sw, USB4_SWITCH_OP_NVM_AUTH, NULL, NULL); + switch (ret) { + /* + * The router is power cycled once NVM_AUTH is started so it is + * expected to get any of the following errors back. + */ + case -EACCES: + case -ENOTCONN: + case -ETIMEDOUT: + return 0; + + default: + return ret; + } +} + +/** + * usb4_switch_nvm_authenticate_status() - Read status of last NVM authenticate + * @sw: USB4 router + * @status: Status code of the operation + * + * The function checks if there is status available from the last NVM + * authenticate router operation. If there is status then %0 is returned + * and the status code is placed in @status. Returns negative errno in case + * of failure. + * + * Must be called before any other router operation. + */ +int usb4_switch_nvm_authenticate_status(struct tb_switch *sw, u32 *status) +{ + const struct tb_cm_ops *cm_ops = sw->tb->cm_ops; + u16 opcode; + u32 val; + int ret; + + if (cm_ops->usb4_switch_nvm_authenticate_status) { + ret = cm_ops->usb4_switch_nvm_authenticate_status(sw, status); + if (ret != -EOPNOTSUPP) + return ret; + } + + ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, ROUTER_CS_26, 1); if (ret) return ret; - switch (status) { - case 0x0: - tb_sw_dbg(sw, "NVM authentication successful\n"); - return 0; - case 0x1: - return -EINVAL; - case 0x2: - return -EAGAIN; - case 0x3: - return -EOPNOTSUPP; - default: - return -EIO; + /* Check that the opcode is correct */ + opcode = val & ROUTER_CS_26_OPCODE_MASK; + if (opcode == USB4_SWITCH_OP_NVM_AUTH) { + if (val & ROUTER_CS_26_OV) + return -EBUSY; + if (val & ROUTER_CS_26_ONS) + return -EOPNOTSUPP; + + *status = (val & ROUTER_CS_26_STATUS_MASK) >> + ROUTER_CS_26_STATUS_SHIFT; + } else { + *status = 0; } + + return 0; } /** @@ -677,14 +740,12 @@ int usb4_switch_nvm_authenticate(struct tb_switch *sw) */ bool usb4_switch_query_dp_resource(struct tb_switch *sw, struct tb_port *in) { + u32 metadata = in->port; u8 status; int ret; - ret = usb4_switch_op_write_metadata(sw, in->port); - if (ret) - return false; - - ret = usb4_switch_op(sw, USB4_SWITCH_OP_QUERY_DP_RESOURCE, &status); + ret = usb4_switch_op(sw, USB4_SWITCH_OP_QUERY_DP_RESOURCE, &metadata, + &status); /* * If DP resource allocation is not supported assume it is * always available. @@ -709,14 +770,12 @@ bool usb4_switch_query_dp_resource(struct tb_switch *sw, struct tb_port *in) */ int usb4_switch_alloc_dp_resource(struct tb_switch *sw, struct tb_port *in) { + u32 metadata = in->port; u8 status; int ret; - ret = usb4_switch_op_write_metadata(sw, in->port); - if (ret) - return ret; - - ret = usb4_switch_op(sw, USB4_SWITCH_OP_ALLOC_DP_RESOURCE, &status); + ret = usb4_switch_op(sw, USB4_SWITCH_OP_ALLOC_DP_RESOURCE, &metadata, + &status); if (ret == -EOPNOTSUPP) return 0; else if (ret) @@ -734,14 +793,12 @@ int usb4_switch_alloc_dp_resource(struct tb_switch *sw, struct tb_port *in) */ int usb4_switch_dealloc_dp_resource(struct tb_switch *sw, struct tb_port *in) { + u32 metadata = in->port; u8 status; int ret; - ret = usb4_switch_op_write_metadata(sw, in->port); - if (ret) - return ret; - - ret = usb4_switch_op(sw, USB4_SWITCH_OP_DEALLOC_DP_RESOURCE, &status); + ret = usb4_switch_op(sw, USB4_SWITCH_OP_DEALLOC_DP_RESOURCE, &metadata, + &status); if (ret == -EOPNOTSUPP) return 0; else if (ret) diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c index c00ad817042e..9b3a299a1202 100644 --- a/drivers/thunderbolt/xdomain.c +++ b/drivers/thunderbolt/xdomain.c @@ -8,6 +8,7 @@ */ #include <linux/device.h> +#include <linux/delay.h> #include <linux/kmod.h> #include <linux/module.h> #include <linux/pm_runtime.h> @@ -21,6 +22,7 @@ #define XDOMAIN_UUID_RETRIES 10 #define XDOMAIN_PROPERTIES_RETRIES 60 #define XDOMAIN_PROPERTIES_CHANGED_RETRIES 10 +#define XDOMAIN_BONDING_WAIT 100 /* ms */ struct xdomain_request_work { struct work_struct work; @@ -587,8 +589,6 @@ static void tb_xdp_handle_request(struct work_struct *work) break; case PROPERTIES_CHANGED_REQUEST: { - const struct tb_xdp_properties_changed *xchg = - (const struct tb_xdp_properties_changed *)pkg; struct tb_xdomain *xd; ret = tb_xdp_properties_changed_response(ctl, route, sequence); @@ -598,10 +598,12 @@ 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_uuid_locked(tb, &xchg->src_uuid); + xd = tb_xdomain_find_by_route_locked(tb, route); if (xd) { - queue_delayed_work(tb->wq, &xd->get_properties_work, - msecs_to_jiffies(50)); + if (device_is_registered(&xd->dev)) { + queue_delayed_work(tb->wq, &xd->get_properties_work, + msecs_to_jiffies(50)); + } tb_xdomain_put(xd); } @@ -777,6 +779,7 @@ static void tb_service_release(struct device *dev) struct tb_service *svc = container_of(dev, struct tb_service, dev); struct tb_xdomain *xd = tb_service_parent(svc); + tb_service_debugfs_remove(svc); ida_simple_remove(&xd->service_ids, svc->id); kfree(svc->key); kfree(svc); @@ -891,6 +894,8 @@ static void enumerate_services(struct tb_xdomain *xd) svc->dev.parent = &xd->dev; dev_set_name(&svc->dev, "%s.%d", dev_name(&xd->dev), svc->id); + tb_service_debugfs_init(svc); + if (device_register(&svc->dev)) { put_device(&svc->dev); break; @@ -943,6 +948,43 @@ static void tb_xdomain_restore_paths(struct tb_xdomain *xd) } } +static inline struct tb_switch *tb_xdomain_parent(struct tb_xdomain *xd) +{ + return tb_to_switch(xd->dev.parent); +} + +static int tb_xdomain_update_link_attributes(struct tb_xdomain *xd) +{ + bool change = false; + struct tb_port *port; + int ret; + + port = tb_port_at(xd->route, tb_xdomain_parent(xd)); + + ret = tb_port_get_link_speed(port); + if (ret < 0) + return ret; + + if (xd->link_speed != ret) + change = true; + + xd->link_speed = ret; + + ret = tb_port_get_link_width(port); + if (ret < 0) + return ret; + + if (xd->link_width != ret) + change = true; + + xd->link_width = ret; + + if (change) + kobject_uevent(&xd->dev.kobj, KOBJ_CHANGE); + + return 0; +} + static void tb_xdomain_get_uuid(struct work_struct *work) { struct tb_xdomain *xd = container_of(work, typeof(*xd), @@ -962,10 +1004,8 @@ static void tb_xdomain_get_uuid(struct work_struct *work) return; } - if (uuid_equal(&uuid, xd->local_uuid)) { + if (uuid_equal(&uuid, xd->local_uuid)) dev_dbg(&xd->dev, "intra-domain loop detected\n"); - return; - } /* * If the UUID is different, there is another domain connected @@ -1056,6 +1096,8 @@ static void tb_xdomain_get_properties(struct work_struct *work) xd->properties = dir; xd->property_block_gen = gen; + tb_xdomain_update_link_attributes(xd); + tb_xdomain_restore_paths(xd); mutex_unlock(&xd->lock); @@ -1162,9 +1204,35 @@ static ssize_t unique_id_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RO(unique_id); +static ssize_t speed_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev); + + return sprintf(buf, "%u.0 Gb/s\n", xd->link_speed); +} + +static DEVICE_ATTR(rx_speed, 0444, speed_show, NULL); +static DEVICE_ATTR(tx_speed, 0444, speed_show, NULL); + +static ssize_t lanes_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev); + + return sprintf(buf, "%u\n", xd->link_width); +} + +static DEVICE_ATTR(rx_lanes, 0444, lanes_show, NULL); +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_rx_lanes.attr, + &dev_attr_rx_speed.attr, + &dev_attr_tx_lanes.attr, + &dev_attr_tx_speed.attr, &dev_attr_unique_id.attr, &dev_attr_vendor.attr, &dev_attr_vendor_name.attr, @@ -1382,6 +1450,70 @@ void tb_xdomain_remove(struct tb_xdomain *xd) } /** + * tb_xdomain_lane_bonding_enable() - Enable lane bonding on XDomain + * @xd: XDomain connection + * + * Lane bonding is disabled by default for XDomains. This function tries + * to enable bonding by first enabling the port and waiting for the CL0 + * state. + * + * Return: %0 in case of success and negative errno in case of error. + */ +int tb_xdomain_lane_bonding_enable(struct tb_xdomain *xd) +{ + struct tb_port *port; + int ret; + + port = tb_port_at(xd->route, tb_xdomain_parent(xd)); + if (!port->dual_link_port) + return -ENODEV; + + ret = tb_port_enable(port->dual_link_port); + if (ret) + return ret; + + ret = tb_wait_for_port(port->dual_link_port, true); + if (ret < 0) + return ret; + if (!ret) + return -ENOTCONN; + + ret = tb_port_lane_bonding_enable(port); + if (ret) { + tb_port_warn(port, "failed to enable lane bonding\n"); + return ret; + } + + tb_xdomain_update_link_attributes(xd); + + dev_dbg(&xd->dev, "lane bonding enabled\n"); + return 0; +} +EXPORT_SYMBOL_GPL(tb_xdomain_lane_bonding_enable); + +/** + * tb_xdomain_lane_bonding_disable() - Disable lane bonding + * @xd: XDomain connection + * + * Lane bonding is disabled by default for XDomains. If bonding has been + * enabled, this function can be used to disable it. + */ +void tb_xdomain_lane_bonding_disable(struct tb_xdomain *xd) +{ + struct tb_port *port; + + port = tb_port_at(xd->route, tb_xdomain_parent(xd)); + if (port->dual_link_port) { + tb_port_lane_bonding_disable(port); + tb_port_disable(port->dual_link_port); + tb_xdomain_update_link_attributes(xd); + + dev_dbg(&xd->dev, "lane bonding disabled\n"); + } +} +EXPORT_SYMBOL_GPL(tb_xdomain_lane_bonding_disable); + +/** * 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 diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 1c1c1d659394..ba5706ccc188 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -30,7 +30,6 @@ obj-$(CONFIG_USB_ISP1362_HCD) += host/ obj-$(CONFIG_USB_U132_HCD) += host/ obj-$(CONFIG_USB_R8A66597_HCD) += host/ obj-$(CONFIG_USB_HWA_HCD) += host/ -obj-$(CONFIG_USB_IMX21_HCD) += host/ obj-$(CONFIG_USB_FSL_USB2) += host/ obj-$(CONFIG_USB_FOTG210_HCD) += host/ obj-$(CONFIG_USB_MAX3421_HCD) += host/ diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c index e62a770a5d3b..4d3947476f0e 100644 --- a/drivers/usb/atm/cxacru.c +++ b/drivers/usb/atm/cxacru.c @@ -810,9 +810,6 @@ static int cxacru_atm_start(struct usbatm_data *usbatm_instance, mutex_unlock(&instance->poll_state_serialize); mutex_unlock(&instance->adsl_state_serialize); - printk(KERN_INFO "%s%d: %s %pM\n", atm_dev->type, atm_dev->number, - usbatm_instance->description, atm_dev->esi); - if (start_polling) cxacru_poll_status(&instance->poll_work.work); return 0; @@ -852,15 +849,15 @@ static void cxacru_poll_status(struct work_struct *work) switch (instance->adsl_status) { case 0: - atm_printk(KERN_INFO, usbatm, "ADSL state: running\n"); + atm_info(usbatm, "ADSL state: running\n"); break; case 1: - atm_printk(KERN_INFO, usbatm, "ADSL state: stopped\n"); + atm_info(usbatm, "ADSL state: stopped\n"); break; default: - atm_printk(KERN_INFO, usbatm, "Unknown adsl status %02x\n", instance->adsl_status); + atm_info(usbatm, "Unknown adsl status %02x\n", instance->adsl_status); break; } } diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c index 56fe30d247da..33ae03ac13a6 100644 --- a/drivers/usb/atm/usbatm.c +++ b/drivers/usb/atm/usbatm.c @@ -249,7 +249,7 @@ static void usbatm_complete(struct urb *urb) /* vdbg("%s: urb 0x%p, status %d, actual_length %d", __func__, urb, status, urb->actual_length); */ - /* usually in_interrupt(), but not always */ + /* Can be invoked from task context, protect against interrupts */ spin_lock_irqsave(&channel->lock, flags); /* must add to the back when receiving; doesn't matter when sending */ @@ -1278,7 +1278,7 @@ EXPORT_SYMBOL_GPL(usbatm_usb_disconnect); static int __init usbatm_usb_init(void) { if (sizeof(struct usbatm_control) > sizeof_field(struct sk_buff, cb)) { - printk(KERN_ERR "%s unusable with this kernel!\n", usbatm_driver_name); + pr_err("%s unusable with this kernel!\n", usbatm_driver_name); return -EIO; } diff --git a/drivers/usb/atm/xusbatm.c b/drivers/usb/atm/xusbatm.c index ffc9810070a3..0befbf63d1cc 100644 --- a/drivers/usb/atm/xusbatm.c +++ b/drivers/usb/atm/xusbatm.c @@ -179,7 +179,7 @@ static int __init xusbatm_init(void) num_vendor != num_product || num_vendor != num_rx_endpoint || num_vendor != num_tx_endpoint) { - printk(KERN_WARNING "xusbatm: malformed module parameters\n"); + pr_warn("xusbatm: malformed module parameters\n"); return -EINVAL; } diff --git a/drivers/usb/cdns3/cdns3-imx.c b/drivers/usb/cdns3/cdns3-imx.c index 54a2d70a9c73..22a56c4dce67 100644 --- a/drivers/usb/cdns3/cdns3-imx.c +++ b/drivers/usb/cdns3/cdns3-imx.c @@ -151,6 +151,7 @@ static int cdns_imx_platform_suspend(struct device *dev, bool suspend, bool wakeup); static struct cdns3_platform_data cdns_imx_pdata = { .platform_suspend = cdns_imx_platform_suspend, + .quirks = CDNS3_DEFAULT_PM_RUNTIME_ALLOW, }; static const struct of_dev_auxdata cdns_imx_auxdata[] = { @@ -206,7 +207,6 @@ static int cdns_imx_probe(struct platform_device *pdev) device_set_wakeup_capable(dev, true); pm_runtime_set_active(dev); pm_runtime_enable(dev); - pm_runtime_forbid(dev); return ret; err: diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c index 039ab5d2435e..1991cb5cf6bf 100644 --- a/drivers/usb/cdns3/core.c +++ b/drivers/usb/cdns3/core.c @@ -465,11 +465,8 @@ static int cdns3_probe(struct platform_device *pdev) cdns->xhci_res[1] = *res; cdns->dev_irq = platform_get_irq_byname(pdev, "peripheral"); - if (cdns->dev_irq == -EPROBE_DEFER) - return cdns->dev_irq; - if (cdns->dev_irq < 0) - dev_err(dev, "couldn't get peripheral irq\n"); + return cdns->dev_irq; regs = devm_platform_ioremap_resource_byname(pdev, "dev"); if (IS_ERR(regs)) @@ -477,14 +474,9 @@ static int cdns3_probe(struct platform_device *pdev) cdns->dev_regs = regs; cdns->otg_irq = platform_get_irq_byname(pdev, "otg"); - if (cdns->otg_irq == -EPROBE_DEFER) + if (cdns->otg_irq < 0) return cdns->otg_irq; - if (cdns->otg_irq < 0) { - dev_err(dev, "couldn't get otg irq\n"); - return cdns->otg_irq; - } - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "otg"); if (!res) { dev_err(dev, "couldn't get otg resource\n"); @@ -569,7 +561,8 @@ static int cdns3_probe(struct platform_device *pdev) device_set_wakeup_capable(dev, true); pm_runtime_set_active(dev); pm_runtime_enable(dev); - pm_runtime_forbid(dev); + if (!(cdns->pdata && (cdns->pdata->quirks & CDNS3_DEFAULT_PM_RUNTIME_ALLOW))) + pm_runtime_forbid(dev); /* * The controller needs less time between bus and controller suspend, diff --git a/drivers/usb/cdns3/core.h b/drivers/usb/cdns3/core.h index 8a40d53d5ede..3176f924293a 100644 --- a/drivers/usb/cdns3/core.h +++ b/drivers/usb/cdns3/core.h @@ -42,6 +42,8 @@ struct cdns3_role_driver { struct cdns3_platform_data { int (*platform_suspend)(struct device *dev, bool suspend, bool wakeup); + unsigned long quirks; +#define CDNS3_DEFAULT_PM_RUNTIME_ALLOW BIT(0) }; /** @@ -73,6 +75,7 @@ struct cdns3_platform_data { * @wakeup_pending: wakeup interrupt pending * @pdata: platform data from glue layer * @lock: spinlock structure + * @xhci_plat_data: xhci private data structure pointer */ struct cdns3 { struct device *dev; @@ -106,6 +109,7 @@ struct cdns3 { bool wakeup_pending; struct cdns3_platform_data *pdata; spinlock_t lock; + struct xhci_plat_priv *xhci_plat_data; }; int cdns3_hw_role_switch(struct cdns3 *cdns); diff --git a/drivers/usb/cdns3/gadget-export.h b/drivers/usb/cdns3/gadget-export.h index 577469eee961..702c5a267a92 100644 --- a/drivers/usb/cdns3/gadget-export.h +++ b/drivers/usb/cdns3/gadget-export.h @@ -13,7 +13,6 @@ #ifdef CONFIG_USB_CDNS3_GADGET int cdns3_gadget_init(struct cdns3 *cdns); -void cdns3_gadget_exit(struct cdns3 *cdns); #else static inline int cdns3_gadget_init(struct cdns3 *cdns) @@ -21,8 +20,6 @@ static inline int cdns3_gadget_init(struct cdns3 *cdns) return -ENXIO; } -static inline void cdns3_gadget_exit(struct cdns3 *cdns) { } - #endif #endif /* __LINUX_CDNS3_GADGET_EXPORT */ diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c index 0aa85cc07ff1..08a4e693c470 100644 --- a/drivers/usb/cdns3/gadget.c +++ b/drivers/usb/cdns3/gadget.c @@ -3084,7 +3084,7 @@ static void cdns3_gadget_release(struct device *dev) kfree(priv_dev); } -void cdns3_gadget_exit(struct cdns3 *cdns) +static void cdns3_gadget_exit(struct cdns3 *cdns) { struct cdns3_device *priv_dev; diff --git a/drivers/usb/cdns3/host-export.h b/drivers/usb/cdns3/host-export.h index ae11810f8826..26041718a086 100644 --- a/drivers/usb/cdns3/host-export.h +++ b/drivers/usb/cdns3/host-export.h @@ -9,9 +9,11 @@ #ifndef __LINUX_CDNS3_HOST_EXPORT #define __LINUX_CDNS3_HOST_EXPORT +struct usb_hcd; #ifdef CONFIG_USB_CDNS3_HOST int cdns3_host_init(struct cdns3 *cdns); +int xhci_cdns3_suspend_quirk(struct usb_hcd *hcd); #else @@ -21,6 +23,10 @@ static inline int cdns3_host_init(struct cdns3 *cdns) } static inline void cdns3_host_exit(struct cdns3 *cdns) { } +static inline int xhci_cdns3_suspend_quirk(struct usb_hcd *hcd) +{ + return 0; +} #endif /* CONFIG_USB_CDNS3_HOST */ diff --git a/drivers/usb/cdns3/host.c b/drivers/usb/cdns3/host.c index b3e2cb69762c..ec89f2e5430f 100644 --- a/drivers/usb/cdns3/host.c +++ b/drivers/usb/cdns3/host.c @@ -14,6 +14,19 @@ #include "drd.h" #include "host-export.h" #include <linux/usb/hcd.h> +#include "../host/xhci.h" +#include "../host/xhci-plat.h" + +#define XECP_PORT_CAP_REG 0x8000 +#define XECP_AUX_CTRL_REG1 0x8120 + +#define CFG_RXDET_P3_EN BIT(15) +#define LPM_2_STB_SWITCH_EN BIT(25) + +static const struct xhci_plat_priv xhci_plat_cdns3_xhci = { + .quirks = XHCI_SKIP_PHY_INIT | XHCI_AVOID_BEI, + .suspend_quirk = xhci_cdns3_suspend_quirk, +}; static int __cdns3_host_init(struct cdns3 *cdns) { @@ -39,10 +52,25 @@ static int __cdns3_host_init(struct cdns3 *cdns) goto err1; } + cdns->xhci_plat_data = kmemdup(&xhci_plat_cdns3_xhci, + sizeof(struct xhci_plat_priv), GFP_KERNEL); + if (!cdns->xhci_plat_data) { + ret = -ENOMEM; + goto err1; + } + + if (cdns->pdata && (cdns->pdata->quirks & CDNS3_DEFAULT_PM_RUNTIME_ALLOW)) + cdns->xhci_plat_data->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW; + + ret = platform_device_add_data(xhci, cdns->xhci_plat_data, + sizeof(struct xhci_plat_priv)); + if (ret) + goto free_memory; + ret = platform_device_add(xhci); if (ret) { dev_err(cdns->dev, "failed to register xHCI device\n"); - goto err1; + goto free_memory; } /* Glue needs to access xHCI region register for Power management */ @@ -51,13 +79,43 @@ static int __cdns3_host_init(struct cdns3 *cdns) cdns->xhci_regs = hcd->regs; return 0; + +free_memory: + kfree(cdns->xhci_plat_data); err1: platform_device_put(xhci); return ret; } +int xhci_cdns3_suspend_quirk(struct usb_hcd *hcd) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + u32 value; + + if (pm_runtime_status_suspended(hcd->self.controller)) + return 0; + + /* set usbcmd.EU3S */ + value = readl(&xhci->op_regs->command); + value |= CMD_PM_INDEX; + writel(value, &xhci->op_regs->command); + + if (hcd->regs) { + value = readl(hcd->regs + XECP_AUX_CTRL_REG1); + value |= CFG_RXDET_P3_EN; + writel(value, hcd->regs + XECP_AUX_CTRL_REG1); + + value = readl(hcd->regs + XECP_PORT_CAP_REG); + value |= LPM_2_STB_SWITCH_EN; + writel(value, hcd->regs + XECP_PORT_CAP_REG); + } + + return 0; +} + static void cdns3_host_exit(struct cdns3 *cdns) { + kfree(cdns->xhci_plat_data); platform_device_unregister(cdns->host_dev); cdns->host_dev = NULL; cdns3_drd_host_off(cdns); diff --git a/drivers/usb/chipidea/Makefile b/drivers/usb/chipidea/Makefile index fae779a23866..6f4a3deced35 100644 --- a/drivers/usb/chipidea/Makefile +++ b/drivers/usb/chipidea/Makefile @@ -1,8 +1,11 @@ # SPDX-License-Identifier: GPL-2.0 + +# define_trace.h needs to know how to find our header +CFLAGS_trace.o := -I$(src) obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc.o ci_hdrc-y := core.o otg.o debug.o ulpi.o -ci_hdrc-$(CONFIG_USB_CHIPIDEA_UDC) += udc.o +ci_hdrc-$(CONFIG_USB_CHIPIDEA_UDC) += udc.o trace.o ci_hdrc-$(CONFIG_USB_CHIPIDEA_HOST) += host.o ci_hdrc-$(CONFIG_USB_OTG_FSM) += otg_fsm.o diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 25c65accf089..9e12152ea46b 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -57,7 +57,8 @@ static const struct ci_hdrc_imx_platform_flag imx6sx_usb_data = { static const struct ci_hdrc_imx_platform_flag imx6ul_usb_data = { .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | - CI_HDRC_TURN_VBUS_EARLY_ON, + CI_HDRC_TURN_VBUS_EARLY_ON | + CI_HDRC_DISABLE_DEVICE_STREAMING, }; static const struct ci_hdrc_imx_platform_flag imx7d_usb_data = { @@ -319,16 +320,11 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) .notify_event = ci_hdrc_imx_notify_event, }; int ret; - const struct of_device_id *of_id; const struct ci_hdrc_imx_platform_flag *imx_platform_flag; struct device_node *np = pdev->dev.of_node; struct device *dev = &pdev->dev; - of_id = of_match_device(ci_hdrc_imx_dt_ids, dev); - if (!of_id) - return -ENODEV; - - imx_platform_flag = of_id->data; + imx_platform_flag = of_device_get_match_data(&pdev->dev); data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (!data) diff --git a/drivers/usb/chipidea/trace.c b/drivers/usb/chipidea/trace.c new file mode 100644 index 000000000000..f6402630a58e --- /dev/null +++ b/drivers/usb/chipidea/trace.c @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Chipidea Device Mode Trace Support + * + * Copyright (C) 2020 NXP + * + * Author: Peter Chen <peter.chen@nxp.com> + */ + +#define CREATE_TRACE_POINTS +#include "trace.h" + +void ci_log(struct ci_hdrc *ci, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + trace_ci_log(ci, &vaf); + va_end(args); +} diff --git a/drivers/usb/chipidea/trace.h b/drivers/usb/chipidea/trace.h new file mode 100644 index 000000000000..1601fd86c4c1 --- /dev/null +++ b/drivers/usb/chipidea/trace.h @@ -0,0 +1,92 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Trace support header file for device mode + * + * Copyright (C) 2020 NXP + * + * Author: Peter Chen <peter.chen@nxp.com> + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM chipidea + +#if !defined(__LINUX_CHIPIDEA_TRACE) || defined(TRACE_HEADER_MULTI_READ) +#define __LINUX_CHIPIDEA_TRACE + +#include <linux/types.h> +#include <linux/tracepoint.h> +#include <linux/usb/chipidea.h> +#include "ci.h" +#include "udc.h" + +#define CHIPIDEA_MSG_MAX 500 + +void ci_log(struct ci_hdrc *ci, const char *fmt, ...); + +TRACE_EVENT(ci_log, + TP_PROTO(struct ci_hdrc *ci, struct va_format *vaf), + TP_ARGS(ci, vaf), + TP_STRUCT__entry( + __string(name, dev_name(ci->dev)) + __dynamic_array(char, msg, CHIPIDEA_MSG_MAX) + ), + TP_fast_assign( + __assign_str(name, dev_name(ci->dev)); + vsnprintf(__get_str(msg), CHIPIDEA_MSG_MAX, vaf->fmt, *vaf->va); + ), + TP_printk("%s: %s", __get_str(name), __get_str(msg)) +); + +DECLARE_EVENT_CLASS(ci_log_trb, + TP_PROTO(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq, struct td_node *td), + TP_ARGS(hwep, hwreq, td), + TP_STRUCT__entry( + __string(name, hwep->name) + __field(struct td_node *, td) + __field(struct usb_request *, req) + __field(dma_addr_t, dma) + __field(s32, td_remaining_size) + __field(u32, next) + __field(u32, token) + __field(u32, type) + ), + TP_fast_assign( + __assign_str(name, hwep->name); + __entry->req = &hwreq->req; + __entry->td = td; + __entry->dma = td->dma; + __entry->td_remaining_size = td->td_remaining_size; + __entry->next = le32_to_cpu(td->ptr->next); + __entry->token = le32_to_cpu(td->ptr->token); + __entry->type = usb_endpoint_type(hwep->ep.desc); + ), + TP_printk("%s: req: %p, td: %p, td_dma_address: %pad, remaining_size: %d, " + "next: %x, total bytes: %d, status: %lx", + __get_str(name), __entry->req, __entry->td, &__entry->dma, + __entry->td_remaining_size, __entry->next, + (int)((__entry->token & TD_TOTAL_BYTES) >> __ffs(TD_TOTAL_BYTES)), + __entry->token & TD_STATUS + ) +); + +DEFINE_EVENT(ci_log_trb, ci_prepare_td, + TP_PROTO(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq, struct td_node *td), + TP_ARGS(hwep, hwreq, td) +); + +DEFINE_EVENT(ci_log_trb, ci_complete_td, + TP_PROTO(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq, struct td_node *td), + TP_ARGS(hwep, hwreq, td) +); + +#endif /* __LINUX_CHIPIDEA_TRACE */ + +/* this part must be outside header guard */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . + +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE trace + +#include <trace/define_trace.h> diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 60ea932afe2b..c16d900cdaee 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -26,6 +26,7 @@ #include "bits.h" #include "otg.h" #include "otg_fsm.h" +#include "trace.h" /* control endpoint description */ static const struct usb_endpoint_descriptor @@ -569,14 +570,18 @@ static int _hardware_enqueue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) if (ret) return ret; - firstnode = list_first_entry(&hwreq->tds, struct td_node, td); - lastnode = list_entry(hwreq->tds.prev, struct td_node, td); lastnode->ptr->next = cpu_to_le32(TD_TERMINATE); if (!hwreq->req.no_interrupt) lastnode->ptr->token |= cpu_to_le32(TD_IOC); + + list_for_each_entry_safe(firstnode, lastnode, &hwreq->tds, td) + trace_ci_prepare_td(hwep, hwreq, firstnode); + + firstnode = list_first_entry(&hwreq->tds, struct td_node, td); + wmb(); hwreq->req.actual = 0; @@ -671,6 +676,7 @@ static int _hardware_dequeue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) list_for_each_entry_safe(node, tmpnode, &hwreq->tds, td) { tmptoken = le32_to_cpu(node->ptr->token); + trace_ci_complete_td(hwep, hwreq, node); if ((TD_STATUS_ACTIVE & tmptoken) != 0) { int n = hw_ep_bit(hwep->num, hwep->dir); diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index 6d8331e7da99..4545b23bda3f 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -1134,11 +1134,6 @@ MODULE_DEVICE_TABLE(of, usbmisc_imx_dt_ids); static int usbmisc_imx_probe(struct platform_device *pdev) { struct imx_usbmisc *data; - const struct of_device_id *of_id; - - of_id = of_match_device(usbmisc_imx_dt_ids, &pdev->dev); - if (!of_id) - return -ENODEV; data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (!data) @@ -1150,7 +1145,7 @@ static int usbmisc_imx_probe(struct platform_device *pdev) if (IS_ERR(data->base)) return PTR_ERR(data->base); - data->ops = (const struct usbmisc_ops *)of_id->data; + data->ops = of_device_get_match_data(&pdev->dev); platform_set_drvdata(pdev, data); return 0; diff --git a/drivers/usb/common/ulpi.c b/drivers/usb/common/ulpi.c index a18d7c4222dd..ce5e6f6711f7 100644 --- a/drivers/usb/common/ulpi.c +++ b/drivers/usb/common/ulpi.c @@ -118,7 +118,7 @@ static struct attribute *ulpi_dev_attrs[] = { NULL }; -static struct attribute_group ulpi_dev_attr_group = { +static const struct attribute_group ulpi_dev_attr_group = { .attrs = ulpi_dev_attrs, }; diff --git a/drivers/usb/core/buffer.c b/drivers/usb/core/buffer.c index 6cf22c27f2d2..fbb087b728dc 100644 --- a/drivers/usb/core/buffer.c +++ b/drivers/usb/core/buffer.c @@ -51,7 +51,8 @@ void __init usb_init_pool_max(void) /** * hcd_buffer_create - initialize buffer pools * @hcd: the bus whose buffer pools are to be initialized - * Context: !in_interrupt() + * + * Context: task context, might sleep * * Call this as part of initializing a host controller that uses the dma * memory allocators. It initializes some pools of dma-coherent memory that @@ -88,7 +89,8 @@ int hcd_buffer_create(struct usb_hcd *hcd) /** * hcd_buffer_destroy - deallocate buffer pools * @hcd: the bus whose buffer pools are to be destroyed - * Context: !in_interrupt() + * + * Context: task context, might sleep * * This frees the buffer pools created by hcd_buffer_create(). */ diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 562a730befda..b199eb65f378 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -1076,6 +1076,7 @@ int usb_get_bos_descriptor(struct usb_device *dev) case USB_PTM_CAP_TYPE: dev->bos->ptm_cap = (struct usb_ptm_cap_descriptor *)buffer; + break; default: break; } diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c index 1c2c04079676..903426b6d305 100644 --- a/drivers/usb/core/endpoint.c +++ b/drivers/usb/core/endpoint.c @@ -153,7 +153,7 @@ static struct attribute *ep_dev_attrs[] = { &dev_attr_direction.attr, NULL, }; -static struct attribute_group ep_dev_attr_grp = { +static const struct attribute_group ep_dev_attr_grp = { .attrs = ep_dev_attrs, }; static const struct attribute_group *ep_dev_groups[] = { diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index ec0d6c50610c..d630cccd2e6e 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -160,7 +160,8 @@ static void ehci_wait_for_companions(struct pci_dev *pdev, struct usb_hcd *hcd, * @dev: USB Host Controller being probed * @id: pci hotplug id connecting controller to HCD framework * @driver: USB HC driver handle - * Context: !in_interrupt() + * + * Context: task context, might sleep * * Allocates basic PCI resources for this USB host controller, and * then invokes the start() method for the HCD associated with it @@ -304,7 +305,8 @@ EXPORT_SYMBOL_GPL(usb_hcd_pci_probe); /** * usb_hcd_pci_remove - shutdown processing for PCI-based HCDs * @dev: USB Host Controller being removed - * Context: !in_interrupt() + * + * Context: task context, might sleep * * Reverses the effect of usb_hcd_pci_probe(), first invoking * the HCD's stop() method. It is always called from a thread diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 2c6b9578a7d3..60886a7464c3 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -747,8 +747,7 @@ error: * driver requests it; otherwise the driver is responsible for * calling usb_hcd_poll_rh_status() when an event occurs. * - * Completions are called in_interrupt(), but they may or may not - * be in_irq(). + * Completion handler may not sleep. See usb_hcd_giveback_urb() for details. */ void usb_hcd_poll_rh_status(struct usb_hcd *hcd) { @@ -904,7 +903,8 @@ static void usb_bus_init (struct usb_bus *bus) /** * usb_register_bus - registers the USB host controller with the usb core * @bus: pointer to the bus to register - * Context: !in_interrupt() + * + * Context: task context, might sleep. * * Assigns a bus number, and links the controller into usbcore data * structures so that it can be seen by scanning the bus list. @@ -939,7 +939,8 @@ error_find_busnum: /** * usb_deregister_bus - deregisters the USB host controller * @bus: pointer to the bus to deregister - * Context: !in_interrupt() + * + * Context: task context, might sleep. * * Recycles the bus number, and unlinks the controller from usbcore data * structures so that it won't be seen by scanning the bus list. @@ -1646,9 +1647,16 @@ static void __usb_hcd_giveback_urb(struct urb *urb) /* pass ownership to the completion handler */ urb->status = status; - kcov_remote_start_usb((u64)urb->dev->bus->busnum); + /* + * This function can be called in task context inside another remote + * coverage collection section, but KCOV doesn't support that kind of + * recursion yet. Only collect coverage in softirq context for now. + */ + if (in_serving_softirq()) + kcov_remote_start_usb((u64)urb->dev->bus->busnum); urb->complete(urb); - kcov_remote_stop(); + if (in_serving_softirq()) + kcov_remote_stop(); usb_anchor_resume_wakeups(anchor); atomic_dec(&urb->use_count); @@ -1691,7 +1699,11 @@ static void usb_giveback_urb_bh(struct tasklet_struct *t) * @hcd: host controller returning the URB * @urb: urb being returned to the USB device driver. * @status: completion status code for the URB. - * Context: in_interrupt() + * + * Context: atomic. The completion callback is invoked in caller's context. + * For HCDs with HCD_BH flag set, the completion callback is invoked in tasklet + * context (except for URBs submitted to the root hub which always complete in + * caller's context). * * This hands the URB from HCD to its USB device driver, using its * completion function. The HCD has freed all per-urb resources @@ -2268,7 +2280,7 @@ EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub); * usb_bus_start_enum - start immediate enumeration (for OTG) * @bus: the bus (must use hcd framework) * @port_num: 1-based number of port; usually bus->otg_port - * Context: in_interrupt() + * Context: atomic * * Starts enumeration, with an immediate reset followed later by * hub_wq identifying and possibly configuring the device. @@ -2474,7 +2486,8 @@ EXPORT_SYMBOL_GPL(__usb_create_hcd); * @bus_name: value to store in hcd->self.bus_name * @primary_hcd: a pointer to the usb_hcd structure that is sharing the * PCI device. Only allocate certain resources for the primary HCD - * Context: !in_interrupt() + * + * Context: task context, might sleep. * * Allocate a struct usb_hcd, with extra space at the end for the * HC driver's private data. Initialize the generic members of the @@ -2496,7 +2509,8 @@ EXPORT_SYMBOL_GPL(usb_create_shared_hcd); * @driver: HC driver that will use this hcd * @dev: device for this HC, stored in hcd->self.controller * @bus_name: value to store in hcd->self.bus_name - * Context: !in_interrupt() + * + * Context: task context, might sleep. * * Allocate a struct usb_hcd, with extra space at the end for the * HC driver's private data. Initialize the generic members of the @@ -2830,7 +2844,8 @@ EXPORT_SYMBOL_GPL(usb_add_hcd); /** * usb_remove_hcd - shutdown processing for generic HCDs * @hcd: the usb_hcd structure to remove - * Context: !in_interrupt() + * + * Context: task context, might sleep. * * Disconnects the root hub, then reverses the effects of usb_add_hcd(), * invoking the HCD's stop() method. diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 17202b2ee063..7f71218cc1e5 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2171,7 +2171,8 @@ static void hub_disconnect_children(struct usb_device *udev) /** * usb_disconnect - disconnect a device (usbcore-internal) * @pdev: pointer to device being disconnected - * Context: !in_interrupt () + * + * Context: task context, might sleep * * Something got disconnected. Get rid of it and all of its children. * diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 19ebb542befc..30e9e680c74c 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -119,7 +119,7 @@ static int usb_internal_control_msg(struct usb_device *usb_dev, * @timeout: time in msecs to wait for the message to complete before timing * out (if 0 the wait is forever) * - * Context: !in_interrupt () + * Context: task context, might sleep. * * This function sends a simple control message to a specified endpoint and * waits for the message to complete, or timeout. @@ -204,9 +204,6 @@ int usb_control_msg_send(struct usb_device *dev, __u8 endpoint, __u8 request, int ret; u8 *data = NULL; - if (usb_pipe_type_check(dev, pipe)) - return -EINVAL; - if (size) { data = kmemdup(driver_data, size, memflags); if (!data) @@ -219,9 +216,8 @@ int usb_control_msg_send(struct usb_device *dev, __u8 endpoint, __u8 request, if (ret < 0) return ret; - if (ret == size) - return 0; - return -EINVAL; + + return 0; } EXPORT_SYMBOL_GPL(usb_control_msg_send); @@ -273,7 +269,7 @@ int usb_control_msg_recv(struct usb_device *dev, __u8 endpoint, __u8 request, int ret; u8 *data; - if (!size || !driver_data || usb_pipe_type_check(dev, pipe)) + if (!size || !driver_data) return -EINVAL; data = kmalloc(size, memflags); @@ -290,7 +286,7 @@ int usb_control_msg_recv(struct usb_device *dev, __u8 endpoint, __u8 request, memcpy(driver_data, data, size); ret = 0; } else { - ret = -EINVAL; + ret = -EREMOTEIO; } exit: @@ -310,7 +306,7 @@ EXPORT_SYMBOL_GPL(usb_control_msg_recv); * @timeout: time in msecs to wait for the message to complete before * timing out (if 0 the wait is forever) * - * Context: !in_interrupt () + * Context: task context, might sleep. * * This function sends a simple interrupt message to a specified endpoint and * waits for the message to complete, or timeout. @@ -343,7 +339,7 @@ EXPORT_SYMBOL_GPL(usb_interrupt_msg); * @timeout: time in msecs to wait for the message to complete before * timing out (if 0 the wait is forever) * - * Context: !in_interrupt () + * Context: task context, might sleep. * * This function sends a simple bulk message to a specified endpoint * and waits for the message to complete, or timeout. @@ -610,7 +606,8 @@ EXPORT_SYMBOL_GPL(usb_sg_init); * usb_sg_wait - synchronously execute scatter/gather request * @io: request block handle, as initialized with usb_sg_init(). * some fields become accessible when this call returns. - * Context: !in_interrupt () + * + * Context: task context, might sleep. * * This function blocks until the specified I/O operation completes. It * leverages the grouping of the related I/O requests to get good transfer @@ -764,7 +761,8 @@ EXPORT_SYMBOL_GPL(usb_sg_cancel); * @index: the number of the descriptor * @buf: where to put the descriptor * @size: how big is "buf"? - * Context: !in_interrupt () + * + * Context: task context, might sleep. * * Gets a USB descriptor. Convenience functions exist to simplify * getting some types of descriptors. Use @@ -812,7 +810,8 @@ EXPORT_SYMBOL_GPL(usb_get_descriptor); * @index: the number of the descriptor * @buf: where to put the string * @size: how big is "buf"? - * Context: !in_interrupt () + * + * Context: task context, might sleep. * * Retrieves a string, encoded using UTF-16LE (Unicode, 16 bits per character, * in little-endian byte order). @@ -947,7 +946,8 @@ static int usb_get_langid(struct usb_device *dev, unsigned char *tbuf) * @index: the number of the descriptor * @buf: where to put the string * @size: how big is "buf"? - * Context: !in_interrupt () + * + * Context: task context, might sleep. * * This converts the UTF-16LE encoded strings returned by devices, from * usb_get_string_descriptor(), to null-terminated UTF-8 encoded ones @@ -1036,7 +1036,8 @@ char *usb_cache_string(struct usb_device *udev, int index) * usb_get_device_descriptor - (re)reads the device descriptor (usbcore) * @dev: the device whose device descriptor is being updated * @size: how much of the descriptor to read - * Context: !in_interrupt () + * + * Context: task context, might sleep. * * Updates the copy of the device descriptor stored in the device structure, * which dedicates space for this purpose. @@ -1071,7 +1072,7 @@ int usb_get_device_descriptor(struct usb_device *dev, unsigned int size) /* * usb_set_isoch_delay - informs the device of the packet transmit delay * @dev: the device whose delay is to be informed - * Context: !in_interrupt() + * Context: task context, might sleep * * Since this is an optional request, we don't bother if it fails. */ @@ -1100,7 +1101,8 @@ int usb_set_isoch_delay(struct usb_device *dev) * @type: USB_STATUS_TYPE_*; for standard or PTM status types * @target: zero (for device), else interface or endpoint number * @data: pointer to two bytes of bitmap data - * Context: !in_interrupt () + * + * Context: task context, might sleep. * * Returns device, interface, or endpoint status. Normally only of * interest to see if the device is self powered, or has enabled the @@ -1177,7 +1179,8 @@ EXPORT_SYMBOL_GPL(usb_get_status); * usb_clear_halt - tells device to clear endpoint halt/stall condition * @dev: device whose endpoint is halted * @pipe: endpoint "pipe" being cleared - * Context: !in_interrupt () + * + * Context: task context, might sleep. * * This is used to clear halt conditions for bulk and interrupt endpoints, * as reported by URB completion status. Endpoints that are halted are @@ -1481,7 +1484,8 @@ void usb_enable_interface(struct usb_device *dev, * @dev: the device whose interface is being updated * @interface: the interface being updated * @alternate: the setting being chosen. - * Context: !in_interrupt () + * + * Context: task context, might sleep. * * This is used to enable data transfers on interfaces that may not * be enabled by default. Not all devices support such configurability. @@ -1902,7 +1906,8 @@ static void __usb_queue_reset_device(struct work_struct *ws) * usb_set_configuration - Makes a particular device setting be current * @dev: the device whose configuration is being updated * @configuration: the configuration being chosen. - * Context: !in_interrupt(), caller owns the device lock + * + * Context: task context, might sleep. Caller holds device lock. * * This is used to enable non-default device modes. Not all devices * use this kind of configurability; many devices only have one diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c index 235a7c645503..dfcca9c876c7 100644 --- a/drivers/usb/core/port.c +++ b/drivers/usb/core/port.c @@ -155,7 +155,7 @@ static struct attribute *port_dev_attrs[] = { NULL, }; -static struct attribute_group port_dev_attr_grp = { +static const struct attribute_group port_dev_attr_grp = { .attrs = port_dev_attrs, }; @@ -169,7 +169,7 @@ static struct attribute *port_dev_usb3_attrs[] = { NULL, }; -static struct attribute_group port_dev_usb3_attr_grp = { +static const struct attribute_group port_dev_usb3_attr_grp = { .attrs = port_dev_usb3_attrs, }; diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index fad31ccd1fa8..1b4eb7046b07 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -342,6 +342,9 @@ static const struct usb_device_id usb_quirk_list[] = { { USB_DEVICE(0x06a3, 0x0006), .driver_info = USB_QUIRK_CONFIG_INTF_STRINGS }, + /* Agfa SNAPSCAN 1212U */ + { USB_DEVICE(0x06bd, 0x0001), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Guillemot Webcam Hercules Dualpix Exchange (2nd ID) */ { USB_DEVICE(0x06f8, 0x0804), .driver_info = USB_QUIRK_RESET_RESUME }, diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 8d134193fa0c..d85699bee671 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -641,7 +641,7 @@ static struct attribute *usb2_hardware_lpm_attr[] = { &dev_attr_usb2_lpm_besl.attr, NULL, }; -static struct attribute_group usb2_hardware_lpm_attr_group = { +static const struct attribute_group usb2_hardware_lpm_attr_group = { .name = power_group_name, .attrs = usb2_hardware_lpm_attr, }; @@ -651,7 +651,7 @@ static struct attribute *usb3_hardware_lpm_attr[] = { &dev_attr_usb3_hardware_lpm_u2.attr, NULL, }; -static struct attribute_group usb3_hardware_lpm_attr_group = { +static const struct attribute_group usb3_hardware_lpm_attr_group = { .name = power_group_name, .attrs = usb3_hardware_lpm_attr, }; @@ -663,7 +663,7 @@ static struct attribute *power_attrs[] = { &dev_attr_active_duration.attr, NULL, }; -static struct attribute_group power_attr_group = { +static const struct attribute_group power_attr_group = { .name = power_group_name, .attrs = power_attrs, }; @@ -832,7 +832,7 @@ static struct attribute *dev_attrs[] = { #endif NULL, }; -static struct attribute_group dev_attr_grp = { +static const struct attribute_group dev_attr_grp = { .attrs = dev_attrs, }; @@ -865,7 +865,7 @@ static umode_t dev_string_attrs_are_visible(struct kobject *kobj, return a->mode; } -static struct attribute_group dev_string_attr_grp = { +static const struct attribute_group dev_string_attr_grp = { .attrs = dev_string_attrs, .is_visible = dev_string_attrs_are_visible, }; @@ -1222,7 +1222,7 @@ static struct attribute *intf_attrs[] = { &dev_attr_interface_authorized.attr, NULL, }; -static struct attribute_group intf_attr_grp = { +static const struct attribute_group intf_attr_grp = { .attrs = intf_attrs, }; @@ -1246,7 +1246,7 @@ static umode_t intf_assoc_attrs_are_visible(struct kobject *kobj, return a->mode; } -static struct attribute_group intf_assoc_attr_grp = { +static const struct attribute_group intf_assoc_attr_grp = { .attrs = intf_assoc_attrs, .is_visible = intf_assoc_attrs_are_visible, }; diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 9b4ac4415f1a..8f07b0516100 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -28,7 +28,6 @@ #include <linux/string.h> #include <linux/bitops.h> #include <linux/slab.h> -#include <linux/interrupt.h> /* for in_interrupt() */ #include <linux/kmod.h> #include <linux/init.h> #include <linux/spinlock.h> @@ -561,7 +560,8 @@ static bool usb_dev_authorized(struct usb_device *dev, struct usb_hcd *hcd) * @parent: hub to which device is connected; null to allocate a root hub * @bus: bus used to access the device * @port1: one-based index of port; ignored for root hubs - * Context: !in_interrupt() + * + * Context: task context, might sleep. * * Only hub drivers (including virtual root hub drivers for host * controllers) should ever call this. diff --git a/drivers/usb/gadget/function/f_acm.c b/drivers/usb/gadget/function/f_acm.c index 46647bfac2ef..349945e064bb 100644 --- a/drivers/usb/gadget/function/f_acm.c +++ b/drivers/usb/gadget/function/f_acm.c @@ -686,7 +686,7 @@ acm_bind(struct usb_configuration *c, struct usb_function *f) acm_ss_out_desc.bEndpointAddress = acm_fs_out_desc.bEndpointAddress; status = usb_assign_descriptors(f, acm_fs_function, acm_hs_function, - acm_ss_function, NULL); + acm_ss_function, acm_ss_function); if (status) goto fail; diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index c727cb5de871..801a8b668a35 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -296,11 +296,11 @@ static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len) reinit_completion(&ffs->ep0req_completion); ret = usb_ep_queue(ffs->gadget->ep0, req, GFP_ATOMIC); - if (unlikely(ret < 0)) + if (ret < 0) return ret; ret = wait_for_completion_interruptible(&ffs->ep0req_completion); - if (unlikely(ret)) { + if (ret) { usb_ep_dequeue(ffs->gadget->ep0, req); return -EINTR; } @@ -337,7 +337,7 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf, /* Acquire mutex */ ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK); - if (unlikely(ret < 0)) + if (ret < 0) return ret; /* Check state */ @@ -345,7 +345,7 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf, case FFS_READ_DESCRIPTORS: case FFS_READ_STRINGS: /* Copy data */ - if (unlikely(len < 16)) { + if (len < 16) { ret = -EINVAL; break; } @@ -360,7 +360,7 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf, if (ffs->state == FFS_READ_DESCRIPTORS) { pr_info("read descriptors\n"); ret = __ffs_data_got_descs(ffs, data, len); - if (unlikely(ret < 0)) + if (ret < 0) break; ffs->state = FFS_READ_STRINGS; @@ -368,11 +368,11 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf, } else { pr_info("read strings\n"); ret = __ffs_data_got_strings(ffs, data, len); - if (unlikely(ret < 0)) + if (ret < 0) break; ret = ffs_epfiles_create(ffs); - if (unlikely(ret)) { + if (ret) { ffs->state = FFS_CLOSING; break; } @@ -381,7 +381,7 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf, mutex_unlock(&ffs->mutex); ret = ffs_ready(ffs); - if (unlikely(ret < 0)) { + if (ret < 0) { ffs->state = FFS_CLOSING; return ret; } @@ -495,7 +495,7 @@ static ssize_t __ffs_ep0_read_events(struct ffs_data *ffs, char __user *buf, spin_unlock_irq(&ffs->ev.waitq.lock); mutex_unlock(&ffs->mutex); - return unlikely(copy_to_user(buf, events, size)) ? -EFAULT : size; + return copy_to_user(buf, events, size) ? -EFAULT : size; } static ssize_t ffs_ep0_read(struct file *file, char __user *buf, @@ -514,7 +514,7 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf, /* Acquire mutex */ ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK); - if (unlikely(ret < 0)) + if (ret < 0) return ret; /* Check state */ @@ -536,7 +536,7 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf, case FFS_NO_SETUP: n = len / sizeof(struct usb_functionfs_event); - if (unlikely(!n)) { + if (!n) { ret = -EINVAL; break; } @@ -567,9 +567,9 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf, spin_unlock_irq(&ffs->ev.waitq.lock); - if (likely(len)) { + if (len) { data = kmalloc(len, GFP_KERNEL); - if (unlikely(!data)) { + if (!data) { ret = -ENOMEM; goto done_mutex; } @@ -586,7 +586,7 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf, /* unlocks spinlock */ ret = __ffs_ep0_queue_wait(ffs, data, len); - if (likely(ret > 0) && unlikely(copy_to_user(buf, data, len))) + if ((ret > 0) && (copy_to_user(buf, data, len))) ret = -EFAULT; goto done_mutex; @@ -608,7 +608,7 @@ static int ffs_ep0_open(struct inode *inode, struct file *file) ENTER(); - if (unlikely(ffs->state == FFS_CLOSING)) + if (ffs->state == FFS_CLOSING) return -EBUSY; file->private_data = ffs; @@ -657,7 +657,7 @@ static __poll_t ffs_ep0_poll(struct file *file, poll_table *wait) poll_wait(file, &ffs->ev.waitq, wait); ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK); - if (unlikely(ret < 0)) + if (ret < 0) return mask; switch (ffs->state) { @@ -678,6 +678,8 @@ static __poll_t ffs_ep0_poll(struct file *file, poll_table *wait) mask |= (EPOLLIN | EPOLLOUT); break; } + break; + case FFS_CLOSING: break; case FFS_DEACTIVATED: @@ -706,7 +708,7 @@ static const struct file_operations ffs_ep0_operations = { static void ffs_epfile_io_complete(struct usb_ep *_ep, struct usb_request *req) { ENTER(); - if (likely(req->context)) { + if (req->context) { struct ffs_ep *ep = _ep->driver_data; ep->status = req->status ? req->status : req->actual; complete(req->context); @@ -716,10 +718,10 @@ static void ffs_epfile_io_complete(struct usb_ep *_ep, struct usb_request *req) static ssize_t ffs_copy_to_iter(void *data, int data_len, struct iov_iter *iter) { ssize_t ret = copy_to_iter(data, data_len, iter); - if (likely(ret == data_len)) + if (ret == data_len) return ret; - if (unlikely(iov_iter_count(iter))) + if (iov_iter_count(iter)) return -EFAULT; /* @@ -885,7 +887,7 @@ static ssize_t __ffs_epfile_read_buffered(struct ffs_epfile *epfile, return ret; } - if (unlikely(iov_iter_count(iter))) { + if (iov_iter_count(iter)) { ret = -EFAULT; } else { buf->length -= ret; @@ -906,10 +908,10 @@ static ssize_t __ffs_epfile_read_data(struct ffs_epfile *epfile, struct ffs_buffer *buf; ssize_t ret = copy_to_iter(data, data_len, iter); - if (likely(data_len == ret)) + if (data_len == ret) return ret; - if (unlikely(iov_iter_count(iter))) + if (iov_iter_count(iter)) return -EFAULT; /* See ffs_copy_to_iter for more context. */ @@ -930,7 +932,7 @@ static ssize_t __ffs_epfile_read_data(struct ffs_epfile *epfile, * in struct ffs_epfile for full read_buffer pointer synchronisation * story. */ - if (unlikely(cmpxchg(&epfile->read_buffer, NULL, buf))) + if (cmpxchg(&epfile->read_buffer, NULL, buf)) kfree(buf); return ret; @@ -968,7 +970,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) /* We will be using request and read_buffer */ ret = ffs_mutex_lock(&epfile->mutex, file->f_flags & O_NONBLOCK); - if (unlikely(ret)) + if (ret) goto error; /* Allocate & copy */ @@ -1013,7 +1015,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) spin_unlock_irq(&epfile->ffs->eps_lock); data = ffs_alloc_buffer(io_data, data_len); - if (unlikely(!data)) { + if (!data) { ret = -ENOMEM; goto error_mutex; } @@ -1033,7 +1035,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) ret = usb_ep_set_halt(ep->ep); if (!ret) ret = -EBADMSG; - } else if (unlikely(data_len == -EINVAL)) { + } else if (data_len == -EINVAL) { /* * Sanity Check: even though data_len can't be used * uninitialized at the time I write this comment, some @@ -1068,12 +1070,12 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) req->complete = ffs_epfile_io_complete; ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC); - if (unlikely(ret < 0)) + if (ret < 0) goto error_lock; spin_unlock_irq(&epfile->ffs->eps_lock); - if (unlikely(wait_for_completion_interruptible(&done))) { + if (wait_for_completion_interruptible(&done)) { /* * To avoid race condition with ffs_epfile_io_complete, * dequeue the request first then check @@ -1115,7 +1117,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) req->complete = ffs_epfile_async_io_complete; ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC); - if (unlikely(ret)) { + if (ret) { io_data->req = NULL; usb_ep_free_request(ep->ep, req); goto error_lock; @@ -1166,7 +1168,7 @@ static int ffs_aio_cancel(struct kiocb *kiocb) spin_lock_irqsave(&epfile->ffs->eps_lock, flags); - if (likely(io_data && io_data->ep && io_data->req)) + if (io_data && io_data->ep && io_data->req) value = usb_ep_dequeue(io_data->ep, io_data->req); else value = -EINVAL; @@ -1185,7 +1187,7 @@ static ssize_t ffs_epfile_write_iter(struct kiocb *kiocb, struct iov_iter *from) if (!is_sync_kiocb(kiocb)) { p = kzalloc(sizeof(io_data), GFP_KERNEL); - if (unlikely(!p)) + if (!p) return -ENOMEM; p->aio = true; } else { @@ -1222,7 +1224,7 @@ static ssize_t ffs_epfile_read_iter(struct kiocb *kiocb, struct iov_iter *to) if (!is_sync_kiocb(kiocb)) { p = kzalloc(sizeof(io_data), GFP_KERNEL); - if (unlikely(!p)) + if (!p) return -ENOMEM; p->aio = true; } else { @@ -1328,6 +1330,7 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code, switch (epfile->ffs->gadget->speed) { case USB_SPEED_SUPER: + case USB_SPEED_SUPER_PLUS: desc_idx = 2; break; case USB_SPEED_HIGH: @@ -1385,7 +1388,7 @@ ffs_sb_make_inode(struct super_block *sb, void *data, inode = new_inode(sb); - if (likely(inode)) { + if (inode) { struct timespec64 ts = current_time(inode); inode->i_ino = get_next_ino(); @@ -1417,11 +1420,11 @@ static struct dentry *ffs_sb_create_file(struct super_block *sb, ENTER(); dentry = d_alloc_name(sb->s_root, name); - if (unlikely(!dentry)) + if (!dentry) return NULL; inode = ffs_sb_make_inode(sb, data, fops, NULL, &ffs->file_perms); - if (unlikely(!inode)) { + if (!inode) { dput(dentry); return NULL; } @@ -1468,12 +1471,11 @@ static int ffs_sb_fill(struct super_block *sb, struct fs_context *fc) &simple_dir_inode_operations, &data->perms); sb->s_root = d_make_root(inode); - if (unlikely(!sb->s_root)) + if (!sb->s_root) return -ENOMEM; /* EP0 file */ - if (unlikely(!ffs_sb_create_file(sb, "ep0", ffs, - &ffs_ep0_operations))) + if (!ffs_sb_create_file(sb, "ep0", ffs, &ffs_ep0_operations)) return -ENOMEM; return 0; @@ -1561,13 +1563,13 @@ static int ffs_fs_get_tree(struct fs_context *fc) return invalf(fc, "No source specified"); ffs = ffs_data_new(fc->source); - if (unlikely(!ffs)) + if (!ffs) return -ENOMEM; ffs->file_perms = ctx->perms; ffs->no_disconnect = ctx->no_disconnect; ffs->dev_name = kstrdup(fc->source, GFP_KERNEL); - if (unlikely(!ffs->dev_name)) { + if (!ffs->dev_name) { ffs_data_put(ffs); return -ENOMEM; } @@ -1653,7 +1655,7 @@ static int functionfs_init(void) ENTER(); ret = register_filesystem(&ffs_fs_type); - if (likely(!ret)) + if (!ret) pr_info("file system registered\n"); else pr_err("failed registering file system (%d)\n", ret); @@ -1698,7 +1700,7 @@ static void ffs_data_put(struct ffs_data *ffs) { ENTER(); - if (unlikely(refcount_dec_and_test(&ffs->ref))) { + if (refcount_dec_and_test(&ffs->ref)) { pr_info("%s(): freeing\n", __func__); ffs_data_clear(ffs); BUG_ON(waitqueue_active(&ffs->ev.waitq) || @@ -1740,7 +1742,7 @@ static void ffs_data_closed(struct ffs_data *ffs) static struct ffs_data *ffs_data_new(const char *dev_name) { struct ffs_data *ffs = kzalloc(sizeof *ffs, GFP_KERNEL); - if (unlikely(!ffs)) + if (!ffs) return NULL; ENTER(); @@ -1830,11 +1832,11 @@ static int functionfs_bind(struct ffs_data *ffs, struct usb_composite_dev *cdev) return -EBADFD; first_id = usb_string_ids_n(cdev, ffs->strings_count); - if (unlikely(first_id < 0)) + if (first_id < 0) return first_id; ffs->ep0req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL); - if (unlikely(!ffs->ep0req)) + if (!ffs->ep0req) return -ENOMEM; ffs->ep0req->complete = ffs_ep0_complete; ffs->ep0req->context = ffs; @@ -1890,7 +1892,7 @@ static int ffs_epfiles_create(struct ffs_data *ffs) epfile->dentry = ffs_sb_create_file(ffs->sb, epfile->name, epfile, &ffs_epfile_operations); - if (unlikely(!epfile->dentry)) { + if (!epfile->dentry) { ffs_epfiles_destroy(epfiles, i - 1); return -ENOMEM; } @@ -1928,7 +1930,7 @@ static void ffs_func_eps_disable(struct ffs_function *func) spin_lock_irqsave(&func->ffs->eps_lock, flags); while (count--) { /* pending requests get nuked */ - if (likely(ep->ep)) + if (ep->ep) usb_ep_disable(ep->ep); ++ep; @@ -1962,7 +1964,7 @@ static int ffs_func_eps_enable(struct ffs_function *func) } ret = usb_ep_enable(ep->ep); - if (likely(!ret)) { + if (!ret) { epfile->ep = ep; epfile->in = usb_endpoint_dir_in(ep->ep->desc); epfile->isoc = usb_endpoint_xfer_isoc(ep->ep->desc); @@ -2035,12 +2037,12 @@ static int __must_check ffs_do_single_desc(char *data, unsigned len, #define __entity_check_ENDPOINT(val) ((val) & USB_ENDPOINT_NUMBER_MASK) #define __entity(type, val) do { \ pr_vdebug("entity " #type "(%02x)\n", (val)); \ - if (unlikely(!__entity_check_ ##type(val))) { \ + if (!__entity_check_ ##type(val)) { \ pr_vdebug("invalid entity's value\n"); \ return -EINVAL; \ } \ ret = entity(FFS_ ##type, &val, _ds, priv); \ - if (unlikely(ret < 0)) { \ + if (ret < 0) { \ pr_debug("entity " #type "(%02x); ret = %d\n", \ (val), ret); \ return ret; \ @@ -2165,7 +2167,7 @@ static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len, /* Record "descriptor" entity */ ret = entity(FFS_DESCRIPTOR, (u8 *)num, (void *)data, priv); - if (unlikely(ret < 0)) { + if (ret < 0) { pr_debug("entity DESCRIPTOR(%02lx); ret = %d\n", num, ret); return ret; @@ -2176,7 +2178,7 @@ static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len, ret = ffs_do_single_desc(data, len, entity, priv, ¤t_class); - if (unlikely(ret < 0)) { + if (ret < 0) { pr_debug("%s returns %d\n", __func__, ret); return ret; } @@ -2282,7 +2284,7 @@ static int __must_check ffs_do_single_os_desc(char *data, unsigned len, /* loop over all ext compat/ext prop descriptors */ while (feature_count--) { ret = entity(type, h, data, len, priv); - if (unlikely(ret < 0)) { + if (ret < 0) { pr_debug("bad OS descriptor, type: %d\n", type); return ret; } @@ -2322,7 +2324,7 @@ static int __must_check ffs_do_os_descs(unsigned count, return -EINVAL; ret = __ffs_do_os_desc_header(&type, desc); - if (unlikely(ret < 0)) { + if (ret < 0) { pr_debug("entity OS_DESCRIPTOR(%02lx); ret = %d\n", num, ret); return ret; @@ -2343,7 +2345,7 @@ static int __must_check ffs_do_os_descs(unsigned count, */ ret = ffs_do_single_os_desc(data, len, type, feature_count, entity, priv, desc); - if (unlikely(ret < 0)) { + if (ret < 0) { pr_debug("%s returns %d\n", __func__, ret); return ret; } @@ -2575,20 +2577,20 @@ static int __ffs_data_got_strings(struct ffs_data *ffs, ENTER(); - if (unlikely(len < 16 || - get_unaligned_le32(data) != FUNCTIONFS_STRINGS_MAGIC || - get_unaligned_le32(data + 4) != len)) + if (len < 16 || + get_unaligned_le32(data) != FUNCTIONFS_STRINGS_MAGIC || + get_unaligned_le32(data + 4) != len) goto error; str_count = get_unaligned_le32(data + 8); lang_count = get_unaligned_le32(data + 12); /* if one is zero the other must be zero */ - if (unlikely(!str_count != !lang_count)) + if (!str_count != !lang_count) goto error; /* Do we have at least as many strings as descriptors need? */ needed_count = ffs->strings_count; - if (unlikely(str_count < needed_count)) + if (str_count < needed_count) goto error; /* @@ -2612,7 +2614,7 @@ static int __ffs_data_got_strings(struct ffs_data *ffs, char *vlabuf = kmalloc(vla_group_size(d), GFP_KERNEL); - if (unlikely(!vlabuf)) { + if (!vlabuf) { kfree(_data); return -ENOMEM; } @@ -2639,7 +2641,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; - if (unlikely(len < 3)) + if (len < 3) goto error_free; t->language = get_unaligned_le16(data); t->strings = s; @@ -2652,7 +2654,7 @@ static int __ffs_data_got_strings(struct ffs_data *ffs, do { /* str_count > 0 so we can use do-while */ size_t length = strnlen(data, len); - if (unlikely(length == len)) + if (length == len) goto error_free; /* @@ -2660,7 +2662,7 @@ static int __ffs_data_got_strings(struct ffs_data *ffs, * if that's the case we simply ignore the * rest */ - if (likely(needed)) { + if (needed) { /* * s->id will be set while adding * function to configuration so for @@ -2682,7 +2684,7 @@ static int __ffs_data_got_strings(struct ffs_data *ffs, } while (--lang_count); /* Some garbage left? */ - if (unlikely(len)) + if (len) goto error_free; /* Done! */ @@ -2829,7 +2831,7 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep, ffs_ep = func->eps + idx; - if (unlikely(ffs_ep->descs[ep_desc_id])) { + if (ffs_ep->descs[ep_desc_id]) { pr_err("two %sspeed descriptors for EP %d\n", speed_names[ep_desc_id], ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); @@ -2860,12 +2862,12 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep, wMaxPacketSize = ds->wMaxPacketSize; pr_vdebug("autoconfig\n"); ep = usb_ep_autoconfig(func->gadget, ds); - if (unlikely(!ep)) + if (!ep) return -ENOTSUPP; ep->driver_data = func->eps + idx; req = usb_ep_alloc_request(ep, GFP_KERNEL); - if (unlikely(!req)) + if (!req) return -ENOMEM; ffs_ep->ep = ep; @@ -2907,7 +2909,7 @@ static int __ffs_func_bind_do_nums(enum ffs_entity_type type, u8 *valuep, idx = *valuep; if (func->interfaces_nums[idx] < 0) { int id = usb_interface_id(func->conf, &func->function); - if (unlikely(id < 0)) + if (id < 0) return id; func->interfaces_nums[idx] = id; } @@ -2928,7 +2930,7 @@ static int __ffs_func_bind_do_nums(enum ffs_entity_type type, u8 *valuep, return 0; idx = (*valuep & USB_ENDPOINT_NUMBER_MASK) - 1; - if (unlikely(!func->eps[idx].ep)) + if (!func->eps[idx].ep) return -EINVAL; { @@ -3111,12 +3113,12 @@ static int _ffs_func_bind(struct usb_configuration *c, ENTER(); /* Has descriptors only for speeds gadget does not support */ - if (unlikely(!(full | high | super))) + if (!(full | high | super)) return -ENOTSUPP; /* Allocate a single chunk, less management later on */ vlabuf = kzalloc(vla_group_size(d), GFP_KERNEL); - if (unlikely(!vlabuf)) + if (!vlabuf) return -ENOMEM; ffs->ms_os_descs_ext_prop_avail = vla_ptr(vlabuf, d, ext_prop); @@ -3145,13 +3147,13 @@ static int _ffs_func_bind(struct usb_configuration *c, * endpoints first, so that later we can rewrite the endpoint * numbers without worrying that it may be described later on. */ - if (likely(full)) { + if (full) { func->function.fs_descriptors = vla_ptr(vlabuf, d, fs_descs); fs_len = ffs_do_descs(ffs->fs_descs_count, vla_ptr(vlabuf, d, raw_descs), d_raw_descs__sz, __ffs_func_bind_do_descs, func); - if (unlikely(fs_len < 0)) { + if (fs_len < 0) { ret = fs_len; goto error; } @@ -3159,13 +3161,13 @@ static int _ffs_func_bind(struct usb_configuration *c, fs_len = 0; } - if (likely(high)) { + if (high) { func->function.hs_descriptors = vla_ptr(vlabuf, d, hs_descs); hs_len = ffs_do_descs(ffs->hs_descs_count, vla_ptr(vlabuf, d, raw_descs) + fs_len, d_raw_descs__sz - fs_len, __ffs_func_bind_do_descs, func); - if (unlikely(hs_len < 0)) { + if (hs_len < 0) { ret = hs_len; goto error; } @@ -3173,13 +3175,14 @@ static int _ffs_func_bind(struct usb_configuration *c, hs_len = 0; } - if (likely(super)) { - func->function.ss_descriptors = vla_ptr(vlabuf, d, ss_descs); + if (super) { + func->function.ss_descriptors = func->function.ssp_descriptors = + vla_ptr(vlabuf, d, ss_descs); ss_len = ffs_do_descs(ffs->ss_descs_count, vla_ptr(vlabuf, d, raw_descs) + fs_len + hs_len, d_raw_descs__sz - fs_len - hs_len, __ffs_func_bind_do_descs, func); - if (unlikely(ss_len < 0)) { + if (ss_len < 0) { ret = ss_len; goto error; } @@ -3197,7 +3200,7 @@ static int _ffs_func_bind(struct usb_configuration *c, (super ? ffs->ss_descs_count : 0), vla_ptr(vlabuf, d, raw_descs), d_raw_descs__sz, __ffs_func_bind_do_nums, func); - if (unlikely(ret < 0)) + if (ret < 0) goto error; func->function.os_desc_table = vla_ptr(vlabuf, d, os_desc_table); @@ -3218,7 +3221,7 @@ static int _ffs_func_bind(struct usb_configuration *c, d_raw_descs__sz - fs_len - hs_len - ss_len, __ffs_func_bind_do_os_desc, func); - if (unlikely(ret < 0)) + if (ret < 0) goto error; } func->function.os_desc_n = @@ -3269,7 +3272,7 @@ static int ffs_func_set_alt(struct usb_function *f, if (alt != (unsigned)-1) { intf = ffs_func_revmap_intf(func, interface); - if (unlikely(intf < 0)) + if (intf < 0) return intf; } @@ -3294,7 +3297,7 @@ static int ffs_func_set_alt(struct usb_function *f, ffs->func = func; ret = ffs_func_eps_enable(func); - if (likely(ret >= 0)) + if (ret >= 0) ffs_event_add(ffs, FUNCTIONFS_ENABLE); return ret; } @@ -3336,13 +3339,13 @@ static int ffs_func_setup(struct usb_function *f, switch (creq->bRequestType & USB_RECIP_MASK) { case USB_RECIP_INTERFACE: ret = ffs_func_revmap_intf(func, le16_to_cpu(creq->wIndex)); - if (unlikely(ret < 0)) + if (ret < 0) return ret; break; case USB_RECIP_ENDPOINT: ret = ffs_func_revmap_ep(func, le16_to_cpu(creq->wIndex)); - if (unlikely(ret < 0)) + if (ret < 0) return ret; if (func->ffs->user_flags & FUNCTIONFS_VIRTUAL_ADDR) ret = func->ffs->eps_addrmap[ret]; @@ -3584,6 +3587,7 @@ static void ffs_func_unbind(struct usb_configuration *c, func->function.fs_descriptors = NULL; func->function.hs_descriptors = NULL; func->function.ss_descriptors = NULL; + func->function.ssp_descriptors = NULL; func->interfaces_nums = NULL; ffs_event_add(ffs, FUNCTIONFS_UNBIND); @@ -3596,7 +3600,7 @@ static struct usb_function *ffs_alloc(struct usb_function_instance *fi) ENTER(); func = kzalloc(sizeof(*func), GFP_KERNEL); - if (unlikely(!func)) + if (!func) return ERR_PTR(-ENOMEM); func->function.name = "Function FS Gadget"; @@ -3811,7 +3815,7 @@ done: static int ffs_mutex_lock(struct mutex *mutex, unsigned nonblock) { return nonblock - ? likely(mutex_trylock(mutex)) ? 0 : -EAGAIN + ? mutex_trylock(mutex) ? 0 : -EAGAIN : mutex_lock_interruptible(mutex); } @@ -3819,14 +3823,14 @@ static char *ffs_prepare_buffer(const char __user *buf, size_t len) { char *data; - if (unlikely(!len)) + if (!len) return NULL; data = kmalloc(len, GFP_KERNEL); - if (unlikely(!data)) + if (!data) return ERR_PTR(-ENOMEM); - if (unlikely(copy_from_user(data, buf, len))) { + if (copy_from_user(data, buf, len)) { kfree(data); return ERR_PTR(-EFAULT); } diff --git a/drivers/usb/gadget/function/f_loopback.c b/drivers/usb/gadget/function/f_loopback.c index 1803646b3678..b56ad7c3838b 100644 --- a/drivers/usb/gadget/function/f_loopback.c +++ b/drivers/usb/gadget/function/f_loopback.c @@ -274,7 +274,7 @@ static void loopback_complete(struct usb_ep *ep, struct usb_request *req) default: ERROR(cdev, "%s loop complete --> %d, %d/%d\n", ep->name, status, req->actual, req->length); - /* FALLTHROUGH */ + fallthrough; /* NOTE: since this driver doesn't maintain an explicit record * of requests it submitted (just maintains qlen count), we diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index 19d97940eeb9..8fff995b8dd5 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -1048,6 +1048,12 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f) f->ss_descriptors = usb_copy_descriptors(midi_function); if (!f->ss_descriptors) goto fail_f_midi; + + if (gadget_is_superspeed_plus(c->cdev->gadget)) { + f->ssp_descriptors = usb_copy_descriptors(midi_function); + if (!f->ssp_descriptors) + goto fail_f_midi; + } } kfree(midi_function); diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c index 9534c8ab62a8..0739b05a0ef7 100644 --- a/drivers/usb/gadget/function/f_rndis.c +++ b/drivers/usb/gadget/function/f_rndis.c @@ -87,8 +87,10 @@ static inline struct f_rndis *func_to_rndis(struct usb_function *f) /* peak (theoretical) bulk transfer rate in bits-per-second */ static unsigned int bitrate(struct usb_gadget *g) { + if (gadget_is_superspeed(g) && g->speed >= USB_SPEED_SUPER_PLUS) + return 4250000000U; if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER) - return 13 * 1024 * 8 * 1000 * 8; + return 3750000000U; else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) return 13 * 512 * 8 * 1000 * 8; else diff --git a/drivers/usb/gadget/function/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c index ed68a4860b7d..5a201ba7b155 100644 --- a/drivers/usb/gadget/function/f_sourcesink.c +++ b/drivers/usb/gadget/function/f_sourcesink.c @@ -559,6 +559,7 @@ static void source_sink_complete(struct usb_ep *ep, struct usb_request *req) #if 1 DBG(cdev, "%s complete --> %d, %d/%d\n", ep->name, status, req->actual, req->length); + break; #endif case -EREMOTEIO: /* short read */ break; diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index debf54205d22..5b5cfeb6c14a 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -897,8 +897,6 @@ EXPORT_SYMBOL_GPL(usb_gadget_unmap_request); * @ep: the endpoint to be used with with the request * @req: the request being given back * - * Context: in_interrupt() - * * This is called by device controller drivers in order to return the * completed request back to the gadget layer. */ diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index 53a227217f1c..ab5e978b5052 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -553,6 +553,7 @@ static int dummy_enable(struct usb_ep *_ep, /* we'll fake any legal size */ break; /* save a return statement */ + fallthrough; default: goto done; } @@ -595,6 +596,7 @@ static int dummy_enable(struct usb_ep *_ep, if (max <= 1023) break; /* save a return statement */ + fallthrough; default: goto done; } @@ -1754,8 +1756,10 @@ static int handle_control_request(struct dummy_hcd *dum_hcd, struct urb *urb, return ret_val; } -/* drive both sides of the transfers; looks like irq handlers to - * both drivers except the callbacks aren't in_irq(). +/* + * Drive both sides of the transfers; looks like irq handlers to both + * drivers except that the callbacks are invoked from soft interrupt + * context. */ static void dummy_timer(struct timer_list *t) { @@ -2734,7 +2738,7 @@ static int __init init(void) { int retval = -ENOMEM; int i; - struct dummy *dum[MAX_NUM_UDC]; + struct dummy *dum[MAX_NUM_UDC] = {}; if (usb_disabled()) return -ENODEV; diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c index cfaeca457fa7..ce57961dfd2d 100644 --- a/drivers/usb/gadget/udc/pxa27x_udc.c +++ b/drivers/usb/gadget/udc/pxa27x_udc.c @@ -304,7 +304,7 @@ static struct pxa_ep *find_pxa_ep(struct pxa_udc *udc, * update_pxa_ep_matches - update pxa_ep cached values in all udc_usb_ep * @udc: pxa udc * - * Context: in_interrupt() + * Context: interrupt handler * * Updates all pxa_ep fields in udc_usb_ep structures, if this field was * previously set up (and is not NULL). The update is necessary is a @@ -859,7 +859,7 @@ static int write_packet(struct pxa_ep *ep, struct pxa27x_request *req, * @ep: pxa physical endpoint * @req: usb request * - * Context: callable when in_interrupt() + * Context: interrupt handler * * Unload as many packets as possible from the fifo we use for usb OUT * transfers and put them into the request. Caller should have made sure @@ -997,7 +997,7 @@ static int read_ep0_fifo(struct pxa_ep *ep, struct pxa27x_request *req) * @ep: control endpoint * @req: request * - * Context: callable when in_interrupt() + * Context: interrupt handler * * Sends a request (or a part of the request) to the control endpoint (ep0 in). * If the request doesn't fit, the remaining part will be sent from irq. @@ -1036,8 +1036,8 @@ static int write_ep0_fifo(struct pxa_ep *ep, struct pxa27x_request *req) * @_req: usb request * @gfp_flags: flags * - * Context: normally called when !in_interrupt, but callable when in_interrupt() - * in the special case of ep0 setup : + * Context: thread context or from the interrupt handler in the + * special case of ep0 setup : * (irq->handle_ep0_ctrl_req->gadget_setup->pxa_ep_queue) * * Returns 0 if succedeed, error otherwise @@ -1512,7 +1512,8 @@ static int should_disable_udc(struct pxa_udc *udc) * pxa_udc_pullup - Offer manual D+ pullup control * @_gadget: usb gadget using the control * @is_active: 0 if disconnect, else connect D+ pullup resistor - * Context: !in_interrupt() + * + * Context: task context, might sleep * * Returns 0 if OK, -EOPNOTSUPP if udc driver doesn't handle D+ pullup */ @@ -1560,7 +1561,7 @@ static int pxa_udc_vbus_session(struct usb_gadget *_gadget, int is_active) * @_gadget: usb gadget * @mA: current drawn * - * Context: !in_interrupt() + * Context: task context, might sleep * * Called after a configuration was chosen by a USB host, to inform how much * current can be drawn by the device from VBus line. @@ -1886,7 +1887,7 @@ stall: * @fifo_irq: 1 if triggered by fifo service type irq * @opc_irq: 1 if triggered by output packet complete type irq * - * Context : when in_interrupt() or with ep->lock held + * Context : interrupt handler * * Tries to transfer all pending request data into the endpoint and/or * transfer all pending data in the endpoint into usb requests. @@ -2011,7 +2012,7 @@ static void handle_ep0(struct pxa_udc *udc, int fifo_irq, int opc_irq) * Tries to transfer all pending request data into the endpoint and/or * transfer all pending data in the endpoint into usb requests. * - * Is always called when in_interrupt() and with ep->lock released. + * Is always called from the interrupt handler. ep->lock must not be held. */ static void handle_ep(struct pxa_ep *ep) { diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index ab12c4bf0ef1..31e59309da1f 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -213,13 +213,6 @@ config USB_EHCI_FSL help Variation of ARC USB block used in some Freescale chips. -config USB_EHCI_MXC - tristate "Support for Freescale i.MX on-chip EHCI USB controller" - depends on ARCH_MXC || COMPILE_TEST - select USB_EHCI_ROOT_HUB_TT - help - Variation of ARC USB block used in some Freescale chips. - config USB_EHCI_HCD_NPCM7XX tristate "Support for Nuvoton NPCM7XX on-chip EHCI USB controller" depends on (USB_EHCI_HCD && ARCH_NPCM7XX) || COMPILE_TEST @@ -741,16 +734,6 @@ config USB_RENESAS_USBHS_HCD To compile this driver as a module, choose M here: the module will be called renesas-usbhs. -config USB_IMX21_HCD - tristate "i.MX21 HCD support" - depends on ARM && ARCH_MXC - help - This driver enables support for the on-chip USB host in the - i.MX21 processor. - - To compile this driver as a module, choose M here: the - module will be called "imx21-hcd". - config USB_HCD_BCMA tristate "BCMA usb host driver" depends on BCMA diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index bc731332fed9..c1b08703af10 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -40,7 +40,6 @@ obj-$(CONFIG_USB_PCI) += pci-quirks.o obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o obj-$(CONFIG_USB_EHCI_PCI) += ehci-pci.o obj-$(CONFIG_USB_EHCI_HCD_PLATFORM) += ehci-platform.o -obj-$(CONFIG_USB_EHCI_MXC) += ehci-mxc.o obj-$(CONFIG_USB_EHCI_HCD_NPCM7XX) += ehci-npcm7xx.o obj-$(CONFIG_USB_EHCI_HCD_OMAP) += ehci-omap.o obj-$(CONFIG_USB_EHCI_HCD_ORION) += ehci-orion.o @@ -81,7 +80,6 @@ obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o obj-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o -obj-$(CONFIG_USB_IMX21_HCD) += imx21-hcd.o obj-$(CONFIG_USB_FSL_USB2) += fsl-mph-dr-of.o obj-$(CONFIG_USB_EHCI_FSL) += fsl-mph-dr-of.o obj-$(CONFIG_USB_EHCI_FSL) += ehci-fsl.o diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 1e8b59ab2272..6f7bd6641694 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -39,10 +39,10 @@ static struct hc_driver __read_mostly fsl_ehci_hc_driver; /* * fsl_ehci_drv_probe - initialize FSL-based HCDs * @pdev: USB Host Controller being probed - * Context: !in_interrupt() * - * Allocates basic resources for this USB host controller. + * Context: task context, might sleep * + * Allocates basic resources for this USB host controller. */ static int fsl_ehci_drv_probe(struct platform_device *pdev) { @@ -684,12 +684,11 @@ static const struct ehci_driver_overrides ehci_fsl_overrides __initconst = { /** * fsl_ehci_drv_remove - shutdown processing for FSL-based HCDs * @pdev: USB Host Controller being removed - * Context: !in_interrupt() * - * Reverses the effect of usb_hcd_fsl_probe(). + * Context: task context, might sleep * + * Reverses the effect of usb_hcd_fsl_probe(). */ - static int fsl_ehci_drv_remove(struct platform_device *pdev) { struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev); diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 3575b7201881..e358ae17d51e 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -867,7 +867,7 @@ static int ehci_urb_enqueue ( */ if (urb->transfer_buffer_length > (16 * 1024)) return -EMSGSIZE; - /* FALLTHROUGH */ + fallthrough; /* case PIPE_BULK: */ default: if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags)) diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c deleted file mode 100644 index dc2676320527..000000000000 --- a/drivers/usb/host/ehci-mxc.c +++ /dev/null @@ -1,213 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (c) 2008 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix - * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de> - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/io.h> -#include <linux/platform_device.h> -#include <linux/clk.h> -#include <linux/delay.h> -#include <linux/usb/otg.h> -#include <linux/usb/ulpi.h> -#include <linux/slab.h> -#include <linux/usb.h> -#include <linux/usb/hcd.h> -#include <linux/platform_data/usb-ehci-mxc.h> -#include "ehci.h" - -#define DRIVER_DESC "Freescale On-Chip EHCI Host driver" - -static const char hcd_name[] = "ehci-mxc"; - -#define ULPI_VIEWPORT_OFFSET 0x170 - -struct ehci_mxc_priv { - struct clk *usbclk, *ahbclk, *phyclk; -}; - -static struct hc_driver __read_mostly ehci_mxc_hc_driver; - -static const struct ehci_driver_overrides ehci_mxc_overrides __initconst = { - .extra_priv_size = sizeof(struct ehci_mxc_priv), -}; - -static int ehci_mxc_drv_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct mxc_usbh_platform_data *pdata = dev_get_platdata(dev); - struct usb_hcd *hcd; - struct resource *res; - int irq, ret; - struct ehci_mxc_priv *priv; - struct ehci_hcd *ehci; - - if (!pdata) { - dev_err(dev, "No platform data given, bailing out.\n"); - return -EINVAL; - } - - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - - hcd = usb_create_hcd(&ehci_mxc_hc_driver, dev, dev_name(dev)); - if (!hcd) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hcd->regs = devm_ioremap_resource(dev, res); - if (IS_ERR(hcd->regs)) { - ret = PTR_ERR(hcd->regs); - goto err_alloc; - } - hcd->rsrc_start = res->start; - hcd->rsrc_len = resource_size(res); - - hcd->has_tt = 1; - ehci = hcd_to_ehci(hcd); - priv = (struct ehci_mxc_priv *) ehci->priv; - - /* enable clocks */ - priv->usbclk = devm_clk_get(dev, "ipg"); - if (IS_ERR(priv->usbclk)) { - ret = PTR_ERR(priv->usbclk); - goto err_alloc; - } - clk_prepare_enable(priv->usbclk); - - priv->ahbclk = devm_clk_get(dev, "ahb"); - if (IS_ERR(priv->ahbclk)) { - ret = PTR_ERR(priv->ahbclk); - goto err_clk_ahb; - } - clk_prepare_enable(priv->ahbclk); - - /* "dr" device has its own clock on i.MX51 */ - priv->phyclk = devm_clk_get(dev, "phy"); - if (IS_ERR(priv->phyclk)) - priv->phyclk = NULL; - if (priv->phyclk) - clk_prepare_enable(priv->phyclk); - - /* call platform specific init function */ - if (pdata->init) { - ret = pdata->init(pdev); - if (ret) { - dev_err(dev, "platform init failed\n"); - goto err_init; - } - /* platforms need some time to settle changed IO settings */ - mdelay(10); - } - - /* EHCI registers start at offset 0x100 */ - ehci->caps = hcd->regs + 0x100; - ehci->regs = hcd->regs + 0x100 + - HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); - - /* set up the PORTSCx register */ - ehci_writel(ehci, pdata->portsc, &ehci->regs->port_status[0]); - - /* is this really needed? */ - msleep(10); - - /* Initialize the transceiver */ - if (pdata->otg) { - pdata->otg->io_priv = hcd->regs + ULPI_VIEWPORT_OFFSET; - ret = usb_phy_init(pdata->otg); - if (ret) { - dev_err(dev, "unable to init transceiver, probably missing\n"); - ret = -ENODEV; - goto err_add; - } - ret = otg_set_vbus(pdata->otg->otg, 1); - if (ret) { - dev_err(dev, "unable to enable vbus on transceiver\n"); - goto err_add; - } - } - - platform_set_drvdata(pdev, hcd); - - ret = usb_add_hcd(hcd, irq, IRQF_SHARED); - if (ret) - goto err_add; - - device_wakeup_enable(hcd->self.controller); - return 0; - -err_add: - if (pdata && pdata->exit) - pdata->exit(pdev); -err_init: - if (priv->phyclk) - clk_disable_unprepare(priv->phyclk); - - clk_disable_unprepare(priv->ahbclk); -err_clk_ahb: - clk_disable_unprepare(priv->usbclk); -err_alloc: - usb_put_hcd(hcd); - return ret; -} - -static int ehci_mxc_drv_remove(struct platform_device *pdev) -{ - struct mxc_usbh_platform_data *pdata = dev_get_platdata(&pdev->dev); - struct usb_hcd *hcd = platform_get_drvdata(pdev); - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - struct ehci_mxc_priv *priv = (struct ehci_mxc_priv *) ehci->priv; - - usb_remove_hcd(hcd); - - if (pdata && pdata->exit) - pdata->exit(pdev); - - if (pdata && pdata->otg) - usb_phy_shutdown(pdata->otg); - - clk_disable_unprepare(priv->usbclk); - clk_disable_unprepare(priv->ahbclk); - - if (priv->phyclk) - clk_disable_unprepare(priv->phyclk); - - usb_put_hcd(hcd); - return 0; -} - -MODULE_ALIAS("platform:mxc-ehci"); - -static struct platform_driver ehci_mxc_driver = { - .probe = ehci_mxc_drv_probe, - .remove = ehci_mxc_drv_remove, - .shutdown = usb_hcd_platform_shutdown, - .driver = { - .name = "mxc-ehci", - }, -}; - -static int __init ehci_mxc_init(void) -{ - if (usb_disabled()) - return -ENODEV; - - pr_info("%s: " DRIVER_DESC "\n", hcd_name); - - ehci_init_driver(&ehci_mxc_hc_driver, &ehci_mxc_overrides); - return platform_driver_register(&ehci_mxc_driver); -} -module_init(ehci_mxc_init); - -static void __exit ehci_mxc_cleanup(void) -{ - platform_driver_unregister(&ehci_mxc_driver); -} -module_exit(ehci_mxc_cleanup); - -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_AUTHOR("Sascha Hauer"); -MODULE_LICENSE("GPL"); diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 8771a2ed6926..7f4a03e8647a 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -220,6 +220,7 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) err_pm_runtime: pm_runtime_put_sync(dev); + pm_runtime_disable(dev); err_phy: for (i = 0; i < omap->nports; i++) { diff --git a/drivers/usb/host/ehci-pmcmsp.c b/drivers/usb/host/ehci-pmcmsp.c index 2d462fbbe0a6..5fb92b956cc7 100644 --- a/drivers/usb/host/ehci-pmcmsp.c +++ b/drivers/usb/host/ehci-pmcmsp.c @@ -147,12 +147,14 @@ err1: /** * usb_hcd_msp_probe - initialize PMC MSP-based HCDs - * Context: !in_interrupt() + * @driver: Pointer to hc driver instance + * @dev: USB controller to probe + * + * Context: task context, might sleep * * Allocates basic resources for this USB host controller, and * then invokes the start() method for the HCD associated with it * through the hotplug entry's driver_data. - * */ int usb_hcd_msp_probe(const struct hc_driver *driver, struct platform_device *dev) @@ -223,8 +225,9 @@ err1: /** * usb_hcd_msp_remove - shutdown processing for PMC MSP-based HCDs - * @dev: USB Host Controller being removed - * Context: !in_interrupt() + * @hcd: USB Host Controller being removed + * + * Context: task context, might sleep * * Reverses the effect of usb_hcd_msp_probe(), first invoking * the HCD's stop() method. It is always called from a thread @@ -233,7 +236,7 @@ err1: * may be called without controller electrically present * may be called with controller, bus, and devices active */ -void usb_hcd_msp_remove(struct usb_hcd *hcd, struct platform_device *dev) +static void usb_hcd_msp_remove(struct usb_hcd *hcd) { usb_remove_hcd(hcd); iounmap(hcd->regs); @@ -306,7 +309,7 @@ static int ehci_hcd_msp_drv_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); - usb_hcd_msp_remove(hcd, pdev); + usb_hcd_msp_remove(hcd); /* free TWI GPIO USB_HOST_DEV pin */ gpio_free(MSP_PIN_USB0_HOST_DEV); diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 6dfb242f9a4b..0f85aa9b2fb1 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -244,6 +244,12 @@ static void reserve_release_intr_bandwidth(struct ehci_hcd *ehci, /* FS/LS bus bandwidth */ if (tt_usecs) { + /* + * find_tt() will not return any error here as we have + * already called find_tt() before calling this function + * and checked for any error return. The previous call + * would have created the data structure. + */ tt = find_tt(qh->ps.udev); if (sign > 0) list_add_tail(&qh->ps.ps_list, &tt->ps_list); @@ -1337,6 +1343,12 @@ static void reserve_release_iso_bandwidth(struct ehci_hcd *ehci, } } + /* + * find_tt() will not return any error here as we have + * already called find_tt() before calling this function + * and checked for any error return. The previous call + * would have created the data structure. + */ tt = find_tt(stream->ps.udev); if (sign > 0) list_add_tail(&stream->ps.ps_list, &tt->ps_list); diff --git a/drivers/usb/host/fotg210-hcd.c b/drivers/usb/host/fotg210-hcd.c index 1d94fcfac2c2..5617ef30530a 100644 --- a/drivers/usb/host/fotg210-hcd.c +++ b/drivers/usb/host/fotg210-hcd.c @@ -1951,7 +1951,7 @@ static int fotg210_mem_init(struct fotg210_hcd *fotg210, gfp_t flags) goto fail; /* Hardware periodic table */ - fotg210->periodic = (__le32 *) + fotg210->periodic = dma_alloc_coherent(fotg210_to_hcd(fotg210)->self.controller, fotg210->periodic_size * sizeof(__le32), &fotg210->periodic_dma, 0); @@ -5276,7 +5276,7 @@ static int fotg210_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, */ if (urb->transfer_buffer_length > (16 * 1024)) return -EMSGSIZE; - /* FALLTHROUGH */ + fallthrough; /* case PIPE_BULK: */ default: if (!qh_urb_transaction(fotg210, urb, &qtd_list, mem_flags)) diff --git a/drivers/usb/host/imx21-dbg.c b/drivers/usb/host/imx21-dbg.c deleted file mode 100644 index 02a1344fbd6a..000000000000 --- a/drivers/usb/host/imx21-dbg.c +++ /dev/null @@ -1,439 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (c) 2009 by Martin Fuzzey - */ - -/* this file is part of imx21-hcd.c */ - -#ifdef CONFIG_DYNAMIC_DEBUG -#define DEBUG -#endif - -#ifndef DEBUG - -static inline void create_debug_files(struct imx21 *imx21) { } -static inline void remove_debug_files(struct imx21 *imx21) { } -static inline void debug_urb_submitted(struct imx21 *imx21, struct urb *urb) {} -static inline void debug_urb_completed(struct imx21 *imx21, struct urb *urb, - int status) {} -static inline void debug_urb_unlinked(struct imx21 *imx21, struct urb *urb) {} -static inline void debug_urb_queued_for_etd(struct imx21 *imx21, - struct urb *urb) {} -static inline void debug_urb_queued_for_dmem(struct imx21 *imx21, - struct urb *urb) {} -static inline void debug_etd_allocated(struct imx21 *imx21) {} -static inline void debug_etd_freed(struct imx21 *imx21) {} -static inline void debug_dmem_allocated(struct imx21 *imx21, int size) {} -static inline void debug_dmem_freed(struct imx21 *imx21, int size) {} -static inline void debug_isoc_submitted(struct imx21 *imx21, - int frame, struct td *td) {} -static inline void debug_isoc_completed(struct imx21 *imx21, - int frame, struct td *td, int cc, int len) {} - -#else - -#include <linux/debugfs.h> -#include <linux/seq_file.h> - -static const char *dir_labels[] = { - "TD 0", - "OUT", - "IN", - "TD 1" -}; - -static const char *speed_labels[] = { - "Full", - "Low" -}; - -static const char *format_labels[] = { - "Control", - "ISO", - "Bulk", - "Interrupt" -}; - -static inline struct debug_stats *stats_for_urb(struct imx21 *imx21, - struct urb *urb) -{ - return usb_pipeisoc(urb->pipe) ? - &imx21->isoc_stats : &imx21->nonisoc_stats; -} - -static void debug_urb_submitted(struct imx21 *imx21, struct urb *urb) -{ - stats_for_urb(imx21, urb)->submitted++; -} - -static void debug_urb_completed(struct imx21 *imx21, struct urb *urb, int st) -{ - if (st) - stats_for_urb(imx21, urb)->completed_failed++; - else - stats_for_urb(imx21, urb)->completed_ok++; -} - -static void debug_urb_unlinked(struct imx21 *imx21, struct urb *urb) -{ - stats_for_urb(imx21, urb)->unlinked++; -} - -static void debug_urb_queued_for_etd(struct imx21 *imx21, struct urb *urb) -{ - stats_for_urb(imx21, urb)->queue_etd++; -} - -static void debug_urb_queued_for_dmem(struct imx21 *imx21, struct urb *urb) -{ - stats_for_urb(imx21, urb)->queue_dmem++; -} - -static inline void debug_etd_allocated(struct imx21 *imx21) -{ - imx21->etd_usage.maximum = max( - ++(imx21->etd_usage.value), - imx21->etd_usage.maximum); -} - -static inline void debug_etd_freed(struct imx21 *imx21) -{ - imx21->etd_usage.value--; -} - -static inline void debug_dmem_allocated(struct imx21 *imx21, int size) -{ - imx21->dmem_usage.value += size; - imx21->dmem_usage.maximum = max( - imx21->dmem_usage.value, - imx21->dmem_usage.maximum); -} - -static inline void debug_dmem_freed(struct imx21 *imx21, int size) -{ - imx21->dmem_usage.value -= size; -} - - -static void debug_isoc_submitted(struct imx21 *imx21, - int frame, struct td *td) -{ - struct debug_isoc_trace *trace = &imx21->isoc_trace[ - imx21->isoc_trace_index++]; - - imx21->isoc_trace_index %= ARRAY_SIZE(imx21->isoc_trace); - trace->schedule_frame = td->frame; - trace->submit_frame = frame; - trace->request_len = td->len; - trace->td = td; -} - -static inline void debug_isoc_completed(struct imx21 *imx21, - int frame, struct td *td, int cc, int len) -{ - struct debug_isoc_trace *trace, *trace_failed; - int i; - int found = 0; - - trace = imx21->isoc_trace; - for (i = 0; i < ARRAY_SIZE(imx21->isoc_trace); i++, trace++) { - if (trace->td == td) { - trace->done_frame = frame; - trace->done_len = len; - trace->cc = cc; - trace->td = NULL; - found = 1; - break; - } - } - - if (found && cc) { - trace_failed = &imx21->isoc_trace_failed[ - imx21->isoc_trace_index_failed++]; - - imx21->isoc_trace_index_failed %= ARRAY_SIZE( - imx21->isoc_trace_failed); - *trace_failed = *trace; - } -} - - -static char *format_ep(struct usb_host_endpoint *ep, char *buf, int bufsize) -{ - if (ep) - snprintf(buf, bufsize, "ep_%02x (type:%02X kaddr:%p)", - ep->desc.bEndpointAddress, - usb_endpoint_type(&ep->desc), - ep); - else - snprintf(buf, bufsize, "none"); - return buf; -} - -static char *format_etd_dword0(u32 value, char *buf, int bufsize) -{ - snprintf(buf, bufsize, - "addr=%d ep=%d dir=%s speed=%s format=%s halted=%d", - value & 0x7F, - (value >> DW0_ENDPNT) & 0x0F, - dir_labels[(value >> DW0_DIRECT) & 0x03], - speed_labels[(value >> DW0_SPEED) & 0x01], - format_labels[(value >> DW0_FORMAT) & 0x03], - (value >> DW0_HALTED) & 0x01); - return buf; -} - -static int debug_status_show(struct seq_file *s, void *v) -{ - struct imx21 *imx21 = s->private; - int etds_allocated = 0; - int etds_sw_busy = 0; - int etds_hw_busy = 0; - int dmem_blocks = 0; - int queued_for_etd = 0; - int queued_for_dmem = 0; - unsigned int dmem_bytes = 0; - int i; - struct etd_priv *etd; - u32 etd_enable_mask; - unsigned long flags; - struct imx21_dmem_area *dmem; - struct ep_priv *ep_priv; - - spin_lock_irqsave(&imx21->lock, flags); - - etd_enable_mask = readl(imx21->regs + USBH_ETDENSET); - for (i = 0, etd = imx21->etd; i < USB_NUM_ETD; i++, etd++) { - if (etd->alloc) - etds_allocated++; - if (etd->urb) - etds_sw_busy++; - if (etd_enable_mask & (1<<i)) - etds_hw_busy++; - } - - list_for_each_entry(dmem, &imx21->dmem_list, list) { - dmem_bytes += dmem->size; - dmem_blocks++; - } - - list_for_each_entry(ep_priv, &imx21->queue_for_etd, queue) - queued_for_etd++; - - list_for_each_entry(etd, &imx21->queue_for_dmem, queue) - queued_for_dmem++; - - spin_unlock_irqrestore(&imx21->lock, flags); - - seq_printf(s, - "Frame: %d\n" - "ETDs allocated: %d/%d (max=%d)\n" - "ETDs in use sw: %d\n" - "ETDs in use hw: %d\n" - "DMEM allocated: %d/%d (max=%d)\n" - "DMEM blocks: %d\n" - "Queued waiting for ETD: %d\n" - "Queued waiting for DMEM: %d\n", - readl(imx21->regs + USBH_FRMNUB) & 0xFFFF, - etds_allocated, USB_NUM_ETD, imx21->etd_usage.maximum, - etds_sw_busy, - etds_hw_busy, - dmem_bytes, DMEM_SIZE, imx21->dmem_usage.maximum, - dmem_blocks, - queued_for_etd, - queued_for_dmem); - - return 0; -} -DEFINE_SHOW_ATTRIBUTE(debug_status); - -static int debug_dmem_show(struct seq_file *s, void *v) -{ - struct imx21 *imx21 = s->private; - struct imx21_dmem_area *dmem; - unsigned long flags; - char ep_text[40]; - - spin_lock_irqsave(&imx21->lock, flags); - - list_for_each_entry(dmem, &imx21->dmem_list, list) - seq_printf(s, - "%04X: size=0x%X " - "ep=%s\n", - dmem->offset, dmem->size, - format_ep(dmem->ep, ep_text, sizeof(ep_text))); - - spin_unlock_irqrestore(&imx21->lock, flags); - - return 0; -} -DEFINE_SHOW_ATTRIBUTE(debug_dmem); - -static int debug_etd_show(struct seq_file *s, void *v) -{ - struct imx21 *imx21 = s->private; - struct etd_priv *etd; - char buf[60]; - u32 dword; - int i, j; - unsigned long flags; - - spin_lock_irqsave(&imx21->lock, flags); - - for (i = 0, etd = imx21->etd; i < USB_NUM_ETD; i++, etd++) { - int state = -1; - struct urb_priv *urb_priv; - if (etd->urb) { - urb_priv = etd->urb->hcpriv; - if (urb_priv) - state = urb_priv->state; - } - - seq_printf(s, - "etd_num: %d\n" - "ep: %s\n" - "alloc: %d\n" - "len: %d\n" - "busy sw: %d\n" - "busy hw: %d\n" - "urb state: %d\n" - "current urb: %p\n", - - i, - format_ep(etd->ep, buf, sizeof(buf)), - etd->alloc, - etd->len, - etd->urb != NULL, - (readl(imx21->regs + USBH_ETDENSET) & (1 << i)) > 0, - state, - etd->urb); - - for (j = 0; j < 4; j++) { - dword = etd_readl(imx21, i, j); - switch (j) { - case 0: - format_etd_dword0(dword, buf, sizeof(buf)); - break; - case 2: - snprintf(buf, sizeof(buf), - "cc=0X%02X", dword >> DW2_COMPCODE); - break; - default: - *buf = 0; - break; - } - seq_printf(s, - "dword %d: submitted=%08X cur=%08X [%s]\n", - j, - etd->submitted_dwords[j], - dword, - buf); - } - seq_printf(s, "\n"); - } - - spin_unlock_irqrestore(&imx21->lock, flags); - - return 0; -} -DEFINE_SHOW_ATTRIBUTE(debug_etd); - -static void debug_statistics_show_one(struct seq_file *s, - const char *name, struct debug_stats *stats) -{ - seq_printf(s, "%s:\n" - "submitted URBs: %lu\n" - "completed OK: %lu\n" - "completed failed: %lu\n" - "unlinked: %lu\n" - "queued for ETD: %lu\n" - "queued for DMEM: %lu\n\n", - name, - stats->submitted, - stats->completed_ok, - stats->completed_failed, - stats->unlinked, - stats->queue_etd, - stats->queue_dmem); -} - -static int debug_statistics_show(struct seq_file *s, void *v) -{ - struct imx21 *imx21 = s->private; - unsigned long flags; - - spin_lock_irqsave(&imx21->lock, flags); - - debug_statistics_show_one(s, "nonisoc", &imx21->nonisoc_stats); - debug_statistics_show_one(s, "isoc", &imx21->isoc_stats); - seq_printf(s, "unblock kludge triggers: %lu\n", imx21->debug_unblocks); - spin_unlock_irqrestore(&imx21->lock, flags); - - return 0; -} -DEFINE_SHOW_ATTRIBUTE(debug_statistics); - -static void debug_isoc_show_one(struct seq_file *s, - const char *name, int index, struct debug_isoc_trace *trace) -{ - seq_printf(s, "%s %d:\n" - "cc=0X%02X\n" - "scheduled frame %d (%d)\n" - "submitted frame %d (%d)\n" - "completed frame %d (%d)\n" - "requested length=%d\n" - "completed length=%d\n\n", - name, index, - trace->cc, - trace->schedule_frame, trace->schedule_frame & 0xFFFF, - trace->submit_frame, trace->submit_frame & 0xFFFF, - trace->done_frame, trace->done_frame & 0xFFFF, - trace->request_len, - trace->done_len); -} - -static int debug_isoc_show(struct seq_file *s, void *v) -{ - struct imx21 *imx21 = s->private; - struct debug_isoc_trace *trace; - unsigned long flags; - int i; - - spin_lock_irqsave(&imx21->lock, flags); - - trace = imx21->isoc_trace_failed; - for (i = 0; i < ARRAY_SIZE(imx21->isoc_trace_failed); i++, trace++) - debug_isoc_show_one(s, "isoc failed", i, trace); - - trace = imx21->isoc_trace; - for (i = 0; i < ARRAY_SIZE(imx21->isoc_trace); i++, trace++) - debug_isoc_show_one(s, "isoc", i, trace); - - spin_unlock_irqrestore(&imx21->lock, flags); - - return 0; -} -DEFINE_SHOW_ATTRIBUTE(debug_isoc); - -static void create_debug_files(struct imx21 *imx21) -{ - struct dentry *root; - - root = debugfs_create_dir(dev_name(imx21->dev), usb_debug_root); - imx21->debug_root = root; - - debugfs_create_file("status", S_IRUGO, root, imx21, &debug_status_fops); - debugfs_create_file("dmem", S_IRUGO, root, imx21, &debug_dmem_fops); - debugfs_create_file("etd", S_IRUGO, root, imx21, &debug_etd_fops); - debugfs_create_file("statistics", S_IRUGO, root, imx21, - &debug_statistics_fops); - debugfs_create_file("isoc", S_IRUGO, root, imx21, &debug_isoc_fops); -} - -static void remove_debug_files(struct imx21 *imx21) -{ - debugfs_remove_recursive(imx21->debug_root); -} - -#endif - diff --git a/drivers/usb/host/imx21-hcd.c b/drivers/usb/host/imx21-hcd.c deleted file mode 100644 index b2716cb72471..000000000000 --- a/drivers/usb/host/imx21-hcd.c +++ /dev/null @@ -1,1933 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * USB Host Controller Driver for IMX21 - * - * Copyright (C) 2006 Loping Dog Embedded Systems - * Copyright (C) 2009 Martin Fuzzey - * Originally written by Jay Monkman <jtm@lopingdog.com> - * Ported to 2.6.30, debugged and enhanced by Martin Fuzzey - */ - - - /* - * The i.MX21 USB hardware contains - * * 32 transfer descriptors (called ETDs) - * * 4Kb of Data memory - * - * The data memory is shared between the host and function controllers - * (but this driver only supports the host controller) - * - * So setting up a transfer involves: - * * Allocating a ETD - * * Fill in ETD with appropriate information - * * Allocating data memory (and putting the offset in the ETD) - * * Activate the ETD - * * Get interrupt when done. - * - * An ETD is assigned to each active endpoint. - * - * Low resource (ETD and Data memory) situations are handled differently for - * isochronous and non insosynchronous transactions : - * - * Non ISOC transfers are queued if either ETDs or Data memory are unavailable - * - * ISOC transfers use 2 ETDs per endpoint to achieve double buffering. - * They allocate both ETDs and Data memory during URB submission - * (and fail if unavailable). - */ - -#include <linux/clk.h> -#include <linux/io.h> -#include <linux/kernel.h> -#include <linux/list.h> -#include <linux/platform_device.h> -#include <linux/slab.h> -#include <linux/usb.h> -#include <linux/usb/hcd.h> -#include <linux/dma-mapping.h> -#include <linux/module.h> - -#include "imx21-hcd.h" - -#ifdef CONFIG_DYNAMIC_DEBUG -#define DEBUG -#endif - -#ifdef DEBUG -#define DEBUG_LOG_FRAME(imx21, etd, event) \ - (etd)->event##_frame = readl((imx21)->regs + USBH_FRMNUB) -#else -#define DEBUG_LOG_FRAME(imx21, etd, event) do { } while (0) -#endif - -static const char hcd_name[] = "imx21-hcd"; - -static inline struct imx21 *hcd_to_imx21(struct usb_hcd *hcd) -{ - return (struct imx21 *)hcd->hcd_priv; -} - - -/* =========================================== */ -/* Hardware access helpers */ -/* =========================================== */ - -static inline void set_register_bits(struct imx21 *imx21, u32 offset, u32 mask) -{ - void __iomem *reg = imx21->regs + offset; - writel(readl(reg) | mask, reg); -} - -static inline void clear_register_bits(struct imx21 *imx21, - u32 offset, u32 mask) -{ - void __iomem *reg = imx21->regs + offset; - writel(readl(reg) & ~mask, reg); -} - -static inline void clear_toggle_bit(struct imx21 *imx21, u32 offset, u32 mask) -{ - void __iomem *reg = imx21->regs + offset; - - if (readl(reg) & mask) - writel(mask, reg); -} - -static inline void set_toggle_bit(struct imx21 *imx21, u32 offset, u32 mask) -{ - void __iomem *reg = imx21->regs + offset; - - if (!(readl(reg) & mask)) - writel(mask, reg); -} - -static void etd_writel(struct imx21 *imx21, int etd_num, int dword, u32 value) -{ - writel(value, imx21->regs + USB_ETD_DWORD(etd_num, dword)); -} - -static u32 etd_readl(struct imx21 *imx21, int etd_num, int dword) -{ - return readl(imx21->regs + USB_ETD_DWORD(etd_num, dword)); -} - -static inline int wrap_frame(int counter) -{ - return counter & 0xFFFF; -} - -static inline int frame_after(int frame, int after) -{ - /* handle wrapping like jiffies time_afer */ - return (s16)((s16)after - (s16)frame) < 0; -} - -static int imx21_hc_get_frame(struct usb_hcd *hcd) -{ - struct imx21 *imx21 = hcd_to_imx21(hcd); - - return wrap_frame(readl(imx21->regs + USBH_FRMNUB)); -} - -static inline bool unsuitable_for_dma(dma_addr_t addr) -{ - return (addr & 3) != 0; -} - -#include "imx21-dbg.c" - -static void nonisoc_urb_completed_for_etd( - struct imx21 *imx21, struct etd_priv *etd, int status); -static void schedule_nonisoc_etd(struct imx21 *imx21, struct urb *urb); -static void free_dmem(struct imx21 *imx21, struct etd_priv *etd); - -/* =========================================== */ -/* ETD management */ -/* =========================================== */ - -static int alloc_etd(struct imx21 *imx21) -{ - int i; - struct etd_priv *etd = imx21->etd; - - for (i = 0; i < USB_NUM_ETD; i++, etd++) { - if (etd->alloc == 0) { - memset(etd, 0, sizeof(imx21->etd[0])); - etd->alloc = 1; - debug_etd_allocated(imx21); - return i; - } - } - return -1; -} - -static void disactivate_etd(struct imx21 *imx21, int num) -{ - int etd_mask = (1 << num); - struct etd_priv *etd = &imx21->etd[num]; - - writel(etd_mask, imx21->regs + USBH_ETDENCLR); - clear_register_bits(imx21, USBH_ETDDONEEN, etd_mask); - writel(etd_mask, imx21->regs + USB_ETDDMACHANLCLR); - clear_toggle_bit(imx21, USBH_ETDDONESTAT, etd_mask); - - etd->active_count = 0; - - DEBUG_LOG_FRAME(imx21, etd, disactivated); -} - -static void reset_etd(struct imx21 *imx21, int num) -{ - struct etd_priv *etd = imx21->etd + num; - int i; - - disactivate_etd(imx21, num); - - for (i = 0; i < 4; i++) - etd_writel(imx21, num, i, 0); - etd->urb = NULL; - etd->ep = NULL; - etd->td = NULL; - etd->bounce_buffer = NULL; -} - -static void free_etd(struct imx21 *imx21, int num) -{ - if (num < 0) - return; - - if (num >= USB_NUM_ETD) { - dev_err(imx21->dev, "BAD etd=%d!\n", num); - return; - } - if (imx21->etd[num].alloc == 0) { - dev_err(imx21->dev, "ETD %d already free!\n", num); - return; - } - - debug_etd_freed(imx21); - reset_etd(imx21, num); - memset(&imx21->etd[num], 0, sizeof(imx21->etd[0])); -} - - -static void setup_etd_dword0(struct imx21 *imx21, - int etd_num, struct urb *urb, u8 dir, u16 maxpacket) -{ - etd_writel(imx21, etd_num, 0, - ((u32) usb_pipedevice(urb->pipe)) << DW0_ADDRESS | - ((u32) usb_pipeendpoint(urb->pipe) << DW0_ENDPNT) | - ((u32) dir << DW0_DIRECT) | - ((u32) ((urb->dev->speed == USB_SPEED_LOW) ? - 1 : 0) << DW0_SPEED) | - ((u32) fmt_urb_to_etd[usb_pipetype(urb->pipe)] << DW0_FORMAT) | - ((u32) maxpacket << DW0_MAXPKTSIZ)); -} - -/* - * Copy buffer to data controller data memory. - * We cannot use memcpy_toio() because the hardware requires 32bit writes - */ -static void copy_to_dmem( - struct imx21 *imx21, int dmem_offset, void *src, int count) -{ - void __iomem *dmem = imx21->regs + USBOTG_DMEM + dmem_offset; - u32 word = 0; - u8 *p = src; - int byte = 0; - int i; - - for (i = 0; i < count; i++) { - byte = i % 4; - word += (*p++ << (byte * 8)); - if (byte == 3) { - writel(word, dmem); - dmem += 4; - word = 0; - } - } - - if (count && byte != 3) - writel(word, dmem); -} - -static void activate_etd(struct imx21 *imx21, int etd_num, u8 dir) -{ - u32 etd_mask = 1 << etd_num; - struct etd_priv *etd = &imx21->etd[etd_num]; - - if (etd->dma_handle && unsuitable_for_dma(etd->dma_handle)) { - /* For non aligned isoc the condition below is always true */ - if (etd->len <= etd->dmem_size) { - /* Fits into data memory, use PIO */ - if (dir != TD_DIR_IN) { - copy_to_dmem(imx21, - etd->dmem_offset, - etd->cpu_buffer, etd->len); - } - etd->dma_handle = 0; - - } else { - /* Too big for data memory, use bounce buffer */ - enum dma_data_direction dmadir; - - if (dir == TD_DIR_IN) { - dmadir = DMA_FROM_DEVICE; - etd->bounce_buffer = kmalloc(etd->len, - GFP_ATOMIC); - } else { - dmadir = DMA_TO_DEVICE; - etd->bounce_buffer = kmemdup(etd->cpu_buffer, - etd->len, - GFP_ATOMIC); - } - if (!etd->bounce_buffer) { - dev_err(imx21->dev, "failed bounce alloc\n"); - goto err_bounce_alloc; - } - - etd->dma_handle = - dma_map_single(imx21->dev, - etd->bounce_buffer, - etd->len, - dmadir); - if (dma_mapping_error(imx21->dev, etd->dma_handle)) { - dev_err(imx21->dev, "failed bounce map\n"); - goto err_bounce_map; - } - } - } - - clear_toggle_bit(imx21, USBH_ETDDONESTAT, etd_mask); - set_register_bits(imx21, USBH_ETDDONEEN, etd_mask); - clear_toggle_bit(imx21, USBH_XFILLSTAT, etd_mask); - clear_toggle_bit(imx21, USBH_YFILLSTAT, etd_mask); - - if (etd->dma_handle) { - set_register_bits(imx21, USB_ETDDMACHANLCLR, etd_mask); - clear_toggle_bit(imx21, USBH_XBUFSTAT, etd_mask); - clear_toggle_bit(imx21, USBH_YBUFSTAT, etd_mask); - writel(etd->dma_handle, imx21->regs + USB_ETDSMSA(etd_num)); - set_register_bits(imx21, USB_ETDDMAEN, etd_mask); - } else { - if (dir != TD_DIR_IN) { - /* need to set for ZLP and PIO */ - set_toggle_bit(imx21, USBH_XFILLSTAT, etd_mask); - set_toggle_bit(imx21, USBH_YFILLSTAT, etd_mask); - } - } - - DEBUG_LOG_FRAME(imx21, etd, activated); - -#ifdef DEBUG - if (!etd->active_count) { - int i; - etd->activated_frame = readl(imx21->regs + USBH_FRMNUB); - etd->disactivated_frame = -1; - etd->last_int_frame = -1; - etd->last_req_frame = -1; - - for (i = 0; i < 4; i++) - etd->submitted_dwords[i] = etd_readl(imx21, etd_num, i); - } -#endif - - etd->active_count = 1; - writel(etd_mask, imx21->regs + USBH_ETDENSET); - return; - -err_bounce_map: - kfree(etd->bounce_buffer); - -err_bounce_alloc: - free_dmem(imx21, etd); - nonisoc_urb_completed_for_etd(imx21, etd, -ENOMEM); -} - -/* =========================================== */ -/* Data memory management */ -/* =========================================== */ - -static int alloc_dmem(struct imx21 *imx21, unsigned int size, - struct usb_host_endpoint *ep) -{ - unsigned int offset = 0; - struct imx21_dmem_area *area; - struct imx21_dmem_area *tmp; - - size += (~size + 1) & 0x3; /* Round to 4 byte multiple */ - - if (size > DMEM_SIZE) { - dev_err(imx21->dev, "size=%d > DMEM_SIZE(%d)\n", - size, DMEM_SIZE); - return -EINVAL; - } - - list_for_each_entry(tmp, &imx21->dmem_list, list) { - if ((size + offset) < offset) - goto fail; - if ((size + offset) <= tmp->offset) - break; - offset = tmp->size + tmp->offset; - if ((offset + size) > DMEM_SIZE) - goto fail; - } - - area = kmalloc(sizeof(struct imx21_dmem_area), GFP_ATOMIC); - if (area == NULL) - return -ENOMEM; - - area->ep = ep; - area->offset = offset; - area->size = size; - list_add_tail(&area->list, &tmp->list); - debug_dmem_allocated(imx21, size); - return offset; - -fail: - return -ENOMEM; -} - -/* Memory now available for a queued ETD - activate it */ -static void activate_queued_etd(struct imx21 *imx21, - struct etd_priv *etd, u32 dmem_offset) -{ - struct urb_priv *urb_priv = etd->urb->hcpriv; - int etd_num = etd - &imx21->etd[0]; - u32 maxpacket = etd_readl(imx21, etd_num, 1) >> DW1_YBUFSRTAD; - u8 dir = (etd_readl(imx21, etd_num, 2) >> DW2_DIRPID) & 0x03; - - dev_dbg(imx21->dev, "activating queued ETD %d now DMEM available\n", - etd_num); - etd_writel(imx21, etd_num, 1, - ((dmem_offset + maxpacket) << DW1_YBUFSRTAD) | dmem_offset); - - etd->dmem_offset = dmem_offset; - urb_priv->active = 1; - activate_etd(imx21, etd_num, dir); -} - -static void free_dmem(struct imx21 *imx21, struct etd_priv *etd) -{ - struct imx21_dmem_area *area; - struct etd_priv *tmp; - int found = 0; - int offset; - - if (!etd->dmem_size) - return; - etd->dmem_size = 0; - - offset = etd->dmem_offset; - list_for_each_entry(area, &imx21->dmem_list, list) { - if (area->offset == offset) { - debug_dmem_freed(imx21, area->size); - list_del(&area->list); - kfree(area); - found = 1; - break; - } - } - - if (!found) { - dev_err(imx21->dev, - "Trying to free unallocated DMEM %d\n", offset); - return; - } - - /* Try again to allocate memory for anything we've queued */ - list_for_each_entry_safe(etd, tmp, &imx21->queue_for_dmem, queue) { - offset = alloc_dmem(imx21, etd->dmem_size, etd->ep); - if (offset >= 0) { - list_del(&etd->queue); - activate_queued_etd(imx21, etd, (u32)offset); - } - } -} - -static void free_epdmem(struct imx21 *imx21, struct usb_host_endpoint *ep) -{ - struct imx21_dmem_area *area, *tmp; - - list_for_each_entry_safe(area, tmp, &imx21->dmem_list, list) { - if (area->ep == ep) { - dev_err(imx21->dev, - "Active DMEM %d for disabled ep=%p\n", - area->offset, ep); - list_del(&area->list); - kfree(area); - } - } -} - - -/* =========================================== */ -/* End handling */ -/* =========================================== */ - -/* Endpoint now idle - release its ETD(s) or assign to queued request */ -static void ep_idle(struct imx21 *imx21, struct ep_priv *ep_priv) -{ - int i; - - for (i = 0; i < NUM_ISO_ETDS; i++) { - int etd_num = ep_priv->etd[i]; - struct etd_priv *etd; - if (etd_num < 0) - continue; - - etd = &imx21->etd[etd_num]; - ep_priv->etd[i] = -1; - - free_dmem(imx21, etd); /* for isoc */ - - if (list_empty(&imx21->queue_for_etd)) { - free_etd(imx21, etd_num); - continue; - } - - dev_dbg(imx21->dev, - "assigning idle etd %d for queued request\n", etd_num); - ep_priv = list_first_entry(&imx21->queue_for_etd, - struct ep_priv, queue); - list_del(&ep_priv->queue); - reset_etd(imx21, etd_num); - ep_priv->waiting_etd = 0; - ep_priv->etd[i] = etd_num; - - if (list_empty(&ep_priv->ep->urb_list)) { - dev_err(imx21->dev, "No urb for queued ep!\n"); - continue; - } - schedule_nonisoc_etd(imx21, list_first_entry( - &ep_priv->ep->urb_list, struct urb, urb_list)); - } -} - -static void urb_done(struct usb_hcd *hcd, struct urb *urb, int status) -__releases(imx21->lock) -__acquires(imx21->lock) -{ - struct imx21 *imx21 = hcd_to_imx21(hcd); - struct ep_priv *ep_priv = urb->ep->hcpriv; - struct urb_priv *urb_priv = urb->hcpriv; - - debug_urb_completed(imx21, urb, status); - dev_vdbg(imx21->dev, "urb %p done %d\n", urb, status); - - kfree(urb_priv->isoc_td); - kfree(urb->hcpriv); - urb->hcpriv = NULL; - usb_hcd_unlink_urb_from_ep(hcd, urb); - spin_unlock(&imx21->lock); - usb_hcd_giveback_urb(hcd, urb, status); - spin_lock(&imx21->lock); - if (list_empty(&ep_priv->ep->urb_list)) - ep_idle(imx21, ep_priv); -} - -static void nonisoc_urb_completed_for_etd( - struct imx21 *imx21, struct etd_priv *etd, int status) -{ - struct usb_host_endpoint *ep = etd->ep; - - urb_done(imx21->hcd, etd->urb, status); - etd->urb = NULL; - - if (!list_empty(&ep->urb_list)) { - struct urb *urb = list_first_entry( - &ep->urb_list, struct urb, urb_list); - - dev_vdbg(imx21->dev, "next URB %p\n", urb); - schedule_nonisoc_etd(imx21, urb); - } -} - - -/* =========================================== */ -/* ISOC Handling ... */ -/* =========================================== */ - -static void schedule_isoc_etds(struct usb_hcd *hcd, - struct usb_host_endpoint *ep) -{ - struct imx21 *imx21 = hcd_to_imx21(hcd); - struct ep_priv *ep_priv = ep->hcpriv; - struct etd_priv *etd; - struct urb_priv *urb_priv; - struct td *td; - int etd_num; - int i; - int cur_frame; - u8 dir; - - for (i = 0; i < NUM_ISO_ETDS; i++) { -too_late: - if (list_empty(&ep_priv->td_list)) - break; - - etd_num = ep_priv->etd[i]; - if (etd_num < 0) - break; - - etd = &imx21->etd[etd_num]; - if (etd->urb) - continue; - - td = list_entry(ep_priv->td_list.next, struct td, list); - list_del(&td->list); - urb_priv = td->urb->hcpriv; - - cur_frame = imx21_hc_get_frame(hcd); - if (frame_after(cur_frame, td->frame)) { - dev_dbg(imx21->dev, "isoc too late frame %d > %d\n", - cur_frame, td->frame); - urb_priv->isoc_status = -EXDEV; - td->urb->iso_frame_desc[ - td->isoc_index].actual_length = 0; - td->urb->iso_frame_desc[td->isoc_index].status = -EXDEV; - if (--urb_priv->isoc_remaining == 0) - urb_done(hcd, td->urb, urb_priv->isoc_status); - goto too_late; - } - - urb_priv->active = 1; - etd->td = td; - etd->ep = td->ep; - etd->urb = td->urb; - etd->len = td->len; - etd->dma_handle = td->dma_handle; - etd->cpu_buffer = td->cpu_buffer; - - debug_isoc_submitted(imx21, cur_frame, td); - - dir = usb_pipeout(td->urb->pipe) ? TD_DIR_OUT : TD_DIR_IN; - setup_etd_dword0(imx21, etd_num, td->urb, dir, etd->dmem_size); - etd_writel(imx21, etd_num, 1, etd->dmem_offset); - etd_writel(imx21, etd_num, 2, - (TD_NOTACCESSED << DW2_COMPCODE) | - ((td->frame & 0xFFFF) << DW2_STARTFRM)); - etd_writel(imx21, etd_num, 3, - (TD_NOTACCESSED << DW3_COMPCODE0) | - (td->len << DW3_PKTLEN0)); - - activate_etd(imx21, etd_num, dir); - } -} - -static void isoc_etd_done(struct usb_hcd *hcd, int etd_num) -{ - struct imx21 *imx21 = hcd_to_imx21(hcd); - int etd_mask = 1 << etd_num; - struct etd_priv *etd = imx21->etd + etd_num; - struct urb *urb = etd->urb; - struct urb_priv *urb_priv = urb->hcpriv; - struct td *td = etd->td; - struct usb_host_endpoint *ep = etd->ep; - int isoc_index = td->isoc_index; - unsigned int pipe = urb->pipe; - int dir_in = usb_pipein(pipe); - int cc; - int bytes_xfrd; - - disactivate_etd(imx21, etd_num); - - cc = (etd_readl(imx21, etd_num, 3) >> DW3_COMPCODE0) & 0xf; - bytes_xfrd = etd_readl(imx21, etd_num, 3) & 0x3ff; - - /* Input doesn't always fill the buffer, don't generate an error - * when this happens. - */ - if (dir_in && (cc == TD_DATAUNDERRUN)) - cc = TD_CC_NOERROR; - - if (cc == TD_NOTACCESSED) - bytes_xfrd = 0; - - debug_isoc_completed(imx21, - imx21_hc_get_frame(hcd), td, cc, bytes_xfrd); - if (cc) { - urb_priv->isoc_status = -EXDEV; - dev_dbg(imx21->dev, - "bad iso cc=0x%X frame=%d sched frame=%d " - "cnt=%d len=%d urb=%p etd=%d index=%d\n", - cc, imx21_hc_get_frame(hcd), td->frame, - bytes_xfrd, td->len, urb, etd_num, isoc_index); - } - - if (dir_in) { - clear_toggle_bit(imx21, USBH_XFILLSTAT, etd_mask); - if (!etd->dma_handle) - memcpy_fromio(etd->cpu_buffer, - imx21->regs + USBOTG_DMEM + etd->dmem_offset, - bytes_xfrd); - } - - urb->actual_length += bytes_xfrd; - urb->iso_frame_desc[isoc_index].actual_length = bytes_xfrd; - urb->iso_frame_desc[isoc_index].status = cc_to_error[cc]; - - etd->td = NULL; - etd->urb = NULL; - etd->ep = NULL; - - if (--urb_priv->isoc_remaining == 0) - urb_done(hcd, urb, urb_priv->isoc_status); - - schedule_isoc_etds(hcd, ep); -} - -static struct ep_priv *alloc_isoc_ep( - struct imx21 *imx21, struct usb_host_endpoint *ep) -{ - struct ep_priv *ep_priv; - int i; - - ep_priv = kzalloc(sizeof(struct ep_priv), GFP_ATOMIC); - if (!ep_priv) - return NULL; - - for (i = 0; i < NUM_ISO_ETDS; i++) - ep_priv->etd[i] = -1; - - INIT_LIST_HEAD(&ep_priv->td_list); - ep_priv->ep = ep; - ep->hcpriv = ep_priv; - return ep_priv; -} - -static int alloc_isoc_etds(struct imx21 *imx21, struct ep_priv *ep_priv) -{ - int i, j; - int etd_num; - - /* Allocate the ETDs if required */ - for (i = 0; i < NUM_ISO_ETDS; i++) { - if (ep_priv->etd[i] < 0) { - etd_num = alloc_etd(imx21); - if (etd_num < 0) - goto alloc_etd_failed; - - ep_priv->etd[i] = etd_num; - imx21->etd[etd_num].ep = ep_priv->ep; - } - } - return 0; - -alloc_etd_failed: - dev_err(imx21->dev, "isoc: Couldn't allocate etd\n"); - for (j = 0; j < i; j++) { - free_etd(imx21, ep_priv->etd[j]); - ep_priv->etd[j] = -1; - } - return -ENOMEM; -} - -static int imx21_hc_urb_enqueue_isoc(struct usb_hcd *hcd, - struct usb_host_endpoint *ep, - struct urb *urb, gfp_t mem_flags) -{ - struct imx21 *imx21 = hcd_to_imx21(hcd); - struct urb_priv *urb_priv; - unsigned long flags; - struct ep_priv *ep_priv; - struct td *td = NULL; - int i; - int ret; - int cur_frame; - u16 maxpacket; - - urb_priv = kzalloc(sizeof(struct urb_priv), mem_flags); - if (urb_priv == NULL) - return -ENOMEM; - - urb_priv->isoc_td = kcalloc(urb->number_of_packets, sizeof(struct td), - mem_flags); - if (urb_priv->isoc_td == NULL) { - ret = -ENOMEM; - goto alloc_td_failed; - } - - spin_lock_irqsave(&imx21->lock, flags); - - if (ep->hcpriv == NULL) { - ep_priv = alloc_isoc_ep(imx21, ep); - if (ep_priv == NULL) { - ret = -ENOMEM; - goto alloc_ep_failed; - } - } else { - ep_priv = ep->hcpriv; - } - - ret = alloc_isoc_etds(imx21, ep_priv); - if (ret) - goto alloc_etd_failed; - - ret = usb_hcd_link_urb_to_ep(hcd, urb); - if (ret) - goto link_failed; - - urb->status = -EINPROGRESS; - urb->actual_length = 0; - urb->error_count = 0; - urb->hcpriv = urb_priv; - urb_priv->ep = ep; - - /* allocate data memory for largest packets if not already done */ - maxpacket = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); - for (i = 0; i < NUM_ISO_ETDS; i++) { - struct etd_priv *etd = &imx21->etd[ep_priv->etd[i]]; - - if (etd->dmem_size > 0 && etd->dmem_size < maxpacket) { - /* not sure if this can really occur.... */ - dev_err(imx21->dev, "increasing isoc buffer %d->%d\n", - etd->dmem_size, maxpacket); - ret = -EMSGSIZE; - goto alloc_dmem_failed; - } - - if (etd->dmem_size == 0) { - etd->dmem_offset = alloc_dmem(imx21, maxpacket, ep); - if (etd->dmem_offset < 0) { - dev_dbg(imx21->dev, "failed alloc isoc dmem\n"); - ret = -EAGAIN; - goto alloc_dmem_failed; - } - etd->dmem_size = maxpacket; - } - } - - /* calculate frame */ - cur_frame = imx21_hc_get_frame(hcd); - i = 0; - if (list_empty(&ep_priv->td_list)) { - urb->start_frame = wrap_frame(cur_frame + 5); - } else { - urb->start_frame = wrap_frame(list_entry(ep_priv->td_list.prev, - struct td, list)->frame + urb->interval); - - if (frame_after(cur_frame, urb->start_frame)) { - dev_dbg(imx21->dev, - "enqueue: adjusting iso start %d (cur=%d) asap=%d\n", - urb->start_frame, cur_frame, - (urb->transfer_flags & URB_ISO_ASAP) != 0); - i = DIV_ROUND_UP(wrap_frame( - cur_frame - urb->start_frame), - urb->interval); - - /* Treat underruns as if URB_ISO_ASAP was set */ - if ((urb->transfer_flags & URB_ISO_ASAP) || - i >= urb->number_of_packets) { - urb->start_frame = wrap_frame(urb->start_frame - + i * urb->interval); - i = 0; - } - } - } - - /* set up transfers */ - urb_priv->isoc_remaining = urb->number_of_packets - i; - td = urb_priv->isoc_td; - for (; i < urb->number_of_packets; i++, td++) { - unsigned int offset = urb->iso_frame_desc[i].offset; - td->ep = ep; - td->urb = urb; - td->len = urb->iso_frame_desc[i].length; - td->isoc_index = i; - td->frame = wrap_frame(urb->start_frame + urb->interval * i); - td->dma_handle = urb->transfer_dma + offset; - td->cpu_buffer = urb->transfer_buffer + offset; - list_add_tail(&td->list, &ep_priv->td_list); - } - - dev_vdbg(imx21->dev, "setup %d packets for iso frame %d->%d\n", - urb->number_of_packets, urb->start_frame, td->frame); - - debug_urb_submitted(imx21, urb); - schedule_isoc_etds(hcd, ep); - - spin_unlock_irqrestore(&imx21->lock, flags); - return 0; - -alloc_dmem_failed: - usb_hcd_unlink_urb_from_ep(hcd, urb); - -link_failed: -alloc_etd_failed: -alloc_ep_failed: - spin_unlock_irqrestore(&imx21->lock, flags); - kfree(urb_priv->isoc_td); - -alloc_td_failed: - kfree(urb_priv); - return ret; -} - -static void dequeue_isoc_urb(struct imx21 *imx21, - struct urb *urb, struct ep_priv *ep_priv) -{ - struct urb_priv *urb_priv = urb->hcpriv; - struct td *td, *tmp; - int i; - - if (urb_priv->active) { - for (i = 0; i < NUM_ISO_ETDS; i++) { - int etd_num = ep_priv->etd[i]; - if (etd_num != -1 && imx21->etd[etd_num].urb == urb) { - struct etd_priv *etd = imx21->etd + etd_num; - - reset_etd(imx21, etd_num); - free_dmem(imx21, etd); - } - } - } - - list_for_each_entry_safe(td, tmp, &ep_priv->td_list, list) { - if (td->urb == urb) { - dev_vdbg(imx21->dev, "removing td %p\n", td); - list_del(&td->list); - } - } -} - -/* =========================================== */ -/* NON ISOC Handling ... */ -/* =========================================== */ - -static void schedule_nonisoc_etd(struct imx21 *imx21, struct urb *urb) -{ - unsigned int pipe = urb->pipe; - struct urb_priv *urb_priv = urb->hcpriv; - struct ep_priv *ep_priv = urb_priv->ep->hcpriv; - int state = urb_priv->state; - int etd_num = ep_priv->etd[0]; - struct etd_priv *etd; - u32 count; - u16 etd_buf_size; - u16 maxpacket; - u8 dir; - u8 bufround; - u8 datatoggle; - u8 interval = 0; - u8 relpolpos = 0; - - if (etd_num < 0) { - dev_err(imx21->dev, "No valid ETD\n"); - return; - } - if (readl(imx21->regs + USBH_ETDENSET) & (1 << etd_num)) - dev_err(imx21->dev, "submitting to active ETD %d\n", etd_num); - - etd = &imx21->etd[etd_num]; - maxpacket = usb_maxpacket(urb->dev, pipe, usb_pipeout(pipe)); - if (!maxpacket) - maxpacket = 8; - - if (usb_pipecontrol(pipe) && (state != US_CTRL_DATA)) { - if (state == US_CTRL_SETUP) { - dir = TD_DIR_SETUP; - if (unsuitable_for_dma(urb->setup_dma)) - usb_hcd_unmap_urb_setup_for_dma(imx21->hcd, - urb); - etd->dma_handle = urb->setup_dma; - etd->cpu_buffer = urb->setup_packet; - bufround = 0; - count = 8; - datatoggle = TD_TOGGLE_DATA0; - } else { /* US_CTRL_ACK */ - dir = usb_pipeout(pipe) ? TD_DIR_IN : TD_DIR_OUT; - bufround = 0; - count = 0; - datatoggle = TD_TOGGLE_DATA1; - } - } else { - dir = usb_pipeout(pipe) ? TD_DIR_OUT : TD_DIR_IN; - bufround = (dir == TD_DIR_IN) ? 1 : 0; - if (unsuitable_for_dma(urb->transfer_dma)) - usb_hcd_unmap_urb_for_dma(imx21->hcd, urb); - - etd->dma_handle = urb->transfer_dma; - etd->cpu_buffer = urb->transfer_buffer; - if (usb_pipebulk(pipe) && (state == US_BULK0)) - count = 0; - else - count = urb->transfer_buffer_length; - - if (usb_pipecontrol(pipe)) { - datatoggle = TD_TOGGLE_DATA1; - } else { - if (usb_gettoggle( - urb->dev, - usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe))) - datatoggle = TD_TOGGLE_DATA1; - else - datatoggle = TD_TOGGLE_DATA0; - } - } - - etd->urb = urb; - etd->ep = urb_priv->ep; - etd->len = count; - - if (usb_pipeint(pipe)) { - interval = urb->interval; - relpolpos = (readl(imx21->regs + USBH_FRMNUB) + 1) & 0xff; - } - - /* Write ETD to device memory */ - setup_etd_dword0(imx21, etd_num, urb, dir, maxpacket); - - etd_writel(imx21, etd_num, 2, - (u32) interval << DW2_POLINTERV | - ((u32) relpolpos << DW2_RELPOLPOS) | - ((u32) dir << DW2_DIRPID) | - ((u32) bufround << DW2_BUFROUND) | - ((u32) datatoggle << DW2_DATATOG) | - ((u32) TD_NOTACCESSED << DW2_COMPCODE)); - - /* DMA will always transfer buffer size even if TOBYCNT in DWORD3 - is smaller. Make sure we don't overrun the buffer! - */ - if (count && count < maxpacket) - etd_buf_size = count; - else - etd_buf_size = maxpacket; - - etd_writel(imx21, etd_num, 3, - ((u32) (etd_buf_size - 1) << DW3_BUFSIZE) | (u32) count); - - if (!count) - etd->dma_handle = 0; - - /* allocate x and y buffer space at once */ - etd->dmem_size = (count > maxpacket) ? maxpacket * 2 : maxpacket; - etd->dmem_offset = alloc_dmem(imx21, etd->dmem_size, urb_priv->ep); - if (etd->dmem_offset < 0) { - /* Setup everything we can in HW and update when we get DMEM */ - etd_writel(imx21, etd_num, 1, (u32)maxpacket << 16); - - dev_dbg(imx21->dev, "Queuing etd %d for DMEM\n", etd_num); - debug_urb_queued_for_dmem(imx21, urb); - list_add_tail(&etd->queue, &imx21->queue_for_dmem); - return; - } - - etd_writel(imx21, etd_num, 1, - (((u32) etd->dmem_offset + (u32) maxpacket) << DW1_YBUFSRTAD) | - (u32) etd->dmem_offset); - - urb_priv->active = 1; - - /* enable the ETD to kick off transfer */ - dev_vdbg(imx21->dev, "Activating etd %d for %d bytes %s\n", - etd_num, count, dir != TD_DIR_IN ? "out" : "in"); - activate_etd(imx21, etd_num, dir); - -} - -static void nonisoc_etd_done(struct usb_hcd *hcd, int etd_num) -{ - struct imx21 *imx21 = hcd_to_imx21(hcd); - struct etd_priv *etd = &imx21->etd[etd_num]; - struct urb *urb = etd->urb; - u32 etd_mask = 1 << etd_num; - struct urb_priv *urb_priv = urb->hcpriv; - int dir; - int cc; - u32 bytes_xfrd; - int etd_done; - - disactivate_etd(imx21, etd_num); - - dir = (etd_readl(imx21, etd_num, 0) >> DW0_DIRECT) & 0x3; - cc = (etd_readl(imx21, etd_num, 2) >> DW2_COMPCODE) & 0xf; - bytes_xfrd = etd->len - (etd_readl(imx21, etd_num, 3) & 0x1fffff); - - /* save toggle carry */ - usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe), - (etd_readl(imx21, etd_num, 0) >> DW0_TOGCRY) & 0x1); - - if (dir == TD_DIR_IN) { - clear_toggle_bit(imx21, USBH_XFILLSTAT, etd_mask); - clear_toggle_bit(imx21, USBH_YFILLSTAT, etd_mask); - - if (etd->bounce_buffer) { - memcpy(etd->cpu_buffer, etd->bounce_buffer, bytes_xfrd); - dma_unmap_single(imx21->dev, - etd->dma_handle, etd->len, DMA_FROM_DEVICE); - } else if (!etd->dma_handle && bytes_xfrd) {/* PIO */ - memcpy_fromio(etd->cpu_buffer, - imx21->regs + USBOTG_DMEM + etd->dmem_offset, - bytes_xfrd); - } - } - - kfree(etd->bounce_buffer); - etd->bounce_buffer = NULL; - free_dmem(imx21, etd); - - urb->error_count = 0; - if (!(urb->transfer_flags & URB_SHORT_NOT_OK) - && (cc == TD_DATAUNDERRUN)) - cc = TD_CC_NOERROR; - - if (cc != 0) - dev_vdbg(imx21->dev, "cc is 0x%x\n", cc); - - etd_done = (cc_to_error[cc] != 0); /* stop if error */ - - switch (usb_pipetype(urb->pipe)) { - case PIPE_CONTROL: - switch (urb_priv->state) { - case US_CTRL_SETUP: - if (urb->transfer_buffer_length > 0) - urb_priv->state = US_CTRL_DATA; - else - urb_priv->state = US_CTRL_ACK; - break; - case US_CTRL_DATA: - urb->actual_length += bytes_xfrd; - urb_priv->state = US_CTRL_ACK; - break; - case US_CTRL_ACK: - etd_done = 1; - break; - default: - dev_err(imx21->dev, - "Invalid pipe state %d\n", urb_priv->state); - etd_done = 1; - break; - } - break; - - case PIPE_BULK: - urb->actual_length += bytes_xfrd; - if ((urb_priv->state == US_BULK) - && (urb->transfer_flags & URB_ZERO_PACKET) - && urb->transfer_buffer_length > 0 - && ((urb->transfer_buffer_length % - usb_maxpacket(urb->dev, urb->pipe, - usb_pipeout(urb->pipe))) == 0)) { - /* need a 0-packet */ - urb_priv->state = US_BULK0; - } else { - etd_done = 1; - } - break; - - case PIPE_INTERRUPT: - urb->actual_length += bytes_xfrd; - etd_done = 1; - break; - } - - if (etd_done) - nonisoc_urb_completed_for_etd(imx21, etd, cc_to_error[cc]); - else { - dev_vdbg(imx21->dev, "next state=%d\n", urb_priv->state); - schedule_nonisoc_etd(imx21, urb); - } -} - - -static struct ep_priv *alloc_ep(void) -{ - int i; - struct ep_priv *ep_priv; - - ep_priv = kzalloc(sizeof(struct ep_priv), GFP_ATOMIC); - if (!ep_priv) - return NULL; - - for (i = 0; i < NUM_ISO_ETDS; ++i) - ep_priv->etd[i] = -1; - - return ep_priv; -} - -static int imx21_hc_urb_enqueue(struct usb_hcd *hcd, - struct urb *urb, gfp_t mem_flags) -{ - struct imx21 *imx21 = hcd_to_imx21(hcd); - struct usb_host_endpoint *ep = urb->ep; - struct urb_priv *urb_priv; - struct ep_priv *ep_priv; - struct etd_priv *etd; - int ret; - unsigned long flags; - - dev_vdbg(imx21->dev, - "enqueue urb=%p ep=%p len=%d " - "buffer=%p dma=%pad setupBuf=%p setupDma=%pad\n", - urb, ep, - urb->transfer_buffer_length, - urb->transfer_buffer, &urb->transfer_dma, - urb->setup_packet, &urb->setup_dma); - - if (usb_pipeisoc(urb->pipe)) - return imx21_hc_urb_enqueue_isoc(hcd, ep, urb, mem_flags); - - urb_priv = kzalloc(sizeof(struct urb_priv), mem_flags); - if (!urb_priv) - return -ENOMEM; - - spin_lock_irqsave(&imx21->lock, flags); - - ep_priv = ep->hcpriv; - if (ep_priv == NULL) { - ep_priv = alloc_ep(); - if (!ep_priv) { - ret = -ENOMEM; - goto failed_alloc_ep; - } - ep->hcpriv = ep_priv; - ep_priv->ep = ep; - } - - ret = usb_hcd_link_urb_to_ep(hcd, urb); - if (ret) - goto failed_link; - - urb->status = -EINPROGRESS; - urb->actual_length = 0; - urb->error_count = 0; - urb->hcpriv = urb_priv; - urb_priv->ep = ep; - - switch (usb_pipetype(urb->pipe)) { - case PIPE_CONTROL: - urb_priv->state = US_CTRL_SETUP; - break; - case PIPE_BULK: - urb_priv->state = US_BULK; - break; - } - - debug_urb_submitted(imx21, urb); - if (ep_priv->etd[0] < 0) { - if (ep_priv->waiting_etd) { - dev_dbg(imx21->dev, - "no ETD available already queued %p\n", - ep_priv); - debug_urb_queued_for_etd(imx21, urb); - goto out; - } - ep_priv->etd[0] = alloc_etd(imx21); - if (ep_priv->etd[0] < 0) { - dev_dbg(imx21->dev, - "no ETD available queueing %p\n", ep_priv); - debug_urb_queued_for_etd(imx21, urb); - list_add_tail(&ep_priv->queue, &imx21->queue_for_etd); - ep_priv->waiting_etd = 1; - goto out; - } - } - - /* Schedule if no URB already active for this endpoint */ - etd = &imx21->etd[ep_priv->etd[0]]; - if (etd->urb == NULL) { - DEBUG_LOG_FRAME(imx21, etd, last_req); - schedule_nonisoc_etd(imx21, urb); - } - -out: - spin_unlock_irqrestore(&imx21->lock, flags); - return 0; - -failed_link: -failed_alloc_ep: - spin_unlock_irqrestore(&imx21->lock, flags); - kfree(urb_priv); - return ret; -} - -static int imx21_hc_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, - int status) -{ - struct imx21 *imx21 = hcd_to_imx21(hcd); - unsigned long flags; - struct usb_host_endpoint *ep; - struct ep_priv *ep_priv; - struct urb_priv *urb_priv = urb->hcpriv; - int ret = -EINVAL; - - dev_vdbg(imx21->dev, "dequeue urb=%p iso=%d status=%d\n", - urb, usb_pipeisoc(urb->pipe), status); - - spin_lock_irqsave(&imx21->lock, flags); - - ret = usb_hcd_check_unlink_urb(hcd, urb, status); - if (ret) - goto fail; - ep = urb_priv->ep; - ep_priv = ep->hcpriv; - - debug_urb_unlinked(imx21, urb); - - if (usb_pipeisoc(urb->pipe)) { - dequeue_isoc_urb(imx21, urb, ep_priv); - schedule_isoc_etds(hcd, ep); - } else if (urb_priv->active) { - int etd_num = ep_priv->etd[0]; - if (etd_num != -1) { - struct etd_priv *etd = &imx21->etd[etd_num]; - - disactivate_etd(imx21, etd_num); - free_dmem(imx21, etd); - etd->urb = NULL; - kfree(etd->bounce_buffer); - etd->bounce_buffer = NULL; - } - } - - urb_done(hcd, urb, status); - - spin_unlock_irqrestore(&imx21->lock, flags); - return 0; - -fail: - spin_unlock_irqrestore(&imx21->lock, flags); - return ret; -} - -/* =========================================== */ -/* Interrupt dispatch */ -/* =========================================== */ - -static void process_etds(struct usb_hcd *hcd, struct imx21 *imx21, int sof) -{ - int etd_num; - int enable_sof_int = 0; - unsigned long flags; - - spin_lock_irqsave(&imx21->lock, flags); - - for (etd_num = 0; etd_num < USB_NUM_ETD; etd_num++) { - u32 etd_mask = 1 << etd_num; - u32 enabled = readl(imx21->regs + USBH_ETDENSET) & etd_mask; - u32 done = readl(imx21->regs + USBH_ETDDONESTAT) & etd_mask; - struct etd_priv *etd = &imx21->etd[etd_num]; - - - if (done) { - DEBUG_LOG_FRAME(imx21, etd, last_int); - } else { -/* - * Kludge warning! - * - * When multiple transfers are using the bus we sometimes get into a state - * where the transfer has completed (the CC field of the ETD is != 0x0F), - * the ETD has self disabled but the ETDDONESTAT flag is not set - * (and hence no interrupt occurs). - * This causes the transfer in question to hang. - * The kludge below checks for this condition at each SOF and processes any - * blocked ETDs (after an arbitrary 10 frame wait) - * - * With a single active transfer the usbtest test suite will run for days - * without the kludge. - * With other bus activity (eg mass storage) even just test1 will hang without - * the kludge. - */ - u32 dword0; - int cc; - - if (etd->active_count && !enabled) /* suspicious... */ - enable_sof_int = 1; - - if (!sof || enabled || !etd->active_count) - continue; - - cc = etd_readl(imx21, etd_num, 2) >> DW2_COMPCODE; - if (cc == TD_NOTACCESSED) - continue; - - if (++etd->active_count < 10) - continue; - - dword0 = etd_readl(imx21, etd_num, 0); - dev_dbg(imx21->dev, - "unblock ETD %d dev=0x%X ep=0x%X cc=0x%02X!\n", - etd_num, dword0 & 0x7F, - (dword0 >> DW0_ENDPNT) & 0x0F, - cc); - -#ifdef DEBUG - dev_dbg(imx21->dev, - "frame: act=%d disact=%d" - " int=%d req=%d cur=%d\n", - etd->activated_frame, - etd->disactivated_frame, - etd->last_int_frame, - etd->last_req_frame, - readl(imx21->regs + USBH_FRMNUB)); - imx21->debug_unblocks++; -#endif - etd->active_count = 0; -/* End of kludge */ - } - - if (etd->ep == NULL || etd->urb == NULL) { - dev_dbg(imx21->dev, - "Interrupt for unexpected etd %d" - " ep=%p urb=%p\n", - etd_num, etd->ep, etd->urb); - disactivate_etd(imx21, etd_num); - continue; - } - - if (usb_pipeisoc(etd->urb->pipe)) - isoc_etd_done(hcd, etd_num); - else - nonisoc_etd_done(hcd, etd_num); - } - - /* only enable SOF interrupt if it may be needed for the kludge */ - if (enable_sof_int) - set_register_bits(imx21, USBH_SYSIEN, USBH_SYSIEN_SOFINT); - else - clear_register_bits(imx21, USBH_SYSIEN, USBH_SYSIEN_SOFINT); - - - spin_unlock_irqrestore(&imx21->lock, flags); -} - -static irqreturn_t imx21_irq(struct usb_hcd *hcd) -{ - struct imx21 *imx21 = hcd_to_imx21(hcd); - u32 ints = readl(imx21->regs + USBH_SYSISR); - - if (ints & USBH_SYSIEN_HERRINT) - dev_dbg(imx21->dev, "Scheduling error\n"); - - if (ints & USBH_SYSIEN_SORINT) - dev_dbg(imx21->dev, "Scheduling overrun\n"); - - if (ints & (USBH_SYSISR_DONEINT | USBH_SYSISR_SOFINT)) - process_etds(hcd, imx21, ints & USBH_SYSISR_SOFINT); - - writel(ints, imx21->regs + USBH_SYSISR); - return IRQ_HANDLED; -} - -static void imx21_hc_endpoint_disable(struct usb_hcd *hcd, - struct usb_host_endpoint *ep) -{ - struct imx21 *imx21 = hcd_to_imx21(hcd); - unsigned long flags; - struct ep_priv *ep_priv; - int i; - - if (ep == NULL) - return; - - spin_lock_irqsave(&imx21->lock, flags); - ep_priv = ep->hcpriv; - dev_vdbg(imx21->dev, "disable ep=%p, ep->hcpriv=%p\n", ep, ep_priv); - - if (!list_empty(&ep->urb_list)) - dev_dbg(imx21->dev, "ep's URB list is not empty\n"); - - if (ep_priv != NULL) { - for (i = 0; i < NUM_ISO_ETDS; i++) { - if (ep_priv->etd[i] > -1) - dev_dbg(imx21->dev, "free etd %d for disable\n", - ep_priv->etd[i]); - - free_etd(imx21, ep_priv->etd[i]); - } - kfree(ep_priv); - ep->hcpriv = NULL; - } - - for (i = 0; i < USB_NUM_ETD; i++) { - if (imx21->etd[i].alloc && imx21->etd[i].ep == ep) { - dev_err(imx21->dev, - "Active etd %d for disabled ep=%p!\n", i, ep); - free_etd(imx21, i); - } - } - free_epdmem(imx21, ep); - spin_unlock_irqrestore(&imx21->lock, flags); -} - -/* =========================================== */ -/* Hub handling */ -/* =========================================== */ - -static int get_hub_descriptor(struct usb_hcd *hcd, - struct usb_hub_descriptor *desc) -{ - struct imx21 *imx21 = hcd_to_imx21(hcd); - desc->bDescriptorType = USB_DT_HUB; /* HUB descriptor */ - desc->bHubContrCurrent = 0; - - desc->bNbrPorts = readl(imx21->regs + USBH_ROOTHUBA) - & USBH_ROOTHUBA_NDNSTMPRT_MASK; - desc->bDescLength = 9; - desc->bPwrOn2PwrGood = 0; - desc->wHubCharacteristics = (__force __u16) cpu_to_le16( - HUB_CHAR_NO_LPSM | /* No power switching */ - HUB_CHAR_NO_OCPM); /* No over current protection */ - - desc->u.hs.DeviceRemovable[0] = 1 << 1; - desc->u.hs.DeviceRemovable[1] = ~0; - return 0; -} - -static int imx21_hc_hub_status_data(struct usb_hcd *hcd, char *buf) -{ - struct imx21 *imx21 = hcd_to_imx21(hcd); - int ports; - int changed = 0; - int i; - unsigned long flags; - - spin_lock_irqsave(&imx21->lock, flags); - ports = readl(imx21->regs + USBH_ROOTHUBA) - & USBH_ROOTHUBA_NDNSTMPRT_MASK; - if (ports > 7) { - ports = 7; - dev_err(imx21->dev, "ports %d > 7\n", ports); - } - for (i = 0; i < ports; i++) { - if (readl(imx21->regs + USBH_PORTSTAT(i)) & - (USBH_PORTSTAT_CONNECTSC | - USBH_PORTSTAT_PRTENBLSC | - USBH_PORTSTAT_PRTSTATSC | - USBH_PORTSTAT_OVRCURIC | - USBH_PORTSTAT_PRTRSTSC)) { - - changed = 1; - buf[0] |= 1 << (i + 1); - } - } - spin_unlock_irqrestore(&imx21->lock, flags); - - if (changed) - dev_info(imx21->dev, "Hub status changed\n"); - return changed; -} - -static int imx21_hc_hub_control(struct usb_hcd *hcd, - u16 typeReq, - u16 wValue, u16 wIndex, char *buf, u16 wLength) -{ - struct imx21 *imx21 = hcd_to_imx21(hcd); - int rc = 0; - u32 status_write = 0; - - switch (typeReq) { - case ClearHubFeature: - dev_dbg(imx21->dev, "ClearHubFeature\n"); - switch (wValue) { - case C_HUB_OVER_CURRENT: - dev_dbg(imx21->dev, " OVER_CURRENT\n"); - break; - case C_HUB_LOCAL_POWER: - dev_dbg(imx21->dev, " LOCAL_POWER\n"); - break; - default: - dev_dbg(imx21->dev, " unknown\n"); - rc = -EINVAL; - break; - } - break; - - case ClearPortFeature: - dev_dbg(imx21->dev, "ClearPortFeature\n"); - switch (wValue) { - case USB_PORT_FEAT_ENABLE: - dev_dbg(imx21->dev, " ENABLE\n"); - status_write = USBH_PORTSTAT_CURCONST; - break; - case USB_PORT_FEAT_SUSPEND: - dev_dbg(imx21->dev, " SUSPEND\n"); - status_write = USBH_PORTSTAT_PRTOVRCURI; - break; - case USB_PORT_FEAT_POWER: - dev_dbg(imx21->dev, " POWER\n"); - status_write = USBH_PORTSTAT_LSDEVCON; - break; - case USB_PORT_FEAT_C_ENABLE: - dev_dbg(imx21->dev, " C_ENABLE\n"); - status_write = USBH_PORTSTAT_PRTENBLSC; - break; - case USB_PORT_FEAT_C_SUSPEND: - dev_dbg(imx21->dev, " C_SUSPEND\n"); - status_write = USBH_PORTSTAT_PRTSTATSC; - break; - case USB_PORT_FEAT_C_CONNECTION: - dev_dbg(imx21->dev, " C_CONNECTION\n"); - status_write = USBH_PORTSTAT_CONNECTSC; - break; - case USB_PORT_FEAT_C_OVER_CURRENT: - dev_dbg(imx21->dev, " C_OVER_CURRENT\n"); - status_write = USBH_PORTSTAT_OVRCURIC; - break; - case USB_PORT_FEAT_C_RESET: - dev_dbg(imx21->dev, " C_RESET\n"); - status_write = USBH_PORTSTAT_PRTRSTSC; - break; - default: - dev_dbg(imx21->dev, " unknown\n"); - rc = -EINVAL; - break; - } - - break; - - case GetHubDescriptor: - dev_dbg(imx21->dev, "GetHubDescriptor\n"); - rc = get_hub_descriptor(hcd, (void *)buf); - break; - - case GetHubStatus: - dev_dbg(imx21->dev, " GetHubStatus\n"); - *(__le32 *) buf = 0; - break; - - case GetPortStatus: - dev_dbg(imx21->dev, "GetPortStatus: port: %d, 0x%x\n", - wIndex, USBH_PORTSTAT(wIndex - 1)); - *(__le32 *) buf = readl(imx21->regs + - USBH_PORTSTAT(wIndex - 1)); - break; - - case SetHubFeature: - dev_dbg(imx21->dev, "SetHubFeature\n"); - switch (wValue) { - case C_HUB_OVER_CURRENT: - dev_dbg(imx21->dev, " OVER_CURRENT\n"); - break; - - case C_HUB_LOCAL_POWER: - dev_dbg(imx21->dev, " LOCAL_POWER\n"); - break; - default: - dev_dbg(imx21->dev, " unknown\n"); - rc = -EINVAL; - break; - } - - break; - - case SetPortFeature: - dev_dbg(imx21->dev, "SetPortFeature\n"); - switch (wValue) { - case USB_PORT_FEAT_SUSPEND: - dev_dbg(imx21->dev, " SUSPEND\n"); - status_write = USBH_PORTSTAT_PRTSUSPST; - break; - case USB_PORT_FEAT_POWER: - dev_dbg(imx21->dev, " POWER\n"); - status_write = USBH_PORTSTAT_PRTPWRST; - break; - case USB_PORT_FEAT_RESET: - dev_dbg(imx21->dev, " RESET\n"); - status_write = USBH_PORTSTAT_PRTRSTST; - break; - default: - dev_dbg(imx21->dev, " unknown\n"); - rc = -EINVAL; - break; - } - break; - - default: - dev_dbg(imx21->dev, " unknown\n"); - rc = -EINVAL; - break; - } - - if (status_write) - writel(status_write, imx21->regs + USBH_PORTSTAT(wIndex - 1)); - return rc; -} - -/* =========================================== */ -/* Host controller management */ -/* =========================================== */ - -static int imx21_hc_reset(struct usb_hcd *hcd) -{ - struct imx21 *imx21 = hcd_to_imx21(hcd); - unsigned long timeout; - unsigned long flags; - - spin_lock_irqsave(&imx21->lock, flags); - - /* Reset the Host controller modules */ - writel(USBOTG_RST_RSTCTRL | USBOTG_RST_RSTRH | - USBOTG_RST_RSTHSIE | USBOTG_RST_RSTHC, - imx21->regs + USBOTG_RST_CTRL); - - /* Wait for reset to finish */ - timeout = jiffies + HZ; - while (readl(imx21->regs + USBOTG_RST_CTRL) != 0) { - if (time_after(jiffies, timeout)) { - spin_unlock_irqrestore(&imx21->lock, flags); - dev_err(imx21->dev, "timeout waiting for reset\n"); - return -ETIMEDOUT; - } - spin_unlock_irq(&imx21->lock); - schedule_timeout_uninterruptible(1); - spin_lock_irq(&imx21->lock); - } - spin_unlock_irqrestore(&imx21->lock, flags); - return 0; -} - -static int imx21_hc_start(struct usb_hcd *hcd) -{ - struct imx21 *imx21 = hcd_to_imx21(hcd); - unsigned long flags; - int i, j; - u32 hw_mode = USBOTG_HWMODE_CRECFG_HOST; - u32 usb_control = 0; - - hw_mode |= ((imx21->pdata->host_xcvr << USBOTG_HWMODE_HOSTXCVR_SHIFT) & - USBOTG_HWMODE_HOSTXCVR_MASK); - hw_mode |= ((imx21->pdata->otg_xcvr << USBOTG_HWMODE_OTGXCVR_SHIFT) & - USBOTG_HWMODE_OTGXCVR_MASK); - - if (imx21->pdata->host1_txenoe) - usb_control |= USBCTRL_HOST1_TXEN_OE; - - if (!imx21->pdata->host1_xcverless) - usb_control |= USBCTRL_HOST1_BYP_TLL; - - if (imx21->pdata->otg_ext_xcvr) - usb_control |= USBCTRL_OTC_RCV_RXDP; - - - spin_lock_irqsave(&imx21->lock, flags); - - writel((USBOTG_CLK_CTRL_HST | USBOTG_CLK_CTRL_MAIN), - imx21->regs + USBOTG_CLK_CTRL); - writel(hw_mode, imx21->regs + USBOTG_HWMODE); - writel(usb_control, imx21->regs + USBCTRL); - writel(USB_MISCCONTROL_SKPRTRY | USB_MISCCONTROL_ARBMODE, - imx21->regs + USB_MISCCONTROL); - - /* Clear the ETDs */ - for (i = 0; i < USB_NUM_ETD; i++) - for (j = 0; j < 4; j++) - etd_writel(imx21, i, j, 0); - - /* Take the HC out of reset */ - writel(USBH_HOST_CTRL_HCUSBSTE_OPERATIONAL | USBH_HOST_CTRL_CTLBLKSR_1, - imx21->regs + USBH_HOST_CTRL); - - /* Enable ports */ - if (imx21->pdata->enable_otg_host) - writel(USBH_PORTSTAT_PRTPWRST | USBH_PORTSTAT_PRTENABST, - imx21->regs + USBH_PORTSTAT(0)); - - if (imx21->pdata->enable_host1) - writel(USBH_PORTSTAT_PRTPWRST | USBH_PORTSTAT_PRTENABST, - imx21->regs + USBH_PORTSTAT(1)); - - if (imx21->pdata->enable_host2) - writel(USBH_PORTSTAT_PRTPWRST | USBH_PORTSTAT_PRTENABST, - imx21->regs + USBH_PORTSTAT(2)); - - - hcd->state = HC_STATE_RUNNING; - - /* Enable host controller interrupts */ - set_register_bits(imx21, USBH_SYSIEN, - USBH_SYSIEN_HERRINT | - USBH_SYSIEN_DONEINT | USBH_SYSIEN_SORINT); - set_register_bits(imx21, USBOTG_CINT_STEN, USBOTG_HCINT); - - spin_unlock_irqrestore(&imx21->lock, flags); - - return 0; -} - -static void imx21_hc_stop(struct usb_hcd *hcd) -{ - struct imx21 *imx21 = hcd_to_imx21(hcd); - unsigned long flags; - - spin_lock_irqsave(&imx21->lock, flags); - - writel(0, imx21->regs + USBH_SYSIEN); - clear_register_bits(imx21, USBOTG_CINT_STEN, USBOTG_HCINT); - clear_register_bits(imx21, USBOTG_CLK_CTRL_HST | USBOTG_CLK_CTRL_MAIN, - USBOTG_CLK_CTRL); - spin_unlock_irqrestore(&imx21->lock, flags); -} - -/* =========================================== */ -/* Driver glue */ -/* =========================================== */ - -static const struct hc_driver imx21_hc_driver = { - .description = hcd_name, - .product_desc = "IMX21 USB Host Controller", - .hcd_priv_size = sizeof(struct imx21), - - .flags = HCD_DMA | HCD_USB11, - .irq = imx21_irq, - - .reset = imx21_hc_reset, - .start = imx21_hc_start, - .stop = imx21_hc_stop, - - /* I/O requests */ - .urb_enqueue = imx21_hc_urb_enqueue, - .urb_dequeue = imx21_hc_urb_dequeue, - .endpoint_disable = imx21_hc_endpoint_disable, - - /* scheduling support */ - .get_frame_number = imx21_hc_get_frame, - - /* Root hub support */ - .hub_status_data = imx21_hc_hub_status_data, - .hub_control = imx21_hc_hub_control, - -}; - -static struct mx21_usbh_platform_data default_pdata = { - .host_xcvr = MX21_USBXCVR_TXDIF_RXDIF, - .otg_xcvr = MX21_USBXCVR_TXDIF_RXDIF, - .enable_host1 = 1, - .enable_host2 = 1, - .enable_otg_host = 1, - -}; - -static int imx21_remove(struct platform_device *pdev) -{ - struct usb_hcd *hcd = platform_get_drvdata(pdev); - struct imx21 *imx21 = hcd_to_imx21(hcd); - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - remove_debug_files(imx21); - usb_remove_hcd(hcd); - - if (res != NULL) { - clk_disable_unprepare(imx21->clk); - clk_put(imx21->clk); - iounmap(imx21->regs); - release_mem_region(res->start, resource_size(res)); - } - - kfree(hcd); - return 0; -} - - -static int imx21_probe(struct platform_device *pdev) -{ - struct usb_hcd *hcd; - struct imx21 *imx21; - struct resource *res; - int ret; - int irq; - - printk(KERN_INFO "%s\n", imx21_hc_driver.product_desc); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - - hcd = usb_create_hcd(&imx21_hc_driver, - &pdev->dev, dev_name(&pdev->dev)); - if (hcd == NULL) { - dev_err(&pdev->dev, "Cannot create hcd (%s)\n", - dev_name(&pdev->dev)); - return -ENOMEM; - } - - imx21 = hcd_to_imx21(hcd); - imx21->hcd = hcd; - imx21->dev = &pdev->dev; - imx21->pdata = dev_get_platdata(&pdev->dev); - if (!imx21->pdata) - imx21->pdata = &default_pdata; - - spin_lock_init(&imx21->lock); - INIT_LIST_HEAD(&imx21->dmem_list); - INIT_LIST_HEAD(&imx21->queue_for_etd); - INIT_LIST_HEAD(&imx21->queue_for_dmem); - create_debug_files(imx21); - - res = request_mem_region(res->start, resource_size(res), hcd_name); - if (!res) { - ret = -EBUSY; - goto failed_request_mem; - } - - imx21->regs = ioremap(res->start, resource_size(res)); - if (imx21->regs == NULL) { - dev_err(imx21->dev, "Cannot map registers\n"); - ret = -ENOMEM; - goto failed_ioremap; - } - - /* Enable clocks source */ - imx21->clk = clk_get(imx21->dev, NULL); - if (IS_ERR(imx21->clk)) { - dev_err(imx21->dev, "no clock found\n"); - ret = PTR_ERR(imx21->clk); - goto failed_clock_get; - } - - ret = clk_set_rate(imx21->clk, clk_round_rate(imx21->clk, 48000000)); - if (ret) - goto failed_clock_set; - ret = clk_prepare_enable(imx21->clk); - if (ret) - goto failed_clock_enable; - - dev_info(imx21->dev, "Hardware HC revision: 0x%02X\n", - (readl(imx21->regs + USBOTG_HWMODE) >> 16) & 0xFF); - - ret = usb_add_hcd(hcd, irq, 0); - if (ret != 0) { - dev_err(imx21->dev, "usb_add_hcd() returned %d\n", ret); - goto failed_add_hcd; - } - device_wakeup_enable(hcd->self.controller); - - return 0; - -failed_add_hcd: - clk_disable_unprepare(imx21->clk); -failed_clock_enable: -failed_clock_set: - clk_put(imx21->clk); -failed_clock_get: - iounmap(imx21->regs); -failed_ioremap: - release_mem_region(res->start, resource_size(res)); -failed_request_mem: - remove_debug_files(imx21); - usb_put_hcd(hcd); - return ret; -} - -static struct platform_driver imx21_hcd_driver = { - .driver = { - .name = hcd_name, - }, - .probe = imx21_probe, - .remove = imx21_remove, - .suspend = NULL, - .resume = NULL, -}; - -module_platform_driver(imx21_hcd_driver); - -MODULE_DESCRIPTION("i.MX21 USB Host controller"); -MODULE_AUTHOR("Martin Fuzzey"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:imx21-hcd"); diff --git a/drivers/usb/host/imx21-hcd.h b/drivers/usb/host/imx21-hcd.h deleted file mode 100644 index 96d16752a73e..000000000000 --- a/drivers/usb/host/imx21-hcd.h +++ /dev/null @@ -1,431 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Macros and prototypes for i.MX21 - * - * Copyright (C) 2006 Loping Dog Embedded Systems - * Copyright (C) 2009 Martin Fuzzey - * Originally written by Jay Monkman <jtm@lopingdog.com> - * Ported to 2.6.30, debugged and enhanced by Martin Fuzzey - */ - -#ifndef __LINUX_IMX21_HCD_H__ -#define __LINUX_IMX21_HCD_H__ - -#ifdef CONFIG_DYNAMIC_DEBUG -#define DEBUG -#endif - -#include <linux/platform_data/usb-mx2.h> - -#define NUM_ISO_ETDS 2 -#define USB_NUM_ETD 32 -#define DMEM_SIZE 4096 - -/* Register definitions */ -#define USBOTG_HWMODE 0x00 -#define USBOTG_HWMODE_ANASDBEN (1 << 14) -#define USBOTG_HWMODE_OTGXCVR_SHIFT 6 -#define USBOTG_HWMODE_OTGXCVR_MASK (3 << 6) -#define USBOTG_HWMODE_OTGXCVR_TD_RD (0 << 6) -#define USBOTG_HWMODE_OTGXCVR_TS_RD (2 << 6) -#define USBOTG_HWMODE_OTGXCVR_TD_RS (1 << 6) -#define USBOTG_HWMODE_OTGXCVR_TS_RS (3 << 6) -#define USBOTG_HWMODE_HOSTXCVR_SHIFT 4 -#define USBOTG_HWMODE_HOSTXCVR_MASK (3 << 4) -#define USBOTG_HWMODE_HOSTXCVR_TD_RD (0 << 4) -#define USBOTG_HWMODE_HOSTXCVR_TS_RD (2 << 4) -#define USBOTG_HWMODE_HOSTXCVR_TD_RS (1 << 4) -#define USBOTG_HWMODE_HOSTXCVR_TS_RS (3 << 4) -#define USBOTG_HWMODE_CRECFG_MASK (3 << 0) -#define USBOTG_HWMODE_CRECFG_HOST (1 << 0) -#define USBOTG_HWMODE_CRECFG_FUNC (2 << 0) -#define USBOTG_HWMODE_CRECFG_HNP (3 << 0) - -#define USBOTG_CINT_STAT 0x04 -#define USBOTG_CINT_STEN 0x08 -#define USBOTG_ASHNPINT (1 << 5) -#define USBOTG_ASFCINT (1 << 4) -#define USBOTG_ASHCINT (1 << 3) -#define USBOTG_SHNPINT (1 << 2) -#define USBOTG_FCINT (1 << 1) -#define USBOTG_HCINT (1 << 0) - -#define USBOTG_CLK_CTRL 0x0c -#define USBOTG_CLK_CTRL_FUNC (1 << 2) -#define USBOTG_CLK_CTRL_HST (1 << 1) -#define USBOTG_CLK_CTRL_MAIN (1 << 0) - -#define USBOTG_RST_CTRL 0x10 -#define USBOTG_RST_RSTI2C (1 << 15) -#define USBOTG_RST_RSTCTRL (1 << 5) -#define USBOTG_RST_RSTFC (1 << 4) -#define USBOTG_RST_RSTFSKE (1 << 3) -#define USBOTG_RST_RSTRH (1 << 2) -#define USBOTG_RST_RSTHSIE (1 << 1) -#define USBOTG_RST_RSTHC (1 << 0) - -#define USBOTG_FRM_INTVL 0x14 -#define USBOTG_FRM_REMAIN 0x18 -#define USBOTG_HNP_CSR 0x1c -#define USBOTG_HNP_ISR 0x2c -#define USBOTG_HNP_IEN 0x30 - -#define USBOTG_I2C_TXCVR_REG(x) (0x100 + (x)) -#define USBOTG_I2C_XCVR_DEVAD 0x118 -#define USBOTG_I2C_SEQ_OP_REG 0x119 -#define USBOTG_I2C_SEQ_RD_STARTAD 0x11a -#define USBOTG_I2C_OP_CTRL_REG 0x11b -#define USBOTG_I2C_SCLK_TO_SCK_HPER 0x11e -#define USBOTG_I2C_MASTER_INT_REG 0x11f - -#define USBH_HOST_CTRL 0x80 -#define USBH_HOST_CTRL_HCRESET (1 << 31) -#define USBH_HOST_CTRL_SCHDOVR(x) ((x) << 16) -#define USBH_HOST_CTRL_RMTWUEN (1 << 4) -#define USBH_HOST_CTRL_HCUSBSTE_RESET (0 << 2) -#define USBH_HOST_CTRL_HCUSBSTE_RESUME (1 << 2) -#define USBH_HOST_CTRL_HCUSBSTE_OPERATIONAL (2 << 2) -#define USBH_HOST_CTRL_HCUSBSTE_SUSPEND (3 << 2) -#define USBH_HOST_CTRL_CTLBLKSR_1 (0 << 0) -#define USBH_HOST_CTRL_CTLBLKSR_2 (1 << 0) -#define USBH_HOST_CTRL_CTLBLKSR_3 (2 << 0) -#define USBH_HOST_CTRL_CTLBLKSR_4 (3 << 0) - -#define USBH_SYSISR 0x88 -#define USBH_SYSISR_PSCINT (1 << 6) -#define USBH_SYSISR_FMOFINT (1 << 5) -#define USBH_SYSISR_HERRINT (1 << 4) -#define USBH_SYSISR_RESDETINT (1 << 3) -#define USBH_SYSISR_SOFINT (1 << 2) -#define USBH_SYSISR_DONEINT (1 << 1) -#define USBH_SYSISR_SORINT (1 << 0) - -#define USBH_SYSIEN 0x8c -#define USBH_SYSIEN_PSCINT (1 << 6) -#define USBH_SYSIEN_FMOFINT (1 << 5) -#define USBH_SYSIEN_HERRINT (1 << 4) -#define USBH_SYSIEN_RESDETINT (1 << 3) -#define USBH_SYSIEN_SOFINT (1 << 2) -#define USBH_SYSIEN_DONEINT (1 << 1) -#define USBH_SYSIEN_SORINT (1 << 0) - -#define USBH_XBUFSTAT 0x98 -#define USBH_YBUFSTAT 0x9c -#define USBH_XYINTEN 0xa0 -#define USBH_XFILLSTAT 0xa8 -#define USBH_YFILLSTAT 0xac -#define USBH_ETDENSET 0xc0 -#define USBH_ETDENCLR 0xc4 -#define USBH_IMMEDINT 0xcc -#define USBH_ETDDONESTAT 0xd0 -#define USBH_ETDDONEEN 0xd4 -#define USBH_FRMNUB 0xe0 -#define USBH_LSTHRESH 0xe4 - -#define USBH_ROOTHUBA 0xe8 -#define USBH_ROOTHUBA_PWRTOGOOD_MASK (0xff) -#define USBH_ROOTHUBA_PWRTOGOOD_SHIFT (24) -#define USBH_ROOTHUBA_NOOVRCURP (1 << 12) -#define USBH_ROOTHUBA_OVRCURPM (1 << 11) -#define USBH_ROOTHUBA_DEVTYPE (1 << 10) -#define USBH_ROOTHUBA_PWRSWTMD (1 << 9) -#define USBH_ROOTHUBA_NOPWRSWT (1 << 8) -#define USBH_ROOTHUBA_NDNSTMPRT_MASK (0xff) - -#define USBH_ROOTHUBB 0xec -#define USBH_ROOTHUBB_PRTPWRCM(x) (1 << ((x) + 16)) -#define USBH_ROOTHUBB_DEVREMOVE(x) (1 << (x)) - -#define USBH_ROOTSTAT 0xf0 -#define USBH_ROOTSTAT_CLRRMTWUE (1 << 31) -#define USBH_ROOTSTAT_OVRCURCHG (1 << 17) -#define USBH_ROOTSTAT_DEVCONWUE (1 << 15) -#define USBH_ROOTSTAT_OVRCURI (1 << 1) -#define USBH_ROOTSTAT_LOCPWRS (1 << 0) - -#define USBH_PORTSTAT(x) (0xf4 + ((x) * 4)) -#define USBH_PORTSTAT_PRTRSTSC (1 << 20) -#define USBH_PORTSTAT_OVRCURIC (1 << 19) -#define USBH_PORTSTAT_PRTSTATSC (1 << 18) -#define USBH_PORTSTAT_PRTENBLSC (1 << 17) -#define USBH_PORTSTAT_CONNECTSC (1 << 16) -#define USBH_PORTSTAT_LSDEVCON (1 << 9) -#define USBH_PORTSTAT_PRTPWRST (1 << 8) -#define USBH_PORTSTAT_PRTRSTST (1 << 4) -#define USBH_PORTSTAT_PRTOVRCURI (1 << 3) -#define USBH_PORTSTAT_PRTSUSPST (1 << 2) -#define USBH_PORTSTAT_PRTENABST (1 << 1) -#define USBH_PORTSTAT_CURCONST (1 << 0) - -#define USB_DMAREV 0x800 -#define USB_DMAINTSTAT 0x804 -#define USB_DMAINTSTAT_EPERR (1 << 1) -#define USB_DMAINTSTAT_ETDERR (1 << 0) - -#define USB_DMAINTEN 0x808 -#define USB_DMAINTEN_EPERRINTEN (1 << 1) -#define USB_DMAINTEN_ETDERRINTEN (1 << 0) - -#define USB_ETDDMAERSTAT 0x80c -#define USB_EPDMAERSTAT 0x810 -#define USB_ETDDMAEN 0x820 -#define USB_EPDMAEN 0x824 -#define USB_ETDDMAXTEN 0x828 -#define USB_EPDMAXTEN 0x82c -#define USB_ETDDMAENXYT 0x830 -#define USB_EPDMAENXYT 0x834 -#define USB_ETDDMABST4EN 0x838 -#define USB_EPDMABST4EN 0x83c - -#define USB_MISCCONTROL 0x840 -#define USB_MISCCONTROL_ISOPREVFRM (1 << 3) -#define USB_MISCCONTROL_SKPRTRY (1 << 2) -#define USB_MISCCONTROL_ARBMODE (1 << 1) -#define USB_MISCCONTROL_FILTCC (1 << 0) - -#define USB_ETDDMACHANLCLR 0x848 -#define USB_EPDMACHANLCLR 0x84c -#define USB_ETDSMSA(x) (0x900 + ((x) * 4)) -#define USB_EPSMSA(x) (0x980 + ((x) * 4)) -#define USB_ETDDMABUFPTR(x) (0xa00 + ((x) * 4)) -#define USB_EPDMABUFPTR(x) (0xa80 + ((x) * 4)) - -#define USB_ETD_DWORD(x, w) (0x200 + ((x) * 16) + ((w) * 4)) -#define DW0_ADDRESS 0 -#define DW0_ENDPNT 7 -#define DW0_DIRECT 11 -#define DW0_SPEED 13 -#define DW0_FORMAT 14 -#define DW0_MAXPKTSIZ 16 -#define DW0_HALTED 27 -#define DW0_TOGCRY 28 -#define DW0_SNDNAK 30 - -#define DW1_XBUFSRTAD 0 -#define DW1_YBUFSRTAD 16 - -#define DW2_RTRYDELAY 0 -#define DW2_POLINTERV 0 -#define DW2_STARTFRM 0 -#define DW2_RELPOLPOS 8 -#define DW2_DIRPID 16 -#define DW2_BUFROUND 18 -#define DW2_DELAYINT 19 -#define DW2_DATATOG 22 -#define DW2_ERRORCNT 24 -#define DW2_COMPCODE 28 - -#define DW3_TOTBYECNT 0 -#define DW3_PKTLEN0 0 -#define DW3_COMPCODE0 12 -#define DW3_PKTLEN1 16 -#define DW3_BUFSIZE 21 -#define DW3_COMPCODE1 28 - -#define USBCTRL 0x600 -#define USBCTRL_I2C_WU_INT_STAT (1 << 27) -#define USBCTRL_OTG_WU_INT_STAT (1 << 26) -#define USBCTRL_HOST_WU_INT_STAT (1 << 25) -#define USBCTRL_FNT_WU_INT_STAT (1 << 24) -#define USBCTRL_I2C_WU_INT_EN (1 << 19) -#define USBCTRL_OTG_WU_INT_EN (1 << 18) -#define USBCTRL_HOST_WU_INT_EN (1 << 17) -#define USBCTRL_FNT_WU_INT_EN (1 << 16) -#define USBCTRL_OTC_RCV_RXDP (1 << 13) -#define USBCTRL_HOST1_BYP_TLL (1 << 12) -#define USBCTRL_OTG_BYP_VAL(x) ((x) << 10) -#define USBCTRL_HOST1_BYP_VAL(x) ((x) << 8) -#define USBCTRL_OTG_PWR_MASK (1 << 6) -#define USBCTRL_HOST1_PWR_MASK (1 << 5) -#define USBCTRL_HOST2_PWR_MASK (1 << 4) -#define USBCTRL_USB_BYP (1 << 2) -#define USBCTRL_HOST1_TXEN_OE (1 << 1) - -#define USBOTG_DMEM 0x1000 - -/* Values in TD blocks */ -#define TD_DIR_SETUP 0 -#define TD_DIR_OUT 1 -#define TD_DIR_IN 2 -#define TD_FORMAT_CONTROL 0 -#define TD_FORMAT_ISO 1 -#define TD_FORMAT_BULK 2 -#define TD_FORMAT_INT 3 -#define TD_TOGGLE_CARRY 0 -#define TD_TOGGLE_DATA0 2 -#define TD_TOGGLE_DATA1 3 - -/* control transfer states */ -#define US_CTRL_SETUP 2 -#define US_CTRL_DATA 1 -#define US_CTRL_ACK 0 - -/* bulk transfer main state and 0-length packet */ -#define US_BULK 1 -#define US_BULK0 0 - -/*ETD format description*/ -#define IMX_FMT_CTRL 0x0 -#define IMX_FMT_ISO 0x1 -#define IMX_FMT_BULK 0x2 -#define IMX_FMT_INT 0x3 - -static char fmt_urb_to_etd[4] = { -/*PIPE_ISOCHRONOUS*/ IMX_FMT_ISO, -/*PIPE_INTERRUPT*/ IMX_FMT_INT, -/*PIPE_CONTROL*/ IMX_FMT_CTRL, -/*PIPE_BULK*/ IMX_FMT_BULK -}; - -/* condition (error) CC codes and mapping (OHCI like) */ - -#define TD_CC_NOERROR 0x00 -#define TD_CC_CRC 0x01 -#define TD_CC_BITSTUFFING 0x02 -#define TD_CC_DATATOGGLEM 0x03 -#define TD_CC_STALL 0x04 -#define TD_DEVNOTRESP 0x05 -#define TD_PIDCHECKFAIL 0x06 -/*#define TD_UNEXPECTEDPID 0x07 - reserved, not active on MX2*/ -#define TD_DATAOVERRUN 0x08 -#define TD_DATAUNDERRUN 0x09 -#define TD_BUFFEROVERRUN 0x0C -#define TD_BUFFERUNDERRUN 0x0D -#define TD_SCHEDULEOVERRUN 0x0E -#define TD_NOTACCESSED 0x0F - -static const int cc_to_error[16] = { - /* No Error */ 0, - /* CRC Error */ -EILSEQ, - /* Bit Stuff */ -EPROTO, - /* Data Togg */ -EILSEQ, - /* Stall */ -EPIPE, - /* DevNotResp */ -ETIMEDOUT, - /* PIDCheck */ -EPROTO, - /* UnExpPID */ -EPROTO, - /* DataOver */ -EOVERFLOW, - /* DataUnder */ -EREMOTEIO, - /* (for hw) */ -EIO, - /* (for hw) */ -EIO, - /* BufferOver */ -ECOMM, - /* BuffUnder */ -ENOSR, - /* (for HCD) */ -ENOSPC, - /* (for HCD) */ -EALREADY -}; - -/* HCD data associated with a usb core URB */ -struct urb_priv { - struct urb *urb; - struct usb_host_endpoint *ep; - int active; - int state; - struct td *isoc_td; - int isoc_remaining; - int isoc_status; -}; - -/* HCD data associated with a usb core endpoint */ -struct ep_priv { - struct usb_host_endpoint *ep; - struct list_head td_list; - struct list_head queue; - int etd[NUM_ISO_ETDS]; - int waiting_etd; -}; - -/* isoc packet */ -struct td { - struct list_head list; - struct urb *urb; - struct usb_host_endpoint *ep; - dma_addr_t dma_handle; - void *cpu_buffer; - int len; - int frame; - int isoc_index; -}; - -/* HCD data associated with a hardware ETD */ -struct etd_priv { - struct usb_host_endpoint *ep; - struct urb *urb; - struct td *td; - struct list_head queue; - dma_addr_t dma_handle; - void *cpu_buffer; - void *bounce_buffer; - int alloc; - int len; - int dmem_size; - int dmem_offset; - int active_count; -#ifdef DEBUG - int activated_frame; - int disactivated_frame; - int last_int_frame; - int last_req_frame; - u32 submitted_dwords[4]; -#endif -}; - -/* Hardware data memory info */ -struct imx21_dmem_area { - struct usb_host_endpoint *ep; - unsigned int offset; - unsigned int size; - struct list_head list; -}; - -#ifdef DEBUG -struct debug_usage_stats { - unsigned int value; - unsigned int maximum; -}; - -struct debug_stats { - unsigned long submitted; - unsigned long completed_ok; - unsigned long completed_failed; - unsigned long unlinked; - unsigned long queue_etd; - unsigned long queue_dmem; -}; - -struct debug_isoc_trace { - int schedule_frame; - int submit_frame; - int request_len; - int done_frame; - int done_len; - int cc; - struct td *td; -}; -#endif - -/* HCD data structure */ -struct imx21 { - spinlock_t lock; - struct device *dev; - struct usb_hcd *hcd; - struct mx21_usbh_platform_data *pdata; - struct list_head dmem_list; - struct list_head queue_for_etd; /* eps queued due to etd shortage */ - struct list_head queue_for_dmem; /* etds queued due to dmem shortage */ - struct etd_priv etd[USB_NUM_ETD]; - struct clk *clk; - void __iomem *regs; -#ifdef DEBUG - struct dentry *debug_root; - struct debug_stats nonisoc_stats; - struct debug_stats isoc_stats; - struct debug_usage_stats etd_usage; - struct debug_usage_stats dmem_usage; - struct debug_isoc_trace isoc_trace[20]; - struct debug_isoc_trace isoc_trace_failed[20]; - unsigned long debug_unblocks; - int isoc_trace_index; - int isoc_trace_index_failed; -#endif -}; - -#endif diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index 3055d9abfec3..8544a2a2c1e6 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -1447,6 +1447,7 @@ static int isp116x_bus_resume(struct usb_hcd *hcd) val &= ~HCCONTROL_HCFS; val |= HCCONTROL_USB_RESUME; isp116x_write_reg32(isp116x, HCCONTROL, val); + break; case HCCONTROL_USB_RESUME: break; case HCCONTROL_USB_OPER: diff --git a/drivers/usb/host/isp1362.h b/drivers/usb/host/isp1362.h index 9bbfcc3fdd3c..208705b08d37 100644 --- a/drivers/usb/host/isp1362.h +++ b/drivers/usb/host/isp1362.h @@ -793,60 +793,6 @@ static void isp1362_write_fifo(struct isp1362_hcd *isp1362_hcd, void *buf, u16 l ISP1362_REG_NO(ISP1362_REG_##r), isp1362_read_reg16(d, r)); \ } -static void __attribute__((__unused__)) isp1362_show_regs(struct isp1362_hcd *isp1362_hcd) -{ - isp1362_show_reg(isp1362_hcd, HCREVISION); - isp1362_show_reg(isp1362_hcd, HCCONTROL); - isp1362_show_reg(isp1362_hcd, HCCMDSTAT); - isp1362_show_reg(isp1362_hcd, HCINTSTAT); - isp1362_show_reg(isp1362_hcd, HCINTENB); - isp1362_show_reg(isp1362_hcd, HCFMINTVL); - isp1362_show_reg(isp1362_hcd, HCFMREM); - isp1362_show_reg(isp1362_hcd, HCFMNUM); - isp1362_show_reg(isp1362_hcd, HCLSTHRESH); - isp1362_show_reg(isp1362_hcd, HCRHDESCA); - isp1362_show_reg(isp1362_hcd, HCRHDESCB); - isp1362_show_reg(isp1362_hcd, HCRHSTATUS); - isp1362_show_reg(isp1362_hcd, HCRHPORT1); - isp1362_show_reg(isp1362_hcd, HCRHPORT2); - - isp1362_show_reg(isp1362_hcd, HCHWCFG); - isp1362_show_reg(isp1362_hcd, HCDMACFG); - isp1362_show_reg(isp1362_hcd, HCXFERCTR); - isp1362_show_reg(isp1362_hcd, HCuPINT); - - if (in_interrupt()) - DBG(0, "%-12s[%02x]: %04x\n", "HCuPINTENB", - ISP1362_REG_NO(ISP1362_REG_HCuPINTENB), isp1362_hcd->irqenb); - else - isp1362_show_reg(isp1362_hcd, HCuPINTENB); - isp1362_show_reg(isp1362_hcd, HCCHIPID); - isp1362_show_reg(isp1362_hcd, HCSCRATCH); - isp1362_show_reg(isp1362_hcd, HCBUFSTAT); - isp1362_show_reg(isp1362_hcd, HCDIRADDR); - /* Access would advance fifo - * isp1362_show_reg(isp1362_hcd, HCDIRDATA); - */ - isp1362_show_reg(isp1362_hcd, HCISTLBUFSZ); - isp1362_show_reg(isp1362_hcd, HCISTLRATE); - isp1362_show_reg(isp1362_hcd, HCINTLBUFSZ); - isp1362_show_reg(isp1362_hcd, HCINTLBLKSZ); - isp1362_show_reg(isp1362_hcd, HCINTLDONE); - isp1362_show_reg(isp1362_hcd, HCINTLSKIP); - isp1362_show_reg(isp1362_hcd, HCINTLLAST); - isp1362_show_reg(isp1362_hcd, HCINTLCURR); - isp1362_show_reg(isp1362_hcd, HCATLBUFSZ); - isp1362_show_reg(isp1362_hcd, HCATLBLKSZ); - /* only valid after ATL_DONE interrupt - * isp1362_show_reg(isp1362_hcd, HCATLDONE); - */ - isp1362_show_reg(isp1362_hcd, HCATLSKIP); - isp1362_show_reg(isp1362_hcd, HCATLLAST); - isp1362_show_reg(isp1362_hcd, HCATLCURR); - isp1362_show_reg(isp1362_hcd, HCATLDTC); - isp1362_show_reg(isp1362_hcd, HCATLDTCTO); -} - static void isp1362_write_diraddr(struct isp1362_hcd *isp1362_hcd, u16 offset, u16 len) { len = (len + 1) & ~1; diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c index 0894f6caccb2..afd9174d83b1 100644 --- a/drivers/usb/host/max3421-hcd.c +++ b/drivers/usb/host/max3421-hcd.c @@ -1537,6 +1537,7 @@ max3421_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) __func__, urb->interval); return -EINVAL; } + break; default: break; } @@ -1847,7 +1848,7 @@ max3421_probe(struct spi_device *spi) struct max3421_hcd *max3421_hcd; struct usb_hcd *hcd = NULL; struct max3421_hcd_platform_data *pdata = NULL; - int retval = -ENOMEM; + int retval; if (spi_setup(spi) < 0) { dev_err(&spi->dev, "Unable to setup SPI bus"); @@ -1889,6 +1890,7 @@ max3421_probe(struct spi_device *spi) goto error; } + retval = -ENOMEM; hcd = usb_create_hcd(&max3421_hcd_desc, &spi->dev, dev_name(&spi->dev)); if (!hcd) { diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index 0487a4b0501e..9bbd7ddd0003 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -155,7 +155,10 @@ static struct regmap *at91_dt_syscon_sfr(void) /* * usb_hcd_at91_probe - initialize AT91-based HCDs - * Context: !in_interrupt() + * @driver: Pointer to hc driver instance + * @pdev: USB controller to probe + * + * Context: task context, might sleep * * Allocates basic resources for this USB host controller, and * then invokes the start() method for the HCD associated with it @@ -246,12 +249,14 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver, /* * usb_hcd_at91_remove - shutdown processing for AT91-based HCDs - * Context: !in_interrupt() + * @hcd: USB controller to remove + * @pdev: Platform device required for cleanup + * + * Context: task context, might sleep * * Reverses the effect of usb_hcd_at91_probe(), first invoking * the HCD's stop() method. It is always called from a thread * context, "rmmod" or something similar. - * */ static void usb_hcd_at91_remove(struct usb_hcd *hcd, struct platform_device *pdev) diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 73e13e7c2b46..1f5e69314a17 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -171,7 +171,7 @@ static int ohci_urb_enqueue ( /* 1 TD for setup, 1 for ACK, plus ... */ size = 2; - /* FALLTHROUGH */ + fallthrough; // case PIPE_INTERRUPT: // case PIPE_BULK: default: diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index 44504c1751e0..f474f2f9c1e4 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -692,6 +692,7 @@ int ohci_hub_control( case C_HUB_OVER_CURRENT: ohci_writel (ohci, RH_HS_OCIC, &ohci->regs->roothub.status); + break; case C_HUB_LOCAL_POWER: break; default: diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index 6374501ba139..0b3722770760 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -285,7 +285,9 @@ static int ohci_omap_reset(struct usb_hcd *hcd) /** * ohci_hcd_omap_probe - initialize OMAP-based HCDs - * Context: !in_interrupt() + * @pdev: USB controller to probe + * + * Context: task context, might sleep * * Allocates basic resources for this USB host controller, and * then invokes the start() method for the HCD associated with it @@ -399,8 +401,9 @@ err_put_hcd: /** * ohci_hcd_omap_remove - shutdown processing for OMAP-based HCDs - * @dev: USB Host Controller being removed - * Context: !in_interrupt() + * @pdev: USB Host Controller being removed + * + * Context: task context, might sleep * * Reverses the effect of ohci_hcd_omap_probe(), first invoking * the HCD's stop() method. It is always called from a thread diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c index 7679fb583e41..54aa5c77e549 100644 --- a/drivers/usb/host/ohci-pxa27x.c +++ b/drivers/usb/host/ohci-pxa27x.c @@ -410,12 +410,13 @@ static int ohci_pxa_of_init(struct platform_device *pdev) /** * ohci_hcd_pxa27x_probe - initialize pxa27x-based HCDs - * Context: !in_interrupt() + * @pdev: USB Host controller to probe + * + * Context: task context, might sleep * * Allocates basic resources for this USB host controller, and * then invokes the start() method for the HCD associated with it * through the hotplug entry's driver_data. - * */ static int ohci_hcd_pxa27x_probe(struct platform_device *pdev) { @@ -509,13 +510,13 @@ static int ohci_hcd_pxa27x_probe(struct platform_device *pdev) /** * ohci_hcd_pxa27x_remove - shutdown processing for pxa27x-based HCDs - * @dev: USB Host Controller being removed - * Context: !in_interrupt() + * @pdev: USB Host Controller being removed + * + * Context: task context, might sleep * * Reverses the effect of ohci_hcd_pxa27x_probe(), first invoking * the HCD's stop() method. It is always called from a thread * context, normally "rmmod", "apmd", or something similar. - * */ static int ohci_hcd_pxa27x_remove(struct platform_device *pdev) { diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c index de5e570c58ba..1bec9b585e2d 100644 --- a/drivers/usb/host/ohci-s3c2410.c +++ b/drivers/usb/host/ohci-s3c2410.c @@ -324,14 +324,13 @@ static void s3c2410_hcd_oc(struct s3c2410_hcd_info *info, int port_oc) /* * ohci_hcd_s3c2410_remove - shutdown processing for HCD * @dev: USB Host Controller being removed - * Context: !in_interrupt() + * + * Context: task context, might sleep * * Reverses the effect of ohci_hcd_3c2410_probe(), first invoking * the HCD's stop() method. It is always called from a thread * context, normally "rmmod", "apmd", or something similar. - * -*/ - + */ static int ohci_hcd_s3c2410_remove(struct platform_device *dev) { @@ -345,12 +344,13 @@ ohci_hcd_s3c2410_remove(struct platform_device *dev) /* * ohci_hcd_s3c2410_probe - initialize S3C2410-based HCDs - * Context: !in_interrupt() + * @dev: USB Host Controller to be probed + * + * Context: task context, might sleep * * Allocates basic resources for this USB host controller, and * then invokes the start() method for the HCD associated with it * through the hotplug entry's driver_data. - * */ static int ohci_hcd_s3c2410_probe(struct platform_device *dev) { diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c index 27dbbe1b28b1..4300326b3730 100644 --- a/drivers/usb/host/oxu210hp-hcd.c +++ b/drivers/usb/host/oxu210hp-hcd.c @@ -1365,6 +1365,7 @@ __acquires(oxu->lock) switch (urb->status) { case -EINPROGRESS: /* success */ urb->status = 0; + break; default: /* fault */ break; case -EREMOTEIO: /* fault or normal */ @@ -4151,8 +4152,10 @@ static struct usb_hcd *oxu_create(struct platform_device *pdev, oxu->is_otg = otg; ret = usb_add_hcd(hcd, irq, IRQF_SHARED); - if (ret < 0) + if (ret < 0) { + usb_put_hcd(hcd); return ERR_PTR(ret); + } device_wakeup_enable(hcd->self.controller); return hcd; diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c index 995bc52d2d22..eb96e1e15b71 100644 --- a/drivers/usb/host/u132-hcd.c +++ b/drivers/usb/host/u132-hcd.c @@ -208,13 +208,13 @@ struct u132 { #define ftdi_read_pcimem(pdev, member, data) usb_ftdi_elan_read_pcimem(pdev, \ offsetof(struct ohci_regs, member), 0, data); #define ftdi_write_pcimem(pdev, member, data) usb_ftdi_elan_write_pcimem(pdev, \ - offsetof(struct ohci_regs, member), 0, data); + offsetof(struct ohci_regs, member), 0, data) #define u132_read_pcimem(u132, member, data) \ usb_ftdi_elan_read_pcimem(u132->platform_dev, offsetof(struct \ - ohci_regs, member), 0, data); + ohci_regs, member), 0, data) #define u132_write_pcimem(u132, member, data) \ usb_ftdi_elan_write_pcimem(u132->platform_dev, offsetof(struct \ - ohci_regs, member), 0, data); + ohci_regs, member), 0, data) static inline struct u132 *udev_to_u132(struct u132_udev *udev) { u8 udev_number = udev->udev_number; diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index c799ca5361d4..74c497fd3476 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -1712,6 +1712,10 @@ retry: hcd->state = HC_STATE_SUSPENDED; bus_state->next_statechange = jiffies + msecs_to_jiffies(10); spin_unlock_irqrestore(&xhci->lock, flags); + + if (bus_state->bus_suspended) + usleep_range(5000, 10000); + return 0; } diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 138ba4528dd3..3589b49b6c8b 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1144,7 +1144,6 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud case USB_SPEED_WIRELESS: xhci_dbg(xhci, "FIXME xHCI doesn't support wireless speeds\n"); return -EINVAL; - break; default: /* Speed was set earlier, this shouldn't happen. */ return -EINVAL; @@ -2110,7 +2109,7 @@ static void xhci_set_hc_event_deq(struct xhci_hcd *xhci) deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, xhci->event_ring->dequeue); - if (deq == 0 && !in_interrupt()) + if (!deq) xhci_warn(xhci, "WARN something wrong with SW event ring " "dequeue ptr.\n"); /* Update HC event ring dequeue pointer */ diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index bf89172c43ca..84da8406d5b4 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -47,6 +47,7 @@ #define PCI_DEVICE_ID_INTEL_DNV_XHCI 0x19d0 #define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_XHCI 0x15b5 #define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_XHCI 0x15b6 +#define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_XHCI 0x15c1 #define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_XHCI 0x15db #define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_XHCI 0x15d4 #define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_XHCI 0x15e9 @@ -55,6 +56,7 @@ #define PCI_DEVICE_ID_INTEL_ICE_LAKE_XHCI 0x8a13 #define PCI_DEVICE_ID_INTEL_CML_XHCI 0xa3af #define PCI_DEVICE_ID_INTEL_TIGER_LAKE_XHCI 0x9a13 +#define PCI_DEVICE_ID_INTEL_MAPLE_RIDGE_XHCI 0x1138 #define PCI_DEVICE_ID_AMD_PROMONTORYA_4 0x43b9 #define PCI_DEVICE_ID_AMD_PROMONTORYA_3 0x43ba @@ -232,13 +234,15 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) if (pdev->vendor == PCI_VENDOR_ID_INTEL && (pdev->device == PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_XHCI || pdev->device == PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_XHCI || + pdev->device == PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_XHCI || pdev->device == PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_XHCI || pdev->device == PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_XHCI || pdev->device == PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_XHCI || pdev->device == PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_XHCI || pdev->device == PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_XHCI || pdev->device == PCI_DEVICE_ID_INTEL_ICE_LAKE_XHCI || - pdev->device == PCI_DEVICE_ID_INTEL_TIGER_LAKE_XHCI)) + pdev->device == PCI_DEVICE_ID_INTEL_TIGER_LAKE_XHCI || + pdev->device == PCI_DEVICE_ID_INTEL_MAPLE_RIDGE_XHCI)) xhci->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW; if (pdev->vendor == PCI_VENDOR_ID_ETRON && diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index aa2d35f98200..4d34f6005381 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -333,6 +333,9 @@ static int xhci_plat_probe(struct platform_device *pdev) if (priv && (priv->quirks & XHCI_SKIP_PHY_INIT)) hcd->skip_phy_initialization = 1; + if (priv && (priv->quirks & XHCI_SG_TRB_CACHE_SIZE_QUIRK)) + xhci->quirks |= XHCI_SG_TRB_CACHE_SIZE_QUIRK; + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret) goto disable_usb_phy; diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 167dae117f73..5677b81c0915 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -2418,6 +2418,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, xhci_warn_ratelimited(xhci, "WARN Successful completion on short TX for slot %u ep %u: needs XHCI_TRUST_TX_LENGTH quirk?\n", slot_id, ep_index); + break; case COMP_SHORT_PACKET: break; /* Completion codes for endpoint stopped state */ @@ -2962,6 +2963,7 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring, return -EINVAL; case EP_STATE_HALTED: xhci_dbg(xhci, "WARN halted endpoint, queueing URB anyway.\n"); + break; case EP_STATE_STOPPED: case EP_STATE_RUNNING: break; @@ -3325,7 +3327,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, full_len = urb->transfer_buffer_length; /* If we have scatter/gather list, we use it. */ - if (urb->num_sgs) { + if (urb->num_sgs && !(urb->transfer_flags & URB_DMA_MAP_SINGLE)) { num_sgs = urb->num_mapped_sgs; sg = urb->sg; addr = (u64) sg_dma_address(sg); diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index d4a8d0efbbc4..91ab81c3fc79 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1259,6 +1259,108 @@ EXPORT_SYMBOL_GPL(xhci_resume); /*-------------------------------------------------------------------------*/ +static int xhci_map_temp_buffer(struct usb_hcd *hcd, struct urb *urb) +{ + void *temp; + int ret = 0; + unsigned int buf_len; + enum dma_data_direction dir; + + dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + buf_len = urb->transfer_buffer_length; + + temp = kzalloc_node(buf_len, GFP_ATOMIC, + dev_to_node(hcd->self.sysdev)); + + if (usb_urb_dir_out(urb)) + sg_pcopy_to_buffer(urb->sg, urb->num_sgs, + temp, buf_len, 0); + + urb->transfer_buffer = temp; + urb->transfer_dma = dma_map_single(hcd->self.sysdev, + urb->transfer_buffer, + urb->transfer_buffer_length, + dir); + + if (dma_mapping_error(hcd->self.sysdev, + urb->transfer_dma)) { + ret = -EAGAIN; + kfree(temp); + } else { + urb->transfer_flags |= URB_DMA_MAP_SINGLE; + } + + return ret; +} + +static bool xhci_urb_temp_buffer_required(struct usb_hcd *hcd, + struct urb *urb) +{ + bool ret = false; + unsigned int i; + unsigned int len = 0; + unsigned int trb_size; + unsigned int max_pkt; + struct scatterlist *sg; + struct scatterlist *tail_sg; + + tail_sg = urb->sg; + max_pkt = usb_endpoint_maxp(&urb->ep->desc); + + if (!urb->num_sgs) + return ret; + + if (urb->dev->speed >= USB_SPEED_SUPER) + trb_size = TRB_CACHE_SIZE_SS; + else + trb_size = TRB_CACHE_SIZE_HS; + + if (urb->transfer_buffer_length != 0 && + !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) { + for_each_sg(urb->sg, sg, urb->num_sgs, i) { + len = len + sg->length; + if (i > trb_size - 2) { + len = len - tail_sg->length; + if (len < max_pkt) { + ret = true; + break; + } + + tail_sg = sg_next(tail_sg); + } + } + } + return ret; +} + +static void xhci_unmap_temp_buf(struct usb_hcd *hcd, struct urb *urb) +{ + unsigned int len; + unsigned int buf_len; + enum dma_data_direction dir; + + dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + + buf_len = urb->transfer_buffer_length; + + if (IS_ENABLED(CONFIG_HAS_DMA) && + (urb->transfer_flags & URB_DMA_MAP_SINGLE)) + dma_unmap_single(hcd->self.sysdev, + urb->transfer_dma, + urb->transfer_buffer_length, + dir); + + if (usb_urb_dir_in(urb)) + len = sg_pcopy_from_buffer(urb->sg, urb->num_sgs, + urb->transfer_buffer, + buf_len, + 0); + + urb->transfer_flags &= ~URB_DMA_MAP_SINGLE; + kfree(urb->transfer_buffer); + urb->transfer_buffer = NULL; +} + /* * Bypass the DMA mapping if URB is suitable for Immediate Transfer (IDT), * we'll copy the actual data into the TRB address register. This is limited to @@ -1268,13 +1370,37 @@ EXPORT_SYMBOL_GPL(xhci_resume); static int xhci_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) { + struct xhci_hcd *xhci; + + xhci = hcd_to_xhci(hcd); + if (xhci_urb_suitable_for_idt(urb)) return 0; + if (xhci->quirks & XHCI_SG_TRB_CACHE_SIZE_QUIRK) { + if (xhci_urb_temp_buffer_required(hcd, urb)) + return xhci_map_temp_buffer(hcd, urb); + } return usb_hcd_map_urb_for_dma(hcd, urb, mem_flags); } -/* +static void xhci_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) +{ + struct xhci_hcd *xhci; + bool unmap_temp_buf = false; + + xhci = hcd_to_xhci(hcd); + + if (urb->num_sgs && (urb->transfer_flags & URB_DMA_MAP_SINGLE)) + unmap_temp_buf = true; + + if ((xhci->quirks & XHCI_SG_TRB_CACHE_SIZE_QUIRK) && unmap_temp_buf) + xhci_unmap_temp_buf(hcd, urb); + else + usb_hcd_unmap_urb_for_dma(hcd, urb); +} + +/** * xhci_get_endpoint_index - Used for passing endpoint bitmasks between the core and * HCDs. Find the index for an endpoint given its descriptor. Use the return * value to right shift 1 for the bitmask. @@ -1476,11 +1602,9 @@ static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag ep_index = xhci_get_endpoint_index(&urb->ep->desc); ep_state = &xhci->devs[slot_id]->eps[ep_index].ep_state; - if (!HCD_HW_ACCESSIBLE(hcd)) { - if (!in_interrupt()) - xhci_dbg(xhci, "urb submitted during PCI suspend\n"); + if (!HCD_HW_ACCESSIBLE(hcd)) return -ESHUTDOWN; - } + if (xhci->devs[slot_id]->flags & VDEV_PORT_ERROR) { xhci_dbg(xhci, "Can't queue urb, port error, link inactive\n"); return -ENODEV; @@ -5329,6 +5453,7 @@ static const struct hc_driver xhci_hc_driver = { * managing i/o requests and associated device resources */ .map_urb_for_dma = xhci_map_urb_for_dma, + .unmap_urb_for_dma = xhci_unmap_urb_for_dma, .urb_enqueue = xhci_urb_enqueue, .urb_dequeue = xhci_urb_dequeue, .alloc_dev = xhci_alloc_dev, diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index ebb359ebb261..25e57bc9c3cc 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1330,6 +1330,10 @@ enum xhci_setup_dev { #define TRB_SIA (1<<31) #define TRB_FRAME_ID(p) (((p) & 0x7ff) << 20) +/* TRB cache size for xHC with TRB cache */ +#define TRB_CACHE_SIZE_HS 8 +#define TRB_CACHE_SIZE_SS 16 + struct xhci_generic_trb { __le32 field[4]; }; @@ -1878,6 +1882,7 @@ struct xhci_hcd { #define XHCI_RENESAS_FW_QUIRK BIT_ULL(36) #define XHCI_SKIP_PHY_INIT BIT_ULL(37) #define XHCI_DISABLE_SPARSE BIT_ULL(38) +#define XHCI_SG_TRB_CACHE_SIZE_QUIRK BIT_ULL(39) unsigned int num_active_eps; unsigned int limit_active_eps; diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index 6818ea689cd9..8f1144359012 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -275,3 +275,12 @@ config USB_CHAOSKEY To compile this driver as a module, choose M here: the module will be called chaoskey. + +config BRCM_USB_PINMAP + tristate "Broadcom pinmap driver support" + depends on (ARCH_BRCMSTB && PHY_BRCM_USB) || COMPILE_TEST + default ARCH_BRCMSTB && PHY_BRCM_USB + help + This option enables support for remapping some USB external + signals, which are typically on dedicated pins on the chip, + to any gpio. diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile index da39bddb0604..5f4e598573ab 100644 --- a/drivers/usb/misc/Makefile +++ b/drivers/usb/misc/Makefile @@ -31,3 +31,4 @@ obj-$(CONFIG_USB_CHAOSKEY) += chaoskey.o obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga/ obj-$(CONFIG_USB_LINK_LAYER_TEST) += lvstest.o +obj-$(CONFIG_BRCM_USB_PINMAP) += brcmstb-usb-pinmap.o diff --git a/drivers/usb/misc/apple-mfi-fastcharge.c b/drivers/usb/misc/apple-mfi-fastcharge.c index 9de0171b5177..ac8695195c13 100644 --- a/drivers/usb/misc/apple-mfi-fastcharge.c +++ b/drivers/usb/misc/apple-mfi-fastcharge.c @@ -184,10 +184,8 @@ static int mfi_fc_probe(struct usb_device *udev) return -ENODEV; mfi = kzalloc(sizeof(struct mfi_device), GFP_KERNEL); - if (!mfi) { - err = -ENOMEM; - goto error; - } + if (!mfi) + return -ENOMEM; battery_cfg.drv_data = mfi; @@ -198,17 +196,14 @@ static int mfi_fc_probe(struct usb_device *udev) if (IS_ERR(mfi->battery)) { dev_err(&udev->dev, "Can't register battery\n"); err = PTR_ERR(mfi->battery); - goto error; + kfree(mfi); + return err; } mfi->udev = usb_get_dev(udev); dev_set_drvdata(&udev->dev, mfi); return 0; - -error: - kfree(mfi); - return err; } static void mfi_fc_disconnect(struct usb_device *udev) diff --git a/drivers/usb/misc/brcmstb-usb-pinmap.c b/drivers/usb/misc/brcmstb-usb-pinmap.c new file mode 100644 index 000000000000..b3cfe8666ea7 --- /dev/null +++ b/drivers/usb/misc/brcmstb-usb-pinmap.c @@ -0,0 +1,351 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2020, Broadcom */ + +#include <linux/init.h> +#include <linux/types.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/device.h> +#include <linux/of.h> +#include <linux/kernel.h> +#include <linux/kdebug.h> +#include <linux/gpio/consumer.h> + +struct out_pin { + u32 enable_mask; + u32 value_mask; + u32 changed_mask; + u32 clr_changed_mask; + struct gpio_desc *gpiod; + const char *name; +}; + +struct in_pin { + u32 enable_mask; + u32 value_mask; + struct gpio_desc *gpiod; + const char *name; + struct brcmstb_usb_pinmap_data *pdata; +}; + +struct brcmstb_usb_pinmap_data { + void __iomem *regs; + int in_count; + struct in_pin *in_pins; + int out_count; + struct out_pin *out_pins; +}; + + +static void pinmap_set(void __iomem *reg, u32 mask) +{ + u32 val; + + val = readl(reg); + val |= mask; + writel(val, reg); +} + +static void pinmap_unset(void __iomem *reg, u32 mask) +{ + u32 val; + + val = readl(reg); + val &= ~mask; + writel(val, reg); +} + +static void sync_in_pin(struct in_pin *pin) +{ + u32 val; + + val = gpiod_get_value(pin->gpiod); + if (val) + pinmap_set(pin->pdata->regs, pin->value_mask); + else + pinmap_unset(pin->pdata->regs, pin->value_mask); +} + +/* + * Interrupt from override register, propagate from override bit + * to GPIO. + */ +static irqreturn_t brcmstb_usb_pinmap_ovr_isr(int irq, void *dev_id) +{ + struct brcmstb_usb_pinmap_data *pdata = dev_id; + struct out_pin *pout; + u32 val; + u32 bit; + int x; + + pr_debug("%s: reg: 0x%x\n", __func__, readl(pdata->regs)); + pout = pdata->out_pins; + for (x = 0; x < pdata->out_count; x++) { + val = readl(pdata->regs); + if (val & pout->changed_mask) { + pinmap_set(pdata->regs, pout->clr_changed_mask); + pinmap_unset(pdata->regs, pout->clr_changed_mask); + bit = val & pout->value_mask; + gpiod_set_value(pout->gpiod, bit ? 1 : 0); + pr_debug("%s: %s bit changed state to %d\n", + __func__, pout->name, bit ? 1 : 0); + } + } + return IRQ_HANDLED; +} + +/* + * Interrupt from GPIO, propagate from GPIO to override bit. + */ +static irqreturn_t brcmstb_usb_pinmap_gpio_isr(int irq, void *dev_id) +{ + struct in_pin *pin = dev_id; + + pr_debug("%s: %s pin changed state\n", __func__, pin->name); + sync_in_pin(pin); + return IRQ_HANDLED; +} + + +static void get_pin_counts(struct device_node *dn, int *in_count, + int *out_count) +{ + int in; + int out; + + *in_count = 0; + *out_count = 0; + in = of_property_count_strings(dn, "brcm,in-functions"); + if (in < 0) + return; + out = of_property_count_strings(dn, "brcm,out-functions"); + if (out < 0) + return; + *in_count = in; + *out_count = out; +} + +static int parse_pins(struct device *dev, struct device_node *dn, + struct brcmstb_usb_pinmap_data *pdata) +{ + struct out_pin *pout; + struct in_pin *pin; + int index; + int res; + int x; + + pin = pdata->in_pins; + for (x = 0, index = 0; x < pdata->in_count; x++) { + pin->gpiod = devm_gpiod_get_index(dev, "in", x, GPIOD_IN); + if (IS_ERR(pin->gpiod)) { + dev_err(dev, "Error getting gpio %s\n", pin->name); + return PTR_ERR(pin->gpiod); + + } + res = of_property_read_string_index(dn, "brcm,in-functions", x, + &pin->name); + if (res < 0) { + dev_err(dev, "Error getting brcm,in-functions for %s\n", + pin->name); + return res; + } + res = of_property_read_u32_index(dn, "brcm,in-masks", index++, + &pin->enable_mask); + if (res < 0) { + dev_err(dev, "Error getting 1st brcm,in-masks for %s\n", + pin->name); + return res; + } + res = of_property_read_u32_index(dn, "brcm,in-masks", index++, + &pin->value_mask); + if (res < 0) { + dev_err(dev, "Error getting 2nd brcm,in-masks for %s\n", + pin->name); + return res; + } + pin->pdata = pdata; + pin++; + } + pout = pdata->out_pins; + for (x = 0, index = 0; x < pdata->out_count; x++) { + pout->gpiod = devm_gpiod_get_index(dev, "out", x, + GPIOD_OUT_HIGH); + if (IS_ERR(pout->gpiod)) { + dev_err(dev, "Error getting gpio %s\n", pin->name); + return PTR_ERR(pout->gpiod); + } + res = of_property_read_string_index(dn, "brcm,out-functions", x, + &pout->name); + if (res < 0) { + dev_err(dev, "Error getting brcm,out-functions for %s\n", + pout->name); + return res; + } + res = of_property_read_u32_index(dn, "brcm,out-masks", index++, + &pout->enable_mask); + if (res < 0) { + dev_err(dev, "Error getting 1st brcm,out-masks for %s\n", + pout->name); + return res; + } + res = of_property_read_u32_index(dn, "brcm,out-masks", index++, + &pout->value_mask); + if (res < 0) { + dev_err(dev, "Error getting 2nd brcm,out-masks for %s\n", + pout->name); + return res; + } + res = of_property_read_u32_index(dn, "brcm,out-masks", index++, + &pout->changed_mask); + if (res < 0) { + dev_err(dev, "Error getting 3rd brcm,out-masks for %s\n", + pout->name); + return res; + } + res = of_property_read_u32_index(dn, "brcm,out-masks", index++, + &pout->clr_changed_mask); + if (res < 0) { + dev_err(dev, "Error getting 4th out-masks for %s\n", + pout->name); + return res; + } + pout++; + } + return 0; +} + +static void sync_all_pins(struct brcmstb_usb_pinmap_data *pdata) +{ + struct out_pin *pout; + struct in_pin *pin; + int val; + int x; + + /* + * Enable the override, clear any changed condition and + * propagate the state to the GPIO for all out pins. + */ + pout = pdata->out_pins; + for (x = 0; x < pdata->out_count; x++) { + pinmap_set(pdata->regs, pout->enable_mask); + pinmap_set(pdata->regs, pout->clr_changed_mask); + pinmap_unset(pdata->regs, pout->clr_changed_mask); + val = readl(pdata->regs) & pout->value_mask; + gpiod_set_value(pout->gpiod, val ? 1 : 0); + pout++; + } + + /* sync and enable all in pins. */ + pin = pdata->in_pins; + for (x = 0; x < pdata->in_count; x++) { + sync_in_pin(pin); + pinmap_set(pdata->regs, pin->enable_mask); + pin++; + } +} + +static int __init brcmstb_usb_pinmap_probe(struct platform_device *pdev) +{ + struct device_node *dn = pdev->dev.of_node; + struct brcmstb_usb_pinmap_data *pdata; + struct in_pin *pin; + struct resource *r; + int out_count; + int in_count; + int err; + int irq; + int x; + + get_pin_counts(dn, &in_count, &out_count); + if ((in_count + out_count) == 0) + return -EINVAL; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + pdata = devm_kzalloc(&pdev->dev, + sizeof(*pdata) + + (sizeof(struct in_pin) * in_count) + + (sizeof(struct out_pin) * out_count), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + pdata->in_count = in_count; + pdata->out_count = out_count; + pdata->in_pins = (struct in_pin *)(pdata + 1); + pdata->out_pins = (struct out_pin *)(pdata->in_pins + in_count); + + pdata->regs = devm_ioremap(&pdev->dev, r->start, resource_size(r)); + if (!pdata->regs) + return -ENOMEM; + platform_set_drvdata(pdev, pdata); + + err = parse_pins(&pdev->dev, dn, pdata); + if (err) + return err; + + sync_all_pins(pdata); + + if (out_count) { + + /* Enable interrupt for out pins */ + irq = platform_get_irq(pdev, 0); + err = devm_request_irq(&pdev->dev, irq, + brcmstb_usb_pinmap_ovr_isr, + IRQF_TRIGGER_RISING, + pdev->name, pdata); + if (err < 0) { + dev_err(&pdev->dev, "Error requesting IRQ\n"); + return err; + } + } + + for (x = 0, pin = pdata->in_pins; x < pdata->in_count; x++, pin++) { + irq = gpiod_to_irq(pin->gpiod); + if (irq < 0) { + dev_err(&pdev->dev, "Error getting IRQ for %s pin\n", + pin->name); + return irq; + } + err = devm_request_irq(&pdev->dev, irq, + brcmstb_usb_pinmap_gpio_isr, + IRQF_SHARED | IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING, + pdev->name, pin); + if (err < 0) { + dev_err(&pdev->dev, "Error requesting IRQ for %s pin\n", + pin->name); + return err; + } + } + + dev_dbg(&pdev->dev, "Driver probe succeeded\n"); + dev_dbg(&pdev->dev, "In pin count: %d, out pin count: %d\n", + pdata->in_count, pdata->out_count); + return 0; +} + + +static const struct of_device_id brcmstb_usb_pinmap_of_match[] = { + { .compatible = "brcm,usb-pinmap" }, + { }, +}; + +static struct platform_driver brcmstb_usb_pinmap_driver = { + .driver = { + .name = "brcm-usb-pinmap", + .of_match_table = brcmstb_usb_pinmap_of_match, + }, +}; + +static int __init brcmstb_usb_pinmap_init(void) +{ + return platform_driver_probe(&brcmstb_usb_pinmap_driver, + brcmstb_usb_pinmap_probe); +} + +module_init(brcmstb_usb_pinmap_init); +MODULE_AUTHOR("Al Cooper <alcooperx@gmail.com>"); +MODULE_DESCRIPTION("Broadcom USB Pinmap Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index 70ec29681526..efbd317f2f25 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -384,7 +384,6 @@ static ssize_t iowarrior_write(struct file *file, retval = usb_set_report(dev->interface, 2, 0, buf, count); kfree(buf); goto exit; - break; case USB_DEVICE_ID_CODEMERCS_IOW56: case USB_DEVICE_ID_CODEMERCS_IOW56AM: case USB_DEVICE_ID_CODEMERCS_IOW28: @@ -454,14 +453,12 @@ static ssize_t iowarrior_write(struct file *file, retval = count; usb_free_urb(int_out_urb); goto exit; - break; default: /* what do we have here ? An unsupported Product-ID ? */ dev_err(&dev->interface->dev, "%s - not supported for product=0x%x\n", __func__, dev->product_id); retval = -EFAULT; goto exit; - break; } error: usb_free_coherent(dev->udev, dev->report_size, buf, diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index ba655b4af4fc..1c9e09138c10 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -797,7 +797,7 @@ static int tower_probe(struct usb_interface *interface, const struct usb_device_ &get_version_reply, sizeof(get_version_reply), 1000, GFP_KERNEL); - if (!result) { + if (result) { dev_err(idev, "get version request failed: %d\n", result); retval = result; goto error; diff --git a/drivers/usb/misc/sisusbvga/Kconfig b/drivers/usb/misc/sisusbvga/Kconfig index 655d9cb0651a..c12cdd015410 100644 --- a/drivers/usb/misc/sisusbvga/Kconfig +++ b/drivers/usb/misc/sisusbvga/Kconfig @@ -16,7 +16,7 @@ config USB_SISUSBVGA config USB_SISUSBVGA_CON bool "Text console and mode switching support" if USB_SISUSBVGA - depends on VT + depends on VT && BROKEN select FONT_8x16 help Say Y here if you want a VGA text console via the USB dongle or diff --git a/drivers/usb/misc/yurex.c b/drivers/usb/misc/yurex.c index e3165d79b5f6..73ebfa6e9715 100644 --- a/drivers/usb/misc/yurex.c +++ b/drivers/usb/misc/yurex.c @@ -137,6 +137,7 @@ static void yurex_interrupt(struct urb *urb) dev_err(&dev->interface->dev, "%s - overflow with length %d, actual length is %d\n", __func__, YUREX_BUF_SIZE, dev->urb->actual_length); + return; case -ECONNRESET: case -ENOENT: case -ESHUTDOWN: diff --git a/drivers/usb/mtu3/mtu3_debug.h b/drivers/usb/mtu3/mtu3_debug.h index 3084c46017c3..9a36134b322d 100644 --- a/drivers/usb/mtu3/mtu3_debug.h +++ b/drivers/usb/mtu3/mtu3_debug.h @@ -19,7 +19,6 @@ struct ssusb_mtk; struct mtu3_regset { char name[MTU3_DEBUGFS_NAME_LEN]; struct debugfs_regset32 regset; - size_t nregs; }; struct mtu3_file_map { diff --git a/drivers/usb/mtu3/mtu3_debugfs.c b/drivers/usb/mtu3/mtu3_debugfs.c index fdeade6254ae..7537bfd651af 100644 --- a/drivers/usb/mtu3/mtu3_debugfs.c +++ b/drivers/usb/mtu3/mtu3_debugfs.c @@ -127,7 +127,7 @@ static void mtu3_debugfs_regset(struct mtu3 *mtu, void __iomem *base, struct debugfs_regset32 *regset; struct mtu3_regset *mregs; - mregs = devm_kzalloc(mtu->dev, sizeof(*regset), GFP_KERNEL); + mregs = devm_kzalloc(mtu->dev, sizeof(*mregs), GFP_KERNEL); if (!mregs) return; diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index c26683a2702b..c42937692207 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -467,6 +467,7 @@ static void musb_do_idle(struct timer_list *t) fallthrough; case OTG_STATE_A_IDLE: tusb_musb_set_vbus(musb, 0); + break; default: break; } diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index ef4787cd3d37..52eebcb88c1f 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -144,7 +144,7 @@ config USB_MV_OTG depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't be 'y' select USB_PHY help - Say Y here if you want to build Marvell USB OTG transciever + Say Y here if you want to build Marvell USB OTG transceiver driver in kernel (including PXA and MMP series). This driver implements role switch between EHCI host driver and gadget driver. diff --git a/drivers/usb/phy/phy-isp1301-omap.c b/drivers/usb/phy/phy-isp1301-omap.c index 4a6462c92ef2..02bb7ddd4bd6 100644 --- a/drivers/usb/phy/phy-isp1301-omap.c +++ b/drivers/usb/phy/phy-isp1301-omap.c @@ -1208,9 +1208,6 @@ static int isp1301_remove(struct i2c_client *i2c) #ifdef CONFIG_USB_OTG otg_unbind(isp); #endif - if (machine_is_omap_h2()) - gpio_free(2); - set_bit(WORK_STOP, &isp->todo); del_timer_sync(&isp->timer); flush_work(&isp->work); @@ -1480,6 +1477,7 @@ isp1301_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { int status; struct isp1301 *isp; + int irq; if (the_transceiver) return 0; @@ -1543,20 +1541,27 @@ isp1301_probe(struct i2c_client *i2c, const struct i2c_device_id *id) #endif if (machine_is_omap_h2()) { + struct gpio_desc *gpiod; + /* full speed signaling by default */ isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_SPEED); isp1301_set_bits(isp, ISP1301_MODE_CONTROL_2, MC2_SPD_SUSP_CTRL); - /* IRQ wired at M14 */ - omap_cfg_reg(M14_1510_GPIO2); - if (gpio_request(2, "isp1301") == 0) - gpio_direction_input(2); + gpiod = devm_gpiod_get(&i2c->dev, NULL, GPIOD_IN); + if (IS_ERR(gpiod)) { + dev_err(&i2c->dev, "cannot obtain H2 GPIO\n"); + goto fail; + } + gpiod_set_consumer_name(gpiod, "isp1301"); + irq = gpiod_to_irq(gpiod); isp->irq_type = IRQF_TRIGGER_FALLING; + } else { + irq = i2c->irq; } - status = request_irq(i2c->irq, isp1301_irq, + status = request_irq(irq, isp1301_irq, isp->irq_type, DRIVER_NAME, isp); if (status < 0) { dev_dbg(&i2c->dev, "can't get IRQ %d, err %d\n", @@ -1566,13 +1571,13 @@ isp1301_probe(struct i2c_client *i2c, const struct i2c_device_id *id) isp->phy.dev = &i2c->dev; isp->phy.label = DRIVER_NAME; - isp->phy.set_power = isp1301_set_power, + isp->phy.set_power = isp1301_set_power; isp->phy.otg->usb_phy = &isp->phy; - isp->phy.otg->set_host = isp1301_set_host, - isp->phy.otg->set_peripheral = isp1301_set_peripheral, - isp->phy.otg->start_srp = isp1301_start_srp, - isp->phy.otg->start_hnp = isp1301_start_hnp, + isp->phy.otg->set_host = isp1301_set_host; + isp->phy.otg->set_peripheral = isp1301_set_peripheral; + isp->phy.otg->start_srp = isp1301_start_srp; + isp->phy.otg->start_hnp = isp1301_start_hnp; enable_vbus_draw(isp, 0); power_down(isp); diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig index 4007fa25a8ff..a21ff5ab6df9 100644 --- a/drivers/usb/serial/Kconfig +++ b/drivers/usb/serial/Kconfig @@ -298,12 +298,12 @@ config USB_SERIAL_IUU module will be called iuu_phoenix.o config USB_SERIAL_KEYSPAN_PDA - tristate "USB Keyspan PDA Single Port Serial Driver" + tristate "USB Keyspan PDA / Xircom Single Port Serial Driver" select USB_EZUSB_FX2 help - Say Y here if you want to use a Keyspan PDA single port USB to - serial converter device. This driver makes use of firmware - developed from scratch by Brian Warner. + Say Y here if you want to use a Keyspan PDA, Xircom or Entrega single + port USB to serial converter device. This driver makes use of + firmware developed from scratch by Brian Warner. To compile this driver as a module, choose M here: the module will be called keyspan_pda. @@ -538,17 +538,6 @@ config USB_SERIAL_CYBERJACK If unsure, say N. -config USB_SERIAL_XIRCOM - tristate "USB Xircom / Entrega Single Port Serial Driver" - select USB_EZUSB_FX2 - help - Say Y here if you want to use a Xircom or Entrega single port USB to - serial converter device. This driver makes use of firmware - developed from scratch by Brian Warner. - - To compile this driver as a module, choose M here: the - module will be called keyspan_pda. - config USB_SERIAL_WWAN tristate diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile index 2d491e434f11..50c53aed787a 100644 --- a/drivers/usb/serial/Makefile +++ b/drivers/usb/serial/Makefile @@ -61,5 +61,4 @@ obj-$(CONFIG_USB_SERIAL_UPD78F0730) += upd78f0730.o obj-$(CONFIG_USB_SERIAL_VISOR) += visor.o obj-$(CONFIG_USB_SERIAL_WISHBONE) += wishbone-serial.o obj-$(CONFIG_USB_SERIAL_WHITEHEAT) += whiteheat.o -obj-$(CONFIG_USB_SERIAL_XIRCOM) += keyspan_pda.o obj-$(CONFIG_USB_SERIAL_XSENS_MT) += xsens_mt.o diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index d0c05aa8a0d6..fbb10dfc56e3 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -31,9 +31,6 @@ */ static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *); static void cp210x_close(struct usb_serial_port *); -static void cp210x_get_termios(struct tty_struct *, struct usb_serial_port *); -static void cp210x_get_termios_port(struct usb_serial_port *port, - tcflag_t *cflagp, unsigned int *baudp); static void cp210x_change_speed(struct tty_struct *, struct usb_serial_port *, struct ktermios *); static void cp210x_set_termios(struct tty_struct *, struct usb_serial_port *, @@ -49,7 +46,7 @@ static void cp210x_disconnect(struct usb_serial *); static void cp210x_release(struct usb_serial *); static int cp210x_port_probe(struct usb_serial_port *); static int cp210x_port_remove(struct usb_serial_port *); -static void cp210x_dtr_rts(struct usb_serial_port *p, int on); +static void cp210x_dtr_rts(struct usb_serial_port *port, int on); static void cp210x_process_read_urb(struct urb *urb); static void cp210x_enable_event_mode(struct usb_serial_port *port); static void cp210x_disable_event_mode(struct usb_serial_port *port); @@ -267,7 +264,6 @@ enum cp210x_event_state { struct cp210x_port_private { u8 bInterfaceNumber; - bool has_swapped_line_ctl; bool event_mode; enum cp210x_event_state event_state; u8 lsr; @@ -559,14 +555,8 @@ static int cp210x_read_reg_block(struct usb_serial_port *port, u8 req, int result; dmabuf = kmalloc(bufsize, GFP_KERNEL); - if (!dmabuf) { - /* - * FIXME Some callers don't bother to check for error, - * at least give them consistent junk until they are fixed - */ - memset(buf, 0, bufsize); + if (!dmabuf) return -ENOMEM; - } result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), req, REQTYPE_INTERFACE_TO_HOST, 0, @@ -580,12 +570,6 @@ static int cp210x_read_reg_block(struct usb_serial_port *port, u8 req, req, bufsize, result); if (result >= 0) result = -EIO; - - /* - * FIXME Some callers don't bother to check for error, - * at least give them consistent junk until they are fixed - */ - memset(buf, 0, bufsize); } kfree(dmabuf); @@ -594,46 +578,6 @@ static int cp210x_read_reg_block(struct usb_serial_port *port, u8 req, } /* - * Reads any 32-bit CP210X_ register identified by req. - */ -static int cp210x_read_u32_reg(struct usb_serial_port *port, u8 req, u32 *val) -{ - __le32 le32_val; - int err; - - err = cp210x_read_reg_block(port, req, &le32_val, sizeof(le32_val)); - if (err) { - /* - * FIXME Some callers don't bother to check for error, - * at least give them consistent junk until they are fixed - */ - *val = 0; - return err; - } - - *val = le32_to_cpu(le32_val); - - return 0; -} - -/* - * Reads any 16-bit CP210X_ register identified by req. - */ -static int cp210x_read_u16_reg(struct usb_serial_port *port, u8 req, u16 *val) -{ - __le16 le16_val; - int err; - - err = cp210x_read_reg_block(port, req, &le16_val, sizeof(le16_val)); - if (err) - return err; - - *val = le16_to_cpu(le16_val); - - return 0; -} - -/* * Reads any 8-bit CP210X_ register identified by req. */ static int cp210x_read_u8_reg(struct usb_serial_port *port, u8 req, u8 *val) @@ -780,59 +724,6 @@ static int cp210x_write_vendor_block(struct usb_serial *serial, u8 type, } #endif -/* - * Detect CP2108 GET_LINE_CTL bug and activate workaround. - * Write a known good value 0x800, read it back. - * If it comes back swapped the bug is detected. - * Preserve the original register value. - */ -static int cp210x_detect_swapped_line_ctl(struct usb_serial_port *port) -{ - struct cp210x_port_private *port_priv = usb_get_serial_port_data(port); - u16 line_ctl_save; - u16 line_ctl_test; - int err; - - err = cp210x_read_u16_reg(port, CP210X_GET_LINE_CTL, &line_ctl_save); - if (err) - return err; - - err = cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, 0x800); - if (err) - return err; - - err = cp210x_read_u16_reg(port, CP210X_GET_LINE_CTL, &line_ctl_test); - if (err) - return err; - - if (line_ctl_test == 8) { - port_priv->has_swapped_line_ctl = true; - line_ctl_save = swab16(line_ctl_save); - } - - return cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, line_ctl_save); -} - -/* - * Must always be called instead of cp210x_read_u16_reg(CP210X_GET_LINE_CTL) - * to workaround cp2108 bug and get correct value. - */ -static int cp210x_get_line_ctl(struct usb_serial_port *port, u16 *ctl) -{ - struct cp210x_port_private *port_priv = usb_get_serial_port_data(port); - int err; - - err = cp210x_read_u16_reg(port, CP210X_GET_LINE_CTL, ctl); - if (err) - return err; - - /* Workaround swapped bytes in 16-bit value from CP210X_GET_LINE_CTL */ - if (port_priv->has_swapped_line_ctl) - *ctl = swab16(*ctl); - - return 0; -} - static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *port) { struct cp210x_port_private *port_priv = usb_get_serial_port_data(port); @@ -844,16 +735,8 @@ static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *port) return result; } - /* Configure the termios structure */ - cp210x_get_termios(tty, port); - - if (tty) { - /* The baud rate must be initialised on cp2104 */ - cp210x_change_speed(tty, port, NULL); - - if (I_INPCK(tty)) - cp210x_enable_event_mode(port); - } + if (tty) + cp210x_set_termios(tty, port, NULL); result = usb_serial_generic_open(tty, port); if (result) @@ -1032,167 +915,6 @@ static bool cp210x_tx_empty(struct usb_serial_port *port) return !count; } -/* - * cp210x_get_termios - * Reads the baud rate, data bits, parity, stop bits and flow control mode - * from the device, corrects any unsupported values, and configures the - * termios structure to reflect the state of the device - */ -static void cp210x_get_termios(struct tty_struct *tty, - struct usb_serial_port *port) -{ - unsigned int baud; - - if (tty) { - cp210x_get_termios_port(tty->driver_data, - &tty->termios.c_cflag, &baud); - tty_encode_baud_rate(tty, baud, baud); - } else { - tcflag_t cflag; - cflag = 0; - cp210x_get_termios_port(port, &cflag, &baud); - } -} - -/* - * cp210x_get_termios_port - * This is the heart of cp210x_get_termios which always uses a &usb_serial_port. - */ -static void cp210x_get_termios_port(struct usb_serial_port *port, - tcflag_t *cflagp, unsigned int *baudp) -{ - struct device *dev = &port->dev; - tcflag_t cflag; - struct cp210x_flow_ctl flow_ctl; - u32 baud; - u16 bits; - u32 ctl_hs; - u32 flow_repl; - - cp210x_read_u32_reg(port, CP210X_GET_BAUDRATE, &baud); - - dev_dbg(dev, "%s - baud rate = %d\n", __func__, baud); - *baudp = baud; - - cflag = *cflagp; - - cp210x_get_line_ctl(port, &bits); - cflag &= ~CSIZE; - switch (bits & BITS_DATA_MASK) { - case BITS_DATA_5: - dev_dbg(dev, "%s - data bits = 5\n", __func__); - cflag |= CS5; - break; - case BITS_DATA_6: - dev_dbg(dev, "%s - data bits = 6\n", __func__); - cflag |= CS6; - break; - case BITS_DATA_7: - dev_dbg(dev, "%s - data bits = 7\n", __func__); - cflag |= CS7; - break; - case BITS_DATA_8: - dev_dbg(dev, "%s - data bits = 8\n", __func__); - cflag |= CS8; - break; - case BITS_DATA_9: - dev_dbg(dev, "%s - data bits = 9 (not supported, using 8 data bits)\n", __func__); - cflag |= CS8; - bits &= ~BITS_DATA_MASK; - bits |= BITS_DATA_8; - cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, bits); - break; - default: - dev_dbg(dev, "%s - Unknown number of data bits, using 8\n", __func__); - cflag |= CS8; - bits &= ~BITS_DATA_MASK; - bits |= BITS_DATA_8; - cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, bits); - break; - } - - switch (bits & BITS_PARITY_MASK) { - case BITS_PARITY_NONE: - dev_dbg(dev, "%s - parity = NONE\n", __func__); - cflag &= ~PARENB; - break; - case BITS_PARITY_ODD: - dev_dbg(dev, "%s - parity = ODD\n", __func__); - cflag |= (PARENB|PARODD); - break; - case BITS_PARITY_EVEN: - dev_dbg(dev, "%s - parity = EVEN\n", __func__); - cflag &= ~PARODD; - cflag |= PARENB; - break; - case BITS_PARITY_MARK: - dev_dbg(dev, "%s - parity = MARK\n", __func__); - cflag |= (PARENB|PARODD|CMSPAR); - break; - case BITS_PARITY_SPACE: - dev_dbg(dev, "%s - parity = SPACE\n", __func__); - cflag &= ~PARODD; - cflag |= (PARENB|CMSPAR); - break; - default: - dev_dbg(dev, "%s - Unknown parity mode, disabling parity\n", __func__); - cflag &= ~PARENB; - bits &= ~BITS_PARITY_MASK; - cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, bits); - break; - } - - cflag &= ~CSTOPB; - switch (bits & BITS_STOP_MASK) { - case BITS_STOP_1: - dev_dbg(dev, "%s - stop bits = 1\n", __func__); - break; - case BITS_STOP_1_5: - dev_dbg(dev, "%s - stop bits = 1.5 (not supported, using 1 stop bit)\n", __func__); - bits &= ~BITS_STOP_MASK; - cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, bits); - break; - case BITS_STOP_2: - dev_dbg(dev, "%s - stop bits = 2\n", __func__); - cflag |= CSTOPB; - break; - default: - dev_dbg(dev, "%s - Unknown number of stop bits, using 1 stop bit\n", __func__); - bits &= ~BITS_STOP_MASK; - cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, bits); - break; - } - - cp210x_read_reg_block(port, CP210X_GET_FLOW, &flow_ctl, - sizeof(flow_ctl)); - ctl_hs = le32_to_cpu(flow_ctl.ulControlHandshake); - if (ctl_hs & CP210X_SERIAL_CTS_HANDSHAKE) { - dev_dbg(dev, "%s - flow control = CRTSCTS\n", __func__); - /* - * When the port is closed, the CP210x hardware disables - * auto-RTS and RTS is deasserted but it leaves auto-CTS when - * in hardware flow control mode. When re-opening the port, if - * auto-CTS is enabled on the cp210x, then auto-RTS must be - * re-enabled in the driver. - */ - flow_repl = le32_to_cpu(flow_ctl.ulFlowReplace); - flow_repl &= ~CP210X_SERIAL_RTS_MASK; - flow_repl |= CP210X_SERIAL_RTS_SHIFT(CP210X_SERIAL_RTS_FLOW_CTL); - flow_ctl.ulFlowReplace = cpu_to_le32(flow_repl); - cp210x_write_reg_block(port, - CP210X_SET_FLOW, - &flow_ctl, - sizeof(flow_ctl)); - - cflag |= CRTSCTS; - } else { - dev_dbg(dev, "%s - flow control = NONE\n", __func__); - cflag &= ~CRTSCTS; - } - - *cflagp = cflag; -} - struct cp210x_rate { speed_t rate; speed_t high; @@ -1352,127 +1074,121 @@ static void cp210x_disable_event_mode(struct usb_serial_port *port) port_priv->event_mode = false; } +static bool cp210x_termios_change(const struct ktermios *a, const struct ktermios *b) +{ + bool iflag_change; + + iflag_change = ((a->c_iflag ^ b->c_iflag) & INPCK); + + return tty_termios_hw_change(a, b) || iflag_change; +} + +static void cp210x_set_flow_control(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) +{ + struct cp210x_flow_ctl flow_ctl; + u32 flow_repl; + u32 ctl_hs; + int ret; + + if (old_termios && C_CRTSCTS(tty) == (old_termios->c_cflag & CRTSCTS)) + return; + + ret = cp210x_read_reg_block(port, CP210X_GET_FLOW, &flow_ctl, + sizeof(flow_ctl)); + if (ret) + return; + + ctl_hs = le32_to_cpu(flow_ctl.ulControlHandshake); + flow_repl = le32_to_cpu(flow_ctl.ulFlowReplace); + + ctl_hs &= ~CP210X_SERIAL_DSR_HANDSHAKE; + ctl_hs &= ~CP210X_SERIAL_DCD_HANDSHAKE; + ctl_hs &= ~CP210X_SERIAL_DSR_SENSITIVITY; + ctl_hs &= ~CP210X_SERIAL_DTR_MASK; + ctl_hs |= CP210X_SERIAL_DTR_SHIFT(CP210X_SERIAL_DTR_ACTIVE); + + if (C_CRTSCTS(tty)) { + ctl_hs |= CP210X_SERIAL_CTS_HANDSHAKE; + flow_repl &= ~CP210X_SERIAL_RTS_MASK; + flow_repl |= CP210X_SERIAL_RTS_SHIFT(CP210X_SERIAL_RTS_FLOW_CTL); + } else { + ctl_hs &= ~CP210X_SERIAL_CTS_HANDSHAKE; + flow_repl &= ~CP210X_SERIAL_RTS_MASK; + flow_repl |= CP210X_SERIAL_RTS_SHIFT(CP210X_SERIAL_RTS_ACTIVE); + } + + dev_dbg(&port->dev, "%s - ulControlHandshake=0x%08x, ulFlowReplace=0x%08x\n", + __func__, ctl_hs, flow_repl); + + flow_ctl.ulControlHandshake = cpu_to_le32(ctl_hs); + flow_ctl.ulFlowReplace = cpu_to_le32(flow_repl); + + cp210x_write_reg_block(port, CP210X_SET_FLOW, &flow_ctl, + sizeof(flow_ctl)); +} + static void cp210x_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { - struct device *dev = &port->dev; - unsigned int cflag, old_cflag; + struct cp210x_serial_private *priv = usb_get_serial_data(port->serial); u16 bits; + int ret; - cflag = tty->termios.c_cflag; - old_cflag = old_termios->c_cflag; + if (old_termios && !cp210x_termios_change(&tty->termios, old_termios)) + return; - if (tty->termios.c_ospeed != old_termios->c_ospeed) + if (!old_termios || tty->termios.c_ospeed != old_termios->c_ospeed) cp210x_change_speed(tty, port, old_termios); - /* If the number of data bits is to be updated */ - if ((cflag & CSIZE) != (old_cflag & CSIZE)) { - cp210x_get_line_ctl(port, &bits); - bits &= ~BITS_DATA_MASK; - switch (cflag & CSIZE) { - case CS5: - bits |= BITS_DATA_5; - dev_dbg(dev, "%s - data bits = 5\n", __func__); - break; - case CS6: - bits |= BITS_DATA_6; - dev_dbg(dev, "%s - data bits = 6\n", __func__); - break; - case CS7: - bits |= BITS_DATA_7; - dev_dbg(dev, "%s - data bits = 7\n", __func__); - break; - case CS8: - default: - bits |= BITS_DATA_8; - dev_dbg(dev, "%s - data bits = 8\n", __func__); - break; - } - if (cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, bits)) - dev_dbg(dev, "Number of data bits requested not supported by device\n"); + /* CP2101 only supports CS8, 1 stop bit and non-stick parity. */ + if (priv->partnum == CP210X_PARTNUM_CP2101) { + tty->termios.c_cflag &= ~(CSIZE | CSTOPB | CMSPAR); + tty->termios.c_cflag |= CS8; } - if ((cflag & (PARENB|PARODD|CMSPAR)) != - (old_cflag & (PARENB|PARODD|CMSPAR))) { - cp210x_get_line_ctl(port, &bits); - bits &= ~BITS_PARITY_MASK; - if (cflag & PARENB) { - if (cflag & CMSPAR) { - if (cflag & PARODD) { - bits |= BITS_PARITY_MARK; - dev_dbg(dev, "%s - parity = MARK\n", __func__); - } else { - bits |= BITS_PARITY_SPACE; - dev_dbg(dev, "%s - parity = SPACE\n", __func__); - } - } else { - if (cflag & PARODD) { - bits |= BITS_PARITY_ODD; - dev_dbg(dev, "%s - parity = ODD\n", __func__); - } else { - bits |= BITS_PARITY_EVEN; - dev_dbg(dev, "%s - parity = EVEN\n", __func__); - } - } - } - if (cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, bits)) - dev_dbg(dev, "Parity mode not supported by device\n"); + bits = 0; + + switch (C_CSIZE(tty)) { + case CS5: + bits |= BITS_DATA_5; + break; + case CS6: + bits |= BITS_DATA_6; + break; + case CS7: + bits |= BITS_DATA_7; + break; + case CS8: + default: + bits |= BITS_DATA_8; + break; } - if ((cflag & CSTOPB) != (old_cflag & CSTOPB)) { - cp210x_get_line_ctl(port, &bits); - bits &= ~BITS_STOP_MASK; - if (cflag & CSTOPB) { - bits |= BITS_STOP_2; - dev_dbg(dev, "%s - stop bits = 2\n", __func__); + if (C_PARENB(tty)) { + if (C_CMSPAR(tty)) { + if (C_PARODD(tty)) + bits |= BITS_PARITY_MARK; + else + bits |= BITS_PARITY_SPACE; } else { - bits |= BITS_STOP_1; - dev_dbg(dev, "%s - stop bits = 1\n", __func__); + if (C_PARODD(tty)) + bits |= BITS_PARITY_ODD; + else + bits |= BITS_PARITY_EVEN; } - if (cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, bits)) - dev_dbg(dev, "Number of stop bits requested not supported by device\n"); } - if ((cflag & CRTSCTS) != (old_cflag & CRTSCTS)) { - struct cp210x_flow_ctl flow_ctl; - u32 ctl_hs; - u32 flow_repl; - - cp210x_read_reg_block(port, CP210X_GET_FLOW, &flow_ctl, - sizeof(flow_ctl)); - ctl_hs = le32_to_cpu(flow_ctl.ulControlHandshake); - flow_repl = le32_to_cpu(flow_ctl.ulFlowReplace); - dev_dbg(dev, "%s - read ulControlHandshake=0x%08x, ulFlowReplace=0x%08x\n", - __func__, ctl_hs, flow_repl); - - ctl_hs &= ~CP210X_SERIAL_DSR_HANDSHAKE; - ctl_hs &= ~CP210X_SERIAL_DCD_HANDSHAKE; - ctl_hs &= ~CP210X_SERIAL_DSR_SENSITIVITY; - ctl_hs &= ~CP210X_SERIAL_DTR_MASK; - ctl_hs |= CP210X_SERIAL_DTR_SHIFT(CP210X_SERIAL_DTR_ACTIVE); - if (cflag & CRTSCTS) { - ctl_hs |= CP210X_SERIAL_CTS_HANDSHAKE; - - flow_repl &= ~CP210X_SERIAL_RTS_MASK; - flow_repl |= CP210X_SERIAL_RTS_SHIFT( - CP210X_SERIAL_RTS_FLOW_CTL); - dev_dbg(dev, "%s - flow control = CRTSCTS\n", __func__); - } else { - ctl_hs &= ~CP210X_SERIAL_CTS_HANDSHAKE; + if (C_CSTOPB(tty)) + bits |= BITS_STOP_2; + else + bits |= BITS_STOP_1; - flow_repl &= ~CP210X_SERIAL_RTS_MASK; - flow_repl |= CP210X_SERIAL_RTS_SHIFT( - CP210X_SERIAL_RTS_ACTIVE); - dev_dbg(dev, "%s - flow control = NONE\n", __func__); - } + ret = cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, bits); + if (ret) + dev_err(&port->dev, "failed to set line control: %d\n", ret); - dev_dbg(dev, "%s - write ulControlHandshake=0x%08x, ulFlowReplace=0x%08x\n", - __func__, ctl_hs, flow_repl); - flow_ctl.ulControlHandshake = cpu_to_le32(ctl_hs); - flow_ctl.ulFlowReplace = cpu_to_le32(flow_repl); - cp210x_write_reg_block(port, CP210X_SET_FLOW, &flow_ctl, - sizeof(flow_ctl)); - } + cp210x_set_flow_control(tty, port, old_termios); /* * Enable event-insertion mode only if input parity checking is @@ -1518,12 +1234,12 @@ static int cp210x_tiocmset_port(struct usb_serial_port *port, return cp210x_write_u16_reg(port, CP210X_SET_MHS, control); } -static void cp210x_dtr_rts(struct usb_serial_port *p, int on) +static void cp210x_dtr_rts(struct usb_serial_port *port, int on) { if (on) - cp210x_tiocmset_port(p, TIOCM_DTR|TIOCM_RTS, 0); + cp210x_tiocmset_port(port, TIOCM_DTR | TIOCM_RTS, 0); else - cp210x_tiocmset_port(p, 0, TIOCM_DTR|TIOCM_RTS); + cp210x_tiocmset_port(port, 0, TIOCM_DTR | TIOCM_RTS); } static int cp210x_tiocmget(struct tty_struct *tty) @@ -1986,7 +1702,6 @@ static int cp210x_port_probe(struct usb_serial_port *port) { struct usb_serial *serial = port->serial; struct cp210x_port_private *port_priv; - int ret; port_priv = kzalloc(sizeof(*port_priv), GFP_KERNEL); if (!port_priv) @@ -1996,12 +1711,6 @@ static int cp210x_port_probe(struct usb_serial_port *port) usb_set_serial_port_data(port, port_priv); - ret = cp210x_detect_swapped_line_ctl(port); - if (ret) { - kfree(port_priv); - return ret; - } - return 0; } diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 91055a191995..0ecd5316d85f 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -19,7 +19,6 @@ #include <linux/tty_flip.h> #include <linux/module.h> #include <linux/spinlock.h> -#include <linux/workqueue.h> #include <linux/uaccess.h> #include <linux/usb.h> #include <linux/wait.h> @@ -198,14 +197,13 @@ struct digi_port { int dp_throttle_restart; wait_queue_head_t dp_flush_wait; wait_queue_head_t dp_close_wait; /* wait queue for close */ - struct work_struct dp_wakeup_work; + wait_queue_head_t write_wait; struct usb_serial_port *dp_port; }; /* Local Function Declarations */ -static void digi_wakeup_write_lock(struct work_struct *work); static int digi_write_oob_command(struct usb_serial_port *port, unsigned char *buf, int count, int interruptible); static int digi_write_inb_command(struct usb_serial_port *port, @@ -356,26 +354,6 @@ __releases(lock) return timeout; } - -/* - * Digi Wakeup Write - * - * Wake up port, line discipline, and tty processes sleeping - * on writes. - */ - -static void digi_wakeup_write_lock(struct work_struct *work) -{ - struct digi_port *priv = - container_of(work, struct digi_port, dp_wakeup_work); - struct usb_serial_port *port = priv->dp_port; - unsigned long flags; - - spin_lock_irqsave(&priv->dp_port_lock, flags); - tty_port_tty_wakeup(&port->port); - spin_unlock_irqrestore(&priv->dp_port_lock, flags); -} - /* * Digi Write OOB Command * @@ -404,7 +382,7 @@ static int digi_write_oob_command(struct usb_serial_port *port, while (count > 0) { while (oob_priv->dp_write_urb_in_use) { cond_wait_interruptible_timeout_irqrestore( - &oob_port->write_wait, DIGI_RETRY_TIMEOUT, + &oob_priv->write_wait, DIGI_RETRY_TIMEOUT, &oob_priv->dp_port_lock, flags); if (interruptible && signal_pending(current)) return -EINTR; @@ -467,7 +445,7 @@ static int digi_write_inb_command(struct usb_serial_port *port, while (priv->dp_write_urb_in_use && time_before(jiffies, timeout)) { cond_wait_interruptible_timeout_irqrestore( - &port->write_wait, DIGI_RETRY_TIMEOUT, + &priv->write_wait, DIGI_RETRY_TIMEOUT, &priv->dp_port_lock, flags); if (signal_pending(current)) return -EINTR; @@ -546,7 +524,7 @@ static int digi_set_modem_signals(struct usb_serial_port *port, while (oob_priv->dp_write_urb_in_use) { spin_unlock(&port_priv->dp_port_lock); cond_wait_interruptible_timeout_irqrestore( - &oob_port->write_wait, DIGI_RETRY_TIMEOUT, + &oob_priv->write_wait, DIGI_RETRY_TIMEOUT, &oob_priv->dp_port_lock, flags); if (interruptible && signal_pending(current)) return -EINTR; @@ -911,9 +889,8 @@ static int digi_write(struct tty_struct *tty, struct usb_serial_port *port, unsigned char *data = port->write_urb->transfer_buffer; unsigned long flags = 0; - dev_dbg(&port->dev, - "digi_write: TOP: port=%d, count=%d, in_interrupt=%ld\n", - priv->dp_port_num, count, in_interrupt()); + dev_dbg(&port->dev, "digi_write: TOP: port=%d, count=%d\n", + priv->dp_port_num, count); /* copy user data (which can sleep) before getting spin lock */ count = min(count, port->bulk_out_size-2); @@ -986,6 +963,7 @@ static void digi_write_bulk_callback(struct urb *urb) unsigned long flags; int ret = 0; int status = urb->status; + bool wakeup; /* port and serial sanity check */ if (port == NULL || (priv = usb_get_serial_port_data(port)) == NULL) { @@ -1006,12 +984,13 @@ static void digi_write_bulk_callback(struct urb *urb) dev_dbg(&port->dev, "digi_write_bulk_callback: oob callback\n"); spin_lock_irqsave(&priv->dp_port_lock, flags); priv->dp_write_urb_in_use = 0; - wake_up_interruptible(&port->write_wait); + wake_up_interruptible(&priv->write_wait); spin_unlock_irqrestore(&priv->dp_port_lock, flags); return; } /* try to send any buffered data on this port */ + wakeup = true; spin_lock_irqsave(&priv->dp_port_lock, flags); priv->dp_write_urb_in_use = 0; if (priv->dp_out_buf_len > 0) { @@ -1027,19 +1006,18 @@ static void digi_write_bulk_callback(struct urb *urb) if (ret == 0) { priv->dp_write_urb_in_use = 1; priv->dp_out_buf_len = 0; + wakeup = false; } } - /* wake up processes sleeping on writes immediately */ - tty_port_tty_wakeup(&port->port); - /* also queue up a wakeup at scheduler time, in case we */ - /* lost the race in write_chan(). */ - schedule_work(&priv->dp_wakeup_work); - spin_unlock_irqrestore(&priv->dp_port_lock, flags); + if (ret && ret != -EPERM) dev_err_console(port, "%s: usb_submit_urb failed, ret=%d, port=%d\n", __func__, ret, priv->dp_port_num); + + if (wakeup) + tty_port_tty_wakeup(&port->port); } static int digi_write_room(struct tty_struct *tty) @@ -1239,11 +1217,9 @@ static int digi_port_init(struct usb_serial_port *port, unsigned port_num) init_waitqueue_head(&priv->dp_transmit_idle_wait); init_waitqueue_head(&priv->dp_flush_wait); init_waitqueue_head(&priv->dp_close_wait); - INIT_WORK(&priv->dp_wakeup_work, digi_wakeup_write_lock); + init_waitqueue_head(&priv->write_wait); priv->dp_port = port; - init_waitqueue_head(&port->write_wait); - usb_set_serial_port_data(port, priv); return 0; @@ -1508,13 +1484,14 @@ static int digi_read_oob_callback(struct urb *urb) rts = C_CRTSCTS(tty); if (tty && opcode == DIGI_CMD_READ_INPUT_SIGNALS) { + bool wakeup = false; + spin_lock_irqsave(&priv->dp_port_lock, flags); /* convert from digi flags to termiox flags */ if (val & DIGI_READ_INPUT_SIGNALS_CTS) { priv->dp_modem_signals |= TIOCM_CTS; - /* port must be open to use tty struct */ if (rts) - tty_port_tty_wakeup(&port->port); + wakeup = true; } else { priv->dp_modem_signals &= ~TIOCM_CTS; /* port must be open to use tty struct */ @@ -1533,6 +1510,9 @@ static int digi_read_oob_callback(struct urb *urb) priv->dp_modem_signals &= ~TIOCM_CD; spin_unlock_irqrestore(&priv->dp_port_lock, flags); + + if (wakeup) + tty_port_tty_wakeup(&port->port); } else if (opcode == DIGI_CMD_TRANSMIT_IDLE) { spin_lock_irqsave(&priv->dp_port_lock, flags); priv->dp_transmit_idle = 1; diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index e0f4c3d9649c..94398f89e600 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1841,9 +1841,6 @@ static int ftdi_gpio_request(struct gpio_chip *gc, unsigned int offset) struct ftdi_private *priv = usb_get_serial_port_data(port); int result; - if (priv->gpio_altfunc & BIT(offset)) - return -ENODEV; - mutex_lock(&priv->gpio_lock); if (!priv->gpio_used) { /* Set default pin states, as we cannot get them from device */ @@ -2002,6 +1999,25 @@ static int ftdi_gpio_direction_output(struct gpio_chip *gc, unsigned int gpio, return result; } +static int ftdi_gpio_init_valid_mask(struct gpio_chip *gc, + unsigned long *valid_mask, + unsigned int ngpios) +{ + struct usb_serial_port *port = gpiochip_get_data(gc); + struct ftdi_private *priv = usb_get_serial_port_data(port); + unsigned long map = priv->gpio_altfunc; + + bitmap_complement(valid_mask, &map, ngpios); + + if (bitmap_empty(valid_mask, ngpios)) + dev_dbg(&port->dev, "no CBUS pin configured for GPIO\n"); + else + dev_dbg(&port->dev, "CBUS%*pbl configured for GPIO\n", ngpios, + valid_mask); + + return 0; +} + static int ftdi_read_eeprom(struct usb_serial *serial, void *dst, u16 addr, u16 nbytes) { @@ -2173,6 +2189,7 @@ static int ftdi_gpio_init(struct usb_serial_port *port) priv->gc.get_direction = ftdi_gpio_direction_get; priv->gc.direction_input = ftdi_gpio_direction_input; priv->gc.direction_output = ftdi_gpio_direction_output; + priv->gc.init_valid_mask = ftdi_gpio_init_valid_mask; priv->gc.get = ftdi_gpio_get; priv->gc.set = ftdi_gpio_set; priv->gc.get_multiple = ftdi_gpio_get_multiple; diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index b4ba79123d9d..f1201d4de297 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -850,7 +850,6 @@ static int iuu_uart_baud(struct usb_serial_port *port, u32 baud_base, default: kfree(dataout); return IUU_INVALID_PARAMETER; - break; } switch (parity & 0xF0) { @@ -864,7 +863,6 @@ static int iuu_uart_baud(struct usb_serial_port *port, u32 baud_base, default: kfree(dataout); return IUU_INVALID_PARAMETER; - break; } status = bulk_immediate(port, dataout, DataCount); diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index c1333919716b..e6f933e8d25f 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -5,12 +5,12 @@ * Copyright (C) 1999 - 2001 Greg Kroah-Hartman <greg@kroah.com> * Copyright (C) 1999, 2000 Brian Warner <warner@lothar.com> * Copyright (C) 2000 Al Borchers <borchers@steinerpoint.com> + * Copyright (C) 2020 Johan Hovold <johan@kernel.org> * * See Documentation/usb/usb-serial.rst for more information on using this * driver */ - #include <linux/kernel.h> #include <linux/errno.h> #include <linux/slab.h> @@ -25,30 +25,19 @@ #include <linux/usb/serial.h> #include <linux/usb/ezusb.h> -/* make a simple define to handle if we are compiling keyspan_pda or xircom support */ -#if IS_ENABLED(CONFIG_USB_SERIAL_KEYSPAN_PDA) - #define KEYSPAN -#else - #undef KEYSPAN -#endif -#if IS_ENABLED(CONFIG_USB_SERIAL_XIRCOM) - #define XIRCOM -#else - #undef XIRCOM -#endif - -#define DRIVER_AUTHOR "Brian Warner <warner@lothar.com>" +#define DRIVER_AUTHOR "Brian Warner <warner@lothar.com>, Johan Hovold <johan@kernel.org>" #define DRIVER_DESC "USB Keyspan PDA Converter driver" +#define KEYSPAN_TX_THRESHOLD 128 + struct keyspan_pda_private { int tx_room; - int tx_throttled; - struct work_struct wakeup_work; - struct work_struct unthrottle_work; + struct work_struct unthrottle_work; struct usb_serial *serial; struct usb_serial_port *port; }; +static int keyspan_pda_write_start(struct usb_serial_port *port); #define KEYSPAN_VENDOR_ID 0x06cd #define KEYSPAN_PDA_FAKE_ID 0x0103 @@ -62,18 +51,13 @@ struct keyspan_pda_private { #define ENTREGA_FAKE_ID 0x8093 static const struct usb_device_id id_table_combined[] = { -#ifdef KEYSPAN { USB_DEVICE(KEYSPAN_VENDOR_ID, KEYSPAN_PDA_FAKE_ID) }, -#endif -#ifdef XIRCOM { USB_DEVICE(XIRCOM_VENDOR_ID, XIRCOM_FAKE_ID) }, { USB_DEVICE(XIRCOM_VENDOR_ID, XIRCOM_FAKE_ID_2) }, { USB_DEVICE(ENTREGA_VENDOR_ID, ENTREGA_FAKE_ID) }, -#endif { USB_DEVICE(KEYSPAN_VENDOR_ID, KEYSPAN_PDA_ID) }, { } /* Terminating entry */ }; - MODULE_DEVICE_TABLE(usb, id_table_combined); static const struct usb_device_id id_table_std[] = { @@ -81,46 +65,71 @@ static const struct usb_device_id id_table_std[] = { { } /* Terminating entry */ }; -#ifdef KEYSPAN static const struct usb_device_id id_table_fake[] = { { USB_DEVICE(KEYSPAN_VENDOR_ID, KEYSPAN_PDA_FAKE_ID) }, - { } /* Terminating entry */ -}; -#endif - -#ifdef XIRCOM -static const struct usb_device_id id_table_fake_xircom[] = { { USB_DEVICE(XIRCOM_VENDOR_ID, XIRCOM_FAKE_ID) }, { USB_DEVICE(XIRCOM_VENDOR_ID, XIRCOM_FAKE_ID_2) }, { USB_DEVICE(ENTREGA_VENDOR_ID, ENTREGA_FAKE_ID) }, - { } + { } /* Terminating entry */ }; -#endif -static void keyspan_pda_wakeup_write(struct work_struct *work) +static int keyspan_pda_get_write_room(struct keyspan_pda_private *priv) { - struct keyspan_pda_private *priv = - container_of(work, struct keyspan_pda_private, wakeup_work); struct usb_serial_port *port = priv->port; + struct usb_serial *serial = port->serial; + u8 *room; + int rc; + + room = kmalloc(1, GFP_KERNEL); + if (!room) + return -ENOMEM; - tty_port_tty_wakeup(&port->port); + rc = usb_control_msg(serial->dev, + usb_rcvctrlpipe(serial->dev, 0), + 6, /* write_room */ + USB_TYPE_VENDOR | USB_RECIP_INTERFACE + | USB_DIR_IN, + 0, /* value: 0 means "remaining room" */ + 0, /* index */ + room, + 1, + 2000); + if (rc != 1) { + if (rc >= 0) + rc = -EIO; + dev_dbg(&port->dev, "roomquery failed: %d\n", rc); + goto out_free; + } + + dev_dbg(&port->dev, "roomquery says %d\n", *room); + rc = *room; +out_free: + kfree(room); + + return rc; } static void keyspan_pda_request_unthrottle(struct work_struct *work) { struct keyspan_pda_private *priv = container_of(work, struct keyspan_pda_private, unthrottle_work); - struct usb_serial *serial = priv->serial; + struct usb_serial_port *port = priv->port; + struct usb_serial *serial = port->serial; + unsigned long flags; int result; - /* ask the device to tell us when the tx buffer becomes - sufficiently empty */ + dev_dbg(&port->dev, "%s\n", __func__); + + /* + * Ask the device to tell us when the tx buffer becomes + * sufficiently empty. + */ result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), 7, /* request_unthrottle */ USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, - 16, /* value: threshold */ + KEYSPAN_TX_THRESHOLD, 0, /* index */ NULL, 0, @@ -128,9 +137,20 @@ static void keyspan_pda_request_unthrottle(struct work_struct *work) if (result < 0) dev_dbg(&serial->dev->dev, "%s - error %d from usb_control_msg\n", __func__, result); + /* + * Need to check available space after requesting notification in case + * buffer is already empty so that no notification is sent. + */ + result = keyspan_pda_get_write_room(priv); + if (result > KEYSPAN_TX_THRESHOLD) { + spin_lock_irqsave(&port->lock, flags); + priv->tx_room = max(priv->tx_room, result); + spin_unlock_irqrestore(&port->lock, flags); + + usb_serial_port_softint(port); + } } - static void keyspan_pda_rx_interrupt(struct urb *urb) { struct usb_serial_port *port = urb->context; @@ -139,6 +159,8 @@ static void keyspan_pda_rx_interrupt(struct urb *urb) int retval; int status = urb->status; struct keyspan_pda_private *priv; + unsigned long flags; + priv = usb_get_serial_port_data(port); switch (status) { @@ -172,18 +194,22 @@ static void keyspan_pda_rx_interrupt(struct urb *urb) break; case 1: /* status interrupt */ - if (len < 3) { + if (len < 2) { dev_warn(&port->dev, "short interrupt message received\n"); break; } - dev_dbg(&port->dev, "rx int, d1=%d, d2=%d\n", data[1], data[2]); + dev_dbg(&port->dev, "rx int, d1=%d\n", data[1]); switch (data[1]) { case 1: /* modemline change */ break; case 2: /* tx unthrottle interrupt */ - priv->tx_throttled = 0; - /* queue up a wakeup at scheduler time */ - schedule_work(&priv->wakeup_work); + spin_lock_irqsave(&port->lock, flags); + priv->tx_room = max(priv->tx_room, KEYSPAN_TX_THRESHOLD); + spin_unlock_irqrestore(&port->lock, flags); + + keyspan_pda_write_start(port); + + usb_serial_port_softint(port); break; default: break; @@ -201,31 +227,30 @@ exit: __func__, retval); } - static void keyspan_pda_rx_throttle(struct tty_struct *tty) { - /* stop receiving characters. We just turn off the URB request, and - let chars pile up in the device. If we're doing hardware - flowcontrol, the device will signal the other end when its buffer - fills up. If we're doing XON/XOFF, this would be a good time to - send an XOFF, although it might make sense to foist that off - upon the device too. */ struct usb_serial_port *port = tty->driver_data; + /* + * Stop receiving characters. We just turn off the URB request, and + * let chars pile up in the device. If we're doing hardware + * flowcontrol, the device will signal the other end when its buffer + * fills up. If we're doing XON/XOFF, this would be a good time to + * send an XOFF, although it might make sense to foist that off upon + * the device too. + */ usb_kill_urb(port->interrupt_in_urb); } - static void keyspan_pda_rx_unthrottle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; - /* just restart the receive interrupt URB */ + /* just restart the receive interrupt URB */ if (usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL)) dev_dbg(&port->dev, "usb_submit_urb(read urb) failed\n"); } - static speed_t keyspan_pda_setbaud(struct usb_serial *serial, speed_t baud) { int rc; @@ -267,8 +292,6 @@ static speed_t keyspan_pda_setbaud(struct usb_serial *serial, speed_t baud) baud = 9600; } - /* rather than figure out how to sleep while waiting for this - to complete, I just use the "legacy" API. */ rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), 0, /* set baud */ USB_TYPE_VENDOR @@ -281,10 +304,10 @@ static speed_t keyspan_pda_setbaud(struct usb_serial *serial, speed_t baud) 2000); /* timeout */ if (rc < 0) return 0; + return baud; } - static void keyspan_pda_break_ctl(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; @@ -296,6 +319,7 @@ static void keyspan_pda_break_ctl(struct tty_struct *tty, int break_state) value = 1; /* start break */ else value = 0; /* clear break */ + result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), 4, /* set break */ USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, @@ -303,39 +327,35 @@ static void keyspan_pda_break_ctl(struct tty_struct *tty, int break_state) if (result < 0) dev_dbg(&port->dev, "%s - error %d from usb_control_msg\n", __func__, result); - /* there is something funky about this.. the TCSBRK that 'cu' performs - ought to translate into a break_ctl(-1),break_ctl(0) pair HZ/4 - seconds apart, but it feels like the break sent isn't as long as it - is on /dev/ttyS0 */ } - static void keyspan_pda_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { struct usb_serial *serial = port->serial; speed_t speed; - /* cflag specifies lots of stuff: number of stop bits, parity, number - of data bits, baud. What can the device actually handle?: - CSTOPB (1 stop bit or 2) - PARENB (parity) - CSIZE (5bit .. 8bit) - There is minimal hw support for parity (a PSW bit seems to hold the - parity of whatever is in the accumulator). The UART either deals - with 10 bits (start, 8 data, stop) or 11 bits (start, 8 data, - 1 special, stop). So, with firmware changes, we could do: - 8N1: 10 bit - 8N2: 11 bit, extra bit always (mark?) - 8[EOMS]1: 11 bit, extra bit is parity - 7[EOMS]1: 10 bit, b0/b7 is parity - 7[EOMS]2: 11 bit, b0/b7 is parity, extra bit always (mark?) - - HW flow control is dictated by the tty->termios.c_cflags & CRTSCTS - bit. - - For now, just do baud. */ - + /* + * cflag specifies lots of stuff: number of stop bits, parity, number + * of data bits, baud. What can the device actually handle?: + * CSTOPB (1 stop bit or 2) + * PARENB (parity) + * CSIZE (5bit .. 8bit) + * There is minimal hw support for parity (a PSW bit seems to hold the + * parity of whatever is in the accumulator). The UART either deals + * with 10 bits (start, 8 data, stop) or 11 bits (start, 8 data, + * 1 special, stop). So, with firmware changes, we could do: + * 8N1: 10 bit + * 8N2: 11 bit, extra bit always (mark?) + * 8[EOMS]1: 11 bit, extra bit is parity + * 7[EOMS]1: 10 bit, b0/b7 is parity + * 7[EOMS]2: 11 bit, b0/b7 is parity, extra bit always (mark?) + * + * HW flow control is dictated by the tty->termios.c_cflags & CRTSCTS + * bit. + * + * For now, just do baud. + */ speed = tty_get_baud_rate(tty); speed = keyspan_pda_setbaud(serial, speed); @@ -344,17 +364,19 @@ static void keyspan_pda_set_termios(struct tty_struct *tty, /* It hasn't changed so.. */ speed = tty_termios_baud_rate(old_termios); } - /* Only speed can change so copy the old h/w parameters - then encode the new speed */ + /* + * Only speed can change so copy the old h/w parameters then encode + * the new speed. + */ tty_termios_copy_hw(&tty->termios, old_termios); tty_encode_baud_rate(tty, speed, speed); } - -/* modem control pins: DTR and RTS are outputs and can be controlled. - DCD, RI, DSR, CTS are inputs and can be read. All outputs can also be - read. The byte passed is: DTR(b7) DCD RI DSR CTS RTS(b2) unused unused */ - +/* + * Modem control pins: DTR and RTS are outputs and can be controlled. + * DCD, RI, DSR, CTS are inputs and can be read. All outputs can also be + * read. The byte passed is: DTR(b7) DCD RI DSR CTS RTS(b2) unused unused. + */ static int keyspan_pda_get_modem_info(struct usb_serial *serial, unsigned char *value) { @@ -378,7 +400,6 @@ static int keyspan_pda_get_modem_info(struct usb_serial *serial, return rc; } - static int keyspan_pda_set_modem_info(struct usb_serial *serial, unsigned char value) { @@ -401,13 +422,14 @@ static int keyspan_pda_tiocmget(struct tty_struct *tty) rc = keyspan_pda_get_modem_info(serial, &status); if (rc < 0) return rc; - value = - ((status & (1<<7)) ? TIOCM_DTR : 0) | - ((status & (1<<6)) ? TIOCM_CAR : 0) | - ((status & (1<<5)) ? TIOCM_RNG : 0) | - ((status & (1<<4)) ? TIOCM_DSR : 0) | - ((status & (1<<3)) ? TIOCM_CTS : 0) | - ((status & (1<<2)) ? TIOCM_RTS : 0); + + value = ((status & BIT(7)) ? TIOCM_DTR : 0) | + ((status & BIT(6)) ? TIOCM_CAR : 0) | + ((status & BIT(5)) ? TIOCM_RNG : 0) | + ((status & BIT(4)) ? TIOCM_DSR : 0) | + ((status & BIT(3)) ? TIOCM_CTS : 0) | + ((status & BIT(2)) ? TIOCM_RTS : 0); + return value; } @@ -424,186 +446,127 @@ static int keyspan_pda_tiocmset(struct tty_struct *tty, return rc; if (set & TIOCM_RTS) - status |= (1<<2); + status |= BIT(2); if (set & TIOCM_DTR) - status |= (1<<7); + status |= BIT(7); if (clear & TIOCM_RTS) - status &= ~(1<<2); + status &= ~BIT(2); if (clear & TIOCM_DTR) - status &= ~(1<<7); + status &= ~BIT(7); rc = keyspan_pda_set_modem_info(serial, status); return rc; } -static int keyspan_pda_write(struct tty_struct *tty, - struct usb_serial_port *port, const unsigned char *buf, int count) +static int keyspan_pda_write_start(struct usb_serial_port *port) { - struct usb_serial *serial = port->serial; - int request_unthrottle = 0; - int rc = 0; - struct keyspan_pda_private *priv; + struct keyspan_pda_private *priv = usb_get_serial_port_data(port); + unsigned long flags; + struct urb *urb; + int count; + int room; + int rc; - priv = usb_get_serial_port_data(port); - /* guess how much room is left in the device's ring buffer, and if we - want to send more than that, check first, updating our notion of - what is left. If our write will result in no room left, ask the - device to give us an interrupt when the room available rises above - a threshold, and hold off all writers (eventually, those using - select() or poll() too) until we receive that unthrottle interrupt. - Block if we can't write anything at all, otherwise write as much as - we can. */ - if (count == 0) { - dev_dbg(&port->dev, "write request of 0 bytes\n"); - return 0; - } + /* + * Guess how much room is left in the device's ring buffer. If our + * write will result in no room left, ask the device to give us an + * interrupt when the room available rises above a threshold but also + * query how much room is currently available (in case our guess was + * too conservative and the buffer is already empty when the + * unthrottle work is scheduled). + */ + + /* + * We might block because of: + * the TX urb is in-flight (wait until it completes) + * the device is full (wait until it says there is room) + */ + spin_lock_irqsave(&port->lock, flags); + + room = priv->tx_room; + count = kfifo_len(&port->write_fifo); - /* we might block because of: - the TX urb is in-flight (wait until it completes) - the device is full (wait until it says there is room) - */ - spin_lock_bh(&port->lock); - if (!test_bit(0, &port->write_urbs_free) || priv->tx_throttled) { - spin_unlock_bh(&port->lock); + if (!test_bit(0, &port->write_urbs_free) || count == 0 || room == 0) { + spin_unlock_irqrestore(&port->lock, flags); return 0; } - clear_bit(0, &port->write_urbs_free); - spin_unlock_bh(&port->lock); - - /* At this point the URB is in our control, nobody else can submit it - again (the only sudden transition was the one from EINPROGRESS to - finished). Also, the tx process is not throttled. So we are - ready to write. */ - - count = (count > port->bulk_out_size) ? port->bulk_out_size : count; - - /* Check if we might overrun the Tx buffer. If so, ask the - device how much room it really has. This is done only on - scheduler time, since usb_control_msg() sleeps. */ - if (count > priv->tx_room && !in_interrupt()) { - u8 *room; - - room = kmalloc(1, GFP_KERNEL); - if (!room) { - rc = -ENOMEM; - goto exit; - } + __clear_bit(0, &port->write_urbs_free); - rc = usb_control_msg(serial->dev, - usb_rcvctrlpipe(serial->dev, 0), - 6, /* write_room */ - USB_TYPE_VENDOR | USB_RECIP_INTERFACE - | USB_DIR_IN, - 0, /* value: 0 means "remaining room" */ - 0, /* index */ - room, - 1, - 2000); - if (rc > 0) { - dev_dbg(&port->dev, "roomquery says %d\n", *room); - priv->tx_room = *room; - } - kfree(room); - if (rc < 0) { - dev_dbg(&port->dev, "roomquery failed\n"); - goto exit; - } - if (rc == 0) { - dev_dbg(&port->dev, "roomquery returned 0 bytes\n"); - rc = -EIO; /* device didn't return any data */ - goto exit; - } - } - if (count > priv->tx_room) { - /* we're about to completely fill the Tx buffer, so - we'll be throttled afterwards. */ - count = priv->tx_room; - request_unthrottle = 1; - } + if (count > room) + count = room; + if (count > port->bulk_out_size) + count = port->bulk_out_size; - if (count) { - /* now transfer data */ - memcpy(port->write_urb->transfer_buffer, buf, count); - /* send the data out the bulk port */ - port->write_urb->transfer_buffer_length = count; + urb = port->write_urb; + count = kfifo_out(&port->write_fifo, urb->transfer_buffer, count); + urb->transfer_buffer_length = count; - priv->tx_room -= count; + port->tx_bytes += count; + priv->tx_room -= count; - rc = usb_submit_urb(port->write_urb, GFP_ATOMIC); - if (rc) { - dev_dbg(&port->dev, "usb_submit_urb(write bulk) failed\n"); - goto exit; - } - } else { - /* There wasn't any room left, so we are throttled until - the buffer empties a bit */ - request_unthrottle = 1; + spin_unlock_irqrestore(&port->lock, flags); + + dev_dbg(&port->dev, "%s - count = %d, txroom = %d\n", __func__, count, room); + + rc = usb_submit_urb(urb, GFP_ATOMIC); + if (rc) { + dev_dbg(&port->dev, "usb_submit_urb(write bulk) failed\n"); + + spin_lock_irqsave(&port->lock, flags); + port->tx_bytes -= count; + priv->tx_room = max(priv->tx_room, room + count); + __set_bit(0, &port->write_urbs_free); + spin_unlock_irqrestore(&port->lock, flags); + + return rc; } - if (request_unthrottle) { - priv->tx_throttled = 1; /* block writers */ + if (count == room) schedule_work(&priv->unthrottle_work); - } - rc = count; -exit: - if (rc < 0) - set_bit(0, &port->write_urbs_free); - return rc; + return count; } - static void keyspan_pda_write_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; - struct keyspan_pda_private *priv; + unsigned long flags; - set_bit(0, &port->write_urbs_free); - priv = usb_get_serial_port_data(port); + spin_lock_irqsave(&port->lock, flags); + port->tx_bytes -= urb->transfer_buffer_length; + __set_bit(0, &port->write_urbs_free); + spin_unlock_irqrestore(&port->lock, flags); - /* queue up a wakeup at scheduler time */ - schedule_work(&priv->wakeup_work); -} + keyspan_pda_write_start(port); + usb_serial_port_softint(port); +} -static int keyspan_pda_write_room(struct tty_struct *tty) +static int keyspan_pda_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) { - struct usb_serial_port *port = tty->driver_data; - struct keyspan_pda_private *priv; - priv = usb_get_serial_port_data(port); - /* used by n_tty.c for processing of tabs and such. Giving it our - conservative guess is probably good enough, but needs testing by - running a console through the device. */ - return priv->tx_room; -} + int rc; + dev_dbg(&port->dev, "%s - count = %d\n", __func__, count); -static int keyspan_pda_chars_in_buffer(struct tty_struct *tty) -{ - struct usb_serial_port *port = tty->driver_data; - struct keyspan_pda_private *priv; - unsigned long flags; - int ret = 0; + if (!count) + return 0; - priv = usb_get_serial_port_data(port); + count = kfifo_in_locked(&port->write_fifo, buf, count, &port->lock); - /* when throttled, return at least WAKEUP_CHARS to tell select() (via - n_tty.c:normal_poll() ) that we're not writeable. */ + rc = keyspan_pda_write_start(port); + if (rc) + return rc; - spin_lock_irqsave(&port->lock, flags); - if (!test_bit(0, &port->write_urbs_free) || priv->tx_throttled) - ret = 256; - spin_unlock_irqrestore(&port->lock, flags); - return ret; + return count; } - static void keyspan_pda_dtr_rts(struct usb_serial_port *port, int on) { struct usb_serial *serial = port->serial; if (on) - keyspan_pda_set_modem_info(serial, (1 << 7) | (1 << 2)); + keyspan_pda_set_modem_info(serial, BIT(7) | BIT(2)); else keyspan_pda_set_modem_info(serial, 0); } @@ -612,74 +575,63 @@ static void keyspan_pda_dtr_rts(struct usb_serial_port *port, int on) static int keyspan_pda_open(struct tty_struct *tty, struct usb_serial_port *port) { - struct usb_serial *serial = port->serial; - u8 *room; - int rc = 0; - struct keyspan_pda_private *priv; + struct keyspan_pda_private *priv = usb_get_serial_port_data(port); + int rc; /* find out how much room is in the Tx ring */ - room = kmalloc(1, GFP_KERNEL); - if (!room) - return -ENOMEM; + rc = keyspan_pda_get_write_room(priv); + if (rc < 0) + return rc; - rc = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), - 6, /* write_room */ - USB_TYPE_VENDOR | USB_RECIP_INTERFACE - | USB_DIR_IN, - 0, /* value */ - 0, /* index */ - room, - 1, - 2000); - if (rc < 0) { - dev_dbg(&port->dev, "%s - roomquery failed\n", __func__); - goto error; - } - if (rc == 0) { - dev_dbg(&port->dev, "%s - roomquery returned 0 bytes\n", __func__); - rc = -EIO; - goto error; - } - priv = usb_get_serial_port_data(port); - priv->tx_room = *room; - priv->tx_throttled = *room ? 0 : 1; + spin_lock_irq(&port->lock); + priv->tx_room = rc; + spin_unlock_irq(&port->lock); - /*Start reading from the device*/ rc = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); if (rc) { dev_dbg(&port->dev, "%s - usb_submit_urb(read int) failed\n", __func__); - goto error; + return rc; } -error: - kfree(room); - return rc; + + return 0; } + static void keyspan_pda_close(struct usb_serial_port *port) { - usb_kill_urb(port->write_urb); + struct keyspan_pda_private *priv = usb_get_serial_port_data(port); + + /* + * Stop the interrupt URB first as its completion handler may submit + * the write URB. + */ usb_kill_urb(port->interrupt_in_urb); -} + usb_kill_urb(port->write_urb); + + cancel_work_sync(&priv->unthrottle_work); + spin_lock_irq(&port->lock); + kfifo_reset(&port->write_fifo); + spin_unlock_irq(&port->lock); +} /* download the firmware to a "fake" device (pre-renumeration) */ static int keyspan_pda_fake_startup(struct usb_serial *serial) { + unsigned int vid = le16_to_cpu(serial->dev->descriptor.idVendor); const char *fw_name; /* download the firmware here ... */ ezusb_fx1_set_reset(serial->dev, 1); - if (0) { ; } -#ifdef KEYSPAN - else if (le16_to_cpu(serial->dev->descriptor.idVendor) == KEYSPAN_VENDOR_ID) + switch (vid) { + case KEYSPAN_VENDOR_ID: fw_name = "keyspan_pda/keyspan_pda.fw"; -#endif -#ifdef XIRCOM - else if ((le16_to_cpu(serial->dev->descriptor.idVendor) == XIRCOM_VENDOR_ID) || - (le16_to_cpu(serial->dev->descriptor.idVendor) == ENTREGA_VENDOR_ID)) + break; + case XIRCOM_VENDOR_ID: + case ENTREGA_VENDOR_ID: fw_name = "keyspan_pda/xircom_pgs.fw"; -#endif - else { + break; + default: dev_err(&serial->dev->dev, "%s: unknown vendor, aborting.\n", __func__); return -ENODEV; @@ -691,19 +643,17 @@ static int keyspan_pda_fake_startup(struct usb_serial *serial) return -ENOENT; } - /* after downloading firmware Renumeration will occur in a - moment and the new device will bind to the real driver */ + /* + * After downloading firmware renumeration will occur in a moment and + * the new device will bind to the real driver. + */ - /* we want this device to fail to have a driver assigned to it. */ + /* We want this device to fail to have a driver assigned to it. */ return 1; } -#ifdef KEYSPAN MODULE_FIRMWARE("keyspan_pda/keyspan_pda.fw"); -#endif -#ifdef XIRCOM MODULE_FIRMWARE("keyspan_pda/xircom_pgs.fw"); -#endif static int keyspan_pda_port_probe(struct usb_serial_port *port) { @@ -714,9 +664,7 @@ static int keyspan_pda_port_probe(struct usb_serial_port *port) if (!priv) return -ENOMEM; - INIT_WORK(&priv->wakeup_work, keyspan_pda_wakeup_write); INIT_WORK(&priv->unthrottle_work, keyspan_pda_request_unthrottle); - priv->serial = port->serial; priv->port = port; usb_set_serial_port_data(port, priv); @@ -734,7 +682,6 @@ static int keyspan_pda_port_remove(struct usb_serial_port *port) return 0; } -#ifdef KEYSPAN static struct usb_serial_driver keyspan_pda_fake_device = { .driver = { .owner = THIS_MODULE, @@ -745,20 +692,6 @@ static struct usb_serial_driver keyspan_pda_fake_device = { .num_ports = 1, .attach = keyspan_pda_fake_startup, }; -#endif - -#ifdef XIRCOM -static struct usb_serial_driver xircom_pgs_fake_device = { - .driver = { - .owner = THIS_MODULE, - .name = "xircom_no_firm", - }, - .description = "Xircom / Entrega PGS - (prerenumeration)", - .id_table = id_table_fake_xircom, - .num_ports = 1, - .attach = keyspan_pda_fake_startup, -}; -#endif static struct usb_serial_driver keyspan_pda_device = { .driver = { @@ -774,10 +707,8 @@ static struct usb_serial_driver keyspan_pda_device = { .open = keyspan_pda_open, .close = keyspan_pda_close, .write = keyspan_pda_write, - .write_room = keyspan_pda_write_room, - .write_bulk_callback = keyspan_pda_write_bulk_callback, + .write_bulk_callback = keyspan_pda_write_bulk_callback, .read_int_callback = keyspan_pda_rx_interrupt, - .chars_in_buffer = keyspan_pda_chars_in_buffer, .throttle = keyspan_pda_rx_throttle, .unthrottle = keyspan_pda_rx_unthrottle, .set_termios = keyspan_pda_set_termios, @@ -790,12 +721,7 @@ static struct usb_serial_driver keyspan_pda_device = { static struct usb_serial_driver * const serial_drivers[] = { &keyspan_pda_device, -#ifdef KEYSPAN &keyspan_pda_fake_device, -#endif -#ifdef XIRCOM - &xircom_pgs_fake_device, -#endif NULL }; diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index 5eed1078fac8..41ee2984a0df 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -79,14 +79,6 @@ MODULE_DEVICE_TABLE(usb, id_table); #define DCR_INIT_VAL 0x0c /* SLCTIN, nINIT */ #define ECR_INIT_VAL 0x00 /* SPP mode */ -struct urbtracker { - struct mos7715_parport *mos_parport; - struct list_head urblist_entry; - struct kref ref_count; - struct urb *urb; - struct usb_ctrlrequest *setup; -}; - enum mos7715_pp_modes { SPP = 0<<5, PS2 = 1<<5, /* moschip calls this 'NIBBLE' mode */ @@ -96,12 +88,9 @@ enum mos7715_pp_modes { struct mos7715_parport { struct parport *pp; /* back to containing struct */ struct kref ref_count; /* to instance of this struct */ - struct list_head deferred_urbs; /* list deferred async urbs */ - struct list_head active_urbs; /* list async urbs in flight */ - spinlock_t listlock; /* protects list access */ bool msg_pending; /* usb sync call pending */ struct completion syncmsg_compl; /* usb sync call completed */ - struct tasklet_struct urb_tasklet; /* for sending deferred urbs */ + struct work_struct work; /* restore deferred writes */ struct usb_serial *serial; /* back to containing struct */ __u8 shadowECR; /* parallel port regs... */ __u8 shadowDCR; @@ -265,174 +254,8 @@ static void destroy_mos_parport(struct kref *kref) kfree(mos_parport); } -static void destroy_urbtracker(struct kref *kref) -{ - struct urbtracker *urbtrack = - container_of(kref, struct urbtracker, ref_count); - struct mos7715_parport *mos_parport = urbtrack->mos_parport; - - usb_free_urb(urbtrack->urb); - kfree(urbtrack->setup); - kfree(urbtrack); - kref_put(&mos_parport->ref_count, destroy_mos_parport); -} - /* - * This runs as a tasklet when sending an urb in a non-blocking parallel - * port callback had to be deferred because the disconnect mutex could not be - * obtained at the time. - */ -static void send_deferred_urbs(struct tasklet_struct *t) -{ - int ret_val; - unsigned long flags; - struct mos7715_parport *mos_parport = from_tasklet(mos_parport, t, - urb_tasklet); - struct urbtracker *urbtrack, *tmp; - struct list_head *cursor, *next; - struct device *dev; - - /* if release function ran, game over */ - if (unlikely(mos_parport->serial == NULL)) - return; - - dev = &mos_parport->serial->dev->dev; - - /* try again to get the mutex */ - if (!mutex_trylock(&mos_parport->serial->disc_mutex)) { - dev_dbg(dev, "%s: rescheduling tasklet\n", __func__); - tasklet_schedule(&mos_parport->urb_tasklet); - return; - } - - /* if device disconnected, game over */ - if (unlikely(mos_parport->serial->disconnected)) { - mutex_unlock(&mos_parport->serial->disc_mutex); - return; - } - - spin_lock_irqsave(&mos_parport->listlock, flags); - if (list_empty(&mos_parport->deferred_urbs)) { - spin_unlock_irqrestore(&mos_parport->listlock, flags); - mutex_unlock(&mos_parport->serial->disc_mutex); - dev_dbg(dev, "%s: deferred_urbs list empty\n", __func__); - return; - } - - /* move contents of deferred_urbs list to active_urbs list and submit */ - list_for_each_safe(cursor, next, &mos_parport->deferred_urbs) - list_move_tail(cursor, &mos_parport->active_urbs); - list_for_each_entry_safe(urbtrack, tmp, &mos_parport->active_urbs, - urblist_entry) { - ret_val = usb_submit_urb(urbtrack->urb, GFP_ATOMIC); - dev_dbg(dev, "%s: urb submitted\n", __func__); - if (ret_val) { - dev_err(dev, "usb_submit_urb() failed: %d\n", ret_val); - list_del(&urbtrack->urblist_entry); - kref_put(&urbtrack->ref_count, destroy_urbtracker); - } - } - spin_unlock_irqrestore(&mos_parport->listlock, flags); - mutex_unlock(&mos_parport->serial->disc_mutex); -} - -/* callback for parallel port control urbs submitted asynchronously */ -static void async_complete(struct urb *urb) -{ - struct urbtracker *urbtrack = urb->context; - int status = urb->status; - unsigned long flags; - - if (unlikely(status)) - dev_dbg(&urb->dev->dev, "%s - nonzero urb status received: %d\n", __func__, status); - - /* remove the urbtracker from the active_urbs list */ - spin_lock_irqsave(&urbtrack->mos_parport->listlock, flags); - list_del(&urbtrack->urblist_entry); - spin_unlock_irqrestore(&urbtrack->mos_parport->listlock, flags); - kref_put(&urbtrack->ref_count, destroy_urbtracker); -} - -static int write_parport_reg_nonblock(struct mos7715_parport *mos_parport, - enum mos_regs reg, __u8 data) -{ - struct urbtracker *urbtrack; - int ret_val; - unsigned long flags; - struct usb_serial *serial = mos_parport->serial; - struct usb_device *usbdev = serial->dev; - - /* create and initialize the control urb and containing urbtracker */ - urbtrack = kmalloc(sizeof(struct urbtracker), GFP_ATOMIC); - if (!urbtrack) - return -ENOMEM; - - urbtrack->urb = usb_alloc_urb(0, GFP_ATOMIC); - if (!urbtrack->urb) { - kfree(urbtrack); - return -ENOMEM; - } - urbtrack->setup = kmalloc(sizeof(*urbtrack->setup), GFP_ATOMIC); - if (!urbtrack->setup) { - usb_free_urb(urbtrack->urb); - kfree(urbtrack); - return -ENOMEM; - } - urbtrack->setup->bRequestType = (__u8)0x40; - urbtrack->setup->bRequest = (__u8)0x0e; - urbtrack->setup->wValue = cpu_to_le16(get_reg_value(reg, dummy)); - urbtrack->setup->wIndex = cpu_to_le16(get_reg_index(reg)); - urbtrack->setup->wLength = 0; - usb_fill_control_urb(urbtrack->urb, usbdev, - usb_sndctrlpipe(usbdev, 0), - (unsigned char *)urbtrack->setup, - NULL, 0, async_complete, urbtrack); - kref_get(&mos_parport->ref_count); - urbtrack->mos_parport = mos_parport; - kref_init(&urbtrack->ref_count); - INIT_LIST_HEAD(&urbtrack->urblist_entry); - - /* - * get the disconnect mutex, or add tracker to the deferred_urbs list - * and schedule a tasklet to try again later - */ - if (!mutex_trylock(&serial->disc_mutex)) { - spin_lock_irqsave(&mos_parport->listlock, flags); - list_add_tail(&urbtrack->urblist_entry, - &mos_parport->deferred_urbs); - spin_unlock_irqrestore(&mos_parport->listlock, flags); - tasklet_schedule(&mos_parport->urb_tasklet); - dev_dbg(&usbdev->dev, "tasklet scheduled\n"); - return 0; - } - - /* bail if device disconnected */ - if (serial->disconnected) { - kref_put(&urbtrack->ref_count, destroy_urbtracker); - mutex_unlock(&serial->disc_mutex); - return -ENODEV; - } - - /* add the tracker to the active_urbs list and submit */ - spin_lock_irqsave(&mos_parport->listlock, flags); - list_add_tail(&urbtrack->urblist_entry, &mos_parport->active_urbs); - spin_unlock_irqrestore(&mos_parport->listlock, flags); - ret_val = usb_submit_urb(urbtrack->urb, GFP_ATOMIC); - mutex_unlock(&serial->disc_mutex); - if (ret_val) { - dev_err(&usbdev->dev, - "%s: submit_urb() failed: %d\n", __func__, ret_val); - spin_lock_irqsave(&mos_parport->listlock, flags); - list_del(&urbtrack->urblist_entry); - spin_unlock_irqrestore(&mos_parport->listlock, flags); - kref_put(&urbtrack->ref_count, destroy_urbtracker); - return ret_val; - } - return 0; -} - -/* - * This is the the common top part of all parallel port callback operations that + * This is the common top part of all parallel port callback operations that * send synchronous messages to the device. This implements convoluted locking * that avoids two scenarios: (1) a port operation is called after usbserial * has called our release function, at which point struct mos7715_parport has @@ -458,6 +281,10 @@ static int parport_prologue(struct parport *pp) reinit_completion(&mos_parport->syncmsg_compl); spin_unlock(&release_lock); + /* ensure writes from restore are submitted before new requests */ + if (work_pending(&mos_parport->work)) + flush_work(&mos_parport->work); + mutex_lock(&mos_parport->serial->disc_mutex); if (mos_parport->serial->disconnected) { /* device disconnected */ @@ -482,6 +309,26 @@ static inline void parport_epilogue(struct parport *pp) complete(&mos_parport->syncmsg_compl); } +static void deferred_restore_writes(struct work_struct *work) +{ + struct mos7715_parport *mos_parport; + + mos_parport = container_of(work, struct mos7715_parport, work); + + mutex_lock(&mos_parport->serial->disc_mutex); + + /* if device disconnected, game over */ + if (mos_parport->serial->disconnected) + goto done; + + write_mos_reg(mos_parport->serial, dummy, MOS7720_DCR, + mos_parport->shadowDCR); + write_mos_reg(mos_parport->serial, dummy, MOS7720_ECR, + mos_parport->shadowECR); +done: + mutex_unlock(&mos_parport->serial->disc_mutex); +} + static void parport_mos7715_write_data(struct parport *pp, unsigned char d) { struct mos7715_parport *mos_parport = pp->private_data; @@ -639,10 +486,10 @@ static void parport_mos7715_restore_state(struct parport *pp, spin_unlock(&release_lock); return; } - write_parport_reg_nonblock(mos_parport, MOS7720_DCR, - mos_parport->shadowDCR); - write_parport_reg_nonblock(mos_parport, MOS7720_ECR, - mos_parport->shadowECR); + mos_parport->shadowDCR = s->u.pc.ctr; + mos_parport->shadowECR = s->u.pc.ecr; + + schedule_work(&mos_parport->work); spin_unlock(&release_lock); } @@ -712,12 +559,9 @@ static int mos7715_parport_init(struct usb_serial *serial) mos_parport->msg_pending = false; kref_init(&mos_parport->ref_count); - spin_lock_init(&mos_parport->listlock); - INIT_LIST_HEAD(&mos_parport->active_urbs); - INIT_LIST_HEAD(&mos_parport->deferred_urbs); usb_set_serial_data(serial, mos_parport); /* hijack private pointer */ mos_parport->serial = serial; - tasklet_setup(&mos_parport->urb_tasklet, send_deferred_urbs); + INIT_WORK(&mos_parport->work, deferred_restore_writes); init_completion(&mos_parport->syncmsg_compl); /* cycle parallel port reset bit */ @@ -1867,8 +1711,6 @@ static void mos7720_release(struct usb_serial *serial) if (le16_to_cpu(serial->dev->descriptor.idProduct) == MOSCHIP_DEVICE_ID_7715) { - struct urbtracker *urbtrack; - unsigned long flags; struct mos7715_parport *mos_parport = usb_get_serial_data(serial); @@ -1881,21 +1723,17 @@ static void mos7720_release(struct usb_serial *serial) if (mos_parport->msg_pending) wait_for_completion_timeout(&mos_parport->syncmsg_compl, msecs_to_jiffies(MOS_WDR_TIMEOUT)); + /* + * If delayed work is currently scheduled, wait for it to + * complete. This also implies barriers that ensure the + * below serial clearing is not hoisted above the ->work. + */ + cancel_work_sync(&mos_parport->work); parport_remove_port(mos_parport->pp); usb_set_serial_data(serial, NULL); mos_parport->serial = NULL; - /* if tasklet currently scheduled, wait for it to complete */ - tasklet_kill(&mos_parport->urb_tasklet); - - /* unlink any urbs sent by the tasklet */ - spin_lock_irqsave(&mos_parport->listlock, flags); - list_for_each_entry(urbtrack, - &mos_parport->active_urbs, - urblist_entry) - usb_unlink_urb(urbtrack->urb); - spin_unlock_irqrestore(&mos_parport->listlock, flags); parport_del_port(mos_parport->pp); kref_put(&mos_parport->ref_count, destroy_mos_parport); diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 56d6f6d83bd7..2c21e34235bb 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -563,6 +563,9 @@ static void option_instat_callback(struct urb *urb); /* Device flags */ +/* Highest interface number which can be used with NCTRL() and RSVD() */ +#define FLAG_IFNUM_MAX 7 + /* Interface does not support modem-control requests */ #define NCTRL(ifnum) ((BIT(ifnum) & 0xff) << 8) @@ -2101,6 +2104,14 @@ static struct usb_serial_driver * const serial_drivers[] = { module_usb_serial_driver(serial_drivers, option_ids); +static bool iface_is_reserved(unsigned long device_flags, u8 ifnum) +{ + if (ifnum > FLAG_IFNUM_MAX) + return false; + + return device_flags & RSVD(ifnum); +} + static int option_probe(struct usb_serial *serial, const struct usb_device_id *id) { @@ -2117,7 +2128,7 @@ static int option_probe(struct usb_serial *serial, * the same class/subclass/protocol as the serial interfaces. Look at * the Windows driver .INF files for reserved interface numbers. */ - if (device_flags & RSVD(iface_desc->bInterfaceNumber)) + if (iface_is_reserved(device_flags, iface_desc->bInterfaceNumber)) return -ENODEV; /* @@ -2133,6 +2144,14 @@ static int option_probe(struct usb_serial *serial, return 0; } +static bool iface_no_modem_control(unsigned long device_flags, u8 ifnum) +{ + if (ifnum > FLAG_IFNUM_MAX) + return false; + + return device_flags & NCTRL(ifnum); +} + static int option_attach(struct usb_serial *serial) { struct usb_interface_descriptor *iface_desc; @@ -2148,7 +2167,7 @@ static int option_attach(struct usb_serial *serial) iface_desc = &serial->interface->cur_altsetting->desc; - if (!(device_flags & NCTRL(iface_desc->bInterfaceNumber))) + if (!iface_no_modem_control(device_flags, iface_desc->bInterfaceNumber)) data->use_send_setup = 1; if (device_flags & ZLP) diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c index 98c1aa594e6c..5f7d678502be 100644 --- a/drivers/usb/storage/ene_ub6250.c +++ b/drivers/usb/storage/ene_ub6250.c @@ -861,6 +861,7 @@ static int ms_count_freeblock(struct us_data *us, u16 PhyBlock) case MS_LB_NOT_USED: case MS_LB_NOT_USED_ERASED: Count++; + break; default: break; } diff --git a/drivers/usb/storage/freecom.c b/drivers/usb/storage/freecom.c index 3d5f7d0ff0f1..2b098b55c4cb 100644 --- a/drivers/usb/storage/freecom.c +++ b/drivers/usb/storage/freecom.c @@ -431,7 +431,6 @@ static int freecom_transport(struct scsi_cmnd *srb, struct us_data *us) us->srb->sc_data_direction); /* Return fail, SCSI seems to handle this better. */ return USB_STOR_TRANSPORT_FAILED; - break; } return USB_STOR_TRANSPORT_GOOD; diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index 238a8088e17f..5eb895b19c55 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -416,7 +416,7 @@ static int usb_stor_bulk_transfer_sglist(struct us_data *us, unsigned int pipe, /* don't submit s-g requests during abort processing */ if (test_bit(US_FLIDX_ABORTING, &us->dflags)) - return USB_STOR_XFER_ERROR; + goto usb_stor_xfer_error; /* initialize the scatter-gather request block */ usb_stor_dbg(us, "xfer %u bytes, %d entries\n", length, num_sg); @@ -424,7 +424,7 @@ static int usb_stor_bulk_transfer_sglist(struct us_data *us, unsigned int pipe, sg, num_sg, length, GFP_NOIO); if (result) { usb_stor_dbg(us, "usb_sg_init returned %d\n", result); - return USB_STOR_XFER_ERROR; + goto usb_stor_xfer_error; } /* @@ -452,6 +452,11 @@ static int usb_stor_bulk_transfer_sglist(struct us_data *us, unsigned int pipe, *act_len = us->current_sg.bytes; return interpret_urb_result(us, pipe, length, result, us->current_sg.bytes); + +usb_stor_xfer_error: + if (act_len) + *act_len = 0; + return USB_STOR_XFER_ERROR; } /* diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 652d6d6f1f36..bef89c6bd1d7 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -690,6 +690,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, fallthrough; case DMA_TO_DEVICE: cmdinfo->state |= ALLOC_DATA_OUT_URB | SUBMIT_DATA_OUT_URB; + break; case DMA_NONE: break; } @@ -867,6 +868,9 @@ static int uas_slave_configure(struct scsi_device *sdev) if (devinfo->flags & US_FL_NO_READ_CAPACITY_16) sdev->no_read_capacity_16 = 1; + /* Some disks cannot handle WRITE_SAME */ + if (devinfo->flags & US_FL_NO_SAME) + sdev->no_write_same = 1; /* * Some disks return the total number of blocks in response * to READ CAPACITY rather than the highest block number. diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h index 711ab240058c..870e9cf3d5dc 100644 --- a/drivers/usb/storage/unusual_uas.h +++ b/drivers/usb/storage/unusual_uas.h @@ -35,12 +35,15 @@ UNUSUAL_DEV(0x054c, 0x087d, 0x0000, 0x9999, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NO_REPORT_OPCODES), -/* Reported-by: Julian Groß <julian.g@posteo.de> */ +/* + * Initially Reported-by: Julian Groß <julian.g@posteo.de> + * Further reports David C. Partridge <david.partridge@perdrix.co.uk> + */ UNUSUAL_DEV(0x059f, 0x105f, 0x0000, 0x9999, "LaCie", "2Big Quadra USB3", USB_SC_DEVICE, USB_PR_DEVICE, NULL, - US_FL_NO_REPORT_OPCODES), + US_FL_NO_REPORT_OPCODES | US_FL_NO_SAME), /* * Apricorn USB3 dongle sometimes returns "USBSUSBSUSBS" in response to SCSI diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 94a64729dc27..90aa9c12ffac 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -541,6 +541,9 @@ void usb_stor_adjust_quirks(struct usb_device *udev, unsigned long *fflags) case 'j': f |= US_FL_NO_REPORT_LUNS; break; + case 'k': + f |= US_FL_NO_SAME; + break; case 'l': f |= US_FL_NOT_LOCKABLE; break; diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig index e7f120874c48..270e81c087e9 100644 --- a/drivers/usb/typec/Kconfig +++ b/drivers/usb/typec/Kconfig @@ -64,8 +64,9 @@ config TYPEC_HD3SS3220 config TYPEC_TPS6598X tristate "TI TPS6598x USB Power Delivery controller driver" depends on I2C - depends on REGMAP_I2C - depends on USB_ROLE_SWITCH || !USB_ROLE_SWITCH + 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. diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 35eec707cb51..ebfd3113a9a8 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -11,6 +11,7 @@ #include <linux/mutex.h> #include <linux/property.h> #include <linux/slab.h> +#include <linux/usb/pd_vdo.h> #include "bus.h" @@ -18,6 +19,7 @@ struct typec_plug { struct device dev; enum typec_plug_index index; struct ida mode_ids; + int num_altmodes; }; struct typec_cable { @@ -33,6 +35,7 @@ struct typec_partner { struct usb_pd_identity *identity; enum typec_accessory accessory; struct ida mode_ids; + int num_altmodes; }; struct typec_port { @@ -81,6 +84,29 @@ static const char * const typec_accessory_modes[] = { [TYPEC_ACCESSORY_DEBUG] = "debug", }; +/* Product types defined in USB PD Specification R3.0 V2.0 */ +static const char * const product_type_ufp[8] = { + [IDH_PTYPE_UNDEF] = "undefined", + [IDH_PTYPE_HUB] = "hub", + [IDH_PTYPE_PERIPH] = "peripheral", + [IDH_PTYPE_PSD] = "psd", + [IDH_PTYPE_AMA] = "ama", +}; + +static const char * const product_type_dfp[8] = { + [IDH_PTYPE_DFP_UNDEF] = "undefined", + [IDH_PTYPE_DFP_HUB] = "hub", + [IDH_PTYPE_DFP_HOST] = "host", + [IDH_PTYPE_DFP_PB] = "power_brick", + [IDH_PTYPE_DFP_AMC] = "amc", +}; + +static const char * const product_type_cable[8] = { + [IDH_PTYPE_UNDEF] = "undefined", + [IDH_PTYPE_PCABLE] = "passive", + [IDH_PTYPE_ACABLE] = "active", +}; + static struct usb_pd_identity *get_pd_identity(struct device *dev) { if (is_typec_partner(dev)) { @@ -95,6 +121,32 @@ static struct usb_pd_identity *get_pd_identity(struct device *dev) return NULL; } +static const char *get_pd_product_type(struct device *dev) +{ + struct typec_port *port = to_typec_port(dev->parent); + struct usb_pd_identity *id = get_pd_identity(dev); + const char *ptype = NULL; + + if (is_typec_partner(dev)) { + if (!id) + return NULL; + + if (port->data_role == TYPEC_HOST) + ptype = product_type_ufp[PD_IDH_PTYPE(id->id_header)]; + else + ptype = product_type_dfp[PD_IDH_DFP_PTYPE(id->id_header)]; + } else if (is_typec_cable(dev)) { + if (id) + ptype = product_type_cable[PD_IDH_PTYPE(id->id_header)]; + else + ptype = to_typec_cable(dev)->active ? + product_type_cable[IDH_PTYPE_ACABLE] : + product_type_cable[IDH_PTYPE_PCABLE]; + } + + return ptype; +} + static ssize_t id_header_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -122,10 +174,40 @@ static ssize_t product_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RO(product); +static ssize_t product_type_vdo1_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct usb_pd_identity *id = get_pd_identity(dev); + + return sysfs_emit(buf, "0x%08x\n", id->vdo[0]); +} +static DEVICE_ATTR_RO(product_type_vdo1); + +static ssize_t product_type_vdo2_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct usb_pd_identity *id = get_pd_identity(dev); + + return sysfs_emit(buf, "0x%08x\n", id->vdo[1]); +} +static DEVICE_ATTR_RO(product_type_vdo2); + +static ssize_t product_type_vdo3_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct usb_pd_identity *id = get_pd_identity(dev); + + return sysfs_emit(buf, "0x%08x\n", id->vdo[2]); +} +static DEVICE_ATTR_RO(product_type_vdo3); + static struct attribute *usb_pd_id_attrs[] = { &dev_attr_id_header.attr, &dev_attr_cert_stat.attr, &dev_attr_product.attr, + &dev_attr_product_type_vdo1.attr, + &dev_attr_product_type_vdo2.attr, + &dev_attr_product_type_vdo3.attr, NULL }; @@ -139,12 +221,48 @@ static const struct attribute_group *usb_pd_id_groups[] = { NULL, }; +static void typec_product_type_notify(struct device *dev) +{ + char *envp[2] = { }; + const char *ptype; + + ptype = get_pd_product_type(dev); + if (!ptype) + return; + + sysfs_notify(&dev->kobj, NULL, "type"); + + envp[0] = kasprintf(GFP_KERNEL, "PRODUCT_TYPE=%s", ptype); + if (!envp[0]) + return; + + kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp); + kfree(envp[0]); +} + static void typec_report_identity(struct device *dev) { sysfs_notify(&dev->kobj, "identity", "id_header"); sysfs_notify(&dev->kobj, "identity", "cert_stat"); sysfs_notify(&dev->kobj, "identity", "product"); + sysfs_notify(&dev->kobj, "identity", "product_type_vdo1"); + sysfs_notify(&dev->kobj, "identity", "product_type_vdo2"); + sysfs_notify(&dev->kobj, "identity", "product_type_vdo3"); + typec_product_type_notify(dev); +} + +static ssize_t +type_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + const char *ptype; + + ptype = get_pd_product_type(dev); + if (!ptype) + return 0; + + return sysfs_emit(buf, "%s\n", ptype); } +static DEVICE_ATTR_RO(type); /* ------------------------------------------------------------------------- */ /* Alternate Modes */ @@ -382,7 +500,7 @@ static umode_t typec_altmode_attr_is_visible(struct kobject *kobj, return attr->mode; } -static struct attribute_group typec_altmode_group = { +static const struct attribute_group typec_altmode_group = { .is_visible = typec_altmode_attr_is_visible, .attrs = typec_altmode_attrs, }; @@ -482,6 +600,10 @@ typec_register_altmode(struct device *parent, if (is_typec_partner(parent)) alt->adev.dev.bus = &typec_bus; + /* Plug alt modes need a class to generate udev events. */ + if (is_typec_plug(parent)) + alt->adev.dev.class = typec_class; + ret = device_register(&alt->adev.dev); if (ret) { dev_err(parent, "failed to register alternate mode (%d)\n", @@ -532,12 +654,60 @@ static ssize_t supports_usb_power_delivery_show(struct device *dev, } static DEVICE_ATTR_RO(supports_usb_power_delivery); +static ssize_t number_of_alternate_modes_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct typec_partner *partner; + struct typec_plug *plug; + int num_altmodes; + + if (is_typec_partner(dev)) { + partner = to_typec_partner(dev); + num_altmodes = partner->num_altmodes; + } else if (is_typec_plug(dev)) { + plug = to_typec_plug(dev); + num_altmodes = plug->num_altmodes; + } else { + return 0; + } + + return sysfs_emit(buf, "%d\n", num_altmodes); +} +static DEVICE_ATTR_RO(number_of_alternate_modes); + static struct attribute *typec_partner_attrs[] = { &dev_attr_accessory_mode.attr, &dev_attr_supports_usb_power_delivery.attr, + &dev_attr_number_of_alternate_modes.attr, + &dev_attr_type.attr, + NULL +}; + +static umode_t typec_partner_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n) +{ + struct typec_partner *partner = to_typec_partner(kobj_to_dev(kobj)); + + if (attr == &dev_attr_number_of_alternate_modes.attr) { + if (partner->num_altmodes < 0) + return 0; + } + + if (attr == &dev_attr_type.attr) + if (!get_pd_product_type(kobj_to_dev(kobj))) + return 0; + + return attr->mode; +} + +static const struct attribute_group typec_partner_group = { + .is_visible = typec_partner_attr_is_visible, + .attrs = typec_partner_attrs +}; + +static const struct attribute_group *typec_partner_groups[] = { + &typec_partner_group, NULL }; -ATTRIBUTE_GROUPS(typec_partner); static void typec_partner_release(struct device *dev) { @@ -571,6 +741,37 @@ int typec_partner_set_identity(struct typec_partner *partner) EXPORT_SYMBOL_GPL(typec_partner_set_identity); /** + * typec_partner_set_num_altmodes - Set the number of available partner altmodes + * @partner: The partner to be updated. + * @num_altmodes: The number of altmodes we want to specify as available. + * + * This routine is used to report the number of alternate modes supported by the + * partner. This value is *not* enforced in alternate mode registration routines. + * + * @partner.num_altmodes is set to -1 on partner registration, denoting that + * a valid value has not been set for it yet. + * + * Returns 0 on success or negative error number on failure. + */ +int typec_partner_set_num_altmodes(struct typec_partner *partner, int num_altmodes) +{ + int ret; + + if (num_altmodes < 0) + return -EINVAL; + + partner->num_altmodes = num_altmodes; + ret = sysfs_update_group(&partner->dev.kobj, &typec_partner_group); + if (ret < 0) + return ret; + + sysfs_notify(&partner->dev.kobj, NULL, "number_of_alternate_modes"); + + return 0; +} +EXPORT_SYMBOL_GPL(typec_partner_set_num_altmodes); + +/** * typec_partner_register_altmode - Register USB Type-C Partner Alternate Mode * @partner: USB Type-C Partner that supports the alternate mode * @desc: Description of the alternate mode @@ -612,6 +813,7 @@ struct typec_partner *typec_register_partner(struct typec_port *port, ida_init(&partner->mode_ids); partner->usb_pd = desc->usb_pd; partner->accessory = desc->accessory; + partner->num_altmodes = -1; if (desc->identity) { /* @@ -662,12 +864,71 @@ static void typec_plug_release(struct device *dev) kfree(plug); } +static struct attribute *typec_plug_attrs[] = { + &dev_attr_number_of_alternate_modes.attr, + NULL +}; + +static umode_t typec_plug_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n) +{ + struct typec_plug *plug = to_typec_plug(kobj_to_dev(kobj)); + + if (attr == &dev_attr_number_of_alternate_modes.attr) { + if (plug->num_altmodes < 0) + return 0; + } + + return attr->mode; +} + +static const struct attribute_group typec_plug_group = { + .is_visible = typec_plug_attr_is_visible, + .attrs = typec_plug_attrs +}; + +static const struct attribute_group *typec_plug_groups[] = { + &typec_plug_group, + NULL +}; + static const struct device_type typec_plug_dev_type = { .name = "typec_plug", + .groups = typec_plug_groups, .release = typec_plug_release, }; /** + * typec_plug_set_num_altmodes - Set the number of available plug altmodes + * @plug: The plug to be updated. + * @num_altmodes: The number of altmodes we want to specify as available. + * + * This routine is used to report the number of alternate modes supported by the + * plug. This value is *not* enforced in alternate mode registration routines. + * + * @plug.num_altmodes is set to -1 on plug registration, denoting that + * a valid value has not been set for it yet. + * + * Returns 0 on success or negative error number on failure. + */ +int typec_plug_set_num_altmodes(struct typec_plug *plug, int num_altmodes) +{ + int ret; + + if (num_altmodes < 0) + return -EINVAL; + + plug->num_altmodes = num_altmodes; + ret = sysfs_update_group(&plug->dev.kobj, &typec_plug_group); + if (ret < 0) + return ret; + + sysfs_notify(&plug->dev.kobj, NULL, "number_of_alternate_modes"); + + return 0; +} +EXPORT_SYMBOL_GPL(typec_plug_set_num_altmodes); + +/** * typec_plug_register_altmode - Register USB Type-C Cable Plug Alternate Mode * @plug: USB Type-C Cable Plug that supports the alternate mode * @desc: Description of the alternate mode @@ -712,6 +973,7 @@ struct typec_plug *typec_register_plug(struct typec_cable *cable, sprintf(name, "plug%d", desc->index); ida_init(&plug->mode_ids); + plug->num_altmodes = -1; plug->index = desc->index; plug->dev.class = typec_class; plug->dev.parent = &cable->dev; @@ -744,15 +1006,6 @@ EXPORT_SYMBOL_GPL(typec_unregister_plug); /* Type-C Cables */ -static ssize_t -type_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct typec_cable *cable = to_typec_cable(dev); - - return sprintf(buf, "%s\n", cable->active ? "active" : "passive"); -} -static DEVICE_ATTR_RO(type); - static const char * const typec_plug_types[] = { [USB_PLUG_NONE] = "unknown", [USB_PLUG_TYPE_A] = "type-a", @@ -1309,7 +1562,7 @@ static umode_t typec_attr_is_visible(struct kobject *kobj, return attr->mode; } -static struct attribute_group typec_group = { +static const struct attribute_group typec_group = { .is_visible = typec_attr_is_visible, .attrs = typec_attrs, }; @@ -1352,6 +1605,11 @@ const struct device_type typec_port_dev_type = { /* --------------------------------------- */ /* Driver callbacks to report role updates */ +static int partner_match(struct device *dev, void *data) +{ + return is_typec_partner(dev); +} + /** * typec_set_data_role - Report data role change * @port: The USB Type-C Port where the role was changed @@ -1361,12 +1619,23 @@ const struct device_type typec_port_dev_type = { */ void typec_set_data_role(struct typec_port *port, enum typec_data_role role) { + struct device *partner_dev; + if (port->data_role == role) return; port->data_role = role; sysfs_notify(&port->dev.kobj, NULL, "data_role"); kobject_uevent(&port->dev.kobj, KOBJ_CHANGE); + + partner_dev = device_find_child(&port->dev, NULL, partner_match); + if (!partner_dev) + return; + + if (to_typec_partner(partner_dev)->identity) + typec_product_type_notify(partner_dev); + + put_device(partner_dev); } EXPORT_SYMBOL_GPL(typec_set_data_role); @@ -1407,11 +1676,6 @@ void typec_set_vconn_role(struct typec_port *port, enum typec_role role) } EXPORT_SYMBOL_GPL(typec_set_vconn_role); -static int partner_match(struct device *dev, void *data) -{ - return is_typec_partner(dev); -} - /** * typec_set_pwr_opmode - Report changed power operation mode * @port: The USB Type-C Port where the mode was changed diff --git a/drivers/usb/typec/mux/intel_pmc_mux.c b/drivers/usb/typec/mux/intel_pmc_mux.c index d7f63b74c6b1..cf37a59ce130 100644 --- a/drivers/usb/typec/mux/intel_pmc_mux.c +++ b/drivers/usb/typec/mux/intel_pmc_mux.c @@ -176,6 +176,7 @@ static int hsl_orientation(struct pmc_usb_port *port) static int pmc_usb_command(struct pmc_usb_port *port, u8 *msg, u32 len) { u8 response[4]; + u8 status_res; int ret; /* @@ -189,9 +190,13 @@ static int pmc_usb_command(struct pmc_usb_port *port, u8 *msg, u32 len) if (ret) return ret; - if (response[2] & PMC_USB_RESP_STATUS_FAILURE) { - if (response[2] & PMC_USB_RESP_STATUS_FATAL) + status_res = (msg[0] & 0xf) < PMC_USB_SAFE_MODE ? + response[2] : response[1]; + + if (status_res & PMC_USB_RESP_STATUS_FAILURE) { + if (status_res & PMC_USB_RESP_STATUS_FATAL) return -EIO; + return -EBUSY; } @@ -256,6 +261,7 @@ static int pmc_usb_mux_tbt(struct pmc_usb_port *port, struct typec_mux_state *state) { struct typec_thunderbolt_data *data = state->data; + u8 cable_rounded = TBT_CABLE_ROUNDED_SUPPORT(data->cable_mode); u8 cable_speed = TBT_CABLE_SPEED(data->cable_mode); struct altmode_req req = { }; @@ -284,6 +290,8 @@ pmc_usb_mux_tbt(struct pmc_usb_port *port, struct typec_mux_state *state) req.mode_data |= PMC_USB_ALTMODE_CABLE_SPD(cable_speed); + req.mode_data |= PMC_USB_ALTMODE_TBT_GEN(cable_rounded); + return pmc_usb_command(port, (void *)&req, sizeof(req)); } @@ -319,6 +327,11 @@ pmc_usb_mux_usb4(struct pmc_usb_port *port, struct typec_mux_state *state) fallthrough; default: req.mode_data |= PMC_USB_ALTMODE_ACTIVE_CABLE; + + /* Configure data rate to rounded in the case of Active TBT3 + * and USB4 cables. + */ + req.mode_data |= PMC_USB_ALTMODE_TBT_GEN(1); break; } diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c index 99562cc65ca6..ebc46b9f776c 100644 --- a/drivers/usb/typec/tcpm/fusb302.c +++ b/drivers/usb/typec/tcpm/fusb302.c @@ -343,12 +343,11 @@ static int fusb302_sw_reset(struct fusb302_chip *chip) return ret; } -static int fusb302_enable_tx_auto_retries(struct fusb302_chip *chip) +static int fusb302_enable_tx_auto_retries(struct fusb302_chip *chip, u8 retry_count) { int ret = 0; - ret = fusb302_i2c_set_bits(chip, FUSB_REG_CONTROL3, - FUSB_REG_CONTROL3_N_RETRIES_3 | + ret = fusb302_i2c_set_bits(chip, FUSB_REG_CONTROL3, retry_count | FUSB_REG_CONTROL3_AUTO_RETRY); return ret; @@ -399,7 +398,7 @@ static int tcpm_init(struct tcpc_dev *dev) ret = fusb302_sw_reset(chip); if (ret < 0) return ret; - ret = fusb302_enable_tx_auto_retries(chip); + ret = fusb302_enable_tx_auto_retries(chip, FUSB_REG_CONTROL3_N_RETRIES_3); if (ret < 0) return ret; ret = fusb302_init_interrupt(chip); @@ -1017,7 +1016,7 @@ static const char * const transmit_type_name[] = { }; static int tcpm_pd_transmit(struct tcpc_dev *dev, enum tcpm_transmit_type type, - const struct pd_message *msg) + const struct pd_message *msg, unsigned int negotiated_rev) { struct fusb302_chip *chip = container_of(dev, struct fusb302_chip, tcpc_dev); @@ -1026,6 +1025,13 @@ static int tcpm_pd_transmit(struct tcpc_dev *dev, enum tcpm_transmit_type type, mutex_lock(&chip->lock); switch (type) { case TCPC_TX_SOP: + /* nRetryCount 3 in P2.0 spec, whereas 2 in PD3.0 spec */ + ret = fusb302_enable_tx_auto_retries(chip, negotiated_rev > PD_REV20 ? + FUSB_REG_CONTROL3_N_RETRIES_2 : + FUSB_REG_CONTROL3_N_RETRIES_3); + if (ret < 0) + fusb302_log(chip, "Cannot update retry count ret=%d", ret); + ret = fusb302_pd_send_message(chip, msg); if (ret < 0) fusb302_log(chip, diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c index f9f0af64da5f..f676abab044b 100644 --- a/drivers/usb/typec/tcpm/tcpci.c +++ b/drivers/usb/typec/tcpm/tcpci.c @@ -18,7 +18,11 @@ #include "tcpci.h" -#define PD_RETRY_COUNT 3 +#define PD_RETRY_COUNT_DEFAULT 3 +#define PD_RETRY_COUNT_3_0_OR_HIGHER 2 +#define AUTO_DISCHARGE_DEFAULT_THRESHOLD_MV 3500 +#define AUTO_DISCHARGE_PD_HEADROOM_MV 850 +#define AUTO_DISCHARGE_PPS_HEADROOM_MV 1250 struct tcpci { struct device *dev; @@ -268,6 +272,58 @@ static int tcpci_set_vconn(struct tcpc_dev *tcpc, bool enable) enable ? TCPC_POWER_CTRL_VCONN_ENABLE : 0); } +static int tcpci_enable_auto_vbus_discharge(struct tcpc_dev *dev, bool enable) +{ + struct tcpci *tcpci = tcpc_to_tcpci(dev); + int ret; + + ret = regmap_update_bits(tcpci->regmap, TCPC_POWER_CTRL, TCPC_POWER_CTRL_AUTO_DISCHARGE, + enable ? TCPC_POWER_CTRL_AUTO_DISCHARGE : 0); + return ret; +} + +static int tcpci_set_auto_vbus_discharge_threshold(struct tcpc_dev *dev, enum typec_pwr_opmode mode, + bool pps_active, u32 requested_vbus_voltage_mv) +{ + struct tcpci *tcpci = tcpc_to_tcpci(dev); + unsigned int pwr_ctrl, threshold = 0; + int ret; + + /* + * Indicates that vbus is going to go away due PR_SWAP, hard reset etc. + * Do not discharge vbus here. + */ + if (requested_vbus_voltage_mv == 0) + goto write_thresh; + + ret = regmap_read(tcpci->regmap, TCPC_POWER_CTRL, &pwr_ctrl); + if (ret < 0) + return ret; + + if (pwr_ctrl & TCPC_FAST_ROLE_SWAP_EN) { + /* To prevent disconnect when the source is fast role swap is capable. */ + threshold = AUTO_DISCHARGE_DEFAULT_THRESHOLD_MV; + } else if (mode == TYPEC_PWR_MODE_PD) { + if (pps_active) + threshold = (95 * requested_vbus_voltage_mv / 100) - + AUTO_DISCHARGE_PD_HEADROOM_MV; + else + threshold = (95 * requested_vbus_voltage_mv / 100) - + AUTO_DISCHARGE_PPS_HEADROOM_MV; + } else { + /* 3.5V for non-pd sink */ + threshold = AUTO_DISCHARGE_DEFAULT_THRESHOLD_MV; + } + + threshold = threshold / TCPC_VBUS_SINK_DISCONNECT_THRESH_LSB_MV; + + if (threshold > TCPC_VBUS_SINK_DISCONNECT_THRESH_MAX) + return -EINVAL; + +write_thresh: + return tcpci_write16(tcpci, TCPC_VBUS_SINK_DISCONNECT_THRESH, threshold); +} + static int tcpci_enable_frs(struct tcpc_dev *dev, bool enable) { struct tcpci *tcpci = tcpc_to_tcpci(dev); @@ -284,6 +340,14 @@ static int tcpci_enable_frs(struct tcpc_dev *dev, bool enable) return ret; } +static void tcpci_frs_sourcing_vbus(struct tcpc_dev *dev) +{ + struct tcpci *tcpci = tcpc_to_tcpci(dev); + + if (tcpci->data->frs_sourcing_vbus) + tcpci->data->frs_sourcing_vbus(tcpci, tcpci->data); +} + static int tcpci_set_bist_data(struct tcpc_dev *tcpc, bool enable) { struct tcpci *tcpci = tcpc_to_tcpci(tcpc); @@ -339,6 +403,19 @@ static int tcpci_get_vbus(struct tcpc_dev *tcpc) return !!(reg & TCPC_POWER_STATUS_VBUS_PRES); } +static bool tcpci_is_vbus_vsafe0v(struct tcpc_dev *tcpc) +{ + struct tcpci *tcpci = tcpc_to_tcpci(tcpc); + unsigned int reg; + int ret; + + ret = regmap_read(tcpci->regmap, TCPC_EXTENDED_STATUS, ®); + if (ret < 0) + return false; + + return !!(reg & TCPC_EXTENDED_STATUS_VSAFE0V); +} + static int tcpci_set_vbus(struct tcpc_dev *tcpc, bool source, bool sink) { struct tcpci *tcpci = tcpc_to_tcpci(tcpc); @@ -384,9 +461,8 @@ static int tcpci_set_vbus(struct tcpc_dev *tcpc, bool source, bool sink) return 0; } -static int tcpci_pd_transmit(struct tcpc_dev *tcpc, - enum tcpm_transmit_type type, - const struct pd_message *msg) +static int tcpci_pd_transmit(struct tcpc_dev *tcpc, enum tcpm_transmit_type type, + const struct pd_message *msg, unsigned int negotiated_rev) { struct tcpci *tcpci = tcpc_to_tcpci(tcpc); u16 header = msg ? le16_to_cpu(msg->header) : 0; @@ -434,7 +510,9 @@ static int tcpci_pd_transmit(struct tcpc_dev *tcpc, } } - reg = (PD_RETRY_COUNT << TCPC_TRANSMIT_RETRY_SHIFT) | (type << TCPC_TRANSMIT_TYPE_SHIFT); + /* nRetryCount is 3 in PD2.0 spec where 2 in PD3.0 spec */ + reg = ((negotiated_rev > PD_REV20 ? PD_RETRY_COUNT_3_0_OR_HIGHER : PD_RETRY_COUNT_DEFAULT) + << TCPC_TRANSMIT_RETRY_SHIFT) | (type << TCPC_TRANSMIT_TYPE_SHIFT); ret = regmap_write(tcpci->regmap, TCPC_TRANSMIT, reg); if (ret < 0) return ret; @@ -491,12 +569,22 @@ static int tcpci_init(struct tcpc_dev *tcpc) TCPC_ALERT_RX_HARD_RST | TCPC_ALERT_CC_STATUS; if (tcpci->controls_vbus) reg |= TCPC_ALERT_POWER_STATUS; + /* Enable VSAFE0V status interrupt when detecting VSAFE0V is supported */ + if (tcpci->data->vbus_vsafe0v) { + reg |= TCPC_ALERT_EXTENDED_STATUS; + ret = regmap_write(tcpci->regmap, TCPC_EXTENDED_STATUS_MASK, + TCPC_EXTENDED_STATUS_VSAFE0V); + if (ret < 0) + return ret; + } return tcpci_write16(tcpci, TCPC_ALERT_MASK, reg); } irqreturn_t tcpci_irq(struct tcpci *tcpci) { u16 status; + int ret; + unsigned int raw; tcpci_read16(tcpci, TCPC_ALERT, &status); @@ -512,15 +600,12 @@ irqreturn_t tcpci_irq(struct tcpci *tcpci) tcpm_cc_change(tcpci->port); if (status & TCPC_ALERT_POWER_STATUS) { - unsigned int reg; - - regmap_read(tcpci->regmap, TCPC_POWER_STATUS_MASK, ®); - + regmap_read(tcpci->regmap, TCPC_POWER_STATUS_MASK, &raw); /* * If power status mask has been reset, then the TCPC * has reset. */ - if (reg == 0xff) + if (raw == 0xff) tcpm_tcpc_reset(tcpci->port); else tcpm_vbus_change(tcpci->port); @@ -559,6 +644,12 @@ irqreturn_t tcpci_irq(struct tcpci *tcpci) tcpm_pd_receive(tcpci->port, &msg); } + if (status & TCPC_ALERT_EXTENDED_STATUS) { + ret = regmap_read(tcpci->regmap, TCPC_EXTENDED_STATUS, &raw); + if (!ret && (raw & TCPC_EXTENDED_STATUS_VSAFE0V)) + tcpm_vbus_change(tcpci->port); + } + if (status & TCPC_ALERT_RX_HARD_RST) tcpm_pd_hard_reset(tcpci->port); @@ -628,6 +719,18 @@ struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data) tcpci->tcpc.pd_transmit = tcpci_pd_transmit; tcpci->tcpc.set_bist_data = tcpci_set_bist_data; tcpci->tcpc.enable_frs = tcpci_enable_frs; + tcpci->tcpc.frs_sourcing_vbus = tcpci_frs_sourcing_vbus; + + if (tcpci->data->auto_discharge_disconnect) { + tcpci->tcpc.enable_auto_vbus_discharge = tcpci_enable_auto_vbus_discharge; + tcpci->tcpc.set_auto_vbus_discharge_threshold = + tcpci_set_auto_vbus_discharge_threshold; + regmap_update_bits(tcpci->regmap, TCPC_POWER_CTRL, TCPC_POWER_CTRL_BLEED_DISCHARGE, + TCPC_POWER_CTRL_BLEED_DISCHARGE); + } + + if (tcpci->data->vbus_vsafe0v) + tcpci->tcpc.is_vbus_vsafe0v = tcpci_is_vbus_vsafe0v; err = tcpci_parse_config(tcpci); if (err < 0) diff --git a/drivers/usb/typec/tcpm/tcpci.h b/drivers/usb/typec/tcpm/tcpci.h index 5ef07a56d67a..c3c7d07d9b4e 100644 --- a/drivers/usb/typec/tcpm/tcpci.h +++ b/drivers/usb/typec/tcpm/tcpci.h @@ -8,6 +8,8 @@ #ifndef __LINUX_USB_TCPCI_H #define __LINUX_USB_TCPCI_H +#include <linux/usb/typec.h> + #define TCPC_VENDOR_ID 0x0 #define TCPC_PRODUCT_ID 0x2 #define TCPC_BCD_DEV 0x4 @@ -47,6 +49,9 @@ #define TCPC_TCPC_CTRL_ORIENTATION BIT(0) #define TCPC_TCPC_CTRL_BIST_TM BIT(1) +#define TCPC_EXTENDED_STATUS 0x20 +#define TCPC_EXTENDED_STATUS_VSAFE0V BIT(0) + #define TCPC_ROLE_CTRL 0x1a #define TCPC_ROLE_CTRL_DRP BIT(6) #define TCPC_ROLE_CTRL_RP_VAL_SHIFT 4 @@ -67,6 +72,8 @@ #define TCPC_POWER_CTRL 0x1c #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_FAST_ROLE_SWAP_EN BIT(7) #define TCPC_CC_STATUS 0x1d @@ -133,6 +140,8 @@ #define TCPC_VBUS_VOLTAGE 0x70 #define TCPC_VBUS_SINK_DISCONNECT_THRESH 0x72 +#define TCPC_VBUS_SINK_DISCONNECT_THRESH_LSB_MV 25 +#define TCPC_VBUS_SINK_DISCONNECT_THRESH_MAX 0x3ff #define TCPC_VBUS_STOP_DISCHARGE_THRESH 0x74 #define TCPC_VBUS_VOLTAGE_ALARM_HI_CFG 0x76 #define TCPC_VBUS_VOLTAGE_ALARM_LO_CFG 0x78 @@ -140,20 +149,32 @@ /* I2C_WRITE_BYTE_COUNT + 1 when TX_BUF_BYTE_x is only accessible I2C_WRITE_BYTE_COUNT */ #define TCPC_TRANSMIT_BUFFER_MAX_LEN 31 +struct tcpci; + /* - * @TX_BUF_BYTE_x_hidden + * @TX_BUF_BYTE_x_hidden: * optional; Set when TX_BUF_BYTE_x can only be accessed through I2C_WRITE_BYTE_COUNT. + * @frs_sourcing_vbus: + * Optional; Callback to perform chip specific operations when FRS + * is sourcing vbus. + * @auto_discharge_disconnect: + * Optional; Enables TCPC to autonously discharge vbus on disconnect. + * @vbus_vsafe0v: + * optional; Set when TCPC can detect whether vbus is at VSAFE0V. */ -struct tcpci; struct tcpci_data { struct regmap *regmap; unsigned char TX_BUF_BYTE_x_hidden:1; + unsigned char auto_discharge_disconnect:1; + unsigned char vbus_vsafe0v:1; + int (*init)(struct tcpci *tcpci, struct tcpci_data *data); int (*set_vconn)(struct tcpci *tcpci, struct tcpci_data *data, bool enable); int (*start_drp_toggling)(struct tcpci *tcpci, struct tcpci_data *data, enum typec_cc_status cc); int (*set_vbus)(struct tcpci *tcpci, struct tcpci_data *data, bool source, bool sink); + void (*frs_sourcing_vbus)(struct tcpci *tcpci, struct tcpci_data *data); }; struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data); diff --git a/drivers/usb/typec/tcpm/tcpci_maxim.c b/drivers/usb/typec/tcpm/tcpci_maxim.c index 723d7dd38f75..319266329b42 100644 --- a/drivers/usb/typec/tcpm/tcpci_maxim.c +++ b/drivers/usb/typec/tcpm/tcpci_maxim.c @@ -112,11 +112,18 @@ static void max_tcpci_init_regs(struct max_tcpci_chip *chip) return; } + /* Enable VSAFE0V detection */ + ret = max_tcpci_write8(chip, TCPC_EXTENDED_STATUS_MASK, TCPC_EXTENDED_STATUS_VSAFE0V); + if (ret < 0) { + dev_err(chip->dev, "Unable to unmask TCPC_EXTENDED_STATUS_VSAFE0V ret:%d\n", ret); + return; + } + alert_mask = TCPC_ALERT_TX_SUCCESS | TCPC_ALERT_TX_DISCARDED | TCPC_ALERT_TX_FAILED | TCPC_ALERT_RX_HARD_RST | TCPC_ALERT_RX_STATUS | TCPC_ALERT_CC_STATUS | TCPC_ALERT_VBUS_DISCNCT | TCPC_ALERT_RX_BUF_OVF | TCPC_ALERT_POWER_STATUS | /* Enable Extended alert for detecting Fast Role Swap Signal */ - TCPC_ALERT_EXTND; + TCPC_ALERT_EXTND | TCPC_ALERT_EXTENDED_STATUS; ret = max_tcpci_write16(chip, TCPC_ALERT_MASK, alert_mask); if (ret < 0) { @@ -238,23 +245,22 @@ static void process_power_status(struct max_tcpci_chip *chip) if (ret < 0) return; - if (pwr_status == 0xff) { + if (pwr_status == 0xff) max_tcpci_init_regs(chip); - } else if (pwr_status & TCPC_POWER_STATUS_SOURCING_VBUS) { + else if (pwr_status & TCPC_POWER_STATUS_SOURCING_VBUS) tcpm_sourcing_vbus(chip->port); - /* - * Alawys re-enable boost here. - * In normal case, when say an headset is attached, TCPM would - * have instructed to TCPC to enable boost, so the call is a - * no-op. - * But for Fast Role Swap case, Boost turns on autonomously without - * AP intervention, but, needs AP to enable source mode explicitly - * for AP to regain control. - */ - max_tcpci_set_vbus(chip->tcpci, &chip->data, true, false); - } else { + else tcpm_vbus_change(chip->port); - } +} + +static void max_tcpci_frs_sourcing_vbus(struct tcpci *tcpci, struct tcpci_data *tdata) +{ + /* + * For Fast Role Swap case, Boost turns on autonomously without + * AP intervention, but, needs AP to enable source mode explicitly + * for AP to regain control. + */ + max_tcpci_set_vbus(tcpci, tdata, true, false); } static void process_tx(struct max_tcpci_chip *chip, u16 status) @@ -316,6 +322,12 @@ static irqreturn_t _max_tcpci_irq(struct max_tcpci_chip *chip, u16 status) } } + if (status & TCPC_ALERT_EXTENDED_STATUS) { + ret = max_tcpci_read8(chip, TCPC_EXTENDED_STATUS, (u8 *)®_status); + if (ret >= 0 && (reg_status & TCPC_EXTENDED_STATUS_VSAFE0V)) + tcpm_vbus_change(chip->port); + } + if (status & TCPC_ALERT_RX_STATUS) process_rx(chip, status); @@ -344,7 +356,7 @@ static irqreturn_t max_tcpci_irq(int irq, void *dev_id) { struct max_tcpci_chip *chip = dev_id; u16 status; - irqreturn_t irq_return; + irqreturn_t irq_return = IRQ_HANDLED; int ret; if (!chip->port) @@ -441,10 +453,13 @@ static int max_tcpci_probe(struct i2c_client *client, const struct i2c_device_id chip->data.start_drp_toggling = max_tcpci_start_toggling; chip->data.TX_BUF_BYTE_x_hidden = true; chip->data.init = tcpci_init; + chip->data.frs_sourcing_vbus = max_tcpci_frs_sourcing_vbus; + chip->data.auto_discharge_disconnect = true; + chip->data.vbus_vsafe0v = true; max_tcpci_init_regs(chip); chip->tcpci = tcpci_register_port(chip->dev, &chip->data); - if (IS_ERR_OR_NULL(chip->tcpci)) { + if (IS_ERR(chip->tcpci)) { dev_err(&client->dev, "TCPCI port registration failed"); ret = PTR_ERR(chip->tcpci); return PTR_ERR(chip->tcpci); @@ -481,7 +496,7 @@ MODULE_DEVICE_TABLE(i2c, max_tcpci_id); #ifdef CONFIG_OF static const struct of_device_id max_tcpci_of_match[] = { - { .compatible = "maxim,tcpc", }, + { .compatible = "maxim,max33359", }, {}, }; MODULE_DEVICE_TABLE(of, max_tcpci_of_match); diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index a6fae1f86505..22a85b396f69 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -258,7 +258,19 @@ struct tcpm_port { bool attached; bool connected; enum typec_port_type port_type; + + /* + * Set to true when vbus is greater than VSAFE5V min. + * Set to false when vbus falls below vSinkDisconnect max threshold. + */ bool vbus_present; + + /* + * Set to true when vbus is less than VSAFE0V max. + * Set to false when vbus is greater than VSAFE0V max. + */ + bool vbus_vsafe0v; + bool vbus_never_low; bool vbus_source; bool vbus_charge; @@ -363,8 +375,8 @@ struct tcpm_port { /* port belongs to a self powered device */ bool self_powered; - /* FRS */ - enum frs_typec_current frs_current; + /* Sink FRS */ + enum frs_typec_current new_source_frs_current; /* Sink caps have been queried */ bool sink_cap_done; @@ -667,7 +679,7 @@ static int tcpm_pd_transmit(struct tcpm_port *port, tcpm_log(port, "PD TX, type: %#x", type); reinit_completion(&port->tx_complete); - ret = port->tcpc->pd_transmit(port->tcpc, type, msg); + ret = port->tcpc->pd_transmit(port->tcpc, type, msg, port->negotiated_rev); if (ret < 0) return ret; @@ -1706,6 +1718,24 @@ static void tcpm_handle_alert(struct tcpm_port *port, const __le32 *payload, } } +static int tcpm_set_auto_vbus_discharge_threshold(struct tcpm_port *port, + enum typec_pwr_opmode mode, bool pps_active, + u32 requested_vbus_voltage) +{ + int ret; + + if (!port->tcpc->set_auto_vbus_discharge_threshold) + return 0; + + ret = port->tcpc->set_auto_vbus_discharge_threshold(port->tcpc, mode, pps_active, + requested_vbus_voltage); + tcpm_log_force(port, + "set_auto_vbus_discharge_threshold mode:%d pps_active:%c vbus:%u ret:%d", + mode, pps_active ? 'y' : 'n', requested_vbus_voltage, ret); + + return ret; +} + static void tcpm_pd_data_request(struct tcpm_port *port, const struct pd_message *msg) { @@ -1713,7 +1743,7 @@ static void tcpm_pd_data_request(struct tcpm_port *port, unsigned int cnt = pd_header_cnt_le(msg->header); unsigned int rev = pd_header_rev_le(msg->header); unsigned int i; - enum frs_typec_current frs_current; + enum frs_typec_current partner_frs_current; bool frs_enable; int ret; @@ -1786,12 +1816,13 @@ static void tcpm_pd_data_request(struct tcpm_port *port, for (i = 0; i < cnt; i++) port->sink_caps[i] = le32_to_cpu(msg->payload[i]); - frs_current = (port->sink_caps[0] & PDO_FIXED_FRS_CURR_MASK) >> + partner_frs_current = (port->sink_caps[0] & PDO_FIXED_FRS_CURR_MASK) >> PDO_FIXED_FRS_CURR_SHIFT; - frs_enable = frs_current && (frs_current <= port->frs_current); + frs_enable = partner_frs_current && (partner_frs_current <= + port->new_source_frs_current); tcpm_log(port, "Port partner FRS capable partner_frs_current:%u port_frs_current:%u enable:%c", - frs_current, port->frs_current, frs_enable ? 'y' : 'n'); + partner_frs_current, port->new_source_frs_current, frs_enable ? 'y' : 'n'); if (frs_enable) { ret = port->tcpc->enable_frs(port->tcpc, true); tcpm_log(port, "Enable FRS %s, ret:%d\n", ret ? "fail" : "success", ret); @@ -1875,6 +1906,10 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, port->current_limit, port->supply_voltage); port->explicit_contract = true; + tcpm_set_auto_vbus_discharge_threshold(port, + TYPEC_PWR_MODE_PD, + port->pps_data.active, + port->supply_voltage); tcpm_set_state(port, SNK_READY, 0); } else { /* @@ -2193,6 +2228,7 @@ static int tcpm_pd_send_control(struct tcpm_port *port, static bool tcpm_send_queued_message(struct tcpm_port *port) { enum pd_msg_request queued_message; + int ret; do { queued_message = port->queued_message; @@ -2212,7 +2248,16 @@ static bool tcpm_send_queued_message(struct tcpm_port *port) tcpm_pd_send_sink_caps(port); break; case PD_MSG_DATA_SOURCE_CAP: - tcpm_pd_send_source_caps(port); + ret = tcpm_pd_send_source_caps(port); + if (ret < 0) { + tcpm_log(port, + "Unable to send src caps, ret=%d", + ret); + tcpm_set_state(port, SOFT_RESET_SEND, 0); + } else if (port->pwr_role == TYPEC_SOURCE) { + tcpm_set_state(port, HARD_RESET_SEND, + PD_T_SENDER_RESPONSE); + } break; default: break; @@ -2789,8 +2834,12 @@ static int tcpm_src_attach(struct tcpm_port *port) if (ret < 0) return ret; - ret = tcpm_set_roles(port, true, TYPEC_SOURCE, - tcpm_data_role_for_source(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); + } + + ret = tcpm_set_roles(port, true, TYPEC_SOURCE, tcpm_data_role_for_source(port)); if (ret < 0) return ret; @@ -2857,6 +2906,12 @@ static void tcpm_unregister_altmodes(struct tcpm_port *port) static void tcpm_reset_port(struct tcpm_port *port) { + int ret; + + 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); + } tcpm_unregister_altmodes(port); tcpm_typec_disconnect(port); port->attached = false; @@ -2921,8 +2976,13 @@ static int tcpm_snk_attach(struct tcpm_port *port) if (ret < 0) return ret; - ret = tcpm_set_roles(port, true, TYPEC_SINK, - tcpm_data_role_for_sink(port)); + if (port->tcpc->enable_auto_vbus_discharge) { + 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); + } + + ret = tcpm_set_roles(port, true, TYPEC_SINK, tcpm_data_role_for_sink(port)); if (ret < 0) return ret; @@ -2997,10 +3057,9 @@ 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) { + port->pd_capable) tcpm_send_vdm(port, USB_SID_PD, CMD_DISCOVER_IDENT, NULL, 0); - port->send_discover = false; - } + port->send_discover = false; } static void tcpm_swap_complete(struct tcpm_port *port, int result) @@ -3056,7 +3115,7 @@ static void run_state_machine(struct tcpm_port *port) else if (tcpm_port_is_audio(port)) tcpm_set_state(port, AUDIO_ACC_ATTACHED, PD_T_CC_DEBOUNCE); - else if (tcpm_port_is_source(port)) + else if (tcpm_port_is_source(port) && port->vbus_vsafe0v) tcpm_set_state(port, tcpm_try_snk(port) ? SNK_TRY : SRC_ATTACHED, @@ -3086,15 +3145,13 @@ static void run_state_machine(struct tcpm_port *port) break; case SNK_TRY_WAIT_DEBOUNCE: tcpm_set_state(port, SNK_TRY_WAIT_DEBOUNCE_CHECK_VBUS, - PD_T_PD_DEBOUNCE); + PD_T_TRY_CC_DEBOUNCE); break; case SNK_TRY_WAIT_DEBOUNCE_CHECK_VBUS: - if (port->vbus_present && tcpm_port_is_sink(port)) { + if (port->vbus_present && tcpm_port_is_sink(port)) tcpm_set_state(port, SNK_ATTACHED, 0); - } else { - tcpm_set_state(port, SRC_TRYWAIT, 0); + else port->max_wait = 0; - } break; case SRC_TRYWAIT: tcpm_set_cc(port, tcpm_rp_cc(port)); @@ -3506,6 +3563,8 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_state(port, SRC_UNATTACHED, PD_T_PS_SOURCE_ON); break; case SNK_HARD_RESET_SINK_OFF: + /* Do not discharge/disconnect during hard reseet */ + tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_USB, false, 0); memset(&port->pps_data, 0, sizeof(port->pps_data)); tcpm_set_vconn(port, false); if (port->pd_capable) @@ -3548,6 +3607,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_charge(port, true); } tcpm_set_attached_state(port, true); + tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_USB, false, VSAFE5V); tcpm_set_state(port, SNK_STARTUP, 0); break; @@ -3649,6 +3709,10 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_state(port, PR_SWAP_SNK_SRC_SINK_OFF, 0); break; case PR_SWAP_SRC_SNK_TRANSITION_OFF: + /* + * Prevent vbus discharge circuit from turning on during PR_SWAP + * as this is not a disconnect. + */ tcpm_set_vbus(port, false); port->explicit_contract = false; /* allow time for Vbus discharge, must be < tSrcSwapStdby */ @@ -3674,12 +3738,20 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_state(port, ERROR_RECOVERY, 0); break; } - tcpm_set_state_cond(port, SNK_UNATTACHED, PD_T_PS_SOURCE_ON); + tcpm_set_state(port, ERROR_RECOVERY, PD_T_PS_SOURCE_ON_PRS); break; case PR_SWAP_SRC_SNK_SINK_ON: + /* Set the vbus disconnect threshold for implicit contract */ + tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_USB, false, VSAFE5V); tcpm_set_state(port, SNK_STARTUP, 0); break; case PR_SWAP_SNK_SRC_SINK_OFF: + /* + * Prevent vbus discharge circuit from turning on during PR_SWAP + * as this is not a disconnect. + */ + tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_USB, + port->pps_data.active, 0); tcpm_set_charge(port, false); tcpm_set_state(port, hard_reset_state(port), PD_T_PS_SOURCE_OFF); @@ -4000,6 +4072,12 @@ static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1, if (!tcpm_port_is_sink(port)) tcpm_set_state(port, SNK_TRYWAIT_DEBOUNCE, 0); break; + case SNK_TRY_WAIT_DEBOUNCE_CHECK_VBUS: + if (!tcpm_port_is_sink(port)) + tcpm_set_state(port, SRC_TRYWAIT, PD_T_TRY_CC_DEBOUNCE); + else + tcpm_set_state(port, SNK_TRY_WAIT_DEBOUNCE_CHECK_VBUS, 0); + break; case SNK_TRYWAIT: /* Do nothing, waiting for tCCDebounce */ break; @@ -4040,6 +4118,12 @@ static void _tcpm_pd_vbus_on(struct tcpm_port *port) { tcpm_log_force(port, "VBUS on"); port->vbus_present = true; + /* + * When vbus_present is true i.e. Voltage at VBUS is greater than VSAFE5V implicitly + * states that vbus is not at VSAFE0V, hence clear the vbus_vsafe0v flag here. + */ + port->vbus_vsafe0v = false; + switch (port->state) { case SNK_TRANSITION_SINK_VBUS: port->explicit_contract = true; @@ -4086,11 +4170,24 @@ static void _tcpm_pd_vbus_on(struct tcpm_port *port) case SNK_TRYWAIT_DEBOUNCE: /* Do nothing, waiting for Rp */ break; + case SNK_TRY_WAIT_DEBOUNCE_CHECK_VBUS: + if (port->vbus_present && tcpm_port_is_sink(port)) + tcpm_set_state(port, SNK_ATTACHED, 0); + break; case SRC_TRY_WAIT: case SRC_TRY_DEBOUNCE: /* Do nothing, waiting for sink detection */ break; + case FR_SWAP_SEND: + case FR_SWAP_SEND_TIMEOUT: + case FR_SWAP_SNK_SRC_TRANSITION_TO_OFF: + case FR_SWAP_SNK_SRC_SOURCE_VBUS_APPLIED: + if (port->tcpc->frs_sourcing_vbus) + port->tcpc->frs_sourcing_vbus(port->tcpc); + break; case FR_SWAP_SNK_SRC_NEW_SINK_READY: + if (port->tcpc->frs_sourcing_vbus) + port->tcpc->frs_sourcing_vbus(port->tcpc); tcpm_set_state(port, FR_SWAP_SNK_SRC_SOURCE_VBUS_APPLIED, 0); break; @@ -4116,16 +4213,8 @@ static void _tcpm_pd_vbus_off(struct tcpm_port *port) case SNK_HARD_RESET_SINK_OFF: tcpm_set_state(port, SNK_HARD_RESET_WAIT_VBUS, 0); break; - case SRC_HARD_RESET_VBUS_OFF: - /* - * After establishing the vSafe0V voltage condition on VBUS, the Source Shall wait - * tSrcRecover before re-applying VCONN and restoring VBUS to vSafe5V. - */ - tcpm_set_state(port, SRC_HARD_RESET_VBUS_ON, PD_T_SRC_RECOVER); - break; case HARD_RESET_SEND: break; - case SNK_TRY: /* Do nothing, waiting for timeout */ break; @@ -4156,6 +4245,14 @@ static void _tcpm_pd_vbus_off(struct tcpm_port *port) /* Do nothing, expected */ break; + case PR_SWAP_SNK_SRC_SOURCE_ON: + /* + * Do nothing when vbus off notification is received. + * TCPM can wait for PD_T_NEWSRC in PR_SWAP_SNK_SRC_SOURCE_ON + * for the vbus source to ramp up. + */ + break; + case PORT_RESET_WAIT_OFF: tcpm_set_state(port, tcpm_default_state(port), 0); break; @@ -4188,6 +4285,28 @@ static void _tcpm_pd_vbus_off(struct tcpm_port *port) } } +static void _tcpm_pd_vbus_vsafe0v(struct tcpm_port *port) +{ + tcpm_log_force(port, "VBUS VSAFE0V"); + port->vbus_vsafe0v = true; + switch (port->state) { + case SRC_HARD_RESET_VBUS_OFF: + /* + * After establishing the vSafe0V voltage condition on VBUS, the Source Shall wait + * tSrcRecover before re-applying VCONN and restoring VBUS to vSafe5V. + */ + tcpm_set_state(port, SRC_HARD_RESET_VBUS_ON, PD_T_SRC_RECOVER); + break; + case SRC_ATTACH_WAIT: + if (tcpm_port_is_source(port)) + tcpm_set_state(port, tcpm_try_snk(port) ? SNK_TRY : SRC_ATTACHED, + PD_T_CC_DEBOUNCE); + break; + default: + break; + } +} + static void _tcpm_pd_hard_reset(struct tcpm_port *port) { tcpm_log_force(port, "Received hard reset"); @@ -4223,10 +4342,19 @@ static void tcpm_pd_event_handler(struct kthread_work *work) bool vbus; vbus = port->tcpc->get_vbus(port->tcpc); - if (vbus) + if (vbus) { _tcpm_pd_vbus_on(port); - else + } else { _tcpm_pd_vbus_off(port); + /* + * When TCPC does not support detecting vsafe0v voltage level, + * treat vbus absent as vsafe0v. Else invoke is_vbus_vsafe0v + * to see if vbus has discharge to VSAFE0V. + */ + if (!port->tcpc->is_vbus_vsafe0v || + port->tcpc->is_vbus_vsafe0v(port->tcpc)) + _tcpm_pd_vbus_vsafe0v(port); + } } if (events & TCPM_CC_EVENT) { enum typec_cc_status cc1, cc2; @@ -4676,6 +4804,24 @@ static void tcpm_init(struct tcpm_port *port) if (port->vbus_present) port->vbus_never_low = true; + /* + * 1. When vbus_present is true, voltage on VBUS is already at VSAFE5V. + * So implicitly vbus_vsafe0v = false. + * + * 2. When vbus_present is false and TCPC does NOT support querying + * vsafe0v status, then, it's best to assume vbus is at VSAFE0V i.e. + * vbus_vsafe0v is true. + * + * 3. When vbus_present is false and TCPC does support querying vsafe0v, + * then, query tcpc for vsafe0v status. + */ + if (port->vbus_present) + port->vbus_vsafe0v = false; + else if (!port->tcpc->is_vbus_vsafe0v) + port->vbus_vsafe0v = true; + else + port->vbus_vsafe0v = port->tcpc->is_vbus_vsafe0v(port->tcpc); + tcpm_set_state(port, tcpm_default_state(port), 0); if (port->tcpc->get_cc(port->tcpc, &cc1, &cc2) == 0) @@ -4808,9 +4954,10 @@ sink: /* FRS can only be supported byb DRP ports */ if (port->port_type == TYPEC_PORT_DRP) { - ret = fwnode_property_read_u32(fwnode, "frs-typec-current", &frs_current); + ret = fwnode_property_read_u32(fwnode, "new-source-frs-typec-current", + &frs_current); if (ret >= 0 && frs_current <= FRS_5V_3A) - port->frs_current = frs_current; + port->new_source_frs_current = frs_current; } return 0; @@ -5024,14 +5171,14 @@ static int devm_tcpm_psy_register(struct tcpm_port *port) snprintf(psy_name, psy_name_len, "%s%s", tcpm_psy_name_prefix, port_dev_name); port->psy_desc.name = psy_name; - port->psy_desc.type = POWER_SUPPLY_TYPE_USB, + port->psy_desc.type = POWER_SUPPLY_TYPE_USB; port->psy_desc.usb_types = tcpm_psy_usb_types; port->psy_desc.num_usb_types = ARRAY_SIZE(tcpm_psy_usb_types); - port->psy_desc.properties = tcpm_psy_props, - port->psy_desc.num_properties = ARRAY_SIZE(tcpm_psy_props), - port->psy_desc.get_property = tcpm_psy_get_prop, - port->psy_desc.set_property = tcpm_psy_set_prop, - port->psy_desc.property_is_writeable = tcpm_psy_prop_writeable, + port->psy_desc.properties = tcpm_psy_props; + port->psy_desc.num_properties = ARRAY_SIZE(tcpm_psy_props); + port->psy_desc.get_property = tcpm_psy_get_prop; + port->psy_desc.set_property = tcpm_psy_set_prop; + port->psy_desc.property_is_writeable = tcpm_psy_prop_writeable; port->usb_type = POWER_SUPPLY_USB_TYPE_C; diff --git a/drivers/usb/typec/tcpm/wcove.c b/drivers/usb/typec/tcpm/wcove.c index 9b745f432c91..79ae63950050 100644 --- a/drivers/usb/typec/tcpm/wcove.c +++ b/drivers/usb/typec/tcpm/wcove.c @@ -356,7 +356,8 @@ static int wcove_set_pd_rx(struct tcpc_dev *tcpc, bool on) static int wcove_pd_transmit(struct tcpc_dev *tcpc, enum tcpm_transmit_type type, - const struct pd_message *msg) + const struct pd_message *msg, + unsigned int negotiated_rev) { struct wcove_typec *wcove = tcpc_to_wcove(tcpc); unsigned int info = 0; diff --git a/drivers/usb/typec/tps6598x.c b/drivers/usb/typec/tps6598x.c index 3db33bb622c3..6e6ef6317523 100644 --- a/drivers/usb/typec/tps6598x.c +++ b/drivers/usb/typec/tps6598x.c @@ -9,6 +9,7 @@ #include <linux/i2c.h> #include <linux/acpi.h> #include <linux/module.h> +#include <linux/power_supply.h> #include <linux/regmap.h> #include <linux/interrupt.h> #include <linux/usb/typec.h> @@ -55,6 +56,7 @@ enum { }; /* 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) @@ -96,8 +98,25 @@ struct tps6598x { struct typec_partner *partner; struct usb_pd_identity partner_identity; struct usb_role_switch *role_sw; + struct typec_capability typec_cap; + + struct power_supply *psy; + struct power_supply_desc psy_desc; + enum power_supply_usb_type usb_type; +}; + +static enum power_supply_property tps6598x_psy_props[] = { + POWER_SUPPLY_PROP_USB_TYPE, + POWER_SUPPLY_PROP_ONLINE, }; +static enum power_supply_usb_type tps6598x_psy_usb_types[] = { + POWER_SUPPLY_USB_TYPE_C, + POWER_SUPPLY_USB_TYPE_PD, +}; + +static const char *tps6598x_psy_name_prefix = "tps6598x-source-psy-"; + /* * Max data bytes for Data1, Data2, and other registers. See ch 1.3.2: * https://www.ti.com/lit/ug/slvuan1a/slvuan1a.pdf @@ -248,6 +267,8 @@ static int tps6598x_connect(struct tps6598x *tps, u32 status) if (desc.identity) typec_partner_set_identity(tps->partner); + power_supply_changed(tps->psy); + return 0; } @@ -260,6 +281,7 @@ static void tps6598x_disconnect(struct tps6598x *tps, u32 status) 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); + power_supply_changed(tps->psy); } static int tps6598x_exec_cmd(struct tps6598x *tps, const char *cmd, @@ -467,6 +489,83 @@ static const struct regmap_config tps6598x_regmap_config = { .max_register = 0x7F, }; +static int tps6598x_psy_get_online(struct tps6598x *tps, + union power_supply_propval *val) +{ + int ret; + u16 pwr_status; + + ret = tps6598x_read16(tps, TPS_REG_POWER_STATUS, &pwr_status); + if (ret < 0) + return ret; + + if ((pwr_status & TPS_POWER_STATUS_CONNECTION) && + (pwr_status & TPS_POWER_STATUS_SOURCESINK)) { + val->intval = 1; + } else { + val->intval = 0; + } + return 0; +} + +static int tps6598x_psy_get_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct tps6598x *tps = power_supply_get_drvdata(psy); + u16 pwr_status; + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_USB_TYPE: + ret = tps6598x_read16(tps, TPS_REG_POWER_STATUS, &pwr_status); + if (ret < 0) + return ret; + if (TPS_POWER_STATUS_PWROPMODE(pwr_status) == TYPEC_PWR_MODE_PD) + val->intval = POWER_SUPPLY_USB_TYPE_PD; + else + val->intval = POWER_SUPPLY_USB_TYPE_C; + break; + case POWER_SUPPLY_PROP_ONLINE: + ret = tps6598x_psy_get_online(tps, val); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int devm_tps6598_psy_register(struct tps6598x *tps) +{ + struct power_supply_config psy_cfg = {}; + const char *port_dev_name = dev_name(tps->dev); + char *psy_name; + + psy_cfg.drv_data = tps; + psy_cfg.fwnode = dev_fwnode(tps->dev); + + psy_name = devm_kasprintf(tps->dev, GFP_KERNEL, "%s%s", tps6598x_psy_name_prefix, + port_dev_name); + if (!psy_name) + return -ENOMEM; + + tps->psy_desc.name = psy_name; + tps->psy_desc.type = POWER_SUPPLY_TYPE_USB; + tps->psy_desc.usb_types = tps6598x_psy_usb_types; + tps->psy_desc.num_usb_types = ARRAY_SIZE(tps6598x_psy_usb_types); + tps->psy_desc.properties = tps6598x_psy_props; + tps->psy_desc.num_properties = ARRAY_SIZE(tps6598x_psy_props); + tps->psy_desc.get_property = tps6598x_psy_get_prop; + + tps->usb_type = POWER_SUPPLY_USB_TYPE_C; + + tps->psy = devm_power_supply_register(tps->dev, &tps->psy_desc, + &psy_cfg); + return PTR_ERR_OR_ZERO(tps->psy); +} + static int tps6598x_probe(struct i2c_client *client) { struct typec_capability typec_cap = { }; @@ -560,6 +659,10 @@ static int tps6598x_probe(struct i2c_client *client) goto err_role_put; } + ret = devm_tps6598_psy_register(tps); + if (ret) + return ret; + tps->port = typec_register_port(&client->dev, &typec_cap); if (IS_ERR(tps->port)) { ret = PTR_ERR(tps->port); diff --git a/drivers/usb/typec/ucsi/psy.c b/drivers/usb/typec/ucsi/psy.c index 571a51e16234..56bf56517f75 100644 --- a/drivers/usb/typec/ucsi/psy.c +++ b/drivers/usb/typec/ucsi/psy.c @@ -220,11 +220,11 @@ int ucsi_register_port_psy(struct ucsi_connector *con) return -ENOMEM; con->psy_desc.name = psy_name; - con->psy_desc.type = POWER_SUPPLY_TYPE_USB, + con->psy_desc.type = POWER_SUPPLY_TYPE_USB; con->psy_desc.usb_types = ucsi_psy_usb_types; con->psy_desc.num_usb_types = ARRAY_SIZE(ucsi_psy_usb_types); - con->psy_desc.properties = ucsi_psy_props, - con->psy_desc.num_properties = ARRAY_SIZE(ucsi_psy_props), + con->psy_desc.properties = ucsi_psy_props; + con->psy_desc.num_properties = ARRAY_SIZE(ucsi_psy_props); con->psy_desc.get_property = ucsi_psy_get_prop; con->psy = power_supply_register(dev, &con->psy_desc, &psy_cfg); diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 51a570d40a42..f02958927cbd 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -53,7 +53,7 @@ static int ucsi_acknowledge_connector_change(struct ucsi *ucsi) ctrl = UCSI_ACK_CC_CI; ctrl |= UCSI_ACK_CONNECTOR_CHANGE; - return ucsi->ops->async_write(ucsi, UCSI_CONTROL, &ctrl, sizeof(ctrl)); + return ucsi->ops->sync_write(ucsi, UCSI_CONTROL, &ctrl, sizeof(ctrl)); } static int ucsi_exec_command(struct ucsi *ucsi, u64 command); @@ -625,21 +625,113 @@ static void ucsi_handle_connector_change(struct work_struct *work) struct ucsi_connector *con = container_of(work, struct ucsi_connector, work); struct ucsi *ucsi = con->ucsi; + struct ucsi_connector_status pre_ack_status; + struct ucsi_connector_status post_ack_status; enum typec_role role; + u16 inferred_changes; + u16 changed_flags; u64 command; int ret; mutex_lock(&con->lock); + /* + * Some/many PPMs have an issue where all fields in the change bitfield + * are cleared when an ACK is send. This will causes any change + * between GET_CONNECTOR_STATUS and ACK to be lost. + * + * We work around this by re-fetching the connector status afterwards. + * We then infer any changes that we see have happened but that may not + * be represented in the change bitfield. + * + * Also, even though we don't need to know the currently supported alt + * modes, we run the GET_CAM_SUPPORTED command to ensure the PPM does + * not get stuck in case it assumes we do. + * Always do this, rather than relying on UCSI_CONSTAT_CAM_CHANGE to be + * set in the change bitfield. + * + * We end up with the following actions: + * 1. UCSI_GET_CONNECTOR_STATUS, store result, update unprocessed_changes + * 2. UCSI_GET_CAM_SUPPORTED, discard result + * 3. ACK connector change + * 4. UCSI_GET_CONNECTOR_STATUS, store result + * 5. Infere lost changes by comparing UCSI_GET_CONNECTOR_STATUS results + * 6. If PPM reported a new change, then restart in order to ACK + * 7. Process everything as usual. + * + * We may end up seeing a change twice, but we can only miss extremely + * short transitional changes. + */ + + /* 1. First UCSI_GET_CONNECTOR_STATUS */ + command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num); + ret = ucsi_send_command(ucsi, command, &pre_ack_status, + sizeof(pre_ack_status)); + if (ret < 0) { + dev_err(ucsi->dev, "%s: GET_CONNECTOR_STATUS failed (%d)\n", + __func__, ret); + goto out_unlock; + } + con->unprocessed_changes |= pre_ack_status.change; + + /* 2. Run UCSI_GET_CAM_SUPPORTED and discard the result. */ + command = UCSI_GET_CAM_SUPPORTED; + command |= UCSI_CONNECTOR_NUMBER(con->num); + ucsi_send_command(con->ucsi, command, NULL, 0); + + /* 3. ACK connector change */ + clear_bit(EVENT_PENDING, &ucsi->flags); + ret = ucsi_acknowledge_connector_change(ucsi); + if (ret) { + dev_err(ucsi->dev, "%s: ACK failed (%d)", __func__, ret); + goto out_unlock; + } + + /* 4. Second UCSI_GET_CONNECTOR_STATUS */ command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num); - ret = ucsi_send_command(ucsi, command, &con->status, - sizeof(con->status)); + ret = ucsi_send_command(ucsi, command, &post_ack_status, + sizeof(post_ack_status)); if (ret < 0) { dev_err(ucsi->dev, "%s: GET_CONNECTOR_STATUS failed (%d)\n", __func__, ret); goto out_unlock; } + /* 5. Inferre any missing changes */ + changed_flags = pre_ack_status.flags ^ post_ack_status.flags; + inferred_changes = 0; + if (UCSI_CONSTAT_PWR_OPMODE(changed_flags) != 0) + inferred_changes |= UCSI_CONSTAT_POWER_OPMODE_CHANGE; + + if (changed_flags & UCSI_CONSTAT_CONNECTED) + inferred_changes |= UCSI_CONSTAT_CONNECT_CHANGE; + + if (changed_flags & UCSI_CONSTAT_PWR_DIR) + inferred_changes |= UCSI_CONSTAT_POWER_DIR_CHANGE; + + if (UCSI_CONSTAT_PARTNER_FLAGS(changed_flags) != 0) + inferred_changes |= UCSI_CONSTAT_PARTNER_CHANGE; + + if (UCSI_CONSTAT_PARTNER_TYPE(changed_flags) != 0) + inferred_changes |= UCSI_CONSTAT_PARTNER_CHANGE; + + /* Mask out anything that was correctly notified in the later call. */ + inferred_changes &= ~post_ack_status.change; + if (inferred_changes) + dev_dbg(ucsi->dev, "%s: Inferred changes that would have been lost: 0x%04x\n", + __func__, inferred_changes); + + con->unprocessed_changes |= inferred_changes; + + /* 6. If PPM reported a new change, then restart in order to ACK */ + if (post_ack_status.change) + goto out_unlock; + + /* 7. Continue as if nothing happened */ + con->status = post_ack_status; + con->status.change = con->unprocessed_changes; + con->unprocessed_changes = 0; + role = !!(con->status.flags & UCSI_CONSTAT_PWR_DIR); if (con->status.change & UCSI_CONSTAT_POWER_OPMODE_CHANGE || @@ -680,28 +772,19 @@ static void ucsi_handle_connector_change(struct work_struct *work) ucsi_port_psy_changed(con); } - if (con->status.change & UCSI_CONSTAT_CAM_CHANGE) { - /* - * We don't need to know the currently supported alt modes here. - * Running GET_CAM_SUPPORTED command just to make sure the PPM - * does not get stuck in case it assumes we do so. - */ - command = UCSI_GET_CAM_SUPPORTED; - command |= UCSI_CONNECTOR_NUMBER(con->num); - ucsi_send_command(con->ucsi, command, NULL, 0); - } - if (con->status.change & UCSI_CONSTAT_PARTNER_CHANGE) ucsi_partner_change(con); - ret = ucsi_acknowledge_connector_change(ucsi); - if (ret) - dev_err(ucsi->dev, "%s: ACK failed (%d)", __func__, ret); - trace_ucsi_connector_change(con->num, &con->status); out_unlock: - clear_bit(EVENT_PENDING, &ucsi->flags); + if (test_and_clear_bit(EVENT_PENDING, &ucsi->flags)) { + schedule_work(&con->work); + mutex_unlock(&con->lock); + return; + } + + clear_bit(EVENT_PROCESSING, &ucsi->flags); mutex_unlock(&con->lock); } @@ -719,7 +802,9 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num) return; } - if (!test_and_set_bit(EVENT_PENDING, &ucsi->flags)) + set_bit(EVENT_PENDING, &ucsi->flags); + + if (!test_and_set_bit(EVENT_PROCESSING, &ucsi->flags)) schedule_work(&con->work); } EXPORT_SYMBOL_GPL(ucsi_connector_change); diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index b7a92f246050..dd9ba60ab4a3 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -296,6 +296,7 @@ struct ucsi { #define EVENT_PENDING 0 #define COMMAND_PENDING 1 #define ACK_PENDING 2 +#define EVENT_PROCESSING 3 }; #define UCSI_MAX_SVID 5 @@ -322,6 +323,7 @@ struct ucsi_connector { struct typec_capability typec_cap; + u16 unprocessed_changes; struct ucsi_connector_status status; struct ucsi_connector_capability cap; struct power_supply *psy; diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c index fbfe8f5933af..04976435ad73 100644 --- a/drivers/usb/typec/ucsi/ucsi_acpi.c +++ b/drivers/usb/typec/ucsi/ucsi_acpi.c @@ -103,11 +103,12 @@ static void ucsi_acpi_notify(acpi_handle handle, u32 event, void *data) if (ret) return; + if (UCSI_CCI_CONNECTOR(cci)) + ucsi_connector_change(ua->ucsi, UCSI_CCI_CONNECTOR(cci)); + if (test_bit(COMMAND_PENDING, &ua->flags) && cci & (UCSI_CCI_ACK_COMPLETE | UCSI_CCI_COMMAND_COMPLETE)) complete(&ua->complete); - else if (UCSI_CCI_CONNECTOR(cci)) - ucsi_connector_change(ua->ucsi, UCSI_CCI_CONNECTOR(cci)); } static int ucsi_acpi_probe(struct platform_device *pdev) diff --git a/drivers/usb/usbip/usbip_common.c b/drivers/usb/usbip/usbip_common.c index 4ce6c6a45eb1..2ab99244bc31 100644 --- a/drivers/usb/usbip/usbip_common.c +++ b/drivers/usb/usbip/usbip_common.c @@ -324,11 +324,6 @@ int usbip_recv(struct socket *sock, void *buf, int size) } while (msg_data_left(&msg)); if (usbip_dbg_flag_xmit) { - if (!in_interrupt()) - pr_debug("%-10s:", current->comm); - else - pr_debug("interrupt :"); - pr_debug("receiving....\n"); usbip_dump_buffer(buf, size); pr_debug("received, osize %d ret %d size %zd total %d\n", diff --git a/include/dt-bindings/usb/pd.h b/include/dt-bindings/usb/pd.h index 985f2bbd4d24..0352893697f0 100644 --- a/include/dt-bindings/usb/pd.h +++ b/include/dt-bindings/usb/pd.h @@ -85,4 +85,12 @@ PDO_PPS_APDO_MIN_VOLT(min_mv) | PDO_PPS_APDO_MAX_VOLT(max_mv) | \ PDO_PPS_APDO_MAX_CURR(max_ma)) + /* + * Based on "Table 6-14 Fixed Supply PDO - Sink" of "USB Power Delivery Specification Revision 3.0, + * Version 1.2" + * Initial current capability of the new source when vSafe5V is applied. + */ +#define FRS_DEFAULT_POWER 1 +#define FRS_5V_1P5A 2 +#define FRS_5V_3A 3 #endif /* __DT_POWER_DELIVERY_H */ diff --git a/include/linux/platform_data/usb-ehci-mxc.h b/include/linux/platform_data/usb-ehci-mxc.h deleted file mode 100644 index ad9794d09bc8..000000000000 --- a/include/linux/platform_data/usb-ehci-mxc.h +++ /dev/null @@ -1,14 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __INCLUDE_ASM_ARCH_MXC_EHCI_H -#define __INCLUDE_ASM_ARCH_MXC_EHCI_H - -struct mxc_usbh_platform_data { - int (*init)(struct platform_device *pdev); - int (*exit)(struct platform_device *pdev); - - unsigned int portsc; - struct usb_phy *otg; -}; - -#endif /* __INCLUDE_ASM_ARCH_MXC_EHCI_H */ - diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index 5db2b11ab085..034dccf93955 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -179,6 +179,8 @@ void tb_unregister_property_dir(const char *key, struct tb_property_dir *dir); * @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 @@ -223,6 +225,8 @@ struct tb_xdomain { 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; @@ -243,6 +247,8 @@ struct tb_xdomain { u8 depth; }; +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); @@ -344,6 +350,9 @@ void tb_unregister_protocol_handler(struct tb_protocol_handler *handler); * @prtcvers: Protocol version from the properties directory * @prtcrevs: Protocol software revision from the properties directory * @prtcstns: Protocol settings mask from the properties directory + * @debugfs_dir: Pointer to the service debugfs directory. Always created + * when debugfs is enabled. Can be used by service drivers to + * add their own entries under the service. * * Each domain exposes set of services it supports as collection of * properties. For each service there will be one corresponding @@ -357,6 +366,7 @@ struct tb_service { u32 prtcvers; u32 prtcrevs; u32 prtcstns; + struct dentry *debugfs_dir; }; static inline struct tb_service *tb_service_get(struct tb_service *svc) @@ -471,6 +481,8 @@ struct tb_nhi { * @irq: MSI-X irq number if the ring uses MSI-X. %0 otherwise. * @vector: MSI-X vector number the ring uses (only set if @irq is > 0) * @flags: Ring specific flags + * @e2e_tx_hop: Transmit HopID when E2E is enabled. Only applicable to + * RX ring. For TX ring this should be set to %0. * @sof_mask: Bit mask used to detect start of frame PDF * @eof_mask: Bit mask used to detect end of frame PDF * @start_poll: Called when ring interrupt is triggered to start @@ -494,6 +506,7 @@ struct tb_ring { int irq; u8 vector; unsigned int flags; + int e2e_tx_hop; u16 sof_mask; u16 eof_mask; void (*start_poll)(void *data); @@ -504,6 +517,8 @@ struct tb_ring { #define RING_FLAG_NO_SUSPEND BIT(0) /* Configure the ring to be in frame mode */ #define RING_FLAG_FRAME BIT(1) +/* Enable end-to-end flow control */ +#define RING_FLAG_E2E BIT(2) struct ring_frame; typedef void (*ring_cb)(struct tb_ring *, struct ring_frame *, bool canceled); @@ -552,7 +567,8 @@ struct ring_frame { struct tb_ring *tb_ring_alloc_tx(struct tb_nhi *nhi, int hop, int size, unsigned int flags); struct tb_ring *tb_ring_alloc_rx(struct tb_nhi *nhi, int hop, int size, - unsigned int flags, u16 sof_mask, u16 eof_mask, + unsigned int flags, int e2e_tx_hop, + u16 sof_mask, u16 eof_mask, void (*start_poll)(void *), void *poll_data); void tb_ring_start(struct tb_ring *ring); void tb_ring_stop(struct tb_ring *ring); diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 3dbb42c637c1..96281cd50ff6 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -734,10 +734,6 @@ static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb, /* random stuff */ -#define RUN_CONTEXT (in_irq() ? "in_irq" \ - : (in_interrupt() ? "in_interrupt" : "can sleep")) - - /* This rwsem is for use only by the hub driver and ehci-hcd. * Nobody else should touch it. */ diff --git a/include/linux/usb/pd.h b/include/linux/usb/pd.h index 3a805e2ecbc9..bb9a782e1411 100644 --- a/include/linux/usb/pd.h +++ b/include/linux/usb/pd.h @@ -466,6 +466,7 @@ static inline unsigned int rdo_max_power(u32 rdo) #define PD_T_DRP_SRC 30 #define PD_T_PS_SOURCE_OFF 920 #define PD_T_PS_SOURCE_ON 480 +#define PD_T_PS_SOURCE_ON_PRS 450 /* 390 - 480ms */ #define PD_T_PS_HARD_RESET 30 #define PD_T_SRC_RECOVER 760 #define PD_T_SRC_RECOVER_MAX 1000 @@ -484,6 +485,7 @@ static inline unsigned int rdo_max_power(u32 rdo) #define PD_T_CC_DEBOUNCE 200 /* 100 - 200 ms */ #define PD_T_PD_DEBOUNCE 20 /* 10 - 20 ms */ +#define PD_T_TRY_CC_DEBOUNCE 15 /* 10 - 20 ms */ #define PD_N_CAPS_COUNT (PD_T_NO_RESPONSE / PD_T_SEND_SOURCE_CAP) #define PD_N_HARD_RESET_COUNT 2 diff --git a/include/linux/usb/pd_vdo.h b/include/linux/usb/pd_vdo.h index 68bdc4e2f5a9..8c08eeb9a74b 100644 --- a/include/linux/usb/pd_vdo.h +++ b/include/linux/usb/pd_vdo.h @@ -103,17 +103,25 @@ * -------------------- * <31> :: data capable as a USB host * <30> :: data capable as a USB device - * <29:27> :: product type + * <29:27> :: product type (UFP / Cable) * <26> :: modal operation supported (1b == yes) - * <25:16> :: Reserved, Shall be set to zero + * <25:16> :: product type (DFP) * <15:0> :: USB-IF assigned VID for this cable vendor */ #define IDH_PTYPE_UNDEF 0 #define IDH_PTYPE_HUB 1 #define IDH_PTYPE_PERIPH 2 +#define IDH_PTYPE_PSD 3 +#define IDH_PTYPE_AMA 5 + #define IDH_PTYPE_PCABLE 3 #define IDH_PTYPE_ACABLE 4 -#define IDH_PTYPE_AMA 5 + +#define IDH_PTYPE_DFP_UNDEF 0 +#define IDH_PTYPE_DFP_HUB 1 +#define IDH_PTYPE_DFP_HOST 2 +#define IDH_PTYPE_DFP_PB 3 +#define IDH_PTYPE_DFP_AMC 4 #define VDO_IDH(usbh, usbd, ptype, is_modal, vid) \ ((usbh) << 31 | (usbd) << 30 | ((ptype) & 0x7) << 27 \ @@ -122,6 +130,7 @@ #define PD_IDH_PTYPE(vdo) (((vdo) >> 27) & 0x7) #define PD_IDH_VID(vdo) ((vdo) & 0xffff) #define PD_IDH_MODAL_SUPP(vdo) ((vdo) & (1 << 26)) +#define PD_IDH_DFP_PTYPE(vdo) (((vdo) >> 23) & 0x7) /* * Cert Stat VDO @@ -177,7 +186,7 @@ * <31:28> :: Cable HW version * <27:24> :: Cable FW version * <23:20> :: Reserved, Shall be set to zero - * <19:18> :: type-C to Type-A/B/C (00b == A, 01 == B, 10 == C) + * <19:18> :: type-C to Type-A/B/C/Captive (00b == A, 01 == B, 10 == C, 11 == Captive) * <17> :: Type-C to Plug/Receptacle (0b == plug, 1b == receptacle) * <16:13> :: cable latency (0001 == <10ns(~1m length)) * <12:11> :: cable termination type (11b == both ends active VCONN req) @@ -193,6 +202,7 @@ #define CABLE_ATYPE 0 #define CABLE_BTYPE 1 #define CABLE_CTYPE 2 +#define CABLE_CAPTIVE 3 #define CABLE_PLUG 0 #define CABLE_RECEPTACLE 1 #define CABLE_CURR_1A5 0 @@ -208,6 +218,7 @@ | (tx1d) << 10 | (tx2d) << 9 | (rx1d) << 8 | (rx2d) << 7 \ | ((cur) & 0x3) << 5 | (vps) << 4 | (sopp) << 3 \ | ((usbss) & 0x7)) +#define VDO_TYPEC_CABLE_TYPE(vdo) (((vdo) >> 18) & 0x3) /* * AMA VDO diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 8e67eff9039f..1c09b922f8b0 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -62,7 +62,6 @@ * @bulk_out_endpointAddress: endpoint address for the bulk out pipe for this * port. * @flags: usb serial port flags - * @write_wait: a wait_queue_head_t used by the port. * @work: work queue entry for the line discipline waking up. * @dev: pointer to the serial device * @@ -108,7 +107,6 @@ struct usb_serial_port { int tx_bytes; unsigned long flags; - wait_queue_head_t write_wait; struct work_struct work; unsigned long sysrq; /* sysrq timeout */ struct device dev; diff --git a/include/linux/usb/tcpm.h b/include/linux/usb/tcpm.h index 09762d26fa0c..f4a18427f5c4 100644 --- a/include/linux/usb/tcpm.h +++ b/include/linux/usb/tcpm.h @@ -83,6 +83,27 @@ enum tcpm_transmit_type { * Optional; Called to enable/disable PD 3.0 fast role swap. * Enabling frs is accessory dependent as not all PD3.0 * accessories support fast role swap. + * @frs_sourcing_vbus: + * Optional; Called to notify that vbus is now being sourced. + * Low level drivers can perform chip specific operations, if any. + * @enable_auto_vbus_discharge: + * Optional; TCPCI spec based TCPC implementations can optionally + * support hardware to autonomously dischrge vbus upon disconnecting + * as sink or source. TCPM signals TCPC to enable the mechanism upon + * entering connected state and signals disabling upon disconnect. + * @set_auto_vbus_discharge_threshold: + * Mandatory when enable_auto_vbus_discharge is implemented. TCPM + * calls this function to allow lower levels drivers to program the + * vbus threshold voltage below which the vbus discharge circuit + * will be turned on. requested_vbus_voltage is set to 0 when vbus + * is going to disappear knowingly i.e. during PR_SWAP and + * HARD_RESET etc. + * @is_vbus_vsafe0v: + * Optional; TCPCI spec based TCPC implementations are expected to + * detect VSAFE0V voltage level at vbus. When detection of VSAFE0V + * is supported by TCPC, set this callback for TCPM to query + * whether vbus is at VSAFE0V when needed. + * Returns true when vbus is at VSAFE0V, false otherwise. */ struct tcpc_dev { struct fwnode_handle *fwnode; @@ -106,9 +127,14 @@ struct tcpc_dev { enum typec_cc_status cc); int (*try_role)(struct tcpc_dev *dev, int role); int (*pd_transmit)(struct tcpc_dev *dev, enum tcpm_transmit_type type, - const struct pd_message *msg); + const struct pd_message *msg, unsigned int negotiated_rev); int (*set_bist_data)(struct tcpc_dev *dev, bool on); int (*enable_frs)(struct tcpc_dev *dev, bool enable); + void (*frs_sourcing_vbus)(struct tcpc_dev *dev); + int (*enable_auto_vbus_discharge)(struct tcpc_dev *dev, bool enable); + int (*set_auto_vbus_discharge_threshold)(struct tcpc_dev *dev, enum typec_pwr_opmode mode, + bool pps_active, u32 requested_vbus_voltage); + bool (*is_vbus_vsafe0v)(struct tcpc_dev *dev); }; struct tcpm_port; diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h index 6be558045942..54475323f83b 100644 --- a/include/linux/usb/typec.h +++ b/include/linux/usb/typec.h @@ -126,9 +126,11 @@ struct typec_altmode_desc { enum typec_port_data roles; }; +int typec_partner_set_num_altmodes(struct typec_partner *partner, int num_altmodes); struct typec_altmode *typec_partner_register_altmode(struct typec_partner *partner, const struct typec_altmode_desc *desc); +int typec_plug_set_num_altmodes(struct typec_plug *plug, int num_altmodes); struct typec_altmode *typec_plug_register_altmode(struct typec_plug *plug, const struct typec_altmode_desc *desc); diff --git a/include/linux/usb/typec_tbt.h b/include/linux/usb/typec_tbt.h index 47c2d501ddce..63dd44b72e0c 100644 --- a/include/linux/usb/typec_tbt.h +++ b/include/linux/usb/typec_tbt.h @@ -39,12 +39,16 @@ struct typec_thunderbolt_data { #define TBT_CABLE_USB3_GEN1 1 #define TBT_CABLE_USB3_PASSIVE 2 #define TBT_CABLE_10_AND_20GBPS 3 -#define TBT_CABLE_ROUNDED BIT(19) +#define TBT_CABLE_ROUNDED_SUPPORT(_vdo_) \ + (((_vdo_) & GENMASK(20, 19)) >> 19) +#define TBT_GEN3_NON_ROUNDED 0 +#define TBT_GEN3_GEN4_ROUNDED_NON_ROUNDED 1 #define TBT_CABLE_OPTICAL BIT(21) #define TBT_CABLE_RETIMER BIT(22) #define TBT_CABLE_LINK_TRAINING BIT(23) #define TBT_SET_CABLE_SPEED(_s_) (((_s_) & GENMASK(2, 0)) << 16) +#define TBT_SET_CABLE_ROUNDED(_g_) (((_g_) & GENMASK(1, 0)) << 19) /* TBT3 Device Enter Mode VDO bits */ #define TBT_ENTER_MODE_CABLE_SPEED(s) TBT_SET_CABLE_SPEED(s) diff --git a/include/linux/usb_usual.h b/include/linux/usb_usual.h index 4a19ac3f24d0..6b03fdd69d27 100644 --- a/include/linux/usb_usual.h +++ b/include/linux/usb_usual.h @@ -84,6 +84,8 @@ /* Cannot handle REPORT_LUNS */ \ US_FLAG(ALWAYS_SYNC, 0x20000000) \ /* lies about caching, so always sync */ \ + US_FLAG(NO_SAME, 0x40000000) \ + /* Cannot handle WRITE_SAME */ \ #define US_FLAG(name, value) US_FL_##name = value , enum { US_DO_ALL_FLAGS }; |