Commit 79160a60 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull USB / Thunderbolt updates from Greg KH:
 "Here is the big set of USB and Thunderbolt patches for 5.14-rc1.

  Nothing major here just lots of little changes for new hardware and
  features. Highlights are:

   - more USB 4 support added to the thunderbolt core

   - build warning fixes all over the place

   - usb-serial driver updates and new device support

   - mtu3 driver updates

   - gadget driver updates

   - dwc3 driver updates

   - dwc2 driver updates

   - isp1760 host driver updates

   - musb driver updates

   - lots of other tiny things.

  Full details are in the shortlog.

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

* tag 'usb-5.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (223 commits)
  phy: qcom-qusb2: Add configuration for SM4250 and SM6115
  dt-bindings: phy: qcom,qusb2: document sm4250/6115 compatible
  dt-bindings: usb: qcom,dwc3: Add bindings for sm6115/4250
  USB: cdc-acm: blacklist Heimann USB Appset device
  usb: xhci-mtk: allow multiple Start-Split in a microframe
  usb: ftdi-elan: remove redundant continue statement in a while-loop
  usb: class: cdc-wdm: return the correct errno code
  xhci: remove redundant continue statement
  usb: dwc3: Fix debugfs creation flow
  usb: gadget: hid: fix error return code in hid_bind()
  usb: gadget: eem: fix echo command packet response issue
  usb: gadget: f_hid: fix endianness issue with descriptors
  Revert "USB: misc: Add onboard_usb_hub driver"
  Revert "of/platform: Add stubs for of_platform_device_create/destroy()"
  Revert "usb: host: xhci-plat: Create platform device for onboard hubs in probe()"
  Revert "arm64: dts: qcom: sc7180-trogdor: Add nodes for onboard USB hub"
  xhci: solve a double free problem while doing s4
  xhci: handle failed buffer copy to URB sg list and fix a W=1 copiler warning
  xhci: Add adaptive interrupt rate for isoch TRBs with XHCI_AVOID_BEI quirk
  xhci: Remove unused defines for ERST_SIZE and ERST_ENTRIES
  ...
