USB/Thunderbolt updates for 6.11-rc1

Here is the big set of USB and Thunderbolt changes for 6.11-rc1.
 Nothing earth-shattering in here, just constant forward progress in
 adding support for new hardware and better debugging functionalities for
 thunderbolt devices and the subsystem.  Included in here are:
   - thunderbolt debugging update and driver additions
   - xhci driver updates
   - typec driver updates
   - kselftest device driver changes (acked by the relevant maintainers,
     depended on other changes in this tree.)
   - cdns3 driver updates
   - gadget driver updates
   - MODULE_DESCRIPTION() additions
   - dwc3 driver updates and fixes
 
 All of these have been in linux-next for a while with no reported
 issues.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCZppaNA8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ylXZwCgrEtIAQw0x6EF7w/iTWVS5UJj9AEAoLCj5UwO
 WX978uThyUctuYYKbw+8
 =Cm7j
 -----END PGP SIGNATURE-----

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

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

  Nothing earth-shattering in here, just constant forward progress in
  adding support for new hardware and better debugging functionalities
  for thunderbolt devices and the subsystem. Included in here are:

   - thunderbolt debugging update and driver additions

   - xhci driver updates

   - typec driver updates

   - kselftest device driver changes (acked by the relevant maintainers,
     depended on other changes in this tree.)

   - cdns3 driver updates

   - gadget driver updates

   - MODULE_DESCRIPTION() additions

   - dwc3 driver updates and fixes

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

* tag 'usb-6.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (112 commits)
  kselftest: devices: Add test to detect device error logs
  kselftest: Move ksft helper module to common directory
  kselftest: devices: Move discoverable devices test to subdirectory
  usb: gadget: f_uac2: fix non-newline-terminated function name
  USB: uas: Implement the new shutdown callback
  USB: core: add 'shutdown' callback to usb_driver
  usb: typec: Drop explicit initialization of struct i2c_device_id::driver_data to 0
  usb: dwc3: enable CCI support for AMD-xilinx DWC3 controller
  usb: dwc2: add support for other Lantiq SoCs
  usb: gadget: Use u16 types for 16-bit fields
  usb: gadget: midi2: Fix incorrect default MIDI2 protocol setup
  usb: dwc3: core: Check all ports when set phy suspend
  usb: typec: tcpci: add support to set connector orientation
  dt-bindings: usb: Convert fsl-usb to yaml
  usb: typec: ucsi: reorder operations in ucsi_run_command()
  usb: typec: ucsi: extract common code for command handling
  usb: typec: ucsi: inline ucsi_read_message_in
  usb: typec: ucsi: rework command execution functions
  usb: typec: ucsi: split read operation
  usb: typec: ucsi: simplify command sending API
  ...
This commit is contained in:
Linus Torvalds 2024-07-19 15:37:48 -07:00
commit 04d17331ca
127 changed files with 2567 additions and 1211 deletions

View File

@ -75,3 +75,13 @@ Description:
The default value is 1 (GNU Remote Debug command).
Other permissible value is 0 which is for vendor defined debug
target.
What: /sys/bus/pci/drivers/xhci_hcd/.../dbc_poll_interval_ms
Date: February 2024
Contact: Mathias Nyman <mathias.nyman@linux.intel.com>
Description:
This attribute adjust the polling interval used to check for
DbC events. Unit is milliseconds. Accepted values range from 0
up to 5000. The default value is 64 ms.
This polling interval is used while DbC is enabled but has no
active data transfers.

View File

@ -7041,6 +7041,9 @@
usb-storage.delay_use=
[UMS] The delay in seconds before a new device is
scanned for Logical Units (default 1).
Optionally the delay in milliseconds if the value has
suffix with "ms".
Example: delay_use=2567ms
usb-storage.quirks=
[UMS] A list of quirks entries to supplement or

View File

@ -42,8 +42,11 @@ properties:
- const: otg
- const: wakeup
dr_mode:
enum: [host, otg, peripheral]
port:
$ref: /schemas/graph.yaml#/properties/port
description:
This port is used with the 'usb-role-switch' property to connect the
cdns3 to type C connector.
maximum-speed:
enum: [super-speed, high-speed, full-speed]
@ -70,6 +73,9 @@ properties:
description: Enable resetting of PHY if Rx fail is detected
type: boolean
dependencies:
port: [ usb-role-switch ]
required:
- compatible
- reg
@ -77,7 +83,10 @@ required:
- interrupts
- interrupt-names
additionalProperties: false
allOf:
- $ref: usb-drd.yaml#
unevaluatedProperties: false
examples:
- |

View File

@ -188,7 +188,7 @@ required:
- clocks
- clock-names
additionalProperties: false
unevaluatedProperties: false
examples:
- |

View File

@ -0,0 +1,95 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/fsl,usb2.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Freescale SOC USB controllers
maintainers:
- Frank Li <Frank.Li@nxp.com>
description: |
The device node for a USB controller that is part of a Freescale
SOC is as described in the document "Open Firmware Recommended
Practice: Universal Serial Bus" with the following modifications
and additions.
properties:
compatible:
oneOf:
- enum:
- fsl-usb2-mph
- fsl-usb2-dr
- items:
- enum:
- fsl-usb2-dr-v2.2
- fsl-usb2-dr-v2.5
- const: fsl-usb2-dr
reg:
maxItems: 1
interrupts:
maxItems: 1
phy_type:
$ref: /schemas/types.yaml#/definitions/string
enum: [ulpi, serial, utmi, utmi_wide]
port0:
$ref: /schemas/types.yaml#/definitions/flag
description:
Indicates port0 is connected for fsl-usb2-mph compatible controllers.
port1:
$ref: /schemas/types.yaml#/definitions/flag
description:
Indicates port1 is connected for "fsl-usb2-mph" compatible controllers.
fsl,invert-drvvbus:
$ref: /schemas/types.yaml#/definitions/flag
description:
for MPC5121 USB0 only. Indicates the
port power polarity of internal PHY signal DRVVBUS is inverted.
fsl,invert-pwr-fault:
$ref: /schemas/types.yaml#/definitions/flag
description:
for MPC5121 USB0 only. Indicates
the PWR_FAULT signal polarity is inverted.
required:
- compatible
- reg
- interrupts
- phy_type
allOf:
- $ref: usb-drd.yaml#
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
usb@22000 {
compatible = "fsl-usb2-mph";
reg = <22000 1000>;
interrupts = <27 IRQ_TYPE_EDGE_RISING>;
phy_type = "ulpi";
port0;
port1;
};
- |
#include <dt-bindings/interrupt-controller/irq.h>
usb@23000 {
compatible = "fsl-usb2-dr";
reg = <23000 1000>;
interrupts = <26 IRQ_TYPE_EDGE_RISING>;
dr_mode = "otg";
phy_type = "ulpi";
};

View File

@ -1,81 +0,0 @@
Freescale SOC USB controllers
The device node for a USB controller that is part of a Freescale
SOC is as described in the document "Open Firmware Recommended
Practice : Universal Serial Bus" with the following modifications
and additions :
Required properties :
- compatible : Should be "fsl-usb2-mph" for multi port host USB
controllers, or "fsl-usb2-dr" for dual role USB controllers
or "fsl,mpc5121-usb2-dr" for dual role USB controllers of MPC5121.
Wherever applicable, the IP version of the USB controller should
also be mentioned (for eg. fsl-usb2-dr-v2.2 for bsc9132).
- phy_type : For multi port host USB controllers, should be one of
"ulpi", or "serial". For dual role USB controllers, should be
one of "ulpi", "utmi", "utmi_wide", or "serial".
- reg : Offset and length of the register set for the device
- port0 : boolean; if defined, indicates port0 is connected for
fsl-usb2-mph compatible controllers. Either this property or
"port1" (or both) must be defined for "fsl-usb2-mph" compatible
controllers.
- port1 : boolean; if defined, indicates port1 is connected for
fsl-usb2-mph compatible controllers. Either this property or
"port0" (or both) must be defined for "fsl-usb2-mph" compatible
controllers.
- dr_mode : indicates the working mode for "fsl-usb2-dr" compatible
controllers. Can be "host", "peripheral", or "otg". Default to
"host" if not defined for backward compatibility.
Recommended properties :
- interrupts : <a b> where a is the interrupt number and b is a
field that represents an encoding of the sense and level
information for the interrupt. This should be encoded based on
the information in section 2) depending on the type of interrupt
controller you have.
Optional properties :
- fsl,invert-drvvbus : boolean; for MPC5121 USB0 only. Indicates the
port power polarity of internal PHY signal DRVVBUS is inverted.
- fsl,invert-pwr-fault : boolean; for MPC5121 USB0 only. Indicates
the PWR_FAULT signal polarity is inverted.
Example multi port host USB controller device node :
usb@22000 {
compatible = "fsl-usb2-mph";
reg = <22000 1000>;
#address-cells = <1>;
#size-cells = <0>;
interrupt-parent = <700>;
interrupts = <27 1>;
phy_type = "ulpi";
port0;
port1;
};
Example dual role USB controller device node :
usb@23000 {
compatible = "fsl-usb2-dr";
reg = <23000 1000>;
#address-cells = <1>;
#size-cells = <0>;
interrupt-parent = <700>;
interrupts = <26 1>;
dr_mode = "otg";
phy = "ulpi";
};
Example dual role USB controller device node for MPC5121ADS:
usb@4000 {
compatible = "fsl,mpc5121-usb2-dr";
reg = <0x4000 0x1000>;
#address-cells = <1>;
#size-cells = <0>;
interrupt-parent = < &ipic >;
interrupts = <44 0x8>;
dr_mode = "otg";
phy_type = "utmi_wide";
fsl,invert-drvvbus;
fsl,invert-pwr-fault;
};

View File

@ -9,9 +9,6 @@ title: Genesys Logic USB hub controller
maintainers:
- Icenowy Zheng <uwu@icenowy.me>
allOf:
- $ref: usb-device.yaml#
properties:
compatible:
enum:
@ -27,17 +24,44 @@ properties:
vdd-supply:
description:
the regulator that provides 3.3V core power to the hub.
The regulator that provides 3.3V or 5.0V core power to the hub.
peer-hub:
$ref: /schemas/types.yaml#/definitions/phandle
description:
phandle to the peer hub on the controller.
For onboard hub controllers that support USB 3.x and USB 2.0 hubs
with shared resets and power supplies, this property is used to identify
the hubs with which these are shared.
required:
- compatible
- reg
allOf:
- $ref: usb-device.yaml#
- if:
properties:
compatible:
contains:
enum:
- usb5e3,608
then:
properties:
peer-hub: false
vdd-supply: false
- if:
properties:
compatible:
contains:
enum:
- usb5e3,610
- usb5e3,620
then:
properties:
peer-hub: true
vdd-supply: true
additionalProperties: false
examples:
@ -54,3 +78,29 @@ examples:
reset-gpios = <&pio 7 2 GPIO_ACTIVE_LOW>;
};
};
- |
#include <dt-bindings/gpio/gpio.h>
usb {
dr_mode = "host";
#address-cells = <1>;
#size-cells = <0>;
/* 2.0 hub on port 1 */
hub_2_0: hub@1 {
compatible = "usb5e3,610";
reg = <1>;
peer-hub = <&hub_3_0>;
reset-gpios = <&gpio 20 GPIO_ACTIVE_LOW>;
vdd-supply = <&vcc_5v>;
};
/* 3.1 hub on port 4 */
hub_3_0: hub@2 {
compatible = "usb5e3,620";
reg = <2>;
peer-hub = <&hub_2_0>;
reset-gpios = <&gpio 20 GPIO_ACTIVE_LOW>;
vdd-supply = <&vcc_5v>;
};
};

View File

@ -22,6 +22,7 @@ properties:
- nxp,cbdtu02043
- onnn,fsusb43l10x
- pericom,pi3usb102
- ti,tmuxhs4212
- const: gpio-sbu-mux
enable-gpios:
@ -44,13 +45,18 @@ properties:
required:
- compatible
- enable-gpios
- select-gpios
- orientation-switch
- port
allOf:
- $ref: usb-switch.yaml#
- if:
required:
- mode-switch
then:
required:
- enable-gpios
additionalProperties: false

View File

@ -34,6 +34,13 @@ properties:
clocks:
maxItems: 1
microchip,ext-vbus-drv:
description:
Some ULPI USB PHYs do not support an internal VBUS supply and driving
the CPEN pin requires the configuration of the UPLI_USE__EXTVBUS
bit in ULPI_BUSCONTROL.
$ref: /schemas/types.yaml#/definitions/flag
required:
- compatible
- reg

View File

@ -30,6 +30,8 @@ properties:
- qcom,sa8775p-dwc3
- qcom,sc7180-dwc3
- qcom,sc7280-dwc3
- qcom,sc8180x-dwc3
- qcom,sc8180x-dwc3-mp
- qcom,sc8280xp-dwc3
- qcom,sc8280xp-dwc3-mp
- qcom,sdm660-dwc3
@ -334,6 +336,8 @@ allOf:
contains:
enum:
- qcom,qcm2290-dwc3
- qcom,sc8180x-dwc3
- qcom,sc8180x-dwc3-mp
- qcom,sm6115-dwc3
- qcom,sm6125-dwc3
- qcom,sm8150-dwc3
@ -448,6 +452,7 @@ allOf:
- qcom,sa8775p-dwc3
- qcom,sc7180-dwc3
- qcom,sc7280-dwc3
- qcom,sc8180x-dwc3
- qcom,sc8280xp-dwc3
- qcom,sdm670-dwc3
- qcom,sdm845-dwc3
@ -475,6 +480,30 @@ allOf:
- const: dm_hs_phy_irq
- const: ss_phy_irq
- if:
properties:
compatible:
contains:
enum:
- qcom,sc8180x-dwc3-mp
then:
properties:
interrupts:
minItems: 10
maxItems: 10
interrupt-names:
items:
- const: pwr_event_1
- const: pwr_event_2
- const: hs_phy_1
- const: hs_phy_2
- const: dp_hs_phy_1
- const: dm_hs_phy_1
- const: dp_hs_phy_2
- const: dm_hs_phy_2
- const: ss_phy_1
- const: ss_phy_2
- if:
properties:
compatible:

View File

@ -31,6 +31,30 @@
enable-active-high;
};
/* USB hub supports both USB 2.0 and USB 3.0 root hub */
usb-hub {
dr_mode = "host";
#address-cells = <1>;
#size-cells = <0>;
/* 2.0 hub on port 1 */
hub_2_0: hub@1 {
compatible = "usb5e3,610";
reg = <1>;
peer-hub = <&hub_3_0>;
vdd-supply = <&usb_pwr_en>;
};
/* 3.0 hub on port 4 */
hub_3_0: hub@2 {
compatible = "usb5e3,620";
reg = <2>;
peer-hub = <&hub_2_0>;
reset-gpios = <&gpio GPIOH_4 GPIO_ACTIVE_LOW>;
vdd-supply = <&vcc_5v>;
};
};
sound {
compatible = "amlogic,axg-sound-card";
model = "ODROID-N2";
@ -234,18 +258,6 @@
"PIN_3", /* GPIOX_17 */
"PIN_5", /* GPIOX_18 */
"PIN_36"; /* GPIOX_19 */
/*
* WARNING: The USB Hub on the Odroid-N2 needs a reset signal
* to be turned high in order to be detected by the USB Controller
* This signal should be handled by a USB specific power sequence
* in order to reset the Hub when USB bus is powered down.
*/
usb-hub-hog {
gpio-hog;
gpios = <GPIOH_4 GPIO_ACTIVE_HIGH>;
output-high;
line-name = "usb-hub-reset";
};
};
&i2c3 {

View File

@ -14,11 +14,13 @@
#include <linux/phy/phy.h>
#include <linux/platform_data/phy-da8xx-usb.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#define PHY_INIT_BITS (CFGCHIP2_SESENDEN | CFGCHIP2_VBDTCTEN)
struct da8xx_usb_phy {
struct device *dev;
struct phy_provider *phy_provider;
struct phy *usb11_phy;
struct phy *usb20_phy;
@ -39,6 +41,12 @@ static int da8xx_usb11_phy_power_on(struct phy *phy)
regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_USB1SUSPENDM,
CFGCHIP2_USB1SUSPENDM);
/*
* USB1.1 can used USB2.0 output clock as reference clock so this is here to prevent USB2.0
* from shutting PHY's power when USB1.1 might use it
*/
pm_runtime_get_sync(d_phy->dev);
return 0;
}
@ -49,6 +57,7 @@ static int da8xx_usb11_phy_power_off(struct phy *phy)
regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_USB1SUSPENDM, 0);
clk_disable_unprepare(d_phy->usb11_clk);
pm_runtime_put_sync(d_phy->dev);
return 0;
}
@ -118,6 +127,35 @@ static const struct phy_ops da8xx_usb20_phy_ops = {
.owner = THIS_MODULE,
};
static int __maybe_unused da8xx_runtime_suspend(struct device *dev)
{
struct da8xx_usb_phy *d_phy = dev_get_drvdata(dev);
dev_dbg(dev, "Suspending ...\n");
regmap_set_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_PHYPWRDN | CFGCHIP2_OTGPWRDN);
return 0;
}
static int __maybe_unused da8xx_runtime_resume(struct device *dev)
{
u32 mask = CFGCHIP2_RESET | CFGCHIP2_PHYPWRDN | CFGCHIP2_OTGPWRDN | CFGCHIP2_PHY_PLLON;
struct da8xx_usb_phy *d_phy = dev_get_drvdata(dev);
u32 pll_status;
regmap_update_bits(d_phy->regmap, CFGCHIP(2), mask, CFGCHIP2_PHY_PLLON);
dev_dbg(dev, "Resuming ...\n");
return regmap_read_poll_timeout(d_phy->regmap, CFGCHIP(2), pll_status,
pll_status & CFGCHIP2_PHYCLKGD, 1000, 500000);
}
static const struct dev_pm_ops da8xx_usb_phy_pm_ops = {
SET_RUNTIME_PM_OPS(da8xx_runtime_suspend, da8xx_runtime_resume, NULL)
};
static struct phy *da8xx_usb_phy_of_xlate(struct device *dev,
const struct of_phandle_args *args)
{
@ -147,6 +185,8 @@ static int da8xx_usb_phy_probe(struct platform_device *pdev)
if (!d_phy)
return -ENOMEM;
d_phy->dev = dev;
if (pdata)
d_phy->regmap = pdata->cfgchip;
else
@ -208,6 +248,14 @@ static int da8xx_usb_phy_probe(struct platform_device *pdev)
regmap_write_bits(d_phy->regmap, CFGCHIP(2),
PHY_INIT_BITS, PHY_INIT_BITS);
pm_runtime_set_active(dev);
devm_pm_runtime_enable(dev);
/*
* Prevent runtime pm from being ON by default. Users can enable
* it using power/control in sysfs.
*/
pm_runtime_forbid(dev);
return 0;
}
@ -232,6 +280,7 @@ static struct platform_driver da8xx_usb_phy_driver = {
.remove_new = da8xx_usb_phy_remove,
.driver = {
.name = "da8xx-usb-phy",
.pm = &da8xx_usb_phy_pm_ops,
.of_match_table = da8xx_usb_phy_ids,
},
};

