root/drivers/usb/gadget/udc/cdns2/cdns2-debug.h
/* 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*/