parents c932ed0a 7756f1d6
...@@ -8,6 +8,8 @@ Description: ...@@ -8,6 +8,8 @@ Description:
c_chmask capture channel mask c_chmask capture channel mask
c_srate capture sampling rate c_srate capture sampling rate
c_ssize capture sample size (bytes) c_ssize capture sample size (bytes)
c_sync capture synchronization type (async/adaptive)
fb_max maximum extra bandwidth in async mode
p_chmask playback channel mask p_chmask playback channel mask
p_srate playback sampling rate p_srate playback sampling rate
p_ssize playback sample size (bytes) p_ssize playback sample size (bytes)
......
What: /sys/bus/thunderbolt/devices/.../domainX/boot_acl What: /sys/bus/thunderbolt/devices/.../domainX/boot_acl
Date: Jun 2018 Date: Jun 2018
KernelVersion: 4.17 KernelVersion: 4.17
Contact: thunderbolt-software@lists.01.org Contact: thunderbolt-software@lists.01.org
...@@ -21,7 +21,7 @@ Description: Holds a comma separated list of device unique_ids that ...@@ -21,7 +21,7 @@ Description: Holds a comma separated list of device unique_ids that
If a device is authorized automatically during boot its If a device is authorized automatically during boot its
boot attribute is set to 1. boot attribute is set to 1.
What: /sys/bus/thunderbolt/devices/.../domainX/deauthorization What: /sys/bus/thunderbolt/devices/.../domainX/deauthorization
Date: May 2021 Date: May 2021
KernelVersion: 5.12 KernelVersion: 5.12
Contact: Mika Westerberg <mika.westerberg@linux.intel.com> Contact: Mika Westerberg <mika.westerberg@linux.intel.com>
...@@ -30,7 +30,7 @@ Description: This attribute tells whether the system supports ...@@ -30,7 +30,7 @@ Description: This attribute tells whether the system supports
de-authorize PCIe tunnel by writing 0 to authorized de-authorize PCIe tunnel by writing 0 to authorized
attribute under each device. attribute under each device.
What: /sys/bus/thunderbolt/devices/.../domainX/iommu_dma_protection What: /sys/bus/thunderbolt/devices/.../domainX/iommu_dma_protection
Date: Mar 2019 Date: Mar 2019
KernelVersion: 4.21 KernelVersion: 4.21
Contact: thunderbolt-software@lists.01.org Contact: thunderbolt-software@lists.01.org
...@@ -39,7 +39,7 @@ Description: This attribute tells whether the system uses IOMMU ...@@ -39,7 +39,7 @@ Description: This attribute tells whether the system uses IOMMU
it is not (DMA protection is solely based on Thunderbolt it is not (DMA protection is solely based on Thunderbolt
security levels). security levels).
What: /sys/bus/thunderbolt/devices/.../domainX/security What: /sys/bus/thunderbolt/devices/.../domainX/security
Date: Sep 2017 Date: Sep 2017
KernelVersion: 4.13 KernelVersion: 4.13
Contact: thunderbolt-software@lists.01.org Contact: thunderbolt-software@lists.01.org
...@@ -61,7 +61,7 @@ Description: This attribute holds current Thunderbolt security level ...@@ -61,7 +61,7 @@ Description: This attribute holds current Thunderbolt security level
the BIOS. the BIOS.
======= ================================================== ======= ==================================================
What: /sys/bus/thunderbolt/devices/.../authorized What: /sys/bus/thunderbolt/devices/.../authorized
Date: Sep 2017 Date: Sep 2017
KernelVersion: 4.13 KernelVersion: 4.13
Contact: thunderbolt-software@lists.01.org Contact: thunderbolt-software@lists.01.org
...@@ -95,14 +95,14 @@ Description: This attribute is used to authorize Thunderbolt devices ...@@ -95,14 +95,14 @@ Description: This attribute is used to authorize Thunderbolt devices
EKEYREJECTED if the challenge response did not match. EKEYREJECTED if the challenge response did not match.
== ======================================================== == ========================================================
What: /sys/bus/thunderbolt/devices/.../boot What: /sys/bus/thunderbolt/devices/.../boot
Date: Jun 2018 Date: Jun 2018
KernelVersion: 4.17 KernelVersion: 4.17
Contact: thunderbolt-software@lists.01.org Contact: thunderbolt-software@lists.01.org
Description: This attribute contains 1 if Thunderbolt device was already Description: This attribute contains 1 if Thunderbolt device was already
authorized on boot and 0 otherwise. authorized on boot and 0 otherwise.
What: /sys/bus/thunderbolt/devices/.../generation What: /sys/bus/thunderbolt/devices/.../generation
Date: Jan 2020 Date: Jan 2020
KernelVersion: 5.5 KernelVersion: 5.5
Contact: Christian Kellner <christian@kellner.me> Contact: Christian Kellner <christian@kellner.me>
...@@ -110,7 +110,7 @@ Description: This attribute contains the generation of the Thunderbolt ...@@ -110,7 +110,7 @@ Description: This attribute contains the generation of the Thunderbolt
controller associated with the device. It will contain 4 controller associated with the device. It will contain 4
for USB4. for USB4.
What: /sys/bus/thunderbolt/devices/.../key What: /sys/bus/thunderbolt/devices/.../key
Date: Sep 2017 Date: Sep 2017
KernelVersion: 4.13 KernelVersion: 4.13
Contact: thunderbolt-software@lists.01.org Contact: thunderbolt-software@lists.01.org
...@@ -213,12 +213,15 @@ Description: When new NVM image is written to the non-active NVM ...@@ -213,12 +213,15 @@ Description: When new NVM image is written to the non-active NVM
restarted with the new NVM firmware. If the image restarted with the new NVM firmware. If the image
verification fails an error code is returned instead. verification fails an error code is returned instead.
This file will accept writing values "1" or "2" This file will accept writing values "1", "2" or "3".
- Writing "1" will flush the image to the storage - Writing "1" will flush the image to the storage
area and authenticate the image in one action. area and authenticate the image in one action.
- Writing "2" will run some basic validation on the image - Writing "2" will run some basic validation on the image
and flush it to the storage area. and flush it to the storage area.
- Writing "3" will authenticate the image that is
currently written in the storage area. This is only
supported with USB4 devices and retimers.
When read holds status of the last authentication When read holds status of the last authentication
operation if an error occurred during the process. This operation if an error occurred during the process. This
...@@ -226,6 +229,20 @@ Description: When new NVM image is written to the non-active NVM ...@@ -226,6 +229,20 @@ Description: When new NVM image is written to the non-active NVM
based mailbox before the device is power cycled. Writing based mailbox before the device is power cycled. Writing
0 here clears the status. 0 here clears the status.
What: /sys/bus/thunderbolt/devices/.../nvm_authenticate_on_disconnect
Date: Oct 2020
KernelVersion: v5.9
Contact: Mario Limonciello <mario.limonciello@dell.com>
Description: For supported devices, automatically authenticate the new Thunderbolt
image when the device is disconnected from the host system.
This file will accept writing values "1" or "2"
- Writing "1" will flush the image to the storage
area and prepare the device for authentication on disconnect.
- Writing "2" will run some basic validation on the image
and flush it to the storage area.
What: /sys/bus/thunderbolt/devices/<xdomain>.<service>/key What: /sys/bus/thunderbolt/devices/<xdomain>.<service>/key
Date: Jan 2018 Date: Jan 2018
KernelVersion: 4.15 KernelVersion: 4.15
...@@ -276,6 +293,39 @@ Contact: thunderbolt-software@lists.01.org ...@@ -276,6 +293,39 @@ Contact: thunderbolt-software@lists.01.org
Description: This contains XDomain service specific settings as Description: This contains XDomain service specific settings as
bitmask. Format: %x bitmask. Format: %x
What: /sys/bus/thunderbolt/devices/usb4_portX/link
Date: Sep 2021
KernelVersion: v5.14
Contact: Mika Westerberg <mika.westerberg@linux.intel.com>
Description: Returns the current link mode. Possible values are
"usb4", "tbt" and "none".
What: /sys/bus/thunderbolt/devices/usb4_portX/offline
Date: Sep 2021
KernelVersion: v5.14
Contact: Rajmohan Mani <rajmohan.mani@intel.com>
Description: Writing 1 to this attribute puts the USB4 port into
offline mode. Only allowed when there is nothing
connected to the port (link attribute returns "none").
Once the port is in offline mode it does not receive any
hotplug events. This is used to update NVM firmware of
on-board retimers. Writing 0 puts the port back to
online mode.
This attribute is only visible if the platform supports
powering on retimers when there is no cable connected.
What: /sys/bus/thunderbolt/devices/usb4_portX/rescan
Date: Sep 2021
KernelVersion: v5.14
Contact: Rajmohan Mani <rajmohan.mani@intel.com>
Description: When the USB4 port is in offline mode writing 1 to this
attribute forces rescan of the sideband for on-board
retimers. Each retimer appear under the USB4 port as if
the USB4 link was up. These retimers act in the same way
as if the cable was connected so upgrading their NVM
firmware can be done the usual way.
What: /sys/bus/thunderbolt/devices/<device>:<port>.<index>/device What: /sys/bus/thunderbolt/devices/<device>:<port>.<index>/device
Date: Oct 2020 Date: Oct 2020
KernelVersion: v5.9 KernelVersion: v5.9
...@@ -308,17 +358,3 @@ Date: Oct 2020 ...@@ -308,17 +358,3 @@ Date: Oct 2020
KernelVersion: v5.9 KernelVersion: v5.9
Contact: Mika Westerberg <mika.westerberg@linux.intel.com> Contact: Mika Westerberg <mika.westerberg@linux.intel.com>
Description: Retimer vendor identifier read from the hardware. Description: Retimer vendor identifier read from the hardware.
What: /sys/bus/thunderbolt/devices/.../nvm_authenticate_on_disconnect
Date: Oct 2020
KernelVersion: v5.9
Contact: Mario Limonciello <mario.limonciello@dell.com>
Description: For supported devices, automatically authenticate the new Thunderbolt
image when the device is disconnected from the host system.
This file will accept writing values "1" or "2"
- Writing "1" will flush the image to the storage
area and prepare the device for authentication on disconnect.
- Writing "2" will run some basic validation on the image
and flush it to the storage area.
...@@ -154,17 +154,6 @@ Description: ...@@ -154,17 +154,6 @@ Description:
files hold a string value (enable or disable) indicating whether files hold a string value (enable or disable) indicating whether
or not USB3 hardware LPM U1 or U2 is enabled for the device. or not USB3 hardware LPM U1 or U2 is enabled for the device.
What: /sys/bus/usb/devices/.../removable
Date: February 2012
Contact: Matthew Garrett <mjg@redhat.com>
Description:
Some information about whether a given USB device is
physically fixed to the platform can be inferred from a
combination of hub descriptor bits and platform-specific data
such as ACPI. This file will read either "removable" or
"fixed" if the information is available, and "unknown"
otherwise.
What: /sys/bus/usb/devices/.../ltm_capable What: /sys/bus/usb/devices/.../ltm_capable
Date: July 2012 Date: July 2012
Contact: Sarah Sharp <sarah.a.sharp@linux.intel.com> Contact: Sarah Sharp <sarah.a.sharp@linux.intel.com>
......
What: /sys/devices/.../removable
Date: May 2021
Contact: Rajat Jain <rajatxjain@gmail.com>
Description:
Information about whether a given device can be removed from the
platform by the user. This is determined by its subsystem in a
bus / platform-specific way. This attribute is only present for
devices that can support determining such information:
"removable": device can be removed from the platform by the user
"fixed": device is fixed to the platform / cannot be removed
by the user.
"unknown": The information is unavailable / cannot be deduced.
Currently this is only supported by USB (which infers the
information from a combination of hub descriptor bits and
platform-specific data such as ACPI) and PCI (which gets this
from ACPI / device tree).
...@@ -256,6 +256,35 @@ Note names of the NVMem devices ``nvm_activeN`` and ``nvm_non_activeN`` ...@@ -256,6 +256,35 @@ Note names of the NVMem devices ``nvm_activeN`` and ``nvm_non_activeN``
depend on the order they are registered in the NVMem subsystem. N in depend on the order they are registered in the NVMem subsystem. N in
the name is the identifier added by the NVMem subsystem. the name is the identifier added by the NVMem subsystem.
Upgrading on-board retimer NVM when there is no cable connected
---------------------------------------------------------------
If the platform supports, it may be possible to upgrade the retimer NVM
firmware even when there is nothing connected to the USB4
ports. When this is the case the ``usb4_portX`` devices have two special
attributes: ``offline`` and ``rescan``. The way to upgrade the firmware
is to first put the USB4 port into offline mode::
# echo 1 > /sys/bus/thunderbolt/devices/0-0/usb4_port1/offline
This step makes sure the port does not respond to any hotplug events,
and also ensures the retimers are powered on. The next step is to scan
for the retimers::
# echo 1 > /sys/bus/thunderbolt/devices/0-0/usb4_port1/rescan
This enumerates and adds the on-board retimers. Now retimer NVM can be
upgraded in the same way than with cable connected (see previous
section). However, the retimer is not disconnected as we are offline
mode) so after writing ``1`` to ``nvm_authenticate`` one should wait for
5 or more seconds before running rescan again::
# echo 1 > /sys/bus/thunderbolt/devices/0-0/usb4_port1/rescan
This point if everything went fine, the port can be put back to
functional state again::
# echo 0 > /sys/bus/thunderbolt/devices/0-0/usb4_port1/offline
Upgrading NVM when host controller is in safe mode Upgrading NVM when host controller is in safe mode
-------------------------------------------------- --------------------------------------------------
If the existing NVM is not properly authenticated (or is missing) the If the existing NVM is not properly authenticated (or is missing) the
......
...@@ -15,7 +15,9 @@ properties: ...@@ -15,7 +15,9 @@ properties:
const: 1 const: 1
compatible: compatible:
const: allwinner,sun8i-h3-usb-phy enum:
- allwinner,sun8i-h3-usb-phy
- allwinner,sun50i-h616-usb-phy
reg: reg:
items: items:
......
...@@ -23,6 +23,8 @@ properties: ...@@ -23,6 +23,8 @@ properties:
- qcom,msm8998-qusb2-phy - qcom,msm8998-qusb2-phy
- qcom,sdm660-qusb2-phy - qcom,sdm660-qusb2-phy
- qcom,ipq6018-qusb2-phy - qcom,ipq6018-qusb2-phy
- qcom,sm4250-qusb2-phy
- qcom,sm6115-qusb2-phy
- items: - items:
- enum: - enum:
- qcom,sc7180-qusb2-phy - qcom,sc7180-qusb2-phy
......
...@@ -22,6 +22,9 @@ properties: ...@@ -22,6 +22,9 @@ properties:
- allwinner,sun8i-a83t-musb - allwinner,sun8i-a83t-musb
- allwinner,sun50i-h6-musb - allwinner,sun50i-h6-musb
- const: allwinner,sun8i-a33-musb - const: allwinner,sun8i-a33-musb
- items:
- const: allwinner,sun50i-h616-musb
- const: allwinner,sun8i-h3-musb
reg: reg:
maxItems: 1 maxItems: 1
......
...@@ -75,6 +75,7 @@ required: ...@@ -75,6 +75,7 @@ required:
- reg - reg
- reg-names - reg-names
- interrupts - interrupts
- interrupt-names
additionalProperties: false additionalProperties: false
......
...@@ -24,6 +24,7 @@ properties: ...@@ -24,6 +24,7 @@ properties:
- rockchip,rk3188-usb - rockchip,rk3188-usb
- rockchip,rk3228-usb - rockchip,rk3228-usb
- rockchip,rk3288-usb - rockchip,rk3288-usb
- rockchip,rk3308-usb
- rockchip,rk3328-usb - rockchip,rk3328-usb
- rockchip,rk3368-usb - rockchip,rk3368-usb
- rockchip,rv1108-usb - rockchip,rv1108-usb
......
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/nxp,isp1760.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NXP ISP1760 family controller bindings
maintainers:
- Sebastian Siewior <bigeasy@linutronix.de>
- Laurent Pinchart <laurent.pinchart@ideasonboard.com>
description: |
NXP ISP1760 family, which includes ISP1760/1761/1763 devicetree controller
bindings
properties:
compatible:
enum:
- nxp,usb-isp1760
- nxp,usb-isp1761
- nxp,usb-isp1763
reg:
maxItems: 1
interrupts:
minItems: 1
maxItems: 2
items:
- description: Host controller interrupt
- description: Device controller interrupt in isp1761
interrupt-names:
minItems: 1
maxItems: 2
items:
- const: host
- const: peripheral
bus-width:
description:
Number of data lines.
enum: [8, 16, 32]
default: 32
dr_mode:
enum:
- host
- peripheral
required:
- compatible
- reg
- interrupts
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
usb@40200000 {
compatible = "nxp,usb-isp1763";
reg = <0x40200000 0x100000>;
interrupts = <GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH>;
bus-width = <16>;
dr_mode = "host";
};
...
...@@ -19,6 +19,8 @@ properties: ...@@ -19,6 +19,8 @@ properties:
- qcom,sc7280-dwc3 - qcom,sc7280-dwc3
- qcom,sdm845-dwc3 - qcom,sdm845-dwc3
- qcom,sdx55-dwc3 - qcom,sdx55-dwc3
- qcom,sm4250-dwc3
- qcom,sm6115-dwc3
- qcom,sm8150-dwc3 - qcom,sm8150-dwc3
- qcom,sm8250-dwc3 - qcom,sm8250-dwc3
- qcom,sm8350-dwc3 - qcom,sm8350-dwc3
......
# SPDX-License-Identifier: GPL-2.0-only or BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/realtek,rts5411.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Binding for the Realtek RTS5411 USB 3.0 hub controller
maintainers:
- Matthias Kaehlcke <mka@chromium.org>
allOf:
- $ref: usb-device.yaml#
properties:
compatible:
items:
- enum:
- usbbda,5411
- usbbda,411
reg: true
vdd-supply:
description:
phandle to the regulator that provides power to the hub.
companion-hub:
$ref: '/schemas/types.yaml#/definitions/phandle'
description:
phandle to the companion hub on the controller.
required:
- companion-hub
- compatible
- reg
additionalProperties: false
examples:
- |
usb {
dr_mode = "host";
#address-cells = <1>;
#size-cells = <0>;
/* 2.0 hub on port 1 */
hub_2_0: hub@1 {
compatible = "usbbda,5411";
reg = <1>;
vdd-supply = <&pp3300_hub>;
companion-hub = <&hub_3_0>;
};
/* 3.0 hub on port 2 */
hub_3_0: hub@2 {
compatible = "usbbda,411";
reg = <2>;
vdd-supply = <&pp3300_hub>;
companion-hub = <&hub_2_0>;
};
};
...@@ -61,6 +61,9 @@ USB-specific: ...@@ -61,6 +61,9 @@ USB-specific:
(c) requested data transfer length is invalid: negative (c) requested data transfer length is invalid: negative
or too large for the host controller. or too large for the host controller.
``-EBADR`` The wLength value in a control URB's setup packet does
not match the URB's transfer_buffer_length.
``-ENOSPC`` This request would overcommit the usb bandwidth reserved ``-ENOSPC`` This request would overcommit the usb bandwidth reserved
for periodic transfers (interrupt, isochronous). for periodic transfers (interrupt, isochronous).
......
...@@ -728,6 +728,8 @@ The uac2 function provides these attributes in its function directory: ...@@ -728,6 +728,8 @@ The uac2 function provides these attributes in its function directory:
c_chmask capture channel mask c_chmask capture channel mask
c_srate capture sampling rate c_srate capture sampling rate
c_ssize capture sample size (bytes) c_ssize capture sample size (bytes)
c_sync capture synchronization type (async/adaptive)
fb_max maximum extra bandwidth in async mode
p_chmask playback channel mask p_chmask playback channel mask
p_srate playback sampling rate p_srate playback sampling rate
p_ssize playback sample size (bytes) p_ssize playback sample size (bytes)
......
...@@ -148,7 +148,7 @@ ethernet: ethernet@4e000000 { ...@@ -148,7 +148,7 @@ ethernet: ethernet@4e000000 {
usb: usb@4f000000 { usb: usb@4f000000 {
compatible = "nxp,usb-isp1761"; compatible = "nxp,usb-isp1761";
reg = <0x4f000000 0x20000>; reg = <0x4f000000 0x20000>;
port1-otg; dr_mode = "peripheral";
}; };
bridge { bridge {
......
...@@ -166,7 +166,7 @@ usb@3b000000 { ...@@ -166,7 +166,7 @@ usb@3b000000 {
reg = <0x3b000000 0x20000>; reg = <0x3b000000 0x20000>;
interrupt-parent = <&intc_fpga1176>; interrupt-parent = <&intc_fpga1176>;
interrupts = <0 11 IRQ_TYPE_LEVEL_HIGH>; interrupts = <0 11 IRQ_TYPE_LEVEL_HIGH>;
port1-otg; dr_mode = "peripheral";
}; };
bridge { bridge {
......
...@@ -712,7 +712,7 @@ usb@4f000000 { ...@@ -712,7 +712,7 @@ usb@4f000000 {
reg = <0x4f000000 0x20000>; reg = <0x4f000000 0x20000>;
interrupt-parent = <&intc_tc11mp>; interrupt-parent = <&intc_tc11mp>;
interrupts = <0 3 IRQ_TYPE_LEVEL_HIGH>; interrupts = <0 3 IRQ_TYPE_LEVEL_HIGH>;
port1-otg; dr_mode = "peripheral";
}; };
}; };
}; };
...@@ -164,7 +164,7 @@ ethernet: ethernet@4e000000 { ...@@ -164,7 +164,7 @@ ethernet: ethernet@4e000000 {
usb: usb@4f000000 { usb: usb@4f000000 {
compatible = "nxp,usb-isp1761"; compatible = "nxp,usb-isp1761";
reg = <0x4f000000 0x20000>; reg = <0x4f000000 0x20000>;
port1-otg; dr_mode = "peripheral";
}; };
bridge { bridge {
......
...@@ -144,7 +144,7 @@ usb@203000000 { ...@@ -144,7 +144,7 @@ usb@203000000 {
compatible = "nxp,usb-isp1761"; compatible = "nxp,usb-isp1761";
reg = <2 0x03000000 0x20000>; reg = <2 0x03000000 0x20000>;
interrupts = <16>; interrupts = <16>;
port1-otg; dr_mode = "peripheral";
}; };
iofpga-bus@300000000 { iofpga-bus@300000000 {
......
...@@ -62,7 +62,7 @@ usb@3,03000000 { ...@@ -62,7 +62,7 @@ usb@3,03000000 {
compatible = "nxp,usb-isp1761"; compatible = "nxp,usb-isp1761";
reg = <3 0x03000000 0x20000>; reg = <3 0x03000000 0x20000>;
interrupts = <16>; interrupts = <16>;
port1-otg; dr_mode = "peripheral";
}; };
iofpga@7,00000000 { iofpga@7,00000000 {
......
...@@ -2409,6 +2409,25 @@ static ssize_t online_store(struct device *dev, struct device_attribute *attr, ...@@ -2409,6 +2409,25 @@ static ssize_t online_store(struct device *dev, struct device_attribute *attr,
} }
static DEVICE_ATTR_RW(online); static DEVICE_ATTR_RW(online);
static ssize_t removable_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
const char *loc;
switch (dev->removable) {
case DEVICE_REMOVABLE:
loc = "removable";
break;
case DEVICE_FIXED:
loc = "fixed";
break;
default:
loc = "unknown";
}
return sysfs_emit(buf, "%s\n", loc);
}
static DEVICE_ATTR_RO(removable);
int device_add_groups(struct device *dev, const struct attribute_group **groups) int device_add_groups(struct device *dev, const struct attribute_group **groups)
{ {
return sysfs_create_groups(&dev->kobj, groups); return sysfs_create_groups(&dev->kobj, groups);
...@@ -2586,8 +2605,16 @@ static int device_add_attrs(struct device *dev) ...@@ -2586,8 +2605,16 @@ static int device_add_attrs(struct device *dev)
goto err_remove_dev_online; goto err_remove_dev_online;
} }
if (dev_removable_is_valid(dev)) {
error = device_create_file(dev, &dev_attr_removable);
if (error)
goto err_remove_dev_waiting_for_supplier;
}
return 0; return 0;
err_remove_dev_waiting_for_supplier:
device_remove_file(dev, &dev_attr_waiting_for_supplier);
err_remove_dev_online: err_remove_dev_online:
device_remove_file(dev, &dev_attr_online); device_remove_file(dev, &dev_attr_online);
err_remove_dev_groups: err_remove_dev_groups:
...@@ -2607,6 +2634,7 @@ static void device_remove_attrs(struct device *dev) ...@@ -2607,6 +2634,7 @@ static void device_remove_attrs(struct device *dev)
struct class *class = dev->class; struct class *class = dev->class;
const struct device_type *type = dev->type; const struct device_type *type = dev->type;
device_remove_file(dev, &dev_attr_removable);
device_remove_file(dev, &dev_attr_waiting_for_supplier); device_remove_file(dev, &dev_attr_waiting_for_supplier);
device_remove_file(dev, &dev_attr_online); device_remove_file(dev, &dev_attr_online);
device_remove_groups(dev, dev->groups); device_remove_groups(dev, dev->groups);
......
...@@ -1576,6 +1576,26 @@ static void set_pcie_untrusted(struct pci_dev *dev) ...@@ -1576,6 +1576,26 @@ static void set_pcie_untrusted(struct pci_dev *dev)
dev->untrusted = true; dev->untrusted = true;
} }
static void pci_set_removable(struct pci_dev *dev)
{
struct pci_dev *parent = pci_upstream_bridge(dev);
/*
* We (only) consider everything downstream from an external_facing
* device to be removable by the user. We're mainly concerned with
* consumer platforms with user accessible thunderbolt ports that are
* vulnerable to DMA attacks, and we expect those ports to be marked by
* the firmware as external_facing. Devices in traditional hotplug
* slots can technically be removed, but the expectation is that unless
* the port is marked with external_facing, such devices are less
* accessible to user / may not be removed by end user, and thus not
* exposed as "removable" to userspace.
*/
if (parent &&
(parent->external_facing || dev_is_removable(&parent->dev)))
dev_set_removable(&dev->dev, DEVICE_REMOVABLE);
}
/** /**
* pci_ext_cfg_is_aliased - Is ext config space just an alias of std config? * pci_ext_cfg_is_aliased - Is ext config space just an alias of std config?
* @dev: PCI device * @dev: PCI device
...@@ -1823,6 +1843,8 @@ int pci_setup_device(struct pci_dev *dev) ...@@ -1823,6 +1843,8 @@ int pci_setup_device(struct pci_dev *dev)
/* Early fixups, before probing the BARs */ /* Early fixups, before probing the BARs */
pci_fixup_device(pci_fixup_early, dev); pci_fixup_device(pci_fixup_early, dev);
pci_set_removable(dev);
pci_info(dev, "[%04x:%04x] type %02x class %#08x\n", pci_info(dev, "[%04x:%04x] type %02x class %#08x\n",
dev->vendor, dev->device, dev->hdr_type, dev->class); dev->vendor, dev->device, dev->hdr_type, dev->class);
......
...@@ -219,6 +219,22 @@ static const struct qusb2_phy_init_tbl msm8998_init_tbl[] = { ...@@ -219,6 +219,22 @@ static const struct qusb2_phy_init_tbl msm8998_init_tbl[] = {
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_DIGITAL_TIMERS_TWO, 0x19), QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_DIGITAL_TIMERS_TWO, 0x19),
}; };
static const struct qusb2_phy_init_tbl sm6115_init_tbl[] = {
QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE1, 0xf8),
QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE2, 0x53),
QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE3, 0x81),
QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE4, 0x17),
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_TUNE, 0x30),
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL1, 0x79),
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL2, 0x21),
QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TEST2, 0x14),
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_AUTOPGM_CTL1, 0x9f),
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_PWR_CTRL, 0x00),
};
static const unsigned int qusb2_v2_regs_layout[] = { static const unsigned int qusb2_v2_regs_layout[] = {
[QUSB2PHY_PLL_CORE_INPUT_OVERRIDE] = 0xa8, [QUSB2PHY_PLL_CORE_INPUT_OVERRIDE] = 0xa8,
[QUSB2PHY_PLL_STATUS] = 0x1a0, [QUSB2PHY_PLL_STATUS] = 0x1a0,
...@@ -342,6 +358,18 @@ static const struct qusb2_phy_cfg sdm660_phy_cfg = { ...@@ -342,6 +358,18 @@ static const struct qusb2_phy_cfg sdm660_phy_cfg = {
.autoresume_en = BIT(3), .autoresume_en = BIT(3),
}; };
static const struct qusb2_phy_cfg sm6115_phy_cfg = {
.tbl = sm6115_init_tbl,
.tbl_num = ARRAY_SIZE(sm6115_init_tbl),
.regs = msm8996_regs_layout,
.has_pll_test = true,
.se_clk_scheme_default = true,
.disable_ctrl = (CLAMP_N_EN | FREEZIO_N | POWER_DOWN),
.mask_core_ready = PLL_LOCKED,
.autoresume_en = BIT(3),
};
static const char * const qusb2_phy_vreg_names[] = { static const char * const qusb2_phy_vreg_names[] = {
"vdda-pll", "vdda-phy-dpdm", "vdda-pll", "vdda-phy-dpdm",
}; };
...@@ -888,6 +916,12 @@ static const struct of_device_id qusb2_phy_of_match_table[] = { ...@@ -888,6 +916,12 @@ static const struct of_device_id qusb2_phy_of_match_table[] = {
}, { }, {
.compatible = "qcom,sdm660-qusb2-phy", .compatible = "qcom,sdm660-qusb2-phy",
.data = &sdm660_phy_cfg, .data = &sdm660_phy_cfg,
}, {
.compatible = "qcom,sm4250-qusb2-phy",
.data = &sm6115_phy_cfg,
}, {
.compatible = "qcom,sm6115-qusb2-phy",
.data = &sm6115_phy_cfg,
}, { }, {
/* /*
* Deprecated. Only here to support legacy device * Deprecated. Only here to support legacy device
......
This diff is collapsed.
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0-only // SPDX-License-Identifier: GPL-2.0-only
/* /*
* Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. * Copyright (c) 2014-2020, NVIDIA CORPORATION. All rights reserved.
*/ */
#include <linux/delay.h> #include <linux/delay.h>
...@@ -321,11 +321,17 @@ static void tegra_xusb_lane_program(struct tegra_xusb_lane *lane) ...@@ -321,11 +321,17 @@ static void tegra_xusb_lane_program(struct tegra_xusb_lane *lane)
if (soc->num_funcs < 2) if (soc->num_funcs < 2)
return; return;
if (lane->pad->ops->iddq_enable)
lane->pad->ops->iddq_enable(lane);
/* choose function */ /* choose function */
value = padctl_readl(padctl, soc->offset); value = padctl_readl(padctl, soc->offset);
value &= ~(soc->mask << soc->shift); value &= ~(soc->mask << soc->shift);
value |= lane->function << soc->shift; value |= lane->function << soc->shift;
padctl_writel(padctl, value, soc->offset); padctl_writel(padctl, value, soc->offset);
if (lane->pad->ops->iddq_disable)
lane->pad->ops->iddq_disable(lane);
} }
static void tegra_xusb_pad_program(struct tegra_xusb_pad *pad) static void tegra_xusb_pad_program(struct tegra_xusb_pad *pad)
...@@ -376,7 +382,7 @@ static int tegra_xusb_setup_pads(struct tegra_xusb_padctl *padctl) ...@@ -376,7 +382,7 @@ static int tegra_xusb_setup_pads(struct tegra_xusb_padctl *padctl)
return 0; return 0;
} }
static bool tegra_xusb_lane_check(struct tegra_xusb_lane *lane, bool tegra_xusb_lane_check(struct tegra_xusb_lane *lane,
const char *function) const char *function)
{ {
const char *func = lane->soc->funcs[lane->function]; const char *func = lane->soc->funcs[lane->function];
...@@ -1267,10 +1273,36 @@ static int tegra_xusb_padctl_remove(struct platform_device *pdev) ...@@ -1267,10 +1273,36 @@ static int tegra_xusb_padctl_remove(struct platform_device *pdev)
return err; return err;
} }
static int tegra_xusb_padctl_suspend_noirq(struct device *dev)
{
struct tegra_xusb_padctl *padctl = dev_get_drvdata(dev);
if (padctl->soc && padctl->soc->ops && padctl->soc->ops->suspend_noirq)
return padctl->soc->ops->suspend_noirq(padctl);
return 0;
}
static int tegra_xusb_padctl_resume_noirq(struct device *dev)
{
struct tegra_xusb_padctl *padctl = dev_get_drvdata(dev);
if (padctl->soc && padctl->soc->ops && padctl->soc->ops->resume_noirq)
return padctl->soc->ops->resume_noirq(padctl);
return 0;
}
static const struct dev_pm_ops tegra_xusb_padctl_pm_ops = {
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_xusb_padctl_suspend_noirq,
tegra_xusb_padctl_resume_noirq)
};
static struct platform_driver tegra_xusb_padctl_driver = { static struct platform_driver tegra_xusb_padctl_driver = {
.driver = { .driver = {
.name = "tegra-xusb-padctl", .name = "tegra-xusb-padctl",
.of_match_table = tegra_xusb_padctl_of_match, .of_match_table = tegra_xusb_padctl_of_match,
.pm = &tegra_xusb_padctl_pm_ops,
}, },
.probe = tegra_xusb_padctl_probe, .probe = tegra_xusb_padctl_probe,
.remove = tegra_xusb_padctl_remove, .remove = tegra_xusb_padctl_remove,
...@@ -1337,6 +1369,62 @@ int tegra_xusb_padctl_hsic_set_idle(struct tegra_xusb_padctl *padctl, ...@@ -1337,6 +1369,62 @@ int tegra_xusb_padctl_hsic_set_idle(struct tegra_xusb_padctl *padctl,
} }
EXPORT_SYMBOL_GPL(tegra_xusb_padctl_hsic_set_idle); EXPORT_SYMBOL_GPL(tegra_xusb_padctl_hsic_set_idle);
int tegra_xusb_padctl_enable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, struct phy *phy,
enum usb_device_speed speed)
{
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
if (lane->pad->ops->enable_phy_sleepwalk)
return lane->pad->ops->enable_phy_sleepwalk(lane, speed);
return -EOPNOTSUPP;
}
EXPORT_SYMBOL_GPL(tegra_xusb_padctl_enable_phy_sleepwalk);
int tegra_xusb_padctl_disable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, struct phy *phy)
{
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
if (lane->pad->ops->disable_phy_sleepwalk)
return lane->pad->ops->disable_phy_sleepwalk(lane);
return -EOPNOTSUPP;
}
EXPORT_SYMBOL_GPL(tegra_xusb_padctl_disable_phy_sleepwalk);
int tegra_xusb_padctl_enable_phy_wake(struct tegra_xusb_padctl *padctl, struct phy *phy)
{
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
if (lane->pad->ops->enable_phy_wake)
return lane->pad->ops->enable_phy_wake(lane);
return -EOPNOTSUPP;
}
EXPORT_SYMBOL_GPL(tegra_xusb_padctl_enable_phy_wake);
int tegra_xusb_padctl_disable_phy_wake(struct tegra_xusb_padctl *padctl, struct phy *phy)
{
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
if (lane->pad->ops->disable_phy_wake)
return lane->pad->ops->disable_phy_wake(lane);
return -EOPNOTSUPP;
}
EXPORT_SYMBOL_GPL(tegra_xusb_padctl_disable_phy_wake);
bool tegra_xusb_padctl_remote_wake_detected(struct tegra_xusb_padctl *padctl, struct phy *phy)
{
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
if (lane->pad->ops->remote_wake_detected)
return lane->pad->ops->remote_wake_detected(lane);
return false;
}
EXPORT_SYMBOL_GPL(tegra_xusb_padctl_remote_wake_detected);
int tegra_xusb_padctl_usb3_set_lfps_detect(struct tegra_xusb_padctl *padctl, int tegra_xusb_padctl_usb3_set_lfps_detect(struct tegra_xusb_padctl *padctl,
unsigned int port, bool enable) unsigned int port, bool enable)
{ {
......
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* /*
* Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved. * Copyright (c) 2014-2020, NVIDIA CORPORATION. All rights reserved.
* Copyright (c) 2015, Google Inc. * Copyright (c) 2015, Google Inc.
*/ */
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/usb/ch9.h>
#include <linux/usb/otg.h> #include <linux/usb/otg.h>
#include <linux/usb/role.h> #include <linux/usb/role.h>
...@@ -35,6 +36,10 @@ struct tegra_xusb_lane_soc { ...@@ -35,6 +36,10 @@ struct tegra_xusb_lane_soc {
const char * const *funcs; const char * const *funcs;
unsigned int num_funcs; unsigned int num_funcs;
struct {
unsigned int misc_ctl2;
} regs;
}; };
struct tegra_xusb_lane { struct tegra_xusb_lane {
...@@ -126,8 +131,17 @@ struct tegra_xusb_lane_ops { ...@@ -126,8 +131,17 @@ struct tegra_xusb_lane_ops {
struct device_node *np, struct device_node *np,
unsigned int index); unsigned int index);
void (*remove)(struct tegra_xusb_lane *lane); void (*remove)(struct tegra_xusb_lane *lane);
void (*iddq_enable)(struct tegra_xusb_lane *lane);
void (*iddq_disable)(struct tegra_xusb_lane *lane);
int (*enable_phy_sleepwalk)(struct tegra_xusb_lane *lane, enum usb_device_speed speed);
int (*disable_phy_sleepwalk)(struct tegra_xusb_lane *lane);
int (*enable_phy_wake)(struct tegra_xusb_lane *lane);
int (*disable_phy_wake)(struct tegra_xusb_lane *lane);
bool (*remote_wake_detected)(struct tegra_xusb_lane *lane);
}; };
bool tegra_xusb_lane_check(struct tegra_xusb_lane *lane, const char *function);
/* /*
* pads * pads
*/ */
...@@ -230,7 +244,7 @@ struct tegra_xusb_pcie_pad { ...@@ -230,7 +244,7 @@ struct tegra_xusb_pcie_pad {
struct reset_control *rst; struct reset_control *rst;
struct clk *pll; struct clk *pll;
unsigned int enable; bool enable;
}; };
static inline struct tegra_xusb_pcie_pad * static inline struct tegra_xusb_pcie_pad *
...@@ -245,7 +259,7 @@ struct tegra_xusb_sata_pad { ...@@ -245,7 +259,7 @@ struct tegra_xusb_sata_pad {
struct reset_control *rst; struct reset_control *rst;
struct clk *pll; struct clk *pll;
unsigned int enable; bool enable;
}; };
static inline struct tegra_xusb_sata_pad * static inline struct tegra_xusb_sata_pad *
...@@ -388,6 +402,8 @@ struct tegra_xusb_padctl_ops { ...@@ -388,6 +402,8 @@ struct tegra_xusb_padctl_ops {
const struct tegra_xusb_padctl_soc *soc); const struct tegra_xusb_padctl_soc *soc);
void (*remove)(struct tegra_xusb_padctl *padctl); void (*remove)(struct tegra_xusb_padctl *padctl);
int (*suspend_noirq)(struct tegra_xusb_padctl *padctl);
int (*resume_noirq)(struct tegra_xusb_padctl *padctl);
int (*usb3_save_context)(struct tegra_xusb_padctl *padctl, int (*usb3_save_context)(struct tegra_xusb_padctl *padctl,
unsigned int index); unsigned int index);
int (*hsic_set_idle)(struct tegra_xusb_padctl *padctl, int (*hsic_set_idle)(struct tegra_xusb_padctl *padctl,
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
obj-${CONFIG_USB4} := thunderbolt.o obj-${CONFIG_USB4} := thunderbolt.o
thunderbolt-objs := nhi.o nhi_ops.o ctl.o tb.o switch.o cap.o path.o tunnel.o eeprom.o thunderbolt-objs := nhi.o nhi_ops.o ctl.o tb.o switch.o cap.o path.o tunnel.o eeprom.o
thunderbolt-objs += domain.o dma_port.o icm.o property.o xdomain.o lc.o tmu.o usb4.o thunderbolt-objs += domain.o dma_port.o icm.o property.o xdomain.o lc.o tmu.o usb4.o
thunderbolt-objs += nvm.o retimer.o quirks.o thunderbolt-objs += usb4_port.o nvm.o retimer.o quirks.o
thunderbolt-${CONFIG_ACPI} += acpi.o thunderbolt-${CONFIG_ACPI} += acpi.o
thunderbolt-$(CONFIG_DEBUG_FS) += debugfs.o thunderbolt-$(CONFIG_DEBUG_FS) += debugfs.o
......
...@@ -180,3 +180,209 @@ bool tb_acpi_is_xdomain_allowed(void) ...@@ -180,3 +180,209 @@ bool tb_acpi_is_xdomain_allowed(void)
return osc_sb_native_usb4_control & OSC_USB_XDOMAIN; return osc_sb_native_usb4_control & OSC_USB_XDOMAIN;
return true; return true;
} }
/* UUID for retimer _DSM: e0053122-795b-4122-8a5e-57be1d26acb3 */
static const guid_t retimer_dsm_guid =
GUID_INIT(0xe0053122, 0x795b, 0x4122,
0x8a, 0x5e, 0x57, 0xbe, 0x1d, 0x26, 0xac, 0xb3);
#define RETIMER_DSM_QUERY_ONLINE_STATE 1
#define RETIMER_DSM_SET_ONLINE_STATE 2
static int tb_acpi_retimer_set_power(struct tb_port *port, bool power)
{
struct usb4_port *usb4 = port->usb4;
union acpi_object argv4[2];
struct acpi_device *adev;
union acpi_object *obj;
int ret;
if (!usb4->can_offline)
return 0;
adev = ACPI_COMPANION(&usb4->dev);
if (WARN_ON(!adev))
return 0;
/* Check if we are already powered on (and in correct mode) */
obj = acpi_evaluate_dsm_typed(adev->handle, &retimer_dsm_guid, 1,
RETIMER_DSM_QUERY_ONLINE_STATE, NULL,
ACPI_TYPE_INTEGER);
if (!obj) {
tb_port_warn(port, "ACPI: query online _DSM failed\n");
return -EIO;
}
ret = obj->integer.value;
ACPI_FREE(obj);
if (power == ret)
return 0;
tb_port_dbg(port, "ACPI: calling _DSM to power %s retimers\n",
power ? "on" : "off");
argv4[0].type = ACPI_TYPE_PACKAGE;
argv4[0].package.count = 1;
argv4[0].package.elements = &argv4[1];
argv4[1].integer.type = ACPI_TYPE_INTEGER;
argv4[1].integer.value = power;
obj = acpi_evaluate_dsm_typed(adev->handle, &retimer_dsm_guid, 1,
RETIMER_DSM_SET_ONLINE_STATE, argv4,
ACPI_TYPE_INTEGER);
if (!obj) {
tb_port_warn(port,
"ACPI: set online state _DSM evaluation failed\n");
return -EIO;
}
ret = obj->integer.value;
ACPI_FREE(obj);
if (ret >= 0) {
if (power)
return ret == 1 ? 0 : -EBUSY;
return 0;
}
tb_port_warn(port, "ACPI: set online state _DSM failed with error %d\n", ret);
return -EIO;
}
/**
* tb_acpi_power_on_retimers() - Call platform to power on retimers
* @port: USB4 port
*
* Calls platform to turn on power to all retimers behind this USB4
* port. After this function returns successfully the caller can
* continue with the normal retimer flows (as specified in the USB4
* spec). Note if this returns %-EBUSY it means the type-C port is in
* non-USB4/TBT mode (there is non-USB4/TBT device connected).
*
* This should only be called if the USB4/TBT link is not up.
*
* Returns %0 on success.
*/
int tb_acpi_power_on_retimers(struct tb_port *port)
{
return tb_acpi_retimer_set_power(port, true);
}
/**
* tb_acpi_power_off_retimers() - Call platform to power off retimers
* @port: USB4 port
*
* This is the opposite of tb_acpi_power_on_retimers(). After returning
* successfully the normal operations with the @port can continue.
*
* Returns %0 on success.
*/
int tb_acpi_power_off_retimers(struct tb_port *port)
{
return tb_acpi_retimer_set_power(port, false);
}
static bool tb_acpi_bus_match(struct device *dev)
{
return tb_is_switch(dev) || tb_is_usb4_port_device(dev);
}
static struct acpi_device *tb_acpi_find_port(struct acpi_device *adev,
const struct tb_port *port)
{
struct acpi_device *port_adev;
if (!adev)
return NULL;
/*
* Device routers exists under the downstream facing USB4 port
* of the parent router. Their _ADR is always 0.
*/
list_for_each_entry(port_adev, &adev->children, node) {
if (acpi_device_adr(port_adev) == port->port)
return port_adev;
}
return NULL;
}
static struct acpi_device *tb_acpi_switch_find_companion(struct tb_switch *sw)
{
struct acpi_device *adev = NULL;
struct tb_switch *parent_sw;
parent_sw = tb_switch_parent(sw);
if (parent_sw) {
struct tb_port *port = tb_port_at(tb_route(sw), parent_sw);
struct acpi_device *port_adev;
port_adev = tb_acpi_find_port(ACPI_COMPANION(&parent_sw->dev), port);
if (port_adev)
adev = acpi_find_child_device(port_adev, 0, false);
} else {
struct tb_nhi *nhi = sw->tb->nhi;
struct acpi_device *parent_adev;
parent_adev = ACPI_COMPANION(&nhi->pdev->dev);
if (parent_adev)
adev = acpi_find_child_device(parent_adev, 0, false);
}
return adev;
}
static struct acpi_device *tb_acpi_find_companion(struct device *dev)
{
/*
* The Thunderbolt/USB4 hierarchy looks like following:
*
* Device (NHI)
* Device (HR) // Host router _ADR == 0
* Device (DFP0) // Downstream port _ADR == lane 0 adapter
* Device (DR) // Device router _ADR == 0
* Device (UFP) // Upstream port _ADR == lane 0 adapter
* Device (DFP1) // Downstream port _ADR == lane 0 adapter number
*
* At the moment we bind the host router to the corresponding
* Linux device.
*/
if (tb_is_switch(dev))
return tb_acpi_switch_find_companion(tb_to_switch(dev));
else if (tb_is_usb4_port_device(dev))
return tb_acpi_find_port(ACPI_COMPANION(dev->parent),
tb_to_usb4_port_device(dev)->port);
return NULL;
}
static void tb_acpi_setup(struct device *dev)
{
struct acpi_device *adev = ACPI_COMPANION(dev);
struct usb4_port *usb4 = tb_to_usb4_port_device(dev);
if (!adev || !usb4)
return;
if (acpi_check_dsm(adev->handle, &retimer_dsm_guid, 1,
BIT(RETIMER_DSM_QUERY_ONLINE_STATE) |
BIT(RETIMER_DSM_SET_ONLINE_STATE)))
usb4->can_offline = true;
}
static struct acpi_bus_type tb_acpi_bus = {
.name = "thunderbolt",
.match = tb_acpi_bus_match,
.find_companion = tb_acpi_find_companion,
.setup = tb_acpi_setup,
};
int tb_acpi_init(void)
{
return register_acpi_bus_type(&tb_acpi_bus);
}
void tb_acpi_exit(void)
{
unregister_acpi_bus_type(&tb_acpi_bus);
}
...@@ -299,15 +299,13 @@ static int dma_port_request(struct tb_dma_port *dma, u32 in, ...@@ -299,15 +299,13 @@ static int dma_port_request(struct tb_dma_port *dma, u32 in,
return status_to_errno(out); return status_to_errno(out);
} }
static int dma_port_flash_read_block(struct tb_dma_port *dma, u32 address, static int dma_port_flash_read_block(void *data, unsigned int dwaddress,
void *buf, u32 size) void *buf, size_t dwords)
{ {
struct tb_dma_port *dma = data;
struct tb_switch *sw = dma->sw; struct tb_switch *sw = dma->sw;
u32 in, dwaddress, dwords;
int ret; int ret;
u32 in;
dwaddress = address / 4;
dwords = size / 4;
in = MAIL_IN_CMD_FLASH_READ << MAIL_IN_CMD_SHIFT; in = MAIL_IN_CMD_FLASH_READ << MAIL_IN_CMD_SHIFT;
if (dwords < MAIL_DATA_DWORDS) if (dwords < MAIL_DATA_DWORDS)
...@@ -323,14 +321,13 @@ static int dma_port_flash_read_block(struct tb_dma_port *dma, u32 address, ...@@ -323,14 +321,13 @@ static int dma_port_flash_read_block(struct tb_dma_port *dma, u32 address,
dma->base + MAIL_DATA, dwords, DMA_PORT_TIMEOUT); dma->base + MAIL_DATA, dwords, DMA_PORT_TIMEOUT);
} }
static int dma_port_flash_write_block(struct tb_dma_port *dma, u32 address, static int dma_port_flash_write_block(void *data, unsigned int dwaddress,
const void *buf, u32 size) const void *buf, size_t dwords)
{ {
struct tb_dma_port *dma = data;
struct tb_switch *sw = dma->sw; struct tb_switch *sw = dma->sw;
u32 in, dwaddress, dwords;
int ret; int ret;
u32 in;
dwords = size / 4;
/* Write the block to MAIL_DATA registers */ /* Write the block to MAIL_DATA registers */
ret = dma_port_write(sw->tb->ctl, buf, tb_route(sw), dma->port, ret = dma_port_write(sw->tb->ctl, buf, tb_route(sw), dma->port,
...@@ -341,12 +338,8 @@ static int dma_port_flash_write_block(struct tb_dma_port *dma, u32 address, ...@@ -341,12 +338,8 @@ static int dma_port_flash_write_block(struct tb_dma_port *dma, u32 address,
in = MAIL_IN_CMD_FLASH_WRITE << MAIL_IN_CMD_SHIFT; in = MAIL_IN_CMD_FLASH_WRITE << MAIL_IN_CMD_SHIFT;
/* CSS header write is always done to the same magic address */ /* CSS header write is always done to the same magic address */
if (address >= DMA_PORT_CSS_ADDRESS) { if (dwaddress >= DMA_PORT_CSS_ADDRESS)
dwaddress = DMA_PORT_CSS_ADDRESS;
in |= MAIL_IN_CSS; in |= MAIL_IN_CSS;
} else {
dwaddress = address / 4;
}
in |= ((dwords - 1) << MAIL_IN_DWORDS_SHIFT) & MAIL_IN_DWORDS_MASK; in |= ((dwords - 1) << MAIL_IN_DWORDS_SHIFT) & MAIL_IN_DWORDS_MASK;
in |= (dwaddress << MAIL_IN_ADDRESS_SHIFT) & MAIL_IN_ADDRESS_MASK; in |= (dwaddress << MAIL_IN_ADDRESS_SHIFT) & MAIL_IN_ADDRESS_MASK;
...@@ -365,36 +358,8 @@ static int dma_port_flash_write_block(struct tb_dma_port *dma, u32 address, ...@@ -365,36 +358,8 @@ static int dma_port_flash_write_block(struct tb_dma_port *dma, u32 address,
int dma_port_flash_read(struct tb_dma_port *dma, unsigned int address, int dma_port_flash_read(struct tb_dma_port *dma, unsigned int address,
void *buf, size_t size) void *buf, size_t size)
{ {
unsigned int retries = DMA_PORT_RETRIES; return tb_nvm_read_data(address, buf, size, DMA_PORT_RETRIES,
dma_port_flash_read_block, dma);
do {
unsigned int offset;
size_t nbytes;
int ret;
offset = address & 3;
nbytes = min_t(size_t, size + offset, MAIL_DATA_DWORDS * 4);
ret = dma_port_flash_read_block(dma, address, dma->buf,
ALIGN(nbytes, 4));
if (ret) {
if (ret == -ETIMEDOUT) {
if (retries--)
continue;
ret = -EIO;
}
return ret;
}
nbytes -= offset;
memcpy(buf, dma->buf + offset, nbytes);
size -= nbytes;
address += nbytes;
buf += nbytes;
} while (size > 0);
return 0;
} }
/** /**
...@@ -411,40 +376,11 @@ int dma_port_flash_read(struct tb_dma_port *dma, unsigned int address, ...@@ -411,40 +376,11 @@ int dma_port_flash_read(struct tb_dma_port *dma, unsigned int address,
int dma_port_flash_write(struct tb_dma_port *dma, unsigned int address, int dma_port_flash_write(struct tb_dma_port *dma, unsigned int address,
const void *buf, size_t size) const void *buf, size_t size)
{ {
unsigned int retries = DMA_PORT_RETRIES; if (address >= DMA_PORT_CSS_ADDRESS && size > DMA_PORT_CSS_MAX_SIZE)
unsigned int offset; return -E2BIG;
if (address >= DMA_PORT_CSS_ADDRESS) {
offset = 0;
if (size > DMA_PORT_CSS_MAX_SIZE)
return -E2BIG;
} else {
offset = address & 3;
address = address & ~3;
}
do {
u32 nbytes = min_t(u32, size, MAIL_DATA_DWORDS * 4);
int ret;
memcpy(dma->buf + offset, buf, nbytes); return tb_nvm_write_data(address, buf, size, DMA_PORT_RETRIES,
dma_port_flash_write_block, dma);
ret = dma_port_flash_write_block(dma, address, buf, nbytes);
if (ret) {
if (ret == -ETIMEDOUT) {
if (retries--)
continue;
ret = -EIO;
}
return ret;
}
size -= nbytes;
address += nbytes;
buf += nbytes;
} while (size > 0);
return 0;
} }
/** /**
......
...@@ -881,11 +881,12 @@ int tb_domain_init(void) ...@@ -881,11 +881,12 @@ int tb_domain_init(void)
int ret; int ret;
tb_test_init(); tb_test_init();
tb_debugfs_init(); tb_debugfs_init();
tb_acpi_init();
ret = tb_xdomain_init(); ret = tb_xdomain_init();
if (ret) if (ret)
goto err_debugfs; goto err_acpi;
ret = bus_register(&tb_bus_type); ret = bus_register(&tb_bus_type);
if (ret) if (ret)
goto err_xdomain; goto err_xdomain;
...@@ -894,7 +895,8 @@ int tb_domain_init(void) ...@@ -894,7 +895,8 @@ int tb_domain_init(void)
err_xdomain: err_xdomain:
tb_xdomain_exit(); tb_xdomain_exit();
err_debugfs: err_acpi:
tb_acpi_exit();
tb_debugfs_exit(); tb_debugfs_exit();
tb_test_exit(); tb_test_exit();
...@@ -907,6 +909,7 @@ void tb_domain_exit(void) ...@@ -907,6 +909,7 @@ void tb_domain_exit(void)
ida_destroy(&tb_domain_ida); ida_destroy(&tb_domain_ida);
tb_nvm_exit(); tb_nvm_exit();
tb_xdomain_exit(); tb_xdomain_exit();
tb_acpi_exit();
tb_debugfs_exit(); tb_debugfs_exit();
tb_test_exit(); tb_test_exit();
} }
...@@ -214,7 +214,10 @@ static u32 tb_crc32(void *data, size_t len) ...@@ -214,7 +214,10 @@ static u32 tb_crc32(void *data, size_t len)
return ~__crc32c_le(~0, data, len); return ~__crc32c_le(~0, data, len);
} }
#define TB_DROM_DATA_START 13 #define TB_DROM_DATA_START 13
#define TB_DROM_HEADER_SIZE 22
#define USB4_DROM_HEADER_SIZE 16
struct tb_drom_header { struct tb_drom_header {
/* BYTE 0 */ /* BYTE 0 */
u8 uid_crc8; /* checksum for uid */ u8 uid_crc8; /* checksum for uid */
...@@ -224,9 +227,9 @@ struct tb_drom_header { ...@@ -224,9 +227,9 @@ struct tb_drom_header {
u32 data_crc32; /* checksum for data_len bytes starting at byte 13 */ u32 data_crc32; /* checksum for data_len bytes starting at byte 13 */
/* BYTE 13 */ /* BYTE 13 */
u8 device_rom_revision; /* should be <= 1 */ u8 device_rom_revision; /* should be <= 1 */
u16 data_len:10; u16 data_len:12;
u8 __unknown1:6; u8 reserved:4;
/* BYTES 16-21 */ /* BYTES 16-21 - Only for TBT DROM, nonexistent in USB4 DROM */
u16 vendor_id; u16 vendor_id;
u16 model_id; u16 model_id;
u8 model_rev; u8 model_rev;
...@@ -401,10 +404,10 @@ static int tb_drom_parse_entry_port(struct tb_switch *sw, ...@@ -401,10 +404,10 @@ static int tb_drom_parse_entry_port(struct tb_switch *sw,
* *
* Drom must have been copied to sw->drom. * Drom must have been copied to sw->drom.
*/ */
static int tb_drom_parse_entries(struct tb_switch *sw) static int tb_drom_parse_entries(struct tb_switch *sw, size_t header_size)
{ {
struct tb_drom_header *header = (void *) sw->drom; struct tb_drom_header *header = (void *) sw->drom;
u16 pos = sizeof(*header); u16 pos = header_size;
u16 drom_size = header->data_len + TB_DROM_DATA_START; u16 drom_size = header->data_len + TB_DROM_DATA_START;
int res; int res;
...@@ -566,7 +569,7 @@ static int tb_drom_parse(struct tb_switch *sw) ...@@ -566,7 +569,7 @@ static int tb_drom_parse(struct tb_switch *sw)
header->data_crc32, crc); header->data_crc32, crc);
} }
return tb_drom_parse_entries(sw); return tb_drom_parse_entries(sw, TB_DROM_HEADER_SIZE);
} }
static int usb4_drom_parse(struct tb_switch *sw) static int usb4_drom_parse(struct tb_switch *sw)
...@@ -583,7 +586,7 @@ static int usb4_drom_parse(struct tb_switch *sw) ...@@ -583,7 +586,7 @@ static int usb4_drom_parse(struct tb_switch *sw)
return -EINVAL; return -EINVAL;
} }
return tb_drom_parse_entries(sw); return tb_drom_parse_entries(sw, USB4_DROM_HEADER_SIZE);
} }
/** /**
......
...@@ -1677,14 +1677,18 @@ static void icm_icl_rtd3_veto(struct tb *tb, const struct icm_pkg_header *hdr) ...@@ -1677,14 +1677,18 @@ static void icm_icl_rtd3_veto(struct tb *tb, const struct icm_pkg_header *hdr)
static bool icm_tgl_is_supported(struct tb *tb) static bool icm_tgl_is_supported(struct tb *tb)
{ {
u32 val; unsigned long end = jiffies + msecs_to_jiffies(10);
/* do {
* If the firmware is not running use software CM. This platform u32 val;
* should fully support both.
*/ val = ioread32(tb->nhi->iobase + REG_FW_STS);
val = ioread32(tb->nhi->iobase + REG_FW_STS); if (val & REG_FW_STS_NVM_AUTH_DONE)
return !!(val & REG_FW_STS_NVM_AUTH_DONE); return true;
usleep_range(100, 500);
} while (time_before(jiffies, end));
return false;
} }
static void icm_handle_notification(struct work_struct *work) static void icm_handle_notification(struct work_struct *work)
...@@ -2505,6 +2509,8 @@ struct tb *icm_probe(struct tb_nhi *nhi) ...@@ -2505,6 +2509,8 @@ struct tb *icm_probe(struct tb_nhi *nhi)
case PCI_DEVICE_ID_INTEL_TGL_NHI1: case PCI_DEVICE_ID_INTEL_TGL_NHI1:
case PCI_DEVICE_ID_INTEL_TGL_H_NHI0: case PCI_DEVICE_ID_INTEL_TGL_H_NHI0:
case PCI_DEVICE_ID_INTEL_TGL_H_NHI1: case PCI_DEVICE_ID_INTEL_TGL_H_NHI1:
case PCI_DEVICE_ID_INTEL_ADL_NHI0:
case PCI_DEVICE_ID_INTEL_ADL_NHI1:
icm->is_supported = icm_tgl_is_supported; icm->is_supported = icm_tgl_is_supported;
icm->driver_ready = icm_icl_driver_ready; icm->driver_ready = icm_icl_driver_ready;
icm->set_uuid = icm_icl_set_uuid; icm->set_uuid = icm_icl_set_uuid;
......
...@@ -208,8 +208,8 @@ static int tb_lc_set_wake_one(struct tb_switch *sw, unsigned int offset, ...@@ -208,8 +208,8 @@ static int tb_lc_set_wake_one(struct tb_switch *sw, unsigned int offset,
if (ret) if (ret)
return ret; return ret;
ctrl &= ~(TB_LC_SX_CTRL_WOC | TB_LC_SX_CTRL_WOD | TB_LC_SX_CTRL_WOP | ctrl &= ~(TB_LC_SX_CTRL_WOC | TB_LC_SX_CTRL_WOD | TB_LC_SX_CTRL_WODPC |
TB_LC_SX_CTRL_WOU4); TB_LC_SX_CTRL_WODPD | TB_LC_SX_CTRL_WOP | TB_LC_SX_CTRL_WOU4);
if (flags & TB_WAKE_ON_CONNECT) if (flags & TB_WAKE_ON_CONNECT)
ctrl |= TB_LC_SX_CTRL_WOC | TB_LC_SX_CTRL_WOD; ctrl |= TB_LC_SX_CTRL_WOC | TB_LC_SX_CTRL_WOD;
...@@ -217,6 +217,8 @@ static int tb_lc_set_wake_one(struct tb_switch *sw, unsigned int offset, ...@@ -217,6 +217,8 @@ static int tb_lc_set_wake_one(struct tb_switch *sw, unsigned int offset,
ctrl |= TB_LC_SX_CTRL_WOU4; ctrl |= TB_LC_SX_CTRL_WOU4;
if (flags & TB_WAKE_ON_PCIE) if (flags & TB_WAKE_ON_PCIE)
ctrl |= TB_LC_SX_CTRL_WOP; ctrl |= TB_LC_SX_CTRL_WOP;
if (flags & TB_WAKE_ON_DP)
ctrl |= TB_LC_SX_CTRL_WODPC | TB_LC_SX_CTRL_WODPD;
return tb_sw_write(sw, &ctrl, TB_CFG_SWITCH, offset + TB_LC_SX_CTRL, 1); return tb_sw_write(sw, &ctrl, TB_CFG_SWITCH, offset + TB_LC_SX_CTRL, 1);
} }
......
...@@ -17,7 +17,6 @@ ...@@ -17,7 +17,6 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/property.h> #include <linux/property.h>
#include <linux/platform_data/x86/apple.h>
#include "nhi.h" #include "nhi.h"
#include "nhi_regs.h" #include "nhi_regs.h"
...@@ -1127,69 +1126,6 @@ static bool nhi_imr_valid(struct pci_dev *pdev) ...@@ -1127,69 +1126,6 @@ static bool nhi_imr_valid(struct pci_dev *pdev)
return true; return true;
} }
/*
* During suspend the Thunderbolt controller is reset and all PCIe
* tunnels are lost. The NHI driver will try to reestablish all tunnels
* during resume. This adds device links between the tunneled PCIe
* downstream ports and the NHI so that the device core will make sure
* NHI is resumed first before the rest.
*/
static void tb_apple_add_links(struct tb_nhi *nhi)
{
struct pci_dev *upstream, *pdev;
if (!x86_apple_machine)
return;
switch (nhi->pdev->device) {
case PCI_DEVICE_ID_INTEL_LIGHT_RIDGE:
case PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C:
case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_NHI:
case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI:
break;
default:
return;
}
upstream = pci_upstream_bridge(nhi->pdev);
while (upstream) {
if (!pci_is_pcie(upstream))
return;
if (pci_pcie_type(upstream) == PCI_EXP_TYPE_UPSTREAM)
break;
upstream = pci_upstream_bridge(upstream);
}
if (!upstream)
return;
/*
* For each hotplug downstream port, create add device link
* back to NHI so that PCIe tunnels can be re-established after
* sleep.
*/
for_each_pci_bridge(pdev, upstream->subordinate) {
const struct device_link *link;
if (!pci_is_pcie(pdev))
continue;
if (pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM ||
!pdev->is_hotplug_bridge)
continue;
link = device_link_add(&pdev->dev, &nhi->pdev->dev,
DL_FLAG_AUTOREMOVE_SUPPLIER |
DL_FLAG_PM_RUNTIME);
if (link) {
dev_dbg(&nhi->pdev->dev, "created link from %s\n",
dev_name(&pdev->dev));
} else {
dev_warn(&nhi->pdev->dev, "device link creation from %s failed\n",
dev_name(&pdev->dev));
}
}
}
static struct tb *nhi_select_cm(struct tb_nhi *nhi) static struct tb *nhi_select_cm(struct tb_nhi *nhi)
{ {
struct tb *tb; struct tb *tb;
...@@ -1278,9 +1214,6 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id) ...@@ -1278,9 +1214,6 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return res; return res;
} }
tb_apple_add_links(nhi);
tb_acpi_add_links(nhi);
tb = nhi_select_cm(nhi); tb = nhi_select_cm(nhi);
if (!tb) { if (!tb) {
dev_err(&nhi->pdev->dev, dev_err(&nhi->pdev->dev,
...@@ -1400,6 +1333,10 @@ static struct pci_device_id nhi_ids[] = { ...@@ -1400,6 +1333,10 @@ static struct pci_device_id nhi_ids[] = {
.driver_data = (kernel_ulong_t)&icl_nhi_ops }, .driver_data = (kernel_ulong_t)&icl_nhi_ops },
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TGL_H_NHI1), { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TGL_H_NHI1),
.driver_data = (kernel_ulong_t)&icl_nhi_ops }, .driver_data = (kernel_ulong_t)&icl_nhi_ops },
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ADL_NHI0),
.driver_data = (kernel_ulong_t)&icl_nhi_ops },
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ADL_NHI1),
.driver_data = (kernel_ulong_t)&icl_nhi_ops },
/* Any USB4 compliant host */ /* Any USB4 compliant host */
{ PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_USB4, ~0) }, { PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_USB4, ~0) },
......
...@@ -72,6 +72,8 @@ extern const struct tb_nhi_ops icl_nhi_ops; ...@@ -72,6 +72,8 @@ extern const struct tb_nhi_ops icl_nhi_ops;
#define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_BRIDGE 0x15ea #define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_BRIDGE 0x15ea
#define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_NHI 0x15eb #define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_NHI 0x15eb
#define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_BRIDGE 0x15ef #define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_BRIDGE 0x15ef
#define PCI_DEVICE_ID_INTEL_ADL_NHI0 0x463e
#define PCI_DEVICE_ID_INTEL_ADL_NHI1 0x466d
#define PCI_DEVICE_ID_INTEL_ICL_NHI1 0x8a0d #define PCI_DEVICE_ID_INTEL_ICL_NHI1 0x8a0d
#define PCI_DEVICE_ID_INTEL_ICL_NHI0 0x8a17 #define PCI_DEVICE_ID_INTEL_ICL_NHI0 0x8a17
#define PCI_DEVICE_ID_INTEL_TGL_NHI0 0x9a1b #define PCI_DEVICE_ID_INTEL_TGL_NHI0 0x9a1b
......
...@@ -164,6 +164,101 @@ void tb_nvm_free(struct tb_nvm *nvm) ...@@ -164,6 +164,101 @@ void tb_nvm_free(struct tb_nvm *nvm)
kfree(nvm); kfree(nvm);
} }
/**
* tb_nvm_read_data() - Read data from NVM
* @address: Start address on the flash
* @buf: Buffer where the read data is copied
* @size: Size of the buffer in bytes
* @retries: Number of retries if block read fails
* @read_block: Function that reads block from the flash
* @read_block_data: Data passsed to @read_block
*
* This is a generic function that reads data from NVM or NVM like
* device.
*
* Returns %0 on success and negative errno otherwise.
*/
int tb_nvm_read_data(unsigned int address, void *buf, size_t size,
unsigned int retries, read_block_fn read_block,
void *read_block_data)
{
do {
unsigned int dwaddress, dwords, offset;
u8 data[NVM_DATA_DWORDS * 4];
size_t nbytes;
int ret;
offset = address & 3;
nbytes = min_t(size_t, size + offset, NVM_DATA_DWORDS * 4);
dwaddress = address / 4;
dwords = ALIGN(nbytes, 4) / 4;
ret = read_block(read_block_data, dwaddress, data, dwords);
if (ret) {
if (ret != -ENODEV && retries--)
continue;
return ret;
}
nbytes -= offset;
memcpy(buf, data + offset, nbytes);
size -= nbytes;
address += nbytes;
buf += nbytes;
} while (size > 0);
return 0;
}
/**
* tb_nvm_write_data() - Write data to NVM
* @address: Start address on the flash
* @buf: Buffer where the data is copied from
* @size: Size of the buffer in bytes
* @retries: Number of retries if the block write fails
* @write_block: Function that writes block to the flash
* @write_block_data: Data passwd to @write_block
*
* This is generic function that writes data to NVM or NVM like device.
*
* Returns %0 on success and negative errno otherwise.
*/
int tb_nvm_write_data(unsigned int address, const void *buf, size_t size,
unsigned int retries, write_block_fn write_block,
void *write_block_data)
{
do {
unsigned int offset, dwaddress;
u8 data[NVM_DATA_DWORDS * 4];
size_t nbytes;
int ret;
offset = address & 3;
nbytes = min_t(u32, size + offset, NVM_DATA_DWORDS * 4);
memcpy(data + offset, buf, nbytes);
dwaddress = address / 4;
ret = write_block(write_block_data, dwaddress, data, nbytes / 4);
if (ret) {
if (ret == -ETIMEDOUT) {
if (retries--)
continue;
ret = -EIO;
}
return ret;
}
size -= nbytes;
address += nbytes;
buf += nbytes;
} while (size > 0);
return 0;
}
void tb_nvm_exit(void) void tb_nvm_exit(void)
{ {
ida_destroy(&nvm_ida); ida_destroy(&nvm_ida);
......
...@@ -367,7 +367,7 @@ static void __tb_path_deallocate_nfc(struct tb_path *path, int first_hop) ...@@ -367,7 +367,7 @@ static void __tb_path_deallocate_nfc(struct tb_path *path, int first_hop)
int i, res; int i, res;
for (i = first_hop; i < path->path_length; i++) { for (i = first_hop; i < path->path_length; i++) {
res = tb_port_add_nfc_credits(path->hops[i].in_port, res = tb_port_add_nfc_credits(path->hops[i].in_port,
-path->nfc_credits); -path->hops[i].nfc_credits);
if (res) if (res)
tb_port_warn(path->hops[i].in_port, tb_port_warn(path->hops[i].in_port,
"nfc credits deallocation failed for hop %d\n", "nfc credits deallocation failed for hop %d\n",
...@@ -502,7 +502,7 @@ int tb_path_activate(struct tb_path *path) ...@@ -502,7 +502,7 @@ int tb_path_activate(struct tb_path *path)
/* Add non flow controlled credits. */ /* Add non flow controlled credits. */
for (i = path->path_length - 1; i >= 0; i--) { for (i = path->path_length - 1; i >= 0; i--) {
res = tb_port_add_nfc_credits(path->hops[i].in_port, res = tb_port_add_nfc_credits(path->hops[i].in_port,
path->nfc_credits); path->hops[i].nfc_credits);
if (res) { if (res) {
__tb_path_deallocate_nfc(path, i); __tb_path_deallocate_nfc(path, i);
goto err; goto err;
......
...@@ -12,7 +12,17 @@ static void quirk_force_power_link(struct tb_switch *sw) ...@@ -12,7 +12,17 @@ static void quirk_force_power_link(struct tb_switch *sw)
sw->quirks |= QUIRK_FORCE_POWER_LINK_CONTROLLER; sw->quirks |= QUIRK_FORCE_POWER_LINK_CONTROLLER;
} }
static void quirk_dp_credit_allocation(struct tb_switch *sw)
{
if (sw->credit_allocation && sw->min_dp_main_credits == 56) {
sw->min_dp_main_credits = 18;
tb_sw_dbg(sw, "quirked DP main: %u\n", sw->min_dp_main_credits);
}
}
struct tb_quirk { struct tb_quirk {
u16 hw_vendor_id;
u16 hw_device_id;
u16 vendor; u16 vendor;
u16 device; u16 device;
void (*hook)(struct tb_switch *sw); void (*hook)(struct tb_switch *sw);
...@@ -20,7 +30,13 @@ struct tb_quirk { ...@@ -20,7 +30,13 @@ struct tb_quirk {
static const struct tb_quirk tb_quirks[] = { static const struct tb_quirk tb_quirks[] = {
/* Dell WD19TB supports self-authentication on unplug */ /* Dell WD19TB supports self-authentication on unplug */
{ 0x00d4, 0xb070, quirk_force_power_link }, { 0x0000, 0x0000, 0x00d4, 0xb070, quirk_force_power_link },
{ 0x0000, 0x0000, 0x00d4, 0xb071, quirk_force_power_link },
/*
* Intel Goshen Ridge NVM 27 and before report wrong number of
* DP buffers.
*/
{ 0x8087, 0x0b26, 0x0000, 0x0000, quirk_dp_credit_allocation },
}; };
/** /**
...@@ -36,7 +52,15 @@ void tb_check_quirks(struct tb_switch *sw) ...@@ -36,7 +52,15 @@ void tb_check_quirks(struct tb_switch *sw)
for (i = 0; i < ARRAY_SIZE(tb_quirks); i++) { for (i = 0; i < ARRAY_SIZE(tb_quirks); i++) {
const struct tb_quirk *q = &tb_quirks[i]; const struct tb_quirk *q = &tb_quirks[i];
if (sw->device == q->device && sw->vendor == q->vendor) if (q->hw_vendor_id && q->hw_vendor_id != sw->config.vendor_id)
q->hook(sw); continue;
if (q->hw_device_id && q->hw_device_id != sw->config.device_id)
continue;
if (q->vendor && q->vendor != sw->vendor)
continue;
if (q->device && q->device != sw->device)
continue;
q->hook(sw);
} }
} }
...@@ -103,6 +103,7 @@ static int tb_retimer_nvm_validate_and_write(struct tb_retimer *rt) ...@@ -103,6 +103,7 @@ static int tb_retimer_nvm_validate_and_write(struct tb_retimer *rt)
unsigned int image_size, hdr_size; unsigned int image_size, hdr_size;
const u8 *buf = rt->nvm->buf; const u8 *buf = rt->nvm->buf;
u16 ds_size, device; u16 ds_size, device;
int ret;
image_size = rt->nvm->buf_data_size; image_size = rt->nvm->buf_data_size;
if (image_size < NVM_MIN_SIZE || image_size > NVM_MAX_SIZE) if (image_size < NVM_MIN_SIZE || image_size > NVM_MAX_SIZE)
...@@ -140,8 +141,43 @@ static int tb_retimer_nvm_validate_and_write(struct tb_retimer *rt) ...@@ -140,8 +141,43 @@ static int tb_retimer_nvm_validate_and_write(struct tb_retimer *rt)
buf += hdr_size; buf += hdr_size;
image_size -= hdr_size; image_size -= hdr_size;
return usb4_port_retimer_nvm_write(rt->port, rt->index, 0, buf, ret = usb4_port_retimer_nvm_write(rt->port, rt->index, 0, buf,
image_size); image_size);
if (!ret)
rt->nvm->flushed = true;
return ret;
}
static int tb_retimer_nvm_authenticate(struct tb_retimer *rt, bool auth_only)
{
u32 status;
int ret;
if (auth_only) {
ret = usb4_port_retimer_nvm_set_offset(rt->port, rt->index, 0);
if (ret)
return ret;
}
ret = usb4_port_retimer_nvm_authenticate(rt->port, rt->index);
if (ret)
return ret;
usleep_range(100, 150);
/*
* Check the status now if we still can access the retimer. It
* is expected that the below fails.
*/
ret = usb4_port_retimer_nvm_authenticate_status(rt->port, rt->index,
&status);
if (!ret) {
rt->auth_status = status;
return status ? -EINVAL : 0;
}
return 0;
} }
static ssize_t device_show(struct device *dev, struct device_attribute *attr, static ssize_t device_show(struct device *dev, struct device_attribute *attr,
...@@ -176,8 +212,7 @@ static ssize_t nvm_authenticate_store(struct device *dev, ...@@ -176,8 +212,7 @@ static ssize_t nvm_authenticate_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count) struct device_attribute *attr, const char *buf, size_t count)
{ {
struct tb_retimer *rt = tb_to_retimer(dev); struct tb_retimer *rt = tb_to_retimer(dev);
bool val; int val, ret;
int ret;
pm_runtime_get_sync(&rt->dev); pm_runtime_get_sync(&rt->dev);
...@@ -191,7 +226,7 @@ static ssize_t nvm_authenticate_store(struct device *dev, ...@@ -191,7 +226,7 @@ static ssize_t nvm_authenticate_store(struct device *dev,
goto exit_unlock; goto exit_unlock;
} }
ret = kstrtobool(buf, &val); ret = kstrtoint(buf, 10, &val);
if (ret) if (ret)
goto exit_unlock; goto exit_unlock;
...@@ -199,16 +234,22 @@ static ssize_t nvm_authenticate_store(struct device *dev, ...@@ -199,16 +234,22 @@ static ssize_t nvm_authenticate_store(struct device *dev,
rt->auth_status = 0; rt->auth_status = 0;
if (val) { if (val) {
if (!rt->nvm->buf) { if (val == AUTHENTICATE_ONLY) {
ret = -EINVAL; ret = tb_retimer_nvm_authenticate(rt, true);
goto exit_unlock; } else {
if (!rt->nvm->flushed) {
if (!rt->nvm->buf) {
ret = -EINVAL;
goto exit_unlock;
}
ret = tb_retimer_nvm_validate_and_write(rt);
if (ret || val == WRITE_ONLY)
goto exit_unlock;
}
if (val == WRITE_AND_AUTHENTICATE)
ret = tb_retimer_nvm_authenticate(rt, false);
} }
ret = tb_retimer_nvm_validate_and_write(rt);
if (ret)
goto exit_unlock;
ret = usb4_port_retimer_nvm_authenticate(rt->port, rt->index);
} }
exit_unlock: exit_unlock:
...@@ -283,11 +324,13 @@ struct device_type tb_retimer_type = { ...@@ -283,11 +324,13 @@ struct device_type tb_retimer_type = {
static int tb_retimer_add(struct tb_port *port, u8 index, u32 auth_status) static int tb_retimer_add(struct tb_port *port, u8 index, u32 auth_status)
{ {
struct usb4_port *usb4;
struct tb_retimer *rt; struct tb_retimer *rt;
u32 vendor, device; u32 vendor, device;
int ret; int ret;
if (!port->cap_usb4) usb4 = port->usb4;
if (!usb4)
return -EINVAL; return -EINVAL;
ret = usb4_port_retimer_read(port, index, USB4_SB_VENDOR_ID, &vendor, ret = usb4_port_retimer_read(port, index, USB4_SB_VENDOR_ID, &vendor,
...@@ -331,7 +374,7 @@ static int tb_retimer_add(struct tb_port *port, u8 index, u32 auth_status) ...@@ -331,7 +374,7 @@ static int tb_retimer_add(struct tb_port *port, u8 index, u32 auth_status)
rt->port = port; rt->port = port;
rt->tb = port->sw->tb; rt->tb = port->sw->tb;
rt->dev.parent = &port->sw->dev; rt->dev.parent = &usb4->dev;
rt->dev.bus = &tb_bus_type; rt->dev.bus = &tb_bus_type;
rt->dev.type = &tb_retimer_type; rt->dev.type = &tb_retimer_type;
dev_set_name(&rt->dev, "%s:%u.%u", dev_name(&port->sw->dev), dev_set_name(&rt->dev, "%s:%u.%u", dev_name(&port->sw->dev),
...@@ -389,7 +432,7 @@ static struct tb_retimer *tb_port_find_retimer(struct tb_port *port, u8 index) ...@@ -389,7 +432,7 @@ static struct tb_retimer *tb_port_find_retimer(struct tb_port *port, u8 index)
struct tb_retimer_lookup lookup = { .port = port, .index = index }; struct tb_retimer_lookup lookup = { .port = port, .index = index };
struct device *dev; struct device *dev;
dev = device_find_child(&port->sw->dev, &lookup, retimer_match); dev = device_find_child(&port->usb4->dev, &lookup, retimer_match);
if (dev) if (dev)
return tb_to_retimer(dev); return tb_to_retimer(dev);
...@@ -399,19 +442,18 @@ static struct tb_retimer *tb_port_find_retimer(struct tb_port *port, u8 index) ...@@ -399,19 +442,18 @@ static struct tb_retimer *tb_port_find_retimer(struct tb_port *port, u8 index)
/** /**
* tb_retimer_scan() - Scan for on-board retimers under port * tb_retimer_scan() - Scan for on-board retimers under port
* @port: USB4 port to scan * @port: USB4 port to scan
* @add: If true also registers found retimers
* *
* Tries to enumerate on-board retimers connected to @port. Found * Brings the sideband into a state where retimers can be accessed.
* retimers are registered as children of @port. Does not scan for cable * Then Tries to enumerate on-board retimers connected to @port. Found
* retimers for now. * retimers are registered as children of @port if @add is set. Does
* not scan for cable retimers for now.
*/ */
int tb_retimer_scan(struct tb_port *port) int tb_retimer_scan(struct tb_port *port, bool add)
{ {
u32 status[TB_MAX_RETIMER_INDEX + 1] = {}; u32 status[TB_MAX_RETIMER_INDEX + 1] = {};
int ret, i, last_idx = 0; int ret, i, last_idx = 0;
if (!port->cap_usb4)
return 0;
/* /*
* Send broadcast RT to make sure retimer indices facing this * Send broadcast RT to make sure retimer indices facing this
* port are set. * port are set.
...@@ -420,6 +462,13 @@ int tb_retimer_scan(struct tb_port *port) ...@@ -420,6 +462,13 @@ int tb_retimer_scan(struct tb_port *port)
if (ret) if (ret)
return ret; return ret;
/*
* Enable sideband channel for each retimer. We can do this
* regardless whether there is device connected or not.
*/
for (i = 1; i <= TB_MAX_RETIMER_INDEX; i++)
usb4_port_retimer_set_inbound_sbtx(port, i);
/* /*
* Before doing anything else, read the authentication status. * Before doing anything else, read the authentication status.
* If the retimer has it set, store it for the new retimer * If the retimer has it set, store it for the new retimer
...@@ -451,10 +500,10 @@ int tb_retimer_scan(struct tb_port *port) ...@@ -451,10 +500,10 @@ int tb_retimer_scan(struct tb_port *port)
rt = tb_port_find_retimer(port, i); rt = tb_port_find_retimer(port, i);
if (rt) { if (rt) {
put_device(&rt->dev); put_device(&rt->dev);
} else { } else if (add) {
ret = tb_retimer_add(port, i, status[i]); ret = tb_retimer_add(port, i, status[i]);
if (ret && ret != -EOPNOTSUPP) if (ret && ret != -EOPNOTSUPP)
return ret; break;
} }
} }
...@@ -479,7 +528,10 @@ static int remove_retimer(struct device *dev, void *data) ...@@ -479,7 +528,10 @@ static int remove_retimer(struct device *dev, void *data)
*/ */
void tb_retimer_remove_all(struct tb_port *port) void tb_retimer_remove_all(struct tb_port *port)
{ {
if (port->cap_usb4) struct usb4_port *usb4;
device_for_each_child_reverse(&port->sw->dev, port,
usb4 = port->usb4;
if (usb4)
device_for_each_child_reverse(&usb4->dev, port,
remove_retimer); remove_retimer);
} }
...@@ -17,7 +17,9 @@ ...@@ -17,7 +17,9 @@
enum usb4_sb_opcode { enum usb4_sb_opcode {
USB4_SB_OPCODE_ERR = 0x20525245, /* "ERR " */ USB4_SB_OPCODE_ERR = 0x20525245, /* "ERR " */
USB4_SB_OPCODE_ONS = 0x444d4321, /* "!CMD" */ USB4_SB_OPCODE_ONS = 0x444d4321, /* "!CMD" */
USB4_SB_OPCODE_ROUTER_OFFLINE = 0x4e45534c, /* "LSEN" */
USB4_SB_OPCODE_ENUMERATE_RETIMERS = 0x4d554e45, /* "ENUM" */ USB4_SB_OPCODE_ENUMERATE_RETIMERS = 0x4d554e45, /* "ENUM" */
USB4_SB_OPCODE_SET_INBOUND_SBTX = 0x5055534c, /* "LSUP" */
USB4_SB_OPCODE_QUERY_LAST_RETIMER = 0x5453414c, /* "LAST" */ USB4_SB_OPCODE_QUERY_LAST_RETIMER = 0x5453414c, /* "LAST" */
USB4_SB_OPCODE_GET_NVM_SECTOR_SIZE = 0x53534e47, /* "GNSS" */ USB4_SB_OPCODE_GET_NVM_SECTOR_SIZE = 0x53534e47, /* "GNSS" */
USB4_SB_OPCODE_NVM_SET_OFFSET = 0x53504f42, /* "BOPS" */ USB4_SB_OPCODE_NVM_SET_OFFSET = 0x53504f42, /* "BOPS" */
......
This diff is collapsed.
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/platform_data/x86/apple.h>
#include "tb.h" #include "tb.h"
#include "tb_regs.h" #include "tb_regs.h"
...@@ -595,7 +596,7 @@ static void tb_scan_port(struct tb_port *port) ...@@ -595,7 +596,7 @@ static void tb_scan_port(struct tb_port *port)
return; return;
} }
tb_retimer_scan(port); tb_retimer_scan(port, true);
sw = tb_switch_alloc(port->sw->tb, &port->sw->dev, sw = tb_switch_alloc(port->sw->tb, &port->sw->dev,
tb_downstream_route(port)); tb_downstream_route(port));
...@@ -662,7 +663,7 @@ static void tb_scan_port(struct tb_port *port) ...@@ -662,7 +663,7 @@ static void tb_scan_port(struct tb_port *port)
tb_sw_warn(sw, "failed to enable TMU\n"); tb_sw_warn(sw, "failed to enable TMU\n");
/* Scan upstream retimers */ /* Scan upstream retimers */
tb_retimer_scan(upstream_port); tb_retimer_scan(upstream_port, true);
/* /*
* Create USB 3.x tunnels only when the switch is plugged to the * Create USB 3.x tunnels only when the switch is plugged to the
...@@ -1571,6 +1572,69 @@ static const struct tb_cm_ops tb_cm_ops = { ...@@ -1571,6 +1572,69 @@ static const struct tb_cm_ops tb_cm_ops = {
.disconnect_xdomain_paths = tb_disconnect_xdomain_paths, .disconnect_xdomain_paths = tb_disconnect_xdomain_paths,
}; };
/*
* During suspend the Thunderbolt controller is reset and all PCIe
* tunnels are lost. The NHI driver will try to reestablish all tunnels
* during resume. This adds device links between the tunneled PCIe
* downstream ports and the NHI so that the device core will make sure
* NHI is resumed first before the rest.
*/
static void tb_apple_add_links(struct tb_nhi *nhi)
{
struct pci_dev *upstream, *pdev;
if (!x86_apple_machine)
return;
switch (nhi->pdev->device) {
case PCI_DEVICE_ID_INTEL_LIGHT_RIDGE:
case PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C:
case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_NHI:
case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI:
break;
default:
return;
}
upstream = pci_upstream_bridge(nhi->pdev);
while (upstream) {
if (!pci_is_pcie(upstream))
return;
if (pci_pcie_type(upstream) == PCI_EXP_TYPE_UPSTREAM)
break;
upstream = pci_upstream_bridge(upstream);
}
if (!upstream)
return;
/*
* For each hotplug downstream port, create add device link
* back to NHI so that PCIe tunnels can be re-established after
* sleep.
*/
for_each_pci_bridge(pdev, upstream->subordinate) {
const struct device_link *link;
if (!pci_is_pcie(pdev))
continue;
if (pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM ||
!pdev->is_hotplug_bridge)
continue;
link = device_link_add(&pdev->dev, &nhi->pdev->dev,
DL_FLAG_AUTOREMOVE_SUPPLIER |
DL_FLAG_PM_RUNTIME);
if (link) {
dev_dbg(&nhi->pdev->dev, "created link from %s\n",
dev_name(&pdev->dev));
} else {
dev_warn(&nhi->pdev->dev, "device link creation from %s failed\n",
dev_name(&pdev->dev));
}
}
}
struct tb *tb_probe(struct tb_nhi *nhi) struct tb *tb_probe(struct tb_nhi *nhi)
{ {
struct tb_cm *tcm; struct tb_cm *tcm;
...@@ -1594,5 +1658,8 @@ struct tb *tb_probe(struct tb_nhi *nhi) ...@@ -1594,5 +1658,8 @@ struct tb *tb_probe(struct tb_nhi *nhi)
tb_dbg(tb, "using software connection manager\n"); tb_dbg(tb, "using software connection manager\n");
tb_apple_add_links(nhi);
tb_acpi_add_links(nhi);
return tb; return tb;
} }
This diff is collapsed.
...@@ -195,6 +195,7 @@ struct tb_regs_switch_header { ...@@ -195,6 +195,7 @@ struct tb_regs_switch_header {
#define ROUTER_CS_5_SLP BIT(0) #define ROUTER_CS_5_SLP BIT(0)
#define ROUTER_CS_5_WOP BIT(1) #define ROUTER_CS_5_WOP BIT(1)
#define ROUTER_CS_5_WOU BIT(2) #define ROUTER_CS_5_WOU BIT(2)
#define ROUTER_CS_5_WOD BIT(3)
#define ROUTER_CS_5_C3S BIT(23) #define ROUTER_CS_5_C3S BIT(23)
#define ROUTER_CS_5_PTO BIT(24) #define ROUTER_CS_5_PTO BIT(24)
#define ROUTER_CS_5_UTO BIT(25) #define ROUTER_CS_5_UTO BIT(25)
...@@ -228,6 +229,7 @@ enum usb4_switch_op { ...@@ -228,6 +229,7 @@ enum usb4_switch_op {
USB4_SWITCH_OP_NVM_SET_OFFSET = 0x23, USB4_SWITCH_OP_NVM_SET_OFFSET = 0x23,
USB4_SWITCH_OP_DROM_READ = 0x24, USB4_SWITCH_OP_DROM_READ = 0x24,
USB4_SWITCH_OP_NVM_SECTOR_SIZE = 0x25, USB4_SWITCH_OP_NVM_SECTOR_SIZE = 0x25,
USB4_SWITCH_OP_BUFFER_ALLOC = 0x33,
}; };
/* Router TMU configuration */ /* Router TMU configuration */
...@@ -458,6 +460,8 @@ struct tb_regs_hop { ...@@ -458,6 +460,8 @@ struct tb_regs_hop {
#define TB_LC_SX_CTRL 0x96 #define TB_LC_SX_CTRL 0x96
#define TB_LC_SX_CTRL_WOC BIT(1) #define TB_LC_SX_CTRL_WOC BIT(1)
#define TB_LC_SX_CTRL_WOD BIT(2) #define TB_LC_SX_CTRL_WOD BIT(2)
#define TB_LC_SX_CTRL_WODPC BIT(3)
#define TB_LC_SX_CTRL_WODPD BIT(4)
#define TB_LC_SX_CTRL_WOU4 BIT(5) #define TB_LC_SX_CTRL_WOU4 BIT(5)
#define TB_LC_SX_CTRL_WOP BIT(6) #define TB_LC_SX_CTRL_WOP BIT(6)
#define TB_LC_SX_CTRL_L1C BIT(16) #define TB_LC_SX_CTRL_L1C BIT(16)
......
This diff is collapsed.
This diff is collapsed.
...@@ -27,6 +27,7 @@ enum tb_tunnel_type { ...@@ -27,6 +27,7 @@ enum tb_tunnel_type {
* @paths: All paths required by the tunnel * @paths: All paths required by the tunnel
* @npaths: Number of paths in @paths * @npaths: Number of paths in @paths
* @init: Optional tunnel specific initialization * @init: Optional tunnel specific initialization
* @deinit: Optional tunnel specific de-initialization
* @activate: Optional tunnel specific activation/deactivation * @activate: Optional tunnel specific activation/deactivation
* @consumed_bandwidth: Return how much bandwidth the tunnel consumes * @consumed_bandwidth: Return how much bandwidth the tunnel consumes
* @release_unused_bandwidth: Release all unused bandwidth * @release_unused_bandwidth: Release all unused bandwidth
...@@ -47,6 +48,7 @@ struct tb_tunnel { ...@@ -47,6 +48,7 @@ struct tb_tunnel {
struct tb_path **paths; struct tb_path **paths;
size_t npaths; size_t npaths;
int (*init)(struct tb_tunnel *tunnel); int (*init)(struct tb_tunnel *tunnel);
void (*deinit)(struct tb_tunnel *tunnel);
int (*activate)(struct tb_tunnel *tunnel, bool activate); int (*activate)(struct tb_tunnel *tunnel, bool activate);
int (*consumed_bandwidth)(struct tb_tunnel *tunnel, int *consumed_up, int (*consumed_bandwidth)(struct tb_tunnel *tunnel, int *consumed_up,
int *consumed_down); int *consumed_down);
......
This diff is collapsed.
This diff is collapsed.
...@@ -1527,6 +1527,13 @@ int tb_xdomain_lane_bonding_enable(struct tb_xdomain *xd) ...@@ -1527,6 +1527,13 @@ int tb_xdomain_lane_bonding_enable(struct tb_xdomain *xd)
return ret; return ret;
} }
ret = tb_port_wait_for_link_width(port, 2, 100);
if (ret) {
tb_port_warn(port, "timeout enabling lane bonding\n");
return ret;
}
tb_port_update_credits(port);
tb_xdomain_update_link_attributes(xd); tb_xdomain_update_link_attributes(xd);
dev_dbg(&xd->dev, "lane bonding enabled\n"); dev_dbg(&xd->dev, "lane bonding enabled\n");
...@@ -1548,7 +1555,10 @@ void tb_xdomain_lane_bonding_disable(struct tb_xdomain *xd) ...@@ -1548,7 +1555,10 @@ void tb_xdomain_lane_bonding_disable(struct tb_xdomain *xd)
port = tb_port_at(xd->route, tb_xdomain_parent(xd)); port = tb_port_at(xd->route, tb_xdomain_parent(xd));
if (port->dual_link_port) { if (port->dual_link_port) {
tb_port_lane_bonding_disable(port); tb_port_lane_bonding_disable(port);
if (tb_port_wait_for_link_width(port, 1, 100) == -ETIMEDOUT)
tb_port_warn(port, "timeout disabling lane bonding\n");
tb_port_disable(port->dual_link_port); tb_port_disable(port->dual_link_port);
tb_port_update_credits(port);
tb_xdomain_update_link_attributes(xd); tb_xdomain_update_link_attributes(xd);
dev_dbg(&xd->dev, "lane bonding disabled\n"); dev_dbg(&xd->dev, "lane bonding disabled\n");
......
...@@ -180,7 +180,7 @@ struct cxacru_data { ...@@ -180,7 +180,7 @@ struct cxacru_data {
struct mutex poll_state_serialize; struct mutex poll_state_serialize;
enum cxacru_poll_state poll_state; enum cxacru_poll_state poll_state;
/* contol handles */ /* control handles */
struct mutex cm_serialize; struct mutex cm_serialize;
u8 *rcv_buf; u8 *rcv_buf;
u8 *snd_buf; u8 *snd_buf;
......
...@@ -677,7 +677,7 @@ static int cdns3_gadget_ep0_set_halt(struct usb_ep *ep, int value) ...@@ -677,7 +677,7 @@ static int cdns3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
} }
/** /**
* cdns3_gadget_ep0_queue Transfer data on endpoint zero * cdns3_gadget_ep0_queue - Transfer data on endpoint zero
* @ep: pointer to endpoint zero object * @ep: pointer to endpoint zero object
* @request: pointer to request object * @request: pointer to request object
* @gfp_flags: gfp flags * @gfp_flags: gfp flags
...@@ -772,7 +772,7 @@ static int cdns3_gadget_ep0_queue(struct usb_ep *ep, ...@@ -772,7 +772,7 @@ static int cdns3_gadget_ep0_queue(struct usb_ep *ep,
} }
/** /**
* cdns3_gadget_ep_set_wedge Set wedge on selected endpoint * cdns3_gadget_ep_set_wedge - Set wedge on selected endpoint
* @ep: endpoint object * @ep: endpoint object
* *
* Returns 0 * Returns 0
...@@ -865,7 +865,7 @@ void cdns3_ep0_config(struct cdns3_device *priv_dev) ...@@ -865,7 +865,7 @@ void cdns3_ep0_config(struct cdns3_device *priv_dev)
} }
/** /**
* cdns3_init_ep0 Initializes software endpoint 0 of gadget * cdns3_init_ep0 - Initializes software endpoint 0 of gadget
* @priv_dev: extended gadget object * @priv_dev: extended gadget object
* @priv_ep: extended endpoint object * @priv_ep: extended endpoint object
* *
......
...@@ -155,7 +155,7 @@ static struct cdns3_request *cdns3_next_priv_request(struct list_head *list) ...@@ -155,7 +155,7 @@ static struct cdns3_request *cdns3_next_priv_request(struct list_head *list)
} }
/** /**
* select_ep - selects endpoint * cdns3_select_ep - selects endpoint
* @priv_dev: extended gadget object * @priv_dev: extended gadget object
* @ep: endpoint address * @ep: endpoint address
*/ */
...@@ -430,9 +430,7 @@ static int cdns3_start_all_request(struct cdns3_device *priv_dev, ...@@ -430,9 +430,7 @@ static int cdns3_start_all_request(struct cdns3_device *priv_dev,
if (ret) if (ret)
return ret; return ret;
list_del(&request->list); list_move_tail(&request->list, &priv_ep->pending_req_list);
list_add_tail(&request->list,
&priv_ep->pending_req_list);
if (request->stream_id != 0 || (priv_ep->flags & EP_TDLCHK_EN)) if (request->stream_id != 0 || (priv_ep->flags & EP_TDLCHK_EN))
break; break;
} }
...@@ -484,7 +482,7 @@ static void __cdns3_descmiss_copy_data(struct usb_request *request, ...@@ -484,7 +482,7 @@ static void __cdns3_descmiss_copy_data(struct usb_request *request,
} }
/** /**
* cdns3_wa2_descmiss_copy_data copy data from internal requests to * cdns3_wa2_descmiss_copy_data - copy data from internal requests to
* request queued by class driver. * request queued by class driver.
* @priv_ep: extended endpoint object * @priv_ep: extended endpoint object
* @request: request object * @request: request object
...@@ -1835,7 +1833,7 @@ __must_hold(&priv_dev->lock) ...@@ -1835,7 +1833,7 @@ __must_hold(&priv_dev->lock)
} }
/** /**
* cdns3_device_irq_handler- interrupt handler for device part of controller * cdns3_device_irq_handler - interrupt handler for device part of controller
* *
* @irq: irq number for cdns3 core device * @irq: irq number for cdns3 core device
* @data: structure of cdns3 * @data: structure of cdns3
...@@ -1879,7 +1877,7 @@ static irqreturn_t cdns3_device_irq_handler(int irq, void *data) ...@@ -1879,7 +1877,7 @@ static irqreturn_t cdns3_device_irq_handler(int irq, void *data)
} }
/** /**
* cdns3_device_thread_irq_handler- interrupt handler for device part * cdns3_device_thread_irq_handler - interrupt handler for device part
* of controller * of controller
* *
* @irq: irq number for cdns3 core device * @irq: irq number for cdns3 core device
...@@ -2022,7 +2020,7 @@ static void cdns3_configure_dmult(struct cdns3_device *priv_dev, ...@@ -2022,7 +2020,7 @@ static void cdns3_configure_dmult(struct cdns3_device *priv_dev,
} }
/** /**
* cdns3_ep_config Configure hardware endpoint * cdns3_ep_config - Configure hardware endpoint
* @priv_ep: extended endpoint object * @priv_ep: extended endpoint object
* @enable: set EP_CFG_ENABLE bit in ep_cfg register. * @enable: set EP_CFG_ENABLE bit in ep_cfg register.
*/ */
...@@ -2221,7 +2219,7 @@ usb_ep *cdns3_gadget_match_ep(struct usb_gadget *gadget, ...@@ -2221,7 +2219,7 @@ usb_ep *cdns3_gadget_match_ep(struct usb_gadget *gadget,
} }
/** /**
* cdns3_gadget_ep_alloc_request Allocates request * cdns3_gadget_ep_alloc_request - Allocates request
* @ep: endpoint object associated with request * @ep: endpoint object associated with request
* @gfp_flags: gfp flags * @gfp_flags: gfp flags
* *
...@@ -2244,7 +2242,7 @@ struct usb_request *cdns3_gadget_ep_alloc_request(struct usb_ep *ep, ...@@ -2244,7 +2242,7 @@ struct usb_request *cdns3_gadget_ep_alloc_request(struct usb_ep *ep,
} }
/** /**
* cdns3_gadget_ep_free_request Free memory occupied by request * cdns3_gadget_ep_free_request - Free memory occupied by request
* @ep: endpoint object associated with request * @ep: endpoint object associated with request
* @request: request to free memory * @request: request to free memory
*/ */
...@@ -2261,7 +2259,7 @@ void cdns3_gadget_ep_free_request(struct usb_ep *ep, ...@@ -2261,7 +2259,7 @@ void cdns3_gadget_ep_free_request(struct usb_ep *ep,
} }
/** /**
* cdns3_gadget_ep_enable Enable endpoint * cdns3_gadget_ep_enable - Enable endpoint
* @ep: endpoint object * @ep: endpoint object
* @desc: endpoint descriptor * @desc: endpoint descriptor
* *
...@@ -2396,7 +2394,7 @@ static int cdns3_gadget_ep_enable(struct usb_ep *ep, ...@@ -2396,7 +2394,7 @@ static int cdns3_gadget_ep_enable(struct usb_ep *ep,
} }
/** /**
* cdns3_gadget_ep_disable Disable endpoint * cdns3_gadget_ep_disable - Disable endpoint
* @ep: endpoint object * @ep: endpoint object
* *
* Returns 0 on success, error code elsewhere * Returns 0 on success, error code elsewhere
...@@ -2486,7 +2484,7 @@ static int cdns3_gadget_ep_disable(struct usb_ep *ep) ...@@ -2486,7 +2484,7 @@ static int cdns3_gadget_ep_disable(struct usb_ep *ep)
} }
/** /**
* cdns3_gadget_ep_queue Transfer data on endpoint * __cdns3_gadget_ep_queue - Transfer data on endpoint
* @ep: endpoint object * @ep: endpoint object
* @request: request object * @request: request object
* @gfp_flags: gfp flags * @gfp_flags: gfp flags
...@@ -2586,7 +2584,7 @@ static int cdns3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request, ...@@ -2586,7 +2584,7 @@ static int cdns3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
} }
/** /**
* cdns3_gadget_ep_dequeue Remove request from transfer queue * cdns3_gadget_ep_dequeue - Remove request from transfer queue
* @ep: endpoint object associated with request * @ep: endpoint object associated with request
* @request: request object * @request: request object
* *
...@@ -2653,7 +2651,7 @@ int cdns3_gadget_ep_dequeue(struct usb_ep *ep, ...@@ -2653,7 +2651,7 @@ int cdns3_gadget_ep_dequeue(struct usb_ep *ep,
} }
/** /**
* __cdns3_gadget_ep_set_halt Sets stall on selected endpoint * __cdns3_gadget_ep_set_halt - Sets stall on selected endpoint
* Should be called after acquiring spin_lock and selecting ep * Should be called after acquiring spin_lock and selecting ep
* @priv_ep: endpoint object to set stall on. * @priv_ep: endpoint object to set stall on.
*/ */
...@@ -2674,7 +2672,7 @@ void __cdns3_gadget_ep_set_halt(struct cdns3_endpoint *priv_ep) ...@@ -2674,7 +2672,7 @@ void __cdns3_gadget_ep_set_halt(struct cdns3_endpoint *priv_ep)
} }
/** /**
* __cdns3_gadget_ep_clear_halt Clears stall on selected endpoint * __cdns3_gadget_ep_clear_halt - Clears stall on selected endpoint
* Should be called after acquiring spin_lock and selecting ep * Should be called after acquiring spin_lock and selecting ep
* @priv_ep: endpoint object to clear stall on * @priv_ep: endpoint object to clear stall on
*/ */
...@@ -2719,7 +2717,7 @@ int __cdns3_gadget_ep_clear_halt(struct cdns3_endpoint *priv_ep) ...@@ -2719,7 +2717,7 @@ int __cdns3_gadget_ep_clear_halt(struct cdns3_endpoint *priv_ep)
} }
/** /**
* cdns3_gadget_ep_set_halt Sets/clears stall on selected endpoint * cdns3_gadget_ep_set_halt - Sets/clears stall on selected endpoint
* @ep: endpoint object to set/clear stall on * @ep: endpoint object to set/clear stall on
* @value: 1 for set stall, 0 for clear stall * @value: 1 for set stall, 0 for clear stall
* *
...@@ -2765,7 +2763,7 @@ static const struct usb_ep_ops cdns3_gadget_ep_ops = { ...@@ -2765,7 +2763,7 @@ static const struct usb_ep_ops cdns3_gadget_ep_ops = {
}; };
/** /**
* cdns3_gadget_get_frame Returns number of actual ITP frame * cdns3_gadget_get_frame - Returns number of actual ITP frame
* @gadget: gadget object * @gadget: gadget object
* *
* Returns number of actual ITP frame * Returns number of actual ITP frame
...@@ -2874,7 +2872,7 @@ static void cdns3_gadget_config(struct cdns3_device *priv_dev) ...@@ -2874,7 +2872,7 @@ static void cdns3_gadget_config(struct cdns3_device *priv_dev)
} }
/** /**
* cdns3_gadget_udc_start Gadget start * cdns3_gadget_udc_start - Gadget start
* @gadget: gadget object * @gadget: gadget object
* @driver: driver which operates on this gadget * @driver: driver which operates on this gadget
* *
...@@ -2920,7 +2918,7 @@ static int cdns3_gadget_udc_start(struct usb_gadget *gadget, ...@@ -2920,7 +2918,7 @@ static int cdns3_gadget_udc_start(struct usb_gadget *gadget,
} }
/** /**
* cdns3_gadget_udc_stop Stops gadget * cdns3_gadget_udc_stop - Stops gadget
* @gadget: gadget object * @gadget: gadget object
* *
* Returns 0 * Returns 0
...@@ -2983,7 +2981,7 @@ static void cdns3_free_all_eps(struct cdns3_device *priv_dev) ...@@ -2983,7 +2981,7 @@ static void cdns3_free_all_eps(struct cdns3_device *priv_dev)
} }
/** /**
* cdns3_init_eps Initializes software endpoints of gadget * cdns3_init_eps - Initializes software endpoints of gadget
* @priv_dev: extended gadget object * @priv_dev: extended gadget object
* *
* Returns 0 on success, error code elsewhere * Returns 0 on success, error code elsewhere
......
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
/** /*
* cdns3-imx.c - NXP i.MX specific Glue layer for Cadence USB Controller * cdns3-imx.c - NXP i.MX specific Glue layer for Cadence USB Controller
* *
* Copyright (C) 2019 NXP * Copyright (C) 2019 NXP
......
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
/** /*
* cdns3-ti.c - TI specific Glue layer for Cadence USB Controller * cdns3-ti.c - TI specific Glue layer for Cadence USB Controller
* *
* Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com * Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment