// SPDX-License-Identifier: GPL-2.0
/**
* xhci-dbgcap.c - xHCI debug capability support
*
* Copyright (C) 2017 Intel Corporation
*
* Author: Lu Baolu <baolu.lu@linux.intel.com>
*/
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/nls.h>
#include "xhci.h"
#include "xhci-trace.h"
#include "xhci-dbgcap.h"
static inline void *
dbc_dma_alloc_coherent(struct xhci_hcd *xhci, size_t size,
dma_addr_t *dma_handle, gfp_t flags)
{
void *vaddr;
vaddr = dma_alloc_coherent(xhci_to_hcd(xhci)->self.sysdev,
size, dma_handle, flags);
return vaddr;
}
static inline void
dbc_dma_free_coherent(struct xhci_hcd *xhci, size_t size,
void *cpu_addr, dma_addr_t dma_handle)
{
if (cpu_addr)
dma_free_coherent(xhci_to_hcd(xhci)->self.sysdev,
size, cpu_addr, dma_handle);
}
static u32 xhci_dbc_populate_strings(struct dbc_str_descs *strings)
{
struct usb_string_descriptor *s_desc;
u32 string_length;
/* Serial string: */
s_desc = (struct usb_string_descriptor *)strings->serial;
utf8s_to_utf16s(DBC_STRING_SERIAL, strlen(DBC_STRING_SERIAL),
UTF16_LITTLE_ENDIAN, (wchar_t *)s_desc->wData,
DBC_MAX_STRING_LENGTH);
s_desc->bLength = (strlen(DBC_STRING_SERIAL) + 1) * 2;
s_desc->bDescriptorType = USB_DT_STRING;
string_length = s_desc->bLength;
string_length <<= 8;
/* Product string: */
s_desc = (struct usb_string_descriptor *)strings->product;
utf8s_to_utf16s(DBC_STRING_PRODUCT, strlen(DBC_STRING_PRODUCT),
UTF16_LITTLE_ENDIAN, (wchar_t *)s_desc->wData,
DBC_MAX_STRING_LENGTH);
s_desc->bLength = (strlen(DBC_STRING_PRODUCT) + 1) * 2;
s_desc->bDescriptorType = USB_DT_STRING;
string_length += s_desc->bLength;
string_length <<= 8;
/* Manufacture string: */
s_desc = (struct usb_string_descriptor *)strings->manufacturer;
utf8s_to_utf16s(DBC_STRING_MANUFACTURER,
strlen(DBC_STRING_MANUFACTURER),
UTF16_LITTLE_ENDIAN, (wchar_t *)s_desc->wData,
DBC_MAX_STRING_LENGTH);
s_desc->bLength = (strlen(DBC_STRING_MANUFACTURER) + 1) * 2;
s_desc->bDescriptorType = USB_DT_STRING;
string_length += s_desc->bLength;
string_length <<= 8;
/* String0: */
strings->string0[0] = 4;
strings->string0[1] = USB_DT_STRING;
strings->string0[2] = 0x09;
strings->string0[3] = 0x04;
string_length += 4;
return string_length;
}
static void xhci_dbc_init_contexts(struct xhci_hcd *xhci, u32 string_length)
{
struct xhci_dbc *dbc;
struct dbc_info_context *info;
struct xhci_ep_ctx *ep_ctx;
u32 dev_info;
dma_addr_t deq, dma;
unsigned int max_burst;
dbc = xhci->dbc;
if (!dbc)
return;
/* Populate info Context: */
info = (struct dbc_info_context *)dbc->ctx->bytes;
dma = dbc->string_dma;
info->string0 = cpu_to_le64(dma);
info->manufacturer = cpu_to_le64(dma + DBC_MAX_STRING_LENGTH);
info->product = cpu_to_le64(dma + DBC_MAX_STRING_LENGTH * 2);
info->serial = cpu_to_le64(dma + DBC_MAX_STRING_LENGTH * 3);
info->length = cpu_to_le32(string_length);
/* Populate bulk out endpoint context: */
ep_ctx = dbc_bulkout_ctx(dbc);
max_burst = DBC_CTRL_MAXBURST(readl(&dbc->regs->control));
deq = dbc_bulkout_enq(dbc);
ep_ctx->ep_info = 0;
ep_ctx->ep_info2 = dbc_epctx_info2(BULK_OUT_EP, 1024, max_burst);
ep_ctx->deq = cpu_to_le64(deq | dbc->ring_out->cycle_state);
/* Populate bulk in endpoint context: */
ep_ctx = dbc_bulkin_ctx(dbc);
deq = dbc_bulkin_enq(dbc);
ep_ctx->ep_info = 0;
ep_ctx->ep_info2 = dbc_epctx_info2(BULK_IN_EP, 1024, max_burst);
ep_ctx->deq = cpu_to_le64(deq | dbc->ring_in->cycle_state);
/* Set DbC context and info registers: */
xhci_write_64(xhci, dbc->ctx->dma, &dbc->regs->dccp);
dev_info = cpu_to_le32((DBC_VENDOR_ID << 16) | DBC_PROTOCOL);
writel(dev_info, &dbc->regs->devinfo1);
dev_info = cpu_to_le32((DBC_DEVICE_REV << 16) | DBC_PRODUCT_ID);
writel(dev_info, &dbc->regs->devinfo2);
}
static void xhci_dbc_giveback(struct dbc_request *req, int status)
__releases(&dbc->lock)
__acquires(&dbc->lock)
{
struct dbc_ep *dep = req->dep;
struct xhci_dbc *dbc = dep->dbc;
struct xhci_hcd *xhci = dbc->xhci;
struct device *dev = xhci_to_hcd(dbc->xhci)->self.sysdev;
list_del_init(&req->list_pending);
req->trb_dma = 0;
req->trb = NULL;
if (req->status == -EINPROGRESS)
req->status = status;
trace_xhci_dbc_giveback_request(req);
dma_unmap_single(dev,
req->dma,
req->