1
0
Fork 0

MLK-18579-1 usb: cdns3: using upstream multiple TD driver

Keep the internal framework, just use the UDC driver from
upstream version which supports multiple TD.

Reviewed-by: Jun Li <jun.li@nxp.com>
Signed-off-by: Peter Chen <peter.chen@nxp.com>
5.4-rM2-2.2.x-imx-squashed
Peter Chen 2019-06-19 16:06:40 +08:00 committed by Dong Aisheng
parent a07b01773a
commit b9fb073145
10 changed files with 4644 additions and 2245 deletions

View File

@ -1,5 +1,5 @@
obj-$(CONFIG_USB_CDNS3) += cdns3.o
cdns3-y := core.o
cdns3-$(CONFIG_USB_CDNS3_GADGET) += gadget.o
cdns3-$(CONFIG_USB_CDNS3_GADGET) += gadget.o ep0.o trace.o
cdns3-$(CONFIG_USB_CDNS3_HOST) += host.o

View File

@ -1,7 +1,7 @@
/**
* core.c - Cadence USB3 DRD Controller Core file
*
* Copyright 2017 NXP
* Copyright 2017-2019 NXP
*
* Authors: Peter Chen <peter.chen@nxp.com>
*
@ -36,6 +36,39 @@
#include "host-export.h"
#include "gadget-export.h"
/**
* cdns3_handshake - spin reading until handshake completes or fails
* @ptr: address of device controller register to be read
* @mask: bits to look at in result of read
* @done: value of those bits when handshake succeeds
* @usec: timeout in microseconds
*
* Returns negative errno, or zero on success
*
* Success happens when the "mask" bits have the specified value (hardware
* handshake done). There are two failure modes: "usec" have passed (major
* hardware flakeout), or the register reads as all-ones (hardware removed).
*/
int cdns3_handshake(void __iomem *ptr, u32 mask, u32 done, int usec)
{
u32 result;
do {
result = readl(ptr);
if (result == ~(u32)0) /* card removed */
return -ENODEV;
result &= mask;
if (result == done)
return 0;
udelay(1);
usec--;
} while (usec > 0);
return -ETIMEDOUT;
}
static void cdns3_usb_phy_init(void __iomem *regs)
{
u32 value;
@ -311,6 +344,18 @@ static irqreturn_t cdns3_irq(int irq, void *data)
return ret;
}
static irqreturn_t cdns3_thread_irq(int irq, void *data)
{
struct cdns3 *cdns = data;
irqreturn_t ret = IRQ_NONE;
/* Handle device/host interrupt */
if (cdns->role != CDNS3_ROLE_END && cdns3_role(cdns)->thread_irq)
ret = cdns3_role(cdns)->thread_irq(cdns);
return ret;
}
static int cdns3_get_clks(struct device *dev)
{
struct cdns3 *cdns = dev_get_drvdata(dev);
@ -388,7 +433,7 @@ static void cdns3_disable_unprepare_clks(struct device *dev)
static void cdns3_remove_roles(struct cdns3 *cdns)
{
cdns3_gadget_remove(cdns);
cdns3_gadget_exit(cdns);
cdns3_host_remove(cdns);
}
@ -656,8 +701,8 @@ static int cdns3_probe(struct platform_device *pdev)
goto err3;
}
ret = devm_request_irq(dev, cdns->irq, cdns3_irq, IRQF_SHARED,
dev_name(dev), cdns);
ret = devm_request_threaded_irq(dev, cdns->irq, cdns3_irq,
cdns3_thread_irq, IRQF_SHARED, dev_name(dev), cdns);
if (ret)
goto err4;

View File

