USB/Thunderbolt changes for 6.16-rc1
Here is the big set of USB and Thunderbolt changes for 6.16-rc1. Included in here are the following: - USB offload support for audio devices. I think this takes the record for the most number of patch series (30+) over the longest period of time (2+ years) to get merged properly. Many props go to Wesley Cheng for seeing this effort through, they took a major out-of-tree hacked-up-monstrosity that was created by multiple vendors for their specific devices, got it all merged into a semi-coherent set of changes, and got all of the different major subsystems to agree on how this should be implemented both with changes to their code as well as userspace apis, AND wrangled the hardware companies into agreeing to go forward with this, despite making them all redo work they had already done in their private device trees. This feature offers major power savings on embedded devices where a USB audio stream can continue to flow while the rest of the system is sleeping, something that devices running on battery power really care about. There are still some more small tweaks left to be done here, and those patches are still out for review and arguing among the different hardware companies, but this is a major step forward and a great example of how to do upstream development well. - small number of thunderbolt fixes and updates, things seem to be slowing down here (famous last words...) - xhci refactors and reworking to try to handle some rough corner cases in some hardware implementations where things don't always work properly - typec driver updates - USB3 power management reworking and updates - Removal of some old and orphaned UDC gadget drivers that had not been used in a very long time, dropping over 11 thousand lines from the tree, always a nice thing, making up for the 12k lines added for the USB offload feature. - lots of little updates and fixes in different drivers All of these have been in linux-next for over 2 weeks, the USB offload logic has been in there for 8 weeks now, with no reported issues Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCaEKnpA8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+ym3zQCgz+Oz3DHfLcYOX7evGI2PjI4GNMkAoJa6Kndw h4YgL+8MeBpKuHCQvxy8 =KVAn -----END PGP SIGNATURE----- Merge tag 'usb-6.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb Pull USB / Thunderbolt updates from Greg KH: "Here is the big set of USB and Thunderbolt changes for 6.16-rc1. Included in here are the following: - USB offload support for audio devices. I think this takes the record for the most number of patch series (30+) over the longest period of time (2+ years) to get merged properly. Many props go to Wesley Cheng for seeing this effort through, they took a major out-of-tree hacked-up-monstrosity that was created by multiple vendors for their specific devices, got it all merged into a semi-coherent set of changes, and got all of the different major subsystems to agree on how this should be implemented both with changes to their code as well as userspace apis, AND wrangled the hardware companies into agreeing to go forward with this, despite making them all redo work they had already done in their private device trees. This feature offers major power savings on embedded devices where a USB audio stream can continue to flow while the rest of the system is sleeping, something that devices running on battery power really care about. There are still some more small tweaks left to be done here, and those patches are still out for review and arguing among the different hardware companies, but this is a major step forward and a great example of how to do upstream development well. - small number of thunderbolt fixes and updates, things seem to be slowing down here (famous last words...) - xhci refactors and reworking to try to handle some rough corner cases in some hardware implementations where things don't always work properly - typec driver updates - USB3 power management reworking and updates - Removal of some old and orphaned UDC gadget drivers that had not been used in a very long time, dropping over 11 thousand lines from the tree, always a nice thing, making up for the 12k lines added for the USB offload feature. - lots of little updates and fixes in different drivers All of these have been in linux-next for over 2 weeks, the USB offload logic has been in there for 8 weeks now, with no reported issues" * tag 'usb-6.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (172 commits) ALSA: usb-audio: qcom: fix USB_XHCI dependency ASoC: qdsp6: fix compile-testing without CONFIG_OF usb: misc: onboard_usb_dev: fix build warning for CONFIG_USB_ONBOARD_DEV_USB5744=n usb: typec: tipd: fix typo in TPS_STATUS_HIGH_VOLAGE_WARNING macro USB: typec: fix const issue in typec_match() USB: gadget: udc: fix const issue in gadget_match_driver() USB: gadget: fix up const issue with struct usb_function_instance USB: serial: pl2303: add new chip PL2303GC-Q20 and PL2303GT-2AB USB: serial: bus: fix const issue in usb_serial_device_match() usb: usbtmc: Fix timeout value in get_stb usb: usbtmc: Fix read_stb function and get_stb ioctl ALSA: qc_audio_offload: try to reduce address space confusion ALSA: qc_audio_offload: avoid leaking xfer_buf allocation ALSA: qc_audio_offload: rename dma/iova/va/cpu/phys variables ALSA: usb-audio: qcom: Fix an error handling path in qc_usb_audio_probe() usb: misc: onboard_usb_dev: Fix usb5744 initialization sequence dt-bindings: usb: ti,usb8041: Add binding for TI USB8044 hub controller usb: misc: onboard_usb_dev: Add support for TI TUSB8044 hub usb: gadget: lpc32xx_udc: Use USB API functions rather than constants usb: gadget: epautoconf: Use USB API functions rather than constants ...
This commit is contained in:
commit
c0c9379f23
|
@ -296,6 +296,39 @@ information is missing.
|
|||
To recover from this mode, one needs to flash a valid NVM image to the
|
||||
host controller in the same way it is done in the previous chapter.
|
||||
|
||||
Tunneling events
|
||||
----------------
|
||||
The driver sends ``KOBJ_CHANGE`` events to userspace when there is a
|
||||
tunneling change in the ``thunderbolt_domain``. The notification carries
|
||||
following environment variables::
|
||||
|
||||
TUNNEL_EVENT=<EVENT>
|
||||
TUNNEL_DETAILS=0:12 <-> 1:20 (USB3)
|
||||
|
||||
Possible values for ``<EVENT>`` are:
|
||||
|
||||
activated
|
||||
The tunnel was activated (created).
|
||||
|
||||
changed
|
||||
There is a change in this tunnel. For example bandwidth allocation was
|
||||
changed.
|
||||
|
||||
deactivated
|
||||
The tunnel was torn down.
|
||||
|
||||
low bandwidth
|
||||
The tunnel is not getting optimal bandwidth.
|
||||
|
||||
insufficient bandwidth
|
||||
There is not enough bandwidth for the current tunnel requirements.
|
||||
|
||||
The ``TUNNEL_DETAILS`` is only provided if the tunnel is known. For
|
||||
example, in case of Firmware Connection Manager this is missing or does
|
||||
not provide full tunnel information. In case of Software Connection Manager
|
||||
this includes full tunnel details. The format currently matches what the
|
||||
driver uses when logging. This may change over time.
|
||||
|
||||
Networking over Thunderbolt cable
|
||||
---------------------------------
|
||||
Thunderbolt technology allows software communication between two hosts
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/net/wireless/realtek,rtl8188e.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Realtek RTL8188E USB WiFi
|
||||
|
||||
maintainers:
|
||||
- J. Neuschäfer <j.ne@posteo.net>
|
||||
|
||||
description:
|
||||
Realtek RTL8188E is a family of USB-connected 2.4 GHz WiFi modules.
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/usb/usb-device.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: usbbda,179 # RTL8188ETV
|
||||
|
||||
reg: true
|
||||
|
||||
vdd-supply:
|
||||
description:
|
||||
Regulator for the 3V3 supply.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- vdd-supply
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
usb {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
wifi: wifi@1 {
|
||||
compatible = "usbbda,179";
|
||||
reg = <1>;
|
||||
vdd-supply = <&vcc3v3>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
|
@ -193,4 +193,19 @@ examples:
|
|||
sound-dai = <&vamacro 0>;
|
||||
};
|
||||
};
|
||||
|
||||
usb-dai-link {
|
||||
link-name = "USB Playback";
|
||||
cpu {
|
||||
sound-dai = <&q6afedai USB_RX>;
|
||||
};
|
||||
|
||||
codec {
|
||||
sound-dai = <&usbdai USB_RX>;
|
||||
};
|
||||
|
||||
platform {
|
||||
sound-dai = <&q6routing>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -42,6 +42,9 @@ properties:
|
|||
|
||||
phy_type: true
|
||||
|
||||
iommus:
|
||||
maxItems: 1
|
||||
|
||||
itc-setting:
|
||||
description:
|
||||
interrupt threshold control register control, the setting should be
|
||||
|
|
|
@ -41,6 +41,7 @@ properties:
|
|||
- fsl,imx8mm-usb
|
||||
- fsl,imx8mn-usb
|
||||
- fsl,imx93-usb
|
||||
- fsl,imx95-usb
|
||||
- const: fsl,imx7d-usb
|
||||
- const: fsl,imx27-usb
|
||||
- items:
|
||||
|
@ -54,7 +55,11 @@ properties:
|
|||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
minItems: 1
|
||||
items:
|
||||
- description: USB controller interrupt or combine USB controller
|
||||
and wakeup interrupts.
|
||||
- description: Wakeup interrupt
|
||||
|
||||
clocks:
|
||||
minItems: 1
|
||||
|
@ -191,6 +196,7 @@ allOf:
|
|||
contains:
|
||||
enum:
|
||||
- fsl,imx93-usb
|
||||
- fsl,imx95-usb
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
|
@ -238,6 +244,22 @@ allOf:
|
|||
maxItems: 1
|
||||
clock-names: false
|
||||
|
||||
# imx95 soc use two interrupts
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- fsl,imx95-usb
|
||||
then:
|
||||
properties:
|
||||
interrupts:
|
||||
minItems: 2
|
||||
else:
|
||||
properties:
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
|
|
|
@ -34,6 +34,7 @@ properties:
|
|||
- fsl,imx8mm-usbmisc
|
||||
- fsl,imx8mn-usbmisc
|
||||
- fsl,imx8ulp-usbmisc
|
||||
- fsl,imx95-usbmisc
|
||||
- const: fsl,imx7d-usbmisc
|
||||
- const: fsl,imx6q-usbmisc
|
||||
- items:
|
||||
|
@ -45,7 +46,10 @@ properties:
|
|||
maxItems: 1
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
minItems: 1
|
||||
items:
|
||||
- description: Base and length of the Wrapper module register
|
||||
- description: Base and length of the HSIO Block Control register
|
||||
|
||||
'#index-cells':
|
||||
const: 1
|
||||
|
@ -56,6 +60,23 @@ required:
|
|||
- compatible
|
||||
- reg
|
||||
|
||||
allOf:
|
||||
# imx95 soc needs use HSIO Block Control
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- fsl,imx95-usbmisc
|
||||
then:
|
||||
properties:
|
||||
reg:
|
||||
minItems: 2
|
||||
else:
|
||||
properties:
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
|
|
|
@ -86,6 +86,7 @@ properties:
|
|||
- nuvoton,npcm845-ehci
|
||||
- ti,ehci-omap
|
||||
- usb-ehci
|
||||
- via,vt8500-ehci
|
||||
|
||||
reg:
|
||||
minItems: 1
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/usb/parade,ps5511.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Parade PS5511 4+1 Port USB 3.2 Gen 1 Hub Controller
|
||||
|
||||
maintainers:
|
||||
- Pin-yen Lin <treapking@chromium.org>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- usb1da0,5511
|
||||
- usb1da0,55a1
|
||||
|
||||
reset-gpios:
|
||||
items:
|
||||
- description: GPIO specifier for RESETB pin.
|
||||
|
||||
vddd11-supply:
|
||||
description:
|
||||
1V1 power supply to the hub
|
||||
|
||||
vdd33-supply:
|
||||
description:
|
||||
3V3 power supply to the hub
|
||||
|
||||
peer-hub: true
|
||||
|
||||
ports:
|
||||
$ref: /schemas/graph.yaml#/properties/ports
|
||||
|
||||
patternProperties:
|
||||
'^port@':
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
|
||||
properties:
|
||||
reg:
|
||||
minimum: 1
|
||||
maximum: 5
|
||||
|
||||
additionalProperties:
|
||||
properties:
|
||||
reg:
|
||||
minimum: 1
|
||||
maximum: 5
|
||||
|
||||
required:
|
||||
- peer-hub
|
||||
|
||||
allOf:
|
||||
- $ref: usb-hub.yaml#
|
||||
- if:
|
||||
not:
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- usb1da0,55a1
|
||||
then:
|
||||
properties:
|
||||
ports:
|
||||
properties:
|
||||
port@5: false
|
||||
|
||||
patternProperties:
|
||||
'^.*@5$': false
|
||||
|
||||
examples:
|
||||
- |
|
||||
usb {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
/* 2.0 hub on port 1 */
|
||||
hub_2_0: hub@1 {
|
||||
compatible = "usb1da0,55a1";
|
||||
reg = <1>;
|
||||
peer-hub = <&hub_3_0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
/* USB 2.0 device on port 5 */
|
||||
device@5 {
|
||||
reg = <5>;
|
||||
compatible = "usb123,4567";
|
||||
};
|
||||
};
|
||||
|
||||
/* 3.0 hub on port 2 */
|
||||
hub_3_0: hub@2 {
|
||||
compatible = "usb1da0,5511";
|
||||
reg = <2>;
|
||||
peer-hub = <&hub_2_0>;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
/* Type-A connector on port 3 */
|
||||
port@3 {
|
||||
reg = <3>;
|
||||
endpoint {
|
||||
remote-endpoint = <&usb_a0_ss>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
|
@ -11,8 +11,11 @@ maintainers:
|
|||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- parade,ps8830
|
||||
oneOf:
|
||||
- items:
|
||||
- const: parade,ps8833
|
||||
- const: parade,ps8830
|
||||
- const: parade,ps8830
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
|
|
@ -4,11 +4,22 @@
|
|||
$id: http://devicetree.org/schemas/usb/qcom,dwc3.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm SuperSpeed DWC3 USB SoC controller
|
||||
title: Legacy Qualcomm SuperSpeed DWC3 USB SoC controller
|
||||
|
||||
maintainers:
|
||||
- Wesley Cheng <quic_wcheng@quicinc.com>
|
||||
|
||||
# Use the combined qcom,snps-dwc3 instead
|
||||
deprecated: true
|
||||
|
||||
select:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: qcom,dwc3
|
||||
required:
|
||||
- compatible
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
|
@ -55,6 +66,7 @@ properties:
|
|||
- qcom,sm8450-dwc3
|
||||
- qcom,sm8550-dwc3
|
||||
- qcom,sm8650-dwc3
|
||||
- qcom,sm8750-dwc3
|
||||
- qcom,x1e80100-dwc3
|
||||
- qcom,x1e80100-dwc3-mp
|
||||
- const: qcom,dwc3
|
||||
|
@ -354,6 +366,7 @@ allOf:
|
|||
- qcom,sm8450-dwc3
|
||||
- qcom,sm8550-dwc3
|
||||
- qcom,sm8650-dwc3
|
||||
- qcom,sm8750-dwc3
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
|
@ -497,6 +510,7 @@ allOf:
|
|||
- qcom,sm8450-dwc3
|
||||
- qcom,sm8550-dwc3
|
||||
- qcom,sm8650-dwc3
|
||||
- qcom,sm8750-dwc3
|
||||
then:
|
||||
properties:
|
||||
interrupts:
|
||||
|
|
|
@ -0,0 +1,622 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/usb/qcom,snps-dwc3.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm SuperSpeed DWC3 USB SoC controller
|
||||
|
||||
maintainers:
|
||||
- Wesley Cheng <quic_wcheng@quicinc.com>
|
||||
|
||||
description:
|
||||
Describes the Qualcomm USB block, based on Synopsys DWC3.
|
||||
|
||||
select:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: qcom,snps-dwc3
|
||||
required:
|
||||
- compatible
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- qcom,ipq4019-dwc3
|
||||
- qcom,ipq5018-dwc3
|
||||
- qcom,ipq5332-dwc3
|
||||
- qcom,ipq5424-dwc3
|
||||
- qcom,ipq6018-dwc3
|
||||
- qcom,ipq8064-dwc3
|
||||
- qcom,ipq8074-dwc3
|
||||
- qcom,ipq9574-dwc3
|
||||
- qcom,msm8953-dwc3
|
||||
- qcom,msm8994-dwc3
|
||||
- qcom,msm8996-dwc3
|
||||
- qcom,msm8998-dwc3
|
||||
- qcom,qcm2290-dwc3
|
||||
- qcom,qcs404-dwc3
|
||||
- qcom,qcs615-dwc3
|
||||
- qcom,qcs8300-dwc3
|
||||
- qcom,qdu1000-dwc3
|
||||
- qcom,sa8775p-dwc3
|
||||
- qcom,sar2130p-dwc3
|
||||
- qcom,sc7180-dwc3
|
||||
- qcom,sc7280-dwc3
|
||||
- qcom,sc8180x-dwc3
|
||||
- qcom,sc8180x-dwc3-mp
|
||||
- qcom,sc8280xp-dwc3
|
||||
- qcom,sc8280xp-dwc3-mp
|
||||
- qcom,sdm660-dwc3
|
||||
- qcom,sdm670-dwc3
|
||||
- qcom,sdm845-dwc3
|
||||
- qcom,sdx55-dwc3
|
||||
- qcom,sdx65-dwc3
|
||||
- qcom,sdx75-dwc3
|
||||
- qcom,sm4250-dwc3
|
||||
- qcom,sm6115-dwc3
|
||||
- qcom,sm6125-dwc3
|
||||
- qcom,sm6350-dwc3
|
||||
- qcom,sm6375-dwc3
|
||||
- qcom,sm8150-dwc3
|
||||
- qcom,sm8250-dwc3
|
||||
- qcom,sm8350-dwc3
|
||||
- qcom,sm8450-dwc3
|
||||
- qcom,sm8550-dwc3
|
||||
- qcom,sm8650-dwc3
|
||||
- qcom,x1e80100-dwc3
|
||||
- const: qcom,snps-dwc3
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
required-opps:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
description: |
|
||||
Several clocks are used, depending on the variant. Typical ones are::
|
||||
- cfg_noc:: System Config NOC clock.
|
||||
- core:: Master/Core clock, has to be >= 125 MHz for SS operation and >=
|
||||
60MHz for HS operation.
|
||||
- iface:: System bus AXI clock.
|
||||
- sleep:: Sleep clock, used for wakeup when USB3 core goes into low
|
||||
power mode (U3).
|
||||
- mock_utmi:: Mock utmi clock needed for ITP/SOF generation in host
|
||||
mode. Its frequency should be 19.2MHz.
|
||||
minItems: 1
|
||||
maxItems: 9
|
||||
|
||||
clock-names:
|
||||
minItems: 1
|
||||
maxItems: 9
|
||||
|
||||
dma-coherent: true
|
||||
|
||||
iommus:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
interconnects:
|
||||
maxItems: 2
|
||||
|
||||
interconnect-names:
|
||||
items:
|
||||
- const: usb-ddr
|
||||
- const: apps-usb
|
||||
|
||||
interrupts:
|
||||
description: |
|
||||
Different types of interrupts are used based on HS PHY used on target:
|
||||
- dwc_usb3: Core DWC3 interrupt
|
||||
- pwr_event: Used for wakeup based on other power events.
|
||||
- hs_phy_irq: Apart from DP/DM/QUSB2 PHY interrupts, there is
|
||||
hs_phy_irq which is not triggered by default and its
|
||||
functionality is mutually exclusive to that of
|
||||
{dp/dm}_hs_phy_irq and qusb2_phy_irq.
|
||||
- qusb2_phy: SoCs with QUSB2 PHY do not have separate DP/DM IRQs and
|
||||
expose only a single IRQ whose behavior can be modified
|
||||
by the QUSB2PHY_INTR_CTRL register. The required DPSE/
|
||||
DMSE configuration is done in QUSB2PHY_INTR_CTRL register
|
||||
of PHY address space.
|
||||
- {dp/dm}_hs_phy_irq: These IRQ's directly reflect changes on the DP/
|
||||
DM pads of the SoC. These are used for wakeup
|
||||
only on SoCs with non-QUSB2 targets with
|
||||
exception of SDM670/SDM845/SM6350.
|
||||
- ss_phy_irq: Used for remote wakeup in Super Speed mode of operation.
|
||||
minItems: 3
|
||||
maxItems: 19
|
||||
|
||||
interrupt-names:
|
||||
minItems: 3
|
||||
maxItems: 19
|
||||
|
||||
qcom,select-utmi-as-pipe-clk:
|
||||
description:
|
||||
If present, disable USB3 pipe_clk requirement.
|
||||
Used when dwc3 operates without SSPHY and only
|
||||
HS/FS/LS modes are supported.
|
||||
type: boolean
|
||||
|
||||
wakeup-source: true
|
||||
|
||||
# Required child node:
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- clock-names
|
||||
- interrupts
|
||||
- interrupt-names
|
||||
|
||||
allOf:
|
||||
- $ref: snps,dwc3-common.yaml#
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,ipq4019-dwc3
|
||||
- qcom,ipq5332-dwc3
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 3
|
||||
clock-names:
|
||||
items:
|
||||
- const: core
|
||||
- const: sleep
|
||||
- const: mock_utmi
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,ipq8064-dwc3
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
items:
|
||||
- description: Master/Core clock, has to be >= 125 MHz
|
||||
for SS operation and >= 60MHz for HS operation.
|
||||
clock-names:
|
||||
items:
|
||||
- const: core
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,ipq9574-dwc3
|
||||
- qcom,msm8953-dwc3
|
||||
- qcom,msm8996-dwc3
|
||||
- qcom,msm8998-dwc3
|
||||
- qcom,qcs8300-dwc3
|
||||
- qcom,sa8775p-dwc3
|
||||
- qcom,sc7180-dwc3
|
||||
- qcom,sc7280-dwc3
|
||||
- qcom,sdm670-dwc3
|
||||
- qcom,sdm845-dwc3
|
||||
- qcom,sdx55-dwc3
|
||||
- qcom,sdx65-dwc3
|
||||
- qcom,sdx75-dwc3
|
||||
- qcom,sm6350-dwc3
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 5
|
||||
clock-names:
|
||||
items:
|
||||
- const: cfg_noc
|
||||
- const: core
|
||||
- const: iface
|
||||
- const: sleep
|
||||
- const: mock_utmi
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,ipq6018-dwc3
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 3
|
||||
maxItems: 4
|
||||
clock-names:
|
||||
oneOf:
|
||||
- items:
|
||||
- const: core
|
||||
- const: sleep
|
||||
- const: mock_utmi
|
||||
- items:
|
||||
- const: cfg_noc
|
||||
- const: core
|
||||
- const: sleep
|
||||
- const: mock_utmi
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,ipq8074-dwc3
|
||||
- qcom,qdu1000-dwc3
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 4
|
||||
clock-names:
|
||||
items:
|
||||
- const: cfg_noc
|
||||
- const: core
|
||||
- const: sleep
|
||||
- const: mock_utmi
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,ipq5018-dwc3
|
||||
- qcom,msm8994-dwc3
|
||||
- qcom,qcs404-dwc3
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 4
|
||||
clock-names:
|
||||
items:
|
||||
- const: core
|
||||
- const: iface
|
||||
- const: sleep
|
||||
- const: mock_utmi
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,sc8280xp-dwc3
|
||||
- qcom,sc8280xp-dwc3-mp
|
||||
- qcom,x1e80100-dwc3
|
||||
- qcom,x1e80100-dwc3-mp
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 9
|
||||
clock-names:
|
||||
items:
|
||||
- const: cfg_noc
|
||||
- const: core
|
||||
- const: iface
|
||||
- const: sleep
|
||||
- const: mock_utmi
|
||||
- const: noc_aggr
|
||||
- const: noc_aggr_north
|
||||
- const: noc_aggr_south
|
||||
- const: noc_sys
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,sdm660-dwc3
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 4
|
||||
maxItems: 5
|
||||
clock-names:
|
||||
oneOf:
|
||||
- items:
|
||||
- const: cfg_noc
|
||||
- const: core
|
||||
- const: iface
|
||||
- const: sleep
|
||||
- const: mock_utmi
|
||||
- items:
|
||||
- const: cfg_noc
|
||||
- const: core
|
||||
- const: sleep
|
||||
- const: mock_utmi
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,qcm2290-dwc3
|
||||
- qcom,qcs615-dwc3
|
||||
- qcom,sar2130p-dwc3
|
||||
- qcom,sc8180x-dwc3
|
||||
- qcom,sc8180x-dwc3-mp
|
||||
- qcom,sm6115-dwc3
|
||||
- qcom,sm6125-dwc3
|
||||
- qcom,sm8150-dwc3
|
||||
- qcom,sm8250-dwc3
|
||||
- qcom,sm8450-dwc3
|
||||
- qcom,sm8550-dwc3
|
||||
- qcom,sm8650-dwc3
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 6
|
||||
clock-names:
|
||||
items:
|
||||
- const: cfg_noc
|
||||
- const: core
|
||||
- const: iface
|
||||
- const: sleep
|
||||
- const: mock_utmi
|
||||
- const: xo
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,sm8350-dwc3
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 5
|
||||
maxItems: 6
|
||||
clock-names:
|
||||
minItems: 5
|
||||
items:
|
||||
- const: cfg_noc
|
||||
- const: core
|
||||
- const: iface
|
||||
- const: sleep
|
||||
- const: mock_utmi
|
||||
- const: xo
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,ipq5018-dwc3
|
||||
- qcom,ipq6018-dwc3
|
||||
- qcom,ipq8074-dwc3
|
||||
- qcom,msm8953-dwc3
|
||||
- qcom,msm8998-dwc3
|
||||
then:
|
||||
properties:
|
||||
interrupts:
|
||||
minItems: 3
|
||||
maxItems: 4
|
||||
interrupt-names:
|
||||
minItems: 3
|
||||
items:
|
||||
- const: dwc_usb3
|
||||
- const: pwr_event
|
||||
- const: qusb2_phy
|
||||
- const: ss_phy_irq
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,msm8996-dwc3
|
||||
- qcom,qcs404-dwc3
|
||||
- qcom,sdm660-dwc3
|
||||
- qcom,sm6115-dwc3
|
||||
- qcom,sm6125-dwc3
|
||||
then:
|
||||
properties:
|
||||
interrupts:
|
||||
minItems: 4
|
||||
maxItems: 5
|
||||
interrupt-names:
|
||||
minItems: 4
|
||||
items:
|
||||
- const: dwc_usb3
|
||||
- const: pwr_event
|
||||
- const: qusb2_phy
|
||||
- const: hs_phy_irq
|
||||
- const: ss_phy_irq
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,ipq5332-dwc3
|
||||
then:
|
||||
properties:
|
||||
interrupts:
|
||||
maxItems: 4
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: dwc_usb3
|
||||
- const: pwr_event
|
||||
- const: dp_hs_phy_irq
|
||||
- const: dm_hs_phy_irq
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,x1e80100-dwc3
|
||||
then:
|
||||
properties:
|
||||
interrupts:
|
||||
maxItems: 5
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: dwc_usb3
|
||||
- const: pwr_event
|
||||
- const: dp_hs_phy_irq
|
||||
- const: dm_hs_phy_irq
|
||||
- const: ss_phy_irq
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,ipq4019-dwc3
|
||||
- qcom,ipq8064-dwc3
|
||||
- qcom,msm8994-dwc3
|
||||
- qcom,qcs615-dwc3
|
||||
- qcom,qcs8300-dwc3
|
||||
- qcom,qdu1000-dwc3
|
||||
- qcom,sa8775p-dwc3
|
||||
- qcom,sc7180-dwc3
|
||||
- qcom,sc7280-dwc3
|
||||
- qcom,sc8180x-dwc3
|
||||
- qcom,sc8280xp-dwc3
|
||||
- qcom,sdm670-dwc3
|
||||
- qcom,sdm845-dwc3
|
||||
- qcom,sdx55-dwc3
|
||||
- qcom,sdx65-dwc3
|
||||
- qcom,sdx75-dwc3
|
||||
- qcom,sm4250-dwc3
|
||||
- qcom,sm6350-dwc3
|
||||
- qcom,sm8150-dwc3
|
||||
- qcom,sm8250-dwc3
|
||||
- qcom,sm8350-dwc3
|
||||
- qcom,sm8450-dwc3
|
||||
- qcom,sm8550-dwc3
|
||||
- qcom,sm8650-dwc3
|
||||
then:
|
||||
properties:
|
||||
interrupts:
|
||||
minItems: 5
|
||||
maxItems: 6
|
||||
interrupt-names:
|
||||
minItems: 5
|
||||
items:
|
||||
- const: dwc_usb3
|
||||
- const: pwr_event
|
||||
- const: hs_phy_irq
|
||||
- const: dp_hs_phy_irq
|
||||
- const: dm_hs_phy_irq
|
||||
- const: ss_phy_irq
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,sc8180x-dwc3-mp
|
||||
- qcom,x1e80100-dwc3-mp
|
||||
then:
|
||||
properties:
|
||||
interrupts:
|
||||
minItems: 11
|
||||
maxItems: 11
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: dwc_usb3
|
||||
- const: pwr_event_1
|
||||
- const: pwr_event_2
|
||||
- const: hs_phy_1
|
||||
- const: hs_phy_2
|
||||
- const: dp_hs_phy_1
|
||||
- const: dm_hs_phy_1
|
||||
- const: dp_hs_phy_2
|
||||
- const: dm_hs_phy_2
|
||||
- const: ss_phy_1
|
||||
- const: ss_phy_2
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,sc8280xp-dwc3-mp
|
||||
then:
|
||||
properties:
|
||||
interrupts:
|
||||
minItems: 19
|
||||
maxItems: 19
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: dwc_usb3
|
||||
- const: pwr_event_1
|
||||
- const: pwr_event_2
|
||||
- const: pwr_event_3
|
||||
- const: pwr_event_4
|
||||
- const: hs_phy_1
|
||||
- const: hs_phy_2
|
||||
- const: hs_phy_3
|
||||
- const: hs_phy_4
|
||||
- const: dp_hs_phy_1
|
||||
- const: dm_hs_phy_1
|
||||
- const: dp_hs_phy_2
|
||||
- const: dm_hs_phy_2
|
||||
- const: dp_hs_phy_3
|
||||
- const: dm_hs_phy_3
|
||||
- const: dp_hs_phy_4
|
||||
- const: dm_hs_phy_4
|
||||
- const: ss_phy_1
|
||||
- const: ss_phy_2
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/qcom,gcc-sdm845.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
soc {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
usb@a600000 {
|
||||
compatible = "qcom,sdm845-dwc3", "qcom,snps-dwc3";
|
||||
reg = <0 0x0a600000 0 0x100000>;
|
||||
|
||||
clocks = <&gcc GCC_CFG_NOC_USB3_PRIM_AXI_CLK>,
|
||||
<&gcc GCC_USB30_PRIM_MASTER_CLK>,
|
||||
<&gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>,
|
||||
<&gcc GCC_USB30_PRIM_SLEEP_CLK>,
|
||||
<&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>;
|
||||
clock-names = "cfg_noc",
|
||||
"core",
|
||||
"iface",
|
||||
"sleep",
|
||||
"mock_utmi";
|
||||
|
||||
assigned-clocks = <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>,
|
||||
<&gcc GCC_USB30_PRIM_MASTER_CLK>;
|
||||
assigned-clock-rates = <19200000>, <150000000>;
|
||||
|
||||
interrupts = <GIC_SPI 133 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 130 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 489 IRQ_TYPE_EDGE_BOTH>,
|
||||
<GIC_SPI 488 IRQ_TYPE_EDGE_BOTH>,
|
||||
<GIC_SPI 486 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "dwc_usb3", "pwr_event", "hs_phy_irq",
|
||||
"dp_hs_phy_irq", "dm_hs_phy_irq", "ss_phy_irq";
|
||||
|
||||
power-domains = <&gcc USB30_PRIM_GDSC>;
|
||||
|
||||
resets = <&gcc GCC_USB30_PRIM_BCR>;
|
||||
|
||||
iommus = <&apps_smmu 0x740 0>;
|
||||
snps,dis_u2_susphy_quirk;
|
||||
snps,dis_enblslpm_quirk;
|
||||
phys = <&usb_1_hsphy>, <&usb_1_ssphy>;
|
||||
phy-names = "usb2-phy", "usb3-phy";
|
||||
};
|
||||
};
|
||||
...
|
|
@ -10,7 +10,7 @@ maintainers:
|
|||
- Matthias Kaehlcke <mka@chromium.org>
|
||||
|
||||
allOf:
|
||||
- $ref: usb-device.yaml#
|
||||
- $ref: usb-hub.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
|
@ -19,61 +19,35 @@ properties:
|
|||
- usbbda,5411
|
||||
- usbbda,411
|
||||
|
||||
reg: true
|
||||
|
||||
'#address-cells':
|
||||
const: 1
|
||||
|
||||
'#size-cells':
|
||||
const: 0
|
||||
|
||||
vdd-supply:
|
||||
description:
|
||||
phandle to the regulator that provides power to the hub.
|
||||
|
||||
peer-hub:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
description:
|
||||
phandle to the peer hub on the controller.
|
||||
peer-hub: true
|
||||
|
||||
ports:
|
||||
$ref: /schemas/graph.yaml#/properties/ports
|
||||
|
||||
properties:
|
||||
port@1:
|
||||
patternProperties:
|
||||
'^port@':
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
description:
|
||||
1st downstream facing USB port
|
||||
|
||||
port@2:
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
description:
|
||||
2nd downstream facing USB port
|
||||
properties:
|
||||
reg:
|
||||
minimum: 1
|
||||
maximum: 4
|
||||
|
||||
port@3:
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
description:
|
||||
3rd downstream facing USB port
|
||||
|
||||
port@4:
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
description:
|
||||
4th downstream facing USB port
|
||||
|
||||
patternProperties:
|
||||
'^.*@[1-4]$':
|
||||
description: The hard wired USB devices
|
||||
type: object
|
||||
$ref: /schemas/usb/usb-device.yaml
|
||||
additionalProperties: true
|
||||
additionalProperties:
|
||||
properties:
|
||||
reg:
|
||||
minimum: 1
|
||||
maximum: 4
|
||||
|
||||
required:
|
||||
- peer-hub
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
usb {
|
||||
|
|
|
@ -27,6 +27,7 @@ properties:
|
|||
- renesas,usbhs-r9a07g044 # RZ/G2{L,LC}
|
||||
- renesas,usbhs-r9a07g054 # RZ/V2L
|
||||
- renesas,usbhs-r9a08g045 # RZ/G3S
|
||||
- renesas,usbhs-r9a09g057 # RZ/V2H(P)
|
||||
- const: renesas,rzg2l-usbhs
|
||||
|
||||
- items:
|
||||
|
@ -127,11 +128,7 @@ allOf:
|
|||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- renesas,usbhs-r9a07g043
|
||||
- renesas,usbhs-r9a07g044
|
||||
- renesas,usbhs-r9a07g054
|
||||
- renesas,usbhs-r9a08g045
|
||||
const: renesas,rzg2l-usbhs
|
||||
then:
|
||||
properties:
|
||||
interrupts:
|
||||
|
|
|
@ -14,11 +14,13 @@ properties:
|
|||
oneOf:
|
||||
- enum:
|
||||
- google,gs101-dwusb3
|
||||
- samsung,exynos2200-dwusb3
|
||||
- samsung,exynos5250-dwusb3
|
||||
- samsung,exynos5433-dwusb3
|
||||
- samsung,exynos7-dwusb3
|
||||
- samsung,exynos7870-dwusb3
|
||||
- samsung,exynos850-dwusb3
|
||||
- samsung,exynosautov920-dwusb3
|
||||
- items:
|
||||
- const: samsung,exynos990-dwusb3
|
||||
- const: samsung,exynos850-dwusb3
|
||||
|
@ -79,6 +81,19 @@ allOf:
|
|||
required:
|
||||
- vdd10-supply
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: samsung,exynos2200-dwusb3
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 1
|
||||
clock-names:
|
||||
items:
|
||||
- const: link_aclk
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
|
@ -165,6 +180,21 @@ allOf:
|
|||
required:
|
||||
- vdd10-supply
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: samsung,exynosautov920-dwusb3
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 2
|
||||
maxItems: 2
|
||||
clock-names:
|
||||
items:
|
||||
- const: ref
|
||||
- const: susp_clk
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
|
|
|
@ -106,54 +106,54 @@ additionalProperties: false
|
|||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
usb-hub@8 {
|
||||
compatible = "smsc,usb3503";
|
||||
reg = <0x08>;
|
||||
connect-gpios = <&gpx3 0 1>;
|
||||
disabled-ports = <2 3>;
|
||||
intn-gpios = <&gpx3 4 1>;
|
||||
reset-gpios = <&gpx3 5 1>;
|
||||
initial-mode = <1>;
|
||||
clocks = <&clks 80>;
|
||||
clock-names = "refclk";
|
||||
};
|
||||
};
|
||||
usb-hub@8 {
|
||||
compatible = "smsc,usb3503";
|
||||
reg = <0x08>;
|
||||
connect-gpios = <&gpx3 0 1>;
|
||||
disabled-ports = <2 3>;
|
||||
intn-gpios = <&gpx3 4 1>;
|
||||
reset-gpios = <&gpx3 5 1>;
|
||||
initial-mode = <1>;
|
||||
clocks = <&clks 80>;
|
||||
clock-names = "refclk";
|
||||
};
|
||||
};
|
||||
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
usb-hub@8 {
|
||||
compatible = "smsc,usb3803";
|
||||
reg = <0x08>;
|
||||
connect-gpios = <&gpx3 0 1>;
|
||||
disabled-ports = <2 3>;
|
||||
intn-gpios = <&gpx3 4 1>;
|
||||
reset-gpios = <&gpx3 5 1>;
|
||||
bypass-gpios = <&gpx3 6 1>;
|
||||
initial-mode = <3>;
|
||||
clocks = <&clks 80>;
|
||||
clock-names = "refclk";
|
||||
};
|
||||
};
|
||||
usb-hub@8 {
|
||||
compatible = "smsc,usb3803";
|
||||
reg = <0x08>;
|
||||
connect-gpios = <&gpx3 0 1>;
|
||||
disabled-ports = <2 3>;
|
||||
intn-gpios = <&gpx3 4 1>;
|
||||
reset-gpios = <&gpx3 5 1>;
|
||||
bypass-gpios = <&gpx3 6 1>;
|
||||
initial-mode = <3>;
|
||||
clocks = <&clks 80>;
|
||||
clock-names = "refclk";
|
||||
};
|
||||
};
|
||||
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
usb-hub {
|
||||
/* I2C is not connected */
|
||||
compatible = "smsc,usb3503";
|
||||
initial-mode = <1>; /* initialize in HUB mode */
|
||||
disabled-ports = <1>;
|
||||
intn-gpios = <&pio 7 5 GPIO_ACTIVE_HIGH>; /* PH5 */
|
||||
reset-gpios = <&pio 4 16 GPIO_ACTIVE_LOW>; /* PE16 */
|
||||
connect-gpios = <&pio 4 17 GPIO_ACTIVE_HIGH>; /* PE17 */
|
||||
refclk-frequency = <19200000>;
|
||||
};
|
||||
usb-hub {
|
||||
/* I2C is not connected */
|
||||
compatible = "smsc,usb3503";
|
||||
initial-mode = <1>; /* initialize in HUB mode */
|
||||
disabled-ports = <1>;
|
||||
intn-gpios = <&pio 7 5 GPIO_ACTIVE_HIGH>; /* PH5 */
|
||||
reset-gpios = <&pio 4 16 GPIO_ACTIVE_LOW>; /* PE16 */
|
||||
connect-gpios = <&pio 4 17 GPIO_ACTIVE_HIGH>; /* PE17 */
|
||||
refclk-frequency = <19200000>;
|
||||
};
|
||||
|
||||
...
|
||||
|
|
|
@ -390,6 +390,12 @@ properties:
|
|||
maximum: 8
|
||||
default: 1
|
||||
|
||||
connector:
|
||||
$ref: /schemas/connector/usb-connector.yaml#
|
||||
description: Connector for dual role switch
|
||||
type: object
|
||||
unevaluatedProperties: false
|
||||
|
||||
port:
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
description:
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
$id: http://devicetree.org/schemas/usb/ti,usb8041.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: TI USB8041 USB 3.0 hub controller
|
||||
title: TI USB8041 and USB8044 USB 3.0 hub controllers
|
||||
|
||||
maintainers:
|
||||
- Alexander Stein <alexander.stein@ew.tq-group.com>
|
||||
|
@ -17,6 +17,8 @@ properties:
|
|||
enum:
|
||||
- usb451,8140
|
||||
- usb451,8142
|
||||
- usb451,8440
|
||||
- usb451,8442
|
||||
|
||||
reg: true
|
||||
|
||||
|
|
|
@ -28,7 +28,8 @@ description: |
|
|||
|
||||
properties:
|
||||
compatible:
|
||||
pattern: "^usb[0-9a-f]{1,4},[0-9a-f]{1,4}$"
|
||||
contains:
|
||||
pattern: "^usb[0-9a-f]{1,4},[0-9a-f]{1,4}$"
|
||||
description: Device nodes or combined nodes.
|
||||
"usbVID,PID", where VID is the vendor id and PID the product id.
|
||||
The textual representation of VID and PID shall be in lower case
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/usb/usb-hub.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Generic USB Hub
|
||||
|
||||
maintainers:
|
||||
- Pin-yen Lin <treapking@chromium.org>
|
||||
|
||||
allOf:
|
||||
- $ref: usb-device.yaml#
|
||||
|
||||
properties:
|
||||
'#address-cells':
|
||||
const: 1
|
||||
|
||||
peer-hub:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
description:
|
||||
phandle to the peer hub on the controller.
|
||||
|
||||
ports:
|
||||
$ref: /schemas/graph.yaml#/properties/ports
|
||||
description:
|
||||
The downstream facing USB ports
|
||||
|
||||
patternProperties:
|
||||
"^port@[1-9a-f][0-9a-f]*$":
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
|
||||
patternProperties:
|
||||
'^.*@[1-9a-f][0-9a-f]*$':
|
||||
description: The hard wired USB devices
|
||||
type: object
|
||||
$ref: /schemas/usb/usb-device.yaml
|
||||
additionalProperties: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: true
|
||||
|
||||
examples:
|
||||
- |
|
||||
usb {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
/* 2.0 hub on port 1 */
|
||||
hub_2_0: hub@1 {
|
||||
compatible = "usb123,4567";
|
||||
reg = <1>;
|
||||
peer-hub = <&hub_3_0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
/* USB 2.0 device on port 5 */
|
||||
device@5 {
|
||||
reg = <5>;
|
||||
compatible = "usb765,4321";
|
||||
};
|
||||
};
|
||||
|
||||
/* 3.0 hub on port 2 */
|
||||
hub_3_0: hub@2 {
|
||||
compatible = "usb123,abcd";
|
||||
reg = <2>;
|
||||
peer-hub = <&hub_2_0>;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
/* Type-A connector on port 3 */
|
||||
port@3 {
|
||||
reg = <3>;
|
||||
endpoint {
|
||||
remote-endpoint = <&usb_a0_ss>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
|
@ -26,11 +26,24 @@ properties:
|
|||
type: boolean
|
||||
|
||||
port:
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
$ref: /schemas/graph.yaml#/$defs/port-base
|
||||
description:
|
||||
A port node to link the device to a TypeC controller for the purpose of
|
||||
handling altmode muxing and orientation switching.
|
||||
|
||||
properties:
|
||||
endpoint:
|
||||
$ref: /schemas/graph.yaml#/$defs/endpoint-base
|
||||
unevaluatedProperties: false
|
||||
properties:
|
||||
data-lanes:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
minItems: 1
|
||||
maxItems: 8
|
||||
uniqueItems: true
|
||||
items:
|
||||
maximum: 8
|
||||
|
||||
ports:
|
||||
$ref: /schemas/graph.yaml#/properties/ports
|
||||
properties:
|
||||
|
|
|
@ -18,3 +18,4 @@ The documentation is spilt into the following sections:-
|
|||
jack
|
||||
dpcm
|
||||
codec-to-codec
|
||||
usb
|
||||
|
|
|
@ -0,0 +1,482 @@
|
|||
================
|
||||
ASoC USB support
|
||||
================
|
||||
|
||||
Overview
|
||||
========
|
||||
In order to leverage the existing USB sound device support in ALSA, the
|
||||
ASoC USB APIs are introduced to allow the subsystems to exchange
|
||||
configuration information.
|
||||
|
||||
One potential use case would be to support USB audio offloading, which is
|
||||
an implementation that allows for an alternate power-optimized path in the audio
|
||||
subsystem to handle the transfer of audio data over the USB bus. This would
|
||||
let the main processor to stay in lower power modes for longer duration. The
|
||||
following is an example design of how the ASoC and ALSA pieces can be connected
|
||||
together to achieve this:
|
||||
|
||||
::
|
||||
|
||||
USB | ASoC
|
||||
| _________________________
|
||||
| | ASoC Platform card |
|
||||
| |_________________________|
|
||||
| | |
|
||||
| ___V____ ____V____
|
||||
| |ASoC BE | |ASoC FE |
|
||||
| |DAI LNK | |DAI LNK |
|
||||
| |________| |_________|
|
||||
| ^ ^ ^
|
||||
| | |________|
|
||||
| ___V____ |
|
||||
| |SoC-USB | |
|
||||
________ ________ | | |
|
||||
|USB SND |<--->|USBSND |<------------>|________| |
|
||||
|(card.c)| |offld |<---------- |
|
||||
|________| |________|___ | | |
|
||||
^ ^ | | | ____________V_________
|
||||
| | | | | |IPC |
|
||||
__ V_______________V_____ | | | |______________________|
|
||||
|USB SND (endpoint.c) | | | | ^
|
||||
|_________________________| | | | |
|
||||
^ | | | ___________V___________
|
||||
| | | |->|audio DSP |
|
||||
___________V_____________ | | |_______________________|
|
||||
|XHCI HCD |<- |
|
||||
|_________________________| |
|
||||
|
||||
|
||||
SoC USB driver
|
||||
==============
|
||||
Structures
|
||||
----------
|
||||
``struct snd_soc_usb``
|
||||
|
||||
- ``list``: list head for SND SoC struct list
|
||||
- ``component``: reference to ASoC component
|
||||
- ``connection_status_cb``: callback to notify connection events
|
||||
- ``update_offload_route_info``: callback to fetch selected USB sound card/PCM
|
||||
device
|
||||
- ``priv_data``: driver data
|
||||
|
||||
The snd_soc_usb structure can be referenced using the ASoC platform card
|
||||
device, or a USB device (udev->dev). This is created by the ASoC BE DAI
|
||||
link, and the USB sound entity will be able to pass information to the
|
||||
ASoC BE DAI link using this structure.
|
||||
|
||||
``struct snd_soc_usb_device``
|
||||
|
||||
- ``card_idx``: sound card index associated with USB sound device
|
||||
- ``chip_idx``: USB sound chip array index
|
||||
- ``cpcm_idx``: capture pcm device indexes associated with the USB sound device
|
||||
- ``ppcm_idx``: playback pcm device indexes associated with the USB sound device
|
||||
- ``num_playback``: number of playback streams
|
||||
- ``num_capture``: number of capture streams
|
||||
- ``list``: list head for the USB sound device list
|
||||
|
||||
The struct snd_soc_usb_device is created by the USB sound offload driver.
|
||||
This will carry basic parameters/limitations that will be used to
|
||||
determine the possible offloading paths for this USB audio device.
|
||||
|
||||
Functions
|
||||
---------
|
||||
.. code-block:: rst
|
||||
|
||||
int snd_soc_usb_find_supported_format(int card_idx,
|
||||
struct snd_pcm_hw_params *params, int direction)
|
||||
..
|
||||
|
||||
- ``card_idx``: the index into the USB sound chip array.
|
||||
- ``params``: Requested PCM parameters from the USB DPCM BE DAI link
|
||||
- ``direction``: capture or playback
|
||||
|
||||
**snd_soc_usb_find_supported_format()** ensures that the requested audio profile
|
||||
being requested by the external DSP is supported by the USB device.
|
||||
|
||||
Returns 0 on success, and -EOPNOTSUPP on failure.
|
||||
|
||||
.. code-block:: rst
|
||||
|
||||
int snd_soc_usb_connect(struct device *usbdev, struct snd_soc_usb_device *sdev)
|
||||
..
|
||||
|
||||
- ``usbdev``: the usb device that was discovered
|
||||
- ``sdev``: capabilities of the device
|
||||
|
||||
**snd_soc_usb_connect()** notifies the ASoC USB DCPM BE DAI link of a USB
|
||||
audio device detection. This can be utilized in the BE DAI
|
||||
driver to keep track of available USB audio devices. This is intended
|
||||
to be called by the USB offload driver residing in USB SND.
|
||||
|
||||
Returns 0 on success, negative error code on failure.
|
||||
|
||||
.. code-block:: rst
|
||||
|
||||
int snd_soc_usb_disconnect(struct device *usbdev, struct snd_soc_usb_device *sdev)
|
||||
..
|
||||
|
||||
- ``usbdev``: the usb device that was removed
|
||||
- ``sdev``: capabilities to free
|
||||
|
||||
**snd_soc_usb_disconnect()** notifies the ASoC USB DCPM BE DAI link of a USB
|
||||
audio device removal. This is intended to be called by the USB offload
|
||||
driver that resides in USB SND.
|
||||
|
||||
.. code-block:: rst
|
||||
|
||||
void *snd_soc_usb_find_priv_data(struct device *usbdev)
|
||||
..
|
||||
|
||||
- ``usbdev``: the usb device to reference to find private data
|
||||
|
||||
**snd_soc_usb_find_priv_data()** fetches the private data saved to the SoC USB
|
||||
device.
|
||||
|
||||
Returns pointer to priv_data on success, NULL on failure.
|
||||
|
||||
.. code-block:: rst
|
||||
|
||||
int snd_soc_usb_setup_offload_jack(struct snd_soc_component *component,
|
||||
struct snd_soc_jack *jack)
|
||||
..
|
||||
|
||||
- ``component``: ASoC component to add the jack
|
||||
- ``jack``: jack component to populate
|
||||
|
||||
**snd_soc_usb_setup_offload_jack()** is a helper to add a sound jack control to
|
||||
the platform sound card. This will allow for consistent naming to be used on
|
||||
designs that support USB audio offloading. Additionally, this will enable the
|
||||
jack to notify of changes.
|
||||
|
||||
Returns 0 on success, negative otherwise.
|
||||
|
||||
.. code-block:: rst
|
||||
|
||||
int snd_soc_usb_update_offload_route(struct device *dev, int card, int pcm,
|
||||
int direction, enum snd_soc_usb_kctl path,
|
||||
long *route)
|
||||
..
|
||||
|
||||
- ``dev``: USB device to look up offload path mapping
|
||||
- ``card``: USB sound card index
|
||||
- ``pcm``: USB sound PCM device index
|
||||
- ``direction``: direction to fetch offload routing information
|
||||
- ``path``: kcontrol selector - pcm device or card index
|
||||
- ``route``: mapping of sound card and pcm indexes for the offload path. This is
|
||||
an array of two integers that will carry the card and pcm device indexes
|
||||
in that specific order. This can be used as the array for the kcontrol
|
||||
output.
|
||||
|
||||
**snd_soc_usb_update_offload_route()** calls a registered callback to the USB BE DAI
|
||||
link to fetch the information about the mapped ASoC devices for executing USB audio
|
||||
offload for the device. ``route`` may be a pointer to a kcontrol value output array,
|
||||
which carries values when the kcontrol is read.
|
||||
|
||||
Returns 0 on success, negative otherwise.
|
||||
|
||||
.. code-block:: rst
|
||||
|
||||
struct snd_soc_usb *snd_soc_usb_allocate_port(struct snd_soc_component *component,
|
||||
void *data);
|
||||
..
|
||||
|
||||
- ``component``: DPCM BE DAI link component
|
||||
- ``data``: private data
|
||||
|
||||
**snd_soc_usb_allocate_port()** allocates a SoC USB device and populates standard
|
||||
parameters that is used for further operations.
|
||||
|
||||
Returns a pointer to struct soc_usb on success, negative on error.
|
||||
|
||||
.. code-block:: rst
|
||||
|
||||
void snd_soc_usb_free_port(struct snd_soc_usb *usb);
|
||||
..
|
||||
|
||||
- ``usb``: SoC USB device to free
|
||||
|
||||
**snd_soc_usb_free_port()** frees a SoC USB device.
|
||||
|
||||
.. code-block:: rst
|
||||
|
||||
void snd_soc_usb_add_port(struct snd_soc_usb *usb);
|
||||
..
|
||||
|
||||
- ``usb``: SoC USB device to add
|
||||
|
||||
**snd_soc_usb_add_port()** add an allocated SoC USB device to the SOC USB framework.
|
||||
Once added, this device can be referenced by further operations.
|
||||
|
||||
.. code-block:: rst
|
||||
|
||||
void snd_soc_usb_remove_port(struct snd_soc_usb *usb);
|
||||
..
|
||||
|
||||
- ``usb``: SoC USB device to remove
|
||||
|
||||
**snd_soc_usb_remove_port()** removes a SoC USB device from the SoC USB framework.
|
||||
After removing a device, any SOC USB operations would not be able to reference the
|
||||
device removed.
|
||||
|
||||
How to Register to SoC USB
|
||||
--------------------------
|
||||
The ASoC DPCM USB BE DAI link is the entity responsible for allocating and
|
||||
registering the SoC USB device on the component bind. Likewise, it will
|
||||
also be responsible for freeing the allocated resources. An example can
|
||||
be shown below:
|
||||
|
||||
.. code-block:: rst
|
||||
|
||||
static int q6usb_component_probe(struct snd_soc_component *component)
|
||||
{
|
||||
...
|
||||
data->usb = snd_soc_usb_allocate_port(component, 1, &data->priv);
|
||||
if (!data->usb)
|
||||
return -ENOMEM;
|
||||
|
||||
usb->connection_status_cb = q6usb_alsa_connection_cb;
|
||||
|
||||
ret = snd_soc_usb_add_port(usb);
|
||||
if (ret < 0) {
|
||||
dev_err(component->dev, "failed to add usb port\n");
|
||||
goto free_usb;
|
||||
}
|
||||
...
|
||||
}
|
||||
|
||||
static void q6usb_component_remove(struct snd_soc_component *component)
|
||||
{
|
||||
...
|
||||
snd_soc_usb_remove_port(data->usb);
|
||||
snd_soc_usb_free_port(data->usb);
|
||||
}
|
||||
|
||||
static const struct snd_soc_component_driver q6usb_dai_component = {
|
||||
.probe = q6usb_component_probe,
|
||||
.remove = q6usb_component_remove,
|
||||
.name = "q6usb-dai-component",
|
||||
...
|
||||
};
|
||||
..
|
||||
|
||||
BE DAI links can pass along vendor specific information as part of the
|
||||
call to allocate the SoC USB device. This will allow any BE DAI link
|
||||
parameters or settings to be accessed by the USB offload driver that
|
||||
resides in USB SND.
|
||||
|
||||
USB Audio Device Connection Flow
|
||||
--------------------------------
|
||||
USB devices can be hotplugged into the USB ports at any point in time.
|
||||
The BE DAI link should be aware of the current state of the physical USB
|
||||
port, i.e. if there are any USB devices with audio interface(s) connected.
|
||||
connection_status_cb() can be used to notify the BE DAI link of any change.
|
||||
|
||||
This is called whenever there is a USB SND interface bind or remove event,
|
||||
using snd_soc_usb_connect() or snd_soc_usb_disconnect():
|
||||
|
||||
.. code-block:: rst
|
||||
|
||||
static void qc_usb_audio_offload_probe(struct snd_usb_audio *chip)
|
||||
{
|
||||
...
|
||||
snd_soc_usb_connect(usb_get_usb_backend(udev), sdev);
|
||||
...
|
||||
}
|
||||
|
||||
static void qc_usb_audio_offload_disconnect(struct snd_usb_audio *chip)
|
||||
{
|
||||
...
|
||||
snd_soc_usb_disconnect(usb_get_usb_backend(chip->dev), dev->sdev);
|
||||
...
|
||||
}
|
||||
..
|
||||
|
||||
In order to account for conditions where driver or device existence is
|
||||
not guaranteed, USB SND exposes snd_usb_rediscover_devices() to resend the
|
||||
connect events for any identified USB audio interfaces. Consider the
|
||||
the following situation:
|
||||
|
||||
**usb_audio_probe()**
|
||||
| --> USB audio streams allocated and saved to usb_chip[]
|
||||
| --> Propagate connect event to USB offload driver in USB SND
|
||||
| --> **snd_soc_usb_connect()** exits as USB BE DAI link is not ready
|
||||
|
||||
BE DAI link component probe
|
||||
| --> DAI link is probed and SoC USB port is allocated
|
||||
| --> The USB audio device connect event is missed
|
||||
|
||||
To ensure connection events are not missed, **snd_usb_rediscover_devices()**
|
||||
is executed when the SoC USB device is registered. Now, when the BE DAI
|
||||
link component probe occurs, the following highlights the sequence:
|
||||
|
||||
BE DAI link component probe
|
||||
| --> DAI link is probed and SoC USB port is allocated
|
||||
| --> SoC USB device added, and **snd_usb_rediscover_devices()** runs
|
||||
|
||||
**snd_usb_rediscover_devices()**
|
||||
| --> Traverses through usb_chip[] and for non-NULL entries issue
|
||||
| **connection_status_cb()**
|
||||
|
||||
In the case where the USB offload driver is unbound, while USB SND is ready,
|
||||
the **snd_usb_rediscover_devices()** is called during module init. This allows
|
||||
for the offloading path to also be enabled with the following flow:
|
||||
|
||||
**usb_audio_probe()**
|
||||
| --> USB audio streams allocated and saved to usb_chip[]
|
||||
| --> Propagate connect event to USB offload driver in USB SND
|
||||
| --> USB offload driver **NOT** ready!
|
||||
|
||||
BE DAI link component probe
|
||||
| --> DAI link is probed and SoC USB port is allocated
|
||||
| --> No USB connect event due to missing USB offload driver
|
||||
|
||||
USB offload driver probe
|
||||
| --> **qc_usb_audio_offload_init()**
|
||||
| --> Calls **snd_usb_rediscover_devices()** to notify of devices
|
||||
|
||||
USB Offload Related Kcontrols
|
||||
=============================
|
||||
Details
|
||||
-------
|
||||
A set of kcontrols can be utilized by applications to help select the proper sound
|
||||
devices to enable USB audio offloading. SoC USB exposes the get_offload_dev()
|
||||
callback that designs can use to ensure that the proper indices are returned to the
|
||||
application.
|
||||
|
||||
Implementation
|
||||
--------------
|
||||
|
||||
**Example:**
|
||||
|
||||
**Sound Cards**:
|
||||
|
||||
::
|
||||
|
||||
0 [SM8250MTPWCD938]: sm8250 - SM8250-MTP-WCD9380-WSA8810-VA-D
|
||||
SM8250-MTP-WCD9380-WSA8810-VA-DMIC
|
||||
1 [Seri ]: USB-Audio - Plantronics Blackwire 3225 Seri
|
||||
Plantronics Plantronics Blackwire
|
||||
3225 Seri at usb-xhci-hcd.1.auto-1.1,
|
||||
full sp
|
||||
2 [C320M ]: USB-Audio - Plantronics C320-M
|
||||
Plantronics Plantronics C320-M at usb-xhci-hcd.1.auto-1.2, full speed
|
||||
|
||||
**PCM Devices**:
|
||||
|
||||
::
|
||||
|
||||
card 0: SM8250MTPWCD938 [SM8250-MTP-WCD9380-WSA8810-VA-D], device 0: MultiMedia1 (*) []
|
||||
Subdevices: 1/1
|
||||
Subdevice #0: subdevice #0
|
||||
card 0: SM8250MTPWCD938 [SM8250-MTP-WCD9380-WSA8810-VA-D], device 1: MultiMedia2 (*) []
|
||||
Subdevices: 1/1
|
||||
Subdevice #0: subdevice #0
|
||||
card 1: Seri [Plantronics Blackwire 3225 Seri], device 0: USB Audio [USB Audio]
|
||||
Subdevices: 1/1
|
||||
Subdevice #0: subdevice #0
|
||||
card 2: C320M [Plantronics C320-M], device 0: USB Audio [USB Audio]
|
||||
Subdevices: 1/1
|
||||
Subdevice #0: subdevice #0
|
||||
|
||||
**USB Sound Card** - card#1:
|
||||
|
||||
::
|
||||
|
||||
USB Offload Playback Card Route PCM#0 -1 (range -1->32)
|
||||
USB Offload Playback PCM Route PCM#0 -1 (range -1->255)
|
||||
|
||||
**USB Sound Card** - card#2:
|
||||
|
||||
::
|
||||
|
||||
USB Offload Playback Card Route PCM#0 0 (range -1->32)
|
||||
USB Offload Playback PCM Route PCM#0 1 (range -1->255)
|
||||
|
||||
The above example shows a scenario where the system has one ASoC platform card
|
||||
(card#0) and two USB sound devices connected (card#1 and card#2). When reading
|
||||
the available kcontrols for each USB audio device, the following kcontrols lists
|
||||
the mapped offload card and pcm device indexes for the specific USB device:
|
||||
|
||||
``USB Offload Playback Card Route PCM#*``
|
||||
|
||||
``USB Offload Playback PCM Route PCM#*``
|
||||
|
||||
The kcontrol is indexed, because a USB audio device could potentially have
|
||||
several PCM devices. The above kcontrols are defined as:
|
||||
|
||||
- ``USB Offload Playback Card Route PCM#`` **(R)**: Returns the ASoC platform sound
|
||||
card index for a mapped offload path. The output **"0"** (card index) signifies
|
||||
that there is an available offload path for the USB SND device through card#0.
|
||||
If **"-1"** is seen, then no offload path is available for the USB SND device.
|
||||
This kcontrol exists for each USB audio device that exists in the system, and
|
||||
its expected to derive the current status of offload based on the output value
|
||||
for the kcontrol along with the PCM route kcontrol.
|
||||
|
||||
- ``USB Offload Playback PCM Route PCM#`` **(R)**: Returns the ASoC platform sound
|
||||
PCM device index for a mapped offload path. The output **"1"** (PCM device index)
|
||||
signifies that there is an available offload path for the USB SND device through
|
||||
PCM device#0. If **"-1"** is seen, then no offload path is available for the USB\
|
||||
SND device. This kcontrol exists for each USB audio device that exists in the
|
||||
system, and its expected to derive the current status of offload based on the
|
||||
output value for this kcontrol, in addition to the card route kcontrol.
|
||||
|
||||
USB Offload Playback Route Kcontrol
|
||||
-----------------------------------
|
||||
In order to allow for vendor specific implementations on audio offloading device
|
||||
selection, the SoC USB layer exposes the following:
|
||||
|
||||
.. code-block:: rst
|
||||
|
||||
int (*update_offload_route_info)(struct snd_soc_component *component,
|
||||
int card, int pcm, int direction,
|
||||
enum snd_soc_usb_kctl path,
|
||||
long *route)
|
||||
..
|
||||
|
||||
These are specific for the **USB Offload Playback Card Route PCM#** and **USB
|
||||
Offload PCM Route PCM#** kcontrols.
|
||||
|
||||
When users issue get calls to the kcontrol, the registered SoC USB callbacks will
|
||||
execute the registered function calls to the DPCM BE DAI link.
|
||||
|
||||
**Callback Registration:**
|
||||
|
||||
.. code-block:: rst
|
||||
|
||||
static int q6usb_component_probe(struct snd_soc_component *component)
|
||||
{
|
||||
...
|
||||
usb = snd_soc_usb_allocate_port(component, 1, &data->priv);
|
||||
if (IS_ERR(usb))
|
||||
return -ENOMEM;
|
||||
|
||||
usb->connection_status_cb = q6usb_alsa_connection_cb;
|
||||
usb->update_offload_route_info = q6usb_get_offload_dev;
|
||||
|
||||
ret = snd_soc_usb_add_port(usb);
|
||||
..
|
||||
|
||||
Existing USB Sound Kcontrol
|
||||
---------------------------
|
||||
With the introduction of USB offload support, the above USB offload kcontrol
|
||||
will be added to the pre existing list of kcontrols identified by the USB sound
|
||||
framework. These kcontrols are still the main controls that are used to
|
||||
modify characteristics pertaining to the USB audio device.
|
||||
|
||||
::
|
||||
|
||||
Number of controls: 9
|
||||
ctl type num name value
|
||||
0 INT 2 Capture Channel Map 0, 0 (range 0->36)
|
||||
1 INT 2 Playback Channel Map 0, 0 (range 0->36)
|
||||
2 BOOL 1 Headset Capture Switch On
|
||||
3 INT 1 Headset Capture Volume 10 (range 0->13)
|
||||
4 BOOL 1 Sidetone Playback Switch On
|
||||
5 INT 1 Sidetone Playback Volume 4096 (range 0->8192)
|
||||
6 BOOL 1 Headset Playback Switch On
|
||||
7 INT 2 Headset Playback Volume 20, 20 (range 0->24)
|
||||
8 INT 1 USB Offload Playback Card Route PCM#0 0 (range -1->32)
|
||||
9 INT 1 USB Offload Playback PCM Route PCM#0 1 (range -1->255)
|
||||
|
||||
Since USB audio device controls are handled over the USB control endpoint, use the
|
||||
existing mechanisms present in the USB mixer to set parameters, such as volume.
|
|
@ -11150,6 +11150,7 @@ S: Maintained
|
|||
F: Documentation/devicetree/bindings/platform/huawei,gaokun-ec.yaml
|
||||
F: drivers/platform/arm64/huawei-gaokun-ec.c
|
||||
F: drivers/power/supply/huawei-gaokun-battery.c
|
||||
F: drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c
|
||||
F: include/linux/platform_data/huawei-gaokun-ec.h
|
||||
|
||||
HUGETLB SUBSYSTEM
|
||||
|
@ -12289,7 +12290,7 @@ F: drivers/crypto/intel/keembay/ocs-hcu.c
|
|||
F: drivers/crypto/intel/keembay/ocs-hcu.h
|
||||
|
||||
INTEL LA JOLLA COVE ADAPTER (LJCA) USB I/O EXPANDER DRIVERS
|
||||
M: Wentong Wu <wentong.wu@intel.com>
|
||||
M: Lixu Zhang <lixu.zhang@intel.com>
|
||||
M: Sakari Ailus <sakari.ailus@linux.intel.com>
|
||||
S: Maintained
|
||||
F: drivers/gpio/gpio-ljca.c
|
||||
|
|
|
@ -151,6 +151,11 @@ static void tb_cfg_request_dequeue(struct tb_cfg_request *req)
|
|||
struct tb_ctl *ctl = req->ctl;
|
||||
|
||||
mutex_lock(&ctl->request_queue_lock);
|
||||
if (!test_bit(TB_CFG_REQUEST_ACTIVE, &req->flags)) {
|
||||
mutex_unlock(&ctl->request_queue_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
list_del(&req->list);
|
||||
clear_bit(TB_CFG_REQUEST_ACTIVE, &req->flags);
|
||||
if (test_bit(TB_CFG_REQUEST_CANCELED, &req->flags))
|
||||
|
|
|
@ -217,7 +217,7 @@ static ssize_t boot_acl_store(struct device *dev, struct device_attribute *attr,
|
|||
ret = tb->cm_ops->set_boot_acl(tb, acl, tb->nboot_acl);
|
||||
if (!ret) {
|
||||
/* Notify userspace about the change */
|
||||
kobject_uevent(&tb->dev.kobj, KOBJ_CHANGE);
|
||||
tb_domain_event(tb, NULL);
|
||||
}
|
||||
mutex_unlock(&tb->lock);
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "ctl.h"
|
||||
#include "nhi_regs.h"
|
||||
#include "tb.h"
|
||||
#include "tunnel.h"
|
||||
|
||||
#define PCIE2CIO_CMD 0x30
|
||||
#define PCIE2CIO_CMD_TIMEOUT BIT(31)
|
||||
|
@ -379,6 +380,27 @@ static bool icm_firmware_running(const struct tb_nhi *nhi)
|
|||
return !!(val & REG_FW_STS_ICM_EN);
|
||||
}
|
||||
|
||||
static void icm_xdomain_activated(struct tb_xdomain *xd, bool activated)
|
||||
{
|
||||
struct tb_port *nhi_port, *dst_port;
|
||||
struct tb *tb = xd->tb;
|
||||
|
||||
nhi_port = tb_switch_find_port(tb->root_switch, TB_TYPE_NHI);
|
||||
dst_port = tb_xdomain_downstream_port(xd);
|
||||
|
||||
if (activated)
|
||||
tb_tunnel_event(tb, TB_TUNNEL_ACTIVATED, TB_TUNNEL_DMA,
|
||||
nhi_port, dst_port);
|
||||
else
|
||||
tb_tunnel_event(tb, TB_TUNNEL_DEACTIVATED, TB_TUNNEL_DMA,
|
||||
nhi_port, dst_port);
|
||||
}
|
||||
|
||||
static void icm_dp_event(struct tb *tb)
|
||||
{
|
||||
tb_tunnel_event(tb, TB_TUNNEL_CHANGED, TB_TUNNEL_DP, NULL, NULL);
|
||||
}
|
||||
|
||||
static bool icm_fr_is_supported(struct tb *tb)
|
||||
{
|
||||
return !x86_apple_machine;
|
||||
|
@ -584,6 +606,7 @@ static int icm_fr_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd,
|
|||
if (reply.hdr.flags & ICM_FLAGS_ERROR)
|
||||
return -EIO;
|
||||
|
||||
icm_xdomain_activated(xd, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -603,6 +626,8 @@ static int icm_fr_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd,
|
|||
nhi_mailbox_cmd(tb->nhi, cmd, 1);
|
||||
usleep_range(10, 50);
|
||||
nhi_mailbox_cmd(tb->nhi, cmd, 2);
|
||||
|
||||
icm_xdomain_activated(xd, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1151,6 +1176,7 @@ static int icm_tr_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd,
|
|||
if (reply.hdr.flags & ICM_FLAGS_ERROR)
|
||||
return -EIO;
|
||||
|
||||
icm_xdomain_activated(xd, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1191,7 +1217,12 @@ static int icm_tr_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd,
|
|||
return ret;
|
||||
|
||||
usleep_range(10, 50);
|
||||
return icm_tr_xdomain_tear_down(tb, xd, 2);
|
||||
ret = icm_tr_xdomain_tear_down(tb, xd, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
icm_xdomain_activated(xd, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1718,6 +1749,9 @@ static void icm_handle_notification(struct work_struct *work)
|
|||
if (tb_is_xdomain_enabled())
|
||||
icm->xdomain_disconnected(tb, n->pkg);
|
||||
break;
|
||||
case ICM_EVENT_DP_CONFIG_CHANGED:
|
||||
icm_dp_event(tb);
|
||||
break;
|
||||
case ICM_EVENT_RTD3_VETO:
|
||||
icm->rtd3_veto(tb, n->pkg);
|
||||
break;
|
||||
|
|
|
@ -3599,6 +3599,7 @@ void tb_switch_suspend(struct tb_switch *sw, bool runtime)
|
|||
flags |= TB_WAKE_ON_USB4;
|
||||
flags |= TB_WAKE_ON_USB3 | TB_WAKE_ON_PCIE | TB_WAKE_ON_DP;
|
||||
} else if (device_may_wakeup(&sw->dev)) {
|
||||
flags |= TB_WAKE_ON_CONNECT | TB_WAKE_ON_DISCONNECT;
|
||||
flags |= TB_WAKE_ON_USB4 | TB_WAKE_ON_USB3 | TB_WAKE_ON_PCIE;
|
||||
}
|
||||
|
||||
|
|
|
@ -952,6 +952,15 @@ static int tb_tunnel_usb3(struct tb *tb, struct tb_switch *sw)
|
|||
tb_port_dbg(up, "available bandwidth for new USB3 tunnel %d/%d Mb/s\n",
|
||||
available_up, available_down);
|
||||
|
||||
/*
|
||||
* If the available bandwidth is less than 1.5 Gb/s notify
|
||||
* userspace that the connected isochronous device may not work
|
||||
* properly.
|
||||
*/
|
||||
if (available_up < 1500 || available_down < 1500)
|
||||
tb_tunnel_event(tb, TB_TUNNEL_LOW_BANDWIDTH, TB_TUNNEL_USB3,
|
||||
down, up);
|
||||
|
||||
tunnel = tb_tunnel_alloc_usb3(tb, up, down, available_up,
|
||||
available_down);
|
||||
if (!tunnel) {
|
||||
|
@ -2000,8 +2009,10 @@ static void tb_tunnel_one_dp(struct tb *tb, struct tb_port *in,
|
|||
|
||||
ret = tb_available_bandwidth(tb, in, out, &available_up, &available_down,
|
||||
true);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
tb_tunnel_event(tb, TB_TUNNEL_NO_BANDWIDTH, TB_TUNNEL_DP, in, out);
|
||||
goto err_reclaim_usb;
|
||||
}
|
||||
|
||||
tb_dbg(tb, "available bandwidth for new DP tunnel %u/%u Mb/s\n",
|
||||
available_up, available_down);
|
||||
|
@ -2622,8 +2633,12 @@ static int tb_alloc_dp_bandwidth(struct tb_tunnel *tunnel, int *requested_up,
|
|||
}
|
||||
}
|
||||
|
||||
return tb_tunnel_alloc_bandwidth(tunnel, requested_up,
|
||||
requested_down);
|
||||
ret = tb_tunnel_alloc_bandwidth(tunnel, requested_up,
|
||||
requested_down);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2699,6 +2714,7 @@ fail:
|
|||
"failing the request by rewriting allocated %d/%d Mb/s\n",
|
||||
allocated_up, allocated_down);
|
||||
tb_tunnel_alloc_bandwidth(tunnel, &allocated_up, &allocated_down);
|
||||
tb_tunnel_event(tb, TB_TUNNEL_NO_BANDWIDTH, TB_TUNNEL_DP, in, out);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
|
@ -804,6 +804,19 @@ static inline void tb_domain_put(struct tb *tb)
|
|||
put_device(&tb->dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* tb_domain_event() - Notify userspace about an event in domain
|
||||
* @tb: Domain where event occurred
|
||||
* @envp: Array of uevent environment strings (can be %NULL)
|
||||
*
|
||||
* This function provides a way to notify userspace about any events
|
||||
* that take place in the domain.
|
||||
*/
|
||||
static inline void tb_domain_event(struct tb *tb, char *envp[])
|
||||
{
|
||||
kobject_uevent_env(&tb->dev.kobj, KOBJ_CHANGE, envp);
|
||||
}
|
||||
|
||||
struct tb_nvm *tb_nvm_alloc(struct device *dev);
|
||||
int tb_nvm_read_version(struct tb_nvm *nvm);
|
||||
int tb_nvm_validate(struct tb_nvm *nvm);
|
||||
|
@ -1468,6 +1481,7 @@ static inline struct usb4_port *tb_to_usb4_port_device(struct device *dev)
|
|||
struct usb4_port *usb4_port_device_add(struct tb_port *port);
|
||||
void usb4_port_device_remove(struct usb4_port *usb4);
|
||||
int usb4_port_device_resume(struct usb4_port *usb4);
|
||||
int usb4_port_index(const struct tb_switch *sw, const struct tb_port *port);
|
||||
|
||||
static inline bool usb4_port_device_is_offline(const struct usb4_port *usb4)
|
||||
{
|
||||
|
|
|
@ -118,6 +118,7 @@ enum icm_event_code {
|
|||
ICM_EVENT_DEVICE_DISCONNECTED = 0x4,
|
||||
ICM_EVENT_XDOMAIN_CONNECTED = 0x6,
|
||||
ICM_EVENT_XDOMAIN_DISCONNECTED = 0x7,
|
||||
ICM_EVENT_DP_CONFIG_CHANGED = 0x8,
|
||||
ICM_EVENT_RTD3_VETO = 0xa,
|
||||
};
|
||||
|
||||
|
|
|
@ -100,6 +100,14 @@ MODULE_PARM_DESC(bw_alloc_mode,
|
|||
|
||||
static const char * const tb_tunnel_names[] = { "PCI", "DP", "DMA", "USB3" };
|
||||
|
||||
static const char * const tb_event_names[] = {
|
||||
[TB_TUNNEL_ACTIVATED] = "activated",
|
||||
[TB_TUNNEL_CHANGED] = "changed",
|
||||
[TB_TUNNEL_DEACTIVATED] = "deactivated",
|
||||
[TB_TUNNEL_LOW_BANDWIDTH] = "low bandwidth",
|
||||
[TB_TUNNEL_NO_BANDWIDTH] = "insufficient bandwidth",
|
||||
};
|
||||
|
||||
/* Synchronizes kref_get()/put() of struct tb_tunnel */
|
||||
static DEFINE_MUTEX(tb_tunnel_lock);
|
||||
|
||||
|
@ -220,6 +228,72 @@ void tb_tunnel_put(struct tb_tunnel *tunnel)
|
|||
mutex_unlock(&tb_tunnel_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* tb_tunnel_event() - Notify userspace about tunneling event
|
||||
* @tb: Domain where the event occurred
|
||||
* @event: Event that happened
|
||||
* @type: Type of the tunnel in question
|
||||
* @src_port: Tunnel source port (can be %NULL)
|
||||
* @dst_port: Tunnel destination port (can be %NULL)
|
||||
*
|
||||
* Notifies userspace about tunneling @event in the domain. The tunnel
|
||||
* does not need to exist (e.g the tunnel was not activated because
|
||||
* there is not enough bandwidth). If the @src_port and @dst_port are
|
||||
* given fill in full %TUNNEL_DETAILS environment variable. Otherwise
|
||||
* uses the shorter one (just the tunnel type).
|
||||
*/
|
||||
void tb_tunnel_event(struct tb *tb, enum tb_tunnel_event event,
|
||||
enum tb_tunnel_type type,
|
||||
const struct tb_port *src_port,
|
||||
const struct tb_port *dst_port)
|
||||
{
|
||||
char *envp[3] = { NULL };
|
||||
|
||||
if (WARN_ON_ONCE(event >= ARRAY_SIZE(tb_event_names)))
|
||||
return;
|
||||
if (WARN_ON_ONCE(type >= ARRAY_SIZE(tb_tunnel_names)))
|
||||
return;
|
||||
|
||||
envp[0] = kasprintf(GFP_KERNEL, "TUNNEL_EVENT=%s", tb_event_names[event]);
|
||||
if (!envp[0])
|
||||
return;
|
||||
|
||||
if (src_port != NULL && dst_port != NULL) {
|
||||
envp[1] = kasprintf(GFP_KERNEL, "TUNNEL_DETAILS=%llx:%u <-> %llx:%u (%s)",
|
||||
tb_route(src_port->sw), src_port->port,
|
||||
tb_route(dst_port->sw), dst_port->port,
|
||||
tb_tunnel_names[type]);
|
||||
} else {
|
||||
envp[1] = kasprintf(GFP_KERNEL, "TUNNEL_DETAILS=(%s)",
|
||||
tb_tunnel_names[type]);
|
||||
}
|
||||
|
||||
if (envp[1])
|
||||
tb_domain_event(tb, envp);
|
||||
|
||||
kfree(envp[1]);
|
||||
kfree(envp[0]);
|
||||
}
|
||||
|
||||
static inline void tb_tunnel_set_active(struct tb_tunnel *tunnel, bool active)
|
||||
{
|
||||
if (active) {
|
||||
tunnel->state = TB_TUNNEL_ACTIVE;
|
||||
tb_tunnel_event(tunnel->tb, TB_TUNNEL_ACTIVATED, tunnel->type,
|
||||
tunnel->src_port, tunnel->dst_port);
|
||||
} else {
|
||||
tunnel->state = TB_TUNNEL_INACTIVE;
|
||||
tb_tunnel_event(tunnel->tb, TB_TUNNEL_DEACTIVATED, tunnel->type,
|
||||
tunnel->src_port, tunnel->dst_port);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void tb_tunnel_changed(struct tb_tunnel *tunnel)
|
||||
{
|
||||
tb_tunnel_event(tunnel->tb, TB_TUNNEL_CHANGED, tunnel->type,
|
||||
tunnel->src_port, tunnel->dst_port);
|
||||
}
|
||||
|
||||
static int tb_pci_set_ext_encapsulation(struct tb_tunnel *tunnel, bool enable)
|
||||
{
|
||||
struct tb_port *port = tb_upstream_port(tunnel->dst_port->sw);
|
||||
|
@ -992,7 +1066,7 @@ static void tb_dp_dprx_work(struct work_struct *work)
|
|||
return;
|
||||
}
|
||||
} else {
|
||||
tunnel->state = TB_TUNNEL_ACTIVE;
|
||||
tb_tunnel_set_active(tunnel, true);
|
||||
}
|
||||
mutex_unlock(&tb->lock);
|
||||
}
|
||||
|
@ -2326,7 +2400,7 @@ int tb_tunnel_activate(struct tb_tunnel *tunnel)
|
|||
}
|
||||
}
|
||||
|
||||
tunnel->state = TB_TUNNEL_ACTIVE;
|
||||
tb_tunnel_set_active(tunnel, true);
|
||||
return 0;
|
||||
|
||||
err:
|
||||
|
@ -2356,7 +2430,7 @@ void tb_tunnel_deactivate(struct tb_tunnel *tunnel)
|
|||
if (tunnel->post_deactivate)
|
||||
tunnel->post_deactivate(tunnel);
|
||||
|
||||
tunnel->state = TB_TUNNEL_INACTIVE;
|
||||
tb_tunnel_set_active(tunnel, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2449,8 +2523,16 @@ int tb_tunnel_alloc_bandwidth(struct tb_tunnel *tunnel, int *alloc_up,
|
|||
if (!tb_tunnel_is_active(tunnel))
|
||||
return -ENOTCONN;
|
||||
|
||||
if (tunnel->alloc_bandwidth)
|
||||
return tunnel->alloc_bandwidth(tunnel, alloc_up, alloc_down);
|
||||
if (tunnel->alloc_bandwidth) {
|
||||
int ret;
|
||||
|
||||
ret = tunnel->alloc_bandwidth(tunnel, alloc_up, alloc_down);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
tb_tunnel_changed(tunnel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
|
|
@ -194,6 +194,29 @@ static inline bool tb_tunnel_direction_downstream(const struct tb_tunnel *tunnel
|
|||
tunnel->dst_port);
|
||||
}
|
||||
|
||||
/**
|
||||
* enum tb_tunnel_event - Tunnel related events
|
||||
* @TB_TUNNEL_ACTIVATED: A tunnel was activated
|
||||
* @TB_TUNNEL_CHANGED: There is a tunneling change in the domain. Includes
|
||||
* full %TUNNEL_DETAILS if the tunnel in question is known
|
||||
* (ICM does not provide that information).
|
||||
* @TB_TUNNEL_DEACTIVATED: A tunnel was torn down
|
||||
* @TB_TUNNEL_LOW_BANDWIDTH: Tunnel bandwidth is not optimal
|
||||
* @TB_TUNNEL_NO_BANDWIDTH: There is not enough bandwidth for a tunnel
|
||||
*/
|
||||
enum tb_tunnel_event {
|
||||
TB_TUNNEL_ACTIVATED,
|
||||
TB_TUNNEL_CHANGED,
|
||||
TB_TUNNEL_DEACTIVATED,
|
||||
TB_TUNNEL_LOW_BANDWIDTH,
|
||||
TB_TUNNEL_NO_BANDWIDTH,
|
||||
};
|
||||
|
||||
void tb_tunnel_event(struct tb *tb, enum tb_tunnel_event event,
|
||||
enum tb_tunnel_type type,
|
||||
const struct tb_port *src_port,
|
||||
const struct tb_port *dst_port);
|
||||
|
||||
const char *tb_tunnel_type_name(const struct tb_tunnel *tunnel);
|
||||
|
||||
#define __TB_TUNNEL_PRINT(level, tunnel, fmt, arg...) \
|
||||
|
|
|
@ -440,10 +440,10 @@ int usb4_switch_set_wake(struct tb_switch *sw, unsigned int flags)
|
|||
bool configured = val & PORT_CS_19_PC;
|
||||
usb4 = port->usb4;
|
||||
|
||||
if (((flags & TB_WAKE_ON_CONNECT) |
|
||||
if (((flags & TB_WAKE_ON_CONNECT) &&
|
||||
device_may_wakeup(&usb4->dev)) && !configured)
|
||||
val |= PORT_CS_19_WOC;
|
||||
if (((flags & TB_WAKE_ON_DISCONNECT) |
|
||||
if (((flags & TB_WAKE_ON_DISCONNECT) &&
|
||||
device_may_wakeup(&usb4->dev)) && configured)
|
||||
val |= PORT_CS_19_WOD;
|
||||
if ((flags & TB_WAKE_ON_USB4) && configured)
|
||||
|
@ -935,7 +935,15 @@ int usb4_switch_dealloc_dp_resource(struct tb_switch *sw, struct tb_port *in)
|
|||
return status ? -EIO : 0;
|
||||
}
|
||||
|
||||
static int usb4_port_idx(const struct tb_switch *sw, const struct tb_port *port)
|
||||
/**
|
||||
* usb4_port_index() - Finds matching USB4 port index
|
||||
* @sw: USB4 router
|
||||
* @port: USB4 protocol or lane adapter
|
||||
*
|
||||
* Finds matching USB4 port index (starting from %0) that given @port goes
|
||||
* through.
|
||||
*/
|
||||
int usb4_port_index(const struct tb_switch *sw, const struct tb_port *port)
|
||||
{
|
||||
struct tb_port *p;
|
||||
int usb4_idx = 0;
|
||||
|
@ -969,7 +977,7 @@ static int usb4_port_idx(const struct tb_switch *sw, const struct tb_port *port)
|
|||
struct tb_port *usb4_switch_map_pcie_down(struct tb_switch *sw,
|
||||
const struct tb_port *port)
|
||||
{
|
||||
int usb4_idx = usb4_port_idx(sw, port);
|
||||
int usb4_idx = usb4_port_index(sw, port);
|
||||
struct tb_port *p;
|
||||
int pcie_idx = 0;
|
||||
|
||||
|
@ -1000,7 +1008,7 @@ struct tb_port *usb4_switch_map_pcie_down(struct tb_switch *sw,
|
|||
struct tb_port *usb4_switch_map_usb3_down(struct tb_switch *sw,
|
||||
const struct tb_port *port)
|
||||
{
|
||||
int usb4_idx = usb4_port_idx(sw, port);
|
||||
int usb4_idx = usb4_port_index(sw, port);
|
||||
struct tb_port *p;
|
||||
int usb_idx = 0;
|
||||
|
||||
|
|
|
@ -105,6 +105,49 @@ static void usb4_port_online(struct usb4_port *usb4)
|
|||
tb_acpi_power_off_retimers(port);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb4_usb3_port_match() - Matches USB4 port device with USB 3.x port device
|
||||
* @usb4_port_dev: USB4 port device
|
||||
* @usb3_port_fwnode: USB 3.x port firmware node
|
||||
*
|
||||
* Checks if USB 3.x port @usb3_port_fwnode is tunneled through USB4 port @usb4_port_dev.
|
||||
* Returns true if match is found, false otherwise.
|
||||
*
|
||||
* Function is designed to be used with component framework (component_match_add).
|
||||
*/
|
||||
bool usb4_usb3_port_match(struct device *usb4_port_dev,
|
||||
const struct fwnode_handle *usb3_port_fwnode)
|
||||
{
|
||||
struct fwnode_handle *nhi_fwnode __free(fwnode_handle) = NULL;
|
||||
struct usb4_port *usb4;
|
||||
struct tb_switch *sw;
|
||||
struct tb_nhi *nhi;
|
||||
u8 usb4_port_num;
|
||||
struct tb *tb;
|
||||
|
||||
usb4 = tb_to_usb4_port_device(usb4_port_dev);
|
||||
if (!usb4)
|
||||
return false;
|
||||
|
||||
sw = usb4->port->sw;
|
||||
tb = sw->tb;
|
||||
nhi = tb->nhi;
|
||||
|
||||
nhi_fwnode = fwnode_find_reference(usb3_port_fwnode, "usb4-host-interface", 0);
|
||||
if (IS_ERR(nhi_fwnode))
|
||||
return false;
|
||||
|
||||
/* Check if USB3 fwnode references same NHI where USB4 port resides */
|
||||
if (!device_match_fwnode(&nhi->pdev->dev, nhi_fwnode))
|
||||
return false;
|
||||
|
||||
if (fwnode_property_read_u8(usb3_port_fwnode, "usb4-port-number", &usb4_port_num))
|
||||
return false;
|
||||
|
||||
return usb4_port_index(sw, usb4->port) == usb4_port_num;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb4_usb3_port_match);
|
||||
|
||||
static ssize_t offline_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
|
@ -276,12 +319,10 @@ struct usb4_port *usb4_port_device_add(struct tb_port *port)
|
|||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
if (dev_fwnode(&usb4->dev)) {
|
||||
ret = component_add(&usb4->dev, &connector_ops);
|
||||
if (ret) {
|
||||
dev_err(&usb4->dev, "failed to add component\n");
|
||||
device_unregister(&usb4->dev);
|
||||
}
|
||||
ret = component_add(&usb4->dev, &connector_ops);
|
||||
if (ret) {
|
||||
dev_err(&usb4->dev, "failed to add component\n");
|
||||
device_unregister(&usb4->dev);
|
||||
}
|
||||
|
||||
if (!tb_is_upstream_port(port))
|
||||
|
@ -306,8 +347,7 @@ struct usb4_port *usb4_port_device_add(struct tb_port *port)
|
|||
*/
|
||||
void usb4_port_device_remove(struct usb4_port *usb4)
|
||||
{
|
||||
if (dev_fwnode(&usb4->dev))
|
||||
component_del(&usb4->dev, &connector_ops);
|
||||
component_del(&usb4->dev, &connector_ops);
|
||||
device_unregister(&usb4->dev);
|
||||
}
|
||||
|
||||
|
|
|
@ -179,8 +179,6 @@ err_phy3_init:
|
|||
/**
|
||||
* cdns3_plat_remove() - unbind drd driver and clean up
|
||||
* @pdev: Pointer to Linux platform device
|
||||
*
|
||||
* Returns 0 on success otherwise negative errno
|
||||
*/
|
||||
static void cdns3_plat_remove(struct platform_device *pdev)
|
||||
{
|
||||
|
|
|
@ -29,7 +29,8 @@
|
|||
unsigned int cdnsp_port_speed(unsigned int port_status)
|
||||
{
|
||||
/*Detect gadget speed based on PORTSC register*/
|
||||
if (DEV_SUPERSPEEDPLUS(port_status))
|
||||
if (DEV_SUPERSPEEDPLUS(port_status) ||
|
||||
DEV_SSP_GEN1x2(port_status) || DEV_SSP_GEN2x2(port_status))
|
||||
return USB_SPEED_SUPER_PLUS;
|
||||
else if (DEV_SUPERSPEED(port_status))
|
||||
return USB_SPEED_SUPER;
|
||||
|
@ -547,6 +548,7 @@ int cdnsp_wait_for_cmd_compl(struct cdnsp_device *pdev)
|
|||
dma_addr_t cmd_deq_dma;
|
||||
union cdnsp_trb *event;
|
||||
u32 cycle_state;
|
||||
u32 retry = 10;
|
||||
int ret, val;
|
||||
u64 cmd_dma;
|
||||
u32 flags;
|
||||
|
@ -578,8 +580,23 @@ int cdnsp_wait_for_cmd_compl(struct cdnsp_device *pdev)
|
|||
flags = le32_to_cpu(event->event_cmd.flags);
|
||||
|
||||
/* Check the owner of the TRB. */
|
||||
if ((flags & TRB_CYCLE) != cycle_state)
|
||||
if ((flags & TRB_CYCLE) != cycle_state) {
|
||||
/*
|
||||
* Give some extra time to get chance controller
|
||||
* to finish command before returning error code.
|
||||
* Checking CMD_RING_BUSY is not sufficient because
|
||||
* this bit is cleared to '0' when the Command
|
||||
* Descriptor has been executed by controller
|
||||
* and not when command completion event has
|
||||
* be added to event ring.
|
||||
*/
|
||||
if (retry--) {
|
||||
udelay(20);
|
||||
continue;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cmd_dma = le64_to_cpu(event->event_cmd.cmd_trb);
|
||||
|
||||
|
|
|
@ -285,11 +285,15 @@ struct cdnsp_port_regs {
|
|||
#define XDEV_HS (0x3 << 10)
|
||||
#define XDEV_SS (0x4 << 10)
|
||||
#define XDEV_SSP (0x5 << 10)
|
||||
#define XDEV_SSP1x2 (0x6 << 10)
|
||||
#define XDEV_SSP2x2 (0x7 << 10)
|
||||
#define DEV_UNDEFSPEED(p) (((p) & DEV_SPEED_MASK) == (0x0 << 10))
|
||||
#define DEV_FULLSPEED(p) (((p) & DEV_SPEED_MASK) == XDEV_FS)
|
||||
#define DEV_HIGHSPEED(p) (((p) & DEV_SPEED_MASK) == XDEV_HS)
|
||||
#define DEV_SUPERSPEED(p) (((p) & DEV_SPEED_MASK) == XDEV_SS)
|
||||
#define DEV_SUPERSPEEDPLUS(p) (((p) & DEV_SPEED_MASK) == XDEV_SSP)
|
||||
#define DEV_SSP_GEN1x2(p) (((p) & DEV_SPEED_MASK) == XDEV_SSP1x2)
|
||||
#define DEV_SSP_GEN2x2(p) (((p) & DEV_SPEED_MASK) == XDEV_SSP2x2)
|
||||
#define DEV_SUPERSPEED_ANY(p) (((p) & DEV_SPEED_MASK) >= XDEV_SS)
|
||||
#define DEV_PORT_SPEED(p) (((p) >> 10) & 0x0f)
|
||||
/* Port Link State Write Strobe - set this when changing link state */
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
@ -98,6 +99,7 @@ struct ci_hdrc_imx_data {
|
|||
struct clk *clk;
|
||||
struct clk *clk_wakeup;
|
||||
struct imx_usbmisc_data *usbmisc_data;
|
||||
int wakeup_irq;
|
||||
bool supports_runtime_pm;
|
||||
bool override_phy_control;
|
||||
bool in_lpm;
|
||||
|
@ -336,6 +338,16 @@ static int ci_hdrc_imx_notify_event(struct ci_hdrc *ci, unsigned int event)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t ci_wakeup_irq_handler(int irq, void *data)
|
||||
{
|
||||
struct ci_hdrc_imx_data *imx_data = data;
|
||||
|
||||
disable_irq_nosync(irq);
|
||||
pm_runtime_resume(&imx_data->ci_pdev->dev);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void ci_hdrc_imx_disable_regulator(void *arg)
|
||||
{
|
||||
struct ci_hdrc_imx_data *data = arg;
|
||||
|
@ -494,6 +506,16 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
|
|||
if (pdata.flags & CI_HDRC_SUPPORTS_RUNTIME_PM)
|
||||
data->supports_runtime_pm = true;
|
||||
|
||||
data->wakeup_irq = platform_get_irq_optional(pdev, 1);
|
||||
if (data->wakeup_irq > 0) {
|
||||
ret = devm_request_threaded_irq(dev, data->wakeup_irq,
|
||||
NULL, ci_wakeup_irq_handler,
|
||||
IRQF_ONESHOT | IRQF_NO_AUTOEN,
|
||||
pdata.name, data);
|
||||
if (ret)
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
ret = imx_usbmisc_init(data->usbmisc_data);
|
||||
if (ret) {
|
||||
dev_err(dev, "usbmisc init failed, ret=%d\n", ret);
|
||||
|
@ -602,6 +624,10 @@ static int imx_controller_suspend(struct device *dev,
|
|||
}
|
||||
|
||||
imx_disable_unprepare_clks(dev);
|
||||
|
||||
if (data->wakeup_irq > 0)
|
||||
enable_irq(data->wakeup_irq);
|
||||
|
||||
if (data->plat_data->flags & CI_HDRC_PMQOS)
|
||||
cpu_latency_qos_remove_request(&data->pm_qos_req);
|
||||
|
||||
|
@ -626,6 +652,10 @@ static int imx_controller_resume(struct device *dev,
|
|||
if (data->plat_data->flags & CI_HDRC_PMQOS)
|
||||
cpu_latency_qos_add_request(&data->pm_qos_req, 0);
|
||||
|
||||
if (data->wakeup_irq > 0 &&
|
||||
!irqd_irq_disabled(irq_get_irq_data(data->wakeup_irq)))
|
||||
disable_irq_nosync(data->wakeup_irq);
|
||||
|
||||
ret = imx_prepare_enable_clks(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -661,6 +691,10 @@ static int ci_hdrc_imx_suspend(struct device *dev)
|
|||
return ret;
|
||||
|
||||
pinctrl_pm_select_sleep_state(dev);
|
||||
|
||||
if (data->wakeup_irq > 0 && device_may_wakeup(dev))
|
||||
enable_irq_wake(data->wakeup_irq);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -669,6 +703,9 @@ static int ci_hdrc_imx_resume(struct device *dev)
|
|||
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
if (data->wakeup_irq > 0 && device_may_wakeup(dev))
|
||||
disable_irq_wake(data->wakeup_irq);
|
||||
|
||||
pinctrl_pm_select_default_state(dev);
|
||||
ret = imx_controller_resume(dev, PMSG_RESUME);
|
||||
if (!ret && data->supports_runtime_pm) {
|
||||
|
|
|
@ -139,6 +139,22 @@
|
|||
#define MX6_USB_OTG_WAKEUP_BITS (MX6_BM_WAKEUP_ENABLE | MX6_BM_VBUS_WAKEUP | \
|
||||
MX6_BM_ID_WAKEUP | MX6SX_BM_DPDM_WAKEUP_EN)
|
||||
|
||||
/*
|
||||
* HSIO Block Control Register
|
||||
*/
|
||||
|
||||
#define BLKCTL_USB_WAKEUP_CTRL 0x0
|
||||
#define BLKCTL_OTG_WAKE_ENABLE BIT(31)
|
||||
#define BLKCTL_OTG_VBUS_SESSVALID BIT(4)
|
||||
#define BLKCTL_OTG_ID_WAKEUP_EN BIT(2)
|
||||
#define BLKCTL_OTG_VBUS_WAKEUP_EN BIT(1)
|
||||
#define BLKCTL_OTG_DPDM_WAKEUP_EN BIT(0)
|
||||
|
||||
#define BLKCTL_WAKEUP_SOURCE (BLKCTL_OTG_WAKE_ENABLE | \
|
||||
BLKCTL_OTG_ID_WAKEUP_EN | \
|
||||
BLKCTL_OTG_VBUS_WAKEUP_EN | \
|
||||
BLKCTL_OTG_DPDM_WAKEUP_EN)
|
||||
|
||||
struct usbmisc_ops {
|
||||
/* It's called once when probe a usb device */
|
||||
int (*init)(struct imx_usbmisc_data *data);
|
||||
|
@ -159,6 +175,7 @@ struct usbmisc_ops {
|
|||
|
||||
struct imx_usbmisc {
|
||||
void __iomem *base;
|
||||
void __iomem *blkctl;
|
||||
spinlock_t lock;
|
||||
const struct usbmisc_ops *ops;
|
||||
};
|
||||
|
@ -1016,6 +1033,44 @@ static int usbmisc_imx6sx_power_lost_check(struct imx_usbmisc_data *data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static u32 usbmisc_blkctl_wakeup_setting(struct imx_usbmisc_data *data)
|
||||
{
|
||||
u32 wakeup_setting = BLKCTL_WAKEUP_SOURCE;
|
||||
|
||||
if (data->ext_id || data->available_role != USB_DR_MODE_OTG)
|
||||
wakeup_setting &= ~BLKCTL_OTG_ID_WAKEUP_EN;
|
||||
|
||||
if (data->ext_vbus || data->available_role == USB_DR_MODE_HOST)
|
||||
wakeup_setting &= ~BLKCTL_OTG_VBUS_WAKEUP_EN;
|
||||
|
||||
/* Select session valid as VBUS wakeup source */
|
||||
wakeup_setting |= BLKCTL_OTG_VBUS_SESSVALID;
|
||||
|
||||
return wakeup_setting;
|
||||
}
|
||||
|
||||
static int usbmisc_imx95_set_wakeup(struct imx_usbmisc_data *data, bool enabled)
|
||||
{
|
||||
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
if (!usbmisc->blkctl)
|
||||
return 0;
|
||||
|
||||
spin_lock_irqsave(&usbmisc->lock, flags);
|
||||
val = readl(usbmisc->blkctl + BLKCTL_USB_WAKEUP_CTRL);
|
||||
val &= ~BLKCTL_WAKEUP_SOURCE;
|
||||
|
||||
if (enabled)
|
||||
val |= usbmisc_blkctl_wakeup_setting(data);
|
||||
|
||||
writel(val, usbmisc->blkctl + BLKCTL_USB_WAKEUP_CTRL);
|
||||
spin_unlock_irqrestore(&usbmisc->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct usbmisc_ops imx25_usbmisc_ops = {
|
||||
.init = usbmisc_imx25_init,
|
||||
.post = usbmisc_imx25_post,
|
||||
|
@ -1068,6 +1123,14 @@ static const struct usbmisc_ops imx7ulp_usbmisc_ops = {
|
|||
.power_lost_check = usbmisc_imx7d_power_lost_check,
|
||||
};
|
||||
|
||||
static const struct usbmisc_ops imx95_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 inline bool is_imx53_usbmisc(struct imx_usbmisc_data *data)
|
||||
{
|
||||
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
|
||||
|
@ -1289,6 +1352,10 @@ static const struct of_device_id usbmisc_imx_dt_ids[] = {
|
|||
.compatible = "fsl,imx8ulp-usbmisc",
|
||||
.data = &imx7ulp_usbmisc_ops,
|
||||
},
|
||||
{
|
||||
.compatible = "fsl,imx95-usbmisc",
|
||||
.data = &imx95_usbmisc_ops,
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, usbmisc_imx_dt_ids);
|
||||
|
@ -1296,6 +1363,7 @@ MODULE_DEVICE_TABLE(of, usbmisc_imx_dt_ids);
|
|||
static int usbmisc_imx_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct imx_usbmisc *data;
|
||||
struct resource *res;
|
||||
|
||||
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
|
@ -1307,6 +1375,15 @@ static int usbmisc_imx_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(data->base))
|
||||
return PTR_ERR(data->base);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
if (res) {
|
||||
data->blkctl = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(data->blkctl))
|
||||
return PTR_ERR(data->blkctl);
|
||||
} else if (device_is_compatible(&pdev->dev, "fsl,imx95-usbmisc")) {
|
||||
dev_warn(&pdev->dev, "wakeup setting is missing\n");
|
||||
}
|
||||
|
||||
data->ops = of_device_get_match_data(&pdev->dev);
|
||||
platform_set_drvdata(pdev, data);
|
||||
|
||||
|
|
|
@ -92,7 +92,6 @@ struct wdm_device {
|
|||
u16 wMaxCommand;
|
||||
u16 wMaxPacketSize;
|
||||
__le16 inum;
|
||||
int reslength;
|
||||
int length;
|
||||
int read;
|
||||
int count;
|
||||
|
@ -214,6 +213,11 @@ static void wdm_in_callback(struct urb *urb)
|
|||
if (desc->rerr == 0 && status != -EPIPE)
|
||||
desc->rerr = status;
|
||||
|
||||
if (length == 0) {
|
||||
dev_dbg(&desc->intf->dev, "received ZLP\n");
|
||||
goto skip_zlp;
|
||||
}
|
||||
|
||||
if (length + desc->length > desc->wMaxCommand) {
|
||||
/* The buffer would overflow */
|
||||
set_bit(WDM_OVERFLOW, &desc->flags);
|
||||
|
@ -222,18 +226,18 @@ static void wdm_in_callback(struct urb *urb)
|
|||
if (!test_bit(WDM_OVERFLOW, &desc->flags)) {
|
||||
memmove(desc->ubuf + desc->length, desc->inbuf, length);
|
||||
desc->length += length;
|
||||
desc->reslength = length;
|
||||
}
|
||||
}
|
||||
skip_error:
|
||||
|
||||
if (desc->rerr) {
|
||||
/*
|
||||
* Since there was an error, userspace may decide to not read
|
||||
* any data after poll'ing.
|
||||
* If there was a ZLP or an error, userspace may decide to not
|
||||
* read any data after poll'ing.
|
||||
* We should respond to further attempts from the device to send
|
||||
* data, so that we can get unstuck.
|
||||
*/
|
||||
skip_zlp:
|
||||
schedule_work(&desc->service_outs_intr);
|
||||
} else {
|
||||
set_bit(WDM_READ, &desc->flags);
|
||||
|
@ -585,15 +589,6 @@ retry:
|
|||
goto retry;
|
||||
}
|
||||
|
||||
if (!desc->reslength) { /* zero length read */
|
||||
dev_dbg(&desc->intf->dev, "zero length - clearing WDM_READ\n");
|
||||
clear_bit(WDM_READ, &desc->flags);
|
||||
rv = service_outstanding_interrupt(desc);
|
||||
spin_unlock_irq(&desc->iuspin);
|
||||
if (rv < 0)
|
||||
goto err;
|
||||
goto retry;
|
||||
}
|
||||
cntr = desc->length;
|
||||
spin_unlock_irq(&desc->iuspin);
|
||||
}
|
||||
|
@ -1016,7 +1011,7 @@ static void service_interrupt_work(struct work_struct *work)
|
|||
|
||||
spin_lock_irq(&desc->iuspin);
|
||||
service_outstanding_interrupt(desc);
|
||||
if (!desc->resp_count) {
|
||||
if (!desc->resp_count && (desc->length || desc->rerr)) {
|
||||
set_bit(WDM_READ, &desc->flags);
|
||||
wake_up(&desc->wait);
|
||||
}
|
||||
|
|
|
@ -483,6 +483,7 @@ static int usbtmc_get_stb(struct usbtmc_file_data *file_data, __u8 *stb)
|
|||
u8 tag;
|
||||
int rv;
|
||||
long wait_rv;
|
||||
unsigned long expire;
|
||||
|
||||
dev_dbg(dev, "Enter ioctl_read_stb iin_ep_present: %d\n",
|
||||
data->iin_ep_present);
|
||||
|
@ -512,10 +513,11 @@ static int usbtmc_get_stb(struct usbtmc_file_data *file_data, __u8 *stb)
|
|||
}
|
||||
|
||||
if (data->iin_ep_present) {
|
||||
expire = msecs_to_jiffies(file_data->timeout);
|
||||
wait_rv = wait_event_interruptible_timeout(
|
||||
data->waitq,
|
||||
atomic_read(&data->iin_data_valid) != 0,
|
||||
file_data->timeout);
|
||||
expire);
|
||||
if (wait_rv < 0) {
|
||||
dev_dbg(dev, "wait interrupted %ld\n", wait_rv);
|
||||
rv = wait_rv;
|
||||
|
@ -563,14 +565,15 @@ static int usbtmc488_ioctl_read_stb(struct usbtmc_file_data *file_data,
|
|||
|
||||
rv = usbtmc_get_stb(file_data, &stb);
|
||||
|
||||
if (rv > 0) {
|
||||
srq_asserted = atomic_xchg(&file_data->srq_asserted,
|
||||
srq_asserted);
|
||||
if (srq_asserted)
|
||||
stb |= 0x40; /* Set RQS bit */
|
||||
if (rv < 0)
|
||||
return rv;
|
||||
|
||||
srq_asserted = atomic_xchg(&file_data->srq_asserted, srq_asserted);
|
||||
if (srq_asserted)
|
||||
stb |= 0x40; /* Set RQS bit */
|
||||
|
||||
rv = put_user(stb, (__u8 __user *)arg);
|
||||
|
||||
rv = put_user(stb, (__u8 __user *)arg);
|
||||
}
|
||||
return rv;
|
||||
|
||||
}
|
||||
|
@ -2199,7 +2202,7 @@ static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|||
|
||||
case USBTMC_IOCTL_GET_STB:
|
||||
retval = usbtmc_get_stb(file_data, &tmp_byte);
|
||||
if (retval > 0)
|
||||
if (!retval)
|
||||
retval = put_user(tmp_byte, (__u8 __user *)arg);
|
||||
break;
|
||||
|
||||
|
|
|
@ -21,6 +21,9 @@
|
|||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/string_choices.h>
|
||||
#include <linux/usb/role.h>
|
||||
#include <linux/idr.h>
|
||||
|
||||
static DEFINE_IDA(usb_conn_ida);
|
||||
|
||||
#define USB_GPIO_DEB_MS 20 /* ms */
|
||||
#define USB_GPIO_DEB_US ((USB_GPIO_DEB_MS) * 1000) /* us */
|
||||
|
@ -30,6 +33,7 @@
|
|||
|
||||
struct usb_conn_info {
|
||||
struct device *dev;
|
||||
int conn_id; /* store the IDA-allocated ID */
|
||||
struct usb_role_switch *role_sw;
|
||||
enum usb_role last_role;
|
||||
struct regulator *vbus;
|
||||
|
@ -161,7 +165,17 @@ static int usb_conn_psy_register(struct usb_conn_info *info)
|
|||
.fwnode = dev_fwnode(dev),
|
||||
};
|
||||
|
||||
desc->name = "usb-charger";
|
||||
info->conn_id = ida_alloc(&usb_conn_ida, GFP_KERNEL);
|
||||
if (info->conn_id < 0)
|
||||
return info->conn_id;
|
||||
|
||||
desc->name = devm_kasprintf(dev, GFP_KERNEL, "usb-charger-%d",
|
||||
info->conn_id);
|
||||
if (!desc->name) {
|
||||
ida_free(&usb_conn_ida, info->conn_id);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
desc->properties = usb_charger_properties;
|
||||
desc->num_properties = ARRAY_SIZE(usb_charger_properties);
|
||||
desc->get_property = usb_charger_get_property;
|
||||
|
@ -169,8 +183,10 @@ static int usb_conn_psy_register(struct usb_conn_info *info)
|
|||
cfg.drv_data = info;
|
||||
|
||||
info->charger = devm_power_supply_register(dev, desc, &cfg);
|
||||
if (IS_ERR(info->charger))
|
||||
dev_err(dev, "Unable to register charger\n");
|
||||
if (IS_ERR(info->charger)) {
|
||||
dev_err(dev, "Unable to register charger %d\n", info->conn_id);
|
||||
ida_free(&usb_conn_ida, info->conn_id);
|
||||
}
|
||||
|
||||
return PTR_ERR_OR_ZERO(info->charger);
|
||||
}
|
||||
|
@ -278,6 +294,9 @@ static void usb_conn_remove(struct platform_device *pdev)
|
|||
|
||||
cancel_delayed_work_sync(&info->dw_det);
|
||||
|
||||
if (info->charger)
|
||||
ida_free(&usb_conn_ida, info->conn_id);
|
||||
|
||||
if (info->last_role == USB_ROLE_HOST && info->vbus)
|
||||
regulator_disable(info->vbus);
|
||||
|
||||
|
|
|
@ -307,7 +307,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno,
|
|||
goto skip_to_next_endpoint_or_interface_descriptor;
|
||||
}
|
||||
|
||||
i = d->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
|
||||
i = usb_endpoint_num(d);
|
||||
if (i == 0) {
|
||||
dev_notice(ddev, "config %d interface %d altsetting %d has an "
|
||||
"invalid descriptor for endpoint zero, skipping\n",
|
||||
|
|
|
@ -4160,7 +4160,7 @@ static int usb_set_device_initiated_lpm(struct usb_device *udev,
|
|||
"for unconfigured device.\n",
|
||||
__func__, str_enable_disable(enable),
|
||||
usb3_lpm_names[state]);
|
||||
return 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
|
@ -4234,9 +4234,9 @@ static int usb_set_lpm_timeout(struct usb_device *udev,
|
|||
}
|
||||
|
||||
/*
|
||||
* Don't allow device intiated U1/U2 if the system exit latency + one bus
|
||||
* interval is greater than the minimum service interval of any active
|
||||
* periodic endpoint. See USB 3.2 section 9.4.9
|
||||
* Don't allow device intiated U1/U2 if device isn't in the configured state,
|
||||
* or the system exit latency + one bus interval is greater than the minimum
|
||||
* service interval of any active periodic endpoint. See USB 3.2 section 9.4.9
|
||||
*/
|
||||
static bool usb_device_may_initiate_lpm(struct usb_device *udev,
|
||||
enum usb3_link_state state)
|
||||
|
@ -4244,7 +4244,7 @@ static bool usb_device_may_initiate_lpm(struct usb_device *udev,
|
|||
unsigned int sel; /* us */
|
||||
int i, j;
|
||||
|
||||
if (!udev->lpm_devinit_allow)
|
||||
if (!udev->lpm_devinit_allow || !udev->actconfig)
|
||||
return false;
|
||||
|
||||
if (state == USB3_LPM_U1)
|
||||
|
@ -4292,7 +4292,7 @@ static bool usb_device_may_initiate_lpm(struct usb_device *udev,
|
|||
* driver know about it. If that call fails, it should be harmless, and just
|
||||
* take up more slightly more bus bandwidth for unnecessary U1/U2 exit latency.
|
||||
*/
|
||||
static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
|
||||
static int usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
|
||||
enum usb3_link_state state)
|
||||
{
|
||||
int timeout;
|
||||
|
@ -4301,7 +4301,7 @@ static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
|
|||
|
||||
/* Skip if the device BOS descriptor couldn't be read */
|
||||
if (!udev->bos)
|
||||
return;
|
||||
return -EINVAL;
|
||||
|
||||
u1_mel = udev->bos->ss_cap->bU1devExitLat;
|
||||
u2_mel = udev->bos->ss_cap->bU2DevExitLat;
|
||||
|
@ -4312,7 +4312,7 @@ static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
|
|||
*/
|
||||
if ((state == USB3_LPM_U1 && u1_mel == 0) ||
|
||||
(state == USB3_LPM_U2 && u2_mel == 0))
|
||||
return;
|
||||
return -EINVAL;
|
||||
|
||||
/* We allow the host controller to set the U1/U2 timeout internally
|
||||
* first, so that it can change its schedule to account for the
|
||||
|
@ -4323,13 +4323,13 @@ static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
|
|||
|
||||
/* xHCI host controller doesn't want to enable this LPM state. */
|
||||
if (timeout == 0)
|
||||
return;
|
||||
return -EINVAL;
|
||||
|
||||
if (timeout < 0) {
|
||||
dev_warn(&udev->dev, "Could not enable %s link state, "
|
||||
"xHCI error %i.\n", usb3_lpm_names[state],
|
||||
timeout);
|
||||
return;
|
||||
return timeout;
|
||||
}
|
||||
|
||||
if (usb_set_lpm_timeout(udev, state, timeout)) {
|
||||
|
@ -4338,29 +4338,15 @@ static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
|
|||
* host know that this link state won't be enabled.
|
||||
*/
|
||||
hcd->driver->disable_usb3_lpm_timeout(hcd, udev, state);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Only a configured device will accept the Set Feature
|
||||
* U1/U2_ENABLE
|
||||
*/
|
||||
if (udev->actconfig &&
|
||||
usb_device_may_initiate_lpm(udev, state)) {
|
||||
if (usb_set_device_initiated_lpm(udev, state, true)) {
|
||||
/*
|
||||
* Request to enable device initiated U1/U2 failed,
|
||||
* better to turn off lpm in this case.
|
||||
*/
|
||||
usb_set_lpm_timeout(udev, state, 0);
|
||||
hcd->driver->disable_usb3_lpm_timeout(hcd, udev, state);
|
||||
return;
|
||||
}
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (state == USB3_LPM_U1)
|
||||
udev->usb3_lpm_u1_enabled = 1;
|
||||
else if (state == USB3_LPM_U2)
|
||||
udev->usb3_lpm_u2_enabled = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* Disable the hub-initiated U1/U2 idle timeouts, and disable device-initiated
|
||||
|
@ -4393,8 +4379,6 @@ static int usb_disable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
|
|||
if (usb_set_lpm_timeout(udev, state, 0))
|
||||
return -EBUSY;
|
||||
|
||||
usb_set_device_initiated_lpm(udev, state, false);
|
||||
|
||||
if (hcd->driver->disable_usb3_lpm_timeout(hcd, udev, state))
|
||||
dev_warn(&udev->dev, "Could not disable xHCI %s timeout, "
|
||||
"bus schedule bandwidth may be impacted.\n",
|
||||
|
@ -4424,6 +4408,7 @@ static int usb_disable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
|
|||
int usb_disable_lpm(struct usb_device *udev)
|
||||
{
|
||||
struct usb_hcd *hcd;
|
||||
int err;
|
||||
|
||||
if (!udev || !udev->parent ||
|
||||
udev->speed < USB_SPEED_SUPER ||
|
||||
|
@ -4441,14 +4426,19 @@ int usb_disable_lpm(struct usb_device *udev)
|
|||
|
||||
/* If LPM is enabled, attempt to disable it. */
|
||||
if (usb_disable_link_state(hcd, udev, USB3_LPM_U1))
|
||||
goto enable_lpm;
|
||||
goto disable_failed;
|
||||
if (usb_disable_link_state(hcd, udev, USB3_LPM_U2))
|
||||
goto enable_lpm;
|
||||
goto disable_failed;
|
||||
|
||||
err = usb_set_device_initiated_lpm(udev, USB3_LPM_U1, false);
|
||||
if (!err)
|
||||
usb_set_device_initiated_lpm(udev, USB3_LPM_U2, false);
|
||||
|
||||
return 0;
|
||||
|
||||
enable_lpm:
|
||||
usb_enable_lpm(udev);
|
||||
disable_failed:
|
||||
udev->lpm_disable_count--;
|
||||
|
||||
return -EBUSY;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_disable_lpm);
|
||||
|
@ -4509,10 +4499,24 @@ void usb_enable_lpm(struct usb_device *udev)
|
|||
port_dev = hub->ports[udev->portnum - 1];
|
||||
|
||||
if (port_dev->usb3_lpm_u1_permit)
|
||||
usb_enable_link_state(hcd, udev, USB3_LPM_U1);
|
||||
if (usb_enable_link_state(hcd, udev, USB3_LPM_U1))
|
||||
return;
|
||||
|
||||
if (port_dev->usb3_lpm_u2_permit)
|
||||
usb_enable_link_state(hcd, udev, USB3_LPM_U2);
|
||||
if (usb_enable_link_state(hcd, udev, USB3_LPM_U2))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Enable device initiated U1/U2 with a SetFeature(U1/U2_ENABLE) request
|
||||
* if system exit latency is short enough and device is configured
|
||||
*/
|
||||
if (usb_device_may_initiate_lpm(udev, USB3_LPM_U1)) {
|
||||
if (usb_set_device_initiated_lpm(udev, USB3_LPM_U1, true))
|
||||
return;
|
||||
|
||||
if (usb_device_may_initiate_lpm(udev, USB3_LPM_U2))
|
||||
usb_set_device_initiated_lpm(udev, USB3_LPM_U2, true);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_enable_lpm);
|
||||
|
||||
|
@ -6133,6 +6137,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
|
|||
struct usb_hub *parent_hub;
|
||||
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
|
||||
struct usb_device_descriptor descriptor;
|
||||
struct usb_interface *intf;
|
||||
struct usb_host_bos *bos;
|
||||
int i, j, ret = 0;
|
||||
int port1 = udev->portnum;
|
||||
|
@ -6190,6 +6195,18 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
|
|||
if (!udev->actconfig)
|
||||
goto done;
|
||||
|
||||
/*
|
||||
* Some devices can't handle setting default altsetting 0 with a
|
||||
* Set-Interface request. Disable host-side endpoints of those
|
||||
* interfaces here. Enable and reset them back after host has set
|
||||
* its internal endpoint structures during usb_hcd_alloc_bandwith()
|
||||
*/
|
||||
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
|
||||
intf = udev->actconfig->interface[i];
|
||||
if (intf->cur_altsetting->desc.bAlternateSetting == 0)
|
||||
usb_disable_interface(udev, intf, true);
|
||||
}
|
||||
|
||||
mutex_lock(hcd->bandwidth_mutex);
|
||||
ret = usb_hcd_alloc_bandwidth(udev, udev->actconfig, NULL, NULL);
|
||||
if (ret < 0) {
|
||||
|
@ -6221,12 +6238,11 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
|
|||
*/
|
||||
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
|
||||
struct usb_host_config *config = udev->actconfig;
|
||||
struct usb_interface *intf = config->interface[i];
|
||||
struct usb_interface_descriptor *desc;
|
||||
|
||||
intf = config->interface[i];
|
||||
desc = &intf->cur_altsetting->desc;
|
||||
if (desc->bAlternateSetting == 0) {
|
||||
usb_disable_interface(udev, intf, true);
|
||||
usb_enable_interface(udev, intf, true);
|
||||
ret = 0;
|
||||
} else {
|
||||
|
|
|
@ -372,6 +372,9 @@ static const struct usb_device_id usb_quirk_list[] = {
|
|||
/* SanDisk Corp. SanDisk 3.2Gen1 */
|
||||
{ USB_DEVICE(0x0781, 0x55a3), .driver_info = USB_QUIRK_DELAY_INIT },
|
||||
|
||||
/* SanDisk Extreme 55AE */
|
||||
{ USB_DEVICE(0x0781, 0x55ae), .driver_info = USB_QUIRK_NO_LPM },
|
||||
|
||||
/* Realforce 87U Keyboard */
|
||||
{ USB_DEVICE(0x0853, 0x011b), .driver_info = USB_QUIRK_NO_LPM },
|
||||
|
||||
|
|
|
@ -165,6 +165,8 @@ static int usb_acpi_add_usb4_devlink(struct usb_device *udev)
|
|||
return 0;
|
||||
|
||||
hub = usb_hub_to_struct_hub(udev->parent);
|
||||
if (!hub)
|
||||
return 0;
|
||||
port_dev = hub->ports[udev->portnum - 1];
|
||||
|
||||
struct fwnode_handle *nhi_fwnode __free(fwnode_handle) =
|
||||
|
|
|
@ -695,15 +695,16 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
|
|||
device_set_of_node_from_dev(&dev->dev, bus->sysdev);
|
||||
dev_set_name(&dev->dev, "usb%d", bus->busnum);
|
||||
} else {
|
||||
int n;
|
||||
|
||||
/* match any labeling on the hubs; it's one-based */
|
||||
if (parent->devpath[0] == '0') {
|
||||
snprintf(dev->devpath, sizeof dev->devpath,
|
||||
"%d", port1);
|
||||
n = snprintf(dev->devpath, sizeof(dev->devpath), "%d", port1);
|
||||
/* Root ports are not counted in route string */
|
||||
dev->route = 0;
|
||||
} else {
|
||||
snprintf(dev->devpath, sizeof dev->devpath,
|
||||
"%s.%d", parent->devpath, port1);
|
||||
n = snprintf(dev->devpath, sizeof(dev->devpath), "%s.%d",
|
||||
parent->devpath, port1);
|
||||
/* Route string assumes hubs have less than 16 ports */
|
||||
if (port1 < 15)
|
||||
dev->route = parent->route +
|
||||
|
@ -712,6 +713,11 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
|
|||
dev->route = parent->route +
|
||||
(15 << ((parent->level - 1)*4));
|
||||
}
|
||||
if (n >= sizeof(dev->devpath)) {
|
||||
usb_put_hcd(bus_to_hcd(bus));
|
||||
usb_put_dev(dev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dev->dev.parent = &parent->dev;
|
||||
dev_set_name(&dev->dev, "%d-%s", bus->busnum, dev->devpath);
|
||||
|
|
|
@ -4049,7 +4049,7 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
ep_type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
|
||||
ep_type = usb_endpoint_type(desc);
|
||||
mps = usb_endpoint_maxp(desc);
|
||||
mc = usb_endpoint_maxp_mult(desc);
|
||||
|
||||
|
@ -4604,6 +4604,12 @@ static int dwc2_hsotg_udc_stop(struct usb_gadget *gadget)
|
|||
if (!hsotg)
|
||||
return -ENODEV;
|
||||
|
||||
/* Exit clock gating when driver is stopped. */
|
||||
if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_NONE &&
|
||||
hsotg->bus_suspended && !hsotg->params.no_clock_gating) {
|
||||
dwc2_gadget_exit_clock_gating(hsotg, 0);
|
||||
}
|
||||
|
||||
/* all endpoints should be shutdown */
|
||||
for (ep = 1; ep < hsotg->num_of_eps; ep++) {
|
||||
if (hsotg->eps_in[ep])
|
||||
|
|
|
@ -52,6 +52,7 @@ obj-$(CONFIG_USB_DWC3_MESON_G12A) += dwc3-meson-g12a.o
|
|||
obj-$(CONFIG_USB_DWC3_OF_SIMPLE) += dwc3-of-simple.o
|
||||
obj-$(CONFIG_USB_DWC3_ST) += dwc3-st.o
|
||||
obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom.o
|
||||
obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom-legacy.o
|
||||
obj-$(CONFIG_USB_DWC3_IMX8MP) += dwc3-imx8mp.o
|
||||
obj-$(CONFIG_USB_DWC3_XILINX) += dwc3-xilinx.o
|
||||
obj-$(CONFIG_USB_DWC3_OCTEON) += dwc3-octeon.o
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <linux/of_graph.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/pinctrl/devinfo.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/bitfield.h>
|
||||
|
||||
|
@ -36,6 +37,7 @@
|
|||
|
||||
#include "core.h"
|
||||
#include "gadget.h"
|
||||
#include "glue.h"
|
||||
#include "io.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
@ -1699,6 +1701,7 @@ static void dwc3_get_properties(struct dwc3 *dwc)
|
|||
u8 tx_thr_num_pkt_prd = 0;
|
||||
u8 tx_max_burst_prd = 0;
|
||||
u8 tx_fifo_resize_max_num;
|
||||
u16 num_hc_interrupters;
|
||||
|
||||
/* default to highest possible threshold */
|
||||
lpm_nyet_threshold = 0xf;
|
||||
|
@ -1719,6 +1722,9 @@ static void dwc3_get_properties(struct dwc3 *dwc)
|
|||
*/
|
||||
tx_fifo_resize_max_num = 6;
|
||||
|
||||
/* default to a single XHCI interrupter */
|
||||
num_hc_interrupters = 1;
|
||||
|
||||
dwc->maximum_speed = usb_get_maximum_speed(dev);
|
||||
dwc->max_ssp_rate = usb_get_maximum_ssp_rate(dev);
|
||||
dwc->dr_mode = usb_get_dr_mode(dev);
|
||||
|
@ -1765,6 +1771,12 @@ static void dwc3_get_properties(struct dwc3 *dwc)
|
|||
&tx_thr_num_pkt_prd);
|
||||
device_property_read_u8(dev, "snps,tx-max-burst-prd",
|
||||
&tx_max_burst_prd);
|
||||
device_property_read_u16(dev, "num-hc-interrupters",
|
||||
&num_hc_interrupters);
|
||||
/* DWC3 core allowed to have a max of 8 interrupters */
|
||||
if (num_hc_interrupters > 8)
|
||||
num_hc_interrupters = 8;
|
||||
|
||||
dwc->do_fifo_resize = device_property_read_bool(dev,
|
||||
"tx-fifo-resize");
|
||||
if (dwc->do_fifo_resize)
|
||||
|
@ -1851,6 +1863,8 @@ static void dwc3_get_properties(struct dwc3 *dwc)
|
|||
dwc->tx_max_burst_prd = tx_max_burst_prd;
|
||||
|
||||
dwc->tx_fifo_resize_max_num = tx_fifo_resize_max_num;
|
||||
|
||||
dwc->num_hc_interrupters = num_hc_interrupters;
|
||||
}
|
||||
|
||||
/* check whether the core supports IMOD */
|
||||
|
@ -2148,27 +2162,16 @@ static struct power_supply *dwc3_get_usb_power_supply(struct dwc3 *dwc)
|
|||
return usb_psy;
|
||||
}
|
||||
|
||||
static int dwc3_probe(struct platform_device *pdev)
|
||||
int dwc3_core_probe(const struct dwc3_probe_data *data)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res, dwc_res;
|
||||
struct dwc3 *dwc = data->dwc;
|
||||
struct device *dev = dwc->dev;
|
||||
struct resource dwc_res;
|
||||
unsigned int hw_mode;
|
||||
void __iomem *regs;
|
||||
struct dwc3 *dwc;
|
||||
struct resource *res = data->res;
|
||||
int ret;
|
||||
|
||||
dwc = devm_kzalloc(dev, sizeof(*dwc), GFP_KERNEL);
|
||||
if (!dwc)
|
||||
return -ENOMEM;
|
||||
|
||||
dwc->dev = dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "missing memory resource\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dwc->xhci_resources[0].start = res->start;
|
||||
dwc->xhci_resources[0].end = dwc->xhci_resources[0].start +
|
||||
DWC3_XHCI_REGS_END;
|
||||
|
@ -2208,15 +2211,17 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(dwc->usb_psy))
|
||||
return dev_err_probe(dev, PTR_ERR(dwc->usb_psy), "couldn't get usb power supply\n");
|
||||
|
||||
dwc->reset = devm_reset_control_array_get_optional_shared(dev);
|
||||
if (IS_ERR(dwc->reset)) {
|
||||
ret = PTR_ERR(dwc->reset);
|
||||
goto err_put_psy;
|
||||
}
|
||||
if (!data->ignore_clocks_and_resets) {
|
||||
dwc->reset = devm_reset_control_array_get_optional_shared(dev);
|
||||
if (IS_ERR(dwc->reset)) {
|
||||
ret = PTR_ERR(dwc->reset);
|
||||
goto err_put_psy;
|
||||
}
|
||||
|
||||
ret = dwc3_get_clocks(dwc);
|
||||
if (ret)
|
||||
goto err_put_psy;
|
||||
ret = dwc3_get_clocks(dwc);
|
||||
if (ret)
|
||||
goto err_put_psy;
|
||||
}
|
||||
|
||||
ret = reset_control_deassert(dwc->reset);
|
||||
if (ret)
|
||||
|
@ -2232,7 +2237,7 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
goto err_disable_clks;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, dwc);
|
||||
dev_set_drvdata(dev, dwc);
|
||||
dwc3_cache_hwparams(dwc);
|
||||
|
||||
if (!dwc->sysdev_is_parent &&
|
||||
|
@ -2327,12 +2332,35 @@ err_put_psy:
|
|||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dwc3_core_probe);
|
||||
|
||||
static void dwc3_remove(struct platform_device *pdev)
|
||||
static int dwc3_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct dwc3 *dwc = platform_get_drvdata(pdev);
|
||||
struct dwc3_probe_data probe_data = {};
|
||||
struct resource *res;
|
||||
struct dwc3 *dwc;
|
||||
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "missing memory resource\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dwc = devm_kzalloc(&pdev->dev, sizeof(*dwc), GFP_KERNEL);
|
||||
if (!dwc)
|
||||
return -ENOMEM;
|
||||
|
||||
dwc->dev = &pdev->dev;
|
||||
|
||||
probe_data.dwc = dwc;
|
||||
probe_data.res = res;
|
||||
|
||||
return dwc3_core_probe(&probe_data);
|
||||
}
|
||||
|
||||
void dwc3_core_remove(struct dwc3 *dwc)
|
||||
{
|
||||
pm_runtime_get_sync(dwc->dev);
|
||||
|
||||
dwc3_core_exit_mode(dwc);
|
||||
dwc3_debugfs_exit(dwc);
|
||||
|
@ -2340,22 +2368,28 @@ static void dwc3_remove(struct platform_device *pdev)
|
|||
dwc3_core_exit(dwc);
|
||||
dwc3_ulpi_exit(dwc);
|
||||
|
||||
pm_runtime_allow(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
pm_runtime_allow(dwc->dev);
|
||||
pm_runtime_disable(dwc->dev);
|
||||
pm_runtime_dont_use_autosuspend(dwc->dev);
|
||||
pm_runtime_put_noidle(dwc->dev);
|
||||
/*
|
||||
* HACK: Clear the driver data, which is currently accessed by parent
|
||||
* glue drivers, before allowing the parent to suspend.
|
||||
*/
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
pm_runtime_set_suspended(&pdev->dev);
|
||||
dev_set_drvdata(dwc->dev, NULL);
|
||||
pm_runtime_set_suspended(dwc->dev);
|
||||
|
||||
dwc3_free_event_buffers(dwc);
|
||||
|
||||
if (dwc->usb_psy)
|
||||
power_supply_put(dwc->usb_psy);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dwc3_core_remove);
|
||||
|
||||
static void dwc3_remove(struct platform_device *pdev)
|
||||
{
|
||||
dwc3_core_remove(platform_get_drvdata(pdev));
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int dwc3_core_init_for_resume(struct dwc3 *dwc)
|
||||
|
@ -2544,9 +2578,8 @@ static int dwc3_runtime_checks(struct dwc3 *dwc)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_runtime_suspend(struct device *dev)
|
||||
int dwc3_runtime_suspend(struct dwc3 *dwc)
|
||||
{
|
||||
struct dwc3 *dwc = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
if (dwc3_runtime_checks(dwc))
|
||||
|
@ -2558,10 +2591,11 @@ static int dwc3_runtime_suspend(struct device *dev)
|
|||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dwc3_runtime_suspend);
|
||||
|
||||
static int dwc3_runtime_resume(struct device *dev)
|
||||
int dwc3_runtime_resume(struct dwc3 *dwc)
|
||||
{
|
||||
struct dwc3 *dwc = dev_get_drvdata(dev);
|
||||
struct device *dev = dwc->dev;
|
||||
int ret;
|
||||
|
||||
ret = dwc3_resume_common(dwc, PMSG_AUTO_RESUME);
|
||||
|
@ -2571,7 +2605,7 @@ static int dwc3_runtime_resume(struct device *dev)
|
|||
switch (dwc->current_dr_role) {
|
||||
case DWC3_GCTL_PRTCAP_DEVICE:
|
||||
if (dwc->pending_events) {
|
||||
pm_runtime_put(dwc->dev);
|
||||
pm_runtime_put(dev);
|
||||
dwc->pending_events = false;
|
||||
enable_irq(dwc->irq_gadget);
|
||||
}
|
||||
|
@ -2586,10 +2620,11 @@ static int dwc3_runtime_resume(struct device *dev)
|
|||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dwc3_runtime_resume);
|
||||
|
||||
static int dwc3_runtime_idle(struct device *dev)
|
||||
int dwc3_runtime_idle(struct dwc3 *dwc)
|
||||
{
|
||||
struct dwc3 *dwc = dev_get_drvdata(dev);
|
||||
struct device *dev = dwc->dev;
|
||||
|
||||
switch (dwc->current_dr_role) {
|
||||
case DWC3_GCTL_PRTCAP_DEVICE:
|
||||
|
@ -2607,12 +2642,28 @@ static int dwc3_runtime_idle(struct device *dev)
|
|||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dwc3_runtime_idle);
|
||||
|
||||
static int dwc3_plat_runtime_suspend(struct device *dev)
|
||||
{
|
||||
return dwc3_runtime_suspend(dev_get_drvdata(dev));
|
||||
}
|
||||
|
||||
static int dwc3_plat_runtime_resume(struct device *dev)
|
||||
{
|
||||
return dwc3_runtime_resume(dev_get_drvdata(dev));
|
||||
}
|
||||
|
||||
static int dwc3_plat_runtime_idle(struct device *dev)
|
||||
{
|
||||
return dwc3_runtime_idle(dev_get_drvdata(dev));
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int dwc3_suspend(struct device *dev)
|
||||
int dwc3_pm_suspend(struct dwc3 *dwc)
|
||||
{
|
||||
struct dwc3 *dwc = dev_get_drvdata(dev);
|
||||
struct device *dev = dwc->dev;
|
||||
int ret;
|
||||
|
||||
ret = dwc3_suspend_common(dwc, PMSG_SUSPEND);
|
||||
|
@ -2623,10 +2674,11 @@ static int dwc3_suspend(struct device *dev)
|
|||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dwc3_pm_suspend);
|
||||
|
||||
static int dwc3_resume(struct device *dev)
|
||||
int dwc3_pm_resume(struct dwc3 *dwc)
|
||||
{
|
||||
struct dwc3 *dwc = dev_get_drvdata(dev);
|
||||
struct device *dev = dwc->dev;
|
||||
int ret = 0;
|
||||
|
||||
pinctrl_pm_select_default_state(dev);
|
||||
|
@ -2645,10 +2697,10 @@ out:
|
|||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dwc3_pm_resume);
|
||||
|
||||
static void dwc3_complete(struct device *dev)
|
||||
void dwc3_pm_complete(struct dwc3 *dwc)
|
||||
{
|
||||
struct dwc3 *dwc = dev_get_drvdata(dev);
|
||||
u32 reg;
|
||||
|
||||
if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST &&
|
||||
|
@ -2658,21 +2710,60 @@ static void dwc3_complete(struct device *dev)
|
|||
dwc3_writel(dwc->regs, DWC3_GUCTL3, reg);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dwc3_pm_complete);
|
||||
|
||||
int dwc3_pm_prepare(struct dwc3 *dwc)
|
||||
{
|
||||
struct device *dev = dwc->dev;
|
||||
|
||||
/*
|
||||
* Indicate to the PM core that it may safely leave the device in
|
||||
* runtime suspend if runtime-suspended already in device mode.
|
||||
*/
|
||||
if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE &&
|
||||
pm_runtime_suspended(dev) &&
|
||||
!dev_pinctrl(dev))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dwc3_pm_prepare);
|
||||
|
||||
static int dwc3_plat_suspend(struct device *dev)
|
||||
{
|
||||
return dwc3_pm_suspend(dev_get_drvdata(dev));
|
||||
}
|
||||
|
||||
static int dwc3_plat_resume(struct device *dev)
|
||||
{
|
||||
return dwc3_pm_resume(dev_get_drvdata(dev));
|
||||
}
|
||||
|
||||
static void dwc3_plat_complete(struct device *dev)
|
||||
{
|
||||
dwc3_pm_complete(dev_get_drvdata(dev));
|
||||
}
|
||||
|
||||
static int dwc3_plat_prepare(struct device *dev)
|
||||
{
|
||||
return dwc3_pm_prepare(dev_get_drvdata(dev));
|
||||
}
|
||||
#else
|
||||
#define dwc3_complete NULL
|
||||
#define dwc3_plat_complete NULL
|
||||
#define dwc3_plat_prepare NULL
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static const struct dev_pm_ops dwc3_dev_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(dwc3_suspend, dwc3_resume)
|
||||
.complete = dwc3_complete,
|
||||
|
||||
SET_SYSTEM_SLEEP_PM_OPS(dwc3_plat_suspend, dwc3_plat_resume)
|
||||
.complete = dwc3_plat_complete,
|
||||
.prepare = dwc3_plat_prepare,
|
||||
/*
|
||||
* Runtime suspend halts the controller on disconnection. It relies on
|
||||
* platforms with custom connection notification to start the controller
|
||||
* again.
|
||||
*/
|
||||
SET_RUNTIME_PM_OPS(dwc3_runtime_suspend, dwc3_runtime_resume,
|
||||
dwc3_runtime_idle)
|
||||
SET_RUNTIME_PM_OPS(dwc3_plat_runtime_suspend, dwc3_plat_runtime_resume,
|
||||
dwc3_plat_runtime_idle)
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
|
|
@ -1083,6 +1083,7 @@ struct dwc3_scratchpad_array {
|
|||
* @tx_max_burst_prd: max periodic ESS transmit burst size
|
||||
* @tx_fifo_resize_max_num: max number of fifos allocated during txfifo resize
|
||||
* @clear_stall_protocol: endpoint number that requires a delayed status phase
|
||||
* @num_hc_interrupters: number of host controller interrupters
|
||||
* @hsphy_interface: "utmi" or "ulpi"
|
||||
* @connected: true when we're connected to a host, false otherwise
|
||||
* @softconnect: true when gadget connect is called, false when disconnect runs
|
||||
|
@ -1333,6 +1334,7 @@ struct dwc3 {
|
|||
u8 tx_max_burst_prd;
|
||||
u8 tx_fifo_resize_max_num;
|
||||
u8 clear_stall_protocol;
|
||||
u16 num_hc_interrupters;
|
||||
|
||||
const char *hsphy_interface;
|
||||
|
||||
|
|
|
@ -145,6 +145,12 @@ static void dwc3_exynos_remove(struct platform_device *pdev)
|
|||
regulator_disable(exynos->vdd10);
|
||||
}
|
||||
|
||||
static const struct dwc3_exynos_driverdata exynos2200_drvdata = {
|
||||
.clk_names = { "link_aclk" },
|
||||
.num_clks = 1,
|
||||
.suspend_clk_idx = -1,
|
||||
};
|
||||
|
||||
static const struct dwc3_exynos_driverdata exynos5250_drvdata = {
|
||||
.clk_names = { "usbdrd30" },
|
||||
.num_clks = 1,
|
||||
|
@ -181,8 +187,17 @@ static const struct dwc3_exynos_driverdata gs101_drvdata = {
|
|||
.suspend_clk_idx = 1,
|
||||
};
|
||||
|
||||
static const struct dwc3_exynos_driverdata exynosautov920_drvdata = {
|
||||
.clk_names = { "ref", "susp_clk"},
|
||||
.num_clks = 2,
|
||||
.suspend_clk_idx = 1,
|
||||
};
|
||||
|
||||
static const struct of_device_id exynos_dwc3_match[] = {
|
||||
{
|
||||
.compatible = "samsung,exynos2200-dwusb3",
|
||||
.data = &exynos2200_drvdata,
|
||||
}, {
|
||||
.compatible = "samsung,exynos5250-dwusb3",
|
||||
.data = &exynos5250_drvdata,
|
||||
}, {
|
||||
|
@ -197,6 +212,9 @@ static const struct of_device_id exynos_dwc3_match[] = {
|
|||
}, {
|
||||
.compatible = "samsung,exynos850-dwusb3",
|
||||
.data = &exynos850_drvdata,
|
||||
}, {
|
||||
.compatible = "samsung,exynosautov920-dwusb3",
|
||||
.data = &exynosautov920_drvdata,
|
||||
}, {
|
||||
.compatible = "google,gs101-dwusb3",
|
||||
.data = &gs101_drvdata,
|
||||
|
|
|
@ -0,0 +1,935 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* Inspired by dwc3-of-simple.c
|
||||
*/
|
||||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/of_clk.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/extcon.h>
|
||||
#include <linux/interconnect.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/usb/of.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/usb.h>
|
||||
#include "core.h"
|
||||
|
||||
/* USB QSCRATCH Hardware registers */
|
||||
#define QSCRATCH_HS_PHY_CTRL 0x10
|
||||
#define UTMI_OTG_VBUS_VALID BIT(20)
|
||||
#define SW_SESSVLD_SEL BIT(28)
|
||||
|
||||
#define QSCRATCH_SS_PHY_CTRL 0x30
|
||||
#define LANE0_PWR_PRESENT BIT(24)
|
||||
|
||||
#define QSCRATCH_GENERAL_CFG 0x08
|
||||
#define PIPE_UTMI_CLK_SEL BIT(0)
|
||||
#define PIPE3_PHYSTATUS_SW BIT(3)
|
||||
#define PIPE_UTMI_CLK_DIS BIT(8)
|
||||
|
||||
#define PWR_EVNT_LPM_IN_L2_MASK BIT(4)
|
||||
#define PWR_EVNT_LPM_OUT_L2_MASK BIT(5)
|
||||
|
||||
#define SDM845_QSCRATCH_BASE_OFFSET 0xf8800
|
||||
#define SDM845_QSCRATCH_SIZE 0x400
|
||||
#define SDM845_DWC3_CORE_SIZE 0xcd00
|
||||
|
||||
/* Interconnect path bandwidths in MBps */
|
||||
#define USB_MEMORY_AVG_HS_BW MBps_to_icc(240)
|
||||
#define USB_MEMORY_PEAK_HS_BW MBps_to_icc(700)
|
||||
#define USB_MEMORY_AVG_SS_BW MBps_to_icc(1000)
|
||||
#define USB_MEMORY_PEAK_SS_BW MBps_to_icc(2500)
|
||||
#define APPS_USB_AVG_BW 0
|
||||
#define APPS_USB_PEAK_BW MBps_to_icc(40)
|
||||
|
||||
/* Qualcomm SoCs with multiport support has up to 4 ports */
|
||||
#define DWC3_QCOM_MAX_PORTS 4
|
||||
|
||||
static const u32 pwr_evnt_irq_stat_reg[DWC3_QCOM_MAX_PORTS] = {
|
||||
0x58,
|
||||
0x1dc,
|
||||
0x228,
|
||||
0x238,
|
||||
};
|
||||
|
||||
struct dwc3_qcom_port {
|
||||
int qusb2_phy_irq;
|
||||
int dp_hs_phy_irq;
|
||||
int dm_hs_phy_irq;
|
||||
int ss_phy_irq;
|
||||
enum usb_device_speed usb2_speed;
|
||||
};
|
||||
|
||||
struct dwc3_qcom {
|
||||
struct device *dev;
|
||||
void __iomem *qscratch_base;
|
||||
struct platform_device *dwc3;
|
||||
struct clk **clks;
|
||||
int num_clocks;
|
||||
struct reset_control *resets;
|
||||
struct dwc3_qcom_port ports[DWC3_QCOM_MAX_PORTS];
|
||||
u8 num_ports;
|
||||
|
||||
struct extcon_dev *edev;
|
||||
struct extcon_dev *host_edev;
|
||||
struct notifier_block vbus_nb;
|
||||
struct notifier_block host_nb;
|
||||
|
||||
enum usb_dr_mode mode;
|
||||
bool is_suspended;
|
||||
bool pm_suspended;
|
||||
struct icc_path *icc_path_ddr;
|
||||
struct icc_path *icc_path_apps;
|
||||
};
|
||||
|
||||
static inline void dwc3_qcom_setbits(void __iomem *base, u32 offset, u32 val)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = readl(base + offset);
|
||||
reg |= val;
|
||||
writel(reg, base + offset);
|
||||
|
||||
/* ensure that above write is through */
|
||||
readl(base + offset);
|
||||
}
|
||||
|
||||
static inline void dwc3_qcom_clrbits(void __iomem *base, u32 offset, u32 val)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = readl(base + offset);
|
||||
reg &= ~val;
|
||||
writel(reg, base + offset);
|
||||
|
||||
/* ensure that above write is through */
|
||||
readl(base + offset);
|
||||
}
|
||||
|
||||
static void dwc3_qcom_vbus_override_enable(struct dwc3_qcom *qcom, bool enable)
|
||||
{
|
||||
if (enable) {
|
||||
dwc3_qcom_setbits(qcom->qscratch_base, QSCRATCH_SS_PHY_CTRL,
|
||||
LANE0_PWR_PRESENT);
|
||||
dwc3_qcom_setbits(qcom->qscratch_base, QSCRATCH_HS_PHY_CTRL,
|
||||
UTMI_OTG_VBUS_VALID | SW_SESSVLD_SEL);
|
||||
} else {
|
||||
dwc3_qcom_clrbits(qcom->qscratch_base, QSCRATCH_SS_PHY_CTRL,
|
||||
LANE0_PWR_PRESENT);
|
||||
dwc3_qcom_clrbits(qcom->qscratch_base, QSCRATCH_HS_PHY_CTRL,
|
||||
UTMI_OTG_VBUS_VALID | SW_SESSVLD_SEL);
|
||||
}
|
||||
}
|
||||
|
||||
static int dwc3_qcom_vbus_notifier(struct notifier_block *nb,
|
||||
unsigned long event, void *ptr)
|
||||
{
|
||||
struct dwc3_qcom *qcom = container_of(nb, struct dwc3_qcom, vbus_nb);
|
||||
|
||||
/* enable vbus override for device mode */
|
||||
dwc3_qcom_vbus_override_enable(qcom, event);
|
||||
qcom->mode = event ? USB_DR_MODE_PERIPHERAL : USB_DR_MODE_HOST;
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static int dwc3_qcom_host_notifier(struct notifier_block *nb,
|
||||
unsigned long event, void *ptr)
|
||||
{
|
||||
struct dwc3_qcom *qcom = container_of(nb, struct dwc3_qcom, host_nb);
|
||||
|
||||
/* disable vbus override in host mode */
|
||||
dwc3_qcom_vbus_override_enable(qcom, !event);
|
||||
qcom->mode = event ? USB_DR_MODE_HOST : USB_DR_MODE_PERIPHERAL;
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static int dwc3_qcom_register_extcon(struct dwc3_qcom *qcom)
|
||||
{
|
||||
struct device *dev = qcom->dev;
|
||||
struct extcon_dev *host_edev;
|
||||
int ret;
|
||||
|
||||
if (!of_property_present(dev->of_node, "extcon"))
|
||||
return 0;
|
||||
|
||||
qcom->edev = extcon_get_edev_by_phandle(dev, 0);
|
||||
if (IS_ERR(qcom->edev))
|
||||
return dev_err_probe(dev, PTR_ERR(qcom->edev),
|
||||
"Failed to get extcon\n");
|
||||
|
||||
qcom->vbus_nb.notifier_call = dwc3_qcom_vbus_notifier;
|
||||
|
||||
qcom->host_edev = extcon_get_edev_by_phandle(dev, 1);
|
||||
if (IS_ERR(qcom->host_edev))
|
||||
qcom->host_edev = NULL;
|
||||
|
||||
ret = devm_extcon_register_notifier(dev, qcom->edev, EXTCON_USB,
|
||||
&qcom->vbus_nb);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "VBUS notifier register failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (qcom->host_edev)
|
||||
host_edev = qcom->host_edev;
|
||||
else
|
||||
host_edev = qcom->edev;
|
||||
|
||||
qcom->host_nb.notifier_call = dwc3_qcom_host_notifier;
|
||||
ret = devm_extcon_register_notifier(dev, host_edev, EXTCON_USB_HOST,
|
||||
&qcom->host_nb);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Host notifier register failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Update initial VBUS override based on extcon state */
|
||||
if (extcon_get_state(qcom->edev, EXTCON_USB) ||
|
||||
!extcon_get_state(host_edev, EXTCON_USB_HOST))
|
||||
dwc3_qcom_vbus_notifier(&qcom->vbus_nb, true, qcom->edev);
|
||||
else
|
||||
dwc3_qcom_vbus_notifier(&qcom->vbus_nb, false, qcom->edev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_qcom_interconnect_enable(struct dwc3_qcom *qcom)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = icc_enable(qcom->icc_path_ddr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = icc_enable(qcom->icc_path_apps);
|
||||
if (ret)
|
||||
icc_disable(qcom->icc_path_ddr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dwc3_qcom_interconnect_disable(struct dwc3_qcom *qcom)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = icc_disable(qcom->icc_path_ddr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = icc_disable(qcom->icc_path_apps);
|
||||
if (ret)
|
||||
icc_enable(qcom->icc_path_ddr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_qcom_interconnect_init() - Get interconnect path handles
|
||||
* and set bandwidth.
|
||||
* @qcom: Pointer to the concerned usb core.
|
||||
*
|
||||
*/
|
||||
static int dwc3_qcom_interconnect_init(struct dwc3_qcom *qcom)
|
||||
{
|
||||
enum usb_device_speed max_speed;
|
||||
struct device *dev = qcom->dev;
|
||||
int ret;
|
||||
|
||||
qcom->icc_path_ddr = of_icc_get(dev, "usb-ddr");
|
||||
if (IS_ERR(qcom->icc_path_ddr)) {
|
||||
return dev_err_probe(dev, PTR_ERR(qcom->icc_path_ddr),
|
||||
"failed to get usb-ddr path\n");
|
||||
}
|
||||
|
||||
qcom->icc_path_apps = of_icc_get(dev, "apps-usb");
|
||||
if (IS_ERR(qcom->icc_path_apps)) {
|
||||
ret = dev_err_probe(dev, PTR_ERR(qcom->icc_path_apps),
|
||||
"failed to get apps-usb path\n");
|
||||
goto put_path_ddr;
|
||||
}
|
||||
|
||||
max_speed = usb_get_maximum_speed(&qcom->dwc3->dev);
|
||||
if (max_speed >= USB_SPEED_SUPER || max_speed == USB_SPEED_UNKNOWN) {
|
||||
ret = icc_set_bw(qcom->icc_path_ddr,
|
||||
USB_MEMORY_AVG_SS_BW, USB_MEMORY_PEAK_SS_BW);
|
||||
} else {
|
||||
ret = icc_set_bw(qcom->icc_path_ddr,
|
||||
USB_MEMORY_AVG_HS_BW, USB_MEMORY_PEAK_HS_BW);
|
||||
}
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to set bandwidth for usb-ddr path: %d\n", ret);
|
||||
goto put_path_apps;
|
||||
}
|
||||
|
||||
ret = icc_set_bw(qcom->icc_path_apps, APPS_USB_AVG_BW, APPS_USB_PEAK_BW);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to set bandwidth for apps-usb path: %d\n", ret);
|
||||
goto put_path_apps;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
put_path_apps:
|
||||
icc_put(qcom->icc_path_apps);
|
||||
put_path_ddr:
|
||||
icc_put(qcom->icc_path_ddr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_qcom_interconnect_exit() - Release interconnect path handles
|
||||
* @qcom: Pointer to the concerned usb core.
|
||||
*
|
||||
* This function is used to release interconnect path handle.
|
||||
*/
|
||||
static void dwc3_qcom_interconnect_exit(struct dwc3_qcom *qcom)
|
||||
{
|
||||
icc_put(qcom->icc_path_ddr);
|
||||
icc_put(qcom->icc_path_apps);
|
||||
}
|
||||
|
||||
/* Only usable in contexts where the role can not change. */
|
||||
static bool dwc3_qcom_is_host(struct dwc3_qcom *qcom)
|
||||
{
|
||||
struct dwc3 *dwc;
|
||||
|
||||
/*
|
||||
* FIXME: Fix this layering violation.
|
||||
*/
|
||||
dwc = platform_get_drvdata(qcom->dwc3);
|
||||
|
||||
/* Core driver may not have probed yet. */
|
||||
if (!dwc)
|
||||
return false;
|
||||
|
||||
return dwc->xhci;
|
||||
}
|
||||
|
||||
static enum usb_device_speed dwc3_qcom_read_usb2_speed(struct dwc3_qcom *qcom, int port_index)
|
||||
{
|
||||
struct dwc3 *dwc = platform_get_drvdata(qcom->dwc3);
|
||||
struct usb_device *udev;
|
||||
struct usb_hcd __maybe_unused *hcd;
|
||||
|
||||
/*
|
||||
* FIXME: Fix this layering violation.
|
||||
*/
|
||||
hcd = platform_get_drvdata(dwc->xhci);
|
||||
|
||||
#ifdef CONFIG_USB
|
||||
udev = usb_hub_find_child(hcd->self.root_hub, port_index + 1);
|
||||
#else
|
||||
udev = NULL;
|
||||
#endif
|
||||
if (!udev)
|
||||
return USB_SPEED_UNKNOWN;
|
||||
|
||||
return udev->speed;
|
||||
}
|
||||
|
||||
static void dwc3_qcom_enable_wakeup_irq(int irq, unsigned int polarity)
|
||||
{
|
||||
if (!irq)
|
||||
return;
|
||||
|
||||
if (polarity)
|
||||
irq_set_irq_type(irq, polarity);
|
||||
|
||||
enable_irq(irq);
|
||||
enable_irq_wake(irq);
|
||||
}
|
||||
|
||||
static void dwc3_qcom_disable_wakeup_irq(int irq)
|
||||
{
|
||||
if (!irq)
|
||||
return;
|
||||
|
||||
disable_irq_wake(irq);
|
||||
disable_irq_nosync(irq);
|
||||
}
|
||||
|
||||
static void dwc3_qcom_disable_port_interrupts(struct dwc3_qcom_port *port)
|
||||
{
|
||||
dwc3_qcom_disable_wakeup_irq(port->qusb2_phy_irq);
|
||||
|
||||
if (port->usb2_speed == USB_SPEED_LOW) {
|
||||
dwc3_qcom_disable_wakeup_irq(port->dm_hs_phy_irq);
|
||||
} else if ((port->usb2_speed == USB_SPEED_HIGH) ||
|
||||
(port->usb2_speed == USB_SPEED_FULL)) {
|
||||
dwc3_qcom_disable_wakeup_irq(port->dp_hs_phy_irq);
|
||||
} else {
|
||||
dwc3_qcom_disable_wakeup_irq(port->dp_hs_phy_irq);
|
||||
dwc3_qcom_disable_wakeup_irq(port->dm_hs_phy_irq);
|
||||
}
|
||||
|
||||
dwc3_qcom_disable_wakeup_irq(port->ss_phy_irq);
|
||||
}
|
||||
|
||||
static void dwc3_qcom_enable_port_interrupts(struct dwc3_qcom_port *port)
|
||||
{
|
||||
dwc3_qcom_enable_wakeup_irq(port->qusb2_phy_irq, 0);
|
||||
|
||||
/*
|
||||
* Configure DP/DM line interrupts based on the USB2 device attached to
|
||||
* the root hub port. When HS/FS device is connected, configure the DP line
|
||||
* as falling edge to detect both disconnect and remote wakeup scenarios. When
|
||||
* LS device is connected, configure DM line as falling edge to detect both
|
||||
* disconnect and remote wakeup. When no device is connected, configure both
|
||||
* DP and DM lines as rising edge to detect HS/HS/LS device connect scenario.
|
||||
*/
|
||||
|
||||
if (port->usb2_speed == USB_SPEED_LOW) {
|
||||
dwc3_qcom_enable_wakeup_irq(port->dm_hs_phy_irq,
|
||||
IRQ_TYPE_EDGE_FALLING);
|
||||
} else if ((port->usb2_speed == USB_SPEED_HIGH) ||
|
||||
(port->usb2_speed == USB_SPEED_FULL)) {
|
||||
dwc3_qcom_enable_wakeup_irq(port->dp_hs_phy_irq,
|
||||
IRQ_TYPE_EDGE_FALLING);
|
||||
} else {
|
||||
dwc3_qcom_enable_wakeup_irq(port->dp_hs_phy_irq,
|
||||
IRQ_TYPE_EDGE_RISING);
|
||||
dwc3_qcom_enable_wakeup_irq(port->dm_hs_phy_irq,
|
||||
IRQ_TYPE_EDGE_RISING);
|
||||
}
|
||||
|
||||
dwc3_qcom_enable_wakeup_irq(port->ss_phy_irq, 0);
|
||||
}
|
||||
|
||||
static void dwc3_qcom_disable_interrupts(struct dwc3_qcom *qcom)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < qcom->num_ports; i++)
|
||||
dwc3_qcom_disable_port_interrupts(&qcom->ports[i]);
|
||||
}
|
||||
|
||||
static void dwc3_qcom_enable_interrupts(struct dwc3_qcom *qcom)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < qcom->num_ports; i++)
|
||||
dwc3_qcom_enable_port_interrupts(&qcom->ports[i]);
|
||||
}
|
||||
|
||||
static int dwc3_qcom_suspend(struct dwc3_qcom *qcom, bool wakeup)
|
||||
{
|
||||
u32 val;
|
||||
int i, ret;
|
||||
|
||||
if (qcom->is_suspended)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < qcom->num_ports; i++) {
|
||||
val = readl(qcom->qscratch_base + pwr_evnt_irq_stat_reg[i]);
|
||||
if (!(val & PWR_EVNT_LPM_IN_L2_MASK))
|
||||
dev_err(qcom->dev, "port-%d HS-PHY not in L2\n", i + 1);
|
||||
}
|
||||
|
||||
for (i = qcom->num_clocks - 1; i >= 0; i--)
|
||||
clk_disable_unprepare(qcom->clks[i]);
|
||||
|
||||
ret = dwc3_qcom_interconnect_disable(qcom);
|
||||
if (ret)
|
||||
dev_warn(qcom->dev, "failed to disable interconnect: %d\n", ret);
|
||||
|
||||
/*
|
||||
* The role is stable during suspend as role switching is done from a
|
||||
* freezable workqueue.
|
||||
*/
|
||||
if (dwc3_qcom_is_host(qcom) && wakeup) {
|
||||
for (i = 0; i < qcom->num_ports; i++)
|
||||
qcom->ports[i].usb2_speed = dwc3_qcom_read_usb2_speed(qcom, i);
|
||||
dwc3_qcom_enable_interrupts(qcom);
|
||||
}
|
||||
|
||||
qcom->is_suspended = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_qcom_resume(struct dwc3_qcom *qcom, bool wakeup)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
if (!qcom->is_suspended)
|
||||
return 0;
|
||||
|
||||
if (dwc3_qcom_is_host(qcom) && wakeup)
|
||||
dwc3_qcom_disable_interrupts(qcom);
|
||||
|
||||
for (i = 0; i < qcom->num_clocks; i++) {
|
||||
ret = clk_prepare_enable(qcom->clks[i]);
|
||||
if (ret < 0) {
|
||||
while (--i >= 0)
|
||||
clk_disable_unprepare(qcom->clks[i]);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = dwc3_qcom_interconnect_enable(qcom);
|
||||
if (ret)
|
||||
dev_warn(qcom->dev, "failed to enable interconnect: %d\n", ret);
|
||||
|
||||
/* Clear existing events from PHY related to L2 in/out */
|
||||
for (i = 0; i < qcom->num_ports; i++) {
|
||||
dwc3_qcom_setbits(qcom->qscratch_base,
|
||||
pwr_evnt_irq_stat_reg[i],
|
||||
PWR_EVNT_LPM_IN_L2_MASK | PWR_EVNT_LPM_OUT_L2_MASK);
|
||||
}
|
||||
|
||||
qcom->is_suspended = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t qcom_dwc3_resume_irq(int irq, void *data)
|
||||
{
|
||||
struct dwc3_qcom *qcom = data;
|
||||
struct dwc3 *dwc = platform_get_drvdata(qcom->dwc3);
|
||||
|
||||
/* If pm_suspended then let pm_resume take care of resuming h/w */
|
||||
if (qcom->pm_suspended)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
/*
|
||||
* This is safe as role switching is done from a freezable workqueue
|
||||
* and the wakeup interrupts are disabled as part of resume.
|
||||
*/
|
||||
if (dwc3_qcom_is_host(qcom))
|
||||
pm_runtime_resume(&dwc->xhci->dev);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void dwc3_qcom_select_utmi_clk(struct dwc3_qcom *qcom)
|
||||
{
|
||||
/* Configure dwc3 to use UTMI clock as PIPE clock not present */
|
||||
dwc3_qcom_setbits(qcom->qscratch_base, QSCRATCH_GENERAL_CFG,
|
||||
PIPE_UTMI_CLK_DIS);
|
||||
|
||||
usleep_range(100, 1000);
|
||||
|
||||
dwc3_qcom_setbits(qcom->qscratch_base, QSCRATCH_GENERAL_CFG,
|
||||
PIPE_UTMI_CLK_SEL | PIPE3_PHYSTATUS_SW);
|
||||
|
||||
usleep_range(100, 1000);
|
||||
|
||||
dwc3_qcom_clrbits(qcom->qscratch_base, QSCRATCH_GENERAL_CFG,
|
||||
PIPE_UTMI_CLK_DIS);
|
||||
}
|
||||
|
||||
static int dwc3_qcom_request_irq(struct dwc3_qcom *qcom, int irq,
|
||||
const char *name)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Keep wakeup interrupts disabled until suspend */
|
||||
ret = devm_request_threaded_irq(qcom->dev, irq, NULL,
|
||||
qcom_dwc3_resume_irq,
|
||||
IRQF_ONESHOT | IRQF_NO_AUTOEN,
|
||||
name, qcom);
|
||||
if (ret)
|
||||
dev_err(qcom->dev, "failed to request irq %s: %d\n", name, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dwc3_qcom_setup_port_irq(struct platform_device *pdev, int port_index, bool is_multiport)
|
||||
{
|
||||
struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
|
||||
const char *irq_name;
|
||||
int irq;
|
||||
int ret;
|
||||
|
||||
if (is_multiport)
|
||||
irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "dp_hs_phy_%d", port_index + 1);
|
||||
else
|
||||
irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "dp_hs_phy_irq");
|
||||
if (!irq_name)
|
||||
return -ENOMEM;
|
||||
|
||||
irq = platform_get_irq_byname_optional(pdev, irq_name);
|
||||
if (irq > 0) {
|
||||
ret = dwc3_qcom_request_irq(qcom, irq, irq_name);
|
||||
if (ret)
|
||||
return ret;
|
||||
qcom->ports[port_index].dp_hs_phy_irq = irq;
|
||||
}
|
||||
|
||||
if (is_multiport)
|
||||
irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "dm_hs_phy_%d", port_index + 1);
|
||||
else
|
||||
irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "dm_hs_phy_irq");
|
||||
if (!irq_name)
|
||||
return -ENOMEM;
|
||||
|
||||
irq = platform_get_irq_byname_optional(pdev, irq_name);
|
||||
if (irq > 0) {
|
||||
ret = dwc3_qcom_request_irq(qcom, irq, irq_name);
|
||||
if (ret)
|
||||
return ret;
|
||||
qcom->ports[port_index].dm_hs_phy_irq = irq;
|
||||
}
|
||||
|
||||
if (is_multiport)
|
||||
irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "ss_phy_%d", port_index + 1);
|
||||
else
|
||||
irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "ss_phy_irq");
|
||||
if (!irq_name)
|
||||
return -ENOMEM;
|
||||
|
||||
irq = platform_get_irq_byname_optional(pdev, irq_name);
|
||||
if (irq > 0) {
|
||||
ret = dwc3_qcom_request_irq(qcom, irq, irq_name);
|
||||
if (ret)
|
||||
return ret;
|
||||
qcom->ports[port_index].ss_phy_irq = irq;
|
||||
}
|
||||
|
||||
if (is_multiport)
|
||||
return 0;
|
||||
|
||||
irq = platform_get_irq_byname_optional(pdev, "qusb2_phy");
|
||||
if (irq > 0) {
|
||||
ret = dwc3_qcom_request_irq(qcom, irq, "qusb2_phy");
|
||||
if (ret)
|
||||
return ret;
|
||||
qcom->ports[port_index].qusb2_phy_irq = irq;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_qcom_find_num_ports(struct platform_device *pdev)
|
||||
{
|
||||
char irq_name[14];
|
||||
int port_num;
|
||||
int irq;
|
||||
|
||||
irq = platform_get_irq_byname_optional(pdev, "dp_hs_phy_1");
|
||||
if (irq <= 0)
|
||||
return 1;
|
||||
|
||||
for (port_num = 2; port_num <= DWC3_QCOM_MAX_PORTS; port_num++) {
|
||||
sprintf(irq_name, "dp_hs_phy_%d", port_num);
|
||||
|
||||
irq = platform_get_irq_byname_optional(pdev, irq_name);
|
||||
if (irq <= 0)
|
||||
return port_num - 1;
|
||||
}
|
||||
|
||||
return DWC3_QCOM_MAX_PORTS;
|
||||
}
|
||||
|
||||
static int dwc3_qcom_setup_irq(struct platform_device *pdev)
|
||||
{
|
||||
struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
|
||||
bool is_multiport;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
qcom->num_ports = dwc3_qcom_find_num_ports(pdev);
|
||||
is_multiport = (qcom->num_ports > 1);
|
||||
|
||||
for (i = 0; i < qcom->num_ports; i++) {
|
||||
ret = dwc3_qcom_setup_port_irq(pdev, i, is_multiport);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_qcom_clk_init(struct dwc3_qcom *qcom, int count)
|
||||
{
|
||||
struct device *dev = qcom->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
int i;
|
||||
|
||||
if (!np || !count)
|
||||
return 0;
|
||||
|
||||
if (count < 0)
|
||||
return count;
|
||||
|
||||
qcom->num_clocks = count;
|
||||
|
||||
qcom->clks = devm_kcalloc(dev, qcom->num_clocks,
|
||||
sizeof(struct clk *), GFP_KERNEL);
|
||||
if (!qcom->clks)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < qcom->num_clocks; i++) {
|
||||
struct clk *clk;
|
||||
int ret;
|
||||
|
||||
clk = of_clk_get(np, i);
|
||||
if (IS_ERR(clk)) {
|
||||
while (--i >= 0)
|
||||
clk_put(qcom->clks[i]);
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (ret < 0) {
|
||||
while (--i >= 0) {
|
||||
clk_disable_unprepare(qcom->clks[i]);
|
||||
clk_put(qcom->clks[i]);
|
||||
}
|
||||
clk_put(clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
qcom->clks[i] = clk;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_qcom_of_register_core(struct platform_device *pdev)
|
||||
{
|
||||
struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret;
|
||||
|
||||
struct device_node *dwc3_np __free(device_node) = of_get_compatible_child(np,
|
||||
"snps,dwc3");
|
||||
if (!dwc3_np) {
|
||||
dev_err(dev, "failed to find dwc3 core child\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = of_platform_populate(np, NULL, NULL, dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register dwc3 core - %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
qcom->dwc3 = of_find_device_by_node(dwc3_np);
|
||||
if (!qcom->dwc3) {
|
||||
ret = -ENODEV;
|
||||
dev_err(dev, "failed to get dwc3 platform device\n");
|
||||
of_platform_depopulate(dev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dwc3_qcom_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct dwc3_qcom *qcom;
|
||||
int ret, i;
|
||||
bool ignore_pipe_clk;
|
||||
bool wakeup_source;
|
||||
|
||||
qcom = devm_kzalloc(&pdev->dev, sizeof(*qcom), GFP_KERNEL);
|
||||
if (!qcom)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, qcom);
|
||||
qcom->dev = &pdev->dev;
|
||||
|
||||
qcom->resets = devm_reset_control_array_get_optional_exclusive(dev);
|
||||
if (IS_ERR(qcom->resets)) {
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(qcom->resets),
|
||||
"failed to get resets\n");
|
||||
}
|
||||
|
||||
ret = reset_control_assert(qcom->resets);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to assert resets, err=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
usleep_range(10, 1000);
|
||||
|
||||
ret = reset_control_deassert(qcom->resets);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to deassert resets, err=%d\n", ret);
|
||||
goto reset_assert;
|
||||
}
|
||||
|
||||
ret = dwc3_qcom_clk_init(qcom, of_clk_get_parent_count(np));
|
||||
if (ret) {
|
||||
dev_err_probe(dev, ret, "failed to get clocks\n");
|
||||
goto reset_assert;
|
||||
}
|
||||
|
||||
qcom->qscratch_base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(qcom->qscratch_base)) {
|
||||
ret = PTR_ERR(qcom->qscratch_base);
|
||||
goto clk_disable;
|
||||
}
|
||||
|
||||
ret = dwc3_qcom_setup_irq(pdev);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to setup IRQs, err=%d\n", ret);
|
||||
goto clk_disable;
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable pipe_clk requirement if specified. Used when dwc3
|
||||
* operates without SSPHY and only HS/FS/LS modes are supported.
|
||||
*/
|
||||
ignore_pipe_clk = device_property_read_bool(dev,
|
||||
"qcom,select-utmi-as-pipe-clk");
|
||||
if (ignore_pipe_clk)
|
||||
dwc3_qcom_select_utmi_clk(qcom);
|
||||
|
||||
ret = dwc3_qcom_of_register_core(pdev);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register DWC3 Core, err=%d\n", ret);
|
||||
goto clk_disable;
|
||||
}
|
||||
|
||||
ret = dwc3_qcom_interconnect_init(qcom);
|
||||
if (ret)
|
||||
goto depopulate;
|
||||
|
||||
qcom->mode = usb_get_dr_mode(&qcom->dwc3->dev);
|
||||
|
||||
/* enable vbus override for device mode */
|
||||
if (qcom->mode != USB_DR_MODE_HOST)
|
||||
dwc3_qcom_vbus_override_enable(qcom, true);
|
||||
|
||||
/* register extcon to override sw_vbus on Vbus change later */
|
||||
ret = dwc3_qcom_register_extcon(qcom);
|
||||
if (ret)
|
||||
goto interconnect_exit;
|
||||
|
||||
wakeup_source = of_property_read_bool(dev->of_node, "wakeup-source");
|
||||
device_init_wakeup(&pdev->dev, wakeup_source);
|
||||
device_init_wakeup(&qcom->dwc3->dev, wakeup_source);
|
||||
|
||||
qcom->is_suspended = false;
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_forbid(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
interconnect_exit:
|
||||
dwc3_qcom_interconnect_exit(qcom);
|
||||
depopulate:
|
||||
of_platform_depopulate(&pdev->dev);
|
||||
platform_device_put(qcom->dwc3);
|
||||
clk_disable:
|
||||
for (i = qcom->num_clocks - 1; i >= 0; i--) {
|
||||
clk_disable_unprepare(qcom->clks[i]);
|
||||
clk_put(qcom->clks[i]);
|
||||
}
|
||||
reset_assert:
|
||||
reset_control_assert(qcom->resets);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void dwc3_qcom_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
|
||||
struct device *dev = &pdev->dev;
|
||||
int i;
|
||||
|
||||
of_platform_depopulate(&pdev->dev);
|
||||
platform_device_put(qcom->dwc3);
|
||||
|
||||
for (i = qcom->num_clocks - 1; i >= 0; i--) {
|
||||
clk_disable_unprepare(qcom->clks[i]);
|
||||
clk_put(qcom->clks[i]);
|
||||
}
|
||||
qcom->num_clocks = 0;
|
||||
|
||||
dwc3_qcom_interconnect_exit(qcom);
|
||||
reset_control_assert(qcom->resets);
|
||||
|
||||
pm_runtime_allow(dev);
|
||||
pm_runtime_disable(dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused dwc3_qcom_pm_suspend(struct device *dev)
|
||||
{
|
||||
struct dwc3_qcom *qcom = dev_get_drvdata(dev);
|
||||
bool wakeup = device_may_wakeup(dev);
|
||||
int ret;
|
||||
|
||||
ret = dwc3_qcom_suspend(qcom, wakeup);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
qcom->pm_suspended = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused dwc3_qcom_pm_resume(struct device *dev)
|
||||
{
|
||||
struct dwc3_qcom *qcom = dev_get_drvdata(dev);
|
||||
bool wakeup = device_may_wakeup(dev);
|
||||
int ret;
|
||||
|
||||
ret = dwc3_qcom_resume(qcom, wakeup);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
qcom->pm_suspended = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused dwc3_qcom_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct dwc3_qcom *qcom = dev_get_drvdata(dev);
|
||||
|
||||
return dwc3_qcom_suspend(qcom, true);
|
||||
}
|
||||
|
||||
static int __maybe_unused dwc3_qcom_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct dwc3_qcom *qcom = dev_get_drvdata(dev);
|
||||
|
||||
return dwc3_qcom_resume(qcom, true);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops dwc3_qcom_dev_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(dwc3_qcom_pm_suspend, dwc3_qcom_pm_resume)
|
||||
SET_RUNTIME_PM_OPS(dwc3_qcom_runtime_suspend, dwc3_qcom_runtime_resume,
|
||||
NULL)
|
||||
};
|
||||
|
||||
static const struct of_device_id dwc3_qcom_of_match[] = {
|
||||
{ .compatible = "qcom,dwc3" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dwc3_qcom_of_match);
|
||||
|
||||
static struct platform_driver dwc3_qcom_driver = {
|
||||
.probe = dwc3_qcom_probe,
|
||||
.remove = dwc3_qcom_remove,
|
||||
.driver = {
|
||||
.name = "dwc3-qcom-legacy",
|
||||
.pm = &dwc3_qcom_dev_pm_ops,
|
||||
.of_match_table = dwc3_qcom_of_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(dwc3_qcom_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("DesignWare DWC3 QCOM legacy glue Driver");
|
|
@ -4,7 +4,6 @@
|
|||
* Inspired by dwc3-of-simple.c
|
||||
*/
|
||||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/clk.h>
|
||||
|
@ -14,7 +13,6 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/extcon.h>
|
||||
#include <linux/interconnect.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/usb/of.h>
|
||||
|
@ -23,6 +21,7 @@
|
|||
#include <linux/usb/hcd.h>
|
||||
#include <linux/usb.h>
|
||||
#include "core.h"
|
||||
#include "glue.h"
|
||||
|
||||
/* USB QSCRATCH Hardware registers */
|
||||
#define QSCRATCH_HS_PHY_CTRL 0x10
|
||||
|
@ -73,8 +72,8 @@ struct dwc3_qcom_port {
|
|||
struct dwc3_qcom {
|
||||
struct device *dev;
|
||||
void __iomem *qscratch_base;
|
||||
struct platform_device *dwc3;
|
||||
struct clk **clks;
|
||||
struct dwc3 dwc;
|
||||
struct clk_bulk_data *clks;
|
||||
int num_clocks;
|
||||
struct reset_control *resets;
|
||||
struct dwc3_qcom_port ports[DWC3_QCOM_MAX_PORTS];
|
||||
|
@ -92,6 +91,8 @@ struct dwc3_qcom {
|
|||
struct icc_path *icc_path_apps;
|
||||
};
|
||||
|
||||
#define to_dwc3_qcom(d) container_of((d), struct dwc3_qcom, dwc)
|
||||
|
||||
static inline void dwc3_qcom_setbits(void __iomem *base, u32 offset, u32 val)
|
||||
{
|
||||
u32 reg;
|
||||
|
@ -116,6 +117,11 @@ static inline void dwc3_qcom_clrbits(void __iomem *base, u32 offset, u32 val)
|
|||
readl(base + offset);
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: Make the in-core role switching code invoke dwc3_qcom_vbus_override_enable(),
|
||||
* validate that the in-core extcon support is functional, and drop extcon
|
||||
* handling from the glue
|
||||
*/
|
||||
static void dwc3_qcom_vbus_override_enable(struct dwc3_qcom *qcom, bool enable)
|
||||
{
|
||||
if (enable) {
|
||||
|
@ -260,7 +266,7 @@ static int dwc3_qcom_interconnect_init(struct dwc3_qcom *qcom)
|
|||
goto put_path_ddr;
|
||||
}
|
||||
|
||||
max_speed = usb_get_maximum_speed(&qcom->dwc3->dev);
|
||||
max_speed = usb_get_maximum_speed(qcom->dwc.dev);
|
||||
if (max_speed >= USB_SPEED_SUPER || max_speed == USB_SPEED_UNKNOWN) {
|
||||
ret = icc_set_bw(qcom->icc_path_ddr,
|
||||
USB_MEMORY_AVG_SS_BW, USB_MEMORY_PEAK_SS_BW);
|
||||
|
@ -303,25 +309,14 @@ static void dwc3_qcom_interconnect_exit(struct dwc3_qcom *qcom)
|
|||
/* Only usable in contexts where the role can not change. */
|
||||
static bool dwc3_qcom_is_host(struct dwc3_qcom *qcom)
|
||||
{
|
||||
struct dwc3 *dwc;
|
||||
|
||||
/*
|
||||
* FIXME: Fix this layering violation.
|
||||
*/
|
||||
dwc = platform_get_drvdata(qcom->dwc3);
|
||||
|
||||
/* Core driver may not have probed yet. */
|
||||
if (!dwc)
|
||||
return false;
|
||||
|
||||
return dwc->xhci;
|
||||
return qcom->dwc.xhci;
|
||||
}
|
||||
|
||||
static enum usb_device_speed dwc3_qcom_read_usb2_speed(struct dwc3_qcom *qcom, int port_index)
|
||||
{
|
||||
struct dwc3 *dwc = platform_get_drvdata(qcom->dwc3);
|
||||
struct usb_device *udev;
|
||||
struct usb_hcd __maybe_unused *hcd;
|
||||
struct dwc3 *dwc = &qcom->dwc;
|
||||
|
||||
/*
|
||||
* FIXME: Fix this layering violation.
|
||||
|
@ -436,9 +431,7 @@ static int dwc3_qcom_suspend(struct dwc3_qcom *qcom, bool wakeup)
|
|||
if (!(val & PWR_EVNT_LPM_IN_L2_MASK))
|
||||
dev_err(qcom->dev, "port-%d HS-PHY not in L2\n", i + 1);
|
||||
}
|
||||
|
||||
for (i = qcom->num_clocks - 1; i >= 0; i--)
|
||||
clk_disable_unprepare(qcom->clks[i]);
|
||||
clk_bulk_disable_unprepare(qcom->num_clocks, qcom->clks);
|
||||
|
||||
ret = dwc3_qcom_interconnect_disable(qcom);
|
||||
if (ret)
|
||||
|
@ -470,14 +463,9 @@ static int dwc3_qcom_resume(struct dwc3_qcom *qcom, bool wakeup)
|
|||
if (dwc3_qcom_is_host(qcom) && wakeup)
|
||||
dwc3_qcom_disable_interrupts(qcom);
|
||||
|
||||
for (i = 0; i < qcom->num_clocks; i++) {
|
||||
ret = clk_prepare_enable(qcom->clks[i]);
|
||||
if (ret < 0) {
|
||||
while (--i >= 0)
|
||||
clk_disable_unprepare(qcom->clks[i]);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
ret = clk_bulk_prepare_enable(qcom->num_clocks, qcom->clks);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = dwc3_qcom_interconnect_enable(qcom);
|
||||
if (ret)
|
||||
|
@ -498,7 +486,7 @@ static int dwc3_qcom_resume(struct dwc3_qcom *qcom, bool wakeup)
|
|||
static irqreturn_t qcom_dwc3_resume_irq(int irq, void *data)
|
||||
{
|
||||
struct dwc3_qcom *qcom = data;
|
||||
struct dwc3 *dwc = platform_get_drvdata(qcom->dwc3);
|
||||
struct dwc3 *dwc = &qcom->dwc;
|
||||
|
||||
/* If pm_suspended then let pm_resume take care of resuming h/w */
|
||||
if (qcom->pm_suspended)
|
||||
|
@ -547,9 +535,10 @@ static int dwc3_qcom_request_irq(struct dwc3_qcom *qcom, int irq,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int dwc3_qcom_setup_port_irq(struct platform_device *pdev, int port_index, bool is_multiport)
|
||||
static int dwc3_qcom_setup_port_irq(struct dwc3_qcom *qcom,
|
||||
struct platform_device *pdev,
|
||||
int port_index, bool is_multiport)
|
||||
{
|
||||
struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
|
||||
const char *irq_name;
|
||||
int irq;
|
||||
int ret;
|
||||
|
@ -634,9 +623,8 @@ static int dwc3_qcom_find_num_ports(struct platform_device *pdev)
|
|||
return DWC3_QCOM_MAX_PORTS;
|
||||
}
|
||||
|
||||
static int dwc3_qcom_setup_irq(struct platform_device *pdev)
|
||||
static int dwc3_qcom_setup_irq(struct dwc3_qcom *qcom, struct platform_device *pdev)
|
||||
{
|
||||
struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
|
||||
bool is_multiport;
|
||||
int ret;
|
||||
int i;
|
||||
|
@ -645,7 +633,7 @@ static int dwc3_qcom_setup_irq(struct platform_device *pdev)
|
|||
is_multiport = (qcom->num_ports > 1);
|
||||
|
||||
for (i = 0; i < qcom->num_ports; i++) {
|
||||
ret = dwc3_qcom_setup_port_irq(pdev, i, is_multiport);
|
||||
ret = dwc3_qcom_setup_port_irq(qcom, pdev, i, is_multiport);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
@ -653,89 +641,14 @@ static int dwc3_qcom_setup_irq(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_qcom_clk_init(struct dwc3_qcom *qcom, int count)
|
||||
{
|
||||
struct device *dev = qcom->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
int i;
|
||||
|
||||
if (!np || !count)
|
||||
return 0;
|
||||
|
||||
if (count < 0)
|
||||
return count;
|
||||
|
||||
qcom->num_clocks = count;
|
||||
|
||||
qcom->clks = devm_kcalloc(dev, qcom->num_clocks,
|
||||
sizeof(struct clk *), GFP_KERNEL);
|
||||
if (!qcom->clks)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < qcom->num_clocks; i++) {
|
||||
struct clk *clk;
|
||||
int ret;
|
||||
|
||||
clk = of_clk_get(np, i);
|
||||
if (IS_ERR(clk)) {
|
||||
while (--i >= 0)
|
||||
clk_put(qcom->clks[i]);
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (ret < 0) {
|
||||
while (--i >= 0) {
|
||||
clk_disable_unprepare(qcom->clks[i]);
|
||||
clk_put(qcom->clks[i]);
|
||||
}
|
||||
clk_put(clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
qcom->clks[i] = clk;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_qcom_of_register_core(struct platform_device *pdev)
|
||||
{
|
||||
struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret;
|
||||
|
||||
struct device_node *dwc3_np __free(device_node) = of_get_compatible_child(np,
|
||||
"snps,dwc3");
|
||||
if (!dwc3_np) {
|
||||
dev_err(dev, "failed to find dwc3 core child\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = of_platform_populate(np, NULL, NULL, dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register dwc3 core - %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
qcom->dwc3 = of_find_device_by_node(dwc3_np);
|
||||
if (!qcom->dwc3) {
|
||||
ret = -ENODEV;
|
||||
dev_err(dev, "failed to get dwc3 platform device\n");
|
||||
of_platform_depopulate(dev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dwc3_qcom_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct dwc3_probe_data probe_data = {};
|
||||
struct device *dev = &pdev->dev;
|
||||
struct dwc3_qcom *qcom;
|
||||
int ret, i;
|
||||
struct resource res;
|
||||
struct resource *r;
|
||||
int ret;
|
||||
bool ignore_pipe_clk;
|
||||
bool wakeup_source;
|
||||
|
||||
|
@ -743,7 +656,6 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
|
|||
if (!qcom)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, qcom);
|
||||
qcom->dev = &pdev->dev;
|
||||
|
||||
qcom->resets = devm_reset_control_array_get_optional_exclusive(dev);
|
||||
|
@ -752,6 +664,11 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
|
|||
"failed to get resets\n");
|
||||
}
|
||||
|
||||
ret = devm_clk_bulk_get_all(&pdev->dev, &qcom->clks);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(dev, ret, "failed to get clocks\n");
|
||||
qcom->num_clocks = ret;
|
||||
|
||||
ret = reset_control_assert(qcom->resets);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to assert resets, err=%d\n", ret);
|
||||
|
@ -766,19 +683,26 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
|
|||
goto reset_assert;
|
||||
}
|
||||
|
||||
ret = dwc3_qcom_clk_init(qcom, of_clk_get_parent_count(np));
|
||||
if (ret) {
|
||||
dev_err_probe(dev, ret, "failed to get clocks\n");
|
||||
ret = clk_bulk_prepare_enable(qcom->num_clocks, qcom->clks);
|
||||
if (ret < 0)
|
||||
goto reset_assert;
|
||||
}
|
||||
|
||||
qcom->qscratch_base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(qcom->qscratch_base)) {
|
||||
ret = PTR_ERR(qcom->qscratch_base);
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!r) {
|
||||
ret = -EINVAL;
|
||||
goto clk_disable;
|
||||
}
|
||||
res = *r;
|
||||
res.end = res.start + SDM845_QSCRATCH_BASE_OFFSET;
|
||||
|
||||
qcom->qscratch_base = devm_ioremap(dev, res.end, SDM845_QSCRATCH_SIZE);
|
||||
if (!qcom->qscratch_base) {
|
||||
dev_err(dev, "failed to map qscratch region\n");
|
||||
ret = -ENOMEM;
|
||||
goto clk_disable;
|
||||
}
|
||||
|
||||
ret = dwc3_qcom_setup_irq(pdev);
|
||||
ret = dwc3_qcom_setup_irq(qcom, pdev);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to setup IRQs, err=%d\n", ret);
|
||||
goto clk_disable;
|
||||
|
@ -793,17 +717,21 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
|
|||
if (ignore_pipe_clk)
|
||||
dwc3_qcom_select_utmi_clk(qcom);
|
||||
|
||||
ret = dwc3_qcom_of_register_core(pdev);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register DWC3 Core, err=%d\n", ret);
|
||||
qcom->dwc.dev = dev;
|
||||
probe_data.dwc = &qcom->dwc;
|
||||
probe_data.res = &res;
|
||||
probe_data.ignore_clocks_and_resets = true;
|
||||
ret = dwc3_core_probe(&probe_data);
|
||||
if (ret) {
|
||||
ret = dev_err_probe(dev, ret, "failed to register DWC3 Core\n");
|
||||
goto clk_disable;
|
||||
}
|
||||
|
||||
ret = dwc3_qcom_interconnect_init(qcom);
|
||||
if (ret)
|
||||
goto depopulate;
|
||||
goto remove_core;
|
||||
|
||||
qcom->mode = usb_get_dr_mode(&qcom->dwc3->dev);
|
||||
qcom->mode = usb_get_dr_mode(dev);
|
||||
|
||||
/* enable vbus override for device mode */
|
||||
if (qcom->mode != USB_DR_MODE_HOST)
|
||||
|
@ -816,25 +744,17 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
|
|||
|
||||
wakeup_source = of_property_read_bool(dev->of_node, "wakeup-source");
|
||||
device_init_wakeup(&pdev->dev, wakeup_source);
|
||||
device_init_wakeup(&qcom->dwc3->dev, wakeup_source);
|
||||
|
||||
qcom->is_suspended = false;
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_forbid(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
interconnect_exit:
|
||||
dwc3_qcom_interconnect_exit(qcom);
|
||||
depopulate:
|
||||
of_platform_depopulate(&pdev->dev);
|
||||
platform_device_put(qcom->dwc3);
|
||||
remove_core:
|
||||
dwc3_core_remove(&qcom->dwc);
|
||||
clk_disable:
|
||||
for (i = qcom->num_clocks - 1; i >= 0; i--) {
|
||||
clk_disable_unprepare(qcom->clks[i]);
|
||||
clk_put(qcom->clks[i]);
|
||||
}
|
||||
clk_bulk_disable_unprepare(qcom->num_clocks, qcom->clks);
|
||||
reset_assert:
|
||||
reset_control_assert(qcom->resets);
|
||||
|
||||
|
@ -843,32 +763,28 @@ reset_assert:
|
|||
|
||||
static void dwc3_qcom_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
|
||||
struct device *dev = &pdev->dev;
|
||||
int i;
|
||||
struct dwc3 *dwc = platform_get_drvdata(pdev);
|
||||
struct dwc3_qcom *qcom = to_dwc3_qcom(dwc);
|
||||
|
||||
of_platform_depopulate(&pdev->dev);
|
||||
platform_device_put(qcom->dwc3);
|
||||
dwc3_core_remove(&qcom->dwc);
|
||||
|
||||
for (i = qcom->num_clocks - 1; i >= 0; i--) {
|
||||
clk_disable_unprepare(qcom->clks[i]);
|
||||
clk_put(qcom->clks[i]);
|
||||
}
|
||||
qcom->num_clocks = 0;
|
||||
clk_bulk_disable_unprepare(qcom->num_clocks, qcom->clks);
|
||||
|
||||
dwc3_qcom_interconnect_exit(qcom);
|
||||
reset_control_assert(qcom->resets);
|
||||
|
||||
pm_runtime_allow(dev);
|
||||
pm_runtime_disable(dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused dwc3_qcom_pm_suspend(struct device *dev)
|
||||
static int dwc3_qcom_pm_suspend(struct device *dev)
|
||||
{
|
||||
struct dwc3_qcom *qcom = dev_get_drvdata(dev);
|
||||
struct dwc3 *dwc = dev_get_drvdata(dev);
|
||||
struct dwc3_qcom *qcom = to_dwc3_qcom(dwc);
|
||||
bool wakeup = device_may_wakeup(dev);
|
||||
int ret;
|
||||
|
||||
ret = dwc3_pm_suspend(&qcom->dwc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = dwc3_qcom_suspend(qcom, wakeup);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -878,9 +794,10 @@ static int __maybe_unused dwc3_qcom_pm_suspend(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused dwc3_qcom_pm_resume(struct device *dev)
|
||||
static int dwc3_qcom_pm_resume(struct device *dev)
|
||||
{
|
||||
struct dwc3_qcom *qcom = dev_get_drvdata(dev);
|
||||
struct dwc3 *dwc = dev_get_drvdata(dev);
|
||||
struct dwc3_qcom *qcom = to_dwc3_qcom(dwc);
|
||||
bool wakeup = device_may_wakeup(dev);
|
||||
int ret;
|
||||
|
||||
|
@ -890,31 +807,68 @@ static int __maybe_unused dwc3_qcom_pm_resume(struct device *dev)
|
|||
|
||||
qcom->pm_suspended = false;
|
||||
|
||||
ret = dwc3_pm_resume(&qcom->dwc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused dwc3_qcom_runtime_suspend(struct device *dev)
|
||||
static void dwc3_qcom_complete(struct device *dev)
|
||||
{
|
||||
struct dwc3_qcom *qcom = dev_get_drvdata(dev);
|
||||
struct dwc3 *dwc = dev_get_drvdata(dev);
|
||||
|
||||
dwc3_pm_complete(dwc);
|
||||
}
|
||||
|
||||
static int dwc3_qcom_prepare(struct device *dev)
|
||||
{
|
||||
struct dwc3 *dwc = dev_get_drvdata(dev);
|
||||
|
||||
return dwc3_pm_prepare(dwc);
|
||||
}
|
||||
|
||||
static int dwc3_qcom_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct dwc3 *dwc = dev_get_drvdata(dev);
|
||||
struct dwc3_qcom *qcom = to_dwc3_qcom(dwc);
|
||||
int ret;
|
||||
|
||||
ret = dwc3_runtime_suspend(&qcom->dwc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return dwc3_qcom_suspend(qcom, true);
|
||||
}
|
||||
|
||||
static int __maybe_unused dwc3_qcom_runtime_resume(struct device *dev)
|
||||
static int dwc3_qcom_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct dwc3_qcom *qcom = dev_get_drvdata(dev);
|
||||
struct dwc3 *dwc = dev_get_drvdata(dev);
|
||||
struct dwc3_qcom *qcom = to_dwc3_qcom(dwc);
|
||||
int ret;
|
||||
|
||||
return dwc3_qcom_resume(qcom, true);
|
||||
ret = dwc3_qcom_resume(qcom, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return dwc3_runtime_resume(&qcom->dwc);
|
||||
}
|
||||
|
||||
static int dwc3_qcom_runtime_idle(struct device *dev)
|
||||
{
|
||||
return dwc3_runtime_idle(dev_get_drvdata(dev));
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops dwc3_qcom_dev_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(dwc3_qcom_pm_suspend, dwc3_qcom_pm_resume)
|
||||
SET_RUNTIME_PM_OPS(dwc3_qcom_runtime_suspend, dwc3_qcom_runtime_resume,
|
||||
NULL)
|
||||
SYSTEM_SLEEP_PM_OPS(dwc3_qcom_pm_suspend, dwc3_qcom_pm_resume)
|
||||
RUNTIME_PM_OPS(dwc3_qcom_runtime_suspend, dwc3_qcom_runtime_resume,
|
||||
dwc3_qcom_runtime_idle)
|
||||
.complete = pm_sleep_ptr(dwc3_qcom_complete),
|
||||
.prepare = pm_sleep_ptr(dwc3_qcom_prepare),
|
||||
};
|
||||
|
||||
static const struct of_device_id dwc3_qcom_of_match[] = {
|
||||
{ .compatible = "qcom,dwc3" },
|
||||
{ .compatible = "qcom,snps-dwc3" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dwc3_qcom_of_match);
|
||||
|
@ -924,7 +878,7 @@ static struct platform_driver dwc3_qcom_driver = {
|
|||
.remove = dwc3_qcom_remove,
|
||||
.driver = {
|
||||
.name = "dwc3-qcom",
|
||||
.pm = &dwc3_qcom_dev_pm_ops,
|
||||
.pm = pm_ptr(&dwc3_qcom_dev_pm_ops),
|
||||
.of_match_table = dwc3_qcom_of_match,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* glue.h - DesignWare USB3 DRD glue header
|
||||
*/
|
||||
|
||||
#ifndef __DRIVERS_USB_DWC3_GLUE_H
|
||||
#define __DRIVERS_USB_DWC3_GLUE_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include "core.h"
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
struct dwc3_probe_data {
|
||||
struct dwc3 *dwc;
|
||||
struct resource *res;
|
||||
bool ignore_clocks_and_resets;
|
||||
};
|
||||
|
||||
int dwc3_core_probe(const struct dwc3_probe_data *data);
|
||||
void dwc3_core_remove(struct dwc3 *dwc);
|
||||
|
||||
int dwc3_runtime_suspend(struct dwc3 *dwc);
|
||||
int dwc3_runtime_resume(struct dwc3 *dwc);
|
||||
int dwc3_runtime_idle(struct dwc3 *dwc);
|
||||
int dwc3_pm_suspend(struct dwc3 *dwc);
|
||||
int dwc3_pm_resume(struct dwc3 *dwc);
|
||||
void dwc3_pm_complete(struct dwc3 *dwc);
|
||||
int dwc3_pm_prepare(struct dwc3 *dwc);
|
||||
|
||||
#endif
|
|
@ -182,6 +182,9 @@ int dwc3_host_init(struct dwc3 *dwc)
|
|||
if (DWC3_VER_IS_WITHIN(DWC3, ANY, 300A))
|
||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("quirk-broken-port-ped");
|
||||
|
||||
props[prop_idx++] = PROPERTY_ENTRY_U16("num-hc-interrupters",
|
||||
dwc->num_hc_interrupters);
|
||||
|
||||
if (prop_idx) {
|
||||
ret = device_create_managed_software_node(&xhci->dev, props, NULL);
|
||||
if (ret) {
|
||||
|
|
|
@ -158,7 +158,7 @@ struct usb_ep *usb_ep_autoconfig(
|
|||
if (!ep)
|
||||
return NULL;
|
||||
|
||||
type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
|
||||
type = usb_endpoint_type(desc);
|
||||
|
||||
/* report (variable) full speed bulk maxpacket */
|
||||
if (type == USB_ENDPOINT_XFER_BULK) {
|
||||
|
|
|
@ -62,6 +62,9 @@ struct f_hidg {
|
|||
unsigned short report_desc_length;
|
||||
char *report_desc;
|
||||
unsigned short report_length;
|
||||
unsigned char interval;
|
||||
bool interval_user_set;
|
||||
|
||||
/*
|
||||
* use_out_ep - if true, the OUT Endpoint (interrupt out method)
|
||||
* will be used to receive reports from the host
|
||||
|
@ -75,6 +78,7 @@ struct f_hidg {
|
|||
/* recv report */
|
||||
spinlock_t read_spinlock;
|
||||
wait_queue_head_t read_queue;
|
||||
bool disabled;
|
||||
/* recv report - interrupt out only (use_out_ep == 1) */
|
||||
struct list_head completed_out_req;
|
||||
unsigned int qlen;
|
||||
|
@ -156,10 +160,7 @@ static struct usb_endpoint_descriptor hidg_ss_in_ep_desc = {
|
|||
.bEndpointAddress = USB_DIR_IN,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
/*.wMaxPacketSize = DYNAMIC */
|
||||
.bInterval = 4, /* FIXME: Add this field in the
|
||||
* HID gadget configuration?
|
||||
* (struct hidg_func_descriptor)
|
||||
*/
|
||||
/*.bInterval = DYNAMIC */
|
||||
};
|
||||
|
||||
static struct usb_ss_ep_comp_descriptor hidg_ss_in_comp_desc = {
|
||||
|
@ -177,10 +178,7 @@ static struct usb_endpoint_descriptor hidg_ss_out_ep_desc = {
|
|||
.bEndpointAddress = USB_DIR_OUT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
/*.wMaxPacketSize = DYNAMIC */
|
||||
.bInterval = 4, /* FIXME: Add this field in the
|
||||
* HID gadget configuration?
|
||||
* (struct hidg_func_descriptor)
|
||||
*/
|
||||
/*.bInterval = DYNAMIC */
|
||||
};
|
||||
|
||||
static struct usb_ss_ep_comp_descriptor hidg_ss_out_comp_desc = {
|
||||
|
@ -218,10 +216,7 @@ static struct usb_endpoint_descriptor hidg_hs_in_ep_desc = {
|
|||
.bEndpointAddress = USB_DIR_IN,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
/*.wMaxPacketSize = DYNAMIC */
|
||||
.bInterval = 4, /* FIXME: Add this field in the
|
||||
* HID gadget configuration?
|
||||
* (struct hidg_func_descriptor)
|
||||
*/
|
||||
/* .bInterval = DYNAMIC */
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor hidg_hs_out_ep_desc = {
|
||||
|
@ -230,10 +225,7 @@ static struct usb_endpoint_descriptor hidg_hs_out_ep_desc = {
|
|||
.bEndpointAddress = USB_DIR_OUT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
/*.wMaxPacketSize = DYNAMIC */
|
||||
.bInterval = 4, /* FIXME: Add this field in the
|
||||
* HID gadget configuration?
|
||||
* (struct hidg_func_descriptor)
|
||||
*/
|
||||
/*.bInterval = DYNAMIC */
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *hidg_hs_descriptors_intout[] = {
|
||||
|
@ -259,10 +251,7 @@ static struct usb_endpoint_descriptor hidg_fs_in_ep_desc = {
|
|||
.bEndpointAddress = USB_DIR_IN,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
/*.wMaxPacketSize = DYNAMIC */
|
||||
.bInterval = 10, /* FIXME: Add this field in the
|
||||
* HID gadget configuration?
|
||||
* (struct hidg_func_descriptor)
|
||||
*/
|
||||
/*.bInterval = DYNAMIC */
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor hidg_fs_out_ep_desc = {
|
||||
|
@ -271,10 +260,7 @@ static struct usb_endpoint_descriptor hidg_fs_out_ep_desc = {
|
|||
.bEndpointAddress = USB_DIR_OUT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
/*.wMaxPacketSize = DYNAMIC */
|
||||
.bInterval = 10, /* FIXME: Add this field in the
|
||||
* HID gadget configuration?
|
||||
* (struct hidg_func_descriptor)
|
||||
*/
|
||||
/*.bInterval = DYNAMIC */
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *hidg_fs_descriptors_intout[] = {
|
||||
|
@ -329,7 +315,7 @@ static ssize_t f_hidg_intout_read(struct file *file, char __user *buffer,
|
|||
|
||||
spin_lock_irqsave(&hidg->read_spinlock, flags);
|
||||
|
||||
#define READ_COND_INTOUT (!list_empty(&hidg->completed_out_req))
|
||||
#define READ_COND_INTOUT (!list_empty(&hidg->completed_out_req) || hidg->disabled)
|
||||
|
||||
/* wait for at least one buffer to complete */
|
||||
while (!READ_COND_INTOUT) {
|
||||
|
@ -343,6 +329,11 @@ static ssize_t f_hidg_intout_read(struct file *file, char __user *buffer,
|
|||
spin_lock_irqsave(&hidg->read_spinlock, flags);
|
||||
}
|
||||
|
||||
if (hidg->disabled) {
|
||||
spin_unlock_irqrestore(&hidg->read_spinlock, flags);
|
||||
return -ESHUTDOWN;
|
||||
}
|
||||
|
||||
/* pick the first one */
|
||||
list = list_first_entry(&hidg->completed_out_req,
|
||||
struct f_hidg_req_list, list);
|
||||
|
@ -387,7 +378,7 @@ static ssize_t f_hidg_intout_read(struct file *file, char __user *buffer,
|
|||
return count;
|
||||
}
|
||||
|
||||
#define READ_COND_SSREPORT (hidg->set_report_buf != NULL)
|
||||
#define READ_COND_SSREPORT (hidg->set_report_buf != NULL || hidg->disabled)
|
||||
|
||||
static ssize_t f_hidg_ssreport_read(struct file *file, char __user *buffer,
|
||||
size_t count, loff_t *ptr)
|
||||
|
@ -1012,6 +1003,11 @@ static void hidg_disable(struct usb_function *f)
|
|||
}
|
||||
spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
|
||||
|
||||
spin_lock_irqsave(&hidg->read_spinlock, flags);
|
||||
hidg->disabled = true;
|
||||
spin_unlock_irqrestore(&hidg->read_spinlock, flags);
|
||||
wake_up(&hidg->read_queue);
|
||||
|
||||
spin_lock_irqsave(&hidg->write_spinlock, flags);
|
||||
if (!hidg->write_pending) {
|
||||
free_ep_req(hidg->in_ep, hidg->req);
|
||||
|
@ -1097,6 +1093,10 @@ static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
|||
}
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&hidg->read_spinlock, flags);
|
||||
hidg->disabled = false;
|
||||
spin_unlock_irqrestore(&hidg->read_spinlock, flags);
|
||||
|
||||
if (hidg->in_ep != NULL) {
|
||||
spin_lock_irqsave(&hidg->write_spinlock, flags);
|
||||
hidg->req = req_in;
|
||||
|
@ -1202,6 +1202,16 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
hidg_hs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
|
||||
hidg_fs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
|
||||
hidg_ss_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
|
||||
|
||||
/* IN endpoints: FS default=10ms, HS default=4µ-frame; user override if set */
|
||||
if (!hidg->interval_user_set) {
|
||||
hidg_fs_in_ep_desc.bInterval = 10;
|
||||
hidg_hs_in_ep_desc.bInterval = 4;
|
||||
} else {
|
||||
hidg_fs_in_ep_desc.bInterval = hidg->interval;
|
||||
hidg_hs_in_ep_desc.bInterval = hidg->interval;
|
||||
}
|
||||
|
||||
hidg_ss_out_comp_desc.wBytesPerInterval =
|
||||
cpu_to_le16(hidg->report_length);
|
||||
hidg_hs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
|
||||
|
@ -1224,19 +1234,27 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
hidg_ss_out_ep_desc.bEndpointAddress =
|
||||
hidg_fs_out_ep_desc.bEndpointAddress;
|
||||
|
||||
if (hidg->use_out_ep)
|
||||
if (hidg->use_out_ep) {
|
||||
/* OUT endpoints: same defaults (FS=10, HS=4) unless user set */
|
||||
if (!hidg->interval_user_set) {
|
||||
hidg_fs_out_ep_desc.bInterval = 10;
|
||||
hidg_hs_out_ep_desc.bInterval = 4;
|
||||
} else {
|
||||
hidg_fs_out_ep_desc.bInterval = hidg->interval;
|
||||
hidg_hs_out_ep_desc.bInterval = hidg->interval;
|
||||
}
|
||||
status = usb_assign_descriptors(f,
|
||||
hidg_fs_descriptors_intout,
|
||||
hidg_hs_descriptors_intout,
|
||||
hidg_ss_descriptors_intout,
|
||||
hidg_ss_descriptors_intout);
|
||||
else
|
||||
hidg_fs_descriptors_intout,
|
||||
hidg_hs_descriptors_intout,
|
||||
hidg_ss_descriptors_intout,
|
||||
hidg_ss_descriptors_intout);
|
||||
} else {
|
||||
status = usb_assign_descriptors(f,
|
||||
hidg_fs_descriptors_ssreport,
|
||||
hidg_hs_descriptors_ssreport,
|
||||
hidg_ss_descriptors_ssreport,
|
||||
hidg_ss_descriptors_ssreport);
|
||||
|
||||
}
|
||||
if (status)
|
||||
goto fail;
|
||||
|
||||
|
@ -1408,6 +1426,53 @@ end:
|
|||
|
||||
CONFIGFS_ATTR(f_hid_opts_, report_desc);
|
||||
|
||||
static ssize_t f_hid_opts_interval_show(struct config_item *item, char *page)
|
||||
{
|
||||
struct f_hid_opts *opts = to_f_hid_opts(item);
|
||||
int result;
|
||||
|
||||
mutex_lock(&opts->lock);
|
||||
result = sprintf(page, "%d\n", opts->interval);
|
||||
mutex_unlock(&opts->lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static ssize_t f_hid_opts_interval_store(struct config_item *item,
|
||||
const char *page, size_t len)
|
||||
{
|
||||
struct f_hid_opts *opts = to_f_hid_opts(item);
|
||||
int ret;
|
||||
unsigned int tmp;
|
||||
|
||||
mutex_lock(&opts->lock);
|
||||
if (opts->refcnt) {
|
||||
ret = -EBUSY;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* parse into a wider type first */
|
||||
ret = kstrtouint(page, 0, &tmp);
|
||||
if (ret)
|
||||
goto end;
|
||||
|
||||
/* range-check against unsigned char max */
|
||||
if (tmp > 255) {
|
||||
ret = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
opts->interval = (unsigned char)tmp;
|
||||
opts->interval_user_set = true;
|
||||
ret = len;
|
||||
|
||||
end:
|
||||
mutex_unlock(&opts->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
CONFIGFS_ATTR(f_hid_opts_, interval);
|
||||
|
||||
static ssize_t f_hid_opts_dev_show(struct config_item *item, char *page)
|
||||
{
|
||||
struct f_hid_opts *opts = to_f_hid_opts(item);
|
||||
|
@ -1422,6 +1487,7 @@ static struct configfs_attribute *hid_attrs[] = {
|
|||
&f_hid_opts_attr_protocol,
|
||||
&f_hid_opts_attr_no_out_endpoint,
|
||||
&f_hid_opts_attr_report_length,
|
||||
&f_hid_opts_attr_interval,
|
||||
&f_hid_opts_attr_report_desc,
|
||||
&f_hid_opts_attr_dev,
|
||||
NULL,
|
||||
|
@ -1468,6 +1534,10 @@ static struct usb_function_instance *hidg_alloc_inst(void)
|
|||
if (!opts)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
mutex_init(&opts->lock);
|
||||
|
||||
opts->interval = 4;
|
||||
opts->interval_user_set = false;
|
||||
|
||||
opts->func_inst.free_func_inst = hidg_free_inst;
|
||||
ret = &opts->func_inst;
|
||||
|
||||
|
@ -1546,6 +1616,8 @@ static struct usb_function *hidg_alloc(struct usb_function_instance *fi)
|
|||
hidg->bInterfaceProtocol = opts->protocol;
|
||||
hidg->report_length = opts->report_length;
|
||||
hidg->report_desc_length = opts->report_desc_length;
|
||||
hidg->interval = opts->interval;
|
||||
hidg->interval_user_set = opts->interval_user_set;
|
||||
if (opts->report_desc) {
|
||||
hidg->report_desc = kmemdup(opts->report_desc,
|
||||
opts->report_desc_length,
|
||||
|
|
|
@ -110,7 +110,7 @@ struct fsg_config {
|
|||
};
|
||||
|
||||
static inline struct fsg_opts *
|
||||
fsg_opts_from_func_inst(const struct usb_function_instance *fi)
|
||||
fsg_opts_from_func_inst(struct usb_function_instance *fi)
|
||||
{
|
||||
return container_of(fi, struct fsg_opts, func_inst);
|
||||
}
|
||||
|
|
|
@ -364,6 +364,12 @@ static void gser_suspend(struct usb_function *f)
|
|||
gserial_suspend(&gser->port);
|
||||
}
|
||||
|
||||
static int gser_get_status(struct usb_function *f)
|
||||
{
|
||||
return (f->func_wakeup_armed ? USB_INTRF_STAT_FUNC_RW : 0) |
|
||||
USB_INTRF_STAT_FUNC_RW_CAP;
|
||||
}
|
||||
|
||||
static struct usb_function *gser_alloc(struct usb_function_instance *fi)
|
||||
{
|
||||
struct f_gser *gser;
|
||||
|
@ -387,6 +393,7 @@ static struct usb_function *gser_alloc(struct usb_function_instance *fi)
|
|||
gser->port.func.free_func = gser_free;
|
||||
gser->port.func.resume = gser_resume;
|
||||
gser->port.func.suspend = gser_suspend;
|
||||
gser->port.func.get_status = gser_get_status;
|
||||
|
||||
return &gser->port.func;
|
||||
}
|
||||
|
|
|
@ -1641,14 +1641,14 @@ static struct se_portal_group *usbg_make_tpg(struct se_wwn *wwn,
|
|||
struct usbg_tport *tport = container_of(wwn, struct usbg_tport,
|
||||
tport_wwn);
|
||||
struct usbg_tpg *tpg;
|
||||
unsigned long tpgt;
|
||||
u16 tpgt;
|
||||
int ret;
|
||||
struct f_tcm_opts *opts;
|
||||
unsigned i;
|
||||
|
||||
if (strstr(name, "tpgt_") != name)
|
||||
return ERR_PTR(-EINVAL);
|
||||
if (kstrtoul(name + 5, 0, &tpgt) || tpgt > UINT_MAX)
|
||||
if (kstrtou16(name + 5, 0, &tpgt))
|
||||
return ERR_PTR(-EINVAL);
|
||||
ret = -ENODEV;
|
||||
mutex_lock(&tpg_instances_lock);
|
||||
|
|
|
@ -25,6 +25,8 @@ struct f_hid_opts {
|
|||
unsigned short report_desc_length;
|
||||
unsigned char *report_desc;
|
||||
bool report_desc_alloc;
|
||||
unsigned char interval;
|
||||
bool interval_user_set;
|
||||
|
||||
/*
|
||||
* Protect the data form concurrent access by read/write
|
||||
|
|
|
@ -592,6 +592,17 @@ static int gs_start_io(struct gs_port *port)
|
|||
return status;
|
||||
}
|
||||
|
||||
static int gserial_wakeup_host(struct gserial *gser)
|
||||
{
|
||||
struct usb_function *func = &gser->func;
|
||||
struct usb_gadget *gadget = func->config->cdev->gadget;
|
||||
|
||||
if (func->func_suspended)
|
||||
return usb_func_wakeup(func);
|
||||
else
|
||||
return usb_gadget_wakeup(gadget);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* TTY Driver */
|
||||
|
@ -746,6 +757,8 @@ static ssize_t gs_write(struct tty_struct *tty, const u8 *buf, size_t count)
|
|||
{
|
||||
struct gs_port *port = tty->driver_data;
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
struct gserial *gser = port->port_usb;
|
||||
|
||||
pr_vdebug("gs_write: ttyGS%d (%p) writing %zu bytes\n",
|
||||
port->port_num, tty, count);
|
||||
|
@ -753,6 +766,17 @@ static ssize_t gs_write(struct tty_struct *tty, const u8 *buf, size_t count)
|
|||
spin_lock_irqsave(&port->port_lock, flags);
|
||||
if (count)
|
||||
count = kfifo_in(&port->port_write_buf, buf, count);
|
||||
|
||||
if (port->suspended) {
|
||||
spin_unlock_irqrestore(&port->port_lock, flags);
|
||||
ret = gserial_wakeup_host(gser);
|
||||
if (ret) {
|
||||
pr_debug("ttyGS%d: Remote wakeup failed:%d\n", port->port_num, ret);
|
||||
return count;
|
||||
}
|
||||
spin_lock_irqsave(&port->port_lock, flags);
|
||||
}
|
||||
|
||||
/* treat count == 0 as flush_chars() */
|
||||
if (port->port_usb)
|
||||
gs_start_tx(port);
|
||||
|
@ -781,10 +805,22 @@ static void gs_flush_chars(struct tty_struct *tty)
|
|||
{
|
||||
struct gs_port *port = tty->driver_data;
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
struct gserial *gser = port->port_usb;
|
||||
|
||||
pr_vdebug("gs_flush_chars: (%d,%p)\n", port->port_num, tty);
|
||||
|
||||
spin_lock_irqsave(&port->port_lock, flags);
|
||||
if (port->suspended) {
|
||||
spin_unlock_irqrestore(&port->port_lock, flags);
|
||||
ret = gserial_wakeup_host(gser);
|
||||
if (ret) {
|
||||
pr_debug("ttyGS%d: Remote wakeup failed:%d\n", port->port_num, ret);
|
||||
return;
|
||||
}
|
||||
spin_lock_irqsave(&port->port_lock, flags);
|
||||
}
|
||||
|
||||
if (port->port_usb)
|
||||
gs_start_tx(port);
|
||||
spin_unlock_irqrestore(&port->port_lock, flags);
|
||||
|
@ -1464,6 +1500,20 @@ void gserial_suspend(struct gserial *gser)
|
|||
return;
|
||||
}
|
||||
|
||||
if (port->write_busy || port->write_started) {
|
||||
/* Wakeup to host if there are ongoing transfers */
|
||||
spin_unlock_irqrestore(&serial_port_lock, flags);
|
||||
if (!gserial_wakeup_host(gser))
|
||||
return;
|
||||
|
||||
/* Check if port is valid after acquiring lock back */
|
||||
spin_lock_irqsave(&serial_port_lock, flags);
|
||||
if (!port) {
|
||||
spin_unlock_irqrestore(&serial_port_lock, flags);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
spin_lock(&port->port_lock);
|
||||
spin_unlock(&serial_port_lock);
|
||||
port->suspended = true;
|
||||
|
|
|
@ -74,10 +74,12 @@ static inline struct uvcg_format *to_uvcg_format(struct config_item *item)
|
|||
|
||||
struct uvcg_streaming_header {
|
||||
struct config_item item;
|
||||
struct uvc_input_header_descriptor desc;
|
||||
unsigned linked;
|
||||
struct list_head formats;
|
||||
unsigned num_fmt;
|
||||
|
||||
/* Must be last --ends in a flexible-array member. */
|
||||
struct uvc_input_header_descriptor desc;
|
||||
};
|
||||
|
||||
static inline struct uvcg_streaming_header *to_uvcg_streaming_header(struct config_item *item)
|
||||
|
|
|
@ -188,7 +188,7 @@ static int __init gfs_init(void)
|
|||
/*
|
||||
* Allocate in one chunk for easier maintenance
|
||||
*/
|
||||
f_ffs[0] = kcalloc(func_num * N_CONF, sizeof(*f_ffs), GFP_KERNEL);
|
||||
f_ffs[0] = kcalloc(func_num * N_CONF, sizeof(*f_ffs[0]), GFP_KERNEL);
|
||||
if (!f_ffs[0]) {
|
||||
ret = -ENOMEM;
|
||||
goto no_func;
|
||||
|
|
|
@ -1615,7 +1615,7 @@ static int activate_ep_files (struct dev_data *dev)
|
|||
mutex_init(&data->lock);
|
||||
init_waitqueue_head (&data->wait);
|
||||
|
||||
strncpy (data->name, ep->name, sizeof (data->name) - 1);
|
||||
strscpy(data->name, ep->name);
|
||||
refcount_set (&data->count, 1);
|
||||
data->dev = dev;
|
||||
get_dev (dev);
|
||||
|
|
|
@ -102,12 +102,6 @@ config USB_FSL_USB2
|
|||
dynamically linked module called "fsl_usb2_udc" and force
|
||||
all gadget drivers to also be dynamically linked.
|
||||
|
||||
config USB_FUSB300
|
||||
tristate "Faraday FUSB300 USB Peripheral Controller"
|
||||
depends on !PHYS_ADDR_T_64BIT && HAS_DMA
|
||||
help
|
||||
Faraday usb device controller FUSB300 driver
|
||||
|
||||
config USB_GR_UDC
|
||||
tristate "Aeroflex Gaisler GRUSBDC USB Peripheral Controller Driver"
|
||||
depends on HAS_DMA
|
||||
|
@ -228,21 +222,6 @@ config USB_PXA27X
|
|||
dynamically linked module called "pxa27x_udc" and force all
|
||||
gadget drivers to also be dynamically linked.
|
||||
|
||||
config USB_MV_UDC
|
||||
tristate "Marvell USB2.0 Device Controller"
|
||||
depends on HAS_DMA
|
||||
help
|
||||
Marvell Socs (including PXA and MMP series) include a high speed
|
||||
USB2.0 OTG controller, which can be configured as high speed or
|
||||
full speed USB peripheral.
|
||||
|
||||
config USB_MV_U3D
|
||||
depends on HAS_DMA
|
||||
tristate "MARVELL PXA2128 USB 3.0 controller"
|
||||
help
|
||||
MARVELL PXA2128 Processor series include a super speed USB3.0 device
|
||||
controller, which support super speed USB peripheral.
|
||||
|
||||
config USB_SNP_CORE
|
||||
depends on (USB_AMD5536UDC || USB_SNP_UDC_PLAT)
|
||||
depends on HAS_DMA
|
||||
|
@ -326,29 +305,6 @@ config USB_FSL_QE
|
|||
Set CONFIG_USB_GADGET to "m" to build this driver as a
|
||||
dynamically linked module called "fsl_qe_udc".
|
||||
|
||||
config USB_NET2272
|
||||
depends on HAS_IOMEM
|
||||
tristate "PLX NET2272"
|
||||
help
|
||||
PLX NET2272 is a USB peripheral controller which supports
|
||||
both full and high speed USB 2.0 data transfers.
|
||||
|
||||
It has three configurable endpoints, as well as endpoint zero
|
||||
(for control transfer).
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "net2272" and force all
|
||||
gadget drivers to also be dynamically linked.
|
||||
|
||||
config USB_NET2272_DMA
|
||||
bool "Support external DMA controller"
|
||||
depends on USB_NET2272 && HAS_DMA
|
||||
help
|
||||
The NET2272 part can optionally support an external DMA
|
||||
controller, but your board has to have support in the
|
||||
driver itself.
|
||||
|
||||
If unsure, say "N" here. The driver works fine in PIO mode.
|
||||
|
||||
config USB_NET2280
|
||||
tristate "NetChip NET228x / PLX USB3x8x"
|
||||
depends on USB_PCI
|
||||
|
|
|
@ -9,7 +9,6 @@ udc-core-y := core.o trace.o
|
|||
#
|
||||
obj-$(CONFIG_USB_GADGET) += udc-core.o
|
||||
obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o
|
||||
obj-$(CONFIG_USB_NET2272) += net2272.o
|
||||
obj-$(CONFIG_USB_NET2280) += net2280.o
|
||||
obj-$(CONFIG_USB_SNP_CORE) += snps_udc_core.o
|
||||
obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc_pci.o
|
||||
|
@ -31,10 +30,6 @@ obj-$(CONFIG_USB_RENESAS_USBF) += renesas_usbf.o
|
|||
obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o
|
||||
obj-$(CONFIG_USB_LPC32XX) += lpc32xx_udc.o
|
||||
obj-$(CONFIG_USB_EG20T) += pch_udc.o
|
||||
obj-$(CONFIG_USB_MV_UDC) += mv_udc.o
|
||||
mv_udc-y := mv_udc_core.o
|
||||
obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o
|
||||
obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o
|
||||
obj-$(CONFIG_USB_GR_UDC) += gr_udc.o
|
||||
obj-$(CONFIG_USB_GADGET_XILINX) += udc-xilinx.o
|
||||
obj-$(CONFIG_USB_SNP_UDC_PLAT) += snps_udc_plat.o
|
||||
|
|
|
@ -1570,7 +1570,7 @@ static int gadget_match_driver(struct device *dev, const struct device_driver *d
|
|||
{
|
||||
struct usb_gadget *gadget = dev_to_usb_gadget(dev);
|
||||
struct usb_udc *udc = gadget->udc;
|
||||
struct usb_gadget_driver *driver = container_of(drv,
|
||||
const struct usb_gadget_driver *driver = container_of(drv,
|
||||
struct usb_gadget_driver, driver);
|
||||
|
||||
/* If the driver specifies a udc_name, it must match the UDC's name */
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,675 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Fusb300 UDC (USB gadget)
|
||||
*
|
||||
* Copyright (C) 2010 Faraday Technology Corp.
|
||||
*
|
||||
* Author : Yuan-hsin Chen <yhchen@faraday-tech.com>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __FUSB300_UDC_H__
|
||||
#define __FUSB300_UDC_H__
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#define FUSB300_OFFSET_GCR 0x00
|
||||
#define FUSB300_OFFSET_GTM 0x04
|
||||
#define FUSB300_OFFSET_DAR 0x08
|
||||
#define FUSB300_OFFSET_CSR 0x0C
|
||||
#define FUSB300_OFFSET_CXPORT 0x10
|
||||
#define FUSB300_OFFSET_EPSET0(n) (0x20 + (n - 1) * 0x30)
|
||||
#define FUSB300_OFFSET_EPSET1(n) (0x24 + (n - 1) * 0x30)
|
||||
#define FUSB300_OFFSET_EPSET2(n) (0x28 + (n - 1) * 0x30)
|
||||
#define FUSB300_OFFSET_EPFFR(n) (0x2c + (n - 1) * 0x30)
|
||||
#define FUSB300_OFFSET_EPSTRID(n) (0x40 + (n - 1) * 0x30)
|
||||
#define FUSB300_OFFSET_HSPTM 0x300
|
||||
#define FUSB300_OFFSET_HSCR 0x304
|
||||
#define FUSB300_OFFSET_SSCR0 0x308
|
||||
#define FUSB300_OFFSET_SSCR1 0x30C
|
||||
#define FUSB300_OFFSET_TT 0x310
|
||||
#define FUSB300_OFFSET_DEVNOTF 0x314
|
||||
#define FUSB300_OFFSET_DNC1 0x318
|
||||
#define FUSB300_OFFSET_CS 0x31C
|
||||
#define FUSB300_OFFSET_SOF 0x324
|
||||
#define FUSB300_OFFSET_EFCS 0x328
|
||||
#define FUSB300_OFFSET_IGR0 0x400
|
||||
#define FUSB300_OFFSET_IGR1 0x404
|
||||
#define FUSB300_OFFSET_IGR2 0x408
|
||||
#define FUSB300_OFFSET_IGR3 0x40C
|
||||
#define FUSB300_OFFSET_IGR4 0x410
|
||||
#define FUSB300_OFFSET_IGR5 0x414
|
||||
#define FUSB300_OFFSET_IGER0 0x420
|
||||
#define FUSB300_OFFSET_IGER1 0x424
|
||||
#define FUSB300_OFFSET_IGER2 0x428
|
||||
#define FUSB300_OFFSET_IGER3 0x42C
|
||||
#define FUSB300_OFFSET_IGER4 0x430
|
||||
#define FUSB300_OFFSET_IGER5 0x434
|
||||
#define FUSB300_OFFSET_DMAHMER 0x500
|
||||
#define FUSB300_OFFSET_EPPRDRDY 0x504
|
||||
#define FUSB300_OFFSET_DMAEPMR 0x508
|
||||
#define FUSB300_OFFSET_DMAENR 0x50C
|
||||
#define FUSB300_OFFSET_DMAAPR 0x510
|
||||
#define FUSB300_OFFSET_AHBCR 0x514
|
||||
#define FUSB300_OFFSET_EPPRD_W0(n) (0x520 + (n - 1) * 0x10)
|
||||
#define FUSB300_OFFSET_EPPRD_W1(n) (0x524 + (n - 1) * 0x10)
|
||||
#define FUSB300_OFFSET_EPPRD_W2(n) (0x528 + (n - 1) * 0x10)
|
||||
#define FUSB300_OFFSET_EPRD_PTR(n) (0x52C + (n - 1) * 0x10)
|
||||
#define FUSB300_OFFSET_BUFDBG_START 0x800
|
||||
#define FUSB300_OFFSET_BUFDBG_END 0xBFC
|
||||
#define FUSB300_OFFSET_EPPORT(n) (0x1010 + (n - 1) * 0x10)
|
||||
|
||||
/*
|
||||
* * Global Control Register (offset = 000H)
|
||||
* */
|
||||
#define FUSB300_GCR_SF_RST (1 << 8)
|
||||
#define FUSB300_GCR_VBUS_STATUS (1 << 7)
|
||||
#define FUSB300_GCR_FORCE_HS_SUSP (1 << 6)
|
||||
#define FUSB300_GCR_SYNC_FIFO1_CLR (1 << 5)
|
||||
#define FUSB300_GCR_SYNC_FIFO0_CLR (1 << 4)
|
||||
#define FUSB300_GCR_FIFOCLR (1 << 3)
|
||||
#define FUSB300_GCR_GLINTEN (1 << 2)
|
||||
#define FUSB300_GCR_DEVEN_FS 0x3
|
||||
#define FUSB300_GCR_DEVEN_HS 0x2
|
||||
#define FUSB300_GCR_DEVEN_SS 0x1
|
||||
#define FUSB300_GCR_DEVDIS 0x0
|
||||
#define FUSB300_GCR_DEVEN_MSK 0x3
|
||||
|
||||
|
||||
/*
|
||||
* *Global Test Mode (offset = 004H)
|
||||
* */
|
||||
#define FUSB300_GTM_TST_DIS_SOFGEN (1 << 16)
|
||||
#define FUSB300_GTM_TST_CUR_EP_ENTRY(n) ((n & 0xF) << 12)
|
||||
#define FUSB300_GTM_TST_EP_ENTRY(n) ((n & 0xF) << 8)
|
||||
#define FUSB300_GTM_TST_EP_NUM(n) ((n & 0xF) << 4)
|
||||
#define FUSB300_GTM_TST_FIFO_DEG (1 << 1)
|
||||
#define FUSB300_GTM_TSTMODE (1 << 0)
|
||||
|
||||
/*
|
||||
* * Device Address Register (offset = 008H)
|
||||
* */
|
||||
#define FUSB300_DAR_SETCONFG (1 << 7)
|
||||
#define FUSB300_DAR_DRVADDR(x) (x & 0x7F)
|
||||
#define FUSB300_DAR_DRVADDR_MSK 0x7F
|
||||
|
||||
/*
|
||||
* *Control Transfer Configuration and Status Register
|
||||
* (CX_Config_Status, offset = 00CH)
|
||||
* */
|
||||
#define FUSB300_CSR_LEN(x) ((x & 0xFFFF) << 8)
|
||||
#define FUSB300_CSR_LEN_MSK (0xFFFF << 8)
|
||||
#define FUSB300_CSR_EMP (1 << 4)
|
||||
#define FUSB300_CSR_FUL (1 << 3)
|
||||
#define FUSB300_CSR_CLR (1 << 2)
|
||||
#define FUSB300_CSR_STL (1 << 1)
|
||||
#define FUSB300_CSR_DONE (1 << 0)
|
||||
|
||||
/*
|
||||
* * EPn Setting 0 (EPn_SET0, offset = 020H+(n-1)*30H, n=1~15 )
|
||||
* */
|
||||
#define FUSB300_EPSET0_STL_CLR (1 << 3)
|
||||
#define FUSB300_EPSET0_CLRSEQNUM (1 << 2)
|
||||
#define FUSB300_EPSET0_STL (1 << 0)
|
||||
|
||||
/*
|
||||
* * EPn Setting 1 (EPn_SET1, offset = 024H+(n-1)*30H, n=1~15)
|
||||
* */
|
||||
#define FUSB300_EPSET1_START_ENTRY(x) ((x & 0xFF) << 24)
|
||||
#define FUSB300_EPSET1_START_ENTRY_MSK (0xFF << 24)
|
||||
#define FUSB300_EPSET1_FIFOENTRY(x) ((x & 0x1F) << 12)
|
||||
#define FUSB300_EPSET1_FIFOENTRY_MSK (0x1f << 12)
|
||||
#define FUSB300_EPSET1_INTERVAL(x) ((x & 0x7) << 6)
|
||||
#define FUSB300_EPSET1_BWNUM(x) ((x & 0x3) << 4)
|
||||
#define FUSB300_EPSET1_TYPEISO (1 << 2)
|
||||
#define FUSB300_EPSET1_TYPEBLK (2 << 2)
|
||||
#define FUSB300_EPSET1_TYPEINT (3 << 2)
|
||||
#define FUSB300_EPSET1_TYPE(x) ((x & 0x3) << 2)
|
||||
#define FUSB300_EPSET1_TYPE_MSK (0x3 << 2)
|
||||
#define FUSB300_EPSET1_DIROUT (0 << 1)
|
||||
#define FUSB300_EPSET1_DIRIN (1 << 1)
|
||||
#define FUSB300_EPSET1_DIR(x) ((x & 0x1) << 1)
|
||||
#define FUSB300_EPSET1_DIRIN (1 << 1)
|
||||
#define FUSB300_EPSET1_DIR_MSK ((0x1) << 1)
|
||||
#define FUSB300_EPSET1_ACTDIS 0
|
||||
#define FUSB300_EPSET1_ACTEN 1
|
||||
|
||||
/*
|
||||
* *EPn Setting 2 (EPn_SET2, offset = 028H+(n-1)*30H, n=1~15)
|
||||
* */
|
||||
#define FUSB300_EPSET2_ADDROFS(x) ((x & 0x7FFF) << 16)
|
||||
#define FUSB300_EPSET2_ADDROFS_MSK (0x7fff << 16)
|
||||
#define FUSB300_EPSET2_MPS(x) (x & 0x7FF)
|
||||
#define FUSB300_EPSET2_MPS_MSK 0x7FF
|
||||
|
||||
/*
|
||||
* * EPn FIFO Register (offset = 2cH+(n-1)*30H)
|
||||
* */
|
||||
#define FUSB300_FFR_RST (1 << 31)
|
||||
#define FUSB300_FF_FUL (1 << 30)
|
||||
#define FUSB300_FF_EMPTY (1 << 29)
|
||||
#define FUSB300_FFR_BYCNT 0x1FFFF
|
||||
|
||||
/*
|
||||
* *EPn Stream ID (EPn_STR_ID, offset = 040H+(n-1)*30H, n=1~15)
|
||||
* */
|
||||
#define FUSB300_STRID_STREN (1 << 16)
|
||||
#define FUSB300_STRID_STRID(x) (x & 0xFFFF)
|
||||
|
||||
/*
|
||||
* *HS PHY Test Mode (offset = 300H)
|
||||
* */
|
||||
#define FUSB300_HSPTM_TSTPKDONE (1 << 4)
|
||||
#define FUSB300_HSPTM_TSTPKT (1 << 3)
|
||||
#define FUSB300_HSPTM_TSTSET0NAK (1 << 2)
|
||||
#define FUSB300_HSPTM_TSTKSTA (1 << 1)
|
||||
#define FUSB300_HSPTM_TSTJSTA (1 << 0)
|
||||
|
||||
/*
|
||||
* *HS Control Register (offset = 304H)
|
||||
* */
|
||||
#define FUSB300_HSCR_HS_LPM_PERMIT (1 << 8)
|
||||
#define FUSB300_HSCR_HS_LPM_RMWKUP (1 << 7)
|
||||
#define FUSB300_HSCR_CAP_LPM_RMWKUP (1 << 6)
|
||||
#define FUSB300_HSCR_HS_GOSUSP (1 << 5)
|
||||
#define FUSB300_HSCR_HS_GORMWKU (1 << 4)
|
||||
#define FUSB300_HSCR_CAP_RMWKUP (1 << 3)
|
||||
#define FUSB300_HSCR_IDLECNT_0MS 0
|
||||
#define FUSB300_HSCR_IDLECNT_1MS 1
|
||||
#define FUSB300_HSCR_IDLECNT_2MS 2
|
||||
#define FUSB300_HSCR_IDLECNT_3MS 3
|
||||
#define FUSB300_HSCR_IDLECNT_4MS 4
|
||||
#define FUSB300_HSCR_IDLECNT_5MS 5
|
||||
#define FUSB300_HSCR_IDLECNT_6MS 6
|
||||
#define FUSB300_HSCR_IDLECNT_7MS 7
|
||||
|
||||
/*
|
||||
* * SS Controller Register 0 (offset = 308H)
|
||||
* */
|
||||
#define FUSB300_SSCR0_MAX_INTERVAL(x) ((x & 0x7) << 4)
|
||||
#define FUSB300_SSCR0_U2_FUN_EN (1 << 1)
|
||||
#define FUSB300_SSCR0_U1_FUN_EN (1 << 0)
|
||||
|
||||
/*
|
||||
* * SS Controller Register 1 (offset = 30CH)
|
||||
* */
|
||||
#define FUSB300_SSCR1_GO_U3_DONE (1 << 8)
|
||||
#define FUSB300_SSCR1_TXDEEMPH_LEVEL (1 << 7)
|
||||
#define FUSB300_SSCR1_DIS_SCRMB (1 << 6)
|
||||
#define FUSB300_SSCR1_FORCE_RECOVERY (1 << 5)
|
||||
#define FUSB300_SSCR1_U3_WAKEUP_EN (1 << 4)
|
||||
#define FUSB300_SSCR1_U2_EXIT_EN (1 << 3)
|
||||
#define FUSB300_SSCR1_U1_EXIT_EN (1 << 2)
|
||||
#define FUSB300_SSCR1_U2_ENTRY_EN (1 << 1)
|
||||
#define FUSB300_SSCR1_U1_ENTRY_EN (1 << 0)
|
||||
|
||||
/*
|
||||
* *SS Controller Register 2 (offset = 310H)
|
||||
* */
|
||||
#define FUSB300_SSCR2_SS_TX_SWING (1 << 25)
|
||||
#define FUSB300_SSCR2_FORCE_LINKPM_ACCEPT (1 << 24)
|
||||
#define FUSB300_SSCR2_U2_INACT_TIMEOUT(x) ((x & 0xFF) << 16)
|
||||
#define FUSB300_SSCR2_U1TIMEOUT(x) ((x & 0xFF) << 8)
|
||||
#define FUSB300_SSCR2_U2TIMEOUT(x) (x & 0xFF)
|
||||
|
||||
/*
|
||||
* *SS Device Notification Control (DEV_NOTF, offset = 314H)
|
||||
* */
|
||||
#define FUSB300_DEVNOTF_CONTEXT0(x) ((x & 0xFFFFFF) << 8)
|
||||
#define FUSB300_DEVNOTF_TYPE_DIS 0
|
||||
#define FUSB300_DEVNOTF_TYPE_FUNCWAKE 1
|
||||
#define FUSB300_DEVNOTF_TYPE_LTM 2
|
||||
#define FUSB300_DEVNOTF_TYPE_BUSINT_ADJMSG 3
|
||||
|
||||
/*
|
||||
* *BFM Arbiter Priority Register (BFM_ARB offset = 31CH)
|
||||
* */
|
||||
#define FUSB300_BFMARB_ARB_M1 (1 << 3)
|
||||
#define FUSB300_BFMARB_ARB_M0 (1 << 2)
|
||||
#define FUSB300_BFMARB_ARB_S1 (1 << 1)
|
||||
#define FUSB300_BFMARB_ARB_S0 1
|
||||
|
||||
/*
|
||||
* *Vendor Specific IO Control Register (offset = 320H)
|
||||
* */
|
||||
#define FUSB300_VSIC_VCTLOAD_N (1 << 8)
|
||||
#define FUSB300_VSIC_VCTL(x) (x & 0x3F)
|
||||
|
||||
/*
|
||||
* *SOF Mask Timer (offset = 324H)
|
||||
* */
|
||||
#define FUSB300_SOF_MASK_TIMER_HS 0x044c
|
||||
#define FUSB300_SOF_MASK_TIMER_FS 0x2710
|
||||
|
||||
/*
|
||||
* *Error Flag and Control Status (offset = 328H)
|
||||
* */
|
||||
#define FUSB300_EFCS_PM_STATE_U3 3
|
||||
#define FUSB300_EFCS_PM_STATE_U2 2
|
||||
#define FUSB300_EFCS_PM_STATE_U1 1
|
||||
#define FUSB300_EFCS_PM_STATE_U0 0
|
||||
|
||||
/*
|
||||
* *Interrupt Group 0 Register (offset = 400H)
|
||||
* */
|
||||
#define FUSB300_IGR0_EP15_PRD_INT (1 << 31)
|
||||
#define FUSB300_IGR0_EP14_PRD_INT (1 << 30)
|
||||
#define FUSB300_IGR0_EP13_PRD_INT (1 << 29)
|
||||
#define FUSB300_IGR0_EP12_PRD_INT (1 << 28)
|
||||
#define FUSB300_IGR0_EP11_PRD_INT (1 << 27)
|
||||
#define FUSB300_IGR0_EP10_PRD_INT (1 << 26)
|
||||
#define FUSB300_IGR0_EP9_PRD_INT (1 << 25)
|
||||
#define FUSB300_IGR0_EP8_PRD_INT (1 << 24)
|
||||
#define FUSB300_IGR0_EP7_PRD_INT (1 << 23)
|
||||
#define FUSB300_IGR0_EP6_PRD_INT (1 << 22)
|
||||
#define FUSB300_IGR0_EP5_PRD_INT (1 << 21)
|
||||
#define FUSB300_IGR0_EP4_PRD_INT (1 << 20)
|
||||
#define FUSB300_IGR0_EP3_PRD_INT (1 << 19)
|
||||
#define FUSB300_IGR0_EP2_PRD_INT (1 << 18)
|
||||
#define FUSB300_IGR0_EP1_PRD_INT (1 << 17)
|
||||
#define FUSB300_IGR0_EPn_PRD_INT(n) (1 << (n + 16))
|
||||
|
||||
#define FUSB300_IGR0_EP15_FIFO_INT (1 << 15)
|
||||
#define FUSB300_IGR0_EP14_FIFO_INT (1 << 14)
|
||||
#define FUSB300_IGR0_EP13_FIFO_INT (1 << 13)
|
||||
#define FUSB300_IGR0_EP12_FIFO_INT (1 << 12)
|
||||
#define FUSB300_IGR0_EP11_FIFO_INT (1 << 11)
|
||||
#define FUSB300_IGR0_EP10_FIFO_INT (1 << 10)
|
||||
#define FUSB300_IGR0_EP9_FIFO_INT (1 << 9)
|
||||
#define FUSB300_IGR0_EP8_FIFO_INT (1 << 8)
|
||||
#define FUSB300_IGR0_EP7_FIFO_INT (1 << 7)
|
||||
#define FUSB300_IGR0_EP6_FIFO_INT (1 << 6)
|
||||
#define FUSB300_IGR0_EP5_FIFO_INT (1 << 5)
|
||||
#define FUSB300_IGR0_EP4_FIFO_INT (1 << 4)
|
||||
#define FUSB300_IGR0_EP3_FIFO_INT (1 << 3)
|
||||
#define FUSB300_IGR0_EP2_FIFO_INT (1 << 2)
|
||||
#define FUSB300_IGR0_EP1_FIFO_INT (1 << 1)
|
||||
#define FUSB300_IGR0_EPn_FIFO_INT(n) (1 << n)
|
||||
|
||||
/*
|
||||
* *Interrupt Group 1 Register (offset = 404H)
|
||||
* */
|
||||
#define FUSB300_IGR1_INTGRP5 (1 << 31)
|
||||
#define FUSB300_IGR1_VBUS_CHG_INT (1 << 30)
|
||||
#define FUSB300_IGR1_SYNF1_EMPTY_INT (1 << 29)
|
||||
#define FUSB300_IGR1_SYNF0_EMPTY_INT (1 << 28)
|
||||
#define FUSB300_IGR1_U3_EXIT_FAIL_INT (1 << 27)
|
||||
#define FUSB300_IGR1_U2_EXIT_FAIL_INT (1 << 26)
|
||||
#define FUSB300_IGR1_U1_EXIT_FAIL_INT (1 << 25)
|
||||
#define FUSB300_IGR1_U2_ENTRY_FAIL_INT (1 << 24)
|
||||
#define FUSB300_IGR1_U1_ENTRY_FAIL_INT (1 << 23)
|
||||
#define FUSB300_IGR1_U3_EXIT_INT (1 << 22)
|
||||
#define FUSB300_IGR1_U2_EXIT_INT (1 << 21)
|
||||
#define FUSB300_IGR1_U1_EXIT_INT (1 << 20)
|
||||
#define FUSB300_IGR1_U3_ENTRY_INT (1 << 19)
|
||||
#define FUSB300_IGR1_U2_ENTRY_INT (1 << 18)
|
||||
#define FUSB300_IGR1_U1_ENTRY_INT (1 << 17)
|
||||
#define FUSB300_IGR1_HOT_RST_INT (1 << 16)
|
||||
#define FUSB300_IGR1_WARM_RST_INT (1 << 15)
|
||||
#define FUSB300_IGR1_RESM_INT (1 << 14)
|
||||
#define FUSB300_IGR1_SUSP_INT (1 << 13)
|
||||
#define FUSB300_IGR1_HS_LPM_INT (1 << 12)
|
||||
#define FUSB300_IGR1_USBRST_INT (1 << 11)
|
||||
#define FUSB300_IGR1_DEV_MODE_CHG_INT (1 << 9)
|
||||
#define FUSB300_IGR1_CX_COMABT_INT (1 << 8)
|
||||
#define FUSB300_IGR1_CX_COMFAIL_INT (1 << 7)
|
||||
#define FUSB300_IGR1_CX_CMDEND_INT (1 << 6)
|
||||
#define FUSB300_IGR1_CX_OUT_INT (1 << 5)
|
||||
#define FUSB300_IGR1_CX_IN_INT (1 << 4)
|
||||
#define FUSB300_IGR1_CX_SETUP_INT (1 << 3)
|
||||
#define FUSB300_IGR1_INTGRP4 (1 << 2)
|
||||
#define FUSB300_IGR1_INTGRP3 (1 << 1)
|
||||
#define FUSB300_IGR1_INTGRP2 (1 << 0)
|
||||
|
||||
/*
|
||||
* *Interrupt Group 2 Register (offset = 408H)
|
||||
* */
|
||||
#define FUSB300_IGR2_EP6_STR_ACCEPT_INT (1 << 29)
|
||||
#define FUSB300_IGR2_EP6_STR_RESUME_INT (1 << 28)
|
||||
#define FUSB300_IGR2_EP6_STR_REQ_INT (1 << 27)
|
||||
#define FUSB300_IGR2_EP6_STR_NOTRDY_INT (1 << 26)
|
||||
#define FUSB300_IGR2_EP6_STR_PRIME_INT (1 << 25)
|
||||
#define FUSB300_IGR2_EP5_STR_ACCEPT_INT (1 << 24)
|
||||
#define FUSB300_IGR2_EP5_STR_RESUME_INT (1 << 23)
|
||||
#define FUSB300_IGR2_EP5_STR_REQ_INT (1 << 22)
|
||||
#define FUSB300_IGR2_EP5_STR_NOTRDY_INT (1 << 21)
|
||||
#define FUSB300_IGR2_EP5_STR_PRIME_INT (1 << 20)
|
||||
#define FUSB300_IGR2_EP4_STR_ACCEPT_INT (1 << 19)
|
||||
#define FUSB300_IGR2_EP4_STR_RESUME_INT (1 << 18)
|
||||
#define FUSB300_IGR2_EP4_STR_REQ_INT (1 << 17)
|
||||
#define FUSB300_IGR2_EP4_STR_NOTRDY_INT (1 << 16)
|
||||
#define FUSB300_IGR2_EP4_STR_PRIME_INT (1 << 15)
|
||||
#define FUSB300_IGR2_EP3_STR_ACCEPT_INT (1 << 14)
|
||||
#define FUSB300_IGR2_EP3_STR_RESUME_INT (1 << 13)
|
||||
#define FUSB300_IGR2_EP3_STR_REQ_INT (1 << 12)
|
||||
#define FUSB300_IGR2_EP3_STR_NOTRDY_INT (1 << 11)
|
||||
#define FUSB300_IGR2_EP3_STR_PRIME_INT (1 << 10)
|
||||
#define FUSB300_IGR2_EP2_STR_ACCEPT_INT (1 << 9)
|
||||
#define FUSB300_IGR2_EP2_STR_RESUME_INT (1 << 8)
|
||||
#define FUSB300_IGR2_EP2_STR_REQ_INT (1 << 7)
|
||||
#define FUSB300_IGR2_EP2_STR_NOTRDY_INT (1 << 6)
|
||||
#define FUSB300_IGR2_EP2_STR_PRIME_INT (1 << 5)
|
||||
#define FUSB300_IGR2_EP1_STR_ACCEPT_INT (1 << 4)
|
||||
#define FUSB300_IGR2_EP1_STR_RESUME_INT (1 << 3)
|
||||
#define FUSB300_IGR2_EP1_STR_REQ_INT (1 << 2)
|
||||
#define FUSB300_IGR2_EP1_STR_NOTRDY_INT (1 << 1)
|
||||
#define FUSB300_IGR2_EP1_STR_PRIME_INT (1 << 0)
|
||||
|
||||
#define FUSB300_IGR2_EP_STR_ACCEPT_INT(n) (1 << (5 * n - 1))
|
||||
#define FUSB300_IGR2_EP_STR_RESUME_INT(n) (1 << (5 * n - 2))
|
||||
#define FUSB300_IGR2_EP_STR_REQ_INT(n) (1 << (5 * n - 3))
|
||||
#define FUSB300_IGR2_EP_STR_NOTRDY_INT(n) (1 << (5 * n - 4))
|
||||
#define FUSB300_IGR2_EP_STR_PRIME_INT(n) (1 << (5 * n - 5))
|
||||
|
||||
/*
|
||||
* *Interrupt Group 3 Register (offset = 40CH)
|
||||
* */
|
||||
#define FUSB300_IGR3_EP12_STR_ACCEPT_INT (1 << 29)
|
||||
#define FUSB300_IGR3_EP12_STR_RESUME_INT (1 << 28)
|
||||
#define FUSB300_IGR3_EP12_STR_REQ_INT (1 << 27)
|
||||
#define FUSB300_IGR3_EP12_STR_NOTRDY_INT (1 << 26)
|
||||
#define FUSB300_IGR3_EP12_STR_PRIME_INT (1 << 25)
|
||||
#define FUSB300_IGR3_EP11_STR_ACCEPT_INT (1 << 24)
|
||||
#define FUSB300_IGR3_EP11_STR_RESUME_INT (1 << 23)
|
||||
#define FUSB300_IGR3_EP11_STR_REQ_INT (1 << 22)
|
||||
#define FUSB300_IGR3_EP11_STR_NOTRDY_INT (1 << 21)
|
||||
#define FUSB300_IGR3_EP11_STR_PRIME_INT (1 << 20)
|
||||
#define FUSB300_IGR3_EP10_STR_ACCEPT_INT (1 << 19)
|
||||
#define FUSB300_IGR3_EP10_STR_RESUME_INT (1 << 18)
|
||||
#define FUSB300_IGR3_EP10_STR_REQ_INT (1 << 17)
|
||||
#define FUSB300_IGR3_EP10_STR_NOTRDY_INT (1 << 16)
|
||||
#define FUSB300_IGR3_EP10_STR_PRIME_INT (1 << 15)
|
||||
#define FUSB300_IGR3_EP9_STR_ACCEPT_INT (1 << 14)
|
||||
#define FUSB300_IGR3_EP9_STR_RESUME_INT (1 << 13)
|
||||
#define FUSB300_IGR3_EP9_STR_REQ_INT (1 << 12)
|
||||
#define FUSB300_IGR3_EP9_STR_NOTRDY_INT (1 << 11)
|
||||
#define FUSB300_IGR3_EP9_STR_PRIME_INT (1 << 10)
|
||||
#define FUSB300_IGR3_EP8_STR_ACCEPT_INT (1 << 9)
|
||||
#define FUSB300_IGR3_EP8_STR_RESUME_INT (1 << 8)
|
||||
#define FUSB300_IGR3_EP8_STR_REQ_INT (1 << 7)
|
||||
#define FUSB300_IGR3_EP8_STR_NOTRDY_INT (1 << 6)
|
||||
#define FUSB300_IGR3_EP8_STR_PRIME_INT (1 << 5)
|
||||
#define FUSB300_IGR3_EP7_STR_ACCEPT_INT (1 << 4)
|
||||
#define FUSB300_IGR3_EP7_STR_RESUME_INT (1 << 3)
|
||||
#define FUSB300_IGR3_EP7_STR_REQ_INT (1 << 2)
|
||||
#define FUSB300_IGR3_EP7_STR_NOTRDY_INT (1 << 1)
|
||||
#define FUSB300_IGR3_EP7_STR_PRIME_INT (1 << 0)
|
||||
|
||||
#define FUSB300_IGR3_EP_STR_ACCEPT_INT(n) (1 << (5 * (n - 6) - 1))
|
||||
#define FUSB300_IGR3_EP_STR_RESUME_INT(n) (1 << (5 * (n - 6) - 2))
|
||||
#define FUSB300_IGR3_EP_STR_REQ_INT(n) (1 << (5 * (n - 6) - 3))
|
||||
#define FUSB300_IGR3_EP_STR_NOTRDY_INT(n) (1 << (5 * (n - 6) - 4))
|
||||
#define FUSB300_IGR3_EP_STR_PRIME_INT(n) (1 << (5 * (n - 6) - 5))
|
||||
|
||||
/*
|
||||
* *Interrupt Group 4 Register (offset = 410H)
|
||||
* */
|
||||
#define FUSB300_IGR4_EP15_RX0_INT (1 << 31)
|
||||
#define FUSB300_IGR4_EP14_RX0_INT (1 << 30)
|
||||
#define FUSB300_IGR4_EP13_RX0_INT (1 << 29)
|
||||
#define FUSB300_IGR4_EP12_RX0_INT (1 << 28)
|
||||
#define FUSB300_IGR4_EP11_RX0_INT (1 << 27)
|
||||
#define FUSB300_IGR4_EP10_RX0_INT (1 << 26)
|
||||
#define FUSB300_IGR4_EP9_RX0_INT (1 << 25)
|
||||
#define FUSB300_IGR4_EP8_RX0_INT (1 << 24)
|
||||
#define FUSB300_IGR4_EP7_RX0_INT (1 << 23)
|
||||
#define FUSB300_IGR4_EP6_RX0_INT (1 << 22)
|
||||
#define FUSB300_IGR4_EP5_RX0_INT (1 << 21)
|
||||
#define FUSB300_IGR4_EP4_RX0_INT (1 << 20)
|
||||
#define FUSB300_IGR4_EP3_RX0_INT (1 << 19)
|
||||
#define FUSB300_IGR4_EP2_RX0_INT (1 << 18)
|
||||
#define FUSB300_IGR4_EP1_RX0_INT (1 << 17)
|
||||
#define FUSB300_IGR4_EP_RX0_INT(x) (1 << (x + 16))
|
||||
#define FUSB300_IGR4_EP15_STR_ACCEPT_INT (1 << 14)
|
||||
#define FUSB300_IGR4_EP15_STR_RESUME_INT (1 << 13)
|
||||
#define FUSB300_IGR4_EP15_STR_REQ_INT (1 << 12)
|
||||
#define FUSB300_IGR4_EP15_STR_NOTRDY_INT (1 << 11)
|
||||
#define FUSB300_IGR4_EP15_STR_PRIME_INT (1 << 10)
|
||||
#define FUSB300_IGR4_EP14_STR_ACCEPT_INT (1 << 9)
|
||||
#define FUSB300_IGR4_EP14_STR_RESUME_INT (1 << 8)
|
||||
#define FUSB300_IGR4_EP14_STR_REQ_INT (1 << 7)
|
||||
#define FUSB300_IGR4_EP14_STR_NOTRDY_INT (1 << 6)
|
||||
#define FUSB300_IGR4_EP14_STR_PRIME_INT (1 << 5)
|
||||
#define FUSB300_IGR4_EP13_STR_ACCEPT_INT (1 << 4)
|
||||
#define FUSB300_IGR4_EP13_STR_RESUME_INT (1 << 3)
|
||||
#define FUSB300_IGR4_EP13_STR_REQ_INT (1 << 2)
|
||||
#define FUSB300_IGR4_EP13_STR_NOTRDY_INT (1 << 1)
|
||||
#define FUSB300_IGR4_EP13_STR_PRIME_INT (1 << 0)
|
||||
|
||||
#define FUSB300_IGR4_EP_STR_ACCEPT_INT(n) (1 << (5 * (n - 12) - 1))
|
||||
#define FUSB300_IGR4_EP_STR_RESUME_INT(n) (1 << (5 * (n - 12) - 2))
|
||||
#define FUSB300_IGR4_EP_STR_REQ_INT(n) (1 << (5 * (n - 12) - 3))
|
||||
#define FUSB300_IGR4_EP_STR_NOTRDY_INT(n) (1 << (5 * (n - 12) - 4))
|
||||
#define FUSB300_IGR4_EP_STR_PRIME_INT(n) (1 << (5 * (n - 12) - 5))
|
||||
|
||||
/*
|
||||
* *Interrupt Group 5 Register (offset = 414H)
|
||||
* */
|
||||
#define FUSB300_IGR5_EP_STL_INT(n) (1 << n)
|
||||
|
||||
/*
|
||||
* *Interrupt Enable Group 0 Register (offset = 420H)
|
||||
* */
|
||||
#define FUSB300_IGER0_EEP15_PRD_INT (1 << 31)
|
||||
#define FUSB300_IGER0_EEP14_PRD_INT (1 << 30)
|
||||
#define FUSB300_IGER0_EEP13_PRD_INT (1 << 29)
|
||||
#define FUSB300_IGER0_EEP12_PRD_INT (1 << 28)
|
||||
#define FUSB300_IGER0_EEP11_PRD_INT (1 << 27)
|
||||
#define FUSB300_IGER0_EEP10_PRD_INT (1 << 26)
|
||||
#define FUSB300_IGER0_EEP9_PRD_INT (1 << 25)
|
||||
#define FUSB300_IGER0_EP8_PRD_INT (1 << 24)
|
||||
#define FUSB300_IGER0_EEP7_PRD_INT (1 << 23)
|
||||
#define FUSB300_IGER0_EEP6_PRD_INT (1 << 22)
|
||||
#define FUSB300_IGER0_EEP5_PRD_INT (1 << 21)
|
||||
#define FUSB300_IGER0_EEP4_PRD_INT (1 << 20)
|
||||
#define FUSB300_IGER0_EEP3_PRD_INT (1 << 19)
|
||||
#define FUSB300_IGER0_EEP2_PRD_INT (1 << 18)
|
||||
#define FUSB300_IGER0_EEP1_PRD_INT (1 << 17)
|
||||
#define FUSB300_IGER0_EEPn_PRD_INT(n) (1 << (n + 16))
|
||||
|
||||
#define FUSB300_IGER0_EEP15_FIFO_INT (1 << 15)
|
||||
#define FUSB300_IGER0_EEP14_FIFO_INT (1 << 14)
|
||||
#define FUSB300_IGER0_EEP13_FIFO_INT (1 << 13)
|
||||
#define FUSB300_IGER0_EEP12_FIFO_INT (1 << 12)
|
||||
#define FUSB300_IGER0_EEP11_FIFO_INT (1 << 11)
|
||||
#define FUSB300_IGER0_EEP10_FIFO_INT (1 << 10)
|
||||
#define FUSB300_IGER0_EEP9_FIFO_INT (1 << 9)
|
||||
#define FUSB300_IGER0_EEP8_FIFO_INT (1 << 8)
|
||||
#define FUSB300_IGER0_EEP7_FIFO_INT (1 << 7)
|
||||
#define FUSB300_IGER0_EEP6_FIFO_INT (1 << 6)
|
||||
#define FUSB300_IGER0_EEP5_FIFO_INT (1 << 5)
|
||||
#define FUSB300_IGER0_EEP4_FIFO_INT (1 << 4)
|
||||
#define FUSB300_IGER0_EEP3_FIFO_INT (1 << 3)
|
||||
#define FUSB300_IGER0_EEP2_FIFO_INT (1 << 2)
|
||||
#define FUSB300_IGER0_EEP1_FIFO_INT (1 << 1)
|
||||
#define FUSB300_IGER0_EEPn_FIFO_INT(n) (1 << n)
|
||||
|
||||
/*
|
||||
* *Interrupt Enable Group 1 Register (offset = 424H)
|
||||
* */
|
||||
#define FUSB300_IGER1_EINT_GRP5 (1 << 31)
|
||||
#define FUSB300_IGER1_VBUS_CHG_INT (1 << 30)
|
||||
#define FUSB300_IGER1_SYNF1_EMPTY_INT (1 << 29)
|
||||
#define FUSB300_IGER1_SYNF0_EMPTY_INT (1 << 28)
|
||||
#define FUSB300_IGER1_U3_EXIT_FAIL_INT (1 << 27)
|
||||
#define FUSB300_IGER1_U2_EXIT_FAIL_INT (1 << 26)
|
||||
#define FUSB300_IGER1_U1_EXIT_FAIL_INT (1 << 25)
|
||||
#define FUSB300_IGER1_U2_ENTRY_FAIL_INT (1 << 24)
|
||||
#define FUSB300_IGER1_U1_ENTRY_FAIL_INT (1 << 23)
|
||||
#define FUSB300_IGER1_U3_EXIT_INT (1 << 22)
|
||||
#define FUSB300_IGER1_U2_EXIT_INT (1 << 21)
|
||||
#define FUSB300_IGER1_U1_EXIT_INT (1 << 20)
|
||||
#define FUSB300_IGER1_U3_ENTRY_INT (1 << 19)
|
||||
#define FUSB300_IGER1_U2_ENTRY_INT (1 << 18)
|
||||
#define FUSB300_IGER1_U1_ENTRY_INT (1 << 17)
|
||||
#define FUSB300_IGER1_HOT_RST_INT (1 << 16)
|
||||
#define FUSB300_IGER1_WARM_RST_INT (1 << 15)
|
||||
#define FUSB300_IGER1_RESM_INT (1 << 14)
|
||||
#define FUSB300_IGER1_SUSP_INT (1 << 13)
|
||||
#define FUSB300_IGER1_LPM_INT (1 << 12)
|
||||
#define FUSB300_IGER1_HS_RST_INT (1 << 11)
|
||||
#define FUSB300_IGER1_EDEV_MODE_CHG_INT (1 << 9)
|
||||
#define FUSB300_IGER1_CX_COMABT_INT (1 << 8)
|
||||
#define FUSB300_IGER1_CX_COMFAIL_INT (1 << 7)
|
||||
#define FUSB300_IGER1_CX_CMDEND_INT (1 << 6)
|
||||
#define FUSB300_IGER1_CX_OUT_INT (1 << 5)
|
||||
#define FUSB300_IGER1_CX_IN_INT (1 << 4)
|
||||
#define FUSB300_IGER1_CX_SETUP_INT (1 << 3)
|
||||
#define FUSB300_IGER1_INTGRP4 (1 << 2)
|
||||
#define FUSB300_IGER1_INTGRP3 (1 << 1)
|
||||
#define FUSB300_IGER1_INTGRP2 (1 << 0)
|
||||
|
||||
/*
|
||||
* *Interrupt Enable Group 2 Register (offset = 428H)
|
||||
* */
|
||||
#define FUSB300_IGER2_EEP_STR_ACCEPT_INT(n) (1 << (5 * n - 1))
|
||||
#define FUSB300_IGER2_EEP_STR_RESUME_INT(n) (1 << (5 * n - 2))
|
||||
#define FUSB300_IGER2_EEP_STR_REQ_INT(n) (1 << (5 * n - 3))
|
||||
#define FUSB300_IGER2_EEP_STR_NOTRDY_INT(n) (1 << (5 * n - 4))
|
||||
#define FUSB300_IGER2_EEP_STR_PRIME_INT(n) (1 << (5 * n - 5))
|
||||
|
||||
/*
|
||||
* *Interrupt Enable Group 3 Register (offset = 42CH)
|
||||
* */
|
||||
|
||||
#define FUSB300_IGER3_EEP_STR_ACCEPT_INT(n) (1 << (5 * (n - 6) - 1))
|
||||
#define FUSB300_IGER3_EEP_STR_RESUME_INT(n) (1 << (5 * (n - 6) - 2))
|
||||
#define FUSB300_IGER3_EEP_STR_REQ_INT(n) (1 << (5 * (n - 6) - 3))
|
||||
#define FUSB300_IGER3_EEP_STR_NOTRDY_INT(n) (1 << (5 * (n - 6) - 4))
|
||||
#define FUSB300_IGER3_EEP_STR_PRIME_INT(n) (1 << (5 * (n - 6) - 5))
|
||||
|
||||
/*
|
||||
* *Interrupt Enable Group 4 Register (offset = 430H)
|
||||
* */
|
||||
|
||||
#define FUSB300_IGER4_EEP_RX0_INT(n) (1 << (n + 16))
|
||||
#define FUSB300_IGER4_EEP_STR_ACCEPT_INT(n) (1 << (5 * (n - 6) - 1))
|
||||
#define FUSB300_IGER4_EEP_STR_RESUME_INT(n) (1 << (5 * (n - 6) - 2))
|
||||
#define FUSB300_IGER4_EEP_STR_REQ_INT(n) (1 << (5 * (n - 6) - 3))
|
||||
#define FUSB300_IGER4_EEP_STR_NOTRDY_INT(n) (1 << (5 * (n - 6) - 4))
|
||||
#define FUSB300_IGER4_EEP_STR_PRIME_INT(n) (1 << (5 * (n - 6) - 5))
|
||||
|
||||
/* EP PRD Ready (EP_PRD_RDY, offset = 504H) */
|
||||
|
||||
#define FUSB300_EPPRDR_EP15_PRD_RDY (1 << 15)
|
||||
#define FUSB300_EPPRDR_EP14_PRD_RDY (1 << 14)
|
||||
#define FUSB300_EPPRDR_EP13_PRD_RDY (1 << 13)
|
||||
#define FUSB300_EPPRDR_EP12_PRD_RDY (1 << 12)
|
||||
#define FUSB300_EPPRDR_EP11_PRD_RDY (1 << 11)
|
||||
#define FUSB300_EPPRDR_EP10_PRD_RDY (1 << 10)
|
||||
#define FUSB300_EPPRDR_EP9_PRD_RDY (1 << 9)
|
||||
#define FUSB300_EPPRDR_EP8_PRD_RDY (1 << 8)
|
||||
#define FUSB300_EPPRDR_EP7_PRD_RDY (1 << 7)
|
||||
#define FUSB300_EPPRDR_EP6_PRD_RDY (1 << 6)
|
||||
#define FUSB300_EPPRDR_EP5_PRD_RDY (1 << 5)
|
||||
#define FUSB300_EPPRDR_EP4_PRD_RDY (1 << 4)
|
||||
#define FUSB300_EPPRDR_EP3_PRD_RDY (1 << 3)
|
||||
#define FUSB300_EPPRDR_EP2_PRD_RDY (1 << 2)
|
||||
#define FUSB300_EPPRDR_EP1_PRD_RDY (1 << 1)
|
||||
#define FUSB300_EPPRDR_EP_PRD_RDY(n) (1 << n)
|
||||
|
||||
/* AHB Bus Control Register (offset = 514H) */
|
||||
#define FUSB300_AHBBCR_S1_SPLIT_ON (1 << 17)
|
||||
#define FUSB300_AHBBCR_S0_SPLIT_ON (1 << 16)
|
||||
#define FUSB300_AHBBCR_S1_1entry (0 << 12)
|
||||
#define FUSB300_AHBBCR_S1_4entry (3 << 12)
|
||||
#define FUSB300_AHBBCR_S1_8entry (5 << 12)
|
||||
#define FUSB300_AHBBCR_S1_16entry (7 << 12)
|
||||
#define FUSB300_AHBBCR_S0_1entry (0 << 8)
|
||||
#define FUSB300_AHBBCR_S0_4entry (3 << 8)
|
||||
#define FUSB300_AHBBCR_S0_8entry (5 << 8)
|
||||
#define FUSB300_AHBBCR_S0_16entry (7 << 8)
|
||||
#define FUSB300_AHBBCR_M1_BURST_SINGLE (0 << 4)
|
||||
#define FUSB300_AHBBCR_M1_BURST_INCR (1 << 4)
|
||||
#define FUSB300_AHBBCR_M1_BURST_INCR4 (3 << 4)
|
||||
#define FUSB300_AHBBCR_M1_BURST_INCR8 (5 << 4)
|
||||
#define FUSB300_AHBBCR_M1_BURST_INCR16 (7 << 4)
|
||||
#define FUSB300_AHBBCR_M0_BURST_SINGLE 0
|
||||
#define FUSB300_AHBBCR_M0_BURST_INCR 1
|
||||
#define FUSB300_AHBBCR_M0_BURST_INCR4 3
|
||||
#define FUSB300_AHBBCR_M0_BURST_INCR8 5
|
||||
#define FUSB300_AHBBCR_M0_BURST_INCR16 7
|
||||
#define FUSB300_IGER5_EEP_STL_INT(n) (1 << n)
|
||||
|
||||
/* WORD 0 Data Structure of PRD Table */
|
||||
#define FUSB300_EPPRD0_M (1 << 30)
|
||||
#define FUSB300_EPPRD0_O (1 << 29)
|
||||
/* The finished prd */
|
||||
#define FUSB300_EPPRD0_F (1 << 28)
|
||||
#define FUSB300_EPPRD0_I (1 << 27)
|
||||
#define FUSB300_EPPRD0_A (1 << 26)
|
||||
/* To decide HW point to first prd at next time */
|
||||
#define FUSB300_EPPRD0_L (1 << 25)
|
||||
#define FUSB300_EPPRD0_H (1 << 24)
|
||||
#define FUSB300_EPPRD0_BTC(n) (n & 0xFFFFFF)
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
#define FUSB300_MAX_NUM_EP 16
|
||||
|
||||
#define FUSB300_FIFO_ENTRY_NUM 8
|
||||
#define FUSB300_MAX_FIFO_ENTRY 8
|
||||
|
||||
#define SS_CTL_MAX_PACKET_SIZE 0x200
|
||||
#define SS_BULK_MAX_PACKET_SIZE 0x400
|
||||
#define SS_INT_MAX_PACKET_SIZE 0x400
|
||||
#define SS_ISO_MAX_PACKET_SIZE 0x400
|
||||
|
||||
#define HS_BULK_MAX_PACKET_SIZE 0x200
|
||||
#define HS_CTL_MAX_PACKET_SIZE 0x40
|
||||
#define HS_INT_MAX_PACKET_SIZE 0x400
|
||||
#define HS_ISO_MAX_PACKET_SIZE 0x400
|
||||
|
||||
struct fusb300_ep_info {
|
||||
u8 epnum;
|
||||
u8 type;
|
||||
u8 interval;
|
||||
u8 dir_in;
|
||||
u16 maxpacket;
|
||||
u16 addrofs;
|
||||
u16 bw_num;
|
||||
};
|
||||
|
||||
struct fusb300_request {
|
||||
|
||||
struct usb_request req;
|
||||
struct list_head queue;
|
||||
};
|
||||
|
||||
|
||||
struct fusb300_ep {
|
||||
struct usb_ep ep;
|
||||
struct fusb300 *fusb300;
|
||||
|
||||
struct list_head queue;
|
||||
unsigned stall:1;
|
||||
unsigned wedged:1;
|
||||
unsigned use_dma:1;
|
||||
|
||||
unsigned char epnum;
|
||||
unsigned char type;
|
||||
};
|
||||
|
||||
struct fusb300 {
|
||||
spinlock_t lock;
|
||||
void __iomem *reg;
|
||||
|
||||
unsigned long irq_trigger;
|
||||
|
||||
struct usb_gadget gadget;
|
||||
struct usb_gadget_driver *driver;
|
||||
|
||||
struct fusb300_ep *ep[FUSB300_MAX_NUM_EP];
|
||||
|
||||
struct usb_request *ep0_req; /* for internal request */
|
||||
__le16 ep0_data;
|
||||
u32 ep0_length; /* for internal request */
|
||||
u8 ep0_dir; /* 0/0x80 out/in */
|
||||
|
||||
u8 fifo_entry_num; /* next start fifo entry */
|
||||
u32 addrofs; /* next fifo address offset */
|
||||
u8 reenum; /* if re-enumeration */
|
||||
};
|
||||
|
||||
#define to_fusb300(g) (container_of((g), struct fusb300, gadget))
|
||||
|
||||
#endif
|
|
@ -1629,7 +1629,7 @@ static int lpc32xx_ep_enable(struct usb_ep *_ep,
|
|||
return -ESHUTDOWN;
|
||||
}
|
||||
|
||||
tmp = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
|
||||
tmp = usb_endpoint_type(desc);
|
||||
switch (tmp) {
|
||||
case USB_ENDPOINT_XFER_CONTROL:
|
||||
return -EINVAL;
|
||||
|
|
|
@ -1,317 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2011 Marvell International Ltd. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __MV_U3D_H
|
||||
#define __MV_U3D_H
|
||||
|
||||
#define MV_U3D_EP_CONTEXT_ALIGNMENT 32
|
||||
#define MV_U3D_TRB_ALIGNMENT 16
|
||||
#define MV_U3D_DMA_BOUNDARY 4096
|
||||
#define MV_U3D_EP0_MAX_PKT_SIZE 512
|
||||
|
||||
/* ep0 transfer state */
|
||||
#define MV_U3D_WAIT_FOR_SETUP 0
|
||||
#define MV_U3D_DATA_STATE_XMIT 1
|
||||
#define MV_U3D_DATA_STATE_NEED_ZLP 2
|
||||
#define MV_U3D_WAIT_FOR_OUT_STATUS 3
|
||||
#define MV_U3D_DATA_STATE_RECV 4
|
||||
#define MV_U3D_STATUS_STAGE 5
|
||||
|
||||
#define MV_U3D_EP_MAX_LENGTH_TRANSFER 0x10000
|
||||
|
||||
/* USB3 Interrupt Status */
|
||||
#define MV_U3D_USBINT_SETUP 0x00000001
|
||||
#define MV_U3D_USBINT_RX_COMPLETE 0x00000002
|
||||
#define MV_U3D_USBINT_TX_COMPLETE 0x00000004
|
||||
#define MV_U3D_USBINT_UNDER_RUN 0x00000008
|
||||
#define MV_U3D_USBINT_RXDESC_ERR 0x00000010
|
||||
#define MV_U3D_USBINT_TXDESC_ERR 0x00000020
|
||||
#define MV_U3D_USBINT_RX_TRB_COMPLETE 0x00000040
|
||||
#define MV_U3D_USBINT_TX_TRB_COMPLETE 0x00000080
|
||||
#define MV_U3D_USBINT_VBUS_VALID 0x00010000
|
||||
#define MV_U3D_USBINT_STORAGE_CMD_FULL 0x00020000
|
||||
#define MV_U3D_USBINT_LINK_CHG 0x01000000
|
||||
|
||||
/* USB3 Interrupt Enable */
|
||||
#define MV_U3D_INTR_ENABLE_SETUP 0x00000001
|
||||
#define MV_U3D_INTR_ENABLE_RX_COMPLETE 0x00000002
|
||||
#define MV_U3D_INTR_ENABLE_TX_COMPLETE 0x00000004
|
||||
#define MV_U3D_INTR_ENABLE_UNDER_RUN 0x00000008
|
||||
#define MV_U3D_INTR_ENABLE_RXDESC_ERR 0x00000010
|
||||
#define MV_U3D_INTR_ENABLE_TXDESC_ERR 0x00000020
|
||||
#define MV_U3D_INTR_ENABLE_RX_TRB_COMPLETE 0x00000040
|
||||
#define MV_U3D_INTR_ENABLE_TX_TRB_COMPLETE 0x00000080
|
||||
#define MV_U3D_INTR_ENABLE_RX_BUFFER_ERR 0x00000100
|
||||
#define MV_U3D_INTR_ENABLE_VBUS_VALID 0x00010000
|
||||
#define MV_U3D_INTR_ENABLE_STORAGE_CMD_FULL 0x00020000
|
||||
#define MV_U3D_INTR_ENABLE_LINK_CHG 0x01000000
|
||||
#define MV_U3D_INTR_ENABLE_PRIME_STATUS 0x02000000
|
||||
|
||||
/* USB3 Link Change */
|
||||
#define MV_U3D_LINK_CHANGE_LINK_UP 0x00000001
|
||||
#define MV_U3D_LINK_CHANGE_SUSPEND 0x00000002
|
||||
#define MV_U3D_LINK_CHANGE_RESUME 0x00000004
|
||||
#define MV_U3D_LINK_CHANGE_WRESET 0x00000008
|
||||
#define MV_U3D_LINK_CHANGE_HRESET 0x00000010
|
||||
#define MV_U3D_LINK_CHANGE_VBUS_INVALID 0x00000020
|
||||
#define MV_U3D_LINK_CHANGE_INACT 0x00000040
|
||||
#define MV_U3D_LINK_CHANGE_DISABLE_AFTER_U0 0x00000080
|
||||
#define MV_U3D_LINK_CHANGE_U1 0x00000100
|
||||
#define MV_U3D_LINK_CHANGE_U2 0x00000200
|
||||
#define MV_U3D_LINK_CHANGE_U3 0x00000400
|
||||
|
||||
/* bridge setting */
|
||||
#define MV_U3D_BRIDGE_SETTING_VBUS_VALID (1 << 16)
|
||||
|
||||
/* Command Register Bit Masks */
|
||||
#define MV_U3D_CMD_RUN_STOP 0x00000001
|
||||
#define MV_U3D_CMD_CTRL_RESET 0x00000002
|
||||
|
||||
/* ep control register */
|
||||
#define MV_U3D_EPXCR_EP_TYPE_CONTROL 0
|
||||
#define MV_U3D_EPXCR_EP_TYPE_ISOC 1
|
||||
#define MV_U3D_EPXCR_EP_TYPE_BULK 2
|
||||
#define MV_U3D_EPXCR_EP_TYPE_INT 3
|
||||
#define MV_U3D_EPXCR_EP_ENABLE_SHIFT 4
|
||||
#define MV_U3D_EPXCR_MAX_BURST_SIZE_SHIFT 12
|
||||
#define MV_U3D_EPXCR_MAX_PACKET_SIZE_SHIFT 16
|
||||
#define MV_U3D_USB_BULK_BURST_OUT 6
|
||||
#define MV_U3D_USB_BULK_BURST_IN 14
|
||||
|
||||
#define MV_U3D_EPXCR_EP_FLUSH (1 << 7)
|
||||
#define MV_U3D_EPXCR_EP_HALT (1 << 1)
|
||||
#define MV_U3D_EPXCR_EP_INIT (1)
|
||||
|
||||
/* TX/RX Status Register */
|
||||
#define MV_U3D_XFERSTATUS_COMPLETE_SHIFT 24
|
||||
#define MV_U3D_COMPLETE_INVALID 0
|
||||
#define MV_U3D_COMPLETE_SUCCESS 1
|
||||
#define MV_U3D_COMPLETE_BUFF_ERR 2
|
||||
#define MV_U3D_COMPLETE_SHORT_PACKET 3
|
||||
#define MV_U3D_COMPLETE_TRB_ERR 5
|
||||
#define MV_U3D_XFERSTATUS_TRB_LENGTH_MASK (0xFFFFFF)
|
||||
|
||||
#define MV_U3D_USB_LINK_BYPASS_VBUS 0x8
|
||||
|
||||
#define MV_U3D_LTSSM_PHY_INIT_DONE 0x80000000
|
||||
#define MV_U3D_LTSSM_NEVER_GO_COMPLIANCE 0x40000000
|
||||
|
||||
#define MV_U3D_USB3_OP_REGS_OFFSET 0x100
|
||||
#define MV_U3D_USB3_PHY_OFFSET 0xB800
|
||||
|
||||
#define DCS_ENABLE 0x1
|
||||
|
||||
/* timeout */
|
||||
#define MV_U3D_RESET_TIMEOUT 10000
|
||||
#define MV_U3D_FLUSH_TIMEOUT 100000
|
||||
#define MV_U3D_OWN_TIMEOUT 10000
|
||||
#define LOOPS_USEC_SHIFT 4
|
||||
#define LOOPS_USEC (1 << LOOPS_USEC_SHIFT)
|
||||
#define LOOPS(timeout) ((timeout) >> LOOPS_USEC_SHIFT)
|
||||
|
||||
/* ep direction */
|
||||
#define MV_U3D_EP_DIR_IN 1
|
||||
#define MV_U3D_EP_DIR_OUT 0
|
||||
#define mv_u3d_ep_dir(ep) (((ep)->ep_num == 0) ? \
|
||||
((ep)->u3d->ep0_dir) : ((ep)->direction))
|
||||
|
||||
/* usb capability registers */
|
||||
struct mv_u3d_cap_regs {
|
||||
u32 rsvd[5];
|
||||
u32 dboff; /* doorbell register offset */
|
||||
u32 rtsoff; /* runtime register offset */
|
||||
u32 vuoff; /* vendor unique register offset */
|
||||
};
|
||||
|
||||
/* operation registers */
|
||||
struct mv_u3d_op_regs {
|
||||
u32 usbcmd; /* Command register */
|
||||
u32 rsvd1[11];
|
||||
u32 dcbaapl; /* Device Context Base Address low register */
|
||||
u32 dcbaaph; /* Device Context Base Address high register */
|
||||
u32 rsvd2[243];
|
||||
u32 portsc; /* port status and control register*/
|
||||
u32 portlinkinfo; /* port link info register*/
|
||||
u32 rsvd3[9917];
|
||||
u32 doorbell; /* doorbell register */
|
||||
};
|
||||
|
||||
/* control endpoint enable registers */
|
||||
struct epxcr {
|
||||
u32 epxoutcr0; /* ep out control 0 register */
|
||||
u32 epxoutcr1; /* ep out control 1 register */
|
||||
u32 epxincr0; /* ep in control 0 register */
|
||||
u32 epxincr1; /* ep in control 1 register */
|
||||
};
|
||||
|
||||
/* transfer status registers */
|
||||
struct xferstatus {
|
||||
u32 curdeqlo; /* current TRB pointer low */
|
||||
u32 curdeqhi; /* current TRB pointer high */
|
||||
u32 statuslo; /* transfer status low */
|
||||
u32 statushi; /* transfer status high */
|
||||
};
|
||||
|
||||
/* vendor unique control registers */
|
||||
struct mv_u3d_vuc_regs {
|
||||
u32 ctrlepenable; /* control endpoint enable register */
|
||||
u32 setuplock; /* setup lock register */
|
||||
u32 endcomplete; /* endpoint transfer complete register */
|
||||
u32 intrcause; /* interrupt cause register */
|
||||
u32 intrenable; /* interrupt enable register */
|
||||
u32 trbcomplete; /* TRB complete register */
|
||||
u32 linkchange; /* link change register */
|
||||
u32 rsvd1[5];
|
||||
u32 trbunderrun; /* TRB underrun register */
|
||||
u32 rsvd2[43];
|
||||
u32 bridgesetting; /* bridge setting register */
|
||||
u32 rsvd3[7];
|
||||
struct xferstatus txst[16]; /* TX status register */
|
||||
struct xferstatus rxst[16]; /* RX status register */
|
||||
u32 ltssm; /* LTSSM control register */
|
||||
u32 pipe; /* PIPE control register */
|
||||
u32 linkcr0; /* link control 0 register */
|
||||
u32 linkcr1; /* link control 1 register */
|
||||
u32 rsvd6[60];
|
||||
u32 mib0; /* MIB0 counter register */
|
||||
u32 usblink; /* usb link control register */
|
||||
u32 ltssmstate; /* LTSSM state register */
|
||||
u32 linkerrorcause; /* link error cause register */
|
||||
u32 rsvd7[60];
|
||||
u32 devaddrtiebrkr; /* device address and tie breaker */
|
||||
u32 itpinfo0; /* ITP info 0 register */
|
||||
u32 itpinfo1; /* ITP info 1 register */
|
||||
u32 rsvd8[61];
|
||||
struct epxcr epcr[16]; /* ep control register */
|
||||
u32 rsvd9[64];
|
||||
u32 phyaddr; /* PHY address register */
|
||||
u32 phydata; /* PHY data register */
|
||||
};
|
||||
|
||||
/* Endpoint context structure */
|
||||
struct mv_u3d_ep_context {
|
||||
u32 rsvd0;
|
||||
u32 rsvd1;
|
||||
u32 trb_addr_lo; /* TRB address low 32 bit */
|
||||
u32 trb_addr_hi; /* TRB address high 32 bit */
|
||||
u32 rsvd2;
|
||||
u32 rsvd3;
|
||||
struct usb_ctrlrequest setup_buffer; /* setup data buffer */
|
||||
};
|
||||
|
||||
/* TRB control data structure */
|
||||
struct mv_u3d_trb_ctrl {
|
||||
u32 own:1; /* owner of TRB */
|
||||
u32 rsvd1:3;
|
||||
u32 chain:1; /* associate this TRB with the
|
||||
next TRB on the Ring */
|
||||
u32 ioc:1; /* interrupt on complete */
|
||||
u32 rsvd2:4;
|
||||
u32 type:6; /* TRB type */
|
||||
#define TYPE_NORMAL 1
|
||||
#define TYPE_DATA 3
|
||||
#define TYPE_LINK 6
|
||||
u32 dir:1; /* Working at data stage of control endpoint
|
||||
operation. 0 is OUT and 1 is IN. */
|
||||
u32 rsvd3:15;
|
||||
};
|
||||
|
||||
/* TRB data structure
|
||||
* For multiple TRB, all the TRBs' physical address should be continuous.
|
||||
*/
|
||||
struct mv_u3d_trb_hw {
|
||||
u32 buf_addr_lo; /* data buffer address low 32 bit */
|
||||
u32 buf_addr_hi; /* data buffer address high 32 bit */
|
||||
u32 trb_len; /* transfer length */
|
||||
struct mv_u3d_trb_ctrl ctrl; /* TRB control data */
|
||||
};
|
||||
|
||||
/* TRB structure */
|
||||
struct mv_u3d_trb {
|
||||
struct mv_u3d_trb_hw *trb_hw; /* point to the trb_hw structure */
|
||||
dma_addr_t trb_dma; /* dma address for this trb_hw */
|
||||
struct list_head trb_list; /* trb list */
|
||||
};
|
||||
|
||||
/* device data structure */
|
||||
struct mv_u3d {
|
||||
struct usb_gadget gadget;
|
||||
struct usb_gadget_driver *driver;
|
||||
spinlock_t lock; /* device lock */
|
||||
struct completion *done;
|
||||
struct device *dev;
|
||||
int irq;
|
||||
|
||||
/* usb controller registers */
|
||||
struct mv_u3d_cap_regs __iomem *cap_regs;
|
||||
struct mv_u3d_op_regs __iomem *op_regs;
|
||||
struct mv_u3d_vuc_regs __iomem *vuc_regs;
|
||||
void __iomem *phy_regs;
|
||||
|
||||
unsigned int max_eps;
|
||||
struct mv_u3d_ep_context *ep_context;
|
||||
size_t ep_context_size;
|
||||
dma_addr_t ep_context_dma;
|
||||
|
||||
struct dma_pool *trb_pool; /* for TRB data structure */
|
||||
struct mv_u3d_ep *eps;
|
||||
|
||||
struct mv_u3d_req *status_req; /* ep0 status request */
|
||||
struct usb_ctrlrequest local_setup_buff; /* store setup data*/
|
||||
|
||||
unsigned int resume_state; /* USB state to resume */
|
||||
unsigned int usb_state; /* USB current state */
|
||||
unsigned int ep0_state; /* Endpoint zero state */
|
||||
unsigned int ep0_dir;
|
||||
|
||||
unsigned int dev_addr; /* device address */
|
||||
|
||||
unsigned int errors;
|
||||
|
||||
unsigned softconnect:1;
|
||||
unsigned vbus_active:1; /* vbus is active or not */
|
||||
unsigned remote_wakeup:1; /* support remote wakeup */
|
||||
unsigned clock_gating:1; /* clock gating or not */
|
||||
unsigned active:1; /* udc is active or not */
|
||||
unsigned vbus_valid_detect:1; /* udc vbus detection */
|
||||
|
||||
struct mv_usb_addon_irq *vbus;
|
||||
unsigned int power;
|
||||
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
/* endpoint data structure */
|
||||
struct mv_u3d_ep {
|
||||
struct usb_ep ep;
|
||||
struct mv_u3d *u3d;
|
||||
struct list_head queue; /* ep request queued hardware */
|
||||
struct list_head req_list; /* list of ep request */
|
||||
struct mv_u3d_ep_context *ep_context; /* ep context */
|
||||
u32 direction;
|
||||
char name[14];
|
||||
u32 processing; /* there is ep request
|
||||
queued on haredware */
|
||||
spinlock_t req_lock; /* ep lock */
|
||||
unsigned wedge:1;
|
||||
unsigned enabled:1;
|
||||
unsigned ep_type:2;
|
||||
unsigned ep_num:8;
|
||||
};
|
||||
|
||||
/* request data structure */
|
||||
struct mv_u3d_req {
|
||||
struct usb_request req;
|
||||
struct mv_u3d_ep *ep;
|
||||
struct list_head queue; /* ep requst queued on hardware */
|
||||
struct list_head list; /* ep request list */
|
||||
struct list_head trb_list; /* trb list of a request */
|
||||
|
||||
struct mv_u3d_trb *trb_head; /* point to first trb of a request */
|
||||
unsigned trb_count; /* TRB number in the chain */
|
||||
unsigned chain; /* TRB chain or not */
|
||||
};
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -1,309 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2011 Marvell International Ltd. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __MV_UDC_H
|
||||
#define __MV_UDC_H
|
||||
|
||||
#define VUSBHS_MAX_PORTS 8
|
||||
|
||||
#define DQH_ALIGNMENT 2048
|
||||
#define DTD_ALIGNMENT 64
|
||||
#define DMA_BOUNDARY 4096
|
||||
|
||||
#define EP_DIR_IN 1
|
||||
#define EP_DIR_OUT 0
|
||||
|
||||
#define DMA_ADDR_INVALID (~(dma_addr_t)0)
|
||||
|
||||
#define EP0_MAX_PKT_SIZE 64
|
||||
/* ep0 transfer state */
|
||||
#define WAIT_FOR_SETUP 0
|
||||
#define DATA_STATE_XMIT 1
|
||||
#define DATA_STATE_NEED_ZLP 2
|
||||
#define WAIT_FOR_OUT_STATUS 3
|
||||
#define DATA_STATE_RECV 4
|
||||
|
||||
#define CAPLENGTH_MASK (0xff)
|
||||
#define DCCPARAMS_DEN_MASK (0x1f)
|
||||
|
||||
#define HCSPARAMS_PPC (0x10)
|
||||
|
||||
/* Frame Index Register Bit Masks */
|
||||
#define USB_FRINDEX_MASKS 0x3fff
|
||||
|
||||
/* Command Register Bit Masks */
|
||||
#define USBCMD_RUN_STOP (0x00000001)
|
||||
#define USBCMD_CTRL_RESET (0x00000002)
|
||||
#define USBCMD_SETUP_TRIPWIRE_SET (0x00002000)
|
||||
#define USBCMD_SETUP_TRIPWIRE_CLEAR (~USBCMD_SETUP_TRIPWIRE_SET)
|
||||
|
||||
#define USBCMD_ATDTW_TRIPWIRE_SET (0x00004000)
|
||||
#define USBCMD_ATDTW_TRIPWIRE_CLEAR (~USBCMD_ATDTW_TRIPWIRE_SET)
|
||||
|
||||
/* bit 15,3,2 are for frame list size */
|
||||
#define USBCMD_FRAME_SIZE_1024 (0x00000000) /* 000 */
|
||||
#define USBCMD_FRAME_SIZE_512 (0x00000004) /* 001 */
|
||||
#define USBCMD_FRAME_SIZE_256 (0x00000008) /* 010 */
|
||||
#define USBCMD_FRAME_SIZE_128 (0x0000000C) /* 011 */
|
||||
#define USBCMD_FRAME_SIZE_64 (0x00008000) /* 100 */
|
||||
#define USBCMD_FRAME_SIZE_32 (0x00008004) /* 101 */
|
||||
#define USBCMD_FRAME_SIZE_16 (0x00008008) /* 110 */
|
||||
#define USBCMD_FRAME_SIZE_8 (0x0000800C) /* 111 */
|
||||
|
||||
#define EPCTRL_TX_ALL_MASK (0xFFFF0000)
|
||||
#define EPCTRL_RX_ALL_MASK (0x0000FFFF)
|
||||
|
||||
#define EPCTRL_TX_DATA_TOGGLE_RST (0x00400000)
|
||||
#define EPCTRL_TX_EP_STALL (0x00010000)
|
||||
#define EPCTRL_RX_EP_STALL (0x00000001)
|
||||
#define EPCTRL_RX_DATA_TOGGLE_RST (0x00000040)
|
||||
#define EPCTRL_RX_ENABLE (0x00000080)
|
||||
#define EPCTRL_TX_ENABLE (0x00800000)
|
||||
#define EPCTRL_CONTROL (0x00000000)
|
||||
#define EPCTRL_ISOCHRONOUS (0x00040000)
|
||||
#define EPCTRL_BULK (0x00080000)
|
||||
#define EPCTRL_INT (0x000C0000)
|
||||
#define EPCTRL_TX_TYPE (0x000C0000)
|
||||
#define EPCTRL_RX_TYPE (0x0000000C)
|
||||
#define EPCTRL_DATA_TOGGLE_INHIBIT (0x00000020)
|
||||
#define EPCTRL_TX_EP_TYPE_SHIFT (18)
|
||||
#define EPCTRL_RX_EP_TYPE_SHIFT (2)
|
||||
|
||||
#define EPCOMPLETE_MAX_ENDPOINTS (16)
|
||||
|
||||
/* endpoint list address bit masks */
|
||||
#define USB_EP_LIST_ADDRESS_MASK 0xfffff800
|
||||
|
||||
#define PORTSCX_W1C_BITS 0x2a
|
||||
#define PORTSCX_PORT_RESET 0x00000100
|
||||
#define PORTSCX_PORT_POWER 0x00001000
|
||||
#define PORTSCX_FORCE_FULL_SPEED_CONNECT 0x01000000
|
||||
#define PORTSCX_PAR_XCVR_SELECT 0xC0000000
|
||||
#define PORTSCX_PORT_FORCE_RESUME 0x00000040
|
||||
#define PORTSCX_PORT_SUSPEND 0x00000080
|
||||
#define PORTSCX_PORT_SPEED_FULL 0x00000000
|
||||
#define PORTSCX_PORT_SPEED_LOW 0x04000000
|
||||
#define PORTSCX_PORT_SPEED_HIGH 0x08000000
|
||||
#define PORTSCX_PORT_SPEED_MASK 0x0C000000
|
||||
|
||||
/* USB MODE Register Bit Masks */
|
||||
#define USBMODE_CTRL_MODE_IDLE 0x00000000
|
||||
#define USBMODE_CTRL_MODE_DEVICE 0x00000002
|
||||
#define USBMODE_CTRL_MODE_HOST 0x00000003
|
||||
#define USBMODE_CTRL_MODE_RSV 0x00000001
|
||||
#define USBMODE_SETUP_LOCK_OFF 0x00000008
|
||||
#define USBMODE_STREAM_DISABLE 0x00000010
|
||||
|
||||
/* USB STS Register Bit Masks */
|
||||
#define USBSTS_INT 0x00000001
|
||||
#define USBSTS_ERR 0x00000002
|
||||
#define USBSTS_PORT_CHANGE 0x00000004
|
||||
#define USBSTS_FRM_LST_ROLL 0x00000008
|
||||
#define USBSTS_SYS_ERR 0x00000010
|
||||
#define USBSTS_IAA 0x00000020
|
||||
#define USBSTS_RESET 0x00000040
|
||||
#define USBSTS_SOF 0x00000080
|
||||
#define USBSTS_SUSPEND 0x00000100
|
||||
#define USBSTS_HC_HALTED 0x00001000
|
||||
#define USBSTS_RCL 0x00002000
|
||||
#define USBSTS_PERIODIC_SCHEDULE 0x00004000
|
||||
#define USBSTS_ASYNC_SCHEDULE 0x00008000
|
||||
|
||||
|
||||
/* Interrupt Enable Register Bit Masks */
|
||||
#define USBINTR_INT_EN (0x00000001)
|
||||
#define USBINTR_ERR_INT_EN (0x00000002)
|
||||
#define USBINTR_PORT_CHANGE_DETECT_EN (0x00000004)
|
||||
|
||||
#define USBINTR_ASYNC_ADV_AAE (0x00000020)
|
||||
#define USBINTR_ASYNC_ADV_AAE_ENABLE (0x00000020)
|
||||
#define USBINTR_ASYNC_ADV_AAE_DISABLE (0xFFFFFFDF)
|
||||
|
||||
#define USBINTR_RESET_EN (0x00000040)
|
||||
#define USBINTR_SOF_UFRAME_EN (0x00000080)
|
||||
#define USBINTR_DEVICE_SUSPEND (0x00000100)
|
||||
|
||||
#define USB_DEVICE_ADDRESS_MASK (0xfe000000)
|
||||
#define USB_DEVICE_ADDRESS_BIT_SHIFT (25)
|
||||
|
||||
struct mv_cap_regs {
|
||||
u32 caplength_hciversion;
|
||||
u32 hcsparams; /* HC structural parameters */
|
||||
u32 hccparams; /* HC Capability Parameters*/
|
||||
u32 reserved[5];
|
||||
u32 dciversion; /* DC version number and reserved 16 bits */
|
||||
u32 dccparams; /* DC Capability Parameters */
|
||||
};
|
||||
|
||||
struct mv_op_regs {
|
||||
u32 usbcmd; /* Command register */
|
||||
u32 usbsts; /* Status register */
|
||||
u32 usbintr; /* Interrupt enable */
|
||||
u32 frindex; /* Frame index */
|
||||
u32 reserved1[1];
|
||||
u32 deviceaddr; /* Device Address */
|
||||
u32 eplistaddr; /* Endpoint List Address */
|
||||
u32 ttctrl; /* HOST TT status and control */
|
||||
u32 burstsize; /* Programmable Burst Size */
|
||||
u32 txfilltuning; /* Host Transmit Pre-Buffer Packet Tuning */
|
||||
u32 reserved[4];
|
||||
u32 epnak; /* Endpoint NAK */
|
||||
u32 epnaken; /* Endpoint NAK Enable */
|
||||
u32 configflag; /* Configured Flag register */
|
||||
u32 portsc[VUSBHS_MAX_PORTS]; /* Port Status/Control x, x = 1..8 */
|
||||
u32 otgsc;
|
||||
u32 usbmode; /* USB Host/Device mode */
|
||||
u32 epsetupstat; /* Endpoint Setup Status */
|
||||
u32 epprime; /* Endpoint Initialize */
|
||||
u32 epflush; /* Endpoint De-initialize */
|
||||
u32 epstatus; /* Endpoint Status */
|
||||
u32 epcomplete; /* Endpoint Interrupt On Complete */
|
||||
u32 epctrlx[16]; /* Endpoint Control, where x = 0.. 15 */
|
||||
u32 mcr; /* Mux Control */
|
||||
u32 isr; /* Interrupt Status */
|
||||
u32 ier; /* Interrupt Enable */
|
||||
};
|
||||
|
||||
struct mv_udc {
|
||||
struct usb_gadget gadget;
|
||||
struct usb_gadget_driver *driver;
|
||||
spinlock_t lock;
|
||||
struct completion *done;
|
||||
struct platform_device *dev;
|
||||
int irq;
|
||||
|
||||
struct mv_cap_regs __iomem *cap_regs;
|
||||
struct mv_op_regs __iomem *op_regs;
|
||||
void __iomem *phy_regs;
|
||||
unsigned int max_eps;
|
||||
struct mv_dqh *ep_dqh;
|
||||
size_t ep_dqh_size;
|
||||
dma_addr_t ep_dqh_dma;
|
||||
|
||||
struct dma_pool *dtd_pool;
|
||||
struct mv_ep *eps;
|
||||
|
||||
struct mv_dtd *dtd_head;
|
||||
struct mv_dtd *dtd_tail;
|
||||
unsigned int dtd_entries;
|
||||
|
||||
struct mv_req *status_req;
|
||||
struct usb_ctrlrequest local_setup_buff;
|
||||
|
||||
unsigned int resume_state; /* USB state to resume */
|
||||
unsigned int usb_state; /* USB current state */
|
||||
unsigned int ep0_state; /* Endpoint zero state */
|
||||
unsigned int ep0_dir;
|
||||
|
||||
unsigned int dev_addr;
|
||||
unsigned int test_mode;
|
||||
|
||||
int errors;
|
||||
unsigned softconnect:1,
|
||||
vbus_active:1,
|
||||
remote_wakeup:1,
|
||||
softconnected:1,
|
||||
force_fs:1,
|
||||
clock_gating:1,
|
||||
active:1,
|
||||
stopped:1; /* stop bit is setted */
|
||||
|
||||
struct work_struct vbus_work;
|
||||
struct workqueue_struct *qwork;
|
||||
|
||||
struct usb_phy *transceiver;
|
||||
|
||||
struct mv_usb_platform_data *pdata;
|
||||
|
||||
/* some SOC has mutiple clock sources for USB*/
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
/* endpoint data structure */
|
||||
struct mv_ep {
|
||||
struct usb_ep ep;
|
||||
struct mv_udc *udc;
|
||||
struct list_head queue;
|
||||
struct mv_dqh *dqh;
|
||||
u32 direction;
|
||||
char name[14];
|
||||
unsigned stopped:1,
|
||||
wedge:1,
|
||||
ep_type:2,
|
||||
ep_num:8;
|
||||
};
|
||||
|
||||
/* request data structure */
|
||||
struct mv_req {
|
||||
struct usb_request req;
|
||||
struct mv_dtd *dtd, *head, *tail;
|
||||
struct mv_ep *ep;
|
||||
struct list_head queue;
|
||||
unsigned int test_mode;
|
||||
unsigned dtd_count;
|
||||
unsigned mapped:1;
|
||||
};
|
||||
|
||||
#define EP_QUEUE_HEAD_MULT_POS 30
|
||||
#define EP_QUEUE_HEAD_ZLT_SEL 0x20000000
|
||||
#define EP_QUEUE_HEAD_MAX_PKT_LEN_POS 16
|
||||
#define EP_QUEUE_HEAD_MAX_PKT_LEN(ep_info) (((ep_info)>>16)&0x07ff)
|
||||
#define EP_QUEUE_HEAD_IOS 0x00008000
|
||||
#define EP_QUEUE_HEAD_NEXT_TERMINATE 0x00000001
|
||||
#define EP_QUEUE_HEAD_IOC 0x00008000
|
||||
#define EP_QUEUE_HEAD_MULTO 0x00000C00
|
||||
#define EP_QUEUE_HEAD_STATUS_HALT 0x00000040
|
||||
#define EP_QUEUE_HEAD_STATUS_ACTIVE 0x00000080
|
||||
#define EP_QUEUE_CURRENT_OFFSET_MASK 0x00000FFF
|
||||
#define EP_QUEUE_HEAD_NEXT_POINTER_MASK 0xFFFFFFE0
|
||||
#define EP_QUEUE_FRINDEX_MASK 0x000007FF
|
||||
#define EP_MAX_LENGTH_TRANSFER 0x4000
|
||||
|
||||
struct mv_dqh {
|
||||
/* Bits 16..26 Bit 15 is Interrupt On Setup */
|
||||
u32 max_packet_length;
|
||||
u32 curr_dtd_ptr; /* Current dTD Pointer */
|
||||
u32 next_dtd_ptr; /* Next dTD Pointer */
|
||||
/* Total bytes (16..30), IOC (15), INT (8), STS (0-7) */
|
||||
u32 size_ioc_int_sts;
|
||||
u32 buff_ptr0; /* Buffer pointer Page 0 (12-31) */
|
||||
u32 buff_ptr1; /* Buffer pointer Page 1 (12-31) */
|
||||
u32 buff_ptr2; /* Buffer pointer Page 2 (12-31) */
|
||||
u32 buff_ptr3; /* Buffer pointer Page 3 (12-31) */
|
||||
u32 buff_ptr4; /* Buffer pointer Page 4 (12-31) */
|
||||
u32 reserved1;
|
||||
/* 8 bytes of setup data that follows the Setup PID */
|
||||
u8 setup_buffer[8];
|
||||
u32 reserved2[4];
|
||||
};
|
||||
|
||||
|
||||
#define DTD_NEXT_TERMINATE (0x00000001)
|
||||
#define DTD_IOC (0x00008000)
|
||||
#define DTD_STATUS_ACTIVE (0x00000080)
|
||||
#define DTD_STATUS_HALTED (0x00000040)
|
||||
#define DTD_STATUS_DATA_BUFF_ERR (0x00000020)
|
||||
#define DTD_STATUS_TRANSACTION_ERR (0x00000008)
|
||||
#define DTD_RESERVED_FIELDS (0x00007F00)
|
||||
#define DTD_ERROR_MASK (0x68)
|
||||
#define DTD_ADDR_MASK (0xFFFFFFE0)
|
||||
#define DTD_PACKET_SIZE 0x7FFF0000
|
||||
#define DTD_LENGTH_BIT_POS (16)
|
||||
|
||||
struct mv_dtd {
|
||||
u32 dtd_next;
|
||||
u32 size_ioc_sts;
|
||||
u32 buff_ptr0; /* Buffer pointer Page 0 */
|
||||
u32 buff_ptr1; /* Buffer pointer Page 1 */
|
||||
u32 buff_ptr2; /* Buffer pointer Page 2 */
|
||||
u32 buff_ptr3; /* Buffer pointer Page 3 */
|
||||
u32 buff_ptr4; /* Buffer pointer Page 4 */
|
||||
u32 scratch_ptr;
|
||||
/* 32 bytes */
|
||||
dma_addr_t td_dma; /* dma address for this td */
|
||||
struct mv_dtd *next_dtd_virt;
|
||||
};
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,584 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* PLX NET2272 high/full speed USB device controller
|
||||
*
|
||||
* Copyright (C) 2005-2006 PLX Technology, Inc.
|
||||
* Copyright (C) 2006-2011 Analog Devices, Inc.
|
||||
*/
|
||||
|
||||
#ifndef __NET2272_H__
|
||||
#define __NET2272_H__
|
||||
|
||||
/* Main Registers */
|
||||
#define REGADDRPTR 0x00
|
||||
#define REGDATA 0x01
|
||||
#define IRQSTAT0 0x02
|
||||
#define ENDPOINT_0_INTERRUPT 0
|
||||
#define ENDPOINT_A_INTERRUPT 1
|
||||
#define ENDPOINT_B_INTERRUPT 2
|
||||
#define ENDPOINT_C_INTERRUPT 3
|
||||
#define VIRTUALIZED_ENDPOINT_INTERRUPT 4
|
||||
#define SETUP_PACKET_INTERRUPT 5
|
||||
#define DMA_DONE_INTERRUPT 6
|
||||
#define SOF_INTERRUPT 7
|
||||
#define IRQSTAT1 0x03
|
||||
#define CONTROL_STATUS_INTERRUPT 1
|
||||
#define VBUS_INTERRUPT 2
|
||||
#define SUSPEND_REQUEST_INTERRUPT 3
|
||||
#define SUSPEND_REQUEST_CHANGE_INTERRUPT 4
|
||||
#define RESUME_INTERRUPT 5
|
||||
#define ROOT_PORT_RESET_INTERRUPT 6
|
||||
#define RESET_STATUS 7
|
||||
#define PAGESEL 0x04
|
||||
#define DMAREQ 0x1c
|
||||
#define DMA_ENDPOINT_SELECT 0
|
||||
#define DREQ_POLARITY 1
|
||||
#define DACK_POLARITY 2
|
||||
#define EOT_POLARITY 3
|
||||
#define DMA_CONTROL_DACK 4
|
||||
#define DMA_REQUEST_ENABLE 5
|
||||
#define DMA_REQUEST 6
|
||||
#define DMA_BUFFER_VALID 7
|
||||
#define SCRATCH 0x1d
|
||||
#define IRQENB0 0x20
|
||||
#define ENDPOINT_0_INTERRUPT_ENABLE 0
|
||||
#define ENDPOINT_A_INTERRUPT_ENABLE 1
|
||||
#define ENDPOINT_B_INTERRUPT_ENABLE 2
|
||||
#define ENDPOINT_C_INTERRUPT_ENABLE 3
|
||||
#define VIRTUALIZED_ENDPOINT_INTERRUPT_ENABLE 4
|
||||
#define SETUP_PACKET_INTERRUPT_ENABLE 5
|
||||
#define DMA_DONE_INTERRUPT_ENABLE 6
|
||||
#define SOF_INTERRUPT_ENABLE 7
|
||||
#define IRQENB1 0x21
|
||||
#define VBUS_INTERRUPT_ENABLE 2
|
||||
#define SUSPEND_REQUEST_INTERRUPT_ENABLE 3
|
||||
#define SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE 4
|
||||
#define RESUME_INTERRUPT_ENABLE 5
|
||||
#define ROOT_PORT_RESET_INTERRUPT_ENABLE 6
|
||||
#define LOCCTL 0x22
|
||||
#define DATA_WIDTH 0
|
||||
#define LOCAL_CLOCK_OUTPUT 1
|
||||
#define LOCAL_CLOCK_OUTPUT_OFF 0
|
||||
#define LOCAL_CLOCK_OUTPUT_3_75MHZ 1
|
||||
#define LOCAL_CLOCK_OUTPUT_7_5MHZ 2
|
||||
#define LOCAL_CLOCK_OUTPUT_15MHZ 3
|
||||
#define LOCAL_CLOCK_OUTPUT_30MHZ 4
|
||||
#define LOCAL_CLOCK_OUTPUT_60MHZ 5
|
||||
#define DMA_SPLIT_BUS_MODE 4
|
||||
#define BYTE_SWAP 5
|
||||
#define BUFFER_CONFIGURATION 6
|
||||
#define BUFFER_CONFIGURATION_EPA512_EPB512 0
|
||||
#define BUFFER_CONFIGURATION_EPA1024_EPB512 1
|
||||
#define BUFFER_CONFIGURATION_EPA1024_EPB1024 2
|
||||
#define BUFFER_CONFIGURATION_EPA1024DB 3
|
||||
#define CHIPREV_LEGACY 0x23
|
||||
#define NET2270_LEGACY_REV 0x40
|
||||
#define LOCCTL1 0x24
|
||||
#define DMA_MODE 0
|
||||
#define SLOW_DREQ 0
|
||||
#define FAST_DREQ 1
|
||||
#define BURST_MODE 2
|
||||
#define DMA_DACK_ENABLE 2
|
||||
#define CHIPREV_2272 0x25
|
||||
#define CHIPREV_NET2272_R1 0x10
|
||||
#define CHIPREV_NET2272_R1A 0x11
|
||||
/* USB Registers */
|
||||
#define USBCTL0 0x18
|
||||
#define IO_WAKEUP_ENABLE 1
|
||||
#define USB_DETECT_ENABLE 3
|
||||
#define USB_ROOT_PORT_WAKEUP_ENABLE 5
|
||||
#define USBCTL1 0x19
|
||||
#define VBUS_PIN 0
|
||||
#define USB_FULL_SPEED 1
|
||||
#define USB_HIGH_SPEED 2
|
||||
#define GENERATE_RESUME 3
|
||||
#define VIRTUAL_ENDPOINT_ENABLE 4
|
||||
#define FRAME0 0x1a
|
||||
#define FRAME1 0x1b
|
||||
#define OURADDR 0x30
|
||||
#define FORCE_IMMEDIATE 7
|
||||
#define USBDIAG 0x31
|
||||
#define FORCE_TRANSMIT_CRC_ERROR 0
|
||||
#define PREVENT_TRANSMIT_BIT_STUFF 1
|
||||
#define FORCE_RECEIVE_ERROR 2
|
||||
#define FAST_TIMES 4
|
||||
#define USBTEST 0x32
|
||||
#define TEST_MODE_SELECT 0
|
||||
#define NORMAL_OPERATION 0
|
||||
#define XCVRDIAG 0x33
|
||||
#define FORCE_FULL_SPEED 2
|
||||
#define FORCE_HIGH_SPEED 3
|
||||
#define OPMODE 4
|
||||
#define NORMAL_OPERATION 0
|
||||
#define NON_DRIVING 1
|
||||
#define DISABLE_BITSTUFF_AND_NRZI_ENCODE 2
|
||||
#define LINESTATE 6
|
||||
#define SE0_STATE 0
|
||||
#define J_STATE 1
|
||||
#define K_STATE 2
|
||||
#define SE1_STATE 3
|
||||
#define VIRTOUT0 0x34
|
||||
#define VIRTOUT1 0x35
|
||||
#define VIRTIN0 0x36
|
||||
#define VIRTIN1 0x37
|
||||
#define SETUP0 0x40
|
||||
#define SETUP1 0x41
|
||||
#define SETUP2 0x42
|
||||
#define SETUP3 0x43
|
||||
#define SETUP4 0x44
|
||||
#define SETUP5 0x45
|
||||
#define SETUP6 0x46
|
||||
#define SETUP7 0x47
|
||||
/* Endpoint Registers (Paged via PAGESEL) */
|
||||
#define EP_DATA 0x05
|
||||
#define EP_STAT0 0x06
|
||||
#define DATA_IN_TOKEN_INTERRUPT 0
|
||||
#define DATA_OUT_TOKEN_INTERRUPT 1
|
||||
#define DATA_PACKET_TRANSMITTED_INTERRUPT 2
|
||||
#define DATA_PACKET_RECEIVED_INTERRUPT 3
|
||||
#define SHORT_PACKET_TRANSFERRED_INTERRUPT 4
|
||||
#define NAK_OUT_PACKETS 5
|
||||
#define BUFFER_EMPTY 6
|
||||
#define BUFFER_FULL 7
|
||||
#define EP_STAT1 0x07
|
||||
#define TIMEOUT 0
|
||||
#define USB_OUT_ACK_SENT 1
|
||||
#define USB_OUT_NAK_SENT 2
|
||||
#define USB_IN_ACK_RCVD 3
|
||||
#define USB_IN_NAK_SENT 4
|
||||
#define USB_STALL_SENT 5
|
||||
#define LOCAL_OUT_ZLP 6
|
||||
#define BUFFER_FLUSH 7
|
||||
#define EP_TRANSFER0 0x08
|
||||
#define EP_TRANSFER1 0x09
|
||||
#define EP_TRANSFER2 0x0a
|
||||
#define EP_IRQENB 0x0b
|
||||
#define DATA_IN_TOKEN_INTERRUPT_ENABLE 0
|
||||
#define DATA_OUT_TOKEN_INTERRUPT_ENABLE 1
|
||||
#define DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE 2
|
||||
#define DATA_PACKET_RECEIVED_INTERRUPT_ENABLE 3
|
||||
#define SHORT_PACKET_TRANSFERRED_INTERRUPT_ENABLE 4
|
||||
#define EP_AVAIL0 0x0c
|
||||
#define EP_AVAIL1 0x0d
|
||||
#define EP_RSPCLR 0x0e
|
||||
#define EP_RSPSET 0x0f
|
||||
#define ENDPOINT_HALT 0
|
||||
#define ENDPOINT_TOGGLE 1
|
||||
#define NAK_OUT_PACKETS_MODE 2
|
||||
#define CONTROL_STATUS_PHASE_HANDSHAKE 3
|
||||
#define INTERRUPT_MODE 4
|
||||
#define AUTOVALIDATE 5
|
||||
#define HIDE_STATUS_PHASE 6
|
||||
#define ALT_NAK_OUT_PACKETS 7
|
||||
#define EP_MAXPKT0 0x28
|
||||
#define EP_MAXPKT1 0x29
|
||||
#define ADDITIONAL_TRANSACTION_OPPORTUNITIES 3
|
||||
#define NONE_ADDITIONAL_TRANSACTION 0
|
||||
#define ONE_ADDITIONAL_TRANSACTION 1
|
||||
#define TWO_ADDITIONAL_TRANSACTION 2
|
||||
#define EP_CFG 0x2a
|
||||
#define ENDPOINT_NUMBER 0
|
||||
#define ENDPOINT_DIRECTION 4
|
||||
#define ENDPOINT_TYPE 5
|
||||
#define ENDPOINT_ENABLE 7
|
||||
#define EP_HBW 0x2b
|
||||
#define HIGH_BANDWIDTH_OUT_TRANSACTION_PID 0
|
||||
#define DATA0_PID 0
|
||||
#define DATA1_PID 1
|
||||
#define DATA2_PID 2
|
||||
#define MDATA_PID 3
|
||||
#define EP_BUFF_STATES 0x2c
|
||||
#define BUFFER_A_STATE 0
|
||||
#define BUFFER_B_STATE 2
|
||||
#define BUFF_FREE 0
|
||||
#define BUFF_VALID 1
|
||||
#define BUFF_LCL 2
|
||||
#define BUFF_USB 3
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
#define PCI_DEVICE_ID_RDK1 0x9054
|
||||
|
||||
/* PCI-RDK EPLD Registers */
|
||||
#define RDK_EPLD_IO_REGISTER1 0x00000000
|
||||
#define RDK_EPLD_USB_RESET 0
|
||||
#define RDK_EPLD_USB_POWERDOWN 1
|
||||
#define RDK_EPLD_USB_WAKEUP 2
|
||||
#define RDK_EPLD_USB_EOT 3
|
||||
#define RDK_EPLD_DPPULL 4
|
||||
#define RDK_EPLD_IO_REGISTER2 0x00000004
|
||||
#define RDK_EPLD_BUSWIDTH 0
|
||||
#define RDK_EPLD_USER 2
|
||||
#define RDK_EPLD_RESET_INTERRUPT_ENABLE 3
|
||||
#define RDK_EPLD_DMA_TIMEOUT_ENABLE 4
|
||||
#define RDK_EPLD_STATUS_REGISTER 0x00000008
|
||||
#define RDK_EPLD_USB_LRESET 0
|
||||
#define RDK_EPLD_REVISION_REGISTER 0x0000000c
|
||||
|
||||
/* PCI-RDK PLX 9054 Registers */
|
||||
#define INTCSR 0x68
|
||||
#define PCI_INTERRUPT_ENABLE 8
|
||||
#define LOCAL_INTERRUPT_INPUT_ENABLE 11
|
||||
#define LOCAL_INPUT_INTERRUPT_ACTIVE 15
|
||||
#define LOCAL_DMA_CHANNEL_0_INTERRUPT_ENABLE 18
|
||||
#define LOCAL_DMA_CHANNEL_1_INTERRUPT_ENABLE 19
|
||||
#define DMA_CHANNEL_0_INTERRUPT_ACTIVE 21
|
||||
#define DMA_CHANNEL_1_INTERRUPT_ACTIVE 22
|
||||
#define CNTRL 0x6C
|
||||
#define RELOAD_CONFIGURATION_REGISTERS 29
|
||||
#define PCI_ADAPTER_SOFTWARE_RESET 30
|
||||
#define DMAMODE0 0x80
|
||||
#define LOCAL_BUS_WIDTH 0
|
||||
#define INTERNAL_WAIT_STATES 2
|
||||
#define TA_READY_INPUT_ENABLE 6
|
||||
#define LOCAL_BURST_ENABLE 8
|
||||
#define SCATTER_GATHER_MODE 9
|
||||
#define DONE_INTERRUPT_ENABLE 10
|
||||
#define LOCAL_ADDRESSING_MODE 11
|
||||
#define DEMAND_MODE 12
|
||||
#define DMA_EOT_ENABLE 14
|
||||
#define FAST_SLOW_TERMINATE_MODE_SELECT 15
|
||||
#define DMA_CHANNEL_INTERRUPT_SELECT 17
|
||||
#define DMAPADR0 0x84
|
||||
#define DMALADR0 0x88
|
||||
#define DMASIZ0 0x8c
|
||||
#define DMADPR0 0x90
|
||||
#define DESCRIPTOR_LOCATION 0
|
||||
#define END_OF_CHAIN 1
|
||||
#define INTERRUPT_AFTER_TERMINAL_COUNT 2
|
||||
#define DIRECTION_OF_TRANSFER 3
|
||||
#define DMACSR0 0xa8
|
||||
#define CHANNEL_ENABLE 0
|
||||
#define CHANNEL_START 1
|
||||
#define CHANNEL_ABORT 2
|
||||
#define CHANNEL_CLEAR_INTERRUPT 3
|
||||
#define CHANNEL_DONE 4
|
||||
#define DMATHR 0xb0
|
||||
#define LBRD1 0xf8
|
||||
#define MEMORY_SPACE_LOCAL_BUS_WIDTH 0
|
||||
#define W8_BIT 0
|
||||
#define W16_BIT 1
|
||||
|
||||
/* Special OR'ing of INTCSR bits */
|
||||
#define LOCAL_INTERRUPT_TEST \
|
||||
((1 << LOCAL_INPUT_INTERRUPT_ACTIVE) | \
|
||||
(1 << LOCAL_INTERRUPT_INPUT_ENABLE))
|
||||
|
||||
#define DMA_CHANNEL_0_TEST \
|
||||
((1 << DMA_CHANNEL_0_INTERRUPT_ACTIVE) | \
|
||||
(1 << LOCAL_DMA_CHANNEL_0_INTERRUPT_ENABLE))
|
||||
|
||||
#define DMA_CHANNEL_1_TEST \
|
||||
((1 << DMA_CHANNEL_1_INTERRUPT_ACTIVE) | \
|
||||
(1 << LOCAL_DMA_CHANNEL_1_INTERRUPT_ENABLE))
|
||||
|
||||
/* EPLD Registers */
|
||||
#define RDK_EPLD_IO_REGISTER1 0x00000000
|
||||
#define RDK_EPLD_USB_RESET 0
|
||||
#define RDK_EPLD_USB_POWERDOWN 1
|
||||
#define RDK_EPLD_USB_WAKEUP 2
|
||||
#define RDK_EPLD_USB_EOT 3
|
||||
#define RDK_EPLD_DPPULL 4
|
||||
#define RDK_EPLD_IO_REGISTER2 0x00000004
|
||||
#define RDK_EPLD_BUSWIDTH 0
|
||||
#define RDK_EPLD_USER 2
|
||||
#define RDK_EPLD_RESET_INTERRUPT_ENABLE 3
|
||||
#define RDK_EPLD_DMA_TIMEOUT_ENABLE 4
|
||||
#define RDK_EPLD_STATUS_REGISTER 0x00000008
|
||||
#define RDK_EPLD_USB_LRESET 0
|
||||
#define RDK_EPLD_REVISION_REGISTER 0x0000000c
|
||||
|
||||
#define EPLD_IO_CONTROL_REGISTER 0x400
|
||||
#define NET2272_RESET 0
|
||||
#define BUSWIDTH 1
|
||||
#define MPX_MODE 3
|
||||
#define USER 4
|
||||
#define DMA_TIMEOUT_ENABLE 5
|
||||
#define DMA_CTL_DACK 6
|
||||
#define EPLD_DMA_ENABLE 7
|
||||
#define EPLD_DMA_CONTROL_REGISTER 0x800
|
||||
#define SPLIT_DMA_MODE 0
|
||||
#define SPLIT_DMA_DIRECTION 1
|
||||
#define SPLIT_DMA_ENABLE 2
|
||||
#define SPLIT_DMA_INTERRUPT_ENABLE 3
|
||||
#define SPLIT_DMA_INTERRUPT 4
|
||||
#define EPLD_DMA_MODE 5
|
||||
#define EPLD_DMA_CONTROLLER_ENABLE 7
|
||||
#define SPLIT_DMA_ADDRESS_LOW 0xc00
|
||||
#define SPLIT_DMA_ADDRESS_HIGH 0x1000
|
||||
#define SPLIT_DMA_BYTE_COUNT_LOW 0x1400
|
||||
#define SPLIT_DMA_BYTE_COUNT_HIGH 0x1800
|
||||
#define EPLD_REVISION_REGISTER 0x1c00
|
||||
#define SPLIT_DMA_RAM 0x4000
|
||||
#define DMA_RAM_SIZE 0x1000
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
#define PCI_DEVICE_ID_RDK2 0x3272
|
||||
|
||||
/* PCI-RDK version 2 registers */
|
||||
|
||||
/* Main Control Registers */
|
||||
|
||||
#define RDK2_IRQENB 0x00
|
||||
#define RDK2_IRQSTAT 0x04
|
||||
#define PB7 23
|
||||
#define PB6 22
|
||||
#define PB5 21
|
||||
#define PB4 20
|
||||
#define PB3 19
|
||||
#define PB2 18
|
||||
#define PB1 17
|
||||
#define PB0 16
|
||||
#define GP3 23
|
||||
#define GP2 23
|
||||
#define GP1 23
|
||||
#define GP0 23
|
||||
#define DMA_RETRY_ABORT 6
|
||||
#define DMA_PAUSE_DONE 5
|
||||
#define DMA_ABORT_DONE 4
|
||||
#define DMA_OUT_FIFO_TRANSFER_DONE 3
|
||||
#define DMA_LOCAL_DONE 2
|
||||
#define DMA_PCI_DONE 1
|
||||
#define NET2272_PCI_IRQ 0
|
||||
|
||||
#define RDK2_LOCCTLRDK 0x08
|
||||
#define CHIP_RESET 3
|
||||
#define SPLIT_DMA 2
|
||||
#define MULTIPLEX_MODE 1
|
||||
#define BUS_WIDTH 0
|
||||
|
||||
#define RDK2_GPIOCTL 0x10
|
||||
#define GP3_OUT_ENABLE 7
|
||||
#define GP2_OUT_ENABLE 6
|
||||
#define GP1_OUT_ENABLE 5
|
||||
#define GP0_OUT_ENABLE 4
|
||||
#define GP3_DATA 3
|
||||
#define GP2_DATA 2
|
||||
#define GP1_DATA 1
|
||||
#define GP0_DATA 0
|
||||
|
||||
#define RDK2_LEDSW 0x14
|
||||
#define LED3 27
|
||||
#define LED2 26
|
||||
#define LED1 25
|
||||
#define LED0 24
|
||||
#define PBUTTON 16
|
||||
#define DIPSW 0
|
||||
|
||||
#define RDK2_DIAG 0x18
|
||||
#define RDK2_FAST_TIMES 2
|
||||
#define FORCE_PCI_SERR 1
|
||||
#define FORCE_PCI_INT 0
|
||||
#define RDK2_FPGAREV 0x1C
|
||||
|
||||
/* Dma Control registers */
|
||||
#define RDK2_DMACTL 0x80
|
||||
#define ADDR_HOLD 24
|
||||
#define RETRY_COUNT 16 /* 23:16 */
|
||||
#define FIFO_THRESHOLD 11 /* 15:11 */
|
||||
#define MEM_WRITE_INVALIDATE 10
|
||||
#define READ_MULTIPLE 9
|
||||
#define READ_LINE 8
|
||||
#define RDK2_DMA_MODE 6 /* 7:6 */
|
||||
#define CONTROL_DACK 5
|
||||
#define EOT_ENABLE 4
|
||||
#define EOT_POLARITY 3
|
||||
#define DACK_POLARITY 2
|
||||
#define DREQ_POLARITY 1
|
||||
#define DMA_ENABLE 0
|
||||
|
||||
#define RDK2_DMASTAT 0x84
|
||||
#define GATHER_COUNT 12 /* 14:12 */
|
||||
#define FIFO_COUNT 6 /* 11:6 */
|
||||
#define FIFO_FLUSH 5
|
||||
#define FIFO_TRANSFER 4
|
||||
#define PAUSE_DONE 3
|
||||
#define ABORT_DONE 2
|
||||
#define DMA_ABORT 1
|
||||
#define DMA_START 0
|
||||
|
||||
#define RDK2_DMAPCICOUNT 0x88
|
||||
#define DMA_DIRECTION 31
|
||||
#define DMA_PCI_BYTE_COUNT 0 /* 0:23 */
|
||||
|
||||
#define RDK2_DMALOCCOUNT 0x8C /* 0:23 dma local byte count */
|
||||
|
||||
#define RDK2_DMAADDR 0x90 /* 2:31 PCI bus starting address */
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
#define REG_INDEXED_THRESHOLD (1 << 5)
|
||||
|
||||
/* DRIVER DATA STRUCTURES and UTILITIES */
|
||||
struct net2272_ep {
|
||||
struct usb_ep ep;
|
||||
struct net2272 *dev;
|
||||
unsigned long irqs;
|
||||
|
||||
/* analogous to a host-side qh */
|
||||
struct list_head queue;
|
||||
const struct usb_endpoint_descriptor *desc;
|
||||
unsigned num:8,
|
||||
fifo_size:12,
|
||||
stopped:1,
|
||||
wedged:1,
|
||||
is_in:1,
|
||||
is_iso:1,
|
||||
dma:1,
|
||||
not_empty:1;
|
||||
};
|
||||
|
||||
struct net2272 {
|
||||
/* each device provides one gadget, several endpoints */
|
||||
struct usb_gadget gadget;
|
||||
struct device *dev;
|
||||
unsigned short dev_id;
|
||||
|
||||
spinlock_t lock;
|
||||
struct net2272_ep ep[4];
|
||||
struct usb_gadget_driver *driver;
|
||||
unsigned protocol_stall:1,
|
||||
softconnect:1,
|
||||
wakeup:1,
|
||||
added:1,
|
||||
async_callbacks:1,
|
||||
dma_eot_polarity:1,
|
||||
dma_dack_polarity:1,
|
||||
dma_dreq_polarity:1,
|
||||
dma_busy:1;
|
||||
u16 chiprev;
|
||||
u8 pagesel;
|
||||
|
||||
unsigned int irq;
|
||||
unsigned short fifo_mode;
|
||||
|
||||
unsigned int base_shift;
|
||||
u16 __iomem *base_addr;
|
||||
union {
|
||||
#ifdef CONFIG_USB_PCI
|
||||
struct {
|
||||
void __iomem *plx9054_base_addr;
|
||||
void __iomem *epld_base_addr;
|
||||
} rdk1;
|
||||
struct {
|
||||
/* Bar0, Bar1 is base_addr both mem-mapped */
|
||||
void __iomem *fpga_base_addr;
|
||||
} rdk2;
|
||||
#endif
|
||||
};
|
||||
};
|
||||
|
||||
static void __iomem *
|
||||
net2272_reg_addr(struct net2272 *dev, unsigned int reg)
|
||||
{
|
||||
return dev->base_addr + (reg << dev->base_shift);
|
||||
}
|
||||
|
||||
static void
|
||||
net2272_write(struct net2272 *dev, unsigned int reg, u8 value)
|
||||
{
|
||||
if (reg >= REG_INDEXED_THRESHOLD) {
|
||||
/*
|
||||
* Indexed register; use REGADDRPTR/REGDATA
|
||||
* - Save and restore REGADDRPTR. This prevents REGADDRPTR from
|
||||
* changes between other code sections, but it is time consuming.
|
||||
* - Performance tips: either do not save and restore REGADDRPTR (if it
|
||||
* is safe) or do save/restore operations only in critical sections.
|
||||
u8 tmp = readb(dev->base_addr + REGADDRPTR);
|
||||
*/
|
||||
writeb((u8)reg, net2272_reg_addr(dev, REGADDRPTR));
|
||||
writeb(value, net2272_reg_addr(dev, REGDATA));
|
||||
/* writeb(tmp, net2272_reg_addr(dev, REGADDRPTR)); */
|
||||
} else
|
||||
writeb(value, net2272_reg_addr(dev, reg));
|
||||
}
|
||||
|
||||
static u8
|
||||
net2272_read(struct net2272 *dev, unsigned int reg)
|
||||
{
|
||||
u8 ret;
|
||||
|
||||
if (reg >= REG_INDEXED_THRESHOLD) {
|
||||
/*
|
||||
* Indexed register; use REGADDRPTR/REGDATA
|
||||
* - Save and restore REGADDRPTR. This prevents REGADDRPTR from
|
||||
* changes between other code sections, but it is time consuming.
|
||||
* - Performance tips: either do not save and restore REGADDRPTR (if it
|
||||
* is safe) or do save/restore operations only in critical sections.
|
||||
u8 tmp = readb(dev->base_addr + REGADDRPTR);
|
||||
*/
|
||||
writeb((u8)reg, net2272_reg_addr(dev, REGADDRPTR));
|
||||
ret = readb(net2272_reg_addr(dev, REGDATA));
|
||||
/* writeb(tmp, net2272_reg_addr(dev, REGADDRPTR)); */
|
||||
} else
|
||||
ret = readb(net2272_reg_addr(dev, reg));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
net2272_ep_write(struct net2272_ep *ep, unsigned int reg, u8 value)
|
||||
{
|
||||
struct net2272 *dev = ep->dev;
|
||||
|
||||
if (dev->pagesel != ep->num) {
|
||||
net2272_write(dev, PAGESEL, ep->num);
|
||||
dev->pagesel = ep->num;
|
||||
}
|
||||
net2272_write(dev, reg, value);
|
||||
}
|
||||
|
||||
static u8
|
||||
net2272_ep_read(struct net2272_ep *ep, unsigned int reg)
|
||||
{
|
||||
struct net2272 *dev = ep->dev;
|
||||
|
||||
if (dev->pagesel != ep->num) {
|
||||
net2272_write(dev, PAGESEL, ep->num);
|
||||
dev->pagesel = ep->num;
|
||||
}
|
||||
return net2272_read(dev, reg);
|
||||
}
|
||||
|
||||
static void allow_status(struct net2272_ep *ep)
|
||||
{
|
||||
/* ep0 only */
|
||||
net2272_ep_write(ep, EP_RSPCLR,
|
||||
(1 << CONTROL_STATUS_PHASE_HANDSHAKE) |
|
||||
(1 << ALT_NAK_OUT_PACKETS) |
|
||||
(1 << NAK_OUT_PACKETS_MODE));
|
||||
ep->stopped = 1;
|
||||
}
|
||||
|
||||
static void set_halt(struct net2272_ep *ep)
|
||||
{
|
||||
/* ep0 and bulk/intr endpoints */
|
||||
net2272_ep_write(ep, EP_RSPCLR, 1 << CONTROL_STATUS_PHASE_HANDSHAKE);
|
||||
net2272_ep_write(ep, EP_RSPSET, 1 << ENDPOINT_HALT);
|
||||
}
|
||||
|
||||
static void clear_halt(struct net2272_ep *ep)
|
||||
{
|
||||
/* ep0 and bulk/intr endpoints */
|
||||
net2272_ep_write(ep, EP_RSPCLR,
|
||||
(1 << ENDPOINT_HALT) | (1 << ENDPOINT_TOGGLE));
|
||||
}
|
||||
|
||||
/* count (<= 4) bytes in the next fifo write will be valid */
|
||||
static void set_fifo_bytecount(struct net2272_ep *ep, unsigned count)
|
||||
{
|
||||
/* net2272_ep_write will truncate to u8 for us */
|
||||
net2272_ep_write(ep, EP_TRANSFER2, count >> 16);
|
||||
net2272_ep_write(ep, EP_TRANSFER1, count >> 8);
|
||||
net2272_ep_write(ep, EP_TRANSFER0, count);
|
||||
}
|
||||
|
||||
struct net2272_request {
|
||||
struct usb_request req;
|
||||
struct list_head queue;
|
||||
unsigned mapped:1,
|
||||
valid:1;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -2397,8 +2397,7 @@ static int renesas_usb3_stop(struct usb_gadget *gadget)
|
|||
rzv2m_usb3drd_reset(usb3_to_dev(usb3)->parent, false);
|
||||
|
||||
renesas_usb3_stop_controller(usb3);
|
||||
if (usb3->phy)
|
||||
phy_exit(usb3->phy);
|
||||
phy_exit(usb3->phy);
|
||||
|
||||
pm_runtime_put(usb3_to_dev(usb3));
|
||||
|
||||
|
@ -2984,8 +2983,7 @@ static int renesas_usb3_suspend(struct device *dev)
|
|||
return 0;
|
||||
|
||||
renesas_usb3_stop_controller(usb3);
|
||||
if (usb3->phy)
|
||||
phy_exit(usb3->phy);
|
||||
phy_exit(usb3->phy);
|
||||
pm_runtime_put(dev);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -2178,8 +2178,6 @@ fail:
|
|||
/**
|
||||
* xudc_remove - Releases the resources allocated during the initialization.
|
||||
* @pdev: pointer to the platform device structure.
|
||||
*
|
||||
* Return: 0 always
|
||||
*/
|
||||
static void xudc_remove(struct platform_device *pdev)
|
||||
{
|
||||
|
|
|
@ -104,6 +104,15 @@ config USB_XHCI_RZV2M
|
|||
Say 'Y' to enable the support for the xHCI host controller
|
||||
found in Renesas RZ/V2M SoC.
|
||||
|
||||
config USB_XHCI_SIDEBAND
|
||||
bool "xHCI support for sideband"
|
||||
help
|
||||
Say 'Y' to enable the support for the xHCI sideband capability.
|
||||
Provide a mechanism for a sideband datapath for payload associated
|
||||
with audio class endpoints. This allows for an audio DSP to use
|
||||
xHCI USB endpoints directly, allowing CPU to sleep while playing
|
||||
audio.
|
||||
|
||||
config USB_XHCI_TEGRA
|
||||
tristate "xHCI support for NVIDIA Tegra SoCs"
|
||||
depends on PHY_TEGRA_XUSB
|
||||
|
@ -225,7 +234,7 @@ config USB_EHCI_HCD_OMAP
|
|||
tristate "EHCI support for OMAP3 and later chips"
|
||||
depends on ARCH_OMAP || COMPILE_TEST
|
||||
depends on NOP_USB_XCEIV
|
||||
default y
|
||||
default ARCH_OMAP
|
||||
help
|
||||
Enables support for the on-chip EHCI controller on
|
||||
OMAP3 and later chips.
|
||||
|
|
|
@ -32,6 +32,10 @@ endif
|
|||
xhci-rcar-hcd-y += xhci-rcar.o
|
||||
xhci-rcar-hcd-$(CONFIG_USB_XHCI_RZV2M) += xhci-rzv2m.o
|
||||
|
||||
ifneq ($(CONFIG_USB_XHCI_SIDEBAND),)
|
||||
xhci-hcd-y += xhci-sideband.o
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_USB_PCI) += pci-quirks.o
|
||||
|
||||
obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o
|
||||
|
|
|
@ -410,15 +410,13 @@ static int ehci_fsl_setup(struct usb_hcd *hcd)
|
|||
return retval;
|
||||
}
|
||||
|
||||
struct ehci_fsl {
|
||||
struct ehci_hcd ehci;
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
struct ehci_fsl_priv {
|
||||
/* Saved USB PHY settings, need to restore after deep sleep. */
|
||||
u32 usb_ctrl;
|
||||
#endif
|
||||
};
|
||||
|
||||
#define hcd_to_ehci_fsl_priv(h) ((struct ehci_fsl_priv *) hcd_to_ehci(h)->priv)
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
#ifdef CONFIG_PPC_MPC512x
|
||||
|
@ -566,17 +564,10 @@ static inline int ehci_fsl_mpc512x_drv_resume(struct device *dev)
|
|||
}
|
||||
#endif /* CONFIG_PPC_MPC512x */
|
||||
|
||||
static struct ehci_fsl *hcd_to_ehci_fsl(struct usb_hcd *hcd)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
|
||||
return container_of(ehci, struct ehci_fsl, ehci);
|
||||
}
|
||||
|
||||
static int ehci_fsl_drv_suspend(struct device *dev)
|
||||
{
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
struct ehci_fsl *ehci_fsl = hcd_to_ehci_fsl(hcd);
|
||||
struct ehci_fsl_priv *priv = hcd_to_ehci_fsl_priv(hcd);
|
||||
void __iomem *non_ehci = hcd->regs;
|
||||
|
||||
if (of_device_is_compatible(dev->parent->of_node,
|
||||
|
@ -589,14 +580,14 @@ static int ehci_fsl_drv_suspend(struct device *dev)
|
|||
if (!fsl_deep_sleep())
|
||||
return 0;
|
||||
|
||||
ehci_fsl->usb_ctrl = ioread32be(non_ehci + FSL_SOC_USB_CTRL);
|
||||
priv->usb_ctrl = ioread32be(non_ehci + FSL_SOC_USB_CTRL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ehci_fsl_drv_resume(struct device *dev)
|
||||
{
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
struct ehci_fsl *ehci_fsl = hcd_to_ehci_fsl(hcd);
|
||||
struct ehci_fsl_priv *priv = hcd_to_ehci_fsl_priv(hcd);
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
void __iomem *non_ehci = hcd->regs;
|
||||
|
||||
|
@ -612,7 +603,7 @@ static int ehci_fsl_drv_resume(struct device *dev)
|
|||
usb_root_hub_lost_power(hcd->self.root_hub);
|
||||
|
||||
/* Restore USB PHY settings and enable the controller. */
|
||||
iowrite32be(ehci_fsl->usb_ctrl, non_ehci + FSL_SOC_USB_CTRL);
|
||||
iowrite32be(priv->usb_ctrl, non_ehci + FSL_SOC_USB_CTRL);
|
||||
|
||||
ehci_reset(ehci);
|
||||
ehci_fsl_reinit(ehci);
|
||||
|
@ -671,7 +662,7 @@ static int ehci_start_port_reset(struct usb_hcd *hcd, unsigned port)
|
|||
#endif /* CONFIG_USB_OTG */
|
||||
|
||||
static const struct ehci_driver_overrides ehci_fsl_overrides __initconst = {
|
||||
.extra_priv_size = sizeof(struct ehci_fsl),
|
||||
.extra_priv_size = sizeof(struct ehci_fsl_priv),
|
||||
.reset = ehci_fsl_setup,
|
||||
};
|
||||
|
||||
|
|
|
@ -62,8 +62,8 @@
|
|||
|
||||
#define CTX_SIZE(_hcc) (HCC_64BYTE_CONTEXT(_hcc) ? 64 : 32)
|
||||
|
||||
/* db_off bitmask - bits 0:1 reserved */
|
||||
#define DBOFF_MASK (~0x3)
|
||||
/* db_off bitmask - bits 31:2 Doorbell Array Offset */
|
||||
#define DBOFF_MASK (0xfffffffc)
|
||||
|
||||
/* run_regs_off bitmask - bits 0:4 reserved */
|
||||
#define RTSOFF_MASK (~0x1f)
|
||||
|
|
|
@ -631,6 +631,112 @@ static void xhci_debugfs_create_ports(struct xhci_hcd *xhci,
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
struct usb_hcd *hcd = xhci_to_hcd(xhci);
|
||||
struct device *dev = hcd->self.controller;
|
||||
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
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);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* get roothub port bandwidth */
|
||||
ret = xhci_get_port_bandwidth(xhci, ctx, dev_speed);
|
||||
if (ret)
|
||||
goto err_out;
|
||||
|
||||
/* 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++)
|
||||
seq_printf(s, "port[%d] available bw: %d%%.\n", i,
|
||||
ctx->bytes[i]);
|
||||
err_out:
|
||||
pm_runtime_put_sync(dev);
|
||||
xhci_free_port_bw_ctx(xhci, ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int xhci_ss_bw_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
int ret;
|
||||
struct xhci_hcd *xhci = (struct xhci_hcd *)s->private;
|
||||
|
||||
ret = xhci_port_bw_show(xhci, USB_SPEED_SUPER, s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int xhci_hs_bw_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
int ret;
|
||||
struct xhci_hcd *xhci = (struct xhci_hcd *)s->private;
|
||||
|
||||
ret = xhci_port_bw_show(xhci, USB_SPEED_HIGH, s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int xhci_fs_bw_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
int ret;
|
||||
struct xhci_hcd *xhci = (struct xhci_hcd *)s->private;
|
||||
|
||||
ret = xhci_port_bw_show(xhci, USB_SPEED_FULL, s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct xhci_file_map bw_context_files[] = {
|
||||
{"SS_BW", xhci_ss_bw_show, },
|
||||
{"HS_BW", xhci_hs_bw_show, },
|
||||
{"FS_BW", xhci_fs_bw_show, },
|
||||
};
|
||||
|
||||
static int bw_context_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
int i;
|
||||
struct xhci_file_map *f_map;
|
||||
const char *file_name = file_dentry(file)->d_iname;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(bw_context_files); i++) {
|
||||
f_map = &bw_context_files[i];
|
||||
|
||||
if (strcmp(f_map->name, file_name) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return single_open(file, f_map->show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations bw_fops = {
|
||||
.open = bw_context_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static void xhci_debugfs_create_bandwidth(struct xhci_hcd *xhci,
|
||||
struct dentry *parent)
|
||||
{
|
||||
parent = debugfs_create_dir("port_bandwidth", parent);
|
||||
|
||||
xhci_debugfs_create_files(xhci, bw_context_files,
|
||||
ARRAY_SIZE(bw_context_files),
|
||||
xhci,
|
||||
parent, &bw_fops);
|
||||
}
|
||||
|
||||
void xhci_debugfs_init(struct xhci_hcd *xhci)
|
||||
{
|
||||
struct device *dev = xhci_to_hcd(xhci)->self.controller;
|
||||
|
@ -681,6 +787,8 @@ void xhci_debugfs_init(struct xhci_hcd *xhci)
|
|||
xhci->debugfs_slots = debugfs_create_dir("devices", xhci->debugfs_root);
|
||||
|
||||
xhci_debugfs_create_ports(xhci, xhci->debugfs_root);
|
||||
|
||||
xhci_debugfs_create_bandwidth(xhci, xhci->debugfs_root);
|
||||
}
|
||||
|
||||
void xhci_debugfs_exit(struct xhci_hcd *xhci)
|
||||
|
|
|
@ -1907,7 +1907,7 @@ int xhci_bus_resume(struct usb_hcd *hcd)
|
|||
* prevent port event interrupts from interfering
|
||||
* with usb2 port resume process
|
||||
*/
|
||||
xhci_disable_interrupter(xhci->interrupters[0]);
|
||||
xhci_disable_interrupter(xhci, xhci->interrupters[0]);
|
||||
disabled_irq = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -484,6 +484,35 @@ void xhci_free_container_ctx(struct xhci_hcd *xhci,
|
|||
kfree(ctx);
|
||||
}
|
||||
|
||||
struct xhci_container_ctx *xhci_alloc_port_bw_ctx(struct xhci_hcd *xhci,
|
||||
gfp_t flags)
|
||||
{
|
||||
struct xhci_container_ctx *ctx;
|
||||
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
|
||||
|
||||
ctx = kzalloc_node(sizeof(*ctx), flags, dev_to_node(dev));
|
||||
if (!ctx)
|
||||
return NULL;
|
||||
|
||||
ctx->size = GET_PORT_BW_ARRAY_SIZE;
|
||||
|
||||
ctx->bytes = dma_pool_zalloc(xhci->port_bw_pool, flags, &ctx->dma);
|
||||
if (!ctx->bytes) {
|
||||
kfree(ctx);
|
||||
return NULL;
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void xhci_free_port_bw_ctx(struct xhci_hcd *xhci,
|
||||
struct xhci_container_ctx *ctx)
|
||||
{
|
||||
if (!ctx)
|
||||
return;
|
||||
dma_pool_free(xhci->port_bw_pool, ctx->bytes, ctx->dma);
|
||||
kfree(ctx);
|
||||
}
|
||||
|
||||
struct xhci_input_control_ctx *xhci_get_input_control_ctx(
|
||||
struct xhci_container_ctx *ctx)
|
||||
{
|
||||
|
@ -1802,10 +1831,10 @@ xhci_remove_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir)
|
|||
*/
|
||||
if (ir->ir_set) {
|
||||
tmp = readl(&ir->ir_set->erst_size);
|
||||
tmp &= ERST_SIZE_MASK;
|
||||
tmp &= ~ERST_SIZE_MASK;
|
||||
writel(tmp, &ir->ir_set->erst_size);
|
||||
|
||||
xhci_write_64(xhci, ERST_EHB, &ir->ir_set->erst_dequeue);
|
||||
xhci_update_erst_dequeue(xhci, ir, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1848,6 +1877,11 @@ void xhci_remove_secondary_interrupter(struct usb_hcd *hcd, struct xhci_interrup
|
|||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cleanup secondary interrupter to ensure there are no pending events.
|
||||
* This also updates event ring dequeue pointer back to the start.
|
||||
*/
|
||||
xhci_skip_sec_intr_events(xhci, ir->event_ring, ir);
|
||||
intr_num = ir->intr_num;
|
||||
|
||||
xhci_remove_interrupter(xhci, ir);
|
||||
|
@ -1907,6 +1941,11 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
|
|||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"Freed small stream array pool");
|
||||
|
||||
dma_pool_destroy(xhci->port_bw_pool);
|
||||
xhci->port_bw_pool = NULL;
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"Freed xhci port bw array pool");
|
||||
|
||||
dma_pool_destroy(xhci->medium_streams_pool);
|
||||
xhci->medium_streams_pool = NULL;
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
|
@ -2282,37 +2321,25 @@ xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int segs, gfp_t flags)
|
|||
return ir;
|
||||
}
|
||||
|
||||
static int
|
||||
xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir,
|
||||
unsigned int intr_num)
|
||||
void xhci_add_interrupter(struct xhci_hcd *xhci, unsigned int intr_num)
|
||||
{
|
||||
struct xhci_interrupter *ir;
|
||||
u64 erst_base;
|
||||
u32 erst_size;
|
||||
|
||||
if (intr_num >= xhci->max_interrupters) {
|
||||
xhci_warn(xhci, "Can't add interrupter %d, max interrupters %d\n",
|
||||
intr_num, xhci->max_interrupters);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (xhci->interrupters[intr_num]) {
|
||||
xhci_warn(xhci, "Interrupter %d\n already set up", intr_num);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
xhci->interrupters[intr_num] = ir;
|
||||
ir = xhci->interrupters[intr_num];
|
||||
ir->intr_num = intr_num;
|
||||
ir->ir_set = &xhci->run_regs->ir_set[intr_num];
|
||||
|
||||
/* set ERST count with the number of entries in the segment table */
|
||||
erst_size = readl(&ir->ir_set->erst_size);
|
||||
erst_size &= ERST_SIZE_MASK;
|
||||
erst_size &= ~ERST_SIZE_MASK;
|
||||
erst_size |= ir->event_ring->num_segs;
|
||||
writel(erst_size, &ir->ir_set->erst_size);
|
||||
|
||||
erst_base = xhci_read_64(xhci, &ir->ir_set->erst_base);
|
||||
erst_base &= ERST_BASE_RSVDP;
|
||||
erst_base |= ir->erst.erst_dma_addr & ~ERST_BASE_RSVDP;
|
||||
erst_base &= ~ERST_BASE_ADDRESS_MASK;
|
||||
erst_base |= ir->erst.erst_dma_addr & ERST_BASE_ADDRESS_MASK;
|
||||
if (xhci->quirks & XHCI_WRITE_64_HI_LO)
|
||||
hi_lo_writeq(erst_base, &ir->ir_set->erst_base);
|
||||
else
|
||||
|
@ -2320,20 +2347,19 @@ xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir,
|
|||
|
||||
/* Set the event ring dequeue address of this interrupter */
|
||||
xhci_set_hc_event_deq(xhci, ir);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct xhci_interrupter *
|
||||
xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs,
|
||||
u32 imod_interval)
|
||||
u32 imod_interval, unsigned int intr_num)
|
||||
{
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
struct xhci_interrupter *ir;
|
||||
unsigned int i;
|
||||
int err = -ENOSPC;
|
||||
|
||||
if (!xhci->interrupters || xhci->max_interrupters <= 1)
|
||||
if (!xhci->interrupters || xhci->max_interrupters <= 1 ||
|
||||
intr_num >= xhci->max_interrupters)
|
||||
return NULL;
|
||||
|
||||
ir = xhci_alloc_interrupter(xhci, segs, GFP_KERNEL);
|
||||
|
@ -2341,15 +2367,23 @@ xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs,
|
|||
return NULL;
|
||||
|
||||
spin_lock_irq(&xhci->lock);
|
||||
|
||||
/* Find available secondary interrupter, interrupter 0 is reserved for primary */
|
||||
for (i = 1; i < xhci->max_interrupters; i++) {
|
||||
if (xhci->interrupters[i] == NULL) {
|
||||
err = xhci_add_interrupter(xhci, ir, i);
|
||||
break;
|
||||
if (!intr_num) {
|
||||
/* Find available secondary interrupter, interrupter 0 is reserved for primary */
|
||||
for (i = 1; i < xhci->max_interrupters; i++) {
|
||||
if (!xhci->interrupters[i]) {
|
||||
xhci->interrupters[i] = ir;
|
||||
xhci_add_interrupter(xhci, i);
|
||||
err = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!xhci->interrupters[intr_num]) {
|
||||
xhci->interrupters[intr_num] = ir;
|
||||
xhci_add_interrupter(xhci, intr_num);
|
||||
err = 0;
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irq(&xhci->lock);
|
||||
|
||||
if (err) {
|
||||
|
@ -2359,78 +2393,32 @@ xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
err = xhci_set_interrupter_moderation(ir, imod_interval);
|
||||
if (err)
|
||||
xhci_warn(xhci, "Failed to set interrupter %d moderation to %uns\n",
|
||||
i, imod_interval);
|
||||
xhci_set_interrupter_moderation(ir, imod_interval);
|
||||
|
||||
xhci_dbg(xhci, "Add secondary interrupter %d, max interrupters %d\n",
|
||||
i, xhci->max_interrupters);
|
||||
ir->intr_num, xhci->max_interrupters);
|
||||
|
||||
return ir;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xhci_create_secondary_interrupter);
|
||||
|
||||
static void xhci_hcd_page_size(struct xhci_hcd *xhci)
|
||||
{
|
||||
u32 page_size;
|
||||
|
||||
page_size = readl(&xhci->op_regs->page_size) & XHCI_PAGE_SIZE_MASK;
|
||||
if (!is_power_of_2(page_size)) {
|
||||
xhci_warn(xhci, "Invalid page size register = 0x%x\n", page_size);
|
||||
/* Fallback to 4K page size, since that's common */
|
||||
page_size = 1;
|
||||
}
|
||||
|
||||
xhci->page_size = page_size << 12;
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "HCD page size set to %iK",
|
||||
xhci->page_size >> 10);
|
||||
}
|
||||
|
||||
int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
|
||||
{
|
||||
struct xhci_interrupter *ir;
|
||||
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
|
||||
dma_addr_t dma;
|
||||
unsigned int val, val2;
|
||||
u64 val_64;
|
||||
u32 temp;
|
||||
int i;
|
||||
|
||||
INIT_LIST_HEAD(&xhci->cmd_list);
|
||||
|
||||
/* init command timeout work */
|
||||
INIT_DELAYED_WORK(&xhci->cmd_timer, xhci_handle_command_timeout);
|
||||
init_completion(&xhci->cmd_ring_stop_completion);
|
||||
|
||||
xhci_hcd_page_size(xhci);
|
||||
|
||||
/*
|
||||
* Program the Number of Device Slots Enabled field in the CONFIG
|
||||
* register with the max value of slots the HC can handle.
|
||||
*/
|
||||
val = HCS_MAX_SLOTS(readl(&xhci->cap_regs->hcs_params1));
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"// xHC can handle at most %d device slots.", val);
|
||||
val2 = readl(&xhci->op_regs->config_reg);
|
||||
val |= (val2 & ~HCS_SLOTS_MASK);
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"// Setting Max device slots reg = 0x%x.", val);
|
||||
writel(val, &xhci->op_regs->config_reg);
|
||||
|
||||
/*
|
||||
* xHCI section 5.4.6 - Device Context array must be
|
||||
* "physically contiguous and 64-byte (cache line) aligned".
|
||||
*/
|
||||
xhci->dcbaa = dma_alloc_coherent(dev, sizeof(*xhci->dcbaa), &dma,
|
||||
flags);
|
||||
xhci->dcbaa = dma_alloc_coherent(dev, sizeof(*xhci->dcbaa), &dma, flags);
|
||||
if (!xhci->dcbaa)
|
||||
goto fail;
|
||||
|
||||
xhci->dcbaa->dma = dma;
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"// Device context base array address = 0x%pad (DMA), %p (virt)",
|
||||
&xhci->dcbaa->dma, xhci->dcbaa);
|
||||
xhci_write_64(xhci, dma, &xhci->op_regs->dcbaa_ptr);
|
||||
"Device context base array address = 0x%pad (DMA), %p (virt)",
|
||||
&xhci->dcbaa->dma, xhci->dcbaa);
|
||||
|
||||
/*
|
||||
* Initialize the ring segment pool. The ring must be a contiguous
|
||||
|
@ -2446,92 +2434,77 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
|
|||
else
|
||||
xhci->segment_pool = dma_pool_create("xHCI ring segments", dev,
|
||||
TRB_SEGMENT_SIZE, TRB_SEGMENT_SIZE, xhci->page_size);
|
||||
|
||||
/* See Table 46 and Note on Figure 55 */
|
||||
xhci->device_pool = dma_pool_create("xHCI input/output contexts", dev,
|
||||
2112, 64, xhci->page_size);
|
||||
if (!xhci->segment_pool || !xhci->device_pool)
|
||||
if (!xhci->segment_pool)
|
||||
goto fail;
|
||||
|
||||
/* Linear stream context arrays don't have any boundary restrictions,
|
||||
/* See Table 46 and Note on Figure 55 */
|
||||
xhci->device_pool = dma_pool_create("xHCI input/output contexts", dev, 2112, 64,
|
||||
xhci->page_size);
|
||||
if (!xhci->device_pool)
|
||||
goto fail;
|
||||
|
||||
/*
|
||||
* Linear stream context arrays don't have any boundary restrictions,
|
||||
* and only need to be 16-byte aligned.
|
||||
*/
|
||||
xhci->small_streams_pool =
|
||||
dma_pool_create("xHCI 256 byte stream ctx arrays",
|
||||
dev, SMALL_STREAM_ARRAY_SIZE, 16, 0);
|
||||
xhci->medium_streams_pool =
|
||||
dma_pool_create("xHCI 1KB stream ctx arrays",
|
||||
dev, MEDIUM_STREAM_ARRAY_SIZE, 16, 0);
|
||||
/* Any stream context array bigger than MEDIUM_STREAM_ARRAY_SIZE
|
||||
* will be allocated with dma_alloc_coherent()
|
||||
xhci->small_streams_pool = dma_pool_create("xHCI 256 byte stream ctx arrays",
|
||||
dev, SMALL_STREAM_ARRAY_SIZE, 16, 0);
|
||||
if (!xhci->small_streams_pool)
|
||||
goto fail;
|
||||
|
||||
/*
|
||||
* Any stream context array bigger than MEDIUM_STREAM_ARRAY_SIZE will be
|
||||
* allocated with dma_alloc_coherent().
|
||||
*/
|
||||
|
||||
if (!xhci->small_streams_pool || !xhci->medium_streams_pool)
|
||||
xhci->medium_streams_pool = dma_pool_create("xHCI 1KB stream ctx arrays",
|
||||
dev, MEDIUM_STREAM_ARRAY_SIZE, 16, 0);
|
||||
if (!xhci->medium_streams_pool)
|
||||
goto fail;
|
||||
|
||||
/*
|
||||
* refer to xhci rev1_2 protocol 5.3.3 max ports is 255.
|
||||
* refer to xhci rev1_2 protocol 6.4.3.14 port bandwidth buffer need
|
||||
* to be 16-byte aligned.
|
||||
*/
|
||||
xhci->port_bw_pool = dma_pool_create("xHCI 256 port bw ctx arrays",
|
||||
dev, GET_PORT_BW_ARRAY_SIZE, 16, 0);
|
||||
if (!xhci->port_bw_pool)
|
||||
goto fail;
|
||||
|
||||
/* Set up the command ring to have one segments for now. */
|
||||
xhci->cmd_ring = xhci_ring_alloc(xhci, 1, TYPE_COMMAND, 0, flags);
|
||||
if (!xhci->cmd_ring)
|
||||
goto fail;
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"Allocated command ring at %p", xhci->cmd_ring);
|
||||
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Allocated command ring at %p", xhci->cmd_ring);
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "First segment DMA is 0x%pad",
|
||||
&xhci->cmd_ring->first_seg->dma);
|
||||
&xhci->cmd_ring->first_seg->dma);
|
||||
|
||||
/* Set the address in the Command Ring Control register */
|
||||
val_64 = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
|
||||
val_64 = (val_64 & (u64) CMD_RING_RSVD_BITS) |
|
||||
(xhci->cmd_ring->first_seg->dma & (u64) ~CMD_RING_RSVD_BITS) |
|
||||
xhci->cmd_ring->cycle_state;
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"// Setting command ring address to 0x%016llx", val_64);
|
||||
xhci_write_64(xhci, val_64, &xhci->op_regs->cmd_ring);
|
||||
|
||||
/* Reserve one command ring TRB for disabling LPM.
|
||||
/*
|
||||
* Reserve one command ring TRB for disabling LPM.
|
||||
* Since the USB core grabs the shared usb_bus bandwidth mutex before
|
||||
* disabling LPM, we only need to reserve one TRB for all devices.
|
||||
*/
|
||||
xhci->cmd_ring_reserved_trbs++;
|
||||
|
||||
val = readl(&xhci->cap_regs->db_off);
|
||||
val &= DBOFF_MASK;
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"// Doorbell array is located at offset 0x%x from cap regs base addr",
|
||||
val);
|
||||
xhci->dba = (void __iomem *) xhci->cap_regs + val;
|
||||
|
||||
/* Allocate and set up primary interrupter 0 with an event ring. */
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"Allocating primary event ring");
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Allocating primary event ring");
|
||||
xhci->interrupters = kcalloc_node(xhci->max_interrupters, sizeof(*xhci->interrupters),
|
||||
flags, dev_to_node(dev));
|
||||
|
||||
ir = xhci_alloc_interrupter(xhci, 0, flags);
|
||||
if (!ir)
|
||||
if (!xhci->interrupters)
|
||||
goto fail;
|
||||
|
||||
if (xhci_add_interrupter(xhci, ir, 0))
|
||||
xhci->interrupters[0] = xhci_alloc_interrupter(xhci, 0, flags);
|
||||
if (!xhci->interrupters[0])
|
||||
goto fail;
|
||||
|
||||
ir->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX;
|
||||
|
||||
for (i = 0; i < MAX_HC_SLOTS; i++)
|
||||
xhci->devs[i] = NULL;
|
||||
|
||||
if (scratchpad_alloc(xhci, flags))
|
||||
goto fail;
|
||||
|
||||
if (xhci_setup_port_arrays(xhci, flags))
|
||||
goto fail;
|
||||
|
||||
/* Enable USB 3.0 device notifications for function remote wake, which
|
||||
* is necessary for allowing USB 3.0 devices to do remote wakeup from
|
||||
* U3 (device suspend).
|
||||
*/
|
||||
temp = readl(&xhci->op_regs->dev_notification);
|
||||
temp &= ~DEV_NOTE_MASK;
|
||||
temp |= DEV_NOTE_FWAKE;
|
||||
writel(temp, &xhci->op_regs->dev_notification);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
|
|
|
@ -267,6 +267,8 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s
|
|||
|
||||
device_property_read_u32(tmpdev, "imod-interval-ns",
|
||||
&xhci->imod_interval);
|
||||
device_property_read_u16(tmpdev, "num-hc-interrupters",
|
||||
&xhci->max_interrupters);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -1898,6 +1898,8 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
|
|||
case TRB_NEC_GET_FW:
|
||||
xhci_handle_cmd_nec_get_fw(xhci, event);
|
||||
break;
|
||||
case TRB_GET_BW:
|
||||
break;
|
||||
default:
|
||||
/* Skip over unknown commands on the event ring */
|
||||
xhci_info(xhci, "INFO unknown command type %d\n", cmd_type);
|
||||
|
@ -2977,9 +2979,6 @@ debug_finding_td:
|
|||
(unsigned long long)xhci_trb_virt_to_dma(td->start_seg, td->start_trb),
|
||||
(unsigned long long)xhci_trb_virt_to_dma(td->end_seg, td->end_trb));
|
||||
|
||||
xhci_for_each_ring_seg(ep_ring->first_seg, ep_seg)
|
||||
xhci_warn(xhci, "Ring seg %u dma %pad\n", ep_seg->num, &ep_seg->dma);
|
||||
|
||||
return -ESHUTDOWN;
|
||||
|
||||
err_out:
|
||||
|
@ -3050,9 +3049,9 @@ static int xhci_handle_event_trb(struct xhci_hcd *xhci, struct xhci_interrupter
|
|||
* - When all events have finished
|
||||
* - To avoid "Event Ring Full Error" condition
|
||||
*/
|
||||
static void xhci_update_erst_dequeue(struct xhci_hcd *xhci,
|
||||
struct xhci_interrupter *ir,
|
||||
bool clear_ehb)
|
||||
void xhci_update_erst_dequeue(struct xhci_hcd *xhci,
|
||||
struct xhci_interrupter *ir,
|
||||
bool clear_ehb)
|
||||
{
|
||||
u64 temp_64;
|
||||
dma_addr_t deq;
|
||||
|
@ -3083,11 +3082,14 @@ static void xhci_update_erst_dequeue(struct xhci_hcd *xhci,
|
|||
static void xhci_clear_interrupt_pending(struct xhci_interrupter *ir)
|
||||
{
|
||||
if (!ir->ip_autoclear) {
|
||||
u32 irq_pending;
|
||||
u32 iman;
|
||||
|
||||
irq_pending = readl(&ir->ir_set->irq_pending);
|
||||
irq_pending |= IMAN_IP;
|
||||
writel(irq_pending, &ir->ir_set->irq_pending);
|
||||
iman = readl(&ir->ir_set->iman);
|
||||
iman |= IMAN_IP;
|
||||
writel(iman, &ir->ir_set->iman);
|
||||
|
||||
/* Read operation to guarantee the write has been flushed from posted buffers */
|
||||
readl(&ir->ir_set->iman);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3095,10 +3097,11 @@ static void xhci_clear_interrupt_pending(struct xhci_interrupter *ir)
|
|||
* Handle all OS-owned events on an interrupter event ring. It may drop
|
||||
* and reaquire xhci->lock between event processing.
|
||||
*/
|
||||
static int xhci_handle_events(struct xhci_hcd *xhci, struct xhci_interrupter *ir)
|
||||
static int xhci_handle_events(struct xhci_hcd *xhci, struct xhci_interrupter *ir,
|
||||
bool skip_events)
|
||||
{
|
||||
int event_loop = 0;
|
||||
int err;
|
||||
int err = 0;
|
||||
u64 temp;
|
||||
|
||||
xhci_clear_interrupt_pending(ir);
|
||||
|
@ -3121,7 +3124,8 @@ static int xhci_handle_events(struct xhci_hcd *xhci, struct xhci_interrupter *ir
|
|||
|
||||
/* Process all OS owned event TRBs on this event ring */
|
||||
while (unhandled_event_trb(ir->event_ring)) {
|
||||
err = xhci_handle_event_trb(xhci, ir, ir->event_ring->dequeue);
|
||||
if (!skip_events)
|
||||
err = xhci_handle_event_trb(xhci, ir, ir->event_ring->dequeue);
|
||||
|
||||
/*
|
||||
* If half a segment of events have been handled in one go then
|
||||
|
@ -3148,6 +3152,37 @@ static int xhci_handle_events(struct xhci_hcd *xhci, struct xhci_interrupter *ir
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Move the event ring dequeue pointer to skip events kept in the secondary
|
||||
* event ring. This is used to ensure that pending events in the ring are
|
||||
* acknowledged, so the xHCI HCD can properly enter suspend/resume. The
|
||||
* secondary ring is typically maintained by an external component.
|
||||
*/
|
||||
void xhci_skip_sec_intr_events(struct xhci_hcd *xhci,
|
||||
struct xhci_ring *ring, struct xhci_interrupter *ir)
|
||||
{
|
||||
union xhci_trb *current_trb;
|
||||
u64 erdp_reg;
|
||||
dma_addr_t deq;
|
||||
|
||||
/* disable irq, ack pending interrupt and ack all pending events */
|
||||
xhci_disable_interrupter(xhci, ir);
|
||||
|
||||
/* last acked event trb is in erdp reg */
|
||||
erdp_reg = xhci_read_64(xhci, &ir->ir_set->erst_dequeue);
|
||||
deq = (dma_addr_t)(erdp_reg & ERST_PTR_MASK);
|
||||
if (!deq) {
|
||||
xhci_err(xhci, "event ring handling not required\n");
|
||||
return;
|
||||
}
|
||||
|
||||
current_trb = ir->event_ring->dequeue;
|
||||
/* read cycle state of the last acked trb to find out CCS */
|
||||
ring->cycle_state = le32_to_cpu(current_trb->event_cmd.flags) & TRB_CYCLE;
|
||||
|
||||
xhci_handle_events(xhci, ir, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* xHCI spec says we can get an interrupt, and if the HC has an error condition,
|
||||
* we might get bad data out of the event ring. Section 4.10.2.7 has a list of
|
||||
|
@ -3192,7 +3227,7 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd)
|
|||
writel(status, &xhci->op_regs->status);
|
||||
|
||||
/* This is the handler of the primary interrupter */
|
||||
xhci_handle_events(xhci, xhci->interrupters[0]);
|
||||
xhci_handle_events(xhci, xhci->interrupters[0], false);
|
||||
out:
|
||||
spin_unlock(&xhci->lock);
|
||||
|
||||
|
@ -4414,6 +4449,17 @@ int xhci_queue_configure_endpoint(struct xhci_hcd *xhci,
|
|||
command_must_succeed);
|
||||
}
|
||||
|
||||
/* Queue a get root hub port bandwidth command TRB */
|
||||
int xhci_queue_get_port_bw(struct xhci_hcd *xhci,
|
||||
struct xhci_command *cmd, dma_addr_t in_ctx_ptr,
|
||||
u8 dev_speed, bool command_must_succeed)
|
||||
{
|
||||
return queue_command(xhci, cmd, lower_32_bits(in_ctx_ptr),
|
||||
upper_32_bits(in_ctx_ptr), 0,
|
||||
TRB_TYPE(TRB_GET_BW) | DEV_SPEED_FOR_TRB(dev_speed),
|
||||
command_must_succeed);
|
||||
}
|
||||
|
||||
/* Queue an evaluate context command TRB */
|
||||
int xhci_queue_evaluate_context(struct xhci_hcd *xhci, struct xhci_command *cmd,
|
||||
dma_addr_t in_ctx_ptr, u32 slot_id, bool command_must_succeed)
|
||||
|
|
|
@ -0,0 +1,457 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
/*
|
||||
* xHCI host controller sideband support
|
||||
*
|
||||
* Copyright (c) 2023-2025, Intel Corporation.
|
||||
*
|
||||
* Author: Mathias Nyman
|
||||
*/
|
||||
|
||||
#include <linux/usb/xhci-sideband.h>
|
||||
#include <linux/dma-direct.h>
|
||||
|
||||
#include "xhci.h"
|
||||
|
||||
/* sideband internal helpers */
|
||||
static struct sg_table *
|
||||
xhci_ring_to_sgtable(struct xhci_sideband *sb, struct xhci_ring *ring)
|
||||
{
|
||||
struct xhci_segment *seg;
|
||||
struct sg_table *sgt;
|
||||
unsigned int n_pages;
|
||||
struct page **pages;
|
||||
struct device *dev;
|
||||
size_t sz;
|
||||
int i;
|
||||
|
||||
dev = xhci_to_hcd(sb->xhci)->self.sysdev;
|
||||
sz = ring->num_segs * TRB_SEGMENT_SIZE;
|
||||
n_pages = PAGE_ALIGN(sz) >> PAGE_SHIFT;
|
||||
pages = kvmalloc_array(n_pages, sizeof(struct page *), GFP_KERNEL);
|
||||
if (!pages)
|
||||
return NULL;
|
||||
|
||||
sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
|
||||
if (!sgt) {
|
||||
kvfree(pages);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
seg = ring->first_seg;
|
||||
if (!seg)
|
||||
goto err;
|
||||
/*
|
||||
* Rings can potentially have multiple segments, create an array that
|
||||
* carries page references to allocated segments. Utilize the
|
||||
* sg_alloc_table_from_pages() to create the sg table, and to ensure
|
||||
* that page links are created.
|
||||
*/
|
||||
for (i = 0; i < ring->num_segs; i++) {
|
||||
dma_get_sgtable(dev, sgt, seg->trbs, seg->dma,
|
||||
TRB_SEGMENT_SIZE);
|
||||
pages[i] = sg_page(sgt->sgl);
|
||||
sg_free_table(sgt);
|
||||
seg = seg->next;
|
||||
}
|
||||
|
||||
if (sg_alloc_table_from_pages(sgt, pages, n_pages, 0, sz, GFP_KERNEL))
|
||||
goto err;
|
||||
|
||||
/*
|
||||
* Save first segment dma address to sg dma_address field for the sideband
|
||||
* client to have access to the IOVA of the ring.
|
||||
*/
|
||||
sg_dma_address(sgt->sgl) = ring->first_seg->dma;
|
||||
|
||||
return sgt;
|
||||
|
||||
err:
|
||||
kvfree(pages);
|
||||
kfree(sgt);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
__xhci_sideband_remove_endpoint(struct xhci_sideband *sb, struct xhci_virt_ep *ep)
|
||||
{
|
||||
/*
|
||||
* Issue a stop endpoint command when an endpoint is removed.
|
||||
* The stop ep cmd handler will handle the ring cleanup.
|
||||
*/
|
||||
xhci_stop_endpoint_sync(sb->xhci, ep, 0, GFP_KERNEL);
|
||||
|
||||
ep->sideband = NULL;
|
||||
sb->eps[ep->ep_index] = NULL;
|
||||
}
|
||||
|
||||
/* sideband api functions */
|
||||
|
||||
/**
|
||||
* xhci_sideband_notify_ep_ring_free - notify client of xfer ring free
|
||||
* @sb: sideband instance for this usb device
|
||||
* @ep_index: usb endpoint index
|
||||
*
|
||||
* Notifies the xHCI sideband client driver of a xHCI transfer ring free
|
||||
* routine. This will allow for the client to ensure that all transfers
|
||||
* are completed.
|
||||
*
|
||||
* The callback should be synchronous, as the ring free happens after.
|
||||
*/
|
||||
void xhci_sideband_notify_ep_ring_free(struct xhci_sideband *sb,
|
||||
unsigned int ep_index)
|
||||
{
|
||||
struct xhci_sideband_event evt;
|
||||
|
||||
evt.type = XHCI_SIDEBAND_XFER_RING_FREE;
|
||||
evt.evt_data = &ep_index;
|
||||
|
||||
if (sb->notify_client)
|
||||
sb->notify_client(sb->intf, &evt);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xhci_sideband_notify_ep_ring_free);
|
||||
|
||||
/**
|
||||
* xhci_sideband_add_endpoint - add endpoint to sideband access list
|
||||
* @sb: sideband instance for this usb device
|
||||
* @host_ep: usb host endpoint
|
||||
*
|
||||
* Adds an endpoint to the list of sideband accessed endpoints for this usb
|
||||
* device.
|
||||
* After an endpoint is added the sideband client can get the endpoint transfer
|
||||
* ring buffer by calling xhci_sideband_endpoint_buffer()
|
||||
*
|
||||
* Return: 0 on success, negative error otherwise.
|
||||
*/
|
||||
int
|
||||
xhci_sideband_add_endpoint(struct xhci_sideband *sb,
|
||||
struct usb_host_endpoint *host_ep)
|
||||
{
|
||||
struct xhci_virt_ep *ep;
|
||||
unsigned int ep_index;
|
||||
|
||||
mutex_lock(&sb->mutex);
|
||||
ep_index = xhci_get_endpoint_index(&host_ep->desc);
|
||||
ep = &sb->vdev->eps[ep_index];
|
||||
|
||||
if (ep->ep_state & EP_HAS_STREAMS) {
|
||||
mutex_unlock(&sb->mutex);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note, we don't know the DMA mask of the audio DSP device, if its
|
||||
* smaller than for xhci it won't be able to access the endpoint ring
|
||||
* buffer. This could be solved by not allowing the audio class driver
|
||||
* to add the endpoint the normal way, but instead offload it immediately,
|
||||
* and let this function add the endpoint and allocate the ring buffer
|
||||
* with the smallest common DMA mask
|
||||
*/
|
||||
if (sb->eps[ep_index] || ep->sideband) {
|
||||
mutex_unlock(&sb->mutex);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
ep->sideband = sb;
|
||||
sb->eps[ep_index] = ep;
|
||||
mutex_unlock(&sb->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xhci_sideband_add_endpoint);
|
||||
|
||||
/**
|
||||
* xhci_sideband_remove_endpoint - remove endpoint from sideband access list
|
||||
* @sb: sideband instance for this usb device
|
||||
* @host_ep: usb host endpoint
|
||||
*
|
||||
* Removes an endpoint from the list of sideband accessed endpoints for this usb
|
||||
* device.
|
||||
* sideband client should no longer touch the endpoint transfer buffer after
|
||||
* calling this.
|
||||
*
|
||||
* Return: 0 on success, negative error otherwise.
|
||||
*/
|
||||
int
|
||||
xhci_sideband_remove_endpoint(struct xhci_sideband *sb,
|
||||
struct usb_host_endpoint *host_ep)
|
||||
{
|
||||
struct xhci_virt_ep *ep;
|
||||
unsigned int ep_index;
|
||||
|
||||
mutex_lock(&sb->mutex);
|
||||
ep_index = xhci_get_endpoint_index(&host_ep->desc);
|
||||
ep = sb->eps[ep_index];
|
||||
|
||||
if (!ep || !ep->sideband || ep->sideband != sb) {
|
||||
mutex_unlock(&sb->mutex);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
__xhci_sideband_remove_endpoint(sb, ep);
|
||||
xhci_initialize_ring_info(ep->ring);
|
||||
mutex_unlock(&sb->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xhci_sideband_remove_endpoint);
|
||||
|
||||
int
|
||||
xhci_sideband_stop_endpoint(struct xhci_sideband *sb,
|
||||
struct usb_host_endpoint *host_ep)
|
||||
{
|
||||
struct xhci_virt_ep *ep;
|
||||
unsigned int ep_index;
|
||||
|
||||
ep_index = xhci_get_endpoint_index(&host_ep->desc);
|
||||
ep = sb->eps[ep_index];
|
||||
|
||||
if (!ep || !ep->sideband || ep->sideband != sb)
|
||||
return -EINVAL;
|
||||
|
||||
return xhci_stop_endpoint_sync(sb->xhci, ep, 0, GFP_KERNEL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xhci_sideband_stop_endpoint);
|
||||
|
||||
/**
|
||||
* xhci_sideband_get_endpoint_buffer - gets the endpoint transfer buffer address
|
||||
* @sb: sideband instance for this usb device
|
||||
* @host_ep: usb host endpoint
|
||||
*
|
||||
* Returns the address of the endpoint buffer where xHC controller reads queued
|
||||
* transfer TRBs from. This is the starting address of the ringbuffer where the
|
||||
* sideband client should write TRBs to.
|
||||
*
|
||||
* Caller needs to free the returned sg_table
|
||||
*
|
||||
* Return: struct sg_table * if successful. NULL otherwise.
|
||||
*/
|
||||
struct sg_table *
|
||||
xhci_sideband_get_endpoint_buffer(struct xhci_sideband *sb,
|
||||
struct usb_host_endpoint *host_ep)
|
||||
{
|
||||
struct xhci_virt_ep *ep;
|
||||
unsigned int ep_index;
|
||||
|
||||
ep_index = xhci_get_endpoint_index(&host_ep->desc);
|
||||
ep = sb->eps[ep_index];
|
||||
|
||||
if (!ep || !ep->ring || !ep->sideband || ep->sideband != sb)
|
||||
return NULL;
|
||||
|
||||
return xhci_ring_to_sgtable(sb, ep->ring);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xhci_sideband_get_endpoint_buffer);
|
||||
|
||||
/**
|
||||
* xhci_sideband_get_event_buffer - return the event buffer for this device
|
||||
* @sb: sideband instance for this usb device
|
||||
*
|
||||
* If a secondary xhci interupter is set up for this usb device then this
|
||||
* function returns the address of the event buffer where xHC writes
|
||||
* the transfer completion events.
|
||||
*
|
||||
* Caller needs to free the returned sg_table
|
||||
*
|
||||
* Return: struct sg_table * if successful. NULL otherwise.
|
||||
*/
|
||||
struct sg_table *
|
||||
xhci_sideband_get_event_buffer(struct xhci_sideband *sb)
|
||||
{
|
||||
if (!sb || !sb->ir)
|
||||
return NULL;
|
||||
|
||||
return xhci_ring_to_sgtable(sb, sb->ir->event_ring);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xhci_sideband_get_event_buffer);
|
||||
|
||||
/**
|
||||
* xhci_sideband_create_interrupter - creates a new interrupter for this sideband
|
||||
* @sb: sideband instance for this usb device
|
||||
* @num_seg: number of event ring segments to allocate
|
||||
* @ip_autoclear: IP autoclearing support such as MSI implemented
|
||||
*
|
||||
* Sets up a xhci interrupter that can be used for this sideband accessed usb
|
||||
* device. Transfer events for this device can be routed to this interrupters
|
||||
* event ring by setting the 'Interrupter Target' field correctly when queueing
|
||||
* the transfer TRBs.
|
||||
* Once this interrupter is created the interrupter target ID can be obtained
|
||||
* by calling xhci_sideband_interrupter_id()
|
||||
*
|
||||
* Returns 0 on success, negative error otherwise
|
||||
*/
|
||||
int
|
||||
xhci_sideband_create_interrupter(struct xhci_sideband *sb, int num_seg,
|
||||
bool ip_autoclear, u32 imod_interval, int intr_num)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!sb || !sb->xhci)
|
||||
return -ENODEV;
|
||||
|
||||
mutex_lock(&sb->mutex);
|
||||
if (sb->ir) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
sb->ir = xhci_create_secondary_interrupter(xhci_to_hcd(sb->xhci),
|
||||
num_seg, imod_interval,
|
||||
intr_num);
|
||||
if (!sb->ir) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
sb->ir->ip_autoclear = ip_autoclear;
|
||||
|
||||
out:
|
||||
mutex_unlock(&sb->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xhci_sideband_create_interrupter);
|
||||
|
||||
/**
|
||||
* xhci_sideband_remove_interrupter - remove the interrupter from a sideband
|
||||
* @sb: sideband instance for this usb device
|
||||
*
|
||||
* Removes a registered interrupt for a sideband. This would allow for other
|
||||
* sideband users to utilize this interrupter.
|
||||
*/
|
||||
void
|
||||
xhci_sideband_remove_interrupter(struct xhci_sideband *sb)
|
||||
{
|
||||
if (!sb || !sb->ir)
|
||||
return;
|
||||
|
||||
mutex_lock(&sb->mutex);
|
||||
xhci_remove_secondary_interrupter(xhci_to_hcd(sb->xhci), sb->ir);
|
||||
|
||||
sb->ir = NULL;
|
||||
mutex_unlock(&sb->mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xhci_sideband_remove_interrupter);
|
||||
|
||||
/**
|
||||
* xhci_sideband_interrupter_id - return the interrupter target id
|
||||
* @sb: sideband instance for this usb device
|
||||
*
|
||||
* If a secondary xhci interrupter is set up for this usb device then this
|
||||
* function returns the ID used by the interrupter. The sideband client
|
||||
* needs to write this ID to the 'Interrupter Target' field of the transfer TRBs
|
||||
* it queues on the endpoints transfer ring to ensure transfer completion event
|
||||
* are written by xHC to the correct interrupter event ring.
|
||||
*
|
||||
* Returns interrupter id on success, negative error othgerwise
|
||||
*/
|
||||
int
|
||||
xhci_sideband_interrupter_id(struct xhci_sideband *sb)
|
||||
{
|
||||
if (!sb || !sb->ir)
|
||||
return -ENODEV;
|
||||
|
||||
return sb->ir->intr_num;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xhci_sideband_interrupter_id);
|
||||
|
||||
/**
|
||||
* xhci_sideband_register - register a sideband for a usb device
|
||||
* @intf: usb interface associated with the sideband device
|
||||
*
|
||||
* Allows for clients to utilize XHCI interrupters and fetch transfer and event
|
||||
* ring parameters for executing data transfers.
|
||||
*
|
||||
* Return: pointer to a new xhci_sideband instance if successful. NULL otherwise.
|
||||
*/
|
||||
struct xhci_sideband *
|
||||
xhci_sideband_register(struct usb_interface *intf, enum xhci_sideband_type type,
|
||||
int (*notify_client)(struct usb_interface *intf,
|
||||
struct xhci_sideband_event *evt))
|
||||
{
|
||||
struct usb_device *udev = interface_to_usbdev(intf);
|
||||
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
struct xhci_virt_device *vdev;
|
||||
struct xhci_sideband *sb;
|
||||
|
||||
/*
|
||||
* Make sure the usb device is connected to a xhci controller. Fail
|
||||
* registration if the type is anything other than XHCI_SIDEBAND_VENDOR,
|
||||
* as this is the only type that is currently supported by xhci-sideband.
|
||||
*/
|
||||
if (!udev->slot_id || type != XHCI_SIDEBAND_VENDOR)
|
||||
return NULL;
|
||||
|
||||
sb = kzalloc_node(sizeof(*sb), GFP_KERNEL, dev_to_node(hcd->self.sysdev));
|
||||
if (!sb)
|
||||
return NULL;
|
||||
|
||||
mutex_init(&sb->mutex);
|
||||
|
||||
/* check this device isn't already controlled via sideband */
|
||||
spin_lock_irq(&xhci->lock);
|
||||
|
||||
vdev = xhci->devs[udev->slot_id];
|
||||
|
||||
if (!vdev || vdev->sideband) {
|
||||
xhci_warn(xhci, "XHCI sideband for slot %d already in use\n",
|
||||
udev->slot_id);
|
||||
spin_unlock_irq(&xhci->lock);
|
||||
kfree(sb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sb->xhci = xhci;
|
||||
sb->vdev = vdev;
|
||||
sb->intf = intf;
|
||||
sb->type = type;
|
||||
sb->notify_client = notify_client;
|
||||
vdev->sideband = sb;
|
||||
|
||||
spin_unlock_irq(&xhci->lock);
|
||||
|
||||
return sb;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xhci_sideband_register);
|
||||
|
||||
/**
|
||||
* xhci_sideband_unregister - unregister sideband access to a usb device
|
||||
* @sb: sideband instance to be unregistered
|
||||
*
|
||||
* Unregisters sideband access to a usb device and frees the sideband
|
||||
* instance.
|
||||
* After this the endpoint and interrupter event buffers should no longer
|
||||
* be accessed via sideband. The xhci driver can now take over handling
|
||||
* the buffers.
|
||||
*/
|
||||
void
|
||||
xhci_sideband_unregister(struct xhci_sideband *sb)
|
||||
{
|
||||
struct xhci_hcd *xhci;
|
||||
int i;
|
||||
|
||||
if (!sb)
|
||||
return;
|
||||
|
||||
xhci = sb->xhci;
|
||||
|
||||
mutex_lock(&sb->mutex);
|
||||
for (i = 0; i < EP_CTX_PER_DEV; i++)
|
||||
if (sb->eps[i])
|
||||
__xhci_sideband_remove_endpoint(sb, sb->eps[i]);
|
||||
mutex_unlock(&sb->mutex);
|
||||
|
||||
xhci_sideband_remove_interrupter(sb);
|
||||
|
||||
spin_lock_irq(&xhci->lock);
|
||||
sb->xhci = NULL;
|
||||
sb->vdev->sideband = NULL;
|
||||
spin_unlock_irq(&xhci->lock);
|
||||
|
||||
kfree(sb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xhci_sideband_unregister);
|
||||
MODULE_DESCRIPTION("xHCI sideband driver for secondary interrupter management");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -20,6 +20,7 @@
|
|||
#include <linux/string_choices.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/usb/xhci-sideband.h>
|
||||
|
||||
#include "xhci.h"
|
||||
#include "xhci-trace.h"
|
||||
|
@ -329,21 +330,29 @@ int xhci_enable_interrupter(struct xhci_interrupter *ir)
|
|||
if (!ir || !ir->ir_set)
|
||||
return -EINVAL;
|
||||
|
||||
iman = readl(&ir->ir_set->irq_pending);
|
||||
writel(ER_IRQ_ENABLE(iman), &ir->ir_set->irq_pending);
|
||||
iman = readl(&ir->ir_set->iman);
|
||||
iman |= IMAN_IE;
|
||||
writel(iman, &ir->ir_set->iman);
|
||||
|
||||
/* Read operation to guarantee the write has been flushed from posted buffers */
|
||||
readl(&ir->ir_set->iman);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xhci_disable_interrupter(struct xhci_interrupter *ir)
|
||||
int xhci_disable_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir)
|
||||
{
|
||||
u32 iman;
|
||||
|
||||
if (!ir || !ir->ir_set)
|
||||
return -EINVAL;
|
||||
|
||||
iman = readl(&ir->ir_set->irq_pending);
|
||||
writel(ER_IRQ_DISABLE(iman), &ir->ir_set->irq_pending);
|
||||
iman = readl(&ir->ir_set->iman);
|
||||
iman &= ~IMAN_IE;
|
||||
writel(iman, &ir->ir_set->iman);
|
||||
|
||||
iman = readl(&ir->ir_set->iman);
|
||||
if (iman & IMAN_IP)
|
||||
xhci_dbg(xhci, "%s: Interrupt pending\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -354,13 +363,16 @@ int xhci_set_interrupter_moderation(struct xhci_interrupter *ir,
|
|||
{
|
||||
u32 imod;
|
||||
|
||||
if (!ir || !ir->ir_set || imod_interval > U16_MAX * 250)
|
||||
if (!ir || !ir->ir_set)
|
||||
return -EINVAL;
|
||||
|
||||
imod = readl(&ir->ir_set->irq_control);
|
||||
imod &= ~ER_IRQ_INTERVAL_MASK;
|
||||
imod |= (imod_interval / 250) & ER_IRQ_INTERVAL_MASK;
|
||||
writel(imod, &ir->ir_set->irq_control);
|
||||
/* IMODI value in IMOD register is in 250ns increments */
|
||||
imod_interval = umin(imod_interval / 250, IMODI_MASK);
|
||||
|
||||
imod = readl(&ir->ir_set->imod);
|
||||
imod &= ~IMODI_MASK;
|
||||
imod |= imod_interval;
|
||||
writel(imod, &ir->ir_set->imod);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -460,6 +472,82 @@ static int xhci_all_ports_seen_u0(struct xhci_hcd *xhci)
|
|||
return (xhci->port_status_u0 == ((1 << xhci->usb3_rhub.num_ports) - 1));
|
||||
}
|
||||
|
||||
static void xhci_hcd_page_size(struct xhci_hcd *xhci)
|
||||
{
|
||||
u32 page_size;
|
||||
|
||||
page_size = readl(&xhci->op_regs->page_size) & XHCI_PAGE_SIZE_MASK;
|
||||
if (!is_power_of_2(page_size)) {
|
||||
xhci_warn(xhci, "Invalid page size register = 0x%x\n", page_size);
|
||||
/* Fallback to 4K page size, since that's common */
|
||||
page_size = 1;
|
||||
}
|
||||
|
||||
xhci->page_size = page_size << 12;
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "HCD page size set to %iK",
|
||||
xhci->page_size >> 10);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
config_reg = readl(&xhci->op_regs->config_reg);
|
||||
config_reg &= ~HCS_SLOTS_MASK;
|
||||
config_reg |= max_slots;
|
||||
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Setting Max device slots reg = 0x%x",
|
||||
config_reg);
|
||||
writel(config_reg, &xhci->op_regs->config_reg);
|
||||
}
|
||||
|
||||
static void xhci_set_cmd_ring_deq(struct xhci_hcd *xhci)
|
||||
{
|
||||
dma_addr_t deq_dma;
|
||||
u64 crcr;
|
||||
|
||||
deq_dma = xhci_trb_virt_to_dma(xhci->cmd_ring->deq_seg, xhci->cmd_ring->dequeue);
|
||||
deq_dma &= CMD_RING_PTR_MASK;
|
||||
|
||||
crcr = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
|
||||
crcr &= ~CMD_RING_PTR_MASK;
|
||||
crcr |= deq_dma;
|
||||
|
||||
crcr &= ~CMD_RING_CYCLE;
|
||||
crcr |= xhci->cmd_ring->cycle_state;
|
||||
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Setting command ring address to 0x%llx", crcr);
|
||||
xhci_write_64(xhci, crcr, &xhci->op_regs->cmd_ring);
|
||||
}
|
||||
|
||||
static void xhci_set_doorbell_ptr(struct xhci_hcd *xhci)
|
||||
{
|
||||
u32 offset;
|
||||
|
||||
offset = readl(&xhci->cap_regs->db_off) & DBOFF_MASK;
|
||||
xhci->dba = (void __iomem *)xhci->cap_regs + offset;
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"Doorbell array is located at offset 0x%x from cap regs base addr", offset);
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable USB 3.0 device notifications for function remote wake, which is necessary
|
||||
* for allowing USB 3.0 devices to do remote wakeup from U3 (device suspend).
|
||||
*/
|
||||
static void xhci_set_dev_notifications(struct xhci_hcd *xhci)
|
||||
{
|
||||
u32 dev_notf;
|
||||
|
||||
dev_notf = readl(&xhci->op_regs->dev_notification);
|
||||
dev_notf &= ~DEV_NOTE_MASK;
|
||||
dev_notf |= DEV_NOTE_FWAKE;
|
||||
writel(dev_notf, &xhci->op_regs->dev_notification);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize memory for HCD and xHC (one-time init).
|
||||
|
@ -473,11 +561,37 @@ static int xhci_init(struct usb_hcd *hcd)
|
|||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
int retval;
|
||||
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "xhci_init");
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Starting %s", __func__);
|
||||
spin_lock_init(&xhci->lock);
|
||||
|
||||
INIT_LIST_HEAD(&xhci->cmd_list);
|
||||
INIT_DELAYED_WORK(&xhci->cmd_timer, xhci_handle_command_timeout);
|
||||
init_completion(&xhci->cmd_ring_stop_completion);
|
||||
xhci_hcd_page_size(xhci);
|
||||
memset(xhci->devs, 0, MAX_HC_SLOTS * sizeof(*xhci->devs));
|
||||
|
||||
retval = xhci_mem_init(xhci, GFP_KERNEL);
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Finished xhci_init");
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
/* Set the Number of Device Slots Enabled to the maximum supported value */
|
||||
xhci_enable_max_dev_slots(xhci);
|
||||
|
||||
/* Set the address in the Command Ring Control register */
|
||||
xhci_set_cmd_ring_deq(xhci);
|
||||
|
||||
/* Set Device Context Base Address Array pointer */
|
||||
xhci_write_64(xhci, xhci->dcbaa->dma, &xhci->op_regs->dcbaa_ptr);
|
||||
|
||||
/* Set Doorbell array pointer */
|
||||
xhci_set_doorbell_ptr(xhci);
|
||||
|
||||
/* Set USB 3.0 device notifications for function remote wake */
|
||||
xhci_set_dev_notifications(xhci);
|
||||
|
||||
/* Initialize the Primary interrupter */
|
||||
xhci_add_interrupter(xhci, 0);
|
||||
xhci->interrupters[0]->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX;
|
||||
|
||||
/* Initializing Compliance Mode Recovery Data If Needed */
|
||||
if (xhci_compliance_mode_recovery_timer_quirk_check()) {
|
||||
|
@ -485,7 +599,8 @@ static int xhci_init(struct usb_hcd *hcd)
|
|||
compliance_mode_recovery_timer_init(xhci);
|
||||
}
|
||||
|
||||
return retval;
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Finished %s", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
@ -640,7 +755,7 @@ void xhci_stop(struct usb_hcd *hcd)
|
|||
"// Disabling event ring interrupts");
|
||||
temp = readl(&xhci->op_regs->status);
|
||||
writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status);
|
||||
xhci_disable_interrupter(ir);
|
||||
xhci_disable_interrupter(xhci, ir);
|
||||
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "cleaning up memory");
|
||||
xhci_mem_cleanup(xhci);
|
||||
|
@ -719,8 +834,8 @@ static void xhci_save_registers(struct xhci_hcd *xhci)
|
|||
ir->s3_erst_size = readl(&ir->ir_set->erst_size);
|
||||
ir->s3_erst_base = xhci_read_64(xhci, &ir->ir_set->erst_base);
|
||||
ir->s3_erst_dequeue = xhci_read_64(xhci, &ir->ir_set->erst_dequeue);
|
||||
ir->s3_irq_pending = readl(&ir->ir_set->irq_pending);
|
||||
ir->s3_irq_control = readl(&ir->ir_set->irq_control);
|
||||
ir->s3_iman = readl(&ir->ir_set->iman);
|
||||
ir->s3_imod = readl(&ir->ir_set->imod);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -743,28 +858,11 @@ static void xhci_restore_registers(struct xhci_hcd *xhci)
|
|||
writel(ir->s3_erst_size, &ir->ir_set->erst_size);
|
||||
xhci_write_64(xhci, ir->s3_erst_base, &ir->ir_set->erst_base);
|
||||
xhci_write_64(xhci, ir->s3_erst_dequeue, &ir->ir_set->erst_dequeue);
|
||||
writel(ir->s3_irq_pending, &ir->ir_set->irq_pending);
|
||||
writel(ir->s3_irq_control, &ir->ir_set->irq_control);
|
||||
writel(ir->s3_iman, &ir->ir_set->iman);
|
||||
writel(ir->s3_imod, &ir->ir_set->imod);
|
||||
}
|
||||
}
|
||||
|
||||
static void xhci_set_cmd_ring_deq(struct xhci_hcd *xhci)
|
||||
{
|
||||
u64 val_64;
|
||||
|
||||
/* step 2: initialize command ring buffer */
|
||||
val_64 = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
|
||||
val_64 = (val_64 & (u64) CMD_RING_RSVD_BITS) |
|
||||
(xhci_trb_virt_to_dma(xhci->cmd_ring->deq_seg,
|
||||
xhci->cmd_ring->dequeue) &
|
||||
(u64) ~CMD_RING_RSVD_BITS) |
|
||||
xhci->cmd_ring->cycle_state;
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"// Setting command ring address to 0x%llx",
|
||||
(long unsigned long) val_64);
|
||||
xhci_write_64(xhci, val_64, &xhci->op_regs->cmd_ring);
|
||||
}
|
||||
|
||||
/*
|
||||
* The whole command ring must be cleared to zero when we suspend the host.
|
||||
*
|
||||
|
@ -1092,7 +1190,7 @@ int xhci_resume(struct xhci_hcd *xhci, bool power_lost, bool is_auto_resume)
|
|||
xhci_dbg(xhci, "// Disabling event ring interrupts\n");
|
||||
temp = readl(&xhci->op_regs->status);
|
||||
writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status);
|
||||
xhci_disable_interrupter(xhci->interrupters[0]);
|
||||
xhci_disable_interrupter(xhci, xhci->interrupters[0]);
|
||||
|
||||
xhci_dbg(xhci, "cleaning up memory\n");
|
||||
xhci_mem_cleanup(xhci);
|
||||
|
@ -1359,6 +1457,7 @@ static void xhci_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
|
|||
* xhci_get_endpoint_index - Used for passing endpoint bitmasks between the core and
|
||||
* HCDs. Find the index for an endpoint given its descriptor. Use the return
|
||||
* value to right shift 1 for the bitmask.
|
||||
* @desc: USB endpoint descriptor to determine index for
|
||||
*
|
||||
* Index = (epnum * 2) + direction - 1,
|
||||
* where direction = 0 for OUT, 1 for IN.
|
||||
|
@ -3089,6 +3188,42 @@ void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(xhci_reset_bandwidth);
|
||||
|
||||
/* Get the available bandwidth of the ports under the xhci roothub */
|
||||
int xhci_get_port_bandwidth(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx,
|
||||
u8 dev_speed)
|
||||
{
|
||||
struct xhci_command *cmd;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
if (!ctx || !xhci)
|
||||
return -EINVAL;
|
||||
|
||||
cmd = xhci_alloc_command(xhci, true, GFP_KERNEL);
|
||||
if (!cmd)
|
||||
return -ENOMEM;
|
||||
|
||||
cmd->in_ctx = ctx;
|
||||
|
||||
/* get xhci port bandwidth, refer to xhci rev1_2 protocol 4.6.15 */
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
|
||||
ret = xhci_queue_get_port_bw(xhci, cmd, ctx->dma, dev_speed, 0);
|
||||
if (ret) {
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
goto err_out;
|
||||
}
|
||||
xhci_ring_cmd_db(xhci);
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
|
||||
wait_for_completion(cmd->completion);
|
||||
err_out:
|
||||
kfree(cmd->completion);
|
||||
kfree(cmd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void xhci_setup_input_ctx_for_config_ep(struct xhci_hcd *xhci,
|
||||
struct xhci_container_ctx *in_ctx,
|
||||
struct xhci_container_ctx *out_ctx,
|
||||
|
@ -3911,6 +4046,8 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd,
|
|||
}
|
||||
|
||||
if (ep->ring) {
|
||||
if (ep->sideband)
|
||||
xhci_sideband_notify_ep_ring_free(ep->sideband, i);
|
||||
xhci_debugfs_remove_endpoint(xhci, virt_dev, i);
|
||||
xhci_free_endpoint_ring(xhci, virt_dev, i);
|
||||
}
|
||||
|
|
|
@ -152,10 +152,6 @@ struct xhci_op_regs {
|
|||
#define XHCI_RESET_LONG_USEC (10 * 1000 * 1000)
|
||||
#define XHCI_RESET_SHORT_USEC (250 * 1000)
|
||||
|
||||
/* IMAN - Interrupt Management Register */
|
||||
#define IMAN_IE (1 << 1)
|
||||
#define IMAN_IP (1 << 0)
|
||||
|
||||
/* USBSTS - USB status - status bitmasks */
|
||||
/* HC not running - set to 1 when run/stop bit is cleared. */
|
||||
#define STS_HALT XHCI_STS_HALT
|
||||
|
@ -184,23 +180,22 @@ struct xhci_op_regs {
|
|||
* notification type that matches a bit set in this bit field.
|
||||
*/
|
||||
#define DEV_NOTE_MASK (0xffff)
|
||||
#define ENABLE_DEV_NOTE(x) (1 << (x))
|
||||
/* Most of the device notification types should only be used for debug.
|
||||
* SW does need to pay attention to function wake notifications.
|
||||
*/
|
||||
#define DEV_NOTE_FWAKE ENABLE_DEV_NOTE(1)
|
||||
#define DEV_NOTE_FWAKE (1 << 1)
|
||||
|
||||
/* CRCR - Command Ring Control Register - cmd_ring bitmasks */
|
||||
/* bit 0 is the command ring cycle state */
|
||||
/* bit 0 - Cycle bit indicates the ownership of the command ring */
|
||||
#define CMD_RING_CYCLE (1 << 0)
|
||||
/* stop ring operation after completion of the currently executing command */
|
||||
#define CMD_RING_PAUSE (1 << 1)
|
||||
/* stop ring immediately - abort the currently executing command */
|
||||
#define CMD_RING_ABORT (1 << 2)
|
||||
/* true: command ring is running */
|
||||
#define CMD_RING_RUNNING (1 << 3)
|
||||
/* bits 4:5 reserved and should be preserved */
|
||||
/* Command Ring pointer - bit mask for the lower 32 bits. */
|
||||
#define CMD_RING_RSVD_BITS (0x3f)
|
||||
/* bits 63:6 - Command Ring pointer */
|
||||
#define CMD_RING_PTR_MASK GENMASK_ULL(63, 6)
|
||||
|
||||
/* CONFIG - Configure Register - config_reg bitmasks */
|
||||
/* bits 0:7 - maximum number of device slots enabled (NumSlotsEn) */
|
||||
|
@ -215,14 +210,13 @@ struct xhci_op_regs {
|
|||
#define XHCI_PAGE_SIZE_MASK 0xffff
|
||||
|
||||
/**
|
||||
* struct xhci_intr_reg - Interrupt Register Set
|
||||
* @irq_pending: IMAN - Interrupt Management Register. Used to enable
|
||||
* struct xhci_intr_reg - Interrupt Register Set, v1.2 section 5.5.2.
|
||||
* @iman: IMAN - Interrupt Management Register. Used to enable
|
||||
* interrupts and check for pending interrupts.
|
||||
* @irq_control: IMOD - Interrupt Moderation Register.
|
||||
* Used to throttle interrupts.
|
||||
* @erst_size: Number of segments in the Event Ring Segment Table (ERST).
|
||||
* @erst_base: ERST base address.
|
||||
* @erst_dequeue: Event ring dequeue pointer.
|
||||
* @imod: IMOD - Interrupt Moderation Register. Used to throttle interrupts.
|
||||
* @erst_size: ERSTSZ - Number of segments in the Event Ring Segment Table (ERST).
|
||||
* @erst_base: ERSTBA - Event ring segment table base address.
|
||||
* @erst_dequeue: ERDP - Event ring dequeue pointer.
|
||||
*
|
||||
* Each interrupter (defined by a MSI-X vector) has an event ring and an Event
|
||||
* Ring Segment Table (ERST) associated with it. The event ring is comprised of
|
||||
|
@ -232,48 +226,51 @@ struct xhci_op_regs {
|
|||
* updates the dequeue pointer.
|
||||
*/
|
||||
struct xhci_intr_reg {
|
||||
__le32 irq_pending;
|
||||
__le32 irq_control;
|
||||
__le32 iman;
|
||||
__le32 imod;
|
||||
__le32 erst_size;
|
||||
__le32 rsvd;
|
||||
__le64 erst_base;
|
||||
__le64 erst_dequeue;
|
||||
};
|
||||
|
||||
/* irq_pending bitmasks */
|
||||
#define ER_IRQ_PENDING(p) ((p) & 0x1)
|
||||
/* bits 2:31 need to be preserved */
|
||||
/* THIS IS BUGGY - FIXME - IP IS WRITE 1 TO CLEAR */
|
||||
#define ER_IRQ_CLEAR(p) ((p) & 0xfffffffe)
|
||||
#define ER_IRQ_ENABLE(p) ((ER_IRQ_CLEAR(p)) | 0x2)
|
||||
#define ER_IRQ_DISABLE(p) ((ER_IRQ_CLEAR(p)) & ~(0x2))
|
||||
/* iman bitmasks */
|
||||
/* bit 0 - Interrupt Pending (IP), whether there is an interrupt pending. Write-1-to-clear. */
|
||||
#define IMAN_IP (1 << 0)
|
||||
/* bit 1 - Interrupt Enable (IE), whether the interrupter is capable of generating an interrupt */
|
||||
#define IMAN_IE (1 << 1)
|
||||
|
||||
/* irq_control bitmasks */
|
||||
/* Minimum interval between interrupts (in 250ns intervals). The interval
|
||||
* between interrupts will be longer if there are no events on the event ring.
|
||||
* Default is 4000 (1 ms).
|
||||
/* imod bitmasks */
|
||||
/*
|
||||
* bits 15:0 - Interrupt Moderation Interval, the minimum interval between interrupts
|
||||
* (in 250ns intervals). The interval between interrupts will be longer if there are no
|
||||
* events on the event ring. Default is 4000 (1 ms).
|
||||
*/
|
||||
#define ER_IRQ_INTERVAL_MASK (0xffff)
|
||||
/* Counter used to count down the time to the next interrupt - HW use only */
|
||||
#define ER_IRQ_COUNTER_MASK (0xffff << 16)
|
||||
#define IMODI_MASK (0xffff)
|
||||
/* bits 31:16 - Interrupt Moderation Counter, used to count down the time to the next interrupt */
|
||||
#define IMODC_MASK (0xffff << 16)
|
||||
|
||||
/* erst_size bitmasks */
|
||||
/* Preserve bits 16:31 of erst_size */
|
||||
#define ERST_SIZE_MASK (0xffff << 16)
|
||||
/* bits 15:0 - Event Ring Segment Table Size, number of ERST entries */
|
||||
#define ERST_SIZE_MASK (0xffff)
|
||||
|
||||
/* erst_base bitmasks */
|
||||
#define ERST_BASE_RSVDP (GENMASK_ULL(5, 0))
|
||||
/* bits 63:6 - Event Ring Segment Table Base Address Register */
|
||||
#define ERST_BASE_ADDRESS_MASK GENMASK_ULL(63, 6)
|
||||
|
||||
/* erst_dequeue bitmasks */
|
||||
/* Dequeue ERST Segment Index (DESI) - Segment number (or alias)
|
||||
* where the current dequeue pointer lies. This is an optional HW hint.
|
||||
/*
|
||||
* bits 2:0 - Dequeue ERST Segment Index (DESI), is the segment number (or alias) where the
|
||||
* current dequeue pointer lies. This is an optional HW hint.
|
||||
*/
|
||||
#define ERST_DESI_MASK (0x7)
|
||||
/* Event Handler Busy (EHB) - is the event ring scheduled to be serviced by
|
||||
/*
|
||||
* bit 3 - Event Handler Busy (EHB), whether the event ring is scheduled to be serviced by
|
||||
* a work queue (or delayed service routine)?
|
||||
*/
|
||||
#define ERST_EHB (1 << 3)
|
||||
#define ERST_PTR_MASK (GENMASK_ULL(63, 4))
|
||||
/* bits 63:4 - Event Ring Dequeue Pointer */
|
||||
#define ERST_PTR_MASK GENMASK_ULL(63, 4)
|
||||
|
||||
/**
|
||||
* struct xhci_run_regs
|
||||
|
@ -589,6 +586,7 @@ struct xhci_stream_info {
|
|||
|
||||
#define SMALL_STREAM_ARRAY_SIZE 256
|
||||
#define MEDIUM_STREAM_ARRAY_SIZE 1024
|
||||
#define GET_PORT_BW_ARRAY_SIZE 256
|
||||
|
||||
/* Some Intel xHCI host controllers need software to keep track of the bus
|
||||
* bandwidth. Keep track of endpoint info here. Each root port is allocated
|
||||
|
@ -700,6 +698,8 @@ struct xhci_virt_ep {
|
|||
int next_frame_id;
|
||||
/* Use new Isoch TRB layout needed for extended TBC support */
|
||||
bool use_extended_tbc;
|
||||
/* set if this endpoint is controlled via sideband access*/
|
||||
struct xhci_sideband *sideband;
|
||||
};
|
||||
|
||||
enum xhci_overhead_type {
|
||||
|
@ -762,6 +762,8 @@ struct xhci_virt_device {
|
|||
u16 current_mel;
|
||||
/* Used for the debugfs interfaces. */
|
||||
void *debugfs_private;
|
||||
/* set if this endpoint is controlled via sideband access*/
|
||||
struct xhci_sideband *sideband;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -1002,6 +1004,9 @@ enum xhci_setup_dev {
|
|||
/* bits 16:23 are the virtual function ID */
|
||||
/* bits 24:31 are the slot ID */
|
||||
|
||||
/* bits 19:16 are the dev speed */
|
||||
#define DEV_SPEED_FOR_TRB(p) ((p) << 16)
|
||||
|
||||
/* Stop Endpoint TRB - ep_index to endpoint ID for this TRB */
|
||||
#define SUSPEND_PORT_FOR_TRB(p) (((p) & 1) << 23)
|
||||
#define TRB_TO_SUSPEND_PORT(p) (((p) & (1 << 23)) >> 23)
|
||||
|
@ -1447,8 +1452,8 @@ struct xhci_interrupter {
|
|||
bool ip_autoclear;
|
||||
u32 isoc_bei_interval;
|
||||
/* For interrupter registers save and restore over suspend/resume */
|
||||
u32 s3_irq_pending;
|
||||
u32 s3_irq_control;
|
||||
u32 s3_iman;
|
||||
u32 s3_imod;
|
||||
u32 s3_erst_size;
|
||||
u64 s3_erst_base;
|
||||
u64 s3_erst_dequeue;
|
||||
|
@ -1554,6 +1559,7 @@ struct xhci_hcd {
|
|||
struct dma_pool *device_pool;
|
||||
struct dma_pool *segment_pool;
|
||||
struct dma_pool *small_streams_pool;
|
||||
struct dma_pool *port_bw_pool;
|
||||
struct dma_pool *medium_streams_pool;
|
||||
|
||||
/* Host controller watchdog timer structures */
|
||||
|
@ -1846,11 +1852,18 @@ struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci,
|
|||
int type, gfp_t flags);
|
||||
void xhci_free_container_ctx(struct xhci_hcd *xhci,
|
||||
struct xhci_container_ctx *ctx);
|
||||
struct xhci_container_ctx *xhci_alloc_port_bw_ctx(struct xhci_hcd *xhci,
|
||||
gfp_t flags);
|
||||
void xhci_free_port_bw_ctx(struct xhci_hcd *xhci,
|
||||
struct xhci_container_ctx *ctx);
|
||||
struct xhci_interrupter *
|
||||
xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs,
|
||||
u32 imod_interval);
|
||||
u32 imod_interval, unsigned int intr_num);
|
||||
void xhci_remove_secondary_interrupter(struct usb_hcd
|
||||
*hcd, struct xhci_interrupter *ir);
|
||||
void xhci_skip_sec_intr_events(struct xhci_hcd *xhci,
|
||||
struct xhci_ring *ring,
|
||||
struct xhci_interrupter *ir);
|
||||
|
||||
/* xHCI host controller glue */
|
||||
typedef void (*xhci_get_quirks_t)(struct device *, struct xhci_hcd *);
|
||||
|
@ -1891,7 +1904,7 @@ int xhci_alloc_tt_info(struct xhci_hcd *xhci,
|
|||
int xhci_set_interrupter_moderation(struct xhci_interrupter *ir,
|
||||
u32 imod_interval);
|
||||
int xhci_enable_interrupter(struct xhci_interrupter *ir);
|
||||
int xhci_disable_interrupter(struct xhci_interrupter *ir);
|
||||
int xhci_disable_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir);
|
||||
|
||||
/* xHCI ring, segment, TRB, and TD functions */
|
||||
dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg, union xhci_trb *trb);
|
||||
|
@ -1916,6 +1929,11 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
|
|||
int xhci_queue_configure_endpoint(struct xhci_hcd *xhci,
|
||||
struct xhci_command *cmd, dma_addr_t in_ctx_ptr, u32 slot_id,
|
||||
bool command_must_succeed);
|
||||
int xhci_queue_get_port_bw(struct xhci_hcd *xhci,
|
||||
struct xhci_command *cmd, dma_addr_t in_ctx_ptr,
|
||||
u8 dev_speed, bool command_must_succeed);
|
||||
int xhci_get_port_bandwidth(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx,
|
||||
u8 dev_speed);
|
||||
int xhci_queue_evaluate_context(struct xhci_hcd *xhci, struct xhci_command *cmd,
|
||||
dma_addr_t in_ctx_ptr, u32 slot_id, bool command_must_succeed);
|
||||
int xhci_queue_reset_ep(struct xhci_hcd *xhci, struct xhci_command *cmd,
|
||||
|
@ -1936,6 +1954,10 @@ unsigned int count_trbs(u64 addr, u64 len);
|
|||
int xhci_stop_endpoint_sync(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
|
||||
int suspend, gfp_t gfp_flags);
|
||||
void xhci_process_cancelled_tds(struct xhci_virt_ep *ep);
|
||||
void xhci_update_erst_dequeue(struct xhci_hcd *xhci,
|
||||
struct xhci_interrupter *ir,
|
||||
bool clear_ehb);
|
||||
void xhci_add_interrupter(struct xhci_hcd *xhci, unsigned int intr_num);
|
||||
|
||||
/* xHCI roothub code */
|
||||
void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port,
|
||||
|
|
|
@ -36,9 +36,10 @@
|
|||
#define USB5744_CMD_CREG_ACCESS 0x99
|
||||
#define USB5744_CMD_CREG_ACCESS_LSB 0x37
|
||||
#define USB5744_CREG_MEM_ADDR 0x00
|
||||
#define USB5744_CREG_MEM_RD_ADDR 0x04
|
||||
#define USB5744_CREG_WRITE 0x00
|
||||
#define USB5744_CREG_RUNTIMEFLAGS2 0x41
|
||||
#define USB5744_CREG_RUNTIMEFLAGS2_LSB 0x1D
|
||||
#define USB5744_CREG_READ 0x01
|
||||
#define USB5744_CREG_RUNTIMEFLAGS2 0x411D
|
||||
#define USB5744_CREG_BYPASS_UDC_SUSPEND BIT(3)
|
||||
|
||||
static void onboard_dev_attach_usb_driver(struct work_struct *work);
|
||||
|
@ -309,11 +310,88 @@ static void onboard_dev_attach_usb_driver(struct work_struct *work)
|
|||
pr_err("Failed to attach USB driver: %pe\n", ERR_PTR(err));
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_ONBOARD_DEV_USB5744)
|
||||
static int onboard_dev_5744_i2c_read_byte(struct i2c_client *client, u16 addr, u8 *data)
|
||||
{
|
||||
struct i2c_msg msg[2];
|
||||
u8 rd_buf[3];
|
||||
int ret;
|
||||
|
||||
u8 wr_buf[7] = {0, USB5744_CREG_MEM_ADDR, 4,
|
||||
USB5744_CREG_READ, 1,
|
||||
addr >> 8 & 0xff,
|
||||
addr & 0xff};
|
||||
msg[0].addr = client->addr;
|
||||
msg[0].flags = 0;
|
||||
msg[0].len = sizeof(wr_buf);
|
||||
msg[0].buf = wr_buf;
|
||||
|
||||
ret = i2c_transfer(client->adapter, msg, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
wr_buf[0] = USB5744_CMD_CREG_ACCESS;
|
||||
wr_buf[1] = USB5744_CMD_CREG_ACCESS_LSB;
|
||||
wr_buf[2] = 0;
|
||||
msg[0].len = 3;
|
||||
|
||||
ret = i2c_transfer(client->adapter, msg, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
wr_buf[0] = 0;
|
||||
wr_buf[1] = USB5744_CREG_MEM_RD_ADDR;
|
||||
msg[0].len = 2;
|
||||
|
||||
msg[1].addr = client->addr;
|
||||
msg[1].flags = I2C_M_RD;
|
||||
msg[1].len = 2;
|
||||
msg[1].buf = rd_buf;
|
||||
|
||||
ret = i2c_transfer(client->adapter, msg, 2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*data = rd_buf[1];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int onboard_dev_5744_i2c_write_byte(struct i2c_client *client, u16 addr, u8 data)
|
||||
{
|
||||
struct i2c_msg msg[2];
|
||||
int ret;
|
||||
|
||||
u8 wr_buf[8] = {0, USB5744_CREG_MEM_ADDR, 5,
|
||||
USB5744_CREG_WRITE, 1,
|
||||
addr >> 8 & 0xff,
|
||||
addr & 0xff,
|
||||
data};
|
||||
msg[0].addr = client->addr;
|
||||
msg[0].flags = 0;
|
||||
msg[0].len = sizeof(wr_buf);
|
||||
msg[0].buf = wr_buf;
|
||||
|
||||
ret = i2c_transfer(client->adapter, msg, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
msg[0].len = 3;
|
||||
wr_buf[0] = USB5744_CMD_CREG_ACCESS;
|
||||
wr_buf[1] = USB5744_CMD_CREG_ACCESS_LSB;
|
||||
wr_buf[2] = 0;
|
||||
|
||||
ret = i2c_transfer(client->adapter, msg, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int onboard_dev_5744_i2c_init(struct i2c_client *client)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_USB_ONBOARD_DEV_USB5744)
|
||||
struct device *dev = &client->dev;
|
||||
int ret;
|
||||
u8 reg;
|
||||
|
||||
/*
|
||||
* Set BYPASS_UDC_SUSPEND bit to ensure MCU is always enabled
|
||||
|
@ -321,21 +399,17 @@ static int onboard_dev_5744_i2c_init(struct i2c_client *client)
|
|||
* The command writes 5 bytes to memory and single data byte in
|
||||
* configuration register.
|
||||
*/
|
||||
char wr_buf[7] = {USB5744_CREG_MEM_ADDR, 5,
|
||||
USB5744_CREG_WRITE, 1,
|
||||
USB5744_CREG_RUNTIMEFLAGS2,
|
||||
USB5744_CREG_RUNTIMEFLAGS2_LSB,
|
||||
USB5744_CREG_BYPASS_UDC_SUSPEND};
|
||||
ret = onboard_dev_5744_i2c_read_byte(client,
|
||||
USB5744_CREG_RUNTIMEFLAGS2, ®);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "CREG_RUNTIMEFLAGS2 read failed\n");
|
||||
|
||||
ret = i2c_smbus_write_block_data(client, 0, sizeof(wr_buf), wr_buf);
|
||||
reg |= USB5744_CREG_BYPASS_UDC_SUSPEND;
|
||||
ret = onboard_dev_5744_i2c_write_byte(client,
|
||||
USB5744_CREG_RUNTIMEFLAGS2, reg);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "BYPASS_UDC_SUSPEND bit configuration failed\n");
|
||||
|
||||
ret = i2c_smbus_write_word_data(client, USB5744_CMD_CREG_ACCESS,
|
||||
USB5744_CMD_CREG_ACCESS_LSB);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Configuration Register Access Command failed\n");
|
||||
|
||||
/* Send SMBus command to boot hub. */
|
||||
ret = i2c_smbus_write_word_data(client, USB5744_CMD_ATTACH,
|
||||
USB5744_CMD_ATTACH_LSB);
|
||||
|
@ -343,10 +417,13 @@ static int onboard_dev_5744_i2c_init(struct i2c_client *client)
|
|||
return dev_err_probe(dev, ret, "USB Attach with SMBus command failed\n");
|
||||
|
||||
return ret;
|
||||
#else
|
||||
return -ENODEV;
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
static int onboard_dev_5744_i2c_init(struct i2c_client *client)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int onboard_dev_probe(struct platform_device *pdev)
|
||||
{
|
||||
|
@ -490,6 +567,7 @@ static struct platform_driver onboard_dev_driver = {
|
|||
#define VENDOR_ID_CYPRESS 0x04b4
|
||||
#define VENDOR_ID_GENESYS 0x05e3
|
||||
#define VENDOR_ID_MICROCHIP 0x0424
|
||||
#define VENDOR_ID_PARADE 0x1da0
|
||||
#define VENDOR_ID_REALTEK 0x0bda
|
||||
#define VENDOR_ID_TI 0x0451
|
||||
#define VENDOR_ID_VIA 0x2109
|
||||
|
@ -586,14 +664,19 @@ static const struct usb_device_id onboard_dev_id_table[] = {
|
|||
{ USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2517) }, /* USB2517 USB 2.0 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2744) }, /* USB5744 USB 2.0 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_MICROCHIP, 0x5744) }, /* USB5744 USB 3.0 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_PARADE, 0x5511) }, /* PS5511 USB 3.2 */
|
||||
{ USB_DEVICE(VENDOR_ID_PARADE, 0x55a1) }, /* PS5511 USB 2.0 */
|
||||
{ USB_DEVICE(VENDOR_ID_REALTEK, 0x0411) }, /* RTS5411 USB 3.1 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_REALTEK, 0x5411) }, /* RTS5411 USB 2.1 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_REALTEK, 0x0414) }, /* RTS5414 USB 3.2 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_REALTEK, 0x5414) }, /* RTS5414 USB 2.1 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_REALTEK, 0x0179) }, /* RTL8188ETV 2.4GHz WiFi */
|
||||
{ USB_DEVICE(VENDOR_ID_TI, 0x8025) }, /* TI USB8020B 3.0 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_TI, 0x8027) }, /* TI USB8020B 2.0 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_TI, 0x8140) }, /* TI USB8041 3.0 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_TI, 0x8142) }, /* TI USB8041 2.0 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_TI, 0x8440) }, /* TI USB8044 3.0 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_TI, 0x8442) }, /* TI USB8044 2.0 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_VIA, 0x0817) }, /* VIA VL817 3.1 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_VIA, 0x2817) }, /* VIA VL817 2.0 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_XMOS, 0x0013) }, /* XMOS XVF3500 Voice Processor */
|
||||
|
|
|
@ -38,6 +38,13 @@ static const struct onboard_dev_pdata microchip_usb5744_data = {
|
|||
.is_hub = true,
|
||||
};
|
||||
|
||||
static const struct onboard_dev_pdata parade_ps5511_data = {
|
||||
.reset_us = 500,
|
||||
.num_supplies = 2,
|
||||
.supply_names = { "vddd11", "vdd33"},
|
||||
.is_hub = true,
|
||||
};
|
||||
|
||||
static const struct onboard_dev_pdata realtek_rts5411_data = {
|
||||
.reset_us = 0,
|
||||
.num_supplies = 1,
|
||||
|
@ -45,6 +52,13 @@ static const struct onboard_dev_pdata realtek_rts5411_data = {
|
|||
.is_hub = true,
|
||||
};
|
||||
|
||||
static const struct onboard_dev_pdata realtek_rtl8188etv_data = {
|
||||
.reset_us = 0,
|
||||
.num_supplies = 1,
|
||||
.supply_names = { "vdd" },
|
||||
.is_hub = false,
|
||||
};
|
||||
|
||||
static const struct onboard_dev_pdata ti_tusb8020b_data = {
|
||||
.reset_us = 3000,
|
||||
.num_supplies = 1,
|
||||
|
@ -111,6 +125,8 @@ static const struct of_device_id onboard_dev_match[] = {
|
|||
{ .compatible = "usb451,8027", .data = &ti_tusb8020b_data, },
|
||||
{ .compatible = "usb451,8140", .data = &ti_tusb8041_data, },
|
||||
{ .compatible = "usb451,8142", .data = &ti_tusb8041_data, },
|
||||
{ .compatible = "usb451,8440", .data = &ti_tusb8041_data, },
|
||||
{ .compatible = "usb451,8442", .data = &ti_tusb8041_data, },
|
||||
{ .compatible = "usb4b4,6504", .data = &cypress_hx3_data, },
|
||||
{ .compatible = "usb4b4,6506", .data = &cypress_hx3_data, },
|
||||
{ .compatible = "usb4b4,6570", .data = &cypress_hx2vl_data, },
|
||||
|
@ -118,10 +134,13 @@ static const struct of_device_id onboard_dev_match[] = {
|
|||
{ .compatible = "usb5e3,610", .data = &genesys_gl852g_data, },
|
||||
{ .compatible = "usb5e3,620", .data = &genesys_gl852g_data, },
|
||||
{ .compatible = "usb5e3,626", .data = &genesys_gl852g_data, },
|
||||
{ .compatible = "usbbda,179", .data = &realtek_rtl8188etv_data, },
|
||||
{ .compatible = "usbbda,411", .data = &realtek_rts5411_data, },
|
||||
{ .compatible = "usbbda,5411", .data = &realtek_rts5411_data, },
|
||||
{ .compatible = "usbbda,414", .data = &realtek_rts5411_data, },
|
||||
{ .compatible = "usbbda,5414", .data = &realtek_rts5411_data, },
|
||||
{ .compatible = "usb1da0,5511", .data = ¶de_ps5511_data, },
|
||||
{ .compatible = "usb1da0,55a1", .data = ¶de_ps5511_data, },
|
||||
{ .compatible = "usb2109,817", .data = &vialab_vl817_data, },
|
||||
{ .compatible = "usb2109,2817", .data = &vialab_vl817_data, },
|
||||
{ .compatible = "usb20b1,0013", .data = &xmos_xvf3500_data, },
|
||||
|
|
|
@ -126,18 +126,6 @@ config USB_ISP1301
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called phy-isp1301.
|
||||
|
||||
config USB_MV_OTG
|
||||
tristate "Marvell USB OTG support"
|
||||
depends on USB_EHCI_MV && USB_MV_UDC && PM && USB_OTG
|
||||
depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't be 'y'
|
||||
select USB_PHY
|
||||
help
|
||||
Say Y here if you want to build Marvell USB OTG transceiver
|
||||
driver in kernel (including PXA and MMP series). This driver
|
||||
implements role switch between EHCI host driver and gadget driver.
|
||||
|
||||
To compile this driver as a module, choose M here.
|
||||
|
||||
config USB_MXS_PHY
|
||||
tristate "Freescale MXS USB PHY support"
|
||||
depends on ARCH_MXC || ARCH_MXS
|
||||
|
|
|
@ -18,7 +18,6 @@ obj-$(CONFIG_TWL6030_USB) += phy-twl6030-usb.o
|
|||
obj-$(CONFIG_USB_TEGRA_PHY) += phy-tegra-usb.o
|
||||
obj-$(CONFIG_USB_GPIO_VBUS) += phy-gpio-vbus-usb.o
|
||||
obj-$(CONFIG_USB_ISP1301) += phy-isp1301.o
|
||||
obj-$(CONFIG_USB_MV_OTG) += phy-mv-usb.o
|
||||
obj-$(CONFIG_USB_MXS_PHY) += phy-mxs-usb.o
|
||||
obj-$(CONFIG_USB_ULPI) += phy-ulpi.o
|
||||
obj-$(CONFIG_USB_ULPI_VIEWPORT) += phy-ulpi-viewport.o
|
||||
|
|
|
@ -1,881 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2011 Marvell International Ltd. All rights reserved.
|
||||
* Author: Chao Xie <chao.xie@marvell.com>
|
||||
* Neil Zhang <zhangwm@marvell.com>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/string_choices.h>
|
||||
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/platform_data/mv_usb.h>
|
||||
|
||||
#include "phy-mv-usb.h"
|
||||
|
||||
#define DRIVER_DESC "Marvell USB OTG transceiver driver"
|
||||
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static const char driver_name[] = "mv-otg";
|
||||
|
||||
static char *state_string[] = {
|
||||
"undefined",
|
||||
"b_idle",
|
||||
"b_srp_init",
|
||||
"b_peripheral",
|
||||
"b_wait_acon",
|
||||
"b_host",
|
||||
"a_idle",
|
||||
"a_wait_vrise",
|
||||
"a_wait_bcon",
|
||||
"a_host",
|
||||
"a_suspend",
|
||||
"a_peripheral",
|
||||
"a_wait_vfall",
|
||||
"a_vbus_err"
|
||||
};
|
||||
|
||||
static int mv_otg_set_vbus(struct usb_otg *otg, bool on)
|
||||
{
|
||||
struct mv_otg *mvotg = container_of(otg->usb_phy, struct mv_otg, phy);
|
||||
if (mvotg->pdata->set_vbus == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
return mvotg->pdata->set_vbus(on);
|
||||
}
|
||||
|
||||
static int mv_otg_set_host(struct usb_otg *otg,
|
||||
struct usb_bus *host)
|
||||
{
|
||||
otg->host = host;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mv_otg_set_peripheral(struct usb_otg *otg,
|
||||
struct usb_gadget *gadget)
|
||||
{
|
||||
otg->gadget = gadget;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mv_otg_run_state_machine(struct mv_otg *mvotg,
|
||||
unsigned long delay)
|
||||
{
|
||||
dev_dbg(&mvotg->pdev->dev, "transceiver is updated\n");
|
||||
if (!mvotg->qwork)
|
||||
return;
|
||||
|
||||
queue_delayed_work(mvotg->qwork, &mvotg->work, delay);
|
||||
}
|
||||
|
||||
static void mv_otg_timer_await_bcon(struct timer_list *t)
|
||||
{
|
||||
struct mv_otg *mvotg = from_timer(mvotg, t,
|
||||
otg_ctrl.timer[A_WAIT_BCON_TIMER]);
|
||||
|
||||
mvotg->otg_ctrl.a_wait_bcon_timeout = 1;
|
||||
|
||||
dev_info(&mvotg->pdev->dev, "B Device No Response!\n");
|
||||
|
||||
if (spin_trylock(&mvotg->wq_lock)) {
|
||||
mv_otg_run_state_machine(mvotg, 0);
|
||||
spin_unlock(&mvotg->wq_lock);
|
||||
}
|
||||
}
|
||||
|
||||
static int mv_otg_cancel_timer(struct mv_otg *mvotg, unsigned int id)
|
||||
{
|
||||
struct timer_list *timer;
|
||||
|
||||
if (id >= OTG_TIMER_NUM)
|
||||
return -EINVAL;
|
||||
|
||||
timer = &mvotg->otg_ctrl.timer[id];
|
||||
|
||||
if (timer_pending(timer))
|
||||
timer_delete(timer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mv_otg_set_timer(struct mv_otg *mvotg, unsigned int id,
|
||||
unsigned long interval)
|
||||
{
|
||||
struct timer_list *timer;
|
||||
|
||||
if (id >= OTG_TIMER_NUM)
|
||||
return -EINVAL;
|
||||
|
||||
timer = &mvotg->otg_ctrl.timer[id];
|
||||
if (timer_pending(timer)) {
|
||||
dev_err(&mvotg->pdev->dev, "Timer%d is already running\n", id);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
timer->expires = jiffies + interval;
|
||||
add_timer(timer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mv_otg_reset(struct mv_otg *mvotg)
|
||||
{
|
||||
u32 tmp;
|
||||
int ret;
|
||||
|
||||
/* Stop the controller */
|
||||
tmp = readl(&mvotg->op_regs->usbcmd);
|
||||
tmp &= ~USBCMD_RUN_STOP;
|
||||
writel(tmp, &mvotg->op_regs->usbcmd);
|
||||
|
||||
/* Reset the controller to get default values */
|
||||
writel(USBCMD_CTRL_RESET, &mvotg->op_regs->usbcmd);
|
||||
|
||||
ret = readl_poll_timeout_atomic(&mvotg->op_regs->usbcmd, tmp,
|
||||
(tmp & USBCMD_CTRL_RESET), 10, 10000);
|
||||
if (ret < 0) {
|
||||
dev_err(&mvotg->pdev->dev,
|
||||
"Wait for RESET completed TIMEOUT\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
writel(0x0, &mvotg->op_regs->usbintr);
|
||||
tmp = readl(&mvotg->op_regs->usbsts);
|
||||
writel(tmp, &mvotg->op_regs->usbsts);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mv_otg_init_irq(struct mv_otg *mvotg)
|
||||
{
|
||||
u32 otgsc;
|
||||
|
||||
mvotg->irq_en = OTGSC_INTR_A_SESSION_VALID
|
||||
| OTGSC_INTR_A_VBUS_VALID;
|
||||
mvotg->irq_status = OTGSC_INTSTS_A_SESSION_VALID
|
||||
| OTGSC_INTSTS_A_VBUS_VALID;
|
||||
|
||||
if (mvotg->pdata->vbus == NULL) {
|
||||
mvotg->irq_en |= OTGSC_INTR_B_SESSION_VALID
|
||||
| OTGSC_INTR_B_SESSION_END;
|
||||
mvotg->irq_status |= OTGSC_INTSTS_B_SESSION_VALID
|
||||
| OTGSC_INTSTS_B_SESSION_END;
|
||||
}
|
||||
|
||||
if (mvotg->pdata->id == NULL) {
|
||||
mvotg->irq_en |= OTGSC_INTR_USB_ID;
|
||||
mvotg->irq_status |= OTGSC_INTSTS_USB_ID;
|
||||
}
|
||||
|
||||
otgsc = readl(&mvotg->op_regs->otgsc);
|
||||
otgsc |= mvotg->irq_en;
|
||||
writel(otgsc, &mvotg->op_regs->otgsc);
|
||||
}
|
||||
|
||||
static void mv_otg_start_host(struct mv_otg *mvotg, int on)
|
||||
{
|
||||
#ifdef CONFIG_USB
|
||||
struct usb_otg *otg = mvotg->phy.otg;
|
||||
struct usb_hcd *hcd;
|
||||
|
||||
if (!otg->host)
|
||||
return;
|
||||
|
||||
dev_info(&mvotg->pdev->dev, "%s host\n", on ? "start" : "stop");
|
||||
|
||||
hcd = bus_to_hcd(otg->host);
|
||||
|
||||
if (on) {
|
||||
usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
|
||||
device_wakeup_enable(hcd->self.controller);
|
||||
} else {
|
||||
usb_remove_hcd(hcd);
|
||||
}
|
||||
#endif /* CONFIG_USB */
|
||||
}
|
||||
|
||||
static void mv_otg_start_periphrals(struct mv_otg *mvotg, int on)
|
||||
{
|
||||
struct usb_otg *otg = mvotg->phy.otg;
|
||||
|
||||
if (!otg->gadget)
|
||||
return;
|
||||
|
||||
dev_info(mvotg->phy.dev, "gadget %s\n", str_on_off(on));
|
||||
|
||||
if (on)
|
||||
usb_gadget_vbus_connect(otg->gadget);
|
||||
else
|
||||
usb_gadget_vbus_disconnect(otg->gadget);
|
||||
}
|
||||
|
||||
static void otg_clock_enable(struct mv_otg *mvotg)
|
||||
{
|
||||
clk_prepare_enable(mvotg->clk);
|
||||
}
|
||||
|
||||
static void otg_clock_disable(struct mv_otg *mvotg)
|
||||
{
|
||||
clk_disable_unprepare(mvotg->clk);
|
||||
}
|
||||
|
||||
static int mv_otg_enable_internal(struct mv_otg *mvotg)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
if (mvotg->active)
|
||||
return 0;
|
||||
|
||||
dev_dbg(&mvotg->pdev->dev, "otg enabled\n");
|
||||
|
||||
otg_clock_enable(mvotg);
|
||||
if (mvotg->pdata->phy_init) {
|
||||
retval = mvotg->pdata->phy_init(mvotg->phy_regs);
|
||||
if (retval) {
|
||||
dev_err(&mvotg->pdev->dev,
|
||||
"init phy error %d\n", retval);
|
||||
otg_clock_disable(mvotg);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
mvotg->active = 1;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static int mv_otg_enable(struct mv_otg *mvotg)
|
||||
{
|
||||
if (mvotg->clock_gating)
|
||||
return mv_otg_enable_internal(mvotg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mv_otg_disable_internal(struct mv_otg *mvotg)
|
||||
{
|
||||
if (mvotg->active) {
|
||||
dev_dbg(&mvotg->pdev->dev, "otg disabled\n");
|
||||
if (mvotg->pdata->phy_deinit)
|
||||
mvotg->pdata->phy_deinit(mvotg->phy_regs);
|
||||
otg_clock_disable(mvotg);
|
||||
mvotg->active = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void mv_otg_disable(struct mv_otg *mvotg)
|
||||
{
|
||||
if (mvotg->clock_gating)
|
||||
mv_otg_disable_internal(mvotg);
|
||||
}
|
||||
|
||||
static void mv_otg_update_inputs(struct mv_otg *mvotg)
|
||||
{
|
||||
struct mv_otg_ctrl *otg_ctrl = &mvotg->otg_ctrl;
|
||||
u32 otgsc;
|
||||
|
||||
otgsc = readl(&mvotg->op_regs->otgsc);
|
||||
|
||||
if (mvotg->pdata->vbus) {
|
||||
if (mvotg->pdata->vbus->poll() == VBUS_HIGH) {
|
||||
otg_ctrl->b_sess_vld = 1;
|
||||
otg_ctrl->b_sess_end = 0;
|
||||
} else {
|
||||
otg_ctrl->b_sess_vld = 0;
|
||||
otg_ctrl->b_sess_end = 1;
|
||||
}
|
||||
} else {
|
||||
otg_ctrl->b_sess_vld = !!(otgsc & OTGSC_STS_B_SESSION_VALID);
|
||||
otg_ctrl->b_sess_end = !!(otgsc & OTGSC_STS_B_SESSION_END);
|
||||
}
|
||||
|
||||
if (mvotg->pdata->id)
|
||||
otg_ctrl->id = !!mvotg->pdata->id->poll();
|
||||
else
|
||||
otg_ctrl->id = !!(otgsc & OTGSC_STS_USB_ID);
|
||||
|
||||
if (mvotg->pdata->otg_force_a_bus_req && !otg_ctrl->id)
|
||||
otg_ctrl->a_bus_req = 1;
|
||||
|
||||
otg_ctrl->a_sess_vld = !!(otgsc & OTGSC_STS_A_SESSION_VALID);
|
||||
otg_ctrl->a_vbus_vld = !!(otgsc & OTGSC_STS_A_VBUS_VALID);
|
||||
|
||||
dev_dbg(&mvotg->pdev->dev, "%s: ", __func__);
|
||||
dev_dbg(&mvotg->pdev->dev, "id %d\n", otg_ctrl->id);
|
||||
dev_dbg(&mvotg->pdev->dev, "b_sess_vld %d\n", otg_ctrl->b_sess_vld);
|
||||
dev_dbg(&mvotg->pdev->dev, "b_sess_end %d\n", otg_ctrl->b_sess_end);
|
||||
dev_dbg(&mvotg->pdev->dev, "a_vbus_vld %d\n", otg_ctrl->a_vbus_vld);
|
||||
dev_dbg(&mvotg->pdev->dev, "a_sess_vld %d\n", otg_ctrl->a_sess_vld);
|
||||
}
|
||||
|
||||
static void mv_otg_update_state(struct mv_otg *mvotg)
|
||||
{
|
||||
struct mv_otg_ctrl *otg_ctrl = &mvotg->otg_ctrl;
|
||||
int old_state = mvotg->phy.otg->state;
|
||||
|
||||
switch (old_state) {
|
||||
case OTG_STATE_UNDEFINED:
|
||||
mvotg->phy.otg->state = OTG_STATE_B_IDLE;
|
||||
fallthrough;
|
||||
case OTG_STATE_B_IDLE:
|
||||
if (otg_ctrl->id == 0)
|
||||
mvotg->phy.otg->state = OTG_STATE_A_IDLE;
|
||||
else if (otg_ctrl->b_sess_vld)
|
||||
mvotg->phy.otg->state = OTG_STATE_B_PERIPHERAL;
|
||||
break;
|
||||
case OTG_STATE_B_PERIPHERAL:
|
||||
if (!otg_ctrl->b_sess_vld || otg_ctrl->id == 0)
|
||||
mvotg->phy.otg->state = OTG_STATE_B_IDLE;
|
||||
break;
|
||||
case OTG_STATE_A_IDLE:
|
||||
if (otg_ctrl->id)
|
||||
mvotg->phy.otg->state = OTG_STATE_B_IDLE;
|
||||
else if (!(otg_ctrl->a_bus_drop) &&
|
||||
(otg_ctrl->a_bus_req || otg_ctrl->a_srp_det))
|
||||
mvotg->phy.otg->state = OTG_STATE_A_WAIT_VRISE;
|
||||
break;
|
||||
case OTG_STATE_A_WAIT_VRISE:
|
||||
if (otg_ctrl->a_vbus_vld)
|
||||
mvotg->phy.otg->state = OTG_STATE_A_WAIT_BCON;
|
||||
break;
|
||||
case OTG_STATE_A_WAIT_BCON:
|
||||
if (otg_ctrl->id || otg_ctrl->a_bus_drop
|
||||
|| otg_ctrl->a_wait_bcon_timeout) {
|
||||
mv_otg_cancel_timer(mvotg, A_WAIT_BCON_TIMER);
|
||||
mvotg->otg_ctrl.a_wait_bcon_timeout = 0;
|
||||
mvotg->phy.otg->state = OTG_STATE_A_WAIT_VFALL;
|
||||
otg_ctrl->a_bus_req = 0;
|
||||
} else if (!otg_ctrl->a_vbus_vld) {
|
||||
mv_otg_cancel_timer(mvotg, A_WAIT_BCON_TIMER);
|
||||
mvotg->otg_ctrl.a_wait_bcon_timeout = 0;
|
||||
mvotg->phy.otg->state = OTG_STATE_A_VBUS_ERR;
|
||||
} else if (otg_ctrl->b_conn) {
|
||||
mv_otg_cancel_timer(mvotg, A_WAIT_BCON_TIMER);
|
||||
mvotg->otg_ctrl.a_wait_bcon_timeout = 0;
|
||||
mvotg->phy.otg->state = OTG_STATE_A_HOST;
|
||||
}
|
||||
break;
|
||||
case OTG_STATE_A_HOST:
|
||||
if (otg_ctrl->id || !otg_ctrl->b_conn
|
||||
|| otg_ctrl->a_bus_drop)
|
||||
mvotg->phy.otg->state = OTG_STATE_A_WAIT_BCON;
|
||||
else if (!otg_ctrl->a_vbus_vld)
|
||||
mvotg->phy.otg->state = OTG_STATE_A_VBUS_ERR;
|
||||
break;
|
||||
case OTG_STATE_A_WAIT_VFALL:
|
||||
if (otg_ctrl->id
|
||||
|| (!otg_ctrl->b_conn && otg_ctrl->a_sess_vld)
|
||||
|| otg_ctrl->a_bus_req)
|
||||
mvotg->phy.otg->state = OTG_STATE_A_IDLE;
|
||||
break;
|
||||
case OTG_STATE_A_VBUS_ERR:
|
||||
if (otg_ctrl->id || otg_ctrl->a_clr_err
|
||||
|| otg_ctrl->a_bus_drop) {
|
||||
otg_ctrl->a_clr_err = 0;
|
||||
mvotg->phy.otg->state = OTG_STATE_A_WAIT_VFALL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void mv_otg_work(struct work_struct *work)
|
||||
{
|
||||
struct mv_otg *mvotg;
|
||||
struct usb_otg *otg;
|
||||
int old_state;
|
||||
|
||||
mvotg = container_of(to_delayed_work(work), struct mv_otg, work);
|
||||
|
||||
run:
|
||||
/* work queue is single thread, or we need spin_lock to protect */
|
||||
otg = mvotg->phy.otg;
|
||||
old_state = otg->state;
|
||||
|
||||
if (!mvotg->active)
|
||||
return;
|
||||
|
||||
mv_otg_update_inputs(mvotg);
|
||||
mv_otg_update_state(mvotg);
|
||||
|
||||
if (old_state != mvotg->phy.otg->state) {
|
||||
dev_info(&mvotg->pdev->dev, "change from state %s to %s\n",
|
||||
state_string[old_state],
|
||||
state_string[mvotg->phy.otg->state]);
|
||||
|
||||
switch (mvotg->phy.otg->state) {
|
||||
case OTG_STATE_B_IDLE:
|
||||
otg->default_a = 0;
|
||||
if (old_state == OTG_STATE_B_PERIPHERAL)
|
||||
mv_otg_start_periphrals(mvotg, 0);
|
||||
mv_otg_reset(mvotg);
|
||||
mv_otg_disable(mvotg);
|
||||
usb_phy_set_event(&mvotg->phy, USB_EVENT_NONE);
|
||||
break;
|
||||
case OTG_STATE_B_PERIPHERAL:
|
||||
mv_otg_enable(mvotg);
|
||||
mv_otg_start_periphrals(mvotg, 1);
|
||||
usb_phy_set_event(&mvotg->phy, USB_EVENT_ENUMERATED);
|
||||
break;
|
||||
case OTG_STATE_A_IDLE:
|
||||
otg->default_a = 1;
|
||||
mv_otg_enable(mvotg);
|
||||
if (old_state == OTG_STATE_A_WAIT_VFALL)
|
||||
mv_otg_start_host(mvotg, 0);
|
||||
mv_otg_reset(mvotg);
|
||||
break;
|
||||
case OTG_STATE_A_WAIT_VRISE:
|
||||
mv_otg_set_vbus(otg, 1);
|
||||
break;
|
||||
case OTG_STATE_A_WAIT_BCON:
|
||||
if (old_state != OTG_STATE_A_HOST)
|
||||
mv_otg_start_host(mvotg, 1);
|
||||
mv_otg_set_timer(mvotg, A_WAIT_BCON_TIMER,
|
||||
T_A_WAIT_BCON);
|
||||
/*
|
||||
* Now, we directly enter A_HOST. So set b_conn = 1
|
||||
* here. In fact, it need host driver to notify us.
|
||||
*/
|
||||
mvotg->otg_ctrl.b_conn = 1;
|
||||
break;
|
||||
case OTG_STATE_A_HOST:
|
||||
break;
|
||||
case OTG_STATE_A_WAIT_VFALL:
|
||||
/*
|
||||
* Now, we has exited A_HOST. So set b_conn = 0
|
||||
* here. In fact, it need host driver to notify us.
|
||||
*/
|
||||
mvotg->otg_ctrl.b_conn = 0;
|
||||
mv_otg_set_vbus(otg, 0);
|
||||
break;
|
||||
case OTG_STATE_A_VBUS_ERR:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
goto run;
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t mv_otg_irq(int irq, void *dev)
|
||||
{
|
||||
struct mv_otg *mvotg = dev;
|
||||
u32 otgsc;
|
||||
|
||||
otgsc = readl(&mvotg->op_regs->otgsc);
|
||||
writel(otgsc, &mvotg->op_regs->otgsc);
|
||||
|
||||
/*
|
||||
* if we have vbus, then the vbus detection for B-device
|
||||
* will be done by mv_otg_inputs_irq().
|
||||
*/
|
||||
if (mvotg->pdata->vbus)
|
||||
if ((otgsc & OTGSC_STS_USB_ID) &&
|
||||
!(otgsc & OTGSC_INTSTS_USB_ID))
|
||||
return IRQ_NONE;
|
||||
|
||||
if ((otgsc & mvotg->irq_status) == 0)
|
||||
return IRQ_NONE;
|
||||
|
||||
mv_otg_run_state_machine(mvotg, 0);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t mv_otg_inputs_irq(int irq, void *dev)
|
||||
{
|
||||
struct mv_otg *mvotg = dev;
|
||||
|
||||
/* The clock may disabled at this time */
|
||||
if (!mvotg->active) {
|
||||
mv_otg_enable(mvotg);
|
||||
mv_otg_init_irq(mvotg);
|
||||
}
|
||||
|
||||
mv_otg_run_state_machine(mvotg, 0);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
a_bus_req_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct mv_otg *mvotg = dev_get_drvdata(dev);
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n",
|
||||
mvotg->otg_ctrl.a_bus_req);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
a_bus_req_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct mv_otg *mvotg = dev_get_drvdata(dev);
|
||||
|
||||
if (count > 2)
|
||||
return -1;
|
||||
|
||||
/* We will use this interface to change to A device */
|
||||
if (mvotg->phy.otg->state != OTG_STATE_B_IDLE
|
||||
&& mvotg->phy.otg->state != OTG_STATE_A_IDLE)
|
||||
return -1;
|
||||
|
||||
/* The clock may disabled and we need to set irq for ID detected */
|
||||
mv_otg_enable(mvotg);
|
||||
mv_otg_init_irq(mvotg);
|
||||
|
||||
if (buf[0] == '1') {
|
||||
mvotg->otg_ctrl.a_bus_req = 1;
|
||||
mvotg->otg_ctrl.a_bus_drop = 0;
|
||||
dev_dbg(&mvotg->pdev->dev,
|
||||
"User request: a_bus_req = 1\n");
|
||||
|
||||
if (spin_trylock(&mvotg->wq_lock)) {
|
||||
mv_otg_run_state_machine(mvotg, 0);
|
||||
spin_unlock(&mvotg->wq_lock);
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(a_bus_req);
|
||||
|
||||
static ssize_t
|
||||
a_clr_err_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct mv_otg *mvotg = dev_get_drvdata(dev);
|
||||
if (!mvotg->phy.otg->default_a)
|
||||
return -1;
|
||||
|
||||
if (count > 2)
|
||||
return -1;
|
||||
|
||||
if (buf[0] == '1') {
|
||||
mvotg->otg_ctrl.a_clr_err = 1;
|
||||
dev_dbg(&mvotg->pdev->dev,
|
||||
"User request: a_clr_err = 1\n");
|
||||
}
|
||||
|
||||
if (spin_trylock(&mvotg->wq_lock)) {
|
||||
mv_otg_run_state_machine(mvotg, 0);
|
||||
spin_unlock(&mvotg->wq_lock);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_WO(a_clr_err);
|
||||
|
||||
static ssize_t
|
||||
a_bus_drop_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct mv_otg *mvotg = dev_get_drvdata(dev);
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n",
|
||||
mvotg->otg_ctrl.a_bus_drop);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
a_bus_drop_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct mv_otg *mvotg = dev_get_drvdata(dev);
|
||||
if (!mvotg->phy.otg->default_a)
|
||||
return -1;
|
||||
|
||||
if (count > 2)
|
||||
return -1;
|
||||
|
||||
if (buf[0] == '0') {
|
||||
mvotg->otg_ctrl.a_bus_drop = 0;
|
||||
dev_dbg(&mvotg->pdev->dev,
|
||||
"User request: a_bus_drop = 0\n");
|
||||
} else if (buf[0] == '1') {
|
||||
mvotg->otg_ctrl.a_bus_drop = 1;
|
||||
mvotg->otg_ctrl.a_bus_req = 0;
|
||||
dev_dbg(&mvotg->pdev->dev,
|
||||
"User request: a_bus_drop = 1\n");
|
||||
dev_dbg(&mvotg->pdev->dev,
|
||||
"User request: and a_bus_req = 0\n");
|
||||
}
|
||||
|
||||
if (spin_trylock(&mvotg->wq_lock)) {
|
||||
mv_otg_run_state_machine(mvotg, 0);
|
||||
spin_unlock(&mvotg->wq_lock);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(a_bus_drop);
|
||||
|
||||
static struct attribute *inputs_attrs[] = {
|
||||
&dev_attr_a_bus_req.attr,
|
||||
&dev_attr_a_clr_err.attr,
|
||||
&dev_attr_a_bus_drop.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group inputs_attr_group = {
|
||||
.name = "inputs",
|
||||
.attrs = inputs_attrs,
|
||||
};
|
||||
|
||||
static const struct attribute_group *mv_otg_groups[] = {
|
||||
&inputs_attr_group,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static void mv_otg_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mv_otg *mvotg = platform_get_drvdata(pdev);
|
||||
|
||||
if (mvotg->qwork)
|
||||
destroy_workqueue(mvotg->qwork);
|
||||
|
||||
mv_otg_disable(mvotg);
|
||||
|
||||
usb_remove_phy(&mvotg->phy);
|
||||
}
|
||||
|
||||
static int mv_otg_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mv_usb_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct mv_otg *mvotg;
|
||||
struct usb_otg *otg;
|
||||
struct resource *r;
|
||||
int retval = 0, i;
|
||||
|
||||
if (pdata == NULL) {
|
||||
dev_err(&pdev->dev, "failed to get platform data\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
mvotg = devm_kzalloc(&pdev->dev, sizeof(*mvotg), GFP_KERNEL);
|
||||
if (!mvotg)
|
||||
return -ENOMEM;
|
||||
|
||||
otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL);
|
||||
if (!otg)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, mvotg);
|
||||
|
||||
mvotg->pdev = pdev;
|
||||
mvotg->pdata = pdata;
|
||||
|
||||
mvotg->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(mvotg->clk))
|
||||
return PTR_ERR(mvotg->clk);
|
||||
|
||||
mvotg->qwork = create_singlethread_workqueue("mv_otg_queue");
|
||||
if (!mvotg->qwork) {
|
||||
dev_dbg(&pdev->dev, "cannot create workqueue for OTG\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
INIT_DELAYED_WORK(&mvotg->work, mv_otg_work);
|
||||
|
||||
/* OTG common part */
|
||||
mvotg->pdev = pdev;
|
||||
mvotg->phy.dev = &pdev->dev;
|
||||
mvotg->phy.otg = otg;
|
||||
mvotg->phy.label = driver_name;
|
||||
|
||||
otg->state = OTG_STATE_UNDEFINED;
|
||||
otg->usb_phy = &mvotg->phy;
|
||||
otg->set_host = mv_otg_set_host;
|
||||
otg->set_peripheral = mv_otg_set_peripheral;
|
||||
otg->set_vbus = mv_otg_set_vbus;
|
||||
|
||||
for (i = 0; i < OTG_TIMER_NUM; i++)
|
||||
timer_setup(&mvotg->otg_ctrl.timer[i],
|
||||
mv_otg_timer_await_bcon, 0);
|
||||
|
||||
r = platform_get_resource_byname(mvotg->pdev,
|
||||
IORESOURCE_MEM, "phyregs");
|
||||
if (r == NULL) {
|
||||
dev_err(&pdev->dev, "no phy I/O memory resource defined\n");
|
||||
retval = -ENODEV;
|
||||
goto err_destroy_workqueue;
|
||||
}
|
||||
|
||||
mvotg->phy_regs = devm_ioremap(&pdev->dev, r->start, resource_size(r));
|
||||
if (mvotg->phy_regs == NULL) {
|
||||
dev_err(&pdev->dev, "failed to map phy I/O memory\n");
|
||||
retval = -EFAULT;
|
||||
goto err_destroy_workqueue;
|
||||
}
|
||||
|
||||
r = platform_get_resource_byname(mvotg->pdev,
|
||||
IORESOURCE_MEM, "capregs");
|
||||
if (r == NULL) {
|
||||
dev_err(&pdev->dev, "no I/O memory resource defined\n");
|
||||
retval = -ENODEV;
|
||||
goto err_destroy_workqueue;
|
||||
}
|
||||
|
||||
mvotg->cap_regs = devm_ioremap(&pdev->dev, r->start, resource_size(r));
|
||||
if (mvotg->cap_regs == NULL) {
|
||||
dev_err(&pdev->dev, "failed to map I/O memory\n");
|
||||
retval = -EFAULT;
|
||||
goto err_destroy_workqueue;
|
||||
}
|
||||
|
||||
/* we will acces controller register, so enable the udc controller */
|
||||
retval = mv_otg_enable_internal(mvotg);
|
||||
if (retval) {
|
||||
dev_err(&pdev->dev, "mv otg enable error %d\n", retval);
|
||||
goto err_destroy_workqueue;
|
||||
}
|
||||
|
||||
mvotg->op_regs =
|
||||
(struct mv_otg_regs __iomem *) ((unsigned long) mvotg->cap_regs
|
||||
+ (readl(mvotg->cap_regs) & CAPLENGTH_MASK));
|
||||
|
||||
if (pdata->id) {
|
||||
retval = devm_request_threaded_irq(&pdev->dev, pdata->id->irq,
|
||||
NULL, mv_otg_inputs_irq,
|
||||
IRQF_ONESHOT, "id", mvotg);
|
||||
if (retval) {
|
||||
dev_info(&pdev->dev,
|
||||
"Failed to request irq for ID\n");
|
||||
pdata->id = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (pdata->vbus) {
|
||||
mvotg->clock_gating = 1;
|
||||
retval = devm_request_threaded_irq(&pdev->dev, pdata->vbus->irq,
|
||||
NULL, mv_otg_inputs_irq,
|
||||
IRQF_ONESHOT, "vbus", mvotg);
|
||||
if (retval) {
|
||||
dev_info(&pdev->dev,
|
||||
"Failed to request irq for VBUS, "
|
||||
"disable clock gating\n");
|
||||
mvotg->clock_gating = 0;
|
||||
pdata->vbus = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (pdata->disable_otg_clock_gating)
|
||||
mvotg->clock_gating = 0;
|
||||
|
||||
mv_otg_reset(mvotg);
|
||||
mv_otg_init_irq(mvotg);
|
||||
|
||||
r = platform_get_resource(mvotg->pdev, IORESOURCE_IRQ, 0);
|
||||
if (r == NULL) {
|
||||
dev_err(&pdev->dev, "no IRQ resource defined\n");
|
||||
retval = -ENODEV;
|
||||
goto err_disable_clk;
|
||||
}
|
||||
|
||||
mvotg->irq = r->start;
|
||||
if (devm_request_irq(&pdev->dev, mvotg->irq, mv_otg_irq, IRQF_SHARED,
|
||||
driver_name, mvotg)) {
|
||||
dev_err(&pdev->dev, "Request irq %d for OTG failed\n",
|
||||
mvotg->irq);
|
||||
mvotg->irq = 0;
|
||||
retval = -ENODEV;
|
||||
goto err_disable_clk;
|
||||
}
|
||||
|
||||
retval = usb_add_phy(&mvotg->phy, USB_PHY_TYPE_USB2);
|
||||
if (retval < 0) {
|
||||
dev_err(&pdev->dev, "can't register transceiver, %d\n",
|
||||
retval);
|
||||
goto err_disable_clk;
|
||||
}
|
||||
|
||||
spin_lock_init(&mvotg->wq_lock);
|
||||
if (spin_trylock(&mvotg->wq_lock)) {
|
||||
mv_otg_run_state_machine(mvotg, 2 * HZ);
|
||||
spin_unlock(&mvotg->wq_lock);
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev,
|
||||
"successful probe OTG device %s clock gating.\n",
|
||||
mvotg->clock_gating ? "with" : "without");
|
||||
|
||||
return 0;
|
||||
|
||||
err_disable_clk:
|
||||
mv_otg_disable_internal(mvotg);
|
||||
err_destroy_workqueue:
|
||||
destroy_workqueue(mvotg->qwork);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int mv_otg_suspend(struct platform_device *pdev, pm_message_t state)
|
||||
{
|
||||
struct mv_otg *mvotg = platform_get_drvdata(pdev);
|
||||
|
||||
if (mvotg->phy.otg->state != OTG_STATE_B_IDLE) {
|
||||
dev_info(&pdev->dev,
|
||||
"OTG state is not B_IDLE, it is %d!\n",
|
||||
mvotg->phy.otg->state);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (!mvotg->clock_gating)
|
||||
mv_otg_disable_internal(mvotg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mv_otg_resume(struct platform_device *pdev)
|
||||
{
|
||||
struct mv_otg *mvotg = platform_get_drvdata(pdev);
|
||||
u32 otgsc;
|
||||
|
||||
if (!mvotg->clock_gating) {
|
||||
mv_otg_enable_internal(mvotg);
|
||||
|
||||
otgsc = readl(&mvotg->op_regs->otgsc);
|
||||
otgsc |= mvotg->irq_en;
|
||||
writel(otgsc, &mvotg->op_regs->otgsc);
|
||||
|
||||
if (spin_trylock(&mvotg->wq_lock)) {
|
||||
mv_otg_run_state_machine(mvotg, 0);
|
||||
spin_unlock(&mvotg->wq_lock);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct platform_driver mv_otg_driver = {
|
||||
.probe = mv_otg_probe,
|
||||
.remove = mv_otg_remove,
|
||||
.driver = {
|
||||
.name = driver_name,
|
||||
.dev_groups = mv_otg_groups,
|
||||
},
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = mv_otg_suspend,
|
||||
.resume = mv_otg_resume,
|
||||
#endif
|
||||
};
|
||||
module_platform_driver(mv_otg_driver);
|
|
@ -685,10 +685,29 @@ static int usbhs_probe(struct platform_device *pdev)
|
|||
INIT_DELAYED_WORK(&priv->notify_hotplug_work, usbhsc_notify_hotplug);
|
||||
spin_lock_init(usbhs_priv_to_lock(priv));
|
||||
|
||||
/*
|
||||
* Acquire clocks and enable power management (PM) early in the
|
||||
* probe process, as the driver accesses registers during
|
||||
* initialization. Ensure the device is active before proceeding.
|
||||
*/
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
ret = usbhsc_clk_get(dev, priv);
|
||||
if (ret)
|
||||
goto probe_pm_disable;
|
||||
|
||||
ret = pm_runtime_resume_and_get(dev);
|
||||
if (ret)
|
||||
goto probe_clk_put;
|
||||
|
||||
ret = usbhsc_clk_prepare_enable(priv);
|
||||
if (ret)
|
||||
goto probe_pm_put;
|
||||
|
||||
/* call pipe and module init */
|
||||
ret = usbhs_pipe_probe(priv);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto probe_clk_dis_unprepare;
|
||||
|
||||
ret = usbhs_fifo_probe(priv);
|
||||
if (ret < 0)
|
||||
|
@ -698,19 +717,15 @@ static int usbhs_probe(struct platform_device *pdev)
|
|||
if (ret < 0)
|
||||
goto probe_end_fifo_exit;
|
||||
|
||||
/* dev_set_drvdata should be called after usbhs_mod_init */
|
||||
/* platform_set_drvdata() should be called after usbhs_mod_probe() */
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
ret = reset_control_deassert(priv->rsts);
|
||||
if (ret)
|
||||
goto probe_fail_rst;
|
||||
|
||||
ret = usbhsc_clk_get(dev, priv);
|
||||
if (ret)
|
||||
goto probe_fail_clks;
|
||||
|
||||
/*
|
||||
* deviece reset here because
|
||||
* device reset here because
|
||||
* USB device might be used in boot loader.
|
||||
*/
|
||||
usbhs_sys_clock_ctrl(priv, 0);
|
||||
|
@ -721,7 +736,7 @@ static int usbhs_probe(struct platform_device *pdev)
|
|||
if (ret) {
|
||||
dev_warn(dev, "USB function not selected (GPIO)\n");
|
||||
ret = -ENOTSUPP;
|
||||
goto probe_end_mod_exit;
|
||||
goto probe_assert_rest;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -735,14 +750,19 @@ static int usbhs_probe(struct platform_device *pdev)
|
|||
ret = usbhs_platform_call(priv, hardware_init, pdev);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "platform init failed.\n");
|
||||
goto probe_end_mod_exit;
|
||||
goto probe_assert_rest;
|
||||
}
|
||||
|
||||
/* reset phy for connection */
|
||||
usbhs_platform_call(priv, phy_reset, pdev);
|
||||
|
||||
/* power control */
|
||||
pm_runtime_enable(dev);
|
||||
/*
|
||||
* Disable the clocks that were enabled earlier in the probe path,
|
||||
* and let the driver handle the clocks beyond this point.
|
||||
*/
|
||||
usbhsc_clk_disable_unprepare(priv);
|
||||
pm_runtime_put(dev);
|
||||
|
||||
if (!usbhs_get_dparam(priv, runtime_pwctrl)) {
|
||||
usbhsc_power_ctrl(priv, 1);
|
||||
usbhs_mod_autonomy_mode(priv);
|
||||
|
@ -759,9 +779,7 @@ static int usbhs_probe(struct platform_device *pdev)
|
|||
|
||||
return ret;
|
||||
|
||||
probe_end_mod_exit:
|
||||
usbhsc_clk_put(priv);
|
||||
probe_fail_clks:
|
||||
probe_assert_rest:
|
||||
reset_control_assert(priv->rsts);
|
||||
probe_fail_rst:
|
||||
usbhs_mod_remove(priv);
|
||||
|
@ -769,6 +787,14 @@ probe_end_fifo_exit:
|
|||
usbhs_fifo_remove(priv);
|
||||
probe_end_pipe_exit:
|
||||
usbhs_pipe_remove(priv);
|
||||
probe_clk_dis_unprepare:
|
||||
usbhsc_clk_disable_unprepare(priv);
|
||||
probe_pm_put:
|
||||
pm_runtime_put(dev);
|
||||
probe_clk_put:
|
||||
usbhsc_clk_put(priv);
|
||||
probe_pm_disable:
|
||||
pm_runtime_disable(dev);
|
||||
|
||||
dev_info(dev, "probe failed (%d)\n", ret);
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ static int usb_serial_device_match(struct device *dev,
|
|||
const struct device_driver *drv)
|
||||
{
|
||||
const struct usb_serial_port *port = to_usb_serial_port(dev);
|
||||
struct usb_serial_driver *driver = to_usb_serial_driver(drv);
|
||||
const struct usb_serial_driver *driver = to_usb_serial_driver(drv);
|
||||
|
||||
/*
|
||||
* drivers are already assigned to ports in serial_probe so it's
|
||||
|
|
|
@ -458,6 +458,8 @@ static int pl2303_detect_type(struct usb_serial *serial)
|
|||
case 0x605:
|
||||
case 0x700: /* GR */
|
||||
case 0x705:
|
||||
case 0x905: /* GT-2AB */
|
||||
case 0x1005: /* GC-Q20 */
|
||||
return TYPE_HXN;
|
||||
}
|
||||
break;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue