usb: Add Cadence USB3 host and gadget driver

Add support for USB3 host and gadget driver. This is a direct sync of
Linux kernel Cadence USB stack that from v5.4-rc1 release.
Driver has been modified so that it compiles without errors against
U-Boot code base.
Features not required for U-Boot such as scatter-gather DMA and OTG
interrupt handling has been dropped.

Signed-off-by: Jean-Jacques Hiblot <jjhiblot@ti.com>
[jjhiblot@ti.com: Add PHY support]
Signed-off-by: Vignesh Raghavendra <vigneshr@ti.com>
This commit is contained in:
Vignesh Raghavendra 2019-10-01 17:26:33 +05:30 committed by Marek Vasut
parent 8d94e184ff
commit 7e91f6ccdc
18 changed files with 6433 additions and 0 deletions

View File

@ -732,6 +732,7 @@ libs-$(CONFIG_SYS_FSL_DDR) += drivers/ddr/fsl/
libs-$(CONFIG_SYS_FSL_MMDC) += drivers/ddr/fsl/
libs-$(CONFIG_$(SPL_)ALTERA_SDRAM) += drivers/ddr/altera/
libs-y += drivers/serial/
libs-y += drivers/usb/cdns3/
libs-y += drivers/usb/dwc3/
libs-y += drivers/usb/common/
libs-y += drivers/usb/emul/

View File

@ -68,6 +68,8 @@ config SPL_DM_USB_GADGET
source "drivers/usb/host/Kconfig"
source "drivers/usb/cdns3/Kconfig"
source "drivers/usb/dwc3/Kconfig"
source "drivers/usb/musb/Kconfig"

51
drivers/usb/cdns3/Kconfig Normal file
View File

@ -0,0 +1,51 @@
config USB_CDNS3
tristate "Cadence USB3 Dual-Role Controller"
depends on USB_HOST || USB_GADGET
help
Say Y here if your system has a Cadence USB3 dual-role controller.
It supports: Host-only, and Peripheral-only.
if USB_CDNS3
config USB_CDNS3_GADGET
bool "Cadence USB3 device controller"
depends on USB_GADGET
select USB_GADGET_DUALSPEED
help
Say Y here to enable device controller functionality of the
Cadence USBSS-DEV driver.
This controller supports FF and HS mode. It doesn't support
LS and SSP mode.
config USB_CDNS3_HOST
bool "Cadence USB3 host controller"
depends on USB_XHCI_HCD
help
Say Y here to enable host controller functionality of the
Cadence driver.
Host controller is compliant with XHCI so it will use
standard XHCI driver.
config SPL_USB_CDNS3_GADGET
bool "SPL support for Cadence USB3 device controller"
depends on SPL_USB_GADGET
select USB_GADGET_DUALSPEED
help
Say Y here to enable device controller functionality of the
Cadence USBSS-DEV driver in SPL.
This controller supports FF and HS mode. It doesn't support
LS and SSP mode.
config SPL_USB_CDNS3_HOST
bool "Cadence USB3 host controller"
depends on USB_XHCI_HCD && SPL_USB_HOST_SUPPORT
help
Say Y here to enable host controller functionality of the
Cadence driver.
Host controller is compliant with XHCI so it will use
standard XHCI driver.
endif

View File

@ -0,0 +1,9 @@
# SPDX-License-Identifier: GPL-2.0
cdns3-y := core.o drd.o
obj-$(CONFIG_USB_CDNS3) += cdns3.o
cdns3-$(CONFIG_$(SPL_)USB_CDNS3_GADGET) += gadget.o ep0.o
cdns3-$(CONFIG_$(SPL_)USB_CDNS3_HOST) += host.o

498
drivers/usb/cdns3/core.c Normal file
View File