View File

@ -22,20 +22,25 @@ config USB4_DEBUGFS_WRITE
bool "Enable write by debugfs to configuration spaces (DANGEROUS)"
help
Enables writing to device configuration registers through
debugfs interface.
debugfs interface. You can use tools such as Thunderbolt/USB4
debugging tools to access these registers. For more
information see:
https://github.com/intel/tbtools
Only enable this if you know what you are doing! Never enable
this for production systems or distro kernels.
config USB4_DEBUGFS_MARGINING
bool "Expose receiver lane margining operations under USB4 ports (DANGEROUS)"
bool "Expose receiver lane margining operations under USB4 ports and retimers (DANGEROUS)"
depends on DEBUG_FS
depends on USB4_DEBUGFS_WRITE
help
Enables hardware and software based receiver lane margining support
under each USB4 port. Used for electrical quality and robustness
validation during manufacturing. Should not be enabled by distro
kernels.
Enables hardware and software based receiver lane margining
support under each USB4 port and retimer, including retimers
on the other side of the cable. Used for electrical quality
and robustness validation during manufacturing. Should not be
enabled by distro kernels.
config USB4_KUNIT_TEST
bool "KUnit tests" if !KUNIT_ALL_TESTS

File diff suppressed because it is too large Load Diff

View File

@ -14,7 +14,11 @@
#include "sb_regs.h"
#include "tb.h"
#if IS_ENABLED(CONFIG_USB4_DEBUGFS_MARGINING)
#define TB_MAX_RETIMER_INDEX 6
#else
#define TB_MAX_RETIMER_INDEX 2
#endif
/**
* tb_retimer_nvm_read() - Read contents of retimer NVM
@ -319,6 +323,8 @@ static ssize_t nvm_version_show(struct device *dev,
if (!rt->nvm)
ret = -EAGAIN;
else if (rt->no_nvm_upgrade)
ret = -EOPNOTSUPP;
else
ret = sysfs_emit(buf, "%x.%x\n", rt->nvm->major, rt->nvm->minor);
@ -366,35 +372,29 @@ const struct device_type tb_retimer_type = {
.release = tb_retimer_release,
};
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,
bool on_board)
{
struct tb_retimer *rt;
u32 vendor, device;
int ret;
ret = usb4_port_retimer_read(port, index, USB4_SB_VENDOR_ID, &vendor,
sizeof(vendor));
ret = usb4_port_sb_read(port, USB4_SB_TARGET_RETIMER, index,
USB4_SB_VENDOR_ID, &vendor, sizeof(vendor));
if (ret) {
if (ret != -ENODEV)
tb_port_warn(port, "failed read retimer VendorId: %d\n", ret);
return ret;
}
ret = usb4_port_retimer_read(port, index, USB4_SB_PRODUCT_ID, &device,
sizeof(device));
ret = usb4_port_sb_read(port, USB4_SB_TARGET_RETIMER, index,
USB4_SB_PRODUCT_ID, &device, sizeof(device));
if (ret) {
if (ret != -ENODEV)
tb_port_warn(port, "failed read retimer ProductId: %d\n", ret);
return ret;
}
/*
* Check that it supports NVM operations. If not then don't add
* the device at all.
*/
ret = usb4_port_retimer_nvm_sector_size(port, index);
if (ret < 0)
return ret;
rt = kzalloc(sizeof(*rt), GFP_KERNEL);
if (!rt)
@ -407,6 +407,13 @@ static int tb_retimer_add(struct tb_port *port, u8 index, u32 auth_status)
rt->port = port;
rt->tb = port->sw->tb;
/*
* Only support NVM upgrade for on-board retimers. The retimers
* on the other side of the connection.
*/
if (!on_board || usb4_port_retimer_nvm_sector_size(port, index) <= 0)
rt->no_nvm_upgrade = true;
rt->dev.parent = &port->usb4->dev;
rt->dev.bus = &tb_bus_type;
rt->dev.type = &tb_retimer_type;
@ -437,12 +444,14 @@ static int tb_retimer_add(struct tb_port *port, u8 index, u32 auth_status)
pm_runtime_mark_last_busy(&rt->dev);
pm_runtime_use_autosuspend(&rt->dev);
tb_retimer_debugfs_init(rt);
return 0;
}
static void tb_retimer_remove(struct tb_retimer *rt)
{
dev_info(&rt->dev, "retimer disconnected\n");
tb_retimer_debugfs_remove(rt);
tb_nvm_free(rt->nvm);
device_unregister(&rt->dev);
}
@ -485,7 +494,7 @@ static struct tb_retimer *tb_port_find_retimer(struct tb_port *port, u8 index)
int tb_retimer_scan(struct tb_port *port, bool add)
{
u32 status[TB_MAX_RETIMER_INDEX + 1] = {};
int ret, i, last_idx = 0;
int ret, i, max, last_idx = 0;
/*
* Send broadcast RT to make sure retimer indices facing this
@ -520,26 +529,28 @@ int tb_retimer_scan(struct tb_port *port, bool add)
break;
}
tb_retimer_unset_inbound_sbtx(port);
if (!last_idx)
return 0;
/* Add on-board retimers if they do not exist already */
max = i;
ret = 0;
for (i = 1; i <= last_idx; i++) {
/* Add retimers if they do not exist already */
for (i = 1; i <= max; i++) {
struct tb_retimer *rt;
/* Skip cable retimers */
if (usb4_port_retimer_is_cable(port, i))
continue;
rt = tb_port_find_retimer(port, i);
if (rt) {
put_device(&rt->dev);
} else if (add) {
ret = tb_retimer_add(port, i, status[i]);
ret = tb_retimer_add(port, i, status[i], i <= last_idx);
if (ret && ret != -EOPNOTSUPP)
break;
}
}
tb_retimer_unset_inbound_sbtx(port);
return ret;
}

View File

@ -12,6 +12,10 @@
#define USB4_SB_VENDOR_ID 0x00
#define USB4_SB_PRODUCT_ID 0x01
#define USB4_SB_FW_VERSION 0x02
#define USB4_SB_DEBUG_CONF 0x05
#define USB4_SB_DEBUG 0x06
#define USB4_SB_LRD_TUNING 0x07
#define USB4_SB_OPCODE 0x08
enum usb4_sb_opcode {
@ -22,6 +26,7 @@ enum usb4_sb_opcode {
USB4_SB_OPCODE_SET_INBOUND_SBTX = 0x5055534c, /* "LSUP" */
USB4_SB_OPCODE_UNSET_INBOUND_SBTX = 0x50555355, /* "USUP" */
USB4_SB_OPCODE_QUERY_LAST_RETIMER = 0x5453414c, /* "LAST" */
USB4_SB_OPCODE_QUERY_CABLE_RETIMER = 0x524c4243, /* "CBLR" */
USB4_SB_OPCODE_GET_NVM_SECTOR_SIZE = 0x53534e47, /* "GNSS" */
USB4_SB_OPCODE_NVM_SET_OFFSET = 0x53504f42, /* "BOPS" */
USB4_SB_OPCODE_NVM_BLOCK_WRITE = 0x574b4c42, /* "BLKW" */
@ -35,6 +40,10 @@ enum usb4_sb_opcode {
#define USB4_SB_METADATA 0x09
#define USB4_SB_METADATA_NVM_AUTH_WRITE_MASK GENMASK(5, 0)
#define USB4_SB_LINK_CONF 0x0c
#define USB4_SB_GEN23_TXFFE 0x0d
#define USB4_SB_GEN4_TXFFE 0x0e
#define USB4_SB_VERSION 0x0f
#define USB4_SB_DATA 0x12
/* USB4_SB_OPCODE_READ_LANE_MARGINING_CAP */
@ -42,30 +51,21 @@ enum usb4_sb_opcode {
#define USB4_MARGIN_CAP_0_MODES_SW BIT(1)
#define USB4_MARGIN_CAP_0_2_LANES BIT(2)
#define USB4_MARGIN_CAP_0_VOLTAGE_INDP_MASK GENMASK(4, 3)
#define USB4_MARGIN_CAP_0_VOLTAGE_INDP_SHIFT 3
#define USB4_MARGIN_CAP_0_VOLTAGE_MIN 0x0
#define USB4_MARGIN_CAP_0_VOLTAGE_HL 0x1
#define USB4_MARGIN_CAP_0_VOLTAGE_BOTH 0x2
#define USB4_MARGIN_CAP_0_TIME BIT(5)
#define USB4_MARGIN_CAP_0_VOLTAGE_STEPS_MASK GENMASK(12, 6)
#define USB4_MARGIN_CAP_0_VOLTAGE_STEPS_SHIFT 6
#define USB4_MARGIN_CAP_0_MAX_VOLTAGE_OFFSET_MASK GENMASK(18, 13)
#define USB4_MARGIN_CAP_0_MAX_VOLTAGE_OFFSET_SHIFT 13
#define USB4_MARGIN_CAP_1_TIME_DESTR BIT(8)
#define USB4_MARGIN_CAP_1_TIME_INDP_MASK GENMASK(10, 9)
#define USB4_MARGIN_CAP_1_TIME_INDP_SHIFT 9
#define USB4_MARGIN_CAP_1_TIME_MIN 0x0
#define USB4_MARGIN_CAP_1_TIME_LR 0x1
#define USB4_MARGIN_CAP_1_TIME_BOTH 0x2
#define USB4_MARGIN_CAP_1_TIME_STEPS_MASK GENMASK(15, 11)
#define USB4_MARGIN_CAP_1_TIME_STEPS_SHIFT 11
#define USB4_MARGIN_CAP_1_TIME_OFFSET_MASK GENMASK(20, 16)
#define USB4_MARGIN_CAP_1_TIME_OFFSET_SHIFT 16
#define USB4_MARGIN_CAP_1_MIN_BER_MASK GENMASK(25, 21)
#define USB4_MARGIN_CAP_1_MIN_BER_SHIFT 21
#define USB4_MARGIN_CAP_1_MAX_BER_MASK GENMASK(30, 26)
#define USB4_MARGIN_CAP_1_MAX_BER_SHIFT 26
#define USB4_MARGIN_CAP_1_MAX_BER_SHIFT 26
/* USB4_SB_OPCODE_RUN_HW_LANE_MARGINING */
#define USB4_MARGIN_HW_TIME BIT(3)

View File

@ -329,6 +329,7 @@ struct usb4_port {
* @nvm: Pointer to the NVM if the retimer has one (%NULL otherwise)
* @no_nvm_upgrade: Prevent NVM upgrade of this retimer
* @auth_status: Status of last NVM authentication
* @margining: Pointer to margining structure if enabled
*/
struct tb_retimer {
struct device dev;
@ -340,6 +341,9 @@ struct tb_retimer {
struct tb_nvm *nvm;
bool no_nvm_upgrade;
u32 auth_status;
#ifdef CONFIG_USB4_DEBUGFS_MARGINING
struct tb_margining *margining;
#endif
};
/**
@ -1327,26 +1331,43 @@ int usb4_port_router_offline(struct tb_port *port);
int usb4_port_router_online(struct tb_port *port);
int usb4_port_enumerate_retimers(struct tb_port *port);
bool usb4_port_clx_supported(struct tb_port *port);
int usb4_port_margining_caps(struct tb_port *port, u32 *caps);
bool usb4_port_asym_supported(struct tb_port *port);
int usb4_port_asym_set_link_width(struct tb_port *port, enum tb_link_width width);
int usb4_port_asym_start(struct tb_port *port);
int usb4_port_hw_margin(struct tb_port *port, unsigned int lanes,
unsigned int ber_level, bool timing, bool right_high,
u32 *results);
int usb4_port_sw_margin(struct tb_port *port, unsigned int lanes, bool timing,
/**
* enum tb_sb_target - Sideband transaction target
* @USB4_SB_TARGET_ROUTER: Target is the router itself
* @USB4_SB_TARGET_PARTNER: Target is partner
* @USB4_SB_TARGET_RETIMER: Target is retimer
*/
enum usb4_sb_target {
USB4_SB_TARGET_ROUTER,
USB4_SB_TARGET_PARTNER,
USB4_SB_TARGET_RETIMER,
};
int usb4_port_sb_read(struct tb_port *port, enum usb4_sb_target target, u8 index,
u8 reg, void *buf, u8 size);
int usb4_port_sb_write(struct tb_port *port, enum usb4_sb_target target,
u8 index, u8 reg, const void *buf, u8 size);
int usb4_port_margining_caps(struct tb_port *port, enum usb4_sb_target target,
u8 index, u32 *caps);
int usb4_port_hw_margin(struct tb_port *port, enum usb4_sb_target target,
u8 index, unsigned int lanes, unsigned int ber_level,
bool timing, bool right_high, u32 *results);
int usb4_port_sw_margin(struct tb_port *port, enum usb4_sb_target target,
u8 index, unsigned int lanes, bool timing,
bool right_high, u32 counter);
int usb4_port_sw_margin_errors(struct tb_port *port, u32 *errors);
int usb4_port_sw_margin_errors(struct tb_port *port, enum usb4_sb_target target,
u8 index, u32 *errors);
int usb4_port_retimer_set_inbound_sbtx(struct tb_port *port, u8 index);
int usb4_port_retimer_unset_inbound_sbtx(struct tb_port *port, u8 index);
int usb4_port_retimer_read(struct tb_port *port, u8 index, u8 reg, void *buf,
u8 size);
int usb4_port_retimer_write(struct tb_port *port, u8 index, u8 reg,
const void *buf, u8 size);
int usb4_port_retimer_is_last(struct tb_port *port, u8 index);
int usb4_port_retimer_is_cable(struct tb_port *port, u8 index);
int usb4_port_retimer_nvm_sector_size(struct tb_port *port, u8 index);
int usb4_port_retimer_nvm_set_offset(struct tb_port *port, u8 index,
unsigned int address);
@ -1445,6 +1466,8 @@ void tb_xdomain_debugfs_init(struct tb_xdomain *xd);
void tb_xdomain_debugfs_remove(struct tb_xdomain *xd);
void tb_service_debugfs_init(struct tb_service *svc);
void tb_service_debugfs_remove(struct tb_service *svc);
void tb_retimer_debugfs_init(struct tb_retimer *rt);
void tb_retimer_debugfs_remove(struct tb_retimer *rt);
#else
static inline void tb_debugfs_init(void) { }
static inline void tb_debugfs_exit(void) { }
@ -1454,6 +1477,8 @@ static inline void tb_xdomain_debugfs_init(struct tb_xdomain *xd) { }
static inline void tb_xdomain_debugfs_remove(struct tb_xdomain *xd) { }
static inline void tb_service_debugfs_init(struct tb_service *svc) { }
static inline void tb_service_debugfs_remove(struct tb_service *svc) { }
static inline void tb_retimer_debugfs_init(struct tb_retimer *rt) { }
static inline void tb_retimer_debugfs_remove(struct tb_retimer *rt) { }
#endif
#endif

View File

@ -17,12 +17,6 @@
#define USB4_DATA_RETRIES 3
#define USB4_DATA_DWORDS 16
enum usb4_sb_target {
USB4_SB_TARGET_ROUTER,
USB4_SB_TARGET_PARTNER,
USB4_SB_TARGET_RETIMER,
};
#define USB4_NVM_READ_OFFSET_MASK GENMASK(23, 2)
#define USB4_NVM_READ_OFFSET_SHIFT 2
#define USB4_NVM_READ_LENGTH_MASK GENMASK(27, 24)
@ -1289,8 +1283,20 @@ static int usb4_port_write_data(struct tb_port *port, const void *data,
dwords);
}
static int usb4_port_sb_read(struct tb_port *port, enum usb4_sb_target target,
u8 index, u8 reg, void *buf, u8 size)
/**
* usb4_port_sb_read() - Read from sideband register
* @port: USB4 port to read
* @target: Sideband target
* @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER
* @reg: Sideband register index
* @buf: Buffer where the sideband data is copied
* @size: Size of @buf
*
* Reads data from sideband register @reg and copies it into @buf.
* Returns %0 in case of success and negative errno in case of failure.
*/
int usb4_port_sb_read(struct tb_port *port, enum usb4_sb_target target, u8 index,
u8 reg, void *buf, u8 size)
{
size_t dwords = DIV_ROUND_UP(size, 4);
int ret;
@ -1329,8 +1335,20 @@ static int usb4_port_sb_read(struct tb_port *port, enum usb4_sb_target target,
return buf ? usb4_port_read_data(port, buf, dwords) : 0;
}
static int usb4_port_sb_write(struct tb_port *port, enum usb4_sb_target target,
u8 index, u8 reg, const void *buf, u8 size)
/**
* usb4_port_sb_write() - Write to sideband register
* @port: USB4 port to write
* @target: Sideband target
* @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER
* @reg: Sideband register index
* @buf: Data to write
* @size: Size of @buf
*
* Writes @buf to sideband register @reg. Returns %0 in case of success
* and negative errno in case of failure.
*/
int usb4_port_sb_write(struct tb_port *port, enum usb4_sb_target target,
u8 index, u8 reg, const void *buf, u8 size)
{
size_t dwords = DIV_ROUND_UP(size, 4);
int ret;
@ -1610,26 +1628,31 @@ int usb4_port_asym_start(struct tb_port *port)
/**
* usb4_port_margining_caps() - Read USB4 port marginig capabilities
* @port: USB4 port
* @target: Sideband target
* @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER
* @caps: Array with at least two elements to hold the results
*
* Reads the USB4 port lane margining capabilities into @caps.
*/
int usb4_port_margining_caps(struct tb_port *port, u32 *caps)
int usb4_port_margining_caps(struct tb_port *port, enum usb4_sb_target target,
u8 index, u32 *caps)
{
int ret;
ret = usb4_port_sb_op(port, USB4_SB_TARGET_ROUTER, 0,
ret = usb4_port_sb_op(port, target, index,
USB4_SB_OPCODE_READ_LANE_MARGINING_CAP, 500);
if (ret)
return ret;
return usb4_port_sb_read(port, USB4_SB_TARGET_ROUTER, 0,
USB4_SB_DATA, caps, sizeof(*caps) * 2);
return usb4_port_sb_read(port, target, index, USB4_SB_DATA, caps,
sizeof(*caps) * 2);
}
/**
* usb4_port_hw_margin() - Run hardware lane margining on port
* @port: USB4 port
* @target: Sideband target
* @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER
* @lanes: Which lanes to run (must match the port capabilities). Can be
* %0, %1 or %7.
* @ber_level: BER level contour value
@ -1640,9 +1663,9 @@ int usb4_port_margining_caps(struct tb_port *port, u32 *caps)
* Runs hardware lane margining on USB4 port and returns the result in
* @results.
*/
int usb4_port_hw_margin(struct tb_port *port, unsigned int lanes,
unsigned int ber_level, bool timing, bool right_high,
u32 *results)
int usb4_port_hw_margin(struct tb_port *port, enum usb4_sb_target target,
u8 index, unsigned int lanes, unsigned int ber_level,
bool timing, bool right_high, u32 *results)
{
u32 val;
int ret;
@ -1656,23 +1679,25 @@ int usb4_port_hw_margin(struct tb_port *port, unsigned int lanes,
val |= (ber_level << USB4_MARGIN_HW_BER_SHIFT) &
USB4_MARGIN_HW_BER_MASK;
ret = usb4_port_sb_write(port, USB4_SB_TARGET_ROUTER, 0,
USB4_SB_METADATA, &val, sizeof(val));
ret = usb4_port_sb_write(port, target, index, USB4_SB_METADATA, &val,
sizeof(val));
if (ret)
return ret;
ret = usb4_port_sb_op(port, USB4_SB_TARGET_ROUTER, 0,
ret = usb4_port_sb_op(port, target, index,
USB4_SB_OPCODE_RUN_HW_LANE_MARGINING, 2500);
if (ret)
return ret;
return usb4_port_sb_read(port, USB4_SB_TARGET_ROUTER, 0,
USB4_SB_DATA, results, sizeof(*results) * 2);
return usb4_port_sb_read(port, target, index, USB4_SB_DATA, results,
sizeof(*results) * 2);
}
/**
* usb4_port_sw_margin() - Run software lane margining on port
* @port: USB4 port
* @target: Sideband target
* @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER
* @lanes: Which lanes to run (must match the port capabilities). Can be
* %0, %1 or %7.
* @timing: Perform timing margining instead of voltage
@ -1683,7 +1708,8 @@ int usb4_port_hw_margin(struct tb_port *port, unsigned int lanes,
* counters by calling usb4_port_sw_margin_errors(). Returns %0 in
* success and negative errno otherwise.
*/
int usb4_port_sw_margin(struct tb_port *port, unsigned int lanes, bool timing,
int usb4_port_sw_margin(struct tb_port *port, enum usb4_sb_target target,
u8 index, unsigned int lanes, bool timing,
bool right_high, u32 counter)
{
u32 val;
@ -1697,34 +1723,37 @@ int usb4_port_sw_margin(struct tb_port *port, unsigned int lanes, bool timing,
val |= (counter << USB4_MARGIN_SW_COUNTER_SHIFT) &
USB4_MARGIN_SW_COUNTER_MASK;
ret = usb4_port_sb_write(port, USB4_SB_TARGET_ROUTER, 0,
USB4_SB_METADATA, &val, sizeof(val));
ret = usb4_port_sb_write(port, target, index, USB4_SB_METADATA, &val,
sizeof(val));
if (ret)
return ret;
return usb4_port_sb_op(port, USB4_SB_TARGET_ROUTER, 0,
return usb4_port_sb_op(port, target, index,
USB4_SB_OPCODE_RUN_SW_LANE_MARGINING, 2500);
}
/**
* usb4_port_sw_margin_errors() - Read the software margining error counters
* @port: USB4 port
* @target: Sideband target
* @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER
* @errors: Error metadata is copied here.
*
* This reads back the software margining error counters from the port.
* Returns %0 in success and negative errno otherwise.
*/
int usb4_port_sw_margin_errors(struct tb_port *port, u32 *errors)
int usb4_port_sw_margin_errors(struct tb_port *port, enum usb4_sb_target target,
u8 index, u32 *errors)
{
int ret;
ret = usb4_port_sb_op(port, USB4_SB_TARGET_ROUTER, 0,
ret = usb4_port_sb_op(port, target, index,
USB4_SB_OPCODE_READ_SW_MARGIN_ERR, 150);
if (ret)
return ret;
return usb4_port_sb_read(port, USB4_SB_TARGET_ROUTER, 0,
USB4_SB_METADATA, errors, sizeof(*errors));
return usb4_port_sb_read(port, target, index, USB4_SB_METADATA, errors,
sizeof(*errors));
}
static inline int usb4_port_retimer_op(struct tb_port *port, u8 index,
@ -1776,47 +1805,6 @@ int usb4_port_retimer_unset_inbound_sbtx(struct tb_port *port, u8 index)
USB4_SB_OPCODE_UNSET_INBOUND_SBTX, 500);
}
/**
* usb4_port_retimer_read() - Read from retimer sideband registers
* @port: USB4 port
* @index: Retimer index
* @reg: Sideband register to read
* @buf: Data from @reg is stored here
* @size: Number of bytes to read
*
* Function reads retimer sideband registers starting from @reg. The
* retimer is connected to @port at @index. Returns %0 in case of
* success, and read data is copied to @buf. If there is no retimer
* present at given @index returns %-ENODEV. In any other failure
* returns negative errno.
*/
int usb4_port_retimer_read(struct tb_port *port, u8 index, u8 reg, void *buf,
u8 size)
{
return usb4_port_sb_read(port, USB4_SB_TARGET_RETIMER, index, reg, buf,
size);
}
/**
* usb4_port_retimer_write() - Write to retimer sideband registers
* @port: USB4 port
* @index: Retimer index
* @reg: Sideband register to write
* @buf: Data that is written starting from @reg
* @size: Number of bytes to write
*
* Writes retimer sideband registers starting from @reg. The retimer is
* connected to @port at @index. Returns %0 in case of success. If there
* is no retimer present at given @index returns %-ENODEV. In any other
* failure returns negative errno.
*/
int usb4_port_retimer_write(struct tb_port *port, u8 index, u8 reg,
const void *buf, u8 size)
{
return usb4_port_sb_write(port, USB4_SB_TARGET_RETIMER, index, reg, buf,
size);
}
/**
* usb4_port_retimer_is_last() - Is the retimer last on-board retimer
* @port: USB4 port
@ -1837,8 +1825,32 @@ int usb4_port_retimer_is_last(struct tb_port *port, u8 index)
if (ret)
return ret;
ret = usb4_port_retimer_read(port, index, USB4_SB_METADATA, &metadata,
sizeof(metadata));
ret = usb4_port_sb_read(port, USB4_SB_TARGET_RETIMER, index,
USB4_SB_METADATA, &metadata, sizeof(metadata));
return ret ? ret : metadata & 1;
}
/**
* usb4_port_retimer_is_cable() - Is the retimer cable retimer
* @port: USB4 port
* @index: Retimer index
*
* If the retimer at @index is last cable retimer this function returns
* %1 and %0 if it is on-board retimer. In case a retimer is not present
* at @index returns %-ENODEV. Otherwise returns negative errno.
*/
int usb4_port_retimer_is_cable(struct tb_port *port, u8 index)
{
u32 metadata;
int ret;
ret = usb4_port_retimer_op(port, index, USB4_SB_OPCODE_QUERY_CABLE_RETIMER,
500);
if (ret)
return ret;
ret = usb4_port_sb_read(port, USB4_SB_TARGET_RETIMER, index,
USB4_SB_METADATA, &metadata, sizeof(metadata));
return ret ? ret : metadata & 1;
}
@ -1863,8 +1875,8 @@ int usb4_port_retimer_nvm_sector_size(struct tb_port *port, u8 index)
if (ret)
return ret;
ret = usb4_port_retimer_read(port, index, USB4_SB_METADATA, &metadata,
sizeof(metadata));
ret = usb4_port_sb_read(port, USB4_SB_TARGET_RETIMER, index,
USB4_SB_METADATA, &metadata, sizeof(metadata));
return ret ? ret : metadata & USB4_NVM_SECTOR_SIZE_MASK;
}
@ -1889,8 +1901,8 @@ int usb4_port_retimer_nvm_set_offset(struct tb_port *port, u8 index,
metadata = (dwaddress << USB4_NVM_SET_OFFSET_SHIFT) &
USB4_NVM_SET_OFFSET_MASK;
ret = usb4_port_retimer_write(port, index, USB4_SB_METADATA, &metadata,
sizeof(metadata));
ret = usb4_port_sb_write(port, USB4_SB_TARGET_RETIMER, index,
USB4_SB_METADATA, &metadata, sizeof(metadata));
if (ret)
return ret;
@ -1912,8 +1924,8 @@ static int usb4_port_retimer_nvm_write_next_block(void *data,
u8 index = info->index;
int ret;
ret = usb4_port_retimer_write(port, index, USB4_SB_DATA,
buf, dwords * 4);
ret = usb4_port_sb_write(port, USB4_SB_TARGET_RETIMER, index,
USB4_SB_DATA, buf, dwords * 4);
if (ret)
return ret;
@ -1992,8 +2004,8 @@ int usb4_port_retimer_nvm_authenticate_status(struct tb_port *port, u8 index,
u32 metadata, val;
int ret;
ret = usb4_port_retimer_read(port, index, USB4_SB_OPCODE, &val,
sizeof(val));
ret = usb4_port_sb_read(port, USB4_SB_TARGET_RETIMER, index,
USB4_SB_OPCODE, &val, sizeof(val));
if (ret)
return ret;
@ -2004,8 +2016,9 @@ int usb4_port_retimer_nvm_authenticate_status(struct tb_port *port, u8 index,
return 0;
case -EAGAIN:
ret = usb4_port_retimer_read(port, index, USB4_SB_METADATA,
&metadata, sizeof(metadata));
ret = usb4_port_sb_read(port, USB4_SB_TARGET_RETIMER, index,
USB4_SB_METADATA, &metadata,
sizeof(metadata));
if (ret)
return ret;
@ -2030,8 +2043,8 @@ static int usb4_port_retimer_nvm_read_block(void *data, unsigned int dwaddress,
if (dwords < USB4_DATA_DWORDS)
metadata |= dwords << USB4_NVM_READ_LENGTH_SHIFT;
ret = usb4_port_retimer_write(port, index, USB4_SB_METADATA, &metadata,
sizeof(metadata));
ret = usb4_port_sb_write(port, USB4_SB_TARGET_RETIMER, index,
USB4_SB_METADATA, &metadata, sizeof(metadata));
if (ret)
return ret;
@ -2039,8 +2052,8 @@ static int usb4_port_retimer_nvm_read_block(void *data, unsigned int dwaddress,
if (ret)
return ret;
return usb4_port_retimer_read(port, index, USB4_SB_DATA, buf,
dwords * 4);
return usb4_port_sb_read(port, USB4_SB_TARGET_RETIMER, index,
USB4_SB_DATA, buf, dwords * 4);
}
/**

View File

@ -16,6 +16,7 @@
#include <linux/of_platform.h>
#include <linux/pm_runtime.h>
#include <linux/property.h>
#include "core.h"
/* USB Wrapper register offsets */
#define USBSS_PID 0x0
@ -85,6 +86,18 @@ static inline void cdns_ti_writel(struct cdns_ti *data, u32 offset, u32 value)
writel(value, data->usbss + offset);
}
static struct cdns3_platform_data cdns_ti_pdata = {
.quirks = CDNS3_DRD_SUSPEND_RESIDENCY_ENABLE, /* Errata i2409 */
};
static const struct of_dev_auxdata cdns_ti_auxdata[] = {
{
.compatible = "cdns,usb3",
.platform_data = &cdns_ti_pdata,
},
{},
};
static int cdns_ti_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@ -176,7 +189,7 @@ static int cdns_ti_probe(struct platform_device *pdev)
reg |= USBSS_W1_PWRUP_RST;
cdns_ti_writel(data, USBSS_W1, reg);
error = of_platform_populate(node, NULL, NULL, dev);
error = of_platform_populate(node, NULL, cdns_ti_auxdata, dev);
if (error) {
dev_err(dev, "failed to create children: %d\n", error);
goto err;

View File

@ -44,6 +44,7 @@ struct cdns3_platform_data {
bool suspend, bool wakeup);
unsigned long quirks;
#define CDNS3_DEFAULT_PM_RUNTIME_ALLOW BIT(0)
#define CDNS3_DRD_SUSPEND_RESIDENCY_ENABLE BIT(1)
};
/**

View File

@ -389,7 +389,7 @@ static irqreturn_t cdns_drd_irq(int irq, void *data)
int cdns_drd_init(struct cdns *cdns)
{
void __iomem *regs;
u32 state;
u32 state, reg;
int ret;
regs = devm_ioremap_resource(cdns->dev, &cdns->otg_res);
@ -433,6 +433,14 @@ int cdns_drd_init(struct cdns *cdns)
cdns->otg_irq_regs = (struct cdns_otg_irq_regs __iomem *)
&cdns->otg_v1_regs->ien;
writel(1, &cdns->otg_v1_regs->simulate);
if (cdns->pdata &&
(cdns->pdata->quirks & CDNS3_DRD_SUSPEND_RESIDENCY_ENABLE)) {
reg = readl(&cdns->otg_v1_regs->susp_ctrl);
reg |= SUSP_CTRL_SUSPEND_RESIDENCY_ENABLE;
writel(reg, &cdns->otg_v1_regs->susp_ctrl);
}
cdns->version = CDNS3_CONTROLLER_V1;
} else {
dev_err(cdns->dev, "not supported DID=0x%08x\n", state);

View File

@ -193,6 +193,9 @@ struct cdns_otg_irq_regs {
/* OTGREFCLK - bitmasks */
#define OTGREFCLK_STB_CLK_SWITCH_EN BIT(31)
/* SUPS_CTRL - bitmasks */
#define SUSP_CTRL_SUSPEND_RESIDENCY_ENABLE BIT(17)
/* OVERRIDE - bitmasks */
#define OVERRIDE_IDPULLUP BIT(0)
/* Only for CDNS3_CONTROLLER_V0 version */

View File

@ -557,7 +557,7 @@ static void ci_hdrc_imx_shutdown(struct platform_device *pdev)
ci_hdrc_imx_remove(pdev);
}
static int __maybe_unused imx_controller_suspend(struct device *dev,
static int imx_controller_suspend(struct device *dev,
pm_message_t msg)
{
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
@ -582,7 +582,7 @@ static int __maybe_unused imx_controller_suspend(struct device *dev,
return 0;
}
static int __maybe_unused imx_controller_resume(struct device *dev,
static int imx_controller_resume(struct device *dev,
pm_message_t msg)
{
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
@ -618,7 +618,7 @@ clk_disable:
return ret;
}
static int __maybe_unused ci_hdrc_imx_suspend(struct device *dev)
static int ci_hdrc_imx_suspend(struct device *dev)
{
int ret;
@ -636,7 +636,7 @@ static int __maybe_unused ci_hdrc_imx_suspend(struct device *dev)
return ret;
}
static int __maybe_unused ci_hdrc_imx_resume(struct device *dev)
static int ci_hdrc_imx_resume(struct device *dev)
{
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
int ret;
@ -652,7 +652,7 @@ static int __maybe_unused ci_hdrc_imx_resume(struct device *dev)
return ret;
}
static int __maybe_unused ci_hdrc_imx_runtime_suspend(struct device *dev)
static int ci_hdrc_imx_runtime_suspend(struct device *dev)
{
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
@ -664,15 +664,14 @@ static int __maybe_unused ci_hdrc_imx_runtime_suspend(struct device *dev)
return imx_controller_suspend(dev, PMSG_AUTO_SUSPEND);
}
static int __maybe_unused ci_hdrc_imx_runtime_resume(struct device *dev)
static int ci_hdrc_imx_runtime_resume(struct device *dev)
{
return imx_controller_resume(dev, PMSG_AUTO_RESUME);
}
static const struct dev_pm_ops ci_hdrc_imx_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume)
SET_RUNTIME_PM_OPS(ci_hdrc_imx_runtime_suspend,
ci_hdrc_imx_runtime_resume, NULL)
SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume)
RUNTIME_PM_OPS(ci_hdrc_imx_runtime_suspend, ci_hdrc_imx_runtime_resume, NULL)
};
static struct platform_driver ci_hdrc_imx_driver = {
.probe = ci_hdrc_imx_probe,
@ -681,7 +680,7 @@ static struct platform_driver ci_hdrc_imx_driver = {
.driver = {
.name = "imx_usb",
.of_match_table = ci_hdrc_imx_dt_ids,
.pm = &ci_hdrc_imx_pm_ops,
.pm = pm_ptr(&ci_hdrc_imx_pm_ops),
},
};

View File

@ -303,4 +303,5 @@ module_platform_driver(ci_hdrc_msm_driver);
MODULE_ALIAS("platform:msm_hsusb");
MODULE_ALIAS("platform:ci13xxx_msm");
MODULE_DESCRIPTION("ChipIdea Highspeed Dual Role Controller");
MODULE_LICENSE("GPL v2");

View File

@ -372,7 +372,7 @@ static void tegra_usb_remove(struct platform_device *pdev)
pm_runtime_force_suspend(&pdev->dev);
}
static int __maybe_unused tegra_usb_runtime_resume(struct device *dev)
static int tegra_usb_runtime_resume(struct device *dev)
{
struct tegra_usb *usb = dev_get_drvdata(dev);
int err;
@ -386,7 +386,7 @@ static int __maybe_unused tegra_usb_runtime_resume(struct device *dev)
return 0;
}
static int __maybe_unused tegra_usb_runtime_suspend(struct device *dev)
static int tegra_usb_runtime_suspend(struct device *dev)
{
struct tegra_usb *usb = dev_get_drvdata(dev);
@ -396,15 +396,14 @@ static int __maybe_unused tegra_usb_runtime_suspend(struct device *dev)
}
static const struct dev_pm_ops tegra_usb_pm = {
SET_RUNTIME_PM_OPS(tegra_usb_runtime_suspend, tegra_usb_runtime_resume,
NULL)
RUNTIME_PM_OPS(tegra_usb_runtime_suspend, tegra_usb_runtime_resume, NULL)
};
static struct platform_driver tegra_usb_driver = {
.driver = {
.name = "tegra-usb",
.of_match_table = tegra_usb_of_match,
.pm = &tegra_usb_pm,
.pm = pm_ptr(&tegra_usb_pm),
},
.probe = tegra_usb_probe,
.remove_new = tegra_usb_remove,

View File

@ -2592,4 +2592,5 @@ static struct usb_driver usbtmc_driver = {
module_usb_driver(usbtmc_driver);
MODULE_DESCRIPTION("USB Test & Measurement class driver");
MODULE_LICENSE("GPL");

View File

@ -433,4 +433,5 @@ static void __exit usb_common_exit(void)
subsys_initcall(usb_common_init);
module_exit(usb_common_exit);
MODULE_DESCRIPTION("Common code for host and device side USB");
MODULE_LICENSE("GPL");

View File

@ -449,4 +449,5 @@ int otg_statemachine(struct otg_fsm *fsm)
return fsm->state_changed;
}
EXPORT_SYMBOL_GPL(otg_statemachine);
MODULE_DESCRIPTION("OTG Finite State Machine");
MODULE_LICENSE("GPL");

View File

@ -517,6 +517,19 @@ static int usb_unbind_interface(struct device *dev)
return 0;
}
static void usb_shutdown_interface(struct device *dev)
{
struct usb_interface *intf = to_usb_interface(dev);
struct usb_driver *driver;
if (!dev->driver)
return;
driver = to_usb_driver(dev->driver);
if (driver->shutdown)
driver->shutdown(intf);
}
/**
* usb_driver_claim_interface - bind a driver to an interface
* @driver: the driver to be bound
@ -1059,6 +1072,7 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
new_driver->driver.bus = &usb_bus_type;
new_driver->driver.probe = usb_probe_interface;
new_driver->driver.remove = usb_unbind_interface;
new_driver->driver.shutdown = usb_shutdown_interface;
new_driver->driver.owner = owner;
new_driver->driver.mod_name = mod_name;
new_driver->driver.dev_groups = new_driver->dev_groups;

View File

@ -1150,4 +1150,5 @@ static void __exit usb_exit(void)
subsys_initcall(usb_init);
module_exit(usb_exit);
MODULE_DESCRIPTION("USB core host-side support");
MODULE_LICENSE("GPL");

View File

@ -885,10 +885,10 @@ static void dwc2_gadget_config_nonisoc_xfer_ddma(struct dwc2_hsotg_ep *hs_ep,
}
/* DMA sg buffer */
for_each_sg(ureq->sg, sg, ureq->num_sgs, i) {
for_each_sg(ureq->sg, sg, ureq->num_mapped_sgs, i) {
dwc2_gadget_fill_nonisoc_xfer_ddma_one(hs_ep, &desc,
sg_dma_address(sg) + sg->offset, sg_dma_len(sg),
sg_is_last(sg));
(i == (ureq->num_mapped_sgs - 1)));
desc_count += hs_ep->desc_count;
}

View File

@ -133,7 +133,15 @@ static void dwc2_set_rk_params(struct dwc2_hsotg *hsotg)
p->no_clock_gating = true;
}
static void dwc2_set_ltq_params(struct dwc2_hsotg *hsotg)
static void dwc2_set_ltq_danube_params(struct dwc2_hsotg *hsotg)
{
struct dwc2_core_params *p = &hsotg->params;
p->otg_caps.hnp_support = false;
p->otg_caps.srp_support = false;
}
static void dwc2_set_ltq_ase_params(struct dwc2_hsotg *hsotg)
{
struct dwc2_core_params *p = &hsotg->params;
@ -142,12 +150,21 @@ static void dwc2_set_ltq_params(struct dwc2_hsotg *hsotg)
p->host_rx_fifo_size = 288;
p->host_nperio_tx_fifo_size = 128;
p->host_perio_tx_fifo_size = 96;
p->max_transfer_size = 65535;
p->max_packet_count = 511;
p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 <<
GAHBCFG_HBSTLEN_SHIFT;
}
static void dwc2_set_ltq_xrx200_params(struct dwc2_hsotg *hsotg)
{
struct dwc2_core_params *p = &hsotg->params;
p->otg_caps.hnp_support = false;
p->otg_caps.srp_support = false;
p->host_rx_fifo_size = 288;
p->host_nperio_tx_fifo_size = 128;
p->host_perio_tx_fifo_size = 136;
}
static void dwc2_set_amlogic_params(struct dwc2_hsotg *hsotg)
{
struct dwc2_core_params *p = &hsotg->params;
@ -297,8 +314,11 @@ const struct of_device_id dwc2_of_match_table[] = {
{ .compatible = "ingenic,x1830-otg", .data = dwc2_set_x1600_params },
{ .compatible = "ingenic,x2000-otg", .data = dwc2_set_x2000_params },
{ .compatible = "rockchip,rk3066-usb", .data = dwc2_set_rk_params },
{ .compatible = "lantiq,arx100-usb", .data = dwc2_set_ltq_params },
{ .compatible = "lantiq,xrx200-usb", .data = dwc2_set_ltq_params },
{ .compatible = "lantiq,danube-usb", .data = &dwc2_set_ltq_danube_params },
{ .compatible = "lantiq,ase-usb", .data = &dwc2_set_ltq_ase_params },
{ .compatible = "lantiq,arx100-usb", .data = &dwc2_set_ltq_ase_params },
{ .compatible = "lantiq,xrx200-usb", .data = &dwc2_set_ltq_xrx200_params },
{ .compatible = "lantiq,xrx300-usb", .data = &dwc2_set_ltq_xrx200_params },
{ .compatible = "snps,dwc2" },
{ .compatible = "samsung,s3c6400-hsotg",
.data = dwc2_set_s3c6400_params },

View File

@ -108,22 +108,27 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc)
void dwc3_enable_susphy(struct dwc3 *dwc, bool enable)
{
u32 reg;
int i;
reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
if (enable && !dwc->dis_u3_susphy_quirk)
reg |= DWC3_GUSB3PIPECTL_SUSPHY;
else
reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
for (i = 0; i < dwc->num_usb3_ports; i++) {
reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(i));
if (enable && !dwc->dis_u3_susphy_quirk)
reg |= DWC3_GUSB3PIPECTL_SUSPHY;
else
reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(i), reg);
}
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
if (enable && !dwc->dis_u2_susphy_quirk)
reg |= DWC3_GUSB2PHYCFG_SUSPHY;
else
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
for (i = 0; i < dwc->num_usb2_ports; i++) {
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(i));
if (enable && !dwc->dis_u2_susphy_quirk)
reg |= DWC3_GUSB2PHYCFG_SUSPHY;
else
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(i), reg);
}
}
void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode)
@ -599,6 +604,18 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc)
parms->hwparams9 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS9);
}
static void dwc3_config_soc_bus(struct dwc3 *dwc)
{
if (dwc->gsbuscfg0_reqinfo != DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED) {
u32 reg;
reg = dwc3_readl(dwc->regs, DWC3_GSBUSCFG0);
reg &= ~DWC3_GSBUSCFG0_REQINFO(~0);
reg |= DWC3_GSBUSCFG0_REQINFO(dwc->gsbuscfg0_reqinfo);
dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, reg);
}
}
static int dwc3_core_ulpi_init(struct dwc3 *dwc)
{
int intf;
@ -1338,6 +1355,8 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc3_set_incr_burst_type(dwc);
dwc3_config_soc_bus(dwc);
ret = dwc3_phy_power_on(dwc);
if (ret)
goto err_exit_phy;
@ -1576,6 +1595,27 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc)
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE);
}
static void dwc3_get_software_properties(struct dwc3 *dwc)
{
struct device *tmpdev;
u16 gsbuscfg0_reqinfo;
int ret;
dwc->gsbuscfg0_reqinfo = DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED;
/*
* Iterate over all parent nodes for finding swnode properties
* and non-DT (non-ABI) properties.
*/
for (tmpdev = dwc->dev; tmpdev; tmpdev = tmpdev->parent) {
ret = device_property_read_u16(tmpdev,
"snps,gsbuscfg0-reqinfo",
&gsbuscfg0_reqinfo);
if (!ret)
dwc->gsbuscfg0_reqinfo = gsbuscfg0_reqinfo;
}
}
static void dwc3_get_properties(struct dwc3 *dwc)
{
struct device *dev = dwc->dev;
@ -2090,6 +2130,8 @@ static int dwc3_probe(struct platform_device *pdev)
dwc3_get_properties(dwc);
dwc3_get_software_properties(dwc);
dwc->reset = devm_reset_control_array_get_optional_shared(dev);
if (IS_ERR(dwc->reset)) {
ret = PTR_ERR(dwc->reset);

View File

@ -194,6 +194,10 @@
#define DWC3_GSBUSCFG0_INCRBRSTENA (1 << 0) /* undefined length enable */
#define DWC3_GSBUSCFG0_INCRBRST_MASK 0xff
/* Global SoC Bus Configuration Register: AHB-prot/AXI-cache/OCP-ReqInfo */
#define DWC3_GSBUSCFG0_REQINFO(n) (((n) & 0xffff) << 16)
#define DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED 0xffffffff
/* Global Debug LSP MUX Select */
#define DWC3_GDBGLSPMUX_ENDBC BIT(15) /* Host only */
#define DWC3_GDBGLSPMUX_HOSTSELECT(n) ((n) & 0x3fff)
@ -1153,6 +1157,9 @@ struct dwc3_scratchpad_array {
* @num_ep_resized: carries the current number endpoints which have had its tx
* fifo resized.
* @debug_root: root debugfs directory for this device to put its files in.
* @gsbuscfg0_reqinfo: store GSBUSCFG0.DATRDREQINFO, DESRDREQINFO,
* DATWRREQINFO, and DESWRREQINFO value passed from
* glue driver.
*/
struct dwc3 {
struct work_struct drd_work;
@ -1380,6 +1387,7 @@ struct dwc3 {
int last_fifo_depth;
int num_ep_resized;
struct dentry *debug_root;
u32 gsbuscfg0_reqinfo;
};
#define INCRX_BURST_MODE 0

View File

@ -246,6 +246,31 @@ static const struct of_device_id dwc3_xlnx_of_match[] = {
};
MODULE_DEVICE_TABLE(of, dwc3_xlnx_of_match);
static int dwc3_set_swnode(struct device *dev)
{
struct device_node *np = dev->of_node, *dwc3_np;
struct property_entry props[2];
int prop_idx = 0, ret = 0;
dwc3_np = of_get_compatible_child(np, "snps,dwc3");
if (!dwc3_np) {
ret = -ENODEV;
dev_err(dev, "failed to find dwc3 core child\n");
return ret;
}
memset(props, 0, sizeof(struct property_entry) * ARRAY_SIZE(props));
if (of_dma_is_coherent(dwc3_np))
props[prop_idx++] = PROPERTY_ENTRY_U16("snps,gsbuscfg0-reqinfo",
0xffff);
of_node_put(dwc3_np);
if (prop_idx)
ret = device_create_managed_software_node(dev, props, NULL);
return ret;
}
static int dwc3_xlnx_probe(struct platform_device *pdev)
{
struct dwc3_xlnx *priv_data;
@ -288,6 +313,10 @@ static int dwc3_xlnx_probe(struct platform_device *pdev)
if (ret)
goto err_clk_put;
ret = dwc3_set_swnode(dev);
if (ret)
goto err_clk_put;
ret = of_platform_populate(np, NULL, NULL, dev);
if (ret)
goto err_clk_put;

View File

@ -126,7 +126,7 @@ out:
int dwc3_host_init(struct dwc3 *dwc)
{
struct property_entry props[5];
struct property_entry props[6];
struct platform_device *xhci;
int ret, irq;
int prop_idx = 0;
@ -162,6 +162,8 @@ int dwc3_host_init(struct dwc3 *dwc)
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-sg-trb-cache-size-quirk");
props[prop_idx++] = PROPERTY_ENTRY_BOOL("write-64-hi-lo-quirk");
if (dwc->usb3_lpm_capable)
props[prop_idx++] = PROPERTY_ENTRY_BOOL("usb3-lpm-capable");

View File

@ -2799,5 +2799,6 @@ void usb_composite_overwrite_options(struct usb_composite_dev *cdev,
}
EXPORT_SYMBOL_GPL(usb_composite_overwrite_options);
MODULE_DESCRIPTION("infrastructure for Composite USB Gadgets");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Brownell");

View File

@ -854,4 +854,5 @@ static struct usb_function_instance *acm_alloc_instance(void)
return &opts->func_inst;
}
DECLARE_USB_FUNCTION_INIT(acm, acm_alloc_instance, acm_alloc_func);
MODULE_DESCRIPTION("USB CDC serial (ACM) function driver");
MODULE_LICENSE("GPL");

View File

@ -966,5 +966,6 @@ static struct usb_function *ecm_alloc(struct usb_function_instance *fi)
}
DECLARE_USB_FUNCTION_INIT(ecm, ecm_alloc_inst, ecm_alloc);
MODULE_DESCRIPTION("USB CDC Ethernet (ECM) link function driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Brownell");

View File

@ -674,5 +674,6 @@ static struct usb_function *eem_alloc(struct usb_function_instance *fi)
}
DECLARE_USB_FUNCTION_INIT(eem, eem_alloc_inst, eem_alloc);
MODULE_DESCRIPTION("USB CDC Ethernet (EEM) link function driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Brownell");

View File

@ -4316,5 +4316,6 @@ static char *ffs_prepare_buffer(const char __user *buf, size_t len)
}
DECLARE_USB_FUNCTION_INIT(ffs, ffs_alloc_inst, ffs_alloc);
MODULE_DESCRIPTION("user mode file system API for USB composite function controllers");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Michal Nazarewicz");

View File

@ -1322,6 +1322,7 @@ err_unlock:
}
DECLARE_USB_FUNCTION_INIT(hid, hidg_alloc_inst, hidg_alloc);
MODULE_DESCRIPTION("USB HID function driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Fabien Chouteau");

View File

@ -593,4 +593,5 @@ void __exit lb_modexit(void)
usb_function_unregister(&Loopbackusb_func);
}
MODULE_DESCRIPTION("USB peripheral loopback configuration driver");
MODULE_LICENSE("GPL");

View File

@ -3577,6 +3577,7 @@ static struct usb_function *fsg_alloc(struct usb_function_instance *fi)
}
DECLARE_USB_FUNCTION_INIT(mass_storage, fsg_alloc_inst, fsg_alloc);
MODULE_DESCRIPTION("Mass Storage USB Composite Function");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Michal Nazarewicz");

View File

@ -38,6 +38,7 @@
#include "u_midi.h"
MODULE_AUTHOR("Ben Williamson");
MODULE_DESCRIPTION("USB MIDI class function driver");
MODULE_LICENSE("GPL v2");
static const char f_midi_shortname[] = "f_midi";

View File

@ -150,6 +150,9 @@ struct f_midi2 {
#define func_to_midi2(f) container_of(f, struct f_midi2, func)
/* convert from MIDI protocol number (1 or 2) to SNDRV_UMP_EP_INFO_PROTO_* */
#define to_ump_protocol(v) (((v) & 3) << 8)
/* get EP name string */
static const char *ump_ep_name(const struct f_midi2_ep *ep)
{
@ -564,8 +567,7 @@ static void reply_ump_stream_ep_config(struct f_midi2_ep *ep)
.status = UMP_STREAM_MSG_STATUS_STREAM_CFG,
};
if ((ep->info.protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI_MASK) ==
SNDRV_UMP_EP_INFO_PROTO_MIDI2)
if (ep->info.protocol == 2)
rep.protocol = UMP_STREAM_MSG_EP_INFO_CAP_MIDI2 >> 8;
else
rep.protocol = UMP_STREAM_MSG_EP_INFO_CAP_MIDI1 >> 8;
@ -627,13 +629,13 @@ static void process_ump_stream_msg(struct f_midi2_ep *ep, const u32 *data)
return;
case UMP_STREAM_MSG_STATUS_STREAM_CFG_REQUEST:
if (*data & UMP_STREAM_MSG_EP_INFO_CAP_MIDI2) {
ep->info.protocol = SNDRV_UMP_EP_INFO_PROTO_MIDI2;
ep->info.protocol = 2;
DBG(midi2, "Switching Protocol to MIDI2\n");
} else {
ep->info.protocol = SNDRV_UMP_EP_INFO_PROTO_MIDI1;
ep->info.protocol = 1;
DBG(midi2, "Switching Protocol to MIDI1\n");
}
snd_ump_switch_protocol(ep->ump, ep->info.protocol);
snd_ump_switch_protocol(ep->ump, to_ump_protocol(ep->info.protocol));
reply_ump_stream_ep_config(ep);
return;
case UMP_STREAM_MSG_STATUS_FB_DISCOVERY:
@ -1065,7 +1067,8 @@ static void f_midi2_midi1_ep_out_complete(struct usb_ep *usb_ep,
group = midi2->out_cable_mapping[cable].group;
bytes = midi1_packet_bytes[*buf & 0x0f];
for (c = 0; c < bytes; c++) {
snd_ump_convert_to_ump(cvt, group, ep->info.protocol,
snd_ump_convert_to_ump(cvt, group,
to_ump_protocol(ep->info.protocol),
buf[c + 1]);
if (cvt->ump_bytes) {
snd_ump_receive(ep->ump, cvt->ump,
@ -1375,7 +1378,7 @@ static void assign_block_descriptors(struct f_midi2 *midi2,
desc->nNumGroupTrm = b->num_groups;
desc->iBlockItem = ep->blks[blk].string_id;
if (ep->info.protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI2)
if (ep->info.protocol == 2)
desc->bMIDIProtocol = USB_MS_MIDI_PROTO_2_0;
else
desc->bMIDIProtocol = USB_MS_MIDI_PROTO_1_0_128;
@ -1552,7 +1555,7 @@ static int f_midi2_create_card(struct f_midi2 *midi2)
if (midi2->info.static_block)
ump->info.flags |= SNDRV_UMP_EP_INFO_STATIC_BLOCKS;
ump->info.protocol_caps = (ep->info.protocol_caps & 3) << 8;
ump->info.protocol = (ep->info.protocol & 3) << 8;
ump->info.protocol = to_ump_protocol(ep->info.protocol);
ump->info.version = 0x0101;
ump->info.family_id = ep->info.family;
ump->info.model_id = ep->info.model;
@ -2868,4 +2871,5 @@ static struct usb_function *f_midi2_alloc(struct usb_function_instance *fi)
DECLARE_USB_FUNCTION_INIT(midi2, f_midi2_alloc_inst, f_midi2_alloc);
MODULE_DESCRIPTION("USB MIDI 2.0 class function driver");
MODULE_LICENSE("GPL");

View File

@ -1797,5 +1797,6 @@ static struct usb_function *ncm_alloc(struct usb_function_instance *fi)
}
DECLARE_USB_FUNCTION_INIT(ncm, ncm_alloc_inst, ncm_alloc);
MODULE_DESCRIPTION("USB CDC Network (NCM) link function driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Yauheni Kaliuta");

View File

@ -487,4 +487,5 @@ static struct usb_function *obex_alloc(struct usb_function_instance *fi)
DECLARE_USB_FUNCTION_INIT(obex, obex_alloc_inst, obex_alloc);
MODULE_AUTHOR("Felipe Balbi");
MODULE_DESCRIPTION("USB CDC OBEX function driver");
MODULE_LICENSE("GPL");

View File

@ -729,4 +729,5 @@ void gphonet_cleanup(struct net_device *dev)
DECLARE_USB_FUNCTION_INIT(phonet, phonet_alloc_inst, phonet_alloc);
MODULE_AUTHOR("Rémi Denis-Courmont");
MODULE_DESCRIPTION("USB CDC Phonet function");
MODULE_LICENSE("GPL");

View File

@ -1527,6 +1527,7 @@ static struct usb_function *gprinter_alloc(struct usb_function_instance *fi)
}
DECLARE_USB_FUNCTION_INIT(printer, gprinter_alloc_inst, gprinter_alloc);
MODULE_DESCRIPTION("USB printer function driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Craig Nadler");

View File

@ -1013,5 +1013,6 @@ static struct usb_function *rndis_alloc(struct usb_function_instance *fi)
}
DECLARE_USB_FUNCTION_INIT(rndis, rndis_alloc_inst, rndis_alloc);
MODULE_DESCRIPTION("RNDIS link function driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Brownell");

View File

@ -392,6 +392,7 @@ static struct usb_function *gser_alloc(struct usb_function_instance *fi)
}
DECLARE_USB_FUNCTION_INIT(gser, gser_alloc_inst, gser_alloc);
MODULE_DESCRIPTION("generic USB serial function driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Al Borchers");
MODULE_AUTHOR("David Brownell");

View File

@ -1284,4 +1284,5 @@ static void __exit sslb_modexit(void)
module_init(sslb_modinit);
module_exit(sslb_modexit);
MODULE_DESCRIPTION("USB peripheral source/sink configuration driver");
MODULE_LICENSE("GPL");

View File

@ -500,5 +500,6 @@ static struct usb_function *geth_alloc(struct usb_function_instance *fi)
}
DECLARE_USB_FUNCTION_INIT(geth, geth_alloc_inst, geth_alloc);
MODULE_DESCRIPTION("\"CDC Subset\" Ethernet link function driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Brownell");

View File

@ -2301,5 +2301,6 @@ static void __exit tcm_exit(void)
}
module_exit(tcm_exit);
MODULE_DESCRIPTION("Target based USB-Gadget");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sebastian Andrzej Siewior");

View File

@ -1823,5 +1823,6 @@ static struct usb_function *f_audio_alloc(struct usb_function_instance *fi)
}
DECLARE_USB_FUNCTION_INIT(uac1, f_audio_alloc_inst, f_audio_alloc);
MODULE_DESCRIPTION("USB Audio Class 1.0 Function (using u_audio API)");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ruslan Bilovol");

View File

@ -1014,5 +1014,6 @@ static struct usb_function *f_audio_alloc(struct usb_function_instance *fi)
}
DECLARE_USB_FUNCTION_INIT(uac1_legacy, f_audio_alloc_inst, f_audio_alloc);
MODULE_DESCRIPTION("USB Audio class function driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Bryan Wu");

View File

@ -2063,7 +2063,10 @@ static ssize_t f_uac2_opts_##name##_store(struct config_item *item, \
goto end; \
} \
\
ret = scnprintf(opts->name, min(sizeof(opts->name), len), \
if (len && page[len - 1] == '\n') \
len--; \
\
ret = scnprintf(opts->name, min(sizeof(opts->name), len + 1), \
"%s", page); \
\
end: \
@ -2251,6 +2254,7 @@ static struct usb_function *afunc_alloc(struct usb_function_instance *fi)
}
DECLARE_USB_FUNCTION_INIT(uac2, afunc_alloc_inst, afunc_alloc);
MODULE_DESCRIPTION("USB Audio Class 2.0 Function");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Yadwinder Singh");
MODULE_AUTHOR("Jaswinder Singh");

View File

@ -1118,5 +1118,6 @@ err_config:
}
DECLARE_USB_FUNCTION_INIT(uvc, uvc_alloc_inst, uvc_alloc);
MODULE_DESCRIPTION("USB Video Class Gadget driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Laurent Pinchart");

View File

@ -537,4 +537,5 @@ ssize_t fsg_store_forced_eject(struct fsg_lun *curlun, struct rw_semaphore *file
}
EXPORT_SYMBOL_GPL(fsg_store_forced_eject);
MODULE_DESCRIPTION("Common definitions for mass storage functionality");
MODULE_LICENSE("GPL");

View File

@ -1245,5 +1245,6 @@ void gether_disconnect(struct gether *link)
}
EXPORT_SYMBOL_GPL(gether_disconnect);
MODULE_DESCRIPTION("Ethernet-over-USB link layer utilities for Gadget stack");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Brownell");