@ -1,7 +1,7 @@
/**
* core.h - Cadence USB3 DRD Controller Core header file
*
* Copyright 2017 NXP
* Copyright 2017-2019 NXP
*
* Authors: Peter Chen <peter.chen@nxp.com>
*
@ -35,6 +35,7 @@ enum cdns3_roles {
* @suspend: suspend callback for this role
* @resume: resume callback for this role
* @irq: irq handler for this role
* @thread_irq: thread irq handler for this role
* @name: role name string (host/gadget)
*/
struct cdns3_role_driver {
@ -43,6 +44,7 @@ struct cdns3_role_driver {
int (*suspend)(struct cdns3 *, bool do_wakeup);
int (*resume)(struct cdns3 *, bool hibernated);
irqreturn_t (*irq)(struct cdns3 *);
irqreturn_t (*thread_irq)(struct cdns3 *);
const char *name;
};
@ -74,7 +76,7 @@ struct cdns3 {
struct device *dev;
void __iomem *xhci_regs;
struct resource *xhci_res;
struct usbss_dev_register_block_type __iomem *dev_regs;
struct cdns3_usb_regs __iomem *dev_regs;
void __iomem *none_core_regs;
void __iomem *phy_regs;
void __iomem *otg_regs;
@ -82,7 +84,7 @@ struct cdns3 {
struct cdns3_role_driver *roles[CDNS3_ROLE_END];
enum cdns3_roles role;
struct device *host_dev;
struct device *gadget_dev;
struct cdns3_device *gadget_dev;
struct usb_phy *usbphy;
struct clk *cdns3_clks[CDNS3_NUM_OF_CLKS];
struct extcon_dev *extcon;
@ -127,5 +129,6 @@ static inline void cdns3_role_stop(struct cdns3 *cdns)
cdns->role = CDNS3_ROLE_END;
mutex_unlock(&cdns->mutex);
}
int cdns3_handshake(void __iomem *ptr, u32 mask, u32 done, int usec);
#endif /* __DRIVERS_USB_CDNS3_CORE_H */

View File

@ -0,0 +1,155 @@
/* 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
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_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;
}
void cdns3_dbg(struct cdns3_device *priv_dev, const char *fmt, ...);
#endif /*__LINUX_CDNS3_DEBUG*/

View File

@ -0,0 +1,914 @@
// 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/delay.h>
#include <linux/interrupt.h>
#include <linux/usb/composite.h>
#include "gadget.h"
#include "trace.h"
#include "core.h"
static struct usb_endpoint_descriptor cdns3_gadget_ep0_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_CONTROL,
};
/**
* cdns3_ep0_run_transfer - Do transfer on default endpoint hardware
* @priv_dev: extended gadget object
* @dma_addr: physical address where data is/will be stored
* @length: data length
* @erdy: set it to 1 when ERDY packet should be sent -
* exit from flow control state
*/
static void cdns3_ep0_run_transfer(struct cdns3_device *priv_dev,
dma_addr_t dma_addr,
unsigned int length, int erdy)
{
struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
priv_ep->trb_pool->buffer = TRB_BUFFER(dma_addr);
priv_ep->trb_pool->length = TRB_LEN(length);
priv_ep->trb_pool->control = TRB_CYCLE | TRB_IOC | TRB_TYPE(TRB_NORMAL);
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);
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);
}
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) {
cdns3_dbg(priv_ep->cdns3_dev, "STALL for ep0\n");
/* 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_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;
u32 reg;
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;
reg = readl(&priv_dev->regs->usb_sts);
if (priv_dev->u1_allowed)
usb_status |= BIT(USB_DEV_STAT_U1_ENABLED);
if (priv_dev->u2_allowed)
usb_status |= BIT(USB_DEV_STAT_U2_ENABLED);
break;
case USB_RECIP_INTERFACE:
return cdns3_ep0_delegate_req(priv_dev, ctrl);
case USB_RECIP_ENDPOINT:
/* check if endpoint is stalled */
cdns3_select_ep(priv_dev, ctrl->wIndex);
if (EP_STS_STALL(readl(&priv_dev->regs->ep_sts)))
usb_status = BIT(USB_ENDPOINT_HALT);
break;
default:
return -EINVAL;
}
response_pkt = (__le16 *)priv_dev->setup_buf;
*response_pkt = cpu_to_le16(usb_status);
cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma,
sizeof(*response_pkt), 1);
return 0;
}
static int cdns3_ep0_feature_handle_device(struct cdns3_device *priv_dev,
struct usb_ctrlrequest *ctrl,
int set)
{
enum usb_device_state state;
enum usb_device_speed speed;
int ret = 0;
u32 wValue;
u32 wIndex;
u16 tmode;
wValue = le16_to_cpu(ctrl->wValue);
wIndex = le16_to_cpu(ctrl->wIndex);
state = priv_dev->gadget.state;
speed = priv_dev->gadget.speed;
switch (ctrl->wValue) {
case USB_DEVICE_REMOTE_WAKEUP:
priv_dev->wake_up_flag = !!set;
break;
case USB_DEVICE_U1_ENABLE:
if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER)
return -EINVAL;
priv_dev->u1_allowed = !!set;
break;
case USB_DEVICE_U2_ENABLE:
if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER)
return -EINVAL;
priv_dev->u2_allowed = !!set;
break;
case USB_DEVICE_LTM_ENABLE:
ret = -EINVAL;
break;
case USB_DEVICE_TEST_MODE:
if (state != USB_STATE_CONFIGURED || speed > USB_SPEED_HIGH)
return -EINVAL;
tmode = le16_to_cpu(ctrl->wIndex);
if (!set || (tmode & 0xff) != 0)
return -EINVAL;
switch (tmode >> 8) {
case TEST_J:
case TEST_K:
case TEST_SE0_NAK:
case TEST_PACKET:
cdns3_ep0_complete_setup(priv_dev, 0, 1);
/**
* Little delay to give the controller some time
* for sending status stage.
* This time should be less then 3ms.
*/
usleep_range(1000, 2000);
cdns3_set_register_bit(&priv_dev->regs->usb_cmd,
USB_CMD_STMODE |
USB_STS_TMODE_SEL(tmode - 1));
break;
default:
ret = -EINVAL;
}
break;
default:
ret = -EINVAL;
}
return ret;
}
static int cdns3_ep0_feature_handle_intf(struct cdns3_device *priv_dev,
struct usb_ctrlrequest *ctrl,
int set)
{
u32 wValue;
int ret = 0;
wValue = le16_to_cpu(ctrl->wValue);
switch (wValue) {
case USB_INTRF_FUNC_SUSPEND:
break;
default:
ret = -EINVAL;
}
return ret;
}
static int cdns3_ep0_feature_handle_endpoint(struct cdns3_device *priv_dev,
struct usb_ctrlrequest *ctrl,
int set)
{
struct cdns3_endpoint *priv_ep;
int ret = 0;
u8 index;
if (le16_to_cpu(ctrl->wValue) != USB_ENDPOINT_HALT)
return -EINVAL;
if (!(ctrl->wIndex & ~USB_DIR_IN))
return 0;
index = cdns3_ep_addr_to_index(ctrl->wIndex);
priv_ep = priv_dev->eps[index];
cdns3_select_ep(priv_dev, ctrl->wIndex);
if (set) {
cdns3_dbg(priv_ep->cdns3_dev, "Stall endpoint %s\n",
priv_ep->name);
writel(EP_CMD_SSTALL, &priv_dev->regs->ep_cmd);
priv_ep->flags |= EP_STALL;
} else {
struct usb_request *request;
if (priv_dev->eps[index]->flags & EP_WEDGE) {
cdns3_select_ep(priv_dev, 0x00);
return 0;
}
cdns3_dbg(priv_ep->cdns3_dev, "Clear Stalled endpoint %s\n",
priv_ep->name);
writel(EP_CMD_CSTALL | EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
/* wait for EPRST cleared */
ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
EP_CMD_EPRST, 0, 100);
if (ret)
return -EINVAL;
priv_ep->flags &= ~EP_STALL;
request = cdns3_next_request(&priv_ep->pending_req_list);
if (request) {
cdns3_dbg(priv_ep->cdns3_dev, "Resume transfer for %s\n",
priv_ep->name);
cdns3_rearm_transfer(priv_ep, 1);
}
}
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);
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_gadget_ep_giveback - call struct usb_request's ->complete callback
* @priv_ep: The endpoint to whom the request belongs to
* @priv_req: The request we're giving back
* @status: completion code for the request
*
* Must be called with controller's lock held and interrupts disabled. This
* function will unmap @req and call its ->complete() callback to notify upper
* layers that it has completed.
*/
void cdns3_gadget_ep0_giveback(struct cdns3_device *priv_dev,
int status)
{
struct cdns3_endpoint *priv_ep;
struct usb_request *request;
priv_ep = priv_dev->eps[0];
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), status);
}
/**
* cdns3_ep0_setup_phase - Handling setup USB requests
* @priv_dev: extended gadget object
*/
static void cdns3_ep0_setup_phase(struct cdns3_device *priv_dev)
{
struct usb_ctrlrequest *ctrl = priv_dev->setup_buf;
struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
int result;
priv_dev->ep0_data_dir = ctrl->bRequestType & USB_DIR_IN;
trace_cdns3_ctrl_req(ctrl);
if (!list_empty(&priv_ep->pending_req_list)) {
struct usb_request *request;
request = cdns3_next_request(&priv_ep->pending_req_list);
priv_ep->dir = priv_dev->ep0_data_dir;
cdns3_gadget_giveback(priv_ep, to_cdns3_request(request),
-ECONNRESET);
}
if (le16_to_cpu(ctrl->wLength))
priv_dev->ep0_stage = CDNS3_DATA_STAGE;
else
priv_dev->ep0_stage = CDNS3_STATUS_STAGE;
if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
result = cdns3_ep0_standard_request(priv_dev, ctrl);
else
result = cdns3_ep0_delegate_req(priv_dev, ctrl);
if (result == USB_GADGET_DELAYED_STATUS)
return;
if (result < 0)
cdns3_ep0_complete_setup(priv_dev, 1, 1);
else if (priv_dev->ep0_stage == CDNS3_STATUS_STAGE)
cdns3_ep0_complete_setup(priv_dev, 0, 1);
}
static void cdns3_transfer_completed(struct cdns3_device *priv_dev)
{
struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
if (!list_empty(&priv_ep->pending_req_list)) {
struct usb_request *request;
trace_cdns3_complete_trb(priv_ep, priv_ep->trb_pool);
request = cdns3_next_request(&priv_ep->pending_req_list);
request->actual =
TRB_LEN(le32_to_cpu(priv_ep->trb_pool->length));
priv_ep->dir = priv_dev->ep0_data_dir;
if (request->zero && request->length && priv_ep->dir
&& (request->length % priv_dev->gadget.ep0->maxpacket == 0))
cdns3_ep0_run_transfer(priv_dev, request->dma, 0, 1);
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)) {
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;
cdns3_dbg(priv_ep->cdns3_dev, "Queue to Ep0%s L: %d\n",
priv_dev->ep0_data_dir ? "IN" : "OUT",
request->length);
/* 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);
request->actual = 0;
priv_dev->status_completion_no_call = true;
priv_dev->pending_status_request = request;
spin_unlock_irqrestore(&priv_dev->lock, flags);
/*
* Since there is no completion interrupt for status stage,
* it needs to call ->completion in software after
* ep0_queue is back.
*/
queue_work(system_freezable_wq, &priv_dev->pending_status_wq);
return 0;
}
spin_lock_irqsave(&priv_dev->lock, flags);
if (!list_empty(&priv_ep->pending_req_list)) {
dev_err(priv_dev->dev,
"can't handle multiple requests for ep0\n");
spin_unlock_irqrestore(&priv_dev->lock, flags);
return -EBUSY;
}
ret = usb_gadget_map_request_by_dev(priv_dev->sysdev, request,
priv_dev->ep0_data_dir);
if (ret) {
spin_unlock_irqrestore(&priv_dev->lock, flags);
dev_err(priv_dev->dev, "failed to map request\n");
return -EINVAL;
}
request->status = -EINPROGRESS;
list_add_tail(&request->list, &priv_ep->pending_req_list);
cdns3_ep0_run_transfer(priv_dev, request->dma, request->length, 1);
spin_unlock_irqrestore(&priv_dev->lock, flags);
return ret;
}
/**
* cdns3_gadget_ep_set_wedge Set wedge on selected endpoint
* @ep: endpoint object
*
* Returns 0
*/
int cdns3_gadget_ep_set_wedge(struct usb_ep *ep)
{
struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
dev_dbg(priv_dev->dev, "Wedge for %s\n", ep->name);
cdns3_gadget_ep_set_halt(ep, 1);
priv_ep->flags |= EP_WEDGE;
return 0;
}
const struct usb_ep_ops cdns3_gadget_ep0_ops = {
.enable = cdns3_gadget_ep0_enable,
.disable = cdns3_gadget_ep0_disable,
.alloc_request = cdns3_gadget_ep_alloc_request,
.free_request = cdns3_gadget_ep_free_request,
.queue = cdns3_gadget_ep0_queue,
.dequeue = cdns3_gadget_ep_dequeue,
.set_halt = cdns3_gadget_ep0_set_halt,
.set_wedge = cdns3_gadget_ep_set_wedge,
};
/**
* cdns3_ep0_config - Configures default endpoint
* @priv_dev: extended gadget object
*
* Functions sets parameters: maximal packet size and enables interrupts
*/
void cdns3_ep0_config(struct cdns3_device *priv_dev)
{
struct cdns3_usb_regs __iomem *regs;
struct cdns3_endpoint *priv_ep;
u32 max_packet_size = 64;
regs = priv_dev->regs;
if (priv_dev->gadget.speed == USB_SPEED_SUPER)
max_packet_size = 512;
priv_ep = priv_dev->eps[0];
if (!list_empty(&priv_ep->pending_req_list)) {
struct usb_request *request;
request = cdns3_next_request(&priv_ep->pending_req_list);
list_del_init(&request->list);
}
priv_dev->u1_allowed = 0;
priv_dev->u2_allowed = 0;
priv_dev->gadget.ep0->maxpacket = max_packet_size;
cdns3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(max_packet_size);
/* init ep out */
cdns3_select_ep(priv_dev, USB_DIR_OUT);
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);
priv_ep->endpoint.address = 0;
priv_ep->endpoint.caps.type_control = 1;
priv_ep->endpoint.caps.dir_in = 1;
priv_ep->endpoint.caps.dir_out = 1;
priv_ep->endpoint.name = priv_ep->name;
priv_ep->endpoint.desc = &cdns3_gadget_ep0_desc;
priv_dev->gadget.ep0 = &priv_ep->endpoint;
priv_ep->type = USB_ENDPOINT_XFER_CONTROL;
return cdns3_allocate_trb_pool(priv_ep);
}