@ -0,0 +1,498 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Cadence USBSS DRD Driver.
*
* Copyright (C) 2018-2019 Cadence.
* Copyright (C) 2017-2018 NXP
* Copyright (C) 2019 Texas Instruments
*
* Author: Peter Chen <peter.chen@nxp.com>
* Pawel Laszczak <pawell@cadence.com>
* Roger Quadros <rogerq@ti.com>
*/
#include <common.h>
#include <dm.h>
#include <dm/device-internal.h>
#include <dm/lists.h>
#include <linux/kernel.h>
#include <linux/io.h>
#include <usb.h>
#include "../host/xhci.h"
#include "core.h"
#include "host-export.h"
#include "gadget-export.h"
#include "drd.h"
static int cdns3_idle_init(struct cdns3 *cdns);
struct cdns3_host_priv {
struct xhci_ctrl xhci_ctrl;
struct cdns3 cdns;
};
struct cdns3_gadget_priv {
struct cdns3 cdns;
};
static inline
struct cdns3_role_driver *cdns3_get_current_role_driver(struct cdns3 *cdns)
{
WARN_ON(!cdns->roles[cdns->role]);
return cdns->roles[cdns->role];
}
static int cdns3_role_start(struct cdns3 *cdns, enum usb_role role)
{
int ret;
if (WARN_ON(role > USB_ROLE_DEVICE))
return 0;
mutex_lock(&cdns->mutex);
cdns->role = role;
mutex_unlock(&cdns->mutex);
if (!cdns->roles[role])
return -ENXIO;
if (cdns->roles[role]->state == CDNS3_ROLE_STATE_ACTIVE)
return 0;
mutex_lock(&cdns->mutex);
ret = cdns->roles[role]->start(cdns);
if (!ret)
cdns->roles[role]->state = CDNS3_ROLE_STATE_ACTIVE;
mutex_unlock(&cdns->mutex);
return ret;
}
static void cdns3_role_stop(struct cdns3 *cdns)
{
enum usb_role role = cdns->role;
if (WARN_ON(role > USB_ROLE_DEVICE))
return;
if (cdns->roles[role]->state == CDNS3_ROLE_STATE_INACTIVE)
return;
mutex_lock(&cdns->mutex);
cdns->roles[role]->stop(cdns);
cdns->roles[role]->state = CDNS3_ROLE_STATE_INACTIVE;
mutex_unlock(&cdns->mutex);
}
static void cdns3_exit_roles(struct cdns3 *cdns)
{
cdns3_role_stop(cdns);
cdns3_drd_exit(cdns);
}
static enum usb_role cdsn3_hw_role_state_machine(struct cdns3 *cdns);
/**
* cdns3_core_init_role - initialize role of operation
* @cdns: Pointer to cdns3 structure
*
* Returns 0 on success otherwise negative errno
*/
static int cdns3_core_init_role(struct cdns3 *cdns)
{
struct udevice *dev = cdns->dev;
enum usb_dr_mode best_dr_mode;
enum usb_dr_mode dr_mode;
int ret = 0;
dr_mode = usb_get_dr_mode(dev_of_offset(dev));
cdns->role = USB_ROLE_NONE;
/*
* If driver can't read mode by means of usb_get_dr_mode function then
* chooses mode according with Kernel configuration. This setting
* can be restricted later depending on strap pin configuration.
*/
if (dr_mode == USB_DR_MODE_UNKNOWN) {
if (IS_ENABLED(CONFIG_USB_CDNS3_HOST) &&
IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
dr_mode = USB_DR_MODE_OTG;
else if (IS_ENABLED(CONFIG_USB_CDNS3_HOST))
dr_mode = USB_DR_MODE_HOST;
else if (IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
dr_mode = USB_DR_MODE_PERIPHERAL;
}
/*
* At this point cdns->dr_mode contains strap configuration.
* Driver try update this setting considering kernel configuration
*/
best_dr_mode = cdns->dr_mode;
ret = cdns3_idle_init(cdns);
if (ret)
return ret;
if (dr_mode == USB_DR_MODE_OTG) {
best_dr_mode = cdns->dr_mode;
} else if (cdns->dr_mode == USB_DR_MODE_OTG) {
best_dr_mode = dr_mode;
} else if (cdns->dr_mode != dr_mode) {
dev_err(dev, "Incorrect DRD configuration\n");
return -EINVAL;
}
dr_mode = best_dr_mode;
#if defined(CONFIG_SPL_USB_HOST_SUPPORT) || !defined(CONFIG_SPL_BUILD)
if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) {
ret = cdns3_host_init(cdns);
if (ret) {
dev_err(dev, "Host initialization failed with %d\n",
ret);
goto err;
}
}
#endif
#if CONFIG_IS_ENABLED(DM_USB_GADGET)
if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_PERIPHERAL) {
ret = cdns3_gadget_init(cdns);
if (ret) {
dev_err(dev, "Device initialization failed with %d\n",
ret);
goto err;
}
}
#endif
cdns->dr_mode = dr_mode;
ret = cdns3_drd_update_mode(cdns);
if (ret)
goto err;
if (cdns->dr_mode != USB_DR_MODE_OTG) {
ret = cdns3_hw_role_switch(cdns);
if (ret)
goto err;
}
return ret;
err:
cdns3_exit_roles(cdns);
return ret;
}
/**
* cdsn3_hw_role_state_machine - role switch state machine based on hw events
* @cdns: Pointer to controller structure.
*
* Returns next role to be entered based on hw events.
*/
static enum usb_role cdsn3_hw_role_state_machine(struct cdns3 *cdns)
{
enum usb_role role;
int id, vbus;
if (cdns->dr_mode != USB_DR_MODE_OTG)
goto not_otg;
id = cdns3_get_id(cdns);
vbus = cdns3_get_vbus(cdns);
/*
* Role change state machine
* Inputs: ID, VBUS
* Previous state: cdns->role
* Next state: role
*/
role = cdns->role;
switch (role) {
case USB_ROLE_NONE:
/*
* Driver treats USB_ROLE_NONE synonymous to IDLE state from
* controller specification.
*/
if (!id)
role = USB_ROLE_HOST;
else if (vbus)
role = USB_ROLE_DEVICE;
break;
case USB_ROLE_HOST: /* from HOST, we can only change to NONE */
if (id)
role = USB_ROLE_NONE;
break;
case USB_ROLE_DEVICE: /* from GADGET, we can only change to NONE*/
if (!vbus)
role = USB_ROLE_NONE;
break;
}
dev_dbg(cdns->dev, "role %d -> %d\n", cdns->role, role);
return role;
not_otg:
if (cdns3_is_host(cdns))
role = USB_ROLE_HOST;
if (cdns3_is_device(cdns))
role = USB_ROLE_DEVICE;
return role;
}
static int cdns3_idle_role_start(struct cdns3 *cdns)
{
return 0;
}
static void cdns3_idle_role_stop(struct cdns3 *cdns)
{
/* Program Lane swap and bring PHY out of RESET */
generic_phy_reset(&cdns->usb3_phy);
}
static int cdns3_idle_init(struct cdns3 *cdns)
{
struct cdns3_role_driver *rdrv;
rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL);
if (!rdrv)
return -ENOMEM;
rdrv->start = cdns3_idle_role_start;
rdrv->stop = cdns3_idle_role_stop;
rdrv->state = CDNS3_ROLE_STATE_INACTIVE;
rdrv->suspend = NULL;
rdrv->resume = NULL;
rdrv->name = "idle";
cdns->roles[USB_ROLE_NONE] = rdrv;
return 0;
}
/**
* cdns3_hw_role_switch - switch roles based on HW state
* @cdns3: controller
*/
int cdns3_hw_role_switch(struct cdns3 *cdns)
{
enum usb_role real_role, current_role;
int ret = 0;
/* Do nothing if role based on syfs. */
if (cdns->role_override)
return 0;
current_role = cdns->role;
real_role = cdsn3_hw_role_state_machine(cdns);
/* Do nothing if nothing changed */
if (current_role == real_role)
goto exit;
cdns3_role_stop(cdns);
dev_dbg(cdns->dev, "Switching role %d -> %d", current_role, real_role);
ret = cdns3_role_start(cdns, real_role);
if (ret) {
/* Back to current role */
dev_err(cdns->dev, "set %d has failed, back to %d\n",
real_role, current_role);
ret = cdns3_role_start(cdns, current_role);
if (ret)
dev_err(cdns->dev, "back to %d failed too\n",
current_role);
}
exit:
return ret;
}
static int cdns3_probe(struct cdns3 *cdns)
{
struct udevice *dev = cdns->dev;
int ret;
cdns->xhci_regs = dev_remap_addr_name(dev, "xhci");
if (!cdns->xhci_regs)
return -EINVAL;
cdns->dev_regs = dev_remap_addr_name(dev, "dev");
if (!cdns->dev_regs)
return -EINVAL;
mutex_init(&cdns->mutex);
ret = generic_phy_get_by_name(dev, "cdns3,usb2-phy", &cdns->usb2_phy);
if (ret)
dev_warn(dev, "Unable to get USB2 phy (ret %d)\n", ret);
ret = generic_phy_init(&cdns->usb2_phy);
if (ret)
return ret;
ret = generic_phy_get_by_name(dev, "cdns3,usb3-phy", &cdns->usb3_phy);
if (ret)
dev_warn(dev, "Unable to get USB3 phy (ret %d)\n", ret);
ret = generic_phy_init(&cdns->usb3_phy);
if (ret)
return ret;
ret = generic_phy_power_on(&cdns->usb2_phy);
if (ret)
return ret;
ret = generic_phy_power_on(&cdns->usb3_phy);
if (ret)
return ret;
ret = cdns3_drd_init(cdns);
if (ret)
return ret;
ret = cdns3_core_init_role(cdns);
if (ret)
return ret;
dev_dbg(dev, "Cadence USB3 core: probe succeed\n");
return 0;
}
static int cdns3_remove(struct cdns3 *cdns)
{
cdns3_exit_roles(cdns);
generic_phy_power_off(&cdns->usb2_phy);
generic_phy_power_off(&cdns->usb3_phy);
generic_phy_exit(&cdns->usb2_phy);
generic_phy_exit(&cdns->usb3_phy);
return 0;
}
static const struct udevice_id cdns3_ids[] = {
{ .compatible = "cdns,usb3" },
{ },
};
int cdns3_bind(struct udevice *parent)
{
int from = dev_of_offset(parent);
const void *fdt = gd->fdt_blob;
enum usb_dr_mode dr_mode;
struct udevice *dev;
const char *driver;
const char *name;
int node;
int ret;
node = fdt_node_offset_by_compatible(fdt, from, "cdns,usb3");
if (node < 0) {
ret = -ENODEV;
goto fail;
}
name = fdt_get_name(fdt, node, NULL);
dr_mode = usb_get_dr_mode(node);
switch (dr_mode) {
#if defined(CONFIG_SPL_USB_HOST_SUPPORT) || \
(!defined(CONFIG_SPL_BUILD) && defined(CONFIG_USB_HOST))
case USB_DR_MODE_HOST:
debug("%s: dr_mode: HOST\n", __func__);
driver = "cdns-usb3-host";
break;
#endif
#if CONFIG_IS_ENABLED(DM_USB_GADGET)
case USB_DR_MODE_PERIPHERAL:
debug("%s: dr_mode: PERIPHERAL\n", __func__);
driver = "cdns-usb3-peripheral";
break;
#endif
default:
printf("%s: unsupported dr_mode\n", __func__);
ret = -ENODEV;
goto fail;
};
ret = device_bind_driver_to_node(parent, driver, name,
offset_to_ofnode(node), &dev);
if (ret) {
printf("%s: not able to bind usb device mode\n",
__func__);
goto fail;
}
return 0;
fail:
/* do not return an error: failing to bind would hang the board */
return 0;
}
#if CONFIG_IS_ENABLED(DM_USB_GADGET)
static int cdns3_gadget_probe(struct udevice *dev)
{
struct cdns3_gadget_priv *priv = dev_get_priv(dev);
struct cdns3 *cdns = &priv->cdns;
cdns->dev = dev;
return cdns3_probe(cdns);
}
static int cdns3_gadget_remove(struct udevice *dev)
{
struct cdns3_gadget_priv *priv = dev_get_priv(dev);
struct cdns3 *cdns = &priv->cdns;
return cdns3_remove(cdns);
}
U_BOOT_DRIVER(cdns_usb3_peripheral) = {
.name = "cdns-usb3-peripheral",
.id = UCLASS_USB_GADGET_GENERIC,
.of_match = cdns3_ids,
.probe = cdns3_gadget_probe,
.remove = cdns3_gadget_remove,
.priv_auto_alloc_size = sizeof(struct cdns3_gadget_priv),
.flags = DM_FLAG_ALLOC_PRIV_DMA,
};
#endif
#if defined(CONFIG_SPL_USB_HOST_SUPPORT) || \
(!defined(CONFIG_SPL_BUILD) && defined(CONFIG_USB_HOST))
static int cdns3_host_probe(struct udevice *dev)
{
struct cdns3_host_priv *priv = dev_get_priv(dev);
struct cdns3 *cdns = &priv->cdns;
cdns->dev = dev;
return cdns3_probe(cdns);
}
static int cdns3_host_remove(struct udevice *dev)
{
struct cdns3_host_priv *priv = dev_get_priv(dev);
struct cdns3 *cdns = &priv->cdns;
return cdns3_remove(cdns);
}
U_BOOT_DRIVER(cdns_usb3_host) = {
.name = "cdns-usb3-host",
.id = UCLASS_USB,
.of_match = cdns3_ids,
.probe = cdns3_host_probe,
.remove = cdns3_host_remove,
.priv_auto_alloc_size = sizeof(struct cdns3_host_priv),
.ops = &xhci_usb_ops,
.flags = DM_FLAG_ALLOC_PRIV_DMA,
};
#endif

108
drivers/usb/cdns3/core.h Normal file
View File