View File

@ -1536,4 +1536,5 @@ static void __exit userial_cleanup(void)
}
module_exit(userial_cleanup);
MODULE_DESCRIPTION("utilities for USB gadget \"serial port\"/TTY support");
MODULE_LICENSE("GPL");

View File

@ -434,6 +434,7 @@ static void __exit dbgp_exit(void)
}
MODULE_AUTHOR("Stephane Duverger");
MODULE_DESCRIPTION("EHCI Debug Port device gadget");
MODULE_LICENSE("GPL");
module_init(dbgp_init);
module_exit(dbgp_exit);

View File

@ -31,6 +31,7 @@
/*-------------------------------------------------------------------------*/
MODULE_AUTHOR("Ben Williamson");
MODULE_DESCRIPTION("USB MIDI Gadget Driver");
MODULE_LICENSE("GPL v2");
static const char longname[] = "MIDI Gadget";

View File

@ -425,4 +425,5 @@ static struct usb_composite_driver zero_driver = {
module_usb_composite_driver(zero_driver);
MODULE_AUTHOR("David Brownell");
MODULE_DESCRIPTION("Gadget Zero, for USB development");
MODULE_LICENSE("GPL");

View File

@ -1009,6 +1009,8 @@ static void ast_udc_getstatus(struct ast_udc_dev *udc)
break;
case USB_RECIP_ENDPOINT:
epnum = crq.wIndex & USB_ENDPOINT_NUMBER_MASK;
if (epnum >= AST_UDC_NUM_ENDPOINTS)
goto stall;
status = udc->ep[epnum].stopped;
break;
default:

View File

@ -293,4 +293,5 @@ module_exit(ohci_exynos_cleanup);
MODULE_ALIAS("platform:exynos-ohci");
MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
MODULE_DESCRIPTION("OHCI support for Samsung S5P/Exynos SoC Series");
MODULE_LICENSE("GPL v2");

View File

@ -196,31 +196,6 @@ struct ehci_regs {
#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC)
} __packed;
/* Appendix C, Debug port ... intended for use with special "debug devices"
* that can help if there's no serial console. (nonstandard enumeration.)
*/
struct ehci_dbg_port {
u32 control;
#define DBGP_OWNER (1<<30)
#define DBGP_ENABLED (1<<28)
#define DBGP_DONE (1<<16)
#define DBGP_INUSE (1<<10)
#define DBGP_ERRCODE(x) (((x)>>7)&0x07)
# define DBGP_ERR_BAD 1
# define DBGP_ERR_SIGNAL 2
#define DBGP_ERROR (1<<6)
#define DBGP_GO (1<<5)
#define DBGP_OUT (1<<4)
#define DBGP_LEN(x) (((x)>>0)&0x0f)
u32 pids;
#define DBGP_PID_GET(x) (((x)>>16)&0xff)
#define DBGP_PID_SET(data, tok) (((data)<<8)|(tok))
u32 data03;
u32 data47;
u32 address;
#define DBGP_EPADDR(dev, ep) (((dev)<<8)|(ep))
} __packed;
#define QTD_NEXT(dma) cpu_to_le32((u32)dma)
/*

View File

@ -1150,11 +1150,48 @@ static ssize_t dbc_bInterfaceProtocol_store(struct device *dev,
return size;
}
static ssize_t dbc_poll_interval_ms_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct xhci_dbc *dbc;
struct xhci_hcd *xhci;
xhci = hcd_to_xhci(dev_get_drvdata(dev));
dbc = xhci->dbc;
return sysfs_emit(buf, "%u\n", dbc->poll_interval);
}
static ssize_t dbc_poll_interval_ms_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct xhci_dbc *dbc;
struct xhci_hcd *xhci;
u32 value;
int ret;
ret = kstrtou32(buf, 0, &value);
if (ret || value > DBC_POLL_INTERVAL_MAX)
return -EINVAL;
xhci = hcd_to_xhci(dev_get_drvdata(dev));
dbc = xhci->dbc;
dbc->poll_interval = value;
mod_delayed_work(system_wq, &dbc->event_work, 0);
return size;
}
static DEVICE_ATTR_RW(dbc);
static DEVICE_ATTR_RW(dbc_idVendor);
static DEVICE_ATTR_RW(dbc_idProduct);
static DEVICE_ATTR_RW(dbc_bcdDevice);
static DEVICE_ATTR_RW(dbc_bInterfaceProtocol);
static DEVICE_ATTR_RW(dbc_poll_interval_ms);
static struct attribute *dbc_dev_attrs[] = {
&dev_attr_dbc.attr,
@ -1162,6 +1199,7 @@ static struct attribute *dbc_dev_attrs[] = {
&dev_attr_dbc_idProduct.attr,
&dev_attr_dbc_bcdDevice.attr,
&dev_attr_dbc_bInterfaceProtocol.attr,
&dev_attr_dbc_poll_interval_ms.attr,
NULL
};
ATTRIBUTE_GROUPS(dbc_dev);

View File

@ -95,7 +95,7 @@ struct dbc_ep {
#define DBC_QUEUE_SIZE 16
#define DBC_WRITE_BUF_SIZE 8192
#define DBC_POLL_INTERVAL_DEFAULT 64 /* milliseconds */
#define DBC_POLL_INTERVAL_MAX 5000 /* milliseconds */
/*
* Private structure for DbC hardware state:
*/

View File

@ -136,10 +136,7 @@ static void xhci_link_rings(struct xhci_hcd *xhci, struct xhci_ring *ring,
if (!ring || !first || !last)
return;
/* Set chain bit for 0.95 hosts, and for isoc rings on AMD 0.96 host */
chain_links = !!(xhci_link_trb_quirk(xhci) ||
(ring->type == TYPE_ISOC &&
(xhci->quirks & XHCI_AMD_0x96_HOST)));
chain_links = xhci_link_chain_quirk(xhci, ring->type);
next = ring->enq_seg->next;
xhci_link_segments(ring->enq_seg, first, ring->type, chain_links);
@ -156,7 +153,7 @@ static void xhci_link_rings(struct xhci_hcd *xhci, struct xhci_ring *ring,
ring->last_seg = last;
}
for (seg = last; seg != ring->last_seg; seg = seg->next)
for (seg = ring->enq_seg; seg != ring->last_seg; seg = seg->next)
seg->next->num = seg->num + 1;
}
@ -327,18 +324,19 @@ EXPORT_SYMBOL_GPL(xhci_initialize_ring_info);
/* Allocate segments and link them for a ring */
static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci,
struct xhci_segment **first, struct xhci_segment **last,
unsigned int num_segs, unsigned int num,
unsigned int cycle_state, enum xhci_ring_type type,
unsigned int max_packet, gfp_t flags)
struct xhci_segment **first,
struct xhci_segment **last,
unsigned int num_segs,
unsigned int cycle_state,
enum xhci_ring_type type,
unsigned int max_packet,
gfp_t flags)
{
struct xhci_segment *prev;
unsigned int num = 0;
bool chain_links;
/* Set chain bit for 0.95 hosts, and for isoc rings on AMD 0.96 host */
chain_links = !!(xhci_link_trb_quirk(xhci) ||
(type == TYPE_ISOC &&
(xhci->quirks & XHCI_AMD_0x96_HOST)));
chain_links = xhci_link_chain_quirk(xhci, type);
prev = xhci_segment_alloc(xhci, cycle_state, max_packet, num, flags);
if (!prev)
@ -394,9 +392,8 @@ struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci,
if (num_segs == 0)
return ring;
ret = xhci_alloc_segments_for_ring(xhci, &ring->first_seg,
&ring->last_seg, num_segs, 0, cycle_state, type,
max_packet, flags);
ret = xhci_alloc_segments_for_ring(xhci, &ring->first_seg, &ring->last_seg, num_segs,
cycle_state, type, max_packet, flags);
if (ret)
goto fail;
@ -434,10 +431,8 @@ int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring,
struct xhci_segment *last;
int ret;
ret = xhci_alloc_segments_for_ring(xhci, &first, &last,
num_new_segs, ring->enq_seg->num + 1,
ring->cycle_state, ring->type,
ring->bounce_buf_len, flags);
ret = xhci_alloc_segments_for_ring(xhci, &first, &last, num_new_segs, ring->cycle_state,
ring->type, ring->bounce_buf_len, flags);
if (ret)
return -ENOMEM;
@ -2325,7 +2320,10 @@ xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir,
erst_base = xhci_read_64(xhci, &ir->ir_set->erst_base);
erst_base &= ERST_BASE_RSVDP;
erst_base |= ir->erst.erst_dma_addr & ~ERST_BASE_RSVDP;
xhci_write_64(xhci, erst_base, &ir->ir_set->erst_base);
if (xhci->quirks & XHCI_WRITE_64_HI_LO)
hi_lo_writeq(erst_base, &ir->ir_set->erst_base);
else
xhci_write_64(xhci, erst_base, &ir->ir_set->erst_base);
/* Set the event ring dequeue address of this interrupter */
xhci_set_hc_event_deq(xhci, ir);

View File

@ -627,4 +627,5 @@ exit:
}
EXPORT_SYMBOL_GPL(renesas_xhci_check_request_fw);
MODULE_DESCRIPTION("Support for Renesas xHCI controller with firmware");
MODULE_LICENSE("GPL v2");

