forked from Minki/linux
USB: changes for v5.10 merge window
Most of changes are on dwc3 (38.8%) with cdns3 falling close behind (24.1%). The biggest changes here are a series of non-critical fixes to corner cases on dwc3, produced by Thinh N, and a series of major improvements to cdns3 produced by Peter C. We also have the traditional set of new device support (Intel Keem Bay, Hikey 970) on dwc3. A series of sparse/coccinelle and checkpatch fixes on dwc3 by yours truly and a set of minor changes all over the stack. -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEElLzh7wn96CXwjh2IzL64meEamQYFAl93ARkACgkQzL64meEa mQZmexAAjVCwthkyfobTbEEzKVmo33i4GsXmrFMujLLyFoF/EIH99bf/Y81ljv6N +krqeyUTBXv9CB0tRHgvJY9oR9clcqW9MKB8Gb61KOOyVw8jFJIGjUmqqzYWsyC0 q1kmue4zf9lWgTCaUygwrok/gC7wHFRqDHYn4admF+Q6NmlB+3W4J5on8kQcIOwG ulnlYbGm9NGv2aPKyJ9pxsN0DpELCs/8C67vr9bSny1cQa31OTL3eesMQzUVjJn0 y/PuNWJbSYzPq5lZPG2S5B7owHWWVIWlT5ZLjiYJXBl1BhIgqUakliyjE6ftsiIr V54qWKL7U43FzrFVAPrrhRgFiknso/qpzXDMzJA7mHDtjCIZ/VXXOPNUvw5ufSUZ uilqAPUYvjdRAPM0VxUKItGHgOmlDd8zxFgn3M9YxiBObZT1zdBYT8ZKoE66x0we qqlzbY0txVJYVbmNYMFZNToMf5g7LXSBZ0dsuAp9Ca58Zf7PanK677wemiP6PpAS hwTxWkc/VHxoqUOxzV8rTvjgKh5dNO7d4Gc7gsfl5bNa5lwpKHBh62CoBN6yVNXM gyrlGHsNKCnu6TaUaia4bb3191sviFNXhRcaaJtrt+GHs96ENMDdgRZ8zXwF3PTV NghFJ2EwVdtnrJL+ifGP+GURgvDlgHzAAMx1gABBCMlrTzEZhR0= =e08s -----END PGP SIGNATURE----- Merge tag 'usb-for-v5.10' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next Felipe writes: USB: changes for v5.10 merge window Most of changes are on dwc3 (38.8%) with cdns3 falling close behind (24.1%). The biggest changes here are a series of non-critical fixes to corner cases on dwc3, produced by Thinh N, and a series of major improvements to cdns3 produced by Peter C. We also have the traditional set of new device support (Intel Keem Bay, Hikey 970) on dwc3. A series of sparse/coccinelle and checkpatch fixes on dwc3 by yours truly and a set of minor changes all over the stack. * tag 'usb-for-v5.10' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb: (117 commits) usb: dwc2: Fix INTR OUT transfers in DDMA mode. usb: dwc2: don't use ID/Vbus detection if usb-role-switch on STM32MP15 SoCs usb: dwc2: override PHY input signals with usb role switch support dt-bindings: usb: dwc2: add optional usb-role-switch property usb: dwc3: of-simple: Add compatible string for Intel Keem Bay platform dt-bindings: usb: Add Intel Keem Bay USB controller bindings usb: dwc3: gadget: Support up to max stream id usb: dwc3: gadget: Return early if no TRB update usb: dwc3: gadget: Keep TRBs in request order usb: dwc3: gadget: Revise setting IOC when no TRB left usb: dwc3: gadget: Look ahead when setting IOC usb: dwc3: gadget: Allow restarting a transfer usb: bdc: remove duplicated error message usb: dwc3: Stop active transfers before halting the controller usb: cdns3: gadget: enlarge the TRB ring length usb: cdns3: gadget: sg_support is only for DEV_VER_V2 or above usb: cdns3: gadget: need to handle sg case for workaround 2 case usb: cdns3: gadget: handle sg list use case at completion correctly usb: cdns3: gadget: add CHAIN and ISP bit for sg list use case usb: cdns3: gadget: improve the dump TRB operation at cdns3_ep_run_transfer ...
This commit is contained in:
commit
21c949b218
@ -25,13 +25,14 @@ description: |
|
||||
The Amlogic A1 embeds a DWC3 USB IP Core configured for USB2 in
|
||||
host-only mode.
|
||||
|
||||
The Amlogic GXL & GXM SoCs doesn't embed an USB3 PHY.
|
||||
The Amlogic GXL, GXM & AXG SoCs doesn't embed an USB3 PHY.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- amlogic,meson-gxl-usb-ctrl
|
||||
- amlogic,meson-gxm-usb-ctrl
|
||||
- amlogic,meson-axg-usb-ctrl
|
||||
- amlogic,meson-g12a-usb-ctrl
|
||||
- amlogic,meson-a1-usb-ctrl
|
||||
|
||||
@ -151,6 +152,25 @@ allOf:
|
||||
|
||||
required:
|
||||
- clock-names
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- amlogic,meson-axg-usb-ctrl
|
||||
|
||||
then:
|
||||
properties:
|
||||
phy-names:
|
||||
items:
|
||||
- const: usb2-phy1 # USB2 PHY1 if USBOTG_B port is used
|
||||
clocks:
|
||||
minItems: 2
|
||||
clock-names:
|
||||
items:
|
||||
- const: usb_ctrl
|
||||
- const: ddr
|
||||
required:
|
||||
- clock-names
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
|
@ -82,6 +82,7 @@ Required properties:
|
||||
"atmel,at91sam9rl-udc"
|
||||
"atmel,at91sam9g45-udc"
|
||||
"atmel,sama5d3-udc"
|
||||
"microchip,sam9x60-udc"
|
||||
- reg: Address and length of the register set for the device
|
||||
- interrupts: Should contain usba interrupt
|
||||
- clocks: Should reference the peripheral and host clocks
|
||||
|
96
Documentation/devicetree/bindings/usb/cdns,usb3.yaml
Normal file
96
Documentation/devicetree/bindings/usb/cdns,usb3.yaml
Normal file
@ -0,0 +1,96 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/usb/cdns,usb3.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Cadence USBSS-DRD controller bindings
|
||||
|
||||
maintainers:
|
||||
- Pawel Laszczak <pawell@cadence.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: cdns,usb3
|
||||
|
||||
reg:
|
||||
items:
|
||||
- description: OTG controller registers
|
||||
- description: XHCI Host controller registers
|
||||
- description: DEVICE controller registers
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: otg
|
||||
- const: xhci
|
||||
- const: dev
|
||||
|
||||
interrupts:
|
||||
items:
|
||||
- description: OTG/DRD controller interrupt
|
||||
- description: XHCI host controller interrupt
|
||||
- description: Device controller interrupt
|
||||
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: host
|
||||
- const: peripheral
|
||||
- const: otg
|
||||
|
||||
dr_mode:
|
||||
enum: [host, otg, peripheral]
|
||||
|
||||
maximum-speed:
|
||||
enum: [super-speed, high-speed, full-speed]
|
||||
|
||||
phys:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
phy-names:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
items:
|
||||
anyOf:
|
||||
- const: cdns3,usb2-phy
|
||||
- const: cdns3,usb3-phy
|
||||
|
||||
cdns,on-chip-buff-size:
|
||||
description:
|
||||
size of memory intended as internal memory for endpoints
|
||||
buffers expressed in KB
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
cdns,phyrst-a-enable:
|
||||
description: Enable resetting of PHY if Rx fail is detected
|
||||
type: boolean
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- reg-names
|
||||
- interrupts
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
bus {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
usb@6000000 {
|
||||
compatible = "cdns,usb3";
|
||||
reg = <0x00 0x6000000 0x00 0x10000>,
|
||||
<0x00 0x6010000 0x00 0x10000>,
|
||||
<0x00 0x6020000 0x00 0x10000>;
|
||||
reg-names = "otg", "xhci", "dev";
|
||||
interrupts = <GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "host", "peripheral", "otg";
|
||||
maximum-speed = "super-speed";
|
||||
dr_mode = "otg";
|
||||
};
|
||||
};
|
@ -1,45 +0,0 @@
|
||||
Binding for the Cadence USBSS-DRD controller
|
||||
|
||||
Required properties:
|
||||
- reg: Physical base address and size of the controller's register areas.
|
||||
Controller has 3 different regions:
|
||||
- HOST registers area
|
||||
- DEVICE registers area
|
||||
- OTG/DRD registers area
|
||||
- reg-names - register memory area names:
|
||||
"xhci" - for HOST registers space
|
||||
"dev" - for DEVICE registers space
|
||||
"otg" - for OTG/DRD registers space
|
||||
- compatible: Should contain: "cdns,usb3"
|
||||
- interrupts: Interrupts used by cdns3 controller:
|
||||
"host" - interrupt used by XHCI driver.
|
||||
"peripheral" - interrupt used by device driver
|
||||
"otg" - interrupt used by DRD/OTG part of driver
|
||||
|
||||
Optional properties:
|
||||
- maximum-speed : valid arguments are "super-speed", "high-speed" and
|
||||
"full-speed"; refer to usb/generic.txt
|
||||
- dr_mode: Should be one of "host", "peripheral" or "otg".
|
||||
- phys: reference to the USB PHY
|
||||
- phy-names: from the *Generic PHY* bindings;
|
||||
Supported names are:
|
||||
- cdns3,usb2-phy
|
||||
- cdns3,usb3-phy
|
||||
|
||||
- cdns,on-chip-buff-size : size of memory intended as internal memory for endpoints
|
||||
buffers expressed in KB
|
||||
|
||||
Example:
|
||||
usb@f3000000 {
|
||||
compatible = "cdns,usb3";
|
||||
interrupts = <GIC_USB_IRQ 7 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_USB_IRQ 7 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_USB_IRQ 8 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "host", "peripheral", "otg";
|
||||
reg = <0xf3000000 0x10000>, /* memory area for HOST registers */
|
||||
<0xf3010000 0x10000>, /* memory area for DEVICE registers */
|
||||
<0xf3020000 0x10000>; /* memory area for OTG/DRD registers */
|
||||
reg-names = "xhci", "dev", "otg";
|
||||
phys = <&usb2_phy>, <&usb3_phy>;
|
||||
phy-names = "cdns3,usb2-phy", "cnds3,usb3-phy";
|
||||
};
|
@ -39,6 +39,7 @@ properties:
|
||||
- amlogic,meson-g12a-usb
|
||||
- const: snps,dwc2
|
||||
- const: amcc,dwc-otg
|
||||
- const: apm,apm82181-dwc-otg
|
||||
- const: snps,dwc2
|
||||
- const: st,stm32f4x9-fsotg
|
||||
- const: st,stm32f4x9-hsotg
|
||||
@ -102,6 +103,10 @@ properties:
|
||||
dr_mode:
|
||||
enum: [host, peripheral, otg]
|
||||
|
||||
usb-role-switch:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description: Support role switch.
|
||||
|
||||
g-rx-fifo-size:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: size of rx fifo size in gadget mode.
|
||||
|
@ -78,6 +78,9 @@ Optional properties:
|
||||
park mode are disabled.
|
||||
- snps,dis_metastability_quirk: when set, disable metastability workaround.
|
||||
CAUTION: use only if you are absolutely sure of it.
|
||||
- snps,dis-split-quirk: when set, change the way URBs are handled by the
|
||||
driver. Needed to avoid -EPROTO errors with usbhid
|
||||
on some devices (Hikey 970).
|
||||
- snps,is-utmi-l1-suspend: true when DWC3 asserts output signal
|
||||
utmi_l1_suspend_n, false when asserts utmi_sleep_n
|
||||
- snps,hird-threshold: HIRD threshold
|
||||
|
@ -0,0 +1,77 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/usb/intel,keembay-dwc3.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Intel Keem Bay DWC3 USB controller
|
||||
|
||||
maintainers:
|
||||
- Wan Ahmad Zainie <wan.ahmad.zainie.wan.mohamad@intel.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: intel,keembay-dwc3
|
||||
|
||||
clocks:
|
||||
maxItems: 4
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: async_master
|
||||
- const: ref
|
||||
- const: alt_ref
|
||||
- const: suspend
|
||||
|
||||
ranges: true
|
||||
|
||||
'#address-cells':
|
||||
enum: [ 1, 2 ]
|
||||
|
||||
'#size-cells':
|
||||
enum: [ 1, 2 ]
|
||||
|
||||
# Required child node:
|
||||
|
||||
patternProperties:
|
||||
"^dwc3@[0-9a-f]+$":
|
||||
type: object
|
||||
description:
|
||||
A child node must exist to represent the core DWC3 IP block.
|
||||
The content of the node is defined in dwc3.txt.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- clocks
|
||||
- clock-names
|
||||
- ranges
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#define KEEM_BAY_A53_AUX_USB
|
||||
#define KEEM_BAY_A53_AUX_USB_REF
|
||||
#define KEEM_BAY_A53_AUX_USB_ALT_REF
|
||||
#define KEEM_BAY_A53_AUX_USB_SUSPEND
|
||||
|
||||
usb {
|
||||
compatible = "intel,keembay-dwc3";
|
||||
clocks = <&scmi_clk KEEM_BAY_A53_AUX_USB>,
|
||||
<&scmi_clk KEEM_BAY_A53_AUX_USB_REF>,
|
||||
<&scmi_clk KEEM_BAY_A53_AUX_USB_ALT_REF>,
|
||||
<&scmi_clk KEEM_BAY_A53_AUX_USB_SUSPEND>;
|
||||
clock-names = "async_master", "ref", "alt_ref", "suspend";
|
||||
ranges;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
dwc3@34000000 {
|
||||
compatible = "snps,dwc3";
|
||||
reg = <0x34000000 0x10000>;
|
||||
interrupts = <GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH>;
|
||||
dr_mode = "peripheral";
|
||||
};
|
||||
};
|
18
MAINTAINERS
18
MAINTAINERS
@ -3475,6 +3475,14 @@ F: drivers/bus/brcmstb_gisb.c
|
||||
F: drivers/pci/controller/pcie-brcmstb.c
|
||||
N: brcmstb
|
||||
|
||||
BROADCOM BDC DRIVER
|
||||
M: Al Cooper <alcooperx@gmail.com>
|
||||
L: linux-usb@vger.kernel.org
|
||||
L: bcm-kernel-feedback-list@broadcom.com
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/usb/brcm,bdc.txt
|
||||
F: drivers/usb/gadget/udc/bdc/
|
||||
|
||||
BROADCOM BMIPS CPUFREQ DRIVER
|
||||
M: Markus Mayer <mmayer@broadcom.com>
|
||||
M: bcm-kernel-feedback-list@broadcom.com
|
||||
@ -3848,6 +3856,16 @@ S: Orphan
|
||||
F: Documentation/devicetree/bindings/mtd/cadence-nand-controller.txt
|
||||
F: drivers/mtd/nand/raw/cadence-nand-controller.c
|
||||
|
||||
CADENCE USB3 DRD IP DRIVER
|
||||
M: Peter Chen <peter.chen@nxp.com>
|
||||
M: Pawel Laszczak <pawell@cadence.com>
|
||||
M: Roger Quadros <rogerq@ti.com>
|
||||
L: linux-usb@vger.kernel.org
|
||||
S: Maintained
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb.git
|
||||
F: Documentation/devicetree/bindings/usb/cdns-usb3.txt
|
||||
F: drivers/usb/cdns3/
|
||||
|
||||
CADET FM/AM RADIO RECEIVER DRIVER
|
||||
M: Hans Verkuil <hverkuil@xs4all.nl>
|
||||
L: linux-media@vger.kernel.org
|
||||
|
@ -563,6 +563,12 @@
|
||||
atmel,pins = <AT91_PIOD 18 AT91_PERIPH_GPIO AT91_PINCTRL_NONE>;
|
||||
};
|
||||
};
|
||||
|
||||
usb0 {
|
||||
pinctrl_usba_vbus: usba_vbus {
|
||||
atmel,pins = <AT91_PIOB 16 AT91_PERIPH_GPIO AT91_PINCTRL_NONE>;
|
||||
};
|
||||
};
|
||||
}; /* pinctrl */
|
||||
|
||||
&pmc {
|
||||
@ -666,6 +672,13 @@
|
||||
};
|
||||
};
|
||||
|
||||
&usb0 {
|
||||
atmel,vbus-gpio = <&pioB 16 GPIO_ACTIVE_HIGH>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_usba_vbus>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&usb1 {
|
||||
num-ports = <3>;
|
||||
atmel,vbus-gpio = <0
|
||||
|
@ -69,6 +69,20 @@
|
||||
#size-cells = <1>;
|
||||
ranges;
|
||||
|
||||
usb0: gadget@500000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "microchip,sam9x60-udc";
|
||||
reg = <0x00500000 0x100000
|
||||
0xf803c000 0x400>;
|
||||
interrupts = <23 IRQ_TYPE_LEVEL_HIGH 2>;
|
||||
clocks = <&pmc PMC_TYPE_PERIPHERAL 23>, <&pmc PMC_TYPE_CORE PMC_UTMI>;
|
||||
clock-names = "pclk", "hclk";
|
||||
assigned-clocks = <&pmc PMC_TYPE_CORE PMC_UTMI>;
|
||||
assigned-clock-rates = <480000000>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
usb1: ohci@600000 {
|
||||
compatible = "atmel,at91rm9200-ohci", "usb-ohci";
|
||||
reg = <0x00600000 0x100000>;
|
||||
|
@ -15,6 +15,8 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include "core.h"
|
||||
|
||||
#define USB3_CORE_CTRL1 0x00
|
||||
#define USB3_CORE_CTRL2 0x04
|
||||
@ -32,7 +34,7 @@
|
||||
/* Register bits definition */
|
||||
|
||||
/* USB3_CORE_CTRL1 */
|
||||
#define SW_RESET_MASK (0x3f << 26)
|
||||
#define SW_RESET_MASK GENMASK(31, 26)
|
||||
#define PWR_SW_RESET BIT(31)
|
||||
#define APB_SW_RESET BIT(30)
|
||||
#define AXI_SW_RESET BIT(29)
|
||||
@ -53,8 +55,8 @@
|
||||
#define LPM_CLK_REQ BIT(28)
|
||||
#define DEVU3_WAEKUP_EN BIT(14)
|
||||
#define OTG_WAKEUP_EN BIT(12)
|
||||
#define DEV_INT_EN (3 << 8) /* DEV INT b9:8 */
|
||||
#define HOST_INT1_EN (1 << 0) /* HOST INT b7:0 */
|
||||
#define DEV_INT_EN (3 << 8) /* DEV INT b9:8 */
|
||||
#define HOST_INT1_EN (1 << 0) /* HOST INT b7:0 */
|
||||
|
||||
/* USB3_CORE_STATUS */
|
||||
#define MDCTRL_CLK_STATUS BIT(15)
|
||||
@ -66,11 +68,30 @@
|
||||
#define CLK_VALID_COMPARE_BITS (0xf << 28)
|
||||
#define PHY_REFCLK_REQ (1 << 0)
|
||||
|
||||
/* OTG registers definition */
|
||||
#define OTGSTS 0x4
|
||||
/* OTGSTS */
|
||||
#define OTG_NRDY BIT(11)
|
||||
|
||||
/* xHCI registers definition */
|
||||
#define XECP_PM_PMCSR 0x8018
|
||||
#define XECP_AUX_CTRL_REG1 0x8120
|
||||
|
||||
/* Register bits definition */
|
||||
/* XECP_AUX_CTRL_REG1 */
|
||||
#define CFG_RXDET_P3_EN BIT(15)
|
||||
|
||||
/* XECP_PM_PMCSR */
|
||||
#define PS_MASK GENMASK(1, 0)
|
||||
#define PS_D0 0
|
||||
#define PS_D1 1
|
||||
|
||||
struct cdns_imx {
|
||||
struct device *dev;
|
||||
void __iomem *noncore;
|
||||
struct clk_bulk_data *clks;
|
||||
int num_clks;
|
||||
struct platform_device *cdns3_pdev;
|
||||
};
|
||||
|
||||
static inline u32 cdns_imx_readl(struct cdns_imx *data, u32 offset)
|
||||
@ -126,6 +147,20 @@ static int cdns_imx_noncore_init(struct cdns_imx *data)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cdns_imx_platform_suspend(struct device *dev,
|
||||
bool suspend, bool wakeup);
|
||||
static struct cdns3_platform_data cdns_imx_pdata = {
|
||||
.platform_suspend = cdns_imx_platform_suspend,
|
||||
};
|
||||
|
||||
static const struct of_dev_auxdata cdns_imx_auxdata[] = {
|
||||
{
|
||||
.compatible = "cdns,usb3",
|
||||
.platform_data = &cdns_imx_pdata,
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
static int cdns_imx_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
@ -162,14 +197,18 @@ static int cdns_imx_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = of_platform_populate(node, NULL, NULL, dev);
|
||||
ret = of_platform_populate(node, NULL, cdns_imx_auxdata, dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to create children: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
return ret;
|
||||
device_set_wakeup_capable(dev, true);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_forbid(dev);
|
||||
|
||||
return ret;
|
||||
err:
|
||||
clk_bulk_disable_unprepare(data->num_clks, data->clks);
|
||||
return ret;
|
||||
@ -194,6 +233,147 @@ static int cdns_imx_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static void cdns3_set_wakeup(struct cdns_imx *data, bool enable)
|
||||
{
|
||||
u32 value;
|
||||
|
||||
value = cdns_imx_readl(data, USB3_INT_REG);
|
||||
if (enable)
|
||||
value |= OTG_WAKEUP_EN | DEVU3_WAEKUP_EN;
|
||||
else
|
||||
value &= ~(OTG_WAKEUP_EN | DEVU3_WAEKUP_EN);
|
||||
|
||||
cdns_imx_writel(data, USB3_INT_REG, value);
|
||||
}
|
||||
|
||||
static int cdns_imx_platform_suspend(struct device *dev,
|
||||
bool suspend, bool wakeup)
|
||||
{
|
||||
struct cdns3 *cdns = dev_get_drvdata(dev);
|
||||
struct device *parent = dev->parent;
|
||||
struct cdns_imx *data = dev_get_drvdata(parent);
|
||||
void __iomem *otg_regs = (void __iomem *)(cdns->otg_regs);
|
||||
void __iomem *xhci_regs = cdns->xhci_regs;
|
||||
u32 value;
|
||||
int ret = 0;
|
||||
|
||||
if (cdns->role != USB_ROLE_HOST)
|
||||
return 0;
|
||||
|
||||
if (suspend) {
|
||||
/* SW request low power when all usb ports allow to it ??? */
|
||||
value = readl(xhci_regs + XECP_PM_PMCSR);
|
||||
value &= ~PS_MASK;
|
||||
value |= PS_D1;
|
||||
writel(value, xhci_regs + XECP_PM_PMCSR);
|
||||
|
||||
/* mdctrl_clk_sel */
|
||||
value = cdns_imx_readl(data, USB3_CORE_CTRL1);
|
||||
value |= MDCTRL_CLK_SEL;
|
||||
cdns_imx_writel(data, USB3_CORE_CTRL1, value);
|
||||
|
||||
/* wait for mdctrl_clk_status */
|
||||
value = cdns_imx_readl(data, USB3_CORE_STATUS);
|
||||
ret = readl_poll_timeout(data->noncore + USB3_CORE_STATUS, value,
|
||||
(value & MDCTRL_CLK_STATUS) == MDCTRL_CLK_STATUS,
|
||||
10, 100000);
|
||||
if (ret)
|
||||
dev_warn(parent, "wait mdctrl_clk_status timeout\n");
|
||||
|
||||
/* wait lpm_clk_req to be 0 */
|
||||
value = cdns_imx_readl(data, USB3_INT_REG);
|
||||
ret = readl_poll_timeout(data->noncore + USB3_INT_REG, value,
|
||||
(value & LPM_CLK_REQ) != LPM_CLK_REQ,
|
||||
10, 100000);
|
||||
if (ret)
|
||||
dev_warn(parent, "wait lpm_clk_req timeout\n");
|
||||
|
||||
/* wait phy_refclk_req to be 0 */
|
||||
value = cdns_imx_readl(data, USB3_SSPHY_STATUS);
|
||||
ret = readl_poll_timeout(data->noncore + USB3_SSPHY_STATUS, value,
|
||||
(value & PHY_REFCLK_REQ) != PHY_REFCLK_REQ,
|
||||
10, 100000);
|
||||
if (ret)
|
||||
dev_warn(parent, "wait phy_refclk_req timeout\n");
|
||||
|
||||
cdns3_set_wakeup(data, wakeup);
|
||||
} else {
|
||||
cdns3_set_wakeup(data, false);
|
||||
|
||||
/* SW request D0 */
|
||||
value = readl(xhci_regs + XECP_PM_PMCSR);
|
||||
value &= ~PS_MASK;
|
||||
value |= PS_D0;
|
||||
writel(value, xhci_regs + XECP_PM_PMCSR);
|
||||
|
||||
/* clr CFG_RXDET_P3_EN */
|
||||
value = readl(xhci_regs + XECP_AUX_CTRL_REG1);
|
||||
value &= ~CFG_RXDET_P3_EN;
|
||||
writel(value, xhci_regs + XECP_AUX_CTRL_REG1);
|
||||
|
||||
/* clear mdctrl_clk_sel */
|
||||
value = cdns_imx_readl(data, USB3_CORE_CTRL1);
|
||||
value &= ~MDCTRL_CLK_SEL;
|
||||
cdns_imx_writel(data, USB3_CORE_CTRL1, value);
|
||||
|
||||
/* wait CLK_125_REQ to be 1 */
|
||||
value = cdns_imx_readl(data, USB3_INT_REG);
|
||||
ret = readl_poll_timeout(data->noncore + USB3_INT_REG, value,
|
||||
(value & CLK_125_REQ) == CLK_125_REQ,
|
||||
10, 100000);
|
||||
if (ret)
|
||||
dev_warn(parent, "wait CLK_125_REQ timeout\n");
|
||||
|
||||
/* wait for mdctrl_clk_status is cleared */
|
||||
value = cdns_imx_readl(data, USB3_CORE_STATUS);
|
||||
ret = readl_poll_timeout(data->noncore + USB3_CORE_STATUS, value,
|
||||
(value & MDCTRL_CLK_STATUS) != MDCTRL_CLK_STATUS,
|
||||
10, 100000);
|
||||
if (ret)
|
||||
dev_warn(parent, "wait mdctrl_clk_status cleared timeout\n");
|
||||
|
||||
/* Wait until OTG_NRDY is 0 */
|
||||
value = readl(otg_regs + OTGSTS);
|
||||
ret = readl_poll_timeout(otg_regs + OTGSTS, value,
|
||||
(value & OTG_NRDY) != OTG_NRDY,
|
||||
10, 100000);
|
||||
if (ret)
|
||||
dev_warn(parent, "wait OTG ready timeout\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static int cdns_imx_resume(struct device *dev)
|
||||
{
|
||||
struct cdns_imx *data = dev_get_drvdata(dev);
|
||||
|
||||
return clk_bulk_prepare_enable(data->num_clks, data->clks);
|
||||
}
|
||||
|
||||
static int cdns_imx_suspend(struct device *dev)
|
||||
{
|
||||
struct cdns_imx *data = dev_get_drvdata(dev);
|
||||
|
||||
clk_bulk_disable_unprepare(data->num_clks, data->clks);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int cdns_imx_platform_suspend(struct device *dev,
|
||||
bool suspend, bool wakeup)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static const struct dev_pm_ops cdns_imx_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(cdns_imx_suspend, cdns_imx_resume, NULL)
|
||||
};
|
||||
|
||||
static const struct of_device_id cdns_imx_of_match[] = {
|
||||
{ .compatible = "fsl,imx8qm-usb3", },
|
||||
{},
|
||||
@ -206,6 +386,7 @@ static struct platform_driver cdns_imx_driver = {
|
||||
.driver = {
|
||||
.name = "cdns3-imx",
|
||||
.of_match_table = cdns_imx_of_match,
|
||||
.pm = &cdns_imx_pm_ops,
|
||||
},
|
||||
};
|
||||
module_platform_driver(cdns_imx_driver);
|
||||
|
@ -280,6 +280,10 @@ int cdns3_hw_role_switch(struct cdns3 *cdns)
|
||||
enum usb_role real_role, current_role;
|
||||
int ret = 0;
|
||||
|
||||
/* Depends on role switch class */
|
||||
if (cdns->role_sw)
|
||||
return 0;
|
||||
|
||||
pm_runtime_get_sync(cdns->dev);
|
||||
|
||||
current_role = cdns->role;
|
||||
@ -371,6 +375,50 @@ pm_put:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_phy_power_on(struct cdns3 *cdns)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = phy_power_on(cdns->usb2_phy);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = phy_power_on(cdns->usb3_phy);
|
||||
if (ret)
|
||||
phy_power_off(cdns->usb2_phy);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void set_phy_power_off(struct cdns3 *cdns)
|
||||
{
|
||||
phy_power_off(cdns->usb3_phy);
|
||||
phy_power_off(cdns->usb2_phy);
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_wakeup_irq - interrupt handler for wakeup events
|
||||
* @irq: irq number for cdns3 core device
|
||||
* @data: structure of cdns3
|
||||
*
|
||||
* Returns IRQ_HANDLED or IRQ_NONE
|
||||
*/
|
||||
static irqreturn_t cdns3_wakeup_irq(int irq, void *data)
|
||||
{
|
||||
struct cdns3 *cdns = data;
|
||||
|
||||
if (cdns->in_lpm) {
|
||||
disable_irq_nosync(irq);
|
||||
cdns->wakeup_pending = true;
|
||||
if ((cdns->role == USB_ROLE_HOST) && cdns->host_dev)
|
||||
pm_request_resume(&cdns->host_dev->dev);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_probe - probe for cdns3 core device
|
||||
* @pdev: Pointer to cdns3 core platform device
|
||||
@ -397,6 +445,7 @@ static int cdns3_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
cdns->dev = dev;
|
||||
cdns->pdata = dev_get_platdata(dev);
|
||||
|
||||
platform_set_drvdata(pdev, cdns);
|
||||
|
||||
@ -443,8 +492,21 @@ static int cdns3_probe(struct platform_device *pdev)
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
cdns->phyrst_a_enable = device_property_read_bool(dev, "cdns,phyrst-a-enable");
|
||||
|
||||
cdns->otg_res = *res;
|
||||
|
||||
cdns->wakeup_irq = platform_get_irq_byname_optional(pdev, "wakeup");
|
||||
if (cdns->wakeup_irq == -EPROBE_DEFER)
|
||||
return cdns->wakeup_irq;
|
||||
else if (cdns->wakeup_irq == 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (cdns->wakeup_irq < 0) {
|
||||
dev_dbg(dev, "couldn't get wakeup irq\n");
|
||||
cdns->wakeup_irq = 0x0;
|
||||
}
|
||||
|
||||
mutex_init(&cdns->mutex);
|
||||
|
||||
cdns->usb2_phy = devm_phy_optional_get(dev, "cdns3,usb2-phy");
|
||||
@ -463,14 +525,10 @@ static int cdns3_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
goto err1;
|
||||
|
||||
ret = phy_power_on(cdns->usb2_phy);
|
||||
ret = set_phy_power_on(cdns);
|
||||
if (ret)
|
||||
goto err2;
|
||||
|
||||
ret = phy_power_on(cdns->usb3_phy);
|
||||
if (ret)
|
||||
goto err3;
|
||||
|
||||
sw_desc.set = cdns3_role_set;
|
||||
sw_desc.get = cdns3_role_get;
|
||||
sw_desc.allow_userspace_control = true;
|
||||
@ -482,20 +540,34 @@ static int cdns3_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(cdns->role_sw)) {
|
||||
ret = PTR_ERR(cdns->role_sw);
|
||||
dev_warn(dev, "Unable to register Role Switch\n");
|
||||
goto err4;
|
||||
goto err3;
|
||||
}
|
||||
|
||||
if (cdns->wakeup_irq) {
|
||||
ret = devm_request_irq(cdns->dev, cdns->wakeup_irq,
|
||||
cdns3_wakeup_irq,
|
||||
IRQF_SHARED,
|
||||
dev_name(cdns->dev), cdns);
|
||||
|
||||
if (ret) {
|
||||
dev_err(cdns->dev, "couldn't register wakeup irq handler\n");
|
||||
goto err3;
|
||||
}
|
||||
}
|
||||
|
||||
ret = cdns3_drd_init(cdns);
|
||||
if (ret)
|
||||
goto err5;
|
||||
goto err4;
|
||||
|
||||
ret = cdns3_core_init_role(cdns);
|
||||
if (ret)
|
||||
goto err5;
|
||||
goto err4;
|
||||
|
||||
spin_lock_init(&cdns->lock);
|
||||
device_set_wakeup_capable(dev, true);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_forbid(dev);
|
||||
|
||||
/*
|
||||
* The controller needs less time between bus and controller suspend,
|
||||
@ -508,14 +580,11 @@ static int cdns3_probe(struct platform_device *pdev)
|
||||
dev_dbg(dev, "Cadence USB3 core: probe succeed\n");
|
||||
|
||||
return 0;
|
||||
err5:
|
||||
err4:
|
||||
cdns3_drd_exit(cdns);
|
||||
usb_role_switch_unregister(cdns->role_sw);
|
||||
err4:
|
||||
phy_power_off(cdns->usb3_phy);
|
||||
|
||||
err3:
|
||||
phy_power_off(cdns->usb2_phy);
|
||||
set_phy_power_off(cdns);
|
||||
err2:
|
||||
phy_exit(cdns->usb3_phy);
|
||||
err1:
|
||||
@ -539,13 +608,89 @@ static int cdns3_remove(struct platform_device *pdev)
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
cdns3_exit_roles(cdns);
|
||||
usb_role_switch_unregister(cdns->role_sw);
|
||||
phy_power_off(cdns->usb2_phy);
|
||||
phy_power_off(cdns->usb3_phy);
|
||||
set_phy_power_off(cdns);
|
||||
phy_exit(cdns->usb2_phy);
|
||||
phy_exit(cdns->usb3_phy);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static int cdns3_set_platform_suspend(struct device *dev,
|
||||
bool suspend, bool wakeup)
|
||||
{
|
||||
struct cdns3 *cdns = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (cdns->pdata && cdns->pdata->platform_suspend)
|
||||
ret = cdns->pdata->platform_suspend(dev, suspend, wakeup);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cdns3_controller_suspend(struct device *dev, pm_message_t msg)
|
||||
{
|
||||
struct cdns3 *cdns = dev_get_drvdata(dev);
|
||||
bool wakeup;
|
||||
unsigned long flags;
|
||||
|
||||
if (cdns->in_lpm)
|
||||
return 0;
|
||||
|
||||
if (PMSG_IS_AUTO(msg))
|
||||
wakeup = true;
|
||||
else
|
||||
wakeup = device_may_wakeup(dev);
|
||||
|
||||
cdns3_set_platform_suspend(cdns->dev, true, wakeup);
|
||||
set_phy_power_off(cdns);
|
||||
spin_lock_irqsave(&cdns->lock, flags);
|
||||
cdns->in_lpm = true;
|
||||
spin_unlock_irqrestore(&cdns->lock, flags);
|
||||
dev_dbg(cdns->dev, "%s ends\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdns3_controller_resume(struct device *dev, pm_message_t msg)
|
||||
{
|
||||
struct cdns3 *cdns = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
|
||||
if (!cdns->in_lpm)
|
||||
return 0;
|
||||
|
||||
ret = set_phy_power_on(cdns);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
cdns3_set_platform_suspend(cdns->dev, false, false);
|
||||
|
||||
spin_lock_irqsave(&cdns->lock, flags);
|
||||
if (cdns->roles[cdns->role]->resume && !PMSG_IS_AUTO(msg))
|
||||
cdns->roles[cdns->role]->resume(cdns, false);
|
||||
|
||||
cdns->in_lpm = false;
|
||||
spin_unlock_irqrestore(&cdns->lock, flags);
|
||||
if (cdns->wakeup_pending) {
|
||||
cdns->wakeup_pending = false;
|
||||
enable_irq(cdns->wakeup_irq);
|
||||
}
|
||||
dev_dbg(cdns->dev, "%s ends\n", __func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cdns3_runtime_suspend(struct device *dev)
|
||||
{
|
||||
return cdns3_controller_suspend(dev, PMSG_AUTO_SUSPEND);
|
||||
}
|
||||
|
||||
static int cdns3_runtime_resume(struct device *dev)
|
||||
{
|
||||
return cdns3_controller_resume(dev, PMSG_AUTO_RESUME);
|
||||
}
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
||||
static int cdns3_suspend(struct device *dev)
|
||||
@ -553,45 +698,38 @@ static int cdns3_suspend(struct device *dev)
|
||||
struct cdns3 *cdns = dev_get_drvdata(dev);
|
||||
unsigned long flags;
|
||||
|
||||
if (cdns->role == USB_ROLE_HOST)
|
||||
return 0;
|
||||
|
||||
if (pm_runtime_status_suspended(dev))
|
||||
pm_runtime_resume(dev);
|
||||
|
||||
if (cdns->roles[cdns->role]->suspend) {
|
||||
spin_lock_irqsave(&cdns->gadget_dev->lock, flags);
|
||||
spin_lock_irqsave(&cdns->lock, flags);
|
||||
cdns->roles[cdns->role]->suspend(cdns, false);
|
||||
spin_unlock_irqrestore(&cdns->gadget_dev->lock, flags);
|
||||
spin_unlock_irqrestore(&cdns->lock, flags);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return cdns3_controller_suspend(dev, PMSG_SUSPEND);
|
||||
}
|
||||
|
||||
static int cdns3_resume(struct device *dev)
|
||||
{
|
||||
struct cdns3 *cdns = dev_get_drvdata(dev);
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
if (cdns->role == USB_ROLE_HOST)
|
||||
return 0;
|
||||
|
||||
if (cdns->roles[cdns->role]->resume) {
|
||||
spin_lock_irqsave(&cdns->gadget_dev->lock, flags);
|
||||
cdns->roles[cdns->role]->resume(cdns, false);
|
||||
spin_unlock_irqrestore(&cdns->gadget_dev->lock, flags);
|
||||
}
|
||||
ret = cdns3_controller_resume(dev, PMSG_RESUME);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static const struct dev_pm_ops cdns3_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(cdns3_suspend, cdns3_resume)
|
||||
SET_RUNTIME_PM_OPS(cdns3_runtime_suspend, cdns3_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
@ -38,6 +38,12 @@ struct cdns3_role_driver {
|
||||
};
|
||||
|
||||
#define CDNS3_XHCI_RESOURCES_NUM 2
|
||||
|
||||
struct cdns3_platform_data {
|
||||
int (*platform_suspend)(struct device *dev,
|
||||
bool suspend, bool wakeup);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cdns3 - Representation of Cadence USB3 DRD controller.
|
||||
* @dev: pointer to Cadence device struct
|
||||
@ -50,6 +56,7 @@ struct cdns3_role_driver {
|
||||
* @otg_regs: pointer to base of otg registers
|
||||
* @otg_irq: irq number for otg controller
|
||||
* @dev_irq: irq number for device controller
|
||||
* @wakeup_irq: irq number for wakeup event, it is optional
|
||||
* @roles: array of supported roles for this controller
|
||||
* @role: current role
|
||||
* @host_dev: the child host device pointer for cdns3 core
|
||||
@ -62,6 +69,10 @@ struct cdns3_role_driver {
|
||||
* This field based on firmware setting, kernel configuration
|
||||
* and hardware configuration.
|
||||
* @role_sw: pointer to role switch object.
|
||||
* @in_lpm: indicate the controller is in low power mode
|
||||
* @wakeup_pending: wakeup interrupt pending
|
||||
* @pdata: platform data from glue layer
|
||||
* @lock: spinlock structure
|
||||
*/
|
||||
struct cdns3 {
|
||||
struct device *dev;
|
||||
@ -76,9 +87,11 @@ struct cdns3 {
|
||||
#define CDNS3_CONTROLLER_V0 0
|
||||
#define CDNS3_CONTROLLER_V1 1
|
||||
u32 version;
|
||||
bool phyrst_a_enable;
|
||||
|
||||
int otg_irq;
|
||||
int dev_irq;
|
||||
int wakeup_irq;
|
||||
struct cdns3_role_driver *roles[USB_ROLE_DEVICE + 1];
|
||||
enum usb_role role;
|
||||
struct platform_device *host_dev;
|
||||
@ -89,6 +102,10 @@ struct cdns3 {
|
||||
struct mutex mutex;
|
||||
enum usb_dr_mode dr_mode;
|
||||
struct usb_role_switch *role_sw;
|
||||
bool in_lpm;
|
||||
bool wakeup_pending;
|
||||
struct cdns3_platform_data *pdata;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
int cdns3_hw_role_switch(struct cdns3 *cdns);
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/phy/phy.h>
|
||||
|
||||
#include "gadget.h"
|
||||
#include "drd.h"
|
||||
@ -42,6 +43,18 @@ int cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode)
|
||||
reg = readl(&cdns->otg_v1_regs->override);
|
||||
reg |= OVERRIDE_IDPULLUP;
|
||||
writel(reg, &cdns->otg_v1_regs->override);
|
||||
|
||||
/*
|
||||
* Enable work around feature built into the
|
||||
* controller to address issue with RX Sensitivity
|
||||
* est (EL_17) for USB2 PHY. The issue only occures
|
||||
* for 0x0002450D controller version.
|
||||
*/
|
||||
if (cdns->phyrst_a_enable) {
|
||||
reg = readl(&cdns->otg_v1_regs->phyrst_cfg);
|
||||
reg |= PHYRST_CFG_PHYRST_A_ENABLE;
|
||||
writel(reg, &cdns->otg_v1_regs->phyrst_cfg);
|
||||
}
|
||||
} else {
|
||||
reg = readl(&cdns->otg_v0_regs->ctrl1);
|
||||
reg |= OVERRIDE_IDPULLUP_V0;
|
||||
@ -145,6 +158,7 @@ int cdns3_drd_host_on(struct cdns3 *cdns)
|
||||
if (ret)
|
||||
dev_err(cdns->dev, "timeout waiting for xhci_ready\n");
|
||||
|
||||
phy_set_mode(cdns->usb3_phy, PHY_MODE_USB_HOST);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -164,6 +178,7 @@ void cdns3_drd_host_off(struct cdns3 *cdns)
|
||||
readl_poll_timeout_atomic(&cdns->otg_regs->state, val,
|
||||
!(val & OTGSTATE_HOST_STATE_MASK),
|
||||
1, 2000000);
|
||||
phy_set_mode(cdns->usb3_phy, PHY_MODE_INVALID);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -190,6 +205,7 @@ int cdns3_drd_gadget_on(struct cdns3 *cdns)
|
||||
return ret;
|
||||
}
|
||||
|
||||
phy_set_mode(cdns->usb3_phy, PHY_MODE_USB_DEVICE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -213,6 +229,7 @@ void cdns3_drd_gadget_off(struct cdns3 *cdns)
|
||||
readl_poll_timeout_atomic(&cdns->otg_regs->state, val,
|
||||
!(val & OTGSTATE_DEV_STATE_MASK),
|
||||
1, 2000000);
|
||||
phy_set_mode(cdns->usb3_phy, PHY_MODE_INVALID);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -293,6 +310,9 @@ static irqreturn_t cdns3_drd_irq(int irq, void *data)
|
||||
if (cdns->dr_mode != USB_DR_MODE_OTG)
|
||||
return IRQ_NONE;
|
||||
|
||||
if (cdns->in_lpm)
|
||||
return ret;
|
||||
|
||||
reg = readl(&cdns->otg_regs->ivect);
|
||||
|
||||
if (!reg)
|
||||
|
@ -31,7 +31,7 @@ struct cdns3_otg_regs {
|
||||
__le32 simulate;
|
||||
__le32 override;
|
||||
__le32 susp_ctrl;
|
||||
__le32 reserved4;
|
||||
__le32 phyrst_cfg;
|
||||
__le32 anasts;
|
||||
__le32 adp_ramp_time;
|
||||
__le32 ctrl1;
|
||||
@ -153,6 +153,9 @@ struct cdns3_otg_common_regs {
|
||||
/* Only for CDNS3_CONTROLLER_V0 version */
|
||||
#define OVERRIDE_IDPULLUP_V0 BIT(24)
|
||||
|
||||
/* PHYRST_CFG - bitmasks */
|
||||
#define PHYRST_CFG_PHYRST_A_ENABLE BIT(0)
|
||||
|
||||
#define CDNS3_ID_PERIPHERAL 1
|
||||
#define CDNS3_ID_HOST 0
|
||||
|
||||
|
@ -717,9 +717,17 @@ static int cdns3_gadget_ep0_queue(struct usb_ep *ep,
|
||||
|
||||
/* send STATUS stage. Should be called only for SET_CONFIGURATION */
|
||||
if (priv_dev->ep0_stage == CDNS3_STATUS_STAGE) {
|
||||
u32 val;
|
||||
|
||||
cdns3_select_ep(priv_dev, 0x00);
|
||||
cdns3_set_hw_configuration(priv_dev);
|
||||
cdns3_ep0_complete_setup(priv_dev, 0, 1);
|
||||
/* wait until configuration set */
|
||||
ret = readl_poll_timeout_atomic(&priv_dev->regs->usb_sts, val,
|
||||
val & USB_STS_CFGSTS_MASK, 1, 100);
|
||||
if (ret == -ETIMEDOUT)
|
||||
dev_warn(priv_dev->dev, "timeout for waiting configuration set\n");
|
||||
|
||||
request->actual = 0;
|
||||
priv_dev->status_completion_no_call = true;
|
||||
priv_dev->pending_status_request = request;
|
||||
@ -731,7 +739,7 @@ static int cdns3_gadget_ep0_queue(struct usb_ep *ep,
|
||||
* ep0_queue is back.
|
||||
*/
|
||||
queue_work(system_freezable_wq, &priv_dev->pending_status_wq);
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!list_empty(&priv_ep->pending_req_list)) {
|
||||
|
@ -261,8 +261,8 @@ int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep)
|
||||
*/
|
||||
link_trb->control = 0;
|
||||
} else {
|
||||
link_trb->buffer = TRB_BUFFER(priv_ep->trb_pool_dma);
|
||||
link_trb->control = TRB_CYCLE | TRB_TYPE(TRB_LINK) | TRB_TOGGLE;
|
||||
link_trb->buffer = cpu_to_le32(TRB_BUFFER(priv_ep->trb_pool_dma));
|
||||
link_trb->control = cpu_to_le32(TRB_CYCLE | TRB_TYPE(TRB_LINK) | TRB_TOGGLE);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -462,6 +462,36 @@ static int cdns3_start_all_request(struct cdns3_device *priv_dev,
|
||||
(reg) |= EP_STS_EN_DESCMISEN; \
|
||||
} } while (0)
|
||||
|
||||
static void __cdns3_descmiss_copy_data(struct usb_request *request,
|
||||
struct usb_request *descmiss_req)
|
||||
{
|
||||
int length = request->actual + descmiss_req->actual;
|
||||
struct scatterlist *s = request->sg;
|
||||
|
||||
if (!s) {
|
||||
if (length <= request->length) {
|
||||
memcpy(&((u8 *)request->buf)[request->actual],
|
||||
descmiss_req->buf,
|
||||
descmiss_req->actual);
|
||||
request->actual = length;
|
||||
} else {
|
||||
/* It should never occures */
|
||||
request->status = -ENOMEM;
|
||||
}
|
||||
} else {
|
||||
if (length <= sg_dma_len(s)) {
|
||||
void *p = phys_to_virt(sg_dma_address(s));
|
||||
|
||||
memcpy(&((u8 *)p)[request->actual],
|
||||
descmiss_req->buf,
|
||||
descmiss_req->actual);
|
||||
request->actual = length;
|
||||
} else {
|
||||
request->status = -ENOMEM;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_wa2_descmiss_copy_data copy data from internal requests to
|
||||
* request queued by class driver.
|
||||
@ -488,21 +518,9 @@ static void cdns3_wa2_descmiss_copy_data(struct cdns3_endpoint *priv_ep,
|
||||
|
||||
chunk_end = descmiss_priv_req->flags & REQUEST_INTERNAL_CH;
|
||||
length = request->actual + descmiss_req->actual;
|
||||
|
||||
request->status = descmiss_req->status;
|
||||
|
||||
if (length <= request->length) {
|
||||
memcpy(&((u8 *)request->buf)[request->actual],
|
||||
descmiss_req->buf,
|
||||
descmiss_req->actual);
|
||||
request->actual = length;
|
||||
} else {
|
||||
/* It should never occures */
|
||||
request->status = -ENOMEM;
|
||||
}
|
||||
|
||||
__cdns3_descmiss_copy_data(request, descmiss_req);
|
||||
list_del_init(&descmiss_priv_req->list);
|
||||
|
||||
kfree(descmiss_req->buf);
|
||||
cdns3_gadget_ep_free_request(&priv_ep->endpoint, descmiss_req);
|
||||
--priv_ep->wa2_counter;
|
||||
@ -817,6 +835,8 @@ void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep,
|
||||
request->length);
|
||||
|
||||
priv_req->flags &= ~(REQUEST_PENDING | REQUEST_UNALIGNED);
|
||||
/* All TRBs have finished, clear the counter */
|
||||
priv_req->finished_trb = 0;
|
||||
trace_cdns3_gadget_giveback(priv_req);
|
||||
|
||||
if (priv_dev->dev_ver < DEV_VER_V2) {
|
||||
@ -847,10 +867,10 @@ static void cdns3_wa1_restore_cycle_bit(struct cdns3_endpoint *priv_ep)
|
||||
priv_ep->wa1_trb_index = 0xFFFF;
|
||||
if (priv_ep->wa1_cycle_bit) {
|
||||
priv_ep->wa1_trb->control =
|
||||
priv_ep->wa1_trb->control | 0x1;
|
||||
priv_ep->wa1_trb->control | cpu_to_le32(0x1);
|
||||
} else {
|
||||
priv_ep->wa1_trb->control =
|
||||
priv_ep->wa1_trb->control & ~0x1;
|
||||
priv_ep->wa1_trb->control & cpu_to_le32(~0x1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1008,17 +1028,16 @@ static int cdns3_ep_run_stream_transfer(struct cdns3_endpoint *priv_ep,
|
||||
TRB_STREAM_ID(priv_req->request.stream_id) | TRB_ISP;
|
||||
|
||||
if (!request->num_sgs) {
|
||||
trb->buffer = TRB_BUFFER(trb_dma);
|
||||
trb->buffer = cpu_to_le32(TRB_BUFFER(trb_dma));
|
||||
length = request->length;
|
||||
} else {
|
||||
trb->buffer = TRB_BUFFER(request->sg[sg_idx].dma_address);
|
||||
trb->buffer = cpu_to_le32(TRB_BUFFER(request->sg[sg_idx].dma_address));
|
||||
length = request->sg[sg_idx].length;
|
||||
}
|
||||
|
||||
tdl = DIV_ROUND_UP(length, priv_ep->endpoint.maxpacket);
|
||||
|
||||
trb->length = TRB_BURST_LEN(16 /*priv_ep->trb_burst_size*/) |
|
||||
TRB_LEN(length);
|
||||
trb->length = cpu_to_le32(TRB_BURST_LEN(16) | TRB_LEN(length));
|
||||
|
||||
/*
|
||||
* For DEV_VER_V2 controller version we have enabled
|
||||
@ -1027,11 +1046,11 @@ static int cdns3_ep_run_stream_transfer(struct cdns3_endpoint *priv_ep,
|
||||
*/
|
||||
if (priv_dev->dev_ver >= DEV_VER_V2) {
|
||||
if (priv_dev->gadget.speed == USB_SPEED_SUPER)
|
||||
trb->length |= TRB_TDL_SS_SIZE(tdl);
|
||||
trb->length |= cpu_to_le32(TRB_TDL_SS_SIZE(tdl));
|
||||
}
|
||||
priv_req->flags |= REQUEST_PENDING;
|
||||
|
||||
trb->control = control;
|
||||
trb->control = cpu_to_le32(control);
|
||||
|
||||
trace_cdns3_prepare_trb(priv_ep, priv_req->trb);
|
||||
|
||||
@ -1091,6 +1110,7 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
|
||||
struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
|
||||
struct cdns3_request *priv_req;
|
||||
struct cdns3_trb *trb;
|
||||
struct cdns3_trb *link_trb;
|
||||
dma_addr_t trb_dma;
|
||||
u32 togle_pcs = 1;
|
||||
int sg_iter = 0;
|
||||
@ -1099,11 +1119,13 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
|
||||
u32 control;
|
||||
int pcs;
|
||||
u16 total_tdl = 0;
|
||||
struct scatterlist *s = NULL;
|
||||
bool sg_supported = !!(request->num_mapped_sgs);
|
||||
|
||||
if (priv_ep->type == USB_ENDPOINT_XFER_ISOC)
|
||||
num_trb = priv_ep->interval;
|
||||
else
|
||||
num_trb = request->num_sgs ? request->num_sgs : 1;
|
||||
num_trb = sg_supported ? request->num_mapped_sgs : 1;
|
||||
|
||||
if (num_trb > priv_ep->free_trbs) {
|
||||
priv_ep->flags |= EP_RING_FULL;
|
||||
@ -1129,7 +1151,6 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
|
||||
|
||||
/* prepare ring */
|
||||
if ((priv_ep->enqueue + num_trb) >= (priv_ep->num_trbs - 1)) {
|
||||
struct cdns3_trb *link_trb;
|
||||
int doorbell, dma_index;
|
||||
u32 ch_bit = 0;
|
||||
|
||||
@ -1156,13 +1177,16 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
|
||||
TRBS_PER_SEGMENT > 2)
|
||||
ch_bit = TRB_CHAIN;
|
||||
|
||||
link_trb->control = ((priv_ep->pcs) ? TRB_CYCLE : 0) |
|
||||
TRB_TYPE(TRB_LINK) | TRB_TOGGLE | ch_bit;
|
||||
link_trb->control = cpu_to_le32(((priv_ep->pcs) ? TRB_CYCLE : 0) |
|
||||
TRB_TYPE(TRB_LINK) | TRB_TOGGLE | ch_bit);
|
||||
}
|
||||
|
||||
if (priv_dev->dev_ver <= DEV_VER_V2)
|
||||
togle_pcs = cdns3_wa1_update_guard(priv_ep, trb);
|
||||
|
||||
if (sg_supported)
|
||||
s = request->sg;
|
||||
|
||||
/* set incorrect Cycle Bit for first trb*/
|
||||
control = priv_ep->pcs ? 0 : TRB_CYCLE;
|
||||
|
||||
@ -1172,13 +1196,13 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
|
||||
|
||||
/* fill TRB */
|
||||
control |= TRB_TYPE(TRB_NORMAL);
|
||||
trb->buffer = TRB_BUFFER(request->num_sgs == 0
|
||||
? trb_dma : request->sg[sg_iter].dma_address);
|
||||
|
||||
if (likely(!request->num_sgs))
|
||||
if (sg_supported) {
|
||||
trb->buffer = cpu_to_le32(TRB_BUFFER(sg_dma_address(s)));
|
||||
length = sg_dma_len(s);
|
||||
} else {
|
||||
trb->buffer = cpu_to_le32(TRB_BUFFER(trb_dma));
|
||||
length = request->length;
|
||||
else
|
||||
length = request->sg[sg_iter].length;
|
||||
}
|
||||
|
||||
if (likely(priv_dev->dev_ver >= DEV_VER_V2))
|
||||
td_size = DIV_ROUND_UP(length,
|
||||
@ -1187,10 +1211,10 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
|
||||
total_tdl += DIV_ROUND_UP(length,
|
||||
priv_ep->endpoint.maxpacket);
|
||||
|
||||
trb->length = TRB_BURST_LEN(priv_ep->trb_burst_size) |
|
||||
TRB_LEN(length);
|
||||
trb->length = cpu_to_le32(TRB_BURST_LEN(priv_ep->trb_burst_size) |
|
||||
TRB_LEN(length));
|
||||
if (priv_dev->gadget.speed == USB_SPEED_SUPER)
|
||||
trb->length |= TRB_TDL_SS_SIZE(td_size);
|
||||
trb->length |= cpu_to_le32(TRB_TDL_SS_SIZE(td_size));
|
||||
else
|
||||
control |= TRB_TDL_HS_SIZE(td_size);
|
||||
|
||||
@ -1212,9 +1236,18 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
|
||||
}
|
||||
|
||||
if (sg_iter)
|
||||
trb->control = control;
|
||||
trb->control = cpu_to_le32(control);
|
||||
else
|
||||
priv_req->trb->control = control;
|
||||
priv_req->trb->control = cpu_to_le32(control);
|
||||
|
||||
if (sg_supported) {
|
||||
trb->control |= TRB_ISP;
|
||||
/* Don't set chain bit for last TRB */
|
||||
if (sg_iter < num_trb - 1)
|
||||
trb->control |= TRB_CHAIN;
|
||||
|
||||
s = sg_next(s);
|
||||
}
|
||||
|
||||
control = 0;
|
||||
++sg_iter;
|
||||
@ -1226,9 +1259,10 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
|
||||
trb = priv_req->trb;
|
||||
|
||||
priv_req->flags |= REQUEST_PENDING;
|
||||
priv_req->num_of_trb = num_trb;
|
||||
|
||||
if (sg_iter == 1)
|
||||
trb->control |= TRB_IOC | TRB_ISP;
|
||||
trb->control |= cpu_to_le32(TRB_IOC | TRB_ISP);
|
||||
|
||||
if (priv_dev->dev_ver < DEV_VER_V2 &&
|
||||
(priv_ep->flags & EP_TDLCHK_EN)) {
|
||||
@ -1254,12 +1288,27 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
|
||||
|
||||
/* give the TD to the consumer*/
|
||||
if (togle_pcs)
|
||||
trb->control = trb->control ^ 1;
|
||||
trb->control = trb->control ^ cpu_to_le32(1);
|
||||
|
||||
if (priv_dev->dev_ver <= DEV_VER_V2)
|
||||
cdns3_wa1_tray_restore_cycle_bit(priv_dev, priv_ep);
|
||||
|
||||
trace_cdns3_prepare_trb(priv_ep, priv_req->trb);
|
||||
if (num_trb > 1) {
|
||||
int i = 0;
|
||||
|
||||
while (i < num_trb) {
|
||||
trace_cdns3_prepare_trb(priv_ep, trb + i);
|
||||
if (trb + i == link_trb) {
|
||||
trb = priv_ep->trb_pool;
|
||||
num_trb = num_trb - i;
|
||||
i = 0;
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
trace_cdns3_prepare_trb(priv_ep, priv_req->trb);
|
||||
}
|
||||
|
||||
/*
|
||||
* Memory barrier - Cycle Bit must be set before trb->length and
|
||||
@ -1310,7 +1359,6 @@ void cdns3_set_hw_configuration(struct cdns3_device *priv_dev)
|
||||
{
|
||||
struct cdns3_endpoint *priv_ep;
|
||||
struct usb_ep *ep;
|
||||
int val;
|
||||
|
||||
if (priv_dev->hw_configured_flag)
|
||||
return;
|
||||
@ -1320,10 +1368,6 @@ void cdns3_set_hw_configuration(struct cdns3_device *priv_dev)
|
||||
cdns3_set_register_bit(&priv_dev->regs->usb_conf,
|
||||
USB_CONF_U1EN | USB_CONF_U2EN);
|
||||
|
||||
/* wait until configuration set */
|
||||
readl_poll_timeout_atomic(&priv_dev->regs->usb_sts, val,
|
||||
val & USB_STS_CFGSTS_MASK, 1, 100);
|
||||
|
||||
priv_dev->hw_configured_flag = 1;
|
||||
|
||||
list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) {
|
||||
@ -1337,7 +1381,7 @@ void cdns3_set_hw_configuration(struct cdns3_device *priv_dev)
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_request_handled - check whether request has been handled by DMA
|
||||
* cdns3_trb_handled - check whether trb has been handled by DMA
|
||||
*
|
||||
* @priv_ep: extended endpoint object.
|
||||
* @priv_req: request object for checking
|
||||
@ -1354,32 +1398,28 @@ void cdns3_set_hw_configuration(struct cdns3_device *priv_dev)
|
||||
* ET = priv_req->end_trb - index of last TRB in transfer ring
|
||||
* CI = current_index - index of processed TRB by DMA.
|
||||
*
|
||||
* As first step, function checks if cycle bit for priv_req->start_trb is
|
||||
* correct.
|
||||
* As first step, we check if the TRB between the ST and ET.
|
||||
* Then, we check if cycle bit for index priv_ep->dequeue
|
||||
* is correct.
|
||||
*
|
||||
* some rules:
|
||||
* 1. priv_ep->dequeue never exceed current_index.
|
||||
* 1. priv_ep->dequeue never equals to current_index.
|
||||
* 2 priv_ep->enqueue never exceed priv_ep->dequeue
|
||||
* 3. exception: priv_ep->enqueue == priv_ep->dequeue
|
||||
* and priv_ep->free_trbs is zero.
|
||||
* This case indicate that TR is full.
|
||||
*
|
||||
* Then We can split recognition into two parts:
|
||||
* At below two cases, the request have been handled.
|
||||
* Case 1 - priv_ep->dequeue < current_index
|
||||
* SR ... EQ ... DQ ... CI ... ER
|
||||
* SR ... DQ ... CI ... EQ ... ER
|
||||
*
|
||||
* Request has been handled by DMA if ST and ET is between DQ and CI.
|
||||
*
|
||||
* Case 2 - priv_ep->dequeue > current_index
|
||||
* This situation take place when CI go through the LINK TRB at the end of
|
||||
* This situation takes place when CI go through the LINK TRB at the end of
|
||||
* transfer ring.
|
||||
* SR ... CI ... EQ ... DQ ... ER
|
||||
*
|
||||
* Request has been handled by DMA if ET is less then CI or
|
||||
* ET is greater or equal DQ.
|
||||
*/
|
||||
static bool cdns3_request_handled(struct cdns3_endpoint *priv_ep,
|
||||
static bool cdns3_trb_handled(struct cdns3_endpoint *priv_ep,
|
||||
struct cdns3_request *priv_req)
|
||||
{
|
||||
struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
|
||||
@ -1391,9 +1431,27 @@ static bool cdns3_request_handled(struct cdns3_endpoint *priv_ep,
|
||||
current_index = cdns3_get_dma_pos(priv_dev, priv_ep);
|
||||
doorbell = !!(readl(&priv_dev->regs->ep_cmd) & EP_CMD_DRDY);
|
||||
|
||||
trb = &priv_ep->trb_pool[priv_req->start_trb];
|
||||
/* current trb doesn't belong to this request */
|
||||
if (priv_req->start_trb < priv_req->end_trb) {
|
||||
if (priv_ep->dequeue > priv_req->end_trb)
|
||||
goto finish;
|
||||
|
||||
if ((trb->control & TRB_CYCLE) != priv_ep->ccs)
|
||||
if (priv_ep->dequeue < priv_req->start_trb)
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if ((priv_req->start_trb > priv_req->end_trb) &&
|
||||
(priv_ep->dequeue > priv_req->end_trb) &&
|
||||
(priv_ep->dequeue < priv_req->start_trb))
|
||||
goto finish;
|
||||
|
||||
if ((priv_req->start_trb == priv_req->end_trb) &&
|
||||
(priv_ep->dequeue != priv_req->end_trb))
|
||||
goto finish;
|
||||
|
||||
trb = &priv_ep->trb_pool[priv_ep->dequeue];
|
||||
|
||||
if ((le32_to_cpu(trb->control) & TRB_CYCLE) != priv_ep->ccs)
|
||||
goto finish;
|
||||
|
||||
if (doorbell == 1 && current_index == priv_ep->dequeue)
|
||||
@ -1413,12 +1471,8 @@ static bool cdns3_request_handled(struct cdns3_endpoint *priv_ep,
|
||||
!priv_ep->dequeue)
|
||||
goto finish;
|
||||
|
||||
if (priv_req->end_trb >= priv_ep->dequeue &&
|
||||
priv_req->end_trb < current_index)
|
||||
handled = 1;
|
||||
handled = 1;
|
||||
} else if (priv_ep->dequeue > current_index) {
|
||||
if (priv_req->end_trb < current_index ||
|
||||
priv_req->end_trb >= priv_ep->dequeue)
|
||||
handled = 1;
|
||||
}
|
||||
|
||||
@ -1434,6 +1488,8 @@ static void cdns3_transfer_completed(struct cdns3_device *priv_dev,
|
||||
struct cdns3_request *priv_req;
|
||||
struct usb_request *request;
|
||||
struct cdns3_trb *trb;
|
||||
bool request_handled = false;
|
||||
bool transfer_end = false;
|
||||
|
||||
while (!list_empty(&priv_ep->pending_req_list)) {
|
||||
request = cdns3_next_request(&priv_ep->pending_req_list);
|
||||
@ -1442,7 +1498,7 @@ static void cdns3_transfer_completed(struct cdns3_device *priv_dev,
|
||||
trb = priv_ep->trb_pool + priv_ep->dequeue;
|
||||
|
||||
/* Request was dequeued and TRB was changed to TRB_LINK. */
|
||||
if (TRB_FIELD_TO_TYPE(trb->control) == TRB_LINK) {
|
||||
if (TRB_FIELD_TO_TYPE(le32_to_cpu(trb->control)) == TRB_LINK) {
|
||||
trace_cdns3_complete_trb(priv_ep, trb);
|
||||
cdns3_move_deq_to_next_trb(priv_req);
|
||||
}
|
||||
@ -1453,20 +1509,32 @@ static void cdns3_transfer_completed(struct cdns3_device *priv_dev,
|
||||
*/
|
||||
cdns3_select_ep(priv_dev, priv_ep->endpoint.address);
|
||||
|
||||
if (!cdns3_request_handled(priv_ep, priv_req))
|
||||
while (cdns3_trb_handled(priv_ep, priv_req)) {
|
||||
priv_req->finished_trb++;
|
||||
if (priv_req->finished_trb >= priv_req->num_of_trb)
|
||||
request_handled = true;
|
||||
|
||||
trb = priv_ep->trb_pool + priv_ep->dequeue;
|
||||
trace_cdns3_complete_trb(priv_ep, trb);
|
||||
|
||||
if (!transfer_end)
|
||||
request->actual +=
|
||||
TRB_LEN(le32_to_cpu(trb->length));
|
||||
|
||||
if (priv_req->num_of_trb > 1 &&
|
||||
le32_to_cpu(trb->control) & TRB_SMM)
|
||||
transfer_end = true;
|
||||
|
||||
cdns3_ep_inc_deq(priv_ep);
|
||||
}
|
||||
|
||||
if (request_handled) {
|
||||
cdns3_gadget_giveback(priv_ep, priv_req, 0);
|
||||
request_handled = false;
|
||||
transfer_end = false;
|
||||
} else {
|
||||
goto prepare_next_td;
|
||||
|
||||
trb = priv_ep->trb_pool + priv_ep->dequeue;
|
||||
trace_cdns3_complete_trb(priv_ep, trb);
|
||||
|
||||
if (trb != priv_req->trb)
|
||||
dev_warn(priv_dev->dev,
|
||||
"request_trb=0x%p, queue_trb=0x%p\n",
|
||||
priv_req->trb, trb);
|
||||
|
||||
request->actual = TRB_LEN(le32_to_cpu(trb->length));
|
||||
cdns3_move_deq_to_next_trb(priv_req);
|
||||
cdns3_gadget_giveback(priv_ep, priv_req, 0);
|
||||
}
|
||||
|
||||
if (priv_ep->type != USB_ENDPOINT_XFER_ISOC &&
|
||||
TRBS_PER_SEGMENT == 2)
|
||||
@ -1574,7 +1642,7 @@ static int cdns3_check_ep_interrupt_proceed(struct cdns3_endpoint *priv_ep)
|
||||
* that host ignore the ERDY packet and driver has to send it
|
||||
* again.
|
||||
*/
|
||||
if (tdl && (dbusy | !EP_STS_BUFFEMPTY(ep_sts_reg) |
|
||||
if (tdl && (dbusy || !EP_STS_BUFFEMPTY(ep_sts_reg) ||
|
||||
EP_STS_HOSTPP(ep_sts_reg))) {
|
||||
writel(EP_CMD_ERDY |
|
||||
EP_CMD_ERDY_SID(priv_ep->last_stream_id),
|
||||
@ -1769,9 +1837,13 @@ static void cdns3_check_usb_interrupt_proceed(struct cdns3_device *priv_dev,
|
||||
static irqreturn_t cdns3_device_irq_handler(int irq, void *data)
|
||||
{
|
||||
struct cdns3_device *priv_dev = data;
|
||||
struct cdns3 *cdns = dev_get_drvdata(priv_dev->dev);
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
u32 reg;
|
||||
|
||||
if (cdns->in_lpm)
|
||||
return ret;
|
||||
|
||||
/* check USB device interrupt */
|
||||
reg = readl(&priv_dev->regs->usb_ists);
|
||||
if (reg) {
|
||||
@ -2552,10 +2624,10 @@ found:
|
||||
|
||||
/* Update ring only if removed request is on pending_req_list list */
|
||||
if (req_on_hw_ring && link_trb) {
|
||||
link_trb->buffer = TRB_BUFFER(priv_ep->trb_pool_dma +
|
||||
((priv_req->end_trb + 1) * TRB_SIZE));
|
||||
link_trb->control = (link_trb->control & TRB_CYCLE) |
|
||||
TRB_TYPE(TRB_LINK) | TRB_CHAIN;
|
||||
link_trb->buffer = cpu_to_le32(TRB_BUFFER(priv_ep->trb_pool_dma +
|
||||
((priv_req->end_trb + 1) * TRB_SIZE)));
|
||||
link_trb->control = cpu_to_le32((le32_to_cpu(link_trb->control) & TRB_CYCLE) |
|
||||
TRB_TYPE(TRB_LINK) | TRB_CHAIN);
|
||||
|
||||
if (priv_ep->wa1_trb == priv_req->trb)
|
||||
cdns3_wa1_restore_cycle_bit(priv_ep);
|
||||
@ -2610,7 +2682,7 @@ int __cdns3_gadget_ep_clear_halt(struct cdns3_endpoint *priv_ep)
|
||||
priv_req = to_cdns3_request(request);
|
||||
trb = priv_req->trb;
|
||||
if (trb)
|
||||
trb->control = trb->control ^ TRB_CYCLE;
|
||||
trb->control = trb->control ^ cpu_to_le32(TRB_CYCLE);
|
||||
}
|
||||
|
||||
writel(EP_CMD_CSTALL | EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
|
||||
@ -2625,7 +2697,8 @@ int __cdns3_gadget_ep_clear_halt(struct cdns3_endpoint *priv_ep)
|
||||
|
||||
if (request) {
|
||||
if (trb)
|
||||
trb->control = trb->control ^ TRB_CYCLE;
|
||||
trb->control = trb->control ^ cpu_to_le32(TRB_CYCLE);
|
||||
|
||||
cdns3_rearm_transfer(priv_ep, 1);
|
||||
}
|
||||
|
||||
@ -2735,10 +2808,13 @@ static int cdns3_gadget_pullup(struct usb_gadget *gadget, int is_on)
|
||||
{
|
||||
struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
|
||||
|
||||
if (is_on)
|
||||
if (is_on) {
|
||||
writel(USB_CONF_DEVEN, &priv_dev->regs->usb_conf);
|
||||
else
|
||||
} else {
|
||||
writel(~0, &priv_dev->regs->ep_ists);
|
||||
writel(~0, &priv_dev->regs->usb_ists);
|
||||
writel(USB_CONF_DEVDS, &priv_dev->regs->usb_conf);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -2779,6 +2855,8 @@ static void cdns3_gadget_config(struct cdns3_device *priv_dev)
|
||||
/* enable generic interrupt*/
|
||||
writel(USB_IEN_INIT, ®s->usb_ien);
|
||||
writel(USB_CONF_CLK2OFFDS | USB_CONF_L1DS, ®s->usb_conf);
|
||||
/* keep Fast Access bit */
|
||||
writel(PUSB_PWR_FST_REG_ACCESS, &priv_dev->regs->usb_pwr);
|
||||
|
||||
cdns3_configure_dmult(priv_dev, NULL);
|
||||
}
|
||||
@ -2862,6 +2940,7 @@ static int cdns3_gadget_udc_stop(struct usb_gadget *gadget)
|
||||
|
||||
/* disable interrupt for device */
|
||||
writel(0, &priv_dev->regs->usb_ien);
|
||||
writel(0, &priv_dev->regs->usb_pwr);
|
||||
writel(USB_CONF_DEVDS, &priv_dev->regs->usb_conf);
|
||||
|
||||
return 0;
|
||||
@ -2984,18 +3063,26 @@ err:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void cdns3_gadget_release(struct device *dev)
|
||||
{
|
||||
struct cdns3_device *priv_dev = container_of(dev,
|
||||
struct cdns3_device, gadget.dev);
|
||||
|
||||
kfree(priv_dev);
|
||||
}
|
||||
|
||||
void cdns3_gadget_exit(struct cdns3 *cdns)
|
||||
{
|
||||
struct cdns3_device *priv_dev;
|
||||
|
||||
priv_dev = cdns->gadget_dev;
|
||||
|
||||
devm_free_irq(cdns->dev, cdns->dev_irq, priv_dev);
|
||||
|
||||
pm_runtime_mark_last_busy(cdns->dev);
|
||||
pm_runtime_put_autosuspend(cdns->dev);
|
||||
|
||||
usb_del_gadget_udc(&priv_dev->gadget);
|
||||
usb_del_gadget(&priv_dev->gadget);
|
||||
devm_free_irq(cdns->dev, cdns->dev_irq, priv_dev);
|
||||
|
||||
cdns3_free_all_eps(priv_dev);
|
||||
|
||||
@ -3015,7 +3102,7 @@ void cdns3_gadget_exit(struct cdns3 *cdns)
|
||||
priv_dev->setup_dma);
|
||||
|
||||
kfree(priv_dev->zlp_buf);
|
||||
kfree(priv_dev);
|
||||
usb_put_gadget(&priv_dev->gadget);
|
||||
cdns->gadget_dev = NULL;
|
||||
cdns3_drd_gadget_off(cdns);
|
||||
}
|
||||
@ -3030,6 +3117,8 @@ static int cdns3_gadget_start(struct cdns3 *cdns)
|
||||
if (!priv_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
usb_initialize_gadget(cdns->dev, &priv_dev->gadget,
|
||||
cdns3_gadget_release);
|
||||
cdns->gadget_dev = priv_dev;
|
||||
priv_dev->sysdev = cdns->dev;
|
||||
priv_dev->dev = cdns->dev;
|
||||
@ -3070,7 +3159,6 @@ static int cdns3_gadget_start(struct cdns3 *cdns)
|
||||
priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
priv_dev->gadget.ops = &cdns3_gadget_ops;
|
||||
priv_dev->gadget.name = "usb-ss-gadget";
|
||||
priv_dev->gadget.sg_supported = 1;
|
||||
priv_dev->gadget.quirk_avoids_skb_reserve = 1;
|
||||
priv_dev->gadget.irq = cdns->dev_irq;
|
||||
|
||||
@ -3109,6 +3197,8 @@ static int cdns3_gadget_start(struct cdns3 *cdns)
|
||||
readl(&priv_dev->regs->usb_cap2));
|
||||
|
||||
priv_dev->dev_ver = GET_DEV_BASE_VERSION(priv_dev->dev_ver);
|
||||
if (priv_dev->dev_ver >= DEV_VER_V2)
|
||||
priv_dev->gadget.sg_supported = 1;
|
||||
|
||||
priv_dev->zlp_buf = kzalloc(CDNS3_EP_ZLP_BUF_SIZE, GFP_KERNEL);
|
||||
if (!priv_dev->zlp_buf) {
|
||||
@ -3117,10 +3207,9 @@ static int cdns3_gadget_start(struct cdns3 *cdns)
|
||||
}
|
||||
|
||||
/* add USB gadget device */
|
||||
ret = usb_add_gadget_udc(priv_dev->dev, &priv_dev->gadget);
|
||||
ret = usb_add_gadget(&priv_dev->gadget);
|
||||
if (ret < 0) {
|
||||
dev_err(priv_dev->dev,
|
||||
"Failed to register USB device controller\n");
|
||||
dev_err(priv_dev->dev, "Failed to add gadget\n");
|
||||
goto err4;
|
||||
}
|
||||
|
||||
@ -3133,6 +3222,7 @@ err3:
|
||||
err2:
|
||||
cdns3_free_all_eps(priv_dev);
|
||||
err1:
|
||||
usb_put_gadget(&priv_dev->gadget);
|
||||
cdns->gadget_dev = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
@ -966,7 +966,7 @@ struct cdns3_usb_regs {
|
||||
/*
|
||||
* USBSS-DEV DMA interface.
|
||||
*/
|
||||
#define TRBS_PER_SEGMENT 40
|
||||
#define TRBS_PER_SEGMENT 600
|
||||
|
||||
#define ISO_MAX_INTERVAL 10
|
||||
|
||||
@ -1030,6 +1030,11 @@ struct cdns3_trb {
|
||||
* When set to '1', the device will toggle its interpretation of the Cycle bit
|
||||
*/
|
||||
#define TRB_TOGGLE BIT(1)
|
||||
/*
|
||||
* The controller will set it if OUTSMM (OUT size mismatch) is detected,
|
||||
* this bit is for normal TRB
|
||||
*/
|
||||
#define TRB_SMM BIT(1)
|
||||
|
||||
/*
|
||||
* Short Packet (SP). OUT EPs at DMULT=1 only. Indicates if the TRB was
|
||||
@ -1215,6 +1220,8 @@ struct cdns3_aligned_buf {
|
||||
* this endpoint
|
||||
* @flags: flag specifying special usage of request
|
||||
* @list: used by internally allocated request to add to wa2_descmiss_req_list.
|
||||
* @finished_trb: number of trb has already finished per request
|
||||
* @num_of_trb: how many trbs in this request
|
||||
*/
|
||||
struct cdns3_request {
|
||||
struct usb_request request;
|
||||
@ -1230,6 +1237,8 @@ struct cdns3_request {
|
||||
#define REQUEST_UNALIGNED BIT(4)
|
||||
u32 flags;
|
||||
struct list_head list;
|
||||
int finished_trb;
|
||||
int num_of_trb;
|
||||
};
|
||||
|
||||
#define to_cdns3_request(r) (container_of(r, struct cdns3_request, request))
|
||||
|
@ -13,11 +13,13 @@
|
||||
#include "core.h"
|
||||
#include "drd.h"
|
||||
#include "host-export.h"
|
||||
#include <linux/usb/hcd.h>
|
||||
|
||||
static int __cdns3_host_init(struct cdns3 *cdns)
|
||||
{
|
||||
struct platform_device *xhci;
|
||||
int ret;
|
||||
struct usb_hcd *hcd;
|
||||
|
||||
cdns3_drd_host_on(cdns);
|
||||
|
||||
@ -43,6 +45,11 @@ static int __cdns3_host_init(struct cdns3 *cdns)
|
||||
goto err1;
|
||||
}
|
||||
|
||||
/* Glue needs to access xHCI region register for Power management */
|
||||
hcd = platform_get_drvdata(xhci);
|
||||
if (hcd)
|
||||
cdns->xhci_regs = hcd->regs;
|
||||
|
||||
return 0;
|
||||
err1:
|
||||
platform_device_put(xhci);
|
||||
|
@ -5,6 +5,7 @@ config USB_DWC2
|
||||
depends on HAS_DMA
|
||||
depends on USB || USB_GADGET
|
||||
depends on HAS_IOMEM
|
||||
select USB_ROLE_SWITCH
|
||||
help
|
||||
Say Y here if your system has a Dual Role Hi-Speed USB
|
||||
controller based on the DesignWare HSOTG IP Core.
|
||||
|
@ -3,7 +3,7 @@ ccflags-$(CONFIG_USB_DWC2_DEBUG) += -DDEBUG
|
||||
ccflags-$(CONFIG_USB_DWC2_VERBOSE) += -DVERBOSE_DEBUG
|
||||
|
||||
obj-$(CONFIG_USB_DWC2) += dwc2.o
|
||||
dwc2-y := core.o core_intr.o platform.o
|
||||
dwc2-y := core.o core_intr.o platform.o drd.o
|
||||
dwc2-y += params.o
|
||||
|
||||
ifneq ($(filter y,$(CONFIG_USB_DWC2_HOST) $(CONFIG_USB_DWC2_DUAL_ROLE)),)
|
||||
|
@ -860,6 +860,7 @@ struct dwc2_hregs_backup {
|
||||
* - USB_DR_MODE_PERIPHERAL
|
||||
* - USB_DR_MODE_HOST
|
||||
* - USB_DR_MODE_OTG
|
||||
* @role_sw: usb_role_switch handle
|
||||
* @hcd_enabled: Host mode sub-driver initialization indicator.
|
||||
* @gadget_enabled: Peripheral mode sub-driver initialization indicator.
|
||||
* @ll_hw_enabled: Status of low-level hardware resources.
|
||||
@ -1054,6 +1055,7 @@ struct dwc2_hsotg {
|
||||
struct dwc2_core_params params;
|
||||
enum usb_otg_state op_state;
|
||||
enum usb_dr_mode dr_mode;
|
||||
struct usb_role_switch *role_sw;
|
||||
unsigned int hcd_enabled:1;
|
||||
unsigned int gadget_enabled:1;
|
||||
unsigned int ll_hw_enabled:1;
|
||||
@ -1376,6 +1378,11 @@ static inline int dwc2_is_device_mode(struct dwc2_hsotg *hsotg)
|
||||
return (dwc2_readl(hsotg, GINTSTS) & GINTSTS_CURMODE_HOST) == 0;
|
||||
}
|
||||
|
||||
int dwc2_drd_init(struct dwc2_hsotg *hsotg);
|
||||
void dwc2_drd_suspend(struct dwc2_hsotg *hsotg);
|
||||
void dwc2_drd_resume(struct dwc2_hsotg *hsotg);
|
||||
void dwc2_drd_exit(struct dwc2_hsotg *hsotg);
|
||||
|
||||
/*
|
||||
* Dump core registers and SPRAM
|
||||
*/
|
||||
@ -1392,6 +1399,7 @@ int dwc2_hsotg_resume(struct dwc2_hsotg *dwc2);
|
||||
int dwc2_gadget_init(struct dwc2_hsotg *hsotg);
|
||||
void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2,
|
||||
bool reset);
|
||||
void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg);
|
||||
void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg);
|
||||
void dwc2_hsotg_disconnect(struct dwc2_hsotg *dwc2);
|
||||
int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode);
|
||||
@ -1417,6 +1425,7 @@ static inline int dwc2_gadget_init(struct dwc2_hsotg *hsotg)
|
||||
{ return 0; }
|
||||
static inline void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2,
|
||||
bool reset) {}
|
||||
static inline void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg) {}
|
||||
static inline void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg) {}
|
||||
static inline void dwc2_hsotg_disconnect(struct dwc2_hsotg *dwc2) {}
|
||||
static inline int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg,
|
||||
|
180
drivers/usb/dwc2/drd.c
Normal file
180
drivers/usb/dwc2/drd.c
Normal file
@ -0,0 +1,180 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* drd.c - DesignWare USB2 DRD Controller Dual-role support
|
||||
*
|
||||
* Copyright (C) 2020 STMicroelectronics
|
||||
*
|
||||
* Author(s): Amelie Delaunay <amelie.delaunay@st.com>
|
||||
*/
|
||||
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/usb/role.h>
|
||||
#include "core.h"
|
||||
|
||||
static void dwc2_ovr_init(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 gotgctl;
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
|
||||
gotgctl = dwc2_readl(hsotg, GOTGCTL);
|
||||
gotgctl |= GOTGCTL_BVALOEN | GOTGCTL_AVALOEN | GOTGCTL_VBVALOEN;
|
||||
gotgctl |= GOTGCTL_DBNCE_FLTR_BYPASS;
|
||||
gotgctl &= ~(GOTGCTL_BVALOVAL | GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL);
|
||||
dwc2_writel(hsotg, gotgctl, GOTGCTL);
|
||||
|
||||
dwc2_force_mode(hsotg, false);
|
||||
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
}
|
||||
|
||||
static int dwc2_ovr_avalid(struct dwc2_hsotg *hsotg, bool valid)
|
||||
{
|
||||
u32 gotgctl = dwc2_readl(hsotg, GOTGCTL);
|
||||
|
||||
/* Check if A-Session is already in the right state */
|
||||
if ((valid && (gotgctl & GOTGCTL_ASESVLD)) ||
|
||||
(!valid && !(gotgctl & GOTGCTL_ASESVLD)))
|
||||
return -EALREADY;
|
||||
|
||||
if (valid)
|
||||
gotgctl |= GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL;
|
||||
else
|
||||
gotgctl &= ~(GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL);
|
||||
dwc2_writel(hsotg, gotgctl, GOTGCTL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc2_ovr_bvalid(struct dwc2_hsotg *hsotg, bool valid)
|
||||
{
|
||||
u32 gotgctl = dwc2_readl(hsotg, GOTGCTL);
|
||||
|
||||
/* Check if B-Session is already in the right state */
|
||||
if ((valid && (gotgctl & GOTGCTL_BSESVLD)) ||
|
||||
(!valid && !(gotgctl & GOTGCTL_BSESVLD)))
|
||||
return -EALREADY;
|
||||
|
||||
if (valid)
|
||||
gotgctl |= GOTGCTL_BVALOVAL | GOTGCTL_VBVALOVAL;
|
||||
else
|
||||
gotgctl &= ~(GOTGCTL_BVALOVAL | GOTGCTL_VBVALOVAL);
|
||||
dwc2_writel(hsotg, gotgctl, GOTGCTL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc2_drd_role_sw_set(struct usb_role_switch *sw, enum usb_role role)
|
||||
{
|
||||
struct dwc2_hsotg *hsotg = usb_role_switch_get_drvdata(sw);
|
||||
unsigned long flags;
|
||||
int already = 0;
|
||||
|
||||
/* Skip session not in line with dr_mode */
|
||||
if ((role == USB_ROLE_DEVICE && hsotg->dr_mode == USB_DR_MODE_HOST) ||
|
||||
(role == USB_ROLE_HOST && hsotg->dr_mode == USB_DR_MODE_PERIPHERAL))
|
||||
return -EINVAL;
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \
|
||||
IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
|
||||
/* Skip session if core is in test mode */
|
||||
if (role == USB_ROLE_NONE && hsotg->test_mode) {
|
||||
dev_dbg(hsotg->dev, "Core is in test mode\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
#endif
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
|
||||
if (role == USB_ROLE_HOST) {
|
||||
already = dwc2_ovr_avalid(hsotg, true);
|
||||
} else if (role == USB_ROLE_DEVICE) {
|
||||
already = dwc2_ovr_bvalid(hsotg, true);
|
||||
/* This clear DCTL.SFTDISCON bit */
|
||||
dwc2_hsotg_core_connect(hsotg);
|
||||
} else {
|
||||
if (dwc2_is_device_mode(hsotg)) {
|
||||
if (!dwc2_ovr_bvalid(hsotg, false))
|
||||
/* This set DCTL.SFTDISCON bit */
|
||||
dwc2_hsotg_core_disconnect(hsotg);
|
||||
} else {
|
||||
dwc2_ovr_avalid(hsotg, false);
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
|
||||
if (!already && hsotg->dr_mode == USB_DR_MODE_OTG)
|
||||
/* This will raise a Connector ID Status Change Interrupt */
|
||||
dwc2_force_mode(hsotg, role == USB_ROLE_HOST);
|
||||
|
||||
dev_dbg(hsotg->dev, "%s-session valid\n",
|
||||
role == USB_ROLE_NONE ? "No" :
|
||||
role == USB_ROLE_HOST ? "A" : "B");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dwc2_drd_init(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct usb_role_switch_desc role_sw_desc = {0};
|
||||
struct usb_role_switch *role_sw;
|
||||
int ret;
|
||||
|
||||
if (!device_property_read_bool(hsotg->dev, "usb-role-switch"))
|
||||
return 0;
|
||||
|
||||
role_sw_desc.driver_data = hsotg;
|
||||
role_sw_desc.fwnode = dev_fwnode(hsotg->dev);
|
||||
role_sw_desc.set = dwc2_drd_role_sw_set;
|
||||
role_sw_desc.allow_userspace_control = true;
|
||||
|
||||
role_sw = usb_role_switch_register(hsotg->dev, &role_sw_desc);
|
||||
if (IS_ERR(role_sw)) {
|
||||
ret = PTR_ERR(role_sw);
|
||||
dev_err(hsotg->dev,
|
||||
"failed to register role switch: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
hsotg->role_sw = role_sw;
|
||||
|
||||
/* Enable override and initialize values */
|
||||
dwc2_ovr_init(hsotg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dwc2_drd_suspend(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
u32 gintsts, gintmsk;
|
||||
|
||||
if (hsotg->role_sw && !hsotg->params.external_id_pin_ctl) {
|
||||
gintmsk = dwc2_readl(hsotg, GINTMSK);
|
||||
gintmsk &= ~GINTSTS_CONIDSTSCHNG;
|
||||
dwc2_writel(hsotg, gintmsk, GINTMSK);
|
||||
gintsts = dwc2_readl(hsotg, GINTSTS);
|
||||
dwc2_writel(hsotg, gintsts | GINTSTS_CONIDSTSCHNG, GINTSTS);
|
||||
}
|
||||
}
|
||||
|
||||
void dwc2_drd_resume(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
u32 gintsts, gintmsk;
|
||||
|
||||
if (hsotg->role_sw && !hsotg->params.external_id_pin_ctl) {
|
||||
gintsts = dwc2_readl(hsotg, GINTSTS);
|
||||
dwc2_writel(hsotg, gintsts | GINTSTS_CONIDSTSCHNG, GINTSTS);
|
||||
gintmsk = dwc2_readl(hsotg, GINTMSK);
|
||||
gintmsk |= GINTSTS_CONIDSTSCHNG;
|
||||
dwc2_writel(hsotg, gintmsk, GINTMSK);
|
||||
}
|
||||
}
|
||||
|
||||
void dwc2_drd_exit(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
if (hsotg->role_sw)
|
||||
usb_role_switch_unregister(hsotg->role_sw);
|
||||
}
|
@ -713,8 +713,11 @@ static u32 dwc2_hsotg_read_frameno(struct dwc2_hsotg *hsotg)
|
||||
*/
|
||||
static unsigned int dwc2_gadget_get_chain_limit(struct dwc2_hsotg_ep *hs_ep)
|
||||
{
|
||||
const struct usb_endpoint_descriptor *ep_desc = hs_ep->ep.desc;
|
||||
int is_isoc = hs_ep->isochronous;
|
||||
unsigned int maxsize;
|
||||
u32 mps = hs_ep->ep.maxpacket;
|
||||
int dir_in = hs_ep->dir_in;
|
||||
|
||||
if (is_isoc)
|
||||
maxsize = (hs_ep->dir_in ? DEV_DMA_ISOC_TX_NBYTES_LIMIT :
|
||||
@ -723,6 +726,11 @@ static unsigned int dwc2_gadget_get_chain_limit(struct dwc2_hsotg_ep *hs_ep)
|
||||
else
|
||||
maxsize = DEV_DMA_NBYTES_LIMIT * MAX_DMA_DESC_NUM_GENERIC;
|
||||
|
||||
/* Interrupt OUT EP with mps not multiple of 4 */
|
||||
if (hs_ep->index)
|
||||
if (usb_endpoint_xfer_int(ep_desc) && !dir_in && (mps % 4))
|
||||
maxsize = mps * MAX_DMA_DESC_NUM_GENERIC;
|
||||
|
||||
return maxsize;
|
||||
}
|
||||
|
||||
@ -738,11 +746,14 @@ static unsigned int dwc2_gadget_get_chain_limit(struct dwc2_hsotg_ep *hs_ep)
|
||||
* Isochronous - descriptor rx/tx bytes bitfield limit,
|
||||
* Control In/Bulk/Interrupt - multiple of mps. This will allow to not
|
||||
* have concatenations from various descriptors within one packet.
|
||||
* Interrupt OUT - if mps not multiple of 4 then a single packet corresponds
|
||||
* to a single descriptor.
|
||||
*
|
||||
* Selects corresponding mask for RX/TX bytes as well.
|
||||
*/
|
||||
static u32 dwc2_gadget_get_desc_params(struct dwc2_hsotg_ep *hs_ep, u32 *mask)
|
||||
{
|
||||
const struct usb_endpoint_descriptor *ep_desc = hs_ep->ep.desc;
|
||||
u32 mps = hs_ep->ep.maxpacket;
|
||||
int dir_in = hs_ep->dir_in;
|
||||
u32 desc_size = 0;
|
||||
@ -766,6 +777,13 @@ static u32 dwc2_gadget_get_desc_params(struct dwc2_hsotg_ep *hs_ep, u32 *mask)
|
||||
desc_size -= desc_size % mps;
|
||||
}
|
||||
|
||||
/* Interrupt OUT EP with mps not multiple of 4 */
|
||||
if (hs_ep->index)
|
||||
if (usb_endpoint_xfer_int(ep_desc) && !dir_in && (mps % 4)) {
|
||||
desc_size = mps;
|
||||
*mask = DEV_DMA_NBYTES_MASK;
|
||||
}
|
||||
|
||||
return desc_size;
|
||||
}
|
||||
|
||||
@ -1123,13 +1141,7 @@ static void dwc2_hsotg_start_req(struct dwc2_hsotg *hsotg,
|
||||
length += (mps - (length % mps));
|
||||
}
|
||||
|
||||
/*
|
||||
* If more data to send, adjust DMA for EP0 out data stage.
|
||||
* ureq->dma stays unchanged, hence increment it by already
|
||||
* passed passed data count before starting new transaction.
|
||||
*/
|
||||
if (!index && hsotg->ep0_state == DWC2_EP0_DATA_OUT &&
|
||||
continuing)
|
||||
if (continuing)
|
||||
offset = ureq->actual;
|
||||
|
||||
/* Fill DDMA chain entries */
|
||||
@ -2320,22 +2332,36 @@ static void dwc2_hsotg_change_ep_iso_parity(struct dwc2_hsotg *hsotg,
|
||||
*/
|
||||
static unsigned int dwc2_gadget_get_xfersize_ddma(struct dwc2_hsotg_ep *hs_ep)
|
||||
{
|
||||
const struct usb_endpoint_descriptor *ep_desc = hs_ep->ep.desc;
|
||||
struct dwc2_hsotg *hsotg = hs_ep->parent;
|
||||
unsigned int bytes_rem = 0;
|
||||
unsigned int bytes_rem_correction = 0;
|
||||
struct dwc2_dma_desc *desc = hs_ep->desc_list;
|
||||
int i;
|
||||
u32 status;
|
||||
u32 mps = hs_ep->ep.maxpacket;
|
||||
int dir_in = hs_ep->dir_in;
|
||||
|
||||
if (!desc)
|
||||
return -EINVAL;
|
||||
|
||||
/* Interrupt OUT EP with mps not multiple of 4 */
|
||||
if (hs_ep->index)
|
||||
if (usb_endpoint_xfer_int(ep_desc) && !dir_in && (mps % 4))
|
||||
bytes_rem_correction = 4 - (mps % 4);
|
||||
|
||||
for (i = 0; i < hs_ep->desc_count; ++i) {
|
||||
status = desc->status;
|
||||
bytes_rem += status & DEV_DMA_NBYTES_MASK;
|
||||
bytes_rem -= bytes_rem_correction;
|
||||
|
||||
if (status & DEV_DMA_STS_MASK)
|
||||
dev_err(hsotg->dev, "descriptor %d closed with %x\n",
|
||||
i, status & DEV_DMA_STS_MASK);
|
||||
|
||||
if (status & DEV_DMA_L)
|
||||
break;
|
||||
|
||||
desc++;
|
||||
}
|
||||
|
||||
@ -3530,7 +3556,7 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
|
||||
dwc2_readl(hsotg, DOEPCTL0));
|
||||
}
|
||||
|
||||
static void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg)
|
||||
void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
/* set the soft-disconnect bit */
|
||||
dwc2_set_bit(hsotg, DCTL, DCTL_SFTDISCON);
|
||||
|
@ -185,7 +185,7 @@ 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->activate_stm_id_vb_detection = !device_property_read_bool(hsotg->dev, "usb-role-switch");
|
||||
p->host_rx_fifo_size = 440;
|
||||
p->host_nperio_tx_fifo_size = 256;
|
||||
p->host_perio_tx_fifo_size = 256;
|
||||
@ -210,6 +210,7 @@ const struct of_device_id dwc2_of_match_table[] = {
|
||||
{ .compatible = "amlogic,meson-g12a-usb",
|
||||
.data = dwc2_set_amlogic_g12a_params },
|
||||
{ .compatible = "amcc,dwc-otg", .data = dwc2_set_amcc_params },
|
||||
{ .compatible = "apm,apm82181-dwc-otg", .data = dwc2_set_amcc_params },
|
||||
{ .compatible = "st,stm32f4x9-fsotg",
|
||||
.data = dwc2_set_stm32f4x9_fsotg_params },
|
||||
{ .compatible = "st,stm32f4x9-hsotg" },
|
||||
@ -860,7 +861,7 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
|
||||
int dwc2_init_params(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
void (*set_params)(void *data);
|
||||
void (*set_params)(struct dwc2_hsotg *data);
|
||||
|
||||
dwc2_set_default_params(hsotg);
|
||||
dwc2_get_device_properties(hsotg);
|
||||
|
@ -323,6 +323,8 @@ static int dwc2_driver_remove(struct platform_device *dev)
|
||||
if (hsotg->gadget_enabled)
|
||||
dwc2_hsotg_remove(hsotg);
|
||||
|
||||
dwc2_drd_exit(hsotg);
|
||||
|
||||
if (hsotg->params.activate_stm_id_vb_detection)
|
||||
regulator_disable(hsotg->usb33d);
|
||||
|
||||
@ -542,10 +544,17 @@ static int dwc2_driver_probe(struct platform_device *dev)
|
||||
dwc2_writel(hsotg, ggpio, GGPIO);
|
||||
}
|
||||
|
||||
retval = dwc2_drd_init(hsotg);
|
||||
if (retval) {
|
||||
if (retval != -EPROBE_DEFER)
|
||||
dev_err(hsotg->dev, "failed to initialize dual-role\n");
|
||||
goto error_init;
|
||||
}
|
||||
|
||||
if (hsotg->dr_mode != USB_DR_MODE_HOST) {
|
||||
retval = dwc2_gadget_init(hsotg);
|
||||
if (retval)
|
||||
goto error_init;
|
||||
goto error_drd;
|
||||
hsotg->gadget_enabled = 1;
|
||||
}
|
||||
|
||||
@ -571,7 +580,7 @@ static int dwc2_driver_probe(struct platform_device *dev)
|
||||
if (retval) {
|
||||
if (hsotg->gadget_enabled)
|
||||
dwc2_hsotg_remove(hsotg);
|
||||
goto error_init;
|
||||
goto error_drd;
|
||||
}
|
||||
hsotg->hcd_enabled = 1;
|
||||
}
|
||||
@ -593,12 +602,19 @@ static int dwc2_driver_probe(struct platform_device *dev)
|
||||
if (retval) {
|
||||
hsotg->gadget.udc = NULL;
|
||||
dwc2_hsotg_remove(hsotg);
|
||||
goto error_init;
|
||||
goto error_debugfs;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_USB_DWC2_PERIPHERAL || CONFIG_USB_DWC2_DUAL_ROLE */
|
||||
return 0;
|
||||
|
||||
error_debugfs:
|
||||
dwc2_debugfs_exit(hsotg);
|
||||
if (hsotg->hcd_enabled)
|
||||
dwc2_hcd_remove(hsotg);
|
||||
error_drd:
|
||||
dwc2_drd_exit(hsotg);
|
||||
|
||||
error_init:
|
||||
if (hsotg->params.activate_stm_id_vb_detection)
|
||||
regulator_disable(hsotg->usb33d);
|
||||
@ -617,6 +633,8 @@ static int __maybe_unused dwc2_suspend(struct device *dev)
|
||||
if (is_device_mode)
|
||||
dwc2_hsotg_suspend(dwc2);
|
||||
|
||||
dwc2_drd_suspend(dwc2);
|
||||
|
||||
if (dwc2->params.activate_stm_id_vb_detection) {
|
||||
unsigned long flags;
|
||||
u32 ggpio, gotgctl;
|
||||
@ -697,6 +715,8 @@ static int __maybe_unused dwc2_resume(struct device *dev)
|
||||
/* Need to restore FORCEDEVMODE/FORCEHOSTMODE */
|
||||
dwc2_force_dr_mode(dwc2);
|
||||
|
||||
dwc2_drd_resume(dwc2);
|
||||
|
||||
if (dwc2_is_device_mode(dwc2))
|
||||
ret = dwc2_hsotg_resume(dwc2);
|
||||
|
||||
|
@ -119,9 +119,7 @@ static void __dwc3_set_mode(struct work_struct *work)
|
||||
struct dwc3 *dwc = work_to_dwc(work);
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
if (dwc->dr_mode != USB_DR_MODE_OTG)
|
||||
return;
|
||||
u32 reg;
|
||||
|
||||
pm_runtime_get_sync(dwc->dev);
|
||||
|
||||
@ -172,6 +170,11 @@ static void __dwc3_set_mode(struct work_struct *work)
|
||||
otg_set_vbus(dwc->usb2_phy->otg, true);
|
||||
phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST);
|
||||
phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST);
|
||||
if (dwc->dis_split_quirk) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUCTL3);
|
||||
reg |= DWC3_GUCTL3_SPLITDISABLE;
|
||||
dwc3_writel(dwc->regs, DWC3_GUCTL3, reg);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DWC3_GCTL_PRTCAP_DEVICE:
|
||||
@ -203,6 +206,9 @@ void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (dwc->dr_mode != USB_DR_MODE_OTG)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
dwc->desired_dr_role = mode;
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
@ -929,13 +935,6 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
||||
*/
|
||||
dwc3_writel(dwc->regs, DWC3_GUID, LINUX_VERSION_CODE);
|
||||
|
||||
/* Handle USB2.0-only core configuration */
|
||||
if (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) ==
|
||||
DWC3_GHWPARAMS3_SSPHY_IFC_DIS) {
|
||||
if (dwc->maximum_speed == USB_SPEED_SUPER)
|
||||
dwc->maximum_speed = USB_SPEED_HIGH;
|
||||
}
|
||||
|
||||
ret = dwc3_phy_setup(dwc);
|
||||
if (ret)
|
||||
goto err0;
|
||||
@ -1356,6 +1355,9 @@ static void dwc3_get_properties(struct dwc3 *dwc)
|
||||
dwc->dis_metastability_quirk = device_property_read_bool(dev,
|
||||
"snps,dis_metastability_quirk");
|
||||
|
||||
dwc->dis_split_quirk = device_property_read_bool(dev,
|
||||
"snps,dis-split-quirk");
|
||||
|
||||
dwc->lpm_nyet_threshold = lpm_nyet_threshold;
|
||||
dwc->tx_de_emphasis = tx_de_emphasis;
|
||||
|
||||
@ -1381,6 +1383,8 @@ bool dwc3_has_imod(struct dwc3 *dwc)
|
||||
static void dwc3_check_params(struct dwc3 *dwc)
|
||||
{
|
||||
struct device *dev = dwc->dev;
|
||||
unsigned int hwparam_gen =
|
||||
DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3);
|
||||
|
||||
/* Check for proper value of imod_interval */
|
||||
if (dwc->imod_interval && !dwc3_has_imod(dwc)) {
|
||||
@ -1404,25 +1408,40 @@ static void dwc3_check_params(struct dwc3 *dwc)
|
||||
case USB_SPEED_LOW:
|
||||
case USB_SPEED_FULL:
|
||||
case USB_SPEED_HIGH:
|
||||
break;
|
||||
case USB_SPEED_SUPER:
|
||||
if (hwparam_gen == DWC3_GHWPARAMS3_SSPHY_IFC_DIS)
|
||||
dev_warn(dev, "UDC doesn't support Gen 1\n");
|
||||
break;
|
||||
case USB_SPEED_SUPER_PLUS:
|
||||
if ((DWC3_IP_IS(DWC32) &&
|
||||
hwparam_gen == DWC3_GHWPARAMS3_SSPHY_IFC_DIS) ||
|
||||
(!DWC3_IP_IS(DWC32) &&
|
||||
hwparam_gen != DWC3_GHWPARAMS3_SSPHY_IFC_GEN2))
|
||||
dev_warn(dev, "UDC doesn't support SSP\n");
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "invalid maximum_speed parameter %d\n",
|
||||
dwc->maximum_speed);
|
||||
fallthrough;
|
||||
case USB_SPEED_UNKNOWN:
|
||||
/* default to superspeed */
|
||||
dwc->maximum_speed = USB_SPEED_SUPER;
|
||||
|
||||
/*
|
||||
* default to superspeed plus if we are capable.
|
||||
*/
|
||||
if ((DWC3_IP_IS(DWC31) || DWC3_IP_IS(DWC32)) &&
|
||||
(DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) ==
|
||||
DWC3_GHWPARAMS3_SSPHY_IFC_GEN2))
|
||||
switch (hwparam_gen) {
|
||||
case DWC3_GHWPARAMS3_SSPHY_IFC_GEN2:
|
||||
dwc->maximum_speed = USB_SPEED_SUPER_PLUS;
|
||||
|
||||
break;
|
||||
case DWC3_GHWPARAMS3_SSPHY_IFC_GEN1:
|
||||
if (DWC3_IP_IS(DWC32))
|
||||
dwc->maximum_speed = USB_SPEED_SUPER_PLUS;
|
||||
else
|
||||
dwc->maximum_speed = USB_SPEED_SUPER;
|
||||
break;
|
||||
case DWC3_GHWPARAMS3_SSPHY_IFC_DIS:
|
||||
dwc->maximum_speed = USB_SPEED_HIGH;
|
||||
break;
|
||||
default:
|
||||
dwc->maximum_speed = USB_SPEED_SUPER;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1554,6 +1573,17 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
|
||||
err5:
|
||||
dwc3_event_buffers_cleanup(dwc);
|
||||
|
||||
usb_phy_shutdown(dwc->usb2_phy);
|
||||
usb_phy_shutdown(dwc->usb3_phy);
|
||||
phy_exit(dwc->usb2_generic_phy);
|
||||
phy_exit(dwc->usb3_generic_phy);
|
||||
|
||||
usb_phy_set_suspend(dwc->usb2_phy, 1);
|
||||
usb_phy_set_suspend(dwc->usb3_phy, 1);
|
||||
phy_power_off(dwc->usb2_generic_phy);
|
||||
phy_power_off(dwc->usb3_generic_phy);
|
||||
|
||||
dwc3_ulpi_exit(dwc);
|
||||
|
||||
err4:
|
||||
@ -1589,9 +1619,9 @@ static int dwc3_remove(struct platform_device *pdev)
|
||||
dwc3_core_exit(dwc);
|
||||
dwc3_ulpi_exit(dwc);
|
||||
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
pm_runtime_allow(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
pm_runtime_set_suspended(&pdev->dev);
|
||||
|
||||
dwc3_free_event_buffers(dwc);
|
||||
dwc3_free_scratch_buffers(dwc);
|
||||
@ -1865,10 +1895,26 @@ static int dwc3_resume(struct device *dev)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dwc3_complete(struct device *dev)
|
||||
{
|
||||
struct dwc3 *dwc = dev_get_drvdata(dev);
|
||||
u32 reg;
|
||||
|
||||
if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST &&
|
||||
dwc->dis_split_quirk) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUCTL3);
|
||||
reg |= DWC3_GUCTL3_SPLITDISABLE;
|
||||
dwc3_writel(dwc->regs, DWC3_GUCTL3, reg);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define dwc3_complete NULL
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static const struct dev_pm_ops dwc3_dev_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(dwc3_suspend, dwc3_resume)
|
||||
.complete = dwc3_complete,
|
||||
SET_RUNTIME_PM_OPS(dwc3_runtime_suspend, dwc3_runtime_resume,
|
||||
dwc3_runtime_idle)
|
||||
};
|
||||
|
@ -138,6 +138,7 @@
|
||||
#define DWC3_GEVNTCOUNT(n) (0xc40c + ((n) * 0x10))
|
||||
|
||||
#define DWC3_GHWPARAMS8 0xc600
|
||||
#define DWC3_GUCTL3 0xc60c
|
||||
#define DWC3_GFLADJ 0xc630
|
||||
|
||||
/* Device Registers */
|
||||
@ -380,6 +381,9 @@
|
||||
/* Global User Control Register 2 */
|
||||
#define DWC3_GUCTL2_RST_ACTBITLATER BIT(14)
|
||||
|
||||
/* Global User Control Register 3 */
|
||||
#define DWC3_GUCTL3_SPLITDISABLE BIT(14)
|
||||
|
||||
/* Device Configuration Register */
|
||||
#define DWC3_DCFG_DEVADDR(addr) ((addr) << 3)
|
||||
#define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f)
|
||||
@ -634,7 +638,7 @@ struct dwc3_trb;
|
||||
struct dwc3_event_buffer {
|
||||
void *buf;
|
||||
void *cache;
|
||||
unsigned length;
|
||||
unsigned int length;
|
||||
unsigned int lpos;
|
||||
unsigned int count;
|
||||
unsigned int flags;
|
||||
@ -694,7 +698,7 @@ struct dwc3_ep {
|
||||
struct dwc3 *dwc;
|
||||
|
||||
u32 saved_state;
|
||||
unsigned flags;
|
||||
unsigned int flags;
|
||||
#define DWC3_EP_ENABLED BIT(0)
|
||||
#define DWC3_EP_STALL BIT(1)
|
||||
#define DWC3_EP_WEDGE BIT(2)
|
||||
@ -706,6 +710,7 @@ struct dwc3_ep {
|
||||
#define DWC3_EP_IGNORE_NEXT_NOSTREAM BIT(8)
|
||||
#define DWC3_EP_FORCE_RESTART_STREAM BIT(9)
|
||||
#define DWC3_EP_FIRST_STREAM_PRIMED BIT(10)
|
||||
#define DWC3_EP_PENDING_CLEAR_STALL BIT(11)
|
||||
|
||||
/* This last one is specific to EP0 */
|
||||
#define DWC3_EP0_DIR_IN BIT(31)
|
||||
@ -893,9 +898,9 @@ struct dwc3_request {
|
||||
struct scatterlist *sg;
|
||||
struct scatterlist *start_sg;
|
||||
|
||||
unsigned num_pending_sgs;
|
||||
unsigned int num_pending_sgs;
|
||||
unsigned int num_queued_sgs;
|
||||
unsigned remaining;
|
||||
unsigned int remaining;
|
||||
|
||||
unsigned int status;
|
||||
#define DWC3_REQUEST_STATUS_QUEUED 0
|
||||
@ -908,11 +913,11 @@ struct dwc3_request {
|
||||
struct dwc3_trb *trb;
|
||||
dma_addr_t trb_dma;
|
||||
|
||||
unsigned num_trbs;
|
||||
unsigned int num_trbs;
|
||||
|
||||
unsigned needs_extra_trb:1;
|
||||
unsigned direction:1;
|
||||
unsigned mapped:1;
|
||||
unsigned int needs_extra_trb:1;
|
||||
unsigned int direction:1;
|
||||
unsigned int mapped:1;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -1010,8 +1015,8 @@ struct dwc3_scratchpad_array {
|
||||
* @has_lpm_erratum: true when core was configured with LPM Erratum. Note that
|
||||
* there's now way for software to detect this in runtime.
|
||||
* @is_utmi_l1_suspend: the core asserts output signal
|
||||
* 0 - utmi_sleep_n
|
||||
* 1 - utmi_l1_suspend_n
|
||||
* 0 - utmi_sleep_n
|
||||
* 1 - utmi_l1_suspend_n
|
||||
* @is_fpga: true when we are using the FPGA board
|
||||
* @pending_events: true when we have pending IRQs to be handled
|
||||
* @pullups_connected: true when Run/Stop bit is set
|
||||
@ -1047,13 +1052,14 @@ struct dwc3_scratchpad_array {
|
||||
* 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
|
||||
* 1 - -3.5dB de-emphasis
|
||||
* 2 - No de-emphasis
|
||||
* 3 - Reserved
|
||||
* 0 - -6dB de-emphasis
|
||||
* 1 - -3.5dB de-emphasis
|
||||
* 2 - No de-emphasis
|
||||
* 3 - Reserved
|
||||
* @dis_metastability_quirk: set to disable metastability quirk.
|
||||
* @dis_split_quirk: set to disable split boundary.
|
||||
* @imod_interval: set the interrupt moderation interval in 250ns
|
||||
* increments or 0 to disable.
|
||||
* increments or 0 to disable.
|
||||
*/
|
||||
struct dwc3 {
|
||||
struct work_struct drd_work;
|
||||
@ -1079,7 +1085,7 @@ struct dwc3 {
|
||||
struct dwc3_event_buffer *ev_buf;
|
||||
struct dwc3_ep *eps[DWC3_ENDPOINTS_NUM];
|
||||
|
||||
struct usb_gadget gadget;
|
||||
struct usb_gadget *gadget;
|
||||
struct usb_gadget_driver *gadget_driver;
|
||||
|
||||
struct clk_bulk_data *clks;
|
||||
@ -1245,6 +1251,8 @@ struct dwc3 {
|
||||
|
||||
unsigned dis_metastability_quirk:1;
|
||||
|
||||
unsigned dis_split_quirk:1;
|
||||
|
||||
u16 imod_interval;
|
||||
};
|
||||
|
||||
@ -1456,9 +1464,10 @@ void dwc3_gadget_exit(struct dwc3 *dwc);
|
||||
int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode);
|
||||
int dwc3_gadget_get_link_state(struct dwc3 *dwc);
|
||||
int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state);
|
||||
int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
|
||||
int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
|
||||
struct dwc3_gadget_ep_cmd_params *params);
|
||||
int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param);
|
||||
int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd,
|
||||
u32 param);
|
||||
#else
|
||||
static inline int dwc3_gadget_init(struct dwc3 *dwc)
|
||||
{ return 0; }
|
||||
@ -1472,7 +1481,7 @@ static inline int dwc3_gadget_set_link_state(struct dwc3 *dwc,
|
||||
enum dwc3_link_state state)
|
||||
{ return 0; }
|
||||
|
||||
static inline int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
|
||||
static inline int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
|
||||
struct dwc3_gadget_ep_cmd_params *params)
|
||||
{ return 0; }
|
||||
static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
|
||||
|
@ -371,7 +371,9 @@ static inline const char *dwc3_gadget_event_type_string(u8 event)
|
||||
static inline const char *dwc3_decode_event(char *str, size_t size, u32 event,
|
||||
u32 ep0state)
|
||||
{
|
||||
const union dwc3_event evt = (union dwc3_event) event;
|
||||
union dwc3_event evt;
|
||||
|
||||
memcpy(&evt, &event, sizeof(event));
|
||||
|
||||
if (evt.type.is_devspec)
|
||||
return dwc3_gadget_event_string(str, size, &evt.devt);
|
||||
@ -411,8 +413,8 @@ static inline const char *dwc3_gadget_generic_cmd_status_string(int status)
|
||||
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
extern void dwc3_debugfs_init(struct dwc3 *);
|
||||
extern void dwc3_debugfs_exit(struct dwc3 *);
|
||||
extern void dwc3_debugfs_init(struct dwc3 *d);
|
||||
extern void dwc3_debugfs_exit(struct dwc3 *d);
|
||||
#else
|
||||
static inline void dwc3_debugfs_init(struct dwc3 *d)
|
||||
{ }
|
||||
|
@ -397,13 +397,13 @@ static int dwc3_mode_show(struct seq_file *s, void *unused)
|
||||
|
||||
switch (DWC3_GCTL_PRTCAP(reg)) {
|
||||
case DWC3_GCTL_PRTCAP_HOST:
|
||||
seq_printf(s, "host\n");
|
||||
seq_puts(s, "host\n");
|
||||
break;
|
||||
case DWC3_GCTL_PRTCAP_DEVICE:
|
||||
seq_printf(s, "device\n");
|
||||
seq_puts(s, "device\n");
|
||||
break;
|
||||
case DWC3_GCTL_PRTCAP_OTG:
|
||||
seq_printf(s, "otg\n");
|
||||
seq_puts(s, "otg\n");
|
||||
break;
|
||||
default:
|
||||
seq_printf(s, "UNKNOWN %08x\n", DWC3_GCTL_PRTCAP(reg));
|
||||
@ -428,6 +428,9 @@ static ssize_t dwc3_mode_write(struct file *file,
|
||||
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
|
||||
return -EFAULT;
|
||||
|
||||
if (dwc->dr_mode != USB_DR_MODE_OTG)
|
||||
return count;
|
||||
|
||||
if (!strncmp(buf, "host", 4))
|
||||
mode = DWC3_GCTL_PRTCAP_HOST;
|
||||
|
||||
@ -464,22 +467,22 @@ static int dwc3_testmode_show(struct seq_file *s, void *unused)
|
||||
|
||||
switch (reg) {
|
||||
case 0:
|
||||
seq_printf(s, "no test\n");
|
||||
seq_puts(s, "no test\n");
|
||||
break;
|
||||
case USB_TEST_J:
|
||||
seq_printf(s, "test_j\n");
|
||||
seq_puts(s, "test_j\n");
|
||||
break;
|
||||
case USB_TEST_K:
|
||||
seq_printf(s, "test_k\n");
|
||||
seq_puts(s, "test_k\n");
|
||||
break;
|
||||
case USB_TEST_SE0_NAK:
|
||||
seq_printf(s, "test_se0_nak\n");
|
||||
seq_puts(s, "test_se0_nak\n");
|
||||
break;
|
||||
case USB_TEST_PACKET:
|
||||
seq_printf(s, "test_packet\n");
|
||||
seq_puts(s, "test_packet\n");
|
||||
break;
|
||||
case USB_TEST_FORCE_ENABLE:
|
||||
seq_printf(s, "test_force_enable\n");
|
||||
seq_puts(s, "test_force_enable\n");
|
||||
break;
|
||||
default:
|
||||
seq_printf(s, "UNKNOWN %d\n", reg);
|
||||
@ -760,27 +763,26 @@ static int dwc3_transfer_type_show(struct seq_file *s, void *unused)
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
if (!(dep->flags & DWC3_EP_ENABLED) ||
|
||||
!dep->endpoint.desc) {
|
||||
seq_printf(s, "--\n");
|
||||
if (!(dep->flags & DWC3_EP_ENABLED) || !dep->endpoint.desc) {
|
||||
seq_puts(s, "--\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (usb_endpoint_type(dep->endpoint.desc)) {
|
||||
case USB_ENDPOINT_XFER_CONTROL:
|
||||
seq_printf(s, "control\n");
|
||||
seq_puts(s, "control\n");
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
seq_printf(s, "isochronous\n");
|
||||
seq_puts(s, "isochronous\n");
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
seq_printf(s, "bulk\n");
|
||||
seq_puts(s, "bulk\n");
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
seq_printf(s, "interrupt\n");
|
||||
seq_puts(s, "interrupt\n");
|
||||
break;
|
||||
default:
|
||||
seq_printf(s, "--\n");
|
||||
seq_puts(s, "--\n");
|
||||
}
|
||||
|
||||
out:
|
||||
@ -798,11 +800,11 @@ static int dwc3_trb_ring_show(struct seq_file *s, void *unused)
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
if (dep->number <= 1) {
|
||||
seq_printf(s, "--\n");
|
||||
seq_puts(s, "--\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
seq_printf(s, "buffer_addr,size,type,ioc,isp_imi,csp,chn,lst,hwo\n");
|
||||
seq_puts(s, "buffer_addr,size,type,ioc,isp_imi,csp,chn,lst,hwo\n");
|
||||
|
||||
for (i = 0; i < DWC3_TRB_NUM; i++) {
|
||||
struct dwc3_trb *trb = &dep->trb_pool[i];
|
||||
@ -884,7 +886,7 @@ static void dwc3_debugfs_create_endpoint_files(struct dwc3_ep *dep,
|
||||
const struct file_operations *fops = dwc3_ep_file_map[i].fops;
|
||||
const char *name = dwc3_ep_file_map[i].name;
|
||||
|
||||
debugfs_create_file(name, S_IRUGO, parent, dep, fops);
|
||||
debugfs_create_file(name, 0444, parent, dep, fops);
|
||||
}
|
||||
}
|
||||
|
||||
@ -929,21 +931,18 @@ void dwc3_debugfs_init(struct dwc3 *dwc)
|
||||
root = debugfs_create_dir(dev_name(dwc->dev), usb_debug_root);
|
||||
dwc->root = root;
|
||||
|
||||
debugfs_create_regset32("regdump", S_IRUGO, root, dwc->regset);
|
||||
debugfs_create_regset32("regdump", 0444, root, dwc->regset);
|
||||
debugfs_create_file("lsp_dump", 0644, root, dwc, &dwc3_lsp_fops);
|
||||
|
||||
debugfs_create_file("lsp_dump", S_IRUGO | S_IWUSR, root, dwc,
|
||||
&dwc3_lsp_fops);
|
||||
|
||||
if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)) {
|
||||
debugfs_create_file("mode", S_IRUGO | S_IWUSR, root, dwc,
|
||||
if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE))
|
||||
debugfs_create_file("mode", 0644, root, dwc,
|
||||
&dwc3_mode_fops);
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) ||
|
||||
IS_ENABLED(CONFIG_USB_DWC3_GADGET)) {
|
||||
debugfs_create_file("testmode", S_IRUGO | S_IWUSR, root, dwc,
|
||||
&dwc3_testmode_fops);
|
||||
debugfs_create_file("link_state", S_IRUGO | S_IWUSR, root, dwc,
|
||||
debugfs_create_file("testmode", 0644, root, dwc,
|
||||
&dwc3_testmode_fops);
|
||||
debugfs_create_file("link_state", 0644, root, dwc,
|
||||
&dwc3_link_state_fops);
|
||||
dwc3_debugfs_create_endpoint_dirs(dwc, root);
|
||||
}
|
||||
|
@ -116,23 +116,24 @@ static struct clk_bulk_data meson_a1_clocks[] = {
|
||||
{ .id = "xtal_usb_ctrl" },
|
||||
};
|
||||
|
||||
static const char *meson_gxm_phy_names[] = {
|
||||
static const char * const meson_gxm_phy_names[] = {
|
||||
"usb2-phy0", "usb2-phy1", "usb2-phy2",
|
||||
};
|
||||
|
||||
static const char *meson_g12a_phy_names[] = {
|
||||
static const char * const meson_g12a_phy_names[] = {
|
||||
"usb2-phy0", "usb2-phy1", "usb3-phy0",
|
||||
};
|
||||
|
||||
/*
|
||||
* Amlogic A1 has a single physical PHY, in slot 1, but still has the
|
||||
* two U2 PHY controls register blocks like G12A.
|
||||
* AXG has the similar scheme, thus needs the same tweak.
|
||||
* Handling the first PHY on slot 1 would need a large amount of code
|
||||
* changes, and the current management is generic enough to handle it
|
||||
* correctly when only the "usb2-phy1" phy is specified on-par with the
|
||||
* DT bindings.
|
||||
*/
|
||||
static const char *meson_a1_phy_names[] = {
|
||||
static const char * const meson_a1_phy_names[] = {
|
||||
"usb2-phy0", "usb2-phy1"
|
||||
};
|
||||
|
||||
@ -143,7 +144,7 @@ struct dwc3_meson_g12a_drvdata {
|
||||
bool otg_phy_host_port_disable;
|
||||
struct clk_bulk_data *clks;
|
||||
int num_clks;
|
||||
const char **phy_names;
|
||||
const char * const *phy_names;
|
||||
int num_phys;
|
||||
int (*setup_regmaps)(struct dwc3_meson_g12a *priv, void __iomem *base);
|
||||
int (*usb2_init_phy)(struct dwc3_meson_g12a *priv, int i,
|
||||
@ -215,6 +216,19 @@ static struct dwc3_meson_g12a_drvdata gxm_drvdata = {
|
||||
.usb_post_init = dwc3_meson_gxl_usb_post_init,
|
||||
};
|
||||
|
||||
static struct dwc3_meson_g12a_drvdata axg_drvdata = {
|
||||
.otg_switch_supported = true,
|
||||
.clks = meson_gxl_clocks,
|
||||
.num_clks = ARRAY_SIZE(meson_gxl_clocks),
|
||||
.phy_names = meson_a1_phy_names,
|
||||
.num_phys = ARRAY_SIZE(meson_a1_phy_names),
|
||||
.setup_regmaps = dwc3_meson_gxl_setup_regmaps,
|
||||
.usb2_init_phy = dwc3_meson_gxl_usb2_init_phy,
|
||||
.set_phy_mode = dwc3_meson_gxl_set_phy_mode,
|
||||
.usb_init = dwc3_meson_g12a_usb_init,
|
||||
.usb_post_init = dwc3_meson_gxl_usb_post_init,
|
||||
};
|
||||
|
||||
static struct dwc3_meson_g12a_drvdata g12a_drvdata = {
|
||||
.otg_switch_supported = true,
|
||||
.clks = meson_g12a_clocks,
|
||||
@ -520,11 +534,7 @@ static int dwc3_meson_g12a_role_set(struct usb_role_switch *sw,
|
||||
return 0;
|
||||
|
||||
if (priv->drvdata->otg_phy_host_port_disable)
|
||||
dev_warn_once(priv->dev, "Manual OTG switch is broken on this "\
|
||||
"SoC, when manual switching from "\
|
||||
"Host to device, DWC3 controller "\
|
||||
"will need to be resetted in order "\
|
||||
"to recover usage of the Host port");
|
||||
dev_warn_once(priv->dev, "Broken manual OTG switch\n");
|
||||
|
||||
return dwc3_meson_g12a_otg_mode_set(priv, mode);
|
||||
}
|
||||
@ -626,10 +636,7 @@ static int dwc3_meson_gxl_setup_regmaps(struct dwc3_meson_g12a *priv,
|
||||
/* GXL controls the PHY mode in the PHY registers unlike G12A */
|
||||
priv->usb_glue_regmap = devm_regmap_init_mmio(priv->dev, base,
|
||||
&phy_meson_g12a_usb_glue_regmap_conf);
|
||||
if (IS_ERR(priv->usb_glue_regmap))
|
||||
return PTR_ERR(priv->usb_glue_regmap);
|
||||
|
||||
return 0;
|
||||
return PTR_ERR_OR_ZERO(priv->usb_glue_regmap);
|
||||
}
|
||||
|
||||
static int dwc3_meson_g12a_setup_regmaps(struct dwc3_meson_g12a *priv,
|
||||
@ -906,8 +913,8 @@ static int __maybe_unused dwc3_meson_g12a_resume(struct device *dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (priv->vbus && priv->otg_phy_mode == PHY_MODE_USB_HOST) {
|
||||
ret = regulator_enable(priv->vbus);
|
||||
if (priv->vbus && priv->otg_phy_mode == PHY_MODE_USB_HOST) {
|
||||
ret = regulator_enable(priv->vbus);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
@ -930,6 +937,10 @@ static const struct of_device_id dwc3_meson_g12a_match[] = {
|
||||
.compatible = "amlogic,meson-gxm-usb-ctrl",
|
||||
.data = &gxm_drvdata,
|
||||
},
|
||||
{
|
||||
.compatible = "amlogic,meson-axg-usb-ctrl",
|
||||
.data = &axg_drvdata,
|
||||
},
|
||||
{
|
||||
.compatible = "amlogic,meson-g12a-usb-ctrl",
|
||||
.data = &g12a_drvdata,
|
||||
|
@ -176,6 +176,8 @@ static const struct of_device_id of_dwc3_simple_match[] = {
|
||||
{ .compatible = "cavium,octeon-7130-usb-uctl" },
|
||||
{ .compatible = "sprd,sc9860-dwc3" },
|
||||
{ .compatible = "allwinner,sun50i-h6-dwc3" },
|
||||
{ .compatible = "hisilicon,hi3670-dwc3" },
|
||||
{ .compatible = "intel,keembay-dwc3" },
|
||||
{ /* Sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_dwc3_simple_match);
|
||||
|
@ -147,7 +147,8 @@ static int dwc3_pci_quirks(struct dwc3_pci *dwc)
|
||||
|
||||
if (pdev->vendor == PCI_VENDOR_ID_INTEL) {
|
||||
if (pdev->device == PCI_DEVICE_ID_INTEL_BXT ||
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_BXT_M) {
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_BXT_M ||
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_EHLLP) {
|
||||
guid_parse(PCI_INTEL_BXT_DSM_GUID, &dwc->guid);
|
||||
dwc->has_dsm_for_pm = true;
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/extcon.h>
|
||||
#include <linux/interconnect.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/phy/phy.h>
|
||||
@ -43,6 +44,14 @@
|
||||
#define SDM845_QSCRATCH_SIZE 0x400
|
||||
#define SDM845_DWC3_CORE_SIZE 0xcd00
|
||||
|
||||
/* Interconnect path bandwidths in MBps */
|
||||
#define USB_MEMORY_AVG_HS_BW MBps_to_icc(240)
|
||||
#define USB_MEMORY_PEAK_HS_BW MBps_to_icc(700)
|
||||
#define USB_MEMORY_AVG_SS_BW MBps_to_icc(1000)
|
||||
#define USB_MEMORY_PEAK_SS_BW MBps_to_icc(2500)
|
||||
#define APPS_USB_AVG_BW 0
|
||||
#define APPS_USB_PEAK_BW MBps_to_icc(40)
|
||||
|
||||
struct dwc3_acpi_pdata {
|
||||
u32 qscratch_base_offset;
|
||||
u32 qscratch_base_size;
|
||||
@ -76,6 +85,8 @@ struct dwc3_qcom {
|
||||
enum usb_dr_mode mode;
|
||||
bool is_suspended;
|
||||
bool pm_suspended;
|
||||
struct icc_path *icc_path_ddr;
|
||||
struct icc_path *icc_path_apps;
|
||||
};
|
||||
|
||||
static inline void dwc3_qcom_setbits(void __iomem *base, u32 offset, u32 val)
|
||||
@ -190,6 +201,96 @@ static int dwc3_qcom_register_extcon(struct dwc3_qcom *qcom)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_qcom_interconnect_enable(struct dwc3_qcom *qcom)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = icc_enable(qcom->icc_path_ddr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = icc_enable(qcom->icc_path_apps);
|
||||
if (ret)
|
||||
icc_disable(qcom->icc_path_ddr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dwc3_qcom_interconnect_disable(struct dwc3_qcom *qcom)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = icc_disable(qcom->icc_path_ddr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = icc_disable(qcom->icc_path_apps);
|
||||
if (ret)
|
||||
icc_enable(qcom->icc_path_ddr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_qcom_interconnect_init() - Get interconnect path handles
|
||||
* and set bandwidhth.
|
||||
* @qcom: Pointer to the concerned usb core.
|
||||
*
|
||||
*/
|
||||
static int dwc3_qcom_interconnect_init(struct dwc3_qcom *qcom)
|
||||
{
|
||||
struct device *dev = qcom->dev;
|
||||
int ret;
|
||||
|
||||
qcom->icc_path_ddr = of_icc_get(dev, "usb-ddr");
|
||||
if (IS_ERR(qcom->icc_path_ddr)) {
|
||||
dev_err(dev, "failed to get usb-ddr path: %ld\n",
|
||||
PTR_ERR(qcom->icc_path_ddr));
|
||||
return PTR_ERR(qcom->icc_path_ddr);
|
||||
}
|
||||
|
||||
qcom->icc_path_apps = of_icc_get(dev, "apps-usb");
|
||||
if (IS_ERR(qcom->icc_path_apps)) {
|
||||
dev_err(dev, "failed to get apps-usb path: %ld\n",
|
||||
PTR_ERR(qcom->icc_path_apps));
|
||||
return PTR_ERR(qcom->icc_path_apps);
|
||||
}
|
||||
|
||||
if (usb_get_maximum_speed(&qcom->dwc3->dev) >= USB_SPEED_SUPER ||
|
||||
usb_get_maximum_speed(&qcom->dwc3->dev) == USB_SPEED_UNKNOWN)
|
||||
ret = icc_set_bw(qcom->icc_path_ddr,
|
||||
USB_MEMORY_AVG_SS_BW, USB_MEMORY_PEAK_SS_BW);
|
||||
else
|
||||
ret = icc_set_bw(qcom->icc_path_ddr,
|
||||
USB_MEMORY_AVG_HS_BW, USB_MEMORY_PEAK_HS_BW);
|
||||
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to set bandwidth for usb-ddr path: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = icc_set_bw(qcom->icc_path_apps,
|
||||
APPS_USB_AVG_BW, APPS_USB_PEAK_BW);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to set bandwidth for apps-usb path: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_qcom_interconnect_exit() - Release interconnect path handles
|
||||
* @qcom: Pointer to the concerned usb core.
|
||||
*
|
||||
* This function is used to release interconnect path handle.
|
||||
*/
|
||||
static void dwc3_qcom_interconnect_exit(struct dwc3_qcom *qcom)
|
||||
{
|
||||
icc_put(qcom->icc_path_ddr);
|
||||
icc_put(qcom->icc_path_apps);
|
||||
}
|
||||
|
||||
static void dwc3_qcom_disable_interrupts(struct dwc3_qcom *qcom)
|
||||
{
|
||||
if (qcom->hs_phy_irq) {
|
||||
@ -239,7 +340,7 @@ static void dwc3_qcom_enable_interrupts(struct dwc3_qcom *qcom)
|
||||
static int dwc3_qcom_suspend(struct dwc3_qcom *qcom)
|
||||
{
|
||||
u32 val;
|
||||
int i;
|
||||
int i, ret;
|
||||
|
||||
if (qcom->is_suspended)
|
||||
return 0;
|
||||
@ -251,6 +352,10 @@ static int dwc3_qcom_suspend(struct dwc3_qcom *qcom)
|
||||
for (i = qcom->num_clocks - 1; i >= 0; i--)
|
||||
clk_disable_unprepare(qcom->clks[i]);
|
||||
|
||||
ret = dwc3_qcom_interconnect_disable(qcom);
|
||||
if (ret)
|
||||
dev_warn(qcom->dev, "failed to disable interconnect: %d\n", ret);
|
||||
|
||||
qcom->is_suspended = true;
|
||||
dwc3_qcom_enable_interrupts(qcom);
|
||||
|
||||
@ -276,6 +381,10 @@ static int dwc3_qcom_resume(struct dwc3_qcom *qcom)
|
||||
}
|
||||
}
|
||||
|
||||
ret = dwc3_qcom_interconnect_enable(qcom);
|
||||
if (ret)
|
||||
dev_warn(qcom->dev, "failed to enable interconnect: %d\n", ret);
|
||||
|
||||
/* Clear existing events from PHY related to L2 in/out */
|
||||
dwc3_qcom_setbits(qcom->qscratch_base, PWR_EVNT_IRQ_STAT_REG,
|
||||
PWR_EVNT_LPM_IN_L2_MASK | PWR_EVNT_LPM_OUT_L2_MASK);
|
||||
@ -335,7 +444,9 @@ static int dwc3_qcom_setup_irq(struct platform_device *pdev)
|
||||
{
|
||||
struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
|
||||
const struct dwc3_acpi_pdata *pdata = qcom->acpi_pdata;
|
||||
int irq, ret;
|
||||
int irq;
|
||||
int ret;
|
||||
|
||||
irq = dwc3_qcom_get_irq(pdev, "hs_phy_irq",
|
||||
pdata ? pdata->hs_phy_irq_index : -1);
|
||||
if (irq > 0) {
|
||||
@ -454,7 +565,7 @@ static const struct property_entry dwc3_qcom_acpi_properties[] = {
|
||||
|
||||
static int dwc3_qcom_acpi_register_core(struct platform_device *pdev)
|
||||
{
|
||||
struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
|
||||
struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res, *child_res = NULL;
|
||||
int irq;
|
||||
@ -514,7 +625,7 @@ out:
|
||||
|
||||
static int dwc3_qcom_of_register_core(struct platform_device *pdev)
|
||||
{
|
||||
struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
|
||||
struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
|
||||
struct device_node *np = pdev->dev.of_node, *dwc3_np;
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret;
|
||||
@ -638,6 +749,10 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
|
||||
goto depopulate;
|
||||
}
|
||||
|
||||
ret = dwc3_qcom_interconnect_init(qcom);
|
||||
if (ret)
|
||||
goto depopulate;
|
||||
|
||||
qcom->mode = usb_get_dr_mode(&qcom->dwc3->dev);
|
||||
|
||||
/* enable vbus override for device mode */
|
||||
@ -647,7 +762,7 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
|
||||
/* register extcon to override sw_vbus on Vbus change later */
|
||||
ret = dwc3_qcom_register_extcon(qcom);
|
||||
if (ret)
|
||||
goto depopulate;
|
||||
goto interconnect_exit;
|
||||
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
qcom->is_suspended = false;
|
||||
@ -657,6 +772,8 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
|
||||
|
||||
return 0;
|
||||
|
||||
interconnect_exit:
|
||||
dwc3_qcom_interconnect_exit(qcom);
|
||||
depopulate:
|
||||
if (np)
|
||||
of_platform_depopulate(&pdev->dev);
|
||||
@ -687,6 +804,7 @@ static int dwc3_qcom_remove(struct platform_device *pdev)
|
||||
}
|
||||
qcom->num_clocks = 0;
|
||||
|
||||
dwc3_qcom_interconnect_exit(qcom);
|
||||
reset_control_assert(qcom->resets);
|
||||
|
||||
pm_runtime_allow(dev);
|
||||
|
@ -105,7 +105,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
|
||||
* IRQ we were waiting for is long gone.
|
||||
*/
|
||||
if (dep->flags & DWC3_EP_PENDING_REQUEST) {
|
||||
unsigned direction;
|
||||
unsigned int direction;
|
||||
|
||||
direction = !!(dep->flags & DWC3_EP0_DIR_IN);
|
||||
|
||||
@ -127,11 +127,11 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
|
||||
* handle it here.
|
||||
*/
|
||||
if (dwc->delayed_status) {
|
||||
unsigned direction;
|
||||
unsigned int direction;
|
||||
|
||||
direction = !dwc->ep0_expect_in;
|
||||
dwc->delayed_status = false;
|
||||
usb_gadget_set_state(&dwc->gadget, USB_STATE_CONFIGURED);
|
||||
usb_gadget_set_state(dwc->gadget, USB_STATE_CONFIGURED);
|
||||
|
||||
if (dwc->ep0state == EP0_STATUS_PHASE)
|
||||
__dwc3_ep0_do_control_status(dwc, dwc->eps[direction]);
|
||||
@ -172,7 +172,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
|
||||
* XferNotReady(STATUS).
|
||||
*/
|
||||
if (dwc->three_stage_setup) {
|
||||
unsigned direction;
|
||||
unsigned int direction;
|
||||
|
||||
direction = dwc->ep0_expect_in;
|
||||
dwc->ep0state = EP0_DATA_PHASE;
|
||||
@ -197,7 +197,7 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
if (!dep->endpoint.desc) {
|
||||
if (!dep->endpoint.desc || !dwc->pullups_connected) {
|
||||
dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n",
|
||||
dep->name);
|
||||
ret = -ESHUTDOWN;
|
||||
@ -325,7 +325,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
|
||||
/*
|
||||
* LTM will be set once we know how to set this in HW.
|
||||
*/
|
||||
usb_status |= dwc->gadget.is_selfpowered;
|
||||
usb_status |= dwc->gadget->is_selfpowered;
|
||||
|
||||
if ((dwc->speed == DWC3_DSTS_SUPERSPEED) ||
|
||||
(dwc->speed == DWC3_DSTS_SUPERSPEED_PLUS)) {
|
||||
@ -450,7 +450,7 @@ static int dwc3_ep0_handle_device(struct dwc3 *dwc,
|
||||
|
||||
wValue = le16_to_cpu(ctrl->wValue);
|
||||
wIndex = le16_to_cpu(ctrl->wIndex);
|
||||
state = dwc->gadget.state;
|
||||
state = dwc->gadget->state;
|
||||
|
||||
switch (wValue) {
|
||||
case USB_DEVICE_REMOTE_WAKEUP:
|
||||
@ -524,6 +524,11 @@ static int dwc3_ep0_handle_endpoint(struct dwc3 *dwc,
|
||||
ret = __dwc3_gadget_ep_set_halt(dep, set, true);
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
|
||||
/* ClearFeature(Halt) may need delayed status */
|
||||
if (!set && (dep->flags & DWC3_EP_END_TRANSFER_PENDING))
|
||||
return USB_GADGET_DELAYED_STATUS;
|
||||
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
@ -559,7 +564,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
|
||||
|
||||
static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
enum usb_device_state state = dwc->gadget.state;
|
||||
enum usb_device_state state = dwc->gadget->state;
|
||||
u32 addr;
|
||||
u32 reg;
|
||||
|
||||
@ -580,9 +585,9 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
||||
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
|
||||
|
||||
if (addr)
|
||||
usb_gadget_set_state(&dwc->gadget, USB_STATE_ADDRESS);
|
||||
usb_gadget_set_state(dwc->gadget, USB_STATE_ADDRESS);
|
||||
else
|
||||
usb_gadget_set_state(&dwc->gadget, USB_STATE_DEFAULT);
|
||||
usb_gadget_set_state(dwc->gadget, USB_STATE_DEFAULT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -592,14 +597,14 @@ static int dwc3_ep0_delegate_req(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
||||
int ret;
|
||||
|
||||
spin_unlock(&dwc->lock);
|
||||
ret = dwc->gadget_driver->setup(&dwc->gadget, ctrl);
|
||||
ret = dwc->gadget_driver->setup(dwc->gadget, ctrl);
|
||||
spin_lock(&dwc->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
enum usb_device_state state = dwc->gadget.state;
|
||||
enum usb_device_state state = dwc->gadget->state;
|
||||
u32 cfg;
|
||||
int ret;
|
||||
u32 reg;
|
||||
@ -622,7 +627,7 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
||||
* to change the state on the next usb_ep_queue()
|
||||
*/
|
||||
if (ret == 0)
|
||||
usb_gadget_set_state(&dwc->gadget,
|
||||
usb_gadget_set_state(dwc->gadget,
|
||||
USB_STATE_CONFIGURED);
|
||||
|
||||
/*
|
||||
@ -641,7 +646,7 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
||||
case USB_STATE_CONFIGURED:
|
||||
ret = dwc3_ep0_delegate_req(dwc, ctrl);
|
||||
if (!cfg && !ret)
|
||||
usb_gadget_set_state(&dwc->gadget,
|
||||
usb_gadget_set_state(dwc->gadget,
|
||||
USB_STATE_ADDRESS);
|
||||
break;
|
||||
default:
|
||||
@ -697,7 +702,7 @@ static void dwc3_ep0_set_sel_cmpl(struct usb_ep *ep, struct usb_request *req)
|
||||
static int dwc3_ep0_set_sel(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
struct dwc3_ep *dep;
|
||||
enum usb_device_state state = dwc->gadget.state;
|
||||
enum usb_device_state state = dwc->gadget->state;
|
||||
u16 wLength;
|
||||
|
||||
if (state == USB_STATE_DEFAULT)
|
||||
@ -741,7 +746,7 @@ static int dwc3_ep0_set_isoch_delay(struct dwc3 *dwc, struct usb_ctrlrequest *ct
|
||||
if (wIndex || wLength)
|
||||
return -EINVAL;
|
||||
|
||||
dwc->gadget.isoch_delay = wValue;
|
||||
dwc->gadget->isoch_delay = wValue;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -942,12 +947,16 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc,
|
||||
static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
|
||||
struct dwc3_ep *dep, struct dwc3_request *req)
|
||||
{
|
||||
unsigned int trb_length = 0;
|
||||
int ret;
|
||||
|
||||
req->direction = !!dep->number;
|
||||
|
||||
if (req->request.length == 0) {
|
||||
dwc3_ep0_prepare_one_trb(dep, dwc->ep0_trb_addr, 0,
|
||||
if (!req->direction)
|
||||
trb_length = dep->endpoint.maxpacket;
|
||||
|
||||
dwc3_ep0_prepare_one_trb(dep, dwc->bounce_addr, trb_length,
|
||||
DWC3_TRBCTL_CONTROL_DATA, false);
|
||||
ret = dwc3_ep0_start_trans(dep);
|
||||
} else if (!IS_ALIGNED(req->request.length, dep->endpoint.maxpacket)
|
||||
@ -994,9 +1003,12 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
|
||||
|
||||
req->trb = &dwc->ep0_trb[dep->trb_enqueue - 1];
|
||||
|
||||
if (!req->direction)
|
||||
trb_length = dep->endpoint.maxpacket;
|
||||
|
||||
/* Now prepare one extra TRB to align transfer size */
|
||||
dwc3_ep0_prepare_one_trb(dep, dwc->bounce_addr,
|
||||
0, DWC3_TRBCTL_CONTROL_DATA,
|
||||
trb_length, DWC3_TRBCTL_CONTROL_DATA,
|
||||
false);
|
||||
ret = dwc3_ep0_start_trans(dep);
|
||||
} else {
|
||||
@ -1042,6 +1054,17 @@ static void dwc3_ep0_do_control_status(struct dwc3 *dwc,
|
||||
__dwc3_ep0_do_control_status(dwc, dep);
|
||||
}
|
||||
|
||||
void dwc3_ep0_send_delayed_status(struct dwc3 *dwc)
|
||||
{
|
||||
unsigned int direction = !dwc->ep0_expect_in;
|
||||
|
||||
if (dwc->ep0state != EP0_STATUS_PHASE)
|
||||
return;
|
||||
|
||||
dwc->delayed_status = false;
|
||||
__dwc3_ep0_do_control_status(dwc, dwc->eps[direction]);
|
||||
}
|
||||
|
||||
static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
|
||||
{
|
||||
struct dwc3_gadget_ep_cmd_params params;
|
||||
@ -1102,7 +1125,7 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
|
||||
*/
|
||||
if (!list_empty(&dep->pending_list)) {
|
||||
dwc->delayed_status = false;
|
||||
usb_gadget_set_state(&dwc->gadget,
|
||||
usb_gadget_set_state(dwc->gadget,
|
||||
USB_STATE_CONFIGURED);
|
||||
dwc3_ep0_do_control_status(dwc, event);
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -17,7 +17,7 @@
|
||||
|
||||
struct dwc3;
|
||||
#define to_dwc3_ep(ep) (container_of(ep, struct dwc3_ep, endpoint))
|
||||
#define gadget_to_dwc(g) (container_of(g, struct dwc3, gadget))
|
||||
#define gadget_to_dwc(g) (dev_get_platdata(&g->dev))
|
||||
|
||||
/* DEPCFG parameter 1 */
|
||||
#define DWC3_DEPCFG_INT_NUM(n) (((n) & 0x1f) << 0)
|
||||
@ -113,6 +113,7 @@ int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value);
|
||||
int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
|
||||
gfp_t gfp_flags);
|
||||
int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol);
|
||||
void dwc3_ep0_send_delayed_status(struct dwc3 *dwc);
|
||||
|
||||
/**
|
||||
* dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW
|
||||
|
@ -104,8 +104,8 @@ DECLARE_EVENT_CLASS(dwc3_log_request,
|
||||
TP_STRUCT__entry(
|
||||
__string(name, req->dep->name)
|
||||
__field(struct dwc3_request *, req)
|
||||
__field(unsigned, actual)
|
||||
__field(unsigned, length)
|
||||
__field(unsigned int, actual)
|
||||
__field(unsigned int, length)
|
||||
__field(int, status)
|
||||
__field(int, zero)
|
||||
__field(int, short_not_ok)
|
||||
@ -246,6 +246,7 @@ DECLARE_EVENT_CLASS(dwc3_log_trb,
|
||||
__entry->dequeue, __entry->bph, __entry->bpl,
|
||||
({char *s;
|
||||
int pcm = ((__entry->size >> 24) & 3) + 1;
|
||||
|
||||
switch (__entry->type) {
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
@ -291,12 +292,12 @@ DECLARE_EVENT_CLASS(dwc3_log_ep,
|
||||
TP_ARGS(dep),
|
||||
TP_STRUCT__entry(
|
||||
__string(name, dep->name)
|
||||
__field(unsigned, maxpacket)
|
||||
__field(unsigned, maxpacket_limit)
|
||||
__field(unsigned, max_streams)
|
||||
__field(unsigned, maxburst)
|
||||
__field(unsigned, flags)
|
||||
__field(unsigned, direction)
|
||||
__field(unsigned int, maxpacket)
|
||||
__field(unsigned int, maxpacket_limit)
|
||||
__field(unsigned int, max_streams)
|
||||
__field(unsigned int, maxburst)
|
||||
__field(unsigned int, flags)
|
||||
__field(unsigned int, direction)
|
||||
__field(u8, trb_enqueue)
|
||||
__field(u8, trb_dequeue)
|
||||
),
|
||||
|
@ -19,7 +19,7 @@
|
||||
|
||||
static int dwc3_ulpi_busyloop(struct dwc3 *dwc)
|
||||
{
|
||||
unsigned count = 1000;
|
||||
unsigned int count = 1000;
|
||||
u32 reg;
|
||||
|
||||
while (count--) {
|
||||
|
@ -425,9 +425,11 @@ static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
||||
/* we know alt == 0, so this is an activation or a reset */
|
||||
|
||||
if (intf == acm->ctrl_id) {
|
||||
dev_vdbg(&cdev->gadget->dev,
|
||||
"reset acm control interface %d\n", intf);
|
||||
usb_ep_disable(acm->notify);
|
||||
if (acm->notify->enabled) {
|
||||
dev_vdbg(&cdev->gadget->dev,
|
||||
"reset acm control interface %d\n", intf);
|
||||
usb_ep_disable(acm->notify);
|
||||
}
|
||||
|
||||
if (!acm->notify->desc)
|
||||
if (config_ep_by_speed(cdev->gadget, f, acm->notify))
|
||||
|
@ -85,8 +85,10 @@ static inline struct f_ncm *func_to_ncm(struct usb_function *f)
|
||||
/* peak (theoretical) bulk transfer rate in bits-per-second */
|
||||
static inline unsigned ncm_bitrate(struct usb_gadget *g)
|
||||
{
|
||||
if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER)
|
||||
return 13 * 1024 * 8 * 1000 * 8;
|
||||
if (gadget_is_superspeed(g) && g->speed >= USB_SPEED_SUPER_PLUS)
|
||||
return 4250000000U;
|
||||
else if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER)
|
||||
return 3750000000U;
|
||||
else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
|
||||
return 13 * 512 * 8 * 1000 * 8;
|
||||
else
|
||||
@ -376,7 +378,7 @@ static struct usb_ss_ep_comp_descriptor ss_ncm_bulk_comp_desc = {
|
||||
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
|
||||
|
||||
/* the following 2 values can be tweaked if necessary */
|
||||
/* .bMaxBurst = 0, */
|
||||
.bMaxBurst = 15,
|
||||
/* .bmAttributes = 0, */
|
||||
};
|
||||
|
||||
@ -1189,7 +1191,6 @@ static int ncm_unwrap_ntb(struct gether *port,
|
||||
const struct ndp_parser_opts *opts = ncm->parser_opts;
|
||||
unsigned crc_len = ncm->is_crc ? sizeof(uint32_t) : 0;
|
||||
int dgram_counter;
|
||||
bool ndp_after_header;
|
||||
|
||||
/* dwSignature */
|
||||
if (get_unaligned_le32(tmp) != opts->nth_sign) {
|
||||
@ -1216,7 +1217,6 @@ static int ncm_unwrap_ntb(struct gether *port,
|
||||
}
|
||||
|
||||
ndp_index = get_ncm(&tmp, opts->ndp_index);
|
||||
ndp_after_header = false;
|
||||
|
||||
/* Run through all the NDP's in the NTB */
|
||||
do {
|
||||
@ -1232,8 +1232,6 @@ static int ncm_unwrap_ntb(struct gether *port,
|
||||
ndp_index);
|
||||
goto err;
|
||||
}
|
||||
if (ndp_index == opts->nth_size)
|
||||
ndp_after_header = true;
|
||||
|
||||
/*
|
||||
* walk through NDP
|
||||
@ -1312,37 +1310,13 @@ static int ncm_unwrap_ntb(struct gether *port,
|
||||
index2 = get_ncm(&tmp, opts->dgram_item_len);
|
||||
dg_len2 = get_ncm(&tmp, opts->dgram_item_len);
|
||||
|
||||
if (index2 == 0 || dg_len2 == 0)
|
||||
break;
|
||||
|
||||
/* wDatagramIndex[1] */
|
||||
if (ndp_after_header) {
|
||||
if (index2 < opts->nth_size + opts->ndp_size) {
|
||||
INFO(port->func.config->cdev,
|
||||
"Bad index: %#X\n", index2);
|
||||
goto err;
|
||||
}
|
||||
} else {
|
||||
if (index2 < opts->nth_size + opts->dpe_size) {
|
||||
INFO(port->func.config->cdev,
|
||||
"Bad index: %#X\n", index2);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
if (index2 > block_len - opts->dpe_size) {
|
||||
INFO(port->func.config->cdev,
|
||||
"Bad index: %#X\n", index2);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* wDatagramLength[1] */
|
||||
if ((dg_len2 < 14 + crc_len) ||
|
||||
(dg_len2 > frame_max)) {
|
||||
INFO(port->func.config->cdev,
|
||||
"Bad dgram length: %#X\n", dg_len);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy the data into a new skb.
|
||||
* This ensures the truesize is correct
|
||||
@ -1359,6 +1333,8 @@ static int ncm_unwrap_ntb(struct gether *port,
|
||||
ndp_len -= 2 * (opts->dgram_item_len * 2);
|
||||
|
||||
dgram_counter++;
|
||||
if (index2 == 0 || dg_len2 == 0)
|
||||
break;
|
||||
} while (ndp_len > 2 * (opts->dgram_item_len * 2));
|
||||
} while (ndp_index);
|
||||
|
||||
@ -1560,7 +1536,7 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
fs_ncm_notify_desc.bEndpointAddress;
|
||||
|
||||
status = usb_assign_descriptors(f, ncm_fs_function, ncm_hs_function,
|
||||
ncm_ss_function, NULL);
|
||||
ncm_ss_function, ncm_ss_function);
|
||||
if (status)
|
||||
goto fail;
|
||||
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/kref.h>
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
#include <linux/io.h>
|
||||
@ -64,7 +65,7 @@ struct printer_dev {
|
||||
struct usb_gadget *gadget;
|
||||
s8 interface;
|
||||
struct usb_ep *in_ep, *out_ep;
|
||||
|
||||
struct kref kref;
|
||||
struct list_head rx_reqs; /* List of free RX structs */
|
||||
struct list_head rx_reqs_active; /* List of Active RX xfers */
|
||||
struct list_head rx_buffers; /* List of completed xfers */
|
||||
@ -218,6 +219,13 @@ static inline struct usb_endpoint_descriptor *ep_desc(struct usb_gadget *gadget,
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static void printer_dev_free(struct kref *kref)
|
||||
{
|
||||
struct printer_dev *dev = container_of(kref, struct printer_dev, kref);
|
||||
|
||||
kfree(dev);
|
||||
}
|
||||
|
||||
static struct usb_request *
|
||||
printer_req_alloc(struct usb_ep *ep, unsigned len, gfp_t gfp_flags)
|
||||
{
|
||||
@ -353,6 +361,7 @@ printer_open(struct inode *inode, struct file *fd)
|
||||
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
|
||||
kref_get(&dev->kref);
|
||||
DBG(dev, "printer_open returned %x\n", ret);
|
||||
return ret;
|
||||
}
|
||||
@ -370,6 +379,7 @@ printer_close(struct inode *inode, struct file *fd)
|
||||
dev->printer_status &= ~PRINTER_SELECTED;
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
|
||||
kref_put(&dev->kref, printer_dev_free);
|
||||
DBG(dev, "printer_close\n");
|
||||
|
||||
return 0;
|
||||
@ -1386,7 +1396,8 @@ static void gprinter_free(struct usb_function *f)
|
||||
struct f_printer_opts *opts;
|
||||
|
||||
opts = container_of(f->fi, struct f_printer_opts, func_inst);
|
||||
kfree(dev);
|
||||
|
||||
kref_put(&dev->kref, printer_dev_free);
|
||||
mutex_lock(&opts->lock);
|
||||
--opts->refcnt;
|
||||
mutex_unlock(&opts->lock);
|
||||
@ -1455,6 +1466,7 @@ static struct usb_function *gprinter_alloc(struct usb_function_instance *fi)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
kref_init(&dev->kref);
|
||||
++opts->refcnt;
|
||||
dev->minor = opts->minor;
|
||||
dev->pnp_string = opts->pnp_string;
|
||||
|
@ -392,12 +392,12 @@ static void bot_set_alt(struct f_uas *fu)
|
||||
|
||||
fu->flags = USBG_IS_BOT;
|
||||
|
||||
config_ep_by_speed(gadget, f, fu->ep_in);
|
||||
config_ep_by_speed_and_alt(gadget, f, fu->ep_in, USB_G_ALT_INT_BBB);
|
||||
ret = usb_ep_enable(fu->ep_in);
|
||||
if (ret)
|
||||
goto err_b_in;
|
||||
|
||||
config_ep_by_speed(gadget, f, fu->ep_out);
|
||||
config_ep_by_speed_and_alt(gadget, f, fu->ep_out, USB_G_ALT_INT_BBB);
|
||||
ret = usb_ep_enable(fu->ep_out);
|
||||
if (ret)
|
||||
goto err_b_out;
|
||||
@ -852,21 +852,21 @@ static void uasp_set_alt(struct f_uas *fu)
|
||||
if (gadget->speed >= USB_SPEED_SUPER)
|
||||
fu->flags |= USBG_USE_STREAMS;
|
||||
|
||||
config_ep_by_speed(gadget, f, fu->ep_in);
|
||||
config_ep_by_speed_and_alt(gadget, f, fu->ep_in, USB_G_ALT_INT_UAS);
|
||||
ret = usb_ep_enable(fu->ep_in);
|
||||
if (ret)
|
||||
goto err_b_in;
|
||||
|
||||
config_ep_by_speed(gadget, f, fu->ep_out);
|
||||
config_ep_by_speed_and_alt(gadget, f, fu->ep_out, USB_G_ALT_INT_UAS);
|
||||
ret = usb_ep_enable(fu->ep_out);
|
||||
if (ret)
|
||||
goto err_b_out;
|
||||
|
||||
config_ep_by_speed(gadget, f, fu->ep_cmd);
|
||||
config_ep_by_speed_and_alt(gadget, f, fu->ep_cmd, USB_G_ALT_INT_UAS);
|
||||
ret = usb_ep_enable(fu->ep_cmd);
|
||||
if (ret)
|
||||
goto err_cmd;
|
||||
config_ep_by_speed(gadget, f, fu->ep_status);
|
||||
config_ep_by_speed_and_alt(gadget, f, fu->ep_status, USB_G_ALT_INT_UAS);
|
||||
ret = usb_ep_enable(fu->ep_status);
|
||||
if (ret)
|
||||
goto err_status;
|
||||
|
@ -740,20 +740,20 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
/* Initialise video. */
|
||||
ret = uvcg_video_init(&uvc->video, uvc);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
goto v4l2_error;
|
||||
|
||||
/* Register a V4L2 device. */
|
||||
ret = uvc_register_video(uvc);
|
||||
if (ret < 0) {
|
||||
uvcg_err(f, "failed to register video device\n");
|
||||
goto error;
|
||||
goto v4l2_error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
v4l2_error:
|
||||
v4l2_device_unregister(&uvc->v4l2_dev);
|
||||
|
||||
error:
|
||||
if (uvc->control_req)
|
||||
usb_ep_free_request(cdev->gadget->ep0, uvc->control_req);
|
||||
kfree(uvc->control_buf);
|
||||
|
@ -93,7 +93,7 @@ struct eth_dev {
|
||||
static inline int qlen(struct usb_gadget *gadget, unsigned qmult)
|
||||
{
|
||||
if (gadget_is_dualspeed(gadget) && (gadget->speed == USB_SPEED_HIGH ||
|
||||
gadget->speed == USB_SPEED_SUPER))
|
||||
gadget->speed >= USB_SPEED_SUPER))
|
||||
return qmult * DEFAULT_QLEN;
|
||||
else
|
||||
return DEFAULT_QLEN;
|
||||
|
@ -1391,6 +1391,7 @@ void gserial_disconnect(struct gserial *gser)
|
||||
if (port->port.tty)
|
||||
tty_hangup(port->port.tty);
|
||||
}
|
||||
port->suspended = false;
|
||||
spin_unlock_irqrestore(&port->port_lock, flags);
|
||||
|
||||
/* disable endpoints, aborting down any active I/O */
|
||||
|
@ -135,13 +135,9 @@ static irqreturn_t ast_vhub_irq(int irq, void *data)
|
||||
|
||||
/* Handle device interrupts */
|
||||
if (istat & vhub->port_irq_mask) {
|
||||
unsigned long bitmap = istat;
|
||||
int offset = VHUB_IRQ_DEV1_BIT;
|
||||
int size = VHUB_IRQ_DEV1_BIT + vhub->max_ports;
|
||||
|
||||
for_each_set_bit_from(offset, &bitmap, size) {
|
||||
i = offset - VHUB_IRQ_DEV1_BIT;
|
||||
ast_vhub_dev_irq(&vhub->ports[i].dev);
|
||||
for (i = 0; i < vhub->max_ports; i++) {
|
||||
if (istat & VHUB_DEV_IRQ(i))
|
||||
ast_vhub_dev_irq(&vhub->ports[i].dev);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,6 +67,9 @@
|
||||
#define VHUB_IRQ_HUB_EP0_SETUP (1 << 0)
|
||||
#define VHUB_IRQ_ACK_ALL 0x1ff
|
||||
|
||||
/* Downstream device IRQ mask. */
|
||||
#define VHUB_DEV_IRQ(n) (VHUB_IRQ_DEVICE1 << (n))
|
||||
|
||||
/* SW reset reg */
|
||||
#define VHUB_SW_RESET_EP_POOL (1 << 9)
|
||||
#define VHUB_SW_RESET_DMA_CONTROLLER (1 << 8)
|
||||
|
@ -1056,16 +1056,19 @@ found_ep:
|
||||
|
||||
switch (usb_endpoint_type(desc)) {
|
||||
case USB_ENDPOINT_XFER_CONTROL:
|
||||
ep->nr_banks = 1;
|
||||
break;
|
||||
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
ep->fifo_size = 1024;
|
||||
ep->nr_banks = 2;
|
||||
if (ep->udc->ep_prealloc)
|
||||
ep->nr_banks = 2;
|
||||
break;
|
||||
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
ep->fifo_size = 512;
|
||||
ep->nr_banks = 1;
|
||||
if (ep->udc->ep_prealloc)
|
||||
ep->nr_banks = 1;
|
||||
break;
|
||||
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
@ -1075,7 +1078,8 @@ found_ep:
|
||||
else
|
||||
ep->fifo_size =
|
||||
roundup_pow_of_two(le16_to_cpu(desc->wMaxPacketSize));
|
||||
ep->nr_banks = 1;
|
||||
if (ep->udc->ep_prealloc)
|
||||
ep->nr_banks = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1091,8 +1095,6 @@ found_ep:
|
||||
USBA_BF(EPT_SIZE, fls(ep->fifo_size - 1) - 3);
|
||||
|
||||
ep->ept_cfg |= USBA_BF(BK_NUMBER, ep->nr_banks);
|
||||
|
||||
ep->udc->configured_ep++;
|
||||
}
|
||||
|
||||
return _ep;
|
||||
@ -1786,7 +1788,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
|
||||
|
||||
if (status & USBA_END_OF_RESET) {
|
||||
struct usba_ep *ep0, *ep;
|
||||
int i, n;
|
||||
int i;
|
||||
|
||||
usba_writel(udc, INT_CLR,
|
||||
USBA_END_OF_RESET|USBA_END_OF_RESUME
|
||||
@ -1834,13 +1836,14 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
|
||||
"ODD: EP0 configuration is invalid!\n");
|
||||
|
||||
/* Preallocate other endpoints */
|
||||
n = fifo_mode ? udc->num_ep : udc->configured_ep;
|
||||
for (i = 1; i < n; i++) {
|
||||
for (i = 1; i < udc->num_ep; i++) {
|
||||
ep = &udc->usba_ep[i];
|
||||
usba_ep_writel(ep, CFG, ep->ept_cfg);
|
||||
if (!(usba_ep_readl(ep, CFG) & USBA_EPT_MAPPED))
|
||||
dev_err(&udc->pdev->dev,
|
||||
"ODD: EP%d configuration is invalid!\n", i);
|
||||
if (ep->ep.claimed) {
|
||||
usba_ep_writel(ep, CFG, ep->ept_cfg);
|
||||
if (!(usba_ep_readl(ep, CFG) & USBA_EPT_MAPPED))
|
||||
dev_err(&udc->pdev->dev,
|
||||
"ODD: EP%d configuration is invalid!\n", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2025,9 +2028,6 @@ static int atmel_usba_stop(struct usb_gadget *gadget)
|
||||
if (udc->vbus_pin)
|
||||
disable_irq(gpiod_to_irq(udc->vbus_pin));
|
||||
|
||||
if (fifo_mode == 0)
|
||||
udc->configured_ep = 1;
|
||||
|
||||
udc->suspended = false;
|
||||
usba_stop(udc);
|
||||
|
||||
@ -2090,33 +2090,51 @@ static const struct usba_udc_config udc_at91sam9rl_cfg = {
|
||||
.errata = &at91sam9rl_errata,
|
||||
.config = ep_config_sam9,
|
||||
.num_ep = ARRAY_SIZE(ep_config_sam9),
|
||||
.ep_prealloc = true,
|
||||
};
|
||||
|
||||
static const struct usba_udc_config udc_at91sam9g45_cfg = {
|
||||
.errata = &at91sam9g45_errata,
|
||||
.config = ep_config_sam9,
|
||||
.num_ep = ARRAY_SIZE(ep_config_sam9),
|
||||
.ep_prealloc = true,
|
||||
};
|
||||
|
||||
static const struct usba_udc_config udc_sama5d3_cfg = {
|
||||
.config = ep_config_sama5,
|
||||
.num_ep = ARRAY_SIZE(ep_config_sama5),
|
||||
.ep_prealloc = true,
|
||||
};
|
||||
|
||||
static const struct usba_udc_config udc_sam9x60_cfg = {
|
||||
.num_ep = ARRAY_SIZE(ep_config_sam9),
|
||||
.config = ep_config_sam9,
|
||||
.ep_prealloc = false,
|
||||
};
|
||||
|
||||
static const struct of_device_id atmel_udc_dt_ids[] = {
|
||||
{ .compatible = "atmel,at91sam9rl-udc", .data = &udc_at91sam9rl_cfg },
|
||||
{ .compatible = "atmel,at91sam9g45-udc", .data = &udc_at91sam9g45_cfg },
|
||||
{ .compatible = "atmel,sama5d3-udc", .data = &udc_sama5d3_cfg },
|
||||
{ .compatible = "microchip,sam9x60-udc", .data = &udc_sam9x60_cfg },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, atmel_udc_dt_ids);
|
||||
|
||||
static const struct of_device_id atmel_pmc_dt_ids[] = {
|
||||
{ .compatible = "atmel,at91sam9g45-pmc" },
|
||||
{ .compatible = "atmel,at91sam9rl-pmc" },
|
||||
{ .compatible = "atmel,at91sam9x5-pmc" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
|
||||
struct usba_udc *udc)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
const struct of_device_id *match;
|
||||
struct device_node *pp;
|
||||
int i, ret;
|
||||
struct usba_ep *eps, *ep;
|
||||
const struct usba_udc_config *udc_config;
|
||||
@ -2126,14 +2144,19 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
udc_config = match->data;
|
||||
udc->ep_prealloc = udc_config->ep_prealloc;
|
||||
udc->errata = udc_config->errata;
|
||||
udc->pmc = syscon_regmap_lookup_by_compatible("atmel,at91sam9g45-pmc");
|
||||
if (IS_ERR(udc->pmc))
|
||||
udc->pmc = syscon_regmap_lookup_by_compatible("atmel,at91sam9rl-pmc");
|
||||
if (IS_ERR(udc->pmc))
|
||||
udc->pmc = syscon_regmap_lookup_by_compatible("atmel,at91sam9x5-pmc");
|
||||
if (udc->errata && IS_ERR(udc->pmc))
|
||||
return ERR_CAST(udc->pmc);
|
||||
if (udc->errata) {
|
||||
pp = of_find_matching_node_and_match(NULL, atmel_pmc_dt_ids,
|
||||
NULL);
|
||||
if (!pp)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
udc->pmc = syscon_node_to_regmap(pp);
|
||||
of_node_put(pp);
|
||||
if (IS_ERR(udc->pmc))
|
||||
return ERR_CAST(udc->pmc);
|
||||
}
|
||||
|
||||
udc->num_ep = 0;
|
||||
|
||||
@ -2142,7 +2165,6 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
|
||||
|
||||
if (fifo_mode == 0) {
|
||||
udc->num_ep = udc_config->num_ep;
|
||||
udc->configured_ep = 1;
|
||||
} else {
|
||||
udc->num_ep = usba_config_fifo_table(udc);
|
||||
}
|
||||
|
@ -317,6 +317,7 @@ struct usba_udc_config {
|
||||
const struct usba_udc_errata *errata;
|
||||
const struct usba_ep_config *config;
|
||||
const int num_ep;
|
||||
const bool ep_prealloc;
|
||||
};
|
||||
|
||||
struct usba_udc {
|
||||
@ -336,7 +337,6 @@ struct usba_udc {
|
||||
int irq;
|
||||
struct gpio_desc *vbus_pin;
|
||||
int num_ep;
|
||||
int configured_ep;
|
||||
struct usba_fifo_cfg *fifo_cfg;
|
||||
struct clk *pclk;
|
||||
struct clk *hclk;
|
||||
@ -344,6 +344,7 @@ struct usba_udc {
|
||||
bool bias_pulse_needed;
|
||||
bool clocked;
|
||||
bool suspended;
|
||||
bool ep_prealloc;
|
||||
|
||||
u16 devstatus;
|
||||
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
@ -484,7 +484,7 @@ static void bdc_phy_exit(struct bdc *bdc)
|
||||
static int bdc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct bdc *bdc;
|
||||
int ret = -ENOMEM;
|
||||
int ret;
|
||||
int irq;
|
||||
u32 temp;
|
||||
struct device *dev = &pdev->dev;
|
||||
@ -510,10 +510,9 @@ static int bdc_probe(struct platform_device *pdev)
|
||||
bdc->clk = clk;
|
||||
|
||||
bdc->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(bdc->regs)) {
|
||||
dev_err(dev, "ioremap error\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (IS_ERR(bdc->regs))
|
||||
return PTR_ERR(bdc->regs);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
@ -715,6 +715,9 @@ int usb_gadget_disconnect(struct usb_gadget *gadget)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!gadget->connected)
|
||||
goto out;
|
||||
|
||||
if (gadget->deactivated) {
|
||||
/*
|
||||
* If gadget is deactivated we only save new state.
|
||||
@ -1164,21 +1167,18 @@ static int check_pending_gadget_drivers(struct usb_udc *udc)
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_add_gadget_udc_release - adds a new gadget to the udc class driver list
|
||||
* usb_initialize_gadget - initialize a gadget and its embedded struct device
|
||||
* @parent: the parent device to this udc. Usually the controller driver's
|
||||
* device.
|
||||
* @gadget: the gadget to be added to the list.
|
||||
* @gadget: the gadget to be initialized.
|
||||
* @release: a gadget release function.
|
||||
*
|
||||
* Returns zero on success, negative errno otherwise.
|
||||
* Calls the gadget release function in the latter case.
|
||||
*/
|
||||
int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
|
||||
void usb_initialize_gadget(struct device *parent, struct usb_gadget *gadget,
|
||||
void (*release)(struct device *dev))
|
||||
{
|
||||
struct usb_udc *udc;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
dev_set_name(&gadget->dev, "gadget");
|
||||
INIT_WORK(&gadget->work, usb_gadget_state_work);
|
||||
gadget->dev.parent = parent;
|
||||
@ -1189,17 +1189,32 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
|
||||
gadget->dev.release = usb_udc_nop_release;
|
||||
|
||||
device_initialize(&gadget->dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_initialize_gadget);
|
||||
|
||||
/**
|
||||
* usb_add_gadget - adds a new gadget to the udc class driver list
|
||||
* @gadget: the gadget to be added to the list.
|
||||
*
|
||||
* Returns zero on success, negative errno otherwise.
|
||||
* Does not do a final usb_put_gadget() if an error occurs.
|
||||
*/
|
||||
int usb_add_gadget(struct usb_gadget *gadget)
|
||||
{
|
||||
struct usb_udc *udc;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
udc = kzalloc(sizeof(*udc), GFP_KERNEL);
|
||||
if (!udc)
|
||||
goto err_put_gadget;
|
||||
goto error;
|
||||
|
||||
device_initialize(&udc->dev);
|
||||
udc->dev.release = usb_udc_release;
|
||||
udc->dev.class = udc_class;
|
||||
udc->dev.groups = usb_udc_attr_groups;
|
||||
udc->dev.parent = parent;
|
||||
ret = dev_set_name(&udc->dev, "%s", kobject_name(&parent->kobj));
|
||||
udc->dev.parent = gadget->dev.parent;
|
||||
ret = dev_set_name(&udc->dev, "%s",
|
||||
kobject_name(&gadget->dev.parent->kobj));
|
||||
if (ret)
|
||||
goto err_put_udc;
|
||||
|
||||
@ -1242,8 +1257,30 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
|
||||
err_put_udc:
|
||||
put_device(&udc->dev);
|
||||
|
||||
err_put_gadget:
|
||||
put_device(&gadget->dev);
|
||||
error:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_add_gadget);
|
||||
|
||||
/**
|
||||
* usb_add_gadget_udc_release - adds a new gadget to the udc class driver list
|
||||
* @parent: the parent device to this udc. Usually the controller driver's
|
||||
* device.
|
||||
* @gadget: the gadget to be added to the list.
|
||||
* @release: a gadget release function.
|
||||
*
|
||||
* Returns zero on success, negative errno otherwise.
|
||||
* Calls the gadget release function in the latter case.
|
||||
*/
|
||||
int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
|
||||
void (*release)(struct device *dev))
|
||||
{
|
||||
int ret;
|
||||
|
||||
usb_initialize_gadget(parent, gadget, release);
|
||||
ret = usb_add_gadget(gadget);
|
||||
if (ret)
|
||||
usb_put_gadget(gadget);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_add_gadget_udc_release);
|
||||
@ -1311,13 +1348,14 @@ static void usb_gadget_remove_driver(struct usb_udc *udc)
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_del_gadget_udc - deletes @udc from udc_list
|
||||
* usb_del_gadget - deletes @udc from udc_list
|
||||
* @gadget: the gadget to be removed.
|
||||
*
|
||||
* This, will call usb_gadget_unregister_driver() if
|
||||
* This will call usb_gadget_unregister_driver() if
|
||||
* the @udc is still busy.
|
||||
* It will not do a final usb_put_gadget().
|
||||
*/
|
||||
void usb_del_gadget_udc(struct usb_gadget *gadget)
|
||||
void usb_del_gadget(struct usb_gadget *gadget)
|
||||
{
|
||||
struct usb_udc *udc = gadget->udc;
|
||||
|
||||
@ -1340,8 +1378,20 @@ void usb_del_gadget_udc(struct usb_gadget *gadget)
|
||||
kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE);
|
||||
flush_work(&gadget->work);
|
||||
device_unregister(&udc->dev);
|
||||
device_unregister(&gadget->dev);
|
||||
memset(&gadget->dev, 0x00, sizeof(gadget->dev));
|
||||
device_del(&gadget->dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_del_gadget);
|
||||
|
||||
/**
|
||||
* usb_del_gadget_udc - deletes @udc from udc_list
|
||||
* @gadget: the gadget to be removed.
|
||||
*
|
||||
* Calls usb_del_gadget() and does a final usb_put_gadget().
|
||||
*/
|
||||
void usb_del_gadget_udc(struct usb_gadget *gadget)
|
||||
{
|
||||
usb_del_gadget(gadget);
|
||||
usb_put_gadget(gadget);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_del_gadget_udc);
|
||||
|
||||
|
@ -2061,7 +2061,7 @@ static int fsl_proc_read(struct seq_file *m, void *v)
|
||||
"Sleep Enable: %d SOF Received Enable: %d "
|
||||
"Reset Enable: %d\n"
|
||||
"System Error Enable: %d "
|
||||
"Port Change Dectected Enable: %d\n"
|
||||
"Port Change Detected Enable: %d\n"
|
||||
"USB Error Intr Enable: %d USB Intr Enable: %d\n\n",
|
||||
(tmp_reg & USB_INTR_DEVICE_SUSPEND) ? 1 : 0,
|
||||
(tmp_reg & USB_INTR_SOF_EN) ? 1 : 0,
|
||||
@ -2439,11 +2439,12 @@ static int fsl_udc_probe(struct platform_device *pdev)
|
||||
/* DEN is bidirectional ep number, max_ep doubles the number */
|
||||
udc_controller->max_ep = (dccparams & DCCPARAMS_DEN_MASK) * 2;
|
||||
|
||||
udc_controller->irq = platform_get_irq(pdev, 0);
|
||||
if (udc_controller->irq <= 0) {
|
||||
ret = udc_controller->irq ? : -ENODEV;
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret <= 0) {
|
||||
ret = ret ? : -ENODEV;
|
||||
goto err_iounmap;
|
||||
}
|
||||
udc_controller->irq = ret;
|
||||
|
||||
ret = request_irq(udc_controller->irq, fsl_udc_irq, IRQF_SHARED,
|
||||
driver_name, udc_controller);
|
||||
|
@ -495,7 +495,7 @@ static void proc_ep_show(struct seq_file *s, struct lpc32xx_ep *ep)
|
||||
}
|
||||
}
|
||||
|
||||
static int proc_udc_show(struct seq_file *s, void *unused)
|
||||
static int udc_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct lpc32xx_udc *udc = s->private;
|
||||
struct lpc32xx_ep *ep;
|
||||
@ -524,22 +524,11 @@ static int proc_udc_show(struct seq_file *s, void *unused)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int proc_udc_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, proc_udc_show, PDE_DATA(inode));
|
||||
}
|
||||
|
||||
static const struct file_operations proc_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = proc_udc_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
DEFINE_SHOW_ATTRIBUTE(udc);
|
||||
|
||||
static void create_debug_file(struct lpc32xx_udc *udc)
|
||||
{
|
||||
udc->pde = debugfs_create_file(debug_filename, 0, NULL, udc, &proc_ops);
|
||||
udc->pde = debugfs_create_file(debug_filename, 0, NULL, udc, &udc_fops);
|
||||
}
|
||||
|
||||
static void remove_debug_file(struct lpc32xx_udc *udc)
|
||||
|
@ -9,7 +9,6 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
@ -2196,7 +2195,8 @@ static int net2272_present(struct net2272 *dev)
|
||||
static void
|
||||
net2272_gadget_release(struct device *_dev)
|
||||
{
|
||||
struct net2272 *dev = dev_get_drvdata(_dev);
|
||||
struct net2272 *dev = container_of(_dev, struct net2272, gadget.dev);
|
||||
|
||||
kfree(dev);
|
||||
}
|
||||
|
||||
@ -2205,7 +2205,8 @@ net2272_gadget_release(struct device *_dev)
|
||||
static void
|
||||
net2272_remove(struct net2272 *dev)
|
||||
{
|
||||
usb_del_gadget_udc(&dev->gadget);
|
||||
if (dev->added)
|
||||
usb_del_gadget(&dev->gadget);
|
||||
free_irq(dev->irq, dev);
|
||||
iounmap(dev->base_addr);
|
||||
device_remove_file(dev->dev, &dev_attr_registers);
|
||||
@ -2235,6 +2236,7 @@ static struct net2272 *net2272_probe_init(struct device *dev, unsigned int irq)
|
||||
|
||||
/* the "gadget" abstracts/virtualizes the controller */
|
||||
ret->gadget.name = driver_name;
|
||||
usb_initialize_gadget(dev, &ret->gadget, net2272_gadget_release);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -2273,10 +2275,10 @@ net2272_probe_fin(struct net2272 *dev, unsigned int irqflags)
|
||||
if (ret)
|
||||
goto err_irq;
|
||||
|
||||
ret = usb_add_gadget_udc_release(dev->dev, &dev->gadget,
|
||||
net2272_gadget_release);
|
||||
ret = usb_add_gadget(&dev->gadget);
|
||||
if (ret)
|
||||
goto err_add_udc;
|
||||
dev->added = 1;
|
||||
|
||||
return 0;
|
||||
|
||||
@ -2451,7 +2453,7 @@ net2272_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
|
||||
if (pci_enable_device(pdev) < 0) {
|
||||
ret = -ENODEV;
|
||||
goto err_free;
|
||||
goto err_put;
|
||||
}
|
||||
|
||||
pci_set_master(pdev);
|
||||
@ -2474,8 +2476,8 @@ net2272_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
|
||||
err_pci:
|
||||
pci_disable_device(pdev);
|
||||
err_free:
|
||||
kfree(dev);
|
||||
err_put:
|
||||
usb_put_gadget(&dev->gadget);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -2536,7 +2538,7 @@ net2272_pci_remove(struct pci_dev *pdev)
|
||||
|
||||
pci_disable_device(pdev);
|
||||
|
||||
kfree(dev);
|
||||
usb_put_gadget(&dev->gadget);
|
||||
}
|
||||
|
||||
/* Table of matching PCI IDs */
|
||||
@ -2649,7 +2651,7 @@ net2272_plat_probe(struct platform_device *pdev)
|
||||
err_req:
|
||||
release_mem_region(base, len);
|
||||
err:
|
||||
kfree(dev);
|
||||
usb_put_gadget(&dev->gadget);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -2664,7 +2666,7 @@ net2272_plat_remove(struct platform_device *pdev)
|
||||
release_mem_region(pdev->resource[0].start,
|
||||
resource_size(&pdev->resource[0]));
|
||||
|
||||
kfree(dev);
|
||||
usb_put_gadget(&dev->gadget);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -441,6 +441,7 @@ struct net2272 {
|
||||
unsigned protocol_stall:1,
|
||||
softconnect:1,
|
||||
wakeup:1,
|
||||
added:1,
|
||||
dma_eot_polarity:1,
|
||||
dma_dack_polarity:1,
|
||||
dma_dreq_polarity:1,
|
||||
|
@ -3560,7 +3560,7 @@ static irqreturn_t net2280_irq(int irq, void *_dev)
|
||||
|
||||
static void gadget_release(struct device *_dev)
|
||||
{
|
||||
struct net2280 *dev = dev_get_drvdata(_dev);
|
||||
struct net2280 *dev = container_of(_dev, struct net2280, gadget.dev);
|
||||
|
||||
kfree(dev);
|
||||
}
|
||||
@ -3571,7 +3571,8 @@ static void net2280_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct net2280 *dev = pci_get_drvdata(pdev);
|
||||
|
||||
usb_del_gadget_udc(&dev->gadget);
|
||||
if (dev->added)
|
||||
usb_del_gadget(&dev->gadget);
|
||||
|
||||
BUG_ON(dev->driver);
|
||||
|
||||
@ -3602,6 +3603,7 @@ static void net2280_remove(struct pci_dev *pdev)
|
||||
device_remove_file(&pdev->dev, &dev_attr_registers);
|
||||
|
||||
ep_info(dev, "unbind\n");
|
||||
usb_put_gadget(&dev->gadget);
|
||||
}
|
||||
|
||||
/* wrap this driver around the specified device, but
|
||||
@ -3623,6 +3625,7 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
}
|
||||
|
||||
pci_set_drvdata(pdev, dev);
|
||||
usb_initialize_gadget(&pdev->dev, &dev->gadget, gadget_release);
|
||||
spin_lock_init(&dev->lock);
|
||||
dev->quirks = id->driver_data;
|
||||
dev->pdev = pdev;
|
||||
@ -3773,10 +3776,10 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
if (retval)
|
||||
goto done;
|
||||
|
||||
retval = usb_add_gadget_udc_release(&pdev->dev, &dev->gadget,
|
||||
gadget_release);
|
||||
retval = usb_add_gadget(&dev->gadget);
|
||||
if (retval)
|
||||
goto done;
|
||||
dev->added = 1;
|
||||
return 0;
|
||||
|
||||
done:
|
||||
|
@ -156,6 +156,7 @@ struct net2280 {
|
||||
softconnect : 1,
|
||||
got_irq : 1,
|
||||
region:1,
|
||||
added:1,
|
||||
u1_enable:1,
|
||||
u2_enable:1,
|
||||
ltm_enable:1,
|
||||
|
@ -12,12 +12,9 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/irq.h>
|
||||
|
||||
/* GPIO port for VBUS detecting */
|
||||
static int vbus_gpio_port = -1; /* GPIO port number (-1:Not used) */
|
||||
|
||||
#define PCH_VBUS_PERIOD 3000 /* VBUS polling period (msec) */
|
||||
#define PCH_VBUS_INTERVAL 10 /* VBUS polling interval (msec) */
|
||||
|
||||
@ -301,13 +298,13 @@ struct pch_udc_ep {
|
||||
/**
|
||||
* struct pch_vbus_gpio_data - Structure holding GPIO informaton
|
||||
* for detecting VBUS
|
||||
* @port: gpio port number
|
||||
* @port: gpio descriptor for the VBUS GPIO
|
||||
* @intr: gpio interrupt number
|
||||
* @irq_work_fall: Structure for WorkQueue
|
||||
* @irq_work_rise: Structure for WorkQueue
|
||||
*/
|
||||
struct pch_vbus_gpio_data {
|
||||
int port;
|
||||
struct gpio_desc *port;
|
||||
int intr;
|
||||
struct work_struct irq_work_fall;
|
||||
struct work_struct irq_work_rise;
|
||||
@ -1254,7 +1251,7 @@ static int pch_vbus_gpio_get_value(struct pch_udc_dev *dev)
|
||||
int vbus = 0;
|
||||
|
||||
if (dev->vbus_gpio.port)
|
||||
vbus = gpio_get_value(dev->vbus_gpio.port) ? 1 : 0;
|
||||
vbus = gpiod_get_value(dev->vbus_gpio.port) ? 1 : 0;
|
||||
else
|
||||
vbus = -1;
|
||||
|
||||
@ -1356,42 +1353,30 @@ static irqreturn_t pch_vbus_gpio_irq(int irq, void *data)
|
||||
/**
|
||||
* pch_vbus_gpio_init() - This API initializes GPIO port detecting VBUS.
|
||||
* @dev: Reference to the driver structure
|
||||
* @vbus_gpio_port: Number of GPIO port to detect gpio
|
||||
*
|
||||
* Return codes:
|
||||
* 0: Success
|
||||
* -EINVAL: GPIO port is invalid or can't be initialized.
|
||||
*/
|
||||
static int pch_vbus_gpio_init(struct pch_udc_dev *dev, int vbus_gpio_port)
|
||||
static int pch_vbus_gpio_init(struct pch_udc_dev *dev)
|
||||
{
|
||||
int err;
|
||||
int irq_num = 0;
|
||||
struct gpio_desc *gpiod;
|
||||
|
||||
dev->vbus_gpio.port = 0;
|
||||
dev->vbus_gpio.port = NULL;
|
||||
dev->vbus_gpio.intr = 0;
|
||||
|
||||
if (vbus_gpio_port <= -1)
|
||||
return -EINVAL;
|
||||
/* Retrieve the GPIO line from the USB gadget device */
|
||||
gpiod = devm_gpiod_get(dev->gadget.dev.parent, NULL, GPIOD_IN);
|
||||
if (IS_ERR(gpiod))
|
||||
return PTR_ERR(gpiod);
|
||||
gpiod_set_consumer_name(gpiod, "pch_vbus");
|
||||
|
||||
err = gpio_is_valid(vbus_gpio_port);
|
||||
if (!err) {
|
||||
pr_err("%s: gpio port %d is invalid\n",
|
||||
__func__, vbus_gpio_port);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = gpio_request(vbus_gpio_port, "pch_vbus");
|
||||
if (err) {
|
||||
pr_err("%s: can't request gpio port %d, err: %d\n",
|
||||
__func__, vbus_gpio_port, err);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev->vbus_gpio.port = vbus_gpio_port;
|
||||
gpio_direction_input(vbus_gpio_port);
|
||||
dev->vbus_gpio.port = gpiod;
|
||||
INIT_WORK(&dev->vbus_gpio.irq_work_fall, pch_vbus_gpio_work_fall);
|
||||
|
||||
irq_num = gpio_to_irq(vbus_gpio_port);
|
||||
irq_num = gpiod_to_irq(gpiod);
|
||||
if (irq_num > 0) {
|
||||
irq_set_irq_type(irq_num, IRQ_TYPE_EDGE_BOTH);
|
||||
err = request_irq(irq_num, pch_vbus_gpio_irq, 0,
|
||||
@ -1417,9 +1402,6 @@ static void pch_vbus_gpio_free(struct pch_udc_dev *dev)
|
||||
{
|
||||
if (dev->vbus_gpio.intr)
|
||||
free_irq(dev->vbus_gpio.intr, dev);
|
||||
|
||||
if (dev->vbus_gpio.port)
|
||||
gpio_free(dev->vbus_gpio.port);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2894,7 +2876,7 @@ static int pch_udc_pcd_init(struct pch_udc_dev *dev)
|
||||
{
|
||||
pch_udc_init(dev);
|
||||
pch_udc_pcd_reinit(dev);
|
||||
pch_vbus_gpio_init(dev, vbus_gpio_port);
|
||||
pch_vbus_gpio_init(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -3096,6 +3078,13 @@ static int pch_udc_probe(struct pci_dev *pdev,
|
||||
|
||||
dev->base_addr = pcim_iomap_table(pdev)[bar];
|
||||
|
||||
/*
|
||||
* FIXME: add a GPIO descriptor table to pdev.dev using
|
||||
* gpiod_add_descriptor_table() from <linux/gpio/machine.h> based on
|
||||
* the PCI subsystem ID. The system-dependent GPIO is necessary for
|
||||
* VBUS operation.
|
||||
*/
|
||||
|
||||
/* initialize the hardware */
|
||||
if (pch_udc_pcd_init(dev))
|
||||
return -ENODEV;
|
||||
|
@ -1270,7 +1270,6 @@ static int s3c2410_udc_queue(struct usb_ep *_ep, struct usb_request *_req,
|
||||
static int s3c2410_udc_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
||||
{
|
||||
struct s3c2410_ep *ep = to_s3c2410_ep(_ep);
|
||||
struct s3c2410_udc *udc;
|
||||
int retval = -EINVAL;
|
||||
unsigned long flags;
|
||||
struct s3c2410_request *req = NULL;
|
||||
@ -1283,8 +1282,6 @@ static int s3c2410_udc_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
||||
if (!_ep || !_req)
|
||||
return retval;
|
||||
|
||||
udc = to_s3c2410_udc(ep->gadget);
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
list_for_each_entry(req, &ep->queue, queue) {
|
||||
|
@ -705,11 +705,11 @@ static void tegra_xudc_device_mode_on(struct tegra_xudc *xudc)
|
||||
|
||||
err = phy_power_on(xudc->curr_utmi_phy);
|
||||
if (err < 0)
|
||||
dev_err(xudc->dev, "utmi power on failed %d\n", err);
|
||||
dev_err(xudc->dev, "UTMI power on failed: %d\n", err);
|
||||
|
||||
err = phy_power_on(xudc->curr_usb3_phy);
|
||||
if (err < 0)
|
||||
dev_err(xudc->dev, "usb3 phy power on failed %d\n", err);
|
||||
dev_err(xudc->dev, "USB3 PHY power on failed: %d\n", err);
|
||||
|
||||
dev_dbg(xudc->dev, "device mode on\n");
|
||||
|
||||
@ -759,11 +759,11 @@ static void tegra_xudc_device_mode_off(struct tegra_xudc *xudc)
|
||||
|
||||
err = phy_power_off(xudc->curr_utmi_phy);
|
||||
if (err < 0)
|
||||
dev_err(xudc->dev, "utmi_phy power off failed %d\n", err);
|
||||
dev_err(xudc->dev, "UTMI PHY power off failed: %d\n", err);
|
||||
|
||||
err = phy_power_off(xudc->curr_usb3_phy);
|
||||
if (err < 0)
|
||||
dev_err(xudc->dev, "usb3_phy power off failed %d\n", err);
|
||||
dev_err(xudc->dev, "USB3 PHY power off failed: %d\n", err);
|
||||
|
||||
pm_runtime_put(xudc->dev);
|
||||
}
|
||||
@ -1539,7 +1539,7 @@ static int __tegra_xudc_ep_set_halt(struct tegra_xudc_ep *ep, bool halt)
|
||||
return -EINVAL;
|
||||
|
||||
if (usb_endpoint_xfer_isoc(ep->desc)) {
|
||||
dev_err(xudc->dev, "can't halt isoc EP\n");
|
||||
dev_err(xudc->dev, "can't halt isochronous EP\n");
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
@ -1788,7 +1788,7 @@ static int __tegra_xudc_ep_enable(struct tegra_xudc_ep *ep,
|
||||
|
||||
if (usb_endpoint_xfer_isoc(desc)) {
|
||||
if (xudc->nr_isoch_eps > XUDC_MAX_ISOCH_EPS) {
|
||||
dev_err(xudc->dev, "too many isoch endpoints\n");
|
||||
dev_err(xudc->dev, "too many isochronous endpoints\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
xudc->nr_isoch_eps++;
|
||||
@ -3509,7 +3509,7 @@ static int tegra_xudc_phy_get(struct tegra_xudc *xudc)
|
||||
if (IS_ERR(xudc->utmi_phy[i])) {
|
||||
err = PTR_ERR(xudc->utmi_phy[i]);
|
||||
if (err != -EPROBE_DEFER)
|
||||
dev_err(xudc->dev, "failed to get usb2-%d phy: %d\n",
|
||||
dev_err(xudc->dev, "failed to get usb2-%d PHY: %d\n",
|
||||
i, err);
|
||||
|
||||
goto clean_up;
|
||||
@ -3539,12 +3539,12 @@ static int tegra_xudc_phy_get(struct tegra_xudc *xudc)
|
||||
if (IS_ERR(xudc->usb3_phy[i])) {
|
||||
err = PTR_ERR(xudc->usb3_phy[i]);
|
||||
if (err != -EPROBE_DEFER)
|
||||
dev_err(xudc->dev, "failed to get usb3-%d phy: %d\n",
|
||||
dev_err(xudc->dev, "failed to get usb3-%d PHY: %d\n",
|
||||
usb3, err);
|
||||
|
||||
goto clean_up;
|
||||
} else if (xudc->usb3_phy[i])
|
||||
dev_dbg(xudc->dev, "usb3_phy-%d registered", usb3);
|
||||
dev_dbg(xudc->dev, "usb3-%d PHY registered", usb3);
|
||||
}
|
||||
|
||||
return err;
|
||||
@ -3577,13 +3577,13 @@ static int tegra_xudc_phy_init(struct tegra_xudc *xudc)
|
||||
for (i = 0; i < xudc->soc->num_phys; i++) {
|
||||
err = phy_init(xudc->utmi_phy[i]);
|
||||
if (err < 0) {
|
||||
dev_err(xudc->dev, "utmi phy init failed: %d\n", err);
|
||||
dev_err(xudc->dev, "UTMI PHY #%u initialization failed: %d\n", i, err);
|
||||
goto exit_phy;
|
||||
}
|
||||
|
||||
err = phy_init(xudc->usb3_phy[i]);
|
||||
if (err < 0) {
|
||||
dev_err(xudc->dev, "usb3 phy init failed: %d\n", err);
|
||||
dev_err(xudc->dev, "USB3 PHY #%u initialization failed: %d\n", i, err);
|
||||
goto exit_phy;
|
||||
}
|
||||
}
|
||||
@ -3692,34 +3692,33 @@ static int tegra_xudc_powerdomain_init(struct tegra_xudc *xudc)
|
||||
struct device *dev = xudc->dev;
|
||||
int err;
|
||||
|
||||
xudc->genpd_dev_device = dev_pm_domain_attach_by_name(dev,
|
||||
"dev");
|
||||
xudc->genpd_dev_device = dev_pm_domain_attach_by_name(dev, "dev");
|
||||
if (IS_ERR(xudc->genpd_dev_device)) {
|
||||
err = PTR_ERR(xudc->genpd_dev_device);
|
||||
dev_err(dev, "failed to get dev pm-domain: %d\n", err);
|
||||
dev_err(dev, "failed to get device power domain: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
xudc->genpd_dev_ss = dev_pm_domain_attach_by_name(dev, "ss");
|
||||
if (IS_ERR(xudc->genpd_dev_ss)) {
|
||||
err = PTR_ERR(xudc->genpd_dev_ss);
|
||||
dev_err(dev, "failed to get superspeed pm-domain: %d\n", err);
|
||||
dev_err(dev, "failed to get SuperSpeed power domain: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
xudc->genpd_dl_device = device_link_add(dev, xudc->genpd_dev_device,
|
||||
DL_FLAG_PM_RUNTIME |
|
||||
DL_FLAG_STATELESS);
|
||||
DL_FLAG_PM_RUNTIME |
|
||||
DL_FLAG_STATELESS);
|
||||
if (!xudc->genpd_dl_device) {
|
||||
dev_err(dev, "adding usb device device link failed!\n");
|
||||
dev_err(dev, "failed to add USB device link\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
xudc->genpd_dl_ss = device_link_add(dev, xudc->genpd_dev_ss,
|
||||
DL_FLAG_PM_RUNTIME |
|
||||
DL_FLAG_STATELESS);
|
||||
DL_FLAG_PM_RUNTIME |
|
||||
DL_FLAG_STATELESS);
|
||||
if (!xudc->genpd_dl_ss) {
|
||||
dev_err(dev, "adding superspeed device link failed!\n");
|
||||
dev_err(dev, "failed to add SuperSpeed device link\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
@ -3733,7 +3732,7 @@ static int tegra_xudc_probe(struct platform_device *pdev)
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
xudc = devm_kzalloc(&pdev->dev, sizeof(*xudc), GFP_ATOMIC);
|
||||
xudc = devm_kzalloc(&pdev->dev, sizeof(*xudc), GFP_KERNEL);
|
||||
if (!xudc)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -3772,18 +3771,19 @@ static int tegra_xudc_probe(struct platform_device *pdev)
|
||||
return err;
|
||||
}
|
||||
|
||||
xudc->clks = devm_kcalloc(&pdev->dev, xudc->soc->num_clks,
|
||||
sizeof(*xudc->clks), GFP_KERNEL);
|
||||
xudc->clks = devm_kcalloc(&pdev->dev, xudc->soc->num_clks, sizeof(*xudc->clks),
|
||||
GFP_KERNEL);
|
||||
if (!xudc->clks)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < xudc->soc->num_clks; i++)
|
||||
xudc->clks[i].id = xudc->soc->clock_names[i];
|
||||
|
||||
err = devm_clk_bulk_get(&pdev->dev, xudc->soc->num_clks,
|
||||
xudc->clks);
|
||||
err = devm_clk_bulk_get(&pdev->dev, xudc->soc->num_clks, xudc->clks);
|
||||
if (err) {
|
||||
dev_err(xudc->dev, "failed to request clks %d\n", err);
|
||||
if (err != -EPROBE_DEFER)
|
||||
dev_err(xudc->dev, "failed to request clocks: %d\n", err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -3798,7 +3798,9 @@ static int tegra_xudc_probe(struct platform_device *pdev)
|
||||
err = devm_regulator_bulk_get(&pdev->dev, xudc->soc->num_supplies,
|
||||
xudc->supplies);
|
||||
if (err) {
|
||||
dev_err(xudc->dev, "failed to request regulators %d\n", err);
|
||||
if (err != -EPROBE_DEFER)
|
||||
dev_err(xudc->dev, "failed to request regulators: %d\n", err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -3808,7 +3810,7 @@ static int tegra_xudc_probe(struct platform_device *pdev)
|
||||
|
||||
err = regulator_bulk_enable(xudc->soc->num_supplies, xudc->supplies);
|
||||
if (err) {
|
||||
dev_err(xudc->dev, "failed to enable regulators %d\n", err);
|
||||
dev_err(xudc->dev, "failed to enable regulators: %d\n", err);
|
||||
goto put_padctl;
|
||||
}
|
||||
|
||||
|
@ -436,6 +436,7 @@ struct usb_gadget {
|
||||
};
|
||||
#define work_to_gadget(w) (container_of((w), struct usb_gadget, work))
|
||||
|
||||
/* Interface to the device model */
|
||||
static inline void set_gadget_data(struct usb_gadget *gadget, void *data)
|
||||
{ dev_set_drvdata(&gadget->dev, data); }
|
||||
static inline void *get_gadget_data(struct usb_gadget *gadget)
|
||||
@ -444,6 +445,26 @@ static inline struct usb_gadget *dev_to_usb_gadget(struct device *dev)
|
||||
{
|
||||
return container_of(dev, struct usb_gadget, dev);
|
||||
}
|
||||
static inline struct usb_gadget *usb_get_gadget(struct usb_gadget *gadget)
|
||||
{
|
||||
get_device(&gadget->dev);
|
||||
return gadget;
|
||||
}
|
||||
static inline void usb_put_gadget(struct usb_gadget *gadget)
|
||||
{
|
||||
put_device(&gadget->dev);
|
||||
}
|
||||
extern void usb_initialize_gadget(struct device *parent,
|
||||
struct usb_gadget *gadget, void (*release)(struct device *dev));
|
||||
extern int usb_add_gadget(struct usb_gadget *gadget);
|
||||
extern void usb_del_gadget(struct usb_gadget *gadget);
|
||||
|
||||
/* Legacy device-model interface */
|
||||
extern int usb_add_gadget_udc_release(struct device *parent,
|
||||
struct usb_gadget *gadget, void (*release)(struct device *dev));
|
||||
extern int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget);
|
||||
extern void usb_del_gadget_udc(struct usb_gadget *gadget);
|
||||
extern char *usb_get_gadget_udc_name(void);
|
||||
|
||||
/* iterates the non-control endpoints; 'tmp' is a struct usb_ep pointer */
|
||||
#define gadget_for_each_ep(tmp, gadget) \
|
||||
@ -735,12 +756,6 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver);
|
||||
*/
|
||||
int usb_gadget_unregister_driver(struct usb_gadget_driver *driver);
|
||||
|
||||
extern int usb_add_gadget_udc_release(struct device *parent,
|
||||
struct usb_gadget *gadget, void (*release)(struct device *dev));
|
||||
extern int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget);
|
||||
extern void usb_del_gadget_udc(struct usb_gadget *gadget);
|
||||
extern char *usb_get_gadget_udc_name(void);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* utility to simplify dealing with string descriptors */
|
||||
|
Loading…
Reference in New Issue
Block a user