mirror of https://gitee.com/openkylin/linux.git
In v5.12-rc1, it adds cdnsp drivers for Cadence USB3 Family,
it is different UDC driver with current cdns3 driver. -----BEGIN PGP SIGNATURE----- iQEzBAABCgAdFiEEDaZUZmFxRG/wNThrSFkpgVDWcbsFAmAhOX8ACgkQSFkpgVDW cbsXRggAnpl6YqWLVhHWHe2LqVuLjcDiY1o0B98iezaWNpcBm7diTx/KHWChXuTm UQBMH4QVvb+k++kW0+s0UeLbdyGMBS84NByWRJZWktUVsPk17Gb890YqJzUod9m6 9uC/9kbdNkaP4OTfnUsgEzdAzIbhPsnGWNPMH7M2GRPCtOf+WH9QJriEhQCMTpi0 6N70vHAcQ1tMN+DxVm+ELCVWonkfh1vZdTyAzBcC5oh9A3JeErtBs5fOsIr9oBJi EForGrMIi6lXb4iHj1Jy8uZLDqnEf3094XrafuVXrZRo9y8KQlGn0CPvUy3xZhD0 Ic+VZZKwXOLgRcwHOzyyU4Ongw8pXQ== =0qJU -----END PGP SIGNATURE----- Merge tag 'usb-v5.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb into usb-next Peter writes: In v5.12-rc1, it adds cdnsp drivers for Cadence USB3 Family, it is different UDC driver with current cdns3 driver. * tag 'usb-v5.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb: (23 commits) usb: cdnsp: Removes some useless trace events usb: cdns3: Add support for TI's AM64 SoC usb: cdnsp: fixes undefined reference to cdns_remove usb: cdns3: Adds missing __iomem markers usb: cdnsp: Fix for undefined reference to `usb_hcd_is_primary_hcd' usb: cdnsp: Fixes for sparse warnings usb: cdns3: Fixes for sparse warnings usb: cdnsp: fix error handling in cdnsp_mem_init() usb: cdns3: Removes xhci_cdns3_suspend_quirk from host-export.h usb: cdnsp: Removes some not useful function arguments usb: cdns3: fix warning when USB_CDNS_HOST is not set usb: cdns3: fix build when PM_SLEEP is not set usb: cdnsp: Mark cdnsp_gadget_ops with static keyword MAINTAINERS: add Cadence USBSSP DRD IP driver entry usb: cdns3: Change file names for cdns3 driver. usb: cdnsp: Add tracepoints for CDNSP driver usb: cdnsp: cdns3 Add main part of Cadence USBSSP DRD Driver usb: cdnsp: Device side header file for CDNSP driver usb: cdns3: Changed type of gadget_dev in cdns structure usb: cdns3: Refactoring names in reusable code ...
This commit is contained in:
commit
68d10458a6
|
@ -3889,6 +3889,15 @@ S: Maintained
|
|||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb.git
|
||||
F: Documentation/devicetree/bindings/usb/cdns,usb3.yaml
|
||||
F: drivers/usb/cdns3/
|
||||
X: drivers/usb/cdns3/cdnsp*
|
||||
|
||||
CADENCE USBSSP DRD IP DRIVER
|
||||
M: Pawel Laszczak <pawell@cadence.com>
|
||||
L: linux-usb@vger.kernel.org
|
||||
S: Maintained
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb.git
|
||||
F: drivers/usb/cdns3/
|
||||
X: drivers/usb/cdns3/cdns3*
|
||||
|
||||
CADET FM/AM RADIO RECEIVER DRIVER
|
||||
M: Hans Verkuil <hverkuil@xs4all.nl>
|
||||
|
|
|
@ -13,7 +13,9 @@ obj-$(CONFIG_USB_DWC3) += dwc3/
|
|||
obj-$(CONFIG_USB_DWC2) += dwc2/
|
||||
obj-$(CONFIG_USB_ISP1760) += isp1760/
|
||||
|
||||
obj-$(CONFIG_USB_CDNS_SUPPORT) += cdns3/
|
||||
obj-$(CONFIG_USB_CDNS3) += cdns3/
|
||||
obj-$(CONFIG_USB_CDNSP_PCI) += cdns3/
|
||||
|
||||
obj-$(CONFIG_USB_MON) += mon/
|
||||
obj-$(CONFIG_USB_MTU3) += mtu3/
|
||||
|
|
|
@ -1,14 +1,28 @@
|
|||
config USB_CDNS3
|
||||
tristate "Cadence USB3 Dual-Role Controller"
|
||||
config USB_CDNS_SUPPORT
|
||||
tristate "Cadence USB Support"
|
||||
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 USBSS or USBSSP
|
||||
dual-role controller.
|
||||
It supports: dual-role switch, Host-only, and Peripheral-only.
|
||||
|
||||
config USB_CDNS_HOST
|
||||
bool
|
||||
|
||||
if USB_CDNS_SUPPORT
|
||||
|
||||
config USB_CDNS3
|
||||
tristate "Cadence USB3 Dual-Role Controller"
|
||||
depends on USB_CDNS_SUPPORT
|
||||
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.
|
||||
endif
|
||||
|
||||
if USB_CDNS3
|
||||
|
||||
|
@ -25,6 +39,7 @@ config USB_CDNS3_GADGET
|
|||
config USB_CDNS3_HOST
|
||||
bool "Cadence USB3 host controller"
|
||||
depends on USB=y || USB=USB_CDNS3
|
||||
select USB_CDNS_HOST
|
||||
help
|
||||
Say Y here to enable host controller functionality of the
|
||||
Cadence driver.
|
||||
|
@ -64,3 +79,44 @@ config USB_CDNS3_IMX
|
|||
For example, imx8qm and imx8qxp.
|
||||
|
||||
endif
|
||||
|
||||
if USB_CDNS_SUPPORT
|
||||
|
||||
config USB_CDNSP_PCI
|
||||
tristate "Cadence CDNSP Dual-Role Controller"
|
||||
depends on USB_CDNS_SUPPORT && USB_PCI && ACPI
|
||||
help
|
||||
Say Y here if your system has a Cadence CDNSP dual-role controller.
|
||||
It supports: dual-role switch Host-only, and Peripheral-only.
|
||||
|
||||
If you choose to build this driver is a dynamically linked
|
||||
module, the module will be called cdnsp.ko.
|
||||
endif
|
||||
|
||||
if USB_CDNSP_PCI
|
||||
|
||||
config USB_CDNSP_GADGET
|
||||
bool "Cadence CDNSP device controller"
|
||||
depends on USB_GADGET=y || USB_GADGET=USB_CDNSP_PCI
|
||||
help
|
||||
Say Y here to enable device controller functionality of the
|
||||
Cadence CDNSP-DEV driver.
|
||||
|
||||
Cadence CDNSP Device Controller in device mode is
|
||||
very similar to XHCI controller. Therefore some algorithms
|
||||
used has been taken from host driver.
|
||||
This controller supports FF, HS, SS and SSP mode.
|
||||
It doesn't support LS.
|
||||
|
||||
config USB_CDNSP_HOST
|
||||
bool "Cadence CDNSP host controller"
|
||||
depends on USB=y || USB=USB_CDNSP_PCI
|
||||
select USB_CDNS_HOST
|
||||
help
|
||||
Say Y here to enable host controller functionality of the
|
||||
Cadence driver.
|
||||
|
||||
Host controller is compliant with XHCI so it uses
|
||||
standard XHCI driver.
|
||||
|
||||
endif
|
||||
|
|
|
@ -1,18 +1,43 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
# define_trace.h needs to know how to find our header
|
||||
CFLAGS_trace.o := -I$(src)
|
||||
CFLAGS_cdns3-trace.o := -I$(src)
|
||||
CFLAGS_cdnsp-trace.o := -I$(src)
|
||||
|
||||
cdns3-y := core.o drd.o
|
||||
cdns-usb-common-y := core.o drd.o
|
||||
cdns3-y := cdns3-plat.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
|
||||
ifeq ($(CONFIG_USB),m)
|
||||
obj-m += cdns-usb-common.o
|
||||
obj-m += cdns3.o
|
||||
else
|
||||
obj-$(CONFIG_USB_CDNS_SUPPORT) += cdns-usb-common.o
|
||||
obj-$(CONFIG_USB_CDNS3) += cdns3.o
|
||||
endif
|
||||
|
||||
cdns3-$(CONFIG_USB_CDNS3_HOST) += host.o
|
||||
cdns-usb-common-$(CONFIG_USB_CDNS_HOST) += host.o
|
||||
cdns3-$(CONFIG_USB_CDNS3_GADGET) += cdns3-gadget.o cdns3-ep0.o
|
||||
|
||||
obj-$(CONFIG_USB_CDNS3_PCI_WRAP) += cdns3-pci-wrap.o
|
||||
obj-$(CONFIG_USB_CDNS3_TI) += cdns3-ti.o
|
||||
obj-$(CONFIG_USB_CDNS3_IMX) += cdns3-imx.o
|
||||
ifneq ($(CONFIG_USB_CDNS3_GADGET),)
|
||||
cdns3-$(CONFIG_TRACING) += cdns3-trace.o
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_USB_CDNS3_PCI_WRAP) += cdns3-pci-wrap.o
|
||||
obj-$(CONFIG_USB_CDNS3_TI) += cdns3-ti.o
|
||||
obj-$(CONFIG_USB_CDNS3_IMX) += cdns3-imx.o
|
||||
|
||||
cdnsp-udc-pci-y := cdnsp-pci.o
|
||||
|
||||
ifdef CONFIG_USB_CDNSP_PCI
|
||||
ifeq ($(CONFIG_USB),m)
|
||||
obj-m += cdnsp-udc-pci.o
|
||||
else
|
||||
obj-$(CONFIG_USB_CDNSP_PCI) += cdnsp-udc-pci.o
|
||||
endif
|
||||
endif
|
||||
|
||||
cdnsp-udc-pci-$(CONFIG_USB_CDNSP_GADGET) += cdnsp-ring.o cdnsp-gadget.o \
|
||||
cdnsp-mem.o cdnsp-ep0.o
|
||||
|
||||
ifneq ($(CONFIG_USB_CDNSP_GADGET),)
|
||||
cdnsp-udc-pci-$(CONFIG_TRACING) += cdnsp-trace.o
|
||||
endif
|
||||
|
|
|
@ -13,8 +13,8 @@
|
|||
#include <linux/usb/composite.h>
|
||||
#include <linux/iopoll.h>
|
||||
|
||||
#include "gadget.h"
|
||||
#include "trace.h"
|
||||
#include "cdns3-gadget.h"
|
||||
#include "cdns3-trace.h"
|
||||
|
||||
static struct usb_endpoint_descriptor cdns3_gadget_ep0_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
|
@ -364,7 +364,7 @@ static int cdns3_ep0_feature_handle_endpoint(struct cdns3_device *priv_dev,
|
|||
if (le16_to_cpu(ctrl->wValue) != USB_ENDPOINT_HALT)
|
||||
return -EINVAL;
|
||||
|
||||
if (!(ctrl->wIndex & ~USB_DIR_IN))
|
||||
if (!(le16_to_cpu(ctrl->wIndex) & ~USB_DIR_IN))
|
||||
return 0;
|
||||
|
||||
index = cdns3_ep_addr_to_index(le16_to_cpu(ctrl->wIndex));
|
||||
|
@ -789,7 +789,7 @@ int cdns3_gadget_ep_set_wedge(struct usb_ep *ep)
|
|||
return 0;
|
||||
}
|
||||
|
||||
const struct usb_ep_ops cdns3_gadget_ep0_ops = {
|
||||
static 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,
|
|
@ -63,8 +63,8 @@
|
|||
|
||||
#include "core.h"
|
||||
#include "gadget-export.h"
|
||||
#include "gadget.h"
|
||||
#include "trace.h"
|
||||
#include "cdns3-gadget.h"
|
||||
#include "cdns3-trace.h"
|
||||
#include "drd.h"
|
||||
|
||||
static int __cdns3_gadget_ep_queue(struct usb_ep *ep,
|
||||
|
@ -1200,7 +1200,7 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
|
|||
td_size = DIV_ROUND_UP(request->length,
|
||||
priv_ep->endpoint.maxpacket);
|
||||
if (priv_dev->gadget.speed == USB_SPEED_SUPER)
|
||||
trb->length = TRB_TDL_SS_SIZE(td_size);
|
||||
trb->length = cpu_to_le32(TRB_TDL_SS_SIZE(td_size));
|
||||
else
|
||||
control |= TRB_TDL_HS_SIZE(td_size);
|
||||
}
|
||||
|
@ -1247,10 +1247,10 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
|
|||
priv_req->trb->control = cpu_to_le32(control);
|
||||
|
||||
if (sg_supported) {
|
||||
trb->control |= TRB_ISP;
|
||||
trb->control |= cpu_to_le32(TRB_ISP);
|
||||
/* Don't set chain bit for last TRB */
|
||||
if (sg_iter < num_trb - 1)
|
||||
trb->control |= TRB_CHAIN;
|
||||
trb->control |= cpu_to_le32(TRB_CHAIN);
|
||||
|
||||
s = sg_next(s);
|
||||
}
|
||||
|
@ -1844,7 +1844,7 @@ __must_hold(&priv_dev->lock)
|
|||
static irqreturn_t cdns3_device_irq_handler(int irq, void *data)
|
||||
{
|
||||
struct cdns3_device *priv_dev = data;
|
||||
struct cdns3 *cdns = dev_get_drvdata(priv_dev->dev);
|
||||
struct cdns *cdns = dev_get_drvdata(priv_dev->dev);
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
u32 reg;
|
||||
|
||||
|
@ -3084,7 +3084,7 @@ static void cdns3_gadget_release(struct device *dev)
|
|||
kfree(priv_dev);
|
||||
}
|
||||
|
||||
static void cdns3_gadget_exit(struct cdns3 *cdns)
|
||||
static void cdns3_gadget_exit(struct cdns *cdns)
|
||||
{
|
||||
struct cdns3_device *priv_dev;
|
||||
|
||||
|
@ -3117,10 +3117,10 @@ static void cdns3_gadget_exit(struct cdns3 *cdns)
|
|||
kfree(priv_dev->zlp_buf);
|
||||
usb_put_gadget(&priv_dev->gadget);
|
||||
cdns->gadget_dev = NULL;
|
||||
cdns3_drd_gadget_off(cdns);
|
||||
cdns_drd_gadget_off(cdns);
|
||||
}
|
||||
|
||||
static int cdns3_gadget_start(struct cdns3 *cdns)
|
||||
static int cdns3_gadget_start(struct cdns *cdns)
|
||||
{
|
||||
struct cdns3_device *priv_dev;
|
||||
u32 max_speed;
|
||||
|
@ -3240,7 +3240,7 @@ static int cdns3_gadget_start(struct cdns3 *cdns)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int __cdns3_gadget_init(struct cdns3 *cdns)
|
||||
static int __cdns3_gadget_init(struct cdns *cdns)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
|
@ -3251,7 +3251,7 @@ static int __cdns3_gadget_init(struct cdns3 *cdns)
|
|||
return ret;
|
||||
}
|
||||
|
||||
cdns3_drd_gadget_on(cdns);
|
||||
cdns_drd_gadget_on(cdns);
|
||||
pm_runtime_get_sync(cdns->dev);
|
||||
|
||||
ret = cdns3_gadget_start(cdns);
|
||||
|
@ -3277,7 +3277,7 @@ static int __cdns3_gadget_init(struct cdns3 *cdns)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int cdns3_gadget_suspend(struct cdns3 *cdns, bool do_wakeup)
|
||||
static int cdns3_gadget_suspend(struct cdns *cdns, bool do_wakeup)
|
||||
__must_hold(&cdns->lock)
|
||||
{
|
||||
struct cdns3_device *priv_dev = cdns->gadget_dev;
|
||||
|
@ -3296,7 +3296,7 @@ __must_hold(&cdns->lock)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int cdns3_gadget_resume(struct cdns3 *cdns, bool hibernated)
|
||||
static int cdns3_gadget_resume(struct cdns *cdns, bool hibernated)
|
||||
{
|
||||
struct cdns3_device *priv_dev = cdns->gadget_dev;
|
||||
|
||||
|
@ -3311,13 +3311,13 @@ static int cdns3_gadget_resume(struct cdns3 *cdns, bool hibernated)
|
|||
/**
|
||||
* cdns3_gadget_init - initialize device structure
|
||||
*
|
||||
* @cdns: cdns3 instance
|
||||
* @cdns: cdns instance
|
||||
*
|
||||
* This function initializes the gadget.
|
||||
*/
|
||||
int cdns3_gadget_init(struct cdns3 *cdns)
|
||||
int cdns3_gadget_init(struct cdns *cdns)
|
||||
{
|
||||
struct cdns3_role_driver *rdrv;
|
||||
struct cdns_role_driver *rdrv;
|
||||
|
||||
rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL);
|
||||
if (!rdrv)
|
||||
|
@ -3327,7 +3327,7 @@ int cdns3_gadget_init(struct cdns3 *cdns)
|
|||
rdrv->stop = cdns3_gadget_exit;
|
||||
rdrv->suspend = cdns3_gadget_suspend;
|
||||
rdrv->resume = cdns3_gadget_resume;
|
||||
rdrv->state = CDNS3_ROLE_STATE_INACTIVE;
|
||||
rdrv->state = CDNS_ROLE_STATE_INACTIVE;
|
||||
rdrv->name = "gadget";
|
||||
cdns->roles[USB_ROLE_DEVICE] = rdrv;
|
||||
|
|
@ -250,7 +250,7 @@ static void cdns3_set_wakeup(struct cdns_imx *data, bool enable)
|
|||
static int cdns_imx_platform_suspend(struct device *dev,
|
||||
bool suspend, bool wakeup)
|
||||
{
|
||||
struct cdns3 *cdns = dev_get_drvdata(dev);
|
||||
struct cdns *cdns = dev_get_drvdata(dev);
|
||||
struct device *parent = dev->parent;
|
||||
struct cdns_imx *data = dev_get_drvdata(parent);
|
||||
void __iomem *otg_regs = (void __iomem *)(cdns->otg_regs);
|
||||
|
|
|
@ -0,0 +1,315 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Cadence USBSS DRD Driver.
|
||||
*
|
||||
* Copyright (C) 2018-2020 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/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "gadget-export.h"
|
||||
|
||||
static int set_phy_power_on(struct cdns *cdns)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = phy_power_on(cdns->usb2_phy);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = phy_power_on(cdns->usb3_phy);
|
||||
if (ret)
|
||||
phy_power_off(cdns->usb2_phy);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void set_phy_power_off(struct cdns *cdns)
|
||||
{
|
||||
phy_power_off(cdns->usb3_phy);
|
||||
phy_power_off(cdns->usb2_phy);
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_plat_probe - probe for cdns3 core device
|
||||
* @pdev: Pointer to cdns3 core platform device
|
||||
*
|
||||
* Returns 0 on success otherwise negative errno
|
||||
*/
|
||||
static int cdns3_plat_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
struct cdns *cdns;
|
||||
void __iomem *regs;
|
||||
int ret;
|
||||
|
||||
cdns = devm_kzalloc(dev, sizeof(*cdns), GFP_KERNEL);
|
||||
if (!cdns)
|
||||
return -ENOMEM;
|
||||
|
||||
cdns->dev = dev;
|
||||
cdns->pdata = dev_get_platdata(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 < 0)
|
||||
return cdns->dev_irq;
|
||||
|
||||
regs = devm_platform_ioremap_resource_byname(pdev, "dev");
|
||||
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 < 0)
|
||||
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->phyrst_a_enable = device_property_read_bool(dev, "cdns,phyrst-a-enable");
|
||||
|
||||
cdns->otg_res = *res;
|
||||
|
||||
cdns->wakeup_irq = platform_get_irq_byname_optional(pdev, "wakeup");
|
||||
if (cdns->wakeup_irq == -EPROBE_DEFER)
|
||||
return cdns->wakeup_irq;
|
||||
else if (cdns->wakeup_irq == 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (cdns->wakeup_irq < 0) {
|
||||
dev_dbg(dev, "couldn't get wakeup irq\n");
|
||||
cdns->wakeup_irq = 0x0;
|
||||
}
|
||||
|
||||
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 err_phy3_init;
|
||||
|
||||
ret = set_phy_power_on(cdns);
|
||||
if (ret)
|
||||
goto err_phy_power_on;
|
||||
|
||||
cdns->gadget_init = cdns3_gadget_init;
|
||||
|
||||
ret = cdns_init(cdns);
|
||||
if (ret)
|
||||
goto err_cdns_init;
|
||||
|
||||
device_set_wakeup_capable(dev, true);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
if (!(cdns->pdata && (cdns->pdata->quirks & CDNS3_DEFAULT_PM_RUNTIME_ALLOW)))
|
||||
pm_runtime_forbid(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);
|
||||
|
||||
return 0;
|
||||
|
||||
err_cdns_init:
|
||||
set_phy_power_off(cdns);
|
||||
err_phy_power_on:
|
||||
phy_exit(cdns->usb3_phy);
|
||||
err_phy3_init:
|
||||
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_plat_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct cdns *cdns = platform_get_drvdata(pdev);
|
||||
struct device *dev = cdns->dev;
|
||||
|
||||
pm_runtime_get_sync(dev);
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_put_noidle(dev);
|
||||
cdns_remove(cdns);
|
||||
set_phy_power_off(cdns);
|
||||
phy_exit(cdns->usb2_phy);
|
||||
phy_exit(cdns->usb3_phy);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static int cdns3_set_platform_suspend(struct device *dev,
|
||||
bool suspend, bool wakeup)
|
||||
{
|
||||
struct cdns *cdns = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (cdns->pdata && cdns->pdata->platform_suspend)
|
||||
ret = cdns->pdata->platform_suspend(dev, suspend, wakeup);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cdns3_controller_suspend(struct device *dev, pm_message_t msg)
|
||||
{
|
||||
struct cdns *cdns = dev_get_drvdata(dev);
|
||||
bool wakeup;
|
||||
unsigned long flags;
|
||||
|
||||
if (cdns->in_lpm)
|
||||
return 0;
|
||||
|
||||
if (PMSG_IS_AUTO(msg))
|
||||
wakeup = true;
|
||||
else
|
||||
wakeup = device_may_wakeup(dev);
|
||||
|
||||
cdns3_set_platform_suspend(cdns->dev, true, wakeup);
|
||||
set_phy_power_off(cdns);
|
||||
spin_lock_irqsave(&cdns->lock, flags);
|
||||
cdns->in_lpm = true;
|
||||
spin_unlock_irqrestore(&cdns->lock, flags);
|
||||
dev_dbg(cdns->dev, "%s ends\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdns3_controller_resume(struct device *dev, pm_message_t msg)
|
||||
{
|
||||
struct cdns *cdns = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
|
||||
if (!cdns->in_lpm)
|
||||
return 0;
|
||||
|
||||
ret = set_phy_power_on(cdns);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
cdns3_set_platform_suspend(cdns->dev, false, false);
|
||||
|
||||
spin_lock_irqsave(&cdns->lock, flags);
|
||||
cdns_resume(cdns, !PMSG_IS_AUTO(msg));
|
||||
cdns->in_lpm = false;
|
||||
spin_unlock_irqrestore(&cdns->lock, flags);
|
||||
if (cdns->wakeup_pending) {
|
||||
cdns->wakeup_pending = false;
|
||||
enable_irq(cdns->wakeup_irq);
|
||||
}
|
||||
dev_dbg(cdns->dev, "%s ends\n", __func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cdns3_plat_runtime_suspend(struct device *dev)
|
||||
{
|
||||
return cdns3_controller_suspend(dev, PMSG_AUTO_SUSPEND);
|
||||
}
|
||||
|
||||
static int cdns3_plat_runtime_resume(struct device *dev)
|
||||
{
|
||||
return cdns3_controller_resume(dev, PMSG_AUTO_RESUME);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
||||
static int cdns3_plat_suspend(struct device *dev)
|
||||
{
|
||||
struct cdns *cdns = dev_get_drvdata(dev);
|
||||
|
||||
cdns_suspend(cdns);
|
||||
|
||||
return cdns3_controller_suspend(dev, PMSG_SUSPEND);
|
||||
}
|
||||
|
||||
static int cdns3_plat_resume(struct device *dev)
|
||||
{
|
||||
return cdns3_controller_resume(dev, PMSG_RESUME);
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static const struct dev_pm_ops cdns3_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(cdns3_plat_suspend, cdns3_plat_resume)
|
||||
SET_RUNTIME_PM_OPS(cdns3_plat_runtime_suspend,
|
||||
cdns3_plat_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
#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_plat_probe,
|
||||
.remove = cdns3_plat_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");
|
|
@ -214,6 +214,7 @@ static int cdns_ti_remove(struct platform_device *pdev)
|
|||
|
||||
static const struct of_device_id cdns_ti_of_match[] = {
|
||||
{ .compatible = "ti,j721e-usb", },
|
||||
{ .compatible = "ti,am64-usb", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, cdns_ti_of_match);
|
||||
|
|
|
@ -8,4 +8,4 @@
|
|||
*/
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include "trace.h"
|
||||
#include "cdns3-trace.h"
|
|
@ -19,8 +19,8 @@
|
|||
#include <asm/byteorder.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include "core.h"
|
||||
#include "gadget.h"
|
||||
#include "debug.h"
|
||||
#include "cdns3-gadget.h"
|
||||
#include "cdns3-debug.h"
|
||||
|
||||
#define CDNS3_MSG_MAX 500
|
||||
|
||||
|
@ -565,6 +565,6 @@ DEFINE_EVENT(cdns3_log_request_handled, cdns3_request_handled,
|
|||
#define TRACE_INCLUDE_PATH .
|
||||
|
||||
#undef TRACE_INCLUDE_FILE
|
||||
#define TRACE_INCLUDE_FILE trace
|
||||
#define TRACE_INCLUDE_FILE cdns3-trace
|
||||
|
||||
#include <trace/define_trace.h>
|
|
@ -0,0 +1,583 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Cadence CDNSP DRD Driver.
|
||||
*
|
||||
* Copyright (C) 2020 Cadence.
|
||||
*
|
||||
* Author: Pawel Laszczak <pawell@cadence.com>
|
||||
*
|
||||
*/
|
||||
#ifndef __LINUX_CDNSP_DEBUG
|
||||
#define __LINUX_CDNSP_DEBUG
|
||||
|
||||
static inline const char *cdnsp_trb_comp_code_string(u8 status)
|
||||
{
|
||||
switch (status) {
|
||||
case COMP_INVALID:
|
||||
return "Invalid";
|
||||
case COMP_SUCCESS:
|
||||
return "Success";
|
||||
case COMP_DATA_BUFFER_ERROR:
|
||||
return "Data Buffer Error";
|
||||
case COMP_BABBLE_DETECTED_ERROR:
|
||||
return "Babble Detected";
|
||||
case COMP_TRB_ERROR:
|
||||
return "TRB Error";
|
||||
case COMP_RESOURCE_ERROR:
|
||||
return "Resource Error";
|
||||
case COMP_NO_SLOTS_AVAILABLE_ERROR:
|
||||
return "No Slots Available Error";
|
||||
case COMP_INVALID_STREAM_TYPE_ERROR:
|
||||
return "Invalid Stream Type Error";
|
||||
case COMP_SLOT_NOT_ENABLED_ERROR:
|
||||
return "Slot Not Enabled Error";
|
||||
case COMP_ENDPOINT_NOT_ENABLED_ERROR:
|
||||
return "Endpoint Not Enabled Error";
|
||||
case COMP_SHORT_PACKET:
|
||||
return "Short Packet";
|
||||
case COMP_RING_UNDERRUN:
|
||||
return "Ring Underrun";
|
||||
case COMP_RING_OVERRUN:
|
||||
return "Ring Overrun";
|
||||
case COMP_VF_EVENT_RING_FULL_ERROR:
|
||||
return "VF Event Ring Full Error";
|
||||
case COMP_PARAMETER_ERROR:
|
||||
return "Parameter Error";
|
||||
case COMP_CONTEXT_STATE_ERROR:
|
||||
return "Context State Error";
|
||||
case COMP_EVENT_RING_FULL_ERROR:
|
||||
return "Event Ring Full Error";
|
||||
case COMP_INCOMPATIBLE_DEVICE_ERROR:
|
||||
return "Incompatible Device Error";
|
||||
case COMP_MISSED_SERVICE_ERROR:
|
||||
return "Missed Service Error";
|
||||
case COMP_COMMAND_RING_STOPPED:
|
||||
return "Command Ring Stopped";
|
||||
case COMP_COMMAND_ABORTED:
|
||||
return "Command Aborted";
|
||||
case COMP_STOPPED:
|
||||
return "Stopped";
|
||||
case COMP_STOPPED_LENGTH_INVALID:
|
||||
return "Stopped - Length Invalid";
|
||||
case COMP_STOPPED_SHORT_PACKET:
|
||||
return "Stopped - Short Packet";
|
||||
case COMP_MAX_EXIT_LATENCY_TOO_LARGE_ERROR:
|
||||
return "Max Exit Latency Too Large Error";
|
||||
case COMP_ISOCH_BUFFER_OVERRUN:
|
||||
return "Isoch Buffer Overrun";
|
||||
case COMP_EVENT_LOST_ERROR:
|
||||
return "Event Lost Error";
|
||||
case COMP_UNDEFINED_ERROR:
|
||||
return "Undefined Error";
|
||||
case COMP_INVALID_STREAM_ID_ERROR:
|
||||
return "Invalid Stream ID Error";
|
||||
default:
|
||||
return "Unknown!!";
|
||||
}
|
||||
}
|
||||
|
||||
static inline const char *cdnsp_trb_type_string(u8 type)
|
||||
{
|
||||
switch (type) {
|
||||
case TRB_NORMAL:
|
||||
return "Normal";
|
||||
case TRB_SETUP:
|
||||
return "Setup Stage";
|
||||
case TRB_DATA:
|
||||
return "Data Stage";
|
||||
case TRB_STATUS:
|
||||
return "Status Stage";
|
||||
case TRB_ISOC:
|
||||
return "Isoch";
|
||||
case TRB_LINK:
|
||||
return "Link";
|
||||
case TRB_EVENT_DATA:
|
||||
return "Event Data";
|
||||
case TRB_TR_NOOP:
|
||||
return "No-Op";
|
||||
case TRB_ENABLE_SLOT:
|
||||
return "Enable Slot Command";
|
||||
case TRB_DISABLE_SLOT:
|
||||
return "Disable Slot Command";
|
||||
case TRB_ADDR_DEV:
|
||||
return "Address Device Command";
|
||||
case TRB_CONFIG_EP:
|
||||
return "Configure Endpoint Command";
|
||||
case TRB_EVAL_CONTEXT:
|
||||
return "Evaluate Context Command";
|
||||
case TRB_RESET_EP:
|
||||
return "Reset Endpoint Command";
|
||||
case TRB_STOP_RING:
|
||||
return "Stop Ring Command";
|
||||
case TRB_SET_DEQ:
|
||||
return "Set TR Dequeue Pointer Command";
|
||||
case TRB_RESET_DEV:
|
||||
return "Reset Device Command";
|
||||
case TRB_FORCE_HEADER:
|
||||
return "Force Header Command";
|
||||
case TRB_CMD_NOOP:
|
||||
return "No-Op Command";
|
||||
case TRB_TRANSFER:
|
||||
return "Transfer Event";
|
||||
case TRB_COMPLETION:
|
||||
return "Command Completion Event";
|
||||
case TRB_PORT_STATUS:
|
||||
return "Port Status Change Event";
|
||||
case TRB_HC_EVENT:
|
||||
return "Device Controller Event";
|
||||
case TRB_MFINDEX_WRAP:
|
||||
return "MFINDEX Wrap Event";
|
||||
case TRB_ENDPOINT_NRDY:
|
||||
return "Endpoint Not ready";
|
||||
case TRB_HALT_ENDPOINT:
|
||||
return "Halt Endpoint";
|
||||
case TRB_FLUSH_ENDPOINT:
|
||||
return "FLush Endpoint";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
static inline const char *cdnsp_ring_type_string(enum cdnsp_ring_type type)
|
||||
{
|
||||
switch (type) {
|
||||
case TYPE_CTRL:
|
||||
return "CTRL";
|
||||
case TYPE_ISOC:
|
||||
return "ISOC";
|
||||
case TYPE_BULK:
|
||||
return "BULK";
|
||||
case TYPE_INTR:
|
||||
return "INTR";
|
||||
case TYPE_STREAM:
|
||||
return "STREAM";
|
||||
case TYPE_COMMAND:
|
||||
return "CMD";
|
||||
case TYPE_EVENT:
|
||||
return "EVENT";
|
||||
}
|
||||
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
static inline char *cdnsp_slot_state_string(u32 state)
|
||||
{
|
||||
switch (state) {
|
||||
case SLOT_STATE_ENABLED:
|
||||
return "enabled/disabled";
|
||||
case SLOT_STATE_DEFAULT:
|
||||
return "default";
|
||||
case SLOT_STATE_ADDRESSED:
|
||||
return "addressed";
|
||||
case SLOT_STATE_CONFIGURED:
|
||||
return "configured";
|
||||
default:
|
||||
return "reserved";
|
||||
}
|
||||
}
|
||||
|
||||
static inline const char *cdnsp_decode_trb(char *str, size_t size, u32 field0,
|
||||
u32 field1, u32 field2, u32 field3)
|
||||
{
|
||||
int ep_id = TRB_TO_EP_INDEX(field3) - 1;
|
||||
int type = TRB_FIELD_TO_TYPE(field3);
|
||||
unsigned int ep_num;
|
||||
int ret = 0;
|
||||
u32 temp;
|
||||
|
||||
ep_num = DIV_ROUND_UP(ep_id, 2);
|
||||
|
||||
switch (type) {
|
||||
case TRB_LINK:
|
||||
ret += snprintf(str, size,
|
||||
"LINK %08x%08x intr %ld type '%s' flags %c:%c:%c:%c",
|
||||
field1, field0, GET_INTR_TARGET(field2),
|
||||
cdnsp_trb_type_string(type),
|
||||
field3 & TRB_IOC ? 'I' : 'i',
|
||||
field3 & TRB_CHAIN ? 'C' : 'c',
|
||||
field3 & TRB_TC ? 'T' : 't',
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_TRANSFER:
|
||||
case TRB_COMPLETION:
|
||||
case TRB_PORT_STATUS:
|
||||
case TRB_HC_EVENT:
|
||||
ret += snprintf(str, size,
|
||||
"ep%d%s(%d) type '%s' TRB %08x%08x status '%s'"
|
||||
" len %ld slot %ld flags %c:%c",
|
||||
ep_num, ep_id % 2 ? "out" : "in",
|
||||
TRB_TO_EP_INDEX(field3),
|
||||
cdnsp_trb_type_string(type), field1, field0,
|
||||
cdnsp_trb_comp_code_string(GET_COMP_CODE(field2)),
|
||||
EVENT_TRB_LEN(field2), TRB_TO_SLOT_ID(field3),
|
||||
field3 & EVENT_DATA ? 'E' : 'e',
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_MFINDEX_WRAP:
|
||||
ret += snprintf(str, size, "%s: flags %c",
|
||||
cdnsp_trb_type_string(type),
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_SETUP:
|
||||
ret += snprintf(str, size,
|
||||
"type '%s' bRequestType %02x bRequest %02x "
|
||||
"wValue %02x%02x wIndex %02x%02x wLength %d "
|
||||
"length %ld TD size %ld intr %ld Setup ID %ld "
|
||||
"flags %c:%c:%c",
|
||||
cdnsp_trb_type_string(type),
|
||||
field0 & 0xff,
|
||||
(field0 & 0xff00) >> 8,
|
||||
(field0 & 0xff000000) >> 24,
|
||||
(field0 & 0xff0000) >> 16,
|
||||
(field1 & 0xff00) >> 8,
|
||||
field1 & 0xff,
|
||||
(field1 & 0xff000000) >> 16 |
|
||||
(field1 & 0xff0000) >> 16,
|
||||
TRB_LEN(field2), GET_TD_SIZE(field2),
|
||||
GET_INTR_TARGET(field2),
|
||||
TRB_SETUPID_TO_TYPE(field3),
|
||||
field3 & TRB_IDT ? 'D' : 'd',
|
||||
field3 & TRB_IOC ? 'I' : 'i',
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_DATA:
|
||||
ret += snprintf(str, size,
|
||||
"type '%s' Buffer %08x%08x length %ld TD size %ld "
|
||||
"intr %ld flags %c:%c:%c:%c:%c:%c:%c",
|
||||
cdnsp_trb_type_string(type),
|
||||
field1, field0, TRB_LEN(field2),
|
||||
GET_TD_SIZE(field2),
|
||||
GET_INTR_TARGET(field2),
|
||||
field3 & TRB_IDT ? 'D' : 'i',
|
||||
field3 & TRB_IOC ? 'I' : 'i',
|
||||
field3 & TRB_CHAIN ? 'C' : 'c',
|
||||
field3 & TRB_NO_SNOOP ? 'S' : 's',
|
||||
field3 & TRB_ISP ? 'I' : 'i',
|
||||
field3 & TRB_ENT ? 'E' : 'e',
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_STATUS:
|
||||
ret += snprintf(str, size,
|
||||
"Buffer %08x%08x length %ld TD size %ld intr"
|
||||
"%ld type '%s' flags %c:%c:%c:%c",
|
||||
field1, field0, TRB_LEN(field2),
|
||||
GET_TD_SIZE(field2),
|
||||
GET_INTR_TARGET(field2),
|
||||
cdnsp_trb_type_string(type),
|
||||
field3 & TRB_IOC ? 'I' : 'i',
|
||||
field3 & TRB_CHAIN ? 'C' : 'c',
|
||||
field3 & TRB_ENT ? 'E' : 'e',
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_NORMAL:
|
||||
case TRB_ISOC:
|
||||
case TRB_EVENT_DATA:
|
||||
case TRB_TR_NOOP:
|
||||
ret += snprintf(str, size,
|
||||
"type '%s' Buffer %08x%08x length %ld "
|
||||
"TD size %ld intr %ld "
|
||||
"flags %c:%c:%c:%c:%c:%c:%c:%c:%c",
|
||||
cdnsp_trb_type_string(type),
|
||||
field1, field0, TRB_LEN(field2),
|
||||
GET_TD_SIZE(field2),
|
||||
GET_INTR_TARGET(field2),
|
||||
field3 & TRB_BEI ? 'B' : 'b',
|
||||
field3 & TRB_IDT ? 'T' : 't',
|
||||
field3 & TRB_IOC ? 'I' : 'i',
|
||||
field3 & TRB_CHAIN ? 'C' : 'c',
|
||||
field3 & TRB_NO_SNOOP ? 'S' : 's',
|
||||
field3 & TRB_ISP ? 'I' : 'i',
|
||||
field3 & TRB_ENT ? 'E' : 'e',
|
||||
field3 & TRB_CYCLE ? 'C' : 'c',
|
||||
!(field3 & TRB_EVENT_INVALIDATE) ? 'V' : 'v');
|
||||
break;
|
||||
case TRB_CMD_NOOP:
|
||||
case TRB_ENABLE_SLOT:
|
||||
ret += snprintf(str, size, "%s: flags %c",
|
||||
cdnsp_trb_type_string(type),
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_DISABLE_SLOT:
|
||||
ret += snprintf(str, size, "%s: slot %ld flags %c",
|
||||
cdnsp_trb_type_string(type),
|
||||
TRB_TO_SLOT_ID(field3),
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_ADDR_DEV:
|
||||
ret += snprintf(str, size,
|
||||
"%s: ctx %08x%08x slot %ld flags %c:%c",
|
||||
cdnsp_trb_type_string(type), field1, field0,
|
||||
TRB_TO_SLOT_ID(field3),
|
||||
field3 & TRB_BSR ? 'B' : 'b',
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_CONFIG_EP:
|
||||
ret += snprintf(str, size,
|
||||
"%s: ctx %08x%08x slot %ld flags %c:%c",
|
||||
cdnsp_trb_type_string(type), field1, field0,
|
||||
TRB_TO_SLOT_ID(field3),
|
||||
field3 & TRB_DC ? 'D' : 'd',
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_EVAL_CONTEXT:
|
||||
ret += snprintf(str, size,
|
||||
"%s: ctx %08x%08x slot %ld flags %c",
|
||||
cdnsp_trb_type_string(type), field1, field0,
|
||||
TRB_TO_SLOT_ID(field3),
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_RESET_EP:
|
||||
case TRB_HALT_ENDPOINT:
|
||||
case TRB_FLUSH_ENDPOINT:
|
||||
ret += snprintf(str, size,
|
||||
"%s: ep%d%s(%d) ctx %08x%08x slot %ld flags %c",
|
||||
cdnsp_trb_type_string(type),
|
||||
ep_num, ep_id % 2 ? "out" : "in",
|
||||
TRB_TO_EP_INDEX(field3), field1, field0,
|
||||
TRB_TO_SLOT_ID(field3),
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_STOP_RING:
|
||||
ret += snprintf(str, size,
|
||||
"%s: ep%d%s(%d) slot %ld sp %d flags %c",
|
||||
cdnsp_trb_type_string(type),
|
||||
ep_num, ep_id % 2 ? "out" : "in",
|
||||
TRB_TO_EP_INDEX(field3),
|
||||
TRB_TO_SLOT_ID(field3),
|
||||
TRB_TO_SUSPEND_PORT(field3),
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_SET_DEQ:
|
||||
ret += snprintf(str, size,
|
||||
"%s: ep%d%s(%d) deq %08x%08x stream %ld slot %ld flags %c",
|
||||
cdnsp_trb_type_string(type),
|
||||
ep_num, ep_id % 2 ? "out" : "in",
|
||||
TRB_TO_EP_INDEX(field3), field1, field0,
|
||||
TRB_TO_STREAM_ID(field2),
|
||||
TRB_TO_SLOT_ID(field3),
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_RESET_DEV:
|
||||
ret += snprintf(str, size, "%s: slot %ld flags %c",
|
||||
cdnsp_trb_type_string(type),
|
||||
TRB_TO_SLOT_ID(field3),
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
case TRB_ENDPOINT_NRDY:
|
||||
temp = TRB_TO_HOST_STREAM(field2);
|
||||
|
||||
ret += snprintf(str, size,
|
||||
"%s: ep%d%s(%d) H_SID %x%s%s D_SID %lx flags %c:%c",
|
||||
cdnsp_trb_type_string(type),
|
||||
ep_num, ep_id % 2 ? "out" : "in",
|
||||
TRB_TO_EP_INDEX(field3), temp,
|
||||
temp == STREAM_PRIME_ACK ? "(PRIME)" : "",
|
||||
temp == STREAM_REJECTED ? "(REJECTED)" : "",
|
||||
TRB_TO_DEV_STREAM(field0),
|
||||
field3 & TRB_STAT ? 'S' : 's',
|
||||
field3 & TRB_CYCLE ? 'C' : 'c');
|
||||
break;
|
||||
default:
|
||||
ret += snprintf(str, size,
|
||||
"type '%s' -> raw %08x %08x %08x %08x",
|
||||
cdnsp_trb_type_string(type),
|
||||
field0, field1, field2, field3);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static inline const char *cdnsp_decode_slot_context(u32 info, u32 info2,
|
||||
u32 int_target, u32 state)
|
||||
{
|
||||
static char str[1024];
|
||||
int ret = 0;
|
||||
u32 speed;
|
||||
char *s;
|
||||
|
||||
speed = info & DEV_SPEED;
|
||||
|
||||
switch (speed) {
|
||||
case SLOT_SPEED_FS:
|
||||
s = "full-speed";
|
||||
break;
|
||||
case SLOT_SPEED_HS:
|
||||
s = "high-speed";
|
||||
break;
|
||||
case SLOT_SPEED_SS:
|
||||
s = "super-speed";
|
||||
break;
|
||||
case SLOT_SPEED_SSP:
|
||||
s = "super-speed plus";
|
||||
break;
|
||||
default:
|
||||
s = "UNKNOWN speed";
|
||||
}
|
||||
|
||||
ret = sprintf(str, "%s Ctx Entries %d",
|
||||
s, (info & LAST_CTX_MASK) >> 27);
|
||||
|
||||
ret += sprintf(str + ret, " [Intr %ld] Addr %ld State %s",
|
||||
GET_INTR_TARGET(int_target), state & DEV_ADDR_MASK,
|
||||
cdnsp_slot_state_string(GET_SLOT_STATE(state)));
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static inline const char *cdnsp_portsc_link_state_string(u32 portsc)
|
||||
{
|
||||
switch (portsc & PORT_PLS_MASK) {
|
||||
case XDEV_U0:
|
||||
return "U0";
|
||||
case XDEV_U1:
|
||||
return "U1";
|
||||
case XDEV_U2:
|
||||
return "U2";
|
||||
case XDEV_U3:
|
||||
return "U3";
|
||||
case XDEV_DISABLED:
|
||||
return "Disabled";
|
||||
case XDEV_RXDETECT:
|
||||
return "RxDetect";
|
||||
case XDEV_INACTIVE:
|
||||
return "Inactive";
|
||||
case XDEV_POLLING:
|
||||
return "Polling";
|
||||
case XDEV_RECOVERY:
|
||||
return "Recovery";
|
||||
case XDEV_HOT_RESET:
|
||||
return "Hot Reset";
|
||||
case XDEV_COMP_MODE:
|
||||
return "Compliance mode";
|
||||
case XDEV_TEST_MODE:
|
||||
return "Test mode";
|
||||
case XDEV_RESUME:
|
||||
return "Resume";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
static inline const char *cdnsp_decode_portsc(char *str, size_t size,
|
||||
u32 portsc)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = snprintf(str, size, "%s %s %s Link:%s PortSpeed:%d ",
|
||||
portsc & PORT_POWER ? "Powered" : "Powered-off",
|
||||
portsc & PORT_CONNECT ? "Connected" : "Not-connected",
|
||||
portsc & PORT_PED ? "Enabled" : "Disabled",
|
||||
cdnsp_portsc_link_state_string(portsc),
|
||||
DEV_PORT_SPEED(portsc));
|
||||
|
||||
if (portsc & PORT_RESET)
|
||||
ret += snprintf(str + ret, size - ret, "In-Reset ");
|
||||
|
||||
ret += snprintf(str + ret, size - ret, "Change: ");
|
||||
if (portsc & PORT_CSC)
|
||||
ret += snprintf(str + ret, size - ret, "CSC ");
|
||||
if (portsc & PORT_WRC)
|
||||
ret += snprintf(str + ret, size - ret, "WRC ");
|
||||
if (portsc & PORT_RC)
|
||||
ret += snprintf(str + ret, size - ret, "PRC ");
|
||||
if (portsc & PORT_PLC)
|
||||
ret += snprintf(str + ret, size - ret, "PLC ");
|
||||
if (portsc & PORT_CEC)
|
||||
ret += snprintf(str + ret, size - ret, "CEC ");
|
||||
ret += snprintf(str + ret, size - ret, "Wake: ");
|
||||
if (portsc & PORT_WKCONN_E)
|
||||
ret += snprintf(str + ret, size - ret, "WCE ");
|
||||
if (portsc & PORT_WKDISC_E)
|
||||
ret += snprintf(str + ret, size - ret, "WDE ");
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static inline const char *cdnsp_ep_state_string(u8 state)
|
||||
{
|
||||
switch (state) {
|
||||
case EP_STATE_DISABLED:
|
||||
return "disabled";
|
||||
case EP_STATE_RUNNING:
|
||||
return "running";
|
||||
case EP_STATE_HALTED:
|
||||
return "halted";
|
||||
case EP_STATE_STOPPED:
|
||||
return "stopped";
|
||||
case EP_STATE_ERROR:
|
||||
return "error";
|
||||
default:
|
||||
return "INVALID";
|
||||
}
|
||||
}
|
||||
|
||||
static inline const char *cdnsp_ep_type_string(u8 type)
|
||||
{
|
||||
switch (type) {
|
||||
case ISOC_OUT_EP:
|
||||
return "Isoc OUT";
|
||||
case BULK_OUT_EP:
|
||||
return "Bulk OUT";
|
||||
case INT_OUT_EP:
|
||||
return "Int OUT";
|
||||
case CTRL_EP:
|
||||
return "Ctrl";
|
||||
case ISOC_IN_EP:
|
||||
return "Isoc IN";
|
||||
case BULK_IN_EP:
|
||||
return "Bulk IN";
|
||||
case INT_IN_EP:
|
||||
return "Int IN";
|
||||
default:
|
||||
return "INVALID";
|
||||
}
|
||||
}
|
||||
|
||||
static inline const char *cdnsp_decode_ep_context(char *str, size_t size,
|
||||
u32 info, u32 info2,
|
||||
u64 deq, u32 tx_info)
|
||||
{
|
||||
u8 max_pstr, ep_state, interval, ep_type, burst, cerr, mult;
|
||||
bool lsa, hid;
|
||||
u16 maxp, avg;
|
||||
u32 esit;
|
||||
int ret;
|
||||
|
||||
esit = CTX_TO_MAX_ESIT_PAYLOAD_HI(info) << 16 |
|
||||
CTX_TO_MAX_ESIT_PAYLOAD_LO(tx_info);
|
||||
|
||||
ep_state = info & EP_STATE_MASK;
|
||||
max_pstr = CTX_TO_EP_MAXPSTREAMS(info);
|
||||
interval = CTX_TO_EP_INTERVAL(info);
|
||||
mult = CTX_TO_EP_MULT(info) + 1;
|
||||
lsa = !!(info & EP_HAS_LSA);
|
||||
|
||||
cerr = (info2 & (3 << 1)) >> 1;
|
||||
ep_type = CTX_TO_EP_TYPE(info2);
|
||||
hid = !!(info2 & (1 << 7));
|
||||
burst = CTX_TO_MAX_BURST(info2);
|
||||
maxp = MAX_PACKET_DECODED(info2);
|
||||
|
||||
avg = EP_AVG_TRB_LENGTH(tx_info);
|
||||
|
||||
ret = snprintf(str, size, "State %s mult %d max P. Streams %d %s",
|
||||
cdnsp_ep_state_string(ep_state), mult,
|
||||
max_pstr, lsa ? "LSA " : "");
|
||||
|
||||
ret += snprintf(str + ret, size - ret,
|
||||
"interval %d us max ESIT payload %d CErr %d ",
|
||||
(1 << interval) * 125, esit, cerr);
|
||||
|
||||
ret += snprintf(str + ret, size - ret,
|
||||
"Type %s %sburst %d maxp %d deq %016llx ",
|
||||
cdnsp_ep_type_string(ep_type), hid ? "HID" : "",
|
||||
burst, maxp, deq);
|
||||
|
||||
ret += snprintf(str + ret, size - ret, "avg trb len %d", avg);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
#endif /*__LINUX_CDNSP_DEBUG*/
|
|
@ -0,0 +1,489 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Cadence CDNSP DRD Driver.
|
||||
*
|
||||
* Copyright (C) 2020 Cadence.
|
||||
*
|
||||
* Author: Pawel Laszczak <pawell@cadence.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/usb/composite.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
#include "cdnsp-gadget.h"
|
||||
#include "cdnsp-trace.h"
|
||||
|
||||
static void cdnsp_ep0_stall(struct cdnsp_device *pdev)
|
||||
{
|
||||
struct cdnsp_request *preq;
|
||||
struct cdnsp_ep *pep;
|
||||
|
||||
pep = &pdev->eps[0];
|
||||
preq = next_request(&pep->pending_list);
|
||||
|
||||
if (pdev->three_stage_setup) {
|
||||
cdnsp_halt_endpoint(pdev, pep, true);
|
||||
|
||||
if (preq)
|
||||
cdnsp_gadget_giveback(pep, preq, -ECONNRESET);
|
||||
} else {
|
||||
pep->ep_state |= EP0_HALTED_STATUS;
|
||||
|
||||
if (preq)
|
||||
list_del(&preq->list);
|
||||
|
||||
cdnsp_status_stage(pdev);
|
||||
}
|
||||
}
|
||||
|
||||
static int cdnsp_ep0_delegate_req(struct cdnsp_device *pdev,
|
||||
struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
int ret;
|
||||
|
||||
spin_unlock(&pdev->lock);
|
||||
ret = pdev->gadget_driver->setup(&pdev->gadget, ctrl);
|
||||
spin_lock(&pdev->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cdnsp_ep0_set_config(struct cdnsp_device *pdev,
|
||||
struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
enum usb_device_state state = pdev->gadget.state;
|
||||
u32 cfg;
|
||||
int ret;
|
||||
|
||||
cfg = le16_to_cpu(ctrl->wValue);
|
||||
|
||||
switch (state) {
|
||||
case USB_STATE_ADDRESS:
|
||||
trace_cdnsp_ep0_set_config("from Address state");
|
||||
break;
|
||||
case USB_STATE_CONFIGURED:
|
||||
trace_cdnsp_ep0_set_config("from Configured state");
|
||||
break;
|
||||
default:
|
||||
dev_err(pdev->dev, "Set Configuration - bad device state\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = cdnsp_ep0_delegate_req(pdev, ctrl);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!cfg)
|
||||
usb_gadget_set_state(&pdev->gadget, USB_STATE_ADDRESS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdnsp_ep0_set_address(struct cdnsp_device *pdev,
|
||||
struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
enum usb_device_state state = pdev->gadget.state;
|
||||
struct cdnsp_slot_ctx *slot_ctx;
|
||||
unsigned int slot_state;
|
||||
int ret;
|
||||
u32 addr;
|
||||
|
||||
addr = le16_to_cpu(ctrl->wValue);
|
||||
|
||||
if (addr > 127) {
|
||||
dev_err(pdev->dev, "Invalid device address %d\n", addr);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
slot_ctx = cdnsp_get_slot_ctx(&pdev->out_ctx);
|
||||
|
||||
if (state == USB_STATE_CONFIGURED) {
|
||||
dev_err(pdev->dev, "Can't Set Address from Configured State\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pdev->device_address = le16_to_cpu(ctrl->wValue);
|
||||
|
||||
slot_ctx = cdnsp_get_slot_ctx(&pdev->out_ctx);
|
||||
slot_state = GET_SLOT_STATE(le32_to_cpu(slot_ctx->dev_state));
|
||||
if (slot_state == SLOT_STATE_ADDRESSED)
|
||||
cdnsp_reset_device(pdev);
|
||||
|
||||
/*set device address*/
|
||||
ret = cdnsp_setup_device(pdev, SETUP_CONTEXT_ADDRESS);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (addr)
|
||||
usb_gadget_set_state(&pdev->gadget, USB_STATE_ADDRESS);
|
||||
else
|
||||
usb_gadget_set_state(&pdev->gadget, USB_STATE_DEFAULT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cdnsp_status_stage(struct cdnsp_device *pdev)
|
||||
{
|
||||
pdev->ep0_stage = CDNSP_STATUS_STAGE;
|
||||
pdev->ep0_preq.request.length = 0;
|
||||
|
||||
return cdnsp_ep_enqueue(pdev->ep0_preq.pep, &pdev->ep0_preq);
|
||||
}
|
||||
|
||||
static int cdnsp_w_index_to_ep_index(u16 wIndex)
|
||||
{
|
||||
if (!(wIndex & USB_ENDPOINT_NUMBER_MASK))
|
||||
return 0;
|
||||
|
||||
return ((wIndex & USB_ENDPOINT_NUMBER_MASK) * 2) +
|
||||
(wIndex & USB_ENDPOINT_DIR_MASK ? 1 : 0) - 1;
|
||||
}
|
||||
|
||||
static int cdnsp_ep0_handle_status(struct cdnsp_device *pdev,
|
||||
struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
struct cdnsp_ep *pep;
|
||||
__le16 *response;
|
||||
int ep_sts = 0;
|
||||
u16 status = 0;
|
||||
u32 recipient;
|
||||
|
||||
recipient = ctrl->bRequestType & USB_RECIP_MASK;
|
||||
|
||||
switch (recipient) {
|
||||
case USB_RECIP_DEVICE:
|
||||
status = pdev->gadget.is_selfpowered;
|
||||
status |= pdev->may_wakeup << USB_DEVICE_REMOTE_WAKEUP;
|
||||
|
||||
if (pdev->gadget.speed >= USB_SPEED_SUPER) {
|
||||
status |= pdev->u1_allowed << USB_DEV_STAT_U1_ENABLED;
|
||||
status |= pdev->u2_allowed << USB_DEV_STAT_U2_ENABLED;
|
||||
}
|
||||
break;
|
||||
case USB_RECIP_INTERFACE:
|
||||
/*
|
||||
* Function Remote Wake Capable D0
|
||||
* Function Remote Wakeup D1
|
||||
*/
|
||||
return cdnsp_ep0_delegate_req(pdev, ctrl);
|
||||
case USB_RECIP_ENDPOINT:
|
||||
ep_sts = cdnsp_w_index_to_ep_index(le16_to_cpu(ctrl->wIndex));
|
||||
pep = &pdev->eps[ep_sts];
|
||||
ep_sts = GET_EP_CTX_STATE(pep->out_ctx);
|
||||
|
||||
/* check if endpoint is stalled */
|
||||
if (ep_sts == EP_STATE_HALTED)
|
||||
status = BIT(USB_ENDPOINT_HALT);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
response = (__le16 *)pdev->setup_buf;
|
||||
*response = cpu_to_le16(status);
|
||||
|
||||
pdev->ep0_preq.request.length = sizeof(*response);
|
||||
pdev->ep0_preq.request.buf = pdev->setup_buf;
|
||||
|
||||
return cdnsp_ep_enqueue(pdev->ep0_preq.pep, &pdev->ep0_preq);
|
||||
}
|
||||
|
||||
static void cdnsp_enter_test_mode(struct cdnsp_device *pdev)
|
||||
{
|
||||
u32 temp;
|
||||
|
||||
temp = readl(&pdev->active_port->regs->portpmsc) & ~GENMASK(31, 28);
|
||||
temp |= PORT_TEST_MODE(pdev->test_mode);
|
||||
writel(temp, &pdev->active_port->regs->portpmsc);
|
||||
}
|
||||
|
||||
static int cdnsp_ep0_handle_feature_device(struct cdnsp_device *pdev,
|
||||
struct usb_ctrlrequest *ctrl,
|
||||
int set)
|
||||
{
|
||||
enum usb_device_state state;
|
||||
enum usb_device_speed speed;
|
||||
u16 tmode;
|
||||
|
||||
state = pdev->gadget.state;
|
||||
speed = pdev->gadget.speed;
|
||||
|
||||
switch (le16_to_cpu(ctrl->wValue)) {
|
||||
case USB_DEVICE_REMOTE_WAKEUP:
|
||||
pdev->may_wakeup = !!set;
|
||||
trace_cdnsp_may_wakeup(set);
|
||||
break;
|
||||
case USB_DEVICE_U1_ENABLE:
|
||||
if (state != USB_STATE_CONFIGURED || speed < USB_SPEED_SUPER)
|
||||
return -EINVAL;
|
||||
|
||||
pdev->u1_allowed = !!set;
|
||||
trace_cdnsp_u1(set);
|
||||
break;
|
||||
case USB_DEVICE_U2_ENABLE:
|
||||
if (state != USB_STATE_CONFIGURED || speed < USB_SPEED_SUPER)
|
||||
return -EINVAL;
|
||||
|
||||
pdev->u2_allowed = !!set;
|
||||
trace_cdnsp_u2(set);
|
||||
break;
|
||||
case USB_DEVICE_LTM_ENABLE:
|
||||
return -EINVAL;
|
||||
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;
|
||||
|
||||
tmode = tmode >> 8;
|
||||
|
||||
if (tmode > USB_TEST_FORCE_ENABLE || tmode < USB_TEST_J)
|
||||
return -EINVAL;
|
||||
|
||||
pdev->test_mode = tmode;
|
||||
|
||||
/*
|
||||
* Test mode must be set before Status Stage but controller
|
||||
* will start testing sequence after Status Stage.
|
||||
*/
|
||||
cdnsp_enter_test_mode(pdev);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdnsp_ep0_handle_feature_intf(struct cdnsp_device *pdev,
|
||||
struct usb_ctrlrequest *ctrl,
|
||||
int set)
|
||||
{
|
||||
u16 wValue, wIndex;
|
||||
int ret;
|
||||
|
||||
wValue = le16_to_cpu(ctrl->wValue);
|
||||
wIndex = le16_to_cpu(ctrl->wIndex);
|
||||
|
||||
switch (wValue) {
|
||||
case USB_INTRF_FUNC_SUSPEND:
|
||||
ret = cdnsp_ep0_delegate_req(pdev, ctrl);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Remote wakeup is enabled when any function within a device
|
||||
* is enabled for function remote wakeup.
|
||||
*/
|
||||
if (wIndex & USB_INTRF_FUNC_SUSPEND_RW)
|
||||
pdev->may_wakeup++;
|
||||
else
|
||||
if (pdev->may_wakeup > 0)
|
||||
pdev->may_wakeup--;
|
||||
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdnsp_ep0_handle_feature_endpoint(struct cdnsp_device *pdev,
|
||||
struct usb_ctrlrequest *ctrl,
|
||||
int set)
|
||||
{
|
||||
struct cdnsp_ep *pep;
|
||||
u16 wValue;
|
||||
|
||||
wValue = le16_to_cpu(ctrl->wValue);
|
||||
pep = &pdev->eps[cdnsp_w_index_to_ep_index(le16_to_cpu(ctrl->wIndex))];
|
||||
|
||||
switch (wValue) {
|
||||
case USB_ENDPOINT_HALT:
|
||||
if (!set && (pep->ep_state & EP_WEDGE)) {
|
||||
/* Resets Sequence Number */
|
||||
cdnsp_halt_endpoint(pdev, pep, 0);
|
||||
cdnsp_halt_endpoint(pdev, pep, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
return cdnsp_halt_endpoint(pdev, pep, set);
|
||||
default:
|
||||
dev_warn(pdev->dev, "WARN Incorrect wValue %04x\n", wValue);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdnsp_ep0_handle_feature(struct cdnsp_device *pdev,
|
||||
struct usb_ctrlrequest *ctrl,
|
||||
int set)
|
||||
{
|
||||
switch (ctrl->bRequestType & USB_RECIP_MASK) {
|
||||
case USB_RECIP_DEVICE:
|
||||
return cdnsp_ep0_handle_feature_device(pdev, ctrl, set);
|
||||
case USB_RECIP_INTERFACE:
|
||||
return cdnsp_ep0_handle_feature_intf(pdev, ctrl, set);
|
||||
case USB_RECIP_ENDPOINT:
|
||||
return cdnsp_ep0_handle_feature_endpoint(pdev, ctrl, set);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int cdnsp_ep0_set_sel(struct cdnsp_device *pdev,
|
||||
struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
enum usb_device_state state = pdev->gadget.state;
|
||||
u16 wLength;
|
||||
|
||||
if (state == USB_STATE_DEFAULT)
|
||||
return -EINVAL;
|
||||
|
||||
wLength = le16_to_cpu(ctrl->wLength);
|
||||
|
||||
if (wLength != 6) {
|
||||
dev_err(pdev->dev, "Set SEL should be 6 bytes, got %d\n",
|
||||
wLength);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* To handle Set SEL we need to receive 6 bytes from Host. So let's
|
||||
* queue a usb_request for 6 bytes.
|
||||
*/
|
||||
pdev->ep0_preq.request.length = 6;
|
||||
pdev->ep0_preq.request.buf = pdev->setup_buf;
|
||||
|
||||
return cdnsp_ep_enqueue(pdev->ep0_preq.pep, &pdev->ep0_preq);
|
||||
}
|
||||
|
||||
static int cdnsp_ep0_set_isoch_delay(struct cdnsp_device *pdev,
|
||||
struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
if (le16_to_cpu(ctrl->wIndex) || le16_to_cpu(ctrl->wLength))
|
||||
return -EINVAL;
|
||||
|
||||
pdev->gadget.isoch_delay = le16_to_cpu(ctrl->wValue);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdnsp_ep0_std_request(struct cdnsp_device *pdev,
|
||||
struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (ctrl->bRequest) {
|
||||
case USB_REQ_GET_STATUS:
|
||||
ret = cdnsp_ep0_handle_status(pdev, ctrl);
|
||||
break;
|
||||
case USB_REQ_CLEAR_FEATURE:
|
||||
ret = cdnsp_ep0_handle_feature(pdev, ctrl, 0);
|
||||
break;
|
||||
case USB_REQ_SET_FEATURE:
|
||||
ret = cdnsp_ep0_handle_feature(pdev, ctrl, 1);
|
||||
break;
|
||||
case USB_REQ_SET_ADDRESS:
|
||||
ret = cdnsp_ep0_set_address(pdev, ctrl);
|
||||
break;
|
||||
case USB_REQ_SET_CONFIGURATION:
|
||||
ret = cdnsp_ep0_set_config(pdev, ctrl);
|
||||
break;
|
||||
case USB_REQ_SET_SEL:
|
||||
ret = cdnsp_ep0_set_sel(pdev, ctrl);
|
||||
break;
|
||||
case USB_REQ_SET_ISOCH_DELAY:
|
||||
ret = cdnsp_ep0_set_isoch_delay(pdev, ctrl);
|
||||
break;
|
||||
case USB_REQ_SET_INTERFACE:
|
||||
/*
|
||||
* Add request into pending list to block sending status stage
|
||||
* by libcomposite.
|
||||
*/
|
||||
list_add_tail(&pdev->ep0_preq.list,
|
||||
&pdev->ep0_preq.pep->pending_list);
|
||||
|
||||
ret = cdnsp_ep0_delegate_req(pdev, ctrl);
|
||||
if (ret == -EBUSY)
|
||||
ret = 0;
|
||||
|
||||
list_del(&pdev->ep0_preq.list);
|
||||
break;
|
||||
default:
|
||||
ret = cdnsp_ep0_delegate_req(pdev, ctrl);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void cdnsp_setup_analyze(struct cdnsp_device *pdev)
|
||||
{
|
||||
struct usb_ctrlrequest *ctrl = &pdev->setup;
|
||||
int ret = 0;
|
||||
u16 len;
|
||||
|
||||
trace_cdnsp_ctrl_req(ctrl);
|
||||
|
||||
if (!pdev->gadget_driver)
|
||||
goto out;
|
||||
|
||||
if (pdev->gadget.state == USB_STATE_NOTATTACHED) {
|
||||
dev_err(pdev->dev, "ERR: Setup detected in unattached state\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Restore the ep0 to Stopped/Running state. */
|
||||
if (pdev->eps[0].ep_state & EP_HALTED) {
|
||||
trace_cdnsp_ep0_halted("Restore to normal state");
|
||||
cdnsp_halt_endpoint(pdev, &pdev->eps[0], 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Finishing previous SETUP transfer by removing request from
|
||||
* list and informing upper layer
|
||||
*/
|
||||
if (!list_empty(&pdev->eps[0].pending_list)) {
|
||||
struct cdnsp_request *req;
|
||||
|
||||
trace_cdnsp_ep0_request("Remove previous");
|
||||
req = next_request(&pdev->eps[0].pending_list);
|
||||
cdnsp_ep_dequeue(&pdev->eps[0], req);
|
||||
}
|
||||
|
||||
len = le16_to_cpu(ctrl->wLength);
|
||||
if (!len) {
|
||||
pdev->three_stage_setup = false;
|
||||
pdev->ep0_expect_in = false;
|
||||
} else {
|
||||
pdev->three_stage_setup = true;
|
||||
pdev->ep0_expect_in = !!(ctrl->bRequestType & USB_DIR_IN);
|
||||
}
|
||||
|
||||
if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
|
||||
ret = cdnsp_ep0_std_request(pdev, ctrl);
|
||||
else
|
||||
ret = cdnsp_ep0_delegate_req(pdev, ctrl);
|
||||
|
||||
if (!len)
|
||||
pdev->ep0_stage = CDNSP_STATUS_STAGE;
|
||||
|
||||
if (ret == USB_GADGET_DELAYED_STATUS) {
|
||||
trace_cdnsp_ep0_status_stage("delayed");
|
||||
return;
|
||||
}
|
||||
out:
|
||||
if (ret < 0)
|
||||
cdnsp_ep0_stall(pdev);
|
||||
else if (pdev->ep0_stage == CDNSP_STATUS_STAGE)
|
||||
cdnsp_status_stage(pdev);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,254 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Cadence PCI Glue driver.
|
||||
*
|
||||
* Copyright (C) 2019 Cadence.
|
||||
*
|
||||
* Author: Pawel Laszczak <pawell@cadence.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "gadget-export.h"
|
||||
|
||||
#define PCI_BAR_HOST 0
|
||||
#define PCI_BAR_OTG 0
|
||||
#define PCI_BAR_DEV 2
|
||||
|
||||
#define PCI_DEV_FN_HOST_DEVICE 0
|
||||
#define PCI_DEV_FN_OTG 1
|
||||
|
||||
#define PCI_DRIVER_NAME "cdns-pci-usbssp"
|
||||
#define PLAT_DRIVER_NAME "cdns-usbssp"
|
||||
|
||||
#define CDNS_VENDOR_ID 0x17cd
|
||||
#define CDNS_DEVICE_ID 0x0100
|
||||
#define CDNS_DRD_IF (PCI_CLASS_SERIAL_USB << 8 | 0x80)
|
||||
|
||||
static struct pci_dev *cdnsp_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 (!func)
|
||||
return NULL;
|
||||
|
||||
if (func->devfn == pdev->devfn) {
|
||||
func = pci_get_device(pdev->vendor, pdev->device, func);
|
||||
if (!func)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return func;
|
||||
}
|
||||
|
||||
static int cdnsp_pci_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct pci_dev *func;
|
||||
struct resource *res;
|
||||
struct cdns *cdnsp;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* 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 = cdnsp_get_second_fun(pdev);
|
||||
if (!func)
|
||||
return -EINVAL;
|
||||
|
||||
if (func->class == PCI_CLASS_SERIAL_USB_XHCI ||
|
||||
pdev->class == PCI_CLASS_SERIAL_USB_XHCI) {
|
||||
ret = -EINVAL;
|
||||
goto put_pci;
|
||||
}
|
||||
|
||||
ret = pcim_enable_device(pdev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Enabling PCI device has failed %d\n", ret);
|
||||
goto put_pci;
|
||||
}
|
||||
|
||||
pci_set_master(pdev);
|
||||
if (pci_is_enabled(func)) {
|
||||
cdnsp = pci_get_drvdata(func);
|
||||
} else {
|
||||
cdnsp = kzalloc(sizeof(*cdnsp), GFP_KERNEL);
|
||||
if (!cdnsp) {
|
||||
ret = -ENOMEM;
|
||||
goto disable_pci;
|
||||
}
|
||||
}
|
||||
|
||||
/* For GADGET device function number is 0. */
|
||||
if (pdev->devfn == 0) {
|
||||
resource_size_t rsrc_start, rsrc_len;
|
||||
|
||||
/* Function 0: host(BAR_0) + device(BAR_1).*/
|
||||
dev_dbg(dev, "Initialize resources\n");
|
||||
rsrc_start = pci_resource_start(pdev, PCI_BAR_DEV);
|
||||
rsrc_len = pci_resource_len(pdev, PCI_BAR_DEV);
|
||||
res = devm_request_mem_region(dev, rsrc_start, rsrc_len, "dev");
|
||||
if (!res) {
|
||||
dev_dbg(dev, "controller already in use\n");
|
||||
ret = -EBUSY;
|
||||
goto free_cdnsp;
|
||||
}
|
||||
|
||||
cdnsp->dev_regs = devm_ioremap(dev, rsrc_start, rsrc_len);
|
||||
if (!cdnsp->dev_regs) {
|
||||
dev_dbg(dev, "error mapping memory\n");
|
||||
ret = -EFAULT;
|
||||
goto free_cdnsp;
|
||||
}
|
||||
|
||||
cdnsp->dev_irq = pdev->irq;
|
||||
dev_dbg(dev, "USBSS-DEV physical base addr: %pa\n",
|
||||
&rsrc_start);
|
||||
|
||||
res = &cdnsp->xhci_res[0];
|
||||
res->start = pci_resource_start(pdev, PCI_BAR_HOST);
|
||||
res->end = pci_resource_end(pdev, PCI_BAR_HOST);
|
||||
res->name = "xhci";
|
||||
res->flags = IORESOURCE_MEM;
|
||||
dev_dbg(dev, "USBSS-XHCI physical base addr: %pa\n",
|
||||
&res->start);
|
||||
|
||||
/* Interrupt for XHCI, */
|
||||
res = &cdnsp->xhci_res[1];
|
||||
res->start = pdev->irq;
|
||||
res->name = "host";
|
||||
res->flags = IORESOURCE_IRQ;
|
||||
} else {
|
||||
res = &cdnsp->otg_res;
|
||||
res->start = pci_resource_start(pdev, PCI_BAR_OTG);
|
||||
res->end = pci_resource_end(pdev, PCI_BAR_OTG);
|
||||
res->name = "otg";
|
||||
res->flags = IORESOURCE_MEM;
|
||||
dev_dbg(dev, "CDNSP-DRD physical base addr: %pa\n",
|
||||
&res->start);
|
||||
|
||||
/* Interrupt for OTG/DRD. */
|
||||
cdnsp->otg_irq = pdev->irq;
|
||||
}
|
||||
|
||||
if (pci_is_enabled(func)) {
|
||||
cdnsp->dev = dev;
|
||||
cdnsp->gadget_init = cdnsp_gadget_init;
|
||||
|
||||
ret = cdns_init(cdnsp);
|
||||
if (ret)
|
||||
goto free_cdnsp;
|
||||
}
|
||||
|
||||
pci_set_drvdata(pdev, cdnsp);
|
||||
|
||||
device_wakeup_enable(&pdev->dev);
|
||||
if (pci_dev_run_wake(pdev))
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
free_cdnsp:
|
||||
if (!pci_is_enabled(func))
|
||||
kfree(cdnsp);
|
||||
|
||||
disable_pci:
|
||||
pci_disable_device(pdev);
|
||||
|
||||
put_pci:
|
||||
pci_dev_put(func);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void cdnsp_pci_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct cdns *cdnsp;
|
||||
struct pci_dev *func;
|
||||
|
||||
func = cdnsp_get_second_fun(pdev);
|
||||
cdnsp = (struct cdns *)pci_get_drvdata(pdev);
|
||||
|
||||
if (pci_dev_run_wake(pdev))
|
||||
pm_runtime_get_noresume(&pdev->dev);
|
||||
|
||||
if (!pci_is_enabled(func)) {
|
||||
kfree(cdnsp);
|
||||
goto pci_put;
|
||||
}
|
||||
|
||||
cdns_remove(cdnsp);
|
||||
|
||||
pci_put:
|
||||
pci_dev_put(func);
|
||||
}
|
||||
|
||||
static int __maybe_unused cdnsp_pci_suspend(struct device *dev)
|
||||
{
|
||||
struct cdns *cdns = dev_get_drvdata(dev);
|
||||
|
||||
return cdns_suspend(cdns);
|
||||
}
|
||||
|
||||
static int __maybe_unused cdnsp_pci_resume(struct device *dev)
|
||||
{
|
||||
struct cdns *cdns = dev_get_drvdata(dev);
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&cdns->lock, flags);
|
||||
ret = cdns_resume(cdns, 1);
|
||||
spin_unlock_irqrestore(&cdns->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops cdnsp_pci_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(cdnsp_pci_suspend, cdnsp_pci_resume)
|
||||
};
|
||||
|
||||
static const struct pci_device_id cdnsp_pci_ids[] = {
|
||||
{ PCI_VENDOR_ID_CDNS, CDNS_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,
|
||||
PCI_CLASS_SERIAL_USB_DEVICE, PCI_ANY_ID },
|
||||
{ PCI_VENDOR_ID_CDNS, CDNS_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,
|
||||
CDNS_DRD_IF, PCI_ANY_ID },
|
||||
{ 0, }
|
||||
};
|
||||
|
||||
static struct pci_driver cdnsp_pci_driver = {
|
||||
.name = "cdnsp-pci",
|
||||
.id_table = &cdnsp_pci_ids[0],
|
||||
.probe = cdnsp_pci_probe,
|
||||
.remove = cdnsp_pci_remove,
|
||||
.driver = {
|
||||
.pm = &cdnsp_pci_pm_ops,
|
||||
}
|
||||
};
|
||||
|
||||
module_pci_driver(cdnsp_pci_driver);
|
||||
MODULE_DEVICE_TABLE(pci, cdnsp_pci_ids);
|
||||
|
||||
MODULE_ALIAS("pci:cdnsp");
|
||||
MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("Cadence CDNSP PCI driver");
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,12 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Cadence CDNSP DRD Driver.
|
||||
*
|
||||
* Copyright (C) 2020 Cadence.
|
||||
*
|
||||
* Author: Pawel Laszczak <pawell@cadence.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include "cdnsp-trace.h"
|
|
@ -0,0 +1,830 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Cadence CDNSP DRD Driver.
|
||||
* Trace support header file
|
||||
*
|
||||
* Copyright (C) 2020 Cadence.
|
||||
*
|
||||
* Author: Pawel Laszczak <pawell@cadence.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#undef TRACE_SYSTEM
|
||||
#define TRACE_SYSTEM cdnsp-dev
|
||||
|
||||
/*
|
||||
* The TRACE_SYSTEM_VAR defaults to TRACE_SYSTEM, but must be a
|
||||
* legitimate C variable. It is not exported to user space.
|
||||
*/
|
||||
#undef TRACE_SYSTEM_VAR
|
||||
#define TRACE_SYSTEM_VAR cdnsp_dev
|
||||
|
||||
#if !defined(__CDNSP_DEV_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||
#define __CDNSP_DEV_TRACE_H
|
||||
|
||||
#include <linux/tracepoint.h>
|
||||
#include "cdnsp-gadget.h"
|
||||
#include "cdnsp-debug.h"
|
||||
|
||||
/*
|
||||
* There is limitation for single buffer size in TRACEPOINT subsystem.
|
||||
* By default TRACE_BUF_SIZE is 1024, so no all data will be logged.
|
||||
* To show more data this must be increased. In most cases the default
|
||||
* value is sufficient.
|
||||
*/
|
||||
#define CDNSP_MSG_MAX 500
|
||||
|
||||
DECLARE_EVENT_CLASS(cdnsp_log_ep,
|
||||
TP_PROTO(struct cdnsp_ep *pep, u32 stream_id),
|
||||
TP_ARGS(pep, stream_id),
|
||||
TP_STRUCT__entry(
|
||||
__string(name, pep->name)
|
||||
__field(unsigned int, state)
|
||||
__field(u32, stream_id)
|
||||
__field(u8, enabled)
|
||||
__field(unsigned int, num_streams)
|
||||
__field(int, td_count)
|
||||
__field(u8, first_prime_det)
|
||||
__field(u8, drbls_count)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__assign_str(name, pep->name);
|
||||
__entry->state = pep->ep_state;
|
||||
__entry->stream_id = stream_id;
|
||||
__entry->enabled = pep->ep_state & EP_HAS_STREAMS;
|
||||
__entry->num_streams = pep->stream_info.num_streams;
|
||||
__entry->td_count = pep->stream_info.td_count;
|
||||
__entry->first_prime_det = pep->stream_info.first_prime_det;
|
||||
__entry->drbls_count = pep->stream_info.drbls_count;
|
||||
),
|
||||
TP_printk("%s: SID: %08x ep state: %x stream: enabled: %d num %d "
|
||||
"tds %d, first prime: %d drbls %d",
|
||||
__get_str(name), __entry->state, __entry->stream_id,
|
||||
__entry->enabled, __entry->num_streams, __entry->td_count,
|
||||
__entry->first_prime_det, __entry->drbls_count)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_ep, cdnsp_tr_drbl,
|
||||
TP_PROTO(struct cdnsp_ep *pep, u32 stream_id),
|
||||
TP_ARGS(pep, stream_id)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_ep, cdnsp_wait_for_prime,
|
||||
TP_PROTO(struct cdnsp_ep *pep, u32 stream_id),
|
||||
TP_ARGS(pep, stream_id)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_ep, cdnsp_ep_list_empty_with_skip,
|
||||
TP_PROTO(struct cdnsp_ep *pep, u32 stream_id),
|
||||
TP_ARGS(pep, stream_id)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_ep, cdnsp_ep_enable_end,
|
||||
TP_PROTO(struct cdnsp_ep *pep, u32 stream_id),
|
||||
TP_ARGS(pep, stream_id)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_ep, cdnsp_ep_disable_end,
|
||||
TP_PROTO(struct cdnsp_ep *pep, u32 stream_id),
|
||||
TP_ARGS(pep, stream_id)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_ep, cdnsp_ep_busy_try_halt_again,
|
||||
TP_PROTO(struct cdnsp_ep *pep, u32 stream_id),
|
||||
TP_ARGS(pep, stream_id)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(cdnsp_log_enable_disable,
|
||||
TP_PROTO(int set),
|
||||
TP_ARGS(set),
|
||||
TP_STRUCT__entry(
|
||||
__field(int, set)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->set = set;
|
||||
),
|
||||
TP_printk("%s", __entry->set ? "enabled" : "disabled")
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_enable_disable, cdnsp_pullup,
|
||||
TP_PROTO(int set),
|
||||
TP_ARGS(set)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_enable_disable, cdnsp_u1,
|
||||
TP_PROTO(int set),
|
||||
TP_ARGS(set)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_enable_disable, cdnsp_u2,
|
||||
TP_PROTO(int set),
|
||||
TP_ARGS(set)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_enable_disable, cdnsp_lpm,
|
||||
TP_PROTO(int set),
|
||||
TP_ARGS(set)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_enable_disable, cdnsp_may_wakeup,
|
||||
TP_PROTO(int set),
|
||||
TP_ARGS(set)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(cdnsp_log_simple,
|
||||
TP_PROTO(char *msg),
|
||||
TP_ARGS(msg),
|
||||
TP_STRUCT__entry(
|
||||
__string(text, msg)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__assign_str(text, msg)
|
||||
),
|
||||
TP_printk("%s", __get_str(text))
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_simple, cdnsp_exit,
|
||||
TP_PROTO(char *msg),
|
||||
TP_ARGS(msg)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_simple, cdnsp_init,
|
||||
TP_PROTO(char *msg),
|
||||
TP_ARGS(msg)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_simple, cdnsp_slot_id,
|
||||
TP_PROTO(char *msg),
|
||||
TP_ARGS(msg)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_simple, cdnsp_no_room_on_ring,
|
||||
TP_PROTO(char *msg),
|
||||
TP_ARGS(msg)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_simple, cdnsp_ep0_status_stage,
|
||||
TP_PROTO(char *msg),
|
||||
TP_ARGS(msg)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_simple, cdnsp_ep0_request,
|
||||
TP_PROTO(char *msg),
|
||||
TP_ARGS(msg)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_simple, cdnsp_ep0_set_config,
|
||||
TP_PROTO(char *msg),
|
||||
TP_ARGS(msg)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_simple, cdnsp_ep0_halted,
|
||||
TP_PROTO(char *msg),
|
||||
TP_ARGS(msg)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_simple, cdnsp_ep_halt,
|
||||
TP_PROTO(char *msg),
|
||||
TP_ARGS(msg)
|
||||
);
|
||||
|
||||
TRACE_EVENT(cdnsp_looking_trb_in_td,
|
||||
TP_PROTO(dma_addr_t suspect, dma_addr_t trb_start, dma_addr_t trb_end,
|
||||
dma_addr_t curr_seg, dma_addr_t end_seg),
|
||||
TP_ARGS(suspect, trb_start, trb_end, curr_seg, end_seg),
|
||||
TP_STRUCT__entry(
|
||||
__field(dma_addr_t, suspect)
|
||||
__field(dma_addr_t, trb_start)
|
||||
__field(dma_addr_t, trb_end)
|
||||
__field(dma_addr_t, curr_seg)
|
||||
__field(dma_addr_t, end_seg)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->suspect = suspect;
|
||||
__entry->trb_start = trb_start;
|
||||
__entry->trb_end = trb_end;
|
||||
__entry->curr_seg = curr_seg;
|
||||
__entry->end_seg = end_seg;
|
||||
),
|
||||
TP_printk("DMA: suspect event: %pad, trb-start: %pad, trb-end %pad, "
|
||||
"seg-start %pad, seg-end %pad",
|
||||
&__entry->suspect, &__entry->trb_start, &__entry->trb_end,
|
||||
&__entry->curr_seg, &__entry->end_seg)
|
||||
);
|
||||
|
||||
TRACE_EVENT(cdnsp_port_info,
|
||||
TP_PROTO(__le32 __iomem *addr, u32 offset, u32 count, u32 rev),
|
||||
TP_ARGS(addr, offset, count, rev),
|
||||
TP_STRUCT__entry(
|
||||
__field(__le32 __iomem *, addr)
|
||||
__field(u32, offset)
|
||||
__field(u32, count)
|
||||
__field(u32, rev)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->addr = addr;
|
||||
__entry->offset = offset;
|
||||
__entry->count = count;
|
||||
__entry->rev = rev;
|
||||
),
|
||||
TP_printk("Ext Cap %p, port offset = %u, count = %u, rev = 0x%x",
|
||||
__entry->addr, __entry->offset, __entry->count, __entry->rev)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(cdnsp_log_deq_state,
|
||||
TP_PROTO(struct cdnsp_dequeue_state *state),
|
||||
TP_ARGS(state),
|
||||
TP_STRUCT__entry(
|
||||
__field(int, new_cycle_state)
|
||||
__field(struct cdnsp_segment *, new_deq_seg)
|
||||
__field(dma_addr_t, deq_seg_dma)
|
||||
__field(union cdnsp_trb *, new_deq_ptr)
|
||||
__field(dma_addr_t, deq_ptr_dma)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->new_cycle_state = state->new_cycle_state;
|
||||
__entry->new_deq_seg = state->new_deq_seg;
|
||||
__entry->deq_seg_dma = state->new_deq_seg->dma;
|
||||
__entry->new_deq_ptr = state->new_deq_ptr,
|
||||
__entry->deq_ptr_dma = cdnsp_trb_virt_to_dma(state->new_deq_seg,
|
||||
state->new_deq_ptr);
|
||||
),
|
||||
TP_printk("New cycle state = 0x%x, New dequeue segment = %p (0x%pad dma), "
|
||||
"New dequeue pointer = %p (0x%pad dma)",
|
||||
__entry->new_cycle_state, __entry->new_deq_seg,
|
||||
&__entry->deq_seg_dma, __entry->new_deq_ptr,
|
||||
&__entry->deq_ptr_dma
|
||||
)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_deq_state, cdnsp_new_deq_state,
|
||||
TP_PROTO(struct cdnsp_dequeue_state *state),
|
||||
TP_ARGS(state)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(cdnsp_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, CDNSP_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), CDNSP_MSG_MAX,
|
||||
__entry->bRequestType,
|
||||
__entry->bRequest, __entry->wValue,
|
||||
__entry->wIndex, __entry->wLength)
|
||||
)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_ctrl, cdnsp_ctrl_req,
|
||||
TP_PROTO(struct usb_ctrlrequest *ctrl),
|
||||
TP_ARGS(ctrl)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(cdnsp_log_bounce,
|
||||
TP_PROTO(struct cdnsp_request *preq, u32 new_buf_len, u32 offset,
|
||||
dma_addr_t dma, unsigned int unalign),
|
||||
TP_ARGS(preq, new_buf_len, offset, dma, unalign),
|
||||
TP_STRUCT__entry(
|
||||
__string(name, preq->pep->name)
|
||||
__field(u32, new_buf_len)
|
||||
__field(u32, offset)
|
||||
__field(dma_addr_t, dma)
|
||||
__field(unsigned int, unalign)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__assign_str(name, preq->pep->name);
|
||||
__entry->new_buf_len = new_buf_len;
|
||||
__entry->offset = offset;
|
||||
__entry->dma = dma;
|
||||
__entry->unalign = unalign;
|
||||
),
|
||||
TP_printk("%s buf len %d, offset %d, dma %pad, unalign %d",
|
||||
__get_str(name), __entry->new_buf_len,
|
||||
__entry->offset, &__entry->dma, __entry->unalign
|
||||
)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_bounce, cdnsp_bounce_align_td_split,
|
||||
TP_PROTO(struct cdnsp_request *preq, u32 new_buf_len, u32 offset,
|
||||
dma_addr_t dma, unsigned int unalign),
|
||||
TP_ARGS(preq, new_buf_len, offset, dma, unalign)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_bounce, cdnsp_bounce_map,
|
||||
TP_PROTO(struct cdnsp_request *preq, u32 new_buf_len, u32 offset,
|
||||
dma_addr_t dma, unsigned int unalign),
|
||||
TP_ARGS(preq, new_buf_len, offset, dma, unalign)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_bounce, cdnsp_bounce_unmap,
|
||||
TP_PROTO(struct cdnsp_request *preq, u32 new_buf_len, u32 offset,
|
||||
dma_addr_t dma, unsigned int unalign),
|
||||
TP_ARGS(preq, new_buf_len, offset, dma, unalign)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(cdnsp_log_trb,
|
||||
TP_PROTO(struct cdnsp_ring *ring, struct cdnsp_generic_trb *trb),
|
||||
TP_ARGS(ring, trb),
|
||||
TP_STRUCT__entry(
|
||||
__field(u32, type)
|
||||
__field(u32, field0)
|
||||
__field(u32, field1)
|
||||
__field(u32, field2)
|
||||
__field(u32, field3)
|
||||
__field(union cdnsp_trb *, trb)
|
||||
__field(dma_addr_t, trb_dma)
|
||||
__dynamic_array(char, str, CDNSP_MSG_MAX)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->type = ring->type;
|
||||
__entry->field0 = le32_to_cpu(trb->field[0]);
|
||||
__entry->field1 = le32_to_cpu(trb->field[1]);
|
||||
__entry->field2 = le32_to_cpu(trb->field[2]);
|
||||
__entry->field3 = le32_to_cpu(trb->field[3]);
|
||||
__entry->trb = (union cdnsp_trb *)trb;
|
||||
__entry->trb_dma = cdnsp_trb_virt_to_dma(ring->deq_seg,
|
||||
(union cdnsp_trb *)trb);
|
||||
|
||||
),
|
||||
TP_printk("%s: %s trb: %p(%pad)", cdnsp_ring_type_string(__entry->type),
|
||||
cdnsp_decode_trb(__get_str(str), CDNSP_MSG_MAX,
|
||||
__entry->field0, __entry->field1,
|
||||
__entry->field2, __entry->field3),
|
||||
__entry->trb, &__entry->trb_dma
|
||||
)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_trb, cdnsp_handle_event,
|
||||
TP_PROTO(struct cdnsp_ring *ring, struct cdnsp_generic_trb *trb),
|
||||
TP_ARGS(ring, trb)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_trb, cdnsp_trb_without_td,
|
||||
TP_PROTO(struct cdnsp_ring *ring, struct cdnsp_generic_trb *trb),
|
||||
TP_ARGS(ring, trb)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_trb, cdnsp_handle_command,
|
||||
TP_PROTO(struct cdnsp_ring *ring, struct cdnsp_generic_trb *trb),
|
||||
TP_ARGS(ring, trb)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_trb, cdnsp_handle_transfer,
|
||||
TP_PROTO(struct cdnsp_ring *ring, struct cdnsp_generic_trb *trb),
|
||||
TP_ARGS(ring, trb)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_trb, cdnsp_queue_trb,
|
||||
TP_PROTO(struct cdnsp_ring *ring, struct cdnsp_generic_trb *trb),
|
||||
TP_ARGS(ring, trb)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_trb, cdnsp_cmd_wait_for_compl,
|
||||
TP_PROTO(struct cdnsp_ring *ring, struct cdnsp_generic_trb *trb),
|
||||
TP_ARGS(ring, trb)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_trb, cdnsp_cmd_timeout,
|
||||
TP_PROTO(struct cdnsp_ring *ring, struct cdnsp_generic_trb *trb),
|
||||
TP_ARGS(ring, trb)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_trb, cdnsp_defered_event,
|
||||
TP_PROTO(struct cdnsp_ring *ring, struct cdnsp_generic_trb *trb),
|
||||
TP_ARGS(ring, trb)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(cdnsp_log_pdev,
|
||||
TP_PROTO(struct cdnsp_device *pdev),
|
||||
TP_ARGS(pdev),
|
||||
TP_STRUCT__entry(
|
||||
__field(struct cdnsp_device *, pdev)
|
||||
__field(struct usb_gadget *, gadget)
|
||||
__field(dma_addr_t, out_ctx)
|
||||
__field(dma_addr_t, in_ctx)
|
||||
__field(u8, port_num)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->pdev = pdev;
|
||||
__entry->gadget = &pdev->gadget;
|
||||
__entry->in_ctx = pdev->in_ctx.dma;
|
||||
__entry->out_ctx = pdev->out_ctx.dma;
|
||||
__entry->port_num = pdev->active_port ?
|
||||
pdev->active_port->port_num : 0xFF;
|
||||
),
|
||||
TP_printk("pdev %p gadget %p ctx %pad | %pad, port %d ",
|
||||
__entry->pdev, __entry->gadget, &__entry->in_ctx,
|
||||
&__entry->out_ctx, __entry->port_num
|
||||
)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_pdev, cdnsp_alloc_priv_device,
|
||||
TP_PROTO(struct cdnsp_device *vdev),
|
||||
TP_ARGS(vdev)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_pdev, cdnsp_free_priv_device,
|
||||
TP_PROTO(struct cdnsp_device *vdev),
|
||||
TP_ARGS(vdev)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_pdev, cdnsp_setup_device,
|
||||
TP_PROTO(struct cdnsp_device *vdev),
|
||||
TP_ARGS(vdev)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_pdev, cdnsp_setup_addressable_priv_device,
|
||||
TP_PROTO(struct cdnsp_device *vdev),
|
||||
TP_ARGS(vdev)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(cdnsp_log_request,
|
||||
TP_PROTO(struct cdnsp_request *req),
|
||||
TP_ARGS(req),
|
||||
TP_STRUCT__entry(
|
||||
__string(name, req->pep->name)
|
||||
__field(struct usb_request *, request)
|
||||
__field(struct cdnsp_request *, preq)
|
||||
__field(void *, buf)
|
||||
__field(unsigned int, actual)
|
||||
__field(unsigned int, length)
|
||||
__field(int, status)
|
||||
__field(dma_addr_t, dma)
|
||||
__field(unsigned int, stream_id)
|
||||
__field(unsigned int, zero)
|
||||
__field(unsigned int, short_not_ok)
|
||||
__field(unsigned int, no_interrupt)
|
||||
__field(struct scatterlist*, sg)
|
||||
__field(unsigned int, num_sgs)
|
||||
__field(unsigned int, num_mapped_sgs)
|
||||
|
||||
),
|
||||
TP_fast_assign(
|
||||
__assign_str(name, req->pep->name);
|
||||
__entry->request = &req->request;
|
||||
__entry->preq = req;
|
||||
__entry->buf = req->request.buf;
|
||||
__entry->actual = req->request.actual;
|
||||
__entry->length = req->request.length;
|
||||
__entry->status = req->request.status;
|
||||
__entry->dma = req->request.dma;
|
||||
__entry->stream_id = req->request.stream_id;
|
||||
__entry->zero = req->request.zero;
|
||||
__entry->short_not_ok = req->request.short_not_ok;
|
||||
__entry->no_interrupt = req->request.no_interrupt;
|
||||
__entry->sg = req->request.sg;
|
||||
__entry->num_sgs = req->request.num_sgs;
|
||||
__entry->num_mapped_sgs = req->request.num_mapped_sgs;
|
||||
),
|
||||
TP_printk("%s; req U:%p/P:%p, req buf %p, length %u/%u, status %d, "
|
||||
"buf dma (%pad), SID %u, %s%s%s, sg %p, num_sg %d,"
|
||||
" num_m_sg %d",
|
||||
__get_str(name), __entry->request, __entry->preq,
|
||||
__entry->buf, __entry->actual, __entry->length,
|
||||
__entry->status, &__entry->dma,
|
||||
__entry->stream_id, __entry->zero ? "Z" : "z",
|
||||
__entry->short_not_ok ? "S" : "s",
|
||||
__entry->no_interrupt ? "I" : "i",
|
||||
__entry->sg, __entry->num_sgs, __entry->num_mapped_sgs
|
||||
)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_request, cdnsp_request_enqueue,
|
||||
TP_PROTO(struct cdnsp_request *req),
|
||||
TP_ARGS(req)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_request, cdnsp_request_enqueue_busy,
|
||||
TP_PROTO(struct cdnsp_request *req),
|
||||
TP_ARGS(req)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_request, cdnsp_request_enqueue_error,
|
||||
TP_PROTO(struct cdnsp_request *req),
|
||||
TP_ARGS(req)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_request, cdnsp_request_dequeue,
|
||||
TP_PROTO(struct cdnsp_request *req),
|
||||
TP_ARGS(req)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_request, cdnsp_request_giveback,
|
||||
TP_PROTO(struct cdnsp_request *req),
|
||||
TP_ARGS(req)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_request, cdnsp_alloc_request,
|
||||
TP_PROTO(struct cdnsp_request *req),
|
||||
TP_ARGS(req)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_request, cdnsp_free_request,
|
||||
TP_PROTO(struct cdnsp_request *req),
|
||||
TP_ARGS(req)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(cdnsp_log_ep_ctx,
|
||||
TP_PROTO(struct cdnsp_ep_ctx *ctx),
|
||||
TP_ARGS(ctx),
|
||||
TP_STRUCT__entry(
|
||||
__field(u32, info)
|
||||
__field(u32, info2)
|
||||
__field(u64, deq)
|
||||
__field(u32, tx_info)
|
||||
__dynamic_array(char, str, CDNSP_MSG_MAX)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->info = le32_to_cpu(ctx->ep_info);
|
||||
__entry->info2 = le32_to_cpu(ctx->ep_info2);
|
||||
__entry->deq = le64_to_cpu(ctx->deq);
|
||||
__entry->tx_info = le32_to_cpu(ctx->tx_info);
|
||||
),
|
||||
TP_printk("%s", cdnsp_decode_ep_context(__get_str(str), CDNSP_MSG_MAX,
|
||||
__entry->info, __entry->info2,
|
||||
__entry->deq, __entry->tx_info)
|
||||
)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_ep_ctx, cdnsp_ep_disabled,
|
||||
TP_PROTO(struct cdnsp_ep_ctx *ctx),
|
||||
TP_ARGS(ctx)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_ep_ctx, cdnsp_ep_stopped_or_disabled,
|
||||
TP_PROTO(struct cdnsp_ep_ctx *ctx),
|
||||
TP_ARGS(ctx)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_ep_ctx, cdnsp_remove_request,
|
||||
TP_PROTO(struct cdnsp_ep_ctx *ctx),
|
||||
TP_ARGS(ctx)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_ep_ctx, cdnsp_handle_cmd_stop_ep,
|
||||
TP_PROTO(struct cdnsp_ep_ctx *ctx),
|
||||
TP_ARGS(ctx)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_ep_ctx, cdnsp_handle_cmd_flush_ep,
|
||||
TP_PROTO(struct cdnsp_ep_ctx *ctx),
|
||||
TP_ARGS(ctx)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_ep_ctx, cdnsp_handle_cmd_set_deq_ep,
|
||||
TP_PROTO(struct cdnsp_ep_ctx *ctx),
|
||||
TP_ARGS(ctx)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_ep_ctx, cdnsp_handle_cmd_reset_ep,
|
||||
TP_PROTO(struct cdnsp_ep_ctx *ctx),
|
||||
TP_ARGS(ctx)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_ep_ctx, cdnsp_handle_cmd_config_ep,
|
||||
TP_PROTO(struct cdnsp_ep_ctx *ctx),
|
||||
TP_ARGS(ctx)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(cdnsp_log_slot_ctx,
|
||||
TP_PROTO(struct cdnsp_slot_ctx *ctx),
|
||||
TP_ARGS(ctx),
|
||||
TP_STRUCT__entry(
|
||||
__field(u32, info)
|
||||
__field(u32, info2)
|
||||
__field(u32, int_target)
|
||||
__field(u32, state)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->info = le32_to_cpu(ctx->dev_info);
|
||||
__entry->info2 = le32_to_cpu(ctx->dev_port);
|
||||
__entry->int_target = le32_to_cpu(ctx->int_target);
|
||||
__entry->state = le32_to_cpu(ctx->dev_state);
|
||||
),
|
||||
TP_printk("%s", cdnsp_decode_slot_context(__entry->info,
|
||||
__entry->info2,
|
||||
__entry->int_target,
|
||||
__entry->state)
|
||||
)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_slot_ctx, cdnsp_slot_already_in_default,
|
||||
TP_PROTO(struct cdnsp_slot_ctx *ctx),
|
||||
TP_ARGS(ctx)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_slot_ctx, cdnsp_handle_cmd_enable_slot,
|
||||
TP_PROTO(struct cdnsp_slot_ctx *ctx),
|
||||
TP_ARGS(ctx)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_slot_ctx, cdnsp_handle_cmd_disable_slot,
|
||||
TP_PROTO(struct cdnsp_slot_ctx *ctx),
|
||||
TP_ARGS(ctx)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_slot_ctx, cdnsp_reset_device,
|
||||
TP_PROTO(struct cdnsp_slot_ctx *ctx),
|
||||
TP_ARGS(ctx)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_slot_ctx, cdnsp_setup_device_slot,
|
||||
TP_PROTO(struct cdnsp_slot_ctx *ctx),
|
||||
TP_ARGS(ctx)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_slot_ctx, cdnsp_handle_cmd_addr_dev,
|
||||
TP_PROTO(struct cdnsp_slot_ctx *ctx),
|
||||
TP_ARGS(ctx)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_slot_ctx, cdnsp_handle_cmd_reset_dev,
|
||||
TP_PROTO(struct cdnsp_slot_ctx *ctx),
|
||||
TP_ARGS(ctx)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_slot_ctx, cdnsp_handle_cmd_set_deq,
|
||||
TP_PROTO(struct cdnsp_slot_ctx *ctx),
|
||||
TP_ARGS(ctx)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_slot_ctx, cdnsp_configure_endpoint,
|
||||
TP_PROTO(struct cdnsp_slot_ctx *ctx),
|
||||
TP_ARGS(ctx)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(cdnsp_log_td_info,
|
||||
TP_PROTO(struct cdnsp_request *preq),
|
||||
TP_ARGS(preq),
|
||||
TP_STRUCT__entry(
|
||||
__string(name, preq->pep->name)
|
||||
__field(struct usb_request *, request)
|
||||
__field(struct cdnsp_request *, preq)
|
||||
__field(union cdnsp_trb *, first_trb)
|
||||
__field(union cdnsp_trb *, last_trb)
|
||||
__field(dma_addr_t, trb_dma)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__assign_str(name, preq->pep->name);
|
||||
__entry->request = &preq->request;
|
||||
__entry->preq = preq;
|
||||
__entry->first_trb = preq->td.first_trb;
|
||||
__entry->last_trb = preq->td.last_trb;
|
||||
__entry->trb_dma = cdnsp_trb_virt_to_dma(preq->td.start_seg,
|
||||
preq->td.first_trb)
|
||||
),
|
||||
TP_printk("%s req/preq: %p/%p, first trb %p[vir]/%pad(dma), last trb %p",
|
||||
__get_str(name), __entry->request, __entry->preq,
|
||||
__entry->first_trb, &__entry->trb_dma,
|
||||
__entry->last_trb
|
||||
)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_td_info, cdnsp_remove_request_td,
|
||||
TP_PROTO(struct cdnsp_request *preq),
|
||||
TP_ARGS(preq)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(cdnsp_log_ring,
|
||||
TP_PROTO(struct cdnsp_ring *ring),
|
||||
TP_ARGS(ring),
|
||||
TP_STRUCT__entry(
|
||||
__field(u32, type)
|
||||
__field(void *, ring)
|
||||
__field(dma_addr_t, enq)
|
||||
__field(dma_addr_t, deq)
|
||||
__field(dma_addr_t, enq_seg)
|
||||
__field(dma_addr_t, deq_seg)
|
||||
__field(unsigned int, num_segs)
|
||||
__field(unsigned int, stream_id)
|
||||
__field(unsigned int, cycle_state)
|
||||
__field(unsigned int, num_trbs_free)
|
||||
__field(unsigned int, bounce_buf_len)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->ring = ring;
|
||||
__entry->type = ring->type;
|
||||
__entry->num_segs = ring->num_segs;
|
||||
__entry->stream_id = ring->stream_id;
|
||||
__entry->enq_seg = ring->enq_seg->dma;
|
||||
__entry->deq_seg = ring->deq_seg->dma;
|
||||
__entry->cycle_state = ring->cycle_state;
|
||||
__entry->num_trbs_free = ring->num_trbs_free;
|
||||
__entry->bounce_buf_len = ring->bounce_buf_len;
|
||||
__entry->enq = cdnsp_trb_virt_to_dma(ring->enq_seg,
|
||||
ring->enqueue);
|
||||
__entry->deq = cdnsp_trb_virt_to_dma(ring->deq_seg,
|
||||
ring->dequeue);
|
||||
),
|
||||
TP_printk("%s %p: enq %pad(%pad) deq %pad(%pad) segs %d stream %d"
|
||||
" free_trbs %d bounce %d cycle %d",
|
||||
cdnsp_ring_type_string(__entry->type), __entry->ring,
|
||||
&__entry->enq, &__entry->enq_seg,
|
||||
&__entry->deq, &__entry->deq_seg,
|
||||
__entry->num_segs,
|
||||
__entry->stream_id,
|
||||
__entry->num_trbs_free,
|
||||
__entry->bounce_buf_len,
|
||||
__entry->cycle_state
|
||||
)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_ring, cdnsp_ring_alloc,
|
||||
TP_PROTO(struct cdnsp_ring *ring),
|
||||
TP_ARGS(ring)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_ring, cdnsp_ring_free,
|
||||
TP_PROTO(struct cdnsp_ring *ring),
|
||||
TP_ARGS(ring)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_ring, cdnsp_set_stream_ring,
|
||||
TP_PROTO(struct cdnsp_ring *ring),
|
||||
TP_ARGS(ring)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_ring, cdnsp_ring_expansion,
|
||||
TP_PROTO(struct cdnsp_ring *ring),
|
||||
TP_ARGS(ring)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_ring, cdnsp_inc_enq,
|
||||
TP_PROTO(struct cdnsp_ring *ring),
|
||||
TP_ARGS(ring)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_ring, cdnsp_inc_deq,
|
||||
TP_PROTO(struct cdnsp_ring *ring),
|
||||
TP_ARGS(ring)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(cdnsp_log_portsc,
|
||||
TP_PROTO(u32 portnum, u32 portsc),
|
||||
TP_ARGS(portnum, portsc),
|
||||
TP_STRUCT__entry(
|
||||
__field(u32, portnum)
|
||||
__field(u32, portsc)
|
||||
__dynamic_array(char, str, CDNSP_MSG_MAX)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->portnum = portnum;
|
||||
__entry->portsc = portsc;
|
||||
),
|
||||
TP_printk("port-%d: %s",
|
||||
__entry->portnum,
|
||||
cdnsp_decode_portsc(__get_str(str), CDNSP_MSG_MAX,
|
||||
__entry->portsc)
|
||||
)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_portsc, cdnsp_handle_port_status,
|
||||
TP_PROTO(u32 portnum, u32 portsc),
|
||||
TP_ARGS(portnum, portsc)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cdnsp_log_portsc, cdnsp_link_state_changed,
|
||||
TP_PROTO(u32 portnum, u32 portsc),
|
||||
TP_ARGS(portnum, portsc)
|
||||
);
|
||||
|
||||
TRACE_EVENT(cdnsp_stream_number,
|
||||
TP_PROTO(struct cdnsp_ep *pep, int num_stream_ctxs, int num_streams),
|
||||
TP_ARGS(pep, num_stream_ctxs, num_streams),
|
||||
TP_STRUCT__entry(
|
||||
__string(name, pep->name)
|
||||
__field(int, num_stream_ctxs)
|
||||
__field(int, num_streams)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->num_stream_ctxs = num_stream_ctxs;
|
||||
__entry->num_streams = num_streams;
|
||||
),
|
||||
TP_printk("%s Need %u stream ctx entries for %u stream IDs.",
|
||||
__get_str(name), __entry->num_stream_ctxs,
|
||||
__entry->num_streams)
|
||||
);
|
||||
|
||||
#endif /* __CDNSP_TRACE_H */
|
||||
|
||||
/* this part must be outside header guard */
|
||||
|
||||
#undef TRACE_INCLUDE_PATH
|
||||
#define TRACE_INCLUDE_PATH .
|
||||
|
||||
#undef TRACE_INCLUDE_FILE
|
||||
#define TRACE_INCLUDE_FILE cdnsp-trace
|
||||
|
||||
#include <trace/define_trace.h>
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Cadence USBSS DRD Driver.
|
||||
* Cadence USBSS and USBSSP DRD Driver.
|
||||
*
|
||||
* Copyright (C) 2018-2019 Cadence.
|
||||
* Copyright (C) 2017-2018 NXP
|
||||
|
@ -19,15 +19,13 @@
|
|||
#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 int cdns_idle_init(struct cdns *cdns);
|
||||
|
||||
static int cdns3_role_start(struct cdns3 *cdns, enum usb_role role)
|
||||
static int cdns_role_start(struct cdns *cdns, enum usb_role role)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
@ -41,47 +39,47 @@ static int cdns3_role_start(struct cdns3 *cdns, enum usb_role role)
|
|||
if (!cdns->roles[role])
|
||||
return -ENXIO;
|
||||
|
||||
if (cdns->roles[role]->state == CDNS3_ROLE_STATE_ACTIVE)
|
||||
if (cdns->roles[role]->state == CDNS_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;
|
||||
cdns->roles[role]->state = CDNS_ROLE_STATE_ACTIVE;
|
||||
mutex_unlock(&cdns->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void cdns3_role_stop(struct cdns3 *cdns)
|
||||
static void cdns_role_stop(struct cdns *cdns)
|
||||
{
|
||||
enum usb_role role = cdns->role;
|
||||
|
||||
if (WARN_ON(role > USB_ROLE_DEVICE))
|
||||
return;
|
||||
|
||||
if (cdns->roles[role]->state == CDNS3_ROLE_STATE_INACTIVE)
|
||||
if (cdns->roles[role]->state == CDNS_ROLE_STATE_INACTIVE)
|
||||
return;
|
||||
|
||||
mutex_lock(&cdns->mutex);
|
||||
cdns->roles[role]->stop(cdns);
|
||||
cdns->roles[role]->state = CDNS3_ROLE_STATE_INACTIVE;
|
||||
cdns->roles[role]->state = CDNS_ROLE_STATE_INACTIVE;
|
||||
mutex_unlock(&cdns->mutex);
|
||||
}
|
||||
|
||||
static void cdns3_exit_roles(struct cdns3 *cdns)
|
||||
static void cdns_exit_roles(struct cdns *cdns)
|
||||
{
|
||||
cdns3_role_stop(cdns);
|
||||
cdns3_drd_exit(cdns);
|
||||
cdns_role_stop(cdns);
|
||||
cdns_drd_exit(cdns);
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_core_init_role - initialize role of operation
|
||||
* @cdns: Pointer to cdns3 structure
|
||||
* cdns_core_init_role - initialize role of operation
|
||||
* @cdns: Pointer to cdns structure
|
||||
*
|
||||
* Returns 0 on success otherwise negative errno
|
||||
*/
|
||||
static int cdns3_core_init_role(struct cdns3 *cdns)
|
||||
static int cdns_core_init_role(struct cdns *cdns)
|
||||
{
|
||||
struct device *dev = cdns->dev;
|
||||
enum usb_dr_mode best_dr_mode;
|
||||
|
@ -97,13 +95,23 @@ static int cdns3_core_init_role(struct cdns3 *cdns)
|
|||
* 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;
|
||||
if (cdns->version == CDNSP_CONTROLLER_V2) {
|
||||
if (IS_ENABLED(CONFIG_USB_CDNSP_HOST) &&
|
||||
IS_ENABLED(CONFIG_USB_CDNSP_GADGET))
|
||||
dr_mode = USB_DR_MODE_OTG;
|
||||
else if (IS_ENABLED(CONFIG_USB_CDNSP_HOST))
|
||||
dr_mode = USB_DR_MODE_HOST;
|
||||
else if (IS_ENABLED(CONFIG_USB_CDNSP_GADGET))
|
||||
dr_mode = USB_DR_MODE_PERIPHERAL;
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -112,7 +120,7 @@ static int cdns3_core_init_role(struct cdns3 *cdns)
|
|||
*/
|
||||
best_dr_mode = cdns->dr_mode;
|
||||
|
||||
ret = cdns3_idle_init(cdns);
|
||||
ret = cdns_idle_init(cdns);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -128,7 +136,14 @@ static int cdns3_core_init_role(struct cdns3 *cdns)
|
|||
dr_mode = best_dr_mode;
|
||||
|
||||
if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) {
|
||||
ret = cdns3_host_init(cdns);
|
||||
if ((cdns->version == CDNSP_CONTROLLER_V2 &&
|
||||
IS_ENABLED(CONFIG_USB_CDNSP_HOST)) ||
|
||||
(cdns->version < CDNSP_CONTROLLER_V2 &&
|
||||
IS_ENABLED(CONFIG_USB_CDNS3_HOST)))
|
||||
ret = cdns_host_init(cdns);
|
||||
else
|
||||
ret = -ENXIO;
|
||||
|
||||
if (ret) {
|
||||
dev_err(dev, "Host initialization failed with %d\n",
|
||||
ret);
|
||||
|
@ -137,7 +152,11 @@ static int cdns3_core_init_role(struct cdns3 *cdns)
|
|||
}
|
||||
|
||||
if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_PERIPHERAL) {
|
||||
ret = cdns3_gadget_init(cdns);
|
||||
if (cdns->gadget_init)
|
||||
ret = cdns->gadget_init(cdns);
|
||||
else
|
||||
ret = -ENXIO;
|
||||
|
||||
if (ret) {
|
||||
dev_err(dev, "Device initialization failed with %d\n",
|
||||
ret);
|
||||
|
@ -147,28 +166,28 @@ static int cdns3_core_init_role(struct cdns3 *cdns)
|
|||
|
||||
cdns->dr_mode = dr_mode;
|
||||
|
||||
ret = cdns3_drd_update_mode(cdns);
|
||||
ret = cdns_drd_update_mode(cdns);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
/* Initialize idle role to start with */
|
||||
ret = cdns3_role_start(cdns, USB_ROLE_NONE);
|
||||
ret = cdns_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);
|
||||
ret = cdns_hw_role_switch(cdns);
|
||||
if (ret)
|
||||
goto err;
|
||||
break;
|
||||
case USB_DR_MODE_PERIPHERAL:
|
||||
ret = cdns3_role_start(cdns, USB_ROLE_DEVICE);
|
||||
ret = cdns_role_start(cdns, USB_ROLE_DEVICE);
|
||||
if (ret)
|
||||
goto err;
|
||||
break;
|
||||
case USB_DR_MODE_HOST:
|
||||
ret = cdns3_role_start(cdns, USB_ROLE_HOST);
|
||||
ret = cdns_role_start(cdns, USB_ROLE_HOST);
|
||||
if (ret)
|
||||
goto err;
|
||||
break;
|
||||
|
@ -179,32 +198,32 @@ static int cdns3_core_init_role(struct cdns3 *cdns)
|
|||
|
||||
return 0;
|
||||
err:
|
||||
cdns3_exit_roles(cdns);
|
||||
cdns_exit_roles(cdns);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_hw_role_state_machine - role switch state machine based on hw events.
|
||||
* cdns_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 cdns3_hw_role_state_machine(struct cdns3 *cdns)
|
||||
static enum usb_role cdns_hw_role_state_machine(struct cdns *cdns)
|
||||
{
|
||||
enum usb_role role = USB_ROLE_NONE;
|
||||
int id, vbus;
|
||||
|
||||
if (cdns->dr_mode != USB_DR_MODE_OTG) {
|
||||
if (cdns3_is_host(cdns))
|
||||
if (cdns_is_host(cdns))
|
||||
role = USB_ROLE_HOST;
|
||||
if (cdns3_is_device(cdns))
|
||||
if (cdns_is_device(cdns))
|
||||
role = USB_ROLE_DEVICE;
|
||||
|
||||
return role;
|
||||
}
|
||||
|
||||
id = cdns3_get_id(cdns);
|
||||
vbus = cdns3_get_vbus(cdns);
|
||||
id = cdns_get_id(cdns);
|
||||
vbus = cdns_get_vbus(cdns);
|
||||
|
||||
/*
|
||||
* Role change state machine
|
||||
|
@ -240,28 +259,28 @@ static enum usb_role cdns3_hw_role_state_machine(struct cdns3 *cdns)
|
|||
return role;
|
||||
}
|
||||
|
||||
static int cdns3_idle_role_start(struct cdns3 *cdns)
|
||||
static int cdns_idle_role_start(struct cdns *cdns)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cdns3_idle_role_stop(struct cdns3 *cdns)
|
||||
static void cdns_idle_role_stop(struct cdns *cdns)
|
||||
{
|
||||
/* Program Lane swap and bring PHY out of RESET */
|
||||
phy_reset(cdns->usb3_phy);
|
||||
}
|
||||
|
||||
static int cdns3_idle_init(struct cdns3 *cdns)
|
||||
static int cdns_idle_init(struct cdns *cdns)
|
||||
{
|
||||
struct cdns3_role_driver *rdrv;
|
||||
struct cdns_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->start = cdns_idle_role_start;
|
||||
rdrv->stop = cdns_idle_role_stop;
|
||||
rdrv->state = CDNS_ROLE_STATE_INACTIVE;
|
||||
rdrv->suspend = NULL;
|
||||
rdrv->resume = NULL;
|
||||
rdrv->name = "idle";
|
||||
|
@ -272,10 +291,10 @@ static int cdns3_idle_init(struct cdns3 *cdns)
|
|||
}
|
||||
|
||||
/**
|
||||
* cdns3_hw_role_switch - switch roles based on HW state
|
||||
* cdns_hw_role_switch - switch roles based on HW state
|
||||
* @cdns: controller
|
||||
*/
|
||||
int cdns3_hw_role_switch(struct cdns3 *cdns)
|
||||
int cdns_hw_role_switch(struct cdns *cdns)
|
||||
{
|
||||
enum usb_role real_role, current_role;
|
||||
int ret = 0;
|
||||
|
@ -287,22 +306,22 @@ int cdns3_hw_role_switch(struct cdns3 *cdns)
|
|||
pm_runtime_get_sync(cdns->dev);
|
||||
|
||||
current_role = cdns->role;
|
||||
real_role = cdns3_hw_role_state_machine(cdns);
|
||||
real_role = cdns_hw_role_state_machine(cdns);
|
||||
|
||||
/* Do nothing if nothing changed */
|
||||
if (current_role == real_role)
|
||||
goto exit;
|
||||
|
||||
cdns3_role_stop(cdns);
|
||||
cdns_role_stop(cdns);
|
||||
|
||||
dev_dbg(cdns->dev, "Switching role %d -> %d", current_role, real_role);
|
||||
|
||||
ret = cdns3_role_start(cdns, real_role);
|
||||
ret = cdns_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);
|
||||
ret = cdns_role_start(cdns, current_role);
|
||||
if (ret)
|
||||
dev_err(cdns->dev, "back to %d failed too\n",
|
||||
current_role);
|
||||
|
@ -319,15 +338,15 @@ int cdns3_hw_role_switch(struct cdns3 *cdns)
|
|||
*
|
||||
* Returns role
|
||||
*/
|
||||
static enum usb_role cdns3_role_get(struct usb_role_switch *sw)
|
||||
static enum usb_role cdns_role_get(struct usb_role_switch *sw)
|
||||
{
|
||||
struct cdns3 *cdns = usb_role_switch_get_drvdata(sw);
|
||||
struct cdns *cdns = usb_role_switch_get_drvdata(sw);
|
||||
|
||||
return cdns->role;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_role_set - set current role of controller.
|
||||
* cdns_role_set - set current role of controller.
|
||||
*
|
||||
* @sw: pointer to USB role switch structure
|
||||
* @role: the previous role
|
||||
|
@ -335,9 +354,9 @@ static enum usb_role cdns3_role_get(struct usb_role_switch *sw)
|
|||
* - Role switch for dual-role devices
|
||||
* - USB_ROLE_GADGET <--> USB_ROLE_NONE for peripheral-only devices
|
||||
*/
|
||||
static int cdns3_role_set(struct usb_role_switch *sw, enum usb_role role)
|
||||
static int cdns_role_set(struct usb_role_switch *sw, enum usb_role role)
|
||||
{
|
||||
struct cdns3 *cdns = usb_role_switch_get_drvdata(sw);
|
||||
struct cdns *cdns = usb_role_switch_get_drvdata(sw);
|
||||
int ret = 0;
|
||||
|
||||
pm_runtime_get_sync(cdns->dev);
|
||||
|
@ -365,8 +384,8 @@ static int cdns3_role_set(struct usb_role_switch *sw, enum usb_role role)
|
|||
}
|
||||
}
|
||||
|
||||
cdns3_role_stop(cdns);
|
||||
ret = cdns3_role_start(cdns, role);
|
||||
cdns_role_stop(cdns);
|
||||
ret = cdns_role_start(cdns, role);
|
||||
if (ret)
|
||||
dev_err(cdns->dev, "set role %d has failed\n", role);
|
||||
|
||||
|
@ -375,37 +394,17 @@ static int cdns3_role_set(struct usb_role_switch *sw, enum usb_role role)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int set_phy_power_on(struct cdns3 *cdns)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = phy_power_on(cdns->usb2_phy);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = phy_power_on(cdns->usb3_phy);
|
||||
if (ret)
|
||||
phy_power_off(cdns->usb2_phy);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void set_phy_power_off(struct cdns3 *cdns)
|
||||
{
|
||||
phy_power_off(cdns->usb3_phy);
|
||||
phy_power_off(cdns->usb2_phy);
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_wakeup_irq - interrupt handler for wakeup events
|
||||
* @irq: irq number for cdns3 core device
|
||||
* @data: structure of cdns3
|
||||
* cdns_wakeup_irq - interrupt handler for wakeup events
|
||||
* @irq: irq number for cdns3/cdnsp core device
|
||||
* @data: structure of cdns
|
||||
*
|
||||
* Returns IRQ_HANDLED or IRQ_NONE
|
||||
*/
|
||||
static irqreturn_t cdns3_wakeup_irq(int irq, void *data)
|
||||
static irqreturn_t cdns_wakeup_irq(int irq, void *data)
|
||||
{
|
||||
struct cdns3 *cdns = data;
|
||||
struct cdns *cdns = data;
|
||||
|
||||
if (cdns->in_lpm) {
|
||||
disable_irq_nosync(irq);
|
||||
|
@ -420,17 +419,14 @@ static irqreturn_t cdns3_wakeup_irq(int irq, void *data)
|
|||
}
|
||||
|
||||
/**
|
||||
* cdns3_probe - probe for cdns3 core device
|
||||
* @pdev: Pointer to cdns3 core platform device
|
||||
* cdns_probe - probe for cdns3/cdnsp core device
|
||||
* @cdns: Pointer to cdns structure.
|
||||
*
|
||||
* Returns 0 on success otherwise negative errno
|
||||
*/
|
||||
static int cdns3_probe(struct platform_device *pdev)
|
||||
int cdns_init(struct cdns *cdns)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
struct cdns3 *cdns;
|
||||
void __iomem *regs;
|
||||
struct device *dev = cdns->dev;
|
||||
int ret;
|
||||
|
||||
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
|
||||
|
@ -439,259 +435,78 @@ static int cdns3_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
cdns = devm_kzalloc(dev, sizeof(*cdns), GFP_KERNEL);
|
||||
if (!cdns)
|
||||
return -ENOMEM;
|
||||
|
||||
cdns->dev = dev;
|
||||
cdns->pdata = dev_get_platdata(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 < 0)
|
||||
return cdns->dev_irq;
|
||||
|
||||
regs = devm_platform_ioremap_resource_byname(pdev, "dev");
|
||||
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 < 0)
|
||||
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->phyrst_a_enable = device_property_read_bool(dev, "cdns,phyrst-a-enable");
|
||||
|
||||
cdns->otg_res = *res;
|
||||
|
||||
cdns->wakeup_irq = platform_get_irq_byname_optional(pdev, "wakeup");
|
||||
if (cdns->wakeup_irq == -EPROBE_DEFER)
|
||||
return cdns->wakeup_irq;
|
||||
else if (cdns->wakeup_irq == 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (cdns->wakeup_irq < 0) {
|
||||
dev_dbg(dev, "couldn't get wakeup irq\n");
|
||||
cdns->wakeup_irq = 0x0;
|
||||
}
|
||||
|
||||
mutex_init(&cdns->mutex);
|
||||
|
||||
cdns->usb2_phy = devm_phy_optional_get(dev, "cdns3,usb2-phy");
|
||||
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 = set_phy_power_on(cdns);
|
||||
if (ret)
|
||||
goto err2;
|
||||
|
||||
if (device_property_read_bool(dev, "usb-role-switch")) {
|
||||
struct usb_role_switch_desc sw_desc = { };
|
||||
|
||||
sw_desc.set = cdns3_role_set;
|
||||
sw_desc.get = cdns3_role_get;
|
||||
sw_desc.set = cdns_role_set;
|
||||
sw_desc.get = cdns_role_get;
|
||||
sw_desc.allow_userspace_control = true;
|
||||
sw_desc.driver_data = cdns;
|
||||
sw_desc.fwnode = dev->fwnode;
|
||||
|
||||
cdns->role_sw = usb_role_switch_register(dev, &sw_desc);
|
||||
if (IS_ERR(cdns->role_sw)) {
|
||||
ret = PTR_ERR(cdns->role_sw);
|
||||
dev_warn(dev, "Unable to register Role Switch\n");
|
||||
goto err3;
|
||||
return PTR_ERR(cdns->role_sw);
|
||||
}
|
||||
}
|
||||
|
||||
if (cdns->wakeup_irq) {
|
||||
ret = devm_request_irq(cdns->dev, cdns->wakeup_irq,
|
||||
cdns3_wakeup_irq,
|
||||
cdns_wakeup_irq,
|
||||
IRQF_SHARED,
|
||||
dev_name(cdns->dev), cdns);
|
||||
|
||||
if (ret) {
|
||||
dev_err(cdns->dev, "couldn't register wakeup irq handler\n");
|
||||
goto err4;
|
||||
goto role_switch_unregister;
|
||||
}
|
||||
}
|
||||
|
||||
ret = cdns3_drd_init(cdns);
|
||||
ret = cdns_drd_init(cdns);
|
||||
if (ret)
|
||||
goto err4;
|
||||
goto init_failed;
|
||||
|
||||
ret = cdns3_core_init_role(cdns);
|
||||
ret = cdns_core_init_role(cdns);
|
||||
if (ret)
|
||||
goto err4;
|
||||
goto init_failed;
|
||||
|
||||
spin_lock_init(&cdns->lock);
|
||||
device_set_wakeup_capable(dev, true);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
if (!(cdns->pdata && (cdns->pdata->quirks & CDNS3_DEFAULT_PM_RUNTIME_ALLOW)))
|
||||
pm_runtime_forbid(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;
|
||||
err4:
|
||||
cdns3_drd_exit(cdns);
|
||||
init_failed:
|
||||
cdns_drd_exit(cdns);
|
||||
role_switch_unregister:
|
||||
if (cdns->role_sw)
|
||||
usb_role_switch_unregister(cdns->role_sw);
|
||||
err3:
|
||||
set_phy_power_off(cdns);
|
||||
err2:
|
||||
phy_exit(cdns->usb3_phy);
|
||||
err1:
|
||||
phy_exit(cdns->usb2_phy);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cdns_init);
|
||||
|
||||
/**
|
||||
* cdns3_remove - unbind drd driver and clean up
|
||||
* @pdev: Pointer to Linux platform device
|
||||
* cdns_remove - unbind drd driver and clean up
|
||||
* @cdns: Pointer to cdns structure.
|
||||
*
|
||||
* Returns 0 on success otherwise negative errno
|
||||
*/
|
||||
static int cdns3_remove(struct platform_device *pdev)
|
||||
int cdns_remove(struct cdns *cdns)
|
||||
{
|
||||
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);
|
||||
cdns_exit_roles(cdns);
|
||||
usb_role_switch_unregister(cdns->role_sw);
|
||||
set_phy_power_off(cdns);
|
||||
phy_exit(cdns->usb2_phy);
|
||||
phy_exit(cdns->usb3_phy);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static int cdns3_set_platform_suspend(struct device *dev,
|
||||
bool suspend, bool wakeup)
|
||||
{
|
||||
struct cdns3 *cdns = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (cdns->pdata && cdns->pdata->platform_suspend)
|
||||
ret = cdns->pdata->platform_suspend(dev, suspend, wakeup);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cdns3_controller_suspend(struct device *dev, pm_message_t msg)
|
||||
{
|
||||
struct cdns3 *cdns = dev_get_drvdata(dev);
|
||||
bool wakeup;
|
||||
unsigned long flags;
|
||||
|
||||
if (cdns->in_lpm)
|
||||
return 0;
|
||||
|
||||
if (PMSG_IS_AUTO(msg))
|
||||
wakeup = true;
|
||||
else
|
||||
wakeup = device_may_wakeup(dev);
|
||||
|
||||
cdns3_set_platform_suspend(cdns->dev, true, wakeup);
|
||||
set_phy_power_off(cdns);
|
||||
spin_lock_irqsave(&cdns->lock, flags);
|
||||
cdns->in_lpm = true;
|
||||
spin_unlock_irqrestore(&cdns->lock, flags);
|
||||
dev_dbg(cdns->dev, "%s ends\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cdns_remove);
|
||||
|
||||
static int cdns3_controller_resume(struct device *dev, pm_message_t msg)
|
||||
{
|
||||
struct cdns3 *cdns = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
|
||||
if (!cdns->in_lpm)
|
||||
return 0;
|
||||
|
||||
ret = set_phy_power_on(cdns);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
cdns3_set_platform_suspend(cdns->dev, false, false);
|
||||
|
||||
spin_lock_irqsave(&cdns->lock, flags);
|
||||
if (cdns->roles[cdns->role]->resume && !PMSG_IS_AUTO(msg))
|
||||
cdns->roles[cdns->role]->resume(cdns, false);
|
||||
|
||||
cdns->in_lpm = false;
|
||||
spin_unlock_irqrestore(&cdns->lock, flags);
|
||||
if (cdns->wakeup_pending) {
|
||||
cdns->wakeup_pending = false;
|
||||
enable_irq(cdns->wakeup_irq);
|
||||
}
|
||||
dev_dbg(cdns->dev, "%s ends\n", __func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cdns3_runtime_suspend(struct device *dev)
|
||||
{
|
||||
return cdns3_controller_suspend(dev, PMSG_AUTO_SUSPEND);
|
||||
}
|
||||
|
||||
static int cdns3_runtime_resume(struct device *dev)
|
||||
{
|
||||
return cdns3_controller_resume(dev, PMSG_AUTO_RESUME);
|
||||
}
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
||||
static int cdns3_suspend(struct device *dev)
|
||||
int cdns_suspend(struct cdns *cdns)
|
||||
{
|
||||
struct cdns3 *cdns = dev_get_drvdata(dev);
|
||||
struct device *dev = cdns->dev;
|
||||
unsigned long flags;
|
||||
|
||||
if (pm_runtime_status_suspended(dev))
|
||||
|
@ -703,52 +518,30 @@ static int cdns3_suspend(struct device *dev)
|
|||
spin_unlock_irqrestore(&cdns->lock, flags);
|
||||
}
|
||||
|
||||
return cdns3_controller_suspend(dev, PMSG_SUSPEND);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cdns_suspend);
|
||||
|
||||
static int cdns3_resume(struct device *dev)
|
||||
int cdns_resume(struct cdns *cdns, u8 set_active)
|
||||
{
|
||||
int ret;
|
||||
struct device *dev = cdns->dev;
|
||||
|
||||
ret = cdns3_controller_resume(dev, PMSG_RESUME);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (cdns->roles[cdns->role]->resume)
|
||||
cdns->roles[cdns->role]->resume(cdns, false);
|
||||
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
if (set_active) {
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cdns_resume);
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static const struct dev_pm_ops cdns3_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(cdns3_suspend, cdns3_resume)
|
||||
SET_RUNTIME_PM_OPS(cdns3_runtime_suspend, cdns3_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
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("Peter Chen <peter.chen@nxp.com>");
|
||||
MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("Cadence USB3 DRD Controller Driver");
|
||||
MODULE_AUTHOR("Roger Quadros <rogerq@ti.com>");
|
||||
MODULE_DESCRIPTION("Cadence USBSS and USBSSP DRD Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Cadence USBSS DRD Header File.
|
||||
* Cadence USBSS and USBSSP DRD Header File.
|
||||
*
|
||||
* Copyright (C) 2017-2018 NXP
|
||||
* Copyright (C) 2018-2019 Cadence.
|
||||
|
@ -14,10 +14,10 @@
|
|||
#ifndef __LINUX_CDNS3_CORE_H
|
||||
#define __LINUX_CDNS3_CORE_H
|
||||
|
||||
struct cdns3;
|
||||
struct cdns;
|
||||
|
||||
/**
|
||||
* struct cdns3_role_driver - host/gadget role driver
|
||||
* struct cdns_role_driver - host/gadget role driver
|
||||
* @start: start this role
|
||||
* @stop: stop this role
|
||||
* @suspend: suspend callback for this role
|
||||
|
@ -26,18 +26,18 @@ struct cdns3;
|
|||
* @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);
|
||||
struct cdns_role_driver {
|
||||
int (*start)(struct cdns *cdns);
|
||||
void (*stop)(struct cdns *cdns);
|
||||
int (*suspend)(struct cdns *cdns, bool do_wakeup);
|
||||
int (*resume)(struct cdns *cdns, bool hibernated);
|
||||
const char *name;
|
||||
#define CDNS3_ROLE_STATE_INACTIVE 0
|
||||
#define CDNS3_ROLE_STATE_ACTIVE 1
|
||||
#define CDNS_ROLE_STATE_INACTIVE 0
|
||||
#define CDNS_ROLE_STATE_ACTIVE 1
|
||||
int state;
|
||||
};
|
||||
|
||||
#define CDNS3_XHCI_RESOURCES_NUM 2
|
||||
#define CDNS_XHCI_RESOURCES_NUM 2
|
||||
|
||||
struct cdns3_platform_data {
|
||||
int (*platform_suspend)(struct device *dev,
|
||||
|
@ -47,7 +47,7 @@ struct cdns3_platform_data {
|
|||
};
|
||||
|
||||
/**
|
||||
* struct cdns3 - Representation of Cadence USB3 DRD controller.
|
||||
* struct cdns - 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
|
||||
|
@ -55,14 +55,16 @@ struct cdns3_platform_data {
|
|||
* @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_cdnsp_regs: pointer to base of CDNSP otg registers
|
||||
* @otg_regs: pointer to base of otg registers
|
||||
* @otg_irq_regs: pointer to interrupt registers
|
||||
* @otg_irq: irq number for otg controller
|
||||
* @dev_irq: irq number for device controller
|
||||
* @wakeup_irq: irq number for wakeup event, it is optional
|
||||
* @roles: array of supported roles for this controller
|
||||
* @role: current role
|
||||
* @host_dev: the child host device pointer for cdns3 core
|
||||
* @gadget_dev: the child gadget device pointer for cdns3 core
|
||||
* @host_dev: the child host device pointer for cdns core
|
||||
* @gadget_dev: the child gadget device pointer
|
||||
* @usb2_phy: pointer to USB2 PHY
|
||||
* @usb3_phy: pointer to USB3 PHY
|
||||
* @mutex: the mutex for concurrent code at driver
|
||||
|
@ -76,29 +78,33 @@ struct cdns3_platform_data {
|
|||
* @pdata: platform data from glue layer
|
||||
* @lock: spinlock structure
|
||||
* @xhci_plat_data: xhci private data structure pointer
|
||||
* @gadget_init: pointer to gadget initialization function
|
||||
*/
|
||||
struct cdns3 {
|
||||
struct cdns {
|
||||
struct device *dev;
|
||||
void __iomem *xhci_regs;
|
||||
struct resource xhci_res[CDNS3_XHCI_RESOURCES_NUM];
|
||||
struct resource xhci_res[CDNS_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;
|
||||
struct resource otg_res;
|
||||
struct cdns3_otg_legacy_regs __iomem *otg_v0_regs;
|
||||
struct cdns3_otg_regs __iomem *otg_v1_regs;
|
||||
struct cdnsp_otg_regs __iomem *otg_cdnsp_regs;
|
||||
struct cdns_otg_common_regs __iomem *otg_regs;
|
||||
struct cdns_otg_irq_regs __iomem *otg_irq_regs;
|
||||
#define CDNS3_CONTROLLER_V0 0
|
||||
#define CDNS3_CONTROLLER_V1 1
|
||||
#define CDNSP_CONTROLLER_V2 2
|
||||
u32 version;
|
||||
bool phyrst_a_enable;
|
||||
|
||||
int otg_irq;
|
||||
int dev_irq;
|
||||
int wakeup_irq;
|
||||
struct cdns3_role_driver *roles[USB_ROLE_DEVICE + 1];
|
||||
struct cdns_role_driver *roles[USB_ROLE_DEVICE + 1];
|
||||
enum usb_role role;
|
||||
struct platform_device *host_dev;
|
||||
struct cdns3_device *gadget_dev;
|
||||
void *gadget_dev;
|
||||
struct phy *usb2_phy;
|
||||
struct phy *usb3_phy;
|
||||
/* mutext used in workqueue*/
|
||||
|
@ -110,8 +116,21 @@ struct cdns3 {
|
|||
struct cdns3_platform_data *pdata;
|
||||
spinlock_t lock;
|
||||
struct xhci_plat_priv *xhci_plat_data;
|
||||
|
||||
int (*gadget_init)(struct cdns *cdns);
|
||||
};
|
||||
|
||||
int cdns3_hw_role_switch(struct cdns3 *cdns);
|
||||
int cdns_hw_role_switch(struct cdns *cdns);
|
||||
int cdns_init(struct cdns *cdns);
|
||||
int cdns_remove(struct cdns *cdns);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
int cdns_resume(struct cdns *cdns, u8 set_active);
|
||||
int cdns_suspend(struct cdns *cdns);
|
||||
#else /* CONFIG_PM_SLEEP */
|
||||
static inline int cdns_resume(struct cdns *cdns, u8 set_active)
|
||||
{ return 0; }
|
||||
static inline int cdns_suspend(struct cdns *cdns)
|
||||
{ return 0; }
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
#endif /* __LINUX_CDNS3_CORE_H */
|
||||
|
|
|
@ -1,35 +1,33 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Cadence USBSS DRD Driver.
|
||||
* Cadence USBSS and USBSSP DRD Driver.
|
||||
*
|
||||
* Copyright (C) 2018-2019 Cadence.
|
||||
* Copyright (C) 2018-2020 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 <linux/phy/phy.h>
|
||||
|
||||
#include "gadget.h"
|
||||
#include "drd.h"
|
||||
#include "core.h"
|
||||
|
||||
/**
|
||||
* cdns3_set_mode - change mode of OTG Core
|
||||
* cdns_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)
|
||||
static int cdns_set_mode(struct cdns *cdns, enum usb_dr_mode mode)
|
||||
{
|
||||
void __iomem *override_reg;
|
||||
u32 reg;
|
||||
|
||||
switch (mode) {
|
||||
|
@ -39,11 +37,24 @@ int cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode)
|
|||
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);
|
||||
|
||||
if (cdns->version == CDNSP_CONTROLLER_V2)
|
||||
override_reg = &cdns->otg_cdnsp_regs->override;
|
||||
else if (cdns->version == CDNS3_CONTROLLER_V1)
|
||||
override_reg = &cdns->otg_v1_regs->override;
|
||||
else
|
||||
override_reg = &cdns->otg_v0_regs->ctrl1;
|
||||
|
||||
reg = readl(override_reg);
|
||||
|
||||
if (cdns->version != CDNS3_CONTROLLER_V0)
|
||||
reg |= OVERRIDE_IDPULLUP;
|
||||
else
|
||||
reg |= OVERRIDE_IDPULLUP_V0;
|
||||
|
||||
writel(reg, override_reg);
|
||||
|
||||
if (cdns->version == CDNS3_CONTROLLER_V1) {
|
||||
/*
|
||||
* Enable work around feature built into the
|
||||
* controller to address issue with RX Sensitivity
|
||||
|
@ -55,10 +66,6 @@ int cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode)
|
|||
reg |= PHYRST_CFG_PHYRST_A_ENABLE;
|
||||
writel(reg, &cdns->otg_v1_regs->phyrst_cfg);
|
||||
}
|
||||
} else {
|
||||
reg = readl(&cdns->otg_v0_regs->ctrl1);
|
||||
reg |= OVERRIDE_IDPULLUP_V0;
|
||||
writel(reg, &cdns->otg_v0_regs->ctrl1);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -76,7 +83,7 @@ int cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int cdns3_get_id(struct cdns3 *cdns)
|
||||
int cdns_get_id(struct cdns *cdns)
|
||||
{
|
||||
int id;
|
||||
|
||||
|
@ -86,7 +93,7 @@ int cdns3_get_id(struct cdns3 *cdns)
|
|||
return id;
|
||||
}
|
||||
|
||||
int cdns3_get_vbus(struct cdns3 *cdns)
|
||||
int cdns_get_vbus(struct cdns *cdns)
|
||||
{
|
||||
int vbus;
|
||||
|
||||
|
@ -96,64 +103,95 @@ int cdns3_get_vbus(struct cdns3 *cdns)
|
|||
return vbus;
|
||||
}
|
||||
|
||||
bool cdns3_is_host(struct cdns3 *cdns)
|
||||
void cdns_clear_vbus(struct cdns *cdns)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
if (cdns->version != CDNSP_CONTROLLER_V2)
|
||||
return;
|
||||
|
||||
reg = readl(&cdns->otg_cdnsp_regs->override);
|
||||
reg |= OVERRIDE_SESS_VLD_SEL;
|
||||
writel(reg, &cdns->otg_cdnsp_regs->override);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cdns_clear_vbus);
|
||||
|
||||
void cdns_set_vbus(struct cdns *cdns)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
if (cdns->version != CDNSP_CONTROLLER_V2)
|
||||
return;
|
||||
|
||||
reg = readl(&cdns->otg_cdnsp_regs->override);
|
||||
reg &= ~OVERRIDE_SESS_VLD_SEL;
|
||||
writel(reg, &cdns->otg_cdnsp_regs->override);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cdns_set_vbus);
|
||||
|
||||
bool cdns_is_host(struct cdns *cdns)
|
||||
{
|
||||
if (cdns->dr_mode == USB_DR_MODE_HOST)
|
||||
return true;
|
||||
else if (cdns3_get_id(cdns) == CDNS3_ID_HOST)
|
||||
else if (cdns_get_id(cdns) == CDNS3_ID_HOST)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cdns3_is_device(struct cdns3 *cdns)
|
||||
bool cdns_is_device(struct cdns *cdns)
|
||||
{
|
||||
if (cdns->dr_mode == USB_DR_MODE_PERIPHERAL)
|
||||
return true;
|
||||
else if (cdns->dr_mode == USB_DR_MODE_OTG)
|
||||
if (cdns3_get_id(cdns) == CDNS3_ID_PERIPHERAL)
|
||||
if (cdns_get_id(cdns) == CDNS3_ID_PERIPHERAL)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_otg_disable_irq - Disable all OTG interrupts
|
||||
* cdns_otg_disable_irq - Disable all OTG interrupts
|
||||
* @cdns: Pointer to controller context structure
|
||||
*/
|
||||
static void cdns3_otg_disable_irq(struct cdns3 *cdns)
|
||||
static void cdns_otg_disable_irq(struct cdns *cdns)
|
||||
{
|
||||
writel(0, &cdns->otg_regs->ien);
|
||||
writel(0, &cdns->otg_irq_regs->ien);
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_otg_enable_irq - enable id and sess_valid interrupts
|
||||
* cdns_otg_enable_irq - enable id and sess_valid interrupts
|
||||
* @cdns: Pointer to controller context structure
|
||||
*/
|
||||
static void cdns3_otg_enable_irq(struct cdns3 *cdns)
|
||||
static void cdns_otg_enable_irq(struct cdns *cdns)
|
||||
{
|
||||
writel(OTGIEN_ID_CHANGE_INT | OTGIEN_VBUSVALID_RISE_INT |
|
||||
OTGIEN_VBUSVALID_FALL_INT, &cdns->otg_regs->ien);
|
||||
OTGIEN_VBUSVALID_FALL_INT, &cdns->otg_irq_regs->ien);
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_drd_host_on - start host.
|
||||
* cdns_drd_host_on - start host.
|
||||
* @cdns: Pointer to controller context structure.
|
||||
*
|
||||
* Returns 0 on success otherwise negative errno.
|
||||
*/
|
||||
int cdns3_drd_host_on(struct cdns3 *cdns)
|
||||
int cdns_drd_host_on(struct cdns *cdns)
|
||||
{
|
||||
u32 val;
|
||||
u32 val, ready_bit;
|
||||
int ret;
|
||||
|
||||
/* Enable host mode. */
|
||||
writel(OTGCMD_HOST_BUS_REQ | OTGCMD_OTG_DIS,
|
||||
&cdns->otg_regs->cmd);
|
||||
|
||||
if (cdns->version == CDNSP_CONTROLLER_V2)
|
||||
ready_bit = OTGSTS_CDNSP_XHCI_READY;
|
||||
else
|
||||
ready_bit = OTGSTS_CDNS3_XHCI_READY;
|
||||
|
||||
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);
|
||||
val & ready_bit, 1, 100000);
|
||||
|
||||
if (ret)
|
||||
dev_err(cdns->dev, "timeout waiting for xhci_ready\n");
|
||||
|
@ -163,10 +201,10 @@ int cdns3_drd_host_on(struct cdns3 *cdns)
|
|||
}
|
||||
|
||||
/**
|
||||
* cdns3_drd_host_off - stop host.
|
||||
* cdns_drd_host_off - stop host.
|
||||
* @cdns: Pointer to controller context structure.
|
||||
*/
|
||||
void cdns3_drd_host_off(struct cdns3 *cdns)
|
||||
void cdns_drd_host_off(struct cdns *cdns)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
|
@ -182,24 +220,29 @@ void cdns3_drd_host_off(struct cdns3 *cdns)
|
|||
}
|
||||
|
||||
/**
|
||||
* cdns3_drd_gadget_on - start gadget.
|
||||
* cdns_drd_gadget_on - start gadget.
|
||||
* @cdns: Pointer to controller context structure.
|
||||
*
|
||||
* Returns 0 on success otherwise negative errno
|
||||
*/
|
||||
int cdns3_drd_gadget_on(struct cdns3 *cdns)
|
||||
int cdns_drd_gadget_on(struct cdns *cdns)
|
||||
{
|
||||
int ret, val;
|
||||
u32 reg = OTGCMD_OTG_DIS;
|
||||
u32 ready_bit;
|
||||
int ret, val;
|
||||
|
||||
/* switch OTG core */
|
||||
writel(OTGCMD_DEV_BUS_REQ | reg, &cdns->otg_regs->cmd);
|
||||
|
||||
dev_dbg(cdns->dev, "Waiting till Device mode is turned on\n");
|
||||
|
||||
if (cdns->version == CDNSP_CONTROLLER_V2)
|
||||
ready_bit = OTGSTS_CDNSP_DEV_READY;
|
||||
else
|
||||
ready_bit = OTGSTS_CDNS3_DEV_READY;
|
||||
|
||||
ret = readl_poll_timeout_atomic(&cdns->otg_regs->sts, val,
|
||||
val & OTGSTS_DEV_READY,
|
||||
1, 100000);
|
||||
val & ready_bit, 1, 100000);
|
||||
if (ret) {
|
||||
dev_err(cdns->dev, "timeout waiting for dev_ready\n");
|
||||
return ret;
|
||||
|
@ -208,12 +251,13 @@ int cdns3_drd_gadget_on(struct cdns3 *cdns)
|
|||
phy_set_mode(cdns->usb3_phy, PHY_MODE_USB_DEVICE);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cdns_drd_gadget_on);
|
||||
|
||||
/**
|
||||
* cdns3_drd_gadget_off - stop gadget.
|
||||
* cdns_drd_gadget_off - stop gadget.
|
||||
* @cdns: Pointer to controller context structure.
|
||||
*/
|
||||
void cdns3_drd_gadget_off(struct cdns3 *cdns)
|
||||
void cdns_drd_gadget_off(struct cdns *cdns)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
|
@ -231,49 +275,50 @@ void cdns3_drd_gadget_off(struct cdns3 *cdns)
|
|||
1, 2000000);
|
||||
phy_set_mode(cdns->usb3_phy, PHY_MODE_INVALID);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cdns_drd_gadget_off);
|
||||
|
||||
/**
|
||||
* cdns3_init_otg_mode - initialize drd controller
|
||||
* cdns_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)
|
||||
static int cdns_init_otg_mode(struct cdns *cdns)
|
||||
{
|
||||
int ret;
|
||||
|
||||
cdns3_otg_disable_irq(cdns);
|
||||
cdns_otg_disable_irq(cdns);
|
||||
/* clear all interrupts */
|
||||
writel(~0, &cdns->otg_regs->ivect);
|
||||
writel(~0, &cdns->otg_irq_regs->ivect);
|
||||
|
||||
ret = cdns3_set_mode(cdns, USB_DR_MODE_OTG);
|
||||
ret = cdns_set_mode(cdns, USB_DR_MODE_OTG);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
cdns3_otg_enable_irq(cdns);
|
||||
cdns_otg_enable_irq(cdns);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_drd_update_mode - initialize mode of operation
|
||||
* cdns_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 cdns_drd_update_mode(struct cdns *cdns)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (cdns->dr_mode) {
|
||||
case USB_DR_MODE_PERIPHERAL:
|
||||
ret = cdns3_set_mode(cdns, USB_DR_MODE_PERIPHERAL);
|
||||
ret = cdns_set_mode(cdns, USB_DR_MODE_PERIPHERAL);
|
||||
break;
|
||||
case USB_DR_MODE_HOST:
|
||||
ret = cdns3_set_mode(cdns, USB_DR_MODE_HOST);
|
||||
ret = cdns_set_mode(cdns, USB_DR_MODE_HOST);
|
||||
break;
|
||||
case USB_DR_MODE_OTG:
|
||||
ret = cdns3_init_otg_mode(cdns);
|
||||
ret = cdns_init_otg_mode(cdns);
|
||||
break;
|
||||
default:
|
||||
dev_err(cdns->dev, "Unsupported mode of operation %d\n",
|
||||
|
@ -284,27 +329,27 @@ int cdns3_drd_update_mode(struct cdns3 *cdns)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t cdns3_drd_thread_irq(int irq, void *data)
|
||||
static irqreturn_t cdns_drd_thread_irq(int irq, void *data)
|
||||
{
|
||||
struct cdns3 *cdns = data;
|
||||
struct cdns *cdns = data;
|
||||
|
||||
cdns3_hw_role_switch(cdns);
|
||||
cdns_hw_role_switch(cdns);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_drd_irq - interrupt handler for OTG events
|
||||
* cdns_drd_irq - interrupt handler for OTG events
|
||||
*
|
||||
* @irq: irq number for cdns3 core device
|
||||
* @data: structure of cdns3
|
||||
* @irq: irq number for cdns core device
|
||||
* @data: structure of cdns
|
||||
*
|
||||
* Returns IRQ_HANDLED or IRQ_NONE
|
||||
*/
|
||||
static irqreturn_t cdns3_drd_irq(int irq, void *data)
|
||||
static irqreturn_t cdns_drd_irq(int irq, void *data)
|
||||
{
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
struct cdns3 *cdns = data;
|
||||
struct cdns *cdns = data;
|
||||
u32 reg;
|
||||
|
||||
if (cdns->dr_mode != USB_DR_MODE_OTG)
|
||||
|
@ -313,30 +358,30 @@ static irqreturn_t cdns3_drd_irq(int irq, void *data)
|
|||
if (cdns->in_lpm)
|
||||
return ret;
|
||||
|
||||
reg = readl(&cdns->otg_regs->ivect);
|
||||
reg = readl(&cdns->otg_irq_regs->ivect);
|
||||
|
||||
if (!reg)
|
||||
return IRQ_NONE;
|
||||
|
||||
if (reg & OTGIEN_ID_CHANGE_INT) {
|
||||
dev_dbg(cdns->dev, "OTG IRQ: new ID: %d\n",
|
||||
cdns3_get_id(cdns));
|
||||
cdns_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));
|
||||
cdns_get_vbus(cdns));
|
||||
|
||||
ret = IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
writel(~0, &cdns->otg_regs->ivect);
|
||||
writel(~0, &cdns->otg_irq_regs->ivect);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cdns3_drd_init(struct cdns3 *cdns)
|
||||
int cdns_drd_init(struct cdns *cdns)
|
||||
{
|
||||
void __iomem *regs;
|
||||
u32 state;
|
||||
|
@ -347,28 +392,43 @@ int cdns3_drd_init(struct cdns3 *cdns)
|
|||
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
|
||||
* in three versions. All are very similar and are software compatible,
|
||||
* but they have same changes in register maps.
|
||||
* The first register in oldest version is command register and it's
|
||||
* read only. Driver should read 0 from it. On the other hand, in v1
|
||||
* and v2 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_cdnsp_regs = NULL;
|
||||
cdns->otg_regs = regs;
|
||||
cdns->otg_irq_regs = (struct cdns_otg_irq_regs __iomem *)
|
||||
&cdns->otg_v0_regs->ien;
|
||||
writel(1, &cdns->otg_v0_regs->simulate);
|
||||
dev_dbg(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);
|
||||
cdns->otg_cdnsp_regs = regs;
|
||||
|
||||
cdns->otg_regs = (void __iomem *)&cdns->otg_v1_regs->cmd;
|
||||
|
||||
if (readl(&cdns->otg_cdnsp_regs->did) == OTG_CDNSP_DID) {
|
||||
cdns->otg_irq_regs = (struct cdns_otg_irq_regs __iomem *)
|
||||
&cdns->otg_cdnsp_regs->ien;
|
||||
cdns->version = CDNSP_CONTROLLER_V2;
|
||||
} else {
|
||||
cdns->otg_irq_regs = (struct cdns_otg_irq_regs __iomem *)
|
||||
&cdns->otg_v1_regs->ien;
|
||||
writel(1, &cdns->otg_v1_regs->simulate);
|
||||
cdns->version = CDNS3_CONTROLLER_V1;
|
||||
}
|
||||
|
||||
dev_dbg(cdns->dev, "DRD version v1 (ID: %08x, rev: %08x)\n",
|
||||
readl(&cdns->otg_v1_regs->did),
|
||||
readl(&cdns->otg_v1_regs->rid));
|
||||
|
@ -378,17 +438,24 @@ int cdns3_drd_init(struct cdns3 *cdns)
|
|||
|
||||
/* Update dr_mode according to STRAP configuration. */
|
||||
cdns->dr_mode = USB_DR_MODE_OTG;
|
||||
if (state == OTGSTS_STRAP_HOST) {
|
||||
|
||||
if ((cdns->version == CDNSP_CONTROLLER_V2 &&
|
||||
state == OTGSTS_CDNSP_STRAP_HOST) ||
|
||||
(cdns->version != CDNSP_CONTROLLER_V2 &&
|
||||
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) {
|
||||
} else if ((cdns->version == CDNSP_CONTROLLER_V2 &&
|
||||
state == OTGSTS_CDNSP_STRAP_GADGET) ||
|
||||
(cdns->version != CDNSP_CONTROLLER_V2 &&
|
||||
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,
|
||||
cdns_drd_irq,
|
||||
cdns_drd_thread_irq,
|
||||
IRQF_SHARED,
|
||||
dev_name(cdns->dev), cdns);
|
||||
if (ret) {
|
||||
|
@ -405,8 +472,9 @@ int cdns3_drd_init(struct cdns3 *cdns)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int cdns3_drd_exit(struct cdns3 *cdns)
|
||||
int cdns_drd_exit(struct cdns *cdns)
|
||||
{
|
||||
cdns3_otg_disable_irq(cdns);
|
||||
cdns_otg_disable_irq(cdns);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Cadence USB3 DRD header file.
|
||||
* Cadence USB3 and USBSSP DRD header file.
|
||||
*
|
||||
* Copyright (C) 2018-2019 Cadence.
|
||||
* Copyright (C) 2018-2020 Cadence.
|
||||
*
|
||||
* Author: Pawel Laszczak <pawell@cadence.com>
|
||||
*/
|
||||
|
@ -10,10 +10,9 @@
|
|||
#define __LINUX_CDNS3_DRD
|
||||
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include "core.h"
|
||||
|
||||
/* DRD register interface for version v1. */
|
||||
/* DRD register interface for version v1 of cdns3 driver. */
|
||||
struct cdns3_otg_regs {
|
||||
__le32 did;
|
||||
__le32 rid;
|
||||
|
@ -38,7 +37,7 @@ struct cdns3_otg_regs {
|
|||
__le32 ctrl2;
|
||||
};
|
||||
|
||||
/* DRD register interface for version v0. */
|
||||
/* DRD register interface for version v0 of cdns3 driver. */
|
||||
struct cdns3_otg_legacy_regs {
|
||||
__le32 cmd;
|
||||
__le32 sts;
|
||||
|
@ -57,14 +56,45 @@ struct cdns3_otg_legacy_regs {
|
|||
__le32 ctrl1;
|
||||
};
|
||||
|
||||
/*
|
||||
* Common registers interface for both version of DRD.
|
||||
*/
|
||||
struct cdns3_otg_common_regs {
|
||||
/* DRD register interface for cdnsp driver */
|
||||
struct cdnsp_otg_regs {
|
||||
__le32 did;
|
||||
__le32 rid;
|
||||
__le32 cfgs1;
|
||||
__le32 cfgs2;
|
||||
__le32 cmd;
|
||||
__le32 sts;
|
||||
__le32 state;
|
||||
__le32 different1;
|
||||
__le32 ien;
|
||||
__le32 ivect;
|
||||
__le32 tmr;
|
||||
__le32 simulate;
|
||||
__le32 adpbc_sts;
|
||||
__le32 adp_ramp_time;
|
||||
__le32 adpbc_ctrl1;
|
||||
__le32 adpbc_ctrl2;
|
||||
__le32 override;
|
||||
__le32 vbusvalid_dbnc_cfg;
|
||||
__le32 sessvalid_dbnc_cfg;
|
||||
__le32 susp_timing_ctrl;
|
||||
};
|
||||
|
||||
#define OTG_CDNSP_DID 0x0004034E
|
||||
|
||||
/*
|
||||
* Common registers interface for both CDNS3 and CDNSP version of DRD.
|
||||
*/
|
||||
struct cdns_otg_common_regs {
|
||||
__le32 cmd;
|
||||
__le32 sts;
|
||||
__le32 state;
|
||||
};
|
||||
|
||||
/*
|
||||
* Interrupt related registers. This registers are mapped in different
|
||||
* location for CDNSP controller.
|
||||
*/
|
||||
struct cdns_otg_irq_regs {
|
||||
__le32 ien;
|
||||
__le32 ivect;
|
||||
};
|
||||
|
@ -92,9 +122,9 @@ struct cdns3_otg_common_regs {
|
|||
#define OTGCMD_DEV_BUS_DROP BIT(8)
|
||||
/* Drop the bus for Host mode*/
|
||||
#define OTGCMD_HOST_BUS_DROP BIT(9)
|
||||
/* Power Down USBSS-DEV. */
|
||||
/* Power Down USBSS-DEV - only for CDNS3.*/
|
||||
#define OTGCMD_DEV_POWER_OFF BIT(11)
|
||||
/* Power Down CDNSXHCI. */
|
||||
/* Power Down CDNSXHCI - only for CDNS3. */
|
||||
#define OTGCMD_HOST_POWER_OFF BIT(12)
|
||||
|
||||
/* OTGIEN - bitmasks */
|
||||
|
@ -123,20 +153,31 @@ struct cdns3_otg_common_regs {
|
|||
#define OTGSTS_OTG_NRDY_MASK BIT(11)
|
||||
#define OTGSTS_OTG_NRDY(p) ((p) & OTGSTS_OTG_NRDY_MASK)
|
||||
/*
|
||||
* Value of the strap pins.
|
||||
* Value of the strap pins for:
|
||||
* CDNS3:
|
||||
* 000 - no default configuration
|
||||
* 010 - Controller initiall configured as Host
|
||||
* 100 - Controller initially configured as Device
|
||||
* CDNSP:
|
||||
* 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
|
||||
#define OTGSTS_CDNSP_STRAP_HOST 0x01
|
||||
#define OTGSTS_CDNSP_STRAP_GADGET 0x02
|
||||
|
||||
/* Host mode is turned on. */
|
||||
#define OTGSTS_XHCI_READY BIT(26)
|
||||
#define OTGSTS_CDNS3_XHCI_READY BIT(26)
|
||||
#define OTGSTS_CDNSP_XHCI_READY BIT(27)
|
||||
|
||||
/* "Device mode is turned on .*/
|
||||
#define OTGSTS_DEV_READY BIT(27)
|
||||
#define OTGSTS_CDNS3_DEV_READY BIT(27)
|
||||
#define OTGSTS_CDNSP_DEV_READY BIT(26)
|
||||
|
||||
/* OTGSTATE- bitmasks */
|
||||
#define OTGSTATE_DEV_STATE_MASK GENMASK(2, 0)
|
||||
|
@ -152,6 +193,8 @@ struct cdns3_otg_common_regs {
|
|||
#define OVERRIDE_IDPULLUP BIT(0)
|
||||
/* Only for CDNS3_CONTROLLER_V0 version */
|
||||
#define OVERRIDE_IDPULLUP_V0 BIT(24)
|
||||
/* Vbusvalid/Sesvalid override select. */
|
||||
#define OVERRIDE_SESS_VLD_SEL BIT(10)
|
||||
|
||||
/* PHYRST_CFG - bitmasks */
|
||||
#define PHYRST_CFG_PHYRST_A_ENABLE BIT(0)
|
||||
|
@ -159,17 +202,18 @@ struct cdns3_otg_common_regs {
|
|||
#define CDNS3_ID_PERIPHERAL 1
|
||||
#define CDNS3_ID_HOST 0
|
||||
|
||||
bool cdns3_is_host(struct cdns3 *cdns);
|
||||
bool 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_gadget_on(struct cdns3 *cdns);
|
||||
void cdns3_drd_gadget_off(struct cdns3 *cdns);
|
||||
int cdns3_drd_host_on(struct cdns3 *cdns);
|
||||
void cdns3_drd_host_off(struct cdns3 *cdns);
|
||||
int cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode);
|
||||
bool cdns_is_host(struct cdns *cdns);
|
||||
bool cdns_is_device(struct cdns *cdns);
|
||||
int cdns_get_id(struct cdns *cdns);
|
||||
int cdns_get_vbus(struct cdns *cdns);
|
||||
void cdns_clear_vbus(struct cdns *cdns);
|
||||
void cdns_set_vbus(struct cdns *cdns);
|
||||
int cdns_drd_init(struct cdns *cdns);
|
||||
int cdns_drd_exit(struct cdns *cdns);
|
||||
int cdns_drd_update_mode(struct cdns *cdns);
|
||||
int cdns_drd_gadget_on(struct cdns *cdns);
|
||||
void cdns_drd_gadget_off(struct cdns *cdns);
|
||||
int cdns_drd_host_on(struct cdns *cdns);
|
||||
void cdns_drd_host_off(struct cdns *cdns);
|
||||
|
||||
#endif /* __LINUX_CDNS3_DRD */
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Cadence USBSS DRD Driver - Gadget Export APIs.
|
||||
* Cadence USBSS and USBSSP DRD Driver - Gadget Export APIs.
|
||||
*
|
||||
* Copyright (C) 2017 NXP
|
||||
* Copyright (C) 2017-2018 NXP
|
||||
|
@ -10,16 +10,28 @@
|
|||
#ifndef __LINUX_CDNS3_GADGET_EXPORT
|
||||
#define __LINUX_CDNS3_GADGET_EXPORT
|
||||
|
||||
#ifdef CONFIG_USB_CDNS3_GADGET
|
||||
#if IS_ENABLED(CONFIG_USB_CDNSP_GADGET)
|
||||
|
||||
int cdns3_gadget_init(struct cdns3 *cdns);
|
||||
int cdnsp_gadget_init(struct cdns *cdns);
|
||||
#else
|
||||
|
||||
static inline int cdns3_gadget_init(struct cdns3 *cdns)
|
||||
static inline int cdnsp_gadget_init(struct cdns *cdns)
|
||||
{
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif /* CONFIG_USB_CDNSP_GADGET */
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_CDNS3_GADGET)
|
||||
|
||||
int cdns3_gadget_init(struct cdns *cdns);
|
||||
#else
|
||||
|
||||
static inline int cdns3_gadget_init(struct cdns *cdns)
|
||||
{
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_USB_CDNS3_GADGET */
|
||||
|
||||
#endif /* __LINUX_CDNS3_GADGET_EXPORT */
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Cadence USBSS DRD Driver - Host Export APIs
|
||||
* Cadence USBSS and USBSSP DRD Driver - Host Export APIs
|
||||
*
|
||||
* Copyright (C) 2017-2018 NXP
|
||||
*
|
||||
|
@ -9,25 +9,19 @@
|
|||
#ifndef __LINUX_CDNS3_HOST_EXPORT
|
||||
#define __LINUX_CDNS3_HOST_EXPORT
|
||||
|
||||
struct usb_hcd;
|
||||
#ifdef CONFIG_USB_CDNS3_HOST
|
||||
#if IS_ENABLED(CONFIG_USB_CDNS_HOST)
|
||||
|
||||
int cdns3_host_init(struct cdns3 *cdns);
|
||||
int xhci_cdns3_suspend_quirk(struct usb_hcd *hcd);
|
||||
int cdns_host_init(struct cdns *cdns);
|
||||
|
||||
#else
|
||||
|
||||
static inline int cdns3_host_init(struct cdns3 *cdns)
|
||||
static inline int cdns_host_init(struct cdns *cdns)
|
||||
{
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static inline void cdns3_host_exit(struct cdns3 *cdns) { }
|
||||
static inline int xhci_cdns3_suspend_quirk(struct usb_hcd *hcd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void cdns_host_exit(struct cdns *cdns) { }
|
||||
|
||||
#endif /* CONFIG_USB_CDNS3_HOST */
|
||||
#endif /* USB_CDNS_HOST */
|
||||
|
||||
#endif /* __LINUX_CDNS3_HOST_EXPORT */
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Cadence USBSS DRD Driver - host side
|
||||
* Cadence USBSS and USBSSP DRD Driver - host side
|
||||
*
|
||||
* Copyright (C) 2018-2019 Cadence Design Systems.
|
||||
* Copyright (C) 2017-2018 NXP
|
||||
|
@ -23,18 +23,20 @@
|
|||
#define CFG_RXDET_P3_EN BIT(15)
|
||||
#define LPM_2_STB_SWITCH_EN BIT(25)
|
||||
|
||||
static int xhci_cdns3_suspend_quirk(struct usb_hcd *hcd);
|
||||
|
||||
static const struct xhci_plat_priv xhci_plat_cdns3_xhci = {
|
||||
.quirks = XHCI_SKIP_PHY_INIT | XHCI_AVOID_BEI,
|
||||
.suspend_quirk = xhci_cdns3_suspend_quirk,
|
||||
};
|
||||
|
||||
static int __cdns3_host_init(struct cdns3 *cdns)
|
||||
static int __cdns_host_init(struct cdns *cdns)
|
||||
{
|
||||
struct platform_device *xhci;
|
||||
int ret;
|
||||
struct usb_hcd *hcd;
|
||||
|
||||
cdns3_drd_host_on(cdns);
|
||||
cdns_drd_host_on(cdns);
|
||||
|
||||
xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
|
||||
if (!xhci) {
|
||||
|
@ -46,7 +48,7 @@ static int __cdns3_host_init(struct cdns3 *cdns)
|
|||
cdns->host_dev = xhci;
|
||||
|
||||
ret = platform_device_add_resources(xhci, cdns->xhci_res,
|
||||
CDNS3_XHCI_RESOURCES_NUM);
|
||||
CDNS_XHCI_RESOURCES_NUM);
|
||||
if (ret) {
|
||||
dev_err(cdns->dev, "couldn't add resources to xHCI device\n");
|
||||
goto err1;
|
||||
|
@ -87,7 +89,7 @@ static int __cdns3_host_init(struct cdns3 *cdns)
|
|||
return ret;
|
||||
}
|
||||
|
||||
int xhci_cdns3_suspend_quirk(struct usb_hcd *hcd)
|
||||
static int xhci_cdns3_suspend_quirk(struct usb_hcd *hcd)
|
||||
{
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
u32 value;
|
||||
|
@ -113,25 +115,25 @@ int xhci_cdns3_suspend_quirk(struct usb_hcd *hcd)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void cdns3_host_exit(struct cdns3 *cdns)
|
||||
static void cdns_host_exit(struct cdns *cdns)
|
||||
{
|
||||
kfree(cdns->xhci_plat_data);
|
||||
platform_device_unregister(cdns->host_dev);
|
||||
cdns->host_dev = NULL;
|
||||
cdns3_drd_host_off(cdns);
|
||||
cdns_drd_host_off(cdns);
|
||||
}
|
||||
|
||||
int cdns3_host_init(struct cdns3 *cdns)
|
||||
int cdns_host_init(struct cdns *cdns)
|
||||
{
|
||||
struct cdns3_role_driver *rdrv;
|
||||
struct cdns_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->start = __cdns_host_init;
|
||||
rdrv->stop = cdns_host_exit;
|
||||
rdrv->state = CDNS_ROLE_STATE_INACTIVE;
|
||||
rdrv->name = "host";
|
||||
|
||||
cdns->roles[USB_ROLE_HOST] = rdrv;
|
||||
|
|
Loading…
Reference in New Issue