Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,11 @@
config USB_CDNS2_UDC
tristate "Cadence USBHS Device Controller"
depends on USB_PCI && ACPI && HAS_DMA
help
Cadence USBHS Device controller is a PCI based USB peripheral
controller which supports both full and high speed USB 2.0
data transfers.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "cdns2-udc-pci.ko" and to
force all gadget drivers to also be dynamically linked.
@@ -0,0 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
# define_trace.h needs to know how to find our header
CFLAGS_cdns2-trace.o := -I$(src)
obj-$(CONFIG_USB_CDNS2_UDC) += cdns2-udc-pci.o
cdns2-udc-pci-$(CONFIG_USB_CDNS2_UDC) += cdns2-pci.o cdns2-gadget.o cdns2-ep0.o
cdns2-udc-pci-$(CONFIG_TRACING) += cdns2-trace.o
@@ -0,0 +1,203 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Cadence USBHS-DEV Driver.
* Debug header file.
*
* Copyright (C) 2023 Cadence.
*
* Author: Pawel Laszczak <pawell@cadence.com>
*/
#ifndef __LINUX_CDNS2_DEBUG
#define __LINUX_CDNS2_DEBUG
static inline const char *cdns2_decode_usb_irq(char *str, size_t size,
u8 usb_irq, u8 ext_irq)
{
int ret;
ret = scnprintf(str, size, "usbirq: 0x%02x - ", usb_irq);
if (usb_irq & USBIRQ_SOF)
ret += scnprintf(str + ret, size - ret, "SOF ");
if (usb_irq & USBIRQ_SUTOK)
ret += scnprintf(str + ret, size - ret, "SUTOK ");
if (usb_irq & USBIRQ_SUDAV)
ret += scnprintf(str + ret, size - ret, "SETUP ");
if (usb_irq & USBIRQ_SUSPEND)
ret += scnprintf(str + ret, size - ret, "Suspend ");
if (usb_irq & USBIRQ_URESET)
ret += scnprintf(str + ret, size - ret, "Reset ");
if (usb_irq & USBIRQ_HSPEED)
ret += scnprintf(str + ret, size - ret, "HS ");
if (usb_irq & USBIRQ_LPM)
ret += scnprintf(str + ret, size - ret, "LPM ");
ret += scnprintf(str + ret, size - ret, ", EXT: 0x%02x - ", ext_irq);
if (ext_irq & EXTIRQ_WAKEUP)
ret += scnprintf(str + ret, size - ret, "Wakeup ");
if (ext_irq & EXTIRQ_VBUSFAULT_FALL)
ret += scnprintf(str + ret, size - ret, "VBUS_FALL ");
if (ext_irq & EXTIRQ_VBUSFAULT_RISE)
ret += scnprintf(str + ret, size - ret, "VBUS_RISE ");
if (ret == size - 1)
pr_info("CDNS2: buffer may be truncated.\n");
return str;
}
static inline const char *cdns2_decode_dma_irq(char *str, size_t size,
u32 ep_ists, u32 ep_sts,
const char *ep_name)
{
int ret;
ret = scnprintf(str, size, "ISTS: %08x, %s: %08x ",
ep_ists, ep_name, ep_sts);
if (ep_sts & DMA_EP_STS_IOC)
ret += scnprintf(str + ret, size - ret, "IOC ");
if (ep_sts & DMA_EP_STS_ISP)
ret += scnprintf(str + ret, size - ret, "ISP ");
if (ep_sts & DMA_EP_STS_DESCMIS)
ret += scnprintf(str + ret, size - ret, "DESCMIS ");
if (ep_sts & DMA_EP_STS_TRBERR)
ret += scnprintf(str + ret, size - ret, "TRBERR ");
if (ep_sts & DMA_EP_STS_OUTSMM)
ret += scnprintf(str + ret, size - ret, "OUTSMM ");
if (ep_sts & DMA_EP_STS_ISOERR)
ret += scnprintf(str + ret, size - ret, "ISOERR ");
if (ep_sts & DMA_EP_STS_DBUSY)
ret += scnprintf(str + ret, size - ret, "DBUSY ");
if (DMA_EP_STS_CCS(ep_sts))
ret += scnprintf(str + ret, size - ret, "CCS ");
if (ret == size - 1)
pr_info("CDNS2: buffer may be truncated.\n");
return str;
}
static inline const char *cdns2_decode_epx_irq(char *str, size_t size,
char *ep_name, u32 ep_ists,
u32 ep_sts)
{
return cdns2_decode_dma_irq(str, size, ep_ists, ep_sts, ep_name);
}
static inline const char *cdns2_decode_ep0_irq(char *str, size_t size,
u32 ep_ists, u32 ep_sts,
int dir)
{
return cdns2_decode_dma_irq(str, size, ep_ists, ep_sts,
dir ? "ep0IN" : "ep0OUT");
}
static inline const char *cdns2_raw_ring(struct cdns2_endpoint *pep,
struct cdns2_trb *trbs,
char *str, size_t size)
{
struct cdns2_ring *ring = &pep->ring;
struct cdns2_trb *trb;
dma_addr_t dma;
int ret;
int i;
ret = scnprintf(str, size, "\n\t\tTR for %s:", pep->name);
trb = &trbs[ring->dequeue];
dma = cdns2_trb_virt_to_dma(pep, trb);
ret += scnprintf(str + ret, size - ret,
"\n\t\tRing deq index: %d, trb: V=%p, P=0x%pad\n",
ring->dequeue, trb, &dma);
trb = &trbs[ring->enqueue];
dma = cdns2_trb_virt_to_dma(pep, trb);
ret += scnprintf(str + ret, size - ret,
"\t\tRing enq index: %d, trb: V=%p, P=0x%pad\n",
ring->enqueue, trb, &dma);
ret += scnprintf(str + ret, size - ret,
"\t\tfree trbs: %d, CCS=%d, PCS=%d\n",
ring->free_trbs, ring->ccs, ring->pcs);
if (TRBS_PER_SEGMENT > 40) {
ret += scnprintf(str + ret, size - ret,
"\t\tTransfer ring %d too big\n", TRBS_PER_SEGMENT);
return str;
}
dma = ring->dma;
for (i = 0; i < TRBS_PER_SEGMENT; ++i) {
trb = &trbs[i];
ret += scnprintf(str + ret, size - ret,
"\t\t@%pad %08x %08x %08x\n", &dma,
le32_to_cpu(trb->buffer),
le32_to_cpu(trb->length),
le32_to_cpu(trb->control));
dma += sizeof(*trb);
}
if (ret == size - 1)
pr_info("CDNS2: buffer may be truncated.\n");
return str;
}
static inline const char *cdns2_trb_type_string(u8 type)
{
switch (type) {
case TRB_NORMAL:
return "Normal";
case TRB_LINK:
return "Link";
default:
return "UNKNOWN";
}
}
static inline const char *cdns2_decode_trb(char *str, size_t size, u32 flags,
u32 length, u32 buffer)
{
int type = TRB_FIELD_TO_TYPE(flags);
int ret;
switch (type) {
case TRB_LINK:
ret = scnprintf(str, size,
"LINK %08x type '%s' flags %c:%c:%c%c:%c",
buffer, cdns2_trb_type_string(type),
flags & TRB_CYCLE ? 'C' : 'c',
flags & TRB_TOGGLE ? 'T' : 't',
flags & TRB_CHAIN ? 'C' : 'c',
flags & TRB_CHAIN ? 'H' : 'h',
flags & TRB_IOC ? 'I' : 'i');
break;
case TRB_NORMAL:
ret = scnprintf(str, size,
"type: '%s', Buffer: %08x, length: %ld, burst len: %ld, "
"flags %c:%c:%c%c:%c",
cdns2_trb_type_string(type),
buffer, TRB_LEN(length),
TRB_FIELD_TO_BURST(length),
flags & TRB_CYCLE ? 'C' : 'c',
flags & TRB_ISP ? 'I' : 'i',
flags & TRB_CHAIN ? 'C' : 'c',
flags & TRB_CHAIN ? 'H' : 'h',
flags & TRB_IOC ? 'I' : 'i');
break;
default:
ret = scnprintf(str, size, "type '%s' -> raw %08x %08x %08x",
cdns2_trb_type_string(type),
buffer, length, flags);
}
if (ret == size - 1)
pr_info("CDNS2: buffer may be truncated.\n");
return str;
}
#endif /*__LINUX_CDNS2_DEBUG*/
@@ -0,0 +1,659 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Cadence USBHS-DEV driver.
*
* Copyright (C) 2023 Cadence Design Systems.
*
* Authors: Pawel Laszczak <pawell@cadence.com>
*/
#include <linux/usb/composite.h>
#include <linux/unaligned.h>
#include "cdns2-gadget.h"
#include "cdns2-trace.h"
static struct usb_endpoint_descriptor cdns2_gadget_ep0_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_CONTROL,
.wMaxPacketSize = cpu_to_le16(64)
};
static int cdns2_w_index_to_ep_index(u16 wIndex)
{
if (!(wIndex & USB_ENDPOINT_NUMBER_MASK))
return 0;
return ((wIndex & USB_ENDPOINT_NUMBER_MASK) * 2) +
(wIndex & USB_ENDPOINT_DIR_MASK ? 1 : 0) - 1;
}
static bool cdns2_check_new_setup(struct cdns2_device *pdev)
{
u8 reg;
reg = readb(&pdev->ep0_regs->cs);
return !!(reg & EP0CS_CHGSET);
}
static void cdns2_ep0_enqueue(struct cdns2_device *pdev, dma_addr_t dma_addr,
unsigned int length, int zlp)
{
struct cdns2_adma_regs __iomem *regs = pdev->adma_regs;
struct cdns2_endpoint *pep = &pdev->eps[0];
struct cdns2_ring *ring = &pep->ring;
ring->trbs[0].buffer = cpu_to_le32(TRB_BUFFER(dma_addr));
ring->trbs[0].length = cpu_to_le32(TRB_LEN(length));
if (zlp) {
ring->trbs[0].control = cpu_to_le32(TRB_CYCLE |
TRB_TYPE(TRB_NORMAL));
ring->trbs[1].buffer = cpu_to_le32(TRB_BUFFER(dma_addr));
ring->trbs[1].length = cpu_to_le32(TRB_LEN(0));
ring->trbs[1].control = cpu_to_le32(TRB_CYCLE | TRB_IOC |
TRB_TYPE(TRB_NORMAL));
} else {
ring->trbs[0].control = cpu_to_le32(TRB_CYCLE | TRB_IOC |
TRB_TYPE(TRB_NORMAL));
ring->trbs[1].control = 0;
}
trace_cdns2_queue_trb(pep, ring->trbs);
if (!pep->dir)
writel(0, &pdev->ep0_regs->rxbc);
cdns2_select_ep(pdev, pep->dir);
writel(DMA_EP_STS_TRBERR, &regs->ep_sts);
writel(pep->ring.dma, &regs->ep_traddr);
trace_cdns2_doorbell_ep0(pep, readl(&regs->ep_traddr));
writel(DMA_EP_CMD_DRDY, &regs->ep_cmd);
}
static int cdns2_ep0_delegate_req(struct cdns2_device *pdev)
{
int ret;
spin_unlock(&pdev->lock);
ret = pdev->gadget_driver->setup(&pdev->gadget, &pdev->setup);
spin_lock(&pdev->lock);
return ret;
}
static void cdns2_ep0_stall(struct cdns2_device *pdev)
{
struct cdns2_endpoint *pep = &pdev->eps[0];
struct cdns2_request *preq;
preq = cdns2_next_preq(&pep->pending_list);
set_reg_bit_8(&pdev->ep0_regs->cs, EP0CS_DSTALL);
if (pdev->ep0_stage == CDNS2_DATA_STAGE && preq)
cdns2_gadget_giveback(pep, preq, -ECONNRESET);
else if (preq)
list_del_init(&preq->list);
pdev->ep0_stage = CDNS2_SETUP_STAGE;
pep->ep_state |= EP_STALLED;
}
static void cdns2_status_stage(struct cdns2_device *pdev)
{
struct cdns2_endpoint *pep = &pdev->eps[0];
struct cdns2_request *preq;
preq = cdns2_next_preq(&pep->pending_list);
if (preq)
list_del_init(&preq->list);
pdev->ep0_stage = CDNS2_SETUP_STAGE;
writeb(EP0CS_HSNAK, &pdev->ep0_regs->cs);
}
static int cdns2_req_ep0_set_configuration(struct cdns2_device *pdev,
struct usb_ctrlrequest *ctrl_req)
{
enum usb_device_state state = pdev->gadget.state;
u32 config = le16_to_cpu(ctrl_req->wValue);
int ret;
if (state < USB_STATE_ADDRESS) {
dev_err(pdev->dev, "Set Configuration - bad device state\n");
return -EINVAL;
}
ret = cdns2_ep0_delegate_req(pdev);
if (ret)
return ret;
trace_cdns2_device_state(config ? "configured" : "addressed");
if (!config)
usb_gadget_set_state(&pdev->gadget, USB_STATE_ADDRESS);
return 0;
}
static int cdns2_req_ep0_set_address(struct cdns2_device *pdev, u32 addr)
{
enum usb_device_state device_state = pdev->gadget.state;
u8 reg;
if (addr > USB_DEVICE_MAX_ADDRESS) {
dev_err(pdev->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(pdev->dev,
"can't set_address from configured state\n");
return -EINVAL;
}
reg = readb(&pdev->usb_regs->fnaddr);
pdev->dev_address = reg;
usb_gadget_set_state(&pdev->gadget,
(addr ? USB_STATE_ADDRESS : USB_STATE_DEFAULT));
trace_cdns2_device_state(addr ? "addressed" : "default");
return 0;
}
static int cdns2_req_ep0_handle_status(struct cdns2_device *pdev,
struct usb_ctrlrequest *ctrl)
{
struct cdns2_endpoint *pep;
__le16 *response_pkt;
u16 status = 0;
int ep_sts;
u32 recip;
recip = ctrl->bRequestType & USB_RECIP_MASK;
switch (recip) {
case USB_RECIP_DEVICE:
status = pdev->gadget.is_selfpowered;
status |= pdev->may_wakeup << USB_DEVICE_REMOTE_WAKEUP;
break;
case USB_RECIP_INTERFACE:
return cdns2_ep0_delegate_req(pdev);
case USB_RECIP_ENDPOINT:
ep_sts = cdns2_w_index_to_ep_index(le16_to_cpu(ctrl->wIndex));
pep = &pdev->eps[ep_sts];
if (pep->ep_state & EP_STALLED)
status = BIT(USB_ENDPOINT_HALT);
break;
default:
return -EINVAL;
}
put_unaligned_le16(status, (__le16 *)pdev->ep0_preq.request.buf);
cdns2_ep0_enqueue(pdev, pdev->ep0_preq.request.dma,
sizeof(*response_pkt), 0);
return 0;
}
static int cdns2_ep0_handle_feature_device(struct cdns2_device *pdev,
struct usb_ctrlrequest *ctrl,
int set)
{
enum usb_device_state state;
enum usb_device_speed speed;
int ret = 0;
u32 wValue;
u16 tmode;
wValue = le16_to_cpu(ctrl->wValue);
state = pdev->gadget.state;
speed = pdev->gadget.speed;
switch (wValue) {
case USB_DEVICE_REMOTE_WAKEUP:
pdev->may_wakeup = !!set;
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;
tmode >>= 8;
switch (tmode) {
case USB_TEST_J:
case USB_TEST_K:
case USB_TEST_SE0_NAK:
case USB_TEST_PACKET:
/*
* The USBHS controller automatically handles the
* Set_Feature(testmode) request. Standard test modes
* that use values of test mode selector from
* 01h to 04h (Test_J, Test_K, Test_SE0_NAK,
* Test_Packet) are supported by the
* controller(HS - ack, FS - stall).
*/
break;
default:
ret = -EINVAL;
}
break;
default:
ret = -EINVAL;
}
return ret;
}
static int cdns2_ep0_handle_feature_intf(struct cdns2_device *pdev,
struct usb_ctrlrequest *ctrl,
int set)
{
int ret = 0;
u32 wValue;
wValue = le16_to_cpu(ctrl->wValue);
switch (wValue) {
case USB_INTRF_FUNC_SUSPEND:
break;
default:
ret = -EINVAL;
}
return ret;
}
static int cdns2_ep0_handle_feature_endpoint(struct cdns2_device *pdev,
struct usb_ctrlrequest *ctrl,
int set)
{
struct cdns2_endpoint *pep;
u8 wValue;
wValue = le16_to_cpu(ctrl->wValue);
pep = &pdev->eps[cdns2_w_index_to_ep_index(le16_to_cpu(ctrl->wIndex))];
if (wValue != USB_ENDPOINT_HALT)
return -EINVAL;
if (!(le16_to_cpu(ctrl->wIndex) & ~USB_DIR_IN))
return 0;
switch (wValue) {
case USB_ENDPOINT_HALT:
if (set || !(pep->ep_state & EP_WEDGE))
return cdns2_halt_endpoint(pdev, pep, set);
break;
default:
dev_warn(pdev->dev, "WARN Incorrect wValue %04x\n", wValue);
return -EINVAL;
}
return 0;
}
static int cdns2_req_ep0_handle_feature(struct cdns2_device *pdev,
struct usb_ctrlrequest *ctrl,
int set)
{
switch (ctrl->bRequestType & USB_RECIP_MASK) {
case USB_RECIP_DEVICE:
return cdns2_ep0_handle_feature_device(pdev, ctrl, set);
case USB_RECIP_INTERFACE:
return cdns2_ep0_handle_feature_intf(pdev, ctrl, set);
case USB_RECIP_ENDPOINT:
return cdns2_ep0_handle_feature_endpoint(pdev, ctrl, set);
default:
return -EINVAL;
}
}
static int cdns2_ep0_std_request(struct cdns2_device *pdev)
{
struct usb_ctrlrequest *ctrl = &pdev->setup;
int ret;
switch (ctrl->bRequest) {
case USB_REQ_SET_ADDRESS:
ret = cdns2_req_ep0_set_address(pdev,
le16_to_cpu(ctrl->wValue));
break;
case USB_REQ_SET_CONFIGURATION:
ret = cdns2_req_ep0_set_configuration(pdev, ctrl);
break;
case USB_REQ_GET_STATUS:
ret = cdns2_req_ep0_handle_status(pdev, ctrl);
break;
case USB_REQ_CLEAR_FEATURE:
ret = cdns2_req_ep0_handle_feature(pdev, ctrl, 0);
break;
case USB_REQ_SET_FEATURE:
ret = cdns2_req_ep0_handle_feature(pdev, ctrl, 1);
break;
default:
ret = cdns2_ep0_delegate_req(pdev);
break;
}
return ret;
}
static void __pending_setup_status_handler(struct cdns2_device *pdev)
{
struct usb_request *request = pdev->pending_status_request;
if (pdev->status_completion_no_call && request && request->complete) {
request->complete(&pdev->eps[0].endpoint, request);
pdev->status_completion_no_call = 0;
}
}
void cdns2_pending_setup_status_handler(struct work_struct *work)
{
struct cdns2_device *pdev = container_of(work, struct cdns2_device,
pending_status_wq);
unsigned long flags;
spin_lock_irqsave(&pdev->lock, flags);
__pending_setup_status_handler(pdev);
spin_unlock_irqrestore(&pdev->lock, flags);
}
void cdns2_handle_setup_packet(struct cdns2_device *pdev)
{
struct usb_ctrlrequest *ctrl = &pdev->setup;
struct cdns2_endpoint *pep = &pdev->eps[0];
struct cdns2_request *preq;
int ret = 0;
u16 len;
u8 reg;
int i;
writeb(EP0CS_CHGSET, &pdev->ep0_regs->cs);
for (i = 0; i < 8; i++)
((u8 *)&pdev->setup)[i] = readb(&pdev->ep0_regs->setupdat[i]);
/*
* If SETUP packet was modified while reading just simple ignore it.
* The new one will be handled latter.
*/
if (cdns2_check_new_setup(pdev)) {
trace_cdns2_ep0_setup("overridden");
return;
}
trace_cdns2_ctrl_req(ctrl);
if (!pdev->gadget_driver)
goto out;
if (pdev->gadget.state == USB_STATE_NOTATTACHED) {
dev_err(pdev->dev, "ERR: Setup detected in unattached state\n");
ret = -EINVAL;
goto out;
}
pep = &pdev->eps[0];
/* Halt for Ep0 is cleared automatically when SETUP packet arrives. */
pep->ep_state &= ~EP_STALLED;
if (!list_empty(&pep->pending_list)) {
preq = cdns2_next_preq(&pep->pending_list);
cdns2_gadget_giveback(pep, preq, -ECONNRESET);
}
len = le16_to_cpu(ctrl->wLength);
if (len)
pdev->ep0_stage = CDNS2_DATA_STAGE;
else
pdev->ep0_stage = CDNS2_STATUS_STAGE;
pep->dir = ctrl->bRequestType & USB_DIR_IN;
/*
* SET_ADDRESS request is acknowledged automatically by controller and
* in the worse case driver may not notice this request. To check
* whether this request has been processed driver can use
* fnaddr register.
*/
reg = readb(&pdev->usb_regs->fnaddr);
if (pdev->setup.bRequest != USB_REQ_SET_ADDRESS &&
pdev->dev_address != reg)
cdns2_req_ep0_set_address(pdev, reg);
if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
ret = cdns2_ep0_std_request(pdev);
else
ret = cdns2_ep0_delegate_req(pdev);
if (ret == USB_GADGET_DELAYED_STATUS) {
trace_cdns2_ep0_status_stage("delayed");
return;
}
out:
if (ret < 0)
cdns2_ep0_stall(pdev);
else if (pdev->ep0_stage == CDNS2_STATUS_STAGE)
cdns2_status_stage(pdev);
}
static void cdns2_transfer_completed(struct cdns2_device *pdev)
{
struct cdns2_endpoint *pep = &pdev->eps[0];
if (!list_empty(&pep->pending_list)) {
struct cdns2_request *preq;
trace_cdns2_complete_trb(pep, pep->ring.trbs);
preq = cdns2_next_preq(&pep->pending_list);
preq->request.actual =
TRB_LEN(le32_to_cpu(pep->ring.trbs->length));
cdns2_gadget_giveback(pep, preq, 0);
}
cdns2_status_stage(pdev);
}
void cdns2_handle_ep0_interrupt(struct cdns2_device *pdev, int dir)
{
u32 ep_sts_reg;
cdns2_select_ep(pdev, dir);
trace_cdns2_ep0_irq(pdev);
ep_sts_reg = readl(&pdev->adma_regs->ep_sts);
writel(ep_sts_reg, &pdev->adma_regs->ep_sts);
__pending_setup_status_handler(pdev);
if ((ep_sts_reg & DMA_EP_STS_IOC) || (ep_sts_reg & DMA_EP_STS_ISP)) {
pdev->eps[0].dir = dir;
cdns2_transfer_completed(pdev);
}
}
/*
* Function shouldn't be called by gadget driver,
* endpoint 0 is allways active.
*/
static int cdns2_gadget_ep0_enable(struct usb_ep *ep,
const struct usb_endpoint_descriptor *desc)
{
return -EINVAL;
}
/*
* Function shouldn't be called by gadget driver,
* endpoint 0 is allways active.
*/
static int cdns2_gadget_ep0_disable(struct usb_ep *ep)
{
return -EINVAL;
}
static int cdns2_gadget_ep0_set_halt(struct usb_ep *ep, int value)
{
struct cdns2_endpoint *pep = ep_to_cdns2_ep(ep);
struct cdns2_device *pdev = pep->pdev;
unsigned long flags;
if (!value)
return 0;
spin_lock_irqsave(&pdev->lock, flags);
cdns2_ep0_stall(pdev);
spin_unlock_irqrestore(&pdev->lock, flags);
return 0;
}
static int cdns2_gadget_ep0_set_wedge(struct usb_ep *ep)
{
return cdns2_gadget_ep0_set_halt(ep, 1);
}
static int cdns2_gadget_ep0_queue(struct usb_ep *ep,
struct usb_request *request,
gfp_t gfp_flags)
{
struct cdns2_endpoint *pep = ep_to_cdns2_ep(ep);
struct cdns2_device *pdev = pep->pdev;
struct cdns2_request *preq;
unsigned long flags;
u8 zlp = 0;
int ret;
spin_lock_irqsave(&pdev->lock, flags);
preq = to_cdns2_request(request);
trace_cdns2_request_enqueue(preq);
/* Cancel the request if controller receive new SETUP packet. */
if (cdns2_check_new_setup(pdev)) {
trace_cdns2_ep0_setup("overridden");
spin_unlock_irqrestore(&pdev->lock, flags);
return -ECONNRESET;
}
/* Send STATUS stage. Should be called only for SET_CONFIGURATION. */
if (pdev->ep0_stage == CDNS2_STATUS_STAGE) {
cdns2_status_stage(pdev);
request->actual = 0;
pdev->status_completion_no_call = true;
pdev->pending_status_request = request;
usb_gadget_set_state(&pdev->gadget, USB_STATE_CONFIGURED);
spin_unlock_irqrestore(&pdev->lock, flags);
/*
* Since there is no completion interrupt for status stage,
* it needs to call ->completion in software after
* cdns2_gadget_ep0_queue is back.
*/
queue_work(system_freezable_wq, &pdev->pending_status_wq);
return 0;
}
if (!list_empty(&pep->pending_list)) {
trace_cdns2_ep0_setup("pending");
dev_err(pdev->dev,
"can't handle multiple requests for ep0\n");
spin_unlock_irqrestore(&pdev->lock, flags);
return -EBUSY;
}
ret = usb_gadget_map_request_by_dev(pdev->dev, request, pep->dir);
if (ret) {
spin_unlock_irqrestore(&pdev->lock, flags);
dev_err(pdev->dev, "failed to map request\n");
return -EINVAL;
}
request->status = -EINPROGRESS;
list_add_tail(&preq->list, &pep->pending_list);
if (request->zero && request->length &&
(request->length % ep->maxpacket == 0))
zlp = 1;
cdns2_ep0_enqueue(pdev, request->dma, request->length, zlp);
spin_unlock_irqrestore(&pdev->lock, flags);
return 0;
}
static const struct usb_ep_ops cdns2_gadget_ep0_ops = {
.enable = cdns2_gadget_ep0_enable,
.disable = cdns2_gadget_ep0_disable,
.alloc_request = cdns2_gadget_ep_alloc_request,
.free_request = cdns2_gadget_ep_free_request,
.queue = cdns2_gadget_ep0_queue,
.dequeue = cdns2_gadget_ep_dequeue,
.set_halt = cdns2_gadget_ep0_set_halt,
.set_wedge = cdns2_gadget_ep0_set_wedge,
};
void cdns2_ep0_config(struct cdns2_device *pdev)
{
struct cdns2_endpoint *pep;
pep = &pdev->eps[0];
if (!list_empty(&pep->pending_list)) {
struct cdns2_request *preq;
preq = cdns2_next_preq(&pep->pending_list);
list_del_init(&preq->list);
}
writeb(EP0_FIFO_AUTO, &pdev->ep0_regs->fifo);
cdns2_select_ep(pdev, USB_DIR_OUT);
writel(DMA_EP_CFG_ENABLE, &pdev->adma_regs->ep_cfg);
writeb(EP0_FIFO_IO_TX | EP0_FIFO_AUTO, &pdev->ep0_regs->fifo);
cdns2_select_ep(pdev, USB_DIR_IN);
writel(DMA_EP_CFG_ENABLE, &pdev->adma_regs->ep_cfg);
writeb(pdev->gadget.ep0->maxpacket, &pdev->ep0_regs->maxpack);
writel(DMA_EP_IEN_EP_OUT0 | DMA_EP_IEN_EP_IN0,
&pdev->adma_regs->ep_ien);
}
void cdns2_init_ep0(struct cdns2_device *pdev,
struct cdns2_endpoint *pep)
{
u16 maxpacket = le16_to_cpu(cdns2_gadget_ep0_desc.wMaxPacketSize);
usb_ep_set_maxpacket_limit(&pep->endpoint, maxpacket);
pep->endpoint.ops = &cdns2_gadget_ep0_ops;
pep->endpoint.desc = &cdns2_gadget_ep0_desc;
pep->endpoint.caps.type_control = true;
pep->endpoint.caps.dir_in = true;
pep->endpoint.caps.dir_out = true;
pdev->gadget.ep0 = &pep->endpoint;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,716 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* USBHS-DEV device controller driver header file
*
* Copyright (C) 2023 Cadence.
*
* Author: Pawel Laszczak <pawell@cadence.com>
*/
#ifndef __LINUX_CDNS2_GADGET
#define __LINUX_CDNS2_GADGET
#include <linux/usb/gadget.h>
#include <linux/dma-direction.h>
/*
* USBHS register interface.
* This corresponds to the USBHS Device Controller Interface.
*/
/**
* struct cdns2_ep0_regs - endpoint 0 related registers.
* @rxbc: receive (OUT) 0 endpoint byte count register.
* @txbc: transmit (IN) 0 endpoint byte count register.
* @cs: 0 endpoint control and status register.
* @reserved1: reserved.
* @fifo: 0 endpoint fifo register.
* @reserved2: reserved.
* @setupdat: SETUP data register.
* @reserved4: reserved.
* @maxpack: 0 endpoint max packet size.
*/
struct cdns2_ep0_regs {
__u8 rxbc;
__u8 txbc;
__u8 cs;
__u8 reserved1[4];
__u8 fifo;
__le32 reserved2[94];
__u8 setupdat[8];
__u8 reserved4[88];
__u8 maxpack;
} __packed __aligned(4);
/* EP0CS - bitmasks. */
/* Endpoint 0 stall bit for status stage. */
#define EP0CS_STALL BIT(0)
/* HSNAK bit. */
#define EP0CS_HSNAK BIT(1)
/* IN 0 endpoint busy bit. */
#define EP0CS_TXBSY_MSK BIT(2)
/* OUT 0 endpoint busy bit. */
#define EP0CS_RXBSY_MSK BIT(3)
/* Send STALL in the data stage phase. */
#define EP0CS_DSTALL BIT(4)
/* SETUP buffer content was changed. */
#define EP0CS_CHGSET BIT(7)
/* EP0FIFO - bitmasks. */
/* Direction. */
#define EP0_FIFO_IO_TX BIT(4)
/* FIFO auto bit. */
#define EP0_FIFO_AUTO BIT(5)
/* FIFO commit bit. */
#define EP0_FIFO_COMMIT BIT(6)
/* FIFO access bit. */
#define EP0_FIFO_ACCES BIT(7)
/**
* struct cdns2_epx_base - base endpoint registers.
* @rxbc: OUT endpoint byte count register.
* @rxcon: OUT endpoint control register.
* @rxcs: OUT endpoint control and status register.
* @txbc: IN endpoint byte count register.
* @txcon: IN endpoint control register.
* @txcs: IN endpoint control and status register.
*/
struct cdns2_epx_base {
__le16 rxbc;
__u8 rxcon;
__u8 rxcs;
__le16 txbc;
__u8 txcon;
__u8 txcs;
} __packed __aligned(4);
/* rxcon/txcon - endpoint control register bitmasks. */
/* Endpoint buffering: 0 - single buffering ... 3 - quad buffering. */
#define EPX_CON_BUF GENMASK(1, 0)
/* Endpoint type. */
#define EPX_CON_TYPE GENMASK(3, 2)
/* Endpoint type: isochronous. */
#define EPX_CON_TYPE_ISOC 0x4
/* Endpoint type: bulk. */
#define EPX_CON_TYPE_BULK 0x8
/* Endpoint type: interrupt. */
#define EPX_CON_TYPE_INT 0xC
/* Number of packets per microframe. */
#define EPX_CON_ISOD GENMASK(5, 4)
#define EPX_CON_ISOD_SHIFT 0x4
/* Endpoint stall bit. */
#define EPX_CON_STALL BIT(6)
/* Endpoint enable bit.*/
#define EPX_CON_VAL BIT(7)
/* rxcs/txcs - endpoint control and status bitmasks. */
/* Data sequence error for the ISO endpoint. */
#define EPX_CS_ERR(p) ((p) & BIT(0))
/**
* struct cdns2_epx_regs - endpoint 1..15 related registers.
* @reserved: reserved.
* @ep: none control endpoints array.
* @reserved2: reserved.
* @endprst: endpoint reset register.
* @reserved3: reserved.
* @isoautoarm: ISO auto-arm register.
* @reserved4: reserved.
* @isodctrl: ISO control register.
* @reserved5: reserved.
* @isoautodump: ISO auto dump enable register.
* @reserved6: reserved.
* @rxmaxpack: receive (OUT) Max packet size register.
* @reserved7: reserved.
* @rxstaddr: receive (OUT) start address endpoint buffer register.
* @reserved8: reserved.
* @txstaddr: transmit (IN) start address endpoint buffer register.
* @reserved9: reserved.
* @txmaxpack: transmit (IN) Max packet size register.
*/
struct cdns2_epx_regs {
__le32 reserved[2];
struct cdns2_epx_base ep[15];
__u8 reserved2[290];
__u8 endprst;
__u8 reserved3[41];
__le16 isoautoarm;
__u8 reserved4[10];
__le16 isodctrl;
__le16 reserved5;
__le16 isoautodump;
__le32 reserved6;
__le16 rxmaxpack[15];
__le32 reserved7[65];
__le32 rxstaddr[15];
__u8 reserved8[4];
__le32 txstaddr[15];
__u8 reserved9[98];
__le16 txmaxpack[15];
} __packed __aligned(4);
/* ENDPRST - bitmasks. */
/* Endpoint number. */
#define ENDPRST_EP GENMASK(3, 0)
/* IN direction bit. */
#define ENDPRST_IO_TX BIT(4)
/* Toggle reset bit. */
#define ENDPRST_TOGRST BIT(5)
/* FIFO reset bit. */
#define ENDPRST_FIFORST BIT(6)
/* Toggle status and reset bit. */
#define ENDPRST_TOGSETQ BIT(7)
/**
* struct cdns2_interrupt_regs - USB interrupt related registers.
* @reserved: reserved.
* @usbirq: USB interrupt request register.
* @extirq: external interrupt request register.
* @rxpngirq: external interrupt request register.
* @reserved1: reserved.
* @usbien: USB interrupt enable register.
* @extien: external interrupt enable register.
* @reserved2: reserved.
* @usbivect: USB interrupt vector register.
*/
struct cdns2_interrupt_regs {
__u8 reserved[396];
__u8 usbirq;
__u8 extirq;
__le16 rxpngirq;
__le16 reserved1[4];
__u8 usbien;
__u8 extien;
__le16 reserved2[3];
__u8 usbivect;
} __packed __aligned(4);
/* EXTIRQ and EXTIEN - bitmasks. */
/* VBUS fault fall interrupt. */
#define EXTIRQ_VBUSFAULT_FALL BIT(0)
/* VBUS fault fall interrupt. */
#define EXTIRQ_VBUSFAULT_RISE BIT(1)
/* Wake up interrupt bit. */
#define EXTIRQ_WAKEUP BIT(7)
/* USBIEN and USBIRQ - bitmasks. */
/* SETUP data valid interrupt bit.*/
#define USBIRQ_SUDAV BIT(0)
/* Start-of-frame interrupt bit. */
#define USBIRQ_SOF BIT(1)
/* SETUP token interrupt bit. */
#define USBIRQ_SUTOK BIT(2)
/* USB suspend interrupt bit. */
#define USBIRQ_SUSPEND BIT(3)
/* USB reset interrupt bit. */
#define USBIRQ_URESET BIT(4)
/* USB high-speed mode interrupt bit. */
#define USBIRQ_HSPEED BIT(5)
/* Link Power Management interrupt bit. */
#define USBIRQ_LPM BIT(7)
#define USB_IEN_INIT (USBIRQ_SUDAV | USBIRQ_SUSPEND | USBIRQ_URESET \
| USBIRQ_HSPEED | USBIRQ_LPM)
/**
* struct cdns2_usb_regs - USB controller registers.
* @reserved: reserved.
* @lpmctrl: LPM control register.
* @lpmclock: LPM clock register.
* @reserved2: reserved.
* @endprst: endpoint reset register.
* @usbcs: USB control and status register.
* @frmnr: USB frame counter register.
* @fnaddr: function Address register.
* @clkgate: clock gate register.
* @fifoctrl: FIFO control register.
* @speedctrl: speed Control register.
* @sleep_clkgate: sleep Clock Gate register.
* @reserved3: reserved.
* @cpuctrl: microprocessor control register.
*/
struct cdns2_usb_regs {
__u8 reserved[4];
__u16 lpmctrl;
__u8 lpmclock;
__u8 reserved2[411];
__u8 endprst;
__u8 usbcs;
__le16 frmnr;
__u8 fnaddr;
__u8 clkgate;
__u8 fifoctrl;
__u8 speedctrl;
__u8 sleep_clkgate;
__u8 reserved3[533];
__u8 cpuctrl;
} __packed __aligned(4);
/* LPMCTRL - bitmasks. */
/* BESL (Best Effort Service Latency). */
#define LPMCTRLLL_HIRD GENMASK(7, 4)
/* Last received Remote Wakeup field from LPM Extended Token packet. */
#define LPMCTRLLH_BREMOTEWAKEUP BIT(8)
/* Reflects value of the lpmnyet bit located in the usbcs[1] register. */
#define LPMCTRLLH_LPMNYET BIT(16)
/* LPMCLOCK - bitmasks. */
/*
* If bit is 1 the controller automatically turns off clock
* (utmisleepm goes to low), else the microprocessor should use
* sleep clock gate register to turn off clock.
*/
#define LPMCLOCK_SLEEP_ENTRY BIT(7)
/* USBCS - bitmasks. */
/* Send NYET handshake for the LPM transaction. */
#define USBCS_LPMNYET BIT(2)
/* Remote wake-up bit. */
#define USBCS_SIGRSUME BIT(5)
/* Software disconnect bit. */
#define USBCS_DISCON BIT(6)
/* Indicates that a wakeup pin resumed the controller. */
#define USBCS_WAKESRC BIT(7)
/* FIFOCTRL - bitmasks. */
/* Endpoint number. */
#define FIFOCTRL_EP GENMASK(3, 0)
/* Direction bit. */
#define FIFOCTRL_IO_TX BIT(4)
/* FIFO auto bit. */
#define FIFOCTRL_FIFOAUTO BIT(5)
/* FIFO commit bit. */
#define FIFOCTRL_FIFOCMIT BIT(6)
/* FIFO access bit. */
#define FIFOCTRL_FIFOACC BIT(7)
/* SPEEDCTRL - bitmasks. */
/* Device works in Full Speed. */
#define SPEEDCTRL_FS BIT(1)
/* Device works in High Speed. */
#define SPEEDCTRL_HS BIT(2)
/* Force FS mode. */
#define SPEEDCTRL_HSDISABLE BIT(7)
/* CPUCTRL- bitmasks. */
/* UP clock enable */
#define CPUCTRL_UPCLK BIT(0)
/* Controller reset bit. */
#define CPUCTRL_SW_RST BIT(1)
/**
* If the wuen bit is ‘1’, the upclken is automatically set to ‘1’ after
* detecting rising edge of wuintereq interrupt. If the wuen bit is ‘0’,
* the wuintereq interrupt is ignored.
*/
#define CPUCTRL_WUEN BIT(7)
/**
* struct cdns2_adma_regs - ADMA controller registers.
* @conf: DMA global configuration register.
* @sts: DMA global Status register.
* @reserved1: reserved.
* @ep_sel: DMA endpoint select register.
* @ep_traddr: DMA endpoint transfer ring address register.
* @ep_cfg: DMA endpoint configuration register.
* @ep_cmd: DMA endpoint command register.
* @ep_sts: DMA endpoint status register.
* @reserved2: reserved.
* @ep_sts_en: DMA endpoint status enable register.
* @drbl: DMA doorbell register.
* @ep_ien: DMA endpoint interrupt enable register.
* @ep_ists: DMA endpoint interrupt status register.
* @axim_ctrl: AXI Master Control register.
* @axim_id: AXI Master ID register.
* @reserved3: reserved.
* @axim_cap: AXI Master Wrapper Extended Capability.
* @reserved4: reserved.
* @axim_ctrl0: AXI Master Wrapper Extended Capability Control Register 0.
* @axim_ctrl1: AXI Master Wrapper Extended Capability Control Register 1.
*/
struct cdns2_adma_regs {
__le32 conf;
__le32 sts;
__le32 reserved1[5];
__le32 ep_sel;
__le32 ep_traddr;
__le32 ep_cfg;
__le32 ep_cmd;
__le32 ep_sts;
__le32 reserved2;
__le32 ep_sts_en;
__le32 drbl;
__le32 ep_ien;
__le32 ep_ists;
__le32 axim_ctrl;
__le32 axim_id;
__le32 reserved3;
__le32 axim_cap;
__le32 reserved4;
__le32 axim_ctrl0;
__le32 axim_ctrl1;
};
#define CDNS2_ADMA_REGS_OFFSET 0x400
/* DMA_CONF - bitmasks. */
/* Reset USB device configuration. */
#define DMA_CONF_CFGRST BIT(0)
/* Singular DMA transfer mode.*/
#define DMA_CONF_DSING BIT(8)
/* Multiple DMA transfers mode.*/
#define DMA_CONF_DMULT BIT(9)
/* DMA_EP_CFG - bitmasks. */
/* Endpoint enable. */
#define DMA_EP_CFG_ENABLE BIT(0)
/* DMA_EP_CMD - bitmasks. */
/* Endpoint reset. */
#define DMA_EP_CMD_EPRST BIT(0)
/* Transfer descriptor ready. */
#define DMA_EP_CMD_DRDY BIT(6)
/* Data flush. */
#define DMA_EP_CMD_DFLUSH BIT(7)
/* DMA_EP_STS - bitmasks. */
/* Interrupt On Complete. */
#define DMA_EP_STS_IOC BIT(2)
/* Interrupt on Short Packet. */
#define DMA_EP_STS_ISP BIT(3)
/* Transfer descriptor missing. */
#define DMA_EP_STS_DESCMIS BIT(4)
/* TRB error. */
#define DMA_EP_STS_TRBERR BIT(7)
/* DMA busy bit. */
#define DMA_EP_STS_DBUSY BIT(9)
/* Current Cycle Status. */
#define DMA_EP_STS_CCS(p) ((p) & BIT(11))
/* OUT size mismatch. */
#define DMA_EP_STS_OUTSMM BIT(14)
/* ISO transmission error. */
#define DMA_EP_STS_ISOERR BIT(15)
/* DMA_EP_STS_EN - bitmasks. */
/* OUT transfer missing descriptor enable. */
#define DMA_EP_STS_EN_DESCMISEN BIT(4)
/* TRB enable. */
#define DMA_EP_STS_EN_TRBERREN BIT(7)
/* OUT size mismatch enable. */
#define DMA_EP_STS_EN_OUTSMMEN BIT(14)
/* ISO transmission error enable. */
#define DMA_EP_STS_EN_ISOERREN BIT(15)
/* DMA_EP_IEN - bitmasks. */
#define DMA_EP_IEN(index) (1 << (index))
#define DMA_EP_IEN_EP_OUT0 BIT(0)
#define DMA_EP_IEN_EP_IN0 BIT(16)
/* DMA_EP_ISTS - bitmasks. */
#define DMA_EP_ISTS(index) (1 << (index))
#define DMA_EP_ISTS_EP_OUT0 BIT(0)
#define DMA_EP_ISTS_EP_IN0 BIT(16)
#define gadget_to_cdns2_device(g) (container_of(g, struct cdns2_device, gadget))
#define ep_to_cdns2_ep(ep) (container_of(ep, struct cdns2_endpoint, endpoint))
/*-------------------------------------------------------------------------*/
#define TRBS_PER_SEGMENT 600
#define ISO_MAX_INTERVAL 8
#define MAX_TRB_LENGTH BIT(16)
#define MAX_ISO_SIZE 3076
/*
* To improve performance the TRB buffer pointers can't cross
* 4KB boundaries.
*/
#define TRB_MAX_ISO_BUFF_SHIFT 12
#define TRB_MAX_ISO_BUFF_SIZE BIT(TRB_MAX_ISO_BUFF_SHIFT)
/* How much data is left before the 4KB boundary? */
#define TRB_BUFF_LEN_UP_TO_BOUNDARY(addr) (TRB_MAX_ISO_BUFF_SIZE - \
((addr) & (TRB_MAX_ISO_BUFF_SIZE - 1)))
#if TRBS_PER_SEGMENT < 2
#error "Incorrect TRBS_PER_SEGMENT. Minimal Transfer Ring size is 2."
#endif
/**
* struct cdns2_trb - represent Transfer Descriptor block.
* @buffer: pointer to buffer data.
* @length: length of data.
* @control: control flags.
*
* This structure describes transfer block handled by DMA module.
*/
struct cdns2_trb {
__le32 buffer;
__le32 length;
__le32 control;
};
#define TRB_SIZE (sizeof(struct cdns2_trb))
/*
* These two extra TRBs are reserved for isochronous transfer
* to inject 0 length packet and extra LINK TRB to synchronize the ISO transfer.
*/
#define TRB_ISO_RESERVED 2
#define TR_SEG_SIZE (TRB_SIZE * (TRBS_PER_SEGMENT + TRB_ISO_RESERVED))
/* TRB bit mask. */
#define TRB_TYPE_BITMASK GENMASK(15, 10)
#define TRB_TYPE(p) ((p) << 10)
#define TRB_FIELD_TO_TYPE(p) (((p) & TRB_TYPE_BITMASK) >> 10)
/* TRB type IDs. */
/* Used for Bulk, Interrupt, ISOC, and control data stage. */
#define TRB_NORMAL 1
/* TRB for linking ring segments. */
#define TRB_LINK 6
/* Cycle bit - indicates TRB ownership by driver or hw. */
#define TRB_CYCLE BIT(0)
/*
* When set to '1', the device will toggle its interpretation of the Cycle bit.
*/
#define TRB_TOGGLE BIT(1)
/* Interrupt on short packet. */
#define TRB_ISP BIT(2)
/* Chain bit associate this TRB with next one TRB. */
#define TRB_CHAIN BIT(4)
/* Interrupt on completion. */
#define TRB_IOC BIT(5)
/* Transfer_len bitmasks. */
#define TRB_LEN(p) ((p) & GENMASK(16, 0))
#define TRB_BURST(p) (((p) << 24) & GENMASK(31, 24))
#define TRB_FIELD_TO_BURST(p) (((p) & GENMASK(31, 24)) >> 24)
/* Data buffer pointer bitmasks. */
#define TRB_BUFFER(p) ((p) & GENMASK(31, 0))
/*-------------------------------------------------------------------------*/
/* Driver numeric constants. */
/* Maximum address that can be assigned to device. */
#define USB_DEVICE_MAX_ADDRESS 127
/* One control and 15 IN and 15 OUT endpoints. */
#define CDNS2_ENDPOINTS_NUM 31
#define CDNS2_EP_ZLP_BUF_SIZE 512
/*-------------------------------------------------------------------------*/
/* Used structures. */
struct cdns2_device;
/**
* struct cdns2_ring - transfer ring representation.
* @trbs: pointer to transfer ring.
* @dma: dma address of transfer ring.
* @free_trbs: number of free TRBs in transfer ring.
* @pcs: producer cycle state.
* @ccs: consumer cycle state.
* @enqueue: enqueue index in transfer ring.
* @dequeue: dequeue index in transfer ring.
*/
struct cdns2_ring {
struct cdns2_trb *trbs;
dma_addr_t dma;
int free_trbs;
u8 pcs;
u8 ccs;
int enqueue;
int dequeue;
};
/**
* struct cdns2_endpoint - extended device side representation of USB endpoint.
* @endpoint: usb endpoint.
* @pending_list: list of requests queuing on transfer ring.
* @deferred_list: list of requests waiting for queuing on transfer ring.
* @pdev: device associated with endpoint.
* @name: a human readable name e.g. ep1out.
* @ring: transfer ring associated with endpoint.
* @ep_state: state of endpoint.
* @idx: index of endpoint in pdev->eps table.
* @dir: endpoint direction.
* @num: endpoint number (1 - 15).
* @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK.
* @interval: interval between packets used for ISOC and Interrupt endpoint.
* @buffering: on-chip buffers assigned to endpoint.
* @trb_burst_size: number of burst used in TRB.
* @skip: Sometimes the controller cannot process isochronous endpoint ring
* quickly enough and it will miss some isoc tds on the ring and
* generate ISO transmition error.
* Driver sets skip flag when receive a ISO transmition error and
* process the missed TDs on the endpoint ring.
* @wa1_set: use WA1.
* @wa1_trb: TRB assigned to WA1.
* @wa1_trb_index: TRB index for WA1.
* @wa1_cycle_bit: correct cycle bit for WA1.
*/
struct cdns2_endpoint {
struct usb_ep endpoint;
struct list_head pending_list;
struct list_head deferred_list;
struct cdns2_device *pdev;
char name[20];
struct cdns2_ring ring;
#define EP_ENABLED BIT(0)
#define EP_STALLED BIT(1)
#define EP_STALL_PENDING BIT(2)
#define EP_WEDGE BIT(3)
#define EP_CLAIMED BIT(4)
#define EP_RING_FULL BIT(5)
#define EP_DEFERRED_DRDY BIT(6)
u32 ep_state;
u8 idx;
u8 dir;
u8 num;
u8 type;
int interval;
u8 buffering;
u8 trb_burst_size;
bool skip;
unsigned int wa1_set:1;
struct cdns2_trb *wa1_trb;
unsigned int wa1_trb_index;
unsigned int wa1_cycle_bit:1;
};
/**
* struct cdns2_request - extended device side representation of usb_request
* object.
* @request: generic usb_request object describing single I/O request.
* @pep: extended representation of usb_ep object.
* @trb: the first TRB association with this request.
* @start_trb: number of the first TRB in transfer ring.
* @end_trb: number of the last TRB in transfer ring.
* @list: used for queuing request in lists.
* @finished_trb: number of trb has already finished per request.
* @num_of_trb: how many trbs are associated with request.
*/
struct cdns2_request {
struct usb_request request;
struct cdns2_endpoint *pep;
struct cdns2_trb *trb;
int start_trb;
int end_trb;
struct list_head list;
int finished_trb;
int num_of_trb;
};
#define to_cdns2_request(r) (container_of(r, struct cdns2_request, request))
/* Stages used during enumeration process.*/
#define CDNS2_SETUP_STAGE 0x0
#define CDNS2_DATA_STAGE 0x1
#define CDNS2_STATUS_STAGE 0x2
/**
* struct cdns2_device - represent USB device.
* @dev: pointer to device structure associated whit this controller.
* @gadget: device side representation of the peripheral controller.
* @gadget_driver: pointer to the gadget driver.
* @lock: for synchronizing.
* @irq: interrupt line number.
* @regs: base address for registers
* @usb_regs: base address for common USB registers.
* @ep0_regs: base address for endpoint 0 related registers.
* @epx_regs: base address for all none control endpoint registers.
* @interrupt_regs: base address for interrupt handling related registers.
* @adma_regs: base address for ADMA registers.
* @eps_dma_pool: endpoint Transfer Ring pool.
* @setup: used while processing usb control requests.
* @ep0_preq: private request used while handling EP0.
* @ep0_stage: ep0 stage during enumeration process.
* @zlp_buf: zlp buffer.
* @dev_address: device address assigned by host.
* @eps: array of objects describing endpoints.
* @selected_ep: actually selected endpoint. It's used only to improve
* performance by limiting access to dma_ep_sel register.
* @is_selfpowered: device is self powered.
* @may_wakeup: allows device to remote wakeup the host.
* @status_completion_no_call: indicate that driver is waiting for status
* stage completion. It's used in deferred SET_CONFIGURATION request.
* @in_lpm: indicate the controller is in low power mode.
* @pending_status_wq: workqueue handling status stage for deferred requests.
* @pending_status_request: request for which status stage was deferred.
* @eps_supported: endpoints supported by controller in form:
* bit: 0 - ep0, 1 - epOut1, 2 - epIn1, 3 - epOut2 ...
* @burst_opt: array with the best burst size value for different TRB size.
* @onchip_tx_buf: size of transmit on-chip buffer in KB.
* @onchip_rx_buf: size of receive on-chip buffer in KB.
*/
struct cdns2_device {
struct device *dev;
struct usb_gadget gadget;
struct usb_gadget_driver *gadget_driver;
/* generic spin-lock for drivers */
spinlock_t lock;
int irq;
void __iomem *regs;
struct cdns2_usb_regs __iomem *usb_regs;
struct cdns2_ep0_regs __iomem *ep0_regs;
struct cdns2_epx_regs __iomem *epx_regs;
struct cdns2_interrupt_regs __iomem *interrupt_regs;
struct cdns2_adma_regs __iomem *adma_regs;
struct dma_pool *eps_dma_pool;
struct usb_ctrlrequest setup;
struct cdns2_request ep0_preq;
u8 ep0_stage;
void *zlp_buf;
u8 dev_address;
struct cdns2_endpoint eps[CDNS2_ENDPOINTS_NUM];
u32 selected_ep;
bool is_selfpowered;
bool may_wakeup;
bool status_completion_no_call;
bool in_lpm;
struct work_struct pending_status_wq;
struct usb_request *pending_status_request;
u32 eps_supported;
u8 burst_opt[MAX_ISO_SIZE + 1];
/*in KB */
u16 onchip_tx_buf;
u16 onchip_rx_buf;
};
#define CDNS2_IF_EP_EXIST(pdev, ep_num, dir) \
((pdev)->eps_supported & \
(BIT(ep_num) << ((dir) ? 0 : 16)))
dma_addr_t cdns2_trb_virt_to_dma(struct cdns2_endpoint *pep,
struct cdns2_trb *trb);
void cdns2_pending_setup_status_handler(struct work_struct *work);
void cdns2_select_ep(struct cdns2_device *pdev, u32 ep);
struct cdns2_request *cdns2_next_preq(struct list_head *list);
struct usb_request *cdns2_gadget_ep_alloc_request(struct usb_ep *ep,
gfp_t gfp_flags);
void cdns2_gadget_ep_free_request(struct usb_ep *ep,
struct usb_request *request);
int cdns2_gadget_ep_dequeue(struct usb_ep *ep, struct usb_request *request);
void cdns2_gadget_giveback(struct cdns2_endpoint *pep,
struct cdns2_request *priv_req,
int status);
void cdns2_init_ep0(struct cdns2_device *pdev, struct cdns2_endpoint *pep);
void cdns2_ep0_config(struct cdns2_device *pdev);
void cdns2_handle_ep0_interrupt(struct cdns2_device *pdev, int dir);
void cdns2_handle_setup_packet(struct cdns2_device *pdev);
int cdns2_gadget_resume(struct cdns2_device *pdev, bool hibernated);
int cdns2_gadget_suspend(struct cdns2_device *pdev);
void cdns2_gadget_remove(struct cdns2_device *pdev);
int cdns2_gadget_init(struct cdns2_device *pdev);
void set_reg_bit_8(void __iomem *ptr, u8 mask);
int cdns2_halt_endpoint(struct cdns2_device *pdev, struct cdns2_endpoint *pep,
int value);
#endif /* __LINUX_CDNS2_GADGET */
@@ -0,0 +1,136 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Cadence USBHS-DEV controller - PCI Glue driver.
*
* Copyright (C) 2023 Cadence.
*
* Author: Pawel Laszczak <pawell@cadence.com>
*
*/
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include "cdns2-gadget.h"
#define PCI_DRIVER_NAME "cdns-pci-usbhs"
#define PCI_BAR_DEV 0
#define PCI_DEV_FN_DEVICE 0
static int cdns2_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
resource_size_t rsrc_start, rsrc_len;
struct device *dev = &pdev->dev;
struct cdns2_device *priv_dev;
struct resource *res;
int ret;
/* For GADGET PCI (devfn) function number is 0. */
if (!id || pdev->devfn != PCI_DEV_FN_DEVICE ||
pdev->class != PCI_CLASS_SERIAL_USB_DEVICE)
return -EINVAL;
ret = pcim_enable_device(pdev);
if (ret) {
dev_err(&pdev->dev, "Enabling PCI device has failed %d\n", ret);
return ret;
}
pci_set_master(pdev);
priv_dev = devm_kzalloc(&pdev->dev, sizeof(*priv_dev), GFP_KERNEL);
if (!priv_dev)
return -ENOMEM;
dev_dbg(dev, "Initialize resources\n");
rsrc_start = pci_resource_start(pdev, PCI_BAR_DEV);
rsrc_len = pci_resource_len(pdev, PCI_BAR_DEV);
res = devm_request_mem_region(dev, rsrc_start, rsrc_len, "dev");
if (!res) {
dev_dbg(dev, "controller already in use\n");
return -EBUSY;
}
priv_dev->regs = devm_ioremap(dev, rsrc_start, rsrc_len);
if (!priv_dev->regs) {
dev_dbg(dev, "error mapping memory\n");
return -EFAULT;
}
priv_dev->irq = pdev->irq;
dev_dbg(dev, "USBSS-DEV physical base addr: %pa\n",
&rsrc_start);
priv_dev->dev = dev;
priv_dev->eps_supported = 0x000f000f;
priv_dev->onchip_tx_buf = 16;
priv_dev->onchip_rx_buf = 16;
ret = cdns2_gadget_init(priv_dev);
if (ret)
return ret;
pci_set_drvdata(pdev, priv_dev);
device_wakeup_enable(&pdev->dev);
if (pci_dev_run_wake(pdev))
pm_runtime_put_noidle(&pdev->dev);
return 0;
}
static void cdns2_pci_remove(struct pci_dev *pdev)
{
struct cdns2_device *priv_dev = pci_get_drvdata(pdev);
if (pci_dev_run_wake(pdev))
pm_runtime_get_noresume(&pdev->dev);
cdns2_gadget_remove(priv_dev);
}
static int cdns2_pci_suspend(struct device *dev)
{
struct cdns2_device *priv_dev = dev_get_drvdata(dev);
return cdns2_gadget_suspend(priv_dev);
}
static int cdns2_pci_resume(struct device *dev)
{
struct cdns2_device *priv_dev = dev_get_drvdata(dev);
return cdns2_gadget_resume(priv_dev, 1);
}
static const struct dev_pm_ops cdns2_pci_pm_ops = {
SYSTEM_SLEEP_PM_OPS(cdns2_pci_suspend, cdns2_pci_resume)
};
static const struct pci_device_id cdns2_pci_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_USB),
.class = PCI_CLASS_SERIAL_USB_DEVICE },
{ 0, }
};
static struct pci_driver cdns2_pci_driver = {
.name = "cdns2-pci",
.id_table = cdns2_pci_ids,
.probe = cdns2_pci_probe,
.remove = cdns2_pci_remove,
.driver = {
.pm = pm_ptr(&cdns2_pci_pm_ops),
}
};
module_pci_driver(cdns2_pci_driver);
MODULE_DEVICE_TABLE(pci, cdns2_pci_ids);
MODULE_ALIAS("pci:cdns2");
MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Cadence CDNS2 PCI driver");
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
/*
* USBHS device controller driver Trace Support
*
* Copyright (C) 2023 Cadence.
*
* Author: Pawel Laszczak <pawell@cadence.com>
*/
#define CREATE_TRACE_POINTS
#include "cdns2-trace.h"
@@ -0,0 +1,536 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* USBHS-DEV device controller driver.
* Trace support header file.
*
* Copyright (C) 2023 Cadence.
*
* Author: Pawel Laszczak <pawell@cadence.com>
*/
#undef TRACE_SYSTEM
#define TRACE_SYSTEM cdns2-dev
/*
* The TRACE_SYSTEM_VAR defaults to TRACE_SYSTEM, but must be a
* legitimate C variable. It is not exported to user space.
*/
#undef TRACE_SYSTEM_VAR
#define TRACE_SYSTEM_VAR cdns2_dev
#if !defined(__LINUX_CDNS2_TRACE) || defined(TRACE_HEADER_MULTI_READ)
#define __LINUX_CDNS2_TRACE
#include <linux/types.h>
#include <linux/tracepoint.h>
#include <asm/byteorder.h>
#include <linux/usb/ch9.h>
#include "cdns2-gadget.h"
#include "cdns2-debug.h"
#define CDNS2_MSG_MAX 500
DECLARE_EVENT_CLASS(cdns2_log_enable_disable,
TP_PROTO(int set),
TP_ARGS(set),
TP_STRUCT__entry(
__field(int, set)
),
TP_fast_assign(
__entry->set = set;
),
TP_printk("%s", __entry->set ? "enabled" : "disabled")
);
DEFINE_EVENT(cdns2_log_enable_disable, cdns2_pullup,
TP_PROTO(int set),
TP_ARGS(set)
);
DECLARE_EVENT_CLASS(cdns2_log_simple,
TP_PROTO(char *msg),
TP_ARGS(msg),
TP_STRUCT__entry(
__string(text, msg)
),
TP_fast_assign(
__assign_str(text);
),
TP_printk("%s", __get_str(text))
);
DEFINE_EVENT(cdns2_log_simple, cdns2_no_room_on_ring,
TP_PROTO(char *msg),
TP_ARGS(msg)
);
DEFINE_EVENT(cdns2_log_simple, cdns2_ep0_status_stage,
TP_PROTO(char *msg),
TP_ARGS(msg)
);
DEFINE_EVENT(cdns2_log_simple, cdns2_ep0_setup,
TP_PROTO(char *msg),
TP_ARGS(msg)
);
DEFINE_EVENT(cdns2_log_simple, cdns2_device_state,
TP_PROTO(char *msg),
TP_ARGS(msg)
);
TRACE_EVENT(cdns2_ep_halt,
TP_PROTO(struct cdns2_endpoint *ep_priv, u8 halt, u8 flush),
TP_ARGS(ep_priv, halt, flush),
TP_STRUCT__entry(
__string(name, ep_priv->name)
__field(u8, halt)
__field(u8, flush)
),
TP_fast_assign(
__assign_str(name);
__entry->halt = halt;
__entry->flush = flush;
),
TP_printk("Halt %s for %s: %s", __entry->flush ? " and flush" : "",
__get_str(name), __entry->halt ? "set" : "cleared")
);
TRACE_EVENT(cdns2_wa1,
TP_PROTO(struct cdns2_endpoint *ep_priv, char *msg),
TP_ARGS(ep_priv, msg),
TP_STRUCT__entry(
__string(ep_name, ep_priv->name)
__string(msg, msg)
),
TP_fast_assign(
__assign_str(ep_name);
__assign_str(msg);
),
TP_printk("WA1: %s %s", __get_str(ep_name), __get_str(msg))
);
DECLARE_EVENT_CLASS(cdns2_log_doorbell,
TP_PROTO(struct cdns2_endpoint *pep, u32 ep_trbaddr),
TP_ARGS(pep, ep_trbaddr),
TP_STRUCT__entry(
__string(name, pep->num ? pep->name :
(pep->dir ? "ep0in" : "ep0out"))
__field(u32, ep_trbaddr)
),
TP_fast_assign(
__assign_str(name);
__entry->ep_trbaddr = ep_trbaddr;
),
TP_printk("%s, ep_trbaddr %08x", __get_str(name),
__entry->ep_trbaddr)
);
DEFINE_EVENT(cdns2_log_doorbell, cdns2_doorbell_ep0,
TP_PROTO(struct cdns2_endpoint *pep, u32 ep_trbaddr),
TP_ARGS(pep, ep_trbaddr)
);
DEFINE_EVENT(cdns2_log_doorbell, cdns2_doorbell_epx,
TP_PROTO(struct cdns2_endpoint *pep, u32 ep_trbaddr),
TP_ARGS(pep, ep_trbaddr)
);
DECLARE_EVENT_CLASS(cdns2_log_usb_irq,
TP_PROTO(u32 usb_irq, u32 ext_irq),
TP_ARGS(usb_irq, ext_irq),
TP_STRUCT__entry(
__field(u32, usb_irq)
__field(u32, ext_irq)
),
TP_fast_assign(
__entry->usb_irq = usb_irq;
__entry->ext_irq = ext_irq;
),
TP_printk("%s", cdns2_decode_usb_irq(__get_buf(CDNS2_MSG_MAX),
CDNS2_MSG_MAX,
__entry->usb_irq,
__entry->ext_irq))
);
DEFINE_EVENT(cdns2_log_usb_irq, cdns2_usb_irq,
TP_PROTO(u32 usb_irq, u32 ext_irq),
TP_ARGS(usb_irq, ext_irq)
);
TRACE_EVENT(cdns2_dma_ep_ists,
TP_PROTO(u32 dma_ep_ists),
TP_ARGS(dma_ep_ists),
TP_STRUCT__entry(
__field(u32, dma_ep_ists)
),
TP_fast_assign(
__entry->dma_ep_ists = dma_ep_ists;
),
TP_printk("OUT: 0x%04x, IN: 0x%04x", (u16)__entry->dma_ep_ists,
__entry->dma_ep_ists >> 16)
);
DECLARE_EVENT_CLASS(cdns2_log_epx_irq,
TP_PROTO(struct cdns2_device *pdev, struct cdns2_endpoint *pep),
TP_ARGS(pdev, pep),
TP_STRUCT__entry(
__string(ep_name, pep->name)
__field(u32, ep_sts)
__field(u32, ep_ists)
__field(u32, ep_traddr)
),
TP_fast_assign(
__assign_str(ep_name);
__entry->ep_sts = readl(&pdev->adma_regs->ep_sts);
__entry->ep_ists = readl(&pdev->adma_regs->ep_ists);
__entry->ep_traddr = readl(&pdev->adma_regs->ep_traddr);
),
TP_printk("%s, ep_traddr: %08x",
cdns2_decode_epx_irq(__get_buf(CDNS2_MSG_MAX), CDNS2_MSG_MAX,
__get_str(ep_name),
__entry->ep_ists, __entry->ep_sts),
__entry->ep_traddr)
);
DEFINE_EVENT(cdns2_log_epx_irq, cdns2_epx_irq,
TP_PROTO(struct cdns2_device *pdev, struct cdns2_endpoint *pep),
TP_ARGS(pdev, pep)
);
DECLARE_EVENT_CLASS(cdns2_log_ep0_irq,
TP_PROTO(struct cdns2_device *pdev),
TP_ARGS(pdev),
TP_STRUCT__entry(
__field(int, ep_dir)
__field(u32, ep_ists)
__field(u32, ep_sts)
),
TP_fast_assign(
__entry->ep_dir = pdev->selected_ep;
__entry->ep_ists = readl(&pdev->adma_regs->ep_ists);
__entry->ep_sts = readl(&pdev->adma_regs->ep_sts);
),
TP_printk("%s", cdns2_decode_ep0_irq(__get_buf(CDNS2_MSG_MAX),
CDNS2_MSG_MAX,
__entry->ep_ists, __entry->ep_sts,
__entry->ep_dir))
);
DEFINE_EVENT(cdns2_log_ep0_irq, cdns2_ep0_irq,
TP_PROTO(struct cdns2_device *pdev),
TP_ARGS(pdev)
);
DECLARE_EVENT_CLASS(cdns2_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)
),
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_buf(CDNS2_MSG_MAX), CDNS2_MSG_MAX,
__entry->bRequestType,
__entry->bRequest, __entry->wValue,
__entry->wIndex, __entry->wLength)
)
);
DEFINE_EVENT(cdns2_log_ctrl, cdns2_ctrl_req,
TP_PROTO(struct usb_ctrlrequest *ctrl),
TP_ARGS(ctrl)
);
DECLARE_EVENT_CLASS(cdns2_log_request,
TP_PROTO(struct cdns2_request *preq),
TP_ARGS(preq),
TP_STRUCT__entry(
__string(name, preq->pep->name)
__field(struct usb_request *, request)
__field(struct cdns2_request *, preq)
__field(void *, buf)
__field(unsigned int, actual)
__field(unsigned int, length)
__field(int, status)
__field(dma_addr_t, dma)
__field(int, zero)
__field(int, short_not_ok)
__field(int, no_interrupt)
__field(struct scatterlist*, sg)
__field(unsigned int, num_sgs)
__field(unsigned int, num_mapped_sgs)
__field(int, start_trb)
__field(int, end_trb)
),
TP_fast_assign(
__assign_str(name);
__entry->request = &preq->request;
__entry->preq = preq;
__entry->buf = preq->request.buf;
__entry->actual = preq->request.actual;
__entry->length = preq->request.length;
__entry->status = preq->request.status;
__entry->dma = preq->request.dma;
__entry->zero = preq->request.zero;
__entry->short_not_ok = preq->request.short_not_ok;
__entry->no_interrupt = preq->request.no_interrupt;
__entry->sg = preq->request.sg;
__entry->num_sgs = preq->request.num_sgs;
__entry->num_mapped_sgs = preq->request.num_mapped_sgs;
__entry->start_trb = preq->start_trb;
__entry->end_trb = preq->end_trb;
),
TP_printk("%s: req: %p, preq: %p, req buf: %p, length: %u/%u, status: %d,"
"buf dma: (%pad), %s%s%s, sg: %p, num_sgs: %d, num_m_sgs: %d,"
"trb: [start: %d, end: %d]",
__get_str(name), __entry->request, __entry->preq,
__entry->buf, __entry->actual, __entry->length,
__entry->status, &__entry->dma,
__entry->zero ? "Z" : "z",
__entry->short_not_ok ? "S" : "s",
__entry->no_interrupt ? "I" : "i",
__entry->sg, __entry->num_sgs, __entry->num_mapped_sgs,
__entry->start_trb,
__entry->end_trb
)
);
DEFINE_EVENT(cdns2_log_request, cdns2_request_enqueue,
TP_PROTO(struct cdns2_request *preq),
TP_ARGS(preq)
);
DEFINE_EVENT(cdns2_log_request, cdns2_request_enqueue_error,
TP_PROTO(struct cdns2_request *preq),
TP_ARGS(preq)
);
DEFINE_EVENT(cdns2_log_request, cdns2_alloc_request,
TP_PROTO(struct cdns2_request *preq),
TP_ARGS(preq)
);
DEFINE_EVENT(cdns2_log_request, cdns2_free_request,
TP_PROTO(struct cdns2_request *preq),
TP_ARGS(preq)
);
DEFINE_EVENT(cdns2_log_request, cdns2_request_dequeue,
TP_PROTO(struct cdns2_request *preq),
TP_ARGS(preq)
);
DEFINE_EVENT(cdns2_log_request, cdns2_request_giveback,
TP_PROTO(struct cdns2_request *preq),
TP_ARGS(preq)
);
DECLARE_EVENT_CLASS(cdns2_log_trb,
TP_PROTO(struct cdns2_endpoint *pep, struct cdns2_trb *trb),
TP_ARGS(pep, trb),
TP_STRUCT__entry(
__string(name, pep->name)
__field(struct cdns2_trb *, trb)
__field(u32, buffer)
__field(u32, length)
__field(u32, control)
__field(u32, type)
),
TP_fast_assign(
__assign_str(name);
__entry->trb = trb;
__entry->buffer = le32_to_cpu(trb->buffer);
__entry->length = le32_to_cpu(trb->length);
__entry->control = le32_to_cpu(trb->control);
__entry->type = usb_endpoint_type(pep->endpoint.desc);
),
TP_printk("%s: trb V: %p, dma buf: P: 0x%08x, %s",
__get_str(name), __entry->trb, __entry->buffer,
cdns2_decode_trb(__get_buf(CDNS2_MSG_MAX), CDNS2_MSG_MAX,
__entry->control, __entry->length,
__entry->buffer))
);
DEFINE_EVENT(cdns2_log_trb, cdns2_queue_trb,
TP_PROTO(struct cdns2_endpoint *pep, struct cdns2_trb *trb),
TP_ARGS(pep, trb)
);
DEFINE_EVENT(cdns2_log_trb, cdns2_complete_trb,
TP_PROTO(struct cdns2_endpoint *pep, struct cdns2_trb *trb),
TP_ARGS(pep, trb)
);
DECLARE_EVENT_CLASS(cdns2_log_ring,
TP_PROTO(struct cdns2_endpoint *pep),
TP_ARGS(pep),
TP_STRUCT__entry(
__dynamic_array(u8, tr_seg, TR_SEG_SIZE)
__dynamic_array(u8, pep, sizeof(struct cdns2_endpoint))
__dynamic_array(char, buffer,
(TRBS_PER_SEGMENT * 65) + CDNS2_MSG_MAX)
),
TP_fast_assign(
memcpy(__get_dynamic_array(pep), pep,
sizeof(struct cdns2_endpoint));
memcpy(__get_dynamic_array(tr_seg), pep->ring.trbs,
TR_SEG_SIZE);
),
TP_printk("%s",
cdns2_raw_ring((struct cdns2_endpoint *)__get_str(pep),
(struct cdns2_trb *)__get_str(tr_seg),
__get_str(buffer),
(TRBS_PER_SEGMENT * 65) + CDNS2_MSG_MAX))
);
DEFINE_EVENT(cdns2_log_ring, cdns2_ring,
TP_PROTO(struct cdns2_endpoint *pep),
TP_ARGS(pep)
);
DECLARE_EVENT_CLASS(cdns2_log_ep,
TP_PROTO(struct cdns2_endpoint *pep),
TP_ARGS(pep),
TP_STRUCT__entry(
__string(name, pep->name)
__field(unsigned int, maxpacket)
__field(unsigned int, maxpacket_limit)
__field(unsigned int, flags)
__field(unsigned int, dir)
__field(u8, enqueue)
__field(u8, dequeue)
),
TP_fast_assign(
__assign_str(name);
__entry->maxpacket = pep->endpoint.maxpacket;
__entry->maxpacket_limit = pep->endpoint.maxpacket_limit;
__entry->flags = pep->ep_state;
__entry->dir = pep->dir;
__entry->enqueue = pep->ring.enqueue;
__entry->dequeue = pep->ring.dequeue;
),
TP_printk("%s: mps: %d/%d, enq idx: %d, deq idx: %d, "
"flags: %s%s%s%s, dir: %s",
__get_str(name), __entry->maxpacket,
__entry->maxpacket_limit, __entry->enqueue,
__entry->dequeue,
__entry->flags & EP_ENABLED ? "EN | " : "",
__entry->flags & EP_STALLED ? "STALLED | " : "",
__entry->flags & EP_WEDGE ? "WEDGE | " : "",
__entry->flags & EP_RING_FULL ? "RING FULL |" : "",
__entry->dir ? "IN" : "OUT"
)
);
DEFINE_EVENT(cdns2_log_ep, cdns2_gadget_ep_enable,
TP_PROTO(struct cdns2_endpoint *pep),
TP_ARGS(pep)
);
DEFINE_EVENT(cdns2_log_ep, cdns2_gadget_ep_disable,
TP_PROTO(struct cdns2_endpoint *pep),
TP_ARGS(pep)
);
DEFINE_EVENT(cdns2_log_ep, cdns2_ep_busy_try_halt_again,
TP_PROTO(struct cdns2_endpoint *pep),
TP_ARGS(pep)
);
DECLARE_EVENT_CLASS(cdns2_log_request_handled,
TP_PROTO(struct cdns2_request *priv_req, int current_index,
int handled),
TP_ARGS(priv_req, current_index, handled),
TP_STRUCT__entry(
__field(struct cdns2_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->pep->ring.dequeue;
__entry->enqueue_idx = priv_req->pep->ring.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(cdns2_log_request_handled, cdns2_request_handled,
TP_PROTO(struct cdns2_request *priv_req, int current_index,
int handled),
TP_ARGS(priv_req, current_index, handled)
);
DECLARE_EVENT_CLASS(cdns2_log_epx_reg_config,
TP_PROTO(struct cdns2_device *pdev, struct cdns2_endpoint *pep),
TP_ARGS(pdev, pep),
TP_STRUCT__entry(
__string(ep_name, pep->name)
__field(u8, burst_size)
__field(__le16, maxpack_reg)
__field(__u8, con_reg)
__field(u32, ep_sel_reg)
__field(u32, ep_sts_en_reg)
__field(u32, ep_cfg_reg)
),
TP_fast_assign(
__assign_str(ep_name);
__entry->burst_size = pep->trb_burst_size;
__entry->maxpack_reg = pep->dir ? readw(&pdev->epx_regs->txmaxpack[pep->num - 1]) :
readw(&pdev->epx_regs->rxmaxpack[pep->num - 1]);
__entry->con_reg = pep->dir ? readb(&pdev->epx_regs->ep[pep->num - 1].txcon) :
readb(&pdev->epx_regs->ep[pep->num - 1].rxcon);
__entry->ep_sel_reg = readl(&pdev->adma_regs->ep_sel);
__entry->ep_sts_en_reg = readl(&pdev->adma_regs->ep_sts_en);
__entry->ep_cfg_reg = readl(&pdev->adma_regs->ep_cfg);
),
TP_printk("%s, maxpack: %d, con: %02x, dma_ep_sel: %08x, dma_ep_sts_en: %08x"
" dma_ep_cfg %08x",
__get_str(ep_name), __entry->maxpack_reg, __entry->con_reg,
__entry->ep_sel_reg, __entry->ep_sts_en_reg,
__entry->ep_cfg_reg
)
);
DEFINE_EVENT(cdns2_log_epx_reg_config, cdns2_epx_hw_cfg,
TP_PROTO(struct cdns2_device *pdev, struct cdns2_endpoint *pep),
TP_ARGS(pdev, pep)
);
#endif /* __LINUX_CDNS2_TRACE */
/* This part must be outside header guard. */
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE cdns2-trace
#include <trace/define_trace.h>