@ -0,0 +1,108 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Cadence USBSS DRD Header File.
*
* Copyright (C) 2017-2018 NXP
* Copyright (C) 2018-2019 Cadence.
*
* Authors: Peter Chen <peter.chen@nxp.com>
* Pawel Laszczak <pawell@cadence.com>
*/
#include <linux/compiler.h>
#include <linux/types.h>
#include <linux/usb/otg.h>
#include <generic-phy.h>
#ifndef __LINUX_CDNS3_CORE_H
#define __LINUX_CDNS3_CORE_H
enum usb_role {
USB_ROLE_NONE,
USB_ROLE_HOST,
USB_ROLE_DEVICE,
};
struct cdns3;
/**
* struct cdns3_role_driver - host/gadget role driver
* @start: start this role
* @stop: stop this role
* @suspend: suspend callback for this role
* @resume: resume callback for this role
* @irq: irq handler for this role
* @name: role name string (host/gadget)
* @state: current state
*/
struct cdns3_role_driver {
int (*start)(struct cdns3 *cdns);
void (*stop)(struct cdns3 *cdns);
int (*suspend)(struct cdns3 *cdns, bool do_wakeup);
int (*resume)(struct cdns3 *cdns, bool hibernated);
const char *name;
#define CDNS3_ROLE_STATE_INACTIVE 0
#define CDNS3_ROLE_STATE_ACTIVE 1
int state;
};
#define CDNS3_XHCI_RESOURCES_NUM 2
/**
* struct cdns3 - Representation of Cadence USB3 DRD controller.
* @dev: pointer to Cadence device struct
* @xhci_regs: pointer to base of xhci registers
* @dev_regs: pointer to base of dev registers
* @otg_v0_regs: pointer to base of v0 otg registers
* @otg_v1_regs: pointer to base of v1 otg registers
* @otg_regs: pointer to base of otg registers
* @otg_irq: irq number for otg controller
* @dev_irq: irq number for device controller
* @roles: array of supported roles for this controller
* @role: current role
* @host_dev: the child host device pointer for cdns3 core
* @gadget_dev: the child gadget device pointer for cdns3 core
* @usb2_phy: pointer to USB2 PHY
* @usb3_phy: pointer to USB3 PHY
* @mutex: the mutex for concurrent code at driver
* @dr_mode: supported mode of operation it can be only Host, only Device
* or OTG mode that allow to switch between Device and Host mode.
* This field based on firmware setting, kernel configuration
* and hardware configuration.
* @role_sw: pointer to role switch object.
* @role_override: set 1 if role rely on SW.
*/
struct cdns3 {
struct udevice *dev;
void __iomem *xhci_regs;
struct cdns3_usb_regs __iomem *dev_regs;
struct cdns3_otg_legacy_regs *otg_v0_regs;
struct cdns3_otg_regs *otg_v1_regs;
struct cdns3_otg_common_regs *otg_regs;
#define CDNS3_CONTROLLER_V0 0
#define CDNS3_CONTROLLER_V1 1
u32 version;
int otg_irq;
int dev_irq;
struct cdns3_role_driver *roles[USB_ROLE_DEVICE + 1];
enum usb_role role;
struct cdns3_device *gadget_dev;
struct phy usb2_phy;
struct phy usb3_phy;
/* mutext used in workqueue*/
struct mutex mutex;
enum usb_dr_mode dr_mode;
int role_override;
};
int cdns3_hw_role_switch(struct cdns3 *cdns);
/**
* cdns3_bind - generic bind function
* @parent - pointer to parent udevice of which cdns3 USB controller
* node is child of
*
* return 0 on success, negative errno otherwise
*/
int cdns3_bind(struct udevice *dev);
#endif /* __LINUX_CDNS3_CORE_H */

162
drivers/usb/cdns3/debug.h Normal file
View File

@ -0,0 +1,162 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Cadence USBSS DRD Driver.
* Debug header file.
*
* Copyright (C) 2018-2019 Cadence.
*
* Author: Pawel Laszczak <pawell@cadence.com>
*/
#ifndef __LINUX_CDNS3_DEBUG
#define __LINUX_CDNS3_DEBUG
#include "core.h"
#include "gadget.h"
static inline char *cdns3_decode_usb_irq(char *str,
enum usb_device_speed speed,
u32 usb_ists)
{
int ret;
ret = sprintf(str, "IRQ %08x = ", usb_ists);
if (usb_ists & (USB_ISTS_CON2I | USB_ISTS_CONI)) {
ret += sprintf(str + ret, "Connection %s\n",
usb_speed_string(speed));
}
if (usb_ists & USB_ISTS_DIS2I || usb_ists & USB_ISTS_DISI)
ret += sprintf(str + ret, "Disconnection ");
if (usb_ists & USB_ISTS_L2ENTI)
ret += sprintf(str + ret, "suspended ");
if (usb_ists & USB_ISTS_L1ENTI)
ret += sprintf(str + ret, "L1 enter ");
if (usb_ists & USB_ISTS_L1EXTI)
ret += sprintf(str + ret, "L1 exit ");
if (usb_ists & USB_ISTS_L2ENTI)
ret += sprintf(str + ret, "L2 enter ");
if (usb_ists & USB_ISTS_L2EXTI)
ret += sprintf(str + ret, "L2 exit ");
if (usb_ists & USB_ISTS_U3EXTI)
ret += sprintf(str + ret, "U3 exit ");
if (usb_ists & USB_ISTS_UWRESI)
ret += sprintf(str + ret, "Warm Reset ");
if (usb_ists & USB_ISTS_UHRESI)
ret += sprintf(str + ret, "Hot Reset ");
if (usb_ists & USB_ISTS_U2RESI)
ret += sprintf(str + ret, "Reset");
return str;
}
static inline char *cdns3_decode_ep_irq(char *str,
u32 ep_sts,
const char *ep_name)
{
int ret;
ret = sprintf(str, "IRQ for %s: %08x ", ep_name, ep_sts);
if (ep_sts & EP_STS_SETUP)
ret += sprintf(str + ret, "SETUP ");
if (ep_sts & EP_STS_IOC)
ret += sprintf(str + ret, "IOC ");
if (ep_sts & EP_STS_ISP)
ret += sprintf(str + ret, "ISP ");
if (ep_sts & EP_STS_DESCMIS)
ret += sprintf(str + ret, "DESCMIS ");
if (ep_sts & EP_STS_STREAMR)
ret += sprintf(str + ret, "STREAMR ");
if (ep_sts & EP_STS_MD_EXIT)
ret += sprintf(str + ret, "MD_EXIT ");
if (ep_sts & EP_STS_TRBERR)
ret += sprintf(str + ret, "TRBERR ");
if (ep_sts & EP_STS_NRDY)
ret += sprintf(str + ret, "NRDY ");
if (ep_sts & EP_STS_PRIME)
ret += sprintf(str + ret, "PRIME ");
if (ep_sts & EP_STS_SIDERR)
ret += sprintf(str + ret, "SIDERRT ");
if (ep_sts & EP_STS_OUTSMM)
ret += sprintf(str + ret, "OUTSMM ");
if (ep_sts & EP_STS_ISOERR)
ret += sprintf(str + ret, "ISOERR ");
if (ep_sts & EP_STS_IOT)
ret += sprintf(str + ret, "IOT ");
return str;
}
static inline char *cdns3_decode_epx_irq(char *str,
char *ep_name,
u32 ep_sts)
{
return cdns3_decode_ep_irq(str, ep_sts, ep_name);
}
static inline char *cdns3_decode_ep0_irq(char *str,
int dir,
u32 ep_sts)
{
return cdns3_decode_ep_irq(str, ep_sts,
dir ? "ep0IN" : "ep0OUT");
}
/**
* Debug a transfer ring.
*
* Prints out all TRBs in the endpoint ring, even those after the Link TRB.
*.
*/
static inline char *cdns3_dbg_ring(struct cdns3_endpoint *priv_ep,
struct cdns3_trb *ring, char *str)
{
dma_addr_t addr = priv_ep->trb_pool_dma;
struct cdns3_trb *trb;
int trb_per_sector;
int ret = 0;
int i;
trb_per_sector = GET_TRBS_PER_SEGMENT(priv_ep->type);
trb = &priv_ep->trb_pool[priv_ep->dequeue];
ret += sprintf(str + ret, "\n\t\tRing contents for %s:", priv_ep->name);
ret += sprintf(str + ret,
"\n\t\tRing deq index: %d, trb: %p (virt), 0x%llx (dma)\n",
priv_ep->dequeue, trb,
(unsigned long long)cdns3_trb_virt_to_dma(priv_ep, trb));
trb = &priv_ep->trb_pool[priv_ep->enqueue];
ret += sprintf(str + ret,
"\t\tRing enq index: %d, trb: %p (virt), 0x%llx (dma)\n",
priv_ep->enqueue, trb,
(unsigned long long)cdns3_trb_virt_to_dma(priv_ep, trb));
ret += sprintf(str + ret,
"\t\tfree trbs: %d, CCS=%d, PCS=%d\n",
priv_ep->free_trbs, priv_ep->ccs, priv_ep->pcs);
if (trb_per_sector > TRBS_PER_SEGMENT)
trb_per_sector = TRBS_PER_SEGMENT;
if (trb_per_sector > TRBS_PER_SEGMENT) {
sprintf(str + ret, "\t\tTo big transfer ring %d\n",
trb_per_sector);
return str;
}
for (i = 0; i < trb_per_sector; ++i) {
trb = &ring[i];
ret += sprintf(str + ret,
"\t\t@%pad %08x %08x %08x\n", &addr,
le32_to_cpu(trb->buffer),
le32_to_cpu(trb->length),
le32_to_cpu(trb->control));
addr += sizeof(*trb);
}
return str;
}
#endif /*__LINUX_CDNS3_DEBUG*/

301
drivers/usb/cdns3/drd.c Normal file
View File

