USB: changes for v5.7 merge window
Lots of changes on dwc3 this time, most of them from Thinh fixing a bunch of really old mishaps on the driver. DWC2 got support for STM32MP15 and a couple RockChip SoCs while DWC3 learned about Amlogic A1 family. Apart from these, we have a few spelling fixes and other minor non-critical fixes all over the place. Signed-off-by: Felipe Balbi <balbi@kernel.org> -----BEGIN PGP SIGNATURE----- iQJFBAABCAAvFiEElLzh7wn96CXwjh2IzL64meEamQYFAl5uL0YRHGJhbGJpQGtl cm5lbC5vcmcACgkQzL64meEamQYLSg//dnxrqey6J8hRGYdp9h5Dm0SQxhxp9KCW IyL7L287W2D9+JCtoXYrCOlvNM7vMJwwiI3O5XvuRbwllJPUC37HidrFwvOEhqDZ BV4J+A30U7OLBlikyzkY/m3I2F94E0DR6PdTsoNVcA3s6GTPs0oDUcKy8ZckDs9/ Fg21Y3GZplVdtCvbHU/q6Ou7iulm2lZlZg+RC/u3edmRycJ1S29BKxnL40C63hiD 8kd4rQ/679IIsmV6JAhUWJAS1/3XZG68ozyWEcKiu8kltyAv7L2ponmKA9jiP+1C 5+EPPvW7xyYb2vkY7N85jR+MjzsF5VK9O4LXPsoPQ/lDW9IZ83zZvOPJ9UuKQwzW iqaocn1SB+Ovx2lQZv3kjv6v6nKCzE7HfTVFQSk/ycV8aprXeOzItO+dJ4mPjsXy uX1hWePs5/9bG1Cr3apoVSf9x+6v4+MNY/qmxcyzrBkXpcmDan6SemkWNgtceVW/ RScdyHdhVNVU22hPblADP4oNTOjHRYpQR4TvS9KXcv7Qjb+alN44JcsYWyAGSHLa Mb3oEDsJ4lvzIaVL/p23ISIPa+aXWgN3VBrwtErUofYQCNEqLia8D/ofncTkHvKp 0aquwfTO+0UVeFvuPcD13TQ66RwfNPWcxMGz36hEzmJqcsKFTcS47QFS6I8KRd7M ab8AAFqizwo= =L9gx -----END PGP SIGNATURE----- Merge tag 'usb-for-v5.7' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next Felipe writes: USB: changes for v5.7 merge window Lots of changes on dwc3 this time, most of them from Thinh fixing a bunch of really old mishaps on the driver. DWC2 got support for STM32MP15 and a couple RockChip SoCs while DWC3 learned about Amlogic A1 family. Apart from these, we have a few spelling fixes and other minor non-critical fixes all over the place. Signed-off-by: Felipe Balbi <balbi@kernel.org> * tag 'usb-for-v5.7' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb: (41 commits) dt-bindings: usb: add documentation for aspeed usb-vhub ARM: dts: aspeed-g4: add vhub port and endpoint properties ARM: dts: aspeed-g5: add vhub port and endpoint properties ARM: dts: aspeed-g6: add usb functions usb: gadget: aspeed: add ast2600 vhub support usb: gadget: aspeed: read vhub properties from device tree usb: gadget: aspeed: support per-vhub usb descriptors usb: gadget: f_phonet: Replace zero-length array with flexible-array member usb: gadget: composite: Inform controller driver of self-powered usb: gadget: amd5536udc: fix spelling mistake "reserverd" -> "reserved" udc: s3c-hsudc: Silence warning about supplies during deferred probe usb: dwc2: Silence warning about supplies during deferred probe dt-bindings: usb: dwc2: add compatible property for rk3368 usb dt-bindings: usb: dwc2: add compatible property for rk3328 usb usb: gadget: add raw-gadget interface usb: dwc2: Implement set_selfpowered() usb: dwc3: qcom: Replace <linux/clk-provider.h> by <linux/of_clk.h> usb: dwc3: core: don't do suspend for device mode if already suspended usb: dwc3: Rework resets initialization to be more flexible usb: dwc3: Rework clock initialization to be more flexible ...
This commit is contained in:
commit
a8ab3e7629
|
@ -22,10 +22,14 @@ description: |
|
|||
The DWC3 Glue controls the PHY routing and power, an interrupt line is
|
||||
connected to the Glue to serve as OTG ID change detection.
|
||||
|
||||
The Amlogic A1 embeds a DWC3 USB IP Core configured for USB2 in
|
||||
host-only mode.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- amlogic,meson-g12a-usb-ctrl
|
||||
- amlogic,meson-a1-usb-ctrl
|
||||
|
||||
ranges: true
|
||||
|
||||
|
@ -84,6 +88,25 @@ required:
|
|||
- phys
|
||||
- dr_mode
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- amlogic,meson-a1-usb-ctrl
|
||||
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 3
|
||||
clock-names:
|
||||
items:
|
||||
- const: usb_ctrl
|
||||
- const: usb_bus
|
||||
- const: xtal_usb_ctrl
|
||||
required:
|
||||
- clock-names
|
||||
|
||||
examples:
|
||||
- |
|
||||
usb: usb@ffe09000 {
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
# Copyright (c) 2020 Facebook Inc.
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/usb/aspeed,usb-vhub.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: ASPEED USB 2.0 Virtual Hub Controller
|
||||
|
||||
maintainers:
|
||||
- Benjamin Herrenschmidt <benh@kernel.crashing.org>
|
||||
|
||||
description: |+
|
||||
The ASPEED USB 2.0 Virtual Hub Controller implements 1 set of USB Hub
|
||||
register and several sets of Device and Endpoint registers to support
|
||||
the Virtual Hub's downstream USB devices.
|
||||
|
||||
Supported number of devices and endpoints vary depending on hardware
|
||||
revisions. AST2400 and AST2500 Virtual Hub supports 5 downstream devices
|
||||
and 15 generic endpoints, while AST2600 Virtual Hub supports 7 downstream
|
||||
devices and 21 generic endpoints.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- aspeed,ast2400-usb-vhub
|
||||
- aspeed,ast2500-usb-vhub
|
||||
- aspeed,ast2600-usb-vhub
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
aspeed,vhub-downstream-ports:
|
||||
description: Number of downstream ports supported by the Virtual Hub
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32
|
||||
- default: 5
|
||||
minimum: 1
|
||||
maximum: 7
|
||||
|
||||
aspeed,vhub-generic-endpoints:
|
||||
description: Number of generic endpoints supported by the Virtual Hub
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32
|
||||
- default: 15
|
||||
minimum: 1
|
||||
maximum: 21
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- interrupts
|
||||
- aspeed,vhub-downstream-ports
|
||||
- aspeed,vhub-generic-endpoints
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/aspeed-clock.h>
|
||||
vhub: usb-vhub@1e6a0000 {
|
||||
compatible = "aspeed,ast2500-usb-vhub";
|
||||
reg = <0x1e6a0000 0x300>;
|
||||
interrupts = <5>;
|
||||
clocks = <&syscon ASPEED_CLK_GATE_USBPORT1CLK>;
|
||||
aspeed,vhub-downstream-ports = <5>;
|
||||
aspeed,vhub-generic-endpoints = <15>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_usb2ad_default>;
|
||||
};
|
|
@ -18,27 +18,15 @@ properties:
|
|||
- const: rockchip,rk3066-usb
|
||||
- const: snps,dwc2
|
||||
- items:
|
||||
- const: rockchip,px30-usb
|
||||
- const: rockchip,rk3066-usb
|
||||
- const: snps,dwc2
|
||||
- items:
|
||||
- const: rockchip,rk3036-usb
|
||||
- const: rockchip,rk3066-usb
|
||||
- const: snps,dwc2
|
||||
- items:
|
||||
- const: rockchip,rv1108-usb
|
||||
- const: rockchip,rk3066-usb
|
||||
- const: snps,dwc2
|
||||
- items:
|
||||
- const: rockchip,rk3188-usb
|
||||
- const: rockchip,rk3066-usb
|
||||
- const: snps,dwc2
|
||||
- items:
|
||||
- const: rockchip,rk3228-usb
|
||||
- const: rockchip,rk3066-usb
|
||||
- const: snps,dwc2
|
||||
- items:
|
||||
- const: rockchip,rk3288-usb
|
||||
- enum:
|
||||
- rockchip,px30-usb
|
||||
- rockchip,rk3036-usb
|
||||
- rockchip,rk3188-usb
|
||||
- rockchip,rk3228-usb
|
||||
- rockchip,rk3288-usb
|
||||
- rockchip,rk3328-usb
|
||||
- rockchip,rk3368-usb
|
||||
- rockchip,rv1108-usb
|
||||
- const: rockchip,rk3066-usb
|
||||
- const: snps,dwc2
|
||||
- const: lantiq,arx100-usb
|
||||
|
|
|
@ -7,7 +7,8 @@ Required properties:
|
|||
- compatible: must be "snps,dwc3"
|
||||
- reg : Address and length of the register set for the device
|
||||
- interrupts: Interrupts used by the dwc3 controller.
|
||||
- clock-names: should contain "ref", "bus_early", "suspend"
|
||||
- clock-names: list of clock names. Ideally should be "ref",
|
||||
"bus_early", "suspend" but may be less or more.
|
||||
- clocks: list of phandle and clock specifier pairs corresponding to
|
||||
entries in the clock-names property.
|
||||
|
||||
|
@ -36,7 +37,7 @@ Optional properties:
|
|||
- phys: from the *Generic PHY* bindings
|
||||
- phy-names: from the *Generic PHY* bindings; supported names are "usb2-phy"
|
||||
or "usb3-phy".
|
||||
- resets: a single pair of phandle and reset specifier
|
||||
- resets: set of phandle and reset specifier pairs
|
||||
- snps,usb2-lpm-disable: indicate if we don't want to enable USB2 HW LPM
|
||||
- snps,usb3_lpm_capable: determines if platform is USB3 LPM capable
|
||||
- snps,dis-start-transfer-quirk: when set, disable isoc START TRANSFER command
|
||||
|
@ -75,6 +76,8 @@ Optional properties:
|
|||
from P0 to P1/P2/P3 without delay.
|
||||
- snps,dis-tx-ipgap-linecheck-quirk: when set, disable u2mac linestate check
|
||||
during HS transmit.
|
||||
- snps,parkmode-disable-ss-quirk: when set, all SuperSpeed bus instances in
|
||||
park mode are disabled.
|
||||
- snps,dis_metastability_quirk: when set, disable metastability workaround.
|
||||
CAUTION: use only if you are absolutely sure of it.
|
||||
- snps,is-utmi-l1-suspend: true when DWC3 asserts output signal
|
||||
|
|
|
@ -35,6 +35,12 @@ Optional properties:
|
|||
the USB data role (USB host or USB device) for a given
|
||||
USB connector, such as Type-C, Type-B(micro).
|
||||
see connector/usb-connector.txt.
|
||||
- role-switch-default-mode: indicating if usb-role-switch is enabled, the
|
||||
device default operation mode of controller while usb
|
||||
role is USB_ROLE_NONE. Valid arguments are "host" and
|
||||
"peripheral". Defaults to "peripheral" if not
|
||||
specified.
|
||||
|
||||
|
||||
This is an attribute to a USB controller such as:
|
||||
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/usb/maxim,max3420-udc.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: MAXIM MAX3420/1 USB Peripheral Controller
|
||||
|
||||
maintainers:
|
||||
- Jassi Brar <jaswinder.singh@linaro.org>
|
||||
|
||||
description: |
|
||||
The controller provices USB2.0 compliant FullSpeed peripheral
|
||||
implementation over the SPI interface.
|
||||
|
||||
Specifications about the part can be found at:
|
||||
http://datasheets.maximintegrated.com/en/ds/MAX3420E.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- maxim,max3420-udc
|
||||
- maxim,max3421-udc
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
items:
|
||||
- description: usb irq from max3420
|
||||
- description: vbus detection irq
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: udc
|
||||
- const: vbus
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
spi-max-frequency:
|
||||
maximum: 26000000
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- interrupt-names
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
spi0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
udc@0 {
|
||||
compatible = "maxim,max3420-udc";
|
||||
reg = <0>;
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupts = <0 IRQ_TYPE_EDGE_FALLING>, <10 IRQ_TYPE_EDGE_BOTH>;
|
||||
interrupt-names = "udc", "vbus";
|
||||
spi-max-frequency = <12500000>;
|
||||
};
|
||||
};
|
|
@ -22,6 +22,7 @@ USB support
|
|||
misc_usbsevseg
|
||||
mtouchusb
|
||||
ohci
|
||||
raw-gadget
|
||||
usbip_protocol
|
||||
usbmon
|
||||
usb-serial
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
==============
|
||||
USB Raw Gadget
|
||||
==============
|
||||
|
||||
USB Raw Gadget is a kernel module that provides a userspace interface for
|
||||
the USB Gadget subsystem. Essentially it allows to emulate USB devices
|
||||
from userspace. Enabled with CONFIG_USB_RAW_GADGET. Raw Gadget is
|
||||
currently a strictly debugging feature and shouldn't be used in
|
||||
production, use GadgetFS instead.
|
||||
|
||||
Comparison to GadgetFS
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Raw Gadget is similar to GadgetFS, but provides a more low-level and
|
||||
direct access to the USB Gadget layer for the userspace. The key
|
||||
differences are:
|
||||
|
||||
1. Every USB request is passed to the userspace to get a response, while
|
||||
GadgetFS responds to some USB requests internally based on the provided
|
||||
descriptors. However note, that the UDC driver might respond to some
|
||||
requests on its own and never forward them to the Gadget layer.
|
||||
|
||||
2. GadgetFS performs some sanity checks on the provided USB descriptors,
|
||||
while Raw Gadget allows you to provide arbitrary data as responses to
|
||||
USB requests.
|
||||
|
||||
3. Raw Gadget provides a way to select a UDC device/driver to bind to,
|
||||
while GadgetFS currently binds to the first available UDC.
|
||||
|
||||
4. Raw Gadget uses predictable endpoint names (handles) across different
|
||||
UDCs (as long as UDCs have enough endpoints of each required transfer
|
||||
type).
|
||||
|
||||
5. Raw Gadget has ioctl-based interface instead of a filesystem-based one.
|
||||
|
||||
Userspace interface
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To create a Raw Gadget instance open /dev/raw-gadget. Multiple raw-gadget
|
||||
instances (bound to different UDCs) can be used at the same time. The
|
||||
interaction with the opened file happens through the ioctl() calls, see
|
||||
comments in include/uapi/linux/usb/raw_gadget.h for details.
|
||||
|
||||
The typical usage of Raw Gadget looks like:
|
||||
|
||||
1. Open Raw Gadget instance via /dev/raw-gadget.
|
||||
2. Initialize the instance via USB_RAW_IOCTL_INIT.
|
||||
3. Launch the instance with USB_RAW_IOCTL_RUN.
|
||||
4. In a loop issue USB_RAW_IOCTL_EVENT_FETCH calls to receive events from
|
||||
Raw Gadget and react to those depending on what kind of USB device
|
||||
needs to be emulated.
|
||||
|
||||
Potential future improvements
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
- Implement ioctl's for setting/clearing halt status on endpoints.
|
||||
|
||||
- Reporting more events (suspend, resume, etc.) through
|
||||
USB_RAW_IOCTL_EVENT_FETCH.
|
||||
|
||||
- Support O_NONBLOCK I/O.
|
|
@ -164,6 +164,8 @@
|
|||
reg = <0x1e6a0000 0x300>;
|
||||
interrupts = <5>;
|
||||
clocks = <&syscon ASPEED_CLK_GATE_USBPORT1CLK>;
|
||||
aspeed,vhub-downstream-ports = <5>;
|
||||
aspeed,vhub-generic-endpoints = <15>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_usb2d_default>;
|
||||
status = "disabled";
|
||||
|
|
|
@ -195,6 +195,8 @@
|
|||
reg = <0x1e6a0000 0x300>;
|
||||
interrupts = <5>;
|
||||
clocks = <&syscon ASPEED_CLK_GATE_USBPORT1CLK>;
|
||||
aspeed,vhub-downstream-ports = <5>;
|
||||
aspeed,vhub-generic-endpoints = <15>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_usb2ad_default>;
|
||||
status = "disabled";
|
||||
|
|
|
@ -1112,6 +1112,31 @@
|
|||
groups = "UART9";
|
||||
};
|
||||
|
||||
pinctrl_usb2ah_default: usb2ah_default {
|
||||
function = "USB2AH";
|
||||
groups = "USBA";
|
||||
};
|
||||
|
||||
pinctrl_usb2ad_default: usb2ad_default {
|
||||
function = "USB2AD";
|
||||
groups = "USBA";
|
||||
};
|
||||
|
||||
pinctrl_usb2bh_default: usb2bh_default {
|
||||
function = "USB2BH";
|
||||
groups = "USBB";
|
||||
};
|
||||
|
||||
pinctrl_usb2bd_default: usb2bd_default {
|
||||
function = "USB2BD";
|
||||
groups = "USBB";
|
||||
};
|
||||
|
||||
pinctrl_usb11bhid_default: usb11bhid_default {
|
||||
function = "USB11BHID";
|
||||
groups = "USBB";
|
||||
};
|
||||
|
||||
pinctrl_vb_default: vb_default {
|
||||
function = "VB";
|
||||
groups = "VB";
|
||||
|
|
|
@ -245,6 +245,51 @@
|
|||
status = "disabled";
|
||||
};
|
||||
|
||||
ehci0: usb@1e6a1000 {
|
||||
compatible = "aspeed,ast2600-ehci", "generic-ehci";
|
||||
reg = <0x1e6a1000 0x100>;
|
||||
interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&syscon ASPEED_CLK_GATE_USBPORT1CLK>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_usb2ah_default>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
ehci1: usb@1e6a3000 {
|
||||
compatible = "aspeed,ast2600-ehci", "generic-ehci";
|
||||
reg = <0x1e6a3000 0x100>;
|
||||
interrupts = <GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&syscon ASPEED_CLK_GATE_USBPORT2CLK>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_usb2bh_default>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
uhci: usb@1e6b0000 {
|
||||
compatible = "aspeed,ast2600-uhci", "generic-uhci";
|
||||
reg = <0x1e6b0000 0x100>;
|
||||
interrupts = <GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>;
|
||||
#ports = <2>;
|
||||
clocks = <&syscon ASPEED_CLK_GATE_USBUHCICLK>;
|
||||
status = "disabled";
|
||||
/*
|
||||
* No default pinmux, it will follow EHCI, use an
|
||||
* explicit pinmux override if EHCI is not enabled.
|
||||
*/
|
||||
};
|
||||
|
||||
vhub: usb-vhub@1e6a0000 {
|
||||
compatible = "aspeed,ast2600-usb-vhub";
|
||||
reg = <0x1e6a0000 0x350>;
|
||||
interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&syscon ASPEED_CLK_GATE_USBPORT1CLK>;
|
||||
aspeed,vhub-downstream-ports = <7>;
|
||||
aspeed,vhub-generic-endpoints = <21>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_usb2ad_default>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
apb {
|
||||
compatible = "simple-bus";
|
||||
#address-cells = <1>;
|
||||
|
|
|
@ -411,6 +411,10 @@ enum dwc2_ep0_state {
|
|||
* register.
|
||||
* 0 - Deactivate the transceiver (default)
|
||||
* 1 - Activate the transceiver
|
||||
* @activate_stm_id_vb_detection: Activate external ID pin and Vbus level
|
||||
* detection using GGPIO register.
|
||||
* 0 - Deactivate the external level detection (default)
|
||||
* 1 - Activate the external level detection
|
||||
* @g_dma: Enables gadget dma usage (default: autodetect).
|
||||
* @g_dma_desc: Enables gadget descriptor DMA (default: autodetect).
|
||||
* @g_rx_fifo_size: The periodic rx fifo size for the device, in
|
||||
|
@ -481,6 +485,7 @@ struct dwc2_core_params {
|
|||
bool service_interval;
|
||||
u8 hird_threshold;
|
||||
bool activate_stm_fs_transceiver;
|
||||
bool activate_stm_id_vb_detection;
|
||||
bool ipg_isoc_en;
|
||||
u16 max_packet_count;
|
||||
u32 max_transfer_size;
|
||||
|
@ -874,6 +879,8 @@ struct dwc2_hregs_backup {
|
|||
* removed once all SoCs support usb transceiver.
|
||||
* @supplies: Definition of USB power supplies
|
||||
* @vbus_supply: Regulator supplying vbus.
|
||||
* @usb33d: Optional 3.3v regulator used on some stm32 devices to
|
||||
* supply ID and VBUS detection hardware.
|
||||
* @lock: Spinlock that protects all the driver data structures
|
||||
* @priv: Stores a pointer to the struct usb_hcd
|
||||
* @queuing_high_bandwidth: True if multiple packets of a high-bandwidth
|
||||
|
@ -1061,6 +1068,7 @@ struct dwc2_hsotg {
|
|||
struct dwc2_hsotg_plat *plat;
|
||||
struct regulator_bulk_data supplies[DWC2_NUM_SUPPLIES];
|
||||
struct regulator *vbus_supply;
|
||||
struct regulator *usb33d;
|
||||
|
||||
spinlock_t lock;
|
||||
void *priv;
|
||||
|
|
|
@ -1646,7 +1646,8 @@ static int dwc2_hsotg_process_req_status(struct dwc2_hsotg *hsotg,
|
|||
|
||||
switch (ctrl->bRequestType & USB_RECIP_MASK) {
|
||||
case USB_RECIP_DEVICE:
|
||||
status = 1 << USB_DEVICE_SELF_POWERED;
|
||||
status = hsotg->gadget.is_selfpowered <<
|
||||
USB_DEVICE_SELF_POWERED;
|
||||
status |= hsotg->remote_wakeup_allowed <<
|
||||
USB_DEVICE_REMOTE_WAKEUP;
|
||||
reply = cpu_to_le16(status);
|
||||
|
@ -4527,6 +4528,26 @@ static int dwc2_hsotg_gadget_getframe(struct usb_gadget *gadget)
|
|||
return dwc2_hsotg_read_frameno(to_hsotg(gadget));
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_hsotg_set_selfpowered - set if device is self/bus powered
|
||||
* @gadget: The usb gadget state
|
||||
* @is_selfpowered: Whether the device is self-powered
|
||||
*
|
||||
* Set if the device is self or bus powered.
|
||||
*/
|
||||
static int dwc2_hsotg_set_selfpowered(struct usb_gadget *gadget,
|
||||
int is_selfpowered)
|
||||
{
|
||||
struct dwc2_hsotg *hsotg = to_hsotg(gadget);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
gadget->is_selfpowered = !!is_selfpowered;
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_hsotg_pullup - connect/disconnect the USB PHY
|
||||
* @gadget: The usb gadget state
|
||||
|
@ -4618,6 +4639,7 @@ static int dwc2_hsotg_vbus_draw(struct usb_gadget *gadget, unsigned int mA)
|
|||
|
||||
static const struct usb_gadget_ops dwc2_hsotg_gadget_ops = {
|
||||
.get_frame = dwc2_hsotg_gadget_getframe,
|
||||
.set_selfpowered = dwc2_hsotg_set_selfpowered,
|
||||
.udc_start = dwc2_hsotg_udc_start,
|
||||
.udc_stop = dwc2_hsotg_udc_stop,
|
||||
.pullup = dwc2_hsotg_pullup,
|
||||
|
|
|
@ -54,6 +54,12 @@
|
|||
#define GOTGCTL_HSTSETHNPEN BIT(10)
|
||||
#define GOTGCTL_HNPREQ BIT(9)
|
||||
#define GOTGCTL_HSTNEGSCS BIT(8)
|
||||
#define GOTGCTL_BVALOVAL BIT(7)
|
||||
#define GOTGCTL_BVALOEN BIT(6)
|
||||
#define GOTGCTL_AVALOVAL BIT(5)
|
||||
#define GOTGCTL_AVALOEN BIT(4)
|
||||
#define GOTGCTL_VBVALOVAL BIT(3)
|
||||
#define GOTGCTL_VBVALOEN BIT(2)
|
||||
#define GOTGCTL_SESREQ BIT(1)
|
||||
#define GOTGCTL_SESREQSCS BIT(0)
|
||||
|
||||
|
@ -227,6 +233,8 @@
|
|||
#define GPVNDCTL HSOTG_REG(0x0034)
|
||||
#define GGPIO HSOTG_REG(0x0038)
|
||||
#define GGPIO_STM32_OTG_GCCFG_PWRDWN BIT(16)
|
||||
#define GGPIO_STM32_OTG_GCCFG_VBDEN BIT(21)
|
||||
#define GGPIO_STM32_OTG_GCCFG_IDEN BIT(22)
|
||||
|
||||
#define GUID HSOTG_REG(0x003c)
|
||||
#define GSNPSID HSOTG_REG(0x0040)
|
||||
|
|
|
@ -163,6 +163,35 @@ static void dwc2_set_stm32f7_hsotg_params(struct dwc2_hsotg *hsotg)
|
|||
p->host_perio_tx_fifo_size = 256;
|
||||
}
|
||||
|
||||
static void dwc2_set_stm32mp15_fsotg_params(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct dwc2_core_params *p = &hsotg->params;
|
||||
|
||||
p->otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE;
|
||||
p->speed = DWC2_SPEED_PARAM_FULL;
|
||||
p->host_rx_fifo_size = 128;
|
||||
p->host_nperio_tx_fifo_size = 96;
|
||||
p->host_perio_tx_fifo_size = 96;
|
||||
p->max_packet_count = 256;
|
||||
p->phy_type = DWC2_PHY_TYPE_PARAM_FS;
|
||||
p->i2c_enable = false;
|
||||
p->activate_stm_fs_transceiver = true;
|
||||
p->activate_stm_id_vb_detection = true;
|
||||
p->power_down = DWC2_POWER_DOWN_PARAM_NONE;
|
||||
}
|
||||
|
||||
static void dwc2_set_stm32mp15_hsotg_params(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct dwc2_core_params *p = &hsotg->params;
|
||||
|
||||
p->otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE;
|
||||
p->activate_stm_id_vb_detection = true;
|
||||
p->host_rx_fifo_size = 440;
|
||||
p->host_nperio_tx_fifo_size = 256;
|
||||
p->host_perio_tx_fifo_size = 256;
|
||||
p->power_down = DWC2_POWER_DOWN_PARAM_NONE;
|
||||
}
|
||||
|
||||
const struct of_device_id dwc2_of_match_table[] = {
|
||||
{ .compatible = "brcm,bcm2835-usb", .data = dwc2_set_bcm_params },
|
||||
{ .compatible = "hisilicon,hi6220-usb", .data = dwc2_set_his_params },
|
||||
|
@ -186,6 +215,10 @@ const struct of_device_id dwc2_of_match_table[] = {
|
|||
{ .compatible = "st,stm32f4x9-hsotg" },
|
||||
{ .compatible = "st,stm32f7-hsotg",
|
||||
.data = dwc2_set_stm32f7_hsotg_params },
|
||||
{ .compatible = "st,stm32mp15-fsotg",
|
||||
.data = dwc2_set_stm32mp15_fsotg_params },
|
||||
{ .compatible = "st,stm32mp15-hsotg",
|
||||
.data = dwc2_set_stm32mp15_hsotg_params },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dwc2_of_match_table);
|
||||
|
|
|
@ -285,7 +285,9 @@ static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg)
|
|||
ret = devm_regulator_bulk_get(hsotg->dev, ARRAY_SIZE(hsotg->supplies),
|
||||
hsotg->supplies);
|
||||
if (ret) {
|
||||
dev_err(hsotg->dev, "failed to request supplies: %d\n", ret);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(hsotg->dev, "failed to request supplies: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
|
@ -312,6 +314,9 @@ static int dwc2_driver_remove(struct platform_device *dev)
|
|||
if (hsotg->gadget_enabled)
|
||||
dwc2_hsotg_remove(hsotg);
|
||||
|
||||
if (hsotg->params.activate_stm_id_vb_detection)
|
||||
regulator_disable(hsotg->usb33d);
|
||||
|
||||
if (hsotg->ll_hw_enabled)
|
||||
dwc2_lowlevel_hw_disable(hsotg);
|
||||
|
||||
|
@ -464,10 +469,35 @@ static int dwc2_driver_probe(struct platform_device *dev)
|
|||
if (retval)
|
||||
goto error;
|
||||
|
||||
if (hsotg->params.activate_stm_id_vb_detection) {
|
||||
u32 ggpio;
|
||||
|
||||
hsotg->usb33d = devm_regulator_get(hsotg->dev, "usb33d");
|
||||
if (IS_ERR(hsotg->usb33d)) {
|
||||
retval = PTR_ERR(hsotg->usb33d);
|
||||
if (retval != -EPROBE_DEFER)
|
||||
dev_err(hsotg->dev,
|
||||
"failed to request usb33d supply: %d\n",
|
||||
retval);
|
||||
goto error;
|
||||
}
|
||||
retval = regulator_enable(hsotg->usb33d);
|
||||
if (retval) {
|
||||
dev_err(hsotg->dev,
|
||||
"failed to enable usb33d supply: %d\n", retval);
|
||||
goto error;
|
||||
}
|
||||
|
||||
ggpio = dwc2_readl(hsotg, GGPIO);
|
||||
ggpio |= GGPIO_STM32_OTG_GCCFG_IDEN;
|
||||
ggpio |= GGPIO_STM32_OTG_GCCFG_VBDEN;
|
||||
dwc2_writel(hsotg, ggpio, GGPIO);
|
||||
}
|
||||
|
||||
if (hsotg->dr_mode != USB_DR_MODE_HOST) {
|
||||
retval = dwc2_gadget_init(hsotg);
|
||||
if (retval)
|
||||
goto error;
|
||||
goto error_init;
|
||||
hsotg->gadget_enabled = 1;
|
||||
}
|
||||
|
||||
|
@ -493,7 +523,7 @@ static int dwc2_driver_probe(struct platform_device *dev)
|
|||
if (retval) {
|
||||
if (hsotg->gadget_enabled)
|
||||
dwc2_hsotg_remove(hsotg);
|
||||
goto error;
|
||||
goto error_init;
|
||||
}
|
||||
hsotg->hcd_enabled = 1;
|
||||
}
|
||||
|
@ -509,6 +539,9 @@ static int dwc2_driver_probe(struct platform_device *dev)
|
|||
|
||||
return 0;
|
||||
|
||||
error_init:
|
||||
if (hsotg->params.activate_stm_id_vb_detection)
|
||||
regulator_disable(hsotg->usb33d);
|
||||
error:
|
||||
dwc2_lowlevel_hw_disable(hsotg);
|
||||
return retval;
|
||||
|
@ -523,6 +556,37 @@ static int __maybe_unused dwc2_suspend(struct device *dev)
|
|||
if (is_device_mode)
|
||||
dwc2_hsotg_suspend(dwc2);
|
||||
|
||||
if (dwc2->params.activate_stm_id_vb_detection) {
|
||||
unsigned long flags;
|
||||
u32 ggpio, gotgctl;
|
||||
|
||||
/*
|
||||
* Need to force the mode to the current mode to avoid Mode
|
||||
* Mismatch Interrupt when ID detection will be disabled.
|
||||
*/
|
||||
dwc2_force_mode(dwc2, !is_device_mode);
|
||||
|
||||
spin_lock_irqsave(&dwc2->lock, flags);
|
||||
gotgctl = dwc2_readl(dwc2, GOTGCTL);
|
||||
/* bypass debounce filter, enable overrides */
|
||||
gotgctl |= GOTGCTL_DBNCE_FLTR_BYPASS;
|
||||
gotgctl |= GOTGCTL_BVALOEN | GOTGCTL_AVALOEN;
|
||||
/* Force A / B session if needed */
|
||||
if (gotgctl & GOTGCTL_ASESVLD)
|
||||
gotgctl |= GOTGCTL_AVALOVAL;
|
||||
if (gotgctl & GOTGCTL_BSESVLD)
|
||||
gotgctl |= GOTGCTL_BVALOVAL;
|
||||
dwc2_writel(dwc2, gotgctl, GOTGCTL);
|
||||
spin_unlock_irqrestore(&dwc2->lock, flags);
|
||||
|
||||
ggpio = dwc2_readl(dwc2, GGPIO);
|
||||
ggpio &= ~GGPIO_STM32_OTG_GCCFG_IDEN;
|
||||
ggpio &= ~GGPIO_STM32_OTG_GCCFG_VBDEN;
|
||||
dwc2_writel(dwc2, ggpio, GGPIO);
|
||||
|
||||
regulator_disable(dwc2->usb33d);
|
||||
}
|
||||
|
||||
if (dwc2->ll_hw_enabled &&
|
||||
(is_device_mode || dwc2_host_can_poweroff_phy(dwc2))) {
|
||||
ret = __dwc2_lowlevel_hw_disable(dwc2);
|
||||
|
@ -544,6 +608,34 @@ static int __maybe_unused dwc2_resume(struct device *dev)
|
|||
}
|
||||
dwc2->phy_off_for_suspend = false;
|
||||
|
||||
if (dwc2->params.activate_stm_id_vb_detection) {
|
||||
unsigned long flags;
|
||||
u32 ggpio, gotgctl;
|
||||
|
||||
ret = regulator_enable(dwc2->usb33d);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ggpio = dwc2_readl(dwc2, GGPIO);
|
||||
ggpio |= GGPIO_STM32_OTG_GCCFG_IDEN;
|
||||
ggpio |= GGPIO_STM32_OTG_GCCFG_VBDEN;
|
||||
dwc2_writel(dwc2, ggpio, GGPIO);
|
||||
|
||||
/* ID/VBUS detection startup time */
|
||||
usleep_range(5000, 7000);
|
||||
|
||||
spin_lock_irqsave(&dwc2->lock, flags);
|
||||
gotgctl = dwc2_readl(dwc2, GOTGCTL);
|
||||
gotgctl &= ~GOTGCTL_DBNCE_FLTR_BYPASS;
|
||||
gotgctl &= ~(GOTGCTL_BVALOEN | GOTGCTL_AVALOEN |
|
||||
GOTGCTL_BVALOVAL | GOTGCTL_AVALOVAL);
|
||||
dwc2_writel(dwc2, gotgctl, GOTGCTL);
|
||||
spin_unlock_irqrestore(&dwc2->lock, flags);
|
||||
}
|
||||
|
||||
/* Need to restore FORCEDEVMODE/FORCEHOSTMODE */
|
||||
dwc2_force_dr_mode(dwc2);
|
||||
|
||||
if (dwc2_is_device_mode(dwc2))
|
||||
ret = dwc2_hsotg_resume(dwc2);
|
||||
|
||||
|
|
|
@ -289,12 +289,6 @@ done:
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_bulk_data dwc3_core_clks[] = {
|
||||
{ .id = "ref" },
|
||||
{ .id = "bus_early" },
|
||||
{ .id = "suspend" },
|
||||
};
|
||||
|
||||
/*
|
||||
* dwc3_frame_length_adjustment - Adjusts frame length if required
|
||||
* @dwc3: Pointer to our controller context structure
|
||||
|
@ -1029,6 +1023,9 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
|||
if (dwc->dis_tx_ipgap_linecheck_quirk)
|
||||
reg |= DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS;
|
||||
|
||||
if (dwc->parkmode_disable_ss_quirk)
|
||||
reg |= DWC3_GUCTL1_PARKMODE_DISABLE_SS;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
|
||||
}
|
||||
|
||||
|
@ -1342,6 +1339,8 @@ static void dwc3_get_properties(struct dwc3 *dwc)
|
|||
"snps,dis-del-phy-power-chg-quirk");
|
||||
dwc->dis_tx_ipgap_linecheck_quirk = device_property_read_bool(dev,
|
||||
"snps,dis-tx-ipgap-linecheck-quirk");
|
||||
dwc->parkmode_disable_ss_quirk = device_property_read_bool(dev,
|
||||
"snps,parkmode-disable-ss-quirk");
|
||||
|
||||
dwc->tx_de_emphasis_quirk = device_property_read_bool(dev,
|
||||
"snps,tx_de_emphasis_quirk");
|
||||
|
@ -1441,11 +1440,6 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
if (!dwc)
|
||||
return -ENOMEM;
|
||||
|
||||
dwc->clks = devm_kmemdup(dev, dwc3_core_clks, sizeof(dwc3_core_clks),
|
||||
GFP_KERNEL);
|
||||
if (!dwc->clks)
|
||||
return -ENOMEM;
|
||||
|
||||
dwc->dev = dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
@ -1476,22 +1470,23 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
|
||||
dwc3_get_properties(dwc);
|
||||
|
||||
dwc->reset = devm_reset_control_get_optional_shared(dev, NULL);
|
||||
dwc->reset = devm_reset_control_array_get(dev, true, true);
|
||||
if (IS_ERR(dwc->reset))
|
||||
return PTR_ERR(dwc->reset);
|
||||
|
||||
if (dev->of_node) {
|
||||
dwc->num_clks = ARRAY_SIZE(dwc3_core_clks);
|
||||
|
||||
ret = devm_clk_bulk_get(dev, dwc->num_clks, dwc->clks);
|
||||
ret = devm_clk_bulk_get_all(dev, &dwc->clks);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
return ret;
|
||||
/*
|
||||
* Clocks are optional, but new DT platforms should support all
|
||||
* clocks as required by the DT-binding.
|
||||
*/
|
||||
if (ret)
|
||||
if (ret < 0)
|
||||
dwc->num_clks = 0;
|
||||
else
|
||||
dwc->num_clks = ret;
|
||||
|
||||
}
|
||||
|
||||
ret = reset_control_deassert(dwc->reset);
|
||||
|
@ -1637,6 +1632,8 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
|
|||
|
||||
switch (dwc->current_dr_role) {
|
||||
case DWC3_GCTL_PRTCAP_DEVICE:
|
||||
if (pm_runtime_suspended(dwc->dev))
|
||||
break;
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
dwc3_gadget_suspend(dwc);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/usb/role.h>
|
||||
#include <linux/ulpi/interface.h>
|
||||
|
||||
#include <linux/phy/phy.h>
|
||||
|
@ -249,6 +250,7 @@
|
|||
#define DWC3_GUCTL_HSTINAUTORETRY BIT(14)
|
||||
|
||||
/* Global User Control 1 Register */
|
||||
#define DWC3_GUCTL1_PARKMODE_DISABLE_SS BIT(17)
|
||||
#define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS BIT(28)
|
||||
#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24)
|
||||
|
||||
|
@ -953,6 +955,9 @@ struct dwc3_scratchpad_array {
|
|||
* @hsphy_mode: UTMI phy mode, one of following:
|
||||
* - USBPHY_INTERFACE_MODE_UTMI
|
||||
* - USBPHY_INTERFACE_MODE_UTMIW
|
||||
* @role_sw: usb_role_switch handle
|
||||
* @role_switch_default_mode: default operation mode of controller while
|
||||
* usb role is USB_ROLE_NONE.
|
||||
* @usb2_phy: pointer to USB2 PHY
|
||||
* @usb3_phy: pointer to USB3 PHY
|
||||
* @usb2_generic_phy: pointer to USB2 PHY
|
||||
|
@ -1024,6 +1029,8 @@ struct dwc3_scratchpad_array {
|
|||
* change quirk.
|
||||
* @dis_tx_ipgap_linecheck_quirk: set if we disable u2mac linestate
|
||||
* check during HS transmit.
|
||||
* @parkmode_disable_ss_quirk: set if we need to disable all SuperSpeed
|
||||
* instances in park mode.
|
||||
* @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk
|
||||
* @tx_de_emphasis: Tx de-emphasis value
|
||||
* 0 - -6dB de-emphasis
|
||||
|
@ -1086,6 +1093,8 @@ struct dwc3 {
|
|||
struct extcon_dev *edev;
|
||||
struct notifier_block edev_nb;
|
||||
enum usb_phy_interface hsphy_mode;
|
||||
struct usb_role_switch *role_sw;
|
||||
enum usb_dr_mode role_switch_default_mode;
|
||||
|
||||
u32 fladj;
|
||||
u32 irq_gadget;
|
||||
|
@ -1215,6 +1224,7 @@ struct dwc3 {
|
|||
unsigned dis_u2_freeclk_exists_quirk:1;
|
||||
unsigned dis_del_phy_power_chg_quirk:1;
|
||||
unsigned dis_tx_ipgap_linecheck_quirk:1;
|
||||
unsigned parkmode_disable_ss_quirk:1;
|
||||
|
||||
unsigned tx_de_emphasis_quirk:1;
|
||||
unsigned tx_de_emphasis:2;
|
||||
|
|
|
@ -476,6 +476,92 @@ static struct extcon_dev *dwc3_get_extcon(struct dwc3 *dwc)
|
|||
return edev;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_ROLE_SWITCH)
|
||||
#define ROLE_SWITCH 1
|
||||
static int dwc3_usb_role_switch_set(struct device *dev, enum usb_role role)
|
||||
{
|
||||
struct dwc3 *dwc = dev_get_drvdata(dev);
|
||||
u32 mode;
|
||||
|
||||
switch (role) {
|
||||
case USB_ROLE_HOST:
|
||||
mode = DWC3_GCTL_PRTCAP_HOST;
|
||||
break;
|
||||
case USB_ROLE_DEVICE:
|
||||
mode = DWC3_GCTL_PRTCAP_DEVICE;
|
||||
break;
|
||||
default:
|
||||
if (dwc->role_switch_default_mode == USB_DR_MODE_HOST)
|
||||
mode = DWC3_GCTL_PRTCAP_HOST;
|
||||
else
|
||||
mode = DWC3_GCTL_PRTCAP_DEVICE;
|
||||
break;
|
||||
}
|
||||
|
||||
dwc3_set_mode(dwc, mode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum usb_role dwc3_usb_role_switch_get(struct device *dev)
|
||||
{
|
||||
struct dwc3 *dwc = dev_get_drvdata(dev);
|
||||
unsigned long flags;
|
||||
enum usb_role role;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
switch (dwc->current_dr_role) {
|
||||
case DWC3_GCTL_PRTCAP_HOST:
|
||||
role = USB_ROLE_HOST;
|
||||
break;
|
||||
case DWC3_GCTL_PRTCAP_DEVICE:
|
||||
role = USB_ROLE_DEVICE;
|
||||
break;
|
||||
case DWC3_GCTL_PRTCAP_OTG:
|
||||
role = dwc->current_otg_role;
|
||||
break;
|
||||
default:
|
||||
if (dwc->role_switch_default_mode == USB_DR_MODE_HOST)
|
||||
role = USB_ROLE_HOST;
|
||||
else
|
||||
role = USB_ROLE_DEVICE;
|
||||
break;
|
||||
}
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
return role;
|
||||
}
|
||||
|
||||
static int dwc3_setup_role_switch(struct dwc3 *dwc)
|
||||
{
|
||||
struct usb_role_switch_desc dwc3_role_switch = {NULL};
|
||||
const char *str;
|
||||
u32 mode;
|
||||
int ret;
|
||||
|
||||
ret = device_property_read_string(dwc->dev, "role-switch-default-mode",
|
||||
&str);
|
||||
if (ret >= 0 && !strncmp(str, "host", strlen("host"))) {
|
||||
dwc->role_switch_default_mode = USB_DR_MODE_HOST;
|
||||
mode = DWC3_GCTL_PRTCAP_HOST;
|
||||
} else {
|
||||
dwc->role_switch_default_mode = USB_DR_MODE_PERIPHERAL;
|
||||
mode = DWC3_GCTL_PRTCAP_DEVICE;
|
||||
}
|
||||
|
||||
dwc3_role_switch.fwnode = dev_fwnode(dwc->dev);
|
||||
dwc3_role_switch.set = dwc3_usb_role_switch_set;
|
||||
dwc3_role_switch.get = dwc3_usb_role_switch_get;
|
||||
dwc->role_sw = usb_role_switch_register(dwc->dev, &dwc3_role_switch);
|
||||
if (IS_ERR(dwc->role_sw))
|
||||
return PTR_ERR(dwc->role_sw);
|
||||
|
||||
dwc3_set_mode(dwc, mode);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define ROLE_SWITCH 0
|
||||
#define dwc3_setup_role_switch(x) 0
|
||||
#endif
|
||||
|
||||
int dwc3_drd_init(struct dwc3 *dwc)
|
||||
{
|
||||
int ret, irq;
|
||||
|
@ -484,7 +570,12 @@ int dwc3_drd_init(struct dwc3 *dwc)
|
|||
if (IS_ERR(dwc->edev))
|
||||
return PTR_ERR(dwc->edev);
|
||||
|
||||
if (dwc->edev) {
|
||||
if (ROLE_SWITCH &&
|
||||
device_property_read_bool(dwc->dev, "usb-role-switch")) {
|
||||
ret = dwc3_setup_role_switch(dwc);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else if (dwc->edev) {
|
||||
dwc->edev_nb.notifier_call = dwc3_drd_notifier;
|
||||
ret = extcon_register_notifier(dwc->edev, EXTCON_USB_HOST,
|
||||
&dwc->edev_nb);
|
||||
|
@ -531,6 +622,9 @@ void dwc3_drd_exit(struct dwc3 *dwc)
|
|||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (dwc->role_sw)
|
||||
usb_role_switch_unregister(dwc->role_sw);
|
||||
|
||||
if (dwc->edev)
|
||||
extcon_unregister_notifier(dwc->edev, EXTCON_USB_HOST,
|
||||
&dwc->edev_nb);
|
||||
|
|
|
@ -162,6 +162,12 @@ static const struct dwc3_exynos_driverdata exynos5250_drvdata = {
|
|||
.suspend_clk_idx = -1,
|
||||
};
|
||||
|
||||
static const struct dwc3_exynos_driverdata exynos5420_drvdata = {
|
||||
.clk_names = { "usbdrd30", "usbdrd30_susp_clk"},
|
||||
.num_clks = 2,
|
||||
.suspend_clk_idx = 1,
|
||||
};
|
||||
|
||||
static const struct dwc3_exynos_driverdata exynos5433_drvdata = {
|
||||
.clk_names = { "aclk", "susp_clk", "pipe_pclk", "phyclk" },
|
||||
.num_clks = 4,
|
||||
|
@ -178,6 +184,9 @@ static const struct of_device_id exynos_dwc3_match[] = {
|
|||
{
|
||||
.compatible = "samsung,exynos5250-dwusb3",
|
||||
.data = &exynos5250_drvdata,
|
||||
}, {
|
||||
.compatible = "samsung,exynos5420-dwusb3",
|
||||
.data = &exynos5420_drvdata,
|
||||
}, {
|
||||
.compatible = "samsung,exynos5433-dwusb3",
|
||||
.data = &exynos5433_drvdata,
|
||||
|
|
|
@ -107,10 +107,37 @@ static const char *phy_names[PHY_COUNT] = {
|
|||
"usb2-phy0", "usb2-phy1", "usb3-phy0",
|
||||
};
|
||||
|
||||
static struct clk_bulk_data meson_g12a_clocks[] = {
|
||||
{ .id = NULL },
|
||||
};
|
||||
|
||||
static struct clk_bulk_data meson_a1_clocks[] = {
|
||||
{ .id = "usb_ctrl" },
|
||||
{ .id = "usb_bus" },
|
||||
{ .id = "xtal_usb_ctrl" },
|
||||
};
|
||||
|
||||
struct dwc3_meson_g12a_drvdata {
|
||||
bool otg_switch_supported;
|
||||
struct clk_bulk_data *clks;
|
||||
int num_clks;
|
||||
};
|
||||
|
||||
static struct dwc3_meson_g12a_drvdata g12a_drvdata = {
|
||||
.otg_switch_supported = true,
|
||||
.clks = meson_g12a_clocks,
|
||||
.num_clks = ARRAY_SIZE(meson_g12a_clocks),
|
||||
};
|
||||
|
||||
static struct dwc3_meson_g12a_drvdata a1_drvdata = {
|
||||
.otg_switch_supported = false,
|
||||
.clks = meson_a1_clocks,
|
||||
.num_clks = ARRAY_SIZE(meson_a1_clocks),
|
||||
};
|
||||
|
||||
struct dwc3_meson_g12a {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct clk *clk;
|
||||
struct reset_control *reset;
|
||||
struct phy *phys[PHY_COUNT];
|
||||
enum usb_dr_mode otg_mode;
|
||||
|
@ -120,6 +147,7 @@ struct dwc3_meson_g12a {
|
|||
struct regulator *vbus;
|
||||
struct usb_role_switch_desc switch_desc;
|
||||
struct usb_role_switch *role_switch;
|
||||
const struct dwc3_meson_g12a_drvdata *drvdata;
|
||||
};
|
||||
|
||||
static void dwc3_meson_g12a_usb2_set_mode(struct dwc3_meson_g12a *priv,
|
||||
|
@ -151,7 +179,7 @@ static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv)
|
|||
U2P_R0_POWER_ON_RESET,
|
||||
U2P_R0_POWER_ON_RESET);
|
||||
|
||||
if (i == USB2_OTG_PHY) {
|
||||
if (priv->drvdata->otg_switch_supported && i == USB2_OTG_PHY) {
|
||||
regmap_update_bits(priv->regmap,
|
||||
U2P_R0 + (U2P_REG_SIZE * i),
|
||||
U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS,
|
||||
|
@ -295,7 +323,7 @@ static int dwc3_meson_g12a_otg_mode_set(struct dwc3_meson_g12a *priv,
|
|||
{
|
||||
int ret;
|
||||
|
||||
if (!priv->phys[USB2_OTG_PHY])
|
||||
if (!priv->drvdata->otg_switch_supported || !priv->phys[USB2_OTG_PHY])
|
||||
return -EINVAL;
|
||||
|
||||
if (mode == PHY_MODE_USB_HOST)
|
||||
|
@ -381,73 +409,15 @@ static struct device *dwc3_meson_g12_find_child(struct device *dev,
|
|||
return &pdev->dev;
|
||||
}
|
||||
|
||||
static int dwc3_meson_g12a_probe(struct platform_device *pdev)
|
||||
static int dwc3_meson_g12a_otg_init(struct platform_device *pdev,
|
||||
struct dwc3_meson_g12a *priv)
|
||||
{
|
||||
struct dwc3_meson_g12a *priv;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
void __iomem *base;
|
||||
enum phy_mode otg_id;
|
||||
int ret, i, irq;
|
||||
int ret, irq;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
priv->regmap = devm_regmap_init_mmio(dev, base,
|
||||
&phy_meson_g12a_usb3_regmap_conf);
|
||||
if (IS_ERR(priv->regmap))
|
||||
return PTR_ERR(priv->regmap);
|
||||
|
||||
priv->vbus = devm_regulator_get_optional(dev, "vbus");
|
||||
if (IS_ERR(priv->vbus)) {
|
||||
if (PTR_ERR(priv->vbus) == -EPROBE_DEFER)
|
||||
return PTR_ERR(priv->vbus);
|
||||
priv->vbus = NULL;
|
||||
}
|
||||
|
||||
priv->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(priv->clk))
|
||||
return PTR_ERR(priv->clk);
|
||||
|
||||
ret = clk_prepare_enable(priv->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
devm_add_action_or_reset(dev,
|
||||
(void(*)(void *))clk_disable_unprepare,
|
||||
priv->clk);
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
priv->dev = dev;
|
||||
|
||||
priv->reset = devm_reset_control_get(dev, NULL);
|
||||
if (IS_ERR(priv->reset)) {
|
||||
ret = PTR_ERR(priv->reset);
|
||||
dev_err(dev, "failed to get device reset, err=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = reset_control_reset(priv->reset);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = dwc3_meson_g12a_get_phys(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (priv->vbus) {
|
||||
ret = regulator_enable(priv->vbus);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Get dr_mode */
|
||||
priv->otg_mode = usb_get_dr_mode(dev);
|
||||
if (!priv->drvdata->otg_switch_supported)
|
||||
return 0;
|
||||
|
||||
if (priv->otg_mode == USB_DR_MODE_OTG) {
|
||||
/* Ack irq before registering */
|
||||
|
@ -462,28 +432,6 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
dwc3_meson_g12a_usb_init(priv);
|
||||
|
||||
/* Init PHYs */
|
||||
for (i = 0 ; i < PHY_COUNT ; ++i) {
|
||||
ret = phy_init(priv->phys[i]);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Set PHY Power */
|
||||
for (i = 0 ; i < PHY_COUNT ; ++i) {
|
||||
ret = phy_power_on(priv->phys[i]);
|
||||
if (ret)
|
||||
goto err_phys_exit;
|
||||
}
|
||||
|
||||
ret = of_platform_populate(np, NULL, NULL, dev);
|
||||
if (ret) {
|
||||
clk_disable_unprepare(priv->clk);
|
||||
goto err_phys_power;
|
||||
}
|
||||
|
||||
/* Setup OTG mode corresponding to the ID pin */
|
||||
if (priv->otg_mode == USB_DR_MODE_OTG) {
|
||||
otg_id = dwc3_meson_g12a_get_id(priv);
|
||||
|
@ -506,6 +454,101 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(priv->role_switch))
|
||||
dev_warn(dev, "Unable to register Role Switch\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_meson_g12a_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct dwc3_meson_g12a *priv;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
void __iomem *base;
|
||||
int ret, i;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
priv->regmap = devm_regmap_init_mmio(dev, base,
|
||||
&phy_meson_g12a_usb3_regmap_conf);
|
||||
if (IS_ERR(priv->regmap))
|
||||
return PTR_ERR(priv->regmap);
|
||||
|
||||
priv->vbus = devm_regulator_get_optional(dev, "vbus");
|
||||
if (IS_ERR(priv->vbus)) {
|
||||
if (PTR_ERR(priv->vbus) == -EPROBE_DEFER)
|
||||
return PTR_ERR(priv->vbus);
|
||||
priv->vbus = NULL;
|
||||
}
|
||||
|
||||
priv->drvdata = of_device_get_match_data(&pdev->dev);
|
||||
|
||||
ret = devm_clk_bulk_get(dev,
|
||||
priv->drvdata->num_clks,
|
||||
priv->drvdata->clks);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_bulk_prepare_enable(priv->drvdata->num_clks,
|
||||
priv->drvdata->clks);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
priv->dev = dev;
|
||||
|
||||
priv->reset = devm_reset_control_get(dev, NULL);
|
||||
if (IS_ERR(priv->reset)) {
|
||||
ret = PTR_ERR(priv->reset);
|
||||
dev_err(dev, "failed to get device reset, err=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = reset_control_reset(priv->reset);
|
||||
if (ret)
|
||||
goto err_disable_clks;
|
||||
|
||||
ret = dwc3_meson_g12a_get_phys(priv);
|
||||
if (ret)
|
||||
goto err_disable_clks;
|
||||
|
||||
if (priv->vbus) {
|
||||
ret = regulator_enable(priv->vbus);
|
||||
if (ret)
|
||||
goto err_disable_clks;
|
||||
}
|
||||
|
||||
/* Get dr_mode */
|
||||
priv->otg_mode = usb_get_dr_mode(dev);
|
||||
|
||||
dwc3_meson_g12a_usb_init(priv);
|
||||
|
||||
/* Init PHYs */
|
||||
for (i = 0 ; i < PHY_COUNT ; ++i) {
|
||||
ret = phy_init(priv->phys[i]);
|
||||
if (ret)
|
||||
goto err_disable_clks;
|
||||
}
|
||||
|
||||
/* Set PHY Power */
|
||||
for (i = 0 ; i < PHY_COUNT ; ++i) {
|
||||
ret = phy_power_on(priv->phys[i]);
|
||||
if (ret)
|
||||
goto err_phys_exit;
|
||||
}
|
||||
|
||||
ret = of_platform_populate(np, NULL, NULL, dev);
|
||||
if (ret)
|
||||
goto err_phys_power;
|
||||
|
||||
ret = dwc3_meson_g12a_otg_init(pdev, priv);
|
||||
if (ret)
|
||||
goto err_phys_power;
|
||||
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_get_sync(dev);
|
||||
|
@ -520,6 +563,10 @@ err_phys_exit:
|
|||
for (i = 0 ; i < PHY_COUNT ; ++i)
|
||||
phy_exit(priv->phys[i]);
|
||||
|
||||
err_disable_clks:
|
||||
clk_bulk_disable_unprepare(priv->drvdata->num_clks,
|
||||
priv->drvdata->clks);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -529,7 +576,8 @@ static int dwc3_meson_g12a_remove(struct platform_device *pdev)
|
|||
struct device *dev = &pdev->dev;
|
||||
int i;
|
||||
|
||||
usb_role_switch_unregister(priv->role_switch);
|
||||
if (priv->drvdata->otg_switch_supported)
|
||||
usb_role_switch_unregister(priv->role_switch);
|
||||
|
||||
of_platform_depopulate(dev);
|
||||
|
||||
|
@ -542,6 +590,9 @@ static int dwc3_meson_g12a_remove(struct platform_device *pdev)
|
|||
pm_runtime_put_noidle(dev);
|
||||
pm_runtime_set_suspended(dev);
|
||||
|
||||
clk_bulk_disable_unprepare(priv->drvdata->num_clks,
|
||||
priv->drvdata->clks);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -549,7 +600,8 @@ static int __maybe_unused dwc3_meson_g12a_runtime_suspend(struct device *dev)
|
|||
{
|
||||
struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable(priv->clk);
|
||||
clk_bulk_disable_unprepare(priv->drvdata->num_clks,
|
||||
priv->drvdata->clks);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -558,7 +610,8 @@ static int __maybe_unused dwc3_meson_g12a_runtime_resume(struct device *dev)
|
|||
{
|
||||
struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
|
||||
|
||||
return clk_enable(priv->clk);
|
||||
return clk_bulk_prepare_enable(priv->drvdata->num_clks,
|
||||
priv->drvdata->clks);
|
||||
}
|
||||
|
||||
static int __maybe_unused dwc3_meson_g12a_suspend(struct device *dev)
|
||||
|
@ -621,7 +674,14 @@ static const struct dev_pm_ops dwc3_meson_g12a_dev_pm_ops = {
|
|||
};
|
||||
|
||||
static const struct of_device_id dwc3_meson_g12a_match[] = {
|
||||
{ .compatible = "amlogic,meson-g12a-usb-ctrl" },
|
||||
{
|
||||
.compatible = "amlogic,meson-g12a-usb-ctrl",
|
||||
.data = &g12a_drvdata,
|
||||
},
|
||||
{
|
||||
.compatible = "amlogic,meson-a1-usb-ctrl",
|
||||
.data = &a1_drvdata,
|
||||
},
|
||||
{ /* Sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dwc3_meson_g12a_match);
|
||||
|
|
|
@ -1521,7 +1521,7 @@ static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep, struct dwc3_request *r
|
|||
for (i = 0; i < req->num_trbs; i++) {
|
||||
struct dwc3_trb *trb;
|
||||
|
||||
trb = req->trb + i;
|
||||
trb = &dep->trb_pool[dep->trb_dequeue];
|
||||
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
|
||||
dwc3_ep_inc_deq(dep);
|
||||
}
|
||||
|
@ -2570,10 +2570,8 @@ static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep,
|
|||
|
||||
dwc3_gadget_ep_cleanup_completed_requests(dep, event, status);
|
||||
|
||||
if (stop) {
|
||||
if (stop)
|
||||
dwc3_stop_active_transfer(dep, true, true);
|
||||
dep->flags = DWC3_EP_ENABLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* WORKAROUND: This is the 2nd half of U1/U2 -> U0 workaround.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/**
|
||||
/*
|
||||
* host.c - DesignWare USB3 DRD Controller Host Glue
|
||||
*
|
||||
* Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com
|
||||
|
@ -7,6 +7,7 @@
|
|||
* Authors: Felipe Balbi <balbi@ti.com>,
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "core.h"
|
||||
|
@ -75,6 +76,7 @@ int dwc3_host_init(struct dwc3 *dwc)
|
|||
}
|
||||
|
||||
xhci->dev.parent = dwc->dev;
|
||||
ACPI_COMPANION_SET(&xhci->dev, ACPI_COMPANION(dwc->dev));
|
||||
|
||||
dwc->xhci = xhci;
|
||||
|
||||
|
|
|
@ -227,6 +227,8 @@ DECLARE_EVENT_CLASS(dwc3_log_trb,
|
|||
__field(u32, size)
|
||||
__field(u32, ctrl)
|
||||
__field(u32, type)
|
||||
__field(u32, enqueue)
|
||||
__field(u32, dequeue)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__assign_str(name, dep->name);
|
||||
|
@ -236,9 +238,12 @@ DECLARE_EVENT_CLASS(dwc3_log_trb,
|
|||
__entry->size = trb->size;
|
||||
__entry->ctrl = trb->ctrl;
|
||||
__entry->type = usb_endpoint_type(dep->endpoint.desc);
|
||||
__entry->enqueue = dep->trb_enqueue;
|
||||
__entry->dequeue = dep->trb_dequeue;
|
||||
),
|
||||
TP_printk("%s: trb %p buf %08x%08x size %s%d ctrl %08x (%c%c%c%c:%c%c:%s)",
|
||||
__get_str(name), __entry->trb, __entry->bph, __entry->bpl,
|
||||
TP_printk("%s: trb %p (E%d:D%d) buf %08x%08x size %s%d ctrl %08x (%c%c%c%c:%c%c:%s)",
|
||||
__get_str(name), __entry->trb, __entry->enqueue,
|
||||
__entry->dequeue, __entry->bph, __entry->bpl,
|
||||
({char *s;
|
||||
int pcm = ((__entry->size >> 24) & 3) + 1;
|
||||
switch (__entry->type) {
|
||||
|
|
|
@ -861,6 +861,11 @@ static int set_config(struct usb_composite_dev *cdev,
|
|||
else
|
||||
power = min(power, 900U);
|
||||
done:
|
||||
if (power <= USB_SELF_POWER_VBUS_MAX_DRAW)
|
||||
usb_gadget_set_selfpowered(gadget);
|
||||
else
|
||||
usb_gadget_clear_selfpowered(gadget);
|
||||
|
||||
usb_gadget_vbus_draw(gadget, power);
|
||||
if (result >= 0 && cdev->delayed_status)
|
||||
result = USB_GADGET_DELAYED_STATUS;
|
||||
|
@ -2279,6 +2284,7 @@ void composite_suspend(struct usb_gadget *gadget)
|
|||
|
||||
cdev->suspended = 1;
|
||||
|
||||
usb_gadget_set_selfpowered(gadget);
|
||||
usb_gadget_vbus_draw(gadget, 2);
|
||||
}
|
||||
|
||||
|
@ -2307,6 +2313,9 @@ void composite_resume(struct usb_gadget *gadget)
|
|||
else
|
||||
maxpower = min(maxpower, 900U);
|
||||
|
||||
if (maxpower > USB_SELF_POWER_VBUS_MAX_DRAW)
|
||||
usb_gadget_clear_selfpowered(gadget);
|
||||
|
||||
usb_gadget_vbus_draw(gadget, maxpower);
|
||||
}
|
||||
|
||||
|
|
|
@ -516,4 +516,15 @@ config USB_G_WEBCAM
|
|||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "g_webcam".
|
||||
|
||||
config USB_RAW_GADGET
|
||||
tristate "USB Raw Gadget"
|
||||
help
|
||||
USB Raw Gadget is a kernel module that provides a userspace interface
|
||||
for the USB Gadget subsystem. Essentially it allows to emulate USB
|
||||
devices from userspace. See Documentation/usb/raw-gadget.rst for
|
||||
details.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "raw_gadget".
|
||||
|
||||
endchoice
|
||||
|
|
|
@ -43,3 +43,4 @@ obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o
|
|||
obj-$(CONFIG_USB_G_NCM) += g_ncm.o
|
||||
obj-$(CONFIG_USB_G_ACM_MS) += g_acm_ms.o
|
||||
obj-$(CONFIG_USB_GADGET_TARGET) += tcm_usb_gadget.o
|
||||
obj-$(CONFIG_USB_RAW_GADGET) += raw_gadget.o
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -441,6 +441,16 @@ config USB_GADGET_XILINX
|
|||
dynamically linked module called "udc-xilinx" and force all
|
||||
gadget drivers to also be dynamically linked.
|
||||
|
||||
config USB_MAX3420_UDC
|
||||
tristate "MAX3420 (USB-over-SPI) support"
|
||||
depends on SPI
|
||||
help
|
||||
The Maxim MAX3420 chip supports USB2.0 full-speed peripheral mode.
|
||||
The MAX3420 is run by SPI interface, and hence the dependency.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called max3420_udc
|
||||
|
||||
config USB_TEGRA_XUDC
|
||||
tristate "NVIDIA Tegra Superspeed USB 3.0 Device Controller"
|
||||
depends on ARCH_TEGRA || COMPILE_TEST
|
||||
|
|
|
@ -42,3 +42,4 @@ obj-$(CONFIG_USB_GADGET_XILINX) += udc-xilinx.o
|
|||
obj-$(CONFIG_USB_SNP_UDC_PLAT) += snps_udc_plat.o
|
||||
obj-$(CONFIG_USB_ASPEED_VHUB) += aspeed-vhub/
|
||||
obj-$(CONFIG_USB_BDC_UDC) += bdc/
|
||||
obj-$(CONFIG_USB_MAX3420_UDC) += max3420_udc.o
|
||||
|
|
|
@ -4,5 +4,5 @@ config USB_ASPEED_VHUB
|
|||
depends on ARCH_ASPEED || COMPILE_TEST
|
||||
depends on USB_LIBCOMPOSITE
|
||||
help
|
||||
USB peripheral controller for the Aspeed AST2500 family
|
||||
SoCs supporting the "vHub" functionality and USB2.0
|
||||
USB peripheral controller for the Aspeed AST2400, AST2500 and
|
||||
AST2600 family SoCs supporting the "vHub" functionality and USB2.0
|
||||
|
|
|
@ -99,7 +99,7 @@ static irqreturn_t ast_vhub_irq(int irq, void *data)
|
|||
{
|
||||
struct ast_vhub *vhub = data;
|
||||
irqreturn_t iret = IRQ_NONE;
|
||||
u32 istat;
|
||||
u32 i, istat;
|
||||
|
||||
/* Stale interrupt while tearing down */
|
||||
if (!vhub->ep0_bufs)
|
||||
|
@ -121,10 +121,10 @@ static irqreturn_t ast_vhub_irq(int irq, void *data)
|
|||
|
||||
/* Handle generic EPs first */
|
||||
if (istat & VHUB_IRQ_EP_POOL_ACK_STALL) {
|
||||
u32 i, ep_acks = readl(vhub->regs + AST_VHUB_EP_ACK_ISR);
|
||||
u32 ep_acks = readl(vhub->regs + AST_VHUB_EP_ACK_ISR);
|
||||
writel(ep_acks, vhub->regs + AST_VHUB_EP_ACK_ISR);
|
||||
|
||||
for (i = 0; ep_acks && i < AST_VHUB_NUM_GEN_EPs; i++) {
|
||||
for (i = 0; ep_acks && i < vhub->max_epns; i++) {
|
||||
u32 mask = VHUB_EP_IRQ(i);
|
||||
if (ep_acks & mask) {
|
||||
ast_vhub_epn_ack_irq(&vhub->epns[i]);
|
||||
|
@ -134,21 +134,11 @@ static irqreturn_t ast_vhub_irq(int irq, void *data)
|
|||
}
|
||||
|
||||
/* Handle device interrupts */
|
||||
if (istat & (VHUB_IRQ_DEVICE1 |
|
||||
VHUB_IRQ_DEVICE2 |
|
||||
VHUB_IRQ_DEVICE3 |
|
||||
VHUB_IRQ_DEVICE4 |
|
||||
VHUB_IRQ_DEVICE5)) {
|
||||
if (istat & VHUB_IRQ_DEVICE1)
|
||||
ast_vhub_dev_irq(&vhub->ports[0].dev);
|
||||
if (istat & VHUB_IRQ_DEVICE2)
|
||||
ast_vhub_dev_irq(&vhub->ports[1].dev);
|
||||
if (istat & VHUB_IRQ_DEVICE3)
|
||||
ast_vhub_dev_irq(&vhub->ports[2].dev);
|
||||
if (istat & VHUB_IRQ_DEVICE4)
|
||||
ast_vhub_dev_irq(&vhub->ports[3].dev);
|
||||
if (istat & VHUB_IRQ_DEVICE5)
|
||||
ast_vhub_dev_irq(&vhub->ports[4].dev);
|
||||
for (i = 0; i < vhub->max_ports; i++) {
|
||||
u32 dev_mask = VHUB_IRQ_DEVICE1 << i;
|
||||
|
||||
if (istat & dev_mask)
|
||||
ast_vhub_dev_irq(&vhub->ports[i].dev);
|
||||
}
|
||||
|
||||
/* Handle top-level vHub EP0 interrupts */
|
||||
|
@ -182,7 +172,7 @@ static irqreturn_t ast_vhub_irq(int irq, void *data)
|
|||
|
||||
void ast_vhub_init_hw(struct ast_vhub *vhub)
|
||||
{
|
||||
u32 ctrl;
|
||||
u32 ctrl, port_mask, epn_mask;
|
||||
|
||||
UDCDBG(vhub,"(Re)Starting HW ...\n");
|
||||
|
||||
|
@ -222,15 +212,20 @@ void ast_vhub_init_hw(struct ast_vhub *vhub)
|
|||
}
|
||||
|
||||
/* Reset all devices */
|
||||
writel(VHUB_SW_RESET_ALL, vhub->regs + AST_VHUB_SW_RESET);
|
||||
port_mask = GENMASK(vhub->max_ports, 1);
|
||||
writel(VHUB_SW_RESET_ROOT_HUB |
|
||||
VHUB_SW_RESET_DMA_CONTROLLER |
|
||||
VHUB_SW_RESET_EP_POOL |
|
||||
port_mask, vhub->regs + AST_VHUB_SW_RESET);
|
||||
udelay(1);
|
||||
writel(0, vhub->regs + AST_VHUB_SW_RESET);
|
||||
|
||||
/* Disable and cleanup EP ACK/NACK interrupts */
|
||||
epn_mask = GENMASK(vhub->max_epns - 1, 0);
|
||||
writel(0, vhub->regs + AST_VHUB_EP_ACK_IER);
|
||||
writel(0, vhub->regs + AST_VHUB_EP_NACK_IER);
|
||||
writel(VHUB_EP_IRQ_ALL, vhub->regs + AST_VHUB_EP_ACK_ISR);
|
||||
writel(VHUB_EP_IRQ_ALL, vhub->regs + AST_VHUB_EP_NACK_ISR);
|
||||
writel(epn_mask, vhub->regs + AST_VHUB_EP_ACK_ISR);
|
||||
writel(epn_mask, vhub->regs + AST_VHUB_EP_NACK_ISR);
|
||||
|
||||
/* Default settings for EP0, enable HW hub EP1 */
|
||||
writel(0, vhub->regs + AST_VHUB_EP0_CTRL);
|
||||
|
@ -273,7 +268,7 @@ static int ast_vhub_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
|
||||
/* Remove devices */
|
||||
for (i = 0; i < AST_VHUB_NUM_PORTS; i++)
|
||||
for (i = 0; i < vhub->max_ports; i++)
|
||||
ast_vhub_del_dev(&vhub->ports[i].dev);
|
||||
|
||||
spin_lock_irqsave(&vhub->lock, flags);
|
||||
|
@ -295,7 +290,7 @@ static int ast_vhub_remove(struct platform_device *pdev)
|
|||
if (vhub->ep0_bufs)
|
||||
dma_free_coherent(&pdev->dev,
|
||||
AST_VHUB_EP0_MAX_PACKET *
|
||||
(AST_VHUB_NUM_PORTS + 1),
|
||||
(vhub->max_ports + 1),
|
||||
vhub->ep0_bufs,
|
||||
vhub->ep0_bufs_dma);
|
||||
vhub->ep0_bufs = NULL;
|
||||
|
@ -309,11 +304,32 @@ static int ast_vhub_probe(struct platform_device *pdev)
|
|||
struct ast_vhub *vhub;
|
||||
struct resource *res;
|
||||
int i, rc = 0;
|
||||
const struct device_node *np = pdev->dev.of_node;
|
||||
|
||||
vhub = devm_kzalloc(&pdev->dev, sizeof(*vhub), GFP_KERNEL);
|
||||
if (!vhub)
|
||||
return -ENOMEM;
|
||||
|
||||
rc = of_property_read_u32(np, "aspeed,vhub-downstream-ports",
|
||||
&vhub->max_ports);
|
||||
if (rc < 0)
|
||||
vhub->max_ports = AST_VHUB_NUM_PORTS;
|
||||
|
||||
vhub->ports = devm_kcalloc(&pdev->dev, vhub->max_ports,
|
||||
sizeof(*vhub->ports), GFP_KERNEL);
|
||||
if (!vhub->ports)
|
||||
return -ENOMEM;
|
||||
|
||||
rc = of_property_read_u32(np, "aspeed,vhub-generic-endpoints",
|
||||
&vhub->max_epns);
|
||||
if (rc < 0)
|
||||
vhub->max_epns = AST_VHUB_NUM_GEN_EPs;
|
||||
|
||||
vhub->epns = devm_kcalloc(&pdev->dev, vhub->max_epns,
|
||||
sizeof(*vhub->epns), GFP_KERNEL);
|
||||
if (!vhub->epns)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&vhub->lock);
|
||||
vhub->pdev = pdev;
|
||||
|
||||
|
@ -366,7 +382,7 @@ static int ast_vhub_probe(struct platform_device *pdev)
|
|||
*/
|
||||
vhub->ep0_bufs = dma_alloc_coherent(&pdev->dev,
|
||||
AST_VHUB_EP0_MAX_PACKET *
|
||||
(AST_VHUB_NUM_PORTS + 1),
|
||||
(vhub->max_ports + 1),
|
||||
&vhub->ep0_bufs_dma, GFP_KERNEL);
|
||||
if (!vhub->ep0_bufs) {
|
||||
dev_err(&pdev->dev, "Failed to allocate EP0 DMA buffers\n");
|
||||
|
@ -380,7 +396,7 @@ static int ast_vhub_probe(struct platform_device *pdev)
|
|||
ast_vhub_init_ep0(vhub, &vhub->ep0, NULL);
|
||||
|
||||
/* Init devices */
|
||||
for (i = 0; i < AST_VHUB_NUM_PORTS && rc == 0; i++)
|
||||
for (i = 0; i < vhub->max_ports && rc == 0; i++)
|
||||
rc = ast_vhub_init_dev(vhub, i);
|
||||
if (rc)
|
||||
goto err;
|
||||
|
@ -407,6 +423,9 @@ static const struct of_device_id ast_vhub_dt_ids[] = {
|
|||
{
|
||||
.compatible = "aspeed,ast2500-usb-vhub",
|
||||
},
|
||||
{
|
||||
.compatible = "aspeed,ast2600-usb-vhub",
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ast_vhub_dt_ids);
|
||||
|
|
|
@ -77,7 +77,7 @@ static void ast_vhub_dev_enable(struct ast_vhub_dev *d)
|
|||
writel(d->ep0.buf_dma, d->regs + AST_VHUB_DEV_EP0_DATA);
|
||||
|
||||
/* Clear stall on all EPs */
|
||||
for (i = 0; i < AST_VHUB_NUM_GEN_EPs; i++) {
|
||||
for (i = 0; i < d->max_epns; i++) {
|
||||
struct ast_vhub_ep *ep = d->epns[i];
|
||||
|
||||
if (ep && (ep->epn.stalled || ep->epn.wedged)) {
|
||||
|
@ -137,7 +137,7 @@ static int ast_vhub_ep_feature(struct ast_vhub_dev *d,
|
|||
is_set ? "SET" : "CLEAR", ep_num, wValue);
|
||||
if (ep_num == 0)
|
||||
return std_req_complete;
|
||||
if (ep_num >= AST_VHUB_NUM_GEN_EPs || !d->epns[ep_num - 1])
|
||||
if (ep_num >= d->max_epns || !d->epns[ep_num - 1])
|
||||
return std_req_stall;
|
||||
if (wValue != USB_ENDPOINT_HALT)
|
||||
return std_req_driver;
|
||||
|
@ -181,7 +181,7 @@ static int ast_vhub_ep_status(struct ast_vhub_dev *d,
|
|||
|
||||
DDBG(d, "GET_STATUS(ep%d)\n", ep_num);
|
||||
|
||||
if (ep_num >= AST_VHUB_NUM_GEN_EPs)
|
||||
if (ep_num >= d->max_epns)
|
||||
return std_req_stall;
|
||||
if (ep_num != 0) {
|
||||
ep = d->epns[ep_num - 1];
|
||||
|
@ -299,7 +299,7 @@ static void ast_vhub_dev_nuke(struct ast_vhub_dev *d)
|
|||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < AST_VHUB_NUM_GEN_EPs; i++) {
|
||||
for (i = 0; i < d->max_epns; i++) {
|
||||
if (!d->epns[i])
|
||||
continue;
|
||||
ast_vhub_nuke(d->epns[i], -ESHUTDOWN);
|
||||
|
@ -416,10 +416,10 @@ static struct usb_ep *ast_vhub_udc_match_ep(struct usb_gadget *gadget,
|
|||
* that will allow the generic code to use our
|
||||
* assigned address.
|
||||
*/
|
||||
for (i = 0; i < AST_VHUB_NUM_GEN_EPs; i++)
|
||||
for (i = 0; i < d->max_epns; i++)
|
||||
if (d->epns[i] == NULL)
|
||||
break;
|
||||
if (i >= AST_VHUB_NUM_GEN_EPs)
|
||||
if (i >= d->max_epns)
|
||||
return NULL;
|
||||
addr = i + 1;
|
||||
|
||||
|
@ -526,6 +526,7 @@ void ast_vhub_del_dev(struct ast_vhub_dev *d)
|
|||
|
||||
usb_del_gadget_udc(&d->gadget);
|
||||
device_unregister(d->port_dev);
|
||||
kfree(d->epns);
|
||||
}
|
||||
|
||||
static void ast_vhub_dev_release(struct device *dev)
|
||||
|
@ -546,14 +547,25 @@ int ast_vhub_init_dev(struct ast_vhub *vhub, unsigned int idx)
|
|||
|
||||
ast_vhub_init_ep0(vhub, &d->ep0, d);
|
||||
|
||||
/*
|
||||
* A USB device can have up to 30 endpoints besides control
|
||||
* endpoint 0.
|
||||
*/
|
||||
d->max_epns = min_t(u32, vhub->max_epns, 30);
|
||||
d->epns = kcalloc(d->max_epns, sizeof(*d->epns), GFP_KERNEL);
|
||||
if (!d->epns)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* The UDC core really needs us to have separate and uniquely
|
||||
* named "parent" devices for each port so we create a sub device
|
||||
* here for that purpose
|
||||
*/
|
||||
d->port_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
|
||||
if (!d->port_dev)
|
||||
return -ENOMEM;
|
||||
if (!d->port_dev) {
|
||||
rc = -ENOMEM;
|
||||
goto fail_alloc;
|
||||
}
|
||||
device_initialize(d->port_dev);
|
||||
d->port_dev->release = ast_vhub_dev_release;
|
||||
d->port_dev->parent = parent;
|
||||
|
@ -584,6 +596,8 @@ int ast_vhub_init_dev(struct ast_vhub *vhub, unsigned int idx)
|
|||
device_del(d->port_dev);
|
||||
fail_add:
|
||||
put_device(d->port_dev);
|
||||
fail_alloc:
|
||||
kfree(d->epns);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -800,10 +800,10 @@ struct ast_vhub_ep *ast_vhub_alloc_epn(struct ast_vhub_dev *d, u8 addr)
|
|||
|
||||
/* Find a free one (no device) */
|
||||
spin_lock_irqsave(&vhub->lock, flags);
|
||||
for (i = 0; i < AST_VHUB_NUM_GEN_EPs; i++)
|
||||
for (i = 0; i < vhub->max_epns; i++)
|
||||
if (vhub->epns[i].dev == NULL)
|
||||
break;
|
||||
if (i >= AST_VHUB_NUM_GEN_EPs) {
|
||||
if (i >= vhub->max_epns) {
|
||||
spin_unlock_irqrestore(&vhub->lock, flags);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -93,11 +93,7 @@ static void ast_vhub_patch_dev_desc_usb1(struct usb_device_descriptor *desc)
|
|||
USB_DT_INTERFACE_SIZE + \
|
||||
USB_DT_ENDPOINT_SIZE)
|
||||
|
||||
static const struct ast_vhub_full_cdesc {
|
||||
struct usb_config_descriptor cfg;
|
||||
struct usb_interface_descriptor intf;
|
||||
struct usb_endpoint_descriptor ep;
|
||||
} __attribute__ ((packed)) ast_vhub_conf_desc = {
|
||||
static const struct ast_vhub_full_cdesc ast_vhub_conf_desc = {
|
||||
.cfg = {
|
||||
.bLength = USB_DT_CONFIG_SIZE,
|
||||
.bDescriptorType = USB_DT_CONFIG,
|
||||
|
@ -266,6 +262,7 @@ static int ast_vhub_rep_desc(struct ast_vhub_ep *ep,
|
|||
u8 desc_type, u16 len)
|
||||
{
|
||||
size_t dsize;
|
||||
struct ast_vhub *vhub = ep->vhub;
|
||||
|
||||
EPDBG(ep, "GET_DESCRIPTOR(type:%d)\n", desc_type);
|
||||
|
||||
|
@ -281,20 +278,20 @@ static int ast_vhub_rep_desc(struct ast_vhub_ep *ep,
|
|||
switch(desc_type) {
|
||||
case USB_DT_DEVICE:
|
||||
dsize = USB_DT_DEVICE_SIZE;
|
||||
memcpy(ep->buf, &ast_vhub_dev_desc, dsize);
|
||||
BUILD_BUG_ON(dsize > sizeof(ast_vhub_dev_desc));
|
||||
memcpy(ep->buf, &vhub->vhub_dev_desc, dsize);
|
||||
BUILD_BUG_ON(dsize > sizeof(vhub->vhub_dev_desc));
|
||||
BUILD_BUG_ON(USB_DT_DEVICE_SIZE >= AST_VHUB_EP0_MAX_PACKET);
|
||||
break;
|
||||
case USB_DT_CONFIG:
|
||||
dsize = AST_VHUB_CONF_DESC_SIZE;
|
||||
memcpy(ep->buf, &ast_vhub_conf_desc, dsize);
|
||||
BUILD_BUG_ON(dsize > sizeof(ast_vhub_conf_desc));
|
||||
memcpy(ep->buf, &vhub->vhub_conf_desc, dsize);
|
||||
BUILD_BUG_ON(dsize > sizeof(vhub->vhub_conf_desc));
|
||||
BUILD_BUG_ON(AST_VHUB_CONF_DESC_SIZE >= AST_VHUB_EP0_MAX_PACKET);
|
||||
break;
|
||||
case USB_DT_HUB:
|
||||
dsize = AST_VHUB_HUB_DESC_SIZE;
|
||||
memcpy(ep->buf, &ast_vhub_hub_desc, dsize);
|
||||
BUILD_BUG_ON(dsize > sizeof(ast_vhub_hub_desc));
|
||||
memcpy(ep->buf, &vhub->vhub_hub_desc, dsize);
|
||||
BUILD_BUG_ON(dsize > sizeof(vhub->vhub_hub_desc));
|
||||
BUILD_BUG_ON(AST_VHUB_HUB_DESC_SIZE >= AST_VHUB_EP0_MAX_PACKET);
|
||||
break;
|
||||
default:
|
||||
|
@ -317,7 +314,8 @@ static int ast_vhub_rep_string(struct ast_vhub_ep *ep,
|
|||
u8 string_id, u16 lang_id,
|
||||
u16 len)
|
||||
{
|
||||
int rc = usb_gadget_get_string (&ast_vhub_strings, string_id, ep->buf);
|
||||
int rc = usb_gadget_get_string(&ep->vhub->vhub_str_desc,
|
||||
string_id, ep->buf);
|
||||
|
||||
/*
|
||||
* This should never happen unless we put too big strings in
|
||||
|
@ -504,7 +502,7 @@ static void ast_vhub_wake_work(struct work_struct *work)
|
|||
* we let the normal host wake path deal with it later.
|
||||
*/
|
||||
spin_lock_irqsave(&vhub->lock, flags);
|
||||
for (i = 0; i < AST_VHUB_NUM_PORTS; i++) {
|
||||
for (i = 0; i < vhub->max_ports; i++) {
|
||||
struct ast_vhub_port *p = &vhub->ports[i];
|
||||
|
||||
if (!(p->status & USB_PORT_STAT_SUSPEND))
|
||||
|
@ -587,7 +585,7 @@ static enum std_req_rc ast_vhub_set_port_feature(struct ast_vhub_ep *ep,
|
|||
struct ast_vhub *vhub = ep->vhub;
|
||||
struct ast_vhub_port *p;
|
||||
|
||||
if (port == 0 || port > AST_VHUB_NUM_PORTS)
|
||||
if (port == 0 || port > vhub->max_ports)
|
||||
return std_req_stall;
|
||||
port--;
|
||||
p = &vhub->ports[port];
|
||||
|
@ -630,7 +628,7 @@ static enum std_req_rc ast_vhub_clr_port_feature(struct ast_vhub_ep *ep,
|
|||
struct ast_vhub *vhub = ep->vhub;
|
||||
struct ast_vhub_port *p;
|
||||
|
||||
if (port == 0 || port > AST_VHUB_NUM_PORTS)
|
||||
if (port == 0 || port > vhub->max_ports)
|
||||
return std_req_stall;
|
||||
port--;
|
||||
p = &vhub->ports[port];
|
||||
|
@ -676,7 +674,7 @@ static enum std_req_rc ast_vhub_get_port_stat(struct ast_vhub_ep *ep,
|
|||
struct ast_vhub *vhub = ep->vhub;
|
||||
u16 stat, chg;
|
||||
|
||||
if (port == 0 || port > AST_VHUB_NUM_PORTS)
|
||||
if (port == 0 || port > vhub->max_ports)
|
||||
return std_req_stall;
|
||||
port--;
|
||||
|
||||
|
@ -757,7 +755,7 @@ void ast_vhub_hub_suspend(struct ast_vhub *vhub)
|
|||
* Forward to unsuspended ports without changing
|
||||
* their connection status.
|
||||
*/
|
||||
for (i = 0; i < AST_VHUB_NUM_PORTS; i++) {
|
||||
for (i = 0; i < vhub->max_ports; i++) {
|
||||
struct ast_vhub_port *p = &vhub->ports[i];
|
||||
|
||||
if (!(p->status & USB_PORT_STAT_SUSPEND))
|
||||
|
@ -780,7 +778,7 @@ void ast_vhub_hub_resume(struct ast_vhub *vhub)
|
|||
* Forward to unsuspended ports without changing
|
||||
* their connection status.
|
||||
*/
|
||||
for (i = 0; i < AST_VHUB_NUM_PORTS; i++) {
|
||||
for (i = 0; i < vhub->max_ports; i++) {
|
||||
struct ast_vhub_port *p = &vhub->ports[i];
|
||||
|
||||
if (!(p->status & USB_PORT_STAT_SUSPEND))
|
||||
|
@ -814,7 +812,7 @@ void ast_vhub_hub_reset(struct ast_vhub *vhub)
|
|||
* Clear all port status, disable gadgets and "suspend"
|
||||
* them. They will be woken up by a port reset.
|
||||
*/
|
||||
for (i = 0; i < AST_VHUB_NUM_PORTS; i++) {
|
||||
for (i = 0; i < vhub->max_ports; i++) {
|
||||
struct ast_vhub_port *p = &vhub->ports[i];
|
||||
|
||||
/* Only keep the connected flag */
|
||||
|
@ -834,9 +832,31 @@ void ast_vhub_hub_reset(struct ast_vhub *vhub)
|
|||
writel(0, vhub->regs + AST_VHUB_EP1_STS_CHG);
|
||||
}
|
||||
|
||||
static void ast_vhub_init_desc(struct ast_vhub *vhub)
|
||||
{
|
||||
/* Initialize vhub Device Descriptor. */
|
||||
memcpy(&vhub->vhub_dev_desc, &ast_vhub_dev_desc,
|
||||
sizeof(vhub->vhub_dev_desc));
|
||||
|
||||
/* Initialize vhub Configuration Descriptor. */
|
||||
memcpy(&vhub->vhub_conf_desc, &ast_vhub_conf_desc,
|
||||
sizeof(vhub->vhub_conf_desc));
|
||||
|
||||
/* Initialize vhub Hub Descriptor. */
|
||||
memcpy(&vhub->vhub_hub_desc, &ast_vhub_hub_desc,
|
||||
sizeof(vhub->vhub_hub_desc));
|
||||
vhub->vhub_hub_desc.bNbrPorts = vhub->max_ports;
|
||||
|
||||
/* Initialize vhub String Descriptors. */
|
||||
memcpy(&vhub->vhub_str_desc, &ast_vhub_strings,
|
||||
sizeof(vhub->vhub_str_desc));
|
||||
}
|
||||
|
||||
void ast_vhub_init_hub(struct ast_vhub *vhub)
|
||||
{
|
||||
vhub->speed = USB_SPEED_UNKNOWN;
|
||||
INIT_WORK(&vhub->wake_work, ast_vhub_wake_work);
|
||||
|
||||
ast_vhub_init_desc(vhub);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
#ifndef __ASPEED_VHUB_H
|
||||
#define __ASPEED_VHUB_H
|
||||
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/ch11.h>
|
||||
|
||||
/*****************************
|
||||
* *
|
||||
* VHUB register definitions *
|
||||
|
@ -76,17 +79,9 @@
|
|||
#define VHUB_SW_RESET_DEVICE2 (1 << 2)
|
||||
#define VHUB_SW_RESET_DEVICE1 (1 << 1)
|
||||
#define VHUB_SW_RESET_ROOT_HUB (1 << 0)
|
||||
#define VHUB_SW_RESET_ALL (VHUB_SW_RESET_EP_POOL | \
|
||||
VHUB_SW_RESET_DMA_CONTROLLER | \
|
||||
VHUB_SW_RESET_DEVICE5 | \
|
||||
VHUB_SW_RESET_DEVICE4 | \
|
||||
VHUB_SW_RESET_DEVICE3 | \
|
||||
VHUB_SW_RESET_DEVICE2 | \
|
||||
VHUB_SW_RESET_DEVICE1 | \
|
||||
VHUB_SW_RESET_ROOT_HUB)
|
||||
|
||||
/* EP ACK/NACK IRQ masks */
|
||||
#define VHUB_EP_IRQ(n) (1 << (n))
|
||||
#define VHUB_EP_IRQ_ALL 0x7fff /* 15 EPs */
|
||||
|
||||
/* USB status reg */
|
||||
#define VHUB_USBSTS_HISPEED (1 << 27)
|
||||
|
@ -210,6 +205,11 @@
|
|||
* *
|
||||
****************************************/
|
||||
|
||||
/*
|
||||
* AST_VHUB_NUM_GEN_EPs and AST_VHUB_NUM_PORTS are kept to avoid breaking
|
||||
* existing AST2400/AST2500 platforms. AST2600 and future vhub revisions
|
||||
* should define number of downstream ports and endpoints in device tree.
|
||||
*/
|
||||
#define AST_VHUB_NUM_GEN_EPs 15 /* Generic non-0 EPs */
|
||||
#define AST_VHUB_NUM_PORTS 5 /* vHub ports */
|
||||
#define AST_VHUB_EP0_MAX_PACKET 64 /* EP0's max packet size */
|
||||
|
@ -312,7 +312,7 @@ struct ast_vhub_ep {
|
|||
/* Registers */
|
||||
void __iomem *regs;
|
||||
|
||||
/* Index in global pool (0..14) */
|
||||
/* Index in global pool (zero-based) */
|
||||
unsigned int g_idx;
|
||||
|
||||
/* DMA Descriptors */
|
||||
|
@ -342,7 +342,7 @@ struct ast_vhub_dev {
|
|||
struct ast_vhub *vhub;
|
||||
void __iomem *regs;
|
||||
|
||||
/* Device index (0...4) and name string */
|
||||
/* Device index (zero-based) and name string */
|
||||
unsigned int index;
|
||||
const char *name;
|
||||
|
||||
|
@ -358,7 +358,8 @@ struct ast_vhub_dev {
|
|||
|
||||
/* Endpoint structures */
|
||||
struct ast_vhub_ep ep0;
|
||||
struct ast_vhub_ep *epns[AST_VHUB_NUM_GEN_EPs];
|
||||
struct ast_vhub_ep **epns;
|
||||
u32 max_epns;
|
||||
|
||||
};
|
||||
#define to_ast_dev(__g) container_of(__g, struct ast_vhub_dev, gadget)
|
||||
|
@ -373,6 +374,12 @@ struct ast_vhub_port {
|
|||
struct ast_vhub_dev dev;
|
||||
};
|
||||
|
||||
struct ast_vhub_full_cdesc {
|
||||
struct usb_config_descriptor cfg;
|
||||
struct usb_interface_descriptor intf;
|
||||
struct usb_endpoint_descriptor ep;
|
||||
} __packed;
|
||||
|
||||
/* Global vhub structure */
|
||||
struct ast_vhub {
|
||||
struct platform_device *pdev;
|
||||
|
@ -393,10 +400,12 @@ struct ast_vhub {
|
|||
bool ep1_stalled : 1;
|
||||
|
||||
/* Per-port info */
|
||||
struct ast_vhub_port ports[AST_VHUB_NUM_PORTS];
|
||||
struct ast_vhub_port *ports;
|
||||
u32 max_ports;
|
||||
|
||||
/* Generic EP data structures */
|
||||
struct ast_vhub_ep epns[AST_VHUB_NUM_GEN_EPs];
|
||||
struct ast_vhub_ep *epns;
|
||||
u32 max_epns;
|
||||
|
||||
/* Upstream bus is suspended ? */
|
||||
bool suspended : 1;
|
||||
|
@ -409,6 +418,12 @@ struct ast_vhub {
|
|||
|
||||
/* Upstream bus speed captured at bus reset */
|
||||
unsigned int speed;
|
||||
|
||||
/* Standard USB Descriptors of the vhub. */
|
||||
struct usb_device_descriptor vhub_dev_desc;
|
||||
struct ast_vhub_full_cdesc vhub_conf_desc;
|
||||
struct usb_hub_descriptor vhub_hub_desc;
|
||||
struct usb_gadget_strings vhub_str_desc;
|
||||
};
|
||||
|
||||
/* Standard request handlers result codes */
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -3493,11 +3493,8 @@ static int tegra_xudc_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
xudc->irq = platform_get_irq(pdev, 0);
|
||||
if (xudc->irq < 0) {
|
||||
dev_err(xudc->dev, "failed to get IRQ: %d\n",
|
||||
xudc->irq);
|
||||
if (xudc->irq < 0)
|
||||
return xudc->irq;
|
||||
}
|
||||
|
||||
err = devm_request_irq(&pdev->dev, xudc->irq, tegra_xudc_irq, 0,
|
||||
dev_name(&pdev->dev), xudc);
|
||||
|
|
|
@ -0,0 +1,167 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
/*
|
||||
* USB Raw Gadget driver.
|
||||
*
|
||||
* See Documentation/usb/raw-gadget.rst for more details.
|
||||
*/
|
||||
|
||||
#ifndef _UAPI__LINUX_USB_RAW_GADGET_H
|
||||
#define _UAPI__LINUX_USB_RAW_GADGET_H
|
||||
|
||||
#include <asm/ioctl.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
|
||||
/* Maximum length of driver_name/device_name in the usb_raw_init struct. */
|
||||
#define UDC_NAME_LENGTH_MAX 128
|
||||
|
||||
/*
|
||||
* struct usb_raw_init - argument for USB_RAW_IOCTL_INIT ioctl.
|
||||
* @speed: The speed of the emulated USB device, takes the same values as
|
||||
* the usb_device_speed enum: USB_SPEED_FULL, USB_SPEED_HIGH, etc.
|
||||
* @driver_name: The name of the UDC driver.
|
||||
* @device_name: The name of a UDC instance.
|
||||
*
|
||||
* The last two fields identify a UDC the gadget driver should bind to.
|
||||
* For example, Dummy UDC has "dummy_udc" as its driver_name and "dummy_udc.N"
|
||||
* as its device_name, where N in the index of the Dummy UDC instance.
|
||||
* At the same time the dwc2 driver that is used on Raspberry Pi Zero, has
|
||||
* "20980000.usb" as both driver_name and device_name.
|
||||
*/
|
||||
struct usb_raw_init {
|
||||
__u8 driver_name[UDC_NAME_LENGTH_MAX];
|
||||
__u8 device_name[UDC_NAME_LENGTH_MAX];
|
||||
__u8 speed;
|
||||
};
|
||||
|
||||
/* The type of event fetched with the USB_RAW_IOCTL_EVENT_FETCH ioctl. */
|
||||
enum usb_raw_event_type {
|
||||
USB_RAW_EVENT_INVALID = 0,
|
||||
|
||||
/* This event is queued when the driver has bound to a UDC. */
|
||||
USB_RAW_EVENT_CONNECT = 1,
|
||||
|
||||
/* This event is queued when a new control request arrived to ep0. */
|
||||
USB_RAW_EVENT_CONTROL = 2,
|
||||
|
||||
/* The list might grow in the future. */
|
||||
};
|
||||
|
||||
/*
|
||||
* struct usb_raw_event - argument for USB_RAW_IOCTL_EVENT_FETCH ioctl.
|
||||
* @type: The type of the fetched event.
|
||||
* @length: Length of the data buffer. Updated by the driver and set to the
|
||||
* actual length of the fetched event data.
|
||||
* @data: A buffer to store the fetched event data.
|
||||
*
|
||||
* Currently the fetched data buffer is empty for USB_RAW_EVENT_CONNECT,
|
||||
* and contains struct usb_ctrlrequest for USB_RAW_EVENT_CONTROL.
|
||||
*/
|
||||
struct usb_raw_event {
|
||||
__u32 type;
|
||||
__u32 length;
|
||||
__u8 data[0];
|
||||
};
|
||||
|
||||
#define USB_RAW_IO_FLAGS_ZERO 0x0001
|
||||
#define USB_RAW_IO_FLAGS_MASK 0x0001
|
||||
|
||||
static int usb_raw_io_flags_valid(__u16 flags)
|
||||
{
|
||||
return (flags & ~USB_RAW_IO_FLAGS_MASK) == 0;
|
||||
}
|
||||
|
||||
static int usb_raw_io_flags_zero(__u16 flags)
|
||||
{
|
||||
return (flags & USB_RAW_IO_FLAGS_ZERO);
|
||||
}
|
||||
|
||||
/*
|
||||
* struct usb_raw_ep_io - argument for USB_RAW_IOCTL_EP0/EP_WRITE/READ ioctls.
|
||||
* @ep: Endpoint handle as returned by USB_RAW_IOCTL_EP_ENABLE for
|
||||
* USB_RAW_IOCTL_EP_WRITE/READ. Ignored for USB_RAW_IOCTL_EP0_WRITE/READ.
|
||||
* @flags: When USB_RAW_IO_FLAGS_ZERO is specified, the zero flag is set on
|
||||
* the submitted USB request, see include/linux/usb/gadget.h for details.
|
||||
* @length: Length of data.
|
||||
* @data: Data to send for USB_RAW_IOCTL_EP0/EP_WRITE. Buffer to store received
|
||||
* data for USB_RAW_IOCTL_EP0/EP_READ.
|
||||
*/
|
||||
struct usb_raw_ep_io {
|
||||
__u16 ep;
|
||||
__u16 flags;
|
||||
__u32 length;
|
||||
__u8 data[0];
|
||||
};
|
||||
|
||||
/*
|
||||
* Initializes a Raw Gadget instance.
|
||||
* Accepts a pointer to the usb_raw_init struct as an argument.
|
||||
* Returns 0 on success or negative error code on failure.
|
||||
*/
|
||||
#define USB_RAW_IOCTL_INIT _IOW('U', 0, struct usb_raw_init)
|
||||
|
||||
/*
|
||||
* Instructs Raw Gadget to bind to a UDC and start emulating a USB device.
|
||||
* Returns 0 on success or negative error code on failure.
|
||||
*/
|
||||
#define USB_RAW_IOCTL_RUN _IO('U', 1)
|
||||
|
||||
/*
|
||||
* A blocking ioctl that waits for an event and returns fetched event data to
|
||||
* the user.
|
||||
* Accepts a pointer to the usb_raw_event struct.
|
||||
* Returns 0 on success or negative error code on failure.
|
||||
*/
|
||||
#define USB_RAW_IOCTL_EVENT_FETCH _IOR('U', 2, struct usb_raw_event)
|
||||
|
||||
/*
|
||||
* Queues an IN (OUT for READ) urb as a response to the last control request
|
||||
* received on endpoint 0, provided that was an IN (OUT for READ) request and
|
||||
* waits until the urb is completed. Copies received data to user for READ.
|
||||
* Accepts a pointer to the usb_raw_ep_io struct as an argument.
|
||||
* Returns length of trasferred data on success or negative error code on
|
||||
* failure.
|
||||
*/
|
||||
#define USB_RAW_IOCTL_EP0_WRITE _IOW('U', 3, struct usb_raw_ep_io)
|
||||
#define USB_RAW_IOCTL_EP0_READ _IOWR('U', 4, struct usb_raw_ep_io)
|
||||
|
||||
/*
|
||||
* Finds an endpoint that supports the transfer type specified in the
|
||||
* descriptor and enables it.
|
||||
* Accepts a pointer to the usb_endpoint_descriptor struct as an argument.
|
||||
* Returns enabled endpoint handle on success or negative error code on failure.
|
||||
*/
|
||||
#define USB_RAW_IOCTL_EP_ENABLE _IOW('U', 5, struct usb_endpoint_descriptor)
|
||||
|
||||
/* Disables specified endpoint.
|
||||
* Accepts endpoint handle as an argument.
|
||||
* Returns 0 on success or negative error code on failure.
|
||||
*/
|
||||
#define USB_RAW_IOCTL_EP_DISABLE _IOW('U', 6, __u32)
|
||||
|
||||
/*
|
||||
* Queues an IN (OUT for READ) urb as a response to the last control request
|
||||
* received on endpoint usb_raw_ep_io.ep, provided that was an IN (OUT for READ)
|
||||
* request and waits until the urb is completed. Copies received data to user
|
||||
* for READ.
|
||||
* Accepts a pointer to the usb_raw_ep_io struct as an argument.
|
||||
* Returns length of trasferred data on success or negative error code on
|
||||
* failure.
|
||||
*/
|
||||
#define USB_RAW_IOCTL_EP_WRITE _IOW('U', 7, struct usb_raw_ep_io)
|
||||
#define USB_RAW_IOCTL_EP_READ _IOWR('U', 8, struct usb_raw_ep_io)
|
||||
|
||||
/*
|
||||
* Switches the gadget into the configured state.
|
||||
* Returns 0 on success or negative error code on failure.
|
||||
*/
|
||||
#define USB_RAW_IOCTL_CONFIGURE _IO('U', 9)
|
||||
|
||||
/*
|
||||
* Constrains UDC VBUS power usage.
|
||||
* Accepts current limit in 2 mA units as an argument.
|
||||
* Returns 0 on success or negative error code on failure.
|
||||
*/
|
||||
#define USB_RAW_IOCTL_VBUS_DRAW _IOW('U', 10, __u32)
|
||||
|
||||
#endif /* _UAPI__LINUX_USB_RAW_GADGET_H */
|
Loading…
Reference in New Issue