View File

@ -50,6 +50,7 @@
#define PCI_DEVICE_ID_INTEL_DENVERTON_XHCI 0x19d0
#define PCI_DEVICE_ID_INTEL_ICE_LAKE_XHCI 0x8a13
#define PCI_DEVICE_ID_INTEL_TIGER_LAKE_XHCI 0x9a13
#define PCI_DEVICE_ID_INTEL_TIGER_LAKE_PCH_XHCI 0xa0ed
#define PCI_DEVICE_ID_INTEL_COMET_LAKE_XHCI 0xa3af
#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_PCH_XHCI 0x51ed
#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_N_PCH_XHCI 0x54ed
@ -373,7 +374,8 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
xhci->quirks |= XHCI_MISSING_CAS;
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
(pdev->device == PCI_DEVICE_ID_INTEL_ALDER_LAKE_PCH_XHCI ||
(pdev->device == PCI_DEVICE_ID_INTEL_TIGER_LAKE_PCH_XHCI ||
pdev->device == PCI_DEVICE_ID_INTEL_ALDER_LAKE_PCH_XHCI ||
pdev->device == PCI_DEVICE_ID_INTEL_ALDER_LAKE_N_PCH_XHCI))
xhci->quirks |= XHCI_RESET_TO_DEFAULT;

View File

@ -256,6 +256,9 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s
if (device_property_read_bool(tmpdev, "xhci-sg-trb-cache-size-quirk"))
xhci->quirks |= XHCI_SG_TRB_CACHE_SIZE_QUIRK;
if (device_property_read_bool(tmpdev, "write-64-hi-lo-quirk"))
xhci->quirks |= XHCI_WRITE_64_HI_LO;
device_property_read_u32(tmpdev, "imod-interval-ns",
&xhci->imod_interval);
}

View File

