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:
Greg Kroah-Hartman
2020-10-03 15:59:57 +02:00
63 changed files with 2062 additions and 765 deletions

View File

@@ -25,13 +25,14 @@ description: |
The Amlogic A1 embeds a DWC3 USB IP Core configured for USB2 in The Amlogic A1 embeds a DWC3 USB IP Core configured for USB2 in
host-only mode. 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: properties:
compatible: compatible:
enum: enum:
- amlogic,meson-gxl-usb-ctrl - amlogic,meson-gxl-usb-ctrl
- amlogic,meson-gxm-usb-ctrl - amlogic,meson-gxm-usb-ctrl
- amlogic,meson-axg-usb-ctrl
- amlogic,meson-g12a-usb-ctrl - amlogic,meson-g12a-usb-ctrl
- amlogic,meson-a1-usb-ctrl - amlogic,meson-a1-usb-ctrl
@@ -151,6 +152,25 @@ allOf:
required: required:
- clock-names - 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: - if:
properties: properties:
compatible: compatible:

View File

@@ -82,6 +82,7 @@ Required properties:
"atmel,at91sam9rl-udc" "atmel,at91sam9rl-udc"
"atmel,at91sam9g45-udc" "atmel,at91sam9g45-udc"
"atmel,sama5d3-udc" "atmel,sama5d3-udc"
"microchip,sam9x60-udc"
- reg: Address and length of the register set for the device - reg: Address and length of the register set for the device
- interrupts: Should contain usba interrupt - interrupts: Should contain usba interrupt
- clocks: Should reference the peripheral and host clocks - clocks: Should reference the peripheral and host clocks

View 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";
};
};

View File

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

View File

@@ -39,6 +39,7 @@ properties:
- amlogic,meson-g12a-usb - amlogic,meson-g12a-usb
- const: snps,dwc2 - const: snps,dwc2
- const: amcc,dwc-otg - const: amcc,dwc-otg
- const: apm,apm82181-dwc-otg
- const: snps,dwc2 - const: snps,dwc2
- const: st,stm32f4x9-fsotg - const: st,stm32f4x9-fsotg
- const: st,stm32f4x9-hsotg - const: st,stm32f4x9-hsotg
@@ -102,6 +103,10 @@ properties:
dr_mode: dr_mode:
enum: [host, peripheral, otg] enum: [host, peripheral, otg]
usb-role-switch:
$ref: /schemas/types.yaml#/definitions/flag
description: Support role switch.
g-rx-fifo-size: g-rx-fifo-size:
$ref: /schemas/types.yaml#/definitions/uint32 $ref: /schemas/types.yaml#/definitions/uint32
description: size of rx fifo size in gadget mode. description: size of rx fifo size in gadget mode.

View File

@@ -78,6 +78,9 @@ Optional properties:
park mode are disabled. park mode are disabled.
- snps,dis_metastability_quirk: when set, disable metastability workaround. - snps,dis_metastability_quirk: when set, disable metastability workaround.
CAUTION: use only if you are absolutely sure of it. 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 - snps,is-utmi-l1-suspend: true when DWC3 asserts output signal
utmi_l1_suspend_n, false when asserts utmi_sleep_n utmi_l1_suspend_n, false when asserts utmi_sleep_n
- snps,hird-threshold: HIRD threshold - snps,hird-threshold: HIRD threshold

View File

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

View File

@@ -3475,6 +3475,14 @@ F: drivers/bus/brcmstb_gisb.c
F: drivers/pci/controller/pcie-brcmstb.c F: drivers/pci/controller/pcie-brcmstb.c
N: brcmstb 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 BROADCOM BMIPS CPUFREQ DRIVER
M: Markus Mayer <mmayer@broadcom.com> M: Markus Mayer <mmayer@broadcom.com>
M: bcm-kernel-feedback-list@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: Documentation/devicetree/bindings/mtd/cadence-nand-controller.txt
F: drivers/mtd/nand/raw/cadence-nand-controller.c 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 CADET FM/AM RADIO RECEIVER DRIVER
M: Hans Verkuil <hverkuil@xs4all.nl> M: Hans Verkuil <hverkuil@xs4all.nl>
L: linux-media@vger.kernel.org L: linux-media@vger.kernel.org

View File

