USB / Thunderbolt changes for 7.4-rc1

Here is the big set of USB and Thunderbolt changes for 7.4-rc1.
 Included in here are:
   - thunderbolt driver updates and fixes
   - lots of dt schema conversions
   - usb gadget driver fixes (and a new maintainer!)
   - usb core fixups with some interactions with the driver core
   - usb serial updates and fixes for scan-found "issues"
   - xhci driver updates
   - typec driver updates
   - dwc3 driver updates
   - lots of usb gadget driver fixes for syzbot found "problems"
   - lots of other small USB driver fixes for randomly reported problems.
 
 All of these have been in linux-next with no reported issues.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCatoSZA8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ylC5ACgyKlacs/i8gEGMvrRxDMTFDvuO3gAoMDvwPsy
 Vf+NM23wH/Ptg3DJkynD
 =Y981
 -----END PGP SIGNATURE-----

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

Pull USB / Thunderbolt update from Greg KH:
 "Here is the big set of USB and Thunderbolt changes for 7.4-rc1.
  Included in here are:

   - thunderbolt driver updates and fixes

   - lots of dt schema conversions

   - usb gadget driver fixes (and a new maintainer!)

   - usb core fixups with some interactions with the driver core

   - usb serial updates and fixes for scan-found "issues"

   - xhci driver updates

   - typec driver updates

   - dwc3 driver updates

   - lots of usb gadget driver fixes for syzbot found "problems"

   - lots of other small USB driver fixes for randomly reported problems.

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

* tag 'usb-7.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (158 commits)
  usb: gadget: dummy_hcd: prevent unbind via sysfs
  usb: gadget: dummy_hcd: add tick_us module parameter for timer-rate tuning
  usb: gadget: dummy_hcd: set no_sg_constraint only on the SuperSpeed bus
  usb: gadget: dummy_hcd: fix SuperSpeed ep0 maxpacket
  usb: gadget: fix refcount underflow in gadgetfs_bind() error path
  USB: core: document purpose of interface parent reference
  MAINTAINERS: add Neill Kapron as USB Gadget maintainer
  USB: Documentation: Update description of completion callback context
  USB: serial: mxuport: support RS485 mode configuration
  USB: serial: mxuport: handle SEND_NEXT transmit flow control
  USB: serial: add generic write control callbacks
  USB: serial: mxuport: add MUX50U-based device support
  USB: serial: mxuport: clean up firmware version handling
  USB: serial: mxuport: clean up product ID definitions and device table
  USB: serial: sierra: fix urb leak on write completion
  usb: typec: ucsi_ccg: Add retry mechanism in ccg_read
  usb: host: xhci-tegra: Add Tegra264 XHCI support
  usb: host: xhci-tegra: Skip MBOX MSG_ENABLED on Tegra264
  dt-bindings: usb: Add Tegra264 XUSB host support
  usb: xhci-pci: Add TUSB73x0 definitions
  ...
This commit is contained in:
Linus Torvalds 2026-10-24 15:31:19 -10:00
commit 168151cdb0
154 changed files with 3546 additions and 7571 deletions

View File

@ -0,0 +1,109 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/dma/ti/ti,cppi41.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: TI CPPI41 DMA Controller
maintainers:
- Sebastian Andrzej Siewior <bigeasy@linutronix.de>
properties:
compatible:
enum:
- ti,am3359-cppi41
- ti,da830-cppi41
reg:
minItems: 3
maxItems: 4
reg-names:
minItems: 3
maxItems: 4
interrupts:
maxItems: 1
interrupt-names:
const: glue
"#dma-cells":
const: 2
dma-channels:
description: Number of DMA channels supported.
$ref: /schemas/types.yaml#/definitions/uint32
dma-requests:
description: Number of DMA requests supported.
$ref: /schemas/types.yaml#/definitions/uint32
power-domains:
maxItems: 1
"#dma-channels":
description: Deprecated, use dma-channels instead.
deprecated: true
"#dma-requests":
description: Deprecated, use dma-requests instead.
deprecated: true
required:
- compatible
- reg
- reg-names
- "#dma-cells"
- dma-channels
allOf:
- $ref: /schemas/dma/dma-controller.yaml#
- if:
properties:
compatible:
contains:
const: ti,am3359-cppi41
then:
properties:
reg:
minItems: 4
reg-names:
items:
- const: glue
- const: controller
- const: scheduler
- const: queuemgr
power-domains: false
required:
- interrupt-names
- interrupts
- dma-requests
else:
properties:
reg:
maxItems: 3
reg-names:
items:
- const: controller
- const: scheduler
- const: queuemgr
unevaluatedProperties: false
examples:
- |
dma-controller@7402000 {
compatible = "ti,am3359-cppi41";
reg = <0x47400000 0x1000>,
<0x47402000 0x1000>,
<0x47403000 0x1000>,
<0x47404000 0x4000>;
reg-names = "glue", "controller", "scheduler", "queuemgr";
interrupts = <17>;
interrupt-names = "glue";
#dma-cells = <2>;
dma-channels = <30>;
dma-requests = <256>;
};

View File

@ -0,0 +1,49 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/phy/ti,am335x-usb-phy.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: TI AM335x USB PHY
maintainers:
- Sebastian Andrzej Siewior <bigeasy@linutronix.de>
properties:
compatible:
const: ti,am335x-usb-phy
reg:
maxItems: 1
reg-names:
const: phy
ti,ctrl_mod:
$ref: /schemas/types.yaml#/definitions/phandle
description: Reference to the reset module node.
"#phy-cells":
const: 0
vcc-supply:
description: Phandle to the regulator that provides power to the PHY.
required:
- compatible
- reg
- reg-names
- ti,ctrl_mod
- "#phy-cells"
additionalProperties: false
examples:
- |
usb-phy@47401300 {
compatible = "ti,am335x-usb-phy";
reg = <0x47401300 0x100>;
reg-names = "phy";
ti,ctrl_mod = <&ctrl_mod>;
#phy-cells = <0>;
};

View File

@ -1,200 +0,0 @@
AM33xx MUSB
~~~~~~~~~~~~~~~
- compatible: ti,am33xx-usb
- reg: offset and length of the usbss register sets
- ti,hwmods : must be "usb_otg_hs"
The glue layer contains multiple child nodes. It is required to have
at least a control module node, USB node and a PHY node. The second USB
node and its PHY node are optional. The DMA node is also optional.
Reset module
~~~~~~~~~~~~
- compatible: ti,am335x-usb-ctrl-module
- reg: offset and length of the "USB control registers" in the "Control
Module" block. A second offset and length for the USB wake up control
in the same memory block.
- reg-names: "phy_ctrl" for the "USB control registers" and "wakeup" for
the USB wake up control register.
USB PHY
~~~~~~~
compatible: ti,am335x-usb-phy
reg: offset and length of the "USB PHY" register space
ti,ctrl_mod: reference to the "reset module" node
reg-names: phy
The PHY should have a "phy" alias numbered properly in the alias
node.
USB
~~~
- compatible: ti,musb-am33xx
- reg: offset and length of "USB Controller Registers", and offset and
length of "USB Core" register space.
- reg-names: control for the ""USB Controller Registers" and "mc" for
"USB Core" register space
- interrupts: USB interrupt number
- interrupt-names: mc
- dr_mode: Should be one of "host", "peripheral" or "otg".
- mentor,multipoint: Should be "1" indicating the musb controller supports
multipoint. This is a MUSB configuration-specific setting.
- mentor,num-eps: Specifies the number of endpoints. This is also a
MUSB configuration-specific setting. Should be set to "16"
- mentor,ram-bits: Specifies the ram address size. Should be set to "12"
- mentor,power: Should be "500". This signifies the controller can supply up to
500mA when operating in host mode.
- phys: reference to the USB phy
- dmas: specifies the dma channels
- dma-names: specifies the names of the channels. Use "rxN" for receive
and "txN" for transmit endpoints. N specifies the endpoint number.
The controller should have an "usb" alias numbered properly in the alias
node.
DMA
~~~
- compatible: ti,am3359-cppi41
- reg: offset and length of the following register spaces: USBSS, USB
CPPI DMA Controller, USB CPPI DMA Scheduler, USB Queue Manager
- reg-names: glue, controller, scheduler, queuemgr
- #dma-cells: should be set to 2. The first number represents the
endpoint number (0 … 14 for endpoints 1 … 15 on instance 0 and 15 … 29
for endpoints 1 … 15 on instance 1). The second number is 0 for RX and
1 for TX transfers.
- dma-channels: should be set to 30 representing the 15 endpoints for
each USB instance.
- #dma-channels: deprecated
Example:
~~~~~~~~
The following example contains all the nodes as used on am335x-evm:
aliases {
usb0 = &usb0;
usb1 = &usb1;
phy0 = &usb0_phy;
phy1 = &usb1_phy;
};
usb: usb@47400000 {
compatible = "ti,am33xx-usb";
reg = <0x47400000 0x1000>;
ranges;
#address-cells = <1>;
#size-cells = <1>;
ti,hwmods = "usb_otg_hs";
ctrl_mod: control@44e10000 {
compatible = "ti,am335x-usb-ctrl-module";
reg = <0x44e10620 0x10
0x44e10648 0x4>;
reg-names = "phy_ctrl", "wakeup";
};
usb0_phy: usb-phy@47401300 {
compatible = "ti,am335x-usb-phy";
reg = <0x47401300 0x100>;
reg-names = "phy";
ti,ctrl_mod = <&ctrl_mod>;
#phy-cells = <0>;
};
usb0: usb@47401000 {
compatible = "ti,musb-am33xx";
reg = <0x47401400 0x400
0x47401000 0x200>;
reg-names = "mc", "control";
interrupts = <18>;
interrupt-names = "mc";
dr_mode = "otg"
mentor,multipoint = <1>;
mentor,num-eps = <16>;
mentor,ram-bits = <12>;
mentor,power = <500>;
phys = <&usb0_phy>;
dmas = <&cppi41dma 0 0 &cppi41dma 1 0
&cppi41dma 2 0 &cppi41dma 3 0
&cppi41dma 4 0 &cppi41dma 5 0
&cppi41dma 6 0 &cppi41dma 7 0
&cppi41dma 8 0 &cppi41dma 9 0
&cppi41dma 10 0 &cppi41dma 11 0
&cppi41dma 12 0 &cppi41dma 13 0
&cppi41dma 14 0 &cppi41dma 0 1
&cppi41dma 1 1 &cppi41dma 2 1
&cppi41dma 3 1 &cppi41dma 4 1
&cppi41dma 5 1 &cppi41dma 6 1
&cppi41dma 7 1 &cppi41dma 8 1
&cppi41dma 9 1 &cppi41dma 10 1
&cppi41dma 11 1 &cppi41dma 12 1
&cppi41dma 13 1 &cppi41dma 14 1>;
dma-names =
"rx1", "rx2", "rx3", "rx4", "rx5", "rx6", "rx7",
"rx8", "rx9", "rx10", "rx11", "rx12", "rx13",
"rx14", "rx15",
"tx1", "tx2", "tx3", "tx4", "tx5", "tx6", "tx7",
"tx8", "tx9", "tx10", "tx11", "tx12", "tx13",
"tx14", "tx15";
};
usb1_phy: usb-phy@47401b00 {
compatible = "ti,am335x-usb-phy";
reg = <0x47401b00 0x100>;
reg-names = "phy";
ti,ctrl_mod = <&ctrl_mod>;
#phy-cells = <0>;
};
usb1: usb@47401800 {
compatible = "ti,musb-am33xx";
reg = <0x47401c00 0x400
0x47401800 0x200>;
reg-names = "mc", "control";
interrupts = <19>;
interrupt-names = "mc";
dr_mode = "host"
mentor,multipoint = <1>;
mentor,num-eps = <16>;
mentor,ram-bits = <12>;
mentor,power = <500>;
phys = <&usb1_phy>;
dmas = <&cppi41dma 15 0 &cppi41dma 16 0
&cppi41dma 17 0 &cppi41dma 18 0
&cppi41dma 19 0 &cppi41dma 20 0
&cppi41dma 21 0 &cppi41dma 22 0
&cppi41dma 23 0 &cppi41dma 24 0
&cppi41dma 25 0 &cppi41dma 26 0
&cppi41dma 27 0 &cppi41dma 28 0
&cppi41dma 29 0 &cppi41dma 15 1
&cppi41dma 16 1 &cppi41dma 17 1
&cppi41dma 18 1 &cppi41dma 19 1
&cppi41dma 20 1 &cppi41dma 21 1
&cppi41dma 22 1 &cppi41dma 23 1
&cppi41dma 24 1 &cppi41dma 25 1
&cppi41dma 26 1 &cppi41dma 27 1
&cppi41dma 28 1 &cppi41dma 29 1>;
dma-names =
"rx1", "rx2", "rx3", "rx4", "rx5", "rx6", "rx7",
"rx8", "rx9", "rx10", "rx11", "rx12", "rx13",
"rx14", "rx15",
"tx1", "tx2", "tx3", "tx4", "tx5", "tx6", "tx7",
"tx8", "tx9", "tx10", "tx11", "tx12", "tx13",
"tx14", "tx15";
};
cppi41dma: dma-controller@7402000 {
compatible = "ti,am3359-cppi41";
reg = <0x47400000 0x1000
0x47402000 0x1000
0x47403000 0x1000
0x47404000 0x4000>;
reg-names = "glue", "controller", "scheduler", "queuemgr";
interrupts = <17>;
interrupt-names = "glue";
#dma-cells = <2>;
dma-channels = <30>;
dma-requests = <256>;
};
};

View File

@ -1,81 +0,0 @@
TI DA8xx MUSB
~~~~~~~~~~~~~
For DA8xx/OMAP-L1x/AM17xx/AM18xx platforms.
Required properties:
~~~~~~~~~~~~~~~~~~~~
- compatible : Should be set to "ti,da830-musb".
- reg: Offset and length of the USB controller register set.
- interrupts: The USB interrupt number.
- interrupt-names: Should be set to "mc".
- dr_mode: The USB operation mode. Should be one of "host", "peripheral" or "otg".
- phys: Phandle for the PHY device
- phy-names: Should be "usb-phy"
- dmas: specifies the dma channels
- dma-names: specifies the names of the channels. Use "rxN" for receive
and "txN" for transmit endpoints. N specifies the endpoint number.
Optional properties:
~~~~~~~~~~~~~~~~~~~~
- vbus-supply: Phandle to a regulator providing the USB bus power.
DMA
~~~
- compatible: ti,da830-cppi41
- reg: offset and length of the following register spaces: CPPI DMA Controller,
CPPI DMA Scheduler, Queue Manager
- reg-names: "controller", "scheduler", "queuemgr"
- #dma-cells: should be set to 2. The first number represents the
channel number (0 … 3 for endpoints 1 … 4).
The second number is 0 for RX and 1 for TX transfers.
- dma-channels: should be set to 4 representing the 4 endpoints.
- #dma-channels: deprecated
Example:
usb_phy: usb-phy {
compatible = "ti,da830-usb-phy";
#phy-cells = <0>;
};
usb0: usb@200000 {
compatible = "ti,da830-musb";
reg = <0x00200000 0x1000>;
ranges;
#address-cells = <1>;
#size-cells = <1>;
interrupts = <58>;
interrupt-names = "mc";
dr_mode = "host";
vbus-supply = <&usb_vbus>;
phys = <&usb_phy 0>;
phy-names = "usb-phy";
dmas = <&cppi41dma 0 0 &cppi41dma 1 0
&cppi41dma 2 0 &cppi41dma 3 0
&cppi41dma 0 1 &cppi41dma 1 1
&cppi41dma 2 1 &cppi41dma 3 1>;
dma-names =
"rx1", "rx2", "rx3", "rx4",
"tx1", "tx2", "tx3", "tx4";
cppi41dma: dma-controller@201000 {
compatible = "ti,da830-cppi41";
reg = <0x201000 0x1000
0x202000 0x1000
0x204000 0x4000>;
reg-names = "controller", "scheduler", "queuemgr";
interrupts = <58>;
#dma-cells = <2>;
dma-channels = <4>;
};
};

View File

@ -55,6 +55,10 @@ properties:
- rockchip,rk3588-ehci
- snps,hsdk-v1.0-ehci
- socionext,uniphier-ehci
- st,stm32mp13-ehci
- st,stm32mp15-ehci
- st,stm32mp23-ehci
- st,stm32mp25-ehci
- const: generic-ehci
- items:
- enum:
@ -80,6 +84,9 @@ properties:
minItems: 1
maxItems: 2
access-controllers:
maxItems: 1
dma-coherent: true
interrupts:
@ -193,6 +200,23 @@ allOf:
else:
properties:
clock-names: false
- if:
properties:
compatible:
contains:
enum:
- st,stm32mp23-ehci
- st,stm32mp25-ehci
then:
properties:
clocks:
items:
- description: PHY clock
- description: Bus clock
required:
- access-controllers
- clocks
unevaluatedProperties: false

View File

@ -49,6 +49,10 @@ properties:
- ingenic,jz4740-ohci
- rockchip,rk3588-ohci
- snps,hsdk-v1.0-ohci
- st,stm32mp13-ohci
- st,stm32mp15-ohci
- st,stm32mp23-ohci
- st,stm32mp25-ohci
- const: generic-ohci
- enum:
- generic-ohci
@ -65,6 +69,9 @@ properties:
reg:
maxItems: 1
access-controllers:
maxItems: 1
dma-coherent: true
interrupts:
@ -213,6 +220,23 @@ allOf:
clocks:
minItems: 1
maxItems: 3
- if:
properties:
compatible:
contains:
enum:
- st,stm32mp23-ohci
- st,stm32mp25-ohci
then:
properties:
clocks:
items:
- description: PHY clock
- description: Bus clock
required:
- access-controllers
- clocks
unevaluatedProperties: false

View File

@ -19,9 +19,9 @@ properties:
- enum:
- marvell,armada-375-xhci
- marvell,armada-380-xhci
- description: Armada 37xx/8k SoCs
items:
- items:
- enum:
- axiado,ax3005-xhci
- marvell,armada3700-xhci
- marvell,armada-8k-xhci
- const: generic-xhci

View File

@ -22,6 +22,7 @@ properties:
- nxp,cbdtu02043
- onnn,fsusb42
- onnn,fsusb43l10x
- onnn,fusb340
- pericom,pi3usb102
- ti,tmuxhs4212
- const: gpio-sbu-mux

View File

@ -17,7 +17,9 @@ description: |
properties:
compatible:
const: nvidia,tegra234-xusb
enum:
- nvidia,tegra234-xusb
- nvidia,tegra264-xusb
reg:
items:
@ -43,6 +45,7 @@ properties:
- description: USB wake event 4
- description: USB wake event 5
- description: USB wake event 6
- description: USB wake event 7
description: |
The first two interrupts are required for the USB host controller. The
remaining USB wake event interrupts are optional. Each USB wake event is
@ -50,10 +53,9 @@ properties:
platform. The USB host controller can function even if no wake-up events
are defined. The USB wake event interrupts are handled by the Tegra PMC;
hence, the interrupt controller for these is the PMC and the interrupt
IDs correspond to the PMC wake event IDs. A complete list of wake event
IDs is provided below, and this information is also present in the Tegra
TRM document.
IDs correspond to the PMC wake event IDs.
Tegra234 PMC wake events:
PMC wake-up 76 for USB3 port 0 wakeup
PMC wake-up 77 for USB3 port 1 wakeup
PMC wake-up 78 for USB3 port 2 and port 3 wakeup
@ -62,6 +64,16 @@ properties:
PMC wake-up 81 for USB2 port 2 wakeup
PMC wake-up 82 for USB2 port 3 wakeup
Tegra264 PMC wake events:
PMC wake-up 79 for USB3 port 0 wakeup
PMC wake-up 80 for USB3 port 1 wakeup
PMC wake-up 81 for USB3 port 2 wakeup
PMC wake-up 82 for USB3 port 3 wakeup
PMC wake-up 83 for USB2 port 0 wakeup
PMC wake-up 84 for USB2 port 1 wakeup
PMC wake-up 85 for USB2 port 2 wakeup
PMC wake-up 86 for USB2 port 3 wakeup
clocks:
items:
- description: XUSB host clock
@ -97,7 +109,13 @@ properties:
- const: write
iommus:
maxItems: 1
minItems: 1
items:
- description: XUSB host stream ID
- description: XUSB host stream ID for virtual function 1
- description: XUSB host stream ID for virtual function 2
- description: XUSB host stream ID for virtual function 3
- description: XUSB host stream ID for virtual function 4
nvidia,xusb-padctl:
$ref: /schemas/types.yaml#/definitions/phandle
@ -137,6 +155,30 @@ properties:
allOf:
- $ref: usb-xhci.yaml
- if:
properties:
compatible:
contains:
const: nvidia,tegra234-xusb
then:
properties:
interrupts:
maxItems: 9
iommus:
maxItems: 1
- if:
properties:
compatible:
contains:
const: nvidia,tegra264-xusb
then:
properties:
interrupts:
maxItems: 10
iommus:
minItems: 5
unevaluatedProperties: false
examples:

View File

@ -1,23 +0,0 @@
DA8XX USB OHCI controller
Required properties:
- compatible: Should be "ti,da830-ohci"
- reg: Should contain one register range i.e. start and length
- interrupts: Description of the interrupt line
- phys: Phandle for the PHY device
- phy-names: Should be "usb-phy"
Optional properties:
- vbus-supply: phandle of regulator that controls vbus power / over-current
Example:
ohci: usb@225000 {
compatible = "ti,da830-ohci";
reg = <0x225000 0x1000>;
interrupts = <59>;
phys = <&usb_phy 1>;
phy-names = "usb-phy";
vbus-supply = <&reg_usb_ohci>;
};

View File

@ -41,9 +41,11 @@ properties:
- qcom,maili-dwc3
- qcom,milos-dwc3
- qcom,msm8953-dwc3
- qcom,msm8974-dwc3
- qcom,msm8994-dwc3
- qcom,msm8996-dwc3
- qcom,msm8998-dwc3
- qcom,nord-dwc3
- qcom,qcm2290-dwc3
- qcom,qcs404-dwc3
- qcom,qcs615-dwc3
@ -270,6 +272,7 @@ allOf:
contains:
enum:
- qcom,ipq8074-dwc3
- qcom,msm8974-dwc3
- qcom,qdu1000-dwc3
then:
properties:
@ -361,6 +364,7 @@ allOf:
enum:
- qcom,eliza-dwc3
- qcom,milos-dwc3
- qcom,nord-dwc3
- qcom,qcm2290-dwc3
- qcom,qcs615-dwc3
- qcom,sar2130p-dwc3
@ -455,6 +459,23 @@ allOf:
- const: qusb2_phy
- const: ss_phy_irq
- if:
properties:
compatible:
contains:
enum:
- qcom,msm8974-dwc3
then:
properties:
interrupts:
minItems: 3
maxItems: 3
interrupt-names:
items:
- const: dwc_usb3
- const: pwr_event
- const: hs_phy_irq
- if:
properties:
compatible:
@ -554,6 +575,7 @@ allOf:
- qcom,ipq8064-dwc3
- qcom,kaanapali-dwc3
- qcom,maili-dwc3
- qcom,nord-dwc3
- qcom,qcs615-dwc3
- qcom,qcs8300-dwc3
- qcom,qdu1000-dwc3

View File

@ -0,0 +1,38 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/ti,am335x-usb-ctrl-module.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: TI AM335x USB Control Module
maintainers:
- Sebastian Andrzej Siewior <bigeasy@linutronix.de>
properties:
compatible:
const: ti,am335x-usb-ctrl-module
reg:
maxItems: 2
reg-names:
items:
- const: phy_ctrl
- const: wakeup
required:
- compatible
- reg
- reg-names
additionalProperties: false
examples:
- |
control@44e10000 {
compatible = "ti,am335x-usb-ctrl-module";
reg = <0x44e10620 0x10>,
<0x44e10648 0x4>;
reg-names = "phy_ctrl", "wakeup";
};

View File

@ -0,0 +1,103 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/ti,am33xx-usb.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: TI AM33xx USB Wrapper
description:
The glue layer contains multiple child nodes. It is required to have at least
a USB node and a PHY node. The second USB node and its PHY node are optional.
The DMA node is also optional.
maintainers:
- Sebastian Andrzej Siewior <bigeasy@linutronix.de>
properties:
compatible:
const: ti,am33xx-usb
reg:
maxItems: 1
ranges: true
"#address-cells":
const: 1
"#size-cells":
const: 1
ti,hwmods:
$ref: /schemas/types.yaml#/definitions/string
description: Name of the hwmod associated with the USB module.
const: usb_otg_hs
deprecated: true
patternProperties:
"^(usb-)?phy@[0-9a-f]+$":
$ref: /schemas/phy/ti,am335x-usb-phy.yaml#
"^usb@[0-9a-f]+$":
$ref: /schemas/usb/ti,musb-am33xx.yaml#
"^dma-controller@[0-9a-f]+$":
$ref: /schemas/dma/ti/ti,cppi41.yaml#
required:
- compatible
- reg
- ranges
- "#address-cells"
- "#size-cells"
additionalProperties: false
examples:
- |
usb@47400000 {
compatible = "ti,am33xx-usb";
reg = <0x47400000 0x1000>;
ranges;
#address-cells = <1>;
#size-cells = <1>;
ti,hwmods = "usb_otg_hs";
usb-phy@47401300 {
compatible = "ti,am335x-usb-phy";
reg = <0x47401300 0x100>;
reg-names = "phy";
ti,ctrl_mod = <&ctrl_mod>;
#phy-cells = <0>;
};
usb@47401000 {
compatible = "ti,musb-am33xx";
reg = <0x47401400 0x400>,
<0x47401000 0x200>;
reg-names = "mc", "control";
interrupts = <18>;
interrupt-names = "mc";
dr_mode = "otg";
mentor,multipoint = <1>;
mentor,num-eps = <16>;
mentor,ram-bits = <12>;
mentor,power = <500>;
phys = <&usb0_phy>;
};
dma-controller@7402000 {
compatible = "ti,am3359-cppi41";
reg = <0x47400000 0x1000>,
<0x47402000 0x1000>,
<0x47403000 0x1000>,
<0x47404000 0x4000>;
reg-names = "glue", "controller", "scheduler", "queuemgr";
interrupts = <17>;
interrupt-names = "glue";
#dma-cells = <2>;
dma-channels = <30>;
dma-requests = <256>;
};
};

View File

@ -0,0 +1,113 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/ti,da830-musb.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: TI DA8xx MUSB
maintainers:
- Bin Liu <b-liu@ti.com>
allOf:
- $ref: /schemas/usb/usb-drd.yaml#
properties:
compatible:
const: ti,da830-musb
reg:
maxItems: 1
interrupts:
maxItems: 1
interrupt-names:
const: mc
phys:
maxItems: 1
phy-names:
const: usb-phy
dmas:
minItems: 1
maxItems: 8
dma-names:
minItems: 1
maxItems: 8
items:
pattern: "^(rx|tx)[1-4]$"
vbus-supply:
description: Phandle to a regulator providing the USB bus power.
clocks:
maxItems: 1
clock-ranges: true
ranges: true
"#address-cells":
const: 1
"#size-cells":
const: 1
patternProperties:
"^dma-controller@[0-9a-f]+$":
$ref: /schemas/dma/ti/ti,cppi41.yaml#
required:
- compatible
- reg
- interrupts
- interrupt-names
- phys
- phy-names
- ranges
- "#address-cells"
- "#size-cells"
unevaluatedProperties: false
examples:
- |
usb@200000 {
compatible = "ti,da830-musb";
reg = <0x00200000 0x1000>;
ranges;
#address-cells = <1>;
#size-cells = <1>;
interrupts = <58>;
interrupt-names = "mc";
dr_mode = "host";
vbus-supply = <&usb_vbus>;
phys = <&usb_phy 0>;
phy-names = "usb-phy";
clocks = <&psc1 1>;
clock-ranges;
dmas = <&cppi41dma 0 0>, <&cppi41dma 1 0>,
<&cppi41dma 2 0>, <&cppi41dma 3 0>,
<&cppi41dma 0 1>, <&cppi41dma 1 1>,
<&cppi41dma 2 1>, <&cppi41dma 3 1>;
dma-names = "rx1", "rx2", "rx3", "rx4",
"tx1", "tx2", "tx3", "tx4";
dma-controller@201000 {
compatible = "ti,da830-cppi41";
reg = <0x201000 0x1000>,
<0x202000 0x1000>,
<0x204000 0x4000>;
reg-names = "controller", "scheduler", "queuemgr";
interrupts = <58>;
#dma-cells = <2>;
dma-channels = <4>;
};
};

