USB / Thunderbolt driver updates for 6.14-rc1

Here is the USB and Thunderbolt driver updates for 6.14-rc1.  Nothing
 huge in here, just lots of new hardware support and updates for existing
 drivers.  Changes here are:
   - big gadget f_tcm driver update
   - other gadget driver updates and fixes
   - thunderbolt driver updates for new hardware and capabilities and
     lots more debugging functionality to handle it when things aren't
     working well.
   - xhci driver updates
   - new USB-serial device updates
   - typec driver updates, including a chrome platform driver (acked by
     the subsystem maintainers)
   - other small driver updates
 
 All of these have been in linux-next for a while with no reported
 issues.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCZ5fI8A8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+yloPwCguHSvL8FM6Xwaxc/hdfalwI4c49AAnRECaMhR
 mA1owvXgrKO3hjDHo2Sg
 =Z5yt
 -----END PGP SIGNATURE-----

Merge tag 'usb-6.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb

Pull USB / Thunderbolt driver updates from Greg KH:
 "Here is the USB and Thunderbolt driver updates for 6.14-rc1. Nothing
  huge in here, just lots of new hardware support and updates for
  existing drivers. Changes here are:

   - big gadget f_tcm driver update

   - other gadget driver updates and fixes

   - thunderbolt driver updates for new hardware and capabilities and
     lots more debugging functionality to handle it when things aren't
     working well.

   - xhci driver updates

   - new USB-serial device updates

   - typec driver updates, including a chrome platform driver (acked by
     the subsystem maintainers)

   - other small driver updates

  All of these have been in linux-next for a while with no reported
  issues"

* tag 'usb-6.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (123 commits)
  usb: hcd: Bump local buffer size in rh_string()
  Revert "usb: gadget: u_serial: Disable ep before setting port to null to fix the crash caused by port being null"
  usb: typec: tcpci: Prevent Sink disconnection before vPpsShutdown in SPR PPS
  usb: xhci: tegra: Fix OF boolean read warning
  usb: host: xhci-plat: add support compatible ID PNP0D15
  usb: typec: ucsi: Add a macro definition for UCSI v1.0
  usb: dwc3: core: Defer the probe until USB power supply ready
  usbip: Correct format specifier for seqnum from %d to %u
  usbip: Fix seqnum sign extension issue in vhci_tx_urb
  dt-bindings: usb: snps,dwc3: Split core description
  usb: quirks: Add NO_LPM quirk for TOSHIBA TransMemory-Mx device
  usb: dwc3: gadget: Reinitiate stream for all host NoStream behavior
  USB: Use str_enable_disable-like helpers
  USB: gadget: Use str_enable_disable-like helpers
  USB: phy: Use str_enable_disable-like helpers
  USB: typec: Use str_enable_disable-like helpers
  USB: host: Use str_enable_disable-like helpers
  USB: Replace own str_plural with common one
  USB: serial: quatech2: fix null-ptr-deref in qt2_process_read_urb()
  usb: phy: Remove API devm_usb_put_phy()
  ...
This commit is contained in:
Linus Torvalds 2025-01-27 16:29:16 -08:00
commit cc8b10fa70
142 changed files with 3833 additions and 1483 deletions

View File

@ -293,6 +293,13 @@ properties:
PD negotiation till BC1.2 detection completes.
default: 0
pd-revision:
description: Specifies the maximum USB PD revision and version supported by
the connector. This property is specified in the following order;
<revision_major, revision_minor, version_major, version_minor>.
$ref: /schemas/types.yaml#/definitions/uint8-array
maxItems: 4
dependencies:
sink-vdos-v1: [ sink-vdos ]
sink-vdos: [ sink-vdos-v1 ]

View File

@ -113,27 +113,27 @@ examples:
- |
#include <dt-bindings/clock/aspeed-clock.h>
vhub: usb-vhub@1e6a0000 {
compatible = "aspeed,ast2500-usb-vhub";
reg = <0x1e6a0000 0x300>;
interrupts = <5>;
clocks = <&syscon ASPEED_CLK_GATE_USBPORT1CLK>;
aspeed,vhub-downstream-ports = <5>;
aspeed,vhub-generic-endpoints = <15>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_usb2ad_default>;
compatible = "aspeed,ast2500-usb-vhub";
reg = <0x1e6a0000 0x300>;
interrupts = <5>;
clocks = <&syscon ASPEED_CLK_GATE_USBPORT1CLK>;
aspeed,vhub-downstream-ports = <5>;
aspeed,vhub-generic-endpoints = <15>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_usb2ad_default>;
vhub-vendor-id = <0x1d6b>;
vhub-product-id = <0x0107>;
vhub-device-revision = <0x0100>;
vhub-strings {
#address-cells = <1>;
#size-cells = <0>;
vhub-vendor-id = <0x1d6b>;
vhub-product-id = <0x0107>;
vhub-device-revision = <0x0100>;
vhub-strings {
#address-cells = <1>;
#size-cells = <0>;
string@409 {
reg = <0x409>;
manufacturer = "ASPEED";
product = "USB Virtual Hub";
serial-number = "0000";
};
string@409 {
reg = <0x409>;
manufacturer = "ASPEED";
product = "USB Virtual Hub";
serial-number = "0000";
};
};
};

View File

@ -41,10 +41,10 @@ additionalProperties: false
examples:
- |
usb@f0b02000 {
compatible = "brcm,bdc-udc-v2";
reg = <0xf0b02000 0xfc4>;
interrupts = <0x0 0x60 0x0>;
phys = <&usbphy_0 0x0>;
clocks = <&sw_usbd>;
};
usb@f0b02000 {
compatible = "brcm,bdc-udc-v2";
reg = <0xf0b02000 0xfc4>;
interrupts = <0x0 0x60 0x0>;
phys = <&usbphy_0 0x0>;
clocks = <&sw_usbd>;
};

View File

@ -56,21 +56,21 @@ examples:
/* 2.0 hub on port 1 */
hub_2_0: hub@1 {
compatible = "usb4b4,6504";
reg = <1>;
peer-hub = <&hub_3_0>;
reset-gpios = <&gpio1 11 GPIO_ACTIVE_LOW>;
vdd-supply = <&reg_1v2_usb>;
vdd2-supply = <&reg_3v3_usb>;
compatible = "usb4b4,6504";
reg = <1>;
peer-hub = <&hub_3_0>;
reset-gpios = <&gpio1 11 GPIO_ACTIVE_LOW>;
vdd-supply = <&reg_1v2_usb>;
vdd2-supply = <&reg_3v3_usb>;
};
/* 3.0 hub on port 2 */
hub_3_0: hub@2 {
compatible = "usb4b4,6506";
reg = <2>;
peer-hub = <&hub_2_0>;
reset-gpios = <&gpio1 11 GPIO_ACTIVE_LOW>;
vdd-supply = <&reg_1v2_usb>;
vdd2-supply = <&reg_3v3_usb>;
compatible = "usb4b4,6506";
reg = <2>;
peer-hub = <&hub_2_0>;
reset-gpios = <&gpio1 11 GPIO_ACTIVE_LOW>;
vdd-supply = <&reg_1v2_usb>;
vdd2-supply = <&reg_3v3_usb>;
};
};

View File

@ -192,7 +192,7 @@ unevaluatedProperties: false
examples:
- |
usb@101c0000 {
usb@101c0000 {
compatible = "rockchip,rk3066-usb", "snps,dwc2";
reg = <0x10180000 0x40000>;
interrupts = <18>;
@ -200,6 +200,6 @@ examples:
clock-names = "otg";
phys = <&usbphy>;
phy-names = "usb2-phy";
};
};
...

View File

@ -87,21 +87,21 @@ examples:
#size-cells = <0>;
typec-mux@42 {
compatible = "fcs,fsa4480";
reg = <0x42>;
compatible = "fcs,fsa4480";
reg = <0x42>;
interrupts-extended = <&tlmm 2 IRQ_TYPE_LEVEL_LOW>;
interrupts-extended = <&tlmm 2 IRQ_TYPE_LEVEL_LOW>;
vcc-supply = <&vreg_bob>;
vcc-supply = <&vreg_bob>;
mode-switch;
orientation-switch;
mode-switch;
orientation-switch;
port {
fsa4480_ept: endpoint {
remote-endpoint = <&typec_controller>;
port {
fsa4480_ept: endpoint {
remote-endpoint = <&typec_controller>;
};
};
};
};
};
...

View File

@ -20,6 +20,7 @@ properties:
items:
- enum:
- nxp,cbdtu02043
- onnn,fsusb42
- onnn,fsusb43l10x
- pericom,pi3usb102
- ti,tmuxhs4212

View File

@ -58,20 +58,20 @@ examples:
#define KEEM_BAY_A53_AUX_USB_SUSPEND
usb {
compatible = "intel,keembay-dwc3";
clocks = <&scmi_clk KEEM_BAY_A53_AUX_USB>,
<&scmi_clk KEEM_BAY_A53_AUX_USB_REF>,
<&scmi_clk KEEM_BAY_A53_AUX_USB_ALT_REF>,
<&scmi_clk KEEM_BAY_A53_AUX_USB_SUSPEND>;
clock-names = "async_master", "ref", "alt_ref", "suspend";
ranges;
#address-cells = <1>;
#size-cells = <1>;
compatible = "intel,keembay-dwc3";
clocks = <&scmi_clk KEEM_BAY_A53_AUX_USB>,
<&scmi_clk KEEM_BAY_A53_AUX_USB_REF>,
<&scmi_clk KEEM_BAY_A53_AUX_USB_ALT_REF>,
<&scmi_clk KEEM_BAY_A53_AUX_USB_SUSPEND>;
clock-names = "async_master", "ref", "alt_ref", "suspend";
ranges;
#address-cells = <1>;
#size-cells = <1>;
usb@34000000 {
compatible = "snps,dwc3";
reg = <0x34000000 0x10000>;
interrupts = <GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH>;
dr_mode = "peripheral";
};
usb@34000000 {
compatible = "snps,dwc3";
reg = <0x34000000 0x10000>;
interrupts = <GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH>;
dr_mode = "peripheral";
};
};

View File

@ -54,19 +54,19 @@ examples:
#size-cells = <0>;
typec-mux@48 {
compatible = "ite,it5205";
reg = <0x48>;
compatible = "ite,it5205";
reg = <0x48>;
mode-switch;
orientation-switch;
mode-switch;
orientation-switch;
vcc-supply = <&mt6359_vibr_ldo_reg>;
vcc-supply = <&mt6359_vibr_ldo_reg>;
port {
it5205_usbss_sbu: endpoint {
remote-endpoint = <&typec_controller>;
port {
it5205_usbss_sbu: endpoint {
remote-endpoint = <&typec_controller>;
};
};
};
};
};
...

View File

@ -13,8 +13,12 @@ description: Maxim TCPCI Type-C PD controller
properties:
compatible:
enum:
- maxim,max33359
oneOf:
- enum:
- maxim,max33359
- items:
- const: maxim,max77759-tcpci
- const: maxim,max33359
reg:
maxItems: 1
@ -70,6 +74,7 @@ examples:
PDO_FIXED_DUAL_ROLE)
PDO_FIXED(9000, 2000, 0)>;
sink-bc12-completion-time-ms = <500>;
pd-revision = /bits/ 8 <0x03 0x01 0x01 0x08>;
};
};
};

View File

@ -50,18 +50,18 @@ additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
spi {
#address-cells = <1>;
#size-cells = <0>;
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
spi {
#address-cells = <1>;
#size-cells = <0>;
udc@0 {
compatible = "maxim,max3420-udc";
reg = <0>;
interrupt-parent = <&gpio>;
interrupts = <0 IRQ_TYPE_EDGE_FALLING>, <10 IRQ_TYPE_EDGE_BOTH>;
interrupt-names = "udc", "vbus";
spi-max-frequency = <12500000>;
};
};
udc@0 {
compatible = "maxim,max3420-udc";
reg = <0>;
interrupt-parent = <&gpio>;
interrupts = <0 IRQ_TYPE_EDGE_FALLING>, <10 IRQ_TYPE_EDGE_BOTH>;
interrupt-names = "udc", "vbus";
spi-max-frequency = <12500000>;
};
};

View File

@ -189,7 +189,7 @@ examples:
#size-cells = <0>;
ethernet@1 {
compatible = "usb955,9ff";
reg = <1>;
compatible = "usb955,9ff";
reg = <1>;
};
};

View File

@ -27,6 +27,7 @@ properties:
- qcom,msm8998-dwc3
- qcom,qcm2290-dwc3
- qcom,qcs404-dwc3
- qcom,qcs615-dwc3
- qcom,qcs8300-dwc3
- qcom,qdu1000-dwc3
- qcom,sa8775p-dwc3
@ -342,6 +343,7 @@ allOf:
contains:
enum:
- qcom,qcm2290-dwc3
- qcom,qcs615-dwc3
- qcom,sar2130p-dwc3
- qcom,sc8180x-dwc3
- qcom,sc8180x-dwc3-mp
@ -454,8 +456,10 @@ allOf:
then:
properties:
interrupts:
minItems: 3
maxItems: 4
interrupt-names:
minItems: 3
items:
- const: pwr_event
- const: dp_hs_phy_irq
@ -470,6 +474,7 @@ allOf:
- qcom,ipq4019-dwc3
- qcom,ipq8064-dwc3
- qcom,msm8994-dwc3
- qcom,qcs615-dwc3
- qcom,qcs8300-dwc3
- qcom,qdu1000-dwc3
- qcom,sa8775p-dwc3

View File

@ -104,26 +104,26 @@ examples:
#size-cells = <1>;
usb3host: usb@85060000 {
compatible = "renesas,r9a09g011-xhci",
"renesas,rzv2m-xhci";
reg = <0x85060000 0x2000>;
interrupts = <GIC_SPI 245 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD R9A09G011_USB_ACLK_H>,
<&cpg CPG_MOD R9A09G011_USB_PCLK>;
clock-names = "axi", "reg";
power-domains = <&cpg>;
resets = <&cpg R9A09G011_USB_ARESETN_H>;
compatible = "renesas,r9a09g011-xhci",
"renesas,rzv2m-xhci";
reg = <0x85060000 0x2000>;
interrupts = <GIC_SPI 245 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD R9A09G011_USB_ACLK_H>,
<&cpg CPG_MOD R9A09G011_USB_PCLK>;
clock-names = "axi", "reg";
power-domains = <&cpg>;
resets = <&cpg R9A09G011_USB_ARESETN_H>;
};
usb3peri: usb3peri@85070000 {
compatible = "renesas,r9a09g011-usb3-peri",
"renesas,rzv2m-usb3-peri";
reg = <0x85070000 0x400>;
interrupts = <GIC_SPI 246 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD R9A09G011_USB_ACLK_P>,
<&cpg CPG_MOD R9A09G011_USB_PCLK>;
clock-names = "axi", "reg";
power-domains = <&cpg>;
resets = <&cpg R9A09G011_USB_ARESETN_P>;
compatible = "renesas,r9a09g011-usb3-peri",
"renesas,rzv2m-usb3-peri";
reg = <0x85070000 0x400>;
interrupts = <GIC_SPI 246 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD R9A09G011_USB_ACLK_P>,
<&cpg CPG_MOD R9A09G011_USB_PCLK>;
clock-names = "axi", "reg";
power-domains = <&cpg>;
resets = <&cpg R9A09G011_USB_ARESETN_P>;
};
};

View File

@ -132,19 +132,19 @@ examples:
usb-role-switch;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
usb3_hs_ep: endpoint {
remote-endpoint = <&hs_ep>;
};
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
usb3_hs_ep: endpoint {
remote-endpoint = <&hs_ep>;
};
port@1 {
reg = <1>;
usb3_role_switch: endpoint {
remote-endpoint = <&hd3ss3220_out_ep>;
};
};
port@1 {
reg = <1>;
usb3_role_switch: endpoint {
remote-endpoint = <&hd3ss3220_out_ep>;
};
};
};
};

View File

@ -26,6 +26,7 @@ properties:
- renesas,usbhs-r9a07g043 # RZ/G2UL and RZ/Five
- renesas,usbhs-r9a07g044 # RZ/G2{L,LC}
- renesas,usbhs-r9a07g054 # RZ/V2L
- renesas,usbhs-r9a08g045 # RZ/G3S
- const: renesas,rzg2l-usbhs
- items:
@ -130,6 +131,7 @@ allOf:
- renesas,usbhs-r9a07g043
- renesas,usbhs-r9a07g044
- renesas,usbhs-r9a07g054
- renesas,usbhs-r9a08g045
then:
properties:
interrupts:

View File

@ -0,0 +1,415 @@
# SPDX-License-Identifier: GPL-2.0
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/snps,dwc3-common.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Synopsys DesignWare USB3 Controller common properties
maintainers:
- Felipe Balbi <balbi@kernel.org>
description:
Defines the properties of the DWC3 core as being embedded in either an
vendor-specific implementation or as a standalone component.
allOf:
- $ref: usb-drd.yaml#
- if:
properties:
dr_mode:
const: peripheral
required:
- dr_mode
then:
$ref: usb.yaml#
else:
$ref: usb-xhci.yaml#
properties:
extcon:
maxItems: 1
deprecated: true
usb-phy:
minItems: 1
items:
- description: USB2/HS PHY
- description: USB3/SS PHY
phys:
minItems: 1
maxItems: 19
phy-names:
minItems: 1
maxItems: 19
oneOf:
- items:
enum: [ usb2-phy, usb3-phy ]
- items:
pattern: "^usb(2-([0-9]|1[0-4])|3-[0-3])$"
snps,usb2-lpm-disable:
description: Indicate if we don't want to enable USB2 HW LPM for host
mode.
type: boolean
snps,usb3_lpm_capable:
description: Determines if platform is USB3 LPM capable
type: boolean
snps,usb2-gadget-lpm-disable:
description: Indicate if we don't want to enable USB2 HW LPM for gadget
mode.
type: boolean
snps,dis-start-transfer-quirk:
description:
When set, disable isoc START TRANSFER command failure SW work-around
for DWC_usb31 version 1.70a-ea06 and prior.
type: boolean
snps,disable_scramble_quirk:
description:
True when SW should disable data scrambling. Only really useful for FPGA
builds.
type: boolean
snps,has-lpm-erratum:
description: True when DWC3 was configured with LPM Erratum enabled
type: boolean
snps,lpm-nyet-threshold:
description: LPM NYET threshold
$ref: /schemas/types.yaml#/definitions/uint8
snps,u2exit_lfps_quirk:
description: Set if we want to enable u2exit lfps quirk
type: boolean
snps,u2ss_inp3_quirk:
description: Set if we enable P3 OK for U2/SS Inactive quirk
type: boolean
snps,req_p1p2p3_quirk:
description:
When set, the core will always request for P1/P2/P3 transition sequence.
type: boolean
snps,del_p1p2p3_quirk:
description:
When set core will delay P1/P2/P3 until a certain amount of 8B10B errors
occur.
type: boolean
snps,del_phy_power_chg_quirk:
description: When set core will delay PHY power change from P0 to P1/P2/P3.
type: boolean
snps,lfps_filter_quirk:
description: When set core will filter LFPS reception.
type: boolean
snps,rx_detect_poll_quirk:
description:
when set core will disable a 400us delay to start Polling LFPS after
RX.Detect.
type: boolean
snps,tx_de_emphasis_quirk:
description: When set core will set Tx de-emphasis value
type: boolean
snps,tx_de_emphasis:
description:
The value driven to the PHY is controlled by the LTSSM during USB3
Compliance mode.
$ref: /schemas/types.yaml#/definitions/uint8
enum:
- 0 # -6dB de-emphasis
- 1 # -3.5dB de-emphasis
- 2 # No de-emphasis
snps,dis_u3_susphy_quirk:
description: When set core will disable USB3 suspend phy
type: boolean
snps,dis_u2_susphy_quirk:
description: When set core will disable USB2 suspend phy
type: boolean
snps,dis_enblslpm_quirk:
description:
When set clears the enblslpm in GUSB2PHYCFG, disabling the suspend signal
to the PHY.
type: boolean
snps,dis-u1-entry-quirk:
description: Set if link entering into U1 needs to be disabled
type: boolean
snps,dis-u2-entry-quirk:
description: Set if link entering into U2 needs to be disabled
type: boolean
snps,dis_rxdet_inp3_quirk:
description:
When set core will disable receiver detection in PHY P3 power state.
type: boolean
snps,dis-u2-freeclk-exists-quirk:
description:
When set, clear the u2_freeclk_exists in GUSB2PHYCFG, specify that USB2
PHY doesn't provide a free-running PHY clock.
type: boolean
snps,dis-del-phy-power-chg-quirk:
description:
When set core will change PHY power from P0 to P1/P2/P3 without delay.
type: boolean
snps,dis-tx-ipgap-linecheck-quirk:
description: When set, disable u2mac linestate check during HS transmit
type: boolean
snps,parkmode-disable-ss-quirk:
description:
When set, all SuperSpeed bus instances in park mode are disabled.
type: boolean
snps,parkmode-disable-hs-quirk:
description:
When set, all HighSpeed bus instances in park mode are disabled.
type: boolean
snps,dis_metastability_quirk:
description:
When set, disable metastability workaround. CAUTION! Use only if you are
absolutely sure of it.
type: boolean
snps,dis-split-quirk:
description:
When set, change the way URBs are handled by the driver. Needed to
avoid -EPROTO errors with usbhid on some devices (Hikey 970).
type: boolean
snps,gfladj-refclk-lpm-sel-quirk:
description:
When set, run the SOF/ITP counter based on ref_clk.
type: boolean
snps,resume-hs-terminations:
description:
Fix the issue of HS terminations CRC error on resume by enabling this
quirk. When set, all the termsel, xcvrsel, opmode becomes 0 during end
of resume. This option is to support certain legacy ULPI PHYs.
type: boolean
snps,ulpi-ext-vbus-drv:
description:
Some ULPI USB PHY does not support internal VBUS supply, and driving
the CPEN pin, requires the configuration of the ulpi DRVVBUSEXTERNAL
bit. When set, the xhci host will configure the USB2 PHY drives VBUS
with an external supply.
type: boolean
snps,is-utmi-l1-suspend:
description:
True when DWC3 asserts output signal utmi_l1_suspend_n, false when
asserts utmi_sleep_n.
type: boolean
snps,hird-threshold:
description: HIRD threshold
$ref: /schemas/types.yaml#/definitions/uint8
snps,hsphy_interface:
description:
High-Speed PHY interface selection between UTMI+ and ULPI when the
DWC_USB3_HSPHY_INTERFACE has value 3.
$ref: /schemas/types.yaml#/definitions/string
enum: [utmi, ulpi]
snps,quirk-frame-length-adjustment:
description:
Value for GFLADJ_30MHZ field of GFLADJ register for post-silicon frame
length adjustment when the fladj_30mhz_sdbnd signal is invalid or
incorrect.
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 0x3f
snps,ref-clock-period-ns:
description:
Value for REFCLKPER field of GUCTL register for reference clock period in
nanoseconds, when the hardware set default does not match the actual
clock.
This binding is deprecated. Instead, provide an appropriate reference clock.
minimum: 8
maximum: 62
deprecated: true
snps,rx-thr-num-pkt:
description:
USB RX packet threshold count. In host mode, this field specifies
the space that must be available in the RX FIFO before the core can
start the corresponding USB RX transaction (burst).
In device mode, this field specifies the space that must be
available in the RX FIFO before the core can send ERDY for a
flow-controlled endpoint. It is only used for SuperSpeed.
The valid values for this field are from 1 to 15. (DWC3 SuperSpeed
USB 3.0 Controller Databook)
$ref: /schemas/types.yaml#/definitions/uint8
minimum: 1
maximum: 15
snps,rx-max-burst:
description:
Max USB RX burst size. In host mode, this field specifies the
Maximum Bulk IN burst the DWC_usb3 core can perform. When the system
bus is slower than the USB, RX FIFO can overrun during a long burst.
You can program a smaller value to this field to limit the RX burst
size that the core can perform. It only applies to SS Bulk,
Isochronous, and Interrupt IN endpoints in the host mode.
In device mode, this field specifies the NUMP value that is sent in
ERDY for an OUT endpoint.
The valid values for this field are from 1 to 16. (DWC3 SuperSpeed
USB 3.0 Controller Databook)
$ref: /schemas/types.yaml#/definitions/uint8
minimum: 1
maximum: 16
snps,tx-thr-num-pkt:
description:
USB TX packet threshold count. This field specifies the number of
packets that must be in the TXFIFO before the core can start
transmission for the corresponding USB transaction (burst).
This count is valid in both host and device modes. It is only used
for SuperSpeed operation.
Valid values are from 1 to 15. (DWC3 SuperSpeed USB 3.0 Controller
Databook)
$ref: /schemas/types.yaml#/definitions/uint8
minimum: 1
maximum: 15
snps,tx-max-burst:
description:
Max USB TX burst size. When the system bus is slower than the USB,
TX FIFO can underrun during a long burst. Program a smaller value
to this field to limit the TX burst size that the core can execute.
In Host mode, it only applies to SS Bulk, Isochronous, and Interrupt
OUT endpoints. This value is not used in device mode.
Valid values are from 1 to 16. (DWC3 SuperSpeed USB 3.0 Controller
Databook)
$ref: /schemas/types.yaml#/definitions/uint8
minimum: 1
maximum: 16
snps,rx-thr-num-pkt-prd:
description:
Periodic ESS RX packet threshold count (host mode only). Set this and
snps,rx-max-burst-prd to a valid, non-zero value 1-16 (DWC_usb31
programming guide section 1.2.4) to enable periodic ESS RX threshold.
$ref: /schemas/types.yaml#/definitions/uint8
minimum: 1
maximum: 16
snps,rx-max-burst-prd:
description:
Max periodic ESS RX burst size (host mode only). Set this and
snps,rx-thr-num-pkt-prd to a valid, non-zero value 1-16 (DWC_usb31
programming guide section 1.2.4) to enable periodic ESS RX threshold.
$ref: /schemas/types.yaml#/definitions/uint8
minimum: 1
maximum: 16
snps,tx-thr-num-pkt-prd:
description:
Periodic ESS TX packet threshold count (host mode only). Set this and
snps,tx-max-burst-prd to a valid, non-zero value 1-16 (DWC_usb31
programming guide section 1.2.3) to enable periodic ESS TX threshold.
$ref: /schemas/types.yaml#/definitions/uint8
minimum: 1
maximum: 16
snps,tx-max-burst-prd:
description:
Max periodic ESS TX burst size (host mode only). Set this and
snps,tx-thr-num-pkt-prd to a valid, non-zero value 1-16 (DWC_usb31
programming guide section 1.2.3) to enable periodic ESS TX threshold.
$ref: /schemas/types.yaml#/definitions/uint8
minimum: 1
maximum: 16
tx-fifo-resize:
description: Determines if the TX fifos can be dynamically resized depending
on the number of IN endpoints used and if bursting is supported. This
may help improve bandwidth on platforms with higher system latencies, as
increased fifo space allows for the controller to prefetch data into its
internal memory.
type: boolean
tx-fifo-max-num:
description: Specifies the max number of packets the txfifo resizing logic
can account for when higher endpoint bursting is used. (bMaxBurst > 6) The
higher the number, the more fifo space the txfifo resizing logic will
allocate for that endpoint.
$ref: /schemas/types.yaml#/definitions/uint8
minimum: 3
snps,incr-burst-type-adjustment:
description:
Value for INCR burst type of GSBUSCFG0 register, undefined length INCR
burst type enable and INCRx type. A single value means INCRX burst mode
enabled. If more than one value specified, undefined length INCR burst
type will be enabled with burst lengths utilized up to the maximum
of the values passed in this property.
$ref: /schemas/types.yaml#/definitions/uint32-array
minItems: 1
maxItems: 8
uniqueItems: true
items:
enum: [1, 4, 8, 16, 32, 64, 128, 256]
num-hc-interrupters:
maximum: 8
default: 1
port:
$ref: /schemas/graph.yaml#/properties/port
description:
This port is used with the 'usb-role-switch' property to connect the
dwc3 to type C connector.
ports:
$ref: /schemas/graph.yaml#/properties/ports
description:
Those ports should be used with any connector to the data bus of this
controller using the OF graph bindings specified if the "usb-role-switch"
property is used.
properties:
port@0:
$ref: /schemas/graph.yaml#/properties/port
description: High Speed (HS) data bus.
port@1:
$ref: /schemas/graph.yaml#/properties/port
description: Super Speed (SS) data bus.
wakeup-source:
$ref: /schemas/types.yaml#/definitions/flag
description:
Enable USB remote wakeup.
required:
- compatible
- reg
additionalProperties: true
...

View File

@ -15,18 +15,7 @@ description:
compatible string.
allOf:
- $ref: usb-drd.yaml#
- if:
properties:
dr_mode:
const: peripheral
required:
- dr_mode
then:
$ref: usb.yaml#
else:
$ref: usb-xhci.yaml#
- $ref: snps,dwc3-common.yaml#
properties:
compatible:
@ -70,32 +59,9 @@ properties:
dma-coherent: true
extcon:
maxItems: 1
deprecated: true
iommus:
maxItems: 1
usb-phy:
minItems: 1
items:
- description: USB2/HS PHY
- description: USB3/SS PHY
phys:
minItems: 1
maxItems: 19
phy-names:
minItems: 1
maxItems: 19
oneOf:
- items:
enum: [ usb2-phy, usb3-phy ]
- items:
pattern: "^usb(2-([0-9]|1[0-4])|3-[0-3])$"
power-domains:
description:
The DWC3 has 2 power-domains. The power management unit (PMU) and
@ -109,361 +75,6 @@ properties:
resets:
minItems: 1
snps,usb2-lpm-disable:
description: Indicate if we don't want to enable USB2 HW LPM for host
mode.
type: boolean
snps,usb3_lpm_capable:
description: Determines if platform is USB3 LPM capable
type: boolean
snps,usb2-gadget-lpm-disable:
description: Indicate if we don't want to enable USB2 HW LPM for gadget
mode.
type: boolean
snps,dis-start-transfer-quirk:
description:
When set, disable isoc START TRANSFER command failure SW work-around
for DWC_usb31 version 1.70a-ea06 and prior.
type: boolean
snps,disable_scramble_quirk:
description:
True when SW should disable data scrambling. Only really useful for FPGA
builds.
type: boolean
snps,has-lpm-erratum:
description: True when DWC3 was configured with LPM Erratum enabled
type: boolean
snps,lpm-nyet-threshold:
description: LPM NYET threshold
$ref: /schemas/types.yaml#/definitions/uint8
snps,u2exit_lfps_quirk:
description: Set if we want to enable u2exit lfps quirk
type: boolean
snps,u2ss_inp3_quirk:
description: Set if we enable P3 OK for U2/SS Inactive quirk
type: boolean
snps,req_p1p2p3_quirk:
description:
When set, the core will always request for P1/P2/P3 transition sequence.
type: boolean
snps,del_p1p2p3_quirk:
description:
When set core will delay P1/P2/P3 until a certain amount of 8B10B errors
occur.
type: boolean
snps,del_phy_power_chg_quirk:
description: When set core will delay PHY power change from P0 to P1/P2/P3.
type: boolean
snps,lfps_filter_quirk:
description: When set core will filter LFPS reception.
type: boolean
snps,rx_detect_poll_quirk:
description:
when set core will disable a 400us delay to start Polling LFPS after
RX.Detect.
type: boolean
snps,tx_de_emphasis_quirk:
description: When set core will set Tx de-emphasis value
type: boolean
snps,tx_de_emphasis:
description:
The value driven to the PHY is controlled by the LTSSM during USB3
Compliance mode.
$ref: /schemas/types.yaml#/definitions/uint8
enum:
- 0 # -6dB de-emphasis
- 1 # -3.5dB de-emphasis
- 2 # No de-emphasis
snps,dis_u3_susphy_quirk:
description: When set core will disable USB3 suspend phy
type: boolean
snps,dis_u2_susphy_quirk:
description: When set core will disable USB2 suspend phy
type: boolean
snps,dis_enblslpm_quirk:
description:
When set clears the enblslpm in GUSB2PHYCFG, disabling the suspend signal
to the PHY.
type: boolean
snps,dis-u1-entry-quirk:
description: Set if link entering into U1 needs to be disabled
type: boolean
snps,dis-u2-entry-quirk:
description: Set if link entering into U2 needs to be disabled
type: boolean
snps,dis_rxdet_inp3_quirk:
description:
When set core will disable receiver detection in PHY P3 power state.
type: boolean
snps,dis-u2-freeclk-exists-quirk:
description:
When set, clear the u2_freeclk_exists in GUSB2PHYCFG, specify that USB2
PHY doesn't provide a free-running PHY clock.
type: boolean
snps,dis-del-phy-power-chg-quirk:
description:
When set core will change PHY power from P0 to P1/P2/P3 without delay.
type: boolean
snps,dis-tx-ipgap-linecheck-quirk:
description: When set, disable u2mac linestate check during HS transmit
type: boolean
snps,parkmode-disable-ss-quirk:
description:
When set, all SuperSpeed bus instances in park mode are disabled.
type: boolean
snps,parkmode-disable-hs-quirk:
description:
When set, all HighSpeed bus instances in park mode are disabled.
type: boolean
snps,dis_metastability_quirk:
description:
When set, disable metastability workaround. CAUTION! Use only if you are
absolutely sure of it.
type: boolean
snps,dis-split-quirk:
description:
When set, change the way URBs are handled by the driver. Needed to
avoid -EPROTO errors with usbhid on some devices (Hikey 970).
type: boolean
snps,gfladj-refclk-lpm-sel-quirk:
description:
When set, run the SOF/ITP counter based on ref_clk.
type: boolean
snps,resume-hs-terminations:
description:
Fix the issue of HS terminations CRC error on resume by enabling this
quirk. When set, all the termsel, xcvrsel, opmode becomes 0 during end
of resume. This option is to support certain legacy ULPI PHYs.
type: boolean
snps,ulpi-ext-vbus-drv:
description:
Some ULPI USB PHY does not support internal VBUS supply, and driving
the CPEN pin, requires the configuration of the ulpi DRVVBUSEXTERNAL
bit. When set, the xhci host will configure the USB2 PHY drives VBUS
with an external supply.
type: boolean
snps,is-utmi-l1-suspend:
description:
True when DWC3 asserts output signal utmi_l1_suspend_n, false when
asserts utmi_sleep_n.
type: boolean
snps,hird-threshold:
description: HIRD threshold
$ref: /schemas/types.yaml#/definitions/uint8
snps,hsphy_interface:
description:
High-Speed PHY interface selection between UTMI+ and ULPI when the
DWC_USB3_HSPHY_INTERFACE has value 3.
$ref: /schemas/types.yaml#/definitions/string
enum: [utmi, ulpi]
snps,quirk-frame-length-adjustment:
description:
Value for GFLADJ_30MHZ field of GFLADJ register for post-silicon frame
length adjustment when the fladj_30mhz_sdbnd signal is invalid or
incorrect.
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 0x3f
snps,ref-clock-period-ns:
description:
Value for REFCLKPER field of GUCTL register for reference clock period in
nanoseconds, when the hardware set default does not match the actual
clock.
This binding is deprecated. Instead, provide an appropriate reference clock.
minimum: 8
maximum: 62
deprecated: true
snps,rx-thr-num-pkt:
description:
USB RX packet threshold count. In host mode, this field specifies
the space that must be available in the RX FIFO before the core can
start the corresponding USB RX transaction (burst).
In device mode, this field specifies the space that must be
available in the RX FIFO before the core can send ERDY for a
flow-controlled endpoint. It is only used for SuperSpeed.
The valid values for this field are from 1 to 15. (DWC3 SuperSpeed
USB 3.0 Controller Databook)
$ref: /schemas/types.yaml#/definitions/uint8
minimum: 1
maximum: 15
snps,rx-max-burst:
description:
Max USB RX burst size. In host mode, this field specifies the
Maximum Bulk IN burst the DWC_usb3 core can perform. When the system
bus is slower than the USB, RX FIFO can overrun during a long burst.
You can program a smaller value to this field to limit the RX burst
size that the core can perform. It only applies to SS Bulk,
Isochronous, and Interrupt IN endpoints in the host mode.
In device mode, this field specifies the NUMP value that is sent in
ERDY for an OUT endpoint.
The valid values for this field are from 1 to 16. (DWC3 SuperSpeed
USB 3.0 Controller Databook)
$ref: /schemas/types.yaml#/definitions/uint8
minimum: 1
maximum: 16
snps,tx-thr-num-pkt:
description:
USB TX packet threshold count. This field specifies the number of
packets that must be in the TXFIFO before the core can start
transmission for the corresponding USB transaction (burst).
This count is valid in both host and device modes. It is only used
for SuperSpeed operation.
Valid values are from 1 to 15. (DWC3 SuperSpeed USB 3.0 Controller
Databook)
$ref: /schemas/types.yaml#/definitions/uint8
minimum: 1
maximum: 15
snps,tx-max-burst:
description:
Max USB TX burst size. When the system bus is slower than the USB,
TX FIFO can underrun during a long burst. Program a smaller value
to this field to limit the TX burst size that the core can execute.
In Host mode, it only applies to SS Bulk, Isochronous, and Interrupt
OUT endpoints. This value is not used in device mode.
Valid values are from 1 to 16. (DWC3 SuperSpeed USB 3.0 Controller
Databook)
$ref: /schemas/types.yaml#/definitions/uint8
minimum: 1
maximum: 16
snps,rx-thr-num-pkt-prd:
description:
Periodic ESS RX packet threshold count (host mode only). Set this and
snps,rx-max-burst-prd to a valid, non-zero value 1-16 (DWC_usb31
programming guide section 1.2.4) to enable periodic ESS RX threshold.
$ref: /schemas/types.yaml#/definitions/uint8
minimum: 1
maximum: 16
snps,rx-max-burst-prd:
description:
Max periodic ESS RX burst size (host mode only). Set this and
snps,rx-thr-num-pkt-prd to a valid, non-zero value 1-16 (DWC_usb31
programming guide section 1.2.4) to enable periodic ESS RX threshold.
$ref: /schemas/types.yaml#/definitions/uint8
minimum: 1
maximum: 16
snps,tx-thr-num-pkt-prd:
description:
Periodic ESS TX packet threshold count (host mode only). Set this and
snps,tx-max-burst-prd to a valid, non-zero value 1-16 (DWC_usb31
programming guide section 1.2.3) to enable periodic ESS TX threshold.
$ref: /schemas/types.yaml#/definitions/uint8
minimum: 1
maximum: 16
snps,tx-max-burst-prd:
description:
Max periodic ESS TX burst size (host mode only). Set this and
snps,tx-thr-num-pkt-prd to a valid, non-zero value 1-16 (DWC_usb31
programming guide section 1.2.3) to enable periodic ESS TX threshold.
$ref: /schemas/types.yaml#/definitions/uint8
minimum: 1
maximum: 16
tx-fifo-resize:
description: Determines if the TX fifos can be dynamically resized depending
on the number of IN endpoints used and if bursting is supported. This
may help improve bandwidth on platforms with higher system latencies, as
increased fifo space allows for the controller to prefetch data into its
internal memory.
type: boolean
tx-fifo-max-num:
description: Specifies the max number of packets the txfifo resizing logic
can account for when higher endpoint bursting is used. (bMaxBurst > 6) The
higher the number, the more fifo space the txfifo resizing logic will
allocate for that endpoint.
$ref: /schemas/types.yaml#/definitions/uint8
minimum: 3
snps,incr-burst-type-adjustment:
description:
Value for INCR burst type of GSBUSCFG0 register, undefined length INCR
burst type enable and INCRx type. A single value means INCRX burst mode
enabled. If more than one value specified, undefined length INCR burst
type will be enabled with burst lengths utilized up to the maximum
of the values passed in this property.
$ref: /schemas/types.yaml#/definitions/uint32-array
minItems: 1
maxItems: 8
uniqueItems: true
items:
enum: [1, 4, 8, 16, 32, 64, 128, 256]
num-hc-interrupters:
maximum: 8
default: 1
port:
$ref: /schemas/graph.yaml#/properties/port
description:
This port is used with the 'usb-role-switch' property to connect the
dwc3 to type C connector.
ports:
$ref: /schemas/graph.yaml#/properties/ports
description:
Those ports should be used with any connector to the data bus of this
controller using the OF graph bindings specified if the "usb-role-switch"
property is used.
properties:
port@0:
$ref: /schemas/graph.yaml#/properties/port
description: High Speed (HS) data bus.
port@1:
$ref: /schemas/graph.yaml#/properties/port
description: Super Speed (SS) data bus.
wakeup-source:
$ref: /schemas/types.yaml#/definitions/flag
description:
Enable USB remote wakeup.
unevaluatedProperties: false
required:

View File

@ -56,26 +56,26 @@ examples:
#size-cells = <0>;
hd3ss3220@47 {
compatible = "ti,hd3ss3220";
reg = <0x47>;
interrupt-parent = <&gpio6>;
interrupts = <3>;
compatible = "ti,hd3ss3220";
reg = <0x47>;
interrupt-parent = <&gpio6>;
interrupts = <3>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
hd3ss3220_in_ep: endpoint {
remote-endpoint = <&ss_ep>;
};
};
port@1 {
reg = <1>;
hd3ss3220_out_ep: endpoint {
remote-endpoint = <&usb3_role_switch>;
};
};
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
hd3ss3220_in_ep: endpoint {
remote-endpoint = <&ss_ep>;
};
};
port@1 {
reg = <1>;
hd3ss3220_out_ep: endpoint {
remote-endpoint = <&usb3_role_switch>;
};
};
};
};
};

View File

@ -48,8 +48,8 @@ examples:
device_type = "pci";
usb@0 {
compatible = "pci104c,8241";
reg = <0x0 0x0 0x0 0x0 0x0>;
ti,pwron-active-high;
compatible = "pci104c,8241";
reg = <0x0 0x0 0x0 0x0 0x0>;
ti,pwron-active-high;
};
};

View File

@ -51,19 +51,19 @@ examples:
/* 2.0 hub on port 1 */
hub_2_0: hub@1 {
compatible = "usb451,8027";
reg = <1>;
peer-hub = <&hub_3_0>;
reset-gpios = <&pio 7 GPIO_ACTIVE_HIGH>;
vdd-supply = <&usb_hub_fixed_3v3>;
compatible = "usb451,8027";
reg = <1>;
peer-hub = <&hub_3_0>;
reset-gpios = <&pio 7 GPIO_ACTIVE_HIGH>;
vdd-supply = <&usb_hub_fixed_3v3>;
};
/* 3.0 hub on port 2 */
hub_3_0: hub@2 {
compatible = "usb451,8025";
reg = <2>;
peer-hub = <&hub_2_0>;
reset-gpios = <&pio 7 GPIO_ACTIVE_HIGH>;
vdd-supply = <&usb_hub_fixed_3v3>;
compatible = "usb451,8025";
reg = <2>;
peer-hub = <&hub_2_0>;
reset-gpios = <&pio 7 GPIO_ACTIVE_HIGH>;
vdd-supply = <&usb_hub_fixed_3v3>;
};
};

View File

@ -51,17 +51,17 @@ examples:
/* 2.0 hub on port 1 */
hub_2_0: hub@1 {
compatible = "usb451,8142";
reg = <1>;
peer-hub = <&hub_3_0>;
reset-gpios = <&gpio1 11 GPIO_ACTIVE_LOW>;
compatible = "usb451,8142";
reg = <1>;
peer-hub = <&hub_3_0>;
reset-gpios = <&gpio1 11 GPIO_ACTIVE_LOW>;
};
/* 3.0 hub on port 2 */
hub_3_0: hub@2 {
compatible = "usb451,8140";
reg = <2>;
peer-hub = <&hub_2_0>;
reset-gpios = <&gpio1 11 GPIO_ACTIVE_LOW>;
compatible = "usb451,8140";
reg = <2>;
peer-hub = <&hub_2_0>;
reset-gpios = <&gpio1 11 GPIO_ACTIVE_LOW>;
};
};

View File

@ -404,7 +404,6 @@ PHY
devm_usb_get_phy()
devm_usb_get_phy_by_node()
devm_usb_get_phy_by_phandle()
devm_usb_put_phy()
PINCTRL
devm_pinctrl_get()

View File

@ -285,17 +285,17 @@ OP_REP_IMPORT:
+-----------+--------+------------+---------------------------------------------------+
| 0x138 | 2 | | bcdDevice |
+-----------+--------+------------+---------------------------------------------------+
| 0x139 | 1 | | bDeviceClass |
| 0x13A | 1 | | bDeviceClass |
+-----------+--------+------------+---------------------------------------------------+
| 0x13A | 1 | | bDeviceSubClass |
| 0x13B | 1 | | bDeviceSubClass |
+-----------+--------+------------+---------------------------------------------------+
| 0x13B | 1 | | bDeviceProtocol |
| 0x13C | 1 | | bDeviceProtocol |
+-----------+--------+------------+---------------------------------------------------+
| 0x13C | 1 | | bConfigurationValue |
| 0x13D | 1 | | bConfigurationValue |
+-----------+--------+------------+---------------------------------------------------+
| 0x13D | 1 | | bNumConfigurations |
| 0x13E | 1 | | bNumConfigurations |
+-----------+--------+------------+---------------------------------------------------+
| 0x13E | 1 | | bNumInterfaces |
| 0x13F | 1 | | bNumInterfaces |
+-----------+--------+------------+---------------------------------------------------+
The following four commands have a common basic header called

View File

@ -5481,9 +5481,12 @@ F: include/linux/platform_data/cros_usbpd_notify.h
CHROMEOS EC USB TYPE-C DRIVER
M: Prashant Malani <pmalani@chromium.org>
M: Benson Leung <bleung@chromium.org>
M: Abhishek Pandit-Subedi <abhishekpandit@chromium.org>
L: chrome-platform@lists.linux.dev
S: Maintained
F: drivers/platform/chrome/cros_ec_typec.*
F: drivers/platform/chrome/cros_typec_altmode.*
F: drivers/platform/chrome/cros_typec_switch.c
F: drivers/platform/chrome/cros_typec_vdm.*
@ -5499,6 +5502,13 @@ L: chrome-platform@lists.linux.dev
S: Maintained
F: drivers/watchdog/cros_ec_wdt.c
CHROMEOS UCSI DRIVER
M: Abhishek Pandit-Subedi <abhishekpandit@chromium.org>
M: Łukasz Bartosik <ukaszb@chromium.org>
L: chrome-platform@lists.linux.dev
S: Maintained
F: drivers/usb/typec/ucsi/cros_ec_ucsi.c
CHRONTEL CH7322 CEC DRIVER
M: Joe Tessler <jrt@google.com>
L: linux-media@vger.kernel.org

View File

@ -237,12 +237,19 @@ config CROS_EC_SYSFS
To compile this driver as a module, choose M here: the
module will be called cros_ec_sysfs.
config CROS_EC_TYPEC_ALTMODES
bool
help
Selectable symbol to enable altmodes.
config CROS_EC_TYPEC
tristate "ChromeOS EC Type-C Connector Control"
depends on MFD_CROS_EC_DEV && TYPEC
depends on CROS_USBPD_NOTIFY
depends on USB_ROLE_SWITCH
default MFD_CROS_EC_DEV
select CROS_EC_TYPEC_ALTMODES if TYPEC_DP_ALTMODE
select CROS_EC_TYPEC_ALTMODES if TYPEC_TBT_ALTMODE
help
If you say Y here, you get support for accessing Type C connector
information from the Chrome OS EC.

View File

@ -19,7 +19,11 @@ obj-$(CONFIG_CROS_EC_SPI) += cros_ec_spi.o
obj-$(CONFIG_CROS_EC_UART) += cros_ec_uart.o
cros_ec_lpcs-objs := cros_ec_lpc.o cros_ec_lpc_mec.o
cros-ec-typec-objs := cros_ec_typec.o cros_typec_vdm.o
ifneq ($(CONFIG_CROS_EC_TYPEC_ALTMODES),)
cros-ec-typec-objs += cros_typec_altmode.o
endif
obj-$(CONFIG_CROS_EC_TYPEC) += cros-ec-typec.o
obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpcs.o
obj-$(CONFIG_CROS_EC_PROTO) += cros_ec_proto.o cros_ec_trace.o
obj-$(CONFIG_CROS_KBD_LED_BACKLIGHT) += cros_kbd_led_backlight.o

View File