@@ -563,6 +563,12 @@
atmel,pins = <AT91_PIOD 18 AT91_PERIPH_GPIO AT91_PINCTRL_NONE>; 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 */ }; /* pinctrl */
&pmc { &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 { &usb1 {
num-ports = <3>; num-ports = <3>;
atmel,vbus-gpio = <0 atmel,vbus-gpio = <0

View File

@@ -69,6 +69,20 @@
#size-cells = <1>; #size-cells = <1>;
ranges; 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 { usb1: ohci@600000 {
compatible = "atmel,at91rm9200-ohci", "usb-ohci"; compatible = "atmel,at91rm9200-ohci", "usb-ohci";
reg = <0x00600000 0x100000>; reg = <0x00600000 0x100000>;

View File

@@ -15,6 +15,8 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/iopoll.h> #include <linux/iopoll.h>
#include <linux/pm_runtime.h>
#include "core.h"
#define USB3_CORE_CTRL1 0x00 #define USB3_CORE_CTRL1 0x00
#define USB3_CORE_CTRL2 0x04 #define USB3_CORE_CTRL2 0x04
@@ -32,7 +34,7 @@
/* Register bits definition */ /* Register bits definition */
/* USB3_CORE_CTRL1 */ /* USB3_CORE_CTRL1 */
#define SW_RESET_MASK (0x3f << 26) #define SW_RESET_MASK GENMASK(31, 26)
#define PWR_SW_RESET BIT(31) #define PWR_SW_RESET BIT(31)
#define APB_SW_RESET BIT(30) #define APB_SW_RESET BIT(30)
#define AXI_SW_RESET BIT(29) #define AXI_SW_RESET BIT(29)
@@ -53,8 +55,8 @@
#define LPM_CLK_REQ BIT(28) #define LPM_CLK_REQ BIT(28)
#define DEVU3_WAEKUP_EN BIT(14) #define DEVU3_WAEKUP_EN BIT(14)
#define OTG_WAKEUP_EN BIT(12) #define OTG_WAKEUP_EN BIT(12)
#define DEV_INT_EN (3 << 8) /* DEV INT b9:8 */ #define DEV_INT_EN (3 << 8) /* DEV INT b9:8 */
#define HOST_INT1_EN (1 << 0) /* HOST INT b7:0 */ #define HOST_INT1_EN (1 << 0) /* HOST INT b7:0 */
/* USB3_CORE_STATUS */ /* USB3_CORE_STATUS */
#define MDCTRL_CLK_STATUS BIT(15) #define MDCTRL_CLK_STATUS BIT(15)
@@ -66,11 +68,30 @@
#define CLK_VALID_COMPARE_BITS (0xf << 28) #define CLK_VALID_COMPARE_BITS (0xf << 28)
#define PHY_REFCLK_REQ (1 << 0) #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 cdns_imx {
struct device *dev; struct device *dev;
void __iomem *noncore; void __iomem *noncore;
struct clk_bulk_data *clks; struct clk_bulk_data *clks;
int num_clks; int num_clks;
struct platform_device *cdns3_pdev;
}; };
static inline u32 cdns_imx_readl(struct cdns_imx *data, u32 offset) 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; 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) static int cdns_imx_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
@@ -162,14 +197,18 @@ static int cdns_imx_probe(struct platform_device *pdev)
if (ret) if (ret)
goto err; goto err;
ret = of_platform_populate(node, NULL, NULL, dev); ret = of_platform_populate(node, NULL, cdns_imx_auxdata, dev);
if (ret) { if (ret) {
dev_err(dev, "failed to create children: %d\n", ret); dev_err(dev, "failed to create children: %d\n", ret);
goto err; 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: err:
clk_bulk_disable_unprepare(data->num_clks, data->clks); clk_bulk_disable_unprepare(data->num_clks, data->clks);
return ret; return ret;
@@ -194,6 +233,147 @@ static int cdns_imx_remove(struct platform_device *pdev)
return 0; 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[] = { static const struct of_device_id cdns_imx_of_match[] = {
{ .compatible = "fsl,imx8qm-usb3", }, { .compatible = "fsl,imx8qm-usb3", },
{}, {},
@@ -206,6 +386,7 @@ static struct platform_driver cdns_imx_driver = {
.driver = { .driver = {
.name = "cdns3-imx", .name = "cdns3-imx",
.of_match_table = cdns_imx_of_match, .of_match_table = cdns_imx_of_match,
.pm = &cdns_imx_pm_ops,
}, },
}; };
module_platform_driver(cdns_imx_driver); module_platform_driver(cdns_imx_driver);

View File

@@ -280,6 +280,10 @@ int cdns3_hw_role_switch(struct cdns3 *cdns)
enum usb_role real_role, current_role; enum usb_role real_role, current_role;
int ret = 0; int ret = 0;
/* Depends on role switch class */
if (cdns->role_sw)
return 0;
pm_runtime_get_sync(cdns->dev); pm_runtime_get_sync(cdns->dev);
current_role = cdns->role; current_role = cdns->role;
@@ -371,6 +375,50 @@ pm_put:
return ret; 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 * cdns3_probe - probe for cdns3 core device
* @pdev: Pointer to cdns3 core platform device * @pdev: Pointer to cdns3 core platform device
@@ -397,6 +445,7 @@ static int cdns3_probe(struct platform_device *pdev)
return -ENOMEM; return -ENOMEM;
cdns->dev = dev; cdns->dev = dev;
cdns->pdata = dev_get_platdata(dev);
platform_set_drvdata(pdev, cdns); platform_set_drvdata(pdev, cdns);
@@ -443,8 +492,21 @@ static int cdns3_probe(struct platform_device *pdev)
return -ENXIO; return -ENXIO;
} }
cdns->phyrst_a_enable = device_property_read_bool(dev, "cdns,phyrst-a-enable");
cdns->otg_res = *res; 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); mutex_init(&cdns->mutex);
cdns->usb2_phy = devm_phy_optional_get(dev, "cdns3,usb2-phy"); 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) if (ret)
goto err1; goto err1;
ret = phy_power_on(cdns->usb2_phy); ret = set_phy_power_on(cdns);
if (ret) if (ret)
goto err2; goto err2;
ret = phy_power_on(cdns->usb3_phy);
if (ret)
goto err3;
sw_desc.set = cdns3_role_set; sw_desc.set = cdns3_role_set;
sw_desc.get = cdns3_role_get; sw_desc.get = cdns3_role_get;
sw_desc.allow_userspace_control = true; sw_desc.allow_userspace_control = true;
@@ -482,20 +540,34 @@ static int cdns3_probe(struct platform_device *pdev)
if (IS_ERR(cdns->role_sw)) { if (IS_ERR(cdns->role_sw)) {
ret = PTR_ERR(cdns->role_sw); ret = PTR_ERR(cdns->role_sw);
dev_warn(dev, "Unable to register Role Switch\n"); 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); ret = cdns3_drd_init(cdns);
if (ret) if (ret)
goto err5; goto err4;
ret = cdns3_core_init_role(cdns); ret = cdns3_core_init_role(cdns);
if (ret) if (ret)
goto err5; goto err4;
spin_lock_init(&cdns->lock);
device_set_wakeup_capable(dev, true); device_set_wakeup_capable(dev, true);
pm_runtime_set_active(dev); pm_runtime_set_active(dev);
pm_runtime_enable(dev); pm_runtime_enable(dev);
pm_runtime_forbid(dev);
/* /*
* The controller needs less time between bus and controller suspend, * 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"); dev_dbg(dev, "Cadence USB3 core: probe succeed\n");
return 0; return 0;
err5: err4:
cdns3_drd_exit(cdns); cdns3_drd_exit(cdns);
usb_role_switch_unregister(cdns->role_sw); usb_role_switch_unregister(cdns->role_sw);
err4:
phy_power_off(cdns->usb3_phy);
err3: err3:
phy_power_off(cdns->usb2_phy); set_phy_power_off(cdns);
err2: err2:
phy_exit(cdns->usb3_phy); phy_exit(cdns->usb3_phy);
err1: err1:
@@ -539,13 +608,89 @@ static int cdns3_remove(struct platform_device *pdev)
pm_runtime_put_noidle(&pdev->dev); pm_runtime_put_noidle(&pdev->dev);
cdns3_exit_roles(cdns); cdns3_exit_roles(cdns);
usb_role_switch_unregister(cdns->role_sw); usb_role_switch_unregister(cdns->role_sw);
phy_power_off(cdns->usb2_phy); set_phy_power_off(cdns);
phy_power_off(cdns->usb3_phy);
phy_exit(cdns->usb2_phy); phy_exit(cdns->usb2_phy);
phy_exit(cdns->usb3_phy); phy_exit(cdns->usb3_phy);
return 0; 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 #ifdef CONFIG_PM_SLEEP
static int cdns3_suspend(struct device *dev) 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); struct cdns3 *cdns = dev_get_drvdata(dev);
unsigned long flags; unsigned long flags;
if (cdns->role == USB_ROLE_HOST)
return 0;
if (pm_runtime_status_suspended(dev)) if (pm_runtime_status_suspended(dev))
pm_runtime_resume(dev); pm_runtime_resume(dev);
if (cdns->roles[cdns->role]->suspend) { 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); 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) static int cdns3_resume(struct device *dev)
{ {
struct cdns3 *cdns = dev_get_drvdata(dev); int ret;
unsigned long flags;
if (cdns->role == USB_ROLE_HOST) ret = cdns3_controller_resume(dev, PMSG_RESUME);
return 0; if (ret)
return ret;
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);
}
pm_runtime_disable(dev); pm_runtime_disable(dev);
pm_runtime_set_active(dev); pm_runtime_set_active(dev);
pm_runtime_enable(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 = { static const struct dev_pm_ops cdns3_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(cdns3_suspend, cdns3_resume) SET_SYSTEM_SLEEP_PM_OPS(cdns3_suspend, cdns3_resume)
SET_RUNTIME_PM_OPS(cdns3_runtime_suspend, cdns3_runtime_resume, NULL)
}; };
#ifdef CONFIG_OF #ifdef CONFIG_OF

View File

@@ -38,6 +38,12 @@ struct cdns3_role_driver {
}; };
#define CDNS3_XHCI_RESOURCES_NUM 2 #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. * struct cdns3 - Representation of Cadence USB3 DRD controller.
* @dev: pointer to Cadence device struct * @dev: pointer to Cadence device struct
@@ -50,6 +56,7 @@ struct cdns3_role_driver {
* @otg_regs: pointer to base of otg registers * @otg_regs: pointer to base of otg registers
* @otg_irq: irq number for otg controller * @otg_irq: irq number for otg controller
* @dev_irq: irq number for device 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 * @roles: array of supported roles for this controller
* @role: current role * @role: current role
* @host_dev: the child host device pointer for cdns3 core * @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 * This field based on firmware setting, kernel configuration
* and hardware configuration. * and hardware configuration.
* @role_sw: pointer to role switch object. * @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 cdns3 {
struct device *dev; struct device *dev;
@@ -76,9 +87,11 @@ struct cdns3 {
#define CDNS3_CONTROLLER_V0 0 #define CDNS3_CONTROLLER_V0 0
#define CDNS3_CONTROLLER_V1 1 #define CDNS3_CONTROLLER_V1 1
u32 version; u32 version;
bool phyrst_a_enable;
int otg_irq; int otg_irq;
int dev_irq; int dev_irq;
int wakeup_irq;
struct cdns3_role_driver *roles[USB_ROLE_DEVICE + 1]; struct cdns3_role_driver *roles[USB_ROLE_DEVICE + 1];
enum usb_role role; enum usb_role role;
struct platform_device *host_dev; struct platform_device *host_dev;
@@ -89,6 +102,10 @@ struct cdns3 {
struct mutex mutex; struct mutex mutex;
enum usb_dr_mode dr_mode; enum usb_dr_mode dr_mode;
struct usb_role_switch *role_sw; 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); int cdns3_hw_role_switch(struct cdns3 *cdns);

View File

@@ -15,6 +15,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/iopoll.h> #include <linux/iopoll.h>
#include <linux/usb/otg.h> #include <linux/usb/otg.h>
#include <linux/phy/phy.h>
#include "gadget.h" #include "gadget.h"
#include "drd.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 = readl(&cdns->otg_v1_regs->override);
reg |= OVERRIDE_IDPULLUP; reg |= OVERRIDE_IDPULLUP;
writel(reg, &cdns->otg_v1_regs->override); 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 { } else {
reg = readl(&cdns->otg_v0_regs->ctrl1); reg = readl(&cdns->otg_v0_regs->ctrl1);
reg |= OVERRIDE_IDPULLUP_V0; reg |= OVERRIDE_IDPULLUP_V0;
@@ -145,6 +158,7 @@ int cdns3_drd_host_on(struct cdns3 *cdns)
if (ret) if (ret)
dev_err(cdns->dev, "timeout waiting for xhci_ready\n"); dev_err(cdns->dev, "timeout waiting for xhci_ready\n");
phy_set_mode(cdns->usb3_phy, PHY_MODE_USB_HOST);
return ret; return ret;
} }
@@ -164,6 +178,7 @@ void cdns3_drd_host_off(struct cdns3 *cdns)
readl_poll_timeout_atomic(&cdns->otg_regs->state, val, readl_poll_timeout_atomic(&cdns->otg_regs->state, val,
!(val & OTGSTATE_HOST_STATE_MASK), !(val & OTGSTATE_HOST_STATE_MASK),
1, 2000000); 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; return ret;
} }
phy_set_mode(cdns->usb3_phy, PHY_MODE_USB_DEVICE);
return 0; return 0;
} }
@@ -213,6 +229,7 @@ void cdns3_drd_gadget_off(struct cdns3 *cdns)
readl_poll_timeout_atomic(&cdns->otg_regs->state, val, readl_poll_timeout_atomic(&cdns->otg_regs->state, val,
!(val & OTGSTATE_DEV_STATE_MASK), !(val & OTGSTATE_DEV_STATE_MASK),
1, 2000000); 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) if (cdns->dr_mode != USB_DR_MODE_OTG)
return IRQ_NONE; return IRQ_NONE;
if (cdns->in_lpm)
return ret;
reg = readl(&cdns->otg_regs->ivect); reg = readl(&cdns->otg_regs->ivect);
if (!reg) if (!reg)

View File

@@ -31,7 +31,7 @@ struct cdns3_otg_regs {
__le32 simulate; __le32 simulate;
__le32 override; __le32 override;
__le32 susp_ctrl; __le32 susp_ctrl;
__le32 reserved4; __le32 phyrst_cfg;
__le32 anasts; __le32 anasts;
__le32 adp_ramp_time; __le32 adp_ramp_time;
__le32 ctrl1; __le32 ctrl1;
@@ -153,6 +153,9 @@ struct cdns3_otg_common_regs {
/* Only for CDNS3_CONTROLLER_V0 version */ /* Only for CDNS3_CONTROLLER_V0 version */
#define OVERRIDE_IDPULLUP_V0 BIT(24) #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_PERIPHERAL 1
#define CDNS3_ID_HOST 0 #define CDNS3_ID_HOST 0

View File

@@ -717,9 +717,17 @@ static int cdns3_gadget_ep0_queue(struct usb_ep *ep,
/* send STATUS stage. Should be called only for SET_CONFIGURATION */ /* send STATUS stage. Should be called only for SET_CONFIGURATION */
if (priv_dev->ep0_stage == CDNS3_STATUS_STAGE) { if (priv_dev->ep0_stage == CDNS3_STATUS_STAGE) {
u32 val;
cdns3_select_ep(priv_dev, 0x00); cdns3_select_ep(priv_dev, 0x00);
cdns3_set_hw_configuration(priv_dev); cdns3_set_hw_configuration(priv_dev);
cdns3_ep0_complete_setup(priv_dev, 0, 1); 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; request->actual = 0;
priv_dev->status_completion_no_call = true; priv_dev->status_completion_no_call = true;
priv_dev->pending_status_request = request; priv_dev->pending_status_request = request;
@@ -731,7 +739,7 @@ static int cdns3_gadget_ep0_queue(struct usb_ep *ep,
* ep0_queue is back. * ep0_queue is back.
*/ */
queue_work(system_freezable_wq, &priv_dev->pending_status_wq); queue_work(system_freezable_wq, &priv_dev->pending_status_wq);
return 0; return ret;
} }
if (!list_empty(&priv_ep->pending_req_list)) { if (!list_empty(&priv_ep->pending_req_list)) {

View File

@@ -261,8 +261,8 @@ int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep)
*/ */
link_trb->control = 0; link_trb->control = 0;
} else { } else {
link_trb->buffer = TRB_BUFFER(priv_ep->trb_pool_dma); link_trb->buffer = cpu_to_le32(TRB_BUFFER(priv_ep->trb_pool_dma));
link_trb->control = TRB_CYCLE | TRB_TYPE(TRB_LINK) | TRB_TOGGLE; link_trb->control = cpu_to_le32(TRB_CYCLE | TRB_TYPE(TRB_LINK) | TRB_TOGGLE);
} }
return 0; return 0;
} }
@@ -462,6 +462,36 @@ static int cdns3_start_all_request(struct cdns3_device *priv_dev,
(reg) |= EP_STS_EN_DESCMISEN; \ (reg) |= EP_STS_EN_DESCMISEN; \
} } while (0) } } 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 * cdns3_wa2_descmiss_copy_data copy data from internal requests to
* request queued by class driver. * 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; chunk_end = descmiss_priv_req->flags & REQUEST_INTERNAL_CH;
length = request->actual + descmiss_req->actual; length = request->actual + descmiss_req->actual;
request->status = descmiss_req->status; request->status = descmiss_req->status;
__cdns3_descmiss_copy_data(request, descmiss_req);
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;
}
list_del_init(&descmiss_priv_req->list); list_del_init(&descmiss_priv_req->list);
kfree(descmiss_req->buf); kfree(descmiss_req->buf);
cdns3_gadget_ep_free_request(&priv_ep->endpoint, descmiss_req); cdns3_gadget_ep_free_request(&priv_ep->endpoint, descmiss_req);
--priv_ep->wa2_counter; --priv_ep->wa2_counter;
@@ -817,6 +835,8 @@ void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep,
request->length); request->length);
priv_req->flags &= ~(REQUEST_PENDING | REQUEST_UNALIGNED); 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); trace_cdns3_gadget_giveback(priv_req);
if (priv_dev->dev_ver < DEV_VER_V2) { 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; priv_ep->wa1_trb_index = 0xFFFF;
if (priv_ep->wa1_cycle_bit) { if (priv_ep->wa1_cycle_bit) {
priv_ep->wa1_trb->control = priv_ep->wa1_trb->control =
priv_ep->wa1_trb->control | 0x1; priv_ep->wa1_trb->control | cpu_to_le32(0x1);
} else { } else {
priv_ep->wa1_trb->control = 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; TRB_STREAM_ID(priv_req->request.stream_id) | TRB_ISP;
if (!request->num_sgs) { if (!request->num_sgs) {
trb->buffer = TRB_BUFFER(trb_dma); trb->buffer = cpu_to_le32(TRB_BUFFER(trb_dma));
length = request->length; length = request->length;
} else { } 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; length = request->sg[sg_idx].length;
} }
tdl = DIV_ROUND_UP(length, priv_ep->endpoint.maxpacket); tdl = DIV_ROUND_UP(length, priv_ep->endpoint.maxpacket);
trb->length = TRB_BURST_LEN(16 /*priv_ep->trb_burst_size*/) | trb->length = cpu_to_le32(TRB_BURST_LEN(16) | TRB_LEN(length));
TRB_LEN(length);
/* /*
* For DEV_VER_V2 controller version we have enabled * 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->dev_ver >= DEV_VER_V2) {
if (priv_dev->gadget.speed == USB_SPEED_SUPER) 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; priv_req->flags |= REQUEST_PENDING;
trb->control = control; trb->control = cpu_to_le32(control);
trace_cdns3_prepare_trb(priv_ep, priv_req->trb); 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_device *priv_dev = priv_ep->cdns3_dev;
struct cdns3_request *priv_req; struct cdns3_request *priv_req;
struct cdns3_trb *trb; struct cdns3_trb *trb;
struct cdns3_trb *link_trb;
dma_addr_t trb_dma; dma_addr_t trb_dma;
u32 togle_pcs = 1; u32 togle_pcs = 1;
int sg_iter = 0; int sg_iter = 0;
@@ -1099,11 +1119,13 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
u32 control; u32 control;
int pcs; int pcs;
u16 total_tdl = 0; u16 total_tdl = 0;
struct scatterlist *s = NULL;
bool sg_supported = !!(request->num_mapped_sgs);
if (priv_ep->type == USB_ENDPOINT_XFER_ISOC) if (priv_ep->type == USB_ENDPOINT_XFER_ISOC)
num_trb = priv_ep->interval; num_trb = priv_ep->interval;
else 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) { if (num_trb > priv_ep->free_trbs) {
priv_ep->flags |= EP_RING_FULL; priv_ep->flags |= EP_RING_FULL;
@@ -1129,7 +1151,6 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
/* prepare ring */ /* prepare ring */
if ((priv_ep->enqueue + num_trb) >= (priv_ep->num_trbs - 1)) { if ((priv_ep->enqueue + num_trb) >= (priv_ep->num_trbs - 1)) {
struct cdns3_trb *link_trb;
int doorbell, dma_index; int doorbell, dma_index;
u32 ch_bit = 0; u32 ch_bit = 0;
@@ -1156,13 +1177,16 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
TRBS_PER_SEGMENT > 2) TRBS_PER_SEGMENT > 2)
ch_bit = TRB_CHAIN; ch_bit = TRB_CHAIN;
link_trb->control = ((priv_ep->pcs) ? TRB_CYCLE : 0) | link_trb->control = cpu_to_le32(((priv_ep->pcs) ? TRB_CYCLE : 0) |
TRB_TYPE(TRB_LINK) | TRB_TOGGLE | ch_bit; TRB_TYPE(TRB_LINK) | TRB_TOGGLE | ch_bit);
} }
if (priv_dev->dev_ver <= DEV_VER_V2) if (priv_dev->dev_ver <= DEV_VER_V2)
togle_pcs = cdns3_wa1_update_guard(priv_ep, trb); togle_pcs = cdns3_wa1_update_guard(priv_ep, trb);
if (sg_supported)
s = request->sg;
/* set incorrect Cycle Bit for first trb*/ /* set incorrect Cycle Bit for first trb*/
control = priv_ep->pcs ? 0 : TRB_CYCLE; control = priv_ep->pcs ? 0 : TRB_CYCLE;
@@ -1172,13 +1196,13 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
/* fill TRB */ /* fill TRB */
control |= TRB_TYPE(TRB_NORMAL); control |= TRB_TYPE(TRB_NORMAL);
trb->buffer = TRB_BUFFER(request->num_sgs == 0 if (sg_supported) {
? trb_dma : request->sg[sg_iter].dma_address); trb->buffer = cpu_to_le32(TRB_BUFFER(sg_dma_address(s)));
length = sg_dma_len(s);
if (likely(!request->num_sgs)) } else {
trb->buffer = cpu_to_le32(TRB_BUFFER(trb_dma));
length = request->length; length = request->length;
else }
length = request->sg[sg_iter].length;
if (likely(priv_dev->dev_ver >= DEV_VER_V2)) if (likely(priv_dev->dev_ver >= DEV_VER_V2))
td_size = DIV_ROUND_UP(length, 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, total_tdl += DIV_ROUND_UP(length,
priv_ep->endpoint.maxpacket); priv_ep->endpoint.maxpacket);
trb->length = TRB_BURST_LEN(priv_ep->trb_burst_size) | trb->length = cpu_to_le32(TRB_BURST_LEN(priv_ep->trb_burst_size) |
TRB_LEN(length); TRB_LEN(length));
if (priv_dev->gadget.speed == USB_SPEED_SUPER) 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 else
control |= TRB_TDL_HS_SIZE(td_size); 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) if (sg_iter)
trb->control = control; trb->control = cpu_to_le32(control);
else 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; control = 0;
++sg_iter; ++sg_iter;
@@ -1226,9 +1259,10 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
trb = priv_req->trb; trb = priv_req->trb;
priv_req->flags |= REQUEST_PENDING; priv_req->flags |= REQUEST_PENDING;
priv_req->num_of_trb = num_trb;
if (sg_iter == 1) 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 && if (priv_dev->dev_ver < DEV_VER_V2 &&
(priv_ep->flags & EP_TDLCHK_EN)) { (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*/ /* give the TD to the consumer*/
if (togle_pcs) if (togle_pcs)
trb->control = trb->control ^ 1; trb->control = trb->control ^ cpu_to_le32(1);
if (priv_dev->dev_ver <= DEV_VER_V2) if (priv_dev->dev_ver <= DEV_VER_V2)
cdns3_wa1_tray_restore_cycle_bit(priv_dev, priv_ep); 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 * 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 cdns3_endpoint *priv_ep;
struct usb_ep *ep; struct usb_ep *ep;
int val;
if (priv_dev->hw_configured_flag) if (priv_dev->hw_configured_flag)
return; return;
@@ -1320,10 +1368,6 @@ void cdns3_set_hw_configuration(struct cdns3_device *priv_dev)
cdns3_set_register_bit(&priv_dev->regs->usb_conf, cdns3_set_register_bit(&priv_dev->regs->usb_conf,
USB_CONF_U1EN | USB_CONF_U2EN); 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; priv_dev->hw_configured_flag = 1;
list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) { 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_ep: extended endpoint object.
* @priv_req: request object for checking * @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 * ET = priv_req->end_trb - index of last TRB in transfer ring
* CI = current_index - index of processed TRB by DMA. * CI = current_index - index of processed TRB by DMA.
* *
* As first step, function checks if cycle bit for priv_req->start_trb is * As first step, we check if the TRB between the ST and ET.
* correct. * Then, we check if cycle bit for index priv_ep->dequeue
* is correct.
* *
* some rules: * 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 * 2 priv_ep->enqueue never exceed priv_ep->dequeue
* 3. exception: priv_ep->enqueue == priv_ep->dequeue * 3. exception: priv_ep->enqueue == priv_ep->dequeue
* and priv_ep->free_trbs is zero. * and priv_ep->free_trbs is zero.
* This case indicate that TR is full. * 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 * Case 1 - priv_ep->dequeue < current_index
* SR ... EQ ... DQ ... CI ... ER * SR ... EQ ... DQ ... CI ... ER
* SR ... DQ ... CI ... EQ ... 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 * 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. * transfer ring.
* SR ... CI ... EQ ... DQ ... ER * 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_request *priv_req)
{ {
struct cdns3_device *priv_dev = priv_ep->cdns3_dev; 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); current_index = cdns3_get_dma_pos(priv_dev, priv_ep);
doorbell = !!(readl(&priv_dev->regs->ep_cmd) & EP_CMD_DRDY); 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; goto finish;
if (doorbell == 1 && current_index == priv_ep->dequeue) 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) !priv_ep->dequeue)
goto finish; goto finish;
if (priv_req->end_trb >= priv_ep->dequeue && handled = 1;
priv_req->end_trb < current_index)
handled = 1;
} else if (priv_ep->dequeue > current_index) { } else if (priv_ep->dequeue > current_index) {
if (priv_req->end_trb < current_index ||
priv_req->end_trb >= priv_ep->dequeue)
handled = 1; handled = 1;
} }
@@ -1434,6 +1488,8 @@ static void cdns3_transfer_completed(struct cdns3_device *priv_dev,
struct cdns3_request *priv_req; struct cdns3_request *priv_req;
struct usb_request *request; struct usb_request *request;
struct cdns3_trb *trb; struct cdns3_trb *trb;
bool request_handled = false;
bool transfer_end = false;
while (!list_empty(&priv_ep->pending_req_list)) { while (!list_empty(&priv_ep->pending_req_list)) {
request = cdns3_next_request(&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; trb = priv_ep->trb_pool + priv_ep->dequeue;
/* Request was dequeued and TRB was changed to TRB_LINK. */ /* 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); trace_cdns3_complete_trb(priv_ep, trb);
cdns3_move_deq_to_next_trb(priv_req); 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); 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; 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 && if (priv_ep->type != USB_ENDPOINT_XFER_ISOC &&
TRBS_PER_SEGMENT == 2) 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 * that host ignore the ERDY packet and driver has to send it
* again. * 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))) { EP_STS_HOSTPP(ep_sts_reg))) {
writel(EP_CMD_ERDY | writel(EP_CMD_ERDY |
EP_CMD_ERDY_SID(priv_ep->last_stream_id), 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) static irqreturn_t cdns3_device_irq_handler(int irq, void *data)
{ {
struct cdns3_device *priv_dev = data; struct cdns3_device *priv_dev = data;
struct cdns3 *cdns = dev_get_drvdata(priv_dev->dev);
irqreturn_t ret = IRQ_NONE; irqreturn_t ret = IRQ_NONE;
u32 reg; u32 reg;
if (cdns->in_lpm)
return ret;
/* check USB device interrupt */ /* check USB device interrupt */
reg = readl(&priv_dev->regs->usb_ists); reg = readl(&priv_dev->regs->usb_ists);
if (reg) { if (reg) {
@@ -2552,10 +2624,10 @@ found:
/* Update ring only if removed request is on pending_req_list list */ /* Update ring only if removed request is on pending_req_list list */
if (req_on_hw_ring && link_trb) { if (req_on_hw_ring && link_trb) {
link_trb->buffer = TRB_BUFFER(priv_ep->trb_pool_dma + link_trb->buffer = cpu_to_le32(TRB_BUFFER(priv_ep->trb_pool_dma +
((priv_req->end_trb + 1) * TRB_SIZE)); ((priv_req->end_trb + 1) * TRB_SIZE)));
link_trb->control = (link_trb->control & TRB_CYCLE) | link_trb->control = cpu_to_le32((le32_to_cpu(link_trb->control) & TRB_CYCLE) |
TRB_TYPE(TRB_LINK) | TRB_CHAIN; TRB_TYPE(TRB_LINK) | TRB_CHAIN);
if (priv_ep->wa1_trb == priv_req->trb) if (priv_ep->wa1_trb == priv_req->trb)
cdns3_wa1_restore_cycle_bit(priv_ep); 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); priv_req = to_cdns3_request(request);
trb = priv_req->trb; trb = priv_req->trb;
if (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); 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 (request) {
if (trb) if (trb)
trb->control = trb->control ^ TRB_CYCLE; trb->control = trb->control ^ cpu_to_le32(TRB_CYCLE);
cdns3_rearm_transfer(priv_ep, 1); 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); 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); 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); writel(USB_CONF_DEVDS, &priv_dev->regs->usb_conf);
}
return 0; return 0;
} }
@@ -2779,6 +2855,8 @@ static void cdns3_gadget_config(struct cdns3_device *priv_dev)
/* enable generic interrupt*/ /* enable generic interrupt*/
writel(USB_IEN_INIT, &regs->usb_ien); writel(USB_IEN_INIT, &regs->usb_ien);
writel(USB_CONF_CLK2OFFDS | USB_CONF_L1DS, &regs->usb_conf); writel(USB_CONF_CLK2OFFDS | USB_CONF_L1DS, &regs->usb_conf);
/* keep Fast Access bit */
writel(PUSB_PWR_FST_REG_ACCESS, &priv_dev->regs->usb_pwr);
cdns3_configure_dmult(priv_dev, NULL); cdns3_configure_dmult(priv_dev, NULL);
} }
@@ -2862,6 +2940,7 @@ static int cdns3_gadget_udc_stop(struct usb_gadget *gadget)
/* disable interrupt for device */ /* disable interrupt for device */
writel(0, &priv_dev->regs->usb_ien); writel(0, &priv_dev->regs->usb_ien);
writel(0, &priv_dev->regs->usb_pwr);
writel(USB_CONF_DEVDS, &priv_dev->regs->usb_conf); writel(USB_CONF_DEVDS, &priv_dev->regs->usb_conf);
return 0; return 0;
@@ -2984,18 +3063,26 @@ err:
return -ENOMEM; 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) void cdns3_gadget_exit(struct cdns3 *cdns)
{ {
struct cdns3_device *priv_dev; struct cdns3_device *priv_dev;
priv_dev = cdns->gadget_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_mark_last_busy(cdns->dev);
pm_runtime_put_autosuspend(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); cdns3_free_all_eps(priv_dev);
@@ -3015,7 +3102,7 @@ void cdns3_gadget_exit(struct cdns3 *cdns)
priv_dev->setup_dma); priv_dev->setup_dma);
kfree(priv_dev->zlp_buf); kfree(priv_dev->zlp_buf);
kfree(priv_dev); usb_put_gadget(&priv_dev->gadget);
cdns->gadget_dev = NULL; cdns->gadget_dev = NULL;
cdns3_drd_gadget_off(cdns); cdns3_drd_gadget_off(cdns);
} }
@@ -3030,6 +3117,8 @@ static int cdns3_gadget_start(struct cdns3 *cdns)
if (!priv_dev) if (!priv_dev)
return -ENOMEM; return -ENOMEM;
usb_initialize_gadget(cdns->dev, &priv_dev->gadget,
cdns3_gadget_release);
cdns->gadget_dev = priv_dev; cdns->gadget_dev = priv_dev;
priv_dev->sysdev = cdns->dev; priv_dev->sysdev = cdns->dev;
priv_dev->dev = 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.speed = USB_SPEED_UNKNOWN;
priv_dev->gadget.ops = &cdns3_gadget_ops; priv_dev->gadget.ops = &cdns3_gadget_ops;
priv_dev->gadget.name = "usb-ss-gadget"; priv_dev->gadget.name = "usb-ss-gadget";
priv_dev->gadget.sg_supported = 1;
priv_dev->gadget.quirk_avoids_skb_reserve = 1; priv_dev->gadget.quirk_avoids_skb_reserve = 1;
priv_dev->gadget.irq = cdns->dev_irq; 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)); readl(&priv_dev->regs->usb_cap2));
priv_dev->dev_ver = GET_DEV_BASE_VERSION(priv_dev->dev_ver); 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); priv_dev->zlp_buf = kzalloc(CDNS3_EP_ZLP_BUF_SIZE, GFP_KERNEL);
if (!priv_dev->zlp_buf) { if (!priv_dev->zlp_buf) {
@@ -3117,10 +3207,9 @@ static int cdns3_gadget_start(struct cdns3 *cdns)
} }
/* add USB gadget device */ /* 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) { if (ret < 0) {
dev_err(priv_dev->dev, dev_err(priv_dev->dev, "Failed to add gadget\n");
"Failed to register USB device controller\n");
goto err4; goto err4;
} }
@@ -3133,6 +3222,7 @@ err3:
err2: err2:
cdns3_free_all_eps(priv_dev); cdns3_free_all_eps(priv_dev);
err1: err1:
usb_put_gadget(&priv_dev->gadget);
cdns->gadget_dev = NULL; cdns->gadget_dev = NULL;
return ret; return ret;
} }

View File

@@ -966,7 +966,7 @@ struct cdns3_usb_regs {
/* /*
* USBSS-DEV DMA interface. * USBSS-DEV DMA interface.
*/ */
#define TRBS_PER_SEGMENT 40 #define TRBS_PER_SEGMENT 600
#define ISO_MAX_INTERVAL 10 #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 * When set to '1', the device will toggle its interpretation of the Cycle bit
*/ */
#define TRB_TOGGLE BIT(1) #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 * Short Packet (SP). OUT EPs at DMULT=1 only. Indicates if the TRB was
@@ -1215,6 +1220,8 @@ struct cdns3_aligned_buf {
* this endpoint * this endpoint
* @flags: flag specifying special usage of request * @flags: flag specifying special usage of request
* @list: used by internally allocated request to add to wa2_descmiss_req_list. * @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 cdns3_request {
struct usb_request request; struct usb_request request;
@@ -1230,6 +1237,8 @@ struct cdns3_request {
#define REQUEST_UNALIGNED BIT(4) #define REQUEST_UNALIGNED BIT(4)
u32 flags; u32 flags;
struct list_head list; struct list_head list;
int finished_trb;
int num_of_trb;
}; };
#define to_cdns3_request(r) (container_of(r, struct cdns3_request, request)) #define to_cdns3_request(r) (container_of(r, struct cdns3_request, request))

View File

@@ -13,11 +13,13 @@
#include "core.h" #include "core.h"
#include "drd.h" #include "drd.h"
#include "host-export.h" #include "host-export.h"
#include <linux/usb/hcd.h>
static int __cdns3_host_init(struct cdns3 *cdns) static int __cdns3_host_init(struct cdns3 *cdns)
{ {
struct platform_device *xhci; struct platform_device *xhci;
int ret; int ret;
struct usb_hcd *hcd;
cdns3_drd_host_on(cdns); cdns3_drd_host_on(cdns);
@@ -43,6 +45,11 @@ static int __cdns3_host_init(struct cdns3 *cdns)
goto err1; 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; return 0;
err1: err1:
platform_device_put(xhci); platform_device_put(xhci);

View File

@@ -5,6 +5,7 @@ config USB_DWC2
depends on HAS_DMA depends on HAS_DMA
depends on USB || USB_GADGET depends on USB || USB_GADGET
depends on HAS_IOMEM depends on HAS_IOMEM
select USB_ROLE_SWITCH
help help
Say Y here if your system has a Dual Role Hi-Speed USB Say Y here if your system has a Dual Role Hi-Speed USB
controller based on the DesignWare HSOTG IP Core. controller based on the DesignWare HSOTG IP Core.

View File

@@ -3,7 +3,7 @@ ccflags-$(CONFIG_USB_DWC2_DEBUG) += -DDEBUG
ccflags-$(CONFIG_USB_DWC2_VERBOSE) += -DVERBOSE_DEBUG ccflags-$(CONFIG_USB_DWC2_VERBOSE) += -DVERBOSE_DEBUG
obj-$(CONFIG_USB_DWC2) += dwc2.o 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 dwc2-y += params.o
ifneq ($(filter y,$(CONFIG_USB_DWC2_HOST) $(CONFIG_USB_DWC2_DUAL_ROLE)),) ifneq ($(filter y,$(CONFIG_USB_DWC2_HOST) $(CONFIG_USB_DWC2_DUAL_ROLE)),)

View File

@@ -860,6 +860,7 @@ struct dwc2_hregs_backup {
* - USB_DR_MODE_PERIPHERAL * - USB_DR_MODE_PERIPHERAL
* - USB_DR_MODE_HOST * - USB_DR_MODE_HOST
* - USB_DR_MODE_OTG * - USB_DR_MODE_OTG
* @role_sw: usb_role_switch handle
* @hcd_enabled: Host mode sub-driver initialization indicator. * @hcd_enabled: Host mode sub-driver initialization indicator.
* @gadget_enabled: Peripheral mode sub-driver initialization indicator. * @gadget_enabled: Peripheral mode sub-driver initialization indicator.
* @ll_hw_enabled: Status of low-level hardware resources. * @ll_hw_enabled: Status of low-level hardware resources.
@@ -1054,6 +1055,7 @@ struct dwc2_hsotg {
struct dwc2_core_params params; struct dwc2_core_params params;
enum usb_otg_state op_state; enum usb_otg_state op_state;
enum usb_dr_mode dr_mode; enum usb_dr_mode dr_mode;
struct usb_role_switch *role_sw;
unsigned int hcd_enabled:1; unsigned int hcd_enabled:1;
unsigned int gadget_enabled:1; unsigned int gadget_enabled:1;
unsigned int ll_hw_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; 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 * 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); int dwc2_gadget_init(struct dwc2_hsotg *hsotg);
void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2, void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2,
bool reset); bool reset);
void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg);
void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg); void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg);
void dwc2_hsotg_disconnect(struct dwc2_hsotg *dwc2); void dwc2_hsotg_disconnect(struct dwc2_hsotg *dwc2);
int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode); 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; } { return 0; }
static inline void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2, static inline void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2,
bool reset) {} 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_core_connect(struct dwc2_hsotg *hsotg) {}
static inline void dwc2_hsotg_disconnect(struct dwc2_hsotg *dwc2) {} static inline void dwc2_hsotg_disconnect(struct dwc2_hsotg *dwc2) {}
static inline int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, static inline int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg,

180
drivers/usb/dwc2/drd.c Normal file
View 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);
}

View File

@@ -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) 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; int is_isoc = hs_ep->isochronous;
unsigned int maxsize; unsigned int maxsize;
u32 mps = hs_ep->ep.maxpacket;
int dir_in = hs_ep->dir_in;
if (is_isoc) if (is_isoc)
maxsize = (hs_ep->dir_in ? DEV_DMA_ISOC_TX_NBYTES_LIMIT : 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 else
maxsize = DEV_DMA_NBYTES_LIMIT * MAX_DMA_DESC_NUM_GENERIC; 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; 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, * Isochronous - descriptor rx/tx bytes bitfield limit,
* Control In/Bulk/Interrupt - multiple of mps. This will allow to not * Control In/Bulk/Interrupt - multiple of mps. This will allow to not
* have concatenations from various descriptors within one packet. * 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. * Selects corresponding mask for RX/TX bytes as well.
*/ */
static u32 dwc2_gadget_get_desc_params(struct dwc2_hsotg_ep *hs_ep, u32 *mask) 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; u32 mps = hs_ep->ep.maxpacket;
int dir_in = hs_ep->dir_in; int dir_in = hs_ep->dir_in;
u32 desc_size = 0; 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; 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; return desc_size;
} }
@@ -1123,13 +1141,7 @@ static void dwc2_hsotg_start_req(struct dwc2_hsotg *hsotg,
length += (mps - (length % mps)); length += (mps - (length % mps));
} }
/* if (continuing)
* 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)
offset = ureq->actual; offset = ureq->actual;
/* Fill DDMA chain entries */ /* 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) 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; struct dwc2_hsotg *hsotg = hs_ep->parent;
unsigned int bytes_rem = 0; unsigned int bytes_rem = 0;
unsigned int bytes_rem_correction = 0;
struct dwc2_dma_desc *desc = hs_ep->desc_list; struct dwc2_dma_desc *desc = hs_ep->desc_list;
int i; int i;
u32 status; u32 status;
u32 mps = hs_ep->ep.maxpacket;
int dir_in = hs_ep->dir_in;
if (!desc) if (!desc)
return -EINVAL; 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) { for (i = 0; i < hs_ep->desc_count; ++i) {
status = desc->status; status = desc->status;
bytes_rem += status & DEV_DMA_NBYTES_MASK; bytes_rem += status & DEV_DMA_NBYTES_MASK;
bytes_rem -= bytes_rem_correction;
if (status & DEV_DMA_STS_MASK) if (status & DEV_DMA_STS_MASK)
dev_err(hsotg->dev, "descriptor %d closed with %x\n", dev_err(hsotg->dev, "descriptor %d closed with %x\n",
i, status & DEV_DMA_STS_MASK); i, status & DEV_DMA_STS_MASK);
if (status & DEV_DMA_L)
break;
desc++; desc++;
} }
@@ -3530,7 +3556,7 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
dwc2_readl(hsotg, DOEPCTL0)); 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 */ /* set the soft-disconnect bit */
dwc2_set_bit(hsotg, DCTL, DCTL_SFTDISCON); dwc2_set_bit(hsotg, DCTL, DCTL_SFTDISCON);

View File