View File

@ -0,0 +1,61 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/ti,da830-ohci.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: DA8XX USB OHCI controller
maintainers:
- Axel Haslam <ahaslam@baylibre.com>
allOf:
- $ref: usb-hcd.yaml#
properties:
compatible:
const: ti,da830-ohci
reg:
maxItems: 1
interrupts:
maxItems: 1
phys:
maxItems: 1
clocks:
maxItems: 1
phy-names:
const: usb-phy
vbus-supply:
description:
phandle of regulator that controls vbus power / over-current
oc-gpios:
maxItems: 1
required:
- compatible
- reg
- interrupts
- phys
- phy-names
- clocks
unevaluatedProperties: false
examples:
- |
usb@225000 {
compatible = "ti,da830-ohci";
reg = <0x225000 0x1000>;
interrupts = <59>;
phys = <&usb_phy 1>;
phy-names = "usb-phy";
clocks = <&psc1 2>;
vbus-supply = <&reg_usb_ohci>;
};

View File

@ -25,6 +25,15 @@ properties:
interrupts:
maxItems: 1
vcc33-supply:
description: 3.3V supply (VCC33 pin), powering the SuperSpeed 2:1 MUX.
vdd5-supply:
description:
5V supply (VDD5 pin), powering the CC controller and sourcing VCONN.
VDD5 has to be stable for at least tVDD5V_PG (2ms) before VCC33 starts
ramping up, unless ENn_CC is held high while both rails ramp up.
id-gpios:
description:
An input gpio for USB ID pin. Upon detecting a UFP device, HD3SS3220
@ -68,6 +77,8 @@ examples:
reg = <0x47>;
interrupt-parent = <&gpio6>;
interrupts = <3>;
vcc33-supply = <&vcc3v3_control>;
vdd5-supply = <&vcc5v0_device_s0>;
ports {
#address-cells = <1>;

View File

@ -0,0 +1,113 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/ti,musb-am33xx.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: TI AM33xx/DM816 MUSB
maintainers:
- Ravi B <ravibabu@ti.com>
- Ajay Kumar Gupta <ajay.gupta@ti.com>
allOf:
- $ref: /schemas/usb/usb-drd.yaml#
properties:
compatible:
enum:
- ti,musb-am33xx
- ti,musb-dm816
reg:
maxItems: 2
reg-names:
items:
- const: mc
- const: control
interrupts:
minItems: 1
maxItems: 2
interrupt-names:
minItems: 1
items:
- const: mc
- const: vbus
mentor,multipoint:
$ref: /schemas/types.yaml#/definitions/uint32
description: Indicates the musb controller supports multipoint.
enum: [0, 1]
mentor,num-eps:
$ref: /schemas/types.yaml#/definitions/uint32
description: Specifies the number of endpoints.
const: 16
mentor,ram-bits:
description: Specifies the ram address size.
const: 12
mentor,power:
$ref: /schemas/types.yaml#/definitions/uint32
description: Controller supply capacity (in mA) when operating in host mode.
const: 500
phys:
maxItems: 1
dmas:
minItems: 1
maxItems: 30
dma-names:
minItems: 1
maxItems: 30
items:
pattern: "^(rx|tx)([1-9]|1[0-5])$"
phy-names:
const: usb2-phy
interface-type:
$ref: /schemas/types.yaml#/definitions/uint32
description: Describes the type of interface between the controller and the PHY.
enum: [ 0, 1 ]
required:
- compatible
- reg
- reg-names
- interrupts
- interrupt-names
- dr_mode
- mentor,multipoint
- mentor,num-eps
- mentor,ram-bits
- mentor,power
- phys
unevaluatedProperties: false
examples:
- |
usb@47401000 {
compatible = "ti,musb-am33xx";
reg = <0x47401400 0x400>,
<0x47401000 0x200>;
reg-names = "mc", "control";
interrupts = <18>;
interrupt-names = "mc";
dr_mode = "otg";
mentor,multipoint = <1>;
mentor,num-eps = <16>;
mentor,ram-bits = <12>;
mentor,power = <500>;
phys = <&usb0_phy>;
dmas = <&cppi41dma 0 0 &cppi41dma 1 0>;
dma-names = "rx1", "tx1";
};

View File

@ -9,7 +9,7 @@ Supported chips:
Prefix: 'prom21_xhci'
PCI IDs: 1022:43fc, 1022:43fd
PCI IDs: 1022:43f7, 1022:43f9, 1022:43fa, 1022:43fc, 1022:43fd
Author:

View File

@ -361,7 +361,7 @@ Then, execute a loop similar to the one written in pseudo-code below::
ioctl(fd, MON_IOCX_MFETCH, &fetch); // Process errors, too
nflush = fetch.nfetch; // This many packets to flush when done
for (i = 0; i < nflush; i++) {
hdr = (struct ubsmon_packet *) &mmap_area[vec[i]];
hdr = (struct usbmon_packet *) &mmap_area[vec[i]];
if (hdr->type == '@') // Filler packet
continue;
caddr_t data = &mmap_area[vec[i]] + 64;

View File

@ -28514,6 +28514,28 @@ S: Maintained
F: Documentation/usb/ehci.rst
F: drivers/usb/host/ehci*
USB GADGET/PERIPHERAL SUBSYSTEM
M: Neill Kapron <nkapron@google.com>
L: linux-usb@vger.kernel.org
S: Supported
T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git
F: Documentation/ABI/testing/configfs-usb-gadget*
F: Documentation/ABI/testing/sysfs-devices-platform-_UDC_-gadget
F: Documentation/driver-api/usb/gadget.rst
F: Documentation/usb/functionfs*
F: Documentation/usb/gadget*
F: Documentation/usb/mass-storage.rst
F: drivers/usb/gadget/
F: include/linux/usb/composite.h
F: include/linux/usb/func_utils.h
F: include/linux/usb/functionfs.h
F: include/linux/usb/g_hid.h
F: include/linux/usb/gadget*
F: include/uapi/linux/usb/functionfs.h
F: include/uapi/linux/usb/g_*.h
F: include/uapi/linux/usb/gadgetfs.h
F: tools/usb/ffs-*
USB HID/HIDBP DRIVERS (USB KEYBOARDS, MICE, REMOTE CONTROLS, ...)
M: Jiri Kosina <jikos@kernel.org>
M: Benjamin Tissoires <bentiss@kernel.org>

View File

@ -1128,7 +1128,7 @@ crc1: crc@58009000 {
};
usbh_ohci: usb@5800c000 {
compatible = "generic-ohci";
compatible = "st,stm32mp13-ohci", "generic-ohci";
reg = <0x5800c000 0x1000>;
clocks = <&usbphyc>, <&rcc USBH>;
resets = <&rcc USBH_R>;
@ -1137,7 +1137,7 @@ usbh_ohci: usb@5800c000 {
};
usbh_ehci: usb@5800d000 {
compatible = "generic-ehci";
compatible = "st,stm32mp13-ehci", "generic-ehci";
reg = <0x5800d000 0x1000>;
clocks = <&usbphyc>, <&rcc USBH>;
resets = <&rcc USBH_R>;

View File

@ -504,7 +504,7 @@ crc1: crc@58009000 {
};
usbh_ohci: usb@5800c000 {
compatible = "generic-ohci";
compatible = "st,stm32mp15-ohci", "generic-ohci";
reg = <0x5800c000 0x1000>;
clocks = <&usbphyc>, <&rcc USBH>;
resets = <&rcc USBH_R>;
@ -515,7 +515,7 @@ usbh_ohci: usb@5800c000 {
};
usbh_ehci: usb@5800d000 {
compatible = "generic-ehci";
compatible = "st,stm32mp15-ehci", "generic-ehci";
reg = <0x5800d000 0x1000>;
clocks = <&usbphyc>, <&rcc USBH>;
resets = <&rcc USBH_R>;

View File

@ -877,6 +877,48 @@ stmmac_axi_config_1: stmmac-axi-config {
snps,wr_osr_lmt = <0x7>;
};
};
usbh_ohci: usb@482e0000 {
compatible = "st,stm32mp23-ohci", "generic-ohci";
reg = <0x482e0000 0x1000>;
access-controllers = <&rifsc 63>;
clocks = <&usb2_phy1 1>, <&rcc CK_BUS_USB2OHCI>;
interrupts = <GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>;
phys = <&usb2_phy1>;
phy-names = "usb";
resets = <&rcc USBH_R>;
status = "disabled";
};
usbh_ehci: usb@482f0000 {
compatible = "st,stm32mp23-ehci", "generic-ehci";
reg = <0x482f0000 0x1000>;
access-controllers = <&rifsc 63>;
clocks = <&usb2_phy1 0>, <&rcc CK_BUS_USB2EHCI>;
companion = <&usbh_ohci>;
interrupts = <GIC_SPI 139 IRQ_TYPE_LEVEL_HIGH>;
phys = <&usb2_phy1>;
phy-names = "usb";
resets = <&rcc USBH_R>;
status = "disabled";
};
usb3dr: usb@48300000 {
compatible = "st,stm32mp25-dwc3";
reg = <0x48300000 0x100000>;
access-controllers = <&rifsc 66>;
clocks = <&rcc CK_KER_USB2PHY2>,
<&rcc CK_BUS_USB3DR>,
<&rcc CK_KER_USB2PHY2>;
clock-names = "ref", "bus_early", "suspend";
interrupts = <GIC_SPI 228 IRQ_TYPE_LEVEL_HIGH>;
maximum-speed = "high-speed";
phys = <&usb2_phy2>;
phy-names = "usb2-phy";
resets = <&rcc USB3DR_R>;
st,syscfg = <&syscfg 0x4800>;
status = "disabled";
};
};
bsec: efuse@44000000 {
@ -1080,6 +1122,28 @@ syscfg: syscon@44230000 {
compatible = "st,stm32mp23-syscfg", "syscon";
reg = <0x44230000 0x10000>;
#clock-cells = <0>;
#address-cells = <1>;
#size-cells = <1>;
usb2_phy1: phy@2400 {
compatible = "st,stm32mp25-usb2phy1";
reg = <0x2400 0x24>;
#phy-cells = <0>;
#clock-cells = <1>;
clocks = <&rcc CK_KER_USB2PHY1>;
resets = <&rcc USB2PHY1_R>;
status = "disabled";
};
usb2_phy2: phy@2800 {
compatible = "st,stm32mp25-usb2phy2";
reg = <0x2800 0x10>;
#phy-cells = <0>;
#clock-cells = <1>;
clocks = <&rcc CK_KER_USB2PHY2EN>;
resets = <&rcc USB2PHY2_R>;
status = "disabled";
};
};
pinctrl: pinctrl@44240000 {

View File

@ -1762,6 +1762,47 @@ stmmac_axi_config_1: stmmac-axi-config {
};
};
usbh_ohci: usb@482e0000 {
compatible = "st,stm32mp25-ohci", "generic-ohci";
reg = <0x482e0000 0x1000>;
access-controllers = <&rifsc 63>;
clocks = <&usb2_phy1 1>, <&rcc CK_BUS_USB2OHCI>;
interrupts = <GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>;
phys = <&usb2_phy1>;
phy-names = "usb";
resets = <&rcc USBH_R>;
status = "disabled";
};
usbh_ehci: usb@482f0000 {
compatible = "st,stm32mp25-ehci", "generic-ehci";
reg = <0x482f0000 0x1000>;
access-controllers = <&rifsc 63>;
clocks = <&usb2_phy1 0>, <&rcc CK_BUS_USB2EHCI>;
companion = <&usbh_ohci>;
interrupts = <GIC_SPI 139 IRQ_TYPE_LEVEL_HIGH>;
phys = <&usb2_phy1>;
phy-names = "usb";
resets = <&rcc USBH_R>;
status = "disabled";
};
usb3dr: usb@48300000 {
compatible = "st,stm32mp25-dwc3";
reg = <0x48300000 0x100000>;
access-controllers = <&rifsc 66>;
clocks = <&rcc CK_KER_USB2PHY2>,
<&rcc CK_BUS_USB3DR>,
<&rcc CK_KER_USB2PHY2>;
clock-names = "ref", "bus_early", "suspend";
interrupts = <GIC_SPI 228 IRQ_TYPE_LEVEL_HIGH>;
phys = <&usb2_phy2>, <&combophy PHY_TYPE_USB3>;
phy-names = "usb2-phy", "usb3-phy";
resets = <&rcc USB3DR_R>;
st,syscfg = <&syscfg 0x4800>;
status = "disabled";
};
pcie_ep: pcie-ep@48400000 {
compatible = "st,stm32mp25-pcie-ep";
reg = <0x48400000 0x100000>,
@ -2036,6 +2077,28 @@ syscfg: syscon@44230000 {
compatible = "st,stm32mp25-syscfg", "syscon";
reg = <0x44230000 0x10000>;
#clock-cells = <0>;
#address-cells = <1>;
#size-cells = <1>;
usb2_phy1: phy@2400 {
compatible = "st,stm32mp25-usb2phy1";
reg = <0x2400 0x24>;
#phy-cells = <0>;
#clock-cells = <1>;
clocks = <&rcc CK_KER_USB2PHY1>;
resets = <&rcc USB2PHY1_R>;
status = "disabled";
};
usb2_phy2: phy@2800 {
compatible = "st,stm32mp25-usb2phy2";
reg = <0x2800 0x10>;
#phy-cells = <0>;
#clock-cells = <1>;
clocks = <&rcc CK_KER_USB2PHY2EN>;
resets = <&rcc USB2PHY2_R>;
status = "disabled";
};
};
pinctrl: pinctrl@44240000 {

View File

@ -540,11 +540,12 @@ static int tbnet_alloc_rx_buffers(struct tbnet *net, unsigned int nbuffers)
trace_tbnet_alloc_rx_frame(index, tf->page, dma_addr,
DMA_FROM_DEVICE);
tb_ring_rx(ring->ring, &tf->frame);
tb_ring_rx_more(ring->ring, &tf->frame);
ring->prod++;
}
tb_ring_notify(ring->ring);
return 0;
err_free:
@ -1243,7 +1244,8 @@ static netdev_tx_t tbnet_start_xmit(struct sk_buff *skb,
goto err_drop;
for (i = 0; i < frame_index + 1; i++)
tb_ring_tx(net->tx_ring.ring, &frames[i]->frame);
tb_ring_tx_more(net->tx_ring.ring, &frames[i]->frame);
tb_ring_notify(net->tx_ring.ring);
if (net->svc->prtcstns & TBNET_MATCH_FRAGS_ID)
atomic_inc(&net->frame_id);

View File

@ -10474,7 +10474,7 @@ static int __init rtl8152_driver_init(void)
{
int ret;
ret = usb_register_device_driver(&rtl8152_cfgselector_driver, THIS_MODULE);
ret = usb_register_device_driver(&rtl8152_cfgselector_driver);
if (ret)
return ret;

View File

@ -37,6 +37,9 @@
#define COUNTER_SET_LEN 3
#define HW_MARGINING_DWORDS 3
#define SW_MARGINING_DWORDS 4
/*
* USB4 spec doesn't specify dwell range, the range of 100 ms to 500 ms
* probed to give good results.
@ -494,7 +497,7 @@ struct tb_margining {
unsigned int gen;
bool asym_rx;
u32 caps[3];
u32 results[3];
u32 results[5];
enum usb4_margining_lane lanes;
unsigned int min_ber_level;
unsigned int max_ber_level;
@ -612,6 +615,11 @@ supports_optional_voltage_offset_range(const struct tb_margining *margining)
return margining->caps[0] & USB4_MARGIN_CAP_0_OPT_VOLTAGE_SUPPORT;
}
static bool supports_extended_error_counter(const struct tb_margining *margining)
{
return margining->caps[0] & USB4_MARGIN_CAP_0_EXT_ERR_COUNTER;
}
static ssize_t
margining_ber_level_write(struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
@ -701,6 +709,8 @@ static int margining_caps_show(struct seq_file *s, void *not_used)
ber_level_show(s, margining->max_ber_level);
} else {
seq_puts(s, "# hardware margining: no\n");
seq_printf(s, "# extended error counter: %s\n",
str_yes_no(supports_extended_error_counter(margining)));
}
seq_printf(s, "# all lanes simultaneously: %s\n",
@ -1150,36 +1160,91 @@ static int margining_mode_show(struct seq_file *s, void *not_used)
}
DEBUGFS_ATTR_RW(margining_mode);
static u32 margining_get_lane_error(const u32 *results, unsigned int lane, bool extended)
{
u32 error_count;
switch (lane) {
case USB4_MARGINING_LANE_RX0:
error_count = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_0_MASK, results[0]);
break;
case USB4_MARGINING_LANE_RX1:
error_count = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_1_MASK, results[0]);
break;
case USB4_MARGINING_LANE_RX2:
error_count = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_2_MASK, results[0]);
break;
default:
return 0;
}
if (extended)
error_count |= FIELD_PREP(USB4_MARGIN_SW_EXT_ERR_COUNTER_MASK, results[1 + lane]);
return error_count;
}
static int margining_run_sw(struct tb_margining *margining,
struct usb4_port_margining_params *params)
{
bool extended_err = supports_extended_error_counter(margining);
u32 nsamples = margining->dwell_time / DWELL_SAMPLE_INTERVAL;
int ret, i;
u32 dwords;
if (params->error_counter != USB4_MARGIN_SW_ERROR_COUNTER_START)
goto out_stop_or_clear;
if (extended_err)
dwords = SW_MARGINING_DWORDS;
else
dwords = 1;
ret = usb4_port_sw_margin(margining->port, margining->target, margining->index,
params, margining->results);
if (ret)
goto out_stop;
return ret;
for (i = 0; i <= nsamples; i++) {
u32 errors = 0;
ret = usb4_port_sw_margin_errors(margining->port, margining->target,
margining->index, &margining->results[1]);
if (ret)
margining->index, &margining->results[1],
dwords);
if (ret) {
tb_port_warn(margining->port, "failed to read margining error counters\n");
break;
}
if (margining->lanes == USB4_MARGINING_LANE_RX0)
errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_0_MASK,
margining->results[1]);
else if (margining->lanes == USB4_MARGINING_LANE_RX1)
errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_1_MASK,
margining->results[1]);
else if (margining->lanes == USB4_MARGINING_LANE_RX2)
errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_2_MASK,
margining->results[1]);
else if (margining->lanes == USB4_MARGINING_LANE_ALL)
errors = margining->results[1];
if (margining->lanes == USB4_MARGINING_LANE_ALL) {
if (extended_err) {
/* Extended counters with all lanes: check each active lane */
int width = tb_port_get_link_width(margining->port);
unsigned int lane, max_lane = 2;
if (width == TB_LINK_WIDTH_ASYM_TX) {
max_lane = 1;
} else if ((width == TB_LINK_WIDTH_ASYM_RX) && margining->asym_rx) {
max_lane = 3;
} else if (width < 0) {
tb_port_warn(margining->port, "failed to read link width\n");
break;
}
for (lane = 0; lane < max_lane; lane++) {
errors = margining_get_lane_error(&margining->results[1],
lane, extended_err);
if (errors)
break;
}
} else {
/* Standard counters: all lane errors fit in results[1]*/
errors = margining->results[1];
}
} else {
errors = margining_get_lane_error(&margining->results[1],
margining->lanes, extended_err);
}
/* Any errors stop the test */
if (errors)
@ -1187,15 +1252,15 @@ static int margining_run_sw(struct tb_margining *margining,
fsleep(DWELL_SAMPLE_INTERVAL * USEC_PER_MSEC);
}
params->error_counter = USB4_MARGIN_SW_ERROR_COUNTER_STOP;
out_stop:
out_stop_or_clear:
/*
* Stop the counters but don't clear them to allow the
* Stop the counters or clear them as per the
* different error counter configurations.
*/
margining_modify_error_counter(margining, margining->lanes,
USB4_MARGIN_SW_ERROR_COUNTER_STOP);
return ret;
return margining_modify_error_counter(margining, margining->lanes,
params->error_counter);
}
static int validate_margining(struct tb_margining *margining)
@ -1272,7 +1337,7 @@ static int margining_run_write(void *data, u64 val)
if (margining->software) {
struct usb4_port_margining_params params = {
.error_counter = USB4_MARGIN_SW_ERROR_COUNTER_CLEAR,
.error_counter = margining->error_counter,
.lanes = margining->lanes,
.time = margining->time,
.voltage_time_offset = margining->voltage_time_offset,
@ -1303,7 +1368,7 @@ static int margining_run_write(void *data, u64 val)
margining->lanes);
ret = usb4_port_hw_margin(port, margining->target, margining->index, &params,
margining->results, ARRAY_SIZE(margining->results));
margining->results, HW_MARGINING_DWORDS);
}
if (down_sw)
@ -1425,7 +1490,7 @@ static int margining_results_show(struct seq_file *s, void *not_used)
seq_printf(s, "0x%08x\n", margining->results[0]);
/* Only the hardware margining has two result dwords */
if (!margining->software) {
for (int i = 1; i < ARRAY_SIZE(margining->results); i++)
for (int i = 1; i < HW_MARGINING_DWORDS; i++)
seq_printf(s, "0x%08x\n", margining->results[i]);
if (margining->lanes == USB4_MARGINING_LANE_ALL) {
@ -1444,18 +1509,30 @@ static int margining_results_show(struct seq_file *s, void *not_used)
u32 lane_errors, result;
seq_printf(s, "0x%08x\n", margining->results[1]);
if (supports_extended_error_counter(margining)) {
seq_printf(s, "0x%08x\n", margining->results[2]);
seq_printf(s, "0x%08x\n", margining->results[3]);
seq_printf(s, "0x%08x\n", margining->results[4]);
}
result = FIELD_GET(USB4_MARGIN_SW_LANES_MASK, margining->results[0]);
if (result == USB4_MARGINING_LANE_RX0 ||
result == USB4_MARGINING_LANE_ALL) {
lane_errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_0_MASK,
margining->results[1]);
if (supports_extended_error_counter(margining))
lane_errors |= FIELD_PREP(USB4_MARGIN_SW_EXT_ERR_COUNTER_MASK,
margining->results[2]);
seq_printf(s, "# lane 0 errors: %u\n", lane_errors);
}
if (result == USB4_MARGINING_LANE_RX1 ||
result == USB4_MARGINING_LANE_ALL) {
lane_errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_1_MASK,
margining->results[1]);
if (supports_extended_error_counter(margining))
lane_errors |= FIELD_PREP(USB4_MARGIN_SW_EXT_ERR_COUNTER_MASK,
margining->results[3]);
seq_printf(s, "# lane 1 errors: %u\n", lane_errors);
}
if (margining->asym_rx &&
@ -1463,6 +1540,9 @@ static int margining_results_show(struct seq_file *s, void *not_used)
result == USB4_MARGINING_LANE_ALL)) {
lane_errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_2_MASK,
margining->results[1]);
if (supports_extended_error_counter(margining))
lane_errors |= FIELD_PREP(USB4_MARGIN_SW_EXT_ERR_COUNTER_MASK,
margining->results[4]);
seq_printf(s, "# lane 2 errors: %u\n", lane_errors);
}
}

View File

@ -215,11 +215,6 @@ static void dma_test_stop_rings(struct dma_test *dt)
{
int ret;
if (dt->rx_ring)
tb_ring_stop(dt->rx_ring);
if (dt->tx_ring)
tb_ring_stop(dt->tx_ring);
ret = tb_xdomain_disable_paths(dt->xd, dt->tx_hopid,
dt->tx_ring ? dt->tx_ring->hop : -1,
dt->rx_hopid,
@ -227,6 +222,11 @@ static void dma_test_stop_rings(struct dma_test *dt)
if (ret)
dev_warn(&dt->svc->dev, "failed to disable DMA paths\n");
if (dt->rx_ring)
tb_ring_stop(dt->rx_ring);
if (dt->tx_ring)
tb_ring_stop(dt->tx_ring);
dma_test_free_rings(dt);
}

View File

@ -324,7 +324,7 @@ int tb_drom_read_uid_only(struct tb_switch *sw, u64 *uid)
}
static int tb_drom_parse_entry_generic(struct tb_switch *sw,
struct tb_drom_entry_header *header)
const struct tb_drom_entry_header *header)
{
const struct tb_drom_entry_generic *entry =
(const struct tb_drom_entry_generic *)header;
@ -360,7 +360,7 @@ static int tb_drom_parse_entry_generic(struct tb_switch *sw,
}
static int tb_drom_parse_entry_port(struct tb_switch *sw,
struct tb_drom_entry_header *header)
const struct tb_drom_entry_header *header)
{
struct tb_port *port;
int res;
@ -386,11 +386,13 @@ static int tb_drom_parse_entry_port(struct tb_switch *sw,
type &= 0xffffff;
if (type == TB_TYPE_PORT) {
struct tb_drom_entry_port *entry = (void *) header;
const struct tb_drom_entry_port *entry =
(const struct tb_drom_entry_port *)header;
if (header->len != sizeof(*entry)) {
tb_sw_warn(sw,
"port entry has size %#x (expected %#zx)\n",
header->len, sizeof(struct tb_drom_entry_port));
header->len, sizeof(*entry));
return -EIO;
}
port->link_nr = entry->link_nr;
@ -421,9 +423,16 @@ static int tb_drom_parse_entries(struct tb_switch *sw, size_t header_size)
int res;
while (pos < drom_size) {
struct tb_drom_entry_header *entry = (void *) (sw->drom + pos);
if (pos + 1 == drom_size || pos + entry->len > drom_size
|| !entry->len) {
const struct tb_drom_entry_header *entry;
if (drom_size - pos < sizeof(*entry)) {
tb_sw_warn(sw, "DROM buffer overrun\n");
return -EIO;
}
entry = (const struct tb_drom_entry_header *)(sw->drom + pos);
if (entry->len < sizeof(*entry) ||
entry->len > drom_size - pos) {
tb_sw_warn(sw, "DROM buffer overrun\n");
return -EIO;
}

View File

@ -2072,7 +2072,7 @@ static int complete_rpm(struct device *dev, void *data)
{
struct tb_switch *sw = tb_to_switch(dev);
if (sw)
if (sw && sw->tb == data)
complete(&sw->rpm_complete);
return 0;
}
@ -2088,8 +2088,8 @@ static void remove_unplugged_switch(struct tb_switch *sw)
* tb_switch_remove() calls pm_runtime_get_sync() that then waits
* for it.
*/
complete_rpm(&sw->dev, NULL);
bus_for_each_dev(&tb_bus_type, &sw->dev, NULL, complete_rpm);
complete_rpm(&sw->dev, sw->tb);
bus_for_each_dev(&tb_bus_type, &sw->dev, sw->tb, complete_rpm);
tb_switch_remove(sw);
pm_runtime_mark_last_busy(parent);

View File

@ -41,6 +41,7 @@ static bool host_reset = true;
module_param(host_reset, bool, 0444);
MODULE_PARM_DESC(host_reset, "reset USB4 host router (default: true)");
/* Returns absolute bit number of the ring in the interrupt registers */
static int ring_interrupt_index(const struct tb_ring *ring)
{
int bit = ring->hop;
@ -49,24 +50,41 @@ static int ring_interrupt_index(const struct tb_ring *ring)
return bit;
}
static void nhi_mask_interrupt(struct tb_nhi *nhi, int mask, int ring)
static void nhi_mask_interrupt(struct tb_nhi *nhi, u32 mask, int reg_index)
{
if (nhi->quirks & QUIRK_AUTO_CLEAR_INT) {
u32 val;
int offset = reg_index * 4;
u32 val;
val = ioread32(nhi->iobase + REG_RING_INTERRUPT_BASE + ring);
iowrite32(val & ~mask, nhi->iobase + REG_RING_INTERRUPT_BASE + ring);
} else {
iowrite32(mask, nhi->iobase + REG_RING_INTERRUPT_MASK_CLEAR_BASE + ring);
}
/* Use shadow copy instead of reading the register */
val = nhi->interrupt_mask[reg_index] & ~mask;
nhi->interrupt_mask[reg_index] = val;
if (nhi->quirks & QUIRK_AUTO_CLEAR_INT)
iowrite32(val, nhi->iobase + REG_RING_INTERRUPT_BASE + offset);
else
iowrite32(mask, nhi->iobase + REG_RING_INTERRUPT_MASK_CLEAR_BASE + offset);
}
static void nhi_clear_interrupt(struct tb_nhi *nhi, int ring)
static void nhi_unmask_interrupt(struct tb_nhi *nhi, u32 mask, int reg_index)
{
int offset = reg_index * 4;
u32 val;
/* Use shadow copy instead of reading the register */
val = nhi->interrupt_mask[reg_index] | mask;
nhi->interrupt_mask[reg_index] = val;
iowrite32(val, nhi->iobase + REG_RING_INTERRUPT_BASE + offset);
}
static void nhi_clear_interrupt(struct tb_nhi *nhi, int reg_index)
{
int offset = reg_index * 4;
if (nhi->quirks & QUIRK_AUTO_CLEAR_INT)
ioread32(nhi->iobase + REG_RING_NOTIFY_BASE + ring);
ioread32(nhi->iobase + REG_RING_NOTIFY_BASE + offset);
else
iowrite32(~0, nhi->iobase + REG_RING_INT_CLEAR + ring);
iowrite32(~0, nhi->iobase + REG_RING_INT_CLEAR + offset);
}
/*
@ -76,22 +94,17 @@ static void nhi_clear_interrupt(struct tb_nhi *nhi, int ring)
*/
static void ring_interrupt_active(struct tb_ring *ring, bool active)
{
int index = ring_interrupt_index(ring) / 32 * 4;
int reg = REG_RING_INTERRUPT_BASE + index;
int interrupt_bit = ring_interrupt_index(ring) & 31;
int mask = 1 << interrupt_bit;
int interrupt_index = ring_interrupt_index(ring);
int reg_index = interrupt_index / 32;
int reg = REG_RING_INTERRUPT_BASE + reg_index * 4;
int interrupt_bit = interrupt_index % 32;
u32 mask = BIT(interrupt_bit);
u32 old, new;
if (ring->irq > 0) {
u32 step, shift, ivr, misc, itr;
void __iomem *ivr_base;
int auto_clear_bit;
int index;
if (ring->is_tx)
index = ring->hop;
else
index = ring->hop + ring->nhi->hop_count;
/*
* Intel routers support a bit that isn't part of
@ -114,8 +127,8 @@ static void ring_interrupt_active(struct tb_ring *ring, bool active)
ring->nhi->iobase + REG_DMA_MISC);
ivr_base = ring->nhi->iobase + REG_INT_VEC_ALLOC_BASE;
step = index / REG_INT_VEC_ALLOC_REGS * REG_INT_VEC_ALLOC_BITS;
shift = index % REG_INT_VEC_ALLOC_REGS * REG_INT_VEC_ALLOC_BITS;
step = interrupt_index / REG_INT_VEC_ALLOC_REGS * REG_INT_VEC_ALLOC_BITS;
shift = interrupt_index % REG_INT_VEC_ALLOC_REGS * REG_INT_VEC_ALLOC_BITS;
ivr = ioread32(ivr_base + step);
ivr &= ~(REG_INT_VEC_ALLOC_MASK << shift);
if (active)
@ -129,7 +142,7 @@ static void ring_interrupt_active(struct tb_ring *ring, bool active)
ring->vector * 4);
}
old = ioread32(ring->nhi->iobase + reg);
old = ring->nhi->interrupt_mask[reg_index];
if (active)
new = old | mask;
else
@ -139,15 +152,24 @@ static void ring_interrupt_active(struct tb_ring *ring, bool active)
"%s interrupt at register %#x bit %d (%#x -> %#x)\n",
active ? "enabling" : "disabling", reg, interrupt_bit, old, new);
if (new == old)
dev_WARN(ring->nhi->dev, "interrupt for %s %d is already %s\n",
RING_TYPE(ring), ring->hop,
str_enabled_disabled(active));
if (new == old) {
/*
* Rings that are polled mask the interrupt while the
* completions are being advanced (see __ring_interrupt())
* so for those it can already be disabled by the time
* the ring is stopped.
*/
if (active || !ring->start_poll)
dev_WARN(ring->nhi->dev,
"interrupt for %s %d is already %s\n",
RING_TYPE(ring), ring->hop,
str_enabled_disabled(active));
}
if (active)
iowrite32(new, ring->nhi->iobase + reg);
nhi_unmask_interrupt(ring->nhi, mask, reg_index);
else
nhi_mask_interrupt(ring->nhi, mask, index);
nhi_mask_interrupt(ring->nhi, mask, reg_index);
}
/*
@ -160,11 +182,11 @@ void nhi_disable_interrupts(struct tb_nhi *nhi)
int i = 0;
/* disable interrupts */
for (i = 0; i < RING_INTERRUPT_REG_COUNT(nhi); i++)
nhi_mask_interrupt(nhi, ~0, 4 * i);
nhi_mask_interrupt(nhi, ~0, i);
/* clear interrupt status bits */
for (i = 0; i < RING_NOTIFY_REG_COUNT(nhi); i++)
nhi_clear_interrupt(nhi, 4 * i);
nhi_clear_interrupt(nhi, i);
}
/* ring helper methods */
@ -227,12 +249,37 @@ static bool ring_empty(struct tb_ring *ring)
return ring->head == ring->tail;
}
static void __ring_notify(struct tb_ring *ring)
{
lockdep_assert_held(&ring->lock);
if (ring->notify_pending) {
/*
* The doorbell carries the absolute index of the head
* so a single write covers all the descriptors posted
* since the previous one.
*/
if (ring->is_tx)
ring_iowrite_prod(ring, ring->head);
else
ring_iowrite_cons(ring, ring->head);
}
ring->notify_pending = false;
}
/*
* ring_write_descriptors() - post frames from ring->queue to the controller
* @ring: Ring to post the frames to
* @notify: Notify the controller about the posted descriptors
*
* Unless @notify is %true the controller is not notified about the posted
* descriptors and the caller is expected to call tb_ring_notify() once it
* is done queuing frames. This allows batching of frames before
* updating the producer/consumer indices.
*
* ring->lock is held.
*/
static void ring_write_descriptors(struct tb_ring *ring)
static void ring_write_descriptors(struct tb_ring *ring, bool notify)
{
struct ring_frame *frame, *n;
struct ring_desc *descriptor;
@ -256,11 +303,11 @@ static void ring_write_descriptors(struct tb_ring *ring)
descriptor->sof = frame->sof;
}
ring->head = (ring->head + 1) % ring->size;
if (ring->is_tx)
ring_iowrite_prod(ring, ring->head);
else
ring_iowrite_cons(ring, ring->head);
ring->notify_pending = true;
}
if (notify)
__ring_notify(ring);
}
/*
@ -305,7 +352,7 @@ static void ring_work(struct work_struct *work)
}
ring->tail = (ring->tail + 1) % ring->size;
}
ring_write_descriptors(ring);
ring_write_descriptors(ring, true);
invoke_callback:
/* allow callbacks to schedule new work */
@ -324,7 +371,7 @@ static void ring_work(struct work_struct *work)
wake_up(&ring->wait);
}
int __tb_ring_enqueue(struct tb_ring *ring, struct ring_frame *frame)
int __tb_ring_enqueue(struct tb_ring *ring, struct ring_frame *frame, bool more)
{
unsigned long flags;
int ret = 0;
@ -332,7 +379,7 @@ int __tb_ring_enqueue(struct tb_ring *ring, struct ring_frame *frame)
spin_lock_irqsave(&ring->lock, flags);
if (ring->running) {
list_add_tail(&frame->list, &ring->queue);
ring_write_descriptors(ring);
ring_write_descriptors(ring, !more);
} else {
ret = -ESHUTDOWN;
}
@ -341,6 +388,22 @@ int __tb_ring_enqueue(struct tb_ring *ring, struct ring_frame *frame)
}
EXPORT_SYMBOL_GPL(__tb_ring_enqueue);
/**
* tb_ring_notify() - Notify the controller about the queued frames
* @ring: Ring to notify
*
* Notifies the controller about frames that were enqueued using
* tb_ring_tx_more() or tb_ring_rx_more(). Does nothing if there are no
* such frames pending.
*/
void tb_ring_notify(struct tb_ring *ring)
{
guard(spinlock_irqsave)(&ring->lock);
if (ring->running)
__ring_notify(ring);
}
EXPORT_SYMBOL_GPL(tb_ring_notify);
/**
* tb_ring_poll() - Poll one completed frame from the ring
* @ring: Ring to poll
@ -377,6 +440,13 @@ struct ring_frame *tb_ring_poll(struct tb_ring *ring)
}
ring->tail = (ring->tail + 1) % ring->size;
/*
* There is one more slot available so we can push next
* descriptor to the ring. This is needed when the ring
* is created with %RING_FLAG_NO_INTERRUPT.
*/
ring_write_descriptors(ring, true);
}
unlock:
@ -385,19 +455,35 @@ struct ring_frame *tb_ring_poll(struct tb_ring *ring)
}
EXPORT_SYMBOL_GPL(tb_ring_poll);
/**
* tb_ring_poll_pending() - Does the ring have completed frame
* @ring: Ring to check
*
* Can be used to check whether there is a completed frame in the ring
* that next call to tb_ring_poll() returns.
*
* Return: %true if a completed frame is waiting, %false otherwise.
*/
bool tb_ring_poll_pending(struct tb_ring *ring)
{
guard(spinlock_irqsave)(&ring->lock);
if (!ring->running || ring_empty(ring))
return false;
return !!(ring->descriptors[ring->tail].flags & RING_DESC_COMPLETED);
}
EXPORT_SYMBOL_GPL(tb_ring_poll_pending);
static void __ring_interrupt_mask(struct tb_ring *ring, bool mask)
{
int idx = ring_interrupt_index(ring);
int reg = REG_RING_INTERRUPT_BASE + idx / 32 * 4;
int bit = idx % 32;
u32 val;
int interrupt_index = ring_interrupt_index(ring);
int reg_index = interrupt_index / 32;
int interrupt_bit = interrupt_index % 32;
val = ioread32(ring->nhi->iobase + reg);
if (mask)
val &= ~BIT(bit);
nhi_mask_interrupt(ring->nhi, BIT(interrupt_bit), reg_index);
else
val |= BIT(bit);
iowrite32(val, ring->nhi->iobase + reg);
nhi_unmask_interrupt(ring->nhi, BIT(interrupt_bit), reg_index);
}
/* Both @nhi->lock and @ring->lock should be held */
@ -788,6 +874,7 @@ void tb_ring_stop(struct tb_ring *ring)
ring_iowrite32desc(ring, 0, 12);
ring->head = 0;
ring->tail = 0;
ring->notify_pending = false;
ring->running = false;
err:
@ -1182,24 +1269,40 @@ static void nhi_reset(struct tb_nhi *nhi)
static struct tb *nhi_select_cm(struct tb_nhi *nhi)
{
bool linked = false;
struct tb *tb;
/*
* USB4 case is simple. If we got control of any of the
* capabilities, we use software CM.
*/
if (tb_acpi_is_native())
return tb_probe(nhi);
if (!tb_acpi_is_native()) {
/*
* Either firmware based CM is running (we did not get
* control from the firmware) or this is pre-USB4 PC so
* try first firmware CM and then fallback to software CM.
*/
tb = icm_probe(nhi);
if (tb)
return tb;
}
/*
* Either firmware based CM is running (we did not get control
* from the firmware) or this is pre-USB4 PC so try first
* firmware CM and then fallback to software CM.
*/
tb = icm_probe(nhi);
tb = tb_probe(nhi);
if (!tb)
tb = tb_probe(nhi);
return NULL;
if (nhi->ops->add_links)
linked = nhi->ops->add_links(nhi);
if (!linked)
linked = tb_acpi_add_links(nhi);
/*
* Device links are needed to make sure we establish tunnels
* before protocol stacks are resumed so complain here if we
* found them missing.
*/
if (!linked)
dev_warn(nhi->dev,
"device links to tunneled native ports are missing!\n");
return tb;
}
@ -1222,7 +1325,10 @@ int nhi_probe(struct tb_nhi *nhi)
sizeof(*nhi->tx_rings), GFP_KERNEL);
nhi->rx_rings = devm_kcalloc(dev, nhi->hop_count,
sizeof(*nhi->rx_rings), GFP_KERNEL);
if (!nhi->tx_rings || !nhi->rx_rings)
nhi->interrupt_mask = devm_kcalloc(dev, RING_INTERRUPT_REG_COUNT(nhi),
sizeof(*nhi->interrupt_mask),
GFP_KERNEL);
if (!nhi->tx_rings || !nhi->rx_rings || !nhi->interrupt_mask)
return -ENOMEM;
nhi_reset(nhi);

