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:
Greg Kroah-Hartman 2020-03-16 08:22:49 +01:00
commit a8ab3e7629
40 changed files with 3532 additions and 219 deletions

View file

@ -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 {

View file

@ -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>;
};

View file

@ -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

View file

@ -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

View file

@ -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:

View file

@ -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>;
};
};

View file

@ -22,6 +22,7 @@ USB support
misc_usbsevseg
mtouchusb
ohci
raw-gadget
usbip_protocol
usbmon
usb-serial

View file

@ -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.

View file

@ -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";

View file

@ -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";

View file

@ -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";

View file

@ -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>;

View file

@ -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;

View file

@ -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,

View file

@ -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)

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -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);

View file

@ -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,

View file

@ -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);

View file

@ -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.

View file

@ -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;

View file

@ -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) {

View file

@ -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);
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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

View file

@ -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);

View file

@ -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 */