USB/Thunderbolt patches for 5.12-rc1

Here is the big set of USB and Thunderbolt driver changes for 5.12-rc1.
 
 It's been an active set of development in these subsystems for the past
 few months:
 	- loads of typec features added for new hardware
 	- xhci features and bugfixes
 	- dwc3 features added for more hardware support
 	- dwc2 fixes and new hardware support
 	- cdns3 driver updates for more hardware support
 	- gadget driver cleanups and minor fixes
 	- usb-serial fixes, new driver, and more devices supported
 	- thunderbolt feature additions for new hardware
 	- lots of other tiny fixups and additions
 The chrome driver changes are in here as well, as they depended on some
 of the typec changes, and the maintainer acked them.
 
 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-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCYCqf5Q8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ykZhQCgoDMMu9YZZI8chWVlIbDYiHxG520An1SlbwDb
 hRh87fsQFkwWk06Bp9AQ
 =LmS1
 -----END PGP SIGNATURE-----

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

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

  It's been an active set of development in these subsystems for the
  past few months:

   - loads of typec features added for new hardware

   - xhci features and bugfixes

   - dwc3 features added for more hardware support

   - dwc2 fixes and new hardware support

   - cdns3 driver updates for more hardware support

   - gadget driver cleanups and minor fixes

   - usb-serial fixes, new driver, and more devices supported

   - thunderbolt feature additions for new hardware

   - lots of other tiny fixups and additions

  The chrome driver changes are in here as well, as they depended on
  some of the typec changes, and the maintainer acked them.

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

* tag 'usb-5.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (300 commits)
  dt-bindings: usb: mediatek: musb: add mt8516 compatbile
  dt-bindings: usb: mtk-xhci: add compatible for mt2701 and mt7623
  dt-bindings: usb: mtk-xhci: add optional assigned clock properties
  Documentation: connector: Update the description of sink-vdos
  usb: misc: usb3503: Fix logic in usb3503_init()
  dt-bindings: usb: usb-device: fix typo in required properties
  usb: Replace lkml.org links with lore
  dt-bindings: usb: dwc3: add description for rk3328
  dt-bindings: usb: convert rockchip,dwc3.txt to yaml
  usb: quirks: add quirk to start video capture on ELMO L-12F document camera reliable
  USB: quirks: sort quirk entries
  USB: serial: drop bogus to_usb_serial_port() checks
  USB: serial: make remove callback return void
  USB: serial: drop if with an always false condition
  usb: gadget: Assign boolean values to a bool variable
  usb: typec: tcpm: Get Sink VDO from fwnode
  dt-bindings: connector: Add SVDM VDO properties
  usb: typec: displayport: Fill the negotiated SVDM Version in the header
  usb: typec: ucsi: Determine common SVDM Version
  usb: typec: tcpm: Determine common SVDM Version
  ...
This commit is contained in:
Linus Torvalds 2021-02-20 21:32:37 -08:00
commit 780607b973
232 changed files with 18532 additions and 3870 deletions

View File

@ -49,6 +49,15 @@ Description: Holds a comma separated list of device unique_ids that
If a device is authorized automatically during boot its
boot attribute is set to 1.
What: /sys/bus/thunderbolt/devices/.../domainX/deauthorization
Date: May 2021
KernelVersion: 5.12
Contact: Mika Westerberg <mika.westerberg@linux.intel.com>
Description: This attribute tells whether the system supports
de-authorization of devices. Value of 1 means user can
de-authorize PCIe tunnel by writing 0 to authorized
attribute under each device.
What: /sys/bus/thunderbolt/devices/.../domainX/iommu_dma_protection
Date: Mar 2019
KernelVersion: 4.21
@ -76,6 +85,8 @@ Description: This attribute holds current Thunderbolt security level
usbonly Automatically tunnel USB controller of the
connected Thunderbolt dock (and Display Port). All
PCIe links downstream of the dock are removed.
nopcie USB4 system where PCIe tunneling is disabled from
the BIOS.
======= ==================================================
What: /sys/bus/thunderbolt/devices/.../authorized
@ -84,22 +95,25 @@ KernelVersion: 4.13
Contact: thunderbolt-software@lists.01.org
Description: This attribute is used to authorize Thunderbolt devices
after they have been connected. If the device is not
authorized, no devices such as PCIe and Display port are
available to the system.
authorized, no PCIe devices are available to the system.
Contents of this attribute will be 0 when the device is not
yet authorized.
Possible values are supported:
== ===========================================
== ===================================================
0 The device will be de-authorized (only supported if
deauthorization attribute under domain contains 1)
1 The device will be authorized and connected
== ===========================================
== ===================================================
When key attribute contains 32 byte hex string the possible
values are:
== ========================================================
0 The device will be de-authorized (only supported if
deauthorization attribute under domain contains 1)
1 The 32 byte hex string is added to the device NVM and
the device is authorized.
2 Send a challenge based on the 32 byte hex string. If the

View File

@ -105,7 +105,25 @@ Date: April 2017
Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Description:
Revision number of the supported USB Power Delivery
specification, or 0 when USB Power Delivery is not supported.
specification, or 0.0 when USB Power Delivery is not supported.
Example values:
- "2.0": USB Power Delivery Release 2.0
- "3.0": USB Power Delivery Release 3.0
- "3.1": USB Power Delivery Release 3.1
What: /sys/class/typec/<port>-{partner|cable}/usb_power_delivery_revision
Date: January 2021
Contact: Benson Leung <bleung@chromium.org>
Description:
Revision number of the supported USB Power Delivery
specification of the port partner or cable, or 0.0 when USB
Power Delivery is not supported.
Example values:
- "2.0": USB Power Delivery Release 2.0
- "3.0": USB Power Delivery Release 3.0
- "3.1": USB Power Delivery Release 3.1
What: /sys/class/typec/<port>/usb_typec_revision
Date: April 2017

View File

@ -47,6 +47,9 @@ be DMA masters and thus read contents of the host memory without CPU and OS
knowing about it. There are ways to prevent this by setting up an IOMMU but
it is not always available for various reasons.
Some USB4 systems have a BIOS setting to disable PCIe tunneling. This is
treated as another security level (nopcie).
The security levels are as follows:
none
@ -77,6 +80,10 @@ The security levels are as follows:
Display Port in a dock. All PCIe links downstream of the dock are
removed.
nopcie
PCIe tunneling is disabled/forbidden from the BIOS. Available in some
USB4 systems.
The current security level can be read from
``/sys/bus/thunderbolt/devices/domainX/security`` where ``domainX`` is
the Thunderbolt domain the host controller manages. There is typically
@ -153,6 +160,22 @@ If the user still wants to connect the device they can either approve
the device without a key or write a new key and write 1 to the
``authorized`` file to get the new key stored on the device NVM.
De-authorizing devices
----------------------
It is possible to de-authorize devices by writing ``0`` to their
``authorized`` attribute. This requires support from the connection
manager implementation and can be checked by reading domain
``deauthorization`` attribute. If it reads ``1`` then the feature is
supported.
When a device is de-authorized the PCIe tunnel from the parent device
PCIe downstream (or root) port to the device PCIe upstream port is torn
down. This is essentially the same thing as PCIe hot-remove and the PCIe
toplogy in question will not be accessible anymore until the device is
authorized again. If there is storage such as NVMe or similar involved,
there is a risk for data loss if the filesystem on that storage is not
properly shut down. You have been warned!
DMA protection utilizing IOMMU
------------------------------
Recent systems from 2018 and forward with Thunderbolt ports may natively

View File

@ -137,6 +137,18 @@ properties:
maxItems: 7
$ref: /schemas/types.yaml#/definitions/uint32-array
sink-vdos:
description: An array of u32 with each entry, a Vendor Defined Message Object (VDO),
providing additional information corresponding to the product, the detailed bit
definitions and the order of each VDO can be found in
"USB Power Delivery Specification Revision 3.0, Version 2.0 + ECNs 2020-12-10"
chapter 6.4.4.3.1 Discover Identity. User can specify the VDO array via
VDO_IDH/_CERT/_PRODUCT/_UFP/_DFP/_PCABLE/_ACABLE(1/2)/_VPD() defined in
dt-bindings/usb/pd.h.
minItems: 3
maxItems: 6
$ref: /schemas/types.yaml#/definitions/uint32-array
op-sink-microwatt:
description: Sink required operating power in microwatt, if source can't
offer the power, Capability Mismatch is set. Required for power sink and

View File

@ -4,7 +4,7 @@ Generic Bluetooth controller over USB (btusb driver)
Required properties:
- compatible : should comply with the format "usbVID,PID" specified in
Documentation/devicetree/bindings/usb/usb-device.txt
Documentation/devicetree/bindings/usb/usb-device.yaml
At the time of writing, the only OF supported devices
(more may be added later) are:

View File

@ -79,7 +79,9 @@ properties:
patternProperties:
"^usb@[0-9a-f]+$":
type: object
oneOf:
- $ref: dwc2.yaml#
- $ref: snps,dwc3.yaml#
additionalProperties: false
@ -229,6 +231,6 @@ examples:
interrupts = <30>;
dr_mode = "host";
snps,dis_u2_susphy_quirk;
snps,quirk-frame-length-adjustment;
snps,quirk-frame-length-adjustment = <0x20>;
};
};

View File

@ -31,13 +31,13 @@ See: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
Sub-nodes:
The dwc3 core should be added as subnode to ST DWC3 glue as shown in the
example below. The DT binding details of dwc3 can be found in:
Documentation/devicetree/bindings/usb/dwc3.txt
Documentation/devicetree/bindings/usb/snps,dwc3.yaml
NB: The dr_mode property described in [1] is NOT optional for this driver, as the default value
is "otg", which isn't supported by this SoC. Valid dr_mode values for dwc3-st are either "host"
or "device".
[1] Documentation/devicetree/bindings/usb/generic.txt
[1] Documentation/devicetree/bindings/usb/usb-drd.yaml
Example:

View File

@ -19,7 +19,7 @@ Example device node:
#address-cells = <0x2>;
#size-cells = <0x1>;
compatible = "xlnx,zynqmp-dwc3";
clock-names = "bus_clk" "ref_clk";
clock-names = "bus_clk", "ref_clk";
clocks = <&clk125>, <&clk125>;
ranges;

View File

@ -1,128 +0,0 @@
synopsys DWC3 CORE
DWC3- USB3 CONTROLLER. Complies to the generic USB binding properties
as described in 'usb/generic.txt'
Required properties:
- compatible: must be "snps,dwc3"
- reg : Address and length of the register set for the device
- interrupts: Interrupts used by the dwc3 controller.
- clock-names: list of clock names. Ideally should be "ref",
"bus_early", "suspend" but may be less or more.
- clocks: list of phandle and clock specifier pairs corresponding to
entries in the clock-names property.
Exception for clocks:
clocks are optional if the parent node (i.e. glue-layer) is compatible to
one of the following:
"cavium,octeon-7130-usb-uctl"
"qcom,dwc3"
"samsung,exynos5250-dwusb3"
"samsung,exynos5433-dwusb3"
"samsung,exynos7-dwusb3"
"sprd,sc9860-dwc3"
"st,stih407-dwc3"
"ti,am437x-dwc3"
"ti,dwc3"
"ti,keystone-dwc3"
"rockchip,rk3399-dwc3"
"xlnx,zynqmp-dwc3"
Optional properties:
- usb-phy : array of phandle for the PHY device. The first element
in the array is expected to be a handle to the USB2/HS PHY and
the second element is expected to be a handle to the USB3/SS PHY
- phys: from the *Generic PHY* bindings
- phy-names: from the *Generic PHY* bindings; supported names are "usb2-phy"
or "usb3-phy".
- resets: set of phandle and reset specifier pairs
- snps,usb2-lpm-disable: indicate if we don't want to enable USB2 HW LPM
- snps,usb3_lpm_capable: determines if platform is USB3 LPM capable
- snps,dis-start-transfer-quirk: when set, disable isoc START TRANSFER command
failure SW work-around for DWC_usb31 version 1.70a-ea06
and prior.
- snps,disable_scramble_quirk: true when SW should disable data scrambling.
Only really useful for FPGA builds.
- snps,has-lpm-erratum: true when DWC3 was configured with LPM Erratum enabled
- snps,lpm-nyet-threshold: LPM NYET threshold
- snps,u2exit_lfps_quirk: set if we want to enable u2exit lfps quirk
- snps,u2ss_inp3_quirk: set if we enable P3 OK for U2/SS Inactive quirk
- snps,req_p1p2p3_quirk: when set, the core will always request for
P1/P2/P3 transition sequence.
- snps,del_p1p2p3_quirk: when set core will delay P1/P2/P3 until a certain
amount of 8B10B errors occur.
- snps,del_phy_power_chg_quirk: when set core will delay PHY power change
from P0 to P1/P2/P3.
- snps,lfps_filter_quirk: when set core will filter LFPS reception.
- snps,rx_detect_poll_quirk: when set core will disable a 400us delay to start
Polling LFPS after RX.Detect.
- snps,tx_de_emphasis_quirk: when set core will set Tx de-emphasis value.
- snps,tx_de_emphasis: the value driven to the PHY is controlled by the
LTSSM during USB3 Compliance mode.
- snps,dis_u3_susphy_quirk: when set core will disable USB3 suspend phy.
- snps,dis_u2_susphy_quirk: when set core will disable USB2 suspend phy.
- snps,dis_enblslpm_quirk: when set clears the enblslpm in GUSB2PHYCFG,
disabling the suspend signal to the PHY.
- snps,dis-u1-entry-quirk: set if link entering into U1 needs to be disabled.
- snps,dis-u2-entry-quirk: set if link entering into U2 needs to be disabled.
- snps,dis_rxdet_inp3_quirk: when set core will disable receiver detection
in PHY P3 power state.
- snps,dis-u2-freeclk-exists-quirk: when set, clear the u2_freeclk_exists
in GUSB2PHYCFG, specify that USB2 PHY doesn't provide
a free-running PHY clock.
- snps,dis-del-phy-power-chg-quirk: when set core will change PHY power
from P0 to P1/P2/P3 without delay.
- snps,dis-tx-ipgap-linecheck-quirk: when set, disable u2mac linestate check
during HS transmit.
- snps,parkmode-disable-ss-quirk: when set, all SuperSpeed bus instances in
park mode are disabled.
- snps,dis_metastability_quirk: when set, disable metastability workaround.
CAUTION: use only if you are absolutely sure of it.
- snps,dis-split-quirk: when set, change the way URBs are handled by the
driver. Needed to avoid -EPROTO errors with usbhid
on some devices (Hikey 970).
- snps,is-utmi-l1-suspend: true when DWC3 asserts output signal
utmi_l1_suspend_n, false when asserts utmi_sleep_n
- snps,hird-threshold: HIRD threshold
- snps,hsphy_interface: High-Speed PHY interface selection between "utmi" for
UTMI+ and "ulpi" for ULPI when the DWC_USB3_HSPHY_INTERFACE has value 3.
- snps,quirk-frame-length-adjustment: Value for GFLADJ_30MHZ field of GFLADJ
register for post-silicon frame length adjustment when the
fladj_30mhz_sdbnd signal is invalid or incorrect.
- snps,rx-thr-num-pkt-prd: periodic ESS RX packet threshold count - host mode
only. Set this and rx-max-burst-prd to a valid,
non-zero value 1-16 (DWC_usb31 programming guide
section 1.2.4) to enable periodic ESS RX threshold.
- snps,rx-max-burst-prd: max periodic ESS RX burst size - host mode only. Set
this and rx-thr-num-pkt-prd to a valid, non-zero value
1-16 (DWC_usb31 programming guide section 1.2.4) to
enable periodic ESS RX threshold.
- snps,tx-thr-num-pkt-prd: periodic ESS TX packet threshold count - host mode
only. Set this and tx-max-burst-prd to a valid,
non-zero value 1-16 (DWC_usb31 programming guide
section 1.2.3) to enable periodic ESS TX threshold.
- snps,tx-max-burst-prd: max periodic ESS TX burst size - host mode only. Set
this and tx-thr-num-pkt-prd to a valid, non-zero value
1-16 (DWC_usb31 programming guide section 1.2.3) to
enable periodic ESS TX threshold.
- <DEPRECATED> tx-fifo-resize: determines if the FIFO *has* to be reallocated.
- snps,incr-burst-type-adjustment: Value for INCR burst type of GSBUSCFG0
register, undefined length INCR burst type enable and INCRx type.
When just one value, which means INCRX burst mode enabled. When
more than one value, which means undefined length INCR burst type
enabled. The values can be 1, 4, 8, 16, 32, 64, 128 and 256.
- in addition all properties from usb-xhci.txt from the current directory are
supported as well
This is usually a subnode to DWC3 glue to which it is connected.
dwc3@4a030000 {
compatible = "snps,dwc3";
reg = <0x4a030000 0xcfff>;
interrupts = <0 92 4>
usb-phy = <&usb2_phy>, <&usb3,phy>;
snps,incr-burst-type-adjustment = <1>, <4>, <8>, <16>;
};

View File

@ -93,7 +93,7 @@ Sub-nodes:
The dwc3 core should be added as subnode to Exynos dwc3 glue.
- dwc3 :
The binding details of dwc3 can be found in:
Documentation/devicetree/bindings/usb/dwc3.txt
Documentation/devicetree/bindings/usb/snps,dwc3.yaml
Example:
usb@12000000 {

View File

@ -0,0 +1,105 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
# Copyright (c) 2020 NXP
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/fsl,imx8mp-dwc3.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NXP iMX8MP Soc USB Controller
maintainers:
- Li Jun <jun.li@nxp.com>
properties:
compatible:
const: fsl,imx8mp-dwc3
reg:
maxItems: 1
description: Address and length of the register set for the wrapper of
dwc3 core on the SOC.
"#address-cells":
enum: [ 1, 2 ]
"#size-cells":
enum: [ 1, 2 ]
dma-ranges:
description:
See section 2.3.9 of the DeviceTree Specification.
ranges: true
interrupts:
maxItems: 1
description: The interrupt that is asserted when a wakeup event is
received.
clocks:
description:
A list of phandle and clock-specifier pairs for the clocks
listed in clock-names.
items:
- description: system hsio root clock.
- description: suspend clock, used for usb wakeup logic.
clock-names:
items:
- const: hsio
- const: suspend
# Required child node:
patternProperties:
"^dwc3@[0-9a-f]+$":
type: object
description:
A child node must exist to represent the core DWC3 IP block
The content of the node is defined in dwc3.txt.
required:
- compatible
- reg
- "#address-cells"
- "#size-cells"
- dma-ranges
- ranges
- clocks
- clock-names
- interrupts
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/imx8mp-clock.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
usb3_0: usb@32f10100 {
compatible = "fsl,imx8mp-dwc3";
reg = <0x32f10100 0x8>;
clocks = <&clk IMX8MP_CLK_HSIO_ROOT>,
<&clk IMX8MP_CLK_USB_ROOT>;
clock-names = "hsio", "suspend";
interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
#size-cells = <1>;
dma-ranges = <0x40000000 0x40000000 0xc0000000>;
ranges;
dwc3@38100000 {
compatible = "snps,dwc3";
reg = <0x38100000 0x10000>;
clocks = <&clk IMX8MP_CLK_HSIO_AXI>,
<&clk IMX8MP_CLK_USB_CORE_REF>,
<&clk IMX8MP_CLK_USB_ROOT>;
clock-names = "bus_early", "ref", "suspend";
assigned-clocks = <&clk IMX8MP_CLK_HSIO_AXI>;
assigned-clock-parents = <&clk IMX8MP_SYS_PLL2_500M>;
assigned-clock-rates = <500000000>;
interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>;
phys = <&usb3_phy0>, <&usb3_phy0>;
phy-names = "usb2-phy", "usb3-phy";
snps,dis-u2-freeclk-exists-quirk;
};
};

View File

@ -24,8 +24,53 @@ allOf:
properties:
compatible:
contains:
const: generic-ehci
oneOf:
- items:
- enum:
- allwinner,sun4i-a10-ehci
- allwinner,sun50i-a64-ehci
- allwinner,sun50i-h6-ehci
- allwinner,sun5i-a13-ehci
- allwinner,sun6i-a31-ehci
- allwinner,sun7i-a20-ehci
- allwinner,sun8i-a23-ehci
- allwinner,sun8i-h3-ehci
- allwinner,sun8i-r40-ehci
- allwinner,sun9i-a80-ehci
- aspeed,ast2400-ehci
- aspeed,ast2500-ehci
- aspeed,ast2600-ehci
- brcm,bcm3384-ehci
- brcm,bcm63268-ehci
- brcm,bcm6328-ehci
- brcm,bcm6358-ehci
- brcm,bcm6362-ehci
- brcm,bcm6368-ehci
- brcm,bcm7125-ehci
- brcm,bcm7346-ehci
- brcm,bcm7358-ehci
- brcm,bcm7360-ehci
- brcm,bcm7362-ehci
- brcm,bcm7420-ehci
- brcm,bcm7425-ehci
- brcm,bcm7435-ehci
- ibm,476gtr-ehci
- nxp,lpc1850-ehci
- qca,ar7100-ehci
- snps,hsdk-v1.0-ehci
- socionext,uniphier-ehci
- const: generic-ehci
- items:
- enum:
- cavium,octeon-6335-ehci
- ibm,usb-ehci-440epx
- ibm,usb-ehci-460ex
- nintendo,hollywood-usb-ehci
- st,spear600-ehci
- const: usb-ehci
- enum:
- generic-ehci
- usb-ehci
reg:
minItems: 1
@ -101,7 +146,7 @@ additionalProperties: false
examples:
- |
usb@e0000300 {
compatible = "ibm,usb-ehci-440epx", "generic-ehci";
compatible = "ibm,usb-ehci-440epx", "usb-ehci";
interrupt-parent = <&UIC0>;
interrupts = <0x1a 4>;
reg = <0xe0000300 90>, <0xe0000390 70>;

View File

@ -14,8 +14,38 @@ maintainers:
properties:
compatible:
contains:
const: generic-ohci
oneOf:
- items:
- enum:
- allwinner,sun4i-a10-ohci
- allwinner,sun50i-a64-ohci
- allwinner,sun50i-h6-ohci
- allwinner,sun5i-a13-ohci
- allwinner,sun6i-a31-ohci
- allwinner,sun7i-a20-ohci
- allwinner,sun8i-a23-ohci
- allwinner,sun8i-h3-ohci
- allwinner,sun8i-r40-ohci
- allwinner,sun9i-a80-ohci
- brcm,bcm3384-ohci
- brcm,bcm63268-ohci
- brcm,bcm6328-ohci
- brcm,bcm6358-ohci
- brcm,bcm6362-ohci
- brcm,bcm6368-ohci
- brcm,bcm7125-ohci
- brcm,bcm7346-ohci
- brcm,bcm7358-ohci
- brcm,bcm7360-ohci
- brcm,bcm7362-ohci
- brcm,bcm7420-ohci
- brcm,bcm7425-ohci
- brcm,bcm7435-ohci
- ibm,476gtr-ohci
- ingenic,jz4740-ohci
- snps,hsdk-v1.0-ohci
- const: generic-ohci
- const: generic-ohci
reg:
maxItems: 1

View File

@ -0,0 +1,65 @@
# SPDX-License-Identifier: GPL-2.0
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/generic-xhci.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: USB xHCI Controller Device Tree Bindings
maintainers:
- Mathias Nyman <mathias.nyman@intel.com>
allOf:
- $ref: "usb-xhci.yaml#"
properties:
compatible:
oneOf:
- description: Generic xHCI device
const: generic-xhci
- description: Armada 37xx/375/38x/8k SoCs
items:
- enum:
- marvell,armada3700-xhci
- marvell,armada-375-xhci
- marvell,armada-380-xhci
- marvell,armada-8k-xhci
- const: generic-xhci
- description: Broadcom STB SoCs with xHCI
enum:
- brcm,xhci-brcm-v2
- brcm,bcm7445-xhci
- description: Generic xHCI device
const: xhci-platform
deprecated: true
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
minItems: 1
maxItems: 2
clock-names:
minItems: 1
items:
- const: core
- const: reg
unevaluatedProperties: false
required:
- compatible
- reg
- interrupts
examples:
- |
usb@f0931000 {
compatible = "generic-xhci";
reg = <0xf0931000 0x8c8>;
interrupts = <0x0 0x4e 0x0>;
};

View File

@ -1,57 +0,0 @@
Generic USB Properties
Optional properties:
- maximum-speed: tells USB controllers we want to work up to a certain
speed. Valid arguments are "super-speed-plus",
"super-speed", "high-speed", "full-speed" and
"low-speed". In case this isn't passed via DT, USB
controllers should default to their maximum HW
capability.
- dr_mode: tells Dual-Role USB controllers that we want to work on a
particular mode. Valid arguments are "host",
"peripheral" and "otg". In case this attribute isn't
passed via DT, USB DRD controllers should default to
OTG.
- phy_type: tells USB controllers that we want to configure the core to support
a UTMI+ PHY with an 8- or 16-bit interface if UTMI+ is
selected. Valid arguments are "utmi" and "utmi_wide".
In case this isn't passed via DT, USB controllers should
default to HW capability.
- otg-rev: tells usb driver the release number of the OTG and EH supplement
with which the device and its descriptors are compliant,
in binary-coded decimal (i.e. 2.0 is 0200H). This
property is used if any real OTG features(HNP/SRP/ADP)
is enabled, if ADP is required, otg-rev should be
0x0200 or above.
- companion: phandle of a companion
- hnp-disable: tells OTG controllers we want to disable OTG HNP, normally HNP
is the basic function of real OTG except you want it
to be a srp-capable only B device.
- srp-disable: tells OTG controllers we want to disable OTG SRP, SRP is
optional for OTG device.
- adp-disable: tells OTG controllers we want to disable OTG ADP, ADP is
optional for OTG device.
- usb-role-switch: boolean, indicates that the device is capable of assigning
the USB data role (USB host or USB device) for a given
USB connector, such as Type-C, Type-B(micro).
see connector/usb-connector.yaml.
- role-switch-default-mode: indicating if usb-role-switch is enabled, the
device default operation mode of controller while usb
role is USB_ROLE_NONE. Valid arguments are "host" and
"peripheral". Defaults to "peripheral" if not
specified.
This is an attribute to a USB controller such as:
dwc3@4a030000 {
compatible = "synopsys,dwc3";
reg = <0x4a030000 0xcfff>;
interrupts = <0 92 4>
usb-phy = <&usb2_phy>, <&usb3,phy>;
maximum-speed = "super-speed";
dr_mode = "otg";
phy_type = "utmi_wide";
otg-rev = <0x0200>;
adp-disable;
};

View File

@ -34,11 +34,8 @@ properties:
# Required child node:
patternProperties:
"^dwc3@[0-9a-f]+$":
type: object
description:
A child node must exist to represent the core DWC3 IP block.
The content of the node is defined in dwc3.txt.
"^usb@[0-9a-f]+$":
$ref: snps,dwc3.yaml#
required:
- compatible
@ -68,7 +65,7 @@ examples:
#address-cells = <1>;
#size-cells = <1>;
dwc3@34000000 {
usb@34000000 {
compatible = "snps,dwc3";
reg = <0x34000000 0x10000>;
interrupts = <GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH>;

View File

@ -1,121 +0,0 @@
MT8173 xHCI
The device node for Mediatek SOC USB3.0 host controller
There are two scenarios: the first one only supports xHCI driver;
the second one supports dual-role mode, and the host is based on xHCI
driver. Take account of backward compatibility, we divide bindings
into two parts.
1st: only supports xHCI driver
------------------------------------------------------------------------
Required properties:
- compatible : should be "mediatek,<soc-model>-xhci", "mediatek,mtk-xhci",
soc-model is the name of SoC, such as mt8173, mt2712 etc, when using
"mediatek,mtk-xhci" compatible string, you need SoC specific ones in
addition, one of:
- "mediatek,mt8173-xhci"
- reg : specifies physical base address and size of the registers
- reg-names: should be "mac" for xHCI MAC and "ippc" for IP port control
- interrupts : interrupt used by the controller
- power-domains : a phandle to USB power domain node to control USB's
mtcmos
- vusb33-supply : regulator of USB avdd3.3v
- clocks : a list of phandle + clock-specifier pairs, one for each
entry in clock-names
- clock-names : must contain
"sys_ck": controller clock used by normal mode,
the following ones are optional:
"ref_ck": reference clock used by low power mode etc,
"mcu_ck": mcu_bus clock for register access,
"dma_ck": dma_bus clock for data transfer by DMA,
"xhci_ck": controller clock
- phys : see usb-hcd.yaml in the current directory
Optional properties:
- wakeup-source : enable USB remote wakeup;
- mediatek,syscon-wakeup : phandle to syscon used to access the register
of the USB wakeup glue layer between xHCI and SPM; it depends on
"wakeup-source", and has two arguments:
- the first one : register base address of the glue layer in syscon;
- the second one : hardware version of the glue layer
- 1 : used by mt8173 etc
- 2 : used by mt2712 etc
- mediatek,u3p-dis-msk : mask to disable u3ports, bit0 for u3port0,
bit1 for u3port1, ... etc;
- vbus-supply : reference to the VBUS regulator;
- usb3-lpm-capable : supports USB3.0 LPM
- pinctrl-names : a pinctrl state named "default" must be defined
- pinctrl-0 : pin control group
See: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
- imod-interval-ns: default interrupt moderation interval is 5000ns
additionally the properties from usb-hcd.yaml (in the current directory) are
supported.
Example:
usb30: usb@11270000 {
compatible = "mediatek,mt8173-xhci";
reg = <0 0x11270000 0 0x1000>,
<0 0x11280700 0 0x0100>;
reg-names = "mac", "ippc";
interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_LOW>;
power-domains = <&scpsys MT8173_POWER_DOMAIN_USB>;
clocks = <&topckgen CLK_TOP_USB30_SEL>, <&clk26m>,
<&pericfg CLK_PERI_USB0>,
<&pericfg CLK_PERI_USB1>;
clock-names = "sys_ck", "ref_ck";
phys = <&phy_port0 PHY_TYPE_USB3>,
<&phy_port1 PHY_TYPE_USB2>;
vusb33-supply = <&mt6397_vusb_reg>;
vbus-supply = <&usb_p1_vbus>;
usb3-lpm-capable;
mediatek,syscon-wakeup = <&pericfg 0x400 1>;
wakeup-source;
imod-interval-ns = <10000>;
};
2nd: dual-role mode with xHCI driver
------------------------------------------------------------------------
In the case, xhci is added as subnode to mtu3. An example and the DT binding
details of mtu3 can be found in:
Documentation/devicetree/bindings/usb/mediatek,mtu3.txt
Required properties:
- compatible : should be "mediatek,<soc-model>-xhci", "mediatek,mtk-xhci",
soc-model is the name of SoC, such as mt8173, mt2712 etc, when using
"mediatek,mtk-xhci" compatible string, you need SoC specific ones in
addition, one of:
- "mediatek,mt8173-xhci"
- reg : specifies physical base address and size of the registers
- reg-names: should be "mac" for xHCI MAC
- interrupts : interrupt used by the host controller
- power-domains : a phandle to USB power domain node to control USB's
mtcmos
- vusb33-supply : regulator of USB avdd3.3v
- clocks : a list of phandle + clock-specifier pairs, one for each
entry in clock-names
- clock-names : must contain "sys_ck", and the following ones are optional:
"ref_ck", "mcu_ck" and "dma_ck", "xhci_ck"
Optional properties:
- vbus-supply : reference to the VBUS regulator;
- usb3-lpm-capable : supports USB3.0 LPM
Example:
usb30: usb@11270000 {
compatible = "mediatek,mt8173-xhci";
reg = <0 0x11270000 0 0x1000>;
reg-names = "mac";
interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_LOW>;
power-domains = <&scpsys MT8173_POWER_DOMAIN_USB>;
clocks = <&topckgen CLK_TOP_USB30_SEL>, <&clk26m>;
clock-names = "sys_ck", "ref_ck";
vusb33-supply = <&mt6397_vusb_reg>;
usb3-lpm-capable;
};

View File

@ -0,0 +1,188 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
# Copyright (c) 2020 MediaTek
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/mediatek,mtk-xhci.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: MediaTek USB3 xHCI Device Tree Bindings
maintainers:
- Chunfeng Yun <chunfeng.yun@mediatek.com>
allOf:
- $ref: "usb-xhci.yaml"
description: |
There are two scenarios:
case 1: only supports xHCI driver;
case 2: supports dual-role mode, and the host is based on xHCI driver.
properties:
# common properties for both case 1 and case 2
compatible:
items:
- enum:
- mediatek,mt2701-xhci
- mediatek,mt2712-xhci
- mediatek,mt7622-xhci
- mediatek,mt7623-xhci
- mediatek,mt7629-xhci
- mediatek,mt8173-xhci
- mediatek,mt8183-xhci
- const: mediatek,mtk-xhci
reg:
minItems: 1
items:
- description: the registers of xHCI MAC
- description: the registers of IP Port Control
reg-names:
minItems: 1
items:
- const: mac
- const: ippc # optional, only needed for case 1.
interrupts:
maxItems: 1
power-domains:
description: A phandle to USB power domain node to control USB's MTCMOS
maxItems: 1
clocks:
minItems: 1
items:
- description: Controller clock used by normal mode
- description: Reference clock used by low power mode etc
- description: Mcu bus clock for register access
- description: DMA bus clock for data transfer
- description: controller clock
clock-names:
minItems: 1
items:
- const: sys_ck # required, the following ones are optional
- const: ref_ck
- const: mcu_ck
- const: dma_ck
- const: xhci_ck
assigned-clocks:
minItems: 1
maxItems: 5
assigned-clock-parents:
minItems: 1
maxItems: 5
phys:
description:
List of all PHYs used on this HCD, it's better to keep PHYs in order
as the hardware layout
minItems: 1
items:
- description: USB2/HS PHY # required, others are optional
- description: USB3/SS(P) PHY
- description: USB2/HS PHY
- description: USB3/SS(P) PHY
- description: USB2/HS PHY
- description: USB3/SS(P) PHY
- description: USB2/HS PHY
- description: USB3/SS(P) PHY
- description: USB2/HS PHY
vusb33-supply:
description: Regulator of USB AVDD3.3v
vbus-supply:
description: Regulator of USB VBUS5v
usb3-lpm-capable:
description: supports USB3.0 LPM
type: boolean
imod-interval-ns:
description:
Interrupt moderation interval value, it is 8 times as much as that
defined in the xHCI spec on MTK's controller.
default: 5000
# the following properties are only used for case 1
wakeup-source:
description: enable USB remote wakeup, see power/wakeup-source.txt
type: boolean
mediatek,syscon-wakeup:
$ref: /schemas/types.yaml#/definitions/phandle-array
maxItems: 1
description:
A phandle to syscon used to access the register of the USB wakeup glue
layer between xHCI and SPM, the field should always be 3 cells long.
items:
items:
- description:
The first cell represents a phandle to syscon
- description:
The second cell represents the register base address of the glue
layer in syscon
- description:
The third cell represents the hardware version of the glue layer,
1 is used by mt8173 etc, 2 is used by mt2712 etc
enum: [1, 2]
mediatek,u3p-dis-msk:
$ref: /schemas/types.yaml#/definitions/uint32
description: The mask to disable u3ports, bit0 for u3port0,
bit1 for u3port1, ... etc
"#address-cells":
const: 1
"#size-cells":
const: 0
patternProperties:
"@[0-9a-f]{1}$":
type: object
description: The hard wired USB devices.
dependencies:
wakeup-source: [ 'mediatek,syscon-wakeup' ]
required:
- compatible
- reg
- reg-names
- interrupts
- clocks
- clock-names
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/mt8173-clk.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/phy/phy.h>
#include <dt-bindings/power/mt8173-power.h>
usb@11270000 {
compatible = "mediatek,mt8173-xhci", "mediatek,mtk-xhci";
reg = <0x11270000 0x1000>, <0x11280700 0x0100>;
reg-names = "mac", "ippc";
interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_LOW>;
power-domains = <&scpsys MT8173_POWER_DOMAIN_USB>;
clocks = <&topckgen CLK_TOP_USB30_SEL>, <&clk26m>;
clock-names = "sys_ck", "ref_ck";
phys = <&u3port0 PHY_TYPE_USB3>, <&u2port1 PHY_TYPE_USB2>;
vusb33-supply = <&mt6397_vusb_reg>;
vbus-supply = <&usb_p1_vbus>;
imod-interval-ns = <10000>;
mediatek,syscon-wakeup = <&pericfg 0x400 1>;
wakeup-source;
usb3-lpm-capable;
};
...

View File

@ -1,108 +0,0 @@
The device node for Mediatek USB3.0 DRD controller
Required properties:
- compatible : should be "mediatek,<soc-model>-mtu3", "mediatek,mtu3",
soc-model is the name of SoC, such as mt8173, mt2712 etc,
when using "mediatek,mtu3" compatible string, you need SoC specific
ones in addition, one of:
- "mediatek,mt8173-mtu3"
- reg : specifies physical base address and size of the registers
- reg-names: should be "mac" for device IP and "ippc" for IP port control
- interrupts : interrupt used by the device IP
- power-domains : a phandle to USB power domain node to control USB's
mtcmos
- vusb33-supply : regulator of USB avdd3.3v
- clocks : a list of phandle + clock-specifier pairs, one for each
entry in clock-names
- clock-names : must contain "sys_ck" for clock of controller,
the following clocks are optional:
"ref_ck", "mcu_ck" and "dma_ck";
- phys : see usb-hcd.yaml in the current directory
- dr_mode : should be one of "host", "peripheral" or "otg",
refer to usb/generic.txt
Optional properties:
- #address-cells, #size-cells : should be '2' if the device has sub-nodes
with 'reg' property
- ranges : allows valid 1:1 translation between child's address space and
parent's address space
- extcon : external connector for vbus and idpin changes detection, needed
when supports dual-role mode.
it's considered valid for compatibility reasons, not allowed for
new bindings, and use "usb-role-switch" property instead.
- vbus-supply : reference to the VBUS regulator, needed when supports
dual-role mode.
it's considered valid for compatibility reasons, not allowed for
new bindings, and put into a usb-connector node.
see connector/usb-connector.yaml.
- pinctrl-names : a pinctrl state named "default" is optional, and need be
defined if auto drd switch is enabled, that means the property dr_mode
is set as "otg", and meanwhile the property "mediatek,enable-manual-drd"
is not set.
- pinctrl-0 : pin control group
See: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
- maximum-speed : valid arguments are "super-speed", "high-speed" and
"full-speed"; refer to usb/generic.txt
- usb-role-switch : use USB Role Switch to support dual-role switch, but
not extcon; see usb/generic.txt.
- enable-manual-drd : supports manual dual-role switch via debugfs; usually
used when receptacle is TYPE-A and also wants to support dual-role
mode.
- wakeup-source: enable USB remote wakeup of host mode.
- mediatek,syscon-wakeup : phandle to syscon used to access the register
of the USB wakeup glue layer between SSUSB and SPM; it depends on
"wakeup-source", and has two arguments:
- the first one : register base address of the glue layer in syscon;
- the second one : hardware version of the glue layer
- 1 : used by mt8173 etc
- 2 : used by mt2712 etc
- mediatek,u3p-dis-msk : mask to disable u3ports, bit0 for u3port0,
bit1 for u3port1, ... etc;
additionally the properties from usb-hcd.yaml (in the current directory) are
supported.
Sub-nodes:
The xhci should be added as subnode to mtu3 as shown in the following example
if host mode is enabled. The DT binding details of xhci can be found in:
Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.txt
The port would be added as subnode if use "usb-role-switch" property.
see graph.txt
Example:
ssusb: usb@11271000 {
compatible = "mediatek,mt8173-mtu3";
reg = <0 0x11271000 0 0x3000>,
<0 0x11280700 0 0x0100>;
reg-names = "mac", "ippc";
interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_LOW>;
phys = <&phy_port0 PHY_TYPE_USB3>,
<&phy_port1 PHY_TYPE_USB2>;
power-domains = <&scpsys MT8173_POWER_DOMAIN_USB>;
clocks = <&topckgen CLK_TOP_USB30_SEL>, <&clk26m>,
<&pericfg CLK_PERI_USB0>,
<&pericfg CLK_PERI_USB1>;
clock-names = "sys_ck", "ref_ck";
vusb33-supply = <&mt6397_vusb_reg>;
vbus-supply = <&usb_p0_vbus>;
extcon = <&extcon_usb>;
dr_mode = "otg";
wakeup-source;
mediatek,syscon-wakeup = <&pericfg 0x400 1>;
#address-cells = <2>;
#size-cells = <2>;
ranges;
usb_host: xhci@11270000 {
compatible = "mediatek,mt8173-xhci";
reg = <0 0x11270000 0 0x1000>;
reg-names = "mac";
interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_LOW>;
power-domains = <&scpsys MT8173_POWER_DOMAIN_USB>;
clocks = <&topckgen CLK_TOP_USB30_SEL>, <&clk26m>;
clock-names = "sys_ck", "ref_ck";
vusb33-supply = <&mt6397_vusb_reg>;
};
};

View File

@ -0,0 +1,287 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
# Copyright (c) 2020 MediaTek
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/mediatek,mtu3.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: MediaTek USB3 DRD Controller Device Tree Bindings
maintainers:
- Chunfeng Yun <chunfeng.yun@mediatek.com>
allOf:
- $ref: "usb-drd.yaml"
description: |
The DRD controller has a glue layer IPPC (IP Port Control), and its host is
based on xHCI.
properties:
compatible:
items:
- enum:
- mediatek,mt2712-mtu3
- mediatek,mt8173-mtu3
- mediatek,mt8183-mtu3
- const: mediatek,mtu3
reg:
items:
- description: the registers of device MAC
- description: the registers of IP Port Control
reg-names:
items:
- const: mac
- const: ippc
interrupts:
maxItems: 1
power-domains:
description: A phandle to USB power domain node to control USB's MTCMOS
maxItems: 1
clocks:
minItems: 1
items:
- description: Controller clock used by normal mode
- description: Reference clock used by low power mode etc
- description: Mcu bus clock for register access
- description: DMA bus clock for data transfer
clock-names:
minItems: 1
items:
- const: sys_ck # required, others are optional
- const: ref_ck
- const: mcu_ck
- const: dma_ck
phys:
description:
List of all the USB PHYs used, it's better to keep the sequence
as the hardware layout.
minItems: 1
items:
- description: USB2/HS PHY # required, others are optional
- description: USB3/SS(P) PHY
- description: USB2/HS PHY # the following for backward compatible
- description: USB3/SS(P) PHY
- description: USB2/HS PHY
- description: USB3/SS(P) PHY
- description: USB2/HS PHY
- description: USB3/SS(P) PHY
- description: USB2/HS PHY
vusb33-supply:
description: Regulator of USB AVDD3.3v
vbus-supply:
deprecated: true
description: |
Regulator of USB VBUS5v, needed when supports dual-role mode.
Particularly, if use an output GPIO to control a VBUS regulator, should
model it as a regulator. See bindings/regulator/fixed-regulator.yaml
It's considered valid for compatibility reasons, not allowed for
new bindings, and put into a usb-connector node.
dr_mode:
enum: [host, peripheral, otg]
default: otg
maximum-speed:
enum: [super-speed-plus, super-speed, high-speed, full-speed]
"#address-cells":
enum: [1, 2]
"#size-cells":
enum: [1, 2]
ranges: true
extcon:
deprecated: true
description: |
Phandle to the extcon device detecting the IDDIG/VBUS state, neede
when supports dual-role mode.
It's considered valid for compatibility reasons, not allowed for
new bindings, and use "usb-role-switch" property instead.
usb-role-switch:
$ref: /schemas/types.yaml#/definitions/flag
description: Support role switch.
type: boolean
connector:
$ref: /connector/usb-connector.yaml#
description:
Connector for dual role switch, especially for "gpio-usb-b-connector"
type: object
port:
description:
Any connector to the data bus of this controller should be modelled
using the OF graph bindings specified, if the "usb-role-switch"
property is used. See graph.txt
type: object
enable-manual-drd:
$ref: /schemas/types.yaml#/definitions/flag
description:
supports manual dual-role switch via debugfs; usually used when
receptacle is TYPE-A and also wants to support dual-role mode.
type: boolean
wakeup-source:
description: enable USB remote wakeup, see power/wakeup-source.txt
type: boolean
mediatek,syscon-wakeup:
$ref: /schemas/types.yaml#/definitions/phandle-array
maxItems: 1
description:
A phandle to syscon used to access the register of the USB wakeup glue
layer between xHCI and SPM, the field should always be 3 cells long.
items:
items:
- description:
The first cell represents a phandle to syscon
- description:
The second cell represents the register base address of the glue
layer in syscon
- description:
The third cell represents the hardware version of the glue layer,
1 is used by mt8173 etc, 2 is used by mt2712 etc
enum: [1, 2]
mediatek,u3p-dis-msk:
$ref: /schemas/types.yaml#/definitions/uint32
description: The mask to disable u3ports, bit0 for u3port0,
bit1 for u3port1, ... etc
# Required child node when support dual-role
patternProperties:
"^usb@[0-9a-f]+$":
type: object
$ref: /usb/mediatek,mtk-xhci.yaml#
description:
The xhci should be added as subnode to mtu3 as shown in the following
example if the host mode is enabled.
dependencies:
connector: [ 'usb-role-switch' ]
port: [ 'usb-role-switch' ]
wakeup-source: [ 'mediatek,syscon-wakeup' ]
required:
- compatible
- reg
- reg-names
- interrupts
- clocks
- clock-names
additionalProperties: false
examples:
# Dual role switch by extcon
- |
#include <dt-bindings/clock/mt8173-clk.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/phy/phy.h>
#include <dt-bindings/power/mt8173-power.h>
usb@11271000 {
compatible = "mediatek,mt8173-mtu3", "mediatek,mtu3";
reg = <0x11271000 0x3000>, <0x11280700 0x0100>;
reg-names = "mac", "ippc";
interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_LOW>;
phys = <&phy_port0 PHY_TYPE_USB3>, <&phy_port1 PHY_TYPE_USB2>;
power-domains = <&scpsys MT8173_POWER_DOMAIN_USB>;
clocks = <&topckgen CLK_TOP_USB30_SEL>;
clock-names = "sys_ck";
vusb33-supply = <&mt6397_vusb_reg>;
vbus-supply = <&usb_p0_vbus>;
extcon = <&extcon_usb>;
dr_mode = "otg";
wakeup-source;
mediatek,syscon-wakeup = <&pericfg 0x400 1>;
#address-cells = <1>;
#size-cells = <1>;
ranges;
xhci: usb@11270000 {
compatible = "mediatek,mt8173-xhci", "mediatek,mtk-xhci";
reg = <0x11270000 0x1000>;
reg-names = "mac";
interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_LOW>;
power-domains = <&scpsys MT8173_POWER_DOMAIN_USB>;
clocks = <&topckgen CLK_TOP_USB30_SEL>, <&clk26m>;
clock-names = "sys_ck", "ref_ck";
vusb33-supply = <&mt6397_vusb_reg>;
};
};
# Enable/disable device by an input gpio for VBUS pin
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/power/mt2712-power.h>
usb@112c1000 {
compatible = "mediatek,mt2712-mtu3", "mediatek,mtu3";
reg = <0x112c1000 0x3000>, <0x112d0700 0x0100>;
reg-names = "mac", "ippc";
interrupts = <GIC_SPI 248 IRQ_TYPE_LEVEL_LOW>;
phys = <&u2port2 PHY_TYPE_USB2>;
power-domains = <&scpsys MT2712_POWER_DOMAIN_USB2>;
clocks = <&topckgen CLK_TOP_USB30_SEL>;
clock-names = "sys_ck";
dr_mode = "peripheral";
usb-role-switch;
connector {
compatible = "gpio-usb-b-connector", "usb-b-connector";
type = "micro";
vbus-gpios = <&pio 13 GPIO_ACTIVE_HIGH>;
};
};
# Dual role switch with type-c
- |
usb@11201000 {
compatible ="mediatek,mt8183-mtu3", "mediatek,mtu3";
reg = <0x11201000 0x2e00>, <0x11203e00 0x0100>;
reg-names = "mac", "ippc";
interrupts = <GIC_SPI 72 IRQ_TYPE_LEVEL_LOW>;
phys = <&u2port0 PHY_TYPE_USB2>;
clocks = <&clk26m>;
clock-names = "sys_ck";
mediatek,syscon-wakeup = <&pericfg 0x400 1>;
wakeup-source;
dr_mode = "otg";
usb-role-switch;
#address-cells = <1>;
#size-cells = <1>;
ranges;
host: usb@11200000 {
compatible = "mediatek,mt8183-xhci", "mediatek,mtk-xhci";
reg = <0x11200000 0x1000>;
reg-names = "mac";
interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_LOW>;
clocks = <&clk26m>;
clock-names = "sys_ck";
};
port {
usb_role_sw: endpoint {
remote-endpoint = <&hs_ep>;
};
};
};
...

View File

@ -1,57 +0,0 @@
MediaTek musb DRD/OTG controller
-------------------------------------------
Required properties:
- compatible : should be one of:
"mediatek,mt2701-musb"
...
followed by "mediatek,mtk-musb"
- reg : specifies physical base address and size of
the registers
- interrupts : interrupt used by musb controller
- interrupt-names : must be "mc"
- phys : PHY specifier for the OTG phy
- dr_mode : should be one of "host", "peripheral" or "otg",
refer to usb/generic.txt
- clocks : a list of phandle + clock-specifier pairs, one for
each entry in clock-names
- clock-names : must contain "main", "mcu", "univpll"
for clocks of controller
Optional properties:
- power-domains : a phandle to USB power domain node to control USB's
MTCMOS
Required child nodes:
usb connector node as defined in bindings/connector/usb-connector.yaml
Optional properties:
- id-gpios : input GPIO for USB ID pin.
- vbus-gpios : input GPIO for USB VBUS pin.
- vbus-supply : reference to the VBUS regulator, needed when supports
dual-role mode
- usb-role-switch : use USB Role Switch to support dual-role switch, see
usb/generic.txt.
Example:
usb2: usb@11200000 {
compatible = "mediatek,mt2701-musb",
"mediatek,mtk-musb";
reg = <0 0x11200000 0 0x1000>;
interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_LOW>;
interrupt-names = "mc";
phys = <&u2port2 PHY_TYPE_USB2>;
dr_mode = "otg";
clocks = <&pericfg CLK_PERI_USB0>,
<&pericfg CLK_PERI_USB0_MCU>,
<&pericfg CLK_PERI_USB_SLV>;
clock-names = "main","mcu","univpll";
power-domains = <&scpsys MT2701_POWER_DOMAIN_IFR_MSC>;
usb-role-switch;
connector{
compatible = "gpio-usb-b-connector", "usb-b-connector";
type = "micro";
id-gpios = <&pio 44 GPIO_ACTIVE_HIGH>;
vbus-supply = <&usb_vbus>;
};
};

View File

@ -0,0 +1,114 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
# Copyright (c) 2020 MediaTek
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/mediatek,musb.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: MediaTek MUSB DRD/OTG Controller Device Tree Bindings
maintainers:
- Min Guo <min.guo@mediatek.com>
properties:
$nodename:
pattern: '^usb@[0-9a-f]+$'
compatible:
items:
- enum:
- mediatek,mt8516-musb
- mediatek,mt2701-musb
- const: mediatek,mtk-musb
reg:
maxItems: 1
interrupts:
maxItems: 1
interrupt-names:
items:
- const: mc
clocks:
items:
- description: The main/core clock
- description: The system bus clock
- description: The 48Mhz clock
clock-names:
items:
- const: main
- const: mcu
- const: univpll
phys:
maxItems: 1
usb-role-switch:
$ref: /schemas/types.yaml#/definitions/flag
description: Support role switch. See usb/generic.txt
type: boolean
dr_mode:
enum:
- host
- otg
- peripheral
power-domains:
description: A phandle to USB power domain node to control USB's MTCMOS
maxItems: 1
connector:
$ref: /connector/usb-connector.yaml#
description: Connector for dual role switch
type: object
dependencies:
usb-role-switch: [ 'connector' ]
connector: [ 'usb-role-switch' ]
required:
- compatible
- reg
- interrupts
- interrupt-names
- phys
- clocks
- clock-names
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/mt2701-clk.h>
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/phy/phy.h>
#include <dt-bindings/power/mt2701-power.h>
usb@11200000 {
compatible = "mediatek,mt2701-musb", "mediatek,mtk-musb";
reg = <0x11200000 0x1000>;
interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_LOW>;
interrupt-names = "mc";
phys = <&u2port2 PHY_TYPE_USB2>;
dr_mode = "otg";
clocks = <&pericfg CLK_PERI_USB0>,
<&pericfg CLK_PERI_USB0_MCU>,
<&pericfg CLK_PERI_USB_SLV>;
clock-names = "main","mcu","univpll";
power-domains = <&scpsys MT2701_POWER_DOMAIN_IFR_MSC>;
usb-role-switch;
connector {
compatible = "gpio-usb-b-connector", "usb-b-connector";
type = "micro";
id-gpios = <&pio 44 GPIO_ACTIVE_HIGH>;
vbus-supply = <&usb_vbus>;
};
};
...

View File

@ -65,7 +65,7 @@ Sub-nodes:
The dwc3 core should be added as subnode to omap dwc3 glue.
- dwc3 :
The binding details of dwc3 can be found in:
Documentation/devicetree/bindings/usb/dwc3.txt
Documentation/devicetree/bindings/usb/snps,dwc3.yaml
omap_dwc3 {
compatible = "ti,dwc3";

View File

@ -18,6 +18,9 @@ properties:
- qcom,sc7180-dwc3
- qcom,sdm845-dwc3
- qcom,sdx55-dwc3
- qcom,sm8150-dwc3
- qcom,sm8250-dwc3
- qcom,sm8350-dwc3
- const: qcom,dwc3
reg:
@ -104,11 +107,8 @@ properties:
# Required child node:
patternProperties:
"^dwc3@[0-9a-f]+$":
type: object
description:
A child node must exist to represent the core DWC3 IP block
The content of the node is defined in dwc3.txt.
"^usb@[0-9a-f]+$":
$ref: snps,dwc3.yaml#
required:
- compatible
@ -163,7 +163,7 @@ examples:
resets = <&gcc GCC_USB30_PRIM_BCR>;
dwc3@a600000 {
usb@a600000 {
compatible = "snps,dwc3";
reg = <0 0x0a600000 0 0xcd00>;
interrupts = <GIC_SPI 133 IRQ_TYPE_LEVEL_HIGH>;

View File

@ -11,7 +11,7 @@ maintainers:
- Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
allOf:
- $ref: "usb-hcd.yaml"
- $ref: "usb-xhci.yaml"
properties:
compatible:
@ -68,7 +68,7 @@ required:
- power-domains
- resets
additionalProperties: false
unevaluatedProperties: false
examples:
- |

View File

@ -1,56 +0,0 @@
Rockchip SuperSpeed DWC3 USB SoC controller
Required properties:
- compatible: should contain "rockchip,rk3399-dwc3" for rk3399 SoC
- clocks: A list of phandle + clock-specifier pairs for the
clocks listed in clock-names
- clock-names: Should contain the following:
"ref_clk" Controller reference clk, have to be 24 MHz
"suspend_clk" Controller suspend clk, have to be 24 MHz or 32 KHz
"bus_clk" Master/Core clock, have to be >= 62.5 MHz for SS
operation and >= 30MHz for HS operation
"grf_clk" Controller grf clk
Required child node:
A child node must exist to represent the core DWC3 IP block. The name of
the node is not important. The content of the node is defined in dwc3.txt.
Phy documentation is provided in the following places:
Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.yaml - USB2.0 PHY
Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt - Type-C PHY
Example device nodes:
usbdrd3_0: usb@fe800000 {
compatible = "rockchip,rk3399-dwc3";
clocks = <&cru SCLK_USB3OTG0_REF>, <&cru SCLK_USB3OTG0_SUSPEND>,
<&cru ACLK_USB3OTG0>, <&cru ACLK_USB3_GRF>;
clock-names = "ref_clk", "suspend_clk",
"bus_clk", "grf_clk";
#address-cells = <2>;
#size-cells = <2>;
ranges;
usbdrd_dwc3_0: dwc3@fe800000 {
compatible = "snps,dwc3";
reg = <0x0 0xfe800000 0x0 0x100000>;
interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>;
dr_mode = "otg";
};
};
usbdrd3_1: usb@fe900000 {
compatible = "rockchip,rk3399-dwc3";
clocks = <&cru SCLK_USB3OTG1_REF>, <&cru SCLK_USB3OTG1_SUSPEND>,
<&cru ACLK_USB3OTG1>, <&cru ACLK_USB3_GRF>;
clock-names = "ref_clk", "suspend_clk",
"bus_clk", "grf_clk";
#address-cells = <2>;
#size-cells = <2>;
ranges;
usbdrd_dwc3_1: dwc3@fe900000 {
compatible = "snps,dwc3";
reg = <0x0 0xfe900000 0x0 0x100000>;
interrupts = <GIC_SPI 110 IRQ_TYPE_LEVEL_HIGH>;
dr_mode = "otg";
};
};

View File

@ -0,0 +1,108 @@
# SPDX-License-Identifier: GPL-2.0
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/rockchip,dwc3.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Rockchip SuperSpeed DWC3 USB SoC controller
maintainers:
- Heiko Stuebner <heiko@sntech.de>
description:
The common content of the node is defined in snps,dwc3.yaml.
Phy documentation is provided in the following places.
USB2.0 PHY
Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.yaml
Type-C PHY
Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt
allOf:
- $ref: snps,dwc3.yaml#
select:
properties:
compatible:
contains:
enum:
- rockchip,rk3328-dwc3
- rockchip,rk3399-dwc3
required:
- compatible
properties:
compatible:
items:
- enum:
- rockchip,rk3328-dwc3
- rockchip,rk3399-dwc3
- const: snps,dwc3
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
minItems: 3
items:
- description:
Controller reference clock, must to be 24 MHz
- description:
Controller suspend clock, must to be 24 MHz or 32 KHz
- description:
Master/Core clock, must to be >= 62.5 MHz for SS
operation and >= 30MHz for HS operation
- description:
Controller grf clock
clock-names:
minItems: 3
items:
- const: ref_clk
- const: suspend_clk
- const: bus_clk
- const: grf_clk
power-domains:
maxItems: 1
resets:
maxItems: 1
reset-names:
const: usb3-otg
unevaluatedProperties: false
required:
- compatible
- reg
- interrupts
- clocks
- clock-names
examples:
- |
#include <dt-bindings/clock/rk3399-cru.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
bus {
#address-cells = <2>;
#size-cells = <2>;
usbdrd3_0: usb@fe800000 {
compatible = "rockchip,rk3399-dwc3", "snps,dwc3";
reg = <0x0 0xfe800000 0x0 0x100000>;
interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cru SCLK_USB3OTG0_REF>, <&cru SCLK_USB3OTG0_SUSPEND>,
<&cru ACLK_USB3OTG0>, <&cru ACLK_USB3_GRF>;
clock-names = "ref_clk", "suspend_clk",
"bus_clk", "grf_clk";
dr_mode = "otg";
};
};

View File

@ -0,0 +1,332 @@
# SPDX-License-Identifier: GPL-2.0
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/snps,dwc3.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Synopsys DesignWare USB3 Controller
maintainers:
- Felipe Balbi <balbi@kernel.org>
description:
This is usually a subnode to DWC3 glue to which it is connected, but can also
be presented as a standalone DT node with an optional vendor-specific
compatible string.
allOf:
- $ref: usb-drd.yaml#
- if:
properties:
dr_mode:
const: peripheral
required:
- dr_mode
then:
$ref: usb.yaml#
else:
$ref: usb-xhci.yaml#
properties:
compatible:
contains:
oneOf:
- const: snps,dwc3
- const: synopsys,dwc3
deprecated: true
interrupts:
description:
It's either a single common DWC3 interrupt (dwc_usb3) or individual
interrupts for the host, gadget and DRD modes.
minItems: 1
maxItems: 3
interrupt-names:
minItems: 1
maxItems: 3
oneOf:
- const: dwc_usb3
- items:
enum: [host, peripheral, otg]
clocks:
description:
In general the core supports three types of clocks. bus_early is a
SoC Bus Clock (AHB/AXI/Native). ref generates ITP when the UTMI/ULPI
PHY is suspended. suspend clocks a small part of the USB3 core when
SS PHY in P3. But particular cases may differ from that having less
or more clock sources with another names.
clock-names:
contains:
anyOf:
- enum: [bus_early, ref, suspend]
- true
usb-phy:
minItems: 1
items:
- description: USB2/HS PHY
- description: USB3/SS PHY
phys:
minItems: 1
items:
- description: USB2/HS PHY
- description: USB3/SS PHY
phy-names:
minItems: 1
items:
- const: usb2-phy
- const: usb3-phy
resets:
minItems: 1
snps,usb2-lpm-disable:
description: Indicate if we don't want to enable USB2 HW LPM
type: boolean
snps,usb3_lpm_capable:
description: Determines if platform is USB3 LPM capable
type: boolean
snps,dis-start-transfer-quirk:
description:
When set, disable isoc START TRANSFER command failure SW work-around
for DWC_usb31 version 1.70a-ea06 and prior.
type: boolean
snps,disable_scramble_quirk:
description:
True when SW should disable data scrambling. Only really useful for FPGA
builds.
type: boolean
snps,has-lpm-erratum:
description: True when DWC3 was configured with LPM Erratum enabled
type: boolean
snps,lpm-nyet-threshold:
description: LPM NYET threshold
$ref: /schemas/types.yaml#/definitions/uint8
snps,u2exit_lfps_quirk:
description: Set if we want to enable u2exit lfps quirk
type: boolean
snps,u2ss_inp3_quirk:
description: Set if we enable P3 OK for U2/SS Inactive quirk
type: boolean
snps,req_p1p2p3_quirk:
description:
When set, the core will always request for P1/P2/P3 transition sequence.
type: boolean
snps,del_p1p2p3_quirk:
description:
When set core will delay P1/P2/P3 until a certain amount of 8B10B errors
occur.
type: boolean
snps,del_phy_power_chg_quirk:
description: When set core will delay PHY power change from P0 to P1/P2/P3.
type: boolean
snps,lfps_filter_quirk:
description: When set core will filter LFPS reception.
type: boolean
snps,rx_detect_poll_quirk:
description:
when set core will disable a 400us delay to start Polling LFPS after
RX.Detect.
type: boolean
snps,tx_de_emphasis_quirk:
description: When set core will set Tx de-emphasis value
type: boolean
snps,tx_de_emphasis:
description:
The value driven to the PHY is controlled by the LTSSM during USB3
Compliance mode.
$ref: /schemas/types.yaml#/definitions/uint8
enum:
- 0 # -6dB de-emphasis
- 1 # -3.5dB de-emphasis
- 2 # No de-emphasis
snps,dis_u3_susphy_quirk:
description: When set core will disable USB3 suspend phy
type: boolean
snps,dis_u2_susphy_quirk:
description: When set core will disable USB2 suspend phy
type: boolean
snps,dis_enblslpm_quirk:
description:
When set clears the enblslpm in GUSB2PHYCFG, disabling the suspend signal
to the PHY.
type: boolean
snps,dis-u1-entry-quirk:
description: Set if link entering into U1 needs to be disabled
type: boolean
snps,dis-u2-entry-quirk:
description: Set if link entering into U2 needs to be disabled
type: boolean
snps,dis_rxdet_inp3_quirk:
description:
When set core will disable receiver detection in PHY P3 power state.
type: boolean
snps,dis-u2-freeclk-exists-quirk:
description:
When set, clear the u2_freeclk_exists in GUSB2PHYCFG, specify that USB2
PHY doesn't provide a free-running PHY clock.
type: boolean
snps,dis-del-phy-power-chg-quirk:
description:
When set core will change PHY power from P0 to P1/P2/P3 without delay.
type: boolean
snps,dis-tx-ipgap-linecheck-quirk:
description: When set, disable u2mac linestate check during HS transmit
type: boolean
snps,parkmode-disable-ss-quirk:
description:
When set, all SuperSpeed bus instances in park mode are disabled.
type: boolean
snps,dis_metastability_quirk:
description:
When set, disable metastability workaround. CAUTION! Use only if you are
absolutely sure of it.
type: boolean
snps,dis-split-quirk:
description:
When set, change the way URBs are handled by the driver. Needed to
avoid -EPROTO errors with usbhid on some devices (Hikey 970).
type: boolean
snps,is-utmi-l1-suspend:
description:
True when DWC3 asserts output signal utmi_l1_suspend_n, false when
asserts utmi_sleep_n.
type: boolean
snps,hird-threshold:
description: HIRD threshold
$ref: /schemas/types.yaml#/definitions/uint8
snps,hsphy_interface:
description:
High-Speed PHY interface selection between UTMI+ and ULPI when the
DWC_USB3_HSPHY_INTERFACE has value 3.
$ref: /schemas/types.yaml#/definitions/uint8
enum: [utmi, ulpi]
snps,quirk-frame-length-adjustment:
description:
Value for GFLADJ_30MHZ field of GFLADJ register for post-silicon frame
length adjustment when the fladj_30mhz_sdbnd signal is invalid or
incorrect.
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 0x3f
snps,rx-thr-num-pkt-prd:
description:
Periodic ESS RX packet threshold count (host mode only). Set this and
snps,rx-max-burst-prd to a valid, non-zero value 1-16 (DWC_usb31
programming guide section 1.2.4) to enable periodic ESS RX threshold.
$ref: /schemas/types.yaml#/definitions/uint8
minimum: 1
maximum: 16
snps,rx-max-burst-prd:
description:
Max periodic ESS RX burst size (host mode only). Set this and
snps,rx-thr-num-pkt-prd to a valid, non-zero value 1-16 (DWC_usb31
programming guide section 1.2.4) to enable periodic ESS RX threshold.
$ref: /schemas/types.yaml#/definitions/uint8
minimum: 1
maximum: 16
snps,tx-thr-num-pkt-prd:
description:
Periodic ESS TX packet threshold count (host mode only). Set this and
snps,tx-max-burst-prd to a valid, non-zero value 1-16 (DWC_usb31
programming guide section 1.2.3) to enable periodic ESS TX threshold.
$ref: /schemas/types.yaml#/definitions/uint8
minimum: 1
maximum: 16
snps,tx-max-burst-prd:
description:
Max periodic ESS TX burst size (host mode only). Set this and
snps,tx-thr-num-pkt-prd to a valid, non-zero value 1-16 (DWC_usb31
programming guide section 1.2.3) to enable periodic ESS TX threshold.
$ref: /schemas/types.yaml#/definitions/uint8
minimum: 1
maximum: 16
tx-fifo-resize:
description: Determines if the FIFO *has* to be reallocated
deprecated: true
type: boolean
snps,incr-burst-type-adjustment:
description:
Value for INCR burst type of GSBUSCFG0 register, undefined length INCR
burst type enable and INCRx type. A single value means INCRX burst mode
enabled. If more than one value specified, undefined length INCR burst
type will be enabled with burst lengths utilized up to the maximum
of the values passed in this property.
$ref: /schemas/types.yaml#/definitions/uint32-array
minItems: 1
maxItems: 8
uniqueItems: true
items:
enum: [1, 4, 8, 16, 32, 64, 128, 256]
unevaluatedProperties: false
required:
- compatible
- reg
- interrupts
examples:
- |
usb@4a030000 {
compatible = "snps,dwc3";
reg = <0x4a030000 0xcfff>;
interrupts = <0 92 4>;
usb-phy = <&usb2_phy>, <&usb3_phy>;
snps,incr-burst-type-adjustment = <1>, <4>, <8>, <16>;
};
- |
usb@4a000000 {
compatible = "snps,dwc3";
reg = <0x4a000000 0xcfff>;
interrupts = <0 92 4>;
clocks = <&clk 1>, <&clk 2>, <&clk 3>;
clock-names = "bus_early", "ref", "suspend";
phys = <&usb2_phy>, <&usb3_phy>;
phy-names = "usb2-phy", "usb3-phy";
snps,dis_u2_susphy_quirk;
snps,dis_enblslpm_quirk;
};
...

View File

@ -64,9 +64,7 @@ properties:
patternProperties:
"usb@[a-f0-9]+$":
type: object
description: This is the node representing the DWC3 controller instance
Documentation/devicetree/bindings/usb/dwc3.txt
$ref: snps,dwc3.yaml#
required:
- compatible

View File

@ -1,102 +0,0 @@
Generic USB Device Properties
Usually, we only use device tree for hard wired USB device.
The reference binding doc is from:
http://www.devicetree.org/open-firmware/bindings/usb/usb-1_0.ps
Four types of device-tree nodes are defined: "host-controller nodes"
representing USB host controllers, "device nodes" representing USB devices,
"interface nodes" representing USB interfaces and "combined nodes"
representing simple USB devices.
A combined node shall be used instead of a device node and an interface node
for devices of class 0 or 9 (hub) with a single configuration and a single
interface.
A "hub node" is a combined node or an interface node that represents a USB
hub.
Required properties for device nodes:
- compatible: "usbVID,PID", where VID is the vendor id and PID the product id.
The textual representation of VID and PID shall be in lower case hexadecimal
with leading zeroes suppressed. The other compatible strings from the above
standard binding could also be used, but a device adhering to this binding
may leave out all except for "usbVID,PID".
- reg: the number of the USB hub port or the USB host-controller port to which
this device is attached. The range is 1-255.
Required properties for device nodes with interface nodes:
- #address-cells: shall be 2
- #size-cells: shall be 0
Required properties for interface nodes:
- compatible: "usbifVID,PID.configCN.IN", where VID is the vendor id, PID is
the product id, CN is the configuration value and IN is the interface
number. The textual representation of VID, PID, CN and IN shall be in lower
case hexadecimal with leading zeroes suppressed. The other compatible
strings from the above standard binding could also be used, but a device
adhering to this binding may leave out all except for
"usbifVID,PID.configCN.IN".
- reg: the interface number and configuration value
The configuration component is not included in the textual representation of
an interface-node unit address for configuration 1.
Required properties for combined nodes:
- compatible: "usbVID,PID", where VID is the vendor id and PID the product id.
The textual representation of VID and PID shall be in lower case hexadecimal
with leading zeroes suppressed. The other compatible strings from the above
standard binding could also be used, but a device adhering to this binding
may leave out all except for "usbVID,PID".
- reg: the number of the USB hub port or the USB host-controller port to which
this device is attached. The range is 1-255.
Required properties for hub nodes with device nodes:
- #address-cells: shall be 1
- #size-cells: shall be 0
Required properties for host-controller nodes with device nodes:
- #address-cells: shall be 1
- #size-cells: shall be 0
Example:
&usb1 { /* host controller */
#address-cells = <1>;
#size-cells = <0>;
hub@1 { /* hub connected to port 1 */
compatible = "usb5e3,608";
reg = <1>;
};
device@2 { /* device connected to port 2 */
compatible = "usb123,4567";
reg = <2>;
};
device@3 { /* device connected to port 3 */
compatible = "usb123,abcd";
reg = <3>;
#address-cells = <2>;
#size-cells = <0>;
interface@0 { /* interface 0 of configuration 1 */
compatible = "usbif123,abcd.config1.0";
reg = <0 1>;
};
interface@0,2 { /* interface 0 of configuration 2 */
compatible = "usbif123,abcd.config2.0";
reg = <0 2>;
};
};
};

View File

@ -0,0 +1,124 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/usb-device.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: The device tree bindings for the Generic USB Device
maintainers:
- Greg Kroah-Hartman <gregkh@linuxfoundation.org>
description: |
Usually, we only use device tree for hard wired USB device.
The reference binding doc is from:
http://www.devicetree.org/open-firmware/bindings/usb/usb-1_0.ps
Four types of device-tree nodes are defined: "host-controller nodes"
representing USB host controllers, "device nodes" representing USB devices,
"interface nodes" representing USB interfaces and "combined nodes"
representing simple USB devices.
A combined node shall be used instead of a device node and an interface node
for devices of class 0 or 9 (hub) with a single configuration and a single
interface.
A "hub node" is a combined node or an interface node that represents a USB
hub.
properties:
compatible:
pattern: "^usb[0-9a-f]{1,4},[0-9a-f]{1,4}$"
description: Device nodes or combined nodes.
"usbVID,PID", where VID is the vendor id and PID the product id.
The textual representation of VID and PID shall be in lower case
hexadecimal with leading zeroes suppressed. The other compatible
strings from the above standard binding could also be used,
but a device adhering to this binding may leave out all except
for "usbVID,PID".
reg:
description: the number of the USB hub port or the USB host-controller
port to which this device is attached. The range is 1-255.
maxItems: 1
"#address-cells":
description: should be 1 for hub nodes with device nodes,
should be 2 for device nodes with interface nodes.
enum: [1, 2]
"#size-cells":
const: 0
patternProperties:
"^interface@[0-9a-f]{1,2}(,[0-9a-f]{1,2})$":
type: object
description: USB interface nodes.
The configuration component is not included in the textual
representation of an interface-node unit address for configuration 1.
properties:
compatible:
pattern: "^usbif[0-9a-f]{1,4},[0-9a-f]{1,4}.config[0-9a-f]{1,2}.[0-9a-f]{1,2}$"
description: Interface nodes.
"usbifVID,PID.configCN.IN", where VID is the vendor id, PID is
the product id, CN is the configuration value and IN is the interface
number. The textual representation of VID, PID, CN and IN shall be
in lower case hexadecimal with leading zeroes suppressed.
The other compatible strings from the above standard binding could
also be used, but a device adhering to this binding may leave out
all except for "usbifVID,PID.configCN.IN".
reg:
description: should be 2 cells long, the first cell represents
the interface number and the second cell represents the
configuration value.
maxItems: 1
required:
- compatible
- reg
additionalProperties: true
examples:
#hub connected to port 1
#device connected to port 2
#device connected to port 3
# interface 0 of configuration 1
# interface 0 of configuration 2
- |
usb@11270000 {
reg = <0x11270000 0x1000>;
interrupts = <0x0 0x4e 0x0>;
#address-cells = <1>;
#size-cells = <0>;
hub@1 {
compatible = "usb5e3,608";
reg = <1>;
};
device@2 {
compatible = "usb123,4567";
reg = <2>;
};
device@3 {
compatible = "usb123,abcd";
reg = <3>;
#address-cells = <2>;
#size-cells = <0>;
interface@0 {
compatible = "usbif123,abcd.config1.0";
reg = <0 1>;
};
interface@0,2 {
compatible = "usbif123,abcd.config2.0";
reg = <0 2>;
};
};
};

View File

@ -0,0 +1,78 @@
# SPDX-License-Identifier: GPL-2.0
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/usb-drd.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Generic USB OTG Controller Device Tree Bindings
maintainers:
- Greg Kroah-Hartman <gregkh@linuxfoundation.org>
properties:
otg-rev:
description:
Tells usb driver the release number of the OTG and EH supplement with
which the device and its descriptors are compliant, in binary-coded
decimal (i.e. 2.0 is 0200H). This property is used if any real OTG
features (HNP/SRP/ADP) is enabled. If ADP is required, otg-rev should be
0x0200 or above.
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0x0100, 0x0120, 0x0130, 0x0200]
dr_mode:
description:
Tells Dual-Role USB controllers that we want to work on a particular
mode. In case this attribute isn't passed via DT, USB DRD controllers
should default to OTG.
$ref: /schemas/types.yaml#/definitions/string
enum: [host, peripheral, otg]
hnp-disable:
description:
Tells OTG controllers we want to disable OTG HNP. Normally HNP is the
basic function of real OTG except you want it to be a srp-capable only B
device.
type: boolean
srp-disable:
description:
Tells OTG controllers we want to disable OTG SRP. SRP is optional for OTG
device.
type: boolean
adp-disable:
description:
Tells OTG controllers we want to disable OTG ADP. ADP is optional for OTG
device.
type: boolean
usb-role-switch:
description:
Indicates that the device is capable of assigning the USB data role
(USB host or USB device) for a given USB connector, such as Type-C,
Type-B(micro). See connector/usb-connector.yaml.
role-switch-default-mode:
description:
Indicates if usb-role-switch is enabled, the device default operation
mode of controller while usb role is USB_ROLE_NONE.
$ref: /schemas/types.yaml#/definitions/string
enum: [host, peripheral]
default: peripheral
additionalProperties: true
examples:
- |
usb@4a030000 {
compatible = "snps,dwc3";
reg = <0x4a030000 0xcfff>;
interrupts = <0 92 4>;
usb-phy = <&usb2_phy>, <&usb3_phy>;
maximum-speed = "super-speed";
dr_mode = "otg";
phy_type = "utmi_wide";
otg-rev = <0x0200>;
adp-disable;
};

View File

@ -9,18 +9,31 @@ title: Generic USB Host Controller Device Tree Bindings
maintainers:
- Greg Kroah-Hartman <gregkh@linuxfoundation.org>
allOf:
- $ref: usb.yaml#
properties:
$nodename:
pattern: "^usb(@.*)?"
companion:
description: Phandle of a companion device
$ref: /schemas/types.yaml#/definitions/phandle
phys:
$ref: /schemas/types.yaml#/definitions/phandle-array
tpl-support:
description:
List of all the USB PHYs on this HCD
Indicates if the Targeted Peripheral List is supported for given
targeted hosts (non-PC hosts).
type: boolean
phy-names:
description:
Name specifier for the USB PHY
"#address-cells":
const: 1
"#size-cells":
const: 0
patternProperties:
"^.*@[0-9a-f]{1,2}$":
description: The hard wired USB devices
type: object
$ref: /usb/usb-device.yaml
additionalProperties: true
@ -29,4 +42,11 @@ examples:
usb {
phys = <&usb2_phy1>, <&usb3_phy1>;
phy-names = "usb";
#address-cells = <1>;
#size-cells = <0>;
hub@1 {
compatible = "usb5e3,610";
reg = <1>;
};
};

View File

@ -1,41 +0,0 @@
USB xHCI controllers
Required properties:
- compatible: should be one or more of
- "generic-xhci" for generic XHCI device
- "marvell,armada3700-xhci" for Armada 37xx SoCs
- "marvell,armada-375-xhci" for Armada 375 SoCs
- "marvell,armada-380-xhci" for Armada 38x SoCs
- "brcm,bcm7445-xhci" for Broadcom STB SoCs with XHCI
- "xhci-platform" (deprecated)
When compatible with the generic version, nodes must list the
SoC-specific version corresponding to the platform first
followed by the generic version.
- reg: should contain address and length of the standard XHCI
register set for the device.
- interrupts: one XHCI interrupt should be described here.
Optional properties:
- clocks: reference to the clocks
- clock-names: mandatory if there is a second clock, in this case
the name must be "core" for the first clock and "reg" for the
second one
- usb2-lpm-disable: indicate if we don't want to enable USB2 HW LPM
- usb3-lpm-capable: determines if platform is USB3 LPM capable
- quirk-broken-port-ped: set if the controller has broken port disable mechanism
- imod-interval-ns: default interrupt moderation interval is 5000ns
- phys : see usb-hcd.yaml in the current directory
additionally the properties from usb-hcd.yaml (in the current directory) are
supported.
Example:
usb@f0931000 {
compatible = "generic-xhci";
reg = <0xf0931000 0x8c8>;
interrupts = <0x0 0x4e 0x0>;
};

View File

@ -0,0 +1,42 @@
# SPDX-License-Identifier: GPL-2.0
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/usb-xhci.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Generic USB xHCI Controller Device Tree Bindings
maintainers:
- Mathias Nyman <mathias.nyman@intel.com>
allOf:
- $ref: "usb-hcd.yaml#"
properties:
usb2-lpm-disable:
description: Indicates if we don't want to enable USB2 HW LPM
type: boolean
usb3-lpm-capable:
description: Determines if platform is USB3 LPM capable
type: boolean
quirk-broken-port-ped:
description: Set if the controller has broken port disable mechanism
type: boolean
imod-interval-ns:
description: Interrupt moderation interval
default: 5000
additionalProperties: true
examples:
- |
usb@f0930000 {
compatible = "generic-xhci";
reg = <0xf0930000 0x8c8>;
interrupts = <0x0 0x4e 0x0>;
usb2-lpm-disable;
usb3-lpm-capable;
};

View File

@ -0,0 +1,63 @@
# SPDX-License-Identifier: GPL-2.0
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/usb.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Generic USB Controller Device Tree Bindings
maintainers:
- Greg Kroah-Hartman <gregkh@linuxfoundation.org>
select: false
properties:
$nodename:
pattern: "^usb(@.*)?"
phys:
$ref: /schemas/types.yaml#/definitions/phandle-array
description:
List of all the USB PHYs on this HCD
phy-names:
description:
Name specifier for the USB PHY
usb-phy:
$ref: /schemas/types.yaml#/definitions/phandle-array
description:
List of all the USB PHYs on this HCD to be accepted by the legacy USB
Physical Layer subsystem.
deprecated: true
phy_type:
description:
Tells USB controllers that we want to configure the core to support a
UTMI+ PHY with an 8- or 16-bit interface if UTMI+ is selected, UTMI+ low
pin interface if ULPI is specified, Serial core/PHY interconnect if
serial is specified and High-Speed Inter-Chip feature if HSIC is
selected. In case this isn't passed via DT, USB controllers should
default to HW capability.
$ref: /schemas/types.yaml#/definitions/string
enum: [utmi, utmi_wide, ulpi, serial, hsic]
maximum-speed:
description:
Tells USB controllers we want to work up to a certain speed. In case this
isn't passed via DT, USB controllers should default to their maximum HW
capability.
$ref: /schemas/types.yaml#/definitions/string
enum:
- low-speed
- full-speed
- high-speed
- super-speed
- super-speed-plus
- super-speed-plus-gen2x1
- super-speed-plus-gen1x2
- super-speed-plus-gen2x2
additionalProperties: true
...

View File

@ -1,7 +1,7 @@
* Freescale i.MX non-core registers
Required properties:
- #index-cells: Cells used to descibe usb controller index. Should be <1>
- #index-cells: Cells used to describe usb controller index. Should be <1>
- compatible: Should be one of below:
"fsl,imx6q-usbmisc" for imx6q
"fsl,vf610-usbmisc" for Vybrid vf610

View File

@ -91,9 +91,9 @@ The ECM function provides these attributes in its function directory:
and after creating the functions/ecm.<instance name> they contain default
values: qmult is 5, dev_addr and host_addr are randomly selected.
Except for ifname they can be written to until the function is linked to a
configuration. The ifname is read-only and contains the name of the interface
which was assigned by the net core, e. g. usb0.
The ifname can be written to if the function is not bound. A write must be an
interface pattern such as "usb%d", which will cause the net core to choose the
next free usbX interface. By default, it is set to "usb%d".
Testing the ECM function
------------------------
@ -131,9 +131,9 @@ The ECM subset function provides these attributes in its function directory:
and after creating the functions/ecm.<instance name> they contain default
values: qmult is 5, dev_addr and host_addr are randomly selected.
Except for ifname they can be written to until the function is linked to a
configuration. The ifname is read-only and contains the name of the interface
which was assigned by the net core, e. g. usb0.
The ifname can be written to if the function is not bound. A write must be an
interface pattern such as "usb%d", which will cause the net core to choose the
next free usbX interface. By default, it is set to "usb%d".
Testing the ECM subset function
-------------------------------
@ -171,9 +171,9 @@ The EEM function provides these attributes in its function directory:
and after creating the functions/eem.<instance name> they contain default
values: qmult is 5, dev_addr and host_addr are randomly selected.
Except for ifname they can be written to until the function is linked to a
configuration. The ifname is read-only and contains the name of the interface
which was assigned by the net core, e. g. usb0.
The ifname can be written to if the function is not bound. A write must be an
interface pattern such as "usb%d", which will cause the net core to choose the
next free usbX interface. By default, it is set to "usb%d".
Testing the EEM function
------------------------
@ -453,9 +453,9 @@ The NCM function provides these attributes in its function directory:
and after creating the functions/ncm.<instance name> they contain default
values: qmult is 5, dev_addr and host_addr are randomly selected.
Except for ifname they can be written to until the function is linked to a
configuration. The ifname is read-only and contains the name of the interface
which was assigned by the net core, e. g. usb0.
The ifname can be written to if the function is not bound. A write must be an
interface pattern such as "usb%d", which will cause the net core to choose the
next free usbX interface. By default, it is set to "usb%d".
Testing the NCM function
------------------------
@ -591,9 +591,9 @@ The RNDIS function provides these attributes in its function directory:
and after creating the functions/rndis.<instance name> they contain default
values: qmult is 5, dev_addr and host_addr are randomly selected.
Except for ifname they can be written to until the function is linked to a
configuration. The ifname is read-only and contains the name of the interface
which was assigned by the net core, e. g. usb0.
The ifname can be written to if the function is not bound. A write must be an
interface pattern such as "usb%d", which will cause the net core to choose the
next free usbX interface. By default, it is set to "usb%d".
Testing the RNDIS function
--------------------------

View File

@ -2,83 +2,93 @@
USB Raw Gadget
==============
USB Raw Gadget is a kernel module that provides a userspace interface for
the USB Gadget subsystem. Essentially it allows to emulate USB devices
from userspace. Enabled with CONFIG_USB_RAW_GADGET. Raw Gadget is
currently a strictly debugging feature and shouldn't be used in
production, use GadgetFS instead.
USB Raw Gadget is a gadget driver that gives userspace low-level control over
the gadget's communication process.
Like any other gadget driver, Raw Gadget implements USB devices via the
USB gadget API. Unlike most gadget drivers, Raw Gadget does not implement
any concrete USB functions itself but requires userspace to do that.
Raw Gadget is currently a strictly debugging feature and should not be used
in production. Use GadgetFS instead.
Enabled with CONFIG_USB_RAW_GADGET.
Comparison to GadgetFS
~~~~~~~~~~~~~~~~~~~~~~
Raw Gadget is similar to GadgetFS, but provides a more low-level and
direct access to the USB Gadget layer for the userspace. The key
differences are:
Raw Gadget is similar to GadgetFS but provides more direct access to the
USB gadget layer for userspace. The key differences are:
1. Every USB request is passed to the userspace to get a response, while
1. Raw Gadget passes every USB request to userspace to get a response, while
GadgetFS responds to some USB requests internally based on the provided
descriptors. However note, that the UDC driver might respond to some
requests on its own and never forward them to the Gadget layer.
descriptors. Note that the UDC driver might respond to some requests on
its own and never forward them to the gadget layer.
2. GadgetFS performs some sanity checks on the provided USB descriptors,
while Raw Gadget allows you to provide arbitrary data as responses to
USB requests.
2. Raw Gadget allows providing arbitrary data as responses to USB requests,
while GadgetFS performs sanity checks on the provided USB descriptors.
This makes Raw Gadget suitable for fuzzing by providing malformed data as
responses to USB requests.
3. Raw Gadget provides a way to select a UDC device/driver to bind to,
while GadgetFS currently binds to the first available UDC.
while GadgetFS currently binds to the first available UDC. This allows
having multiple Raw Gadget instances bound to different UDCs.
4. Raw Gadget explicitly exposes information about endpoints addresses and
capabilities allowing a user to write UDC-agnostic gadgets.
capabilities. This allows the user to write UDC-agnostic gadgets.
5. Raw Gadget has ioctl-based interface instead of a filesystem-based one.
5. Raw Gadget has an ioctl-based interface instead of a filesystem-based
one.
Userspace interface
~~~~~~~~~~~~~~~~~~~
To create a Raw Gadget instance open /dev/raw-gadget. Multiple raw-gadget
instances (bound to different UDCs) can be used at the same time. The
interaction with the opened file happens through the ioctl() calls, see
comments in include/uapi/linux/usb/raw_gadget.h for details.
The user can interact with Raw Gadget by opening ``/dev/raw-gadget`` and
issuing ioctl calls; see the comments in include/uapi/linux/usb/raw_gadget.h
for details. Multiple Raw Gadget instances (bound to different UDCs) can be
used at the same time.
The typical usage of Raw Gadget looks like:
A typical usage scenario of Raw Gadget:
1. Open Raw Gadget instance via /dev/raw-gadget.
2. Initialize the instance via USB_RAW_IOCTL_INIT.
3. Launch the instance with USB_RAW_IOCTL_RUN.
4. In a loop issue USB_RAW_IOCTL_EVENT_FETCH calls to receive events from
Raw Gadget and react to those depending on what kind of USB device
needs to be emulated.
1. Create a Raw Gadget instance by opening ``/dev/raw-gadget``.
2. Initialize the instance via ``USB_RAW_IOCTL_INIT``.
3. Launch the instance with ``USB_RAW_IOCTL_RUN``.
4. In a loop issue ``USB_RAW_IOCTL_EVENT_FETCH`` to receive events from
Raw Gadget and react to those depending on what kind of USB gadget must
be implemented.
Note, that some UDC drivers have fixed addresses assigned to endpoints, and
therefore arbitrary endpoint addresses can't be used in the descriptors.
Nevertheles, Raw Gadget provides a UDC-agnostic way to write USB gadgets.
Once a USB_RAW_EVENT_CONNECT event is received via USB_RAW_IOCTL_EVENT_FETCH,
the USB_RAW_IOCTL_EPS_INFO ioctl can be used to find out information about
endpoints that the UDC driver has. Based on that information, the user must
chose UDC endpoints that will be used for the gadget being emulated, and
properly assign addresses in endpoint descriptors.
Note that some UDC drivers have fixed addresses assigned to endpoints, and
therefore arbitrary endpoint addresses cannot be used in the descriptors.
Nevertheless, Raw Gadget provides a UDC-agnostic way to write USB gadgets.
Once ``USB_RAW_EVENT_CONNECT`` is received via ``USB_RAW_IOCTL_EVENT_FETCH``,
``USB_RAW_IOCTL_EPS_INFO`` can be used to find out information about the
endpoints that the UDC driver has. Based on that, userspace must choose UDC
endpoints for the gadget and assign addresses in the endpoint descriptors
correspondingly.
You can find usage examples (along with a test suite) here:
Raw Gadget usage examples and a test suite:
https://github.com/xairy/raw-gadget
Internal details
~~~~~~~~~~~~~~~~
Currently every endpoint read/write ioctl submits a USB request and waits until
its completion. This is the desired mode for coverage-guided fuzzing (as we'd
like all USB request processing happen during the lifetime of a syscall),
and must be kept in the implementation. (This might be slow for real world
applications, thus the O_NONBLOCK improvement suggestion below.)
Every Raw Gadget endpoint read/write ioctl submits a USB request and waits
until its completion. This is done deliberately to assist with coverage-guided
fuzzing by having a single syscall fully process a single USB request. This
feature must be kept in the implementation.
Potential future improvements
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Report more events (suspend, resume, etc.) through USB_RAW_IOCTL_EVENT_FETCH.
- Report more events (suspend, resume, etc.) through
``USB_RAW_IOCTL_EVENT_FETCH``.
- Support O_NONBLOCK I/O.
- Support ``O_NONBLOCK`` I/O. This would be another mode of operation, where
Raw Gadget would not wait until the completion of each USB request.
- Support USB 3 features (accept SS endpoint companion descriptor when
enabling endpoints; allow providing stream_id for bulk transfers).
enabling endpoints; allow providing ``stream_id`` for bulk transfers).
- Support ISO transfer features (expose frame_number for completed requests).
- Support ISO transfer features (expose ``frame_number`` for completed
requests).

View File

@ -2071,7 +2071,7 @@ M: Chunfeng Yun <chunfeng.yun@mediatek.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
L: linux-mediatek@lists.infradead.org (moderated for non-subscribers)
S: Maintained
F: Documentation/devicetree/bindings/phy/phy-mtk-*
F: Documentation/devicetree/bindings/phy/mediatek,*
F: drivers/phy/mediatek/
ARM/Microchip (AT91) SoC support
@ -3855,6 +3855,15 @@ S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb.git
F: Documentation/devicetree/bindings/usb/cdns,usb3.yaml
F: drivers/usb/cdns3/
X: drivers/usb/cdns3/cdnsp*
CADENCE USBSSP DRD IP DRIVER
M: Pawel Laszczak <pawell@cadence.com>
L: linux-usb@vger.kernel.org
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb.git
F: drivers/usb/cdns3/
X: drivers/usb/cdns3/cdns3*
CADET FM/AM RADIO RECEIVER DRIVER
M: Hans Verkuil <hverkuil@xs4all.nl>
@ -11229,6 +11238,8 @@ L: linux-usb@vger.kernel.org
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
L: linux-mediatek@lists.infradead.org (moderated for non-subscribers)
S: Maintained
F: Documentation/devicetree/bindings/usb/mediatek,*
F: drivers/usb/host/xhci-mtk*
F: drivers/usb/mtu3/
MEGACHIPS STDPXXXX-GE-B850V3-FW LVDS/DP++ BRIDGES

View File

@ -238,12 +238,13 @@ CONFIG_USB=y
CONFIG_USB_XHCI_HCD=y
CONFIG_USB_XHCI_TEGRA=y
CONFIG_USB_EHCI_HCD=y
CONFIG_USB_EHCI_TEGRA=y
CONFIG_USB_ACM=y
CONFIG_USB_WDM=y
CONFIG_USB_STORAGE=y
CONFIG_USB_CHIPIDEA=y
CONFIG_USB_CHIPIDEA_UDC=y
CONFIG_USB_CHIPIDEA_HOST=y
CONFIG_USB_CHIPIDEA_TEGRA=y
CONFIG_USB_GADGET=y
CONFIG_MMC=y
CONFIG_MMC_BLOCK_MINORS=16

View File

@ -127,6 +127,21 @@
status = "okay";
};
&usb3_phy1 {
status = "okay";
};
&usb3_1 {
status = "okay";
};
&usb_dwc3_1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_usb1_vbus>;
dr_mode = "host";
status = "okay";
};
&usdhc2 {
assigned-clocks = <&clk IMX8MP_CLK_USDHC2>;
assigned-clock-rates = <400000000>;
@ -232,6 +247,12 @@
>;
};
pinctrl_usb1_vbus: usb1grp {
fsl,pins = <
MX8MP_IOMUXC_GPIO1_IO14__USB2_OTG_PWR 0x19
>;
};
pinctrl_usdhc2: usdhc2grp {
fsl,pins = <
MX8MP_IOMUXC_SD2_CLK__USDHC2_CLK 0x190

View File

@ -803,5 +803,87 @@
reg = <0x3d800000 0x400000>;
interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
};
usb3_phy0: usb-phy@381f0040 {
compatible = "fsl,imx8mp-usb-phy";
reg = <0x381f0040 0x40>;
clocks = <&clk IMX8MP_CLK_USB_PHY_ROOT>;
clock-names = "phy";
assigned-clocks = <&clk IMX8MP_CLK_USB_PHY_REF>;
assigned-clock-parents = <&clk IMX8MP_CLK_24M>;
#phy-cells = <0>;
status = "disabled";
};
usb3_0: usb@32f10100 {
compatible = "fsl,imx8mp-dwc3";
reg = <0x32f10100 0x8>;
clocks = <&clk IMX8MP_CLK_HSIO_ROOT>,
<&clk IMX8MP_CLK_USB_ROOT>;
clock-names = "hsio", "suspend";
interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
#size-cells = <1>;
dma-ranges = <0x40000000 0x40000000 0xc0000000>;
ranges;
status = "disabled";
usb_dwc3_0: dwc3@38100000 {
compatible = "snps,dwc3";
reg = <0x38100000 0x10000>;
clocks = <&clk IMX8MP_CLK_HSIO_AXI>,
<&clk IMX8MP_CLK_USB_CORE_REF>,
<&clk IMX8MP_CLK_USB_ROOT>;
clock-names = "bus_early", "ref", "suspend";
assigned-clocks = <&clk IMX8MP_CLK_HSIO_AXI>;
assigned-clock-parents = <&clk IMX8MP_SYS_PLL2_500M>;
assigned-clock-rates = <500000000>;
interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>;
phys = <&usb3_phy0>, <&usb3_phy0>;
phy-names = "usb2-phy", "usb3-phy";
snps,dis-u2-freeclk-exists-quirk;
};
};
usb3_phy1: usb-phy@382f0040 {
compatible = "fsl,imx8mp-usb-phy";
reg = <0x382f0040 0x40>;
clocks = <&clk IMX8MP_CLK_USB_PHY_ROOT>;
clock-names = "phy";
assigned-clocks = <&clk IMX8MP_CLK_USB_PHY_REF>;
assigned-clock-parents = <&clk IMX8MP_CLK_24M>;
#phy-cells = <0>;
};
usb3_1: usb@32f10108 {
compatible = "fsl,imx8mp-dwc3";
reg = <0x32f10108 0x8>;
clocks = <&clk IMX8MP_CLK_HSIO_ROOT>,
<&clk IMX8MP_CLK_USB_ROOT>;
clock-names = "hsio", "suspend";
interrupts = <GIC_SPI 149 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
#size-cells = <1>;
dma-ranges = <0x40000000 0x40000000 0xc0000000>;
ranges;
status = "disabled";
usb_dwc3_1: dwc3@38200000 {
compatible = "snps,dwc3";
reg = <0x38200000 0x10000>;
clocks = <&clk IMX8MP_CLK_HSIO_AXI>,
<&clk IMX8MP_CLK_USB_CORE_REF>,
<&clk IMX8MP_CLK_USB_ROOT>;
clock-names = "bus_early", "ref", "suspend";
assigned-clocks = <&clk IMX8MP_CLK_HSIO_AXI>;
assigned-clock-parents = <&clk IMX8MP_SYS_PLL2_500M>;
assigned-clock-rates = <500000000>;
interrupts = <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>;
phys = <&usb3_phy1>, <&usb3_phy1>;
phy-names = "usb2-phy", "usb3-phy";
snps,dis-u2-freeclk-exists-quirk;
};
};
};
};

View File

@ -281,10 +281,16 @@ bool osc_sb_apei_support_acked;
bool osc_pc_lpi_support_confirmed;
EXPORT_SYMBOL_GPL(osc_pc_lpi_support_confirmed);
/*
* ACPI 6.4 Operating System Capabilities for USB.
*/
bool osc_sb_native_usb4_support_confirmed;
EXPORT_SYMBOL_GPL(osc_sb_native_usb4_support_confirmed);
static u8 sb_uuid_str[] = "0811B06E-4A27-44F9-8D60-3CBBC22E7B48";
static void acpi_bus_osc_support(void)
static void acpi_bus_osc_negotiate_platform_control(void)
{
u32 capbuf[2];
u32 capbuf[2], *capbuf_ret;
struct acpi_osc_context context = {
.uuid_str = sb_uuid_str,
.rev = 1,
@ -317,21 +323,109 @@ static void acpi_bus_osc_support(void)
if (IS_ENABLED(CONFIG_SCHED_MC_PRIO))
capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_CPC_DIVERSE_HIGH_SUPPORT;
if (IS_ENABLED(CONFIG_USB4))
capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_NATIVE_USB4_SUPPORT;
if (!ghes_disable)
capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_APEI_SUPPORT;
if (ACPI_FAILURE(acpi_get_handle(NULL, "\\_SB", &handle)))
return;
if (ACPI_SUCCESS(acpi_run_osc(handle, &context))) {
u32 *capbuf_ret = context.ret.pointer;
if (context.ret.length > OSC_SUPPORT_DWORD) {
osc_sb_apei_support_acked =
capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_APEI_SUPPORT;
osc_pc_lpi_support_confirmed =
capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_PCLPI_SUPPORT;
}
if (ACPI_FAILURE(acpi_run_osc(handle, &context)))
return;
capbuf_ret = context.ret.pointer;
if (context.ret.length <= OSC_SUPPORT_DWORD) {
kfree(context.ret.pointer);
return;
}
/* do we need to check other returned cap? Sounds no */
/*
* Now run _OSC again with query flag clear and with the caps
* supported by both the OS and the platform.
*/
capbuf[OSC_QUERY_DWORD] = 0;
capbuf[OSC_SUPPORT_DWORD] = capbuf_ret[OSC_SUPPORT_DWORD];
kfree(context.ret.pointer);
if (ACPI_FAILURE(acpi_run_osc(handle, &context)))
return;
capbuf_ret = context.ret.pointer;
if (context.ret.length > OSC_SUPPORT_DWORD) {
osc_sb_apei_support_acked =
capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_APEI_SUPPORT;
osc_pc_lpi_support_confirmed =
capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_PCLPI_SUPPORT;
osc_sb_native_usb4_support_confirmed =
capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_NATIVE_USB4_SUPPORT;
}
kfree(context.ret.pointer);
}
/*
* Native control of USB4 capabilities. If any of the tunneling bits is
* set it means OS is in control and we use software based connection
* manager.
*/
u32 osc_sb_native_usb4_control;
EXPORT_SYMBOL_GPL(osc_sb_native_usb4_control);
static void acpi_bus_decode_usb_osc(const char *msg, u32 bits)
{
printk(KERN_INFO PREFIX "%s USB3%c DisplayPort%c PCIe%c XDomain%c\n", msg,
(bits & OSC_USB_USB3_TUNNELING) ? '+' : '-',
(bits & OSC_USB_DP_TUNNELING) ? '+' : '-',
(bits & OSC_USB_PCIE_TUNNELING) ? '+' : '-',
(bits & OSC_USB_XDOMAIN) ? '+' : '-');
}
static u8 sb_usb_uuid_str[] = "23A0D13A-26AB-486C-9C5F-0FFA525A575A";
static void acpi_bus_osc_negotiate_usb_control(void)
{
u32 capbuf[3];
struct acpi_osc_context context = {
.uuid_str = sb_usb_uuid_str,
.rev = 1,
.cap.length = sizeof(capbuf),
.cap.pointer = capbuf,
};
acpi_handle handle;
acpi_status status;
u32 control;
if (!osc_sb_native_usb4_support_confirmed)
return;
if (ACPI_FAILURE(acpi_get_handle(NULL, "\\_SB", &handle)))
return;
control = OSC_USB_USB3_TUNNELING | OSC_USB_DP_TUNNELING |
OSC_USB_PCIE_TUNNELING | OSC_USB_XDOMAIN;
capbuf[OSC_QUERY_DWORD] = 0;
capbuf[OSC_SUPPORT_DWORD] = 0;
capbuf[OSC_CONTROL_DWORD] = control;
status = acpi_run_osc(handle, &context);
if (ACPI_FAILURE(status))
return;
if (context.ret.length != sizeof(capbuf)) {
printk(KERN_INFO PREFIX "USB4 _OSC: returned invalid length buffer\n");
goto out_free;
}
osc_sb_native_usb4_control =
control & ((u32 *)context.ret.pointer)[OSC_CONTROL_DWORD];
acpi_bus_decode_usb_osc("USB4 _OSC: OS supports", control);
acpi_bus_decode_usb_osc("USB4 _OSC: OS controls",
osc_sb_native_usb4_control);
out_free:
kfree(context.ret.pointer);
}
/* --------------------------------------------------------------------------
@ -1168,7 +1262,8 @@ static int __init acpi_bus_init(void)
* _OSC method may exist in module level code,
* so it must be run after ACPI_FULL_INITIALIZATION
*/
acpi_bus_osc_support();
acpi_bus_osc_negotiate_platform_control();
acpi_bus_osc_negotiate_usb_control();
/*
* _PDC control method may load dynamic SSDT tables,

View File

@ -24,6 +24,7 @@ struct swnode {
struct swnode *parent;
unsigned int allocated:1;
unsigned int managed:1;
};
static DEFINE_IDA(swnode_root_ids);
@ -48,6 +49,19 @@ EXPORT_SYMBOL_GPL(is_software_node);
struct swnode, fwnode) : NULL; \
})
static inline struct swnode *dev_to_swnode(struct device *dev)
{
struct fwnode_handle *fwnode = dev_fwnode(dev);
if (!fwnode)
return NULL;
if (!is_software_node(fwnode))
fwnode = fwnode->secondary;
return to_swnode(fwnode);
}
static struct swnode *
software_node_to_swnode(const struct software_node *node)
{
@ -843,22 +857,99 @@ void fwnode_remove_software_node(struct fwnode_handle *fwnode)
}
EXPORT_SYMBOL_GPL(fwnode_remove_software_node);
/**
* device_add_software_node - Assign software node to a device
* @dev: The device the software node is meant for.
* @swnode: The software node.
*
* This function will register @swnode and make it the secondary firmware node
* pointer of @dev. If @dev has no primary node, then @swnode will become the primary
* node.
*/
int device_add_software_node(struct device *dev, const struct software_node *swnode)
{
int ret;
/* Only one software node per device. */
if (dev_to_swnode(dev))
return -EBUSY;
ret = software_node_register(swnode);
if (ret)
return ret;
set_secondary_fwnode(dev, software_node_fwnode(swnode));
return 0;
}
EXPORT_SYMBOL_GPL(device_add_software_node);
/**
* device_remove_software_node - Remove device's software node
* @dev: The device with the software node.
*
* This function will unregister the software node of @dev.
*/
void device_remove_software_node(struct device *dev)
{
struct swnode *swnode;
swnode = dev_to_swnode(dev);
if (!swnode)
return;
software_node_notify(dev, KOBJ_REMOVE);
set_secondary_fwnode(dev, NULL);
kobject_put(&swnode->kobj);
}
EXPORT_SYMBOL_GPL(device_remove_software_node);
/**
* device_create_managed_software_node - Create a software node for a device
* @dev: The device the software node is assigned to.
* @properties: Device properties for the software node.
* @parent: Parent of the software node.
*
* Creates a software node as a managed resource for @dev, which means the
* lifetime of the newly created software node is tied to the lifetime of @dev.
* Software nodes created with this function should not be reused or shared
* because of that. The function takes a deep copy of @properties for the
* software node.
*
* Since the new software node is assigned directly to @dev, and since it should
* not be shared, it is not returned to the caller. The function returns 0 on
* success, and errno in case of an error.
*/
int device_create_managed_software_node(struct device *dev,
const struct property_entry *properties,
const struct software_node *parent)
{
struct fwnode_handle *p = software_node_fwnode(parent);
struct fwnode_handle *fwnode;
if (parent && !p)
return -EINVAL;
fwnode = fwnode_create_software_node(properties, p);
if (IS_ERR(fwnode))
return PTR_ERR(fwnode);
to_swnode(fwnode)->managed = true;
set_secondary_fwnode(dev, fwnode);
return 0;
}
EXPORT_SYMBOL_GPL(device_create_managed_software_node);
int software_node_notify(struct device *dev, unsigned long action)
{
struct fwnode_handle *fwnode = dev_fwnode(dev);
struct swnode *swnode;
int ret;
if (!fwnode)
swnode = dev_to_swnode(dev);
if (!swnode)
return 0;
if (!is_software_node(fwnode))
fwnode = fwnode->secondary;
if (!is_software_node(fwnode))
return 0;
swnode = to_swnode(fwnode);
switch (action) {
case KOBJ_ADD:
ret = sysfs_create_link(&dev->kobj, &swnode->kobj,
@ -878,6 +969,11 @@ int software_node_notify(struct device *dev, unsigned long action)
sysfs_remove_link(&swnode->kobj, dev_name(dev));
sysfs_remove_link(&dev->kobj, "software_node");
kobject_put(&swnode->kobj);
if (swnode->managed) {
set_secondary_fwnode(dev, NULL);
kobject_put(&swnode->kobj);
}
break;
default:
break;

View File

@ -44,8 +44,13 @@ struct cros_typec_port {
/* Initial capabilities for the port. */
struct typec_capability caps;
struct typec_partner *partner;
struct typec_cable *cable;
/* SOP' plug. */
struct typec_plug *plug;
/* Port partner PD identity info. */
struct usb_pd_identity p_identity;
/* Port cable PD identity info. */
struct usb_pd_identity c_identity;
struct typec_switch *ori_sw;
struct typec_mux *mux;
struct usb_role_switch *role_sw;
@ -57,10 +62,12 @@ struct cros_typec_port {
/* Port alt modes. */
struct typec_altmode p_altmode[CROS_EC_ALTMODE_MAX];
/* Flag indicating that PD discovery data parsing is completed. */
bool disc_done;
struct ec_response_typec_discovery *sop_disc;
/* Flag indicating that PD partner discovery data parsing is completed. */
bool sop_disc_done;
bool sop_prime_disc_done;
struct ec_response_typec_discovery *disc_data;
struct list_head partner_mode_list;
struct list_head plug_mode_list;
};
/* Platform-specific data for the Chrome OS EC Type C controller. */
@ -74,6 +81,7 @@ struct cros_typec_data {
struct notifier_block nb;
struct work_struct port_work;
bool typec_cmd_supported;
bool needs_mux_ack;
};
static int cros_typec_parse_port_props(struct typec_capability *cap,
@ -180,12 +188,15 @@ static int cros_typec_add_partner(struct cros_typec_data *typec, int port_num,
return ret;
}
static void cros_typec_unregister_altmodes(struct cros_typec_data *typec, int port_num)
static void cros_typec_unregister_altmodes(struct cros_typec_data *typec, int port_num,
bool is_partner)
{
struct cros_typec_port *port = typec->ports[port_num];
struct cros_typec_altmode_node *node, *tmp;
struct list_head *head;
list_for_each_entry_safe(node, tmp, &port->partner_mode_list, list) {
head = is_partner ? &port->partner_mode_list : &port->plug_mode_list;
list_for_each_entry_safe(node, tmp, head, list) {
list_del(&node->list);
typec_unregister_altmode(node->amode);
devm_kfree(typec->dev, node);
@ -197,7 +208,7 @@ static void cros_typec_remove_partner(struct cros_typec_data *typec,
{
struct cros_typec_port *port = typec->ports[port_num];
cros_typec_unregister_altmodes(typec, port_num);
cros_typec_unregister_altmodes(typec, port_num, true);
port->state.alt = NULL;
port->state.mode = TYPEC_STATE_USB;
@ -210,7 +221,22 @@ static void cros_typec_remove_partner(struct cros_typec_data *typec,
typec_unregister_partner(port->partner);
port->partner = NULL;
memset(&port->p_identity, 0, sizeof(port->p_identity));
port->disc_done = false;
port->sop_disc_done = false;
}
static void cros_typec_remove_cable(struct cros_typec_data *typec,
int port_num)
{
struct cros_typec_port *port = typec->ports[port_num];
cros_typec_unregister_altmodes(typec, port_num, false);
typec_unregister_plug(port->plug);
port->plug = NULL;
typec_unregister_cable(port->cable);
port->cable = NULL;
memset(&port->c_identity, 0, sizeof(port->c_identity));
port->sop_prime_disc_done = false;
}
static void cros_unregister_ports(struct cros_typec_data *typec)
@ -224,6 +250,9 @@ static void cros_unregister_ports(struct cros_typec_data *typec)
if (typec->ports[i]->partner)
cros_typec_remove_partner(typec, i);
if (typec->ports[i]->cable)
cros_typec_remove_cable(typec, i);
usb_role_switch_put(typec->ports[i]->role_sw);
typec_switch_put(typec->ports[i]->ori_sw);
typec_mux_put(typec->ports[i]->mux);
@ -323,13 +352,14 @@ static int cros_typec_init_ports(struct cros_typec_data *typec)
cros_typec_register_port_altmodes(typec, port_num);
cros_port->sop_disc = devm_kzalloc(dev, EC_PROTO2_MAX_RESPONSE_SIZE, GFP_KERNEL);
if (!cros_port->sop_disc) {
cros_port->disc_data = devm_kzalloc(dev, EC_PROTO2_MAX_RESPONSE_SIZE, GFP_KERNEL);
if (!cros_port->disc_data) {
ret = -ENOMEM;
goto unregister_ports;
}
INIT_LIST_HEAD(&cros_port->partner_mode_list);
INIT_LIST_HEAD(&cros_port->plug_mode_list);
}
return 0;
@ -502,6 +532,7 @@ static int cros_typec_configure_mux(struct cros_typec_data *typec, int port_num,
struct ec_response_usb_pd_control_v2 *pd_ctrl)
{
struct cros_typec_port *port = typec->ports[port_num];
struct ec_params_usb_pd_mux_ack mux_ack;
enum typec_orientation orientation;
int ret;
@ -541,6 +572,18 @@ static int cros_typec_configure_mux(struct cros_typec_data *typec, int port_num,
mux_flags);
}
if (!typec->needs_mux_ack)
return ret;
/* Sending Acknowledgment to EC */
mux_ack.port = port_num;
if (cros_typec_ec_command(typec, 0, EC_CMD_USB_PD_MUX_ACK, &mux_ack,
sizeof(mux_ack), NULL, 0) < 0)
dev_warn(typec->dev,
"Failed to send Mux ACK to EC for port: %d\n",
port_num);
return ret;
}
@ -598,6 +641,9 @@ static void cros_typec_set_port_params_v1(struct cros_typec_data *typec,
if (!typec->ports[port_num]->partner)
return;
cros_typec_remove_partner(typec, port_num);
if (typec->ports[port_num]->cable)
cros_typec_remove_cable(typec, port_num);
}
}
@ -612,13 +658,18 @@ static int cros_typec_get_mux_info(struct cros_typec_data *typec, int port_num,
sizeof(req), resp, sizeof(*resp));
}
static int cros_typec_register_altmodes(struct cros_typec_data *typec, int port_num)
/*
* Helper function to register partner/plug altmodes.
*/
static int cros_typec_register_altmodes(struct cros_typec_data *typec, int port_num,
bool is_partner)
{
struct cros_typec_port *port = typec->ports[port_num];
struct ec_response_typec_discovery *sop_disc = port->sop_disc;
struct ec_response_typec_discovery *sop_disc = port->disc_data;
struct cros_typec_altmode_node *node;
struct typec_altmode_desc desc;
struct typec_altmode *amode;
int num_altmodes = 0;
int ret = 0;
int i, j;
@ -629,7 +680,11 @@ static int cros_typec_register_altmodes(struct cros_typec_data *typec, int port_
desc.mode = j;
desc.vdo = sop_disc->svids[i].mode_vdo[j];
amode = typec_partner_register_altmode(port->partner, &desc);
if (is_partner)
amode = typec_partner_register_altmode(port->partner, &desc);
else
amode = typec_plug_register_altmode(port->plug, &desc);
if (IS_ERR(amode)) {
ret = PTR_ERR(amode);
goto err_cleanup;
@ -644,27 +699,140 @@ static int cros_typec_register_altmodes(struct cros_typec_data *typec, int port_
}
node->amode = amode;
list_add_tail(&node->list, &port->partner_mode_list);
if (is_partner)
list_add_tail(&node->list, &port->partner_mode_list);
else
list_add_tail(&node->list, &port->plug_mode_list);
num_altmodes++;
}
}
if (is_partner)
ret = typec_partner_set_num_altmodes(port->partner, num_altmodes);
else
ret = typec_plug_set_num_altmodes(port->plug, num_altmodes);
if (ret < 0) {
dev_err(typec->dev, "Unable to set %s num_altmodes for port: %d\n",
is_partner ? "partner" : "plug", port_num);
goto err_cleanup;
}
return 0;
err_cleanup:
cros_typec_unregister_altmodes(typec, port_num);
cros_typec_unregister_altmodes(typec, port_num, is_partner);
return ret;
}
static int cros_typec_handle_sop_disc(struct cros_typec_data *typec, int port_num)
/*
* Parse the PD identity data from the EC PD discovery responses and copy that to the supplied
* PD identity struct.
*/
static void cros_typec_parse_pd_identity(struct usb_pd_identity *id,
struct ec_response_typec_discovery *disc)
{
int i;
/* First, update the PD identity VDOs for the partner. */
if (disc->identity_count > 0)
id->id_header = disc->discovery_vdo[0];
if (disc->identity_count > 1)
id->cert_stat = disc->discovery_vdo[1];
if (disc->identity_count > 2)
id->product = disc->discovery_vdo[2];
/* Copy the remaining identity VDOs till a maximum of 6. */
for (i = 3; i < disc->identity_count && i < VDO_MAX_OBJECTS; i++)
id->vdo[i - 3] = disc->discovery_vdo[i];
}
static int cros_typec_handle_sop_prime_disc(struct cros_typec_data *typec, int port_num, u16 pd_revision)
{
struct cros_typec_port *port = typec->ports[port_num];
struct ec_response_typec_discovery *sop_disc = port->sop_disc;
struct ec_response_typec_discovery *disc = port->disc_data;
struct typec_cable_desc c_desc = {};
struct typec_plug_desc p_desc;
struct ec_params_typec_discovery req = {
.port = port_num,
.partner_type = TYPEC_PARTNER_SOP_PRIME,
};
u32 cable_plug_type;
int ret = 0;
memset(disc, 0, EC_PROTO2_MAX_RESPONSE_SIZE);
ret = cros_typec_ec_command(typec, 0, EC_CMD_TYPEC_DISCOVERY, &req, sizeof(req),
disc, EC_PROTO2_MAX_RESPONSE_SIZE);
if (ret < 0) {
dev_err(typec->dev, "Failed to get SOP' discovery data for port: %d\n", port_num);
goto sop_prime_disc_exit;
}
/* Parse the PD identity data, even if only 0s were returned. */
cros_typec_parse_pd_identity(&port->c_identity, disc);
if (disc->identity_count != 0) {
cable_plug_type = VDO_TYPEC_CABLE_TYPE(port->c_identity.vdo[0]);
switch (cable_plug_type) {
case CABLE_ATYPE:
c_desc.type = USB_PLUG_TYPE_A;
break;
case CABLE_BTYPE:
c_desc.type = USB_PLUG_TYPE_B;
break;
case CABLE_CTYPE:
c_desc.type = USB_PLUG_TYPE_C;
break;
case CABLE_CAPTIVE:
c_desc.type = USB_PLUG_CAPTIVE;
break;
default:
c_desc.type = USB_PLUG_NONE;
}
c_desc.active = PD_IDH_PTYPE(port->c_identity.id_header) == IDH_PTYPE_ACABLE;
}
c_desc.identity = &port->c_identity;
c_desc.pd_revision = pd_revision;
port->cable = typec_register_cable(port->port, &c_desc);
if (IS_ERR(port->cable)) {
ret = PTR_ERR(port->cable);
port->cable = NULL;
goto sop_prime_disc_exit;
}
p_desc.index = TYPEC_PLUG_SOP_P;
port->plug = typec_register_plug(port->cable, &p_desc);
if (IS_ERR(port->plug)) {
ret = PTR_ERR(port->plug);
port->plug = NULL;
goto sop_prime_disc_exit;
}
ret = cros_typec_register_altmodes(typec, port_num, false);
if (ret < 0) {
dev_err(typec->dev, "Failed to register plug altmodes, port: %d\n", port_num);
goto sop_prime_disc_exit;
}
return 0;
sop_prime_disc_exit:
cros_typec_remove_cable(typec, port_num);
return ret;
}
static int cros_typec_handle_sop_disc(struct cros_typec_data *typec, int port_num, u16 pd_revision)
{
struct cros_typec_port *port = typec->ports[port_num];
struct ec_response_typec_discovery *sop_disc = port->disc_data;
struct ec_params_typec_discovery req = {
.port = port_num,
.partner_type = TYPEC_PARTNER_SOP,
};
int ret = 0;
int i;
if (!port->partner) {
dev_err(typec->dev,
@ -674,6 +842,8 @@ static int cros_typec_handle_sop_disc(struct cros_typec_data *typec, int port_nu
goto disc_exit;
}
typec_partner_set_pd_revision(port->partner, pd_revision);
memset(sop_disc, 0, EC_PROTO2_MAX_RESPONSE_SIZE);
ret = cros_typec_ec_command(typec, 0, EC_CMD_TYPEC_DISCOVERY, &req, sizeof(req),
sop_disc, EC_PROTO2_MAX_RESPONSE_SIZE);
@ -682,17 +852,7 @@ static int cros_typec_handle_sop_disc(struct cros_typec_data *typec, int port_nu
goto disc_exit;
}
/* First, update the PD identity VDOs for the partner. */
if (sop_disc->identity_count > 0)
port->p_identity.id_header = sop_disc->discovery_vdo[0];
if (sop_disc->identity_count > 1)
port->p_identity.cert_stat = sop_disc->discovery_vdo[1];
if (sop_disc->identity_count > 2)
port->p_identity.product = sop_disc->discovery_vdo[2];
/* Copy the remaining identity VDOs till a maximum of 6. */
for (i = 3; i < sop_disc->identity_count && i < VDO_MAX_OBJECTS; i++)
port->p_identity.vdo[i - 3] = sop_disc->discovery_vdo[i];
cros_typec_parse_pd_identity(&port->p_identity, sop_disc);
ret = typec_partner_set_identity(port->partner);
if (ret < 0) {
@ -700,7 +860,7 @@ static int cros_typec_handle_sop_disc(struct cros_typec_data *typec, int port_nu
goto disc_exit;
}
ret = cros_typec_register_altmodes(typec, port_num);
ret = cros_typec_register_altmodes(typec, port_num, true);
if (ret < 0) {
dev_err(typec->dev, "Failed to register partner altmodes, port: %d\n", port_num);
goto disc_exit;
@ -710,6 +870,18 @@ disc_exit:
return ret;
}
static int cros_typec_send_clear_event(struct cros_typec_data *typec, int port_num, u32 events_mask)
{
struct ec_params_typec_control req = {
.port = port_num,
.command = TYPEC_CONTROL_COMMAND_CLEAR_EVENTS,
.clear_events_mask = events_mask,
};
return cros_typec_ec_command(typec, 0, EC_CMD_TYPEC_CONTROL, &req,
sizeof(req), NULL, 0);
}
static void cros_typec_handle_status(struct cros_typec_data *typec, int port_num)
{
struct ec_response_typec_status resp;
@ -725,18 +897,44 @@ static void cros_typec_handle_status(struct cros_typec_data *typec, int port_num
return;
}
if (typec->ports[port_num]->disc_done)
return;
/* Handle any events appropriately. */
if (resp.events & PD_STATUS_EVENT_SOP_DISC_DONE) {
ret = cros_typec_handle_sop_disc(typec, port_num);
if (ret < 0) {
dev_err(typec->dev, "Couldn't parse SOP Disc data, port: %d\n", port_num);
return;
}
if (resp.events & PD_STATUS_EVENT_SOP_DISC_DONE && !typec->ports[port_num]->sop_disc_done) {
u16 sop_revision;
typec->ports[port_num]->disc_done = true;
/* Convert BCD to the format preferred by the TypeC framework */
sop_revision = (le16_to_cpu(resp.sop_revision) & 0xff00) >> 4;
ret = cros_typec_handle_sop_disc(typec, port_num, sop_revision);
if (ret < 0)
dev_err(typec->dev, "Couldn't parse SOP Disc data, port: %d\n", port_num);
else {
typec->ports[port_num]->sop_disc_done = true;
ret = cros_typec_send_clear_event(typec, port_num,
PD_STATUS_EVENT_SOP_DISC_DONE);
if (ret < 0)
dev_warn(typec->dev,
"Failed SOP Disc event clear, port: %d\n", port_num);
}
if (resp.sop_connected)
typec_set_pwr_opmode(typec->ports[port_num]->port, TYPEC_PWR_MODE_PD);
}
if (resp.events & PD_STATUS_EVENT_SOP_PRIME_DISC_DONE &&
!typec->ports[port_num]->sop_prime_disc_done) {
u16 sop_prime_revision;
/* Convert BCD to the format preferred by the TypeC framework */
sop_prime_revision = (le16_to_cpu(resp.sop_prime_revision) & 0xff00) >> 4;
ret = cros_typec_handle_sop_prime_disc(typec, port_num, sop_prime_revision);
if (ret < 0)
dev_err(typec->dev, "Couldn't parse SOP' Disc data, port: %d\n", port_num);
else {
typec->ports[port_num]->sop_prime_disc_done = true;
ret = cros_typec_send_clear_event(typec, port_num,
PD_STATUS_EVENT_SOP_PRIME_DISC_DONE);
if (ret < 0)
dev_warn(typec->dev,
"Failed SOP Disc event clear, port: %d\n", port_num);
}
}
}
@ -827,8 +1025,8 @@ static int cros_typec_get_cmd_version(struct cros_typec_data *typec)
return 0;
}
/* Check the EC feature flags to see if TYPEC_* commands are supported. */
static int cros_typec_cmds_supported(struct cros_typec_data *typec)
/* Check the EC feature flags to see if TYPEC_* features are supported. */
static int cros_typec_feature_supported(struct cros_typec_data *typec, enum ec_feature_code feature)
{
struct ec_response_get_features resp = {};
int ret;
@ -837,11 +1035,12 @@ static int cros_typec_cmds_supported(struct cros_typec_data *typec)
&resp, sizeof(resp));
if (ret < 0) {
dev_warn(typec->dev,
"Failed to get features, assuming typec commands unsupported.\n");
"Failed to get features, assuming typec feature=%d unsupported.\n",
feature);
return 0;
}
return resp.flags[EC_FEATURE_TYPEC_CMD / 32] & EC_FEATURE_MASK_1(EC_FEATURE_TYPEC_CMD);
return resp.flags[feature / 32] & EC_FEATURE_MASK_1(feature);
}
static void cros_typec_port_work(struct work_struct *work)
@ -903,7 +1102,10 @@ static int cros_typec_probe(struct platform_device *pdev)
return ret;
}
typec->typec_cmd_supported = !!cros_typec_cmds_supported(typec);
typec->typec_cmd_supported = !!cros_typec_feature_supported(typec,
EC_FEATURE_TYPEC_CMD);
typec->needs_mux_ack = !!cros_typec_feature_supported(typec,
EC_FEATURE_TYPEC_MUX_REQUIRE_AP_ACK);
ret = cros_typec_ec_command(typec, 0, EC_CMD_USB_PD_PORTS, NULL, 0,
&resp, sizeof(resp));

View File

@ -115,3 +115,68 @@ void tb_acpi_add_links(struct tb_nhi *nhi)
if (ACPI_FAILURE(status))
dev_warn(&nhi->pdev->dev, "failed to enumerate tunneled ports\n");
}
/**
* tb_acpi_is_native() - Did the platform grant native TBT/USB4 control
*
* Returns %true if the platform granted OS native control over
* TBT/USB4. In this case software based connection manager can be used,
* otherwise there is firmware based connection manager running.
*/
bool tb_acpi_is_native(void)
{
return osc_sb_native_usb4_support_confirmed &&
osc_sb_native_usb4_control;
}
/**
* tb_acpi_may_tunnel_usb3() - Is USB3 tunneling allowed by the platform
*
* When software based connection manager is used, this function
* returns %true if platform allows native USB3 tunneling.
*/
bool tb_acpi_may_tunnel_usb3(void)
{
if (tb_acpi_is_native())
return osc_sb_native_usb4_control & OSC_USB_USB3_TUNNELING;
return true;
}
/**
* tb_acpi_may_tunnel_dp() - Is DisplayPort tunneling allowed by the platform
*
* When software based connection manager is used, this function
* returns %true if platform allows native DP tunneling.
*/
bool tb_acpi_may_tunnel_dp(void)
{
if (tb_acpi_is_native())
return osc_sb_native_usb4_control & OSC_USB_DP_TUNNELING;
return true;
}
/**
* tb_acpi_may_tunnel_pcie() - Is PCIe tunneling allowed by the platform
*
* When software based connection manager is used, this function
* returns %true if platform allows native PCIe tunneling.
*/
bool tb_acpi_may_tunnel_pcie(void)
{
if (tb_acpi_is_native())
return osc_sb_native_usb4_control & OSC_USB_PCIE_TUNNELING;
return true;
}
/**
* tb_acpi_is_xdomain_allowed() - Are XDomain connections allowed
*
* When software based connection manager is used, this function
* returns %true if platform allows XDomain connections.
*/
bool tb_acpi_is_xdomain_allowed(void)
{
if (tb_acpi_is_native())
return osc_sb_native_usb4_control & OSC_USB_XDOMAIN;
return true;
}

View File

@ -178,7 +178,7 @@ int tb_switch_next_cap(struct tb_switch *sw, unsigned int offset)
/**
* tb_switch_find_cap() - Find switch capability
* @sw Switch to find the capability for
* @sw: Switch to find the capability for
* @cap: Capability to look
*
* Returns offset to start of capability or %-ENOENT if no such

View File

@ -20,7 +20,17 @@
#define TB_CTL_RETRIES 4
/**
* struct tb_cfg - thunderbolt control channel
* struct tb_ctl - Thunderbolt control channel
* @nhi: Pointer to the NHI structure
* @tx: Transmit ring
* @rx: Receive ring
* @frame_pool: DMA pool for control messages
* @rx_packets: Received control messages
* @request_queue_lock: Lock protecting @request_queue
* @request_queue: List of outstanding requests
* @running: Is the control channel running at the moment
* @callback: Callback called when hotplug message is received
* @callback_data: Data passed to @callback
*/
struct tb_ctl {
struct tb_nhi *nhi;
@ -338,7 +348,7 @@ static void tb_ctl_tx_callback(struct tb_ring *ring, struct ring_frame *frame,
tb_ctl_pkg_free(pkg);
}
/**
/*
* tb_cfg_tx() - transmit a packet on the control channel
*
* len must be a multiple of four.
@ -375,7 +385,7 @@ static int tb_ctl_tx(struct tb_ctl *ctl, const void *data, size_t len,
return res;
}
/**
/*
* tb_ctl_handle_event() - acknowledge a plug event, invoke ctl->callback
*/
static bool tb_ctl_handle_event(struct tb_ctl *ctl, enum tb_cfg_pkg_type type,
@ -602,6 +612,9 @@ struct tb_cfg_result tb_cfg_request_sync(struct tb_ctl *ctl,
/**
* tb_ctl_alloc() - allocate a control channel
* @nhi: Pointer to NHI
* @cb: Callback called for plug events
* @cb_data: Data passed to @cb
*
* cb will be invoked once for every hot plug event.
*
@ -649,6 +662,7 @@ err:
/**
* tb_ctl_free() - free a control channel
* @ctl: Control channel to free
*
* Must be called after tb_ctl_stop.
*
@ -677,6 +691,7 @@ void tb_ctl_free(struct tb_ctl *ctl)
/**
* tb_cfg_start() - start/resume the control channel
* @ctl: Control channel to start
*/
void tb_ctl_start(struct tb_ctl *ctl)
{
@ -691,7 +706,8 @@ void tb_ctl_start(struct tb_ctl *ctl)
}
/**
* control() - pause the control channel
* tb_ctrl_stop() - pause the control channel
* @ctl: Control channel to stop
*
* All invocations of ctl->callback will have finished after this method
* returns.
@ -784,6 +800,9 @@ static bool tb_cfg_copy(struct tb_cfg_request *req, const struct ctl_pkg *pkg)
/**
* tb_cfg_reset() - send a reset packet and wait for a response
* @ctl: Control channel pointer
* @route: Router string for the router to send reset
* @timeout_msec: Timeout in ms how long to wait for the response
*
* If the switch at route is incorrectly configured then we will not receive a
* reply (even though the switch will reset). The caller should check for
@ -820,9 +839,17 @@ struct tb_cfg_result tb_cfg_reset(struct tb_ctl *ctl, u64 route,
}
/**
* tb_cfg_read() - read from config space into buffer
* tb_cfg_read_raw() - read from config space into buffer
* @ctl: Pointer to the control channel
* @buffer: Buffer where the data is read
* @route: Route string of the router
* @port: Port number when reading from %TB_CFG_PORT, %0 otherwise
* @space: Config space selector
* @offset: Dword word offset of the register to start reading
* @length: Number of dwords to read
* @timeout_msec: Timeout in ms how long to wait for the response
*
* Offset and length are in dwords.
* Reads from router config space without translating the possible error.
*/
struct tb_cfg_result tb_cfg_read_raw(struct tb_ctl *ctl, void *buffer,
u64 route, u32 port, enum tb_cfg_space space,
@ -884,8 +911,16 @@ struct tb_cfg_result tb_cfg_read_raw(struct tb_ctl *ctl, void *buffer,
/**
* tb_cfg_write() - write from buffer into config space
* @ctl: Pointer to the control channel
* @buffer: Data to write
* @route: Route string of the router
* @port: Port number when writing to %TB_CFG_PORT, %0 otherwise
* @space: Config space selector
* @offset: Dword word offset of the register to start writing
* @length: Number of dwords to write
* @timeout_msec: Timeout in ms how long to wait for the response
*
* Offset and length are in dwords.
* Writes to router config space without translating the possible error.
*/
struct tb_cfg_result tb_cfg_write_raw(struct tb_ctl *ctl, const void *buffer,
u64 route, u32 port, enum tb_cfg_space space,
@ -1022,6 +1057,8 @@ int tb_cfg_write(struct tb_ctl *ctl, const void *buffer, u64 route, u32 port,
/**
* tb_cfg_get_upstream_port() - get upstream port number of switch at route
* @ctl: Pointer to the control channel
* @route: Route string of the router
*
* Reads the first dword from the switches TB_CFG_SWITCH config area and
* returns the port number from which the reply originated.

View File

@ -335,6 +335,8 @@ static int dma_port_flash_write_block(struct tb_dma_port *dma, u32 address,
/* Write the block to MAIL_DATA registers */
ret = dma_port_write(sw->tb->ctl, buf, tb_route(sw), dma->port,
dma->base + MAIL_DATA, dwords, DMA_PORT_TIMEOUT);
if (ret)
return ret;
in = MAIL_IN_CMD_FLASH_WRITE << MAIL_IN_CMD_SHIFT;

View File

@ -7,7 +7,6 @@
* Mika Westerberg <mika.westerberg@linux.intel.com>
*/
#include <linux/acpi.h>
#include <linux/completion.h>
#include <linux/debugfs.h>
#include <linux/module.h>
@ -299,14 +298,12 @@ static int dma_test_submit_tx(struct dma_test *dt, size_t npackets)
tf->frame.size = 0; /* means 4096 */
tf->dma_test = dt;
tf->data = kzalloc(DMA_TEST_FRAME_SIZE, GFP_KERNEL);
tf->data = kmemdup(dma_test_pattern, DMA_TEST_FRAME_SIZE, GFP_KERNEL);
if (!tf->data) {
kfree(tf);
return -ENOMEM;
}
memcpy(tf->data, dma_test_pattern, DMA_TEST_FRAME_SIZE);
dma_addr = dma_map_single(dma_dev, tf->data, DMA_TEST_FRAME_SIZE,
DMA_TO_DEVICE);
if (dma_mapping_error(dma_dev, dma_addr)) {

View File

@ -118,6 +118,7 @@ static const char * const tb_security_names[] = {
[TB_SECURITY_SECURE] = "secure",
[TB_SECURITY_DPONLY] = "dponly",
[TB_SECURITY_USBONLY] = "usbonly",
[TB_SECURITY_NOPCIE] = "nopcie",
};
static ssize_t boot_acl_show(struct device *dev, struct device_attribute *attr,
@ -238,6 +239,22 @@ err_free_str:
}
static DEVICE_ATTR_RW(boot_acl);
static ssize_t deauthorization_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
const struct tb *tb = container_of(dev, struct tb, dev);
bool deauthorization = false;
/* Only meaningful if authorization is supported */
if (tb->security_level == TB_SECURITY_USER ||
tb->security_level == TB_SECURITY_SECURE)
deauthorization = !!tb->cm_ops->disapprove_switch;
return sprintf(buf, "%d\n", deauthorization);
}
static DEVICE_ATTR_RO(deauthorization);
static ssize_t iommu_dma_protection_show(struct device *dev,
struct device_attribute *attr,
char *buf)
@ -267,6 +284,7 @@ static DEVICE_ATTR_RO(security);
static struct attribute *domain_attrs[] = {
&dev_attr_boot_acl.attr,
&dev_attr_deauthorization.attr,
&dev_attr_iommu_dma_protection.attr,
&dev_attr_security.attr,
NULL,
@ -289,7 +307,7 @@ static umode_t domain_attr_is_visible(struct kobject *kobj,
return attr->mode;
}
static struct attribute_group domain_attr_group = {
static const struct attribute_group domain_attr_group = {
.is_visible = domain_attr_is_visible,
.attrs = domain_attrs,
};
@ -394,7 +412,9 @@ static bool tb_domain_event_cb(void *data, enum tb_cfg_pkg_type type,
switch (type) {
case TB_CFG_PKG_XDOMAIN_REQ:
case TB_CFG_PKG_XDOMAIN_RESP:
return tb_xdomain_handle_request(tb, type, buf, size);
if (tb_is_xdomain_enabled())
return tb_xdomain_handle_request(tb, type, buf, size);
break;
default:
tb->cm_ops->handle_event(tb, type, buf, size);
@ -441,6 +461,9 @@ int tb_domain_add(struct tb *tb)
goto err_ctl_stop;
}
tb_dbg(tb, "security level set to %s\n",
tb_security_names[tb->security_level]);
ret = device_add(&tb->dev);
if (ret)
goto err_ctl_stop;
@ -601,14 +624,31 @@ int tb_domain_runtime_resume(struct tb *tb)
return 0;
}
/**
* tb_domain_disapprove_switch() - Disapprove switch
* @tb: Domain the switch belongs to
* @sw: Switch to disapprove
*
* This will disconnect PCIe tunnel from parent to this @sw.
*
* Return: %0 on success and negative errno in case of failure.
*/
int tb_domain_disapprove_switch(struct tb *tb, struct tb_switch *sw)
{
if (!tb->cm_ops->disapprove_switch)
return -EPERM;
return tb->cm_ops->disapprove_switch(tb, sw);
}
/**
* tb_domain_approve_switch() - Approve switch
* @tb: Domain the switch belongs to
* @sw: Switch to approve
*
* This will approve switch by connection manager specific means. In
* case of success the connection manager will create tunnels for all
* supported protocols.
* case of success the connection manager will create PCIe tunnel from
* parent to @sw.
*/
int tb_domain_approve_switch(struct tb *tb, struct tb_switch *sw)
{

View File

@ -12,7 +12,7 @@
#include <linux/slab.h>
#include "tb.h"
/**
/*
* tb_eeprom_ctl_write() - write control word
*/
static int tb_eeprom_ctl_write(struct tb_switch *sw, struct tb_eeprom_ctl *ctl)
@ -20,7 +20,7 @@ static int tb_eeprom_ctl_write(struct tb_switch *sw, struct tb_eeprom_ctl *ctl)
return tb_sw_write(sw, ctl, TB_CFG_SWITCH, sw->cap_plug_events + 4, 1);
}
/**
/*
* tb_eeprom_ctl_write() - read control word
*/
static int tb_eeprom_ctl_read(struct tb_switch *sw, struct tb_eeprom_ctl *ctl)
@ -33,7 +33,7 @@ enum tb_eeprom_transfer {
TB_EEPROM_OUT,
};
/**
/*
* tb_eeprom_active - enable rom access
*
* WARNING: Always disable access after usage. Otherwise the controller will
@ -62,7 +62,7 @@ static int tb_eeprom_active(struct tb_switch *sw, bool enable)
}
}
/**
/*
* tb_eeprom_transfer - transfer one bit
*
* If TB_EEPROM_IN is passed, then the bit can be retrieved from ctl->data_in.
@ -90,7 +90,7 @@ static int tb_eeprom_transfer(struct tb_switch *sw, struct tb_eeprom_ctl *ctl,
return tb_eeprom_ctl_write(sw, ctl);
}
/**
/*
* tb_eeprom_out - write one byte to the bus
*/
static int tb_eeprom_out(struct tb_switch *sw, u8 val)
@ -110,7 +110,7 @@ static int tb_eeprom_out(struct tb_switch *sw, u8 val)
return 0;
}
/**
/*
* tb_eeprom_in - read one byte from the bus
*/
static int tb_eeprom_in(struct tb_switch *sw, u8 *val)
@ -131,7 +131,7 @@ static int tb_eeprom_in(struct tb_switch *sw, u8 *val)
return 0;
}
/**
/*
* tb_eeprom_get_drom_offset - get drom offset within eeprom
*/
static int tb_eeprom_get_drom_offset(struct tb_switch *sw, u16 *offset)
@ -162,7 +162,7 @@ static int tb_eeprom_get_drom_offset(struct tb_switch *sw, u16 *offset)
return 0;
}
/**
/*
* tb_eeprom_read_n - read count bytes from offset into val
*/
static int tb_eeprom_read_n(struct tb_switch *sw, u16 offset, u8 *val,
@ -279,7 +279,9 @@ struct tb_drom_entry_port {
/**
* tb_drom_read_uid_only - read uid directly from drom
* tb_drom_read_uid_only() - Read UID directly from DROM
* @sw: Router whose UID to read
* @uid: UID is placed here
*
* Does not use the cached copy in sw->drom. Used during resume to check switch
* identity.
@ -374,7 +376,7 @@ static int tb_drom_parse_entry_port(struct tb_switch *sw,
return 0;
}
/**
/*
* tb_drom_parse_entries - parse the linked list of drom entries
*
* Drom must have been copied to sw->drom.
@ -410,7 +412,7 @@ static int tb_drom_parse_entries(struct tb_switch *sw)
return 0;
}
/**
/*
* tb_drom_copy_efi - copy drom supplied by EFI to sw->drom if present
*/
static int tb_drom_copy_efi(struct tb_switch *sw, u16 *size)
@ -520,7 +522,14 @@ static int tb_drom_read_n(struct tb_switch *sw, u16 offset, u8 *val,
}
/**
* tb_drom_read - copy drom to sw->drom and parse it
* tb_drom_read() - Copy DROM to sw->drom and parse it
* @sw: Router whose DROM to read and parse
*
* This function reads router DROM and if successful parses the entries and
* populates the fields in @sw accordingly. Can be called for any router
* generation.
*
* Returns %0 in case of success and negative errno otherwise.
*/
int tb_drom_read(struct tb_switch *sw)
{

View File

@ -85,8 +85,8 @@ struct usb4_switch_nvm_auth {
* @set_uuid: Set UUID for the root switch (optional)
* @device_connected: Handle device connected ICM message
* @device_disconnected: Handle device disconnected ICM message
* @xdomain_connected - Handle XDomain connected ICM message
* @xdomain_disconnected - Handle XDomain disconnected ICM message
* @xdomain_connected: Handle XDomain connected ICM message
* @xdomain_disconnected: Handle XDomain disconnected ICM message
* @rtd3_veto: Handle RTD3 veto notification ICM message
*/
struct icm {
@ -1701,10 +1701,12 @@ static void icm_handle_notification(struct work_struct *work)
icm->device_disconnected(tb, n->pkg);
break;
case ICM_EVENT_XDOMAIN_CONNECTED:
icm->xdomain_connected(tb, n->pkg);
if (tb_is_xdomain_enabled())
icm->xdomain_connected(tb, n->pkg);
break;
case ICM_EVENT_XDOMAIN_DISCONNECTED:
icm->xdomain_disconnected(tb, n->pkg);
if (tb_is_xdomain_enabled())
icm->xdomain_disconnected(tb, n->pkg);
break;
case ICM_EVENT_RTD3_VETO:
icm->rtd3_veto(tb, n->pkg);

View File

@ -158,6 +158,41 @@ void tb_lc_unconfigure_xdomain(struct tb_port *port)
tb_lc_set_xdomain_configured(port, false);
}
/**
* tb_lc_start_lane_initialization() - Start lane initialization
* @port: Device router lane 0 adapter
*
* Starts lane initialization for @port after the router resumed from
* sleep. Should be called for those downstream lane adapters that were
* not connected (tb_lc_configure_port() was not called) before sleep.
*
* Returns %0 in success and negative errno in case of failure.
*/
int tb_lc_start_lane_initialization(struct tb_port *port)
{
struct tb_switch *sw = port->sw;
int ret, cap;
u32 ctrl;
if (!tb_route(sw))
return 0;
if (sw->generation < 2)
return 0;
cap = find_port_lc_cap(port);
if (cap < 0)
return cap;
ret = tb_sw_read(sw, &ctrl, TB_CFG_SWITCH, cap + TB_LC_SX_CTRL, 1);
if (ret)
return ret;
ctrl |= TB_LC_SX_CTRL_SLI;
return tb_sw_write(sw, &ctrl, TB_CFG_SWITCH, cap + TB_LC_SX_CTRL, 1);
}
static int tb_lc_set_wake_one(struct tb_switch *sw, unsigned int offset,
unsigned int flags)
{

View File

@ -44,7 +44,7 @@ static int ring_interrupt_index(struct tb_ring *ring)
return bit;
}
/**
/*
* ring_interrupt_active() - activate/deactivate interrupts for a single ring
*
* ring->nhi->lock must be held.
@ -105,7 +105,7 @@ static void ring_interrupt_active(struct tb_ring *ring, bool active)
iowrite32(new, ring->nhi->iobase + reg);
}
/**
/*
* nhi_disable_interrupts() - disable interrupts for all rings
*
* Use only during init and shutdown.
@ -182,7 +182,7 @@ static bool ring_empty(struct tb_ring *ring)
return ring->head == ring->tail;
}
/**
/*
* ring_write_descriptors() - post frames from ring->queue to the controller
*
* ring->lock is held.
@ -212,7 +212,7 @@ static void ring_write_descriptors(struct tb_ring *ring)
}
}
/**
/*
* ring_work() - progress completed frames
*
* If the ring is shutting down then all frames are marked as canceled and
@ -592,6 +592,7 @@ EXPORT_SYMBOL_GPL(tb_ring_alloc_rx);
/**
* tb_ring_start() - enable a ring
* @ring: Ring to start
*
* Must not be invoked in parallel with tb_ring_stop().
*/
@ -667,6 +668,7 @@ EXPORT_SYMBOL_GPL(tb_ring_start);
/**
* tb_ring_stop() - shutdown a ring
* @ring: Ring to stop
*
* Must not be invoked from a callback.
*
@ -754,7 +756,7 @@ void tb_ring_free(struct tb_ring *ring)
dev_dbg(&ring->nhi->pdev->dev, "freeing %s %d\n", RING_TYPE(ring),
ring->hop);
/**
/*
* ring->work can no longer be scheduled (it is scheduled only
* by nhi_interrupt_work, ring_stop and ring_msix). Wait for it
* to finish before freeing the ring.
@ -1188,6 +1190,29 @@ static void tb_apple_add_links(struct tb_nhi *nhi)
}
}
static struct tb *nhi_select_cm(struct tb_nhi *nhi)
{
struct tb *tb;
/*
* USB4 case is simple. If we got control of any of the
* capabilities, we use software CM.
*/
if (tb_acpi_is_native())
return tb_probe(nhi);
/*
* Either firmware based CM is running (we did not get control
* from the firmware) or this is pre-USB4 PC so try first
* firmware CM and then fallback to software CM.
*/
tb = icm_probe(nhi);
if (!tb)
tb = tb_probe(nhi);
return tb;
}
static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct tb_nhi *nhi;
@ -1256,9 +1281,7 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
tb_apple_add_links(nhi);
tb_acpi_add_links(nhi);
tb = icm_probe(nhi);
if (!tb)
tb = tb_probe(nhi);
tb = nhi_select_cm(nhi);
if (!tb) {
dev_err(&nhi->pdev->dev,
"failed to determine connection manager, aborting\n");

View File

@ -466,6 +466,7 @@ void tb_path_deactivate(struct tb_path *path)
/**
* tb_path_activate() - activate a path
* @path: Path to activate
*
* Activate a path starting with the last hop and iterating backwards. The
* caller must fill path->hops before calling tb_path_activate().
@ -561,6 +562,7 @@ err:
/**
* tb_path_is_invalid() - check whether any ports on the path are invalid
* @path: Path to check
*
* Return: Returns true if the path is invalid, false otherwise.
*/

View File

@ -525,6 +525,8 @@ int tb_port_state(struct tb_port *port)
/**
* tb_wait_for_port() - wait for a port to become ready
* @port: Port to wait
* @wait_if_unplugged: Wait also when port is unplugged
*
* Wait up to 1 second for a port to reach state TB_PORT_UP. If
* wait_if_unplugged is set then we also wait if the port is in state
@ -589,6 +591,8 @@ int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged)
/**
* tb_port_add_nfc_credits() - add/remove non flow controlled credits to port
* @port: Port to add/remove NFC credits
* @credits: Credits to add/remove
*
* Change the number of NFC credits allocated to @port by @credits. To remove
* NFC credits pass a negative amount of credits.
@ -646,6 +650,8 @@ int tb_port_set_initial_credits(struct tb_port *port, u32 credits)
/**
* tb_port_clear_counter() - clear a counter in TB_CFG_COUNTER
* @port: Port whose counters to clear
* @counter: Counter index to clear
*
* Return: Returns 0 on success or an error code on failure.
*/
@ -718,7 +724,7 @@ int tb_port_disable(struct tb_port *port)
return __tb_port_enable(port, false);
}
/**
/*
* tb_init_port() - initialize a port
*
* This is a helper method for tb_switch_alloc. Does not check or initialize
@ -1065,6 +1071,17 @@ void tb_port_lane_bonding_disable(struct tb_port *port)
tb_port_set_link_width(port, 1);
}
static int tb_port_start_lane_initialization(struct tb_port *port)
{
int ret;
if (tb_switch_is_usb4(port->sw))
return 0;
ret = tb_lc_start_lane_initialization(port);
return ret == -EINVAL ? 0 : ret;
}
/**
* tb_port_is_enabled() - Is the adapter port enabled
* @port: Port to check
@ -1302,7 +1319,7 @@ static void tb_dump_switch(const struct tb *tb, const struct tb_switch *sw)
}
/**
* reset_switch() - reconfigure route, enable and send TB_CFG_PKG_RESET
* tb_switch_reset() - reconfigure route, enable and send TB_CFG_PKG_RESET
* @sw: Switch to reset
*
* Return: Returns 0 on success or an error code on failure.
@ -1326,7 +1343,7 @@ int tb_switch_reset(struct tb_switch *sw)
return res.err;
}
/**
/*
* tb_plug_events_active() - enable/disable plug events on a switch
*
* Also configures a sane plug_events_delay of 255ms.
@ -1376,6 +1393,30 @@ static ssize_t authorized_show(struct device *dev,
return sprintf(buf, "%u\n", sw->authorized);
}
static int disapprove_switch(struct device *dev, void *not_used)
{
struct tb_switch *sw;
sw = tb_to_switch(dev);
if (sw && sw->authorized) {
int ret;
/* First children */
ret = device_for_each_child_reverse(&sw->dev, NULL, disapprove_switch);
if (ret)
return ret;
ret = tb_domain_disapprove_switch(sw->tb, sw);
if (ret)
return ret;
sw->authorized = 0;
kobject_uevent(&sw->dev.kobj, KOBJ_CHANGE);
}
return 0;
}
static int tb_switch_set_authorized(struct tb_switch *sw, unsigned int val)
{
int ret = -EINVAL;
@ -1383,10 +1424,18 @@ static int tb_switch_set_authorized(struct tb_switch *sw, unsigned int val)
if (!mutex_trylock(&sw->tb->lock))
return restart_syscall();
if (sw->authorized)
if (!!sw->authorized == !!val)
goto unlock;
switch (val) {
/* Disapprove switch */
case 0:
if (tb_route(sw)) {
ret = disapprove_switch(&sw->dev, NULL);
goto unlock;
}
break;
/* Approve switch */
case 1:
if (sw->key)
@ -1725,7 +1774,11 @@ static umode_t switch_attr_is_visible(struct kobject *kobj,
struct device *dev = kobj_to_dev(kobj);
struct tb_switch *sw = tb_to_switch(dev);
if (attr == &dev_attr_device.attr) {
if (attr == &dev_attr_authorized.attr) {
if (sw->tb->security_level == TB_SECURITY_NOPCIE ||
sw->tb->security_level == TB_SECURITY_DPONLY)
return 0;
} else if (attr == &dev_attr_device.attr) {
if (!sw->device)
return 0;
} else if (attr == &dev_attr_device_name.attr) {
@ -1771,7 +1824,7 @@ static umode_t switch_attr_is_visible(struct kobject *kobj,
return sw->safe_mode ? 0 : attr->mode;
}
static struct attribute_group switch_group = {
static const struct attribute_group switch_group = {
.is_visible = switch_attr_is_visible,
.attrs = switch_attrs,
};
@ -2606,6 +2659,7 @@ void tb_switch_remove(struct tb_switch *sw)
/**
* tb_sw_set_unplugged() - set is_unplugged on switch and downstream switches
* @sw: Router to mark unplugged
*/
void tb_sw_set_unplugged(struct tb_switch *sw)
{
@ -2694,8 +2748,22 @@ int tb_switch_resume(struct tb_switch *sw)
/* check for surviving downstream switches */
tb_switch_for_each_port(sw, port) {
if (!tb_port_has_remote(port) && !port->xdomain)
if (!tb_port_has_remote(port) && !port->xdomain) {
/*
* For disconnected downstream lane adapters
* start lane initialization now so we detect
* future connects.
*/
if (!tb_is_upstream_port(port) && tb_port_is_null(port))
tb_port_start_lane_initialization(port);
continue;
} else if (port->xdomain) {
/*
* Start lane initialization for XDomain so the
* link gets re-established.
*/
tb_port_start_lane_initialization(port);
}
if (tb_wait_for_port(port, true) <= 0) {
tb_port_warn(port,

View File

@ -179,6 +179,9 @@ static void tb_scan_xdomain(struct tb_port *port)
struct tb_xdomain *xd;
u64 route;
if (!tb_is_xdomain_enabled())
return;
route = tb_downstream_route(port);
xd = tb_xdomain_find_by_route(tb, route);
if (xd) {
@ -434,6 +437,11 @@ static int tb_tunnel_usb3(struct tb *tb, struct tb_switch *sw)
struct tb_cm *tcm = tb_priv(tb);
struct tb_tunnel *tunnel;
if (!tb_acpi_may_tunnel_usb3()) {
tb_dbg(tb, "USB3 tunneling disabled, not creating tunnel\n");
return 0;
}
up = tb_switch_find_port(sw, TB_TYPE_USB3_UP);
if (!up)
return 0;
@ -509,6 +517,9 @@ static int tb_create_usb3_tunnels(struct tb_switch *sw)
struct tb_port *port;
int ret;
if (!tb_acpi_may_tunnel_usb3())
return 0;
if (tb_route(sw)) {
ret = tb_tunnel_usb3(sw->tb, sw);
if (ret)
@ -528,7 +539,7 @@ static int tb_create_usb3_tunnels(struct tb_switch *sw)
static void tb_scan_port(struct tb_port *port);
/**
/*
* tb_scan_switch() - scan for and initialize downstream switches
*/
static void tb_scan_switch(struct tb_switch *sw)
@ -544,7 +555,7 @@ static void tb_scan_switch(struct tb_switch *sw)
pm_runtime_put_autosuspend(&sw->dev);
}
/**
/*
* tb_scan_port() - check for and initialize switches below port
*/
static void tb_scan_port(struct tb_port *port)
@ -704,7 +715,7 @@ static void tb_deactivate_and_free_tunnel(struct tb_tunnel *tunnel)
tb_tunnel_free(tunnel);
}
/**
/*
* tb_free_invalid_tunnels() - destroy tunnels of devices that have gone away
*/
static void tb_free_invalid_tunnels(struct tb *tb)
@ -719,7 +730,7 @@ static void tb_free_invalid_tunnels(struct tb *tb)
}
}
/**
/*
* tb_free_unplugged_children() - traverse hierarchy and free unplugged switches
*/
static void tb_free_unplugged_children(struct tb_switch *sw)
@ -838,6 +849,11 @@ static void tb_tunnel_dp(struct tb *tb)
struct tb_port *port, *in, *out;
struct tb_tunnel *tunnel;
if (!tb_acpi_may_tunnel_dp()) {
tb_dbg(tb, "DP tunneling disabled, not creating tunnel\n");
return;
}
/*
* Find pair of inactive DP IN and DP OUT adapters and then
* establish a DP tunnel between them.
@ -1002,6 +1018,25 @@ static void tb_disconnect_and_release_dp(struct tb *tb)
}
}
static int tb_disconnect_pci(struct tb *tb, struct tb_switch *sw)
{
struct tb_tunnel *tunnel;
struct tb_port *up;
up = tb_switch_find_port(sw, TB_TYPE_PCIE_UP);
if (WARN_ON(!up))
return -ENODEV;
tunnel = tb_find_tunnel(tb, TB_TUNNEL_PCI, NULL, up);
if (WARN_ON(!tunnel))
return -ENODEV;
tb_tunnel_deactivate(tunnel);
list_del(&tunnel->list);
tb_tunnel_free(tunnel);
return 0;
}
static int tb_tunnel_pci(struct tb *tb, struct tb_switch *sw)
{
struct tb_port *up, *down, *port;
@ -1101,7 +1136,7 @@ static int tb_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd)
/* hotplug handling */
/**
/*
* tb_handle_hotplug() - handle hotplug event
*
* Executes on tb->wq.
@ -1210,7 +1245,7 @@ out:
kfree(ev);
}
/**
/*
* tb_schedule_hotplug_handler() - callback function for the control channel
*
* Delegates to tb_handle_hotplug.
@ -1512,6 +1547,7 @@ static const struct tb_cm_ops tb_cm_ops = {
.runtime_suspend = tb_runtime_suspend,
.runtime_resume = tb_runtime_resume,
.handle_event = tb_handle_event,
.disapprove_switch = tb_disconnect_pci,
.approve_switch = tb_tunnel_pci,
.approve_xdomain_paths = tb_approve_xdomain_paths,
.disconnect_xdomain_paths = tb_disconnect_xdomain_paths,
@ -1526,7 +1562,11 @@ struct tb *tb_probe(struct tb_nhi *nhi)
if (!tb)
return NULL;
tb->security_level = TB_SECURITY_USER;
if (tb_acpi_may_tunnel_pcie())
tb->security_level = TB_SECURITY_USER;
else
tb->security_level = TB_SECURITY_NOPCIE;
tb->cm_ops = &tb_cm_ops;
tcm = tb_priv(tb);

View File

@ -138,6 +138,8 @@ struct tb_switch_tmu {
*
* When the switch is being added or removed to the domain (other
* switches) you need to have domain lock held.
*
* In USB4 terminology this structure represents a router.
*/
struct tb_switch {
struct device dev;
@ -196,6 +198,9 @@ struct tb_switch {
* @in_hopids: Currently allocated input HopIDs
* @out_hopids: Currently allocated output HopIDs
* @list: Used to link ports to DP resources list
*
* In USB4 terminology this structure represents an adapter (protocol or
* lane adapter).
*/
struct tb_port {
struct tb_regs_port_header config;
@ -361,6 +366,7 @@ struct tb_path {
* @handle_event: Handle thunderbolt event
* @get_boot_acl: Get boot ACL list
* @set_boot_acl: Set boot ACL list
* @disapprove_switch: Disapprove switch (disconnect PCIe tunnel)
* @approve_switch: Approve switch
* @add_switch_key: Add key to switch
* @challenge_switch_key: Challenge switch using key
@ -394,6 +400,7 @@ struct tb_cm_ops {
const void *buf, size_t size);
int (*get_boot_acl)(struct tb *tb, uuid_t *uuids, size_t nuuids);
int (*set_boot_acl)(struct tb *tb, const uuid_t *uuids, size_t nuuids);
int (*disapprove_switch)(struct tb *tb, struct tb_switch *sw);
int (*approve_switch)(struct tb *tb, struct tb_switch *sw);
int (*add_switch_key)(struct tb *tb, struct tb_switch *sw);
int (*challenge_switch_key)(struct tb *tb, struct tb_switch *sw,
@ -629,6 +636,7 @@ int tb_domain_thaw_noirq(struct tb *tb);
void tb_domain_complete(struct tb *tb);
int tb_domain_runtime_suspend(struct tb *tb);
int tb_domain_runtime_resume(struct tb *tb);
int tb_domain_disapprove_switch(struct tb *tb, struct tb_switch *sw);
int tb_domain_approve_switch(struct tb *tb, struct tb_switch *sw);
int tb_domain_approve_switch_key(struct tb *tb, struct tb_switch *sw);
int tb_domain_challenge_switch_key(struct tb *tb, struct tb_switch *sw);
@ -923,6 +931,7 @@ int tb_lc_configure_port(struct tb_port *port);
void tb_lc_unconfigure_port(struct tb_port *port);
int tb_lc_configure_xdomain(struct tb_port *port);
void tb_lc_unconfigure_xdomain(struct tb_port *port);
int tb_lc_start_lane_initialization(struct tb_port *port);
int tb_lc_set_wake(struct tb_switch *sw, unsigned int flags);
int tb_lc_set_sleep(struct tb_switch *sw);
bool tb_lc_lane_bonding_possible(struct tb_switch *sw);
@ -949,6 +958,7 @@ static inline u64 tb_downstream_route(struct tb_port *port)
| ((u64) port->port << (port->sw->config.depth * 8));
}
bool tb_is_xdomain_enabled(void);
bool tb_xdomain_handle_request(struct tb *tb, enum tb_cfg_pkg_type type,
const void *buf, size_t size);
struct tb_xdomain *tb_xdomain_alloc(struct tb *tb, struct device *parent,
@ -1034,8 +1044,20 @@ void tb_check_quirks(struct tb_switch *sw);
#ifdef CONFIG_ACPI
void tb_acpi_add_links(struct tb_nhi *nhi);
bool tb_acpi_is_native(void);
bool tb_acpi_may_tunnel_usb3(void);
bool tb_acpi_may_tunnel_dp(void);
bool tb_acpi_may_tunnel_pcie(void);
bool tb_acpi_is_xdomain_allowed(void);
#else
static inline void tb_acpi_add_links(struct tb_nhi *nhi) { }
static inline bool tb_acpi_is_native(void) { return true; }
static inline bool tb_acpi_may_tunnel_usb3(void) { return true; }
static inline bool tb_acpi_may_tunnel_dp(void) { return true; }
static inline bool tb_acpi_may_tunnel_pcie(void) { return true; }
static inline bool tb_acpi_is_xdomain_allowed(void) { return true; }
#endif
#ifdef CONFIG_DEBUG_FS

View File

@ -464,6 +464,7 @@ struct tb_regs_hop {
#define TB_LC_SX_CTRL_L1D BIT(17)
#define TB_LC_SX_CTRL_L2C BIT(20)
#define TB_LC_SX_CTRL_L2D BIT(21)
#define TB_LC_SX_CTRL_SLI BIT(29)
#define TB_LC_SX_CTRL_UPSTREAM BIT(30)
#define TB_LC_SX_CTRL_SLP BIT(31)

View File

@ -830,7 +830,7 @@ static void tb_dma_init_path(struct tb_path *path, unsigned int isb,
* @transmit_path: HopID used for transmitting packets
* @receive_ring: NHI ring number used to receive packets from the
* other domain. Set to %0 if RX path is not needed.
* @reveive_path: HopID used for receiving packets
* @receive_path: HopID used for receiving packets
*
* Return: Returns a tb_tunnel on success or NULL on failure.
*/
@ -932,12 +932,14 @@ static int tb_usb3_activate(struct tb_tunnel *tunnel, bool activate)
static int tb_usb3_consumed_bandwidth(struct tb_tunnel *tunnel,
int *consumed_up, int *consumed_down)
{
int pcie_enabled = tb_acpi_may_tunnel_pcie();
/*
* PCIe tunneling affects the USB3 bandwidth so take that it
* into account here.
* PCIe tunneling, if enabled, affects the USB3 bandwidth so
* take that it into account here.
*/
*consumed_up = tunnel->allocated_up * (3 + 1) / 3;
*consumed_down = tunnel->allocated_down * (3 + 1) / 3;
*consumed_up = tunnel->allocated_up * (3 + pcie_enabled) / 3;
*consumed_down = tunnel->allocated_down * (3 + pcie_enabled) / 3;
return 0;
}

View File

@ -331,13 +331,18 @@ int usb4_switch_setup(struct tb_switch *sw)
if (ret)
return ret;
if (sw->link_usb4 && tb_switch_find_port(parent, TB_TYPE_USB3_DOWN)) {
if (tb_acpi_may_tunnel_usb3() && sw->link_usb4 &&
tb_switch_find_port(parent, TB_TYPE_USB3_DOWN)) {
val |= ROUTER_CS_5_UTO;
xhci = false;
}
/* Only enable PCIe tunneling if the parent router supports it */
if (tb_switch_find_port(parent, TB_TYPE_PCIE_DOWN)) {
/*
* Only enable PCIe tunneling if the parent router supports it
* and it is not disabled.
*/
if (tb_acpi_may_tunnel_pcie() &&
tb_switch_find_port(parent, TB_TYPE_PCIE_DOWN)) {
val |= ROUTER_CS_5_PTO;
/*
* xHCI can be enabled if PCIe tunneling is supported

View File

@ -30,6 +30,10 @@ struct xdomain_request_work {
struct tb *tb;
};
static bool tb_xdomain_enabled = true;
module_param_named(xdomain, tb_xdomain_enabled, bool, 0444);
MODULE_PARM_DESC(xdomain, "allow XDomain protocol (default: true)");
/* Serializes access to the properties and protocol handlers below */
static DEFINE_MUTEX(xdomain_lock);
@ -47,6 +51,11 @@ static const uuid_t tb_xdp_uuid =
UUID_INIT(0xb638d70e, 0x42ff, 0x40bb,
0x97, 0xc2, 0x90, 0xe2, 0xc0, 0xb2, 0xff, 0x07);
bool tb_is_xdomain_enabled(void)
{
return tb_xdomain_enabled && tb_acpi_is_xdomain_allowed();
}
static bool tb_xdomain_match(const struct tb_cfg_request *req,
const struct ctl_pkg *pkg)
{
@ -670,7 +679,7 @@ EXPORT_SYMBOL_GPL(tb_register_service_driver);
/**
* tb_unregister_service_driver() - Unregister XDomain service driver
* @xdrv: Driver to unregister
* @drv: Driver to unregister
*
* Unregisters XDomain service driver from the bus.
*/
@ -756,7 +765,7 @@ static struct attribute *tb_service_attrs[] = {
NULL,
};
static struct attribute_group tb_service_attr_group = {
static const struct attribute_group tb_service_attr_group = {
.attrs = tb_service_attrs,
};
@ -1239,7 +1248,7 @@ static struct attribute *xdomain_attrs[] = {
NULL,
};
static struct attribute_group xdomain_attr_group = {
static const struct attribute_group xdomain_attr_group = {
.attrs = xdomain_attrs,
};

View File

@ -13,7 +13,9 @@ obj-$(CONFIG_USB_DWC3) += dwc3/
obj-$(CONFIG_USB_DWC2) += dwc2/
obj-$(CONFIG_USB_ISP1760) += isp1760/
obj-$(CONFIG_USB_CDNS_SUPPORT) += cdns3/
obj-$(CONFIG_USB_CDNS3) += cdns3/
obj-$(CONFIG_USB_CDNSP_PCI) += cdns3/
obj-$(CONFIG_USB_MON) += mon/
obj-$(CONFIG_USB_MTU3) += mtu3/

View File

@ -76,7 +76,7 @@ struct c67x00_hcd {
u16 next_td_addr;
u16 next_buf_addr;
struct tasklet_struct tasklet;
struct work_struct work;
struct completion endpoint_disable;

View File

@ -1123,24 +1123,26 @@ static void c67x00_do_work(struct c67x00_hcd *c67x00)
/* -------------------------------------------------------------------------- */
static void c67x00_sched_tasklet(struct tasklet_struct *t)
static void c67x00_sched_work(struct work_struct *work)
{
struct c67x00_hcd *c67x00 = from_tasklet(c67x00, t, tasklet);
struct c67x00_hcd *c67x00;
c67x00 = container_of(work, struct c67x00_hcd, work);
c67x00_do_work(c67x00);
}
void c67x00_sched_kick(struct c67x00_hcd *c67x00)
{
tasklet_hi_schedule(&c67x00->tasklet);
queue_work(system_highpri_wq, &c67x00->work);
}
int c67x00_sched_start_scheduler(struct c67x00_hcd *c67x00)
{
tasklet_setup(&c67x00->tasklet, c67x00_sched_tasklet);
INIT_WORK(&c67x00->work, c67x00_sched_work);
return 0;
}
void c67x00_sched_stop_scheduler(struct c67x00_hcd *c67x00)
{
tasklet_kill(&c67x00->tasklet);
cancel_work_sync(&c67x00->work);
}

View File

@ -1,14 +1,28 @@
config USB_CDNS3
tristate "Cadence USB3 Dual-Role Controller"
config USB_CDNS_SUPPORT
tristate "Cadence USB Support"
depends on USB_SUPPORT && (USB || USB_GADGET) && HAS_DMA
select USB_XHCI_PLATFORM if USB_XHCI_HCD
select USB_ROLE_SWITCH
help
Say Y here if your system has a Cadence USBSS or USBSSP
dual-role controller.
It supports: dual-role switch, Host-only, and Peripheral-only.
config USB_CDNS_HOST
bool
if USB_CDNS_SUPPORT
config USB_CDNS3
tristate "Cadence USB3 Dual-Role Controller"
depends on USB_CDNS_SUPPORT
help
Say Y here if your system has a Cadence USB3 dual-role controller.
It supports: dual-role switch, Host-only, and Peripheral-only.
If you choose to build this driver is a dynamically linked
as module, the module will be called cdns3.ko.
endif
if USB_CDNS3
@ -25,6 +39,7 @@ config USB_CDNS3_GADGET
config USB_CDNS3_HOST
bool "Cadence USB3 host controller"
depends on USB=y || USB=USB_CDNS3
select USB_CDNS_HOST
help
Say Y here to enable host controller functionality of the
Cadence driver.
@ -64,3 +79,44 @@ config USB_CDNS3_IMX
For example, imx8qm and imx8qxp.
endif
if USB_CDNS_SUPPORT
config USB_CDNSP_PCI
tristate "Cadence CDNSP Dual-Role Controller"
depends on USB_CDNS_SUPPORT && USB_PCI && ACPI
help
Say Y here if your system has a Cadence CDNSP dual-role controller.
It supports: dual-role switch Host-only, and Peripheral-only.
If you choose to build this driver is a dynamically linked
module, the module will be called cdnsp.ko.
endif
if USB_CDNSP_PCI
config USB_CDNSP_GADGET
bool "Cadence CDNSP device controller"
depends on USB_GADGET=y || USB_GADGET=USB_CDNSP_PCI
help
Say Y here to enable device controller functionality of the
Cadence CDNSP-DEV driver.
Cadence CDNSP Device Controller in device mode is
very similar to XHCI controller. Therefore some algorithms
used has been taken from host driver.
This controller supports FF, HS, SS and SSP mode.
It doesn't support LS.
config USB_CDNSP_HOST
bool "Cadence CDNSP host controller"
depends on USB=y || USB=USB_CDNSP_PCI
select USB_CDNS_HOST
help
Say Y here to enable host controller functionality of the
Cadence driver.
Host controller is compliant with XHCI so it uses
standard XHCI driver.
endif

View File

@ -1,18 +1,43 @@
# SPDX-License-Identifier: GPL-2.0
# define_trace.h needs to know how to find our header
CFLAGS_trace.o := -I$(src)
CFLAGS_cdns3-trace.o := -I$(src)
CFLAGS_cdnsp-trace.o := -I$(src)
cdns3-y := core.o drd.o
cdns-usb-common-y := core.o drd.o
cdns3-y := cdns3-plat.o
obj-$(CONFIG_USB_CDNS3) += cdns3.o
cdns3-$(CONFIG_USB_CDNS3_GADGET) += gadget.o ep0.o
ifneq ($(CONFIG_USB_CDNS3_GADGET),)
cdns3-$(CONFIG_TRACING) += trace.o
ifeq ($(CONFIG_USB),m)
obj-m += cdns-usb-common.o
obj-m += cdns3.o
else
obj-$(CONFIG_USB_CDNS_SUPPORT) += cdns-usb-common.o
obj-$(CONFIG_USB_CDNS3) += cdns3.o
endif
cdns3-$(CONFIG_USB_CDNS3_HOST) += host.o
cdns-usb-common-$(CONFIG_USB_CDNS_HOST) += host.o
cdns3-$(CONFIG_USB_CDNS3_GADGET) += cdns3-gadget.o cdns3-ep0.o
obj-$(CONFIG_USB_CDNS3_PCI_WRAP) += cdns3-pci-wrap.o
obj-$(CONFIG_USB_CDNS3_TI) += cdns3-ti.o
obj-$(CONFIG_USB_CDNS3_IMX) += cdns3-imx.o
ifneq ($(CONFIG_USB_CDNS3_GADGET),)
cdns3-$(CONFIG_TRACING) += cdns3-trace.o
endif
obj-$(CONFIG_USB_CDNS3_PCI_WRAP) += cdns3-pci-wrap.o
obj-$(CONFIG_USB_CDNS3_TI) += cdns3-ti.o
obj-$(CONFIG_USB_CDNS3_IMX) += cdns3-imx.o
cdnsp-udc-pci-y := cdnsp-pci.o
ifdef CONFIG_USB_CDNSP_PCI
ifeq ($(CONFIG_USB),m)
obj-m += cdnsp-udc-pci.o
else
obj-$(CONFIG_USB_CDNSP_PCI) += cdnsp-udc-pci.o
endif
endif
cdnsp-udc-pci-$(CONFIG_USB_CDNSP_GADGET) += cdnsp-ring.o cdnsp-gadget.o \
cdnsp-mem.o cdnsp-ep0.o
ifneq ($(CONFIG_USB_CDNSP_GADGET),)
cdnsp-udc-pci-$(CONFIG_TRACING) += cdnsp-trace.o
endif

View File

@ -13,8 +13,8 @@
#include <linux/usb/composite.h>
#include <linux/iopoll.h>
#include "gadget.h"
#include "trace.h"
#include "cdns3-gadget.h"
#include "cdns3-trace.h"
static struct usb_endpoint_descriptor cdns3_gadget_ep0_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
@ -364,7 +364,7 @@ static int cdns3_ep0_feature_handle_endpoint(struct cdns3_device *priv_dev,
if (le16_to_cpu(ctrl->wValue) != USB_ENDPOINT_HALT)
return -EINVAL;
if (!(ctrl->wIndex & ~USB_DIR_IN))
if (!(le16_to_cpu(ctrl->wIndex) & ~USB_DIR_IN))
return 0;
index = cdns3_ep_addr_to_index(le16_to_cpu(ctrl->wIndex));
@ -789,7 +789,7 @@ int cdns3_gadget_ep_set_wedge(struct usb_ep *ep)
return 0;
}
const struct usb_ep_ops cdns3_gadget_ep0_ops = {
static const struct usb_ep_ops cdns3_gadget_ep0_ops = {
.enable = cdns3_gadget_ep0_enable,
.disable = cdns3_gadget_ep0_disable,
.alloc_request = cdns3_gadget_ep_alloc_request,

View File

@ -63,8 +63,8 @@
#include "core.h"
#include "gadget-export.h"
#include "gadget.h"
#include "trace.h"
#include "cdns3-gadget.h"
#include "cdns3-trace.h"
#include "drd.h"
static int __cdns3_gadget_ep_queue(struct usb_ep *ep,
@ -1200,7 +1200,7 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
td_size = DIV_ROUND_UP(request->length,
priv_ep->endpoint.maxpacket);
if (priv_dev->gadget.speed == USB_SPEED_SUPER)
trb->length = TRB_TDL_SS_SIZE(td_size);
trb->length = cpu_to_le32(TRB_TDL_SS_SIZE(td_size));
else
control |= TRB_TDL_HS_SIZE(td_size);
}
@ -1247,10 +1247,10 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
priv_req->trb->control = cpu_to_le32(control);
if (sg_supported) {
trb->control |= TRB_ISP;
trb->control |= cpu_to_le32(TRB_ISP);
/* Don't set chain bit for last TRB */
if (sg_iter < num_trb - 1)
trb->control |= TRB_CHAIN;
trb->control |= cpu_to_le32(TRB_CHAIN);
s = sg_next(s);
}
@ -1844,7 +1844,7 @@ __must_hold(&priv_dev->lock)
static irqreturn_t cdns3_device_irq_handler(int irq, void *data)
{
struct cdns3_device *priv_dev = data;
struct cdns3 *cdns = dev_get_drvdata(priv_dev->dev);
struct cdns *cdns = dev_get_drvdata(priv_dev->dev);
irqreturn_t ret = IRQ_NONE;
u32 reg;
@ -3084,7 +3084,7 @@ static void cdns3_gadget_release(struct device *dev)
kfree(priv_dev);
}
static void cdns3_gadget_exit(struct cdns3 *cdns)
static void cdns3_gadget_exit(struct cdns *cdns)
{
struct cdns3_device *priv_dev;
@ -3117,10 +3117,10 @@ static void cdns3_gadget_exit(struct cdns3 *cdns)
kfree(priv_dev->zlp_buf);
usb_put_gadget(&priv_dev->gadget);
cdns->gadget_dev = NULL;
cdns3_drd_gadget_off(cdns);
cdns_drd_gadget_off(cdns);
}
static int cdns3_gadget_start(struct cdns3 *cdns)
static int cdns3_gadget_start(struct cdns *cdns)
{
struct cdns3_device *priv_dev;
u32 max_speed;
@ -3240,7 +3240,7 @@ err1:
return ret;
}
static int __cdns3_gadget_init(struct cdns3 *cdns)
static int __cdns3_gadget_init(struct cdns *cdns)
{
int ret = 0;
@ -3251,7 +3251,7 @@ static int __cdns3_gadget_init(struct cdns3 *cdns)
return ret;
}
cdns3_drd_gadget_on(cdns);
cdns_drd_gadget_on(cdns);
pm_runtime_get_sync(cdns->dev);
ret = cdns3_gadget_start(cdns);
@ -3277,7 +3277,7 @@ err0:
return ret;
}
static int cdns3_gadget_suspend(struct cdns3 *cdns, bool do_wakeup)
static int cdns3_gadget_suspend(struct cdns *cdns, bool do_wakeup)
__must_hold(&cdns->lock)
{
struct cdns3_device *priv_dev = cdns->gadget_dev;
@ -3296,7 +3296,7 @@ __must_hold(&cdns->lock)
return 0;
}
static int cdns3_gadget_resume(struct cdns3 *cdns, bool hibernated)
static int cdns3_gadget_resume(struct cdns *cdns, bool hibernated)
{
struct cdns3_device *priv_dev = cdns->gadget_dev;
@ -3311,13 +3311,13 @@ static int cdns3_gadget_resume(struct cdns3 *cdns, bool hibernated)
/**
* cdns3_gadget_init - initialize device structure
*
* @cdns: cdns3 instance
* @cdns: cdns instance
*
* This function initializes the gadget.
*/
int cdns3_gadget_init(struct cdns3 *cdns)
int cdns3_gadget_init(struct cdns *cdns)
{
struct cdns3_role_driver *rdrv;
struct cdns_role_driver *rdrv;
rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL);
if (!rdrv)
@ -3327,7 +3327,7 @@ int cdns3_gadget_init(struct cdns3 *cdns)
rdrv->stop = cdns3_gadget_exit;
rdrv->suspend = cdns3_gadget_suspend;
rdrv->resume = cdns3_gadget_resume;
rdrv->state = CDNS3_ROLE_STATE_INACTIVE;
rdrv->state = CDNS_ROLE_STATE_INACTIVE;
rdrv->name = "gadget";
cdns->roles[USB_ROLE_DEVICE] = rdrv;

View File

@ -250,7 +250,7 @@ static void cdns3_set_wakeup(struct cdns_imx *data, bool enable)
static int cdns_imx_platform_suspend(struct device *dev,
bool suspend, bool wakeup)
{
struct cdns3 *cdns = dev_get_drvdata(dev);
struct cdns *cdns = dev_get_drvdata(dev);
struct device *parent = dev->parent;
struct cdns_imx *data = dev_get_drvdata(parent);
void __iomem *otg_regs = (void __iomem *)(cdns->otg_regs);

View File

@ -0,0 +1,315 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Cadence USBSS DRD Driver.
*
* Copyright (C) 2018-2020 Cadence.
* Copyright (C) 2017-2018 NXP
* Copyright (C) 2019 Texas Instruments
*
*
* Author: Peter Chen <peter.chen@nxp.com>
* Pawel Laszczak <pawell@cadence.com>
* Roger Quadros <rogerq@ti.com>
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include "core.h"
#include "gadget-export.h"
static int set_phy_power_on(struct cdns *cdns)
{
int ret;
ret = phy_power_on(cdns->usb2_phy);
if (ret)
return ret;
ret = phy_power_on(cdns->usb3_phy);
if (ret)
phy_power_off(cdns->usb2_phy);
return ret;
}
static void set_phy_power_off(struct cdns *cdns)
{
phy_power_off(cdns->usb3_phy);
phy_power_off(cdns->usb2_phy);
}
/**
* cdns3_plat_probe - probe for cdns3 core device
* @pdev: Pointer to cdns3 core platform device
*
* Returns 0 on success otherwise negative errno
*/
static int cdns3_plat_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *res;
struct cdns *cdns;
void __iomem *regs;
int ret;
cdns = devm_kzalloc(dev, sizeof(*cdns), GFP_KERNEL);
if (!cdns)
return -ENOMEM;
cdns->dev = dev;
cdns->pdata = dev_get_platdata(dev);
platform_set_drvdata(pdev, cdns);
res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "host");
if (!res) {
dev_err(dev, "missing host IRQ\n");
return -ENODEV;
}
cdns->xhci_res[0] = *res;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "xhci");
if (!res) {
dev_err(dev, "couldn't get xhci resource\n");
return -ENXIO;
}
cdns->xhci_res[1] = *res;
cdns->dev_irq = platform_get_irq_byname(pdev, "peripheral");
if (cdns->dev_irq < 0)
return cdns->dev_irq;
regs = devm_platform_ioremap_resource_byname(pdev, "dev");
if (IS_ERR(regs))
return PTR_ERR(regs);
cdns->dev_regs = regs;
cdns->otg_irq = platform_get_irq_byname(pdev, "otg");
if (cdns->otg_irq < 0)
return cdns->otg_irq;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "otg");
if (!res) {
dev_err(dev, "couldn't get otg resource\n");
return -ENXIO;
}
cdns->phyrst_a_enable = device_property_read_bool(dev, "cdns,phyrst-a-enable");
cdns->otg_res = *res;
cdns->wakeup_irq = platform_get_irq_byname_optional(pdev, "wakeup");
if (cdns->wakeup_irq == -EPROBE_DEFER)
return cdns->wakeup_irq;
else if (cdns->wakeup_irq == 0)
return -EINVAL;
if (cdns->wakeup_irq < 0) {
dev_dbg(dev, "couldn't get wakeup irq\n");
cdns->wakeup_irq = 0x0;
}
cdns->usb2_phy = devm_phy_optional_get(dev, "cdns3,usb2-phy");
if (IS_ERR(cdns->usb2_phy))
return PTR_ERR(cdns->usb2_phy);
ret = phy_init(cdns->usb2_phy);
if (ret)
return ret;
cdns->usb3_phy = devm_phy_optional_get(dev, "cdns3,usb3-phy");
if (IS_ERR(cdns->usb3_phy))
return PTR_ERR(cdns->usb3_phy);
ret = phy_init(cdns->usb3_phy);
if (ret)
goto err_phy3_init;
ret = set_phy_power_on(cdns);
if (ret)
goto err_phy_power_on;
cdns->gadget_init = cdns3_gadget_init;
ret = cdns_init(cdns);
if (ret)
goto err_cdns_init;
device_set_wakeup_capable(dev, true);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
if (!(cdns->pdata && (cdns->pdata->quirks & CDNS3_DEFAULT_PM_RUNTIME_ALLOW)))
pm_runtime_forbid(dev);
/*
* The controller needs less time between bus and controller suspend,
* and we also needs a small delay to avoid frequently entering low
* power mode.
*/
pm_runtime_set_autosuspend_delay(dev, 20);
pm_runtime_mark_last_busy(dev);
pm_runtime_use_autosuspend(dev);
return 0;
err_cdns_init:
set_phy_power_off(cdns);
err_phy_power_on:
phy_exit(cdns->usb3_phy);
err_phy3_init:
phy_exit(cdns->usb2_phy);
return ret;
}
/**
* cdns3_remove - unbind drd driver and clean up
* @pdev: Pointer to Linux platform device
*
* Returns 0 on success otherwise negative errno
*/
static int cdns3_plat_remove(struct platform_device *pdev)
{
struct cdns *cdns = platform_get_drvdata(pdev);
struct device *dev = cdns->dev;
pm_runtime_get_sync(dev);
pm_runtime_disable(dev);
pm_runtime_put_noidle(dev);
cdns_remove(cdns);
set_phy_power_off(cdns);
phy_exit(cdns->usb2_phy);
phy_exit(cdns->usb3_phy);
return 0;
}
#ifdef CONFIG_PM
static int cdns3_set_platform_suspend(struct device *dev,
bool suspend, bool wakeup)
{
struct cdns *cdns = dev_get_drvdata(dev);
int ret = 0;
if (cdns->pdata && cdns->pdata->platform_suspend)
ret = cdns->pdata->platform_suspend(dev, suspend, wakeup);
return ret;
}
static int cdns3_controller_suspend(struct device *dev, pm_message_t msg)
{
struct cdns *cdns = dev_get_drvdata(dev);
bool wakeup;
unsigned long flags;
if (cdns->in_lpm)
return 0;
if (PMSG_IS_AUTO(msg))
wakeup = true;
else
wakeup = device_may_wakeup(dev);
cdns3_set_platform_suspend(cdns->dev, true, wakeup);
set_phy_power_off(cdns);
spin_lock_irqsave(&cdns->lock, flags);
cdns->in_lpm = true;
spin_unlock_irqrestore(&cdns->lock, flags);
dev_dbg(cdns->dev, "%s ends\n", __func__);
return 0;
}
static int cdns3_controller_resume(struct device *dev, pm_message_t msg)
{
struct cdns *cdns = dev_get_drvdata(dev);
int ret;
unsigned long flags;
if (!cdns->in_lpm)
return 0;
ret = set_phy_power_on(cdns);
if (ret)
return ret;
cdns3_set_platform_suspend(cdns->dev, false, false);
spin_lock_irqsave(&cdns->lock, flags);
cdns_resume(cdns, !PMSG_IS_AUTO(msg));
cdns->in_lpm = false;
spin_unlock_irqrestore(&cdns->lock, flags);
if (cdns->wakeup_pending) {
cdns->wakeup_pending = false;
enable_irq(cdns->wakeup_irq);
}
dev_dbg(cdns->dev, "%s ends\n", __func__);
return ret;
}
static int cdns3_plat_runtime_suspend(struct device *dev)
{
return cdns3_controller_suspend(dev, PMSG_AUTO_SUSPEND);
}
static int cdns3_plat_runtime_resume(struct device *dev)
{
return cdns3_controller_resume(dev, PMSG_AUTO_RESUME);
}
#ifdef CONFIG_PM_SLEEP
static int cdns3_plat_suspend(struct device *dev)
{
struct cdns *cdns = dev_get_drvdata(dev);
cdns_suspend(cdns);
return cdns3_controller_suspend(dev, PMSG_SUSPEND);
}
static int cdns3_plat_resume(struct device *dev)
{
return cdns3_controller_resume(dev, PMSG_RESUME);
}
#endif /* CONFIG_PM_SLEEP */
#endif /* CONFIG_PM */
static const struct dev_pm_ops cdns3_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(cdns3_plat_suspend, cdns3_plat_resume)
SET_RUNTIME_PM_OPS(cdns3_plat_runtime_suspend,
cdns3_plat_runtime_resume, NULL)
};
#ifdef CONFIG_OF
static const struct of_device_id of_cdns3_match[] = {
{ .compatible = "cdns,usb3" },
{ },
};
MODULE_DEVICE_TABLE(of, of_cdns3_match);
#endif
static struct platform_driver cdns3_driver = {
.probe = cdns3_plat_probe,
.remove = cdns3_plat_remove,
.driver = {
.name = "cdns-usb3",
.of_match_table = of_match_ptr(of_cdns3_match),
.pm = &cdns3_pm_ops,
},
};
module_platform_driver(cdns3_driver);
MODULE_ALIAS("platform:cdns3");
MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Cadence USB3 DRD Controller Driver");

View File

@ -214,6 +214,7 @@ static int cdns_ti_remove(struct platform_device *pdev)
static const struct of_device_id cdns_ti_of_match[] = {
{ .compatible = "ti,j721e-usb", },
{ .compatible = "ti,am64-usb", },
{},
};
MODULE_DEVICE_TABLE(of, cdns_ti_of_match);

View File

@ -8,4 +8,4 @@
*/
#define CREATE_TRACE_POINTS
#include "trace.h"
#include "cdns3-trace.h"

View File

@ -19,8 +19,8 @@
#include <asm/byteorder.h>
#include <linux/usb/ch9.h>
#include "core.h"
#include "gadget.h"
#include "debug.h"
#include "cdns3-gadget.h"
#include "cdns3-debug.h"
#define CDNS3_MSG_MAX 500
@ -565,6 +565,6 @@ DEFINE_EVENT(cdns3_log_request_handled, cdns3_request_handled,
#define TRACE_INCLUDE_PATH .
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE trace
#define TRACE_INCLUDE_FILE cdns3-trace
#include <trace/define_trace.h>

View File

@ -0,0 +1,583 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Cadence CDNSP DRD Driver.
*
* Copyright (C) 2020 Cadence.
*
* Author: Pawel Laszczak <pawell@cadence.com>
*
*/
#ifndef __LINUX_CDNSP_DEBUG
#define __LINUX_CDNSP_DEBUG
static inline const char *cdnsp_trb_comp_code_string(u8 status)
{
switch (status) {
case COMP_INVALID:
return "Invalid";
case COMP_SUCCESS:
return "Success";
case COMP_DATA_BUFFER_ERROR:
return "Data Buffer Error";
case COMP_BABBLE_DETECTED_ERROR:
return "Babble Detected";
case COMP_TRB_ERROR:
return "TRB Error";
case COMP_RESOURCE_ERROR:
return "Resource Error";
case COMP_NO_SLOTS_AVAILABLE_ERROR:
return "No Slots Available Error";
case COMP_INVALID_STREAM_TYPE_ERROR:
return "Invalid Stream Type Error";
case COMP_SLOT_NOT_ENABLED_ERROR:
return "Slot Not Enabled Error";
case COMP_ENDPOINT_NOT_ENABLED_ERROR:
return "Endpoint Not Enabled Error";
case COMP_SHORT_PACKET:
return "Short Packet";
case COMP_RING_UNDERRUN:
return "Ring Underrun";
case COMP_RING_OVERRUN:
return "Ring Overrun";
case COMP_VF_EVENT_RING_FULL_ERROR:
return "VF Event Ring Full Error";
case COMP_PARAMETER_ERROR:
return "Parameter Error";
case COMP_CONTEXT_STATE_ERROR:
return "Context State Error";
case COMP_EVENT_RING_FULL_ERROR:
return "Event Ring Full Error";
case COMP_INCOMPATIBLE_DEVICE_ERROR:
return "Incompatible Device Error";
case COMP_MISSED_SERVICE_ERROR:
return "Missed Service Error";
case COMP_COMMAND_RING_STOPPED:
return "Command Ring Stopped";
case COMP_COMMAND_ABORTED:
return "Command Aborted";
case COMP_STOPPED:
return "Stopped";
case COMP_STOPPED_LENGTH_INVALID:
return "Stopped - Length Invalid";
case COMP_STOPPED_SHORT_PACKET:
return "Stopped - Short Packet";
case COMP_MAX_EXIT_LATENCY_TOO_LARGE_ERROR:
return "Max Exit Latency Too Large Error";
case COMP_ISOCH_BUFFER_OVERRUN:
return "Isoch Buffer Overrun";
case COMP_EVENT_LOST_ERROR:
return "Event Lost Error";
case COMP_UNDEFINED_ERROR:
return "Undefined Error";
case COMP_INVALID_STREAM_ID_ERROR:
return "Invalid Stream ID Error";
default:
return "Unknown!!";
}
}
static inline const char *cdnsp_trb_type_string(u8 type)
{
switch (type) {
case TRB_NORMAL:
return "Normal";
case TRB_SETUP:
return "Setup Stage";
case TRB_DATA:
return "Data Stage";
case TRB_STATUS:
return "Status Stage";
case TRB_ISOC:
return "Isoch";
case TRB_LINK:
return "Link";
case TRB_EVENT_DATA:
return "Event Data";
case TRB_TR_NOOP:
return "No-Op";
case TRB_ENABLE_SLOT:
return "Enable Slot Command";
case TRB_DISABLE_SLOT:
return "Disable Slot Command";
case TRB_ADDR_DEV:
return "Address Device Command";
case TRB_CONFIG_EP:
return "Configure Endpoint Command";
case TRB_EVAL_CONTEXT:
return "Evaluate Context Command";
case TRB_RESET_EP:
return "Reset Endpoint Command";
case TRB_STOP_RING:
return "Stop Ring Command";
case TRB_SET_DEQ:
return "Set TR Dequeue Pointer Command";
case TRB_RESET_DEV:
return "Reset Device Command";
case TRB_FORCE_HEADER:
return "Force Header Command";
case TRB_CMD_NOOP:
return "No-Op Command";
case TRB_TRANSFER:
return "Transfer Event";
case TRB_COMPLETION:
return "Command Completion Event";
case TRB_PORT_STATUS:
return "Port Status Change Event";
case TRB_HC_EVENT:
return "Device Controller Event";
case TRB_MFINDEX_WRAP:
return "MFINDEX Wrap Event";
case TRB_ENDPOINT_NRDY:
return "Endpoint Not ready";
case TRB_HALT_ENDPOINT:
return "Halt Endpoint";
case TRB_FLUSH_ENDPOINT:
return "FLush Endpoint";
default:
return "UNKNOWN";
}
}
static inline const char *cdnsp_ring_type_string(enum cdnsp_ring_type type)
{
switch (type) {
case TYPE_CTRL:
return "CTRL";
case TYPE_ISOC:
return "ISOC";
case TYPE_BULK:
return "BULK";
case TYPE_INTR:
return "INTR";
case TYPE_STREAM:
return "STREAM";
case TYPE_COMMAND:
return "CMD";
case TYPE_EVENT:
return "EVENT";
}
return "UNKNOWN";
}
static inline char *cdnsp_slot_state_string(u32 state)
{
switch (state) {
case SLOT_STATE_ENABLED:
return "enabled/disabled";
case SLOT_STATE_DEFAULT:
return "default";
case SLOT_STATE_ADDRESSED:
return "addressed";
case SLOT_STATE_CONFIGURED:
return "configured";
default:
return "reserved";
}
}
static inline const char *cdnsp_decode_trb(char *str, size_t size, u32 field0,
u32 field1, u32 field2, u32 field3)
{
int ep_id = TRB_TO_EP_INDEX(field3) - 1;
int type = TRB_FIELD_TO_TYPE(field3);
unsigned int ep_num;
int ret = 0;
u32 temp;
ep_num = DIV_ROUND_UP(ep_id, 2);
switch (type) {
case TRB_LINK:
ret += snprintf(str, size,
"LINK %08x%08x intr %ld type '%s' flags %c:%c:%c:%c",
field1, field0, GET_INTR_TARGET(field2),
cdnsp_trb_type_string(type),
field3 & TRB_IOC ? 'I' : 'i',
field3 & TRB_CHAIN ? 'C' : 'c',
field3 & TRB_TC ? 'T' : 't',
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_TRANSFER:
case TRB_COMPLETION:
case TRB_PORT_STATUS:
case TRB_HC_EVENT:
ret += snprintf(str, size,
"ep%d%s(%d) type '%s' TRB %08x%08x status '%s'"
" len %ld slot %ld flags %c:%c",
ep_num, ep_id % 2 ? "out" : "in",
TRB_TO_EP_INDEX(field3),
cdnsp_trb_type_string(type), field1, field0,
cdnsp_trb_comp_code_string(GET_COMP_CODE(field2)),
EVENT_TRB_LEN(field2), TRB_TO_SLOT_ID(field3),
field3 & EVENT_DATA ? 'E' : 'e',
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_MFINDEX_WRAP:
ret += snprintf(str, size, "%s: flags %c",
cdnsp_trb_type_string(type),
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_SETUP:
ret += snprintf(str, size,
"type '%s' bRequestType %02x bRequest %02x "
"wValue %02x%02x wIndex %02x%02x wLength %d "
"length %ld TD size %ld intr %ld Setup ID %ld "
"flags %c:%c:%c",
cdnsp_trb_type_string(type),
field0 & 0xff,
(field0 & 0xff00) >> 8,
(field0 & 0xff000000) >> 24,
(field0 & 0xff0000) >> 16,
(field1 & 0xff00) >> 8,
field1 & 0xff,
(field1 & 0xff000000) >> 16 |
(field1 & 0xff0000) >> 16,
TRB_LEN(field2), GET_TD_SIZE(field2),
GET_INTR_TARGET(field2),
TRB_SETUPID_TO_TYPE(field3),
field3 & TRB_IDT ? 'D' : 'd',
field3 & TRB_IOC ? 'I' : 'i',
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_DATA:
ret += snprintf(str, size,
"type '%s' Buffer %08x%08x length %ld TD size %ld "
"intr %ld flags %c:%c:%c:%c:%c:%c:%c",
cdnsp_trb_type_string(type),
field1, field0, TRB_LEN(field2),
GET_TD_SIZE(field2),
GET_INTR_TARGET(field2),
field3 & TRB_IDT ? 'D' : 'i',
field3 & TRB_IOC ? 'I' : 'i',
field3 & TRB_CHAIN ? 'C' : 'c',
field3 & TRB_NO_SNOOP ? 'S' : 's',
field3 & TRB_ISP ? 'I' : 'i',
field3 & TRB_ENT ? 'E' : 'e',
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_STATUS:
ret += snprintf(str, size,
"Buffer %08x%08x length %ld TD size %ld intr"
"%ld type '%s' flags %c:%c:%c:%c",
field1, field0, TRB_LEN(field2),
GET_TD_SIZE(field2),
GET_INTR_TARGET(field2),
cdnsp_trb_type_string(type),
field3 & TRB_IOC ? 'I' : 'i',
field3 & TRB_CHAIN ? 'C' : 'c',
field3 & TRB_ENT ? 'E' : 'e',
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_NORMAL:
case TRB_ISOC:
case TRB_EVENT_DATA:
case TRB_TR_NOOP:
ret += snprintf(str, size,
"type '%s' Buffer %08x%08x length %ld "
"TD size %ld intr %ld "
"flags %c:%c:%c:%c:%c:%c:%c:%c:%c",
cdnsp_trb_type_string(type),
field1, field0, TRB_LEN(field2),
GET_TD_SIZE(field2),
GET_INTR_TARGET(field2),
field3 & TRB_BEI ? 'B' : 'b',
field3 & TRB_IDT ? 'T' : 't',
field3 & TRB_IOC ? 'I' : 'i',
field3 & TRB_CHAIN ? 'C' : 'c',
field3 & TRB_NO_SNOOP ? 'S' : 's',
field3 & TRB_ISP ? 'I' : 'i',
field3 & TRB_ENT ? 'E' : 'e',
field3 & TRB_CYCLE ? 'C' : 'c',
!(field3 & TRB_EVENT_INVALIDATE) ? 'V' : 'v');
break;
case TRB_CMD_NOOP:
case TRB_ENABLE_SLOT:
ret += snprintf(str, size, "%s: flags %c",
cdnsp_trb_type_string(type),
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_DISABLE_SLOT:
ret += snprintf(str, size, "%s: slot %ld flags %c",
cdnsp_trb_type_string(type),
TRB_TO_SLOT_ID(field3),
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_ADDR_DEV:
ret += snprintf(str, size,
"%s: ctx %08x%08x slot %ld flags %c:%c",
cdnsp_trb_type_string(type), field1, field0,
TRB_TO_SLOT_ID(field3),
field3 & TRB_BSR ? 'B' : 'b',
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_CONFIG_EP:
ret += snprintf(str, size,
"%s: ctx %08x%08x slot %ld flags %c:%c",
cdnsp_trb_type_string(type), field1, field0,
TRB_TO_SLOT_ID(field3),
field3 & TRB_DC ? 'D' : 'd',
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_EVAL_CONTEXT:
ret += snprintf(str, size,
"%s: ctx %08x%08x slot %ld flags %c",
cdnsp_trb_type_string(type), field1, field0,
TRB_TO_SLOT_ID(field3),
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_RESET_EP:
case TRB_HALT_ENDPOINT:
case TRB_FLUSH_ENDPOINT:
ret += snprintf(str, size,
"%s: ep%d%s(%d) ctx %08x%08x slot %ld flags %c",
cdnsp_trb_type_string(type),
ep_num, ep_id % 2 ? "out" : "in",
TRB_TO_EP_INDEX(field3), field1, field0,
TRB_TO_SLOT_ID(field3),
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_STOP_RING:
ret += snprintf(str, size,
"%s: ep%d%s(%d) slot %ld sp %d flags %c",
cdnsp_trb_type_string(type),
ep_num, ep_id % 2 ? "out" : "in",
TRB_TO_EP_INDEX(field3),
TRB_TO_SLOT_ID(field3),
TRB_TO_SUSPEND_PORT(field3),
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_SET_DEQ:
ret += snprintf(str, size,
"%s: ep%d%s(%d) deq %08x%08x stream %ld slot %ld flags %c",
cdnsp_trb_type_string(type),
ep_num, ep_id % 2 ? "out" : "in",
TRB_TO_EP_INDEX(field3), field1, field0,
TRB_TO_STREAM_ID(field2),
TRB_TO_SLOT_ID(field3),
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_RESET_DEV:
ret += snprintf(str, size, "%s: slot %ld flags %c",
cdnsp_trb_type_string(type),
TRB_TO_SLOT_ID(field3),
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_ENDPOINT_NRDY:
temp = TRB_TO_HOST_STREAM(field2);
ret += snprintf(str, size,
"%s: ep%d%s(%d) H_SID %x%s%s D_SID %lx flags %c:%c",
cdnsp_trb_type_string(type),
ep_num, ep_id % 2 ? "out" : "in",
TRB_TO_EP_INDEX(field3), temp,
temp == STREAM_PRIME_ACK ? "(PRIME)" : "",
temp == STREAM_REJECTED ? "(REJECTED)" : "",
TRB_TO_DEV_STREAM(field0),
field3 & TRB_STAT ? 'S' : 's',
field3 & TRB_CYCLE ? 'C' : 'c');
break;
default:
ret += snprintf(str, size,
"type '%s' -> raw %08x %08x %08x %08x",
cdnsp_trb_type_string(type),
field0, field1, field2, field3);
}
return str;
}
static inline const char *cdnsp_decode_slot_context(u32 info, u32 info2,
u32 int_target, u32 state)
{
static char str[1024];
int ret = 0;
u32 speed;
char *s;
speed = info & DEV_SPEED;
switch (speed) {
case SLOT_SPEED_FS:
s = "full-speed";
break;
case SLOT_SPEED_HS:
s = "high-speed";
break;
case SLOT_SPEED_SS:
s = "super-speed";
break;
case SLOT_SPEED_SSP:
s = "super-speed plus";
break;
default:
s = "UNKNOWN speed";
}
ret = sprintf(str, "%s Ctx Entries %d",
s, (info & LAST_CTX_MASK) >> 27);
ret += sprintf(str + ret, " [Intr %ld] Addr %ld State %s",
GET_INTR_TARGET(int_target), state & DEV_ADDR_MASK,
cdnsp_slot_state_string(GET_SLOT_STATE(state)));
return str;
}
static inline const char *cdnsp_portsc_link_state_string(u32 portsc)
{
switch (portsc & PORT_PLS_MASK) {
case XDEV_U0:
return "U0";
case XDEV_U1:
return "U1";
case XDEV_U2:
return "U2";
case XDEV_U3:
return "U3";
case XDEV_DISABLED:
return "Disabled";
case XDEV_RXDETECT:
return "RxDetect";
case XDEV_INACTIVE:
return "Inactive";
case XDEV_POLLING:
return "Polling";
case XDEV_RECOVERY:
return "Recovery";
case XDEV_HOT_RESET:
return "Hot Reset";
case XDEV_COMP_MODE:
return "Compliance mode";
case XDEV_TEST_MODE:
return "Test mode";
case XDEV_RESUME:
return "Resume";
default:
break;
}
return "Unknown";
}
static inline const char *cdnsp_decode_portsc(char *str, size_t size,
u32 portsc)
{
int ret;
ret = snprintf(str, size, "%s %s %s Link:%s PortSpeed:%d ",
portsc & PORT_POWER ? "Powered" : "Powered-off",
portsc & PORT_CONNECT ? "Connected" : "Not-connected",
portsc & PORT_PED ? "Enabled" : "Disabled",
cdnsp_portsc_link_state_string(portsc),
DEV_PORT_SPEED(portsc));
if (portsc & PORT_RESET)
ret += snprintf(str + ret, size - ret, "In-Reset ");
ret += snprintf(str + ret, size - ret, "Change: ");
if (portsc & PORT_CSC)
ret += snprintf(str + ret, size - ret, "CSC ");
if (portsc & PORT_WRC)
ret += snprintf(str + ret, size - ret, "WRC ");
if (portsc & PORT_RC)
ret += snprintf(str + ret, size - ret, "PRC ");
if (portsc & PORT_PLC)
ret += snprintf(str + ret, size - ret, "PLC ");
if (portsc & PORT_CEC)
ret += snprintf(str + ret, size - ret, "CEC ");
ret += snprintf(str + ret, size - ret, "Wake: ");
if (portsc & PORT_WKCONN_E)
ret += snprintf(str + ret, size - ret, "WCE ");
if (portsc & PORT_WKDISC_E)
ret += snprintf(str + ret, size - ret, "WDE ");
return str;
}
static inline const char *cdnsp_ep_state_string(u8 state)
{
switch (state) {
case EP_STATE_DISABLED:
return "disabled";
case EP_STATE_RUNNING:
return "running";
case EP_STATE_HALTED:
return "halted";
case EP_STATE_STOPPED:
return "stopped";
case EP_STATE_ERROR:
return "error";
default:
return "INVALID";
}
}
static inline const char *cdnsp_ep_type_string(u8 type)
{
switch (type) {
case ISOC_OUT_EP:
return "Isoc OUT";
case BULK_OUT_EP:
return "Bulk OUT";
case INT_OUT_EP:
return "Int OUT";
case CTRL_EP:
return "Ctrl";
case ISOC_IN_EP:
return "Isoc IN";
case BULK_IN_EP:
return "Bulk IN";
case INT_IN_EP:
return "Int IN";
default:
return "INVALID";
}
}
static inline const char *cdnsp_decode_ep_context(char *str, size_t size,
u32 info, u32 info2,
u64 deq, u32 tx_info)
{
u8 max_pstr, ep_state, interval, ep_type, burst, cerr, mult;
bool lsa, hid;
u16 maxp, avg;
u32 esit;
int ret;
esit = CTX_TO_MAX_ESIT_PAYLOAD_HI(info) << 16 |
CTX_TO_MAX_ESIT_PAYLOAD_LO(tx_info);
ep_state = info & EP_STATE_MASK;
max_pstr = CTX_TO_EP_MAXPSTREAMS(info);
interval = CTX_TO_EP_INTERVAL(info);
mult = CTX_TO_EP_MULT(info) + 1;
lsa = !!(info & EP_HAS_LSA);
cerr = (info2 & (3 << 1)) >> 1;
ep_type = CTX_TO_EP_TYPE(info2);
hid = !!(info2 & (1 << 7));
burst = CTX_TO_MAX_BURST(info2);
maxp = MAX_PACKET_DECODED(info2);
avg = EP_AVG_TRB_LENGTH(tx_info);
ret = snprintf(str, size, "State %s mult %d max P. Streams %d %s",
cdnsp_ep_state_string(ep_state), mult,
max_pstr, lsa ? "LSA " : "");
ret += snprintf(str + ret, size - ret,
"interval %d us max ESIT payload %d CErr %d ",
(1 << interval) * 125, esit, cerr);
ret += snprintf(str + ret, size - ret,
"Type %s %sburst %d maxp %d deq %016llx ",
cdnsp_ep_type_string(ep_type), hid ? "HID" : "",
burst, maxp, deq);
ret += snprintf(str + ret, size - ret, "avg trb len %d", avg);
return str;
}
#endif /*__LINUX_CDNSP_DEBUG*/

View File

@ -0,0 +1,489 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Cadence CDNSP DRD Driver.
*
* Copyright (C) 2020 Cadence.
*
* Author: Pawel Laszczak <pawell@cadence.com>
*
*/
#include <linux/usb/composite.h>
#include <linux/usb/gadget.h>
#include <linux/list.h>
#include "cdnsp-gadget.h"
#include "cdnsp-trace.h"
static void cdnsp_ep0_stall(struct cdnsp_device *pdev)
{
struct cdnsp_request *preq;
struct cdnsp_ep *pep;
pep = &pdev->eps[0];
preq = next_request(&pep->pending_list);
if (pdev->three_stage_setup) {
cdnsp_halt_endpoint(pdev, pep, true);
if (preq)
cdnsp_gadget_giveback(pep, preq, -ECONNRESET);
} else {
pep->ep_state |= EP0_HALTED_STATUS;
if (preq)
list_del(&preq->list);
cdnsp_status_stage(pdev);
}
}
static int cdnsp_ep0_delegate_req(struct cdnsp_device *pdev,
struct usb_ctrlrequest *ctrl)
{
int ret;
spin_unlock(&pdev->lock);
ret = pdev->gadget_driver->setup(&pdev->gadget, ctrl);
spin_lock(&pdev->lock);
return ret;
}
static int cdnsp_ep0_set_config(struct cdnsp_device *pdev,
struct usb_ctrlrequest *ctrl)
{
enum usb_device_state state = pdev->gadget.state;
u32 cfg;
int ret;
cfg = le16_to_cpu(ctrl->wValue);
switch (state) {
case USB_STATE_ADDRESS:
trace_cdnsp_ep0_set_config("from Address state");
break;
case USB_STATE_CONFIGURED:
trace_cdnsp_ep0_set_config("from Configured state");
break;
default:
dev_err(pdev->dev, "Set Configuration - bad device state\n");
return -EINVAL;
}
ret = cdnsp_ep0_delegate_req(pdev, ctrl);
if (ret)
return ret;
if (!cfg)
usb_gadget_set_state(&pdev->gadget, USB_STATE_ADDRESS);
return 0;
}
static int cdnsp_ep0_set_address(struct cdnsp_device *pdev,
struct usb_ctrlrequest *ctrl)
{
enum usb_device_state state = pdev->gadget.state;
struct cdnsp_slot_ctx *slot_ctx;
unsigned int slot_state;
int ret;
u32 addr;
addr = le16_to_cpu(ctrl->wValue);
if (addr > 127) {
dev_err(pdev->dev, "Invalid device address %d\n", addr);
return -EINVAL;
}
slot_ctx = cdnsp_get_slot_ctx(&pdev->out_ctx);
if (state == USB_STATE_CONFIGURED) {
dev_err(pdev->dev, "Can't Set Address from Configured State\n");
return -EINVAL;
}
pdev->device_address = le16_to_cpu(ctrl->wValue);
slot_ctx = cdnsp_get_slot_ctx(&pdev->out_ctx);
slot_state = GET_SLOT_STATE(le32_to_cpu(slot_ctx->dev_state));
if (slot_state == SLOT_STATE_ADDRESSED)
cdnsp_reset_device(pdev);
/*set device address*/
ret = cdnsp_setup_device(pdev, SETUP_CONTEXT_ADDRESS);
if (ret)
return ret;
if (addr)
usb_gadget_set_state(&pdev->gadget, USB_STATE_ADDRESS);
else
usb_gadget_set_state(&pdev->gadget, USB_STATE_DEFAULT);
return 0;
}
int cdnsp_status_stage(struct cdnsp_device *pdev)
{
pdev->ep0_stage = CDNSP_STATUS_STAGE;
pdev->ep0_preq.request.length = 0;
return cdnsp_ep_enqueue(pdev->ep0_preq.pep, &pdev->ep0_preq);
}
static int cdnsp_w_index_to_ep_index(u16 wIndex)
{
if (!(wIndex & USB_ENDPOINT_NUMBER_MASK))
return 0;
return ((wIndex & USB_ENDPOINT_NUMBER_MASK) * 2) +
(wIndex & USB_ENDPOINT_DIR_MASK ? 1 : 0) - 1;
}
static int cdnsp_ep0_handle_status(struct cdnsp_device *pdev,
struct usb_ctrlrequest *ctrl)
{
struct cdnsp_ep *pep;
__le16 *response;
int ep_sts = 0;
u16 status = 0;
u32 recipient;
recipient = ctrl->bRequestType & USB_RECIP_MASK;
switch (recipient) {
case USB_RECIP_DEVICE:
status = pdev->gadget.is_selfpowered;
status |= pdev->may_wakeup << USB_DEVICE_REMOTE_WAKEUP;
if (pdev->gadget.speed >= USB_SPEED_SUPER) {
status |= pdev->u1_allowed << USB_DEV_STAT_U1_ENABLED;
status |= pdev->u2_allowed << USB_DEV_STAT_U2_ENABLED;
}
break;
case USB_RECIP_INTERFACE:
/*
* Function Remote Wake Capable D0
* Function Remote Wakeup D1
*/
return cdnsp_ep0_delegate_req(pdev, ctrl);
case USB_RECIP_ENDPOINT:
ep_sts = cdnsp_w_index_to_ep_index(le16_to_cpu(ctrl->wIndex));
pep = &pdev->eps[ep_sts];
ep_sts = GET_EP_CTX_STATE(pep->out_ctx);
/* check if endpoint is stalled */
if (ep_sts == EP_STATE_HALTED)
status = BIT(USB_ENDPOINT_HALT);
break;
default:
return -EINVAL;
}
response = (__le16 *)pdev->setup_buf;
*response = cpu_to_le16(status);
pdev->ep0_preq.request.length = sizeof(*response);
pdev->ep0_preq.request.buf = pdev->setup_buf;
return cdnsp_ep_enqueue(pdev->ep0_preq.pep, &pdev->ep0_preq);
}
static void cdnsp_enter_test_mode(struct cdnsp_device *pdev)
{
u32 temp;
temp = readl(&pdev->active_port->regs->portpmsc) & ~GENMASK(31, 28);
temp |= PORT_TEST_MODE(pdev->test_mode);
writel(temp, &pdev->active_port->regs->portpmsc);
}
static int cdnsp_ep0_handle_feature_device(struct cdnsp_device *pdev,
struct usb_ctrlrequest *ctrl,
int set)
{
enum usb_device_state state;
enum usb_device_speed speed;
u16 tmode;
state = pdev->gadget.state;
speed = pdev->gadget.speed;
switch (le16_to_cpu(ctrl->wValue)) {
case USB_DEVICE_REMOTE_WAKEUP:
pdev->may_wakeup = !!set;
trace_cdnsp_may_wakeup(set);
break;
case USB_DEVICE_U1_ENABLE:
if (state != USB_STATE_CONFIGURED || speed < USB_SPEED_SUPER)
return -EINVAL;
pdev->u1_allowed = !!set;
trace_cdnsp_u1(set);
break;
case USB_DEVICE_U2_ENABLE:
if (state != USB_STATE_CONFIGURED || speed < USB_SPEED_SUPER)
return -EINVAL;
pdev->u2_allowed = !!set;
trace_cdnsp_u2(set);
break;
case USB_DEVICE_LTM_ENABLE:
return -EINVAL;
case USB_DEVICE_TEST_MODE:
if (state != USB_STATE_CONFIGURED || speed > USB_SPEED_HIGH)
return -EINVAL;
tmode = le16_to_cpu(ctrl->wIndex);
if (!set || (tmode & 0xff) != 0)
return -EINVAL;
tmode = tmode >> 8;
if (tmode > USB_TEST_FORCE_ENABLE || tmode < USB_TEST_J)
return -EINVAL;
pdev->test_mode = tmode;
/*
* Test mode must be set before Status Stage but controller
* will start testing sequence after Status Stage.
*/
cdnsp_enter_test_mode(pdev);
break;
default:
return -EINVAL;
}
return 0;
}
static int cdnsp_ep0_handle_feature_intf(struct cdnsp_device *pdev,
struct usb_ctrlrequest *ctrl,
int set)
{
u16 wValue, wIndex;
int ret;
wValue = le16_to_cpu(ctrl->wValue);
wIndex = le16_to_cpu(ctrl->wIndex);
switch (wValue) {
case USB_INTRF_FUNC_SUSPEND:
ret = cdnsp_ep0_delegate_req(pdev, ctrl);
if (ret)
return ret;
/*
* Remote wakeup is enabled when any function within a device
* is enabled for function remote wakeup.
*/
if (wIndex & USB_INTRF_FUNC_SUSPEND_RW)
pdev->may_wakeup++;
else
if (pdev->may_wakeup > 0)
pdev->may_wakeup--;
return 0;
default:
return -EINVAL;
}
return 0;
}
static int cdnsp_ep0_handle_feature_endpoint(struct cdnsp_device *pdev,
struct usb_ctrlrequest *ctrl,
int set)
{
struct cdnsp_ep *pep;
u16 wValue;
wValue = le16_to_cpu(ctrl->wValue);
pep = &pdev->eps[cdnsp_w_index_to_ep_index(le16_to_cpu(ctrl->wIndex))];
switch (wValue) {
case USB_ENDPOINT_HALT:
if (!set && (pep->ep_state & EP_WEDGE)) {
/* Resets Sequence Number */
cdnsp_halt_endpoint(pdev, pep, 0);
cdnsp_halt_endpoint(pdev, pep, 1);
break;
}
return cdnsp_halt_endpoint(pdev, pep, set);
default:
dev_warn(pdev->dev, "WARN Incorrect wValue %04x\n", wValue);
return -EINVAL;
}
return 0;
}
static int cdnsp_ep0_handle_feature(struct cdnsp_device *pdev,
struct usb_ctrlrequest *ctrl,
int set)
{
switch (ctrl->bRequestType & USB_RECIP_MASK) {
case USB_RECIP_DEVICE:
return cdnsp_ep0_handle_feature_device(pdev, ctrl, set);
case USB_RECIP_INTERFACE:
return cdnsp_ep0_handle_feature_intf(pdev, ctrl, set);
case USB_RECIP_ENDPOINT:
return cdnsp_ep0_handle_feature_endpoint(pdev, ctrl, set);
default:
return -EINVAL;
}
}
static int cdnsp_ep0_set_sel(struct cdnsp_device *pdev,
struct usb_ctrlrequest *ctrl)
{
enum usb_device_state state = pdev->gadget.state;
u16 wLength;
if (state == USB_STATE_DEFAULT)
return -EINVAL;
wLength = le16_to_cpu(ctrl->wLength);
if (wLength != 6) {
dev_err(pdev->dev, "Set SEL should be 6 bytes, got %d\n",
wLength);
return -EINVAL;
}
/*
* To handle Set SEL we need to receive 6 bytes from Host. So let's
* queue a usb_request for 6 bytes.
*/
pdev->ep0_preq.request.length = 6;
pdev->ep0_preq.request.buf = pdev->setup_buf;
return cdnsp_ep_enqueue(pdev->ep0_preq.pep, &pdev->ep0_preq);
}
static int cdnsp_ep0_set_isoch_delay(struct cdnsp_device *pdev,
struct usb_ctrlrequest *ctrl)
{
if (le16_to_cpu(ctrl->wIndex) || le16_to_cpu(ctrl->wLength))
return -EINVAL;
pdev->gadget.isoch_delay = le16_to_cpu(ctrl->wValue);
return 0;
}
static int cdnsp_ep0_std_request(struct cdnsp_device *pdev,
struct usb_ctrlrequest *ctrl)
{
int ret;
switch (ctrl->bRequest) {
case USB_REQ_GET_STATUS:
ret = cdnsp_ep0_handle_status(pdev, ctrl);
break;
case USB_REQ_CLEAR_FEATURE:
ret = cdnsp_ep0_handle_feature(pdev, ctrl, 0);
break;
case USB_REQ_SET_FEATURE:
ret = cdnsp_ep0_handle_feature(pdev, ctrl, 1);
break;
case USB_REQ_SET_ADDRESS:
ret = cdnsp_ep0_set_address(pdev, ctrl);
break;
case USB_REQ_SET_CONFIGURATION:
ret = cdnsp_ep0_set_config(pdev, ctrl);
break;
case USB_REQ_SET_SEL:
ret = cdnsp_ep0_set_sel(pdev, ctrl);
break;
case USB_REQ_SET_ISOCH_DELAY:
ret = cdnsp_ep0_set_isoch_delay(pdev, ctrl);
break;
case USB_REQ_SET_INTERFACE:
/*
* Add request into pending list to block sending status stage
* by libcomposite.
*/
list_add_tail(&pdev->ep0_preq.list,
&pdev->ep0_preq.pep->pending_list);
ret = cdnsp_ep0_delegate_req(pdev, ctrl);
if (ret == -EBUSY)
ret = 0;
list_del(&pdev->ep0_preq.list);
break;
default:
ret = cdnsp_ep0_delegate_req(pdev, ctrl);
break;
}
return ret;
}
void cdnsp_setup_analyze(struct cdnsp_device *pdev)
{
struct usb_ctrlrequest *ctrl = &pdev->setup;
int ret = 0;
u16 len;
trace_cdnsp_ctrl_req(ctrl);
if (!pdev->gadget_driver)
goto out;
if (pdev->gadget.state == USB_STATE_NOTATTACHED) {
dev_err(pdev->dev, "ERR: Setup detected in unattached state\n");
ret = -EINVAL;
goto out;
}
/* Restore the ep0 to Stopped/Running state. */
if (pdev->eps[0].ep_state & EP_HALTED) {
trace_cdnsp_ep0_halted("Restore to normal state");
cdnsp_halt_endpoint(pdev, &pdev->eps[0], 0);
}
/*
* Finishing previous SETUP transfer by removing request from
* list and informing upper layer
*/
if (!list_empty(&pdev->eps[0].pending_list)) {
struct cdnsp_request *req;
trace_cdnsp_ep0_request("Remove previous");
req = next_request(&pdev->eps[0].pending_list);
cdnsp_ep_dequeue(&pdev->eps[0], req);
}
len = le16_to_cpu(ctrl->wLength);
if (!len) {
pdev->three_stage_setup = false;
pdev->ep0_expect_in = false;
} else {
pdev->three_stage_setup = true;
pdev->ep0_expect_in = !!(ctrl->bRequestType & USB_DIR_IN);
}
if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
ret = cdnsp_ep0_std_request(pdev, ctrl);
else
ret = cdnsp_ep0_delegate_req(pdev, ctrl);
if (!len)
pdev->ep0_stage = CDNSP_STATUS_STAGE;
if (ret == USB_GADGET_DELAYED_STATUS) {
trace_cdnsp_ep0_status_stage("delayed");
return;
}
out:
if (ret < 0)
cdnsp_ep0_stall(pdev);
else if (pdev->ep0_stage == CDNSP_STATUS_STAGE)
cdnsp_status_stage(pdev);
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,254 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Cadence PCI Glue driver.
*
* Copyright (C) 2019 Cadence.
*
* Author: Pawel Laszczak <pawell@cadence.com>
*
*/
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include "core.h"
#include "gadget-export.h"
#define PCI_BAR_HOST 0
#define PCI_BAR_OTG 0
#define PCI_BAR_DEV 2
#define PCI_DEV_FN_HOST_DEVICE 0
#define PCI_DEV_FN_OTG 1
#define PCI_DRIVER_NAME "cdns-pci-usbssp"
#define PLAT_DRIVER_NAME "cdns-usbssp"
#define CDNS_VENDOR_ID 0x17cd
#define CDNS_DEVICE_ID 0x0100
#define CDNS_DRD_IF (PCI_CLASS_SERIAL_USB << 8 | 0x80)
static struct pci_dev *cdnsp_get_second_fun(struct pci_dev *pdev)
{
struct pci_dev *func;
/*
* Gets the second function.
* It's little tricky, but this platform has two function.
* The fist keeps resources for Host/Device while the second
* keeps resources for DRD/OTG.
*/
func = pci_get_device(pdev->vendor, pdev->device, NULL);
if (!func)
return NULL;
if (func->devfn == pdev->devfn) {
func = pci_get_device(pdev->vendor, pdev->device, func);
if (!func)
return NULL;
}
return func;
}
static int cdnsp_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
struct device *dev = &pdev->dev;
struct pci_dev *func;
struct resource *res;
struct cdns *cdnsp;
int ret;
/*
* For GADGET/HOST PCI (devfn) function number is 0,
* for OTG PCI (devfn) function number is 1.
*/
if (!id || (pdev->devfn != PCI_DEV_FN_HOST_DEVICE &&
pdev->devfn != PCI_DEV_FN_OTG))
return -EINVAL;
func = cdnsp_get_second_fun(pdev);
if (!func)
return -EINVAL;
if (func->class == PCI_CLASS_SERIAL_USB_XHCI ||
pdev->class == PCI_CLASS_SERIAL_USB_XHCI) {
ret = -EINVAL;
goto put_pci;
}
ret = pcim_enable_device(pdev);
if (ret) {
dev_err(&pdev->dev, "Enabling PCI device has failed %d\n", ret);
goto put_pci;
}
pci_set_master(pdev);
if (pci_is_enabled(func)) {
cdnsp = pci_get_drvdata(func);
} else {
cdnsp = kzalloc(sizeof(*cdnsp), GFP_KERNEL);
if (!cdnsp) {
ret = -ENOMEM;
goto disable_pci;
}
}
/* For GADGET device function number is 0. */
if (pdev->devfn == 0) {
resource_size_t rsrc_start, rsrc_len;
/* Function 0: host(BAR_0) + device(BAR_1).*/
dev_dbg(dev, "Initialize resources\n");
rsrc_start = pci_resource_start(pdev, PCI_BAR_DEV);
rsrc_len = pci_resource_len(pdev, PCI_BAR_DEV);
res = devm_request_mem_region(dev, rsrc_start, rsrc_len, "dev");
if (!res) {
dev_dbg(dev, "controller already in use\n");
ret = -EBUSY;
goto free_cdnsp;
}
cdnsp->dev_regs = devm_ioremap(dev, rsrc_start, rsrc_len);
if (!cdnsp->dev_regs) {
dev_dbg(dev, "error mapping memory\n");
ret = -EFAULT;
goto free_cdnsp;
}
cdnsp->dev_irq = pdev->irq;
dev_dbg(dev, "USBSS-DEV physical base addr: %pa\n",
&rsrc_start);
res = &cdnsp->xhci_res[0];
res->start = pci_resource_start(pdev, PCI_BAR_HOST);
res->end = pci_resource_end(pdev, PCI_BAR_HOST);
res->name = "xhci";
res->flags = IORESOURCE_MEM;
dev_dbg(dev, "USBSS-XHCI physical base addr: %pa\n",
&res->start);
/* Interrupt for XHCI, */
res = &cdnsp->xhci_res[1];
res->start = pdev->irq;
res->name = "host";
res->flags = IORESOURCE_IRQ;
} else {
res = &cdnsp->otg_res;
res->start = pci_resource_start(pdev, PCI_BAR_OTG);
res->end = pci_resource_end(pdev, PCI_BAR_OTG);
res->name = "otg";
res->flags = IORESOURCE_MEM;
dev_dbg(dev, "CDNSP-DRD physical base addr: %pa\n",
&res->start);
/* Interrupt for OTG/DRD. */
cdnsp->otg_irq = pdev->irq;
}
if (pci_is_enabled(func)) {
cdnsp->dev = dev;
cdnsp->gadget_init = cdnsp_gadget_init;
ret = cdns_init(cdnsp);
if (ret)
goto free_cdnsp;
}
pci_set_drvdata(pdev, cdnsp);
device_wakeup_enable(&pdev->dev);
if (pci_dev_run_wake(pdev))
pm_runtime_put_noidle(&pdev->dev);
return 0;
free_cdnsp:
if (!pci_is_enabled(func))
kfree(cdnsp);
disable_pci:
pci_disable_device(pdev);
put_pci:
pci_dev_put(func);
return ret;
}
static void cdnsp_pci_remove(struct pci_dev *pdev)
{
struct cdns *cdnsp;
struct pci_dev *func;
func = cdnsp_get_second_fun(pdev);
cdnsp = (struct cdns *)pci_get_drvdata(pdev);
if (pci_dev_run_wake(pdev))
pm_runtime_get_noresume(&pdev->dev);
if (!pci_is_enabled(func)) {
kfree(cdnsp);
goto pci_put;
}
cdns_remove(cdnsp);
pci_put:
pci_dev_put(func);
}
static int __maybe_unused cdnsp_pci_suspend(struct device *dev)
{
struct cdns *cdns = dev_get_drvdata(dev);
return cdns_suspend(cdns);
}
static int __maybe_unused cdnsp_pci_resume(struct device *dev)
{
struct cdns *cdns = dev_get_drvdata(dev);
unsigned long flags;
int ret;
spin_lock_irqsave(&cdns->lock, flags);
ret = cdns_resume(cdns, 1);
spin_unlock_irqrestore(&cdns->lock, flags);
return ret;
}
static const struct dev_pm_ops cdnsp_pci_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(cdnsp_pci_suspend, cdnsp_pci_resume)
};
static const struct pci_device_id cdnsp_pci_ids[] = {
{ PCI_VENDOR_ID_CDNS, CDNS_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,
PCI_CLASS_SERIAL_USB_DEVICE, PCI_ANY_ID },
{ PCI_VENDOR_ID_CDNS, CDNS_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,
CDNS_DRD_IF, PCI_ANY_ID },
{ 0, }
};
static struct pci_driver cdnsp_pci_driver = {
.name = "cdnsp-pci",
.id_table = &cdnsp_pci_ids[0],
.probe = cdnsp_pci_probe,
.remove = cdnsp_pci_remove,
.driver = {
.pm = &cdnsp_pci_pm_ops,
}
};
module_pci_driver(cdnsp_pci_driver);
MODULE_DEVICE_TABLE(pci, cdnsp_pci_ids);
MODULE_ALIAS("pci:cdnsp");
MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Cadence CDNSP PCI driver");

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,12 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Cadence CDNSP DRD Driver.
*
* Copyright (C) 2020 Cadence.
*
* Author: Pawel Laszczak <pawell@cadence.com>
*
*/
#define CREATE_TRACE_POINTS
#include "cdnsp-trace.h"

View File

@ -0,0 +1,830 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Cadence CDNSP DRD Driver.
* Trace support header file
*
* Copyright (C) 2020 Cadence.
*
* Author: Pawel Laszczak <pawell@cadence.com>
*
*/
#undef TRACE_SYSTEM
#define TRACE_SYSTEM cdnsp-dev
/*
* The TRACE_SYSTEM_VAR defaults to TRACE_SYSTEM, but must be a
* legitimate C variable. It is not exported to user space.
*/
#undef TRACE_SYSTEM_VAR
#define TRACE_SYSTEM_VAR cdnsp_dev
#if !defined(__CDNSP_DEV_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
#define __CDNSP_DEV_TRACE_H
#include <linux/tracepoint.h>
#include "cdnsp-gadget.h"
#include "cdnsp-debug.h"
/*
* There is limitation for single buffer size in TRACEPOINT subsystem.
* By default TRACE_BUF_SIZE is 1024, so no all data will be logged.
* To show more data this must be increased. In most cases the default
* value is sufficient.
*/
#define CDNSP_MSG_MAX 500
DECLARE_EVENT_CLASS(cdnsp_log_ep,
TP_PROTO(struct cdnsp_ep *pep, u32 stream_id),
TP_ARGS(pep, stream_id),
TP_STRUCT__entry(
__string(name, pep->name)
__field(unsigned int, state)
__field(u32, stream_id)
__field(u8, enabled)
__field(unsigned int, num_streams)
__field(int, td_count)
__field(u8, first_prime_det)
__field(u8, drbls_count)
),
TP_fast_assign(
__assign_str(name, pep->name);
__entry->state = pep->ep_state;
__entry->stream_id = stream_id;
__entry->enabled = pep->ep_state & EP_HAS_STREAMS;
__entry->num_streams = pep->stream_info.num_streams;
__entry->td_count = pep->stream_info.td_count;
__entry->first_prime_det = pep->stream_info.first_prime_det;
__entry->drbls_count = pep->stream_info.drbls_count;
),
TP_printk("%s: SID: %08x ep state: %x stream: enabled: %d num %d "
"tds %d, first prime: %d drbls %d",
__get_str(name), __entry->state, __entry->stream_id,
__entry->enabled, __entry->num_streams, __entry->td_count,
__entry->first_prime_det, __entry->drbls_count)
);
DEFINE_EVENT(cdnsp_log_ep, cdnsp_tr_drbl,
TP_PROTO(struct cdnsp_ep *pep, u32 stream_id),
TP_ARGS(pep, stream_id)
);
DEFINE_EVENT(cdnsp_log_ep, cdnsp_wait_for_prime,
TP_PROTO(struct cdnsp_ep *pep, u32 stream_id),
TP_ARGS(pep, stream_id)
);
DEFINE_EVENT(cdnsp_log_ep, cdnsp_ep_list_empty_with_skip,
TP_PROTO(struct cdnsp_ep *pep, u32 stream_id),
TP_ARGS(pep, stream_id)
);
DEFINE_EVENT(cdnsp_log_ep, cdnsp_ep_enable_end,
TP_PROTO(struct cdnsp_ep *pep, u32 stream_id),
TP_ARGS(pep, stream_id)
);
DEFINE_EVENT(cdnsp_log_ep, cdnsp_ep_disable_end,
TP_PROTO(struct cdnsp_ep *pep, u32 stream_id),
TP_ARGS(pep, stream_id)
);
DEFINE_EVENT(cdnsp_log_ep, cdnsp_ep_busy_try_halt_again,
TP_PROTO(struct cdnsp_ep *pep, u32 stream_id),
TP_ARGS(pep, stream_id)
);
DECLARE_EVENT_CLASS(cdnsp_log_enable_disable,
TP_PROTO(int set),
TP_ARGS(set),
TP_STRUCT__entry(
__field(int, set)
),
TP_fast_assign(
__entry->set = set;
),
TP_printk("%s", __entry->set ? "enabled" : "disabled")
);
DEFINE_EVENT(cdnsp_log_enable_disable, cdnsp_pullup,
TP_PROTO(int set),
TP_ARGS(set)
);
DEFINE_EVENT(cdnsp_log_enable_disable, cdnsp_u1,
TP_PROTO(int set),
TP_ARGS(set)
);
DEFINE_EVENT(cdnsp_log_enable_disable, cdnsp_u2,
TP_PROTO(int set),
TP_ARGS(set)
);
DEFINE_EVENT(cdnsp_log_enable_disable, cdnsp_lpm,
TP_PROTO(int set),
TP_ARGS(set)
);
DEFINE_EVENT(cdnsp_log_enable_disable, cdnsp_may_wakeup,
TP_PROTO(int set),
TP_ARGS(set)
);
DECLARE_EVENT_CLASS(cdnsp_log_simple,
TP_PROTO(char *msg),
TP_ARGS(msg),
TP_STRUCT__entry(
__string(text, msg)
),
TP_fast_assign(
__assign_str(text, msg)
),
TP_printk("%s", __get_str(text))
);
DEFINE_EVENT(cdnsp_log_simple, cdnsp_exit,
TP_PROTO(char *msg),
TP_ARGS(msg)
);
DEFINE_EVENT(cdnsp_log_simple, cdnsp_init,
TP_PROTO(char *msg),
TP_ARGS(msg)
);
DEFINE_EVENT(cdnsp_log_simple, cdnsp_slot_id,
TP_PROTO(char *msg),
TP_ARGS(msg)
);
DEFINE_EVENT(cdnsp_log_simple, cdnsp_no_room_on_ring,
TP_PROTO(char *msg),
TP_ARGS(msg)
);
DEFINE_EVENT(cdnsp_log_simple, cdnsp_ep0_status_stage,
TP_PROTO(char *msg),
TP_ARGS(msg)
);
DEFINE_EVENT(cdnsp_log_simple, cdnsp_ep0_request,
TP_PROTO(char *msg),
TP_ARGS(msg)
);
DEFINE_EVENT(cdnsp_log_simple, cdnsp_ep0_set_config,
TP_PROTO(char *msg),
TP_ARGS(msg)
);
DEFINE_EVENT(cdnsp_log_simple, cdnsp_ep0_halted,
TP_PROTO(char *msg),
TP_ARGS(msg)
);
DEFINE_EVENT(cdnsp_log_simple, cdnsp_ep_halt,
TP_PROTO(char *msg),
TP_ARGS(msg)
);
TRACE_EVENT(cdnsp_looking_trb_in_td,
TP_PROTO(dma_addr_t suspect, dma_addr_t trb_start, dma_addr_t trb_end,
dma_addr_t curr_seg, dma_addr_t end_seg),
TP_ARGS(suspect, trb_start, trb_end, curr_seg, end_seg),
TP_STRUCT__entry(
__field(dma_addr_t, suspect)
__field(dma_addr_t, trb_start)
__field(dma_addr_t, trb_end)
__field(dma_addr_t, curr_seg)
__field(dma_addr_t, end_seg)
),
TP_fast_assign(
__entry->suspect = suspect;
__entry->trb_start = trb_start;
__entry->trb_end = trb_end;
__entry->curr_seg = curr_seg;
__entry->end_seg = end_seg;
),
TP_printk("DMA: suspect event: %pad, trb-start: %pad, trb-end %pad, "
"seg-start %pad, seg-end %pad",
&__entry->suspect, &__entry->trb_start, &__entry->trb_end,
&__entry->curr_seg, &__entry->end_seg)
);
TRACE_EVENT(cdnsp_port_info,
TP_PROTO(__le32 __iomem *addr, u32 offset, u32 count, u32 rev),
TP_ARGS(addr, offset, count, rev),
TP_STRUCT__entry(
__field(__le32 __iomem *, addr)
__field(u32, offset)
__field(u32, count)
__field(u32, rev)
),
TP_fast_assign(
__entry->addr = addr;
__entry->offset = offset;
__entry->count = count;
__entry->rev = rev;
),
TP_printk("Ext Cap %p, port offset = %u, count = %u, rev = 0x%x",
__entry->addr, __entry->offset, __entry->count, __entry->rev)
);
DECLARE_EVENT_CLASS(cdnsp_log_deq_state,
TP_PROTO(struct cdnsp_dequeue_state *state),
TP_ARGS(state),
TP_STRUCT__entry(
__field(int, new_cycle_state)
__field(struct cdnsp_segment *, new_deq_seg)
__field(dma_addr_t, deq_seg_dma)
__field(union cdnsp_trb *, new_deq_ptr)
__field(dma_addr_t, deq_ptr_dma)
),
TP_fast_assign(
__entry->new_cycle_state = state->new_cycle_state;
__entry->new_deq_seg = state->new_deq_seg;
__entry->deq_seg_dma = state->new_deq_seg->dma;
__entry->new_deq_ptr = state->new_deq_ptr,
__entry->deq_ptr_dma = cdnsp_trb_virt_to_dma(state->new_deq_seg,
state->new_deq_ptr);
),
TP_printk("New cycle state = 0x%x, New dequeue segment = %p (0x%pad dma), "
"New dequeue pointer = %p (0x%pad dma)",
__entry->new_cycle_state, __entry->new_deq_seg,
&__entry->deq_seg_dma, __entry->new_deq_ptr,
&__entry->deq_ptr_dma
)
);
DEFINE_EVENT(cdnsp_log_deq_state, cdnsp_new_deq_state,
TP_PROTO(struct cdnsp_dequeue_state *state),
TP_ARGS(state)
);
DECLARE_EVENT_CLASS(cdnsp_log_ctrl,
TP_PROTO(struct usb_ctrlrequest *ctrl),
TP_ARGS(ctrl),
TP_STRUCT__entry(
__field(u8, bRequestType)
__field(u8, bRequest)
__field(u16, wValue)
__field(u16, wIndex)
__field(u16, wLength)
__dynamic_array(char, str, CDNSP_MSG_MAX)
),
TP_fast_assign(
__entry->bRequestType = ctrl->bRequestType;
__entry->bRequest = ctrl->bRequest;
__entry->wValue = le16_to_cpu(ctrl->wValue);
__entry->wIndex = le16_to_cpu(ctrl->wIndex);
__entry->wLength = le16_to_cpu(ctrl->wLength);
),
TP_printk("%s", usb_decode_ctrl(__get_str(str), CDNSP_MSG_MAX,
__entry->bRequestType,
__entry->bRequest, __entry->wValue,
__entry->wIndex, __entry->wLength)
)
);
DEFINE_EVENT(cdnsp_log_ctrl, cdnsp_ctrl_req,
TP_PROTO(struct usb_ctrlrequest *ctrl),
TP_ARGS(ctrl)
);
DECLARE_EVENT_CLASS(cdnsp_log_bounce,
TP_PROTO(struct cdnsp_request *preq, u32 new_buf_len, u32 offset,
dma_addr_t dma, unsigned int unalign),
TP_ARGS(preq, new_buf_len, offset, dma, unalign),
TP_STRUCT__entry(
__string(name, preq->pep->name)
__field(u32, new_buf_len)
__field(u32, offset)
__field(dma_addr_t, dma)
__field(unsigned int, unalign)
),
TP_fast_assign(
__assign_str(name, preq->pep->name);
__entry->new_buf_len = new_buf_len;
__entry->offset = offset;
__entry->dma = dma;
__entry->unalign = unalign;
),
TP_printk("%s buf len %d, offset %d, dma %pad, unalign %d",
__get_str(name), __entry->new_buf_len,
__entry->offset, &__entry->dma, __entry->unalign
)
);
DEFINE_EVENT(cdnsp_log_bounce, cdnsp_bounce_align_td_split,
TP_PROTO(struct cdnsp_request *preq, u32 new_buf_len, u32 offset,
dma_addr_t dma, unsigned int unalign),
TP_ARGS(preq, new_buf_len, offset, dma, unalign)
);
DEFINE_EVENT(cdnsp_log_bounce, cdnsp_bounce_map,
TP_PROTO(struct cdnsp_request *preq, u32 new_buf_len, u32 offset,
dma_addr_t dma, unsigned int unalign),
TP_ARGS(preq, new_buf_len, offset, dma, unalign)
);
DEFINE_EVENT(cdnsp_log_bounce, cdnsp_bounce_unmap,
TP_PROTO(struct cdnsp_request *preq, u32 new_buf_len, u32 offset,
dma_addr_t dma, unsigned int unalign),
TP_ARGS(preq, new_buf_len, offset, dma, unalign)
);
DECLARE_EVENT_CLASS(cdnsp_log_trb,
TP_PROTO(struct cdnsp_ring *ring, struct cdnsp_generic_trb *trb),
TP_ARGS(ring, trb),
TP_STRUCT__entry(
__field(u32, type)
__field(u32, field0)
__field(u32, field1)
__field(u32, field2)
__field(u32, field3)
__field(union cdnsp_trb *, trb)
__field(dma_addr_t, trb_dma)
__dynamic_array(char, str, CDNSP_MSG_MAX)
),
TP_fast_assign(
__entry->type = ring->type;
__entry->field0 = le32_to_cpu(trb->field[0]);
__entry->field1 = le32_to_cpu(trb->field[1]);
__entry->field2 = le32_to_cpu(trb->field[2]);
__entry->field3 = le32_to_cpu(trb->field[3]);
__entry->trb = (union cdnsp_trb *)trb;
__entry->trb_dma = cdnsp_trb_virt_to_dma(ring->deq_seg,
(union cdnsp_trb *)trb);
),
TP_printk("%s: %s trb: %p(%pad)", cdnsp_ring_type_string(__entry->type),
cdnsp_decode_trb(__get_str(str), CDNSP_MSG_MAX,
__entry->field0, __entry->field1,
__entry->field2, __entry->field3),
__entry->trb, &__entry->trb_dma
)
);
DEFINE_EVENT(cdnsp_log_trb, cdnsp_handle_event,
TP_PROTO(struct cdnsp_ring *ring, struct cdnsp_generic_trb *trb),
TP_ARGS(ring, trb)
);
DEFINE_EVENT(cdnsp_log_trb, cdnsp_trb_without_td,
TP_PROTO(struct cdnsp_ring *ring, struct cdnsp_generic_trb *trb),
TP_ARGS(ring, trb)
);
DEFINE_EVENT(cdnsp_log_trb, cdnsp_handle_command,
TP_PROTO(struct cdnsp_ring *ring, struct cdnsp_generic_trb *trb),
TP_ARGS(ring, trb)
);
DEFINE_EVENT(cdnsp_log_trb, cdnsp_handle_transfer,
TP_PROTO(struct cdnsp_ring *ring, struct cdnsp_generic_trb *trb),
TP_ARGS(ring, trb)
);
DEFINE_EVENT(cdnsp_log_trb, cdnsp_queue_trb,
TP_PROTO(struct cdnsp_ring *ring, struct cdnsp_generic_trb *trb),
TP_ARGS(ring, trb)
);
DEFINE_EVENT(cdnsp_log_trb, cdnsp_cmd_wait_for_compl,
TP_PROTO(struct cdnsp_ring *ring, struct cdnsp_generic_trb *trb),
TP_ARGS(ring, trb)
);
DEFINE_EVENT(cdnsp_log_trb, cdnsp_cmd_timeout,
TP_PROTO(struct cdnsp_ring *ring, struct cdnsp_generic_trb *trb),
TP_ARGS(ring, trb)
);
DEFINE_EVENT(cdnsp_log_trb, cdnsp_defered_event,
TP_PROTO(struct cdnsp_ring *ring, struct cdnsp_generic_trb *trb),
TP_ARGS(ring, trb)
);
DECLARE_EVENT_CLASS(cdnsp_log_pdev,
TP_PROTO(struct cdnsp_device *pdev),
TP_ARGS(pdev),
TP_STRUCT__entry(
__field(struct cdnsp_device *, pdev)
__field(struct usb_gadget *, gadget)
__field(dma_addr_t, out_ctx)
__field(dma_addr_t, in_ctx)
__field(u8, port_num)
),
TP_fast_assign(
__entry->pdev = pdev;
__entry->gadget = &pdev->gadget;
__entry->in_ctx = pdev->in_ctx.dma;
__entry->out_ctx = pdev->out_ctx.dma;
__entry->port_num = pdev->active_port ?
pdev->active_port->port_num : 0xFF;
),
TP_printk("pdev %p gadget %p ctx %pad | %pad, port %d ",
__entry->pdev, __entry->gadget, &__entry->in_ctx,
&__entry->out_ctx, __entry->port_num
)
);
DEFINE_EVENT(cdnsp_log_pdev, cdnsp_alloc_priv_device,
TP_PROTO(struct cdnsp_device *vdev),
TP_ARGS(vdev)
);
DEFINE_EVENT(cdnsp_log_pdev, cdnsp_free_priv_device,
TP_PROTO(struct cdnsp_device *vdev),
TP_ARGS(vdev)
);
DEFINE_EVENT(cdnsp_log_pdev, cdnsp_setup_device,
TP_PROTO(struct cdnsp_device *vdev),
TP_ARGS(vdev)
);
DEFINE_EVENT(cdnsp_log_pdev, cdnsp_setup_addressable_priv_device,
TP_PROTO(struct cdnsp_device *vdev),
TP_ARGS(vdev)
);
DECLARE_EVENT_CLASS(cdnsp_log_request,
TP_PROTO(struct cdnsp_request *req),
TP_ARGS(req),
TP_STRUCT__entry(
__string(name, req->pep->name)
__field(struct usb_request *, request)
__field(struct cdnsp_request *, preq)
__field(void *, buf)
__field(unsigned int, actual)
__field(unsigned int, length)
__field(int, status)
__field(dma_addr_t, dma)
__field(unsigned int, stream_id)
__field(unsigned int, zero)
__field(unsigned int, short_not_ok)
__field(unsigned int, no_interrupt)
__field(struct scatterlist*, sg)
__field(unsigned int, num_sgs)
__field(unsigned int, num_mapped_sgs)
),
TP_fast_assign(
__assign_str(name, req->pep->name);
__entry->request = &req->request;
__entry->preq = req;
__entry->buf = req->request.buf;
__entry->actual = req->request.actual;
__entry->length = req->request.length;
__entry->status = req->request.status;
__entry->dma = req->request.dma;
__entry->stream_id = req->request.stream_id;
__entry->zero = req->request.zero;
__entry->short_not_ok = req->request.short_not_ok;
__entry->no_interrupt = req->request.no_interrupt;
__entry->sg = req->request.sg;
__entry->num_sgs = req->request.num_sgs;
__entry->num_mapped_sgs = req->request.num_mapped_sgs;
),
TP_printk("%s; req U:%p/P:%p, req buf %p, length %u/%u, status %d, "
"buf dma (%pad), SID %u, %s%s%s, sg %p, num_sg %d,"
" num_m_sg %d",
__get_str(name), __entry->request, __entry->preq,
__entry->buf, __entry->actual, __entry->length,
__entry->status, &__entry->dma,
__entry->stream_id, __entry->zero ? "Z" : "z",
__entry->short_not_ok ? "S" : "s",
__entry->no_interrupt ? "I" : "i",
__entry->sg, __entry->num_sgs, __entry->num_mapped_sgs
)
);
DEFINE_EVENT(cdnsp_log_request, cdnsp_request_enqueue,
TP_PROTO(struct cdnsp_request *req),
TP_ARGS(req)
);
DEFINE_EVENT(cdnsp_log_request, cdnsp_request_enqueue_busy,
TP_PROTO(struct cdnsp_request *req),
TP_ARGS(req)
);
DEFINE_EVENT(cdnsp_log_request, cdnsp_request_enqueue_error,
TP_PROTO(struct cdnsp_request *req),
TP_ARGS(req)
);
DEFINE_EVENT(cdnsp_log_request, cdnsp_request_dequeue,
TP_PROTO(struct cdnsp_request *req),
TP_ARGS(req)
);
DEFINE_EVENT(cdnsp_log_request, cdnsp_request_giveback,
TP_PROTO(struct cdnsp_request *req),
TP_ARGS(req)
);
DEFINE_EVENT(cdnsp_log_request, cdnsp_alloc_request,
TP_PROTO(struct cdnsp_request *req),
TP_ARGS(req)
);
DEFINE_EVENT(cdnsp_log_request, cdnsp_free_request,
TP_PROTO(struct cdnsp_request *req),
TP_ARGS(req)
);
DECLARE_EVENT_CLASS(cdnsp_log_ep_ctx,
TP_PROTO(struct cdnsp_ep_ctx *ctx),
TP_ARGS(ctx),
TP_STRUCT__entry(
__field(u32, info)
__field(u32, info2)
__field(u64, deq)
__field(u32, tx_info)
__dynamic_array(char, str, CDNSP_MSG_MAX)
),
TP_fast_assign(
__entry->info = le32_to_cpu(ctx->ep_info);
__entry->info2 = le32_to_cpu(ctx->ep_info2);
__entry->deq = le64_to_cpu(ctx->deq);
__entry->tx_info = le32_to_cpu(ctx->tx_info);
),
TP_printk("%s", cdnsp_decode_ep_context(__get_str(str), CDNSP_MSG_MAX,
__entry->info, __entry->info2,
__entry->deq, __entry->tx_info)
)
);
DEFINE_EVENT(cdnsp_log_ep_ctx, cdnsp_ep_disabled,
TP_PROTO(struct cdnsp_ep_ctx *ctx),
TP_ARGS(ctx)
);
DEFINE_EVENT(cdnsp_log_ep_ctx, cdnsp_ep_stopped_or_disabled,
TP_PROTO(struct cdnsp_ep_ctx *ctx),
TP_ARGS(ctx)
);
DEFINE_EVENT(cdnsp_log_ep_ctx, cdnsp_remove_request,
TP_PROTO(struct cdnsp_ep_ctx *ctx),
TP_ARGS(ctx)
);
DEFINE_EVENT(cdnsp_log_ep_ctx, cdnsp_handle_cmd_stop_ep,
TP_PROTO(struct cdnsp_ep_ctx *ctx),
TP_ARGS(ctx)
);
DEFINE_EVENT(cdnsp_log_ep_ctx, cdnsp_handle_cmd_flush_ep,
TP_PROTO(struct cdnsp_ep_ctx *ctx),
TP_ARGS(ctx)
);
DEFINE_EVENT(cdnsp_log_ep_ctx, cdnsp_handle_cmd_set_deq_ep,
TP_PROTO(struct cdnsp_ep_ctx *ctx),
TP_ARGS(ctx)
);
DEFINE_EVENT(cdnsp_log_ep_ctx, cdnsp_handle_cmd_reset_ep,
TP_PROTO(struct cdnsp_ep_ctx *ctx),
TP_ARGS(ctx)
);
DEFINE_EVENT(cdnsp_log_ep_ctx, cdnsp_handle_cmd_config_ep,
TP_PROTO(struct cdnsp_ep_ctx *ctx),
TP_ARGS(ctx)
);
DECLARE_EVENT_CLASS(cdnsp_log_slot_ctx,
TP_PROTO(struct cdnsp_slot_ctx *ctx),
TP_ARGS(ctx),
TP_STRUCT__entry(
__field(u32, info)
__field(u32, info2)
__field(u32, int_target)
__field(u32, state)
),
TP_fast_assign(
__entry->info = le32_to_cpu(ctx->dev_info);
__entry->info2 = le32_to_cpu(ctx->dev_port);
__entry->int_target = le32_to_cpu(ctx->int_target);
__entry->state = le32_to_cpu(ctx->dev_state);
),
TP_printk("%s", cdnsp_decode_slot_context(__entry->info,
__entry->info2,
__entry->int_target,
__entry->state)
)
);
DEFINE_EVENT(cdnsp_log_slot_ctx, cdnsp_slot_already_in_default,
TP_PROTO(struct cdnsp_slot_ctx *ctx),
TP_ARGS(ctx)
);
DEFINE_EVENT(cdnsp_log_slot_ctx, cdnsp_handle_cmd_enable_slot,
TP_PROTO(struct cdnsp_slot_ctx *ctx),
TP_ARGS(ctx)
);
DEFINE_EVENT(cdnsp_log_slot_ctx, cdnsp_handle_cmd_disable_slot,
TP_PROTO(struct cdnsp_slot_ctx *ctx),
TP_ARGS(ctx)
);
DEFINE_EVENT(cdnsp_log_slot_ctx, cdnsp_reset_device,
TP_PROTO(struct cdnsp_slot_ctx *ctx),
TP_ARGS(ctx)
);
DEFINE_EVENT(cdnsp_log_slot_ctx, cdnsp_setup_device_slot,
TP_PROTO(struct cdnsp_slot_ctx *ctx),
TP_ARGS(ctx)
);
DEFINE_EVENT(cdnsp_log_slot_ctx, cdnsp_handle_cmd_addr_dev,
TP_PROTO(struct cdnsp_slot_ctx *ctx),
TP_ARGS(ctx)
);
DEFINE_EVENT(cdnsp_log_slot_ctx, cdnsp_handle_cmd_reset_dev,
TP_PROTO(struct cdnsp_slot_ctx *ctx),
TP_ARGS(ctx)
);
DEFINE_EVENT(cdnsp_log_slot_ctx, cdnsp_handle_cmd_set_deq,
TP_PROTO(struct cdnsp_slot_ctx *ctx),
TP_ARGS(ctx)
);
DEFINE_EVENT(cdnsp_log_slot_ctx, cdnsp_configure_endpoint,
TP_PROTO(struct cdnsp_slot_ctx *ctx),
TP_ARGS(ctx)
);
DECLARE_EVENT_CLASS(cdnsp_log_td_info,
TP_PROTO(struct cdnsp_request *preq),
TP_ARGS(preq),
TP_STRUCT__entry(
__string(name, preq->pep->name)
__field(struct usb_request *, request)
__field(struct cdnsp_request *, preq)
__field(union cdnsp_trb *, first_trb)
__field(union cdnsp_trb *, last_trb)
__field(dma_addr_t, trb_dma)
),
TP_fast_assign(
__assign_str(name, preq->pep->name);
__entry->request = &preq->request;
__entry->preq = preq;
__entry->first_trb = preq->td.first_trb;
__entry->last_trb = preq->td.last_trb;
__entry->trb_dma = cdnsp_trb_virt_to_dma(preq->td.start_seg,
preq->td.first_trb)
),
TP_printk("%s req/preq: %p/%p, first trb %p[vir]/%pad(dma), last trb %p",
__get_str(name), __entry->request, __entry->preq,
__entry->first_trb, &__entry->trb_dma,
__entry->last_trb
)
);
DEFINE_EVENT(cdnsp_log_td_info, cdnsp_remove_request_td,
TP_PROTO(struct cdnsp_request *preq),
TP_ARGS(preq)
);
DECLARE_EVENT_CLASS(cdnsp_log_ring,
TP_PROTO(struct cdnsp_ring *ring),
TP_ARGS(ring),
TP_STRUCT__entry(
__field(u32, type)
__field(void *, ring)
__field(dma_addr_t, enq)
__field(dma_addr_t, deq)
__field(dma_addr_t, enq_seg)
__field(dma_addr_t, deq_seg)
__field(unsigned int, num_segs)
__field(unsigned int, stream_id)
__field(unsigned int, cycle_state)
__field(unsigned int, num_trbs_free)
__field(unsigned int, bounce_buf_len)
),
TP_fast_assign(
__entry->ring = ring;
__entry->type = ring->type;
__entry->num_segs = ring->num_segs;
__entry->stream_id = ring->stream_id;
__entry->enq_seg = ring->enq_seg->dma;
__entry->deq_seg = ring->deq_seg->dma;
__entry->cycle_state = ring->cycle_state;
__entry->num_trbs_free = ring->num_trbs_free;
__entry->bounce_buf_len = ring->bounce_buf_len;
__entry->enq = cdnsp_trb_virt_to_dma(ring->enq_seg,
ring->enqueue);
__entry->deq = cdnsp_trb_virt_to_dma(ring->deq_seg,
ring->dequeue);
),
TP_printk("%s %p: enq %pad(%pad) deq %pad(%pad) segs %d stream %d"
" free_trbs %d bounce %d cycle %d",
cdnsp_ring_type_string(__entry->type), __entry->ring,
&__entry->enq, &__entry->enq_seg,
&__entry->deq, &__entry->deq_seg,
__entry->num_segs,
__entry->stream_id,
__entry->num_trbs_free,
__entry->bounce_buf_len,
__entry->cycle_state
)
);
DEFINE_EVENT(cdnsp_log_ring, cdnsp_ring_alloc,
TP_PROTO(struct cdnsp_ring *ring),
TP_ARGS(ring)
);
DEFINE_EVENT(cdnsp_log_ring, cdnsp_ring_free,
TP_PROTO(struct cdnsp_ring *ring),
TP_ARGS(ring)
);
DEFINE_EVENT(cdnsp_log_ring, cdnsp_set_stream_ring,
TP_PROTO(struct cdnsp_ring *ring),
TP_ARGS(ring)
);
DEFINE_EVENT(cdnsp_log_ring, cdnsp_ring_expansion,
TP_PROTO(struct cdnsp_ring *ring),
TP_ARGS(ring)
);
DEFINE_EVENT(cdnsp_log_ring, cdnsp_inc_enq,
TP_PROTO(struct cdnsp_ring *ring),
TP_ARGS(ring)
);
DEFINE_EVENT(cdnsp_log_ring, cdnsp_inc_deq,
TP_PROTO(struct cdnsp_ring *ring),
TP_ARGS(ring)
);
DECLARE_EVENT_CLASS(cdnsp_log_portsc,
TP_PROTO(u32 portnum, u32 portsc),
TP_ARGS(portnum, portsc),
TP_STRUCT__entry(
__field(u32, portnum)
__field(u32, portsc)
__dynamic_array(char, str, CDNSP_MSG_MAX)
),
TP_fast_assign(
__entry->portnum = portnum;
__entry->portsc = portsc;
),
TP_printk("port-%d: %s",
__entry->portnum,
cdnsp_decode_portsc(__get_str(str), CDNSP_MSG_MAX,
__entry->portsc)
)
);
DEFINE_EVENT(cdnsp_log_portsc, cdnsp_handle_port_status,
TP_PROTO(u32 portnum, u32 portsc),
TP_ARGS(portnum, portsc)
);
DEFINE_EVENT(cdnsp_log_portsc, cdnsp_link_state_changed,
TP_PROTO(u32 portnum, u32 portsc),
TP_ARGS(portnum, portsc)
);
TRACE_EVENT(cdnsp_stream_number,
TP_PROTO(struct cdnsp_ep *pep, int num_stream_ctxs, int num_streams),
TP_ARGS(pep, num_stream_ctxs, num_streams),
TP_STRUCT__entry(
__string(name, pep->name)
__field(int, num_stream_ctxs)
__field(int, num_streams)
),
TP_fast_assign(
__entry->num_stream_ctxs = num_stream_ctxs;
__entry->num_streams = num_streams;
),
TP_printk("%s Need %u stream ctx entries for %u stream IDs.",
__get_str(name), __entry->num_stream_ctxs,
__entry->num_streams)
);
#endif /* __CDNSP_TRACE_H */
/* this part must be outside header guard */
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE cdnsp-trace
#include <trace/define_trace.h>

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Cadence USBSS DRD Driver.
* Cadence USBSS and USBSSP DRD Driver.
*
* Copyright (C) 2018-2019 Cadence.
* Copyright (C) 2017-2018 NXP
@ -19,15 +19,13 @@
#include <linux/io.h>
#include <linux/pm_runtime.h>
#include "gadget.h"
#include "core.h"
#include "host-export.h"
#include "gadget-export.h"
#include "drd.h"
static int cdns3_idle_init(struct cdns3 *cdns);
static int cdns_idle_init(struct cdns *cdns);
static int cdns3_role_start(struct cdns3 *cdns, enum usb_role role)
static int cdns_role_start(struct cdns *cdns, enum usb_role role)
{
int ret;
@ -41,47 +39,47 @@ static int cdns3_role_start(struct cdns3 *cdns, enum usb_role role)
if (!cdns->roles[role])
return -ENXIO;
if (cdns->roles[role]->state == CDNS3_ROLE_STATE_ACTIVE)
if (cdns->roles[role]->state == CDNS_ROLE_STATE_ACTIVE)
return 0;
mutex_lock(&cdns->mutex);
ret = cdns->roles[role]->start(cdns);
if (!ret)
cdns->roles[role]->state = CDNS3_ROLE_STATE_ACTIVE;
cdns->roles[role]->state = CDNS_ROLE_STATE_ACTIVE;
mutex_unlock(&cdns->mutex);
return ret;
}
static void cdns3_role_stop(struct cdns3 *cdns)
static void cdns_role_stop(struct cdns *cdns)
{
enum usb_role role = cdns->role;
if (WARN_ON(role > USB_ROLE_DEVICE))
return;
if (cdns->roles[role]->state == CDNS3_ROLE_STATE_INACTIVE)
if (cdns->roles[role]->state == CDNS_ROLE_STATE_INACTIVE)
return;
mutex_lock(&cdns->mutex);
cdns->roles[role]->stop(cdns);
cdns->roles[role]->state = CDNS3_ROLE_STATE_INACTIVE;
cdns->roles[role]->state = CDNS_ROLE_STATE_INACTIVE;
mutex_unlock(&cdns->mutex);
}
static void cdns3_exit_roles(struct cdns3 *cdns)
static void cdns_exit_roles(struct cdns *cdns)
{
cdns3_role_stop(cdns);
cdns3_drd_exit(cdns);
cdns_role_stop(cdns);
cdns_drd_exit(cdns);
}
/**
* cdns3_core_init_role - initialize role of operation
* @cdns: Pointer to cdns3 structure
* cdns_core_init_role - initialize role of operation
* @cdns: Pointer to cdns structure
*
* Returns 0 on success otherwise negative errno
*/
static int cdns3_core_init_role(struct cdns3 *cdns)
static int cdns_core_init_role(struct cdns *cdns)
{
struct device *dev = cdns->dev;
enum usb_dr_mode best_dr_mode;
@ -97,13 +95,23 @@ static int cdns3_core_init_role(struct cdns3 *cdns)
* can be restricted later depending on strap pin configuration.
*/
if (dr_mode == USB_DR_MODE_UNKNOWN) {
if (IS_ENABLED(CONFIG_USB_CDNS3_HOST) &&
IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
dr_mode = USB_DR_MODE_OTG;
else if (IS_ENABLED(CONFIG_USB_CDNS3_HOST))
dr_mode = USB_DR_MODE_HOST;
else if (IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
dr_mode = USB_DR_MODE_PERIPHERAL;
if (cdns->version == CDNSP_CONTROLLER_V2) {
if (IS_ENABLED(CONFIG_USB_CDNSP_HOST) &&
IS_ENABLED(CONFIG_USB_CDNSP_GADGET))
dr_mode = USB_DR_MODE_OTG;
else if (IS_ENABLED(CONFIG_USB_CDNSP_HOST))
dr_mode = USB_DR_MODE_HOST;
else if (IS_ENABLED(CONFIG_USB_CDNSP_GADGET))
dr_mode = USB_DR_MODE_PERIPHERAL;
} else {
if (IS_ENABLED(CONFIG_USB_CDNS3_HOST) &&
IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
dr_mode = USB_DR_MODE_OTG;
else if (IS_ENABLED(CONFIG_USB_CDNS3_HOST))
dr_mode = USB_DR_MODE_HOST;
else if (IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
dr_mode = USB_DR_MODE_PERIPHERAL;
}
}
/*
@ -112,7 +120,7 @@ static int cdns3_core_init_role(struct cdns3 *cdns)
*/
best_dr_mode = cdns->dr_mode;
ret = cdns3_idle_init(cdns);
ret = cdns_idle_init(cdns);
if (ret)
return ret;
@ -128,7 +136,14 @@ static int cdns3_core_init_role(struct cdns3 *cdns)
dr_mode = best_dr_mode;
if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) {
ret = cdns3_host_init(cdns);
if ((cdns->version == CDNSP_CONTROLLER_V2 &&
IS_ENABLED(CONFIG_USB_CDNSP_HOST)) ||
(cdns->version < CDNSP_CONTROLLER_V2 &&
IS_ENABLED(CONFIG_USB_CDNS3_HOST)))
ret = cdns_host_init(cdns);
else
ret = -ENXIO;
if (ret) {
dev_err(dev, "Host initialization failed with %d\n",
ret);
@ -137,7 +152,11 @@ static int cdns3_core_init_role(struct cdns3 *cdns)
}
if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_PERIPHERAL) {
ret = cdns3_gadget_init(cdns);
if (cdns->gadget_init)
ret = cdns->gadget_init(cdns);
else
ret = -ENXIO;
if (ret) {
dev_err(dev, "Device initialization failed with %d\n",
ret);
@ -147,28 +166,28 @@ static int cdns3_core_init_role(struct cdns3 *cdns)
cdns->dr_mode = dr_mode;
ret = cdns3_drd_update_mode(cdns);
ret = cdns_drd_update_mode(cdns);
if (ret)
goto err;
/* Initialize idle role to start with */
ret = cdns3_role_start(cdns, USB_ROLE_NONE);
ret = cdns_role_start(cdns, USB_ROLE_NONE);
if (ret)
goto err;
switch (cdns->dr_mode) {
case USB_DR_MODE_OTG:
ret = cdns3_hw_role_switch(cdns);
ret = cdns_hw_role_switch(cdns);
if (ret)
goto err;
break;
case USB_DR_MODE_PERIPHERAL:
ret = cdns3_role_start(cdns, USB_ROLE_DEVICE);
ret = cdns_role_start(cdns, USB_ROLE_DEVICE);
if (ret)
goto err;
break;
case USB_DR_MODE_HOST:
ret = cdns3_role_start(cdns, USB_ROLE_HOST);
ret = cdns_role_start(cdns, USB_ROLE_HOST);
if (ret)
goto err;
break;
@ -179,32 +198,32 @@ static int cdns3_core_init_role(struct cdns3 *cdns)
return 0;
err:
cdns3_exit_roles(cdns);
cdns_exit_roles(cdns);
return ret;
}
/**
* cdns3_hw_role_state_machine - role switch state machine based on hw events.
* cdns_hw_role_state_machine - role switch state machine based on hw events.
* @cdns: Pointer to controller structure.
*
* Returns next role to be entered based on hw events.
*/
static enum usb_role cdns3_hw_role_state_machine(struct cdns3 *cdns)
static enum usb_role cdns_hw_role_state_machine(struct cdns *cdns)
{
enum usb_role role = USB_ROLE_NONE;
int id, vbus;
if (cdns->dr_mode != USB_DR_MODE_OTG) {
if (cdns3_is_host(cdns))
if (cdns_is_host(cdns))
role = USB_ROLE_HOST;
if (cdns3_is_device(cdns))
if (cdns_is_device(cdns))
role = USB_ROLE_DEVICE;
return role;
}
id = cdns3_get_id(cdns);
vbus = cdns3_get_vbus(cdns);
id = cdns_get_id(cdns);
vbus = cdns_get_vbus(cdns);
/*
* Role change state machine
@ -240,28 +259,28 @@ static enum usb_role cdns3_hw_role_state_machine(struct cdns3 *cdns)
return role;
}
static int cdns3_idle_role_start(struct cdns3 *cdns)
static int cdns_idle_role_start(struct cdns *cdns)
{
return 0;
}
static void cdns3_idle_role_stop(struct cdns3 *cdns)
static void cdns_idle_role_stop(struct cdns *cdns)
{
/* Program Lane swap and bring PHY out of RESET */
phy_reset(cdns->usb3_phy);
}
static int cdns3_idle_init(struct cdns3 *cdns)
static int cdns_idle_init(struct cdns *cdns)
{
struct cdns3_role_driver *rdrv;
struct cdns_role_driver *rdrv;
rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL);
if (!rdrv)
return -ENOMEM;
rdrv->start = cdns3_idle_role_start;
rdrv->stop = cdns3_idle_role_stop;
rdrv->state = CDNS3_ROLE_STATE_INACTIVE;
rdrv->start = cdns_idle_role_start;
rdrv->stop = cdns_idle_role_stop;
rdrv->state = CDNS_ROLE_STATE_INACTIVE;
rdrv->suspend = NULL;
rdrv->resume = NULL;
rdrv->name = "idle";
@ -272,10 +291,10 @@ static int cdns3_idle_init(struct cdns3 *cdns)
}
/**
* cdns3_hw_role_switch - switch roles based on HW state
* cdns_hw_role_switch - switch roles based on HW state
* @cdns: controller
*/
int cdns3_hw_role_switch(struct cdns3 *cdns)
int cdns_hw_role_switch(struct cdns *cdns)
{
enum usb_role real_role, current_role;
int ret = 0;
@ -287,22 +306,22 @@ int cdns3_hw_role_switch(struct cdns3 *cdns)
pm_runtime_get_sync(cdns->dev);
current_role = cdns->role;
real_role = cdns3_hw_role_state_machine(cdns);
real_role = cdns_hw_role_state_machine(cdns);
/* Do nothing if nothing changed */
if (current_role == real_role)
goto exit;
cdns3_role_stop(cdns);
cdns_role_stop(cdns);
dev_dbg(cdns->dev, "Switching role %d -> %d", current_role, real_role);
ret = cdns3_role_start(cdns, real_role);
ret = cdns_role_start(cdns, real_role);
if (ret) {
/* Back to current role */
dev_err(cdns->dev, "set %d has failed, back to %d\n",
real_role, current_role);
ret = cdns3_role_start(cdns, current_role);
ret = cdns_role_start(cdns, current_role);
if (ret)
dev_err(cdns->dev, "back to %d failed too\n",
current_role);
@ -319,15 +338,15 @@ exit:
*
* Returns role
*/
static enum usb_role cdns3_role_get(struct usb_role_switch *sw)
static enum usb_role cdns_role_get(struct usb_role_switch *sw)
{
struct cdns3 *cdns = usb_role_switch_get_drvdata(sw);
struct cdns *cdns = usb_role_switch_get_drvdata(sw);
return cdns->role;
}
/**
* cdns3_role_set - set current role of controller.
* cdns_role_set - set current role of controller.
*
* @sw: pointer to USB role switch structure
* @role: the previous role
@ -335,9 +354,9 @@ static enum usb_role cdns3_role_get(struct usb_role_switch *sw)
* - Role switch for dual-role devices
* - USB_ROLE_GADGET <--> USB_ROLE_NONE for peripheral-only devices
*/
static int cdns3_role_set(struct usb_role_switch *sw, enum usb_role role)
static int cdns_role_set(struct usb_role_switch *sw, enum usb_role role)
{
struct cdns3 *cdns = usb_role_switch_get_drvdata(sw);
struct cdns *cdns = usb_role_switch_get_drvdata(sw);
int ret = 0;
pm_runtime_get_sync(cdns->dev);
@ -365,8 +384,8 @@ static int cdns3_role_set(struct usb_role_switch *sw, enum usb_role role)
}
}
cdns3_role_stop(cdns);
ret = cdns3_role_start(cdns, role);
cdns_role_stop(cdns);
ret = cdns_role_start(cdns, role);
if (ret)
dev_err(cdns->dev, "set role %d has failed\n", role);
@ -375,37 +394,17 @@ pm_put:
return ret;
}
static int set_phy_power_on(struct cdns3 *cdns)
{
int ret;
ret = phy_power_on(cdns->usb2_phy);
if (ret)
return ret;
ret = phy_power_on(cdns->usb3_phy);
if (ret)
phy_power_off(cdns->usb2_phy);
return ret;
}
static void set_phy_power_off(struct cdns3 *cdns)
{
phy_power_off(cdns->usb3_phy);
phy_power_off(cdns->usb2_phy);
}
/**
* cdns3_wakeup_irq - interrupt handler for wakeup events
* @irq: irq number for cdns3 core device
* @data: structure of cdns3
* cdns_wakeup_irq - interrupt handler for wakeup events
* @irq: irq number for cdns3/cdnsp core device
* @data: structure of cdns
*
* Returns IRQ_HANDLED or IRQ_NONE
*/
static irqreturn_t cdns3_wakeup_irq(int irq, void *data)
static irqreturn_t cdns_wakeup_irq(int irq, void *data)
{
struct cdns3 *cdns = data;
struct cdns *cdns = data;
if (cdns->in_lpm) {
disable_irq_nosync(irq);
@ -420,17 +419,14 @@ static irqreturn_t cdns3_wakeup_irq(int irq, void *data)
}
/**
* cdns3_probe - probe for cdns3 core device
* @pdev: Pointer to cdns3 core platform device
* cdns_probe - probe for cdns3/cdnsp core device
* @cdns: Pointer to cdns structure.
*
* Returns 0 on success otherwise negative errno
*/
static int cdns3_probe(struct platform_device *pdev)
int cdns_init(struct cdns *cdns)
{
struct device *dev = &pdev->dev;
struct resource *res;
struct cdns3 *cdns;
void __iomem *regs;
struct device *dev = cdns->dev;
int ret;
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
@ -439,259 +435,78 @@ static int cdns3_probe(struct platform_device *pdev)
return ret;
}
cdns = devm_kzalloc(dev, sizeof(*cdns), GFP_KERNEL);
if (!cdns)
return -ENOMEM;
cdns->dev = dev;
cdns->pdata = dev_get_platdata(dev);
platform_set_drvdata(pdev, cdns);
res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "host");
if (!res) {
dev_err(dev, "missing host IRQ\n");
return -ENODEV;
}
cdns->xhci_res[0] = *res;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "xhci");
if (!res) {
dev_err(dev, "couldn't get xhci resource\n");
return -ENXIO;
}
cdns->xhci_res[1] = *res;
cdns->dev_irq = platform_get_irq_byname(pdev, "peripheral");
if (cdns->dev_irq < 0)
return cdns->dev_irq;
regs = devm_platform_ioremap_resource_byname(pdev, "dev");
if (IS_ERR(regs))
return PTR_ERR(regs);
cdns->dev_regs = regs;
cdns->otg_irq = platform_get_irq_byname(pdev, "otg");
if (cdns->otg_irq < 0)
return cdns->otg_irq;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "otg");
if (!res) {
dev_err(dev, "couldn't get otg resource\n");
return -ENXIO;
}
cdns->phyrst_a_enable = device_property_read_bool(dev, "cdns,phyrst-a-enable");
cdns->otg_res = *res;
cdns->wakeup_irq = platform_get_irq_byname_optional(pdev, "wakeup");
if (cdns->wakeup_irq == -EPROBE_DEFER)
return cdns->wakeup_irq;
else if (cdns->wakeup_irq == 0)
return -EINVAL;
if (cdns->wakeup_irq < 0) {
dev_dbg(dev, "couldn't get wakeup irq\n");
cdns->wakeup_irq = 0x0;
}
mutex_init(&cdns->mutex);
cdns->usb2_phy = devm_phy_optional_get(dev, "cdns3,usb2-phy");
if (IS_ERR(cdns->usb2_phy))
return PTR_ERR(cdns->usb2_phy);
ret = phy_init(cdns->usb2_phy);
if (ret)
return ret;
cdns->usb3_phy = devm_phy_optional_get(dev, "cdns3,usb3-phy");
if (IS_ERR(cdns->usb3_phy))
return PTR_ERR(cdns->usb3_phy);
ret = phy_init(cdns->usb3_phy);
if (ret)
goto err1;
ret = set_phy_power_on(cdns);
if (ret)
goto err2;
if (device_property_read_bool(dev, "usb-role-switch")) {
struct usb_role_switch_desc sw_desc = { };
sw_desc.set = cdns3_role_set;
sw_desc.get = cdns3_role_get;
sw_desc.set = cdns_role_set;
sw_desc.get = cdns_role_get;
sw_desc.allow_userspace_control = true;
sw_desc.driver_data = cdns;
sw_desc.fwnode = dev->fwnode;
cdns->role_sw = usb_role_switch_register(dev, &sw_desc);
if (IS_ERR(cdns->role_sw)) {
ret = PTR_ERR(cdns->role_sw);
dev_warn(dev, "Unable to register Role Switch\n");
goto err3;
return PTR_ERR(cdns->role_sw);
}
}
if (cdns->wakeup_irq) {
ret = devm_request_irq(cdns->dev, cdns->wakeup_irq,
cdns3_wakeup_irq,
cdns_wakeup_irq,
IRQF_SHARED,
dev_name(cdns->dev), cdns);
if (ret) {
dev_err(cdns->dev, "couldn't register wakeup irq handler\n");
goto err4;
goto role_switch_unregister;
}
}
ret = cdns3_drd_init(cdns);
ret = cdns_drd_init(cdns);
if (ret)
goto err4;
goto init_failed;
ret = cdns3_core_init_role(cdns);
ret = cdns_core_init_role(cdns);
if (ret)
goto err4;
goto init_failed;
spin_lock_init(&cdns->lock);
device_set_wakeup_capable(dev, true);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
if (!(cdns->pdata && (cdns->pdata->quirks & CDNS3_DEFAULT_PM_RUNTIME_ALLOW)))
pm_runtime_forbid(dev);
/*
* The controller needs less time between bus and controller suspend,
* and we also needs a small delay to avoid frequently entering low
* power mode.
*/
pm_runtime_set_autosuspend_delay(dev, 20);
pm_runtime_mark_last_busy(dev);
pm_runtime_use_autosuspend(dev);
dev_dbg(dev, "Cadence USB3 core: probe succeed\n");
return 0;
err4:
cdns3_drd_exit(cdns);
init_failed:
cdns_drd_exit(cdns);
role_switch_unregister:
if (cdns->role_sw)
usb_role_switch_unregister(cdns->role_sw);
err3:
set_phy_power_off(cdns);
err2:
phy_exit(cdns->usb3_phy);
err1:
phy_exit(cdns->usb2_phy);
return ret;
}
EXPORT_SYMBOL_GPL(cdns_init);
/**
* cdns3_remove - unbind drd driver and clean up
* @pdev: Pointer to Linux platform device
* cdns_remove - unbind drd driver and clean up
* @cdns: Pointer to cdns structure.
*
* Returns 0 on success otherwise negative errno
*/
static int cdns3_remove(struct platform_device *pdev)
int cdns_remove(struct cdns *cdns)
{
struct cdns3 *cdns = platform_get_drvdata(pdev);
pm_runtime_get_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
pm_runtime_put_noidle(&pdev->dev);
cdns3_exit_roles(cdns);
cdns_exit_roles(cdns);
usb_role_switch_unregister(cdns->role_sw);
set_phy_power_off(cdns);
phy_exit(cdns->usb2_phy);
phy_exit(cdns->usb3_phy);
return 0;
}
#ifdef CONFIG_PM
static int cdns3_set_platform_suspend(struct device *dev,
bool suspend, bool wakeup)
{
struct cdns3 *cdns = dev_get_drvdata(dev);
int ret = 0;
if (cdns->pdata && cdns->pdata->platform_suspend)
ret = cdns->pdata->platform_suspend(dev, suspend, wakeup);
return ret;
}
static int cdns3_controller_suspend(struct device *dev, pm_message_t msg)
{
struct cdns3 *cdns = dev_get_drvdata(dev);
bool wakeup;
unsigned long flags;
if (cdns->in_lpm)
return 0;
if (PMSG_IS_AUTO(msg))
wakeup = true;
else
wakeup = device_may_wakeup(dev);
cdns3_set_platform_suspend(cdns->dev, true, wakeup);
set_phy_power_off(cdns);
spin_lock_irqsave(&cdns->lock, flags);
cdns->in_lpm = true;
spin_unlock_irqrestore(&cdns->lock, flags);
dev_dbg(cdns->dev, "%s ends\n", __func__);
return 0;
}
EXPORT_SYMBOL_GPL(cdns_remove);
static int cdns3_controller_resume(struct device *dev, pm_message_t msg)
{
struct cdns3 *cdns = dev_get_drvdata(dev);
int ret;
unsigned long flags;
if (!cdns->in_lpm)
return 0;
ret = set_phy_power_on(cdns);
if (ret)
return ret;
cdns3_set_platform_suspend(cdns->dev, false, false);
spin_lock_irqsave(&cdns->lock, flags);
if (cdns->roles[cdns->role]->resume && !PMSG_IS_AUTO(msg))
cdns->roles[cdns->role]->resume(cdns, false);
cdns->in_lpm = false;
spin_unlock_irqrestore(&cdns->lock, flags);
if (cdns->wakeup_pending) {
cdns->wakeup_pending = false;
enable_irq(cdns->wakeup_irq);
}
dev_dbg(cdns->dev, "%s ends\n", __func__);
return ret;
}
static int cdns3_runtime_suspend(struct device *dev)
{
return cdns3_controller_suspend(dev, PMSG_AUTO_SUSPEND);
}
static int cdns3_runtime_resume(struct device *dev)
{
return cdns3_controller_resume(dev, PMSG_AUTO_RESUME);
}
#ifdef CONFIG_PM_SLEEP
static int cdns3_suspend(struct device *dev)
int cdns_suspend(struct cdns *cdns)
{
struct cdns3 *cdns = dev_get_drvdata(dev);
struct device *dev = cdns->dev;
unsigned long flags;
if (pm_runtime_status_suspended(dev))
@ -703,52 +518,30 @@ static int cdns3_suspend(struct device *dev)
spin_unlock_irqrestore(&cdns->lock, flags);
}
return cdns3_controller_suspend(dev, PMSG_SUSPEND);
return 0;
}
EXPORT_SYMBOL_GPL(cdns_suspend);
static int cdns3_resume(struct device *dev)
int cdns_resume(struct cdns *cdns, u8 set_active)
{
int ret;
struct device *dev = cdns->dev;
ret = cdns3_controller_resume(dev, PMSG_RESUME);
if (ret)
return ret;
if (cdns->roles[cdns->role]->resume)
cdns->roles[cdns->role]->resume(cdns, false);
pm_runtime_disable(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
if (set_active) {
pm_runtime_disable(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
}
return ret;
return 0;
}
EXPORT_SYMBOL_GPL(cdns_resume);
#endif /* CONFIG_PM_SLEEP */
#endif /* CONFIG_PM */
static const struct dev_pm_ops cdns3_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(cdns3_suspend, cdns3_resume)
SET_RUNTIME_PM_OPS(cdns3_runtime_suspend, cdns3_runtime_resume, NULL)
};
#ifdef CONFIG_OF
static const struct of_device_id of_cdns3_match[] = {
{ .compatible = "cdns,usb3" },
{ },
};
MODULE_DEVICE_TABLE(of, of_cdns3_match);
#endif
static struct platform_driver cdns3_driver = {
.probe = cdns3_probe,
.remove = cdns3_remove,
.driver = {
.name = "cdns-usb3",
.of_match_table = of_match_ptr(of_cdns3_match),
.pm = &cdns3_pm_ops,
},
};
module_platform_driver(cdns3_driver);
MODULE_ALIAS("platform:cdns3");
MODULE_AUTHOR("Peter Chen <peter.chen@nxp.com>");
MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Cadence USB3 DRD Controller Driver");
MODULE_AUTHOR("Roger Quadros <rogerq@ti.com>");
MODULE_DESCRIPTION("Cadence USBSS and USBSSP DRD Driver");
MODULE_LICENSE("GPL");

View File

@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Cadence USBSS DRD Header File.
* Cadence USBSS and USBSSP DRD Header File.
*
* Copyright (C) 2017-2018 NXP
* Copyright (C) 2018-2019 Cadence.
@ -14,10 +14,10 @@
#ifndef __LINUX_CDNS3_CORE_H
#define __LINUX_CDNS3_CORE_H
struct cdns3;
struct cdns;
/**
* struct cdns3_role_driver - host/gadget role driver
* struct cdns_role_driver - host/gadget role driver
* @start: start this role
* @stop: stop this role
* @suspend: suspend callback for this role
@ -26,18 +26,18 @@ struct cdns3;
* @name: role name string (host/gadget)
* @state: current state
*/
struct cdns3_role_driver {
int (*start)(struct cdns3 *cdns);
void (*stop)(struct cdns3 *cdns);
int (*suspend)(struct cdns3 *cdns, bool do_wakeup);
int (*resume)(struct cdns3 *cdns, bool hibernated);
struct cdns_role_driver {
int (*start)(struct cdns *cdns);
void (*stop)(struct cdns *cdns);
int (*suspend)(struct cdns *cdns, bool do_wakeup);
int (*resume)(struct cdns *cdns, bool hibernated);
const char *name;
#define CDNS3_ROLE_STATE_INACTIVE 0
#define CDNS3_ROLE_STATE_ACTIVE 1
#define CDNS_ROLE_STATE_INACTIVE 0
#define CDNS_ROLE_STATE_ACTIVE 1
int state;
};
#define CDNS3_XHCI_RESOURCES_NUM 2
#define CDNS_XHCI_RESOURCES_NUM 2
struct cdns3_platform_data {
int (*platform_suspend)(struct device *dev,
@ -47,7 +47,7 @@ struct cdns3_platform_data {
};
/**
* struct cdns3 - Representation of Cadence USB3 DRD controller.
* struct cdns - Representation of Cadence USB3 DRD controller.
* @dev: pointer to Cadence device struct
* @xhci_regs: pointer to base of xhci registers
* @xhci_res: the resource for xhci
@ -55,14 +55,16 @@ struct cdns3_platform_data {
* @otg_res: the resource for otg
* @otg_v0_regs: pointer to base of v0 otg registers
* @otg_v1_regs: pointer to base of v1 otg registers
* @otg_cdnsp_regs: pointer to base of CDNSP otg registers
* @otg_regs: pointer to base of otg registers
* @otg_irq_regs: pointer to interrupt registers
* @otg_irq: irq number for otg controller
* @dev_irq: irq number for device controller
* @wakeup_irq: irq number for wakeup event, it is optional
* @roles: array of supported roles for this controller
* @role: current role
* @host_dev: the child host device pointer for cdns3 core
* @gadget_dev: the child gadget device pointer for cdns3 core
* @host_dev: the child host device pointer for cdns core
* @gadget_dev: the child gadget device pointer
* @usb2_phy: pointer to USB2 PHY
* @usb3_phy: pointer to USB3 PHY
* @mutex: the mutex for concurrent code at driver
@ -76,29 +78,33 @@ struct cdns3_platform_data {
* @pdata: platform data from glue layer
* @lock: spinlock structure
* @xhci_plat_data: xhci private data structure pointer
* @gadget_init: pointer to gadget initialization function
*/
struct cdns3 {
struct cdns {
struct device *dev;
void __iomem *xhci_regs;
struct resource xhci_res[CDNS3_XHCI_RESOURCES_NUM];
struct resource xhci_res[CDNS_XHCI_RESOURCES_NUM];
struct cdns3_usb_regs __iomem *dev_regs;
struct resource otg_res;
struct cdns3_otg_legacy_regs *otg_v0_regs;
struct cdns3_otg_regs *otg_v1_regs;
struct cdns3_otg_common_regs *otg_regs;
struct resource otg_res;
struct cdns3_otg_legacy_regs __iomem *otg_v0_regs;
struct cdns3_otg_regs __iomem *otg_v1_regs;
struct cdnsp_otg_regs __iomem *otg_cdnsp_regs;
struct cdns_otg_common_regs __iomem *otg_regs;
struct cdns_otg_irq_regs __iomem *otg_irq_regs;
#define CDNS3_CONTROLLER_V0 0
#define CDNS3_CONTROLLER_V1 1
#define CDNSP_CONTROLLER_V2 2
u32 version;
bool phyrst_a_enable;
int otg_irq;
int dev_irq;
int wakeup_irq;
struct cdns3_role_driver *roles[USB_ROLE_DEVICE + 1];
struct cdns_role_driver *roles[USB_ROLE_DEVICE + 1];
enum usb_role role;
struct platform_device *host_dev;
struct cdns3_device *gadget_dev;
void *gadget_dev;
struct phy *usb2_phy;
struct phy *usb3_phy;
/* mutext used in workqueue*/
@ -110,8 +116,21 @@ struct cdns3 {
struct cdns3_platform_data *pdata;
spinlock_t lock;
struct xhci_plat_priv *xhci_plat_data;
int (*gadget_init)(struct cdns *cdns);
};
int cdns3_hw_role_switch(struct cdns3 *cdns);
int cdns_hw_role_switch(struct cdns *cdns);
int cdns_init(struct cdns *cdns);
int cdns_remove(struct cdns *cdns);
#ifdef CONFIG_PM_SLEEP
int cdns_resume(struct cdns *cdns, u8 set_active);
int cdns_suspend(struct cdns *cdns);
#else /* CONFIG_PM_SLEEP */
static inline int cdns_resume(struct cdns *cdns, u8 set_active)
{ return 0; }
static inline int cdns_suspend(struct cdns *cdns)
{ return 0; }
#endif /* CONFIG_PM_SLEEP */
#endif /* __LINUX_CDNS3_CORE_H */

View File

@ -1,35 +1,33 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Cadence USBSS DRD Driver.
* Cadence USBSS and USBSSP DRD Driver.
*
* Copyright (C) 2018-2019 Cadence.
* Copyright (C) 2018-2020 Cadence.
* Copyright (C) 2019 Texas Instruments
*
* Author: Pawel Laszczak <pawell@cadence.com>
* Roger Quadros <rogerq@ti.com>
*
*
*/
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/iopoll.h>
#include <linux/usb/otg.h>
#include <linux/phy/phy.h>
#include "gadget.h"
#include "drd.h"
#include "core.h"
/**
* cdns3_set_mode - change mode of OTG Core
* cdns_set_mode - change mode of OTG Core
* @cdns: pointer to context structure
* @mode: selected mode from cdns_role
*
* Returns 0 on success otherwise negative errno
*/
int cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode)
static int cdns_set_mode(struct cdns *cdns, enum usb_dr_mode mode)
{
void __iomem *override_reg;
u32 reg;
switch (mode) {
@ -39,11 +37,24 @@ int cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode)
break;
case USB_DR_MODE_OTG:
dev_dbg(cdns->dev, "Set controller to OTG mode\n");
if (cdns->version == CDNS3_CONTROLLER_V1) {
reg = readl(&cdns->otg_v1_regs->override);
reg |= OVERRIDE_IDPULLUP;
writel(reg, &cdns->otg_v1_regs->override);
if (cdns->version == CDNSP_CONTROLLER_V2)
override_reg = &cdns->otg_cdnsp_regs->override;
else if (cdns->version == CDNS3_CONTROLLER_V1)
override_reg = &cdns->otg_v1_regs->override;
else
override_reg = &cdns->otg_v0_regs->ctrl1;
reg = readl(override_reg);
if (cdns->version != CDNS3_CONTROLLER_V0)
reg |= OVERRIDE_IDPULLUP;
else
reg |= OVERRIDE_IDPULLUP_V0;
writel(reg, override_reg);
if (cdns->version == CDNS3_CONTROLLER_V1) {
/*
* Enable work around feature built into the
* controller to address issue with RX Sensitivity
@ -55,10 +66,6 @@ int cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode)
reg |= PHYRST_CFG_PHYRST_A_ENABLE;
writel(reg, &cdns->otg_v1_regs->phyrst_cfg);
}
} else {
reg = readl(&cdns->otg_v0_regs->ctrl1);
reg |= OVERRIDE_IDPULLUP_V0;
writel(reg, &cdns->otg_v0_regs->ctrl1);
}
/*
@ -76,7 +83,7 @@ int cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode)
return 0;
}
int cdns3_get_id(struct cdns3 *cdns)
int cdns_get_id(struct cdns *cdns)
{
int id;
@ -86,7 +93,7 @@ int cdns3_get_id(struct cdns3 *cdns)
return id;
}
int cdns3_get_vbus(struct cdns3 *cdns)
int cdns_get_vbus(struct cdns *cdns)
{
int vbus;
@ -96,64 +103,95 @@ int cdns3_get_vbus(struct cdns3 *cdns)
return vbus;
}
bool cdns3_is_host(struct cdns3 *cdns)
void cdns_clear_vbus(struct cdns *cdns)
{
u32 reg;
if (cdns->version != CDNSP_CONTROLLER_V2)
return;
reg = readl(&cdns->otg_cdnsp_regs->override);
reg |= OVERRIDE_SESS_VLD_SEL;
writel(reg, &cdns->otg_cdnsp_regs->override);
}
EXPORT_SYMBOL_GPL(cdns_clear_vbus);
void cdns_set_vbus(struct cdns *cdns)
{
u32 reg;
if (cdns->version != CDNSP_CONTROLLER_V2)
return;
reg = readl(&cdns->otg_cdnsp_regs->override);
reg &= ~OVERRIDE_SESS_VLD_SEL;
writel(reg, &cdns->otg_cdnsp_regs->override);
}
EXPORT_SYMBOL_GPL(cdns_set_vbus);
bool cdns_is_host(struct cdns *cdns)
{
if (cdns->dr_mode == USB_DR_MODE_HOST)
return true;
else if (cdns3_get_id(cdns) == CDNS3_ID_HOST)
else if (cdns_get_id(cdns) == CDNS3_ID_HOST)
return true;
return false;
}
bool cdns3_is_device(struct cdns3 *cdns)
bool cdns_is_device(struct cdns *cdns)
{
if (cdns->dr_mode == USB_DR_MODE_PERIPHERAL)
return true;
else if (cdns->dr_mode == USB_DR_MODE_OTG)
if (cdns3_get_id(cdns) == CDNS3_ID_PERIPHERAL)
if (cdns_get_id(cdns) == CDNS3_ID_PERIPHERAL)
return true;
return false;
}
/**
* cdns3_otg_disable_irq - Disable all OTG interrupts
* cdns_otg_disable_irq - Disable all OTG interrupts
* @cdns: Pointer to controller context structure
*/
static void cdns3_otg_disable_irq(struct cdns3 *cdns)
static void cdns_otg_disable_irq(struct cdns *cdns)
{
writel(0, &cdns->otg_regs->ien);
writel(0, &cdns->otg_irq_regs->ien);
}
/**
* cdns3_otg_enable_irq - enable id and sess_valid interrupts
* cdns_otg_enable_irq - enable id and sess_valid interrupts
* @cdns: Pointer to controller context structure
*/
static void cdns3_otg_enable_irq(struct cdns3 *cdns)
static void cdns_otg_enable_irq(struct cdns *cdns)
{
writel(OTGIEN_ID_CHANGE_INT | OTGIEN_VBUSVALID_RISE_INT |
OTGIEN_VBUSVALID_FALL_INT, &cdns->otg_regs->ien);
OTGIEN_VBUSVALID_FALL_INT, &cdns->otg_irq_regs->ien);
}
/**
* cdns3_drd_host_on - start host.
* cdns_drd_host_on - start host.
* @cdns: Pointer to controller context structure.
*
* Returns 0 on success otherwise negative errno.
*/
int cdns3_drd_host_on(struct cdns3 *cdns)
int cdns_drd_host_on(struct cdns *cdns)
{
u32 val;
u32 val, ready_bit;
int ret;
/* Enable host mode. */
writel(OTGCMD_HOST_BUS_REQ | OTGCMD_OTG_DIS,
&cdns->otg_regs->cmd);
if (cdns->version == CDNSP_CONTROLLER_V2)
ready_bit = OTGSTS_CDNSP_XHCI_READY;
else
ready_bit = OTGSTS_CDNS3_XHCI_READY;
dev_dbg(cdns->dev, "Waiting till Host mode is turned on\n");
ret = readl_poll_timeout_atomic(&cdns->otg_regs->sts, val,
val & OTGSTS_XHCI_READY, 1, 100000);
val & ready_bit, 1, 100000);
if (ret)
dev_err(cdns->dev, "timeout waiting for xhci_ready\n");
@ -163,10 +201,10 @@ int cdns3_drd_host_on(struct cdns3 *cdns)
}
/**
* cdns3_drd_host_off - stop host.
* cdns_drd_host_off - stop host.
* @cdns: Pointer to controller context structure.
*/
void cdns3_drd_host_off(struct cdns3 *cdns)
void cdns_drd_host_off(struct cdns *cdns)
{
u32 val;
@ -182,24 +220,29 @@ void cdns3_drd_host_off(struct cdns3 *cdns)
}
/**
* cdns3_drd_gadget_on - start gadget.
* cdns_drd_gadget_on - start gadget.
* @cdns: Pointer to controller context structure.
*
* Returns 0 on success otherwise negative errno
*/
int cdns3_drd_gadget_on(struct cdns3 *cdns)
int cdns_drd_gadget_on(struct cdns *cdns)
{
int ret, val;
u32 reg = OTGCMD_OTG_DIS;
u32 ready_bit;
int ret, val;
/* switch OTG core */
writel(OTGCMD_DEV_BUS_REQ | reg, &cdns->otg_regs->cmd);
dev_dbg(cdns->dev, "Waiting till Device mode is turned on\n");
if (cdns->version == CDNSP_CONTROLLER_V2)
ready_bit = OTGSTS_CDNSP_DEV_READY;
else
ready_bit = OTGSTS_CDNS3_DEV_READY;
ret = readl_poll_timeout_atomic(&cdns->otg_regs->sts, val,
val & OTGSTS_DEV_READY,
1, 100000);
val & ready_bit, 1, 100000);
if (ret) {
dev_err(cdns->dev, "timeout waiting for dev_ready\n");
return ret;
@ -208,12 +251,13 @@ int cdns3_drd_gadget_on(struct cdns3 *cdns)
phy_set_mode(cdns->usb3_phy, PHY_MODE_USB_DEVICE);
return 0;
}
EXPORT_SYMBOL_GPL(cdns_drd_gadget_on);
/**
* cdns3_drd_gadget_off - stop gadget.
* cdns_drd_gadget_off - stop gadget.
* @cdns: Pointer to controller context structure.
*/
void cdns3_drd_gadget_off(struct cdns3 *cdns)
void cdns_drd_gadget_off(struct cdns *cdns)
{
u32 val;
@ -231,49 +275,50 @@ void cdns3_drd_gadget_off(struct cdns3 *cdns)
1, 2000000);
phy_set_mode(cdns->usb3_phy, PHY_MODE_INVALID);
}
EXPORT_SYMBOL_GPL(cdns_drd_gadget_off);
/**
* cdns3_init_otg_mode - initialize drd controller
* cdns_init_otg_mode - initialize drd controller
* @cdns: Pointer to controller context structure
*
* Returns 0 on success otherwise negative errno
*/
static int cdns3_init_otg_mode(struct cdns3 *cdns)
static int cdns_init_otg_mode(struct cdns *cdns)
{
int ret;
cdns3_otg_disable_irq(cdns);
cdns_otg_disable_irq(cdns);
/* clear all interrupts */
writel(~0, &cdns->otg_regs->ivect);
writel(~0, &cdns->otg_irq_regs->ivect);
ret = cdns3_set_mode(cdns, USB_DR_MODE_OTG);
ret = cdns_set_mode(cdns, USB_DR_MODE_OTG);
if (ret)
return ret;
cdns3_otg_enable_irq(cdns);
cdns_otg_enable_irq(cdns);
return 0;
}
/**
* cdns3_drd_update_mode - initialize mode of operation
* cdns_drd_update_mode - initialize mode of operation
* @cdns: Pointer to controller context structure
*
* Returns 0 on success otherwise negative errno
*/
int cdns3_drd_update_mode(struct cdns3 *cdns)
int cdns_drd_update_mode(struct cdns *cdns)
{
int ret;
switch (cdns->dr_mode) {
case USB_DR_MODE_PERIPHERAL:
ret = cdns3_set_mode(cdns, USB_DR_MODE_PERIPHERAL);
ret = cdns_set_mode(cdns, USB_DR_MODE_PERIPHERAL);
break;
case USB_DR_MODE_HOST:
ret = cdns3_set_mode(cdns, USB_DR_MODE_HOST);
ret = cdns_set_mode(cdns, USB_DR_MODE_HOST);
break;
case USB_DR_MODE_OTG:
ret = cdns3_init_otg_mode(cdns);
ret = cdns_init_otg_mode(cdns);
break;
default:
dev_err(cdns->dev, "Unsupported mode of operation %d\n",
@ -284,27 +329,27 @@ int cdns3_drd_update_mode(struct cdns3 *cdns)
return ret;
}
static irqreturn_t cdns3_drd_thread_irq(int irq, void *data)
static irqreturn_t cdns_drd_thread_irq(int irq, void *data)
{
struct cdns3 *cdns = data;
struct cdns *cdns = data;
cdns3_hw_role_switch(cdns);
cdns_hw_role_switch(cdns);
return IRQ_HANDLED;
}
/**
* cdns3_drd_irq - interrupt handler for OTG events
* cdns_drd_irq - interrupt handler for OTG events
*
* @irq: irq number for cdns3 core device
* @data: structure of cdns3
* @irq: irq number for cdns core device
* @data: structure of cdns
*
* Returns IRQ_HANDLED or IRQ_NONE
*/
static irqreturn_t cdns3_drd_irq(int irq, void *data)
static irqreturn_t cdns_drd_irq(int irq, void *data)
{
irqreturn_t ret = IRQ_NONE;
struct cdns3 *cdns = data;
struct cdns *cdns = data;
u32 reg;
if (cdns->dr_mode != USB_DR_MODE_OTG)
@ -313,30 +358,30 @@ static irqreturn_t cdns3_drd_irq(int irq, void *data)
if (cdns->in_lpm)
return ret;
reg = readl(&cdns->otg_regs->ivect);
reg = readl(&cdns->otg_irq_regs->ivect);
if (!reg)
return IRQ_NONE;
if (reg & OTGIEN_ID_CHANGE_INT) {
dev_dbg(cdns->dev, "OTG IRQ: new ID: %d\n",
cdns3_get_id(cdns));
cdns_get_id(cdns));
ret = IRQ_WAKE_THREAD;
}
if (reg & (OTGIEN_VBUSVALID_RISE_INT | OTGIEN_VBUSVALID_FALL_INT)) {
dev_dbg(cdns->dev, "OTG IRQ: new VBUS: %d\n",
cdns3_get_vbus(cdns));
cdns_get_vbus(cdns));
ret = IRQ_WAKE_THREAD;
}
writel(~0, &cdns->otg_regs->ivect);
writel(~0, &cdns->otg_irq_regs->ivect);
return ret;
}
int cdns3_drd_init(struct cdns3 *cdns)
int cdns_drd_init(struct cdns *cdns)
{
void __iomem *regs;
u32 state;
@ -347,28 +392,43 @@ int cdns3_drd_init(struct cdns3 *cdns)
return PTR_ERR(regs);
/* Detection of DRD version. Controller has been released
* in two versions. Both are similar, but they have same changes
* in register maps.
* The first register in old version is command register and it's read
* only, so driver should read 0 from it. On the other hand, in v1
* the first register contains device ID number which is not set to 0.
* Driver uses this fact to detect the proper version of
* in three versions. All are very similar and are software compatible,
* but they have same changes in register maps.
* The first register in oldest version is command register and it's
* read only. Driver should read 0 from it. On the other hand, in v1
* and v2 the first register contains device ID number which is not
* set to 0. Driver uses this fact to detect the proper version of
* controller.
*/
cdns->otg_v0_regs = regs;
if (!readl(&cdns->otg_v0_regs->cmd)) {
cdns->version = CDNS3_CONTROLLER_V0;
cdns->otg_v1_regs = NULL;
cdns->otg_cdnsp_regs = NULL;
cdns->otg_regs = regs;
cdns->otg_irq_regs = (struct cdns_otg_irq_regs __iomem *)
&cdns->otg_v0_regs->ien;
writel(1, &cdns->otg_v0_regs->simulate);
dev_dbg(cdns->dev, "DRD version v0 (%08x)\n",
readl(&cdns->otg_v0_regs->version));
} else {
cdns->otg_v0_regs = NULL;
cdns->otg_v1_regs = regs;
cdns->otg_regs = (void *)&cdns->otg_v1_regs->cmd;
cdns->version = CDNS3_CONTROLLER_V1;
writel(1, &cdns->otg_v1_regs->simulate);
cdns->otg_cdnsp_regs = regs;
cdns->otg_regs = (void __iomem *)&cdns->otg_v1_regs->cmd;
if (readl(&cdns->otg_cdnsp_regs->did) == OTG_CDNSP_DID) {
cdns->otg_irq_regs = (struct cdns_otg_irq_regs __iomem *)
&cdns->otg_cdnsp_regs->ien;
cdns->version = CDNSP_CONTROLLER_V2;
} else {
cdns->otg_irq_regs = (struct cdns_otg_irq_regs __iomem *)
&cdns->otg_v1_regs->ien;
writel(1, &cdns->otg_v1_regs->simulate);
cdns->version = CDNS3_CONTROLLER_V1;
}
dev_dbg(cdns->dev, "DRD version v1 (ID: %08x, rev: %08x)\n",
readl(&cdns->otg_v1_regs->did),
readl(&cdns->otg_v1_regs->rid));
@ -378,17 +438,24 @@ int cdns3_drd_init(struct cdns3 *cdns)
/* Update dr_mode according to STRAP configuration. */
cdns->dr_mode = USB_DR_MODE_OTG;
if (state == OTGSTS_STRAP_HOST) {
if ((cdns->version == CDNSP_CONTROLLER_V2 &&
state == OTGSTS_CDNSP_STRAP_HOST) ||
(cdns->version != CDNSP_CONTROLLER_V2 &&
state == OTGSTS_STRAP_HOST)) {
dev_dbg(cdns->dev, "Controller strapped to HOST\n");
cdns->dr_mode = USB_DR_MODE_HOST;
} else if (state == OTGSTS_STRAP_GADGET) {
} else if ((cdns->version == CDNSP_CONTROLLER_V2 &&
state == OTGSTS_CDNSP_STRAP_GADGET) ||
(cdns->version != CDNSP_CONTROLLER_V2 &&
state == OTGSTS_STRAP_GADGET)) {
dev_dbg(cdns->dev, "Controller strapped to PERIPHERAL\n");
cdns->dr_mode = USB_DR_MODE_PERIPHERAL;
}
ret = devm_request_threaded_irq(cdns->dev, cdns->otg_irq,
cdns3_drd_irq,
cdns3_drd_thread_irq,
cdns_drd_irq,
cdns_drd_thread_irq,
IRQF_SHARED,
dev_name(cdns->dev), cdns);
if (ret) {
@ -405,8 +472,9 @@ int cdns3_drd_init(struct cdns3 *cdns)
return 0;
}
int cdns3_drd_exit(struct cdns3 *cdns)
int cdns_drd_exit(struct cdns *cdns)
{
cdns3_otg_disable_irq(cdns);
cdns_otg_disable_irq(cdns);
return 0;
}

View File

@ -1,8 +1,8 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Cadence USB3 DRD header file.
* Cadence USB3 and USBSSP DRD header file.
*
* Copyright (C) 2018-2019 Cadence.
* Copyright (C) 2018-2020 Cadence.
*
* Author: Pawel Laszczak <pawell@cadence.com>
*/
@ -10,10 +10,9 @@
#define __LINUX_CDNS3_DRD
#include <linux/usb/otg.h>
#include <linux/phy/phy.h>
#include "core.h"
/* DRD register interface for version v1. */
/* DRD register interface for version v1 of cdns3 driver. */
struct cdns3_otg_regs {
__le32 did;
__le32 rid;
@ -38,7 +37,7 @@ struct cdns3_otg_regs {
__le32 ctrl2;
};
/* DRD register interface for version v0. */
/* DRD register interface for version v0 of cdns3 driver. */
struct cdns3_otg_legacy_regs {
__le32 cmd;
__le32 sts;
@ -57,14 +56,45 @@ struct cdns3_otg_legacy_regs {
__le32 ctrl1;
};
/*
* Common registers interface for both version of DRD.
*/
struct cdns3_otg_common_regs {
/* DRD register interface for cdnsp driver */
struct cdnsp_otg_regs {
__le32 did;
__le32 rid;
__le32 cfgs1;
__le32 cfgs2;
__le32 cmd;
__le32 sts;
__le32 state;
__le32 different1;
__le32 ien;
__le32 ivect;
__le32 tmr;
__le32 simulate;
__le32 adpbc_sts;
__le32 adp_ramp_time;
__le32 adpbc_ctrl1;
__le32 adpbc_ctrl2;
__le32 override;
__le32 vbusvalid_dbnc_cfg;
__le32 sessvalid_dbnc_cfg;
__le32 susp_timing_ctrl;
};
#define OTG_CDNSP_DID 0x0004034E
/*
* Common registers interface for both CDNS3 and CDNSP version of DRD.
*/
struct cdns_otg_common_regs {
__le32 cmd;
__le32 sts;
__le32 state;
};
/*
* Interrupt related registers. This registers are mapped in different
* location for CDNSP controller.
*/
struct cdns_otg_irq_regs {
__le32 ien;
__le32 ivect;
};
@ -92,9 +122,9 @@ struct cdns3_otg_common_regs {
#define OTGCMD_DEV_BUS_DROP BIT(8)
/* Drop the bus for Host mode*/
#define OTGCMD_HOST_BUS_DROP BIT(9)
/* Power Down USBSS-DEV. */
/* Power Down USBSS-DEV - only for CDNS3.*/
#define OTGCMD_DEV_POWER_OFF BIT(11)
/* Power Down CDNSXHCI. */
/* Power Down CDNSXHCI - only for CDNS3. */
#define OTGCMD_HOST_POWER_OFF BIT(12)
/* OTGIEN - bitmasks */
@ -123,20 +153,31 @@ struct cdns3_otg_common_regs {
#define OTGSTS_OTG_NRDY_MASK BIT(11)
#define OTGSTS_OTG_NRDY(p) ((p) & OTGSTS_OTG_NRDY_MASK)
/*
* Value of the strap pins.
* Value of the strap pins for:
* CDNS3:
* 000 - no default configuration
* 010 - Controller initiall configured as Host
* 100 - Controller initially configured as Device
* CDNSP:
* 000 - No default configuration.
* 010 - Controller initiall configured as Host.
* 100 - Controller initially configured as Device.
*/
#define OTGSTS_STRAP(p) (((p) & GENMASK(14, 12)) >> 12)
#define OTGSTS_STRAP_NO_DEFAULT_CFG 0x00
#define OTGSTS_STRAP_HOST_OTG 0x01
#define OTGSTS_STRAP_HOST 0x02
#define OTGSTS_STRAP_GADGET 0x04
#define OTGSTS_CDNSP_STRAP_HOST 0x01
#define OTGSTS_CDNSP_STRAP_GADGET 0x02
/* Host mode is turned on. */
#define OTGSTS_XHCI_READY BIT(26)
#define OTGSTS_CDNS3_XHCI_READY BIT(26)
#define OTGSTS_CDNSP_XHCI_READY BIT(27)
/* "Device mode is turned on .*/
#define OTGSTS_DEV_READY BIT(27)
#define OTGSTS_CDNS3_DEV_READY BIT(27)
#define OTGSTS_CDNSP_DEV_READY BIT(26)
/* OTGSTATE- bitmasks */
#define OTGSTATE_DEV_STATE_MASK GENMASK(2, 0)
@ -152,6 +193,8 @@ struct cdns3_otg_common_regs {
#define OVERRIDE_IDPULLUP BIT(0)
/* Only for CDNS3_CONTROLLER_V0 version */
#define OVERRIDE_IDPULLUP_V0 BIT(24)
/* Vbusvalid/Sesvalid override select. */
#define OVERRIDE_SESS_VLD_SEL BIT(10)
/* PHYRST_CFG - bitmasks */
#define PHYRST_CFG_PHYRST_A_ENABLE BIT(0)
@ -159,17 +202,18 @@ struct cdns3_otg_common_regs {
#define CDNS3_ID_PERIPHERAL 1
#define CDNS3_ID_HOST 0
bool cdns3_is_host(struct cdns3 *cdns);
bool cdns3_is_device(struct cdns3 *cdns);
int cdns3_get_id(struct cdns3 *cdns);
int cdns3_get_vbus(struct cdns3 *cdns);
int cdns3_drd_init(struct cdns3 *cdns);
int cdns3_drd_exit(struct cdns3 *cdns);
int cdns3_drd_update_mode(struct cdns3 *cdns);
int cdns3_drd_gadget_on(struct cdns3 *cdns);
void cdns3_drd_gadget_off(struct cdns3 *cdns);
int cdns3_drd_host_on(struct cdns3 *cdns);
void cdns3_drd_host_off(struct cdns3 *cdns);
int cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode);
bool cdns_is_host(struct cdns *cdns);
bool cdns_is_device(struct cdns *cdns);
int cdns_get_id(struct cdns *cdns);
int cdns_get_vbus(struct cdns *cdns);
void cdns_clear_vbus(struct cdns *cdns);
void cdns_set_vbus(struct cdns *cdns);
int cdns_drd_init(struct cdns *cdns);
int cdns_drd_exit(struct cdns *cdns);
int cdns_drd_update_mode(struct cdns *cdns);
int cdns_drd_gadget_on(struct cdns *cdns);
void cdns_drd_gadget_off(struct cdns *cdns);
int cdns_drd_host_on(struct cdns *cdns);
void cdns_drd_host_off(struct cdns *cdns);
#endif /* __LINUX_CDNS3_DRD */

View File

@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Cadence USBSS DRD Driver - Gadget Export APIs.
* Cadence USBSS and USBSSP DRD Driver - Gadget Export APIs.
*
* Copyright (C) 2017 NXP
* Copyright (C) 2017-2018 NXP
@ -10,16 +10,28 @@
#ifndef __LINUX_CDNS3_GADGET_EXPORT
#define __LINUX_CDNS3_GADGET_EXPORT
#ifdef CONFIG_USB_CDNS3_GADGET
#if IS_ENABLED(CONFIG_USB_CDNSP_GADGET)
int cdns3_gadget_init(struct cdns3 *cdns);
int cdnsp_gadget_init(struct cdns *cdns);
#else
static inline int cdns3_gadget_init(struct cdns3 *cdns)
static inline int cdnsp_gadget_init(struct cdns *cdns)
{
return -ENXIO;
}
#endif
#endif /* CONFIG_USB_CDNSP_GADGET */
#if IS_ENABLED(CONFIG_USB_CDNS3_GADGET)
int cdns3_gadget_init(struct cdns *cdns);
#else
static inline int cdns3_gadget_init(struct cdns *cdns)
{
return -ENXIO;
}
#endif /* CONFIG_USB_CDNS3_GADGET */
#endif /* __LINUX_CDNS3_GADGET_EXPORT */

View File

@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Cadence USBSS DRD Driver - Host Export APIs
* Cadence USBSS and USBSSP DRD Driver - Host Export APIs
*
* Copyright (C) 2017-2018 NXP
*
@ -9,25 +9,19 @@
#ifndef __LINUX_CDNS3_HOST_EXPORT
#define __LINUX_CDNS3_HOST_EXPORT
struct usb_hcd;
#ifdef CONFIG_USB_CDNS3_HOST
#if IS_ENABLED(CONFIG_USB_CDNS_HOST)
int cdns3_host_init(struct cdns3 *cdns);
int xhci_cdns3_suspend_quirk(struct usb_hcd *hcd);
int cdns_host_init(struct cdns *cdns);
#else
static inline int cdns3_host_init(struct cdns3 *cdns)
static inline int cdns_host_init(struct cdns *cdns)
{
return -ENXIO;
}
static inline void cdns3_host_exit(struct cdns3 *cdns) { }
static inline int xhci_cdns3_suspend_quirk(struct usb_hcd *hcd)
{
return 0;
}
static inline void cdns_host_exit(struct cdns *cdns) { }
#endif /* CONFIG_USB_CDNS3_HOST */
#endif /* USB_CDNS_HOST */
#endif /* __LINUX_CDNS3_HOST_EXPORT */

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Cadence USBSS DRD Driver - host side
* Cadence USBSS and USBSSP DRD Driver - host side
*
* Copyright (C) 2018-2019 Cadence Design Systems.
* Copyright (C) 2017-2018 NXP
@ -23,18 +23,20 @@
#define CFG_RXDET_P3_EN BIT(15)
#define LPM_2_STB_SWITCH_EN BIT(25)
static int xhci_cdns3_suspend_quirk(struct usb_hcd *hcd);
static const struct xhci_plat_priv xhci_plat_cdns3_xhci = {
.quirks = XHCI_SKIP_PHY_INIT | XHCI_AVOID_BEI,
.suspend_quirk = xhci_cdns3_suspend_quirk,
};
static int __cdns3_host_init(struct cdns3 *cdns)
static int __cdns_host_init(struct cdns *cdns)
{
struct platform_device *xhci;
int ret;
struct usb_hcd *hcd;
cdns3_drd_host_on(cdns);
cdns_drd_host_on(cdns);
xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
if (!xhci) {
@ -46,7 +48,7 @@ static int __cdns3_host_init(struct cdns3 *cdns)
cdns->host_dev = xhci;
ret = platform_device_add_resources(xhci, cdns->xhci_res,
CDNS3_XHCI_RESOURCES_NUM);
CDNS_XHCI_RESOURCES_NUM);
if (ret) {
dev_err(cdns->dev, "couldn't add resources to xHCI device\n");
goto err1;
@ -87,7 +89,7 @@ err1:
return ret;
}
int xhci_cdns3_suspend_quirk(struct usb_hcd *hcd)
static int xhci_cdns3_suspend_quirk(struct usb_hcd *hcd)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
u32 value;
@ -113,25 +115,25 @@ int xhci_cdns3_suspend_quirk(struct usb_hcd *hcd)
return 0;
}
static void cdns3_host_exit(struct cdns3 *cdns)
static void cdns_host_exit(struct cdns *cdns)
{
kfree(cdns->xhci_plat_data);
platform_device_unregister(cdns->host_dev);
cdns->host_dev = NULL;
cdns3_drd_host_off(cdns);
cdns_drd_host_off(cdns);
}
int cdns3_host_init(struct cdns3 *cdns)
int cdns_host_init(struct cdns *cdns)
{
struct cdns3_role_driver *rdrv;
struct cdns_role_driver *rdrv;
rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL);
if (!rdrv)
return -ENOMEM;
rdrv->start = __cdns3_host_init;
rdrv->stop = cdns3_host_exit;
rdrv->state = CDNS3_ROLE_STATE_INACTIVE;
rdrv->start = __cdns_host_init;
rdrv->stop = cdns_host_exit;
rdrv->state = CDNS_ROLE_STATE_INACTIVE;
rdrv->name = "host";
cdns->roles[USB_ROLE_HOST] = rdrv;

View File

@ -53,9 +53,8 @@ config USB_CHIPIDEA_GENERIC
default USB_CHIPIDEA
config USB_CHIPIDEA_TEGRA
tristate "Enable Tegra UDC glue driver" if EMBEDDED
tristate "Enable Tegra USB glue driver" if EMBEDDED
depends on OF
depends on USB_CHIPIDEA_UDC
default USB_CHIPIDEA
endif

View File

@ -4,57 +4,278 @@
*/
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/reset.h>
#include <linux/usb.h>
#include <linux/usb/chipidea.h>
#include <linux/usb/hcd.h>
#include <linux/usb/of.h>
#include <linux/usb/phy.h>
#include "../host/ehci.h"
#include "ci.h"
struct tegra_udc {
struct tegra_usb {
struct ci_hdrc_platform_data data;
struct platform_device *dev;
const struct tegra_usb_soc_info *soc;
struct usb_phy *phy;
struct clk *clk;
bool needs_double_reset;
};
struct tegra_udc_soc_info {
struct tegra_usb_soc_info {
unsigned long flags;
unsigned int txfifothresh;
enum usb_dr_mode dr_mode;
};
static const struct tegra_udc_soc_info tegra_udc_soc_info = {
.flags = CI_HDRC_REQUIRES_ALIGNED_DMA,
static const struct tegra_usb_soc_info tegra20_ehci_soc_info = {
.flags = CI_HDRC_REQUIRES_ALIGNED_DMA |
CI_HDRC_OVERRIDE_PHY_CONTROL |
CI_HDRC_SUPPORTS_RUNTIME_PM,
.dr_mode = USB_DR_MODE_HOST,
.txfifothresh = 10,
};
static const struct of_device_id tegra_udc_of_match[] = {
static const struct tegra_usb_soc_info tegra30_ehci_soc_info = {
.flags = CI_HDRC_REQUIRES_ALIGNED_DMA |
CI_HDRC_OVERRIDE_PHY_CONTROL |
CI_HDRC_SUPPORTS_RUNTIME_PM,
.dr_mode = USB_DR_MODE_HOST,
.txfifothresh = 16,
};
static const struct tegra_usb_soc_info tegra20_udc_soc_info = {
.flags = CI_HDRC_REQUIRES_ALIGNED_DMA |
CI_HDRC_OVERRIDE_PHY_CONTROL |
CI_HDRC_SUPPORTS_RUNTIME_PM,
.dr_mode = USB_DR_MODE_UNKNOWN,
.txfifothresh = 10,
};
static const struct tegra_usb_soc_info tegra30_udc_soc_info = {
.flags = CI_HDRC_REQUIRES_ALIGNED_DMA |
CI_HDRC_OVERRIDE_PHY_CONTROL |
CI_HDRC_SUPPORTS_RUNTIME_PM,
.dr_mode = USB_DR_MODE_UNKNOWN,
.txfifothresh = 16,
};
static const struct of_device_id tegra_usb_of_match[] = {
{
.compatible = "nvidia,tegra20-ehci",
.data = &tegra20_ehci_soc_info,
}, {
.compatible = "nvidia,tegra30-ehci",
.data = &tegra30_ehci_soc_info,
}, {
.compatible = "nvidia,tegra20-udc",
.data = &tegra_udc_soc_info,
.data = &tegra20_udc_soc_info,
}, {
.compatible = "nvidia,tegra30-udc",
.data = &tegra_udc_soc_info,
.data = &tegra30_udc_soc_info,
}, {
.compatible = "nvidia,tegra114-udc",
.data = &tegra_udc_soc_info,
.data = &tegra30_udc_soc_info,
}, {
.compatible = "nvidia,tegra124-udc",
.data = &tegra_udc_soc_info,
.data = &tegra30_udc_soc_info,
}, {
/* sentinel */
}
};
MODULE_DEVICE_TABLE(of, tegra_udc_of_match);
MODULE_DEVICE_TABLE(of, tegra_usb_of_match);
static int tegra_udc_probe(struct platform_device *pdev)
static int tegra_usb_reset_controller(struct device *dev)
{
const struct tegra_udc_soc_info *soc;
struct tegra_udc *udc;
struct reset_control *rst, *rst_utmi;
struct device_node *phy_np;
int err;
udc = devm_kzalloc(&pdev->dev, sizeof(*udc), GFP_KERNEL);
if (!udc)
rst = devm_reset_control_get_shared(dev, "usb");
if (IS_ERR(rst)) {
dev_err(dev, "can't get ehci reset: %pe\n", rst);
return PTR_ERR(rst);
}
phy_np = of_parse_phandle(dev->of_node, "nvidia,phy", 0);
if (!phy_np)
return -ENOENT;
/*
* The 1st USB controller contains some UTMI pad registers that are
* global for all the controllers on the chip. Those registers are
* also cleared when reset is asserted to the 1st controller.
*/
rst_utmi = of_reset_control_get_shared(phy_np, "utmi-pads");
if (IS_ERR(rst_utmi)) {
dev_warn(dev, "can't get utmi-pads reset from the PHY\n");
dev_warn(dev, "continuing, but please update your DT\n");
} else {
/*
* PHY driver performs UTMI-pads reset in a case of a
* non-legacy DT.
*/
reset_control_put(rst_utmi);
}
of_node_put(phy_np);
/* reset control is shared, hence initialize it first */
err = reset_control_deassert(rst);
if (err)
return err;
err = reset_control_assert(rst);
if (err)
return err;
udelay(1);
err = reset_control_deassert(rst);
if (err)
return err;
return 0;
}
static int tegra_usb_notify_event(struct ci_hdrc *ci, unsigned int event)
{
struct tegra_usb *usb = dev_get_drvdata(ci->dev->parent);
struct ehci_hcd *ehci;
switch (event) {
case CI_HDRC_CONTROLLER_RESET_EVENT:
if (ci->hcd) {
ehci = hcd_to_ehci(ci->hcd);
ehci->has_tdi_phy_lpm = false;
ehci_writel(ehci, usb->soc->txfifothresh << 16,
&ehci->regs->txfill_tuning);
}
break;
}
return 0;
}
static int tegra_usb_internal_port_reset(struct ehci_hcd *ehci,
u32 __iomem *portsc_reg,
unsigned long *flags)
{
u32 saved_usbintr, temp;
unsigned int i, tries;
int retval = 0;
saved_usbintr = ehci_readl(ehci, &ehci->regs->intr_enable);
/* disable USB interrupt */
ehci_writel(ehci, 0, &ehci->regs->intr_enable);
spin_unlock_irqrestore(&ehci->lock, *flags);
/*
* Here we have to do Port Reset at most twice for
* Port Enable bit to be set.
*/
for (i = 0; i < 2; i++) {
temp = ehci_readl(ehci, portsc_reg);
temp |= PORT_RESET;
ehci_writel(ehci, temp, portsc_reg);
fsleep(10000);
temp &= ~PORT_RESET;
ehci_writel(ehci, temp, portsc_reg);
fsleep(1000);
tries = 100;
do {
fsleep(1000);
/*
* Up to this point, Port Enable bit is
* expected to be set after 2 ms waiting.
* USB1 usually takes extra 45 ms, for safety,
* we take 100 ms as timeout.
*/
temp = ehci_readl(ehci, portsc_reg);
} while (!(temp & PORT_PE) && tries--);
if (temp & PORT_PE)
break;
}
if (i == 2)
retval = -ETIMEDOUT;
/*
* Clear Connect Status Change bit if it's set.
* We can't clear PORT_PEC. It will also cause PORT_PE to be cleared.
*/
if (temp & PORT_CSC)
ehci_writel(ehci, PORT_CSC, portsc_reg);
/*
* Write to clear any interrupt status bits that might be set
* during port reset.
*/
temp = ehci_readl(ehci, &ehci->regs->status);
ehci_writel(ehci, temp, &ehci->regs->status);
/* restore original interrupt-enable bits */
spin_lock_irqsave(&ehci->lock, *flags);
ehci_writel(ehci, saved_usbintr, &ehci->regs->intr_enable);
return retval;
}
static int tegra_ehci_hub_control(struct ci_hdrc *ci, u16 typeReq, u16 wValue,
u16 wIndex, char *buf, u16 wLength,
bool *done, unsigned long *flags)
{
struct tegra_usb *usb = dev_get_drvdata(ci->dev->parent);
struct ehci_hcd *ehci = hcd_to_ehci(ci->hcd);
u32 __iomem *status_reg;
int retval = 0;
status_reg = &ehci->regs->port_status[(wIndex & 0xff) - 1];
switch (typeReq) {
case SetPortFeature:
if (wValue != USB_PORT_FEAT_RESET || !usb->needs_double_reset)
break;
/* for USB1 port we need to issue Port Reset twice internally */
retval = tegra_usb_internal_port_reset(ehci, status_reg, flags);
*done = true;
break;
}
return retval;
}
static void tegra_usb_enter_lpm(struct ci_hdrc *ci, bool enable)
{
/*
* Touching any register which belongs to AHB clock domain will
* hang CPU if USB controller is put into low power mode because
* AHB USB clock is gated on Tegra in the LPM.
*
* Tegra PHY has a separate register for checking the clock status
* and usb_phy_set_suspend() takes care of gating/ungating the clocks
* and restoring the PHY state on Tegra. Hence DEVLC/PORTSC registers
* shouldn't be touched directly by the CI driver.
*/
usb_phy_set_suspend(ci->usb_phy, enable);
}
static int tegra_usb_probe(struct platform_device *pdev)
{
const struct tegra_usb_soc_info *soc;
struct tegra_usb *usb;
int err;
usb = devm_kzalloc(&pdev->dev, sizeof(*usb), GFP_KERNEL);
if (!usb)
return -ENOMEM;
soc = of_device_get_match_data(&pdev->dev);
@ -63,70 +284,99 @@ static int tegra_udc_probe(struct platform_device *pdev)
return -EINVAL;
}
udc->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "nvidia,phy", 0);
if (IS_ERR(udc->phy)) {
err = PTR_ERR(udc->phy);
usb->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "nvidia,phy", 0);
if (IS_ERR(usb->phy)) {
err = PTR_ERR(usb->phy);
dev_err(&pdev->dev, "failed to get PHY: %d\n", err);
return err;
}
udc->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(udc->clk)) {
err = PTR_ERR(udc->clk);
usb->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(usb->clk)) {
err = PTR_ERR(usb->clk);
dev_err(&pdev->dev, "failed to get clock: %d\n", err);
return err;
}
err = clk_prepare_enable(udc->clk);
err = clk_prepare_enable(usb->clk);
if (err < 0) {
dev_err(&pdev->dev, "failed to enable clock: %d\n", err);
return err;
}
/* setup and register ChipIdea HDRC device */
udc->data.name = "tegra-udc";
udc->data.flags = soc->flags;
udc->data.usb_phy = udc->phy;
udc->data.capoffset = DEF_CAPOFFSET;
if (device_property_present(&pdev->dev, "nvidia,needs-double-reset"))
usb->needs_double_reset = true;
udc->dev = ci_hdrc_add_device(&pdev->dev, pdev->resource,
pdev->num_resources, &udc->data);
if (IS_ERR(udc->dev)) {
err = PTR_ERR(udc->dev);
dev_err(&pdev->dev, "failed to add HDRC device: %d\n", err);
err = tegra_usb_reset_controller(&pdev->dev);
if (err) {
dev_err(&pdev->dev, "failed to reset controller: %d\n", err);
goto fail_power_off;
}
platform_set_drvdata(pdev, udc);
/*
* USB controller registers shouldn't be touched before PHY is
* initialized, otherwise CPU will hang because clocks are gated.
* PHY driver controls gating of internal USB clocks on Tegra.
*/
err = usb_phy_init(usb->phy);
if (err)
goto fail_power_off;
platform_set_drvdata(pdev, usb);
/* setup and register ChipIdea HDRC device */
usb->soc = soc;
usb->data.name = "tegra-usb";
usb->data.flags = soc->flags;
usb->data.usb_phy = usb->phy;
usb->data.dr_mode = soc->dr_mode;
usb->data.capoffset = DEF_CAPOFFSET;
usb->data.enter_lpm = tegra_usb_enter_lpm;
usb->data.hub_control = tegra_ehci_hub_control;
usb->data.notify_event = tegra_usb_notify_event;
/* Tegra PHY driver currently doesn't support LPM for ULPI */
if (of_usb_get_phy_mode(pdev->dev.of_node) == USBPHY_INTERFACE_MODE_ULPI)
usb->data.flags &= ~CI_HDRC_SUPPORTS_RUNTIME_PM;
usb->dev = ci_hdrc_add_device(&pdev->dev, pdev->resource,
pdev->num_resources, &usb->data);
if (IS_ERR(usb->dev)) {
err = PTR_ERR(usb->dev);
dev_err(&pdev->dev, "failed to add HDRC device: %d\n", err);
goto phy_shutdown;
}
return 0;
phy_shutdown:
usb_phy_shutdown(usb->phy);
fail_power_off:
clk_disable_unprepare(udc->clk);
clk_disable_unprepare(usb->clk);
return err;
}
static int tegra_udc_remove(struct platform_device *pdev)
static int tegra_usb_remove(struct platform_device *pdev)
{
struct tegra_udc *udc = platform_get_drvdata(pdev);
struct tegra_usb *usb = platform_get_drvdata(pdev);
ci_hdrc_remove_device(udc->dev);
clk_disable_unprepare(udc->clk);
ci_hdrc_remove_device(usb->dev);
usb_phy_shutdown(usb->phy);
clk_disable_unprepare(usb->clk);
return 0;
}
static struct platform_driver tegra_udc_driver = {
static struct platform_driver tegra_usb_driver = {
.driver = {
.name = "tegra-udc",
.of_match_table = tegra_udc_of_match,
.name = "tegra-usb",
.of_match_table = tegra_usb_of_match,
},
.probe = tegra_udc_probe,
.remove = tegra_udc_remove,
.probe = tegra_usb_probe,
.remove = tegra_usb_remove,
};
module_platform_driver(tegra_udc_driver);
module_platform_driver(tegra_usb_driver);
MODULE_DESCRIPTION("NVIDIA Tegra USB device mode driver");
MODULE_DESCRIPTION("NVIDIA Tegra USB driver");
MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
MODULE_ALIAS("platform:tegra-udc");
MODULE_LICENSE("GPL v2");

View File

@ -195,7 +195,7 @@ static void hw_wait_phy_stable(void)
}
/* The PHY enters/leaves low power mode */
static void ci_hdrc_enter_lpm(struct ci_hdrc *ci, bool enable)
static void ci_hdrc_enter_lpm_common(struct ci_hdrc *ci, bool enable)
{
enum ci_hw_regs reg = ci->hw_bank.lpm ? OP_DEVLC : OP_PORTSC;
bool lpm = !!(hw_read(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm)));
@ -208,6 +208,11 @@ static void ci_hdrc_enter_lpm(struct ci_hdrc *ci, bool enable)
0);
}
static void ci_hdrc_enter_lpm(struct ci_hdrc *ci, bool enable)
{
return ci->platdata->enter_lpm(ci, enable);
}
static int hw_device_init(struct ci_hdrc *ci, void __iomem *base)
{
u32 reg;
@ -790,6 +795,9 @@ static int ci_get_platdata(struct device *dev,
platdata->pins_device = p;
}
if (!platdata->enter_lpm)
platdata->enter_lpm = ci_hdrc_enter_lpm_common;
return 0;
}

View File

@ -29,6 +29,12 @@ struct ehci_ci_priv {
bool enabled;
};
struct ci_hdrc_dma_aligned_buffer {
void *kmalloc_ptr;
void *old_xfer_buffer;
u8 data[0];
};
static int ehci_ci_portpower(struct usb_hcd *hcd, int portnum, bool enable)
{
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
@ -160,14 +166,15 @@ static int host_start(struct ci_hdrc *ci)
pinctrl_select_state(ci->platdata->pctl,
ci->platdata->pins_host);
ci->hcd = hcd;
ret = usb_add_hcd(hcd, 0, 0);
if (ret) {
ci->hcd = NULL;
goto disable_reg;
} else {
struct usb_otg *otg = &ci->otg;
ci->hcd = hcd;
if (ci_otg_is_fsm_mode(ci)) {
otg->host = &hcd->self;
hcd->self.otg_port = 1;
@ -237,6 +244,7 @@ static int ci_ehci_hub_control(
u32 temp;
unsigned long flags;
int retval = 0;
bool done = false;
struct device *dev = hcd->self.controller;
struct ci_hdrc *ci = dev_get_drvdata(dev);
@ -244,6 +252,13 @@ static int ci_ehci_hub_control(
spin_lock_irqsave(&ehci->lock, flags);
if (ci->platdata->hub_control) {
retval = ci->platdata->hub_control(ci, typeReq, wValue, wIndex,
buf, wLength, &done, &flags);
if (done)
goto done;
}
if (typeReq == SetPortFeature && wValue == USB_PORT_FEAT_SUSPEND) {
temp = ehci_readl(ehci, status_reg);
if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) != 0) {
@ -349,6 +364,86 @@ static int ci_ehci_bus_suspend(struct usb_hcd *hcd)
return 0;
}
static void ci_hdrc_free_dma_aligned_buffer(struct urb *urb)
{
struct ci_hdrc_dma_aligned_buffer *temp;
size_t length;
if (!(urb->transfer_flags & URB_ALIGNED_TEMP_BUFFER))
return;
temp = container_of(urb->transfer_buffer,
struct ci_hdrc_dma_aligned_buffer, data);
if (usb_urb_dir_in(urb)) {
if (usb_pipeisoc(urb->pipe))
length = urb->transfer_buffer_length;
else
length = urb->actual_length;
memcpy(temp->old_xfer_buffer, temp->data, length);
}
urb->transfer_buffer = temp->old_xfer_buffer;
kfree(temp->kmalloc_ptr);
urb->transfer_flags &= ~URB_ALIGNED_TEMP_BUFFER;
}
static int ci_hdrc_alloc_dma_aligned_buffer(struct urb *urb, gfp_t mem_flags)
{
struct ci_hdrc_dma_aligned_buffer *temp, *kmalloc_ptr;
const unsigned int ci_hdrc_usb_dma_align = 32;
size_t kmalloc_size;
if (urb->num_sgs || urb->sg || urb->transfer_buffer_length == 0 ||
!((uintptr_t)urb->transfer_buffer & (ci_hdrc_usb_dma_align - 1)))
return 0;
/* Allocate a buffer with enough padding for alignment */
kmalloc_size = urb->transfer_buffer_length +
sizeof(struct ci_hdrc_dma_aligned_buffer) +
ci_hdrc_usb_dma_align - 1;
kmalloc_ptr = kmalloc(kmalloc_size, mem_flags);
if (!kmalloc_ptr)
return -ENOMEM;
/* Position our struct dma_aligned_buffer such that data is aligned */
temp = PTR_ALIGN(kmalloc_ptr + 1, ci_hdrc_usb_dma_align) - 1;
temp->kmalloc_ptr = kmalloc_ptr;
temp->old_xfer_buffer = urb->transfer_buffer;
if (usb_urb_dir_out(urb))
memcpy(temp->data, urb->transfer_buffer,
urb->transfer_buffer_length);
urb->transfer_buffer = temp->data;
urb->transfer_flags |= URB_ALIGNED_TEMP_BUFFER;
return 0;
}
static int ci_hdrc_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
gfp_t mem_flags)
{
int ret;
ret = ci_hdrc_alloc_dma_aligned_buffer(urb, mem_flags);
if (ret)
return ret;
ret = usb_hcd_map_urb_for_dma(hcd, urb, mem_flags);
if (ret)
ci_hdrc_free_dma_aligned_buffer(urb);
return ret;
}
static void ci_hdrc_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
{
usb_hcd_unmap_urb_for_dma(hcd, urb);
ci_hdrc_free_dma_aligned_buffer(urb);
}
int ci_hdrc_host_init(struct ci_hdrc *ci)
{
struct ci_role_driver *rdrv;
@ -366,6 +461,11 @@ int ci_hdrc_host_init(struct ci_hdrc *ci)
rdrv->name = "host";
ci->roles[CI_ROLE_HOST] = rdrv;
if (ci->platdata->flags & CI_HDRC_REQUIRES_ALIGNED_DMA) {
ci_ehci_hc_driver.map_urb_for_dma = ci_hdrc_map_urb_for_dma;
ci_ehci_hc_driver.unmap_urb_for_dma = ci_hdrc_unmap_urb_for_dma;
}
return 0;
}

View File

@ -1901,6 +1901,12 @@ static const struct usb_device_id acm_ids[] = {
},
#endif
#if IS_ENABLED(CONFIG_USB_SERIAL_XR)
{ USB_DEVICE(0x04e2, 0x1410), /* Ignore XR21V141X USB to Serial converter */
.driver_info = IGNORE_DEVICE,
},
#endif
/*Samsung phone in firmware update mode */
{ USB_DEVICE(0x04e8, 0x685d),
.driver_info = IGNORE_DEVICE,

View File

@ -25,7 +25,7 @@
/* Increment API VERSION when changing tmc.h with new flags or ioctls
* or when changing a significant behavior of the driver.
*/
#define USBTMC_API_VERSION (2)
#define USBTMC_API_VERSION (3)
#define USBTMC_HEADER_SIZE 12
#define USBTMC_MINOR_BASE 176
@ -475,33 +475,17 @@ static int usbtmc_ioctl_abort_bulk_out(struct usbtmc_device_data *data)
return usbtmc_ioctl_abort_bulk_out_tag(data, data->bTag_last_write);
}
static int usbtmc488_ioctl_read_stb(struct usbtmc_file_data *file_data,
void __user *arg)
static int usbtmc_get_stb(struct usbtmc_file_data *file_data, __u8 *stb)
{
struct usbtmc_device_data *data = file_data->data;
struct device *dev = &data->intf->dev;
int srq_asserted = 0;
u8 *buffer;
u8 tag;
__u8 stb;
int rv;
dev_dbg(dev, "Enter ioctl_read_stb iin_ep_present: %d\n",
data->iin_ep_present);
spin_lock_irq(&data->dev_lock);
srq_asserted = atomic_xchg(&file_data->srq_asserted, srq_asserted);
if (srq_asserted) {
/* a STB with SRQ is already received */
stb = file_data->srq_byte;
spin_unlock_irq(&data->dev_lock);
rv = put_user(stb, (__u8 __user *)arg);
dev_dbg(dev, "stb:0x%02x with srq received %d\n",
(unsigned int)stb, rv);
return rv;
}
spin_unlock_irq(&data->dev_lock);
buffer = kmalloc(8, GFP_KERNEL);
if (!buffer)
return -ENOMEM;
@ -548,13 +532,12 @@ static int usbtmc488_ioctl_read_stb(struct usbtmc_file_data *file_data,
data->iin_bTag, tag);
}
stb = data->bNotify2;
*stb = data->bNotify2;
} else {
stb = buffer[2];
*stb = buffer[2];
}
rv = put_user(stb, (__u8 __user *)arg);
dev_dbg(dev, "stb:0x%02x received %d\n", (unsigned int)stb, rv);
dev_dbg(dev, "stb:0x%02x received %d\n", (unsigned int)*stb, rv);
exit:
/* bump interrupt bTag */
@ -567,6 +550,53 @@ static int usbtmc488_ioctl_read_stb(struct usbtmc_file_data *file_data,
return rv;
}
static int usbtmc488_ioctl_read_stb(struct usbtmc_file_data *file_data,
void __user *arg)
{
int srq_asserted = 0;
__u8 stb;
int rv;
rv = usbtmc_get_stb(file_data, &stb);
if (rv > 0) {
srq_asserted = atomic_xchg(&file_data->srq_asserted,
srq_asserted);
if (srq_asserted)
stb |= 0x40; /* Set RQS bit */
rv = put_user(stb, (__u8 __user *)arg);
}
return rv;
}
static int usbtmc_ioctl_get_srq_stb(struct usbtmc_file_data *file_data,
void __user *arg)
{
struct usbtmc_device_data *data = file_data->data;
struct device *dev = &data->intf->dev;
int srq_asserted = 0;
__u8 stb = 0;
int rv;
spin_lock_irq(&data->dev_lock);
srq_asserted = atomic_xchg(&file_data->srq_asserted, srq_asserted);
if (srq_asserted) {
stb = file_data->srq_byte;
spin_unlock_irq(&data->dev_lock);
rv = put_user(stb, (__u8 __user *)arg);
} else {
spin_unlock_irq(&data->dev_lock);
rv = -ENOMSG;
}
dev_dbg(dev, "stb:0x%02x with srq received %d\n", (unsigned int)stb, rv);
return rv;
}
static int usbtmc488_ioctl_wait_srq(struct usbtmc_file_data *file_data,
__u32 __user *arg)
{
@ -2145,6 +2175,17 @@ static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
file_data->auto_abort = !!tmp_byte;
break;
case USBTMC_IOCTL_GET_STB:
retval = usbtmc_get_stb(file_data, &tmp_byte);
if (retval > 0)
retval = put_user(tmp_byte, (__u8 __user *)arg);
break;
case USBTMC_IOCTL_GET_SRQ_STB:
retval = usbtmc_ioctl_get_srq_stb(file_data,
(void __user *)arg);
break;
case USBTMC_IOCTL_CANCEL_IO:
retval = usbtmc_ioctl_cancel_io(file_data);
break;

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