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:
c_chmask capture channel mask
c_srate capture sampling rate
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_srate playback sampling rate
p_ssize playback sample size (bytes)
......
......@@ -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
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
area and authenticate the image in one action.
- Writing "2" will run some basic validation on the image
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
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
based mailbox before the device is power cycled. Writing
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
Date: Jan 2018
KernelVersion: 4.15
......@@ -276,6 +293,39 @@ Contact: thunderbolt-software@lists.01.org
Description: This contains XDomain service specific settings as
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
Date: Oct 2020
KernelVersion: v5.9
......@@ -308,17 +358,3 @@ Date: Oct 2020
KernelVersion: v5.9
Contact: Mika Westerberg <mika.westerberg@linux.intel.com>
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:
files hold a string value (enable or disable) indicating whether
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
Date: July 2012
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``
depend on the order they are registered in the NVMem subsystem. N in
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
--------------------------------------------------
If the existing NVM is not properly authenticated (or is missing) the
......
......@@ -15,7 +15,9 @@ properties:
const: 1
compatible:
const: allwinner,sun8i-h3-usb-phy
enum:
- allwinner,sun8i-h3-usb-phy
- allwinner,sun50i-h616-usb-phy
reg:
items:
......
......@@ -23,6 +23,8 @@ properties:
- qcom,msm8998-qusb2-phy
- qcom,sdm660-qusb2-phy
- qcom,ipq6018-qusb2-phy
- qcom,sm4250-qusb2-phy
- qcom,sm6115-qusb2-phy
- items:
- enum:
- qcom,sc7180-qusb2-phy
......
......@@ -22,6 +22,9 @@ properties:
- allwinner,sun8i-a83t-musb
- allwinner,sun50i-h6-musb
- const: allwinner,sun8i-a33-musb
- items:
- const: allwinner,sun50i-h616-musb
- const: allwinner,sun8i-h3-musb
reg:
maxItems: 1
......
......@@ -75,6 +75,7 @@ required:
- reg
- reg-names
- interrupts
- interrupt-names
additionalProperties: false
......
......@@ -24,6 +24,7 @@ properties:
- rockchip,rk3188-usb
- rockchip,rk3228-usb
- rockchip,rk3288-usb
- rockchip,rk3308-usb
- rockchip,rk3328-usb
- rockchip,rk3368-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:
- qcom,sc7280-dwc3
- qcom,sdm845-dwc3
- qcom,sdx55-dwc3
- qcom,sm4250-dwc3
- qcom,sm6115-dwc3
- qcom,sm8150-dwc3
- qcom,sm8250-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:
(c) requested data transfer length is invalid: negative
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
for periodic transfers (interrupt, isochronous).
......
......@@ -728,6 +728,8 @@ The uac2 function provides these attributes in its function directory:
c_chmask capture channel mask
c_srate capture sampling rate
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_srate playback sampling rate
p_ssize playback sample size (bytes)
......
......@@ -148,7 +148,7 @@ ethernet: ethernet@4e000000 {
usb: usb@4f000000 {
compatible = "nxp,usb-isp1761";
reg = <0x4f000000 0x20000>;
port1-otg;
dr_mode = "peripheral";
};
bridge {
......
......@@ -166,7 +166,7 @@ usb@3b000000 {
reg = <0x3b000000 0x20000>;
interrupt-parent = <&intc_fpga1176>;
interrupts = <0 11 IRQ_TYPE_LEVEL_HIGH>;
port1-otg;
dr_mode = "peripheral";
};
bridge {
......
......@@ -712,7 +712,7 @@ usb@4f000000 {
reg = <0x4f000000 0x20000>;
interrupt-parent = <&intc_tc11mp>;
interrupts = <0 3 IRQ_TYPE_LEVEL_HIGH>;
port1-otg;
dr_mode = "peripheral";
};
};
};
......@@ -164,7 +164,7 @@ ethernet: ethernet@4e000000 {
usb: usb@4f000000 {
compatible = "nxp,usb-isp1761";
reg = <0x4f000000 0x20000>;
port1-otg;
dr_mode = "peripheral";
};
bridge {
......
......@@ -144,7 +144,7 @@ usb@203000000 {
compatible = "nxp,usb-isp1761";
reg = <2 0x03000000 0x20000>;
interrupts = <16>;
port1-otg;
dr_mode = "peripheral";
};
iofpga-bus@300000000 {
......
......@@ -62,7 +62,7 @@ usb@3,03000000 {
compatible = "nxp,usb-isp1761";
reg = <3 0x03000000 0x20000>;
interrupts = <16>;
port1-otg;
dr_mode = "peripheral";
};
iofpga@7,00000000 {
......
......@@ -2409,6 +2409,25 @@ static ssize_t online_store(struct device *dev, struct device_attribute *attr,
}
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)
{
return sysfs_create_groups(&dev->kobj, groups);
......@@ -2586,8 +2605,16 @@ static int device_add_attrs(struct device *dev)
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;
err_remove_dev_waiting_for_supplier:
device_remove_file(dev, &dev_attr_waiting_for_supplier);
err_remove_dev_online:
device_remove_file(dev, &dev_attr_online);
err_remove_dev_groups:
......@@ -2607,6 +2634,7 @@ static void device_remove_attrs(struct device *dev)
struct class *class = dev->class;
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_online);
device_remove_groups(dev, dev->groups);
......
......@@ -1576,6 +1576,26 @@ static void set_pcie_untrusted(struct pci_dev *dev)
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?
* @dev: PCI device
......@@ -1823,6 +1843,8 @@ int pci_setup_device(struct pci_dev *dev)
/* Early fixups, before probing the BARs */
pci_fixup_device(pci_fixup_early, dev);
pci_set_removable(dev);
pci_info(dev, "[%04x:%04x] type %02x class %#08x\n",
dev->vendor, dev->device, dev->hdr_type, dev->class);
......
......@@ -219,6 +219,22 @@ static const struct qusb2_phy_init_tbl msm8998_init_tbl[] = {
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[] = {
[QUSB2PHY_PLL_CORE_INPUT_OVERRIDE] = 0xa8,
[QUSB2PHY_PLL_STATUS] = 0x1a0,
......@@ -342,6 +358,18 @@ static const struct qusb2_phy_cfg sdm660_phy_cfg = {
.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[] = {
"vdda-pll", "vdda-phy-dpdm",
};
......@@ -888,6 +916,12 @@ static const struct of_device_id qusb2_phy_of_match_table[] = {
}, {
.compatible = "qcom,sdm660-qusb2-phy",
.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
......
This diff is collapsed.
This diff is collapsed.
// 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>
......@@ -321,11 +321,17 @@ static void tegra_xusb_lane_program(struct tegra_xusb_lane *lane)
if (soc->num_funcs < 2)
return;
if (lane->pad->ops->iddq_enable)
lane->pad->ops->iddq_enable(lane);
/* choose function */
value = padctl_readl(padctl, soc->offset);
value &= ~(soc->mask << soc->shift);
value |= lane->function << soc->shift;
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)
......@@ -376,7 +382,7 @@ static int tegra_xusb_setup_pads(struct tegra_xusb_padctl *padctl)
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 *func = lane->soc->funcs[lane->function];
......@@ -1267,10 +1273,36 @@ static int tegra_xusb_padctl_remove(struct platform_device *pdev)
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 = {
.driver = {
.name = "tegra-xusb-padctl",
.of_match_table = tegra_xusb_padctl_of_match,
.pm = &tegra_xusb_padctl_pm_ops,
},
.probe = tegra_xusb_padctl_probe,
.remove = tegra_xusb_padctl_remove,
......@@ -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);
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,
unsigned int port, bool enable)
{
......
/* 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.
*/
......@@ -11,6 +11,7 @@
#include <linux/mutex.h>
#include <linux/workqueue.h>
#include <linux/usb/ch9.h>
#include <linux/usb/otg.h>
#include <linux/usb/role.h>
......@@ -35,6 +36,10 @@ struct tegra_xusb_lane_soc {
const char * const *funcs;
unsigned int num_funcs;
struct {
unsigned int misc_ctl2;
} regs;
};
struct tegra_xusb_lane {
......@@ -126,8 +131,17 @@ struct tegra_xusb_lane_ops {
struct device_node *np,
unsigned int index);
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
*/
......@@ -230,7 +244,7 @@ struct tegra_xusb_pcie_pad {
struct reset_control *rst;
struct clk *pll;
unsigned int enable;
bool enable;
};
static inline struct tegra_xusb_pcie_pad *
......@@ -245,7 +259,7 @@ struct tegra_xusb_sata_pad {
struct reset_control *rst;
struct clk *pll;
unsigned int enable;
bool enable;
};
static inline struct tegra_xusb_sata_pad *
......@@ -388,6 +402,8 @@ struct tegra_xusb_padctl_ops {
const struct tegra_xusb_padctl_soc *soc);
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,
unsigned int index);
int (*hsic_set_idle)(struct tegra_xusb_padctl *padctl,
......
......@@ -2,7 +2,7 @@
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 += 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_DEBUG_FS) += debugfs.o
......
......@@ -180,3 +180,209 @@ bool tb_acpi_is_xdomain_allowed(void)
return osc_sb_native_usb4_control & OSC_USB_XDOMAIN;
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,
return status_to_errno(out);
}
static int dma_port_flash_read_block(struct tb_dma_port *dma, u32 address,
void *buf, u32 size)
static int dma_port_flash_read_block(void *data, unsigned int dwaddress,
void *buf, size_t dwords)
{
struct tb_dma_port *dma = data;
struct tb_switch *sw = dma->sw;
u32 in, dwaddress, dwords;
int ret;
dwaddress = address / 4;
dwords = size / 4;
u32 in;
in = MAIL_IN_CMD_FLASH_READ << MAIL_IN_CMD_SHIFT;
if (dwords < MAIL_DATA_DWORDS)
......@@ -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);
}
static int dma_port_flash_write_block(struct tb_dma_port *dma, u32 address,
const void *buf, u32 size)
static int dma_port_flash_write_block(void *data, unsigned int dwaddress,
const void *buf, size_t dwords)
{
struct tb_dma_port *dma = data;
struct tb_switch *sw = dma->sw;
u32 in, dwaddress, dwords;
int ret;
dwords = size / 4;
u32 in;
/* Write the block to MAIL_DATA registers */
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,
in = MAIL_IN_CMD_FLASH_WRITE << MAIL_IN_CMD_SHIFT;
/* CSS header write is always done to the same magic address */
if (address >= DMA_PORT_CSS_ADDRESS) {
dwaddress = DMA_PORT_CSS_ADDRESS;
if (dwaddress >= DMA_PORT_CSS_ADDRESS)
in |= MAIL_IN_CSS;
} else {
dwaddress = address / 4;
}
in |= ((dwords - 1) << MAIL_IN_DWORDS_SHIFT) & MAIL_IN_DWORDS_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,
int dma_port_flash_read(struct tb_dma_port *dma, unsigned int address,
void *buf, size_t size)
{
unsigned int retries = DMA_PORT_RETRIES;
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;
return tb_nvm_read_data(address, buf, size, DMA_PORT_RETRIES,
dma_port_flash_read_block, dma);
}
/**
......@@ -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,
const void *buf, size_t size)
{
unsigned int retries = DMA_PORT_RETRIES;
unsigned int offset;
if (address >= DMA_PORT_CSS_ADDRESS) {
offset = 0;
if (size > DMA_PORT_CSS_MAX_SIZE)
if (address >= DMA_PORT_CSS_ADDRESS && 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);
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;
return tb_nvm_write_data(address, buf, size, DMA_PORT_RETRIES,
dma_port_flash_write_block, dma);
}
/**
......
......@@ -881,11 +881,12 @@ int tb_domain_init(void)
int ret;
tb_test_init();
tb_debugfs_init();
tb_acpi_init();
ret = tb_xdomain_init();
if (ret)
goto err_debugfs;
goto err_acpi;
ret = bus_register(&tb_bus_type);
if (ret)
goto err_xdomain;
......@@ -894,7 +895,8 @@ int tb_domain_init(void)
err_xdomain:
tb_xdomain_exit();
err_debugfs:
err_acpi:
tb_acpi_exit();
tb_debugfs_exit();
tb_test_exit();
......@@ -907,6 +909,7 @@ void tb_domain_exit(void)
ida_destroy(&tb_domain_ida);
tb_nvm_exit();
tb_xdomain_exit();
tb_acpi_exit();
tb_debugfs_exit();
tb_test_exit();
}
......@@ -215,6 +215,9 @@ static u32 tb_crc32(void *data, size_t len)
}
#define TB_DROM_DATA_START 13
#define TB_DROM_HEADER_SIZE 22
#define USB4_DROM_HEADER_SIZE 16
struct tb_drom_header {
/* BYTE 0 */
u8 uid_crc8; /* checksum for uid */
......@@ -224,9 +227,9 @@ struct tb_drom_header {
u32 data_crc32; /* checksum for data_len bytes starting at byte 13 */
/* BYTE 13 */
u8 device_rom_revision; /* should be <= 1 */
u16 data_len:10;
u8 __unknown1:6;
/* BYTES 16-21 */
u16 data_len:12;
u8 reserved:4;
/* BYTES 16-21 - Only for TBT DROM, nonexistent in USB4 DROM */
u16 vendor_id;
u16 model_id;
u8 model_rev;
......@@ -401,10 +404,10 @@ static int tb_drom_parse_entry_port(struct tb_switch *sw,
*
* 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;
u16 pos = sizeof(*header);
u16 pos = header_size;
u16 drom_size = header->data_len + TB_DROM_DATA_START;
int res;
......@@ -566,7 +569,7 @@ static int tb_drom_parse(struct tb_switch *sw)
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)
......@@ -583,7 +586,7 @@ static int usb4_drom_parse(struct tb_switch *sw)
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)
static bool icm_tgl_is_supported(struct tb *tb)
{
unsigned long end = jiffies + msecs_to_jiffies(10);
do {
u32 val;
/*
* If the firmware is not running use software CM. This platform
* should fully support both.
*/
val = ioread32(tb->nhi->iobase + REG_FW_STS);
return !!(val & REG_FW_STS_NVM_AUTH_DONE);
if (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)
......@@ -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_H_NHI0:
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->driver_ready = icm_icl_driver_ready;
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,
if (ret)
return ret;
ctrl &= ~(TB_LC_SX_CTRL_WOC | TB_LC_SX_CTRL_WOD | TB_LC_SX_CTRL_WOP |
TB_LC_SX_CTRL_WOU4);
ctrl &= ~(TB_LC_SX_CTRL_WOC | TB_LC_SX_CTRL_WOD | TB_LC_SX_CTRL_WODPC |
TB_LC_SX_CTRL_WODPD | TB_LC_SX_CTRL_WOP | TB_LC_SX_CTRL_WOU4);
if (flags & TB_WAKE_ON_CONNECT)
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,
ctrl |= TB_LC_SX_CTRL_WOU4;
if (flags & TB_WAKE_ON_PCIE)
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);
}
......
......@@ -17,7 +17,6 @@
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/property.h>
#include <linux/platform_data/x86/apple.h>
#include "nhi.h"
#include "nhi_regs.h"
......@@ -1127,69 +1126,6 @@ static bool nhi_imr_valid(struct pci_dev *pdev)
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)
{
struct tb *tb;
......@@ -1278,9 +1214,6 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return res;
}
tb_apple_add_links(nhi);
tb_acpi_add_links(nhi);
tb = nhi_select_cm(nhi);
if (!tb) {
dev_err(&nhi->pdev->dev,
......@@ -1400,6 +1333,10 @@ static struct pci_device_id nhi_ids[] = {
.driver_data = (kernel_ulong_t)&icl_nhi_ops },
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TGL_H_NHI1),
.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 */
{ PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_USB4, ~0) },
......
......@@ -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_NHI 0x15eb
#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_NHI0 0x8a17
#define PCI_DEVICE_ID_INTEL_TGL_NHI0 0x9a1b
......
......@@ -164,6 +164,101 @@ void tb_nvm_free(struct tb_nvm *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)
{
ida_destroy(&nvm_ida);
......
......@@ -367,7 +367,7 @@ static void __tb_path_deallocate_nfc(struct tb_path *path, int first_hop)
int i, res;
for (i = first_hop; i < path->path_length; i++) {
res = tb_port_add_nfc_credits(path->hops[i].in_port,
-path->nfc_credits);
-path->hops[i].nfc_credits);
if (res)
tb_port_warn(path->hops[i].in_port,
"nfc credits deallocation failed for hop %d\n",
......@@ -502,7 +502,7 @@ int tb_path_activate(struct tb_path *path)
/* Add non flow controlled credits. */
for (i = path->path_length - 1; i >= 0; i--) {
res = tb_port_add_nfc_credits(path->hops[i].in_port,
path->nfc_credits);
path->hops[i].nfc_credits);
if (res) {
__tb_path_deallocate_nfc(path, i);
goto err;
......
......@@ -12,7 +12,17 @@ static void quirk_force_power_link(struct tb_switch *sw)
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 {
u16 hw_vendor_id;
u16 hw_device_id;
u16 vendor;
u16 device;
void (*hook)(struct tb_switch *sw);
......@@ -20,7 +30,13 @@ struct tb_quirk {
static const struct tb_quirk tb_quirks[] = {
/* 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)
for (i = 0; i < ARRAY_SIZE(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)
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)
unsigned int image_size, hdr_size;
const u8 *buf = rt->nvm->buf;
u16 ds_size, device;
int ret;
image_size = rt->nvm->buf_data_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)
buf += 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);
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,
......@@ -176,8 +212,7 @@ static ssize_t nvm_authenticate_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct tb_retimer *rt = tb_to_retimer(dev);
bool val;
int ret;
int val, ret;
pm_runtime_get_sync(&rt->dev);
......@@ -191,7 +226,7 @@ static ssize_t nvm_authenticate_store(struct device *dev,
goto exit_unlock;
}
ret = kstrtobool(buf, &val);
ret = kstrtoint(buf, 10, &val);
if (ret)
goto exit_unlock;
......@@ -199,16 +234,22 @@ static ssize_t nvm_authenticate_store(struct device *dev,
rt->auth_status = 0;
if (val) {
if (val == AUTHENTICATE_ONLY) {
ret = tb_retimer_nvm_authenticate(rt, true);
} else {
if (!rt->nvm->flushed) {
if (!rt->nvm->buf) {
ret = -EINVAL;
goto exit_unlock;
}
ret = tb_retimer_nvm_validate_and_write(rt);
if (ret)
if (ret || val == WRITE_ONLY)
goto exit_unlock;
ret = usb4_port_retimer_nvm_authenticate(rt->port, rt->index);
}
if (val == WRITE_AND_AUTHENTICATE)
ret = tb_retimer_nvm_authenticate(rt, false);
}
}
exit_unlock:
......@@ -283,11 +324,13 @@ struct device_type tb_retimer_type = {
static int tb_retimer_add(struct tb_port *port, u8 index, u32 auth_status)
{
struct usb4_port *usb4;
struct tb_retimer *rt;
u32 vendor, device;
int ret;
if (!port->cap_usb4)
usb4 = port->usb4;
if (!usb4)
return -EINVAL;
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)
rt->port = port;
rt->tb = port->sw->tb;
rt->dev.parent = &port->sw->dev;
rt->dev.parent = &usb4->dev;
rt->dev.bus = &tb_bus_type;
rt->dev.type = &tb_retimer_type;
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)
struct tb_retimer_lookup lookup = { .port = port, .index = index };
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)
return tb_to_retimer(dev);
......@@ -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
* @port: USB4 port to scan
* @add: If true also registers found retimers
*
* Tries to enumerate on-board retimers connected to @port. Found
* retimers are registered as children of @port. Does not scan for cable
* retimers for now.
* Brings the sideband into a state where retimers can be accessed.
* Then Tries to enumerate on-board retimers connected to @port. Found
* 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] = {};
int ret, i, last_idx = 0;
if (!port->cap_usb4)
return 0;
/*
* Send broadcast RT to make sure retimer indices facing this
* port are set.
......@@ -420,6 +462,13 @@ int tb_retimer_scan(struct tb_port *port)
if (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.
* If the retimer has it set, store it for the new retimer
......@@ -451,10 +500,10 @@ int tb_retimer_scan(struct tb_port *port)
rt = tb_port_find_retimer(port, i);
if (rt) {
put_device(&rt->dev);
} else {
} else if (add) {
ret = tb_retimer_add(port, i, status[i]);
if (ret && ret != -EOPNOTSUPP)
return ret;
break;
}
}
......@@ -479,7 +528,10 @@ static int remove_retimer(struct device *dev, void *data)
*/
void tb_retimer_remove_all(struct tb_port *port)
{
if (port->cap_usb4)
device_for_each_child_reverse(&port->sw->dev, port,
struct usb4_port *usb4;
usb4 = port->usb4;
if (usb4)
device_for_each_child_reverse(&usb4->dev, port,
remove_retimer);
}
......@@ -17,7 +17,9 @@
enum usb4_sb_opcode {
USB4_SB_OPCODE_ERR = 0x20525245, /* "ERR " */
USB4_SB_OPCODE_ONS = 0x444d4321, /* "!CMD" */
USB4_SB_OPCODE_ROUTER_OFFLINE = 0x4e45534c, /* "LSEN" */
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_GET_NVM_SECTOR_SIZE = 0x53534e47, /* "GNSS" */
USB4_SB_OPCODE_NVM_SET_OFFSET = 0x53504f42, /* "BOPS" */
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -195,6 +195,7 @@ struct tb_regs_switch_header {
#define ROUTER_CS_5_SLP BIT(0)
#define ROUTER_CS_5_WOP BIT(1)
#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_PTO BIT(24)
#define ROUTER_CS_5_UTO BIT(25)
......@@ -228,6 +229,7 @@ enum usb4_switch_op {
USB4_SWITCH_OP_NVM_SET_OFFSET = 0x23,
USB4_SWITCH_OP_DROM_READ = 0x24,
USB4_SWITCH_OP_NVM_SECTOR_SIZE = 0x25,
USB4_SWITCH_OP_BUFFER_ALLOC = 0x33,
};
/* Router TMU configuration */
......@@ -458,6 +460,8 @@ struct tb_regs_hop {
#define TB_LC_SX_CTRL 0x96
#define TB_LC_SX_CTRL_WOC BIT(1)
#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_WOP BIT(6)
#define TB_LC_SX_CTRL_L1C BIT(16)
......
This diff is collapsed.
This diff is collapsed.
......@@ -27,6 +27,7 @@ enum tb_tunnel_type {
* @paths: All paths required by the tunnel
* @npaths: Number of paths in @paths
* @init: Optional tunnel specific initialization
* @deinit: Optional tunnel specific de-initialization
* @activate: Optional tunnel specific activation/deactivation
* @consumed_bandwidth: Return how much bandwidth the tunnel consumes
* @release_unused_bandwidth: Release all unused bandwidth
......@@ -47,6 +48,7 @@ struct tb_tunnel {
struct tb_path **paths;
size_t npaths;
int (*init)(struct tb_tunnel *tunnel);
void (*deinit)(struct tb_tunnel *tunnel);
int (*activate)(struct tb_tunnel *tunnel, bool activate);
int (*consumed_bandwidth)(struct tb_tunnel *tunnel, int *consumed_up,
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)
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);
dev_dbg(&xd->dev, "lane bonding enabled\n");
......@@ -1548,7 +1555,10 @@ void tb_xdomain_lane_bonding_disable(struct tb_xdomain *xd)
port = tb_port_at(xd->route, tb_xdomain_parent(xd));
if (port->dual_link_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_update_credits(port);
tb_xdomain_update_link_attributes(xd);
dev_dbg(&xd->dev, "lane bonding disabled\n");
......
......@@ -180,7 +180,7 @@ struct cxacru_data {
struct mutex poll_state_serialize;
enum cxacru_poll_state poll_state;
/* contol handles */
/* control handles */
struct mutex cm_serialize;
u8 *rcv_buf;
u8 *snd_buf;
......
This diff is collapsed.
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0
/**
/*
* cdns3-imx.c - NXP i.MX specific Glue layer for Cadence USB Controller
*
* Copyright (C) 2019 NXP
......
......@@ -170,7 +170,7 @@ static int cdns3_plat_probe(struct platform_device *pdev)
}
/**
* cdns3_remove - unbind drd driver and clean up
* cdns3_plat_remove() - unbind drd driver and clean up
* @pdev: Pointer to Linux platform device
*
* Returns 0 on success otherwise negative errno
......
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