@@ -185,7 +185,7 @@ static void dwc2_set_stm32mp15_hsotg_params(struct dwc2_hsotg *hsotg)
struct dwc2_core_params *p = &hsotg->params; struct dwc2_core_params *p = &hsotg->params;
p->otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE; 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_rx_fifo_size = 440;
p->host_nperio_tx_fifo_size = 256; p->host_nperio_tx_fifo_size = 256;
p->host_perio_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", { .compatible = "amlogic,meson-g12a-usb",
.data = dwc2_set_amlogic_g12a_params }, .data = dwc2_set_amlogic_g12a_params },
{ .compatible = "amcc,dwc-otg", .data = dwc2_set_amcc_params }, { .compatible = "amcc,dwc-otg", .data = dwc2_set_amcc_params },
{ .compatible = "apm,apm82181-dwc-otg", .data = dwc2_set_amcc_params },
{ .compatible = "st,stm32f4x9-fsotg", { .compatible = "st,stm32f4x9-fsotg",
.data = dwc2_set_stm32f4x9_fsotg_params }, .data = dwc2_set_stm32f4x9_fsotg_params },
{ .compatible = "st,stm32f4x9-hsotg" }, { .compatible = "st,stm32f4x9-hsotg" },
@@ -860,7 +861,7 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
int dwc2_init_params(struct dwc2_hsotg *hsotg) int dwc2_init_params(struct dwc2_hsotg *hsotg)
{ {
const struct of_device_id *match; const struct of_device_id *match;
void (*set_params)(void *data); void (*set_params)(struct dwc2_hsotg *data);
dwc2_set_default_params(hsotg); dwc2_set_default_params(hsotg);
dwc2_get_device_properties(hsotg); dwc2_get_device_properties(hsotg);

View File

@@ -323,6 +323,8 @@ static int dwc2_driver_remove(struct platform_device *dev)
if (hsotg->gadget_enabled) if (hsotg->gadget_enabled)
dwc2_hsotg_remove(hsotg); dwc2_hsotg_remove(hsotg);
dwc2_drd_exit(hsotg);
if (hsotg->params.activate_stm_id_vb_detection) if (hsotg->params.activate_stm_id_vb_detection)
regulator_disable(hsotg->usb33d); regulator_disable(hsotg->usb33d);
@@ -542,10 +544,17 @@ static int dwc2_driver_probe(struct platform_device *dev)
dwc2_writel(hsotg, ggpio, GGPIO); 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) { if (hsotg->dr_mode != USB_DR_MODE_HOST) {
retval = dwc2_gadget_init(hsotg); retval = dwc2_gadget_init(hsotg);
if (retval) if (retval)
goto error_init; goto error_drd;
hsotg->gadget_enabled = 1; hsotg->gadget_enabled = 1;
} }
@@ -571,7 +580,7 @@ static int dwc2_driver_probe(struct platform_device *dev)
if (retval) { if (retval) {
if (hsotg->gadget_enabled) if (hsotg->gadget_enabled)
dwc2_hsotg_remove(hsotg); dwc2_hsotg_remove(hsotg);
goto error_init; goto error_drd;
} }
hsotg->hcd_enabled = 1; hsotg->hcd_enabled = 1;
} }
@@ -593,12 +602,19 @@ static int dwc2_driver_probe(struct platform_device *dev)
if (retval) { if (retval) {
hsotg->gadget.udc = NULL; hsotg->gadget.udc = NULL;
dwc2_hsotg_remove(hsotg); dwc2_hsotg_remove(hsotg);
goto error_init; goto error_debugfs;
} }
} }
#endif /* CONFIG_USB_DWC2_PERIPHERAL || CONFIG_USB_DWC2_DUAL_ROLE */ #endif /* CONFIG_USB_DWC2_PERIPHERAL || CONFIG_USB_DWC2_DUAL_ROLE */
return 0; return 0;
error_debugfs:
dwc2_debugfs_exit(hsotg);
if (hsotg->hcd_enabled)
dwc2_hcd_remove(hsotg);
error_drd:
dwc2_drd_exit(hsotg);
error_init: error_init:
if (hsotg->params.activate_stm_id_vb_detection) if (hsotg->params.activate_stm_id_vb_detection)
regulator_disable(hsotg->usb33d); regulator_disable(hsotg->usb33d);
@@ -617,6 +633,8 @@ static int __maybe_unused dwc2_suspend(struct device *dev)
if (is_device_mode) if (is_device_mode)
dwc2_hsotg_suspend(dwc2); dwc2_hsotg_suspend(dwc2);
dwc2_drd_suspend(dwc2);
if (dwc2->params.activate_stm_id_vb_detection) { if (dwc2->params.activate_stm_id_vb_detection) {
unsigned long flags; unsigned long flags;
u32 ggpio, gotgctl; u32 ggpio, gotgctl;
@@ -697,6 +715,8 @@ static int __maybe_unused dwc2_resume(struct device *dev)
/* Need to restore FORCEDEVMODE/FORCEHOSTMODE */ /* Need to restore FORCEDEVMODE/FORCEHOSTMODE */
dwc2_force_dr_mode(dwc2); dwc2_force_dr_mode(dwc2);
dwc2_drd_resume(dwc2);
if (dwc2_is_device_mode(dwc2)) if (dwc2_is_device_mode(dwc2))
ret = dwc2_hsotg_resume(dwc2); ret = dwc2_hsotg_resume(dwc2);

View File

@@ -119,9 +119,7 @@ static void __dwc3_set_mode(struct work_struct *work)
struct dwc3 *dwc = work_to_dwc(work); struct dwc3 *dwc = work_to_dwc(work);
unsigned long flags; unsigned long flags;
int ret; int ret;
u32 reg;
if (dwc->dr_mode != USB_DR_MODE_OTG)
return;
pm_runtime_get_sync(dwc->dev); 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); otg_set_vbus(dwc->usb2_phy->otg, true);
phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST); phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST);
phy_set_mode(dwc->usb3_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; break;
case DWC3_GCTL_PRTCAP_DEVICE: case DWC3_GCTL_PRTCAP_DEVICE:
@@ -203,6 +206,9 @@ void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
{ {
unsigned long flags; unsigned long flags;
if (dwc->dr_mode != USB_DR_MODE_OTG)
return;
spin_lock_irqsave(&dwc->lock, flags); spin_lock_irqsave(&dwc->lock, flags);
dwc->desired_dr_role = mode; dwc->desired_dr_role = mode;
spin_unlock_irqrestore(&dwc->lock, flags); 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); 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); ret = dwc3_phy_setup(dwc);
if (ret) if (ret)
goto err0; goto err0;
@@ -1356,6 +1355,9 @@ static void dwc3_get_properties(struct dwc3 *dwc)
dwc->dis_metastability_quirk = device_property_read_bool(dev, dwc->dis_metastability_quirk = device_property_read_bool(dev,
"snps,dis_metastability_quirk"); "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->lpm_nyet_threshold = lpm_nyet_threshold;
dwc->tx_de_emphasis = tx_de_emphasis; 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) static void dwc3_check_params(struct dwc3 *dwc)
{ {
struct device *dev = dwc->dev; struct device *dev = dwc->dev;
unsigned int hwparam_gen =
DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3);
/* Check for proper value of imod_interval */ /* Check for proper value of imod_interval */
if (dwc->imod_interval && !dwc3_has_imod(dwc)) { 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_LOW:
case USB_SPEED_FULL: case USB_SPEED_FULL:
case USB_SPEED_HIGH: case USB_SPEED_HIGH:
break;
case USB_SPEED_SUPER: 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: 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; break;
default: default:
dev_err(dev, "invalid maximum_speed parameter %d\n", dev_err(dev, "invalid maximum_speed parameter %d\n",
dwc->maximum_speed); dwc->maximum_speed);
fallthrough; fallthrough;
case USB_SPEED_UNKNOWN: case USB_SPEED_UNKNOWN:
/* default to superspeed */ switch (hwparam_gen) {
dwc->maximum_speed = USB_SPEED_SUPER; case DWC3_GHWPARAMS3_SSPHY_IFC_GEN2:
/*
* 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))
dwc->maximum_speed = USB_SPEED_SUPER_PLUS; 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; break;
} }
} }
@@ -1554,6 +1573,17 @@ static int dwc3_probe(struct platform_device *pdev)
err5: err5:
dwc3_event_buffers_cleanup(dwc); 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); dwc3_ulpi_exit(dwc);
err4: err4:
@@ -1589,9 +1619,9 @@ static int dwc3_remove(struct platform_device *pdev)
dwc3_core_exit(dwc); dwc3_core_exit(dwc);
dwc3_ulpi_exit(dwc); dwc3_ulpi_exit(dwc);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_allow(&pdev->dev);
pm_runtime_disable(&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_event_buffers(dwc);
dwc3_free_scratch_buffers(dwc); dwc3_free_scratch_buffers(dwc);
@@ -1865,10 +1895,26 @@ static int dwc3_resume(struct device *dev)
return 0; 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 */ #endif /* CONFIG_PM_SLEEP */
static const struct dev_pm_ops dwc3_dev_pm_ops = { static const struct dev_pm_ops dwc3_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(dwc3_suspend, dwc3_resume) SET_SYSTEM_SLEEP_PM_OPS(dwc3_suspend, dwc3_resume)
.complete = dwc3_complete,
SET_RUNTIME_PM_OPS(dwc3_runtime_suspend, dwc3_runtime_resume, SET_RUNTIME_PM_OPS(dwc3_runtime_suspend, dwc3_runtime_resume,
dwc3_runtime_idle) dwc3_runtime_idle)
}; };

View File

@@ -138,6 +138,7 @@
#define DWC3_GEVNTCOUNT(n) (0xc40c + ((n) * 0x10)) #define DWC3_GEVNTCOUNT(n) (0xc40c + ((n) * 0x10))
#define DWC3_GHWPARAMS8 0xc600 #define DWC3_GHWPARAMS8 0xc600
#define DWC3_GUCTL3 0xc60c
#define DWC3_GFLADJ 0xc630 #define DWC3_GFLADJ 0xc630
/* Device Registers */ /* Device Registers */
@@ -380,6 +381,9 @@
/* Global User Control Register 2 */ /* Global User Control Register 2 */
#define DWC3_GUCTL2_RST_ACTBITLATER BIT(14) #define DWC3_GUCTL2_RST_ACTBITLATER BIT(14)
/* Global User Control Register 3 */
#define DWC3_GUCTL3_SPLITDISABLE BIT(14)
/* Device Configuration Register */ /* Device Configuration Register */
#define DWC3_DCFG_DEVADDR(addr) ((addr) << 3) #define DWC3_DCFG_DEVADDR(addr) ((addr) << 3)
#define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f) #define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f)
@@ -634,7 +638,7 @@ struct dwc3_trb;
struct dwc3_event_buffer { struct dwc3_event_buffer {
void *buf; void *buf;
void *cache; void *cache;
unsigned length; unsigned int length;
unsigned int lpos; unsigned int lpos;
unsigned int count; unsigned int count;
unsigned int flags; unsigned int flags;
@@ -694,7 +698,7 @@ struct dwc3_ep {
struct dwc3 *dwc; struct dwc3 *dwc;
u32 saved_state; u32 saved_state;
unsigned flags; unsigned int flags;
#define DWC3_EP_ENABLED BIT(0) #define DWC3_EP_ENABLED BIT(0)
#define DWC3_EP_STALL BIT(1) #define DWC3_EP_STALL BIT(1)
#define DWC3_EP_WEDGE BIT(2) #define DWC3_EP_WEDGE BIT(2)
@@ -706,6 +710,7 @@ struct dwc3_ep {
#define DWC3_EP_IGNORE_NEXT_NOSTREAM BIT(8) #define DWC3_EP_IGNORE_NEXT_NOSTREAM BIT(8)
#define DWC3_EP_FORCE_RESTART_STREAM BIT(9) #define DWC3_EP_FORCE_RESTART_STREAM BIT(9)
#define DWC3_EP_FIRST_STREAM_PRIMED BIT(10) #define DWC3_EP_FIRST_STREAM_PRIMED BIT(10)
#define DWC3_EP_PENDING_CLEAR_STALL BIT(11)
/* This last one is specific to EP0 */ /* This last one is specific to EP0 */
#define DWC3_EP0_DIR_IN BIT(31) #define DWC3_EP0_DIR_IN BIT(31)
@@ -893,9 +898,9 @@ struct dwc3_request {
struct scatterlist *sg; struct scatterlist *sg;
struct scatterlist *start_sg; struct scatterlist *start_sg;
unsigned num_pending_sgs; unsigned int num_pending_sgs;
unsigned int num_queued_sgs; unsigned int num_queued_sgs;
unsigned remaining; unsigned int remaining;
unsigned int status; unsigned int status;
#define DWC3_REQUEST_STATUS_QUEUED 0 #define DWC3_REQUEST_STATUS_QUEUED 0
@@ -908,11 +913,11 @@ struct dwc3_request {
struct dwc3_trb *trb; struct dwc3_trb *trb;
dma_addr_t trb_dma; dma_addr_t trb_dma;
unsigned num_trbs; unsigned int num_trbs;
unsigned needs_extra_trb:1; unsigned int needs_extra_trb:1;
unsigned direction:1; unsigned int direction:1;
unsigned mapped: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 * @has_lpm_erratum: true when core was configured with LPM Erratum. Note that
* there's now way for software to detect this in runtime. * there's now way for software to detect this in runtime.
* @is_utmi_l1_suspend: the core asserts output signal * @is_utmi_l1_suspend: the core asserts output signal
* 0 - utmi_sleep_n * 0 - utmi_sleep_n
* 1 - utmi_l1_suspend_n * 1 - utmi_l1_suspend_n
* @is_fpga: true when we are using the FPGA board * @is_fpga: true when we are using the FPGA board
* @pending_events: true when we have pending IRQs to be handled * @pending_events: true when we have pending IRQs to be handled
* @pullups_connected: true when Run/Stop bit is set * @pullups_connected: true when Run/Stop bit is set
@@ -1047,13 +1052,14 @@ struct dwc3_scratchpad_array {
* instances in park mode. * instances in park mode.
* @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk * @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk
* @tx_de_emphasis: Tx de-emphasis value * @tx_de_emphasis: Tx de-emphasis value
* 0 - -6dB de-emphasis * 0 - -6dB de-emphasis
* 1 - -3.5dB de-emphasis * 1 - -3.5dB de-emphasis
* 2 - No de-emphasis * 2 - No de-emphasis
* 3 - Reserved * 3 - Reserved
* @dis_metastability_quirk: set to disable metastability quirk. * @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 * @imod_interval: set the interrupt moderation interval in 250ns
* increments or 0 to disable. * increments or 0 to disable.
*/ */
struct dwc3 { struct dwc3 {
struct work_struct drd_work; struct work_struct drd_work;
@@ -1079,7 +1085,7 @@ struct dwc3 {
struct dwc3_event_buffer *ev_buf; struct dwc3_event_buffer *ev_buf;
struct dwc3_ep *eps[DWC3_ENDPOINTS_NUM]; struct dwc3_ep *eps[DWC3_ENDPOINTS_NUM];
struct usb_gadget gadget; struct usb_gadget *gadget;
struct usb_gadget_driver *gadget_driver; struct usb_gadget_driver *gadget_driver;
struct clk_bulk_data *clks; struct clk_bulk_data *clks;
@@ -1245,6 +1251,8 @@ struct dwc3 {
unsigned dis_metastability_quirk:1; unsigned dis_metastability_quirk:1;
unsigned dis_split_quirk:1;
u16 imod_interval; 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_set_test_mode(struct dwc3 *dwc, int mode);
int dwc3_gadget_get_link_state(struct dwc3 *dwc); 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_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); 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 #else
static inline int dwc3_gadget_init(struct dwc3 *dwc) static inline int dwc3_gadget_init(struct dwc3 *dwc)
{ return 0; } { return 0; }
@@ -1472,7 +1481,7 @@ static inline int dwc3_gadget_set_link_state(struct dwc3 *dwc,
enum dwc3_link_state state) enum dwc3_link_state state)
{ return 0; } { 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) struct dwc3_gadget_ep_cmd_params *params)
{ return 0; } { return 0; }
static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc, static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,

View File

@@ -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, static inline const char *dwc3_decode_event(char *str, size_t size, u32 event,
u32 ep0state) 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) if (evt.type.is_devspec)
return dwc3_gadget_event_string(str, size, &evt.devt); 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 #ifdef CONFIG_DEBUG_FS
extern void dwc3_debugfs_init(struct dwc3 *); extern void dwc3_debugfs_init(struct dwc3 *d);
extern void dwc3_debugfs_exit(struct dwc3 *); extern void dwc3_debugfs_exit(struct dwc3 *d);
#else #else
static inline void dwc3_debugfs_init(struct dwc3 *d) static inline void dwc3_debugfs_init(struct dwc3 *d)
{ } { }

View File

@@ -397,13 +397,13 @@ static int dwc3_mode_show(struct seq_file *s, void *unused)
switch (DWC3_GCTL_PRTCAP(reg)) { switch (DWC3_GCTL_PRTCAP(reg)) {
case DWC3_GCTL_PRTCAP_HOST: case DWC3_GCTL_PRTCAP_HOST:
seq_printf(s, "host\n"); seq_puts(s, "host\n");
break; break;
case DWC3_GCTL_PRTCAP_DEVICE: case DWC3_GCTL_PRTCAP_DEVICE:
seq_printf(s, "device\n"); seq_puts(s, "device\n");
break; break;
case DWC3_GCTL_PRTCAP_OTG: case DWC3_GCTL_PRTCAP_OTG:
seq_printf(s, "otg\n"); seq_puts(s, "otg\n");
break; break;
default: default:
seq_printf(s, "UNKNOWN %08x\n", DWC3_GCTL_PRTCAP(reg)); 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))) if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT; return -EFAULT;
if (dwc->dr_mode != USB_DR_MODE_OTG)
return count;
if (!strncmp(buf, "host", 4)) if (!strncmp(buf, "host", 4))
mode = DWC3_GCTL_PRTCAP_HOST; mode = DWC3_GCTL_PRTCAP_HOST;
@@ -464,22 +467,22 @@ static int dwc3_testmode_show(struct seq_file *s, void *unused)
switch (reg) { switch (reg) {
case 0: case 0:
seq_printf(s, "no test\n"); seq_puts(s, "no test\n");
break; break;
case USB_TEST_J: case USB_TEST_J:
seq_printf(s, "test_j\n"); seq_puts(s, "test_j\n");
break; break;
case USB_TEST_K: case USB_TEST_K:
seq_printf(s, "test_k\n"); seq_puts(s, "test_k\n");
break; break;
case USB_TEST_SE0_NAK: case USB_TEST_SE0_NAK:
seq_printf(s, "test_se0_nak\n"); seq_puts(s, "test_se0_nak\n");
break; break;
case USB_TEST_PACKET: case USB_TEST_PACKET:
seq_printf(s, "test_packet\n"); seq_puts(s, "test_packet\n");
break; break;
case USB_TEST_FORCE_ENABLE: case USB_TEST_FORCE_ENABLE:
seq_printf(s, "test_force_enable\n"); seq_puts(s, "test_force_enable\n");
break; break;
default: default:
seq_printf(s, "UNKNOWN %d\n", reg); 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; unsigned long flags;
spin_lock_irqsave(&dwc->lock, flags); spin_lock_irqsave(&dwc->lock, flags);
if (!(dep->flags & DWC3_EP_ENABLED) || if (!(dep->flags & DWC3_EP_ENABLED) || !dep->endpoint.desc) {
!dep->endpoint.desc) { seq_puts(s, "--\n");
seq_printf(s, "--\n");
goto out; goto out;
} }
switch (usb_endpoint_type(dep->endpoint.desc)) { switch (usb_endpoint_type(dep->endpoint.desc)) {
case USB_ENDPOINT_XFER_CONTROL: case USB_ENDPOINT_XFER_CONTROL:
seq_printf(s, "control\n"); seq_puts(s, "control\n");
break; break;
case USB_ENDPOINT_XFER_ISOC: case USB_ENDPOINT_XFER_ISOC:
seq_printf(s, "isochronous\n"); seq_puts(s, "isochronous\n");
break; break;
case USB_ENDPOINT_XFER_BULK: case USB_ENDPOINT_XFER_BULK:
seq_printf(s, "bulk\n"); seq_puts(s, "bulk\n");
break; break;
case USB_ENDPOINT_XFER_INT: case USB_ENDPOINT_XFER_INT:
seq_printf(s, "interrupt\n"); seq_puts(s, "interrupt\n");
break; break;
default: default:
seq_printf(s, "--\n"); seq_puts(s, "--\n");
} }
out: out:
@@ -798,11 +800,11 @@ static int dwc3_trb_ring_show(struct seq_file *s, void *unused)
spin_lock_irqsave(&dwc->lock, flags); spin_lock_irqsave(&dwc->lock, flags);
if (dep->number <= 1) { if (dep->number <= 1) {
seq_printf(s, "--\n"); seq_puts(s, "--\n");
goto out; 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++) { for (i = 0; i < DWC3_TRB_NUM; i++) {
struct dwc3_trb *trb = &dep->trb_pool[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 struct file_operations *fops = dwc3_ep_file_map[i].fops;
const char *name = dwc3_ep_file_map[i].name; 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); root = debugfs_create_dir(dev_name(dwc->dev), usb_debug_root);
dwc->root = 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, if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE))
&dwc3_lsp_fops); debugfs_create_file("mode", 0644, root, dwc,
if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)) {
debugfs_create_file("mode", S_IRUGO | S_IWUSR, root, dwc,
&dwc3_mode_fops); &dwc3_mode_fops);
}
if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) || if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) ||
IS_ENABLED(CONFIG_USB_DWC3_GADGET)) { IS_ENABLED(CONFIG_USB_DWC3_GADGET)) {
debugfs_create_file("testmode", S_IRUGO | S_IWUSR, root, dwc, debugfs_create_file("testmode", 0644, root, dwc,
&dwc3_testmode_fops); &dwc3_testmode_fops);
debugfs_create_file("link_state", S_IRUGO | S_IWUSR, root, dwc, debugfs_create_file("link_state", 0644, root, dwc,
&dwc3_link_state_fops); &dwc3_link_state_fops);
dwc3_debugfs_create_endpoint_dirs(dwc, root); dwc3_debugfs_create_endpoint_dirs(dwc, root);
} }

View File

@@ -116,23 +116,24 @@ static struct clk_bulk_data meson_a1_clocks[] = {
{ .id = "xtal_usb_ctrl" }, { .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", "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", "usb2-phy0", "usb2-phy1", "usb3-phy0",
}; };
/* /*
* Amlogic A1 has a single physical PHY, in slot 1, but still has the * Amlogic A1 has a single physical PHY, in slot 1, but still has the
* two U2 PHY controls register blocks like G12A. * 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 * 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 * changes, and the current management is generic enough to handle it
* correctly when only the "usb2-phy1" phy is specified on-par with the * correctly when only the "usb2-phy1" phy is specified on-par with the
* DT bindings. * DT bindings.
*/ */
static const char *meson_a1_phy_names[] = { static const char * const meson_a1_phy_names[] = {
"usb2-phy0", "usb2-phy1" "usb2-phy0", "usb2-phy1"
}; };
@@ -143,7 +144,7 @@ struct dwc3_meson_g12a_drvdata {
bool otg_phy_host_port_disable; bool otg_phy_host_port_disable;
struct clk_bulk_data *clks; struct clk_bulk_data *clks;
int num_clks; int num_clks;
const char **phy_names; const char * const *phy_names;
int num_phys; int num_phys;
int (*setup_regmaps)(struct dwc3_meson_g12a *priv, void __iomem *base); int (*setup_regmaps)(struct dwc3_meson_g12a *priv, void __iomem *base);
int (*usb2_init_phy)(struct dwc3_meson_g12a *priv, int i, 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, .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 = { static struct dwc3_meson_g12a_drvdata g12a_drvdata = {
.otg_switch_supported = true, .otg_switch_supported = true,
.clks = meson_g12a_clocks, .clks = meson_g12a_clocks,
@@ -520,11 +534,7 @@ static int dwc3_meson_g12a_role_set(struct usb_role_switch *sw,
return 0; return 0;
if (priv->drvdata->otg_phy_host_port_disable) if (priv->drvdata->otg_phy_host_port_disable)
dev_warn_once(priv->dev, "Manual OTG switch is broken on this "\ dev_warn_once(priv->dev, "Broken manual OTG switch\n");
"SoC, when manual switching from "\
"Host to device, DWC3 controller "\
"will need to be resetted in order "\
"to recover usage of the Host port");
return dwc3_meson_g12a_otg_mode_set(priv, mode); 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 */ /* GXL controls the PHY mode in the PHY registers unlike G12A */
priv->usb_glue_regmap = devm_regmap_init_mmio(priv->dev, base, priv->usb_glue_regmap = devm_regmap_init_mmio(priv->dev, base,
&phy_meson_g12a_usb_glue_regmap_conf); &phy_meson_g12a_usb_glue_regmap_conf);
if (IS_ERR(priv->usb_glue_regmap)) return PTR_ERR_OR_ZERO(priv->usb_glue_regmap);
return PTR_ERR(priv->usb_glue_regmap);
return 0;
} }
static int dwc3_meson_g12a_setup_regmaps(struct dwc3_meson_g12a *priv, 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; return ret;
} }
if (priv->vbus && priv->otg_phy_mode == PHY_MODE_USB_HOST) { if (priv->vbus && priv->otg_phy_mode == PHY_MODE_USB_HOST) {
ret = regulator_enable(priv->vbus); ret = regulator_enable(priv->vbus);
if (ret) if (ret)
return ret; return ret;
} }
@@ -930,6 +937,10 @@ static const struct of_device_id dwc3_meson_g12a_match[] = {
.compatible = "amlogic,meson-gxm-usb-ctrl", .compatible = "amlogic,meson-gxm-usb-ctrl",
.data = &gxm_drvdata, .data = &gxm_drvdata,
}, },
{
.compatible = "amlogic,meson-axg-usb-ctrl",
.data = &axg_drvdata,
},
{ {
.compatible = "amlogic,meson-g12a-usb-ctrl", .compatible = "amlogic,meson-g12a-usb-ctrl",
.data = &g12a_drvdata, .data = &g12a_drvdata,

View File

@@ -176,6 +176,8 @@ static const struct of_device_id of_dwc3_simple_match[] = {
{ .compatible = "cavium,octeon-7130-usb-uctl" }, { .compatible = "cavium,octeon-7130-usb-uctl" },
{ .compatible = "sprd,sc9860-dwc3" }, { .compatible = "sprd,sc9860-dwc3" },
{ .compatible = "allwinner,sun50i-h6-dwc3" }, { .compatible = "allwinner,sun50i-h6-dwc3" },
{ .compatible = "hisilicon,hi3670-dwc3" },
{ .compatible = "intel,keembay-dwc3" },
{ /* Sentinel */ } { /* Sentinel */ }
}; };
MODULE_DEVICE_TABLE(of, of_dwc3_simple_match); MODULE_DEVICE_TABLE(of, of_dwc3_simple_match);

View File

@@ -147,7 +147,8 @@ static int dwc3_pci_quirks(struct dwc3_pci *dwc)
if (pdev->vendor == PCI_VENDOR_ID_INTEL) { if (pdev->vendor == PCI_VENDOR_ID_INTEL) {
if (pdev->device == PCI_DEVICE_ID_INTEL_BXT || 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); guid_parse(PCI_INTEL_BXT_DSM_GUID, &dwc->guid);
dwc->has_dsm_for_pm = true; dwc->has_dsm_for_pm = true;
} }

View File

@@ -13,6 +13,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/extcon.h> #include <linux/extcon.h>
#include <linux/interconnect.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/phy/phy.h> #include <linux/phy/phy.h>
@@ -43,6 +44,14 @@
#define SDM845_QSCRATCH_SIZE 0x400 #define SDM845_QSCRATCH_SIZE 0x400
#define SDM845_DWC3_CORE_SIZE 0xcd00 #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 { struct dwc3_acpi_pdata {
u32 qscratch_base_offset; u32 qscratch_base_offset;
u32 qscratch_base_size; u32 qscratch_base_size;
@@ -76,6 +85,8 @@ struct dwc3_qcom {
enum usb_dr_mode mode; enum usb_dr_mode mode;
bool is_suspended; bool is_suspended;
bool pm_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) 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; 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) static void dwc3_qcom_disable_interrupts(struct dwc3_qcom *qcom)
{ {
if (qcom->hs_phy_irq) { 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) static int dwc3_qcom_suspend(struct dwc3_qcom *qcom)
{ {
u32 val; u32 val;
int i; int i, ret;
if (qcom->is_suspended) if (qcom->is_suspended)
return 0; return 0;
@@ -251,6 +352,10 @@ static int dwc3_qcom_suspend(struct dwc3_qcom *qcom)
for (i = qcom->num_clocks - 1; i >= 0; i--) for (i = qcom->num_clocks - 1; i >= 0; i--)
clk_disable_unprepare(qcom->clks[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; qcom->is_suspended = true;
dwc3_qcom_enable_interrupts(qcom); 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 */ /* Clear existing events from PHY related to L2 in/out */
dwc3_qcom_setbits(qcom->qscratch_base, PWR_EVNT_IRQ_STAT_REG, dwc3_qcom_setbits(qcom->qscratch_base, PWR_EVNT_IRQ_STAT_REG,
PWR_EVNT_LPM_IN_L2_MASK | PWR_EVNT_LPM_OUT_L2_MASK); 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); struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
const struct dwc3_acpi_pdata *pdata = qcom->acpi_pdata; 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", irq = dwc3_qcom_get_irq(pdev, "hs_phy_irq",
pdata ? pdata->hs_phy_irq_index : -1); pdata ? pdata->hs_phy_irq_index : -1);
if (irq > 0) { 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) 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 device *dev = &pdev->dev;
struct resource *res, *child_res = NULL; struct resource *res, *child_res = NULL;
int irq; int irq;
@@ -514,7 +625,7 @@ out:
static int dwc3_qcom_of_register_core(struct platform_device *pdev) 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_node *np = pdev->dev.of_node, *dwc3_np;
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
int ret; int ret;
@@ -638,6 +749,10 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
goto depopulate; goto depopulate;
} }
ret = dwc3_qcom_interconnect_init(qcom);
if (ret)
goto depopulate;
qcom->mode = usb_get_dr_mode(&qcom->dwc3->dev); qcom->mode = usb_get_dr_mode(&qcom->dwc3->dev);
/* enable vbus override for device mode */ /* 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 */ /* register extcon to override sw_vbus on Vbus change later */
ret = dwc3_qcom_register_extcon(qcom); ret = dwc3_qcom_register_extcon(qcom);
if (ret) if (ret)
goto depopulate; goto interconnect_exit;
device_init_wakeup(&pdev->dev, 1); device_init_wakeup(&pdev->dev, 1);
qcom->is_suspended = false; qcom->is_suspended = false;
@@ -657,6 +772,8 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
return 0; return 0;
interconnect_exit:
dwc3_qcom_interconnect_exit(qcom);
depopulate: depopulate:
if (np) if (np)
of_platform_depopulate(&pdev->dev); of_platform_depopulate(&pdev->dev);
@@ -687,6 +804,7 @@ static int dwc3_qcom_remove(struct platform_device *pdev)
} }
qcom->num_clocks = 0; qcom->num_clocks = 0;
dwc3_qcom_interconnect_exit(qcom);
reset_control_assert(qcom->resets); reset_control_assert(qcom->resets);
pm_runtime_allow(dev); pm_runtime_allow(dev);

View File

@@ -105,7 +105,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
* IRQ we were waiting for is long gone. * IRQ we were waiting for is long gone.
*/ */
if (dep->flags & DWC3_EP_PENDING_REQUEST) { if (dep->flags & DWC3_EP_PENDING_REQUEST) {
unsigned direction; unsigned int direction;
direction = !!(dep->flags & DWC3_EP0_DIR_IN); direction = !!(dep->flags & DWC3_EP0_DIR_IN);
@@ -127,11 +127,11 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
* handle it here. * handle it here.
*/ */
if (dwc->delayed_status) { if (dwc->delayed_status) {
unsigned direction; unsigned int direction;
direction = !dwc->ep0_expect_in; direction = !dwc->ep0_expect_in;
dwc->delayed_status = false; 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) if (dwc->ep0state == EP0_STATUS_PHASE)
__dwc3_ep0_do_control_status(dwc, dwc->eps[direction]); __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). * XferNotReady(STATUS).
*/ */
if (dwc->three_stage_setup) { if (dwc->three_stage_setup) {
unsigned direction; unsigned int direction;
direction = dwc->ep0_expect_in; direction = dwc->ep0_expect_in;
dwc->ep0state = EP0_DATA_PHASE; dwc->ep0state = EP0_DATA_PHASE;
@@ -197,7 +197,7 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
int ret; int ret;
spin_lock_irqsave(&dwc->lock, flags); 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", dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n",
dep->name); dep->name);
ret = -ESHUTDOWN; 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. * 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) || if ((dwc->speed == DWC3_DSTS_SUPERSPEED) ||
(dwc->speed == DWC3_DSTS_SUPERSPEED_PLUS)) { (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); wValue = le16_to_cpu(ctrl->wValue);
wIndex = le16_to_cpu(ctrl->wIndex); wIndex = le16_to_cpu(ctrl->wIndex);
state = dwc->gadget.state; state = dwc->gadget->state;
switch (wValue) { switch (wValue) {
case USB_DEVICE_REMOTE_WAKEUP: 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); ret = __dwc3_gadget_ep_set_halt(dep, set, true);
if (ret) if (ret)
return -EINVAL; return -EINVAL;
/* ClearFeature(Halt) may need delayed status */
if (!set && (dep->flags & DWC3_EP_END_TRANSFER_PENDING))
return USB_GADGET_DELAYED_STATUS;
break; break;
default: default:
return -EINVAL; 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) 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 addr;
u32 reg; 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); dwc3_writel(dwc->regs, DWC3_DCFG, reg);
if (addr) if (addr)
usb_gadget_set_state(&dwc->gadget, USB_STATE_ADDRESS); usb_gadget_set_state(dwc->gadget, USB_STATE_ADDRESS);
else else
usb_gadget_set_state(&dwc->gadget, USB_STATE_DEFAULT); usb_gadget_set_state(dwc->gadget, USB_STATE_DEFAULT);
return 0; return 0;
} }
@@ -592,14 +597,14 @@ static int dwc3_ep0_delegate_req(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
int ret; int ret;
spin_unlock(&dwc->lock); spin_unlock(&dwc->lock);
ret = dwc->gadget_driver->setup(&dwc->gadget, ctrl); ret = dwc->gadget_driver->setup(dwc->gadget, ctrl);
spin_lock(&dwc->lock); spin_lock(&dwc->lock);
return ret; return ret;
} }
static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) 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; u32 cfg;
int ret; int ret;
u32 reg; 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() * to change the state on the next usb_ep_queue()
*/ */
if (ret == 0) if (ret == 0)
usb_gadget_set_state(&dwc->gadget, usb_gadget_set_state(dwc->gadget,
USB_STATE_CONFIGURED); USB_STATE_CONFIGURED);
/* /*
@@ -641,7 +646,7 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
case USB_STATE_CONFIGURED: case USB_STATE_CONFIGURED:
ret = dwc3_ep0_delegate_req(dwc, ctrl); ret = dwc3_ep0_delegate_req(dwc, ctrl);
if (!cfg && !ret) if (!cfg && !ret)
usb_gadget_set_state(&dwc->gadget, usb_gadget_set_state(dwc->gadget,
USB_STATE_ADDRESS); USB_STATE_ADDRESS);
break; break;
default: 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) static int dwc3_ep0_set_sel(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
{ {
struct dwc3_ep *dep; struct dwc3_ep *dep;
enum usb_device_state state = dwc->gadget.state; enum usb_device_state state = dwc->gadget->state;
u16 wLength; u16 wLength;
if (state == USB_STATE_DEFAULT) 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) if (wIndex || wLength)
return -EINVAL; return -EINVAL;
dwc->gadget.isoch_delay = wValue; dwc->gadget->isoch_delay = wValue;
return 0; 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, static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
struct dwc3_ep *dep, struct dwc3_request *req) struct dwc3_ep *dep, struct dwc3_request *req)
{ {
unsigned int trb_length = 0;
int ret; int ret;
req->direction = !!dep->number; req->direction = !!dep->number;
if (req->request.length == 0) { 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); DWC3_TRBCTL_CONTROL_DATA, false);
ret = dwc3_ep0_start_trans(dep); ret = dwc3_ep0_start_trans(dep);
} else if (!IS_ALIGNED(req->request.length, dep->endpoint.maxpacket) } 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]; 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 */ /* Now prepare one extra TRB to align transfer size */
dwc3_ep0_prepare_one_trb(dep, dwc->bounce_addr, dwc3_ep0_prepare_one_trb(dep, dwc->bounce_addr,
0, DWC3_TRBCTL_CONTROL_DATA, trb_length, DWC3_TRBCTL_CONTROL_DATA,
false); false);
ret = dwc3_ep0_start_trans(dep); ret = dwc3_ep0_start_trans(dep);
} else { } else {
@@ -1042,6 +1054,17 @@ static void dwc3_ep0_do_control_status(struct dwc3 *dwc,
__dwc3_ep0_do_control_status(dwc, dep); __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) static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
{ {
struct dwc3_gadget_ep_cmd_params params; 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)) { if (!list_empty(&dep->pending_list)) {
dwc->delayed_status = false; dwc->delayed_status = false;
usb_gadget_set_state(&dwc->gadget, usb_gadget_set_state(dwc->gadget,
USB_STATE_CONFIGURED); USB_STATE_CONFIGURED);
dwc3_ep0_do_control_status(dwc, event); dwc3_ep0_do_control_status(dwc, event);
} }

File diff suppressed because it is too large Load Diff

View File

@@ -17,7 +17,7 @@
struct dwc3; struct dwc3;
#define to_dwc3_ep(ep) (container_of(ep, struct dwc3_ep, endpoint)) #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 */ /* DEPCFG parameter 1 */
#define DWC3_DEPCFG_INT_NUM(n) (((n) & 0x1f) << 0) #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, int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
gfp_t gfp_flags); gfp_t gfp_flags);
int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol); 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 * dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW

View File

@@ -104,8 +104,8 @@ DECLARE_EVENT_CLASS(dwc3_log_request,
TP_STRUCT__entry( TP_STRUCT__entry(
__string(name, req->dep->name) __string(name, req->dep->name)
__field(struct dwc3_request *, req) __field(struct dwc3_request *, req)
__field(unsigned, actual) __field(unsigned int, actual)
__field(unsigned, length) __field(unsigned int, length)
__field(int, status) __field(int, status)
__field(int, zero) __field(int, zero)
__field(int, short_not_ok) __field(int, short_not_ok)
@@ -246,6 +246,7 @@ DECLARE_EVENT_CLASS(dwc3_log_trb,
__entry->dequeue, __entry->bph, __entry->bpl, __entry->dequeue, __entry->bph, __entry->bpl,
({char *s; ({char *s;
int pcm = ((__entry->size >> 24) & 3) + 1; int pcm = ((__entry->size >> 24) & 3) + 1;
switch (__entry->type) { switch (__entry->type) {
case USB_ENDPOINT_XFER_INT: case USB_ENDPOINT_XFER_INT:
case USB_ENDPOINT_XFER_ISOC: case USB_ENDPOINT_XFER_ISOC:
@@ -291,12 +292,12 @@ DECLARE_EVENT_CLASS(dwc3_log_ep,
TP_ARGS(dep), TP_ARGS(dep),
TP_STRUCT__entry( TP_STRUCT__entry(
__string(name, dep->name) __string(name, dep->name)
__field(unsigned, maxpacket) __field(unsigned int, maxpacket)
__field(unsigned, maxpacket_limit) __field(unsigned int, maxpacket_limit)
__field(unsigned, max_streams) __field(unsigned int, max_streams)
__field(unsigned, maxburst) __field(unsigned int, maxburst)
__field(unsigned, flags) __field(unsigned int, flags)
__field(unsigned, direction) __field(unsigned int, direction)
__field(u8, trb_enqueue) __field(u8, trb_enqueue)
__field(u8, trb_dequeue) __field(u8, trb_dequeue)
), ),

View File

@@ -19,7 +19,7 @@
static int dwc3_ulpi_busyloop(struct dwc3 *dwc) static int dwc3_ulpi_busyloop(struct dwc3 *dwc)
{ {
unsigned count = 1000; unsigned int count = 1000;
u32 reg; u32 reg;
while (count--) { while (count--) {

View File

@@ -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 */ /* we know alt == 0, so this is an activation or a reset */
if (intf == acm->ctrl_id) { if (intf == acm->ctrl_id) {
dev_vdbg(&cdev->gadget->dev, if (acm->notify->enabled) {
"reset acm control interface %d\n", intf); dev_vdbg(&cdev->gadget->dev,
usb_ep_disable(acm->notify); "reset acm control interface %d\n", intf);
usb_ep_disable(acm->notify);
}
if (!acm->notify->desc) if (!acm->notify->desc)
if (config_ep_by_speed(cdev->gadget, f, acm->notify)) if (config_ep_by_speed(cdev->gadget, f, acm->notify))

View File

@@ -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 */ /* peak (theoretical) bulk transfer rate in bits-per-second */
static inline unsigned ncm_bitrate(struct usb_gadget *g) static inline unsigned ncm_bitrate(struct usb_gadget *g)
{ {
if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER) if (gadget_is_superspeed(g) && g->speed >= USB_SPEED_SUPER_PLUS)
return 13 * 1024 * 8 * 1000 * 8; 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) else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
return 13 * 512 * 8 * 1000 * 8; return 13 * 512 * 8 * 1000 * 8;
else else
@@ -376,7 +378,7 @@ static struct usb_ss_ep_comp_descriptor ss_ncm_bulk_comp_desc = {
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP, .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
/* the following 2 values can be tweaked if necessary */ /* the following 2 values can be tweaked if necessary */
/* .bMaxBurst = 0, */ .bMaxBurst = 15,
/* .bmAttributes = 0, */ /* .bmAttributes = 0, */
}; };
@@ -1189,7 +1191,6 @@ static int ncm_unwrap_ntb(struct gether *port,
const struct ndp_parser_opts *opts = ncm->parser_opts; const struct ndp_parser_opts *opts = ncm->parser_opts;
unsigned crc_len = ncm->is_crc ? sizeof(uint32_t) : 0; unsigned crc_len = ncm->is_crc ? sizeof(uint32_t) : 0;
int dgram_counter; int dgram_counter;
bool ndp_after_header;
/* dwSignature */ /* dwSignature */
if (get_unaligned_le32(tmp) != opts->nth_sign) { 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_index = get_ncm(&tmp, opts->ndp_index);
ndp_after_header = false;
/* Run through all the NDP's in the NTB */ /* Run through all the NDP's in the NTB */
do { do {
@@ -1232,8 +1232,6 @@ static int ncm_unwrap_ntb(struct gether *port,
ndp_index); ndp_index);
goto err; goto err;
} }
if (ndp_index == opts->nth_size)
ndp_after_header = true;
/* /*
* walk through NDP * walk through NDP
@@ -1312,37 +1310,13 @@ static int ncm_unwrap_ntb(struct gether *port,
index2 = get_ncm(&tmp, opts->dgram_item_len); index2 = get_ncm(&tmp, opts->dgram_item_len);
dg_len2 = 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] */ /* 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) { if (index2 > block_len - opts->dpe_size) {
INFO(port->func.config->cdev, INFO(port->func.config->cdev,
"Bad index: %#X\n", index2); "Bad index: %#X\n", index2);
goto err; 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. * Copy the data into a new skb.
* This ensures the truesize is correct * 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); ndp_len -= 2 * (opts->dgram_item_len * 2);
dgram_counter++; dgram_counter++;
if (index2 == 0 || dg_len2 == 0)
break;
} while (ndp_len > 2 * (opts->dgram_item_len * 2)); } while (ndp_len > 2 * (opts->dgram_item_len * 2));
} while (ndp_index); } while (ndp_index);
@@ -1560,7 +1536,7 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
fs_ncm_notify_desc.bEndpointAddress; fs_ncm_notify_desc.bEndpointAddress;
status = usb_assign_descriptors(f, ncm_fs_function, ncm_hs_function, status = usb_assign_descriptors(f, ncm_fs_function, ncm_hs_function,
ncm_ss_function, NULL); ncm_ss_function, ncm_ss_function);
if (status) if (status)
goto fail; goto fail;

View File

@@ -31,6 +31,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/cdev.h> #include <linux/cdev.h>
#include <linux/kref.h>
#include <asm/byteorder.h> #include <asm/byteorder.h>
#include <linux/io.h> #include <linux/io.h>
@@ -64,7 +65,7 @@ struct printer_dev {
struct usb_gadget *gadget; struct usb_gadget *gadget;
s8 interface; s8 interface;
struct usb_ep *in_ep, *out_ep; 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; /* List of free RX structs */
struct list_head rx_reqs_active; /* List of Active RX xfers */ struct list_head rx_reqs_active; /* List of Active RX xfers */
struct list_head rx_buffers; /* List of completed 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 * static struct usb_request *
printer_req_alloc(struct usb_ep *ep, unsigned len, gfp_t gfp_flags) 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); spin_unlock_irqrestore(&dev->lock, flags);
kref_get(&dev->kref);
DBG(dev, "printer_open returned %x\n", ret); DBG(dev, "printer_open returned %x\n", ret);
return ret; return ret;
} }
@@ -370,6 +379,7 @@ printer_close(struct inode *inode, struct file *fd)
dev->printer_status &= ~PRINTER_SELECTED; dev->printer_status &= ~PRINTER_SELECTED;
spin_unlock_irqrestore(&dev->lock, flags); spin_unlock_irqrestore(&dev->lock, flags);
kref_put(&dev->kref, printer_dev_free);
DBG(dev, "printer_close\n"); DBG(dev, "printer_close\n");
return 0; return 0;
@@ -1386,7 +1396,8 @@ static void gprinter_free(struct usb_function *f)
struct f_printer_opts *opts; struct f_printer_opts *opts;
opts = container_of(f->fi, struct f_printer_opts, func_inst); opts = container_of(f->fi, struct f_printer_opts, func_inst);
kfree(dev);
kref_put(&dev->kref, printer_dev_free);
mutex_lock(&opts->lock); mutex_lock(&opts->lock);
--opts->refcnt; --opts->refcnt;
mutex_unlock(&opts->lock); mutex_unlock(&opts->lock);
@@ -1455,6 +1466,7 @@ static struct usb_function *gprinter_alloc(struct usb_function_instance *fi)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
} }
kref_init(&dev->kref);
++opts->refcnt; ++opts->refcnt;
dev->minor = opts->minor; dev->minor = opts->minor;
dev->pnp_string = opts->pnp_string; dev->pnp_string = opts->pnp_string;

View File

@@ -392,12 +392,12 @@ static void bot_set_alt(struct f_uas *fu)
fu->flags = USBG_IS_BOT; 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); ret = usb_ep_enable(fu->ep_in);
if (ret) if (ret)
goto err_b_in; 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); ret = usb_ep_enable(fu->ep_out);
if (ret) if (ret)
goto err_b_out; goto err_b_out;
@@ -852,21 +852,21 @@ static void uasp_set_alt(struct f_uas *fu)
if (gadget->speed >= USB_SPEED_SUPER) if (gadget->speed >= USB_SPEED_SUPER)
fu->flags |= USBG_USE_STREAMS; 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); ret = usb_ep_enable(fu->ep_in);
if (ret) if (ret)
goto err_b_in; 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); ret = usb_ep_enable(fu->ep_out);
if (ret) if (ret)
goto err_b_out; 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); ret = usb_ep_enable(fu->ep_cmd);
if (ret) if (ret)
goto err_cmd; 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); ret = usb_ep_enable(fu->ep_status);
if (ret) if (ret)
goto err_status; goto err_status;

View File

@@ -740,20 +740,20 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
/* Initialise video. */ /* Initialise video. */
ret = uvcg_video_init(&uvc->video, uvc); ret = uvcg_video_init(&uvc->video, uvc);
if (ret < 0) if (ret < 0)
goto error; goto v4l2_error;
/* Register a V4L2 device. */ /* Register a V4L2 device. */
ret = uvc_register_video(uvc); ret = uvc_register_video(uvc);
if (ret < 0) { if (ret < 0) {
uvcg_err(f, "failed to register video device\n"); uvcg_err(f, "failed to register video device\n");
goto error; goto v4l2_error;
} }
return 0; return 0;
error: v4l2_error:
v4l2_device_unregister(&uvc->v4l2_dev); v4l2_device_unregister(&uvc->v4l2_dev);
error:
if (uvc->control_req) if (uvc->control_req)
usb_ep_free_request(cdev->gadget->ep0, uvc->control_req); usb_ep_free_request(cdev->gadget->ep0, uvc->control_req);
kfree(uvc->control_buf); kfree(uvc->control_buf);

View File

@@ -93,7 +93,7 @@ struct eth_dev {
static inline int qlen(struct usb_gadget *gadget, unsigned qmult) static inline int qlen(struct usb_gadget *gadget, unsigned qmult)
{ {
if (gadget_is_dualspeed(gadget) && (gadget->speed == USB_SPEED_HIGH || if (gadget_is_dualspeed(gadget) && (gadget->speed == USB_SPEED_HIGH ||
gadget->speed == USB_SPEED_SUPER)) gadget->speed >= USB_SPEED_SUPER))
return qmult * DEFAULT_QLEN; return qmult * DEFAULT_QLEN;
else else
return DEFAULT_QLEN; return DEFAULT_QLEN;

View File

@@ -1391,6 +1391,7 @@ void gserial_disconnect(struct gserial *gser)
if (port->port.tty) if (port->port.tty)
tty_hangup(port->port.tty); tty_hangup(port->port.tty);
} }
port->suspended = false;
spin_unlock_irqrestore(&port->port_lock, flags); spin_unlock_irqrestore(&port->port_lock, flags);
/* disable endpoints, aborting down any active I/O */ /* disable endpoints, aborting down any active I/O */

View File

@@ -135,13 +135,9 @@ static irqreturn_t ast_vhub_irq(int irq, void *data)
/* Handle device interrupts */ /* Handle device interrupts */
if (istat & vhub->port_irq_mask) { if (istat & vhub->port_irq_mask) {
unsigned long bitmap = istat; for (i = 0; i < vhub->max_ports; i++) {
int offset = VHUB_IRQ_DEV1_BIT; if (istat & VHUB_DEV_IRQ(i))
int size = VHUB_IRQ_DEV1_BIT + vhub->max_ports; ast_vhub_dev_irq(&vhub->ports[i].dev);
for_each_set_bit_from(offset, &bitmap, size) {
i = offset - VHUB_IRQ_DEV1_BIT;
ast_vhub_dev_irq(&vhub->ports[i].dev);
} }
} }

View File

@@ -67,6 +67,9 @@
#define VHUB_IRQ_HUB_EP0_SETUP (1 << 0) #define VHUB_IRQ_HUB_EP0_SETUP (1 << 0)
#define VHUB_IRQ_ACK_ALL 0x1ff #define VHUB_IRQ_ACK_ALL 0x1ff
/* Downstream device IRQ mask. */
#define VHUB_DEV_IRQ(n) (VHUB_IRQ_DEVICE1 << (n))
/* SW reset reg */ /* SW reset reg */
#define VHUB_SW_RESET_EP_POOL (1 << 9) #define VHUB_SW_RESET_EP_POOL (1 << 9)
#define VHUB_SW_RESET_DMA_CONTROLLER (1 << 8) #define VHUB_SW_RESET_DMA_CONTROLLER (1 << 8)

View File

@@ -1056,16 +1056,19 @@ found_ep:
switch (usb_endpoint_type(desc)) { switch (usb_endpoint_type(desc)) {
case USB_ENDPOINT_XFER_CONTROL: case USB_ENDPOINT_XFER_CONTROL:
ep->nr_banks = 1;
break; break;
case USB_ENDPOINT_XFER_ISOC: case USB_ENDPOINT_XFER_ISOC:
ep->fifo_size = 1024; ep->fifo_size = 1024;
ep->nr_banks = 2; if (ep->udc->ep_prealloc)
ep->nr_banks = 2;
break; break;
case USB_ENDPOINT_XFER_BULK: case USB_ENDPOINT_XFER_BULK:
ep->fifo_size = 512; ep->fifo_size = 512;
ep->nr_banks = 1; if (ep->udc->ep_prealloc)
ep->nr_banks = 1;
break; break;
case USB_ENDPOINT_XFER_INT: case USB_ENDPOINT_XFER_INT:
@@ -1075,7 +1078,8 @@ found_ep:
else else
ep->fifo_size = ep->fifo_size =
roundup_pow_of_two(le16_to_cpu(desc->wMaxPacketSize)); roundup_pow_of_two(le16_to_cpu(desc->wMaxPacketSize));
ep->nr_banks = 1; if (ep->udc->ep_prealloc)
ep->nr_banks = 1;
break; break;
} }
@@ -1091,8 +1095,6 @@ found_ep:
USBA_BF(EPT_SIZE, fls(ep->fifo_size - 1) - 3); USBA_BF(EPT_SIZE, fls(ep->fifo_size - 1) - 3);
ep->ept_cfg |= USBA_BF(BK_NUMBER, ep->nr_banks); ep->ept_cfg |= USBA_BF(BK_NUMBER, ep->nr_banks);
ep->udc->configured_ep++;
} }
return _ep; return _ep;
@@ -1786,7 +1788,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
if (status & USBA_END_OF_RESET) { if (status & USBA_END_OF_RESET) {
struct usba_ep *ep0, *ep; struct usba_ep *ep0, *ep;
int i, n; int i;
usba_writel(udc, INT_CLR, usba_writel(udc, INT_CLR,
USBA_END_OF_RESET|USBA_END_OF_RESUME 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"); "ODD: EP0 configuration is invalid!\n");
/* Preallocate other endpoints */ /* Preallocate other endpoints */
n = fifo_mode ? udc->num_ep : udc->configured_ep; for (i = 1; i < udc->num_ep; i++) {
for (i = 1; i < n; i++) {
ep = &udc->usba_ep[i]; ep = &udc->usba_ep[i];
usba_ep_writel(ep, CFG, ep->ept_cfg); if (ep->ep.claimed) {
if (!(usba_ep_readl(ep, CFG) & USBA_EPT_MAPPED)) usba_ep_writel(ep, CFG, ep->ept_cfg);
dev_err(&udc->pdev->dev, if (!(usba_ep_readl(ep, CFG) & USBA_EPT_MAPPED))
"ODD: EP%d configuration is invalid!\n", i); 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) if (udc->vbus_pin)
disable_irq(gpiod_to_irq(udc->vbus_pin)); disable_irq(gpiod_to_irq(udc->vbus_pin));
if (fifo_mode == 0)
udc->configured_ep = 1;
udc->suspended = false; udc->suspended = false;
usba_stop(udc); usba_stop(udc);
@@ -2090,33 +2090,51 @@ static const struct usba_udc_config udc_at91sam9rl_cfg = {
.errata = &at91sam9rl_errata, .errata = &at91sam9rl_errata,
.config = ep_config_sam9, .config = ep_config_sam9,
.num_ep = ARRAY_SIZE(ep_config_sam9), .num_ep = ARRAY_SIZE(ep_config_sam9),
.ep_prealloc = true,
}; };
static const struct usba_udc_config udc_at91sam9g45_cfg = { static const struct usba_udc_config udc_at91sam9g45_cfg = {
.errata = &at91sam9g45_errata, .errata = &at91sam9g45_errata,
.config = ep_config_sam9, .config = ep_config_sam9,
.num_ep = ARRAY_SIZE(ep_config_sam9), .num_ep = ARRAY_SIZE(ep_config_sam9),
.ep_prealloc = true,
}; };
static const struct usba_udc_config udc_sama5d3_cfg = { static const struct usba_udc_config udc_sama5d3_cfg = {
.config = ep_config_sama5, .config = ep_config_sama5,
.num_ep = ARRAY_SIZE(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[] = { static const struct of_device_id atmel_udc_dt_ids[] = {
{ .compatible = "atmel,at91sam9rl-udc", .data = &udc_at91sam9rl_cfg }, { .compatible = "atmel,at91sam9rl-udc", .data = &udc_at91sam9rl_cfg },
{ .compatible = "atmel,at91sam9g45-udc", .data = &udc_at91sam9g45_cfg }, { .compatible = "atmel,at91sam9g45-udc", .data = &udc_at91sam9g45_cfg },
{ .compatible = "atmel,sama5d3-udc", .data = &udc_sama5d3_cfg }, { .compatible = "atmel,sama5d3-udc", .data = &udc_sama5d3_cfg },
{ .compatible = "microchip,sam9x60-udc", .data = &udc_sam9x60_cfg },
{ /* sentinel */ } { /* sentinel */ }
}; };
MODULE_DEVICE_TABLE(of, atmel_udc_dt_ids); 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, static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
struct usba_udc *udc) struct usba_udc *udc)
{ {
struct device_node *np = pdev->dev.of_node; struct device_node *np = pdev->dev.of_node;
const struct of_device_id *match; const struct of_device_id *match;
struct device_node *pp;
int i, ret; int i, ret;
struct usba_ep *eps, *ep; struct usba_ep *eps, *ep;
const struct usba_udc_config *udc_config; 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); return ERR_PTR(-EINVAL);
udc_config = match->data; udc_config = match->data;
udc->ep_prealloc = udc_config->ep_prealloc;
udc->errata = udc_config->errata; udc->errata = udc_config->errata;
udc->pmc = syscon_regmap_lookup_by_compatible("atmel,at91sam9g45-pmc"); if (udc->errata) {
if (IS_ERR(udc->pmc)) pp = of_find_matching_node_and_match(NULL, atmel_pmc_dt_ids,
udc->pmc = syscon_regmap_lookup_by_compatible("atmel,at91sam9rl-pmc"); NULL);
if (IS_ERR(udc->pmc)) if (!pp)
udc->pmc = syscon_regmap_lookup_by_compatible("atmel,at91sam9x5-pmc"); return ERR_PTR(-ENODEV);
if (udc->errata && IS_ERR(udc->pmc))
return ERR_CAST(udc->pmc); 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; udc->num_ep = 0;
@@ -2142,7 +2165,6 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
if (fifo_mode == 0) { if (fifo_mode == 0) {
udc->num_ep = udc_config->num_ep; udc->num_ep = udc_config->num_ep;
udc->configured_ep = 1;
} else { } else {
udc->num_ep = usba_config_fifo_table(udc); udc->num_ep = usba_config_fifo_table(udc);
} }

View File

@@ -317,6 +317,7 @@ struct usba_udc_config {
const struct usba_udc_errata *errata; const struct usba_udc_errata *errata;
const struct usba_ep_config *config; const struct usba_ep_config *config;
const int num_ep; const int num_ep;
const bool ep_prealloc;
}; };
struct usba_udc { struct usba_udc {
@@ -336,7 +337,6 @@ struct usba_udc {
int irq; int irq;
struct gpio_desc *vbus_pin; struct gpio_desc *vbus_pin;
int num_ep; int num_ep;
int configured_ep;
struct usba_fifo_cfg *fifo_cfg; struct usba_fifo_cfg *fifo_cfg;
struct clk *pclk; struct clk *pclk;
struct clk *hclk; struct clk *hclk;
@@ -344,6 +344,7 @@ struct usba_udc {
bool bias_pulse_needed; bool bias_pulse_needed;
bool clocked; bool clocked;
bool suspended; bool suspended;
bool ep_prealloc;
u16 devstatus; u16 devstatus;

View File

@@ -26,6 +26,7 @@
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/usb.h>
#include <linux/usb/ch9.h> #include <linux/usb/ch9.h>
#include <linux/usb/gadget.h> #include <linux/usb/gadget.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>

View File

@@ -484,7 +484,7 @@ static void bdc_phy_exit(struct bdc *bdc)
static int bdc_probe(struct platform_device *pdev) static int bdc_probe(struct platform_device *pdev)
{ {
struct bdc *bdc; struct bdc *bdc;
int ret = -ENOMEM; int ret;
int irq; int irq;
u32 temp; u32 temp;
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
@@ -510,10 +510,9 @@ static int bdc_probe(struct platform_device *pdev)
bdc->clk = clk; bdc->clk = clk;
bdc->regs = devm_platform_ioremap_resource(pdev, 0); bdc->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(bdc->regs)) { if (IS_ERR(bdc->regs))
dev_err(dev, "ioremap error\n"); return PTR_ERR(bdc->regs);
return -ENOMEM;
}
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);
if (irq < 0) if (irq < 0)
return irq; return irq;

View File

@@ -715,6 +715,9 @@ int usb_gadget_disconnect(struct usb_gadget *gadget)
goto out; goto out;
} }
if (!gadget->connected)
goto out;
if (gadget->deactivated) { if (gadget->deactivated) {
/* /*
* If gadget is deactivated we only save new state. * 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 * @parent: the parent device to this udc. Usually the controller driver's
* device. * device.
* @gadget: the gadget to be added to the list. * @gadget: the gadget to be initialized.
* @release: a gadget release function. * @release: a gadget release function.
* *
* Returns zero on success, negative errno otherwise. * Returns zero on success, negative errno otherwise.
* Calls the gadget release function in the latter case. * 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)) void (*release)(struct device *dev))
{ {
struct usb_udc *udc;
int ret = -ENOMEM;
dev_set_name(&gadget->dev, "gadget"); dev_set_name(&gadget->dev, "gadget");
INIT_WORK(&gadget->work, usb_gadget_state_work); INIT_WORK(&gadget->work, usb_gadget_state_work);
gadget->dev.parent = parent; 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; gadget->dev.release = usb_udc_nop_release;
device_initialize(&gadget->dev); 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); udc = kzalloc(sizeof(*udc), GFP_KERNEL);
if (!udc) if (!udc)
goto err_put_gadget; goto error;
device_initialize(&udc->dev); device_initialize(&udc->dev);
udc->dev.release = usb_udc_release; udc->dev.release = usb_udc_release;
udc->dev.class = udc_class; udc->dev.class = udc_class;
udc->dev.groups = usb_udc_attr_groups; udc->dev.groups = usb_udc_attr_groups;
udc->dev.parent = parent; udc->dev.parent = gadget->dev.parent;
ret = dev_set_name(&udc->dev, "%s", kobject_name(&parent->kobj)); ret = dev_set_name(&udc->dev, "%s",
kobject_name(&gadget->dev.parent->kobj));
if (ret) if (ret)
goto err_put_udc; goto err_put_udc;
@@ -1242,8 +1257,30 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
err_put_udc: err_put_udc:
put_device(&udc->dev); put_device(&udc->dev);
err_put_gadget: error:
put_device(&gadget->dev); 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; return ret;
} }
EXPORT_SYMBOL_GPL(usb_add_gadget_udc_release); 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. * @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. * 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; 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); kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE);
flush_work(&gadget->work); flush_work(&gadget->work);
device_unregister(&udc->dev); device_unregister(&udc->dev);
device_unregister(&gadget->dev); device_del(&gadget->dev);
memset(&gadget->dev, 0x00, sizeof(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); EXPORT_SYMBOL_GPL(usb_del_gadget_udc);

View File

@@ -2061,7 +2061,7 @@ static int fsl_proc_read(struct seq_file *m, void *v)
"Sleep Enable: %d SOF Received Enable: %d " "Sleep Enable: %d SOF Received Enable: %d "
"Reset Enable: %d\n" "Reset Enable: %d\n"
"System Error Enable: %d " "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", "USB Error Intr Enable: %d USB Intr Enable: %d\n\n",
(tmp_reg & USB_INTR_DEVICE_SUSPEND) ? 1 : 0, (tmp_reg & USB_INTR_DEVICE_SUSPEND) ? 1 : 0,
(tmp_reg & USB_INTR_SOF_EN) ? 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 */ /* DEN is bidirectional ep number, max_ep doubles the number */
udc_controller->max_ep = (dccparams & DCCPARAMS_DEN_MASK) * 2; udc_controller->max_ep = (dccparams & DCCPARAMS_DEN_MASK) * 2;
udc_controller->irq = platform_get_irq(pdev, 0); ret = platform_get_irq(pdev, 0);
if (udc_controller->irq <= 0) { if (ret <= 0) {
ret = udc_controller->irq ? : -ENODEV; ret = ret ? : -ENODEV;
goto err_iounmap; goto err_iounmap;
} }
udc_controller->irq = ret;
ret = request_irq(udc_controller->irq, fsl_udc_irq, IRQF_SHARED, ret = request_irq(udc_controller->irq, fsl_udc_irq, IRQF_SHARED,
driver_name, udc_controller); driver_name, udc_controller);

View File

@@ -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_udc *udc = s->private;
struct lpc32xx_ep *ep; struct lpc32xx_ep *ep;
@@ -524,22 +524,11 @@ static int proc_udc_show(struct seq_file *s, void *unused)
return 0; return 0;
} }
static int proc_udc_open(struct inode *inode, struct file *file) DEFINE_SHOW_ATTRIBUTE(udc);
{
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,
};
static void create_debug_file(struct lpc32xx_udc *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) static void remove_debug_file(struct lpc32xx_udc *udc)

View File

@@ -9,7 +9,6 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/io.h> #include <linux/io.h>
@@ -2196,7 +2195,8 @@ static int net2272_present(struct net2272 *dev)
static void static void
net2272_gadget_release(struct device *_dev) 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); kfree(dev);
} }
@@ -2205,7 +2205,8 @@ net2272_gadget_release(struct device *_dev)
static void static void
net2272_remove(struct net2272 *dev) net2272_remove(struct net2272 *dev)
{ {
usb_del_gadget_udc(&dev->gadget); if (dev->added)
usb_del_gadget(&dev->gadget);
free_irq(dev->irq, dev); free_irq(dev->irq, dev);
iounmap(dev->base_addr); iounmap(dev->base_addr);
device_remove_file(dev->dev, &dev_attr_registers); 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 */ /* the "gadget" abstracts/virtualizes the controller */
ret->gadget.name = driver_name; ret->gadget.name = driver_name;
usb_initialize_gadget(dev, &ret->gadget, net2272_gadget_release);
return ret; return ret;
} }
@@ -2273,10 +2275,10 @@ net2272_probe_fin(struct net2272 *dev, unsigned int irqflags)
if (ret) if (ret)
goto err_irq; goto err_irq;
ret = usb_add_gadget_udc_release(dev->dev, &dev->gadget, ret = usb_add_gadget(&dev->gadget);
net2272_gadget_release);
if (ret) if (ret)
goto err_add_udc; goto err_add_udc;
dev->added = 1;
return 0; 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) { if (pci_enable_device(pdev) < 0) {
ret = -ENODEV; ret = -ENODEV;
goto err_free; goto err_put;
} }
pci_set_master(pdev); pci_set_master(pdev);
@@ -2474,8 +2476,8 @@ net2272_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
err_pci: err_pci:
pci_disable_device(pdev); pci_disable_device(pdev);
err_free: err_put:
kfree(dev); usb_put_gadget(&dev->gadget);
return ret; return ret;
} }
@@ -2536,7 +2538,7 @@ net2272_pci_remove(struct pci_dev *pdev)
pci_disable_device(pdev); pci_disable_device(pdev);
kfree(dev); usb_put_gadget(&dev->gadget);
} }
/* Table of matching PCI IDs */ /* Table of matching PCI IDs */
@@ -2649,7 +2651,7 @@ net2272_plat_probe(struct platform_device *pdev)
err_req: err_req:
release_mem_region(base, len); release_mem_region(base, len);
err: err:
kfree(dev); usb_put_gadget(&dev->gadget);
return ret; return ret;
} }
@@ -2664,7 +2666,7 @@ net2272_plat_remove(struct platform_device *pdev)
release_mem_region(pdev->resource[0].start, release_mem_region(pdev->resource[0].start,
resource_size(&pdev->resource[0])); resource_size(&pdev->resource[0]));
kfree(dev); usb_put_gadget(&dev->gadget);
return 0; return 0;
} }

View File

@@ -441,6 +441,7 @@ struct net2272 {
unsigned protocol_stall:1, unsigned protocol_stall:1,
softconnect:1, softconnect:1,
wakeup:1, wakeup:1,
added:1,
dma_eot_polarity:1, dma_eot_polarity:1,
dma_dack_polarity:1, dma_dack_polarity:1,
dma_dreq_polarity:1, dma_dreq_polarity:1,

View File

@@ -3560,7 +3560,7 @@ static irqreturn_t net2280_irq(int irq, void *_dev)
static void gadget_release(struct device *_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); kfree(dev);
} }
@@ -3571,7 +3571,8 @@ static void net2280_remove(struct pci_dev *pdev)
{ {
struct net2280 *dev = pci_get_drvdata(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); BUG_ON(dev->driver);
@@ -3602,6 +3603,7 @@ static void net2280_remove(struct pci_dev *pdev)
device_remove_file(&pdev->dev, &dev_attr_registers); device_remove_file(&pdev->dev, &dev_attr_registers);
ep_info(dev, "unbind\n"); ep_info(dev, "unbind\n");
usb_put_gadget(&dev->gadget);
} }
/* wrap this driver around the specified device, but /* 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); pci_set_drvdata(pdev, dev);
usb_initialize_gadget(&pdev->dev, &dev->gadget, gadget_release);
spin_lock_init(&dev->lock); spin_lock_init(&dev->lock);
dev->quirks = id->driver_data; dev->quirks = id->driver_data;
dev->pdev = pdev; dev->pdev = pdev;
@@ -3773,10 +3776,10 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if (retval) if (retval)
goto done; goto done;
retval = usb_add_gadget_udc_release(&pdev->dev, &dev->gadget, retval = usb_add_gadget(&dev->gadget);
gadget_release);
if (retval) if (retval)
goto done; goto done;
dev->added = 1;
return 0; return 0;
done: done:

View File

@@ -156,6 +156,7 @@ struct net2280 {
softconnect : 1, softconnect : 1,
got_irq : 1, got_irq : 1,
region:1, region:1,
added:1,
u1_enable:1, u1_enable:1,
u2_enable:1, u2_enable:1,
ltm_enable:1, ltm_enable:1,

View File

@@ -12,12 +12,9 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/usb/ch9.h> #include <linux/usb/ch9.h>
#include <linux/usb/gadget.h> #include <linux/usb/gadget.h>
#include <linux/gpio.h> #include <linux/gpio/consumer.h>
#include <linux/irq.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_PERIOD 3000 /* VBUS polling period (msec) */
#define PCH_VBUS_INTERVAL 10 /* VBUS polling interval (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 * struct pch_vbus_gpio_data - Structure holding GPIO informaton
* for detecting VBUS * for detecting VBUS
* @port: gpio port number * @port: gpio descriptor for the VBUS GPIO
* @intr: gpio interrupt number * @intr: gpio interrupt number
* @irq_work_fall: Structure for WorkQueue * @irq_work_fall: Structure for WorkQueue
* @irq_work_rise: Structure for WorkQueue * @irq_work_rise: Structure for WorkQueue
*/ */
struct pch_vbus_gpio_data { struct pch_vbus_gpio_data {
int port; struct gpio_desc *port;
int intr; int intr;
struct work_struct irq_work_fall; struct work_struct irq_work_fall;
struct work_struct irq_work_rise; 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; int vbus = 0;
if (dev->vbus_gpio.port) 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 else
vbus = -1; 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. * pch_vbus_gpio_init() - This API initializes GPIO port detecting VBUS.
* @dev: Reference to the driver structure * @dev: Reference to the driver structure
* @vbus_gpio_port: Number of GPIO port to detect gpio
* *
* Return codes: * Return codes:
* 0: Success * 0: Success
* -EINVAL: GPIO port is invalid or can't be initialized. * -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 err;
int irq_num = 0; int irq_num = 0;
struct gpio_desc *gpiod;
dev->vbus_gpio.port = 0; dev->vbus_gpio.port = NULL;
dev->vbus_gpio.intr = 0; dev->vbus_gpio.intr = 0;
if (vbus_gpio_port <= -1) /* Retrieve the GPIO line from the USB gadget device */
return -EINVAL; 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); dev->vbus_gpio.port = gpiod;
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);
INIT_WORK(&dev->vbus_gpio.irq_work_fall, pch_vbus_gpio_work_fall); 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) { if (irq_num > 0) {
irq_set_irq_type(irq_num, IRQ_TYPE_EDGE_BOTH); irq_set_irq_type(irq_num, IRQ_TYPE_EDGE_BOTH);
err = request_irq(irq_num, pch_vbus_gpio_irq, 0, 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) if (dev->vbus_gpio.intr)
free_irq(dev->vbus_gpio.intr, dev); 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_init(dev);
pch_udc_pcd_reinit(dev); pch_udc_pcd_reinit(dev);
pch_vbus_gpio_init(dev, vbus_gpio_port); pch_vbus_gpio_init(dev);
return 0; return 0;
} }
@@ -3096,6 +3078,13 @@ static int pch_udc_probe(struct pci_dev *pdev,
dev->base_addr = pcim_iomap_table(pdev)[bar]; 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 */ /* initialize the hardware */
if (pch_udc_pcd_init(dev)) if (pch_udc_pcd_init(dev))
return -ENODEV; return -ENODEV;

View File

@@ -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) static int s3c2410_udc_dequeue(struct usb_ep *_ep, struct usb_request *_req)
{ {
struct s3c2410_ep *ep = to_s3c2410_ep(_ep); struct s3c2410_ep *ep = to_s3c2410_ep(_ep);
struct s3c2410_udc *udc;
int retval = -EINVAL; int retval = -EINVAL;
unsigned long flags; unsigned long flags;
struct s3c2410_request *req = NULL; 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) if (!_ep || !_req)
return retval; return retval;
udc = to_s3c2410_udc(ep->gadget);
local_irq_save(flags); local_irq_save(flags);
list_for_each_entry(req, &ep->queue, queue) { list_for_each_entry(req, &ep->queue, queue) {

View File

@@ -705,11 +705,11 @@ static void tegra_xudc_device_mode_on(struct tegra_xudc *xudc)
err = phy_power_on(xudc->curr_utmi_phy); err = phy_power_on(xudc->curr_utmi_phy);
if (err < 0) 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); err = phy_power_on(xudc->curr_usb3_phy);
if (err < 0) 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"); 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); err = phy_power_off(xudc->curr_utmi_phy);
if (err < 0) 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); err = phy_power_off(xudc->curr_usb3_phy);
if (err < 0) 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); 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; return -EINVAL;
if (usb_endpoint_xfer_isoc(ep->desc)) { 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; return -ENOTSUPP;
} }
@@ -1788,7 +1788,7 @@ static int __tegra_xudc_ep_enable(struct tegra_xudc_ep *ep,
if (usb_endpoint_xfer_isoc(desc)) { if (usb_endpoint_xfer_isoc(desc)) {
if (xudc->nr_isoch_eps > XUDC_MAX_ISOCH_EPS) { 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; return -EBUSY;
} }
xudc->nr_isoch_eps++; 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])) { if (IS_ERR(xudc->utmi_phy[i])) {
err = PTR_ERR(xudc->utmi_phy[i]); err = PTR_ERR(xudc->utmi_phy[i]);
if (err != -EPROBE_DEFER) 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); i, err);
goto clean_up; goto clean_up;
@@ -3539,12 +3539,12 @@ static int tegra_xudc_phy_get(struct tegra_xudc *xudc)
if (IS_ERR(xudc->usb3_phy[i])) { if (IS_ERR(xudc->usb3_phy[i])) {
err = PTR_ERR(xudc->usb3_phy[i]); err = PTR_ERR(xudc->usb3_phy[i]);
if (err != -EPROBE_DEFER) 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); usb3, err);
goto clean_up; goto clean_up;
} else if (xudc->usb3_phy[i]) } 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; 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++) { for (i = 0; i < xudc->soc->num_phys; i++) {
err = phy_init(xudc->utmi_phy[i]); err = phy_init(xudc->utmi_phy[i]);
if (err < 0) { 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; goto exit_phy;
} }
err = phy_init(xudc->usb3_phy[i]); err = phy_init(xudc->usb3_phy[i]);
if (err < 0) { 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; goto exit_phy;
} }
} }
@@ -3692,34 +3692,33 @@ static int tegra_xudc_powerdomain_init(struct tegra_xudc *xudc)
struct device *dev = xudc->dev; struct device *dev = xudc->dev;
int err; int err;
xudc->genpd_dev_device = dev_pm_domain_attach_by_name(dev, xudc->genpd_dev_device = dev_pm_domain_attach_by_name(dev, "dev");
"dev");
if (IS_ERR(xudc->genpd_dev_device)) { if (IS_ERR(xudc->genpd_dev_device)) {
err = PTR_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; return err;
} }
xudc->genpd_dev_ss = dev_pm_domain_attach_by_name(dev, "ss"); xudc->genpd_dev_ss = dev_pm_domain_attach_by_name(dev, "ss");
if (IS_ERR(xudc->genpd_dev_ss)) { if (IS_ERR(xudc->genpd_dev_ss)) {
err = PTR_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; return err;
} }
xudc->genpd_dl_device = device_link_add(dev, xudc->genpd_dev_device, xudc->genpd_dl_device = device_link_add(dev, xudc->genpd_dev_device,
DL_FLAG_PM_RUNTIME | DL_FLAG_PM_RUNTIME |
DL_FLAG_STATELESS); DL_FLAG_STATELESS);
if (!xudc->genpd_dl_device) { 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; return -ENODEV;
} }
xudc->genpd_dl_ss = device_link_add(dev, xudc->genpd_dev_ss, xudc->genpd_dl_ss = device_link_add(dev, xudc->genpd_dev_ss,
DL_FLAG_PM_RUNTIME | DL_FLAG_PM_RUNTIME |
DL_FLAG_STATELESS); DL_FLAG_STATELESS);
if (!xudc->genpd_dl_ss) { 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; return -ENODEV;
} }
@@ -3733,7 +3732,7 @@ static int tegra_xudc_probe(struct platform_device *pdev)
unsigned int i; unsigned int i;
int err; int err;
xudc = devm_kzalloc(&pdev->dev, sizeof(*xudc), GFP_ATOMIC); xudc = devm_kzalloc(&pdev->dev, sizeof(*xudc), GFP_KERNEL);
if (!xudc) if (!xudc)
return -ENOMEM; return -ENOMEM;
@@ -3772,18 +3771,19 @@ static int tegra_xudc_probe(struct platform_device *pdev)
return err; return err;
} }
xudc->clks = devm_kcalloc(&pdev->dev, xudc->soc->num_clks, xudc->clks = devm_kcalloc(&pdev->dev, xudc->soc->num_clks, sizeof(*xudc->clks),
sizeof(*xudc->clks), GFP_KERNEL); GFP_KERNEL);
if (!xudc->clks) if (!xudc->clks)
return -ENOMEM; return -ENOMEM;
for (i = 0; i < xudc->soc->num_clks; i++) for (i = 0; i < xudc->soc->num_clks; i++)
xudc->clks[i].id = xudc->soc->clock_names[i]; xudc->clks[i].id = xudc->soc->clock_names[i];
err = devm_clk_bulk_get(&pdev->dev, xudc->soc->num_clks, err = devm_clk_bulk_get(&pdev->dev, xudc->soc->num_clks, xudc->clks);
xudc->clks);
if (err) { 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; 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, err = devm_regulator_bulk_get(&pdev->dev, xudc->soc->num_supplies,
xudc->supplies); xudc->supplies);
if (err) { 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; 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); err = regulator_bulk_enable(xudc->soc->num_supplies, xudc->supplies);
if (err) { 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; goto put_padctl;
} }

View File

@@ -436,6 +436,7 @@ struct usb_gadget {
}; };
#define work_to_gadget(w) (container_of((w), struct usb_gadget, work)) #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) static inline void set_gadget_data(struct usb_gadget *gadget, void *data)
{ dev_set_drvdata(&gadget->dev, data); } { dev_set_drvdata(&gadget->dev, data); }
static inline void *get_gadget_data(struct usb_gadget *gadget) 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); 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 */ /* iterates the non-control endpoints; 'tmp' is a struct usb_ep pointer */
#define gadget_for_each_ep(tmp, gadget) \ #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); 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 */ /* utility to simplify dealing with string descriptors */