View File

@ -1,24 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* gadget-export.h - Gadget Export APIs
* Cadence USBSS DRD Driver - Gadget Export APIs.
*
* Copyright (C) 2017-2019 NXP
*
* Copyright 2017 NXP
* Authors: Peter Chen <peter.chen@nxp.com>
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
#ifndef __CDNS3_GADGET_EXPORT_H
#define __CDNS3_GADGET_EXPORT_H
#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_remove(struct cdns3 *cdns);
void cdns3_gadget_exit(struct cdns3 *cdns);
#else
static inline int cdns3_gadget_init(struct cdns3 *cdns)
@ -26,11 +20,8 @@ static inline int cdns3_gadget_init(struct cdns3 *cdns)
return -ENXIO;
}
static inline void cdns3_gadget_remove(struct cdns3 *cdns)
{
}
static inline void cdns3_gadget_exit(struct cdns3 *cdns) { }
#endif
#endif /* __CDNS3_GADGET_EXPORT_H */
#endif /* __LINUX_CDNS3_GADGET_EXPORT */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,23 @@
// SPDX-License-Identifier: GPL-2.0
/*
* USBSS device controller driver Trace Support
*
* Copyright (C) 2018 Cadence.
*
* Author: Pawel Laszczak <pawell@cadence.com>
*/
#define CREATE_TRACE_POINTS
#include "trace.h"
void cdns3_dbg(struct cdns3_device *priv_dev, const char *fmt, ...)
{
struct va_format vaf;
va_list args;
va_start(args, fmt);
vaf.fmt = fmt;
vaf.va = &args;
trace_cdns3_log(priv_dev, &vaf);
va_end(args);
}

