usb: cdns3: remove upstream cadence USB3 driver
Upstream version is an initial version, it can't be used directly. We will use downstream version for v5.4 instead. Signed-off-by: Peter Chen <peter.chen@nxp.com>5.4-rM2-2.2.x-imx-squashed
parent
219d54332a
commit
576d5c3cde
|
@ -1,45 +0,0 @@
|
|||
Binding for the Cadence USBSS-DRD controller
|
||||
|
||||
Required properties:
|
||||
- reg: Physical base address and size of the controller's register areas.
|
||||
Controller has 3 different regions:
|
||||
- HOST registers area
|
||||
- DEVICE registers area
|
||||
- OTG/DRD registers area
|
||||
- reg-names - register memory area names:
|
||||
"xhci" - for HOST registers space
|
||||
"dev" - for DEVICE registers space
|
||||
"otg" - for OTG/DRD registers space
|
||||
- compatible: Should contain: "cdns,usb3"
|
||||
- interrupts: Interrupts used by cdns3 controller:
|
||||
"host" - interrupt used by XHCI driver.
|
||||
"peripheral" - interrupt used by device driver
|
||||
"otg" - interrupt used by DRD/OTG part of driver
|
||||
|
||||
Optional properties:
|
||||
- maximum-speed : valid arguments are "super-speed", "high-speed" and
|
||||
"full-speed"; refer to usb/generic.txt
|
||||
- dr_mode: Should be one of "host", "peripheral" or "otg".
|
||||
- phys: reference to the USB PHY
|
||||
- phy-names: from the *Generic PHY* bindings;
|
||||
Supported names are:
|
||||
- cdns3,usb2-phy
|
||||
- cdns3,usb3-phy
|
||||
|
||||
- cdns,on-chip-buff-size : size of memory intended as internal memory for endpoints
|
||||
buffers expressed in KB
|
||||
|
||||
Example:
|
||||
usb@f3000000 {
|
||||
compatible = "cdns,usb3";
|
||||
interrupts = <GIC_USB_IRQ 7 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_USB_IRQ 7 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_USB_IRQ 8 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "host", "peripheral", "otg";
|
||||
reg = <0xf3000000 0x10000>, /* memory area for HOST registers */
|
||||
<0xf3010000 0x10000>, /* memory area for DEVICE registers */
|
||||
<0xf3020000 0x10000>; /* memory area for OTG/DRD registers */
|
||||
reg-names = "xhci", "dev", "otg";
|
||||
phys = <&usb2_phy>, <&usb3_phy>;
|
||||
phy-names = "cdns3,usb2-phy", "cnds3,usb3-phy";
|
||||
};
|
|
@ -13,8 +13,6 @@ 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/
|
||||
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
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
|
|
@ -1,16 +0,0 @@
|
|||
# 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
|
|
@ -1,204 +0,0 @@
|
|||
// 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);
|
||||
err = PTR_ERR(wrap->plat_dev);
|
||||
kfree(wrap);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
|
@ -1,671 +0,0 @@
|
|||
// 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;
|
||||
|
||||
/* Initialize idle role to start with */
|
||||
ret = cdns3_role_start(cdns, USB_ROLE_NONE);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
switch (cdns->dr_mode) {
|
||||
case USB_DR_MODE_OTG:
|
||||
ret = cdns3_hw_role_switch(cdns);
|
||||
if (ret)
|
||||
goto err;
|
||||
break;
|
||||
case USB_DR_MODE_PERIPHERAL:
|
||||
ret = cdns3_role_start(cdns, USB_ROLE_DEVICE);
|
||||
if (ret)
|
||||
goto err;
|
||||
break;
|
||||
case USB_DR_MODE_HOST:
|
||||
ret = cdns3_role_start(cdns, USB_ROLE_HOST);
|
||||
if (ret)
|
||||
goto err;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
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))
|
||||
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");
|
|
@ -1,98 +0,0 @@
|
|||
/* 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 */
|
|
@ -1,161 +0,0 @@
|
|||
/* 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*/
|
|
@ -1,381 +0,0 @@
|
|||
// 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;
|
||||
}
|
|
@ -1,167 +0,0 @@
|
|||
/* 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 */
|
|
@ -1,892 +0,0 @@
|
|||
// 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)
|
||||
{
|
||||
struct cdns3_endpoint *priv_ep;
|
||||
__le16 *response_pkt;
|
||||
u16 usb_status = 0;
|
||||
u32 recip;
|
||||
u8 index;
|
||||
|
||||
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:
|
||||
index = cdns3_ep_addr_to_index(ctrl->wIndex);
|
||||
priv_ep = priv_dev->eps[index];
|
||||
|
||||
/* check if endpoint is stalled or stall is pending */
|
||||
cdns3_select_ep(priv_dev, ctrl->wIndex);
|
||||
if (EP_STS_STALL(readl(&priv_dev->regs->ep_sts)) ||
|
||||
(priv_ep->flags & EP_STALL_PENDING))
|
||||
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;
|
||||
u16 tmode;
|
||||
|
||||
wValue = le16_to_cpu(ctrl->wValue);
|
||||
state = priv_dev->gadget.state;
|
||||
speed = priv_dev->gadget.speed;
|
||||
|
||||
switch (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.
|
||||
*/
|
||||
mdelay(1);
|
||||
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);
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
/* 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 */
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,27 +0,0 @@
|
|||
/* 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);
|
||||
|
||||
#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 */
|
|
@ -1,75 +0,0 @@
|
|||
// 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"
|
||||
#include "host-export.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;
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
// 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"
|
|
@ -1,493 +0,0 @@
|
|||
/* 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>
|
Loading…
Reference in New Issue