@ -250,9 +250,7 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring,
* AMD 0.96 host, carry over the chain bit of the previous TRB
* (which may mean the chain bit is cleared).
*/
if (!(ring->type == TYPE_ISOC &&
(xhci->quirks & XHCI_AMD_0x96_HOST)) &&
!xhci_link_trb_quirk(xhci)) {
if (!xhci_link_chain_quirk(xhci, ring->type)) {
next->link.control &= cpu_to_le32(~TRB_CHAIN);
next->link.control |= cpu_to_le32(chain);
}
@ -283,7 +281,7 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring,
* Only for transfer and command rings where driver is the producer, not for
* event rings.
*/
static unsigned int xhci_num_trbs_free(struct xhci_hcd *xhci, struct xhci_ring *ring)
static unsigned int xhci_num_trbs_free(struct xhci_ring *ring)
{
struct xhci_segment *enq_seg = ring->enq_seg;
union xhci_trb *enq = ring->enqueue;
@ -654,25 +652,6 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci,
stream_id);
return -ENODEV;
}
/*
* A cancelled TD can complete with a stall if HW cached the trb.
* In this case driver can't find td, but if the ring is empty we
* can move the dequeue pointer to the current enqueue position.
* We shouldn't hit this anymore as cached cancelled TRBs are given back
* after clearing the cache, but be on the safe side and keep it anyway
*/
if (!td) {
if (list_empty(&ep_ring->td_list)) {
new_seg = ep_ring->enq_seg;
new_deq = ep_ring->enqueue;
new_cycle = ep_ring->cycle_state;
xhci_dbg(xhci, "ep ring empty, Set new dequeue = enqueue");
goto deq_found;
} else {
xhci_warn(xhci, "Can't find new dequeue state, missing td\n");
return -EINVAL;
}
}
hw_dequeue = xhci_get_hw_deq(xhci, dev, ep_index, stream_id);
new_seg = ep_ring->deq_seg;
@ -709,8 +688,6 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci,
} while (!cycle_found || !td_last_trb_found);
deq_found:
/* Don't update the ring cycle state for the producer (us). */
addr = xhci_trb_virt_to_dma(new_seg, new_deq);
if (addr == 0) {
@ -738,7 +715,7 @@ deq_found:
lower_32_bits(addr) | trb_sct | new_cycle,
upper_32_bits(addr),
STREAM_ID_FOR_TRB(stream_id), SLOT_ID_FOR_TRB(slot_id) |
EP_ID_FOR_TRB(ep_index) | TRB_TYPE(TRB_SET_DEQ), false);
EP_INDEX_FOR_TRB(ep_index) | TRB_TYPE(TRB_SET_DEQ), false);
if (ret < 0) {
xhci_free_command(xhci, cmd);
return ret;
@ -783,10 +760,6 @@ static void td_to_noop(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
}
}
/*
* Must be called with xhci->lock held in interrupt context,
* releases and re-acquires xhci->lock
*/
static void xhci_giveback_urb_in_irq(struct xhci_hcd *xhci,
struct xhci_td *cur_td, int status)
{
@ -1511,8 +1484,8 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id,
ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
}
static void xhci_handle_cmd_enable_slot(struct xhci_hcd *xhci, int slot_id,
struct xhci_command *command, u32 cmd_comp_code)
static void xhci_handle_cmd_enable_slot(int slot_id, struct xhci_command *command,
u32 cmd_comp_code)
{
if (cmd_comp_code == COMP_SUCCESS)
command->slot_id = slot_id;
@ -1537,8 +1510,7 @@ static void xhci_handle_cmd_disable_slot(struct xhci_hcd *xhci, int slot_id)
xhci_free_device_endpoint_resources(xhci, virt_dev, true);
}
static void xhci_handle_cmd_config_ep(struct xhci_hcd *xhci, int slot_id,
u32 cmd_comp_code)
static void xhci_handle_cmd_config_ep(struct xhci_hcd *xhci, int slot_id)
{
struct xhci_virt_device *virt_dev;
struct xhci_input_control_ctx *ctrl_ctx;
@ -1780,14 +1752,14 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
cmd_type = TRB_FIELD_TO_TYPE(le32_to_cpu(cmd_trb->generic.field[3]));
switch (cmd_type) {
case TRB_ENABLE_SLOT:
xhci_handle_cmd_enable_slot(xhci, slot_id, cmd, cmd_comp_code);
xhci_handle_cmd_enable_slot(slot_id, cmd, cmd_comp_code);
break;
case TRB_DISABLE_SLOT:
xhci_handle_cmd_disable_slot(xhci, slot_id);
break;
case TRB_CONFIG_EP:
if (!cmd->completion)
xhci_handle_cmd_config_ep(xhci, slot_id, cmd_comp_code);
xhci_handle_cmd_config_ep(xhci, slot_id);
break;
case TRB_EVAL_CONTEXT:
break;
@ -1905,9 +1877,7 @@ static void xhci_cavium_reset_phy_quirk(struct xhci_hcd *xhci)
} while (!(pll_lock_check & 0x1) && --retry_count);
}
static void handle_port_status(struct xhci_hcd *xhci,
struct xhci_interrupter *ir,
union xhci_trb *event)
static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event)
{
struct usb_hcd *hcd;
u32 port_id;
@ -2156,30 +2126,34 @@ static void xhci_clear_hub_tt_buffer(struct xhci_hcd *xhci, struct xhci_td *td,
}
}
/* Check if an error has halted the endpoint ring. The class driver will
* cleanup the halt for a non-default control endpoint if we indicate a stall.
* However, a babble and other errors also halt the endpoint ring, and the class
* driver won't clear the halt in that case, so we need to issue a Set Transfer
* Ring Dequeue Pointer command manually.
/*
* Check if xhci internal endpoint state has gone to a "halt" state due to an
* error or stall, including default control pipe protocol stall.
* The internal halt needs to be cleared with a reset endpoint command.
*
* External device side is also halted in functional stall cases. Class driver
* will clear the device halt with a CLEAR_FEATURE(ENDPOINT_HALT) request later.
*/
static int xhci_requires_manual_halt_cleanup(struct xhci_hcd *xhci,
struct xhci_ep_ctx *ep_ctx,
unsigned int trb_comp_code)
static bool xhci_halted_host_endpoint(struct xhci_ep_ctx *ep_ctx, unsigned int comp_code)
{
/* TRB completion codes that may require a manual halt cleanup */
if (trb_comp_code == COMP_USB_TRANSACTION_ERROR ||
trb_comp_code == COMP_BABBLE_DETECTED_ERROR ||
trb_comp_code == COMP_SPLIT_TRANSACTION_ERROR)
/* The 0.95 spec says a babbling control endpoint
* is not halted. The 0.96 spec says it is. Some HW
* claims to be 0.95 compliant, but it halts the control
* endpoint anyway. Check if a babble halted the
* endpoint.
/* Stall halts both internal and device side endpoint */
if (comp_code == COMP_STALL_ERROR)
return true;
/* TRB completion codes that may require internal halt cleanup */
if (comp_code == COMP_USB_TRANSACTION_ERROR ||
comp_code == COMP_BABBLE_DETECTED_ERROR ||
comp_code == COMP_SPLIT_TRANSACTION_ERROR)
/*
* The 0.95 spec says a babbling control endpoint is not halted.
* The 0.96 spec says it is. Some HW claims to be 0.95
* compliant, but it halts the control endpoint anyway.
* Check endpoint context if endpoint is halted.
*/
if (GET_EP_CTX_STATE(ep_ctx) == EP_STATE_HALTED)
return 1;
return true;
return 0;
return false;
}
int xhci_is_vendor_info_code(struct xhci_hcd *xhci, unsigned int trb_comp_code)
@ -2349,8 +2323,7 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
case COMP_STOPPED_LENGTH_INVALID:
goto finish_td;
default:
if (!xhci_requires_manual_halt_cleanup(xhci,
ep_ctx, trb_comp_code))
if (!xhci_halted_host_endpoint(ep_ctx, trb_comp_code))
break;
xhci_dbg(xhci, "TRB error %u, halted endpoint index = %u\n",
trb_comp_code, ep->ep_index);
@ -2460,7 +2433,9 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
requested = remaining;
break;
case COMP_STOPPED_LENGTH_INVALID:
requested = 0;
/* exclude stopped trb with invalid length from length sum */
sum_trbs_for_length = true;
ep_trb_len = 0;
remaining = 0;
break;
default:
@ -2589,6 +2564,33 @@ finish_td:
return finish_td(xhci, ep, ep_ring, td, trb_comp_code);
}
/* Transfer events which don't point to a transfer TRB, see xhci 4.17.4 */
static int handle_transferless_tx_event(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
u32 trb_comp_code)
{
switch (trb_comp_code) {
case COMP_STALL_ERROR:
case COMP_USB_TRANSACTION_ERROR:
case COMP_INVALID_STREAM_TYPE_ERROR:
case COMP_INVALID_STREAM_ID_ERROR:
xhci_dbg(xhci, "Stream transaction error ep %u no id\n", ep->ep_index);
if (ep->err_count++ > MAX_SOFT_RETRY)
xhci_handle_halted_endpoint(xhci, ep, NULL, EP_HARD_RESET);
else
xhci_handle_halted_endpoint(xhci, ep, NULL, EP_SOFT_RESET);
break;
case COMP_RING_UNDERRUN:
case COMP_RING_OVERRUN:
case COMP_STOPPED_LENGTH_INVALID:
break;
default:
xhci_err(xhci, "Transfer event %u for unknown stream ring slot %u ep %u\n",
trb_comp_code, ep->vdev->slot_id, ep->ep_index);
return -ENODEV;
}
return 0;
}
/*
* If this function returns an error condition, it means it got a Transfer
* event with a corrupted Slot ID, Endpoint ID, or TRB DMA address.
@ -2609,7 +2611,6 @@ static int handle_tx_event(struct xhci_hcd *xhci,
int status = -EINPROGRESS;
struct xhci_ep_ctx *ep_ctx;
u32 trb_comp_code;
int td_num = 0;
slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1;
@ -2632,37 +2633,8 @@ static int handle_tx_event(struct xhci_hcd *xhci,
goto err_out;
}
/* Some transfer events don't always point to a trb, see xhci 4.17.4 */
if (!ep_ring) {
switch (trb_comp_code) {
case COMP_STALL_ERROR:
case COMP_USB_TRANSACTION_ERROR:
case COMP_INVALID_STREAM_TYPE_ERROR:
case COMP_INVALID_STREAM_ID_ERROR:
xhci_dbg(xhci, "Stream transaction error ep %u no id\n",
ep_index);
if (ep->err_count++ > MAX_SOFT_RETRY)
xhci_handle_halted_endpoint(xhci, ep, NULL,
EP_HARD_RESET);
else
xhci_handle_halted_endpoint(xhci, ep, NULL,
EP_SOFT_RESET);
break;
case COMP_RING_UNDERRUN:
case COMP_RING_OVERRUN:
case COMP_STOPPED_LENGTH_INVALID:
break;
default:
xhci_err(xhci, "ERROR Transfer event for unknown stream ring slot %u ep %u\n",
slot_id, ep_index);
goto err_out;
}
return 0;
}
/* Count current td numbers if ep->skip is set */
if (ep->skip)
td_num += list_count_nodes(&ep_ring->td_list);
if (!ep_ring)
return handle_transferless_tx_event(xhci, ep, trb_comp_code);
/* Look for common error cases */
switch (trb_comp_code) {
@ -2744,18 +2716,12 @@ static int handle_tx_event(struct xhci_hcd *xhci,
* a Ring Overrun Event for IN Isoch endpoint or Ring
* Underrun Event for OUT Isoch endpoint.
*/
xhci_dbg(xhci, "underrun event on endpoint\n");
if (!list_empty(&ep_ring->td_list))
xhci_dbg(xhci, "Underrun Event for slot %u ep %d still with TDs queued?\n",
slot_id, ep_index);
xhci_dbg(xhci, "Underrun event on slot %u ep %u\n", slot_id, ep_index);
if (ep->skip)
break;
return 0;
case COMP_RING_OVERRUN:
xhci_dbg(xhci, "overrun event on endpoint\n");
if (!list_empty(&ep_ring->td_list))
xhci_dbg(xhci, "Overrun Event for slot %u ep %d still with TDs queued?\n",
slot_id, ep_index);
xhci_dbg(xhci, "Overrun event on slot %u ep %u\n", slot_id, ep_index);
if (ep->skip)
break;
return 0;
@ -2822,44 +2788,17 @@ static int handle_tx_event(struct xhci_hcd *xhci,
xhci_dbg(xhci, "td_list is empty while skip flag set. Clear skip flag for slot %u ep %u.\n",
slot_id, ep_index);
}
if (trb_comp_code == COMP_STALL_ERROR ||
xhci_requires_manual_halt_cleanup(xhci, ep_ctx,
trb_comp_code)) {
xhci_handle_halted_endpoint(xhci, ep, NULL,
EP_HARD_RESET);
}
return 0;
}
/* We've skipped all the TDs on the ep ring when ep->skip set */
if (ep->skip && td_num == 0) {
ep->skip = false;
xhci_dbg(xhci, "All tds on the ep_ring skipped. Clear skip flag for slot %u ep %u.\n",
slot_id, ep_index);
return 0;
td = NULL;
goto check_endpoint_halted;
}
td = list_first_entry(&ep_ring->td_list, struct xhci_td,
td_list);
if (ep->skip)
td_num--;
/* Is this a TRB in the currently executing TD? */
ep_seg = trb_in_td(xhci, td, ep_trb_dma, false);
/*
* Skip the Force Stopped Event. The event_trb(event_dma) of FSE
* is not in the current TD pointed by ep_ring->dequeue because
* that the hardware dequeue pointer still at the previous TRB
* of the current TD. The previous TRB maybe a Link TD or the
* last TRB of the previous TD. The command completion handle
* will take care the rest.
*/
if (!ep_seg && (trb_comp_code == COMP_STOPPED ||
trb_comp_code == COMP_STOPPED_LENGTH_INVALID)) {
continue;
}
if (!ep_seg) {
if (ep->skip && usb_endpoint_xfer_isoc(&td->urb->ep->desc)) {
@ -2867,6 +2806,18 @@ static int handle_tx_event(struct xhci_hcd *xhci,
continue;
}
/*
* Skip the Force Stopped Event. The 'ep_trb' of FSE is not in the current
* TD pointed by 'ep_ring->dequeue' because that the hardware dequeue
* pointer still at the previous TRB of the current TD. The previous TRB
* maybe a Link TD or the last TRB of the previous TD. The command
* completion handle will take care the rest.
*/
if (trb_comp_code == COMP_STOPPED ||
trb_comp_code == COMP_STOPPED_LENGTH_INVALID) {
return 0;
}
/*
* Some hosts give a spurious success event after a short
* transfer. Ignore it.
@ -2916,10 +2867,6 @@ static int handle_tx_event(struct xhci_hcd *xhci,
return -ESHUTDOWN;
}
}
if (trb_comp_code == COMP_SHORT_PACKET)
ep_ring->last_td_was_short = true;
else
ep_ring->last_td_was_short = false;
if (ep->skip) {
xhci_dbg(xhci,
@ -2928,37 +2875,6 @@ static int handle_tx_event(struct xhci_hcd *xhci,
ep->skip = false;
}
ep_trb = &ep_seg->trbs[(ep_trb_dma - ep_seg->dma) /
sizeof(*ep_trb)];
trace_xhci_handle_transfer(ep_ring,
(struct xhci_generic_trb *) ep_trb);
/*
* No-op TRB could trigger interrupts in a case where
* a URB was killed and a STALL_ERROR happens right
* after the endpoint ring stopped. Reset the halted
* endpoint. Otherwise, the endpoint remains stalled
* indefinitely.
*/
if (trb_is_noop(ep_trb)) {
if (trb_comp_code == COMP_STALL_ERROR ||
xhci_requires_manual_halt_cleanup(xhci, ep_ctx,
trb_comp_code))
xhci_handle_halted_endpoint(xhci, ep, td,
EP_HARD_RESET);
} else {
td->status = status;
/* update the urb's actual_length and give back to the core */
if (usb_endpoint_xfer_control(&td->urb->ep->desc))
process_ctrl_td(xhci, ep, ep_ring, td, ep_trb, event);
else if (usb_endpoint_xfer_isoc(&td->urb->ep->desc))
process_isoc_td(xhci, ep, ep_ring, td, ep_trb, event);
else
process_bulk_intr_td(xhci, ep, ep_ring, td, ep_trb, event);
}
/*
* If ep->skip is set, it means there are missed tds on the
* endpoint ring need to take care of.
@ -2967,6 +2883,38 @@ static int handle_tx_event(struct xhci_hcd *xhci,
*/
} while (ep->skip);
if (trb_comp_code == COMP_SHORT_PACKET)
ep_ring->last_td_was_short = true;
else
ep_ring->last_td_was_short = false;
ep_trb = &ep_seg->trbs[(ep_trb_dma - ep_seg->dma) / sizeof(*ep_trb)];
trace_xhci_handle_transfer(ep_ring, (struct xhci_generic_trb *) ep_trb);
/*
* No-op TRB could trigger interrupts in a case where a URB was killed
* and a STALL_ERROR happens right after the endpoint ring stopped.
* Reset the halted endpoint. Otherwise, the endpoint remains stalled
* indefinitely.
*/
if (trb_is_noop(ep_trb))
goto check_endpoint_halted;
td->status = status;
/* update the urb's actual_length and give back to the core */
if (usb_endpoint_xfer_control(&td->urb->ep->desc))
process_ctrl_td(xhci, ep, ep_ring, td, ep_trb, event);
else if (usb_endpoint_xfer_isoc(&td->urb->ep->desc))
process_isoc_td(xhci, ep, ep_ring, td, ep_trb, event);
else
process_bulk_intr_td(xhci, ep, ep_ring, td, ep_trb, event);
check_endpoint_halted:
if (xhci_halted_host_endpoint(ep_ctx, trb_comp_code))
xhci_handle_halted_endpoint(xhci, ep, td, EP_HARD_RESET);
return 0;
err_out:
@ -3005,7 +2953,7 @@ static int xhci_handle_event_trb(struct xhci_hcd *xhci, struct xhci_interrupter
handle_cmd_completion(xhci, &event->event_cmd);
break;
case TRB_PORT_STATUS:
handle_port_status(xhci, ir, event);
handle_port_status(xhci, event);
break;
case TRB_TRANSFER:
handle_tx_event(xhci, ir, &event->trans_event);
@ -3065,8 +3013,7 @@ static void xhci_update_erst_dequeue(struct xhci_hcd *xhci,
}
/* Clear the interrupt pending bit for a specific interrupter. */
static void xhci_clear_interrupt_pending(struct xhci_hcd *xhci,
struct xhci_interrupter *ir)
static void xhci_clear_interrupt_pending(struct xhci_interrupter *ir)
{
if (!ir->ip_autoclear) {
u32 irq_pending;
@ -3087,7 +3034,7 @@ static int xhci_handle_events(struct xhci_hcd *xhci, struct xhci_interrupter *ir
int err;
u64 temp;
xhci_clear_interrupt_pending(xhci, ir);
xhci_clear_interrupt_pending(ir);
/* Event ring hasn't been allocated yet. */
if (!ir->event_ring || !ir->event_ring->dequeue) {
@ -3260,7 +3207,7 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
if (ep_ring != xhci->cmd_ring) {
new_segs = xhci_ring_expansion_needed(xhci, ep_ring, num_trbs);
} else if (xhci_num_trbs_free(xhci, ep_ring) <= num_trbs) {
} else if (xhci_num_trbs_free(ep_ring) <= num_trbs) {
xhci_err(xhci, "Do not support expand command ring\n");
return -ENOMEM;
}
@ -3278,9 +3225,7 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
/* If we're not dealing with 0.95 hardware or isoc rings
* on AMD 0.96 host, clear the chain bit.
*/
if (!xhci_link_trb_quirk(xhci) &&
!(ep_ring->type == TYPE_ISOC &&
(xhci->quirks & XHCI_AMD_0x96_HOST)))
if (!xhci_link_chain_quirk(xhci, ep_ring->type))
ep_ring->enqueue->link.control &=
cpu_to_le32(~TRB_CHAIN);
else
@ -3435,8 +3380,7 @@ static void giveback_first_trb(struct xhci_hcd *xhci, int slot_id,
xhci_ring_ep_doorbell(xhci, slot_id, ep_index, stream_id);
}
static void check_interval(struct xhci_hcd *xhci, struct urb *urb,
struct xhci_ep_ctx *ep_ctx)
static void check_interval(struct urb *urb, struct xhci_ep_ctx *ep_ctx)
{
int xhci_interval;
int ep_interval;
@ -3477,7 +3421,7 @@ int xhci_queue_intr_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
struct xhci_ep_ctx *ep_ctx;
ep_ctx = xhci_get_ep_ctx(xhci, xhci->devs[slot_id]->out_ctx, ep_index);
check_interval(xhci, urb, ep_ctx);
check_interval(urb, ep_ctx);
return xhci_queue_bulk_tx(xhci, mem_flags, urb, slot_id, ep_index);
}
@ -3723,7 +3667,6 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
upper_32_bits(send_addr),
length_field,
field);
td->num_trbs++;
addr += trb_buff_len;
sent_len = trb_buff_len;
@ -3750,7 +3693,6 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
urb_priv->td[1].last_trb_seg = ring->enq_seg;
field = TRB_TYPE(TRB_NORMAL) | ring->cycle_state | TRB_IOC;
queue_trb(xhci, ring, 0, 0, 0, TRB_INTR_TARGET(0), field);
urb_priv->td[1].num_trbs++;
}
check_trb_math(urb, enqd_len);
@ -3801,7 +3743,6 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
urb_priv = urb->hcpriv;
td = &urb_priv->td[0];
td->num_trbs = num_trbs;
/*
* Don't give the first TRB to the hardware (by toggling the cycle bit)
@ -4122,7 +4063,6 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
goto cleanup;
}
td = &urb_priv->td[i];
td->num_trbs = trbs_per_td;
/* use SIA as default, if frame id is used overwrite it */
sia_frame_id = TRB_SIA;
if (!(urb->transfer_flags & URB_ISO_ASAP) &&
@ -4286,7 +4226,7 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
* Check interval value. This should be done before we start to
* calculate the start frame value.
*/
check_interval(xhci, urb, ep_ctx);
check_interval(urb, ep_ctx);
/* Calculate the start frame and put it in urb->start_frame. */
if (HCC_CFC(xhci->hcc_params) && !list_empty(&ep_ring->td_list)) {
@ -4439,7 +4379,7 @@ int xhci_queue_stop_endpoint(struct xhci_hcd *xhci, struct xhci_command *cmd,
int slot_id, unsigned int ep_index, int suspend)
{
u32 trb_slot_id = SLOT_ID_FOR_TRB(slot_id);
u32 trb_ep_index = EP_ID_FOR_TRB(ep_index);
u32 trb_ep_index = EP_INDEX_FOR_TRB(ep_index);
u32 type = TRB_TYPE(TRB_STOP_RING);
u32 trb_suspend = SUSPEND_PORT_FOR_TRB(suspend);
@ -4452,7 +4392,7 @@ int xhci_queue_reset_ep(struct xhci_hcd *xhci, struct xhci_command *cmd,
enum xhci_ep_reset_type reset_type)
{
u32 trb_slot_id = SLOT_ID_FOR_TRB(slot_id);
u32 trb_ep_index = EP_ID_FOR_TRB(ep_index);
u32 trb_ep_index = EP_INDEX_FOR_TRB(ep_index);
u32 type = TRB_TYPE(TRB_RESET_EP);
if (reset_type == EP_SOFT_RESET)

View File

@ -250,6 +250,7 @@ DECLARE_EVENT_CLASS(xhci_log_urb,
TP_PROTO(struct urb *urb),
TP_ARGS(urb),
TP_STRUCT__entry(
__string(devname, dev_name(&urb->dev->dev))
__field(void *, urb)
__field(unsigned int, pipe)
__field(unsigned int, stream)
@ -265,6 +266,7 @@ DECLARE_EVENT_CLASS(xhci_log_urb,
__field(int, slot_id)
),
TP_fast_assign(
__assign_str(devname);
__entry->urb = urb;
__entry->pipe = urb->pipe;
__entry->stream = urb->stream_id;
@ -279,7 +281,8 @@ DECLARE_EVENT_CLASS(xhci_log_urb,
__entry->type = usb_endpoint_type(&urb->ep->desc);
__entry->slot_id = urb->dev->slot_id;
),
TP_printk("ep%d%s-%s: urb %p pipe %u slot %d length %d/%d sgs %d/%d stream %d flags %08x",
TP_printk("%s ep%d%s-%s: urb %p pipe %u slot %d length %d/%d sgs %d/%d stream %d flags %08x",
__get_str(devname),
__entry->epnum, __entry->dir_in ? "in" : "out",
__print_symbolic(__entry->type,
{ USB_ENDPOINT_XFER_INT, "intr" },

View File

@ -17,6 +17,7 @@
#include <linux/kernel.h>
#include <linux/usb/hcd.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/io-64-nonatomic-hi-lo.h>
/* Code sharing between pci-quirks and xhci hcd */
#include "xhci-ext-caps.h"
@ -805,12 +806,18 @@ struct xhci_transfer_event {
__le32 flags;
};
/* Transfer event TRB length bit mask */
/* bits 0:23 */
#define EVENT_TRB_LEN(p) ((p) & 0xffffff)
/* Transfer event flags bitfield, also for select command completion events */
#define TRB_TO_SLOT_ID(p) (((p) >> 24) & 0xff)
#define SLOT_ID_FOR_TRB(p) (((p) & 0xff) << 24)
/** Transfer Event bit fields **/
#define TRB_TO_EP_ID(p) (((p) >> 16) & 0x1f)
#define TRB_TO_EP_ID(p) (((p) >> 16) & 0x1f) /* Endpoint ID 1 - 31 */
#define EP_ID_FOR_TRB(p) (((p) & 0x1f) << 16)
#define TRB_TO_EP_INDEX(p) (TRB_TO_EP_ID(p) - 1) /* Endpoint index 0 - 30 */
#define EP_INDEX_FOR_TRB(p) ((((p) + 1) & 0x1f) << 16)
/* Transfer event TRB length bit mask */
#define EVENT_TRB_LEN(p) ((p) & 0xffffff)
/* Completion Code - only applicable for some types of TRBs */
#define COMP_CODE_MASK (0xff << 24)
@ -950,8 +957,6 @@ struct xhci_event_cmd {
__le32 flags;
};
/* flags bitmasks */
/* Address device - disable SetAddress */
#define TRB_BSR (1<<9)
@ -987,13 +992,8 @@ enum xhci_setup_dev {
/* bits 16:23 are the virtual function ID */
/* bits 24:31 are the slot ID */
#define TRB_TO_SLOT_ID(p) (((p) & (0xff<<24)) >> 24)
#define SLOT_ID_FOR_TRB(p) (((p) & 0xff) << 24)
/* Stop Endpoint TRB - ep_index to endpoint ID for this TRB */
#define TRB_TO_EP_INDEX(p) ((((p) & (0x1f << 16)) >> 16) - 1)
#define EP_ID_FOR_TRB(p) ((((p) + 1) & 0x1f) << 16)
#define SUSPEND_PORT_FOR_TRB(p) (((p) & 1) << 23)
#define TRB_TO_SUSPEND_PORT(p) (((p) & (1 << 23)) >> 23)
#define LAST_EP_INDEX 30
@ -1294,7 +1294,6 @@ struct xhci_td {
/* actual_length of the URB has already been set */
bool urb_length_set;
bool error_mid_td;
unsigned int num_trbs;
};
/*
@ -1628,6 +1627,7 @@ struct xhci_hcd {
#define XHCI_RESET_TO_DEFAULT BIT_ULL(44)
#define XHCI_ZHAOXIN_TRB_FETCH BIT_ULL(45)
#define XHCI_ZHAOXIN_HOST BIT_ULL(46)
#define XHCI_WRITE_64_HI_LO BIT_ULL(47)
unsigned int num_active_eps;
unsigned int limit_active_eps;
@ -1749,9 +1749,12 @@ static inline void xhci_write_64(struct xhci_hcd *xhci,
lo_hi_writeq(val, regs);
}
static inline int xhci_link_trb_quirk(struct xhci_hcd *xhci)
/* Link TRB chain should always be set on 0.95 hosts, and AMD 0.96 ISOC rings */
static inline bool xhci_link_chain_quirk(struct xhci_hcd *xhci, enum xhci_ring_type type)
{
return xhci->quirks & XHCI_LINK_TRB_QUIRK;
return (xhci->quirks & XHCI_LINK_TRB_QUIRK) ||
(type == TYPE_ISOC && (xhci->quirks & XHCI_AMD_0x96_HOST));
}
/* xHCI debugging */
@ -2021,8 +2024,7 @@ static inline const char *xhci_decode_trb(char *str, size_t size,
field1, field0,
xhci_trb_comp_code_string(GET_COMP_CODE(field2)),
EVENT_TRB_LEN(field2), TRB_TO_SLOT_ID(field3),
/* Macro decrements 1, maybe it shouldn't?!? */
TRB_TO_EP_INDEX(field3) + 1,
TRB_TO_EP_ID(field3),
xhci_trb_type_string(type),
field3 & EVENT_DATA ? 'E' : 'e',
field3 & TRB_CYCLE ? 'C' : 'c');
@ -2137,8 +2139,7 @@ static inline const char *xhci_decode_trb(char *str, size_t size,
xhci_trb_type_string(type),
field1, field0,
TRB_TO_SLOT_ID(field3),
/* Macro decrements 1, maybe it shouldn't?!? */
TRB_TO_EP_INDEX(field3) + 1,
TRB_TO_EP_ID(field3),
field3 & TRB_TSP ? 'T' : 't',
field3 & TRB_CYCLE ? 'C' : 'c');
break;
@ -2148,8 +2149,7 @@ static inline const char *xhci_decode_trb(char *str, size_t size,
xhci_trb_type_string(type),
TRB_TO_SLOT_ID(field3),
TRB_TO_SUSPEND_PORT(field3),
/* Macro decrements 1, maybe it shouldn't?!? */
TRB_TO_EP_INDEX(field3) + 1,
TRB_TO_EP_ID(field3),
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_SET_DEQ:
@ -2159,8 +2159,7 @@ static inline const char *xhci_decode_trb(char *str, size_t size,
field1, field0,
TRB_TO_STREAM_ID(field2),
TRB_TO_SLOT_ID(field3),
/* Macro decrements 1, maybe it shouldn't?!? */
TRB_TO_EP_INDEX(field3) + 1,
TRB_TO_EP_ID(field3),
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_RESET_DEV:

View File

@ -148,4 +148,5 @@ int ezusb_fx2_ihex_firmware_download(struct usb_device *dev,
EXPORT_SYMBOL_GPL(ezusb_fx2_ihex_firmware_download);
#endif
MODULE_DESCRIPTION("EZUSB device support");
MODULE_LICENSE("GPL");

View File

@ -127,5 +127,6 @@ static struct usb_driver isight_firmware_driver = {
module_usb_driver(isight_firmware_driver);
MODULE_DESCRIPTION("iSight firmware loading support");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");

View File

@ -454,16 +454,18 @@ static struct onboard_dev *_find_onboard_dev(struct device *dev)
return onboard_dev;
}
static bool onboard_dev_usbdev_match(struct usb_device *udev)
{
/* Onboard devices using this driver must have a device tree node */
return !!udev->dev.of_node;
}
static int onboard_dev_usbdev_probe(struct usb_device *udev)
{
struct device *dev = &udev->dev;
struct onboard_dev *onboard_dev;
int err;
/* ignore supported devices without device tree node */
if (!dev->of_node)
return -ENODEV;
onboard_dev = _find_onboard_dev(dev);
if (IS_ERR(onboard_dev))
return PTR_ERR(onboard_dev);
@ -513,6 +515,7 @@ MODULE_DEVICE_TABLE(usb, onboard_dev_id_table);
static struct usb_device_driver onboard_dev_usbdev_driver = {
.name = "onboard-usb-dev",
.match = onboard_dev_usbdev_match,
.probe = onboard_dev_usbdev_probe,
.disconnect = onboard_dev_usbdev_disconnect,
.generic_subclass = 1,

View File

@ -726,15 +726,15 @@ static int __maybe_unused usb251xb_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(usb251xb_pm_ops, usb251xb_suspend, usb251xb_resume);
static const struct i2c_device_id usb251xb_id[] = {
{ "usb2422", 0 },
{ "usb2512b", 0 },
{ "usb2512bi", 0 },
{ "usb2513b", 0 },
{ "usb2513bi", 0 },
{ "usb2514b", 0 },
{ "usb2514bi", 0 },
{ "usb2517", 0 },
{ "usb2517i", 0 },
{ "usb2422" },
{ "usb2512b" },
{ "usb2512bi" },
{ "usb2513b" },
{ "usb2513bi" },
{ "usb2514b" },
{ "usb2514bi" },
{ "usb2517" },
{ "usb2517i" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, usb251xb_id);

View File

@ -390,7 +390,7 @@ static SIMPLE_DEV_PM_OPS(usb3503_platform_pm_ops, usb3503_platform_suspend,
usb3503_platform_resume);
static const struct i2c_device_id usb3503_id[] = {
{ USB3503_I2C_NAME, 0 },
{ USB3503_I2C_NAME },
{ }
};
MODULE_DEVICE_TABLE(i2c, usb3503_id);

View File

@ -135,7 +135,7 @@ static SIMPLE_DEV_PM_OPS(usb4604_i2c_pm_ops, usb4604_i2c_suspend,
usb4604_i2c_resume);
static const struct i2c_device_id usb4604_id[] = {
{ "usb4604", 0 },
{ "usb4604" },
{ }
};
MODULE_DEVICE_TABLE(i2c, usb4604_id);

View File

@ -531,4 +531,5 @@ static const struct file_operations yurex_fops = {
module_usb_driver(yurex_driver);
MODULE_DESCRIPTION("USB YUREX driver support");
MODULE_LICENSE("GPL");

View File

@ -419,4 +419,5 @@ static void __exit mon_exit(void)
module_init(mon_init);
module_exit(mon_exit);
MODULE_DESCRIPTION("USB Monitor");
MODULE_LICENSE("GPL");

View File

@ -191,7 +191,7 @@ static void otg_timer(struct timer_list *t)
spin_unlock_irqrestore(&musb->lock, flags);
}
static void da8xx_musb_try_idle(struct musb *musb, unsigned long timeout)
static void __maybe_unused da8xx_musb_try_idle(struct musb *musb, unsigned long timeout)
{
static unsigned long last_timer;
@ -220,6 +220,13 @@ static void da8xx_musb_try_idle(struct musb *musb, unsigned long timeout)
mod_timer(&musb->dev_timer, timeout);
}
static int da8xx_babble_recover(struct musb *musb)
{
dev_dbg(musb->controller, "resetting controller to recover from babble\n");
musb_writel(musb->ctrl_base, DA8XX_USB_CTRL_REG, DA8XX_SOFT_RESET_MASK);
return 0;
}
static irqreturn_t da8xx_musb_interrupt(int irq, void *hci)
{
struct musb *musb = hci;
@ -328,13 +335,6 @@ static int da8xx_musb_set_mode(struct musb *musb, u8 musb_mode)
struct da8xx_glue *glue = dev_get_drvdata(musb->controller->parent);
enum phy_mode phy_mode;
/*
* The PHY has some issues when it is forced in device or host mode.
* Unless the user request another mode, configure the PHY in OTG mode.
*/
if (!musb->is_initialized)
return phy_set_mode(glue->phy, PHY_MODE_USB_OTG);
switch (musb_mode) {
case MUSB_HOST: /* Force VBUS valid, ID = 0 */
phy_mode = PHY_MODE_USB_HOST;
@ -483,7 +483,11 @@ static const struct musb_platform_ops da8xx_ops = {
.disable = da8xx_musb_disable,
.set_mode = da8xx_musb_set_mode,
#ifndef CONFIG_USB_MUSB_HOST
.try_idle = da8xx_musb_try_idle,
#endif
.recover = da8xx_babble_recover,
.set_vbus = da8xx_musb_set_vbus,
};

View File

@ -190,6 +190,8 @@ static int mpfs_probe(struct platform_device *pdev)
pdata->config = &mpfs_musb_hdrc_config;
pdata->platform_ops = &mpfs_ops;
pdata->extvbus = device_property_read_bool(dev, "microchip,ext-vbus-drv");
pdata->mode = usb_get_dr_mode(dev);
if (pdata->mode == USB_DR_MODE_UNKNOWN) {
dev_info(dev, "No dr_mode property found, defaulting to otg\n");

View File

@ -189,4 +189,5 @@ static struct platform_driver am335x_control_driver = {
};
module_platform_driver(am335x_control_driver);
MODULE_DESCRIPTION("AM335x USB PHY Control Driver");
MODULE_LICENSE("GPL v2");

View File

@ -142,4 +142,5 @@ static struct platform_driver am335x_phy_driver = {
};
module_platform_driver(am335x_phy_driver);
MODULE_DESCRIPTION("AM335x USB PHY Driver");
MODULE_LICENSE("GPL v2");

View File

@ -423,6 +423,7 @@ static void uas_data_cmplt(struct urb *urb)
uas_log_cmd_state(cmnd, "data cmplt err", status);
/* error: no data transfered */
scsi_set_resid(cmnd, sdb->length);
set_host_byte(cmnd, DID_ERROR);
} else {
scsi_set_resid(cmnd, sdb->length - urb->actual_length);
}
@ -1232,9 +1233,8 @@ static void uas_disconnect(struct usb_interface *intf)
* hang on reboot when the device is still in uas mode. Note the reset is
* necessary as some devices won't revert to usb-storage mode without it.
*/
static void uas_shutdown(struct device *dev)
static void uas_shutdown(struct usb_interface *intf)
{
struct usb_interface *intf = to_usb_interface(dev);
struct usb_device *udev = interface_to_usbdev(intf);
struct Scsi_Host *shost = usb_get_intfdata(intf);
struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata;
@ -1257,7 +1257,7 @@ static struct usb_driver uas_driver = {
.suspend = uas_suspend,
.resume = uas_resume,
.reset_resume = uas_reset_resume,
.driver.shutdown = uas_shutdown,
.shutdown = uas_shutdown,
.id_table = uas_usb_ids,
};
@ -1287,6 +1287,7 @@ static void __exit uas_exit(void)
module_init(uas_init);
module_exit(uas_exit);
MODULE_DESCRIPTION("USB Attached SCSI driver");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(USB_STORAGE);
MODULE_AUTHOR(

View File

@ -68,9 +68,102 @@ MODULE_AUTHOR("Matthew Dharm <mdharm-usb@one-eyed-alien.net>");
MODULE_DESCRIPTION("USB Mass Storage driver for Linux");
MODULE_LICENSE("GPL");
static unsigned int delay_use = 1;
module_param(delay_use, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(delay_use, "seconds to delay before using a new device");
static unsigned int delay_use = 1 * MSEC_PER_SEC;
/**
* parse_delay_str - parse an unsigned decimal integer delay
* @str: String to parse.
* @ndecimals: Number of decimal to scale up.
* @suffix: Suffix string to parse.
* @val: Where to store the parsed value.
*
* Parse an unsigned decimal value in @str, optionally end with @suffix.
* Stores the parsed value in @val just as it is if @str ends with @suffix.
* Otherwise store the value scale up by 10^(@ndecimal).
*
* Returns 0 on success, a negative error code otherwise.
*/
static int parse_delay_str(const char *str, int ndecimals, const char *suffix,
unsigned int *val)
{
int n, n2, l;
char buf[16];
l = strlen(suffix);
n = strlen(str);
if (n > 0 && str[n - 1] == '\n')
--n;
if (n >= l && !strncmp(&str[n - l], suffix, l)) {
n -= l;
n2 = 0;
} else
n2 = ndecimals;
if (n + n2 > sizeof(buf) - 1)
return -EINVAL;
memcpy(buf, str, n);
while (n2-- > 0)
buf[n++] = '0';
buf[n] = 0;
return kstrtouint(buf, 10, val);
}
/**
* format_delay_ms - format an integer value into a delay string
* @val: The integer value to format, scaled by 10^(@ndecimals).
* @ndecimals: Number of decimal to scale down.
* @suffix: Suffix string to format.
* @str: Where to store the formatted string.
* @size: The size of buffer for @str.
*
* Format an integer value in @val scale down by 10^(@ndecimals) without @suffix
* if @val is divisible by 10^(@ndecimals).
* Otherwise format a value in @val just as it is with @suffix
*
* Returns the number of characters written into @str.
*/
static int format_delay_ms(unsigned int val, int ndecimals, const char *suffix,
char *str, int size)
{
u64 delay_ms = val;
unsigned int rem = do_div(delay_ms, int_pow(10, ndecimals));
int ret;
if (rem)
ret = scnprintf(str, size, "%u%s\n", val, suffix);
else
ret = scnprintf(str, size, "%u\n", (unsigned int)delay_ms);
return ret;
}
static int delay_use_set(const char *s, const struct kernel_param *kp)
{
unsigned int delay_ms;
int ret;
ret = parse_delay_str(skip_spaces(s), 3, "ms", &delay_ms);
if (ret < 0)
return ret;
*((unsigned int *)kp->arg) = delay_ms;
return 0;
}
static int delay_use_get(char *s, const struct kernel_param *kp)
{
unsigned int delay_ms = *((unsigned int *)kp->arg);
return format_delay_ms(delay_ms, 3, "ms", s, PAGE_SIZE);
}
static const struct kernel_param_ops delay_use_ops = {
.set = delay_use_set,
.get = delay_use_get,
};
module_param_cb(delay_use, &delay_use_ops, &delay_use, 0644);
MODULE_PARM_DESC(delay_use, "time to delay before using a new device");
static char quirks[128];
module_param_string(quirks, quirks, sizeof(quirks), S_IRUGO | S_IWUSR);
@ -1064,7 +1157,7 @@ int usb_stor_probe2(struct us_data *us)
if (delay_use > 0)
dev_dbg(dev, "waiting for device to settle before scanning\n");
queue_delayed_work(system_freezable_wq, &us->scan_dwork,
delay_use * HZ);
msecs_to_jiffies(delay_use));
return 0;
/* We come here if there are any problems */

View File

@ -746,7 +746,7 @@ int dp_altmode_probe(struct typec_altmode *alt)
dp->alt = alt;
alt->desc = "DisplayPort";
alt->ops = &dp_altmode_ops;
typec_altmode_set_ops(alt, &dp_altmode_ops);
if (plug) {
plug->desc = "Displayport";

View File

@ -1566,7 +1566,7 @@ static void anx7411_i2c_remove(struct i2c_client *client)
}
static const struct i2c_device_id anx7411_id[] = {
{"anx7411", 0},
{ "anx7411" },
{}
};

View File

@ -467,6 +467,22 @@ static const struct attribute_group *typec_altmode_groups[] = {
NULL
};
/**
* typec_altmode_set_ops - Set ops for altmode
* @adev: Handle to the alternate mode
* @ops: Ops for the alternate mode
*
* After setting ops, attribute visiblity needs to be refreshed if the alternate
* mode can be activated.
*/
void typec_altmode_set_ops(struct typec_altmode *adev,
const struct typec_altmode_ops *ops)
{
adev->ops = ops;
sysfs_update_group(&adev->dev.kobj, &typec_altmode_group);
}
EXPORT_SYMBOL_GPL(typec_altmode_set_ops);
static int altmode_id_get(struct device *dev)
{
struct ida *ids;
@ -2317,7 +2333,7 @@ void typec_port_register_altmodes(struct typec_port *port,
continue;
}
alt->ops = ops;
typec_altmode_set_ops(alt, ops);
typec_altmode_set_drvdata(alt, drvdata);
altmodes[index] = alt;
index++;

View File

@ -66,6 +66,9 @@ static int gpio_sbu_mux_set(struct typec_mux_dev *mux,
{
struct gpio_sbu_mux *sbu_mux = typec_mux_get_drvdata(mux);
if (!sbu_mux->enable_gpio)
return -EOPNOTSUPP;
mutex_lock(&sbu_mux->lock);
switch (state->mode) {
@ -102,7 +105,8 @@ static int gpio_sbu_mux_probe(struct platform_device *pdev)
mutex_init(&sbu_mux->lock);
sbu_mux->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
sbu_mux->enable_gpio = devm_gpiod_get_optional(dev, "enable",
GPIOD_OUT_LOW);
if (IS_ERR(sbu_mux->enable_gpio))
return dev_err_probe(dev, PTR_ERR(sbu_mux->enable_gpio),
"unable to acquire enable gpio\n");

View File

@ -69,6 +69,7 @@ struct nb7vpq904m {
bool swap_data_lanes;
struct typec_switch *typec_switch;
struct typec_mux *typec_mux;
struct mutex lock; /* protect non-concurrent retimer & switch */
@ -275,6 +276,7 @@ static int nb7vpq904m_sw_set(struct typec_switch_dev *sw, enum typec_orientation
static int nb7vpq904m_retimer_set(struct typec_retimer *retimer, struct typec_retimer_state *state)
{
struct nb7vpq904m *nb7 = typec_retimer_get_drvdata(retimer);
struct typec_mux_state mux_state;
int ret = 0;
mutex_lock(&nb7->lock);
@ -292,7 +294,14 @@ static int nb7vpq904m_retimer_set(struct typec_retimer *retimer, struct typec_re
mutex_unlock(&nb7->lock);
return ret;
if (ret)
return ret;
mux_state.alt = state->alt;
mux_state.data = state->data;
mux_state.mode = state->mode;
return typec_mux_set(nb7->typec_mux, &mux_state);
}
static const struct regmap_config nb7_regmap = {
@ -321,46 +330,48 @@ static int nb7vpq904m_parse_data_lanes_mapping(struct nb7vpq904m *nb7)
ep = of_graph_get_endpoint_by_regs(nb7->client->dev.of_node, 1, 0);
if (ep) {
ret = of_property_count_u32_elems(ep, "data-lanes");
if (ret == -EINVAL)
/* Property isn't here, consider default mapping */
goto out_done;
if (ret < 0)
goto out_error;
if (!ep)
return 0;
if (ret != DATA_LANES_COUNT) {
dev_err(&nb7->client->dev, "expected 4 data lanes\n");
ret = -EINVAL;
goto out_error;
}
ret = of_property_read_u32_array(ep, "data-lanes", data_lanes, DATA_LANES_COUNT);
if (ret)
goto out_error;
ret = of_property_count_u32_elems(ep, "data-lanes");
if (ret == -EINVAL)
/* Property isn't here, consider default mapping */
goto out_done;
if (ret < 0)
goto out_error;
for (i = 0; i < ARRAY_SIZE(supported_data_lane_mapping); i++) {
for (j = 0; j < DATA_LANES_COUNT; j++) {
if (data_lanes[j] != supported_data_lane_mapping[i][j])
break;
}
if (ret != DATA_LANES_COUNT) {
dev_err(&nb7->client->dev, "expected 4 data lanes\n");
ret = -EINVAL;
goto out_error;
}
if (j == DATA_LANES_COUNT)
ret = of_property_read_u32_array(ep, "data-lanes", data_lanes, DATA_LANES_COUNT);
if (ret)
goto out_error;
for (i = 0; i < ARRAY_SIZE(supported_data_lane_mapping); i++) {
for (j = 0; j < DATA_LANES_COUNT; j++) {
if (data_lanes[j] != supported_data_lane_mapping[i][j])
break;
}
switch (i) {
case NORMAL_LANE_MAPPING:
if (j == DATA_LANES_COUNT)
break;
case INVERT_LANE_MAPPING:
nb7->swap_data_lanes = true;
dev_info(&nb7->client->dev, "using inverted data lanes mapping\n");
break;
default:
dev_err(&nb7->client->dev, "invalid data lanes mapping\n");
ret = -EINVAL;
goto out_error;
}
}
switch (i) {
case NORMAL_LANE_MAPPING:
break;
case INVERT_LANE_MAPPING:
nb7->swap_data_lanes = true;
dev_info(&nb7->client->dev, "using inverted data lanes mapping\n");
break;
default:
dev_err(&nb7->client->dev, "invalid data lanes mapping\n");
ret = -EINVAL;
goto out_error;
}
out_done:
@ -411,9 +422,16 @@ static int nb7vpq904m_probe(struct i2c_client *client)
return dev_err_probe(dev, PTR_ERR(nb7->typec_switch),
"failed to acquire orientation-switch\n");
nb7->typec_mux = fwnode_typec_mux_get(dev->fwnode);
if (IS_ERR(nb7->typec_mux)) {
ret = dev_err_probe(dev, PTR_ERR(nb7->typec_mux),
"Failed to acquire mode-switch\n");
goto err_switch_put;
}
ret = nb7vpq904m_parse_data_lanes_mapping(nb7);
if (ret)
return ret;
goto err_mux_put;
ret = regulator_enable(nb7->vcc_supply);
if (ret)
@ -456,6 +474,12 @@ err_disable_gpio:
gpiod_set_value(nb7->enable_gpio, 0);
regulator_disable(nb7->vcc_supply);
err_mux_put:
typec_mux_put(nb7->typec_mux);
err_switch_put:
typec_switch_put(nb7->typec_switch);
return ret;
}
@ -469,6 +493,9 @@ static void nb7vpq904m_remove(struct i2c_client *client)
gpiod_set_value(nb7->enable_gpio, 0);
regulator_disable(nb7->vcc_supply);
typec_mux_put(nb7->typec_mux);
typec_switch_put(nb7->typec_switch);
}
static const struct i2c_device_id nb7vpq904m_table[] = {

View File

@ -67,6 +67,7 @@ struct ptn36502 {
struct typec_retimer *retimer;
struct typec_switch *typec_switch;
struct typec_mux *typec_mux;
struct mutex lock; /* protect non-concurrent retimer & switch */
@ -235,6 +236,7 @@ static int ptn36502_sw_set(struct typec_switch_dev *sw, enum typec_orientation o
static int ptn36502_retimer_set(struct typec_retimer *retimer, struct typec_retimer_state *state)
{
struct ptn36502 *ptn = typec_retimer_get_drvdata(retimer);
struct typec_mux_state mux_state;
int ret = 0;
mutex_lock(&ptn->lock);
@ -252,7 +254,14 @@ static int ptn36502_retimer_set(struct typec_retimer *retimer, struct typec_reti
mutex_unlock(&ptn->lock);
return ret;
if (ret)
return ret;
mux_state.alt = state->alt;
mux_state.data = state->data;
mux_state.mode = state->mode;
return typec_mux_set(ptn->typec_mux, &mux_state);
}
static int ptn36502_detect(struct ptn36502 *ptn)
@ -321,9 +330,18 @@ static int ptn36502_probe(struct i2c_client *client)
return dev_err_probe(dev, PTR_ERR(ptn->typec_switch),
"Failed to acquire orientation-switch\n");
ptn->typec_mux = fwnode_typec_mux_get(dev->fwnode);
if (IS_ERR(ptn->typec_mux)) {
ret = dev_err_probe(dev, PTR_ERR(ptn->typec_mux),
"Failed to acquire mode-switch\n");
goto err_switch_put;
}
ret = regulator_enable(ptn->vdd18_supply);
if (ret)
return dev_err_probe(dev, ret, "Failed to enable vdd18\n");
if (ret) {
ret = dev_err_probe(dev, ret, "Failed to enable vdd18\n");
goto err_mux_put;
}
ret = ptn36502_detect(ptn);
if (ret)
@ -363,6 +381,12 @@ err_switch_unregister:
err_disable_regulator:
regulator_disable(ptn->vdd18_supply);
err_mux_put:
typec_mux_put(ptn->typec_mux);
err_switch_put:
typec_switch_put(ptn->typec_switch);
return ret;
}
@ -374,6 +398,9 @@ static void ptn36502_remove(struct i2c_client *client)
typec_switch_unregister(ptn->sw);
regulator_disable(ptn->vdd18_supply);
typec_mux_put(ptn->typec_mux);
typec_switch_put(ptn->typec_switch);
}
static const struct i2c_device_id ptn36502_table[] = {

View File

@ -1820,8 +1820,8 @@ static const struct of_device_id fusb302_dt_match[] __maybe_unused = {
MODULE_DEVICE_TABLE(of, fusb302_dt_match);
static const struct i2c_device_id fusb302_i2c_device_id[] = {
{"typec_fusb302", 0},
{},
{ "typec_fusb302" },
{}
};
MODULE_DEVICE_TABLE(i2c, fusb302_i2c_device_id);

View File

@ -67,6 +67,18 @@ static int tcpci_write16(struct tcpci *tcpci, unsigned int reg, u16 val)
return regmap_raw_write(tcpci->regmap, reg, &val, sizeof(u16));
}
static bool tcpci_check_std_output_cap(struct regmap *regmap, u8 mask)
{
unsigned int reg;
int ret;
ret = regmap_read(regmap, TCPC_STD_OUTPUT_CAP, &reg);
if (ret < 0)
return ret;
return (reg & mask) == mask;
}
static int tcpci_set_cc(struct tcpc_dev *tcpc, enum typec_cc_status cc)
{
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
@ -301,6 +313,28 @@ static int tcpci_set_polarity(struct tcpc_dev *tcpc,
TCPC_TCPC_CTRL_ORIENTATION : 0);
}
static int tcpci_set_orientation(struct tcpc_dev *tcpc,
enum typec_orientation orientation)
{
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
unsigned int reg;
switch (orientation) {
case TYPEC_ORIENTATION_NONE:
/* We can't put a single output into high impedance */
fallthrough;
case TYPEC_ORIENTATION_NORMAL:
reg = TCPC_CONFIG_STD_OUTPUT_ORIENTATION_NORMAL;
break;
case TYPEC_ORIENTATION_REVERSE:
reg = TCPC_CONFIG_STD_OUTPUT_ORIENTATION_FLIPPED;
break;
}
return regmap_update_bits(tcpci->regmap, TCPC_CONFIG_STD_OUTPUT,
TCPC_CONFIG_STD_OUTPUT_ORIENTATION_MASK, reg);
}
static void tcpci_set_partner_usb_comm_capable(struct tcpc_dev *tcpc, bool capable)
{
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
@ -830,6 +864,9 @@ struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data)
if (tcpci->data->vbus_vsafe0v)
tcpci->tcpc.is_vbus_vsafe0v = tcpci_is_vbus_vsafe0v;
if (tcpci->data->set_orientation)
tcpci->tcpc.set_orientation = tcpci_set_orientation;
err = tcpci_parse_config(tcpci);
if (err < 0)
return ERR_PTR(err);
@ -873,6 +910,13 @@ static int tcpci_probe(struct i2c_client *client)
if (err < 0)
return err;
err = tcpci_check_std_output_cap(chip->data.regmap,
TCPC_STD_OUTPUT_CAP_ORIENTATION);
if (err < 0)
return err;
chip->data.set_orientation = err;
chip->tcpci = tcpci_register_port(&client->dev, &chip->data);
if (IS_ERR(chip->tcpci))
return PTR_ERR(chip->tcpci);
@ -903,7 +947,7 @@ static void tcpci_remove(struct i2c_client *client)
}
static const struct i2c_device_id tcpci_id[] = {
{ "tcpci", 0 },
{ "tcpci" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tcpci_id);

View File

@ -538,7 +538,7 @@ static void max_tcpci_remove(struct i2c_client *client)
}
static const struct i2c_device_id max_tcpci_id[] = {
{ "maxtcpc", 0 },
{ "maxtcpc" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max_tcpci_id);

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