mirror of
git://git.yoctoproject.org/linux-yocto.git
synced 2025-10-22 15:03:53 +02:00
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:
commit
cc8b10fa70
|
@ -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 ]
|
||||
|
|
|
@ -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";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
|
|
|
@ -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 = <®_1v2_usb>;
|
||||
vdd2-supply = <®_3v3_usb>;
|
||||
compatible = "usb4b4,6504";
|
||||
reg = <1>;
|
||||
peer-hub = <&hub_3_0>;
|
||||
reset-gpios = <&gpio1 11 GPIO_ACTIVE_LOW>;
|
||||
vdd-supply = <®_1v2_usb>;
|
||||
vdd2-supply = <®_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 = <®_1v2_usb>;
|
||||
vdd2-supply = <®_3v3_usb>;
|
||||
compatible = "usb4b4,6506";
|
||||
reg = <2>;
|
||||
peer-hub = <&hub_2_0>;
|
||||
reset-gpios = <&gpio1 11 GPIO_ACTIVE_LOW>;
|
||||
vdd-supply = <®_1v2_usb>;
|
||||
vdd2-supply = <®_3v3_usb>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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";
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
||||
|
|
|
@ -20,6 +20,7 @@ properties:
|
|||
items:
|
||||
- enum:
|
||||
- nxp,cbdtu02043
|
||||
- onnn,fsusb42
|
||||
- onnn,fsusb43l10x
|
||||
- pericom,pi3usb102
|
||||
- ti,tmuxhs4212
|
||||
|
|
|
@ -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";
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -189,7 +189,7 @@ examples:
|
|||
#size-cells = <0>;
|
||||
|
||||
ethernet@1 {
|
||||
compatible = "usb955,9ff";
|
||||
reg = <1>;
|
||||
compatible = "usb955,9ff";
|
||||
reg = <1>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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:
|
||||
|
|
415
Documentation/devicetree/bindings/usb/snps,dwc3-common.yaml
Normal file
415
Documentation/devicetree/bindings/usb/snps,dwc3-common.yaml
Normal 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
|
||||
...
|
||||
|
|
@ -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:
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
10
MAINTAINERS
10
MAINTAINERS
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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. */
|
||||
|
|
373
drivers/platform/chrome/cros_typec_altmode.c
Normal file
373
drivers/platform/chrome/cros_typec_altmode.c
Normal 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
|
51
drivers/platform/chrome/cros_typec_altmode.h
Normal file
51
drivers/platform/chrome/cros_typec_altmode.h
Normal 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__ */
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -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[] = {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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],
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 ||
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue
Block a user