Commit a8ab3e76 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

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: default avatarFelipe 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
  ...
parents 6a7c533d 4a5dbd90
......@@ -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 {
......
# 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:
......
# 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
......
==============
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 @@ vhub: usb-vhub@1e6a0000 {
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 @@ vhub: usb-vhub@1e6a0000 {
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 @@ pinctrl_uart9_default: uart9_default {
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 @@ mac3: ftgmac@1e690000 {
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 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
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,14 +409,61 @@ static struct device *dwc3_meson_g12_find_child(struct device *dev,
return &pdev->dev;
}
static int dwc3_meson_g12a_otg_init(struct platform_device *pdev,
struct dwc3_meson_g12a *priv)
{
enum phy_mode otg_id;
int ret, irq;
struct device *dev = &pdev->dev;
if (!priv->drvdata->otg_switch_supported)
return 0;
if (priv->otg_mode == USB_DR_MODE_OTG) {
/* Ack irq before registering */
regmap_update_bits(priv->regmap, USB_R5,
USB_R5_ID_DIG_IRQ, 0);
irq = platform_get_irq(pdev, 0);
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
dwc3_meson_g12a_irq_thread,
IRQF_ONESHOT, pdev->name, priv);
if (ret)
return ret;
}
/* Setup OTG mode corresponding to the ID pin */
if (priv->otg_mode == USB_DR_MODE_OTG) {
otg_id = dwc3_meson_g12a_get_id(priv);
if (otg_id != priv->otg_phy_mode) {
if (dwc3_meson_g12a_otg_mode_set(priv, otg_id))
dev_warn(dev, "Failed to switch OTG mode\n");
}
}
/* Setup role switcher */
priv->switch_desc.usb2_port = dwc3_meson_g12_find_child(dev,
"snps,dwc3");
priv->switch_desc.udc = dwc3_meson_g12_find_child(dev, "snps,dwc2");
priv->switch_desc.allow_userspace_control = true;
priv->switch_desc.set = dwc3_meson_g12a_role_set;
priv->switch_desc.get = dwc3_meson_g12a_role_get;
priv->switch_desc.driver_data = priv;
priv->role_switch = usb_role_switch_register(dev, &priv->switch_desc);
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;
enum phy_mode otg_id;
int ret, i, irq;
int ret, i;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
......@@ -410,17 +485,18 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
priv->vbus = NULL;
}
priv->clk = devm_clk_get(dev, NULL);
if (IS_ERR(priv->clk))
return PTR_ERR(priv->clk);
priv->drvdata = of_device_get_match_data(&pdev->dev);
ret = clk_prepare_enable(priv->clk);
ret = devm_clk_bulk_get(dev,
priv->drvdata->num_clks,
priv->drvdata->clks);
if (ret)
return ret;
devm_add_action_or_reset(dev,
(void(*)(void *))clk_disable_unprepare,
priv->clk);
ret = clk_bulk_prepare_enable(priv->drvdata->num_clks,
priv->drvdata->clks);
if (ret)
return ret;
platform_set_drvdata(pdev, priv);
priv->dev = dev;
......@@ -434,41 +510,28 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
ret = reset_control_reset(priv->reset);
if (ret)
return ret;
goto err_disable_clks;
ret = dwc3_meson_g12a_get_phys(priv);
if (ret)
return ret;
goto err_disable_clks;
if (priv->vbus) {
ret = regulator_enable(priv->vbus);
if (ret)
return ret;
goto err_disable_clks;
}
/* Get dr_mode */
priv->otg_mode = usb_get_dr_mode(dev);
if (priv->otg_mode == USB_DR_MODE_OTG) {
/* Ack irq before registering */
regmap_update_bits(priv->regmap, USB_R5,
USB_R5_ID_DIG_IRQ, 0);
irq = platform_get_irq(pdev, 0);
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
dwc3_meson_g12a_irq_thread,
IRQF_ONESHOT, pdev->name, priv);
if (ret)
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;
goto err_disable_clks;
}
/* Set PHY Power */
......@@ -479,32 +542,12 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
}
ret = of_platform_populate(np, NULL, NULL, dev);
if (ret) {
clk_disable_unprepare(priv->clk);
if (ret)
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);
if (otg_id != priv->otg_phy_mode) {
if (dwc3_meson_g12a_otg_mode_set(priv, otg_id))
dev_warn(dev, "Failed to switch OTG mode\n");
}
}
/* Setup role switcher */
priv->switch_desc.usb2_port = dwc3_meson_g12_find_child(dev,
"snps,dwc3");
priv->switch_desc.udc = dwc3_meson_g12_find_child(dev, "snps,dwc2");
priv->switch_desc.allow_userspace_control = true;
priv->switch_desc.set = dwc3_meson_g12a_role_set;
priv->switch_desc.get = dwc3_meson_g12a_role_get;
priv->switch_desc.driver_data = priv;
priv->role_switch = usb_role_switch_register(dev, &priv->switch_desc);
if (IS_ERR(priv->role_switch))
dev_warn(dev, "Unable to register Role Switch\n");
ret = dwc3_meson_g12a_otg_init(pdev, priv);
if (ret)
goto err_phys_power;
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
......@@ -520,6 +563,10 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
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.
......
// 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
// SPDX-License-Identifier: GPL-2.0
/*
* USB Raw Gadget driver.
* See Documentation/usb/raw-gadget.rst for more details.
*
* Andrey Konovalov <andreyknvl@gmail.com>
*/
#include <linux/compiler.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/kref.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/semaphore.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/wait.h>
#include <linux/usb.h>
#include <linux/usb/ch9.h>
#include <linux/usb/ch11.h>
#include <linux/usb/gadget.h>
#include <uapi/linux/usb/raw_gadget.h>
#define DRIVER_DESC "USB Raw Gadget"
#define DRIVER_NAME "raw-gadget"
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("Andrey Konovalov");
MODULE_LICENSE("GPL");
/*----------------------------------------------------------------------*/
#define RAW_EVENT_QUEUE_SIZE 16
struct raw_event_queue {
/* See the comment in raw_event_queue_fetch() for locking details. */
spinlock_t lock;
struct semaphore sema;
struct usb_raw_event *events[RAW_EVENT_QUEUE_SIZE];
int size;
};
static void raw_event_queue_init(struct raw_event_queue *queue)
{
spin_lock_init(&queue->lock);
sema_init(&queue->sema, 0);
queue->size = 0;
}
static int raw_event_queue_add(struct raw_event_queue *queue,
enum usb_raw_event_type type, size_t length, const void *data)
{
unsigned long flags;
struct usb_raw_event *event;
spin_lock_irqsave(&queue->lock, flags);
if (WARN_ON(queue->size >= RAW_EVENT_QUEUE_SIZE)) {
spin_unlock_irqrestore(&queue->lock, flags);
return -ENOMEM;
}
event = kmalloc(sizeof(*event) + length, GFP_ATOMIC);
if (!event) {
spin_unlock_irqrestore(&queue->lock, flags);
return -ENOMEM;
}
event->type = type;
event->length = length;
if (event->length)
memcpy(&event->data[0], data, length);
queue->events[queue->size] = event;
queue->size++;
up(&queue->sema);
spin_unlock_irqrestore(&queue->lock, flags);
return 0;
}
static struct usb_raw_event *raw_event_queue_fetch(
struct raw_event_queue *queue)
{
unsigned long flags;
struct usb_raw_event *event;
/*
* This function can be called concurrently. We first check that
* there's at least one event queued by decrementing the semaphore,
* and then take the lock to protect queue struct fields.
*/
if (down_interruptible(&queue->sema))
return NULL;
spin_lock_irqsave(&queue->lock, flags);
if (WARN_ON(!queue->size))
return NULL;
event = queue->events[0];
queue->size--;
memmove(&queue->events[0], &queue->events[1],
queue->size * sizeof(queue->events[0]));
spin_unlock_irqrestore(&queue->lock, flags);
return event;
}
static void raw_event_queue_destroy(struct raw_event_queue *queue)
{
int i;
for (i = 0; i < queue->size; i++)
kfree(queue->events[i]);
queue->size = 0;
}
/*----------------------------------------------------------------------*/
struct raw_dev;
#define USB_RAW_MAX_ENDPOINTS 32
enum ep_state {
STATE_EP_DISABLED,
STATE_EP_ENABLED,
};
struct raw_ep {
struct raw_dev *dev;
enum ep_state state;
struct usb_ep *ep;
struct usb_request *req;
bool urb_queued;
bool disabling;
ssize_t status;
};
enum dev_state {
STATE_DEV_INVALID = 0,
STATE_DEV_OPENED,
STATE_DEV_INITIALIZED,
STATE_DEV_RUNNING,
STATE_DEV_CLOSED,
STATE_DEV_FAILED
};
struct raw_dev {
struct kref count;
spinlock_t lock;
const char *udc_name;
struct usb_gadget_driver driver;
/* Reference to misc device: */
struct device *dev;
/* Protected by lock: */
enum dev_state state;
bool gadget_registered;
struct usb_gadget *gadget;
struct usb_request *req;
bool ep0_in_pending;
bool ep0_out_pending;
bool ep0_urb_queued;
ssize_t ep0_status;
struct raw_ep eps[USB_RAW_MAX_ENDPOINTS];
struct completion ep0_done;
struct raw_event_queue queue;
};
static struct raw_dev *dev_new(void)
{
struct raw_dev *dev;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return NULL;
/* Matches kref_put() in raw_release(). */
kref_init(&dev->count);
spin_lock_init(&dev->lock);
init_completion(&dev->ep0_done);
raw_event_queue_init(&dev->queue);
return dev;
}
static void dev_free(struct kref *kref)
{
struct raw_dev *dev = container_of(kref, struct raw_dev, count);
int i;
kfree(dev->udc_name);
kfree(dev->driver.udc_name);
if (dev->req) {
if (dev->ep0_urb_queued)
usb_ep_dequeue(dev->gadget->ep0, dev->req);
usb_ep_free_request(dev->gadget->ep0, dev->req);
}
raw_event_queue_destroy(&dev->queue);
for (i = 0; i < USB_RAW_MAX_ENDPOINTS; i++) {
if (dev->eps[i].state != STATE_EP_ENABLED)
continue;
usb_ep_disable(dev->eps[i].ep);
usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
kfree(dev->eps[i].ep->desc);
dev->eps[i].state = STATE_EP_DISABLED;
}
kfree(dev);
}
/*----------------------------------------------------------------------*/
static int raw_queue_event(struct raw_dev *dev,
enum usb_raw_event_type type, size_t length, const void *data)
{
int ret = 0;
unsigned long flags;
ret = raw_event_queue_add(&dev->queue, type, length, data);
if (ret < 0) {
spin_lock_irqsave(&dev->lock, flags);
dev->state = STATE_DEV_FAILED;
spin_unlock_irqrestore(&dev->lock, flags);
}
return ret;
}
static void gadget_ep0_complete(struct usb_ep *ep, struct usb_request *req)
{
struct raw_dev *dev = req->context;
unsigned long flags;
spin_lock_irqsave(&dev->lock, flags);
if (req->status)
dev->ep0_status = req->status;
else
dev->ep0_status = req->actual;
if (dev->ep0_in_pending)
dev->ep0_in_pending = false;
else
dev->ep0_out_pending = false;
spin_unlock_irqrestore(&dev->lock, flags);
complete(&dev->ep0_done);
}
static int gadget_bind(struct usb_gadget *gadget,
struct usb_gadget_driver *driver)
{
int ret = 0;
struct raw_dev *dev = container_of(driver, struct raw_dev, driver);
struct usb_request *req;
unsigned long flags;
if (strcmp(gadget->name, dev->udc_name) != 0)
return -ENODEV;
set_gadget_data(gadget, dev);
req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
if (!req) {
dev_err(&gadget->dev, "usb_ep_alloc_request failed\n");
set_gadget_data(gadget, NULL);
return -ENOMEM;
}
spin_lock_irqsave(&dev->lock, flags);
dev->req = req;
dev->req->context = dev;
dev->req->complete = gadget_ep0_complete;
dev->gadget = gadget;
spin_unlock_irqrestore(&dev->lock, flags);
/* Matches kref_put() in gadget_unbind(). */
kref_get(&dev->count);
ret = raw_queue_event(dev, USB_RAW_EVENT_CONNECT, 0, NULL);
if (ret < 0)
dev_err(&gadget->dev, "failed to queue event\n");
return ret;
}
static void gadget_unbind(struct usb_gadget *gadget)
{
struct raw_dev *dev = get_gadget_data(gadget);
set_gadget_data(gadget, NULL);
/* Matches kref_get() in gadget_bind(). */
kref_put(&dev->count, dev_free);
}
static int gadget_setup(struct usb_gadget *gadget,
const struct usb_ctrlrequest *ctrl)
{
int ret = 0;
struct raw_dev *dev = get_gadget_data(gadget);
unsigned long flags;
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_RUNNING) {
dev_err(&gadget->dev, "ignoring, device is not running\n");
ret = -ENODEV;
goto out_unlock;
}
if (dev->ep0_in_pending || dev->ep0_out_pending) {
dev_dbg(&gadget->dev, "stalling, request already pending\n");
ret = -EBUSY;
goto out_unlock;
}
if ((ctrl->bRequestType & USB_DIR_IN) && ctrl->wLength)
dev->ep0_in_pending = true;
else
dev->ep0_out_pending = true;
spin_unlock_irqrestore(&dev->lock, flags);
ret = raw_queue_event(dev, USB_RAW_EVENT_CONTROL, sizeof(*ctrl), ctrl);
if (ret < 0)
dev_err(&gadget->dev, "failed to queue event\n");
goto out;
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
out:
return ret;
}
/* These are currently unused but present in case UDC driver requires them. */
static void gadget_disconnect(struct usb_gadget *gadget) { }
static void gadget_suspend(struct usb_gadget *gadget) { }
static void gadget_resume(struct usb_gadget *gadget) { }
static void gadget_reset(struct usb_gadget *gadget) { }
/*----------------------------------------------------------------------*/
static struct miscdevice raw_misc_device;
static int raw_open(struct inode *inode, struct file *fd)
{
struct raw_dev *dev;
/* Nonblocking I/O is not supported yet. */
if (fd->f_flags & O_NONBLOCK)
return -EINVAL;
dev = dev_new();
if (!dev)
return -ENOMEM;
fd->private_data = dev;
dev->state = STATE_DEV_OPENED;
dev->dev = raw_misc_device.this_device;
return 0;
}
static int raw_release(struct inode *inode, struct file *fd)
{
int ret = 0;
struct raw_dev *dev = fd->private_data;
unsigned long flags;
bool unregister = false;
spin_lock_irqsave(&dev->lock, flags);
dev->state = STATE_DEV_CLOSED;
if (!dev->gadget) {
spin_unlock_irqrestore(&dev->lock, flags);
goto out_put;
}
if (dev->gadget_registered)
unregister = true;
dev->gadget_registered = false;
spin_unlock_irqrestore(&dev->lock, flags);
if (unregister) {
ret = usb_gadget_unregister_driver(&dev->driver);
if (ret != 0)
dev_err(dev->dev,
"usb_gadget_unregister_driver() failed with %d\n",
ret);
/* Matches kref_get() in raw_ioctl_run(). */
kref_put(&dev->count, dev_free);
}
out_put:
/* Matches dev_new() in raw_open(). */
kref_put(&dev->count, dev_free);
return ret;
}
/*----------------------------------------------------------------------*/
static int raw_ioctl_init(struct raw_dev *dev, unsigned long value)
{
int ret = 0;
struct usb_raw_init arg;
char *udc_driver_name;
char *udc_device_name;
unsigned long flags;
ret = copy_from_user(&arg, (void __user *)value, sizeof(arg));
if (ret)
return ret;
switch (arg.speed) {
case USB_SPEED_UNKNOWN:
arg.speed = USB_SPEED_HIGH;
break;
case USB_SPEED_LOW:
case USB_SPEED_FULL:
case USB_SPEED_HIGH:
case USB_SPEED_SUPER:
break;
default:
return -EINVAL;
}
udc_driver_name = kmalloc(UDC_NAME_LENGTH_MAX, GFP_KERNEL);
if (!udc_driver_name)
return -ENOMEM;
ret = strscpy(udc_driver_name, &arg.driver_name[0],
UDC_NAME_LENGTH_MAX);
if (ret < 0) {
kfree(udc_driver_name);
return ret;
}
ret = 0;
udc_device_name = kmalloc(UDC_NAME_LENGTH_MAX, GFP_KERNEL);
if (!udc_device_name) {
kfree(udc_driver_name);
return -ENOMEM;
}
ret = strscpy(udc_device_name, &arg.device_name[0],
UDC_NAME_LENGTH_MAX);
if (ret < 0) {
kfree(udc_driver_name);
kfree(udc_device_name);
return ret;
}
ret = 0;
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_OPENED) {
dev_dbg(dev->dev, "fail, device is not opened\n");
kfree(udc_driver_name);
kfree(udc_device_name);
ret = -EINVAL;
goto out_unlock;
}
dev->udc_name = udc_driver_name;
dev->driver.function = DRIVER_DESC;
dev->driver.max_speed = arg.speed;
dev->driver.setup = gadget_setup;
dev->driver.disconnect = gadget_disconnect;
dev->driver.bind = gadget_bind;
dev->driver.unbind = gadget_unbind;
dev->driver.suspend = gadget_suspend;
dev->driver.resume = gadget_resume;
dev->driver.reset = gadget_reset;
dev->driver.driver.name = DRIVER_NAME;
dev->driver.udc_name = udc_device_name;
dev->driver.match_existing_only = 1;
dev->state = STATE_DEV_INITIALIZED;
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
}
static int raw_ioctl_run(struct raw_dev *dev, unsigned long value)
{
int ret = 0;
unsigned long flags;
if (value)
return -EINVAL;
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_INITIALIZED) {
dev_dbg(dev->dev, "fail, device is not initialized\n");
ret = -EINVAL;
goto out_unlock;
}
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_gadget_probe_driver(&dev->driver);
spin_lock_irqsave(&dev->lock, flags);
if (ret) {
dev_err(dev->dev,
"fail, usb_gadget_probe_driver returned %d\n", ret);
dev->state = STATE_DEV_FAILED;
goto out_unlock;
}
dev->gadget_registered = true;
dev->state = STATE_DEV_RUNNING;
/* Matches kref_put() in raw_release(). */
kref_get(&dev->count);
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
}
static int raw_ioctl_event_fetch(struct raw_dev *dev, unsigned long value)
{
int ret = 0;
struct usb_raw_event arg;
unsigned long flags;
struct usb_raw_event *event;
uint32_t length;
ret = copy_from_user(&arg, (void __user *)value, sizeof(arg));
if (ret)
return ret;
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_RUNNING) {
dev_dbg(dev->dev, "fail, device is not running\n");
spin_unlock_irqrestore(&dev->lock, flags);
return -EINVAL;
}
if (!dev->gadget) {
dev_dbg(dev->dev, "fail, gadget is not bound\n");
spin_unlock_irqrestore(&dev->lock, flags);
return -EBUSY;
}
spin_unlock_irqrestore(&dev->lock, flags);
event = raw_event_queue_fetch(&dev->queue);
if (!event) {
dev_dbg(&dev->gadget->dev, "event fetching interrupted\n");
return -EINTR;
}
length = min(arg.length, event->length);
ret = copy_to_user((void __user *)value, event,
sizeof(*event) + length);
return ret;
}
static void *raw_alloc_io_data(struct usb_raw_ep_io *io, void __user *ptr,
bool get_from_user)
{
int ret;
void *data;
ret = copy_from_user(io, ptr, sizeof(*io));
if (ret)
return ERR_PTR(ret);
if (io->ep >= USB_RAW_MAX_ENDPOINTS)
return ERR_PTR(-EINVAL);
if (!usb_raw_io_flags_valid(io->flags))
return ERR_PTR(-EINVAL);
if (io->length > PAGE_SIZE)
return ERR_PTR(-EINVAL);
if (get_from_user)
data = memdup_user(ptr + sizeof(*io), io->length);
else {
data = kmalloc(io->length, GFP_KERNEL);
if (!data)
data = ERR_PTR(-ENOMEM);
}
return data;
}
static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
void *data, bool in)
{
int ret = 0;
unsigned long flags;
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_RUNNING) {
dev_dbg(dev->dev, "fail, device is not running\n");
ret = -EINVAL;
goto out_unlock;
}
if (!dev->gadget) {
dev_dbg(dev->dev, "fail, gadget is not bound\n");
ret = -EBUSY;
goto out_unlock;
}
if (dev->ep0_urb_queued) {
dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
ret = -EBUSY;
goto out_unlock;
}
if ((in && !dev->ep0_in_pending) ||
(!in && !dev->ep0_out_pending)) {
dev_dbg(&dev->gadget->dev, "fail, wrong direction\n");
ret = -EBUSY;
goto out_unlock;
}
if (WARN_ON(in && dev->ep0_out_pending)) {
ret = -ENODEV;
dev->state = STATE_DEV_FAILED;
goto out_done;
}
if (WARN_ON(!in && dev->ep0_in_pending)) {
ret = -ENODEV;
dev->state = STATE_DEV_FAILED;
goto out_done;
}
dev->req->buf = data;
dev->req->length = io->length;
dev->req->zero = usb_raw_io_flags_zero(io->flags);
dev->ep0_urb_queued = true;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
if (ret) {
dev_err(&dev->gadget->dev,
"fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
dev->state = STATE_DEV_FAILED;
goto out_done;
}
ret = wait_for_completion_interruptible(&dev->ep0_done);
if (ret) {
dev_dbg(&dev->gadget->dev, "wait interrupted\n");
usb_ep_dequeue(dev->gadget->ep0, dev->req);
wait_for_completion(&dev->ep0_done);
spin_lock_irqsave(&dev->lock, flags);
goto out_done;
}
spin_lock_irqsave(&dev->lock, flags);
ret = dev->ep0_status;
out_done:
dev->ep0_urb_queued = false;
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
}
static int raw_ioctl_ep0_write(struct raw_dev *dev, unsigned long value)
{
int ret = 0;
void *data;
struct usb_raw_ep_io io;
data = raw_alloc_io_data(&io, (void __user *)value, true);
if (IS_ERR(data))
return PTR_ERR(data);
ret = raw_process_ep0_io(dev, &io, data, true);
kfree(data);
return ret;
}
static int raw_ioctl_ep0_read(struct raw_dev *dev, unsigned long value)
{
int ret = 0;
void *data;
struct usb_raw_ep_io io;
unsigned int length;
data = raw_alloc_io_data(&io, (void __user *)value, false);
if (IS_ERR(data))
return PTR_ERR(data);
ret = raw_process_ep0_io(dev, &io, data, false);
if (ret < 0) {
kfree(data);
return ret;
}
length = min(io.length, (unsigned int)ret);
ret = copy_to_user((void __user *)(value + sizeof(io)), data, length);
kfree(data);
return ret;
}
static bool check_ep_caps(struct usb_ep *ep,
struct usb_endpoint_descriptor *desc)
{
switch (usb_endpoint_type(desc)) {
case USB_ENDPOINT_XFER_ISOC:
if (!ep->caps.type_iso)
return false;
break;
case USB_ENDPOINT_XFER_BULK:
if (!ep->caps.type_bulk)
return false;
break;
case USB_ENDPOINT_XFER_INT:
if (!ep->caps.type_int)
return false;
break;
default:
return false;
}
if (usb_endpoint_dir_in(desc) && !ep->caps.dir_in)
return false;
if (usb_endpoint_dir_out(desc) && !ep->caps.dir_out)
return false;
return true;
}
static int raw_ioctl_ep_enable(struct raw_dev *dev, unsigned long value)
{
int ret = 0, i;
unsigned long flags;
struct usb_endpoint_descriptor *desc;
struct usb_ep *ep = NULL;
desc = memdup_user((void __user *)value, sizeof(*desc));
if (IS_ERR(desc))
return PTR_ERR(desc);
/*
* Endpoints with a maxpacket length of 0 can cause crashes in UDC
* drivers.
*/
if (usb_endpoint_maxp(desc) == 0) {
dev_dbg(dev->dev, "fail, bad endpoint maxpacket\n");
kfree(desc);
return -EINVAL;
}
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_RUNNING) {
dev_dbg(dev->dev, "fail, device is not running\n");
ret = -EINVAL;
goto out_free;
}
if (!dev->gadget) {
dev_dbg(dev->dev, "fail, gadget is not bound\n");
ret = -EBUSY;
goto out_free;
}
for (i = 0; i < USB_RAW_MAX_ENDPOINTS; i++) {
if (dev->eps[i].state == STATE_EP_ENABLED)
continue;
break;
}
if (i == USB_RAW_MAX_ENDPOINTS) {
dev_dbg(&dev->gadget->dev,
"fail, no device endpoints available\n");
ret = -EBUSY;
goto out_free;
}
gadget_for_each_ep(ep, dev->gadget) {
if (ep->enabled)
continue;
if (!check_ep_caps(ep, desc))
continue;
ep->desc = desc;
ret = usb_ep_enable(ep);
if (ret < 0) {
dev_err(&dev->gadget->dev,
"fail, usb_ep_enable returned %d\n", ret);
goto out_free;
}
dev->eps[i].req = usb_ep_alloc_request(ep, GFP_ATOMIC);
if (!dev->eps[i].req) {
dev_err(&dev->gadget->dev,
"fail, usb_ep_alloc_request failed\n");
usb_ep_disable(ep);
ret = -ENOMEM;
goto out_free;
}
dev->eps[i].ep = ep;
dev->eps[i].state = STATE_EP_ENABLED;
ep->driver_data = &dev->eps[i];
ret = i;
goto out_unlock;
}
dev_dbg(&dev->gadget->dev, "fail, no gadget endpoints available\n");
ret = -EBUSY;
out_free:
kfree(desc);
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
}
static int raw_ioctl_ep_disable(struct raw_dev *dev, unsigned long value)
{
int ret = 0, i = value;
unsigned long flags;
const void *desc;
if (i < 0 || i >= USB_RAW_MAX_ENDPOINTS)
return -EINVAL;
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_RUNNING) {
dev_dbg(dev->dev, "fail, device is not running\n");
ret = -EINVAL;
goto out_unlock;
}
if (!dev->gadget) {
dev_dbg(dev->dev, "fail, gadget is not bound\n");
ret = -EBUSY;
goto out_unlock;
}
if (dev->eps[i].state != STATE_EP_ENABLED) {
dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
ret = -EINVAL;
goto out_unlock;
}
if (dev->eps[i].disabling) {
dev_dbg(&dev->gadget->dev,
"fail, disable already in progress\n");
ret = -EINVAL;
goto out_unlock;
}
if (dev->eps[i].urb_queued) {
dev_dbg(&dev->gadget->dev,
"fail, waiting for urb completion\n");
ret = -EINVAL;
goto out_unlock;
}
dev->eps[i].disabling = true;
spin_unlock_irqrestore(&dev->lock, flags);
usb_ep_disable(dev->eps[i].ep);
spin_lock_irqsave(&dev->lock, flags);
usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
desc = dev->eps[i].ep->desc;
dev->eps[i].ep = NULL;
dev->eps[i].state = STATE_EP_DISABLED;
kfree(desc);
dev->eps[i].disabling = false;
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
}
static void gadget_ep_complete(struct usb_ep *ep, struct usb_request *req)
{
struct raw_ep *r_ep = (struct raw_ep *)ep->driver_data;
struct raw_dev *dev = r_ep->dev;
unsigned long flags;
spin_lock_irqsave(&dev->lock, flags);
if (req->status)
r_ep->status = req->status;
else
r_ep->status = req->actual;
spin_unlock_irqrestore(&dev->lock, flags);
complete((struct completion *)req->context);
}
static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
void *data, bool in)
{
int ret = 0;
unsigned long flags;
struct raw_ep *ep = &dev->eps[io->ep];
DECLARE_COMPLETION_ONSTACK(done);
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_RUNNING) {
dev_dbg(dev->dev, "fail, device is not running\n");
ret = -EINVAL;
goto out_unlock;
}
if (!dev->gadget) {
dev_dbg(dev->dev, "fail, gadget is not bound\n");
ret = -EBUSY;
goto out_unlock;
}
if (ep->state != STATE_EP_ENABLED) {
dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
ret = -EBUSY;
goto out_unlock;
}
if (ep->disabling) {
dev_dbg(&dev->gadget->dev,
"fail, endpoint is already being disabled\n");
ret = -EBUSY;
goto out_unlock;
}
if (ep->urb_queued) {
dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
ret = -EBUSY;
goto out_unlock;
}
if ((in && !ep->ep->caps.dir_in) || (!in && ep->ep->caps.dir_in)) {
dev_dbg(&dev->gadget->dev, "fail, wrong direction\n");
ret = -EINVAL;
goto out_unlock;
}
ep->dev = dev;
ep->req->context = &done;
ep->req->complete = gadget_ep_complete;
ep->req->buf = data;
ep->req->length = io->length;
ep->req->zero = usb_raw_io_flags_zero(io->flags);
ep->urb_queued = true;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(ep->ep, ep->req, GFP_KERNEL);
if (ret) {
dev_err(&dev->gadget->dev,
"fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
dev->state = STATE_DEV_FAILED;
goto out_done;
}
ret = wait_for_completion_interruptible(&done);
if (ret) {
dev_dbg(&dev->gadget->dev, "wait interrupted\n");
usb_ep_dequeue(ep->ep, ep->req);
wait_for_completion(&done);
spin_lock_irqsave(&dev->lock, flags);
goto out_done;
}
spin_lock_irqsave(&dev->lock, flags);
ret = ep->status;
out_done:
ep->urb_queued = false;
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
}
static int raw_ioctl_ep_write(struct raw_dev *dev, unsigned long value)
{
int ret = 0;
char *data;
struct usb_raw_ep_io io;
data = raw_alloc_io_data(&io, (void __user *)value, true);
if (IS_ERR(data))
return PTR_ERR(data);
ret = raw_process_ep_io(dev, &io, data, true);
kfree(data);
return ret;
}
static int raw_ioctl_ep_read(struct raw_dev *dev, unsigned long value)
{
int ret = 0;
char *data;
struct usb_raw_ep_io io;
unsigned int length;
data = raw_alloc_io_data(&io, (void __user *)value, false);
if (IS_ERR(data))
return PTR_ERR(data);
ret = raw_process_ep_io(dev, &io, data, false);
if (ret < 0) {
kfree(data);
return ret;
}
length = min(io.length, (unsigned int)ret);
ret = copy_to_user((void __user *)(value + sizeof(io)), data, length);
kfree(data);
return ret;
}
static int raw_ioctl_configure(struct raw_dev *dev, unsigned long value)
{
int ret = 0;
unsigned long flags;
if (value)
return -EINVAL;
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_RUNNING) {
dev_dbg(dev->dev, "fail, device is not running\n");
ret = -EINVAL;
goto out_unlock;
}
if (!dev->gadget) {
dev_dbg(dev->dev, "fail, gadget is not bound\n");
ret = -EBUSY;
goto out_unlock;
}
usb_gadget_set_state(dev->gadget, USB_STATE_CONFIGURED);
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
}
static int raw_ioctl_vbus_draw(struct raw_dev *dev, unsigned long value)
{
int ret = 0;
unsigned long flags;
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_RUNNING) {
dev_dbg(dev->dev, "fail, device is not running\n");
ret = -EINVAL;
goto out_unlock;
}
if (!dev->gadget) {
dev_dbg(dev->dev, "fail, gadget is not bound\n");
ret = -EBUSY;
goto out_unlock;
}
usb_gadget_vbus_draw(dev->gadget, 2 * value);
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
}
static long raw_ioctl(struct file *fd, unsigned int cmd, unsigned long value)
{
struct raw_dev *dev = fd->private_data;
int ret = 0;
if (!dev)
return -EBUSY;
switch (cmd) {
case USB_RAW_IOCTL_INIT:
ret = raw_ioctl_init(dev, value);
break;
case USB_RAW_IOCTL_RUN:
ret = raw_ioctl_run(dev, value);
break;
case USB_RAW_IOCTL_EVENT_FETCH:
ret = raw_ioctl_event_fetch(dev, value);
break;
case USB_RAW_IOCTL_EP0_WRITE:
ret = raw_ioctl_ep0_write(dev, value);
break;
case USB_RAW_IOCTL_EP0_READ:
ret = raw_ioctl_ep0_read(dev, value);
break;
case USB_RAW_IOCTL_EP_ENABLE:
ret = raw_ioctl_ep_enable(dev, value);
break;
case USB_RAW_IOCTL_EP_DISABLE:
ret = raw_ioctl_ep_disable(dev, value);
break;
case USB_RAW_IOCTL_EP_WRITE:
ret = raw_ioctl_ep_write(dev, value);
break;
case USB_RAW_IOCTL_EP_READ:
ret = raw_ioctl_ep_read(dev, value);
break;
case USB_RAW_IOCTL_CONFIGURE:
ret = raw_ioctl_configure(dev, value);
break;
case USB_RAW_IOCTL_VBUS_DRAW:
ret = raw_ioctl_vbus_draw(dev, value);
break;
default:
ret = -EINVAL;
}
return ret;
}
/*----------------------------------------------------------------------*/
static const struct file_operations raw_fops = {
.open = raw_open,
.unlocked_ioctl = raw_ioctl,
.compat_ioctl = raw_ioctl,
.release = raw_release,
.llseek = no_llseek,
};
static struct miscdevice raw_misc_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = DRIVER_NAME,
.fops = &raw_fops,
};
module_misc_device(raw_misc_device);
......@@ -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 */
......
// SPDX-License-Identifier: GPL-2.0+
/*
* MAX3420 Device Controller driver for USB.
*
* Author: Jaswinder Singh Brar <jaswinder.singh@linaro.org>
* (C) Copyright 2019-2020 Linaro Ltd
*
* Based on:
* o MAX3420E datasheet
* http://datasheets.maximintegrated.com/en/ds/MAX3420E.pdf
* o MAX342{0,1}E Programming Guides
* https://pdfserv.maximintegrated.com/en/an/AN3598.pdf
* https://pdfserv.maximintegrated.com/en/an/AN3785.pdf
*/
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/bitfield.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/of_irq.h>
#include <linux/prefetch.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/spi/spi.h>
#include <linux/gpio/consumer.h>
#define MAX3420_MAX_EPS 4
#define MAX3420_EP_MAX_PACKET 64 /* Same for all Endpoints */
#define MAX3420_EPNAME_SIZE 16 /* Buffer size for endpoint name */
#define MAX3420_ACKSTAT BIT(0)
#define MAX3420_SPI_DIR_RD 0 /* read register from MAX3420 */
#define MAX3420_SPI_DIR_WR 1 /* write register to MAX3420 */
/* SPI commands: */
#define MAX3420_SPI_DIR_SHIFT 1
#define MAX3420_SPI_REG_SHIFT 3
#define MAX3420_REG_EP0FIFO 0
#define MAX3420_REG_EP1FIFO 1
#define MAX3420_REG_EP2FIFO 2
#define MAX3420_REG_EP3FIFO 3
#define MAX3420_REG_SUDFIFO 4
#define MAX3420_REG_EP0BC 5
#define MAX3420_REG_EP1BC 6
#define MAX3420_REG_EP2BC 7
#define MAX3420_REG_EP3BC 8
#define MAX3420_REG_EPSTALLS 9
#define ACKSTAT BIT(6)
#define STLSTAT BIT(5)
#define STLEP3IN BIT(4)
#define STLEP2IN BIT(3)
#define STLEP1OUT BIT(2)
#define STLEP0OUT BIT(1)
#define STLEP0IN BIT(0)
#define MAX3420_REG_CLRTOGS 10
#define EP3DISAB BIT(7)
#define EP2DISAB BIT(6)
#define EP1DISAB BIT(5)
#define CTGEP3IN BIT(4)
#define CTGEP2IN BIT(3)
#define CTGEP1OUT BIT(2)
#define MAX3420_REG_EPIRQ 11
#define MAX3420_REG_EPIEN 12
#define SUDAVIRQ BIT(5)
#define IN3BAVIRQ BIT(4)
#define IN2BAVIRQ BIT(3)
#define OUT1DAVIRQ BIT(2)
#define OUT0DAVIRQ BIT(1)
#define IN0BAVIRQ BIT(0)
#define MAX3420_REG_USBIRQ 13
#define MAX3420_REG_USBIEN 14
#define OSCOKIRQ BIT(0)
#define RWUDNIRQ BIT(1)
#define BUSACTIRQ BIT(2)
#define URESIRQ BIT(3)
#define SUSPIRQ BIT(4)
#define NOVBUSIRQ BIT(5)
#define VBUSIRQ BIT(6)
#define URESDNIRQ BIT(7)
#define MAX3420_REG_USBCTL 15
#define HOSCSTEN BIT(7)
#define VBGATE BIT(6)
#define CHIPRES BIT(5)
#define PWRDOWN BIT(4)
#define CONNECT BIT(3)
#define SIGRWU BIT(2)
#define MAX3420_REG_CPUCTL 16
#define IE BIT(0)
#define MAX3420_REG_PINCTL 17
#define EP3INAK BIT(7)
#define EP2INAK BIT(6)
#define EP0INAK BIT(5)
#define FDUPSPI BIT(4)
#define INTLEVEL BIT(3)
#define POSINT BIT(2)
#define GPXB BIT(1)
#define GPXA BIT(0)
#define MAX3420_REG_REVISION 18
#define MAX3420_REG_FNADDR 19
#define FNADDR_MASK 0x7f
#define MAX3420_REG_IOPINS 20
#define MAX3420_REG_IOPINS2 21
#define MAX3420_REG_GPINIRQ 22
#define MAX3420_REG_GPINIEN 23
#define MAX3420_REG_GPINPOL 24
#define MAX3420_REG_HIRQ 25
#define MAX3420_REG_HIEN 26
#define MAX3420_REG_MODE 27
#define MAX3420_REG_PERADDR 28
#define MAX3420_REG_HCTL 29
#define MAX3420_REG_HXFR 30
#define MAX3420_REG_HRSL 31
#define ENABLE_IRQ BIT(0)
#define IOPIN_UPDATE BIT(1)
#define REMOTE_WAKEUP BIT(2)
#define CONNECT_HOST GENMASK(4, 3)
#define HCONNECT (1 << 3)
#define HDISCONNECT (3 << 3)
#define UDC_START GENMASK(6, 5)
#define START (1 << 5)
#define STOP (3 << 5)
#define ENABLE_EP GENMASK(8, 7)
#define ENABLE (1 << 7)
#define DISABLE (3 << 7)
#define STALL_EP GENMASK(10, 9)
#define STALL (1 << 9)
#define UNSTALL (3 << 9)
#define MAX3420_CMD(c) FIELD_PREP(GENMASK(7, 3), c)
#define MAX3420_SPI_CMD_RD(c) (MAX3420_CMD(c) | (0 << 1))
#define MAX3420_SPI_CMD_WR(c) (MAX3420_CMD(c) | (1 << 1))
struct max3420_req {
struct usb_request usb_req;
struct list_head queue;
struct max3420_ep *ep;
};
struct max3420_ep {
struct usb_ep ep_usb;
struct max3420_udc *udc;
struct list_head queue;
char name[MAX3420_EPNAME_SIZE];
unsigned int maxpacket;
spinlock_t lock;
int halted;
u32 todo;
int id;
};
struct max3420_udc {
struct usb_gadget gadget;
struct max3420_ep ep[MAX3420_MAX_EPS];
struct usb_gadget_driver *driver;
struct task_struct *thread_task;
int remote_wkp, is_selfpowered;
bool vbus_active, softconnect;
struct usb_ctrlrequest setup;
struct mutex spi_bus_mutex;
struct max3420_req ep0req;
struct spi_device *spi;
struct device *dev;
spinlock_t lock;
bool suspended;
u8 ep0buf[64];
u32 todo;
};
#define to_max3420_req(r) container_of((r), struct max3420_req, usb_req)
#define to_max3420_ep(e) container_of((e), struct max3420_ep, ep_usb)
#define to_udc(g) container_of((g), struct max3420_udc, gadget)
#define DRIVER_DESC "MAX3420 USB Device-Mode Driver"
static const char driver_name[] = "max3420-udc";
/* Control endpoint configuration.*/
static const struct usb_endpoint_descriptor ep0_desc = {
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_CONTROL,
.wMaxPacketSize = cpu_to_le16(MAX3420_EP_MAX_PACKET),
};
static void spi_ack_ctrl(struct max3420_udc *udc)
{
struct spi_device *spi = udc->spi;
struct spi_transfer transfer;
struct spi_message msg;
u8 txdata[1];
memset(&transfer, 0, sizeof(transfer));
spi_message_init(&msg);
txdata[0] = MAX3420_ACKSTAT;
transfer.tx_buf = txdata;
transfer.len = 1;
spi_message_add_tail(&transfer, &msg);
spi_sync(spi, &msg);
}
static u8 spi_rd8_ack(struct max3420_udc *udc, u8 reg, int actstat)
{
struct spi_device *spi = udc->spi;
struct spi_transfer transfer;
struct spi_message msg;
u8 txdata[2], rxdata[2];
memset(&transfer, 0, sizeof(transfer));
spi_message_init(&msg);
txdata[0] = MAX3420_SPI_CMD_RD(reg) | (actstat ? MAX3420_ACKSTAT : 0);
transfer.tx_buf = txdata;
transfer.rx_buf = rxdata;
transfer.len = 2;
spi_message_add_tail(&transfer, &msg);
spi_sync(spi, &msg);
return rxdata[1];
}
static u8 spi_rd8(struct max3420_udc *udc, u8 reg)
{
return spi_rd8_ack(udc, reg, 0);
}
static void spi_wr8_ack(struct max3420_udc *udc, u8 reg, u8 val, int actstat)
{
struct spi_device *spi = udc->spi;
struct spi_transfer transfer;
struct spi_message msg;
u8 txdata[2];
memset(&transfer, 0, sizeof(transfer));
spi_message_init(&msg);
txdata[0] = MAX3420_SPI_CMD_WR(reg) | (actstat ? MAX3420_ACKSTAT : 0);
txdata[1] = val;
transfer.tx_buf = txdata;
transfer.len = 2;
spi_message_add_tail(&transfer, &msg);
spi_sync(spi, &msg);
}
static void spi_wr8(struct max3420_udc *udc, u8 reg, u8 val)
{
spi_wr8_ack(udc, reg, val, 0);
}
static void spi_rd_buf(struct max3420_udc *udc, u8 reg, void *buf, u8 len)
{
struct spi_device *spi = udc->spi;
struct spi_transfer transfer;
struct spi_message msg;
u8 local_buf[MAX3420_EP_MAX_PACKET + 1] = {};
memset(&transfer, 0, sizeof(transfer));
spi_message_init(&msg);
local_buf[0] = MAX3420_SPI_CMD_RD(reg);
transfer.tx_buf = &local_buf[0];
transfer.rx_buf = &local_buf[0];
transfer.len = len + 1;
spi_message_add_tail(&transfer, &msg);
spi_sync(spi, &msg);
memcpy(buf, &local_buf[1], len);
}
static void spi_wr_buf(struct max3420_udc *udc, u8 reg, void *buf, u8 len)
{
struct spi_device *spi = udc->spi;
struct spi_transfer transfer;
struct spi_message msg;
u8 local_buf[MAX3420_EP_MAX_PACKET + 1] = {};
memset(&transfer, 0, sizeof(transfer));
spi_message_init(&msg);
local_buf[0] = MAX3420_SPI_CMD_WR(reg);
memcpy(&local_buf[1], buf, len);
transfer.tx_buf = local_buf;
transfer.len = len + 1;
spi_message_add_tail(&transfer, &msg);
spi_sync(spi, &msg);
}
static int spi_max3420_enable(struct max3420_ep *ep)
{
struct max3420_udc *udc = ep->udc;
unsigned long flags;
u8 epdis, epien;
int todo;
spin_lock_irqsave(&ep->lock, flags);
todo = ep->todo & ENABLE_EP;
ep->todo &= ~ENABLE_EP;
spin_unlock_irqrestore(&ep->lock, flags);
if (!todo || ep->id == 0)
return false;
epien = spi_rd8(udc, MAX3420_REG_EPIEN);
epdis = spi_rd8(udc, MAX3420_REG_CLRTOGS);
if (todo == ENABLE) {
epdis &= ~BIT(ep->id + 4);
epien |= BIT(ep->id + 1);
} else {
epdis |= BIT(ep->id + 4);
epien &= ~BIT(ep->id + 1);
}
spi_wr8(udc, MAX3420_REG_CLRTOGS, epdis);
spi_wr8(udc, MAX3420_REG_EPIEN, epien);
return true;
}
static int spi_max3420_stall(struct max3420_ep *ep)
{
struct max3420_udc *udc = ep->udc;
unsigned long flags;
u8 epstalls;
int todo;
spin_lock_irqsave(&ep->lock, flags);
todo = ep->todo & STALL_EP;
ep->todo &= ~STALL_EP;
spin_unlock_irqrestore(&ep->lock, flags);
if (!todo || ep->id == 0)
return false;
epstalls = spi_rd8(udc, MAX3420_REG_EPSTALLS);
if (todo == STALL) {
ep->halted = 1;
epstalls |= BIT(ep->id + 1);
} else {
u8 clrtogs;
ep->halted = 0;
epstalls &= ~BIT(ep->id + 1);
clrtogs = spi_rd8(udc, MAX3420_REG_CLRTOGS);
clrtogs |= BIT(ep->id + 1);
spi_wr8(udc, MAX3420_REG_CLRTOGS, clrtogs);
}
spi_wr8(udc, MAX3420_REG_EPSTALLS, epstalls | ACKSTAT);
return true;
}
static int spi_max3420_rwkup(struct max3420_udc *udc)
{
unsigned long flags;
int wake_remote;
u8 usbctl;
spin_lock_irqsave(&udc->lock, flags);
wake_remote = udc->todo & REMOTE_WAKEUP;
udc->todo &= ~REMOTE_WAKEUP;
spin_unlock_irqrestore(&udc->lock, flags);
if (!wake_remote || !udc->suspended)
return false;
/* Set Remote-WkUp Signal*/
usbctl = spi_rd8(udc, MAX3420_REG_USBCTL);
usbctl |= SIGRWU;
spi_wr8(udc, MAX3420_REG_USBCTL, usbctl);
msleep_interruptible(5);
/* Clear Remote-WkUp Signal*/
usbctl = spi_rd8(udc, MAX3420_REG_USBCTL);
usbctl &= ~SIGRWU;
spi_wr8(udc, MAX3420_REG_USBCTL, usbctl);
udc->suspended = false;
return true;
}
static void max3420_nuke(struct max3420_ep *ep, int status);
static void __max3420_stop(struct max3420_udc *udc)
{
u8 val;
int i;
/* clear all pending requests */
for (i = 1; i < MAX3420_MAX_EPS; i++)
max3420_nuke(&udc->ep[i], -ECONNRESET);
/* Disable IRQ to CPU */
spi_wr8(udc, MAX3420_REG_CPUCTL, 0);
val = spi_rd8(udc, MAX3420_REG_USBCTL);
val |= PWRDOWN;
if (udc->is_selfpowered)
val &= ~HOSCSTEN;
else
val |= HOSCSTEN;
spi_wr8(udc, MAX3420_REG_USBCTL, val);
}
static void __max3420_start(struct max3420_udc *udc)
{
u8 val;
/* Need this delay if bus-powered,
* but even for self-powered it helps stability
*/
msleep_interruptible(250);
/* configure SPI */
spi_wr8(udc, MAX3420_REG_PINCTL, FDUPSPI);
/* Chip Reset */
spi_wr8(udc, MAX3420_REG_USBCTL, CHIPRES);
msleep_interruptible(5);
spi_wr8(udc, MAX3420_REG_USBCTL, 0);
/* Poll for OSC to stabilize */
while (1) {
val = spi_rd8(udc, MAX3420_REG_USBIRQ);
if (val & OSCOKIRQ)
break;
cond_resched();
}
/* Enable PULL-UP only when Vbus detected */
val = spi_rd8(udc, MAX3420_REG_USBCTL);
val |= VBGATE | CONNECT;
spi_wr8(udc, MAX3420_REG_USBCTL, val);
val = URESDNIRQ | URESIRQ;
if (udc->is_selfpowered)
val |= NOVBUSIRQ;
spi_wr8(udc, MAX3420_REG_USBIEN, val);
/* Enable only EP0 interrupts */
val = IN0BAVIRQ | OUT0DAVIRQ | SUDAVIRQ;
spi_wr8(udc, MAX3420_REG_EPIEN, val);
/* Enable IRQ to CPU */
spi_wr8(udc, MAX3420_REG_CPUCTL, IE);
}
static int max3420_start(struct max3420_udc *udc)
{
unsigned long flags;
int todo;
spin_lock_irqsave(&udc->lock, flags);
todo = udc->todo & UDC_START;
udc->todo &= ~UDC_START;
spin_unlock_irqrestore(&udc->lock, flags);
if (!todo)
return false;
if (udc->vbus_active && udc->softconnect)
__max3420_start(udc);
else
__max3420_stop(udc);
return true;
}
static irqreturn_t max3420_vbus_handler(int irq, void *dev_id)
{
struct max3420_udc *udc = dev_id;
unsigned long flags;
spin_lock_irqsave(&udc->lock, flags);
/* its a vbus change interrupt */
udc->vbus_active = !udc->vbus_active;
udc->todo |= UDC_START;
usb_udc_vbus_handler(&udc->gadget, udc->vbus_active);
usb_gadget_set_state(&udc->gadget, udc->vbus_active
? USB_STATE_POWERED : USB_STATE_NOTATTACHED);
spin_unlock_irqrestore(&udc->lock, flags);
if (udc->thread_task &&
udc->thread_task->state != TASK_RUNNING)
wake_up_process(udc->thread_task);
return IRQ_HANDLED;
}
static irqreturn_t max3420_irq_handler(int irq, void *dev_id)
{
struct max3420_udc *udc = dev_id;
struct spi_device *spi = udc->spi;
unsigned long flags;
spin_lock_irqsave(&udc->lock, flags);
if ((udc->todo & ENABLE_IRQ) == 0) {
disable_irq_nosync(spi->irq);
udc->todo |= ENABLE_IRQ;
}
spin_unlock_irqrestore(&udc->lock, flags);
if (udc->thread_task &&
udc->thread_task->state != TASK_RUNNING)
wake_up_process(udc->thread_task);
return IRQ_HANDLED;
}
static void max3420_getstatus(struct max3420_udc *udc)
{
struct max3420_ep *ep;
u16 status = 0;
switch (udc->setup.bRequestType & USB_RECIP_MASK) {
case USB_RECIP_DEVICE:
/* Get device status */
status = udc->gadget.is_selfpowered << USB_DEVICE_SELF_POWERED;
status |= (udc->remote_wkp << USB_DEVICE_REMOTE_WAKEUP);
break;
case USB_RECIP_INTERFACE:
if (udc->driver->setup(&udc->gadget, &udc->setup) < 0)
goto stall;
break;
case USB_RECIP_ENDPOINT:
ep = &udc->ep[udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK];
if (udc->setup.wIndex & USB_DIR_IN) {
if (!ep->ep_usb.caps.dir_in)
goto stall;
} else {
if (!ep->ep_usb.caps.dir_out)
goto stall;
}
if (ep->halted)
status = 1 << USB_ENDPOINT_HALT;
break;
default:
goto stall;
}
status = cpu_to_le16(status);
spi_wr_buf(udc, MAX3420_REG_EP0FIFO, &status, 2);
spi_wr8_ack(udc, MAX3420_REG_EP0BC, 2, 1);
return;
stall:
dev_err(udc->dev, "Can't respond to getstatus request\n");
spi_wr8(udc, MAX3420_REG_EPSTALLS, STLEP0IN | STLEP0OUT | STLSTAT);
}
static void max3420_set_clear_feature(struct max3420_udc *udc)
{
struct max3420_ep *ep;
int set = udc->setup.bRequest == USB_REQ_SET_FEATURE;
unsigned long flags;
int id;
switch (udc->setup.bRequestType) {
case USB_RECIP_DEVICE:
if (udc->setup.wValue != USB_DEVICE_REMOTE_WAKEUP)
break;
if (udc->setup.bRequest == USB_REQ_SET_FEATURE)
udc->remote_wkp = 1;
else
udc->remote_wkp = 0;
return spi_ack_ctrl(udc);
case USB_RECIP_ENDPOINT:
if (udc->setup.wValue != USB_ENDPOINT_HALT)
break;
id = udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK;
ep = &udc->ep[id];
spin_lock_irqsave(&ep->lock, flags);
ep->todo &= ~STALL_EP;
if (set)
ep->todo |= STALL;
else
ep->todo |= UNSTALL;
spin_unlock_irqrestore(&ep->lock, flags);
spi_max3420_stall(ep);
return;
default:
break;
}
dev_err(udc->dev, "Can't respond to SET/CLEAR FEATURE\n");
spi_wr8(udc, MAX3420_REG_EPSTALLS, STLEP0IN | STLEP0OUT | STLSTAT);
}
static void max3420_handle_setup(struct max3420_udc *udc)
{
struct usb_ctrlrequest setup;
u8 addr;
spi_rd_buf(udc, MAX3420_REG_SUDFIFO, (void *)&setup, 8);
udc->setup = setup;
udc->setup.wValue = cpu_to_le16(setup.wValue);
udc->setup.wIndex = cpu_to_le16(setup.wIndex);
udc->setup.wLength = cpu_to_le16(setup.wLength);
switch (udc->setup.bRequest) {
case USB_REQ_GET_STATUS:
/* Data+Status phase form udc */
if ((udc->setup.bRequestType &
(USB_DIR_IN | USB_TYPE_MASK)) !=
(USB_DIR_IN | USB_TYPE_STANDARD)) {
break;
}
return max3420_getstatus(udc);
case USB_REQ_SET_ADDRESS:
/* Status phase from udc */
if (udc->setup.bRequestType != (USB_DIR_OUT |
USB_TYPE_STANDARD | USB_RECIP_DEVICE)) {
break;
}
addr = spi_rd8_ack(udc, MAX3420_REG_FNADDR, 1);
dev_dbg(udc->dev, "Assigned Address=%d\n", udc->setup.wValue);
return;
case USB_REQ_CLEAR_FEATURE:
case USB_REQ_SET_FEATURE:
/* Requests with no data phase, status phase from udc */
if ((udc->setup.bRequestType & USB_TYPE_MASK)
!= USB_TYPE_STANDARD)
break;
return max3420_set_clear_feature(udc);
default:
break;
}
if (udc->driver->setup(&udc->gadget, &setup) < 0) {
/* Stall EP0 */
spi_wr8(udc, MAX3420_REG_EPSTALLS,
STLEP0IN | STLEP0OUT | STLSTAT);
}
}
static void max3420_req_done(struct max3420_req *req, int status)
{
struct max3420_ep *ep = req->ep;
struct max3420_udc *udc = ep->udc;
if (req->usb_req.status == -EINPROGRESS)
req->usb_req.status = status;
else
status = req->usb_req.status;
if (status && status != -ESHUTDOWN)
dev_err(udc->dev, "%s done %p, status %d\n",
ep->ep_usb.name, req, status);
if (req->usb_req.complete)
req->usb_req.complete(&ep->ep_usb, &req->usb_req);
}
static int max3420_do_data(struct max3420_udc *udc, int ep_id, int in)
{
struct max3420_ep *ep = &udc->ep[ep_id];
struct max3420_req *req;
int done, length, psz;
void *buf;
if (list_empty(&ep->queue))
return false;
req = list_first_entry(&ep->queue, struct max3420_req, queue);
buf = req->usb_req.buf + req->usb_req.actual;
psz = ep->ep_usb.maxpacket;
length = req->usb_req.length - req->usb_req.actual;
length = min(length, psz);
if (length == 0) {
done = 1;
goto xfer_done;
}
done = 0;
if (in) {
prefetch(buf);
spi_wr_buf(udc, MAX3420_REG_EP0FIFO + ep_id, buf, length);
spi_wr8(udc, MAX3420_REG_EP0BC + ep_id, length);
if (length < psz)
done = 1;
} else {
psz = spi_rd8(udc, MAX3420_REG_EP0BC + ep_id);
length = min(length, psz);
prefetchw(buf);
spi_rd_buf(udc, MAX3420_REG_EP0FIFO + ep_id, buf, length);
if (length < ep->ep_usb.maxpacket)
done = 1;
}
req->usb_req.actual += length;
if (req->usb_req.actual == req->usb_req.length)
done = 1;
xfer_done:
if (done) {
unsigned long flags;
spin_lock_irqsave(&ep->lock, flags);
list_del_init(&req->queue);
spin_unlock_irqrestore(&ep->lock, flags);
if (ep_id == 0)
spi_ack_ctrl(udc);
max3420_req_done(req, 0);
}
return true;
}
static int max3420_handle_irqs(struct max3420_udc *udc)
{
u8 epien, epirq, usbirq, usbien, reg[4];
bool ret = false;
spi_rd_buf(udc, MAX3420_REG_EPIRQ, reg, 4);
epirq = reg[0];
epien = reg[1];
usbirq = reg[2];
usbien = reg[3];
usbirq &= usbien;
epirq &= epien;
if (epirq & SUDAVIRQ) {
spi_wr8(udc, MAX3420_REG_EPIRQ, SUDAVIRQ);
max3420_handle_setup(udc);
return true;
}
if (usbirq & VBUSIRQ) {
spi_wr8(udc, MAX3420_REG_USBIRQ, VBUSIRQ);
dev_dbg(udc->dev, "Cable plugged in\n");
return true;
}
if (usbirq & NOVBUSIRQ) {
spi_wr8(udc, MAX3420_REG_USBIRQ, NOVBUSIRQ);
dev_dbg(udc->dev, "Cable pulled out\n");
return true;
}
if (usbirq & URESIRQ) {
spi_wr8(udc, MAX3420_REG_USBIRQ, URESIRQ);
dev_dbg(udc->dev, "USB Reset - Start\n");
return true;
}
if (usbirq & URESDNIRQ) {
spi_wr8(udc, MAX3420_REG_USBIRQ, URESDNIRQ);
dev_dbg(udc->dev, "USB Reset - END\n");
spi_wr8(udc, MAX3420_REG_USBIEN, URESDNIRQ | URESIRQ);
spi_wr8(udc, MAX3420_REG_EPIEN, SUDAVIRQ | IN0BAVIRQ
| OUT0DAVIRQ);
return true;
}
if (usbirq & SUSPIRQ) {
spi_wr8(udc, MAX3420_REG_USBIRQ, SUSPIRQ);
dev_dbg(udc->dev, "USB Suspend - Enter\n");
udc->suspended = true;
return true;
}
if (usbirq & BUSACTIRQ) {
spi_wr8(udc, MAX3420_REG_USBIRQ, BUSACTIRQ);
dev_dbg(udc->dev, "USB Suspend - Exit\n");
udc->suspended = false;
return true;
}
if (usbirq & RWUDNIRQ) {
spi_wr8(udc, MAX3420_REG_USBIRQ, RWUDNIRQ);
dev_dbg(udc->dev, "Asked Host to wakeup\n");
return true;
}
if (usbirq & OSCOKIRQ) {
spi_wr8(udc, MAX3420_REG_USBIRQ, OSCOKIRQ);
dev_dbg(udc->dev, "Osc stabilized, start work\n");
return true;
}
if (epirq & OUT0DAVIRQ && max3420_do_data(udc, 0, 0)) {
spi_wr8_ack(udc, MAX3420_REG_EPIRQ, OUT0DAVIRQ, 1);
ret = true;
}
if (epirq & IN0BAVIRQ && max3420_do_data(udc, 0, 1))
ret = true;
if (epirq & OUT1DAVIRQ && max3420_do_data(udc, 1, 0)) {
spi_wr8_ack(udc, MAX3420_REG_EPIRQ, OUT1DAVIRQ, 1);
ret = true;
}
if (epirq & IN2BAVIRQ && max3420_do_data(udc, 2, 1))
ret = true;
if (epirq & IN3BAVIRQ && max3420_do_data(udc, 3, 1))
ret = true;
return ret;
}
static int max3420_thread(void *dev_id)
{
struct max3420_udc *udc = dev_id;
struct spi_device *spi = udc->spi;
int i, loop_again = 1;
unsigned long flags;
while (!kthread_should_stop()) {
if (!loop_again) {
ktime_t kt = ns_to_ktime(1000 * 1000 * 250); /* 250ms */
set_current_state(TASK_INTERRUPTIBLE);
spin_lock_irqsave(&udc->lock, flags);
if (udc->todo & ENABLE_IRQ) {
enable_irq(spi->irq);
udc->todo &= ~ENABLE_IRQ;
}
spin_unlock_irqrestore(&udc->lock, flags);
schedule_hrtimeout(&kt, HRTIMER_MODE_REL);
}
loop_again = 0;
mutex_lock(&udc->spi_bus_mutex);
/* If bus-vbus_active and disconnected */
if (!udc->vbus_active || !udc->softconnect)
goto loop;
if (max3420_start(udc)) {
loop_again = 1;
goto loop;
}
if (max3420_handle_irqs(udc)) {
loop_again = 1;
goto loop;
}
if (spi_max3420_rwkup(udc)) {
loop_again = 1;
goto loop;
}
max3420_do_data(udc, 0, 1); /* get done with the EP0 ZLP */
for (i = 1; i < MAX3420_MAX_EPS; i++) {
struct max3420_ep *ep = &udc->ep[i];
if (spi_max3420_enable(ep))
loop_again = 1;
if (spi_max3420_stall(ep))
loop_again = 1;
}
loop:
mutex_unlock(&udc->spi_bus_mutex);
}
set_current_state(TASK_RUNNING);
dev_info(udc->dev, "SPI thread exiting");
return 0;
}
static int max3420_ep_set_halt(struct usb_ep *_ep, int stall)
{
struct max3420_ep *ep = to_max3420_ep(_ep);
struct max3420_udc *udc = ep->udc;
unsigned long flags;
spin_lock_irqsave(&ep->lock, flags);
ep->todo &= ~STALL_EP;
if (stall)
ep->todo |= STALL;
else
ep->todo |= UNSTALL;
spin_unlock_irqrestore(&ep->lock, flags);
wake_up_process(udc->thread_task);
dev_dbg(udc->dev, "%sStall %s\n", stall ? "" : "Un", ep->name);
return 0;
}
static int __max3420_ep_enable(struct max3420_ep *ep,
const struct usb_endpoint_descriptor *desc)
{
unsigned int maxp = usb_endpoint_maxp(desc);
unsigned long flags;
spin_lock_irqsave(&ep->lock, flags);
ep->ep_usb.desc = desc;
ep->ep_usb.maxpacket = maxp;
ep->todo &= ~ENABLE_EP;
ep->todo |= ENABLE;
spin_unlock_irqrestore(&ep->lock, flags);
return 0;
}
static int max3420_ep_enable(struct usb_ep *_ep,
const struct usb_endpoint_descriptor *desc)
{
struct max3420_ep *ep = to_max3420_ep(_ep);
struct max3420_udc *udc = ep->udc;
__max3420_ep_enable(ep, desc);
wake_up_process(udc->thread_task);
return 0;
}
static void max3420_nuke(struct max3420_ep *ep, int status)
{
struct max3420_req *req, *r;
unsigned long flags;
spin_lock_irqsave(&ep->lock, flags);
list_for_each_entry_safe(req, r, &ep->queue, queue) {
list_del_init(&req->queue);
spin_unlock_irqrestore(&ep->lock, flags);
max3420_req_done(req, status);
spin_lock_irqsave(&ep->lock, flags);
}
spin_unlock_irqrestore(&ep->lock, flags);
}
static void __max3420_ep_disable(struct max3420_ep *ep)
{
struct max3420_udc *udc = ep->udc;
unsigned long flags;
spin_lock_irqsave(&ep->lock, flags);
ep->ep_usb.desc = NULL;
ep->todo &= ~ENABLE_EP;
ep->todo |= DISABLE;
spin_unlock_irqrestore(&ep->lock, flags);
dev_dbg(udc->dev, "Disabled %s\n", ep->name);
}
static int max3420_ep_disable(struct usb_ep *_ep)
{
struct max3420_ep *ep = to_max3420_ep(_ep);
struct max3420_udc *udc = ep->udc;
max3420_nuke(ep, -ESHUTDOWN);
__max3420_ep_disable(ep);
wake_up_process(udc->thread_task);
return 0;
}
static struct usb_request *max3420_alloc_request(struct usb_ep *_ep,
gfp_t gfp_flags)
{
struct max3420_ep *ep = to_max3420_ep(_ep);
struct max3420_req *req;
req = kzalloc(sizeof(*req), gfp_flags);
if (!req)
return NULL;
req->ep = ep;
return &req->usb_req;
}
static void max3420_free_request(struct usb_ep *_ep, struct usb_request *_req)
{
kfree(to_max3420_req(_req));
}
static int max3420_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
gfp_t ignored)
{
struct max3420_req *req = to_max3420_req(_req);
struct max3420_ep *ep = to_max3420_ep(_ep);
struct max3420_udc *udc = ep->udc;
unsigned long flags;
_req->status = -EINPROGRESS;
_req->actual = 0;
spin_lock_irqsave(&ep->lock, flags);
list_add_tail(&req->queue, &ep->queue);
spin_unlock_irqrestore(&ep->lock, flags);
wake_up_process(udc->thread_task);
return 0;
}
static int max3420_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
{
struct max3420_req *t, *req = to_max3420_req(_req);
struct max3420_ep *ep = to_max3420_ep(_ep);
unsigned long flags;
spin_lock_irqsave(&ep->lock, flags);
/* Pluck the descriptor from queue */
list_for_each_entry(t, &ep->queue, queue)
if (t == req) {
list_del_init(&req->queue);
break;
}
spin_unlock_irqrestore(&ep->lock, flags);
if (t == req)
max3420_req_done(req, -ECONNRESET);
return 0;
}
static const struct usb_ep_ops max3420_ep_ops = {
.enable = max3420_ep_enable,
.disable = max3420_ep_disable,
.alloc_request = max3420_alloc_request,
.free_request = max3420_free_request,
.queue = max3420_ep_queue,
.dequeue = max3420_ep_dequeue,
.set_halt = max3420_ep_set_halt,
};
static int max3420_wakeup(struct usb_gadget *gadget)
{
struct max3420_udc *udc = to_udc(gadget);
unsigned long flags;
int ret = -EINVAL;
spin_lock_irqsave(&udc->lock, flags);
/* Only if wakeup allowed by host */
if (udc->remote_wkp) {
udc->todo |= REMOTE_WAKEUP;
ret = 0;
}
spin_unlock_irqrestore(&udc->lock, flags);
if (udc->thread_task &&
udc->thread_task->state != TASK_RUNNING)
wake_up_process(udc->thread_task);
return ret;
}
static int max3420_udc_start(struct usb_gadget *gadget,
struct usb_gadget_driver *driver)
{
struct max3420_udc *udc = to_udc(gadget);
unsigned long flags;
spin_lock_irqsave(&udc->lock, flags);
/* hook up the driver */
driver->driver.bus = NULL;
udc->driver = driver;
udc->gadget.speed = USB_SPEED_FULL;
udc->gadget.is_selfpowered = udc->is_selfpowered;
udc->remote_wkp = 0;
udc->softconnect = true;
udc->todo |= UDC_START;
spin_unlock_irqrestore(&udc->lock, flags);
if (udc->thread_task &&
udc->thread_task->state != TASK_RUNNING)
wake_up_process(udc->thread_task);
return 0;
}
static int max3420_udc_stop(struct usb_gadget *gadget)
{
struct max3420_udc *udc = to_udc(gadget);
unsigned long flags;
spin_lock_irqsave(&udc->lock, flags);
udc->is_selfpowered = udc->gadget.is_selfpowered;
udc->gadget.speed = USB_SPEED_UNKNOWN;
udc->driver = NULL;
udc->softconnect = false;
udc->todo |= UDC_START;
spin_unlock_irqrestore(&udc->lock, flags);
if (udc->thread_task &&
udc->thread_task->state != TASK_RUNNING)
wake_up_process(udc->thread_task);
return 0;
}
static const struct usb_gadget_ops max3420_udc_ops = {
.udc_start = max3420_udc_start,
.udc_stop = max3420_udc_stop,
.wakeup = max3420_wakeup,
};
static void max3420_eps_init(struct max3420_udc *udc)
{
int idx;
INIT_LIST_HEAD(&udc->gadget.ep_list);
for (idx = 0; idx < MAX3420_MAX_EPS; idx++) {
struct max3420_ep *ep = &udc->ep[idx];
spin_lock_init(&ep->lock);
INIT_LIST_HEAD(&ep->queue);
ep->udc = udc;
ep->id = idx;
ep->halted = 0;
ep->maxpacket = 0;
ep->ep_usb.name = ep->name;
ep->ep_usb.ops = &max3420_ep_ops;
usb_ep_set_maxpacket_limit(&ep->ep_usb, MAX3420_EP_MAX_PACKET);
if (idx == 0) { /* For EP0 */
ep->ep_usb.desc = &ep0_desc;
ep->ep_usb.maxpacket = usb_endpoint_maxp(&ep0_desc);
ep->ep_usb.caps.type_control = true;
ep->ep_usb.caps.dir_in = true;
ep->ep_usb.caps.dir_out = true;
snprintf(ep->name, MAX3420_EPNAME_SIZE, "ep0");
continue;
}
if (idx == 1) { /* EP1 is OUT */
ep->ep_usb.caps.dir_in = false;
ep->ep_usb.caps.dir_out = true;
snprintf(ep->name, MAX3420_EPNAME_SIZE, "ep1-bulk-out");
} else { /* EP2 & EP3 are IN */
ep->ep_usb.caps.dir_in = true;
ep->ep_usb.caps.dir_out = false;
snprintf(ep->name, MAX3420_EPNAME_SIZE,
"ep%d-bulk-in", idx);
}
ep->ep_usb.caps.type_iso = false;
ep->ep_usb.caps.type_int = false;
ep->ep_usb.caps.type_bulk = true;
list_add_tail(&ep->ep_usb.ep_list,
&udc->gadget.ep_list);
}
}
static int max3420_probe(struct spi_device *spi)
{
struct max3420_udc *udc;
int err, irq;
u8 reg[8];
if (spi->master->flags & SPI_MASTER_HALF_DUPLEX) {
dev_err(&spi->dev, "UDC needs full duplex to work\n");
return -EINVAL;
}
spi->mode = SPI_MODE_3;
spi->bits_per_word = 8;
err = spi_setup(spi);
if (err) {
dev_err(&spi->dev, "Unable to setup SPI bus\n");
return -EFAULT;
}
udc = devm_kzalloc(&spi->dev, sizeof(*udc), GFP_KERNEL);
if (!udc)
return -ENOMEM;
udc->spi = spi;
udc->remote_wkp = 0;
/* Setup gadget structure */
udc->gadget.ops = &max3420_udc_ops;
udc->gadget.max_speed = USB_SPEED_FULL;
udc->gadget.speed = USB_SPEED_UNKNOWN;
udc->gadget.ep0 = &udc->ep[0].ep_usb;
udc->gadget.name = driver_name;
spin_lock_init(&udc->lock);
mutex_init(&udc->spi_bus_mutex);
udc->ep0req.ep = &udc->ep[0];
udc->ep0req.usb_req.buf = udc->ep0buf;
INIT_LIST_HEAD(&udc->ep0req.queue);
/* setup Endpoints */
max3420_eps_init(udc);
/* configure SPI */
spi_rd_buf(udc, MAX3420_REG_EPIRQ, reg, 8);
spi_wr8(udc, MAX3420_REG_PINCTL, FDUPSPI);
err = usb_add_gadget_udc(&spi->dev, &udc->gadget);
if (err)
return err;
udc->dev = &udc->gadget.dev;
spi_set_drvdata(spi, udc);
irq = of_irq_get_byname(spi->dev.of_node, "udc");
err = devm_request_irq(&spi->dev, irq, max3420_irq_handler, 0,
"max3420", udc);
if (err < 0)
return err;
udc->thread_task = kthread_create(max3420_thread, udc,
"max3420-thread");
if (IS_ERR(udc->thread_task))
return PTR_ERR(udc->thread_task);
irq = of_irq_get_byname(spi->dev.of_node, "vbus");
if (irq <= 0) { /* no vbus irq implies self-powered design */
udc->is_selfpowered = 1;
udc->vbus_active = true;
udc->todo |= UDC_START;
usb_udc_vbus_handler(&udc->gadget, udc->vbus_active);
usb_gadget_set_state(&udc->gadget, USB_STATE_POWERED);
max3420_start(udc);
} else {
udc->is_selfpowered = 0;
/* Detect current vbus status */
spi_rd_buf(udc, MAX3420_REG_EPIRQ, reg, 8);
if (reg[7] != 0xff)
udc->vbus_active = true;
err = devm_request_irq(&spi->dev, irq,
max3420_vbus_handler, 0, "vbus", udc);
if (err < 0)
return err;
}
return 0;
}
static int max3420_remove(struct spi_device *spi)
{
struct max3420_udc *udc = spi_get_drvdata(spi);
unsigned long flags;
usb_del_gadget_udc(&udc->gadget);
spin_lock_irqsave(&udc->lock, flags);
kthread_stop(udc->thread_task);
spin_unlock_irqrestore(&udc->lock, flags);
return 0;
}
static const struct of_device_id max3420_udc_of_match[] = {
{ .compatible = "maxim,max3420-udc"},
{ .compatible = "maxim,max3421-udc"},
{},
};
MODULE_DEVICE_TABLE(of, max3420_udc_of_match);
static struct spi_driver max3420_driver = {
.driver = {
.name = "max3420-udc",
.of_match_table = of_match_ptr(max3420_udc_of_match),
},
.probe = max3420_probe,
.remove = max3420_remove,
};
module_spi_driver(max3420_driver);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("Jassi Brar <jaswinder.singh@linaro.org>");
MODULE_LICENSE("GPL");
......@@ -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);
......
/* 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 */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment