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

Pull USB/Thunderbolt updates from Greg KH:
 "Here is the big set of USB and Thunderbolt driver updates for
  6.19-rc1. Nothing major here, just lots of tiny updates for most of
  the common USB drivers. Included in here are:

   - more xhci driver updates and fixes

   - Thunderbolt driver cleanups

   - usb serial driver updates

   - typec driver updates

   - USB tracepoint additions

   - dwc3 driver updates, including support for Apple hardware

   - lots of other smaller driver updates and cleanups

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

* tag 'usb-6.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (161 commits)
  usb: gadget: tegra-xudc: Always reinitialize data toggle when clear halt
  USB: serial: option: move Telit 0x10c7 composition in the right place
  USB: serial: option: add Telit Cinterion FE910C04 new compositions
  usb: typec: ucsi: fix use-after-free caused by uec->work
  usb: typec: ucsi: fix probe failure in gaokun_ucsi_probe()
  usb: dwc3: core: Remove redundant comment in core init
  usb: phy: Initialize struct usb_phy list_head
  USB: serial: option: add Foxconn T99W760
  usb: usb-storage: No additional quirks need to be added to the EL-R12 optical drive.
  usb: typec: hd3ss3220: Enable VBUS based on ID pin state
  dt-bindings: usb: ti,hd3ss3220: Add support for VBUS based on ID state
  usb: typec: anx7411: add WQ_PERCPU to alloc_workqueue users
  USB: add WQ_PERCPU to alloc_workqueue users
  dt-bindings: usb: dwc3-xilinx: Describe the reset constraint for the versal platform
  drivers/usb/storage: use min() instead of min_t()
  usb: raw-gadget: cap raw_io transfer length to KMALLOC_MAX_SIZE
  usb: ohci-da8xx: remove unused platform data
  usb: gadget: functionfs: use dma_buf_unmap_attachment_unlocked() helper
  usb: uas: reduce time under spinlock
  usb: dwc3: eic7700: Add EIC7700 USB driver
  ...
This commit is contained in:
Linus Torvalds
2025-12-06 18:42:12 -08:00
139 changed files with 2943 additions and 1247 deletions

View File

@@ -254,3 +254,31 @@ Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Description: Description:
The PPS Power Limited bit indicates whether or not the source The PPS Power Limited bit indicates whether or not the source
supply will exceed the rated output power if requested. supply will exceed the rated output power if requested.
Standard Power Range (SPR) Adjustable Voltage Supplies
What: /sys/class/usb_power_delivery/.../<capability>/<position>:spr_adjustable_voltage_supply
Date: Oct 2025
Contact: Badhri Jagan Sridharan <badhri@google.com>
Description:
Adjustable Voltage Supply (AVS) Augmented PDO (APDO).
What: /sys/class/usb_power_delivery/.../<capability>/<position>:spr_adjustable_voltage_supply/maximum_current_9V_to_15V
Date: Oct 2025
Contact: Badhri Jagan Sridharan <badhri@google.com>
Description:
Maximum Current for 9V to 15V range in milliamperes.
What: /sys/class/usb_power_delivery/.../<capability>/<position>:spr_adjustable_voltage_supply/maximum_current_15V_to_20V
Date: Oct 2025
Contact: Badhri Jagan Sridharan <badhri@google.com>
Description:
Maximum Current for greater than 15V till 20V range in
milliamperes.
What: /sys/class/usb_power_delivery/.../<capability>/<position>:spr_adjustable_voltage_supply/peak_current
Date: Oct 2025
Contact: Badhri Jagan Sridharan <badhri@google.com>
Description:
This file shows the value of the Adjustable Voltage Supply Peak Current
Capability field.

View File

@@ -203,10 +203,10 @@ host controller or a device, it is important that the firmware can be
upgraded to the latest where possible bugs in it have been fixed. upgraded to the latest where possible bugs in it have been fixed.
Typically OEMs provide this firmware from their support site. Typically OEMs provide this firmware from their support site.
There is also a central site which has links where to download firmware Currently, recommended method of updating firmware is through "fwupd" tool.
for some machines: It uses LVFS (Linux Vendor Firmware Service) portal by default to get the
latest firmware from hardware vendors and updates connected devices if found
`Thunderbolt Updates <https://thunderbolttechnology.net/updates>`_ compatible. For details refer to: https://github.com/fwupd/fwupd.
Before you upgrade firmware on a device, host or retimer, please make Before you upgrade firmware on a device, host or retimer, please make
sure it is a suitable upgrade. Failing to do that may render the device sure it is a suitable upgrade. Failing to do that may render the device
@@ -215,18 +215,40 @@ tools!
Host NVM upgrade on Apple Macs is not supported. Host NVM upgrade on Apple Macs is not supported.
Once the NVM image has been downloaded, you need to plug in a Fwupd is installed by default. If you don't have it on your system, simply
Thunderbolt device so that the host controller appears. It does not use your distro package manager to get it.
matter which device is connected (unless you are upgrading NVM on a
device - then you need to connect that particular device). To see possible updates through fwupd, you need to plug in a Thunderbolt
device so that the host controller appears. It does not matter which
device is connected (unless you are upgrading NVM on a device - then you
need to connect that particular device).
Note an OEM-specific method to power the controller up ("force power") may Note an OEM-specific method to power the controller up ("force power") may
be available for your system in which case there is no need to plug in a be available for your system in which case there is no need to plug in a
Thunderbolt device. Thunderbolt device.
After that we can write the firmware to the non-active parts of the NVM Updating firmware using fwupd is straightforward - refer to official
of the host or device. As an example here is how Intel NUC6i7KYK (Skull readme on fwupd github.
Canyon) Thunderbolt controller NVM is upgraded::
If firmware image is written successfully, the device shortly disappears.
Once it comes back, the driver notices it and initiates a full power
cycle. After a while device appears again and this time it should be
fully functional.
Device of interest should display new version under "Current version"
and "Update State: Success" in fwupd's interface.
Upgrading firmware manually
---------------------------------------------------------------
If possible, use fwupd to updated the firmware. However, if your device OEM
has not uploaded the firmware to LVFS, but it is available for download
from their side, you can use method below to directly upgrade the
firmware.
Manual firmware update can be done with 'dd' tool. To update firmware
using this method, you need to write it to the non-active parts of NVM
of the host or device. Example on how to update Intel NUC6i7KYK
(Skull Canyon) Thunderbolt controller NVM::
# dd if=KYK_TBT_FW_0018.bin of=/sys/bus/thunderbolt/devices/0-0/nvm_non_active0/nvmem # dd if=KYK_TBT_FW_0018.bin of=/sys/bus/thunderbolt/devices/0-0/nvm_non_active0/nvmem
@@ -235,10 +257,8 @@ upgrade process as follows::
# echo 1 > /sys/bus/thunderbolt/devices/0-0/nvm_authenticate # echo 1 > /sys/bus/thunderbolt/devices/0-0/nvm_authenticate
If no errors are returned, the host controller shortly disappears. Once If no errors are returned, device should behave as described in previous
it comes back the driver notices it and initiates a full power cycle. section.
After a while the host controller appears again and this time it should
be fully functional.
We can verify that the new NVM firmware is active by running the following We can verify that the new NVM firmware is active by running the following
commands:: commands::

View File

@@ -0,0 +1,80 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/apple,dwc3.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Apple Silicon DWC3 USB controller
maintainers:
- Sven Peter <sven@kernel.org>
description:
Apple Silicon SoCs use a Synopsys DesignWare DWC3 based controller for each of
their Type-C ports.
allOf:
- $ref: snps,dwc3-common.yaml#
properties:
compatible:
oneOf:
- items:
- enum:
- apple,t6000-dwc3
- apple,t6020-dwc3
- apple,t8112-dwc3
- const: apple,t8103-dwc3
- const: apple,t8103-dwc3
reg:
items:
- description: Core DWC3 region
- description: Apple-specific DWC3 region
reg-names:
items:
- const: dwc3-core
- const: dwc3-apple
interrupts:
maxItems: 1
iommus:
maxItems: 2
resets:
maxItems: 1
power-domains:
maxItems: 1
required:
- compatible
- reg
- reg-names
- interrupts
- iommus
- resets
- power-domains
- usb-role-switch
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/apple-aic.h>
#include <dt-bindings/interrupt-controller/irq.h>
usb@82280000 {
compatible = "apple,t8103-dwc3";
reg = <0x82280000 0xcd00>, <0x8228cd00 0x3200>;
reg-names = "dwc3-core", "dwc3-apple";
interrupts = <AIC_IRQ 777 IRQ_TYPE_LEVEL_HIGH>;
iommus = <&dwc3_0_dart_0 0>, <&dwc3_0_dart_1 1>;
power-domains = <&ps_atc0_usb>;
resets = <&atcphy0>;
usb-role-switch;
};

View File

@@ -47,6 +47,7 @@ properties:
- const: ref_clk - const: ref_clk
resets: resets:
minItems: 1
description: description:
A list of phandles for resets listed in reset-names. A list of phandles for resets listed in reset-names.
@@ -56,6 +57,7 @@ properties:
- description: USB APB reset - description: USB APB reset
reset-names: reset-names:
minItems: 1
items: items:
- const: usb_crst - const: usb_crst
- const: usb_hibrst - const: usb_hibrst
@@ -95,6 +97,26 @@ required:
- resets - resets
- reset-names - reset-names
allOf:
- if:
properties:
compatible:
contains:
enum:
- xlnx,versal-dwc3
then:
properties:
resets:
maxItems: 1
reset-names:
maxItems: 1
else:
properties:
resets:
minItems: 3
reset-names:
minItems: 3
additionalProperties: false additionalProperties: false
examples: examples:

View File

@@ -0,0 +1,94 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/eswin,eic7700-usb.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: ESWIN EIC7700 SoC Usb Controller
maintainers:
- Wei Yang <yangwei1@eswincomputing.com>
- Senchuan Zhang <zhangsenchuan@eswincomputing.com>
- Hang Cao <caohang@eswincomputing.com>
description:
The Usb controller on EIC7700 SoC.
allOf:
- $ref: snps,dwc3-common.yaml#
properties:
compatible:
const: eswin,eic7700-dwc3
reg:
maxItems: 1
interrupts:
maxItems: 1
interrupt-names:
items:
- const: peripheral
clocks:
maxItems: 3
clock-names:
items:
- const: aclk
- const: cfg
- const: usb_en
resets:
maxItems: 2
reset-names:
items:
- const: vaux
- const: usb_rst
eswin,hsp-sp-csr:
description:
HSP CSR is to control and get status of different high-speed peripherals
(such as Ethernet, USB, SATA, etc.) via register, which can tune
board-level's parameters of PHY, etc.
$ref: /schemas/types.yaml#/definitions/phandle-array
items:
- items:
- description: phandle to HSP Register Controller hsp_sp_csr node.
- description: USB bus register offset.
- description: AXI low power register offset.
required:
- compatible
- reg
- clocks
- clock-names
- interrupts
- interrupt-names
- resets
- reset-names
- eswin,hsp-sp-csr
unevaluatedProperties: false
examples:
- |
usb@50480000 {
compatible = "eswin,eic7700-dwc3";
reg = <0x50480000 0x10000>;
clocks = <&clock 135>,
<&clock 136>,
<&hspcrg 18>;
clock-names = "aclk", "cfg", "usb_en";
interrupt-parent = <&plic>;
interrupts = <85>;
interrupt-names = "peripheral";
resets = <&reset 84>, <&hspcrg 2>;
reset-names = "vaux", "usb_rst";
dr_mode = "peripheral";
maximum-speed = "high-speed";
phy_type = "utmi";
eswin,hsp-sp-csr = <&hsp_sp_csr 0x800 0x818>;
};

View File

@@ -9,21 +9,19 @@ title: Freescale layerscape SuperSpeed DWC3 USB SoC controller
maintainers: maintainers:
- Frank Li <Frank.Li@nxp.com> - Frank Li <Frank.Li@nxp.com>
select:
properties:
compatible:
contains:
enum:
- fsl,ls1028a-dwc3
required:
- compatible
properties: properties:
compatible: compatible:
items: oneOf:
- enum: - items:
- fsl,ls1028a-dwc3 - enum:
- const: snps,dwc3 - fsl,ls1012a-dwc3
- fsl,ls1043a-dwc3
- fsl,ls1046a-dwc3
- fsl,ls1088a-dwc3
- fsl,ls208xa-dwc3
- fsl,lx2160a-dwc3
- const: fsl,ls1028a-dwc3
- const: fsl,ls1028a-dwc3
reg: reg:
maxItems: 1 maxItems: 1
@@ -31,6 +29,11 @@ properties:
interrupts: interrupts:
maxItems: 1 maxItems: 1
iommus:
maxItems: 1
dma-coherent: true
unevaluatedProperties: false unevaluatedProperties: false
required: required:
@@ -39,14 +42,14 @@ required:
- interrupts - interrupts
allOf: allOf:
- $ref: snps,dwc3.yaml# - $ref: snps,dwc3-common.yaml#
examples: examples:
- | - |
#include <dt-bindings/interrupt-controller/arm-gic.h> #include <dt-bindings/interrupt-controller/arm-gic.h>
usb@fe800000 { usb@fe800000 {
compatible = "fsl,ls1028a-dwc3", "snps,dwc3"; compatible = "fsl,ls1028a-dwc3";
reg = <0xfe800000 0x100000>; reg = <0xfe800000 0x100000>;
interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>; interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>;
}; };

View File

@@ -36,6 +36,7 @@ properties:
- fsl,imx8mm-usbmisc - fsl,imx8mm-usbmisc
- fsl,imx8mn-usbmisc - fsl,imx8mn-usbmisc
- fsl,imx8ulp-usbmisc - fsl,imx8ulp-usbmisc
- fsl,imx94-usbmisc
- fsl,imx95-usbmisc - fsl,imx95-usbmisc
- const: fsl,imx7d-usbmisc - const: fsl,imx7d-usbmisc
- const: fsl,imx6q-usbmisc - const: fsl,imx6q-usbmisc

View File

@@ -46,6 +46,7 @@ properties:
- aspeed,ast2400-ehci - aspeed,ast2400-ehci
- aspeed,ast2500-ehci - aspeed,ast2500-ehci
- aspeed,ast2600-ehci - aspeed,ast2600-ehci
- aspeed,ast2700-ehci
- brcm,bcm3384-ehci - brcm,bcm3384-ehci
- brcm,bcm63268-ehci - brcm,bcm63268-ehci
- brcm,bcm6328-ehci - brcm,bcm6328-ehci

View File

@@ -14,12 +14,15 @@ properties:
oneOf: oneOf:
- description: Generic xHCI device - description: Generic xHCI device
const: generic-xhci const: generic-xhci
- description: Armada 37xx/375/38x/8k SoCs - description: Armada 375/38x SoCs
items:
- enum:
- marvell,armada-375-xhci
- marvell,armada-380-xhci
- description: Armada 37xx/8k SoCs
items: items:
- enum: - enum:
- marvell,armada3700-xhci - marvell,armada3700-xhci
- marvell,armada-375-xhci
- marvell,armada-380-xhci
- marvell,armada-8k-xhci - marvell,armada-8k-xhci
- const: generic-xhci - const: generic-xhci
- description: Broadcom SoCs with power domains - description: Broadcom SoCs with power domains
@@ -53,6 +56,14 @@ properties:
dma-coherent: true dma-coherent: true
dr_mode:
enum:
- host
- otg
iommus:
maxItems: 1
power-domains: power-domains:
maxItems: 1 maxItems: 1

View File

@@ -34,6 +34,7 @@ properties:
- mediatek,mt8183-xhci - mediatek,mt8183-xhci
- mediatek,mt8186-xhci - mediatek,mt8186-xhci
- mediatek,mt8188-xhci - mediatek,mt8188-xhci
- mediatek,mt8189-xhci
- mediatek,mt8192-xhci - mediatek,mt8192-xhci
- mediatek,mt8195-xhci - mediatek,mt8195-xhci
- mediatek,mt8365-xhci - mediatek,mt8365-xhci
@@ -168,7 +169,8 @@ properties:
104 - used by mt8195, IP1, specific 1.04; 104 - used by mt8195, IP1, specific 1.04;
105 - used by mt8195, IP2, specific 1.05; 105 - used by mt8195, IP2, specific 1.05;
106 - used by mt8195, IP3, specific 1.06; 106 - used by mt8195, IP3, specific 1.06;
enum: [1, 2, 101, 102, 103, 104, 105, 106] 110 - used by mt8189, IP4, specific 1.10;
enum: [1, 2, 101, 102, 103, 104, 105, 106, 110]
mediatek,u3p-dis-msk: mediatek,u3p-dis-msk:
$ref: /schemas/types.yaml#/definitions/uint32 $ref: /schemas/types.yaml#/definitions/uint32

View File

@@ -24,6 +24,8 @@ properties:
compatible: compatible:
items: items:
- enum: - enum:
- qcom,glymur-dwc3
- qcom,glymur-dwc3-mp
- qcom,ipq4019-dwc3 - qcom,ipq4019-dwc3
- qcom,ipq5018-dwc3 - qcom,ipq5018-dwc3
- qcom,ipq5332-dwc3 - qcom,ipq5332-dwc3
@@ -32,6 +34,7 @@ properties:
- qcom,ipq8064-dwc3 - qcom,ipq8064-dwc3
- qcom,ipq8074-dwc3 - qcom,ipq8074-dwc3
- qcom,ipq9574-dwc3 - qcom,ipq9574-dwc3
- qcom,kaanapali-dwc3
- qcom,milos-dwc3 - qcom,milos-dwc3
- qcom,msm8953-dwc3 - qcom,msm8953-dwc3
- qcom,msm8994-dwc3 - qcom,msm8994-dwc3
@@ -67,6 +70,7 @@ properties:
- qcom,sm8450-dwc3 - qcom,sm8450-dwc3
- qcom,sm8550-dwc3 - qcom,sm8550-dwc3
- qcom,sm8650-dwc3 - qcom,sm8650-dwc3
- qcom,sm8750-dwc3
- qcom,x1e80100-dwc3 - qcom,x1e80100-dwc3
- qcom,x1e80100-dwc3-mp - qcom,x1e80100-dwc3-mp
- const: qcom,snps-dwc3 - const: qcom,snps-dwc3
@@ -200,6 +204,7 @@ allOf:
contains: contains:
enum: enum:
- qcom,ipq9574-dwc3 - qcom,ipq9574-dwc3
- qcom,kaanapali-dwc3
- qcom,msm8953-dwc3 - qcom,msm8953-dwc3
- qcom,msm8996-dwc3 - qcom,msm8996-dwc3
- qcom,msm8998-dwc3 - qcom,msm8998-dwc3
@@ -213,6 +218,7 @@ allOf:
- qcom,sdx65-dwc3 - qcom,sdx65-dwc3
- qcom,sdx75-dwc3 - qcom,sdx75-dwc3
- qcom,sm6350-dwc3 - qcom,sm6350-dwc3
- qcom,sm8750-dwc3
then: then:
properties: properties:
clocks: clocks:
@@ -387,6 +393,28 @@ allOf:
- const: mock_utmi - const: mock_utmi
- const: xo - const: xo
- if:
properties:
compatible:
contains:
enum:
- qcom,glymur-dwc3
- qcom,glymur-dwc3-mp
then:
properties:
clocks:
maxItems: 7
clock-names:
items:
- const: cfg_noc
- const: core
- const: iface
- const: sleep
- const: mock_utmi
- const: noc_aggr_north
- const: noc_aggr_south
- if: - if:
properties: properties:
compatible: compatible:
@@ -456,6 +484,7 @@ allOf:
compatible: compatible:
contains: contains:
enum: enum:
- qcom,glymur-dwc3
- qcom,milos-dwc3 - qcom,milos-dwc3
- qcom,x1e80100-dwc3 - qcom,x1e80100-dwc3
then: then:
@@ -479,6 +508,7 @@ allOf:
enum: enum:
- qcom,ipq4019-dwc3 - qcom,ipq4019-dwc3
- qcom,ipq8064-dwc3 - qcom,ipq8064-dwc3
- qcom,kaanapali-dwc3
- qcom,msm8994-dwc3 - qcom,msm8994-dwc3
- qcom,qcs615-dwc3 - qcom,qcs615-dwc3
- qcom,qcs8300-dwc3 - qcom,qcs8300-dwc3
@@ -501,6 +531,7 @@ allOf:
- qcom,sm8450-dwc3 - qcom,sm8450-dwc3
- qcom,sm8550-dwc3 - qcom,sm8550-dwc3
- qcom,sm8650-dwc3 - qcom,sm8650-dwc3
- qcom,sm8750-dwc3
then: then:
properties: properties:
interrupts: interrupts:
@@ -521,6 +552,7 @@ allOf:
compatible: compatible:
contains: contains:
enum: enum:
- qcom,glymur-dwc3-mp
- qcom,sc8180x-dwc3-mp - qcom,sc8180x-dwc3-mp
- qcom,x1e80100-dwc3-mp - qcom,x1e80100-dwc3-mp
then: then:

View File

@@ -4,14 +4,22 @@
$id: http://devicetree.org/schemas/usb/renesas,rzg3e-xhci.yaml# $id: http://devicetree.org/schemas/usb/renesas,rzg3e-xhci.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Renesas RZ/G3E USB 3.2 Gen2 Host controller title: Renesas USB 3.2 Gen2 Host controller
maintainers: maintainers:
- Biju Das <biju.das.jz@bp.renesas.com> - Biju Das <biju.das.jz@bp.renesas.com>
properties: properties:
compatible: compatible:
const: renesas,r9a09g047-xhci oneOf:
- items:
- enum:
- renesas,r9a09g056-xhci # RZ/V2N
- renesas,r9a09g057-xhci # RZ/V2H(P)
- const: renesas,r9a09g047-xhci
- items:
- const: renesas,r9a09g047-xhci # RZ/G3E
reg: reg:
maxItems: 1 maxItems: 1

View File

@@ -21,6 +21,9 @@ properties:
- samsung,exynos7870-dwusb3 - samsung,exynos7870-dwusb3
- samsung,exynos850-dwusb3 - samsung,exynos850-dwusb3
- samsung,exynosautov920-dwusb3 - samsung,exynosautov920-dwusb3
- items:
- const: samsung,exynos8890-dwusb3
- const: samsung,exynos7-dwusb3
- items: - items:
- const: samsung,exynos990-dwusb3 - const: samsung,exynos990-dwusb3
- const: samsung,exynos850-dwusb3 - const: samsung,exynos850-dwusb3
@@ -36,6 +39,9 @@ properties:
minItems: 1 minItems: 1
maxItems: 4 maxItems: 4
power-domains:
maxItems: 1
ranges: true ranges: true
'#size-cells': '#size-cells':

View File

@@ -25,6 +25,14 @@ properties:
interrupts: interrupts:
maxItems: 1 maxItems: 1
id-gpios:
description:
An input gpio for USB ID pin. Upon detecting a UFP device, HD3SS3220
will keep ID pin high if VBUS is not at VSafe0V. Once VBUS is at VSafe0V,
the HD3SS3220 will assert ID pin low. This is done to enforce Type-C
requirement that VBUS must be at VSafe0V before re-enabling VBUS.
maxItems: 1
ports: ports:
$ref: /schemas/graph.yaml#/properties/ports $ref: /schemas/graph.yaml#/properties/ports
description: OF graph bindings (specified in bindings/graph.txt) that model description: OF graph bindings (specified in bindings/graph.txt) that model

View File

@@ -20,6 +20,7 @@ properties:
- aspeed,ast2400-uhci - aspeed,ast2400-uhci
- aspeed,ast2500-uhci - aspeed,ast2500-uhci
- aspeed,ast2600-uhci - aspeed,ast2600-uhci
- aspeed,ast2700-uhci
- const: generic-uhci - const: generic-uhci
reg: reg:
@@ -28,6 +29,9 @@ properties:
interrupts: interrupts:
maxItems: 1 maxItems: 1
resets:
maxItems: 1
'#ports': '#ports':
$ref: /schemas/types.yaml#/definitions/uint32 $ref: /schemas/types.yaml#/definitions/uint32
@@ -50,6 +54,15 @@ allOf:
required: required:
- clocks - clocks
- if:
properties:
compatible:
contains:
const: aspeed,ast2700-uhci
then:
required:
- resets
unevaluatedProperties: false unevaluatedProperties: false
examples: examples:

View File

@@ -2477,6 +2477,7 @@ F: Documentation/devicetree/bindings/power/reset/apple,smc-reboot.yaml
F: Documentation/devicetree/bindings/pwm/apple,s5l-fpwm.yaml F: Documentation/devicetree/bindings/pwm/apple,s5l-fpwm.yaml
F: Documentation/devicetree/bindings/spi/apple,spi.yaml F: Documentation/devicetree/bindings/spi/apple,spi.yaml
F: Documentation/devicetree/bindings/spmi/apple,spmi.yaml F: Documentation/devicetree/bindings/spmi/apple,spmi.yaml
F: Documentation/devicetree/bindings/usb/apple,dwc3.yaml
F: Documentation/devicetree/bindings/watchdog/apple,wdt.yaml F: Documentation/devicetree/bindings/watchdog/apple,wdt.yaml
F: Documentation/hwmon/macsmc-hwmon.rst F: Documentation/hwmon/macsmc-hwmon.rst
F: arch/arm64/boot/dts/apple/ F: arch/arm64/boot/dts/apple/
@@ -2503,6 +2504,7 @@ F: drivers/pwm/pwm-apple.c
F: drivers/soc/apple/* F: drivers/soc/apple/*
F: drivers/spi/spi-apple.c F: drivers/spi/spi-apple.c
F: drivers/spmi/spmi-apple-controller.c F: drivers/spmi/spmi-apple-controller.c
F: drivers/usb/dwc3/dwc3-apple.c
F: drivers/video/backlight/apple_dwi_bl.c F: drivers/video/backlight/apple_dwi_bl.c
F: drivers/watchdog/apple_wdt.c F: drivers/watchdog/apple_wdt.c
F: include/dt-bindings/interrupt-controller/apple-aic.h F: include/dt-bindings/interrupt-controller/apple-aic.h

View File

@@ -412,7 +412,7 @@ static void tb_ctl_rx_submit(struct ctl_pkg *pkg)
* We ignore failures during stop. * We ignore failures during stop.
* All rx packets are referenced * All rx packets are referenced
* from ctl->rx_packets, so we do * from ctl->rx_packets, so we do
* not loose them. * not lose them.
*/ */
} }

View File

@@ -201,7 +201,7 @@ static bool parse_line(char **line, u32 *offs, u32 *val, int short_fmt_len,
#if IS_ENABLED(CONFIG_USB4_DEBUGFS_WRITE) #if IS_ENABLED(CONFIG_USB4_DEBUGFS_WRITE)
/* /*
* Path registers need to be written in double word pairs and they both must be * Path registers need to be written in double word pairs and they both must be
* read before written. This writes one double word in patch config space * read before written. This writes one double word in path config space
* following the spec flow. * following the spec flow.
*/ */
static int path_write_one(struct tb_port *port, u32 val, u32 offset) static int path_write_one(struct tb_port *port, u32 val, u32 offset)
@@ -1196,7 +1196,7 @@ static int validate_margining(struct tb_margining *margining)
{ {
/* /*
* For running on RX2 the link must be asymmetric with 3 * For running on RX2 the link must be asymmetric with 3
* receivers. Because this is can change dynamically, check it * receivers. Because this can change dynamically, check it
* here before we start the margining and report back error if * here before we start the margining and report back error if
* expectations are not met. * expectations are not met.
*/ */

View File

@@ -376,7 +376,7 @@ struct tb *tb_domain_alloc(struct tb_nhi *nhi, int timeout_msec, size_t privsize
struct tb *tb; struct tb *tb;
/* /*
* Make sure the structure sizes map with that the hardware * Make sure the structure sizes map with what the hardware
* expects because bit-fields are being used. * expects because bit-fields are being used.
*/ */
BUILD_BUG_ON(sizeof(struct tb_regs_switch_header) != 5 * 4); BUILD_BUG_ON(sizeof(struct tb_regs_switch_header) != 5 * 4);

View File

@@ -21,7 +21,7 @@ static int tb_eeprom_ctl_write(struct tb_switch *sw, struct tb_eeprom_ctl *ctl)
} }
/* /*
* tb_eeprom_ctl_write() - read control word * tb_eeprom_ctl_read() - read control word
*/ */
static int tb_eeprom_ctl_read(struct tb_switch *sw, struct tb_eeprom_ctl *ctl) static int tb_eeprom_ctl_read(struct tb_switch *sw, struct tb_eeprom_ctl *ctl)
{ {

View File

@@ -787,7 +787,7 @@ icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr)
* information might have changed for example by the * information might have changed for example by the
* fact that a switch on a dual-link connection might * fact that a switch on a dual-link connection might
* have been enumerated using the other link now. Make * have been enumerated using the other link now. Make
* sure our book keeping matches that. * sure our bookkeeping matches that.
*/ */
if (sw->depth == depth && sw_phy_port == phy_port && if (sw->depth == depth && sw_phy_port == phy_port &&
!!sw->authorized == authorized) { !!sw->authorized == authorized) {
@@ -969,7 +969,7 @@ icm_fr_xdomain_connected(struct tb *tb, const struct icm_pkg_header *hdr)
/* /*
* Look if there already exists an XDomain in the same place * Look if there already exists an XDomain in the same place
* than the new one and in that case remove it because it is * as the new one and in that case remove it because it is
* most likely another host that got disconnected. * most likely another host that got disconnected.
*/ */
xd = tb_xdomain_find_by_link_depth(tb, link, depth); xd = tb_xdomain_find_by_link_depth(tb, link, depth);
@@ -2000,7 +2000,7 @@ static int icm_driver_ready(struct tb *tb)
if (icm->safe_mode) { if (icm->safe_mode) {
tb_info(tb, "Thunderbolt host controller is in safe mode.\n"); tb_info(tb, "Thunderbolt host controller is in safe mode.\n");
tb_info(tb, "You need to update NVM firmware of the controller before it can be used.\n"); tb_info(tb, "You need to update NVM firmware of the controller before it can be used.\n");
tb_info(tb, "For latest updates check https://thunderbolttechnology.net/updates.\n"); tb_info(tb, "Use fwupd tool to apply update. Check Documentation/admin-guide/thunderbolt.rst for details.\n");
return 0; return 0;
} }
@@ -2171,7 +2171,7 @@ static int icm_runtime_resume_switch(struct tb_switch *sw)
static int icm_runtime_resume(struct tb *tb) static int icm_runtime_resume(struct tb *tb)
{ {
/* /*
* We can reuse the same resume functionality than with system * We can reuse the same resume functionality as with system
* suspend. * suspend.
*/ */
icm_complete(tb); icm_complete(tb);

View File

@@ -558,7 +558,7 @@ static int tb_lc_dp_sink_available(struct tb_switch *sw, int sink)
return ret; return ret;
/* /*
* Sink is available for CM/SW to use if the allocation valie is * Sink is available for CM/SW to use if the allocation value is
* either 0 or 1. * either 0 or 1.
*/ */
if (!sink) { if (!sink) {

View File

@@ -712,7 +712,7 @@ void tb_ring_start(struct tb_ring *ring)
ring_iowrite64desc(ring, ring->descriptors_dma, 0); ring_iowrite64desc(ring, ring->descriptors_dma, 0);
if (ring->is_tx) { if (ring->is_tx) {
ring_iowrite32desc(ring, ring->size, 12); ring_iowrite32desc(ring, ring->size, 12);
ring_iowrite32options(ring, 0, 4); /* time releated ? */ ring_iowrite32options(ring, 0, 4);
ring_iowrite32options(ring, flags, 0); ring_iowrite32options(ring, flags, 0);
} else { } else {
u32 sof_eof_mask = ring->sof_mask << 16 | ring->eof_mask; u32 sof_eof_mask = ring->sof_mask << 16 | ring->eof_mask;

View File

@@ -501,7 +501,7 @@ static struct tb_retimer *tb_port_find_retimer(struct tb_port *port, u8 index)
* @add: If true also registers found retimers * @add: If true also registers found retimers
* *
* Brings the sideband into a state where retimers can be accessed. * Brings the sideband into a state where retimers can be accessed.
* Then Tries to enumerate on-board retimers connected to @port. Found * Then tries to enumerate on-board retimers connected to @port. Found
* retimers are registered as children of @port if @add is set. Does * retimers are registered as children of @port if @add is set. Does
* not scan for cable retimers for now. * not scan for cable retimers for now.
* *

View File

@@ -736,9 +736,9 @@ static int tb_init_port(struct tb_port *port)
port->cap_usb4 = cap; port->cap_usb4 = cap;
/* /*
* USB4 ports the buffers allocated for the control path * USB4 port buffers allocated for the control path
* can be read from the path config space. Legacy * can be read from the path config space. Legacy
* devices we use hard-coded value. * devices use hard-coded value.
*/ */
if (port->cap_usb4) { if (port->cap_usb4) {
struct tb_regs_hop hop; struct tb_regs_hop hop;
@@ -3221,7 +3221,7 @@ int tb_switch_configure_link(struct tb_switch *sw)
* @sw: Switch whose link is unconfigured * @sw: Switch whose link is unconfigured
* *
* Sets the link unconfigured so the @sw will be disconnected if the * Sets the link unconfigured so the @sw will be disconnected if the
* domain exists sleep. * domain exits sleep.
*/ */
void tb_switch_unconfigure_link(struct tb_switch *sw) void tb_switch_unconfigure_link(struct tb_switch *sw)
{ {

View File

@@ -322,7 +322,7 @@ static int tb_enable_tmu(struct tb_switch *sw)
/* /*
* If both routers at the end of the link are v2 we simply * If both routers at the end of the link are v2 we simply
* enable the enhanched uni-directional mode. That covers all * enable the enhanced uni-directional mode. That covers all
* the CL states. For v1 and before we need to use the normal * the CL states. For v1 and before we need to use the normal
* rate to allow CL1 (when supported). Otherwise we keep the TMU * rate to allow CL1 (when supported). Otherwise we keep the TMU
* running at the highest accuracy. * running at the highest accuracy.
@@ -538,7 +538,7 @@ static struct tb_tunnel *tb_find_first_usb3_tunnel(struct tb *tb,
* @src_port: Source protocol adapter * @src_port: Source protocol adapter
* @dst_port: Destination protocol adapter * @dst_port: Destination protocol adapter
* @port: USB4 port the consumed bandwidth is calculated * @port: USB4 port the consumed bandwidth is calculated
* @consumed_up: Consumed upsream bandwidth (Mb/s) * @consumed_up: Consumed upstream bandwidth (Mb/s)
* @consumed_down: Consumed downstream bandwidth (Mb/s) * @consumed_down: Consumed downstream bandwidth (Mb/s)
* *
* Calculates consumed USB3 and PCIe bandwidth at @port between path * Calculates consumed USB3 and PCIe bandwidth at @port between path
@@ -589,7 +589,7 @@ static int tb_consumed_usb3_pcie_bandwidth(struct tb *tb,
* @src_port: Source protocol adapter * @src_port: Source protocol adapter
* @dst_port: Destination protocol adapter * @dst_port: Destination protocol adapter
* @port: USB4 port the consumed bandwidth is calculated * @port: USB4 port the consumed bandwidth is calculated
* @consumed_up: Consumed upsream bandwidth (Mb/s) * @consumed_up: Consumed upstream bandwidth (Mb/s)
* @consumed_down: Consumed downstream bandwidth (Mb/s) * @consumed_down: Consumed downstream bandwidth (Mb/s)
* *
* Calculates consumed DP bandwidth at @port between path from @src_port * Calculates consumed DP bandwidth at @port between path from @src_port
@@ -1115,7 +1115,7 @@ static int tb_configure_asym(struct tb *tb, struct tb_port *src_port,
/* /*
* Here requested + consumed > threshold so we need to * Here requested + consumed > threshold so we need to
* transtion the link into asymmetric now. * transition the link into asymmetric now.
*/ */
ret = tb_switch_set_link_width(up->sw, width_up); ret = tb_switch_set_link_width(up->sw, width_up);
if (ret) { if (ret) {
@@ -1936,7 +1936,7 @@ static void tb_dp_tunnel_active(struct tb_tunnel *tunnel, void *data)
*/ */
tb_recalc_estimated_bandwidth(tb); tb_recalc_estimated_bandwidth(tb);
/* /*
* In case of DP tunnel exists, change host * In case DP tunnel exists, change host
* router's 1st children TMU mode to HiFi for * router's 1st children TMU mode to HiFi for
* CL0s to work. * CL0s to work.
*/ */
@@ -2636,7 +2636,7 @@ static int tb_alloc_dp_bandwidth(struct tb_tunnel *tunnel, int *requested_up,
* the 10s already expired and we should * the 10s already expired and we should
* give the reserved back to others). * give the reserved back to others).
*/ */
mod_delayed_work(system_wq, &group->release_work, mod_delayed_work(system_percpu_wq, &group->release_work,
msecs_to_jiffies(TB_RELEASE_BW_TIMEOUT)); msecs_to_jiffies(TB_RELEASE_BW_TIMEOUT));
} }
} }
@@ -2786,8 +2786,8 @@ static void tb_handle_dp_bandwidth_request(struct work_struct *work)
* There is no request active so this means the * There is no request active so this means the
* BW allocation mode was enabled from graphics * BW allocation mode was enabled from graphics
* side. At this point we know that the graphics * side. At this point we know that the graphics
* driver has read the DRPX capabilities so we * driver has read the DPRX capabilities so we
* can offer an better bandwidth estimatation. * can offer better bandwidth estimation.
*/ */
tb_port_dbg(in, "DPTX enabled bandwidth allocation mode, updating estimated bandwidth\n"); tb_port_dbg(in, "DPTX enabled bandwidth allocation mode, updating estimated bandwidth\n");
tb_recalc_estimated_bandwidth(tb); tb_recalc_estimated_bandwidth(tb);

View File

@@ -308,7 +308,7 @@ struct tb_port {
* struct usb4_port - USB4 port device * struct usb4_port - USB4 port device
* @dev: Device for the port * @dev: Device for the port
* @port: Pointer to the lane 0 adapter * @port: Pointer to the lane 0 adapter
* @can_offline: Does the port have necessary platform support to moved * @can_offline: Does the port have necessary platform support to move
* it into offline mode and back * it into offline mode and back
* @offline: The port is currently in offline mode * @offline: The port is currently in offline mode
* @margining: Pointer to margining structure if enabled * @margining: Pointer to margining structure if enabled
@@ -355,7 +355,7 @@ struct tb_retimer {
* struct tb_path_hop - routing information for a tb_path * struct tb_path_hop - routing information for a tb_path
* @in_port: Ingress port of a switch * @in_port: Ingress port of a switch
* @out_port: Egress port of a switch where the packet is routed out * @out_port: Egress port of a switch where the packet is routed out
* (must be on the same switch than @in_port) * (must be on the same switch as @in_port)
* @in_hop_index: HopID where the path configuration entry is placed in * @in_hop_index: HopID where the path configuration entry is placed in
* the path config space of @in_port. * the path config space of @in_port.
* @in_counter_index: Used counter index (not used in the driver * @in_counter_index: Used counter index (not used in the driver
@@ -499,9 +499,9 @@ struct tb_path {
* performed. If this returns %-EOPNOTSUPP then the * performed. If this returns %-EOPNOTSUPP then the
* native USB4 router operation is called. * native USB4 router operation is called.
* @usb4_switch_nvm_authenticate_status: Optional callback that the CM * @usb4_switch_nvm_authenticate_status: Optional callback that the CM
* implementation can be used to * implementation can use to return
* return status of USB4 NVM_AUTH * status of USB4 NVM_AUTH router
* router operation. * operation.
*/ */
struct tb_cm_ops { struct tb_cm_ops {
int (*driver_ready)(struct tb *tb); int (*driver_ready)(struct tb *tb);
@@ -1109,7 +1109,7 @@ struct tb_port *tb_next_port_on_path(struct tb_port *start, struct tb_port *end,
struct tb_port *prev); struct tb_port *prev);
/** /**
* tb_port_path_direction_downstream() - Checks if path directed downstream * tb_port_path_direction_downstream() - Checks if path is directed downstream
* @src: Source adapter * @src: Source adapter
* @dst: Destination adapter * @dst: Destination adapter
* *
@@ -1141,7 +1141,7 @@ static inline bool tb_port_use_credit_allocation(const struct tb_port *port)
(p) = tb_next_port_on_path((src), (dst), (p))) (p) = tb_next_port_on_path((src), (dst), (p)))
/** /**
* tb_for_each_upstream_port_on_path() - Iterate over each upstreamm port on path * tb_for_each_upstream_port_on_path() - Iterate over each upstream port on path
* @src: Source port * @src: Source port
* @dst: Destination port * @dst: Destination port
* @p: Port used as iterator * @p: Port used as iterator

View File

@@ -99,7 +99,7 @@ struct tb_cap_extended_long {
} __packed; } __packed;
/** /**
* struct tb_cap_any - Structure capable of hold every capability * struct tb_cap_any - Structure capable of holding every capability
* @basic: Basic capability * @basic: Basic capability
* @extended_short: Vendor specific capability * @extended_short: Vendor specific capability
* @extended_long: Vendor specific extended capability * @extended_long: Vendor specific extended capability
@@ -534,8 +534,8 @@ struct tb_regs_hop {
/* /*
* Used for Titan Ridge only. Bits are part of the same register: TMU_ADP_CS_6 * Used for Titan Ridge only. Bits are part of the same register: TMU_ADP_CS_6
* (see above) as in USB4 spec, but these specific bits used for Titan Ridge * (see above) as in USB4 spec, but these specific bits are used for Titan Ridge
* only and reserved in USB4 spec. * only and are reserved in USB4 spec.
*/ */
#define TMU_ADP_CS_6_DISABLE_TMU_OBJ_MASK GENMASK(3, 2) #define TMU_ADP_CS_6_DISABLE_TMU_OBJ_MASK GENMASK(3, 2)
#define TMU_ADP_CS_6_DISABLE_TMU_OBJ_CL1 BIT(2) #define TMU_ADP_CS_6_DISABLE_TMU_OBJ_CL1 BIT(2)

View File

@@ -400,10 +400,10 @@ static int tmu_mode_init(struct tb_switch *sw)
/** /**
* tb_switch_tmu_init() - Initialize switch TMU structures * tb_switch_tmu_init() - Initialize switch TMU structures
* @sw: Switch to initialized * @sw: Switch to be initialized
* *
* This function must be called before other TMU related functions to * This function must be called before other TMU related functions to
* makes the internal structures are filled in correctly. Does not * make sure the internal structures are filled in correctly. Does not
* change any hardware configuration. * change any hardware configuration.
* *
* Return: %0 on success, negative errno otherwise. * Return: %0 on success, negative errno otherwise.

View File

@@ -301,7 +301,7 @@ static int tb_pci_set_ext_encapsulation(struct tb_tunnel *tunnel, bool enable)
struct tb_port *port = tb_upstream_port(tunnel->dst_port->sw); struct tb_port *port = tb_upstream_port(tunnel->dst_port->sw);
int ret; int ret;
/* Only supported of both routers are at least USB4 v2 */ /* Only supported if both routers are at least USB4 v2 */
if ((usb4_switch_version(tunnel->src_port->sw) < 2) || if ((usb4_switch_version(tunnel->src_port->sw) < 2) ||
(usb4_switch_version(tunnel->dst_port->sw) < 2)) (usb4_switch_version(tunnel->dst_port->sw) < 2))
return 0; return 0;
@@ -1170,8 +1170,8 @@ static int tb_dp_bandwidth_mode_maximum_bandwidth(struct tb_tunnel *tunnel,
/* /*
* DP IN adapter DP_LOCAL_CAP gets updated to the lowest AUX * DP IN adapter DP_LOCAL_CAP gets updated to the lowest AUX
* read parameter values so this so we can use this to determine * read parameter values so we can use this to determine the
* the maximum possible bandwidth over this link. * maximum possible bandwidth over this link.
* *
* See USB4 v2 spec 1.0 10.4.4.5. * See USB4 v2 spec 1.0 10.4.4.5.
*/ */
@@ -1783,8 +1783,8 @@ static int tb_dma_init_rx_path(struct tb_path *path, unsigned int credits)
/* /*
* First lane adapter is the one connected to the remote host. * First lane adapter is the one connected to the remote host.
* We don't tunnel other traffic over this link so can use all * We don't tunnel other traffic over this link so we can use
* the credits (except the ones reserved for control traffic). * all the credits (except the ones reserved for control traffic).
*/ */
hop = &path->hops[0]; hop = &path->hops[0];
tmp = min(tb_usable_credits(hop->in_port), credits); tmp = min(tb_usable_credits(hop->in_port), credits);
@@ -2044,7 +2044,7 @@ static int tb_usb3_consumed_bandwidth(struct tb_tunnel *tunnel,
/* /*
* PCIe tunneling, if enabled, affects the USB3 bandwidth so * PCIe tunneling, if enabled, affects the USB3 bandwidth so
* take that it into account here. * take that into account here.
*/ */
*consumed_up = tunnel->allocated_up * *consumed_up = tunnel->allocated_up *
(TB_USB3_WEIGHT + pcie_weight) / TB_USB3_WEIGHT; (TB_USB3_WEIGHT + pcie_weight) / TB_USB3_WEIGHT;
@@ -2605,7 +2605,7 @@ int tb_tunnel_consumed_bandwidth(struct tb_tunnel *tunnel, int *consumed_up,
* @tunnel: Tunnel whose unused bandwidth to release * @tunnel: Tunnel whose unused bandwidth to release
* *
* If tunnel supports dynamic bandwidth management (USB3 tunnels at the * If tunnel supports dynamic bandwidth management (USB3 tunnels at the
* moment) this function makes it to release all the unused bandwidth. * moment) this function makes it release all the unused bandwidth.
* *
* Return: %0 on success, negative errno otherwise. * Return: %0 on success, negative errno otherwise.
*/ */

View File

@@ -284,7 +284,7 @@ int usb4_switch_setup(struct tb_switch *sw)
val |= ROUTER_CS_5_PTO; val |= ROUTER_CS_5_PTO;
/* /*
* xHCI can be enabled if PCIe tunneling is supported * xHCI can be enabled if PCIe tunneling is supported
* and the parent does not have any USB3 dowstream * and the parent does not have any USB3 downstream
* adapters (so we cannot do USB 3.x tunneling). * adapters (so we cannot do USB 3.x tunneling).
*/ */
if (xhci) if (xhci)
@@ -1342,7 +1342,7 @@ static int usb4_port_write_data(struct tb_port *port, const void *data,
* usb4_port_sb_read() - Read from sideband register * usb4_port_sb_read() - Read from sideband register
* @port: USB4 port to read * @port: USB4 port to read
* @target: Sideband target * @target: Sideband target
* @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER * @index: Retimer index if target is %USB4_SB_TARGET_RETIMER
* @reg: Sideband register index * @reg: Sideband register index
* @buf: Buffer where the sideband data is copied * @buf: Buffer where the sideband data is copied
* @size: Size of @buf * @size: Size of @buf
@@ -1395,7 +1395,7 @@ int usb4_port_sb_read(struct tb_port *port, enum usb4_sb_target target, u8 index
* usb4_port_sb_write() - Write to sideband register * usb4_port_sb_write() - Write to sideband register
* @port: USB4 port to write * @port: USB4 port to write
* @target: Sideband target * @target: Sideband target
* @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER * @index: Retimer index if target is %USB4_SB_TARGET_RETIMER
* @reg: Sideband register index * @reg: Sideband register index
* @buf: Data to write * @buf: Data to write
* @size: Size of @buf * @size: Size of @buf
@@ -1527,7 +1527,7 @@ int usb4_port_router_offline(struct tb_port *port)
} }
/** /**
* usb4_port_router_online() - Put the USB4 port back to online * usb4_port_router_online() - Put the USB4 port back online
* @port: USB4 port * @port: USB4 port
* *
* Makes the USB4 port functional again. * Makes the USB4 port functional again.
@@ -1692,10 +1692,10 @@ int usb4_port_asym_start(struct tb_port *port)
} }
/** /**
* usb4_port_margining_caps() - Read USB4 port marginig capabilities * usb4_port_margining_caps() - Read USB4 port margining capabilities
* @port: USB4 port * @port: USB4 port
* @target: Sideband target * @target: Sideband target
* @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER * @index: Retimer index if target is %USB4_SB_TARGET_RETIMER
* @caps: Array with at least two elements to hold the results * @caps: Array with at least two elements to hold the results
* @ncaps: Number of elements in the caps array * @ncaps: Number of elements in the caps array
* *
@@ -1721,7 +1721,7 @@ int usb4_port_margining_caps(struct tb_port *port, enum usb4_sb_target target,
* usb4_port_hw_margin() - Run hardware lane margining on port * usb4_port_hw_margin() - Run hardware lane margining on port
* @port: USB4 port * @port: USB4 port
* @target: Sideband target * @target: Sideband target
* @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER * @index: Retimer index if target is %USB4_SB_TARGET_RETIMER
* @params: Parameters for USB4 hardware margining * @params: Parameters for USB4 hardware margining
* @results: Array to hold the results * @results: Array to hold the results
* @nresults: Number of elements in the results array * @nresults: Number of elements in the results array
@@ -1769,7 +1769,7 @@ int usb4_port_hw_margin(struct tb_port *port, enum usb4_sb_target target,
* usb4_port_sw_margin() - Run software lane margining on port * usb4_port_sw_margin() - Run software lane margining on port
* @port: USB4 port * @port: USB4 port
* @target: Sideband target * @target: Sideband target
* @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER * @index: Retimer index if target is %USB4_SB_TARGET_RETIMER
* @params: Parameters for USB4 software margining * @params: Parameters for USB4 software margining
* @results: Data word for the operation completion data * @results: Data word for the operation completion data
* *
@@ -1819,7 +1819,7 @@ int usb4_port_sw_margin(struct tb_port *port, enum usb4_sb_target target,
* usb4_port_sw_margin_errors() - Read the software margining error counters * usb4_port_sw_margin_errors() - Read the software margining error counters
* @port: USB4 port * @port: USB4 port
* @target: Sideband target * @target: Sideband target
* @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER * @index: Retimer index if target is %USB4_SB_TARGET_RETIMER
* @errors: Error metadata is copied here. * @errors: Error metadata is copied here.
* *
* This reads back the software margining error counters from the port. * This reads back the software margining error counters from the port.
@@ -1853,7 +1853,7 @@ static inline int usb4_port_retimer_op(struct tb_port *port, u8 index,
* @port: USB4 port * @port: USB4 port
* @index: Retimer index * @index: Retimer index
* *
* Enables sideband channel transations on SBTX. Can be used when USB4 * Enables sideband channel transactions on SBTX. Can be used when USB4
* link does not go up, for example if there is no device connected. * link does not go up, for example if there is no device connected.
* *
* Return: %0 on success, negative errno otherwise. * Return: %0 on success, negative errno otherwise.
@@ -1882,7 +1882,7 @@ int usb4_port_retimer_set_inbound_sbtx(struct tb_port *port, u8 index)
* @port: USB4 port * @port: USB4 port
* @index: Retimer index * @index: Retimer index
* *
* Disables sideband channel transations on SBTX. The reverse of * Disables sideband channel transactions on SBTX. The reverse of
* usb4_port_retimer_set_inbound_sbtx(). * usb4_port_retimer_set_inbound_sbtx().
* *
* Return: %0 on success, negative errno otherwise. * Return: %0 on success, negative errno otherwise.
@@ -1981,7 +1981,7 @@ int usb4_port_retimer_nvm_sector_size(struct tb_port *port, u8 index)
* @index: Retimer index * @index: Retimer index
* @address: Start offset * @address: Start offset
* *
* Exlicitly sets NVM write offset. Normally when writing to NVM this is * Explicitly sets NVM write offset. Normally when writing to NVM this is
* done automatically by usb4_port_retimer_nvm_write(). * done automatically by usb4_port_retimer_nvm_write().
* *
* Return: %0 on success, negative errno otherwise. * Return: %0 on success, negative errno otherwise.
@@ -2190,7 +2190,7 @@ usb4_usb3_port_max_bandwidth(const struct tb_port *port, unsigned int bw)
} }
/** /**
* usb4_usb3_port_max_link_rate() - Maximum support USB3 link rate * usb4_usb3_port_max_link_rate() - Maximum supported USB3 link rate
* @port: USB3 adapter port * @port: USB3 adapter port
* *
* Return: Maximum supported link rate of a USB3 adapter in Mb/s. * Return: Maximum supported link rate of a USB3 adapter in Mb/s.

View File

@@ -1951,8 +1951,8 @@ static void tb_xdomain_link_exit(struct tb_xdomain *xd)
/** /**
* tb_xdomain_alloc() - Allocate new XDomain object * tb_xdomain_alloc() - Allocate new XDomain object
* @tb: Domain where the XDomain belongs * @tb: Domain where the XDomain belongs
* @parent: Parent device (the switch through the connection to the * @parent: Parent device (the switch through which the other domain
* other domain is reached). * is reached).
* @route: Route string used to reach the other domain * @route: Route string used to reach the other domain
* @local_uuid: Our local domain UUID * @local_uuid: Our local domain UUID
* @remote_uuid: UUID of the other domain (optional) * @remote_uuid: UUID of the other domain (optional)

View File

@@ -3251,7 +3251,6 @@ static void cdns3_gadget_exit(struct cdns *cdns)
priv_dev = cdns->gadget_dev; priv_dev = cdns->gadget_dev;
pm_runtime_mark_last_busy(cdns->dev);
pm_runtime_put_autosuspend(cdns->dev); pm_runtime_put_autosuspend(cdns->dev);
usb_del_gadget(&priv_dev->gadget); usb_del_gadget(&priv_dev->gadget);

View File

@@ -1999,7 +1999,6 @@ static void cdnsp_gadget_exit(struct cdns *cdns)
struct cdnsp_device *pdev = cdns->gadget_dev; struct cdnsp_device *pdev = cdns->gadget_dev;
devm_free_irq(pdev->dev, cdns->dev_irq, pdev); devm_free_irq(pdev->dev, cdns->dev_irq, pdev);
pm_runtime_mark_last_busy(cdns->dev);
pm_runtime_put_autosuspend(cdns->dev); pm_runtime_put_autosuspend(cdns->dev);
usb_del_gadget(&pdev->gadget); usb_del_gadget(&pdev->gadget);
cdnsp_gadget_free_endpoints(pdev); cdnsp_gadget_free_endpoints(pdev);

View File

@@ -1375,7 +1375,6 @@ static int ci_controller_resume(struct device *dev)
ci->in_lpm = false; ci->in_lpm = false;
if (ci->wakeup_int) { if (ci->wakeup_int) {
ci->wakeup_int = false; ci->wakeup_int = false;
pm_runtime_mark_last_busy(ci->dev);
pm_runtime_put_autosuspend(ci->dev); pm_runtime_put_autosuspend(ci->dev);
enable_irq(ci->irq); enable_irq(ci->irq);
if (ci_otg_is_fsm_mode(ci)) if (ci_otg_is_fsm_mode(ci))

View File

@@ -629,7 +629,6 @@ int ci_otg_fsm_work(struct ci_hdrc *ci)
ci_otg_queue_work(ci); ci_otg_queue_work(ci);
} }
} else if (ci->fsm.otg->state == OTG_STATE_A_HOST) { } else if (ci->fsm.otg->state == OTG_STATE_A_HOST) {
pm_runtime_mark_last_busy(ci->dev);
pm_runtime_put_autosuspend(ci->dev); pm_runtime_put_autosuspend(ci->dev);
return 0; return 0;
} }

View File

@@ -1224,6 +1224,14 @@ static const struct usbmisc_ops imx7ulp_usbmisc_ops = {
.power_lost_check = usbmisc_imx7d_power_lost_check, .power_lost_check = usbmisc_imx7d_power_lost_check,
}; };
static const struct usbmisc_ops imx94_usbmisc_ops = {
.init = usbmisc_imx7d_init,
.set_wakeup = usbmisc_imx95_set_wakeup,
.charger_detection = imx7d_charger_detection,
.power_lost_check = usbmisc_imx7d_power_lost_check,
.vbus_comparator_on = usbmisc_imx7d_vbus_comparator_on,
};
static const struct usbmisc_ops imx95_usbmisc_ops = { static const struct usbmisc_ops imx95_usbmisc_ops = {
.init = usbmisc_imx7d_init, .init = usbmisc_imx7d_init,
.set_wakeup = usbmisc_imx95_set_wakeup, .set_wakeup = usbmisc_imx95_set_wakeup,
@@ -1481,6 +1489,10 @@ static const struct of_device_id usbmisc_imx_dt_ids[] = {
.compatible = "fsl,imx8ulp-usbmisc", .compatible = "fsl,imx8ulp-usbmisc",
.data = &imx7ulp_usbmisc_ops, .data = &imx7ulp_usbmisc_ops,
}, },
{
.compatible = "fsl,imx94-usbmisc",
.data = &imx94_usbmisc_ops,
},
{ {
.compatible = "fsl,imx95-usbmisc", .compatible = "fsl,imx95-usbmisc",
.data = &imx95_usbmisc_ops, .data = &imx95_usbmisc_ops,

View File

@@ -1475,7 +1475,7 @@ made_compressed_probe:
if (!acm->country_codes) if (!acm->country_codes)
goto skip_countries; goto skip_countries;
acm->country_code_size = cfd->bLength - 4; acm->country_code_size = cfd->bLength - 4;
memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0, memcpy(acm->country_codes, cfd->wCountryCodes,
cfd->bLength - 4); cfd->bLength - 4);
acm->country_rel_date = cfd->iCountryCodeRelDate; acm->country_rel_date = cfd->iCountryCodeRelDate;

View File

@@ -1936,10 +1936,8 @@ static int usbtmc_ioctl_request(struct usbtmc_device_data *data,
u8 *buffer = NULL; u8 *buffer = NULL;
int rv; int rv;
unsigned int is_in, pipe; unsigned int is_in, pipe;
unsigned long res;
res = copy_from_user(&request, arg, sizeof(struct usbtmc_ctrlrequest)); if (copy_from_user(&request, arg, sizeof(struct usbtmc_ctrlrequest)))
if (res)
return -EFAULT; return -EFAULT;
if (request.req.wLength > USBTMC_BUFSIZE) if (request.req.wLength > USBTMC_BUFSIZE)
@@ -1956,9 +1954,8 @@ static int usbtmc_ioctl_request(struct usbtmc_device_data *data,
if (!is_in) { if (!is_in) {
/* Send control data to device */ /* Send control data to device */
res = copy_from_user(buffer, request.data, if (copy_from_user(buffer, request.data,
request.req.wLength); request.req.wLength)) {
if (res) {
rv = -EFAULT; rv = -EFAULT;
goto exit; goto exit;
} }
@@ -1984,8 +1981,7 @@ static int usbtmc_ioctl_request(struct usbtmc_device_data *data,
if (rv && is_in) { if (rv && is_in) {
/* Read control data from device */ /* Read control data from device */
res = copy_to_user(request.data, buffer, rv); if (copy_to_user(request.data, buffer, rv))
if (res)
rv = -EFAULT; rv = -EFAULT;
} }

View File

@@ -3,10 +3,13 @@
# Makefile for USB Core files and filesystem # Makefile for USB Core files and filesystem
# #
# define_trace.h needs to know how to find our header
CFLAGS_trace.o := -I$(src)
usbcore-y := usb.o hub.o hcd.o urb.o message.o driver.o usbcore-y := usb.o hub.o hcd.o urb.o message.o driver.o
usbcore-y += config.o file.o buffer.o sysfs.o endpoint.o usbcore-y += config.o file.o buffer.o sysfs.o endpoint.o
usbcore-y += devio.o notify.o generic.o quirks.o devices.o usbcore-y += devio.o notify.o generic.o quirks.o devices.o
usbcore-y += phy.o port.o usbcore-y += phy.o port.o trace.o
usbcore-$(CONFIG_OF) += of.o usbcore-$(CONFIG_OF) += of.o
usbcore-$(CONFIG_USB_XHCI_SIDEBAND) += offload.o usbcore-$(CONFIG_USB_XHCI_SIDEBAND) += offload.o

View File

@@ -2696,18 +2696,18 @@ static void hcd_release(struct kref *kref)
kfree(hcd); kfree(hcd);
} }
struct usb_hcd *usb_get_hcd (struct usb_hcd *hcd) struct usb_hcd *usb_get_hcd(struct usb_hcd *hcd)
{ {
if (hcd) if (hcd)
kref_get (&hcd->kref); kref_get(&hcd->kref);
return hcd; return hcd;
} }
EXPORT_SYMBOL_GPL(usb_get_hcd); EXPORT_SYMBOL_GPL(usb_get_hcd);
void usb_put_hcd (struct usb_hcd *hcd) void usb_put_hcd(struct usb_hcd *hcd)
{ {
if (hcd) if (hcd)
kref_put (&hcd->kref, hcd_release); kref_put(&hcd->kref, hcd_release);
} }
EXPORT_SYMBOL_GPL(usb_put_hcd); EXPORT_SYMBOL_GPL(usb_put_hcd);

View File

@@ -28,6 +28,7 @@
#include <linux/usb/otg.h> #include <linux/usb/otg.h>
#include <linux/usb/quirks.h> #include <linux/usb/quirks.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/minmax.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/random.h> #include <linux/random.h>
#include <linux/pm_qos.h> #include <linux/pm_qos.h>
@@ -40,6 +41,7 @@
#include "hub.h" #include "hub.h"
#include "phy.h" #include "phy.h"
#include "otg_productlist.h" #include "otg_productlist.h"
#include "trace.h"
#define USB_VENDOR_GENESYS_LOGIC 0x05e3 #define USB_VENDOR_GENESYS_LOGIC 0x05e3
#define USB_VENDOR_SMSC 0x0424 #define USB_VENDOR_SMSC 0x0424
@@ -277,10 +279,7 @@ static void usb_set_lpm_pel(struct usb_device *udev,
* device and the parent hub into U0. The exit latency is the bigger of * device and the parent hub into U0. The exit latency is the bigger of
* the device exit latency or the hub exit latency. * the device exit latency or the hub exit latency.
*/ */
if (udev_exit_latency > hub_exit_latency) first_link_pel = max(udev_exit_latency, hub_exit_latency) * 1000;
first_link_pel = udev_exit_latency * 1000;
else
first_link_pel = hub_exit_latency * 1000;
/* /*
* When the hub starts to receive the LFPS, there is a slight delay for * When the hub starts to receive the LFPS, there is a slight delay for
@@ -294,10 +293,7 @@ static void usb_set_lpm_pel(struct usb_device *udev,
* According to figure C-7 in the USB 3.0 spec, the PEL for this device * According to figure C-7 in the USB 3.0 spec, the PEL for this device
* is the greater of the two exit latencies. * is the greater of the two exit latencies.
*/ */
if (first_link_pel > hub_pel) udev_lpm_params->pel = max(first_link_pel, hub_pel);
udev_lpm_params->pel = first_link_pel;
else
udev_lpm_params->pel = hub_pel;
} }
/* /*
@@ -2147,6 +2143,21 @@ static void update_port_device_state(struct usb_device *udev)
} }
} }
static void update_usb_device_state(struct usb_device *udev,
enum usb_device_state new_state)
{
if (udev->state == USB_STATE_SUSPENDED &&
new_state != USB_STATE_SUSPENDED)
udev->active_duration -= jiffies;
else if (new_state == USB_STATE_SUSPENDED &&
udev->state != USB_STATE_SUSPENDED)
udev->active_duration += jiffies;
udev->state = new_state;
update_port_device_state(udev);
trace_usb_set_device_state(udev);
}
static void recursively_mark_NOTATTACHED(struct usb_device *udev) static void recursively_mark_NOTATTACHED(struct usb_device *udev)
{ {
struct usb_hub *hub = usb_hub_to_struct_hub(udev); struct usb_hub *hub = usb_hub_to_struct_hub(udev);
@@ -2156,10 +2167,7 @@ static void recursively_mark_NOTATTACHED(struct usb_device *udev)
if (hub->ports[i]->child) if (hub->ports[i]->child)
recursively_mark_NOTATTACHED(hub->ports[i]->child); recursively_mark_NOTATTACHED(hub->ports[i]->child);
} }
if (udev->state == USB_STATE_SUSPENDED) update_usb_device_state(udev, USB_STATE_NOTATTACHED);
udev->active_duration -= jiffies;
udev->state = USB_STATE_NOTATTACHED;
update_port_device_state(udev);
} }
/** /**
@@ -2209,14 +2217,7 @@ void usb_set_device_state(struct usb_device *udev,
else else
wakeup = 0; wakeup = 0;
} }
if (udev->state == USB_STATE_SUSPENDED && update_usb_device_state(udev, new_state);
new_state != USB_STATE_SUSPENDED)
udev->active_duration -= jiffies;
else if (new_state == USB_STATE_SUSPENDED &&
udev->state != USB_STATE_SUSPENDED)
udev->active_duration += jiffies;
udev->state = new_state;
update_port_device_state(udev);
} else } else
recursively_mark_NOTATTACHED(udev); recursively_mark_NOTATTACHED(udev);
spin_unlock_irqrestore(&device_state_lock, flags); spin_unlock_irqrestore(&device_state_lock, flags);
@@ -6077,7 +6078,7 @@ int usb_hub_init(void)
* device was gone before the EHCI controller had handed its port * device was gone before the EHCI controller had handed its port
* over to the companion full-speed controller. * over to the companion full-speed controller.
*/ */
hub_wq = alloc_workqueue("usb_hub_wq", WQ_FREEZABLE, 0); hub_wq = alloc_workqueue("usb_hub_wq", WQ_FREEZABLE | WQ_PERCPU, 0);
if (hub_wq) if (hub_wq)
return 0; return 0;

View File

@@ -2431,7 +2431,7 @@ int cdc_parse_cdc_header(struct usb_cdc_parsed_header *hdr,
break; break;
case USB_CDC_MBIM_EXTENDED_TYPE: case USB_CDC_MBIM_EXTENDED_TYPE:
if (elength < sizeof(struct usb_cdc_mbim_extended_desc)) if (elength < sizeof(struct usb_cdc_mbim_extended_desc))
break; goto next_desc;
hdr->usb_cdc_mbim_extended_desc = hdr->usb_cdc_mbim_extended_desc =
(struct usb_cdc_mbim_extended_desc *)buffer; (struct usb_cdc_mbim_extended_desc *)buffer;
break; break;

6
drivers/usb/core/trace.c Normal file
View File

@@ -0,0 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2025 Google LLC
*/
#define CREATE_TRACE_POINTS
#include "trace.h"

61
drivers/usb/core/trace.h Normal file
View File

@@ -0,0 +1,61 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2025 Google LLC
*/
#undef TRACE_SYSTEM
#define TRACE_SYSTEM usbcore
#if !defined(_USB_CORE_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
#define _USB_CORE_TRACE_H
#include <linux/types.h>
#include <linux/tracepoint.h>
#include <linux/usb.h>
DECLARE_EVENT_CLASS(usb_core_log_usb_device,
TP_PROTO(struct usb_device *udev),
TP_ARGS(udev),
TP_STRUCT__entry(
__string(name, dev_name(&udev->dev))
__field(enum usb_device_speed, speed)
__field(enum usb_device_state, state)
__field(unsigned short, bus_mA)
__field(unsigned, authorized)
),
TP_fast_assign(
__assign_str(name);
__entry->speed = udev->speed;
__entry->state = udev->state;
__entry->bus_mA = udev->bus_mA;
__entry->authorized = udev->authorized;
),
TP_printk("usb %s speed %s state %s %dmA [%s]",
__get_str(name),
usb_speed_string(__entry->speed),
usb_state_string(__entry->state),
__entry->bus_mA,
__entry->authorized ? "authorized" : "unauthorized")
);
DEFINE_EVENT(usb_core_log_usb_device, usb_set_device_state,
TP_PROTO(struct usb_device *udev),
TP_ARGS(udev)
);
DEFINE_EVENT(usb_core_log_usb_device, usb_alloc_dev,
TP_PROTO(struct usb_device *udev),
TP_ARGS(udev)
);
#endif /* _USB_CORE_TRACE_H */
/* this part has to be here */
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE trace
#include <trace/define_trace.h>

View File

@@ -46,6 +46,7 @@
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include "hub.h" #include "hub.h"
#include "trace.h"
const char *usbcore_name = "usbcore"; const char *usbcore_name = "usbcore";
@@ -746,6 +747,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
#endif #endif
dev->authorized = usb_dev_authorized(dev, usb_hcd); dev->authorized = usb_dev_authorized(dev, usb_hcd);
trace_usb_alloc_dev(dev);
return dev; return dev;
} }
EXPORT_SYMBOL_GPL(usb_alloc_dev); EXPORT_SYMBOL_GPL(usb_alloc_dev);

View File

@@ -369,11 +369,11 @@ static void dwc2_driver_shutdown(struct platform_device *dev)
{ {
struct dwc2_hsotg *hsotg = platform_get_drvdata(dev); struct dwc2_hsotg *hsotg = platform_get_drvdata(dev);
dwc2_disable_global_interrupts(hsotg); if (hsotg->ll_hw_enabled) {
synchronize_irq(hsotg->irq); dwc2_disable_global_interrupts(hsotg);
synchronize_irq(hsotg->irq);
if (hsotg->ll_hw_enabled)
dwc2_lowlevel_hw_disable(hsotg); dwc2_lowlevel_hw_disable(hsotg);
}
} }
/** /**
@@ -649,9 +649,13 @@ error:
static int __maybe_unused dwc2_suspend(struct device *dev) static int __maybe_unused dwc2_suspend(struct device *dev)
{ {
struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev); struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev);
bool is_device_mode = dwc2_is_device_mode(dwc2); bool is_device_mode;
int ret = 0; int ret = 0;
if (!dwc2->ll_hw_enabled)
return 0;
is_device_mode = dwc2_is_device_mode(dwc2);
if (is_device_mode) if (is_device_mode)
dwc2_hsotg_suspend(dwc2); dwc2_hsotg_suspend(dwc2);
@@ -728,6 +732,9 @@ static int __maybe_unused dwc2_resume(struct device *dev)
struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev); struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev);
int ret = 0; int ret = 0;
if (!dwc2->ll_hw_enabled)
return 0;
if (dwc2->phy_off_for_suspend && dwc2->ll_hw_enabled) { if (dwc2->phy_off_for_suspend && dwc2->ll_hw_enabled) {
ret = __dwc2_lowlevel_hw_enable(dwc2); ret = __dwc2_lowlevel_hw_enable(dwc2);
if (ret) if (ret)

View File

@@ -200,4 +200,15 @@ config USB_DWC3_GENERIC_PLAT
the dwc3 child node in the device tree. the dwc3 child node in the device tree.
Say 'Y' or 'M' here if your platform integrates DWC3 in a similar way. Say 'Y' or 'M' here if your platform integrates DWC3 in a similar way.
config USB_DWC3_APPLE
tristate "Apple Silicon DWC3 Platform Driver"
depends on OF && ARCH_APPLE
default USB_DWC3
select USB_ROLE_SWITCH
help
Support Apple Silicon SoCs with DesignWare Core USB3 IP.
The DesignWare Core USB3 IP has to be used in dual-role
mode on these machines.
Say 'Y' or 'M' if you have such device.
endif endif

View File

@@ -43,6 +43,7 @@ endif
## ##
obj-$(CONFIG_USB_DWC3_AM62) += dwc3-am62.o obj-$(CONFIG_USB_DWC3_AM62) += dwc3-am62.o
obj-$(CONFIG_USB_DWC3_APPLE) += dwc3-apple.o
obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.o obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.o
obj-$(CONFIG_USB_DWC3_EXYNOS) += dwc3-exynos.o obj-$(CONFIG_USB_DWC3_EXYNOS) += dwc3-exynos.o
obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o

View File

@@ -133,6 +133,7 @@ void dwc3_enable_susphy(struct dwc3 *dwc, bool enable)
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(i), reg); dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(i), reg);
} }
} }
EXPORT_SYMBOL_GPL(dwc3_enable_susphy);
void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy) void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy)
{ {
@@ -159,6 +160,7 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy)
dwc->current_dr_role = mode; dwc->current_dr_role = mode;
trace_dwc3_set_prtcap(mode); trace_dwc3_set_prtcap(mode);
} }
EXPORT_SYMBOL_GPL(dwc3_set_prtcap);
static void __dwc3_set_mode(struct work_struct *work) static void __dwc3_set_mode(struct work_struct *work)
{ {
@@ -281,7 +283,6 @@ static void __dwc3_set_mode(struct work_struct *work)
} }
out: out:
pm_runtime_mark_last_busy(dwc->dev);
pm_runtime_put_autosuspend(dwc->dev); pm_runtime_put_autosuspend(dwc->dev);
mutex_unlock(&dwc->mutex); mutex_unlock(&dwc->mutex);
} }
@@ -976,7 +977,7 @@ static void dwc3_clk_disable(struct dwc3 *dwc)
clk_disable_unprepare(dwc->bus_clk); clk_disable_unprepare(dwc->bus_clk);
} }
static void dwc3_core_exit(struct dwc3 *dwc) void dwc3_core_exit(struct dwc3 *dwc)
{ {
dwc3_event_buffers_cleanup(dwc); dwc3_event_buffers_cleanup(dwc);
dwc3_phy_power_off(dwc); dwc3_phy_power_off(dwc);
@@ -984,6 +985,7 @@ static void dwc3_core_exit(struct dwc3 *dwc)
dwc3_clk_disable(dwc); dwc3_clk_disable(dwc);
reset_control_assert(dwc->reset); reset_control_assert(dwc->reset);
} }
EXPORT_SYMBOL_GPL(dwc3_core_exit);
static bool dwc3_core_is_valid(struct dwc3 *dwc) static bool dwc3_core_is_valid(struct dwc3 *dwc)
{ {
@@ -1329,7 +1331,7 @@ static void dwc3_config_threshold(struct dwc3 *dwc)
* *
* Returns 0 on success otherwise negative errno. * Returns 0 on success otherwise negative errno.
*/ */
static int dwc3_core_init(struct dwc3 *dwc) int dwc3_core_init(struct dwc3 *dwc)
{ {
unsigned int hw_mode; unsigned int hw_mode;
u32 reg; u32 reg;
@@ -1482,10 +1484,6 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc3_config_threshold(dwc); dwc3_config_threshold(dwc);
/*
* Modify this for all supported Super Speed ports when
* multiport support is added.
*/
if (hw_mode != DWC3_GHWPARAMS0_MODE_GADGET && if (hw_mode != DWC3_GHWPARAMS0_MODE_GADGET &&
(DWC3_IP_IS(DWC31)) && (DWC3_IP_IS(DWC31)) &&
dwc->maximum_speed == USB_SPEED_SUPER) { dwc->maximum_speed == USB_SPEED_SUPER) {
@@ -1529,6 +1527,7 @@ err_exit_ulpi:
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(dwc3_core_init);
static int dwc3_core_get_phy(struct dwc3 *dwc) static int dwc3_core_get_phy(struct dwc3 *dwc)
{ {
@@ -1667,7 +1666,8 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc)
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE, true); dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE, true);
} }
static void dwc3_get_software_properties(struct dwc3 *dwc) static void dwc3_get_software_properties(struct dwc3 *dwc,
const struct dwc3_properties *properties)
{ {
struct device *tmpdev; struct device *tmpdev;
u16 gsbuscfg0_reqinfo; u16 gsbuscfg0_reqinfo;
@@ -1675,6 +1675,12 @@ static void dwc3_get_software_properties(struct dwc3 *dwc)
dwc->gsbuscfg0_reqinfo = DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED; dwc->gsbuscfg0_reqinfo = DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED;
if (properties->gsbuscfg0_reqinfo !=
DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED) {
dwc->gsbuscfg0_reqinfo = properties->gsbuscfg0_reqinfo;
return;
}
/* /*
* Iterate over all parent nodes for finding swnode properties * Iterate over all parent nodes for finding swnode properties
* and non-DT (non-ABI) properties. * and non-DT (non-ABI) properties.
@@ -2207,7 +2213,7 @@ int dwc3_core_probe(const struct dwc3_probe_data *data)
dwc3_get_properties(dwc); dwc3_get_properties(dwc);
dwc3_get_software_properties(dwc); dwc3_get_software_properties(dwc, &data->properties);
dwc->usb_psy = dwc3_get_usb_power_supply(dwc); dwc->usb_psy = dwc3_get_usb_power_supply(dwc);
if (IS_ERR(dwc->usb_psy)) if (IS_ERR(dwc->usb_psy))
@@ -2300,9 +2306,11 @@ int dwc3_core_probe(const struct dwc3_probe_data *data)
dwc3_check_params(dwc); dwc3_check_params(dwc);
dwc3_debugfs_init(dwc); dwc3_debugfs_init(dwc);
ret = dwc3_core_init_mode(dwc); if (!data->skip_core_init_mode) {
if (ret) ret = dwc3_core_init_mode(dwc);
goto err_exit_debugfs; if (ret)
goto err_exit_debugfs;
}
pm_runtime_put(dev); pm_runtime_put(dev);
@@ -2357,6 +2365,7 @@ static int dwc3_probe(struct platform_device *pdev)
probe_data.dwc = dwc; probe_data.dwc = dwc;
probe_data.res = res; probe_data.res = res;
probe_data.properties = DWC3_DEFAULT_PROPERTIES;
return dwc3_core_probe(&probe_data); return dwc3_core_probe(&probe_data);
} }
@@ -2645,7 +2654,6 @@ int dwc3_runtime_idle(struct dwc3 *dwc)
break; break;
} }
pm_runtime_mark_last_busy(dev);
pm_runtime_autosuspend(dev); pm_runtime_autosuspend(dev);
return 0; return 0;

View File

@@ -515,6 +515,7 @@ static int dwc3_setup_role_switch(struct dwc3 *dwc)
dwc3_role_switch.set = dwc3_usb_role_switch_set; dwc3_role_switch.set = dwc3_usb_role_switch_set;
dwc3_role_switch.get = dwc3_usb_role_switch_get; dwc3_role_switch.get = dwc3_usb_role_switch_get;
dwc3_role_switch.driver_data = dwc; dwc3_role_switch.driver_data = dwc;
dwc3_role_switch.allow_userspace_control = true;
dwc->role_sw = usb_role_switch_register(dwc->dev, &dwc3_role_switch); dwc->role_sw = usb_role_switch_register(dwc->dev, &dwc3_role_switch);
if (IS_ERR(dwc->role_sw)) if (IS_ERR(dwc->role_sw))
return PTR_ERR(dwc->role_sw); return PTR_ERR(dwc->role_sw);

View File

@@ -292,7 +292,6 @@ static int dwc3_ti_probe(struct platform_device *pdev)
/* Setting up autosuspend */ /* Setting up autosuspend */
pm_runtime_set_autosuspend_delay(dev, DWC3_AM62_AUTOSUSPEND_DELAY); pm_runtime_set_autosuspend_delay(dev, DWC3_AM62_AUTOSUSPEND_DELAY);
pm_runtime_use_autosuspend(dev); pm_runtime_use_autosuspend(dev);
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev); pm_runtime_put_autosuspend(dev);
return 0; return 0;

View File

@@ -0,0 +1,489 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Apple Silicon DWC3 Glue driver
* Copyright (C) The Asahi Linux Contributors
*
* Based on:
* - dwc3-qcom.c Copyright (c) 2018, The Linux Foundation. All rights reserved.
* - dwc3-of-simple.c Copyright (c) 2015 Texas Instruments Incorporated - https://www.ti.com
*/
#include <linux/of.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include "glue.h"
/*
* This platform requires a very specific sequence of operations to bring up dwc3 and its USB3 PHY:
*
* 1) The PHY itself has to be brought up; for this we need to know the mode (USB3,
* USB3+DisplayPort, USB4, etc) and the lane orientation. This happens through typec_mux_set.
* 2) DWC3 has to be brought up but we must not touch the gadget area or start xhci yet.
* 3) The PHY bring-up has to be finalized and dwc3's PIPE interface has to be switched to the
* USB3 PHY, this is done inside phy_set_mode.
* 4) We can now initialize xhci or gadget mode.
*
* We can switch 1 and 2 but 3 has to happen after (1 and 2) and 4 has to happen after 3.
*
* And then to bring this all down again:
*
* 1) DWC3 has to exit host or gadget mode and must no longer touch those registers
* 2) The PHY has to switch dwc3's PIPE interface back to the dummy backend
* 3) The PHY itself can be shut down, this happens from typec_mux_set
*
* We also can't transition the PHY from one mode to another while dwc3 is up and running (this is
* slightly wrong, some transitions are possible, others aren't but because we have no documentation
* for this I'd rather play it safe).
*
* After both the PHY and dwc3 are initialized we will only ever see a single "new device connected"
* event. If we just keep them running only the first device plugged in will ever work. XHCI's port
* status register actually does show the correct state but no interrupt ever comes in. In gadget
* mode we don't even get a USBDisconnected event and everything looks like there's still something
* connected on the other end.
* This can be partially explained because the USB2 D+/D- lines are connected through a stateful
* eUSB2 repeater which in turn is controlled by a variant of the TI TPS6598x USB PD chip which
* resets the repeater out-of-band everytime the CC lines are (dis)connected. This then requires a
* PHY reset to make sure the PHY and the eUSB2 repeater state are synchronized again.
*
* And to make this all extra fun: If we get the order of some of this wrong either the port is just
* broken until a phy+dwc3 reset, or it's broken until a full SoC reset (likely because we can't
* reset some parts of the PHY), or some watchdog kicks in after a few seconds and forces a full SoC
* reset (mostly seen this with USB4/Thunderbolt but there's clearly some watchdog that hates
* invalid states).
*
* Hence there's really no good way to keep dwc3 fully up and running after we disconnect a cable
* because then we can't shut down the PHY anymore. And if we kept the PHY running in whatever mode
* it was until the next cable is connected we'd need to tear it all down and bring it back up again
* anyway to detect and use the next device.
*
* Instead, we just shut down everything when a cable is disconnected and transition to
* DWC3_APPLE_NO_CABLE.
* During initial probe we don't have any information about the connected cable and can't bring up
* the PHY properly and thus also can't fully bring up dwc3. Instead, we just keep everything off
* and defer the first dwc3 probe until we get the first cable connected event. Until then we stay
* in DWC3_APPLE_PROBE_PENDING.
* Once a cable is connected we then keep track of the controller mode here by transitioning to
* DWC3_APPLE_HOST or DWC3_APPLE_DEVICE.
*/
enum dwc3_apple_state {
DWC3_APPLE_PROBE_PENDING, /* Before first cable connection, dwc3_core_probe not called */
DWC3_APPLE_NO_CABLE, /* No cable connected, dwc3 suspended after dwc3_core_exit */
DWC3_APPLE_HOST, /* Cable connected, dwc3 in host mode */
DWC3_APPLE_DEVICE, /* Cable connected, dwc3 in device mode */
};
/**
* struct dwc3_apple - Apple-specific DWC3 USB controller
* @dwc: Core DWC3 structure
* @dev: Pointer to the device structure
* @mmio_resource: Resource to be passed to dwc3_core_probe
* @apple_regs: Apple-specific DWC3 registers
* @reset: Reset control
* @role_sw: USB role switch
* @lock: Mutex for synchronizing access
* @state: Current state of the controller, see documentation for the enum for details
*/
struct dwc3_apple {
struct dwc3 dwc;
struct device *dev;
struct resource *mmio_resource;
void __iomem *apple_regs;
struct reset_control *reset;
struct usb_role_switch *role_sw;
struct mutex lock;
enum dwc3_apple_state state;
};
#define to_dwc3_apple(d) container_of((d), struct dwc3_apple, dwc)
/*
* Apple Silicon dwc3 vendor-specific registers
*
* These registers were identified by tracing XNU's memory access patterns and correlating them with
* debug output over serial to determine their names. We don't exactly know what these do but
* without these USB3 devices sometimes don't work.
*/
#define APPLE_DWC3_REGS_START 0xcd00
#define APPLE_DWC3_REGS_END 0xcdff
#define APPLE_DWC3_CIO_LFPS_OFFSET 0xcd38
#define APPLE_DWC3_CIO_LFPS_OFFSET_VALUE 0xf800f80
#define APPLE_DWC3_CIO_BW_NGT_OFFSET 0xcd3c
#define APPLE_DWC3_CIO_BW_NGT_OFFSET_VALUE 0xfc00fc0
#define APPLE_DWC3_CIO_LINK_TIMER 0xcd40
#define APPLE_DWC3_CIO_PENDING_HP_TIMER GENMASK(23, 16)
#define APPLE_DWC3_CIO_PENDING_HP_TIMER_VALUE 0x14
#define APPLE_DWC3_CIO_PM_LC_TIMER GENMASK(15, 8)
#define APPLE_DWC3_CIO_PM_LC_TIMER_VALUE 0xa
#define APPLE_DWC3_CIO_PM_ENTRY_TIMER GENMASK(7, 0)
#define APPLE_DWC3_CIO_PM_ENTRY_TIMER_VALUE 0x10
static inline void dwc3_apple_writel(struct dwc3_apple *appledwc, u32 offset, u32 value)
{
writel(value, appledwc->apple_regs + offset - APPLE_DWC3_REGS_START);
}
static inline u32 dwc3_apple_readl(struct dwc3_apple *appledwc, u32 offset)
{
return readl(appledwc->apple_regs + offset - APPLE_DWC3_REGS_START);
}
static inline void dwc3_apple_mask(struct dwc3_apple *appledwc, u32 offset, u32 mask, u32 value)
{
u32 reg;
reg = dwc3_apple_readl(appledwc, offset);
reg &= ~mask;
reg |= value;
dwc3_apple_writel(appledwc, offset, reg);
}
static void dwc3_apple_setup_cio(struct dwc3_apple *appledwc)
{
dwc3_apple_writel(appledwc, APPLE_DWC3_CIO_LFPS_OFFSET, APPLE_DWC3_CIO_LFPS_OFFSET_VALUE);
dwc3_apple_writel(appledwc, APPLE_DWC3_CIO_BW_NGT_OFFSET,
APPLE_DWC3_CIO_BW_NGT_OFFSET_VALUE);
dwc3_apple_mask(appledwc, APPLE_DWC3_CIO_LINK_TIMER, APPLE_DWC3_CIO_PENDING_HP_TIMER,
FIELD_PREP(APPLE_DWC3_CIO_PENDING_HP_TIMER,
APPLE_DWC3_CIO_PENDING_HP_TIMER_VALUE));
dwc3_apple_mask(appledwc, APPLE_DWC3_CIO_LINK_TIMER, APPLE_DWC3_CIO_PM_LC_TIMER,
FIELD_PREP(APPLE_DWC3_CIO_PM_LC_TIMER, APPLE_DWC3_CIO_PM_LC_TIMER_VALUE));
dwc3_apple_mask(appledwc, APPLE_DWC3_CIO_LINK_TIMER, APPLE_DWC3_CIO_PM_ENTRY_TIMER,
FIELD_PREP(APPLE_DWC3_CIO_PM_ENTRY_TIMER,
APPLE_DWC3_CIO_PM_ENTRY_TIMER_VALUE));
}
static void dwc3_apple_set_ptrcap(struct dwc3_apple *appledwc, u32 mode)
{
guard(spinlock_irqsave)(&appledwc->dwc.lock);
dwc3_set_prtcap(&appledwc->dwc, mode, false);
}
static int dwc3_apple_core_probe(struct dwc3_apple *appledwc)
{
struct dwc3_probe_data probe_data = {};
int ret;
lockdep_assert_held(&appledwc->lock);
WARN_ON_ONCE(appledwc->state != DWC3_APPLE_PROBE_PENDING);
appledwc->dwc.dev = appledwc->dev;
probe_data.dwc = &appledwc->dwc;
probe_data.res = appledwc->mmio_resource;
probe_data.ignore_clocks_and_resets = true;
probe_data.skip_core_init_mode = true;
probe_data.properties = DWC3_DEFAULT_PROPERTIES;
ret = dwc3_core_probe(&probe_data);
if (ret)
return ret;
appledwc->state = DWC3_APPLE_NO_CABLE;
return 0;
}
static int dwc3_apple_core_init(struct dwc3_apple *appledwc)
{
int ret;
lockdep_assert_held(&appledwc->lock);
switch (appledwc->state) {
case DWC3_APPLE_PROBE_PENDING:
ret = dwc3_apple_core_probe(appledwc);
if (ret)
dev_err(appledwc->dev, "Failed to probe DWC3 Core, err=%d\n", ret);
break;
case DWC3_APPLE_NO_CABLE:
ret = dwc3_core_init(&appledwc->dwc);
if (ret)
dev_err(appledwc->dev, "Failed to initialize DWC3 Core, err=%d\n", ret);
break;
default:
/* Unreachable unless there's a bug in this driver */
WARN_ON_ONCE(1);
ret = -EINVAL;
break;
}
return ret;
}
static void dwc3_apple_phy_set_mode(struct dwc3_apple *appledwc, enum phy_mode mode)
{
lockdep_assert_held(&appledwc->lock);
/*
* This platform requires SUSPHY to be enabled here already in order to properly configure
* the PHY and switch dwc3's PIPE interface to USB3 PHY.
*/
dwc3_enable_susphy(&appledwc->dwc, true);
phy_set_mode(appledwc->dwc.usb2_generic_phy[0], mode);
phy_set_mode(appledwc->dwc.usb3_generic_phy[0], mode);
}
static int dwc3_apple_init(struct dwc3_apple *appledwc, enum dwc3_apple_state state)
{
int ret, ret_reset;
lockdep_assert_held(&appledwc->lock);
ret = reset_control_deassert(appledwc->reset);
if (ret) {
dev_err(appledwc->dev, "Failed to deassert reset, err=%d\n", ret);
return ret;
}
ret = dwc3_apple_core_init(appledwc);
if (ret)
goto reset_assert;
/*
* Now that the core is initialized and already went through dwc3_core_soft_reset we can
* configure some unknown Apple-specific settings and then bring up xhci or gadget mode.
*/
dwc3_apple_setup_cio(appledwc);
switch (state) {
case DWC3_APPLE_HOST:
appledwc->dwc.dr_mode = USB_DR_MODE_HOST;
dwc3_apple_set_ptrcap(appledwc, DWC3_GCTL_PRTCAP_HOST);
dwc3_apple_phy_set_mode(appledwc, PHY_MODE_USB_HOST);
ret = dwc3_host_init(&appledwc->dwc);
if (ret) {
dev_err(appledwc->dev, "Failed to initialize host, ret=%d\n", ret);
goto core_exit;
}
break;
case DWC3_APPLE_DEVICE:
appledwc->dwc.dr_mode = USB_DR_MODE_PERIPHERAL;
dwc3_apple_set_ptrcap(appledwc, DWC3_GCTL_PRTCAP_DEVICE);
dwc3_apple_phy_set_mode(appledwc, PHY_MODE_USB_DEVICE);
ret = dwc3_gadget_init(&appledwc->dwc);
if (ret) {
dev_err(appledwc->dev, "Failed to initialize gadget, ret=%d\n", ret);
goto core_exit;
}
break;
default:
/* Unreachable unless there's a bug in this driver */
WARN_ON_ONCE(1);
ret = -EINVAL;
goto core_exit;
}
appledwc->state = state;
return 0;
core_exit:
dwc3_core_exit(&appledwc->dwc);
reset_assert:
ret_reset = reset_control_assert(appledwc->reset);
if (ret_reset)
dev_warn(appledwc->dev, "Failed to assert reset, err=%d\n", ret_reset);
return ret;
}
static int dwc3_apple_exit(struct dwc3_apple *appledwc)
{
int ret = 0;
lockdep_assert_held(&appledwc->lock);
switch (appledwc->state) {
case DWC3_APPLE_PROBE_PENDING:
case DWC3_APPLE_NO_CABLE:
/* Nothing to do if we're already off */
return 0;
case DWC3_APPLE_DEVICE:
dwc3_gadget_exit(&appledwc->dwc);
break;
case DWC3_APPLE_HOST:
dwc3_host_exit(&appledwc->dwc);
break;
}
/*
* This platform requires SUSPHY to be enabled in order to properly power down the PHY
* and switch dwc3's PIPE interface back to a dummy PHY (i.e. no USB3 support and USB2 via
* a different PHY connected through ULPI).
*/
dwc3_enable_susphy(&appledwc->dwc, true);
dwc3_core_exit(&appledwc->dwc);
appledwc->state = DWC3_APPLE_NO_CABLE;
ret = reset_control_assert(appledwc->reset);
if (ret) {
dev_err(appledwc->dev, "Failed to assert reset, err=%d\n", ret);
return ret;
}
return 0;
}
static int dwc3_usb_role_switch_set(struct usb_role_switch *sw, enum usb_role role)
{
struct dwc3_apple *appledwc = usb_role_switch_get_drvdata(sw);
int ret;
guard(mutex)(&appledwc->lock);
/*
* We need to tear all of dwc3 down and re-initialize it every time a cable is
* connected or disconnected or when the mode changes. See the documentation for enum
* dwc3_apple_state for details.
*/
ret = dwc3_apple_exit(appledwc);
if (ret)
return ret;
switch (role) {
case USB_ROLE_NONE:
/* Nothing to do if no cable is connected */
return 0;
case USB_ROLE_HOST:
return dwc3_apple_init(appledwc, DWC3_APPLE_HOST);
case USB_ROLE_DEVICE:
return dwc3_apple_init(appledwc, DWC3_APPLE_DEVICE);
default:
dev_err(appledwc->dev, "Invalid target role: %d\n", role);
return -EINVAL;
}
}
static enum usb_role dwc3_usb_role_switch_get(struct usb_role_switch *sw)
{
struct dwc3_apple *appledwc = usb_role_switch_get_drvdata(sw);
guard(mutex)(&appledwc->lock);
switch (appledwc->state) {
case DWC3_APPLE_HOST:
return USB_ROLE_HOST;
case DWC3_APPLE_DEVICE:
return USB_ROLE_DEVICE;
case DWC3_APPLE_NO_CABLE:
case DWC3_APPLE_PROBE_PENDING:
return USB_ROLE_NONE;
default:
/* Unreachable unless there's a bug in this driver */
dev_err(appledwc->dev, "Invalid internal state: %d\n", appledwc->state);
return USB_ROLE_NONE;
}
}
static int dwc3_apple_setup_role_switch(struct dwc3_apple *appledwc)
{
struct usb_role_switch_desc dwc3_role_switch = { NULL };
dwc3_role_switch.fwnode = dev_fwnode(appledwc->dev);
dwc3_role_switch.set = dwc3_usb_role_switch_set;
dwc3_role_switch.get = dwc3_usb_role_switch_get;
dwc3_role_switch.driver_data = appledwc;
appledwc->role_sw = usb_role_switch_register(appledwc->dev, &dwc3_role_switch);
if (IS_ERR(appledwc->role_sw))
return PTR_ERR(appledwc->role_sw);
return 0;
}
static int dwc3_apple_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct dwc3_apple *appledwc;
int ret;
appledwc = devm_kzalloc(&pdev->dev, sizeof(*appledwc), GFP_KERNEL);
if (!appledwc)
return -ENOMEM;
appledwc->dev = &pdev->dev;
mutex_init(&appledwc->lock);
appledwc->reset = devm_reset_control_get_exclusive(dev, NULL);
if (IS_ERR(appledwc->reset))
return dev_err_probe(&pdev->dev, PTR_ERR(appledwc->reset),
"Failed to get reset control\n");
ret = reset_control_assert(appledwc->reset);
if (ret) {
dev_err(&pdev->dev, "Failed to assert reset, err=%d\n", ret);
return ret;
}
appledwc->mmio_resource = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dwc3-core");
if (!appledwc->mmio_resource) {
dev_err(dev, "Failed to get DWC3 MMIO\n");
return -EINVAL;
}
appledwc->apple_regs = devm_platform_ioremap_resource_byname(pdev, "dwc3-apple");
if (IS_ERR(appledwc->apple_regs))
return dev_err_probe(dev, PTR_ERR(appledwc->apple_regs),
"Failed to map Apple-specific MMIO\n");
/*
* On this platform, DWC3 can only be brought up after parts of the PHY have been
* initialized with knowledge of the target mode and cable orientation from typec_set_mux.
* Since this has not happened here we cannot setup DWC3 yet and instead defer this until
* the first cable is connected. See the documentation for enum dwc3_apple_state for
* details.
*/
appledwc->state = DWC3_APPLE_PROBE_PENDING;
ret = dwc3_apple_setup_role_switch(appledwc);
if (ret)
return dev_err_probe(&pdev->dev, ret, "Failed to setup role switch\n");
return 0;
}
static void dwc3_apple_remove(struct platform_device *pdev)
{
struct dwc3 *dwc = platform_get_drvdata(pdev);
struct dwc3_apple *appledwc = to_dwc3_apple(dwc);
guard(mutex)(&appledwc->lock);
usb_role_switch_unregister(appledwc->role_sw);
/*
* If we're still in DWC3_APPLE_PROBE_PENDING we never got any cable connected event and
* dwc3_core_probe was never called and there's hence no need to call dwc3_core_remove.
* dwc3_apple_exit can be called unconditionally because it checks the state itself.
*/
dwc3_apple_exit(appledwc);
if (appledwc->state != DWC3_APPLE_PROBE_PENDING)
dwc3_core_remove(&appledwc->dwc);
}
static const struct of_device_id dwc3_apple_of_match[] = {
{ .compatible = "apple,t8103-dwc3" },
{}
};
MODULE_DEVICE_TABLE(of, dwc3_apple_of_match);
static struct platform_driver dwc3_apple_driver = {
.probe = dwc3_apple_probe,
.remove = dwc3_apple_remove,
.driver = {
.name = "dwc3-apple",
.of_match_table = dwc3_apple_of_match,
},
};
module_platform_driver(dwc3_apple_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sven Peter <sven@kernel.org>");
MODULE_DESCRIPTION("DesignWare DWC3 Apple Silicon Glue Driver");

View File

@@ -10,8 +10,16 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/reset.h> #include <linux/reset.h>
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
#include "glue.h" #include "glue.h"
#define EIC7700_HSP_BUS_FILTER_EN BIT(0)
#define EIC7700_HSP_BUS_CLKEN_GM BIT(9)
#define EIC7700_HSP_BUS_CLKEN_GS BIT(16)
#define EIC7700_HSP_AXI_LP_XM_CSYSREQ BIT(0)
#define EIC7700_HSP_AXI_LP_XS_CSYSREQ BIT(16)
struct dwc3_generic { struct dwc3_generic {
struct device *dev; struct device *dev;
struct dwc3 dwc; struct dwc3 dwc;
@@ -20,6 +28,11 @@ struct dwc3_generic {
struct reset_control *resets; struct reset_control *resets;
}; };
struct dwc3_generic_config {
int (*init)(struct dwc3_generic *dwc3g);
struct dwc3_properties properties;
};
#define to_dwc3_generic(d) container_of((d), struct dwc3_generic, dwc) #define to_dwc3_generic(d) container_of((d), struct dwc3_generic, dwc)
static void dwc3_generic_reset_control_assert(void *data) static void dwc3_generic_reset_control_assert(void *data)
@@ -27,8 +40,38 @@ static void dwc3_generic_reset_control_assert(void *data)
reset_control_assert(data); reset_control_assert(data);
} }
static int dwc3_eic7700_init(struct dwc3_generic *dwc3g)
{
struct device *dev = dwc3g->dev;
struct regmap *regmap;
u32 hsp_usb_axi_lp;
u32 hsp_usb_bus;
u32 args[2];
u32 val;
regmap = syscon_regmap_lookup_by_phandle_args(dev->of_node,
"eswin,hsp-sp-csr",
ARRAY_SIZE(args), args);
if (IS_ERR(regmap)) {
dev_err(dev, "No hsp-sp-csr phandle specified\n");
return PTR_ERR(regmap);
}
hsp_usb_bus = args[0];
hsp_usb_axi_lp = args[1];
regmap_read(regmap, hsp_usb_bus, &val);
regmap_write(regmap, hsp_usb_bus, val | EIC7700_HSP_BUS_FILTER_EN |
EIC7700_HSP_BUS_CLKEN_GM | EIC7700_HSP_BUS_CLKEN_GS);
regmap_write(regmap, hsp_usb_axi_lp, EIC7700_HSP_AXI_LP_XM_CSYSREQ |
EIC7700_HSP_AXI_LP_XS_CSYSREQ);
return 0;
}
static int dwc3_generic_probe(struct platform_device *pdev) static int dwc3_generic_probe(struct platform_device *pdev)
{ {
const struct dwc3_generic_config *plat_config;
struct dwc3_probe_data probe_data = {}; struct dwc3_probe_data probe_data = {};
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct dwc3_generic *dwc3g; struct dwc3_generic *dwc3g;
@@ -75,6 +118,22 @@ static int dwc3_generic_probe(struct platform_device *pdev)
probe_data.dwc = &dwc3g->dwc; probe_data.dwc = &dwc3g->dwc;
probe_data.res = res; probe_data.res = res;
probe_data.ignore_clocks_and_resets = true; probe_data.ignore_clocks_and_resets = true;
plat_config = of_device_get_match_data(dev);
if (!plat_config) {
probe_data.properties = DWC3_DEFAULT_PROPERTIES;
goto core_probe;
}
probe_data.properties = plat_config->properties;
if (plat_config->init) {
ret = plat_config->init(dwc3g);
if (ret)
return dev_err_probe(dev, ret,
"failed to init platform\n");
}
core_probe:
ret = dwc3_core_probe(&probe_data); ret = dwc3_core_probe(&probe_data);
if (ret) if (ret)
return dev_err_probe(dev, ret, "failed to register DWC3 Core\n"); return dev_err_probe(dev, ret, "failed to register DWC3 Core\n");
@@ -142,8 +201,19 @@ static const struct dev_pm_ops dwc3_generic_dev_pm_ops = {
dwc3_generic_runtime_idle) dwc3_generic_runtime_idle)
}; };
static const struct dwc3_generic_config fsl_ls1028_dwc3 = {
.properties.gsbuscfg0_reqinfo = 0x2222,
};
static const struct dwc3_generic_config eic7700_dwc3 = {
.init = dwc3_eic7700_init,
.properties = DWC3_DEFAULT_PROPERTIES,
};
static const struct of_device_id dwc3_generic_of_match[] = { static const struct of_device_id dwc3_generic_of_match[] = {
{ .compatible = "spacemit,k1-dwc3", }, { .compatible = "spacemit,k1-dwc3", },
{ .compatible = "fsl,ls1028a-dwc3", &fsl_ls1028_dwc3},
{ .compatible = "eswin,eic7700-dwc3", &eic7700_dwc3},
{ /* sentinel */ } { /* sentinel */ }
}; };
MODULE_DEVICE_TABLE(of, dwc3_generic_of_match); MODULE_DEVICE_TABLE(of, dwc3_generic_of_match);

View File

@@ -312,7 +312,6 @@ static int dwc3_imx8mp_resume(struct dwc3_imx8mp *dwc3_imx, pm_message_t msg)
if (dwc3_imx->wakeup_pending) { if (dwc3_imx->wakeup_pending) {
dwc3_imx->wakeup_pending = false; dwc3_imx->wakeup_pending = false;
if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE) { if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE) {
pm_runtime_mark_last_busy(dwc->dev);
pm_runtime_put_autosuspend(dwc->dev); pm_runtime_put_autosuspend(dwc->dev);
} else { } else {
/* /*

View File

@@ -323,7 +323,6 @@ static void dwc3_pci_resume_work(struct work_struct *work)
return; return;
} }
pm_runtime_mark_last_busy(&dwc3->dev);
pm_runtime_put_sync_autosuspend(&dwc3->dev); pm_runtime_put_sync_autosuspend(&dwc3->dev);
} }
#endif #endif

View File

@@ -704,6 +704,7 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
probe_data.dwc = &qcom->dwc; probe_data.dwc = &qcom->dwc;
probe_data.res = &res; probe_data.res = &res;
probe_data.ignore_clocks_and_resets = true; probe_data.ignore_clocks_and_resets = true;
probe_data.properties = DWC3_DEFAULT_PROPERTIES;
ret = dwc3_core_probe(&probe_data); ret = dwc3_core_probe(&probe_data);
if (ret) { if (ret) {
ret = dev_err_probe(dev, ret, "failed to register DWC3 Core\n"); ret = dev_err_probe(dev, ret, "failed to register DWC3 Core\n");

View File

@@ -383,7 +383,6 @@ static int __maybe_unused dwc3_xlnx_runtime_resume(struct device *dev)
static int __maybe_unused dwc3_xlnx_runtime_idle(struct device *dev) static int __maybe_unused dwc3_xlnx_runtime_idle(struct device *dev)
{ {
pm_runtime_mark_last_busy(dev);
pm_runtime_autosuspend(dev); pm_runtime_autosuspend(dev);
return 0; return 0;

View File

@@ -3879,7 +3879,7 @@ static void dwc3_gadget_endpoint_stream_event(struct dwc3_ep *dep,
case DEPEVT_STREAM_NOSTREAM: case DEPEVT_STREAM_NOSTREAM:
dep->flags &= ~DWC3_EP_STREAM_PRIMED; dep->flags &= ~DWC3_EP_STREAM_PRIMED;
if (dep->flags & DWC3_EP_FORCE_RESTART_STREAM) if (dep->flags & DWC3_EP_FORCE_RESTART_STREAM)
queue_delayed_work(system_wq, &dep->nostream_work, queue_delayed_work(system_percpu_wq, &dep->nostream_work,
msecs_to_jiffies(100)); msecs_to_jiffies(100));
break; break;
} }
@@ -4817,6 +4817,7 @@ err1:
err0: err0:
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(dwc3_gadget_init);
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
@@ -4835,6 +4836,7 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
dma_free_coherent(dwc->sysdev, sizeof(*dwc->ep0_trb) * 2, dma_free_coherent(dwc->sysdev, sizeof(*dwc->ep0_trb) * 2,
dwc->ep0_trb, dwc->ep0_trb_addr); dwc->ep0_trb, dwc->ep0_trb_addr);
} }
EXPORT_SYMBOL_GPL(dwc3_gadget_exit);
int dwc3_gadget_suspend(struct dwc3 *dwc) int dwc3_gadget_suspend(struct dwc3 *dwc)
{ {

View File

@@ -9,22 +9,66 @@
#include <linux/types.h> #include <linux/types.h>
#include "core.h" #include "core.h"
/**
* dwc3_properties: DWC3 core properties
* @gsbuscfg0_reqinfo: Value to be programmed in the GSBUSCFG0.REQINFO field
*/
struct dwc3_properties {
u32 gsbuscfg0_reqinfo;
};
#define DWC3_DEFAULT_PROPERTIES ((struct dwc3_properties){ \
.gsbuscfg0_reqinfo = DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED, \
})
/** /**
* dwc3_probe_data: Initialization parameters passed to dwc3_core_probe() * dwc3_probe_data: Initialization parameters passed to dwc3_core_probe()
* @dwc: Reference to dwc3 context structure * @dwc: Reference to dwc3 context structure
* @res: resource for the DWC3 core mmio region * @res: resource for the DWC3 core mmio region
* @ignore_clocks_and_resets: clocks and resets defined for the device should * @ignore_clocks_and_resets: clocks and resets defined for the device should
* be ignored by the DWC3 core, as they are managed by the glue * be ignored by the DWC3 core, as they are managed by the glue
* @skip_core_init_mode: Skip the finial initialization of the target mode, as
* it must be managed by the glue
* @properties: dwc3 software manage properties
*/ */
struct dwc3_probe_data { struct dwc3_probe_data {
struct dwc3 *dwc; struct dwc3 *dwc;
struct resource *res; struct resource *res;
bool ignore_clocks_and_resets; bool ignore_clocks_and_resets;
bool skip_core_init_mode;
struct dwc3_properties properties;
}; };
/**
* dwc3_core_probe - Initialize the core dwc3 driver
* @data: Initialization and configuration parameters for the controller
*
* Initializes the DesignWare USB3 core driver by setting up resources,
* registering interrupts, performing hardware setup, and preparing
* the controller for operation in the appropriate mode (host, gadget,
* or OTG). This is the main initialization function called by glue
* layer drivers to set up the core controller.
*
* Return: 0 on success, negative error code on failure
*/
int dwc3_core_probe(const struct dwc3_probe_data *data); int dwc3_core_probe(const struct dwc3_probe_data *data);
/**
* dwc3_core_remove - Deinitialize and remove the core dwc3 driver
* @dwc: Pointer to DWC3 controller context
*
* Cleans up resources and disables the dwc3 core driver. This should be called
* during driver removal or when the glue layer needs to shut down the
* controller completely.
*/
void dwc3_core_remove(struct dwc3 *dwc); void dwc3_core_remove(struct dwc3 *dwc);
/*
* The following callbacks are provided for glue drivers to call from their
* own pm callbacks provided in struct dev_pm_ops. Glue drivers can perform
* platform-specific work before or after calling these functions and delegate
* the core suspend/resume operations to the core driver.
*/
int dwc3_runtime_suspend(struct dwc3 *dwc); int dwc3_runtime_suspend(struct dwc3 *dwc);
int dwc3_runtime_resume(struct dwc3 *dwc); int dwc3_runtime_resume(struct dwc3 *dwc);
int dwc3_runtime_idle(struct dwc3 *dwc); int dwc3_runtime_idle(struct dwc3 *dwc);
@@ -33,4 +77,117 @@ int dwc3_pm_resume(struct dwc3 *dwc);
void dwc3_pm_complete(struct dwc3 *dwc); void dwc3_pm_complete(struct dwc3 *dwc);
int dwc3_pm_prepare(struct dwc3 *dwc); int dwc3_pm_prepare(struct dwc3 *dwc);
/* All of the following functions must only be used with skip_core_init_mode */
/**
* dwc3_core_init - Initialize DWC3 core hardware
* @dwc: Pointer to DWC3 controller context
*
* Configures and initializes the core hardware, usually done by dwc3_core_probe.
* This function is provided for platforms that use skip_core_init_mode and need
* to finalize the core initialization after some platform-specific setup.
* It must only be called when using skip_core_init_mode and before
* dwc3_host_init or dwc3_gadget_init.
*
* Return: 0 on success, negative error code on failure
*/
int dwc3_core_init(struct dwc3 *dwc);
/**
* dwc3_core_exit - Shut down DWC3 core hardware
* @dwc: Pointer to DWC3 controller context
*
* Disables and cleans up the core hardware state. This is usually handled
* internally by dwc3 and must only be called when using skip_core_init_mode
* and only after dwc3_core_init. Afterwards, dwc3_core_init may be called
* again.
*/
void dwc3_core_exit(struct dwc3 *dwc);
/**
* dwc3_host_init - Initialize host mode operation
* @dwc: Pointer to DWC3 controller context
*
* Initializes the controller for USB host mode operation, usually done by
* dwc3_core_probe or from within the dwc3 USB role switch callback.
* This function is provided for platforms that use skip_core_init_mode and need
* to finalize the host initialization after some platform-specific setup.
* It must not be called before dwc3_core_init or when skip_core_init_mode is
* not used. It must also not be called when gadget or host mode has already
* been initialized.
*
* Return: 0 on success, negative error code on failure
*/
int dwc3_host_init(struct dwc3 *dwc);
/**
* dwc3_host_exit - Shut down host mode operation
* @dwc: Pointer to DWC3 controller context
*
* Disables and cleans up host mode resources, usually done by
* the dwc3 USB role switch callback before switching controller mode.
* It must only be called when skip_core_init_mode is used and only after
* dwc3_host_init.
*/
void dwc3_host_exit(struct dwc3 *dwc);
/**
* dwc3_gadget_init - Initialize gadget mode operation
* @dwc: Pointer to DWC3 controller context
*
* Initializes the controller for USB gadget mode operation, usually done by
* dwc3_core_probe or from within the dwc3 USB role switch callback. This
* function is provided for platforms that use skip_core_init_mode and need to
* finalize the gadget initialization after some platform-specific setup.
* It must not be called before dwc3_core_init or when skip_core_init_mode is
* not used. It must also not be called when gadget or host mode has already
* been initialized.
*
* Return: 0 on success, negative error code on failure
*/
int dwc3_gadget_init(struct dwc3 *dwc);
/**
* dwc3_gadget_exit - Shut down gadget mode operation
* @dwc: Pointer to DWC3 controller context
*
* Disables and cleans up gadget mode resources, usually done by
* the dwc3 USB role switch callback before switching controller mode.
* It must only be called when skip_core_init_mode is used and only after
* dwc3_gadget_init.
*/
void dwc3_gadget_exit(struct dwc3 *dwc);
/**
* dwc3_enable_susphy - Control SUSPHY status for all USB ports
* @dwc: Pointer to DWC3 controller context
* @enable: True to enable SUSPHY, false to disable
*
* Enables or disables the USB3 PHY SUSPEND and USB2 PHY SUSPHY feature for
* all available ports.
* This is usually handled by the dwc3 core code and should only be used
* when skip_core_init_mode is used and the glue layer needs to manage SUSPHY
* settings itself, e.g., due to platform-specific requirements during mode
* switches.
*/
void dwc3_enable_susphy(struct dwc3 *dwc, bool enable);
/**
* dwc3_set_prtcap - Set the USB controller PRTCAP mode
* @dwc: Pointer to DWC3 controller context
* @mode: Target mode, must be one of DWC3_GCTL_PRTCAP_{HOST,DEVICE,OTG}
* @ignore_susphy: If true, skip disabling the SUSPHY and keep the current state
*
* Updates PRTCAP of the controller and current_dr_role inside the dwc3
* structure. For DRD controllers, this also disables SUSPHY unless explicitly
* told to skip via the ignore_susphy parameter.
*
* This is usually handled by the dwc3 core code and should only be used
* when skip_core_init_mode is used and the glue layer needs to manage mode
* transitions itself due to platform-specific requirements. It must be called
* with the correct mode before calling dwc3_host_init or dwc3_gadget_init.
*/
void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy);
#endif #endif

View File

@@ -37,7 +37,10 @@ static void dwc3_power_off_all_roothub_ports(struct dwc3 *dwc)
/* xhci regs are not mapped yet, do it temporarily here */ /* xhci regs are not mapped yet, do it temporarily here */
if (dwc->xhci_resources[0].start) { if (dwc->xhci_resources[0].start) {
xhci_regs = ioremap(dwc->xhci_resources[0].start, DWC3_XHCI_REGS_END); if (dwc->xhci_resources[0].flags & IORESOURCE_MEM_NONPOSTED)
xhci_regs = ioremap_np(dwc->xhci_resources[0].start, DWC3_XHCI_REGS_END);
else
xhci_regs = ioremap(dwc->xhci_resources[0].start, DWC3_XHCI_REGS_END);
if (!xhci_regs) { if (!xhci_regs) {
dev_err(dwc->dev, "Failed to ioremap xhci_regs\n"); dev_err(dwc->dev, "Failed to ioremap xhci_regs\n");
return; return;
@@ -217,6 +220,7 @@ err:
platform_device_put(xhci); platform_device_put(xhci);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(dwc3_host_init);
void dwc3_host_exit(struct dwc3 *dwc) void dwc3_host_exit(struct dwc3 *dwc)
{ {
@@ -227,3 +231,4 @@ void dwc3_host_exit(struct dwc3 *dwc)
platform_device_unregister(dwc->xhci); platform_device_unregister(dwc->xhci);
dwc->xhci = NULL; dwc->xhci = NULL;
} }
EXPORT_SYMBOL_GPL(dwc3_host_exit);

View File

@@ -1332,9 +1332,7 @@ static void ffs_dmabuf_release(struct kref *ref)
struct dma_buf *dmabuf = attach->dmabuf; struct dma_buf *dmabuf = attach->dmabuf;
pr_vdebug("FFS DMABUF release\n"); pr_vdebug("FFS DMABUF release\n");
dma_resv_lock(dmabuf->resv, NULL); dma_buf_unmap_attachment_unlocked(attach, priv->sgt, priv->dir);
dma_buf_unmap_attachment(attach, priv->sgt, priv->dir);
dma_resv_unlock(dmabuf->resv);
dma_buf_detach(attach->dmabuf, attach); dma_buf_detach(attach->dmabuf, attach);
dma_buf_put(dmabuf); dma_buf_put(dmabuf);

View File

@@ -1272,8 +1272,7 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
INIT_WORK(&hidg->work, get_report_workqueue_handler); INIT_WORK(&hidg->work, get_report_workqueue_handler);
hidg->workqueue = alloc_workqueue("report_work", hidg->workqueue = alloc_workqueue("report_work",
WQ_FREEZABLE | WQ_FREEZABLE | WQ_MEM_RECLAIM | WQ_PERCPU,
WQ_MEM_RECLAIM,
1); 1);
if (!hidg->workqueue) { if (!hidg->workqueue) {

View File

@@ -40,6 +40,7 @@ MODULE_LICENSE("GPL");
static DEFINE_IDA(driver_id_numbers); static DEFINE_IDA(driver_id_numbers);
#define DRIVER_DRIVER_NAME_LENGTH_MAX 32 #define DRIVER_DRIVER_NAME_LENGTH_MAX 32
#define USB_RAW_IO_LENGTH_MAX KMALLOC_MAX_SIZE
#define RAW_EVENT_QUEUE_SIZE 16 #define RAW_EVENT_QUEUE_SIZE 16
@@ -667,6 +668,8 @@ static void *raw_alloc_io_data(struct usb_raw_ep_io *io, void __user *ptr,
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
if (!usb_raw_io_flags_valid(io->flags)) if (!usb_raw_io_flags_valid(io->flags))
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
if (io->length > USB_RAW_IO_LENGTH_MAX)
return ERR_PTR(-EINVAL);
if (get_from_user) if (get_from_user)
data = memdup_user(ptr + sizeof(*io), io->length); data = memdup_user(ptr + sizeof(*io), io->length);
else { else {

View File

@@ -147,6 +147,12 @@ static struct usb_gadget_strings *dev_strings[] = {
NULL, NULL,
}; };
static struct usb_function *func_lb;
static struct usb_function_instance *func_inst_lb;
static struct usb_function *func_ss;
static struct usb_function_instance *func_inst_ss;
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
static struct timer_list autoresume_timer; static struct timer_list autoresume_timer;
@@ -156,6 +162,7 @@ static void zero_autoresume(struct timer_list *unused)
{ {
struct usb_composite_dev *cdev = autoresume_cdev; struct usb_composite_dev *cdev = autoresume_cdev;
struct usb_gadget *g = cdev->gadget; struct usb_gadget *g = cdev->gadget;
int status;
/* unconfigured devices can't issue wakeups */ /* unconfigured devices can't issue wakeups */
if (!cdev->config) if (!cdev->config)
@@ -165,10 +172,18 @@ static void zero_autoresume(struct timer_list *unused)
* more significant than just a timer firing; likely * more significant than just a timer firing; likely
* because of some direct user request. * because of some direct user request.
*/ */
if (g->speed != USB_SPEED_UNKNOWN) { if (g->speed == USB_SPEED_UNKNOWN)
int status = usb_gadget_wakeup(g); return;
INFO(cdev, "%s --> %d\n", __func__, status);
if (g->speed >= USB_SPEED_SUPER) {
if (loopdefault)
status = usb_func_wakeup(func_lb);
else
status = usb_func_wakeup(func_ss);
} else {
status = usb_gadget_wakeup(g);
} }
INFO(cdev, "%s --> %d\n", __func__, status);
} }
static void zero_suspend(struct usb_composite_dev *cdev) static void zero_suspend(struct usb_composite_dev *cdev)
@@ -206,9 +221,6 @@ static struct usb_configuration loopback_driver = {
/* .iConfiguration = DYNAMIC */ /* .iConfiguration = DYNAMIC */
}; };
static struct usb_function *func_ss;
static struct usb_function_instance *func_inst_ss;
static int ss_config_setup(struct usb_configuration *c, static int ss_config_setup(struct usb_configuration *c,
const struct usb_ctrlrequest *ctrl) const struct usb_ctrlrequest *ctrl)
{ {
@@ -248,9 +260,6 @@ module_param_named(isoc_maxburst, gzero_options.isoc_maxburst, uint,
S_IRUGO|S_IWUSR); S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(isoc_maxburst, "0 - 15 (ss only)"); MODULE_PARM_DESC(isoc_maxburst, "0 - 15 (ss only)");
static struct usb_function *func_lb;
static struct usb_function_instance *func_inst_lb;
module_param_named(qlen, gzero_options.qlen, uint, S_IRUGO|S_IWUSR); module_param_named(qlen, gzero_options.qlen, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(qlen, "depth of loopback queue"); MODULE_PARM_DESC(qlen, "depth of loopback queue");

View File

@@ -2415,7 +2415,6 @@ int cdns2_gadget_resume(struct cdns2_device *pdev, bool hibernated)
void cdns2_gadget_remove(struct cdns2_device *pdev) void cdns2_gadget_remove(struct cdns2_device *pdev)
{ {
pm_runtime_mark_last_busy(pdev->dev);
pm_runtime_put_autosuspend(pdev->dev); pm_runtime_put_autosuspend(pdev->dev);
usb_del_gadget(&pdev->gadget); usb_del_gadget(&pdev->gadget);

View File

@@ -1558,12 +1558,6 @@ static int __tegra_xudc_ep_set_halt(struct tegra_xudc_ep *ep, bool halt)
return -ENOTSUPP; return -ENOTSUPP;
} }
if (!!(xudc_readl(xudc, EP_HALT) & BIT(ep->index)) == halt) {
dev_dbg(xudc->dev, "EP %u already %s\n", ep->index,
halt ? "halted" : "not halted");
return 0;
}
if (halt) { if (halt) {
ep_halt(xudc, ep->index); ep_halt(xudc, ep->index);
} else { } else {

View File

@@ -27,6 +27,7 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/reset.h> #include <linux/reset.h>
#include <linux/sys_soc.h> #include <linux/sys_soc.h>
@@ -111,8 +112,7 @@ static void ehci_platform_power_off(struct platform_device *dev)
int clk; int clk;
for (clk = EHCI_MAX_CLKS - 1; clk >= 0; clk--) for (clk = EHCI_MAX_CLKS - 1; clk >= 0; clk--)
if (priv->clks[clk]) clk_disable_unprepare(priv->clks[clk]);
clk_disable_unprepare(priv->clks[clk]);
} }
static struct hc_driver __read_mostly ehci_platform_hc_driver; static struct hc_driver __read_mostly ehci_platform_hc_driver;
@@ -239,9 +239,11 @@ static int ehci_platform_probe(struct platform_device *dev)
struct usb_hcd *hcd; struct usb_hcd *hcd;
struct resource *res_mem; struct resource *res_mem;
struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev); struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev);
const struct of_device_id *match;
struct ehci_platform_priv *priv; struct ehci_platform_priv *priv;
struct ehci_hcd *ehci; struct ehci_hcd *ehci;
int err, irq, clk = 0; int err, irq, clk = 0;
bool dma_mask_64;
if (usb_disabled()) if (usb_disabled())
return -ENODEV; return -ENODEV;
@@ -253,8 +255,13 @@ static int ehci_platform_probe(struct platform_device *dev)
if (!pdata) if (!pdata)
pdata = &ehci_platform_defaults; pdata = &ehci_platform_defaults;
dma_mask_64 = pdata->dma_mask_64;
match = of_match_device(dev->dev.driver->of_match_table, &dev->dev);
if (match && match->data)
dma_mask_64 = true;
err = dma_coerce_mask_and_coherent(&dev->dev, err = dma_coerce_mask_and_coherent(&dev->dev,
pdata->dma_mask_64 ? DMA_BIT_MASK(64) : DMA_BIT_MASK(32)); dma_mask_64 ? DMA_BIT_MASK(64) : DMA_BIT_MASK(32));
if (err) { if (err) {
dev_err(&dev->dev, "Error: DMA mask configuration failed\n"); dev_err(&dev->dev, "Error: DMA mask configuration failed\n");
return err; return err;
@@ -298,7 +305,9 @@ static int ehci_platform_probe(struct platform_device *dev)
if (of_device_is_compatible(dev->dev.of_node, if (of_device_is_compatible(dev->dev.of_node,
"aspeed,ast2500-ehci") || "aspeed,ast2500-ehci") ||
of_device_is_compatible(dev->dev.of_node, of_device_is_compatible(dev->dev.of_node,
"aspeed,ast2600-ehci")) "aspeed,ast2600-ehci") ||
of_device_is_compatible(dev->dev.of_node,
"aspeed,ast2700-ehci"))
ehci->is_aspeed = 1; ehci->is_aspeed = 1;
if (soc_device_match(quirk_poll_match)) if (soc_device_match(quirk_poll_match))
@@ -445,6 +454,17 @@ static int __maybe_unused ehci_platform_suspend(struct device *dev)
if (pdata->power_suspend) if (pdata->power_suspend)
pdata->power_suspend(pdev); pdata->power_suspend(pdev);
ret = reset_control_assert(priv->rsts);
if (ret) {
if (pdata->power_on)
pdata->power_on(pdev);
ehci_resume(hcd, false);
if (priv->quirk_poll)
quirk_poll_init(priv);
}
return ret; return ret;
} }
@@ -455,11 +475,18 @@ static int __maybe_unused ehci_platform_resume(struct device *dev)
struct platform_device *pdev = to_platform_device(dev); struct platform_device *pdev = to_platform_device(dev);
struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd);
struct device *companion_dev; struct device *companion_dev;
int err;
err = reset_control_deassert(priv->rsts);
if (err)
return err;
if (pdata->power_on) { if (pdata->power_on) {
int err = pdata->power_on(pdev); err = pdata->power_on(pdev);
if (err < 0) if (err < 0) {
reset_control_assert(priv->rsts);
return err; return err;
}
} }
companion_dev = usb_of_get_companion_dev(hcd->self.controller); companion_dev = usb_of_get_companion_dev(hcd->self.controller);
@@ -485,6 +512,7 @@ static const struct of_device_id vt8500_ehci_ids[] = {
{ .compatible = "wm,prizm-ehci", }, { .compatible = "wm,prizm-ehci", },
{ .compatible = "generic-ehci", }, { .compatible = "generic-ehci", },
{ .compatible = "cavium,octeon-6335-ehci", }, { .compatible = "cavium,octeon-6335-ehci", },
{ .compatible = "aspeed,ast2700-ehci", .data = (void *)1 },
{} {}
}; };
MODULE_DEVICE_TABLE(of, vt8500_ehci_ids); MODULE_DEVICE_TABLE(of, vt8500_ehci_ids);

View File

@@ -18,7 +18,6 @@
#include <linux/of.h> #include <linux/of.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/phy/phy.h> #include <linux/phy/phy.h>
#include <linux/platform_data/usb-davinci.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/usb.h> #include <linux/usb.h>
#include <linux/usb/hcd.h> #include <linux/usb/hcd.h>
@@ -166,17 +165,6 @@ static int ohci_da8xx_has_oci(struct usb_hcd *hcd)
return 0; return 0;
} }
static int ohci_da8xx_has_potpgt(struct usb_hcd *hcd)
{
struct device *dev = hcd->self.controller;
struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev);
if (hub && hub->potpgt)
return 1;
return 0;
}
static int ohci_da8xx_regulator_event(struct notifier_block *nb, static int ohci_da8xx_regulator_event(struct notifier_block *nb,
unsigned long event, void *data) unsigned long event, void *data)
{ {
@@ -228,7 +216,6 @@ static int ohci_da8xx_register_notify(struct usb_hcd *hcd)
static int ohci_da8xx_reset(struct usb_hcd *hcd) static int ohci_da8xx_reset(struct usb_hcd *hcd)
{ {
struct device *dev = hcd->self.controller; struct device *dev = hcd->self.controller;
struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev);
struct ohci_hcd *ohci = hcd_to_ohci(hcd); struct ohci_hcd *ohci = hcd_to_ohci(hcd);
int result; int result;
u32 rh_a; u32 rh_a;
@@ -266,10 +253,6 @@ static int ohci_da8xx_reset(struct usb_hcd *hcd)
rh_a &= ~RH_A_NOCP; rh_a &= ~RH_A_NOCP;
rh_a |= RH_A_OCPM; rh_a |= RH_A_OCPM;
} }
if (ohci_da8xx_has_potpgt(hcd)) {
rh_a &= ~RH_A_POTPGT;
rh_a |= hub->potpgt << 24;
}
ohci_writel(ohci, rh_a, &ohci->regs->roothub.a); ohci_writel(ohci, rh_a, &ohci->regs->roothub.a);
return result; return result;

View File

@@ -69,8 +69,7 @@ static void ohci_platform_power_off(struct platform_device *dev)
int clk; int clk;
for (clk = OHCI_MAX_CLKS - 1; clk >= 0; clk--) for (clk = OHCI_MAX_CLKS - 1; clk >= 0; clk--)
if (priv->clks[clk]) clk_disable_unprepare(priv->clks[clk]);
clk_disable_unprepare(priv->clks[clk]);
} }
static struct hc_driver __read_mostly ohci_platform_hc_driver; static struct hc_driver __read_mostly ohci_platform_hc_driver;
@@ -271,6 +270,7 @@ static int ohci_platform_suspend(struct device *dev)
struct usb_hcd *hcd = dev_get_drvdata(dev); struct usb_hcd *hcd = dev_get_drvdata(dev);
struct usb_ohci_pdata *pdata = dev->platform_data; struct usb_ohci_pdata *pdata = dev->platform_data;
struct platform_device *pdev = to_platform_device(dev); struct platform_device *pdev = to_platform_device(dev);
struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd);
bool do_wakeup = device_may_wakeup(dev); bool do_wakeup = device_may_wakeup(dev);
int ret; int ret;
@@ -281,6 +281,14 @@ static int ohci_platform_suspend(struct device *dev)
if (pdata->power_suspend) if (pdata->power_suspend)
pdata->power_suspend(pdev); pdata->power_suspend(pdev);
ret = reset_control_assert(priv->resets);
if (ret) {
if (pdata->power_on)
pdata->power_on(pdev);
ohci_resume(hcd, false);
}
return ret; return ret;
} }
@@ -289,11 +297,19 @@ static int ohci_platform_resume_common(struct device *dev, bool hibernated)
struct usb_hcd *hcd = dev_get_drvdata(dev); struct usb_hcd *hcd = dev_get_drvdata(dev);
struct usb_ohci_pdata *pdata = dev_get_platdata(dev); struct usb_ohci_pdata *pdata = dev_get_platdata(dev);
struct platform_device *pdev = to_platform_device(dev); struct platform_device *pdev = to_platform_device(dev);
struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd);
int err;
err = reset_control_deassert(priv->resets);
if (err)
return err;
if (pdata->power_on) { if (pdata->power_on) {
int err = pdata->power_on(pdev); err = pdata->power_on(pdev);
if (err < 0) if (err < 0) {
reset_control_assert(priv->resets);
return err; return err;
}
} }
ohci_resume(hcd, hibernated); ohci_resume(hcd, hibernated);

View File

@@ -445,6 +445,7 @@ struct uhci_hcd {
short load[MAX_PHASE]; /* Periodic allocations */ short load[MAX_PHASE]; /* Periodic allocations */
struct clk *clk; /* (optional) clock source */ struct clk *clk; /* (optional) clock source */
struct reset_control *rsts; /* (optional) clock reset */
/* Reset host controller */ /* Reset host controller */
void (*reset_hc) (struct uhci_hcd *uhci); void (*reset_hc) (struct uhci_hcd *uhci);

View File

@@ -11,6 +11,7 @@
#include <linux/of.h> #include <linux/of.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/reset.h>
static int uhci_platform_init(struct usb_hcd *hcd) static int uhci_platform_init(struct usb_hcd *hcd)
{ {
@@ -67,6 +68,7 @@ static const struct hc_driver uhci_platform_hc_driver = {
static int uhci_hcd_platform_probe(struct platform_device *pdev) static int uhci_hcd_platform_probe(struct platform_device *pdev)
{ {
struct device_node *np = pdev->dev.of_node; struct device_node *np = pdev->dev.of_node;
bool dma_mask_64 = false;
struct usb_hcd *hcd; struct usb_hcd *hcd;
struct uhci_hcd *uhci; struct uhci_hcd *uhci;
struct resource *res; struct resource *res;
@@ -80,7 +82,11 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev)
* Since shared usb code relies on it, set it here for now. * Since shared usb code relies on it, set it here for now.
* Once we have dma capability bindings this can go away. * Once we have dma capability bindings this can go away.
*/ */
ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); if (of_device_get_match_data(&pdev->dev))
dma_mask_64 = true;
ret = dma_coerce_mask_and_coherent(&pdev->dev,
dma_mask_64 ? DMA_BIT_MASK(64) : DMA_BIT_MASK(32));
if (ret) if (ret)
return ret; return ret;
@@ -113,7 +119,8 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev)
} }
if (of_device_is_compatible(np, "aspeed,ast2400-uhci") || if (of_device_is_compatible(np, "aspeed,ast2400-uhci") ||
of_device_is_compatible(np, "aspeed,ast2500-uhci") || of_device_is_compatible(np, "aspeed,ast2500-uhci") ||
of_device_is_compatible(np, "aspeed,ast2600-uhci")) { of_device_is_compatible(np, "aspeed,ast2600-uhci") ||
of_device_is_compatible(np, "aspeed,ast2700-uhci")) {
uhci->is_aspeed = 1; uhci->is_aspeed = 1;
dev_info(&pdev->dev, dev_info(&pdev->dev,
"Enabled Aspeed implementation workarounds\n"); "Enabled Aspeed implementation workarounds\n");
@@ -132,17 +139,28 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev)
goto err_rmr; goto err_rmr;
} }
uhci->rsts = devm_reset_control_array_get_optional_shared(&pdev->dev);
if (IS_ERR(uhci->rsts)) {
ret = PTR_ERR(uhci->rsts);
goto err_clk;
}
ret = reset_control_deassert(uhci->rsts);
if (ret)
goto err_clk;
ret = platform_get_irq(pdev, 0); ret = platform_get_irq(pdev, 0);
if (ret < 0) if (ret < 0)
goto err_clk; goto err_reset;
ret = usb_add_hcd(hcd, ret, IRQF_SHARED); ret = usb_add_hcd(hcd, ret, IRQF_SHARED);
if (ret) if (ret)
goto err_clk; goto err_reset;
device_wakeup_enable(hcd->self.controller); device_wakeup_enable(hcd->self.controller);
return 0; return 0;
err_reset:
reset_control_assert(uhci->rsts);
err_clk: err_clk:
clk_disable_unprepare(uhci->clk); clk_disable_unprepare(uhci->clk);
err_rmr: err_rmr:
@@ -156,6 +174,7 @@ static void uhci_hcd_platform_remove(struct platform_device *pdev)
struct usb_hcd *hcd = platform_get_drvdata(pdev); struct usb_hcd *hcd = platform_get_drvdata(pdev);
struct uhci_hcd *uhci = hcd_to_uhci(hcd); struct uhci_hcd *uhci = hcd_to_uhci(hcd);
reset_control_assert(uhci->rsts);
clk_disable_unprepare(uhci->clk); clk_disable_unprepare(uhci->clk);
usb_remove_hcd(hcd); usb_remove_hcd(hcd);
usb_put_hcd(hcd); usb_put_hcd(hcd);
@@ -178,6 +197,7 @@ static void uhci_hcd_platform_shutdown(struct platform_device *op)
static const struct of_device_id platform_uhci_ids[] = { static const struct of_device_id platform_uhci_ids[] = {
{ .compatible = "generic-uhci", }, { .compatible = "generic-uhci", },
{ .compatible = "platform-uhci", }, { .compatible = "platform-uhci", },
{ .compatible = "aspeed,ast2700-uhci", .data = (void *)1 },
{} {}
}; };
MODULE_DEVICE_TABLE(of, platform_uhci_ids); MODULE_DEVICE_TABLE(of, platform_uhci_ids);

View File

@@ -1388,7 +1388,7 @@ static int xenhcd_get_frame(struct usb_hcd *hcd)
return 0; return 0;
} }
static struct hc_driver xenhcd_usb20_hc_driver = { static const struct hc_driver xenhcd_usb20_hc_driver = {
.description = "xen-hcd", .description = "xen-hcd",
.product_desc = "Xen USB2.0 Virtual Host Controller", .product_desc = "Xen USB2.0 Virtual Host Controller",
.hcd_priv_size = sizeof(struct xenhcd_info), .hcd_priv_size = sizeof(struct xenhcd_info),
@@ -1413,7 +1413,7 @@ static struct hc_driver xenhcd_usb20_hc_driver = {
#endif #endif
}; };
static struct hc_driver xenhcd_usb11_hc_driver = { static const struct hc_driver xenhcd_usb11_hc_driver = {
.description = "xen-hcd", .description = "xen-hcd",
.product_desc = "Xen USB1.1 Virtual Host Controller", .product_desc = "Xen USB1.1 Virtual Host Controller",
.hcd_priv_size = sizeof(struct xenhcd_info), .hcd_priv_size = sizeof(struct xenhcd_info),

View File

@@ -1,93 +1,120 @@
/* SPDX-License-Identifier: GPL-2.0 */ /* SPDX-License-Identifier: GPL-2.0 */
/*
* xHCI Host Controller Capability Registers.
* xHCI Specification Section 5.3, Revision 1.2.
*/
/* hc_capbase bitmasks */ #include <linux/bits.h>
/* bits 7:0 - how long is the Capabilities register */
#define HC_LENGTH(p) XHCI_HC_LENGTH(p) /* hc_capbase - bitmasks */
/* bits 31:16 */ /* bits 7:0 - Capability Registers Length */
#define HC_LENGTH(p) ((p) & 0xff)
/* bits 15:8 - Rsvd */
/* bits 31:16 - Host Controller Interface Version Number */
#define HC_VERSION(p) (((p) >> 16) & 0xffff) #define HC_VERSION(p) (((p) >> 16) & 0xffff)
/* HCSPARAMS1 - hcs_params1 - bitmasks */ /* HCSPARAMS1 - hcs_params1 - bitmasks */
/* bits 0:7, Max Device Slots */ /* bits 7:0 - Number of Device Slots */
#define HCS_MAX_SLOTS(p) (((p) >> 0) & 0xff) #define HCS_MAX_SLOTS(p) (((p) >> 0) & 0xff)
#define HCS_SLOTS_MASK 0xff #define HCS_SLOTS_MASK 0xff
/* bits 8:18, Max Interrupters */ /* bits 18:8 - Number of Interrupters, max values is 1024 */
#define HCS_MAX_INTRS(p) (((p) >> 8) & 0x7ff) #define HCS_MAX_INTRS(p) (((p) >> 8) & 0x7ff)
/* bits 24:31, Max Ports - max value is 0x7F = 127 ports */ /* bits 31:24, Max Ports - max value is 255 */
#define HCS_MAX_PORTS(p) (((p) >> 24) & 0x7f) #define HCS_MAX_PORTS(p) (((p) >> 24) & 0xff)
/* HCSPARAMS2 - hcs_params2 - bitmasks */ /* HCSPARAMS2 - hcs_params2 - bitmasks */
/* bits 0:3, frames or uframes that SW needs to queue transactions /*
* ahead of the HW to meet periodic deadlines */ * bits 3:0 - Isochronous Scheduling Threshold, frames or uframes that SW
#define HCS_IST(p) (((p) >> 0) & 0xf) * needs to queue transactions ahead of the HW to meet periodic deadlines.
/* bits 4:7, max number of Event Ring segments */ * - Bits 2:0: Threshold value
* - Bit 3: Unit indicator
* - '1': Threshold in Frames
* - '0': Threshold in Microframes (uframes)
* Note: 1 Frame = 8 Microframes
* xHCI specification section 5.3.4.
*/
#define HCS_IST_VALUE(p) ((p) & 0x7)
#define HCS_IST_UNIT BIT(3)
/* bits 7:4 - Event Ring Segment Table Max, 2^(n) */
#define HCS_ERST_MAX(p) (((p) >> 4) & 0xf) #define HCS_ERST_MAX(p) (((p) >> 4) & 0xf)
/* bits 21:25 Hi 5 bits of Scratchpad buffers SW must allocate for the HW */ /* bits 20:8 - Rsvd */
/* bit 26 Scratchpad restore - for save/restore HW state - not used yet */ /* bits 25:21 - Max Scratchpad Buffers (Hi), 5 Most significant bits */
/* bits 27:31 Lo 5 bits of Scratchpad buffers SW must allocate for the HW */ #define HCS_MAX_SP_HI(p) (((p) >> 21) & 0x1f)
#define HCS_MAX_SCRATCHPAD(p) ((((p) >> 16) & 0x3e0) | (((p) >> 27) & 0x1f)) /* bit 26 - Scratchpad restore, for save/restore HW state */
/* bits 31:27 - Max Scratchpad Buffers (Lo), 5 Least significant bits */
#define HCS_MAX_SP_LO(p) (((p) >> 27) & 0x1f)
#define HCS_MAX_SCRATCHPAD(p) (HCS_MAX_SP_HI(p) << 5 | HCS_MAX_SP_LO(p))
/* HCSPARAMS3 - hcs_params3 - bitmasks */ /* HCSPARAMS3 - hcs_params3 - bitmasks */
/* bits 0:7, Max U1 to U0 latency for the roothub ports */ /* bits 7:0 - U1 Device Exit Latency, Max U1 to U0 latency for the roothub ports */
#define HCS_U1_LATENCY(p) (((p) >> 0) & 0xff) #define HCS_U1_LATENCY(p) (((p) >> 0) & 0xff)
/* bits 16:31, Max U2 to U0 latency for the roothub ports */ /* bits 15:8 - Rsvd */
/* bits 31:16 - U2 Device Exit Latency, Max U2 to U0 latency for the roothub ports */
#define HCS_U2_LATENCY(p) (((p) >> 16) & 0xffff) #define HCS_U2_LATENCY(p) (((p) >> 16) & 0xffff)
/* HCCPARAMS - hcc_params - bitmasks */ /* HCCPARAMS1 - hcc_params - bitmasks */
/* true: HC can use 64-bit address pointers */ /* bit 0 - 64-bit Addressing Capability */
#define HCC_64BIT_ADDR(p) ((p) & (1 << 0)) #define HCC_64BIT_ADDR BIT(0)
/* true: HC can do bandwidth negotiation */ /* bit 1 - BW Negotiation Capability */
#define HCC_BANDWIDTH_NEG(p) ((p) & (1 << 1)) #define HCC_BANDWIDTH_NEG BIT(1)
/* true: HC uses 64-byte Device Context structures /* bit 2 - Context Size */
* FIXME 64-byte context structures aren't supported yet. #define HCC_64BYTE_CONTEXT BIT(2)
*/ #define CTX_SIZE(_hcc) (_hcc & HCC_64BYTE_CONTEXT ? 64 : 32)
#define HCC_64BYTE_CONTEXT(p) ((p) & (1 << 2)) /* bit 3 - Port Power Control */
/* true: HC has port power switches */ #define HCC_PPC BIT(3)
#define HCC_PPC(p) ((p) & (1 << 3)) /* bit 4 - Port Indicators */
/* true: HC has port indicators */ #define HCS_INDICATOR BIT(4)
#define HCS_INDICATOR(p) ((p) & (1 << 4)) /* bit 5 - Light HC Reset Capability */
/* true: HC has Light HC Reset Capability */ #define HCC_LIGHT_RESET BIT(5)
#define HCC_LIGHT_RESET(p) ((p) & (1 << 5)) /* bit 6 - Latency Tolerance Messaging Capability */
/* true: HC supports latency tolerance messaging */ #define HCC_LTC BIT(6)
#define HCC_LTC(p) ((p) & (1 << 6)) /* bit 7 - No Secondary Stream ID Support */
/* true: no secondary Stream ID Support */ #define HCC_NSS BIT(7)
#define HCC_NSS(p) ((p) & (1 << 7)) /* bit 8 - Parse All Event Data */
/* true: HC supports Stopped - Short Packet */ /* bit 9 - Short Packet Capability */
#define HCC_SPC(p) ((p) & (1 << 9)) #define HCC_SPC BIT(9)
/* true: HC has Contiguous Frame ID Capability */ /* bit 10 - Stopped EDTLA Capability */
#define HCC_CFC(p) ((p) & (1 << 11)) /* bit 11 - Contiguous Frame ID Capability */
/* Max size for Primary Stream Arrays - 2^(n+1), where n is bits 12:15 */ #define HCC_CFC BIT(11)
/* bits 15:12 - Max size for Primary Stream Arrays, 2^(n+1) */
#define HCC_MAX_PSA(p) (1 << ((((p) >> 12) & 0xf) + 1)) #define HCC_MAX_PSA(p) (1 << ((((p) >> 12) & 0xf) + 1))
/* Extended Capabilities pointer from PCI base - section 5.3.6 */ /* bits 31:16 - xHCI Extended Capabilities Pointer, from PCI base: 2^(n) */
#define HCC_EXT_CAPS(p) XHCI_HCC_EXT_CAPS(p) #define HCC_EXT_CAPS(p) (((p) >> 16) & 0xffff)
#define CTX_SIZE(_hcc) (HCC_64BYTE_CONTEXT(_hcc) ? 64 : 32) /* DBOFF - db_off - bitmasks */
/* bits 1:0 - Rsvd */
/* db_off bitmask - bits 31:2 Doorbell Array Offset */ /* bits 31:2 - Doorbell Array Offset */
#define DBOFF_MASK (0xfffffffc) #define DBOFF_MASK (0xfffffffc)
/* run_regs_off bitmask - bits 0:4 reserved */ /* RTSOFF - run_regs_off - bitmasks */
/* bits 4:0 - Rsvd */
/* bits 31:5 - Runtime Register Space Offse */
#define RTSOFF_MASK (~0x1f) #define RTSOFF_MASK (~0x1f)
/* HCCPARAMS2 - hcc_params2 - bitmasks */ /* HCCPARAMS2 - hcc_params2 - bitmasks */
/* true: HC supports U3 entry Capability */ /* bit 0 - U3 Entry Capability */
#define HCC2_U3C(p) ((p) & (1 << 0)) #define HCC2_U3C BIT(0)
/* true: HC supports Configure endpoint command Max exit latency too large */ /* bit 1 - Configure Endpoint Command Max Exit Latency Too Large Capability */
#define HCC2_CMC(p) ((p) & (1 << 1)) #define HCC2_CMC BIT(1)
/* true: HC supports Force Save context Capability */ /* bit 2 - Force Save Context Capabilitu */
#define HCC2_FSC(p) ((p) & (1 << 2)) #define HCC2_FSC BIT(2)
/* true: HC supports Compliance Transition Capability */ /* bit 3 - Compliance Transition Capability, false: compliance is enabled by default */
#define HCC2_CTC(p) ((p) & (1 << 3)) #define HCC2_CTC BIT(3)
/* true: HC support Large ESIT payload Capability > 48k */ /* bit 4 - Large ESIT Payload Capability, true: HC support ESIT payload > 48k */
#define HCC2_LEC(p) ((p) & (1 << 4)) #define HCC2_LEC BIT(4)
/* true: HC support Configuration Information Capability */ /* bit 5 - Configuration Information Capability */
#define HCC2_CIC(p) ((p) & (1 << 5)) #define HCC2_CIC BIT(5)
/* true: HC support Extended TBC Capability, Isoc burst count > 65535 */ /* bit 6 - Extended TBC Capability, true: Isoc burst count > 65535 */
#define HCC2_ETC(p) ((p) & (1 << 6)) #define HCC2_ETC BIT(6)
/* true: HC support Extended TBC TRB Status Capability */ /* bit 7 - Extended TBC TRB Status Capability */
#define HCC2_ETC_TSC(p) ((p) & (1 << 7)) #define HCC2_ETC_TSC BIT(7)
/* true: HC support Get/Set Extended Property Capability */ /* bit 8 - Get/Set Extended Property Capability */
#define HCC2_GSC(p) ((p) & (1 << 8)) #define HCC2_GSC BIT(8)
/* true: HC support Virtualization Based Trusted I/O Capability */ /* bit 9 - Virtualization Based Trusted I/O Capability */
#define HCC2_VTC(p) ((p) & (1 << 9)) #define HCC2_VTC BIT(9)
/* true: HC support Double BW on a eUSB2 HS ISOC EP */ /* bit 10 - Rsvd */
#define HCC2_EUSB2_DIC(p) ((p) & (1 << 11)) /* bit 11 - HC support Double BW on a eUSB2 HS ISOC EP */
#define HCC2_EUSB2_DIC BIT(11)
/* bit 12 - HC support eUSB2V2 capability */
#define HCC2_E2V2C BIT(12)
/* bits 31:13 - Rsvd */

View File

@@ -374,7 +374,7 @@ int dbc_ep_queue(struct dbc_request *req)
ret = dbc_ep_do_queue(req); ret = dbc_ep_do_queue(req);
spin_unlock_irqrestore(&dbc->lock, flags); spin_unlock_irqrestore(&dbc->lock, flags);
mod_delayed_work(system_wq, &dbc->event_work, 0); mod_delayed_work(system_percpu_wq, &dbc->event_work, 0);
trace_xhci_dbc_queue_request(req); trace_xhci_dbc_queue_request(req);
@@ -677,7 +677,7 @@ static int xhci_dbc_start(struct xhci_dbc *dbc)
return ret; return ret;
} }
return mod_delayed_work(system_wq, &dbc->event_work, return mod_delayed_work(system_percpu_wq, &dbc->event_work,
msecs_to_jiffies(dbc->poll_interval)); msecs_to_jiffies(dbc->poll_interval));
} }
@@ -1023,7 +1023,7 @@ static void xhci_dbc_handle_events(struct work_struct *work)
return; return;
} }
mod_delayed_work(system_wq, &dbc->event_work, mod_delayed_work(system_percpu_wq, &dbc->event_work,
msecs_to_jiffies(poll_interval)); msecs_to_jiffies(poll_interval));
} }
@@ -1274,7 +1274,7 @@ static ssize_t dbc_poll_interval_ms_store(struct device *dev,
dbc->poll_interval = value; dbc->poll_interval = value;
mod_delayed_work(system_wq, &dbc->event_work, 0); mod_delayed_work(system_percpu_wq, &dbc->event_work, 0);
return size; return size;
} }

View File

@@ -329,7 +329,7 @@ static int xhci_portsc_show(struct seq_file *s, void *unused)
u32 portsc; u32 portsc;
char str[XHCI_MSG_MAX]; char str[XHCI_MSG_MAX];
portsc = readl(port->addr); portsc = xhci_portsc_readl(port);
seq_printf(s, "%s\n", xhci_decode_portsc(str, portsc)); seq_printf(s, "%s\n", xhci_decode_portsc(str, portsc));
return 0; return 0;
@@ -355,11 +355,11 @@ static ssize_t xhci_port_write(struct file *file, const char __user *ubuf,
if (!strncmp(buf, "compliance", 10)) { if (!strncmp(buf, "compliance", 10)) {
/* If CTC is clear, compliance is enabled by default */ /* If CTC is clear, compliance is enabled by default */
if (!HCC2_CTC(xhci->hcc_params2)) if (!(xhci->hcc_params2 & HCC2_CTC))
return count; return count;
spin_lock_irqsave(&xhci->lock, flags); spin_lock_irqsave(&xhci->lock, flags);
/* compliance mode can only be enabled on ports in RxDetect */ /* compliance mode can only be enabled on ports in RxDetect */
portsc = readl(port->addr); portsc = xhci_portsc_readl(port);
if ((portsc & PORT_PLS_MASK) != XDEV_RXDETECT) { if ((portsc & PORT_PLS_MASK) != XDEV_RXDETECT) {
spin_unlock_irqrestore(&xhci->lock, flags); spin_unlock_irqrestore(&xhci->lock, flags);
return -EPERM; return -EPERM;
@@ -367,7 +367,7 @@ static ssize_t xhci_port_write(struct file *file, const char __user *ubuf,
portsc = xhci_port_state_to_neutral(portsc); portsc = xhci_port_state_to_neutral(portsc);
portsc &= ~PORT_PLS_MASK; portsc &= ~PORT_PLS_MASK;
portsc |= PORT_LINK_STROBE | XDEV_COMP_MODE; portsc |= PORT_LINK_STROBE | XDEV_COMP_MODE;
writel(portsc, port->addr); xhci_portsc_writel(port, portsc);
spin_unlock_irqrestore(&xhci->lock, flags); spin_unlock_irqrestore(&xhci->lock, flags);
} else { } else {
return -EINVAL; return -EINVAL;
@@ -383,6 +383,39 @@ static const struct file_operations port_fops = {
.release = single_release, .release = single_release,
}; };
static int xhci_portli_show(struct seq_file *s, void *unused)
{
struct xhci_port *port = s->private;
struct xhci_hcd *xhci = hcd_to_xhci(port->rhub->hcd);
u32 portli;
portli = readl(&port->port_reg->portli);
/* PORTLI fields are valid if port is a USB3 or eUSB2V2 port */
if (port->rhub == &xhci->usb3_rhub)
seq_printf(s, "0x%08x LEC=%u RLC=%u TLC=%u\n", portli,
PORT_LEC(portli), PORT_RX_LANES(portli), PORT_TX_LANES(portli));
else if (xhci->hcc_params2 & HCC2_E2V2C)
seq_printf(s, "0x%08x RDR=%u TDR=%u\n", portli,
PORTLI_RDR(portli), PORTLI_TDR(portli));
else
seq_printf(s, "0x%08x RsvdP\n", portli);
return 0;
}
static int xhci_portli_open(struct inode *inode, struct file *file)
{
return single_open(file, xhci_portli_show, inode->i_private);
}
static const struct file_operations portli_fops = {
.open = xhci_portli_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static void xhci_debugfs_create_files(struct xhci_hcd *xhci, static void xhci_debugfs_create_files(struct xhci_hcd *xhci,
struct xhci_file_map *files, struct xhci_file_map *files,
size_t nentries, void *data, size_t nentries, void *data,
@@ -613,28 +646,24 @@ void xhci_debugfs_remove_slot(struct xhci_hcd *xhci, int slot_id)
static void xhci_debugfs_create_ports(struct xhci_hcd *xhci, static void xhci_debugfs_create_ports(struct xhci_hcd *xhci,
struct dentry *parent) struct dentry *parent)
{ {
unsigned int num_ports;
char port_name[8]; char port_name[8];
struct xhci_port *port; struct xhci_port *port;
struct dentry *dir; struct dentry *dir;
num_ports = HCS_MAX_PORTS(xhci->hcs_params1);
parent = debugfs_create_dir("ports", parent); parent = debugfs_create_dir("ports", parent);
while (num_ports--) { for (int i = 0; i < xhci->max_ports; i++) {
scnprintf(port_name, sizeof(port_name), "port%02d", scnprintf(port_name, sizeof(port_name), "port%02d", i + 1);
num_ports + 1);
dir = debugfs_create_dir(port_name, parent); dir = debugfs_create_dir(port_name, parent);
port = &xhci->hw_ports[num_ports]; port = &xhci->hw_ports[i];
debugfs_create_file("portsc", 0644, dir, port, &port_fops); debugfs_create_file("portsc", 0644, dir, port, &port_fops);
debugfs_create_file("portli", 0444, dir, port, &portli_fops);
} }
} }
static int xhci_port_bw_show(struct xhci_hcd *xhci, u8 dev_speed, static int xhci_port_bw_show(struct xhci_hcd *xhci, u8 dev_speed,
struct seq_file *s) struct seq_file *s)
{ {
unsigned int num_ports;
unsigned int i; unsigned int i;
int ret; int ret;
struct xhci_container_ctx *ctx; struct xhci_container_ctx *ctx;
@@ -645,8 +674,6 @@ static int xhci_port_bw_show(struct xhci_hcd *xhci, u8 dev_speed,
if (ret < 0) if (ret < 0)
return ret; return ret;
num_ports = HCS_MAX_PORTS(xhci->hcs_params1);
ctx = xhci_alloc_port_bw_ctx(xhci, 0); ctx = xhci_alloc_port_bw_ctx(xhci, 0);
if (!ctx) { if (!ctx) {
pm_runtime_put_sync(dev); pm_runtime_put_sync(dev);
@@ -661,7 +688,7 @@ static int xhci_port_bw_show(struct xhci_hcd *xhci, u8 dev_speed,
/* print all roothub ports available bandwidth /* print all roothub ports available bandwidth
* refer to xhci rev1_2 protocol 6.2.6 , byte 0 is reserved * refer to xhci rev1_2 protocol 6.2.6 , byte 0 is reserved
*/ */
for (i = 1; i < num_ports+1; i++) for (i = 1; i <= xhci->max_ports; i++)
seq_printf(s, "port[%d] available bw: %d%%.\n", i, seq_printf(s, "port[%d] available bw: %d%%.\n", i,
ctx->bytes[i]); ctx->bytes[i]);
err_out: err_out:

View File

@@ -110,7 +110,7 @@ static int xhci_create_usb3x_bos_desc(struct xhci_hcd *xhci, char *buf,
ss_cap->bU2DevExitLat = 0; /* set later */ ss_cap->bU2DevExitLat = 0; /* set later */
reg = readl(&xhci->cap_regs->hcc_params); reg = readl(&xhci->cap_regs->hcc_params);
if (HCC_LTC(reg)) if (reg & HCC_LTC)
ss_cap->bmAttributes |= USB_LTM_SUPPORT; ss_cap->bmAttributes |= USB_LTM_SUPPORT;
if ((xhci->quirks & XHCI_LPM_SUPPORT)) { if ((xhci->quirks & XHCI_LPM_SUPPORT)) {
@@ -263,7 +263,7 @@ static void xhci_common_hub_descriptor(struct xhci_hcd *xhci,
desc->bNbrPorts = ports; desc->bNbrPorts = ports;
temp = 0; temp = 0;
/* Bits 1:0 - support per-port power switching, or power always on */ /* Bits 1:0 - support per-port power switching, or power always on */
if (HCC_PPC(xhci->hcc_params)) if (xhci->hcc_params & HCC_PPC)
temp |= HUB_CHAR_INDV_PORT_LPSM; temp |= HUB_CHAR_INDV_PORT_LPSM;
else else
temp |= HUB_CHAR_NO_LPSM; temp |= HUB_CHAR_NO_LPSM;
@@ -299,7 +299,7 @@ static void xhci_usb2_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci,
*/ */
memset(port_removable, 0, sizeof(port_removable)); memset(port_removable, 0, sizeof(port_removable));
for (i = 0; i < ports; i++) { for (i = 0; i < ports; i++) {
portsc = readl(rhub->ports[i]->addr); portsc = xhci_portsc_readl(rhub->ports[i]);
/* If a device is removable, PORTSC reports a 0, same as in the /* If a device is removable, PORTSC reports a 0, same as in the
* hub descriptor DeviceRemovable bits. * hub descriptor DeviceRemovable bits.
*/ */
@@ -356,7 +356,7 @@ static void xhci_usb3_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci,
port_removable = 0; port_removable = 0;
/* bit 0 is reserved, bit 1 is for port 1, etc. */ /* bit 0 is reserved, bit 1 is for port 1, etc. */
for (i = 0; i < ports; i++) { for (i = 0; i < ports; i++) {
portsc = readl(rhub->ports[i]->addr); portsc = xhci_portsc_readl(rhub->ports[i]);
if (portsc & PORT_DEV_REMOVE) if (portsc & PORT_DEV_REMOVE)
port_removable |= 1 << (i + 1); port_removable |= 1 << (i + 1);
} }
@@ -566,19 +566,19 @@ static void xhci_disable_port(struct xhci_hcd *xhci, struct xhci_port *port)
return; return;
} }
portsc = readl(port->addr); portsc = xhci_portsc_readl(port);
portsc = xhci_port_state_to_neutral(portsc); portsc = xhci_port_state_to_neutral(portsc);
/* Write 1 to disable the port */ /* Write 1 to disable the port */
writel(portsc | PORT_PE, port->addr); xhci_portsc_writel(port, portsc | PORT_PE);
portsc = readl(port->addr); portsc = xhci_portsc_readl(port);
xhci_dbg(xhci, "disable port %d-%d, portsc: 0x%x\n", xhci_dbg(xhci, "disable port %d-%d, portsc: 0x%x\n",
hcd->self.busnum, port->hcd_portnum + 1, portsc); hcd->self.busnum, port->hcd_portnum + 1, portsc);
} }
static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue, static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue,
u16 wIndex, __le32 __iomem *addr, u32 port_status) u16 wIndex, struct xhci_port *port, u32 port_status)
{ {
char *port_change_bit; char *port_change_bit;
u32 status; u32 status;
@@ -621,8 +621,8 @@ static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue,
return; return;
} }
/* Change bits are all write 1 to clear */ /* Change bits are all write 1 to clear */
writel(port_status | status, addr); xhci_portsc_writel(port, port_status | status);
port_status = readl(addr); port_status = xhci_portsc_readl(port);
xhci_dbg(xhci, "clear port%d %s change, portsc: 0x%x\n", xhci_dbg(xhci, "clear port%d %s change, portsc: 0x%x\n",
wIndex + 1, port_change_bit, port_status); wIndex + 1, port_change_bit, port_status);
@@ -650,7 +650,7 @@ static void xhci_set_port_power(struct xhci_hcd *xhci, struct xhci_port *port,
u32 temp; u32 temp;
hcd = port->rhub->hcd; hcd = port->rhub->hcd;
temp = readl(port->addr); temp = xhci_portsc_readl(port);
xhci_dbg(xhci, "set port power %d-%d %s, portsc: 0x%x\n", xhci_dbg(xhci, "set port power %d-%d %s, portsc: 0x%x\n",
hcd->self.busnum, port->hcd_portnum + 1, on ? "ON" : "OFF", temp); hcd->self.busnum, port->hcd_portnum + 1, on ? "ON" : "OFF", temp);
@@ -659,11 +659,11 @@ static void xhci_set_port_power(struct xhci_hcd *xhci, struct xhci_port *port,
if (on) { if (on) {
/* Power on */ /* Power on */
writel(temp | PORT_POWER, port->addr); xhci_portsc_writel(port, temp | PORT_POWER);
readl(port->addr); xhci_portsc_readl(port);
} else { } else {
/* Power off */ /* Power off */
writel(temp & ~PORT_POWER, port->addr); xhci_portsc_writel(port, temp & ~PORT_POWER);
} }
spin_unlock_irqrestore(&xhci->lock, *flags); spin_unlock_irqrestore(&xhci->lock, *flags);
@@ -683,9 +683,9 @@ static void xhci_port_set_test_mode(struct xhci_hcd *xhci,
/* xhci only supports test mode for usb2 ports */ /* xhci only supports test mode for usb2 ports */
port = xhci->usb2_rhub.ports[wIndex]; port = xhci->usb2_rhub.ports[wIndex];
temp = readl(port->addr + PORTPMSC); temp = readl(&port->port_reg->portpmsc);
temp |= test_mode << PORT_TEST_MODE_SHIFT; temp |= test_mode << PORT_TEST_MODE_SHIFT;
writel(temp, port->addr + PORTPMSC); writel(temp, &port->port_reg->portpmsc);
xhci->test_mode = test_mode; xhci->test_mode = test_mode;
if (test_mode == USB_TEST_FORCE_ENABLE) if (test_mode == USB_TEST_FORCE_ENABLE)
xhci_start(xhci); xhci_start(xhci);
@@ -700,7 +700,7 @@ static int xhci_enter_test_mode(struct xhci_hcd *xhci,
/* Disable all Device Slots */ /* Disable all Device Slots */
xhci_dbg(xhci, "Disable all slots\n"); xhci_dbg(xhci, "Disable all slots\n");
spin_unlock_irqrestore(&xhci->lock, *flags); spin_unlock_irqrestore(&xhci->lock, *flags);
for (i = 1; i <= HCS_MAX_SLOTS(xhci->hcs_params1); i++) { for (i = 1; i <= xhci->max_slots; i++) {
if (!xhci->devs[i]) if (!xhci->devs[i])
continue; continue;
@@ -801,11 +801,11 @@ void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port,
u32 temp; u32 temp;
u32 portsc; u32 portsc;
portsc = readl(port->addr); portsc = xhci_portsc_readl(port);
temp = xhci_port_state_to_neutral(portsc); temp = xhci_port_state_to_neutral(portsc);
temp &= ~PORT_PLS_MASK; temp &= ~PORT_PLS_MASK;
temp |= PORT_LINK_STROBE | link_state; temp |= PORT_LINK_STROBE | link_state;
writel(temp, port->addr); xhci_portsc_writel(port, temp);
xhci_dbg(xhci, "Set port %d-%d link state, portsc: 0x%x, write 0x%x", xhci_dbg(xhci, "Set port %d-%d link state, portsc: 0x%x, write 0x%x",
port->rhub->hcd->self.busnum, port->hcd_portnum + 1, port->rhub->hcd->self.busnum, port->hcd_portnum + 1,
@@ -817,7 +817,7 @@ static void xhci_set_remote_wake_mask(struct xhci_hcd *xhci,
{ {
u32 temp; u32 temp;
temp = readl(port->addr); temp = xhci_portsc_readl(port);
temp = xhci_port_state_to_neutral(temp); temp = xhci_port_state_to_neutral(temp);
if (wake_mask & USB_PORT_FEAT_REMOTE_WAKE_CONNECT) if (wake_mask & USB_PORT_FEAT_REMOTE_WAKE_CONNECT)
@@ -835,7 +835,7 @@ static void xhci_set_remote_wake_mask(struct xhci_hcd *xhci,
else else
temp &= ~PORT_WKOC_E; temp &= ~PORT_WKOC_E;
writel(temp, port->addr); xhci_portsc_writel(port, temp);
} }
/* Test and clear port RWC bit */ /* Test and clear port RWC bit */
@@ -844,11 +844,11 @@ void xhci_test_and_clear_bit(struct xhci_hcd *xhci, struct xhci_port *port,
{ {
u32 temp; u32 temp;
temp = readl(port->addr); temp = xhci_portsc_readl(port);
if (temp & port_bit) { if (temp & port_bit) {
temp = xhci_port_state_to_neutral(temp); temp = xhci_port_state_to_neutral(temp);
temp |= port_bit; temp |= port_bit;
writel(temp, port->addr); xhci_portsc_writel(port, temp);
} }
} }
@@ -1002,7 +1002,7 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port,
} }
xhci_ring_device(xhci, port->slot_id); xhci_ring_device(xhci, port->slot_id);
} else { } else {
int port_status = readl(port->addr); int port_status = xhci_portsc_readl(port);
xhci_warn(xhci, "Port resume timed out, port %d-%d: 0x%x\n", xhci_warn(xhci, "Port resume timed out, port %d-%d: 0x%x\n",
hcd->self.busnum, wIndex + 1, port_status); hcd->self.busnum, wIndex + 1, port_status);
@@ -1263,7 +1263,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
wIndex--; wIndex--;
port = ports[portnum1 - 1]; port = ports[portnum1 - 1];
temp = readl(port->addr); temp = xhci_portsc_readl(port);
if (temp == ~(u32)0) { if (temp == ~(u32)0) {
xhci_hc_died(xhci); xhci_hc_died(xhci);
retval = -ENODEV; retval = -ENODEV;
@@ -1288,7 +1288,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
retval = -EINVAL; retval = -EINVAL;
break; break;
} }
port_li = readl(port->addr + PORTLI); port_li = readl(&port->port_reg->portli);
status = xhci_get_ext_port_status(temp, port_li); status = xhci_get_ext_port_status(temp, port_li);
put_unaligned_le32(status, &buf[4]); put_unaligned_le32(status, &buf[4]);
} }
@@ -1309,7 +1309,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
port = ports[portnum1 - 1]; port = ports[portnum1 - 1];
wIndex--; wIndex--;
temp = readl(port->addr); temp = xhci_portsc_readl(port);
if (temp == ~(u32)0) { if (temp == ~(u32)0) {
xhci_hc_died(xhci); xhci_hc_died(xhci);
retval = -ENODEV; retval = -ENODEV;
@@ -1319,7 +1319,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
/* FIXME: What new port features do we need to support? */ /* FIXME: What new port features do we need to support? */
switch (wValue) { switch (wValue) {
case USB_PORT_FEAT_SUSPEND: case USB_PORT_FEAT_SUSPEND:
temp = readl(port->addr); temp = xhci_portsc_readl(port);
if ((temp & PORT_PLS_MASK) != XDEV_U0) { if ((temp & PORT_PLS_MASK) != XDEV_U0) {
/* Resume the port to U0 first */ /* Resume the port to U0 first */
xhci_set_link_state(xhci, port, XDEV_U0); xhci_set_link_state(xhci, port, XDEV_U0);
@@ -1331,7 +1331,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
* a port unless the port reports that it is in the * a port unless the port reports that it is in the
* enabled (PED = 1,PLS < 3) state. * enabled (PED = 1,PLS < 3) state.
*/ */
temp = readl(port->addr); temp = xhci_portsc_readl(port);
if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) if ((temp & PORT_PE) == 0 || (temp & PORT_RESET)
|| (temp & PORT_PLS_MASK) >= XDEV_U3) { || (temp & PORT_PLS_MASK) >= XDEV_U3) {
xhci_warn(xhci, "USB core suspending port %d-%d not in U0/U1/U2\n", xhci_warn(xhci, "USB core suspending port %d-%d not in U0/U1/U2\n",
@@ -1354,11 +1354,11 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
msleep(10); /* wait device to enter */ msleep(10); /* wait device to enter */
spin_lock_irqsave(&xhci->lock, flags); spin_lock_irqsave(&xhci->lock, flags);
temp = readl(port->addr); temp = xhci_portsc_readl(port);
bus_state->suspended_ports |= 1 << wIndex; bus_state->suspended_ports |= 1 << wIndex;
break; break;
case USB_PORT_FEAT_LINK_STATE: case USB_PORT_FEAT_LINK_STATE:
temp = readl(port->addr); temp = xhci_portsc_readl(port);
/* Disable port */ /* Disable port */
if (link_state == USB_SS_PORT_LS_SS_DISABLED) { if (link_state == USB_SS_PORT_LS_SS_DISABLED) {
xhci_dbg(xhci, "Disable port %d-%d\n", xhci_dbg(xhci, "Disable port %d-%d\n",
@@ -1371,8 +1371,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
temp |= PORT_CSC | PORT_PEC | PORT_WRC | temp |= PORT_CSC | PORT_PEC | PORT_WRC |
PORT_OCC | PORT_RC | PORT_PLC | PORT_OCC | PORT_RC | PORT_PLC |
PORT_CEC; PORT_CEC;
writel(temp | PORT_PE, port->addr); xhci_portsc_writel(port, temp | PORT_PE);
temp = readl(port->addr); temp = xhci_portsc_readl(port);
break; break;
} }
@@ -1381,7 +1381,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
xhci_dbg(xhci, "Enable port %d-%d\n", xhci_dbg(xhci, "Enable port %d-%d\n",
hcd->self.busnum, portnum1); hcd->self.busnum, portnum1);
xhci_set_link_state(xhci, port, link_state); xhci_set_link_state(xhci, port, link_state);
temp = readl(port->addr); temp = xhci_portsc_readl(port);
break; break;
} }
@@ -1400,7 +1400,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
* automatically entered as on 1.0 and prior. * automatically entered as on 1.0 and prior.
*/ */
if (link_state == USB_SS_PORT_LS_COMP_MOD) { if (link_state == USB_SS_PORT_LS_COMP_MOD) {
if (!HCC2_CTC(xhci->hcc_params2)) { if (!(xhci->hcc_params2 & HCC2_CTC)) {
xhci_dbg(xhci, "CTC flag is 0, port already supports entering compliance mode\n"); xhci_dbg(xhci, "CTC flag is 0, port already supports entering compliance mode\n");
break; break;
} }
@@ -1414,7 +1414,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
hcd->self.busnum, portnum1); hcd->self.busnum, portnum1);
xhci_set_link_state(xhci, port, link_state); xhci_set_link_state(xhci, port, link_state);
temp = readl(port->addr); temp = xhci_portsc_readl(port);
break; break;
} }
/* Port must be enabled */ /* Port must be enabled */
@@ -1462,7 +1462,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
xhci_dbg(xhci, "missing U0 port change event for port %d-%d\n", xhci_dbg(xhci, "missing U0 port change event for port %d-%d\n",
hcd->self.busnum, portnum1); hcd->self.busnum, portnum1);
spin_lock_irqsave(&xhci->lock, flags); spin_lock_irqsave(&xhci->lock, flags);
temp = readl(port->addr); temp = xhci_portsc_readl(port);
break; break;
} }
@@ -1480,12 +1480,12 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
spin_unlock_irqrestore(&xhci->lock, flags); spin_unlock_irqrestore(&xhci->lock, flags);
while (retries--) { while (retries--) {
usleep_range(4000, 8000); usleep_range(4000, 8000);
temp = readl(port->addr); temp = xhci_portsc_readl(port);
if ((temp & PORT_PLS_MASK) == XDEV_U3) if ((temp & PORT_PLS_MASK) == XDEV_U3)
break; break;
} }
spin_lock_irqsave(&xhci->lock, flags); spin_lock_irqsave(&xhci->lock, flags);
temp = readl(port->addr); temp = xhci_portsc_readl(port);
bus_state->suspended_ports |= 1 << wIndex; bus_state->suspended_ports |= 1 << wIndex;
} }
break; break;
@@ -1500,38 +1500,38 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
break; break;
case USB_PORT_FEAT_RESET: case USB_PORT_FEAT_RESET:
temp = (temp | PORT_RESET); temp = (temp | PORT_RESET);
writel(temp, port->addr); xhci_portsc_writel(port, temp);
temp = readl(port->addr); temp = xhci_portsc_readl(port);
xhci_dbg(xhci, "set port reset, actual port %d-%d status = 0x%x\n", xhci_dbg(xhci, "set port reset, actual port %d-%d status = 0x%x\n",
hcd->self.busnum, portnum1, temp); hcd->self.busnum, portnum1, temp);
break; break;
case USB_PORT_FEAT_REMOTE_WAKE_MASK: case USB_PORT_FEAT_REMOTE_WAKE_MASK:
xhci_set_remote_wake_mask(xhci, port, wake_mask); xhci_set_remote_wake_mask(xhci, port, wake_mask);
temp = readl(port->addr); temp = xhci_portsc_readl(port);
xhci_dbg(xhci, "set port remote wake mask, actual port %d-%d status = 0x%x\n", xhci_dbg(xhci, "set port remote wake mask, actual port %d-%d status = 0x%x\n",
hcd->self.busnum, portnum1, temp); hcd->self.busnum, portnum1, temp);
break; break;
case USB_PORT_FEAT_BH_PORT_RESET: case USB_PORT_FEAT_BH_PORT_RESET:
temp |= PORT_WR; temp |= PORT_WR;
writel(temp, port->addr); xhci_portsc_writel(port, temp);
temp = readl(port->addr); temp = xhci_portsc_readl(port);
break; break;
case USB_PORT_FEAT_U1_TIMEOUT: case USB_PORT_FEAT_U1_TIMEOUT:
if (hcd->speed < HCD_USB3) if (hcd->speed < HCD_USB3)
goto error; goto error;
temp = readl(port->addr + PORTPMSC); temp = readl(&port->port_reg->portpmsc);
temp &= ~PORT_U1_TIMEOUT_MASK; temp &= ~PORT_U1_TIMEOUT_MASK;
temp |= PORT_U1_TIMEOUT(timeout); temp |= PORT_U1_TIMEOUT(timeout);
writel(temp, port->addr + PORTPMSC); writel(temp, &port->port_reg->portpmsc);
break; break;
case USB_PORT_FEAT_U2_TIMEOUT: case USB_PORT_FEAT_U2_TIMEOUT:
if (hcd->speed < HCD_USB3) if (hcd->speed < HCD_USB3)
goto error; goto error;
temp = readl(port->addr + PORTPMSC); temp = readl(&port->port_reg->portpmsc);
temp &= ~PORT_U2_TIMEOUT_MASK; temp &= ~PORT_U2_TIMEOUT_MASK;
temp |= PORT_U2_TIMEOUT(timeout); temp |= PORT_U2_TIMEOUT(timeout);
writel(temp, port->addr + PORTPMSC); writel(temp, &port->port_reg->portpmsc);
break; break;
case USB_PORT_FEAT_TEST: case USB_PORT_FEAT_TEST:
/* 4.19.6 Port Test Modes (USB2 Test Mode) */ /* 4.19.6 Port Test Modes (USB2 Test Mode) */
@@ -1547,7 +1547,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
goto error; goto error;
} }
/* unblock any posted writes */ /* unblock any posted writes */
temp = readl(port->addr); temp = xhci_portsc_readl(port);
break; break;
case ClearPortFeature: case ClearPortFeature:
if (!portnum1 || portnum1 > max_ports) if (!portnum1 || portnum1 > max_ports)
@@ -1556,7 +1556,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
port = ports[portnum1 - 1]; port = ports[portnum1 - 1];
wIndex--; wIndex--;
temp = readl(port->addr); temp = xhci_portsc_readl(port);
if (temp == ~(u32)0) { if (temp == ~(u32)0) {
xhci_hc_died(xhci); xhci_hc_died(xhci);
retval = -ENODEV; retval = -ENODEV;
@@ -1566,7 +1566,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
temp = xhci_port_state_to_neutral(temp); temp = xhci_port_state_to_neutral(temp);
switch (wValue) { switch (wValue) {
case USB_PORT_FEAT_SUSPEND: case USB_PORT_FEAT_SUSPEND:
temp = readl(port->addr); temp = xhci_portsc_readl(port);
xhci_dbg(xhci, "clear USB_PORT_FEAT_SUSPEND\n"); xhci_dbg(xhci, "clear USB_PORT_FEAT_SUSPEND\n");
xhci_dbg(xhci, "PORTSC %04x\n", temp); xhci_dbg(xhci, "PORTSC %04x\n", temp);
if (temp & PORT_RESET) if (temp & PORT_RESET)
@@ -1603,8 +1603,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
case USB_PORT_FEAT_C_ENABLE: case USB_PORT_FEAT_C_ENABLE:
case USB_PORT_FEAT_C_PORT_LINK_STATE: case USB_PORT_FEAT_C_PORT_LINK_STATE:
case USB_PORT_FEAT_C_PORT_CONFIG_ERROR: case USB_PORT_FEAT_C_PORT_CONFIG_ERROR:
xhci_clear_port_change_bit(xhci, wValue, wIndex, xhci_clear_port_change_bit(xhci, wValue, wIndex, port, temp);
port->addr, temp);
break; break;
case USB_PORT_FEAT_ENABLE: case USB_PORT_FEAT_ENABLE:
xhci_disable_port(xhci, port); xhci_disable_port(xhci, port);
@@ -1671,7 +1670,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
* SS devices are only visible to roothub after link training completes. * SS devices are only visible to roothub after link training completes.
* Keep polling roothubs for a grace period after xHC start * Keep polling roothubs for a grace period after xHC start
*/ */
if (xhci->run_graceperiod) { if (hcd->speed >= HCD_USB3 && xhci->run_graceperiod) {
if (time_before(jiffies, xhci->run_graceperiod)) if (time_before(jiffies, xhci->run_graceperiod))
status = 1; status = 1;
else else
@@ -1682,7 +1681,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
/* For each port, did anything change? If so, set that bit in buf. */ /* For each port, did anything change? If so, set that bit in buf. */
for (i = 0; i < max_ports; i++) { for (i = 0; i < max_ports; i++) {
temp = readl(ports[i]->addr); temp = xhci_portsc_readl(ports[i]);
if (temp == ~(u32)0) { if (temp == ~(u32)0) {
xhci_hc_died(xhci); xhci_hc_died(xhci);
retval = -ENODEV; retval = -ENODEV;
@@ -1751,7 +1750,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
u32 t1, t2; u32 t1, t2;
int retries = 10; int retries = 10;
retry: retry:
t1 = readl(ports[port_index]->addr); t1 = xhci_portsc_readl(ports[port_index]);
t2 = xhci_port_state_to_neutral(t1); t2 = xhci_port_state_to_neutral(t1);
portsc_buf[port_index] = 0; portsc_buf[port_index] = 0;
@@ -1829,7 +1828,7 @@ retry:
spin_lock_irqsave(&xhci->lock, flags); spin_lock_irqsave(&xhci->lock, flags);
} }
} }
writel(portsc_buf[port_index], ports[port_index]->addr); xhci_portsc_writel(ports[port_index], portsc_buf[port_index]);
} }
hcd->state = HC_STATE_SUSPENDED; hcd->state = HC_STATE_SUSPENDED;
bus_state->next_statechange = jiffies + msecs_to_jiffies(10); bus_state->next_statechange = jiffies + msecs_to_jiffies(10);
@@ -1850,7 +1849,7 @@ static bool xhci_port_missing_cas_quirk(struct xhci_port *port)
{ {
u32 portsc; u32 portsc;
portsc = readl(port->addr); portsc = xhci_portsc_readl(port);
/* if any of these are set we are not stuck */ /* if any of these are set we are not stuck */
if (portsc & (PORT_CONNECT | PORT_CAS)) if (portsc & (PORT_CONNECT | PORT_CAS))
@@ -1863,9 +1862,9 @@ static bool xhci_port_missing_cas_quirk(struct xhci_port *port)
/* clear wakeup/change bits, and do a warm port reset */ /* clear wakeup/change bits, and do a warm port reset */
portsc &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS); portsc &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS);
portsc |= PORT_WR; portsc |= PORT_WR;
writel(portsc, port->addr); xhci_portsc_writel(port, portsc);
/* flush write */ /* flush write */
readl(port->addr); xhci_portsc_readl(port);
return true; return true;
} }
@@ -1912,7 +1911,7 @@ int xhci_bus_resume(struct usb_hcd *hcd)
} }
port_index = max_ports; port_index = max_ports;
while (port_index--) { while (port_index--) {
portsc = readl(ports[port_index]->addr); portsc = xhci_portsc_readl(ports[port_index]);
/* warm reset CAS limited ports stuck in polling/compliance */ /* warm reset CAS limited ports stuck in polling/compliance */
if ((xhci->quirks & XHCI_MISSING_CAS) && if ((xhci->quirks & XHCI_MISSING_CAS) &&
@@ -1942,7 +1941,7 @@ int xhci_bus_resume(struct usb_hcd *hcd)
} }
/* disable wake for all ports, write new link state if needed */ /* disable wake for all ports, write new link state if needed */
portsc &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS); portsc &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS);
writel(portsc, ports[port_index]->addr); xhci_portsc_writel(ports[port_index], portsc);
} }
/* USB2 specific resume signaling delay and U0 link state transition */ /* USB2 specific resume signaling delay and U0 link state transition */
@@ -1963,7 +1962,7 @@ int xhci_bus_resume(struct usb_hcd *hcd)
/* poll for U0 link state complete, both USB2 and USB3 */ /* poll for U0 link state complete, both USB2 and USB3 */
for_each_set_bit(port_index, &bus_state->bus_suspended, BITS_PER_LONG) { for_each_set_bit(port_index, &bus_state->bus_suspended, BITS_PER_LONG) {
sret = xhci_handshake(ports[port_index]->addr, PORT_PLC, sret = xhci_handshake(&ports[port_index]->port_reg->portsc, PORT_PLC,
PORT_PLC, 10 * 1000); PORT_PLC, 10 * 1000);
if (sret) { if (sret) {
xhci_warn(xhci, "port %d-%d resume PLC timeout\n", xhci_warn(xhci, "port %d-%d resume PLC timeout\n",

View File

@@ -463,7 +463,7 @@ struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci,
return NULL; return NULL;
ctx->type = type; ctx->type = type;
ctx->size = HCC_64BYTE_CONTEXT(xhci->hcc_params) ? 2048 : 1024; ctx->size = xhci->hcc_params & HCC_64BYTE_CONTEXT ? 2048 : 1024;
if (type == XHCI_CTX_TYPE_INPUT) if (type == XHCI_CTX_TYPE_INPUT)
ctx->size += CTX_SIZE(xhci->hcc_params); ctx->size += CTX_SIZE(xhci->hcc_params);
@@ -951,7 +951,7 @@ static void xhci_free_virt_devices_depth_first(struct xhci_hcd *xhci, int slot_i
/* is this a hub device that added a tt_info to the tts list */ /* is this a hub device that added a tt_info to the tts list */
if (tt_info->slot_id == slot_id) { if (tt_info->slot_id == slot_id) {
/* are any devices using this tt_info? */ /* are any devices using this tt_info? */
for (i = 1; i < HCS_MAX_SLOTS(xhci->hcs_params1); i++) { for (i = 1; i < xhci->max_slots; i++) {
vdev = xhci->devs[i]; vdev = xhci->devs[i];
if (vdev && (vdev->tt_info == tt_info)) if (vdev && (vdev->tt_info == tt_info))
xhci_free_virt_devices_depth_first( xhci_free_virt_devices_depth_first(
@@ -1344,7 +1344,7 @@ static u32 xhci_get_endpoint_mult(struct xhci_hcd *xhci,
bool lec; bool lec;
/* xHCI 1.1 with LEC set does not use mult field, except intel eUSB2 */ /* xHCI 1.1 with LEC set does not use mult field, except intel eUSB2 */
lec = xhci->hci_version > 0x100 && HCC2_LEC(xhci->hcc_params2); lec = xhci->hci_version > 0x100 && (xhci->hcc_params2 & HCC2_LEC);
/* eUSB2 double isoc bw devices are the only USB2 devices using mult */ /* eUSB2 double isoc bw devices are the only USB2 devices using mult */
if (usb_endpoint_is_hs_isoc_double(udev, ep) && if (usb_endpoint_is_hs_isoc_double(udev, ep) &&
@@ -1433,8 +1433,7 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
ring_type = usb_endpoint_type(&ep->desc); ring_type = usb_endpoint_type(&ep->desc);
/* Ensure host supports double isoc bandwidth for eUSB2 devices */ /* Ensure host supports double isoc bandwidth for eUSB2 devices */
if (usb_endpoint_is_hs_isoc_double(udev, ep) && if (usb_endpoint_is_hs_isoc_double(udev, ep) && !(xhci->hcc_params2 & HCC2_EUSB2_DIC)) {
!HCC2_EUSB2_DIC(xhci->hcc_params2)) {
dev_dbg(&udev->dev, "Double Isoc Bandwidth not supported by xhci\n"); dev_dbg(&udev->dev, "Double Isoc Bandwidth not supported by xhci\n");
return -EINVAL; return -EINVAL;
} }
@@ -1899,7 +1898,7 @@ EXPORT_SYMBOL_GPL(xhci_remove_secondary_interrupter);
void xhci_mem_cleanup(struct xhci_hcd *xhci) void xhci_mem_cleanup(struct xhci_hcd *xhci)
{ {
struct device *dev = xhci_to_hcd(xhci)->self.sysdev; struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
int i, j, num_ports; int i, j;
cancel_delayed_work_sync(&xhci->cmd_timer); cancel_delayed_work_sync(&xhci->cmd_timer);
@@ -1918,8 +1917,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed command ring"); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed command ring");
xhci_cleanup_command_queue(xhci); xhci_cleanup_command_queue(xhci);
num_ports = HCS_MAX_PORTS(xhci->hcs_params1); for (i = 0; i < xhci->max_ports && xhci->rh_bw; i++) {
for (i = 0; i < num_ports && xhci->rh_bw; i++) {
struct xhci_interval_bw_table *bwt = &xhci->rh_bw[i].bw_table; struct xhci_interval_bw_table *bwt = &xhci->rh_bw[i].bw_table;
for (j = 0; j < XHCI_MAX_INTERVAL; j++) { for (j = 0; j < XHCI_MAX_INTERVAL; j++) {
struct list_head *ep = &bwt->interval_bw[j].endpoints; struct list_head *ep = &bwt->interval_bw[j].endpoints;
@@ -1928,7 +1926,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
} }
} }
for (i = HCS_MAX_SLOTS(xhci->hcs_params1); i > 0; i--) for (i = xhci->max_slots; i > 0; i--)
xhci_free_virt_devices_depth_first(xhci, i); xhci_free_virt_devices_depth_first(xhci, i);
dma_pool_destroy(xhci->segment_pool); dma_pool_destroy(xhci->segment_pool);
@@ -1964,7 +1962,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
if (!xhci->rh_bw) if (!xhci->rh_bw)
goto no_bw; goto no_bw;
for (i = 0; i < num_ports; i++) { for (i = 0; i < xhci->max_ports; i++) {
struct xhci_tt_bw_info *tt, *n; struct xhci_tt_bw_info *tt, *n;
list_for_each_entry_safe(tt, n, &xhci->rh_bw[i].tts, tt_list) { list_for_each_entry_safe(tt, n, &xhci->rh_bw[i].tts, tt_list) {
list_del(&tt->tt_list); list_del(&tt->tt_list);
@@ -2165,7 +2163,7 @@ static void xhci_create_rhub_port_array(struct xhci_hcd *xhci,
if (!rhub->ports) if (!rhub->ports)
return; return;
for (i = 0; i < HCS_MAX_PORTS(xhci->hcs_params1); i++) { for (i = 0; i < xhci->max_ports; i++) {
if (xhci->hw_ports[i].rhub != rhub || if (xhci->hw_ports[i].rhub != rhub ||
xhci->hw_ports[i].hcd_portnum == DUPLICATE_ENTRY) xhci->hw_ports[i].hcd_portnum == DUPLICATE_ENTRY)
continue; continue;
@@ -2188,32 +2186,28 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
{ {
void __iomem *base; void __iomem *base;
u32 offset; u32 offset;
unsigned int num_ports;
int i, j; int i, j;
int cap_count = 0; int cap_count = 0;
u32 cap_start; u32 cap_start;
struct device *dev = xhci_to_hcd(xhci)->self.sysdev; struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
num_ports = HCS_MAX_PORTS(xhci->hcs_params1); xhci->hw_ports = kcalloc_node(xhci->max_ports, sizeof(*xhci->hw_ports),
xhci->hw_ports = kcalloc_node(num_ports, sizeof(*xhci->hw_ports), flags, dev_to_node(dev));
flags, dev_to_node(dev));
if (!xhci->hw_ports) if (!xhci->hw_ports)
return -ENOMEM; return -ENOMEM;
for (i = 0; i < num_ports; i++) { for (i = 0; i < xhci->max_ports; i++) {
xhci->hw_ports[i].addr = &xhci->op_regs->port_status_base + xhci->hw_ports[i].port_reg = &xhci->op_regs->port_regs[i];
NUM_PORT_REGS * i;
xhci->hw_ports[i].hw_portnum = i; xhci->hw_ports[i].hw_portnum = i;
init_completion(&xhci->hw_ports[i].rexit_done); init_completion(&xhci->hw_ports[i].rexit_done);
init_completion(&xhci->hw_ports[i].u3exit_done); init_completion(&xhci->hw_ports[i].u3exit_done);
} }
xhci->rh_bw = kcalloc_node(num_ports, sizeof(*xhci->rh_bw), flags, xhci->rh_bw = kcalloc_node(xhci->max_ports, sizeof(*xhci->rh_bw), flags, dev_to_node(dev));
dev_to_node(dev));
if (!xhci->rh_bw) if (!xhci->rh_bw)
return -ENOMEM; return -ENOMEM;
for (i = 0; i < num_ports; i++) { for (i = 0; i < xhci->max_ports; i++) {
struct xhci_interval_bw_table *bw_table; struct xhci_interval_bw_table *bw_table;
INIT_LIST_HEAD(&xhci->rh_bw[i].tts); INIT_LIST_HEAD(&xhci->rh_bw[i].tts);
@@ -2245,9 +2239,8 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
offset = cap_start; offset = cap_start;
while (offset) { while (offset) {
xhci_add_in_port(xhci, num_ports, base + offset, cap_count); xhci_add_in_port(xhci, xhci->max_ports, base + offset, cap_count);
if (xhci->usb2_rhub.num_ports + xhci->usb3_rhub.num_ports == if (xhci->usb2_rhub.num_ports + xhci->usb3_rhub.num_ports == xhci->max_ports)
num_ports)
break; break;
offset = xhci_find_next_ext_cap(base, offset, offset = xhci_find_next_ext_cap(base, offset,
XHCI_EXT_CAPS_PROTOCOL); XHCI_EXT_CAPS_PROTOCOL);

View File

@@ -670,7 +670,6 @@ static int xhci_mtk_probe(struct platform_device *pdev)
} }
device_enable_async_suspend(dev); device_enable_async_suspend(dev);
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev); pm_runtime_put_autosuspend(dev);
pm_runtime_forbid(dev); pm_runtime_forbid(dev);

View File

@@ -21,7 +21,7 @@
/* support at most 64 ep, use 32 size hash table */ /* support at most 64 ep, use 32 size hash table */
#define SCH_EP_HASH_BITS 5 #define SCH_EP_HASH_BITS 5
/** /*
* To simplify scheduler algorithm, set a upper limit for ESIT, * To simplify scheduler algorithm, set a upper limit for ESIT,
* if a synchromous ep's ESIT is larger than @XHCI_MTK_MAX_ESIT, * if a synchromous ep's ESIT is larger than @XHCI_MTK_MAX_ESIT,
* round down to the limit value, that means allocating more * round down to the limit value, that means allocating more
@@ -34,6 +34,7 @@
#define XHCI_MTK_FRAMES_CNT (XHCI_MTK_MAX_ESIT / UFRAMES_PER_FRAME) #define XHCI_MTK_FRAMES_CNT (XHCI_MTK_MAX_ESIT / UFRAMES_PER_FRAME)
/** /**
* struct mu3h_sch_tt - TT scheduling data
* @fs_bus_bw_out: save bandwidth used by FS/LS OUT eps in each uframes * @fs_bus_bw_out: save bandwidth used by FS/LS OUT eps in each uframes
* @fs_bus_bw_in: save bandwidth used by FS/LS IN eps in each uframes * @fs_bus_bw_in: save bandwidth used by FS/LS IN eps in each uframes
* @ls_bus_bw: save bandwidth used by LS eps in each uframes * @ls_bus_bw: save bandwidth used by LS eps in each uframes
@@ -51,7 +52,7 @@ struct mu3h_sch_tt {
}; };
/** /**
* struct mu3h_sch_bw_info: schedule information for bandwidth domain * struct mu3h_sch_bw_info - schedule information for bandwidth domain
* *
* @bus_bw: array to keep track of bandwidth already used at each uframes * @bus_bw: array to keep track of bandwidth already used at each uframes
* *
@@ -63,7 +64,7 @@ struct mu3h_sch_bw_info {
}; };
/** /**
* struct mu3h_sch_ep_info: schedule information for endpoint * struct mu3h_sch_ep_info - schedule information for endpoint
* *
* @esit: unit is 125us, equal to 2 << Interval field in ep-context * @esit: unit is 125us, equal to 2 << Interval field in ep-context
* @num_esit: number of @esit in a period * @num_esit: number of @esit in a period
@@ -77,6 +78,7 @@ struct mu3h_sch_bw_info {
* @ep_type: endpoint type * @ep_type: endpoint type
* @maxpkt: max packet size of endpoint * @maxpkt: max packet size of endpoint
* @ep: address of usb_host_endpoint struct * @ep: address of usb_host_endpoint struct
* @speed: usb device speed
* @allocated: the bandwidth is aready allocated from bus_bw * @allocated: the bandwidth is aready allocated from bus_bw
* @offset: which uframe of the interval that transfer should be * @offset: which uframe of the interval that transfer should be
* scheduled first time within the interval * scheduled first time within the interval
@@ -125,7 +127,7 @@ struct mu3h_sch_ep_info {
#define MU3C_U2_PORT_MAX 5 #define MU3C_U2_PORT_MAX 5
/** /**
* struct mu3c_ippc_regs: MTK ssusb ip port control registers * struct mu3c_ippc_regs - MTK ssusb ip port control registers
* @ip_pw_ctr0~3: ip power and clock control registers * @ip_pw_ctr0~3: ip power and clock control registers
* @ip_pw_sts1~2: ip power and clock status registers * @ip_pw_sts1~2: ip power and clock status registers
* @ip_xhci_cap: ip xHCI capability register * @ip_xhci_cap: ip xHCI capability register

View File

@@ -896,9 +896,9 @@ static int xhci_pci_poweroff_late(struct usb_hcd *hcd, bool do_wakeup)
if (!(xhci->quirks & XHCI_RESET_TO_DEFAULT)) if (!(xhci->quirks & XHCI_RESET_TO_DEFAULT))
return 0; return 0;
for (i = 0; i < HCS_MAX_PORTS(xhci->hcs_params1); i++) { for (i = 0; i < xhci->max_ports; i++) {
port = &xhci->hw_ports[i]; port = &xhci->hw_ports[i];
portsc = readl(port->addr); portsc = xhci_portsc_readl(port);
if ((portsc & PORT_PLS_MASK) != XDEV_U3) if ((portsc & PORT_PLS_MASK) != XDEV_U3)
continue; continue;
@@ -919,7 +919,7 @@ static int xhci_pci_poweroff_late(struct usb_hcd *hcd, bool do_wakeup)
xhci_dbg(xhci, "port %d-%d in U3 without wakeup, disable it\n", xhci_dbg(xhci, "port %d-%d in U3 without wakeup, disable it\n",
port->rhub->hcd->self.busnum, port->hcd_portnum + 1); port->rhub->hcd->self.busnum, port->hcd_portnum + 1);
portsc = xhci_port_state_to_neutral(portsc); portsc = xhci_port_state_to_neutral(portsc);
writel(portsc | PORT_PE, port->addr); xhci_portsc_writel(port, portsc | PORT_PE);
} }
return 0; return 0;

View File

@@ -144,9 +144,14 @@
#define PORT_TEST_MODE_SHIFT 28 #define PORT_TEST_MODE_SHIFT 28
/* USB3 Protocol PORTLI Port Link Information */ /* USB3 Protocol PORTLI Port Link Information */
#define PORT_LEC(p) ((p) & 0xffff)
#define PORT_RX_LANES(p) (((p) >> 16) & 0xf) #define PORT_RX_LANES(p) (((p) >> 16) & 0xf)
#define PORT_TX_LANES(p) (((p) >> 20) & 0xf) #define PORT_TX_LANES(p) (((p) >> 20) & 0xf)
/* eUSB2v2 protocol PORTLI Port Link information, RsvdP for normal USB2 */
#define PORTLI_RDR(p) ((p) & 0xf)
#define PORTLI_TDR(p) (((p) >> 4) & 0xf)
/* USB2 Protocol PORTHLPMC */ /* USB2 Protocol PORTHLPMC */
#define PORT_HIRDM(p)((p) & 3) #define PORT_HIRDM(p)((p) & 3)
#define PORT_L1_TIMEOUT(p)(((p) & 0xff) << 2) #define PORT_L1_TIMEOUT(p)(((p) & 0xff) << 2)

View File

@@ -82,6 +82,23 @@ dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg,
return seg->dma + (segment_offset * sizeof(*trb)); return seg->dma + (segment_offset * sizeof(*trb));
} }
static union xhci_trb *xhci_dma_to_trb(struct xhci_segment *start_seg,
dma_addr_t dma,
struct xhci_segment **match_seg)
{
struct xhci_segment *seg;
xhci_for_each_ring_seg(start_seg, seg) {
if (in_range(dma, seg->dma, TRB_SEGMENT_SIZE)) {
if (match_seg)
*match_seg = seg;
return &seg->trbs[(dma - seg->dma) / sizeof(union xhci_trb)];
}
}
return NULL;
}
static bool trb_is_noop(union xhci_trb *trb) static bool trb_is_noop(union xhci_trb *trb)
{ {
return TRB_TYPE_NOOP_LE32(trb->generic.field[3]); return TRB_TYPE_NOOP_LE32(trb->generic.field[3]);
@@ -128,11 +145,11 @@ static void inc_td_cnt(struct urb *urb)
urb_priv->num_tds_done++; urb_priv->num_tds_done++;
} }
static void trb_to_noop(union xhci_trb *trb, u32 noop_type) static void trb_to_noop(union xhci_trb *trb, u32 noop_type, bool unchain_links)
{ {
if (trb_is_link(trb)) { if (trb_is_link(trb)) {
/* unchain chained link TRBs */ if (unchain_links)
trb->link.control &= cpu_to_le32(~TRB_CHAIN); trb->link.control &= cpu_to_le32(~TRB_CHAIN);
} else { } else {
trb->generic.field[0] = 0; trb->generic.field[0] = 0;
trb->generic.field[1] = 0; trb->generic.field[1] = 0;
@@ -143,6 +160,11 @@ static void trb_to_noop(union xhci_trb *trb, u32 noop_type)
} }
} }
static unsigned int trb_to_pos(struct xhci_segment *seg, union xhci_trb *trb)
{
return seg->num * TRBS_PER_SEGMENT + (trb - seg->trbs);
}
/* Updates trb to point to the next TRB in the ring, and updates seg if the next /* Updates trb to point to the next TRB in the ring, and updates seg if the next
* TRB is in a new segment. This does not skip over link TRBs, and it does not * TRB is in a new segment. This does not skip over link TRBs, and it does not
* effect the ring dequeue or enqueue pointers. * effect the ring dequeue or enqueue pointers.
@@ -282,55 +304,34 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring,
inc_enq_past_link(xhci, ring, chain); inc_enq_past_link(xhci, ring, chain);
} }
/* static bool dma_in_range(dma_addr_t dma,
* If the suspect DMA address is a TRB in this TD, this function returns that struct xhci_segment *start_seg, union xhci_trb *start_trb,
* TRB's segment. Otherwise it returns 0. struct xhci_segment *end_seg, union xhci_trb *end_trb)
*/
static struct xhci_segment *trb_in_td(struct xhci_td *td, dma_addr_t suspect_dma)
{ {
dma_addr_t start_dma; unsigned int pos, start, end;
dma_addr_t end_seg_dma; struct xhci_segment *pos_seg;
dma_addr_t end_trb_dma; union xhci_trb *pos_trb = xhci_dma_to_trb(start_seg, dma, &pos_seg);
struct xhci_segment *cur_seg;
start_dma = xhci_trb_virt_to_dma(td->start_seg, td->start_trb); /* Is the trb dma address even part of the whole ring? */
cur_seg = td->start_seg; if (!pos_trb)
return false;
do { pos = trb_to_pos(pos_seg, pos_trb);
if (start_dma == 0) start = trb_to_pos(start_seg, start_trb);
return NULL; end = trb_to_pos(end_seg, end_trb);
/* We may get an event for a Link TRB in the middle of a TD */
end_seg_dma = xhci_trb_virt_to_dma(cur_seg,
&cur_seg->trbs[TRBS_PER_SEGMENT - 1]);
/* If the end TRB isn't in this segment, this is set to 0 */
end_trb_dma = xhci_trb_virt_to_dma(cur_seg, td->end_trb);
if (end_trb_dma > 0) { /* end position is smaller than start, search range wraps around */
/* The end TRB is in this segment, so suspect should be here */ if (end < start)
if (start_dma <= end_trb_dma) { return !(pos > end && pos < start);
if (suspect_dma >= start_dma && suspect_dma <= end_trb_dma)
return cur_seg;
} else {
/* Case for one segment with
* a TD wrapped around to the top
*/
if ((suspect_dma >= start_dma &&
suspect_dma <= end_seg_dma) ||
(suspect_dma >= cur_seg->dma &&
suspect_dma <= end_trb_dma))
return cur_seg;
}
return NULL;
}
/* Might still be somewhere in this segment */
if (suspect_dma >= start_dma && suspect_dma <= end_seg_dma)
return cur_seg;
cur_seg = cur_seg->next; return (pos >= start && pos <= end);
start_dma = xhci_trb_virt_to_dma(cur_seg, &cur_seg->trbs[0]); }
} while (cur_seg != td->start_seg);
return NULL; /* If the suspect DMA address is a TRB in this TD, this function returns true */
static bool trb_in_td(struct xhci_td *td, dma_addr_t suspect_dma)
{
return dma_in_range(suspect_dma, td->start_seg, td->start_trb,
td->end_seg, td->end_trb);
} }
/* /*
@@ -434,7 +435,7 @@ void xhci_ring_cmd_db(struct xhci_hcd *xhci)
static bool xhci_mod_cmd_timer(struct xhci_hcd *xhci) static bool xhci_mod_cmd_timer(struct xhci_hcd *xhci)
{ {
return mod_delayed_work(system_wq, &xhci->cmd_timer, return mod_delayed_work(system_percpu_wq, &xhci->cmd_timer,
msecs_to_jiffies(xhci->current_cmd->timeout_ms)); msecs_to_jiffies(xhci->current_cmd->timeout_ms));
} }
@@ -465,7 +466,7 @@ static void xhci_handle_stopped_cmd_ring(struct xhci_hcd *xhci,
xhci_dbg(xhci, "Turn aborted command %p to no-op\n", xhci_dbg(xhci, "Turn aborted command %p to no-op\n",
i_cmd->command_trb); i_cmd->command_trb);
trb_to_noop(i_cmd->command_trb, TRB_CMD_NOOP); trb_to_noop(i_cmd->command_trb, TRB_CMD_NOOP, false);
/* /*
* caller waiting for completion is called when command * caller waiting for completion is called when command
@@ -797,13 +798,18 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci,
* (The last TRB actually points to the ring enqueue pointer, which is not part * (The last TRB actually points to the ring enqueue pointer, which is not part
* of this TD.) This is used to remove partially enqueued isoc TDs from a ring. * of this TD.) This is used to remove partially enqueued isoc TDs from a ring.
*/ */
static void td_to_noop(struct xhci_td *td, bool flip_cycle) static void td_to_noop(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
struct xhci_td *td, bool flip_cycle)
{ {
bool unchain_links;
struct xhci_segment *seg = td->start_seg; struct xhci_segment *seg = td->start_seg;
union xhci_trb *trb = td->start_trb; union xhci_trb *trb = td->start_trb;
/* link TRBs should now be unchained, but some old HCs expect otherwise */
unchain_links = !xhci_link_chain_quirk(xhci, ep->ring ? ep->ring->type : TYPE_STREAM);
while (1) { while (1) {
trb_to_noop(trb, TRB_TR_NOOP); trb_to_noop(trb, TRB_TR_NOOP, unchain_links);
/* flip cycle if asked to */ /* flip cycle if asked to */
if (flip_cycle && trb != td->start_trb && trb != td->end_trb) if (flip_cycle && trb != td->start_trb && trb != td->end_trb)
@@ -1091,16 +1097,16 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep)
"Found multiple active URBs %p and %p in stream %u?\n", "Found multiple active URBs %p and %p in stream %u?\n",
td->urb, cached_td->urb, td->urb, cached_td->urb,
td->urb->stream_id); td->urb->stream_id);
td_to_noop(cached_td, false); td_to_noop(xhci, ep, cached_td, false);
cached_td->cancel_status = TD_CLEARED; cached_td->cancel_status = TD_CLEARED;
} }
td_to_noop(td, false); td_to_noop(xhci, ep, td, false);
td->cancel_status = TD_CLEARING_CACHE; td->cancel_status = TD_CLEARING_CACHE;
cached_td = td; cached_td = td;
break; break;
} }
} else { } else {
td_to_noop(td, false); td_to_noop(xhci, ep, td, false);
td->cancel_status = TD_CLEARED; td->cancel_status = TD_CLEARED;
} }
} }
@@ -1125,7 +1131,7 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep)
continue; continue;
xhci_warn(xhci, "Failed to clear cancelled cached URB %p, mark clear anyway\n", xhci_warn(xhci, "Failed to clear cancelled cached URB %p, mark clear anyway\n",
td->urb); td->urb);
td_to_noop(td, false); td_to_noop(xhci, ep, td, false);
td->cancel_status = TD_CLEARED; td->cancel_status = TD_CLEARED;
} }
} }
@@ -1388,7 +1394,7 @@ void xhci_hc_died(struct xhci_hcd *xhci)
xhci_cleanup_command_queue(xhci); xhci_cleanup_command_queue(xhci);
/* return any pending urbs, remove may be waiting for them */ /* return any pending urbs, remove may be waiting for them */
for (i = 0; i <= HCS_MAX_SLOTS(xhci->hcs_params1); i++) { for (i = 0; i <= xhci->max_slots; i++) {
if (!xhci->devs[i]) if (!xhci->devs[i])
continue; continue;
for (j = 0; j < 31; j++) for (j = 0; j < 31; j++)
@@ -1989,7 +1995,6 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event)
struct usb_hcd *hcd; struct usb_hcd *hcd;
u32 port_id; u32 port_id;
u32 portsc, cmd_reg; u32 portsc, cmd_reg;
int max_ports;
unsigned int hcd_portnum; unsigned int hcd_portnum;
struct xhci_bus_state *bus_state; struct xhci_bus_state *bus_state;
bool bogus_port_status = false; bool bogus_port_status = false;
@@ -2001,9 +2006,8 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event)
"WARN: xHC returned failed port status event\n"); "WARN: xHC returned failed port status event\n");
port_id = GET_PORT_ID(le32_to_cpu(event->generic.field[0])); port_id = GET_PORT_ID(le32_to_cpu(event->generic.field[0]));
max_ports = HCS_MAX_PORTS(xhci->hcs_params1);
if ((port_id <= 0) || (port_id > max_ports)) { if ((port_id <= 0) || (port_id > xhci->max_ports)) {
xhci_warn(xhci, "Port change event with invalid port ID %d\n", xhci_warn(xhci, "Port change event with invalid port ID %d\n",
port_id); port_id);
return; return;
@@ -2030,7 +2034,7 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event)
hcd = port->rhub->hcd; hcd = port->rhub->hcd;
bus_state = &port->rhub->bus_state; bus_state = &port->rhub->bus_state;
hcd_portnum = port->hcd_portnum; hcd_portnum = port->hcd_portnum;
portsc = readl(port->addr); portsc = xhci_portsc_readl(port);
xhci_dbg(xhci, "Port change event, %d-%d, id %d, portsc: 0x%x\n", xhci_dbg(xhci, "Port change event, %d-%d, id %d, portsc: 0x%x\n",
hcd->self.busnum, hcd_portnum + 1, port_id, portsc); hcd->self.busnum, hcd_portnum + 1, port_id, portsc);
@@ -2184,24 +2188,31 @@ static void xhci_clear_hub_tt_buffer(struct xhci_hcd *xhci, struct xhci_td *td,
* External device side is also halted in functional stall cases. Class driver * External device side is also halted in functional stall cases. Class driver
* will clear the device halt with a CLEAR_FEATURE(ENDPOINT_HALT) request later. * will clear the device halt with a CLEAR_FEATURE(ENDPOINT_HALT) request later.
*/ */
static bool xhci_halted_host_endpoint(struct xhci_ep_ctx *ep_ctx, unsigned int comp_code) static bool xhci_halted_host_endpoint(struct xhci_hcd *xhci, struct xhci_ep_ctx *ep_ctx,
unsigned int comp_code)
{ {
/* Stall halts both internal and device side endpoint */ int ep_type = CTX_TO_EP_TYPE(le32_to_cpu(ep_ctx->ep_info2));
if (comp_code == COMP_STALL_ERROR)
return true;
/* TRB completion codes that may require internal halt cleanup */ switch (comp_code) {
if (comp_code == COMP_USB_TRANSACTION_ERROR || case COMP_STALL_ERROR:
comp_code == COMP_BABBLE_DETECTED_ERROR || /* on xHCI this always halts, including protocol stall */
comp_code == COMP_SPLIT_TRANSACTION_ERROR) return true;
case COMP_BABBLE_DETECTED_ERROR:
/* /*
* The 0.95 spec says a babbling control endpoint is not halted. * The 0.95 spec says a babbling control endpoint is not halted.
* The 0.96 spec says it is. Some HW claims to be 0.95 * The 0.96 spec says it is. Some HW claims to be 0.95
* compliant, but it halts the control endpoint anyway. * compliant, but it halts the control endpoint anyway.
* Check endpoint context if endpoint is halted. * Check endpoint context if endpoint is halted.
*/ */
if (GET_EP_CTX_STATE(ep_ctx) == EP_STATE_HALTED) if (xhci->hci_version <= 0x95 && ep_type == CTRL_EP)
return true; return GET_EP_CTX_STATE(ep_ctx) == EP_STATE_HALTED;
fallthrough;
case COMP_USB_TRANSACTION_ERROR:
case COMP_SPLIT_TRANSACTION_ERROR:
/* these errors halt all non-isochronous endpoints */
return ep_type != ISOC_IN_EP && ep_type != ISOC_OUT_EP;
}
return false; return false;
} }
@@ -2238,41 +2249,9 @@ static void finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
* the ring dequeue pointer or take this TD off any lists yet. * the ring dequeue pointer or take this TD off any lists yet.
*/ */
return; return;
case COMP_USB_TRANSACTION_ERROR: }
case COMP_BABBLE_DETECTED_ERROR:
case COMP_SPLIT_TRANSACTION_ERROR: if (xhci_halted_host_endpoint(xhci, ep_ctx, trb_comp_code)) {
/*
* If endpoint context state is not halted we might be
* racing with a reset endpoint command issued by a unsuccessful
* stop endpoint completion (context error). In that case the
* td should be on the cancelled list, and EP_HALTED flag set.
*
* Or then it's not halted due to the 0.95 spec stating that a
* babbling control endpoint should not halt. The 0.96 spec
* again says it should. Some HW claims to be 0.95 compliant,
* but it halts the control endpoint anyway.
*/
if (GET_EP_CTX_STATE(ep_ctx) != EP_STATE_HALTED) {
/*
* If EP_HALTED is set and TD is on the cancelled list
* the TD and dequeue pointer will be handled by reset
* ep command completion
*/
if ((ep->ep_state & EP_HALTED) &&
!list_empty(&td->cancelled_td_list)) {
xhci_dbg(xhci, "Already resolving halted ep for 0x%llx\n",
(unsigned long long)xhci_trb_virt_to_dma(
td->start_seg, td->start_trb));
return;
}
/* endpoint not halted, don't reset it */
break;
}
/* Almost same procedure as for STALL_ERROR below */
xhci_clear_hub_tt_buffer(xhci, td, ep);
xhci_handle_halted_endpoint(xhci, ep, td, EP_HARD_RESET);
return;
case COMP_STALL_ERROR:
/* /*
* xhci internal endpoint state will go to a "halt" state for * xhci internal endpoint state will go to a "halt" state for
* any stall, including default control pipe protocol stall. * any stall, including default control pipe protocol stall.
@@ -2283,14 +2262,12 @@ static void finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
* stall later. Hub TT buffer should only be cleared for FS/LS * stall later. Hub TT buffer should only be cleared for FS/LS
* devices behind HS hubs for functional stalls. * devices behind HS hubs for functional stalls.
*/ */
if (ep->ep_index != 0) if (!(ep->ep_index == 0 && trb_comp_code == COMP_STALL_ERROR))
xhci_clear_hub_tt_buffer(xhci, td, ep); xhci_clear_hub_tt_buffer(xhci, td, ep);
xhci_handle_halted_endpoint(xhci, ep, td, EP_HARD_RESET); xhci_handle_halted_endpoint(xhci, ep, td, EP_HARD_RESET);
return; /* xhci_handle_halted_endpoint marked td cancelled */ return; /* xhci_handle_halted_endpoint marked td cancelled */
default:
break;
} }
xhci_dequeue_td(xhci, td, ep_ring, td->status); xhci_dequeue_td(xhci, td, ep_ring, td->status);
@@ -2367,7 +2344,7 @@ static void process_ctrl_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
case COMP_STOPPED_LENGTH_INVALID: case COMP_STOPPED_LENGTH_INVALID:
goto finish_td; goto finish_td;
default: default:
if (!xhci_halted_host_endpoint(ep_ctx, trb_comp_code)) if (!xhci_halted_host_endpoint(xhci, ep_ctx, trb_comp_code))
break; break;
xhci_dbg(xhci, "TRB error %u, halted endpoint index = %u\n", xhci_dbg(xhci, "TRB error %u, halted endpoint index = %u\n",
trb_comp_code, ep->ep_index); trb_comp_code, ep->ep_index);
@@ -2663,7 +2640,6 @@ static int handle_tx_event(struct xhci_hcd *xhci,
int ep_index; int ep_index;
struct xhci_td *td = NULL; struct xhci_td *td = NULL;
dma_addr_t ep_trb_dma; dma_addr_t ep_trb_dma;
struct xhci_segment *ep_seg;
union xhci_trb *ep_trb; union xhci_trb *ep_trb;
int status = -EINPROGRESS; int status = -EINPROGRESS;
struct xhci_ep_ctx *ep_ctx; struct xhci_ep_ctx *ep_ctx;
@@ -2694,6 +2670,9 @@ static int handle_tx_event(struct xhci_hcd *xhci,
if (!ep_ring) if (!ep_ring)
return handle_transferless_tx_event(xhci, ep, trb_comp_code); return handle_transferless_tx_event(xhci, ep, trb_comp_code);
/* find the transfer trb this events points to */
ep_trb = xhci_dma_to_trb(ep_ring->deq_seg, ep_trb_dma, NULL);
/* Look for common error cases */ /* Look for common error cases */
switch (trb_comp_code) { switch (trb_comp_code) {
/* Skip codes that require special handling depending on /* Skip codes that require special handling depending on
@@ -2867,10 +2846,8 @@ static int handle_tx_event(struct xhci_hcd *xhci,
td = list_first_entry(&ep_ring->td_list, struct xhci_td, td = list_first_entry(&ep_ring->td_list, struct xhci_td,
td_list); td_list);
/* Is this a TRB in the currently executing TD? */ /* Is this TRB not part of the currently executing TD? */
ep_seg = trb_in_td(td, ep_trb_dma); if (!trb_in_td(td, ep_trb_dma)) {
if (!ep_seg) {
if (ep->skip && usb_endpoint_xfer_isoc(&td->urb->ep->desc)) { if (ep->skip && usb_endpoint_xfer_isoc(&td->urb->ep->desc)) {
/* this event is unlikely to match any TD, don't skip them all */ /* this event is unlikely to match any TD, don't skip them all */
@@ -2953,7 +2930,6 @@ static int handle_tx_event(struct xhci_hcd *xhci,
if (ring_xrun_event) if (ring_xrun_event)
return 0; return 0;
ep_trb = &ep_seg->trbs[(ep_trb_dma - ep_seg->dma) / sizeof(*ep_trb)];
trace_xhci_handle_transfer(ep_ring, (struct xhci_generic_trb *) ep_trb, ep_trb_dma); trace_xhci_handle_transfer(ep_ring, (struct xhci_generic_trb *) ep_trb, ep_trb_dma);
/* /*
@@ -2978,7 +2954,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
return 0; return 0;
check_endpoint_halted: check_endpoint_halted:
if (xhci_halted_host_endpoint(ep_ctx, trb_comp_code)) if (xhci_halted_host_endpoint(xhci, ep_ctx, trb_comp_code))
xhci_handle_halted_endpoint(xhci, ep, td, EP_HARD_RESET); xhci_handle_halted_endpoint(xhci, ep, td, EP_HARD_RESET);
return 0; return 0;
@@ -3990,6 +3966,16 @@ static unsigned int xhci_get_last_burst_packet_count(struct xhci_hcd *xhci,
return total_packet_count - 1; return total_packet_count - 1;
} }
/* Returns the Isochronous Scheduling Threshold in Microframes. 1 Frame is 8 Microframes. */
static int xhci_ist_microframes(struct xhci_hcd *xhci)
{
int ist = HCS_IST_VALUE(xhci->hcs_params2);
if (xhci->hcs_params2 & HCS_IST_UNIT)
ist *= 8;
return ist;
}
/* /*
* Calculates Frame ID field of the isochronous TRB identifies the * Calculates Frame ID field of the isochronous TRB identifies the
* target frame that the Interval associated with this Isochronous * target frame that the Interval associated with this Isochronous
@@ -4009,17 +3995,7 @@ static int xhci_get_isoc_frame_id(struct xhci_hcd *xhci,
else else
start_frame = (urb->start_frame + index * urb->interval) >> 3; start_frame = (urb->start_frame + index * urb->interval) >> 3;
/* Isochronous Scheduling Threshold (IST, bits 0~3 in HCSPARAMS2): ist = xhci_ist_microframes(xhci);
*
* If bit [3] of IST is cleared to '0', software can add a TRB no
* later than IST[2:0] Microframes before that TRB is scheduled to
* be executed.
* If bit [3] of IST is set to '1', software can add a TRB no later
* than IST[2:0] Frames before that TRB is scheduled to be executed.
*/
ist = HCS_IST(xhci->hcs_params2) & 0x7;
if (HCS_IST(xhci->hcs_params2) & (1 << 3))
ist <<= 3;
/* Software shall not schedule an Isoch TD with a Frame ID value that /* Software shall not schedule an Isoch TD with a Frame ID value that
* is less than the Start Frame ID or greater than the End Frame ID, * is less than the Start Frame ID or greater than the End Frame ID,
@@ -4164,7 +4140,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
/* use SIA as default, if frame id is used overwrite it */ /* use SIA as default, if frame id is used overwrite it */
sia_frame_id = TRB_SIA; sia_frame_id = TRB_SIA;
if (!(urb->transfer_flags & URB_ISO_ASAP) && if (!(urb->transfer_flags & URB_ISO_ASAP) &&
HCC_CFC(xhci->hcc_params)) { (xhci->hcc_params & HCC_CFC)) {
frame_id = xhci_get_isoc_frame_id(xhci, urb, i); frame_id = xhci_get_isoc_frame_id(xhci, urb, i);
if (frame_id >= 0) if (frame_id >= 0)
sia_frame_id = TRB_FRAME_ID(frame_id); sia_frame_id = TRB_FRAME_ID(frame_id);
@@ -4248,7 +4224,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
} }
/* store the next frame id */ /* store the next frame id */
if (HCC_CFC(xhci->hcc_params)) if (xhci->hcc_params & HCC_CFC)
xep->next_frame_id = urb->start_frame + num_tds * urb->interval; xep->next_frame_id = urb->start_frame + num_tds * urb->interval;
if (xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs == 0) { if (xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs == 0) {
@@ -4273,7 +4249,7 @@ cleanup:
*/ */
urb_priv->td[0].end_trb = ep_ring->enqueue; urb_priv->td[0].end_trb = ep_ring->enqueue;
/* Every TRB except the first & last will have its cycle bit flipped. */ /* Every TRB except the first & last will have its cycle bit flipped. */
td_to_noop(&urb_priv->td[0], true); td_to_noop(xhci, xep, &urb_priv->td[0], true);
/* Reset the ring enqueue back to the first TRB and its cycle bit. */ /* Reset the ring enqueue back to the first TRB and its cycle bit. */
ep_ring->enqueue = urb_priv->td[0].start_trb; ep_ring->enqueue = urb_priv->td[0].start_trb;
@@ -4327,7 +4303,7 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
check_interval(urb, ep_ctx); check_interval(urb, ep_ctx);
/* Calculate the start frame and put it in urb->start_frame. */ /* Calculate the start frame and put it in urb->start_frame. */
if (HCC_CFC(xhci->hcc_params) && !list_empty(&ep_ring->td_list)) { if ((xhci->hcc_params & HCC_CFC) && !list_empty(&ep_ring->td_list)) {
if (GET_EP_CTX_STATE(ep_ctx) == EP_STATE_RUNNING) { if (GET_EP_CTX_STATE(ep_ctx) == EP_STATE_RUNNING) {
urb->start_frame = xep->next_frame_id; urb->start_frame = xep->next_frame_id;
goto skip_start_over; goto skip_start_over;
@@ -4340,9 +4316,7 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
* Round up to the next frame and consider the time before trb really * Round up to the next frame and consider the time before trb really
* gets scheduled by hardare. * gets scheduled by hardare.
*/ */
ist = HCS_IST(xhci->hcs_params2) & 0x7; ist = xhci_ist_microframes(xhci);
if (HCS_IST(xhci->hcs_params2) & (1 << 3))
ist <<= 3;
start_frame += ist + XHCI_CFC_DELAY; start_frame += ist + XHCI_CFC_DELAY;
start_frame = roundup(start_frame, 8); start_frame = roundup(start_frame, 8);

View File

@@ -1399,7 +1399,6 @@ static void tegra_xhci_id_work(struct work_struct *work)
} }
tegra_xhci_set_port_power(tegra, true, true); tegra_xhci_set_port_power(tegra, true, true);
pm_runtime_mark_last_busy(tegra->dev);
} else { } else {
if (tegra->otg_usb3_port >= 0) if (tegra->otg_usb3_port >= 0)
@@ -2036,7 +2035,7 @@ static bool xhci_hub_ports_suspended(struct xhci_hub *hub)
u32 value; u32 value;
for (i = 0; i < hub->num_ports; i++) { for (i = 0; i < hub->num_ports; i++) {
value = readl(hub->ports[i]->addr); value = xhci_portsc_readl(hub->ports[i]);
if ((value & PORT_PE) == 0) if ((value & PORT_PE) == 0)
continue; continue;
@@ -2162,7 +2161,7 @@ static void tegra_xhci_enable_phy_sleepwalk_wake(struct tegra_xusb *tegra)
if (!is_host_mode_phy(tegra, i, j)) if (!is_host_mode_phy(tegra, i, j))
continue; continue;
portsc = readl(rhub->ports[index]->addr); portsc = xhci_portsc_readl(rhub->ports[index]);
speed = tegra_xhci_portsc_to_speed(tegra, portsc); speed = tegra_xhci_portsc_to_speed(tegra, portsc);
tegra_xusb_padctl_enable_phy_sleepwalk(padctl, phy, speed); tegra_xusb_padctl_enable_phy_sleepwalk(padctl, phy, speed);
tegra_xusb_padctl_enable_phy_wake(padctl, phy); tegra_xusb_padctl_enable_phy_wake(padctl, phy);
@@ -2257,7 +2256,7 @@ static int tegra_xusb_enter_elpg(struct tegra_xusb *tegra, bool is_auto_resume)
for (i = 0; i < xhci->usb2_rhub.num_ports; i++) { for (i = 0; i < xhci->usb2_rhub.num_ports; i++) {
if (!xhci->usb2_rhub.ports[i]) if (!xhci->usb2_rhub.ports[i])
continue; continue;
portsc = readl(xhci->usb2_rhub.ports[i]->addr); portsc = xhci_portsc_readl(xhci->usb2_rhub.ports[i]);
tegra->lp0_utmi_pad_mask &= ~BIT(i); tegra->lp0_utmi_pad_mask &= ~BIT(i);
if (((portsc & PORT_PLS_MASK) == XDEV_U3) || ((portsc & DEV_SPEED_MASK) == XDEV_FS)) if (((portsc & PORT_PLS_MASK) == XDEV_U3) || ((portsc & DEV_SPEED_MASK) == XDEV_FS))
tegra->lp0_utmi_pad_mask |= BIT(i); tegra->lp0_utmi_pad_mask |= BIT(i);
@@ -2790,7 +2789,7 @@ static int tegra_xhci_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value,
while (i--) { while (i--) {
if (!test_bit(i, &bus_state->resuming_ports)) if (!test_bit(i, &bus_state->resuming_ports))
continue; continue;
portsc = readl(ports[i]->addr); portsc = xhci_portsc_readl(ports[i]);
if ((portsc & PORT_PLS_MASK) == XDEV_RESUME) if ((portsc & PORT_PLS_MASK) == XDEV_RESUME)
tegra_phy_xusb_utmi_pad_power_on( tegra_phy_xusb_utmi_pad_power_on(
tegra_xusb_get_phy(tegra, "usb2", (int) i)); tegra_xusb_get_phy(tegra, "usb2", (int) i));
@@ -2808,7 +2807,7 @@ static int tegra_xhci_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value,
if (!index || index > rhub->num_ports) if (!index || index > rhub->num_ports)
return -EPIPE; return -EPIPE;
ports = rhub->ports; ports = rhub->ports;
portsc = readl(ports[port]->addr); portsc = xhci_portsc_readl(ports[port]);
if (portsc & PORT_CONNECT) if (portsc & PORT_CONNECT)
tegra_phy_xusb_utmi_pad_power_on(phy); tegra_phy_xusb_utmi_pad_power_on(phy);
} }
@@ -2827,7 +2826,7 @@ static int tegra_xhci_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value,
if ((type_req == ClearPortFeature) && (value == USB_PORT_FEAT_C_CONNECTION)) { if ((type_req == ClearPortFeature) && (value == USB_PORT_FEAT_C_CONNECTION)) {
ports = rhub->ports; ports = rhub->ports;
portsc = readl(ports[port]->addr); portsc = xhci_portsc_readl(ports[port]);
if (!(portsc & PORT_CONNECT)) { if (!(portsc & PORT_CONNECT)) {
/* We don't suspend the PAD while HNP role swap happens on the OTG /* We don't suspend the PAD while HNP role swap happens on the OTG
* port * port

View File

@@ -71,29 +71,20 @@ DEFINE_EVENT(xhci_log_msg, xhci_dbg_ring_expansion,
); );
DECLARE_EVENT_CLASS(xhci_log_ctx, DECLARE_EVENT_CLASS(xhci_log_ctx,
TP_PROTO(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx, TP_PROTO(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx),
unsigned int ep_num), TP_ARGS(xhci, ctx),
TP_ARGS(xhci, ctx, ep_num),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(int, ctx_64) __field(int, ctx_64)
__field(unsigned, ctx_type) __field(unsigned, ctx_type)
__field(dma_addr_t, ctx_dma) __field(dma_addr_t, ctx_dma)
__field(u8 *, ctx_va) __field(u8 *, ctx_va)
__field(unsigned, ctx_ep_num)
__dynamic_array(u32, ctx_data,
((HCC_64BYTE_CONTEXT(xhci->hcc_params) + 1) * 8) *
((ctx->type == XHCI_CTX_TYPE_INPUT) + ep_num + 1))
), ),
TP_fast_assign( TP_fast_assign(
__entry->ctx_64 = HCC_64BYTE_CONTEXT(xhci->hcc_params); __entry->ctx_64 = xhci->hcc_params & HCC_64BYTE_CONTEXT;
__entry->ctx_type = ctx->type; __entry->ctx_type = ctx->type;
__entry->ctx_dma = ctx->dma; __entry->ctx_dma = ctx->dma;
__entry->ctx_va = ctx->bytes; __entry->ctx_va = ctx->bytes;
__entry->ctx_ep_num = ep_num;
memcpy(__get_dynamic_array(ctx_data), ctx->bytes,
((HCC_64BYTE_CONTEXT(xhci->hcc_params) + 1) * 32) *
((ctx->type == XHCI_CTX_TYPE_INPUT) + ep_num + 1));
), ),
TP_printk("ctx_64=%d, ctx_type=%u, ctx_dma=@%llx, ctx_va=@%p", TP_printk("ctx_64=%d, ctx_type=%u, ctx_dma=@%llx, ctx_va=@%p",
__entry->ctx_64, __entry->ctx_type, __entry->ctx_64, __entry->ctx_type,
@@ -102,9 +93,8 @@ DECLARE_EVENT_CLASS(xhci_log_ctx,
); );
DEFINE_EVENT(xhci_log_ctx, xhci_address_ctx, DEFINE_EVENT(xhci_log_ctx, xhci_address_ctx,
TP_PROTO(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx, TP_PROTO(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx),
unsigned int ep_num), TP_ARGS(xhci, ctx)
TP_ARGS(xhci, ctx, ep_num)
); );
DECLARE_EVENT_CLASS(xhci_log_trb, DECLARE_EVENT_CLASS(xhci_log_trb,
@@ -575,6 +565,11 @@ DEFINE_EVENT(xhci_log_portsc, xhci_hub_status_data,
TP_ARGS(port, portsc) TP_ARGS(port, portsc)
); );
DEFINE_EVENT(xhci_log_portsc, xhci_portsc_writel,
TP_PROTO(struct xhci_port *port, u32 portsc),
TP_ARGS(port, portsc)
);
DECLARE_EVENT_CLASS(xhci_log_doorbell, DECLARE_EVENT_CLASS(xhci_log_doorbell,
TP_PROTO(u32 slot, u32 doorbell), TP_PROTO(u32 slot, u32 doorbell),
TP_ARGS(slot, doorbell), TP_ARGS(slot, doorbell),

View File

@@ -41,6 +41,19 @@ static unsigned long long quirks;
module_param(quirks, ullong, S_IRUGO); module_param(quirks, ullong, S_IRUGO);
MODULE_PARM_DESC(quirks, "Bit flags for quirks to be enabled as default"); MODULE_PARM_DESC(quirks, "Bit flags for quirks to be enabled as default");
void xhci_portsc_writel(struct xhci_port *port, u32 val)
{
trace_xhci_portsc_writel(port, val);
writel(val, &port->port_reg->portsc);
}
EXPORT_SYMBOL_GPL(xhci_portsc_writel);
u32 xhci_portsc_readl(struct xhci_port *port)
{
return readl(&port->port_reg->portsc);
}
EXPORT_SYMBOL_GPL(xhci_portsc_readl);
static bool td_on_ring(struct xhci_td *td, struct xhci_ring *ring) static bool td_on_ring(struct xhci_td *td, struct xhci_ring *ring)
{ {
struct xhci_segment *seg; struct xhci_segment *seg;
@@ -237,7 +250,6 @@ static void xhci_zero_64b_regs(struct xhci_hcd *xhci)
struct iommu_domain *domain; struct iommu_domain *domain;
int err, i; int err, i;
u64 val; u64 val;
u32 intrs;
/* /*
* Some Renesas controllers get into a weird state if they are * Some Renesas controllers get into a weird state if they are
@@ -278,10 +290,7 @@ static void xhci_zero_64b_regs(struct xhci_hcd *xhci)
if (upper_32_bits(val)) if (upper_32_bits(val))
xhci_write_64(xhci, 0, &xhci->op_regs->cmd_ring); xhci_write_64(xhci, 0, &xhci->op_regs->cmd_ring);
intrs = min_t(u32, HCS_MAX_INTRS(xhci->hcs_params1), for (i = 0; i < xhci->max_interrupters; i++) {
ARRAY_SIZE(xhci->run_regs->ir_set));
for (i = 0; i < intrs; i++) {
struct xhci_intr_reg __iomem *ir; struct xhci_intr_reg __iomem *ir;
ir = &xhci->run_regs->ir_set[i]; ir = &xhci->run_regs->ir_set[i];
@@ -373,7 +382,7 @@ static void compliance_mode_recovery(struct timer_list *t)
return; return;
for (i = 0; i < rhub->num_ports; i++) { for (i = 0; i < rhub->num_ports; i++) {
temp = readl(rhub->ports[i]->addr); temp = xhci_portsc_readl(rhub->ports[i]);
if ((temp & PORT_PLS_MASK) == USB_SS_PORT_LS_COMP_MOD) { if ((temp & PORT_PLS_MASK) == USB_SS_PORT_LS_COMP_MOD) {
/* /*
* Compliance Mode Detected. Letting USB Core * Compliance Mode Detected. Letting USB Core
@@ -471,15 +480,13 @@ static void xhci_hcd_page_size(struct xhci_hcd *xhci)
static void xhci_enable_max_dev_slots(struct xhci_hcd *xhci) static void xhci_enable_max_dev_slots(struct xhci_hcd *xhci)
{ {
u32 config_reg; u32 config_reg;
u32 max_slots;
max_slots = HCS_MAX_SLOTS(xhci->hcs_params1);
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "xHC can handle at most %d device slots", xhci_dbg_trace(xhci, trace_xhci_dbg_init, "xHC can handle at most %d device slots",
max_slots); xhci->max_slots);
config_reg = readl(&xhci->op_regs->config_reg); config_reg = readl(&xhci->op_regs->config_reg);
config_reg &= ~HCS_SLOTS_MASK; config_reg &= ~HCS_SLOTS_MASK;
config_reg |= max_slots; config_reg |= xhci->max_slots;
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Setting Max device slots reg = 0x%x", xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Setting Max device slots reg = 0x%x",
config_reg); config_reg);
@@ -896,7 +903,7 @@ static void xhci_disable_hub_port_wake(struct xhci_hcd *xhci,
spin_lock_irqsave(&xhci->lock, flags); spin_lock_irqsave(&xhci->lock, flags);
for (i = 0; i < rhub->num_ports; i++) { for (i = 0; i < rhub->num_ports; i++) {
portsc = readl(rhub->ports[i]->addr); portsc = xhci_portsc_readl(rhub->ports[i]);
t1 = xhci_port_state_to_neutral(portsc); t1 = xhci_port_state_to_neutral(portsc);
t2 = t1; t2 = t1;
@@ -909,7 +916,7 @@ static void xhci_disable_hub_port_wake(struct xhci_hcd *xhci,
t2 |= PORT_CSC; t2 |= PORT_CSC;
if (t1 != t2) { if (t1 != t2) {
writel(t2, rhub->ports[i]->addr); xhci_portsc_writel(rhub->ports[i], t2);
xhci_dbg(xhci, "config port %d-%d wake bits, portsc: 0x%x, write: 0x%x\n", xhci_dbg(xhci, "config port %d-%d wake bits, portsc: 0x%x, write: 0x%x\n",
rhub->hcd->self.busnum, i + 1, portsc, t2); rhub->hcd->self.busnum, i + 1, portsc, t2);
} }
@@ -936,7 +943,7 @@ static bool xhci_pending_portevent(struct xhci_hcd *xhci)
port_index = xhci->usb2_rhub.num_ports; port_index = xhci->usb2_rhub.num_ports;
ports = xhci->usb2_rhub.ports; ports = xhci->usb2_rhub.ports;
while (port_index--) { while (port_index--) {
portsc = readl(ports[port_index]->addr); portsc = xhci_portsc_readl(ports[port_index]);
if (portsc & PORT_CHANGE_MASK || if (portsc & PORT_CHANGE_MASK ||
(portsc & PORT_PLS_MASK) == XDEV_RESUME) (portsc & PORT_PLS_MASK) == XDEV_RESUME)
return true; return true;
@@ -944,7 +951,7 @@ static bool xhci_pending_portevent(struct xhci_hcd *xhci)
port_index = xhci->usb3_rhub.num_ports; port_index = xhci->usb3_rhub.num_ports;
ports = xhci->usb3_rhub.ports; ports = xhci->usb3_rhub.ports;
while (port_index--) { while (port_index--) {
portsc = readl(ports[port_index]->addr); portsc = xhci_portsc_readl(ports[port_index]);
if (portsc & (PORT_CHANGE_MASK | PORT_CAS) || if (portsc & (PORT_CHANGE_MASK | PORT_CAS) ||
(portsc & PORT_PLS_MASK) == XDEV_RESUME) (portsc & PORT_PLS_MASK) == XDEV_RESUME)
return true; return true;
@@ -4223,8 +4230,7 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
xhci_err(xhci, "Error while assigning device slot ID: %s\n", xhci_err(xhci, "Error while assigning device slot ID: %s\n",
xhci_trb_comp_code_string(command->status)); xhci_trb_comp_code_string(command->status));
xhci_err(xhci, "Max number of devices this xHCI host supports is %u.\n", xhci_err(xhci, "Max number of devices this xHCI host supports is %u.\n",
HCS_MAX_SLOTS( xhci->max_slots);
readl(&xhci->cap_regs->hcs_params1)));
xhci_free_command(xhci, command); xhci_free_command(xhci, command);
return 0; return 0;
} }
@@ -4367,8 +4373,7 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
ctrl_ctx->add_flags = cpu_to_le32(SLOT_FLAG | EP0_FLAG); ctrl_ctx->add_flags = cpu_to_le32(SLOT_FLAG | EP0_FLAG);
ctrl_ctx->drop_flags = 0; ctrl_ctx->drop_flags = 0;
trace_xhci_address_ctx(xhci, virt_dev->in_ctx, trace_xhci_address_ctx(xhci, virt_dev->in_ctx);
le32_to_cpu(slot_ctx->dev_info) >> 27);
trace_xhci_address_ctrl_ctx(ctrl_ctx); trace_xhci_address_ctrl_ctx(ctrl_ctx);
spin_lock_irqsave(&xhci->lock, flags); spin_lock_irqsave(&xhci->lock, flags);
@@ -4428,7 +4433,7 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
xhci_err(xhci, xhci_err(xhci,
"ERROR: unexpected setup %s command completion code 0x%x.\n", "ERROR: unexpected setup %s command completion code 0x%x.\n",
act, command->status); act, command->status);
trace_xhci_address_ctx(xhci, virt_dev->out_ctx, 1); trace_xhci_address_ctx(xhci, virt_dev->out_ctx);
ret = -EINVAL; ret = -EINVAL;
break; break;
} }
@@ -4446,14 +4451,12 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
xhci_dbg_trace(xhci, trace_xhci_dbg_address, xhci_dbg_trace(xhci, trace_xhci_dbg_address,
"Output Context DMA address = %#08llx", "Output Context DMA address = %#08llx",
(unsigned long long)virt_dev->out_ctx->dma); (unsigned long long)virt_dev->out_ctx->dma);
trace_xhci_address_ctx(xhci, virt_dev->in_ctx, trace_xhci_address_ctx(xhci, virt_dev->in_ctx);
le32_to_cpu(slot_ctx->dev_info) >> 27);
/* /*
* USB core uses address 1 for the roothubs, so we add one to the * USB core uses address 1 for the roothubs, so we add one to the
* address given back to us by the HC. * address given back to us by the HC.
*/ */
trace_xhci_address_ctx(xhci, virt_dev->out_ctx, trace_xhci_address_ctx(xhci, virt_dev->out_ctx);
le32_to_cpu(slot_ctx->dev_info) >> 27);
/* Zero the input context control for later use */ /* Zero the input context control for later use */
ctrl_ctx->add_flags = 0; ctrl_ctx->add_flags = 0;
ctrl_ctx->drop_flags = 0; ctrl_ctx->drop_flags = 0;
@@ -4637,7 +4640,7 @@ static int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
{ {
struct xhci_hcd *xhci = hcd_to_xhci(hcd); struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct xhci_port **ports; struct xhci_port **ports;
__le32 __iomem *pm_addr, *hlpm_addr; struct xhci_port_regs __iomem *port_reg;
u32 pm_val, hlpm_val, field; u32 pm_val, hlpm_val, field;
unsigned int port_num; unsigned int port_num;
unsigned long flags; unsigned long flags;
@@ -4662,9 +4665,8 @@ static int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
ports = xhci->usb2_rhub.ports; ports = xhci->usb2_rhub.ports;
port_num = udev->portnum - 1; port_num = udev->portnum - 1;
pm_addr = ports[port_num]->addr + PORTPMSC; port_reg = ports[port_num]->port_reg;
pm_val = readl(pm_addr); pm_val = readl(&port_reg->portpmsc);
hlpm_addr = ports[port_num]->addr + PORTHLPMC;
xhci_dbg(xhci, "%s port %d USB2 hardware LPM\n", xhci_dbg(xhci, "%s port %d USB2 hardware LPM\n",
str_enable_disable(enable), port_num + 1); str_enable_disable(enable), port_num + 1);
@@ -4693,30 +4695,30 @@ static int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
spin_lock_irqsave(&xhci->lock, flags); spin_lock_irqsave(&xhci->lock, flags);
hlpm_val = xhci_calculate_usb2_hw_lpm_params(udev); hlpm_val = xhci_calculate_usb2_hw_lpm_params(udev);
writel(hlpm_val, hlpm_addr); writel(hlpm_val, &port_reg->porthlmpc);
/* flush write */ /* flush write */
readl(hlpm_addr); readl(&port_reg->porthlmpc);
} else { } else {
hird = xhci_calculate_hird_besl(xhci, udev); hird = xhci_calculate_hird_besl(xhci, udev);
} }
pm_val &= ~PORT_HIRD_MASK; pm_val &= ~PORT_HIRD_MASK;
pm_val |= PORT_HIRD(hird) | PORT_RWE | PORT_L1DS(udev->slot_id); pm_val |= PORT_HIRD(hird) | PORT_RWE | PORT_L1DS(udev->slot_id);
writel(pm_val, pm_addr); writel(pm_val, &port_reg->portpmsc);
pm_val = readl(pm_addr); pm_val = readl(&port_reg->portpmsc);
pm_val |= PORT_HLE; pm_val |= PORT_HLE;
writel(pm_val, pm_addr); writel(pm_val, &port_reg->portpmsc);
/* flush write */ /* flush write */
readl(pm_addr); readl(&port_reg->portpmsc);
} else { } else {
pm_val &= ~(PORT_HLE | PORT_RWE | PORT_HIRD_MASK | PORT_L1DS_MASK); pm_val &= ~(PORT_HLE | PORT_RWE | PORT_HIRD_MASK | PORT_L1DS_MASK);
writel(pm_val, pm_addr); writel(pm_val, &port_reg->portpmsc);
/* flush write */ /* flush write */
readl(pm_addr); readl(&port_reg->portpmsc);
if (udev->usb2_hw_lpm_besl_capable) { if (udev->usb2_hw_lpm_besl_capable) {
spin_unlock_irqrestore(&xhci->lock, flags); spin_unlock_irqrestore(&xhci->lock, flags);
xhci_change_max_exit_latency(xhci, udev, 0); xhci_change_max_exit_latency(xhci, udev, 0);
readl_poll_timeout(ports[port_num]->addr, pm_val, readl_poll_timeout(&ports[port_num]->port_reg->portsc, pm_val,
(pm_val & PORT_PLS_MASK) == XDEV_U0, (pm_val & PORT_PLS_MASK) == XDEV_U0,
100, 10000); 100, 10000);
return 0; return 0;
@@ -5409,6 +5411,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
*/ */
struct device *dev = hcd->self.sysdev; struct device *dev = hcd->self.sysdev;
int retval; int retval;
u32 hcs_params1;
/* Accept arbitrarily long scatter-gather lists */ /* Accept arbitrarily long scatter-gather lists */
hcd->self.sg_tablesize = ~0; hcd->self.sg_tablesize = ~0;
@@ -5434,7 +5437,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
xhci->run_regs = hcd->regs + xhci->run_regs = hcd->regs +
(readl(&xhci->cap_regs->run_regs_off) & RTSOFF_MASK); (readl(&xhci->cap_regs->run_regs_off) & RTSOFF_MASK);
/* Cache read-only capability registers */ /* Cache read-only capability registers */
xhci->hcs_params1 = readl(&xhci->cap_regs->hcs_params1); hcs_params1 = readl(&xhci->cap_regs->hcs_params1);
xhci->hcs_params2 = readl(&xhci->cap_regs->hcs_params2); xhci->hcs_params2 = readl(&xhci->cap_regs->hcs_params2);
xhci->hcs_params3 = readl(&xhci->cap_regs->hcs_params3); xhci->hcs_params3 = readl(&xhci->cap_regs->hcs_params3);
xhci->hci_version = HC_VERSION(readl(&xhci->cap_regs->hc_capbase)); xhci->hci_version = HC_VERSION(readl(&xhci->cap_regs->hc_capbase));
@@ -5442,10 +5445,13 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
if (xhci->hci_version > 0x100) if (xhci->hci_version > 0x100)
xhci->hcc_params2 = readl(&xhci->cap_regs->hcc_params2); xhci->hcc_params2 = readl(&xhci->cap_regs->hcc_params2);
xhci->max_slots = HCS_MAX_SLOTS(hcs_params1);
xhci->max_ports = min(HCS_MAX_PORTS(hcs_params1), MAX_HC_PORTS);
/* xhci-plat or xhci-pci might have set max_interrupters already */ /* xhci-plat or xhci-pci might have set max_interrupters already */
if ((!xhci->max_interrupters) || if (!xhci->max_interrupters)
xhci->max_interrupters > HCS_MAX_INTRS(xhci->hcs_params1)) xhci->max_interrupters = min(HCS_MAX_INTRS(hcs_params1), MAX_HC_INTRS);
xhci->max_interrupters = HCS_MAX_INTRS(xhci->hcs_params1); else if (xhci->max_interrupters > HCS_MAX_INTRS(hcs_params1))
xhci->max_interrupters = HCS_MAX_INTRS(hcs_params1);
xhci->quirks |= quirks; xhci->quirks |= quirks;
@@ -5490,7 +5496,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
/* Set dma_mask and coherent_dma_mask to 64-bits, /* Set dma_mask and coherent_dma_mask to 64-bits,
* if xHC supports 64-bit addressing */ * if xHC supports 64-bit addressing */
if (HCC_64BIT_ADDR(xhci->hcc_params) && if ((xhci->hcc_params & HCC_64BIT_ADDR) &&
!dma_set_mask(dev, DMA_BIT_MASK(64))) { !dma_set_mask(dev, DMA_BIT_MASK(64))) {
xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n"); xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n");
dma_set_coherent_mask(dev, DMA_BIT_MASK(64)); dma_set_coherent_mask(dev, DMA_BIT_MASK(64));
@@ -5664,8 +5670,8 @@ static int __init xhci_hcd_init(void)
BUILD_BUG_ON(sizeof(struct xhci_erst_entry) != 4*32/8); BUILD_BUG_ON(sizeof(struct xhci_erst_entry) != 4*32/8);
BUILD_BUG_ON(sizeof(struct xhci_cap_regs) != 8*32/8); BUILD_BUG_ON(sizeof(struct xhci_cap_regs) != 8*32/8);
BUILD_BUG_ON(sizeof(struct xhci_intr_reg) != 8*32/8); BUILD_BUG_ON(sizeof(struct xhci_intr_reg) != 8*32/8);
/* xhci_run_regs has eight fields and embeds 128 xhci_intr_regs */ /* xhci_run_regs has eight fields and embeds 1024 xhci_intr_regs */
BUILD_BUG_ON(sizeof(struct xhci_run_regs) != (8+8*128)*32/8); BUILD_BUG_ON(sizeof(struct xhci_run_regs) != (8+8*1024)*32/8);
if (usb_disabled()) if (usb_disabled())
return -ENODEV; return -ENODEV;

View File

@@ -34,8 +34,16 @@
/* Max number of USB devices for any host controller - limit in section 6.1 */ /* Max number of USB devices for any host controller - limit in section 6.1 */
#define MAX_HC_SLOTS 256 #define MAX_HC_SLOTS 256
/* Section 5.3.3 - MaxPorts */ /*
* Max Number of Ports. xHCI specification section 5.3.3
* Valid values are in the range of 1 to 255.
*/
#define MAX_HC_PORTS 127 #define MAX_HC_PORTS 127
/*
* Max number of Interrupter Register Sets. xHCI specification section 5.3.3
* Valid values are in the range of 1 to 1024.
*/
#define MAX_HC_INTRS 128
/* /*
* xHCI register interface. * xHCI register interface.
@@ -66,13 +74,19 @@ struct xhci_cap_regs {
/* Reserved up to (CAPLENGTH - 0x1C) */ /* Reserved up to (CAPLENGTH - 0x1C) */
}; };
/* Number of registers per port */ /*
#define NUM_PORT_REGS 4 * struct xhci_port_regs - Host Controller USB Port Register Set. xHCI spec 5.4.8
* @portsc: Port Status and Control
#define PORTSC 0 * @portpmsc: Port Power Management Status and Control
#define PORTPMSC 1 * @portli: Port Link Info
#define PORTLI 2 * @porthlmpc: Port Hardware LPM Control
#define PORTHLPMC 3 */
struct xhci_port_regs {
__le32 portsc;
__le32 portpmsc;
__le32 portli;
__le32 porthlmpc;
};
/** /**
* struct xhci_op_regs - xHCI Host Controller Operational Registers. * struct xhci_op_regs - xHCI Host Controller Operational Registers.
@@ -85,16 +99,7 @@ struct xhci_cap_regs {
* @cmd_ring: CRP - 64-bit Command Ring Pointer * @cmd_ring: CRP - 64-bit Command Ring Pointer
* @dcbaa_ptr: DCBAAP - 64-bit Device Context Base Address Array Pointer * @dcbaa_ptr: DCBAAP - 64-bit Device Context Base Address Array Pointer
* @config_reg: CONFIG - Configure Register * @config_reg: CONFIG - Configure Register
* @port_status_base: PORTSCn - base address for Port Status and Control * @port_regs: Port Register Sets, from 1 to MaxPorts (defined by HCSPARAMS1).
* Each port has a Port Status and Control register,
* followed by a Port Power Management Status and Control
* register, a Port Link Info register, and a reserved
* register.
* @port_power_base: PORTPMSCn - base address for
* Port Power Management Status and Control
* @port_link_base: PORTLIn - base address for Port Link Info (current
* Link PM state and control) for USB 2.1 and USB 3.0
* devices.
*/ */
struct xhci_op_regs { struct xhci_op_regs {
__le32 command; __le32 command;
@@ -110,13 +115,7 @@ struct xhci_op_regs {
__le32 config_reg; __le32 config_reg;
/* rsvd: offset 0x3C-3FF */ /* rsvd: offset 0x3C-3FF */
__le32 reserved4[241]; __le32 reserved4[241];
/* port 1 registers, which serve as a base address for other ports */ struct xhci_port_regs port_regs[];
__le32 port_status_base;
__le32 port_power_base;
__le32 port_link_base;
__le32 reserved5;
/* registers for ports 2-255 */
__le32 reserved6[NUM_PORT_REGS*254];
}; };
/* USBCMD - USB command - command bitmasks */ /* USBCMD - USB command - command bitmasks */
@@ -284,7 +283,7 @@ struct xhci_intr_reg {
struct xhci_run_regs { struct xhci_run_regs {
__le32 microframe_index; __le32 microframe_index;
__le32 rsvd[7]; __le32 rsvd[7];
struct xhci_intr_reg ir_set[128]; struct xhci_intr_reg ir_set[1024];
}; };
/** /**
@@ -800,7 +799,6 @@ struct xhci_device_context_array {
/* private xHCD pointers */ /* private xHCD pointers */
dma_addr_t dma; dma_addr_t dma;
}; };
/* TODO: write function to set the 64-bit device DMA address */
/* /*
* TODO: change this to be dynamically sized at HC mem init time since the HC * TODO: change this to be dynamically sized at HC mem init time since the HC
* might not be able to handle the maximum number of devices possible. * might not be able to handle the maximum number of devices possible.
@@ -1474,7 +1472,7 @@ struct xhci_port_cap {
}; };
struct xhci_port { struct xhci_port {
__le32 __iomem *addr; struct xhci_port_regs __iomem *port_reg;
int hw_portnum; int hw_portnum;
int hcd_portnum; int hcd_portnum;
struct xhci_hub *rhub; struct xhci_hub *rhub;
@@ -1510,7 +1508,6 @@ struct xhci_hcd {
struct xhci_doorbell_array __iomem *dba; struct xhci_doorbell_array __iomem *dba;
/* Cached register copies of read-only HC data */ /* Cached register copies of read-only HC data */
__u32 hcs_params1;
__u32 hcs_params2; __u32 hcs_params2;
__u32 hcs_params3; __u32 hcs_params3;
__u32 hcc_params; __u32 hcc_params;
@@ -1521,6 +1518,8 @@ struct xhci_hcd {
/* packed release number */ /* packed release number */
u16 hci_version; u16 hci_version;
u16 max_interrupters; u16 max_interrupters;
u8 max_slots;
u8 max_ports;
/* imod_interval in ns (I * 250ns) */ /* imod_interval in ns (I * 250ns) */
u32 imod_interval; u32 imod_interval;
u32 page_size; u32 page_size;
@@ -1961,6 +1960,8 @@ void xhci_update_erst_dequeue(struct xhci_hcd *xhci,
void xhci_add_interrupter(struct xhci_hcd *xhci, unsigned int intr_num); void xhci_add_interrupter(struct xhci_hcd *xhci, unsigned int intr_num);
int xhci_usb_endpoint_maxp(struct usb_device *udev, int xhci_usb_endpoint_maxp(struct usb_device *udev,
struct usb_host_endpoint *host_ep); struct usb_host_endpoint *host_ep);
void xhci_portsc_writel(struct xhci_port *port, u32 val);
u32 xhci_portsc_readl(struct xhci_port *port);
/* xHCI roothub code */ /* xHCI roothub code */
void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port, void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port,
@@ -2399,25 +2400,48 @@ static inline const char *xhci_decode_portsc(char *str, u32 portsc)
if (portsc == ~(u32)0) if (portsc == ~(u32)0)
return str; return str;
ret += sprintf(str + ret, "%s %s %s Link:%s PortSpeed:%d ", ret += sprintf(str + ret, "Speed=%d ", DEV_PORT_SPEED(portsc));
portsc & PORT_POWER ? "Powered" : "Powered-off", ret += sprintf(str + ret, "Link=%s ", xhci_portsc_link_state_string(portsc));
portsc & PORT_CONNECT ? "Connected" : "Not-connected",
portsc & PORT_PE ? "Enabled" : "Disabled",
xhci_portsc_link_state_string(portsc),
DEV_PORT_SPEED(portsc));
/* RO/ROS: Read-only */
if (portsc & PORT_CONNECT)
ret += sprintf(str + ret, "CCS ");
if (portsc & PORT_OC) if (portsc & PORT_OC)
ret += sprintf(str + ret, "OverCurrent "); ret += sprintf(str + ret, "OCA "); /* No set for USB2 ports */
if (portsc & PORT_RESET) if (portsc & PORT_CAS)
ret += sprintf(str + ret, "In-Reset "); ret += sprintf(str + ret, "CAS ");
if (portsc & PORT_DEV_REMOVE)
ret += sprintf(str + ret, "DR ");
ret += sprintf(str + ret, "Change: "); /* RWS; writing 1 sets the bit, writing 0 clears the bit. */
if (portsc & PORT_POWER)
ret += sprintf(str + ret, "PP ");
if (portsc & PORT_WKCONN_E)
ret += sprintf(str + ret, "WCE ");
if (portsc & PORT_WKDISC_E)
ret += sprintf(str + ret, "WDE ");
if (portsc & PORT_WKOC_E)
ret += sprintf(str + ret, "WOE ");
/* RW; writing 1 sets the bit, writing 0 clears the bit */
if (portsc & PORT_LINK_STROBE)
ret += sprintf(str + ret, "LWS "); /* LWS 0 write is ignored */
/* RW1S; writing 1 sets the bit, writing 0 has no effect */
if (portsc & PORT_RESET)
ret += sprintf(str + ret, "PR ");
if (portsc & PORT_WR)
ret += sprintf(str + ret, "WPR "); /* RsvdZ for USB2 ports */
/* RW1CS; writing 1 clears the bit, writing 0 has no effect. */
if (portsc & PORT_PE)
ret += sprintf(str + ret, "PED ");
if (portsc & PORT_CSC) if (portsc & PORT_CSC)
ret += sprintf(str + ret, "CSC "); ret += sprintf(str + ret, "CSC ");
if (portsc & PORT_PEC) if (portsc & PORT_PEC)
ret += sprintf(str + ret, "PEC "); ret += sprintf(str + ret, "PEC "); /* No set for USB3 ports */
if (portsc & PORT_WRC) if (portsc & PORT_WRC)
ret += sprintf(str + ret, "WRC "); ret += sprintf(str + ret, "WRC "); /* RsvdZ for USB2 ports */
if (portsc & PORT_OCC) if (portsc & PORT_OCC)
ret += sprintf(str + ret, "OCC "); ret += sprintf(str + ret, "OCC ");
if (portsc & PORT_RC) if (portsc & PORT_RC)
@@ -2425,17 +2449,7 @@ static inline const char *xhci_decode_portsc(char *str, u32 portsc)
if (portsc & PORT_PLC) if (portsc & PORT_PLC)
ret += sprintf(str + ret, "PLC "); ret += sprintf(str + ret, "PLC ");
if (portsc & PORT_CEC) if (portsc & PORT_CEC)
ret += sprintf(str + ret, "CEC "); ret += sprintf(str + ret, "CEC "); /* RsvdZ for USB2 ports */
if (portsc & PORT_CAS)
ret += sprintf(str + ret, "CAS ");
ret += sprintf(str + ret, "Wake: ");
if (portsc & PORT_WKCONN_E)
ret += sprintf(str + ret, "WCE ");
if (portsc & PORT_WKDISC_E)
ret += sprintf(str + ret, "WDE ");
if (portsc & PORT_WKOC_E)
ret += sprintf(str + ret, "WOE ");
return str; return str;
} }

View File

@@ -134,7 +134,6 @@ static int apple_mfi_fc_set_property(struct power_supply *psy,
ret = -EINVAL; ret = -EINVAL;
} }
pm_runtime_mark_last_busy(&mfi->udev->dev);
pm_runtime_put_autosuspend(&mfi->udev->dev); pm_runtime_put_autosuspend(&mfi->udev->dev);
return ret; return ret;

View File

@@ -444,9 +444,19 @@ static ssize_t chaoskey_read(struct file *file,
goto bail; goto bail;
mutex_unlock(&dev->rng_lock); mutex_unlock(&dev->rng_lock);
result = mutex_lock_interruptible(&dev->lock); if (file->f_flags & O_NONBLOCK) {
if (result) result = mutex_trylock(&dev->lock);
goto bail; if (result == 0) {
result = -EAGAIN;
goto bail;
} else {
result = 0;
}
} else {
result = mutex_lock_interruptible(&dev->lock);
if (result)
goto bail;
}
if (dev->valid == dev->used) { if (dev->valid == dev->used) {
result = _chaoskey_fill(dev); result = _chaoskey_fill(dev);
if (result < 0) { if (result < 0) {

View File

@@ -164,28 +164,39 @@ struct ljca_match_ids_walk_data {
struct acpi_device *adev; struct acpi_device *adev;
}; };
/*
* ACPI hardware IDs for LJCA client devices.
*
* [1] Some BIOS implementations use these IDs for denoting LJCA client devices
* even though the IDs have been allocated for USBIO. This isn't a problem
* as the usb-ljca driver is probed based on the USB device's vendor and
* product IDs and its client drivers are probed based on auxiliary device
* names, not these ACPI _HIDs. List of such systems:
*
* Dell Precision 5490
*/
static const struct acpi_device_id ljca_gpio_hids[] = { static const struct acpi_device_id ljca_gpio_hids[] = {
{ "INTC1074" }, { "INTC100B" }, /* RPL LJCA GPIO */
{ "INTC1096" }, { "INTC1074" }, /* CVF LJCA GPIO */
{ "INTC100B" }, { "INTC1096" }, /* ADL LJCA GPIO */
{ "INTC10D1" }, { "INTC10B5" }, /* LNL LJCA GPIO */
{ "INTC10B5" }, { "INTC10D1" }, /* MTL (CVF VSC) USBIO GPIO [1] */
{}, {},
}; };
static const struct acpi_device_id ljca_i2c_hids[] = { static const struct acpi_device_id ljca_i2c_hids[] = {
{ "INTC1075" }, { "INTC100C" }, /* RPL LJCA I2C */
{ "INTC1097" }, { "INTC1075" }, /* CVF LJCA I2C */
{ "INTC100C" }, { "INTC1097" }, /* ADL LJCA I2C */
{ "INTC10D2" }, { "INTC10D2" }, /* MTL (CVF VSC) USBIO I2C [1] */
{}, {},
}; };
static const struct acpi_device_id ljca_spi_hids[] = { static const struct acpi_device_id ljca_spi_hids[] = {
{ "INTC1091" }, { "INTC100D" }, /* RPL LJCA SPI */
{ "INTC1098" }, { "INTC1091" }, /* TGL/ADL LJCA SPI */
{ "INTC100D" }, { "INTC1098" }, /* ADL LJCA SPI */
{ "INTC10D3" }, { "INTC10D3" }, /* MTL (CVF VSC) USBIO SPI [1] */
{}, {},
}; };
@@ -891,7 +902,7 @@ static struct usb_driver ljca_driver = {
}; };
module_usb_driver(ljca_driver); module_usb_driver(ljca_driver);
MODULE_AUTHOR("Wentong Wu <wentong.wu@intel.com>"); MODULE_AUTHOR("Wentong Wu");
MODULE_AUTHOR("Zhifeng Wang <zhifeng.wang@intel.com>"); MODULE_AUTHOR("Zhifeng Wang <zhifeng.wang@intel.com>");
MODULE_AUTHOR("Lixu Zhang <lixu.zhang@intel.com>"); MODULE_AUTHOR("Lixu Zhang <lixu.zhang@intel.com>");
MODULE_DESCRIPTION("Intel La Jolla Cove Adapter USB driver"); MODULE_DESCRIPTION("Intel La Jolla Cove Adapter USB driver");

View File

@@ -65,7 +65,7 @@ struct mtu3_request;
#define MTU3_U3_IP_SLOT_DEFAULT 2 #define MTU3_U3_IP_SLOT_DEFAULT 2
#define MTU3_U2_IP_SLOT_DEFAULT 1 #define MTU3_U2_IP_SLOT_DEFAULT 1
/** /*
* IP TRUNK version * IP TRUNK version
* from 0x1003 version, USB3 Gen2 is supported, two changes affect driver: * from 0x1003 version, USB3 Gen2 is supported, two changes affect driver:
* 1. MAXPKT and MULTI bits layout of TXCSR1 and RXCSR1 are adjusted, * 1. MAXPKT and MULTI bits layout of TXCSR1 and RXCSR1 are adjusted,
@@ -74,9 +74,9 @@ struct mtu3_request;
*/ */
#define MTU3_TRUNK_VERS_1003 0x1003 #define MTU3_TRUNK_VERS_1003 0x1003
/** /*
* Normally the device works on HS or SS, to simplify fifo management, * Normally the device works on HS or SS, to simplify fifo management,
* devide fifo into some 512B parts, use bitmap to manage it; And * divide fifo into some 512B parts, use bitmap to manage it; And
* 128 bits size of bitmap is large enough, that means it can manage * 128 bits size of bitmap is large enough, that means it can manage
* up to 64KB fifo size. * up to 64KB fifo size.
* NOTE: MTU3_EP_FIFO_UNIT should be power of two * NOTE: MTU3_EP_FIFO_UNIT should be power of two
@@ -85,7 +85,7 @@ struct mtu3_request;
#define MTU3_FIFO_BIT_SIZE 128 #define MTU3_FIFO_BIT_SIZE 128
#define MTU3_U2_IP_EP0_FIFO_SIZE 64 #define MTU3_U2_IP_EP0_FIFO_SIZE 64
/** /*
* Maximum size of ep0 response buffer for ch9 requests, * Maximum size of ep0 response buffer for ch9 requests,
* the SET_SEL request uses 6 so far, and GET_STATUS is 2 * the SET_SEL request uses 6 so far, and GET_STATUS is 2
*/ */
@@ -103,6 +103,7 @@ enum mtu3_speed {
}; };
/** /**
* enum mtu3_g_ep0_state - endpoint 0 states
* @MU3D_EP0_STATE_SETUP: waits for SETUP or received a SETUP * @MU3D_EP0_STATE_SETUP: waits for SETUP or received a SETUP
* without data stage. * without data stage.
* @MU3D_EP0_STATE_TX: IN data stage * @MU3D_EP0_STATE_TX: IN data stage
@@ -121,11 +122,12 @@ enum mtu3_g_ep0_state {
}; };
/** /**
* MTU3_DR_FORCE_NONE: automatically switch host and periperal mode * enum mtu3_dr_force_mode - indicates host/OTG operating mode
* @MTU3_DR_FORCE_NONE: automatically switch host and peripheral mode
* by IDPIN signal. * by IDPIN signal.
* MTU3_DR_FORCE_HOST: force to enter host mode and override OTG * @MTU3_DR_FORCE_HOST: force to enter host mode and override OTG
* IDPIN signal. * IDPIN signal.
* MTU3_DR_FORCE_DEVICE: force to enter peripheral mode. * @MTU3_DR_FORCE_DEVICE: force to enter peripheral mode.
*/ */
enum mtu3_dr_force_mode { enum mtu3_dr_force_mode {
MTU3_DR_FORCE_NONE = 0, MTU3_DR_FORCE_NONE = 0,
@@ -134,6 +136,7 @@ enum mtu3_dr_force_mode {
}; };
/** /**
* struct mtu3_fifo_info - HW FIFO description and management data
* @base: the base address of fifo * @base: the base address of fifo
* @limit: the bitmap size in bits * @limit: the bitmap size in bits
* @bitmap: fifo bitmap in unit of @MTU3_EP_FIFO_UNIT * @bitmap: fifo bitmap in unit of @MTU3_EP_FIFO_UNIT
@@ -145,7 +148,7 @@ struct mtu3_fifo_info {
}; };
/** /**
* General Purpose Descriptor (GPD): * struct qmu_gpd - General Purpose Descriptor (GPD):
* The format of TX GPD is a little different from RX one. * The format of TX GPD is a little different from RX one.
* And the size of GPD is 16 bytes. * And the size of GPD is 16 bytes.
* *
@@ -179,11 +182,13 @@ struct qmu_gpd {
} __packed; } __packed;
/** /**
* dma: physical base address of GPD segment * struct mtu3_gpd_ring - GPD ring descriptor
* start: virtual base address of GPD segment * @dma: physical base address of GPD segment
* end: the last GPD element * @start: virtual base address of GPD segment
* enqueue: the first empty GPD to use * @end: the last GPD element
* dequeue: the first completed GPD serviced by ISR * @enqueue: the first empty GPD to use
* @dequeue: the first completed GPD serviced by ISR
*
* NOTE: the size of GPD ring should be >= 2 * NOTE: the size of GPD ring should be >= 2
*/ */
struct mtu3_gpd_ring { struct mtu3_gpd_ring {
@@ -195,6 +200,7 @@ struct mtu3_gpd_ring {
}; };
/** /**
* struct otg_switch_mtk - OTG/dual-role switch management
* @vbus: vbus 5V used by host mode * @vbus: vbus 5V used by host mode
* @edev: external connector used to detect vbus and iddig changes * @edev: external connector used to detect vbus and iddig changes
* @id_nb : notifier for iddig(idpin) detection * @id_nb : notifier for iddig(idpin) detection
@@ -222,6 +228,7 @@ struct otg_switch_mtk {
}; };
/** /**
* struct ssusb_mtk - SuperSpeed USB descriptor (MTK)
* @mac_base: register base address of device MAC, exclude xHCI's * @mac_base: register base address of device MAC, exclude xHCI's
* @ippc_base: register base address of IP Power and Clock interface (IPPC) * @ippc_base: register base address of IP Power and Clock interface (IPPC)
* @vusb33: usb3.3V shared by device/host IP * @vusb33: usb3.3V shared by device/host IP
@@ -268,6 +275,7 @@ struct ssusb_mtk {
}; };
/** /**
* struct mtu3_ep - common mtu3 endpoint description
* @fifo_size: it is (@slot + 1) * @fifo_seg_size * @fifo_size: it is (@slot + 1) * @fifo_seg_size
* @fifo_seg_size: it is roundup_pow_of_two(@maxp) * @fifo_seg_size: it is roundup_pow_of_two(@maxp)
*/ */

View File

@@ -290,7 +290,7 @@ static void mtu3_csr_init(struct mtu3 *mtu)
/* delay about 0.1us from detecting reset to send chirp-K */ /* delay about 0.1us from detecting reset to send chirp-K */
mtu3_clrbits(mbase, U3D_LINK_RESET_INFO, WTCHRP_MSK); mtu3_clrbits(mbase, U3D_LINK_RESET_INFO, WTCHRP_MSK);
/* enable automatical HWRW from L1 */ /* enable automatic HWRW from L1 */
mtu3_setbits(mbase, U3D_POWER_MANAGEMENT, LPM_HRWE); mtu3_setbits(mbase, U3D_POWER_MANAGEMENT, LPM_HRWE);
} }

View File

@@ -431,7 +431,6 @@ static int mtu3_probe(struct platform_device *pdev)
} }
device_enable_async_suspend(dev); device_enable_async_suspend(dev);
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev); pm_runtime_put_autosuspend(dev);
pm_runtime_forbid(dev); pm_runtime_forbid(dev);

View File

@@ -221,7 +221,7 @@ static struct qmu_gpd *advance_deq_gpd(struct mtu3_gpd_ring *ring)
return ring->dequeue; return ring->dequeue;
} }
/* check if a ring is emtpy */ /* check if a ring is empty */
static bool gpd_ring_empty(struct mtu3_gpd_ring *ring) static bool gpd_ring_empty(struct mtu3_gpd_ring *ring)
{ {
struct qmu_gpd *enq = ring->enqueue; struct qmu_gpd *enq = ring->enqueue;

View File

@@ -2031,7 +2031,6 @@ static void musb_pm_runtime_check_session(struct musb *musb)
if (!musb->session) if (!musb->session)
break; break;
trace_musb_state(musb, devctl, "Allow PM on possible host mode disconnect"); trace_musb_state(musb, devctl, "Allow PM on possible host mode disconnect");
pm_runtime_mark_last_busy(musb->controller);
pm_runtime_put_autosuspend(musb->controller); pm_runtime_put_autosuspend(musb->controller);
musb->session = false; musb->session = false;
return; return;
@@ -2063,7 +2062,6 @@ static void musb_pm_runtime_check_session(struct musb *musb)
msecs_to_jiffies(3000)); msecs_to_jiffies(3000));
} else { } else {
trace_musb_state(musb, devctl, "Allow PM with no session"); trace_musb_state(musb, devctl, "Allow PM with no session");
pm_runtime_mark_last_busy(musb->controller);
pm_runtime_put_autosuspend(musb->controller); pm_runtime_put_autosuspend(musb->controller);
} }
@@ -2090,7 +2088,6 @@ static void musb_irq_work(struct work_struct *data)
sysfs_notify(&musb->controller->kobj, NULL, "mode"); sysfs_notify(&musb->controller->kobj, NULL, "mode");
} }
pm_runtime_mark_last_busy(musb->controller);
pm_runtime_put_autosuspend(musb->controller); pm_runtime_put_autosuspend(musb->controller);
} }
@@ -2564,7 +2561,6 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
musb_init_debugfs(musb); musb_init_debugfs(musb);
musb->is_initialized = 1; musb->is_initialized = 1;
pm_runtime_mark_last_busy(musb->controller);
pm_runtime_put_autosuspend(musb->controller); pm_runtime_put_autosuspend(musb->controller);
return 0; return 0;
@@ -2887,7 +2883,6 @@ static int musb_resume(struct device *dev)
error); error);
spin_unlock_irqrestore(&musb->lock, flags); spin_unlock_irqrestore(&musb->lock, flags);
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev); pm_runtime_put_autosuspend(dev);
return 0; return 0;

View File

@@ -106,7 +106,6 @@ static int musb_regdump_show(struct seq_file *s, void *unused)
} }
} }
pm_runtime_mark_last_busy(musb->controller);
pm_runtime_put_autosuspend(musb->controller); pm_runtime_put_autosuspend(musb->controller);
return 0; return 0;
} }
@@ -119,7 +118,6 @@ static int musb_test_mode_show(struct seq_file *s, void *unused)
pm_runtime_get_sync(musb->controller); pm_runtime_get_sync(musb->controller);
test = musb_readb(musb->mregs, MUSB_TESTMODE); test = musb_readb(musb->mregs, MUSB_TESTMODE);
pm_runtime_mark_last_busy(musb->controller);
pm_runtime_put_autosuspend(musb->controller); pm_runtime_put_autosuspend(musb->controller);
if (test == (MUSB_TEST_FORCE_HOST | MUSB_TEST_FORCE_FS)) if (test == (MUSB_TEST_FORCE_HOST | MUSB_TEST_FORCE_FS))
@@ -216,7 +214,6 @@ static ssize_t musb_test_mode_write(struct file *file,
musb_writeb(musb->mregs, MUSB_TESTMODE, test); musb_writeb(musb->mregs, MUSB_TESTMODE, test);
ret: ret:
pm_runtime_mark_last_busy(musb->controller);
pm_runtime_put_autosuspend(musb->controller); pm_runtime_put_autosuspend(musb->controller);
return count; return count;
} }
@@ -243,7 +240,6 @@ static int musb_softconnect_show(struct seq_file *s, void *unused)
reg = musb_readb(musb->mregs, MUSB_DEVCTL); reg = musb_readb(musb->mregs, MUSB_DEVCTL);
connect = reg & MUSB_DEVCTL_SESSION ? 1 : 0; connect = reg & MUSB_DEVCTL_SESSION ? 1 : 0;
pm_runtime_mark_last_busy(musb->controller);
pm_runtime_put_autosuspend(musb->controller); pm_runtime_put_autosuspend(musb->controller);
break; break;
default: default:
@@ -304,7 +300,6 @@ static ssize_t musb_softconnect_write(struct file *file,
} }
} }
pm_runtime_mark_last_busy(musb->controller);
pm_runtime_put_autosuspend(musb->controller); pm_runtime_put_autosuspend(musb->controller);
return count; return count;
} }

View File

@@ -296,7 +296,6 @@ static void otg_timer(struct timer_list *t)
if (err < 0) if (err < 0)
dev_err(dev, "%s resume work: %i\n", __func__, err); dev_err(dev, "%s resume work: %i\n", __func__, err);
spin_unlock_irqrestore(&musb->lock, flags); spin_unlock_irqrestore(&musb->lock, flags);
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev); pm_runtime_put_autosuspend(dev);
} }

View File

@@ -1258,7 +1258,6 @@ static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req,
unlock: unlock:
spin_unlock_irqrestore(&musb->lock, lockflags); spin_unlock_irqrestore(&musb->lock, lockflags);
pm_runtime_mark_last_busy(musb->controller);
pm_runtime_put_autosuspend(musb->controller); pm_runtime_put_autosuspend(musb->controller);
return status; return status;
@@ -1642,7 +1641,6 @@ static void musb_gadget_work(struct work_struct *work)
spin_lock_irqsave(&musb->lock, flags); spin_lock_irqsave(&musb->lock, flags);
musb_pullup(musb, musb->softconnect); musb_pullup(musb, musb->softconnect);
spin_unlock_irqrestore(&musb->lock, flags); spin_unlock_irqrestore(&musb->lock, flags);
pm_runtime_mark_last_busy(musb->controller);
pm_runtime_put_autosuspend(musb->controller); pm_runtime_put_autosuspend(musb->controller);
} }
@@ -1862,7 +1860,6 @@ static int musb_gadget_start(struct usb_gadget *g,
if (musb->xceiv && musb->xceiv->last_event == USB_EVENT_ID) if (musb->xceiv && musb->xceiv->last_event == USB_EVENT_ID)
musb_platform_set_vbus(musb, 1); musb_platform_set_vbus(musb, 1);
pm_runtime_mark_last_busy(musb->controller);
pm_runtime_put_autosuspend(musb->controller); pm_runtime_put_autosuspend(musb->controller);
return 0; return 0;
@@ -1916,7 +1913,6 @@ static int musb_gadget_stop(struct usb_gadget *g)
usb_gadget_set_state(g, USB_STATE_NOTATTACHED); usb_gadget_set_state(g, USB_STATE_NOTATTACHED);
/* Force check of devctl register for PM runtime */ /* Force check of devctl register for PM runtime */
pm_runtime_mark_last_busy(musb->controller);
pm_runtime_put_autosuspend(musb->controller); pm_runtime_put_autosuspend(musb->controller);
return 0; return 0;

View File

@@ -151,7 +151,6 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue)
default: default:
dev_dbg(musb->controller, "ID float\n"); dev_dbg(musb->controller, "ID float\n");
} }
pm_runtime_mark_last_busy(musb->controller);
pm_runtime_put_autosuspend(musb->controller); pm_runtime_put_autosuspend(musb->controller);
atomic_notifier_call_chain(&musb->xceiv->notifier, atomic_notifier_call_chain(&musb->xceiv->notifier,
musb->xceiv->last_event, NULL); musb->xceiv->last_event, NULL);

View File

@@ -646,6 +646,8 @@ int usb_add_phy(struct usb_phy *x, enum usb_phy_type type)
return -EINVAL; return -EINVAL;
} }
INIT_LIST_HEAD(&x->head);
usb_charger_init(x); usb_charger_init(x);
ret = usb_add_extcon(x); ret = usb_add_extcon(x);
if (ret) if (ret)
@@ -696,6 +698,8 @@ int usb_add_phy_dev(struct usb_phy *x)
return -EINVAL; return -EINVAL;
} }
INIT_LIST_HEAD(&x->head);
usb_charger_init(x); usb_charger_init(x);
ret = usb_add_extcon(x); ret = usb_add_extcon(x);
if (ret) if (ret)

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