View File

@ -0,0 +1,446 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* USBSS device controller driver.
* Trace support header file.
*
* Copyright (C) 2018 Cadence.
*
* Author: Pawel Laszczak <pawell@cadence.com>
*/
#undef TRACE_SYSTEM
#define TRACE_SYSTEM cdns3
#if !defined(__LINUX_CDNS3_TRACE) || defined(TRACE_HEADER_MULTI_READ)
#define __LINUX_CDNS3_TRACE
#include <linux/types.h>
#include <linux/tracepoint.h>
#include <asm/byteorder.h>
#include <linux/usb/ch9.h>
#include "gadget.h"
#include "debug.h"
#define CDNS3_MSG_MAX 500
TRACE_EVENT(cdns3_log,
TP_PROTO(struct cdns3_device *priv_dev, struct va_format *vaf),
TP_ARGS(priv_dev, vaf),
TP_STRUCT__entry(
__string(name, dev_name(priv_dev->dev))
__dynamic_array(char, msg, CDNS3_MSG_MAX)
),
TP_fast_assign(
__assign_str(name, dev_name(priv_dev->dev));
vsnprintf(__get_str(msg), CDNS3_MSG_MAX, vaf->fmt, *vaf->va);
),
TP_printk("%s: %s", __get_str(name), __get_str(msg))
);
DECLARE_EVENT_CLASS(cdns3_log_doorbell,
TP_PROTO(const char *ep_name, u32 ep_trbaddr),
TP_ARGS(ep_name, ep_trbaddr),
TP_STRUCT__entry(
__string(name, ep_name)
__field(u32, ep_trbaddr)
),
TP_fast_assign(
__assign_str(name, ep_name);
__entry->ep_trbaddr = ep_trbaddr;
),
TP_printk("//Ding Dong %s, ep_trbaddr %08x", __get_str(name),
__entry->ep_trbaddr)
);
DEFINE_EVENT(cdns3_log_doorbell, cdns3_doorbell_ep0,
TP_PROTO(const char *ep_name, u32 ep_trbaddr),
TP_ARGS(ep_name, ep_trbaddr)
);
DEFINE_EVENT(cdns3_log_doorbell, cdns3_doorbell_epx,
TP_PROTO(const char *ep_name, u32 ep_trbaddr),
TP_ARGS(ep_name, ep_trbaddr)
);
DECLARE_EVENT_CLASS(cdns3_log_usb_irq,
TP_PROTO(struct cdns3_device *priv_dev, u32 usb_ists),
TP_ARGS(priv_dev, usb_ists),
TP_STRUCT__entry(
__field(enum usb_device_speed, speed)
__field(u32, usb_ists)
__dynamic_array(char, str, CDNS3_MSG_MAX)
),
TP_fast_assign(
__entry->speed = cdns3_get_speed(priv_dev);
__entry->usb_ists = usb_ists;
),
TP_printk("%s", cdns3_decode_usb_irq(__get_str(str), __entry->speed,
__entry->usb_ists))
);
DEFINE_EVENT(cdns3_log_usb_irq, cdns3_usb_irq,
TP_PROTO(struct cdns3_device *priv_dev, u32 usb_ists),
TP_ARGS(priv_dev, usb_ists)
);
DECLARE_EVENT_CLASS(cdns3_log_epx_irq,
TP_PROTO(struct cdns3_device *priv_dev, struct cdns3_endpoint *priv_ep),
TP_ARGS(priv_dev, priv_ep),
TP_STRUCT__entry(
__string(ep_name, priv_ep->name)
__field(u32, ep_sts)
__field(u32, ep_traddr)
__dynamic_array(char, str, CDNS3_MSG_MAX)
),
TP_fast_assign(
__assign_str(ep_name, priv_ep->name);
__entry->ep_sts = readl(&priv_dev->regs->ep_sts);
__entry->ep_traddr = readl(&priv_dev->regs->ep_traddr);
),
TP_printk("%s, ep_traddr: %08x",
cdns3_decode_epx_irq(__get_str(str),
__get_str(ep_name),
__entry->ep_sts),
__entry->ep_traddr)
);
DEFINE_EVENT(cdns3_log_epx_irq, cdns3_epx_irq,
TP_PROTO(struct cdns3_device *priv_dev, struct cdns3_endpoint *priv_ep),
TP_ARGS(priv_dev, priv_ep)
);
DECLARE_EVENT_CLASS(cdns3_log_ep0_irq,
TP_PROTO(struct cdns3_device *priv_dev, u32 ep_sts),
TP_ARGS(priv_dev, ep_sts),
TP_STRUCT__entry(
__field(int, ep_dir)
__field(u32, ep_sts)
__dynamic_array(char, str, CDNS3_MSG_MAX)
),
TP_fast_assign(
__entry->ep_dir = priv_dev->ep0_data_dir;
__entry->ep_sts = ep_sts;
),
TP_printk("%s", cdns3_decode_ep0_irq(__get_str(str),
__entry->ep_dir,
__entry->ep_sts))
);
DEFINE_EVENT(cdns3_log_ep0_irq, cdns3_ep0_irq,
TP_PROTO(struct cdns3_device *priv_dev, u32 ep_sts),
TP_ARGS(priv_dev, ep_sts)
);
DECLARE_EVENT_CLASS(cdns3_log_ctrl,
TP_PROTO(struct usb_ctrlrequest *ctrl),
TP_ARGS(ctrl),
TP_STRUCT__entry(
__field(u8, bRequestType)
__field(u8, bRequest)
__field(u16, wValue)
__field(u16, wIndex)
__field(u16, wLength)
__dynamic_array(char, str, CDNS3_MSG_MAX)
),
TP_fast_assign(
__entry->bRequestType = ctrl->bRequestType;
__entry->bRequest = ctrl->bRequest;
__entry->wValue = le16_to_cpu(ctrl->wValue);
__entry->wIndex = le16_to_cpu(ctrl->wIndex);
__entry->wLength = le16_to_cpu(ctrl->wLength);
),
TP_printk("%s", usb_decode_ctrl(__get_str(str), CDNS3_MSG_MAX,
__entry->bRequestType,
__entry->bRequest, __entry->wValue,
__entry->wIndex, __entry->wLength)
)
);
DEFINE_EVENT(cdns3_log_ctrl, cdns3_ctrl_req,
TP_PROTO(struct usb_ctrlrequest *ctrl),
TP_ARGS(ctrl)
);
DECLARE_EVENT_CLASS(cdns3_log_request,
TP_PROTO(struct cdns3_request *req),
TP_ARGS(req),
TP_STRUCT__entry(
__string(name, req->priv_ep->name)
__field(struct cdns3_request *, req)
__field(void *, buf)
__field(unsigned int, actual)
__field(unsigned int, length)
__field(int, status)
__field(int, zero)
__field(int, short_not_ok)
__field(int, no_interrupt)
__field(int, start_trb)
__field(int, end_trb)
__field(struct cdns3_trb *, start_trb_addr)
__field(int, flags)
),
TP_fast_assign(
__assign_str(name, req->priv_ep->name);
__entry->req = req;
__entry->buf = req->request.buf;
__entry->actual = req->request.actual;
__entry->length = req->request.length;
__entry->status = req->request.status;
__entry->zero = req->request.zero;
__entry->short_not_ok = req->request.short_not_ok;
__entry->no_interrupt = req->request.no_interrupt;
__entry->start_trb = req->start_trb;
__entry->end_trb = req->end_trb;
__entry->start_trb_addr = req->trb;
__entry->flags = req->flags;
),
TP_printk("%s: req: %p, req buff %p, length: %u/%u %s%s%s, status: %d,"
" trb: [start:%d, end:%d: virt addr %pa], flags:%x ",
__get_str(name), __entry->req, __entry->buf, __entry->actual,
__entry->length,
__entry->zero ? "zero | " : "",
__entry->short_not_ok ? "short | " : "",
__entry->no_interrupt ? "no int" : "",
__entry->status,
__entry->start_trb,
__entry->end_trb,
__entry->start_trb_addr,
__entry->flags
)
);
DEFINE_EVENT(cdns3_log_request, cdns3_alloc_request,
TP_PROTO(struct cdns3_request *req),
TP_ARGS(req)
);
DEFINE_EVENT(cdns3_log_request, cdns3_free_request,
TP_PROTO(struct cdns3_request *req),
TP_ARGS(req)
);
DEFINE_EVENT(cdns3_log_request, cdns3_ep_queue,
TP_PROTO(struct cdns3_request *req),
TP_ARGS(req)
);
DEFINE_EVENT(cdns3_log_request, cdns3_ep_dequeue,
TP_PROTO(struct cdns3_request *req),
TP_ARGS(req)
);
DEFINE_EVENT(cdns3_log_request, cdns3_gadget_giveback,
TP_PROTO(struct cdns3_request *req),
TP_ARGS(req)
);
DECLARE_EVENT_CLASS(cdns3_log_aligned_request,
TP_PROTO(struct cdns3_request *priv_req),
TP_ARGS(priv_req),
TP_STRUCT__entry(
__string(name, priv_req->priv_ep->name)
__field(struct usb_request *, req)
__field(void *, buf)
__field(dma_addr_t, dma)
__field(void *, aligned_buf)
__field(dma_addr_t, aligned_dma)
__field(u32, aligned_buf_size)
),
TP_fast_assign(
__assign_str(name, priv_req->priv_ep->name);
__entry->req = &priv_req->request;
__entry->buf = priv_req->request.buf;
__entry->dma = priv_req->request.dma;
__entry->aligned_buf = priv_req->aligned_buf->buf;
__entry->aligned_dma = priv_req->aligned_buf->dma;
__entry->aligned_buf_size = priv_req->aligned_buf->size;
),
TP_printk("%s: req: %p, req buf %p, dma %08llx a_buf %p a_dma %08llx, size %d",
__get_str(name), __entry->req, __entry->buf, __entry->dma,
__entry->aligned_buf, __entry->aligned_dma,
__entry->aligned_buf_size
)
);
DEFINE_EVENT(cdns3_log_aligned_request, cdns3_free_aligned_request,
TP_PROTO(struct cdns3_request *req),
TP_ARGS(req)
);
DEFINE_EVENT(cdns3_log_aligned_request, cdns3_prepare_aligned_request,
TP_PROTO(struct cdns3_request *req),
TP_ARGS(req)
);
DECLARE_EVENT_CLASS(cdns3_log_trb,
TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb),
TP_ARGS(priv_ep, trb),
TP_STRUCT__entry(
__string(name, priv_ep->name)
__field(struct cdns3_trb *, trb)
__field(u32, buffer)
__field(u32, length)
__field(u32, control)
__field(u32, type)
),
TP_fast_assign(
__assign_str(name, priv_ep->name);
__entry->trb = trb;
__entry->buffer = trb->buffer;
__entry->length = trb->length;
__entry->control = trb->control;
__entry->type = usb_endpoint_type(priv_ep->endpoint.desc);
),
TP_printk("%s: trb %p, dma buf: 0x%08x, size: %ld, burst: %d ctrl: 0x%08x (%s%s%s%s%s%s%s)",
__get_str(name), __entry->trb, __entry->buffer,
TRB_LEN(__entry->length),
(u8)TRB_BURST_LEN_GET(__entry->length),
__entry->control,
__entry->control & TRB_CYCLE ? "C=1, " : "C=0, ",
__entry->control & TRB_TOGGLE ? "T=1, " : "T=0, ",
__entry->control & TRB_ISP ? "ISP, " : "",
__entry->control & TRB_FIFO_MODE ? "FIFO, " : "",
__entry->control & TRB_CHAIN ? "CHAIN, " : "",
__entry->control & TRB_IOC ? "IOC, " : "",
TRB_FIELD_TO_TYPE(__entry->control) == TRB_NORMAL ? "Normal" : "LINK"
)
);
DEFINE_EVENT(cdns3_log_trb, cdns3_prepare_trb,
TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb),
TP_ARGS(priv_ep, trb)
);
DEFINE_EVENT(cdns3_log_trb, cdns3_complete_trb,
TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb),
TP_ARGS(priv_ep, trb)
);
DECLARE_EVENT_CLASS(cdns3_log_ring,
TP_PROTO(struct cdns3_endpoint *priv_ep),
TP_ARGS(priv_ep),
TP_STRUCT__entry(
__dynamic_array(u8, ring, TRB_RING_SIZE)
__dynamic_array(u8, priv_ep, sizeof(struct cdns3_endpoint))
__dynamic_array(char, buffer,
(TRBS_PER_SEGMENT * 65) + CDNS3_MSG_MAX)
),
TP_fast_assign(
memcpy(__get_dynamic_array(priv_ep), priv_ep,
sizeof(struct cdns3_endpoint));
memcpy(__get_dynamic_array(ring), priv_ep->trb_pool,
TRB_RING_SIZE);
),
TP_printk("%s",
cdns3_dbg_ring((struct cdns3_endpoint *)__get_str(priv_ep),
(struct cdns3_trb *)__get_str(ring),
__get_str(buffer)))
);
DEFINE_EVENT(cdns3_log_ring, cdns3_ring,
TP_PROTO(struct cdns3_endpoint *priv_ep),
TP_ARGS(priv_ep)
);
DECLARE_EVENT_CLASS(cdns3_log_ep,
TP_PROTO(struct cdns3_endpoint *priv_ep),
TP_ARGS(priv_ep),
TP_STRUCT__entry(
__string(name, priv_ep->name)
__field(unsigned int, maxpacket)
__field(unsigned int, maxpacket_limit)
__field(unsigned int, max_streams)
__field(unsigned int, maxburst)
__field(unsigned int, flags)
__field(unsigned int, dir)
__field(u8, enqueue)
__field(u8, dequeue)
),
TP_fast_assign(
__assign_str(name, priv_ep->name);
__entry->maxpacket = priv_ep->endpoint.maxpacket;
__entry->maxpacket_limit = priv_ep->endpoint.maxpacket_limit;
__entry->max_streams = priv_ep->endpoint.max_streams;
__entry->maxburst = priv_ep->endpoint.maxburst;
__entry->flags = priv_ep->flags;
__entry->dir = priv_ep->dir;
__entry->enqueue = priv_ep->enqueue;
__entry->dequeue = priv_ep->dequeue;
),
TP_printk("%s: mps: %d/%d. streams: %d, burst: %d, enq idx: %d, "
"deq idx: %d, flags %s%s%s%s%s%s%s%s, dir: %s",
__get_str(name), __entry->maxpacket,
__entry->maxpacket_limit, __entry->max_streams,
__entry->maxburst, __entry->enqueue,
__entry->dequeue,
__entry->flags & EP_ENABLED ? "EN | " : "",
__entry->flags & EP_STALL ? "STALL | " : "",
__entry->flags & EP_WEDGE ? "WEDGE | " : "",
__entry->flags & EP_TRANSFER_STARTED ? "STARTED | " : "",
__entry->flags & EP_UPDATE_EP_TRBADDR ? "UPD TRB | " : "",
__entry->flags & EP_PENDING_REQUEST ? "REQ PEN | " : "",
__entry->flags & EP_RING_FULL ? "RING FULL |" : "",
__entry->flags & EP_CLAIMED ? "CLAIMED " : "",
__entry->dir ? "IN" : "OUT"
)
);
DEFINE_EVENT(cdns3_log_ep, cdns3_gadget_ep_enable,
TP_PROTO(struct cdns3_endpoint *priv_ep),
TP_ARGS(priv_ep)
);
DEFINE_EVENT(cdns3_log_ep, cdns3_gadget_ep_disable,
TP_PROTO(struct cdns3_endpoint *priv_ep),
TP_ARGS(priv_ep)
);
DECLARE_EVENT_CLASS(cdns3_log_request_handled,
TP_PROTO(struct cdns3_request *priv_req, int current_index,
int handled),
TP_ARGS(priv_req, current_index, handled),
TP_STRUCT__entry(
__field(struct cdns3_request *, priv_req)
__field(unsigned int, dma_position)
__field(unsigned int, handled)
__field(unsigned int, dequeue_idx)
__field(unsigned int, enqueue_idx)
__field(unsigned int, start_trb)
__field(unsigned int, end_trb)
),
TP_fast_assign(
__entry->priv_req = priv_req;
__entry->dma_position = current_index;
__entry->handled = handled;
__entry->dequeue_idx = priv_req->priv_ep->dequeue;
__entry->enqueue_idx = priv_req->priv_ep->enqueue;
__entry->start_trb = priv_req->start_trb;
__entry->end_trb = priv_req->end_trb;
),
TP_printk("Req: %p %s, DMA pos: %d, ep deq: %d, ep enq: %d,"
" start trb: %d, end trb: %d",
__entry->priv_req,
__entry->handled ? "handled" : "not handled",
__entry->dma_position, __entry->dequeue_idx,
__entry->enqueue_idx, __entry->start_trb,
__entry->end_trb
)
);
DEFINE_EVENT(cdns3_log_request_handled, cdns3_request_handled,
TP_PROTO(struct cdns3_request *priv_req, int current_index,
int handled),
TP_ARGS(priv_req, current_index, handled)
);
#endif /* __LINUX_CDNS3_TRACE */
/* this part must be outside header guard */
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE trace
#include <trace/define_trace.h>