@ -18,6 +18,7 @@
#include "cros_ec_typec.h"
#include "cros_typec_vdm.h"
#include "cros_typec_altmode.h"
#define DRV_NAME "cros-ec-typec"
@ -290,30 +291,32 @@ static int cros_typec_register_port_altmodes(struct cros_typec_data *typec,
struct typec_altmode *amode;
/* All PD capable CrOS devices are assumed to support DP altmode. */
memset(&desc, 0, sizeof(desc));
desc.svid = USB_TYPEC_DP_SID;
desc.mode = USB_TYPEC_DP_MODE;
desc.vdo = DP_PORT_VDO;
amode = typec_port_register_altmode(port->port, &desc);
amode = cros_typec_register_displayport(port, &desc,
typec->ap_driven_altmode);
if (IS_ERR(amode))
return PTR_ERR(amode);
port->port_altmode[CROS_EC_ALTMODE_DP] = amode;
typec_altmode_set_drvdata(amode, port);
amode->ops = &port_amode_ops;
/*
* Register TBT compatibility alt mode. The EC will not enter the mode
* if it doesn't support it, so it's safe to register it unconditionally
* here for now.
* if it doesn't support it and it will not enter automatically by
* design so we can use the |ap_driven_altmode| feature to check if we
* should register it.
*/
memset(&desc, 0, sizeof(desc));
desc.svid = USB_TYPEC_TBT_SID;
desc.mode = TYPEC_ANY_MODE;
amode = typec_port_register_altmode(port->port, &desc);
if (IS_ERR(amode))
return PTR_ERR(amode);
port->port_altmode[CROS_EC_ALTMODE_TBT] = amode;
typec_altmode_set_drvdata(amode, port);
amode->ops = &port_amode_ops;
if (typec->ap_driven_altmode) {
memset(&desc, 0, sizeof(desc));
desc.svid = USB_TYPEC_TBT_SID;
desc.mode = TBT_MODE;
desc.inactive = true;
amode = cros_typec_register_thunderbolt(port, &desc);
if (IS_ERR(amode))
return PTR_ERR(amode);
port->port_altmode[CROS_EC_ALTMODE_TBT] = amode;
}
port->state.alt = NULL;
port->state.mode = TYPEC_STATE_USB;
@ -576,6 +579,10 @@ static int cros_typec_enable_dp(struct cros_typec_data *typec,
if (!ret)
ret = typec_mux_set(port->mux, &port->state);
if (!ret)
ret = cros_typec_displayport_status_update(port->state.alt,
port->state.data);
return ret;
}
@ -619,6 +626,7 @@ static int cros_typec_configure_mux(struct cros_typec_data *typec, int port_num,
};
struct ec_params_usb_pd_mux_ack mux_ack;
enum typec_orientation orientation;
struct cros_typec_altmode_node *node;
int ret;
ret = cros_ec_cmd(typec->ec, 0, EC_CMD_USB_PD_MUX_INFO,
@ -677,6 +685,14 @@ static int cros_typec_configure_mux(struct cros_typec_data *typec, int port_num,
port->mux_flags);
}
/* Iterate all partner alt-modes and set the active alternate mode. */
list_for_each_entry(node, &port->partner_mode_list, list) {
typec_altmode_update_active(
node->amode,
port->state.alt &&
node->amode->svid == port->state.alt->svid);
}
mux_ack:
if (!typec->needs_mux_ack)
return ret;
@ -1244,6 +1260,8 @@ static int cros_typec_probe(struct platform_device *pdev)
typec->typec_cmd_supported = cros_ec_check_features(ec_dev, EC_FEATURE_TYPEC_CMD);
typec->needs_mux_ack = cros_ec_check_features(ec_dev, EC_FEATURE_TYPEC_MUX_REQUIRE_AP_ACK);
typec->ap_driven_altmode = cros_ec_check_features(
ec_dev, EC_FEATURE_TYPEC_REQUIRE_AP_MODE_ENTRY);
ret = cros_ec_cmd(typec->ec, 0, EC_CMD_USB_PD_PORTS, NULL, 0,
&resp, sizeof(resp));

View File

@ -39,6 +39,7 @@ struct cros_typec_data {
struct work_struct port_work;
bool typec_cmd_supported;
bool needs_mux_ack;
bool ap_driven_altmode;
};
/* Per port data. */

View File

@ -0,0 +1,373 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Alt-mode implementation on ChromeOS EC.
*
* Copyright 2024 Google LLC
* Author: Abhishek Pandit-Subedi <abhishekpandit@chromium.org>
*/
#include "cros_ec_typec.h"
#include <linux/mutex.h>
#include <linux/workqueue.h>
#include <linux/usb/typec_dp.h>
#include <linux/usb/typec_tbt.h>
#include <linux/usb/pd_vdo.h>
#include "cros_typec_altmode.h"
struct cros_typec_altmode_data {
struct work_struct work;
struct cros_typec_port *port;
struct typec_altmode *alt;
bool ap_mode_entry;
struct mutex lock;
u32 header;
u32 *vdo_data;
u8 vdo_size;
u16 sid;
u8 mode;
};
struct cros_typec_dp_data {
struct cros_typec_altmode_data adata;
struct typec_displayport_data data;
bool configured;
bool pending_status_update;
};
static void cros_typec_altmode_work(struct work_struct *work)
{
struct cros_typec_altmode_data *data =
container_of(work, struct cros_typec_altmode_data, work);
mutex_lock(&data->lock);
if (typec_altmode_vdm(data->alt, data->header, data->vdo_data,
data->vdo_size))
dev_err(&data->alt->dev, "VDM 0x%x failed\n", data->header);
data->header = 0;
data->vdo_data = NULL;
data->vdo_size = 0;
mutex_unlock(&data->lock);
}
static int cros_typec_altmode_enter(struct typec_altmode *alt, u32 *vdo)
{
struct cros_typec_altmode_data *adata = typec_altmode_get_drvdata(alt);
struct ec_params_typec_control req = {
.port = adata->port->port_num,
.command = TYPEC_CONTROL_COMMAND_ENTER_MODE,
};
int svdm_version;
int ret;
if (!adata->ap_mode_entry) {
dev_warn(&alt->dev,
"EC does not support AP driven mode entry\n");
return -EOPNOTSUPP;
}
if (adata->sid == USB_TYPEC_DP_SID)
req.mode_to_enter = CROS_EC_ALTMODE_DP;
else if (adata->sid == USB_TYPEC_TBT_SID)
req.mode_to_enter = CROS_EC_ALTMODE_TBT;
else
return -EOPNOTSUPP;
ret = cros_ec_cmd(adata->port->typec_data->ec, 0, EC_CMD_TYPEC_CONTROL,
&req, sizeof(req), NULL, 0);
if (ret < 0)
return ret;
svdm_version = typec_altmode_get_svdm_version(alt);
if (svdm_version < 0)
return svdm_version;
mutex_lock(&adata->lock);
adata->header = VDO(adata->sid, 1, svdm_version, CMD_ENTER_MODE);
adata->header |= VDO_OPOS(adata->mode);
adata->header |= VDO_CMDT(CMDT_RSP_ACK);
adata->vdo_data = NULL;
adata->vdo_size = 1;
schedule_work(&adata->work);
mutex_unlock(&adata->lock);
return ret;
}
static int cros_typec_altmode_exit(struct typec_altmode *alt)
{
struct cros_typec_altmode_data *adata = typec_altmode_get_drvdata(alt);
struct ec_params_typec_control req = {
.port = adata->port->port_num,
.command = TYPEC_CONTROL_COMMAND_EXIT_MODES,
};
int svdm_version;
int ret;
if (!adata->ap_mode_entry) {
dev_warn(&alt->dev,
"EC does not support AP driven mode exit\n");
return -EOPNOTSUPP;
}
ret = cros_ec_cmd(adata->port->typec_data->ec, 0, EC_CMD_TYPEC_CONTROL,
&req, sizeof(req), NULL, 0);
if (ret < 0)
return ret;
svdm_version = typec_altmode_get_svdm_version(alt);
if (svdm_version < 0)
return svdm_version;
mutex_lock(&adata->lock);
adata->header = VDO(adata->sid, 1, svdm_version, CMD_EXIT_MODE);
adata->header |= VDO_OPOS(adata->mode);
adata->header |= VDO_CMDT(CMDT_RSP_ACK);
adata->vdo_data = NULL;
adata->vdo_size = 1;
schedule_work(&adata->work);
mutex_unlock(&adata->lock);
return ret;
}
static int cros_typec_displayport_vdm(struct typec_altmode *alt, u32 header,
const u32 *data, int count)
{
struct cros_typec_dp_data *dp_data = typec_altmode_get_drvdata(alt);
struct cros_typec_altmode_data *adata = &dp_data->adata;
int cmd_type = PD_VDO_CMDT(header);
int cmd = PD_VDO_CMD(header);
int svdm_version;
svdm_version = typec_altmode_get_svdm_version(alt);
if (svdm_version < 0)
return svdm_version;
mutex_lock(&adata->lock);
switch (cmd_type) {
case CMDT_INIT:
if (PD_VDO_SVDM_VER(header) < svdm_version) {
typec_partner_set_svdm_version(adata->port->partner,
PD_VDO_SVDM_VER(header));
svdm_version = PD_VDO_SVDM_VER(header);
}
adata->header = VDO(adata->sid, 1, svdm_version, cmd);
adata->header |= VDO_OPOS(adata->mode);
/*
* DP_CMD_CONFIGURE: We can't actually do anything with the
* provided VDO yet so just send back an ACK.
*
* DP_CMD_STATUS_UPDATE: We wait for Mux changes to send
* DPStatus Acks.
*/
switch (cmd) {
case DP_CMD_CONFIGURE:
dp_data->data.conf = *data;
adata->header |= VDO_CMDT(CMDT_RSP_ACK);
dp_data->configured = true;
schedule_work(&adata->work);
break;
case DP_CMD_STATUS_UPDATE:
dp_data->pending_status_update = true;
break;
default:
adata->header |= VDO_CMDT(CMDT_RSP_ACK);
schedule_work(&adata->work);
break;
}
break;
default:
break;
}
mutex_unlock(&adata->lock);
return 0;
}
static int cros_typec_thunderbolt_vdm(struct typec_altmode *alt, u32 header,
const u32 *data, int count)
{
struct cros_typec_altmode_data *adata = typec_altmode_get_drvdata(alt);
int cmd_type = PD_VDO_CMDT(header);
int cmd = PD_VDO_CMD(header);
int svdm_version;
svdm_version = typec_altmode_get_svdm_version(alt);
if (svdm_version < 0)
return svdm_version;
mutex_lock(&adata->lock);
switch (cmd_type) {
case CMDT_INIT:
if (PD_VDO_SVDM_VER(header) < svdm_version) {
typec_partner_set_svdm_version(adata->port->partner,
PD_VDO_SVDM_VER(header));
svdm_version = PD_VDO_SVDM_VER(header);
}
adata->header = VDO(adata->sid, 1, svdm_version, cmd);
adata->header |= VDO_OPOS(adata->mode);
switch (cmd) {
case CMD_ENTER_MODE:
/* Don't respond to the enter mode vdm because it
* triggers mux configuration. This is handled directly
* by the cros_ec_typec driver so the Thunderbolt driver
* doesn't need to be involved.
*/
break;
default:
adata->header |= VDO_CMDT(CMDT_RSP_ACK);
schedule_work(&adata->work);
break;
}
break;
default:
break;
}
mutex_unlock(&adata->lock);
return 0;
}
static int cros_typec_altmode_vdm(struct typec_altmode *alt, u32 header,
const u32 *data, int count)
{
struct cros_typec_altmode_data *adata = typec_altmode_get_drvdata(alt);
if (!adata->ap_mode_entry)
return -EOPNOTSUPP;
if (adata->sid == USB_TYPEC_DP_SID)
return cros_typec_displayport_vdm(alt, header, data, count);
if (adata->sid == USB_TYPEC_TBT_SID)
return cros_typec_thunderbolt_vdm(alt, header, data, count);
return -EINVAL;
}
static const struct typec_altmode_ops cros_typec_altmode_ops = {
.enter = cros_typec_altmode_enter,
.exit = cros_typec_altmode_exit,
.vdm = cros_typec_altmode_vdm,
};
#if IS_ENABLED(CONFIG_TYPEC_DP_ALTMODE)
int cros_typec_displayport_status_update(struct typec_altmode *altmode,
struct typec_displayport_data *data)
{
struct cros_typec_dp_data *dp_data =
typec_altmode_get_drvdata(altmode);
struct cros_typec_altmode_data *adata = &dp_data->adata;
if (!dp_data->pending_status_update) {
dev_dbg(&altmode->dev,
"Got DPStatus without a pending request\n");
return 0;
}
if (dp_data->configured && dp_data->data.conf != data->conf)
dev_dbg(&altmode->dev,
"DP Conf doesn't match. Requested 0x%04x, Actual 0x%04x\n",
dp_data->data.conf, data->conf);
mutex_lock(&adata->lock);
dp_data->data = *data;
dp_data->pending_status_update = false;
adata->header |= VDO_CMDT(CMDT_RSP_ACK);
adata->vdo_data = &dp_data->data.status;
adata->vdo_size = 2;
schedule_work(&adata->work);
mutex_unlock(&adata->lock);
return 0;
}
struct typec_altmode *
cros_typec_register_displayport(struct cros_typec_port *port,
struct typec_altmode_desc *desc,
bool ap_mode_entry)
{
struct typec_altmode *alt;
struct cros_typec_dp_data *dp_data;
struct cros_typec_altmode_data *adata;
alt = typec_port_register_altmode(port->port, desc);
if (IS_ERR(alt))
return alt;
dp_data = devm_kzalloc(&alt->dev, sizeof(*dp_data), GFP_KERNEL);
if (!dp_data) {
typec_unregister_altmode(alt);
return ERR_PTR(-ENOMEM);
}
adata = &dp_data->adata;
INIT_WORK(&adata->work, cros_typec_altmode_work);
mutex_init(&adata->lock);
adata->alt = alt;
adata->port = port;
adata->ap_mode_entry = ap_mode_entry;
adata->sid = desc->svid;
adata->mode = desc->mode;
typec_altmode_set_ops(alt, &cros_typec_altmode_ops);
typec_altmode_set_drvdata(alt, adata);
return alt;
}
#endif
#if IS_ENABLED(CONFIG_TYPEC_TBT_ALTMODE)
struct typec_altmode *
cros_typec_register_thunderbolt(struct cros_typec_port *port,
struct typec_altmode_desc *desc)
{
struct typec_altmode *alt;
struct cros_typec_altmode_data *adata;
alt = typec_port_register_altmode(port->port, desc);
if (IS_ERR(alt))
return alt;
adata = devm_kzalloc(&alt->dev, sizeof(*adata), GFP_KERNEL);
if (!adata) {
typec_unregister_altmode(alt);
return ERR_PTR(-ENOMEM);
}
INIT_WORK(&adata->work, cros_typec_altmode_work);
adata->alt = alt;
adata->port = port;
adata->ap_mode_entry = true;
adata->sid = desc->svid;
adata->mode = desc->mode;
typec_altmode_set_ops(alt, &cros_typec_altmode_ops);
typec_altmode_set_drvdata(alt, adata);
return alt;
}
#endif

View File

@ -0,0 +1,51 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef __CROS_TYPEC_ALTMODE_H__
#define __CROS_TYPEC_ALTMODE_H__
#include <linux/kconfig.h>
#include <linux/usb/typec.h>
struct cros_typec_port;
struct typec_altmode;
struct typec_altmode_desc;
struct typec_displayport_data;
#if IS_ENABLED(CONFIG_TYPEC_DP_ALTMODE)
struct typec_altmode *
cros_typec_register_displayport(struct cros_typec_port *port,
struct typec_altmode_desc *desc,
bool ap_mode_entry);
int cros_typec_displayport_status_update(struct typec_altmode *altmode,
struct typec_displayport_data *data);
#else
static inline struct typec_altmode *
cros_typec_register_displayport(struct cros_typec_port *port,
struct typec_altmode_desc *desc,
bool ap_mode_entry)
{
return typec_port_register_altmode(port->port, desc);
}
static inline int cros_typec_displayport_status_update(struct typec_altmode *altmode,
struct typec_displayport_data *data)
{
return 0;
}
#endif
#if IS_ENABLED(CONFIG_TYPEC_TBT_ALTMODE)
struct typec_altmode *
cros_typec_register_thunderbolt(struct cros_typec_port *port,
struct typec_altmode_desc *desc);
#else
static inline struct typec_altmode *
cros_typec_register_thunderbolt(struct cros_typec_port *port,
struct typec_altmode_desc *desc)
{
return typec_port_register_altmode(port->port, desc);
}
#endif
#endif /* __CROS_TYPEC_ALTMODE_H__ */

View File

@ -70,6 +70,9 @@ struct tb_ctl {
#define tb_ctl_dbg(ctl, format, arg...) \
dev_dbg(&(ctl)->nhi->pdev->dev, format, ## arg)
#define tb_ctl_dbg_once(ctl, format, arg...) \
dev_dbg_once(&(ctl)->nhi->pdev->dev, format, ## arg)
static DECLARE_WAIT_QUEUE_HEAD(tb_cfg_request_cancel_queue);
/* Serializes access to request kref_get/put */
static DEFINE_MUTEX(tb_cfg_request_lock);
@ -265,7 +268,7 @@ static struct tb_cfg_result parse_header(const struct ctl_pkg *pkg, u32 len,
return res;
}
static void tb_cfg_print_error(struct tb_ctl *ctl,
static void tb_cfg_print_error(struct tb_ctl *ctl, enum tb_cfg_space space,
const struct tb_cfg_result *res)
{
WARN_ON(res->err != 1);
@ -279,8 +282,8 @@ static void tb_cfg_print_error(struct tb_ctl *ctl,
* Invalid cfg_space/offset/length combination in
* cfg_read/cfg_write.
*/
tb_ctl_dbg(ctl, "%llx:%x: invalid config space or offset\n",
res->response_route, res->response_port);
tb_ctl_dbg_once(ctl, "%llx:%x: invalid config space (%u) or offset\n",
res->response_route, res->response_port, space);
return;
case TB_CFG_ERROR_NO_SUCH_PORT:
/*
@ -1072,7 +1075,7 @@ static int tb_cfg_get_error(struct tb_ctl *ctl, enum tb_cfg_space space,
res->tb_error == TB_CFG_ERROR_INVALID_CONFIG_SPACE)
return -ENODEV;
tb_cfg_print_error(ctl, res);
tb_cfg_print_error(ctl, space, res);
if (res->tb_error == TB_CFG_ERROR_LOCK)
return -EACCES;

View File

@ -140,5 +140,4 @@ int tb_cfg_write(struct tb_ctl *ctl, const void *buffer, u64 route, u32 port,
enum tb_cfg_space space, u32 offset, u32 length);
int tb_cfg_get_upstream_port(struct tb_ctl *ctl, u64 route);
#endif

View File

@ -168,6 +168,13 @@ static bool parse_line(char **line, u32 *offs, u32 *val, int short_fmt_len,
* offset relative_offset cap_id vs_cap_id value\n
* v[0] v[1] v[2] v[3] v[4]
*
* For Path configuration space:
* Short format is: offset value\n
* v[0] v[1]
* Long format as produced from the read side:
* offset relative_offset in_hop_id value\n
* v[0] v[1] v[2] v[3]
*
* For Counter configuration space:
* Short format is: offset\n
* v[0]
@ -191,14 +198,33 @@ static bool parse_line(char **line, u32 *offs, u32 *val, int short_fmt_len,
}
#if IS_ENABLED(CONFIG_USB4_DEBUGFS_WRITE)
static ssize_t regs_write(struct tb_switch *sw, struct tb_port *port,
const char __user *user_buf, size_t count,
loff_t *ppos)
/*
* Path registers need to be written in double word pairs and they both must be
* read before written. This writes one double word in patch config space
* following the spec flow.
*/
static int path_write_one(struct tb_port *port, u32 val, u32 offset)
{
u32 index = offset % PATH_LEN;
u32 offs = offset - index;
u32 data[PATH_LEN];
int ret;
ret = tb_port_read(port, data, TB_CFG_HOPS, offs, PATH_LEN);
if (ret)
return ret;
data[index] = val;
return tb_port_write(port, data, TB_CFG_HOPS, offs, PATH_LEN);
}
static ssize_t regs_write(struct tb_switch *sw, struct tb_port *port,
enum tb_cfg_space space, const char __user *user_buf,
size_t count, loff_t *ppos)
{
int long_fmt_len, ret = 0;
struct tb *tb = sw->tb;
char *line, *buf;
u32 val, offset;
int ret = 0;
buf = validate_and_copy_from_user(user_buf, &count);
if (IS_ERR(buf))
@ -214,12 +240,21 @@ static ssize_t regs_write(struct tb_switch *sw, struct tb_port *port,
/* User did hardware changes behind the driver's back */
add_taint(TAINT_USER, LOCKDEP_STILL_OK);
if (space == TB_CFG_HOPS)
long_fmt_len = 4;
else
long_fmt_len = 5;
line = buf;
while (parse_line(&line, &offset, &val, 2, 5)) {
if (port)
ret = tb_port_write(port, &val, TB_CFG_PORT, offset, 1);
else
while (parse_line(&line, &offset, &val, 2, long_fmt_len)) {
if (port) {
if (space == TB_CFG_HOPS)
ret = path_write_one(port, val, offset);
else
ret = tb_port_write(port, &val, space, offset, 1);
} else {
ret = tb_sw_write(sw, &val, TB_CFG_SWITCH, offset, 1);
}
if (ret)
break;
}
@ -240,7 +275,16 @@ static ssize_t port_regs_write(struct file *file, const char __user *user_buf,
struct seq_file *s = file->private_data;
struct tb_port *port = s->private;
return regs_write(port->sw, port, user_buf, count, ppos);
return regs_write(port->sw, port, TB_CFG_PORT, user_buf, count, ppos);
}
static ssize_t path_write(struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct seq_file *s = file->private_data;
struct tb_port *port = s->private;
return regs_write(port->sw, port, TB_CFG_HOPS, user_buf, count, ppos);
}
static ssize_t switch_regs_write(struct file *file, const char __user *user_buf,
@ -249,7 +293,7 @@ static ssize_t switch_regs_write(struct file *file, const char __user *user_buf,
struct seq_file *s = file->private_data;
struct tb_switch *sw = s->private;
return regs_write(sw, NULL, user_buf, count, ppos);
return regs_write(sw, NULL, TB_CFG_SWITCH, user_buf, count, ppos);
}
static bool parse_sb_line(char **line, u8 *reg, u8 *data, size_t data_size,
@ -401,6 +445,7 @@ out:
#define DEBUGFS_MODE 0600
#else
#define port_regs_write NULL
#define path_write NULL
#define switch_regs_write NULL
#define port_sb_regs_write NULL
#define retimer_sb_regs_write NULL
@ -2243,7 +2288,7 @@ out_rpm_put:
return ret;
}
DEBUGFS_ATTR_RO(path);
DEBUGFS_ATTR_RW(path);
static int counter_set_regs_show(struct tb_port *port, struct seq_file *s,
int counter)
@ -2368,6 +2413,8 @@ void tb_switch_debugfs_init(struct tb_switch *sw)
sw->debugfs_dir = debugfs_dir;
debugfs_create_file("regs", DEBUGFS_MODE, debugfs_dir, sw,
&switch_regs_fops);
if (sw->drom)
debugfs_create_blob("drom", 0400, debugfs_dir, &sw->drom_blob);
tb_switch_for_each_port(sw, port) {
struct dentry *debugfs_dir;

View File

@ -435,6 +435,29 @@ static int tb_drom_parse_entries(struct tb_switch *sw, size_t header_size)
return 0;
}
static int tb_switch_drom_alloc(struct tb_switch *sw, size_t size)
{
sw->drom = kzalloc(size, GFP_KERNEL);
if (!sw->drom)
return -ENOMEM;
#ifdef CONFIG_DEBUG_FS
sw->drom_blob.data = sw->drom;
sw->drom_blob.size = size;
#endif
return 0;
}
static void tb_switch_drom_free(struct tb_switch *sw)
{
#ifdef CONFIG_DEBUG_FS
sw->drom_blob.data = NULL;
sw->drom_blob.size = 0;
#endif
kfree(sw->drom);
sw->drom = NULL;
}
/*
* tb_drom_copy_efi - copy drom supplied by EFI to sw->drom if present
*/
@ -447,9 +470,9 @@ static int tb_drom_copy_efi(struct tb_switch *sw, u16 *size)
if (len < 0 || len < sizeof(struct tb_drom_header))
return -EINVAL;
sw->drom = kmalloc(len, GFP_KERNEL);
if (!sw->drom)
return -ENOMEM;
res = tb_switch_drom_alloc(sw, len);
if (res)
return res;
res = device_property_read_u8_array(dev, "ThunderboltDROM", sw->drom,
len);
@ -464,8 +487,7 @@ static int tb_drom_copy_efi(struct tb_switch *sw, u16 *size)
return 0;
err:
kfree(sw->drom);
sw->drom = NULL;
tb_switch_drom_free(sw);
return -EINVAL;
}
@ -491,13 +513,15 @@ static int tb_drom_copy_nvm(struct tb_switch *sw, u16 *size)
/* Size includes CRC8 + UID + CRC32 */
*size += 1 + 8 + 4;
sw->drom = kzalloc(*size, GFP_KERNEL);
if (!sw->drom)
return -ENOMEM;
ret = tb_switch_drom_alloc(sw, *size);
if (ret)
return ret;
ret = dma_port_flash_read(sw->dma_port, drom_offset, sw->drom, *size);
if (ret)
goto err_free;
if (ret) {
tb_switch_drom_free(sw);
return ret;
}
/*
* Read UID from the minimal DROM because the one in NVM is just
@ -505,11 +529,6 @@ static int tb_drom_copy_nvm(struct tb_switch *sw, u16 *size)
*/
tb_drom_read_uid_only(sw, &sw->uid);
return 0;
err_free:
kfree(sw->drom);
sw->drom = NULL;
return ret;
}
static int usb4_copy_drom(struct tb_switch *sw, u16 *size)
@ -522,15 +541,13 @@ static int usb4_copy_drom(struct tb_switch *sw, u16 *size)
/* Size includes CRC8 + UID + CRC32 */
*size += 1 + 8 + 4;
sw->drom = kzalloc(*size, GFP_KERNEL);
if (!sw->drom)
return -ENOMEM;
ret = tb_switch_drom_alloc(sw, *size);
if (ret)
return ret;
ret = usb4_switch_drom_read(sw, 0, sw->drom, *size);
if (ret) {
kfree(sw->drom);
sw->drom = NULL;
}
if (ret)
tb_switch_drom_free(sw);
return ret;
}
@ -552,19 +569,14 @@ static int tb_drom_bit_bang(struct tb_switch *sw, u16 *size)
return -EIO;
}
sw->drom = kzalloc(*size, GFP_KERNEL);
if (!sw->drom)
return -ENOMEM;
ret = tb_switch_drom_alloc(sw, *size);
if (ret)
return ret;
ret = tb_eeprom_read_n(sw, 0, sw->drom, *size);
if (ret)
goto err;
tb_switch_drom_free(sw);
return 0;
err:
kfree(sw->drom);
sw->drom = NULL;
return ret;
}
@ -646,9 +658,7 @@ static int tb_drom_parse(struct tb_switch *sw, u16 size)
return 0;
err:
kfree(sw->drom);
sw->drom = NULL;
tb_switch_drom_free(sw);
return ret;
}

View File

@ -581,10 +581,10 @@ int tb_path_activate(struct tb_path *path)
}
}
path->activated = true;
tb_dbg(path->tb, "path activation complete\n");
tb_dbg(path->tb, "%s path activation complete\n", path->name);
return 0;
err:
tb_WARN(path->tb, "path activation failed\n");
tb_WARN(path->tb, "%s path activation failed\n", path->name);
return res;
}

View File

@ -19,6 +19,12 @@
#define TB_TIMEOUT 100 /* ms */
#define TB_RELEASE_BW_TIMEOUT 10000 /* ms */
/*
* How many time bandwidth allocation request from graphics driver is
* retried if the DP tunnel is still activating.
*/
#define TB_BW_ALLOC_RETRIES 3
/*
* Minimum bandwidth (in Mb/s) that is needed in the single transmitter/receiver
* direction. This is 40G - 10% guard band bandwidth.
@ -69,14 +75,20 @@ static inline struct tb *tcm_to_tb(struct tb_cm *tcm)
}
struct tb_hotplug_event {
struct work_struct work;
struct delayed_work work;
struct tb *tb;
u64 route;
u8 port;
bool unplug;
int retry;
};
static void tb_scan_port(struct tb_port *port);
static void tb_handle_hotplug(struct work_struct *work);
static void tb_dp_resource_unavailable(struct tb *tb, struct tb_port *port,
const char *reason);
static void tb_queue_dp_bandwidth_request(struct tb *tb, u64 route, u8 port,
int retry, unsigned long delay);
static void tb_queue_hotplug(struct tb *tb, u64 route, u8 port, bool unplug)
{
@ -90,8 +102,8 @@ static void tb_queue_hotplug(struct tb *tb, u64 route, u8 port, bool unplug)
ev->route = route;
ev->port = port;
ev->unplug = unplug;
INIT_WORK(&ev->work, tb_handle_hotplug);
queue_work(tb->wq, &ev->work);
INIT_DELAYED_WORK(&ev->work, tb_handle_hotplug);
queue_delayed_work(tb->wq, &ev->work, 0);
}
/* enumeration & hot plug handling */
@ -961,7 +973,7 @@ static int tb_tunnel_usb3(struct tb *tb, struct tb_switch *sw)
return 0;
err_free:
tb_tunnel_free(tunnel);
tb_tunnel_put(tunnel);
err_reclaim:
if (tb_route(parent))
tb_reclaim_usb3_bandwidth(tb, down, up);
@ -1238,8 +1250,6 @@ static void tb_configure_link(struct tb_port *down, struct tb_port *up,
tb_switch_configure_link(sw);
}
static void tb_scan_port(struct tb_port *port);
/*
* tb_scan_switch() - scan for and initialize downstream switches
*/
@ -1727,7 +1737,7 @@ static void tb_deactivate_and_free_tunnel(struct tb_tunnel *tunnel)
break;
}
tb_tunnel_free(tunnel);
tb_tunnel_put(tunnel);
}
/*
@ -1864,12 +1874,76 @@ static struct tb_port *tb_find_dp_out(struct tb *tb, struct tb_port *in)
return NULL;
}
static bool tb_tunnel_one_dp(struct tb *tb, struct tb_port *in,
static void tb_dp_tunnel_active(struct tb_tunnel *tunnel, void *data)
{
struct tb_port *in = tunnel->src_port;
struct tb_port *out = tunnel->dst_port;
struct tb *tb = data;
mutex_lock(&tb->lock);
if (tb_tunnel_is_active(tunnel)) {
int consumed_up, consumed_down, ret;
tb_tunnel_dbg(tunnel, "DPRX capabilities read completed\n");
/* If fail reading tunnel's consumed bandwidth, tear it down */
ret = tb_tunnel_consumed_bandwidth(tunnel, &consumed_up,
&consumed_down);
if (ret) {
tb_tunnel_warn(tunnel,
"failed to read consumed bandwidth, tearing down\n");
tb_deactivate_and_free_tunnel(tunnel);
} else {
tb_reclaim_usb3_bandwidth(tb, in, out);
/*
* Transition the links to asymmetric if the
* consumption exceeds the threshold.
*/
tb_configure_asym(tb, in, out, consumed_up,
consumed_down);
/*
* Update the domain with the new bandwidth
* estimation.
*/
tb_recalc_estimated_bandwidth(tb);
/*
* In case of DP tunnel exists, change host
* router's 1st children TMU mode to HiFi for
* CL0s to work.
*/
tb_increase_tmu_accuracy(tunnel);
}
} else {
struct tb_port *in = tunnel->src_port;
/*
* This tunnel failed to establish. This means DPRX
* negotiation most likely did not complete which
* happens either because there is no graphics driver
* loaded or not all DP cables where connected to the
* discrete router.
*
* In both cases we remove the DP IN adapter from the
* available resources as it is not usable. This will
* also tear down the tunnel and try to re-use the
* released DP OUT.
*
* It will be added back only if there is hotplug for
* the DP IN again.
*/
tb_tunnel_warn(tunnel, "not active, tearing down\n");
tb_dp_resource_unavailable(tb, in, "DPRX negotiation failed");
}
mutex_unlock(&tb->lock);
tb_domain_put(tb);
}
static void tb_tunnel_one_dp(struct tb *tb, struct tb_port *in,
struct tb_port *out)
{
int available_up, available_down, ret, link_nr;
struct tb_cm *tcm = tb_priv(tb);
int consumed_up, consumed_down;
struct tb_tunnel *tunnel;
/*
@ -1921,47 +1995,29 @@ static bool tb_tunnel_one_dp(struct tb *tb, struct tb_port *in,
available_up, available_down);
tunnel = tb_tunnel_alloc_dp(tb, in, out, link_nr, available_up,
available_down);
available_down, tb_dp_tunnel_active,
tb_domain_get(tb));
if (!tunnel) {
tb_port_dbg(out, "could not allocate DP tunnel\n");
goto err_reclaim_usb;
}
if (tb_tunnel_activate(tunnel)) {
list_add_tail(&tunnel->list, &tcm->tunnel_list);
ret = tb_tunnel_activate(tunnel);
if (ret && ret != -EINPROGRESS) {
tb_port_info(out, "DP tunnel activation failed, aborting\n");
list_del(&tunnel->list);
goto err_free;
}
/* If fail reading tunnel's consumed bandwidth, tear it down */
ret = tb_tunnel_consumed_bandwidth(tunnel, &consumed_up, &consumed_down);
if (ret)
goto err_deactivate;
return;
list_add_tail(&tunnel->list, &tcm->tunnel_list);
tb_reclaim_usb3_bandwidth(tb, in, out);
/*
* Transition the links to asymmetric if the consumption exceeds
* the threshold.
*/
tb_configure_asym(tb, in, out, consumed_up, consumed_down);
/* Update the domain with the new bandwidth estimation */
tb_recalc_estimated_bandwidth(tb);
/*
* In case of DP tunnel exists, change host router's 1st children
* TMU mode to HiFi for CL0s to work.
*/
tb_increase_tmu_accuracy(tunnel);
return true;
err_deactivate:
tb_tunnel_deactivate(tunnel);
err_free:
tb_tunnel_free(tunnel);
tb_tunnel_put(tunnel);
err_reclaim_usb:
tb_reclaim_usb3_bandwidth(tb, in, out);
tb_domain_put(tb);
err_detach_group:
tb_detach_bandwidth_group(in);
err_dealloc_dp:
@ -1971,8 +2027,6 @@ err_rpm_put:
pm_runtime_put_autosuspend(&out->sw->dev);
pm_runtime_mark_last_busy(&in->sw->dev);
pm_runtime_put_autosuspend(&in->sw->dev);
return false;
}
static void tb_tunnel_dp(struct tb *tb)
@ -2090,17 +2144,18 @@ static void tb_switch_exit_redrive(struct tb_switch *sw)
}
}
static void tb_dp_resource_unavailable(struct tb *tb, struct tb_port *port)
static void tb_dp_resource_unavailable(struct tb *tb, struct tb_port *port,
const char *reason)
{
struct tb_port *in, *out;
struct tb_tunnel *tunnel;
if (tb_port_is_dpin(port)) {
tb_port_dbg(port, "DP IN resource unavailable\n");
tb_port_dbg(port, "DP IN resource unavailable: %s\n", reason);
in = port;
out = NULL;
} else {
tb_port_dbg(port, "DP OUT resource unavailable\n");
tb_port_dbg(port, "DP OUT resource unavailable: %s\n", reason);
in = NULL;
out = port;
}
@ -2182,7 +2237,7 @@ static int tb_disconnect_pci(struct tb *tb, struct tb_switch *sw)
tb_tunnel_deactivate(tunnel);
list_del(&tunnel->list);
tb_tunnel_free(tunnel);
tb_tunnel_put(tunnel);
return 0;
}
@ -2212,7 +2267,7 @@ static int tb_tunnel_pci(struct tb *tb, struct tb_switch *sw)
if (tb_tunnel_activate(tunnel)) {
tb_port_info(up,
"PCIe tunnel activation failed, aborting\n");
tb_tunnel_free(tunnel);
tb_tunnel_put(tunnel);
return -EIO;
}
@ -2271,7 +2326,7 @@ static int tb_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd,
return 0;
err_free:
tb_tunnel_free(tunnel);
tb_tunnel_put(tunnel);
err_clx:
tb_enable_clx(sw);
mutex_unlock(&tb->lock);
@ -2334,7 +2389,7 @@ static int tb_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd,
*/
static void tb_handle_hotplug(struct work_struct *work)
{
struct tb_hotplug_event *ev = container_of(work, typeof(*ev), work);
struct tb_hotplug_event *ev = container_of(work, typeof(*ev), work.work);
struct tb *tb = ev->tb;
struct tb_cm *tcm = tb_priv(tb);
struct tb_switch *sw;
@ -2406,7 +2461,7 @@ static void tb_handle_hotplug(struct work_struct *work)
tb_xdomain_put(xd);
tb_port_unconfigure_xdomain(port);
} else if (tb_port_is_dpout(port) || tb_port_is_dpin(port)) {
tb_dp_resource_unavailable(tb, port);
tb_dp_resource_unavailable(tb, port, "adapter unplug");
} else if (!port->port) {
tb_sw_dbg(sw, "xHCI disconnect request\n");
tb_switch_xhci_disconnect(sw);
@ -2639,7 +2694,7 @@ fail:
static void tb_handle_dp_bandwidth_request(struct work_struct *work)
{
struct tb_hotplug_event *ev = container_of(work, typeof(*ev), work);
struct tb_hotplug_event *ev = container_of(work, typeof(*ev), work.work);
int requested_bw, requested_up, requested_down, ret;
struct tb_tunnel *tunnel;
struct tb *tb = ev->tb;
@ -2666,7 +2721,7 @@ static void tb_handle_dp_bandwidth_request(struct work_struct *work)
goto put_sw;
}
tb_port_dbg(in, "handling bandwidth allocation request\n");
tb_port_dbg(in, "handling bandwidth allocation request, retry %d\n", ev->retry);
tunnel = tb_find_tunnel(tb, TB_TUNNEL_DP, in, NULL);
if (!tunnel) {
@ -2719,12 +2774,33 @@ static void tb_handle_dp_bandwidth_request(struct work_struct *work)
ret = tb_alloc_dp_bandwidth(tunnel, &requested_up, &requested_down);
if (ret) {
if (ret == -ENOBUFS)
if (ret == -ENOBUFS) {
tb_tunnel_warn(tunnel,
"not enough bandwidth available\n");
else
} else if (ret == -ENOTCONN) {
tb_tunnel_dbg(tunnel, "not active yet\n");
/*
* We got bandwidth allocation request but the
* tunnel is not yet active. This means that
* tb_dp_tunnel_active() is not yet called for
* this tunnel. Allow it some time and retry
* this request a couple of times.
*/
if (ev->retry < TB_BW_ALLOC_RETRIES) {
tb_tunnel_dbg(tunnel,
"retrying bandwidth allocation request\n");
tb_queue_dp_bandwidth_request(tb, ev->route,
ev->port,
ev->retry + 1,
msecs_to_jiffies(50));
} else {
tb_tunnel_dbg(tunnel,
"run out of retries, failing the request");
}
} else {
tb_tunnel_warn(tunnel,
"failed to change bandwidth allocation\n");
}
} else {
tb_tunnel_dbg(tunnel,
"bandwidth allocation changed to %d/%d Mb/s\n",
@ -2745,7 +2821,8 @@ unlock:
kfree(ev);
}
static void tb_queue_dp_bandwidth_request(struct tb *tb, u64 route, u8 port)
static void tb_queue_dp_bandwidth_request(struct tb *tb, u64 route, u8 port,
int retry, unsigned long delay)
{
struct tb_hotplug_event *ev;
@ -2756,8 +2833,9 @@ static void tb_queue_dp_bandwidth_request(struct tb *tb, u64 route, u8 port)
ev->tb = tb;
ev->route = route;
ev->port = port;
INIT_WORK(&ev->work, tb_handle_dp_bandwidth_request);
queue_work(tb->wq, &ev->work);
ev->retry = retry;
INIT_DELAYED_WORK(&ev->work, tb_handle_dp_bandwidth_request);
queue_delayed_work(tb->wq, &ev->work, delay);
}
static void tb_handle_notification(struct tb *tb, u64 route,
@ -2777,7 +2855,7 @@ static void tb_handle_notification(struct tb *tb, u64 route,
if (tb_cfg_ack_notification(tb->ctl, route, error))
tb_warn(tb, "could not ack notification on %llx\n",
route);
tb_queue_dp_bandwidth_request(tb, route, error->port);
tb_queue_dp_bandwidth_request(tb, route, error->port, 0, 0);
break;
default:
@ -2832,7 +2910,7 @@ static void tb_stop(struct tb *tb)
*/
if (tb_tunnel_is_dma(tunnel))
tb_tunnel_deactivate(tunnel);
tb_tunnel_free(tunnel);
tb_tunnel_put(tunnel);
}
tb_switch_remove(tb->root_switch);
tcm->hotplug_active = false; /* signal tb_handle_hotplug to quit */
@ -3028,7 +3106,7 @@ static int tb_resume_noirq(struct tb *tb)
if (tb_tunnel_is_usb3(tunnel))
usb3_delay = 500;
tb_tunnel_deactivate(tunnel);
tb_tunnel_free(tunnel);
tb_tunnel_put(tunnel);
}
/* Re-create our tunnels now */
@ -3039,7 +3117,7 @@ static int tb_resume_noirq(struct tb *tb)
/* Only need to do it once */
usb3_delay = 0;
}
tb_tunnel_restart(tunnel);
tb_tunnel_activate(tunnel);
}
if (!list_empty(&tcm->tunnel_list)) {
/*
@ -3149,7 +3227,7 @@ static int tb_runtime_resume(struct tb *tb)
tb_free_invalid_tunnels(tb);
tb_restore_children(tb->root_switch);
list_for_each_entry_safe(tunnel, n, &tcm->tunnel_list, list)
tb_tunnel_restart(tunnel);
tb_tunnel_activate(tunnel);
tb_switch_enter_redrive(tb->root_switch);
tcm->hotplug_active = true;
mutex_unlock(&tb->lock);

View File

@ -9,6 +9,7 @@
#ifndef TB_H_
#define TB_H_
#include <linux/debugfs.h>
#include <linux/nvmem-provider.h>
#include <linux/pci.h>
#include <linux/thunderbolt.h>
@ -160,6 +161,7 @@ struct tb_switch_tmu {
* @max_pcie_credits: Router preferred number of buffers for PCIe
* @max_dma_credits: Router preferred number of buffers for DMA/P2P
* @clx: CLx states on the upstream link of the router
* @drom_blob: DROM debugfs blob wrapper
*
* When the switch is being added or removed to the domain (other
* switches) you need to have domain lock held.
@ -212,6 +214,9 @@ struct tb_switch {
unsigned int max_pcie_credits;
unsigned int max_dma_credits;
unsigned int clx;
#ifdef CONFIG_DEBUG_FS
struct debugfs_blob_wrapper drom_blob;
#endif
};
/**

View File

@ -1382,8 +1382,8 @@ static void tb_test_tunnel_pcie(struct kunit *test)
KUNIT_EXPECT_PTR_EQ(test, tunnel2->paths[1]->hops[0].in_port, up);
KUNIT_EXPECT_PTR_EQ(test, tunnel2->paths[1]->hops[1].out_port, down);
tb_tunnel_free(tunnel2);
tb_tunnel_free(tunnel1);
tb_tunnel_put(tunnel2);
tb_tunnel_put(tunnel1);
}
static void tb_test_tunnel_dp(struct kunit *test)
@ -1406,7 +1406,7 @@ static void tb_test_tunnel_dp(struct kunit *test)
in = &host->ports[5];
out = &dev->ports[13];
tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0);
tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL);
KUNIT_ASSERT_NOT_NULL(test, tunnel);
KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP);
KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in);
@ -1421,7 +1421,7 @@ static void tb_test_tunnel_dp(struct kunit *test)
KUNIT_ASSERT_EQ(test, tunnel->paths[2]->path_length, 2);
KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[2]->hops[0].in_port, out);
KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[2]->hops[1].out_port, in);
tb_tunnel_free(tunnel);
tb_tunnel_put(tunnel);
}
static void tb_test_tunnel_dp_chain(struct kunit *test)
@ -1452,7 +1452,7 @@ static void tb_test_tunnel_dp_chain(struct kunit *test)
in = &host->ports[5];
out = &dev4->ports[14];
tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0);
tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL);
KUNIT_ASSERT_NOT_NULL(test, tunnel);
KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP);
KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in);
@ -1467,7 +1467,7 @@ static void tb_test_tunnel_dp_chain(struct kunit *test)
KUNIT_ASSERT_EQ(test, tunnel->paths[2]->path_length, 3);
KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[2]->hops[0].in_port, out);
KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[2]->hops[2].out_port, in);
tb_tunnel_free(tunnel);
tb_tunnel_put(tunnel);
}
static void tb_test_tunnel_dp_tree(struct kunit *test)
@ -1502,7 +1502,7 @@ static void tb_test_tunnel_dp_tree(struct kunit *test)
in = &dev2->ports[13];
out = &dev5->ports[13];
tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0);
tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL);
KUNIT_ASSERT_NOT_NULL(test, tunnel);
KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP);
KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in);
@ -1517,7 +1517,7 @@ static void tb_test_tunnel_dp_tree(struct kunit *test)
KUNIT_ASSERT_EQ(test, tunnel->paths[2]->path_length, 4);
KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[2]->hops[0].in_port, out);
KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[2]->hops[3].out_port, in);
tb_tunnel_free(tunnel);
tb_tunnel_put(tunnel);
}
static void tb_test_tunnel_dp_max_length(struct kunit *test)
@ -1567,7 +1567,7 @@ static void tb_test_tunnel_dp_max_length(struct kunit *test)
in = &dev6->ports[13];
out = &dev12->ports[13];
tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0);
tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL);
KUNIT_ASSERT_NOT_NULL(test, tunnel);
KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP);
KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in);
@ -1597,7 +1597,7 @@ static void tb_test_tunnel_dp_max_length(struct kunit *test)
KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[2]->hops[6].out_port,
&host->ports[1]);
KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[2]->hops[12].out_port, in);
tb_tunnel_free(tunnel);
tb_tunnel_put(tunnel);
}
static void tb_test_tunnel_3dp(struct kunit *test)
@ -1637,7 +1637,7 @@ static void tb_test_tunnel_3dp(struct kunit *test)
out2 = &dev5->ports[13];
out3 = &dev4->ports[14];
tunnel1 = tb_tunnel_alloc_dp(NULL, in1, out1, 1, 0, 0);
tunnel1 = tb_tunnel_alloc_dp(NULL, in1, out1, 1, 0, 0, NULL, NULL);
KUNIT_ASSERT_TRUE(test, tunnel1 != NULL);
KUNIT_EXPECT_EQ(test, tunnel1->type, TB_TUNNEL_DP);
KUNIT_EXPECT_PTR_EQ(test, tunnel1->src_port, in1);
@ -1645,7 +1645,7 @@ static void tb_test_tunnel_3dp(struct kunit *test)
KUNIT_ASSERT_EQ(test, tunnel1->npaths, 3);
KUNIT_ASSERT_EQ(test, tunnel1->paths[0]->path_length, 3);
tunnel2 = tb_tunnel_alloc_dp(NULL, in2, out2, 1, 0, 0);
tunnel2 = tb_tunnel_alloc_dp(NULL, in2, out2, 1, 0, 0, NULL, NULL);
KUNIT_ASSERT_TRUE(test, tunnel2 != NULL);
KUNIT_EXPECT_EQ(test, tunnel2->type, TB_TUNNEL_DP);
KUNIT_EXPECT_PTR_EQ(test, tunnel2->src_port, in2);
@ -1653,7 +1653,7 @@ static void tb_test_tunnel_3dp(struct kunit *test)
KUNIT_ASSERT_EQ(test, tunnel2->npaths, 3);
KUNIT_ASSERT_EQ(test, tunnel2->paths[0]->path_length, 4);
tunnel3 = tb_tunnel_alloc_dp(NULL, in3, out3, 1, 0, 0);
tunnel3 = tb_tunnel_alloc_dp(NULL, in3, out3, 1, 0, 0, NULL, NULL);
KUNIT_ASSERT_TRUE(test, tunnel3 != NULL);
KUNIT_EXPECT_EQ(test, tunnel3->type, TB_TUNNEL_DP);
KUNIT_EXPECT_PTR_EQ(test, tunnel3->src_port, in3);
@ -1661,8 +1661,8 @@ static void tb_test_tunnel_3dp(struct kunit *test)
KUNIT_ASSERT_EQ(test, tunnel3->npaths, 3);
KUNIT_ASSERT_EQ(test, tunnel3->paths[0]->path_length, 3);
tb_tunnel_free(tunnel2);
tb_tunnel_free(tunnel1);
tb_tunnel_put(tunnel2);
tb_tunnel_put(tunnel1);
}
static void tb_test_tunnel_usb3(struct kunit *test)
@ -1716,8 +1716,8 @@ static void tb_test_tunnel_usb3(struct kunit *test)
KUNIT_EXPECT_PTR_EQ(test, tunnel2->paths[1]->hops[0].in_port, up);
KUNIT_EXPECT_PTR_EQ(test, tunnel2->paths[1]->hops[1].out_port, down);
tb_tunnel_free(tunnel2);
tb_tunnel_free(tunnel1);
tb_tunnel_put(tunnel2);
tb_tunnel_put(tunnel1);
}
static void tb_test_tunnel_port_on_path(struct kunit *test)
@ -1750,7 +1750,7 @@ static void tb_test_tunnel_port_on_path(struct kunit *test)
in = &dev2->ports[13];
out = &dev5->ports[13];
dp_tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0);
dp_tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL);
KUNIT_ASSERT_NOT_NULL(test, dp_tunnel);
KUNIT_EXPECT_TRUE(test, tb_tunnel_port_on_path(dp_tunnel, in));
@ -1783,7 +1783,7 @@ static void tb_test_tunnel_port_on_path(struct kunit *test)
port = &dev4->ports[1];
KUNIT_EXPECT_FALSE(test, tb_tunnel_port_on_path(dp_tunnel, port));
tb_tunnel_free(dp_tunnel);
tb_tunnel_put(dp_tunnel);
}
static void tb_test_tunnel_dma(struct kunit *test)
@ -1826,7 +1826,7 @@ static void tb_test_tunnel_dma(struct kunit *test)
KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[0].out_port, port);
KUNIT_EXPECT_EQ(test, tunnel->paths[1]->hops[0].next_hop_index, 8);
tb_tunnel_free(tunnel);
tb_tunnel_put(tunnel);
}
static void tb_test_tunnel_dma_rx(struct kunit *test)
@ -1863,7 +1863,7 @@ static void tb_test_tunnel_dma_rx(struct kunit *test)
KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[0].out_port, nhi);
KUNIT_EXPECT_EQ(test, tunnel->paths[0]->hops[0].next_hop_index, 2);
tb_tunnel_free(tunnel);
tb_tunnel_put(tunnel);
}
static void tb_test_tunnel_dma_tx(struct kunit *test)
@ -1900,7 +1900,7 @@ static void tb_test_tunnel_dma_tx(struct kunit *test)
KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[0].out_port, port);
KUNIT_EXPECT_EQ(test, tunnel->paths[0]->hops[0].next_hop_index, 15);
tb_tunnel_free(tunnel);
tb_tunnel_put(tunnel);
}
static void tb_test_tunnel_dma_chain(struct kunit *test)
@ -1966,7 +1966,7 @@ static void tb_test_tunnel_dma_chain(struct kunit *test)
KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[2].out_port, port);
KUNIT_EXPECT_EQ(test, tunnel->paths[1]->hops[2].next_hop_index, 8);
tb_tunnel_free(tunnel);
tb_tunnel_put(tunnel);
}
static void tb_test_tunnel_dma_match(struct kunit *test)
@ -1993,7 +1993,7 @@ static void tb_test_tunnel_dma_match(struct kunit *test)
KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, -1, -1, -1, -1));
KUNIT_ASSERT_FALSE(test, tb_tunnel_match_dma(tunnel, 8, -1, 8, -1));
tb_tunnel_free(tunnel);
tb_tunnel_put(tunnel);
tunnel = tb_tunnel_alloc_dma(NULL, nhi, port, 15, 1, -1, -1);
KUNIT_ASSERT_NOT_NULL(test, tunnel);
@ -2005,7 +2005,7 @@ static void tb_test_tunnel_dma_match(struct kunit *test)
KUNIT_ASSERT_FALSE(test, tb_tunnel_match_dma(tunnel, -1, -1, 15, 1));
KUNIT_ASSERT_FALSE(test, tb_tunnel_match_dma(tunnel, 15, 11, -1, -1));
tb_tunnel_free(tunnel);
tb_tunnel_put(tunnel);
tunnel = tb_tunnel_alloc_dma(NULL, nhi, port, -1, -1, 15, 11);
KUNIT_ASSERT_NOT_NULL(test, tunnel);
@ -2017,7 +2017,7 @@ static void tb_test_tunnel_dma_match(struct kunit *test)
KUNIT_ASSERT_FALSE(test, tb_tunnel_match_dma(tunnel, -1, -1, 10, 11));
KUNIT_ASSERT_FALSE(test, tb_tunnel_match_dma(tunnel, 15, 11, -1, -1));
tb_tunnel_free(tunnel);
tb_tunnel_put(tunnel);
}
static void tb_test_credit_alloc_legacy_not_bonded(struct kunit *test)
@ -2050,7 +2050,7 @@ static void tb_test_credit_alloc_legacy_not_bonded(struct kunit *test)
KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 16U);
tb_tunnel_free(tunnel);
tb_tunnel_put(tunnel);
}
static void tb_test_credit_alloc_legacy_bonded(struct kunit *test)
@ -2083,7 +2083,7 @@ static void tb_test_credit_alloc_legacy_bonded(struct kunit *test)
KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 32U);
tb_tunnel_free(tunnel);
tb_tunnel_put(tunnel);
}
static void tb_test_credit_alloc_pcie(struct kunit *test)
@ -2116,7 +2116,7 @@ static void tb_test_credit_alloc_pcie(struct kunit *test)
KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 64U);
tb_tunnel_free(tunnel);
tb_tunnel_put(tunnel);
}
static void tb_test_credit_alloc_without_dp(struct kunit *test)
@ -2166,7 +2166,7 @@ static void tb_test_credit_alloc_without_dp(struct kunit *test)
KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 64U);
tb_tunnel_free(tunnel);
tb_tunnel_put(tunnel);
}
static void tb_test_credit_alloc_dp(struct kunit *test)
@ -2182,7 +2182,7 @@ static void tb_test_credit_alloc_dp(struct kunit *test)
in = &host->ports[5];
out = &dev->ports[14];
tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0);
tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL);
KUNIT_ASSERT_NOT_NULL(test, tunnel);
KUNIT_ASSERT_EQ(test, tunnel->npaths, (size_t)3);
@ -2210,7 +2210,7 @@ static void tb_test_credit_alloc_dp(struct kunit *test)
KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 1U);
tb_tunnel_free(tunnel);
tb_tunnel_put(tunnel);
}
static void tb_test_credit_alloc_usb3(struct kunit *test)
@ -2243,7 +2243,7 @@ static void tb_test_credit_alloc_usb3(struct kunit *test)
KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 32U);
tb_tunnel_free(tunnel);
tb_tunnel_put(tunnel);
}
static void tb_test_credit_alloc_dma(struct kunit *test)
@ -2279,7 +2279,7 @@ static void tb_test_credit_alloc_dma(struct kunit *test)
KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 14U);
tb_tunnel_free(tunnel);
tb_tunnel_put(tunnel);
}
static void tb_test_credit_alloc_dma_multiple(struct kunit *test)
@ -2356,7 +2356,7 @@ static void tb_test_credit_alloc_dma_multiple(struct kunit *test)
* Release the first DMA tunnel. That should make 14 buffers
* available for the next tunnel.
*/
tb_tunnel_free(tunnel1);
tb_tunnel_put(tunnel1);
tunnel3 = tb_tunnel_alloc_dma(NULL, nhi, port, 10, 3, 10, 3);
KUNIT_ASSERT_NOT_NULL(test, tunnel3);
@ -2375,8 +2375,8 @@ static void tb_test_credit_alloc_dma_multiple(struct kunit *test)
KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 14U);
tb_tunnel_free(tunnel3);
tb_tunnel_free(tunnel2);
tb_tunnel_put(tunnel3);
tb_tunnel_put(tunnel2);
}
static struct tb_tunnel *TB_TEST_PCIE_TUNNEL(struct kunit *test,
@ -2418,7 +2418,7 @@ static struct tb_tunnel *TB_TEST_DP_TUNNEL1(struct kunit *test,
in = &host->ports[5];
out = &dev->ports[13];
dp_tunnel1 = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0);
dp_tunnel1 = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL);
KUNIT_ASSERT_NOT_NULL(test, dp_tunnel1);
KUNIT_ASSERT_EQ(test, dp_tunnel1->npaths, (size_t)3);
@ -2455,7 +2455,7 @@ static struct tb_tunnel *TB_TEST_DP_TUNNEL2(struct kunit *test,
in = &host->ports[6];
out = &dev->ports[14];
dp_tunnel2 = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0);
dp_tunnel2 = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL);
KUNIT_ASSERT_NOT_NULL(test, dp_tunnel2);
KUNIT_ASSERT_EQ(test, dp_tunnel2->npaths, (size_t)3);
@ -2595,12 +2595,12 @@ static void tb_test_credit_alloc_all(struct kunit *test)
dma_tunnel1 = TB_TEST_DMA_TUNNEL1(test, host, dev);
dma_tunnel2 = TB_TEST_DMA_TUNNEL2(test, host, dev);
tb_tunnel_free(dma_tunnel2);
tb_tunnel_free(dma_tunnel1);
tb_tunnel_free(usb3_tunnel);
tb_tunnel_free(dp_tunnel2);
tb_tunnel_free(dp_tunnel1);
tb_tunnel_free(pcie_tunnel);
tb_tunnel_put(dma_tunnel2);
tb_tunnel_put(dma_tunnel1);
tb_tunnel_put(usb3_tunnel);
tb_tunnel_put(dp_tunnel2);
tb_tunnel_put(dp_tunnel1);
tb_tunnel_put(pcie_tunnel);
}
static const u32 root_directory[] = {

View File

@ -70,6 +70,24 @@
#define USB4_V2_PCI_MIN_BANDWIDTH (1500 * TB_PCI_WEIGHT)
#define USB4_V2_USB3_MIN_BANDWIDTH (1500 * TB_USB3_WEIGHT)
/*
* According to VESA spec, the DPRX negotiation shall compete in 5
* seconds after tunnel is established. Since at least i915 can runtime
* suspend if there is nothing connected, and that it polls any new
* connections every 10 seconds, we use 12 seconds here.
*
* These are in ms.
*/
#define TB_DPRX_TIMEOUT 12000
#define TB_DPRX_WAIT_TIMEOUT 25
#define TB_DPRX_POLL_DELAY 50
static int dprx_timeout = TB_DPRX_TIMEOUT;
module_param(dprx_timeout, int, 0444);
MODULE_PARM_DESC(dprx_timeout,
"DPRX capability read timeout in ms, -1 waits forever (default: "
__MODULE_STRING(TB_DPRX_TIMEOUT) ")");
static unsigned int dma_credits = TB_DMA_CREDITS;
module_param(dma_credits, uint, 0444);
MODULE_PARM_DESC(dma_credits, "specify custom credits for DMA tunnels (default: "
@ -82,6 +100,9 @@ MODULE_PARM_DESC(bw_alloc_mode,
static const char * const tb_tunnel_names[] = { "PCI", "DP", "DMA", "USB3" };
/* Synchronizes kref_get()/put() of struct tb_tunnel */
static DEFINE_MUTEX(tb_tunnel_lock);
static inline unsigned int tb_usable_credits(const struct tb_port *port)
{
return port->total_credits - port->ctl_credits;
@ -155,7 +176,7 @@ static struct tb_tunnel *tb_tunnel_alloc(struct tb *tb, size_t npaths,
tunnel->paths = kcalloc(npaths, sizeof(tunnel->paths[0]), GFP_KERNEL);
if (!tunnel->paths) {
tb_tunnel_free(tunnel);
kfree(tunnel);
return NULL;
}
@ -163,10 +184,42 @@ static struct tb_tunnel *tb_tunnel_alloc(struct tb *tb, size_t npaths,
tunnel->tb = tb;
tunnel->npaths = npaths;
tunnel->type = type;
kref_init(&tunnel->kref);
return tunnel;
}
static void tb_tunnel_get(struct tb_tunnel *tunnel)
{
mutex_lock(&tb_tunnel_lock);
kref_get(&tunnel->kref);
mutex_unlock(&tb_tunnel_lock);
}
static void tb_tunnel_destroy(struct kref *kref)
{
struct tb_tunnel *tunnel = container_of(kref, typeof(*tunnel), kref);
int i;
if (tunnel->destroy)
tunnel->destroy(tunnel);
for (i = 0; i < tunnel->npaths; i++) {
if (tunnel->paths[i])
tb_path_free(tunnel->paths[i]);
}
kfree(tunnel->paths);
kfree(tunnel);
}
void tb_tunnel_put(struct tb_tunnel *tunnel)
{
mutex_lock(&tb_tunnel_lock);
kref_put(&tunnel->kref, tb_tunnel_destroy);
mutex_unlock(&tb_tunnel_lock);
}
static int tb_pci_set_ext_encapsulation(struct tb_tunnel *tunnel, bool enable)
{
struct tb_port *port = tb_upstream_port(tunnel->dst_port->sw);
@ -355,7 +408,7 @@ struct tb_tunnel *tb_tunnel_discover_pci(struct tb *tb, struct tb_port *down,
err_deactivate:
tb_tunnel_deactivate(tunnel);
err_free:
tb_tunnel_free(tunnel);
tb_tunnel_put(tunnel);
return NULL;
}
@ -404,7 +457,7 @@ struct tb_tunnel *tb_tunnel_alloc_pci(struct tb *tb, struct tb_port *up,
return tunnel;
err_free:
tb_tunnel_free(tunnel);
tb_tunnel_put(tunnel);
return NULL;
}
@ -851,7 +904,7 @@ static int tb_dp_bandwidth_alloc_mode_enable(struct tb_tunnel *tunnel)
return 0;
}
static int tb_dp_init(struct tb_tunnel *tunnel)
static int tb_dp_pre_activate(struct tb_tunnel *tunnel)
{
struct tb_port *in = tunnel->src_port;
struct tb_switch *sw = in->sw;
@ -877,7 +930,7 @@ static int tb_dp_init(struct tb_tunnel *tunnel)
return tb_dp_bandwidth_alloc_mode_enable(tunnel);
}
static void tb_dp_deinit(struct tb_tunnel *tunnel)
static void tb_dp_post_deactivate(struct tb_tunnel *tunnel)
{
struct tb_port *in = tunnel->src_port;
@ -889,6 +942,90 @@ static void tb_dp_deinit(struct tb_tunnel *tunnel)
}
}
static ktime_t dprx_timeout_to_ktime(int timeout_msec)
{
return timeout_msec >= 0 ?
ktime_add_ms(ktime_get(), timeout_msec) : KTIME_MAX;
}
static int tb_dp_wait_dprx(struct tb_tunnel *tunnel, int timeout_msec)
{
ktime_t timeout = dprx_timeout_to_ktime(timeout_msec);
struct tb_port *in = tunnel->src_port;
/*
* Wait for DPRX done. Normally it should be already set for
* active tunnel.
*/
do {
u32 val;
int ret;
ret = tb_port_read(in, &val, TB_CFG_PORT,
in->cap_adap + DP_COMMON_CAP, 1);
if (ret)
return ret;
if (val & DP_COMMON_CAP_DPRX_DONE)
return 0;
usleep_range(100, 150);
} while (ktime_before(ktime_get(), timeout));
tb_tunnel_dbg(tunnel, "DPRX read timeout\n");
return -ETIMEDOUT;
}
static void tb_dp_dprx_work(struct work_struct *work)
{
struct tb_tunnel *tunnel = container_of(work, typeof(*tunnel), dprx_work.work);
struct tb *tb = tunnel->tb;
if (!tunnel->dprx_canceled) {
mutex_lock(&tb->lock);
if (tb_dp_is_usb4(tunnel->src_port->sw) &&
tb_dp_wait_dprx(tunnel, TB_DPRX_WAIT_TIMEOUT)) {
if (ktime_before(ktime_get(), tunnel->dprx_timeout)) {
queue_delayed_work(tb->wq, &tunnel->dprx_work,
msecs_to_jiffies(TB_DPRX_POLL_DELAY));
mutex_unlock(&tb->lock);
return;
}
} else {
tunnel->state = TB_TUNNEL_ACTIVE;
}
mutex_unlock(&tb->lock);
}
if (tunnel->callback)
tunnel->callback(tunnel, tunnel->callback_data);
}
static int tb_dp_dprx_start(struct tb_tunnel *tunnel)
{
/*
* Bump up the reference to keep the tunnel around. It will be
* dropped in tb_dp_dprx_stop() once the tunnel is deactivated.
*/
tb_tunnel_get(tunnel);
if (tunnel->callback) {
tunnel->dprx_timeout = dprx_timeout_to_ktime(dprx_timeout);
queue_delayed_work(tunnel->tb->wq, &tunnel->dprx_work, 0);
return -EINPROGRESS;
}
return tb_dp_is_usb4(tunnel->src_port->sw) ?
tb_dp_wait_dprx(tunnel, dprx_timeout) : 0;
}
static void tb_dp_dprx_stop(struct tb_tunnel *tunnel)
{
tunnel->dprx_canceled = true;
cancel_delayed_work(&tunnel->dprx_work);
tb_tunnel_put(tunnel);
}
static int tb_dp_activate(struct tb_tunnel *tunnel, bool active)
{
int ret;
@ -910,6 +1047,7 @@ static int tb_dp_activate(struct tb_tunnel *tunnel, bool active)
paths[TB_DP_AUX_PATH_IN]->hops[0].in_hop_index,
paths[TB_DP_AUX_PATH_OUT]->hops[last].next_hop_index);
} else {
tb_dp_dprx_stop(tunnel);
tb_dp_port_hpd_clear(tunnel->src_port);
tb_dp_port_set_hops(tunnel->src_port, 0, 0, 0);
if (tb_port_is_dpout(tunnel->dst_port))
@ -920,10 +1058,13 @@ static int tb_dp_activate(struct tb_tunnel *tunnel, bool active)
if (ret)
return ret;
if (tb_port_is_dpout(tunnel->dst_port))
return tb_dp_port_enable(tunnel->dst_port, active);
if (tb_port_is_dpout(tunnel->dst_port)) {
ret = tb_dp_port_enable(tunnel->dst_port, active);
if (ret)
return ret;
}
return 0;
return active ? tb_dp_dprx_start(tunnel) : 0;
}
/**
@ -1076,35 +1217,6 @@ static int tb_dp_alloc_bandwidth(struct tb_tunnel *tunnel, int *alloc_up,
return 0;
}
static int tb_dp_wait_dprx(struct tb_tunnel *tunnel, int timeout_msec)
{
ktime_t timeout = ktime_add_ms(ktime_get(), timeout_msec);
struct tb_port *in = tunnel->src_port;
/*
* Wait for DPRX done. Normally it should be already set for
* active tunnel.
*/
do {
u32 val;
int ret;
ret = tb_port_read(in, &val, TB_CFG_PORT,
in->cap_adap + DP_COMMON_CAP, 1);
if (ret)
return ret;
if (val & DP_COMMON_CAP_DPRX_DONE) {
tb_tunnel_dbg(tunnel, "DPRX read done\n");
return 0;
}
usleep_range(100, 150);
} while (ktime_before(ktime_get(), timeout));
tb_tunnel_dbg(tunnel, "DPRX read timeout\n");
return -ETIMEDOUT;
}
/* Read cap from tunnel DP IN */
static int tb_dp_read_cap(struct tb_tunnel *tunnel, unsigned int cap, u32 *rate,
u32 *lanes)
@ -1168,32 +1280,39 @@ static int tb_dp_consumed_bandwidth(struct tb_tunnel *tunnel, int *consumed_up,
int ret;
if (tb_dp_is_usb4(sw)) {
/*
* On USB4 routers check if the bandwidth allocation
* mode is enabled first and then read the bandwidth
* through those registers.
*/
ret = tb_dp_bandwidth_mode_consumed_bandwidth(tunnel, consumed_up,
consumed_down);
if (ret < 0) {
if (ret != -EOPNOTSUPP)
ret = tb_dp_wait_dprx(tunnel, 0);
if (ret) {
if (ret == -ETIMEDOUT) {
/*
* While we wait for DPRX complete the
* tunnel consumes as much as it had
* been reserved initially.
*/
ret = tb_dp_read_cap(tunnel, DP_REMOTE_CAP,
&rate, &lanes);
if (ret)
return ret;
} else {
return ret;
}
} else {
/*
* On USB4 routers check if the bandwidth allocation
* mode is enabled first and then read the bandwidth
* through those registers.
*/
ret = tb_dp_bandwidth_mode_consumed_bandwidth(tunnel, consumed_up,
consumed_down);
if (ret < 0) {
if (ret != -EOPNOTSUPP)
return ret;
} else if (!ret) {
return 0;
}
ret = tb_dp_read_cap(tunnel, DP_COMMON_CAP, &rate, &lanes);
if (ret)
return ret;
} else if (!ret) {
return 0;
}
/*
* Then see if the DPRX negotiation is ready and if yes
* return that bandwidth (it may be smaller than the
* reduced one). According to VESA spec, the DPRX
* negotiation shall compete in 5 seconds after tunnel
* established. We give it 100ms extra just in case.
*/
ret = tb_dp_wait_dprx(tunnel, 5100);
if (ret)
return ret;
ret = tb_dp_read_cap(tunnel, DP_COMMON_CAP, &rate, &lanes);
if (ret)
return ret;
} else if (sw->generation >= 2) {
ret = tb_dp_read_cap(tunnel, DP_REMOTE_CAP, &rate, &lanes);
if (ret)
@ -1365,9 +1484,9 @@ struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in,
if (!tunnel)
return NULL;
tunnel->init = tb_dp_init;
tunnel->deinit = tb_dp_deinit;
tunnel->pre_activate = tb_dp_pre_activate;
tunnel->activate = tb_dp_activate;
tunnel->post_deactivate = tb_dp_post_deactivate;
tunnel->maximum_bandwidth = tb_dp_maximum_bandwidth;
tunnel->allocated_bandwidth = tb_dp_allocated_bandwidth;
tunnel->alloc_bandwidth = tb_dp_alloc_bandwidth;
@ -1424,7 +1543,7 @@ struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in,
err_deactivate:
tb_tunnel_deactivate(tunnel);
err_free:
tb_tunnel_free(tunnel);
tb_tunnel_put(tunnel);
return NULL;
}
@ -1439,15 +1558,24 @@ err_free:
* %0 if no available bandwidth.
* @max_down: Maximum available downstream bandwidth for the DP tunnel.
* %0 if no available bandwidth.
* @callback: Optional callback that is called when the DP tunnel is
* fully activated (or there is an error)
* @callback_data: Optional data for @callback
*
* Allocates a tunnel between @in and @out that is capable of tunneling
* Display Port traffic.
* Display Port traffic. If @callback is not %NULL it will be called
* after tb_tunnel_activate() once the tunnel has been fully activated.
* It can call tb_tunnel_is_active() to check if activation was
* successful (or if it returns %false there was some sort of issue).
* The @callback is called without @tb->lock held.
*
* Return: Returns a tb_tunnel on success or NULL on failure.
* Return: Returns a tb_tunnel on success or &NULL on failure.
*/
struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in,
struct tb_port *out, int link_nr,
int max_up, int max_down)
int max_up, int max_down,
void (*callback)(struct tb_tunnel *, void *),
void *callback_data)
{
struct tb_tunnel *tunnel;
struct tb_path **paths;
@ -1461,9 +1589,9 @@ struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in,
if (!tunnel)
return NULL;
tunnel->init = tb_dp_init;
tunnel->deinit = tb_dp_deinit;
tunnel->pre_activate = tb_dp_pre_activate;
tunnel->activate = tb_dp_activate;
tunnel->post_deactivate = tb_dp_post_deactivate;
tunnel->maximum_bandwidth = tb_dp_maximum_bandwidth;
tunnel->allocated_bandwidth = tb_dp_allocated_bandwidth;
tunnel->alloc_bandwidth = tb_dp_alloc_bandwidth;
@ -1472,6 +1600,9 @@ struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in,
tunnel->dst_port = out;
tunnel->max_up = max_up;
tunnel->max_down = max_down;
tunnel->callback = callback;
tunnel->callback_data = callback_data;
INIT_DELAYED_WORK(&tunnel->dprx_work, tb_dp_dprx_work);
paths = tunnel->paths;
pm_support = usb4_switch_version(in->sw) >= 2;
@ -1500,7 +1631,7 @@ struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in,
return tunnel;
err_free:
tb_tunnel_free(tunnel);
tb_tunnel_put(tunnel);
return NULL;
}
@ -1620,7 +1751,7 @@ static void tb_dma_release_credits(struct tb_path_hop *hop)
}
}
static void tb_dma_deinit_path(struct tb_path *path)
static void tb_dma_destroy_path(struct tb_path *path)
{
struct tb_path_hop *hop;
@ -1628,14 +1759,14 @@ static void tb_dma_deinit_path(struct tb_path *path)
tb_dma_release_credits(hop);
}
static void tb_dma_deinit(struct tb_tunnel *tunnel)
static void tb_dma_destroy(struct tb_tunnel *tunnel)
{
int i;
for (i = 0; i < tunnel->npaths; i++) {
if (!tunnel->paths[i])
continue;
tb_dma_deinit_path(tunnel->paths[i]);
tb_dma_destroy_path(tunnel->paths[i]);
}
}
@ -1681,7 +1812,7 @@ struct tb_tunnel *tb_tunnel_alloc_dma(struct tb *tb, struct tb_port *nhi,
tunnel->src_port = nhi;
tunnel->dst_port = dst;
tunnel->deinit = tb_dma_deinit;
tunnel->destroy = tb_dma_destroy;
credits = min_not_zero(dma_credits, nhi->sw->max_dma_credits);
@ -1712,7 +1843,7 @@ struct tb_tunnel *tb_tunnel_alloc_dma(struct tb *tb, struct tb_port *nhi,
return tunnel;
err_free:
tb_tunnel_free(tunnel);
tb_tunnel_put(tunnel);
return NULL;
}
@ -1793,7 +1924,7 @@ static int tb_usb3_max_link_rate(struct tb_port *up, struct tb_port *down)
return min(up_max_rate, down_max_rate);
}
static int tb_usb3_init(struct tb_tunnel *tunnel)
static int tb_usb3_pre_activate(struct tb_tunnel *tunnel)
{
tb_tunnel_dbg(tunnel, "allocating initial bandwidth %d/%d Mb/s\n",
tunnel->allocated_up, tunnel->allocated_down);
@ -2024,7 +2155,7 @@ struct tb_tunnel *tb_tunnel_discover_usb3(struct tb *tb, struct tb_port *down,
tb_tunnel_dbg(tunnel, "currently allocated bandwidth %d/%d Mb/s\n",
tunnel->allocated_up, tunnel->allocated_down);
tunnel->init = tb_usb3_init;
tunnel->pre_activate = tb_usb3_pre_activate;
tunnel->consumed_bandwidth = tb_usb3_consumed_bandwidth;
tunnel->release_unused_bandwidth =
tb_usb3_release_unused_bandwidth;
@ -2038,7 +2169,7 @@ struct tb_tunnel *tb_tunnel_discover_usb3(struct tb *tb, struct tb_port *down,
err_deactivate:
tb_tunnel_deactivate(tunnel);
err_free:
tb_tunnel_free(tunnel);
tb_tunnel_put(tunnel);
return NULL;
}
@ -2094,7 +2225,7 @@ struct tb_tunnel *tb_tunnel_alloc_usb3(struct tb *tb, struct tb_port *up,
path = tb_path_alloc(tb, down, TB_USB3_HOPID, up, TB_USB3_HOPID, 0,
"USB3 Down");
if (!path) {
tb_tunnel_free(tunnel);
tb_tunnel_put(tunnel);
return NULL;
}
tb_usb3_init_path(path);
@ -2103,7 +2234,7 @@ struct tb_tunnel *tb_tunnel_alloc_usb3(struct tb *tb, struct tb_port *up,
path = tb_path_alloc(tb, up, TB_USB3_HOPID, down, TB_USB3_HOPID, 0,
"USB3 Up");
if (!path) {
tb_tunnel_free(tunnel);
tb_tunnel_put(tunnel);
return NULL;
}
tb_usb3_init_path(path);
@ -2113,7 +2244,7 @@ struct tb_tunnel *tb_tunnel_alloc_usb3(struct tb *tb, struct tb_port *up,
tunnel->allocated_up = min(max_rate, max_up);
tunnel->allocated_down = min(max_rate, max_down);
tunnel->init = tb_usb3_init;
tunnel->pre_activate = tb_usb3_pre_activate;
tunnel->consumed_bandwidth = tb_usb3_consumed_bandwidth;
tunnel->release_unused_bandwidth =
tb_usb3_release_unused_bandwidth;
@ -2124,31 +2255,6 @@ struct tb_tunnel *tb_tunnel_alloc_usb3(struct tb *tb, struct tb_port *up,
return tunnel;
}
/**
* tb_tunnel_free() - free a tunnel
* @tunnel: Tunnel to be freed
*
* Frees a tunnel. The tunnel does not need to be deactivated.
*/
void tb_tunnel_free(struct tb_tunnel *tunnel)
{
int i;
if (!tunnel)
return;
if (tunnel->deinit)
tunnel->deinit(tunnel);
for (i = 0; i < tunnel->npaths; i++) {
if (tunnel->paths[i])
tb_path_free(tunnel->paths[i]);
}
kfree(tunnel->paths);
kfree(tunnel);
}
/**
* tb_tunnel_is_invalid - check whether an activated path is still valid
* @tunnel: Tunnel to check
@ -2167,12 +2273,15 @@ bool tb_tunnel_is_invalid(struct tb_tunnel *tunnel)
}
/**
* tb_tunnel_restart() - activate a tunnel after a hardware reset
* @tunnel: Tunnel to restart
* tb_tunnel_activate() - activate a tunnel
* @tunnel: Tunnel to activate
*
* Return: 0 on success and negative errno in case if failure
* Return: 0 on success and negative errno in case if failure.
* Specifically returns %-EINPROGRESS if the tunnel activation is still
* in progress (that's for DP tunnels to complete DPRX capabilities
* read).
*/
int tb_tunnel_restart(struct tb_tunnel *tunnel)
int tb_tunnel_activate(struct tb_tunnel *tunnel)
{
int res, i;
@ -2189,8 +2298,10 @@ int tb_tunnel_restart(struct tb_tunnel *tunnel)
}
}
if (tunnel->init) {
res = tunnel->init(tunnel);
tunnel->state = TB_TUNNEL_ACTIVATING;
if (tunnel->pre_activate) {
res = tunnel->pre_activate(tunnel);
if (res)
return res;
}
@ -2203,10 +2314,14 @@ int tb_tunnel_restart(struct tb_tunnel *tunnel)
if (tunnel->activate) {
res = tunnel->activate(tunnel, true);
if (res)
if (res) {
if (res == -EINPROGRESS)
return res;
goto err;
}
}
tunnel->state = TB_TUNNEL_ACTIVE;
return 0;
err:
@ -2215,27 +2330,6 @@ err:
return res;
}
/**
* tb_tunnel_activate() - activate a tunnel
* @tunnel: Tunnel to activate
*
* Return: Returns 0 on success or an error code on failure.
*/
int tb_tunnel_activate(struct tb_tunnel *tunnel)
{
int i;
for (i = 0; i < tunnel->npaths; i++) {
if (tunnel->paths[i]->activated) {
tb_tunnel_WARN(tunnel,
"trying to activate an already activated tunnel\n");
return -EINVAL;
}
}
return tb_tunnel_restart(tunnel);
}
/**
* tb_tunnel_deactivate() - deactivate a tunnel
* @tunnel: Tunnel to deactivate
@ -2253,6 +2347,11 @@ void tb_tunnel_deactivate(struct tb_tunnel *tunnel)
if (tunnel->paths[i] && tunnel->paths[i]->activated)
tb_path_deactivate(tunnel->paths[i]);
}
if (tunnel->post_deactivate)
tunnel->post_deactivate(tunnel);
tunnel->state = TB_TUNNEL_INACTIVE;
}
/**
@ -2279,18 +2378,10 @@ bool tb_tunnel_port_on_path(const struct tb_tunnel *tunnel,
return false;
}
static bool tb_tunnel_is_active(const struct tb_tunnel *tunnel)
// Is tb_tunnel_activate() called for the tunnel
static bool tb_tunnel_is_activated(const struct tb_tunnel *tunnel)
{
int i;
for (i = 0; i < tunnel->npaths; i++) {
if (!tunnel->paths[i])
return false;
if (!tunnel->paths[i]->activated)
return false;
}
return true;
return tunnel->state == TB_TUNNEL_ACTIVATING || tb_tunnel_is_active(tunnel);
}
/**
@ -2307,7 +2398,7 @@ int tb_tunnel_maximum_bandwidth(struct tb_tunnel *tunnel, int *max_up,
int *max_down)
{
if (!tb_tunnel_is_active(tunnel))
return -EINVAL;
return -ENOTCONN;
if (tunnel->maximum_bandwidth)
return tunnel->maximum_bandwidth(tunnel, max_up, max_down);
@ -2328,7 +2419,7 @@ int tb_tunnel_allocated_bandwidth(struct tb_tunnel *tunnel, int *allocated_up,
int *allocated_down)
{
if (!tb_tunnel_is_active(tunnel))
return -EINVAL;
return -ENOTCONN;
if (tunnel->allocated_bandwidth)
return tunnel->allocated_bandwidth(tunnel, allocated_up,
@ -2351,7 +2442,7 @@ int tb_tunnel_alloc_bandwidth(struct tb_tunnel *tunnel, int *alloc_up,
int *alloc_down)
{
if (!tb_tunnel_is_active(tunnel))
return -EINVAL;
return -ENOTCONN;
if (tunnel->alloc_bandwidth)
return tunnel->alloc_bandwidth(tunnel, alloc_up, alloc_down);
@ -2376,26 +2467,27 @@ int tb_tunnel_consumed_bandwidth(struct tb_tunnel *tunnel, int *consumed_up,
{
int up_bw = 0, down_bw = 0;
if (!tb_tunnel_is_active(tunnel))
goto out;
if (tunnel->consumed_bandwidth) {
/*
* Here we need to distinguish between not active tunnel from
* tunnels that are either fully active or activation started.
* The latter is true for DP tunnels where we must report the
* consumed to be the maximum we gave it until DPRX capabilities
* read is done by the graphics driver.
*/
if (tb_tunnel_is_activated(tunnel) && tunnel->consumed_bandwidth) {
int ret;
ret = tunnel->consumed_bandwidth(tunnel, &up_bw, &down_bw);
if (ret)
return ret;
tb_tunnel_dbg(tunnel, "consumed bandwidth %d/%d Mb/s\n", up_bw,
down_bw);
}
out:
if (consumed_up)
*consumed_up = up_bw;
if (consumed_down)
*consumed_down = down_bw;
tb_tunnel_dbg(tunnel, "consumed bandwidth %d/%d Mb/s\n", up_bw, down_bw);
return 0;
}
@ -2411,7 +2503,7 @@ out:
int tb_tunnel_release_unused_bandwidth(struct tb_tunnel *tunnel)
{
if (!tb_tunnel_is_active(tunnel))
return 0;
return -ENOTCONN;
if (tunnel->release_unused_bandwidth) {
int ret;

View File

@ -18,17 +18,34 @@ enum tb_tunnel_type {
TB_TUNNEL_USB3,
};
/**
* enum tb_tunnel_state - State of a tunnel
* @TB_TUNNEL_INACTIVE: tb_tunnel_activate() is not called for the tunnel
* @TB_TUNNEL_ACTIVATING: tb_tunnel_activate() returned successfully for the tunnel
* @TB_TUNNEL_ACTIVE: The tunnel is fully active
*/
enum tb_tunnel_state {
TB_TUNNEL_INACTIVE,
TB_TUNNEL_ACTIVATING,
TB_TUNNEL_ACTIVE,
};
/**
* struct tb_tunnel - Tunnel between two ports
* @kref: Reference count
* @tb: Pointer to the domain
* @src_port: Source port of the tunnel
* @dst_port: Destination port of the tunnel. For discovered incomplete
* tunnels may be %NULL or null adapter port instead.
* @paths: All paths required by the tunnel
* @npaths: Number of paths in @paths
* @init: Optional tunnel specific initialization
* @deinit: Optional tunnel specific de-initialization
* @pre_activate: Optional tunnel specific initialization called before
* activation. Can touch hardware.
* @activate: Optional tunnel specific activation/deactivation
* @post_deactivate: Optional tunnel specific de-initialization called
* after deactivation. Can touch hardware.
* @destroy: Optional tunnel specific callback called when the tunnel
* memory is being released. Should not touch hardware.
* @maximum_bandwidth: Returns maximum possible bandwidth for this tunnel
* @allocated_bandwidth: Return how much bandwidth is allocated for the tunnel
* @alloc_bandwidth: Change tunnel bandwidth allocation
@ -37,6 +54,7 @@ enum tb_tunnel_type {
* @reclaim_available_bandwidth: Reclaim back available bandwidth
* @list: Tunnels are linked using this field
* @type: Type of the tunnel
* @state: Current state of the tunnel
* @max_up: Maximum upstream bandwidth (Mb/s) available for the tunnel.
* Only set if the bandwidth needs to be limited.
* @max_down: Maximum downstream bandwidth (Mb/s) available for the tunnel.
@ -45,16 +63,23 @@ enum tb_tunnel_type {
* @allocated_down: Allocated downstream bandwidth (only for USB3)
* @bw_mode: DP bandwidth allocation mode registers can be used to
* determine consumed and allocated bandwidth
* @dprx_canceled: Was DPRX capabilities read poll canceled
* @dprx_timeout: If set DPRX capabilities read poll work will timeout after this passes
* @dprx_work: Worker that is scheduled to poll completion of DPRX capabilities read
* @callback: Optional callback called when DP tunnel is fully activated
* @callback_data: Optional data for @callback
*/
struct tb_tunnel {
struct kref kref;
struct tb *tb;
struct tb_port *src_port;
struct tb_port *dst_port;
struct tb_path **paths;
size_t npaths;
int (*init)(struct tb_tunnel *tunnel);
void (*deinit)(struct tb_tunnel *tunnel);
int (*pre_activate)(struct tb_tunnel *tunnel);
int (*activate)(struct tb_tunnel *tunnel, bool activate);
void (*post_deactivate)(struct tb_tunnel *tunnel);
void (*destroy)(struct tb_tunnel *tunnel);
int (*maximum_bandwidth)(struct tb_tunnel *tunnel, int *max_up,
int *max_down);
int (*allocated_bandwidth)(struct tb_tunnel *tunnel, int *allocated_up,
@ -69,11 +94,17 @@ struct tb_tunnel {
int *available_down);
struct list_head list;
enum tb_tunnel_type type;
enum tb_tunnel_state state;
int max_up;
int max_down;
int allocated_up;
int allocated_down;
bool bw_mode;
bool dprx_canceled;
ktime_t dprx_timeout;
struct delayed_work dprx_work;
void (*callback)(struct tb_tunnel *tunnel, void *data);
void *callback_data;
};
struct tb_tunnel *tb_tunnel_discover_pci(struct tb *tb, struct tb_port *down,
@ -86,7 +117,9 @@ struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in,
bool alloc_hopid);
struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in,
struct tb_port *out, int link_nr,
int max_up, int max_down);
int max_up, int max_down,
void (*callback)(struct tb_tunnel *, void *),
void *callback_data);
struct tb_tunnel *tb_tunnel_alloc_dma(struct tb *tb, struct tb_port *nhi,
struct tb_port *dst, int transmit_path,
int transmit_ring, int receive_path,
@ -99,10 +132,24 @@ struct tb_tunnel *tb_tunnel_alloc_usb3(struct tb *tb, struct tb_port *up,
struct tb_port *down, int max_up,
int max_down);
void tb_tunnel_free(struct tb_tunnel *tunnel);
void tb_tunnel_put(struct tb_tunnel *tunnel);
int tb_tunnel_activate(struct tb_tunnel *tunnel);
int tb_tunnel_restart(struct tb_tunnel *tunnel);
void tb_tunnel_deactivate(struct tb_tunnel *tunnel);
/**
* tb_tunnel_is_active() - Is tunnel fully activated
* @tunnel: Tunnel to check
*
* Returns %true if @tunnel is fully activated. For other than DP
* tunnels this is pretty much once tb_tunnel_activate() returns
* successfully. However, for DP tunnels this returns %true only once the
* DPRX capabilities read has been issued successfully.
*/
static inline bool tb_tunnel_is_active(const struct tb_tunnel *tunnel)
{
return tunnel->state == TB_TUNNEL_ACTIVE;
}
bool tb_tunnel_is_invalid(struct tb_tunnel *tunnel);
bool tb_tunnel_port_on_path(const struct tb_tunnel *tunnel,
const struct tb_port *port);

View File

@ -15,6 +15,7 @@
#include <linux/delay.h>
#include <linux/log2.h>
#include <linux/slab.h>
#include <linux/string_choices.h>
#include <linux/pci.h>
#include <linux/irq.h>
#include <linux/dmi.h>
@ -1671,12 +1672,12 @@ static int cdnsp_gadget_init_endpoints(struct cdnsp_device *pdev)
"CTRL: %s, INT: %s, BULK: %s, ISOC %s, "
"SupDir IN: %s, OUT: %s\n",
pep->name, 1024,
(pep->endpoint.caps.type_control) ? "yes" : "no",
(pep->endpoint.caps.type_int) ? "yes" : "no",
(pep->endpoint.caps.type_bulk) ? "yes" : "no",
(pep->endpoint.caps.type_iso) ? "yes" : "no",
(pep->endpoint.caps.dir_in) ? "yes" : "no",
(pep->endpoint.caps.dir_out) ? "yes" : "no");
str_yes_no(pep->endpoint.caps.type_control),
str_yes_no(pep->endpoint.caps.type_int),
str_yes_no(pep->endpoint.caps.type_bulk),
str_yes_no(pep->endpoint.caps.type_iso),
str_yes_no(pep->endpoint.caps.dir_in),
str_yes_no(pep->endpoint.caps.dir_out));
INIT_LIST_HEAD(&pep->pending_list);
}

View File

@ -529,9 +529,7 @@ int cdns_resume(struct cdns *cdns)
int ret = 0;
if (cdns_power_is_lost(cdns)) {
if (cdns->role_sw) {
cdns->role = cdns_role_get(cdns->role_sw);
} else {
if (!cdns->role_sw) {
real_role = cdns_hw_role_state_machine(cdns);
if (real_role != cdns->role) {
ret = cdns_hw_role_switch(cdns);

View File

@ -13,6 +13,7 @@
#include <linux/usb/hcd.h>
#include <linux/usb/chipidea.h>
#include <linux/regulator/consumer.h>
#include <linux/string_choices.h>
#include <linux/pinctrl/consumer.h>
#include "../host/ehci.h"
@ -56,7 +57,7 @@ static int ehci_ci_portpower(struct usb_hcd *hcd, int portnum, bool enable)
if (ret) {
dev_err(dev,
"Failed to %s vbus regulator, ret=%d\n",
enable ? "enable" : "disable", ret);
str_enable_disable(enable), ret);
return ret;
}
priv->enabled = enable;
@ -256,8 +257,14 @@ static int ci_ehci_hub_control(
struct device *dev = hcd->self.controller;
struct ci_hdrc *ci = dev_get_drvdata(dev);
port_index = wIndex & 0xff;
port_index -= (port_index > 0);
/*
* Avoid out-of-bounds values while calculating the port index
* from wIndex. The compiler doesn't like pointers to invalid
* addresses, even if they are never used.
*/
port_index = (wIndex - 1) & 0xff;
if (port_index >= HCS_N_PORTS_MAX)
port_index = 0;
status_reg = &ehci->regs->port_status[port_index];
spin_lock_irqsave(&ehci->lock, flags);

View File

@ -87,7 +87,7 @@
/* Get two-int array: [0]=vendor ID, [1]=product ID: */
#define LPIOC_GET_VID_PID(len) _IOC(_IOC_READ, 'P', IOCNR_GET_VID_PID, len)
/* Perform class specific soft reset */
#define LPIOC_SOFT_RESET _IOC(_IOC_NONE, 'P', IOCNR_SOFT_RESET, 0);
#define LPIOC_SOFT_RESET _IOC(_IOC_NONE, 'P', IOCNR_SOFT_RESET, 0)
/*
* A DEVICE_ID string may include the printer's serial number.

View File

@ -41,6 +41,12 @@ const char *usb_ep_type_string(int ep_type)
}
EXPORT_SYMBOL_GPL(usb_ep_type_string);
/**
* usb_otg_state_string() - returns human readable name of OTG state.
* @state: the OTG state to return the human readable name of. If it's not
* any of the states defined in usb_otg_state enum, 'UNDEFINED' will be
* returned.
*/
const char *usb_otg_state_string(enum usb_otg_state state)
{
static const char *const names[] = {
@ -179,6 +185,14 @@ static const char *const usb_dr_modes[] = {
[USB_DR_MODE_OTG] = "otg",
};
/**
* usb_get_dr_mode_from_string() - Get dual role mode for given string
* @str: String to find the corresponding dual role mode for
*
* This function performs a lookup for the given string and returns the
* corresponding enum usb_dr_mode. If no match for the string could be found,
* 'USB_DR_MODE_UNKNOWN' is returned.
*/
static enum usb_dr_mode usb_get_dr_mode_from_string(const char *str)
{
int ret;

View File

@ -19,6 +19,7 @@
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/regulator/consumer.h>
#include <linux/string_choices.h>
#include <linux/usb/role.h>
#define USB_GPIO_DEB_MS 20 /* ms */
@ -111,7 +112,7 @@ static void usb_conn_detect_cable(struct work_struct *work)
if (info->vbus)
dev_dbg(info->dev, "vbus regulator is %s\n",
regulator_is_enabled(info->vbus) ? "enabled" : "disabled");
str_enabled_disabled(regulator_is_enabled(info->vbus)));
power_supply_changed(info->charger);
}

View File

@ -9,6 +9,7 @@
#include <linux/usb/quirks.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/string_choices.h>
#include <linux/device.h>
#include <asm/byteorder.h>
#include "usb.h"
@ -18,12 +19,6 @@
#define USB_MAXCONFIG 8 /* Arbitrary limit */
static inline const char *plural(int n)
{
return (n == 1 ? "" : "s");
}
static int find_next_descriptor(unsigned char *buffer, int size,
int dt1, int dt2, int *num_skipped)
{
@ -484,7 +479,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno,
retval = buffer - buffer0 + i;
if (n > 0)
dev_dbg(ddev, "skipped %d descriptor%s after %s\n",
n, plural(n), "endpoint");
n, str_plural(n), "endpoint");
return retval;
skip_to_next_endpoint_or_interface_descriptor:
@ -563,7 +558,7 @@ static int usb_parse_interface(struct device *ddev, int cfgno,
alt->extralen = i;
if (n > 0)
dev_dbg(ddev, "skipped %d descriptor%s after %s\n",
n, plural(n), "interface");
n, str_plural(n), "interface");
buffer += i;
size -= i;
@ -605,7 +600,7 @@ static int usb_parse_interface(struct device *ddev, int cfgno,
dev_notice(ddev, "config %d interface %d altsetting %d has %d "
"endpoint descriptor%s, different from the interface "
"descriptor's value: %d\n",
cfgno, inum, asnum, n, plural(n), num_ep_orig);
cfgno, inum, asnum, n, str_plural(n), num_ep_orig);
return buffer - buffer0;
skip_to_next_interface_descriptor:
@ -664,7 +659,7 @@ static int usb_parse_configuration(struct usb_device *dev, int cfgidx,
if (size2 < sizeof(struct usb_descriptor_header)) {
dev_notice(ddev, "config %d descriptor has %d excess "
"byte%s, ignoring\n",
cfgno, size2, plural(size2));
cfgno, size2, str_plural(size2));
break;
}
@ -754,7 +749,7 @@ static int usb_parse_configuration(struct usb_device *dev, int cfgidx,
if (n != nintf)
dev_notice(ddev, "config %d has %d interface%s, different from "
"the descriptor's value: %d\n",
cfgno, n, plural(n), nintf_orig);
cfgno, n, str_plural(n), nintf_orig);
else if (n == 0)
dev_notice(ddev, "config %d has no interfaces?\n", cfgno);
config->desc.bNumInterfaces = nintf = n;
@ -798,7 +793,7 @@ static int usb_parse_configuration(struct usb_device *dev, int cfgidx,
config->extralen = i;
if (n > 0)
dev_dbg(ddev, "skipped %d descriptor%s after %s\n",
n, plural(n), "configuration");
n, str_plural(n), "configuration");
buffer += i;
size -= i;

View File

@ -1086,15 +1086,14 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
pr_info("%s: registered new interface driver %s\n",
usbcore_name, new_driver->name);
out:
return retval;
return 0;
out_newid:
driver_unregister(&new_driver->driver);
out:
pr_err("%s: error %d registering interface driver %s\n",
usbcore_name, retval, new_driver->name);
goto out;
return retval;
}
EXPORT_SYMBOL_GPL(usb_register_driver);

View File

@ -21,14 +21,10 @@
#include <linux/usb.h>
#include <linux/usb/hcd.h>
#include <linux/string_choices.h>
#include <uapi/linux/usb/audio.h>
#include "usb.h"
static inline const char *plural(int n)
{
return (n == 1 ? "" : "s");
}
static int is_rndis(struct usb_interface_descriptor *desc)
{
return desc->bInterfaceClass == USB_CLASS_COMM
@ -194,18 +190,18 @@ int usb_choose_configuration(struct usb_device *udev)
if (insufficient_power > 0)
dev_info(&udev->dev, "rejected %d configuration%s "
"due to insufficient available bus power\n",
insufficient_power, plural(insufficient_power));
insufficient_power, str_plural(insufficient_power));
if (best) {
i = best->desc.bConfigurationValue;
dev_dbg(&udev->dev,
"configuration #%d chosen from %d choice%s\n",
i, num_configs, plural(num_configs));
i, num_configs, str_plural(num_configs));
} else {
i = -1;
dev_warn(&udev->dev,
"no configuration chosen from %d choice%s\n",
num_configs, plural(num_configs));
num_configs, str_plural(num_configs));
}
return i;
}

View File

@ -422,7 +422,12 @@ static int suspend_common(struct device *dev, pm_message_t msg)
bool do_wakeup;
int retval;
do_wakeup = PMSG_IS_AUTO(msg) ? true : device_may_wakeup(dev);
if (PMSG_IS_AUTO(msg))
do_wakeup = true;
else if (PMSG_NO_WAKEUP(msg))
do_wakeup = false;
else
do_wakeup = device_may_wakeup(dev);
/* Root hub suspend should have stopped all downstream traffic,
* and all bus master traffic. And done so for both the interface
@ -521,6 +526,11 @@ static int hcd_pci_suspend(struct device *dev)
return suspend_common(dev, PMSG_SUSPEND);
}
static int hcd_pci_freeze(struct device *dev)
{
return suspend_common(dev, PMSG_FREEZE);
}
static int hcd_pci_suspend_noirq(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
@ -590,6 +600,7 @@ static int hcd_pci_restore(struct device *dev)
#else
#define hcd_pci_suspend NULL
#define hcd_pci_freeze NULL
#define hcd_pci_suspend_noirq NULL
#define hcd_pci_poweroff_late NULL
#define hcd_pci_resume_noirq NULL
@ -624,7 +635,7 @@ const struct dev_pm_ops usb_hcd_pci_pm_ops = {
.suspend_noirq = hcd_pci_suspend_noirq,
.resume_noirq = hcd_pci_resume_noirq,
.resume = hcd_pci_resume,
.freeze = hcd_pci_suspend,
.freeze = hcd_pci_freeze,
.freeze_noirq = check_root_hub_suspended,
.thaw_noirq = NULL,
.thaw = hcd_pci_resume,

View File

@ -415,7 +415,7 @@ ascii2desc(char const *s, u8 *buf, unsigned len)
static unsigned
rh_string(int id, struct usb_hcd const *hcd, u8 *data, unsigned len)
{
char buf[100];
char buf[160];
char const *s;
static char const langids[4] = {4, USB_DT_STRING, 0x09, 0x04};

View File

@ -18,6 +18,7 @@
#include <linux/sched/mm.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/string_choices.h>
#include <linux/kcov.h>
#include <linux/ioctl.h>
#include <linux/usb.h>
@ -1496,7 +1497,7 @@ static int hub_configure(struct usb_hub *hub,
maxchild = hub->descriptor->bNbrPorts;
dev_info(hub_dev, "%d port%s detected\n", maxchild,
(maxchild == 1) ? "" : "s");
str_plural(maxchild));
hub->ports = kcalloc(maxchild, sizeof(struct usb_port *), GFP_KERNEL);
if (!hub->ports) {
@ -4139,14 +4140,14 @@ static int usb_set_device_initiated_lpm(struct usb_device *udev,
break;
default:
dev_warn(&udev->dev, "%s: Can't %s non-U1 or U2 state.\n",
__func__, enable ? "enable" : "disable");
__func__, str_enable_disable(enable));
return -EINVAL;
}
if (udev->state != USB_STATE_CONFIGURED) {
dev_dbg(&udev->dev, "%s: Can't %s %s state "
"for unconfigured device.\n",
__func__, enable ? "enable" : "disable",
__func__, str_enable_disable(enable),
usb3_lpm_names[state]);
return 0;
}
@ -4172,8 +4173,7 @@ static int usb_set_device_initiated_lpm(struct usb_device *udev,
}
if (ret < 0) {
dev_warn(&udev->dev, "%s of device-initiated %s failed.\n",
enable ? "Enable" : "Disable",
usb3_lpm_names[state]);
str_enable_disable(enable), usb3_lpm_names[state]);
return -EBUSY;
}
return 0;

View File

@ -9,6 +9,7 @@
#include <linux/kstrtox.h>
#include <linux/slab.h>
#include <linux/string_choices.h>
#include <linux/sysfs.h>
#include <linux/pm_qos.h>
#include <linux/component.h>
@ -25,7 +26,7 @@ static ssize_t early_stop_show(struct device *dev,
{
struct usb_port *port_dev = to_usb_port(dev);
return sysfs_emit(buf, "%s\n", port_dev->early_stop ? "yes" : "no");
return sysfs_emit(buf, "%s\n", str_yes_no(port_dev->early_stop));
}
static ssize_t early_stop_store(struct device *dev, struct device_attribute *attr,

View File

@ -394,6 +394,9 @@ static const struct usb_device_id usb_quirk_list[] = {
/* Kingston DataTraveler 3.0 */
{ USB_DEVICE(0x0951, 0x1666), .driver_info = USB_QUIRK_NO_LPM },
/* TOSHIBA TransMemory-Mx */
{ USB_DEVICE(0x0930, 0x1408), .driver_info = USB_QUIRK_NO_LPM },
/* NVIDIA Jetson devices in Force Recovery mode */
{ USB_DEVICE(0x0955, 0x7018), .driver_info = USB_QUIRK_RESET_RESUME },
{ USB_DEVICE(0x0955, 0x7019), .driver_info = USB_QUIRK_RESET_RESUME },

View File

@ -854,7 +854,7 @@ static const struct attribute_group dev_string_attr_grp = {
static ssize_t
descriptors_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr,
const struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct device *dev = kobj_to_dev(kobj);
@ -890,11 +890,11 @@ descriptors_read(struct file *filp, struct kobject *kobj,
}
return count - nleft;
}
static BIN_ATTR_RO(descriptors, 18 + 65535); /* dev descr + max-size raw descriptor */
static const BIN_ATTR_RO(descriptors, 18 + 65535); /* dev descr + max-size raw descriptor */
static ssize_t
bos_descriptors_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr,
const struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct device *dev = kobj_to_dev(kobj);
@ -913,12 +913,12 @@ bos_descriptors_read(struct file *filp, struct kobject *kobj,
}
return n;
}
static BIN_ATTR_RO(bos_descriptors, 65535); /* max-size BOS */
static const BIN_ATTR_RO(bos_descriptors, 65535); /* max-size BOS */
/* When modifying this list, be sure to modify dev_bin_attrs_are_visible()
* accordingly.
*/
static struct bin_attribute *dev_bin_attrs[] = {
static const struct bin_attribute *const dev_bin_attrs[] = {
&bin_attr_descriptors,
&bin_attr_bos_descriptors,
NULL
@ -944,7 +944,7 @@ static umode_t dev_bin_attrs_are_visible(struct kobject *kobj,
}
static const struct attribute_group dev_bin_attr_grp = {
.bin_attrs = dev_bin_attrs,
.bin_attrs_new = dev_bin_attrs,
.is_bin_visible = dev_bin_attrs_are_visible,
};

View File

@ -1479,6 +1479,26 @@ static int dwc3_core_init(struct dwc3 *dwc)
}
}
/*
* STAR 9001346572: This issue affects DWC_usb31 versions 1.80a and
* prior. When an active endpoint not currently cached in the host
* controller is chosen to be cached to the same index as an endpoint
* receiving NAKs, the endpoint receiving NAKs enters continuous
* retry mode. This prevents it from being evicted from the host
* controller cache, blocking the new endpoint from being cached and
* serviced.
*
* To resolve this, for controller versions 1.70a and 1.80a, set the
* GUCTL3 bit[16] (USB2.0 Internal Retry Disable) to 1. This bit
* disables the USB2.0 internal retry feature. The GUCTL3[16] register
* function is available only from version 1.70a.
*/
if (DWC3_VER_IS_WITHIN(DWC31, 170A, 180A)) {
reg = dwc3_readl(dwc->regs, DWC3_GUCTL3);
reg |= DWC3_GUCTL3_USB20_RETRY_DISABLE;
dwc3_writel(dwc->regs, DWC3_GUCTL3, reg);
}
return 0;
err_power_off_phy:
@ -1664,8 +1684,6 @@ static void dwc3_get_properties(struct dwc3 *dwc)
u8 tx_thr_num_pkt_prd = 0;
u8 tx_max_burst_prd = 0;
u8 tx_fifo_resize_max_num;
const char *usb_psy_name;
int ret;
/* default to highest possible threshold */
lpm_nyet_threshold = 0xf;
@ -1700,13 +1718,6 @@ static void dwc3_get_properties(struct dwc3 *dwc)
dwc->sys_wakeup = device_may_wakeup(dwc->sysdev);
ret = device_property_read_string(dev, "usb-psy-name", &usb_psy_name);
if (ret >= 0) {
dwc->usb_psy = power_supply_get_by_name(usb_psy_name);
if (!dwc->usb_psy)
dev_err(dev, "couldn't get usb power supply\n");
}
dwc->has_lpm_erratum = device_property_read_bool(dev,
"snps,has-lpm-erratum");
device_property_read_u8(dev, "snps,lpm-nyet-threshold",
@ -2109,6 +2120,23 @@ static int dwc3_get_num_ports(struct dwc3 *dwc)
return 0;
}
static struct power_supply *dwc3_get_usb_power_supply(struct dwc3 *dwc)
{
struct power_supply *usb_psy;
const char *usb_psy_name;
int ret;
ret = device_property_read_string(dwc->dev, "usb-psy-name", &usb_psy_name);
if (ret < 0)
return NULL;
usb_psy = power_supply_get_by_name(usb_psy_name);
if (!usb_psy)
return ERR_PTR(-EPROBE_DEFER);
return usb_psy;
}
static int dwc3_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@ -2165,6 +2193,10 @@ static int dwc3_probe(struct platform_device *pdev)
dwc3_get_software_properties(dwc);
dwc->usb_psy = dwc3_get_usb_power_supply(dwc);
if (IS_ERR(dwc->usb_psy))
return dev_err_probe(dev, PTR_ERR(dwc->usb_psy), "couldn't get usb power supply\n");
dwc->reset = devm_reset_control_array_get_optional_shared(dev);
if (IS_ERR(dwc->reset)) {
ret = PTR_ERR(dwc->reset);
@ -2589,12 +2621,15 @@ static int dwc3_resume(struct device *dev)
pinctrl_pm_select_default_state(dev);
pm_runtime_disable(dev);
pm_runtime_set_active(dev);
ret = pm_runtime_set_active(dev);
if (ret)
goto out;
ret = dwc3_resume_common(dwc, PMSG_RESUME);
if (ret)
pm_runtime_set_suspended(dev);
out:
pm_runtime_enable(dev);
return ret;

View File

@ -425,6 +425,7 @@
/* Global User Control Register 3 */
#define DWC3_GUCTL3_SPLITDISABLE BIT(14)
#define DWC3_GUCTL3_USB20_RETRY_DISABLE BIT(16)
/* Device Configuration Register */
#define DWC3_DCFG_NUMLANES(n) (((n) & 0x3) << 30) /* DWC_usb32 only */
@ -742,6 +743,7 @@ struct dwc3_event_buffer {
*/
struct dwc3_ep {
struct usb_ep endpoint;
struct delayed_work nostream_work;
struct list_head cancelled_list;
struct list_head pending_list;
struct list_head started_list;
@ -764,7 +766,7 @@ struct dwc3_ep {
#define DWC3_EP_WAIT_TRANSFER_COMPLETE BIT(7)
#define DWC3_EP_IGNORE_NEXT_NOSTREAM BIT(8)
#define DWC3_EP_FORCE_RESTART_STREAM BIT(9)
#define DWC3_EP_FIRST_STREAM_PRIMED BIT(10)
#define DWC3_EP_STREAM_PRIMED BIT(10)
#define DWC3_EP_PENDING_CLEAR_STALL BIT(11)
#define DWC3_EP_TXFIFO_RESIZED BIT(12)
#define DWC3_EP_DELAY_STOP BIT(13)
@ -957,7 +959,6 @@ struct dwc3_request {
struct usb_request request;
struct list_head list;
struct dwc3_ep *dep;
struct scatterlist *sg;
struct scatterlist *start_sg;
unsigned int num_pending_sgs;

View File

@ -108,6 +108,9 @@
#define DWC3_AM62_AUTOSUSPEND_DELAY 100
#define USBSS_DEBUG_CFG_OFF 0x0
#define USBSS_DEBUG_CFG_DISABLED 0x7
struct dwc3_am62 {
struct device *dev;
void __iomem *usbss;
@ -117,6 +120,7 @@ struct dwc3_am62 {
unsigned int offset;
unsigned int vbus_divider;
u32 wakeup_stat;
void __iomem *phy_regs;
};
static const int dwc3_ti_rate_table[] = { /* in KHZ */
@ -166,6 +170,7 @@ static int phy_syscon_pll_refclk(struct dwc3_am62 *am62)
if (ret)
return ret;
of_node_put(args.np);
am62->offset = args.args[0];
/* Core voltage. PHY_CORE_VOLTAGE bit Recommended to be 0 always */
@ -184,15 +189,47 @@ static int phy_syscon_pll_refclk(struct dwc3_am62 *am62)
return 0;
}
static int dwc3_ti_init(struct dwc3_am62 *am62)
{
int ret;
u32 reg;
/* Read the syscon property and set the rate code */
ret = phy_syscon_pll_refclk(am62);
if (ret)
return ret;
/* Workaround Errata i2409 */
if (am62->phy_regs) {
reg = readl(am62->phy_regs + USB_PHY_PLL_REG12);
reg |= USB_PHY_PLL_LDO_REF_EN | USB_PHY_PLL_LDO_REF_EN_EN;
writel(reg, am62->phy_regs + USB_PHY_PLL_REG12);
}
/* VBUS divider select */
reg = dwc3_ti_readl(am62, USBSS_PHY_CONFIG);
if (am62->vbus_divider)
reg |= 1 << USBSS_PHY_VBUS_SEL_SHIFT;
dwc3_ti_writel(am62, USBSS_PHY_CONFIG, reg);
clk_prepare_enable(am62->usb2_refclk);
/* Set mode valid bit to indicate role is valid */
reg = dwc3_ti_readl(am62, USBSS_MODE_CONTROL);
reg |= USBSS_MODE_VALID;
dwc3_ti_writel(am62, USBSS_MODE_CONTROL, reg);
return 0;
}
static int dwc3_ti_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *node = pdev->dev.of_node;
struct dwc3_am62 *am62;
unsigned long rate;
void __iomem *phy;
int i, ret;
u32 reg;
am62 = devm_kzalloc(dev, sizeof(*am62), GFP_KERNEL);
if (!am62)
@ -228,29 +265,17 @@ static int dwc3_ti_probe(struct platform_device *pdev)
am62->rate_code = i;
/* Read the syscon property and set the rate code */
ret = phy_syscon_pll_refclk(am62);
if (ret)
return ret;
/* Workaround Errata i2409 */
phy = devm_platform_ioremap_resource(pdev, 1);
if (IS_ERR(phy)) {
am62->phy_regs = devm_platform_ioremap_resource(pdev, 1);
if (IS_ERR(am62->phy_regs)) {
dev_err(dev, "can't map PHY IOMEM resource. Won't apply i2409 fix.\n");
phy = NULL;
} else {
reg = readl(phy + USB_PHY_PLL_REG12);
reg |= USB_PHY_PLL_LDO_REF_EN | USB_PHY_PLL_LDO_REF_EN_EN;
writel(reg, phy + USB_PHY_PLL_REG12);
am62->phy_regs = NULL;
}
/* VBUS divider select */
am62->vbus_divider = device_property_read_bool(dev, "ti,vbus-divider");
reg = dwc3_ti_readl(am62, USBSS_PHY_CONFIG);
if (am62->vbus_divider)
reg |= 1 << USBSS_PHY_VBUS_SEL_SHIFT;
dwc3_ti_writel(am62, USBSS_PHY_CONFIG, reg);
ret = dwc3_ti_init(am62);
if (ret)
return ret;
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
@ -258,7 +283,6 @@ static int dwc3_ti_probe(struct platform_device *pdev)
* Don't ignore its dependencies with its children
*/
pm_suspend_ignore_children(dev, false);
clk_prepare_enable(am62->usb2_refclk);
pm_runtime_get_noresume(dev);
ret = of_platform_populate(node, NULL, NULL, dev);
@ -267,11 +291,6 @@ static int dwc3_ti_probe(struct platform_device *pdev)
goto err_pm_disable;
}
/* Set mode valid bit to indicate role is valid */
reg = dwc3_ti_readl(am62, USBSS_MODE_CONTROL);
reg |= USBSS_MODE_VALID;
dwc3_ti_writel(am62, USBSS_MODE_CONTROL, reg);
/* Device has capability to wakeup system from sleep */
device_set_wakeup_capable(dev, true);
ret = device_wakeup_enable(dev);
@ -339,6 +358,9 @@ static int dwc3_ti_suspend_common(struct device *dev)
dwc3_ti_writel(am62, USBSS_WAKEUP_STAT, USBSS_WAKEUP_STAT_CLR);
}
/* just to track if module resets on suspend */
dwc3_ti_writel(am62, USBSS_DEBUG_CFG, USBSS_DEBUG_CFG_DISABLED);
clk_disable_unprepare(am62->usb2_refclk);
return 0;
@ -349,7 +371,14 @@ static int dwc3_ti_resume_common(struct device *dev)
struct dwc3_am62 *am62 = dev_get_drvdata(dev);
u32 reg;
clk_prepare_enable(am62->usb2_refclk);
reg = dwc3_ti_readl(am62, USBSS_DEBUG_CFG);
if (reg != USBSS_DEBUG_CFG_DISABLED) {
/* lost power/context */
dwc3_ti_init(am62);
} else {
dwc3_ti_writel(am62, USBSS_DEBUG_CFG, USBSS_DEBUG_CFG_OFF);
clk_prepare_enable(am62->usb2_refclk);
}
if (device_may_wakeup(dev)) {
/* Clear wakeup config enable bits */

View File

@ -457,7 +457,7 @@ static int dwc3_omap_probe(struct platform_device *pdev)
struct dwc3_omap *omap;
struct device *dev = &pdev->dev;
struct regulator *vbus_reg = NULL;
struct regulator *vbus_reg;
int ret;
int irq;
@ -483,12 +483,11 @@ static int dwc3_omap_probe(struct platform_device *pdev)
if (IS_ERR(base))
return PTR_ERR(base);
if (of_property_read_bool(node, "vbus-supply")) {
vbus_reg = devm_regulator_get(dev, "vbus");
if (IS_ERR(vbus_reg)) {
dev_err(dev, "vbus init failed\n");
return PTR_ERR(vbus_reg);
}
vbus_reg = devm_regulator_get_optional(dev, "vbus");
if (IS_ERR(vbus_reg)) {
if (PTR_ERR(vbus_reg) != -ENODEV)
return dev_err_probe(dev, PTR_ERR(vbus_reg), "vbus init failed\n");
vbus_reg = NULL;
}
omap->dev = dev;

View File

@ -309,7 +309,6 @@ static void st_dwc3_remove(struct platform_device *pdev)
reset_control_assert(dwc3_data->rstc_rst);
}
#ifdef CONFIG_PM_SLEEP
static int st_dwc3_suspend(struct device *dev)
{
struct st_dwc3 *dwc3_data = dev_get_drvdata(dev);
@ -343,9 +342,8 @@ static int st_dwc3_resume(struct device *dev)
return 0;
}
#endif /* CONFIG_PM_SLEEP */
static SIMPLE_DEV_PM_OPS(st_dwc3_dev_pm_ops, st_dwc3_suspend, st_dwc3_resume);
static DEFINE_SIMPLE_DEV_PM_OPS(st_dwc3_dev_pm_ops, st_dwc3_suspend, st_dwc3_resume);
static const struct of_device_id st_dwc3_match[] = {
{ .compatible = "st,stih407-dwc3" },
@ -360,7 +358,7 @@ static struct platform_driver st_dwc3_driver = {
.driver = {
.name = "usb-st-dwc3",
.of_match_table = st_dwc3_match,
.pm = &st_dwc3_dev_pm_ops,
.pm = pm_sleep_ptr(&st_dwc3_dev_pm_ops),
},
};

View File

@ -996,8 +996,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
/*
* All stream eps will reinitiate stream on NoStream
* rejection until we can determine that the host can
* prime after the first transfer.
* rejection.
*
* However, if the controller is capable of
* TXF_FLUSH_BYPASS, then IN direction endpoints will
@ -2740,6 +2739,8 @@ static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc)
__dwc3_gadget_stop(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
usb_gadget_set_state(dwc->gadget, USB_STATE_NOTATTACHED);
return ret;
}
@ -3298,6 +3299,50 @@ static int dwc3_gadget_init_out_endpoint(struct dwc3_ep *dep)
return dwc3_alloc_trb_pool(dep);
}
#define nostream_work_to_dep(w) (container_of(to_delayed_work(w), struct dwc3_ep, nostream_work))
static void dwc3_nostream_work(struct work_struct *work)
{
struct dwc3_ep *dep = nostream_work_to_dep(work);
struct dwc3 *dwc = dep->dwc;
unsigned long flags;
spin_lock_irqsave(&dwc->lock, flags);
if (dep->flags & DWC3_EP_STREAM_PRIMED)
goto out;
if ((dep->flags & DWC3_EP_IGNORE_NEXT_NOSTREAM) ||
(!DWC3_MST_CAPABLE(&dwc->hwparams) &&
!(dep->flags & DWC3_EP_WAIT_TRANSFER_COMPLETE)))
goto out;
/*
* If the host rejects a stream due to no active stream, by the
* USB and xHCI spec, the endpoint will be put back to idle
* state. When the host is ready (buffer added/updated), it will
* prime the endpoint to inform the usb device controller. This
* triggers the device controller to issue ERDY to restart the
* stream. However, some hosts don't follow this and keep the
* endpoint in the idle state. No prime will come despite host
* streams are updated, and the device controller will not be
* triggered to generate ERDY to move the next stream data. To
* workaround this and maintain compatibility with various
* hosts, force to reinitiate the stream until the host is ready
* instead of waiting for the host to prime the endpoint.
*/
if (DWC3_VER_IS_WITHIN(DWC32, 100A, ANY)) {
unsigned int cmd = DWC3_DGCMD_SET_ENDPOINT_PRIME;
dwc3_send_gadget_generic_command(dwc, cmd, dep->number);
} else {
dep->flags |= DWC3_EP_DELAY_START;
dwc3_stop_active_transfer(dep, true, true);
spin_unlock_irqrestore(&dwc->lock, flags);
return;
}
out:
dep->flags &= ~DWC3_EP_IGNORE_NEXT_NOSTREAM;
spin_unlock_irqrestore(&dwc->lock, flags);
}
static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum)
{
struct dwc3_ep *dep;
@ -3343,6 +3388,7 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum)
INIT_LIST_HEAD(&dep->pending_list);
INIT_LIST_HEAD(&dep->started_list);
INIT_LIST_HEAD(&dep->cancelled_list);
INIT_DELAYED_WORK(&dep->nostream_work, dwc3_nostream_work);
dwc3_debugfs_create_endpoint_dir(dep);
@ -3742,66 +3788,27 @@ static void dwc3_gadget_endpoint_command_complete(struct dwc3_ep *dep,
static void dwc3_gadget_endpoint_stream_event(struct dwc3_ep *dep,
const struct dwc3_event_depevt *event)
{
struct dwc3 *dwc = dep->dwc;
if (event->status == DEPEVT_STREAMEVT_FOUND) {
dep->flags |= DWC3_EP_FIRST_STREAM_PRIMED;
goto out;
cancel_delayed_work(&dep->nostream_work);
dep->flags |= DWC3_EP_STREAM_PRIMED;
dep->flags &= ~DWC3_EP_IGNORE_NEXT_NOSTREAM;
return;
}
/* Note: NoStream rejection event param value is 0 and not 0xFFFF */
switch (event->parameters) {
case DEPEVT_STREAM_PRIME:
/*
* If the host can properly transition the endpoint state from
* idle to prime after a NoStream rejection, there's no need to
* force restarting the endpoint to reinitiate the stream. To
* simplify the check, assume the host follows the USB spec if
* it primed the endpoint more than once.
*/
if (dep->flags & DWC3_EP_FORCE_RESTART_STREAM) {
if (dep->flags & DWC3_EP_FIRST_STREAM_PRIMED)
dep->flags &= ~DWC3_EP_FORCE_RESTART_STREAM;
else
dep->flags |= DWC3_EP_FIRST_STREAM_PRIMED;
}
cancel_delayed_work(&dep->nostream_work);
dep->flags |= DWC3_EP_STREAM_PRIMED;
dep->flags &= ~DWC3_EP_IGNORE_NEXT_NOSTREAM;
break;
case DEPEVT_STREAM_NOSTREAM:
if ((dep->flags & DWC3_EP_IGNORE_NEXT_NOSTREAM) ||
!(dep->flags & DWC3_EP_FORCE_RESTART_STREAM) ||
(!DWC3_MST_CAPABLE(&dwc->hwparams) &&
!(dep->flags & DWC3_EP_WAIT_TRANSFER_COMPLETE)))
break;
/*
* If the host rejects a stream due to no active stream, by the
* USB and xHCI spec, the endpoint will be put back to idle
* state. When the host is ready (buffer added/updated), it will
* prime the endpoint to inform the usb device controller. This
* triggers the device controller to issue ERDY to restart the
* stream. However, some hosts don't follow this and keep the
* endpoint in the idle state. No prime will come despite host
* streams are updated, and the device controller will not be
* triggered to generate ERDY to move the next stream data. To
* workaround this and maintain compatibility with various
* hosts, force to reinitiate the stream until the host is ready
* instead of waiting for the host to prime the endpoint.
*/
if (DWC3_VER_IS_WITHIN(DWC32, 100A, ANY)) {
unsigned int cmd = DWC3_DGCMD_SET_ENDPOINT_PRIME;
dwc3_send_gadget_generic_command(dwc, cmd, dep->number);
} else {
dep->flags |= DWC3_EP_DELAY_START;
dwc3_stop_active_transfer(dep, true, true);
return;
}
dep->flags &= ~DWC3_EP_STREAM_PRIMED;
if (dep->flags & DWC3_EP_FORCE_RESTART_STREAM)
queue_delayed_work(system_wq, &dep->nostream_work,
msecs_to_jiffies(100));
break;
}
out:
dep->flags &= ~DWC3_EP_IGNORE_NEXT_NOSTREAM;
}
static void dwc3_endpoint_interrupt(struct dwc3 *dwc,

View File

@ -13,6 +13,7 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/string_choices.h>
#include <linux/usb.h>
#include <linux/usb/otg.h>
@ -119,8 +120,8 @@ void fotg210_vbus(struct fotg210 *fotg, bool enable)
ret = regmap_update_bits(fotg->map, GEMINI_GLOBAL_MISC_CTRL, mask, val);
if (ret)
dev_err(fotg->dev, "failed to %s VBUS\n",
enable ? "enable" : "disable");
dev_info(fotg->dev, "%s: %s VBUS\n", __func__, enable ? "enable" : "disable");
str_enable_disable(enable));
dev_info(fotg->dev, "%s: %s VBUS\n", __func__, str_enable_disable(enable));
}
static int fotg210_probe(struct platform_device *pdev)

View File

@ -13,6 +13,7 @@
#include <linux/module.h>
#include <linux/device.h>
#include <linux/etherdevice.h>
#include <linux/string_choices.h>
#include "u_ether.h"
#include "u_ether_configfs.h"
@ -387,8 +388,7 @@ static void ecm_do_notify(struct f_ecm *ecm)
event->wLength = 0;
req->length = sizeof *event;
DBG(cdev, "notify connect %s\n",
ecm->is_open ? "true" : "false");
DBG(cdev, "notify connect %s\n", str_true_false(ecm->is_open));
ecm->notify_state = ECM_NOTIFY_SPEED;
break;

View File

@ -17,6 +17,7 @@
#include <linux/device.h>
#include <linux/etherdevice.h>
#include <linux/crc32.h>
#include <linux/string_choices.h>
#include <linux/usb/cdc.h>
@ -558,7 +559,7 @@ static void ncm_do_notify(struct f_ncm *ncm)
req->length = sizeof *event;
DBG(cdev, "notify connect %s\n",
ncm->is_open ? "true" : "false");
str_true_false(ncm->is_open));
ncm->notify_state = NCM_NOTIFY_NONE;
break;

File diff suppressed because it is too large Load Diff

View File

@ -131,7 +131,7 @@ static inline bool fsg_lun_is_open(struct fsg_lun *curlun)
#define FSG_BUFLEN ((u32)16384)
/* Maximal number of LUNs supported in mass storage function */
#define FSG_MAX_LUNS 16
#define FSG_MAX_LUNS (US_BULK_MAX_LUN_LIMIT + 1)
enum fsg_buffer_state {
BUF_STATE_SENDING = -2,

View File

@ -4,6 +4,7 @@
#include <linux/kref.h>
/* #include <linux/usb/uas.h> */
#include <linux/hashtable.h>
#include <linux/usb/composite.h>
#include <linux/usb/uas.h>
#include <linux/usb/storage.h>
@ -13,9 +14,11 @@
#define USBG_NAMELEN 32
#define fuas_to_gadget(f) (f->function.config->cdev->gadget)
#define UASP_SS_EP_COMP_LOG_STREAMS 4
#define UASP_SS_EP_COMP_LOG_STREAMS 5
#define UASP_SS_EP_COMP_NUM_STREAMS (1 << UASP_SS_EP_COMP_LOG_STREAMS)
#define USBG_NUM_CMDS (UASP_SS_EP_COMP_NUM_STREAMS + 1)
enum {
USB_G_STR_INT_UAS = 0,
USB_G_STR_INT_BBB,
@ -24,7 +27,7 @@ enum {
#define USB_G_ALT_INT_BBB 0
#define USB_G_ALT_INT_UAS 1
#define USB_G_DEFAULT_SESSION_TAGS 128
#define USB_G_DEFAULT_SESSION_TAGS USBG_NUM_CMDS
struct tcm_usbg_nexus {
struct se_session *tvn_se_sess;
@ -72,15 +75,23 @@ struct usbg_cmd {
struct se_cmd se_cmd;
void *data_buf; /* used if no sg support available */
struct f_uas *fu;
struct completion write_complete;
struct kref ref;
struct usb_request *req;
u32 flags;
#define USBG_CMD_PENDING_DATA_WRITE BIT(0)
/* UAS only */
u16 tag;
u16 prio_attr;
struct sense_iu sense_iu;
struct response_iu response_iu;
enum uas_state state;
struct uas_stream *stream;
int tmr_func;
int tmr_rsp;
#define RC_RESPONSE_UNKNOWN 0xff
/* BOT only */
__le32 bot_tag;
@ -93,6 +104,9 @@ struct uas_stream {
struct usb_request *req_in;
struct usb_request *req_out;
struct usb_request *req_status;
struct completion cmd_completion;
struct hlist_node node;
};
struct usbg_cdb {
@ -116,15 +130,17 @@ struct f_uas {
#define USBG_USE_STREAMS (1 << 2)
#define USBG_IS_BOT (1 << 3)
#define USBG_BOT_CMD_PEND (1 << 4)
#define USBG_BOT_WEDGED (1 << 5)
struct usbg_cdb cmd;
struct usbg_cdb cmd[USBG_NUM_CMDS];
struct usb_ep *ep_in;
struct usb_ep *ep_out;
/* UAS */
struct usb_ep *ep_status;
struct usb_ep *ep_cmd;
struct uas_stream stream[UASP_SS_EP_COMP_NUM_STREAMS];
struct uas_stream stream[USBG_NUM_CMDS];
DECLARE_HASHTABLE(stream_hash, UASP_SS_EP_COMP_LOG_STREAMS);
/* BOT */
struct bot_status bot_status;

View File

@ -21,6 +21,7 @@
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/slab.h>
#include <linux/string_choices.h>
#include <linux/export.h>
#include <linux/module.h>
#include <linux/console.h>
@ -1420,10 +1421,6 @@ void gserial_disconnect(struct gserial *gser)
/* REVISIT as above: how best to track this? */
port->port_line_coding = gser->port_line_coding;
/* disable endpoints, aborting down any active I/O */
usb_ep_disable(gser->out);
usb_ep_disable(gser->in);
port->port_usb = NULL;
gser->ioport = NULL;
if (port->port.count > 0) {
@ -1435,6 +1432,10 @@ void gserial_disconnect(struct gserial *gser)
spin_unlock(&port->port_lock);
spin_unlock_irqrestore(&serial_port_lock, flags);
/* disable endpoints, aborting down any active I/O */
usb_ep_disable(gser->out);
usb_ep_disable(gser->in);
/* finally, free any unused/unusable I/O buffers */
spin_lock_irqsave(&port->port_lock, flags);
if (port->port.count == 0)
@ -1545,7 +1546,7 @@ static int __init userial_init(void)
pr_debug("%s: registered %d ttyGS* device%s\n", __func__,
MAX_U_SERIAL_PORTS,
(MAX_U_SERIAL_PORTS == 1) ? "" : "s");
str_plural(MAX_U_SERIAL_PORTS));
return status;
fail:

View File

@ -20,6 +20,7 @@
#include <linux/uaccess.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/string_choices.h>
#include <linux/poll.h>
#include <linux/kthread.h>
#include <linux/aio.h>
@ -1182,7 +1183,7 @@ ep0_fasync (int f, struct file *fd, int on)
{
struct dev_data *dev = fd->private_data;
// caller must F_SETOWN before signal delivery happens
VDEBUG (dev, "%s %s\n", __func__, on ? "on" : "off");
VDEBUG(dev, "%s %s\n", __func__, str_on_off(on));
return fasync_helper (f, fd, on, &dev->fasync);
}

View File

@ -22,6 +22,7 @@
#include <linux/usb/gadget.h>
#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/string_choices.h>
#include <linux/dma-mapping.h>
#include <linux/bcd.h>
#include <linux/version.h>
@ -219,7 +220,7 @@ static int ast_vhub_hub_dev_feature(struct ast_vhub_ep *ep,
if (wValue == USB_DEVICE_REMOTE_WAKEUP) {
ep->vhub->wakeup_en = is_set;
EPDBG(ep, "Hub remote wakeup %s\n",
is_set ? "enabled" : "disabled");
str_enabled_disabled(is_set));
return std_req_complete;
}

View File

@ -16,6 +16,7 @@
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/slab.h>
#include <linux/string_choices.h>
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/interrupt.h>
@ -131,7 +132,7 @@ static void proc_ep_show(struct seq_file *s, struct at91_ep *ep)
seq_printf(s, "csr %08x rxbytes=%d %s %s %s" EIGHTBITS "\n",
csr,
(csr & 0x07ff0000) >> 16,
(csr & (1 << 15)) ? "enabled" : "disabled",
str_enabled_disabled(csr & (1 << 15)),
(csr & (1 << 11)) ? "DATA1" : "DATA0",
types[(csr & 0x700) >> 8],

View File

@ -29,6 +29,7 @@
#include <linux/pm_runtime.h>
#include <linux/interrupt.h>
#include <linux/property.h>
#include <linux/string_choices.h>
#include <linux/dmapool.h>
#include <linux/iopoll.h>
@ -2233,12 +2234,12 @@ static int cdns2_init_eps(struct cdns2_device *pdev)
dev_dbg(pdev->dev, "Init %s, SupType: CTRL: %s, INT: %s, "
"BULK: %s, ISOC %s, SupDir IN: %s, OUT: %s\n",
pep->name,
(pep->endpoint.caps.type_control) ? "yes" : "no",
(pep->endpoint.caps.type_int) ? "yes" : "no",
(pep->endpoint.caps.type_bulk) ? "yes" : "no",
(pep->endpoint.caps.type_iso) ? "yes" : "no",
(pep->endpoint.caps.dir_in) ? "yes" : "no",
(pep->endpoint.caps.dir_out) ? "yes" : "no");
str_yes_no(pep->endpoint.caps.type_control),
str_yes_no(pep->endpoint.caps.type_int),
str_yes_no(pep->endpoint.caps.type_bulk),
str_yes_no(pep->endpoint.caps.type_iso),
str_yes_no(pep->endpoint.caps.dir_in),
str_yes_no(pep->endpoint.caps.dir_out));
INIT_LIST_HEAD(&pep->pending_list);
INIT_LIST_HEAD(&pep->deferred_list);

View File

@ -28,6 +28,7 @@
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/slab.h>
#include <linux/string_choices.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/hrtimer.h>
@ -625,7 +626,7 @@ static int dummy_enable(struct usb_ep *_ep,
desc->bEndpointAddress & 0x0f,
(desc->bEndpointAddress & USB_DIR_IN) ? "in" : "out",
usb_ep_type_string(usb_endpoint_type(desc)),
max, ep->stream_en ? "enabled" : "disabled");
max, str_enabled_disabled(ep->stream_en));
/* at this point real hardware should be NAKing transfers
* to that endpoint, until a buffer is queued to it.

View File

@ -22,6 +22,7 @@
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/string_choices.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/interrupt.h>
@ -1181,7 +1182,7 @@ static int fsl_vbus_session(struct usb_gadget *gadget, int is_active)
udc = container_of(gadget, struct fsl_udc, gadget);
spin_lock_irqsave(&udc->lock, flags);
dev_vdbg(&gadget->dev, "VBUS %s\n", is_active ? "on" : "off");
dev_vdbg(&gadget->dev, "VBUS %s\n", str_on_off(is_active));
udc->vbus_active = (is_active != 0);
if (can_pullup(udc))
fsl_writel((fsl_readl(&dr_regs->usbcmd) | USB_CMD_RUN_STOP),

View File

@ -18,6 +18,7 @@
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/string_choices.h>
#include <linux/timer.h>
#include <linux/list.h>
#include <linux/interrupt.h>
@ -1252,7 +1253,7 @@ static int omap_vbus_session(struct usb_gadget *gadget, int is_active)
udc = container_of(gadget, struct omap_udc, gadget);
spin_lock_irqsave(&udc->lock, flags);
VDBG("VBUS %s\n", is_active ? "on" : "off");
VDBG("VBUS %s\n", str_on_off(is_active));
udc->vbus_active = (is_active != 0);
if (cpu_is_omap15xx()) {
/* "software" detect, ignored if !VBUS_MODE_1510 */

View File

@ -20,6 +20,7 @@
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/slab.h>
#include <linux/string_choices.h>
#include <linux/prefetch.h>
#include <linux/byteorder/generic.h>
#include <linux/platform_data/pxa2xx_udc.h>
@ -1083,7 +1084,7 @@ static int pxa_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
is_first_req = list_empty(&ep->queue);
ep_dbg(ep, "queue req %p(first=%s), len %d buf %p\n",
_req, is_first_req ? "yes" : "no",
_req, str_yes_no(is_first_req),
_req->length, _req->buf);
if (!ep->enabled) {

View File

@ -15,6 +15,7 @@
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/string_choices.h>
#include <linux/errno.h>
#include <linux/timer.h>
#include <linux/list.h>
@ -2756,7 +2757,7 @@ static void ehci_port_power(struct oxu_hcd *oxu, int is_on)
if (!HCS_PPC(oxu->hcs_params))
return;
oxu_dbg(oxu, "...power%s ports...\n", is_on ? "up" : "down");
oxu_dbg(oxu, "...power%s ports...\n", str_up_down(is_on));
for (port = HCS_N_PORTS(oxu->hcs_params); port > 0; ) {
if (is_on)
oxu_hub_control(oxu_to_hcd(oxu), SetPortFeature,

View File

@ -48,6 +48,7 @@
#include <linux/usb/hcd.h>
#include <linux/platform_device.h>
#include <linux/prefetch.h>
#include <linux/string_choices.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
@ -98,7 +99,7 @@ static void port_power(struct sl811 *sl811, int is_on)
if (sl811->board && sl811->board->port_power) {
/* switch VBUS, at 500mA unless hub power budget gets set */
dev_dbg(hcd->self.controller, "power %s\n",
is_on ? "on" : "off");
str_on_off(is_on));
sl811->board->port_power(hcd->self.controller, is_on);
}

View File

@ -83,3 +83,9 @@
#define HCC2_CIC(p) ((p) & (1 << 5))
/* true: HC support Extended TBC Capability, Isoc burst count > 65535 */
#define HCC2_ETC(p) ((p) & (1 << 6))
/* true: HC support Extended TBC TRB Status Capability */
#define HCC2_ETC_TSC(p) ((p) & (1 << 7))
/* true: HC support Get/Set Extended Property Capability */
#define HCC2_GSC(p) ((p) & (1 << 8))
/* true: HC support Virtualization Based Trusted I/O Capability */
#define HCC2_VTC(p) ((p) & (1 << 9))

View File

@ -957,7 +957,7 @@ static void xhci_dbc_handle_events(struct work_struct *work)
/* set fast poll rate if there are pending data transfers */
if (!list_empty(&dbc->eps[BULK_OUT].list_pending) ||
!list_empty(&dbc->eps[BULK_IN].list_pending))
poll_interval = 1;
poll_interval = 0;
break;
default:
dev_info(dbc->dev, "stop handling dbc events\n");

View File

@ -110,15 +110,74 @@ static void dbc_start_rx(struct dbc_port *port)
}
}
/*
* Queue received data to tty buffer and push it.
*
* Returns nr of remaining bytes that didn't fit tty buffer, i.e. 0 if all
* bytes sucessfullt moved. In case of error returns negative errno.
* Call with lock held
*/
static int dbc_rx_push_buffer(struct dbc_port *port, struct dbc_request *req)
{
char *packet = req->buf;
unsigned int n, size = req->actual;
int count;
if (!req->actual)
return 0;
/* if n_read is set then request was partially moved to tty buffer */
n = port->n_read;
if (n) {
packet += n;
size -= n;
}
count = tty_insert_flip_string(&port->port, packet, size);
if (count)
tty_flip_buffer_push(&port->port);
if (count != size) {
port->n_read += count;
return size - count;
}
port->n_read = 0;
return 0;
}
static void
dbc_read_complete(struct xhci_dbc *dbc, struct dbc_request *req)
{
unsigned long flags;
struct dbc_port *port = dbc_to_port(dbc);
struct tty_struct *tty;
int untransferred;
tty = port->port.tty;
spin_lock_irqsave(&port->port_lock, flags);
/*
* Only defer copyig data to tty buffer in case:
* - !list_empty(&port->read_queue), there are older pending data
* - tty is throttled
* - failed to copy all data to buffer, defer remaining part
*/
if (list_empty(&port->read_queue) && tty && !tty_throttled(tty)) {
untransferred = dbc_rx_push_buffer(port, req);
if (untransferred == 0) {
list_add_tail(&req->list_pool, &port->read_pool);
if (req->status != -ESHUTDOWN)
dbc_start_rx(port);
goto out;
}
}
/* defer moving data from req to tty buffer to a tasklet */
list_add_tail(&req->list_pool, &port->read_queue);
tasklet_schedule(&port->push);
out:
spin_unlock_irqrestore(&port->port_lock, flags);
}
@ -331,10 +390,10 @@ static void dbc_rx_push(struct tasklet_struct *t)
struct dbc_request *req;
struct tty_struct *tty;
unsigned long flags;
bool do_push = false;
bool disconnect = false;
struct dbc_port *port = from_tasklet(port, t, push);
struct list_head *queue = &port->read_queue;
int untransferred;
spin_lock_irqsave(&port->port_lock, flags);
tty = port->port.tty;
@ -356,42 +415,15 @@ static void dbc_rx_push(struct tasklet_struct *t)
break;
}
if (req->actual) {
char *packet = req->buf;
unsigned int n, size = req->actual;
int count;
n = port->n_read;
if (n) {
packet += n;
size -= n;
}
count = tty_insert_flip_string(&port->port, packet,
size);
if (count)
do_push = true;
if (count != size) {
port->n_read += count;
break;
}
port->n_read = 0;
}
untransferred = dbc_rx_push_buffer(port, req);
if (untransferred > 0)
break;
list_move_tail(&req->list_pool, &port->read_pool);
}
if (do_push)
tty_flip_buffer_push(&port->port);
if (!list_empty(queue) && tty) {
if (!tty_throttled(tty)) {
if (do_push)
tasklet_schedule(&port->push);
else
pr_warn("ttyDBC0: RX not scheduled?\n");
}
}
if (!list_empty(queue))
tasklet_schedule(&port->push);
if (!disconnect)
dbc_start_rx(port);

View File

@ -330,6 +330,8 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s
usb3_hcd->can_do_streams = 1;
if (xhci->shared_hcd) {
xhci->shared_hcd->rsrc_start = hcd->rsrc_start;
xhci->shared_hcd->rsrc_len = hcd->rsrc_len;
ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED);
if (ret)
goto put_usb3_hcd;
@ -567,6 +569,7 @@ EXPORT_SYMBOL_GPL(xhci_plat_pm_ops);
static const struct acpi_device_id usb_xhci_acpi_match[] = {
/* XHCI-compliant USB Controller */
{ "PNP0D10", },
{ "PNP0D15", },
{ }
};
MODULE_DEVICE_TABLE(acpi, usb_xhci_acpi_match);

View File

@ -55,6 +55,7 @@
#include <linux/jiffies.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include <linux/string_choices.h>
#include <linux/dma-mapping.h>
#include "xhci.h"
#include "xhci-trace.h"
@ -422,7 +423,8 @@ static void xhci_handle_stopped_cmd_ring(struct xhci_hcd *xhci,
if ((xhci->cmd_ring->dequeue != xhci->cmd_ring->enqueue) &&
!(xhci->xhc_state & XHCI_STATE_DYING)) {
xhci->current_cmd = cur_cmd;
xhci_mod_cmd_timer(xhci);
if (cur_cmd)
xhci_mod_cmd_timer(xhci);
xhci_ring_cmd_db(xhci);
}
}
@ -1649,12 +1651,13 @@ static void xhci_handle_cmd_nec_get_fw(struct xhci_hcd *xhci,
NEC_FW_MINOR(le32_to_cpu(event->status)));
}
static void xhci_complete_del_and_free_cmd(struct xhci_command *cmd, u32 status)
static void xhci_complete_del_and_free_cmd(struct xhci_command *cmd, u32 comp_code, u32 comp_param)
{
list_del(&cmd->cmd_list);
if (cmd->completion) {
cmd->status = status;
cmd->status = comp_code;
cmd->comp_param = comp_param;
complete(cmd->completion);
} else {
kfree(cmd);
@ -1666,7 +1669,7 @@ void xhci_cleanup_command_queue(struct xhci_hcd *xhci)
struct xhci_command *cur_cmd, *tmp_cmd;
xhci->current_cmd = NULL;
list_for_each_entry_safe(cur_cmd, tmp_cmd, &xhci->cmd_list, cmd_list)
xhci_complete_del_and_free_cmd(cur_cmd, COMP_COMMAND_ABORTED);
xhci_complete_del_and_free_cmd(cur_cmd, COMP_COMMAND_ABORTED, 0);
}
void xhci_handle_command_timeout(struct work_struct *work)
@ -1751,6 +1754,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
struct xhci_event_cmd *event)
{
unsigned int slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
u32 status = le32_to_cpu(event->status);
u64 cmd_dma;
dma_addr_t cmd_dequeue_dma;
u32 cmd_comp_code;
@ -1879,7 +1883,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
}
event_handled:
xhci_complete_del_and_free_cmd(cmd, cmd_comp_code);
xhci_complete_del_and_free_cmd(cmd, cmd_comp_code, COMP_PARAM(status));
inc_deq(xhci, xhci->cmd_ring);
}
@ -3438,8 +3442,8 @@ static void check_interval(struct urb *urb, struct xhci_ep_ctx *ep_ctx)
if (xhci_interval != ep_interval) {
dev_dbg_ratelimited(&urb->dev->dev,
"Driver uses different interval (%d microframe%s) than xHCI (%d microframe%s)\n",
ep_interval, ep_interval == 1 ? "" : "s",
xhci_interval, xhci_interval == 1 ? "" : "s");
ep_interval, str_plural(ep_interval),
xhci_interval, str_plural(xhci_interval));
urb->interval = xhci_interval;
/* Convert back to frames for LS/FS devices */
if (urb->dev->speed == USB_SPEED_LOW ||

View File

@ -26,6 +26,7 @@
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include <linux/slab.h>
#include <linux/string_choices.h>
#include <linux/usb/otg.h>
#include <linux/usb/phy.h>
#include <linux/usb/role.h>
@ -724,7 +725,7 @@ static void tegra_xusb_mbox_handle(struct tegra_xusb *tegra,
if (err < 0) {
dev_err(dev,
"failed to %s LFPS detection on USB3#%u: %d\n",
enable ? "enable" : "disable", port, err);
str_enable_disable(enable), port, err);
rsp.cmd = MBOX_CMD_NAK;
} else {
rsp.cmd = MBOX_CMD_ACK;
@ -1349,7 +1350,7 @@ static void tegra_xhci_id_work(struct work_struct *work)
u32 status;
int ret;
dev_dbg(tegra->dev, "host mode %s\n", tegra->host_mode ? "on" : "off");
dev_dbg(tegra->dev, "host mode %s\n", str_on_off(tegra->host_mode));
mutex_lock(&tegra->lock);
@ -1667,7 +1668,7 @@ static int tegra_xusb_probe(struct platform_device *pdev)
goto put_padctl;
}
if (!of_property_read_bool(pdev->dev.of_node, "power-domains")) {
if (!of_property_present(pdev->dev.of_node, "power-domains")) {
tegra->host_rst = devm_reset_control_get(&pdev->dev,
"xusb_host");
if (IS_ERR(tegra->host_rst)) {

View File

@ -17,6 +17,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/string_choices.h>
#include <linux/dmi.h>
#include <linux/dma-mapping.h>
@ -4523,7 +4524,7 @@ static int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
hlpm_addr = ports[port_num]->addr + PORTHLPMC;
xhci_dbg(xhci, "%s port %d USB2 hardware LPM\n",
enable ? "enable" : "disable", port_num + 1);
str_enable_disable(enable), port_num + 1);
if (enable) {
/* Host supports BESL timeout instead of HIRD */

View File

@ -529,6 +529,7 @@ struct xhci_command {
/* Input context for changing device state */
struct xhci_container_ctx *in_ctx;
u32 status;
u32 comp_param;
int slot_id;
/* If completion is null, no one is waiting on this command
* and the structure can be freed after the command completes.
@ -959,6 +960,9 @@ struct xhci_event_cmd {
__le32 flags;
};
/* status bitmasks */
#define COMP_PARAM(p) ((p) & 0xffffff) /* Command Completion Parameter */
/* Address device - disable SetAddress */
#define TRB_BSR (1<<9)

View File

@ -7,6 +7,7 @@
* Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
*/
#include <linux/string_choices.h>
#include <linux/uaccess.h>
#include "mtu3.h"
@ -479,7 +480,7 @@ static int ssusb_vbus_show(struct seq_file *sf, void *unused)
struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
seq_printf(sf, "vbus state: %s\n(echo on/off)\n",
regulator_is_enabled(otg_sx->vbus) ? "on" : "off");
str_on_off(regulator_is_enabled(otg_sx->vbus)));
return 0;
}

View File

@ -7,6 +7,7 @@
* Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
*/
#include <linux/string_choices.h>
#include "mtu3.h"
#include "mtu3_dr.h"
#include "mtu3_debug.h"
@ -109,7 +110,7 @@ int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on)
if (!vbus)
return 0;
dev_dbg(ssusb->dev, "%s: turn %s\n", __func__, is_on ? "on" : "off");
dev_dbg(ssusb->dev, "%s: turn %s\n", __func__, str_on_off(is_on));
if (is_on) {
ret = regulator_enable(vbus);

View File

@ -7,6 +7,7 @@
* Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
*/
#include <linux/string_choices.h>
#include "mtu3.h"
#include "mtu3_trace.h"
@ -490,7 +491,7 @@ static int mtu3_gadget_pullup(struct usb_gadget *gadget, int is_on)
unsigned long flags;
dev_dbg(mtu->dev, "%s (%s) for %sactive device\n", __func__,
is_on ? "on" : "off", mtu->is_active ? "" : "in");
str_on_off(is_on), mtu->is_active ? "" : "in");
pm_runtime_get_sync(mtu->dev);

View File

@ -21,6 +21,7 @@
#include <linux/of_platform.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/string_choices.h>
#include <linux/dma-mapping.h>
#include <linux/usb/usb_phy_generic.h>
@ -306,7 +307,7 @@ static irqreturn_t da8xx_musb_interrupt(int irq, void *hci)
}
dev_dbg(musb->controller, "VBUS %s (%s)%s, devctl %02x\n",
drvvbus ? "on" : "off",
str_on_off(drvvbus),
usb_otg_state_string(musb->xceiv->otg->state),
err ? " ERROR" : "",
devctl);

View File

@ -72,6 +72,7 @@
#include <linux/kobject.h>
#include <linux/prefetch.h>
#include <linux/platform_device.h>
#include <linux/string_choices.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/dma-mapping.h>
@ -1937,7 +1938,7 @@ vbus_show(struct device *dev, struct device_attribute *attr, char *buf)
pm_runtime_put_sync(dev);
return sprintf(buf, "Vbus %s, timeout %lu msec\n",
vbus ? "on" : "off", val);
str_on_off(vbus), val);
}
static DEVICE_ATTR_RW(vbus);

View File

@ -24,6 +24,7 @@
#include <linux/usb/usb_phy_generic.h>
#include <linux/platform_data/usb-omap.h>
#include <linux/sizes.h>
#include <linux/string_choices.h>
#include <linux/of.h>
#include <linux/of_address.h>
@ -378,7 +379,7 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
/* NOTE: this must complete power-on within 100 ms. */
dev_dbg(musb->controller, "VBUS %s (%s)%s, devctl %02x\n",
drvvbus ? "on" : "off",
str_on_off(drvvbus),
usb_otg_state_string(musb->xceiv->otg->state),
err ? " ERROR" : "",
devctl);

View File

@ -14,6 +14,7 @@
#include <linux/module.h>
#include <linux/smp.h>
#include <linux/spinlock.h>
#include <linux/string_choices.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
@ -1606,7 +1607,7 @@ static void musb_pullup(struct musb *musb, int is_on)
/* FIXME if on, HdrcStart; if off, HdrcStop */
musb_dbg(musb, "gadget D+ pullup %s",
is_on ? "on" : "off");
str_on_off(is_on));
musb_writeb(musb->mregs, MUSB_POWER, power);
}

View File

@ -13,6 +13,7 @@
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/string_choices.h>
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/dma-mapping.h>
@ -1028,7 +1029,7 @@ static bool musb_h_ep0_continue(struct musb *musb, u16 len, struct urb *urb)
+ urb->actual_length);
musb_dbg(musb, "Sending %d byte%s to ep0 fifo %p",
fifo_count,
(fifo_count == 1) ? "" : "s",
str_plural(fifo_count),
fifo_dest);
musb_write_fifo(hw_ep, fifo_count, fifo_dest);

View File

@ -12,6 +12,7 @@
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/string_choices.h>
#include <linux/proc_fs.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
@ -529,7 +530,7 @@ int fsl_otg_start_gadget(struct otg_fsm *fsm, int on)
if (!otg->gadget || !otg->gadget->dev.parent)
return -ENODEV;
VDBG("gadget %s\n", on ? "on" : "off");
VDBG("gadget %s\n", str_on_off(on));
dev = otg->gadget->dev.parent;
if (on) {

View File

@ -15,6 +15,7 @@
#include <linux/clk.h>
#include <linux/workqueue.h>
#include <linux/platform_device.h>
#include <linux/string_choices.h>
#include <linux/usb.h>
#include <linux/usb/ch9.h>
@ -217,7 +218,7 @@ static void mv_otg_start_periphrals(struct mv_otg *mvotg, int on)
if (!otg->gadget)
return;
dev_info(mvotg->phy.dev, "gadget %s\n", on ? "on" : "off");
dev_info(mvotg->phy.dev, "gadget %s\n", str_on_off(on));
if (on)
usb_gadget_vbus_connect(otg->gadget);

View File

@ -18,6 +18,7 @@
#include <linux/extcon-provider.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/string_choices.h>
#include <linux/usb/otg.h>
#include <linux/mfd/retu.h>
#include <linux/usb/gadget.h>
@ -63,7 +64,7 @@ static ssize_t vbus_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct tahvo_usb *tu = dev_get_drvdata(device);
return sprintf(buf, "%s\n", tu->vbus_state ? "on" : "off");
return sprintf(buf, "%s\n", str_on_off(tu->vbus_state));
}
static DEVICE_ATTR_RO(vbus);

View File

@ -346,13 +346,6 @@ static void devm_usb_phy_release2(struct device *dev, void *_res)
usb_put_phy(res->phy);
}
static int devm_usb_phy_match(struct device *dev, void *res, void *match_data)
{
struct usb_phy **phy = res;
return *phy == match_data;
}
static void usb_charger_init(struct usb_phy *usb_phy)
{
usb_phy->chg_type = UNKNOWN_TYPE;
@ -614,25 +607,6 @@ struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev,
}
EXPORT_SYMBOL_GPL(devm_usb_get_phy_by_phandle);
/**
* devm_usb_put_phy - release the USB PHY
* @dev: device that wants to release this phy
* @phy: the phy returned by devm_usb_get_phy()
*
* destroys the devres associated with this phy and invokes usb_put_phy
* to release the phy.
*
* For use by USB host and peripheral drivers.
*/
void devm_usb_put_phy(struct device *dev, struct usb_phy *phy)
{
int r;
r = devres_release(dev, devm_usb_phy_release, devm_usb_phy_match, phy);
dev_WARN_ONCE(dev, r, "couldn't find PHY resource\n");
}
EXPORT_SYMBOL_GPL(devm_usb_put_phy);
/**
* usb_put_phy - release the USB PHY
* @x: the phy returned by usb_get_phy()

View File

@ -63,6 +63,7 @@
#define CH341_REG_DIVISOR 0x13
#define CH341_REG_LCR 0x18
#define CH341_REG_LCR2 0x25
#define CH341_REG_FLOW_CTL 0x27
#define CH341_NBREAK_BITS 0x01
@ -77,6 +78,9 @@
#define CH341_LCR_CS6 0x01
#define CH341_LCR_CS5 0x00
#define CH341_FLOW_CTL_NONE 0x00
#define CH341_FLOW_CTL_RTSCTS 0x01
#define CH341_QUIRK_LIMITED_PRESCALER BIT(0)
#define CH341_QUIRK_SIMULATE_BREAK BIT(1)
@ -478,6 +482,28 @@ err_kill_interrupt_urb:
return r;
}
static void ch341_set_flow_control(struct tty_struct *tty,
struct usb_serial_port *port,
const struct ktermios *old_termios)
{
u16 flow_ctl;
int r;
if (C_CRTSCTS(tty))
flow_ctl = CH341_FLOW_CTL_RTSCTS;
else
flow_ctl = CH341_FLOW_CTL_NONE;
r = ch341_control_out(port->serial->dev,
CH341_REQ_WRITE_REG,
(CH341_REG_FLOW_CTL << 8) | CH341_REG_FLOW_CTL,
(flow_ctl << 8) | flow_ctl);
if (r < 0 && old_termios) {
tty->termios.c_cflag &= ~CRTSCTS;
tty->termios.c_cflag |= (old_termios->c_cflag & CRTSCTS);
}
}
/* Old_termios contains the original termios settings and
* tty->termios contains the new setting to be used.
*/
@ -546,6 +572,8 @@ static void ch341_set_termios(struct tty_struct *tty,
spin_unlock_irqrestore(&priv->lock, flags);
ch341_set_handshake(port->serial->dev, priv->mcr);
ch341_set_flow_control(tty, port, old_termios);
}
/*
@ -632,13 +660,12 @@ restore:
static int ch341_break_ctl(struct tty_struct *tty, int break_state)
{
const uint16_t ch341_break_reg =
((uint16_t) CH341_REG_LCR << 8) | CH341_REG_BREAK;
const u16 ch341_break_reg = (CH341_REG_LCR << 8) | CH341_REG_BREAK;
struct usb_serial_port *port = tty->driver_data;
struct ch341_private *priv = usb_get_serial_port_data(port);
u16 reg_contents;
u8 break_reg[2];
int r;
uint16_t reg_contents;
uint8_t break_reg[2];
if (priv->quirks & CH341_QUIRK_SIMULATE_BREAK)
return ch341_simulate_break(tty, break_state);

Some files were not shown because too many files have changed in this diff Show More