@ -0,0 +1,301 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Cadence USBSS DRD Driver.
*
* Copyright (C) 2018-2019 Cadence.
* Copyright (C) 2019 Texas Instruments
*
* Author: Pawel Laszczak <pawell@cadence.com>
* Roger Quadros <rogerq@ti.com>
*
*
*/
#include <dm.h>
#include <linux/delay.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/usb/otg.h>
#include "gadget.h"
#include "drd.h"
#include "core.h"
#define readl_poll_timeout_atomic readl_poll_timeout
#define usleep_range(a, b) udelay((b))
/**
* cdns3_set_mode - change mode of OTG Core
* @cdns: pointer to context structure
* @mode: selected mode from cdns_role
*
* Returns 0 on success otherwise negative errno
*/
int cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode)
{
int ret = 0;
u32 reg;
switch (mode) {
case USB_DR_MODE_PERIPHERAL:
break;
case USB_DR_MODE_HOST:
break;
case USB_DR_MODE_OTG:
dev_dbg(cdns->dev, "Set controller to OTG mode\n");
if (cdns->version == CDNS3_CONTROLLER_V1) {
reg = readl(&cdns->otg_v1_regs->override);
reg |= OVERRIDE_IDPULLUP;
writel(reg, &cdns->otg_v1_regs->override);
} else {
reg = readl(&cdns->otg_v0_regs->ctrl1);
reg |= OVERRIDE_IDPULLUP_V0;
writel(reg, &cdns->otg_v0_regs->ctrl1);
}
/*
* Hardware specification says: "ID_VALUE must be valid within
* 50ms after idpullup is set to '1" so driver must wait
* 50ms before reading this pin.
*/
usleep_range(50000, 60000);
break;
default:
dev_err(cdns->dev, "Unsupported mode of operation %d\n", mode);
return -EINVAL;
}
return ret;
}
int cdns3_get_id(struct cdns3 *cdns)
{
int id;
id = readl(&cdns->otg_regs->sts) & OTGSTS_ID_VALUE;
dev_dbg(cdns->dev, "OTG ID: %d", id);
return id;
}
int cdns3_get_vbus(struct cdns3 *cdns)
{
int vbus;
vbus = !!(readl(&cdns->otg_regs->sts) & OTGSTS_VBUS_VALID);
dev_dbg(cdns->dev, "OTG VBUS: %d", vbus);
return vbus;
}
int cdns3_is_host(struct cdns3 *cdns)
{
if (cdns->dr_mode == USB_DR_MODE_HOST)
return 1;
else if (!cdns3_get_id(cdns))
return 1;
return 0;
}
int cdns3_is_device(struct cdns3 *cdns)
{
if (cdns->dr_mode == USB_DR_MODE_PERIPHERAL)
return 1;
else if (cdns->dr_mode == USB_DR_MODE_OTG)
if (cdns3_get_id(cdns))
return 1;
return 0;
}
/**
* cdns3_drd_switch_host - start/stop host
* @cdns: Pointer to controller context structure
* @on: 1 for start, 0 for stop
*
* Returns 0 on success otherwise negative errno
*/
int cdns3_drd_switch_host(struct cdns3 *cdns, int on)
{
int ret, val;
u32 reg = OTGCMD_OTG_DIS;
/* switch OTG core */
if (on) {
writel(OTGCMD_HOST_BUS_REQ | reg, &cdns->otg_regs->cmd);
dev_dbg(cdns->dev, "Waiting till Host mode is turned on\n");
ret = readl_poll_timeout_atomic(&cdns->otg_regs->sts, val,
val & OTGSTS_XHCI_READY,
100000);
if (ret) {
dev_err(cdns->dev, "timeout waiting for xhci_ready\n");
return ret;
}
} else {
writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
&cdns->otg_regs->cmd);
/* Waiting till H_IDLE state.*/
readl_poll_timeout_atomic(&cdns->otg_regs->state, val,
!(val & OTGSTATE_HOST_STATE_MASK),
2000000);
}
return 0;
}
/**
* cdns3_drd_switch_gadget - start/stop gadget
* @cdns: Pointer to controller context structure
* @on: 1 for start, 0 for stop
*
* Returns 0 on success otherwise negative errno
*/
int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on)
{
int ret, val;
u32 reg = OTGCMD_OTG_DIS;
/* switch OTG core */
if (on) {
writel(OTGCMD_DEV_BUS_REQ | reg, &cdns->otg_regs->cmd);
dev_dbg(cdns->dev, "Waiting till Device mode is turned on\n");
ret = readl_poll_timeout_atomic(&cdns->otg_regs->sts, val,
val & OTGSTS_DEV_READY,
100000);
if (ret) {
dev_err(cdns->dev, "timeout waiting for dev_ready\n");
return ret;
}
} else {
/*
* driver should wait at least 10us after disabling Device
* before turning-off Device (DEV_BUS_DROP)
*/
usleep_range(20, 30);
writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
&cdns->otg_regs->cmd);
/* Waiting till DEV_IDLE state.*/
readl_poll_timeout_atomic(&cdns->otg_regs->state, val,
!(val & OTGSTATE_DEV_STATE_MASK),
2000000);
}
return 0;
}
/**
* cdns3_init_otg_mode - initialize drd controller
* @cdns: Pointer to controller context structure
*
* Returns 0 on success otherwise negative errno
*/
static int cdns3_init_otg_mode(struct cdns3 *cdns)
{
int ret = 0;
/* clear all interrupts */
writel(~0, &cdns->otg_regs->ivect);
ret = cdns3_set_mode(cdns, USB_DR_MODE_OTG);
if (ret)
return ret;
return ret;
}
/**
* cdns3_drd_update_mode - initialize mode of operation
* @cdns: Pointer to controller context structure
*
* Returns 0 on success otherwise negative errno
*/
int cdns3_drd_update_mode(struct cdns3 *cdns)
{
int ret = 0;
switch (cdns->dr_mode) {
case USB_DR_MODE_PERIPHERAL:
ret = cdns3_set_mode(cdns, USB_DR_MODE_PERIPHERAL);
break;
case USB_DR_MODE_HOST:
ret = cdns3_set_mode(cdns, USB_DR_MODE_HOST);
break;
case USB_DR_MODE_OTG:
ret = cdns3_init_otg_mode(cdns);
break;
default:
dev_err(cdns->dev, "Unsupported mode of operation %d\n",
cdns->dr_mode);
return -EINVAL;
}
return ret;
}
int cdns3_drd_init(struct cdns3 *cdns)
{
void __iomem *regs;
int ret = 0;
u32 state;
regs = dev_remap_addr_name(cdns->dev, "otg");
if (!regs)
return -EINVAL;
/* Detection of DRD version. Controller has been released
* in two versions. Both are similar, but they have same changes
* in register maps.
* The first register in old version is command register and it's read
* only, so driver should read 0 from it. On the other hand, in v1
* the first register contains device ID number which is not set to 0.
* Driver uses this fact to detect the proper version of
* controller.
*/
cdns->otg_v0_regs = regs;
if (!readl(&cdns->otg_v0_regs->cmd)) {
cdns->version = CDNS3_CONTROLLER_V0;
cdns->otg_v1_regs = NULL;
cdns->otg_regs = regs;
writel(1, &cdns->otg_v0_regs->simulate);
dev_info(cdns->dev, "DRD version v0 (%08x)\n",
readl(&cdns->otg_v0_regs->version));
} else {
cdns->otg_v0_regs = NULL;
cdns->otg_v1_regs = regs;
cdns->otg_regs = (void *)&cdns->otg_v1_regs->cmd;
cdns->version = CDNS3_CONTROLLER_V1;
writel(1, &cdns->otg_v1_regs->simulate);
dev_info(cdns->dev, "DRD version v1 (ID: %08x, rev: %08x)\n",
readl(&cdns->otg_v1_regs->did),
readl(&cdns->otg_v1_regs->rid));
}
state = OTGSTS_STRAP(readl(&cdns->otg_regs->sts));
/* Update dr_mode according to STRAP configuration. */
cdns->dr_mode = USB_DR_MODE_OTG;
if (state == OTGSTS_STRAP_HOST) {
dev_dbg(cdns->dev, "Controller strapped to HOST\n");
cdns->dr_mode = USB_DR_MODE_HOST;
} else if (state == OTGSTS_STRAP_GADGET) {
dev_dbg(cdns->dev, "Controller strapped to PERIPHERAL\n");
cdns->dr_mode = USB_DR_MODE_PERIPHERAL;
}
state = readl(&cdns->otg_regs->sts);
if (OTGSTS_OTG_NRDY(state) != 0) {
dev_err(cdns->dev, "Cadence USB3 OTG device not ready\n");
return -ENODEV;
}
return ret;
}
int cdns3_drd_exit(struct cdns3 *cdns)
{
return 0;
}

166
drivers/usb/cdns3/drd.h Normal file
View File