View File

@ -41,6 +41,7 @@ extern const struct dev_pm_ops nhi_pm_ops;
/**
* struct tb_nhi_ops - NHI specific optional operations
* @init: NHI specific initialization
* @add_links: Setup device links for tunneling native protocols
* @suspend_noirq: NHI specific suspend_noirq hook
* @resume_noirq: NHI specific resume_noirq hook
* @runtime_suspend: NHI specific runtime_suspend hook
@ -55,6 +56,7 @@ extern const struct dev_pm_ops nhi_pm_ops;
*/
struct tb_nhi_ops {
int (*init)(struct tb_nhi *nhi);
bool (*add_links)(struct tb_nhi *nhi);
int (*suspend_noirq)(struct tb_nhi *nhi, bool wakeup);
int (*resume_noirq)(struct tb_nhi *nhi);
int (*runtime_suspend)(struct tb_nhi *nhi);

View File

@ -398,7 +398,7 @@ static int __tb_path_deactivate_hop(struct tb_port *port, int hop_index,
return ret;
/* Wait until it is drained */
timeout = ktime_add_ms(ktime_get(), 500);
timeout = ktime_add_ms(ktime_get(), port->pp_timeout_msec);
do {
ret = tb_port_read(port, &hop, TB_CFG_HOPS, 2 * hop_index, 2);
if (ret)

View File

@ -18,6 +18,7 @@
#include <linux/property.h>
#include <linux/string_helpers.h>
#include <linux/suspend.h>
#include <linux/platform_data/x86/apple.h>
#include "nhi.h"
#include "nhi_regs.h"
@ -108,6 +109,82 @@ static void nhi_pci_check_iommu(struct tb_nhi_pci *nhi_pci)
str_enabled_disabled(port_ok));
}
static bool add_link(struct tb_nhi *nhi, struct pci_dev *pdev)
{
const struct device_link *link;
link = device_link_add(&pdev->dev, nhi->dev,
DL_FLAG_AUTOREMOVE_SUPPLIER |
DL_FLAG_PM_RUNTIME);
if (!link) {
dev_warn(nhi->dev, "device link creation from %s failed\n",
dev_name(&pdev->dev));
return false;
}
dev_dbg(nhi->dev, "created link from %s\n", dev_name(&pdev->dev));
return true;
}
/*
* During suspend the Thunderbolt controller is reset and all PCIe
* tunnels are lost. The NHI driver will try to reestablish all tunnels
* during resume. This adds device links between the tunneled PCIe
* downstream ports and the NHI so that the device core will make sure
* NHI is resumed first before the rest.
*/
static bool nhi_pci_add_links(struct tb_nhi *nhi)
{
struct pci_dev *nhi_pdev = to_pci_dev(nhi->dev);
struct pci_dev *upstream, *pdev;
bool ret;
if (!x86_apple_machine)
return false;
switch (nhi_pdev->device) {
case PCI_DEVICE_ID_INTEL_LIGHT_RIDGE:
case PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C:
case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_NHI:
case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI:
case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_NHI:
case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_NHI:
break;
default:
return false;
}
upstream = pci_upstream_bridge(nhi_pdev);
while (upstream) {
if (!pci_is_pcie(upstream))
return false;
if (pci_pcie_type(upstream) == PCI_EXP_TYPE_UPSTREAM)
break;
upstream = pci_upstream_bridge(upstream);
}
if (!upstream)
return false;
/*
* For each hotplug downstream port, create add device link back
* to NHI so that PCIe tunnels can be re-established after
* sleep.
*/
ret = false;
for_each_pci_bridge(pdev, upstream->subordinate) {
if (!pci_is_pcie(pdev))
continue;
if (pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM ||
!pdev->is_pciehp)
continue;
ret |= add_link(nhi, pdev);
}
return ret;
}
static int nhi_pci_init_msi(struct tb_nhi *nhi)
{
struct tb_nhi_pci *nhi_pci = nhi_to_pci(nhi);
@ -251,6 +328,7 @@ static bool nhi_pci_is_present(struct tb_nhi *nhi)
}
static const struct tb_nhi_ops pci_nhi_default_ops = {
.add_links = nhi_pci_add_links,
.pre_nvm_auth = nhi_pci_start_dma_port,
.post_nvm_auth = nhi_pci_complete_dma_port,
.request_ring_irq = nhi_pci_ring_request_msix,
@ -421,6 +499,40 @@ static int icl_nhi_resume(struct tb_nhi *nhi)
return 0;
}
static bool icl_nhi_add_links(struct tb_nhi *nhi)
{
struct pci_dev *nhi_pdev = to_pci_dev(nhi->dev);
struct pci_bus *bus = nhi_pdev->bus;
struct pci_dev *pdev;
bool ret;
if (!x86_apple_machine)
return false;
/*
* On integrated controllers the tunneled PCIe root ports are
* directly under the host bridge.
*/
if (!pci_is_root_bus(bus))
return false;
ret = false;
for_each_pci_bridge(pdev, bus) {
switch (nhi_pdev->device) {
case PCI_DEVICE_ID_INTEL_ICL_NHI0:
if (pdev->device == 0x8a1d || pdev->device == 0x8a1f)
ret |= add_link(nhi, pdev);
break;
case PCI_DEVICE_ID_INTEL_ICL_NHI1:
if (pdev->device == 0x8a21 || pdev->device == 0x8a23)
ret |= add_link(nhi, pdev);
break;
}
}
return ret;
}
static void icl_nhi_shutdown(struct tb_nhi *nhi)
{
nhi_pci_release_irq(nhi);
@ -430,6 +542,7 @@ static void icl_nhi_shutdown(struct tb_nhi *nhi)
static const struct tb_nhi_ops icl_nhi_ops = {
.init = icl_nhi_resume,
.add_links = icl_nhi_add_links,
.suspend_noirq = icl_nhi_suspend_noirq,
.resume_noirq = icl_nhi_resume,
.runtime_suspend = icl_nhi_suspend,

View File

@ -52,6 +52,19 @@ static void quirk_block_rpm_in_redrive(struct tb_switch *sw)
tb_sw_dbg(sw, "preventing runtime PM in DP redrive mode\n");
}
static void quirk_stuck_pending(struct tb_switch *sw)
{
struct tb_port *port;
tb_switch_for_each_port(sw, port) {
if (!tb_port_is_nhi(port))
continue;
port->pp_timeout_msec = 0;
tb_port_dbg(port, "pending bit does not clear, reading it once\n");
}
}
struct tb_quirk {
u16 hw_vendor_id;
u16 hw_device_id;
@ -117,6 +130,11 @@ static const struct tb_quirk tb_quirks[] = {
{ 0x0438, 0x0209, 0x0000, 0x0000, quirk_clx_disable },
{ 0x0438, 0x020a, 0x0000, 0x0000, quirk_clx_disable },
{ 0x0438, 0x020b, 0x0000, 0x0000, quirk_clx_disable },
/*
* ASMedia ASM4242 never clears the Pending Packets bit of its host
* interface adapter.
*/
{ 0x174c, 0x2428, 0x0000, 0x0000, quirk_stuck_pending },
};
/**

View File

@ -59,6 +59,7 @@ enum usb4_sb_opcode {
#define USB4_MARGIN_CAP_0_MAX_VOLTAGE_OFFSET_MASK GENMASK(18, 13)
#define USB4_MARGIN_CAP_0_OPT_VOLTAGE_SUPPORT BIT(19)
#define USB4_MARGIN_CAP_0_VOLT_STEPS_OPT_MASK GENMASK(26, 20)
#define USB4_MARGIN_CAP_0_EXT_ERR_COUNTER BIT(28)
#define USB4_MARGIN_CAP_1_MAX_VOLT_OFS_OPT_MASK GENMASK(7, 0)
#define USB4_MARGIN_CAP_1_TIME_DESTR BIT(8)
#define USB4_MARGIN_CAP_1_TIME_INDP_MASK GENMASK(10, 9)
@ -108,5 +109,6 @@ enum usb4_sb_opcode {
#define USB4_MARGIN_SW_ERR_COUNTER_LANE_0_MASK GENMASK(3, 0)
#define USB4_MARGIN_SW_ERR_COUNTER_LANE_1_MASK GENMASK(7, 4)
#define USB4_MARGIN_SW_ERR_COUNTER_LANE_2_MASK GENMASK(11, 8)
#define USB4_MARGIN_SW_EXT_ERR_COUNTER_MASK GENMASK(15, 4)
#endif

View File

@ -131,14 +131,20 @@ struct tbstream_ring {
* @ring_size: Size of the rings
* @throttling: Interrupt throttling rate in ns
* @busy_poll: Instead of interrupts, busy poll the rings
* @rx_pending: Receive ring has completions that need to be advanced
* @users: Number of times @cdev has been opened
* @closed: CLOSE packet was received
* @close_received: CLOSE packet appeared in the RX ring
* @closed: CLOSE packet was handled in the read side
* @removed: Userspace removed the ConfigFS group underneath.
* @wait: Waitqueue for open, read and write
* @lock: Lock protecting this structure
* @tx_ring: Transmit ring
* @rx_ring: Receive ring
* @list: Stream devices are linked through this
*
* @close_received is used on write side to notify the writer that the
* other side sent CLOSE. @closed on the other hand is used on the read
* side to make read(2) return EOF to the caller.
*/
struct tbstream_dev {
struct config_group group;
@ -151,7 +157,9 @@ struct tbstream_dev {
unsigned int ring_size;
unsigned int throttling;
bool busy_poll;
bool rx_pending;
int users;
bool close_received;
bool closed;
bool removed;
wait_queue_head_t wait;
@ -278,6 +286,14 @@ static inline bool tbstream_ring_available(const struct tbstream_ring *ring)
return ring->prod > ring->cons;
}
static void tbstream_ring_poll(struct tbstream_ring *ring)
{
struct ring_frame *frame;
while ((frame = tb_ring_poll(ring->ring)))
frame->callback(ring->ring, frame, false);
}
static inline struct tb_xdomain *tbstream_dev_xdomain(struct tbstream_dev *sdev)
{
if (sdev->stream)
@ -338,9 +354,19 @@ static inline bool tbstream_dev_removed(const struct tbstream_dev *sdev)
return sdev->removed;
}
static inline bool tbstream_dev_close_received(const struct tbstream_dev *sdev)
{
return READ_ONCE(sdev->close_received);
}
static inline bool tbstream_dev_closed(const struct tbstream_dev *sdev)
{
return sdev->closed;
return READ_ONCE(sdev->closed);
}
static inline bool tbstream_dev_rx_pending(const struct tbstream_dev *sdev)
{
return READ_ONCE(sdev->rx_pending);
}
static void
@ -349,6 +375,7 @@ tbstream_dev_rx_callback(struct tb_ring *ring, struct ring_frame *frame,
{
struct tbstream_frame *sf = container_of(frame, typeof(*sf), frame);
struct tbstream_dev *sdev = sf->sdev;
__poll_t mask;
if (canceled)
return;
@ -356,12 +383,17 @@ tbstream_dev_rx_callback(struct tb_ring *ring, struct ring_frame *frame,
sf->completed = true;
sdev->rx_ring.prod++;
if (sf->frame.flags & RING_DESC_CRC_ERROR)
pr_warn("RX CRC error\n");
else if (sf->frame.flags & RING_DESC_BUFFER_OVERRUN)
pr_warn("RX buffer overrun\n");
else
wake_up_interruptible_poll(&sdev->wait, EPOLLIN | EPOLLRDNORM);
mask = EPOLLIN | EPOLLRDNORM;
/*
* The CLOSE packet does not have a payload so the flags do not
* matter. The read_iter() deals with the flags.
*/
if (sf->frame.eof == TBSTREAM_CLOSE) {
WRITE_ONCE(sdev->close_received, true);
mask |= EPOLLHUP;
}
wake_up_interruptible_poll(&sdev->wait, mask);
}
static struct tbstream_frame *
@ -538,38 +570,27 @@ tbstream_dev_send_data(struct tbstream_dev *sdev, struct iov_iter *from,
return tb_ring_tx(sdev->tx_ring.ring, &sf->frame);
}
static void
tbstream_dev_poll_ring(struct tbstream_dev *sdev, struct tbstream_ring *ring)
{
struct ring_frame *frame;
if (!sdev->busy_poll)
return;
while ((frame = tb_ring_poll(ring->ring)))
frame->callback(ring->ring, frame, false);
}
static int tbstream_dev_send_close(struct tbstream_dev *sdev)
{
struct tbstream_frame *sf;
ktime_t timeout;
if (sdev->busy_poll) {
/*
* Wait for the ring to have available slots before we send the
* CLOSE packet.
*/
timeout = ktime_add_ms(ktime_get(), 500);
do {
if (tbstream_ring_available(&sdev->tx_ring))
break;
/*
* When busy polling it's the write(2) path that
* advances the completions so it is possible that the
* ring is full at this point. Advance the ring here so
* that there is room for the CLOSE packet to be sent.
* For busy polling we need to advance the ring here as
* well to make the slots available.
*/
ktime_t timeout = ktime_add_ms(ktime_get(), 500);
do {
if (tbstream_ring_available(&sdev->tx_ring))
break;
tbstream_dev_poll_ring(sdev, &sdev->tx_ring);
fsleep(15);
} while (ktime_before(ktime_get(), timeout));
}
if (sdev->busy_poll)
tbstream_ring_poll(&sdev->tx_ring);
fsleep(15);
} while (ktime_before(ktime_get(), timeout));
sf = tbstream_dev_alloc_tx(sdev, TBSTREAM_CLOSE, NULL, SZ_256);
if (IS_ERR(sf))
@ -577,16 +598,45 @@ static int tbstream_dev_send_close(struct tbstream_dev *sdev)
return tb_ring_tx(sdev->tx_ring.ring, &sf->frame);
}
static void tbstream_dev_start_poll(void *data)
{
struct tbstream_dev *sdev = data;
WRITE_ONCE(sdev->rx_pending, true);
wake_up_interruptible_poll(&sdev->wait, EPOLLIN | EPOLLRDNORM);
}
/* sdev->lock must be held */
static void tbstream_dev_advance_rx(struct tbstream_dev *sdev)
{
/*
* Clear before running the completions so that an interrupt
* that arrives while we are doing that is not missed.
*/
WRITE_ONCE(sdev->rx_pending, false);
tbstream_ring_poll(&sdev->rx_ring);
}
/* sdev->lock must be held */
static void tbstream_dev_complete_rx(struct tbstream_dev *sdev)
{
if (!sdev->busy_poll)
tb_ring_poll_complete(sdev->rx_ring.ring);
}
static int tbstream_dev_start(struct tbstream_dev *sdev)
{
struct tb_xdomain *xd = tbstream_dev_xdomain(sdev);
unsigned int flags = RING_FLAG_FRAME | RING_FLAG_E2E;
void (*start_poll)(void *) = NULL;
u16 sof_mask, eof_mask;
struct tb_ring *ring;
int ret, e2e_tx_hop;
if (sdev->busy_poll)
flags |= RING_FLAG_NO_INTERRUPT;
else
start_poll = tbstream_dev_start_poll;
ring = tb_ring_alloc_tx(xd->tb->nhi, -1, sdev->ring_size, flags);
if (!ring)
@ -602,7 +652,8 @@ static int tbstream_dev_start(struct tbstream_dev *sdev)
eof_mask = BIT(TBSTREAM_DATA) | BIT(TBSTREAM_CLOSE);
ring = tb_ring_alloc_rx(xd->tb->nhi, -1, sdev->ring_size, flags,
e2e_tx_hop, sof_mask, eof_mask, NULL, NULL);
e2e_tx_hop, sof_mask, eof_mask, start_poll,
sdev);
if (!ring) {
ret = -ENOMEM;
goto err_free_tx_buffers;
@ -619,6 +670,8 @@ static int tbstream_dev_start(struct tbstream_dev *sdev)
tb_ring_throttling(sdev->tx_ring.ring, sdev->throttling);
tb_ring_throttling(sdev->rx_ring.ring, sdev->throttling);
sdev->rx_pending = false;
tb_ring_start(sdev->tx_ring.ring);
tb_ring_start(sdev->rx_ring.ring);
@ -665,19 +718,16 @@ static void tbstream_dev_stop(struct tbstream_dev *sdev)
do {
if (tbstream_dev_tx_drained(sdev))
break;
tbstream_dev_poll_ring(sdev, &sdev->tx_ring);
tbstream_ring_poll(&sdev->tx_ring);
fsleep(15);
} while (ktime_before(ktime_get(), timeout));
tb_ring_stop(sdev->tx_ring.ring);
tb_ring_stop(sdev->rx_ring.ring);
} else {
tb_ring_flush(sdev->tx_ring.ring, 500);
tb_ring_stop(sdev->tx_ring.ring);
tb_ring_flush(sdev->rx_ring.ring, 500);
tb_ring_stop(sdev->rx_ring.ring);
}
tb_ring_stop(sdev->tx_ring.ring);
tb_ring_stop(sdev->rx_ring.ring);
xd = tbstream_dev_xdomain(sdev);
if (xd) {
tb_xdomain_disable_paths(xd, sdev->out_hopid,
@ -707,6 +757,45 @@ static int tbstream_dev_lock(struct tbstream_dev *sdev, bool nowait)
return 0;
}
/* Must not be called with @sdev->lock held */
static int tbstream_dev_busy_poll_wait(struct tbstream_dev *sdev,
struct tbstream_ring *ring)
{
for (;;) {
if (signal_pending(current))
return -ERESTARTSYS;
if (tb_ring_poll_pending(ring->ring))
return 0;
/*
* For TX ring we need to check the RX side too because
* it might have received CLOSE packet.
*/
if (ring == &sdev->tx_ring &&
tb_ring_poll_pending(sdev->rx_ring.ring))
return 0;
if (tbstream_dev_valid(sdev) != 0 ||
tbstream_dev_closed(sdev) || tbstream_dev_removed(sdev))
return 0;
cond_resched();
}
}
static bool
tbstream_dev_has_event(struct tbstream_dev *sdev, struct tbstream_ring *ring)
{
if (tbstream_dev_valid(sdev) != 0)
return true;
if (tbstream_dev_closed(sdev))
return true;
if (tbstream_dev_removed(sdev))
return true;
if (tbstream_dev_rx_pending(sdev))
return true;
if (tbstream_ring_available(ring))
return true;
return tb_ring_poll_pending(sdev->rx_ring.ring);
}
static ssize_t
tbstream_dev_fops_read_iter(struct kiocb *kiocb, struct iov_iter *to)
{
@ -725,8 +814,8 @@ tbstream_dev_fops_read_iter(struct kiocb *kiocb, struct iov_iter *to)
return ret;
for (;;) {
/* When busy polling, advance any completions manually */
tbstream_dev_poll_ring(sdev, &sdev->rx_ring);
/* Advance RX completions */
tbstream_dev_advance_rx(sdev);
ret = tbstream_dev_valid(sdev);
if (ret) {
@ -742,21 +831,20 @@ tbstream_dev_fops_read_iter(struct kiocb *kiocb, struct iov_iter *to)
if (tbstream_ring_available(&sdev->rx_ring))
break;
/* Polled all we could. Re-enable the interrupt now. */
tbstream_dev_complete_rx(sdev);
mutex_unlock(&sdev->lock);
if (nowait)
return -EAGAIN;
if (sdev->busy_poll) {
if (signal_pending(current))
return -ERESTARTSYS;
cond_resched();
ret = tbstream_dev_busy_poll_wait(sdev, &sdev->rx_ring);
if (ret)
return ret;
} else {
ret = wait_event_interruptible(sdev->wait,
tbstream_ring_available(&sdev->rx_ring) ||
tbstream_dev_valid(sdev) != 0 ||
tbstream_dev_closed(sdev) ||
tbstream_dev_removed(sdev));
tbstream_dev_has_event(sdev, &sdev->rx_ring));
if (ret)
return ret;
}
@ -783,7 +871,20 @@ tbstream_dev_fops_read_iter(struct kiocb *kiocb, struct iov_iter *to)
if (sf->frame.eof == TBSTREAM_CLOSE) {
if (!nbytes) {
tbstream_dev_consume_rx(sdev);
sdev->closed = true;
WRITE_ONCE(sdev->closed, true);
}
break;
} else if (sf->frame.flags &
(RING_DESC_CRC_ERROR | RING_DESC_BUFFER_OVERRUN)) {
/*
* If something was already read return that now
* and next read will report the error.
*/
if (!nbytes) {
pr_warn("corrupted frame received, flags %#x\n",
sf->frame.flags);
tbstream_dev_consume_rx(sdev);
ret = -EIO;
}
break;
}
@ -819,6 +920,25 @@ tbstream_dev_fops_read_iter(struct kiocb *kiocb, struct iov_iter *to)
return nbytes;
}
static void tbstream_dev_advance_both(struct tbstream_dev *sdev)
{
/*
* When busy polling, advance TX completions manually.
*
* We also need to advance the RX side in both modes to be able
* to receive CLOSE packet from the other peer if there is no
* reader.
*/
if (sdev->busy_poll) {
tbstream_ring_poll(&sdev->tx_ring);
tbstream_dev_advance_rx(sdev);
} else if (tbstream_dev_rx_pending(sdev) ||
tb_ring_poll_pending(sdev->rx_ring.ring)) {
tbstream_dev_advance_rx(sdev);
tbstream_dev_complete_rx(sdev);
}
}
static ssize_t
tbstream_dev_fops_write_iter(struct kiocb *kiocb, struct iov_iter *from)
{
@ -837,7 +957,8 @@ tbstream_dev_fops_write_iter(struct kiocb *kiocb, struct iov_iter *from)
return ret;
for (;;) {
tbstream_dev_poll_ring(sdev, &sdev->tx_ring);
/* Advance TX (and RX) completions */
tbstream_dev_advance_both(sdev);
ret = tbstream_dev_valid(sdev);
if (ret) {
@ -845,7 +966,12 @@ tbstream_dev_fops_write_iter(struct kiocb *kiocb, struct iov_iter *from)
return ret;
}
if (tbstream_dev_closed(sdev) || tbstream_dev_removed(sdev)) {
if (tbstream_dev_close_received(sdev)) {
mutex_unlock(&sdev->lock);
return -EPIPE;
}
if (tbstream_dev_removed(sdev)) {
mutex_unlock(&sdev->lock);
return -ENXIO;
}
@ -859,15 +985,13 @@ tbstream_dev_fops_write_iter(struct kiocb *kiocb, struct iov_iter *from)
return -EAGAIN;
if (sdev->busy_poll) {
if (signal_pending(current))
return -ERESTARTSYS;
cond_resched();
ret = tbstream_dev_busy_poll_wait(sdev, &sdev->tx_ring);
if (ret)
return ret;
} else {
ret = wait_event_interruptible(sdev->wait,
tbstream_ring_available(&sdev->tx_ring) ||
tbstream_dev_valid(sdev) != 0 ||
tbstream_dev_closed(sdev) ||
tbstream_dev_removed(sdev));
tbstream_dev_has_event(sdev, &sdev->tx_ring) ||
tbstream_dev_close_received(sdev));
if (ret)
return ret;
}
@ -917,14 +1041,28 @@ tbstream_dev_fops_poll(struct file *file, struct poll_table_struct *wait)
poll_wait(file, &sdev->wait, wait);
guard(mutex)(&sdev->lock);
if (tbstream_dev_valid(sdev) != 0) {
mask |= EPOLLHUP | EPOLLERR;
if (tbstream_dev_valid(sdev) != 0)
return EPOLLHUP | EPOLLERR;
/*
* The RX completions are only advanced from here and from
* read(2) so do that now, otherwise we would never report
* anything to be available.
*/
tbstream_dev_advance_rx(sdev);
if (tbstream_dev_close_received(sdev))
mask |= EPOLLHUP;
if (tbstream_ring_available(&sdev->tx_ring))
mask |= EPOLLOUT | EPOLLWRNORM;
if (tbstream_ring_available(&sdev->rx_ring)) {
mask |= EPOLLIN | EPOLLRDNORM;
} else {
if (tbstream_ring_available(&sdev->tx_ring))
mask |= EPOLLOUT | EPOLLWRNORM;
if (tbstream_ring_available(&sdev->rx_ring))
tbstream_dev_complete_rx(sdev);
if (tb_ring_poll_pending(sdev->rx_ring.ring))
mask |= EPOLLIN | EPOLLRDNORM;
}
return mask;
}
@ -974,6 +1112,8 @@ static int tbstream_dev_fops_open(struct inode *inode, struct file *file)
sdev->users--;
goto err_unlock;
}
sdev->close_received = false;
sdev->closed = false;
}
@ -998,11 +1138,18 @@ static int tbstream_dev_fops_release(struct inode *inode, struct file *file)
mutex_lock(&sdev->lock);
if (--sdev->users == 0) {
/*
* Send CLOSE tunneled packet to notify the other end
* that we are closing the file. We do this twice if the
* first one fails.
* Advance now in case there is CLOSE waiting in the RX
* ring.
*/
tbstream_dev_send_close(sdev);
tbstream_dev_advance_both(sdev);
/*
* Send CLOSE tunneled packet to notify the other end
* that we are closing the file, if it is not closed
* already.
*/
if (!tbstream_dev_close_received(sdev) &&
tbstream_dev_valid(sdev) == 0)
tbstream_dev_send_close(sdev);
tbstream_dev_stop(sdev);
}
mutex_unlock(&sdev->lock);

View File

@ -19,6 +19,9 @@
#include "tb.h"
/* How long a hop is given to drain when a path is deactivated */
#define TB_PORT_PENDING_TIMEOUT 500 /* ms */
/* Switch NVM support */
struct nvm_auth_status {
@ -712,6 +715,7 @@ static int tb_init_port(struct tb_port *port)
int cap;
INIT_LIST_HEAD(&port->list);
port->pp_timeout_msec = TB_PORT_PENDING_TIMEOUT;
/* Control adapter does not have configuration space */
if (!port->port)

View File

@ -10,7 +10,6 @@
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/pm_runtime.h>
#include <linux/platform_data/x86/apple.h>
#include <linux/stringify.h>
#include "tb.h"
@ -3335,75 +3334,6 @@ static const struct tb_cm_ops tb_cm_ops = {
.disconnect_xdomain_paths = tb_disconnect_xdomain_paths,
};
/*
* During suspend the Thunderbolt controller is reset and all PCIe
* tunnels are lost. The NHI driver will try to reestablish all tunnels
* during resume. This adds device links between the tunneled PCIe
* downstream ports and the NHI so that the device core will make sure
* NHI is resumed first before the rest.
*/
static bool tb_apple_add_links(struct tb_nhi *nhi)
{
struct pci_dev *nhi_pdev = to_pci_dev(nhi->dev);
struct pci_dev *upstream, *pdev;
bool ret;
if (!x86_apple_machine)
return false;
switch (nhi_pdev->device) {
case PCI_DEVICE_ID_INTEL_LIGHT_RIDGE:
case PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C:
case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_NHI:
case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI:
break;
default:
return false;
}
upstream = pci_upstream_bridge(nhi_pdev);
while (upstream) {
if (!pci_is_pcie(upstream))
return false;
if (pci_pcie_type(upstream) == PCI_EXP_TYPE_UPSTREAM)
break;
upstream = pci_upstream_bridge(upstream);
}
if (!upstream)
return false;
/*
* For each hotplug downstream port, create add device link
* back to NHI so that PCIe tunnels can be re-established after
* sleep.
*/
ret = false;
for_each_pci_bridge(pdev, upstream->subordinate) {
const struct device_link *link;
if (!pci_is_pcie(pdev))
continue;
if (pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM ||
!pdev->is_pciehp)
continue;
link = device_link_add(&pdev->dev, nhi->dev,
DL_FLAG_AUTOREMOVE_SUPPLIER |
DL_FLAG_PM_RUNTIME);
if (link) {
dev_dbg(nhi->dev, "created link from %s\n",
dev_name(&pdev->dev));
ret = true;
} else {
dev_warn(nhi->dev, "device link creation from %s failed\n",
dev_name(&pdev->dev));
}
}
return ret;
}
struct tb *tb_probe(struct tb_nhi *nhi)
{
struct tb_cm *tcm;
@ -3428,13 +3358,5 @@ struct tb *tb_probe(struct tb_nhi *nhi)
tb_dbg(tb, "using software connection manager\n");
/*
* Device links are needed to make sure we establish tunnels
* before the PCIe/USB stack is resumed so complain here if we
* found them missing.
*/
if (!tb_apple_add_links(nhi) && !tb_acpi_add_links(nhi))
tb_warn(tb, "device links to tunneled native ports are missing!\n");
return tb;
}

View File

@ -273,6 +273,8 @@ struct tb_bandwidth_group {
* @max_bw: Maximum possible bandwidth through this adapter if set to
* non-zero.
* @redrive: For DP IN, if true the adapter is in redrive mode.
* @pp_timeout_msec: How long a hop of this adapter is given to drain when a
* path is deactivated. %0 means a single read.
*
* In USB4 terminology this structure represents an adapter (protocol or
* lane adapter).
@ -302,6 +304,7 @@ struct tb_port {
struct list_head group_list;
unsigned int max_bw;
bool redrive;
unsigned int pp_timeout_msec;
};
/**
@ -1439,7 +1442,7 @@ int usb4_port_sw_margin(struct tb_port *port, enum usb4_sb_target target,
u8 index, const struct usb4_port_margining_params *params,
u32 *results);
int usb4_port_sw_margin_errors(struct tb_port *port, enum usb4_sb_target target,
u8 index, u32 *errors);
u8 index, u32 *errors, size_t dwords);
int usb4_port_retimer_set_inbound_sbtx(struct tb_port *port, u8 index);
int usb4_port_retimer_unset_inbound_sbtx(struct tb_port *port, u8 index);

View File

@ -1826,23 +1826,27 @@ int usb4_port_sw_margin(struct tb_port *port, enum usb4_sb_target target,
* @target: Sideband target
* @index: Retimer index if target is %USB4_SB_TARGET_RETIMER
* @errors: Error metadata is copied here.
* @dwords: Number of dwords to read error counter values
*
* This reads back the software margining error counters from the port.
*
* Return: %0 on success, negative errno otherwise.
*/
int usb4_port_sw_margin_errors(struct tb_port *port, enum usb4_sb_target target,
u8 index, u32 *errors)
u8 index, u32 *errors, size_t dwords)
{
int ret;
u8 reg;
ret = usb4_port_sb_op(port, target, index,
USB4_SB_OPCODE_READ_SW_MARGIN_ERR, 150);
if (ret)
return ret;
return usb4_port_sb_read(port, target, index, USB4_SB_METADATA, errors,
sizeof(*errors));
reg = (dwords > 1) ? USB4_SB_DATA : USB4_SB_METADATA;
return usb4_port_sb_read(port, target, index, reg, errors,
sizeof(*errors) * dwords);
}
static inline int usb4_port_retimer_op(struct tb_port *port, u8 index,

View File

@ -449,10 +449,8 @@ int cdns_init(struct cdns *cdns)
IRQF_SHARED,
dev_name(cdns->dev), cdns);
if (ret) {
dev_err(cdns->dev, "couldn't register wakeup irq handler\n");
if (ret)
goto role_switch_unregister;
}
}
ret = cdns_drd_init(cdns);

View File

@ -509,10 +509,8 @@ int cdns_drd_init(struct cdns *cdns)
cdns_drd_thread_irq,
IRQF_SHARED,
dev_name(cdns->dev), cdns);
if (ret) {
dev_err(cdns->dev, "couldn't get otg_irq\n");
if (ret)
return ret;
}
state = readl(&cdns->otg_regs->sts);
if (OTGSTS_OTG_NRDY(state)) {

View File

@ -525,11 +525,15 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
data->supports_runtime_pm = true;
data->wakeup_irq = platform_get_irq_optional(pdev, 1);
if (data->wakeup_irq < 0 && data->wakeup_irq != -ENXIO) {
ret = data->wakeup_irq;
goto phy_shutdown;
}
if (data->wakeup_irq > 0) {
irq_name = devm_kasprintf(dev, GFP_KERNEL, "%s:wakeup", pdata.name);
if (!irq_name) {
ret = dev_err_probe(dev, -ENOMEM, "failed to create irq_name\n");
goto err_clk;
goto phy_shutdown;
}
ret = devm_request_threaded_irq(dev, data->wakeup_irq,
@ -537,7 +541,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
IRQF_ONESHOT | IRQF_NO_AUTOEN,
irq_name, data);
if (ret)
goto err_clk;
goto phy_shutdown;
}
ret = imx_usbmisc_init(data->usbmisc_data);

View File

@ -705,7 +705,7 @@ static void ci_otg_fsm_event(struct ci_hdrc *ci)
if ((intr_sts & USBi_PCI) && !port_conn) {
fsm->b_conn = 0;
/* if gadget driver is binded */
/* if gadget driver is bound */
if (ci->driver) {
/* A device to be peripheral mode */
ci->gadget.is_a_peripheral = 1;

View File

@ -1862,7 +1862,7 @@ static int ci_udc_get_frame(struct usb_gadget *_gadget)
}
/*
* ci_hdrc_gadget_connect: caller makes sure gadget driver is binded
* ci_hdrc_gadget_connect: caller makes sure gadget driver is bound
*/
static void ci_hdrc_gadget_connect(struct usb_gadget *_gadget, int is_active)
{

View File

@ -782,6 +782,7 @@ static void acm_port_shutdown(struct tty_port *port)
urb = usb_get_from_anchor(&acm->delayed);
if (!urb)
break;
usb_put_urb(urb);
wb = urb->context;
wb->use = false;
usb_autopm_put_interface_async(acm->control);
@ -1710,7 +1711,7 @@ static int acm_resume(struct usb_interface *intf)
urb = usb_get_from_anchor(&acm->delayed);
if (!urb)
break;
usb_put_urb(urb);
acm_start_wb(acm, urb->context);
}
@ -1818,6 +1819,9 @@ static const struct usb_device_id acm_ids[] = {
{ USB_DEVICE(0x11ca, 0x0201), /* VeriFone Mx870 Gadget Serial */
.driver_info = SINGLE_RX_URB,
},
{ USB_DEVICE(0x11ca, 0x0219), /* VeriFone Vx820 Gadget Serial */
.driver_info = SINGLE_RX_URB,
},
{ USB_DEVICE(0x1901, 0x0006), /* GE Healthcare Patient Monitor UI Controller */
.driver_info = DISABLE_ECHO, /* DISABLE ECHO in termios flag */
},

View File

@ -147,15 +147,17 @@ static const struct device_type ulpi_dev_type = {
* __ulpi_register_driver - register a driver with the ULPI bus
* @drv: driver being registered
* @module: ends up being THIS_MODULE
* @mod_name: ends up being KBUILD_MODNAME
*
* Registers a driver with the ULPI bus.
*/
int __ulpi_register_driver(struct ulpi_driver *drv, struct module *module)
int __ulpi_register_driver(struct ulpi_driver *drv, struct module *module, const char *mod_name)
{
if (!drv->probe)
return -EINVAL;
drv->driver.owner = module;
drv->driver.mod_name = mod_name;
drv->driver.bus = &ulpi_bus;
return driver_register(&drv->driver);

View File

@ -251,10 +251,8 @@ static int usb_conn_probe(struct platform_device *pdev)
ret = devm_request_threaded_irq(dev, info->id_irq, NULL,
usb_conn_isr, USB_CONN_IRQF,
pdev->name, info);
if (ret < 0) {
dev_err(dev, "failed to request ID IRQ\n");
if (ret < 0)
goto put_role_sw;
}
}
if (info->vbus_gpiod) {
@ -268,10 +266,8 @@ static int usb_conn_probe(struct platform_device *pdev)
ret = devm_request_threaded_irq(dev, info->vbus_irq, NULL,
usb_conn_isr, USB_CONN_IRQF,
pdev->name, info);
if (ret < 0) {
dev_err(dev, "failed to request VBUS IRQ\n");
if (ret < 0)
goto put_role_sw;
}
}
platform_set_drvdata(pdev, info);

View File

@ -410,7 +410,6 @@ static int usb_probe_interface(struct device *dev)
return error;
err:
usb_set_intfdata(intf, NULL);
intf->needs_remote_wakeup = 0;
intf->condition = USB_INTERFACE_UNBOUND;
@ -497,7 +496,6 @@ static int usb_unbind_interface(struct device *dev)
} else {
intf->needs_altsetting0 = 1;
}
usb_set_intfdata(intf, NULL);
intf->condition = USB_INTERFACE_UNBOUND;
intf->needs_remote_wakeup = 0;
@ -644,6 +642,7 @@ void usb_driver_release_interface(struct usb_driver *driver,
} else {
device_lock(dev);
usb_unbind_interface(dev);
dev_set_drvdata(dev, NULL);
dev->driver = NULL;
device_unlock(dev);
}
@ -984,9 +983,10 @@ bool is_usb_device_driver(const struct device_driver *drv)
}
/**
* usb_register_device_driver - register a USB device (not interface) driver
* __usb_register_device_driver - register a USB device (not interface) driver
* @new_udriver: USB operations for the device driver
* @owner: module owner of this driver.
* @mod_name: module name string
*
* Registers a USB device driver with the USB core. The list of
* unattached devices will be rescanned whenever a new driver is
@ -994,8 +994,8 @@ bool is_usb_device_driver(const struct device_driver *drv)
*
* Return: A negative error code on failure and 0 on success.
*/
int usb_register_device_driver(struct usb_device_driver *new_udriver,
struct module *owner)
int __usb_register_device_driver(struct usb_device_driver *new_udriver,
struct module *owner, const char *mod_name)
{
int retval = 0;
@ -1007,6 +1007,7 @@ int usb_register_device_driver(struct usb_device_driver *new_udriver,
new_udriver->driver.probe = usb_probe_device;
new_udriver->driver.remove = usb_unbind_device;
new_udriver->driver.owner = owner;
new_udriver->driver.mod_name = mod_name;
new_udriver->driver.dev_groups = new_udriver->dev_groups;
retval = driver_register(&new_udriver->driver);
@ -1027,7 +1028,7 @@ int usb_register_device_driver(struct usb_device_driver *new_udriver,
return retval;
}
EXPORT_SYMBOL_GPL(usb_register_device_driver);
EXPORT_SYMBOL_GPL(__usb_register_device_driver);
/**
* usb_deregister_device_driver - unregister a USB device (not interface) driver

View File

@ -2416,7 +2416,7 @@ static void announce_device_ids(struct usb_device *udev)
u16 bcdDevice = le16_to_cpu(udev->descriptor.bcdDevice);
dev_info(&udev->dev,
"New USB device found, idVendor=%04x, idProduct=%04x, bcdDevice=%2x.%02x\n",
"New USB device found: idVendor=%04x, idProduct=%04x, bcdDevice=%x.%02x\n",
le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct),
bcdDevice >> 8, bcdDevice & 0xff);

View File

@ -2208,6 +2208,11 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
pm_runtime_no_callbacks(&intf->dev);
dev_set_name(&intf->dev, "%d-%s:%d.%d", dev->bus->busnum,
dev->devpath, configuration, ifnum);
/*
* Make sure the interface does not outlive its parent (e.g.
* as needed for usb_queue_reset_device() and
* usb_if_device_type->need_parent_lock).
*/
usb_get_dev(dev);
}
kfree(new_interfaces);

View File

@ -612,6 +612,9 @@ static const struct usb_device_id usb_quirk_list[] = {
/* ezcap401 - BOS descriptor fetch hangs at SuperSpeed Plus */
{ USB_DEVICE(0x32ed, 0x0401), .driver_info = USB_QUIRK_NO_BOS },
/* GameSir G7 SE */
{ USB_DEVICE(0x3537, 0x10a0), .driver_info = USB_QUIRK_WINDOWS_CONFIG_REQ_SIZE },
/* DELL USB GEN2 */
{ USB_DEVICE(0x413c, 0xb062), .driver_info = USB_QUIRK_NO_LPM | USB_QUIRK_RESET_RESUME },

View File

@ -254,6 +254,14 @@ EXPORT_SYMBOL_GPL(usb_urb_ep_type_check);
* request. The completion handler may then immediately free or reuse that
* URB.
*
* The USB core makes no guarantees at all about the context in which
* the complete() callback is invoked. It may be in process context,
* interrupt context, BH context, or anything else. Also, local interrupts
* may be enabled or disabled. To deal with this, the callback routine
* must always use the _irqsave and _irqrestore variants of things like
* spin_lock(), and it must always use GFP_ATOMIC for its memory
* allocation flags.
*
* With few exceptions, USB device drivers should never access URB fields
* provided by usbcore or the HCD until its complete() is called.
* The exceptions relate to periodic transfer scheduling. For both
@ -951,8 +959,8 @@ EXPORT_SYMBOL_GPL(usb_wait_anchor_empty_timeout);
* usb_get_from_anchor - get an anchor's oldest urb
* @anchor: the anchor whose urb you want
*
* This will take the oldest urb from an anchor,
* unanchor and return it
* This will take the oldest urb from an anchor, unanchor and return it with
* a reference held.
*
* Return: The oldest urb from @anchor, or %NULL if @anchor has no
* urbs associated with it.

View File

@ -1241,7 +1241,7 @@ static int __init usb_init(void)
retval = usb_hub_init();
if (retval)
goto hub_init_failed;
retval = usb_register_device_driver(&usb_generic_driver, THIS_MODULE);
retval = usb_register_device_driver(&usb_generic_driver);
if (!retval)
goto out;

View File

@ -173,7 +173,7 @@ static void dwc2_restore_essential_regs(struct dwc2_hsotg *hsotg, int rmode,
/* Umnask global Interrupt in GAHBCFG and restore it */
dwc2_writel(hsotg, gr->gahbcfg | GAHBCFG_GLBL_INTR_EN, GAHBCFG);
/* Clear all pending interupts */
/* Clear all pending interrupts */
dwc2_writel(hsotg, 0xffffffff, GINTSTS);
/* Unmask restore done interrupt */

View File

@ -334,7 +334,7 @@ enum dwc2_ep0_state {
* (default when phy_type is Full Speed)
* @oc_disable: Flag to disable overcurrent condition.
* 0 - Allow overcurrent condition to get detected
* 1 - Disable overcurrent condtion to get detected
* 1 - Disable overcurrent condition to get detected
* @ts_dline: Enable Term Select Dline pulsing
* 0 - No (default)
* 1 - Yes

View File

@ -9,6 +9,7 @@
#include <linux/spinlock.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include "core.h"
@ -787,8 +788,7 @@ int dwc2_debugfs_init(struct dwc2_hsotg *hsotg)
/* Add gadget debugfs nodes */
dwc2_hsotg_create_debug(hsotg);
hsotg->regset = devm_kzalloc(hsotg->dev, sizeof(*hsotg->regset),
GFP_KERNEL);
hsotg->regset = kzalloc_obj(*hsotg->regset, GFP_KERNEL);
if (!hsotg->regset) {
ret = -ENOMEM;
goto err;
@ -810,4 +810,6 @@ void dwc2_debugfs_exit(struct dwc2_hsotg *hsotg)
{
debugfs_remove_recursive(hsotg->debug_root);
hsotg->debug_root = NULL;
kfree(hsotg->regset);
hsotg->regset = NULL;
}

View File

@ -658,7 +658,7 @@ static int dwc2_hsotg_write_fifo(struct dwc2_hsotg *hsotg,
}
/**
* get_ep_limit - get the maximum data legnth for this endpoint
* get_ep_limit - get the maximum data length for this endpoint
* @hs_ep: The endpoint
*
* Return the maximum data that can be queued in one go on a given endpoint
@ -1780,6 +1780,7 @@ static int dwc2_hsotg_process_req_feature(struct dwc2_hsotg *hsotg,
struct dwc2_hsotg_ep *ep;
int ret;
bool halted;
u32 otgctl;
u32 recip;
u32 wValue;
u32 wIndex;
@ -1809,6 +1810,36 @@ static int dwc2_hsotg_process_req_feature(struct dwc2_hsotg *hsotg,
hsotg->test_mode = wIndex >> 8;
break;
case USB_DEVICE_B_HNP_ENABLE:
if (!hsotg->params.otg_caps.hnp_support)
return -ENOENT;
if (!set)
return -EINVAL;
otgctl = dwc2_readl(hsotg, GOTGCTL);
otgctl |= GOTGCTL_DEVHNPEN;
dwc2_writel(hsotg, otgctl, GOTGCTL);
hsotg->gadget.b_hnp_enable = 1;
dev_dbg(hsotg->dev, "HNP enabled\n");
break;
case USB_DEVICE_A_HNP_SUPPORT:
if (!hsotg->params.otg_caps.hnp_support)
return -ENOENT;
if (!set)
return -EINVAL;
hsotg->gadget.a_hnp_support = 1;
dev_dbg(hsotg->dev, "a_hnp_support set\n");
break;
case USB_DEVICE_A_ALT_HNP_SUPPORT:
if (!hsotg->params.otg_caps.hnp_support)
return -ENOENT;
if (!set)
return -EINVAL;
hsotg->gadget.a_alt_hnp_support = 1;
dev_dbg(hsotg->dev, "a_alt_hnp_support set\n");
break;
default:
return -ENOENT;
}
@ -2267,6 +2298,7 @@ static void dwc2_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size)
int to_read;
int max_req;
int read_ptr;
u32 drain;
if (!hs_req) {
u32 epctl = dwc2_readl(hsotg, DOEPCTL(ep_idx));
@ -2286,30 +2318,46 @@ static void dwc2_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size)
to_read = size;
read_ptr = hs_req->req.actual;
max_req = hs_req->req.length - read_ptr;
drain = 0;
dev_dbg(hsotg->dev, "%s: read %d/%d, done %d/%d\n",
__func__, to_read, max_req, read_ptr, hs_req->req.length);
if (to_read > max_req) {
/*
* more data appeared than we where willing
* to deal with in this request.
* More data appeared than we were willing to deal with in
* this request. Keep only what fits in the request buffer
* and discard the rest from the FIFO, instead of overwriting
* bytes past the request buffer (a 64-byte packet into a
* request with one byte left used to over-write 63 bytes past
* its end).
*/
/* currently we don't deal this */
WARN_ON_ONCE(1);
dev_dbg(hsotg->dev, "%s: packet %d > request space %d, dropping %d\n",
__func__, to_read, max_req, to_read - max_req);
drain = DIV_ROUND_UP(to_read - max_req, 4);
to_read = max_req;
}
hs_ep->total_data += to_read;
hs_req->req.actual += to_read;
to_read = DIV_ROUND_UP(to_read, 4);
/*
* note, we might over-write the buffer end by 3 bytes depending on
* alignment of the data.
* Copy word-at-a-time so the request buffer is exactly filled and
* never over-run by the 4-byte rounding of the previous FIFO bulk
* read (which could also emit up to 3 bytes past the buffer end).
*/
dwc2_readl_rep(hsotg, EPFIFO(ep_idx),
hs_req->req.buf + read_ptr, to_read);
while (to_read > 0) {
u32 word = dwc2_readl(hsotg, EPFIFO(ep_idx));
unsigned int chunk = min_t(unsigned int, to_read, 4);
memcpy(hs_req->req.buf + read_ptr, &word, chunk);
read_ptr += chunk;
to_read -= chunk;
}
/* drain the discarded bytes so the next FIFO event is a fresh packet */
while (drain-- > 0)
(void)dwc2_readl(hsotg, EPFIFO(ep_idx));
}
/**
@ -3322,6 +3370,13 @@ void dwc2_hsotg_disconnect(struct dwc2_hsotg *hsotg)
hsotg->connected = 0;
hsotg->test_mode = 0;
if (hsotg->params.otg_caps.hnp_support) {
hsotg->gadget.b_hnp_enable = 0;
hsotg->gadget.a_hnp_support = 0;
hsotg->gadget.a_alt_hnp_support = 0;
dwc2_clear_bit(hsotg, GOTGCTL, GOTGCTL_DEVHNPEN);
}
/* all endpoints should be shutdown */
for (ep = 0; ep < hsotg->num_of_eps; ep++) {
if (hsotg->eps_in[ep])
@ -3416,9 +3471,18 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
usbcfg &= ~GUSBCFG_TOUTCAL_MASK;
usbcfg |= GUSBCFG_TOUTCAL(7);
/* remove the HNP/SRP and set the PHY */
/*
* Configure HNP/SRP capability from params (same idea as
* dwc2_gusbcfg_init() for host). Unconditionally clearing these
* bits leaves an HNP-capable OTG gadget unable to negotiate.
*/
usbcfg &= ~(GUSBCFG_SRPCAP | GUSBCFG_HNPCAP);
dwc2_writel(hsotg, usbcfg, GUSBCFG);
if (hsotg->params.otg_caps.hnp_support &&
hsotg->params.otg_caps.srp_support)
usbcfg |= GUSBCFG_HNPCAP;
if (hsotg->params.otg_caps.srp_support)
usbcfg |= GUSBCFG_SRPCAP;
dwc2_writel(hsotg, usbcfg, GUSBCFG);
dwc2_phy_init(hsotg, true);
@ -5053,10 +5117,8 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg)
ret = devm_request_irq(hsotg->dev, hsotg->irq, dwc2_hsotg_irq,
IRQF_SHARED, dev_name(hsotg->dev), hsotg);
if (ret < 0) {
dev_err(dev, "cannot claim IRQ for gadget\n");
if (ret < 0)
return ret;
}
/* hsotg->num_of_eps holds number of EPs other than ep0 */
@ -5501,7 +5563,7 @@ int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg,
dwc2_hib_restore_common(hsotg, rem_wakeup, 0);
if (!reset) {
/* Clear all pending interupts */
/* Clear all pending interrupts */
dwc2_writel(hsotg, 0xffffffff, GINTSTS);
}
@ -5550,7 +5612,7 @@ int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg,
}
/* Wait for interrupts which must be cleared */
mdelay(2);
/* Clear all pending interupts */
/* Clear all pending interrupts */
dwc2_writel(hsotg, 0xffffffff, GINTSTS);
/* Restore global registers */

View File

@ -5655,7 +5655,7 @@ int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup,
*/
mdelay(100);
/* Clear all pending interupts */
/* Clear all pending interrupts */
dwc2_writel(hsotg, 0xffffffff, GINTSTS);
/* De-assert Restore */
@ -5725,7 +5725,7 @@ int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup,
hprt0 = dwc2_readl(hsotg, HPRT0);
/* Clear all pending interupts */
/* Clear all pending interrupts */
dwc2_writel(hsotg, 0xffffffff, GINTSTS);
/* Restore global registers */

View File

@ -963,7 +963,7 @@ static void dwc2_complete_isoc_xfer_ddma(struct dwc2_hsotg *hsotg,
* whether some of the descriptors (frames) succeeded or not.
* Pass error code to completion routine as well, to update
* urb->status, some of class drivers might use it to stop
* queing transfer requests.
* queuing transfer requests.
*/
int err = halt_status == DWC2_HC_XFER_AHB_ERR ?
-EIO : -EOVERFLOW;

View File

@ -367,14 +367,10 @@ static ssize_t dwc3_lsp_write(struct file *file, const char __user *ubuf,
struct seq_file *s = file->private_data;
struct dwc3 *dwc = s->private;
unsigned long flags;
char buf[32] = { 0 };
u32 sel;
int ret;
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
ret = kstrtouint(buf, 0, &sel);
ret = kstrtou32_from_user(ubuf, count, 0, &sel);
if (ret)
return ret;

View File

@ -235,10 +235,8 @@ static int dwc3_google_request_irq(struct dwc3_google *google, struct platform_d
dwc3_google_resume_irq,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
req_name, google);
if (ret < 0) {
dev_err(google->dev, "failed to request irq %s\n", req_name);
if (ret < 0)
return ret;
}
return irq;
}

View File

@ -250,7 +250,7 @@ static int dwc3_imx_probe(struct platform_device *pdev)
IRQF_ONESHOT | IRQF_NO_AUTOEN,
irq_name, dwc_imx);
if (ret)
return dev_err_probe(dev, ret, "failed to request IRQ #%d\n", irq);
return ret;
ret = device_add_software_node(dev, &dwc3_imx_swnode);
if (ret)

View File

@ -273,10 +273,8 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev)
err = devm_request_threaded_irq(dev, irq, NULL, dwc3_imx8mp_interrupt,
IRQF_ONESHOT, dev_name(dev), dwc3_imx);
if (err) {
dev_err(dev, "failed to request IRQ #%d --> %d\n", irq, err);
if (err)
goto put_dwc3;
}
device_set_wakeup_capable(dev, true);
pm_runtime_put(dev);

View File

@ -144,11 +144,8 @@ static int kdwc3_probe(struct platform_device *pdev)
error = devm_request_irq(dev, irq, dwc3_keystone_interrupt, IRQF_SHARED,
dev_name(dev), kdwc);
if (error) {
dev_err(dev, "failed to request IRQ #%d --> %d\n",
irq, error);
if (error)
goto err_irq;
}
kdwc3_enable_irqs(kdwc);

View File

@ -518,11 +518,8 @@ static int dwc3_omap_probe(struct platform_device *pdev)
ret = devm_request_threaded_irq(dev, omap->irq, dwc3_omap_interrupt,
dwc3_omap_interrupt_thread, IRQF_SHARED,
"dwc3-omap", omap);
if (ret) {
dev_err(dev, "failed to request IRQ #%d --> %d\n",
omap->irq, ret);
if (ret)
goto err2;
}
dwc3_omap_enable_irqs(omap);
return 0;

View File

@ -16,6 +16,11 @@ if USB_FOTG210
config USB_FOTG210_HCD
bool "Faraday FOTG210 USB Host Controller support"
depends on USB=y || USB=USB_FOTG210
depends on USB_EHCI_HCD=y || USB_EHCI_HCD=USB_FOTG210
select USB_EHCI_DEVIANT_PORT_STATUS_REG
select USB_EHCI_PORT_RESET_HOOKS
select USB_EHCI_PORT_SPEED_HOOK
select USB_EHCI_ROOT_HUB_TT
help
Faraday FOTG210 is an OTG controller which can be configured as
an USB2.0 host. It is designed to meet USB2.0 EHCI specification

View File

@ -1,37 +1,50 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Central probing code for the FOTG210 dual role driver
* We register one driver for the hardware and then we decide
* whether to proceed with probing the host or the peripheral
* driver.
* Central probing code for the FOTG210 dual-role controller.
*
* The role is selected once at probe time. The driver does not attempt to
* switch between the host and peripheral blocks while it is running.
*/
#include <linux/bitops.h>
#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/iopoll.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/string_choices.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
#include <linux/usb/otg.h>
#include "fotg210.h"
/* Role Register 0x80 */
#define FOTG210_RR 0x80
#define FOTG210_RR_ID BIT(21) /* 1 = B-device, 0 = A-device */
#define FOTG210_RR_CROLE BIT(20) /* 1 = device, 0 = host */
/* OTG control/status register. */
#define FOTG210_OTGCSR 0x80
#define FOTG210_OTGCSR_ID BIT(21)
#define FOTG210_OTGCSR_CROLE BIT(20)
#define FOTG210_OTGCSR_A_VBUS_VLD BIT(19)
#define FOTG210_OTGCSR_A_BUS_DROP BIT(5)
#define FOTG210_OTGCSR_A_BUS_REQ BIT(4)
#define FOTG210_OTGCSR_B_HNP_EN BIT(1)
/*
* Gemini-specific initialization function, only executed on the
* Gemini SoC using the global misc control register.
*
* The gemini USB blocks are connected to either Mini-A (host mode) or
* Mini-B (peripheral mode) plugs. There is no role switch support on the
* Gemini SoC, just either-or.
*/
/* OTG interrupt status and enable registers. */
#define FOTG210_OTGISR 0x84
#define FOTG210_OTGIEN 0x88
/* Global interrupt mask register; set bits mask the corresponding source. */
#define FOTG210_GINTM 0xc4
#define FOTG210_GINTM_INT_POLARITY BIT(3)
#define FOTG210_GINTM_MHC_INT BIT(2)
#define FOTG210_GINTM_MOTG_INT BIT(1)
#define FOTG210_GINTM_MDEV_INT BIT(0)
/* Gemini global miscellaneous-control register. */
#define GEMINI_GLOBAL_MISC_CTRL 0x30
#define GEMINI_MISC_USB0_WAKEUP BIT(14)
#define GEMINI_MISC_USB1_WAKEUP BIT(15)
@ -40,94 +53,133 @@
#define GEMINI_MISC_USB0_MINI_B BIT(29)
#define GEMINI_MISC_USB1_MINI_B BIT(30)
static int fotg210_gemini_init(struct fotg210 *fotg, struct resource *res,
enum usb_dr_mode mode)
static int fotg210_gemini_init(struct fotg210 *fotg)
{
struct device *dev = fotg->dev;
struct device_node *np = dev->of_node;
struct regmap *map;
bool wakeup;
bool wakeup = of_property_read_bool(np, "wakeup-source");
u32 mask, val;
int ret;
map = syscon_regmap_lookup_by_phandle(np, "syscon");
if (IS_ERR(map))
return dev_err_probe(dev, PTR_ERR(map), "no syscon\n");
fotg->map = map;
wakeup = of_property_read_bool(np, "wakeup-source");
fotg->map = syscon_regmap_lookup_by_phandle(np, "syscon");
if (IS_ERR(fotg->map))
return dev_err_probe(dev, PTR_ERR(fotg->map), "no syscon\n");
/*
* Figure out if this is USB0 or USB1 by simply checking the
* physical base address.
*/
mask = 0;
if (res->start == 0x69000000) {
if (fotg->res->start == 0x69000000) {
fotg->port = GEMINI_PORT_1;
mask = GEMINI_MISC_USB1_VBUS_ON | GEMINI_MISC_USB1_MINI_B |
GEMINI_MISC_USB1_WAKEUP;
if (mode == USB_DR_MODE_HOST)
val = GEMINI_MISC_USB1_VBUS_ON;
else
val = GEMINI_MISC_USB1_MINI_B;
GEMINI_MISC_USB1_WAKEUP;
val = fotg->mode == USB_DR_MODE_HOST ?
GEMINI_MISC_USB1_VBUS_ON : GEMINI_MISC_USB1_MINI_B;
if (wakeup)
val |= GEMINI_MISC_USB1_WAKEUP;
} else {
fotg->port = GEMINI_PORT_0;
mask = GEMINI_MISC_USB0_VBUS_ON | GEMINI_MISC_USB0_MINI_B |
GEMINI_MISC_USB0_WAKEUP;
if (mode == USB_DR_MODE_HOST)
val = GEMINI_MISC_USB0_VBUS_ON;
else
val = GEMINI_MISC_USB0_MINI_B;
GEMINI_MISC_USB0_WAKEUP;
val = fotg->mode == USB_DR_MODE_HOST ?
GEMINI_MISC_USB0_VBUS_ON : GEMINI_MISC_USB0_MINI_B;
if (wakeup)
val |= GEMINI_MISC_USB0_WAKEUP;
}
ret = regmap_update_bits(map, GEMINI_GLOBAL_MISC_CTRL, mask, val);
if (ret) {
dev_err(dev, "failed to initialize Gemini PHY\n");
return ret;
}
ret = regmap_update_bits(fotg->map, GEMINI_GLOBAL_MISC_CTRL,
mask, val);
if (ret)
return dev_err_probe(dev, ret, "failed to initialize Gemini PHY\n");
dev_info(dev, "initialized Gemini PHY in %s mode\n",
(mode == USB_DR_MODE_HOST) ? "host" : "gadget");
fotg->mode == USB_DR_MODE_HOST ? "host" : "gadget");
return 0;
}
/**
* fotg210_vbus() - Called by gadget driver to enable/disable VBUS
* @fotg: pointer to a private fotg210 object
* @enable: true to enable VBUS, false to disable VBUS
* fotg210_vbus() - enable or disable the A-device VBUS supply
* @fotg: controller state
* @enable: whether to drive VBUS
*/
void fotg210_vbus(struct fotg210 *fotg, bool enable)
{
u32 mask;
u32 mask = 0;
u32 val;
int ret;
val = readl(fotg->base + FOTG210_OTGCSR);
if (enable) {
val &= ~FOTG210_OTGCSR_A_BUS_DROP;
val |= FOTG210_OTGCSR_A_BUS_REQ;
} else {
val &= ~FOTG210_OTGCSR_A_BUS_REQ;
val |= FOTG210_OTGCSR_A_BUS_DROP;
}
writel(val, fotg->base + FOTG210_OTGCSR);
switch (fotg->port) {
case GEMINI_PORT_0:
mask = GEMINI_MISC_USB0_VBUS_ON;
val = enable ? GEMINI_MISC_USB0_VBUS_ON : 0;
break;
case GEMINI_PORT_1:
mask = GEMINI_MISC_USB1_VBUS_ON;
val = enable ? GEMINI_MISC_USB1_VBUS_ON : 0;
break;
default:
return;
case GEMINI_PORT_NONE:
break;
}
ret = regmap_update_bits(fotg->map, GEMINI_GLOBAL_MISC_CTRL, mask, val);
if (mask) {
ret = regmap_update_bits(fotg->map, GEMINI_GLOBAL_MISC_CTRL,
mask, enable ? mask : 0);
if (ret) {
dev_err(fotg->dev, "failed to %s VBUS\n",
str_enable_disable(enable));
return;
}
}
ret = readl_poll_timeout(fotg->base + FOTG210_OTGCSR, val,
enable == !!(val & FOTG210_OTGCSR_A_VBUS_VLD),
1000, 500000);
if (ret)
dev_err(fotg->dev, "failed to %s VBUS\n",
str_enable_disable(enable));
dev_info(fotg->dev, "%s: %s VBUS\n", __func__, str_enable_disable(enable));
dev_warn(fotg->dev, "timeout waiting for VBUS to %s\n",
str_enable_disable(enable));
}
static void fotg210_init_host(struct fotg210 *fotg)
{
u32 val;
/* This driver keeps a fixed A-host role and does not negotiate HNP. */
val = readl(fotg->base + FOTG210_OTGCSR);
val &= ~FOTG210_OTGCSR_B_HNP_EN;
writel(val, fotg->base + FOTG210_OTGCSR);
/* Mask peripheral and OTG sources, enable the host source, active high. */
writel(FOTG210_GINTM_MDEV_INT | FOTG210_GINTM_MOTG_INT |
FOTG210_GINTM_INT_POLARITY, fotg->base + FOTG210_GINTM);
writel(0, fotg->base + FOTG210_OTGIEN);
writel(readl(fotg->base + FOTG210_OTGISR),
fotg->base + FOTG210_OTGISR);
/* Vendor trees cycle the A bus before starting the host controller. */
fotg210_vbus(fotg, false);
usleep_range(10000, 12000);
fotg210_vbus(fotg, true);
usleep_range(10000, 12000);
}
static void fotg210_init_peripheral(struct fotg210 *fotg)
{
/* Mask host and OTG sources, enable the device source, active high. */
writel(FOTG210_GINTM_MHC_INT | FOTG210_GINTM_MOTG_INT |
FOTG210_GINTM_INT_POLARITY, fotg->base + FOTG210_GINTM);
writel(0, fotg->base + FOTG210_OTGIEN);
writel(readl(fotg->base + FOTG210_OTGISR),
fotg->base + FOTG210_OTGISR);
}
static int fotg210_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
enum usb_dr_mode mode;
struct reset_control *rst;
struct fotg210 *fotg;
u32 val;
int ret;
@ -141,62 +193,111 @@ static int fotg210_probe(struct platform_device *pdev)
if (IS_ERR(fotg->base))
return PTR_ERR(fotg->base);
fotg->pclk = devm_clk_get_optional_enabled(dev, "PCLK");
fotg->pclk = devm_clk_get_enabled(dev, "PCLK");
if (IS_ERR(fotg->pclk))
return PTR_ERR(fotg->pclk);
return dev_err_probe(dev, PTR_ERR(fotg->pclk),
"failed to enable PCLK\n");
mode = usb_get_dr_mode(dev);
rst = devm_reset_control_get_optional_exclusive(dev, NULL);
if (IS_ERR(rst))
return dev_err_probe(dev, PTR_ERR(rst),
"failed to get reset\n");
ret = reset_control_reset(rst);
if (ret)
return dev_err_probe(dev, ret,
"failed to reset controller\n");
val = readl(fotg->base + FOTG210_OTGCSR);
fotg->mode = usb_get_dr_mode(dev);
/* Keep the historical fixed-host default for old device trees. */
if (fotg->mode == USB_DR_MODE_UNKNOWN)
fotg->mode = USB_DR_MODE_HOST;
if (fotg->mode != USB_DR_MODE_HOST &&
fotg->mode != USB_DR_MODE_PERIPHERAL)
return dev_err_probe(dev, -EINVAL,
"dr_mode must select host or peripheral\n");
if (of_device_is_compatible(dev->of_node, "cortina,gemini-usb")) {
ret = fotg210_gemini_init(fotg, fotg->res, mode);
fotg->is_fotg210 = true;
ret = fotg210_gemini_init(fotg);
if (ret)
return ret;
} else if (of_device_is_compatible(dev->of_node, "faraday,fotg210")) {
fotg->is_fotg210 = true;
}
val = readl(fotg->base + FOTG210_RR);
if (mode == USB_DR_MODE_PERIPHERAL) {
if (!(val & FOTG210_RR_CROLE))
dev_err(dev, "block not in device role\n");
ret = fotg210_udc_probe(pdev, fotg);
} else {
if (val & FOTG210_RR_CROLE)
dev_err(dev, "block not in host role\n");
ret = fotg210_hcd_probe(pdev, fotg);
if (fotg->mode == USB_DR_MODE_PERIPHERAL) {
if (!(val & FOTG210_OTGCSR_CROLE))
dev_warn(dev, "controller does not report device role\n");
if (!(val & FOTG210_OTGCSR_ID))
dev_warn(dev, "controller does not report B-device role\n");
fotg210_init_peripheral(fotg);
return fotg210_udc_probe(pdev, fotg);
}
if (val & FOTG210_OTGCSR_CROLE)
dev_warn(dev, "controller does not report host role\n");
if (val & FOTG210_OTGCSR_ID)
dev_warn(dev, "controller does not report A-device role\n");
fotg210_init_host(fotg);
ret = fotg210_hcd_probe(pdev, fotg);
if (ret)
fotg210_vbus(fotg, false);
return ret;
}
static void fotg210_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
enum usb_dr_mode mode;
mode = usb_get_dr_mode(dev);
if (mode == USB_DR_MODE_PERIPHERAL)
/* The subdrivers own drvdata, so recover the fixed role from firmware. */
if (usb_get_dr_mode(&pdev->dev) == USB_DR_MODE_PERIPHERAL)
fotg210_udc_remove(pdev);
else
fotg210_hcd_remove(pdev);
}
#ifdef CONFIG_OF
static void fotg210_shutdown(struct platform_device *pdev)
{
if (IS_ENABLED(CONFIG_USB_FOTG210_HCD) &&
usb_get_dr_mode(&pdev->dev) != USB_DR_MODE_PERIPHERAL)
usb_hcd_platform_shutdown(pdev);
}
static int fotg210_suspend(struct device *dev)
{
if (usb_get_dr_mode(dev) == USB_DR_MODE_PERIPHERAL)
return 0;
return fotg210_hcd_suspend(dev);
}
static int fotg210_resume(struct device *dev)
{
if (usb_get_dr_mode(dev) == USB_DR_MODE_PERIPHERAL)
return 0;
return fotg210_hcd_resume(dev);
}
static DEFINE_SIMPLE_DEV_PM_OPS(fotg210_pm_ops, fotg210_suspend,
fotg210_resume);
static const struct of_device_id fotg210_of_match[] = {
{ .compatible = "faraday,fotg200" },
{ .compatible = "faraday,fotg210" },
/* TODO: can we also handle FUSB220? */
{},
};
MODULE_DEVICE_TABLE(of, fotg210_of_match);
#endif
static struct platform_driver fotg210_driver = {
.driver = {
.name = "fotg210",
.of_match_table = of_match_ptr(fotg210_of_match),
.name = "fotg210",
.of_match_table = fotg210_of_match,
.pm = pm_sleep_ptr(&fotg210_pm_ops),
},
.probe = fotg210_probe,
.probe = fotg210_probe,
.remove = fotg210_remove,
.shutdown = fotg210_shutdown,
};
static int __init fotg210_init(void)

File diff suppressed because it is too large Load Diff

View File

@ -1,689 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __LINUX_FOTG210_H
#define __LINUX_FOTG210_H
#include <linux/usb/ehci-dbgp.h>
/* definitions used for the EHCI driver */
/*
* __hc32 and __hc16 are "Host Controller" types, they may be equivalent to
* __leXX (normally) or __beXX (given FOTG210_BIG_ENDIAN_DESC), depending on
* the host controller implementation.
*
* To facilitate the strongest possible byte-order checking from "sparse"
* and so on, we use __leXX unless that's not practical.
*/
#define __hc32 __le32
#define __hc16 __le16
/* statistics can be kept for tuning/monitoring */
struct fotg210_stats {
/* irq usage */
unsigned long normal;
unsigned long error;
unsigned long iaa;
unsigned long lost_iaa;
/* termination of urbs from core */
unsigned long complete;
unsigned long unlink;
};
/* fotg210_hcd->lock guards shared data against other CPUs:
* fotg210_hcd: async, unlink, periodic (and shadow), ...
* usb_host_endpoint: hcpriv
* fotg210_qh: qh_next, qtd_list
* fotg210_qtd: qtd_list
*
* Also, hold this lock when talking to HC registers or
* when updating hw_* fields in shared qh/qtd/... structures.
*/
#define FOTG210_MAX_ROOT_PORTS 1 /* see HCS_N_PORTS */
/*
* fotg210_rh_state values of FOTG210_RH_RUNNING or above mean that the
* controller may be doing DMA. Lower values mean there's no DMA.
*/
enum fotg210_rh_state {
FOTG210_RH_HALTED,
FOTG210_RH_SUSPENDED,
FOTG210_RH_RUNNING,
FOTG210_RH_STOPPING
};
/*
* Timer events, ordered by increasing delay length.
* Always update event_delays_ns[] and event_handlers[] (defined in
* ehci-timer.c) in parallel with this list.
*/
enum fotg210_hrtimer_event {
FOTG210_HRTIMER_POLL_ASS, /* Poll for async schedule off */
FOTG210_HRTIMER_POLL_PSS, /* Poll for periodic schedule off */
FOTG210_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */
FOTG210_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */
FOTG210_HRTIMER_FREE_ITDS, /* Wait for unused iTDs and siTDs */
FOTG210_HRTIMER_ASYNC_UNLINKS, /* Unlink empty async QHs */
FOTG210_HRTIMER_IAA_WATCHDOG, /* Handle lost IAA interrupts */
FOTG210_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */
FOTG210_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */
FOTG210_HRTIMER_IO_WATCHDOG, /* Check for missing IRQs */
FOTG210_HRTIMER_NUM_EVENTS /* Must come last */
};
#define FOTG210_HRTIMER_NO_EVENT 99
struct fotg210_hcd { /* one per controller */
/* timing support */
enum fotg210_hrtimer_event next_hrtimer_event;
unsigned enabled_hrtimer_events;
ktime_t hr_timeouts[FOTG210_HRTIMER_NUM_EVENTS];
struct hrtimer hrtimer;
int PSS_poll_count;
int ASS_poll_count;
int died_poll_count;
/* glue to PCI and HCD framework */
struct fotg210_caps __iomem *caps;
struct fotg210_regs __iomem *regs;
struct ehci_dbg_port __iomem *debug;
__u32 hcs_params; /* cached register copy */
spinlock_t lock;
enum fotg210_rh_state rh_state;
/* general schedule support */
bool scanning:1;
bool need_rescan:1;
bool intr_unlinking:1;
bool async_unlinking:1;
bool shutdown:1;
struct fotg210_qh *qh_scan_next;
/* async schedule support */
struct fotg210_qh *async;
struct fotg210_qh *dummy; /* For AMD quirk use */
struct fotg210_qh *async_unlink;
struct fotg210_qh *async_unlink_last;
struct fotg210_qh *async_iaa;
unsigned async_unlink_cycle;
unsigned async_count; /* async activity count */
/* periodic schedule support */
#define DEFAULT_I_TDPS 1024 /* some HCs can do less */
unsigned periodic_size;
__hc32 *periodic; /* hw periodic table */
dma_addr_t periodic_dma;
struct list_head intr_qh_list;
unsigned i_thresh; /* uframes HC might cache */
union fotg210_shadow *pshadow; /* mirror hw periodic table */
struct fotg210_qh *intr_unlink;
struct fotg210_qh *intr_unlink_last;
unsigned intr_unlink_cycle;
unsigned now_frame; /* frame from HC hardware */
unsigned next_frame; /* scan periodic, start here */
unsigned intr_count; /* intr activity count */
unsigned isoc_count; /* isoc activity count */
unsigned periodic_count; /* periodic activity count */
/* max periodic time per uframe */
unsigned uframe_periodic_max;
/* list of itds completed while now_frame was still active */
struct list_head cached_itd_list;
struct fotg210_itd *last_itd_to_free;
/* per root hub port */
unsigned long reset_done[FOTG210_MAX_ROOT_PORTS];
/* bit vectors (one bit per port)
* which ports were already suspended at the start of a bus suspend
*/
unsigned long bus_suspended;
/* which ports are edicated to the companion controller */
unsigned long companion_ports;
/* which ports are owned by the companion during a bus suspend */
unsigned long owned_ports;
/* which ports have the change-suspend feature turned on */
unsigned long port_c_suspend;
/* which ports are suspended */
unsigned long suspended_ports;
/* which ports have started to resume */
unsigned long resuming_ports;
/* per-HC memory pools (could be per-bus, but ...) */
struct dma_pool *qh_pool; /* qh per active urb */
struct dma_pool *qtd_pool; /* one or more per qh */
struct dma_pool *itd_pool; /* itd per iso urb */
unsigned random_frame;
unsigned long next_statechange;
ktime_t last_periodic_enable;
u32 command;
/* SILICON QUIRKS */
unsigned need_io_watchdog:1;
unsigned fs_i_thresh:1; /* Intel iso scheduling */
u8 sbrn; /* packed release number */
/* irq statistics */
#ifdef FOTG210_STATS
struct fotg210_stats stats;
# define INCR(x) ((x)++)
#else
# define INCR(x) do {} while (0)
#endif
struct fotg210 *fotg; /* Overarching FOTG210 device */
/* silicon clock */
struct clk *pclk;
};
/* convert between an HCD pointer and the corresponding FOTG210_HCD */
static inline struct fotg210_hcd *hcd_to_fotg210(struct usb_hcd *hcd)
{
return (struct fotg210_hcd *)(hcd->hcd_priv);
}
static inline struct usb_hcd *fotg210_to_hcd(struct fotg210_hcd *fotg210)
{
return container_of((void *) fotg210, struct usb_hcd, hcd_priv);
}
/*-------------------------------------------------------------------------*/
/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */
/* Section 2.2 Host Controller Capability Registers */
struct fotg210_caps {
/* these fields are specified as 8 and 16 bit registers,
* but some hosts can't perform 8 or 16 bit PCI accesses.
* some hosts treat caplength and hciversion as parts of a 32-bit
* register, others treat them as two separate registers, this
* affects the memory map for big endian controllers.
*/
u32 hc_capbase;
#define HC_LENGTH(fotg210, p) (0x00ff&((p) >> /* bits 7:0 / offset 00h */ \
(fotg210_big_endian_capbase(fotg210) ? 24 : 0)))
#define HC_VERSION(fotg210, p) (0xffff&((p) >> /* bits 31:16 / offset 02h */ \
(fotg210_big_endian_capbase(fotg210) ? 0 : 16)))
u32 hcs_params; /* HCSPARAMS - offset 0x4 */
#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */
u32 hcc_params; /* HCCPARAMS - offset 0x8 */
#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */
#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/
u8 portroute[8]; /* nibbles for routing - offset 0xC */
};
/* Section 2.3 Host Controller Operational Registers */
struct fotg210_regs {
/* USBCMD: offset 0x00 */
u32 command;
/* EHCI 1.1 addendum */
/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */
#define CMD_PARK (1<<11) /* enable "park" on async qh */
#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */
#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */
#define CMD_ASE (1<<5) /* async schedule enable */
#define CMD_PSE (1<<4) /* periodic schedule enable */
/* 3:2 is periodic frame list size */
#define CMD_RESET (1<<1) /* reset HC not bus */
#define CMD_RUN (1<<0) /* start/stop HC */
/* USBSTS: offset 0x04 */
u32 status;
#define STS_ASS (1<<15) /* Async Schedule Status */
#define STS_PSS (1<<14) /* Periodic Schedule Status */
#define STS_RECL (1<<13) /* Reclamation */
#define STS_HALT (1<<12) /* Not running (any reason) */
/* some bits reserved */
/* these STS_* flags are also intr_enable bits (USBINTR) */
#define STS_IAA (1<<5) /* Interrupted on async advance */
#define STS_FATAL (1<<4) /* such as some PCI access errors */
#define STS_FLR (1<<3) /* frame list rolled over */
#define STS_PCD (1<<2) /* port change detect */
#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */
#define STS_INT (1<<0) /* "normal" completion (short, ...) */
/* USBINTR: offset 0x08 */
u32 intr_enable;
/* FRINDEX: offset 0x0C */
u32 frame_index; /* current microframe number */
/* CTRLDSSEGMENT: offset 0x10 */
u32 segment; /* address bits 63:32 if needed */
/* PERIODICLISTBASE: offset 0x14 */
u32 frame_list; /* points to periodic list */
/* ASYNCLISTADDR: offset 0x18 */
u32 async_next; /* address of next async queue head */
u32 reserved1;
/* PORTSC: offset 0x20 */
u32 port_status;
/* 31:23 reserved */
#define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */
#define PORT_RESET (1<<8) /* reset port */
#define PORT_SUSPEND (1<<7) /* suspend port */
#define PORT_RESUME (1<<6) /* resume it */
#define PORT_PEC (1<<3) /* port enable change */
#define PORT_PE (1<<2) /* port enable */
#define PORT_CSC (1<<1) /* connect status change */
#define PORT_CONNECT (1<<0) /* device connected */
#define PORT_RWC_BITS (PORT_CSC | PORT_PEC)
u32 reserved2[19];
/* OTGCSR: offet 0x70 */
u32 otgcsr;
#define OTGCSR_HOST_SPD_TYP (3 << 22)
#define OTGCSR_A_BUS_DROP (1 << 5)
#define OTGCSR_A_BUS_REQ (1 << 4)
/* OTGISR: offset 0x74 */
u32 otgisr;
#define OTGISR_OVC (1 << 10)
u32 reserved3[15];
/* GMIR: offset 0xB4 */
u32 gmir;
#define GMIR_INT_POLARITY (1 << 3) /*Active High*/
#define GMIR_MHC_INT (1 << 2)
#define GMIR_MOTG_INT (1 << 1)
#define GMIR_MDEV_INT (1 << 0)
};
/*-------------------------------------------------------------------------*/
#define QTD_NEXT(fotg210, dma) cpu_to_hc32(fotg210, (u32)dma)
/*
* EHCI Specification 0.95 Section 3.5
* QTD: describe data transfer components (buffer, direction, ...)
* See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram".
*
* These are associated only with "QH" (Queue Head) structures,
* used with control, bulk, and interrupt transfers.
*/
struct fotg210_qtd {
/* first part defined by EHCI spec */
__hc32 hw_next; /* see EHCI 3.5.1 */
__hc32 hw_alt_next; /* see EHCI 3.5.2 */
__hc32 hw_token; /* see EHCI 3.5.3 */
#define QTD_TOGGLE (1 << 31) /* data toggle */
#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff)
#define QTD_IOC (1 << 15) /* interrupt on complete */
#define QTD_CERR(tok) (((tok)>>10) & 0x3)
#define QTD_PID(tok) (((tok)>>8) & 0x3)
#define QTD_STS_ACTIVE (1 << 7) /* HC may execute this */
#define QTD_STS_HALT (1 << 6) /* halted on error */
#define QTD_STS_DBE (1 << 5) /* data buffer error (in HC) */
#define QTD_STS_BABBLE (1 << 4) /* device was babbling (qtd halted) */
#define QTD_STS_XACT (1 << 3) /* device gave illegal response */
#define QTD_STS_MMF (1 << 2) /* incomplete split transaction */
#define QTD_STS_STS (1 << 1) /* split transaction state */
#define QTD_STS_PING (1 << 0) /* issue PING? */
#define ACTIVE_BIT(fotg210) cpu_to_hc32(fotg210, QTD_STS_ACTIVE)
#define HALT_BIT(fotg210) cpu_to_hc32(fotg210, QTD_STS_HALT)
#define STATUS_BIT(fotg210) cpu_to_hc32(fotg210, QTD_STS_STS)
__hc32 hw_buf[5]; /* see EHCI 3.5.4 */
__hc32 hw_buf_hi[5]; /* Appendix B */
/* the rest is HCD-private */
dma_addr_t qtd_dma; /* qtd address */
struct list_head qtd_list; /* sw qtd list */
struct urb *urb; /* qtd's urb */
size_t length; /* length of buffer */
} __aligned(32);
/* mask NakCnt+T in qh->hw_alt_next */
#define QTD_MASK(fotg210) cpu_to_hc32(fotg210, ~0x1f)
#define IS_SHORT_READ(token) (QTD_LENGTH(token) != 0 && QTD_PID(token) == 1)
/*-------------------------------------------------------------------------*/
/* type tag from {qh,itd,fstn}->hw_next */
#define Q_NEXT_TYPE(fotg210, dma) ((dma) & cpu_to_hc32(fotg210, 3 << 1))
/*
* Now the following defines are not converted using the
* cpu_to_le32() macro anymore, since we have to support
* "dynamic" switching between be and le support, so that the driver
* can be used on one system with SoC EHCI controller using big-endian
* descriptors as well as a normal little-endian PCI EHCI controller.
*/
/* values for that type tag */
#define Q_TYPE_ITD (0 << 1)
#define Q_TYPE_QH (1 << 1)
#define Q_TYPE_SITD (2 << 1)
#define Q_TYPE_FSTN (3 << 1)
/* next async queue entry, or pointer to interrupt/periodic QH */
#define QH_NEXT(fotg210, dma) \
(cpu_to_hc32(fotg210, (((u32)dma)&~0x01f)|Q_TYPE_QH))
/* for periodic/async schedules and qtd lists, mark end of list */
#define FOTG210_LIST_END(fotg210) \
cpu_to_hc32(fotg210, 1) /* "null pointer" to hw */
/*
* Entries in periodic shadow table are pointers to one of four kinds
* of data structure. That's dictated by the hardware; a type tag is
* encoded in the low bits of the hardware's periodic schedule. Use
* Q_NEXT_TYPE to get the tag.
*
* For entries in the async schedule, the type tag always says "qh".
*/
union fotg210_shadow {
struct fotg210_qh *qh; /* Q_TYPE_QH */
struct fotg210_itd *itd; /* Q_TYPE_ITD */
struct fotg210_fstn *fstn; /* Q_TYPE_FSTN */
__hc32 *hw_next; /* (all types) */
void *ptr;
};
/*-------------------------------------------------------------------------*/
/*
* EHCI Specification 0.95 Section 3.6
* QH: describes control/bulk/interrupt endpoints
* See Fig 3-7 "Queue Head Structure Layout".
*
* These appear in both the async and (for interrupt) periodic schedules.
*/
/* first part defined by EHCI spec */
struct fotg210_qh_hw {
__hc32 hw_next; /* see EHCI 3.6.1 */
__hc32 hw_info1; /* see EHCI 3.6.2 */
#define QH_CONTROL_EP (1 << 27) /* FS/LS control endpoint */
#define QH_HEAD (1 << 15) /* Head of async reclamation list */
#define QH_TOGGLE_CTL (1 << 14) /* Data toggle control */
#define QH_HIGH_SPEED (2 << 12) /* Endpoint speed */
#define QH_LOW_SPEED (1 << 12)
#define QH_FULL_SPEED (0 << 12)
#define QH_INACTIVATE (1 << 7) /* Inactivate on next transaction */
__hc32 hw_info2; /* see EHCI 3.6.2 */
#define QH_SMASK 0x000000ff
#define QH_CMASK 0x0000ff00
#define QH_HUBADDR 0x007f0000
#define QH_HUBPORT 0x3f800000
#define QH_MULT 0xc0000000
__hc32 hw_current; /* qtd list - see EHCI 3.6.4 */
/* qtd overlay (hardware parts of a struct fotg210_qtd) */
__hc32 hw_qtd_next;
__hc32 hw_alt_next;
__hc32 hw_token;
__hc32 hw_buf[5];
__hc32 hw_buf_hi[5];
} __aligned(32);
struct fotg210_qh {
struct fotg210_qh_hw *hw; /* Must come first */
/* the rest is HCD-private */
dma_addr_t qh_dma; /* address of qh */
union fotg210_shadow qh_next; /* ptr to qh; or periodic */
struct list_head qtd_list; /* sw qtd list */
struct list_head intr_node; /* list of intr QHs */
struct fotg210_qtd *dummy;
struct fotg210_qh *unlink_next; /* next on unlink list */
unsigned unlink_cycle;
u8 needs_rescan; /* Dequeue during giveback */
u8 qh_state;
#define QH_STATE_LINKED 1 /* HC sees this */
#define QH_STATE_UNLINK 2 /* HC may still see this */
#define QH_STATE_IDLE 3 /* HC doesn't see this */
#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on unlink q */
#define QH_STATE_COMPLETING 5 /* don't touch token.HALT */
u8 xacterrs; /* XactErr retry counter */
#define QH_XACTERR_MAX 32 /* XactErr retry limit */
/* periodic schedule info */
u8 usecs; /* intr bandwidth */
u8 gap_uf; /* uframes split/csplit gap */
u8 c_usecs; /* ... split completion bw */
u16 tt_usecs; /* tt downstream bandwidth */
unsigned short period; /* polling interval */
unsigned short start; /* where polling starts */
#define NO_FRAME ((unsigned short)~0) /* pick new start */
struct usb_device *dev; /* access to TT */
unsigned is_out:1; /* bulk or intr OUT */
unsigned clearing_tt:1; /* Clear-TT-Buf in progress */
};
/*-------------------------------------------------------------------------*/
/* description of one iso transaction (up to 3 KB data if highspeed) */
struct fotg210_iso_packet {
/* These will be copied to iTD when scheduling */
u64 bufp; /* itd->hw_bufp{,_hi}[pg] |= */
__hc32 transaction; /* itd->hw_transaction[i] |= */
u8 cross; /* buf crosses pages */
/* for full speed OUT splits */
u32 buf1;
};
/* temporary schedule data for packets from iso urbs (both speeds)
* each packet is one logical usb transaction to the device (not TT),
* beginning at stream->next_uframe
*/
struct fotg210_iso_sched {
struct list_head td_list;
unsigned span;
struct fotg210_iso_packet packet[];
};
/*
* fotg210_iso_stream - groups all (s)itds for this endpoint.
* acts like a qh would, if EHCI had them for ISO.
*/
struct fotg210_iso_stream {
/* first field matches fotg210_hq, but is NULL */
struct fotg210_qh_hw *hw;
u8 bEndpointAddress;
u8 highspeed;
struct list_head td_list; /* queued itds */
struct list_head free_list; /* list of unused itds */
struct usb_device *udev;
struct usb_host_endpoint *ep;
/* output of (re)scheduling */
int next_uframe;
__hc32 splits;
/* the rest is derived from the endpoint descriptor,
* trusting urb->interval == f(epdesc->bInterval) and
* including the extra info for hw_bufp[0..2]
*/
u8 usecs, c_usecs;
u16 interval;
u16 tt_usecs;
u16 maxp;
u16 raw_mask;
unsigned bandwidth;
/* This is used to initialize iTD's hw_bufp fields */
__hc32 buf0;
__hc32 buf1;
__hc32 buf2;
/* this is used to initialize sITD's tt info */
__hc32 address;
};
/*-------------------------------------------------------------------------*/
/*
* EHCI Specification 0.95 Section 3.3
* Fig 3-4 "Isochronous Transaction Descriptor (iTD)"
*
* Schedule records for high speed iso xfers
*/
struct fotg210_itd {
/* first part defined by EHCI spec */
__hc32 hw_next; /* see EHCI 3.3.1 */
__hc32 hw_transaction[8]; /* see EHCI 3.3.2 */
#define FOTG210_ISOC_ACTIVE (1<<31) /* activate transfer this slot */
#define FOTG210_ISOC_BUF_ERR (1<<30) /* Data buffer error */
#define FOTG210_ISOC_BABBLE (1<<29) /* babble detected */
#define FOTG210_ISOC_XACTERR (1<<28) /* XactErr - transaction error */
#define FOTG210_ITD_LENGTH(tok) (((tok)>>16) & 0x0fff)
#define FOTG210_ITD_IOC (1 << 15) /* interrupt on complete */
#define ITD_ACTIVE(fotg210) cpu_to_hc32(fotg210, FOTG210_ISOC_ACTIVE)
__hc32 hw_bufp[7]; /* see EHCI 3.3.3 */
__hc32 hw_bufp_hi[7]; /* Appendix B */
/* the rest is HCD-private */
dma_addr_t itd_dma; /* for this itd */
union fotg210_shadow itd_next; /* ptr to periodic q entry */
struct urb *urb;
struct fotg210_iso_stream *stream; /* endpoint's queue */
struct list_head itd_list; /* list of stream's itds */
/* any/all hw_transactions here may be used by that urb */
unsigned frame; /* where scheduled */
unsigned pg;
unsigned index[8]; /* in urb->iso_frame_desc */
} __aligned(32);
/*-------------------------------------------------------------------------*/
/*
* EHCI Specification 0.96 Section 3.7
* Periodic Frame Span Traversal Node (FSTN)
*
* Manages split interrupt transactions (using TT) that span frame boundaries
* into uframes 0/1; see 4.12.2.2. In those uframes, a "save place" FSTN
* makes the HC jump (back) to a QH to scan for fs/ls QH completions until
* it hits a "restore" FSTN; then it returns to finish other uframe 0/1 work.
*/
struct fotg210_fstn {
__hc32 hw_next; /* any periodic q entry */
__hc32 hw_prev; /* qh or FOTG210_LIST_END */
/* the rest is HCD-private */
dma_addr_t fstn_dma;
union fotg210_shadow fstn_next; /* ptr to periodic q entry */
} __aligned(32);
/*-------------------------------------------------------------------------*/
/* Prepare the PORTSC wakeup flags during controller suspend/resume */
#define fotg210_prepare_ports_for_controller_suspend(fotg210, do_wakeup) \
fotg210_adjust_port_wakeup_flags(fotg210, true, do_wakeup)
#define fotg210_prepare_ports_for_controller_resume(fotg210) \
fotg210_adjust_port_wakeup_flags(fotg210, false, false)
/*-------------------------------------------------------------------------*/
/*
* Some EHCI controllers have a Transaction Translator built into the
* root hub. This is a non-standard feature. Each controller will need
* to add code to the following inline functions, and call them as
* needed (mostly in root hub code).
*/
static inline unsigned int
fotg210_get_speed(struct fotg210_hcd *fotg210, unsigned int portsc)
{
return (readl(&fotg210->regs->otgcsr)
& OTGCSR_HOST_SPD_TYP) >> 22;
}
/* Returns the speed of a device attached to a port on the root hub. */
static inline unsigned int
fotg210_port_speed(struct fotg210_hcd *fotg210, unsigned int portsc)
{
switch (fotg210_get_speed(fotg210, portsc)) {
case 0:
return 0;
case 1:
return USB_PORT_STAT_LOW_SPEED;
case 2:
default:
return USB_PORT_STAT_HIGH_SPEED;
}
}
/*-------------------------------------------------------------------------*/
#define fotg210_has_fsl_portno_bug(e) (0)
/*
* While most USB host controllers implement their registers in
* little-endian format, a minority (celleb companion chip) implement
* them in big endian format.
*
* This attempts to support either format at compile time without a
* runtime penalty, or both formats with the additional overhead
* of checking a flag bit.
*
*/
#define fotg210_big_endian_mmio(e) 0
#define fotg210_big_endian_capbase(e) 0
static inline unsigned int fotg210_readl(const struct fotg210_hcd *fotg210,
__u32 __iomem *regs)
{
return readl(regs);
}
static inline void fotg210_writel(const struct fotg210_hcd *fotg210,
const unsigned int val, __u32 __iomem *regs)
{
writel(val, regs);
}
/* cpu to fotg210 */
static inline __hc32 cpu_to_hc32(const struct fotg210_hcd *fotg210, const u32 x)
{
return cpu_to_le32(x);
}
/* fotg210 to cpu */
static inline u32 hc32_to_cpu(const struct fotg210_hcd *fotg210, const __hc32 x)
{
return le32_to_cpu(x);
}
static inline u32 hc32_to_cpup(const struct fotg210_hcd *fotg210,
const __hc32 *x)
{
return le32_to_cpup(x);
}
/*-------------------------------------------------------------------------*/
static inline unsigned fotg210_read_frame_index(struct fotg210_hcd *fotg210)
{
return fotg210_readl(fotg210, &fotg210->regs->frame_index);
}
/*-------------------------------------------------------------------------*/
#endif /* __LINUX_FOTG210_H */

View File

@ -124,6 +124,7 @@ static void fotg210_set_fifo_dir(struct fotg210_ep *ep, u32 epnum, u32 dir_in)
u32 val;
val = ioread32(fotg210->reg + FOTG210_FIFOMAP);
val &= ~FIFOMAP_NA(epnum - 1);
val |= (dir_in ? FIFOMAP_DIRIN(epnum - 1) : FIFOMAP_DIROUT(epnum - 1));
iowrite32(val, fotg210->reg + FOTG210_FIFOMAP);
}
@ -134,6 +135,7 @@ static void fotg210_set_tfrtype(struct fotg210_ep *ep, u32 epnum, u32 type)
u32 val;
val = ioread32(fotg210->reg + FOTG210_FIFOCF);
val &= ~FIFOCF_TYPE(3, epnum - 1);
val |= FIFOCF_TYPE(type, epnum - 1);
iowrite32(val, fotg210->reg + FOTG210_FIFOCF);
}
@ -147,6 +149,7 @@ static void fotg210_set_mps(struct fotg210_ep *ep, u32 epnum, u32 mps,
FOTG210_OUTEPMPSR(epnum);
val = ioread32(fotg210->reg + offset);
val &= ~INOUTEPMPSR_MPS(~0);
val |= INOUTEPMPSR_MPS(mps);
iowrite32(val, fotg210->reg + offset);
}
@ -209,12 +212,13 @@ static int fotg210_ep_release(struct fotg210_ep *ep)
{
if (!ep->epnum)
return 0;
fotg210_reset_tseq(ep->fotg210, ep->epnum);
ep->epnum = 0;
ep->stall = 0;
ep->wedged = 0;
fotg210_reset_tseq(ep->fotg210, ep->epnum);
return 0;
}
@ -338,6 +342,7 @@ static void fotg210_start_dma(struct fotg210_ep *ep,
struct fotg210_request *req)
{
struct device *dev = &ep->fotg210->gadget.dev;
enum dma_data_direction direction;
dma_addr_t d;
u8 *buffer;
u32 length;
@ -361,8 +366,8 @@ static void fotg210_start_dma(struct fotg210_ep *ep,
length = req->req.length - req->req.actual;
}
d = dma_map_single(dev, buffer, length,
ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
direction = ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
d = dma_map_single(dev, buffer, length, direction);
if (dma_mapping_error(dev, d)) {
pr_err("dma_mapping_error\n");
@ -379,7 +384,7 @@ static void fotg210_start_dma(struct fotg210_ep *ep,
/* update actual transfer length */
req->req.actual += length;
dma_unmap_single(dev, d, length, DMA_TO_DEVICE);
dma_unmap_single(dev, d, length, direction);
}
static void fotg210_ep0_queue(struct fotg210_ep *ep,
@ -445,7 +450,7 @@ static int fotg210_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
req = container_of(_req, struct fotg210_request, req);
spin_lock_irqsave(&ep->fotg210->lock, flags);
if (!list_empty(&ep->queue))
if (!list_empty(&req->queue))
fotg210_done(ep, req, -ECONNRESET);
spin_unlock_irqrestore(&ep->fotg210->lock, flags);
@ -661,7 +666,7 @@ static void fotg210_set_feature(struct fotg210_udc *fotg210,
case USB_RECIP_ENDPOINT: {
u8 epnum;
epnum = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK;
if (epnum)
if (epnum && epnum < FOTG210_MAX_NUM_EP)
fotg210_set_epnstall(fotg210->ep[epnum]);
else
fotg210_set_cxstall(fotg210);
@ -677,8 +682,8 @@ static void fotg210_set_feature(struct fotg210_udc *fotg210,
static void fotg210_clear_feature(struct fotg210_udc *fotg210,
struct usb_ctrlrequest *ctrl)
{
struct fotg210_ep *ep =
fotg210->ep[ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK];
u8 epnum = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK;
struct fotg210_ep *ep;
switch (ctrl->bRequestType & USB_RECIP_MASK) {
case USB_RECIP_DEVICE:
@ -688,7 +693,12 @@ static void fotg210_clear_feature(struct fotg210_udc *fotg210,
fotg210_set_cxdone(fotg210);
break;
case USB_RECIP_ENDPOINT:
if (ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK) {
if (epnum >= FOTG210_MAX_NUM_EP) {
fotg210_request_error(fotg210);
break;
}
if (epnum) {
ep = fotg210->ep[epnum];
if (ep->wedged) {
fotg210_set_cxdone(fotg210);
break;
@ -744,8 +754,8 @@ static void fotg210_get_status(struct fotg210_udc *fotg210,
fotg210->ep0_data = cpu_to_le16(0);
break;
case USB_RECIP_ENDPOINT:
epnum = ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK;
if (epnum)
epnum = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK;
if (epnum && epnum < FOTG210_MAX_NUM_EP)
fotg210->ep0_data =
cpu_to_le16(fotg210_is_epnstall(fotg210->ep[epnum])
<< USB_ENDPOINT_HALT);
@ -886,6 +896,8 @@ static irqreturn_t fotg210_irq(int irq, void *_fotg210)
u32 int_msk = ioread32(fotg210->reg + FOTG210_DMIGR);
int_grp &= ~int_msk;
if (!int_grp)
return IRQ_NONE;
spin_lock(&fotg210->lock);
@ -1002,6 +1014,14 @@ static void fotg210_disable_unplug(struct fotg210_udc *fotg210)
iowrite32(reg, fotg210->reg + FOTG210_PHYTMSR);
}
static void fotg210_enable_unplug(struct fotg210_udc *fotg210)
{
u32 reg = ioread32(fotg210->reg + FOTG210_PHYTMSR);
reg |= PHYTMSR_UNPLUG;
iowrite32(reg, fotg210->reg + FOTG210_PHYTMSR);
}
static int fotg210_udc_start(struct usb_gadget *g,
struct usb_gadget_driver *driver)
{
@ -1009,20 +1029,23 @@ static int fotg210_udc_start(struct usb_gadget *g,
u32 value;
int ret;
/* hook up the driver */
/* Hook up the driver before enabling device interrupts. */
fotg210->driver = driver;
fotg210->gadget.dev.of_node = fotg210->dev->of_node;
fotg210->gadget.speed = USB_SPEED_UNKNOWN;
dev_info(fotg210->dev, "bound driver %s\n", driver->driver.name);
if (!IS_ERR_OR_NULL(fotg210->phy)) {
ret = otg_set_peripheral(fotg210->phy->otg,
&fotg210->gadget);
if (ret)
dev_err(fotg210->dev, "can't bind to phy\n");
if (ret) {
fotg210->driver = NULL;
return dev_err_probe(fotg210->dev, ret,
"can't bind to PHY\n");
}
}
dev_info(fotg210->dev, "bound driver %s\n", driver->driver.name);
/* chip enable */
value = ioread32(fotg210->reg + FOTG210_DMCR);
value |= DMCR_CHIP_EN;
@ -1076,20 +1099,23 @@ static void fotg210_init(struct fotg210_udc *fotg210)
static int fotg210_udc_stop(struct usb_gadget *g)
{
struct fotg210_udc *fotg210 = gadget_to_fotg210(g);
unsigned long flags;
unsigned long flags;
int ret = 0;
if (!IS_ERR_OR_NULL(fotg210->phy))
return otg_set_peripheral(fotg210->phy->otg, NULL);
ret = otg_set_peripheral(fotg210->phy->otg, NULL);
/* fotg210_init() sleeps, so it must run outside the spinlock. */
fotg210_init(fotg210);
spin_lock_irqsave(&fotg210->lock, flags);
fotg210_init(fotg210);
fotg210->driver = NULL;
fotg210->gadget.speed = USB_SPEED_UNKNOWN;
spin_unlock_irqrestore(&fotg210->lock, flags);
return 0;
return ret;
}
/**
@ -1103,8 +1129,12 @@ static int fotg210_vbus_session(struct usb_gadget *g, int is_active)
{
struct fotg210_udc *fotg210 = gadget_to_fotg210(g);
/* Call down to core integration layer to drive or disable VBUS */
fotg210_vbus(fotg210->fotg, is_active);
/* A peripheral must never source VBUS; only control its pull-up. */
if (is_active)
fotg210_disable_unplug(fotg210);
else
fotg210_enable_unplug(fotg210);
return 0;
}
@ -1144,28 +1174,24 @@ static int fotg210_phy_event(struct notifier_block *nb, unsigned long action,
}
}
static struct notifier_block fotg210_phy_notifier = {
.notifier_call = fotg210_phy_event,
};
int fotg210_udc_remove(struct platform_device *pdev)
{
struct fotg210_udc *fotg210 = platform_get_drvdata(pdev);
int i;
usb_del_gadget_udc(&fotg210->gadget);
if (!IS_ERR_OR_NULL(fotg210->phy)) {
usb_unregister_notifier(fotg210->phy, &fotg210_phy_notifier);
usb_put_phy(fotg210->phy);
}
iounmap(fotg210->reg);
if (!IS_ERR_OR_NULL(fotg210->phy))
usb_unregister_notifier(fotg210->phy,
&fotg210->phy_notifier);
free_irq(platform_get_irq(pdev, 0), fotg210);
usb_phy_shutdown(fotg210->phy);
fotg210_ep_free_request(&fotg210->ep[0]->ep, fotg210->ep0_req);
for (i = 0; i < FOTG210_MAX_NUM_EP; i++)
kfree(fotg210->ep[i]);
kfree(fotg210);
platform_set_drvdata(pdev, NULL);
return 0;
}
@ -1188,7 +1214,6 @@ int fotg210_udc_probe(struct platform_device *pdev, struct fotg210 *fotg)
return -ENOMEM;
fotg210->dev = dev;
fotg210->fotg = fotg;
fotg210->phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 0);
if (IS_ERR(fotg210->phy)) {
@ -1216,8 +1241,6 @@ int fotg210_udc_probe(struct platform_device *pdev, struct fotg210 *fotg)
spin_lock_init(&fotg210->lock);
platform_set_drvdata(pdev, fotg210);
fotg210->gadget.ops = &fotg210_gadget_ops;
fotg210->gadget.max_speed = USB_SPEED_HIGH;
@ -1267,38 +1290,45 @@ int fotg210_udc_probe(struct platform_device *pdev, struct fotg210 *fotg)
fotg210_disable_unplug(fotg210);
ret = request_irq(irq, fotg210_irq, IRQF_SHARED,
udc_name, fotg210);
ret = request_irq(irq, fotg210_irq, 0, udc_name, fotg210);
if (ret < 0) {
dev_err_probe(dev, ret, "request_irq error\n");
goto err_req;
}
if (!IS_ERR_OR_NULL(fotg210->phy))
usb_register_notifier(fotg210->phy, &fotg210_phy_notifier);
if (!IS_ERR_OR_NULL(fotg210->phy)) {
fotg210->phy_notifier.notifier_call = fotg210_phy_event;
ret = usb_register_notifier(fotg210->phy,
&fotg210->phy_notifier);
if (ret)
goto err_notifier;
}
ret = usb_add_gadget_udc(dev, &fotg210->gadget);
if (ret)
goto err_add_udc;
platform_set_drvdata(pdev, fotg210);
dev_info(dev, "version %s\n", DRIVER_VERSION);
return 0;
err_add_udc:
if (!IS_ERR_OR_NULL(fotg210->phy))
usb_unregister_notifier(fotg210->phy, &fotg210_phy_notifier);
usb_unregister_notifier(fotg210->phy,
&fotg210->phy_notifier);
err_notifier:
free_irq(irq, fotg210);
err_req:
fotg210_ep_free_request(&fotg210->ep[0]->ep, fotg210->ep0_req);
err_map:
iounmap(fotg210->reg);
err_alloc:
for (i = 0; i < FOTG210_MAX_NUM_EP; i++)
kfree(fotg210->ep[i]);
usb_phy_shutdown(fotg210->phy);
err_free:
kfree(fotg210);

View File

@ -237,8 +237,8 @@ struct fotg210_udc {
unsigned long irq_trigger;
struct device *dev;
struct fotg210 *fotg;
struct usb_phy *phy;
struct notifier_block phy_notifier;
struct usb_gadget gadget;
struct usb_gadget_driver *driver;

View File

@ -2,6 +2,8 @@
#ifndef __FOTG210_H
#define __FOTG210_H
#include <linux/usb/otg.h>
enum gemini_port {
GEMINI_PORT_NONE = 0,
GEMINI_PORT_0,
@ -15,6 +17,8 @@ struct fotg210 {
struct clk *pclk;
struct regmap *map;
enum gemini_port port;
enum usb_dr_mode mode;
bool is_fotg210;
};
void fotg210_vbus(struct fotg210 *fotg, bool enable);
@ -22,22 +26,37 @@ void fotg210_vbus(struct fotg210 *fotg, bool enable);
#ifdef CONFIG_USB_FOTG210_HCD
int fotg210_hcd_probe(struct platform_device *pdev, struct fotg210 *fotg);
int fotg210_hcd_remove(struct platform_device *pdev);
int fotg210_hcd_suspend(struct device *dev);
int fotg210_hcd_resume(struct device *dev);
int fotg210_hcd_init(void);
void fotg210_hcd_cleanup(void);
#else
static inline int fotg210_hcd_probe(struct platform_device *pdev,
struct fotg210 *fotg)
{
return 0;
return -ENODEV;
}
static inline int fotg210_hcd_remove(struct platform_device *pdev)
{
return 0;
return -ENODEV;
}
static inline int fotg210_hcd_suspend(struct device *dev)
{
return -ENODEV;
}
static inline int fotg210_hcd_resume(struct device *dev)
{
return -ENODEV;
}
static inline int fotg210_hcd_init(void)
{
return 0;
}
static inline void fotg210_hcd_cleanup(void)
{
}
@ -50,8 +69,9 @@ int fotg210_udc_remove(struct platform_device *pdev);
static inline int fotg210_udc_probe(struct platform_device *pdev,
struct fotg210 *fotg)
{
return 0;
return -ENODEV;
}
static inline int fotg210_udc_remove(struct platform_device *pdev)
{
return 0;

View File

@ -2740,7 +2740,7 @@ void usb_composite_setup_continue(struct usb_composite_dev *cdev)
spin_lock_irqsave(&cdev->lock, flags);
if (cdev->delayed_status == 0) {
WARN(cdev, "%s: Unexpected call\n", __func__);
DBG(cdev, "%s: Ignoring obsolete call\n", __func__);
} else if (--cdev->delayed_status == 0) {
DBG(cdev, "%s: Completing delayed status\n", __func__);

View File

@ -463,7 +463,12 @@ static int fsg_setup(struct usb_function *f,
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength);
if (!fsg_is_set(fsg->common))
/*
* fsg_is_set() warns when common->fsg is NULL, but that can happen
* normally while a configuration change installs or removes the active
* function.
*/
if (!fsg->common->fsg)
return -EOPNOTSUPP;
++fsg->common->ep0_req_tag; /* Record arrival of a new request */

View File

@ -236,6 +236,9 @@ static void put_ep (struct ep_data *data)
static const char *CHIP;
static DEFINE_MUTEX(sb_mutex); /* Serialize superblock operations */
static DEFINE_SPINLOCK(aio_lock); /* Protect aio cancellation info */
static struct workqueue_struct *gadgetfs_wq;
/*----------------------------------------------------------------------*/
/* NOTE: don't use dev_printk calls before binding to the gadget
@ -433,40 +436,105 @@ static long ep_ioctl(struct file *fd, unsigned code, unsigned long value)
/* ASYNCHRONOUS ENDPOINT I/O OPERATIONS (bulk/intr/iso) */
enum aio_req_state {
AIO_SUBMITTING,
AIO_RUNNING,
AIO_COMPLETED,
AIO_GIVEN_BACK,
};
enum aio_cancel_state {
AIO_NOT_CANCELLED,
AIO_UNLINKING,
AIO_UNLINK_DONE,
};
struct kiocb_priv {
struct usb_request *req;
struct ep_data *epdata;
struct usb_ep *ep;
struct kiocb *iocb;
struct mm_struct *mm;
struct work_struct work;
struct work_struct copy_work;
struct work_struct unlink_work;
void *buf;
struct iov_iter to;
const void *to_free;
unsigned actual;
enum aio_req_state req_state;
enum aio_cancel_state cancel_state;
bool is_read;
};
static void ep_unlink_worker(struct work_struct *work)
{
struct kiocb_priv *priv;
struct usb_request *req;
struct ep_data *epdata;
struct usb_ep *ep;
enum aio_req_state req_state;
priv = container_of(work, struct kiocb_priv, unlink_work);
req = priv->req;
epdata = priv->epdata;
ep = priv->ep;
usb_ep_dequeue(ep, req);
spin_lock_irq(&aio_lock);
req_state = priv->req_state;
priv->cancel_state = AIO_UNLINK_DONE;
spin_unlock_irq(&aio_lock);
/*
* req and epdata are freed after unlinking and completion are both done.
* priv is freed after unlinking and giveback are both done.
*/
if (req_state >= AIO_COMPLETED) {
/* priv may have been freed already, depending on req_state */
usb_ep_free_request(ep, req);
put_ep(epdata);
if (req_state == AIO_GIVEN_BACK)
kfree(priv);
}
}
static int ep_aio_cancel(struct kiocb *iocb)
{
struct kiocb_priv *priv = iocb->private;
struct ep_data *epdata;
int value;
struct kiocb_priv *priv;
unsigned long flags;
local_irq_disable();
epdata = priv->epdata;
// spin_lock(&epdata->dev->lock);
if (likely(epdata && epdata->ep && priv->req))
value = usb_ep_dequeue (epdata->ep, priv->req);
else
value = -EINVAL;
// spin_unlock(&epdata->dev->lock);
local_irq_enable();
spin_lock_irqsave(&aio_lock, flags);
priv = iocb->private;
if (!priv || priv->cancel_state != AIO_NOT_CANCELLED) {
spin_unlock_irqrestore(&aio_lock, flags);
return -EINVAL; /* Already completed or cancelled */
}
if (priv->req_state == AIO_SUBMITTING) {
priv->cancel_state = AIO_UNLINK_DONE;
spin_unlock_irqrestore(&aio_lock, flags);
return 0; /* ep_aio() will call us again if needed */
}
priv->cancel_state = AIO_UNLINKING;
return value;
/*
* We are called with the aio core holding iocb's context lock.
* usb_ep_dequeue() is allowed to run synchronously, calling the
* completion handler ep_aio_complete() before it returns.
* But ep_aio_complete() may call iocb->io_complete(), which
* tries to acquire the context lock, leading to deadlock.
* For this reason, do the dequeue operation in a work routine.
*/
INIT_WORK(&priv->unlink_work, ep_unlink_worker);
queue_work(gadgetfs_wq, &priv->unlink_work);
spin_unlock_irqrestore(&aio_lock, flags);
return 0;
}
static void ep_user_copy_worker(struct work_struct *work)
{
struct kiocb_priv *priv = container_of(work, struct kiocb_priv, work);
struct kiocb_priv *priv = container_of(work, struct kiocb_priv, copy_work);
struct mm_struct *mm = priv->mm;
struct kiocb *iocb = priv->iocb;
size_t ret;
@ -488,47 +556,63 @@ static void ep_user_copy_worker(struct work_struct *work)
kfree(priv->buf);
kfree(priv->to_free);
kfree(priv);
spin_lock_irq(&aio_lock);
priv->req_state = AIO_GIVEN_BACK;
if (priv->cancel_state != AIO_UNLINKING)
kfree(priv);
spin_unlock_irq(&aio_lock);
}
static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req)
{
struct kiocb *iocb = req->context;
struct kiocb_priv *priv = iocb->private;
struct ep_data *epdata = priv->epdata;
enum aio_req_state new_req_state;
enum aio_cancel_state cancel_state;
/* lock against disconnect (and ideally, cancel) */
spin_lock(&epdata->dev->lock);
priv->req = NULL;
priv->epdata = NULL;
/* Prevent future cancellation */
spin_lock(&aio_lock);
iocb->private = NULL;
spin_unlock(&aio_lock);
/* if this was a write or a read returning no data then we
* don't need to copy anything to userspace, so we can
* complete the aio request immediately.
*/
if (priv->to_free == NULL || unlikely(req->actual == 0)) {
if (!priv->is_read || unlikely(req->actual == 0)) {
mmdrop(priv->mm);
kfree(req->buf);
kfree(priv->to_free);
kfree(priv);
iocb->private = NULL;
iocb->ki_complete(iocb,
req->actual ? req->actual : (long)req->status);
new_req_state = AIO_GIVEN_BACK;
} else {
/* ep_copy_to_user() won't report both; we hide some faults */
if (unlikely(0 != req->status))
DBG(epdata->dev, "%s fault %d len %d\n",
DBG(priv->epdata->dev, "%s fault %d len %d\n",
ep->name, req->status, req->actual);
priv->buf = req->buf;
priv->actual = req->actual;
INIT_WORK(&priv->work, ep_user_copy_worker);
schedule_work(&priv->work);
new_req_state = AIO_COMPLETED;
}
usb_ep_free_request(ep, req);
spin_unlock(&epdata->dev->lock);
put_ep(epdata);
spin_lock(&aio_lock);
priv->req_state = new_req_state;
cancel_state = priv->cancel_state;
spin_unlock(&aio_lock);
if (new_req_state == AIO_COMPLETED) {
INIT_WORK(&priv->copy_work, ep_user_copy_worker);
queue_work(gadgetfs_wq, &priv->copy_work);
}
if (cancel_state != AIO_UNLINKING) {
usb_ep_free_request(ep, req);
put_ep(priv->epdata);
if (new_req_state == AIO_GIVEN_BACK)
kfree(priv);
}
}
static ssize_t ep_aio(struct kiocb *iocb,
@ -539,11 +623,12 @@ static ssize_t ep_aio(struct kiocb *iocb,
{
struct usb_request *req;
ssize_t value;
struct usb_ep *ep;
bool need_unlink = false;
iocb->private = priv;
priv->iocb = iocb;
kiocb_set_cancel_fn(iocb, ep_aio_cancel);
get_ep(epdata);
priv->epdata = epdata;
priv->actual = 0;
@ -555,10 +640,12 @@ static ssize_t ep_aio(struct kiocb *iocb,
*/
spin_lock_irq(&epdata->dev->lock);
value = -ENODEV;
if (unlikely(epdata->ep == NULL))
ep = epdata->ep;
if (unlikely(ep == NULL))
goto fail;
priv->ep = ep;
req = usb_ep_alloc_request(epdata->ep, GFP_ATOMIC);
req = usb_ep_alloc_request(ep, GFP_ATOMIC);
value = -ENOMEM;
if (unlikely(!req))
goto fail;
@ -568,12 +655,45 @@ static ssize_t ep_aio(struct kiocb *iocb,
req->length = len;
req->complete = ep_aio_complete;
req->context = iocb;
value = usb_ep_queue(epdata->ep, req, GFP_ATOMIC);
priv->req_state = AIO_SUBMITTING;
priv->cancel_state = AIO_NOT_CANCELLED;
/* Not allowed to manipulate the aio context while holding dev->lock */
++epdata->dev->udc_usage;
spin_unlock_irq(&epdata->dev->lock);
kiocb_set_cancel_fn(iocb, ep_aio_cancel);
value = usb_ep_queue(ep, req, GFP_KERNEL);
spin_lock_irq(&epdata->dev->lock);
--epdata->dev->udc_usage;
if (unlikely(0 != value)) {
usb_ep_free_request(epdata->ep, req);
spin_lock(&aio_lock);
iocb->private = NULL;
spin_unlock(&aio_lock);
usb_ep_free_request(ep, req);
goto fail;
}
spin_unlock_irq(&epdata->dev->lock);
spin_lock_irq(&aio_lock);
if (iocb->private != NULL) {
priv->req_state = AIO_RUNNING;
/* Cancelled before or just after submission? */
if (priv->cancel_state == AIO_UNLINK_DONE) {
priv->cancel_state = AIO_NOT_CANCELLED;
need_unlink = true;
}
} /* Otherwise already completed */
spin_unlock_irq(&aio_lock);
if (need_unlink) /* Redo cancel that was too early */
ep_aio_cancel(iocb);
return -EIOCBQUEUED;
fail:
@ -627,6 +747,7 @@ ep_read_iter(struct kiocb *iocb, struct iov_iter *to)
value = -ENOMEM;
if (!priv)
goto fail;
priv->is_read = true;
priv->to_free = dup_iter(&priv->to, to, GFP_KERNEL);
if (!iter_is_ubuf(&priv->to) && !priv->to_free) {
kfree(priv);
@ -1692,6 +1813,8 @@ static int gadgetfs_bind(struct usb_gadget *gadget,
dev->gadget = gadget;
gadget->ep0->driver_data = dev;
get_dev (dev);
/* preallocate control response and buffer */
dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL);
if (!dev->req)
@ -1706,7 +1829,6 @@ static int gadgetfs_bind(struct usb_gadget *gadget,
spin_lock_irq(&dev->lock);
dev->state = STATE_DEV_UNCONNECTED;
spin_unlock_irq(&dev->lock);
get_dev (dev);
return 0;
enomem:
@ -2135,10 +2257,17 @@ static int __init gadgetfs_init (void)
{
int status;
gadgetfs_wq = alloc_workqueue("gadgetfs", WQ_PERCPU, 0);
if (!gadgetfs_wq)
return -ENOMEM;
status = register_filesystem (&gadgetfs_type);
if (status == 0)
pr_info ("%s: %s, version " DRIVER_VERSION "\n",
shortname, driver_desc);
if (status) {
destroy_workqueue(gadgetfs_wq);
return status;
}
pr_info("%s: %s, version " DRIVER_VERSION "\n", shortname, driver_desc);
return status;
}
module_init (gadgetfs_init);
@ -2147,6 +2276,7 @@ static void __exit gadgetfs_cleanup (void)
{
pr_debug ("unregister %s\n", shortname);
unregister_filesystem (&gadgetfs_type);
destroy_workqueue(gadgetfs_wq);
}
module_exit (gadgetfs_cleanup);

View File

@ -383,10 +383,8 @@ static int ast_vhub_probe(struct platform_device *pdev)
}
rc = devm_request_irq(&pdev->dev, vhub->irq, ast_vhub_irq, 0,
KBUILD_MODNAME, vhub);
if (rc) {
dev_err(&pdev->dev, "Failed to request interrupt\n");
if (rc)
goto err;
}
dma_mask_ptr = (u64 *)of_device_get_match_data(&pdev->dev);
if (dma_mask_ptr) {

View File

@ -592,8 +592,6 @@ int ast_vhub_init_dev(struct ast_vhub *vhub, unsigned int idx)
else
d->gadget.max_speed = USB_SPEED_HIGH;
d->gadget.speed = USB_SPEED_UNKNOWN;
d->gadget.dev.of_node = vhub->pdev->dev.of_node;
dev_set_of_node_reused(&d->gadget.dev);
rc = usb_add_gadget_udc(d->port_dev, &d->gadget);
if (rc != 0)

View File

@ -1299,7 +1299,6 @@ static int ast_udc_start(struct usb_gadget *gadget,
UDC_DBG(udc, "\n");
udc->driver = driver;
udc->gadget.dev.of_node = udc->pdev->dev.of_node;
for (i = 0; i < AST_UDC_NUM_ENDPOINTS; i++) {
ep = &udc->ep[i];
@ -1568,10 +1567,8 @@ static int ast_udc_probe(struct platform_device *pdev)
rc = devm_request_irq(&pdev->dev, udc->irq, ast_udc_isr, 0,
KBUILD_MODNAME, udc);
if (rc) {
dev_err(&pdev->dev, "Failed to request interrupt\n");
if (rc)
goto err_cleanup;
}
rc = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
if (rc) {

View File

@ -1558,7 +1558,6 @@ static int at91_start(struct usb_gadget *gadget,
udc = container_of(gadget, struct at91_udc, gadget);
udc->driver = driver;
udc->gadget.dev.of_node = udc->pdev->dev.of_node;
udc->enabled = 1;
udc->gadget.is_selfpowered = 1;

View File

@ -2332,11 +2332,8 @@ static int usba_udc_probe(struct platform_device *pdev)
ret = devm_request_irq(&pdev->dev, irq, usba_udc_irq, 0,
"atmel_usba_udc", udc);
if (ret) {
dev_err(&pdev->dev, "Cannot request irq %d (error %d)\n",
irq, ret);
if (ret)
return ret;
}
udc->irq = irq;
if (udc->vbus_pin) {

View File

@ -1830,7 +1830,6 @@ static int bcm63xx_udc_start(struct usb_gadget *gadget,
bcm63xx_select_phy_mode(udc, true);
udc->driver = driver;
udc->gadget.dev.of_node = udc->dev->of_node;
spin_unlock_irqrestore(&udc->lock, flags);

View File

@ -142,13 +142,14 @@ int usb_ep_enable(struct usb_ep *ep)
EXPORT_SYMBOL_GPL(usb_ep_enable);
/**
* usb_ep_disable - endpoint is no longer usable
* usb_ep_disable - unconfigure an endpoint, making it no longer usable
* @ep:the endpoint being unconfigured. may not be the endpoint named "ep0".
*
* no other task may be using this endpoint when this is called.
* any pending and uncompleted requests will complete with status
* Any pending and uncompleted requests will complete with status
* indicating disconnect (-ESHUTDOWN) before this call returns.
* gadget drivers must call usb_ep_enable() again before queueing
* (However, this call will not wait for requests whose completion
* handlers have already started running but not yet returned.)
* Gadget drivers must call usb_ep_enable() again before queuing
* requests to the endpoint.
*
* This routine may be called in an atomic (interrupt) context.

View File

@ -51,8 +51,6 @@
#define POWER_BUDGET 500 /* in mA; use 8 for low-power port testing */
#define POWER_BUDGET_3 900 /* in mA */
#define DUMMY_TIMER_INT_NSECS 125000 /* 1 microframe */
static const char driver_name[] = "dummy_hcd";
static const char driver_desc[] = "USB Host+Gadget Emulator";
@ -66,6 +64,31 @@ struct dummy_hcd_module_parameters {
bool is_super_speed;
bool is_high_speed;
unsigned int num;
unsigned int tick_us;
};
/*
* Length in microseconds of an emulated microframe. The default (125,
* one high-speed microframe) preserves the historical timer rate; larger
* values slow the emulated controller down and reduce its interrupt-rate
* pressure on the host platform. Values below 125 are rejected.
*/
static int tick_us_set(const char *val,
const struct kernel_param *kp)
{
unsigned int v;
int ret = kstrtouint(val, 0, &v);
if (ret < 0 || v < 125) {
pr_err("dummy_hcd: tick_us must be >= 125\n");
return -EINVAL;
}
return param_set_uint(val, kp);
}
static const struct kernel_param_ops tick_us_ops = {
.set = tick_us_set,
.get = param_get_uint,
};
static struct dummy_hcd_module_parameters mod_data = {
@ -79,6 +102,9 @@ module_param_named(is_high_speed, mod_data.is_high_speed, bool, S_IRUGO);
MODULE_PARM_DESC(is_high_speed, "true to simulate HighSpeed connection");
module_param_named(num, mod_data.num, uint, S_IRUGO);
MODULE_PARM_DESC(num, "number of emulated controllers");
module_param_cb(tick_us, &tick_us_ops, &mod_data.tick_us, 0444);
MODULE_PARM_DESC(tick_us,
"Length in microseconds of an emulated (micro)frame (default 125 for high/super-speed emulation, 1000 for full-speed); larger values reduce the interrupt-rate pressure on the host platform");
/*-------------------------------------------------------------------------*/
/* gadget side driver data structures */
@ -244,6 +270,7 @@ struct dummy_hcd {
struct dummy *dum;
enum dummy_rh_state rh_state;
struct hrtimer timer;
ktime_t timer_interval; /* emulated microframe length */
u32 port_status;
u32 old_status;
unsigned long re_timeout;
@ -918,7 +945,7 @@ static int dummy_set_selfpowered(struct usb_gadget *_gadget, int value)
static void dummy_udc_update_ep0(struct dummy *dum)
{
if (dum->gadget.speed == USB_SPEED_SUPER)
dum->ep[0].ep.maxpacket = 9;
dum->ep[0].ep.maxpacket = 512;
else
dum->ep[0].ep.maxpacket = 64;
}
@ -1184,6 +1211,7 @@ static struct platform_driver dummy_udc_driver = {
.resume = dummy_udc_resume,
.driver = {
.name = gadget_name,
.suppress_bind_attrs = true,
},
};
@ -1333,7 +1361,7 @@ static int dummy_urb_enqueue(
/* kick the scheduler, it'll do the rest */
if (!dum_hcd->timer_pending) {
dum_hcd->timer_pending = 1;
hrtimer_start(&dum_hcd->timer, ns_to_ktime(DUMMY_TIMER_INT_NSECS),
hrtimer_start(&dum_hcd->timer, dum_hcd->timer_interval,
HRTIMER_MODE_REL_SOFT);
}
@ -2033,7 +2061,7 @@ static enum hrtimer_restart dummy_timer(struct hrtimer *t)
dum_hcd->rh_state == DUMMY_RH_RUNNING) {
/* want a 1 msec delay here */
dum_hcd->timer_pending = 1;
hrtimer_start(&dum_hcd->timer, ns_to_ktime(DUMMY_TIMER_INT_NSECS),
hrtimer_start(&dum_hcd->timer, dum_hcd->timer_interval,
HRTIMER_MODE_REL_SOFT);
}
@ -2513,6 +2541,7 @@ static DEVICE_ATTR_RO(urbs);
static int dummy_start_ss(struct dummy_hcd *dum_hcd)
{
hrtimer_setup(&dum_hcd->timer, dummy_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
dum_hcd->timer_interval = ns_to_ktime((u64)mod_data.tick_us * NSEC_PER_USEC);
dum_hcd->rh_state = DUMMY_RH_RUNNING;
dum_hcd->stream_en_ep = 0;
INIT_LIST_HEAD(&dum_hcd->urbp_list);
@ -2542,6 +2571,7 @@ static int dummy_start(struct usb_hcd *hcd)
spin_lock_init(&dum_hcd->dum->lock);
hrtimer_setup(&dum_hcd->timer, dummy_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
dum_hcd->timer_interval = ns_to_ktime((u64)mod_data.tick_us * NSEC_PER_USEC);
dum_hcd->rh_state = DUMMY_RH_RUNNING;
INIT_LIST_HEAD(&dum_hcd->urbp_list);
@ -2596,6 +2626,15 @@ static int dummy_setup(struct usb_hcd *hcd)
dum->ss_hcd->dum = dum;
hcd->speed = HCD_USB3;
hcd->self.root_hub->speed = USB_SPEED_SUPER;
/*
* xHCI-class SuperSpeed controllers have no maxpacket
* alignment requirement for non-final SG segments. Only
* relax the constraint on the SS bus; the HS bus keeps
* emulating the alignment requirement of EHCI-class
* controllers.
*/
hcd->self.no_sg_constraint = 1;
}
return 0;
}
@ -2806,6 +2845,7 @@ static struct platform_driver dummy_hcd_driver = {
.resume = dummy_hcd_resume,
.driver = {
.name = driver_name,
.suppress_bind_attrs = true,
},
};
@ -2832,6 +2872,17 @@ static int __init dummy_hcd_init(void)
return -EINVAL;
}
/*
* The emulated microframe length defaults to one high-speed
* microframe (125 us). When neither is_high_speed nor
* is_super_speed is set the emulation is full-speed, where the
* natural scheduling unit is the 1-ms frame, so default to
* 1000 us instead.
*/
if (!mod_data.tick_us)
mod_data.tick_us = (mod_data.is_super_speed ||
mod_data.is_high_speed) ? 125 : 1000;
for (i = 0; i < mod_data.num; i++) {
the_hcd_pdev[i] = platform_device_alloc(driver_name, i);
if (!the_hcd_pdev[i]) {

View File

@ -2465,9 +2465,6 @@ static int fsl_udc_probe(struct platform_device *pdev)
udc_controller->gadget.speed = USB_SPEED_UNKNOWN;
udc_controller->gadget.name = driver_name;
/* Setup gadget.dev and register with kernel */
udc_controller->gadget.dev.of_node = pdev->dev.of_node;
if (!IS_ERR_OR_NULL(udc_controller->transceiver))
udc_controller->gadget.is_otg = 1;

View File

@ -2889,7 +2889,6 @@ static int lpc32xx_start(struct usb_gadget *gadget,
}
udc->driver = driver;
udc->gadget.dev.of_node = udc->dev->of_node;
udc->enabled = 1;
udc->gadget.is_selfpowered = 1;
udc->vbus = 0;

View File

@ -1894,10 +1894,8 @@ static int r8a66597_probe(struct platform_device *pdev)
ret = devm_request_irq(dev, irq, r8a66597_irq, IRQF_SHARED,
udc_name, r8a66597);
if (ret < 0) {
dev_err(dev, "request_irq error (%d)\n", ret);
if (ret < 0)
goto clean_up2;
}
INIT_LIST_HEAD(&r8a66597->gadget.ep_list);
r8a66597->gadget.ep0 = &r8a66597->ep[0].ep;

View File

@ -3335,19 +3335,15 @@ static int usbf_probe(struct platform_device *pdev)
if (irq < 0)
return irq;
ret = devm_request_irq(dev, irq, usbf_epc_irq, 0, "usbf-epc", udc);
if (ret) {
dev_err(dev, "cannot request irq %d err %d\n", irq, ret);
if (ret)
return ret;
}
irq = platform_get_irq(pdev, 1);
if (irq < 0)
return irq;
ret = devm_request_irq(dev, irq, usbf_ahb_epc_irq, 0, "usbf-ahb-epc", udc);
if (ret) {
dev_err(dev, "cannot request irq %d err %d\n", irq, ret);
if (ret)
return ret;
}
usbf_reg_bitset(udc, USBF_REG_AHBMCTR, USBF_SYS_WBURST_TYPE);

View File

@ -192,10 +192,8 @@ static int udc_plat_probe(struct platform_device *pdev)
ret = devm_request_irq(dev, udc->irq, udc_irq, IRQF_SHARED,
"snps-udc", udc);
if (ret < 0) {
dev_err(dev, "Request irq %d failed for UDC\n", udc->irq);
if (ret < 0)
goto exit_dma;
}
platform_set_drvdata(pdev, udc);
udc->chiprev = UDC_BCM_REV;

View File

@ -3803,11 +3803,8 @@ static int tegra_xudc_probe(struct platform_device *pdev)
err = devm_request_irq(&pdev->dev, xudc->irq, tegra_xudc_irq, 0,
dev_name(&pdev->dev), xudc);
if (err < 0) {
dev_err(xudc->dev, "failed to claim IRQ#%u: %d\n", xudc->irq,
err);
if (err < 0)
return err;
}
xudc->clks = devm_kcalloc(&pdev->dev, xudc->soc->num_clks, sizeof(*xudc->clks),
GFP_KERNEL);

View File

@ -211,6 +211,19 @@ config USB_EHCI_PCI
depends on USB_PCI
default y
config USB_EHCI_DEVIANT_PORT_STATUS_REG
bool
# Used for hosts with a deviant port status register location
config USB_EHCI_PORT_RESET_HOOKS
bool
# Used for hosts with controller-specific port reset sequencing
config USB_EHCI_PORT_SPEED_HOOK
bool
depends on USB_EHCI_ROOT_HUB_TT
# Used for hosts with controller-specific port speed reporting
config XPS_USB_HCD_XILINX
bool "Use Xilinx usb host EHCI controller core"
depends on (PPC32 || MICROBLAZE)

View File

@ -869,8 +869,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf)
for (i = 1; i <= HCS_N_PORTS(ehci->hcs_params); i++) {
temp = dbg_port_buf(scratch, sizeof(scratch), label, i,
ehci_readl(ehci,
&ehci->regs->port_status[i - 1]));
ehci_readl(ehci, ehci_portsc(ehci, i - 1)));
temp = scnprintf(next, size, fmt, temp, scratch);
size -= temp;
next += temp;

View File

@ -187,7 +187,8 @@ static int ehci_halt (struct ehci_hcd *ehci)
/* disable any irqs left enabled by previous code */
ehci_writel(ehci, 0, &ehci->regs->intr_enable);
if (ehci_is_TDI(ehci) && !tdi_in_host_mode(ehci)) {
if (ehci_is_TDI(ehci) && !ehci->no_tdi_mode &&
!tdi_in_host_mode(ehci)) {
spin_unlock_irq(&ehci->lock);
return 0;
}
@ -254,7 +255,7 @@ int ehci_reset(struct ehci_hcd *ehci)
if (retval)
return retval;
if (ehci_is_TDI(ehci))
if (ehci_is_TDI(ehci) && !ehci->no_tdi_mode)
tdi_reset (ehci);
if (ehci->debug)
@ -325,7 +326,7 @@ static void ehci_turn_off_all_ports(struct ehci_hcd *ehci)
ehci_port_power(ehci, port, false);
spin_lock_irq(&ehci->lock);
ehci_writel(ehci, PORT_RWC_BITS,
&ehci->regs->port_status[port]);
ehci_portsc(ehci, port));
}
}
@ -342,10 +343,12 @@ static void ehci_silence_controller(struct ehci_hcd *ehci)
ehci_turn_off_all_ports(ehci);
/* make BIOS/etc use companion controller during reboot */
ehci_writel(ehci, 0, &ehci->regs->configured_flag);
if (!ehci->no_configured_flag) {
ehci_writel(ehci, 0, &ehci->regs->configured_flag);
/* unblock posted writes */
ehci_readl(ehci, &ehci->regs->configured_flag);
/* unblock posted writes */
ehci_readl(ehci, &ehci->regs->configured_flag);
}
spin_unlock_irq(&ehci->lock);
}
@ -629,7 +632,8 @@ static int ehci_run (struct usb_hcd *hcd)
*/
down_write(&ehci_cf_port_reset_rwsem);
ehci->rh_state = EHCI_RH_RUNNING;
ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
if (!ehci->no_configured_flag)
ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
/* Wait until HC become operational */
ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
@ -811,8 +815,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
/* leverage per-port change bits feature */
if (!(ppcd & (1 << i)))
continue;
pstatus = ehci_readl(ehci,
&ehci->regs->port_status[i]);
pstatus = ehci_readl(ehci, ehci_portsc(ehci, i));
if (pstatus & PORT_OWNER)
continue;
@ -910,6 +913,8 @@ static int ehci_urb_enqueue (
case PIPE_ISOCHRONOUS:
if (urb->dev->speed == USB_SPEED_HIGH)
return itd_submit (ehci, urb, mem_flags);
else if (ehci->no_fsls_isoc)
return -EOPNOTSUPP;
else
return sitd_submit (ehci, urb, mem_flags);
}
@ -1109,7 +1114,7 @@ static void ehci_remove_device(struct usb_hcd *hcd, struct usb_device *udev)
/* Clear wakeup signal locked in zhaoxin platform when device plug in. */
static void ehci_zx_wakeup_clear(struct ehci_hcd *ehci)
{
u32 __iomem *reg = &ehci->regs->port_status[4];
u32 __iomem *reg = ehci_portsc(ehci, 4);
u32 t1 = ehci_readl(ehci, reg);
t1 &= (u32)~0xf0000;
@ -1185,7 +1190,8 @@ int ehci_resume(struct usb_hcd *hcd, bool force_reset)
* then we maintained suspend power.
* Just undo the effect of ehci_suspend().
*/
if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF &&
if ((ehci->no_configured_flag ||
ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) &&
!force_reset) {
int mask = INTR_MASK;
@ -1217,7 +1223,8 @@ int ehci_resume(struct usb_hcd *hcd, bool force_reset)
goto skip;
ehci_writel(ehci, ehci->command, &ehci->regs->command);
ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
if (!ehci->no_configured_flag)
ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
ehci->rh_state = EHCI_RH_SUSPENDED;

View File

@ -15,6 +15,44 @@
/*-------------------------------------------------------------------------*/
/*
* Some EHCI controllers have a Transaction Translator built into the
* root hub. This is a non-standard feature. Each controller will need
* to add code to the following function, and call it as needed.
*/
/*
* In a bunch of EHCI implementations with transaction translators,
* the port speed can be found in the reserved bits in position 26 and
* 27. Implementations with the HOSTPC register will have this in
* bits 25 and 26 of the HOSTPC registers.
*/
#define PORTSC_SPEED_BITS(a) (((a) >> 26) & 3)
#define HOSTPC_SPEED_BITS(a) (((a) >> 25) & 3)
/* Returns the speed of a device attached to a port on the root hub. */
static unsigned int ehci_port_speed(struct ehci_hcd *ehci,
unsigned int port, unsigned int speed)
{
#ifdef CONFIG_USB_EHCI_PORT_SPEED_HOOK
if (ehci->get_port_speed)
return ehci->get_port_speed(ehci, port);
#endif
if (!IS_ENABLED(CONFIG_USB_EHCI_ROOT_HUB_TT) || !ehci_is_TDI(ehci))
return USB_PORT_STAT_HIGH_SPEED;
switch (speed) {
case 0:
return 0;
case 1:
return USB_PORT_STAT_LOW_SPEED;
case 2:
default:
return USB_PORT_STAT_HIGH_SPEED;
}
}
#define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E)
#ifdef CONFIG_PM
@ -55,7 +93,7 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci)
port = HCS_N_PORTS(ehci->hcs_params);
while (port--) {
if (test_bit(port, &ehci->owned_ports)) {
reg = &ehci->regs->port_status[port];
reg = ehci_portsc(ehci, port);
status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
if (!(status & PORT_POWER))
ehci_port_power(ehci, port, true);
@ -69,7 +107,7 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci)
port = HCS_N_PORTS(ehci->hcs_params);
while (port--) {
if (test_bit(port, &ehci->owned_ports)) {
reg = &ehci->regs->port_status[port];
reg = ehci_portsc(ehci, port);
status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
/* Port already owned by companion? */
@ -106,7 +144,7 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci)
* but if something went wrong the port must not
* remain enabled.
*/
reg = &ehci->regs->port_status[port];
reg = ehci_portsc(ehci, port);
status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
if (status & PORT_OWNER)
ehci_writel(ehci, status | PORT_CSC, reg);
@ -137,7 +175,7 @@ static int ehci_port_change(struct ehci_hcd *ehci)
*/
while (i--)
if (ehci_readl(ehci, &ehci->regs->port_status[i]) & PORT_CSC)
if (ehci_readl(ehci, ehci_portsc(ehci, i)) & PORT_CSC)
return 1;
return 0;
@ -175,7 +213,7 @@ void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
port = HCS_N_PORTS(ehci->hcs_params);
while (port--) {
u32 __iomem *reg = &ehci->regs->port_status[port];
u32 __iomem *reg = ehci_portsc(ehci, port);
u32 t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
u32 t2 = t1 & ~PORT_WAKE_BITS;
@ -254,7 +292,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
fs_idle_delay = false;
port = HCS_N_PORTS(ehci->hcs_params);
while (port--) {
u32 __iomem *reg = &ehci->regs->port_status [port];
u32 __iomem *reg = ehci_portsc(ehci, port);
u32 t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
u32 t2 = t1 & ~PORT_WAKE_BITS;
@ -287,8 +325,9 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
* sake, add a delay if we need one.
*/
if ((t2 & PORT_WKDISC_E) &&
ehci_port_speed(ehci, t2) ==
USB_PORT_STAT_HIGH_SPEED)
ehci_port_speed(ehci, port,
PORTSC_SPEED_BITS(t2)) ==
USB_PORT_STAT_HIGH_SPEED)
fs_idle_delay = true;
ehci_writel(ehci, t2, reg);
changed = 1;
@ -426,7 +465,7 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
*/
i = HCS_N_PORTS(ehci->hcs_params);
while (i--) {
temp = ehci_readl(ehci, &ehci->regs->port_status[i]);
temp = ehci_readl(ehci, ehci_portsc(ehci, i));
if ((temp & PORT_PE) &&
!(temp & (PORT_SUSPEND | PORT_RESUME))) {
ehci_dbg(ehci, "Port status(0x%x) is wrong\n", temp);
@ -463,14 +502,14 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
/* manually resume the ports we suspended during bus_suspend() */
i = HCS_N_PORTS (ehci->hcs_params);
while (i--) {
temp = ehci_readl(ehci, &ehci->regs->port_status [i]);
temp = ehci_readl(ehci, ehci_portsc(ehci, i));
temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS);
if (test_bit(i, &ehci->bus_suspended) &&
(temp & PORT_SUSPEND)) {
temp |= PORT_RESUME;
set_bit(i, &resume_needed);
}
ehci_writel(ehci, temp, &ehci->regs->port_status [i]);
ehci_writel(ehci, temp, ehci_portsc(ehci, i));
}
/*
@ -487,10 +526,10 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
i = HCS_N_PORTS (ehci->hcs_params);
while (i--) {
temp = ehci_readl(ehci, &ehci->regs->port_status [i]);
temp = ehci_readl(ehci, ehci_portsc(ehci, i));
if (test_bit(i, &resume_needed)) {
temp &= ~(PORT_RWC_BITS | PORT_SUSPEND | PORT_RESUME);
ehci_writel(ehci, temp, &ehci->regs->port_status [i]);
ehci_writel(ehci, temp, ehci_portsc(ehci, i));
}
}
@ -540,7 +579,7 @@ static void set_owner(struct ehci_hcd *ehci, int portnum, int new_owner)
u32 port_status;
int try;
status_reg = &ehci->regs->port_status[portnum];
status_reg = ehci_portsc(ehci, portnum);
/*
* The controller won't set the OWNER bit if the port is
@ -661,7 +700,7 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
for (i = 0; i < ports; i++) {
/* leverage per-port change bits feature */
if (ppcd & (1 << i))
temp = ehci_readl(ehci, &ehci->regs->port_status[i]);
temp = ehci_readl(ehci, ehci_portsc(ehci, i));
else
temp = 0;
@ -753,7 +792,7 @@ int ehci_hub_control(
temp = (wIndex - 1) & 0xff;
if (temp >= HCS_N_PORTS_MAX)
temp = 0;
status_reg = &ehci->regs->port_status[temp];
status_reg = ehci_portsc(ehci, temp);
hostpc_reg = &ehci->regs->hostpc[temp];
/*
@ -963,6 +1002,13 @@ int ehci_hub_control(
/* see what we found out */
temp = check_reset_complete (ehci, wIndex, status_reg,
ehci_readl(ehci, status_reg));
#ifdef CONFIG_USB_EHCI_PORT_RESET_HOOKS
if (ehci->post_port_reset) {
retval = ehci->post_port_reset(ehci, wIndex);
if (retval)
goto error_exit;
}
#endif
}
/* transfer dedicated ports to the companion hc */
@ -983,12 +1029,14 @@ int ehci_hub_control(
if (temp & PORT_CONNECT) {
status |= USB_PORT_STAT_CONNECTION;
// status may be from integrated TT
if (ehci->has_hostpc) {
temp1 = ehci_readl(ehci, hostpc_reg);
status |= ehci_port_speed(ehci, temp1);
} else
status |= ehci_port_speed(ehci, temp);
status |= ehci_port_speed(ehci, wIndex,
HOSTPC_SPEED_BITS(temp1));
} else {
status |= ehci_port_speed(ehci, wIndex,
PORTSC_SPEED_BITS(temp));
}
}
if (temp & PORT_PE)
status |= USB_PORT_STAT_ENABLE;
@ -1121,7 +1169,16 @@ int ehci_hub_control(
*/
if (ehci_has_fsl_hs_errata(ehci))
temp |= (1 << PORTSC_FSL_PFSC);
#ifdef CONFIG_USB_EHCI_PORT_RESET_HOOKS
if (ehci->pre_port_reset) {
retval = ehci->pre_port_reset(ehci, wIndex);
if (retval)
goto error_exit;
}
#endif
}
ehci_writel(ehci, temp, status_reg);
break;
@ -1149,8 +1206,7 @@ int ehci_hub_control(
/* Put all enabled ports into suspend */
while (ports--) {
u32 __iomem *sreg =
&ehci->regs->port_status[ports];
u32 __iomem *sreg = ehci_portsc(ehci, ports);
temp = ehci_readl(ehci, sreg) & ~PORT_RWC_BITS;
if (temp & PORT_PE)
@ -1200,14 +1256,14 @@ static int ehci_port_handed_over(struct usb_hcd *hcd, int portnum)
if (ehci_is_TDI(ehci))
return 0;
reg = &ehci->regs->port_status[portnum - 1];
reg = ehci_portsc(ehci, portnum - 1);
return ehci_readl(ehci, reg) & PORT_OWNER;
}
static int ehci_port_power(struct ehci_hcd *ehci, int portnum, bool enable)
{
struct usb_hcd *hcd = ehci_to_hcd(ehci);
u32 __iomem *status_reg = &ehci->regs->port_status[portnum];
u32 __iomem *status_reg = ehci_portsc(ehci, portnum);
u32 temp = ehci_readl(ehci, status_reg) & ~PORT_RWC_BITS;
if (enable)

View File

@ -18,8 +18,6 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
static const struct hc_driver ehci_ppc_of_hc_driver = {
@ -96,7 +94,9 @@ static int ehci_hcd_ppc_of_probe(struct platform_device *op)
struct device_node *dn = op->dev.of_node;
struct usb_hcd *hcd;
struct ehci_hcd *ehci = NULL;
struct resource res;
struct resource *res;
struct resource ohci_res;
void __iomem *regs;
int irq;
int rv;
@ -107,39 +107,30 @@ static int ehci_hcd_ppc_of_probe(struct platform_device *op)
dev_dbg(&op->dev, "initializing PPC-OF USB Controller\n");
rv = of_address_to_resource(dn, 0, &res);
if (rv)
return rv;
regs = devm_platform_get_and_ioremap_resource(op, 0, &res);
if (IS_ERR(regs))
return PTR_ERR(regs);
irq = platform_get_irq(op, 0);
if (irq < 0)
return irq;
hcd = usb_create_hcd(&ehci_ppc_of_hc_driver, &op->dev, "PPC-OF USB");
if (!hcd)
return -ENOMEM;
hcd->rsrc_start = res.start;
hcd->rsrc_len = resource_size(&res);
irq = irq_of_parse_and_map(dn, 0);
if (!irq) {
dev_err(&op->dev, "%s: irq_of_parse_and_map failed\n",
__FILE__);
rv = -EBUSY;
goto err_irq;
}
hcd->regs = devm_ioremap_resource(&op->dev, &res);
if (IS_ERR(hcd->regs)) {
rv = PTR_ERR(hcd->regs);
goto err_ioremap;
}
hcd->regs = regs;
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
ehci = hcd_to_ehci(hcd);
np = of_find_compatible_node(NULL, NULL, "ibm,usb-ohci-440epx");
if (np != NULL) {
/* claim we really affected by usb23 erratum */
if (!of_address_to_resource(np, 0, &res))
if (!of_address_to_resource(np, 0, &ohci_res))
ehci->ohci_hcctrl_reg =
devm_ioremap(&op->dev,
res.start + OHCI_HCCTRL_OFFSET,
ohci_res.start + OHCI_HCCTRL_OFFSET,
OHCI_HCCTRL_LEN);
else
pr_debug("%s: no ohci offset in fdt\n", __FILE__);
@ -170,14 +161,12 @@ static int ehci_hcd_ppc_of_probe(struct platform_device *op)
rv = usb_add_hcd(hcd, irq, 0);
if (rv)
goto err_ioremap;
goto err;
device_wakeup_enable(hcd->self.controller);
return 0;
err_ioremap:
irq_dispose_mapping(irq);
err_irq:
err:
usb_put_hcd(hcd);
return rv;
@ -196,8 +185,6 @@ static void ehci_hcd_ppc_of_remove(struct platform_device *op)
usb_remove_hcd(hcd);
irq_dispose_mapping(hcd->irq);
/* use request_mem_region to test if the ohci driver is loaded. if so
* ensure the ohci core is operational.
*/

View File

@ -199,7 +199,8 @@ static void ehci_handle_controller_death(struct ehci_hcd *ehci)
/* Clean up the mess */
ehci->rh_state = EHCI_RH_HALTED;
ehci_writel(ehci, 0, &ehci->regs->configured_flag);
if (!ehci->no_configured_flag)
ehci_writel(ehci, 0, &ehci->regs->configured_flag);
ehci_writel(ehci, 0, &ehci->regs->intr_enable);
ehci_work(ehci);
end_unlink_async(ehci);

View File

@ -123,6 +123,19 @@ struct ehci_hcd { /* one per controller */
struct ehci_caps __iomem *caps;
struct ehci_regs __iomem *regs;
struct ehci_dbg_port __iomem *debug;
#ifdef CONFIG_USB_EHCI_DEVIANT_PORT_STATUS_REG
u32 __iomem *port_status;
#endif
#ifdef CONFIG_USB_EHCI_PORT_SPEED_HOOK
unsigned int (*get_port_speed)(struct ehci_hcd *ehci,
unsigned int port);
#endif
#ifdef CONFIG_USB_EHCI_PORT_RESET_HOOKS
int (*pre_port_reset)(struct ehci_hcd *ehci,
unsigned int port);
int (*post_port_reset)(struct ehci_hcd *ehci,
unsigned int port);
#endif
__u32 hcs_params; /* cached register copy */
spinlock_t lock;
@ -222,6 +235,9 @@ struct ehci_hcd { /* one per controller */
unsigned spurious_oc:1;
unsigned is_aspeed:1;
unsigned zx_wakeup_clear_needed:1;
unsigned no_configured_flag:1;
unsigned no_tdi_mode:1;
unsigned no_fsls_isoc:1;
/* required for usb32 quirk */
#define OHCI_CTRL_HCFS (3 << 6)
@ -278,6 +294,19 @@ static inline struct usb_hcd *ehci_to_hcd(struct ehci_hcd *ehci)
/*-------------------------------------------------------------------------*/
static inline u32 __iomem *ehci_portsc(struct ehci_hcd *ehci,
unsigned int port)
{
#ifdef CONFIG_USB_EHCI_DEVIANT_PORT_STATUS_REG
if (ehci->port_status)
return ehci->port_status + port;
#endif
return &ehci->regs->port_status[port];
}
/*-------------------------------------------------------------------------*/
#define QTD_NEXT(ehci, dma) cpu_to_hc32(ehci, (u32)dma)
/*
@ -651,38 +680,12 @@ struct ehci_tt {
#ifdef CONFIG_USB_EHCI_ROOT_HUB_TT
/*
* Some EHCI controllers have a Transaction Translator built into the
* root hub. This is a non-standard feature. Each controller will need
* to add code to the following inline functions, and call them as
* needed (mostly in root hub code).
*/
#define ehci_is_TDI(e) (ehci_to_hcd(e)->has_tt)
/* Returns the speed of a device attached to a port on the root hub. */
static inline unsigned int
ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc)
{
if (ehci_is_TDI(ehci)) {
switch ((portsc >> (ehci->has_hostpc ? 25 : 26)) & 3) {
case 0:
return 0;
case 1:
return USB_PORT_STAT_LOW_SPEED;
case 2:
default:
return USB_PORT_STAT_HIGH_SPEED;
}
}
return USB_PORT_STAT_HIGH_SPEED;
}
#else
#define ehci_is_TDI(e) (0)
#define ehci_port_speed(ehci, portsc) USB_PORT_STAT_HIGH_SPEED
#endif
/*-------------------------------------------------------------------------*/

View File

@ -708,6 +708,7 @@ static int cvmx_usb_initialize(struct device *dev,
int channel;
int divisor;
int retries = 0;
int status;
union cvmx_usbcx_hcfg usbcx_hcfg;
union cvmx_usbnx_clk_ctl usbn_clk_ctl;
union cvmx_usbcx_gintsts usbc_gintsts;
@ -872,7 +873,8 @@ static int cvmx_usb_initialize(struct device *dev,
if (usbcx_gahbcfg.u32 == 0xffffffff || usbcx_gahbcfg.u32 == 0) {
dev_err(dev, "USB core is not responding (GHWCFG3=0x%08x)\n",
usbcx_gahbcfg.u32);
return -ENODEV;
status = -ENODEV;
goto err_shutdown;
}
usbcx_gahbcfg.u32 = 0;
@ -961,14 +963,27 @@ static int cvmx_usb_initialize(struct device *dev,
dev_dbg(dev, "gintsts after reset: 0x%x\n", (int)usbc_gintsts.u32);
if (!usbc_gintsts.s.disconnint && !usbc_gintsts.s.prtint)
return 0;
if (retries++ >= 5)
return -EAGAIN;
if (retries++ >= 5) {
status = -EAGAIN;
goto err_shutdown;
}
dev_info(dev, "controller reset failed (gintsts=0x%x) - retrying\n",
(int)usbc_gintsts.u32);
msleep(50);
cvmx_usb_shutdown(usb);
msleep(50);
goto retry;
err_shutdown:
/*
* Steps 2b-2e above selected the reference clock, enabled
* USBN_CLK_CTL and took the PHY out of reset, and the caller only
* frees the HCD, so hand the block back before giving up. The pipe
* lists cvmx_usb_shutdown() checks are initialised by the caller
* before it gets here, so it cannot refuse with -EBUSY.
*/
cvmx_usb_shutdown(usb);
return status;
}
/**
@ -1923,6 +1938,7 @@ static void cvmx_usb_schedule(struct octeon_hcd *usb, int is_sof)
struct cvmx_usb_pipe *pipe;
int need_sof;
u64 min_due;
bool due_now;
enum cvmx_usb_transfer ttype;
if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA) {
@ -1964,15 +1980,32 @@ static void cvmx_usb_schedule(struct octeon_hcd *usb, int is_sof)
*/
need_sof = 0;
min_due = ~0ull;
due_now = false;
for (ttype = CVMX_USB_TRANSFER_CONTROL;
ttype <= CVMX_USB_TRANSFER_INTERRUPT; ttype++) {
list_for_each_entry(pipe, &usb->active_pipes[ttype], node) {
/*
* Do not sleep past a pipe that is only retried from a
* SOF: a periodic one that is due but was not started
* (no idle channel, closed split window, another pipe's
* split active), or any pipe waiting for the frame its
* split complete may go out in. Other control and bulk
* pipes are left out: their next_tx_frame is not updated
* on completion, so they always look due.
*/
if (pipe->split_sc_frame != -1 ||
(pipe->next_tx_frame <= usb->frame_number &&
(ttype == CVMX_USB_TRANSFER_ISOCHRONOUS ||
ttype == CVMX_USB_TRANSFER_INTERRUPT)))
due_now = true;
if (pipe->next_tx_frame > usb->frame_number &&
pipe->next_tx_frame < min_due)
min_due = pipe->next_tx_frame;
}
}
if (min_due != ~0ull) {
if (due_now) {
need_sof = 1;
} else if (min_due != ~0ull) {
u64 delta = min_due - usb->frame_number;
/*

View File

@ -184,7 +184,7 @@ ohci_dump_status (struct ohci_hcd *controller, char **next, unsigned *size)
\
(temp & RH_PS_PES) ? " PES" : "", \
(temp & RH_PS_CCS) ? " CCS" : "" \
);
)
static void

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