mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-16 02:14:58 +00:00
USB / Thunderbolt changes for 6.10-rc1
Here is the big set of USB and Thunderbolt changes for 6.10-rc1. Nothing hugely earth-shattering, just constant forward progress for hardware support of new devices and cleanups over the drivers. Included in here are: - Thunderbolt / USB 4 driver updates - typec driver updates - dwc3 driver updates - gadget driver updates - uss720 driver id additions and fixes (people use USB->arallel port devices still!) - onboard-hub driver rename and additions for new hardware - xhci driver updates - other small USB driver updates and additions for quirks and api changes All of these have been in linux-next for a while with no reported problems. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCZk4E7w8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+yn0kACgr3uvAWXvfb9R4vCpC65F4f49ZQwAoIkHQBPl /5HdrlIIYW2OzdUixH3e =e3pI -----END PGP SIGNATURE----- Merge tag 'usb-6.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb Pull USB / Thunderbolt updates from Greg KH: "Here is the big set of USB and Thunderbolt changes for 6.10-rc1. Nothing hugely earth-shattering, just constant forward progress for hardware support of new devices and cleanups over the drivers. Included in here are: - Thunderbolt / USB 4 driver updates - typec driver updates - dwc3 driver updates - gadget driver updates - uss720 driver id additions and fixes (people use USB->arallel port devices still!) - onboard-hub driver rename and additions for new hardware - xhci driver updates - other small USB driver updates and additions for quirks and api changes All of these have been in linux-next for a while with no reported problems" * tag 'usb-6.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (154 commits) drm/bridge: aux-hpd-bridge: correct devm_drm_dp_hpd_bridge_add() stub usb: fotg210: Add missing kernel doc description usb: dwc3: core: Fix unused variable warning in core driver usb: typec: tipd: rely on i2c_get_match_data() usb: typec: tipd: fix event checking for tps6598x usb: typec: tipd: fix event checking for tps25750 dt-bindings: usb: qcom,dwc3: fix interrupt max items usb: fotg210: Use *-y instead of *-objs in Makefile usb: phy: tegra: Replace of_gpio.h by proper one usb: typec: ucsi: displayport: Fix potential deadlock usb: typec: qcom-pmic-typec: split HPD bridge alloc and registration usb: musc: Remove unused list 'buffers' usb: dwc3: Wait unconditionally after issuing EndXfer command usb: gadget: u_audio: Clear uac pointer when freed. usb: gadget: u_audio: Fix race condition use of controls after free during gadget unbind. dt-bindings: usb: dwc3: Add QDU1000 compatible usb: core: Remove the useless struct usb_devmap which is just a bitmap MAINTAINERS: Remove {ehci,uhci}-platform.c from ARM/VT8500 entry USB: usb_parse_endpoint: ignore reserved bits usb: xhci: compact 'trb_in_td()' arguments ...
This commit is contained in:
commit
89601f675b
@ -5,4 +5,5 @@ Contact: Matthias Kaehlcke <matthias@kaehlcke.net>
|
||||
linux-usb@vger.kernel.org
|
||||
Description:
|
||||
(RW) Controls whether the USB hub remains always powered
|
||||
during system suspend or not.
|
||||
during system suspend or not. This attribute is not
|
||||
available for non-hub devices.
|
63
Documentation/devicetree/bindings/sound/xmos,xvf3500.yaml
Normal file
63
Documentation/devicetree/bindings/sound/xmos,xvf3500.yaml
Normal file
@ -0,0 +1,63 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/sound/xmos,xvf3500.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: XMOS XVF3500 VocalFusion Voice Processor
|
||||
|
||||
maintainers:
|
||||
- Javier Carrasco <javier.carrasco@wolfvision.net>
|
||||
|
||||
description:
|
||||
The XMOS XVF3500 VocalFusion Voice Processor is a low-latency, 32-bit
|
||||
multicore controller for voice processing.
|
||||
https://www.xmos.com/xvf3500/
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/usb/usb-device.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: usb20b1,0013
|
||||
|
||||
reg: true
|
||||
|
||||
reset-gpios:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply:
|
||||
description:
|
||||
Regulator for the 1V0 supply.
|
||||
|
||||
vddio-supply:
|
||||
description:
|
||||
Regulator for the 3V3 supply.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- reset-gpios
|
||||
- vdd-supply
|
||||
- vddio-supply
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
usb {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
voice_processor: voice-processor@1 {
|
||||
compatible = "usb20b1,0013";
|
||||
reg = <1>;
|
||||
reset-gpios = <&gpio 5 GPIO_ACTIVE_LOW>;
|
||||
vdd-supply = <&vcc1v0>;
|
||||
vddio-supply = <&vcc3v3>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
200
Documentation/devicetree/bindings/usb/chipidea,usb2-common.yaml
Normal file
200
Documentation/devicetree/bindings/usb/chipidea,usb2-common.yaml
Normal file
@ -0,0 +1,200 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/usb/chipidea,usb2-common.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: USB2 ChipIdea USB controller Common Properties
|
||||
|
||||
maintainers:
|
||||
- Xu Yang <xu.yang_2@nxp.com>
|
||||
|
||||
properties:
|
||||
reg:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
interrupts:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
clocks:
|
||||
minItems: 1
|
||||
maxItems: 3
|
||||
|
||||
clock-names:
|
||||
minItems: 1
|
||||
maxItems: 3
|
||||
|
||||
dr_mode: true
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
reset-names:
|
||||
maxItems: 1
|
||||
|
||||
"#reset-cells":
|
||||
const: 1
|
||||
|
||||
phy_type: true
|
||||
|
||||
itc-setting:
|
||||
description:
|
||||
interrupt threshold control register control, the setting should be
|
||||
aligned with ITC bits at register USBCMD.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
ahb-burst-config:
|
||||
description:
|
||||
it is vendor dependent, the required value should be aligned with
|
||||
AHBBRST at SBUSCFG, the range is from 0x0 to 0x7. This property is
|
||||
used to change AHB burst configuration, check the chipidea spec for
|
||||
meaning of each value. If this property is not existed, it will use
|
||||
the reset value.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 0x0
|
||||
maximum: 0x7
|
||||
|
||||
tx-burst-size-dword:
|
||||
description:
|
||||
it is vendor dependent, the tx burst size in dword (4 bytes), This
|
||||
register represents the maximum length of a the burst in 32-bit
|
||||
words while moving data from system memory to the USB bus, the value
|
||||
of this property will only take effect if property "ahb-burst-config"
|
||||
is set to 0, if this property is missing the reset default of the
|
||||
hardware implementation will be used.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 0x0
|
||||
maximum: 0x20
|
||||
|
||||
rx-burst-size-dword:
|
||||
description:
|
||||
it is vendor dependent, the rx burst size in dword (4 bytes), This
|
||||
register represents the maximum length of a the burst in 32-bit words
|
||||
while moving data from the USB bus to system memory, the value of
|
||||
this property will only take effect if property "ahb-burst-config"
|
||||
is set to 0, if this property is missing the reset default of the
|
||||
hardware implementation will be used.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 0x0
|
||||
maximum: 0x20
|
||||
|
||||
extcon:
|
||||
description:
|
||||
Phandles to external connector devices. First phandle should point
|
||||
to external connector, which provide "USB" cable events, the second
|
||||
should point to external connector device, which provide "USB-HOST"
|
||||
cable events. If one of the external connector devices is not
|
||||
required, empty <0> phandle should be specified.
|
||||
$ref: /schemas/types.yaml#/definitions/phandle-array
|
||||
minItems: 1
|
||||
items:
|
||||
- description: vbus extcon
|
||||
- description: id extcon
|
||||
|
||||
phy-clkgate-delay-us:
|
||||
description:
|
||||
The delay time (us) between putting the PHY into low power mode and
|
||||
gating the PHY clock.
|
||||
|
||||
non-zero-ttctrl-ttha:
|
||||
description:
|
||||
After setting this property, the value of register ttctrl.ttha
|
||||
will be 0x7f; if not, the value will be 0x0, this is the default
|
||||
value. It needs to be very carefully for setting this property, it
|
||||
is recommended that consult with your IC engineer before setting
|
||||
this value. On the most of chipidea platforms, the "usage_tt" flag
|
||||
at RTL is 0, so this property only affects siTD.
|
||||
|
||||
If this property is not set, the max packet size is 1023 bytes, and
|
||||
if the total of packet size for previous transactions are more than
|
||||
256 bytes, it can't accept any transactions within this frame. The
|
||||
use case is single transaction, but higher frame rate.
|
||||
|
||||
If this property is set, the max packet size is 188 bytes, it can
|
||||
handle more transactions than above case, it can accept transactions
|
||||
until it considers the left room size within frame is less than 188
|
||||
bytes, software needs to make sure it does not send more than 90%
|
||||
maximum_periodic_data_per_frame. The use case is multiple
|
||||
transactions, but less frame rate.
|
||||
type: boolean
|
||||
|
||||
mux-controls:
|
||||
description:
|
||||
The mux control for toggling host/device output of this controller.
|
||||
It's expected that a mux state of 0 indicates device mode and a mux
|
||||
state of 1 indicates host mode.
|
||||
maxItems: 1
|
||||
|
||||
mux-control-names:
|
||||
const: usb_switch
|
||||
|
||||
pinctrl-names:
|
||||
description:
|
||||
Names for optional pin modes in "default", "host", "device".
|
||||
In case of HSIC-mode, "idle" and "active" pin modes are mandatory.
|
||||
In this case, the "idle" state needs to pull down the data and
|
||||
strobe pin and the "active" state needs to pull up the strobe pin.
|
||||
oneOf:
|
||||
- items:
|
||||
- const: idle
|
||||
- const: active
|
||||
- items:
|
||||
- const: default
|
||||
- const: host
|
||||
- const: device
|
||||
- items:
|
||||
- const: default
|
||||
- enum:
|
||||
- host
|
||||
- device
|
||||
- items:
|
||||
- const: default
|
||||
|
||||
pinctrl-0:
|
||||
maxItems: 1
|
||||
|
||||
pinctrl-1:
|
||||
maxItems: 1
|
||||
|
||||
phys:
|
||||
maxItems: 1
|
||||
|
||||
phy-names:
|
||||
const: usb-phy
|
||||
|
||||
vbus-supply:
|
||||
description: reference to the VBUS regulator.
|
||||
|
||||
usb-phy:
|
||||
description: phandle for the PHY device. Use "phys" instead.
|
||||
maxItems: 1
|
||||
deprecated: true
|
||||
|
||||
port:
|
||||
description:
|
||||
Any connector to the data bus of this controller should be modelled
|
||||
using the OF graph bindings specified, if the "usb-role-switch"
|
||||
property is used.
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
|
||||
reset-gpios:
|
||||
maxItems: 1
|
||||
|
||||
dependencies:
|
||||
port: [ usb-role-switch ]
|
||||
mux-controls: [ mux-control-names ]
|
||||
|
||||
required:
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
allOf:
|
||||
- $ref: usb-hcd.yaml#
|
||||
- $ref: usb-drd.yaml#
|
||||
|
||||
additionalProperties: true
|
287
Documentation/devicetree/bindings/usb/chipidea,usb2-imx.yaml
Normal file
287
Documentation/devicetree/bindings/usb/chipidea,usb2-imx.yaml
Normal file
@ -0,0 +1,287 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/usb/chipidea,usb2-imx.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: NXP USB2 ChipIdea USB controller
|
||||
|
||||
maintainers:
|
||||
- Xu Yang <xu.yang_2@nxp.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- enum:
|
||||
- fsl,imx27-usb
|
||||
- items:
|
||||
- enum:
|
||||
- fsl,imx23-usb
|
||||
- fsl,imx25-usb
|
||||
- fsl,imx28-usb
|
||||
- fsl,imx35-usb
|
||||
- fsl,imx50-usb
|
||||
- fsl,imx51-usb
|
||||
- fsl,imx53-usb
|
||||
- fsl,imx6q-usb
|
||||
- fsl,imx6sl-usb
|
||||
- fsl,imx6sx-usb
|
||||
- fsl,imx6ul-usb
|
||||
- fsl,imx7d-usb
|
||||
- fsl,vf610-usb
|
||||
- const: fsl,imx27-usb
|
||||
- items:
|
||||
- enum:
|
||||
- fsl,imx8dxl-usb
|
||||
- fsl,imx8ulp-usb
|
||||
- const: fsl,imx7ulp-usb
|
||||
- const: fsl,imx6ul-usb
|
||||
- items:
|
||||
- enum:
|
||||
- fsl,imx8mm-usb
|
||||
- fsl,imx8mn-usb
|
||||
- fsl,imx93-usb
|
||||
- const: fsl,imx7d-usb
|
||||
- const: fsl,imx27-usb
|
||||
- items:
|
||||
- enum:
|
||||
- fsl,imx6sll-usb
|
||||
- fsl,imx7ulp-usb
|
||||
- const: fsl,imx6ul-usb
|
||||
- const: fsl,imx27-usb
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
minItems: 1
|
||||
maxItems: 3
|
||||
|
||||
clock-names:
|
||||
minItems: 1
|
||||
maxItems: 3
|
||||
|
||||
fsl,usbmisc:
|
||||
description:
|
||||
Phandler of non-core register device, with one argument that
|
||||
indicate usb controller index
|
||||
$ref: /schemas/types.yaml#/definitions/phandle-array
|
||||
items:
|
||||
- items:
|
||||
- description: phandle to usbmisc node
|
||||
- description: index of usb controller
|
||||
|
||||
disable-over-current:
|
||||
type: boolean
|
||||
description: disable over current detect
|
||||
|
||||
over-current-active-low:
|
||||
type: boolean
|
||||
description: over current signal polarity is active low
|
||||
|
||||
over-current-active-high:
|
||||
type: boolean
|
||||
description:
|
||||
Over current signal polarity is active high. It's recommended to
|
||||
specify the over current polarity.
|
||||
|
||||
power-active-high:
|
||||
type: boolean
|
||||
description: power signal polarity is active high
|
||||
|
||||
external-vbus-divider:
|
||||
type: boolean
|
||||
description: enables off-chip resistor divider for Vbus
|
||||
|
||||
samsung,picophy-pre-emp-curr-control:
|
||||
description:
|
||||
HS Transmitter Pre-Emphasis Current Control. This signal controls
|
||||
the amount of current sourced to the USB_OTG*_DP and USB_OTG*_DN
|
||||
pins after a J-to-K or K-to-J transition. The range is from 0x0 to
|
||||
0x3, the default value is 0x1. Details can refer to TXPREEMPAMPTUNE0
|
||||
bits of USBNC_n_PHY_CFG1.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 0x0
|
||||
maximum: 0x3
|
||||
|
||||
samsung,picophy-dc-vol-level-adjust:
|
||||
description:
|
||||
HS DC Voltage Level Adjustment. Adjust the high-speed transmitter DC
|
||||
level voltage. The range is from 0x0 to 0xf, the default value is
|
||||
0x3. Details can refer to TXVREFTUNE0 bits of USBNC_n_PHY_CFG1.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 0x0
|
||||
maximum: 0xf
|
||||
|
||||
fsl,picophy-rise-fall-time-adjust:
|
||||
description:
|
||||
HS Transmitter Rise/Fall Time Adjustment. Adjust the rise/fall times
|
||||
of the high-speed transmitter waveform. It has no unit. The rise/fall
|
||||
time will be increased or decreased by a certain percentage relative
|
||||
to design default time. (0:-10%; 1:design default; 2:+15%; 3:+20%)
|
||||
Details can refer to TXRISETUNE0 bit of USBNC_n_PHY_CFG1.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 0
|
||||
maximum: 3
|
||||
default: 1
|
||||
|
||||
fsl,usbphy:
|
||||
description: phandle of usb phy that connects to the port. Use "phys" instead.
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
deprecated: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
||||
allOf:
|
||||
- $ref: chipidea,usb2-common.yaml#
|
||||
- if:
|
||||
properties:
|
||||
phy_type:
|
||||
const: hsic
|
||||
required:
|
||||
- phy_type
|
||||
then:
|
||||
properties:
|
||||
pinctrl-names:
|
||||
items:
|
||||
- const: idle
|
||||
- const: active
|
||||
|
||||
# imx27 Soc needs three clocks
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
const: fsl,imx27-usb
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 3
|
||||
clock-names:
|
||||
items:
|
||||
- const: ipg
|
||||
- const: ahb
|
||||
- const: per
|
||||
|
||||
# imx25 and imx35 Soc need three clocks
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- fsl,imx25-usb
|
||||
- fsl,imx35-usb
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 3
|
||||
clock-names:
|
||||
items:
|
||||
- const: ipg
|
||||
- const: ahb
|
||||
- const: per
|
||||
|
||||
# imx93 Soc needs two clocks
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- fsl,imx93-usb
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 2
|
||||
maxItems: 2
|
||||
clock-names:
|
||||
items:
|
||||
- const: usb_ctrl_root
|
||||
- const: usb_wakeup
|
||||
|
||||
# imx7d Soc need one clock
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- const: fsl,imx7d-usb
|
||||
- const: fsl,imx27-usb
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 1
|
||||
clock-names: false
|
||||
|
||||
# other Soc need one clock
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- fsl,imx23-usb
|
||||
- fsl,imx28-usb
|
||||
- fsl,imx50-usb
|
||||
- fsl,imx51-usb
|
||||
- fsl,imx53-usb
|
||||
- fsl,imx6q-usb
|
||||
- fsl,imx6sl-usb
|
||||
- fsl,imx6sx-usb
|
||||
- fsl,imx6ul-usb
|
||||
- fsl,imx8mm-usb
|
||||
- fsl,imx8mn-usb
|
||||
- fsl,vf610-usb
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 1
|
||||
clock-names: false
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/clock/imx7d-clock.h>
|
||||
|
||||
usb@30b10000 {
|
||||
compatible = "fsl,imx7d-usb", "fsl,imx27-usb";
|
||||
reg = <0x30b10000 0x200>;
|
||||
interrupts = <GIC_SPI 43 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clks IMX7D_USB_CTRL_CLK>;
|
||||
fsl,usbphy = <&usbphynop1>;
|
||||
fsl,usbmisc = <&usbmisc1 0>;
|
||||
phy-clkgate-delay-us = <400>;
|
||||
};
|
||||
|
||||
# Example for HSIC:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/clock/imx6qdl-clock.h>
|
||||
|
||||
usb@2184400 {
|
||||
compatible = "fsl,imx6q-usb", "fsl,imx27-usb";
|
||||
reg = <0x02184400 0x200>;
|
||||
interrupts = <0 41 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clks IMX6QDL_CLK_USBOH3>;
|
||||
fsl,usbphy = <&usbphynop1>;
|
||||
fsl,usbmisc = <&usbmisc 2>;
|
||||
phy_type = "hsic";
|
||||
dr_mode = "host";
|
||||
ahb-burst-config = <0x0>;
|
||||
tx-burst-size-dword = <0x10>;
|
||||
rx-burst-size-dword = <0x10>;
|
||||
pinctrl-names = "idle", "active";
|
||||
pinctrl-0 = <&pinctrl_usbh2_idle>;
|
||||
pinctrl-1 = <&pinctrl_usbh2_active>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
ethernet@1 {
|
||||
compatible = "usb424,9730";
|
||||
reg = <1>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
@ -15,7 +15,6 @@ properties:
|
||||
oneOf:
|
||||
- enum:
|
||||
- chipidea,usb2
|
||||
- fsl,imx27-usb
|
||||
- lsi,zevio-usb
|
||||
- nuvoton,npcm750-udc
|
||||
- nvidia,tegra20-ehci
|
||||
@ -31,40 +30,6 @@ properties:
|
||||
- nvidia,tegra124-ehci
|
||||
- nvidia,tegra210-ehci
|
||||
- const: nvidia,tegra30-ehci
|
||||
- items:
|
||||
- enum:
|
||||
- fsl,imx23-usb
|
||||
- fsl,imx25-usb
|
||||
- fsl,imx28-usb
|
||||
- fsl,imx35-usb
|
||||
- fsl,imx50-usb
|
||||
- fsl,imx51-usb
|
||||
- fsl,imx53-usb
|
||||
- fsl,imx6q-usb
|
||||
- fsl,imx6sl-usb
|
||||
- fsl,imx6sx-usb
|
||||
- fsl,imx6ul-usb
|
||||
- fsl,imx7d-usb
|
||||
- fsl,vf610-usb
|
||||
- const: fsl,imx27-usb
|
||||
- items:
|
||||
- enum:
|
||||
- fsl,imx8dxl-usb
|
||||
- fsl,imx8ulp-usb
|
||||
- const: fsl,imx7ulp-usb
|
||||
- const: fsl,imx6ul-usb
|
||||
- items:
|
||||
- enum:
|
||||
- fsl,imx8mm-usb
|
||||
- fsl,imx8mn-usb
|
||||
- const: fsl,imx7d-usb
|
||||
- const: fsl,imx27-usb
|
||||
- items:
|
||||
- enum:
|
||||
- fsl,imx6sll-usb
|
||||
- fsl,imx7ulp-usb
|
||||
- const: fsl,imx6ul-usb
|
||||
- const: fsl,imx27-usb
|
||||
- items:
|
||||
- const: xlnx,zynq-usb-2.20a
|
||||
- const: chipidea,usb2
|
||||
@ -73,163 +38,18 @@ properties:
|
||||
- nuvoton,npcm845-udc
|
||||
- const: nuvoton,npcm750-udc
|
||||
|
||||
reg:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
interrupts:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
clocks:
|
||||
minItems: 1
|
||||
maxItems: 3
|
||||
maxItems: 2
|
||||
|
||||
clock-names:
|
||||
minItems: 1
|
||||
maxItems: 3
|
||||
|
||||
dr_mode: true
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
reset-names:
|
||||
maxItems: 1
|
||||
|
||||
"#reset-cells":
|
||||
const: 1
|
||||
|
||||
phy_type: true
|
||||
|
||||
itc-setting:
|
||||
description:
|
||||
interrupt threshold control register control, the setting should be
|
||||
aligned with ITC bits at register USBCMD.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
ahb-burst-config:
|
||||
description:
|
||||
it is vendor dependent, the required value should be aligned with
|
||||
AHBBRST at SBUSCFG, the range is from 0x0 to 0x7. This property is
|
||||
used to change AHB burst configuration, check the chipidea spec for
|
||||
meaning of each value. If this property is not existed, it will use
|
||||
the reset value.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 0x0
|
||||
maximum: 0x7
|
||||
|
||||
tx-burst-size-dword:
|
||||
description:
|
||||
it is vendor dependent, the tx burst size in dword (4 bytes), This
|
||||
register represents the maximum length of a the burst in 32-bit
|
||||
words while moving data from system memory to the USB bus, the value
|
||||
of this property will only take effect if property "ahb-burst-config"
|
||||
is set to 0, if this property is missing the reset default of the
|
||||
hardware implementation will be used.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 0x0
|
||||
maximum: 0x20
|
||||
|
||||
rx-burst-size-dword:
|
||||
description:
|
||||
it is vendor dependent, the rx burst size in dword (4 bytes), This
|
||||
register represents the maximum length of a the burst in 32-bit words
|
||||
while moving data from the USB bus to system memory, the value of
|
||||
this property will only take effect if property "ahb-burst-config"
|
||||
is set to 0, if this property is missing the reset default of the
|
||||
hardware implementation will be used.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 0x0
|
||||
maximum: 0x20
|
||||
|
||||
extcon:
|
||||
description:
|
||||
Phandles to external connector devices. First phandle should point
|
||||
to external connector, which provide "USB" cable events, the second
|
||||
should point to external connector device, which provide "USB-HOST"
|
||||
cable events. If one of the external connector devices is not
|
||||
required, empty <0> phandle should be specified.
|
||||
$ref: /schemas/types.yaml#/definitions/phandle-array
|
||||
minItems: 1
|
||||
items:
|
||||
- description: vbus extcon
|
||||
- description: id extcon
|
||||
|
||||
phy-clkgate-delay-us:
|
||||
description:
|
||||
The delay time (us) between putting the PHY into low power mode and
|
||||
gating the PHY clock.
|
||||
|
||||
non-zero-ttctrl-ttha:
|
||||
description:
|
||||
After setting this property, the value of register ttctrl.ttha
|
||||
will be 0x7f; if not, the value will be 0x0, this is the default
|
||||
value. It needs to be very carefully for setting this property, it
|
||||
is recommended that consult with your IC engineer before setting
|
||||
this value. On the most of chipidea platforms, the "usage_tt" flag
|
||||
at RTL is 0, so this property only affects siTD.
|
||||
|
||||
If this property is not set, the max packet size is 1023 bytes, and
|
||||
if the total of packet size for previous transactions are more than
|
||||
256 bytes, it can't accept any transactions within this frame. The
|
||||
use case is single transaction, but higher frame rate.
|
||||
|
||||
If this property is set, the max packet size is 188 bytes, it can
|
||||
handle more transactions than above case, it can accept transactions
|
||||
until it considers the left room size within frame is less than 188
|
||||
bytes, software needs to make sure it does not send more than 90%
|
||||
maximum_periodic_data_per_frame. The use case is multiple
|
||||
transactions, but less frame rate.
|
||||
type: boolean
|
||||
|
||||
mux-controls:
|
||||
description:
|
||||
The mux control for toggling host/device output of this controller.
|
||||
It's expected that a mux state of 0 indicates device mode and a mux
|
||||
state of 1 indicates host mode.
|
||||
maxItems: 1
|
||||
|
||||
mux-control-names:
|
||||
const: usb_switch
|
||||
maxItems: 2
|
||||
|
||||
operating-points-v2:
|
||||
description: A phandle to the OPP table containing the performance states.
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
|
||||
pinctrl-names:
|
||||
description:
|
||||
Names for optional pin modes in "default", "host", "device".
|
||||
In case of HSIC-mode, "idle" and "active" pin modes are mandatory.
|
||||
In this case, the "idle" state needs to pull down the data and
|
||||
strobe pin and the "active" state needs to pull up the strobe pin.
|
||||
oneOf:
|
||||
- items:
|
||||
- const: idle
|
||||
- const: active
|
||||
- items:
|
||||
- const: default
|
||||
- enum:
|
||||
- host
|
||||
- device
|
||||
- items:
|
||||
- const: default
|
||||
|
||||
pinctrl-0:
|
||||
maxItems: 1
|
||||
|
||||
pinctrl-1:
|
||||
maxItems: 1
|
||||
|
||||
phys:
|
||||
maxItems: 1
|
||||
|
||||
phy-names:
|
||||
const: usb-phy
|
||||
|
||||
phy-select:
|
||||
description:
|
||||
Phandler of TCSR node with two argument that indicate register
|
||||
@ -240,87 +60,6 @@ properties:
|
||||
- description: register offset
|
||||
- description: phy index
|
||||
|
||||
vbus-supply:
|
||||
description: reference to the VBUS regulator.
|
||||
|
||||
fsl,usbmisc:
|
||||
description:
|
||||
Phandler of non-core register device, with one argument that
|
||||
indicate usb controller index
|
||||
$ref: /schemas/types.yaml#/definitions/phandle-array
|
||||
items:
|
||||
- items:
|
||||
- description: phandle to usbmisc node
|
||||
- description: index of usb controller
|
||||
|
||||
fsl,anatop:
|
||||
description: phandle for the anatop node.
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
|
||||
disable-over-current:
|
||||
type: boolean
|
||||
description: disable over current detect
|
||||
|
||||
over-current-active-low:
|
||||
type: boolean
|
||||
description: over current signal polarity is active low
|
||||
|
||||
over-current-active-high:
|
||||
type: boolean
|
||||
description:
|
||||
Over current signal polarity is active high. It's recommended to
|
||||
specify the over current polarity.
|
||||
|
||||
power-active-high:
|
||||
type: boolean
|
||||
description: power signal polarity is active high
|
||||
|
||||
external-vbus-divider:
|
||||
type: boolean
|
||||
description: enables off-chip resistor divider for Vbus
|
||||
|
||||
samsung,picophy-pre-emp-curr-control:
|
||||
description:
|
||||
HS Transmitter Pre-Emphasis Current Control. This signal controls
|
||||
the amount of current sourced to the USB_OTG*_DP and USB_OTG*_DN
|
||||
pins after a J-to-K or K-to-J transition. The range is from 0x0 to
|
||||
0x3, the default value is 0x1. Details can refer to TXPREEMPAMPTUNE0
|
||||
bits of USBNC_n_PHY_CFG1.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 0x0
|
||||
maximum: 0x3
|
||||
|
||||
samsung,picophy-dc-vol-level-adjust:
|
||||
description:
|
||||
HS DC Voltage Level Adjustment. Adjust the high-speed transmitter DC
|
||||
level voltage. The range is from 0x0 to 0xf, the default value is
|
||||
0x3. Details can refer to TXVREFTUNE0 bits of USBNC_n_PHY_CFG1.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 0x0
|
||||
maximum: 0xf
|
||||
|
||||
fsl,picophy-rise-fall-time-adjust:
|
||||
description:
|
||||
HS Transmitter Rise/Fall Time Adjustment. Adjust the rise/fall times
|
||||
of the high-speed transmitter waveform. It has no unit. The rise/fall
|
||||
time will be increased or decreased by a certain percentage relative
|
||||
to design default time. (0:-10%; 1:design default; 2:+15%; 3:+20%)
|
||||
Details can refer to TXRISETUNE0 bit of USBNC_n_PHY_CFG1.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 0
|
||||
maximum: 3
|
||||
default: 1
|
||||
|
||||
usb-phy:
|
||||
description: phandle for the PHY device. Use "phys" instead.
|
||||
maxItems: 1
|
||||
deprecated: true
|
||||
|
||||
fsl,usbphy:
|
||||
description: phandle of usb phy that connects to the port. Use "phys" instead.
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
deprecated: true
|
||||
|
||||
nvidia,phy:
|
||||
description: phandle of usb phy that connects to the port. Use "phys" instead.
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
@ -331,16 +70,6 @@ properties:
|
||||
type: boolean
|
||||
deprecated: true
|
||||
|
||||
port:
|
||||
description:
|
||||
Any connector to the data bus of this controller should be modelled
|
||||
using the OF graph bindings specified, if the "usb-role-switch"
|
||||
property is used.
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
|
||||
reset-gpios:
|
||||
maxItems: 1
|
||||
|
||||
ulpi:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
@ -350,67 +79,13 @@ properties:
|
||||
type: object
|
||||
$ref: /schemas/phy/qcom,usb-hs-phy.yaml
|
||||
|
||||
dependencies:
|
||||
port: [ usb-role-switch ]
|
||||
mux-controls: [ mux-control-names ]
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
allOf:
|
||||
- $ref: chipidea,usb2-common.yaml#
|
||||
- $ref: usb-hcd.yaml#
|
||||
- $ref: usb-drd.yaml#
|
||||
- if:
|
||||
properties:
|
||||
phy_type:
|
||||
const: hsic
|
||||
required:
|
||||
- phy_type
|
||||
then:
|
||||
properties:
|
||||
pinctrl-names:
|
||||
items:
|
||||
- const: idle
|
||||
- const: active
|
||||
else:
|
||||
properties:
|
||||
pinctrl-names:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
oneOf:
|
||||
- items:
|
||||
- const: default
|
||||
- enum:
|
||||
- host
|
||||
- device
|
||||
- items:
|
||||
- const: default
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- chipidea,usb2
|
||||
- lsi,zevio-usb
|
||||
- nuvoton,npcm750-udc
|
||||
- nvidia,tegra20-udc
|
||||
- nvidia,tegra30-udc
|
||||
- nvidia,tegra114-udc
|
||||
- nvidia,tegra124-udc
|
||||
- qcom,ci-hdrc
|
||||
- xlnx,zynq-usb-2.20a
|
||||
then:
|
||||
properties:
|
||||
fsl,usbmisc: false
|
||||
disable-over-current: false
|
||||
over-current-active-low: false
|
||||
over-current-active-high: false
|
||||
power-active-high: false
|
||||
external-vbus-divider: false
|
||||
samsung,picophy-pre-emp-curr-control: false
|
||||
samsung,picophy-dc-vol-level-adjust: false
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
@ -438,33 +113,4 @@ examples:
|
||||
mux-control-names = "usb_switch";
|
||||
};
|
||||
|
||||
# Example for HSIC:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/clock/imx6qdl-clock.h>
|
||||
|
||||
usb@2184400 {
|
||||
compatible = "fsl,imx6q-usb", "fsl,imx27-usb";
|
||||
reg = <0x02184400 0x200>;
|
||||
interrupts = <0 41 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clks IMX6QDL_CLK_USBOH3>;
|
||||
fsl,usbphy = <&usbphynop1>;
|
||||
fsl,usbmisc = <&usbmisc 2>;
|
||||
phy_type = "hsic";
|
||||
dr_mode = "host";
|
||||
ahb-burst-config = <0x0>;
|
||||
tx-burst-size-dword = <0x10>;
|
||||
rx-burst-size-dword = <0x10>;
|
||||
pinctrl-names = "idle", "active";
|
||||
pinctrl-0 = <&pinctrl_usbh2_idle>;
|
||||
pinctrl-1 = <&pinctrl_usbh2_active>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
ethernet@1 {
|
||||
compatible = "usb424,9730";
|
||||
reg = <1>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
|
@ -51,7 +51,6 @@ examples:
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
usb {
|
||||
dr_mode = "host";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
|
@ -59,6 +59,7 @@ properties:
|
||||
- const: amcc,dwc-otg
|
||||
- const: apm,apm82181-dwc-otg
|
||||
- const: snps,dwc2
|
||||
- const: sophgo,cv1800-usb
|
||||
- const: st,stm32f4x9-fsotg
|
||||
- const: st,stm32f4x9-hsotg
|
||||
- const: st,stm32f7-hsotg
|
||||
|
63
Documentation/devicetree/bindings/usb/microchip,usb2514.yaml
Normal file
63
Documentation/devicetree/bindings/usb/microchip,usb2514.yaml
Normal file
@ -0,0 +1,63 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/usb/microchip,usb2514.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Microchip USB2514 Hub Controller
|
||||
|
||||
maintainers:
|
||||
- Fabio Estevam <festevam@gmail.com>
|
||||
|
||||
allOf:
|
||||
- $ref: usb-hcd.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- usb424,2412
|
||||
- usb424,2417
|
||||
- usb424,2514
|
||||
|
||||
reg: true
|
||||
|
||||
reset-gpios:
|
||||
description: GPIO connected to the RESET_N pin.
|
||||
|
||||
vdd-supply:
|
||||
description: 3.3V power supply.
|
||||
|
||||
clocks:
|
||||
description: External 24MHz clock connected to the CLKIN pin.
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/imx6qdl-clock.h>
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
usb {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
usb-hub@1 {
|
||||
compatible = "usb424,2514";
|
||||
reg = <1>;
|
||||
clocks = <&clks IMX6QDL_CLK_CKO>;
|
||||
reset-gpios = <&gpio7 12 GPIO_ACTIVE_LOW>;
|
||||
vdd-supply = <®_3v3_hub>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
ethernet@1 {
|
||||
compatible = "usbb95,772b";
|
||||
reg = <1>;
|
||||
};
|
||||
};
|
||||
};
|
@ -26,10 +26,12 @@ properties:
|
||||
- qcom,msm8998-dwc3
|
||||
- qcom,qcm2290-dwc3
|
||||
- qcom,qcs404-dwc3
|
||||
- qcom,qdu1000-dwc3
|
||||
- qcom,sa8775p-dwc3
|
||||
- qcom,sc7180-dwc3
|
||||
- qcom,sc7280-dwc3
|
||||
- qcom,sc8280xp-dwc3
|
||||
- qcom,sc8280xp-dwc3-mp
|
||||
- qcom,sdm660-dwc3
|
||||
- qcom,sdm670-dwc3
|
||||
- qcom,sdm845-dwc3
|
||||
@ -117,11 +119,11 @@ properties:
|
||||
exception of SDM670/SDM845/SM6350.
|
||||
- ss_phy_irq: Used for remote wakeup in Super Speed mode of operation.
|
||||
minItems: 2
|
||||
maxItems: 5
|
||||
maxItems: 18
|
||||
|
||||
interrupt-names:
|
||||
minItems: 2
|
||||
maxItems: 5
|
||||
maxItems: 18
|
||||
|
||||
qcom,select-utmi-as-pipe-clk:
|
||||
description:
|
||||
@ -245,6 +247,7 @@ allOf:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,ipq8074-dwc3
|
||||
- qcom,qdu1000-dwc3
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
@ -282,6 +285,7 @@ allOf:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,sc8280xp-dwc3
|
||||
- qcom,sc8280xp-dwc3-mp
|
||||
- qcom,x1e80100-dwc3
|
||||
then:
|
||||
properties:
|
||||
@ -440,6 +444,7 @@ allOf:
|
||||
- qcom,ipq4019-dwc3
|
||||
- qcom,ipq8064-dwc3
|
||||
- qcom,msm8994-dwc3
|
||||
- qcom,qdu1000-dwc3
|
||||
- qcom,sa8775p-dwc3
|
||||
- qcom,sc7180-dwc3
|
||||
- qcom,sc7280-dwc3
|
||||
@ -470,6 +475,38 @@ allOf:
|
||||
- const: dm_hs_phy_irq
|
||||
- const: ss_phy_irq
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,sc8280xp-dwc3-mp
|
||||
then:
|
||||
properties:
|
||||
interrupts:
|
||||
minItems: 18
|
||||
maxItems: 18
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: pwr_event_1
|
||||
- const: pwr_event_2
|
||||
- const: pwr_event_3
|
||||
- const: pwr_event_4
|
||||
- const: hs_phy_1
|
||||
- const: hs_phy_2
|
||||
- const: hs_phy_3
|
||||
- const: hs_phy_4
|
||||
- const: dp_hs_phy_1
|
||||
- const: dm_hs_phy_1
|
||||
- const: dp_hs_phy_2
|
||||
- const: dm_hs_phy_2
|
||||
- const: dp_hs_phy_3
|
||||
- const: dm_hs_phy_3
|
||||
- const: dp_hs_phy_4
|
||||
- const: dm_hs_phy_4
|
||||
- const: ss_phy_1
|
||||
- const: ss_phy_2
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
|
@ -21,6 +21,7 @@ properties:
|
||||
- items:
|
||||
- enum:
|
||||
- qcom,pm6150-typec
|
||||
- qcom,pm7250b-typec
|
||||
- const: qcom,pm8150b-typec
|
||||
- items:
|
||||
- enum:
|
||||
@ -192,15 +193,22 @@ examples:
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
pmic_typec_mux_out: endpoint {
|
||||
remote-endpoint = <&usb_phy_typec_mux_in>;
|
||||
pmic_typec_hs_in: endpoint {
|
||||
remote-endpoint = <&usb_hs_out>;
|
||||
};
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
pmic_typec_role_switch_out: endpoint {
|
||||
remote-endpoint = <&usb_role_switch_in>;
|
||||
pmic_typec_ss_in: endpoint {
|
||||
remote-endpoint = <&usb_phy_typec_ss_out>;
|
||||
};
|
||||
};
|
||||
|
||||
port@2 {
|
||||
reg = <2>;
|
||||
pmic_typec_sbu: endpoint {
|
||||
remote-endpoint = <&usb_mux_sbu>;
|
||||
};
|
||||
};
|
||||
};
|
||||
@ -212,8 +220,8 @@ examples:
|
||||
dr_mode = "otg";
|
||||
usb-role-switch;
|
||||
port {
|
||||
usb_role_switch_in: endpoint {
|
||||
remote-endpoint = <&pmic_typec_role_switch_out>;
|
||||
usb_hs_out: endpoint {
|
||||
remote-endpoint = <&pmic_typec_hs_in>;
|
||||
};
|
||||
};
|
||||
};
|
||||
@ -221,8 +229,19 @@ examples:
|
||||
usb-phy {
|
||||
orientation-switch;
|
||||
port {
|
||||
usb_phy_typec_mux_in: endpoint {
|
||||
remote-endpoint = <&pmic_typec_mux_out>;
|
||||
usb_phy_typec_ss_out: endpoint {
|
||||
remote-endpoint = <&pmic_typec_ss_in>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
usb-mux {
|
||||
orientation-switch;
|
||||
mode-switch;
|
||||
|
||||
port {
|
||||
usb_mux_sbu: endpoint {
|
||||
remote-endpoint = <&pmic_typec_sbu>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -19,10 +19,14 @@ properties:
|
||||
- items:
|
||||
- enum:
|
||||
- renesas,usbhs-r7s9210 # RZ/A2
|
||||
- const: renesas,rza2-usbhs
|
||||
|
||||
- items:
|
||||
- enum:
|
||||
- renesas,usbhs-r9a07g043 # RZ/G2UL and RZ/Five
|
||||
- renesas,usbhs-r9a07g044 # RZ/G2{L,LC}
|
||||
- renesas,usbhs-r9a07g054 # RZ/V2L
|
||||
- const: renesas,rza2-usbhs
|
||||
- const: renesas,rzg2l-usbhs
|
||||
|
||||
- items:
|
||||
- enum:
|
||||
|
@ -12,6 +12,7 @@ maintainers:
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- google,gs101-dwusb3
|
||||
- samsung,exynos5250-dwusb3
|
||||
- samsung,exynos5433-dwusb3
|
||||
- samsung,exynos7-dwusb3
|
||||
@ -55,6 +56,23 @@ required:
|
||||
- vdd33-supply
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: google,gs101-dwusb3
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 4
|
||||
maxItems: 4
|
||||
clock-names:
|
||||
items:
|
||||
- const: bus_early
|
||||
- const: susp_clk
|
||||
- const: link_aclk
|
||||
- const: link_pclk
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
|
@ -85,15 +85,16 @@ properties:
|
||||
|
||||
phys:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
maxItems: 19
|
||||
|
||||
phy-names:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
items:
|
||||
enum:
|
||||
- usb2-phy
|
||||
- usb3-phy
|
||||
maxItems: 19
|
||||
oneOf:
|
||||
- items:
|
||||
enum: [ usb2-phy, usb3-phy ]
|
||||
- items:
|
||||
pattern: "^usb(2-([0-9]|1[0-4])|3-[0-3])$"
|
||||
|
||||
power-domains:
|
||||
description:
|
||||
|
@ -1,18 +0,0 @@
|
||||
Generic Platform UHCI Controller
|
||||
-----------------------------------------------------
|
||||
|
||||
Required properties:
|
||||
- compatible : "generic-uhci" (deprecated: "platform-uhci")
|
||||
- reg : Should contain 1 register ranges(address and length)
|
||||
- interrupts : UHCI controller interrupt
|
||||
|
||||
additionally the properties from usb-hcd.yaml (in the current directory) are
|
||||
supported.
|
||||
|
||||
Example:
|
||||
|
||||
uhci@d8007b00 {
|
||||
compatible = "generic-uhci";
|
||||
reg = <0xd8007b00 0x200>;
|
||||
interrupts = <43>;
|
||||
};
|
75
Documentation/devicetree/bindings/usb/usb-uhci.yaml
Normal file
75
Documentation/devicetree/bindings/usb/usb-uhci.yaml
Normal file
@ -0,0 +1,75 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/usb/usb-uhci.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Generic Platform UHCI Controller
|
||||
|
||||
maintainers:
|
||||
- Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- const: generic-uhci
|
||||
- const: platform-uhci
|
||||
deprecated: true
|
||||
- items:
|
||||
- enum:
|
||||
- aspeed,ast2400-uhci
|
||||
- aspeed,ast2500-uhci
|
||||
- aspeed,ast2600-uhci
|
||||
- const: generic-uhci
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
'#ports':
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
allOf:
|
||||
- $ref: usb-hcd.yaml
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: generic-uhci
|
||||
then:
|
||||
required:
|
||||
- clocks
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/aspeed-clock.h>
|
||||
|
||||
usb@d8007b00 {
|
||||
compatible = "generic-uhci";
|
||||
reg = <0xd8007b00 0x200>;
|
||||
interrupts = <43>;
|
||||
clocks = <&syscon ASPEED_CLK_GATE_USBUHCICLK>;
|
||||
};
|
||||
- |
|
||||
#include <dt-bindings/clock/aspeed-clock.h>
|
||||
|
||||
usb@1e6b0000 {
|
||||
compatible = "aspeed,ast2500-uhci", "generic-uhci";
|
||||
reg = <0x1e6b0000 0x100>;
|
||||
interrupts = <14>;
|
||||
#ports = <2>;
|
||||
clocks = <&syscon ASPEED_CLK_GATE_USBUHCICLK>;
|
||||
};
|
||||
...
|
@ -3087,8 +3087,6 @@ F: drivers/mmc/host/wmt-sdmmc.c
|
||||
F: drivers/pwm/pwm-vt8500.c
|
||||
F: drivers/rtc/rtc-vt8500.c
|
||||
F: drivers/tty/serial/vt8500_serial.c
|
||||
F: drivers/usb/host/ehci-platform.c
|
||||
F: drivers/usb/host/uhci-platform.c
|
||||
F: drivers/video/fbdev/vt8500lcdfb.*
|
||||
F: drivers/video/fbdev/wm8505fb*
|
||||
F: drivers/video/fbdev/wmt_ge_rops.*
|
||||
@ -16691,8 +16689,8 @@ ONBOARD USB HUB DRIVER
|
||||
M: Matthias Kaehlcke <mka@chromium.org>
|
||||
L: linux-usb@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/ABI/testing/sysfs-bus-platform-onboard-usb-hub
|
||||
F: drivers/usb/misc/onboard_usb_hub.c
|
||||
F: Documentation/ABI/testing/sysfs-bus-platform-onboard-usb-dev
|
||||
F: drivers/usb/misc/onboard_usb_dev.c
|
||||
|
||||
ONENAND FLASH DRIVER
|
||||
M: Kyungmin Park <kyungmin.park@samsung.com>
|
||||
|
@ -885,7 +885,7 @@ CONFIG_USB_CHIPIDEA_UDC=y
|
||||
CONFIG_USB_CHIPIDEA_HOST=y
|
||||
CONFIG_USB_ISP1760=y
|
||||
CONFIG_USB_HSIC_USB3503=y
|
||||
CONFIG_USB_ONBOARD_HUB=m
|
||||
CONFIG_USB_ONBOARD_DEV=m
|
||||
CONFIG_AB8500_USB=y
|
||||
CONFIG_KEYSTONE_USB_PHY=m
|
||||
CONFIG_NOP_USB_XCEIV=y
|
||||
|
@ -813,7 +813,7 @@
|
||||
|
||||
hsusb: usb@11c60000 {
|
||||
compatible = "renesas,usbhs-r9a07g043",
|
||||
"renesas,rza2-usbhs";
|
||||
"renesas,rzg2l-usbhs";
|
||||
reg = <0 0x11c60000 0 0x10000>;
|
||||
interrupts = <SOC_PERIPHERAL_IRQ(100) IRQ_TYPE_EDGE_RISING>,
|
||||
<SOC_PERIPHERAL_IRQ(101) IRQ_TYPE_LEVEL_HIGH>,
|
||||
|
@ -1217,7 +1217,7 @@
|
||||
|
||||
hsusb: usb@11c60000 {
|
||||
compatible = "renesas,usbhs-r9a07g044",
|
||||
"renesas,rza2-usbhs";
|
||||
"renesas,rzg2l-usbhs";
|
||||
reg = <0 0x11c60000 0 0x10000>;
|
||||
interrupts = <GIC_SPI 100 IRQ_TYPE_EDGE_RISING>,
|
||||
<GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>,
|
||||
|
@ -1225,7 +1225,7 @@
|
||||
|
||||
hsusb: usb@11c60000 {
|
||||
compatible = "renesas,usbhs-r9a07g054",
|
||||
"renesas,rza2-usbhs";
|
||||
"renesas,rzg2l-usbhs";
|
||||
reg = <0 0x11c60000 0 0x10000>;
|
||||
interrupts = <GIC_SPI 100 IRQ_TYPE_EDGE_RISING>,
|
||||
<GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>,
|
||||
|
@ -1073,7 +1073,7 @@ CONFIG_USB_SERIAL_FTDI_SIO=m
|
||||
CONFIG_USB_SERIAL_OPTION=m
|
||||
CONFIG_USB_QCOM_EUD=m
|
||||
CONFIG_USB_HSIC_USB3503=y
|
||||
CONFIG_USB_ONBOARD_HUB=m
|
||||
CONFIG_USB_ONBOARD_DEV=m
|
||||
CONFIG_NOP_USB_XCEIV=y
|
||||
CONFIG_USB_MXS_PHY=m
|
||||
CONFIG_USB_GADGET=y
|
||||
|
@ -87,7 +87,7 @@ CONFIG_DRM_PARADE_PS8640=y
|
||||
CONFIG_DRM_LONTIUM_LT9611UXC=y
|
||||
CONFIG_PHY_QCOM_USB_HS=y
|
||||
CONFIG_QCOM_GPI_DMA=y
|
||||
CONFIG_USB_ONBOARD_HUB=y
|
||||
CONFIG_USB_ONBOARD_DEV=y
|
||||
CONFIG_NVMEM_QCOM_QFPROM=y
|
||||
CONFIG_PHY_QCOM_USB_SNPS_FEMTO_V2=y
|
||||
|
||||
@ -97,7 +97,7 @@ CONFIG_USB_RTL8152=y
|
||||
# db820c ethernet
|
||||
CONFIG_ATL1C=y
|
||||
# Chromebooks ethernet
|
||||
CONFIG_USB_ONBOARD_HUB=y
|
||||
CONFIG_USB_ONBOARD_DEV=y
|
||||
# 888 HDK ethernet
|
||||
CONFIG_USB_LAN78XX=y
|
||||
|
||||
|
@ -348,16 +348,11 @@ static void pmic_glink_remove(struct platform_device *pdev)
|
||||
mutex_unlock(&__pmic_glink_lock);
|
||||
}
|
||||
|
||||
static const unsigned long pmic_glink_sc8180x_client_mask = BIT(PMIC_GLINK_CLIENT_BATT) |
|
||||
BIT(PMIC_GLINK_CLIENT_ALTMODE);
|
||||
|
||||
static const unsigned long pmic_glink_sm8450_client_mask = BIT(PMIC_GLINK_CLIENT_BATT) |
|
||||
BIT(PMIC_GLINK_CLIENT_ALTMODE) |
|
||||
BIT(PMIC_GLINK_CLIENT_UCSI);
|
||||
|
||||
static const struct of_device_id pmic_glink_of_match[] = {
|
||||
{ .compatible = "qcom,sc8180x-pmic-glink", .data = &pmic_glink_sc8180x_client_mask },
|
||||
{ .compatible = "qcom,sc8280xp-pmic-glink", .data = &pmic_glink_sc8180x_client_mask },
|
||||
{ .compatible = "qcom,pmic-glink", .data = &pmic_glink_sm8450_client_mask },
|
||||
{}
|
||||
};
|
||||
|
@ -1346,7 +1346,7 @@ static int switch_basic_regs_show(struct tb_switch *sw, struct seq_file *s)
|
||||
if (tb_switch_is_usb4(sw))
|
||||
dwords = ARRAY_SIZE(data);
|
||||
else
|
||||
dwords = 7;
|
||||
dwords = 5;
|
||||
|
||||
ret = tb_sw_read(sw, data, TB_CFG_SWITCH, 0, dwords);
|
||||
if (ret)
|
||||
|
@ -2532,6 +2532,7 @@ struct tb *icm_probe(struct tb_nhi *nhi)
|
||||
|
||||
case PCI_DEVICE_ID_INTEL_MAPLE_RIDGE_2C_NHI:
|
||||
case PCI_DEVICE_ID_INTEL_MAPLE_RIDGE_4C_NHI:
|
||||
icm->can_upgrade_nvm = true;
|
||||
icm->is_supported = icm_tgl_is_supported;
|
||||
icm->get_mode = icm_ar_get_mode;
|
||||
icm->driver_ready = icm_tr_driver_ready;
|
||||
|
@ -199,8 +199,10 @@ static void tb_retimer_nvm_authenticate_status(struct tb_port *port, u32 *status
|
||||
* If the retimer has it set, store it for the new retimer
|
||||
* device instance.
|
||||
*/
|
||||
for (i = 1; i <= TB_MAX_RETIMER_INDEX; i++)
|
||||
usb4_port_retimer_nvm_authenticate_status(port, i, &status[i]);
|
||||
for (i = 1; i <= TB_MAX_RETIMER_INDEX; i++) {
|
||||
if (usb4_port_retimer_nvm_authenticate_status(port, i, &status[i]))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void tb_retimer_set_inbound_sbtx(struct tb_port *port)
|
||||
@ -234,8 +236,10 @@ static void tb_retimer_unset_inbound_sbtx(struct tb_port *port)
|
||||
|
||||
tb_port_dbg(port, "disabling sideband transactions\n");
|
||||
|
||||
for (i = TB_MAX_RETIMER_INDEX; i >= 1; i--)
|
||||
usb4_port_retimer_unset_inbound_sbtx(port, i);
|
||||
for (i = TB_MAX_RETIMER_INDEX; i >= 1; i--) {
|
||||
if (usb4_port_retimer_unset_inbound_sbtx(port, i))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t nvm_authenticate_store(struct device *dev,
|
||||
|
@ -498,8 +498,9 @@ static struct tb_tunnel *tb_find_first_usb3_tunnel(struct tb *tb,
|
||||
* @consumed_down: Consumed downstream bandwidth (Mb/s)
|
||||
*
|
||||
* Calculates consumed USB3 and PCIe bandwidth at @port between path
|
||||
* from @src_port to @dst_port. Does not take tunnel starting from
|
||||
* @src_port and ending from @src_port into account.
|
||||
* from @src_port to @dst_port. Does not take USB3 tunnel starting from
|
||||
* @src_port and ending on @src_port into account because that bandwidth is
|
||||
* already included in as part of the "first hop" USB3 tunnel.
|
||||
*/
|
||||
static int tb_consumed_usb3_pcie_bandwidth(struct tb *tb,
|
||||
struct tb_port *src_port,
|
||||
@ -514,8 +515,8 @@ static int tb_consumed_usb3_pcie_bandwidth(struct tb *tb,
|
||||
*consumed_up = *consumed_down = 0;
|
||||
|
||||
tunnel = tb_find_first_usb3_tunnel(tb, src_port, dst_port);
|
||||
if (tunnel && tunnel->src_port != src_port &&
|
||||
tunnel->dst_port != dst_port) {
|
||||
if (tunnel && !tb_port_is_usb3_down(src_port) &&
|
||||
!tb_port_is_usb3_up(dst_port)) {
|
||||
int ret;
|
||||
|
||||
ret = tb_tunnel_consumed_bandwidth(tunnel, consumed_up,
|
||||
|
@ -98,12 +98,6 @@ struct cfg_reset_pkg {
|
||||
struct tb_cfg_header header;
|
||||
} __packed;
|
||||
|
||||
/* TB_CFG_PKG_PREPARE_TO_SLEEP */
|
||||
struct cfg_pts_pkg {
|
||||
struct tb_cfg_header header;
|
||||
u32 data;
|
||||
} __packed;
|
||||
|
||||
/* ICM messages */
|
||||
|
||||
enum icm_pkg_code {
|
||||
|
@ -87,23 +87,32 @@ static inline const char *show_data(struct trace_seq *p, u8 type,
|
||||
const char *prefix = "";
|
||||
int i;
|
||||
|
||||
show_route(p, data);
|
||||
|
||||
switch (type) {
|
||||
case TB_CFG_PKG_READ:
|
||||
case TB_CFG_PKG_WRITE:
|
||||
show_route(p, data);
|
||||
show_data_read_write(p, data);
|
||||
break;
|
||||
|
||||
case TB_CFG_PKG_ERROR:
|
||||
show_route(p, data);
|
||||
show_data_error(p, data);
|
||||
break;
|
||||
|
||||
case TB_CFG_PKG_EVENT:
|
||||
show_route(p, data);
|
||||
show_data_event(p, data);
|
||||
break;
|
||||
|
||||
case TB_CFG_PKG_ICM_EVENT:
|
||||
case TB_CFG_PKG_ICM_CMD:
|
||||
case TB_CFG_PKG_ICM_RESP:
|
||||
/* ICM messages always target the host router */
|
||||
trace_seq_puts(p, "route=0, ");
|
||||
break;
|
||||
|
||||
default:
|
||||
show_route(p, data);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1435,10 +1435,10 @@ err_free:
|
||||
* @in: DP in adapter port
|
||||
* @out: DP out adapter port
|
||||
* @link_nr: Preferred lane adapter when the link is not bonded
|
||||
* @max_up: Maximum available upstream bandwidth for the DP tunnel (%0
|
||||
* if not limited)
|
||||
* @max_down: Maximum available downstream bandwidth for the DP tunnel
|
||||
* (%0 if not limited)
|
||||
* @max_up: Maximum available upstream bandwidth for the DP tunnel.
|
||||
* %0 if no available bandwidth.
|
||||
* @max_down: Maximum available downstream bandwidth for the DP tunnel.
|
||||
* %0 if no available bandwidth.
|
||||
*
|
||||
* Allocates a tunnel between @in and @out that is capable of tunneling
|
||||
* Display Port traffic.
|
||||
@ -2048,10 +2048,10 @@ err_free:
|
||||
* @tb: Pointer to the domain structure
|
||||
* @up: USB3 upstream adapter port
|
||||
* @down: USB3 downstream adapter port
|
||||
* @max_up: Maximum available upstream bandwidth for the USB3 tunnel (%0
|
||||
* if not limited).
|
||||
* @max_down: Maximum available downstream bandwidth for the USB3 tunnel
|
||||
* (%0 if not limited).
|
||||
* @max_up: Maximum available upstream bandwidth for the USB3 tunnel.
|
||||
* %0 if no available bandwidth.
|
||||
* @max_down: Maximum available downstream bandwidth for the USB3 tunnel.
|
||||
* %0 if no available bandwidth.
|
||||
*
|
||||
* Allocate an USB3 tunnel. The ports must be of type @TB_TYPE_USB3_UP and
|
||||
* @TB_TYPE_USB3_DOWN.
|
||||
@ -2066,24 +2066,19 @@ struct tb_tunnel *tb_tunnel_alloc_usb3(struct tb *tb, struct tb_port *up,
|
||||
struct tb_path *path;
|
||||
int max_rate = 0;
|
||||
|
||||
/*
|
||||
* Check that we have enough bandwidth available for the new
|
||||
* USB3 tunnel.
|
||||
*/
|
||||
if (max_up > 0 || max_down > 0) {
|
||||
if (!tb_route(down->sw) && (max_up > 0 || max_down > 0)) {
|
||||
/*
|
||||
* For USB3 isochronous transfers, we allow bandwidth which is
|
||||
* not higher than 90% of maximum supported bandwidth by USB3
|
||||
* adapters.
|
||||
*/
|
||||
max_rate = tb_usb3_max_link_rate(down, up);
|
||||
if (max_rate < 0)
|
||||
return NULL;
|
||||
|
||||
/* Only 90% can be allocated for USB3 isochronous transfers */
|
||||
max_rate = max_rate * 90 / 100;
|
||||
tb_port_dbg(up, "required bandwidth for USB3 tunnel %d Mb/s\n",
|
||||
tb_port_dbg(up, "maximum required bandwidth for USB3 tunnel %d Mb/s\n",
|
||||
max_rate);
|
||||
|
||||
if (max_rate > max_up || max_rate > max_down) {
|
||||
tb_port_warn(up, "not enough bandwidth for USB3 tunnel\n");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
tunnel = tb_tunnel_alloc(tb, 2, TB_TUNNEL_USB3);
|
||||
@ -2115,8 +2110,8 @@ struct tb_tunnel *tb_tunnel_alloc_usb3(struct tb *tb, struct tb_port *up,
|
||||
tunnel->paths[TB_USB3_PATH_UP] = path;
|
||||
|
||||
if (!tb_route(down->sw)) {
|
||||
tunnel->allocated_up = max_rate;
|
||||
tunnel->allocated_down = max_rate;
|
||||
tunnel->allocated_up = min(max_rate, max_up);
|
||||
tunnel->allocated_down = min(max_rate, max_down);
|
||||
|
||||
tunnel->init = tb_usb3_init;
|
||||
tunnel->consumed_bandwidth = tb_usb3_consumed_bandwidth;
|
||||
|
@ -52,6 +52,10 @@ enum usb4_ba_index {
|
||||
#define USB4_BA_VALUE_MASK GENMASK(31, 16)
|
||||
#define USB4_BA_VALUE_SHIFT 16
|
||||
|
||||
/* Delays in us used with usb4_port_wait_for_bit() */
|
||||
#define USB4_PORT_DELAY 50
|
||||
#define USB4_PORT_SB_DELAY 5000
|
||||
|
||||
static int usb4_native_switch_op(struct tb_switch *sw, u16 opcode,
|
||||
u32 *metadata, u8 *status,
|
||||
const void *tx_data, size_t tx_dwords,
|
||||
@ -1245,7 +1249,7 @@ void usb4_port_unconfigure_xdomain(struct tb_port *port)
|
||||
}
|
||||
|
||||
static int usb4_port_wait_for_bit(struct tb_port *port, u32 offset, u32 bit,
|
||||
u32 value, int timeout_msec)
|
||||
u32 value, int timeout_msec, unsigned long delay_usec)
|
||||
{
|
||||
ktime_t timeout = ktime_add_ms(ktime_get(), timeout_msec);
|
||||
|
||||
@ -1260,7 +1264,7 @@ static int usb4_port_wait_for_bit(struct tb_port *port, u32 offset, u32 bit,
|
||||
if ((val & bit) == value)
|
||||
return 0;
|
||||
|
||||
usleep_range(50, 100);
|
||||
fsleep(delay_usec);
|
||||
} while (ktime_before(ktime_get(), timeout));
|
||||
|
||||
return -ETIMEDOUT;
|
||||
@ -1308,7 +1312,7 @@ static int usb4_port_sb_read(struct tb_port *port, enum usb4_sb_target target,
|
||||
return ret;
|
||||
|
||||
ret = usb4_port_wait_for_bit(port, port->cap_usb4 + PORT_CS_1,
|
||||
PORT_CS_1_PND, 0, 500);
|
||||
PORT_CS_1_PND, 0, 500, USB4_PORT_SB_DELAY);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -1355,7 +1359,7 @@ static int usb4_port_sb_write(struct tb_port *port, enum usb4_sb_target target,
|
||||
return ret;
|
||||
|
||||
ret = usb4_port_wait_for_bit(port, port->cap_usb4 + PORT_CS_1,
|
||||
PORT_CS_1_PND, 0, 500);
|
||||
PORT_CS_1_PND, 0, 500, USB4_PORT_SB_DELAY);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -1410,6 +1414,8 @@ static int usb4_port_sb_op(struct tb_port *port, enum usb4_sb_target target,
|
||||
|
||||
if (val != opcode)
|
||||
return usb4_port_sb_opcode_err_to_errno(val);
|
||||
|
||||
fsleep(USB4_PORT_SB_DELAY);
|
||||
} while (ktime_before(ktime_get(), timeout));
|
||||
|
||||
return -ETIMEDOUT;
|
||||
@ -1591,13 +1597,14 @@ int usb4_port_asym_start(struct tb_port *port)
|
||||
* port started the symmetry transition.
|
||||
*/
|
||||
ret = usb4_port_wait_for_bit(port, port->cap_usb4 + PORT_CS_19,
|
||||
PORT_CS_19_START_ASYM, 0, 1000);
|
||||
PORT_CS_19_START_ASYM, 0, 1000,
|
||||
USB4_PORT_DELAY);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Then wait for the transtion to be completed */
|
||||
return usb4_port_wait_for_bit(port, port->cap_usb4 + PORT_CS_18,
|
||||
PORT_CS_18_TIP, 0, 5000);
|
||||
PORT_CS_18_TIP, 0, 5000, USB4_PORT_DELAY);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2123,7 +2130,8 @@ static int usb4_usb3_port_cm_request(struct tb_port *port, bool request)
|
||||
*/
|
||||
val &= ADP_USB3_CS_2_CMR;
|
||||
return usb4_port_wait_for_bit(port, port->cap_adap + ADP_USB3_CS_1,
|
||||
ADP_USB3_CS_1_HCA, val, 1500);
|
||||
ADP_USB3_CS_1_HCA, val, 1500,
|
||||
USB4_PORT_DELAY);
|
||||
}
|
||||
|
||||
static inline int usb4_usb3_port_set_cm_request(struct tb_port *port)
|
||||
|
@ -250,7 +250,7 @@ static int tb_xdp_handle_error(const struct tb_xdp_error_response *res)
|
||||
case ERROR_UNKNOWN_DOMAIN:
|
||||
return -EIO;
|
||||
case ERROR_NOT_SUPPORTED:
|
||||
return -ENOTSUPP;
|
||||
return -EOPNOTSUPP;
|
||||
case ERROR_NOT_READY:
|
||||
return -EAGAIN;
|
||||
default:
|
||||
|
@ -212,7 +212,7 @@ static int imx_get_clks(struct device *dev)
|
||||
/* Get wakeup clock. Not all of the platforms need to
|
||||
* handle this clock. So make it optional.
|
||||
*/
|
||||
data->clk_wakeup = devm_clk_get_optional(dev, "usb_wakeup_clk");
|
||||
data->clk_wakeup = devm_clk_get_optional(dev, "usb_wakeup");
|
||||
if (IS_ERR(data->clk_wakeup))
|
||||
ret = dev_err_probe(dev, PTR_ERR(data->clk_wakeup),
|
||||
"Failed to get wakeup clk\n");
|
||||
|
@ -80,15 +80,13 @@ clk_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int npcm_udc_remove(struct platform_device *pdev)
|
||||
static void npcm_udc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct npcm_udc_data *ci = platform_get_drvdata(pdev);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
ci_hdrc_remove_device(ci->ci);
|
||||
clk_disable_unprepare(ci->core_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id npcm_udc_dt_match[] = {
|
||||
@ -100,7 +98,7 @@ MODULE_DEVICE_TABLE(of, npcm_udc_dt_match);
|
||||
|
||||
static struct platform_driver npcm_udc_driver = {
|
||||
.probe = npcm_udc_probe,
|
||||
.remove = npcm_udc_remove,
|
||||
.remove_new = npcm_udc_remove,
|
||||
.driver = {
|
||||
.name = "npcm_udc",
|
||||
.of_match_table = npcm_udc_dt_match,
|
||||
|
@ -1084,10 +1084,6 @@ static int ci_hdrc_probe(struct platform_device *pdev)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = ci_ulpi_init(ci);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (ci->platdata->phy) {
|
||||
ci->phy = ci->platdata->phy;
|
||||
} else if (ci->platdata->usb_phy) {
|
||||
@ -1142,6 +1138,10 @@ static int ci_hdrc_probe(struct platform_device *pdev)
|
||||
goto ulpi_exit;
|
||||
}
|
||||
|
||||
ret = ci_ulpi_init(ci);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ci->hw_bank.phys = res->start;
|
||||
|
||||
ci->irq = platform_get_irq(pdev, 0);
|
||||
|
@ -68,11 +68,6 @@ int ci_ulpi_init(struct ci_hdrc *ci)
|
||||
if (ci->platdata->phy_mode != USBPHY_INTERFACE_MODE_ULPI)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Set PORTSC correctly so we can read/write ULPI registers for
|
||||
* identification purposes
|
||||
*/
|
||||
hw_phymode_configure(ci);
|
||||
|
||||
ci->ulpi_ops.read = ci_ulpi_read;
|
||||
ci->ulpi_ops.write = ci_ulpi_write;
|
||||
|
@ -12,8 +12,8 @@ usbcore-$(CONFIG_OF) += of.o
|
||||
usbcore-$(CONFIG_USB_PCI) += hcd-pci.o
|
||||
usbcore-$(CONFIG_ACPI) += usb-acpi.o
|
||||
|
||||
ifdef CONFIG_USB_ONBOARD_HUB
|
||||
usbcore-y += ../misc/onboard_usb_hub_pdevs.o
|
||||
ifdef CONFIG_USB_ONBOARD_DEV
|
||||
usbcore-y += ../misc/onboard_usb_dev_pdevs.o
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_USB) += usbcore.o
|
||||
|
@ -279,11 +279,11 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno,
|
||||
goto skip_to_next_endpoint_or_interface_descriptor;
|
||||
}
|
||||
|
||||
i = d->bEndpointAddress & ~USB_ENDPOINT_DIR_MASK;
|
||||
if (i >= 16 || i == 0) {
|
||||
i = d->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
|
||||
if (i == 0) {
|
||||
dev_notice(ddev, "config %d interface %d altsetting %d has an "
|
||||
"invalid endpoint with address 0x%X, skipping\n",
|
||||
cfgno, inum, asnum, d->bEndpointAddress);
|
||||
"invalid descriptor for endpoint zero, skipping\n",
|
||||
cfgno, inum, asnum);
|
||||
goto skip_to_next_endpoint_or_interface_descriptor;
|
||||
}
|
||||
|
||||
|
@ -866,7 +866,7 @@ static int usb_rh_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
|
||||
*/
|
||||
static void usb_bus_init (struct usb_bus *bus)
|
||||
{
|
||||
memset (&bus->devmap, 0, sizeof(struct usb_devmap));
|
||||
memset(&bus->devmap, 0, sizeof(bus->devmap));
|
||||
|
||||
bus->devnum_next = 1;
|
||||
|
||||
@ -962,7 +962,7 @@ static int register_root_hub(struct usb_hcd *hcd)
|
||||
|
||||
usb_dev->devnum = devnum;
|
||||
usb_dev->bus->devnum_next = devnum + 1;
|
||||
set_bit (devnum, usb_dev->bus->devmap.devicemap);
|
||||
set_bit(devnum, usb_dev->bus->devmap);
|
||||
usb_set_device_state(usb_dev, USB_STATE_ADDRESS);
|
||||
|
||||
mutex_lock(&usb_bus_idr_lock);
|
||||
|
@ -23,7 +23,7 @@
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usbdevice_fs.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/usb/onboard_hub.h>
|
||||
#include <linux/usb/onboard_dev.h>
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/usb/quirks.h>
|
||||
#include <linux/workqueue.h>
|
||||
@ -1814,7 +1814,7 @@ static void hub_disconnect(struct usb_interface *intf)
|
||||
if (hub->quirk_disable_autosuspend)
|
||||
usb_autopm_put_interface(intf);
|
||||
|
||||
onboard_hub_destroy_pdevs(&hub->onboard_hub_devs);
|
||||
onboard_dev_destroy_pdevs(&hub->onboard_devs);
|
||||
|
||||
hub_put(hub);
|
||||
}
|
||||
@ -1933,7 +1933,7 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
INIT_DELAYED_WORK(&hub->leds, led_work);
|
||||
INIT_DELAYED_WORK(&hub->init_work, NULL);
|
||||
INIT_WORK(&hub->events, hub_event);
|
||||
INIT_LIST_HEAD(&hub->onboard_hub_devs);
|
||||
INIT_LIST_HEAD(&hub->onboard_devs);
|
||||
spin_lock_init(&hub->irq_urb_lock);
|
||||
timer_setup(&hub->irq_urb_retry, hub_retry_irq_urb, 0);
|
||||
usb_get_intf(intf);
|
||||
@ -1963,7 +1963,7 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
}
|
||||
|
||||
if (hub_configure(hub, &desc->endpoint[0].desc) >= 0) {
|
||||
onboard_hub_create_pdevs(hdev, &hub->onboard_hub_devs);
|
||||
onboard_dev_create_pdevs(hdev, &hub->onboard_devs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -2207,13 +2207,12 @@ static void choose_devnum(struct usb_device *udev)
|
||||
mutex_lock(&bus->devnum_next_mutex);
|
||||
|
||||
/* Try to allocate the next devnum beginning at bus->devnum_next. */
|
||||
devnum = find_next_zero_bit(bus->devmap.devicemap, 128,
|
||||
bus->devnum_next);
|
||||
devnum = find_next_zero_bit(bus->devmap, 128, bus->devnum_next);
|
||||
if (devnum >= 128)
|
||||
devnum = find_next_zero_bit(bus->devmap.devicemap, 128, 1);
|
||||
devnum = find_next_zero_bit(bus->devmap, 128, 1);
|
||||
bus->devnum_next = (devnum >= 127 ? 1 : devnum + 1);
|
||||
if (devnum < 128) {
|
||||
set_bit(devnum, bus->devmap.devicemap);
|
||||
set_bit(devnum, bus->devmap);
|
||||
udev->devnum = devnum;
|
||||
}
|
||||
mutex_unlock(&bus->devnum_next_mutex);
|
||||
@ -2222,7 +2221,7 @@ static void choose_devnum(struct usb_device *udev)
|
||||
static void release_devnum(struct usb_device *udev)
|
||||
{
|
||||
if (udev->devnum > 0) {
|
||||
clear_bit(udev->devnum, udev->bus->devmap.devicemap);
|
||||
clear_bit(udev->devnum, udev->bus->devmap);
|
||||
udev->devnum = -1;
|
||||
}
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ struct usb_hub {
|
||||
spinlock_t irq_urb_lock;
|
||||
struct timer_list irq_urb_retry;
|
||||
struct usb_port **ports;
|
||||
struct list_head onboard_hub_devs;
|
||||
struct list_head onboard_devs;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -249,6 +249,11 @@ void dwc2_hib_restore_common(struct dwc2_hsotg *hsotg, int rem_wakeup,
|
||||
dwc2_writel(hsotg, gpwrdn, GPWRDN);
|
||||
udelay(10);
|
||||
|
||||
/* Reset ULPI latch */
|
||||
gpwrdn = dwc2_readl(hsotg, GPWRDN);
|
||||
gpwrdn &= ~GPWRDN_ULPI_LATCH_EN_DURING_HIB_ENTRY;
|
||||
dwc2_writel(hsotg, gpwrdn, GPWRDN);
|
||||
|
||||
/* Disable PMU interrupt */
|
||||
gpwrdn = dwc2_readl(hsotg, GPWRDN);
|
||||
gpwrdn &= ~GPWRDN_PMUINTSEL;
|
||||
@ -975,6 +980,41 @@ void dwc2_init_fs_ls_pclk_sel(struct dwc2_hsotg *hsotg)
|
||||
dwc2_writel(hsotg, hcfg, HCFG);
|
||||
}
|
||||
|
||||
static void dwc2_set_clock_switch_timer(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
u32 grstctl, gsnpsid, val = 0;
|
||||
|
||||
gsnpsid = dwc2_readl(hsotg, GSNPSID);
|
||||
|
||||
/*
|
||||
* Applicable only to HSOTG core v5.00a or higher.
|
||||
* Not applicable to HS/FS IOT devices.
|
||||
*/
|
||||
if ((gsnpsid & ~DWC2_CORE_REV_MASK) != DWC2_OTG_ID ||
|
||||
gsnpsid < DWC2_CORE_REV_5_00a)
|
||||
return;
|
||||
|
||||
if ((hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI &&
|
||||
hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_NOT_SUPPORTED) ||
|
||||
(hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI &&
|
||||
hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_NOT_SUPPORTED) ||
|
||||
(hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_NOT_SUPPORTED &&
|
||||
hsotg->hw_params.fs_phy_type != GHWCFG2_FS_PHY_TYPE_NOT_SUPPORTED)) {
|
||||
val = GRSTCTL_CLOCK_SWITH_TIMER_VALUE_DIS;
|
||||
}
|
||||
|
||||
if (hsotg->params.speed == DWC2_SPEED_PARAM_LOW &&
|
||||
hsotg->hw_params.hs_phy_type != GHWCFG2_HS_PHY_TYPE_NOT_SUPPORTED &&
|
||||
hsotg->hw_params.fs_phy_type != GHWCFG2_FS_PHY_TYPE_NOT_SUPPORTED) {
|
||||
val = GRSTCTL_CLOCK_SWITH_TIMER_VALUE_147;
|
||||
}
|
||||
|
||||
grstctl = dwc2_readl(hsotg, GRSTCTL);
|
||||
grstctl &= ~GRSTCTL_CLOCK_SWITH_TIMER_MASK;
|
||||
grstctl |= GRSTCTL_CLOCK_SWITH_TIMER(val);
|
||||
dwc2_writel(hsotg, grstctl, GRSTCTL);
|
||||
}
|
||||
|
||||
static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
|
||||
{
|
||||
u32 usbcfg, ggpio, i2cctl;
|
||||
@ -992,6 +1032,8 @@ static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
|
||||
usbcfg |= GUSBCFG_PHYSEL;
|
||||
dwc2_writel(hsotg, usbcfg, GUSBCFG);
|
||||
|
||||
dwc2_set_clock_switch_timer(hsotg);
|
||||
|
||||
/* Reset after a PHY select */
|
||||
retval = dwc2_core_reset(hsotg, false);
|
||||
|
||||
|
@ -288,6 +288,11 @@ enum dwc2_ep0_state {
|
||||
* core has been configured to work at either data path
|
||||
* width.
|
||||
* 8 or 16 (default 16 if available)
|
||||
* @eusb2_disc: Specifies whether eUSB2 PHY disconnect support flow
|
||||
* applicable or no. Applicable in device mode of HSOTG
|
||||
* and HS IOT cores v5.00 or higher.
|
||||
* 0 - eUSB2 PHY disconnect support flow not applicable
|
||||
* 1 - eUSB2 PHY disconnect support flow applicable
|
||||
* @phy_ulpi_ddr: Specifies whether the ULPI operates at double or single
|
||||
* data rate. This parameter is only applicable if phy_type
|
||||
* is ULPI.
|
||||
@ -442,6 +447,7 @@ struct dwc2_core_params {
|
||||
#define DWC2_SPEED_PARAM_LOW 2
|
||||
|
||||
u8 phy_utmi_width;
|
||||
bool eusb2_disc;
|
||||
bool phy_ulpi_ddr;
|
||||
bool phy_ulpi_ext_vbus;
|
||||
bool enable_dynamic_fifo;
|
||||
@ -1110,8 +1116,10 @@ struct dwc2_hsotg {
|
||||
#define DWC2_CORE_REV_3_10a 0x4f54310a
|
||||
#define DWC2_CORE_REV_4_00a 0x4f54400a
|
||||
#define DWC2_CORE_REV_4_20a 0x4f54420a
|
||||
#define DWC2_CORE_REV_5_00a 0x4f54500a
|
||||
#define DWC2_FS_IOT_REV_1_00a 0x5531100a
|
||||
#define DWC2_HS_IOT_REV_1_00a 0x5532100a
|
||||
#define DWC2_HS_IOT_REV_5_00a 0x5532500a
|
||||
#define DWC2_CORE_REV_MASK 0x0000ffff
|
||||
|
||||
/* DWC OTG HW Core ID */
|
||||
|
@ -84,6 +84,7 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
|
||||
u32 gotgint;
|
||||
u32 gotgctl;
|
||||
u32 gintmsk;
|
||||
u32 pcgctl;
|
||||
|
||||
gotgint = dwc2_readl(hsotg, GOTGINT);
|
||||
gotgctl = dwc2_readl(hsotg, GOTGCTL);
|
||||
@ -96,8 +97,22 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
|
||||
dwc2_op_state_str(hsotg));
|
||||
gotgctl = dwc2_readl(hsotg, GOTGCTL);
|
||||
|
||||
if (dwc2_is_device_mode(hsotg))
|
||||
if (dwc2_is_device_mode(hsotg)) {
|
||||
if (hsotg->params.eusb2_disc) {
|
||||
/* Clear the Gate hclk. */
|
||||
pcgctl = dwc2_readl(hsotg, PCGCTL);
|
||||
pcgctl &= ~PCGCTL_GATEHCLK;
|
||||
dwc2_writel(hsotg, pcgctl, PCGCTL);
|
||||
udelay(5);
|
||||
|
||||
/* Clear Phy Clock bit. */
|
||||
pcgctl = dwc2_readl(hsotg, PCGCTL);
|
||||
pcgctl &= ~PCGCTL_STOPPCLK;
|
||||
dwc2_writel(hsotg, pcgctl, PCGCTL);
|
||||
udelay(5);
|
||||
}
|
||||
dwc2_hsotg_disconnect(hsotg);
|
||||
}
|
||||
|
||||
if (hsotg->op_state == OTG_STATE_B_HOST) {
|
||||
hsotg->op_state = OTG_STATE_B_PERIPHERAL;
|
||||
@ -117,7 +132,7 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
|
||||
* disconnected
|
||||
*/
|
||||
/* Reset to a clean state */
|
||||
hsotg->lx_state = DWC2_L0;
|
||||
hsotg->lx_state = DWC2_L3;
|
||||
}
|
||||
|
||||
gotgctl = dwc2_readl(hsotg, GOTGCTL);
|
||||
@ -286,7 +301,7 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
|
||||
hsotg->lx_state);
|
||||
|
||||
if (dwc2_is_device_mode(hsotg)) {
|
||||
if (hsotg->lx_state == DWC2_L2) {
|
||||
if (hsotg->lx_state != DWC2_L0) {
|
||||
if (hsotg->in_ppd) {
|
||||
ret = dwc2_exit_partial_power_down(hsotg, 0,
|
||||
true);
|
||||
@ -714,6 +729,11 @@ static inline void dwc_handle_gpwrdn_disc_det(struct dwc2_hsotg *hsotg,
|
||||
gpwrdn_tmp &= ~GPWRDN_PMUINTSEL;
|
||||
dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN);
|
||||
|
||||
/* Reset ULPI latch */
|
||||
gpwrdn = dwc2_readl(hsotg, GPWRDN);
|
||||
gpwrdn &= ~GPWRDN_ULPI_LATCH_EN_DURING_HIB_ENTRY;
|
||||
dwc2_writel(hsotg, gpwrdn, GPWRDN);
|
||||
|
||||
/* De-assert Wakeup Logic */
|
||||
gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN);
|
||||
gpwrdn_tmp &= ~GPWRDN_PMUACTV;
|
||||
|
@ -686,6 +686,7 @@ static int params_show(struct seq_file *seq, void *v)
|
||||
print_param(seq, p, host_channels);
|
||||
print_param(seq, p, phy_type);
|
||||
print_param(seq, p, phy_utmi_width);
|
||||
print_param(seq, p, eusb2_disc);
|
||||
print_param(seq, p, phy_ulpi_ddr);
|
||||
print_param(seq, p, phy_ulpi_ext_vbus);
|
||||
print_param(seq, p, i2c_enable);
|
||||
|
@ -3424,8 +3424,11 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
|
||||
|
||||
dwc2_hsotg_init_fifo(hsotg);
|
||||
|
||||
if (!is_usb_reset)
|
||||
if (!is_usb_reset) {
|
||||
dwc2_set_bit(hsotg, DCTL, DCTL_SFTDISCON);
|
||||
if (hsotg->params.eusb2_disc)
|
||||
dwc2_set_bit(hsotg, GOTGCTL, GOTGCTL_EUSB2_DISC_SUPP);
|
||||
}
|
||||
|
||||
dcfg |= DCFG_EPMISCNT(1);
|
||||
|
||||
@ -5316,6 +5319,8 @@ void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg)
|
||||
int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
u32 gpwrdn;
|
||||
u32 gusbcfg;
|
||||
u32 pcgcctl;
|
||||
int ret = 0;
|
||||
|
||||
/* Change to L2(suspend) state */
|
||||
@ -5335,6 +5340,22 @@ int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg)
|
||||
}
|
||||
|
||||
gpwrdn = GPWRDN_PWRDNRSTN;
|
||||
udelay(10);
|
||||
gusbcfg = dwc2_readl(hsotg, GUSBCFG);
|
||||
if (gusbcfg & GUSBCFG_ULPI_UTMI_SEL) {
|
||||
/* ULPI interface */
|
||||
gpwrdn |= GPWRDN_ULPI_LATCH_EN_DURING_HIB_ENTRY;
|
||||
}
|
||||
dwc2_writel(hsotg, gpwrdn, GPWRDN);
|
||||
udelay(10);
|
||||
|
||||
/* Suspend the Phy Clock */
|
||||
pcgcctl = dwc2_readl(hsotg, PCGCTL);
|
||||
pcgcctl |= PCGCTL_STOPPCLK;
|
||||
dwc2_writel(hsotg, pcgcctl, PCGCTL);
|
||||
udelay(10);
|
||||
|
||||
gpwrdn = dwc2_readl(hsotg, GPWRDN);
|
||||
gpwrdn |= GPWRDN_PMUACTV;
|
||||
dwc2_writel(hsotg, gpwrdn, GPWRDN);
|
||||
udelay(10);
|
||||
@ -5435,6 +5456,11 @@ int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg,
|
||||
if (reset)
|
||||
dwc2_clear_bit(hsotg, DCFG, DCFG_DEVADDR_MASK);
|
||||
|
||||
/* Reset ULPI latch */
|
||||
gpwrdn = dwc2_readl(hsotg, GPWRDN);
|
||||
gpwrdn &= ~GPWRDN_ULPI_LATCH_EN_DURING_HIB_ENTRY;
|
||||
dwc2_writel(hsotg, gpwrdn, GPWRDN);
|
||||
|
||||
/* De-assert Wakeup Logic */
|
||||
gpwrdn = dwc2_readl(hsotg, GPWRDN);
|
||||
gpwrdn &= ~GPWRDN_PMUACTV;
|
||||
|
@ -5525,6 +5525,11 @@ int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg)
|
||||
gusbcfg = dwc2_readl(hsotg, GUSBCFG);
|
||||
if (gusbcfg & GUSBCFG_ULPI_UTMI_SEL) {
|
||||
/* ULPI interface */
|
||||
udelay(10);
|
||||
gpwrdn = dwc2_readl(hsotg, GPWRDN);
|
||||
gpwrdn |= GPWRDN_ULPI_LATCH_EN_DURING_HIB_ENTRY;
|
||||
dwc2_writel(hsotg, gpwrdn, GPWRDN);
|
||||
udelay(10);
|
||||
/* Suspend the Phy Clock */
|
||||
pcgcctl = dwc2_readl(hsotg, PCGCTL);
|
||||
pcgcctl |= PCGCTL_STOPPCLK;
|
||||
@ -5631,6 +5636,11 @@ int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup,
|
||||
dwc2_writel(hsotg, gr->gusbcfg, GUSBCFG);
|
||||
dwc2_writel(hsotg, hr->hcfg, HCFG);
|
||||
|
||||
/* Reset ULPI latch */
|
||||
gpwrdn = dwc2_readl(hsotg, GPWRDN);
|
||||
gpwrdn &= ~GPWRDN_ULPI_LATCH_EN_DURING_HIB_ENTRY;
|
||||
dwc2_writel(hsotg, gpwrdn, GPWRDN);
|
||||
|
||||
/* De-assert Wakeup Logic */
|
||||
if (!(rem_wakeup && hsotg->hw_params.snpsid >= DWC2_CORE_REV_4_30a)) {
|
||||
gpwrdn = dwc2_readl(hsotg, GPWRDN);
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/seq_buf.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
@ -359,41 +360,6 @@ static unsigned long *dwc2_get_ls_map(struct dwc2_hsotg *hsotg,
|
||||
}
|
||||
|
||||
#ifdef DWC2_PRINT_SCHEDULE
|
||||
/*
|
||||
* cat_printf() - A printf() + strcat() helper
|
||||
*
|
||||
* This is useful for concatenating a bunch of strings where each string is
|
||||
* constructed using printf.
|
||||
*
|
||||
* @buf: The destination buffer; will be updated to point after the printed
|
||||
* data.
|
||||
* @size: The number of bytes in the buffer (includes space for '\0').
|
||||
* @fmt: The format for printf.
|
||||
* @...: The args for printf.
|
||||
*/
|
||||
static __printf(3, 4)
|
||||
void cat_printf(char **buf, size_t *size, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int i;
|
||||
|
||||
if (*size == 0)
|
||||
return;
|
||||
|
||||
va_start(args, fmt);
|
||||
i = vsnprintf(*buf, *size, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if (i >= *size) {
|
||||
(*buf)[*size - 1] = '\0';
|
||||
*buf += *size;
|
||||
*size = 0;
|
||||
} else {
|
||||
*buf += i;
|
||||
*size -= i;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* pmap_print() - Print the given periodic map
|
||||
*
|
||||
@ -416,9 +382,7 @@ static void pmap_print(unsigned long *map, int bits_per_period,
|
||||
int period;
|
||||
|
||||
for (period = 0; period < periods_in_map; period++) {
|
||||
char tmp[64];
|
||||
char *buf = tmp;
|
||||
size_t buf_size = sizeof(tmp);
|
||||
DECLARE_SEQ_BUF(buf, 64);
|
||||
int period_start = period * bits_per_period;
|
||||
int period_end = period_start + bits_per_period;
|
||||
int start = 0;
|
||||
@ -442,19 +406,19 @@ static void pmap_print(unsigned long *map, int bits_per_period,
|
||||
continue;
|
||||
|
||||
if (!printed)
|
||||
cat_printf(&buf, &buf_size, "%s %d: ",
|
||||
period_name, period);
|
||||
seq_buf_printf(&buf, "%s %d: ",
|
||||
period_name, period);
|
||||
else
|
||||
cat_printf(&buf, &buf_size, ", ");
|
||||
seq_buf_puts(&buf, ", ");
|
||||
printed = true;
|
||||
|
||||
cat_printf(&buf, &buf_size, "%d %s -%3d %s", start,
|
||||
units, start + count - 1, units);
|
||||
seq_buf_printf(&buf, "%d %s -%3d %s", start,
|
||||
units, start + count - 1, units);
|
||||
count = 0;
|
||||
}
|
||||
|
||||
if (printed)
|
||||
print_fn(tmp, print_data);
|
||||
print_fn(seq_buf_str(&buf), print_data);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
#define HSOTG_REG(x) (x)
|
||||
|
||||
#define GOTGCTL HSOTG_REG(0x000)
|
||||
#define GOTGCTL_EUSB2_DISC_SUPP BIT(28)
|
||||
#define GOTGCTL_CHIRPEN BIT(27)
|
||||
#define GOTGCTL_MULT_VALID_BC_MASK (0x1f << 22)
|
||||
#define GOTGCTL_MULT_VALID_BC_SHIFT 22
|
||||
@ -98,6 +99,17 @@
|
||||
#define GRSTCTL_AHBIDLE BIT(31)
|
||||
#define GRSTCTL_DMAREQ BIT(30)
|
||||
#define GRSTCTL_CSFTRST_DONE BIT(29)
|
||||
#define GRSTCTL_CLOCK_SWITH_TIMER_MASK (0x7 << 11)
|
||||
#define GRSTCTL_CLOCK_SWITH_TIMER_SHIFT 11
|
||||
#define GRSTCTL_CLOCK_SWITH_TIMER_VALUE_19 0x0
|
||||
#define GRSTCTL_CLOCK_SWITH_TIMER_VALUE_15 0x1
|
||||
#define GRSTCTL_CLOCK_SWITH_TIMER_VALUE_147 0x2
|
||||
#define GRSTCTL_CLOCK_SWITH_TIMER_VALUE_50 0x3
|
||||
#define GRSTCTL_CLOCK_SWITH_TIMER_VALUE_100 0x4
|
||||
#define GRSTCTL_CLOCK_SWITH_TIMER_VALUE_125 0x5
|
||||
#define GRSTCTL_CLOCK_SWITH_TIMER_VALUE_200 0x6
|
||||
#define GRSTCTL_CLOCK_SWITH_TIMER_VALUE_DIS 0x7
|
||||
#define GRSTCTL_CLOCK_SWITH_TIMER(_x) ((_x) << 11)
|
||||
#define GRSTCTL_TXFNUM_MASK (0x1f << 6)
|
||||
#define GRSTCTL_TXFNUM_SHIFT 6
|
||||
#define GRSTCTL_TXFNUM_LIMIT 0x1f
|
||||
@ -332,6 +344,8 @@
|
||||
#define GLPMCFG_LPMCAP BIT(0)
|
||||
|
||||
#define GPWRDN HSOTG_REG(0x0058)
|
||||
|
||||
#define GPWRDN_ULPI_LATCH_EN_DURING_HIB_ENTRY BIT(29)
|
||||
#define GPWRDN_MULT_VAL_ID_BC_MASK (0x1f << 24)
|
||||
#define GPWRDN_MULT_VAL_ID_BC_SHIFT 24
|
||||
#define GPWRDN_ADP_INT BIT(23)
|
||||
|
@ -201,6 +201,25 @@ static void dwc2_set_amcc_params(struct dwc2_hsotg *hsotg)
|
||||
p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 << GAHBCFG_HBSTLEN_SHIFT;
|
||||
}
|
||||
|
||||
static void dwc2_set_cv1800_params(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct dwc2_core_params *p = &hsotg->params;
|
||||
|
||||
p->otg_caps.hnp_support = false;
|
||||
p->otg_caps.srp_support = false;
|
||||
p->host_dma = false;
|
||||
p->g_dma = false;
|
||||
p->speed = DWC2_SPEED_PARAM_HIGH;
|
||||
p->phy_type = DWC2_PHY_TYPE_PARAM_UTMI;
|
||||
p->phy_utmi_width = 16;
|
||||
p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 << GAHBCFG_HBSTLEN_SHIFT;
|
||||
p->lpm = false;
|
||||
p->lpm_clock_gating = false;
|
||||
p->besl = false;
|
||||
p->hird_threshold_en = false;
|
||||
p->power_down = DWC2_POWER_DOWN_PARAM_NONE;
|
||||
}
|
||||
|
||||
static void dwc2_set_stm32f4x9_fsotg_params(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct dwc2_core_params *p = &hsotg->params;
|
||||
@ -295,6 +314,8 @@ const struct of_device_id dwc2_of_match_table[] = {
|
||||
.data = dwc2_set_amlogic_a1_params },
|
||||
{ .compatible = "amcc,dwc-otg", .data = dwc2_set_amcc_params },
|
||||
{ .compatible = "apm,apm82181-dwc-otg", .data = dwc2_set_amcc_params },
|
||||
{ .compatible = "sophgo,cv1800-usb",
|
||||
.data = dwc2_set_cv1800_params },
|
||||
{ .compatible = "st,stm32f4x9-fsotg",
|
||||
.data = dwc2_set_stm32f4x9_fsotg_params },
|
||||
{ .compatible = "st,stm32f4x9-hsotg" },
|
||||
@ -475,6 +496,7 @@ static void dwc2_set_default_params(struct dwc2_hsotg *hsotg)
|
||||
dwc2_set_param_lpm(hsotg);
|
||||
p->phy_ulpi_ddr = false;
|
||||
p->phy_ulpi_ext_vbus = false;
|
||||
p->eusb2_disc = false;
|
||||
|
||||
p->enable_dynamic_fifo = hw->enable_dynamic_fifo;
|
||||
p->en_multiple_tx_fifo = hw->en_multiple_tx_fifo;
|
||||
@ -737,6 +759,25 @@ static void dwc2_check_param_tx_fifo_sizes(struct dwc2_hsotg *hsotg)
|
||||
}
|
||||
}
|
||||
|
||||
static void dwc2_check_param_eusb2_disc(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
u32 gsnpsid;
|
||||
|
||||
if (!hsotg->params.eusb2_disc)
|
||||
return;
|
||||
gsnpsid = dwc2_readl(hsotg, GSNPSID);
|
||||
/*
|
||||
* eusb2_disc not supported by FS IOT devices.
|
||||
* For other cores, it supported starting from version 5.00a
|
||||
*/
|
||||
if ((gsnpsid & ~DWC2_CORE_REV_MASK) == DWC2_FS_IOT_ID ||
|
||||
(gsnpsid & DWC2_CORE_REV_MASK) <
|
||||
(DWC2_CORE_REV_5_00a & DWC2_CORE_REV_MASK)) {
|
||||
hsotg->params.eusb2_disc = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#define CHECK_RANGE(_param, _min, _max, _def) do { \
|
||||
if ((int)(hsotg->params._param) < (_min) || \
|
||||
(hsotg->params._param) > (_max)) { \
|
||||
@ -765,6 +806,8 @@ static void dwc2_check_params(struct dwc2_hsotg *hsotg)
|
||||
dwc2_check_param_speed(hsotg);
|
||||
dwc2_check_param_phy_utmi_width(hsotg);
|
||||
dwc2_check_param_power_down(hsotg);
|
||||
dwc2_check_param_eusb2_disc(hsotg);
|
||||
|
||||
CHECK_BOOL(enable_dynamic_fifo, hw->enable_dynamic_fifo);
|
||||
CHECK_BOOL(en_multiple_tx_fifo, hw->en_multiple_tx_fifo);
|
||||
CHECK_BOOL(i2c_enable, hw->i2c_enable);
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include "io.h"
|
||||
|
||||
#include "debug.h"
|
||||
#include "../host/xhci-ext-caps.h"
|
||||
|
||||
#define DWC3_DEFAULT_AUTOSUSPEND_DELAY 5000 /* ms */
|
||||
|
||||
@ -144,6 +145,7 @@ static void __dwc3_set_mode(struct work_struct *work)
|
||||
int ret;
|
||||
u32 reg;
|
||||
u32 desired_dr_role;
|
||||
int i;
|
||||
|
||||
mutex_lock(&dwc->mutex);
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
@ -221,8 +223,12 @@ static void __dwc3_set_mode(struct work_struct *work)
|
||||
} else {
|
||||
if (dwc->usb2_phy)
|
||||
otg_set_vbus(dwc->usb2_phy->otg, true);
|
||||
phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST);
|
||||
phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST);
|
||||
|
||||
for (i = 0; i < dwc->num_usb2_ports; i++)
|
||||
phy_set_mode(dwc->usb2_generic_phy[i], PHY_MODE_USB_HOST);
|
||||
for (i = 0; i < dwc->num_usb3_ports; i++)
|
||||
phy_set_mode(dwc->usb3_generic_phy[i], PHY_MODE_USB_HOST);
|
||||
|
||||
if (dwc->dis_split_quirk) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUCTL3);
|
||||
reg |= DWC3_GUCTL3_SPLITDISABLE;
|
||||
@ -237,8 +243,8 @@ static void __dwc3_set_mode(struct work_struct *work)
|
||||
|
||||
if (dwc->usb2_phy)
|
||||
otg_set_vbus(dwc->usb2_phy->otg, false);
|
||||
phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_DEVICE);
|
||||
phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_DEVICE);
|
||||
phy_set_mode(dwc->usb2_generic_phy[0], PHY_MODE_USB_DEVICE);
|
||||
phy_set_mode(dwc->usb3_generic_phy[0], PHY_MODE_USB_DEVICE);
|
||||
|
||||
ret = dwc3_gadget_init(dwc);
|
||||
if (ret)
|
||||
@ -506,6 +512,13 @@ static void dwc3_free_event_buffers(struct dwc3 *dwc)
|
||||
static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned int length)
|
||||
{
|
||||
struct dwc3_event_buffer *evt;
|
||||
unsigned int hw_mode;
|
||||
|
||||
hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0);
|
||||
if (hw_mode == DWC3_GHWPARAMS0_MODE_HOST) {
|
||||
dwc->ev_buf = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
evt = dwc3_alloc_one_event_buffer(dwc, length);
|
||||
if (IS_ERR(evt)) {
|
||||
@ -527,6 +540,9 @@ int dwc3_event_buffers_setup(struct dwc3 *dwc)
|
||||
{
|
||||
struct dwc3_event_buffer *evt;
|
||||
|
||||
if (!dwc->ev_buf)
|
||||
return 0;
|
||||
|
||||
evt = dwc->ev_buf;
|
||||
evt->lpos = 0;
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(0),
|
||||
@ -544,6 +560,9 @@ void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
|
||||
{
|
||||
struct dwc3_event_buffer *evt;
|
||||
|
||||
if (!dwc->ev_buf)
|
||||
return;
|
||||
|
||||
evt = dwc->ev_buf;
|
||||
|
||||
evt->lpos = 0;
|
||||
@ -596,19 +615,11 @@ static int dwc3_core_ulpi_init(struct dwc3 *dwc)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core
|
||||
* @dwc: Pointer to our controller context structure
|
||||
*
|
||||
* Returns 0 on success. The USB PHY interfaces are configured but not
|
||||
* initialized. The PHY interfaces and the PHYs get initialized together with
|
||||
* the core in dwc3_core_init.
|
||||
*/
|
||||
static int dwc3_phy_setup(struct dwc3 *dwc)
|
||||
static int dwc3_ss_phy_setup(struct dwc3 *dwc, int index)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(index));
|
||||
|
||||
/*
|
||||
* Make sure UX_EXIT_PX is cleared as that causes issues with some
|
||||
@ -655,9 +666,16 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
|
||||
if (dwc->dis_del_phy_power_chg_quirk)
|
||||
reg &= ~DWC3_GUSB3PIPECTL_DEPOCHANGE;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(index), reg);
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_hs_phy_setup(struct dwc3 *dwc, int index)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(index));
|
||||
|
||||
/* Select the HS PHY interface */
|
||||
switch (DWC3_GHWPARAMS3_HSPHY_IFC(dwc->hwparams.hwparams3)) {
|
||||
@ -669,7 +687,7 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
|
||||
} else if (dwc->hsphy_interface &&
|
||||
!strncmp(dwc->hsphy_interface, "ulpi", 4)) {
|
||||
reg |= DWC3_GUSB2PHYCFG_ULPI_UTMI;
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(index), reg);
|
||||
} else {
|
||||
/* Relying on default value. */
|
||||
if (!(reg & DWC3_GUSB2PHYCFG_ULPI_UTMI))
|
||||
@ -727,7 +745,35 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
|
||||
if (dwc->ulpi_ext_vbus_drv)
|
||||
reg |= DWC3_GUSB2PHYCFG_ULPIEXTVBUSDRV;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(index), reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core
|
||||
* @dwc: Pointer to our controller context structure
|
||||
*
|
||||
* Returns 0 on success. The USB PHY interfaces are configured but not
|
||||
* initialized. The PHY interfaces and the PHYs get initialized together with
|
||||
* the core in dwc3_core_init.
|
||||
*/
|
||||
static int dwc3_phy_setup(struct dwc3 *dwc)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
for (i = 0; i < dwc->num_usb3_ports; i++) {
|
||||
ret = dwc3_ss_phy_setup(dwc, i);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < dwc->num_usb2_ports; i++) {
|
||||
ret = dwc3_hs_phy_setup(dwc, i);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -735,23 +781,34 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
|
||||
static int dwc3_phy_init(struct dwc3 *dwc)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
int j;
|
||||
|
||||
usb_phy_init(dwc->usb2_phy);
|
||||
usb_phy_init(dwc->usb3_phy);
|
||||
|
||||
ret = phy_init(dwc->usb2_generic_phy);
|
||||
if (ret < 0)
|
||||
goto err_shutdown_usb3_phy;
|
||||
for (i = 0; i < dwc->num_usb2_ports; i++) {
|
||||
ret = phy_init(dwc->usb2_generic_phy[i]);
|
||||
if (ret < 0)
|
||||
goto err_exit_usb2_phy;
|
||||
}
|
||||
|
||||
ret = phy_init(dwc->usb3_generic_phy);
|
||||
if (ret < 0)
|
||||
goto err_exit_usb2_phy;
|
||||
for (j = 0; j < dwc->num_usb3_ports; j++) {
|
||||
ret = phy_init(dwc->usb3_generic_phy[j]);
|
||||
if (ret < 0)
|
||||
goto err_exit_usb3_phy;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_exit_usb3_phy:
|
||||
while (--j >= 0)
|
||||
phy_exit(dwc->usb3_generic_phy[j]);
|
||||
|
||||
err_exit_usb2_phy:
|
||||
phy_exit(dwc->usb2_generic_phy);
|
||||
err_shutdown_usb3_phy:
|
||||
while (--i >= 0)
|
||||
phy_exit(dwc->usb2_generic_phy[i]);
|
||||
|
||||
usb_phy_shutdown(dwc->usb3_phy);
|
||||
usb_phy_shutdown(dwc->usb2_phy);
|
||||
|
||||
@ -760,8 +817,13 @@ err_shutdown_usb3_phy:
|
||||
|
||||
static void dwc3_phy_exit(struct dwc3 *dwc)
|
||||
{
|
||||
phy_exit(dwc->usb3_generic_phy);
|
||||
phy_exit(dwc->usb2_generic_phy);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dwc->num_usb3_ports; i++)
|
||||
phy_exit(dwc->usb3_generic_phy[i]);
|
||||
|
||||
for (i = 0; i < dwc->num_usb2_ports; i++)
|
||||
phy_exit(dwc->usb2_generic_phy[i]);
|
||||
|
||||
usb_phy_shutdown(dwc->usb3_phy);
|
||||
usb_phy_shutdown(dwc->usb2_phy);
|
||||
@ -770,23 +832,34 @@ static void dwc3_phy_exit(struct dwc3 *dwc)
|
||||
static int dwc3_phy_power_on(struct dwc3 *dwc)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
int j;
|
||||
|
||||
usb_phy_set_suspend(dwc->usb2_phy, 0);
|
||||
usb_phy_set_suspend(dwc->usb3_phy, 0);
|
||||
|
||||
ret = phy_power_on(dwc->usb2_generic_phy);
|
||||
if (ret < 0)
|
||||
goto err_suspend_usb3_phy;
|
||||
for (i = 0; i < dwc->num_usb2_ports; i++) {
|
||||
ret = phy_power_on(dwc->usb2_generic_phy[i]);
|
||||
if (ret < 0)
|
||||
goto err_power_off_usb2_phy;
|
||||
}
|
||||
|
||||
ret = phy_power_on(dwc->usb3_generic_phy);
|
||||
if (ret < 0)
|
||||
goto err_power_off_usb2_phy;
|
||||
for (j = 0; j < dwc->num_usb3_ports; j++) {
|
||||
ret = phy_power_on(dwc->usb3_generic_phy[j]);
|
||||
if (ret < 0)
|
||||
goto err_power_off_usb3_phy;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_power_off_usb3_phy:
|
||||
while (--j >= 0)
|
||||
phy_power_off(dwc->usb3_generic_phy[j]);
|
||||
|
||||
err_power_off_usb2_phy:
|
||||
phy_power_off(dwc->usb2_generic_phy);
|
||||
err_suspend_usb3_phy:
|
||||
while (--i >= 0)
|
||||
phy_power_off(dwc->usb2_generic_phy[i]);
|
||||
|
||||
usb_phy_set_suspend(dwc->usb3_phy, 1);
|
||||
usb_phy_set_suspend(dwc->usb2_phy, 1);
|
||||
|
||||
@ -795,8 +868,13 @@ err_suspend_usb3_phy:
|
||||
|
||||
static void dwc3_phy_power_off(struct dwc3 *dwc)
|
||||
{
|
||||
phy_power_off(dwc->usb3_generic_phy);
|
||||
phy_power_off(dwc->usb2_generic_phy);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dwc->num_usb3_ports; i++)
|
||||
phy_power_off(dwc->usb3_generic_phy[i]);
|
||||
|
||||
for (i = 0; i < dwc->num_usb2_ports; i++)
|
||||
phy_power_off(dwc->usb2_generic_phy[i]);
|
||||
|
||||
usb_phy_set_suspend(dwc->usb3_phy, 1);
|
||||
usb_phy_set_suspend(dwc->usb2_phy, 1);
|
||||
@ -1306,10 +1384,13 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
||||
if (dwc->parkmode_disable_hs_quirk)
|
||||
reg |= DWC3_GUCTL1_PARKMODE_DISABLE_HS;
|
||||
|
||||
if (DWC3_VER_IS_WITHIN(DWC3, 290A, ANY) &&
|
||||
(dwc->maximum_speed == USB_SPEED_HIGH ||
|
||||
dwc->maximum_speed == USB_SPEED_FULL))
|
||||
reg |= DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK;
|
||||
if (DWC3_VER_IS_WITHIN(DWC3, 290A, ANY)) {
|
||||
if (dwc->maximum_speed == USB_SPEED_FULL ||
|
||||
dwc->maximum_speed == USB_SPEED_HIGH)
|
||||
reg |= DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK;
|
||||
else
|
||||
reg &= ~DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK;
|
||||
}
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
|
||||
}
|
||||
@ -1344,7 +1425,9 @@ static int dwc3_core_get_phy(struct dwc3 *dwc)
|
||||
{
|
||||
struct device *dev = dwc->dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
char phy_name[9];
|
||||
int ret;
|
||||
u8 i;
|
||||
|
||||
if (node) {
|
||||
dwc->usb2_phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 0);
|
||||
@ -1370,22 +1453,38 @@ static int dwc3_core_get_phy(struct dwc3 *dwc)
|
||||
return dev_err_probe(dev, ret, "no usb3 phy configured\n");
|
||||
}
|
||||
|
||||
dwc->usb2_generic_phy = devm_phy_get(dev, "usb2-phy");
|
||||
if (IS_ERR(dwc->usb2_generic_phy)) {
|
||||
ret = PTR_ERR(dwc->usb2_generic_phy);
|
||||
if (ret == -ENOSYS || ret == -ENODEV)
|
||||
dwc->usb2_generic_phy = NULL;
|
||||
for (i = 0; i < dwc->num_usb2_ports; i++) {
|
||||
if (dwc->num_usb2_ports == 1)
|
||||
snprintf(phy_name, sizeof(phy_name), "usb2-phy");
|
||||
else
|
||||
return dev_err_probe(dev, ret, "no usb2 phy configured\n");
|
||||
snprintf(phy_name, sizeof(phy_name), "usb2-%u", i);
|
||||
|
||||
dwc->usb2_generic_phy[i] = devm_phy_get(dev, phy_name);
|
||||
if (IS_ERR(dwc->usb2_generic_phy[i])) {
|
||||
ret = PTR_ERR(dwc->usb2_generic_phy[i]);
|
||||
if (ret == -ENOSYS || ret == -ENODEV)
|
||||
dwc->usb2_generic_phy[i] = NULL;
|
||||
else
|
||||
return dev_err_probe(dev, ret, "failed to lookup phy %s\n",
|
||||
phy_name);
|
||||
}
|
||||
}
|
||||
|
||||
dwc->usb3_generic_phy = devm_phy_get(dev, "usb3-phy");
|
||||
if (IS_ERR(dwc->usb3_generic_phy)) {
|
||||
ret = PTR_ERR(dwc->usb3_generic_phy);
|
||||
if (ret == -ENOSYS || ret == -ENODEV)
|
||||
dwc->usb3_generic_phy = NULL;
|
||||
for (i = 0; i < dwc->num_usb3_ports; i++) {
|
||||
if (dwc->num_usb3_ports == 1)
|
||||
snprintf(phy_name, sizeof(phy_name), "usb3-phy");
|
||||
else
|
||||
return dev_err_probe(dev, ret, "no usb3 phy configured\n");
|
||||
snprintf(phy_name, sizeof(phy_name), "usb3-%u", i);
|
||||
|
||||
dwc->usb3_generic_phy[i] = devm_phy_get(dev, phy_name);
|
||||
if (IS_ERR(dwc->usb3_generic_phy[i])) {
|
||||
ret = PTR_ERR(dwc->usb3_generic_phy[i]);
|
||||
if (ret == -ENOSYS || ret == -ENODEV)
|
||||
dwc->usb3_generic_phy[i] = NULL;
|
||||
else
|
||||
return dev_err_probe(dev, ret, "failed to lookup phy %s\n",
|
||||
phy_name);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1395,6 +1494,7 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
|
||||
{
|
||||
struct device *dev = dwc->dev;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
switch (dwc->dr_mode) {
|
||||
case USB_DR_MODE_PERIPHERAL:
|
||||
@ -1402,8 +1502,8 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
|
||||
|
||||
if (dwc->usb2_phy)
|
||||
otg_set_vbus(dwc->usb2_phy->otg, false);
|
||||
phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_DEVICE);
|
||||
phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_DEVICE);
|
||||
phy_set_mode(dwc->usb2_generic_phy[0], PHY_MODE_USB_DEVICE);
|
||||
phy_set_mode(dwc->usb3_generic_phy[0], PHY_MODE_USB_DEVICE);
|
||||
|
||||
ret = dwc3_gadget_init(dwc);
|
||||
if (ret)
|
||||
@ -1414,8 +1514,10 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
|
||||
|
||||
if (dwc->usb2_phy)
|
||||
otg_set_vbus(dwc->usb2_phy->otg, true);
|
||||
phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST);
|
||||
phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST);
|
||||
for (i = 0; i < dwc->num_usb2_ports; i++)
|
||||
phy_set_mode(dwc->usb2_generic_phy[i], PHY_MODE_USB_HOST);
|
||||
for (i = 0; i < dwc->num_usb3_ports; i++)
|
||||
phy_set_mode(dwc->usb3_generic_phy[i], PHY_MODE_USB_HOST);
|
||||
|
||||
ret = dwc3_host_init(dwc);
|
||||
if (ret)
|
||||
@ -1867,10 +1969,60 @@ static int dwc3_get_clocks(struct dwc3 *dwc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_get_num_ports(struct dwc3 *dwc)
|
||||
{
|
||||
void __iomem *base;
|
||||
u8 major_revision;
|
||||
u32 offset;
|
||||
u32 val;
|
||||
|
||||
/*
|
||||
* Remap xHCI address space to access XHCI ext cap regs since it is
|
||||
* needed to get information on number of ports present.
|
||||
*/
|
||||
base = ioremap(dwc->xhci_resources[0].start,
|
||||
resource_size(&dwc->xhci_resources[0]));
|
||||
if (!base)
|
||||
return -ENOMEM;
|
||||
|
||||
offset = 0;
|
||||
do {
|
||||
offset = xhci_find_next_ext_cap(base, offset,
|
||||
XHCI_EXT_CAPS_PROTOCOL);
|
||||
if (!offset)
|
||||
break;
|
||||
|
||||
val = readl(base + offset);
|
||||
major_revision = XHCI_EXT_PORT_MAJOR(val);
|
||||
|
||||
val = readl(base + offset + 0x08);
|
||||
if (major_revision == 0x03) {
|
||||
dwc->num_usb3_ports += XHCI_EXT_PORT_COUNT(val);
|
||||
} else if (major_revision <= 0x02) {
|
||||
dwc->num_usb2_ports += XHCI_EXT_PORT_COUNT(val);
|
||||
} else {
|
||||
dev_warn(dwc->dev, "unrecognized port major revision %d\n",
|
||||
major_revision);
|
||||
}
|
||||
} while (1);
|
||||
|
||||
dev_dbg(dwc->dev, "hs-ports: %u ss-ports: %u\n",
|
||||
dwc->num_usb2_ports, dwc->num_usb3_ports);
|
||||
|
||||
iounmap(base);
|
||||
|
||||
if (dwc->num_usb2_ports > DWC3_USB2_MAX_PORTS ||
|
||||
dwc->num_usb3_ports > DWC3_USB3_MAX_PORTS)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res, dwc_res;
|
||||
unsigned int hw_mode;
|
||||
void __iomem *regs;
|
||||
struct dwc3 *dwc;
|
||||
int ret;
|
||||
@ -1954,6 +2106,20 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
goto err_disable_clks;
|
||||
}
|
||||
|
||||
/*
|
||||
* Currently only DWC3 controllers that are host-only capable
|
||||
* can have more than one port.
|
||||
*/
|
||||
hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0);
|
||||
if (hw_mode == DWC3_GHWPARAMS0_MODE_HOST) {
|
||||
ret = dwc3_get_num_ports(dwc);
|
||||
if (ret)
|
||||
goto err_disable_clks;
|
||||
} else {
|
||||
dwc->num_usb2_ports = 1;
|
||||
dwc->num_usb3_ports = 1;
|
||||
}
|
||||
|
||||
spin_lock_init(&dwc->lock);
|
||||
mutex_init(&dwc->mutex);
|
||||
|
||||
@ -2086,6 +2252,7 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 reg;
|
||||
int i;
|
||||
|
||||
switch (dwc->current_dr_role) {
|
||||
case DWC3_GCTL_PRTCAP_DEVICE:
|
||||
@ -2104,17 +2271,21 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
|
||||
/* Let controller to suspend HSPHY before PHY driver suspends */
|
||||
if (dwc->dis_u2_susphy_quirk ||
|
||||
dwc->dis_enblslpm_quirk) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
|
||||
reg |= DWC3_GUSB2PHYCFG_ENBLSLPM |
|
||||
DWC3_GUSB2PHYCFG_SUSPHY;
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
|
||||
for (i = 0; i < dwc->num_usb2_ports; i++) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(i));
|
||||
reg |= DWC3_GUSB2PHYCFG_ENBLSLPM |
|
||||
DWC3_GUSB2PHYCFG_SUSPHY;
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(i), reg);
|
||||
}
|
||||
|
||||
/* Give some time for USB2 PHY to suspend */
|
||||
usleep_range(5000, 6000);
|
||||
}
|
||||
|
||||
phy_pm_runtime_put_sync(dwc->usb2_generic_phy);
|
||||
phy_pm_runtime_put_sync(dwc->usb3_generic_phy);
|
||||
for (i = 0; i < dwc->num_usb2_ports; i++)
|
||||
phy_pm_runtime_put_sync(dwc->usb2_generic_phy[i]);
|
||||
for (i = 0; i < dwc->num_usb3_ports; i++)
|
||||
phy_pm_runtime_put_sync(dwc->usb3_generic_phy[i]);
|
||||
break;
|
||||
case DWC3_GCTL_PRTCAP_OTG:
|
||||
/* do nothing during runtime_suspend */
|
||||
@ -2144,6 +2315,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
u32 reg;
|
||||
int i;
|
||||
|
||||
switch (dwc->current_dr_role) {
|
||||
case DWC3_GCTL_PRTCAP_DEVICE:
|
||||
@ -2163,17 +2335,21 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
|
||||
break;
|
||||
}
|
||||
/* Restore GUSB2PHYCFG bits that were modified in suspend */
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
|
||||
if (dwc->dis_u2_susphy_quirk)
|
||||
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
|
||||
for (i = 0; i < dwc->num_usb2_ports; i++) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(i));
|
||||
if (dwc->dis_u2_susphy_quirk)
|
||||
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
|
||||
|
||||
if (dwc->dis_enblslpm_quirk)
|
||||
reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM;
|
||||
if (dwc->dis_enblslpm_quirk)
|
||||
reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(i), reg);
|
||||
}
|
||||
|
||||
phy_pm_runtime_get_sync(dwc->usb2_generic_phy);
|
||||
phy_pm_runtime_get_sync(dwc->usb3_generic_phy);
|
||||
for (i = 0; i < dwc->num_usb2_ports; i++)
|
||||
phy_pm_runtime_get_sync(dwc->usb2_generic_phy[i]);
|
||||
for (i = 0; i < dwc->num_usb3_ports; i++)
|
||||
phy_pm_runtime_get_sync(dwc->usb3_generic_phy[i]);
|
||||
break;
|
||||
case DWC3_GCTL_PRTCAP_OTG:
|
||||
/* nothing to do on runtime_resume */
|
||||
|
@ -33,6 +33,13 @@
|
||||
|
||||
#include <linux/power_supply.h>
|
||||
|
||||
/*
|
||||
* DWC3 Multiport controllers support up to 15 High-Speed PHYs
|
||||
* and 4 SuperSpeed PHYs.
|
||||
*/
|
||||
#define DWC3_USB2_MAX_PORTS 15
|
||||
#define DWC3_USB3_MAX_PORTS 4
|
||||
|
||||
#define DWC3_MSG_MAX 500
|
||||
|
||||
/* Global constants */
|
||||
@ -1037,8 +1044,10 @@ struct dwc3_scratchpad_array {
|
||||
* @usb_psy: pointer to power supply interface.
|
||||
* @usb2_phy: pointer to USB2 PHY
|
||||
* @usb3_phy: pointer to USB3 PHY
|
||||
* @usb2_generic_phy: pointer to USB2 PHY
|
||||
* @usb3_generic_phy: pointer to USB3 PHY
|
||||
* @usb2_generic_phy: pointer to array of USB2 PHYs
|
||||
* @usb3_generic_phy: pointer to array of USB3 PHYs
|
||||
* @num_usb2_ports: number of USB2 ports
|
||||
* @num_usb3_ports: number of USB3 ports
|
||||
* @phys_ready: flag to indicate that PHYs are ready
|
||||
* @ulpi: pointer to ulpi interface
|
||||
* @ulpi_ready: flag to indicate that ULPI is initialized
|
||||
@ -1184,8 +1193,11 @@ struct dwc3 {
|
||||
struct usb_phy *usb2_phy;
|
||||
struct usb_phy *usb3_phy;
|
||||
|
||||
struct phy *usb2_generic_phy;
|
||||
struct phy *usb3_generic_phy;
|
||||
struct phy *usb2_generic_phy[DWC3_USB2_MAX_PORTS];
|
||||
struct phy *usb3_generic_phy[DWC3_USB3_MAX_PORTS];
|
||||
|
||||
u8 num_usb2_ports;
|
||||
u8 num_usb3_ports;
|
||||
|
||||
bool phys_ready;
|
||||
|
||||
|
@ -331,6 +331,7 @@ void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus)
|
||||
u32 reg;
|
||||
int id;
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
if (dwc->dr_mode != USB_DR_MODE_OTG)
|
||||
return;
|
||||
@ -386,9 +387,12 @@ void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus)
|
||||
} else {
|
||||
if (dwc->usb2_phy)
|
||||
otg_set_vbus(dwc->usb2_phy->otg, true);
|
||||
if (dwc->usb2_generic_phy)
|
||||
phy_set_mode(dwc->usb2_generic_phy,
|
||||
PHY_MODE_USB_HOST);
|
||||
for (i = 0; i < dwc->num_usb2_ports; i++) {
|
||||
if (dwc->usb2_generic_phy[i]) {
|
||||
phy_set_mode(dwc->usb2_generic_phy[i],
|
||||
PHY_MODE_USB_HOST);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DWC3_OTG_ROLE_DEVICE:
|
||||
@ -400,9 +404,8 @@ void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus)
|
||||
|
||||
if (dwc->usb2_phy)
|
||||
otg_set_vbus(dwc->usb2_phy->otg, false);
|
||||
if (dwc->usb2_generic_phy)
|
||||
phy_set_mode(dwc->usb2_generic_phy,
|
||||
PHY_MODE_USB_DEVICE);
|
||||
if (dwc->usb2_generic_phy[0])
|
||||
phy_set_mode(dwc->usb2_generic_phy[0], PHY_MODE_USB_DEVICE);
|
||||
ret = dwc3_gadget_init(dwc);
|
||||
if (ret)
|
||||
dev_err(dwc->dev, "failed to initialize peripheral\n");
|
||||
|
@ -169,6 +169,12 @@ static const struct dwc3_exynos_driverdata exynos850_drvdata = {
|
||||
.suspend_clk_idx = -1,
|
||||
};
|
||||
|
||||
static const struct dwc3_exynos_driverdata gs101_drvdata = {
|
||||
.clk_names = { "bus_early", "susp_clk", "link_aclk", "link_pclk" },
|
||||
.num_clks = 4,
|
||||
.suspend_clk_idx = 1,
|
||||
};
|
||||
|
||||
static const struct of_device_id exynos_dwc3_match[] = {
|
||||
{
|
||||
.compatible = "samsung,exynos5250-dwusb3",
|
||||
@ -182,12 +188,14 @@ static const struct of_device_id exynos_dwc3_match[] = {
|
||||
}, {
|
||||
.compatible = "samsung,exynos850-dwusb3",
|
||||
.data = &exynos850_drvdata,
|
||||
}, {
|
||||
.compatible = "google,gs101-dwusb3",
|
||||
.data = &gs101_drvdata,
|
||||
}, {
|
||||
}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, exynos_dwc3_match);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int dwc3_exynos_suspend(struct device *dev)
|
||||
{
|
||||
struct dwc3_exynos *exynos = dev_get_drvdata(dev);
|
||||
@ -230,14 +238,8 @@ static int dwc3_exynos_resume(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops dwc3_exynos_dev_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(dwc3_exynos_suspend, dwc3_exynos_resume)
|
||||
};
|
||||
|
||||
#define DEV_PM_OPS (&dwc3_exynos_dev_pm_ops)
|
||||
#else
|
||||
#define DEV_PM_OPS NULL
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(dwc3_exynos_dev_pm_ops,
|
||||
dwc3_exynos_suspend, dwc3_exynos_resume);
|
||||
|
||||
static struct platform_driver dwc3_exynos_driver = {
|
||||
.probe = dwc3_exynos_probe,
|
||||
@ -245,7 +247,7 @@ static struct platform_driver dwc3_exynos_driver = {
|
||||
.driver = {
|
||||
.name = "exynos-dwc3",
|
||||
.of_match_table = exynos_dwc3_match,
|
||||
.pm = DEV_PM_OPS,
|
||||
.pm = pm_sleep_ptr(&dwc3_exynos_dev_pm_ops),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
*/
|
||||
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
@ -220,6 +221,7 @@ static int dwc3_pci_quirks(struct dwc3_pci *dwc,
|
||||
|
||||
if (pdev->device == PCI_DEVICE_ID_INTEL_BYT) {
|
||||
struct gpio_desc *gpio;
|
||||
const char *bios_ver;
|
||||
int ret;
|
||||
|
||||
/* On BYT the FW does not always enable the refclock */
|
||||
@ -277,8 +279,12 @@ static int dwc3_pci_quirks(struct dwc3_pci *dwc,
|
||||
* detection. These can be identified by them _not_
|
||||
* using the standard ACPI battery and ac drivers.
|
||||
*/
|
||||
bios_ver = dmi_get_system_info(DMI_BIOS_VERSION);
|
||||
if (acpi_dev_present("INT33FD", "1", 2) &&
|
||||
acpi_quirk_skip_acpi_ac_and_battery()) {
|
||||
acpi_quirk_skip_acpi_ac_and_battery() &&
|
||||
/* Lenovo Yoga Tablet 2 Pro 1380 uses LC824206XA instead */
|
||||
!(bios_ver &&
|
||||
strstarts(bios_ver, "BLADE_21.X64.0005.R00.1504101516"))) {
|
||||
dev_info(&pdev->dev, "Using TUSB1211 phy for charger detection\n");
|
||||
swnode = &dwc3_pci_intel_phy_charger_detect_swnode;
|
||||
}
|
||||
|
@ -36,7 +36,6 @@
|
||||
#define PIPE3_PHYSTATUS_SW BIT(3)
|
||||
#define PIPE_UTMI_CLK_DIS BIT(8)
|
||||
|
||||
#define PWR_EVNT_IRQ_STAT_REG 0x58
|
||||
#define PWR_EVNT_LPM_IN_L2_MASK BIT(4)
|
||||
#define PWR_EVNT_LPM_OUT_L2_MASK BIT(5)
|
||||
|
||||
@ -52,6 +51,24 @@
|
||||
#define APPS_USB_AVG_BW 0
|
||||
#define APPS_USB_PEAK_BW MBps_to_icc(40)
|
||||
|
||||
/* Qualcomm SoCs with multiport support has up to 4 ports */
|
||||
#define DWC3_QCOM_MAX_PORTS 4
|
||||
|
||||
static const u32 pwr_evnt_irq_stat_reg[DWC3_QCOM_MAX_PORTS] = {
|
||||
0x58,
|
||||
0x1dc,
|
||||
0x228,
|
||||
0x238,
|
||||
};
|
||||
|
||||
struct dwc3_qcom_port {
|
||||
int qusb2_phy_irq;
|
||||
int dp_hs_phy_irq;
|
||||
int dm_hs_phy_irq;
|
||||
int ss_phy_irq;
|
||||
enum usb_device_speed usb2_speed;
|
||||
};
|
||||
|
||||
struct dwc3_qcom {
|
||||
struct device *dev;
|
||||
void __iomem *qscratch_base;
|
||||
@ -59,12 +76,8 @@ struct dwc3_qcom {
|
||||
struct clk **clks;
|
||||
int num_clocks;
|
||||
struct reset_control *resets;
|
||||
|
||||
int qusb2_phy_irq;
|
||||
int dp_hs_phy_irq;
|
||||
int dm_hs_phy_irq;
|
||||
int ss_phy_irq;
|
||||
enum usb_device_speed usb2_speed;
|
||||
struct dwc3_qcom_port ports[DWC3_QCOM_MAX_PORTS];
|
||||
u8 num_ports;
|
||||
|
||||
struct extcon_dev *edev;
|
||||
struct extcon_dev *host_edev;
|
||||
@ -303,7 +316,7 @@ static bool dwc3_qcom_is_host(struct dwc3_qcom *qcom)
|
||||
return dwc->xhci;
|
||||
}
|
||||
|
||||
static enum usb_device_speed dwc3_qcom_read_usb2_speed(struct dwc3_qcom *qcom)
|
||||
static enum usb_device_speed dwc3_qcom_read_usb2_speed(struct dwc3_qcom *qcom, int port_index)
|
||||
{
|
||||
struct dwc3 *dwc = platform_get_drvdata(qcom->dwc3);
|
||||
struct usb_device *udev;
|
||||
@ -314,14 +327,8 @@ static enum usb_device_speed dwc3_qcom_read_usb2_speed(struct dwc3_qcom *qcom)
|
||||
*/
|
||||
hcd = platform_get_drvdata(dwc->xhci);
|
||||
|
||||
/*
|
||||
* It is possible to query the speed of all children of
|
||||
* USB2.0 root hub via usb_hub_for_each_child(). DWC3 code
|
||||
* currently supports only 1 port per controller. So
|
||||
* this is sufficient.
|
||||
*/
|
||||
#ifdef CONFIG_USB
|
||||
udev = usb_hub_find_child(hcd->self.root_hub, 1);
|
||||
udev = usb_hub_find_child(hcd->self.root_hub, port_index + 1);
|
||||
#else
|
||||
udev = NULL;
|
||||
#endif
|
||||
@ -352,26 +359,26 @@ static void dwc3_qcom_disable_wakeup_irq(int irq)
|
||||
disable_irq_nosync(irq);
|
||||
}
|
||||
|
||||
static void dwc3_qcom_disable_interrupts(struct dwc3_qcom *qcom)
|
||||
static void dwc3_qcom_disable_port_interrupts(struct dwc3_qcom_port *port)
|
||||
{
|
||||
dwc3_qcom_disable_wakeup_irq(qcom->qusb2_phy_irq);
|
||||
dwc3_qcom_disable_wakeup_irq(port->qusb2_phy_irq);
|
||||
|
||||
if (qcom->usb2_speed == USB_SPEED_LOW) {
|
||||
dwc3_qcom_disable_wakeup_irq(qcom->dm_hs_phy_irq);
|
||||
} else if ((qcom->usb2_speed == USB_SPEED_HIGH) ||
|
||||
(qcom->usb2_speed == USB_SPEED_FULL)) {
|
||||
dwc3_qcom_disable_wakeup_irq(qcom->dp_hs_phy_irq);
|
||||
if (port->usb2_speed == USB_SPEED_LOW) {
|
||||
dwc3_qcom_disable_wakeup_irq(port->dm_hs_phy_irq);
|
||||
} else if ((port->usb2_speed == USB_SPEED_HIGH) ||
|
||||
(port->usb2_speed == USB_SPEED_FULL)) {
|
||||
dwc3_qcom_disable_wakeup_irq(port->dp_hs_phy_irq);
|
||||
} else {
|
||||
dwc3_qcom_disable_wakeup_irq(qcom->dp_hs_phy_irq);
|
||||
dwc3_qcom_disable_wakeup_irq(qcom->dm_hs_phy_irq);
|
||||
dwc3_qcom_disable_wakeup_irq(port->dp_hs_phy_irq);
|
||||
dwc3_qcom_disable_wakeup_irq(port->dm_hs_phy_irq);
|
||||
}
|
||||
|
||||
dwc3_qcom_disable_wakeup_irq(qcom->ss_phy_irq);
|
||||
dwc3_qcom_disable_wakeup_irq(port->ss_phy_irq);
|
||||
}
|
||||
|
||||
static void dwc3_qcom_enable_interrupts(struct dwc3_qcom *qcom)
|
||||
static void dwc3_qcom_enable_port_interrupts(struct dwc3_qcom_port *port)
|
||||
{
|
||||
dwc3_qcom_enable_wakeup_irq(qcom->qusb2_phy_irq, 0);
|
||||
dwc3_qcom_enable_wakeup_irq(port->qusb2_phy_irq, 0);
|
||||
|
||||
/*
|
||||
* Configure DP/DM line interrupts based on the USB2 device attached to
|
||||
@ -382,21 +389,37 @@ static void dwc3_qcom_enable_interrupts(struct dwc3_qcom *qcom)
|
||||
* DP and DM lines as rising edge to detect HS/HS/LS device connect scenario.
|
||||
*/
|
||||
|
||||
if (qcom->usb2_speed == USB_SPEED_LOW) {
|
||||
dwc3_qcom_enable_wakeup_irq(qcom->dm_hs_phy_irq,
|
||||
IRQ_TYPE_EDGE_FALLING);
|
||||
} else if ((qcom->usb2_speed == USB_SPEED_HIGH) ||
|
||||
(qcom->usb2_speed == USB_SPEED_FULL)) {
|
||||
dwc3_qcom_enable_wakeup_irq(qcom->dp_hs_phy_irq,
|
||||
IRQ_TYPE_EDGE_FALLING);
|
||||
if (port->usb2_speed == USB_SPEED_LOW) {
|
||||
dwc3_qcom_enable_wakeup_irq(port->dm_hs_phy_irq,
|
||||
IRQ_TYPE_EDGE_FALLING);
|
||||
} else if ((port->usb2_speed == USB_SPEED_HIGH) ||
|
||||
(port->usb2_speed == USB_SPEED_FULL)) {
|
||||
dwc3_qcom_enable_wakeup_irq(port->dp_hs_phy_irq,
|
||||
IRQ_TYPE_EDGE_FALLING);
|
||||
} else {
|
||||
dwc3_qcom_enable_wakeup_irq(qcom->dp_hs_phy_irq,
|
||||
IRQ_TYPE_EDGE_RISING);
|
||||
dwc3_qcom_enable_wakeup_irq(qcom->dm_hs_phy_irq,
|
||||
IRQ_TYPE_EDGE_RISING);
|
||||
dwc3_qcom_enable_wakeup_irq(port->dp_hs_phy_irq,
|
||||
IRQ_TYPE_EDGE_RISING);
|
||||
dwc3_qcom_enable_wakeup_irq(port->dm_hs_phy_irq,
|
||||
IRQ_TYPE_EDGE_RISING);
|
||||
}
|
||||
|
||||
dwc3_qcom_enable_wakeup_irq(qcom->ss_phy_irq, 0);
|
||||
dwc3_qcom_enable_wakeup_irq(port->ss_phy_irq, 0);
|
||||
}
|
||||
|
||||
static void dwc3_qcom_disable_interrupts(struct dwc3_qcom *qcom)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < qcom->num_ports; i++)
|
||||
dwc3_qcom_disable_port_interrupts(&qcom->ports[i]);
|
||||
}
|
||||
|
||||
static void dwc3_qcom_enable_interrupts(struct dwc3_qcom *qcom)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < qcom->num_ports; i++)
|
||||
dwc3_qcom_enable_port_interrupts(&qcom->ports[i]);
|
||||
}
|
||||
|
||||
static int dwc3_qcom_suspend(struct dwc3_qcom *qcom, bool wakeup)
|
||||
@ -407,9 +430,11 @@ static int dwc3_qcom_suspend(struct dwc3_qcom *qcom, bool wakeup)
|
||||
if (qcom->is_suspended)
|
||||
return 0;
|
||||
|
||||
val = readl(qcom->qscratch_base + PWR_EVNT_IRQ_STAT_REG);
|
||||
if (!(val & PWR_EVNT_LPM_IN_L2_MASK))
|
||||
dev_err(qcom->dev, "HS-PHY not in L2\n");
|
||||
for (i = 0; i < qcom->num_ports; i++) {
|
||||
val = readl(qcom->qscratch_base + pwr_evnt_irq_stat_reg[i]);
|
||||
if (!(val & PWR_EVNT_LPM_IN_L2_MASK))
|
||||
dev_err(qcom->dev, "port-%d HS-PHY not in L2\n", i + 1);
|
||||
}
|
||||
|
||||
for (i = qcom->num_clocks - 1; i >= 0; i--)
|
||||
clk_disable_unprepare(qcom->clks[i]);
|
||||
@ -423,7 +448,8 @@ static int dwc3_qcom_suspend(struct dwc3_qcom *qcom, bool wakeup)
|
||||
* freezable workqueue.
|
||||
*/
|
||||
if (dwc3_qcom_is_host(qcom) && wakeup) {
|
||||
qcom->usb2_speed = dwc3_qcom_read_usb2_speed(qcom);
|
||||
for (i = 0; i < qcom->num_ports; i++)
|
||||
qcom->ports[i].usb2_speed = dwc3_qcom_read_usb2_speed(qcom, i);
|
||||
dwc3_qcom_enable_interrupts(qcom);
|
||||
}
|
||||
|
||||
@ -457,8 +483,11 @@ static int dwc3_qcom_resume(struct dwc3_qcom *qcom, bool wakeup)
|
||||
dev_warn(qcom->dev, "failed to enable interconnect: %d\n", ret);
|
||||
|
||||
/* Clear existing events from PHY related to L2 in/out */
|
||||
dwc3_qcom_setbits(qcom->qscratch_base, PWR_EVNT_IRQ_STAT_REG,
|
||||
PWR_EVNT_LPM_IN_L2_MASK | PWR_EVNT_LPM_OUT_L2_MASK);
|
||||
for (i = 0; i < qcom->num_ports; i++) {
|
||||
dwc3_qcom_setbits(qcom->qscratch_base,
|
||||
pwr_evnt_irq_stat_reg[i],
|
||||
PWR_EVNT_LPM_IN_L2_MASK | PWR_EVNT_LPM_OUT_L2_MASK);
|
||||
}
|
||||
|
||||
qcom->is_suspended = false;
|
||||
|
||||
@ -501,63 +530,123 @@ static void dwc3_qcom_select_utmi_clk(struct dwc3_qcom *qcom)
|
||||
PIPE_UTMI_CLK_DIS);
|
||||
}
|
||||
|
||||
static int dwc3_qcom_setup_irq(struct platform_device *pdev)
|
||||
static int dwc3_qcom_request_irq(struct dwc3_qcom *qcom, int irq,
|
||||
const char *name)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Keep wakeup interrupts disabled until suspend */
|
||||
ret = devm_request_threaded_irq(qcom->dev, irq, NULL,
|
||||
qcom_dwc3_resume_irq,
|
||||
IRQF_ONESHOT | IRQF_NO_AUTOEN,
|
||||
name, qcom);
|
||||
if (ret)
|
||||
dev_err(qcom->dev, "failed to request irq %s: %d\n", name, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dwc3_qcom_setup_port_irq(struct platform_device *pdev, int port_index, bool is_multiport)
|
||||
{
|
||||
struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
|
||||
const char *irq_name;
|
||||
int irq;
|
||||
int ret;
|
||||
|
||||
if (is_multiport)
|
||||
irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "dp_hs_phy_%d", port_index + 1);
|
||||
else
|
||||
irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "dp_hs_phy_irq");
|
||||
if (!irq_name)
|
||||
return -ENOMEM;
|
||||
|
||||
irq = platform_get_irq_byname_optional(pdev, irq_name);
|
||||
if (irq > 0) {
|
||||
ret = dwc3_qcom_request_irq(qcom, irq, irq_name);
|
||||
if (ret)
|
||||
return ret;
|
||||
qcom->ports[port_index].dp_hs_phy_irq = irq;
|
||||
}
|
||||
|
||||
if (is_multiport)
|
||||
irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "dm_hs_phy_%d", port_index + 1);
|
||||
else
|
||||
irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "dm_hs_phy_irq");
|
||||
if (!irq_name)
|
||||
return -ENOMEM;
|
||||
|
||||
irq = platform_get_irq_byname_optional(pdev, irq_name);
|
||||
if (irq > 0) {
|
||||
ret = dwc3_qcom_request_irq(qcom, irq, irq_name);
|
||||
if (ret)
|
||||
return ret;
|
||||
qcom->ports[port_index].dm_hs_phy_irq = irq;
|
||||
}
|
||||
|
||||
if (is_multiport)
|
||||
irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "ss_phy_%d", port_index + 1);
|
||||
else
|
||||
irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "ss_phy_irq");
|
||||
if (!irq_name)
|
||||
return -ENOMEM;
|
||||
|
||||
irq = platform_get_irq_byname_optional(pdev, irq_name);
|
||||
if (irq > 0) {
|
||||
ret = dwc3_qcom_request_irq(qcom, irq, irq_name);
|
||||
if (ret)
|
||||
return ret;
|
||||
qcom->ports[port_index].ss_phy_irq = irq;
|
||||
}
|
||||
|
||||
if (is_multiport)
|
||||
return 0;
|
||||
|
||||
irq = platform_get_irq_byname_optional(pdev, "qusb2_phy");
|
||||
if (irq > 0) {
|
||||
/* Keep wakeup interrupts disabled until suspend */
|
||||
ret = devm_request_threaded_irq(qcom->dev, irq, NULL,
|
||||
qcom_dwc3_resume_irq,
|
||||
IRQF_ONESHOT | IRQF_NO_AUTOEN,
|
||||
"qcom_dwc3 QUSB2", qcom);
|
||||
if (ret) {
|
||||
dev_err(qcom->dev, "qusb2_phy_irq failed: %d\n", ret);
|
||||
ret = dwc3_qcom_request_irq(qcom, irq, "qusb2_phy");
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
qcom->qusb2_phy_irq = irq;
|
||||
qcom->ports[port_index].qusb2_phy_irq = irq;
|
||||
}
|
||||
|
||||
irq = platform_get_irq_byname_optional(pdev, "dp_hs_phy_irq");
|
||||
if (irq > 0) {
|
||||
ret = devm_request_threaded_irq(qcom->dev, irq, NULL,
|
||||
qcom_dwc3_resume_irq,
|
||||
IRQF_ONESHOT | IRQF_NO_AUTOEN,
|
||||
"qcom_dwc3 DP_HS", qcom);
|
||||
if (ret) {
|
||||
dev_err(qcom->dev, "dp_hs_phy_irq failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
qcom->dp_hs_phy_irq = irq;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_qcom_find_num_ports(struct platform_device *pdev)
|
||||
{
|
||||
char irq_name[14];
|
||||
int port_num;
|
||||
int irq;
|
||||
|
||||
irq = platform_get_irq_byname_optional(pdev, "dp_hs_phy_1");
|
||||
if (irq <= 0)
|
||||
return 1;
|
||||
|
||||
for (port_num = 2; port_num <= DWC3_QCOM_MAX_PORTS; port_num++) {
|
||||
sprintf(irq_name, "dp_hs_phy_%d", port_num);
|
||||
|
||||
irq = platform_get_irq_byname_optional(pdev, irq_name);
|
||||
if (irq <= 0)
|
||||
return port_num - 1;
|
||||
}
|
||||
|
||||
irq = platform_get_irq_byname_optional(pdev, "dm_hs_phy_irq");
|
||||
if (irq > 0) {
|
||||
ret = devm_request_threaded_irq(qcom->dev, irq, NULL,
|
||||
qcom_dwc3_resume_irq,
|
||||
IRQF_ONESHOT | IRQF_NO_AUTOEN,
|
||||
"qcom_dwc3 DM_HS", qcom);
|
||||
if (ret) {
|
||||
dev_err(qcom->dev, "dm_hs_phy_irq failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
qcom->dm_hs_phy_irq = irq;
|
||||
}
|
||||
return DWC3_QCOM_MAX_PORTS;
|
||||
}
|
||||
|
||||
irq = platform_get_irq_byname_optional(pdev, "ss_phy_irq");
|
||||
if (irq > 0) {
|
||||
ret = devm_request_threaded_irq(qcom->dev, irq, NULL,
|
||||
qcom_dwc3_resume_irq,
|
||||
IRQF_ONESHOT | IRQF_NO_AUTOEN,
|
||||
"qcom_dwc3 SS", qcom);
|
||||
if (ret) {
|
||||
dev_err(qcom->dev, "ss_phy_irq failed: %d\n", ret);
|
||||
static int dwc3_qcom_setup_irq(struct platform_device *pdev)
|
||||
{
|
||||
struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
|
||||
bool is_multiport;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
qcom->num_ports = dwc3_qcom_find_num_ports(pdev);
|
||||
is_multiport = (qcom->num_ports > 1);
|
||||
|
||||
for (i = 0; i < qcom->num_ports; i++) {
|
||||
ret = dwc3_qcom_setup_port_irq(pdev, i, is_multiport);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
qcom->ss_phy_irq = irq;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -1699,7 +1699,6 @@ static int __dwc3_gadget_get_frame(struct dwc3 *dwc)
|
||||
*/
|
||||
static int __dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt)
|
||||
{
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
struct dwc3_gadget_ep_cmd_params params;
|
||||
u32 cmd;
|
||||
int ret;
|
||||
@ -1724,8 +1723,7 @@ static int __dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool int
|
||||
dep->resource_index = 0;
|
||||
|
||||
if (!interrupt) {
|
||||
if (!DWC3_IP_IS(DWC3) || DWC3_VER_IS_PRIOR(DWC3, 310A))
|
||||
mdelay(1);
|
||||
mdelay(1);
|
||||
dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
|
||||
} else if (!ret) {
|
||||
dep->flags |= DWC3_EP_END_TRANSFER_PENDING;
|
||||
|
@ -1,10 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
# This setup links the different object files into one single
|
||||
# module so we don't have to EXPORT() a lot of internal symbols
|
||||
# or create unnecessary submodules.
|
||||
fotg210-objs-y += fotg210-core.o
|
||||
fotg210-objs-$(CONFIG_USB_FOTG210_HCD) += fotg210-hcd.o
|
||||
fotg210-objs-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o
|
||||
fotg210-objs := $(fotg210-objs-y)
|
||||
obj-$(CONFIG_USB_FOTG210) += fotg210.o
|
||||
fotg210-y := fotg210-core.o
|
||||
fotg210-$(CONFIG_USB_FOTG210_HCD) += fotg210-hcd.o
|
||||
fotg210-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o
|
||||
|
@ -95,6 +95,7 @@ static int fotg210_gemini_init(struct fotg210 *fotg, struct resource *res,
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
void fotg210_vbus(struct fotg210 *fotg, bool enable)
|
||||
|
@ -45,6 +45,7 @@
|
||||
#include "configfs.h"
|
||||
|
||||
#define FUNCTIONFS_MAGIC 0xa647361 /* Chosen by a honest dice roll ;) */
|
||||
#define MAX_ALT_SETTINGS 2 /* Allow up to 2 alt settings to be set. */
|
||||
|
||||
#define DMABUF_ENQUEUE_TIMEOUT_MS 5000
|
||||
|
||||
@ -82,6 +83,7 @@ struct ffs_function {
|
||||
short *interfaces_nums;
|
||||
|
||||
struct usb_function function;
|
||||
int cur_alt[MAX_CONFIG_INTERFACES];
|
||||
};
|
||||
|
||||
|
||||
@ -105,6 +107,7 @@ static int __must_check ffs_func_eps_enable(struct ffs_function *func);
|
||||
static int ffs_func_bind(struct usb_configuration *,
|
||||
struct usb_function *);
|
||||
static int ffs_func_set_alt(struct usb_function *, unsigned, unsigned);
|
||||
static int ffs_func_get_alt(struct usb_function *f, unsigned int intf);
|
||||
static void ffs_func_disable(struct usb_function *);
|
||||
static int ffs_func_setup(struct usb_function *,
|
||||
const struct usb_ctrlrequest *);
|
||||
@ -3712,6 +3715,15 @@ static void ffs_reset_work(struct work_struct *work)
|
||||
ffs_data_reset(ffs);
|
||||
}
|
||||
|
||||
static int ffs_func_get_alt(struct usb_function *f,
|
||||
unsigned int interface)
|
||||
{
|
||||
struct ffs_function *func = ffs_func_from_usb(f);
|
||||
int intf = ffs_func_revmap_intf(func, interface);
|
||||
|
||||
return (intf < 0) ? intf : func->cur_alt[interface];
|
||||
}
|
||||
|
||||
static int ffs_func_set_alt(struct usb_function *f,
|
||||
unsigned interface, unsigned alt)
|
||||
{
|
||||
@ -3719,6 +3731,9 @@ static int ffs_func_set_alt(struct usb_function *f,
|
||||
struct ffs_data *ffs = func->ffs;
|
||||
int ret = 0, intf;
|
||||
|
||||
if (alt > MAX_ALT_SETTINGS)
|
||||
return -EINVAL;
|
||||
|
||||
if (alt != (unsigned)-1) {
|
||||
intf = ffs_func_revmap_intf(func, interface);
|
||||
if (intf < 0)
|
||||
@ -3746,8 +3761,10 @@ static int ffs_func_set_alt(struct usb_function *f,
|
||||
|
||||
ffs->func = func;
|
||||
ret = ffs_func_eps_enable(func);
|
||||
if (ret >= 0)
|
||||
if (ret >= 0) {
|
||||
ffs_event_add(ffs, FUNCTIONFS_ENABLE);
|
||||
func->cur_alt[interface] = alt;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -4074,6 +4091,7 @@ static struct usb_function *ffs_alloc(struct usb_function_instance *fi)
|
||||
func->function.bind = ffs_func_bind;
|
||||
func->function.unbind = ffs_func_unbind;
|
||||
func->function.set_alt = ffs_func_set_alt;
|
||||
func->function.get_alt = ffs_func_get_alt;
|
||||
func->function.disable = ffs_func_disable;
|
||||
func->function.setup = ffs_func_setup;
|
||||
func->function.req_match = ffs_func_req_match;
|
||||
|
@ -1029,9 +1029,9 @@ static inline int hidg_get_minor(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ida_simple_get(&hidg_ida, 0, 0, GFP_KERNEL);
|
||||
ret = ida_alloc(&hidg_ida, GFP_KERNEL);
|
||||
if (ret >= HIDG_MINORS) {
|
||||
ida_simple_remove(&hidg_ida, ret);
|
||||
ida_free(&hidg_ida, ret);
|
||||
ret = -ENODEV;
|
||||
}
|
||||
|
||||
@ -1176,7 +1176,7 @@ static const struct config_item_type hid_func_type = {
|
||||
|
||||
static inline void hidg_put_minor(int minor)
|
||||
{
|
||||
ida_simple_remove(&hidg_ida, minor);
|
||||
ida_free(&hidg_ida, minor);
|
||||
}
|
||||
|
||||
static void hidg_free_inst(struct usb_function_instance *f)
|
||||
|
@ -1312,9 +1312,9 @@ static inline int gprinter_get_minor(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ida_simple_get(&printer_ida, 0, 0, GFP_KERNEL);
|
||||
ret = ida_alloc(&printer_ida, GFP_KERNEL);
|
||||
if (ret >= PRINTER_MINORS) {
|
||||
ida_simple_remove(&printer_ida, ret);
|
||||
ida_free(&printer_ida, ret);
|
||||
ret = -ENODEV;
|
||||
}
|
||||
|
||||
@ -1323,7 +1323,7 @@ static inline int gprinter_get_minor(void)
|
||||
|
||||
static inline void gprinter_put_minor(int minor)
|
||||
{
|
||||
ida_simple_remove(&printer_ida, minor);
|
||||
ida_free(&printer_ida, minor);
|
||||
}
|
||||
|
||||
static int gprinter_setup(int);
|
||||
|
@ -869,12 +869,12 @@ EXPORT_SYMBOL_GPL(rndis_msg_parser);
|
||||
|
||||
static inline int rndis_get_nr(void)
|
||||
{
|
||||
return ida_simple_get(&rndis_ida, 0, 1000, GFP_KERNEL);
|
||||
return ida_alloc_max(&rndis_ida, 999, GFP_KERNEL);
|
||||
}
|
||||
|
||||
static inline void rndis_put_nr(int nr)
|
||||
{
|
||||
ida_simple_remove(&rndis_ida, nr);
|
||||
ida_free(&rndis_ida, nr);
|
||||
}
|
||||
|
||||
struct rndis_params *rndis_register(void (*resp_avail)(void *v), void *v)
|
||||
|
@ -57,13 +57,13 @@ struct uac_rtd_params {
|
||||
|
||||
/* Volume/Mute controls and their state */
|
||||
int fu_id; /* Feature Unit ID */
|
||||
struct snd_kcontrol *snd_kctl_volume;
|
||||
struct snd_kcontrol *snd_kctl_mute;
|
||||
struct snd_ctl_elem_id snd_kctl_volume_id;
|
||||
struct snd_ctl_elem_id snd_kctl_mute_id;
|
||||
s16 volume_min, volume_max, volume_res;
|
||||
s16 volume;
|
||||
int mute;
|
||||
|
||||
struct snd_kcontrol *snd_kctl_rate; /* read-only current rate */
|
||||
struct snd_ctl_elem_id snd_kctl_rate_id; /* read-only current rate */
|
||||
int srate; /* selected samplerate */
|
||||
int active; /* playback/capture running */
|
||||
|
||||
@ -494,14 +494,13 @@ static inline void free_ep_fback(struct uac_rtd_params *prm, struct usb_ep *ep)
|
||||
static void set_active(struct uac_rtd_params *prm, bool active)
|
||||
{
|
||||
// notifying through the Rate ctrl
|
||||
struct snd_kcontrol *kctl = prm->snd_kctl_rate;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&prm->lock, flags);
|
||||
if (prm->active != active) {
|
||||
prm->active = active;
|
||||
snd_ctl_notify(prm->uac->card, SNDRV_CTL_EVENT_MASK_VALUE,
|
||||
&kctl->id);
|
||||
&prm->snd_kctl_rate_id);
|
||||
}
|
||||
spin_unlock_irqrestore(&prm->lock, flags);
|
||||
}
|
||||
@ -807,7 +806,7 @@ int u_audio_set_volume(struct g_audio *audio_dev, int playback, s16 val)
|
||||
|
||||
if (change)
|
||||
snd_ctl_notify(uac->card, SNDRV_CTL_EVENT_MASK_VALUE,
|
||||
&prm->snd_kctl_volume->id);
|
||||
&prm->snd_kctl_volume_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -856,7 +855,7 @@ int u_audio_set_mute(struct g_audio *audio_dev, int playback, int val)
|
||||
|
||||
if (change)
|
||||
snd_ctl_notify(uac->card, SNDRV_CTL_EVENT_MASK_VALUE,
|
||||
&prm->snd_kctl_mute->id);
|
||||
&prm->snd_kctl_mute_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1243,7 +1242,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
|
||||
if (err < 0)
|
||||
goto snd_fail;
|
||||
|
||||
strscpy(pcm->name, pcm_name, sizeof(pcm->name));
|
||||
strscpy(pcm->name, pcm_name);
|
||||
pcm->private_data = uac;
|
||||
uac->pcm = pcm;
|
||||
|
||||
@ -1257,7 +1256,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
|
||||
if ((c_chmask && g_audio->in_ep_fback)
|
||||
|| (p_chmask && params->p_fu.id)
|
||||
|| (c_chmask && params->c_fu.id))
|
||||
strscpy(card->mixername, card_name, sizeof(card->driver));
|
||||
strscpy(card->mixername, card_name);
|
||||
|
||||
if (c_chmask && g_audio->in_ep_fback) {
|
||||
kctl = snd_ctl_new1(&u_audio_controls[UAC_FBACK_CTRL],
|
||||
@ -1331,7 +1330,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
|
||||
err = snd_ctl_add(card, kctl);
|
||||
if (err < 0)
|
||||
goto snd_fail;
|
||||
prm->snd_kctl_mute = kctl;
|
||||
prm->snd_kctl_mute_id = kctl->id;
|
||||
prm->mute = 0;
|
||||
}
|
||||
|
||||
@ -1359,7 +1358,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
|
||||
err = snd_ctl_add(card, kctl);
|
||||
if (err < 0)
|
||||
goto snd_fail;
|
||||
prm->snd_kctl_volume = kctl;
|
||||
prm->snd_kctl_volume_id = kctl->id;
|
||||
prm->volume = fu->volume_max;
|
||||
prm->volume_max = fu->volume_max;
|
||||
prm->volume_min = fu->volume_min;
|
||||
@ -1383,12 +1382,13 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
|
||||
err = snd_ctl_add(card, kctl);
|
||||
if (err < 0)
|
||||
goto snd_fail;
|
||||
prm->snd_kctl_rate = kctl;
|
||||
prm->snd_kctl_rate_id = kctl->id;
|
||||
}
|
||||
|
||||
strscpy(card->driver, card_name, sizeof(card->driver));
|
||||
strscpy(card->shortname, card_name, sizeof(card->shortname));
|
||||
sprintf(card->longname, "%s %i", card_name, card->dev->id);
|
||||
strscpy(card->driver, card_name);
|
||||
strscpy(card->shortname, card_name);
|
||||
snprintf(card->longname, sizeof(card->longname), "%s %i",
|
||||
card_name, card->dev->id);
|
||||
|
||||
snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
|
||||
NULL, 0, BUFF_SIZE_MAX);
|
||||
@ -1420,6 +1420,8 @@ void g_audio_cleanup(struct g_audio *g_audio)
|
||||
return;
|
||||
|
||||
uac = g_audio->uac;
|
||||
g_audio->uac = NULL;
|
||||
|
||||
card = uac->card;
|
||||
if (card)
|
||||
snd_card_free_when_closed(card);
|
||||
|
@ -1032,7 +1032,7 @@ int gether_set_ifname(struct net_device *net, const char *name, int len)
|
||||
if (!p || p[1] != 'd' || strchr(p + 2, '%'))
|
||||
return -EINVAL;
|
||||
|
||||
strncpy(net->name, tmp, sizeof(net->name));
|
||||
strscpy(net->name, tmp);
|
||||
dev->ifname_set = true;
|
||||
|
||||
return 0;
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "uvc_configfs.h"
|
||||
|
||||
#include <linux/sort.h>
|
||||
#include <linux/usb/uvc.h>
|
||||
#include <linux/usb/video.h>
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
@ -2260,6 +2261,8 @@ static ssize_t uvcg_uncompressed_guid_format_store(struct config_item *item,
|
||||
struct f_uvc_opts *opts;
|
||||
struct config_item *opts_item;
|
||||
struct mutex *su_mutex = &ch->fmt.group.cg_subsys->su_mutex;
|
||||
const struct uvc_format_desc *format;
|
||||
u8 tmpguidFormat[sizeof(ch->desc.guidFormat)];
|
||||
int ret;
|
||||
|
||||
mutex_lock(su_mutex); /* for navigating configfs hierarchy */
|
||||
@ -2273,7 +2276,16 @@ static ssize_t uvcg_uncompressed_guid_format_store(struct config_item *item,
|
||||
goto end;
|
||||
}
|
||||
|
||||
memcpy(ch->desc.guidFormat, page,
|
||||
memcpy(tmpguidFormat, page,
|
||||
min(sizeof(tmpguidFormat), len));
|
||||
|
||||
format = uvc_format_by_guid(tmpguidFormat);
|
||||
if (!format) {
|
||||
ret = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
memcpy(ch->desc.guidFormat, tmpguidFormat,
|
||||
min(sizeof(ch->desc.guidFormat), len));
|
||||
ret = sizeof(ch->desc.guidFormat);
|
||||
|
||||
|
@ -260,12 +260,26 @@ uvc_v4l2_try_format(struct file *file, void *fh, struct v4l2_format *fmt)
|
||||
if (!uframe)
|
||||
return -EINVAL;
|
||||
|
||||
fmt->fmt.pix.width = uframe->frame.w_width;
|
||||
fmt->fmt.pix.height = uframe->frame.w_height;
|
||||
if (uformat->type == UVCG_UNCOMPRESSED) {
|
||||
struct uvcg_uncompressed *u =
|
||||
to_uvcg_uncompressed(&uformat->group.cg_item);
|
||||
if (!u)
|
||||
return 0;
|
||||
|
||||
v4l2_fill_pixfmt(&fmt->fmt.pix, fmt->fmt.pix.pixelformat,
|
||||
uframe->frame.w_width, uframe->frame.w_height);
|
||||
|
||||
if (fmt->fmt.pix.sizeimage != (uvc_v4l2_get_bytesperline(uformat, uframe) *
|
||||
uframe->frame.w_height))
|
||||
return -EINVAL;
|
||||
} else {
|
||||
fmt->fmt.pix.width = uframe->frame.w_width;
|
||||
fmt->fmt.pix.height = uframe->frame.w_height;
|
||||
fmt->fmt.pix.bytesperline = uvc_v4l2_get_bytesperline(uformat, uframe);
|
||||
fmt->fmt.pix.sizeimage = uvc_get_frame_size(uformat, uframe);
|
||||
fmt->fmt.pix.pixelformat = to_uvc_format(uformat)->fcc;
|
||||
}
|
||||
fmt->fmt.pix.field = V4L2_FIELD_NONE;
|
||||
fmt->fmt.pix.bytesperline = uvc_v4l2_get_bytesperline(uformat, uframe);
|
||||
fmt->fmt.pix.sizeimage = uvc_get_frame_size(uformat, uframe);
|
||||
fmt->fmt.pix.pixelformat = to_uvc_format(uformat)->fcc;
|
||||
fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
|
||||
fmt->fmt.pix.priv = 0;
|
||||
|
||||
|
@ -1426,8 +1426,16 @@ int usb_add_gadget(struct usb_gadget *gadget)
|
||||
if (ret)
|
||||
goto err_free_id;
|
||||
|
||||
ret = sysfs_create_link(&udc->dev.kobj,
|
||||
&gadget->dev.kobj, "gadget");
|
||||
if (ret)
|
||||
goto err_del_gadget;
|
||||
|
||||
return 0;
|
||||
|
||||
err_del_gadget:
|
||||
device_del(&gadget->dev);
|
||||
|
||||
err_free_id:
|
||||
ida_free(&gadget_id_numbers, gadget->id_number);
|
||||
|
||||
@ -1536,6 +1544,7 @@ void usb_del_gadget(struct usb_gadget *gadget)
|
||||
mutex_unlock(&udc_lock);
|
||||
|
||||
kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE);
|
||||
sysfs_remove_link(&udc->dev.kobj, "gadget");
|
||||
flush_work(&gadget->work);
|
||||
device_del(&gadget->dev);
|
||||
ida_free(&gadget_id_numbers, gadget->id_number);
|
||||
|
@ -30,7 +30,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
@ -50,6 +50,8 @@
|
||||
#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";
|
||||
|
||||
@ -240,7 +242,7 @@ enum dummy_rh_state {
|
||||
struct dummy_hcd {
|
||||
struct dummy *dum;
|
||||
enum dummy_rh_state rh_state;
|
||||
struct timer_list timer;
|
||||
struct hrtimer timer;
|
||||
u32 port_status;
|
||||
u32 old_status;
|
||||
unsigned long re_timeout;
|
||||
@ -1301,8 +1303,8 @@ static int dummy_urb_enqueue(
|
||||
urb->error_count = 1; /* mark as a new urb */
|
||||
|
||||
/* kick the scheduler, it'll do the rest */
|
||||
if (!timer_pending(&dum_hcd->timer))
|
||||
mod_timer(&dum_hcd->timer, jiffies + 1);
|
||||
if (!hrtimer_active(&dum_hcd->timer))
|
||||
hrtimer_start(&dum_hcd->timer, ns_to_ktime(DUMMY_TIMER_INT_NSECS), HRTIMER_MODE_REL);
|
||||
|
||||
done:
|
||||
spin_unlock_irqrestore(&dum_hcd->dum->lock, flags);
|
||||
@ -1323,7 +1325,7 @@ static int dummy_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
|
||||
rc = usb_hcd_check_unlink_urb(hcd, urb, status);
|
||||
if (!rc && dum_hcd->rh_state != DUMMY_RH_RUNNING &&
|
||||
!list_empty(&dum_hcd->urbp_list))
|
||||
mod_timer(&dum_hcd->timer, jiffies);
|
||||
hrtimer_start(&dum_hcd->timer, ns_to_ktime(0), HRTIMER_MODE_REL);
|
||||
|
||||
spin_unlock_irqrestore(&dum_hcd->dum->lock, flags);
|
||||
return rc;
|
||||
@ -1777,7 +1779,7 @@ static int handle_control_request(struct dummy_hcd *dum_hcd, struct urb *urb,
|
||||
* drivers except that the callbacks are invoked from soft interrupt
|
||||
* context.
|
||||
*/
|
||||
static void dummy_timer(struct timer_list *t)
|
||||
static enum hrtimer_restart dummy_timer(struct hrtimer *t)
|
||||
{
|
||||
struct dummy_hcd *dum_hcd = from_timer(dum_hcd, t, timer);
|
||||
struct dummy *dum = dum_hcd->dum;
|
||||
@ -1808,8 +1810,6 @@ static void dummy_timer(struct timer_list *t)
|
||||
break;
|
||||
}
|
||||
|
||||
/* FIXME if HZ != 1000 this will probably misbehave ... */
|
||||
|
||||
/* look at each urb queued by the host side driver */
|
||||
spin_lock_irqsave(&dum->lock, flags);
|
||||
|
||||
@ -1817,7 +1817,7 @@ static void dummy_timer(struct timer_list *t)
|
||||
dev_err(dummy_dev(dum_hcd),
|
||||
"timer fired with no URBs pending?\n");
|
||||
spin_unlock_irqrestore(&dum->lock, flags);
|
||||
return;
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
dum_hcd->next_frame_urbp = NULL;
|
||||
|
||||
@ -1995,10 +1995,12 @@ return_urb:
|
||||
dum_hcd->udev = NULL;
|
||||
} else if (dum_hcd->rh_state == DUMMY_RH_RUNNING) {
|
||||
/* want a 1 msec delay here */
|
||||
mod_timer(&dum_hcd->timer, jiffies + msecs_to_jiffies(1));
|
||||
hrtimer_start(&dum_hcd->timer, ns_to_ktime(DUMMY_TIMER_INT_NSECS), HRTIMER_MODE_REL);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&dum->lock, flags);
|
||||
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
@ -2387,7 +2389,7 @@ static int dummy_bus_resume(struct usb_hcd *hcd)
|
||||
dum_hcd->rh_state = DUMMY_RH_RUNNING;
|
||||
set_link_state(dum_hcd);
|
||||
if (!list_empty(&dum_hcd->urbp_list))
|
||||
mod_timer(&dum_hcd->timer, jiffies);
|
||||
hrtimer_start(&dum_hcd->timer, ns_to_ktime(0), HRTIMER_MODE_REL);
|
||||
hcd->state = HC_STATE_RUNNING;
|
||||
}
|
||||
spin_unlock_irq(&dum_hcd->dum->lock);
|
||||
@ -2465,7 +2467,8 @@ static DEVICE_ATTR_RO(urbs);
|
||||
|
||||
static int dummy_start_ss(struct dummy_hcd *dum_hcd)
|
||||
{
|
||||
timer_setup(&dum_hcd->timer, dummy_timer, 0);
|
||||
hrtimer_init(&dum_hcd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
dum_hcd->timer.function = dummy_timer;
|
||||
dum_hcd->rh_state = DUMMY_RH_RUNNING;
|
||||
dum_hcd->stream_en_ep = 0;
|
||||
INIT_LIST_HEAD(&dum_hcd->urbp_list);
|
||||
@ -2494,7 +2497,8 @@ static int dummy_start(struct usb_hcd *hcd)
|
||||
return dummy_start_ss(dum_hcd);
|
||||
|
||||
spin_lock_init(&dum_hcd->dum->lock);
|
||||
timer_setup(&dum_hcd->timer, dummy_timer, 0);
|
||||
hrtimer_init(&dum_hcd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
dum_hcd->timer.function = dummy_timer;
|
||||
dum_hcd->rh_state = DUMMY_RH_RUNNING;
|
||||
|
||||
INIT_LIST_HEAD(&dum_hcd->urbp_list);
|
||||
@ -2513,8 +2517,11 @@ static int dummy_start(struct usb_hcd *hcd)
|
||||
|
||||
static void dummy_stop(struct usb_hcd *hcd)
|
||||
{
|
||||
device_remove_file(dummy_dev(hcd_to_dummy_hcd(hcd)), &dev_attr_urbs);
|
||||
dev_info(dummy_dev(hcd_to_dummy_hcd(hcd)), "stopped\n");
|
||||
struct dummy_hcd *dum_hcd = hcd_to_dummy_hcd(hcd);
|
||||
|
||||
hrtimer_cancel(&dum_hcd->timer);
|
||||
device_remove_file(dummy_dev(dum_hcd), &dev_attr_urbs);
|
||||
dev_info(dummy_dev(dum_hcd), "stopped\n");
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
@ -1307,7 +1307,7 @@ static int mv_u3d_eps_init(struct mv_u3d *u3d)
|
||||
/* initialize ep0, ep0 in/out use eps[1] */
|
||||
ep = &u3d->eps[1];
|
||||
ep->u3d = u3d;
|
||||
strncpy(ep->name, "ep0", sizeof(ep->name));
|
||||
strscpy(ep->name, "ep0");
|
||||
ep->ep.name = ep->name;
|
||||
ep->ep.ops = &mv_u3d_ep_ops;
|
||||
ep->wedge = 0;
|
||||
@ -1337,7 +1337,7 @@ static int mv_u3d_eps_init(struct mv_u3d *u3d)
|
||||
ep->ep.caps.dir_out = true;
|
||||
}
|
||||
ep->u3d = u3d;
|
||||
strncpy(ep->name, name, sizeof(ep->name));
|
||||
strscpy(ep->name, name);
|
||||
ep->ep.name = ep->name;
|
||||
|
||||
ep->ep.caps.type_iso = true;
|
||||
|
@ -56,7 +56,6 @@
|
||||
/* ISO too */
|
||||
#define USE_ISO
|
||||
|
||||
#define DRIVER_DESC "OMAP UDC driver"
|
||||
#define DRIVER_VERSION "4 October 2004"
|
||||
|
||||
#define OMAP_DMA_USB_W2FC_TX0 29
|
||||
@ -110,7 +109,6 @@ MODULE_PARM_DESC(use_dma, "enable/disable DMA");
|
||||
|
||||
|
||||
static const char driver_name[] = "omap_udc";
|
||||
static const char driver_desc[] = DRIVER_DESC;
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
@ -2299,13 +2297,11 @@ static int proc_udc_show(struct seq_file *s, void *_)
|
||||
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
|
||||
seq_printf(s, "%s, version: " DRIVER_VERSION
|
||||
seq_printf(s, "OMAP UDC driver, version: " DRIVER_VERSION
|
||||
#ifdef USE_ISO
|
||||
" (iso)"
|
||||
#endif
|
||||
"%s\n",
|
||||
driver_desc,
|
||||
use_dma ? " (dma)" : "");
|
||||
"%s\n", use_dma ? " (dma)" : "");
|
||||
|
||||
tmp = omap_readw(UDC_REV) & 0xff;
|
||||
seq_printf(s,
|
||||
@ -2994,6 +2990,6 @@ static struct platform_driver udc_driver = {
|
||||
|
||||
module_platform_driver(udc_driver);
|
||||
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_DESCRIPTION("OMAP UDC driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:omap_udc");
|
||||
|
@ -430,13 +430,13 @@ static void qh_lines(struct ehci_hcd *ehci, struct ehci_qh *qh,
|
||||
mark = '/';
|
||||
}
|
||||
switch ((scratch >> 8) & 0x03) {
|
||||
case 0:
|
||||
case PID_CODE_OUT:
|
||||
type = "out";
|
||||
break;
|
||||
case 1:
|
||||
case PID_CODE_IN:
|
||||
type = "in";
|
||||
break;
|
||||
case 2:
|
||||
case PID_CODE_SETUP:
|
||||
type = "setup";
|
||||
break;
|
||||
default:
|
||||
@ -602,10 +602,10 @@ static unsigned output_buf_tds_dir(char *buf, struct ehci_hcd *ehci,
|
||||
list_for_each_entry(qtd, &qh->qtd_list, qtd_list) {
|
||||
temp++;
|
||||
switch ((hc32_to_cpu(ehci, qtd->hw_token) >> 8) & 0x03) {
|
||||
case 0:
|
||||
case PID_CODE_OUT:
|
||||
type = "out";
|
||||
continue;
|
||||
case 1:
|
||||
case PID_CODE_IN:
|
||||
type = "in";
|
||||
continue;
|
||||
}
|
||||
|
@ -159,20 +159,16 @@ static int exynos_ehci_probe(struct platform_device *pdev)
|
||||
|
||||
err = exynos_ehci_get_phy(&pdev->dev, exynos_ehci);
|
||||
if (err)
|
||||
goto fail_clk;
|
||||
goto fail_io;
|
||||
|
||||
exynos_ehci->clk = devm_clk_get(&pdev->dev, "usbhost");
|
||||
exynos_ehci->clk = devm_clk_get_enabled(&pdev->dev, "usbhost");
|
||||
|
||||
if (IS_ERR(exynos_ehci->clk)) {
|
||||
dev_err(&pdev->dev, "Failed to get usbhost clock\n");
|
||||
err = PTR_ERR(exynos_ehci->clk);
|
||||
goto fail_clk;
|
||||
goto fail_io;
|
||||
}
|
||||
|
||||
err = clk_prepare_enable(exynos_ehci->clk);
|
||||
if (err)
|
||||
goto fail_clk;
|
||||
|
||||
hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
|
||||
if (IS_ERR(hcd->regs)) {
|
||||
err = PTR_ERR(hcd->regs);
|
||||
@ -223,8 +219,6 @@ fail_add_hcd:
|
||||
exynos_ehci_phy_disable(&pdev->dev);
|
||||
pdev->dev.of_node = exynos_ehci->of_node;
|
||||
fail_io:
|
||||
clk_disable_unprepare(exynos_ehci->clk);
|
||||
fail_clk:
|
||||
usb_put_hcd(hcd);
|
||||
return err;
|
||||
}
|
||||
@ -240,12 +234,9 @@ static void exynos_ehci_remove(struct platform_device *pdev)
|
||||
|
||||
exynos_ehci_phy_disable(&pdev->dev);
|
||||
|
||||
clk_disable_unprepare(exynos_ehci->clk);
|
||||
|
||||
usb_put_hcd(hcd);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int exynos_ehci_suspend(struct device *dev)
|
||||
{
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
@ -288,15 +279,9 @@ static int exynos_ehci_resume(struct device *dev)
|
||||
ehci_resume(hcd, false);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define exynos_ehci_suspend NULL
|
||||
#define exynos_ehci_resume NULL
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops exynos_ehci_pm_ops = {
|
||||
.suspend = exynos_ehci_suspend,
|
||||
.resume = exynos_ehci_resume,
|
||||
};
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(exynos_ehci_pm_ops,
|
||||
exynos_ehci_suspend, exynos_ehci_resume);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id exynos_ehci_match[] = {
|
||||
@ -312,7 +297,7 @@ static struct platform_driver exynos_ehci_driver = {
|
||||
.shutdown = usb_hcd_platform_shutdown,
|
||||
.driver = {
|
||||
.name = "exynos-ehci",
|
||||
.pm = &exynos_ehci_pm_ops,
|
||||
.pm = pm_ptr(&exynos_ehci_pm_ops),
|
||||
.of_match_table = of_match_ptr(exynos_ehci_match),
|
||||
}
|
||||
};
|
||||
|
@ -27,10 +27,6 @@
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* PID Codes that are used here, from EHCI specification, Table 3-16. */
|
||||
#define PID_CODE_IN 1
|
||||
#define PID_CODE_SETUP 2
|
||||
|
||||
/* fill a qtd, returning how much of the buffer we were able to queue up */
|
||||
|
||||
static unsigned int
|
||||
@ -230,7 +226,7 @@ static int qtd_copy_status (
|
||||
/* fs/ls interrupt xfer missed the complete-split */
|
||||
status = -EPROTO;
|
||||
} else if (token & QTD_STS_DBE) {
|
||||
status = (QTD_PID (token) == 1) /* IN ? */
|
||||
status = (QTD_PID(token) == PID_CODE_IN) /* IN ? */
|
||||
? -ENOSR /* hc couldn't read data */
|
||||
: -ECOMM; /* hc couldn't write data */
|
||||
} else if (token & QTD_STS_XACT) {
|
||||
@ -606,7 +602,7 @@ qh_urb_transaction (
|
||||
/* SETUP pid */
|
||||
qtd_fill(ehci, qtd, urb->setup_dma,
|
||||
sizeof (struct usb_ctrlrequest),
|
||||
token | (2 /* "setup" */ << 8), 8);
|
||||
token | (PID_CODE_SETUP << 8), 8);
|
||||
|
||||
/* ... and always at least one more pid */
|
||||
token ^= QTD_TOGGLE;
|
||||
@ -620,7 +616,7 @@ qh_urb_transaction (
|
||||
|
||||
/* for zero length DATA stages, STATUS is always IN */
|
||||
if (len == 0)
|
||||
token |= (1 /* "in" */ << 8);
|
||||
token |= (PID_CODE_IN << 8);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -642,7 +638,7 @@ qh_urb_transaction (
|
||||
}
|
||||
|
||||
if (is_input)
|
||||
token |= (1 /* "in" */ << 8);
|
||||
token |= (PID_CODE_IN << 8);
|
||||
/* else it's already initted to "out" pid (0 << 8) */
|
||||
|
||||
maxpacket = usb_endpoint_maxp(&urb->ep->desc);
|
||||
@ -709,7 +705,7 @@ qh_urb_transaction (
|
||||
|
||||
if (usb_pipecontrol (urb->pipe)) {
|
||||
one_more = 1;
|
||||
token ^= 0x0100; /* "in" <--> "out" */
|
||||
token ^= (PID_CODE_IN << 8); /* "in" <--> "out" */
|
||||
token |= QTD_TOGGLE; /* force DATA1 */
|
||||
} else if (usb_pipeout(urb->pipe)
|
||||
&& (urb->transfer_flags & URB_ZERO_PACKET)
|
||||
@ -1203,7 +1199,7 @@ static int ehci_submit_single_step_set_feature(
|
||||
/* SETUP pid, and interrupt after SETUP completion */
|
||||
qtd_fill(ehci, qtd, urb->setup_dma,
|
||||
sizeof(struct usb_ctrlrequest),
|
||||
QTD_IOC | token | (2 /* "setup" */ << 8), 8);
|
||||
QTD_IOC | token | (PID_CODE_SETUP << 8), 8);
|
||||
|
||||
submit_async(ehci, urb, &qtd_list, GFP_ATOMIC);
|
||||
return 0; /*Return now; we shall come back after 15 seconds*/
|
||||
@ -1216,7 +1212,7 @@ static int ehci_submit_single_step_set_feature(
|
||||
token ^= QTD_TOGGLE; /*We need to start IN with DATA-1 Pid-sequence*/
|
||||
buf = urb->transfer_dma;
|
||||
|
||||
token |= (1 /* "in" */ << 8); /*This is IN stage*/
|
||||
token |= (PID_CODE_IN << 8); /*This is IN stage*/
|
||||
|
||||
maxpacket = usb_endpoint_maxp(&urb->ep->desc);
|
||||
|
||||
@ -1229,7 +1225,7 @@ static int ehci_submit_single_step_set_feature(
|
||||
qtd->hw_alt_next = EHCI_LIST_END(ehci);
|
||||
|
||||
/* STATUS stage for GetDesc control request */
|
||||
token ^= 0x0100; /* "in" <--> "out" */
|
||||
token ^= (PID_CODE_IN << 8); /* "in" <--> "out" */
|
||||
token |= QTD_TOGGLE; /* force DATA1 */
|
||||
|
||||
qtd_prev = qtd;
|
||||
|
@ -321,10 +321,16 @@ struct ehci_qtd {
|
||||
size_t length; /* length of buffer */
|
||||
} __aligned(32);
|
||||
|
||||
/* PID Codes that are used here, from EHCI specification, Table 3-16. */
|
||||
#define PID_CODE_OUT 0
|
||||
#define PID_CODE_IN 1
|
||||
#define PID_CODE_SETUP 2
|
||||
|
||||
/* mask NakCnt+T in qh->hw_alt_next */
|
||||
#define QTD_MASK(ehci) cpu_to_hc32(ehci, ~0x1f)
|
||||
|
||||
#define IS_SHORT_READ(token) (QTD_LENGTH(token) != 0 && QTD_PID(token) == 1)
|
||||
#define IS_SHORT_READ(token) (QTD_LENGTH(token) != 0 && \
|
||||
QTD_PID(token) == PID_CODE_IN)
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
|
@ -135,20 +135,16 @@ static int exynos_ohci_probe(struct platform_device *pdev)
|
||||
|
||||
err = exynos_ohci_get_phy(&pdev->dev, exynos_ohci);
|
||||
if (err)
|
||||
goto fail_clk;
|
||||
goto fail_io;
|
||||
|
||||
exynos_ohci->clk = devm_clk_get(&pdev->dev, "usbhost");
|
||||
exynos_ohci->clk = devm_clk_get_enabled(&pdev->dev, "usbhost");
|
||||
|
||||
if (IS_ERR(exynos_ohci->clk)) {
|
||||
dev_err(&pdev->dev, "Failed to get usbhost clock\n");
|
||||
err = PTR_ERR(exynos_ohci->clk);
|
||||
goto fail_clk;
|
||||
goto fail_io;
|
||||
}
|
||||
|
||||
err = clk_prepare_enable(exynos_ohci->clk);
|
||||
if (err)
|
||||
goto fail_clk;
|
||||
|
||||
hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
|
||||
if (IS_ERR(hcd->regs)) {
|
||||
err = PTR_ERR(hcd->regs);
|
||||
@ -191,8 +187,6 @@ fail_add_hcd:
|
||||
exynos_ohci_phy_disable(&pdev->dev);
|
||||
pdev->dev.of_node = exynos_ohci->of_node;
|
||||
fail_io:
|
||||
clk_disable_unprepare(exynos_ohci->clk);
|
||||
fail_clk:
|
||||
usb_put_hcd(hcd);
|
||||
return err;
|
||||
}
|
||||
@ -208,8 +202,6 @@ static void exynos_ohci_remove(struct platform_device *pdev)
|
||||
|
||||
exynos_ohci_phy_disable(&pdev->dev);
|
||||
|
||||
clk_disable_unprepare(exynos_ohci->clk);
|
||||
|
||||
usb_put_hcd(hcd);
|
||||
}
|
||||
|
||||
@ -221,7 +213,6 @@ static void exynos_ohci_shutdown(struct platform_device *pdev)
|
||||
hcd->driver->shutdown(hcd);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int exynos_ohci_suspend(struct device *dev)
|
||||
{
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
@ -258,19 +249,13 @@ static int exynos_ohci_resume(struct device *dev)
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define exynos_ohci_suspend NULL
|
||||
#define exynos_ohci_resume NULL
|
||||
#endif
|
||||
|
||||
static const struct ohci_driver_overrides exynos_overrides __initconst = {
|
||||
.extra_priv_size = sizeof(struct exynos_ohci_hcd),
|
||||
};
|
||||
|
||||
static const struct dev_pm_ops exynos_ohci_pm_ops = {
|
||||
.suspend = exynos_ohci_suspend,
|
||||
.resume = exynos_ohci_resume,
|
||||
};
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(exynos_ohci_pm_ops,
|
||||
exynos_ohci_suspend, exynos_ohci_resume);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id exynos_ohci_match[] = {
|
||||
@ -286,7 +271,7 @@ static struct platform_driver exynos_ohci_driver = {
|
||||
.shutdown = exynos_ohci_shutdown,
|
||||
.driver = {
|
||||
.name = "exynos-ohci",
|
||||
.pm = &exynos_ohci_pm_ops,
|
||||
.pm = pm_ptr(&exynos_ohci_pm_ops),
|
||||
.of_match_table = of_match_ptr(exynos_ohci_match),
|
||||
}
|
||||
};
|
||||
|
@ -516,7 +516,7 @@ static int xhci_dbc_mem_init(struct xhci_dbc *dbc, gfp_t flags)
|
||||
goto string_fail;
|
||||
|
||||
/* Setup ERST register: */
|
||||
writel(dbc->erst.erst_size, &dbc->regs->ersts);
|
||||
writel(dbc->erst.num_entries, &dbc->regs->ersts);
|
||||
|
||||
lo_hi_writeq(dbc->erst.erst_dma_addr, &dbc->regs->erstba);
|
||||
deq = xhci_trb_virt_to_dma(dbc->ring_evt->deq_seg,
|
||||
|
@ -536,7 +536,7 @@ static void xhci_free_stream_ctx(struct xhci_hcd *xhci,
|
||||
struct xhci_stream_ctx *stream_ctx, dma_addr_t dma)
|
||||
{
|
||||
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
|
||||
size_t size = sizeof(struct xhci_stream_ctx) * num_stream_ctxs;
|
||||
size_t size = array_size(sizeof(struct xhci_stream_ctx), num_stream_ctxs);
|
||||
|
||||
if (size > MEDIUM_STREAM_ARRAY_SIZE)
|
||||
dma_free_coherent(dev, size, stream_ctx, dma);
|
||||
@ -561,7 +561,7 @@ static struct xhci_stream_ctx *xhci_alloc_stream_ctx(struct xhci_hcd *xhci,
|
||||
gfp_t mem_flags)
|
||||
{
|
||||
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
|
||||
size_t size = size_mul(sizeof(struct xhci_stream_ctx), num_stream_ctxs);
|
||||
size_t size = array_size(sizeof(struct xhci_stream_ctx), num_stream_ctxs);
|
||||
|
||||
if (size > MEDIUM_STREAM_ARRAY_SIZE)
|
||||
return dma_alloc_coherent(dev, size, dma, mem_flags);
|
||||
@ -1638,7 +1638,7 @@ static int scratchpad_alloc(struct xhci_hcd *xhci, gfp_t flags)
|
||||
goto fail_sp;
|
||||
|
||||
xhci->scratchpad->sp_array = dma_alloc_coherent(dev,
|
||||
size_mul(sizeof(u64), num_sp),
|
||||
array_size(sizeof(u64), num_sp),
|
||||
&xhci->scratchpad->sp_dma, flags);
|
||||
if (!xhci->scratchpad->sp_array)
|
||||
goto fail_sp2;
|
||||
@ -1671,7 +1671,7 @@ static int scratchpad_alloc(struct xhci_hcd *xhci, gfp_t flags)
|
||||
kfree(xhci->scratchpad->sp_buffers);
|
||||
|
||||
fail_sp3:
|
||||
dma_free_coherent(dev, num_sp * sizeof(u64),
|
||||
dma_free_coherent(dev, array_size(sizeof(u64), num_sp),
|
||||
xhci->scratchpad->sp_array,
|
||||
xhci->scratchpad->sp_dma);
|
||||
|
||||
@ -1700,7 +1700,7 @@ static void scratchpad_free(struct xhci_hcd *xhci)
|
||||
xhci->scratchpad->sp_array[i]);
|
||||
}
|
||||
kfree(xhci->scratchpad->sp_buffers);
|
||||
dma_free_coherent(dev, num_sp * sizeof(u64),
|
||||
dma_free_coherent(dev, array_size(sizeof(u64), num_sp),
|
||||
xhci->scratchpad->sp_array,
|
||||
xhci->scratchpad->sp_dma);
|
||||
kfree(xhci->scratchpad);
|
||||
@ -1778,7 +1778,7 @@ static int xhci_alloc_erst(struct xhci_hcd *xhci,
|
||||
struct xhci_segment *seg;
|
||||
struct xhci_erst_entry *entry;
|
||||
|
||||
size = size_mul(sizeof(struct xhci_erst_entry), evt_ring->num_segs);
|
||||
size = array_size(sizeof(struct xhci_erst_entry), evt_ring->num_segs);
|
||||
erst->entries = dma_alloc_coherent(xhci_to_hcd(xhci)->self.sysdev,
|
||||
size, &erst->erst_dma_addr, flags);
|
||||
if (!erst->entries)
|
||||
@ -1829,7 +1829,7 @@ xhci_free_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir)
|
||||
if (!ir)
|
||||
return;
|
||||
|
||||
erst_size = sizeof(struct xhci_erst_entry) * ir->erst.num_entries;
|
||||
erst_size = array_size(sizeof(struct xhci_erst_entry), ir->erst.num_entries);
|
||||
if (ir->erst.entries)
|
||||
dma_free_coherent(dev, erst_size,
|
||||
ir->erst.entries,
|
||||
@ -1950,7 +1950,6 @@ no_bw:
|
||||
kfree(xhci->usb3_rhub.ports);
|
||||
kfree(xhci->hw_ports);
|
||||
kfree(xhci->rh_bw);
|
||||
kfree(xhci->ext_caps);
|
||||
for (i = 0; i < xhci->num_port_caps; i++)
|
||||
kfree(xhci->port_caps[i].psi);
|
||||
kfree(xhci->port_caps);
|
||||
@ -1961,7 +1960,6 @@ no_bw:
|
||||
xhci->usb3_rhub.ports = NULL;
|
||||
xhci->hw_ports = NULL;
|
||||
xhci->rh_bw = NULL;
|
||||
xhci->ext_caps = NULL;
|
||||
xhci->port_caps = NULL;
|
||||
xhci->interrupters = NULL;
|
||||
|
||||
@ -2089,10 +2087,7 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
|
||||
|
||||
port_cap->maj_rev = major_revision;
|
||||
port_cap->min_rev = minor_revision;
|
||||
|
||||
/* cache usb2 port capabilities */
|
||||
if (major_revision < 0x03 && xhci->num_ext_caps < max_caps)
|
||||
xhci->ext_caps[xhci->num_ext_caps++] = temp;
|
||||
port_cap->protocol_caps = temp;
|
||||
|
||||
if ((xhci->hci_version >= 0x100) && (major_revision != 0x03) &&
|
||||
(temp & XHCI_HLC)) {
|
||||
@ -2212,11 +2207,6 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
|
||||
XHCI_EXT_CAPS_PROTOCOL);
|
||||
}
|
||||
|
||||
xhci->ext_caps = kcalloc_node(cap_count, sizeof(*xhci->ext_caps),
|
||||
flags, dev_to_node(dev));
|
||||
if (!xhci->ext_caps)
|
||||
return -ENOMEM;
|
||||
|
||||
xhci->port_caps = kcalloc_node(cap_count, sizeof(*xhci->port_caps),
|
||||
flags, dev_to_node(dev));
|
||||
if (!xhci->port_caps)
|
||||
@ -2269,24 +2259,24 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
|
||||
}
|
||||
|
||||
static struct xhci_interrupter *
|
||||
xhci_alloc_interrupter(struct xhci_hcd *xhci, int segs, gfp_t flags)
|
||||
xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int segs, gfp_t flags)
|
||||
{
|
||||
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
|
||||
struct xhci_interrupter *ir;
|
||||
unsigned int num_segs = segs;
|
||||
unsigned int max_segs;
|
||||
int ret;
|
||||
|
||||
if (!segs)
|
||||
segs = ERST_DEFAULT_SEGS;
|
||||
|
||||
max_segs = BIT(HCS_ERST_MAX(xhci->hcs_params2));
|
||||
segs = min(segs, max_segs);
|
||||
|
||||
ir = kzalloc_node(sizeof(*ir), flags, dev_to_node(dev));
|
||||
if (!ir)
|
||||
return NULL;
|
||||
|
||||
/* number of ring segments should be greater than 0 */
|
||||
if (segs <= 0)
|
||||
num_segs = min_t(unsigned int, 1 << HCS_ERST_MAX(xhci->hcs_params2),
|
||||
ERST_MAX_SEGS);
|
||||
|
||||
ir->event_ring = xhci_ring_alloc(xhci, num_segs, 1, TYPE_EVENT, 0,
|
||||
flags);
|
||||
ir->event_ring = xhci_ring_alloc(xhci, segs, 1, TYPE_EVENT, 0, flags);
|
||||
if (!ir->event_ring) {
|
||||
xhci_warn(xhci, "Failed to allocate interrupter event ring\n");
|
||||
kfree(ir);
|
||||
@ -2344,7 +2334,7 @@ xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir,
|
||||
}
|
||||
|
||||
struct xhci_interrupter *
|
||||
xhci_create_secondary_interrupter(struct usb_hcd *hcd, int num_seg)
|
||||
xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs)
|
||||
{
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
struct xhci_interrupter *ir;
|
||||
@ -2354,7 +2344,7 @@ xhci_create_secondary_interrupter(struct usb_hcd *hcd, int num_seg)
|
||||
if (!xhci->interrupters || xhci->max_interrupters <= 1)
|
||||
return NULL;
|
||||
|
||||
ir = xhci_alloc_interrupter(xhci, num_seg, GFP_KERNEL);
|
||||
ir = xhci_alloc_interrupter(xhci, segs, GFP_KERNEL);
|
||||
if (!ir)
|
||||
return NULL;
|
||||
|
||||
|
@ -45,8 +45,16 @@
|
||||
#define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_XHCI 0x9d2f
|
||||
#define PCI_DEVICE_ID_INTEL_BROXTON_M_XHCI 0x0aa8
|
||||
#define PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI 0x1aa8
|
||||
#define PCI_DEVICE_ID_INTEL_APL_XHCI 0x5aa8
|
||||
#define PCI_DEVICE_ID_INTEL_DNV_XHCI 0x19d0
|
||||
#define PCI_DEVICE_ID_INTEL_APOLLO_LAKE_XHCI 0x5aa8
|
||||
#define PCI_DEVICE_ID_INTEL_DENVERTON_XHCI 0x19d0
|
||||
#define PCI_DEVICE_ID_INTEL_ICE_LAKE_XHCI 0x8a13
|
||||
#define PCI_DEVICE_ID_INTEL_TIGER_LAKE_XHCI 0x9a13
|
||||
#define PCI_DEVICE_ID_INTEL_COMET_LAKE_XHCI 0xa3af
|
||||
#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_PCH_XHCI 0x51ed
|
||||
#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_N_PCH_XHCI 0x54ed
|
||||
|
||||
/* Thunderbolt */
|
||||
#define PCI_DEVICE_ID_INTEL_MAPLE_RIDGE_XHCI 0x1138
|
||||
#define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_XHCI 0x15b5
|
||||
#define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_XHCI 0x15b6
|
||||
#define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_XHCI 0x15c1
|
||||
@ -55,12 +63,6 @@
|
||||
#define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_XHCI 0x15e9
|
||||
#define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_XHCI 0x15ec
|
||||
#define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_XHCI 0x15f0
|
||||
#define PCI_DEVICE_ID_INTEL_ICE_LAKE_XHCI 0x8a13
|
||||
#define PCI_DEVICE_ID_INTEL_CML_XHCI 0xa3af
|
||||
#define PCI_DEVICE_ID_INTEL_TIGER_LAKE_XHCI 0x9a13
|
||||
#define PCI_DEVICE_ID_INTEL_MAPLE_RIDGE_XHCI 0x1138
|
||||
#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_PCH_XHCI 0x51ed
|
||||
#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_N_PCH_XHCI 0x54ed
|
||||
|
||||
#define PCI_DEVICE_ID_AMD_RENOIR_XHCI 0x1639
|
||||
#define PCI_DEVICE_ID_AMD_PROMONTORYA_4 0x43b9
|
||||
@ -270,17 +272,12 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
|
||||
"QUIRK: Fresco Logic revision %u "
|
||||
"has broken MSI implementation",
|
||||
pdev->revision);
|
||||
xhci->quirks |= XHCI_TRUST_TX_LENGTH;
|
||||
}
|
||||
|
||||
if (pdev->vendor == PCI_VENDOR_ID_FRESCO_LOGIC &&
|
||||
pdev->device == PCI_DEVICE_ID_FRESCO_LOGIC_FL1009)
|
||||
xhci->quirks |= XHCI_BROKEN_STREAMS;
|
||||
|
||||
if (pdev->vendor == PCI_VENDOR_ID_FRESCO_LOGIC &&
|
||||
pdev->device == PCI_DEVICE_ID_FRESCO_LOGIC_FL1100)
|
||||
xhci->quirks |= XHCI_TRUST_TX_LENGTH;
|
||||
|
||||
if (pdev->vendor == PCI_VENDOR_ID_NEC)
|
||||
xhci->quirks |= XHCI_NEC_HOST;
|
||||
|
||||
@ -307,11 +304,8 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
|
||||
xhci->quirks |= XHCI_RESET_ON_RESUME;
|
||||
}
|
||||
|
||||
if (pdev->vendor == PCI_VENDOR_ID_AMD) {
|
||||
xhci->quirks |= XHCI_TRUST_TX_LENGTH;
|
||||
if (pdev->device == 0x43f7)
|
||||
xhci->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW;
|
||||
}
|
||||
if (pdev->vendor == PCI_VENDOR_ID_AMD && pdev->device == 0x43f7)
|
||||
xhci->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW;
|
||||
|
||||
if ((pdev->vendor == PCI_VENDOR_ID_AMD) &&
|
||||
((pdev->device == PCI_DEVICE_ID_AMD_PROMONTORYA_4) ||
|
||||
@ -356,9 +350,9 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI ||
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_M_XHCI ||
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI ||
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_APL_XHCI ||
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_DNV_XHCI ||
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_CML_XHCI)) {
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_APOLLO_LAKE_XHCI ||
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_DENVERTON_XHCI ||
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_COMET_LAKE_XHCI)) {
|
||||
xhci->quirks |= XHCI_PME_STUCK_QUIRK;
|
||||
}
|
||||
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
|
||||
@ -367,14 +361,14 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
|
||||
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
|
||||
(pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI ||
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_XHCI ||
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_APL_XHCI))
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_APOLLO_LAKE_XHCI))
|
||||
xhci->quirks |= XHCI_INTEL_USB_ROLE_SW;
|
||||
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
|
||||
(pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI ||
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_XHCI ||
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_XHCI ||
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_APL_XHCI ||
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_DNV_XHCI))
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_APOLLO_LAKE_XHCI ||
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_DENVERTON_XHCI))
|
||||
xhci->quirks |= XHCI_MISSING_CAS;
|
||||
|
||||
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
|
||||
@ -399,12 +393,10 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
|
||||
if (pdev->vendor == PCI_VENDOR_ID_ETRON &&
|
||||
pdev->device == PCI_DEVICE_ID_EJ168) {
|
||||
xhci->quirks |= XHCI_RESET_ON_RESUME;
|
||||
xhci->quirks |= XHCI_TRUST_TX_LENGTH;
|
||||
xhci->quirks |= XHCI_BROKEN_STREAMS;
|
||||
}
|
||||
if (pdev->vendor == PCI_VENDOR_ID_RENESAS &&
|
||||
pdev->device == 0x0014) {
|
||||
xhci->quirks |= XHCI_TRUST_TX_LENGTH;
|
||||
xhci->quirks |= XHCI_ZERO_64B_REGS;
|
||||
}
|
||||
if (pdev->vendor == PCI_VENDOR_ID_RENESAS &&
|
||||
@ -434,7 +426,6 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
|
||||
}
|
||||
if (pdev->vendor == PCI_VENDOR_ID_ASMEDIA &&
|
||||
pdev->device == PCI_DEVICE_ID_ASMEDIA_1042A_XHCI) {
|
||||
xhci->quirks |= XHCI_TRUST_TX_LENGTH;
|
||||
xhci->quirks |= XHCI_NO_64BIT_SUPPORT;
|
||||
}
|
||||
if (pdev->vendor == PCI_VENDOR_ID_ASMEDIA &&
|
||||
@ -889,10 +880,10 @@ static const struct xhci_driver_data reneses_data = {
|
||||
|
||||
/* PCI driver selection metadata; PCI hotplugging uses this */
|
||||
static const struct pci_device_id pci_ids[] = {
|
||||
{ PCI_DEVICE(0x1912, 0x0014),
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_RENESAS, 0x0014),
|
||||
.driver_data = (unsigned long)&reneses_data,
|
||||
},
|
||||
{ PCI_DEVICE(0x1912, 0x0015),
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_RENESAS, 0x0015),
|
||||
.driver_data = (unsigned long)&reneses_data,
|
||||
},
|
||||
/* handle any USB 3.0 xHCI controller */
|
||||
|
@ -214,8 +214,7 @@ static int xhci_rcar_resume_quirk(struct usb_hcd *hcd)
|
||||
*/
|
||||
#define SET_XHCI_PLAT_PRIV_FOR_RCAR(firmware) \
|
||||
.firmware_name = firmware, \
|
||||
.quirks = XHCI_NO_64BIT_SUPPORT | XHCI_TRUST_TX_LENGTH | \
|
||||
XHCI_SLOW_SUSPEND, \
|
||||
.quirks = XHCI_NO_64BIT_SUPPORT | XHCI_SLOW_SUSPEND, \
|
||||
.init_quirk = xhci_rcar_init_quirk, \
|
||||
.plat_start = xhci_rcar_start, \
|
||||
.resume_quirk = xhci_rcar_resume_quirk,
|
||||
@ -229,8 +228,7 @@ static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen3 = {
|
||||
};
|
||||
|
||||
static const struct xhci_plat_priv xhci_plat_renesas_rzv2m = {
|
||||
.quirks = XHCI_NO_64BIT_SUPPORT | XHCI_TRUST_TX_LENGTH |
|
||||
XHCI_SLOW_SUSPEND,
|
||||
.quirks = XHCI_NO_64BIT_SUPPORT | XHCI_SLOW_SUSPEND,
|
||||
.init_quirk = xhci_rzv2m_init_quirk,
|
||||
.plat_start = xhci_rzv2m_start,
|
||||
};
|
||||
|
@ -308,7 +308,7 @@ static unsigned int xhci_num_trbs_free(struct xhci_hcd *xhci, struct xhci_ring *
|
||||
free += last_on_seg - enq;
|
||||
enq_seg = enq_seg->next;
|
||||
enq = enq_seg->trbs;
|
||||
} while (i++ <= ring->num_segs);
|
||||
} while (i++ < ring->num_segs);
|
||||
|
||||
return free;
|
||||
}
|
||||
@ -351,10 +351,8 @@ static unsigned int xhci_ring_expansion_needed(struct xhci_hcd *xhci, struct xhc
|
||||
while (new_segs > 0) {
|
||||
seg = seg->next;
|
||||
if (seg == ring->deq_seg) {
|
||||
xhci_dbg(xhci, "Ring expansion by %d segments needed\n",
|
||||
new_segs);
|
||||
xhci_dbg(xhci, "Adding %d trbs moves enq %d trbs into deq seg\n",
|
||||
num_trbs, trbs_past_seg % TRBS_PER_SEGMENT);
|
||||
xhci_dbg(xhci, "Adding %d trbs requires expanding ring by %d segments\n",
|
||||
num_trbs, new_segs);
|
||||
return new_segs;
|
||||
}
|
||||
new_segs--;
|
||||
@ -1026,8 +1024,7 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep)
|
||||
td->urb->stream_id);
|
||||
hw_deq &= ~0xf;
|
||||
|
||||
if (td->cancel_status == TD_HALTED ||
|
||||
trb_in_td(xhci, td->start_seg, td->first_trb, td->last_trb, hw_deq, false)) {
|
||||
if (td->cancel_status == TD_HALTED || trb_in_td(xhci, td, hw_deq, false)) {
|
||||
switch (td->cancel_status) {
|
||||
case TD_CLEARED: /* TD is already no-op */
|
||||
case TD_CLEARING_CACHE: /* set TR deq command already queued */
|
||||
@ -1084,8 +1081,7 @@ static struct xhci_td *find_halted_td(struct xhci_virt_ep *ep)
|
||||
hw_deq = xhci_get_hw_deq(ep->xhci, ep->vdev, ep->ep_index, 0);
|
||||
hw_deq &= ~0xf;
|
||||
td = list_first_entry(&ep->ring->td_list, struct xhci_td, td_list);
|
||||
if (trb_in_td(ep->xhci, td->start_seg, td->first_trb,
|
||||
td->last_trb, hw_deq, false))
|
||||
if (trb_in_td(ep->xhci, td, hw_deq, false))
|
||||
return td;
|
||||
}
|
||||
return NULL;
|
||||
@ -2051,25 +2047,19 @@ cleanup:
|
||||
}
|
||||
|
||||
/*
|
||||
* This TD is defined by the TRBs starting at start_trb in start_seg and ending
|
||||
* at end_trb, which may be in another segment. If the suspect DMA address is a
|
||||
* TRB in this TD, this function returns that TRB's segment. Otherwise it
|
||||
* returns 0.
|
||||
* If the suspect DMA address is a TRB in this TD, this function returns that
|
||||
* TRB's segment. Otherwise it returns 0.
|
||||
*/
|
||||
struct xhci_segment *trb_in_td(struct xhci_hcd *xhci,
|
||||
struct xhci_segment *start_seg,
|
||||
union xhci_trb *start_trb,
|
||||
union xhci_trb *end_trb,
|
||||
dma_addr_t suspect_dma,
|
||||
bool debug)
|
||||
struct xhci_segment *trb_in_td(struct xhci_hcd *xhci, struct xhci_td *td, dma_addr_t suspect_dma,
|
||||
bool debug)
|
||||
{
|
||||
dma_addr_t start_dma;
|
||||
dma_addr_t end_seg_dma;
|
||||
dma_addr_t end_trb_dma;
|
||||
struct xhci_segment *cur_seg;
|
||||
|
||||
start_dma = xhci_trb_virt_to_dma(start_seg, start_trb);
|
||||
cur_seg = start_seg;
|
||||
start_dma = xhci_trb_virt_to_dma(td->start_seg, td->first_trb);
|
||||
cur_seg = td->start_seg;
|
||||
|
||||
do {
|
||||
if (start_dma == 0)
|
||||
@ -2078,7 +2068,7 @@ struct xhci_segment *trb_in_td(struct xhci_hcd *xhci,
|
||||
end_seg_dma = xhci_trb_virt_to_dma(cur_seg,
|
||||
&cur_seg->trbs[TRBS_PER_SEGMENT - 1]);
|
||||
/* If the end TRB isn't in this segment, this is set to 0 */
|
||||
end_trb_dma = xhci_trb_virt_to_dma(cur_seg, end_trb);
|
||||
end_trb_dma = xhci_trb_virt_to_dma(cur_seg, td->last_trb);
|
||||
|
||||
if (debug)
|
||||
xhci_warn(xhci,
|
||||
@ -2112,7 +2102,7 @@ struct xhci_segment *trb_in_td(struct xhci_hcd *xhci,
|
||||
}
|
||||
cur_seg = cur_seg->next;
|
||||
start_dma = xhci_trb_virt_to_dma(cur_seg, &cur_seg->trbs[0]);
|
||||
} while (cur_seg != start_seg);
|
||||
} while (cur_seg != td->start_seg);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@ -2399,8 +2389,7 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
|
||||
break;
|
||||
if (remaining) {
|
||||
frame->status = short_framestatus;
|
||||
if (xhci->quirks & XHCI_TRUST_TX_LENGTH)
|
||||
sum_trbs_for_length = true;
|
||||
sum_trbs_for_length = true;
|
||||
break;
|
||||
}
|
||||
frame->status = 0;
|
||||
@ -2590,7 +2579,6 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
||||
struct xhci_ep_ctx *ep_ctx;
|
||||
u32 trb_comp_code;
|
||||
int td_num = 0;
|
||||
bool handling_skipped_tds = false;
|
||||
|
||||
slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
|
||||
ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1;
|
||||
@ -2628,16 +2616,17 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
||||
else
|
||||
xhci_handle_halted_endpoint(xhci, ep, NULL,
|
||||
EP_SOFT_RESET);
|
||||
goto cleanup;
|
||||
break;
|
||||
case COMP_RING_UNDERRUN:
|
||||
case COMP_RING_OVERRUN:
|
||||
case COMP_STOPPED_LENGTH_INVALID:
|
||||
goto cleanup;
|
||||
break;
|
||||
default:
|
||||
xhci_err(xhci, "ERROR Transfer event for unknown stream ring slot %u ep %u\n",
|
||||
slot_id, ep_index);
|
||||
goto err_out;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Count current td numbers if ep->skip is set */
|
||||
@ -2650,15 +2639,11 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
||||
* transfer type
|
||||
*/
|
||||
case COMP_SUCCESS:
|
||||
if (EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)) == 0)
|
||||
break;
|
||||
if (xhci->quirks & XHCI_TRUST_TX_LENGTH ||
|
||||
ep_ring->last_td_was_short)
|
||||
if (EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)) != 0) {
|
||||
trb_comp_code = COMP_SHORT_PACKET;
|
||||
else
|
||||
xhci_warn_ratelimited(xhci,
|
||||
"WARN Successful completion on short TX for slot %u ep %u: needs XHCI_TRUST_TX_LENGTH quirk?\n",
|
||||
slot_id, ep_index);
|
||||
xhci_dbg(xhci, "Successful completion on short TX for slot %u ep %u with last td short %d\n",
|
||||
slot_id, ep_index, ep_ring->last_td_was_short);
|
||||
}
|
||||
break;
|
||||
case COMP_SHORT_PACKET:
|
||||
break;
|
||||
@ -2730,19 +2715,19 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
||||
*/
|
||||
xhci_dbg(xhci, "underrun event on endpoint\n");
|
||||
if (!list_empty(&ep_ring->td_list))
|
||||
xhci_dbg(xhci, "Underrun Event for slot %d ep %d "
|
||||
"still with TDs queued?\n",
|
||||
TRB_TO_SLOT_ID(le32_to_cpu(event->flags)),
|
||||
ep_index);
|
||||
goto cleanup;
|
||||
xhci_dbg(xhci, "Underrun Event for slot %u ep %d still with TDs queued?\n",
|
||||
slot_id, ep_index);
|
||||
if (ep->skip)
|
||||
break;
|
||||
return 0;
|
||||
case COMP_RING_OVERRUN:
|
||||
xhci_dbg(xhci, "overrun event on endpoint\n");
|
||||
if (!list_empty(&ep_ring->td_list))
|
||||
xhci_dbg(xhci, "Overrun Event for slot %d ep %d "
|
||||
"still with TDs queued?\n",
|
||||
TRB_TO_SLOT_ID(le32_to_cpu(event->flags)),
|
||||
ep_index);
|
||||
goto cleanup;
|
||||
xhci_dbg(xhci, "Overrun Event for slot %u ep %d still with TDs queued?\n",
|
||||
slot_id, ep_index);
|
||||
if (ep->skip)
|
||||
break;
|
||||
return 0;
|
||||
case COMP_MISSED_SERVICE_ERROR:
|
||||
/*
|
||||
* When encounter missed service error, one or more isoc tds
|
||||
@ -2754,13 +2739,13 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
||||
xhci_dbg(xhci,
|
||||
"Miss service interval error for slot %u ep %u, set skip flag\n",
|
||||
slot_id, ep_index);
|
||||
goto cleanup;
|
||||
return 0;
|
||||
case COMP_NO_PING_RESPONSE_ERROR:
|
||||
ep->skip = true;
|
||||
xhci_dbg(xhci,
|
||||
"No Ping response error for slot %u ep %u, Skip one Isoc TD\n",
|
||||
slot_id, ep_index);
|
||||
goto cleanup;
|
||||
return 0;
|
||||
|
||||
case COMP_INCOMPATIBLE_DEVICE_ERROR:
|
||||
/* needs disable slot command to recover */
|
||||
@ -2777,7 +2762,9 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
||||
xhci_warn(xhci,
|
||||
"ERROR Unknown event condition %u for slot %u ep %u , HC probably busted\n",
|
||||
trb_comp_code, slot_id, ep_index);
|
||||
goto cleanup;
|
||||
if (ep->skip)
|
||||
break;
|
||||
return 0;
|
||||
}
|
||||
|
||||
do {
|
||||
@ -2796,9 +2783,8 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
||||
if (!(trb_comp_code == COMP_STOPPED ||
|
||||
trb_comp_code == COMP_STOPPED_LENGTH_INVALID ||
|
||||
ep_ring->last_td_was_short)) {
|
||||
xhci_warn(xhci, "WARN Event TRB for slot %d ep %d with no TDs queued?\n",
|
||||
TRB_TO_SLOT_ID(le32_to_cpu(event->flags)),
|
||||
ep_index);
|
||||
xhci_warn(xhci, "WARN Event TRB for slot %u ep %d with no TDs queued?\n",
|
||||
slot_id, ep_index);
|
||||
}
|
||||
if (ep->skip) {
|
||||
ep->skip = false;
|
||||
@ -2811,7 +2797,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
||||
xhci_handle_halted_endpoint(xhci, ep, NULL,
|
||||
EP_HARD_RESET);
|
||||
}
|
||||
goto cleanup;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We've skipped all the TDs on the ep ring when ep->skip set */
|
||||
@ -2819,7 +2805,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
||||
ep->skip = false;
|
||||
xhci_dbg(xhci, "All tds on the ep_ring skipped. Clear skip flag for slot %u ep %u.\n",
|
||||
slot_id, ep_index);
|
||||
goto cleanup;
|
||||
return 0;
|
||||
}
|
||||
|
||||
td = list_first_entry(&ep_ring->td_list, struct xhci_td,
|
||||
@ -2828,8 +2814,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
||||
td_num--;
|
||||
|
||||
/* Is this a TRB in the currently executing TD? */
|
||||
ep_seg = trb_in_td(xhci, td->start_seg, td->first_trb,
|
||||
td->last_trb, ep_trb_dma, false);
|
||||
ep_seg = trb_in_td(xhci, td, ep_trb_dma, false);
|
||||
|
||||
/*
|
||||
* Skip the Force Stopped Event. The event_trb(event_dma) of FSE
|
||||
@ -2841,14 +2826,14 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
||||
*/
|
||||
if (!ep_seg && (trb_comp_code == COMP_STOPPED ||
|
||||
trb_comp_code == COMP_STOPPED_LENGTH_INVALID)) {
|
||||
goto cleanup;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ep_seg) {
|
||||
|
||||
if (ep->skip && usb_endpoint_xfer_isoc(&td->urb->ep->desc)) {
|
||||
skip_isoc_td(xhci, td, ep, status);
|
||||
goto cleanup;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2858,7 +2843,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
||||
if ((xhci->quirks & XHCI_SPURIOUS_SUCCESS) &&
|
||||
ep_ring->last_td_was_short) {
|
||||
ep_ring->last_td_was_short = false;
|
||||
goto cleanup;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2876,8 +2861,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
||||
!list_is_last(&td->td_list, &ep_ring->td_list)) {
|
||||
struct xhci_td *td_next = list_next_entry(td, td_list);
|
||||
|
||||
ep_seg = trb_in_td(xhci, td_next->start_seg, td_next->first_trb,
|
||||
td_next->last_trb, ep_trb_dma, false);
|
||||
ep_seg = trb_in_td(xhci, td_next, ep_trb_dma, false);
|
||||
if (ep_seg) {
|
||||
/* give back previous TD, start handling new */
|
||||
xhci_dbg(xhci, "Missing TD completion event after mid TD error\n");
|
||||
@ -2896,8 +2880,8 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
||||
"part of current TD ep_index %d "
|
||||
"comp_code %u\n", ep_index,
|
||||
trb_comp_code);
|
||||
trb_in_td(xhci, td->start_seg, td->first_trb,
|
||||
td->last_trb, ep_trb_dma, true);
|
||||
trb_in_td(xhci, td, ep_trb_dma, true);
|
||||
|
||||
return -ESHUTDOWN;
|
||||
}
|
||||
}
|
||||
@ -2933,30 +2917,24 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
||||
trb_comp_code))
|
||||
xhci_handle_halted_endpoint(xhci, ep, td,
|
||||
EP_HARD_RESET);
|
||||
goto cleanup;
|
||||
} else {
|
||||
td->status = status;
|
||||
|
||||
/* update the urb's actual_length and give back to the core */
|
||||
if (usb_endpoint_xfer_control(&td->urb->ep->desc))
|
||||
process_ctrl_td(xhci, ep, ep_ring, td, ep_trb, event);
|
||||
else if (usb_endpoint_xfer_isoc(&td->urb->ep->desc))
|
||||
process_isoc_td(xhci, ep, ep_ring, td, ep_trb, event);
|
||||
else
|
||||
process_bulk_intr_td(xhci, ep, ep_ring, td, ep_trb, event);
|
||||
}
|
||||
|
||||
td->status = status;
|
||||
|
||||
/* update the urb's actual_length and give back to the core */
|
||||
if (usb_endpoint_xfer_control(&td->urb->ep->desc))
|
||||
process_ctrl_td(xhci, ep, ep_ring, td, ep_trb, event);
|
||||
else if (usb_endpoint_xfer_isoc(&td->urb->ep->desc))
|
||||
process_isoc_td(xhci, ep, ep_ring, td, ep_trb, event);
|
||||
else
|
||||
process_bulk_intr_td(xhci, ep, ep_ring, td, ep_trb, event);
|
||||
cleanup:
|
||||
handling_skipped_tds = ep->skip &&
|
||||
trb_comp_code != COMP_MISSED_SERVICE_ERROR &&
|
||||
trb_comp_code != COMP_NO_PING_RESPONSE_ERROR;
|
||||
|
||||
/*
|
||||
* If ep->skip is set, it means there are missed tds on the
|
||||
* endpoint ring need to take care of.
|
||||
* Process them as short transfer until reach the td pointed by
|
||||
* the event.
|
||||
*/
|
||||
} while (handling_skipped_tds);
|
||||
} while (ep->skip);
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -4507,35 +4507,13 @@ static int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* check if a usb2 port supports a given extened capability protocol
|
||||
* only USB2 ports extended protocol capability values are cached.
|
||||
* Return 1 if capability is supported
|
||||
*/
|
||||
static int xhci_check_usb2_port_capability(struct xhci_hcd *xhci, int port,
|
||||
unsigned capability)
|
||||
{
|
||||
u32 port_offset, port_count;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < xhci->num_ext_caps; i++) {
|
||||
if (xhci->ext_caps[i] & capability) {
|
||||
/* port offsets starts at 1 */
|
||||
port_offset = XHCI_EXT_PORT_OFF(xhci->ext_caps[i]) - 1;
|
||||
port_count = XHCI_EXT_PORT_COUNT(xhci->ext_caps[i]);
|
||||
if (port >= port_offset &&
|
||||
port < port_offset + port_count)
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
|
||||
{
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
int portnum = udev->portnum - 1;
|
||||
struct xhci_port *port;
|
||||
u32 capability;
|
||||
|
||||
if (hcd->speed >= HCD_USB3 || !udev->lpm_capable)
|
||||
if (hcd->speed >= HCD_USB3 || !udev->lpm_capable || !xhci->hw_lpm_support)
|
||||
return 0;
|
||||
|
||||
/* we only support lpm for non-hub device connected to root hub yet */
|
||||
@ -4543,14 +4521,14 @@ static int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
|
||||
udev->descriptor.bDeviceClass == USB_CLASS_HUB)
|
||||
return 0;
|
||||
|
||||
if (xhci->hw_lpm_support == 1 &&
|
||||
xhci_check_usb2_port_capability(
|
||||
xhci, portnum, XHCI_HLC)) {
|
||||
port = xhci->usb2_rhub.ports[udev->portnum - 1];
|
||||
capability = port->port_cap->protocol_caps;
|
||||
|
||||
if (capability & XHCI_HLC) {
|
||||
udev->usb2_hw_lpm_capable = 1;
|
||||
udev->l1_params.timeout = XHCI_L1_TIMEOUT;
|
||||
udev->l1_params.besl = XHCI_DEFAULT_BESL;
|
||||
if (xhci_check_usb2_port_capability(xhci, portnum,
|
||||
XHCI_BLC))
|
||||
if (capability & XHCI_BLC)
|
||||
udev->usb2_hw_lpm_besl_capable = 1;
|
||||
}
|
||||
|
||||
|
@ -1376,8 +1376,6 @@ struct xhci_erst {
|
||||
unsigned int num_entries;
|
||||
/* xhci->event_ring keeps track of segment dma addresses */
|
||||
dma_addr_t erst_dma_addr;
|
||||
/* Num entries the ERST can contain */
|
||||
unsigned int erst_size;
|
||||
};
|
||||
|
||||
struct xhci_scratchpad {
|
||||
@ -1392,8 +1390,8 @@ struct urb_priv {
|
||||
struct xhci_td td[] __counted_by(num_tds);
|
||||
};
|
||||
|
||||
/* Reasonable limit for number of Event Ring segments (spec allows 32k) */
|
||||
#define ERST_MAX_SEGS 2
|
||||
/* Number of Event Ring segments to allocate, when amount is not specified. (spec allows 32k) */
|
||||
#define ERST_DEFAULT_SEGS 2
|
||||
/* Poll every 60 seconds */
|
||||
#define POLL_TIMEOUT 60
|
||||
/* Stop endpoint command timeout (secs) for URB cancellation watchdog timer */
|
||||
@ -1451,6 +1449,7 @@ struct xhci_port_cap {
|
||||
u8 psi_uid_count;
|
||||
u8 maj_rev;
|
||||
u8 min_rev;
|
||||
u32 protocol_caps;
|
||||
};
|
||||
|
||||
struct xhci_port {
|
||||
@ -1589,7 +1588,7 @@ struct xhci_hcd {
|
||||
#define XHCI_RESET_ON_RESUME BIT_ULL(7)
|
||||
#define XHCI_SW_BW_CHECKING BIT_ULL(8)
|
||||
#define XHCI_AMD_0x96_HOST BIT_ULL(9)
|
||||
#define XHCI_TRUST_TX_LENGTH BIT_ULL(10)
|
||||
#define XHCI_TRUST_TX_LENGTH BIT_ULL(10) /* Deprecated */
|
||||
#define XHCI_LPM_SUPPORT BIT_ULL(11)
|
||||
#define XHCI_INTEL_HOST BIT_ULL(12)
|
||||
#define XHCI_SPURIOUS_REBOOT BIT_ULL(13)
|
||||
@ -1640,9 +1639,6 @@ struct xhci_hcd {
|
||||
unsigned broken_suspend:1;
|
||||
/* Indicates that omitting hcd is supported if root hub has no ports */
|
||||
unsigned allow_single_roothub:1;
|
||||
/* cached usb2 extened protocol capabilites */
|
||||
u32 *ext_caps;
|
||||
unsigned int num_ext_caps;
|
||||
/* cached extended protocol port capabilities */
|
||||
struct xhci_port_cap *port_caps;
|
||||
unsigned int num_port_caps;
|
||||
@ -1729,8 +1725,6 @@ static inline bool xhci_has_one_roothub(struct xhci_hcd *xhci)
|
||||
dev_err(xhci_to_hcd(xhci)->self.controller , fmt , ## args)
|
||||
#define xhci_warn(xhci, fmt, args...) \
|
||||
dev_warn(xhci_to_hcd(xhci)->self.controller , fmt , ## args)
|
||||
#define xhci_warn_ratelimited(xhci, fmt, args...) \
|
||||
dev_warn_ratelimited(xhci_to_hcd(xhci)->self.controller , fmt , ## args)
|
||||
#define xhci_info(xhci, fmt, args...) \
|
||||
dev_info(xhci_to_hcd(xhci)->self.controller , fmt , ## args)
|
||||
|
||||
@ -1833,7 +1827,7 @@ struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci,
|
||||
void xhci_free_container_ctx(struct xhci_hcd *xhci,
|
||||
struct xhci_container_ctx *ctx);
|
||||
struct xhci_interrupter *
|
||||
xhci_create_secondary_interrupter(struct usb_hcd *hcd, int num_seg);
|
||||
xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs);
|
||||
void xhci_remove_secondary_interrupter(struct usb_hcd
|
||||
*hcd, struct xhci_interrupter *ir);
|
||||
|
||||
@ -1876,9 +1870,8 @@ int xhci_alloc_tt_info(struct xhci_hcd *xhci,
|
||||
|
||||
/* xHCI ring, segment, TRB, and TD functions */
|
||||
dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg, union xhci_trb *trb);
|
||||
struct xhci_segment *trb_in_td(struct xhci_hcd *xhci,
|
||||
struct xhci_segment *start_seg, union xhci_trb *start_trb,
|
||||
union xhci_trb *end_trb, dma_addr_t suspect_dma, bool debug);
|
||||
struct xhci_segment *trb_in_td(struct xhci_hcd *xhci, struct xhci_td *td,
|
||||
dma_addr_t suspect_dma, bool debug);
|
||||
int xhci_is_vendor_info_code(struct xhci_hcd *xhci, unsigned int trb_comp_code);
|
||||
void xhci_ring_cmd_db(struct xhci_hcd *xhci);
|
||||
int xhci_queue_slot_control(struct xhci_hcd *xhci, struct xhci_command *cmd,
|
||||
@ -2340,7 +2333,12 @@ static inline const char *xhci_decode_portsc(char *str, u32 portsc)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = sprintf(str, "%s %s %s Link:%s PortSpeed:%d ",
|
||||
ret = sprintf(str, "0x%08x ", portsc);
|
||||
|
||||
if (portsc == ~(u32)0)
|
||||
return str;
|
||||
|
||||
ret += sprintf(str + ret, "%s %s %s Link:%s PortSpeed:%d ",
|
||||
portsc & PORT_POWER ? "Powered" : "Powered-off",
|
||||
portsc & PORT_CONNECT ? "Connected" : "Not-connected",
|
||||
portsc & PORT_PE ? "Enabled" : "Disabled",
|
||||
|
@ -316,18 +316,18 @@ config BRCM_USB_PINMAP
|
||||
signals, which are typically on dedicated pins on the chip,
|
||||
to any gpio.
|
||||
|
||||
config USB_ONBOARD_HUB
|
||||
tristate "Onboard USB hub support"
|
||||
config USB_ONBOARD_DEV
|
||||
tristate "Onboard USB device support"
|
||||
depends on OF
|
||||
help
|
||||
Say Y here if you want to support discrete onboard USB hubs that
|
||||
don't require an additional control bus for initialization, but
|
||||
need some non-trivial form of initialization, such as enabling a
|
||||
power regulator. An example for such a hub is the Realtek
|
||||
RTS5411.
|
||||
Say Y here if you want to support discrete onboard USB devices
|
||||
that don't require an additional control bus for initialization,
|
||||
but need some non-trivial form of initialization, such as
|
||||
enabling a power regulator. An example for such device is the
|
||||
Realtek RTS5411 hub.
|
||||
|
||||
This driver can be used as a module but its state (module vs
|
||||
builtin) must match the state of the USB subsystem. Enabling
|
||||
this config will enable the driver and it will automatically
|
||||
match the state of the USB subsystem. If this driver is a
|
||||
module it will be called onboard_usb_hub.
|
||||
module it will be called onboard_usb_dev.
|
||||
|
@ -33,4 +33,4 @@ obj-$(CONFIG_USB_CHAOSKEY) += chaoskey.o
|
||||
obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga/
|
||||
obj-$(CONFIG_USB_LINK_LAYER_TEST) += lvstest.o
|
||||
obj-$(CONFIG_BRCM_USB_PINMAP) += brcmstb-usb-pinmap.o
|
||||
obj-$(CONFIG_USB_ONBOARD_HUB) += onboard_usb_hub.o
|
||||
obj-$(CONFIG_USB_ONBOARD_DEV) += onboard_usb_dev.o
|
||||
|
550
drivers/usb/misc/onboard_usb_dev.c
Normal file
550
drivers/usb/misc/onboard_usb_dev.c
Normal file
@ -0,0 +1,550 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Driver for onboard USB devices
|
||||
*
|
||||
* Copyright (c) 2022, Google LLC
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/usb/onboard_dev.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include "onboard_usb_dev.h"
|
||||
|
||||
static void onboard_dev_attach_usb_driver(struct work_struct *work);
|
||||
|
||||
static struct usb_device_driver onboard_dev_usbdev_driver;
|
||||
static DECLARE_WORK(attach_usb_driver_work, onboard_dev_attach_usb_driver);
|
||||
|
||||
/************************** Platform driver **************************/
|
||||
|
||||
struct usbdev_node {
|
||||
struct usb_device *udev;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct onboard_dev {
|
||||
struct regulator_bulk_data supplies[MAX_SUPPLIES];
|
||||
struct device *dev;
|
||||
const struct onboard_dev_pdata *pdata;
|
||||
struct gpio_desc *reset_gpio;
|
||||
bool always_powered_in_suspend;
|
||||
bool is_powered_on;
|
||||
bool going_away;
|
||||
struct list_head udev_list;
|
||||
struct mutex lock;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static int onboard_dev_get_regulators(struct onboard_dev *onboard_dev)
|
||||
{
|
||||
const char * const *supply_names = onboard_dev->pdata->supply_names;
|
||||
unsigned int num_supplies = onboard_dev->pdata->num_supplies;
|
||||
struct device *dev = onboard_dev->dev;
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
if (num_supplies > MAX_SUPPLIES)
|
||||
return dev_err_probe(dev, -EINVAL, "max %d supplies supported!\n",
|
||||
MAX_SUPPLIES);
|
||||
|
||||
for (i = 0; i < num_supplies; i++)
|
||||
onboard_dev->supplies[i].supply = supply_names[i];
|
||||
|
||||
err = devm_regulator_bulk_get(dev, num_supplies, onboard_dev->supplies);
|
||||
if (err)
|
||||
dev_err(dev, "Failed to get regulator supplies: %pe\n",
|
||||
ERR_PTR(err));
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int onboard_dev_power_on(struct onboard_dev *onboard_dev)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = clk_prepare_enable(onboard_dev->clk);
|
||||
if (err) {
|
||||
dev_err(onboard_dev->dev, "failed to enable clock: %pe\n",
|
||||
ERR_PTR(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
err = regulator_bulk_enable(onboard_dev->pdata->num_supplies,
|
||||
onboard_dev->supplies);
|
||||
if (err) {
|
||||
dev_err(onboard_dev->dev, "failed to enable supplies: %pe\n",
|
||||
ERR_PTR(err));
|
||||
goto disable_clk;
|
||||
}
|
||||
|
||||
fsleep(onboard_dev->pdata->reset_us);
|
||||
gpiod_set_value_cansleep(onboard_dev->reset_gpio, 0);
|
||||
|
||||
onboard_dev->is_powered_on = true;
|
||||
|
||||
return 0;
|
||||
|
||||
disable_clk:
|
||||
clk_disable_unprepare(onboard_dev->clk);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int onboard_dev_power_off(struct onboard_dev *onboard_dev)
|
||||
{
|
||||
int err;
|
||||
|
||||
gpiod_set_value_cansleep(onboard_dev->reset_gpio, 1);
|
||||
|
||||
err = regulator_bulk_disable(onboard_dev->pdata->num_supplies,
|
||||
onboard_dev->supplies);
|
||||
if (err) {
|
||||
dev_err(onboard_dev->dev, "failed to disable supplies: %pe\n",
|
||||
ERR_PTR(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
clk_disable_unprepare(onboard_dev->clk);
|
||||
|
||||
onboard_dev->is_powered_on = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused onboard_dev_suspend(struct device *dev)
|
||||
{
|
||||
struct onboard_dev *onboard_dev = dev_get_drvdata(dev);
|
||||
struct usbdev_node *node;
|
||||
bool power_off = true;
|
||||
|
||||
if (onboard_dev->always_powered_in_suspend)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&onboard_dev->lock);
|
||||
|
||||
list_for_each_entry(node, &onboard_dev->udev_list, list) {
|
||||
if (!device_may_wakeup(node->udev->bus->controller))
|
||||
continue;
|
||||
|
||||
if (usb_wakeup_enabled_descendants(node->udev)) {
|
||||
power_off = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&onboard_dev->lock);
|
||||
|
||||
if (!power_off)
|
||||
return 0;
|
||||
|
||||
return onboard_dev_power_off(onboard_dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused onboard_dev_resume(struct device *dev)
|
||||
{
|
||||
struct onboard_dev *onboard_dev = dev_get_drvdata(dev);
|
||||
|
||||
if (onboard_dev->is_powered_on)
|
||||
return 0;
|
||||
|
||||
return onboard_dev_power_on(onboard_dev);
|
||||
}
|
||||
|
||||
static inline void get_udev_link_name(const struct usb_device *udev, char *buf,
|
||||
size_t size)
|
||||
{
|
||||
snprintf(buf, size, "usb_dev.%s", dev_name(&udev->dev));
|
||||
}
|
||||
|
||||
static int onboard_dev_add_usbdev(struct onboard_dev *onboard_dev,
|
||||
struct usb_device *udev)
|
||||
{
|
||||
struct usbdev_node *node;
|
||||
char link_name[64];
|
||||
int err;
|
||||
|
||||
mutex_lock(&onboard_dev->lock);
|
||||
|
||||
if (onboard_dev->going_away) {
|
||||
err = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
node = kzalloc(sizeof(*node), GFP_KERNEL);
|
||||
if (!node) {
|
||||
err = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
node->udev = udev;
|
||||
|
||||
list_add(&node->list, &onboard_dev->udev_list);
|
||||
|
||||
mutex_unlock(&onboard_dev->lock);
|
||||
|
||||
get_udev_link_name(udev, link_name, sizeof(link_name));
|
||||
WARN_ON(sysfs_create_link(&onboard_dev->dev->kobj, &udev->dev.kobj,
|
||||
link_name));
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
mutex_unlock(&onboard_dev->lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void onboard_dev_remove_usbdev(struct onboard_dev *onboard_dev,
|
||||
const struct usb_device *udev)
|
||||
{
|
||||
struct usbdev_node *node;
|
||||
char link_name[64];
|
||||
|
||||
get_udev_link_name(udev, link_name, sizeof(link_name));
|
||||
sysfs_remove_link(&onboard_dev->dev->kobj, link_name);
|
||||
|
||||
mutex_lock(&onboard_dev->lock);
|
||||
|
||||
list_for_each_entry(node, &onboard_dev->udev_list, list) {
|
||||
if (node->udev == udev) {
|
||||
list_del(&node->list);
|
||||
kfree(node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&onboard_dev->lock);
|
||||
}
|
||||
|
||||
static ssize_t always_powered_in_suspend_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
const struct onboard_dev *onboard_dev = dev_get_drvdata(dev);
|
||||
|
||||
return sysfs_emit(buf, "%d\n", onboard_dev->always_powered_in_suspend);
|
||||
}
|
||||
|
||||
static ssize_t always_powered_in_suspend_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct onboard_dev *onboard_dev = dev_get_drvdata(dev);
|
||||
bool val;
|
||||
int ret;
|
||||
|
||||
ret = kstrtobool(buf, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
onboard_dev->always_powered_in_suspend = val;
|
||||
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR_RW(always_powered_in_suspend);
|
||||
|
||||
static struct attribute *onboard_dev_attrs[] = {
|
||||
&dev_attr_always_powered_in_suspend.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static umode_t onboard_dev_attrs_are_visible(struct kobject *kobj,
|
||||
struct attribute *attr,
|
||||
int n)
|
||||
{
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct onboard_dev *onboard_dev = dev_get_drvdata(dev);
|
||||
|
||||
if (attr == &dev_attr_always_powered_in_suspend.attr &&
|
||||
!onboard_dev->pdata->is_hub)
|
||||
return 0;
|
||||
|
||||
return attr->mode;
|
||||
}
|
||||
|
||||
static const struct attribute_group onboard_dev_group = {
|
||||
.is_visible = onboard_dev_attrs_are_visible,
|
||||
.attrs = onboard_dev_attrs,
|
||||
};
|
||||
__ATTRIBUTE_GROUPS(onboard_dev);
|
||||
|
||||
|
||||
static void onboard_dev_attach_usb_driver(struct work_struct *work)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = driver_attach(&onboard_dev_usbdev_driver.driver);
|
||||
if (err)
|
||||
pr_err("Failed to attach USB driver: %pe\n", ERR_PTR(err));
|
||||
}
|
||||
|
||||
static int onboard_dev_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct onboard_dev *onboard_dev;
|
||||
int err;
|
||||
|
||||
onboard_dev = devm_kzalloc(dev, sizeof(*onboard_dev), GFP_KERNEL);
|
||||
if (!onboard_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
onboard_dev->pdata = device_get_match_data(dev);
|
||||
if (!onboard_dev->pdata)
|
||||
return -EINVAL;
|
||||
|
||||
if (!onboard_dev->pdata->is_hub)
|
||||
onboard_dev->always_powered_in_suspend = true;
|
||||
|
||||
onboard_dev->dev = dev;
|
||||
|
||||
err = onboard_dev_get_regulators(onboard_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
onboard_dev->clk = devm_clk_get_optional(dev, NULL);
|
||||
if (IS_ERR(onboard_dev->clk))
|
||||
return dev_err_probe(dev, PTR_ERR(onboard_dev->clk),
|
||||
"failed to get clock\n");
|
||||
|
||||
onboard_dev->reset_gpio = devm_gpiod_get_optional(dev, "reset",
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(onboard_dev->reset_gpio))
|
||||
return dev_err_probe(dev, PTR_ERR(onboard_dev->reset_gpio),
|
||||
"failed to get reset GPIO\n");
|
||||
|
||||
mutex_init(&onboard_dev->lock);
|
||||
INIT_LIST_HEAD(&onboard_dev->udev_list);
|
||||
|
||||
dev_set_drvdata(dev, onboard_dev);
|
||||
|
||||
err = onboard_dev_power_on(onboard_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* The USB driver might have been detached from the USB devices by
|
||||
* onboard_dev_remove() (e.g. through an 'unbind' by userspace),
|
||||
* make sure to re-attach it if needed.
|
||||
*
|
||||
* This needs to be done deferred to avoid self-deadlocks on systems
|
||||
* with nested onboard hubs.
|
||||
*/
|
||||
schedule_work(&attach_usb_driver_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void onboard_dev_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct onboard_dev *onboard_dev = dev_get_drvdata(&pdev->dev);
|
||||
struct usbdev_node *node;
|
||||
struct usb_device *udev;
|
||||
|
||||
onboard_dev->going_away = true;
|
||||
|
||||
mutex_lock(&onboard_dev->lock);
|
||||
|
||||
/* unbind the USB devices to avoid dangling references to this device */
|
||||
while (!list_empty(&onboard_dev->udev_list)) {
|
||||
node = list_first_entry(&onboard_dev->udev_list,
|
||||
struct usbdev_node, list);
|
||||
udev = node->udev;
|
||||
|
||||
/*
|
||||
* Unbinding the driver will call onboard_dev_remove_usbdev(),
|
||||
* which acquires onboard_dev->lock. We must release the lock
|
||||
* first.
|
||||
*/
|
||||
get_device(&udev->dev);
|
||||
mutex_unlock(&onboard_dev->lock);
|
||||
device_release_driver(&udev->dev);
|
||||
put_device(&udev->dev);
|
||||
mutex_lock(&onboard_dev->lock);
|
||||
}
|
||||
|
||||
mutex_unlock(&onboard_dev->lock);
|
||||
|
||||
onboard_dev_power_off(onboard_dev);
|
||||
}
|
||||
|
||||
MODULE_DEVICE_TABLE(of, onboard_dev_match);
|
||||
|
||||
static const struct dev_pm_ops __maybe_unused onboard_dev_pm_ops = {
|
||||
SET_LATE_SYSTEM_SLEEP_PM_OPS(onboard_dev_suspend, onboard_dev_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver onboard_dev_driver = {
|
||||
.probe = onboard_dev_probe,
|
||||
.remove_new = onboard_dev_remove,
|
||||
|
||||
.driver = {
|
||||
.name = "onboard-usb-dev",
|
||||
.of_match_table = onboard_dev_match,
|
||||
.pm = pm_ptr(&onboard_dev_pm_ops),
|
||||
.dev_groups = onboard_dev_groups,
|
||||
},
|
||||
};
|
||||
|
||||
/************************** USB driver **************************/
|
||||
|
||||
#define VENDOR_ID_CYPRESS 0x04b4
|
||||
#define VENDOR_ID_GENESYS 0x05e3
|
||||
#define VENDOR_ID_MICROCHIP 0x0424
|
||||
#define VENDOR_ID_REALTEK 0x0bda
|
||||
#define VENDOR_ID_TI 0x0451
|
||||
#define VENDOR_ID_VIA 0x2109
|
||||
#define VENDOR_ID_XMOS 0x20B1
|
||||
|
||||
/*
|
||||
* Returns the onboard_dev platform device that is associated with the USB
|
||||
* device passed as parameter.
|
||||
*/
|
||||
static struct onboard_dev *_find_onboard_dev(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
struct device_node *np;
|
||||
struct onboard_dev *onboard_dev;
|
||||
|
||||
pdev = of_find_device_by_node(dev->of_node);
|
||||
if (!pdev) {
|
||||
np = of_parse_phandle(dev->of_node, "peer-hub", 0);
|
||||
if (!np) {
|
||||
dev_err(dev, "failed to find device node for peer hub\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
pdev = of_find_device_by_node(np);
|
||||
of_node_put(np);
|
||||
|
||||
if (!pdev)
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
onboard_dev = dev_get_drvdata(&pdev->dev);
|
||||
put_device(&pdev->dev);
|
||||
|
||||
/*
|
||||
* The presence of drvdata indicates that the platform driver finished
|
||||
* probing. This handles the case where (conceivably) we could be
|
||||
* running at the exact same time as the platform driver's probe. If
|
||||
* we detect the race we request probe deferral and we'll come back and
|
||||
* try again.
|
||||
*/
|
||||
if (!onboard_dev)
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
|
||||
return onboard_dev;
|
||||
}
|
||||
|
||||
static int onboard_dev_usbdev_probe(struct usb_device *udev)
|
||||
{
|
||||
struct device *dev = &udev->dev;
|
||||
struct onboard_dev *onboard_dev;
|
||||
int err;
|
||||
|
||||
/* ignore supported devices without device tree node */
|
||||
if (!dev->of_node)
|
||||
return -ENODEV;
|
||||
|
||||
onboard_dev = _find_onboard_dev(dev);
|
||||
if (IS_ERR(onboard_dev))
|
||||
return PTR_ERR(onboard_dev);
|
||||
|
||||
dev_set_drvdata(dev, onboard_dev);
|
||||
|
||||
err = onboard_dev_add_usbdev(onboard_dev, udev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void onboard_dev_usbdev_disconnect(struct usb_device *udev)
|
||||
{
|
||||
struct onboard_dev *onboard_dev = dev_get_drvdata(&udev->dev);
|
||||
|
||||
onboard_dev_remove_usbdev(onboard_dev, udev);
|
||||
}
|
||||
|
||||
static const struct usb_device_id onboard_dev_id_table[] = {
|
||||
{ USB_DEVICE(VENDOR_ID_CYPRESS, 0x6504) }, /* CYUSB33{0,1,2}x/CYUSB230x 3.0 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_CYPRESS, 0x6506) }, /* CYUSB33{0,1,2}x/CYUSB230x 2.0 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_CYPRESS, 0x6570) }, /* CY7C6563x 2.0 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_GENESYS, 0x0608) }, /* Genesys Logic GL850G USB 2.0 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_GENESYS, 0x0610) }, /* Genesys Logic GL852G USB 2.0 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_GENESYS, 0x0620) }, /* Genesys Logic GL3523 USB 3.1 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2412) }, /* USB2412 USB 2.0 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2514) }, /* USB2514B USB 2.0 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2517) }, /* USB2517 USB 2.0 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2744) }, /* USB5744 USB 2.0 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_MICROCHIP, 0x5744) }, /* USB5744 USB 3.0 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_REALTEK, 0x0411) }, /* RTS5411 USB 3.1 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_REALTEK, 0x5411) }, /* RTS5411 USB 2.1 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_REALTEK, 0x0414) }, /* RTS5414 USB 3.2 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_REALTEK, 0x5414) }, /* RTS5414 USB 2.1 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_TI, 0x8025) }, /* TI USB8020B 3.0 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_TI, 0x8027) }, /* TI USB8020B 2.0 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_TI, 0x8140) }, /* TI USB8041 3.0 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_TI, 0x8142) }, /* TI USB8041 2.0 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_VIA, 0x0817) }, /* VIA VL817 3.1 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_VIA, 0x2817) }, /* VIA VL817 2.0 HUB */
|
||||
{ USB_DEVICE(VENDOR_ID_XMOS, 0x0013) }, /* XMOS XVF3500 Voice Processor */
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, onboard_dev_id_table);
|
||||
|
||||
static struct usb_device_driver onboard_dev_usbdev_driver = {
|
||||
.name = "onboard-usb-dev",
|
||||
.probe = onboard_dev_usbdev_probe,
|
||||
.disconnect = onboard_dev_usbdev_disconnect,
|
||||
.generic_subclass = 1,
|
||||
.supports_autosuspend = 1,
|
||||
.id_table = onboard_dev_id_table,
|
||||
};
|
||||
|
||||
static int __init onboard_dev_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = usb_register_device_driver(&onboard_dev_usbdev_driver, THIS_MODULE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = platform_driver_register(&onboard_dev_driver);
|
||||
if (ret)
|
||||
usb_deregister_device_driver(&onboard_dev_usbdev_driver);
|
||||
|
||||
return ret;
|
||||
}
|
||||
module_init(onboard_dev_init);
|
||||
|
||||
static void __exit onboard_dev_exit(void)
|
||||
{
|
||||
usb_deregister_device_driver(&onboard_dev_usbdev_driver);
|
||||
platform_driver_unregister(&onboard_dev_driver);
|
||||
|
||||
cancel_work_sync(&attach_usb_driver_work);
|
||||
}
|
||||
module_exit(onboard_dev_exit);
|
||||
|
||||
MODULE_AUTHOR("Matthias Kaehlcke <mka@chromium.org>");
|
||||
MODULE_DESCRIPTION("Driver for discrete onboard USB devices");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -3,65 +3,96 @@
|
||||
* Copyright (c) 2022, Google LLC
|
||||
*/
|
||||
|
||||
#ifndef _USB_MISC_ONBOARD_USB_HUB_H
|
||||
#define _USB_MISC_ONBOARD_USB_HUB_H
|
||||
#ifndef _USB_MISC_ONBOARD_USB_DEV_H
|
||||
#define _USB_MISC_ONBOARD_USB_DEV_H
|
||||
|
||||
struct onboard_hub_pdata {
|
||||
#define MAX_SUPPLIES 2
|
||||
|
||||
struct onboard_dev_pdata {
|
||||
unsigned long reset_us; /* reset pulse width in us */
|
||||
unsigned int num_supplies; /* number of supplies */
|
||||
const char * const supply_names[MAX_SUPPLIES];
|
||||
bool is_hub;
|
||||
};
|
||||
|
||||
static const struct onboard_hub_pdata microchip_usb424_data = {
|
||||
static const struct onboard_dev_pdata microchip_usb424_data = {
|
||||
.reset_us = 1,
|
||||
.num_supplies = 1,
|
||||
.supply_names = { "vdd" },
|
||||
.is_hub = true,
|
||||
};
|
||||
|
||||
static const struct onboard_hub_pdata microchip_usb5744_data = {
|
||||
static const struct onboard_dev_pdata microchip_usb5744_data = {
|
||||
.reset_us = 0,
|
||||
.num_supplies = 2,
|
||||
.supply_names = { "vdd", "vdd2" },
|
||||
.is_hub = true,
|
||||
};
|
||||
|
||||
static const struct onboard_hub_pdata realtek_rts5411_data = {
|
||||
static const struct onboard_dev_pdata realtek_rts5411_data = {
|
||||
.reset_us = 0,
|
||||
.num_supplies = 1,
|
||||
.supply_names = { "vdd" },
|
||||
.is_hub = true,
|
||||
};
|
||||
|
||||
static const struct onboard_hub_pdata ti_tusb8020b_data = {
|
||||
static const struct onboard_dev_pdata ti_tusb8020b_data = {
|
||||
.reset_us = 3000,
|
||||
.num_supplies = 1,
|
||||
.supply_names = { "vdd" },
|
||||
.is_hub = true,
|
||||
};
|
||||
|
||||
static const struct onboard_hub_pdata ti_tusb8041_data = {
|
||||
static const struct onboard_dev_pdata ti_tusb8041_data = {
|
||||
.reset_us = 3000,
|
||||
.num_supplies = 1,
|
||||
.supply_names = { "vdd" },
|
||||
.is_hub = true,
|
||||
};
|
||||
|
||||
static const struct onboard_hub_pdata cypress_hx3_data = {
|
||||
static const struct onboard_dev_pdata cypress_hx3_data = {
|
||||
.reset_us = 10000,
|
||||
.num_supplies = 2,
|
||||
.supply_names = { "vdd", "vdd2" },
|
||||
.is_hub = true,
|
||||
};
|
||||
|
||||
static const struct onboard_hub_pdata cypress_hx2vl_data = {
|
||||
static const struct onboard_dev_pdata cypress_hx2vl_data = {
|
||||
.reset_us = 1,
|
||||
.num_supplies = 1,
|
||||
.supply_names = { "vdd" },
|
||||
.is_hub = true,
|
||||
};
|
||||
|
||||
static const struct onboard_hub_pdata genesys_gl850g_data = {
|
||||
static const struct onboard_dev_pdata genesys_gl850g_data = {
|
||||
.reset_us = 3,
|
||||
.num_supplies = 1,
|
||||
.supply_names = { "vdd" },
|
||||
.is_hub = true,
|
||||
};
|
||||
|
||||
static const struct onboard_hub_pdata genesys_gl852g_data = {
|
||||
static const struct onboard_dev_pdata genesys_gl852g_data = {
|
||||
.reset_us = 50,
|
||||
.num_supplies = 1,
|
||||
.supply_names = { "vdd" },
|
||||
.is_hub = true,
|
||||
};
|
||||
|
||||
static const struct onboard_hub_pdata vialab_vl817_data = {
|
||||
static const struct onboard_dev_pdata vialab_vl817_data = {
|
||||
.reset_us = 10,
|
||||
.num_supplies = 1,
|
||||
.supply_names = { "vdd" },
|
||||
.is_hub = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id onboard_hub_match[] = {
|
||||
static const struct onboard_dev_pdata xmos_xvf3500_data = {
|
||||
.reset_us = 1,
|
||||
.num_supplies = 2,
|
||||
.supply_names = { "vdd", "vddio" },
|
||||
.is_hub = false,
|
||||
};
|
||||
|
||||
static const struct of_device_id onboard_dev_match[] = {
|
||||
{ .compatible = "usb424,2412", .data = µchip_usb424_data, },
|
||||
{ .compatible = "usb424,2514", .data = µchip_usb424_data, },
|
||||
{ .compatible = "usb424,2517", .data = µchip_usb424_data, },
|
||||
@ -84,7 +115,8 @@ static const struct of_device_id onboard_hub_match[] = {
|
||||
{ .compatible = "usbbda,5414", .data = &realtek_rts5411_data, },
|
||||
{ .compatible = "usb2109,817", .data = &vialab_vl817_data, },
|
||||
{ .compatible = "usb2109,2817", .data = &vialab_vl817_data, },
|
||||
{ .compatible = "usb20b1,0013", .data = &xmos_xvf3500_data, },
|
||||
{}
|
||||
};
|
||||
|
||||
#endif /* _USB_MISC_ONBOARD_USB_HUB_H */
|
||||
#endif /* _USB_MISC_ONBOARD_USB_DEV_H */
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* API for creating and destroying USB onboard hub platform devices
|
||||
* API for creating and destroying USB onboard platform devices
|
||||
*
|
||||
* Copyright (c) 2022, Google LLC
|
||||
*/
|
||||
@ -15,29 +15,30 @@
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/usb/of.h>
|
||||
#include <linux/usb/onboard_hub.h>
|
||||
#include <linux/usb/onboard_dev.h>
|
||||
|
||||
#include "onboard_usb_hub.h"
|
||||
#include "onboard_usb_dev.h"
|
||||
|
||||
struct pdev_list_entry {
|
||||
struct platform_device *pdev;
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
static bool of_is_onboard_usb_hub(const struct device_node *np)
|
||||
static bool of_is_onboard_usb_dev(struct device_node *np)
|
||||
{
|
||||
return !!of_match_node(onboard_hub_match, np);
|
||||
return !!of_match_node(onboard_dev_match, np);
|
||||
}
|
||||
|
||||
/**
|
||||
* onboard_hub_create_pdevs -- create platform devices for onboard USB hubs
|
||||
* @parent_hub : parent hub to scan for connected onboard hubs
|
||||
* @pdev_list : list of onboard hub platform devices owned by the parent hub
|
||||
* onboard_dev_create_pdevs -- create platform devices for onboard USB devices
|
||||
* @parent_hub : parent hub to scan for connected onboard devices
|
||||
* @pdev_list : list of onboard platform devices owned by the parent hub
|
||||
*
|
||||
* Creates a platform device for each supported onboard hub that is connected to
|
||||
* the given parent hub. The platform device is in charge of initializing the
|
||||
* hub (enable regulators, take the hub out of reset, ...) and can optionally
|
||||
* control whether the hub remains powered during system suspend or not.
|
||||
* Creates a platform device for each supported onboard device that is connected
|
||||
* to the given parent hub. The platform device is in charge of initializing the
|
||||
* device (enable regulators, take the device out of reset, ...). For onboard
|
||||
* hubs, it can optionally control whether the device remains powered during
|
||||
* system suspend or not.
|
||||
*
|
||||
* To keep track of the platform devices they are added to a list that is owned
|
||||
* by the parent hub.
|
||||
@ -50,9 +51,9 @@ static bool of_is_onboard_usb_hub(const struct device_node *np)
|
||||
* node. That means the root hubs of the primary and secondary HCD share the
|
||||
* same device tree node (the HCD node). As a result this function can be called
|
||||
* twice with the same DT node for root hubs. We only want to create a single
|
||||
* platform device for each physical onboard hub, hence for root hubs the loop
|
||||
* is only executed for the root hub of the primary HCD. Since the function
|
||||
* scans through all child nodes it still creates pdevs for onboard hubs
|
||||
* platform device for each physical onboard device, hence for root hubs the
|
||||
* loop is only executed for the root hub of the primary HCD. Since the function
|
||||
* scans through all child nodes it still creates pdevs for onboard devices
|
||||
* connected to the root hub of the secondary HCD if needed.
|
||||
*
|
||||
* Further there must be only one platform device for onboard hubs with a peer
|
||||
@ -63,7 +64,7 @@ static bool of_is_onboard_usb_hub(const struct device_node *np)
|
||||
* the function processes the nodes of both peers. A platform device is only
|
||||
* created if the peer hub doesn't have one already.
|
||||
*/
|
||||
void onboard_hub_create_pdevs(struct usb_device *parent_hub, struct list_head *pdev_list)
|
||||
void onboard_dev_create_pdevs(struct usb_device *parent_hub, struct list_head *pdev_list)
|
||||
{
|
||||
int i;
|
||||
struct usb_hcd *hcd = bus_to_hcd(parent_hub->bus);
|
||||
@ -82,7 +83,7 @@ void onboard_hub_create_pdevs(struct usb_device *parent_hub, struct list_head *p
|
||||
if (!np)
|
||||
continue;
|
||||
|
||||
if (!of_is_onboard_usb_hub(np))
|
||||
if (!of_is_onboard_usb_dev(np))
|
||||
goto node_put;
|
||||
|
||||
npc = of_parse_phandle(np, "peer-hub", 0);
|
||||
@ -104,7 +105,7 @@ void onboard_hub_create_pdevs(struct usb_device *parent_hub, struct list_head *p
|
||||
pdev = of_platform_device_create(np, NULL, &parent_hub->dev);
|
||||
if (!pdev) {
|
||||
dev_err(&parent_hub->dev,
|
||||
"failed to create platform device for onboard hub '%pOF'\n", np);
|
||||
"failed to create platform device for onboard dev '%pOF'\n", np);
|
||||
goto node_put;
|
||||
}
|
||||
|
||||
@ -121,16 +122,16 @@ node_put:
|
||||
of_node_put(np);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(onboard_hub_create_pdevs);
|
||||
EXPORT_SYMBOL_GPL(onboard_dev_create_pdevs);
|
||||
|
||||
/**
|
||||
* onboard_hub_destroy_pdevs -- free resources of onboard hub platform devices
|
||||
* @pdev_list : list of onboard hub platform devices
|
||||
* onboard_dev_destroy_pdevs -- free resources of onboard platform devices
|
||||
* @pdev_list : list of onboard platform devices
|
||||
*
|
||||
* Destroys the platform devices in the given list and frees the memory associated
|
||||
* with the list entry.
|
||||
*/
|
||||
void onboard_hub_destroy_pdevs(struct list_head *pdev_list)
|
||||
void onboard_dev_destroy_pdevs(struct list_head *pdev_list)
|
||||
{
|
||||
struct pdev_list_entry *pdle, *tmp;
|
||||
|
||||
@ -140,4 +141,4 @@ void onboard_hub_destroy_pdevs(struct list_head *pdev_list)
|
||||
kfree(pdle);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(onboard_hub_destroy_pdevs);
|
||||
EXPORT_SYMBOL_GPL(onboard_dev_destroy_pdevs);
|
@ -1,507 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Driver for onboard USB hubs
|
||||
*
|
||||
* Copyright (c) 2022, Google LLC
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/usb/onboard_hub.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include "onboard_usb_hub.h"
|
||||
|
||||
/*
|
||||
* Use generic names, as the actual names might differ between hubs. If a new
|
||||
* hub requires more than the currently supported supplies, add a new one here.
|
||||
*/
|
||||
static const char * const supply_names[] = {
|
||||
"vdd",
|
||||
"vdd2",
|
||||
};
|
||||
|
||||
#define MAX_SUPPLIES ARRAY_SIZE(supply_names)
|
||||
|
||||
static void onboard_hub_attach_usb_driver(struct work_struct *work);
|
||||
|
||||
static struct usb_device_driver onboard_hub_usbdev_driver;
|
||||
static DECLARE_WORK(attach_usb_driver_work, onboard_hub_attach_usb_driver);
|
||||
|
||||
/************************** Platform driver **************************/
|
||||
|
||||
struct usbdev_node {
|
||||
struct usb_device *udev;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct onboard_hub {
|
||||
struct regulator_bulk_data supplies[MAX_SUPPLIES];
|
||||
struct device *dev;
|
||||
const struct onboard_hub_pdata *pdata;
|
||||
struct gpio_desc *reset_gpio;
|
||||
bool always_powered_in_suspend;
|
||||
bool is_powered_on;
|
||||
bool going_away;
|
||||
struct list_head udev_list;
|
||||
struct mutex lock;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static int onboard_hub_power_on(struct onboard_hub *hub)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = clk_prepare_enable(hub->clk);
|
||||
if (err) {
|
||||
dev_err(hub->dev, "failed to enable clock: %pe\n", ERR_PTR(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
err = regulator_bulk_enable(hub->pdata->num_supplies, hub->supplies);
|
||||
if (err) {
|
||||
dev_err(hub->dev, "failed to enable supplies: %pe\n", ERR_PTR(err));
|
||||
goto disable_clk;
|
||||
}
|
||||
|
||||
fsleep(hub->pdata->reset_us);
|
||||
gpiod_set_value_cansleep(hub->reset_gpio, 0);
|
||||
|
||||
hub->is_powered_on = true;
|
||||
|
||||
return 0;
|
||||
|
||||
disable_clk:
|
||||
clk_disable_unprepare(hub->clk);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int onboard_hub_power_off(struct onboard_hub *hub)
|
||||
{
|
||||
int err;
|
||||
|
||||
gpiod_set_value_cansleep(hub->reset_gpio, 1);
|
||||
|
||||
err = regulator_bulk_disable(hub->pdata->num_supplies, hub->supplies);
|
||||
if (err) {
|
||||
dev_err(hub->dev, "failed to disable supplies: %pe\n", ERR_PTR(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
clk_disable_unprepare(hub->clk);
|
||||
|
||||
hub->is_powered_on = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused onboard_hub_suspend(struct device *dev)
|
||||
{
|
||||
struct onboard_hub *hub = dev_get_drvdata(dev);
|
||||
struct usbdev_node *node;
|
||||
bool power_off = true;
|
||||
|
||||
if (hub->always_powered_in_suspend)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&hub->lock);
|
||||
|
||||
list_for_each_entry(node, &hub->udev_list, list) {
|
||||
if (!device_may_wakeup(node->udev->bus->controller))
|
||||
continue;
|
||||
|
||||
if (usb_wakeup_enabled_descendants(node->udev)) {
|
||||
power_off = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&hub->lock);
|
||||
|
||||
if (!power_off)
|
||||
return 0;
|
||||
|
||||
return onboard_hub_power_off(hub);
|
||||
}
|
||||
|
||||
static int __maybe_unused onboard_hub_resume(struct device *dev)
|
||||
{
|
||||
struct onboard_hub *hub = dev_get_drvdata(dev);
|
||||
|
||||
if (hub->is_powered_on)
|
||||
return 0;
|
||||
|
||||
return onboard_hub_power_on(hub);
|
||||
}
|
||||
|
||||
static inline void get_udev_link_name(const struct usb_device *udev, char *buf, size_t size)
|
||||
{
|
||||
snprintf(buf, size, "usb_dev.%s", dev_name(&udev->dev));
|
||||
}
|
||||
|
||||
static int onboard_hub_add_usbdev(struct onboard_hub *hub, struct usb_device *udev)
|
||||
{
|
||||
struct usbdev_node *node;
|
||||
char link_name[64];
|
||||
int err;
|
||||
|
||||
mutex_lock(&hub->lock);
|
||||
|
||||
if (hub->going_away) {
|
||||
err = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
node = kzalloc(sizeof(*node), GFP_KERNEL);
|
||||
if (!node) {
|
||||
err = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
node->udev = udev;
|
||||
|
||||
list_add(&node->list, &hub->udev_list);
|
||||
|
||||
mutex_unlock(&hub->lock);
|
||||
|
||||
get_udev_link_name(udev, link_name, sizeof(link_name));
|
||||
WARN_ON(sysfs_create_link(&hub->dev->kobj, &udev->dev.kobj, link_name));
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
mutex_unlock(&hub->lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void onboard_hub_remove_usbdev(struct onboard_hub *hub, const struct usb_device *udev)
|
||||
{
|
||||
struct usbdev_node *node;
|
||||
char link_name[64];
|
||||
|
||||
get_udev_link_name(udev, link_name, sizeof(link_name));
|
||||
sysfs_remove_link(&hub->dev->kobj, link_name);
|
||||
|
||||
mutex_lock(&hub->lock);
|
||||
|
||||
list_for_each_entry(node, &hub->udev_list, list) {
|
||||
if (node->udev == udev) {
|
||||
list_del(&node->list);
|
||||
kfree(node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&hub->lock);
|
||||
}
|
||||
|
||||
static ssize_t always_powered_in_suspend_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
const struct onboard_hub *hub = dev_get_drvdata(dev);
|
||||
|
||||
return sysfs_emit(buf, "%d\n", hub->always_powered_in_suspend);
|
||||
}
|
||||
|
||||
static ssize_t always_powered_in_suspend_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct onboard_hub *hub = dev_get_drvdata(dev);
|
||||
bool val;
|
||||
int ret;
|
||||
|
||||
ret = kstrtobool(buf, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
hub->always_powered_in_suspend = val;
|
||||
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR_RW(always_powered_in_suspend);
|
||||
|
||||
static struct attribute *onboard_hub_attrs[] = {
|
||||
&dev_attr_always_powered_in_suspend.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(onboard_hub);
|
||||
|
||||
static void onboard_hub_attach_usb_driver(struct work_struct *work)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = driver_attach(&onboard_hub_usbdev_driver.driver);
|
||||
if (err)
|
||||
pr_err("Failed to attach USB driver: %pe\n", ERR_PTR(err));
|
||||
}
|
||||
|
||||
static int onboard_hub_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct onboard_hub *hub;
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
hub = devm_kzalloc(dev, sizeof(*hub), GFP_KERNEL);
|
||||
if (!hub)
|
||||
return -ENOMEM;
|
||||
|
||||
hub->pdata = device_get_match_data(dev);
|
||||
if (!hub->pdata)
|
||||
return -EINVAL;
|
||||
|
||||
if (hub->pdata->num_supplies > MAX_SUPPLIES)
|
||||
return dev_err_probe(dev, -EINVAL, "max %zu supplies supported!\n",
|
||||
MAX_SUPPLIES);
|
||||
|
||||
for (i = 0; i < hub->pdata->num_supplies; i++)
|
||||
hub->supplies[i].supply = supply_names[i];
|
||||
|
||||
err = devm_regulator_bulk_get(dev, hub->pdata->num_supplies, hub->supplies);
|
||||
if (err) {
|
||||
dev_err(dev, "Failed to get regulator supplies: %pe\n", ERR_PTR(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
hub->clk = devm_clk_get_optional(dev, NULL);
|
||||
if (IS_ERR(hub->clk))
|
||||
return dev_err_probe(dev, PTR_ERR(hub->clk), "failed to get clock\n");
|
||||
|
||||
hub->reset_gpio = devm_gpiod_get_optional(dev, "reset",
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(hub->reset_gpio))
|
||||
return dev_err_probe(dev, PTR_ERR(hub->reset_gpio), "failed to get reset GPIO\n");
|
||||
|
||||
hub->dev = dev;
|
||||
mutex_init(&hub->lock);
|
||||
INIT_LIST_HEAD(&hub->udev_list);
|
||||
|
||||
dev_set_drvdata(dev, hub);
|
||||
|
||||
err = onboard_hub_power_on(hub);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* The USB driver might have been detached from the USB devices by
|
||||
* onboard_hub_remove() (e.g. through an 'unbind' by userspace),
|
||||
* make sure to re-attach it if needed.
|
||||
*
|
||||
* This needs to be done deferred to avoid self-deadlocks on systems
|
||||
* with nested onboard hubs.
|
||||
*/
|
||||
schedule_work(&attach_usb_driver_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void onboard_hub_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct onboard_hub *hub = dev_get_drvdata(&pdev->dev);
|
||||
struct usbdev_node *node;
|
||||
struct usb_device *udev;
|
||||
|
||||
hub->going_away = true;
|
||||
|
||||
mutex_lock(&hub->lock);
|
||||
|
||||
/* unbind the USB devices to avoid dangling references to this device */
|
||||
while (!list_empty(&hub->udev_list)) {
|
||||
node = list_first_entry(&hub->udev_list, struct usbdev_node, list);
|
||||
udev = node->udev;
|
||||
|
||||
/*
|
||||
* Unbinding the driver will call onboard_hub_remove_usbdev(),
|
||||
* which acquires hub->lock. We must release the lock first.
|
||||
*/
|
||||
get_device(&udev->dev);
|
||||
mutex_unlock(&hub->lock);
|
||||
device_release_driver(&udev->dev);
|
||||
put_device(&udev->dev);
|
||||
mutex_lock(&hub->lock);
|
||||
}
|
||||
|
||||
mutex_unlock(&hub->lock);
|
||||
|
||||
onboard_hub_power_off(hub);
|
||||
}
|
||||
|
||||
MODULE_DEVICE_TABLE(of, onboard_hub_match);
|
||||
|
||||
static const struct dev_pm_ops __maybe_unused onboard_hub_pm_ops = {
|
||||
SET_LATE_SYSTEM_SLEEP_PM_OPS(onboard_hub_suspend, onboard_hub_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver onboard_hub_driver = {
|
||||
.probe = onboard_hub_probe,
|
||||
.remove_new = onboard_hub_remove,
|
||||
|
||||
.driver = {
|
||||
.name = "onboard-usb-hub",
|
||||
.of_match_table = onboard_hub_match,
|
||||
.pm = pm_ptr(&onboard_hub_pm_ops),
|
||||
.dev_groups = onboard_hub_groups,
|
||||
},
|
||||
};
|
||||
|
||||
/************************** USB driver **************************/
|
||||
|
||||
#define VENDOR_ID_CYPRESS 0x04b4
|
||||
#define VENDOR_ID_GENESYS 0x05e3
|
||||
#define VENDOR_ID_MICROCHIP 0x0424
|
||||
#define VENDOR_ID_REALTEK 0x0bda
|
||||
#define VENDOR_ID_TI 0x0451
|
||||
#define VENDOR_ID_VIA 0x2109
|
||||
|
||||
/*
|
||||
* Returns the onboard_hub platform device that is associated with the USB
|
||||
* device passed as parameter.
|
||||
*/
|
||||
static struct onboard_hub *_find_onboard_hub(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
struct device_node *np;
|
||||
struct onboard_hub *hub;
|
||||
|
||||
pdev = of_find_device_by_node(dev->of_node);
|
||||
if (!pdev) {
|
||||
np = of_parse_phandle(dev->of_node, "peer-hub", 0);
|
||||
if (!np) {
|
||||
dev_err(dev, "failed to find device node for peer hub\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
pdev = of_find_device_by_node(np);
|
||||
of_node_put(np);
|
||||
|
||||
if (!pdev)
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
hub = dev_get_drvdata(&pdev->dev);
|
||||
put_device(&pdev->dev);
|
||||
|
||||
/*
|
||||
* The presence of drvdata ('hub') indicates that the platform driver
|
||||
* finished probing. This handles the case where (conceivably) we could
|
||||
* be running at the exact same time as the platform driver's probe. If
|
||||
* we detect the race we request probe deferral and we'll come back and
|
||||
* try again.
|
||||
*/
|
||||
if (!hub)
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
|
||||
return hub;
|
||||
}
|
||||
|
||||
static int onboard_hub_usbdev_probe(struct usb_device *udev)
|
||||
{
|
||||
struct device *dev = &udev->dev;
|
||||
struct onboard_hub *hub;
|
||||
int err;
|
||||
|
||||
/* ignore supported hubs without device tree node */
|
||||
if (!dev->of_node)
|
||||
return -ENODEV;
|
||||
|
||||
hub = _find_onboard_hub(dev);
|
||||
if (IS_ERR(hub))
|
||||
return PTR_ERR(hub);
|
||||
|
||||
dev_set_drvdata(dev, hub);
|
||||
|
||||
err = onboard_hub_add_usbdev(hub, udev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void onboard_hub_usbdev_disconnect(struct usb_device *udev)
|
||||
{
|
||||
struct onboard_hub *hub = dev_get_drvdata(&udev->dev);
|
||||
|
||||
onboard_hub_remove_usbdev(hub, udev);
|
||||
}
|
||||
|
||||
static const struct usb_device_id onboard_hub_id_table[] = {
|
||||
{ USB_DEVICE(VENDOR_ID_CYPRESS, 0x6504) }, /* CYUSB33{0,1,2}x/CYUSB230x 3.0 */
|
||||
{ USB_DEVICE(VENDOR_ID_CYPRESS, 0x6506) }, /* CYUSB33{0,1,2}x/CYUSB230x 2.0 */
|
||||
{ USB_DEVICE(VENDOR_ID_CYPRESS, 0x6570) }, /* CY7C6563x 2.0 */
|
||||
{ USB_DEVICE(VENDOR_ID_GENESYS, 0x0608) }, /* Genesys Logic GL850G USB 2.0 */
|
||||
{ USB_DEVICE(VENDOR_ID_GENESYS, 0x0610) }, /* Genesys Logic GL852G USB 2.0 */
|
||||
{ USB_DEVICE(VENDOR_ID_GENESYS, 0x0620) }, /* Genesys Logic GL3523 USB 3.1 */
|
||||
{ USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2412) }, /* USB2412 USB 2.0 */
|
||||
{ USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2514) }, /* USB2514B USB 2.0 */
|
||||
{ USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2517) }, /* USB2517 USB 2.0 */
|
||||
{ USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2744) }, /* USB5744 USB 2.0 */
|
||||
{ USB_DEVICE(VENDOR_ID_MICROCHIP, 0x5744) }, /* USB5744 USB 3.0 */
|
||||
{ USB_DEVICE(VENDOR_ID_REALTEK, 0x0411) }, /* RTS5411 USB 3.1 */
|
||||
{ USB_DEVICE(VENDOR_ID_REALTEK, 0x5411) }, /* RTS5411 USB 2.1 */
|
||||
{ USB_DEVICE(VENDOR_ID_REALTEK, 0x0414) }, /* RTS5414 USB 3.2 */
|
||||
{ USB_DEVICE(VENDOR_ID_REALTEK, 0x5414) }, /* RTS5414 USB 2.1 */
|
||||
{ USB_DEVICE(VENDOR_ID_TI, 0x8025) }, /* TI USB8020B 3.0 */
|
||||
{ USB_DEVICE(VENDOR_ID_TI, 0x8027) }, /* TI USB8020B 2.0 */
|
||||
{ USB_DEVICE(VENDOR_ID_TI, 0x8140) }, /* TI USB8041 3.0 */
|
||||
{ USB_DEVICE(VENDOR_ID_TI, 0x8142) }, /* TI USB8041 2.0 */
|
||||
{ USB_DEVICE(VENDOR_ID_VIA, 0x0817) }, /* VIA VL817 3.1 */
|
||||
{ USB_DEVICE(VENDOR_ID_VIA, 0x2817) }, /* VIA VL817 2.0 */
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, onboard_hub_id_table);
|
||||
|
||||
static struct usb_device_driver onboard_hub_usbdev_driver = {
|
||||
.name = "onboard-usb-hub",
|
||||
.probe = onboard_hub_usbdev_probe,
|
||||
.disconnect = onboard_hub_usbdev_disconnect,
|
||||
.generic_subclass = 1,
|
||||
.supports_autosuspend = 1,
|
||||
.id_table = onboard_hub_id_table,
|
||||
};
|
||||
|
||||
static int __init onboard_hub_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = usb_register_device_driver(&onboard_hub_usbdev_driver, THIS_MODULE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = platform_driver_register(&onboard_hub_driver);
|
||||
if (ret)
|
||||
usb_deregister_device_driver(&onboard_hub_usbdev_driver);
|
||||
|
||||
return ret;
|
||||
}
|
||||
module_init(onboard_hub_init);
|
||||
|
||||
static void __exit onboard_hub_exit(void)
|
||||
{
|
||||
usb_deregister_device_driver(&onboard_hub_usbdev_driver);
|
||||
platform_driver_unregister(&onboard_hub_driver);
|
||||
|
||||
cancel_work_sync(&attach_usb_driver_work);
|
||||
}
|
||||
module_exit(onboard_hub_exit);
|
||||
|
||||
MODULE_AUTHOR("Matthias Kaehlcke <mka@chromium.org>");
|
||||
MODULE_DESCRIPTION("Driver for discrete onboard USB hubs");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -677,7 +677,7 @@ static int uss720_probe(struct usb_interface *intf,
|
||||
struct parport_uss720_private *priv;
|
||||
struct parport *pp;
|
||||
unsigned char reg;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
dev_dbg(&intf->dev, "probe: vendor id 0x%x, device id 0x%x\n",
|
||||
le16_to_cpu(usbdev->descriptor.idVendor),
|
||||
@ -688,12 +688,12 @@ static int uss720_probe(struct usb_interface *intf,
|
||||
usb_put_dev(usbdev);
|
||||
return -ENODEV;
|
||||
}
|
||||
i = usb_set_interface(usbdev, intf->altsetting->desc.bInterfaceNumber, 2);
|
||||
dev_dbg(&intf->dev, "set interface result %d\n", i);
|
||||
ret = usb_set_interface(usbdev, intf->altsetting->desc.bInterfaceNumber, 2);
|
||||
dev_dbg(&intf->dev, "set interface result %d\n", ret);
|
||||
|
||||
interface = intf->cur_altsetting;
|
||||
|
||||
if (interface->desc.bNumEndpoints < 3) {
|
||||
if (interface->desc.bNumEndpoints < 2) {
|
||||
usb_put_dev(usbdev);
|
||||
return -ENODEV;
|
||||
}
|
||||
@ -719,18 +719,27 @@ static int uss720_probe(struct usb_interface *intf,
|
||||
|
||||
priv->pp = pp;
|
||||
pp->private_data = priv;
|
||||
pp->modes = PARPORT_MODE_PCSPP | PARPORT_MODE_TRISTATE | PARPORT_MODE_EPP | PARPORT_MODE_ECP | PARPORT_MODE_COMPAT;
|
||||
pp->modes = PARPORT_MODE_PCSPP | PARPORT_MODE_TRISTATE | PARPORT_MODE_EPP | PARPORT_MODE_COMPAT;
|
||||
if (interface->desc.bNumEndpoints >= 3)
|
||||
pp->modes |= PARPORT_MODE_ECP;
|
||||
pp->dev = &usbdev->dev;
|
||||
|
||||
/* set the USS720 control register to manual mode, no ECP compression, enable all ints */
|
||||
set_1284_register(pp, 7, 0x00, GFP_KERNEL);
|
||||
set_1284_register(pp, 6, 0x30, GFP_KERNEL); /* PS/2 mode */
|
||||
set_1284_register(pp, 2, 0x0c, GFP_KERNEL);
|
||||
/* debugging */
|
||||
get_1284_register(pp, 0, ®, GFP_KERNEL);
|
||||
dev_dbg(&intf->dev, "reg: %7ph\n", priv->reg);
|
||||
|
||||
i = usb_find_last_int_in_endpoint(interface, &epd);
|
||||
if (!i) {
|
||||
/* The Belkin F5U002 Rev 2 P80453-B USB parallel port adapter shares the
|
||||
* device ID 050d:0002 with some other device that works with this
|
||||
* driver, but it itself does not. Detect and handle the bad cable
|
||||
* here. */
|
||||
ret = get_1284_register(pp, 0, ®, GFP_KERNEL);
|
||||
dev_dbg(&intf->dev, "reg: %7ph\n", priv->reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = usb_find_last_int_in_endpoint(interface, &epd);
|
||||
if (!ret) {
|
||||
dev_dbg(&intf->dev, "epaddr %d interval %d\n",
|
||||
epd->bEndpointAddress, epd->bInterval);
|
||||
}
|
||||
@ -766,14 +775,15 @@ static void uss720_disconnect(struct usb_interface *intf)
|
||||
|
||||
/* table of cables that work through this driver */
|
||||
static const struct usb_device_id uss720_table[] = {
|
||||
{ USB_DEVICE(0x047e, 0x1001) },
|
||||
{ USB_DEVICE(0x04b8, 0x0002) },
|
||||
{ USB_DEVICE(0x04b8, 0x0003) },
|
||||
{ USB_DEVICE(0x047e, 0x1001) }, /* Infowave 901-0030 */
|
||||
{ USB_DEVICE(0x04b8, 0x0002) }, /* Epson CAEUL0002 ISD-103 */
|
||||
{ USB_DEVICE(0x04b8, 0x0003) }, /* Epson ISD-101 */
|
||||
{ USB_DEVICE(0x050d, 0x0002) },
|
||||
{ USB_DEVICE(0x050d, 0x1202) },
|
||||
{ USB_DEVICE(0x050d, 0x1202) }, /* Belkin F5U120-PC */
|
||||
{ USB_DEVICE(0x0557, 0x2001) },
|
||||
{ USB_DEVICE(0x05ab, 0x0002) },
|
||||
{ USB_DEVICE(0x06c6, 0x0100) },
|
||||
{ USB_DEVICE(0x05ab, 0x0002) }, /* Belkin F5U002 ISD-101 */
|
||||
{ USB_DEVICE(0x05ab, 0x1001) }, /* Belkin F5U002 P80453-A */
|
||||
{ USB_DEVICE(0x06c6, 0x0100) }, /* Infowave ISD-103 */
|
||||
{ USB_DEVICE(0x0729, 0x1284) },
|
||||
{ USB_DEVICE(0x1293, 0x0002) },
|
||||
{ } /* Terminating entry */
|
||||
|
@ -1156,15 +1156,6 @@ void musb_free_request(struct usb_ep *ep, struct usb_request *req)
|
||||
kfree(request);
|
||||
}
|
||||
|
||||
static LIST_HEAD(buffers);
|
||||
|
||||
struct free_record {
|
||||
struct list_head list;
|
||||
struct device *dev;
|
||||
unsigned bytes;
|
||||
dma_addr_t dma;
|
||||
};
|
||||
|
||||
/*
|
||||
* Context: controller locked, IRQs blocked.
|
||||
*/
|
||||
|
@ -1005,7 +1005,6 @@ struct platform_driver fsl_otg_driver = {
|
||||
.remove_new = fsl_otg_remove,
|
||||
.driver = {
|
||||
.name = driver_name,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -71,6 +71,7 @@ static void nop_reset(struct usb_phy_generic *nop)
|
||||
gpiod_set_value_cansleep(nop->gpiod_reset, 1);
|
||||
usleep_range(10000, 20000);
|
||||
gpiod_set_value_cansleep(nop->gpiod_reset, 0);
|
||||
usleep_range(10000, 30000);
|
||||
}
|
||||
|
||||
/* interface to regulator framework */
|
||||
|
@ -363,14 +363,14 @@ static void usbhsc_clk_disable_unprepare(struct usbhs_priv *priv)
|
||||
* platform default param
|
||||
*/
|
||||
|
||||
/* commonly used on old SH-Mobile SoCs */
|
||||
/* commonly used on old SH-Mobile and RZ/G2L family SoCs */
|
||||
static struct renesas_usbhs_driver_pipe_config usbhsc_default_pipe[] = {
|
||||
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_CONTROL, 64, 0x00, false),
|
||||
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_ISOC, 1024, 0x08, false),
|
||||
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_ISOC, 1024, 0x18, false),
|
||||
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x28, true),
|
||||
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x38, true),
|
||||
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_ISOC, 1024, 0x08, true),
|
||||
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_ISOC, 1024, 0x28, true),
|
||||
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x48, true),
|
||||
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x58, true),
|
||||
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x68, true),
|
||||
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x04, false),
|
||||
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x05, false),
|
||||
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x06, false),
|
||||
@ -565,6 +565,18 @@ static const struct of_device_id usbhs_of_match[] = {
|
||||
.compatible = "renesas,usbhs-r8a77995",
|
||||
.data = &usbhs_rcar_gen3_with_pll_plat_info,
|
||||
},
|
||||
{
|
||||
.compatible = "renesas,usbhs-r9a07g043",
|
||||
.data = &usbhs_rzg2l_plat_info,
|
||||
},
|
||||
{
|
||||
.compatible = "renesas,usbhs-r9a07g044",
|
||||
.data = &usbhs_rzg2l_plat_info,
|
||||
},
|
||||
{
|
||||
.compatible = "renesas,usbhs-r9a07g054",
|
||||
.data = &usbhs_rzg2l_plat_info,
|
||||
},
|
||||
{
|
||||
.compatible = "renesas,rcar-gen2-usbhs",
|
||||
.data = &usbhs_rcar_gen2_plat_info,
|
||||
@ -581,7 +593,11 @@ static const struct of_device_id usbhs_of_match[] = {
|
||||
.compatible = "renesas,rza2-usbhs",
|
||||
.data = &usbhs_rza2_plat_info,
|
||||
},
|
||||
{ },
|
||||
{
|
||||
.compatible = "renesas,rzg2l-usbhs",
|
||||
.data = &usbhs_rzg2l_plat_info,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, usbhs_of_match);
|
||||
|
||||
@ -595,16 +611,11 @@ static int usbhs_probe(struct platform_device *pdev)
|
||||
u32 tmp;
|
||||
int irq;
|
||||
|
||||
/* check device node */
|
||||
if (dev_of_node(dev))
|
||||
info = of_device_get_match_data(dev);
|
||||
else
|
||||
info = renesas_usbhs_get_info(pdev);
|
||||
|
||||
/* check platform information */
|
||||
info = of_device_get_match_data(dev);
|
||||
if (!info) {
|
||||
dev_err(dev, "no platform information\n");
|
||||
return -EINVAL;
|
||||
info = dev_get_platdata(dev);
|
||||
if (!info)
|
||||
return dev_err_probe(dev, -EINVAL, "no platform info\n");
|
||||
}
|
||||
|
||||
/* platform data */
|
||||
|
@ -3,3 +3,4 @@
|
||||
|
||||
extern const struct renesas_usbhs_platform_info usbhs_rza1_plat_info;
|
||||
extern const struct renesas_usbhs_platform_info usbhs_rza2_plat_info;
|
||||
extern const struct renesas_usbhs_platform_info usbhs_rzg2l_plat_info;
|
||||
|
@ -71,3 +71,16 @@ const struct renesas_usbhs_platform_info usbhs_rza2_plat_info = {
|
||||
.has_new_pipe_configs = 1,
|
||||
},
|
||||
};
|
||||
|
||||
const struct renesas_usbhs_platform_info usbhs_rzg2l_plat_info = {
|
||||
.platform_callback = {
|
||||
.hardware_init = usbhs_rza2_hardware_init,
|
||||
.hardware_exit = usbhs_rza2_hardware_exit,
|
||||
.power_ctrl = usbhs_rza2_power_ctrl,
|
||||
.get_id = usbhs_get_id_as_gadget,
|
||||
},
|
||||
.driver_param = {
|
||||
.has_cnen = 1,
|
||||
.cfifo_byte_addr = 1,
|
||||
},
|
||||
};
|
||||
|
@ -802,7 +802,6 @@ static struct typec_altmode_driver dp_altmode_driver = {
|
||||
.remove = dp_altmode_remove,
|
||||
.driver = {
|
||||
.name = "typec_displayport",
|
||||
.owner = THIS_MODULE,
|
||||
.dev_groups = displayport_groups,
|
||||
},
|
||||
};
|
||||
|
@ -35,7 +35,6 @@ static struct typec_altmode_driver nvidia_altmode_driver = {
|
||||
.remove = nvidia_altmode_remove,
|
||||
.driver = {
|
||||
.name = "typec_nvidia",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
module_typec_altmode_driver(nvidia_altmode_driver);
|
||||
|
@ -60,7 +60,7 @@ config TYPEC_MUX_PTN36502
|
||||
tristate "NXP PTN36502 Type-C redriver driver"
|
||||
depends on I2C
|
||||
depends on DRM || DRM=n
|
||||
select DRM_PANEL_BRIDGE if DRM
|
||||
select DRM_AUX_BRIDGE if DRM_BRIDGE && OF
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Say Y or M if your system has a NXP PTN36502 Type-C redriver chip
|
||||
|
@ -48,10 +48,10 @@ static int gpio_sbu_switch_set(struct typec_switch_dev *sw,
|
||||
}
|
||||
|
||||
if (enabled != sbu_mux->enabled)
|
||||
gpiod_set_value(sbu_mux->enable_gpio, enabled);
|
||||
gpiod_set_value_cansleep(sbu_mux->enable_gpio, enabled);
|
||||
|
||||
if (swapped != sbu_mux->swapped)
|
||||
gpiod_set_value(sbu_mux->select_gpio, swapped);
|
||||
gpiod_set_value_cansleep(sbu_mux->select_gpio, swapped);
|
||||
|
||||
sbu_mux->enabled = enabled;
|
||||
sbu_mux->swapped = swapped;
|
||||
@ -82,7 +82,7 @@ static int gpio_sbu_mux_set(struct typec_mux_dev *mux,
|
||||
break;
|
||||
}
|
||||
|
||||
gpiod_set_value(sbu_mux->enable_gpio, sbu_mux->enabled);
|
||||
gpiod_set_value_cansleep(sbu_mux->enable_gpio, sbu_mux->enabled);
|
||||
|
||||
mutex_unlock(&sbu_mux->lock);
|
||||
|
||||
@ -141,7 +141,7 @@ static void gpio_sbu_mux_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct gpio_sbu_mux *sbu_mux = platform_get_drvdata(pdev);
|
||||
|
||||
gpiod_set_value(sbu_mux->enable_gpio, 0);
|
||||
gpiod_set_value_cansleep(sbu_mux->enable_gpio, 0);
|
||||
|
||||
typec_mux_unregister(sbu_mux->mux);
|
||||
typec_switch_unregister(sbu_mux->sw);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user