@ -0,0 +1,166 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Cadence USB3 DRD header file.
*
* Copyright (C) 2018-2019 Cadence.
*
* Author: Pawel Laszczak <pawell@cadence.com>
*/
#ifndef __LINUX_CDNS3_DRD
#define __LINUX_CDNS3_DRD
#include <linux/types.h>
#include <linux/usb/otg.h>
#include "core.h"
/* DRD register interface for version v1. */
struct cdns3_otg_regs {
__le32 did;
__le32 rid;
__le32 capabilities;
__le32 reserved1;
__le32 cmd;
__le32 sts;
__le32 state;
__le32 reserved2;
__le32 ien;
__le32 ivect;
__le32 refclk;
__le32 tmr;
__le32 reserved3[4];
__le32 simulate;
__le32 override;
__le32 susp_ctrl;
__le32 reserved4;
__le32 anasts;
__le32 adp_ramp_time;
__le32 ctrl1;
__le32 ctrl2;
};
/* DRD register interface for version v0. */
struct cdns3_otg_legacy_regs {
__le32 cmd;
__le32 sts;
__le32 state;
__le32 refclk;
__le32 ien;
__le32 ivect;
__le32 reserved1[3];
__le32 tmr;
__le32 reserved2[2];
__le32 version;
__le32 capabilities;
__le32 reserved3[2];
__le32 simulate;
__le32 reserved4[5];
__le32 ctrl1;
};
/*
* Common registers interface for both version of DRD.
*/
struct cdns3_otg_common_regs {
__le32 cmd;
__le32 sts;
__le32 state;
__le32 different1;
__le32 ien;
__le32 ivect;
};
/* CDNS_RID - bitmasks */
#define CDNS_RID(p) ((p) & GENMASK(15, 0))
/* CDNS_VID - bitmasks */
#define CDNS_DID(p) ((p) & GENMASK(31, 0))
/* OTGCMD - bitmasks */
/* "Request the bus for Device mode. */
#define OTGCMD_DEV_BUS_REQ BIT(0)
/* Request the bus for Host mode */
#define OTGCMD_HOST_BUS_REQ BIT(1)
/* Enable OTG mode. */
#define OTGCMD_OTG_EN BIT(2)
/* Disable OTG mode */
#define OTGCMD_OTG_DIS BIT(3)
/*"Configure OTG as A-Device. */
#define OTGCMD_A_DEV_EN BIT(4)
/*"Configure OTG as A-Device. */
#define OTGCMD_A_DEV_DIS BIT(5)
/* Drop the bus for Device mod e. */
#define OTGCMD_DEV_BUS_DROP BIT(8)
/* Drop the bus for Host mode*/
#define OTGCMD_HOST_BUS_DROP BIT(9)
/* Power Down USBSS-DEV. */
#define OTGCMD_DEV_POWER_OFF BIT(11)
/* Power Down CDNSXHCI. */
#define OTGCMD_HOST_POWER_OFF BIT(12)
/* OTGIEN - bitmasks */
/* ID change interrupt enable */
#define OTGIEN_ID_CHANGE_INT BIT(0)
/* Vbusvalid fall detected interrupt enable.*/
#define OTGIEN_VBUSVALID_RISE_INT BIT(4)
/* Vbusvalid fall detected interrupt enable */
#define OTGIEN_VBUSVALID_FALL_INT BIT(5)
/* OTGSTS - bitmasks */
/*
* Current value of the ID pin. It is only valid when idpullup in
* OTGCTRL1_TYPE register is set to '1'.
*/
#define OTGSTS_ID_VALUE BIT(0)
/* Current value of the vbus_valid */
#define OTGSTS_VBUS_VALID BIT(1)
/* Current value of the b_sess_vld */
#define OTGSTS_SESSION_VALID BIT(2)
/*Device mode is active*/
#define OTGSTS_DEV_ACTIVE BIT(3)
/* Host mode is active. */
#define OTGSTS_HOST_ACTIVE BIT(4)
/* OTG Controller not ready. */
#define OTGSTS_OTG_NRDY_MASK BIT(11)
#define OTGSTS_OTG_NRDY(p) ((p) & OTGSTS_OTG_NRDY_MASK)
/*
* Value of the strap pins.
* 000 - no default configuration
* 010 - Controller initiall configured as Host
* 100 - Controller initially configured as Device
*/
#define OTGSTS_STRAP(p) (((p) & GENMASK(14, 12)) >> 12)
#define OTGSTS_STRAP_NO_DEFAULT_CFG 0x00
#define OTGSTS_STRAP_HOST_OTG 0x01
#define OTGSTS_STRAP_HOST 0x02
#define OTGSTS_STRAP_GADGET 0x04
/* Host mode is turned on. */
#define OTGSTS_XHCI_READY BIT(26)
/* "Device mode is turned on .*/
#define OTGSTS_DEV_READY BIT(27)
/* OTGSTATE- bitmasks */
#define OTGSTATE_DEV_STATE_MASK GENMASK(2, 0)
#define OTGSTATE_HOST_STATE_MASK GENMASK(5, 3)
#define OTGSTATE_HOST_STATE_IDLE 0x0
#define OTGSTATE_HOST_STATE_VBUS_FALL 0x7
#define OTGSTATE_HOST_STATE(p) (((p) & OTGSTATE_HOST_STATE_MASK) >> 3)
/* OTGREFCLK - bitmasks */
#define OTGREFCLK_STB_CLK_SWITCH_EN BIT(31)
/* OVERRIDE - bitmasks */
#define OVERRIDE_IDPULLUP BIT(0)
/* Only for CDNS3_CONTROLLER_V0 version */
#define OVERRIDE_IDPULLUP_V0 BIT(24)
int cdns3_is_host(struct cdns3 *cdns);
int cdns3_is_device(struct cdns3 *cdns);
int cdns3_get_id(struct cdns3 *cdns);
int cdns3_get_vbus(struct cdns3 *cdns);
int cdns3_drd_init(struct cdns3 *cdns);
int cdns3_drd_exit(struct cdns3 *cdns);
int cdns3_drd_update_mode(struct cdns3 *cdns);
int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on);
int cdns3_drd_switch_host(struct cdns3 *cdns, int on);
#endif /* __LINUX_CDNS3_DRD */

910
drivers/usb/cdns3/ep0.c Normal file
View File

@ -0,0 +1,910 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Cadence USBSS DRD Driver - gadget side.
*
* Copyright (C) 2018 Cadence Design Systems.
* Copyright (C) 2017-2018 NXP
*
* Authors: Pawel Jez <pjez@cadence.com>,
* Pawel Laszczak <pawell@cadence.com>
* Peter Chen <peter.chen@nxp.com>
*/
#include <linux/usb/composite.h>
#include <linux/iopoll.h>
#include "gadget.h"
#include "trace.h"
#define readl_poll_timeout_atomic readl_poll_timeout
#define usleep_range(a, b) udelay((b))
static struct usb_endpoint_descriptor cdns3_gadget_ep0_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_CONTROL,
};
/**
* cdns3_ep0_run_transfer - Do transfer on default endpoint hardware
* @priv_dev: extended gadget object
* @dma_addr: physical address where data is/will be stored
* @length: data length
* @erdy: set it to 1 when ERDY packet should be sent -
* exit from flow control state
*/
static void cdns3_ep0_run_transfer(struct cdns3_device *priv_dev,
dma_addr_t dma_addr,
unsigned int length, int erdy, int zlp)
{
struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
priv_ep->trb_pool[0].buffer = TRB_BUFFER(dma_addr);
priv_ep->trb_pool[0].length = TRB_LEN(length);
if (zlp) {
priv_ep->trb_pool[0].control = TRB_CYCLE | TRB_TYPE(TRB_NORMAL);
priv_ep->trb_pool[1].buffer = TRB_BUFFER(dma_addr);
priv_ep->trb_pool[1].length = TRB_LEN(0);
priv_ep->trb_pool[1].control = TRB_CYCLE | TRB_IOC |
TRB_TYPE(TRB_NORMAL);
} else {
priv_ep->trb_pool[0].control = TRB_CYCLE | TRB_IOC |
TRB_TYPE(TRB_NORMAL);
priv_ep->trb_pool[1].control = 0;
}
/* Flush both TRBs */
flush_dcache_range((unsigned long)priv_ep->trb_pool,
(unsigned long)priv_ep->trb_pool +
ROUND(sizeof(struct cdns3_trb) * 2,
CONFIG_SYS_CACHELINE_SIZE));
trace_cdns3_prepare_trb(priv_ep, priv_ep->trb_pool);
cdns3_select_ep(priv_dev, priv_dev->ep0_data_dir);
writel(EP_STS_TRBERR, &regs->ep_sts);
writel(EP_TRADDR_TRADDR(priv_ep->trb_pool_dma), &regs->ep_traddr);
trace_cdns3_doorbell_ep0(priv_dev->ep0_data_dir ? "ep0in" : "ep0out",
readl(&regs->ep_traddr));
/* TRB should be prepared before starting transfer. */
writel(EP_CMD_DRDY, &regs->ep_cmd);
/* Resume controller before arming transfer. */
__cdns3_gadget_wakeup(priv_dev);
if (erdy)
writel(EP_CMD_ERDY, &priv_dev->regs->ep_cmd);
}
/**
* cdns3_ep0_delegate_req - Returns status of handling setup packet
* Setup is handled by gadget driver
* @priv_dev: extended gadget object
* @ctrl_req: pointer to received setup packet
*
* Returns zero on success or negative value on failure
*/
static int cdns3_ep0_delegate_req(struct cdns3_device *priv_dev,
struct usb_ctrlrequest *ctrl_req)
{
int ret;
spin_unlock(&priv_dev->lock);
priv_dev->setup_pending = 1;
ret = priv_dev->gadget_driver->setup(&priv_dev->gadget, ctrl_req);
priv_dev->setup_pending = 0;
spin_lock(&priv_dev->lock);
return ret;
}
static void cdns3_prepare_setup_packet(struct cdns3_device *priv_dev)
{
priv_dev->ep0_data_dir = 0;
priv_dev->ep0_stage = CDNS3_SETUP_STAGE;
cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma,
sizeof(struct usb_ctrlrequest), 0, 0);
}
static void cdns3_ep0_complete_setup(struct cdns3_device *priv_dev,
u8 send_stall, u8 send_erdy)
{
struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
struct usb_request *request;
request = cdns3_next_request(&priv_ep->pending_req_list);
if (request)
list_del_init(&request->list);
if (send_stall) {
trace_cdns3_halt(priv_ep, send_stall, 0);
/* set_stall on ep0 */
cdns3_select_ep(priv_dev, 0x00);
writel(EP_CMD_SSTALL, &priv_dev->regs->ep_cmd);
} else {
cdns3_prepare_setup_packet(priv_dev);
}
priv_dev->ep0_stage = CDNS3_SETUP_STAGE;
writel((send_erdy ? EP_CMD_ERDY : 0) | EP_CMD_REQ_CMPL,
&priv_dev->regs->ep_cmd);
cdns3_allow_enable_l1(priv_dev, 1);
}
/**
* cdns3_req_ep0_set_configuration - Handling of SET_CONFIG standard USB request
* @priv_dev: extended gadget object
* @ctrl_req: pointer to received setup packet
*
* Returns 0 if success, USB_GADGET_DELAYED_STATUS on deferred status stage,
* error code on error
*/
static int cdns3_req_ep0_set_configuration(struct cdns3_device *priv_dev,
struct usb_ctrlrequest *ctrl_req)
{
enum usb_device_state device_state = priv_dev->gadget.state;
struct cdns3_endpoint *priv_ep;
u32 config = le16_to_cpu(ctrl_req->wValue);
int result = 0;
int i;
switch (device_state) {
case USB_STATE_ADDRESS:
/* Configure non-control EPs */
for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) {
priv_ep = priv_dev->eps[i];
if (!priv_ep)
continue;
if (priv_ep->flags & EP_CLAIMED)
cdns3_ep_config(priv_ep);
}
result = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
if (result)
return result;
if (config) {
cdns3_set_hw_configuration(priv_dev);
} else {
cdns3_hw_reset_eps_config(priv_dev);
usb_gadget_set_state(&priv_dev->gadget,
USB_STATE_ADDRESS);
}
break;
case USB_STATE_CONFIGURED:
result = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
if (!config && !result) {
cdns3_hw_reset_eps_config(priv_dev);
usb_gadget_set_state(&priv_dev->gadget,
USB_STATE_ADDRESS);
}
break;
default:
result = -EINVAL;
}
return result;
}
/**
* cdns3_req_ep0_set_address - Handling of SET_ADDRESS standard USB request
* @priv_dev: extended gadget object
* @ctrl_req: pointer to received setup packet
*
* Returns 0 if success, error code on error
*/
static int cdns3_req_ep0_set_address(struct cdns3_device *priv_dev,
struct usb_ctrlrequest *ctrl_req)
{
enum usb_device_state device_state = priv_dev->gadget.state;
u32 reg;
u32 addr;
addr = le16_to_cpu(ctrl_req->wValue);
if (addr > USB_DEVICE_MAX_ADDRESS) {
dev_err(priv_dev->dev,
"Device address (%d) cannot be greater than %d\n",
addr, USB_DEVICE_MAX_ADDRESS);
return -EINVAL;
}
if (device_state == USB_STATE_CONFIGURED) {
dev_err(priv_dev->dev,
"can't set_address from configured state\n");
return -EINVAL;
}
reg = readl(&priv_dev->regs->usb_cmd);
writel(reg | USB_CMD_FADDR(addr) | USB_CMD_SET_ADDR,
&priv_dev->regs->usb_cmd);
usb_gadget_set_state(&priv_dev->gadget,
(addr ? USB_STATE_ADDRESS : USB_STATE_DEFAULT));
return 0;
}
/**
* cdns3_req_ep0_get_status - Handling of GET_STATUS standard USB request
* @priv_dev: extended gadget object
* @ctrl_req: pointer to received setup packet
*
* Returns 0 if success, error code on error
*/
static int cdns3_req_ep0_get_status(struct cdns3_device *priv_dev,
struct usb_ctrlrequest *ctrl)
{
__le16 *response_pkt;
u16 usb_status = 0;
u32 recip;
recip = ctrl->bRequestType & USB_RECIP_MASK;
switch (recip) {
case USB_RECIP_DEVICE:
/* self powered */
if (priv_dev->is_selfpowered)
usb_status = BIT(USB_DEVICE_SELF_POWERED);
if (priv_dev->wake_up_flag)
usb_status |= BIT(USB_DEVICE_REMOTE_WAKEUP);
if (priv_dev->gadget.speed != USB_SPEED_SUPER)
break;
if (priv_dev->u1_allowed)
usb_status |= BIT(USB_DEV_STAT_U1_ENABLED);
if (priv_dev->u2_allowed)
usb_status |= BIT(USB_DEV_STAT_U2_ENABLED);
break;
case USB_RECIP_INTERFACE:
return cdns3_ep0_delegate_req(priv_dev, ctrl);
case USB_RECIP_ENDPOINT:
/* check if endpoint is stalled */
cdns3_select_ep(priv_dev, ctrl->wIndex);
if (EP_STS_STALL(readl(&priv_dev->regs->ep_sts)))
usb_status = BIT(USB_ENDPOINT_HALT);
break;
default:
return -EINVAL;
}
response_pkt = (__le16 *)priv_dev->setup_buf;
*response_pkt = cpu_to_le16(usb_status);
/* Flush setup response */
flush_dcache_range((unsigned long)priv_dev->setup_buf,
(unsigned long)priv_dev->setup_buf +
ROUND(sizeof(struct usb_ctrlrequest),
CONFIG_SYS_CACHELINE_SIZE));
cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma,
sizeof(*response_pkt), 1, 0);
return 0;
}
static int cdns3_ep0_feature_handle_device(struct cdns3_device *priv_dev,
struct usb_ctrlrequest *ctrl,
int set)
{
enum usb_device_state state;
enum usb_device_speed speed;
int ret = 0;
u16 tmode;
state = priv_dev->gadget.state;
speed = priv_dev->gadget.speed;
switch (ctrl->wValue) {
case USB_DEVICE_REMOTE_WAKEUP:
priv_dev->wake_up_flag = !!set;
break;
case USB_DEVICE_U1_ENABLE:
if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER)
return -EINVAL;
priv_dev->u1_allowed = !!set;
break;
case USB_DEVICE_U2_ENABLE:
if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER)
return -EINVAL;
priv_dev->u2_allowed = !!set;
break;
case USB_DEVICE_LTM_ENABLE:
ret = -EINVAL;
break;
case USB_DEVICE_TEST_MODE:
if (state != USB_STATE_CONFIGURED || speed > USB_SPEED_HIGH)
return -EINVAL;
tmode = le16_to_cpu(ctrl->wIndex);
if (!set || (tmode & 0xff) != 0)
return -EINVAL;
switch (tmode >> 8) {
case TEST_J:
case TEST_K:
case TEST_SE0_NAK:
case TEST_PACKET:
cdns3_ep0_complete_setup(priv_dev, 0, 1);
/**
* Little delay to give the controller some time
* for sending status stage.
* This time should be less then 3ms.
*/
usleep_range(1000, 2000);
cdns3_set_register_bit(&priv_dev->regs->usb_cmd,
USB_CMD_STMODE |
USB_STS_TMODE_SEL(tmode - 1));
break;
default:
ret = -EINVAL;
}
break;
default:
ret = -EINVAL;
}
return ret;
}
static int cdns3_ep0_feature_handle_intf(struct cdns3_device *priv_dev,
struct usb_ctrlrequest *ctrl,
int set)
{
u32 wValue;
int ret = 0;
wValue = le16_to_cpu(ctrl->wValue);
switch (wValue) {
case USB_INTRF_FUNC_SUSPEND:
break;
default:
ret = -EINVAL;
}
return ret;
}
static int cdns3_ep0_feature_handle_endpoint(struct cdns3_device *priv_dev,
struct usb_ctrlrequest *ctrl,
int set)
{
struct cdns3_endpoint *priv_ep;
int ret = 0;
u8 index;
if (le16_to_cpu(ctrl->wValue) != USB_ENDPOINT_HALT)
return -EINVAL;
if (!(ctrl->wIndex & ~USB_DIR_IN))
return 0;
index = cdns3_ep_addr_to_index(ctrl->wIndex);
priv_ep = priv_dev->eps[index];
cdns3_select_ep(priv_dev, ctrl->wIndex);
if (set)
__cdns3_gadget_ep_set_halt(priv_ep);
else if (!(priv_ep->flags & EP_WEDGE))
ret = __cdns3_gadget_ep_clear_halt(priv_ep);
cdns3_select_ep(priv_dev, 0x00);
return ret;
}
/**
* cdns3_req_ep0_handle_feature -
* Handling of GET/SET_FEATURE standard USB request
*
* @priv_dev: extended gadget object
* @ctrl_req: pointer to received setup packet
* @set: must be set to 1 for SET_FEATURE request
*
* Returns 0 if success, error code on error
*/
static int cdns3_req_ep0_handle_feature(struct cdns3_device *priv_dev,
struct usb_ctrlrequest *ctrl,
int set)
{
int ret = 0;
u32 recip;
recip = ctrl->bRequestType & USB_RECIP_MASK;
switch (recip) {
case USB_RECIP_DEVICE:
ret = cdns3_ep0_feature_handle_device(priv_dev, ctrl, set);
break;
case USB_RECIP_INTERFACE:
ret = cdns3_ep0_feature_handle_intf(priv_dev, ctrl, set);
break;
case USB_RECIP_ENDPOINT:
ret = cdns3_ep0_feature_handle_endpoint(priv_dev, ctrl, set);
break;
default:
return -EINVAL;
}
return ret;
}
/**
* cdns3_req_ep0_set_sel - Handling of SET_SEL standard USB request
* @priv_dev: extended gadget object
* @ctrl_req: pointer to received setup packet
*
* Returns 0 if success, error code on error
*/
static int cdns3_req_ep0_set_sel(struct cdns3_device *priv_dev,
struct usb_ctrlrequest *ctrl_req)
{
if (priv_dev->gadget.state < USB_STATE_ADDRESS)
return -EINVAL;
if (ctrl_req->wLength != 6) {
dev_err(priv_dev->dev, "Set SEL should be 6 bytes, got %d\n",
ctrl_req->wLength);
return -EINVAL;
}
cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma, 6, 1, 0);
return 0;
}
/**
* cdns3_req_ep0_set_isoch_delay -
* Handling of GET_ISOCH_DELAY standard USB request
* @priv_dev: extended gadget object
* @ctrl_req: pointer to received setup packet
*
* Returns 0 if success, error code on error
*/
static int cdns3_req_ep0_set_isoch_delay(struct cdns3_device *priv_dev,
struct usb_ctrlrequest *ctrl_req)
{
if (ctrl_req->wIndex || ctrl_req->wLength)
return -EINVAL;
priv_dev->isoch_delay = ctrl_req->wValue;
return 0;
}
/**
* cdns3_ep0_standard_request - Handling standard USB requests
* @priv_dev: extended gadget object
* @ctrl_req: pointer to received setup packet
*
* Returns 0 if success, error code on error
*/
static int cdns3_ep0_standard_request(struct cdns3_device *priv_dev,
struct usb_ctrlrequest *ctrl_req)
{
int ret;
switch (ctrl_req->bRequest) {
case USB_REQ_SET_ADDRESS:
ret = cdns3_req_ep0_set_address(priv_dev, ctrl_req);
break;
case USB_REQ_SET_CONFIGURATION:
ret = cdns3_req_ep0_set_configuration(priv_dev, ctrl_req);
break;
case USB_REQ_GET_STATUS:
ret = cdns3_req_ep0_get_status(priv_dev, ctrl_req);
break;
case USB_REQ_CLEAR_FEATURE:
ret = cdns3_req_ep0_handle_feature(priv_dev, ctrl_req, 0);
break;
case USB_REQ_SET_FEATURE:
ret = cdns3_req_ep0_handle_feature(priv_dev, ctrl_req, 1);
break;
case USB_REQ_SET_SEL:
ret = cdns3_req_ep0_set_sel(priv_dev, ctrl_req);
break;
case USB_REQ_SET_ISOCH_DELAY:
ret = cdns3_req_ep0_set_isoch_delay(priv_dev, ctrl_req);
break;
default:
ret = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
break;
}
return ret;
}
static void __pending_setup_status_handler(struct cdns3_device *priv_dev)
{
struct usb_request *request = priv_dev->pending_status_request;
if (priv_dev->status_completion_no_call && request &&
request->complete) {
request->complete(&priv_dev->eps[0]->endpoint, request);
priv_dev->status_completion_no_call = 0;
}
}
void cdns3_pending_setup_status_handler(struct work_struct *work)
{
struct cdns3_device *priv_dev = container_of(work, struct cdns3_device,
pending_status_wq);
unsigned long flags;
spin_lock_irqsave(&priv_dev->lock, flags);
__pending_setup_status_handler(priv_dev);
spin_unlock_irqrestore(&priv_dev->lock, flags);
}
/**
* cdns3_ep0_setup_phase - Handling setup USB requests
* @priv_dev: extended gadget object
*/
static void cdns3_ep0_setup_phase(struct cdns3_device *priv_dev)
{
struct usb_ctrlrequest *ctrl = priv_dev->setup_buf;
struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
int result;
priv_dev->ep0_data_dir = ctrl->bRequestType & USB_DIR_IN;
trace_cdns3_ctrl_req(ctrl);
if (!list_empty(&priv_ep->pending_req_list)) {
struct usb_request *request;
request = cdns3_next_request(&priv_ep->pending_req_list);
priv_ep->dir = priv_dev->ep0_data_dir;
cdns3_gadget_giveback(priv_ep, to_cdns3_request(request),
-ECONNRESET);
}
if (le16_to_cpu(ctrl->wLength))
priv_dev->ep0_stage = CDNS3_DATA_STAGE;
else
priv_dev->ep0_stage = CDNS3_STATUS_STAGE;
if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
result = cdns3_ep0_standard_request(priv_dev, ctrl);
else
result = cdns3_ep0_delegate_req(priv_dev, ctrl);
if (result == USB_GADGET_DELAYED_STATUS)
return;
if (result < 0)
cdns3_ep0_complete_setup(priv_dev, 1, 1);
else if (priv_dev->ep0_stage == CDNS3_STATUS_STAGE)
cdns3_ep0_complete_setup(priv_dev, 0, 1);
}
static void cdns3_transfer_completed(struct cdns3_device *priv_dev)
{
struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
if (!list_empty(&priv_ep->pending_req_list)) {
struct usb_request *request;
trace_cdns3_complete_trb(priv_ep, priv_ep->trb_pool);
request = cdns3_next_request(&priv_ep->pending_req_list);
/* Invalidate TRB before accessing it */
invalidate_dcache_range((unsigned long)priv_ep->trb_pool,
(unsigned long)priv_ep->trb_pool +
ROUND(sizeof(struct cdns3_trb),
CONFIG_SYS_CACHELINE_SIZE));
request->actual =
TRB_LEN(le32_to_cpu(priv_ep->trb_pool->length));
priv_ep->dir = priv_dev->ep0_data_dir;
cdns3_gadget_giveback(priv_ep, to_cdns3_request(request), 0);
}
cdns3_ep0_complete_setup(priv_dev, 0, 0);
}
/**
* cdns3_check_new_setup - Check if controller receive new SETUP packet.
* @priv_dev: extended gadget object
*
* The SETUP packet can be kept in on-chip memory or in system memory.
*/
static bool cdns3_check_new_setup(struct cdns3_device *priv_dev)
{
u32 ep_sts_reg;
cdns3_select_ep(priv_dev, 0 | USB_DIR_OUT);
ep_sts_reg = readl(&priv_dev->regs->ep_sts);
return !!(ep_sts_reg & (EP_STS_SETUP | EP_STS_STPWAIT));
}
/**
* cdns3_check_ep0_interrupt_proceed - Processes interrupt related to endpoint 0
* @priv_dev: extended gadget object
* @dir: USB_DIR_IN for IN direction, USB_DIR_OUT for OUT direction
*/
void cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir)
{
u32 ep_sts_reg;
cdns3_select_ep(priv_dev, dir);
ep_sts_reg = readl(&priv_dev->regs->ep_sts);
writel(ep_sts_reg, &priv_dev->regs->ep_sts);
trace_cdns3_ep0_irq(priv_dev, ep_sts_reg);
__pending_setup_status_handler(priv_dev);
if (ep_sts_reg & EP_STS_SETUP)
priv_dev->wait_for_setup = 1;
if (priv_dev->wait_for_setup && ep_sts_reg & EP_STS_IOC) {
priv_dev->wait_for_setup = 0;
cdns3_allow_enable_l1(priv_dev, 0);
cdns3_ep0_setup_phase(priv_dev);
} else if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) {
priv_dev->ep0_data_dir = dir;
cdns3_transfer_completed(priv_dev);
}
if (ep_sts_reg & EP_STS_DESCMIS) {
if (dir == 0 && !priv_dev->setup_pending)
cdns3_prepare_setup_packet(priv_dev);
}
}
/**
* cdns3_gadget_ep0_enable
* Function shouldn't be called by gadget driver,
* endpoint 0 is allways active
*/
static int cdns3_gadget_ep0_enable(struct usb_ep *ep,
const struct usb_endpoint_descriptor *desc)
{
return -EINVAL;
}
/**
* cdns3_gadget_ep0_disable
* Function shouldn't be called by gadget driver,
* endpoint 0 is allways active
*/
static int cdns3_gadget_ep0_disable(struct usb_ep *ep)
{
return -EINVAL;
}
/**
* cdns3_gadget_ep0_set_halt
* @ep: pointer to endpoint zero object
* @value: 1 for set stall, 0 for clear stall
*
* Returns 0
*/
static int cdns3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
{
/* TODO */
return 0;
}
/**
* cdns3_gadget_ep0_queue Transfer data on endpoint zero
* @ep: pointer to endpoint zero object
* @request: pointer to request object
* @gfp_flags: gfp flags
*
* Returns 0 on success, error code elsewhere
*/
static int cdns3_gadget_ep0_queue(struct usb_ep *ep,
struct usb_request *request,
gfp_t gfp_flags)
{
struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
unsigned long flags;
int erdy_sent = 0;
int ret = 0;
u8 zlp = 0;
trace_cdns3_ep0_queue(priv_dev, request);
/* cancel the request if controller receive new SETUP packet. */
if (cdns3_check_new_setup(priv_dev))
return -ECONNRESET;
/* send STATUS stage. Should be called only for SET_CONFIGURATION */
if (priv_dev->ep0_stage == CDNS3_STATUS_STAGE) {
spin_lock_irqsave(&priv_dev->lock, flags);
cdns3_select_ep(priv_dev, 0x00);
erdy_sent = !priv_dev->hw_configured_flag;
cdns3_set_hw_configuration(priv_dev);
if (!erdy_sent)
cdns3_ep0_complete_setup(priv_dev, 0, 1);
cdns3_allow_enable_l1(priv_dev, 1);
request->actual = 0;
priv_dev->status_completion_no_call = true;
priv_dev->pending_status_request = request;
spin_unlock_irqrestore(&priv_dev->lock, flags);
/*
* Since there is no completion interrupt for status stage,
* it needs to call ->completion in software after
* ep0_queue is back.
*/
#ifndef __UBOOT__
queue_work(system_freezable_wq, &priv_dev->pending_status_wq);
#else
__pending_setup_status_handler(priv_dev);
#endif
return 0;
}
spin_lock_irqsave(&priv_dev->lock, flags);
if (!list_empty(&priv_ep->pending_req_list)) {
dev_err(priv_dev->dev,
"can't handle multiple requests for ep0\n");
spin_unlock_irqrestore(&priv_dev->lock, flags);
return -EBUSY;
}
ret = usb_gadget_map_request(&priv_dev->gadget, request,
priv_dev->ep0_data_dir);
if (ret) {
spin_unlock_irqrestore(&priv_dev->lock, flags);
dev_err(priv_dev->dev, "failed to map request\n");
return -EINVAL;
}
request->status = -EINPROGRESS;
list_add_tail(&request->list, &priv_ep->pending_req_list);
if (request->zero && request->length &&
(request->length % ep->maxpacket == 0))
zlp = 1;
cdns3_ep0_run_transfer(priv_dev, request->dma, request->length, 1, zlp);
spin_unlock_irqrestore(&priv_dev->lock, flags);
return ret;
}
/**
* cdns3_gadget_ep_set_wedge Set wedge on selected endpoint
* @ep: endpoint object
*
* Returns 0
*/
int cdns3_gadget_ep_set_wedge(struct usb_ep *ep)
{
struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
dev_dbg(priv_dev->dev, "Wedge for %s\n", ep->name);
cdns3_gadget_ep_set_halt(ep, 1);
priv_ep->flags |= EP_WEDGE;
return 0;
}
const struct usb_ep_ops cdns3_gadget_ep0_ops = {
.enable = cdns3_gadget_ep0_enable,
.disable = cdns3_gadget_ep0_disable,
.alloc_request = cdns3_gadget_ep_alloc_request,
.free_request = cdns3_gadget_ep_free_request,
.queue = cdns3_gadget_ep0_queue,
.dequeue = cdns3_gadget_ep_dequeue,
.set_halt = cdns3_gadget_ep0_set_halt,
.set_wedge = cdns3_gadget_ep_set_wedge,
};
/**
* cdns3_ep0_config - Configures default endpoint
* @priv_dev: extended gadget object
*
* Functions sets parameters: maximal packet size and enables interrupts
*/
void cdns3_ep0_config(struct cdns3_device *priv_dev)
{
struct cdns3_usb_regs __iomem *regs;
struct cdns3_endpoint *priv_ep;
u32 max_packet_size = 64;
regs = priv_dev->regs;
if (priv_dev->gadget.speed == USB_SPEED_SUPER)
max_packet_size = 512;
priv_ep = priv_dev->eps[0];
if (!list_empty(&priv_ep->pending_req_list)) {
struct usb_request *request;
request = cdns3_next_request(&priv_ep->pending_req_list);
list_del_init(&request->list);
}
priv_dev->u1_allowed = 0;
priv_dev->u2_allowed = 0;
priv_dev->gadget.ep0->maxpacket = max_packet_size;
cdns3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(max_packet_size);
/* init ep out */
cdns3_select_ep(priv_dev, USB_DIR_OUT);
if (priv_dev->dev_ver >= DEV_VER_V3) {
cdns3_set_register_bit(&priv_dev->regs->dtrans,
BIT(0) | BIT(16));
cdns3_set_register_bit(&priv_dev->regs->tdl_from_trb,
BIT(0) | BIT(16));
}
writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size),
&regs->ep_cfg);
writel(EP_STS_EN_SETUPEN | EP_STS_EN_DESCMISEN | EP_STS_EN_TRBERREN,
&regs->ep_sts_en);
/* init ep in */
cdns3_select_ep(priv_dev, USB_DIR_IN);
writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size),
&regs->ep_cfg);
writel(EP_STS_EN_SETUPEN | EP_STS_EN_TRBERREN, &regs->ep_sts_en);
cdns3_set_register_bit(&regs->usb_conf, USB_CONF_U1DS | USB_CONF_U2DS);
}
/**
* cdns3_init_ep0 Initializes software endpoint 0 of gadget
* @priv_dev: extended gadget object
* @ep_priv: extended endpoint object
*
* Returns 0 on success else error code.
*/
int cdns3_init_ep0(struct cdns3_device *priv_dev,
struct cdns3_endpoint *priv_ep)
{
sprintf(priv_ep->name, "ep0");
/* fill linux fields */
priv_ep->endpoint.ops = &cdns3_gadget_ep0_ops;
priv_ep->endpoint.maxburst = 1;
usb_ep_set_maxpacket_limit(&priv_ep->endpoint,
CDNS3_EP0_MAX_PACKET_LIMIT);
#ifndef __UBOOT__
priv_ep->endpoint.address = 0;
#endif
priv_ep->endpoint.caps.type_control = 1;
priv_ep->endpoint.caps.dir_in = 1;
priv_ep->endpoint.caps.dir_out = 1;
priv_ep->endpoint.name = priv_ep->name;
priv_ep->endpoint.desc = &cdns3_gadget_ep0_desc;
priv_dev->gadget.ep0 = &priv_ep->endpoint;
priv_ep->type = USB_ENDPOINT_XFER_CONTROL;
return cdns3_allocate_trb_pool(priv_ep);
}

