mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-17 09:43:59 +08:00
USB: Changes for v5.4 merge window
With only 45 non-merge commits, we have a small merge window from the Gadget perspective. The biggest change here is the addition of the Cadence USB3 DRD Driver. All other changes are small, non-critical fixes or smaller new features like the improvement to BESL handling in dwc3. Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com> -----BEGIN PGP SIGNATURE----- iQJRBAABCAA7FiEElLzh7wn96CXwjh2IzL64meEamQYFAl1o58UdHGZlbGlwZS5i YWxiaUBsaW51eC5pbnRlbC5jb20ACgkQzL64meEamQaKtxAAvvrwvleA7lZwInU2 FariXyI7+ihPAUDtlEHA+kHclpUw4hyWPc5rg0Mk7CH5gbtqodj9Uo4vbWtM5EaR EN6AJTn90KlygBXlw2pJAb6GBiJ2hMK8K5Gl5gdowRg4LExLYdLZCgCDueAv1p2o 2vg7LSJbHaeC0pv+L97W6/YH7q4fdacGjFm/49ROrChCwNNJygoXchO2+eAcXNlo uMzx2QxF3cHOMeSxSGnXC5VsZYAk6VftXgAiGvVZ5+6EgPIA5b07JovDhn0nAoDR 1aVQTqWDzdcd/tkJKEMBi16mOaeLDzsnGmsr+/2vzPzLJaWKPKwZ/FgGTyBXzDfM qEVrj2zWioH+6oOE3G5Ar3j1dgBqbgWhTfO47UWjgdQww5HeKZJqewJh19h5t/uS hxxSFgwNMVIo4B8DJc5J/vIbDsQbk1LsXkT2b3B/UaNYXQuKBdiv4jVpUgLJX+FF mN46SsfLVBpaYANcBPlgC8zGoOzWJFXgoh0qffxnQ6/Egvh55k0xVL/YPRgHiiw5 FbaGAlnJo9ue9vadzInde0X4SZnpBIQ5WR9fyHYm8fKDT1neB97F3yhZxhJ3apxN 0PM9P7auGtmjYYLAfIIL9687c3BNvDk8YvLehxXI4YqVHUZF3YNAz/DPrnCG2O5D W33pwIhL1swIZCFRtGWpokGiN+o= =KUJN -----END PGP SIGNATURE----- Merge tag 'usb-for-v5.4' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next Felipe writes: USB: Changes for v5.4 merge window With only 45 non-merge commits, we have a small merge window from the Gadget perspective. The biggest change here is the addition of the Cadence USB3 DRD Driver. All other changes are small, non-critical fixes or smaller new features like the improvement to BESL handling in dwc3. Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com> * tag 'usb-for-v5.4' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb: (45 commits) usb: gadget: net2280: Add workaround for AB chip Errata 11 usb: gadget: net2280: Move all "ll" registers in one structure usb: dwc3: gadget: Workaround Mirosoft's BESL check usb:cdns3 Fix for stuck packets in on-chip OUT buffer. usb: cdns3: Add Cadence USB3 DRD Driver usb: common: Simplify usb_decode_get_set_descriptor function. usb: common: Patch simplify usb_decode_set_clear_feature function. usb: common: Separated decoding functions from dwc3 driver. dt-bindings: add binding for USBSS-DRD controller. usb: gadget: composite: Set recommended BESL values usb: dwc3: gadget: Set BESL config parameter usb: dwc3: Separate field holding multiple properties usb: gadget: Export recommended BESL values usb: phy: phy-fsl-usb: Make structure fsl_otg_initdata constant usb: udc: lpc32xx: silence fall-through warning usb: dwc3: meson-g12a: fix suspend resume regulator unbalanced disables usb: udc: lpc32xx: remove set but not used 3 variables usb: gadget: udc: core: Fix segfault if udc_bind_to_driver() for pending driver fails usb: dwc3: st: Add of_dev_put() in probe function usb: dwc3: st: Add of_node_put() before return in probe function ...
This commit is contained in:
commit
96e46dcfb8
45
Documentation/devicetree/bindings/usb/cdns-usb3.txt
Normal file
45
Documentation/devicetree/bindings/usb/cdns-usb3.txt
Normal file
@ -0,0 +1,45 @@
|
||||
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";
|
||||
};
|
@ -112,6 +112,8 @@ source "drivers/usb/usbip/Kconfig"
|
||||
|
||||
endif
|
||||
|
||||
source "drivers/usb/cdns3/Kconfig"
|
||||
|
||||
source "drivers/usb/mtu3/Kconfig"
|
||||
|
||||
source "drivers/usb/musb/Kconfig"
|
||||
|
@ -13,6 +13,8 @@ obj-$(CONFIG_USB_DWC3) += dwc3/
|
||||
obj-$(CONFIG_USB_DWC2) += dwc2/
|
||||
obj-$(CONFIG_USB_ISP1760) += isp1760/
|
||||
|
||||
obj-$(CONFIG_USB_CDNS3) += cdns3/
|
||||
|
||||
obj-$(CONFIG_USB_MON) += mon/
|
||||
obj-$(CONFIG_USB_MTU3) += mtu3/
|
||||
|
||||
|
46
drivers/usb/cdns3/Kconfig
Normal file
46
drivers/usb/cdns3/Kconfig
Normal file
@ -0,0 +1,46 @@
|
||||
config USB_CDNS3
|
||||
tristate "Cadence USB3 Dual-Role Controller"
|
||||
depends on USB_SUPPORT && (USB || USB_GADGET) && HAS_DMA
|
||||
select USB_XHCI_PLATFORM if USB_XHCI_HCD
|
||||
select USB_ROLE_SWITCH
|
||||
help
|
||||
Say Y here if your system has a Cadence USB3 dual-role controller.
|
||||
It supports: dual-role switch, Host-only, and Peripheral-only.
|
||||
|
||||
If you choose to build this driver is a dynamically linked
|
||||
as module, the module will be called cdns3.ko.
|
||||
|
||||
if USB_CDNS3
|
||||
|
||||
config USB_CDNS3_GADGET
|
||||
bool "Cadence USB3 device controller"
|
||||
depends on USB_GADGET=y || USB_GADGET=USB_CDNS3
|
||||
help
|
||||
Say Y here to enable device controller functionality of the
|
||||
Cadence USBSS-DEV driver.
|
||||
|
||||
This controller supports FF, HS and SS mode. It doesn't support
|
||||
LS and SSP mode.
|
||||
|
||||
config USB_CDNS3_HOST
|
||||
bool "Cadence USB3 host controller"
|
||||
depends on USB=y || USB=USB_CDNS3
|
||||
help
|
||||
Say Y here to enable host controller functionality of the
|
||||
Cadence driver.
|
||||
|
||||
Host controller is compliant with XHCI so it will use
|
||||
standard XHCI driver.
|
||||
|
||||
config USB_CDNS3_PCI_WRAP
|
||||
tristate "Cadence USB3 support on PCIe-based platforms"
|
||||
depends on USB_PCI && ACPI
|
||||
default USB_CDNS3
|
||||
help
|
||||
If you're using the USBSS Core IP with a PCIe, please say
|
||||
'Y' or 'M' here.
|
||||
|
||||
If you choose to build this driver as module it will
|
||||
be dynamically linked and module will be called cdns3-pci.ko
|
||||
|
||||
endif
|
16
drivers/usb/cdns3/Makefile
Normal file
16
drivers/usb/cdns3/Makefile
Normal file
@ -0,0 +1,16 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# define_trace.h needs to know how to find our header
|
||||
CFLAGS_trace.o := -I$(src)
|
||||
|
||||
cdns3-y := core.o drd.o
|
||||
|
||||
obj-$(CONFIG_USB_CDNS3) += cdns3.o
|
||||
cdns3-$(CONFIG_USB_CDNS3_GADGET) += gadget.o ep0.o
|
||||
|
||||
ifneq ($(CONFIG_USB_CDNS3_GADGET),)
|
||||
cdns3-$(CONFIG_TRACING) += trace.o
|
||||
endif
|
||||
|
||||
cdns3-$(CONFIG_USB_CDNS3_HOST) += host.o
|
||||
|
||||
obj-$(CONFIG_USB_CDNS3_PCI_WRAP) += cdns3-pci-wrap.o
|
203
drivers/usb/cdns3/cdns3-pci-wrap.c
Normal file
203
drivers/usb/cdns3/cdns3-pci-wrap.c
Normal file
@ -0,0 +1,203 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Cadence USBSS PCI Glue driver
|
||||
*
|
||||
* Copyright (C) 2018-2019 Cadence.
|
||||
*
|
||||
* Author: Pawel Laszczak <pawell@cadence.com>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct cdns3_wrap {
|
||||
struct platform_device *plat_dev;
|
||||
struct resource dev_res[6];
|
||||
int devfn;
|
||||
};
|
||||
|
||||
#define RES_IRQ_HOST_ID 0
|
||||
#define RES_IRQ_PERIPHERAL_ID 1
|
||||
#define RES_IRQ_OTG_ID 2
|
||||
#define RES_HOST_ID 3
|
||||
#define RES_DEV_ID 4
|
||||
#define RES_DRD_ID 5
|
||||
|
||||
#define PCI_BAR_HOST 0
|
||||
#define PCI_BAR_DEV 2
|
||||
#define PCI_BAR_OTG 0
|
||||
|
||||
#define PCI_DEV_FN_HOST_DEVICE 0
|
||||
#define PCI_DEV_FN_OTG 1
|
||||
|
||||
#define PCI_DRIVER_NAME "cdns3-pci-usbss"
|
||||
#define PLAT_DRIVER_NAME "cdns-usb3"
|
||||
|
||||
#define CDNS_VENDOR_ID 0x17cd
|
||||
#define CDNS_DEVICE_ID 0x0100
|
||||
|
||||
static struct pci_dev *cdns3_get_second_fun(struct pci_dev *pdev)
|
||||
{
|
||||
struct pci_dev *func;
|
||||
|
||||
/*
|
||||
* Gets the second function.
|
||||
* It's little tricky, but this platform has two function.
|
||||
* The fist keeps resources for Host/Device while the second
|
||||
* keeps resources for DRD/OTG.
|
||||
*/
|
||||
func = pci_get_device(pdev->vendor, pdev->device, NULL);
|
||||
if (unlikely(!func))
|
||||
return NULL;
|
||||
|
||||
if (func->devfn == pdev->devfn) {
|
||||
func = pci_get_device(pdev->vendor, pdev->device, func);
|
||||
if (unlikely(!func))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return func;
|
||||
}
|
||||
|
||||
static int cdns3_pci_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
struct platform_device_info plat_info;
|
||||
struct cdns3_wrap *wrap;
|
||||
struct resource *res;
|
||||
struct pci_dev *func;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* for GADGET/HOST PCI (devfn) function number is 0,
|
||||
* for OTG PCI (devfn) function number is 1
|
||||
*/
|
||||
if (!id || (pdev->devfn != PCI_DEV_FN_HOST_DEVICE &&
|
||||
pdev->devfn != PCI_DEV_FN_OTG))
|
||||
return -EINVAL;
|
||||
|
||||
func = cdns3_get_second_fun(pdev);
|
||||
if (unlikely(!func))
|
||||
return -EINVAL;
|
||||
|
||||
err = pcim_enable_device(pdev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "Enabling PCI device has failed %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
pci_set_master(pdev);
|
||||
|
||||
if (pci_is_enabled(func)) {
|
||||
wrap = pci_get_drvdata(func);
|
||||
} else {
|
||||
wrap = kzalloc(sizeof(*wrap), GFP_KERNEL);
|
||||
if (!wrap) {
|
||||
pci_disable_device(pdev);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
res = wrap->dev_res;
|
||||
|
||||
if (pdev->devfn == PCI_DEV_FN_HOST_DEVICE) {
|
||||
/* function 0: host(BAR_0) + device(BAR_1).*/
|
||||
dev_dbg(&pdev->dev, "Initialize Device resources\n");
|
||||
res[RES_DEV_ID].start = pci_resource_start(pdev, PCI_BAR_DEV);
|
||||
res[RES_DEV_ID].end = pci_resource_end(pdev, PCI_BAR_DEV);
|
||||
res[RES_DEV_ID].name = "dev";
|
||||
res[RES_DEV_ID].flags = IORESOURCE_MEM;
|
||||
dev_dbg(&pdev->dev, "USBSS-DEV physical base addr: %pa\n",
|
||||
&res[RES_DEV_ID].start);
|
||||
|
||||
res[RES_HOST_ID].start = pci_resource_start(pdev, PCI_BAR_HOST);
|
||||
res[RES_HOST_ID].end = pci_resource_end(pdev, PCI_BAR_HOST);
|
||||
res[RES_HOST_ID].name = "xhci";
|
||||
res[RES_HOST_ID].flags = IORESOURCE_MEM;
|
||||
dev_dbg(&pdev->dev, "USBSS-XHCI physical base addr: %pa\n",
|
||||
&res[RES_HOST_ID].start);
|
||||
|
||||
/* Interrupt for XHCI */
|
||||
wrap->dev_res[RES_IRQ_HOST_ID].start = pdev->irq;
|
||||
wrap->dev_res[RES_IRQ_HOST_ID].name = "host";
|
||||
wrap->dev_res[RES_IRQ_HOST_ID].flags = IORESOURCE_IRQ;
|
||||
|
||||
/* Interrupt device. It's the same as for HOST. */
|
||||
wrap->dev_res[RES_IRQ_PERIPHERAL_ID].start = pdev->irq;
|
||||
wrap->dev_res[RES_IRQ_PERIPHERAL_ID].name = "peripheral";
|
||||
wrap->dev_res[RES_IRQ_PERIPHERAL_ID].flags = IORESOURCE_IRQ;
|
||||
} else {
|
||||
res[RES_DRD_ID].start = pci_resource_start(pdev, PCI_BAR_OTG);
|
||||
res[RES_DRD_ID].end = pci_resource_end(pdev, PCI_BAR_OTG);
|
||||
res[RES_DRD_ID].name = "otg";
|
||||
res[RES_DRD_ID].flags = IORESOURCE_MEM;
|
||||
dev_dbg(&pdev->dev, "USBSS-DRD physical base addr: %pa\n",
|
||||
&res[RES_DRD_ID].start);
|
||||
|
||||
/* Interrupt for OTG/DRD. */
|
||||
wrap->dev_res[RES_IRQ_OTG_ID].start = pdev->irq;
|
||||
wrap->dev_res[RES_IRQ_OTG_ID].name = "otg";
|
||||
wrap->dev_res[RES_IRQ_OTG_ID].flags = IORESOURCE_IRQ;
|
||||
}
|
||||
|
||||
if (pci_is_enabled(func)) {
|
||||
/* set up platform device info */
|
||||
memset(&plat_info, 0, sizeof(plat_info));
|
||||
plat_info.parent = &pdev->dev;
|
||||
plat_info.fwnode = pdev->dev.fwnode;
|
||||
plat_info.name = PLAT_DRIVER_NAME;
|
||||
plat_info.id = pdev->devfn;
|
||||
wrap->devfn = pdev->devfn;
|
||||
plat_info.res = wrap->dev_res;
|
||||
plat_info.num_res = ARRAY_SIZE(wrap->dev_res);
|
||||
plat_info.dma_mask = pdev->dma_mask;
|
||||
/* register platform device */
|
||||
wrap->plat_dev = platform_device_register_full(&plat_info);
|
||||
if (IS_ERR(wrap->plat_dev)) {
|
||||
pci_disable_device(pdev);
|
||||
kfree(wrap);
|
||||
return PTR_ERR(wrap->plat_dev);
|
||||
}
|
||||
}
|
||||
|
||||
pci_set_drvdata(pdev, wrap);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void cdns3_pci_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct cdns3_wrap *wrap;
|
||||
struct pci_dev *func;
|
||||
|
||||
func = cdns3_get_second_fun(pdev);
|
||||
|
||||
wrap = (struct cdns3_wrap *)pci_get_drvdata(pdev);
|
||||
if (wrap->devfn == pdev->devfn)
|
||||
platform_device_unregister(wrap->plat_dev);
|
||||
|
||||
if (!pci_is_enabled(func))
|
||||
kfree(wrap);
|
||||
}
|
||||
|
||||
static const struct pci_device_id cdns3_pci_ids[] = {
|
||||
{ PCI_DEVICE(CDNS_VENDOR_ID, CDNS_DEVICE_ID), },
|
||||
{ 0, }
|
||||
};
|
||||
|
||||
static struct pci_driver cdns3_pci_driver = {
|
||||
.name = PCI_DRIVER_NAME,
|
||||
.id_table = cdns3_pci_ids,
|
||||
.probe = cdns3_pci_probe,
|
||||
.remove = cdns3_pci_remove,
|
||||
};
|
||||
|
||||
module_pci_driver(cdns3_pci_driver);
|
||||
MODULE_DEVICE_TABLE(pci, cdns3_pci_ids);
|
||||
|
||||
MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("Cadence USBSS PCI wrapperr");
|
653
drivers/usb/cdns3/core.c
Normal file
653
drivers/usb/cdns3/core.c
Normal file
@ -0,0 +1,653 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Cadence USBSS DRD Driver.
|
||||
*
|
||||
* Copyright (C) 2018-2019 Cadence.
|
||||
* Copyright (C) 2017-2018 NXP
|
||||
* Copyright (C) 2019 Texas Instruments
|
||||
*
|
||||
* Author: Peter Chen <peter.chen@nxp.com>
|
||||
* Pawel Laszczak <pawell@cadence.com>
|
||||
* Roger Quadros <rogerq@ti.com>
|
||||
*/
|
||||
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include "gadget.h"
|
||||
#include "core.h"
|
||||
#include "host-export.h"
|
||||
#include "gadget-export.h"
|
||||
#include "drd.h"
|
||||
|
||||
static int cdns3_idle_init(struct cdns3 *cdns);
|
||||
|
||||
static inline
|
||||
struct cdns3_role_driver *cdns3_get_current_role_driver(struct cdns3 *cdns)
|
||||
{
|
||||
WARN_ON(!cdns->roles[cdns->role]);
|
||||
return cdns->roles[cdns->role];
|
||||
}
|
||||
|
||||
static int cdns3_role_start(struct cdns3 *cdns, enum usb_role role)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (WARN_ON(role > USB_ROLE_DEVICE))
|
||||
return 0;
|
||||
|
||||
mutex_lock(&cdns->mutex);
|
||||
cdns->role = role;
|
||||
mutex_unlock(&cdns->mutex);
|
||||
|
||||
if (!cdns->roles[role])
|
||||
return -ENXIO;
|
||||
|
||||
if (cdns->roles[role]->state == CDNS3_ROLE_STATE_ACTIVE)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&cdns->mutex);
|
||||
ret = cdns->roles[role]->start(cdns);
|
||||
if (!ret)
|
||||
cdns->roles[role]->state = CDNS3_ROLE_STATE_ACTIVE;
|
||||
mutex_unlock(&cdns->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void cdns3_role_stop(struct cdns3 *cdns)
|
||||
{
|
||||
enum usb_role role = cdns->role;
|
||||
|
||||
if (WARN_ON(role > USB_ROLE_DEVICE))
|
||||
return;
|
||||
|
||||
if (cdns->roles[role]->state == CDNS3_ROLE_STATE_INACTIVE)
|
||||
return;
|
||||
|
||||
mutex_lock(&cdns->mutex);
|
||||
cdns->roles[role]->stop(cdns);
|
||||
cdns->roles[role]->state = CDNS3_ROLE_STATE_INACTIVE;
|
||||
mutex_unlock(&cdns->mutex);
|
||||
}
|
||||
|
||||
static void cdns3_exit_roles(struct cdns3 *cdns)
|
||||
{
|
||||
cdns3_role_stop(cdns);
|
||||
cdns3_drd_exit(cdns);
|
||||
}
|
||||
|
||||
static enum usb_role cdsn3_hw_role_state_machine(struct cdns3 *cdns);
|
||||
|
||||
/**
|
||||
* cdns3_core_init_role - initialize role of operation
|
||||
* @cdns: Pointer to cdns3 structure
|
||||
*
|
||||
* Returns 0 on success otherwise negative errno
|
||||
*/
|
||||
static int cdns3_core_init_role(struct cdns3 *cdns)
|
||||
{
|
||||
struct device *dev = cdns->dev;
|
||||
enum usb_dr_mode best_dr_mode;
|
||||
enum usb_dr_mode dr_mode;
|
||||
int ret = 0;
|
||||
|
||||
dr_mode = usb_get_dr_mode(dev);
|
||||
cdns->role = USB_ROLE_NONE;
|
||||
|
||||
/*
|
||||
* If driver can't read mode by means of usb_get_dr_mode function then
|
||||
* chooses mode according with Kernel configuration. This setting
|
||||
* can be restricted later depending on strap pin configuration.
|
||||
*/
|
||||
if (dr_mode == USB_DR_MODE_UNKNOWN) {
|
||||
if (IS_ENABLED(CONFIG_USB_CDNS3_HOST) &&
|
||||
IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
|
||||
dr_mode = USB_DR_MODE_OTG;
|
||||
else if (IS_ENABLED(CONFIG_USB_CDNS3_HOST))
|
||||
dr_mode = USB_DR_MODE_HOST;
|
||||
else if (IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
|
||||
dr_mode = USB_DR_MODE_PERIPHERAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* At this point cdns->dr_mode contains strap configuration.
|
||||
* Driver try update this setting considering kernel configuration
|
||||
*/
|
||||
best_dr_mode = cdns->dr_mode;
|
||||
|
||||
ret = cdns3_idle_init(cdns);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (dr_mode == USB_DR_MODE_OTG) {
|
||||
best_dr_mode = cdns->dr_mode;
|
||||
} else if (cdns->dr_mode == USB_DR_MODE_OTG) {
|
||||
best_dr_mode = dr_mode;
|
||||
} else if (cdns->dr_mode != dr_mode) {
|
||||
dev_err(dev, "Incorrect DRD configuration\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dr_mode = best_dr_mode;
|
||||
|
||||
if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) {
|
||||
ret = cdns3_host_init(cdns);
|
||||
if (ret) {
|
||||
dev_err(dev, "Host initialization failed with %d\n",
|
||||
ret);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_PERIPHERAL) {
|
||||
ret = cdns3_gadget_init(cdns);
|
||||
if (ret) {
|
||||
dev_err(dev, "Device initialization failed with %d\n",
|
||||
ret);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
cdns->dr_mode = dr_mode;
|
||||
|
||||
ret = cdns3_drd_update_mode(cdns);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
if (cdns->dr_mode != USB_DR_MODE_OTG) {
|
||||
ret = cdns3_hw_role_switch(cdns);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
||||
return ret;
|
||||
err:
|
||||
cdns3_exit_roles(cdns);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdsn3_hw_role_state_machine - role switch state machine based on hw events.
|
||||
* @cdns: Pointer to controller structure.
|
||||
*
|
||||
* Returns next role to be entered based on hw events.
|
||||
*/
|
||||
static enum usb_role cdsn3_hw_role_state_machine(struct cdns3 *cdns)
|
||||
{
|
||||
enum usb_role role;
|
||||
int id, vbus;
|
||||
|
||||
if (cdns->dr_mode != USB_DR_MODE_OTG)
|
||||
goto not_otg;
|
||||
|
||||
id = cdns3_get_id(cdns);
|
||||
vbus = cdns3_get_vbus(cdns);
|
||||
|
||||
/*
|
||||
* Role change state machine
|
||||
* Inputs: ID, VBUS
|
||||
* Previous state: cdns->role
|
||||
* Next state: role
|
||||
*/
|
||||
role = cdns->role;
|
||||
|
||||
switch (role) {
|
||||
case USB_ROLE_NONE:
|
||||
/*
|
||||
* Driver treats USB_ROLE_NONE synonymous to IDLE state from
|
||||
* controller specification.
|
||||
*/
|
||||
if (!id)
|
||||
role = USB_ROLE_HOST;
|
||||
else if (vbus)
|
||||
role = USB_ROLE_DEVICE;
|
||||
break;
|
||||
case USB_ROLE_HOST: /* from HOST, we can only change to NONE */
|
||||
if (id)
|
||||
role = USB_ROLE_NONE;
|
||||
break;
|
||||
case USB_ROLE_DEVICE: /* from GADGET, we can only change to NONE*/
|
||||
if (!vbus)
|
||||
role = USB_ROLE_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
dev_dbg(cdns->dev, "role %d -> %d\n", cdns->role, role);
|
||||
|
||||
return role;
|
||||
|
||||
not_otg:
|
||||
if (cdns3_is_host(cdns))
|
||||
role = USB_ROLE_HOST;
|
||||
if (cdns3_is_device(cdns))
|
||||
role = USB_ROLE_DEVICE;
|
||||
|
||||
return role;
|
||||
}
|
||||
|
||||
static int cdns3_idle_role_start(struct cdns3 *cdns)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cdns3_idle_role_stop(struct cdns3 *cdns)
|
||||
{
|
||||
/* Program Lane swap and bring PHY out of RESET */
|
||||
phy_reset(cdns->usb3_phy);
|
||||
}
|
||||
|
||||
static int cdns3_idle_init(struct cdns3 *cdns)
|
||||
{
|
||||
struct cdns3_role_driver *rdrv;
|
||||
|
||||
rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL);
|
||||
if (!rdrv)
|
||||
return -ENOMEM;
|
||||
|
||||
rdrv->start = cdns3_idle_role_start;
|
||||
rdrv->stop = cdns3_idle_role_stop;
|
||||
rdrv->state = CDNS3_ROLE_STATE_INACTIVE;
|
||||
rdrv->suspend = NULL;
|
||||
rdrv->resume = NULL;
|
||||
rdrv->name = "idle";
|
||||
|
||||
cdns->roles[USB_ROLE_NONE] = rdrv;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_hw_role_switch - switch roles based on HW state
|
||||
* @cdns3: controller
|
||||
*/
|
||||
int cdns3_hw_role_switch(struct cdns3 *cdns)
|
||||
{
|
||||
enum usb_role real_role, current_role;
|
||||
int ret = 0;
|
||||
|
||||
/* Do nothing if role based on syfs. */
|
||||
if (cdns->role_override)
|
||||
return 0;
|
||||
|
||||
pm_runtime_get_sync(cdns->dev);
|
||||
|
||||
current_role = cdns->role;
|
||||
real_role = cdsn3_hw_role_state_machine(cdns);
|
||||
|
||||
/* Do nothing if nothing changed */
|
||||
if (current_role == real_role)
|
||||
goto exit;
|
||||
|
||||
cdns3_role_stop(cdns);
|
||||
|
||||
dev_dbg(cdns->dev, "Switching role %d -> %d", current_role, real_role);
|
||||
|
||||
ret = cdns3_role_start(cdns, real_role);
|
||||
if (ret) {
|
||||
/* Back to current role */
|
||||
dev_err(cdns->dev, "set %d has failed, back to %d\n",
|
||||
real_role, current_role);
|
||||
ret = cdns3_role_start(cdns, current_role);
|
||||
if (ret)
|
||||
dev_err(cdns->dev, "back to %d failed too\n",
|
||||
current_role);
|
||||
}
|
||||
exit:
|
||||
pm_runtime_put_sync(cdns->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdsn3_role_get - get current role of controller.
|
||||
*
|
||||
* @dev: Pointer to device structure
|
||||
*
|
||||
* Returns role
|
||||
*/
|
||||
static enum usb_role cdns3_role_get(struct device *dev)
|
||||
{
|
||||
struct cdns3 *cdns = dev_get_drvdata(dev);
|
||||
|
||||
return cdns->role;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_role_set - set current role of controller.
|
||||
*
|
||||
* @dev: pointer to device object
|
||||
* @role - the previous role
|
||||
* Handles below events:
|
||||
* - Role switch for dual-role devices
|
||||
* - USB_ROLE_GADGET <--> USB_ROLE_NONE for peripheral-only devices
|
||||
*/
|
||||
static int cdns3_role_set(struct device *dev, enum usb_role role)
|
||||
{
|
||||
struct cdns3 *cdns = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
pm_runtime_get_sync(cdns->dev);
|
||||
|
||||
/*
|
||||
* FIXME: switch role framework should be extended to meet
|
||||
* requirements. Driver assumes that role can be controlled
|
||||
* by SW or HW. Temporary workaround is to use USB_ROLE_NONE to
|
||||
* switch from SW to HW control.
|
||||
*
|
||||
* For dr_mode == USB_DR_MODE_OTG:
|
||||
* if user sets USB_ROLE_HOST or USB_ROLE_DEVICE then driver
|
||||
* sets role_override flag and forces that role.
|
||||
* if user sets USB_ROLE_NONE, driver clears role_override and lets
|
||||
* HW state machine take over.
|
||||
*
|
||||
* For dr_mode != USB_DR_MODE_OTG:
|
||||
* Assumptions:
|
||||
* 1. Restricted user control between NONE and dr_mode.
|
||||
* 2. Driver doesn't need to rely on role_override flag.
|
||||
* 3. Driver needs to ensure that HW state machine is never called
|
||||
* if dr_mode != USB_DR_MODE_OTG.
|
||||
*/
|
||||
if (role == USB_ROLE_NONE)
|
||||
cdns->role_override = 0;
|
||||
else
|
||||
cdns->role_override = 1;
|
||||
|
||||
/*
|
||||
* HW state might have changed so driver need to trigger
|
||||
* HW state machine if dr_mode == USB_DR_MODE_OTG.
|
||||
*/
|
||||
if (!cdns->role_override && cdns->dr_mode == USB_DR_MODE_OTG) {
|
||||
cdns3_hw_role_switch(cdns);
|
||||
goto pm_put;
|
||||
}
|
||||
|
||||
if (cdns->role == role)
|
||||
goto pm_put;
|
||||
|
||||
if (cdns->dr_mode == USB_DR_MODE_HOST) {
|
||||
switch (role) {
|
||||
case USB_ROLE_NONE:
|
||||
case USB_ROLE_HOST:
|
||||
break;
|
||||
default:
|
||||
ret = -EPERM;
|
||||
goto pm_put;
|
||||
}
|
||||
}
|
||||
|
||||
if (cdns->dr_mode == USB_DR_MODE_PERIPHERAL) {
|
||||
switch (role) {
|
||||
case USB_ROLE_NONE:
|
||||
case USB_ROLE_DEVICE:
|
||||
break;
|
||||
default:
|
||||
ret = -EPERM;
|
||||
goto pm_put;
|
||||
}
|
||||
}
|
||||
|
||||
cdns3_role_stop(cdns);
|
||||
ret = cdns3_role_start(cdns, role);
|
||||
if (ret) {
|
||||
dev_err(cdns->dev, "set role %d has failed\n", role);
|
||||
ret = -EPERM;
|
||||
}
|
||||
|
||||
pm_put:
|
||||
pm_runtime_put_sync(cdns->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct usb_role_switch_desc cdns3_switch_desc = {
|
||||
.set = cdns3_role_set,
|
||||
.get = cdns3_role_get,
|
||||
.allow_userspace_control = true,
|
||||
};
|
||||
|
||||
/**
|
||||
* cdns3_probe - probe for cdns3 core device
|
||||
* @pdev: Pointer to cdns3 core platform device
|
||||
*
|
||||
* Returns 0 on success otherwise negative errno
|
||||
*/
|
||||
static int cdns3_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
struct cdns3 *cdns;
|
||||
void __iomem *regs;
|
||||
int ret;
|
||||
|
||||
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
|
||||
if (ret) {
|
||||
dev_err(dev, "error setting dma mask: %d\n", ret);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
cdns = devm_kzalloc(dev, sizeof(*cdns), GFP_KERNEL);
|
||||
if (!cdns)
|
||||
return -ENOMEM;
|
||||
|
||||
cdns->dev = dev;
|
||||
|
||||
platform_set_drvdata(pdev, cdns);
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "host");
|
||||
if (!res) {
|
||||
dev_err(dev, "missing host IRQ\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
cdns->xhci_res[0] = *res;
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "xhci");
|
||||
if (!res) {
|
||||
dev_err(dev, "couldn't get xhci resource\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
cdns->xhci_res[1] = *res;
|
||||
|
||||
cdns->dev_irq = platform_get_irq_byname(pdev, "peripheral");
|
||||
if (cdns->dev_irq == -EPROBE_DEFER)
|
||||
return cdns->dev_irq;
|
||||
|
||||
if (cdns->dev_irq < 0)
|
||||
dev_err(dev, "couldn't get peripheral irq\n");
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dev");
|
||||
regs = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(regs)) {
|
||||
dev_err(dev, "couldn't iomap dev resource\n");
|
||||
return PTR_ERR(regs);
|
||||
}
|
||||
cdns->dev_regs = regs;
|
||||
|
||||
cdns->otg_irq = platform_get_irq_byname(pdev, "otg");
|
||||
if (cdns->otg_irq == -EPROBE_DEFER)
|
||||
return cdns->otg_irq;
|
||||
|
||||
if (cdns->otg_irq < 0) {
|
||||
dev_err(dev, "couldn't get otg irq\n");
|
||||
return cdns->otg_irq;
|
||||
}
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "otg");
|
||||
if (!res) {
|
||||
dev_err(dev, "couldn't get otg resource\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
cdns->otg_res = *res;
|
||||
|
||||
mutex_init(&cdns->mutex);
|
||||
|
||||
cdns->usb2_phy = devm_phy_optional_get(dev, "cdns3,usb2-phy");
|
||||
if (IS_ERR(cdns->usb2_phy))
|
||||
return PTR_ERR(cdns->usb2_phy);
|
||||
|
||||
ret = phy_init(cdns->usb2_phy);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
cdns->usb3_phy = devm_phy_optional_get(dev, "cdns3,usb3-phy");
|
||||
if (IS_ERR(cdns->usb3_phy))
|
||||
return PTR_ERR(cdns->usb3_phy);
|
||||
|
||||
ret = phy_init(cdns->usb3_phy);
|
||||
if (ret)
|
||||
goto err1;
|
||||
|
||||
ret = phy_power_on(cdns->usb2_phy);
|
||||
if (ret)
|
||||
goto err2;
|
||||
|
||||
ret = phy_power_on(cdns->usb3_phy);
|
||||
if (ret)
|
||||
goto err3;
|
||||
|
||||
cdns->role_sw = usb_role_switch_register(dev, &cdns3_switch_desc);
|
||||
if (IS_ERR(cdns->role_sw)) {
|
||||
ret = PTR_ERR(cdns->role_sw);
|
||||
dev_warn(dev, "Unable to register Role Switch\n");
|
||||
goto err4;
|
||||
}
|
||||
|
||||
ret = cdns3_drd_init(cdns);
|
||||
if (ret)
|
||||
goto err5;
|
||||
|
||||
ret = cdns3_core_init_role(cdns);
|
||||
if (ret)
|
||||
goto err5;
|
||||
|
||||
device_set_wakeup_capable(dev, true);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
/*
|
||||
* The controller needs less time between bus and controller suspend,
|
||||
* and we also needs a small delay to avoid frequently entering low
|
||||
* power mode.
|
||||
*/
|
||||
pm_runtime_set_autosuspend_delay(dev, 20);
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
dev_dbg(dev, "Cadence USB3 core: probe succeed\n");
|
||||
|
||||
return 0;
|
||||
err5:
|
||||
cdns3_drd_exit(cdns);
|
||||
usb_role_switch_unregister(cdns->role_sw);
|
||||
err4:
|
||||
phy_power_off(cdns->usb3_phy);
|
||||
|
||||
err3:
|
||||
phy_power_off(cdns->usb2_phy);
|
||||
err2:
|
||||
phy_exit(cdns->usb3_phy);
|
||||
err1:
|
||||
phy_exit(cdns->usb2_phy);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_remove - unbind drd driver and clean up
|
||||
* @pdev: Pointer to Linux platform device
|
||||
*
|
||||
* Returns 0 on success otherwise negative errno
|
||||
*/
|
||||
static int cdns3_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct cdns3 *cdns = platform_get_drvdata(pdev);
|
||||
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
cdns3_exit_roles(cdns);
|
||||
usb_role_switch_unregister(cdns->role_sw);
|
||||
phy_power_off(cdns->usb2_phy);
|
||||
phy_power_off(cdns->usb3_phy);
|
||||
phy_exit(cdns->usb2_phy);
|
||||
phy_exit(cdns->usb3_phy);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
||||
static int cdns3_suspend(struct device *dev)
|
||||
{
|
||||
struct cdns3 *cdns = dev_get_drvdata(dev);
|
||||
unsigned long flags;
|
||||
|
||||
if (cdns->role == USB_ROLE_HOST)
|
||||
return 0;
|
||||
|
||||
if (pm_runtime_status_suspended(dev))
|
||||
pm_runtime_resume(dev);
|
||||
|
||||
if (cdns->roles[cdns->role]->suspend) {
|
||||
spin_lock_irqsave(&cdns->gadget_dev->lock, flags);
|
||||
cdns->roles[cdns->role]->suspend(cdns, false);
|
||||
spin_unlock_irqrestore(&cdns->gadget_dev->lock, flags);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdns3_resume(struct device *dev)
|
||||
{
|
||||
struct cdns3 *cdns = dev_get_drvdata(dev);
|
||||
unsigned long flags;
|
||||
|
||||
if (cdns->role == USB_ROLE_HOST)
|
||||
return 0;
|
||||
|
||||
if (cdns->roles[cdns->role]->resume) {
|
||||
spin_lock_irqsave(&cdns->gadget_dev->lock, flags);
|
||||
cdns->roles[cdns->role]->resume(cdns, false);
|
||||
spin_unlock_irqrestore(&cdns->gadget_dev->lock, flags);
|
||||
}
|
||||
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops cdns3_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(cdns3_suspend, cdns3_resume)
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id of_cdns3_match[] = {
|
||||
{ .compatible = "cdns,usb3" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_cdns3_match);
|
||||
#endif
|
||||
|
||||
static struct platform_driver cdns3_driver = {
|
||||
.probe = cdns3_probe,
|
||||
.remove = cdns3_remove,
|
||||
.driver = {
|
||||
.name = "cdns-usb3",
|
||||
.of_match_table = of_match_ptr(of_cdns3_match),
|
||||
.pm = &cdns3_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(cdns3_driver);
|
||||
|
||||
MODULE_ALIAS("platform:cdns3");
|
||||
MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("Cadence USB3 DRD Controller Driver");
|
98
drivers/usb/cdns3/core.h
Normal file
98
drivers/usb/cdns3/core.h
Normal file
@ -0,0 +1,98 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Cadence USBSS DRD Header File.
|
||||
*
|
||||
* Copyright (C) 2017-2018 NXP
|
||||
* Copyright (C) 2018-2019 Cadence.
|
||||
*
|
||||
* Authors: Peter Chen <peter.chen@nxp.com>
|
||||
* Pawel Laszczak <pawell@cadence.com>
|
||||
*/
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/usb/role.h>
|
||||
|
||||
#ifndef __LINUX_CDNS3_CORE_H
|
||||
#define __LINUX_CDNS3_CORE_H
|
||||
|
||||
struct cdns3;
|
||||
|
||||
/**
|
||||
* struct cdns3_role_driver - host/gadget role driver
|
||||
* @start: start this role
|
||||
* @stop: stop this role
|
||||
* @suspend: suspend callback for this role
|
||||
* @resume: resume callback for this role
|
||||
* @irq: irq handler for this role
|
||||
* @name: role name string (host/gadget)
|
||||
* @state: current state
|
||||
*/
|
||||
struct cdns3_role_driver {
|
||||
int (*start)(struct cdns3 *cdns);
|
||||
void (*stop)(struct cdns3 *cdns);
|
||||
int (*suspend)(struct cdns3 *cdns, bool do_wakeup);
|
||||
int (*resume)(struct cdns3 *cdns, bool hibernated);
|
||||
const char *name;
|
||||
#define CDNS3_ROLE_STATE_INACTIVE 0
|
||||
#define CDNS3_ROLE_STATE_ACTIVE 1
|
||||
int state;
|
||||
};
|
||||
|
||||
#define CDNS3_XHCI_RESOURCES_NUM 2
|
||||
/**
|
||||
* struct cdns3 - Representation of Cadence USB3 DRD controller.
|
||||
* @dev: pointer to Cadence device struct
|
||||
* @xhci_regs: pointer to base of xhci registers
|
||||
* @xhci_res: the resource for xhci
|
||||
* @dev_regs: pointer to base of dev registers
|
||||
* @otg_res: the resource for otg
|
||||
* @otg_v0_regs: pointer to base of v0 otg registers
|
||||
* @otg_v1_regs: pointer to base of v1 otg registers
|
||||
* @otg_regs: pointer to base of otg registers
|
||||
* @otg_irq: irq number for otg controller
|
||||
* @dev_irq: irq number for device controller
|
||||
* @roles: array of supported roles for this controller
|
||||
* @role: current role
|
||||
* @host_dev: the child host device pointer for cdns3 core
|
||||
* @gadget_dev: the child gadget device pointer for cdns3 core
|
||||
* @usb2_phy: pointer to USB2 PHY
|
||||
* @usb3_phy: pointer to USB3 PHY
|
||||
* @mutex: the mutex for concurrent code at driver
|
||||
* @dr_mode: supported mode of operation it can be only Host, only Device
|
||||
* or OTG mode that allow to switch between Device and Host mode.
|
||||
* This field based on firmware setting, kernel configuration
|
||||
* and hardware configuration.
|
||||
* @role_sw: pointer to role switch object.
|
||||
* @role_override: set 1 if role rely on SW.
|
||||
*/
|
||||
struct cdns3 {
|
||||
struct device *dev;
|
||||
void __iomem *xhci_regs;
|
||||
struct resource xhci_res[CDNS3_XHCI_RESOURCES_NUM];
|
||||
struct cdns3_usb_regs __iomem *dev_regs;
|
||||
|
||||
struct resource otg_res;
|
||||
struct cdns3_otg_legacy_regs *otg_v0_regs;
|
||||
struct cdns3_otg_regs *otg_v1_regs;
|
||||
struct cdns3_otg_common_regs *otg_regs;
|
||||
#define CDNS3_CONTROLLER_V0 0
|
||||
#define CDNS3_CONTROLLER_V1 1
|
||||
u32 version;
|
||||
|
||||
int otg_irq;
|
||||
int dev_irq;
|
||||
struct cdns3_role_driver *roles[USB_ROLE_DEVICE + 1];
|
||||
enum usb_role role;
|
||||
struct platform_device *host_dev;
|
||||
struct cdns3_device *gadget_dev;
|
||||
struct phy *usb2_phy;
|
||||
struct phy *usb3_phy;
|
||||
/* mutext used in workqueue*/
|
||||
struct mutex mutex;
|
||||
enum usb_dr_mode dr_mode;
|
||||
struct usb_role_switch *role_sw;
|
||||
int role_override;
|
||||
};
|
||||
|
||||
int cdns3_hw_role_switch(struct cdns3 *cdns);
|
||||
|
||||
#endif /* __LINUX_CDNS3_CORE_H */
|
161
drivers/usb/cdns3/debug.h
Normal file
161
drivers/usb/cdns3/debug.h
Normal file
@ -0,0 +1,161 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Cadence USBSS DRD Driver.
|
||||
* Debug header file.
|
||||
*
|
||||
* Copyright (C) 2018-2019 Cadence.
|
||||
*
|
||||
* Author: Pawel Laszczak <pawell@cadence.com>
|
||||
*/
|
||||
#ifndef __LINUX_CDNS3_DEBUG
|
||||
#define __LINUX_CDNS3_DEBUG
|
||||
|
||||
#include "core.h"
|
||||
|
||||
static inline char *cdns3_decode_usb_irq(char *str,
|
||||
enum usb_device_speed speed,
|
||||
u32 usb_ists)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = sprintf(str, "IRQ %08x = ", usb_ists);
|
||||
|
||||
if (usb_ists & (USB_ISTS_CON2I | USB_ISTS_CONI)) {
|
||||
ret += sprintf(str + ret, "Connection %s\n",
|
||||
usb_speed_string(speed));
|
||||
}
|
||||
if (usb_ists & USB_ISTS_DIS2I || usb_ists & USB_ISTS_DISI)
|
||||
ret += sprintf(str + ret, "Disconnection ");
|
||||
if (usb_ists & USB_ISTS_L2ENTI)
|
||||
ret += sprintf(str + ret, "suspended ");
|
||||
if (usb_ists & USB_ISTS_L1ENTI)
|
||||
ret += sprintf(str + ret, "L1 enter ");
|
||||
if (usb_ists & USB_ISTS_L1EXTI)
|
||||
ret += sprintf(str + ret, "L1 exit ");
|
||||
if (usb_ists & USB_ISTS_L2ENTI)
|
||||
ret += sprintf(str + ret, "L2 enter ");
|
||||
if (usb_ists & USB_ISTS_L2EXTI)
|
||||
ret += sprintf(str + ret, "L2 exit ");
|
||||
if (usb_ists & USB_ISTS_U3EXTI)
|
||||
ret += sprintf(str + ret, "U3 exit ");
|
||||
if (usb_ists & USB_ISTS_UWRESI)
|
||||
ret += sprintf(str + ret, "Warm Reset ");
|
||||
if (usb_ists & USB_ISTS_UHRESI)
|
||||
ret += sprintf(str + ret, "Hot Reset ");
|
||||
if (usb_ists & USB_ISTS_U2RESI)
|
||||
ret += sprintf(str + ret, "Reset");
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static inline char *cdns3_decode_ep_irq(char *str,
|
||||
u32 ep_sts,
|
||||
const char *ep_name)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = sprintf(str, "IRQ for %s: %08x ", ep_name, ep_sts);
|
||||
|
||||
if (ep_sts & EP_STS_SETUP)
|
||||
ret += sprintf(str + ret, "SETUP ");
|
||||
if (ep_sts & EP_STS_IOC)
|
||||
ret += sprintf(str + ret, "IOC ");
|
||||
if (ep_sts & EP_STS_ISP)
|
||||
ret += sprintf(str + ret, "ISP ");
|
||||
if (ep_sts & EP_STS_DESCMIS)
|
||||
ret += sprintf(str + ret, "DESCMIS ");
|
||||
if (ep_sts & EP_STS_STREAMR)
|
||||
ret += sprintf(str + ret, "STREAMR ");
|
||||
if (ep_sts & EP_STS_MD_EXIT)
|
||||
ret += sprintf(str + ret, "MD_EXIT ");
|
||||
if (ep_sts & EP_STS_TRBERR)
|
||||
ret += sprintf(str + ret, "TRBERR ");
|
||||
if (ep_sts & EP_STS_NRDY)
|
||||
ret += sprintf(str + ret, "NRDY ");
|
||||
if (ep_sts & EP_STS_PRIME)
|
||||
ret += sprintf(str + ret, "PRIME ");
|
||||
if (ep_sts & EP_STS_SIDERR)
|
||||
ret += sprintf(str + ret, "SIDERRT ");
|
||||
if (ep_sts & EP_STS_OUTSMM)
|
||||
ret += sprintf(str + ret, "OUTSMM ");
|
||||
if (ep_sts & EP_STS_ISOERR)
|
||||
ret += sprintf(str + ret, "ISOERR ");
|
||||
if (ep_sts & EP_STS_IOT)
|
||||
ret += sprintf(str + ret, "IOT ");
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static inline char *cdns3_decode_epx_irq(char *str,
|
||||
char *ep_name,
|
||||
u32 ep_sts)
|
||||
{
|
||||
return cdns3_decode_ep_irq(str, ep_sts, ep_name);
|
||||
}
|
||||
|
||||
static inline char *cdns3_decode_ep0_irq(char *str,
|
||||
int dir,
|
||||
u32 ep_sts)
|
||||
{
|
||||
return cdns3_decode_ep_irq(str, ep_sts,
|
||||
dir ? "ep0IN" : "ep0OUT");
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug a transfer ring.
|
||||
*
|
||||
* Prints out all TRBs in the endpoint ring, even those after the Link TRB.
|
||||
*.
|
||||
*/
|
||||
static inline char *cdns3_dbg_ring(struct cdns3_endpoint *priv_ep,
|
||||
struct cdns3_trb *ring, char *str)
|
||||
{
|
||||
dma_addr_t addr = priv_ep->trb_pool_dma;
|
||||
struct cdns3_trb *trb;
|
||||
int trb_per_sector;
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
trb_per_sector = GET_TRBS_PER_SEGMENT(priv_ep->type);
|
||||
|
||||
trb = &priv_ep->trb_pool[priv_ep->dequeue];
|
||||
ret += sprintf(str + ret, "\n\t\tRing contents for %s:", priv_ep->name);
|
||||
|
||||
ret += sprintf(str + ret,
|
||||
"\n\t\tRing deq index: %d, trb: %p (virt), 0x%llx (dma)\n",
|
||||
priv_ep->dequeue, trb,
|
||||
(unsigned long long)cdns3_trb_virt_to_dma(priv_ep, trb));
|
||||
|
||||
trb = &priv_ep->trb_pool[priv_ep->enqueue];
|
||||
ret += sprintf(str + ret,
|
||||
"\t\tRing enq index: %d, trb: %p (virt), 0x%llx (dma)\n",
|
||||
priv_ep->enqueue, trb,
|
||||
(unsigned long long)cdns3_trb_virt_to_dma(priv_ep, trb));
|
||||
|
||||
ret += sprintf(str + ret,
|
||||
"\t\tfree trbs: %d, CCS=%d, PCS=%d\n",
|
||||
priv_ep->free_trbs, priv_ep->ccs, priv_ep->pcs);
|
||||
|
||||
if (trb_per_sector > TRBS_PER_SEGMENT)
|
||||
trb_per_sector = TRBS_PER_SEGMENT;
|
||||
|
||||
if (trb_per_sector > TRBS_PER_SEGMENT) {
|
||||
sprintf(str + ret, "\t\tTo big transfer ring %d\n",
|
||||
trb_per_sector);
|
||||
return str;
|
||||
}
|
||||
|
||||
for (i = 0; i < trb_per_sector; ++i) {
|
||||
trb = &ring[i];
|
||||
ret += sprintf(str + ret,
|
||||
"\t\t@%pad %08x %08x %08x\n", &addr,
|
||||
le32_to_cpu(trb->buffer),
|
||||
le32_to_cpu(trb->length),
|
||||
le32_to_cpu(trb->control));
|
||||
addr += sizeof(*trb);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
#endif /*__LINUX_CDNS3_DEBUG*/
|
381
drivers/usb/cdns3/drd.c
Normal file
381
drivers/usb/cdns3/drd.c
Normal file
@ -0,0 +1,381 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Cadence USBSS DRD Driver.
|
||||
*
|
||||
* Copyright (C) 2018-2019 Cadence.
|
||||
* Copyright (C) 2019 Texas Instruments
|
||||
*
|
||||
* Author: Pawel Laszczak <pawell@cadence.com>
|
||||
* Roger Quadros <rogerq@ti.com>
|
||||
*
|
||||
*
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/usb/otg.h>
|
||||
|
||||
#include "gadget.h"
|
||||
#include "drd.h"
|
||||
#include "core.h"
|
||||
|
||||
/**
|
||||
* cdns3_set_mode - change mode of OTG Core
|
||||
* @cdns: pointer to context structure
|
||||
* @mode: selected mode from cdns_role
|
||||
*
|
||||
* Returns 0 on success otherwise negative errno
|
||||
*/
|
||||
int cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode)
|
||||
{
|
||||
int ret = 0;
|
||||
u32 reg;
|
||||
|
||||
switch (mode) {
|
||||
case USB_DR_MODE_PERIPHERAL:
|
||||
break;
|
||||
case USB_DR_MODE_HOST:
|
||||
break;
|
||||
case USB_DR_MODE_OTG:
|
||||
dev_dbg(cdns->dev, "Set controller to OTG mode\n");
|
||||
if (cdns->version == CDNS3_CONTROLLER_V1) {
|
||||
reg = readl(&cdns->otg_v1_regs->override);
|
||||
reg |= OVERRIDE_IDPULLUP;
|
||||
writel(reg, &cdns->otg_v1_regs->override);
|
||||
} else {
|
||||
reg = readl(&cdns->otg_v0_regs->ctrl1);
|
||||
reg |= OVERRIDE_IDPULLUP_V0;
|
||||
writel(reg, &cdns->otg_v0_regs->ctrl1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Hardware specification says: "ID_VALUE must be valid within
|
||||
* 50ms after idpullup is set to '1" so driver must wait
|
||||
* 50ms before reading this pin.
|
||||
*/
|
||||
usleep_range(50000, 60000);
|
||||
break;
|
||||
default:
|
||||
dev_err(cdns->dev, "Unsupported mode of operation %d\n", mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cdns3_get_id(struct cdns3 *cdns)
|
||||
{
|
||||
int id;
|
||||
|
||||
id = readl(&cdns->otg_regs->sts) & OTGSTS_ID_VALUE;
|
||||
dev_dbg(cdns->dev, "OTG ID: %d", id);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
int cdns3_get_vbus(struct cdns3 *cdns)
|
||||
{
|
||||
int vbus;
|
||||
|
||||
vbus = !!(readl(&cdns->otg_regs->sts) & OTGSTS_VBUS_VALID);
|
||||
dev_dbg(cdns->dev, "OTG VBUS: %d", vbus);
|
||||
|
||||
return vbus;
|
||||
}
|
||||
|
||||
int cdns3_is_host(struct cdns3 *cdns)
|
||||
{
|
||||
if (cdns->dr_mode == USB_DR_MODE_HOST)
|
||||
return 1;
|
||||
else if (!cdns3_get_id(cdns))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cdns3_is_device(struct cdns3 *cdns)
|
||||
{
|
||||
if (cdns->dr_mode == USB_DR_MODE_PERIPHERAL)
|
||||
return 1;
|
||||
else if (cdns->dr_mode == USB_DR_MODE_OTG)
|
||||
if (cdns3_get_id(cdns))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_otg_disable_irq - Disable all OTG interrupts
|
||||
* @cdns: Pointer to controller context structure
|
||||
*/
|
||||
static void cdns3_otg_disable_irq(struct cdns3 *cdns)
|
||||
{
|
||||
writel(0, &cdns->otg_regs->ien);
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_otg_enable_irq - enable id and sess_valid interrupts
|
||||
* @cdns: Pointer to controller context structure
|
||||
*/
|
||||
static void cdns3_otg_enable_irq(struct cdns3 *cdns)
|
||||
{
|
||||
writel(OTGIEN_ID_CHANGE_INT | OTGIEN_VBUSVALID_RISE_INT |
|
||||
OTGIEN_VBUSVALID_FALL_INT, &cdns->otg_regs->ien);
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_drd_switch_host - start/stop host
|
||||
* @cdns: Pointer to controller context structure
|
||||
* @on: 1 for start, 0 for stop
|
||||
*
|
||||
* Returns 0 on success otherwise negative errno
|
||||
*/
|
||||
int cdns3_drd_switch_host(struct cdns3 *cdns, int on)
|
||||
{
|
||||
int ret, val;
|
||||
u32 reg = OTGCMD_OTG_DIS;
|
||||
|
||||
/* switch OTG core */
|
||||
if (on) {
|
||||
writel(OTGCMD_HOST_BUS_REQ | reg, &cdns->otg_regs->cmd);
|
||||
|
||||
dev_dbg(cdns->dev, "Waiting till Host mode is turned on\n");
|
||||
ret = readl_poll_timeout_atomic(&cdns->otg_regs->sts, val,
|
||||
val & OTGSTS_XHCI_READY,
|
||||
1, 100000);
|
||||
if (ret) {
|
||||
dev_err(cdns->dev, "timeout waiting for xhci_ready\n");
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
|
||||
OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
|
||||
&cdns->otg_regs->cmd);
|
||||
/* Waiting till H_IDLE state.*/
|
||||
readl_poll_timeout_atomic(&cdns->otg_regs->state, val,
|
||||
!(val & OTGSTATE_HOST_STATE_MASK),
|
||||
1, 2000000);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_drd_switch_gadget - start/stop gadget
|
||||
* @cdns: Pointer to controller context structure
|
||||
* @on: 1 for start, 0 for stop
|
||||
*
|
||||
* Returns 0 on success otherwise negative errno
|
||||
*/
|
||||
int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on)
|
||||
{
|
||||
int ret, val;
|
||||
u32 reg = OTGCMD_OTG_DIS;
|
||||
|
||||
/* switch OTG core */
|
||||
if (on) {
|
||||
writel(OTGCMD_DEV_BUS_REQ | reg, &cdns->otg_regs->cmd);
|
||||
|
||||
dev_dbg(cdns->dev, "Waiting till Device mode is turned on\n");
|
||||
|
||||
ret = readl_poll_timeout_atomic(&cdns->otg_regs->sts, val,
|
||||
val & OTGSTS_DEV_READY,
|
||||
1, 100000);
|
||||
if (ret) {
|
||||
dev_err(cdns->dev, "timeout waiting for dev_ready\n");
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* driver should wait at least 10us after disabling Device
|
||||
* before turning-off Device (DEV_BUS_DROP)
|
||||
*/
|
||||
usleep_range(20, 30);
|
||||
writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
|
||||
OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
|
||||
&cdns->otg_regs->cmd);
|
||||
/* Waiting till DEV_IDLE state.*/
|
||||
readl_poll_timeout_atomic(&cdns->otg_regs->state, val,
|
||||
!(val & OTGSTATE_DEV_STATE_MASK),
|
||||
1, 2000000);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_init_otg_mode - initialize drd controller
|
||||
* @cdns: Pointer to controller context structure
|
||||
*
|
||||
* Returns 0 on success otherwise negative errno
|
||||
*/
|
||||
static int cdns3_init_otg_mode(struct cdns3 *cdns)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
cdns3_otg_disable_irq(cdns);
|
||||
/* clear all interrupts */
|
||||
writel(~0, &cdns->otg_regs->ivect);
|
||||
|
||||
ret = cdns3_set_mode(cdns, USB_DR_MODE_OTG);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
cdns3_otg_enable_irq(cdns);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_drd_update_mode - initialize mode of operation
|
||||
* @cdns: Pointer to controller context structure
|
||||
*
|
||||
* Returns 0 on success otherwise negative errno
|
||||
*/
|
||||
int cdns3_drd_update_mode(struct cdns3 *cdns)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
switch (cdns->dr_mode) {
|
||||
case USB_DR_MODE_PERIPHERAL:
|
||||
ret = cdns3_set_mode(cdns, USB_DR_MODE_PERIPHERAL);
|
||||
break;
|
||||
case USB_DR_MODE_HOST:
|
||||
ret = cdns3_set_mode(cdns, USB_DR_MODE_HOST);
|
||||
break;
|
||||
case USB_DR_MODE_OTG:
|
||||
ret = cdns3_init_otg_mode(cdns);
|
||||
break;
|
||||
default:
|
||||
dev_err(cdns->dev, "Unsupported mode of operation %d\n",
|
||||
cdns->dr_mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t cdns3_drd_thread_irq(int irq, void *data)
|
||||
{
|
||||
struct cdns3 *cdns = data;
|
||||
|
||||
cdns3_hw_role_switch(cdns);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_drd_irq - interrupt handler for OTG events
|
||||
*
|
||||
* @irq: irq number for cdns3 core device
|
||||
* @data: structure of cdns3
|
||||
*
|
||||
* Returns IRQ_HANDLED or IRQ_NONE
|
||||
*/
|
||||
static irqreturn_t cdns3_drd_irq(int irq, void *data)
|
||||
{
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
struct cdns3 *cdns = data;
|
||||
u32 reg;
|
||||
|
||||
if (cdns->dr_mode != USB_DR_MODE_OTG)
|
||||
return ret;
|
||||
|
||||
reg = readl(&cdns->otg_regs->ivect);
|
||||
|
||||
if (!reg)
|
||||
return ret;
|
||||
|
||||
if (reg & OTGIEN_ID_CHANGE_INT) {
|
||||
dev_dbg(cdns->dev, "OTG IRQ: new ID: %d\n",
|
||||
cdns3_get_id(cdns));
|
||||
|
||||
ret = IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
if (reg & (OTGIEN_VBUSVALID_RISE_INT | OTGIEN_VBUSVALID_FALL_INT)) {
|
||||
dev_dbg(cdns->dev, "OTG IRQ: new VBUS: %d\n",
|
||||
cdns3_get_vbus(cdns));
|
||||
|
||||
ret = IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
writel(~0, &cdns->otg_regs->ivect);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cdns3_drd_init(struct cdns3 *cdns)
|
||||
{
|
||||
void __iomem *regs;
|
||||
int ret = 0;
|
||||
u32 state;
|
||||
|
||||
regs = devm_ioremap_resource(cdns->dev, &cdns->otg_res);
|
||||
if (IS_ERR(regs))
|
||||
return PTR_ERR(regs);
|
||||
|
||||
/* Detection of DRD version. Controller has been released
|
||||
* in two versions. Both are similar, but they have same changes
|
||||
* in register maps.
|
||||
* The first register in old version is command register and it's read
|
||||
* only, so driver should read 0 from it. On the other hand, in v1
|
||||
* the first register contains device ID number which is not set to 0.
|
||||
* Driver uses this fact to detect the proper version of
|
||||
* controller.
|
||||
*/
|
||||
cdns->otg_v0_regs = regs;
|
||||
if (!readl(&cdns->otg_v0_regs->cmd)) {
|
||||
cdns->version = CDNS3_CONTROLLER_V0;
|
||||
cdns->otg_v1_regs = NULL;
|
||||
cdns->otg_regs = regs;
|
||||
writel(1, &cdns->otg_v0_regs->simulate);
|
||||
dev_info(cdns->dev, "DRD version v0 (%08x)\n",
|
||||
readl(&cdns->otg_v0_regs->version));
|
||||
} else {
|
||||
cdns->otg_v0_regs = NULL;
|
||||
cdns->otg_v1_regs = regs;
|
||||
cdns->otg_regs = (void *)&cdns->otg_v1_regs->cmd;
|
||||
cdns->version = CDNS3_CONTROLLER_V1;
|
||||
writel(1, &cdns->otg_v1_regs->simulate);
|
||||
dev_info(cdns->dev, "DRD version v1 (ID: %08x, rev: %08x)\n",
|
||||
readl(&cdns->otg_v1_regs->did),
|
||||
readl(&cdns->otg_v1_regs->rid));
|
||||
}
|
||||
|
||||
state = OTGSTS_STRAP(readl(&cdns->otg_regs->sts));
|
||||
|
||||
/* Update dr_mode according to STRAP configuration. */
|
||||
cdns->dr_mode = USB_DR_MODE_OTG;
|
||||
if (state == OTGSTS_STRAP_HOST) {
|
||||
dev_dbg(cdns->dev, "Controller strapped to HOST\n");
|
||||
cdns->dr_mode = USB_DR_MODE_HOST;
|
||||
} else if (state == OTGSTS_STRAP_GADGET) {
|
||||
dev_dbg(cdns->dev, "Controller strapped to PERIPHERAL\n");
|
||||
cdns->dr_mode = USB_DR_MODE_PERIPHERAL;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(cdns->dev, cdns->otg_irq,
|
||||
cdns3_drd_irq,
|
||||
cdns3_drd_thread_irq,
|
||||
IRQF_SHARED,
|
||||
dev_name(cdns->dev), cdns);
|
||||
|
||||
if (ret) {
|
||||
dev_err(cdns->dev, "couldn't get otg_irq\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
state = readl(&cdns->otg_regs->sts);
|
||||
if (OTGSTS_OTG_NRDY(state) != 0) {
|
||||
dev_err(cdns->dev, "Cadence USB3 OTG device not ready\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cdns3_drd_exit(struct cdns3 *cdns)
|
||||
{
|
||||
cdns3_otg_disable_irq(cdns);
|
||||
return 0;
|
||||
}
|
167
drivers/usb/cdns3/drd.h
Normal file
167
drivers/usb/cdns3/drd.h
Normal file
@ -0,0 +1,167 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Cadence USB3 DRD header file.
|
||||
*
|
||||
* Copyright (C) 2018-2019 Cadence.
|
||||
*
|
||||
* Author: Pawel Laszczak <pawell@cadence.com>
|
||||
*/
|
||||
#ifndef __LINUX_CDNS3_DRD
|
||||
#define __LINUX_CDNS3_DRD
|
||||
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include "core.h"
|
||||
|
||||
/* DRD register interface for version v1. */
|
||||
struct cdns3_otg_regs {
|
||||
__le32 did;
|
||||
__le32 rid;
|
||||
__le32 capabilities;
|
||||
__le32 reserved1;
|
||||
__le32 cmd;
|
||||
__le32 sts;
|
||||
__le32 state;
|
||||
__le32 reserved2;
|
||||
__le32 ien;
|
||||
__le32 ivect;
|
||||
__le32 refclk;
|
||||
__le32 tmr;
|
||||
__le32 reserved3[4];
|
||||
__le32 simulate;
|
||||
__le32 override;
|
||||
__le32 susp_ctrl;
|
||||
__le32 reserved4;
|
||||
__le32 anasts;
|
||||
__le32 adp_ramp_time;
|
||||
__le32 ctrl1;
|
||||
__le32 ctrl2;
|
||||
};
|
||||
|
||||
/* DRD register interface for version v0. */
|
||||
struct cdns3_otg_legacy_regs {
|
||||
__le32 cmd;
|
||||
__le32 sts;
|
||||
__le32 state;
|
||||
__le32 refclk;
|
||||
__le32 ien;
|
||||
__le32 ivect;
|
||||
__le32 reserved1[3];
|
||||
__le32 tmr;
|
||||
__le32 reserved2[2];
|
||||
__le32 version;
|
||||
__le32 capabilities;
|
||||
__le32 reserved3[2];
|
||||
__le32 simulate;
|
||||
__le32 reserved4[5];
|
||||
__le32 ctrl1;
|
||||
};
|
||||
|
||||
/*
|
||||
* Common registers interface for both version of DRD.
|
||||
*/
|
||||
struct cdns3_otg_common_regs {
|
||||
__le32 cmd;
|
||||
__le32 sts;
|
||||
__le32 state;
|
||||
__le32 different1;
|
||||
__le32 ien;
|
||||
__le32 ivect;
|
||||
};
|
||||
|
||||
/* CDNS_RID - bitmasks */
|
||||
#define CDNS_RID(p) ((p) & GENMASK(15, 0))
|
||||
|
||||
/* CDNS_VID - bitmasks */
|
||||
#define CDNS_DID(p) ((p) & GENMASK(31, 0))
|
||||
|
||||
/* OTGCMD - bitmasks */
|
||||
/* "Request the bus for Device mode. */
|
||||
#define OTGCMD_DEV_BUS_REQ BIT(0)
|
||||
/* Request the bus for Host mode */
|
||||
#define OTGCMD_HOST_BUS_REQ BIT(1)
|
||||
/* Enable OTG mode. */
|
||||
#define OTGCMD_OTG_EN BIT(2)
|
||||
/* Disable OTG mode */
|
||||
#define OTGCMD_OTG_DIS BIT(3)
|
||||
/*"Configure OTG as A-Device. */
|
||||
#define OTGCMD_A_DEV_EN BIT(4)
|
||||
/*"Configure OTG as A-Device. */
|
||||
#define OTGCMD_A_DEV_DIS BIT(5)
|
||||
/* Drop the bus for Device mod e. */
|
||||
#define OTGCMD_DEV_BUS_DROP BIT(8)
|
||||
/* Drop the bus for Host mode*/
|
||||
#define OTGCMD_HOST_BUS_DROP BIT(9)
|
||||
/* Power Down USBSS-DEV. */
|
||||
#define OTGCMD_DEV_POWER_OFF BIT(11)
|
||||
/* Power Down CDNSXHCI. */
|
||||
#define OTGCMD_HOST_POWER_OFF BIT(12)
|
||||
|
||||
/* OTGIEN - bitmasks */
|
||||
/* ID change interrupt enable */
|
||||
#define OTGIEN_ID_CHANGE_INT BIT(0)
|
||||
/* Vbusvalid fall detected interrupt enable.*/
|
||||
#define OTGIEN_VBUSVALID_RISE_INT BIT(4)
|
||||
/* Vbusvalid fall detected interrupt enable */
|
||||
#define OTGIEN_VBUSVALID_FALL_INT BIT(5)
|
||||
|
||||
/* OTGSTS - bitmasks */
|
||||
/*
|
||||
* Current value of the ID pin. It is only valid when idpullup in
|
||||
* OTGCTRL1_TYPE register is set to '1'.
|
||||
*/
|
||||
#define OTGSTS_ID_VALUE BIT(0)
|
||||
/* Current value of the vbus_valid */
|
||||
#define OTGSTS_VBUS_VALID BIT(1)
|
||||
/* Current value of the b_sess_vld */
|
||||
#define OTGSTS_SESSION_VALID BIT(2)
|
||||
/*Device mode is active*/
|
||||
#define OTGSTS_DEV_ACTIVE BIT(3)
|
||||
/* Host mode is active. */
|
||||
#define OTGSTS_HOST_ACTIVE BIT(4)
|
||||
/* OTG Controller not ready. */
|
||||
#define OTGSTS_OTG_NRDY_MASK BIT(11)
|
||||
#define OTGSTS_OTG_NRDY(p) ((p) & OTGSTS_OTG_NRDY_MASK)
|
||||
/*
|
||||
* Value of the strap pins.
|
||||
* 000 - no default configuration
|
||||
* 010 - Controller initiall configured as Host
|
||||
* 100 - Controller initially configured as Device
|
||||
*/
|
||||
#define OTGSTS_STRAP(p) (((p) & GENMASK(14, 12)) >> 12)
|
||||
#define OTGSTS_STRAP_NO_DEFAULT_CFG 0x00
|
||||
#define OTGSTS_STRAP_HOST_OTG 0x01
|
||||
#define OTGSTS_STRAP_HOST 0x02
|
||||
#define OTGSTS_STRAP_GADGET 0x04
|
||||
/* Host mode is turned on. */
|
||||
#define OTGSTS_XHCI_READY BIT(26)
|
||||
/* "Device mode is turned on .*/
|
||||
#define OTGSTS_DEV_READY BIT(27)
|
||||
|
||||
/* OTGSTATE- bitmasks */
|
||||
#define OTGSTATE_DEV_STATE_MASK GENMASK(2, 0)
|
||||
#define OTGSTATE_HOST_STATE_MASK GENMASK(5, 3)
|
||||
#define OTGSTATE_HOST_STATE_IDLE 0x0
|
||||
#define OTGSTATE_HOST_STATE_VBUS_FALL 0x7
|
||||
#define OTGSTATE_HOST_STATE(p) (((p) & OTGSTATE_HOST_STATE_MASK) >> 3)
|
||||
|
||||
/* OTGREFCLK - bitmasks */
|
||||
#define OTGREFCLK_STB_CLK_SWITCH_EN BIT(31)
|
||||
|
||||
/* OVERRIDE - bitmasks */
|
||||
#define OVERRIDE_IDPULLUP BIT(0)
|
||||
/* Only for CDNS3_CONTROLLER_V0 version */
|
||||
#define OVERRIDE_IDPULLUP_V0 BIT(24)
|
||||
|
||||
int cdns3_is_host(struct cdns3 *cdns);
|
||||
int cdns3_is_device(struct cdns3 *cdns);
|
||||
int cdns3_get_id(struct cdns3 *cdns);
|
||||
int cdns3_get_vbus(struct cdns3 *cdns);
|
||||
int cdns3_drd_init(struct cdns3 *cdns);
|
||||
int cdns3_drd_exit(struct cdns3 *cdns);
|
||||
int cdns3_drd_update_mode(struct cdns3 *cdns);
|
||||
int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on);
|
||||
int cdns3_drd_switch_host(struct cdns3 *cdns, int on);
|
||||
int cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode);
|
||||
|
||||
#endif /* __LINUX_CDNS3_DRD */
|
888
drivers/usb/cdns3/ep0.c
Normal file
888
drivers/usb/cdns3/ep0.c
Normal file
@ -0,0 +1,888 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Cadence USBSS DRD Driver - gadget side.
|
||||
*
|
||||
* Copyright (C) 2018 Cadence Design Systems.
|
||||
* Copyright (C) 2017-2018 NXP
|
||||
*
|
||||
* Authors: Pawel Jez <pjez@cadence.com>,
|
||||
* Pawel Laszczak <pawell@cadence.com>
|
||||
* Peter Chen <peter.chen@nxp.com>
|
||||
*/
|
||||
|
||||
#include <linux/usb/composite.h>
|
||||
#include <linux/iopoll.h>
|
||||
|
||||
#include "gadget.h"
|
||||
#include "trace.h"
|
||||
|
||||
static struct usb_endpoint_descriptor cdns3_gadget_ep0_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_CONTROL,
|
||||
};
|
||||
|
||||
/**
|
||||
* cdns3_ep0_run_transfer - Do transfer on default endpoint hardware
|
||||
* @priv_dev: extended gadget object
|
||||
* @dma_addr: physical address where data is/will be stored
|
||||
* @length: data length
|
||||
* @erdy: set it to 1 when ERDY packet should be sent -
|
||||
* exit from flow control state
|
||||
*/
|
||||
static void cdns3_ep0_run_transfer(struct cdns3_device *priv_dev,
|
||||
dma_addr_t dma_addr,
|
||||
unsigned int length, int erdy, int zlp)
|
||||
{
|
||||
struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
|
||||
struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
|
||||
|
||||
priv_ep->trb_pool[0].buffer = TRB_BUFFER(dma_addr);
|
||||
priv_ep->trb_pool[0].length = TRB_LEN(length);
|
||||
|
||||
if (zlp) {
|
||||
priv_ep->trb_pool[0].control = TRB_CYCLE | TRB_TYPE(TRB_NORMAL);
|
||||
priv_ep->trb_pool[1].buffer = TRB_BUFFER(dma_addr);
|
||||
priv_ep->trb_pool[1].length = TRB_LEN(0);
|
||||
priv_ep->trb_pool[1].control = TRB_CYCLE | TRB_IOC |
|
||||
TRB_TYPE(TRB_NORMAL);
|
||||
} else {
|
||||
priv_ep->trb_pool[0].control = TRB_CYCLE | TRB_IOC |
|
||||
TRB_TYPE(TRB_NORMAL);
|
||||
priv_ep->trb_pool[1].control = 0;
|
||||
}
|
||||
|
||||
trace_cdns3_prepare_trb(priv_ep, priv_ep->trb_pool);
|
||||
|
||||
cdns3_select_ep(priv_dev, priv_dev->ep0_data_dir);
|
||||
|
||||
writel(EP_STS_TRBERR, ®s->ep_sts);
|
||||
writel(EP_TRADDR_TRADDR(priv_ep->trb_pool_dma), ®s->ep_traddr);
|
||||
trace_cdns3_doorbell_ep0(priv_dev->ep0_data_dir ? "ep0in" : "ep0out",
|
||||
readl(®s->ep_traddr));
|
||||
|
||||
/* TRB should be prepared before starting transfer. */
|
||||
writel(EP_CMD_DRDY, ®s->ep_cmd);
|
||||
|
||||
/* Resume controller before arming transfer. */
|
||||
__cdns3_gadget_wakeup(priv_dev);
|
||||
|
||||
if (erdy)
|
||||
writel(EP_CMD_ERDY, &priv_dev->regs->ep_cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_ep0_delegate_req - Returns status of handling setup packet
|
||||
* Setup is handled by gadget driver
|
||||
* @priv_dev: extended gadget object
|
||||
* @ctrl_req: pointer to received setup packet
|
||||
*
|
||||
* Returns zero on success or negative value on failure
|
||||
*/
|
||||
static int cdns3_ep0_delegate_req(struct cdns3_device *priv_dev,
|
||||
struct usb_ctrlrequest *ctrl_req)
|
||||
{
|
||||
int ret;
|
||||
|
||||
spin_unlock(&priv_dev->lock);
|
||||
priv_dev->setup_pending = 1;
|
||||
ret = priv_dev->gadget_driver->setup(&priv_dev->gadget, ctrl_req);
|
||||
priv_dev->setup_pending = 0;
|
||||
spin_lock(&priv_dev->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void cdns3_prepare_setup_packet(struct cdns3_device *priv_dev)
|
||||
{
|
||||
priv_dev->ep0_data_dir = 0;
|
||||
priv_dev->ep0_stage = CDNS3_SETUP_STAGE;
|
||||
cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma,
|
||||
sizeof(struct usb_ctrlrequest), 0, 0);
|
||||
}
|
||||
|
||||
static void cdns3_ep0_complete_setup(struct cdns3_device *priv_dev,
|
||||
u8 send_stall, u8 send_erdy)
|
||||
{
|
||||
struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
|
||||
struct usb_request *request;
|
||||
|
||||
request = cdns3_next_request(&priv_ep->pending_req_list);
|
||||
if (request)
|
||||
list_del_init(&request->list);
|
||||
|
||||
if (send_stall) {
|
||||
trace_cdns3_halt(priv_ep, send_stall, 0);
|
||||
/* set_stall on ep0 */
|
||||
cdns3_select_ep(priv_dev, 0x00);
|
||||
writel(EP_CMD_SSTALL, &priv_dev->regs->ep_cmd);
|
||||
} else {
|
||||
cdns3_prepare_setup_packet(priv_dev);
|
||||
}
|
||||
|
||||
priv_dev->ep0_stage = CDNS3_SETUP_STAGE;
|
||||
writel((send_erdy ? EP_CMD_ERDY : 0) | EP_CMD_REQ_CMPL,
|
||||
&priv_dev->regs->ep_cmd);
|
||||
|
||||
cdns3_allow_enable_l1(priv_dev, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_req_ep0_set_configuration - Handling of SET_CONFIG standard USB request
|
||||
* @priv_dev: extended gadget object
|
||||
* @ctrl_req: pointer to received setup packet
|
||||
*
|
||||
* Returns 0 if success, USB_GADGET_DELAYED_STATUS on deferred status stage,
|
||||
* error code on error
|
||||
*/
|
||||
static int cdns3_req_ep0_set_configuration(struct cdns3_device *priv_dev,
|
||||
struct usb_ctrlrequest *ctrl_req)
|
||||
{
|
||||
enum usb_device_state device_state = priv_dev->gadget.state;
|
||||
struct cdns3_endpoint *priv_ep;
|
||||
u32 config = le16_to_cpu(ctrl_req->wValue);
|
||||
int result = 0;
|
||||
int i;
|
||||
|
||||
switch (device_state) {
|
||||
case USB_STATE_ADDRESS:
|
||||
/* Configure non-control EPs */
|
||||
for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) {
|
||||
priv_ep = priv_dev->eps[i];
|
||||
if (!priv_ep)
|
||||
continue;
|
||||
|
||||
if (priv_ep->flags & EP_CLAIMED)
|
||||
cdns3_ep_config(priv_ep);
|
||||
}
|
||||
|
||||
result = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
|
||||
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
if (config) {
|
||||
cdns3_set_hw_configuration(priv_dev);
|
||||
} else {
|
||||
cdns3_hw_reset_eps_config(priv_dev);
|
||||
usb_gadget_set_state(&priv_dev->gadget,
|
||||
USB_STATE_ADDRESS);
|
||||
}
|
||||
break;
|
||||
case USB_STATE_CONFIGURED:
|
||||
result = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
|
||||
|
||||
if (!config && !result) {
|
||||
cdns3_hw_reset_eps_config(priv_dev);
|
||||
usb_gadget_set_state(&priv_dev->gadget,
|
||||
USB_STATE_ADDRESS);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
result = -EINVAL;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_req_ep0_set_address - Handling of SET_ADDRESS standard USB request
|
||||
* @priv_dev: extended gadget object
|
||||
* @ctrl_req: pointer to received setup packet
|
||||
*
|
||||
* Returns 0 if success, error code on error
|
||||
*/
|
||||
static int cdns3_req_ep0_set_address(struct cdns3_device *priv_dev,
|
||||
struct usb_ctrlrequest *ctrl_req)
|
||||
{
|
||||
enum usb_device_state device_state = priv_dev->gadget.state;
|
||||
u32 reg;
|
||||
u32 addr;
|
||||
|
||||
addr = le16_to_cpu(ctrl_req->wValue);
|
||||
|
||||
if (addr > USB_DEVICE_MAX_ADDRESS) {
|
||||
dev_err(priv_dev->dev,
|
||||
"Device address (%d) cannot be greater than %d\n",
|
||||
addr, USB_DEVICE_MAX_ADDRESS);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (device_state == USB_STATE_CONFIGURED) {
|
||||
dev_err(priv_dev->dev,
|
||||
"can't set_address from configured state\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
reg = readl(&priv_dev->regs->usb_cmd);
|
||||
|
||||
writel(reg | USB_CMD_FADDR(addr) | USB_CMD_SET_ADDR,
|
||||
&priv_dev->regs->usb_cmd);
|
||||
|
||||
usb_gadget_set_state(&priv_dev->gadget,
|
||||
(addr ? USB_STATE_ADDRESS : USB_STATE_DEFAULT));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_req_ep0_get_status - Handling of GET_STATUS standard USB request
|
||||
* @priv_dev: extended gadget object
|
||||
* @ctrl_req: pointer to received setup packet
|
||||
*
|
||||
* Returns 0 if success, error code on error
|
||||
*/
|
||||
static int cdns3_req_ep0_get_status(struct cdns3_device *priv_dev,
|
||||
struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
__le16 *response_pkt;
|
||||
u16 usb_status = 0;
|
||||
u32 recip;
|
||||
|
||||
recip = ctrl->bRequestType & USB_RECIP_MASK;
|
||||
|
||||
switch (recip) {
|
||||
case USB_RECIP_DEVICE:
|
||||
/* self powered */
|
||||
if (priv_dev->is_selfpowered)
|
||||
usb_status = BIT(USB_DEVICE_SELF_POWERED);
|
||||
|
||||
if (priv_dev->wake_up_flag)
|
||||
usb_status |= BIT(USB_DEVICE_REMOTE_WAKEUP);
|
||||
|
||||
if (priv_dev->gadget.speed != USB_SPEED_SUPER)
|
||||
break;
|
||||
|
||||
if (priv_dev->u1_allowed)
|
||||
usb_status |= BIT(USB_DEV_STAT_U1_ENABLED);
|
||||
|
||||
if (priv_dev->u2_allowed)
|
||||
usb_status |= BIT(USB_DEV_STAT_U2_ENABLED);
|
||||
|
||||
break;
|
||||
case USB_RECIP_INTERFACE:
|
||||
return cdns3_ep0_delegate_req(priv_dev, ctrl);
|
||||
case USB_RECIP_ENDPOINT:
|
||||
/* check if endpoint is stalled */
|
||||
cdns3_select_ep(priv_dev, ctrl->wIndex);
|
||||
if (EP_STS_STALL(readl(&priv_dev->regs->ep_sts)))
|
||||
usb_status = BIT(USB_ENDPOINT_HALT);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
response_pkt = (__le16 *)priv_dev->setup_buf;
|
||||
*response_pkt = cpu_to_le16(usb_status);
|
||||
|
||||
cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma,
|
||||
sizeof(*response_pkt), 1, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdns3_ep0_feature_handle_device(struct cdns3_device *priv_dev,
|
||||
struct usb_ctrlrequest *ctrl,
|
||||
int set)
|
||||
{
|
||||
enum usb_device_state state;
|
||||
enum usb_device_speed speed;
|
||||
int ret = 0;
|
||||
u32 wValue;
|
||||
u32 wIndex;
|
||||
u16 tmode;
|
||||
|
||||
wValue = le16_to_cpu(ctrl->wValue);
|
||||
wIndex = le16_to_cpu(ctrl->wIndex);
|
||||
state = priv_dev->gadget.state;
|
||||
speed = priv_dev->gadget.speed;
|
||||
|
||||
switch (ctrl->wValue) {
|
||||
case USB_DEVICE_REMOTE_WAKEUP:
|
||||
priv_dev->wake_up_flag = !!set;
|
||||
break;
|
||||
case USB_DEVICE_U1_ENABLE:
|
||||
if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER)
|
||||
return -EINVAL;
|
||||
|
||||
priv_dev->u1_allowed = !!set;
|
||||
break;
|
||||
case USB_DEVICE_U2_ENABLE:
|
||||
if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER)
|
||||
return -EINVAL;
|
||||
|
||||
priv_dev->u2_allowed = !!set;
|
||||
break;
|
||||
case USB_DEVICE_LTM_ENABLE:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
case USB_DEVICE_TEST_MODE:
|
||||
if (state != USB_STATE_CONFIGURED || speed > USB_SPEED_HIGH)
|
||||
return -EINVAL;
|
||||
|
||||
tmode = le16_to_cpu(ctrl->wIndex);
|
||||
|
||||
if (!set || (tmode & 0xff) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
switch (tmode >> 8) {
|
||||
case TEST_J:
|
||||
case TEST_K:
|
||||
case TEST_SE0_NAK:
|
||||
case TEST_PACKET:
|
||||
cdns3_ep0_complete_setup(priv_dev, 0, 1);
|
||||
/**
|
||||
* Little delay to give the controller some time
|
||||
* for sending status stage.
|
||||
* This time should be less then 3ms.
|
||||
*/
|
||||
usleep_range(1000, 2000);
|
||||
cdns3_set_register_bit(&priv_dev->regs->usb_cmd,
|
||||
USB_CMD_STMODE |
|
||||
USB_STS_TMODE_SEL(tmode - 1));
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cdns3_ep0_feature_handle_intf(struct cdns3_device *priv_dev,
|
||||
struct usb_ctrlrequest *ctrl,
|
||||
int set)
|
||||
{
|
||||
u32 wValue;
|
||||
int ret = 0;
|
||||
|
||||
wValue = le16_to_cpu(ctrl->wValue);
|
||||
|
||||
switch (wValue) {
|
||||
case USB_INTRF_FUNC_SUSPEND:
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cdns3_ep0_feature_handle_endpoint(struct cdns3_device *priv_dev,
|
||||
struct usb_ctrlrequest *ctrl,
|
||||
int set)
|
||||
{
|
||||
struct cdns3_endpoint *priv_ep;
|
||||
int ret = 0;
|
||||
u8 index;
|
||||
|
||||
if (le16_to_cpu(ctrl->wValue) != USB_ENDPOINT_HALT)
|
||||
return -EINVAL;
|
||||
|
||||
if (!(ctrl->wIndex & ~USB_DIR_IN))
|
||||
return 0;
|
||||
|
||||
index = cdns3_ep_addr_to_index(ctrl->wIndex);
|
||||
priv_ep = priv_dev->eps[index];
|
||||
|
||||
cdns3_select_ep(priv_dev, ctrl->wIndex);
|
||||
|
||||
if (set)
|
||||
__cdns3_gadget_ep_set_halt(priv_ep);
|
||||
else if (!(priv_ep->flags & EP_WEDGE))
|
||||
ret = __cdns3_gadget_ep_clear_halt(priv_ep);
|
||||
|
||||
cdns3_select_ep(priv_dev, 0x00);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_req_ep0_handle_feature -
|
||||
* Handling of GET/SET_FEATURE standard USB request
|
||||
*
|
||||
* @priv_dev: extended gadget object
|
||||
* @ctrl_req: pointer to received setup packet
|
||||
* @set: must be set to 1 for SET_FEATURE request
|
||||
*
|
||||
* Returns 0 if success, error code on error
|
||||
*/
|
||||
static int cdns3_req_ep0_handle_feature(struct cdns3_device *priv_dev,
|
||||
struct usb_ctrlrequest *ctrl,
|
||||
int set)
|
||||
{
|
||||
int ret = 0;
|
||||
u32 recip;
|
||||
|
||||
recip = ctrl->bRequestType & USB_RECIP_MASK;
|
||||
|
||||
switch (recip) {
|
||||
case USB_RECIP_DEVICE:
|
||||
ret = cdns3_ep0_feature_handle_device(priv_dev, ctrl, set);
|
||||
break;
|
||||
case USB_RECIP_INTERFACE:
|
||||
ret = cdns3_ep0_feature_handle_intf(priv_dev, ctrl, set);
|
||||
break;
|
||||
case USB_RECIP_ENDPOINT:
|
||||
ret = cdns3_ep0_feature_handle_endpoint(priv_dev, ctrl, set);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_req_ep0_set_sel - Handling of SET_SEL standard USB request
|
||||
* @priv_dev: extended gadget object
|
||||
* @ctrl_req: pointer to received setup packet
|
||||
*
|
||||
* Returns 0 if success, error code on error
|
||||
*/
|
||||
static int cdns3_req_ep0_set_sel(struct cdns3_device *priv_dev,
|
||||
struct usb_ctrlrequest *ctrl_req)
|
||||
{
|
||||
if (priv_dev->gadget.state < USB_STATE_ADDRESS)
|
||||
return -EINVAL;
|
||||
|
||||
if (ctrl_req->wLength != 6) {
|
||||
dev_err(priv_dev->dev, "Set SEL should be 6 bytes, got %d\n",
|
||||
ctrl_req->wLength);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma, 6, 1, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_req_ep0_set_isoch_delay -
|
||||
* Handling of GET_ISOCH_DELAY standard USB request
|
||||
* @priv_dev: extended gadget object
|
||||
* @ctrl_req: pointer to received setup packet
|
||||
*
|
||||
* Returns 0 if success, error code on error
|
||||
*/
|
||||
static int cdns3_req_ep0_set_isoch_delay(struct cdns3_device *priv_dev,
|
||||
struct usb_ctrlrequest *ctrl_req)
|
||||
{
|
||||
if (ctrl_req->wIndex || ctrl_req->wLength)
|
||||
return -EINVAL;
|
||||
|
||||
priv_dev->isoch_delay = ctrl_req->wValue;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_ep0_standard_request - Handling standard USB requests
|
||||
* @priv_dev: extended gadget object
|
||||
* @ctrl_req: pointer to received setup packet
|
||||
*
|
||||
* Returns 0 if success, error code on error
|
||||
*/
|
||||
static int cdns3_ep0_standard_request(struct cdns3_device *priv_dev,
|
||||
struct usb_ctrlrequest *ctrl_req)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (ctrl_req->bRequest) {
|
||||
case USB_REQ_SET_ADDRESS:
|
||||
ret = cdns3_req_ep0_set_address(priv_dev, ctrl_req);
|
||||
break;
|
||||
case USB_REQ_SET_CONFIGURATION:
|
||||
ret = cdns3_req_ep0_set_configuration(priv_dev, ctrl_req);
|
||||
break;
|
||||
case USB_REQ_GET_STATUS:
|
||||
ret = cdns3_req_ep0_get_status(priv_dev, ctrl_req);
|
||||
break;
|
||||
case USB_REQ_CLEAR_FEATURE:
|
||||
ret = cdns3_req_ep0_handle_feature(priv_dev, ctrl_req, 0);
|
||||
break;
|
||||
case USB_REQ_SET_FEATURE:
|
||||
ret = cdns3_req_ep0_handle_feature(priv_dev, ctrl_req, 1);
|
||||
break;
|
||||
case USB_REQ_SET_SEL:
|
||||
ret = cdns3_req_ep0_set_sel(priv_dev, ctrl_req);
|
||||
break;
|
||||
case USB_REQ_SET_ISOCH_DELAY:
|
||||
ret = cdns3_req_ep0_set_isoch_delay(priv_dev, ctrl_req);
|
||||
break;
|
||||
default:
|
||||
ret = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __pending_setup_status_handler(struct cdns3_device *priv_dev)
|
||||
{
|
||||
struct usb_request *request = priv_dev->pending_status_request;
|
||||
|
||||
if (priv_dev->status_completion_no_call && request &&
|
||||
request->complete) {
|
||||
request->complete(&priv_dev->eps[0]->endpoint, request);
|
||||
priv_dev->status_completion_no_call = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void cdns3_pending_setup_status_handler(struct work_struct *work)
|
||||
{
|
||||
struct cdns3_device *priv_dev = container_of(work, struct cdns3_device,
|
||||
pending_status_wq);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&priv_dev->lock, flags);
|
||||
__pending_setup_status_handler(priv_dev);
|
||||
spin_unlock_irqrestore(&priv_dev->lock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_ep0_setup_phase - Handling setup USB requests
|
||||
* @priv_dev: extended gadget object
|
||||
*/
|
||||
static void cdns3_ep0_setup_phase(struct cdns3_device *priv_dev)
|
||||
{
|
||||
struct usb_ctrlrequest *ctrl = priv_dev->setup_buf;
|
||||
struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
|
||||
int result;
|
||||
|
||||
priv_dev->ep0_data_dir = ctrl->bRequestType & USB_DIR_IN;
|
||||
|
||||
trace_cdns3_ctrl_req(ctrl);
|
||||
|
||||
if (!list_empty(&priv_ep->pending_req_list)) {
|
||||
struct usb_request *request;
|
||||
|
||||
request = cdns3_next_request(&priv_ep->pending_req_list);
|
||||
priv_ep->dir = priv_dev->ep0_data_dir;
|
||||
cdns3_gadget_giveback(priv_ep, to_cdns3_request(request),
|
||||
-ECONNRESET);
|
||||
}
|
||||
|
||||
if (le16_to_cpu(ctrl->wLength))
|
||||
priv_dev->ep0_stage = CDNS3_DATA_STAGE;
|
||||
else
|
||||
priv_dev->ep0_stage = CDNS3_STATUS_STAGE;
|
||||
|
||||
if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
|
||||
result = cdns3_ep0_standard_request(priv_dev, ctrl);
|
||||
else
|
||||
result = cdns3_ep0_delegate_req(priv_dev, ctrl);
|
||||
|
||||
if (result == USB_GADGET_DELAYED_STATUS)
|
||||
return;
|
||||
|
||||
if (result < 0)
|
||||
cdns3_ep0_complete_setup(priv_dev, 1, 1);
|
||||
else if (priv_dev->ep0_stage == CDNS3_STATUS_STAGE)
|
||||
cdns3_ep0_complete_setup(priv_dev, 0, 1);
|
||||
}
|
||||
|
||||
static void cdns3_transfer_completed(struct cdns3_device *priv_dev)
|
||||
{
|
||||
struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
|
||||
|
||||
if (!list_empty(&priv_ep->pending_req_list)) {
|
||||
struct usb_request *request;
|
||||
|
||||
trace_cdns3_complete_trb(priv_ep, priv_ep->trb_pool);
|
||||
request = cdns3_next_request(&priv_ep->pending_req_list);
|
||||
|
||||
request->actual =
|
||||
TRB_LEN(le32_to_cpu(priv_ep->trb_pool->length));
|
||||
|
||||
priv_ep->dir = priv_dev->ep0_data_dir;
|
||||
cdns3_gadget_giveback(priv_ep, to_cdns3_request(request), 0);
|
||||
}
|
||||
|
||||
cdns3_ep0_complete_setup(priv_dev, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_check_new_setup - Check if controller receive new SETUP packet.
|
||||
* @priv_dev: extended gadget object
|
||||
*
|
||||
* The SETUP packet can be kept in on-chip memory or in system memory.
|
||||
*/
|
||||
static bool cdns3_check_new_setup(struct cdns3_device *priv_dev)
|
||||
{
|
||||
u32 ep_sts_reg;
|
||||
|
||||
cdns3_select_ep(priv_dev, 0 | USB_DIR_OUT);
|
||||
ep_sts_reg = readl(&priv_dev->regs->ep_sts);
|
||||
|
||||
return !!(ep_sts_reg & (EP_STS_SETUP | EP_STS_STPWAIT));
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_check_ep0_interrupt_proceed - Processes interrupt related to endpoint 0
|
||||
* @priv_dev: extended gadget object
|
||||
* @dir: USB_DIR_IN for IN direction, USB_DIR_OUT for OUT direction
|
||||
*/
|
||||
void cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir)
|
||||
{
|
||||
u32 ep_sts_reg;
|
||||
|
||||
cdns3_select_ep(priv_dev, dir);
|
||||
|
||||
ep_sts_reg = readl(&priv_dev->regs->ep_sts);
|
||||
writel(ep_sts_reg, &priv_dev->regs->ep_sts);
|
||||
|
||||
trace_cdns3_ep0_irq(priv_dev, ep_sts_reg);
|
||||
|
||||
__pending_setup_status_handler(priv_dev);
|
||||
|
||||
if (ep_sts_reg & EP_STS_SETUP)
|
||||
priv_dev->wait_for_setup = 1;
|
||||
|
||||
if (priv_dev->wait_for_setup && ep_sts_reg & EP_STS_IOC) {
|
||||
priv_dev->wait_for_setup = 0;
|
||||
cdns3_allow_enable_l1(priv_dev, 0);
|
||||
cdns3_ep0_setup_phase(priv_dev);
|
||||
} else if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) {
|
||||
priv_dev->ep0_data_dir = dir;
|
||||
cdns3_transfer_completed(priv_dev);
|
||||
}
|
||||
|
||||
if (ep_sts_reg & EP_STS_DESCMIS) {
|
||||
if (dir == 0 && !priv_dev->setup_pending)
|
||||
cdns3_prepare_setup_packet(priv_dev);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_gadget_ep0_enable
|
||||
* Function shouldn't be called by gadget driver,
|
||||
* endpoint 0 is allways active
|
||||
*/
|
||||
static int cdns3_gadget_ep0_enable(struct usb_ep *ep,
|
||||
const struct usb_endpoint_descriptor *desc)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_gadget_ep0_disable
|
||||
* Function shouldn't be called by gadget driver,
|
||||
* endpoint 0 is allways active
|
||||
*/
|
||||
static int cdns3_gadget_ep0_disable(struct usb_ep *ep)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_gadget_ep0_set_halt
|
||||
* @ep: pointer to endpoint zero object
|
||||
* @value: 1 for set stall, 0 for clear stall
|
||||
*
|
||||
* Returns 0
|
||||
*/
|
||||
static int cdns3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
|
||||
{
|
||||
/* TODO */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_gadget_ep0_queue Transfer data on endpoint zero
|
||||
* @ep: pointer to endpoint zero object
|
||||
* @request: pointer to request object
|
||||
* @gfp_flags: gfp flags
|
||||
*
|
||||
* Returns 0 on success, error code elsewhere
|
||||
*/
|
||||
static int cdns3_gadget_ep0_queue(struct usb_ep *ep,
|
||||
struct usb_request *request,
|
||||
gfp_t gfp_flags)
|
||||
{
|
||||
struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
|
||||
struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
|
||||
unsigned long flags;
|
||||
int erdy_sent = 0;
|
||||
int ret = 0;
|
||||
u8 zlp = 0;
|
||||
|
||||
trace_cdns3_ep0_queue(priv_dev, request);
|
||||
|
||||
/* cancel the request if controller receive new SETUP packet. */
|
||||
if (cdns3_check_new_setup(priv_dev))
|
||||
return -ECONNRESET;
|
||||
|
||||
/* send STATUS stage. Should be called only for SET_CONFIGURATION */
|
||||
if (priv_dev->ep0_stage == CDNS3_STATUS_STAGE) {
|
||||
spin_lock_irqsave(&priv_dev->lock, flags);
|
||||
cdns3_select_ep(priv_dev, 0x00);
|
||||
|
||||
erdy_sent = !priv_dev->hw_configured_flag;
|
||||
cdns3_set_hw_configuration(priv_dev);
|
||||
|
||||
if (!erdy_sent)
|
||||
cdns3_ep0_complete_setup(priv_dev, 0, 1);
|
||||
|
||||
cdns3_allow_enable_l1(priv_dev, 1);
|
||||
|
||||
request->actual = 0;
|
||||
priv_dev->status_completion_no_call = true;
|
||||
priv_dev->pending_status_request = request;
|
||||
spin_unlock_irqrestore(&priv_dev->lock, flags);
|
||||
|
||||
/*
|
||||
* Since there is no completion interrupt for status stage,
|
||||
* it needs to call ->completion in software after
|
||||
* ep0_queue is back.
|
||||
*/
|
||||
queue_work(system_freezable_wq, &priv_dev->pending_status_wq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&priv_dev->lock, flags);
|
||||
if (!list_empty(&priv_ep->pending_req_list)) {
|
||||
dev_err(priv_dev->dev,
|
||||
"can't handle multiple requests for ep0\n");
|
||||
spin_unlock_irqrestore(&priv_dev->lock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
ret = usb_gadget_map_request_by_dev(priv_dev->sysdev, request,
|
||||
priv_dev->ep0_data_dir);
|
||||
if (ret) {
|
||||
spin_unlock_irqrestore(&priv_dev->lock, flags);
|
||||
dev_err(priv_dev->dev, "failed to map request\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
request->status = -EINPROGRESS;
|
||||
list_add_tail(&request->list, &priv_ep->pending_req_list);
|
||||
|
||||
if (request->zero && request->length &&
|
||||
(request->length % ep->maxpacket == 0))
|
||||
zlp = 1;
|
||||
|
||||
cdns3_ep0_run_transfer(priv_dev, request->dma, request->length, 1, zlp);
|
||||
|
||||
spin_unlock_irqrestore(&priv_dev->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_gadget_ep_set_wedge Set wedge on selected endpoint
|
||||
* @ep: endpoint object
|
||||
*
|
||||
* Returns 0
|
||||
*/
|
||||
int cdns3_gadget_ep_set_wedge(struct usb_ep *ep)
|
||||
{
|
||||
struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
|
||||
struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
|
||||
|
||||
dev_dbg(priv_dev->dev, "Wedge for %s\n", ep->name);
|
||||
cdns3_gadget_ep_set_halt(ep, 1);
|
||||
priv_ep->flags |= EP_WEDGE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct usb_ep_ops cdns3_gadget_ep0_ops = {
|
||||
.enable = cdns3_gadget_ep0_enable,
|
||||
.disable = cdns3_gadget_ep0_disable,
|
||||
.alloc_request = cdns3_gadget_ep_alloc_request,
|
||||
.free_request = cdns3_gadget_ep_free_request,
|
||||
.queue = cdns3_gadget_ep0_queue,
|
||||
.dequeue = cdns3_gadget_ep_dequeue,
|
||||
.set_halt = cdns3_gadget_ep0_set_halt,
|
||||
.set_wedge = cdns3_gadget_ep_set_wedge,
|
||||
};
|
||||
|
||||
/**
|
||||
* cdns3_ep0_config - Configures default endpoint
|
||||
* @priv_dev: extended gadget object
|
||||
*
|
||||
* Functions sets parameters: maximal packet size and enables interrupts
|
||||
*/
|
||||
void cdns3_ep0_config(struct cdns3_device *priv_dev)
|
||||
{
|
||||
struct cdns3_usb_regs __iomem *regs;
|
||||
struct cdns3_endpoint *priv_ep;
|
||||
u32 max_packet_size = 64;
|
||||
|
||||
regs = priv_dev->regs;
|
||||
|
||||
if (priv_dev->gadget.speed == USB_SPEED_SUPER)
|
||||
max_packet_size = 512;
|
||||
|
||||
priv_ep = priv_dev->eps[0];
|
||||
|
||||
if (!list_empty(&priv_ep->pending_req_list)) {
|
||||
struct usb_request *request;
|
||||
|
||||
request = cdns3_next_request(&priv_ep->pending_req_list);
|
||||
list_del_init(&request->list);
|
||||
}
|
||||
|
||||
priv_dev->u1_allowed = 0;
|
||||
priv_dev->u2_allowed = 0;
|
||||
|
||||
priv_dev->gadget.ep0->maxpacket = max_packet_size;
|
||||
cdns3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(max_packet_size);
|
||||
|
||||
/* init ep out */
|
||||
cdns3_select_ep(priv_dev, USB_DIR_OUT);
|
||||
|
||||
if (priv_dev->dev_ver >= DEV_VER_V3) {
|
||||
cdns3_set_register_bit(&priv_dev->regs->dtrans,
|
||||
BIT(0) | BIT(16));
|
||||
cdns3_set_register_bit(&priv_dev->regs->tdl_from_trb,
|
||||
BIT(0) | BIT(16));
|
||||
}
|
||||
|
||||
writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size),
|
||||
®s->ep_cfg);
|
||||
|
||||
writel(EP_STS_EN_SETUPEN | EP_STS_EN_DESCMISEN | EP_STS_EN_TRBERREN,
|
||||
®s->ep_sts_en);
|
||||
|
||||
/* init ep in */
|
||||
cdns3_select_ep(priv_dev, USB_DIR_IN);
|
||||
|
||||
writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size),
|
||||
®s->ep_cfg);
|
||||
|
||||
writel(EP_STS_EN_SETUPEN | EP_STS_EN_TRBERREN, ®s->ep_sts_en);
|
||||
|
||||
cdns3_set_register_bit(®s->usb_conf, USB_CONF_U1DS | USB_CONF_U2DS);
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_init_ep0 Initializes software endpoint 0 of gadget
|
||||
* @priv_dev: extended gadget object
|
||||
* @ep_priv: extended endpoint object
|
||||
*
|
||||
* Returns 0 on success else error code.
|
||||
*/
|
||||
int cdns3_init_ep0(struct cdns3_device *priv_dev,
|
||||
struct cdns3_endpoint *priv_ep)
|
||||
{
|
||||
sprintf(priv_ep->name, "ep0");
|
||||
|
||||
/* fill linux fields */
|
||||
priv_ep->endpoint.ops = &cdns3_gadget_ep0_ops;
|
||||
priv_ep->endpoint.maxburst = 1;
|
||||
usb_ep_set_maxpacket_limit(&priv_ep->endpoint,
|
||||
CDNS3_EP0_MAX_PACKET_LIMIT);
|
||||
priv_ep->endpoint.address = 0;
|
||||
priv_ep->endpoint.caps.type_control = 1;
|
||||
priv_ep->endpoint.caps.dir_in = 1;
|
||||
priv_ep->endpoint.caps.dir_out = 1;
|
||||
priv_ep->endpoint.name = priv_ep->name;
|
||||
priv_ep->endpoint.desc = &cdns3_gadget_ep0_desc;
|
||||
priv_dev->gadget.ep0 = &priv_ep->endpoint;
|
||||
priv_ep->type = USB_ENDPOINT_XFER_CONTROL;
|
||||
|
||||
return cdns3_allocate_trb_pool(priv_ep);
|
||||
}
|
28
drivers/usb/cdns3/gadget-export.h
Normal file
28
drivers/usb/cdns3/gadget-export.h
Normal file
@ -0,0 +1,28 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Cadence USBSS DRD Driver - Gadget Export APIs.
|
||||
*
|
||||
* Copyright (C) 2017 NXP
|
||||
* Copyright (C) 2017-2018 NXP
|
||||
*
|
||||
* Authors: Peter Chen <peter.chen@nxp.com>
|
||||
*/
|
||||
#ifndef __LINUX_CDNS3_GADGET_EXPORT
|
||||
#define __LINUX_CDNS3_GADGET_EXPORT
|
||||
|
||||
#ifdef CONFIG_USB_CDNS3_GADGET
|
||||
|
||||
int cdns3_gadget_init(struct cdns3 *cdns);
|
||||
void cdns3_gadget_exit(struct cdns3 *cdns);
|
||||
#else
|
||||
|
||||
static inline int cdns3_gadget_init(struct cdns3 *cdns)
|
||||
{
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static inline void cdns3_gadget_exit(struct cdns3 *cdns) { }
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* __LINUX_CDNS3_GADGET_EXPORT */
|
2751
drivers/usb/cdns3/gadget.c
Normal file
2751
drivers/usb/cdns3/gadget.c
Normal file
File diff suppressed because it is too large
Load Diff
1338
drivers/usb/cdns3/gadget.h
Normal file
1338
drivers/usb/cdns3/gadget.h
Normal file
File diff suppressed because it is too large
Load Diff
28
drivers/usb/cdns3/host-export.h
Normal file
28
drivers/usb/cdns3/host-export.h
Normal file
@ -0,0 +1,28 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Cadence USBSS DRD Driver - Host Export APIs
|
||||
*
|
||||
* Copyright (C) 2017-2018 NXP
|
||||
*
|
||||
* Authors: Peter Chen <peter.chen@nxp.com>
|
||||
*/
|
||||
#ifndef __LINUX_CDNS3_HOST_EXPORT
|
||||
#define __LINUX_CDNS3_HOST_EXPORT
|
||||
|
||||
#ifdef CONFIG_USB_CDNS3_HOST
|
||||
|
||||
int cdns3_host_init(struct cdns3 *cdns);
|
||||
void cdns3_host_exit(struct cdns3 *cdns);
|
||||
|
||||
#else
|
||||
|
||||
static inline int cdns3_host_init(struct cdns3 *cdns)
|
||||
{
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static inline void cdns3_host_exit(struct cdns3 *cdns) { }
|
||||
|
||||
#endif /* CONFIG_USB_CDNS3_HOST */
|
||||
|
||||
#endif /* __LINUX_CDNS3_HOST_EXPORT */
|
74
drivers/usb/cdns3/host.c
Normal file
74
drivers/usb/cdns3/host.c
Normal file
@ -0,0 +1,74 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Cadence USBSS DRD Driver - host side
|
||||
*
|
||||
* Copyright (C) 2018-2019 Cadence Design Systems.
|
||||
* Copyright (C) 2017-2018 NXP
|
||||
*
|
||||
* Authors: Peter Chen <peter.chen@nxp.com>
|
||||
* Pawel Laszczak <pawell@cadence.com>
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include "core.h"
|
||||
#include "drd.h"
|
||||
|
||||
static int __cdns3_host_init(struct cdns3 *cdns)
|
||||
{
|
||||
struct platform_device *xhci;
|
||||
int ret;
|
||||
|
||||
cdns3_drd_switch_host(cdns, 1);
|
||||
|
||||
xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
|
||||
if (!xhci) {
|
||||
dev_err(cdns->dev, "couldn't allocate xHCI device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
xhci->dev.parent = cdns->dev;
|
||||
cdns->host_dev = xhci;
|
||||
|
||||
ret = platform_device_add_resources(xhci, cdns->xhci_res,
|
||||
CDNS3_XHCI_RESOURCES_NUM);
|
||||
if (ret) {
|
||||
dev_err(cdns->dev, "couldn't add resources to xHCI device\n");
|
||||
goto err1;
|
||||
}
|
||||
|
||||
ret = platform_device_add(xhci);
|
||||
if (ret) {
|
||||
dev_err(cdns->dev, "failed to register xHCI device\n");
|
||||
goto err1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err1:
|
||||
platform_device_put(xhci);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void cdns3_host_exit(struct cdns3 *cdns)
|
||||
{
|
||||
platform_device_unregister(cdns->host_dev);
|
||||
cdns->host_dev = NULL;
|
||||
cdns3_drd_switch_host(cdns, 0);
|
||||
}
|
||||
|
||||
int cdns3_host_init(struct cdns3 *cdns)
|
||||
{
|
||||
struct cdns3_role_driver *rdrv;
|
||||
|
||||
rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL);
|
||||
if (!rdrv)
|
||||
return -ENOMEM;
|
||||
|
||||
rdrv->start = __cdns3_host_init;
|
||||
rdrv->stop = cdns3_host_exit;
|
||||
rdrv->state = CDNS3_ROLE_STATE_INACTIVE;
|
||||
rdrv->name = "host";
|
||||
|
||||
cdns->roles[USB_ROLE_HOST] = rdrv;
|
||||
|
||||
return 0;
|
||||
}
|
11
drivers/usb/cdns3/trace.c
Normal file
11
drivers/usb/cdns3/trace.c
Normal file
@ -0,0 +1,11 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* USBSS device controller driver Trace Support
|
||||
*
|
||||
* Copyright (C) 2018-2019 Cadence.
|
||||
*
|
||||
* Author: Pawel Laszczak <pawell@cadence.com>
|
||||
*/
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include "trace.h"
|
493
drivers/usb/cdns3/trace.h
Normal file
493
drivers/usb/cdns3/trace.h
Normal file
@ -0,0 +1,493 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* USBSS device controller driver.
|
||||
* Trace support header file.
|
||||
*
|
||||
* Copyright (C) 2018-2019 Cadence.
|
||||
*
|
||||
* Author: Pawel Laszczak <pawell@cadence.com>
|
||||
*/
|
||||
|
||||
#undef TRACE_SYSTEM
|
||||
#define TRACE_SYSTEM cdns3
|
||||
|
||||
#if !defined(__LINUX_CDNS3_TRACE) || defined(TRACE_HEADER_MULTI_READ)
|
||||
#define __LINUX_CDNS3_TRACE
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/tracepoint.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include "core.h"
|
||||
#include "gadget.h"
|
||||
#include "debug.h"
|
||||
|
||||
#define CDNS3_MSG_MAX 500
|
||||
|
||||
TRACE_EVENT(cdns3_halt,
|
||||
TP_PROTO(struct cdns3_endpoint *ep_priv, u8 halt, u8 flush),
|
||||
TP_ARGS(ep_priv, halt, flush),
|
||||
TP_STRUCT__entry(
|
||||
__string(name, ep_priv->name)
|
||||
__field(u8, halt)
|
||||
__field(u8, flush)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__assign_str(name, ep_priv->name);
|
||||
__entry->halt = halt;
|
||||
__entry->flush = flush;
|
||||
),
|
||||
TP_printk("Halt %s for %s: %s", __entry->flush ? " and flush" : "",
|
||||
__get_str(name), __entry->halt ? "set" : "cleared")
|
||||
);
|
||||
|
||||
TRACE_EVENT(cdns3_wa1,
|
||||
TP_PROTO(struct cdns3_endpoint *ep_priv, char *msg),
|
||||
TP_ARGS(ep_priv, msg),
|
||||
TP_STRUCT__entry(
|
||||
__string(ep_name, ep_priv->name)
|
||||
__string(msg, msg)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__assign_str(ep_name, ep_priv->name);
|
||||
__assign_str(msg, msg);
|
||||
),
|
||||
TP_printk("WA1: %s %s", __get_str(ep_name), __get_str(msg))
|
||||
);
|
||||
|
||||
TRACE_EVENT(cdns3_wa2,
|
||||
TP_PROTO(struct cdns3_endpoint *ep_priv, char *msg),
|
||||
TP_ARGS(ep_priv, msg),
|
||||
TP_STRUCT__entry(
|
||||
__string(ep_name, ep_priv->name)
|
||||
__string(msg, msg)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__assign_str(ep_name, ep_priv->name);
|
||||
__assign_str(msg, msg);
|
||||
),
|
||||
TP_printk("WA2: %s %s", __get_str(ep_name), __get_str(msg))
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(cdns3_log_doorbell,
|
||||
TP_PROTO(const char *ep_name, u32 ep_trbaddr),
|
||||
TP_ARGS(ep_name, ep_trbaddr),
|
||||
TP_STRUCT__entry(
|
||||
__string(name, ep_name)
|
||||
__field(u32, ep_trbaddr)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__assign_str(name, ep_name);
|
||||
__entry->ep_trbaddr = ep_trbaddr;
|
||||
),
|
||||
TP_printk("%s, ep_trbaddr %08x", __get_str(name),
|
||||
__entry->ep_trbaddr)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdns3_log_doorbell, cdns3_doorbell_ep0,
|
||||
TP_PROTO(const char *ep_name, u32 ep_trbaddr),
|
||||
TP_ARGS(ep_name, ep_trbaddr)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdns3_log_doorbell, cdns3_doorbell_epx,
|
||||
TP_PROTO(const char *ep_name, u32 ep_trbaddr),
|
||||
TP_ARGS(ep_name, ep_trbaddr)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(cdns3_log_usb_irq,
|
||||
TP_PROTO(struct cdns3_device *priv_dev, u32 usb_ists),
|
||||
TP_ARGS(priv_dev, usb_ists),
|
||||
TP_STRUCT__entry(
|
||||
__field(enum usb_device_speed, speed)
|
||||
__field(u32, usb_ists)
|
||||
__dynamic_array(char, str, CDNS3_MSG_MAX)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->speed = cdns3_get_speed(priv_dev);
|
||||
__entry->usb_ists = usb_ists;
|
||||
),
|
||||
TP_printk("%s", cdns3_decode_usb_irq(__get_str(str), __entry->speed,
|
||||
__entry->usb_ists))
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdns3_log_usb_irq, cdns3_usb_irq,
|
||||
TP_PROTO(struct cdns3_device *priv_dev, u32 usb_ists),
|
||||
TP_ARGS(priv_dev, usb_ists)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(cdns3_log_epx_irq,
|
||||
TP_PROTO(struct cdns3_device *priv_dev, struct cdns3_endpoint *priv_ep),
|
||||
TP_ARGS(priv_dev, priv_ep),
|
||||
TP_STRUCT__entry(
|
||||
__string(ep_name, priv_ep->name)
|
||||
__field(u32, ep_sts)
|
||||
__field(u32, ep_traddr)
|
||||
__dynamic_array(char, str, CDNS3_MSG_MAX)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__assign_str(ep_name, priv_ep->name);
|
||||
__entry->ep_sts = readl(&priv_dev->regs->ep_sts);
|
||||
__entry->ep_traddr = readl(&priv_dev->regs->ep_traddr);
|
||||
),
|
||||
TP_printk("%s, ep_traddr: %08x",
|
||||
cdns3_decode_epx_irq(__get_str(str),
|
||||
__get_str(ep_name),
|
||||
__entry->ep_sts),
|
||||
__entry->ep_traddr)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdns3_log_epx_irq, cdns3_epx_irq,
|
||||
TP_PROTO(struct cdns3_device *priv_dev, struct cdns3_endpoint *priv_ep),
|
||||
TP_ARGS(priv_dev, priv_ep)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(cdns3_log_ep0_irq,
|
||||
TP_PROTO(struct cdns3_device *priv_dev, u32 ep_sts),
|
||||
TP_ARGS(priv_dev, ep_sts),
|
||||
TP_STRUCT__entry(
|
||||
__field(int, ep_dir)
|
||||
__field(u32, ep_sts)
|
||||
__dynamic_array(char, str, CDNS3_MSG_MAX)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->ep_dir = priv_dev->ep0_data_dir;
|
||||
__entry->ep_sts = ep_sts;
|
||||
),
|
||||
TP_printk("%s", cdns3_decode_ep0_irq(__get_str(str),
|
||||
__entry->ep_dir,
|
||||
__entry->ep_sts))
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdns3_log_ep0_irq, cdns3_ep0_irq,
|
||||
TP_PROTO(struct cdns3_device *priv_dev, u32 ep_sts),
|
||||
TP_ARGS(priv_dev, ep_sts)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(cdns3_log_ctrl,
|
||||
TP_PROTO(struct usb_ctrlrequest *ctrl),
|
||||
TP_ARGS(ctrl),
|
||||
TP_STRUCT__entry(
|
||||
__field(u8, bRequestType)
|
||||
__field(u8, bRequest)
|
||||
__field(u16, wValue)
|
||||
__field(u16, wIndex)
|
||||
__field(u16, wLength)
|
||||
__dynamic_array(char, str, CDNS3_MSG_MAX)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->bRequestType = ctrl->bRequestType;
|
||||
__entry->bRequest = ctrl->bRequest;
|
||||
__entry->wValue = le16_to_cpu(ctrl->wValue);
|
||||
__entry->wIndex = le16_to_cpu(ctrl->wIndex);
|
||||
__entry->wLength = le16_to_cpu(ctrl->wLength);
|
||||
),
|
||||
TP_printk("%s", usb_decode_ctrl(__get_str(str), CDNS3_MSG_MAX,
|
||||
__entry->bRequestType,
|
||||
__entry->bRequest, __entry->wValue,
|
||||
__entry->wIndex, __entry->wLength)
|
||||
)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdns3_log_ctrl, cdns3_ctrl_req,
|
||||
TP_PROTO(struct usb_ctrlrequest *ctrl),
|
||||
TP_ARGS(ctrl)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(cdns3_log_request,
|
||||
TP_PROTO(struct cdns3_request *req),
|
||||
TP_ARGS(req),
|
||||
TP_STRUCT__entry(
|
||||
__string(name, req->priv_ep->name)
|
||||
__field(struct cdns3_request *, req)
|
||||
__field(void *, buf)
|
||||
__field(unsigned int, actual)
|
||||
__field(unsigned int, length)
|
||||
__field(int, status)
|
||||
__field(int, zero)
|
||||
__field(int, short_not_ok)
|
||||
__field(int, no_interrupt)
|
||||
__field(int, start_trb)
|
||||
__field(int, end_trb)
|
||||
__field(struct cdns3_trb *, start_trb_addr)
|
||||
__field(int, flags)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__assign_str(name, req->priv_ep->name);
|
||||
__entry->req = req;
|
||||
__entry->buf = req->request.buf;
|
||||
__entry->actual = req->request.actual;
|
||||
__entry->length = req->request.length;
|
||||
__entry->status = req->request.status;
|
||||
__entry->zero = req->request.zero;
|
||||
__entry->short_not_ok = req->request.short_not_ok;
|
||||
__entry->no_interrupt = req->request.no_interrupt;
|
||||
__entry->start_trb = req->start_trb;
|
||||
__entry->end_trb = req->end_trb;
|
||||
__entry->start_trb_addr = req->trb;
|
||||
__entry->flags = req->flags;
|
||||
),
|
||||
TP_printk("%s: req: %p, req buff %p, length: %u/%u %s%s%s, status: %d,"
|
||||
" trb: [start:%d, end:%d: virt addr %pa], flags:%x ",
|
||||
__get_str(name), __entry->req, __entry->buf, __entry->actual,
|
||||
__entry->length,
|
||||
__entry->zero ? "Z" : "z",
|
||||
__entry->short_not_ok ? "S" : "s",
|
||||
__entry->no_interrupt ? "I" : "i",
|
||||
__entry->status,
|
||||
__entry->start_trb,
|
||||
__entry->end_trb,
|
||||
__entry->start_trb_addr,
|
||||
__entry->flags
|
||||
)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdns3_log_request, cdns3_alloc_request,
|
||||
TP_PROTO(struct cdns3_request *req),
|
||||
TP_ARGS(req)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdns3_log_request, cdns3_free_request,
|
||||
TP_PROTO(struct cdns3_request *req),
|
||||
TP_ARGS(req)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdns3_log_request, cdns3_ep_queue,
|
||||
TP_PROTO(struct cdns3_request *req),
|
||||
TP_ARGS(req)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdns3_log_request, cdns3_ep_dequeue,
|
||||
TP_PROTO(struct cdns3_request *req),
|
||||
TP_ARGS(req)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdns3_log_request, cdns3_gadget_giveback,
|
||||
TP_PROTO(struct cdns3_request *req),
|
||||
TP_ARGS(req)
|
||||
);
|
||||
|
||||
TRACE_EVENT(cdns3_ep0_queue,
|
||||
TP_PROTO(struct cdns3_device *dev_priv, struct usb_request *request),
|
||||
TP_ARGS(dev_priv, request),
|
||||
TP_STRUCT__entry(
|
||||
__field(int, dir)
|
||||
__field(int, length)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->dir = dev_priv->ep0_data_dir;
|
||||
__entry->length = request->length;
|
||||
),
|
||||
TP_printk("Queue to ep0%s length: %u", __entry->dir ? "in" : "out",
|
||||
__entry->length)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(cdns3_log_aligned_request,
|
||||
TP_PROTO(struct cdns3_request *priv_req),
|
||||
TP_ARGS(priv_req),
|
||||
TP_STRUCT__entry(
|
||||
__string(name, priv_req->priv_ep->name)
|
||||
__field(struct usb_request *, req)
|
||||
__field(void *, buf)
|
||||
__field(dma_addr_t, dma)
|
||||
__field(void *, aligned_buf)
|
||||
__field(dma_addr_t, aligned_dma)
|
||||
__field(u32, aligned_buf_size)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__assign_str(name, priv_req->priv_ep->name);
|
||||
__entry->req = &priv_req->request;
|
||||
__entry->buf = priv_req->request.buf;
|
||||
__entry->dma = priv_req->request.dma;
|
||||
__entry->aligned_buf = priv_req->aligned_buf->buf;
|
||||
__entry->aligned_dma = priv_req->aligned_buf->dma;
|
||||
__entry->aligned_buf_size = priv_req->aligned_buf->size;
|
||||
),
|
||||
TP_printk("%s: req: %p, req buf %p, dma %pad a_buf %p a_dma %pad, size %d",
|
||||
__get_str(name), __entry->req, __entry->buf, &__entry->dma,
|
||||
__entry->aligned_buf, &__entry->aligned_dma,
|
||||
__entry->aligned_buf_size
|
||||
)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdns3_log_aligned_request, cdns3_free_aligned_request,
|
||||
TP_PROTO(struct cdns3_request *req),
|
||||
TP_ARGS(req)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdns3_log_aligned_request, cdns3_prepare_aligned_request,
|
||||
TP_PROTO(struct cdns3_request *req),
|
||||
TP_ARGS(req)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(cdns3_log_trb,
|
||||
TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb),
|
||||
TP_ARGS(priv_ep, trb),
|
||||
TP_STRUCT__entry(
|
||||
__string(name, priv_ep->name)
|
||||
__field(struct cdns3_trb *, trb)
|
||||
__field(u32, buffer)
|
||||
__field(u32, length)
|
||||
__field(u32, control)
|
||||
__field(u32, type)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__assign_str(name, priv_ep->name);
|
||||
__entry->trb = trb;
|
||||
__entry->buffer = trb->buffer;
|
||||
__entry->length = trb->length;
|
||||
__entry->control = trb->control;
|
||||
__entry->type = usb_endpoint_type(priv_ep->endpoint.desc);
|
||||
),
|
||||
TP_printk("%s: trb %p, dma buf: 0x%08x, size: %ld, burst: %d ctrl: 0x%08x (%s%s%s%s%s%s%s)",
|
||||
__get_str(name), __entry->trb, __entry->buffer,
|
||||
TRB_LEN(__entry->length),
|
||||
(u8)TRB_BURST_LEN_GET(__entry->length),
|
||||
__entry->control,
|
||||
__entry->control & TRB_CYCLE ? "C=1, " : "C=0, ",
|
||||
__entry->control & TRB_TOGGLE ? "T=1, " : "T=0, ",
|
||||
__entry->control & TRB_ISP ? "ISP, " : "",
|
||||
__entry->control & TRB_FIFO_MODE ? "FIFO, " : "",
|
||||
__entry->control & TRB_CHAIN ? "CHAIN, " : "",
|
||||
__entry->control & TRB_IOC ? "IOC, " : "",
|
||||
TRB_FIELD_TO_TYPE(__entry->control) == TRB_NORMAL ? "Normal" : "LINK"
|
||||
)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdns3_log_trb, cdns3_prepare_trb,
|
||||
TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb),
|
||||
TP_ARGS(priv_ep, trb)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdns3_log_trb, cdns3_complete_trb,
|
||||
TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb),
|
||||
TP_ARGS(priv_ep, trb)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(cdns3_log_ring,
|
||||
TP_PROTO(struct cdns3_endpoint *priv_ep),
|
||||
TP_ARGS(priv_ep),
|
||||
TP_STRUCT__entry(
|
||||
__dynamic_array(u8, ring, TRB_RING_SIZE)
|
||||
__dynamic_array(u8, priv_ep, sizeof(struct cdns3_endpoint))
|
||||
__dynamic_array(char, buffer,
|
||||
(TRBS_PER_SEGMENT * 65) + CDNS3_MSG_MAX)
|
||||
),
|
||||
TP_fast_assign(
|
||||
memcpy(__get_dynamic_array(priv_ep), priv_ep,
|
||||
sizeof(struct cdns3_endpoint));
|
||||
memcpy(__get_dynamic_array(ring), priv_ep->trb_pool,
|
||||
TRB_RING_SIZE);
|
||||
),
|
||||
|
||||
TP_printk("%s",
|
||||
cdns3_dbg_ring((struct cdns3_endpoint *)__get_str(priv_ep),
|
||||
(struct cdns3_trb *)__get_str(ring),
|
||||
__get_str(buffer)))
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdns3_log_ring, cdns3_ring,
|
||||
TP_PROTO(struct cdns3_endpoint *priv_ep),
|
||||
TP_ARGS(priv_ep)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(cdns3_log_ep,
|
||||
TP_PROTO(struct cdns3_endpoint *priv_ep),
|
||||
TP_ARGS(priv_ep),
|
||||
TP_STRUCT__entry(
|
||||
__string(name, priv_ep->name)
|
||||
__field(unsigned int, maxpacket)
|
||||
__field(unsigned int, maxpacket_limit)
|
||||
__field(unsigned int, max_streams)
|
||||
__field(unsigned int, maxburst)
|
||||
__field(unsigned int, flags)
|
||||
__field(unsigned int, dir)
|
||||
__field(u8, enqueue)
|
||||
__field(u8, dequeue)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__assign_str(name, priv_ep->name);
|
||||
__entry->maxpacket = priv_ep->endpoint.maxpacket;
|
||||
__entry->maxpacket_limit = priv_ep->endpoint.maxpacket_limit;
|
||||
__entry->max_streams = priv_ep->endpoint.max_streams;
|
||||
__entry->maxburst = priv_ep->endpoint.maxburst;
|
||||
__entry->flags = priv_ep->flags;
|
||||
__entry->dir = priv_ep->dir;
|
||||
__entry->enqueue = priv_ep->enqueue;
|
||||
__entry->dequeue = priv_ep->dequeue;
|
||||
),
|
||||
TP_printk("%s: mps: %d/%d. streams: %d, burst: %d, enq idx: %d, "
|
||||
"deq idx: %d, flags %s%s%s%s%s%s%s%s, dir: %s",
|
||||
__get_str(name), __entry->maxpacket,
|
||||
__entry->maxpacket_limit, __entry->max_streams,
|
||||
__entry->maxburst, __entry->enqueue,
|
||||
__entry->dequeue,
|
||||
__entry->flags & EP_ENABLED ? "EN | " : "",
|
||||
__entry->flags & EP_STALLED ? "STALLED | " : "",
|
||||
__entry->flags & EP_WEDGE ? "WEDGE | " : "",
|
||||
__entry->flags & EP_TRANSFER_STARTED ? "STARTED | " : "",
|
||||
__entry->flags & EP_UPDATE_EP_TRBADDR ? "UPD TRB | " : "",
|
||||
__entry->flags & EP_PENDING_REQUEST ? "REQ PEN | " : "",
|
||||
__entry->flags & EP_RING_FULL ? "RING FULL |" : "",
|
||||
__entry->flags & EP_CLAIMED ? "CLAIMED " : "",
|
||||
__entry->dir ? "IN" : "OUT"
|
||||
)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdns3_log_ep, cdns3_gadget_ep_enable,
|
||||
TP_PROTO(struct cdns3_endpoint *priv_ep),
|
||||
TP_ARGS(priv_ep)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdns3_log_ep, cdns3_gadget_ep_disable,
|
||||
TP_PROTO(struct cdns3_endpoint *priv_ep),
|
||||
TP_ARGS(priv_ep)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(cdns3_log_request_handled,
|
||||
TP_PROTO(struct cdns3_request *priv_req, int current_index,
|
||||
int handled),
|
||||
TP_ARGS(priv_req, current_index, handled),
|
||||
TP_STRUCT__entry(
|
||||
__field(struct cdns3_request *, priv_req)
|
||||
__field(unsigned int, dma_position)
|
||||
__field(unsigned int, handled)
|
||||
__field(unsigned int, dequeue_idx)
|
||||
__field(unsigned int, enqueue_idx)
|
||||
__field(unsigned int, start_trb)
|
||||
__field(unsigned int, end_trb)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->priv_req = priv_req;
|
||||
__entry->dma_position = current_index;
|
||||
__entry->handled = handled;
|
||||
__entry->dequeue_idx = priv_req->priv_ep->dequeue;
|
||||
__entry->enqueue_idx = priv_req->priv_ep->enqueue;
|
||||
__entry->start_trb = priv_req->start_trb;
|
||||
__entry->end_trb = priv_req->end_trb;
|
||||
),
|
||||
TP_printk("Req: %p %s, DMA pos: %d, ep deq: %d, ep enq: %d,"
|
||||
" start trb: %d, end trb: %d",
|
||||
__entry->priv_req,
|
||||
__entry->handled ? "handled" : "not handled",
|
||||
__entry->dma_position, __entry->dequeue_idx,
|
||||
__entry->enqueue_idx, __entry->start_trb,
|
||||
__entry->end_trb
|
||||
)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdns3_log_request_handled, cdns3_request_handled,
|
||||
TP_PROTO(struct cdns3_request *priv_req, int current_index,
|
||||
int handled),
|
||||
TP_ARGS(priv_req, current_index, handled)
|
||||
);
|
||||
#endif /* __LINUX_CDNS3_TRACE */
|
||||
|
||||
/* this part must be outside header guard */
|
||||
|
||||
#undef TRACE_INCLUDE_PATH
|
||||
#define TRACE_INCLUDE_PATH .
|
||||
|
||||
#undef TRACE_INCLUDE_FILE
|
||||
#define TRACE_INCLUDE_FILE trace
|
||||
|
||||
#include <trace/define_trace.h>
|
@ -5,6 +5,7 @@
|
||||
|
||||
obj-$(CONFIG_USB_COMMON) += usb-common.o
|
||||
usb-common-y += common.o
|
||||
usb-common-$(CONFIG_TRACING) += debug.o
|
||||
usb-common-$(CONFIG_USB_LED_TRIG) += led.o
|
||||
|
||||
obj-$(CONFIG_USB_OTG_FSM) += usb-otg-fsm.o
|
||||
|
268
drivers/usb/common/debug.c
Normal file
268
drivers/usb/common/debug.c
Normal file
@ -0,0 +1,268 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/**
|
||||
* Common USB debugging functions
|
||||
*
|
||||
* Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
|
||||
*
|
||||
* Authors: Felipe Balbi <balbi@ti.com>,
|
||||
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
*/
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
|
||||
static void usb_decode_get_status(__u8 bRequestType, __u16 wIndex,
|
||||
__u16 wLength, char *str, size_t size)
|
||||
{
|
||||
switch (bRequestType & USB_RECIP_MASK) {
|
||||
case USB_RECIP_DEVICE:
|
||||
snprintf(str, size, "Get Device Status(Length = %d)", wLength);
|
||||
break;
|
||||
case USB_RECIP_INTERFACE:
|
||||
snprintf(str, size,
|
||||
"Get Interface Status(Intf = %d, Length = %d)",
|
||||
wIndex, wLength);
|
||||
break;
|
||||
case USB_RECIP_ENDPOINT:
|
||||
snprintf(str, size, "Get Endpoint Status(ep%d%s)",
|
||||
wIndex & ~USB_DIR_IN,
|
||||
wIndex & USB_DIR_IN ? "in" : "out");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *usb_decode_device_feature(u16 wValue)
|
||||
{
|
||||
switch (wValue) {
|
||||
case USB_DEVICE_SELF_POWERED:
|
||||
return "Self Powered";
|
||||
case USB_DEVICE_REMOTE_WAKEUP:
|
||||
return "Remote Wakeup";
|
||||
case USB_DEVICE_TEST_MODE:
|
||||
return "Test Mode";
|
||||
case USB_DEVICE_U1_ENABLE:
|
||||
return "U1 Enable";
|
||||
case USB_DEVICE_U2_ENABLE:
|
||||
return "U2 Enable";
|
||||
case USB_DEVICE_LTM_ENABLE:
|
||||
return "LTM Enable";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *usb_decode_test_mode(u16 wIndex)
|
||||
{
|
||||
switch (wIndex) {
|
||||
case TEST_J:
|
||||
return ": TEST_J";
|
||||
case TEST_K:
|
||||
return ": TEST_K";
|
||||
case TEST_SE0_NAK:
|
||||
return ": TEST_SE0_NAK";
|
||||
case TEST_PACKET:
|
||||
return ": TEST_PACKET";
|
||||
case TEST_FORCE_EN:
|
||||
return ": TEST_FORCE_EN";
|
||||
default:
|
||||
return ": UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_decode_set_clear_feature(__u8 bRequestType,
|
||||
__u8 bRequest, __u16 wValue,
|
||||
__u16 wIndex, char *str, size_t size)
|
||||
{
|
||||
switch (bRequestType & USB_RECIP_MASK) {
|
||||
case USB_RECIP_DEVICE:
|
||||
snprintf(str, size, "%s Device Feature(%s%s)",
|
||||
bRequest == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
|
||||
usb_decode_device_feature(wValue),
|
||||
wValue == USB_DEVICE_TEST_MODE ?
|
||||
usb_decode_test_mode(wIndex) : "");
|
||||
break;
|
||||
case USB_RECIP_INTERFACE:
|
||||
snprintf(str, size, "%s Interface Feature(%s)",
|
||||
bRequest == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
|
||||
wValue == USB_INTRF_FUNC_SUSPEND ?
|
||||
"Function Suspend" : "UNKNOWN");
|
||||
break;
|
||||
case USB_RECIP_ENDPOINT:
|
||||
snprintf(str, size, "%s Endpoint Feature(%s ep%d%s)",
|
||||
bRequest == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
|
||||
wValue == USB_ENDPOINT_HALT ? "Halt" : "UNKNOWN",
|
||||
wIndex & ~USB_DIR_IN,
|
||||
wIndex & USB_DIR_IN ? "in" : "out");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_decode_set_address(__u16 wValue, char *str, size_t size)
|
||||
{
|
||||
snprintf(str, size, "Set Address(Addr = %02x)", wValue);
|
||||
}
|
||||
|
||||
static void usb_decode_get_set_descriptor(__u8 bRequestType, __u8 bRequest,
|
||||
__u16 wValue, __u16 wIndex,
|
||||
__u16 wLength, char *str, size_t size)
|
||||
{
|
||||
char *s;
|
||||
|
||||
switch (wValue >> 8) {
|
||||
case USB_DT_DEVICE:
|
||||
s = "Device";
|
||||
break;
|
||||
case USB_DT_CONFIG:
|
||||
s = "Configuration";
|
||||
break;
|
||||
case USB_DT_STRING:
|
||||
s = "String";
|
||||
break;
|
||||
case USB_DT_INTERFACE:
|
||||
s = "Interface";
|
||||
break;
|
||||
case USB_DT_ENDPOINT:
|
||||
s = "Endpoint";
|
||||
break;
|
||||
case USB_DT_DEVICE_QUALIFIER:
|
||||
s = "Device Qualifier";
|
||||
break;
|
||||
case USB_DT_OTHER_SPEED_CONFIG:
|
||||
s = "Other Speed Config";
|
||||
break;
|
||||
case USB_DT_INTERFACE_POWER:
|
||||
s = "Interface Power";
|
||||
break;
|
||||
case USB_DT_OTG:
|
||||
s = "OTG";
|
||||
break;
|
||||
case USB_DT_DEBUG:
|
||||
s = "Debug";
|
||||
break;
|
||||
case USB_DT_INTERFACE_ASSOCIATION:
|
||||
s = "Interface Association";
|
||||
break;
|
||||
case USB_DT_BOS:
|
||||
s = "BOS";
|
||||
break;
|
||||
case USB_DT_DEVICE_CAPABILITY:
|
||||
s = "Device Capability";
|
||||
break;
|
||||
case USB_DT_PIPE_USAGE:
|
||||
s = "Pipe Usage";
|
||||
break;
|
||||
case USB_DT_SS_ENDPOINT_COMP:
|
||||
s = "SS Endpoint Companion";
|
||||
break;
|
||||
case USB_DT_SSP_ISOC_ENDPOINT_COMP:
|
||||
s = "SSP Isochronous Endpoint Companion";
|
||||
break;
|
||||
default:
|
||||
s = "UNKNOWN";
|
||||
break;
|
||||
}
|
||||
|
||||
snprintf(str, size, "%s %s Descriptor(Index = %d, Length = %d)",
|
||||
bRequest == USB_REQ_GET_DESCRIPTOR ? "Get" : "Set",
|
||||
s, wValue & 0xff, wLength);
|
||||
}
|
||||
|
||||
static void usb_decode_get_configuration(__u16 wLength, char *str, size_t size)
|
||||
{
|
||||
snprintf(str, size, "Get Configuration(Length = %d)", wLength);
|
||||
}
|
||||
|
||||
static void usb_decode_set_configuration(__u8 wValue, char *str, size_t size)
|
||||
{
|
||||
snprintf(str, size, "Set Configuration(Config = %d)", wValue);
|
||||
}
|
||||
|
||||
static void usb_decode_get_intf(__u16 wIndex, __u16 wLength, char *str,
|
||||
size_t size)
|
||||
{
|
||||
snprintf(str, size, "Get Interface(Intf = %d, Length = %d)",
|
||||
wIndex, wLength);
|
||||
}
|
||||
|
||||
static void usb_decode_set_intf(__u8 wValue, __u16 wIndex, char *str,
|
||||
size_t size)
|
||||
{
|
||||
snprintf(str, size, "Set Interface(Intf = %d, Alt.Setting = %d)",
|
||||
wIndex, wValue);
|
||||
}
|
||||
|
||||
static void usb_decode_synch_frame(__u16 wIndex, __u16 wLength,
|
||||
char *str, size_t size)
|
||||
{
|
||||
snprintf(str, size, "Synch Frame(Endpoint = %d, Length = %d)",
|
||||
wIndex, wLength);
|
||||
}
|
||||
|
||||
static void usb_decode_set_sel(__u16 wLength, char *str, size_t size)
|
||||
{
|
||||
snprintf(str, size, "Set SEL(Length = %d)", wLength);
|
||||
}
|
||||
|
||||
static void usb_decode_set_isoch_delay(__u8 wValue, char *str, size_t size)
|
||||
{
|
||||
snprintf(str, size, "Set Isochronous Delay(Delay = %d ns)", wValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_decode_ctrl - returns a string representation of ctrl request
|
||||
*/
|
||||
const char *usb_decode_ctrl(char *str, size_t size, __u8 bRequestType,
|
||||
__u8 bRequest, __u16 wValue, __u16 wIndex,
|
||||
__u16 wLength)
|
||||
{
|
||||
switch (bRequest) {
|
||||
case USB_REQ_GET_STATUS:
|
||||
usb_decode_get_status(bRequestType, wIndex, wLength, str, size);
|
||||
break;
|
||||
case USB_REQ_CLEAR_FEATURE:
|
||||
case USB_REQ_SET_FEATURE:
|
||||
usb_decode_set_clear_feature(bRequestType, bRequest, wValue,
|
||||
wIndex, str, size);
|
||||
break;
|
||||
case USB_REQ_SET_ADDRESS:
|
||||
usb_decode_set_address(wValue, str, size);
|
||||
break;
|
||||
case USB_REQ_GET_DESCRIPTOR:
|
||||
case USB_REQ_SET_DESCRIPTOR:
|
||||
usb_decode_get_set_descriptor(bRequestType, bRequest, wValue,
|
||||
wIndex, wLength, str, size);
|
||||
break;
|
||||
case USB_REQ_GET_CONFIGURATION:
|
||||
usb_decode_get_configuration(wLength, str, size);
|
||||
break;
|
||||
case USB_REQ_SET_CONFIGURATION:
|
||||
usb_decode_set_configuration(wValue, str, size);
|
||||
break;
|
||||
case USB_REQ_GET_INTERFACE:
|
||||
usb_decode_get_intf(wIndex, wLength, str, size);
|
||||
break;
|
||||
case USB_REQ_SET_INTERFACE:
|
||||
usb_decode_set_intf(wValue, wIndex, str, size);
|
||||
break;
|
||||
case USB_REQ_SYNCH_FRAME:
|
||||
usb_decode_synch_frame(wIndex, wLength, str, size);
|
||||
break;
|
||||
case USB_REQ_SET_SEL:
|
||||
usb_decode_set_sel(wLength, str, size);
|
||||
break;
|
||||
case USB_REQ_SET_ISOCH_DELAY:
|
||||
usb_decode_set_isoch_delay(wValue, str, size);
|
||||
break;
|
||||
default:
|
||||
snprintf(str, size, "%02x %02x %02x %02x %02x %02x %02x %02x",
|
||||
bRequestType, bRequest,
|
||||
(u8)(cpu_to_le16(wValue) & 0xff),
|
||||
(u8)(cpu_to_le16(wValue) >> 8),
|
||||
(u8)(cpu_to_le16(wIndex) & 0xff),
|
||||
(u8)(cpu_to_le16(wIndex) >> 8),
|
||||
(u8)(cpu_to_le16(wLength) & 0xff),
|
||||
(u8)(cpu_to_le16(wLength) >> 8));
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_decode_ctrl);
|
@ -3224,14 +3224,15 @@ static void kill_all_requests(struct dwc2_hsotg *hsotg,
|
||||
struct dwc2_hsotg_ep *ep,
|
||||
int result)
|
||||
{
|
||||
struct dwc2_hsotg_req *req, *treq;
|
||||
unsigned int size;
|
||||
|
||||
ep->req = NULL;
|
||||
|
||||
list_for_each_entry_safe(req, treq, &ep->queue, queue)
|
||||
dwc2_hsotg_complete_request(hsotg, ep, req,
|
||||
result);
|
||||
while (!list_empty(&ep->queue)) {
|
||||
struct dwc2_hsotg_req *req = get_ep_head(ep);
|
||||
|
||||
dwc2_hsotg_complete_request(hsotg, ep, req, result);
|
||||
}
|
||||
|
||||
if (!hsotg->dedicated_fifos)
|
||||
return;
|
||||
|
@ -252,12 +252,25 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
|
||||
reg |= DWC3_DCTL_CSFTRST;
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||
|
||||
/*
|
||||
* For DWC_usb31 controller 1.90a and later, the DCTL.CSFRST bit
|
||||
* is cleared only after all the clocks are synchronized. This can
|
||||
* take a little more than 50ms. Set the polling rate at 20ms
|
||||
* for 10 times instead.
|
||||
*/
|
||||
if (dwc3_is_usb31(dwc) && dwc->revision >= DWC3_USB31_REVISION_190A)
|
||||
retries = 10;
|
||||
|
||||
do {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
if (!(reg & DWC3_DCTL_CSFTRST))
|
||||
goto done;
|
||||
|
||||
udelay(1);
|
||||
if (dwc3_is_usb31(dwc) &&
|
||||
dwc->revision >= DWC3_USB31_REVISION_190A)
|
||||
msleep(20);
|
||||
else
|
||||
udelay(1);
|
||||
} while (--retries);
|
||||
|
||||
phy_exit(dwc->usb3_generic_phy);
|
||||
@ -267,11 +280,11 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
|
||||
|
||||
done:
|
||||
/*
|
||||
* For DWC_usb31 controller, once DWC3_DCTL_CSFTRST bit is cleared,
|
||||
* we must wait at least 50ms before accessing the PHY domain
|
||||
* (synchronization delay). DWC_usb31 programming guide section 1.3.2.
|
||||
* For DWC_usb31 controller 1.80a and prior, once DCTL.CSFRST bit
|
||||
* is cleared, we must wait at least 50ms before accessing the PHY
|
||||
* domain (synchronization delay).
|
||||
*/
|
||||
if (dwc3_is_usb31(dwc))
|
||||
if (dwc3_is_usb31(dwc) && dwc->revision <= DWC3_USB31_REVISION_180A)
|
||||
msleep(50);
|
||||
|
||||
return 0;
|
||||
@ -686,8 +699,7 @@ static void dwc3_core_exit(struct dwc3 *dwc)
|
||||
usb_phy_set_suspend(dwc->usb3_phy, 1);
|
||||
phy_power_off(dwc->usb2_generic_phy);
|
||||
phy_power_off(dwc->usb3_generic_phy);
|
||||
clk_bulk_disable(dwc->num_clks, dwc->clks);
|
||||
clk_bulk_unprepare(dwc->num_clks, dwc->clks);
|
||||
clk_bulk_disable_unprepare(dwc->num_clks, dwc->clks);
|
||||
reset_control_assert(dwc->reset);
|
||||
}
|
||||
|
||||
@ -1309,8 +1321,7 @@ static void dwc3_get_properties(struct dwc3 *dwc)
|
||||
dwc->lpm_nyet_threshold = lpm_nyet_threshold;
|
||||
dwc->tx_de_emphasis = tx_de_emphasis;
|
||||
|
||||
dwc->hird_threshold = hird_threshold
|
||||
| (dwc->is_utmi_l1_suspend << 4);
|
||||
dwc->hird_threshold = hird_threshold;
|
||||
|
||||
dwc->rx_thr_num_pkt_prd = rx_thr_num_pkt_prd;
|
||||
dwc->rx_max_burst_prd = rx_max_burst_prd;
|
||||
@ -1435,7 +1446,7 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
if (dev->of_node) {
|
||||
dwc->num_clks = ARRAY_SIZE(dwc3_core_clks);
|
||||
|
||||
ret = clk_bulk_get(dev, dwc->num_clks, dwc->clks);
|
||||
ret = devm_clk_bulk_get(dev, dwc->num_clks, dwc->clks);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
return ret;
|
||||
/*
|
||||
@ -1448,16 +1459,12 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
|
||||
ret = reset_control_deassert(dwc->reset);
|
||||
if (ret)
|
||||
goto put_clks;
|
||||
return ret;
|
||||
|
||||
ret = clk_bulk_prepare(dwc->num_clks, dwc->clks);
|
||||
ret = clk_bulk_prepare_enable(dwc->num_clks, dwc->clks);
|
||||
if (ret)
|
||||
goto assert_reset;
|
||||
|
||||
ret = clk_bulk_enable(dwc->num_clks, dwc->clks);
|
||||
if (ret)
|
||||
goto unprepare_clks;
|
||||
|
||||
if (!dwc3_core_is_valid(dwc)) {
|
||||
dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n");
|
||||
ret = -ENODEV;
|
||||
@ -1530,13 +1537,9 @@ err1:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
disable_clks:
|
||||
clk_bulk_disable(dwc->num_clks, dwc->clks);
|
||||
unprepare_clks:
|
||||
clk_bulk_unprepare(dwc->num_clks, dwc->clks);
|
||||
clk_bulk_disable_unprepare(dwc->num_clks, dwc->clks);
|
||||
assert_reset:
|
||||
reset_control_assert(dwc->reset);
|
||||
put_clks:
|
||||
clk_bulk_put(dwc->num_clks, dwc->clks);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -1559,7 +1562,6 @@ static int dwc3_remove(struct platform_device *pdev)
|
||||
|
||||
dwc3_free_event_buffers(dwc);
|
||||
dwc3_free_scratch_buffers(dwc);
|
||||
clk_bulk_put(dwc->num_clks, dwc->clks);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1573,14 +1575,10 @@ static int dwc3_core_init_for_resume(struct dwc3 *dwc)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_bulk_prepare(dwc->num_clks, dwc->clks);
|
||||
ret = clk_bulk_prepare_enable(dwc->num_clks, dwc->clks);
|
||||
if (ret)
|
||||
goto assert_reset;
|
||||
|
||||
ret = clk_bulk_enable(dwc->num_clks, dwc->clks);
|
||||
if (ret)
|
||||
goto unprepare_clks;
|
||||
|
||||
ret = dwc3_core_init(dwc);
|
||||
if (ret)
|
||||
goto disable_clks;
|
||||
@ -1588,9 +1586,7 @@ static int dwc3_core_init_for_resume(struct dwc3 *dwc)
|
||||
return 0;
|
||||
|
||||
disable_clks:
|
||||
clk_bulk_disable(dwc->num_clks, dwc->clks);
|
||||
unprepare_clks:
|
||||
clk_bulk_unprepare(dwc->num_clks, dwc->clks);
|
||||
clk_bulk_disable_unprepare(dwc->num_clks, dwc->clks);
|
||||
assert_reset:
|
||||
reset_control_assert(dwc->reset);
|
||||
|
||||
|
@ -1137,6 +1137,8 @@ struct dwc3 {
|
||||
#define DWC3_USB31_REVISION_120A (0x3132302a | DWC3_REVISION_IS_DWC31)
|
||||
#define DWC3_USB31_REVISION_160A (0x3136302a | DWC3_REVISION_IS_DWC31)
|
||||
#define DWC3_USB31_REVISION_170A (0x3137302a | DWC3_REVISION_IS_DWC31)
|
||||
#define DWC3_USB31_REVISION_180A (0x3138302a | DWC3_REVISION_IS_DWC31)
|
||||
#define DWC3_USB31_REVISION_190A (0x3139302a | DWC3_REVISION_IS_DWC31)
|
||||
|
||||
u32 version_type;
|
||||
|
||||
|
@ -246,258 +246,6 @@ static inline const char *dwc3_gadget_event_string(char *str, size_t size,
|
||||
return str;
|
||||
}
|
||||
|
||||
static inline void dwc3_decode_get_status(__u8 t, __u16 i, __u16 l, char *str,
|
||||
size_t size)
|
||||
{
|
||||
switch (t & USB_RECIP_MASK) {
|
||||
case USB_RECIP_DEVICE:
|
||||
snprintf(str, size, "Get Device Status(Length = %d)", l);
|
||||
break;
|
||||
case USB_RECIP_INTERFACE:
|
||||
snprintf(str, size, "Get Interface Status(Intf = %d, Length = %d)",
|
||||
i, l);
|
||||
break;
|
||||
case USB_RECIP_ENDPOINT:
|
||||
snprintf(str, size, "Get Endpoint Status(ep%d%s)",
|
||||
i & ~USB_DIR_IN,
|
||||
i & USB_DIR_IN ? "in" : "out");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void dwc3_decode_set_clear_feature(__u8 t, __u8 b, __u16 v,
|
||||
__u16 i, char *str, size_t size)
|
||||
{
|
||||
switch (t & USB_RECIP_MASK) {
|
||||
case USB_RECIP_DEVICE:
|
||||
snprintf(str, size, "%s Device Feature(%s%s)",
|
||||
b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
|
||||
({char *s;
|
||||
switch (v) {
|
||||
case USB_DEVICE_SELF_POWERED:
|
||||
s = "Self Powered";
|
||||
break;
|
||||
case USB_DEVICE_REMOTE_WAKEUP:
|
||||
s = "Remote Wakeup";
|
||||
break;
|
||||
case USB_DEVICE_TEST_MODE:
|
||||
s = "Test Mode";
|
||||
break;
|
||||
case USB_DEVICE_U1_ENABLE:
|
||||
s = "U1 Enable";
|
||||
break;
|
||||
case USB_DEVICE_U2_ENABLE:
|
||||
s = "U2 Enable";
|
||||
break;
|
||||
case USB_DEVICE_LTM_ENABLE:
|
||||
s = "LTM Enable";
|
||||
break;
|
||||
default:
|
||||
s = "UNKNOWN";
|
||||
} s; }),
|
||||
v == USB_DEVICE_TEST_MODE ?
|
||||
({ char *s;
|
||||
switch (i) {
|
||||
case TEST_J:
|
||||
s = ": TEST_J";
|
||||
break;
|
||||
case TEST_K:
|
||||
s = ": TEST_K";
|
||||
break;
|
||||
case TEST_SE0_NAK:
|
||||
s = ": TEST_SE0_NAK";
|
||||
break;
|
||||
case TEST_PACKET:
|
||||
s = ": TEST_PACKET";
|
||||
break;
|
||||
case TEST_FORCE_EN:
|
||||
s = ": TEST_FORCE_EN";
|
||||
break;
|
||||
default:
|
||||
s = ": UNKNOWN";
|
||||
} s; }) : "");
|
||||
break;
|
||||
case USB_RECIP_INTERFACE:
|
||||
snprintf(str, size, "%s Interface Feature(%s)",
|
||||
b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
|
||||
v == USB_INTRF_FUNC_SUSPEND ?
|
||||
"Function Suspend" : "UNKNOWN");
|
||||
break;
|
||||
case USB_RECIP_ENDPOINT:
|
||||
snprintf(str, size, "%s Endpoint Feature(%s ep%d%s)",
|
||||
b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
|
||||
v == USB_ENDPOINT_HALT ? "Halt" : "UNKNOWN",
|
||||
i & ~USB_DIR_IN,
|
||||
i & USB_DIR_IN ? "in" : "out");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void dwc3_decode_set_address(__u16 v, char *str, size_t size)
|
||||
{
|
||||
snprintf(str, size, "Set Address(Addr = %02x)", v);
|
||||
}
|
||||
|
||||
static inline void dwc3_decode_get_set_descriptor(__u8 t, __u8 b, __u16 v,
|
||||
__u16 i, __u16 l, char *str, size_t size)
|
||||
{
|
||||
snprintf(str, size, "%s %s Descriptor(Index = %d, Length = %d)",
|
||||
b == USB_REQ_GET_DESCRIPTOR ? "Get" : "Set",
|
||||
({ char *s;
|
||||
switch (v >> 8) {
|
||||
case USB_DT_DEVICE:
|
||||
s = "Device";
|
||||
break;
|
||||
case USB_DT_CONFIG:
|
||||
s = "Configuration";
|
||||
break;
|
||||
case USB_DT_STRING:
|
||||
s = "String";
|
||||
break;
|
||||
case USB_DT_INTERFACE:
|
||||
s = "Interface";
|
||||
break;
|
||||
case USB_DT_ENDPOINT:
|
||||
s = "Endpoint";
|
||||
break;
|
||||
case USB_DT_DEVICE_QUALIFIER:
|
||||
s = "Device Qualifier";
|
||||
break;
|
||||
case USB_DT_OTHER_SPEED_CONFIG:
|
||||
s = "Other Speed Config";
|
||||
break;
|
||||
case USB_DT_INTERFACE_POWER:
|
||||
s = "Interface Power";
|
||||
break;
|
||||
case USB_DT_OTG:
|
||||
s = "OTG";
|
||||
break;
|
||||
case USB_DT_DEBUG:
|
||||
s = "Debug";
|
||||
break;
|
||||
case USB_DT_INTERFACE_ASSOCIATION:
|
||||
s = "Interface Association";
|
||||
break;
|
||||
case USB_DT_BOS:
|
||||
s = "BOS";
|
||||
break;
|
||||
case USB_DT_DEVICE_CAPABILITY:
|
||||
s = "Device Capability";
|
||||
break;
|
||||
case USB_DT_PIPE_USAGE:
|
||||
s = "Pipe Usage";
|
||||
break;
|
||||
case USB_DT_SS_ENDPOINT_COMP:
|
||||
s = "SS Endpoint Companion";
|
||||
break;
|
||||
case USB_DT_SSP_ISOC_ENDPOINT_COMP:
|
||||
s = "SSP Isochronous Endpoint Companion";
|
||||
break;
|
||||
default:
|
||||
s = "UNKNOWN";
|
||||
break;
|
||||
} s; }), v & 0xff, l);
|
||||
}
|
||||
|
||||
|
||||
static inline void dwc3_decode_get_configuration(__u16 l, char *str,
|
||||
size_t size)
|
||||
{
|
||||
snprintf(str, size, "Get Configuration(Length = %d)", l);
|
||||
}
|
||||
|
||||
static inline void dwc3_decode_set_configuration(__u8 v, char *str, size_t size)
|
||||
{
|
||||
snprintf(str, size, "Set Configuration(Config = %d)", v);
|
||||
}
|
||||
|
||||
static inline void dwc3_decode_get_intf(__u16 i, __u16 l, char *str,
|
||||
size_t size)
|
||||
{
|
||||
snprintf(str, size, "Get Interface(Intf = %d, Length = %d)", i, l);
|
||||
}
|
||||
|
||||
static inline void dwc3_decode_set_intf(__u8 v, __u16 i, char *str, size_t size)
|
||||
{
|
||||
snprintf(str, size, "Set Interface(Intf = %d, Alt.Setting = %d)", i, v);
|
||||
}
|
||||
|
||||
static inline void dwc3_decode_synch_frame(__u16 i, __u16 l, char *str,
|
||||
size_t size)
|
||||
{
|
||||
snprintf(str, size, "Synch Frame(Endpoint = %d, Length = %d)", i, l);
|
||||
}
|
||||
|
||||
static inline void dwc3_decode_set_sel(__u16 l, char *str, size_t size)
|
||||
{
|
||||
snprintf(str, size, "Set SEL(Length = %d)", l);
|
||||
}
|
||||
|
||||
static inline void dwc3_decode_set_isoch_delay(__u8 v, char *str, size_t size)
|
||||
{
|
||||
snprintf(str, size, "Set Isochronous Delay(Delay = %d ns)", v);
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_decode_ctrl - returns a string represetion of ctrl request
|
||||
*/
|
||||
static inline const char *dwc3_decode_ctrl(char *str, size_t size,
|
||||
__u8 bRequestType, __u8 bRequest, __u16 wValue, __u16 wIndex,
|
||||
__u16 wLength)
|
||||
{
|
||||
switch (bRequest) {
|
||||
case USB_REQ_GET_STATUS:
|
||||
dwc3_decode_get_status(bRequestType, wIndex, wLength, str,
|
||||
size);
|
||||
break;
|
||||
case USB_REQ_CLEAR_FEATURE:
|
||||
case USB_REQ_SET_FEATURE:
|
||||
dwc3_decode_set_clear_feature(bRequestType, bRequest, wValue,
|
||||
wIndex, str, size);
|
||||
break;
|
||||
case USB_REQ_SET_ADDRESS:
|
||||
dwc3_decode_set_address(wValue, str, size);
|
||||
break;
|
||||
case USB_REQ_GET_DESCRIPTOR:
|
||||
case USB_REQ_SET_DESCRIPTOR:
|
||||
dwc3_decode_get_set_descriptor(bRequestType, bRequest, wValue,
|
||||
wIndex, wLength, str, size);
|
||||
break;
|
||||
case USB_REQ_GET_CONFIGURATION:
|
||||
dwc3_decode_get_configuration(wLength, str, size);
|
||||
break;
|
||||
case USB_REQ_SET_CONFIGURATION:
|
||||
dwc3_decode_set_configuration(wValue, str, size);
|
||||
break;
|
||||
case USB_REQ_GET_INTERFACE:
|
||||
dwc3_decode_get_intf(wIndex, wLength, str, size);
|
||||
break;
|
||||
case USB_REQ_SET_INTERFACE:
|
||||
dwc3_decode_set_intf(wValue, wIndex, str, size);
|
||||
break;
|
||||
case USB_REQ_SYNCH_FRAME:
|
||||
dwc3_decode_synch_frame(wIndex, wLength, str, size);
|
||||
break;
|
||||
case USB_REQ_SET_SEL:
|
||||
dwc3_decode_set_sel(wLength, str, size);
|
||||
break;
|
||||
case USB_REQ_SET_ISOCH_DELAY:
|
||||
dwc3_decode_set_isoch_delay(wValue, str, size);
|
||||
break;
|
||||
default:
|
||||
snprintf(str, size, "%02x %02x %02x %02x %02x %02x %02x %02x",
|
||||
bRequestType, bRequest,
|
||||
cpu_to_le16(wValue) & 0xff,
|
||||
cpu_to_le16(wValue) >> 8,
|
||||
cpu_to_le16(wIndex) & 0xff,
|
||||
cpu_to_le16(wIndex) >> 8,
|
||||
cpu_to_le16(wLength) & 0xff,
|
||||
cpu_to_le16(wLength) >> 8);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_ep_event_string - returns event name
|
||||
* @event: then event code
|
||||
|
@ -81,7 +81,6 @@ static int kdwc3_probe(struct platform_device *pdev)
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct dwc3_keystone *kdwc;
|
||||
struct resource *res;
|
||||
int error, irq;
|
||||
|
||||
kdwc = devm_kzalloc(dev, sizeof(*kdwc), GFP_KERNEL);
|
||||
@ -92,8 +91,7 @@ static int kdwc3_probe(struct platform_device *pdev)
|
||||
|
||||
kdwc->dev = dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
kdwc->usbss = devm_ioremap_resource(dev, res);
|
||||
kdwc->usbss = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(kdwc->usbss))
|
||||
return PTR_ERR(kdwc->usbss);
|
||||
|
||||
|
@ -562,7 +562,13 @@ static int __maybe_unused dwc3_meson_g12a_runtime_resume(struct device *dev)
|
||||
static int __maybe_unused dwc3_meson_g12a_suspend(struct device *dev)
|
||||
{
|
||||
struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
|
||||
int i;
|
||||
int i, ret;
|
||||
|
||||
if (priv->vbus && priv->otg_phy_mode == PHY_MODE_USB_HOST) {
|
||||
ret = regulator_disable(priv->vbus);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0 ; i < PHY_COUNT ; ++i) {
|
||||
phy_power_off(priv->phys[i]);
|
||||
@ -597,6 +603,12 @@ static int __maybe_unused dwc3_meson_g12a_resume(struct device *dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (priv->vbus && priv->otg_phy_mode == PHY_MODE_USB_HOST) {
|
||||
ret = regulator_enable(priv->vbus);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,6 @@
|
||||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/platform_data/dwc3-omap.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/ioport.h>
|
||||
@ -106,6 +105,12 @@
|
||||
#define USBOTGSS_UTMI_OTG_CTRL_SESSVALID BIT(2)
|
||||
#define USBOTGSS_UTMI_OTG_CTRL_VBUSVALID BIT(1)
|
||||
|
||||
enum dwc3_omap_utmi_mode {
|
||||
DWC3_OMAP_UTMI_MODE_UNKNOWN = 0,
|
||||
DWC3_OMAP_UTMI_MODE_HW,
|
||||
DWC3_OMAP_UTMI_MODE_SW,
|
||||
};
|
||||
|
||||
struct dwc3_omap {
|
||||
struct device *dev;
|
||||
|
||||
@ -446,7 +451,6 @@ static int dwc3_omap_probe(struct platform_device *pdev)
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
|
||||
struct dwc3_omap *omap;
|
||||
struct resource *res;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct regulator *vbus_reg = NULL;
|
||||
|
||||
@ -472,8 +476,7 @@ static int dwc3_omap_probe(struct platform_device *pdev)
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(dev, res);
|
||||
base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
|
@ -255,24 +255,26 @@ static int st_dwc3_probe(struct platform_device *pdev)
|
||||
if (!child) {
|
||||
dev_err(&pdev->dev, "failed to find dwc3 core node\n");
|
||||
ret = -ENODEV;
|
||||
goto undo_softreset;
|
||||
goto err_node_put;
|
||||
}
|
||||
|
||||
/* Allocate and initialize the core */
|
||||
ret = of_platform_populate(node, NULL, NULL, dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to add dwc3 core\n");
|
||||
goto undo_softreset;
|
||||
goto err_node_put;
|
||||
}
|
||||
|
||||
child_pdev = of_find_device_by_node(child);
|
||||
if (!child_pdev) {
|
||||
dev_err(dev, "failed to find dwc3 core device\n");
|
||||
ret = -ENODEV;
|
||||
goto undo_softreset;
|
||||
goto err_node_put;
|
||||
}
|
||||
|
||||
dwc3_data->dr_mode = usb_get_dr_mode(&child_pdev->dev);
|
||||
of_node_put(child);
|
||||
of_dev_put(child_pdev);
|
||||
|
||||
/*
|
||||
* Configure the USB port as device or host according to the static
|
||||
@ -292,6 +294,8 @@ static int st_dwc3_probe(struct platform_device *pdev)
|
||||
platform_set_drvdata(pdev, dwc3_data);
|
||||
return 0;
|
||||
|
||||
err_node_put:
|
||||
of_node_put(child);
|
||||
undo_softreset:
|
||||
reset_control_assert(dwc3_data->rstc_rst);
|
||||
undo_powerdown:
|
||||
|
@ -2078,6 +2078,26 @@ static void dwc3_gadget_config_params(struct usb_gadget *g,
|
||||
{
|
||||
struct dwc3 *dwc = gadget_to_dwc(g);
|
||||
|
||||
params->besl_baseline = USB_DEFAULT_BESL_UNSPECIFIED;
|
||||
params->besl_deep = USB_DEFAULT_BESL_UNSPECIFIED;
|
||||
|
||||
/* Recommended BESL */
|
||||
if (!dwc->dis_enblslpm_quirk) {
|
||||
/*
|
||||
* If the recommended BESL baseline is 0 or if the BESL deep is
|
||||
* less than 2, Microsoft's Windows 10 host usb stack will issue
|
||||
* a usb reset immediately after it receives the extended BOS
|
||||
* descriptor and the enumeration will fail. To maintain
|
||||
* compatibility with the Windows' usb stack, let's set the
|
||||
* recommended BESL baseline to 1 and clamp the BESL deep to be
|
||||
* within 2 to 15.
|
||||
*/
|
||||
params->besl_baseline = 1;
|
||||
if (dwc->is_utmi_l1_suspend)
|
||||
params->besl_deep =
|
||||
clamp_t(u8, dwc->hird_threshold, 2, 15);
|
||||
}
|
||||
|
||||
/* U1 Device exit Latency */
|
||||
if (dwc->dis_u1_entry_quirk)
|
||||
params->bU1devExitLat = 0;
|
||||
@ -2868,7 +2888,8 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg &= ~(DWC3_DCTL_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN);
|
||||
|
||||
reg |= DWC3_DCTL_HIRD_THRES(dwc->hird_threshold);
|
||||
reg |= DWC3_DCTL_HIRD_THRES(dwc->hird_threshold |
|
||||
(dwc->is_utmi_l1_suspend << 4));
|
||||
|
||||
/*
|
||||
* When dwc3 revisions >= 2.40a, LPM Erratum is enabled and
|
||||
@ -3318,7 +3339,6 @@ int dwc3_gadget_init(struct dwc3 *dwc)
|
||||
dwc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
dwc->gadget.sg_supported = true;
|
||||
dwc->gadget.name = "dwc3-gadget";
|
||||
dwc->gadget.is_otg = dwc->dr_mode == USB_DR_MODE_OTG;
|
||||
dwc->gadget.lpm_capable = true;
|
||||
|
||||
/*
|
||||
|
@ -85,7 +85,7 @@ int dwc3_host_init(struct dwc3 *dwc)
|
||||
DWC3_XHCI_RESOURCES_NUM);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "couldn't add resources to xHCI device\n");
|
||||
goto err1;
|
||||
goto err;
|
||||
}
|
||||
|
||||
memset(props, 0, sizeof(struct property_entry) * ARRAY_SIZE(props));
|
||||
@ -112,37 +112,23 @@ int dwc3_host_init(struct dwc3 *dwc)
|
||||
ret = platform_device_add_properties(xhci, props);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to add properties to xHCI\n");
|
||||
goto err1;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
phy_create_lookup(dwc->usb2_generic_phy, "usb2-phy",
|
||||
dev_name(dwc->dev));
|
||||
phy_create_lookup(dwc->usb3_generic_phy, "usb3-phy",
|
||||
dev_name(dwc->dev));
|
||||
|
||||
ret = platform_device_add(xhci);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to register xHCI device\n");
|
||||
goto err2;
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err2:
|
||||
phy_remove_lookup(dwc->usb2_generic_phy, "usb2-phy",
|
||||
dev_name(dwc->dev));
|
||||
phy_remove_lookup(dwc->usb3_generic_phy, "usb3-phy",
|
||||
dev_name(dwc->dev));
|
||||
err1:
|
||||
err:
|
||||
platform_device_put(xhci);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void dwc3_host_exit(struct dwc3 *dwc)
|
||||
{
|
||||
phy_remove_lookup(dwc->usb2_generic_phy, "usb2-phy",
|
||||
dev_name(dwc->dev));
|
||||
phy_remove_lookup(dwc->usb3_generic_phy, "usb3-phy",
|
||||
dev_name(dwc->dev));
|
||||
platform_device_unregister(dwc->xhci);
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ DECLARE_EVENT_CLASS(dwc3_log_ctrl,
|
||||
__entry->wIndex = le16_to_cpu(ctrl->wIndex);
|
||||
__entry->wLength = le16_to_cpu(ctrl->wLength);
|
||||
),
|
||||
TP_printk("%s", dwc3_decode_ctrl(__get_str(str), DWC3_MSG_MAX,
|
||||
TP_printk("%s", usb_decode_ctrl(__get_str(str), DWC3_MSG_MAX,
|
||||
__entry->bRequestType,
|
||||
__entry->bRequest, __entry->wValue,
|
||||
__entry->wIndex, __entry->wLength)
|
||||
|
@ -612,6 +612,7 @@ static int bos_desc(struct usb_composite_dev *cdev)
|
||||
struct usb_ext_cap_descriptor *usb_ext;
|
||||
struct usb_dcd_config_params dcd_config_params;
|
||||
struct usb_bos_descriptor *bos = cdev->req->buf;
|
||||
unsigned int besl = 0;
|
||||
|
||||
bos->bLength = USB_DT_BOS_SIZE;
|
||||
bos->bDescriptorType = USB_DT_BOS;
|
||||
@ -619,6 +620,29 @@ static int bos_desc(struct usb_composite_dev *cdev)
|
||||
bos->wTotalLength = cpu_to_le16(USB_DT_BOS_SIZE);
|
||||
bos->bNumDeviceCaps = 0;
|
||||
|
||||
/* Get Controller configuration */
|
||||
if (cdev->gadget->ops->get_config_params) {
|
||||
cdev->gadget->ops->get_config_params(cdev->gadget,
|
||||
&dcd_config_params);
|
||||
} else {
|
||||
dcd_config_params.besl_baseline =
|
||||
USB_DEFAULT_BESL_UNSPECIFIED;
|
||||
dcd_config_params.besl_deep =
|
||||
USB_DEFAULT_BESL_UNSPECIFIED;
|
||||
dcd_config_params.bU1devExitLat =
|
||||
USB_DEFAULT_U1_DEV_EXIT_LAT;
|
||||
dcd_config_params.bU2DevExitLat =
|
||||
cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT);
|
||||
}
|
||||
|
||||
if (dcd_config_params.besl_baseline != USB_DEFAULT_BESL_UNSPECIFIED)
|
||||
besl = USB_BESL_BASELINE_VALID |
|
||||
USB_SET_BESL_BASELINE(dcd_config_params.besl_baseline);
|
||||
|
||||
if (dcd_config_params.besl_deep != USB_DEFAULT_BESL_UNSPECIFIED)
|
||||
besl |= USB_BESL_DEEP_VALID |
|
||||
USB_SET_BESL_DEEP(dcd_config_params.besl_deep);
|
||||
|
||||
/*
|
||||
* A SuperSpeed device shall include the USB2.0 extension descriptor
|
||||
* and shall support LPM when operating in USB2.0 HS mode.
|
||||
@ -629,7 +653,8 @@ static int bos_desc(struct usb_composite_dev *cdev)
|
||||
usb_ext->bLength = USB_DT_USB_EXT_CAP_SIZE;
|
||||
usb_ext->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
|
||||
usb_ext->bDevCapabilityType = USB_CAP_TYPE_EXT;
|
||||
usb_ext->bmAttributes = cpu_to_le32(USB_LPM_SUPPORT | USB_BESL_SUPPORT);
|
||||
usb_ext->bmAttributes = cpu_to_le32(USB_LPM_SUPPORT |
|
||||
USB_BESL_SUPPORT | besl);
|
||||
|
||||
/*
|
||||
* The Superspeed USB Capability descriptor shall be implemented by all
|
||||
@ -650,17 +675,6 @@ static int bos_desc(struct usb_composite_dev *cdev)
|
||||
USB_HIGH_SPEED_OPERATION |
|
||||
USB_5GBPS_OPERATION);
|
||||
ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION;
|
||||
|
||||
/* Get Controller configuration */
|
||||
if (cdev->gadget->ops->get_config_params) {
|
||||
cdev->gadget->ops->get_config_params(cdev->gadget,
|
||||
&dcd_config_params);
|
||||
} else {
|
||||
dcd_config_params.bU1devExitLat =
|
||||
USB_DEFAULT_U1_DEV_EXIT_LAT;
|
||||
dcd_config_params.bU2DevExitLat =
|
||||
cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT);
|
||||
}
|
||||
ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat;
|
||||
ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat;
|
||||
}
|
||||
|
@ -65,14 +65,16 @@ void ast_vhub_done(struct ast_vhub_ep *ep, struct ast_vhub_req *req,
|
||||
void ast_vhub_nuke(struct ast_vhub_ep *ep, int status)
|
||||
{
|
||||
struct ast_vhub_req *req;
|
||||
|
||||
EPDBG(ep, "Nuking\n");
|
||||
int count = 0;
|
||||
|
||||
/* Beware, lock will be dropped & req-acquired by done() */
|
||||
while (!list_empty(&ep->queue)) {
|
||||
req = list_first_entry(&ep->queue, struct ast_vhub_req, queue);
|
||||
ast_vhub_done(ep, req, status);
|
||||
count++;
|
||||
}
|
||||
if (count)
|
||||
EPDBG(ep, "Nuked %d request(s)\n", count);
|
||||
}
|
||||
|
||||
struct usb_request *ast_vhub_alloc_request(struct usb_ep *u_ep,
|
||||
|
@ -50,11 +50,14 @@ void ast_vhub_dev_irq(struct ast_vhub_dev *d)
|
||||
|
||||
static void ast_vhub_dev_enable(struct ast_vhub_dev *d)
|
||||
{
|
||||
u32 reg, hmsk;
|
||||
u32 reg, hmsk, i;
|
||||
|
||||
if (d->enabled)
|
||||
return;
|
||||
|
||||
/* Cleanup EP0 state */
|
||||
ast_vhub_reset_ep0(d);
|
||||
|
||||
/* Enable device and its EP0 interrupts */
|
||||
reg = VHUB_DEV_EN_ENABLE_PORT |
|
||||
VHUB_DEV_EN_EP0_IN_ACK_IRQEN |
|
||||
@ -73,6 +76,19 @@ static void ast_vhub_dev_enable(struct ast_vhub_dev *d)
|
||||
/* Set EP0 DMA buffer address */
|
||||
writel(d->ep0.buf_dma, d->regs + AST_VHUB_DEV_EP0_DATA);
|
||||
|
||||
/* Clear stall on all EPs */
|
||||
for (i = 0; i < AST_VHUB_NUM_GEN_EPs; i++) {
|
||||
struct ast_vhub_ep *ep = d->epns[i];
|
||||
|
||||
if (ep && (ep->epn.stalled || ep->epn.wedged)) {
|
||||
ep->epn.stalled = false;
|
||||
ep->epn.wedged = false;
|
||||
ast_vhub_update_epn_stall(ep);
|
||||
}
|
||||
}
|
||||
|
||||
/* Additional cleanups */
|
||||
d->wakeup_en = false;
|
||||
d->enabled = true;
|
||||
}
|
||||
|
||||
@ -93,7 +109,6 @@ static void ast_vhub_dev_disable(struct ast_vhub_dev *d)
|
||||
writel(0, d->regs + AST_VHUB_DEV_EN_CTRL);
|
||||
d->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
d->enabled = false;
|
||||
d->suspended = false;
|
||||
}
|
||||
|
||||
static int ast_vhub_dev_feature(struct ast_vhub_dev *d,
|
||||
@ -201,14 +216,19 @@ int ast_vhub_std_dev_request(struct ast_vhub_ep *ep,
|
||||
u16 wValue, wIndex;
|
||||
|
||||
/* No driver, we shouldn't be enabled ... */
|
||||
if (!d->driver || !d->enabled || d->suspended) {
|
||||
if (!d->driver || !d->enabled) {
|
||||
EPDBG(ep,
|
||||
"Device is wrong state driver=%p enabled=%d"
|
||||
" suspended=%d\n",
|
||||
d->driver, d->enabled, d->suspended);
|
||||
"Device is wrong state driver=%p enabled=%d\n",
|
||||
d->driver, d->enabled);
|
||||
return std_req_stall;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: we used to reject/stall requests while suspended,
|
||||
* we don't do that anymore as we seem to have cases of
|
||||
* mass storage getting very upset.
|
||||
*/
|
||||
|
||||
/* First packet, grab speed */
|
||||
if (d->gadget.speed == USB_SPEED_UNKNOWN) {
|
||||
d->gadget.speed = ep->vhub->speed;
|
||||
@ -449,8 +469,7 @@ static const struct usb_gadget_ops ast_vhub_udc_ops = {
|
||||
|
||||
void ast_vhub_dev_suspend(struct ast_vhub_dev *d)
|
||||
{
|
||||
d->suspended = true;
|
||||
if (d->driver) {
|
||||
if (d->driver && d->driver->suspend) {
|
||||
spin_unlock(&d->vhub->lock);
|
||||
d->driver->suspend(&d->gadget);
|
||||
spin_lock(&d->vhub->lock);
|
||||
@ -459,8 +478,7 @@ void ast_vhub_dev_suspend(struct ast_vhub_dev *d)
|
||||
|
||||
void ast_vhub_dev_resume(struct ast_vhub_dev *d)
|
||||
{
|
||||
d->suspended = false;
|
||||
if (d->driver) {
|
||||
if (d->driver && d->driver->resume) {
|
||||
spin_unlock(&d->vhub->lock);
|
||||
d->driver->resume(&d->gadget);
|
||||
spin_lock(&d->vhub->lock);
|
||||
@ -469,46 +487,28 @@ void ast_vhub_dev_resume(struct ast_vhub_dev *d)
|
||||
|
||||
void ast_vhub_dev_reset(struct ast_vhub_dev *d)
|
||||
{
|
||||
/*
|
||||
* If speed is not set, we enable the port. If it is,
|
||||
* send reset to the gadget and reset "speed".
|
||||
*
|
||||
* Speed is an indication that we have got the first
|
||||
* setup packet to the device.
|
||||
*/
|
||||
if (d->gadget.speed == USB_SPEED_UNKNOWN && !d->enabled) {
|
||||
DDBG(d, "Reset at unknown speed of disabled device, enabling...\n");
|
||||
ast_vhub_dev_enable(d);
|
||||
d->suspended = false;
|
||||
/* No driver, just disable the device and return */
|
||||
if (!d->driver) {
|
||||
ast_vhub_dev_disable(d);
|
||||
return;
|
||||
}
|
||||
if (d->gadget.speed != USB_SPEED_UNKNOWN && d->driver) {
|
||||
unsigned int i;
|
||||
|
||||
DDBG(d, "Reset at known speed of bound device, resetting...\n");
|
||||
/* If the port isn't enabled, just enable it */
|
||||
if (!d->enabled) {
|
||||
DDBG(d, "Reset of disabled device, enabling...\n");
|
||||
ast_vhub_dev_enable(d);
|
||||
} else {
|
||||
DDBG(d, "Reset of enabled device, resetting...\n");
|
||||
spin_unlock(&d->vhub->lock);
|
||||
d->driver->reset(&d->gadget);
|
||||
usb_gadget_udc_reset(&d->gadget, d->driver);
|
||||
spin_lock(&d->vhub->lock);
|
||||
|
||||
/*
|
||||
* Disable/re-enable HW, this will clear the address
|
||||
* Disable and maybe re-enable HW, this will clear the address
|
||||
* and speed setting.
|
||||
*/
|
||||
ast_vhub_dev_disable(d);
|
||||
ast_vhub_dev_enable(d);
|
||||
|
||||
/* Clear stall on all EPs */
|
||||
for (i = 0; i < AST_VHUB_NUM_GEN_EPs; i++) {
|
||||
struct ast_vhub_ep *ep = d->epns[i];
|
||||
|
||||
if (ep && ep->epn.stalled) {
|
||||
ep->epn.stalled = false;
|
||||
ast_vhub_update_epn_stall(ep);
|
||||
}
|
||||
}
|
||||
|
||||
/* Additional cleanups */
|
||||
d->wakeup_en = false;
|
||||
d->suspended = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,18 +105,20 @@ void ast_vhub_ep0_handle_setup(struct ast_vhub_ep *ep)
|
||||
(crq.bRequestType & USB_DIR_IN) ? "in" : "out",
|
||||
ep->ep0.state);
|
||||
|
||||
/* Check our state, cancel pending requests if needed */
|
||||
if (ep->ep0.state != ep0_state_token) {
|
||||
/*
|
||||
* Check our state, cancel pending requests if needed
|
||||
*
|
||||
* Note: Under some circumstances, we can get a new setup
|
||||
* packet while waiting for the stall ack, just accept it.
|
||||
*
|
||||
* In any case, a SETUP packet in wrong state should have
|
||||
* reset the HW state machine, so let's just log, nuke
|
||||
* requests, move on.
|
||||
*/
|
||||
if (ep->ep0.state != ep0_state_token &&
|
||||
ep->ep0.state != ep0_state_stall) {
|
||||
EPDBG(ep, "wrong state\n");
|
||||
ast_vhub_nuke(ep, -EIO);
|
||||
|
||||
/*
|
||||
* Accept the packet regardless, this seems to happen
|
||||
* when stalling a SETUP packet that has an OUT data
|
||||
* phase.
|
||||
*/
|
||||
ast_vhub_nuke(ep, 0);
|
||||
goto stall;
|
||||
}
|
||||
|
||||
/* Calculate next state for EP0 */
|
||||
@ -165,7 +167,7 @@ void ast_vhub_ep0_handle_setup(struct ast_vhub_ep *ep)
|
||||
stall:
|
||||
EPDBG(ep, "stalling\n");
|
||||
writel(VHUB_EP0_CTRL_STALL, ep->ep0.ctlstat);
|
||||
ep->ep0.state = ep0_state_status;
|
||||
ep->ep0.state = ep0_state_stall;
|
||||
ep->ep0.dir_in = false;
|
||||
return;
|
||||
|
||||
@ -299,8 +301,8 @@ void ast_vhub_ep0_handle_ack(struct ast_vhub_ep *ep, bool in_ack)
|
||||
if ((ep->ep0.dir_in && (stat & VHUB_EP0_TX_BUFF_RDY)) ||
|
||||
(!ep->ep0.dir_in && (stat & VHUB_EP0_RX_BUFF_RDY)) ||
|
||||
(ep->ep0.dir_in != in_ack)) {
|
||||
/* In that case, ignore interrupt */
|
||||
dev_warn(dev, "irq state mismatch");
|
||||
stall = true;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
@ -335,12 +337,22 @@ void ast_vhub_ep0_handle_ack(struct ast_vhub_ep *ep, bool in_ack)
|
||||
dev_warn(dev, "status direction mismatch\n");
|
||||
stall = true;
|
||||
}
|
||||
break;
|
||||
case ep0_state_stall:
|
||||
/*
|
||||
* There shouldn't be any request left, but nuke just in case
|
||||
* otherwise the stale request will block subsequent ones
|
||||
*/
|
||||
ast_vhub_nuke(ep, -EIO);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Reset to token state */
|
||||
ep->ep0.state = ep0_state_token;
|
||||
if (stall)
|
||||
/* Reset to token state or stall */
|
||||
if (stall) {
|
||||
writel(VHUB_EP0_CTRL_STALL, ep->ep0.ctlstat);
|
||||
ep->ep0.state = ep0_state_stall;
|
||||
} else
|
||||
ep->ep0.state = ep0_state_token;
|
||||
}
|
||||
|
||||
static int ast_vhub_ep0_queue(struct usb_ep* u_ep, struct usb_request *u_req,
|
||||
@ -367,7 +379,7 @@ static int ast_vhub_ep0_queue(struct usb_ep* u_ep, struct usb_request *u_req,
|
||||
return -EINVAL;
|
||||
|
||||
/* Disabled device */
|
||||
if (ep->dev && (!ep->dev->enabled || ep->dev->suspended))
|
||||
if (ep->dev && !ep->dev->enabled)
|
||||
return -ESHUTDOWN;
|
||||
|
||||
/* Data, no buffer and not internal ? */
|
||||
@ -390,8 +402,12 @@ static int ast_vhub_ep0_queue(struct usb_ep* u_ep, struct usb_request *u_req,
|
||||
spin_lock_irqsave(&vhub->lock, flags);
|
||||
|
||||
/* EP0 can only support a single request at a time */
|
||||
if (!list_empty(&ep->queue) || ep->ep0.state == ep0_state_token) {
|
||||
if (!list_empty(&ep->queue) ||
|
||||
ep->ep0.state == ep0_state_token ||
|
||||
ep->ep0.state == ep0_state_stall) {
|
||||
dev_warn(dev, "EP0: Request in wrong state\n");
|
||||
EPVDBG(ep, "EP0: list_empty=%d state=%d\n",
|
||||
list_empty(&ep->queue), ep->ep0.state);
|
||||
spin_unlock_irqrestore(&vhub->lock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
@ -459,6 +475,15 @@ static const struct usb_ep_ops ast_vhub_ep0_ops = {
|
||||
.free_request = ast_vhub_free_request,
|
||||
};
|
||||
|
||||
void ast_vhub_reset_ep0(struct ast_vhub_dev *dev)
|
||||
{
|
||||
struct ast_vhub_ep *ep = &dev->ep0;
|
||||
|
||||
ast_vhub_nuke(ep, -EIO);
|
||||
ep->ep0.state = ep0_state_token;
|
||||
}
|
||||
|
||||
|
||||
void ast_vhub_init_ep0(struct ast_vhub *vhub, struct ast_vhub_ep *ep,
|
||||
struct ast_vhub_dev *dev)
|
||||
{
|
||||
|
@ -352,7 +352,7 @@ static int ast_vhub_epn_queue(struct usb_ep* u_ep, struct usb_request *u_req,
|
||||
|
||||
/* Endpoint enabled ? */
|
||||
if (!ep->epn.enabled || !u_ep->desc || !ep->dev || !ep->d_idx ||
|
||||
!ep->dev->enabled || ep->dev->suspended) {
|
||||
!ep->dev->enabled) {
|
||||
EPDBG(ep, "Enqueuing request on wrong or disabled EP\n");
|
||||
return -ESHUTDOWN;
|
||||
}
|
||||
|
@ -449,8 +449,15 @@ static void ast_vhub_change_port_stat(struct ast_vhub *vhub,
|
||||
USB_PORT_STAT_C_OVERCURRENT |
|
||||
USB_PORT_STAT_C_RESET |
|
||||
USB_PORT_STAT_C_L1;
|
||||
p->change |= chg;
|
||||
|
||||
/*
|
||||
* We only set USB_PORT_STAT_C_ENABLE if we are disabling
|
||||
* the port as per USB spec, otherwise MacOS gets upset
|
||||
*/
|
||||
if (p->status & USB_PORT_STAT_ENABLE)
|
||||
chg &= ~USB_PORT_STAT_C_ENABLE;
|
||||
|
||||
p->change = chg;
|
||||
ast_vhub_update_hub_ep1(vhub, port);
|
||||
}
|
||||
}
|
||||
@ -723,6 +730,12 @@ enum std_req_rc ast_vhub_class_hub_request(struct ast_vhub_ep *ep,
|
||||
case ClearPortFeature:
|
||||
EPDBG(ep, "ClearPortFeature(%d,%d)\n", wIndex & 0xf, wValue);
|
||||
return ast_vhub_clr_port_feature(ep, wIndex & 0xf, wValue);
|
||||
case ClearTTBuffer:
|
||||
case ResetTT:
|
||||
case StopTT:
|
||||
return std_req_complete;
|
||||
case GetTTState:
|
||||
return ast_vhub_simple_reply(ep, 0, 0, 0, 0);
|
||||
default:
|
||||
EPDBG(ep, "Unknown class request\n");
|
||||
}
|
||||
|
@ -257,6 +257,7 @@ enum ep0_state {
|
||||
ep0_state_token,
|
||||
ep0_state_data,
|
||||
ep0_state_status,
|
||||
ep0_state_stall,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -353,7 +354,6 @@ struct ast_vhub_dev {
|
||||
struct usb_gadget_driver *driver;
|
||||
bool registered : 1;
|
||||
bool wakeup_en : 1;
|
||||
bool suspended : 1;
|
||||
bool enabled : 1;
|
||||
|
||||
/* Endpoint structures */
|
||||
@ -507,6 +507,7 @@ void ast_vhub_init_hw(struct ast_vhub *vhub);
|
||||
/* ep0.c */
|
||||
void ast_vhub_ep0_handle_ack(struct ast_vhub_ep *ep, bool in_ack);
|
||||
void ast_vhub_ep0_handle_setup(struct ast_vhub_ep *ep);
|
||||
void ast_vhub_reset_ep0(struct ast_vhub_dev *dev);
|
||||
void ast_vhub_init_ep0(struct ast_vhub *vhub, struct ast_vhub_ep *ep,
|
||||
struct ast_vhub_dev *dev);
|
||||
int ast_vhub_reply(struct ast_vhub_ep *ep, char *ptr, int len);
|
||||
|
@ -1143,7 +1143,7 @@ static int check_pending_gadget_drivers(struct usb_udc *udc)
|
||||
dev_name(&udc->dev)) == 0) {
|
||||
ret = udc_bind_to_driver(udc, driver);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
list_del(&driver->pending);
|
||||
list_del_init(&driver->pending);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -742,7 +742,6 @@ static inline void udc_protocol_cmd_data_w(struct lpc32xx_udc *udc, u32 cmd,
|
||||
* response data */
|
||||
static u32 udc_protocol_cmd_r(struct lpc32xx_udc *udc, u32 cmd)
|
||||
{
|
||||
u32 tmp;
|
||||
int to = 1000;
|
||||
|
||||
/* Write a command and read data from the protocol engine */
|
||||
@ -752,7 +751,6 @@ static u32 udc_protocol_cmd_r(struct lpc32xx_udc *udc, u32 cmd)
|
||||
/* Write command code */
|
||||
udc_protocol_cmd_w(udc, cmd);
|
||||
|
||||
tmp = readl(USBD_DEVINTST(udc->udp_baseaddr));
|
||||
while ((!(readl(USBD_DEVINTST(udc->udp_baseaddr)) & USBD_CDFULL))
|
||||
&& (to > 0))
|
||||
to--;
|
||||
@ -1992,7 +1990,7 @@ void udc_handle_eps(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep)
|
||||
/* DMA end of transfer completion */
|
||||
static void udc_handle_dma_ep(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep)
|
||||
{
|
||||
u32 status, epstatus;
|
||||
u32 status;
|
||||
struct lpc32xx_request *req;
|
||||
struct lpc32xx_usbd_dd_gad *dd;
|
||||
|
||||
@ -2086,7 +2084,7 @@ static void udc_handle_dma_ep(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep)
|
||||
if (udc_clearep_getsts(udc, ep->hwep_num) & EP_SEL_F) {
|
||||
udc_clearep_getsts(udc, ep->hwep_num);
|
||||
uda_enable_hwepint(udc, ep->hwep_num);
|
||||
epstatus = udc_clearep_getsts(udc, ep->hwep_num);
|
||||
udc_clearep_getsts(udc, ep->hwep_num);
|
||||
|
||||
/* Let the EP interrupt handle the ZLP */
|
||||
return;
|
||||
@ -2198,7 +2196,7 @@ static void udc_handle_ep0_setup(struct lpc32xx_udc *udc)
|
||||
struct lpc32xx_ep *ep, *ep0 = &udc->ep[0];
|
||||
struct usb_ctrlrequest ctrlpkt;
|
||||
int i, bytes;
|
||||
u16 wIndex, wValue, wLength, reqtype, req, tmp;
|
||||
u16 wIndex, wValue, reqtype, req, tmp;
|
||||
|
||||
/* Nuke previous transfers */
|
||||
nuke(ep0, -EPROTO);
|
||||
@ -2214,7 +2212,6 @@ static void udc_handle_ep0_setup(struct lpc32xx_udc *udc)
|
||||
/* Native endianness */
|
||||
wIndex = le16_to_cpu(ctrlpkt.wIndex);
|
||||
wValue = le16_to_cpu(ctrlpkt.wValue);
|
||||
wLength = le16_to_cpu(ctrlpkt.wLength);
|
||||
reqtype = le16_to_cpu(ctrlpkt.bRequestType);
|
||||
|
||||
/* Set direction of EP0 */
|
||||
@ -2265,7 +2262,7 @@ static void udc_handle_ep0_setup(struct lpc32xx_udc *udc)
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case USB_REQ_SET_ADDRESS:
|
||||
if (reqtype == (USB_TYPE_STANDARD | USB_RECIP_DEVICE)) {
|
||||
|
@ -2244,30 +2244,40 @@ static void usb_reinit_338x(struct net2280 *dev)
|
||||
}
|
||||
|
||||
/* Hardware Defect and Workaround */
|
||||
val = readl(&dev->ll_lfps_regs->ll_lfps_5);
|
||||
val = readl(&dev->llregs->ll_lfps_5);
|
||||
val &= ~(0xf << TIMER_LFPS_6US);
|
||||
val |= 0x5 << TIMER_LFPS_6US;
|
||||
writel(val, &dev->ll_lfps_regs->ll_lfps_5);
|
||||
writel(val, &dev->llregs->ll_lfps_5);
|
||||
|
||||
val = readl(&dev->ll_lfps_regs->ll_lfps_6);
|
||||
val = readl(&dev->llregs->ll_lfps_6);
|
||||
val &= ~(0xffff << TIMER_LFPS_80US);
|
||||
val |= 0x0100 << TIMER_LFPS_80US;
|
||||
writel(val, &dev->ll_lfps_regs->ll_lfps_6);
|
||||
writel(val, &dev->llregs->ll_lfps_6);
|
||||
|
||||
/*
|
||||
* AA_AB Errata. Issue 4. Workaround for SuperSpeed USB
|
||||
* Hot Reset Exit Handshake may Fail in Specific Case using
|
||||
* Default Register Settings. Workaround for Enumeration test.
|
||||
*/
|
||||
val = readl(&dev->ll_tsn_regs->ll_tsn_counters_2);
|
||||
val = readl(&dev->llregs->ll_tsn_counters_2);
|
||||
val &= ~(0x1f << HOT_TX_NORESET_TS2);
|
||||
val |= 0x10 << HOT_TX_NORESET_TS2;
|
||||
writel(val, &dev->ll_tsn_regs->ll_tsn_counters_2);
|
||||
writel(val, &dev->llregs->ll_tsn_counters_2);
|
||||
|
||||
val = readl(&dev->ll_tsn_regs->ll_tsn_counters_3);
|
||||
val = readl(&dev->llregs->ll_tsn_counters_3);
|
||||
val &= ~(0x1f << HOT_RX_RESET_TS2);
|
||||
val |= 0x3 << HOT_RX_RESET_TS2;
|
||||
writel(val, &dev->ll_tsn_regs->ll_tsn_counters_3);
|
||||
writel(val, &dev->llregs->ll_tsn_counters_3);
|
||||
|
||||
/*
|
||||
* AB errata. Errata 11. Workaround for Default Duration of LFPS
|
||||
* Handshake Signaling for Device-Initiated U1 Exit is too short.
|
||||
* Without this, various enumeration failures observed with
|
||||
* modern superspeed hosts.
|
||||
*/
|
||||
val = readl(&dev->llregs->ll_lfps_timers_2);
|
||||
writel((val & 0xffff0000) | LFPS_TIMERS_2_WORKAROUND_VALUE,
|
||||
&dev->llregs->ll_lfps_timers_2);
|
||||
|
||||
/*
|
||||
* Set Recovery Idle to Recover bit:
|
||||
@ -2276,10 +2286,10 @@ static void usb_reinit_338x(struct net2280 *dev)
|
||||
* - It is safe to set for all connection speeds; all chip revisions.
|
||||
* - R-M-W to leave other bits undisturbed.
|
||||
* - Reference PLX TT-7372
|
||||
*/
|
||||
val = readl(&dev->ll_chicken_reg->ll_tsn_chicken_bit);
|
||||
*/
|
||||
val = readl(&dev->llregs->ll_tsn_chicken_bit);
|
||||
val |= BIT(RECOVERY_IDLE_TO_RECOVER_FMW);
|
||||
writel(val, &dev->ll_chicken_reg->ll_tsn_chicken_bit);
|
||||
writel(val, &dev->llregs->ll_tsn_chicken_bit);
|
||||
|
||||
INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
|
||||
|
||||
@ -3669,12 +3679,6 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
(base + 0x00b4);
|
||||
dev->llregs = (struct usb338x_ll_regs __iomem *)
|
||||
(base + 0x0700);
|
||||
dev->ll_lfps_regs = (struct usb338x_ll_lfps_regs __iomem *)
|
||||
(base + 0x0748);
|
||||
dev->ll_tsn_regs = (struct usb338x_ll_tsn_regs __iomem *)
|
||||
(base + 0x077c);
|
||||
dev->ll_chicken_reg = (struct usb338x_ll_chi_regs __iomem *)
|
||||
(base + 0x079c);
|
||||
dev->plregs = (struct usb338x_pl_regs __iomem *)
|
||||
(base + 0x0800);
|
||||
usbstat = readl(&dev->usb->usbstat);
|
||||
|
@ -178,9 +178,6 @@ struct net2280 {
|
||||
struct net2280_dep_regs __iomem *dep;
|
||||
struct net2280_ep_regs __iomem *epregs;
|
||||
struct usb338x_ll_regs __iomem *llregs;
|
||||
struct usb338x_ll_lfps_regs __iomem *ll_lfps_regs;
|
||||
struct usb338x_ll_tsn_regs __iomem *ll_tsn_regs;
|
||||
struct usb338x_ll_chi_regs __iomem *ll_chicken_reg;
|
||||
struct usb338x_pl_regs __iomem *plregs;
|
||||
|
||||
struct dma_pool *requests;
|
||||
|
@ -3046,8 +3046,7 @@ static void pch_udc_remove(struct pci_dev *pdev)
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int pch_udc_suspend(struct device *d)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(d);
|
||||
struct pch_udc_dev *dev = pci_get_drvdata(pdev);
|
||||
struct pch_udc_dev *dev = dev_get_drvdata(d);
|
||||
|
||||
pch_udc_disable_interrupts(dev, UDC_DEVINT_MSK);
|
||||
pch_udc_disable_ep_interrupts(dev, UDC_EPINT_MSK_DISABLE_ALL);
|
||||
|
@ -65,7 +65,7 @@ struct fsl_otg_timer *b_data_pulse_tmr, *b_vbus_pulse_tmr, *b_srp_fail_tmr,
|
||||
|
||||
static struct list_head active_timers;
|
||||
|
||||
static struct fsl_otg_config fsl_otg_initdata = {
|
||||
static const struct fsl_otg_config fsl_otg_initdata = {
|
||||
.otg_port = 1,
|
||||
};
|
||||
|
||||
|
@ -1,43 +0,0 @@
|
||||
/**
|
||||
* dwc3-omap.h - OMAP Specific Glue layer, header.
|
||||
*
|
||||
* Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
|
||||
* All rights reserved.
|
||||
*
|
||||
* Author: Felipe Balbi <balbi@ti.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The names of the above-listed copyright holders may not be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* ALTERNATIVELY, this software may be distributed under the terms of the
|
||||
* GNU General Public License ("GPL") version 2, as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
enum dwc3_omap_utmi_mode {
|
||||
DWC3_OMAP_UTMI_MODE_UNKNOWN = 0,
|
||||
DWC3_OMAP_UTMI_MODE_HW,
|
||||
DWC3_OMAP_UTMI_MODE_SW,
|
||||
};
|
@ -70,4 +70,31 @@ extern enum usb_device_speed usb_get_maximum_speed(struct device *dev);
|
||||
*/
|
||||
extern const char *usb_state_string(enum usb_device_state state);
|
||||
|
||||
#ifdef CONFIG_TRACING
|
||||
/**
|
||||
* usb_decode_ctrl - Returns human readable representation of control request.
|
||||
* @str: buffer to return a human-readable representation of control request.
|
||||
* This buffer should have about 200 bytes.
|
||||
* @size: size of str buffer.
|
||||
* @bRequestType: matches the USB bmRequestType field
|
||||
* @bRequest: matches the USB bRequest field
|
||||
* @wValue: matches the USB wValue field (CPU byte order)
|
||||
* @wIndex: matches the USB wIndex field (CPU byte order)
|
||||
* @wLength: matches the USB wLength field (CPU byte order)
|
||||
*
|
||||
* Function returns decoded, formatted and human-readable description of
|
||||
* control request packet.
|
||||
*
|
||||
* The usage scenario for this is for tracepoints, so function as a return
|
||||
* use the same value as in parameters. This approach allows to use this
|
||||
* function in TP_printk
|
||||
*
|
||||
* Important: wValue, wIndex, wLength parameters before invoking this function
|
||||
* should be processed by le16_to_cpu macro.
|
||||
*/
|
||||
extern const char *usb_decode_ctrl(char *str, size_t size, __u8 bRequestType,
|
||||
__u8 bRequest, __u16 wValue, __u16 wIndex,
|
||||
__u16 wLength);
|
||||
#endif
|
||||
|
||||
#endif /* __LINUX_USB_CH9_H */
|
||||
|
@ -291,6 +291,9 @@ struct usb_dcd_config_params {
|
||||
#define USB_DEFAULT_U1_DEV_EXIT_LAT 0x01 /* Less then 1 microsec */
|
||||
__le16 bU2DevExitLat; /* U2 Device exit Latency */
|
||||
#define USB_DEFAULT_U2_DEV_EXIT_LAT 0x1F4 /* Less then 500 microsec */
|
||||
__u8 besl_baseline; /* Recommended baseline BESL (0-15) */
|
||||
__u8 besl_deep; /* Recommended deep BESL (0-15) */
|
||||
#define USB_DEFAULT_BESL_UNSPECIFIED 0xFF /* No recommended value */
|
||||
};
|
||||
|
||||
|
||||
|
@ -597,6 +597,10 @@ extern void usb_ep0_reinit(struct usb_device *);
|
||||
#define GetPortStatus HUB_CLASS_REQ(USB_DIR_IN, USB_RT_PORT, USB_REQ_GET_STATUS)
|
||||
#define SetHubFeature HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_HUB, USB_REQ_SET_FEATURE)
|
||||
#define SetPortFeature HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_PORT, USB_REQ_SET_FEATURE)
|
||||
#define ClearTTBuffer HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_PORT, HUB_CLEAR_TT_BUFFER)
|
||||
#define ResetTT HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_PORT, HUB_RESET_TT)
|
||||
#define GetTTState HUB_CLASS_REQ(USB_DIR_IN, USB_RT_PORT, HUB_GET_TT_STATE)
|
||||
#define StopTT HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_PORT, HUB_STOP_TT)
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
@ -113,7 +113,10 @@ struct usb338x_ll_regs {
|
||||
u32 ll_ltssm_ctrl1;
|
||||
u32 ll_ltssm_ctrl2;
|
||||
u32 ll_ltssm_ctrl3;
|
||||
u32 unused[2];
|
||||
u32 unused1;
|
||||
|
||||
/* 0x710 */
|
||||
u32 unused2;
|
||||
u32 ll_general_ctrl0;
|
||||
u32 ll_general_ctrl1;
|
||||
#define PM_U3_AUTO_EXIT 29
|
||||
@ -136,29 +139,41 @@ struct usb338x_ll_regs {
|
||||
u32 ll_general_ctrl2;
|
||||
#define SELECT_INVERT_LANE_POLARITY 7
|
||||
#define FORCE_INVERT_LANE_POLARITY 6
|
||||
|
||||
/* 0x720 */
|
||||
u32 ll_general_ctrl3;
|
||||
u32 ll_general_ctrl4;
|
||||
u32 ll_error_gen;
|
||||
} __packed;
|
||||
u32 unused3;
|
||||
|
||||
struct usb338x_ll_lfps_regs {
|
||||
/* offset 0x748 */
|
||||
/* 0x730 */
|
||||
u32 unused4[4];
|
||||
|
||||
/* 0x740 */
|
||||
u32 unused5[2];
|
||||
u32 ll_lfps_5;
|
||||
#define TIMER_LFPS_6US 16
|
||||
u32 ll_lfps_6;
|
||||
#define TIMER_LFPS_80US 0
|
||||
} __packed;
|
||||
|
||||
struct usb338x_ll_tsn_regs {
|
||||
/* offset 0x77C */
|
||||
/* 0x750 */
|
||||
u32 unused6[8];
|
||||
|
||||
/* 0x770 */
|
||||
u32 unused7[3];
|
||||
u32 ll_tsn_counters_2;
|
||||
#define HOT_TX_NORESET_TS2 24
|
||||
|
||||
/* 0x780 */
|
||||
u32 ll_tsn_counters_3;
|
||||
#define HOT_RX_RESET_TS2 0
|
||||
} __packed;
|
||||
u32 unused8[3];
|
||||
|
||||
struct usb338x_ll_chi_regs {
|
||||
/* offset 0x79C */
|
||||
/* 0x790 */
|
||||
u32 unused9;
|
||||
u32 ll_lfps_timers_2;
|
||||
#define LFPS_TIMERS_2_WORKAROUND_VALUE 0x084d
|
||||
u32 unused10;
|
||||
u32 ll_tsn_chicken_bit;
|
||||
#define RECOVERY_IDLE_TO_RECOVER_FMW 3
|
||||
} __packed;
|
||||
|
@ -894,6 +894,8 @@ struct usb_ext_cap_descriptor { /* Link Power Management */
|
||||
#define USB_BESL_SUPPORT (1 << 2) /* supports BESL */
|
||||
#define USB_BESL_BASELINE_VALID (1 << 3) /* Baseline BESL valid*/
|
||||
#define USB_BESL_DEEP_VALID (1 << 4) /* Deep BESL valid */
|
||||
#define USB_SET_BESL_BASELINE(p) (((p) & 0xf) << 8)
|
||||
#define USB_SET_BESL_DEEP(p) (((p) & 0xf) << 12)
|
||||
#define USB_GET_BESL_BASELINE(p) (((p) & (0xf << 8)) >> 8)
|
||||
#define USB_GET_BESL_DEEP(p) (((p) & (0xf << 12)) >> 12)
|
||||
} __attribute__((packed));
|
||||
|
Loading…
Reference in New Issue
Block a user