mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 20:06:24 +00:00
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:
@@ -254,3 +254,31 @@ Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
|
||||
Description:
|
||||
The PPS Power Limited bit indicates whether or not the source
|
||||
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.
|
||||
|
||||
@@ -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.
|
||||
Typically OEMs provide this firmware from their support site.
|
||||
|
||||
There is also a central site which has links where to download firmware
|
||||
for some machines:
|
||||
|
||||
`Thunderbolt Updates <https://thunderbolttechnology.net/updates>`_
|
||||
Currently, recommended method of updating firmware is through "fwupd" tool.
|
||||
It uses LVFS (Linux Vendor Firmware Service) portal by default to get the
|
||||
latest firmware from hardware vendors and updates connected devices if found
|
||||
compatible. For details refer to: https://github.com/fwupd/fwupd.
|
||||
|
||||
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
|
||||
@@ -215,18 +215,40 @@ tools!
|
||||
|
||||
Host NVM upgrade on Apple Macs is not supported.
|
||||
|
||||
Once the NVM image has been downloaded, 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).
|
||||
Fwupd is installed by default. If you don't have it on your system, simply
|
||||
use your distro package manager to get it.
|
||||
|
||||
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
|
||||
be available for your system in which case there is no need to plug in a
|
||||
Thunderbolt device.
|
||||
|
||||
After that we can write the firmware to the non-active parts of the NVM
|
||||
of the host or device. As an example here is how Intel NUC6i7KYK (Skull
|
||||
Canyon) Thunderbolt controller NVM is upgraded::
|
||||
Updating firmware using fwupd is straightforward - refer to official
|
||||
readme on fwupd github.
|
||||
|
||||
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
|
||||
|
||||
@@ -235,10 +257,8 @@ upgrade process as follows::
|
||||
|
||||
# echo 1 > /sys/bus/thunderbolt/devices/0-0/nvm_authenticate
|
||||
|
||||
If no errors are returned, the host controller shortly disappears. Once
|
||||
it comes back the driver notices it and initiates a full power cycle.
|
||||
After a while the host controller appears again and this time it should
|
||||
be fully functional.
|
||||
If no errors are returned, device should behave as described in previous
|
||||
section.
|
||||
|
||||
We can verify that the new NVM firmware is active by running the following
|
||||
commands::
|
||||
|
||||
80
Documentation/devicetree/bindings/usb/apple,dwc3.yaml
Normal file
80
Documentation/devicetree/bindings/usb/apple,dwc3.yaml
Normal 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;
|
||||
};
|
||||
@@ -47,6 +47,7 @@ properties:
|
||||
- const: ref_clk
|
||||
|
||||
resets:
|
||||
minItems: 1
|
||||
description:
|
||||
A list of phandles for resets listed in reset-names.
|
||||
|
||||
@@ -56,6 +57,7 @@ properties:
|
||||
- description: USB APB reset
|
||||
|
||||
reset-names:
|
||||
minItems: 1
|
||||
items:
|
||||
- const: usb_crst
|
||||
- const: usb_hibrst
|
||||
@@ -95,6 +97,26 @@ required:
|
||||
- resets
|
||||
- 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
|
||||
|
||||
examples:
|
||||
|
||||
94
Documentation/devicetree/bindings/usb/eswin,eic7700-usb.yaml
Normal file
94
Documentation/devicetree/bindings/usb/eswin,eic7700-usb.yaml
Normal 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>;
|
||||
};
|
||||
@@ -9,21 +9,19 @@ title: Freescale layerscape SuperSpeed DWC3 USB SoC controller
|
||||
maintainers:
|
||||
- Frank Li <Frank.Li@nxp.com>
|
||||
|
||||
select:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- fsl,ls1028a-dwc3
|
||||
required:
|
||||
- compatible
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- fsl,ls1028a-dwc3
|
||||
- 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:
|
||||
maxItems: 1
|
||||
@@ -31,6 +29,11 @@ properties:
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
iommus:
|
||||
maxItems: 1
|
||||
|
||||
dma-coherent: true
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
required:
|
||||
@@ -39,14 +42,14 @@ required:
|
||||
- interrupts
|
||||
|
||||
allOf:
|
||||
- $ref: snps,dwc3.yaml#
|
||||
- $ref: snps,dwc3-common.yaml#
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
usb@fe800000 {
|
||||
compatible = "fsl,ls1028a-dwc3", "snps,dwc3";
|
||||
compatible = "fsl,ls1028a-dwc3";
|
||||
reg = <0xfe800000 0x100000>;
|
||||
interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
||||
|
||||
@@ -36,6 +36,7 @@ properties:
|
||||
- fsl,imx8mm-usbmisc
|
||||
- fsl,imx8mn-usbmisc
|
||||
- fsl,imx8ulp-usbmisc
|
||||
- fsl,imx94-usbmisc
|
||||
- fsl,imx95-usbmisc
|
||||
- const: fsl,imx7d-usbmisc
|
||||
- const: fsl,imx6q-usbmisc
|
||||
|
||||
@@ -46,6 +46,7 @@ properties:
|
||||
- aspeed,ast2400-ehci
|
||||
- aspeed,ast2500-ehci
|
||||
- aspeed,ast2600-ehci
|
||||
- aspeed,ast2700-ehci
|
||||
- brcm,bcm3384-ehci
|
||||
- brcm,bcm63268-ehci
|
||||
- brcm,bcm6328-ehci
|
||||
|
||||
@@ -14,12 +14,15 @@ properties:
|
||||
oneOf:
|
||||
- description: Generic xHCI device
|
||||
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:
|
||||
- enum:
|
||||
- marvell,armada3700-xhci
|
||||
- marvell,armada-375-xhci
|
||||
- marvell,armada-380-xhci
|
||||
- marvell,armada-8k-xhci
|
||||
- const: generic-xhci
|
||||
- description: Broadcom SoCs with power domains
|
||||
@@ -53,6 +56,14 @@ properties:
|
||||
|
||||
dma-coherent: true
|
||||
|
||||
dr_mode:
|
||||
enum:
|
||||
- host
|
||||
- otg
|
||||
|
||||
iommus:
|
||||
maxItems: 1
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ properties:
|
||||
- mediatek,mt8183-xhci
|
||||
- mediatek,mt8186-xhci
|
||||
- mediatek,mt8188-xhci
|
||||
- mediatek,mt8189-xhci
|
||||
- mediatek,mt8192-xhci
|
||||
- mediatek,mt8195-xhci
|
||||
- mediatek,mt8365-xhci
|
||||
@@ -168,7 +169,8 @@ properties:
|
||||
104 - used by mt8195, IP1, specific 1.04;
|
||||
105 - used by mt8195, IP2, specific 1.05;
|
||||
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:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
@@ -24,6 +24,8 @@ properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- qcom,glymur-dwc3
|
||||
- qcom,glymur-dwc3-mp
|
||||
- qcom,ipq4019-dwc3
|
||||
- qcom,ipq5018-dwc3
|
||||
- qcom,ipq5332-dwc3
|
||||
@@ -32,6 +34,7 @@ properties:
|
||||
- qcom,ipq8064-dwc3
|
||||
- qcom,ipq8074-dwc3
|
||||
- qcom,ipq9574-dwc3
|
||||
- qcom,kaanapali-dwc3
|
||||
- qcom,milos-dwc3
|
||||
- qcom,msm8953-dwc3
|
||||
- qcom,msm8994-dwc3
|
||||
@@ -67,6 +70,7 @@ properties:
|
||||
- qcom,sm8450-dwc3
|
||||
- qcom,sm8550-dwc3
|
||||
- qcom,sm8650-dwc3
|
||||
- qcom,sm8750-dwc3
|
||||
- qcom,x1e80100-dwc3
|
||||
- qcom,x1e80100-dwc3-mp
|
||||
- const: qcom,snps-dwc3
|
||||
@@ -200,6 +204,7 @@ allOf:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,ipq9574-dwc3
|
||||
- qcom,kaanapali-dwc3
|
||||
- qcom,msm8953-dwc3
|
||||
- qcom,msm8996-dwc3
|
||||
- qcom,msm8998-dwc3
|
||||
@@ -213,6 +218,7 @@ allOf:
|
||||
- qcom,sdx65-dwc3
|
||||
- qcom,sdx75-dwc3
|
||||
- qcom,sm6350-dwc3
|
||||
- qcom,sm8750-dwc3
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
@@ -387,6 +393,28 @@ allOf:
|
||||
- const: mock_utmi
|
||||
- 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:
|
||||
properties:
|
||||
compatible:
|
||||
@@ -456,6 +484,7 @@ allOf:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,glymur-dwc3
|
||||
- qcom,milos-dwc3
|
||||
- qcom,x1e80100-dwc3
|
||||
then:
|
||||
@@ -479,6 +508,7 @@ allOf:
|
||||
enum:
|
||||
- qcom,ipq4019-dwc3
|
||||
- qcom,ipq8064-dwc3
|
||||
- qcom,kaanapali-dwc3
|
||||
- qcom,msm8994-dwc3
|
||||
- qcom,qcs615-dwc3
|
||||
- qcom,qcs8300-dwc3
|
||||
@@ -501,6 +531,7 @@ allOf:
|
||||
- qcom,sm8450-dwc3
|
||||
- qcom,sm8550-dwc3
|
||||
- qcom,sm8650-dwc3
|
||||
- qcom,sm8750-dwc3
|
||||
then:
|
||||
properties:
|
||||
interrupts:
|
||||
@@ -521,6 +552,7 @@ allOf:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,glymur-dwc3-mp
|
||||
- qcom,sc8180x-dwc3-mp
|
||||
- qcom,x1e80100-dwc3-mp
|
||||
then:
|
||||
|
||||
@@ -4,14 +4,22 @@
|
||||
$id: http://devicetree.org/schemas/usb/renesas,rzg3e-xhci.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:
|
||||
- Biju Das <biju.das.jz@bp.renesas.com>
|
||||
|
||||
properties:
|
||||
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:
|
||||
maxItems: 1
|
||||
|
||||
@@ -21,6 +21,9 @@ properties:
|
||||
- samsung,exynos7870-dwusb3
|
||||
- samsung,exynos850-dwusb3
|
||||
- samsung,exynosautov920-dwusb3
|
||||
- items:
|
||||
- const: samsung,exynos8890-dwusb3
|
||||
- const: samsung,exynos7-dwusb3
|
||||
- items:
|
||||
- const: samsung,exynos990-dwusb3
|
||||
- const: samsung,exynos850-dwusb3
|
||||
@@ -36,6 +39,9 @@ properties:
|
||||
minItems: 1
|
||||
maxItems: 4
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
ranges: true
|
||||
|
||||
'#size-cells':
|
||||
|
||||
@@ -25,6 +25,14 @@ properties:
|
||||
interrupts:
|
||||
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:
|
||||
$ref: /schemas/graph.yaml#/properties/ports
|
||||
description: OF graph bindings (specified in bindings/graph.txt) that model
|
||||
|
||||
@@ -20,6 +20,7 @@ properties:
|
||||
- aspeed,ast2400-uhci
|
||||
- aspeed,ast2500-uhci
|
||||
- aspeed,ast2600-uhci
|
||||
- aspeed,ast2700-uhci
|
||||
- const: generic-uhci
|
||||
|
||||
reg:
|
||||
@@ -28,6 +29,9 @@ properties:
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
'#ports':
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
@@ -50,6 +54,15 @@ allOf:
|
||||
required:
|
||||
- clocks
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: aspeed,ast2700-uhci
|
||||
then:
|
||||
required:
|
||||
- resets
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
|
||||
@@ -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/spi/apple,spi.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/hwmon/macsmc-hwmon.rst
|
||||
F: arch/arm64/boot/dts/apple/
|
||||
@@ -2503,6 +2504,7 @@ F: drivers/pwm/pwm-apple.c
|
||||
F: drivers/soc/apple/*
|
||||
F: drivers/spi/spi-apple.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/watchdog/apple_wdt.c
|
||||
F: include/dt-bindings/interrupt-controller/apple-aic.h
|
||||
|
||||
@@ -412,7 +412,7 @@ static void tb_ctl_rx_submit(struct ctl_pkg *pkg)
|
||||
* We ignore failures during stop.
|
||||
* All rx packets are referenced
|
||||
* from ctl->rx_packets, so we do
|
||||
* not loose them.
|
||||
* not lose them.
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
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
|
||||
* 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
|
||||
* expectations are not met.
|
||||
*/
|
||||
|
||||
@@ -376,7 +376,7 @@ struct tb *tb_domain_alloc(struct tb_nhi *nhi, int timeout_msec, size_t privsize
|
||||
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.
|
||||
*/
|
||||
BUILD_BUG_ON(sizeof(struct tb_regs_switch_header) != 5 * 4);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
* fact that a switch on a dual-link connection might
|
||||
* 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 &&
|
||||
!!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
|
||||
* 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.
|
||||
*/
|
||||
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) {
|
||||
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, "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;
|
||||
}
|
||||
|
||||
@@ -2171,7 +2171,7 @@ static int icm_runtime_resume_switch(struct tb_switch *sw)
|
||||
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.
|
||||
*/
|
||||
icm_complete(tb);
|
||||
|
||||
@@ -558,7 +558,7 @@ static int tb_lc_dp_sink_available(struct tb_switch *sw, int sink)
|
||||
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.
|
||||
*/
|
||||
if (!sink) {
|
||||
|
||||
@@ -712,7 +712,7 @@ void tb_ring_start(struct tb_ring *ring)
|
||||
ring_iowrite64desc(ring, ring->descriptors_dma, 0);
|
||||
if (ring->is_tx) {
|
||||
ring_iowrite32desc(ring, ring->size, 12);
|
||||
ring_iowrite32options(ring, 0, 4); /* time releated ? */
|
||||
ring_iowrite32options(ring, 0, 4);
|
||||
ring_iowrite32options(ring, flags, 0);
|
||||
} else {
|
||||
u32 sof_eof_mask = ring->sof_mask << 16 | ring->eof_mask;
|
||||
|
||||
@@ -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
|
||||
*
|
||||
* 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
|
||||
* not scan for cable retimers for now.
|
||||
*
|
||||
|
||||
@@ -736,9 +736,9 @@ static int tb_init_port(struct tb_port *port)
|
||||
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
|
||||
* devices we use hard-coded value.
|
||||
* devices use hard-coded value.
|
||||
*/
|
||||
if (port->cap_usb4) {
|
||||
struct tb_regs_hop hop;
|
||||
@@ -3221,7 +3221,7 @@ int tb_switch_configure_link(struct tb_switch *sw)
|
||||
* @sw: Switch whose link is unconfigured
|
||||
*
|
||||
* 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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
* 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
|
||||
* rate to allow CL1 (when supported). Otherwise we keep the TMU
|
||||
* 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
|
||||
* @dst_port: Destination protocol adapter
|
||||
* @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)
|
||||
*
|
||||
* 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
|
||||
* @dst_port: Destination protocol adapter
|
||||
* @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)
|
||||
*
|
||||
* 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
|
||||
* transtion the link into asymmetric now.
|
||||
* transition the link into asymmetric now.
|
||||
*/
|
||||
ret = tb_switch_set_link_width(up->sw, width_up);
|
||||
if (ret) {
|
||||
@@ -1936,7 +1936,7 @@ static void tb_dp_tunnel_active(struct tb_tunnel *tunnel, void *data)
|
||||
*/
|
||||
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
|
||||
* 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
|
||||
* 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));
|
||||
}
|
||||
}
|
||||
@@ -2786,8 +2786,8 @@ static void tb_handle_dp_bandwidth_request(struct work_struct *work)
|
||||
* There is no request active so this means the
|
||||
* BW allocation mode was enabled from graphics
|
||||
* side. At this point we know that the graphics
|
||||
* driver has read the DRPX capabilities so we
|
||||
* can offer an better bandwidth estimatation.
|
||||
* driver has read the DPRX capabilities so we
|
||||
* can offer better bandwidth estimation.
|
||||
*/
|
||||
tb_port_dbg(in, "DPTX enabled bandwidth allocation mode, updating estimated bandwidth\n");
|
||||
tb_recalc_estimated_bandwidth(tb);
|
||||
|
||||
@@ -308,7 +308,7 @@ struct tb_port {
|
||||
* struct usb4_port - USB4 port device
|
||||
* @dev: Device for the port
|
||||
* @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
|
||||
* @offline: The port is currently in offline mode
|
||||
* @margining: Pointer to margining structure if enabled
|
||||
@@ -355,7 +355,7 @@ struct tb_retimer {
|
||||
* struct tb_path_hop - routing information for a tb_path
|
||||
* @in_port: Ingress port of a switch
|
||||
* @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
|
||||
* the path config space of @in_port.
|
||||
* @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
|
||||
* native USB4 router operation is called.
|
||||
* @usb4_switch_nvm_authenticate_status: Optional callback that the CM
|
||||
* implementation can be used to
|
||||
* return status of USB4 NVM_AUTH
|
||||
* router operation.
|
||||
* implementation can use to return
|
||||
* status of USB4 NVM_AUTH router
|
||||
* operation.
|
||||
*/
|
||||
struct tb_cm_ops {
|
||||
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);
|
||||
|
||||
/**
|
||||
* tb_port_path_direction_downstream() - Checks if path directed downstream
|
||||
* tb_port_path_direction_downstream() - Checks if path is directed downstream
|
||||
* @src: Source 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)))
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @dst: Destination port
|
||||
* @p: Port used as iterator
|
||||
|
||||
@@ -99,7 +99,7 @@ struct tb_cap_extended_long {
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct tb_cap_any - Structure capable of hold every capability
|
||||
* struct tb_cap_any - Structure capable of holding every capability
|
||||
* @basic: Basic capability
|
||||
* @extended_short: Vendor specific 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
|
||||
* (see above) as in USB4 spec, but these specific bits used for Titan Ridge
|
||||
* only and reserved in USB4 spec.
|
||||
* (see above) as in USB4 spec, but these specific bits are used for Titan Ridge
|
||||
* 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_CL1 BIT(2)
|
||||
|
||||
@@ -400,10 +400,10 @@ static int tmu_mode_init(struct tb_switch *sw)
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
* Return: %0 on success, negative errno otherwise.
|
||||
|
||||
@@ -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);
|
||||
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) ||
|
||||
(usb4_switch_version(tunnel->dst_port->sw) < 2))
|
||||
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
|
||||
* read parameter values so this so we can use this to determine
|
||||
* the maximum possible bandwidth over this link.
|
||||
* read parameter values so we can use this to determine the
|
||||
* maximum possible bandwidth over this link.
|
||||
*
|
||||
* 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.
|
||||
* We don't tunnel other traffic over this link so can use all
|
||||
* the credits (except the ones reserved for control traffic).
|
||||
* We don't tunnel other traffic over this link so we can use
|
||||
* all the credits (except the ones reserved for control traffic).
|
||||
*/
|
||||
hop = &path->hops[0];
|
||||
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
|
||||
* take that it into account here.
|
||||
* take that into account here.
|
||||
*/
|
||||
*consumed_up = tunnel->allocated_up *
|
||||
(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
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@@ -284,7 +284,7 @@ int usb4_switch_setup(struct tb_switch *sw)
|
||||
val |= ROUTER_CS_5_PTO;
|
||||
/*
|
||||
* 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).
|
||||
*/
|
||||
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
|
||||
* @port: USB4 port to read
|
||||
* @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
|
||||
* @buf: Buffer where the sideband data is copied
|
||||
* @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
|
||||
* @port: USB4 port to write
|
||||
* @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
|
||||
* @buf: Data to write
|
||||
* @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
|
||||
*
|
||||
* 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
|
||||
* @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
|
||||
* @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
|
||||
* @port: USB4 port
|
||||
* @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
|
||||
* @results: Array to hold the results
|
||||
* @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
|
||||
* @port: USB4 port
|
||||
* @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
|
||||
* @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
|
||||
* @port: USB4 port
|
||||
* @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.
|
||||
*
|
||||
* 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
|
||||
* @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.
|
||||
*
|
||||
* 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
|
||||
* @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().
|
||||
*
|
||||
* 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
|
||||
* @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().
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* Return: Maximum supported link rate of a USB3 adapter in Mb/s.
|
||||
|
||||
@@ -1951,8 +1951,8 @@ static void tb_xdomain_link_exit(struct tb_xdomain *xd)
|
||||
/**
|
||||
* tb_xdomain_alloc() - Allocate new XDomain object
|
||||
* @tb: Domain where the XDomain belongs
|
||||
* @parent: Parent device (the switch through the connection to the
|
||||
* other domain is reached).
|
||||
* @parent: Parent device (the switch through which the other domain
|
||||
* is reached).
|
||||
* @route: Route string used to reach the other domain
|
||||
* @local_uuid: Our local domain UUID
|
||||
* @remote_uuid: UUID of the other domain (optional)
|
||||
|
||||
@@ -3251,7 +3251,6 @@ static void cdns3_gadget_exit(struct cdns *cdns)
|
||||
priv_dev = cdns->gadget_dev;
|
||||
|
||||
|
||||
pm_runtime_mark_last_busy(cdns->dev);
|
||||
pm_runtime_put_autosuspend(cdns->dev);
|
||||
|
||||
usb_del_gadget(&priv_dev->gadget);
|
||||
|
||||
@@ -1999,7 +1999,6 @@ static void cdnsp_gadget_exit(struct cdns *cdns)
|
||||
struct cdnsp_device *pdev = cdns->gadget_dev;
|
||||
|
||||
devm_free_irq(pdev->dev, cdns->dev_irq, pdev);
|
||||
pm_runtime_mark_last_busy(cdns->dev);
|
||||
pm_runtime_put_autosuspend(cdns->dev);
|
||||
usb_del_gadget(&pdev->gadget);
|
||||
cdnsp_gadget_free_endpoints(pdev);
|
||||
|
||||
@@ -1375,7 +1375,6 @@ static int ci_controller_resume(struct device *dev)
|
||||
ci->in_lpm = false;
|
||||
if (ci->wakeup_int) {
|
||||
ci->wakeup_int = false;
|
||||
pm_runtime_mark_last_busy(ci->dev);
|
||||
pm_runtime_put_autosuspend(ci->dev);
|
||||
enable_irq(ci->irq);
|
||||
if (ci_otg_is_fsm_mode(ci))
|
||||
|
||||
@@ -629,7 +629,6 @@ int ci_otg_fsm_work(struct ci_hdrc *ci)
|
||||
ci_otg_queue_work(ci);
|
||||
}
|
||||
} else if (ci->fsm.otg->state == OTG_STATE_A_HOST) {
|
||||
pm_runtime_mark_last_busy(ci->dev);
|
||||
pm_runtime_put_autosuspend(ci->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1224,6 +1224,14 @@ static const struct usbmisc_ops imx7ulp_usbmisc_ops = {
|
||||
.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 = {
|
||||
.init = usbmisc_imx7d_init,
|
||||
.set_wakeup = usbmisc_imx95_set_wakeup,
|
||||
@@ -1481,6 +1489,10 @@ static const struct of_device_id usbmisc_imx_dt_ids[] = {
|
||||
.compatible = "fsl,imx8ulp-usbmisc",
|
||||
.data = &imx7ulp_usbmisc_ops,
|
||||
},
|
||||
{
|
||||
.compatible = "fsl,imx94-usbmisc",
|
||||
.data = &imx94_usbmisc_ops,
|
||||
},
|
||||
{
|
||||
.compatible = "fsl,imx95-usbmisc",
|
||||
.data = &imx95_usbmisc_ops,
|
||||
|
||||
@@ -1475,7 +1475,7 @@ made_compressed_probe:
|
||||
if (!acm->country_codes)
|
||||
goto skip_countries;
|
||||
acm->country_code_size = cfd->bLength - 4;
|
||||
memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0,
|
||||
memcpy(acm->country_codes, cfd->wCountryCodes,
|
||||
cfd->bLength - 4);
|
||||
acm->country_rel_date = cfd->iCountryCodeRelDate;
|
||||
|
||||
|
||||
@@ -1936,10 +1936,8 @@ static int usbtmc_ioctl_request(struct usbtmc_device_data *data,
|
||||
u8 *buffer = NULL;
|
||||
int rv;
|
||||
unsigned int is_in, pipe;
|
||||
unsigned long res;
|
||||
|
||||
res = copy_from_user(&request, arg, sizeof(struct usbtmc_ctrlrequest));
|
||||
if (res)
|
||||
if (copy_from_user(&request, arg, sizeof(struct usbtmc_ctrlrequest)))
|
||||
return -EFAULT;
|
||||
|
||||
if (request.req.wLength > USBTMC_BUFSIZE)
|
||||
@@ -1956,9 +1954,8 @@ static int usbtmc_ioctl_request(struct usbtmc_device_data *data,
|
||||
|
||||
if (!is_in) {
|
||||
/* Send control data to device */
|
||||
res = copy_from_user(buffer, request.data,
|
||||
request.req.wLength);
|
||||
if (res) {
|
||||
if (copy_from_user(buffer, request.data,
|
||||
request.req.wLength)) {
|
||||
rv = -EFAULT;
|
||||
goto exit;
|
||||
}
|
||||
@@ -1984,8 +1981,7 @@ static int usbtmc_ioctl_request(struct usbtmc_device_data *data,
|
||||
|
||||
if (rv && is_in) {
|
||||
/* Read control data from device */
|
||||
res = copy_to_user(request.data, buffer, rv);
|
||||
if (res)
|
||||
if (copy_to_user(request.data, buffer, rv))
|
||||
rv = -EFAULT;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,10 +3,13 @@
|
||||
# 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 += config.o file.o buffer.o sysfs.o endpoint.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_USB_XHCI_SIDEBAND) += offload.o
|
||||
|
||||
@@ -2696,18 +2696,18 @@ static void hcd_release(struct kref *kref)
|
||||
kfree(hcd);
|
||||
}
|
||||
|
||||
struct usb_hcd *usb_get_hcd (struct usb_hcd *hcd)
|
||||
struct usb_hcd *usb_get_hcd(struct usb_hcd *hcd)
|
||||
{
|
||||
if (hcd)
|
||||
kref_get (&hcd->kref);
|
||||
kref_get(&hcd->kref);
|
||||
return 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)
|
||||
kref_put (&hcd->kref, hcd_release);
|
||||
kref_put(&hcd->kref, hcd_release);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_put_hcd);
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/usb/quirks.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/minmax.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/pm_qos.h>
|
||||
@@ -40,6 +41,7 @@
|
||||
#include "hub.h"
|
||||
#include "phy.h"
|
||||
#include "otg_productlist.h"
|
||||
#include "trace.h"
|
||||
|
||||
#define USB_VENDOR_GENESYS_LOGIC 0x05e3
|
||||
#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
|
||||
* the device exit latency or the hub exit latency.
|
||||
*/
|
||||
if (udev_exit_latency > hub_exit_latency)
|
||||
first_link_pel = udev_exit_latency * 1000;
|
||||
else
|
||||
first_link_pel = hub_exit_latency * 1000;
|
||||
first_link_pel = max(udev_exit_latency, hub_exit_latency) * 1000;
|
||||
|
||||
/*
|
||||
* 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
|
||||
* is the greater of the two exit latencies.
|
||||
*/
|
||||
if (first_link_pel > hub_pel)
|
||||
udev_lpm_params->pel = first_link_pel;
|
||||
else
|
||||
udev_lpm_params->pel = hub_pel;
|
||||
udev_lpm_params->pel = max(first_link_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)
|
||||
{
|
||||
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)
|
||||
recursively_mark_NOTATTACHED(hub->ports[i]->child);
|
||||
}
|
||||
if (udev->state == USB_STATE_SUSPENDED)
|
||||
udev->active_duration -= jiffies;
|
||||
udev->state = USB_STATE_NOTATTACHED;
|
||||
update_port_device_state(udev);
|
||||
update_usb_device_state(udev, USB_STATE_NOTATTACHED);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2209,14 +2217,7 @@ void usb_set_device_state(struct usb_device *udev,
|
||||
else
|
||||
wakeup = 0;
|
||||
}
|
||||
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);
|
||||
update_usb_device_state(udev, new_state);
|
||||
} else
|
||||
recursively_mark_NOTATTACHED(udev);
|
||||
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
|
||||
* 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)
|
||||
return 0;
|
||||
|
||||
|
||||
@@ -2431,7 +2431,7 @@ int cdc_parse_cdc_header(struct usb_cdc_parsed_header *hdr,
|
||||
break;
|
||||
case USB_CDC_MBIM_EXTENDED_TYPE:
|
||||
if (elength < sizeof(struct usb_cdc_mbim_extended_desc))
|
||||
break;
|
||||
goto next_desc;
|
||||
hdr->usb_cdc_mbim_extended_desc =
|
||||
(struct usb_cdc_mbim_extended_desc *)buffer;
|
||||
break;
|
||||
|
||||
6
drivers/usb/core/trace.c
Normal file
6
drivers/usb/core/trace.c
Normal 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
61
drivers/usb/core/trace.h
Normal 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>
|
||||
@@ -46,6 +46,7 @@
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include "hub.h"
|
||||
#include "trace.h"
|
||||
|
||||
const char *usbcore_name = "usbcore";
|
||||
|
||||
@@ -746,6 +747,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
|
||||
#endif
|
||||
|
||||
dev->authorized = usb_dev_authorized(dev, usb_hcd);
|
||||
trace_usb_alloc_dev(dev);
|
||||
return dev;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_alloc_dev);
|
||||
|
||||
@@ -369,11 +369,11 @@ static void dwc2_driver_shutdown(struct platform_device *dev)
|
||||
{
|
||||
struct dwc2_hsotg *hsotg = platform_get_drvdata(dev);
|
||||
|
||||
if (hsotg->ll_hw_enabled) {
|
||||
dwc2_disable_global_interrupts(hsotg);
|
||||
synchronize_irq(hsotg->irq);
|
||||
|
||||
if (hsotg->ll_hw_enabled)
|
||||
dwc2_lowlevel_hw_disable(hsotg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -649,9 +649,13 @@ error:
|
||||
static int __maybe_unused dwc2_suspend(struct device *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;
|
||||
|
||||
if (!dwc2->ll_hw_enabled)
|
||||
return 0;
|
||||
|
||||
is_device_mode = dwc2_is_device_mode(dwc2);
|
||||
if (is_device_mode)
|
||||
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);
|
||||
int ret = 0;
|
||||
|
||||
if (!dwc2->ll_hw_enabled)
|
||||
return 0;
|
||||
|
||||
if (dwc2->phy_off_for_suspend && dwc2->ll_hw_enabled) {
|
||||
ret = __dwc2_lowlevel_hw_enable(dwc2);
|
||||
if (ret)
|
||||
|
||||
@@ -200,4 +200,15 @@ config USB_DWC3_GENERIC_PLAT
|
||||
the dwc3 child node in the device tree.
|
||||
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
|
||||
|
||||
@@ -43,6 +43,7 @@ endif
|
||||
##
|
||||
|
||||
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_EXYNOS) += dwc3-exynos.o
|
||||
obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o
|
||||
|
||||
@@ -133,6 +133,7 @@ void dwc3_enable_susphy(struct dwc3 *dwc, bool enable)
|
||||
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)
|
||||
{
|
||||
@@ -159,6 +160,7 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy)
|
||||
dwc->current_dr_role = mode;
|
||||
trace_dwc3_set_prtcap(mode);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dwc3_set_prtcap);
|
||||
|
||||
static void __dwc3_set_mode(struct work_struct *work)
|
||||
{
|
||||
@@ -281,7 +283,6 @@ static void __dwc3_set_mode(struct work_struct *work)
|
||||
}
|
||||
|
||||
out:
|
||||
pm_runtime_mark_last_busy(dwc->dev);
|
||||
pm_runtime_put_autosuspend(dwc->dev);
|
||||
mutex_unlock(&dwc->mutex);
|
||||
}
|
||||
@@ -976,7 +977,7 @@ static void dwc3_clk_disable(struct dwc3 *dwc)
|
||||
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_phy_power_off(dwc);
|
||||
@@ -984,6 +985,7 @@ static void dwc3_core_exit(struct dwc3 *dwc)
|
||||
dwc3_clk_disable(dwc);
|
||||
reset_control_assert(dwc->reset);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dwc3_core_exit);
|
||||
|
||||
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.
|
||||
*/
|
||||
static int dwc3_core_init(struct dwc3 *dwc)
|
||||
int dwc3_core_init(struct dwc3 *dwc)
|
||||
{
|
||||
unsigned int hw_mode;
|
||||
u32 reg;
|
||||
@@ -1482,10 +1484,6 @@ static int dwc3_core_init(struct dwc3 *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 &&
|
||||
(DWC3_IP_IS(DWC31)) &&
|
||||
dwc->maximum_speed == USB_SPEED_SUPER) {
|
||||
@@ -1529,6 +1527,7 @@ err_exit_ulpi:
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dwc3_core_init);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
u16 gsbuscfg0_reqinfo;
|
||||
@@ -1675,6 +1675,12 @@ static void dwc3_get_software_properties(struct dwc3 *dwc)
|
||||
|
||||
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
|
||||
* 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_software_properties(dwc);
|
||||
dwc3_get_software_properties(dwc, &data->properties);
|
||||
|
||||
dwc->usb_psy = dwc3_get_usb_power_supply(dwc);
|
||||
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_debugfs_init(dwc);
|
||||
|
||||
if (!data->skip_core_init_mode) {
|
||||
ret = dwc3_core_init_mode(dwc);
|
||||
if (ret)
|
||||
goto err_exit_debugfs;
|
||||
}
|
||||
|
||||
pm_runtime_put(dev);
|
||||
|
||||
@@ -2357,6 +2365,7 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
|
||||
probe_data.dwc = dwc;
|
||||
probe_data.res = res;
|
||||
probe_data.properties = DWC3_DEFAULT_PROPERTIES;
|
||||
|
||||
return dwc3_core_probe(&probe_data);
|
||||
}
|
||||
@@ -2645,7 +2654,6 @@ int dwc3_runtime_idle(struct dwc3 *dwc)
|
||||
break;
|
||||
}
|
||||
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_autosuspend(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -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.get = dwc3_usb_role_switch_get;
|
||||
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);
|
||||
if (IS_ERR(dwc->role_sw))
|
||||
return PTR_ERR(dwc->role_sw);
|
||||
|
||||
@@ -292,7 +292,6 @@ static int dwc3_ti_probe(struct platform_device *pdev)
|
||||
/* Setting up autosuspend */
|
||||
pm_runtime_set_autosuspend_delay(dev, DWC3_AM62_AUTOSUSPEND_DELAY);
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
return 0;
|
||||
|
||||
|
||||
489
drivers/usb/dwc3/dwc3-apple.c
Normal file
489
drivers/usb/dwc3/dwc3-apple.c
Normal 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");
|
||||
@@ -10,8 +10,16 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/mfd/syscon.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 device *dev;
|
||||
struct dwc3 dwc;
|
||||
@@ -20,6 +28,11 @@ struct dwc3_generic {
|
||||
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)
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
const struct dwc3_generic_config *plat_config;
|
||||
struct dwc3_probe_data probe_data = {};
|
||||
struct device *dev = &pdev->dev;
|
||||
struct dwc3_generic *dwc3g;
|
||||
@@ -75,6 +118,22 @@ static int dwc3_generic_probe(struct platform_device *pdev)
|
||||
probe_data.dwc = &dwc3g->dwc;
|
||||
probe_data.res = res;
|
||||
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);
|
||||
if (ret)
|
||||
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)
|
||||
};
|
||||
|
||||
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[] = {
|
||||
{ .compatible = "spacemit,k1-dwc3", },
|
||||
{ .compatible = "fsl,ls1028a-dwc3", &fsl_ls1028_dwc3},
|
||||
{ .compatible = "eswin,eic7700-dwc3", &eic7700_dwc3},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dwc3_generic_of_match);
|
||||
|
||||
@@ -312,7 +312,6 @@ static int dwc3_imx8mp_resume(struct dwc3_imx8mp *dwc3_imx, pm_message_t msg)
|
||||
if (dwc3_imx->wakeup_pending) {
|
||||
dwc3_imx->wakeup_pending = false;
|
||||
if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE) {
|
||||
pm_runtime_mark_last_busy(dwc->dev);
|
||||
pm_runtime_put_autosuspend(dwc->dev);
|
||||
} else {
|
||||
/*
|
||||
|
||||
@@ -323,7 +323,6 @@ static void dwc3_pci_resume_work(struct work_struct *work)
|
||||
return;
|
||||
}
|
||||
|
||||
pm_runtime_mark_last_busy(&dwc3->dev);
|
||||
pm_runtime_put_sync_autosuspend(&dwc3->dev);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -704,6 +704,7 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
|
||||
probe_data.dwc = &qcom->dwc;
|
||||
probe_data.res = &res;
|
||||
probe_data.ignore_clocks_and_resets = true;
|
||||
probe_data.properties = DWC3_DEFAULT_PROPERTIES;
|
||||
ret = dwc3_core_probe(&probe_data);
|
||||
if (ret) {
|
||||
ret = dev_err_probe(dev, ret, "failed to register DWC3 Core\n");
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_autosuspend(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -3879,7 +3879,7 @@ static void dwc3_gadget_endpoint_stream_event(struct dwc3_ep *dep,
|
||||
case DEPEVT_STREAM_NOSTREAM:
|
||||
dep->flags &= ~DWC3_EP_STREAM_PRIMED;
|
||||
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));
|
||||
break;
|
||||
}
|
||||
@@ -4817,6 +4817,7 @@ err1:
|
||||
err0:
|
||||
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,
|
||||
dwc->ep0_trb, dwc->ep0_trb_addr);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dwc3_gadget_exit);
|
||||
|
||||
int dwc3_gadget_suspend(struct dwc3 *dwc)
|
||||
{
|
||||
|
||||
@@ -9,22 +9,66 @@
|
||||
#include <linux/types.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()
|
||||
* @dwc: Reference to dwc3 context structure
|
||||
* @res: resource for the DWC3 core mmio region
|
||||
* @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
|
||||
* @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 *dwc;
|
||||
struct resource *res;
|
||||
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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/*
|
||||
* 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_resume(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);
|
||||
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
|
||||
|
||||
@@ -37,6 +37,9 @@ static void dwc3_power_off_all_roothub_ports(struct dwc3 *dwc)
|
||||
|
||||
/* xhci regs are not mapped yet, do it temporarily here */
|
||||
if (dwc->xhci_resources[0].start) {
|
||||
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) {
|
||||
dev_err(dwc->dev, "Failed to ioremap xhci_regs\n");
|
||||
@@ -217,6 +220,7 @@ err:
|
||||
platform_device_put(xhci);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dwc3_host_init);
|
||||
|
||||
void dwc3_host_exit(struct dwc3 *dwc)
|
||||
{
|
||||
@@ -227,3 +231,4 @@ void dwc3_host_exit(struct dwc3 *dwc)
|
||||
platform_device_unregister(dwc->xhci);
|
||||
dwc->xhci = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dwc3_host_exit);
|
||||
|
||||
@@ -1332,9 +1332,7 @@ static void ffs_dmabuf_release(struct kref *ref)
|
||||
struct dma_buf *dmabuf = attach->dmabuf;
|
||||
|
||||
pr_vdebug("FFS DMABUF release\n");
|
||||
dma_resv_lock(dmabuf->resv, NULL);
|
||||
dma_buf_unmap_attachment(attach, priv->sgt, priv->dir);
|
||||
dma_resv_unlock(dmabuf->resv);
|
||||
dma_buf_unmap_attachment_unlocked(attach, priv->sgt, priv->dir);
|
||||
|
||||
dma_buf_detach(attach->dmabuf, attach);
|
||||
dma_buf_put(dmabuf);
|
||||
|
||||
@@ -1272,8 +1272,7 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
|
||||
INIT_WORK(&hidg->work, get_report_workqueue_handler);
|
||||
hidg->workqueue = alloc_workqueue("report_work",
|
||||
WQ_FREEZABLE |
|
||||
WQ_MEM_RECLAIM,
|
||||
WQ_FREEZABLE | WQ_MEM_RECLAIM | WQ_PERCPU,
|
||||
1);
|
||||
|
||||
if (!hidg->workqueue) {
|
||||
|
||||
@@ -40,6 +40,7 @@ MODULE_LICENSE("GPL");
|
||||
|
||||
static DEFINE_IDA(driver_id_numbers);
|
||||
#define DRIVER_DRIVER_NAME_LENGTH_MAX 32
|
||||
#define USB_RAW_IO_LENGTH_MAX KMALLOC_MAX_SIZE
|
||||
|
||||
#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);
|
||||
if (!usb_raw_io_flags_valid(io->flags))
|
||||
return ERR_PTR(-EINVAL);
|
||||
if (io->length > USB_RAW_IO_LENGTH_MAX)
|
||||
return ERR_PTR(-EINVAL);
|
||||
if (get_from_user)
|
||||
data = memdup_user(ptr + sizeof(*io), io->length);
|
||||
else {
|
||||
|
||||
@@ -147,6 +147,12 @@ static struct usb_gadget_strings *dev_strings[] = {
|
||||
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;
|
||||
@@ -156,6 +162,7 @@ static void zero_autoresume(struct timer_list *unused)
|
||||
{
|
||||
struct usb_composite_dev *cdev = autoresume_cdev;
|
||||
struct usb_gadget *g = cdev->gadget;
|
||||
int status;
|
||||
|
||||
/* unconfigured devices can't issue wakeups */
|
||||
if (!cdev->config)
|
||||
@@ -165,10 +172,18 @@ static void zero_autoresume(struct timer_list *unused)
|
||||
* more significant than just a timer firing; likely
|
||||
* because of some direct user request.
|
||||
*/
|
||||
if (g->speed != USB_SPEED_UNKNOWN) {
|
||||
int status = usb_gadget_wakeup(g);
|
||||
INFO(cdev, "%s --> %d\n", __func__, status);
|
||||
if (g->speed == USB_SPEED_UNKNOWN)
|
||||
return;
|
||||
|
||||
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)
|
||||
@@ -206,9 +221,6 @@ static struct usb_configuration loopback_driver = {
|
||||
/* .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,
|
||||
const struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
@@ -248,9 +260,6 @@ module_param_named(isoc_maxburst, gzero_options.isoc_maxburst, uint,
|
||||
S_IRUGO|S_IWUSR);
|
||||
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_PARM_DESC(qlen, "depth of loopback queue");
|
||||
|
||||
|
||||
@@ -2415,7 +2415,6 @@ int cdns2_gadget_resume(struct cdns2_device *pdev, bool hibernated)
|
||||
|
||||
void cdns2_gadget_remove(struct cdns2_device *pdev)
|
||||
{
|
||||
pm_runtime_mark_last_busy(pdev->dev);
|
||||
pm_runtime_put_autosuspend(pdev->dev);
|
||||
|
||||
usb_del_gadget(&pdev->gadget);
|
||||
|
||||
@@ -1558,12 +1558,6 @@ static int __tegra_xudc_ep_set_halt(struct tegra_xudc_ep *ep, bool halt)
|
||||
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) {
|
||||
ep_halt(xudc, ep->index);
|
||||
} else {
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/sys_soc.h>
|
||||
@@ -111,7 +112,6 @@ static void ehci_platform_power_off(struct platform_device *dev)
|
||||
int clk;
|
||||
|
||||
for (clk = EHCI_MAX_CLKS - 1; clk >= 0; clk--)
|
||||
if (priv->clks[clk])
|
||||
clk_disable_unprepare(priv->clks[clk]);
|
||||
}
|
||||
|
||||
@@ -239,9 +239,11 @@ static int ehci_platform_probe(struct platform_device *dev)
|
||||
struct usb_hcd *hcd;
|
||||
struct resource *res_mem;
|
||||
struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev);
|
||||
const struct of_device_id *match;
|
||||
struct ehci_platform_priv *priv;
|
||||
struct ehci_hcd *ehci;
|
||||
int err, irq, clk = 0;
|
||||
bool dma_mask_64;
|
||||
|
||||
if (usb_disabled())
|
||||
return -ENODEV;
|
||||
@@ -253,8 +255,13 @@ static int ehci_platform_probe(struct platform_device *dev)
|
||||
if (!pdata)
|
||||
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,
|
||||
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) {
|
||||
dev_err(&dev->dev, "Error: DMA mask configuration failed\n");
|
||||
return err;
|
||||
@@ -298,7 +305,9 @@ static int ehci_platform_probe(struct platform_device *dev)
|
||||
if (of_device_is_compatible(dev->dev.of_node,
|
||||
"aspeed,ast2500-ehci") ||
|
||||
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;
|
||||
|
||||
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)
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -455,12 +475,19 @@ static int __maybe_unused ehci_platform_resume(struct device *dev)
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd);
|
||||
struct device *companion_dev;
|
||||
int err;
|
||||
|
||||
err = reset_control_deassert(priv->rsts);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (pdata->power_on) {
|
||||
int err = pdata->power_on(pdev);
|
||||
if (err < 0)
|
||||
err = pdata->power_on(pdev);
|
||||
if (err < 0) {
|
||||
reset_control_assert(priv->rsts);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
companion_dev = usb_of_get_companion_dev(hcd->self.controller);
|
||||
if (companion_dev) {
|
||||
@@ -485,6 +512,7 @@ static const struct of_device_id vt8500_ehci_ids[] = {
|
||||
{ .compatible = "wm,prizm-ehci", },
|
||||
{ .compatible = "generic-ehci", },
|
||||
{ .compatible = "cavium,octeon-6335-ehci", },
|
||||
{ .compatible = "aspeed,ast2700-ehci", .data = (void *)1 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, vt8500_ehci_ids);
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_data/usb-davinci.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
@@ -166,17 +165,6 @@ static int ohci_da8xx_has_oci(struct usb_hcd *hcd)
|
||||
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,
|
||||
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)
|
||||
{
|
||||
struct device *dev = hcd->self.controller;
|
||||
struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev);
|
||||
struct ohci_hcd *ohci = hcd_to_ohci(hcd);
|
||||
int result;
|
||||
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_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);
|
||||
|
||||
return result;
|
||||
|
||||
@@ -69,7 +69,6 @@ static void ohci_platform_power_off(struct platform_device *dev)
|
||||
int clk;
|
||||
|
||||
for (clk = OHCI_MAX_CLKS - 1; clk >= 0; clk--)
|
||||
if (priv->clks[clk])
|
||||
clk_disable_unprepare(priv->clks[clk]);
|
||||
}
|
||||
|
||||
@@ -271,6 +270,7 @@ static int ohci_platform_suspend(struct device *dev)
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
struct usb_ohci_pdata *pdata = dev->platform_data;
|
||||
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);
|
||||
int ret;
|
||||
|
||||
@@ -281,6 +281,14 @@ static int ohci_platform_suspend(struct device *dev)
|
||||
if (pdata->power_suspend)
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -289,12 +297,20 @@ static int ohci_platform_resume_common(struct device *dev, bool hibernated)
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
struct usb_ohci_pdata *pdata = dev_get_platdata(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) {
|
||||
int err = pdata->power_on(pdev);
|
||||
if (err < 0)
|
||||
err = pdata->power_on(pdev);
|
||||
if (err < 0) {
|
||||
reset_control_assert(priv->resets);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
ohci_resume(hcd, hibernated);
|
||||
|
||||
|
||||
@@ -445,6 +445,7 @@ struct uhci_hcd {
|
||||
short load[MAX_PHASE]; /* Periodic allocations */
|
||||
|
||||
struct clk *clk; /* (optional) clock source */
|
||||
struct reset_control *rsts; /* (optional) clock reset */
|
||||
|
||||
/* Reset host controller */
|
||||
void (*reset_hc) (struct uhci_hcd *uhci);
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
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)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
bool dma_mask_64 = false;
|
||||
struct usb_hcd *hcd;
|
||||
struct uhci_hcd *uhci;
|
||||
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.
|
||||
* 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)
|
||||
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") ||
|
||||
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;
|
||||
dev_info(&pdev->dev,
|
||||
"Enabled Aspeed implementation workarounds\n");
|
||||
@@ -132,17 +139,28 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev)
|
||||
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);
|
||||
if (ret < 0)
|
||||
goto err_clk;
|
||||
goto err_reset;
|
||||
|
||||
ret = usb_add_hcd(hcd, ret, IRQF_SHARED);
|
||||
if (ret)
|
||||
goto err_clk;
|
||||
goto err_reset;
|
||||
|
||||
device_wakeup_enable(hcd->self.controller);
|
||||
return 0;
|
||||
|
||||
err_reset:
|
||||
reset_control_assert(uhci->rsts);
|
||||
err_clk:
|
||||
clk_disable_unprepare(uhci->clk);
|
||||
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 uhci_hcd *uhci = hcd_to_uhci(hcd);
|
||||
|
||||
reset_control_assert(uhci->rsts);
|
||||
clk_disable_unprepare(uhci->clk);
|
||||
usb_remove_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[] = {
|
||||
{ .compatible = "generic-uhci", },
|
||||
{ .compatible = "platform-uhci", },
|
||||
{ .compatible = "aspeed,ast2700-uhci", .data = (void *)1 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, platform_uhci_ids);
|
||||
|
||||
@@ -1388,7 +1388,7 @@ static int xenhcd_get_frame(struct usb_hcd *hcd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct hc_driver xenhcd_usb20_hc_driver = {
|
||||
static const struct hc_driver xenhcd_usb20_hc_driver = {
|
||||
.description = "xen-hcd",
|
||||
.product_desc = "Xen USB2.0 Virtual Host Controller",
|
||||
.hcd_priv_size = sizeof(struct xenhcd_info),
|
||||
@@ -1413,7 +1413,7 @@ static struct hc_driver xenhcd_usb20_hc_driver = {
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct hc_driver xenhcd_usb11_hc_driver = {
|
||||
static const struct hc_driver xenhcd_usb11_hc_driver = {
|
||||
.description = "xen-hcd",
|
||||
.product_desc = "Xen USB1.1 Virtual Host Controller",
|
||||
.hcd_priv_size = sizeof(struct xenhcd_info),
|
||||
|
||||
@@ -1,93 +1,120 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* xHCI Host Controller Capability Registers.
|
||||
* xHCI Specification Section 5.3, Revision 1.2.
|
||||
*/
|
||||
|
||||
/* hc_capbase bitmasks */
|
||||
/* bits 7:0 - how long is the Capabilities register */
|
||||
#define HC_LENGTH(p) XHCI_HC_LENGTH(p)
|
||||
/* bits 31:16 */
|
||||
#include <linux/bits.h>
|
||||
|
||||
/* hc_capbase - bitmasks */
|
||||
/* 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)
|
||||
|
||||
/* 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_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)
|
||||
/* bits 24:31, Max Ports - max value is 0x7F = 127 ports */
|
||||
#define HCS_MAX_PORTS(p) (((p) >> 24) & 0x7f)
|
||||
/* bits 31:24, Max Ports - max value is 255 */
|
||||
#define HCS_MAX_PORTS(p) (((p) >> 24) & 0xff)
|
||||
|
||||
/* HCSPARAMS2 - hcs_params2 - bitmasks */
|
||||
/* bits 0:3, frames or uframes that SW needs to queue transactions
|
||||
* ahead of the HW to meet periodic deadlines */
|
||||
#define HCS_IST(p) (((p) >> 0) & 0xf)
|
||||
/* bits 4:7, max number of Event Ring segments */
|
||||
/*
|
||||
* bits 3:0 - Isochronous Scheduling Threshold, frames or uframes that SW
|
||||
* needs to queue transactions ahead of the HW to meet periodic deadlines.
|
||||
* - 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)
|
||||
/* bits 21:25 Hi 5 bits of Scratchpad buffers SW must allocate for the HW */
|
||||
/* bit 26 Scratchpad restore - for save/restore HW state - not used yet */
|
||||
/* bits 27:31 Lo 5 bits of Scratchpad buffers SW must allocate for the HW */
|
||||
#define HCS_MAX_SCRATCHPAD(p) ((((p) >> 16) & 0x3e0) | (((p) >> 27) & 0x1f))
|
||||
/* bits 20:8 - Rsvd */
|
||||
/* bits 25:21 - Max Scratchpad Buffers (Hi), 5 Most significant bits */
|
||||
#define HCS_MAX_SP_HI(p) (((p) >> 21) & 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 */
|
||||
/* 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)
|
||||
/* 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)
|
||||
|
||||
/* HCCPARAMS - hcc_params - bitmasks */
|
||||
/* true: HC can use 64-bit address pointers */
|
||||
#define HCC_64BIT_ADDR(p) ((p) & (1 << 0))
|
||||
/* true: HC can do bandwidth negotiation */
|
||||
#define HCC_BANDWIDTH_NEG(p) ((p) & (1 << 1))
|
||||
/* true: HC uses 64-byte Device Context structures
|
||||
* FIXME 64-byte context structures aren't supported yet.
|
||||
*/
|
||||
#define HCC_64BYTE_CONTEXT(p) ((p) & (1 << 2))
|
||||
/* true: HC has port power switches */
|
||||
#define HCC_PPC(p) ((p) & (1 << 3))
|
||||
/* true: HC has port indicators */
|
||||
#define HCS_INDICATOR(p) ((p) & (1 << 4))
|
||||
/* true: HC has Light HC Reset Capability */
|
||||
#define HCC_LIGHT_RESET(p) ((p) & (1 << 5))
|
||||
/* true: HC supports latency tolerance messaging */
|
||||
#define HCC_LTC(p) ((p) & (1 << 6))
|
||||
/* true: no secondary Stream ID Support */
|
||||
#define HCC_NSS(p) ((p) & (1 << 7))
|
||||
/* true: HC supports Stopped - Short Packet */
|
||||
#define HCC_SPC(p) ((p) & (1 << 9))
|
||||
/* true: HC has Contiguous Frame ID Capability */
|
||||
#define HCC_CFC(p) ((p) & (1 << 11))
|
||||
/* Max size for Primary Stream Arrays - 2^(n+1), where n is bits 12:15 */
|
||||
/* HCCPARAMS1 - hcc_params - bitmasks */
|
||||
/* bit 0 - 64-bit Addressing Capability */
|
||||
#define HCC_64BIT_ADDR BIT(0)
|
||||
/* bit 1 - BW Negotiation Capability */
|
||||
#define HCC_BANDWIDTH_NEG BIT(1)
|
||||
/* bit 2 - Context Size */
|
||||
#define HCC_64BYTE_CONTEXT BIT(2)
|
||||
#define CTX_SIZE(_hcc) (_hcc & HCC_64BYTE_CONTEXT ? 64 : 32)
|
||||
/* bit 3 - Port Power Control */
|
||||
#define HCC_PPC BIT(3)
|
||||
/* bit 4 - Port Indicators */
|
||||
#define HCS_INDICATOR BIT(4)
|
||||
/* bit 5 - Light HC Reset Capability */
|
||||
#define HCC_LIGHT_RESET BIT(5)
|
||||
/* bit 6 - Latency Tolerance Messaging Capability */
|
||||
#define HCC_LTC BIT(6)
|
||||
/* bit 7 - No Secondary Stream ID Support */
|
||||
#define HCC_NSS BIT(7)
|
||||
/* bit 8 - Parse All Event Data */
|
||||
/* bit 9 - Short Packet Capability */
|
||||
#define HCC_SPC BIT(9)
|
||||
/* bit 10 - Stopped EDTLA Capability */
|
||||
/* bit 11 - Contiguous Frame ID Capability */
|
||||
#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))
|
||||
/* Extended Capabilities pointer from PCI base - section 5.3.6 */
|
||||
#define HCC_EXT_CAPS(p) XHCI_HCC_EXT_CAPS(p)
|
||||
/* bits 31:16 - xHCI Extended Capabilities Pointer, from PCI base: 2^(n) */
|
||||
#define HCC_EXT_CAPS(p) (((p) >> 16) & 0xffff)
|
||||
|
||||
#define CTX_SIZE(_hcc) (HCC_64BYTE_CONTEXT(_hcc) ? 64 : 32)
|
||||
|
||||
/* db_off bitmask - bits 31:2 Doorbell Array Offset */
|
||||
/* DBOFF - db_off - bitmasks */
|
||||
/* bits 1:0 - Rsvd */
|
||||
/* bits 31:2 - Doorbell Array Offset */
|
||||
#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)
|
||||
|
||||
/* HCCPARAMS2 - hcc_params2 - bitmasks */
|
||||
/* true: HC supports U3 entry Capability */
|
||||
#define HCC2_U3C(p) ((p) & (1 << 0))
|
||||
/* true: HC supports Configure endpoint command Max exit latency too large */
|
||||
#define HCC2_CMC(p) ((p) & (1 << 1))
|
||||
/* true: HC supports Force Save context Capability */
|
||||
#define HCC2_FSC(p) ((p) & (1 << 2))
|
||||
/* true: HC supports Compliance Transition Capability */
|
||||
#define HCC2_CTC(p) ((p) & (1 << 3))
|
||||
/* true: HC support Large ESIT payload Capability > 48k */
|
||||
#define HCC2_LEC(p) ((p) & (1 << 4))
|
||||
/* true: HC support Configuration Information Capability */
|
||||
#define HCC2_CIC(p) ((p) & (1 << 5))
|
||||
/* true: HC support Extended TBC Capability, Isoc burst count > 65535 */
|
||||
#define HCC2_ETC(p) ((p) & (1 << 6))
|
||||
/* true: HC support Extended TBC TRB Status Capability */
|
||||
#define HCC2_ETC_TSC(p) ((p) & (1 << 7))
|
||||
/* true: HC support Get/Set Extended Property Capability */
|
||||
#define HCC2_GSC(p) ((p) & (1 << 8))
|
||||
/* true: HC support Virtualization Based Trusted I/O Capability */
|
||||
#define HCC2_VTC(p) ((p) & (1 << 9))
|
||||
/* true: HC support Double BW on a eUSB2 HS ISOC EP */
|
||||
#define HCC2_EUSB2_DIC(p) ((p) & (1 << 11))
|
||||
/* bit 0 - U3 Entry Capability */
|
||||
#define HCC2_U3C BIT(0)
|
||||
/* bit 1 - Configure Endpoint Command Max Exit Latency Too Large Capability */
|
||||
#define HCC2_CMC BIT(1)
|
||||
/* bit 2 - Force Save Context Capabilitu */
|
||||
#define HCC2_FSC BIT(2)
|
||||
/* bit 3 - Compliance Transition Capability, false: compliance is enabled by default */
|
||||
#define HCC2_CTC BIT(3)
|
||||
/* bit 4 - Large ESIT Payload Capability, true: HC support ESIT payload > 48k */
|
||||
#define HCC2_LEC BIT(4)
|
||||
/* bit 5 - Configuration Information Capability */
|
||||
#define HCC2_CIC BIT(5)
|
||||
/* bit 6 - Extended TBC Capability, true: Isoc burst count > 65535 */
|
||||
#define HCC2_ETC BIT(6)
|
||||
/* bit 7 - Extended TBC TRB Status Capability */
|
||||
#define HCC2_ETC_TSC BIT(7)
|
||||
/* bit 8 - Get/Set Extended Property Capability */
|
||||
#define HCC2_GSC BIT(8)
|
||||
/* bit 9 - Virtualization Based Trusted I/O Capability */
|
||||
#define HCC2_VTC BIT(9)
|
||||
/* bit 10 - Rsvd */
|
||||
/* 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 */
|
||||
|
||||
@@ -374,7 +374,7 @@ int dbc_ep_queue(struct dbc_request *req)
|
||||
ret = dbc_ep_do_queue(req);
|
||||
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);
|
||||
|
||||
@@ -677,7 +677,7 @@ static int xhci_dbc_start(struct xhci_dbc *dbc)
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -1023,7 +1023,7 @@ static void xhci_dbc_handle_events(struct work_struct *work)
|
||||
return;
|
||||
}
|
||||
|
||||
mod_delayed_work(system_wq, &dbc->event_work,
|
||||
mod_delayed_work(system_percpu_wq, &dbc->event_work,
|
||||
msecs_to_jiffies(poll_interval));
|
||||
}
|
||||
|
||||
@@ -1274,7 +1274,7 @@ static ssize_t dbc_poll_interval_ms_store(struct device *dev,
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -329,7 +329,7 @@ static int xhci_portsc_show(struct seq_file *s, void *unused)
|
||||
u32 portsc;
|
||||
char str[XHCI_MSG_MAX];
|
||||
|
||||
portsc = readl(port->addr);
|
||||
portsc = xhci_portsc_readl(port);
|
||||
seq_printf(s, "%s\n", xhci_decode_portsc(str, portsc));
|
||||
|
||||
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 CTC is clear, compliance is enabled by default */
|
||||
if (!HCC2_CTC(xhci->hcc_params2))
|
||||
if (!(xhci->hcc_params2 & HCC2_CTC))
|
||||
return count;
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
/* 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) {
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
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 &= ~PORT_PLS_MASK;
|
||||
portsc |= PORT_LINK_STROBE | XDEV_COMP_MODE;
|
||||
writel(portsc, port->addr);
|
||||
xhci_portsc_writel(port, portsc);
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
} else {
|
||||
return -EINVAL;
|
||||
@@ -383,6 +383,39 @@ static const struct file_operations port_fops = {
|
||||
.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,
|
||||
struct xhci_file_map *files,
|
||||
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,
|
||||
struct dentry *parent)
|
||||
{
|
||||
unsigned int num_ports;
|
||||
char port_name[8];
|
||||
struct xhci_port *port;
|
||||
struct dentry *dir;
|
||||
|
||||
num_ports = HCS_MAX_PORTS(xhci->hcs_params1);
|
||||
|
||||
parent = debugfs_create_dir("ports", parent);
|
||||
|
||||
while (num_ports--) {
|
||||
scnprintf(port_name, sizeof(port_name), "port%02d",
|
||||
num_ports + 1);
|
||||
for (int i = 0; i < xhci->max_ports; i++) {
|
||||
scnprintf(port_name, sizeof(port_name), "port%02d", i + 1);
|
||||
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("portli", 0444, dir, port, &portli_fops);
|
||||
}
|
||||
}
|
||||
|
||||
static int xhci_port_bw_show(struct xhci_hcd *xhci, u8 dev_speed,
|
||||
struct seq_file *s)
|
||||
{
|
||||
unsigned int num_ports;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
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)
|
||||
return ret;
|
||||
|
||||
num_ports = HCS_MAX_PORTS(xhci->hcs_params1);
|
||||
|
||||
ctx = xhci_alloc_port_bw_ctx(xhci, 0);
|
||||
if (!ctx) {
|
||||
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
|
||||
* 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,
|
||||
ctx->bytes[i]);
|
||||
err_out:
|
||||
|
||||
@@ -110,7 +110,7 @@ static int xhci_create_usb3x_bos_desc(struct xhci_hcd *xhci, char *buf,
|
||||
ss_cap->bU2DevExitLat = 0; /* set later */
|
||||
|
||||
reg = readl(&xhci->cap_regs->hcc_params);
|
||||
if (HCC_LTC(reg))
|
||||
if (reg & HCC_LTC)
|
||||
ss_cap->bmAttributes |= USB_LTM_SUPPORT;
|
||||
|
||||
if ((xhci->quirks & XHCI_LPM_SUPPORT)) {
|
||||
@@ -263,7 +263,7 @@ static void xhci_common_hub_descriptor(struct xhci_hcd *xhci,
|
||||
desc->bNbrPorts = ports;
|
||||
temp = 0;
|
||||
/* 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;
|
||||
else
|
||||
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));
|
||||
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
|
||||
* 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;
|
||||
/* bit 0 is reserved, bit 1 is for port 1, etc. */
|
||||
for (i = 0; i < ports; i++) {
|
||||
portsc = readl(rhub->ports[i]->addr);
|
||||
portsc = xhci_portsc_readl(rhub->ports[i]);
|
||||
if (portsc & PORT_DEV_REMOVE)
|
||||
port_removable |= 1 << (i + 1);
|
||||
}
|
||||
@@ -566,19 +566,19 @@ static void xhci_disable_port(struct xhci_hcd *xhci, struct xhci_port *port)
|
||||
return;
|
||||
}
|
||||
|
||||
portsc = readl(port->addr);
|
||||
portsc = xhci_portsc_readl(port);
|
||||
portsc = xhci_port_state_to_neutral(portsc);
|
||||
|
||||
/* 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",
|
||||
hcd->self.busnum, port->hcd_portnum + 1, portsc);
|
||||
}
|
||||
|
||||
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;
|
||||
u32 status;
|
||||
@@ -621,8 +621,8 @@ static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue,
|
||||
return;
|
||||
}
|
||||
/* Change bits are all write 1 to clear */
|
||||
writel(port_status | status, addr);
|
||||
port_status = readl(addr);
|
||||
xhci_portsc_writel(port, port_status | status);
|
||||
port_status = xhci_portsc_readl(port);
|
||||
|
||||
xhci_dbg(xhci, "clear port%d %s change, portsc: 0x%x\n",
|
||||
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;
|
||||
|
||||
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",
|
||||
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) {
|
||||
/* Power on */
|
||||
writel(temp | PORT_POWER, port->addr);
|
||||
readl(port->addr);
|
||||
xhci_portsc_writel(port, temp | PORT_POWER);
|
||||
xhci_portsc_readl(port);
|
||||
} else {
|
||||
/* Power off */
|
||||
writel(temp & ~PORT_POWER, port->addr);
|
||||
xhci_portsc_writel(port, temp & ~PORT_POWER);
|
||||
}
|
||||
|
||||
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 */
|
||||
port = xhci->usb2_rhub.ports[wIndex];
|
||||
temp = readl(port->addr + PORTPMSC);
|
||||
temp = readl(&port->port_reg->portpmsc);
|
||||
temp |= test_mode << PORT_TEST_MODE_SHIFT;
|
||||
writel(temp, port->addr + PORTPMSC);
|
||||
writel(temp, &port->port_reg->portpmsc);
|
||||
xhci->test_mode = test_mode;
|
||||
if (test_mode == USB_TEST_FORCE_ENABLE)
|
||||
xhci_start(xhci);
|
||||
@@ -700,7 +700,7 @@ static int xhci_enter_test_mode(struct xhci_hcd *xhci,
|
||||
/* Disable all Device Slots */
|
||||
xhci_dbg(xhci, "Disable all slots\n");
|
||||
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])
|
||||
continue;
|
||||
|
||||
@@ -801,11 +801,11 @@ void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port,
|
||||
u32 temp;
|
||||
u32 portsc;
|
||||
|
||||
portsc = readl(port->addr);
|
||||
portsc = xhci_portsc_readl(port);
|
||||
temp = xhci_port_state_to_neutral(portsc);
|
||||
temp &= ~PORT_PLS_MASK;
|
||||
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",
|
||||
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;
|
||||
|
||||
temp = readl(port->addr);
|
||||
temp = xhci_portsc_readl(port);
|
||||
temp = xhci_port_state_to_neutral(temp);
|
||||
|
||||
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
|
||||
temp &= ~PORT_WKOC_E;
|
||||
|
||||
writel(temp, port->addr);
|
||||
xhci_portsc_writel(port, temp);
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
temp = readl(port->addr);
|
||||
temp = xhci_portsc_readl(port);
|
||||
if (temp & port_bit) {
|
||||
temp = xhci_port_state_to_neutral(temp);
|
||||
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);
|
||||
} 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",
|
||||
hcd->self.busnum, wIndex + 1, port_status);
|
||||
@@ -1263,7 +1263,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
||||
|
||||
wIndex--;
|
||||
port = ports[portnum1 - 1];
|
||||
temp = readl(port->addr);
|
||||
temp = xhci_portsc_readl(port);
|
||||
if (temp == ~(u32)0) {
|
||||
xhci_hc_died(xhci);
|
||||
retval = -ENODEV;
|
||||
@@ -1288,7 +1288,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
||||
retval = -EINVAL;
|
||||
break;
|
||||
}
|
||||
port_li = readl(port->addr + PORTLI);
|
||||
port_li = readl(&port->port_reg->portli);
|
||||
status = xhci_get_ext_port_status(temp, port_li);
|
||||
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];
|
||||
wIndex--;
|
||||
temp = readl(port->addr);
|
||||
temp = xhci_portsc_readl(port);
|
||||
if (temp == ~(u32)0) {
|
||||
xhci_hc_died(xhci);
|
||||
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? */
|
||||
switch (wValue) {
|
||||
case USB_PORT_FEAT_SUSPEND:
|
||||
temp = readl(port->addr);
|
||||
temp = xhci_portsc_readl(port);
|
||||
if ((temp & PORT_PLS_MASK) != XDEV_U0) {
|
||||
/* Resume the port to U0 first */
|
||||
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
|
||||
* enabled (PED = ‘1’,PLS < ‘3’) state.
|
||||
*/
|
||||
temp = readl(port->addr);
|
||||
temp = xhci_portsc_readl(port);
|
||||
if ((temp & PORT_PE) == 0 || (temp & PORT_RESET)
|
||||
|| (temp & PORT_PLS_MASK) >= XDEV_U3) {
|
||||
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 */
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
|
||||
temp = readl(port->addr);
|
||||
temp = xhci_portsc_readl(port);
|
||||
bus_state->suspended_ports |= 1 << wIndex;
|
||||
break;
|
||||
case USB_PORT_FEAT_LINK_STATE:
|
||||
temp = readl(port->addr);
|
||||
temp = xhci_portsc_readl(port);
|
||||
/* Disable port */
|
||||
if (link_state == USB_SS_PORT_LS_SS_DISABLED) {
|
||||
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 |
|
||||
PORT_OCC | PORT_RC | PORT_PLC |
|
||||
PORT_CEC;
|
||||
writel(temp | PORT_PE, port->addr);
|
||||
temp = readl(port->addr);
|
||||
xhci_portsc_writel(port, temp | PORT_PE);
|
||||
temp = xhci_portsc_readl(port);
|
||||
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",
|
||||
hcd->self.busnum, portnum1);
|
||||
xhci_set_link_state(xhci, port, link_state);
|
||||
temp = readl(port->addr);
|
||||
temp = xhci_portsc_readl(port);
|
||||
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.
|
||||
*/
|
||||
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");
|
||||
break;
|
||||
}
|
||||
@@ -1414,7 +1414,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
||||
hcd->self.busnum, portnum1);
|
||||
xhci_set_link_state(xhci, port, link_state);
|
||||
|
||||
temp = readl(port->addr);
|
||||
temp = xhci_portsc_readl(port);
|
||||
break;
|
||||
}
|
||||
/* 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",
|
||||
hcd->self.busnum, portnum1);
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
temp = readl(port->addr);
|
||||
temp = xhci_portsc_readl(port);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1480,12 +1480,12 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
while (retries--) {
|
||||
usleep_range(4000, 8000);
|
||||
temp = readl(port->addr);
|
||||
temp = xhci_portsc_readl(port);
|
||||
if ((temp & PORT_PLS_MASK) == XDEV_U3)
|
||||
break;
|
||||
}
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
temp = readl(port->addr);
|
||||
temp = xhci_portsc_readl(port);
|
||||
bus_state->suspended_ports |= 1 << wIndex;
|
||||
}
|
||||
break;
|
||||
@@ -1500,38 +1500,38 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
||||
break;
|
||||
case USB_PORT_FEAT_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",
|
||||
hcd->self.busnum, portnum1, temp);
|
||||
break;
|
||||
case USB_PORT_FEAT_REMOTE_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",
|
||||
hcd->self.busnum, portnum1, temp);
|
||||
break;
|
||||
case USB_PORT_FEAT_BH_PORT_RESET:
|
||||
temp |= PORT_WR;
|
||||
writel(temp, port->addr);
|
||||
temp = readl(port->addr);
|
||||
xhci_portsc_writel(port, temp);
|
||||
temp = xhci_portsc_readl(port);
|
||||
break;
|
||||
case USB_PORT_FEAT_U1_TIMEOUT:
|
||||
if (hcd->speed < HCD_USB3)
|
||||
goto error;
|
||||
temp = readl(port->addr + PORTPMSC);
|
||||
temp = readl(&port->port_reg->portpmsc);
|
||||
temp &= ~PORT_U1_TIMEOUT_MASK;
|
||||
temp |= PORT_U1_TIMEOUT(timeout);
|
||||
writel(temp, port->addr + PORTPMSC);
|
||||
writel(temp, &port->port_reg->portpmsc);
|
||||
break;
|
||||
case USB_PORT_FEAT_U2_TIMEOUT:
|
||||
if (hcd->speed < HCD_USB3)
|
||||
goto error;
|
||||
temp = readl(port->addr + PORTPMSC);
|
||||
temp = readl(&port->port_reg->portpmsc);
|
||||
temp &= ~PORT_U2_TIMEOUT_MASK;
|
||||
temp |= PORT_U2_TIMEOUT(timeout);
|
||||
writel(temp, port->addr + PORTPMSC);
|
||||
writel(temp, &port->port_reg->portpmsc);
|
||||
break;
|
||||
case USB_PORT_FEAT_TEST:
|
||||
/* 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;
|
||||
}
|
||||
/* unblock any posted writes */
|
||||
temp = readl(port->addr);
|
||||
temp = xhci_portsc_readl(port);
|
||||
break;
|
||||
case ClearPortFeature:
|
||||
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];
|
||||
|
||||
wIndex--;
|
||||
temp = readl(port->addr);
|
||||
temp = xhci_portsc_readl(port);
|
||||
if (temp == ~(u32)0) {
|
||||
xhci_hc_died(xhci);
|
||||
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);
|
||||
switch (wValue) {
|
||||
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, "PORTSC %04x\n", temp);
|
||||
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_PORT_LINK_STATE:
|
||||
case USB_PORT_FEAT_C_PORT_CONFIG_ERROR:
|
||||
xhci_clear_port_change_bit(xhci, wValue, wIndex,
|
||||
port->addr, temp);
|
||||
xhci_clear_port_change_bit(xhci, wValue, wIndex, port, temp);
|
||||
break;
|
||||
case USB_PORT_FEAT_ENABLE:
|
||||
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.
|
||||
* 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))
|
||||
status = 1;
|
||||
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 (i = 0; i < max_ports; i++) {
|
||||
temp = readl(ports[i]->addr);
|
||||
temp = xhci_portsc_readl(ports[i]);
|
||||
if (temp == ~(u32)0) {
|
||||
xhci_hc_died(xhci);
|
||||
retval = -ENODEV;
|
||||
@@ -1751,7 +1750,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
|
||||
u32 t1, t2;
|
||||
int retries = 10;
|
||||
retry:
|
||||
t1 = readl(ports[port_index]->addr);
|
||||
t1 = xhci_portsc_readl(ports[port_index]);
|
||||
t2 = xhci_port_state_to_neutral(t1);
|
||||
portsc_buf[port_index] = 0;
|
||||
|
||||
@@ -1829,7 +1828,7 @@ retry:
|
||||
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;
|
||||
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;
|
||||
|
||||
portsc = readl(port->addr);
|
||||
portsc = xhci_portsc_readl(port);
|
||||
|
||||
/* if any of these are set we are not stuck */
|
||||
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 */
|
||||
portsc &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS);
|
||||
portsc |= PORT_WR;
|
||||
writel(portsc, port->addr);
|
||||
xhci_portsc_writel(port, portsc);
|
||||
/* flush write */
|
||||
readl(port->addr);
|
||||
xhci_portsc_readl(port);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1912,7 +1911,7 @@ int xhci_bus_resume(struct usb_hcd *hcd)
|
||||
}
|
||||
port_index = max_ports;
|
||||
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 */
|
||||
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 */
|
||||
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 */
|
||||
@@ -1963,7 +1962,7 @@ int xhci_bus_resume(struct usb_hcd *hcd)
|
||||
|
||||
/* poll for U0 link state complete, both USB2 and USB3 */
|
||||
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);
|
||||
if (sret) {
|
||||
xhci_warn(xhci, "port %d-%d resume PLC timeout\n",
|
||||
|
||||
@@ -463,7 +463,7 @@ struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci,
|
||||
return NULL;
|
||||
|
||||
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)
|
||||
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 */
|
||||
if (tt_info->slot_id == slot_id) {
|
||||
/* 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];
|
||||
if (vdev && (vdev->tt_info == tt_info))
|
||||
xhci_free_virt_devices_depth_first(
|
||||
@@ -1344,7 +1344,7 @@ static u32 xhci_get_endpoint_mult(struct xhci_hcd *xhci,
|
||||
bool lec;
|
||||
|
||||
/* 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 */
|
||||
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);
|
||||
|
||||
/* Ensure host supports double isoc bandwidth for eUSB2 devices */
|
||||
if (usb_endpoint_is_hs_isoc_double(udev, ep) &&
|
||||
!HCC2_EUSB2_DIC(xhci->hcc_params2)) {
|
||||
if (usb_endpoint_is_hs_isoc_double(udev, ep) && !(xhci->hcc_params2 & HCC2_EUSB2_DIC)) {
|
||||
dev_dbg(&udev->dev, "Double Isoc Bandwidth not supported by xhci\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -1899,7 +1898,7 @@ EXPORT_SYMBOL_GPL(xhci_remove_secondary_interrupter);
|
||||
void xhci_mem_cleanup(struct xhci_hcd *xhci)
|
||||
{
|
||||
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
|
||||
int i, j, num_ports;
|
||||
int i, j;
|
||||
|
||||
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_cleanup_command_queue(xhci);
|
||||
|
||||
num_ports = HCS_MAX_PORTS(xhci->hcs_params1);
|
||||
for (i = 0; i < num_ports && xhci->rh_bw; i++) {
|
||||
for (i = 0; i < xhci->max_ports && xhci->rh_bw; i++) {
|
||||
struct xhci_interval_bw_table *bwt = &xhci->rh_bw[i].bw_table;
|
||||
for (j = 0; j < XHCI_MAX_INTERVAL; j++) {
|
||||
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);
|
||||
|
||||
dma_pool_destroy(xhci->segment_pool);
|
||||
@@ -1964,7 +1962,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
|
||||
if (!xhci->rh_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;
|
||||
list_for_each_entry_safe(tt, n, &xhci->rh_bw[i].tts, 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)
|
||||
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 ||
|
||||
xhci->hw_ports[i].hcd_portnum == DUPLICATE_ENTRY)
|
||||
continue;
|
||||
@@ -2188,32 +2186,28 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
|
||||
{
|
||||
void __iomem *base;
|
||||
u32 offset;
|
||||
unsigned int num_ports;
|
||||
int i, j;
|
||||
int cap_count = 0;
|
||||
u32 cap_start;
|
||||
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
|
||||
|
||||
num_ports = HCS_MAX_PORTS(xhci->hcs_params1);
|
||||
xhci->hw_ports = kcalloc_node(num_ports, sizeof(*xhci->hw_ports),
|
||||
xhci->hw_ports = kcalloc_node(xhci->max_ports, sizeof(*xhci->hw_ports),
|
||||
flags, dev_to_node(dev));
|
||||
if (!xhci->hw_ports)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < num_ports; i++) {
|
||||
xhci->hw_ports[i].addr = &xhci->op_regs->port_status_base +
|
||||
NUM_PORT_REGS * i;
|
||||
for (i = 0; i < xhci->max_ports; i++) {
|
||||
xhci->hw_ports[i].port_reg = &xhci->op_regs->port_regs[i];
|
||||
xhci->hw_ports[i].hw_portnum = i;
|
||||
|
||||
init_completion(&xhci->hw_ports[i].rexit_done);
|
||||
init_completion(&xhci->hw_ports[i].u3exit_done);
|
||||
}
|
||||
|
||||
xhci->rh_bw = kcalloc_node(num_ports, sizeof(*xhci->rh_bw), flags,
|
||||
dev_to_node(dev));
|
||||
xhci->rh_bw = kcalloc_node(xhci->max_ports, sizeof(*xhci->rh_bw), flags, dev_to_node(dev));
|
||||
if (!xhci->rh_bw)
|
||||
return -ENOMEM;
|
||||
for (i = 0; i < num_ports; i++) {
|
||||
for (i = 0; i < xhci->max_ports; i++) {
|
||||
struct xhci_interval_bw_table *bw_table;
|
||||
|
||||
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;
|
||||
|
||||
while (offset) {
|
||||
xhci_add_in_port(xhci, num_ports, base + offset, cap_count);
|
||||
if (xhci->usb2_rhub.num_ports + xhci->usb3_rhub.num_ports ==
|
||||
num_ports)
|
||||
xhci_add_in_port(xhci, xhci->max_ports, base + offset, cap_count);
|
||||
if (xhci->usb2_rhub.num_ports + xhci->usb3_rhub.num_ports == xhci->max_ports)
|
||||
break;
|
||||
offset = xhci_find_next_ext_cap(base, offset,
|
||||
XHCI_EXT_CAPS_PROTOCOL);
|
||||
|
||||
@@ -670,7 +670,6 @@ static int xhci_mtk_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
device_enable_async_suspend(dev);
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
pm_runtime_forbid(dev);
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
/* support at most 64 ep, use 32 size hash table */
|
||||
#define SCH_EP_HASH_BITS 5
|
||||
|
||||
/**
|
||||
/*
|
||||
* To simplify scheduler algorithm, set a upper limit for ESIT,
|
||||
* if a synchromous ep's ESIT is larger than @XHCI_MTK_MAX_ESIT,
|
||||
* 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)
|
||||
|
||||
/**
|
||||
* 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_in: save bandwidth used by FS/LS IN 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
|
||||
*
|
||||
@@ -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
|
||||
* @num_esit: number of @esit in a period
|
||||
@@ -77,6 +78,7 @@ struct mu3h_sch_bw_info {
|
||||
* @ep_type: endpoint type
|
||||
* @maxpkt: max packet size of endpoint
|
||||
* @ep: address of usb_host_endpoint struct
|
||||
* @speed: usb device speed
|
||||
* @allocated: the bandwidth is aready allocated from bus_bw
|
||||
* @offset: which uframe of the interval that transfer should be
|
||||
* scheduled first time within the interval
|
||||
@@ -125,7 +127,7 @@ struct mu3h_sch_ep_info {
|
||||
#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_sts1~2: ip power and clock status registers
|
||||
* @ip_xhci_cap: ip xHCI capability register
|
||||
|
||||
@@ -896,9 +896,9 @@ static int xhci_pci_poweroff_late(struct usb_hcd *hcd, bool do_wakeup)
|
||||
if (!(xhci->quirks & XHCI_RESET_TO_DEFAULT))
|
||||
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];
|
||||
portsc = readl(port->addr);
|
||||
portsc = xhci_portsc_readl(port);
|
||||
|
||||
if ((portsc & PORT_PLS_MASK) != XDEV_U3)
|
||||
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",
|
||||
port->rhub->hcd->self.busnum, port->hcd_portnum + 1);
|
||||
portsc = xhci_port_state_to_neutral(portsc);
|
||||
writel(portsc | PORT_PE, port->addr);
|
||||
xhci_portsc_writel(port, portsc | PORT_PE);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -144,9 +144,14 @@
|
||||
#define PORT_TEST_MODE_SHIFT 28
|
||||
|
||||
/* USB3 Protocol PORTLI Port Link Information */
|
||||
#define PORT_LEC(p) ((p) & 0xffff)
|
||||
#define PORT_RX_LANES(p) (((p) >> 16) & 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 */
|
||||
#define PORT_HIRDM(p)((p) & 3)
|
||||
#define PORT_L1_TIMEOUT(p)(((p) & 0xff) << 2)
|
||||
|
||||
@@ -82,6 +82,23 @@ dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg,
|
||||
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)
|
||||
{
|
||||
return TRB_TYPE_NOOP_LE32(trb->generic.field[3]);
|
||||
@@ -128,10 +145,10 @@ static void inc_td_cnt(struct urb *urb)
|
||||
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)) {
|
||||
/* unchain chained link TRBs */
|
||||
if (unchain_links)
|
||||
trb->link.control &= cpu_to_le32(~TRB_CHAIN);
|
||||
} else {
|
||||
trb->generic.field[0] = 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
|
||||
* TRB is in a new segment. This does not skip over link TRBs, and it does not
|
||||
* 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);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the suspect DMA address is a TRB in this TD, this function returns that
|
||||
* TRB's segment. Otherwise it returns 0.
|
||||
*/
|
||||
static struct xhci_segment *trb_in_td(struct xhci_td *td, dma_addr_t suspect_dma)
|
||||
static bool dma_in_range(dma_addr_t dma,
|
||||
struct xhci_segment *start_seg, union xhci_trb *start_trb,
|
||||
struct xhci_segment *end_seg, union xhci_trb *end_trb)
|
||||
{
|
||||
dma_addr_t start_dma;
|
||||
dma_addr_t end_seg_dma;
|
||||
dma_addr_t end_trb_dma;
|
||||
struct xhci_segment *cur_seg;
|
||||
unsigned int pos, start, end;
|
||||
struct xhci_segment *pos_seg;
|
||||
union xhci_trb *pos_trb = xhci_dma_to_trb(start_seg, dma, &pos_seg);
|
||||
|
||||
start_dma = xhci_trb_virt_to_dma(td->start_seg, td->start_trb);
|
||||
cur_seg = td->start_seg;
|
||||
/* Is the trb dma address even part of the whole ring? */
|
||||
if (!pos_trb)
|
||||
return false;
|
||||
|
||||
do {
|
||||
if (start_dma == 0)
|
||||
return NULL;
|
||||
/* 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);
|
||||
pos = trb_to_pos(pos_seg, pos_trb);
|
||||
start = trb_to_pos(start_seg, start_trb);
|
||||
end = trb_to_pos(end_seg, end_trb);
|
||||
|
||||
if (end_trb_dma > 0) {
|
||||
/* The end TRB is in this segment, so suspect should be here */
|
||||
if (start_dma <= end_trb_dma) {
|
||||
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;
|
||||
/* end position is smaller than start, search range wraps around */
|
||||
if (end < start)
|
||||
return !(pos > end && pos < start);
|
||||
|
||||
cur_seg = cur_seg->next;
|
||||
start_dma = xhci_trb_virt_to_dma(cur_seg, &cur_seg->trbs[0]);
|
||||
} while (cur_seg != td->start_seg);
|
||||
return (pos >= start && pos <= end);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
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
|
||||
@@ -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
|
||||
* 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;
|
||||
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) {
|
||||
trb_to_noop(trb, TRB_TR_NOOP);
|
||||
trb_to_noop(trb, TRB_TR_NOOP, unchain_links);
|
||||
|
||||
/* flip cycle if asked to */
|
||||
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",
|
||||
td->urb, cached_td->urb,
|
||||
td->urb->stream_id);
|
||||
td_to_noop(cached_td, false);
|
||||
td_to_noop(xhci, ep, cached_td, false);
|
||||
cached_td->cancel_status = TD_CLEARED;
|
||||
}
|
||||
td_to_noop(td, false);
|
||||
td_to_noop(xhci, ep, td, false);
|
||||
td->cancel_status = TD_CLEARING_CACHE;
|
||||
cached_td = td;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
td_to_noop(td, false);
|
||||
td_to_noop(xhci, ep, td, false);
|
||||
td->cancel_status = TD_CLEARED;
|
||||
}
|
||||
}
|
||||
@@ -1125,7 +1131,7 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep)
|
||||
continue;
|
||||
xhci_warn(xhci, "Failed to clear cancelled cached URB %p, mark clear anyway\n",
|
||||
td->urb);
|
||||
td_to_noop(td, false);
|
||||
td_to_noop(xhci, ep, td, false);
|
||||
td->cancel_status = TD_CLEARED;
|
||||
}
|
||||
}
|
||||
@@ -1388,7 +1394,7 @@ void xhci_hc_died(struct xhci_hcd *xhci)
|
||||
xhci_cleanup_command_queue(xhci);
|
||||
|
||||
/* 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])
|
||||
continue;
|
||||
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;
|
||||
u32 port_id;
|
||||
u32 portsc, cmd_reg;
|
||||
int max_ports;
|
||||
unsigned int hcd_portnum;
|
||||
struct xhci_bus_state *bus_state;
|
||||
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");
|
||||
|
||||
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",
|
||||
port_id);
|
||||
return;
|
||||
@@ -2030,7 +2034,7 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event)
|
||||
hcd = port->rhub->hcd;
|
||||
bus_state = &port->rhub->bus_state;
|
||||
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",
|
||||
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
|
||||
* 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 */
|
||||
if (comp_code == COMP_STALL_ERROR)
|
||||
return true;
|
||||
int ep_type = CTX_TO_EP_TYPE(le32_to_cpu(ep_ctx->ep_info2));
|
||||
|
||||
/* TRB completion codes that may require internal halt cleanup */
|
||||
if (comp_code == COMP_USB_TRANSACTION_ERROR ||
|
||||
comp_code == COMP_BABBLE_DETECTED_ERROR ||
|
||||
comp_code == COMP_SPLIT_TRANSACTION_ERROR)
|
||||
switch (comp_code) {
|
||||
case COMP_STALL_ERROR:
|
||||
/* on xHCI this always halts, including protocol stall */
|
||||
return true;
|
||||
case COMP_BABBLE_DETECTED_ERROR:
|
||||
/*
|
||||
* The 0.95 spec says a babbling control endpoint is not halted.
|
||||
* The 0.96 spec says it is. Some HW claims to be 0.95
|
||||
* compliant, but it halts the control endpoint anyway.
|
||||
* Check endpoint context if endpoint is halted.
|
||||
*/
|
||||
if (GET_EP_CTX_STATE(ep_ctx) == EP_STATE_HALTED)
|
||||
return true;
|
||||
if (xhci->hci_version <= 0x95 && ep_type == CTRL_EP)
|
||||
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;
|
||||
}
|
||||
@@ -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.
|
||||
*/
|
||||
return;
|
||||
case COMP_USB_TRANSACTION_ERROR:
|
||||
case COMP_BABBLE_DETECTED_ERROR:
|
||||
case COMP_SPLIT_TRANSACTION_ERROR:
|
||||
/*
|
||||
* 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:
|
||||
|
||||
if (xhci_halted_host_endpoint(xhci, ep_ctx, trb_comp_code)) {
|
||||
/*
|
||||
* xhci internal endpoint state will go to a "halt" state for
|
||||
* 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
|
||||
* 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_handle_halted_endpoint(xhci, ep, td, EP_HARD_RESET);
|
||||
|
||||
return; /* xhci_handle_halted_endpoint marked td cancelled */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
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:
|
||||
goto finish_td;
|
||||
default:
|
||||
if (!xhci_halted_host_endpoint(ep_ctx, trb_comp_code))
|
||||
if (!xhci_halted_host_endpoint(xhci, ep_ctx, trb_comp_code))
|
||||
break;
|
||||
xhci_dbg(xhci, "TRB error %u, halted endpoint index = %u\n",
|
||||
trb_comp_code, ep->ep_index);
|
||||
@@ -2663,7 +2640,6 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
||||
int ep_index;
|
||||
struct xhci_td *td = NULL;
|
||||
dma_addr_t ep_trb_dma;
|
||||
struct xhci_segment *ep_seg;
|
||||
union xhci_trb *ep_trb;
|
||||
int status = -EINPROGRESS;
|
||||
struct xhci_ep_ctx *ep_ctx;
|
||||
@@ -2694,6 +2670,9 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
||||
if (!ep_ring)
|
||||
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 */
|
||||
switch (trb_comp_code) {
|
||||
/* 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);
|
||||
|
||||
/* Is this a TRB in the currently executing TD? */
|
||||
ep_seg = trb_in_td(td, ep_trb_dma);
|
||||
|
||||
if (!ep_seg) {
|
||||
/* Is this TRB not part of the currently executing TD? */
|
||||
if (!trb_in_td(td, ep_trb_dma)) {
|
||||
|
||||
if (ep->skip && usb_endpoint_xfer_isoc(&td->urb->ep->desc)) {
|
||||
/* 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)
|
||||
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);
|
||||
|
||||
/*
|
||||
@@ -2978,7 +2954,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
||||
return 0;
|
||||
|
||||
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);
|
||||
|
||||
return 0;
|
||||
@@ -3990,6 +3966,16 @@ static unsigned int xhci_get_last_burst_packet_count(struct xhci_hcd *xhci,
|
||||
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
|
||||
* 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
|
||||
start_frame = (urb->start_frame + index * urb->interval) >> 3;
|
||||
|
||||
/* Isochronous Scheduling Threshold (IST, bits 0~3 in HCSPARAMS2):
|
||||
*
|
||||
* 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;
|
||||
ist = xhci_ist_microframes(xhci);
|
||||
|
||||
/* 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,
|
||||
@@ -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 */
|
||||
sia_frame_id = TRB_SIA;
|
||||
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);
|
||||
if (frame_id >= 0)
|
||||
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 */
|
||||
if (HCC_CFC(xhci->hcc_params))
|
||||
if (xhci->hcc_params & HCC_CFC)
|
||||
xep->next_frame_id = urb->start_frame + num_tds * urb->interval;
|
||||
|
||||
if (xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs == 0) {
|
||||
@@ -4273,7 +4249,7 @@ cleanup:
|
||||
*/
|
||||
urb_priv->td[0].end_trb = ep_ring->enqueue;
|
||||
/* 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. */
|
||||
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);
|
||||
|
||||
/* 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) {
|
||||
urb->start_frame = xep->next_frame_id;
|
||||
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
|
||||
* gets scheduled by hardare.
|
||||
*/
|
||||
ist = HCS_IST(xhci->hcs_params2) & 0x7;
|
||||
if (HCS_IST(xhci->hcs_params2) & (1 << 3))
|
||||
ist <<= 3;
|
||||
ist = xhci_ist_microframes(xhci);
|
||||
start_frame += ist + XHCI_CFC_DELAY;
|
||||
start_frame = roundup(start_frame, 8);
|
||||
|
||||
|
||||
@@ -1399,7 +1399,6 @@ static void tegra_xhci_id_work(struct work_struct *work)
|
||||
}
|
||||
|
||||
tegra_xhci_set_port_power(tegra, true, true);
|
||||
pm_runtime_mark_last_busy(tegra->dev);
|
||||
|
||||
} else {
|
||||
if (tegra->otg_usb3_port >= 0)
|
||||
@@ -2036,7 +2035,7 @@ static bool xhci_hub_ports_suspended(struct xhci_hub *hub)
|
||||
u32 value;
|
||||
|
||||
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)
|
||||
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))
|
||||
continue;
|
||||
|
||||
portsc = readl(rhub->ports[index]->addr);
|
||||
portsc = xhci_portsc_readl(rhub->ports[index]);
|
||||
speed = tegra_xhci_portsc_to_speed(tegra, portsc);
|
||||
tegra_xusb_padctl_enable_phy_sleepwalk(padctl, phy, speed);
|
||||
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++) {
|
||||
if (!xhci->usb2_rhub.ports[i])
|
||||
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);
|
||||
if (((portsc & PORT_PLS_MASK) == XDEV_U3) || ((portsc & DEV_SPEED_MASK) == XDEV_FS))
|
||||
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--) {
|
||||
if (!test_bit(i, &bus_state->resuming_ports))
|
||||
continue;
|
||||
portsc = readl(ports[i]->addr);
|
||||
portsc = xhci_portsc_readl(ports[i]);
|
||||
if ((portsc & PORT_PLS_MASK) == XDEV_RESUME)
|
||||
tegra_phy_xusb_utmi_pad_power_on(
|
||||
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)
|
||||
return -EPIPE;
|
||||
ports = rhub->ports;
|
||||
portsc = readl(ports[port]->addr);
|
||||
portsc = xhci_portsc_readl(ports[port]);
|
||||
if (portsc & PORT_CONNECT)
|
||||
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)) {
|
||||
ports = rhub->ports;
|
||||
portsc = readl(ports[port]->addr);
|
||||
portsc = xhci_portsc_readl(ports[port]);
|
||||
if (!(portsc & PORT_CONNECT)) {
|
||||
/* We don't suspend the PAD while HNP role swap happens on the OTG
|
||||
* port
|
||||
|
||||
@@ -71,29 +71,20 @@ DEFINE_EVENT(xhci_log_msg, xhci_dbg_ring_expansion,
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(xhci_log_ctx,
|
||||
TP_PROTO(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx,
|
||||
unsigned int ep_num),
|
||||
TP_ARGS(xhci, ctx, ep_num),
|
||||
TP_PROTO(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx),
|
||||
TP_ARGS(xhci, ctx),
|
||||
TP_STRUCT__entry(
|
||||
__field(int, ctx_64)
|
||||
__field(unsigned, ctx_type)
|
||||
__field(dma_addr_t, ctx_dma)
|
||||
__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(
|
||||
|
||||
__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_dma = ctx->dma;
|
||||
__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",
|
||||
__entry->ctx_64, __entry->ctx_type,
|
||||
@@ -102,9 +93,8 @@ DECLARE_EVENT_CLASS(xhci_log_ctx,
|
||||
);
|
||||
|
||||
DEFINE_EVENT(xhci_log_ctx, xhci_address_ctx,
|
||||
TP_PROTO(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx,
|
||||
unsigned int ep_num),
|
||||
TP_ARGS(xhci, ctx, ep_num)
|
||||
TP_PROTO(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx),
|
||||
TP_ARGS(xhci, ctx)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(xhci_log_trb,
|
||||
@@ -575,6 +565,11 @@ DEFINE_EVENT(xhci_log_portsc, xhci_hub_status_data,
|
||||
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,
|
||||
TP_PROTO(u32 slot, u32 doorbell),
|
||||
TP_ARGS(slot, doorbell),
|
||||
|
||||
@@ -41,6 +41,19 @@ static unsigned long long quirks;
|
||||
module_param(quirks, ullong, S_IRUGO);
|
||||
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)
|
||||
{
|
||||
struct xhci_segment *seg;
|
||||
@@ -237,7 +250,6 @@ static void xhci_zero_64b_regs(struct xhci_hcd *xhci)
|
||||
struct iommu_domain *domain;
|
||||
int err, i;
|
||||
u64 val;
|
||||
u32 intrs;
|
||||
|
||||
/*
|
||||
* 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))
|
||||
xhci_write_64(xhci, 0, &xhci->op_regs->cmd_ring);
|
||||
|
||||
intrs = min_t(u32, HCS_MAX_INTRS(xhci->hcs_params1),
|
||||
ARRAY_SIZE(xhci->run_regs->ir_set));
|
||||
|
||||
for (i = 0; i < intrs; i++) {
|
||||
for (i = 0; i < xhci->max_interrupters; i++) {
|
||||
struct xhci_intr_reg __iomem *ir;
|
||||
|
||||
ir = &xhci->run_regs->ir_set[i];
|
||||
@@ -373,7 +382,7 @@ static void compliance_mode_recovery(struct timer_list *t)
|
||||
return;
|
||||
|
||||
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) {
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
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",
|
||||
max_slots);
|
||||
xhci->max_slots);
|
||||
|
||||
config_reg = readl(&xhci->op_regs->config_reg);
|
||||
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",
|
||||
config_reg);
|
||||
@@ -896,7 +903,7 @@ static void xhci_disable_hub_port_wake(struct xhci_hcd *xhci,
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
|
||||
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);
|
||||
t2 = t1;
|
||||
|
||||
@@ -909,7 +916,7 @@ static void xhci_disable_hub_port_wake(struct xhci_hcd *xhci,
|
||||
t2 |= PORT_CSC;
|
||||
|
||||
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",
|
||||
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;
|
||||
ports = xhci->usb2_rhub.ports;
|
||||
while (port_index--) {
|
||||
portsc = readl(ports[port_index]->addr);
|
||||
portsc = xhci_portsc_readl(ports[port_index]);
|
||||
if (portsc & PORT_CHANGE_MASK ||
|
||||
(portsc & PORT_PLS_MASK) == XDEV_RESUME)
|
||||
return true;
|
||||
@@ -944,7 +951,7 @@ static bool xhci_pending_portevent(struct xhci_hcd *xhci)
|
||||
port_index = xhci->usb3_rhub.num_ports;
|
||||
ports = xhci->usb3_rhub.ports;
|
||||
while (port_index--) {
|
||||
portsc = readl(ports[port_index]->addr);
|
||||
portsc = xhci_portsc_readl(ports[port_index]);
|
||||
if (portsc & (PORT_CHANGE_MASK | PORT_CAS) ||
|
||||
(portsc & PORT_PLS_MASK) == XDEV_RESUME)
|
||||
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_trb_comp_code_string(command->status));
|
||||
xhci_err(xhci, "Max number of devices this xHCI host supports is %u.\n",
|
||||
HCS_MAX_SLOTS(
|
||||
readl(&xhci->cap_regs->hcs_params1)));
|
||||
xhci->max_slots);
|
||||
xhci_free_command(xhci, command);
|
||||
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->drop_flags = 0;
|
||||
|
||||
trace_xhci_address_ctx(xhci, virt_dev->in_ctx,
|
||||
le32_to_cpu(slot_ctx->dev_info) >> 27);
|
||||
trace_xhci_address_ctx(xhci, virt_dev->in_ctx);
|
||||
|
||||
trace_xhci_address_ctrl_ctx(ctrl_ctx);
|
||||
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,
|
||||
"ERROR: unexpected setup %s command completion code 0x%x.\n",
|
||||
act, command->status);
|
||||
trace_xhci_address_ctx(xhci, virt_dev->out_ctx, 1);
|
||||
trace_xhci_address_ctx(xhci, virt_dev->out_ctx);
|
||||
ret = -EINVAL;
|
||||
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,
|
||||
"Output Context DMA address = %#08llx",
|
||||
(unsigned long long)virt_dev->out_ctx->dma);
|
||||
trace_xhci_address_ctx(xhci, virt_dev->in_ctx,
|
||||
le32_to_cpu(slot_ctx->dev_info) >> 27);
|
||||
trace_xhci_address_ctx(xhci, virt_dev->in_ctx);
|
||||
/*
|
||||
* USB core uses address 1 for the roothubs, so we add one to the
|
||||
* address given back to us by the HC.
|
||||
*/
|
||||
trace_xhci_address_ctx(xhci, virt_dev->out_ctx,
|
||||
le32_to_cpu(slot_ctx->dev_info) >> 27);
|
||||
trace_xhci_address_ctx(xhci, virt_dev->out_ctx);
|
||||
/* Zero the input context control for later use */
|
||||
ctrl_ctx->add_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_port **ports;
|
||||
__le32 __iomem *pm_addr, *hlpm_addr;
|
||||
struct xhci_port_regs __iomem *port_reg;
|
||||
u32 pm_val, hlpm_val, field;
|
||||
unsigned int port_num;
|
||||
unsigned long flags;
|
||||
@@ -4662,9 +4665,8 @@ static int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
|
||||
|
||||
ports = xhci->usb2_rhub.ports;
|
||||
port_num = udev->portnum - 1;
|
||||
pm_addr = ports[port_num]->addr + PORTPMSC;
|
||||
pm_val = readl(pm_addr);
|
||||
hlpm_addr = ports[port_num]->addr + PORTHLPMC;
|
||||
port_reg = ports[port_num]->port_reg;
|
||||
pm_val = readl(&port_reg->portpmsc);
|
||||
|
||||
xhci_dbg(xhci, "%s port %d USB2 hardware LPM\n",
|
||||
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);
|
||||
|
||||
hlpm_val = xhci_calculate_usb2_hw_lpm_params(udev);
|
||||
writel(hlpm_val, hlpm_addr);
|
||||
writel(hlpm_val, &port_reg->porthlmpc);
|
||||
/* flush write */
|
||||
readl(hlpm_addr);
|
||||
readl(&port_reg->porthlmpc);
|
||||
} else {
|
||||
hird = xhci_calculate_hird_besl(xhci, udev);
|
||||
}
|
||||
|
||||
pm_val &= ~PORT_HIRD_MASK;
|
||||
pm_val |= PORT_HIRD(hird) | PORT_RWE | PORT_L1DS(udev->slot_id);
|
||||
writel(pm_val, pm_addr);
|
||||
pm_val = readl(pm_addr);
|
||||
writel(pm_val, &port_reg->portpmsc);
|
||||
pm_val = readl(&port_reg->portpmsc);
|
||||
pm_val |= PORT_HLE;
|
||||
writel(pm_val, pm_addr);
|
||||
writel(pm_val, &port_reg->portpmsc);
|
||||
/* flush write */
|
||||
readl(pm_addr);
|
||||
readl(&port_reg->portpmsc);
|
||||
} else {
|
||||
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 */
|
||||
readl(pm_addr);
|
||||
readl(&port_reg->portpmsc);
|
||||
if (udev->usb2_hw_lpm_besl_capable) {
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
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,
|
||||
100, 10000);
|
||||
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;
|
||||
int retval;
|
||||
u32 hcs_params1;
|
||||
|
||||
/* Accept arbitrarily long scatter-gather lists */
|
||||
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 +
|
||||
(readl(&xhci->cap_regs->run_regs_off) & RTSOFF_MASK);
|
||||
/* 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_params3 = readl(&xhci->cap_regs->hcs_params3);
|
||||
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)
|
||||
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 */
|
||||
if ((!xhci->max_interrupters) ||
|
||||
xhci->max_interrupters > HCS_MAX_INTRS(xhci->hcs_params1))
|
||||
xhci->max_interrupters = HCS_MAX_INTRS(xhci->hcs_params1);
|
||||
if (!xhci->max_interrupters)
|
||||
xhci->max_interrupters = min(HCS_MAX_INTRS(hcs_params1), MAX_HC_INTRS);
|
||||
else if (xhci->max_interrupters > HCS_MAX_INTRS(hcs_params1))
|
||||
xhci->max_interrupters = HCS_MAX_INTRS(hcs_params1);
|
||||
|
||||
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,
|
||||
* 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))) {
|
||||
xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n");
|
||||
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_cap_regs) != 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 */
|
||||
BUILD_BUG_ON(sizeof(struct xhci_run_regs) != (8+8*128)*32/8);
|
||||
/* xhci_run_regs has eight fields and embeds 1024 xhci_intr_regs */
|
||||
BUILD_BUG_ON(sizeof(struct xhci_run_regs) != (8+8*1024)*32/8);
|
||||
|
||||
if (usb_disabled())
|
||||
return -ENODEV;
|
||||
|
||||
@@ -34,8 +34,16 @@
|
||||
|
||||
/* Max number of USB devices for any host controller - limit in section 6.1 */
|
||||
#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
|
||||
/*
|
||||
* 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.
|
||||
@@ -66,13 +74,19 @@ struct xhci_cap_regs {
|
||||
/* Reserved up to (CAPLENGTH - 0x1C) */
|
||||
};
|
||||
|
||||
/* Number of registers per port */
|
||||
#define NUM_PORT_REGS 4
|
||||
|
||||
#define PORTSC 0
|
||||
#define PORTPMSC 1
|
||||
#define PORTLI 2
|
||||
#define PORTHLPMC 3
|
||||
/*
|
||||
* struct xhci_port_regs - Host Controller USB Port Register Set. xHCI spec 5.4.8
|
||||
* @portsc: Port Status and Control
|
||||
* @portpmsc: Port Power Management Status and Control
|
||||
* @portli: Port Link Info
|
||||
* @porthlmpc: Port Hardware LPM Control
|
||||
*/
|
||||
struct xhci_port_regs {
|
||||
__le32 portsc;
|
||||
__le32 portpmsc;
|
||||
__le32 portli;
|
||||
__le32 porthlmpc;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @dcbaa_ptr: DCBAAP - 64-bit Device Context Base Address Array Pointer
|
||||
* @config_reg: CONFIG - Configure Register
|
||||
* @port_status_base: PORTSCn - base address for Port Status and Control
|
||||
* 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.
|
||||
* @port_regs: Port Register Sets, from 1 to MaxPorts (defined by HCSPARAMS1).
|
||||
*/
|
||||
struct xhci_op_regs {
|
||||
__le32 command;
|
||||
@@ -110,13 +115,7 @@ struct xhci_op_regs {
|
||||
__le32 config_reg;
|
||||
/* rsvd: offset 0x3C-3FF */
|
||||
__le32 reserved4[241];
|
||||
/* port 1 registers, which serve as a base address for other ports */
|
||||
__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];
|
||||
struct xhci_port_regs port_regs[];
|
||||
};
|
||||
|
||||
/* USBCMD - USB command - command bitmasks */
|
||||
@@ -284,7 +283,7 @@ struct xhci_intr_reg {
|
||||
struct xhci_run_regs {
|
||||
__le32 microframe_index;
|
||||
__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 */
|
||||
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
|
||||
* might not be able to handle the maximum number of devices possible.
|
||||
@@ -1474,7 +1472,7 @@ struct xhci_port_cap {
|
||||
};
|
||||
|
||||
struct xhci_port {
|
||||
__le32 __iomem *addr;
|
||||
struct xhci_port_regs __iomem *port_reg;
|
||||
int hw_portnum;
|
||||
int hcd_portnum;
|
||||
struct xhci_hub *rhub;
|
||||
@@ -1510,7 +1508,6 @@ struct xhci_hcd {
|
||||
struct xhci_doorbell_array __iomem *dba;
|
||||
|
||||
/* Cached register copies of read-only HC data */
|
||||
__u32 hcs_params1;
|
||||
__u32 hcs_params2;
|
||||
__u32 hcs_params3;
|
||||
__u32 hcc_params;
|
||||
@@ -1521,6 +1518,8 @@ struct xhci_hcd {
|
||||
/* packed release number */
|
||||
u16 hci_version;
|
||||
u16 max_interrupters;
|
||||
u8 max_slots;
|
||||
u8 max_ports;
|
||||
/* imod_interval in ns (I * 250ns) */
|
||||
u32 imod_interval;
|
||||
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);
|
||||
int xhci_usb_endpoint_maxp(struct usb_device *udev,
|
||||
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 */
|
||||
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)
|
||||
return str;
|
||||
|
||||
ret += sprintf(str + ret, "%s %s %s Link:%s PortSpeed:%d ",
|
||||
portsc & PORT_POWER ? "Powered" : "Powered-off",
|
||||
portsc & PORT_CONNECT ? "Connected" : "Not-connected",
|
||||
portsc & PORT_PE ? "Enabled" : "Disabled",
|
||||
xhci_portsc_link_state_string(portsc),
|
||||
DEV_PORT_SPEED(portsc));
|
||||
ret += sprintf(str + ret, "Speed=%d ", DEV_PORT_SPEED(portsc));
|
||||
ret += sprintf(str + ret, "Link=%s ", xhci_portsc_link_state_string(portsc));
|
||||
|
||||
/* RO/ROS: Read-only */
|
||||
if (portsc & PORT_CONNECT)
|
||||
ret += sprintf(str + ret, "CCS ");
|
||||
if (portsc & PORT_OC)
|
||||
ret += sprintf(str + ret, "OverCurrent ");
|
||||
if (portsc & PORT_RESET)
|
||||
ret += sprintf(str + ret, "In-Reset ");
|
||||
ret += sprintf(str + ret, "OCA "); /* No set for USB2 ports */
|
||||
if (portsc & PORT_CAS)
|
||||
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)
|
||||
ret += sprintf(str + ret, "CSC ");
|
||||
if (portsc & PORT_PEC)
|
||||
ret += sprintf(str + ret, "PEC ");
|
||||
ret += sprintf(str + ret, "PEC "); /* No set for USB3 ports */
|
||||
if (portsc & PORT_WRC)
|
||||
ret += sprintf(str + ret, "WRC ");
|
||||
ret += sprintf(str + ret, "WRC "); /* RsvdZ for USB2 ports */
|
||||
if (portsc & PORT_OCC)
|
||||
ret += sprintf(str + ret, "OCC ");
|
||||
if (portsc & PORT_RC)
|
||||
@@ -2425,17 +2449,7 @@ static inline const char *xhci_decode_portsc(char *str, u32 portsc)
|
||||
if (portsc & PORT_PLC)
|
||||
ret += sprintf(str + ret, "PLC ");
|
||||
if (portsc & PORT_CEC)
|
||||
ret += sprintf(str + ret, "CEC ");
|
||||
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 ");
|
||||
ret += sprintf(str + ret, "CEC "); /* RsvdZ for USB2 ports */
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
@@ -134,7 +134,6 @@ static int apple_mfi_fc_set_property(struct power_supply *psy,
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
pm_runtime_mark_last_busy(&mfi->udev->dev);
|
||||
pm_runtime_put_autosuspend(&mfi->udev->dev);
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -444,9 +444,19 @@ static ssize_t chaoskey_read(struct file *file,
|
||||
goto bail;
|
||||
mutex_unlock(&dev->rng_lock);
|
||||
|
||||
if (file->f_flags & O_NONBLOCK) {
|
||||
result = mutex_trylock(&dev->lock);
|
||||
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) {
|
||||
result = _chaoskey_fill(dev);
|
||||
if (result < 0) {
|
||||
|
||||
@@ -164,28 +164,39 @@ struct ljca_match_ids_walk_data {
|
||||
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[] = {
|
||||
{ "INTC1074" },
|
||||
{ "INTC1096" },
|
||||
{ "INTC100B" },
|
||||
{ "INTC10D1" },
|
||||
{ "INTC10B5" },
|
||||
{ "INTC100B" }, /* RPL LJCA GPIO */
|
||||
{ "INTC1074" }, /* CVF LJCA GPIO */
|
||||
{ "INTC1096" }, /* ADL LJCA GPIO */
|
||||
{ "INTC10B5" }, /* LNL LJCA GPIO */
|
||||
{ "INTC10D1" }, /* MTL (CVF VSC) USBIO GPIO [1] */
|
||||
{},
|
||||
};
|
||||
|
||||
static const struct acpi_device_id ljca_i2c_hids[] = {
|
||||
{ "INTC1075" },
|
||||
{ "INTC1097" },
|
||||
{ "INTC100C" },
|
||||
{ "INTC10D2" },
|
||||
{ "INTC100C" }, /* RPL LJCA I2C */
|
||||
{ "INTC1075" }, /* CVF LJCA I2C */
|
||||
{ "INTC1097" }, /* ADL LJCA I2C */
|
||||
{ "INTC10D2" }, /* MTL (CVF VSC) USBIO I2C [1] */
|
||||
{},
|
||||
};
|
||||
|
||||
static const struct acpi_device_id ljca_spi_hids[] = {
|
||||
{ "INTC1091" },
|
||||
{ "INTC1098" },
|
||||
{ "INTC100D" },
|
||||
{ "INTC10D3" },
|
||||
{ "INTC100D" }, /* RPL LJCA SPI */
|
||||
{ "INTC1091" }, /* TGL/ADL LJCA SPI */
|
||||
{ "INTC1098" }, /* ADL LJCA SPI */
|
||||
{ "INTC10D3" }, /* MTL (CVF VSC) USBIO SPI [1] */
|
||||
{},
|
||||
};
|
||||
|
||||
@@ -891,7 +902,7 @@ static struct 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("Lixu Zhang <lixu.zhang@intel.com>");
|
||||
MODULE_DESCRIPTION("Intel La Jolla Cove Adapter USB driver");
|
||||
|
||||
@@ -65,7 +65,7 @@ struct mtu3_request;
|
||||
#define MTU3_U3_IP_SLOT_DEFAULT 2
|
||||
#define MTU3_U2_IP_SLOT_DEFAULT 1
|
||||
|
||||
/**
|
||||
/*
|
||||
* IP TRUNK version
|
||||
* from 0x1003 version, USB3 Gen2 is supported, two changes affect driver:
|
||||
* 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
|
||||
|
||||
/**
|
||||
/*
|
||||
* 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
|
||||
* up to 64KB fifo size.
|
||||
* 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_U2_IP_EP0_FIFO_SIZE 64
|
||||
|
||||
/**
|
||||
/*
|
||||
* Maximum size of ep0 response buffer for ch9 requests,
|
||||
* 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
|
||||
* without 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.
|
||||
* 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.
|
||||
* MTU3_DR_FORCE_DEVICE: force to enter peripheral mode.
|
||||
* @MTU3_DR_FORCE_DEVICE: force to enter peripheral mode.
|
||||
*/
|
||||
enum mtu3_dr_force_mode {
|
||||
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
|
||||
* @limit: the bitmap size in bits
|
||||
* @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.
|
||||
* And the size of GPD is 16 bytes.
|
||||
*
|
||||
@@ -179,11 +182,13 @@ struct qmu_gpd {
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* dma: physical base address of GPD segment
|
||||
* start: virtual base address of GPD segment
|
||||
* end: the last GPD element
|
||||
* enqueue: the first empty GPD to use
|
||||
* dequeue: the first completed GPD serviced by ISR
|
||||
* struct mtu3_gpd_ring - GPD ring descriptor
|
||||
* @dma: physical base address of GPD segment
|
||||
* @start: virtual base address of GPD segment
|
||||
* @end: the last GPD element
|
||||
* @enqueue: the first empty GPD to use
|
||||
* @dequeue: the first completed GPD serviced by ISR
|
||||
*
|
||||
* NOTE: the size of GPD ring should be >= 2
|
||||
*/
|
||||
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
|
||||
* @edev: external connector used to detect vbus and iddig changes
|
||||
* @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
|
||||
* @ippc_base: register base address of IP Power and Clock interface (IPPC)
|
||||
* @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_seg_size: it is roundup_pow_of_two(@maxp)
|
||||
*/
|
||||
|
||||
@@ -290,7 +290,7 @@ static void mtu3_csr_init(struct mtu3 *mtu)
|
||||
|
||||
/* delay about 0.1us from detecting reset to send chirp-K */
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -431,7 +431,6 @@ static int mtu3_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
device_enable_async_suspend(dev);
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
pm_runtime_forbid(dev);
|
||||
|
||||
|
||||
@@ -221,7 +221,7 @@ static struct qmu_gpd *advance_deq_gpd(struct mtu3_gpd_ring *ring)
|
||||
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)
|
||||
{
|
||||
struct qmu_gpd *enq = ring->enqueue;
|
||||
|
||||
@@ -2031,7 +2031,6 @@ static void musb_pm_runtime_check_session(struct musb *musb)
|
||||
if (!musb->session)
|
||||
break;
|
||||
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);
|
||||
musb->session = false;
|
||||
return;
|
||||
@@ -2063,7 +2062,6 @@ static void musb_pm_runtime_check_session(struct musb *musb)
|
||||
msecs_to_jiffies(3000));
|
||||
} else {
|
||||
trace_musb_state(musb, devctl, "Allow PM with no session");
|
||||
pm_runtime_mark_last_busy(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");
|
||||
}
|
||||
|
||||
pm_runtime_mark_last_busy(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->is_initialized = 1;
|
||||
pm_runtime_mark_last_busy(musb->controller);
|
||||
pm_runtime_put_autosuspend(musb->controller);
|
||||
|
||||
return 0;
|
||||
@@ -2887,7 +2883,6 @@ static int musb_resume(struct device *dev)
|
||||
error);
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -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);
|
||||
return 0;
|
||||
}
|
||||
@@ -119,7 +118,6 @@ static int musb_test_mode_show(struct seq_file *s, void *unused)
|
||||
|
||||
pm_runtime_get_sync(musb->controller);
|
||||
test = musb_readb(musb->mregs, MUSB_TESTMODE);
|
||||
pm_runtime_mark_last_busy(musb->controller);
|
||||
pm_runtime_put_autosuspend(musb->controller);
|
||||
|
||||
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);
|
||||
|
||||
ret:
|
||||
pm_runtime_mark_last_busy(musb->controller);
|
||||
pm_runtime_put_autosuspend(musb->controller);
|
||||
return count;
|
||||
}
|
||||
@@ -243,7 +240,6 @@ static int musb_softconnect_show(struct seq_file *s, void *unused)
|
||||
reg = musb_readb(musb->mregs, MUSB_DEVCTL);
|
||||
connect = reg & MUSB_DEVCTL_SESSION ? 1 : 0;
|
||||
|
||||
pm_runtime_mark_last_busy(musb->controller);
|
||||
pm_runtime_put_autosuspend(musb->controller);
|
||||
break;
|
||||
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);
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -296,7 +296,6 @@ static void otg_timer(struct timer_list *t)
|
||||
if (err < 0)
|
||||
dev_err(dev, "%s resume work: %i\n", __func__, err);
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
}
|
||||
|
||||
|
||||
@@ -1258,7 +1258,6 @@ static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req,
|
||||
|
||||
unlock:
|
||||
spin_unlock_irqrestore(&musb->lock, lockflags);
|
||||
pm_runtime_mark_last_busy(musb->controller);
|
||||
pm_runtime_put_autosuspend(musb->controller);
|
||||
|
||||
return status;
|
||||
@@ -1642,7 +1641,6 @@ static void musb_gadget_work(struct work_struct *work)
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
musb_pullup(musb, musb->softconnect);
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
pm_runtime_mark_last_busy(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)
|
||||
musb_platform_set_vbus(musb, 1);
|
||||
|
||||
pm_runtime_mark_last_busy(musb->controller);
|
||||
pm_runtime_put_autosuspend(musb->controller);
|
||||
|
||||
return 0;
|
||||
@@ -1916,7 +1913,6 @@ static int musb_gadget_stop(struct usb_gadget *g)
|
||||
usb_gadget_set_state(g, USB_STATE_NOTATTACHED);
|
||||
|
||||
/* Force check of devctl register for PM runtime */
|
||||
pm_runtime_mark_last_busy(musb->controller);
|
||||
pm_runtime_put_autosuspend(musb->controller);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -151,7 +151,6 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue)
|
||||
default:
|
||||
dev_dbg(musb->controller, "ID float\n");
|
||||
}
|
||||
pm_runtime_mark_last_busy(musb->controller);
|
||||
pm_runtime_put_autosuspend(musb->controller);
|
||||
atomic_notifier_call_chain(&musb->xceiv->notifier,
|
||||
musb->xceiv->last_event, NULL);
|
||||
|
||||
@@ -646,6 +646,8 @@ int usb_add_phy(struct usb_phy *x, enum usb_phy_type type)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&x->head);
|
||||
|
||||
usb_charger_init(x);
|
||||
ret = usb_add_extcon(x);
|
||||
if (ret)
|
||||
@@ -696,6 +698,8 @@ int usb_add_phy_dev(struct usb_phy *x)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&x->head);
|
||||
|
||||
usb_charger_init(x);
|
||||
ret = usb_add_extcon(x);
|
||||
if (ret)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user