View File

@ -0,0 +1,28 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Cadence USBSS DRD Driver - Gadget Export APIs.
*
* Copyright (C) 2017 NXP
* Copyright (C) 2017-2018 NXP
*
* Authors: Peter Chen <peter.chen@nxp.com>
*/
#ifndef __LINUX_CDNS3_GADGET_EXPORT
#define __LINUX_CDNS3_GADGET_EXPORT
#ifdef CONFIG_USB_CDNS3_GADGET
int cdns3_gadget_init(struct cdns3 *cdns);
void cdns3_gadget_exit(struct cdns3 *cdns);
#else
static inline int cdns3_gadget_init(struct cdns3 *cdns)
{
return -ENXIO;
}
static inline void cdns3_gadget_exit(struct cdns3 *cdns) { }
#endif
#endif /* __LINUX_CDNS3_GADGET_EXPORT */

2738
drivers/usb/cdns3/gadget.c Normal file

File diff suppressed because it is too large Load Diff

1338
drivers/usb/cdns3/gadget.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,28 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Cadence USBSS DRD Driver - Host Export APIs
*
* Copyright (C) 2017-2018 NXP
*
* Authors: Peter Chen <peter.chen@nxp.com>
*/
#ifndef __LINUX_CDNS3_HOST_EXPORT
#define __LINUX_CDNS3_HOST_EXPORT
#ifdef CONFIG_USB_CDNS3_HOST
int cdns3_host_init(struct cdns3 *cdns);
void cdns3_host_exit(struct cdns3 *cdns);
#else
static inline int cdns3_host_init(struct cdns3 *cdns)
{
return -ENXIO;
}
static inline void cdns3_host_exit(struct cdns3 *cdns) { }
#endif /* CONFIG_USB_CDNS3_HOST */
#endif /* __LINUX_CDNS3_HOST_EXPORT */

55
drivers/usb/cdns3/host.c Normal file
View File

@ -0,0 +1,55 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Cadence USBSS DRD Driver - host side
*
* Copyright (C) 2018-2019 Cadence Design Systems.
* Copyright (C) 2017-2018 NXP
*
* Authors: Peter Chen <peter.chen@nxp.com>
* Pawel Laszczak <pawell@cadence.com>
*/
#include <dm.h>
#include <linux/compat.h>
#include <usb.h>
#include "../host/xhci.h"
#include "core.h"
#include "drd.h"
static int __cdns3_host_init(struct cdns3 *cdns)
{
struct xhci_hcor *hcor;
struct xhci_hccr *hccr;
cdns3_drd_switch_host(cdns, 1);
hccr = (struct xhci_hccr *)cdns->xhci_regs;
hcor = (struct xhci_hcor *)(cdns->xhci_regs +
HC_LENGTH(xhci_readl(&(hccr)->cr_capbase)));
return xhci_register(cdns->dev, hccr, hcor);
}
static void cdns3_host_exit(struct cdns3 *cdns)
{
xhci_deregister(cdns->dev);
cdns3_drd_switch_host(cdns, 0);
}
int cdns3_host_init(struct cdns3 *cdns)
{
struct cdns3_role_driver *rdrv;
rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL);
if (!rdrv)
return -ENOMEM;
rdrv->start = __cdns3_host_init;
rdrv->stop = cdns3_host_exit;
rdrv->state = CDNS3_ROLE_STATE_INACTIVE;
rdrv->name = "host";
cdns->roles[USB_ROLE_HOST] = rdrv;
return 0;
}

11
drivers/usb/cdns3/trace.c Normal file
View File

@ -0,0 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
/*
* USBSS device controller driver Trace Support
*
* Copyright (C) 2018-2019 Cadence.
*
* Author: Pawel Laszczak <pawell@cadence.com>
*/
#define CREATE_TRACE_POINTS
#include "trace.h"

26
drivers/usb/cdns3/trace.h Normal file
View File

@ -0,0 +1,26 @@
/* SPDX-License-Identifier: GPL-2.0 */
#define trace_cdns3_prepare_trb(a, b)
#define trace_cdns3_doorbell_ep0(a, b)
#define trace_cdns3_ctrl_req(a)
#define trace_cdns3_complete_trb(a, b)
#define trace_cdns3_ep0_irq(a, b)
#define trace_cdns3_gadget_giveback(a)
#define trace_cdns3_free_aligned_request(a)
#define trace_cdns3_prepare_aligned_request(a)
#define trace_cdns3_ring(a)
#define trace_cdns3_doorbell_epx(a, b)
#define trace_cdns3_request_handled(a, b, c)
#define trace_cdns3_epx_irq(a, b)
#define trace_cdns3_usb_irq(a, b)
#define trace_cdns3_alloc_request(a)
#define trace_cdns3_free_request(a)
#define trace_cdns3_gadget_ep_enable(a)
#define trace_cdns3_gadget_ep_disable(a)
#define trace_cdns3_ep0_queue(a, b)
#define trace_cdns3_ep0_dequeue(a)
#define trace_cdns3_ep_queue(a)
#define trace_cdns3_ep_dequeue(a)
#define trace_cdns3_halt(a, b, c)
#define trace_cdns3_wa1(a, b)
#define trace_cdns3_wa2(a, b)

View File

@ -88,6 +88,7 @@ endif
libs-y += drivers/
libs-$(CONFIG_SPL_USB_GADGET) += drivers/usb/dwc3/
libs-$(CONFIG_SPL_USB_GADGET) += drivers/usb/cdns3/
libs-y += dts/
libs-y += fs/
libs-$(CONFIG_SPL_POST_MEM_SUPPORT) += post/drivers/