2018-07-19 18:01:09

by Pawel Laszczak

[permalink] [raw]
Subject: [PATCH 00/31] Introduced new Cadence USBSSP DRD Driver

This patch set introduce new Cadence USBSSP DRD driver
to linux kernel.

The Cadence USBSSP DRD Driver s a highly
configurable IP Core which can be
instantiated as Dual-Role Device (DRD),
Peripheral Only and Host Only (XHCI)
configurations.

The current driver has been validated with
FPGA burned. We have support for PCIe
bus, which is used on FPGA prototyping.

The host site of USBSSP controller is compliance
with XHCI specification, so it works with
standard XHCI linux driver.

Also, device side of USBSSP controller was designed
in such way to looks like XHCI. It means that
most of logic of USBSSP controller is also compliance
with XHCI specification.

Consequently, the USBSSP driver for peripheral mode
is very similar to XHCI driver.

This version of driver supports only Device mode but
DRD and Host mode will be added in the future.

---

Pawel Laszczak (31):
usb: usbssp: Defined register maps and other useful structures.
usb: usbssp: Added some decoding functions.
usb: usbssp: Add trace events used in driver
usb: usbssp: Added USBSSP platform driver
usb: usbssp: Added first part of initialization sequence.
usb: usbssp: added template functions used by upper layer.
usb: usbssp: Initialization - added usbssp_mem_init
usb: usbssp: Added ring and segment handling functions.
usb: usbssp: add implementation of usbssp_mem_cleanup
usb: usbssp: added usbssp_trb_in_td function.
usb: usbssp: added function for stopping driver.
usb: usbssp: added functions for queuing commands.
usb: usbssp: addec procedure for handlin Port Status Change events.
usb: usbssp: added procedure handling command completion events.
usb: usbssp: added device controller error, transfer and SETUP
completion event.
usb: usbssp: added connect/disconnect procedures.
usb: usbssp: added implementation of usbssp_halt_endpoint function.
usb: usbssp: added handling of Port Reset event.
usb: usbssp: added support for USB enumeration process.
usb: usbssp: added queuing procedure for control transfer.
usb: usbssp: added queuing procedure for BULK and INT transfer.
usb: usbssp: added procedure removing request from transfer ring
usb: usbssp: added implementation of transfer events.
usb: usbssp: added detecting command timeout.
usb: usbssp: added implementation of usbssp interface.
usb: usbssp: added endpoint configuration functionality.
usb: usbssp: implements usbssp_gadget_ep_enable function
usb: usbssp: implemented usbssp_gadget_ep_disable function.
usb: usbssp: added support for LPM.
usb: usbssp: added support for TEST_MODE.
usb: usbssp: add pci to platform driver wrapper.

drivers/usb/Kconfig | 2 +
drivers/usb/Makefile | 2 +
drivers/usb/usbssp/Kconfig | 29 +
drivers/usb/usbssp/Makefile | 15 +
drivers/usb/usbssp/gadget-dbg.c | 39 +
drivers/usb/usbssp/gadget-ep0.c | 571 +++++
drivers/usb/usbssp/gadget-ext-caps.h | 102 +
drivers/usb/usbssp/gadget-if.c | 569 +++++
drivers/usb/usbssp/gadget-mem.c | 1938 +++++++++++++++
drivers/usb/usbssp/gadget-port.c | 287 +++
drivers/usb/usbssp/gadget-ring.c | 3452 ++++++++++++++++++++++++++
drivers/usb/usbssp/gadget-trace.c | 13 +
drivers/usb/usbssp/gadget-trace.h | 482 ++++
drivers/usb/usbssp/gadget.c | 1909 ++++++++++++++
drivers/usb/usbssp/gadget.h | 2373 ++++++++++++++++++
drivers/usb/usbssp/usbssp-pci-wrap.c | 226 ++
drivers/usb/usbssp/usbssp-plat.c | 186 ++
17 files changed, 12195 insertions(+)
create mode 100644 drivers/usb/usbssp/Kconfig
create mode 100644 drivers/usb/usbssp/Makefile
create mode 100644 drivers/usb/usbssp/gadget-dbg.c
create mode 100644 drivers/usb/usbssp/gadget-ep0.c
create mode 100644 drivers/usb/usbssp/gadget-ext-caps.h
create mode 100644 drivers/usb/usbssp/gadget-if.c
create mode 100644 drivers/usb/usbssp/gadget-mem.c
create mode 100644 drivers/usb/usbssp/gadget-port.c
create mode 100644 drivers/usb/usbssp/gadget-ring.c
create mode 100644 drivers/usb/usbssp/gadget-trace.c
create mode 100644 drivers/usb/usbssp/gadget-trace.h
create mode 100644 drivers/usb/usbssp/gadget.c
create mode 100644 drivers/usb/usbssp/gadget.h
create mode 100644 drivers/usb/usbssp/usbssp-pci-wrap.c
create mode 100644 drivers/usb/usbssp/usbssp-plat.c

--
2.17.1



2018-07-19 18:00:23

by Pawel Laszczak

[permalink] [raw]
Subject: [PATCH 05/31] usb: usbssp: Added first part of initialization sequence.

Patch adds some initialization function. The initialization sequence
is quite complicated and this patch implements it only partially.
Initialization will be completed in next few patches.

Patch introduce three new files:
1. gadget-dbg.c - file contains functions used for debugging purpose.
2. gadget-ext-caps.h - holds macro definition related to
Extended Capabilities
3. gadget-if - file implements stuff related to upper layer
(e.g usb_ep_ops, usb_gadget_ops interface).

Signed-off-by: Pawel Laszczak <[email protected]>
---
drivers/usb/usbssp/Makefile | 1 +
drivers/usb/usbssp/gadget-dbg.c | 30 ++++
drivers/usb/usbssp/gadget-ext-caps.h | 53 ++++++
drivers/usb/usbssp/gadget-if.c | 24 +++
drivers/usb/usbssp/gadget.c | 242 +++++++++++++++++++++++++++
drivers/usb/usbssp/gadget.h | 15 ++
6 files changed, 365 insertions(+)
create mode 100644 drivers/usb/usbssp/gadget-dbg.c
create mode 100644 drivers/usb/usbssp/gadget-ext-caps.h
create mode 100644 drivers/usb/usbssp/gadget-if.c

diff --git a/drivers/usb/usbssp/Makefile b/drivers/usb/usbssp/Makefile
index d85f15afb51c..0606f3c63cd0 100644
--- a/drivers/usb/usbssp/Makefile
+++ b/drivers/usb/usbssp/Makefile
@@ -5,6 +5,7 @@ CFLAGS_gadget-trace.o := -I$(src)
obj-$(CONFIG_USB_USBSSP_GADGET) += usbssp.o
usbssp-y := usbssp-plat.o gadget-ring.o \
gadget.o
+ gadget-dbg.o

ifneq ($(CONFIG_TRACING),)
usbssp-y += gadget-trace.o
diff --git a/drivers/usb/usbssp/gadget-dbg.c b/drivers/usb/usbssp/gadget-dbg.c
new file mode 100644
index 000000000000..277617d1a996
--- /dev/null
+++ b/drivers/usb/usbssp/gadget-dbg.c
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USBSSP device controller driver
+ *
+ * Copyright (C) 2018 Cadence.
+ *
+ * Author: Pawel Laszczak
+ *
+ * A lot of code based on Linux XHCI driver.
+ * Origin: Copyright (C) 2008 Intel Corp
+ */
+
+#include "gadget.h"
+
+void usbssp_dbg_trace(struct usbssp_udc *usbssp_data,
+ void (*trace)(struct va_format *),
+ const char *fmt, ...)
+{
+ struct va_format vaf;
+ va_list args;
+
+ va_start(args, fmt);
+ vaf.fmt = fmt;
+ vaf.va = &args;
+ dev_dbg(usbssp_data->dev, "%pV\n", &vaf);
+ trace(&vaf);
+ va_end(args);
+}
+EXPORT_SYMBOL_GPL(usbssp_dbg_trace);
+
diff --git a/drivers/usb/usbssp/gadget-ext-caps.h b/drivers/usb/usbssp/gadget-ext-caps.h
new file mode 100644
index 000000000000..2bf327046376
--- /dev/null
+++ b/drivers/usb/usbssp/gadget-ext-caps.h
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USBSSP device controller driver
+ *
+ * Copyright (C) 2018 Cadence.
+ *
+ * Author: Pawel Laszczak
+ *
+ * A lot of code based on Linux XHCI driver.
+ * Origin: Copyright (C) 2008 Intel Corp
+ */
+
+/* Up to 16 ms to halt an DC */
+#define USBSSP_MAX_HALT_USEC (16*1000)
+
+/* DC not running - set to 1 when run/stop bit is cleared. */
+#define USBSSP_STS_HALT BIT(0)
+
+/* HCCPARAMS offset from PCI base address */
+#define USBSSP_HCC_PARAMS_OFFSET 0x10
+/* HCCPARAMS contains the first extended capability pointer */
+#define USBSSP_HCC_EXT_CAPS(p) (((p)>>16)&0xffff)
+
+/* Command and Status registers offset from the Operational Registers address */
+#define USBSSP_CMD_OFFSET 0x00
+#define USBSSP_STS_OFFSET 0x04
+
+/* Capability Register */
+/* bits 7:0 - how long is the Capabilities register */
+#define USBSSP_HC_LENGTH(p) (((p)>>00)&0x00ff)
+
+/* Extended capability register fields */
+#define USBSSP_EXT_CAPS_ID(p) (((p)>>0)&0xff)
+#define USBSSP_EXT_CAPS_NEXT(p) (((p)>>8)&0xff)
+#define v_EXT_CAPS_VAL(p) ((p)>>16)
+/* Extended capability IDs - ID 0 reserved */
+#define USBSSP_EXT_CAPS_PROTOCOL 2
+
+/* USB 2.0 hardware LMP capability*/
+#define USBSSP_HLC BIT(19)
+#define USBSSP_BLC BIT(20)
+
+/* command register values to disable interrupts and halt the DC */
+/* start/stop DC execution - do not write unless DC is halted*/
+#define USBSSP_CMD_RUN BIT(0)
+/* Event Interrupt Enable - get irq when EINT bit is set in USBSTS register */
+#define USBSSP_CMD_EIE BIT(2)
+/* Host System Error Interrupt Enable - get irq when HSEIE bit set in USBSTS */
+#define USBSSP_CMD_HSEIE BIT(3)
+/* Enable Wrap Event - '1' means DC generates an event when MFINDEX wraps. */
+#define USBSSP_CMD_EWE BIT(10)
+
+#define USBSSP_IRQS (USBSSP_CMD_EIE | USBSSP_CMD_HSEIE | USBSSP_CMD_EWE)
diff --git a/drivers/usb/usbssp/gadget-if.c b/drivers/usb/usbssp/gadget-if.c
new file mode 100644
index 000000000000..d53e0fb65299
--- /dev/null
+++ b/drivers/usb/usbssp/gadget-if.c
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USBSSP device controller driver
+ *
+ * Copyright (C) 2018 Cadence.
+ *
+ * Author: Pawel Laszczak
+ *
+ */
+
+#include <linux/usb/gadget.h>
+#include <linux/usb/composite.h>
+#include "gadget.h"
+
+int usbssp_gadget_init_endpoint(struct usbssp_udc *usbssp_data)
+{
+ /*TODO: it has to be implemented*/
+ return 0;
+}
+
+void usbssp_gadget_free_endpoint(struct usbssp_udc *usbssp_data)
+{
+ /*TODO: it has to be implemented*/
+}
diff --git a/drivers/usb/usbssp/gadget.c b/drivers/usb/usbssp/gadget.c
index 2f60d7dd1fe4..338ec2ec18b1 100644
--- a/drivers/usb/usbssp/gadget.c
+++ b/drivers/usb/usbssp/gadget.c
@@ -23,6 +23,107 @@
#include "gadget-trace.h"
#include "gadget.h"

+
+/*
+ * usbssp_handshake - spin reading dc until handshake completes or fails
+ * @ptr: address of dc 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 usbssp_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;
+}
+
+/*
+ * Disable interrupts and begin the DC halting process.
+ */
+void usbssp_quiesce(struct usbssp_udc *usbssp_data)
+{
+ u32 halted;
+ u32 cmd;
+ u32 mask;
+
+ mask = ~(u32)(USBSSP_IRQS);
+
+ halted = readl(&usbssp_data->op_regs->status) & STS_HALT;
+ if (!halted)
+ mask &= ~CMD_RUN;
+
+ cmd = readl(&usbssp_data->op_regs->command);
+ cmd &= mask;
+ writel(cmd, &usbssp_data->op_regs->command);
+}
+
+/*
+ * Force DC into halt state.
+ *
+ * Disable any IRQs and clear the run/stop bit.
+ * USBSSP will complete any current and actively pipelined transactions, and
+ * should halt within 16 ms of the run/stop bit being cleared.
+ * Read DC Halted bit in the status register to see when the DC is finished.
+ */
+int usbssp_halt(struct usbssp_udc *usbssp_data)
+{
+ int ret;
+
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
+ "// Halt the USBSSP");
+ usbssp_quiesce(usbssp_data);
+
+ ret = usbssp_handshake(&usbssp_data->op_regs->status,
+ STS_HALT, STS_HALT, USBSSP_MAX_HALT_USEC);
+
+ if (!ret) {
+ dev_warn(usbssp_data->dev, "Device halt failed, %d\n", ret);
+ return ret;
+ }
+
+ usbssp_data->usbssp_state |= USBSSP_STATE_HALTED;
+ usbssp_data->cmd_ring_state = CMD_RING_STATE_STOPPED;
+ return ret;
+}
+
+
+/*
+ * Initialize memory for gadget driver and USBSSP (one-time init).
+ *
+ * Program the PAGESIZE register, initialize the device context array, create
+ * device contexts, set up a command ring segment (or two?), create event
+ * ring (one for now).
+ */
+int usbssp_init(struct usbssp_udc *usbssp_data)
+{
+ int retval = 0;
+
+ spin_lock_init(&usbssp_data->lock);
+ spin_lock_init(&usbssp_data->irq_thread_lock);
+
+ /*TODO: memory initialization*/
+ //retval = usbssp_mem_init(usbssp_data, GFP_KERNEL);
+
+ return retval;
+}
+
#ifdef CONFIG_PM
/*
* Stop DC (not bus-specific)
@@ -50,9 +151,146 @@ int usbssp_resume(struct usbssp_udc *usbssp_data, bool hibernated)

#endif /* CONFIG_PM */

+int usbssp_gen_setup(struct usbssp_udc *usbssp_data)
+{
+ int retval;
+
+ mutex_init(&usbssp_data->mutex);
+
+ usbssp_data->cap_regs = usbssp_data->regs;
+ usbssp_data->op_regs = usbssp_data->regs +
+ HC_LENGTH(readl(&usbssp_data->cap_regs->hc_capbase));
+
+ usbssp_data->run_regs = usbssp_data->regs +
+ (readl(&usbssp_data->cap_regs->run_regs_off) & RTSOFF_MASK);
+ /* Cache read-only capability registers */
+ usbssp_data->hcs_params1 = readl(&usbssp_data->cap_regs->hcs_params1);
+ usbssp_data->hcs_params2 = readl(&usbssp_data->cap_regs->hcs_params2);
+ usbssp_data->hcs_params3 = readl(&usbssp_data->cap_regs->hcs_params3);
+ usbssp_data->hcc_params = readl(&usbssp_data->cap_regs->hc_capbase);
+ usbssp_data->hci_version = HC_VERSION(usbssp_data->hcc_params);
+ usbssp_data->hcc_params = readl(&usbssp_data->cap_regs->hcc_params);
+ usbssp_data->hcc_params2 = readl(&usbssp_data->cap_regs->hcc_params2);
+
+ /* Make sure the Device Controller is halted. */
+ retval = usbssp_halt(usbssp_data);
+ if (retval)
+ return retval;
+
+ dev_dbg(usbssp_data->dev, "Resetting Device Controller\n");
+ /* Reset the internal DC memory state and registers. */
+ /*TODO: add implementation of usbssp_reset function*/
+ //retval = usbssp_reset(usbssp_data);
+ if (retval)
+ return retval;
+ dev_dbg(usbssp_data->dev, "Reset complete\n");
+
+ /* Set dma_mask and coherent_dma_mask to 64-bits,
+ * if USBSSP supports 64-bit addressing
+ */
+ if (HCC_64BIT_ADDR(usbssp_data->hcc_params) &&
+ !dma_set_mask(usbssp_data->dev, DMA_BIT_MASK(64))) {
+ dev_dbg(usbssp_data->dev, "Enabling 64-bit DMA addresses.\n");
+ dma_set_coherent_mask(usbssp_data->dev, DMA_BIT_MASK(64));
+ } else {
+ /*
+ * This is to avoid error in cases where a 32-bit USB
+ * controller is used on a 64-bit capable system.
+ */
+ retval = dma_set_mask(usbssp_data->dev, DMA_BIT_MASK(32));
+ if (retval)
+ return retval;
+ dev_dbg(usbssp_data->dev, "Enabling 32-bit DMA addresses.\n");
+ dma_set_coherent_mask(usbssp_data->dev, DMA_BIT_MASK(32));
+ }
+
+ /* Initialize USBSSP controller data structures. */
+ retval = usbssp_init(usbssp_data);
+ if (retval)
+ return retval;
+
+ dev_info(usbssp_data->dev, "USBSSP params 0x%08x USBSSP version 0x%x\n",
+ usbssp_data->hcc_params, usbssp_data->hci_version);
+
+ return 0;
+}
+
+/*
+ * gadget-if.c file is part of gadget.c file and implements interface
+ * for gadget driver
+ */
+#include "gadget-if.c"
+
int usbssp_gadget_init(struct usbssp_udc *usbssp_data)
{
int ret;
+
+ /*
+ * Check the compiler generated sizes of structures that must be laid
+ * out in specific ways for hardware access.
+ */
+ BUILD_BUG_ON(sizeof(struct usbssp_doorbell_array) != 2*32/8);
+ BUILD_BUG_ON(sizeof(struct usbssp_slot_ctx) != 8*32/8);
+ BUILD_BUG_ON(sizeof(struct usbssp_ep_ctx) != 8*32/8);
+ /* usbssp_device has eight fields, and also
+ * embeds one usbssp_slot_ctx and 31 usbssp_ep_ctx
+ */
+ BUILD_BUG_ON(sizeof(struct usbssp_stream_ctx) != 4*32/8);
+ BUILD_BUG_ON(sizeof(union usbssp_trb) != 4*32/8);
+ BUILD_BUG_ON(sizeof(struct usbssp_erst_entry) != 4*32/8);
+ BUILD_BUG_ON(sizeof(struct usbssp_cap_regs) != 8*32/8);
+ BUILD_BUG_ON(sizeof(struct usbssp_intr_reg) != 8*32/8);
+ /* usbssp_run_regs has eight fields and embeds 128 usbssp_intr_regs */
+ BUILD_BUG_ON(sizeof(struct usbssp_run_regs) != (8+8*128)*32/8);
+
+ /* fill gadget fields */
+ /*TODO: implements usbssp_gadget_ops object*/
+ //usbssp_data->gadget.ops = &usbssp_gadget_ops;
+ usbssp_data->gadget.name = "usbssp-gadget";
+ usbssp_data->gadget.max_speed = USB_SPEED_SUPER_PLUS;
+ usbssp_data->gadget.speed = USB_SPEED_UNKNOWN;
+ usbssp_data->gadget.sg_supported = true;
+ usbssp_data->gadget.lpm_capable = 1;
+
+ usbssp_data->setup_buf = kzalloc(USBSSP_EP0_SETUP_SIZE, GFP_KERNEL);
+ if (!usbssp_data->setup_buf)
+ return -ENOMEM;
+
+ /*USBSSP support not aligned buffer but this option
+ * improve performance of this controller.
+ */
+ usbssp_data->gadget.quirk_ep_out_aligned_size = true;
+ ret = usbssp_gen_setup(usbssp_data);
+ if (ret < 0) {
+ dev_err(usbssp_data->dev,
+ "Generic initialization failed with error code%d\n",
+ ret);
+ goto err3;
+ }
+
+ ret = usbssp_gadget_init_endpoint(usbssp_data);
+ if (ret < 0) {
+ dev_err(usbssp_data->dev, "failed to initialize endpoints\n");
+ goto err1;
+ }
+
+ ret = usb_add_gadget_udc(usbssp_data->dev, &usbssp_data->gadget);
+
+ if (ret) {
+ dev_err(usbssp_data->dev, "failed to register udc\n");
+ goto err2;
+ }
+
+ return ret;
+err2:
+ usbssp_gadget_free_endpoint(usbssp_data);
+err1:
+ usbssp_halt(usbssp_data);
+ /*TODO add implementation of usbssp_reset function*/
+ //usbssp_reset(usbssp_data);
+ /*TODO add implementation of freeing memory*/
+ //usbssp_mem_cleanup(usbssp_data);
+err3:
return ret;
}

@@ -60,5 +298,9 @@ int usbssp_gadget_exit(struct usbssp_udc *usbssp_data)
{
int ret = 0;

+ usb_del_gadget_udc(&usbssp_data->gadget);
+ usbssp_gadget_free_endpoint(usbssp_data);
+ /*TODO: add usbssp_stop implementation*/
+ //usbssp_stop(usbssp_data);
return ret;
}
diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h
index 55e20795d900..5d8918f8da84 100644
--- a/drivers/usb/usbssp/gadget.h
+++ b/drivers/usb/usbssp/gadget.h
@@ -12,8 +12,10 @@
#ifndef __LINUX_USBSSP_GADGET_H
#define __LINUX_USBSSP_GADGET_H

+#include <linux/irq.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/usb/gadget.h>
+#include "gadget-ext-caps.h"

/* Max number slots - only 1 is allowed */
#define DEV_MAX_SLOTS 1
@@ -1676,7 +1678,18 @@ static inline void usbssp_write_64(struct usbssp_udc *usbssp_data,
lo_hi_writeq(val, regs);
}

+/* USBSSP memory management */
+void usbssp_dbg_trace(struct usbssp_udc *usbssp_data,
+ void (*trace)(struct va_format *),
+ const char *fmt, ...);
/* USBSSP Device controller glue */
+void usbssp_bottom_irq(struct work_struct *work);
+int usbssp_init(struct usbssp_udc *usbssp_data);
+void usbssp_stop(struct usbssp_udc *usbssp_data);
+int usbssp_handshake(void __iomem *ptr, u32 mask, u32 done, int usec);
+void usbssp_quiesce(struct usbssp_udc *usbssp_data);
+extern int usbssp_reset(struct usbssp_udc *usbssp_data);
+
int usbssp_suspend(struct usbssp_udc *usbssp_data, bool do_wakeup);
int usbssp_resume(struct usbssp_udc *usbssp_data, bool hibernated);

@@ -1689,6 +1702,8 @@ dma_addr_t usbssp_trb_virt_to_dma(struct usbssp_segment *seg,
/* USBSSP gadget interface*/
int usbssp_gadget_init(struct usbssp_udc *usbssp_data);
int usbssp_gadget_exit(struct usbssp_udc *usbssp_data);
+void usbssp_gadget_free_endpoint(struct usbssp_udc *usbssp_data);
+int usbssp_gadget_init_endpoint(struct usbssp_udc *usbssp_data);

static inline char *usbssp_slot_state_string(u32 state)
{
--
2.17.1


2018-07-19 18:00:42

by Pawel Laszczak

[permalink] [raw]
Subject: [PATCH 26/31] usb: usbssp: added endpoint configuration functionality.

Patch implements usbssp_configure_endpoint function.
Function is responsible for configure usbssp controller endpoints.
To configure endpoints driver must send Configure Endpoint command
to controller. To change some parameters of endpoint driver must
send Evaluate Context command.
Function usbssp_configure_endpoint is blocking function. It means that
after issuing command to controller driver is waiting for completion.

Signed-off-by: Pawel Laszczak <[email protected]>
---
drivers/usb/usbssp/gadget.c | 162 ++++++++++++++++++++++++++++++++++++
drivers/usb/usbssp/gadget.h | 18 ++++
2 files changed, 180 insertions(+)

diff --git a/drivers/usb/usbssp/gadget.c b/drivers/usb/usbssp/gadget.c
index 0180ecfdaf9c..d5ed2c48e3fa 100644
--- a/drivers/usb/usbssp/gadget.c
+++ b/drivers/usb/usbssp/gadget.c
@@ -692,6 +692,168 @@ int usbssp_dequeue(struct usbssp_ep *ep_priv, struct usbssp_request *req_priv)
return ret;
}

+static int usbssp_configure_endpoint_result(struct usbssp_udc *usbssp_data,
+ struct usb_gadget *g,
+ u32 *cmd_status)
+{
+ int ret;
+
+ switch (*cmd_status) {
+ case COMP_COMMAND_ABORTED:
+ case COMP_COMMAND_RING_STOPPED:
+ dev_warn(usbssp_data->dev,
+ "Timeout while waiting for configure endpoint command\n");
+ ret = -ETIME;
+ break;
+ case COMP_RESOURCE_ERROR:
+ dev_warn(&g->dev,
+ "Not enough device controller resources for new device state.\n");
+ ret = -ENOMEM;
+ break;
+ case COMP_TRB_ERROR:
+ /* the gadget driver set up something wrong */
+ dev_warn(&g->dev, "ERROR: Endpoint drop flag = 0, "
+ "add flag = 1, and endpoint is not disabled.\n");
+ ret = -EINVAL;
+ break;
+ case COMP_INCOMPATIBLE_DEVICE_ERROR:
+ dev_warn(&g->dev,
+ "ERROR: Incompatible device for endpoint configure command.\n");
+ ret = -ENODEV;
+ break;
+ case COMP_SUCCESS:
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_context_change,
+ "Successful Endpoint Configure command");
+ ret = 0;
+ break;
+ default:
+ dev_err(usbssp_data->dev,
+ "ERROR: unexpected command completion code 0x%x.\n",
+ *cmd_status);
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int usbssp_evaluate_context_result(struct usbssp_udc *usbssp_data,
+ struct usb_gadget *g, u32 *cmd_status)
+{
+ int ret;
+
+ switch (*cmd_status) {
+ case COMP_COMMAND_ABORTED:
+ case COMP_COMMAND_RING_STOPPED:
+ dev_warn(usbssp_data->dev,
+ "Timeout while waiting for evaluate context command\n");
+ ret = -ETIME;
+ break;
+ case COMP_PARAMETER_ERROR:
+ dev_warn(&g->dev,
+ "WARN: USBSSP driver setup invalid evaluate context command.\n");
+ ret = -EINVAL;
+ break;
+ case COMP_SLOT_NOT_ENABLED_ERROR:
+ dev_warn(&g->dev,
+ "WARN: slot not enabled for evaluate context command.\n");
+ ret = -EINVAL;
+ break;
+ case COMP_CONTEXT_STATE_ERROR:
+ dev_warn(&g->dev,
+ "WARN: invalid context state for evaluate context command.\n");
+ ret = -EINVAL;
+ break;
+ case COMP_INCOMPATIBLE_DEVICE_ERROR:
+ dev_warn(&g->dev,
+ "ERROR: Incompatible device for evaluate context command.\n");
+ ret = -ENODEV;
+ break;
+ case COMP_MAX_EXIT_LATENCY_TOO_LARGE_ERROR:
+ /* Max Exit Latency too large error */
+ dev_warn(&g->dev, "WARN: Max Exit Latency too large\n");
+ ret = -EINVAL;
+ break;
+ case COMP_SUCCESS:
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_context_change,
+ "Successful evaluate context command");
+ ret = 0;
+ break;
+ default:
+ dev_err(usbssp_data->dev,
+ "ERROR: unexpected command completion code 0x%x.\n",
+ *cmd_status);
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+/* Issue a configure endpoint command or evaluate context command
+ * and wait for it to finish.
+ */
+static int usbssp_configure_endpoint(struct usbssp_udc *usbssp_data,
+ struct usb_gadget *g,
+ struct usbssp_command *command,
+ bool ctx_change, bool must_succeed)
+{
+ int ret;
+ struct usbssp_input_control_ctx *ctrl_ctx;
+ struct usbssp_device *dev_priv;
+ struct usbssp_slot_ctx *slot_ctx;
+
+ if (!command)
+ return -EINVAL;
+
+ if (usbssp_data->usbssp_state & USBSSP_STATE_DYING)
+ return -ESHUTDOWN;
+
+ dev_priv = &usbssp_data->devs;
+ ctrl_ctx = usbssp_get_input_control_ctx(command->in_ctx);
+ if (!ctrl_ctx) {
+ dev_warn(usbssp_data->dev,
+ "%s: Could not get input context, bad type.\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ slot_ctx = usbssp_get_slot_ctx(usbssp_data, command->in_ctx);
+ trace_usbssp_configure_endpoint(slot_ctx);
+
+ if (!ctx_change) {
+ ret = usbssp_queue_configure_endpoint(usbssp_data, command,
+ command->in_ctx->dma, must_succeed);
+ } else {
+ ret = usbssp_queue_evaluate_context(usbssp_data, command,
+ command->in_ctx->dma, must_succeed);
+ }
+
+ if (ret < 0) {
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_context_change,
+ "FIXME allocate a new ring segment");
+ return -ENOMEM;
+ }
+
+ usbssp_ring_cmd_db(usbssp_data);
+
+ spin_unlock_irqrestore(&usbssp_data->irq_thread_lock,
+ usbssp_data->irq_thread_flag);
+
+ /*Waiting for handling Endpoint Configure command */
+ while (!command->status)
+ udelay(100);
+
+ spin_lock_irqsave(&usbssp_data->irq_thread_lock,
+ usbssp_data->irq_thread_flag);
+
+ if (!ctx_change)
+ ret = usbssp_configure_endpoint_result(usbssp_data, g,
+ &command->status);
+ else
+ ret = usbssp_evaluate_context_result(usbssp_data, g,
+ &command->status);
+ return ret;
+}
+
int usbssp_halt_endpoint(struct usbssp_udc *usbssp_data, struct usbssp_ep *dep,
int value)
{
diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h
index 6737cb05cd27..81d7fe44519a 100644
--- a/drivers/usb/usbssp/gadget.h
+++ b/drivers/usb/usbssp/gadget.h
@@ -1692,6 +1692,7 @@ int usbssp_alloc_priv_device(struct usbssp_udc *usbssp_data, gfp_t flags);
int usbssp_setup_addressable_priv_dev(struct usbssp_udc *usbssp_data);
void usbssp_copy_ep0_dequeue_into_input_ctx(struct usbssp_udc *usbssp_data);
unsigned int usbssp_get_endpoint_index(const struct usb_endpoint_descriptor *desc);
+unsigned int usbssp_get_endpoint_address(unsigned int ep_index);
unsigned int usbssp_last_valid_endpoint(u32 added_ctxs);
int usbssp_ring_expansion(struct usbssp_udc *usbssp_data,
struct usbssp_ring *ring,
@@ -1704,6 +1705,15 @@ struct usbssp_ring *usbssp_dma_to_transfer_ring(struct usbssp_ep *ep,
struct usbssp_ring *usbssp_stream_id_to_ring(struct usbssp_device *dev,
unsigned int ep_index,
unsigned int stream_id);
+void usbssp_free_stream_info(struct usbssp_udc *usbssp_data,
+ struct usbssp_stream_info *stream_info);
+struct usbssp_ring *usbssp_dma_to_transfer_ring(
+ struct usbssp_ep *ep,
+ u64 address);
+struct usbssp_ring *usbssp_stream_id_to_ring(
+ struct usbssp_device *dev,
+ unsigned int ep_index,
+ unsigned int stream_id);

struct usbssp_command *usbssp_alloc_command(struct usbssp_udc *usbssp_data,
bool allocate_completion,
@@ -1771,6 +1781,14 @@ int usbssp_queue_isoc_tx_prepare(
struct usbssp_udc *usbssp_data, gfp_t mem_flags,
struct usbssp_request *req_priv,
unsigned int ep_index);
+int usbssp_queue_configure_endpoint(struct usbssp_udc *usbssp_data,
+ struct usbssp_command *cmd,
+ dma_addr_t in_ctx_ptr,
+ bool command_must_succeed);
+int usbssp_queue_evaluate_context(struct usbssp_udc *usbssp_data,
+ struct usbssp_command *cmd,
+ dma_addr_t in_ctx_ptr,
+ bool command_must_succeed);
int usbssp_queue_reset_ep(struct usbssp_udc *usbssp_data,
struct usbssp_command *cmd,
unsigned int ep_index,
--
2.17.1


2018-07-19 18:00:47

by Pawel Laszczak

[permalink] [raw]
Subject: [PATCH 21/31] usb: usbssp: added queuing procedure for BULK and INT transfer.

Patch adds usbssp_queue_bulk_tx and usbssp_queue_int_tx function
that prepares TD, adds TD to transfer ring and arms the transfer.

Signed-off-by: Pawel Laszczak <[email protected]>
---
drivers/usb/usbssp/gadget-ring.c | 286 ++++++++++++++++++++++++++++++-
1 file changed, 285 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/usbssp/gadget-ring.c b/drivers/usb/usbssp/gadget-ring.c
index a0cfce0dc49d..4bb13f9e311a 100644
--- a/drivers/usb/usbssp/gadget-ring.c
+++ b/drivers/usb/usbssp/gadget-ring.c
@@ -1573,6 +1573,74 @@ static int prepare_transfer(struct usbssp_udc *usbssp_data,
return 0;
}

+unsigned int count_trbs(u64 addr, u64 len)
+{
+ unsigned int num_trbs;
+
+ num_trbs = DIV_ROUND_UP(len + (addr & (TRB_MAX_BUFF_SIZE - 1)),
+ TRB_MAX_BUFF_SIZE);
+ if (num_trbs == 0)
+ num_trbs++;
+
+ return num_trbs;
+}
+
+static inline unsigned int count_trbs_needed(struct usbssp_request *req_priv)
+{
+ return count_trbs(req_priv->request.dma, req_priv->request.length);
+}
+
+static unsigned int count_sg_trbs_needed(struct usbssp_request *req_priv)
+{
+ struct scatterlist *sg;
+ unsigned int i, len, full_len, num_trbs = 0;
+
+ full_len = req_priv->request.length;
+
+ for_each_sg(req_priv->sg, sg, req_priv->num_pending_sgs, i) {
+ len = sg_dma_len(sg);
+ num_trbs += count_trbs(sg_dma_address(sg), len);
+ len = min_t(unsigned int, len, full_len);
+ full_len -= len;
+ if (full_len == 0)
+ break;
+ }
+
+ return num_trbs;
+}
+
+static void check_trb_math(struct usbssp_request *req_priv, int running_total)
+{
+ if (unlikely(running_total != req_priv->request.length))
+ dev_err(req_priv->dep->usbssp_data->dev,
+ "%s - ep %#x - Miscalculated tx length, "
+ "queued %#x (%d), asked for %#x (%d)\n",
+ __func__,
+ req_priv->dep->endpoint.desc->bEndpointAddress,
+ running_total, running_total,
+ req_priv->request.length,
+ req_priv->request.length);
+}
+
+static void giveback_first_trb(struct usbssp_udc *usbssp_data,
+ unsigned int ep_index,
+ unsigned int stream_id,
+ int start_cycle,
+ struct usbssp_generic_trb *start_trb)
+{
+ /*
+ * Pass all the TRBs to the hardware at once and make sure this write
+ * isn't reordered.
+ */
+ wmb();
+ if (start_cycle)
+ start_trb->field[3] |= cpu_to_le32(start_cycle);
+ else
+ start_trb->field[3] &= cpu_to_le32(~TRB_CYCLE);
+
+ usbssp_ring_ep_doorbell(usbssp_data, ep_index, stream_id);
+}
+
/*
* USBSSP uses normal TRBs for both bulk and interrupt. When the interrupt
* endpoint is to be serviced, the DC will consume (at most) one TD. A TD
@@ -1628,12 +1696,228 @@ static u32 usbssp_td_remainder(struct usbssp_udc *usbssp_data,
return (total_packet_count - ((transferred + trb_buff_len) / maxp));
}

+static int usbssp_align_td(struct usbssp_udc *usbssp_data,
+ struct usbssp_request *req_priv, u32 enqd_len,
+ u32 *trb_buff_len, struct usbssp_segment *seg)
+{
+ struct device *dev = usbssp_data->dev;
+ unsigned int unalign;
+ unsigned int max_pkt;
+ u32 new_buff_len;
+
+ max_pkt = GET_MAX_PACKET(
+ usb_endpoint_maxp(req_priv->dep->endpoint.desc));
+ unalign = (enqd_len + *trb_buff_len) % max_pkt;
+
+ /* we got lucky, last normal TRB data on segment is packet aligned */
+ if (unalign == 0)
+ return 0;
+
+ dev_dbg(usbssp_data->dev, "Unaligned %d bytes, buff len %d\n",
+ unalign, *trb_buff_len);
+
+ /* is the last nornal TRB alignable by splitting it */
+ if (*trb_buff_len > unalign) {
+ *trb_buff_len -= unalign;
+ dev_dbg(usbssp_data->dev, "split align, new buff len %d\n",
+ *trb_buff_len);
+ return 0;
+ }
+
+ /*
+ * We want enqd_len + trb_buff_len to sum up to a number aligned to
+ * number which is divisible by the endpoint's wMaxPacketSize. IOW:
+ * (size of currently enqueued TRBs + remainder) % wMaxPacketSize == 0.
+ */
+ new_buff_len = max_pkt - (enqd_len % max_pkt);
+
+ if (new_buff_len > (req_priv->request.length - enqd_len))
+ new_buff_len = (req_priv->request.length - enqd_len);
+
+ /* create a max max_pkt sized bounce buffer pointed to by last trb */
+ if (req_priv->direction) {
+ sg_pcopy_to_buffer(req_priv->request.sg,
+ req_priv->request.num_mapped_sgs,
+ seg->bounce_buf, new_buff_len, enqd_len);
+ seg->bounce_dma = dma_map_single(dev, seg->bounce_buf,
+ max_pkt, DMA_TO_DEVICE);
+ } else {
+ seg->bounce_dma = dma_map_single(dev, seg->bounce_buf,
+ max_pkt, DMA_FROM_DEVICE);
+ }
+
+ if (dma_mapping_error(dev, seg->bounce_dma)) {
+ /* try without aligning.*/
+ dev_warn(usbssp_data->dev,
+ "Failed mapping bounce buffer, not aligning\n");
+ return 0;
+ }
+ *trb_buff_len = new_buff_len;
+ seg->bounce_len = new_buff_len;
+ seg->bounce_offs = enqd_len;
+
+ dev_dbg(usbssp_data->dev, "Bounce align, new buff len %d\n",
+ *trb_buff_len);
+
+ return 1;
+}
+
+
int usbssp_queue_bulk_tx(struct usbssp_udc *usbssp_data,
gfp_t mem_flags,
struct usbssp_request *req_priv,
unsigned int ep_index)
{
- /*TODO: function musb be implemented*/
+ struct usbssp_ring *ring;
+ struct usbssp_td *td;
+ struct usbssp_generic_trb *start_trb;
+ struct scatterlist *sg = NULL;
+ bool more_trbs_coming = true;
+ bool need_zero_pkt = false;
+ bool first_trb = true;
+ unsigned int num_trbs;
+ unsigned int start_cycle, num_sgs = 0;
+ unsigned int enqd_len, block_len, trb_buff_len, full_len;
+ int sent_len, ret;
+ u32 field, length_field, remainder;
+ u64 addr, send_addr;
+
+ ring = usbssp_request_to_transfer_ring(usbssp_data, req_priv);
+ if (!ring)
+ return -EINVAL;
+
+ full_len = req_priv->request.length;
+ /* If we have scatter/gather list, we use it. */
+ if (req_priv->request.num_sgs) {
+ num_sgs = req_priv->num_pending_sgs;
+ sg = req_priv->sg;
+ addr = (u64) sg_dma_address(sg);
+ block_len = sg_dma_len(sg);
+ num_trbs = count_sg_trbs_needed(req_priv);
+ } else {
+ num_trbs = count_trbs_needed(req_priv);
+ addr = (u64) req_priv->request.dma;
+ block_len = full_len;
+ }
+
+ ret = prepare_transfer(usbssp_data, &usbssp_data->devs,
+ ep_index, req_priv->request.stream_id,
+ num_trbs, req_priv, 0, mem_flags);
+ if (unlikely(ret < 0))
+ return ret;
+
+ /* Deal with request.zero - need one more td/trb */
+ if (req_priv->request.zero && req_priv->num_tds_done > 1)
+ need_zero_pkt = true;
+
+ td = &req_priv->td[0];
+
+ dev_dbg(usbssp_data->dev, "Queue Bulk transfer to %s - ep_index: %d,"
+ " num trb: %d, block len %d, nzp: %d\n",
+ req_priv->dep->name, ep_index,
+ num_trbs, block_len, need_zero_pkt);
+
+ /*
+ * Don't give the first TRB to the hardware (by toggling the cycle bit)
+ * until we've finished creating all the other TRBs. The ring's cycle
+ * state may change as we enqueue the other TRBs, so save it too.
+ */
+ start_trb = &ring->enqueue->generic;
+ start_cycle = ring->cycle_state;
+ send_addr = addr;
+
+ /* Queue the TRBs, even if they are zero-length */
+ for (enqd_len = 0; first_trb || enqd_len < full_len;
+ enqd_len += trb_buff_len) {
+ field = TRB_TYPE(TRB_NORMAL);
+
+ /* TRB buffer should not cross 64KB boundaries */
+ trb_buff_len = TRB_BUFF_LEN_UP_TO_BOUNDARY(addr);
+ trb_buff_len = min_t(unsigned int, trb_buff_len, block_len);
+
+ if (enqd_len + trb_buff_len > full_len)
+ trb_buff_len = full_len - enqd_len;
+
+ /* Don't change the cycle bit of the first TRB until later */
+ if (first_trb) {
+ first_trb = false;
+ if (start_cycle == 0)
+ field |= TRB_CYCLE;
+ } else
+ field |= ring->cycle_state;
+
+ /* Chain all the TRBs together; clear the chain bit in the last
+ * TRB to indicate it's the last TRB in the chain.
+ */
+ if (enqd_len + trb_buff_len < full_len) {
+ field |= TRB_CHAIN;
+ if (trb_is_link(ring->enqueue + 1)) {
+ if (usbssp_align_td(usbssp_data, req_priv,
+ enqd_len, &trb_buff_len,
+ ring->enq_seg)) {
+ send_addr = ring->enq_seg->bounce_dma;
+ /* assuming TD won't span 2 segs */
+ td->bounce_seg = ring->enq_seg;
+ }
+ }
+ }
+ if (enqd_len + trb_buff_len >= full_len) {
+ field &= ~TRB_CHAIN;
+ field |= TRB_IOC;
+ more_trbs_coming = false;
+ td->last_trb = ring->enqueue;
+ }
+
+ /* Only set interrupt on short packet for OUT endpoints */
+ if (!req_priv->direction)
+ field |= TRB_ISP;
+
+ /* Set the TRB length, TD size, and interrupter fields. */
+ remainder = usbssp_td_remainder(usbssp_data, enqd_len,
+ trb_buff_len, full_len, req_priv,
+ more_trbs_coming);
+
+ length_field = TRB_LEN(trb_buff_len) |
+ TRB_TD_SIZE(remainder) |
+ TRB_INTR_TARGET(0);
+
+ queue_trb(usbssp_data, ring, more_trbs_coming | need_zero_pkt,
+ lower_32_bits(send_addr),
+ upper_32_bits(send_addr),
+ length_field,
+ field);
+
+ addr += trb_buff_len;
+ sent_len = trb_buff_len;
+
+ while (sg && sent_len >= block_len) {
+ /* New sg entry */
+ --num_sgs;
+ sent_len -= block_len;
+ if (num_sgs != 0) {
+ sg = sg_next(sg);
+ block_len = sg_dma_len(sg);
+ addr = (u64) sg_dma_address(sg);
+ addr += sent_len;
+ }
+ }
+ block_len -= sent_len;
+ send_addr = addr;
+ }
+
+ if (need_zero_pkt) {
+ ret = prepare_transfer(usbssp_data, &usbssp_data->devs,
+ ep_index, req_priv->request.stream_id,
+ 1, req_priv, 1, mem_flags);
+ req_priv->td[1].last_trb = ring->enqueue;
+ field = TRB_TYPE(TRB_NORMAL) | ring->cycle_state | TRB_IOC;
+ queue_trb(usbssp_data, ring, 0, 0, 0,
+ TRB_INTR_TARGET(0), field);
+ }
+
+ check_trb_math(req_priv, enqd_len);
+ giveback_first_trb(usbssp_data, ep_index, req_priv->request.stream_id,
+ start_cycle, start_trb);
return 0;
}

--
2.17.1


2018-07-19 18:00:52

by Pawel Laszczak

[permalink] [raw]
Subject: [PATCH 30/31] usb: usbssp: added support for TEST_MODE.

Patch implements TEST_MODE feature that puts the device port
into test mode. This feature is used only when device works
in HS mode

Signed-off-by: Pawel Laszczak <[email protected]>
---
drivers/usb/usbssp/gadget-ep0.c | 39 ++++++++++++++
drivers/usb/usbssp/gadget-port.c | 93 ++++++++++++++++++++++++++++++++
drivers/usb/usbssp/gadget.h | 6 +++
3 files changed, 138 insertions(+)

diff --git a/drivers/usb/usbssp/gadget-ep0.c b/drivers/usb/usbssp/gadget-ep0.c
index 3b7066875d46..e03a34d11dab 100644
--- a/drivers/usb/usbssp/gadget-ep0.c
+++ b/drivers/usb/usbssp/gadget-ep0.c
@@ -245,6 +245,41 @@ static int usbssp_ep0_handle_feature_u2(struct usbssp_udc *usbssp_data,
return 0;
}

+static int usbssp_ep0_handle_feature_test(struct usbssp_udc *usbssp_data,
+ enum usb_device_state state,
+ u32 wIndex, int set)
+{
+ int test_mode;
+ __le32 __iomem *port_regs;
+ u32 temp;
+ unsigned long flags;
+ int retval;
+
+ if (usbssp_data->port_major_revision == 0x03)
+ return -EINVAL;
+
+ dev_info(usbssp_data->dev, "Test mode; %d\n", wIndex);
+
+ port_regs = usbssp_get_port_io_addr(usbssp_data);
+
+
+ test_mode = (wIndex & 0xff00) >> 8;
+
+ temp = readl(port_regs);
+ temp = usbssp_port_state_to_neutral(temp);
+
+ if (test_mode > TEST_FORCE_EN || test_mode < TEST_J) {
+ /* "stall" on error */
+ retval = -EPIPE;
+ }
+
+ usbssp_status_stage(usbssp_data);
+ retval = usbssp_enter_test_mode(usbssp_data, test_mode, &flags);
+ usbssp_exit_test_mode(usbssp_data);
+
+ return 0;
+}
+
static int usbssp_ep0_handle_feature_device(struct usbssp_udc *usbssp_data,
struct usb_ctrlrequest *ctrl,
int set)
@@ -275,6 +310,10 @@ static int usbssp_ep0_handle_feature_device(struct usbssp_udc *usbssp_data,
case USB_DEVICE_LTM_ENABLE:
ret = -EINVAL;
break;
+ case USB_DEVICE_TEST_MODE:
+ ret = usbssp_ep0_handle_feature_test(usbssp_data, state,
+ wIndex, set);
+ break;
default:
dev_err(usbssp_data->dev, "%s Feature Request not supported\n",
(set) ? "Set" : "Clear");
diff --git a/drivers/usb/usbssp/gadget-port.c b/drivers/usb/usbssp/gadget-port.c
index 05a4eb2fd8bf..ebbff47e9455 100644
--- a/drivers/usb/usbssp/gadget-port.c
+++ b/drivers/usb/usbssp/gadget-port.c
@@ -192,3 +192,96 @@ void usbssp_test_and_clear_bit(struct usbssp_udc *usbssp_data,
writel(temp, port_regs);
}
}
+
+static void usbssp_set_port_power(struct usbssp_udc *usbssp_data,
+ bool on, unsigned long *flags)
+{
+ __le32 __iomem *addr;
+ u32 temp;
+
+ addr = usbssp_get_port_io_addr(usbssp_data);
+ temp = readl(addr);
+ temp = usbssp_port_state_to_neutral(temp);
+ if (on) {
+ /* Power on */
+ writel(temp | PORT_POWER, addr);
+ temp = readl(addr);
+ dev_dbg(usbssp_data->dev,
+ "set port power, actual port status = 0x%x\n",
+ temp);
+ } else {
+ /* Power off */
+ writel(temp & ~PORT_POWER, addr);
+ dev_dbg(usbssp_data->dev,
+ "clear port power, actual port status = 0x%x\n",
+ temp);
+ }
+}
+
+static void usbssp_port_set_test_mode(struct usbssp_udc *usbssp_data,
+ u16 test_mode)
+{
+ u32 temp;
+ __le32 __iomem *addr;
+
+ /* USBSSP only supports test mode for usb2 ports, */
+ addr = usbssp_get_port_io_addr(usbssp_data);
+ temp = readl(addr + PORTPMSC);
+ temp |= test_mode << PORT_TEST_MODE_SHIFT;
+ writel(temp, addr + PORTPMSC);
+ usbssp_data->test_mode = test_mode;
+ if (test_mode == TEST_FORCE_EN)
+ usbssp_start(usbssp_data);
+}
+
+int usbssp_enter_test_mode(struct usbssp_udc *usbssp_data,
+ u16 test_mode, unsigned long *flags)
+{
+ int retval;
+
+ retval = usbssp_disable_slot(usbssp_data);
+ if (retval) {
+ dev_err(usbssp_data->dev,
+ "Failed to disable slot %d, %d. Enter test mode anyway\n",
+ usbssp_data->slot_id, retval);
+ return retval;
+ }
+
+ /* Put port to the Disable state by clear PP */
+ usbssp_set_port_power(usbssp_data, false, flags);
+
+ /* Stop the controller */
+ retval = usbssp_halt(usbssp_data);
+ if (retval)
+ return retval;
+
+ /* Disable runtime PM for test mode */
+ pm_runtime_forbid(usbssp_data->dev);
+
+ /* Set PORTPMSC.PTC field to enter selected test mode */
+ /* Port is selected by wIndex. port_id = wIndex + 1 */
+ dev_dbg(usbssp_data->dev, "Enter Test Mode: _id=%d\n",
+ test_mode);
+ usbssp_port_set_test_mode(usbssp_data, test_mode);
+
+ return retval;
+}
+
+int usbssp_exit_test_mode(struct usbssp_udc *usbssp_data)
+{
+ int retval;
+
+ if (!usbssp_data->test_mode) {
+ dev_err(usbssp_data->dev, "Not in test mode, do nothing.\n");
+ return 0;
+ }
+ if (usbssp_data->test_mode == TEST_FORCE_EN &&
+ !(usbssp_data->usbssp_state & USBSSP_STATE_HALTED)) {
+ retval = usbssp_halt(usbssp_data);
+ if (retval)
+ return retval;
+ }
+ pm_runtime_allow(usbssp_data->dev);
+ usbssp_data->test_mode = 0;
+ return usbssp_reset(usbssp_data);
+}
diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h
index 59d7ef573d96..418bc5ad2a22 100644
--- a/drivers/usb/usbssp/gadget.h
+++ b/drivers/usb/usbssp/gadget.h
@@ -1744,6 +1744,7 @@ void usbssp_stop(struct usbssp_udc *usbssp_data);
int usbssp_handshake(void __iomem *ptr, u32 mask, u32 done, int usec);
void usbssp_quiesce(struct usbssp_udc *usbssp_data);
int usbssp_halt(struct usbssp_udc *usbssp_data);
+int usbssp_start(struct usbssp_udc *usbssp_data);
extern int usbssp_reset(struct usbssp_udc *usbssp_data);
int usbssp_disable_slot(struct usbssp_udc *usbssp_data);

@@ -1836,6 +1837,8 @@ void usbssp_test_and_clear_bit(struct usbssp_udc *usbssp_data,
__le32 __iomem *port_regs, u32 port_bit);

void usbssp_udc_died(struct usbssp_udc *usbssp_data);
+u32 usbssp_port_state_to_neutral(u32 state);
+
/* USBSSP DC contexts */
struct usbssp_input_control_ctx *usbssp_get_input_control_ctx(
struct usbssp_container_ctx *ctx);
@@ -1867,6 +1870,9 @@ int usbssp_halt_endpoint(struct usbssp_udc *usbssp_data,
struct usbssp_ep *dep, int value);
int usbssp_cmd_stop_ep(struct usbssp_udc *usbssp_data, struct usb_gadget *g,
struct usbssp_ep *ep_priv);
+int usbssp_enter_test_mode(struct usbssp_udc *usbssp_data,
+ u16 test_mode, unsigned long *flags);
+int usbssp_exit_test_mode(struct usbssp_udc *usbssp_data);
int usbssp_setup_analyze(struct usbssp_udc *usbssp_data);
int usbssp_status_stage(struct usbssp_udc *usbssp_data);

--
2.17.1


2018-07-19 18:00:59

by Pawel Laszczak

[permalink] [raw]
Subject: [PATCH 23/31] usb: usbssp: added implementation of transfer events.

Patch add functionality related to handling transfer events.
This kind of events are added to event ring after completion transfer.

This patch add supports for handling transfer events only for
control, interrupt and bulk transfer.

After the transfer, usbssp driver release the TD and informs about it
gadget core driver by means of usbssp_gadget_giveback function.

Signed-off-by: Pawel Laszczak <[email protected]>
---
drivers/usb/usbssp/gadget-mem.c | 9 +
drivers/usb/usbssp/gadget-ring.c | 602 ++++++++++++++++++++++++++++++-
drivers/usb/usbssp/gadget.h | 5 +
3 files changed, 615 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/usbssp/gadget-mem.c b/drivers/usb/usbssp/gadget-mem.c
index 858bee77b0dc..d5d732b94454 100644
--- a/drivers/usb/usbssp/gadget-mem.c
+++ b/drivers/usb/usbssp/gadget-mem.c
@@ -555,6 +555,15 @@ static void usbssp_free_stream_ctx(struct usbssp_udc *usbssp_data,
stream_ctx, dma);
}

+struct usbssp_ring *usbssp_dma_to_transfer_ring(struct usbssp_ep *ep,
+ u64 address)
+{
+ if (ep->ep_state & EP_HAS_STREAMS)
+ return radix_tree_lookup(&ep->stream_info->trb_address_map,
+ address >> TRB_SEGMENT_SHIFT);
+ return ep->ring;
+}
+
struct usbssp_ring *usbssp_stream_id_to_ring(struct usbssp_device *dev,
unsigned int ep_index,
unsigned int stream_id)
diff --git a/drivers/usb/usbssp/gadget-ring.c b/drivers/usb/usbssp/gadget-ring.c
index cfb31120eef8..8cc6e4247eef 100644
--- a/drivers/usb/usbssp/gadget-ring.c
+++ b/drivers/usb/usbssp/gadget-ring.c
@@ -62,6 +62,13 @@
#include "gadget-trace.h"
#include "gadget.h"

+static void giveback_first_trb(struct usbssp_udc *usbssp_data,
+ unsigned int ep_index,
+ unsigned int stream_id,
+ int start_cycle,
+ struct usbssp_generic_trb
+ *start_trb);
+
/*
* Returns zero if the TRB isn't in this segment, otherwise it returns the DMA
* address of the TRB.
@@ -80,6 +87,11 @@ dma_addr_t usbssp_trb_virt_to_dma(struct usbssp_segment *seg,
return seg->dma + (segment_offset * sizeof(*trb));
}

+static bool trb_is_noop(union usbssp_trb *trb)
+{
+ return TRB_TYPE_NOOP_LE32(trb->generic.field[3]);
+}
+
static bool trb_is_link(union usbssp_trb *trb)
{
return TRB_TYPE_LINK_LE32(trb->link.control);
@@ -1528,6 +1540,289 @@ int usbssp_is_vendor_info_code(struct usbssp_udc *usbssp_data,
return 0;
}

+static int usbssp_td_cleanup(struct usbssp_udc *usbssp_data,
+ struct usbssp_td *td, struct usbssp_ring *ep_ring,
+ int *status)
+{
+ struct usbssp_request *req_priv = NULL;
+
+ /* Clean up the endpoint's TD list */
+ req_priv = td->priv_request;
+
+ /* if a bounce buffer was used to align this td then unmap it */
+ usbssp_unmap_td_bounce_buffer(usbssp_data, ep_ring, td);
+
+ /*
+ * Do one last check of the actual transfer length.
+ * If the DC controller said we transferred more data than the buffer
+ * length, req_priv->request.actual will be a very big number (since it's
+ * unsigned). Play it safe and say we didn't transfer anything.
+ */
+ if (req_priv->request.actual > req_priv->request.length) {
+ dev_warn(usbssp_data->dev,
+ "USB req %u and actual %u transfer length mismatch\n",
+ req_priv->request.length, req_priv->request.actual);
+ req_priv->request.actual = 0;
+ *status = 0;
+ }
+ list_del_init(&td->td_list);
+
+ inc_td_cnt(req_priv);
+ /* Giveback the USB request when all the tds are completed */
+ if (last_td_in_request(td)) {
+ if ((req_priv->request.actual != req_priv->request.length &&
+ td->priv_request->request.short_not_ok) || (*status != 0 &&
+ !usb_endpoint_xfer_isoc(req_priv->dep->endpoint.desc)))
+ dev_dbg(usbssp_data->dev,
+ "Giveback Request %p, len = %d, expected = %d"
+ " status = %d\n",
+ req_priv, req_priv->request.actual,
+ req_priv->request.length, *status);
+
+ if (usb_endpoint_xfer_isoc(req_priv->dep->endpoint.desc))
+ *status = 0;
+
+ usbssp_giveback_request_in_irq(usbssp_data, td, *status);
+ }
+
+ return 0;
+}
+
+static int finish_td(struct usbssp_udc *usbssp_data, struct usbssp_td *td,
+ struct usbssp_transfer_event *event, struct usbssp_ep *ep,
+ int *status)
+{
+ struct usbssp_device *dev_priv;
+ struct usbssp_ring *ep_ring;
+ unsigned int slot_id;
+ int ep_index;
+ struct usbssp_ep_ctx *ep_ctx;
+ u32 trb_comp_code;
+
+ slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
+ dev_priv = &usbssp_data->devs;
+ ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1;
+ ep_ring = usbssp_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer));
+ ep_ctx = usbssp_get_ep_ctx(usbssp_data, dev_priv->out_ctx, ep_index);
+ trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len));
+
+ if (trb_comp_code == COMP_STOPPED_LENGTH_INVALID ||
+ trb_comp_code == COMP_STOPPED ||
+ trb_comp_code == COMP_STOPPED_SHORT_PACKET) {
+ /*
+ * The Endpoint Stop Command completion will take care of any
+ * stopped TDs. A stopped TD may be restarted, so don't update
+ * the ring dequeue pointer or take this TD off any lists yet.
+ */
+ return 0;
+ }
+
+ /* Update ring dequeue pointer */
+ while (ep_ring->dequeue != td->last_trb)
+ inc_deq(usbssp_data, ep_ring);
+
+ inc_deq(usbssp_data, ep_ring);
+
+ return usbssp_td_cleanup(usbssp_data, td, ep_ring, status);
+}
+
+/* sum trb lengths from ring dequeue up to stop_trb, _excluding_ stop_trb */
+static int sum_trb_lengths(struct usbssp_udc *usbssp_data,
+ struct usbssp_ring *ring,
+ union usbssp_trb *stop_trb)
+{
+ u32 sum;
+ union usbssp_trb *trb = ring->dequeue;
+ struct usbssp_segment *seg = ring->deq_seg;
+
+ for (sum = 0; trb != stop_trb; next_trb(usbssp_data, ring, &seg, &trb)) {
+ if (!trb_is_noop(trb) && !trb_is_link(trb))
+ sum += TRB_LEN(le32_to_cpu(trb->generic.field[2]));
+ }
+ return sum;
+}
+
+/*
+ * Process control tds, update USB request status and actual_length.
+ */
+static int process_ctrl_td(struct usbssp_udc *usbssp_data, struct usbssp_td *td,
+ union usbssp_trb *event_trb,
+ struct usbssp_transfer_event *event,
+ struct usbssp_ep *ep_priv, int *status)
+{
+ struct usbssp_device *dev_priv;
+ struct usbssp_ring *ep_ring;
+ unsigned int slot_id;
+ int ep_index;
+ struct usbssp_ep_ctx *ep_ctx;
+ u32 trb_comp_code;
+ u32 remaining, requested;
+ u32 trb_type;
+
+ trb_type = TRB_FIELD_TO_TYPE(le32_to_cpu(event_trb->generic.field[3]));
+ slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
+ dev_priv = &usbssp_data->devs;
+ ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1;
+ ep_ring = usbssp_dma_to_transfer_ring(ep_priv, le64_to_cpu(event->buffer));
+ ep_ctx = usbssp_get_ep_ctx(usbssp_data, dev_priv->out_ctx, ep_index);
+ trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len));
+ requested = td->priv_request->request.length;
+ remaining = EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));
+
+ switch (trb_comp_code) {
+ case COMP_SUCCESS:
+ *status = 0;
+ break;
+ case COMP_SHORT_PACKET:
+ *status = 0;
+ break;
+ case COMP_STOPPED_SHORT_PACKET:
+ if (trb_type == TRB_DATA || trb_type == TRB_NORMAL)
+ td->priv_request->request.actual = remaining;
+ goto finish_td;
+ case COMP_STOPPED:
+ switch (trb_type) {
+ case TRB_DATA:
+ case TRB_NORMAL:
+ td->priv_request->request.actual = requested - remaining;
+ goto finish_td;
+ case TRB_STATUS:
+ td->priv_request->request.actual = requested;
+ goto finish_td;
+ default:
+ dev_warn(usbssp_data->dev,
+ "WARN: unexpected TRB Type %d\n",
+ trb_type);
+ goto finish_td;
+ }
+ case COMP_STOPPED_LENGTH_INVALID:
+ goto finish_td;
+ default:
+ dev_dbg(usbssp_data->dev, "TRB error code %u, "
+ "halted endpoint index = %u\n",
+ trb_comp_code, ep_index);
+ }
+
+ /*
+ * if on data stage then update the actual_length of the USB
+ * request and flag it as set, so it won't be overwritten in the event
+ * for the last TRB.
+ */
+ if (trb_type == TRB_DATA ||
+ trb_type == TRB_NORMAL) {
+ td->request_length_set = true;
+ td->priv_request->request.actual = requested - remaining;
+ }
+
+ /* at status stage */
+ if (!td->request_length_set)
+ td->priv_request->request.actual = requested;
+
+ if (usbssp_data->ep0state == USBSSP_EP0_DATA_PHASE
+ && ep_priv->number == 0
+ && usbssp_data->three_stage_setup) {
+
+ td = list_entry(ep_ring->td_list.next, struct usbssp_td, td_list);
+ usbssp_data->ep0state = USBSSP_EP0_STATUS_PHASE;
+ dev_dbg(usbssp_data->dev, "Arm Status stage\n");
+ giveback_first_trb(usbssp_data, ep_index, 0,
+ ep_ring->cycle_state, &td->last_trb->generic);
+ return 0;
+ }
+finish_td:
+ return finish_td(usbssp_data, td, event, ep_priv, status);
+}
+
+/*
+ * Process isochronous tds, update usb request status and actual_length.
+ */
+static int process_isoc_td(struct usbssp_udc *usbssp_data, struct usbssp_td *td,
+ union usbssp_trb *ep_trb,
+ struct usbssp_transfer_event *event,
+ struct usbssp_ep *ep_priv, int *status)
+{
+ /*TODO: this function must be implemented*/
+ return 0;
+}
+
+static int skip_isoc_td(struct usbssp_udc *usbssp_data,
+ struct usbssp_td *td,
+ struct usbssp_transfer_event *event,
+ struct usbssp_ep *ep_priv,
+ int *status)
+{
+ /*TODO: this function must be implemented*/
+ return 0;
+}
+
+/*
+ * Process bulk and interrupt tds, update usb request status and actual_length.
+ */
+static int process_bulk_intr_td(struct usbssp_udc *usbssp_data,
+ struct usbssp_td *td,
+ union usbssp_trb *ep_trb,
+ struct usbssp_transfer_event *event,
+ struct usbssp_ep *ep, int *status)
+{
+ struct usbssp_ring *ep_ring;
+ u32 trb_comp_code;
+ u32 remaining, requested, ep_trb_len;
+
+ ep_ring = usbssp_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer));
+ trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len));
+ remaining = EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));
+ ep_trb_len = TRB_LEN(le32_to_cpu(ep_trb->generic.field[2]));
+ requested = td->priv_request->request.length;
+
+ switch (trb_comp_code) {
+ case COMP_SUCCESS:
+ /* handle success with untransferred data as short packet */
+ if (ep_trb != td->last_trb || remaining) {
+ dev_warn(usbssp_data->dev, "WARN Successful completion "
+ "on short TX\n");
+ dev_dbg(usbssp_data->dev,
+ "ep %#x - asked for %d bytes, %d bytes untransferred\n",
+ td->priv_request->dep->endpoint.desc->bEndpointAddress,
+ requested, remaining);
+ }
+ *status = 0;
+ break;
+ case COMP_SHORT_PACKET:
+ dev_dbg(usbssp_data->dev,
+ "ep %#x - asked for %d bytes, %d bytes untransferred\n",
+ td->priv_request->dep->endpoint.desc->bEndpointAddress,
+ requested, remaining);
+
+ *status = 0;
+ break;
+ case COMP_STOPPED_SHORT_PACKET:
+ td->priv_request->request.length = remaining;
+ goto finish_td;
+ case COMP_STOPPED_LENGTH_INVALID:
+ /* stopped on ep trb with invalid length, exclude it */
+ ep_trb_len = 0;
+ remaining = 0;
+ break;
+ default:
+ /* Others already handled above */
+ break;
+ }
+
+ if (ep_trb == td->last_trb)
+ td->priv_request->request.actual = requested - remaining;
+ else
+ td->priv_request->request.actual =
+ sum_trb_lengths(usbssp_data, ep_ring, ep_trb) + ep_trb_len - remaining;
+finish_td:
+ if (remaining > requested) {
+ dev_warn(usbssp_data->dev,
+ "bad transfer trb length %d in event trb\n",
+ remaining);
+ td->priv_request->request.actual = 0;
+ }
+
+ return finish_td(usbssp_data, td, event, ep, status);
+}
/*
* If this function returns an error condition, it means it got a Transfer
* event with a corrupted Slot ID, Endpoint ID, or TRB DMA address.
@@ -1536,8 +1831,313 @@ int usbssp_is_vendor_info_code(struct usbssp_udc *usbssp_data,
static int handle_tx_event(struct usbssp_udc *usbssp_data,
struct usbssp_transfer_event *event)
{
- /*TODO: implement function handling transfer event*/
+ struct usbssp_device *dev_priv;
+ struct usbssp_ep *ep_priv;
+ struct usbssp_ring *ep_ring;
+ unsigned int slot_id;
+ int ep_index;
+ struct usbssp_td *td = NULL;
+ dma_addr_t ep_trb_dma;
+ struct usbssp_segment *ep_seg;
+ union usbssp_trb *ep_trb;
+ int status = -EINPROGRESS;
+ struct usbssp_ep_ctx *ep_ctx;
+ struct list_head *tmp;
+ u32 trb_comp_code;
+ int ret = 0;
+ int td_num = 0;
+ bool handling_skipped_tds = false;
+ const struct usb_endpoint_descriptor *desc;
+
+ slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
+ ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1;
+ trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len));
+ ep_trb_dma = le64_to_cpu(event->buffer);
+
+ dev_priv = &usbssp_data->devs;
+
+ ep_priv = &dev_priv->eps[ep_index];
+ ep_ring = usbssp_dma_to_transfer_ring(ep_priv, le64_to_cpu(event->buffer));
+ ep_ctx = usbssp_get_ep_ctx(usbssp_data, dev_priv->out_ctx, ep_index);
+
+ if (GET_EP_CTX_STATE(ep_ctx) == EP_STATE_DISABLED) {
+ dev_err(usbssp_data->dev,
+ "ERROR Transfer event for disabled endpoint slot %u ep %u\n",
+ slot_id, ep_index);
+ goto err_out;
+ }
+
+ /* Some transfer events don't always point to a trb*/
+ if (!ep_ring) {
+ switch (trb_comp_code) {
+ case COMP_USB_TRANSACTION_ERROR:
+ case COMP_INVALID_STREAM_TYPE_ERROR:
+ case COMP_INVALID_STREAM_ID_ERROR:
+ goto cleanup;
+ case COMP_RING_UNDERRUN:
+ case COMP_RING_OVERRUN:
+ goto cleanup;
+ default:
+ dev_err(usbssp_data->dev, "ERROR Transfer event for "
+ "unknown stream ring slot %u ep %u\n",
+ slot_id, ep_index);
+ goto err_out;
+ }
+ }
+
+ /* Count current td numbers if ep->skip is set */
+ if (ep_priv->skip) {
+ list_for_each(tmp, &ep_ring->td_list)
+ td_num++;
+ }
+
+ /* Look for common error cases */
+ switch (trb_comp_code) {
+ /*
+ * Skip codes that require special handling depending on
+ * transfer type
+ */
+ case COMP_SUCCESS:
+ if (EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)) == 0)
+ break;
+
+ dev_warn_ratelimited(usbssp_data->dev,
+ "WARN Successful completion on short TX\n");
+ case COMP_SHORT_PACKET:
+ break;
+ case COMP_STOPPED:
+ dev_dbg(usbssp_data->dev, "Stopped on Transfer TRB for ep %u\n",
+ ep_index);
+ break;
+ case COMP_STOPPED_LENGTH_INVALID:
+ dev_dbg(usbssp_data->dev,
+ "Stopped on No-op or Link TRB for ep %u\n",
+ ep_index);
+ break;
+ case COMP_STOPPED_SHORT_PACKET:
+ dev_dbg(usbssp_data->dev,
+ "Stopped with short packet transfer detected for ep %u\n",
+ ep_index);
+ break;
+ case COMP_BABBLE_DETECTED_ERROR:
+ dev_dbg(usbssp_data->dev, "Babble error for ep %u on endpoint\n",
+ ep_index);
+ status = -EOVERFLOW;
+ break;
+ case COMP_TRB_ERROR:
+ dev_warn(usbssp_data->dev, "WARN: TRB error on endpoint %u\n",
+ ep_index);
+ status = -EILSEQ;
+ break;
+ /* completion codes not indicating endpoint state change */
+ case COMP_DATA_BUFFER_ERROR:
+ dev_warn(usbssp_data->dev,
+ "WARN: USBSSP couldn't access mem fast enough for ep %u\n",
+ ep_index);
+ status = -ENOSR;
+ break;
+ case COMP_ISOCH_BUFFER_OVERRUN:
+ dev_warn(usbssp_data->dev,
+ "WARN: buffer overrun event for ep %u on endpoint",
+ ep_index);
+ break;
+ case COMP_RING_UNDERRUN:
+ /*
+ * When the Isoch ring is empty, the DC will generate
+ * a Ring Overrun Event for IN Isoch endpoint or Ring
+ * Underrun Event for OUT Isoch endpoint.
+ */
+ dev_dbg(usbssp_data->dev, "underrun event on endpoint\n");
+ if (!list_empty(&ep_ring->td_list))
+ dev_dbg(usbssp_data->dev, "Underrun Event for ep %d "
+ "still with TDs queued?\n", ep_index);
+ goto cleanup;
+ case COMP_RING_OVERRUN:
+ dev_dbg(usbssp_data->dev, "overrun event on endpoint\n");
+ if (!list_empty(&ep_ring->td_list))
+ dev_dbg(usbssp_data->dev, "Overrun Event for ep %d "
+ "still with TDs queued?\n",
+ ep_index);
+ goto cleanup;
+ case COMP_MISSED_SERVICE_ERROR:
+ /*
+ * When encounter missed service error, one or more isoc tds
+ * may be missed by DC.
+ * Set skip flag of the ep_ring; Complete the missed tds as
+ * short transfer when process the ep_ring next time.
+ */
+ ep_priv->skip = true;
+ dev_dbg(usbssp_data->dev,
+ "Miss service interval error for ep %u, set skip flag\n",
+ ep_index);
+ goto cleanup;
+ case COMP_INCOMPATIBLE_DEVICE_ERROR:
+ /* needs disable slot command to recover */
+ dev_warn(usbssp_data->dev,
+ "WARN: detect an incompatible device for ep %u",
+ ep_index);
+ status = -EPROTO;
+ break;
+ default:
+ if (usbssp_is_vendor_info_code(usbssp_data, trb_comp_code)) {
+ status = 0;
+ break;
+ }
+
+ dev_warn(usbssp_data->dev,
+ "ERROR Unknown event condition %u, for ep %u - USBSSP probably busted\n",
+ trb_comp_code, ep_index);
+ goto cleanup;
+ }
+
+ do {
+ /*
+ * This TRB should be in the TD at the head of this ring's TD
+ * list.
+ */
+ if (list_empty(&ep_ring->td_list)) {
+ /*
+ * Don't print wanings if it's due to a stopped endpoint
+ * generating an extra completion event if the device
+ * was suspended. Or, a event for the last TRB of a
+ * short TD we already got a short event for.
+ * The short TD is already removed from the TD list.
+ */
+ if (!(trb_comp_code == COMP_STOPPED ||
+ trb_comp_code == COMP_STOPPED_LENGTH_INVALID ||
+ ep_ring->last_td_was_short)) {
+ dev_warn(usbssp_data->dev,
+ "WARN Event TRB for ep %d with no TDs queued?\n",
+ ep_index);
+ }
+
+ if (ep_priv->skip) {
+ ep_priv->skip = false;
+ dev_dbg(usbssp_data->dev,
+ "td_list is empty while skip "
+ "flag set. Clear skip flag for ep %u.\n",
+ ep_index);
+ }
+ goto cleanup;
+ }
+
+ /* We've skipped all the TDs on the ep ring when ep->skip set */
+ if (ep_priv->skip && td_num == 0) {
+ ep_priv->skip = false;
+ dev_dbg(usbssp_data->dev,
+ "All tds on the ep_ring skipped. "
+ "Clear skip flag for ep %u.\n", ep_index);
+ goto cleanup;
+ }
+
+ td = list_entry(ep_ring->td_list.next, struct usbssp_td, td_list);
+
+ if (ep_priv->skip)
+ td_num--;
+
+ /* Is this a TRB in the currently executing TD? */
+ ep_seg = usbssp_trb_in_td(usbssp_data, ep_ring->deq_seg,
+ ep_ring->dequeue, td->last_trb,
+ ep_trb_dma, false);
+
+ /*
+ * Skip the Force Stopped Event. The event_trb(ep_trb_dma)
+ * of FSE is not in the current TD pointed by ep_ring->dequeue
+ * because that the hardware dequeue pointer still at the
+ * previous TRB of the current TD. The previous TRB maybe a
+ * Link TD or the last TRB of the previous TD. The command
+ * completion handle will take care the rest.
+ */
+ if (!ep_seg && (trb_comp_code == COMP_STOPPED ||
+ trb_comp_code == COMP_STOPPED_LENGTH_INVALID)) {
+ goto cleanup;
+ }
+
+ desc = td->priv_request->dep->endpoint.desc;
+ if (!ep_seg) {
+ if (!ep_priv->skip || !usb_endpoint_xfer_isoc(desc)) {
+
+ /* USBSSP is busted, give up! */
+ dev_err(usbssp_data->dev,
+ "ERROR Transfer event TRB DMA ptr not "
+ "part of current TD ep_index %d "
+ "comp_code %u\n", ep_index,
+ trb_comp_code);
+
+ usbssp_trb_in_td(usbssp_data, ep_ring->deq_seg,
+ ep_ring->dequeue, td->last_trb,
+ ep_trb_dma, true);
+ return -ESHUTDOWN;
+ }
+
+ ret = skip_isoc_td(usbssp_data, td, event, ep_priv,
+ &status);
+ goto cleanup;
+ }
+
+ if (trb_comp_code == COMP_SHORT_PACKET)
+ ep_ring->last_td_was_short = true;
+ else
+ ep_ring->last_td_was_short = false;
+
+ if (ep_priv->skip) {
+ dev_dbg(usbssp_data->dev,
+ "Found td. Clear skip flag for ep %u.\n",
+ ep_index);
+ ep_priv->skip = false;
+ }
+
+ ep_trb = &ep_seg->trbs[(ep_trb_dma - ep_seg->dma) / sizeof(*ep_trb)];
+
+ trace_usbssp_handle_transfer(ep_ring,
+ (struct usbssp_generic_trb *) ep_trb);
+
+ if (trb_is_noop(ep_trb)) {
+ dev_dbg(usbssp_data->dev,
+ "event_trb is a no-op TRB. Skip it\n");
+ goto cleanup;
+ }
+
+ if (usb_endpoint_xfer_control(desc)) {
+ ret = process_ctrl_td(usbssp_data, td, ep_trb, event,
+ ep_priv, &status);
+ } else if (usb_endpoint_xfer_isoc(desc)) {
+ ret = process_isoc_td(usbssp_data, td, ep_trb,
+ event, ep_priv, &status);
+ } else {
+ ret = process_bulk_intr_td(usbssp_data, td, ep_trb,
+ event, ep_priv, &status);
+ }
+cleanup:
+ handling_skipped_tds = ep_priv->skip &&
+ trb_comp_code != COMP_MISSED_SERVICE_ERROR;
+
+ /*
+ * Do not update event ring dequeue pointer if we're in a loop
+ * processing missed tds.
+ */
+ if (!handling_skipped_tds)
+ inc_deq(usbssp_data, usbssp_data->event_ring);
+ /*
+ * If ep->skip is set, it means there are missed tds on the
+ * endpoint ring need to take care of.
+ * Process them as short transfer until reach the td pointed by
+ * the event.
+ */
+ } while (handling_skipped_tds);
+
return 0;
+
+err_out:
+ dev_err(usbssp_data->dev, "@%016llx %08x %08x %08x %08x\n",
+ (unsigned long long) usbssp_trb_virt_to_dma(
+ usbssp_data->event_ring->deq_seg,
+ usbssp_data->event_ring->dequeue),
+ lower_32_bits(le64_to_cpu(event->buffer)),
+ upper_32_bits(le64_to_cpu(event->buffer)),
+ le32_to_cpu(event->transfer_len),
+ le32_to_cpu(event->flags));
+ return -ENODEV;
}

/*
diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h
index 000f2cb93723..c4e440db6b23 100644
--- a/drivers/usb/usbssp/gadget.h
+++ b/drivers/usb/usbssp/gadget.h
@@ -1699,6 +1699,11 @@ int usbssp_ring_expansion(struct usbssp_udc *usbssp_data,
struct usbssp_ring *usbssp_stream_id_to_ring(struct usbssp_device *dev,
unsigned int ep_index,
unsigned int stream_id);
+struct usbssp_ring *usbssp_dma_to_transfer_ring(struct usbssp_ep *ep,
+ u64 address);
+struct usbssp_ring *usbssp_stream_id_to_ring(struct usbssp_device *dev,
+ unsigned int ep_index,
+ unsigned int stream_id);

struct usbssp_command *usbssp_alloc_command(struct usbssp_udc *usbssp_data,
bool allocate_completion,
--
2.17.1


2018-07-19 18:01:10

by Pawel Laszczak

[permalink] [raw]
Subject: [PATCH 22/31] usb: usbssp: added procedure removing request from transfer ring

Patch adds functionality that allows to remove the request from the
endpoint ring. This may cause the DC to stop USB transfers,
potentially stopping in the middle of a TRB buffer. The DC should
pick up where it left off in the TD, unless a Set Transfer Ring
Dequeue Pointer is issued.

Signed-off-by: Pawel Laszczak <[email protected]>
---
drivers/usb/usbssp/gadget-ring.c | 331 ++++++++++++++++++++++++++++++-
drivers/usb/usbssp/gadget.c | 49 ++++-
drivers/usb/usbssp/gadget.h | 11 +
3 files changed, 387 insertions(+), 4 deletions(-)

diff --git a/drivers/usb/usbssp/gadget-ring.c b/drivers/usb/usbssp/gadget-ring.c
index 4bb13f9e311a..cfb31120eef8 100644
--- a/drivers/usb/usbssp/gadget-ring.c
+++ b/drivers/usb/usbssp/gadget-ring.c
@@ -102,11 +102,51 @@ static bool link_trb_toggles_cycle(union usbssp_trb *trb)
return le32_to_cpu(trb->link.control) & LINK_TOGGLE;
}

+static bool last_td_in_request(struct usbssp_td *td)
+{
+ struct usbssp_request *req_priv = td->priv_request;
+
+ return req_priv->num_tds_done == req_priv->num_tds;
+}
+
static void inc_td_cnt(struct usbssp_request *priv_req)
{
priv_req->num_tds_done++;
}

+static void trb_to_noop(union usbssp_trb *trb, u32 noop_type)
+{
+ if (trb_is_link(trb)) {
+ /* unchain chained link TRBs */
+ trb->link.control &= cpu_to_le32(~TRB_CHAIN);
+ } else {
+ trb->generic.field[0] = 0;
+ trb->generic.field[1] = 0;
+ trb->generic.field[2] = 0;
+ /* Preserve only the cycle bit of this TRB */
+ trb->generic.field[3] &= cpu_to_le32(TRB_CYCLE);
+ trb->generic.field[3] |= cpu_to_le32(TRB_TYPE(noop_type));
+ }
+}
+
+/*
+ * Updates trb to point to the next TRB in the ring, and updates seg if the next
+ * TRB is in a new segment. This does not skip over link TRBs, and it does not
+ * effect the ring dequeue or enqueue pointers.
+ */
+static void next_trb(struct usbssp_udc *usbssp_data,
+ struct usbssp_ring *ring,
+ struct usbssp_segment **seg,
+ union usbssp_trb **trb)
+{
+ if (trb_is_link(*trb)) {
+ *seg = (*seg)->next;
+ *trb = ((*seg)->trbs);
+ } else {
+ (*trb)++;
+ }
+}
+
/*
* See Cycle bit rules. SW is the consumer for the event ring only.
* Don't make a ring full of link TRBs. That would be dumb and this would loop.
@@ -347,6 +387,157 @@ struct usbssp_ring *usbssp_triad_to_transfer_ring(struct usbssp_udc *usbssp_data
return NULL;
}

+/*
+ * Get the hw dequeue pointer DC stopped on, either directly from the
+ * endpoint context, or if streams are in use from the stream context.
+ * The returned hw_dequeue contains the lowest four bits with cycle state
+ * and possbile stream context type.
+ */
+u64 usbssp_get_hw_deq(struct usbssp_udc *usbssp_data,
+ struct usbssp_device *dev,
+ unsigned int ep_index,
+ unsigned int stream_id)
+{
+ struct usbssp_ep_ctx *ep_ctx;
+ struct usbssp_stream_ctx *st_ctx;
+ struct usbssp_ep *ep;
+
+ ep = &dev->eps[ep_index];
+
+ if (ep->ep_state & EP_HAS_STREAMS) {
+ st_ctx = &ep->stream_info->stream_ctx_array[stream_id];
+ return le64_to_cpu(st_ctx->stream_ring);
+ }
+ ep_ctx = usbssp_get_ep_ctx(usbssp_data, dev->out_ctx, ep_index);
+ return le64_to_cpu(ep_ctx->deq);
+}
+
+/*
+ * Move the DC endpoint ring dequeue pointer past cur_td.
+ * Record the new state of the DC endpoint ring dequeue segment,
+ * dequeue pointer, and new consumer cycle state in state.
+ * Update our internal representation of the ring's dequeue pointer.
+ *
+ * We do this in three jumps:
+ * - First we update our new ring state to be the same as when the DC stopped.
+ * - Then we traverse the ring to find the segment that contains
+ * the last TRB in the TD. We toggle the DC new cycle state when we pass
+ * any link TRBs with the toggle cycle bit set.
+ * - Finally we move the dequeue state one TRB further, toggling the cycle bit
+ * if we've moved it past a link TRB with the toggle cycle bit set.
+ */
+void usbssp_find_new_dequeue_state(struct usbssp_udc *usbssp_data,
+ unsigned int ep_index,
+ unsigned int stream_id,
+ struct usbssp_td *cur_td,
+ struct usbssp_dequeue_state *state)
+{
+ struct usbssp_device *dev_priv = &usbssp_data->devs;
+ struct usbssp_ep *ep_priv = &dev_priv->eps[ep_index];
+ struct usbssp_ring *ep_ring;
+ struct usbssp_segment *new_seg;
+ union usbssp_trb *new_deq;
+ dma_addr_t addr;
+ u64 hw_dequeue;
+ bool cycle_found = false;
+ bool td_last_trb_found = false;
+
+ ep_ring = usbssp_triad_to_transfer_ring(usbssp_data,
+ ep_index, stream_id);
+ if (!ep_ring) {
+ dev_warn(usbssp_data->dev, "WARN can't find new dequeue state "
+ "for invalid stream ID %u.\n",
+ stream_id);
+ return;
+ }
+
+ /* Dig out the cycle state saved by the DC during the stop ep cmd */
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_cancel_request,
+ "Finding endpoint context");
+
+ hw_dequeue = usbssp_get_hw_deq(usbssp_data, dev_priv,
+ ep_index, stream_id);
+ new_seg = ep_ring->deq_seg;
+ new_deq = ep_ring->dequeue;
+ state->new_cycle_state = hw_dequeue & 0x1;
+ state->stream_id = stream_id;
+
+ /*
+ * We want to find the pointer, segment and cycle state of the new trb
+ * (the one after current TD's last_trb). We know the cycle state at
+ * hw_dequeue, so walk the ring until both hw_dequeue and last_trb are
+ * found.
+ */
+ do {
+ if (!cycle_found && usbssp_trb_virt_to_dma(new_seg, new_deq)
+ == (dma_addr_t)(hw_dequeue & ~0xf)) {
+ cycle_found = true;
+ if (td_last_trb_found)
+ break;
+ }
+
+ if (new_deq == cur_td->last_trb)
+ td_last_trb_found = true;
+
+ if (cycle_found && trb_is_link(new_deq) &&
+ link_trb_toggles_cycle(new_deq))
+ state->new_cycle_state ^= 0x1;
+
+ next_trb(usbssp_data, ep_ring, &new_seg, &new_deq);
+
+ /* Search wrapped around, bail out */
+ if (new_deq == ep_priv->ring->dequeue) {
+ dev_err(usbssp_data->dev,
+ "Error: Failed finding new dequeue state\n");
+ state->new_deq_seg = NULL;
+ state->new_deq_ptr = NULL;
+ return;
+ }
+
+ } while (!cycle_found || !td_last_trb_found);
+
+ state->new_deq_seg = new_seg;
+ state->new_deq_ptr = new_deq;
+
+ /* Don't update the ring cycle state for the producer (us). */
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_cancel_request,
+ "Cycle state = 0x%x", state->new_cycle_state);
+
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_cancel_request,
+ "New dequeue segment = %p (virtual)",
+ state->new_deq_seg);
+ addr = usbssp_trb_virt_to_dma(state->new_deq_seg, state->new_deq_ptr);
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_cancel_request,
+ "New dequeue pointer = 0x%llx (DMA)",
+ (unsigned long long) addr);
+}
+
+/*
+ * flip_cycle means flip the cycle bit of all but the first and last TRB.
+ * (The last TRB actually points to the ring enqueue pointer, which is not part
+ * of this TD.) This is used to remove partially enqueued isoc TDs from a ring.
+ */
+static void td_to_noop(struct usbssp_udc *usbssp_data,
+ struct usbssp_ring *ep_ring,
+ struct usbssp_td *td, bool flip_cycle)
+{
+ struct usbssp_segment *seg = td->start_seg;
+ union usbssp_trb *trb = td->first_trb;
+
+ while (1) {
+ trb_to_noop(trb, TRB_TR_NOOP);
+
+ /* flip cycle if asked to */
+ if (flip_cycle && trb != td->first_trb && trb != td->last_trb)
+ trb->generic.field[3] ^= cpu_to_le32(TRB_CYCLE);
+
+ if (trb == td->last_trb)
+ break;
+
+ next_trb(usbssp_data, ep_ring, &seg, &trb);
+ }
+}
+
/* Must be called with usbssp_data->lock held in interrupt context
* or usbssp_data->irq_thread_lock from thread conext (defered interrupt)
*/
@@ -365,6 +556,139 @@ void usbssp_giveback_request_in_irq(struct usbssp_udc *usbssp_data,
usbssp_gadget_giveback(req_priv->dep, req_priv, status);
}

+void usbssp_unmap_td_bounce_buffer(struct usbssp_udc *usbssp_data,
+ struct usbssp_ring *ring,
+ struct usbssp_td *td)
+{
+ /*TODO: ??? */
+}
+
+void usbssp_remove_request(struct usbssp_udc *usbssp_data,
+ struct usbssp_request *req_priv, int ep_index)
+{
+ int i = 0;
+ struct usbssp_ring *ep_ring;
+ struct usbssp_ep *ep;
+ struct usbssp_td *cur_td = NULL;
+ struct usbssp_ep_ctx *ep_ctx;
+ struct usbssp_device *priv_dev;
+ u64 hw_deq;
+ struct usbssp_dequeue_state deq_state;
+
+ memset(&deq_state, 0, sizeof(deq_state));
+ ep = &usbssp_data->devs.eps[ep_index];
+
+ priv_dev = &usbssp_data->devs;
+ ep_ctx = usbssp_get_ep_ctx(usbssp_data, priv_dev->out_ctx, ep_index);
+ trace_usbssp_remove_request(ep_ctx);
+
+ i = req_priv->num_tds_done;
+
+ for (; i < req_priv->num_tds; i++) {
+ cur_td = &req_priv->td[i];
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_cancel_request,
+ "Removing canceled TD starting at 0x%llx (dma).",
+ (unsigned long long)usbssp_trb_virt_to_dma(
+ cur_td->start_seg,
+ cur_td->first_trb));
+
+ ep_ring = usbssp_request_to_transfer_ring(usbssp_data,
+ cur_td->priv_request);
+
+ if (!ep_ring) {
+ /*
+ * This shouldn't happen unless a driver is mucking
+ * with the stream ID after submission. This will
+ * leave the TD on the hardware ring, and the hardware
+ * will try to execute it, and may access a buffer
+ * that has already been freed. In the best case, the
+ * hardware will execute it, and the event handler will
+ * ignore the completion event for that TD, since it was
+ * removed from the td_list for that endpoint. In
+ * short, don't muck with the stream ID after
+ * submission.
+ */
+ dev_warn(usbssp_data->dev, "WARN Cancelled USB Request %p"
+ " has invalid stream ID %u.\n",
+ cur_td->priv_request,
+ cur_td->priv_request->request.stream_id);
+ goto remove_finished_td;
+ }
+
+ if (!(ep->ep_state & USBSSP_EP_ENABLED) ||
+ ep->ep_state & USBSSP_EP_DISABLE_PENDING) {
+ goto remove_finished_td;
+ }
+
+ /*
+ * If we stopped on the TD we need to cancel, then we have to
+ * move the DC endpoint ring dequeue pointer past this TD.
+ */
+ hw_deq = usbssp_get_hw_deq(usbssp_data, priv_dev, ep_index,
+ cur_td->priv_request->request.stream_id);
+ hw_deq &= ~0xf;
+
+ if (usbssp_trb_in_td(usbssp_data, cur_td->start_seg,
+ cur_td->first_trb, cur_td->last_trb, hw_deq, false)) {
+ usbssp_find_new_dequeue_state(usbssp_data, ep_index,
+ cur_td->priv_request->request.stream_id,
+ cur_td, &deq_state);
+ } else {
+ td_to_noop(usbssp_data, ep_ring, cur_td, false);
+ }
+
+remove_finished_td:
+ /*
+ * The event handler won't see a completion for this TD anymore,
+ * so remove it from the endpoint ring's TD list.
+ */
+ list_del_init(&cur_td->td_list);
+ }
+
+ ep->ep_state &= ~EP_STOP_CMD_PENDING;
+
+ if (!(ep->ep_state & USBSSP_EP_DISABLE_PENDING) &&
+ ep->ep_state & USBSSP_EP_ENABLED) {
+ /* If necessary, queue a Set Transfer Ring Dequeue Pointer command*/
+ if (deq_state.new_deq_ptr && deq_state.new_deq_seg) {
+ usbssp_queue_new_dequeue_state(usbssp_data, ep_index,
+ &deq_state);
+ usbssp_ring_cmd_db(usbssp_data);
+ } else {
+ /*
+ * Otherwise ring the doorbell(s) to restart queued
+ * transfers
+ */
+ ring_doorbell_for_active_rings(usbssp_data, ep_index);
+ }
+ }
+
+ /*
+ * Complete the cancellation of USB request.
+ */
+ i = req_priv->num_tds_done;
+ for (; i < req_priv->num_tds; i++) {
+ cur_td = &req_priv->td[i];
+
+ /*
+ * Clean up the cancelled USB Request
+ * Doesn't matter what we pass for status, since the core will
+ * just overwrite it.
+ */
+ ep_ring = usbssp_request_to_transfer_ring(usbssp_data,
+ cur_td->priv_request);
+
+ usbssp_unmap_td_bounce_buffer(usbssp_data, ep_ring, cur_td);
+
+ inc_td_cnt(cur_td->priv_request);
+ if (last_td_in_request(cur_td)) {
+ usbssp_giveback_request_in_irq(usbssp_data,
+ cur_td, -ECONNRESET);
+ }
+ }
+}
+
+
/*
* When we get a command completion for a Stop Endpoint Command, we need to
* stop timer and clear EP_STOP_CMD_PENDING flag.
@@ -385,7 +709,6 @@ static void usbssp_handle_cmd_stop_ep(struct usbssp_udc *usbssp_data,
"CMD stop endpoint completion for ep index: %d - %s\n",
ep_index, ep->name);

-
priv_dev = &usbssp_data->devs;
ep_ctx = usbssp_get_ep_ctx(usbssp_data, priv_dev->out_ctx, ep_index);
trace_usbssp_handle_cmd_stop_ep(ep_ctx);
@@ -2273,7 +2596,8 @@ void usbssp_queue_new_dequeue_state(struct usbssp_udc *usbssp_data,
(unsigned long long)deq_state->new_deq_seg->dma,
deq_state->new_deq_ptr,
(unsigned long long)usbssp_trb_virt_to_dma(
- deq_state->new_deq_seg, deq_state->new_deq_ptr),
+ deq_state->new_deq_seg,
+ deq_state->new_deq_ptr),
deq_state->new_cycle_state);

addr = usbssp_trb_virt_to_dma(deq_state->new_deq_seg,
@@ -2284,6 +2608,7 @@ void usbssp_queue_new_dequeue_state(struct usbssp_udc *usbssp_data,
deq_state->new_deq_seg, deq_state->new_deq_ptr);
return;
}
+
ep_priv = &usbssp_data->devs.eps[ep_index];
if ((ep_priv->ep_state & SET_DEQ_PENDING)) {
dev_warn(usbssp_data->dev, "WARN Cannot submit Set TR Deq Ptr\n");
@@ -2304,10 +2629,12 @@ void usbssp_queue_new_dequeue_state(struct usbssp_udc *usbssp_data,
ep_priv->queued_deq_ptr = deq_state->new_deq_ptr;
if (deq_state->stream_id)
trb_sct = SCT_FOR_TRB(SCT_PRI_TR);
+
ret = queue_command(usbssp_data, cmd,
lower_32_bits(addr) | trb_sct | deq_state->new_cycle_state,
upper_32_bits(addr), trb_stream_id,
trb_slot_id | trb_ep_index | type, false);
+
if (ret < 0) {
usbssp_free_command(usbssp_data, cmd);
return;
diff --git a/drivers/usb/usbssp/gadget.c b/drivers/usb/usbssp/gadget.c
index e2751693404d..fe373a7b7198 100644
--- a/drivers/usb/usbssp/gadget.c
+++ b/drivers/usb/usbssp/gadget.c
@@ -545,8 +545,53 @@ int usbssp_enqueue(struct usbssp_ep *dep, struct usbssp_request *req_priv)
*/
int usbssp_dequeue(struct usbssp_ep *ep_priv, struct usbssp_request *req_priv)
{
- /*TODO: this function must be implemented*/
- return 0;
+ int ret = 0, i;
+ struct usbssp_udc *usbssp_data;
+ unsigned int ep_index;
+ struct usbssp_ring *ep_ring;
+ struct usbssp_device *priv_dev;
+ struct usbssp_ep_ctx *ep_ctx;
+
+ usbssp_data = ep_priv->usbssp_data;
+ trace_usbssp_request_dequeue(&req_priv->request);
+
+ priv_dev = &usbssp_data->devs;
+ ep_index = usbssp_get_endpoint_index(req_priv->dep->endpoint.desc);
+ ep_priv = &usbssp_data->devs.eps[ep_index];
+ ep_ring = usbssp_request_to_transfer_ring(usbssp_data, req_priv);
+
+ if (!ep_ring)
+ goto err_giveback;
+
+ i = req_priv->num_tds_done;
+
+ if (i < req_priv->num_tds)
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_cancel_request,
+ "Cancel request %p, dev %s, ep 0x%x, "
+ "starting at offset 0x%llx",
+ &req_priv->request, usbssp_data->gadget.name,
+ req_priv->dep->endpoint.desc->bEndpointAddress,
+ (unsigned long long) usbssp_trb_virt_to_dma(
+ req_priv->td[i].start_seg,
+ req_priv->td[i].first_trb));
+
+ /* Queue a stop endpoint command, but only if it is
+ * in EP_STATE_RUNNING state.
+ */
+ ep_ctx = usbssp_get_ep_ctx(usbssp_data, priv_dev->out_ctx, ep_index);
+ if (GET_EP_CTX_STATE(ep_ctx) == EP_STATE_RUNNING) {
+ ret = usbssp_cmd_stop_ep(usbssp_data, &usbssp_data->gadget,
+ ep_priv);
+ if (ret)
+ return ret;
+ }
+
+ usbssp_remove_request(usbssp_data, req_priv, ep_index);
+ return ret;
+
+err_giveback:
+ usbssp_giveback_request_in_irq(usbssp_data, req_priv->td, -ESHUTDOWN);
+ return ret;
}

int usbssp_halt_endpoint(struct usbssp_udc *usbssp_data, struct usbssp_ep *dep,
diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h
index 0477eb0f354c..000f2cb93723 100644
--- a/drivers/usb/usbssp/gadget.h
+++ b/drivers/usb/usbssp/gadget.h
@@ -1780,6 +1780,14 @@ int usbssp_queue_halt_endpoint(struct usbssp_udc *usbssp_data,
unsigned int ep_index);
int usbssp_queue_reset_device(struct usbssp_udc *usbssp_data,
struct usbssp_command *cmd);
+void usbssp_find_new_dequeue_state(struct usbssp_udc *usbssp_data,
+ unsigned int ep_index,
+ unsigned int stream_id,
+ struct usbssp_td *cur_td,
+ struct usbssp_dequeue_state *state);
+void usbssp_queue_new_dequeue_state(struct usbssp_udc *usbssp_data,
+ unsigned int ep_index,
+ struct usbssp_dequeue_state *deq_state);
void usbssp_handle_command_timeout(struct work_struct *work);

void usbssp_cleanup_command_queue(struct usbssp_udc *usbssp_data);
@@ -2313,4 +2321,7 @@ __le32 __iomem *usbssp_get_port_io_addr(struct usbssp_udc *usbssp_data);
void usbssp_giveback_request_in_irq(struct usbssp_udc *usbssp_data,
struct usbssp_td *cur_td, int status);

+void usbssp_remove_request(struct usbssp_udc *usbssp_data,
+ struct usbssp_request *req_priv, int ep_index);
+
#endif /* __LINUX_USBSSP_GADGET_H */
--
2.17.1


2018-07-19 18:01:17

by Pawel Laszczak

[permalink] [raw]
Subject: [PATCH 06/31] usb: usbssp: added template functions used by upper layer.

Patch adds some functionality for initialization process.
It adds to driver usbssp_reset and usbssp_start function.

Next elements added are objects usbssp_gadget_ep0_ops,
usbssp_gadget_ep_ops and usbssp_gadget_ops. These objects
constitute the interface used by gadget subsystem.
At this moment functions related to these objects are empty
and do nothing.

This patch also implements usbssp_gadget_init_endpoint and
usbssp_gadget_free_endpoint used during initialization.

Signed-off-by: Pawel Laszczak <[email protected]>
---
drivers/usb/usbssp/gadget-ext-caps.h | 3 +
drivers/usb/usbssp/gadget-if.c | 269 ++++++++++++++++++++++++++-
drivers/usb/usbssp/gadget.c | 84 ++++++++-
3 files changed, 350 insertions(+), 6 deletions(-)

diff --git a/drivers/usb/usbssp/gadget-ext-caps.h b/drivers/usb/usbssp/gadget-ext-caps.h
index 2bf327046376..86c0ce331037 100644
--- a/drivers/usb/usbssp/gadget-ext-caps.h
+++ b/drivers/usb/usbssp/gadget-ext-caps.h
@@ -51,3 +51,6 @@
#define USBSSP_CMD_EWE BIT(10)

#define USBSSP_IRQS (USBSSP_CMD_EIE | USBSSP_CMD_HSEIE | USBSSP_CMD_EWE)
+
+/* true: Controller Not Ready to accept doorbell or op reg writes after reset */
+#define USBSSP_STS_CNR BIT(11)
diff --git a/drivers/usb/usbssp/gadget-if.c b/drivers/usb/usbssp/gadget-if.c
index d53e0fb65299..70def978b085 100644
--- a/drivers/usb/usbssp/gadget-if.c
+++ b/drivers/usb/usbssp/gadget-if.c
@@ -12,13 +12,278 @@
#include <linux/usb/composite.h>
#include "gadget.h"

+static int usbssp_gadget_ep_enable(struct usb_ep *ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
+ int ret = 0;
+
+ if (!ep_priv)
+ return -EINVAL;
+
+ /*TODO: implements this function*/
+ return ret;
+}
+
+int usbssp_gadget_ep_disable(struct usb_ep *ep)
+{
+ struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
+ int ret = 0;
+
+ if (!ep_priv)
+ return -EINVAL;
+
+ /*TODO: implements this function*/
+ return ret;
+}
+
+static struct usb_request *usbssp_gadget_ep_alloc_request(struct usb_ep *ep,
+ gfp_t gfp_flags)
+{
+ struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
+
+ if (!ep_priv)
+ return NULL;
+
+ /*TODO: implements this function*/
+ return NULL;
+}
+
+static void usbssp_gadget_ep_free_request(struct usb_ep *ep,
+ struct usb_request *request)
+{
+ struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
+
+ if (!ep_priv)
+ return;
+
+ /*TODO: implements this function*/
+}
+
+static int usbssp_gadget_ep_queue(struct usb_ep *ep,
+ struct usb_request *request,
+ gfp_t gfp_flags)
+{
+ struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
+ int ret = 0;
+
+ if (!ep_priv)
+ return -EINVAL;
+
+ /*TODO: implements this function*/
+ return ret;
+}
+
+static int usbssp_gadget_ep_dequeue(struct usb_ep *ep,
+ struct usb_request *request)
+{
+ struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
+ int ret = 0;
+
+ if (!ep_priv)
+ return -EINVAL;
+
+ /*TODO: implements this function*/
+ return ret;
+}
+
+static int usbssp_gadget_ep_set_halt(struct usb_ep *ep, int value)
+{
+ struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
+ int ret = 0;
+
+ if (!ep_priv)
+ return -EINVAL;
+
+ /*TODO: implements this function*/
+ return ret;
+}
+
+static int usbssp_gadget_ep_set_wedge(struct usb_ep *ep)
+{
+ struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
+ int ret = 0;
+
+ if (!ep_priv)
+ return -EINVAL;
+
+ /*TODO: implements this function*/
+ return ret;
+}
+
+static const struct usb_ep_ops usbssp_gadget_ep0_ops = {
+ .enable = usbssp_gadget_ep_enable,
+ .disable = usbssp_gadget_ep_disable,
+ .alloc_request = usbssp_gadget_ep_alloc_request,
+ .free_request = usbssp_gadget_ep_free_request,
+ .queue = usbssp_gadget_ep_queue,
+ .dequeue = usbssp_gadget_ep_dequeue,
+ .set_halt = usbssp_gadget_ep_set_halt,
+ .set_wedge = usbssp_gadget_ep_set_wedge,
+};
+
+static const struct usb_ep_ops usbssp_gadget_ep_ops = {
+ .enable = usbssp_gadget_ep_enable,
+ .disable = usbssp_gadget_ep_disable,
+ .alloc_request = usbssp_gadget_ep_alloc_request,
+ .free_request = usbssp_gadget_ep_free_request,
+ .queue = usbssp_gadget_ep_queue,
+ .dequeue = usbssp_gadget_ep_dequeue,
+ .set_halt = usbssp_gadget_ep_set_halt,
+ .set_wedge = usbssp_gadget_ep_set_wedge,
+};
+
+static struct usb_endpoint_descriptor usbssp_gadget_ep0_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
+};
+
+static int usbssp_gadget_start(struct usb_gadget *g,
+ struct usb_gadget_driver *driver)
+{
+ struct usbssp_udc *usbssp_data = gadget_to_usbssp(g);
+ int ret = 0;
+
+ if (usbssp_data->gadget_driver) {
+ dev_err(usbssp_data->dev, "%s is already bound to %s\n",
+ usbssp_data->gadget.name,
+ usbssp_data->gadget_driver->driver.name);
+ ret = -EBUSY;
+ }
+
+ /*TODO: add implementation*/
+ return ret;
+}
+
+static int usbssp_gadget_stop(struct usb_gadget *g)
+{
+ struct usbssp_udc *usbssp_data = gadget_to_usbssp(g);
+
+ if (!usbssp_data)
+ return -EINVAL;
+ /*TODO: add implementation*/
+ return 0;
+}
+
+static int usbssp_gadget_get_frame(struct usb_gadget *g)
+{
+ struct usbssp_udc *usbssp_data = gadget_to_usbssp(g);
+
+ if (!usbssp_data)
+ return -EINVAL;
+
+ /*TODO: add implementation*/
+ return 0;
+}
+
+static int usbssp_gadget_wakeup(struct usb_gadget *g)
+{
+ struct usbssp_udc *usbssp_data = gadget_to_usbssp(g);
+
+ if (!usbssp_data)
+ return -EINVAL;
+
+ /*TODO: add implementation*/
+ return 0;
+}
+
+static int usbssp_gadget_set_selfpowered(struct usb_gadget *g,
+ int is_selfpowered)
+{
+ struct usbssp_udc *usbssp_data = gadget_to_usbssp(g);
+
+ if (!usbssp_data)
+ return -EINVAL;
+
+ /*TODO: add implementation*/
+ return 0;
+}
+
+static const struct usb_gadget_ops usbssp_gadget_ops = {
+ .get_frame = usbssp_gadget_get_frame,
+ .wakeup = usbssp_gadget_wakeup,
+ .set_selfpowered = usbssp_gadget_set_selfpowered,
+ .udc_start = usbssp_gadget_start,
+ .udc_stop = usbssp_gadget_stop,
+};
+
int usbssp_gadget_init_endpoint(struct usbssp_udc *usbssp_data)
{
- /*TODO: it has to be implemented*/
+ int i = 0;
+ struct usbssp_ep *ep_priv;
+
+ usbssp_data->num_endpoints = USBSSP_ENDPOINTS_NUM;
+ INIT_LIST_HEAD(&usbssp_data->gadget.ep_list);
+
+ for (i = 1; i < usbssp_data->num_endpoints; i++) {
+ bool direction = i & 1; /*start from OUT endpoint*/
+ u8 epnum = (i >> 1);
+
+ ep_priv = &usbssp_data->devs.eps[i-1];
+ ep_priv->usbssp_data = usbssp_data;
+ ep_priv->number = epnum;
+ ep_priv->direction = direction; /*0 for OUT, 1 for IN*/
+
+ snprintf(ep_priv->name, sizeof(ep_priv->name), "ep%d%s", epnum,
+ (ep_priv->direction) ? "in" : "out");
+
+ ep_priv->endpoint.name = ep_priv->name;
+
+ if (ep_priv->number < 2) {
+ ep_priv->endpoint.desc = &usbssp_gadget_ep0_desc;
+ ep_priv->endpoint.comp_desc = NULL;
+ }
+
+ if (epnum == 0) {
+ /*EP0 is bidirectional endpoint*/
+ usb_ep_set_maxpacket_limit(&ep_priv->endpoint, 512);
+ dev_dbg(usbssp_data->dev,
+ "Initializing %s, MaxPack: %04x Type: Ctrl\n",
+ ep_priv->name, 512);
+ ep_priv->endpoint.maxburst = 1;
+ ep_priv->endpoint.ops = &usbssp_gadget_ep0_ops;
+ ep_priv->endpoint.caps.type_control = true;
+
+ usbssp_data->usb_req_ep0_in.epnum = ep_priv->number;
+ usbssp_data->usb_req_ep0_in.dep = ep_priv;
+
+ if (!epnum)
+ usbssp_data->gadget.ep0 = &ep_priv->endpoint;
+ } else {
+ usb_ep_set_maxpacket_limit(&ep_priv->endpoint, 1024);
+ ep_priv->endpoint.maxburst = 15;
+ ep_priv->endpoint.ops = &usbssp_gadget_ep_ops;
+ list_add_tail(&ep_priv->endpoint.ep_list,
+ &usbssp_data->gadget.ep_list);
+ ep_priv->endpoint.caps.type_iso = true;
+ ep_priv->endpoint.caps.type_bulk = true;
+ ep_priv->endpoint.caps.type_int = true;
+
+ }
+
+ ep_priv->endpoint.caps.dir_in = direction;
+ ep_priv->endpoint.caps.dir_out = !direction;
+
+ dev_dbg(usbssp_data->dev, "Init %s, MaxPack: %04x SupType:"
+ " INT/BULK/ISOC , SupDir %s\n",
+ ep_priv->name, 1024,
+ (ep_priv->endpoint.caps.dir_in) ? "IN" : "OUT");
+
+ INIT_LIST_HEAD(&ep_priv->pending_list);
+ }
return 0;
}

void usbssp_gadget_free_endpoint(struct usbssp_udc *usbssp_data)
{
- /*TODO: it has to be implemented*/
+ int i;
+ struct usbssp_ep *ep_priv;
+
+ for (i = 0; i < usbssp_data->num_endpoints; i++) {
+ ep_priv = &usbssp_data->devs.eps[i];
+
+ if (ep_priv->number != 0)
+ list_del(&ep_priv->endpoint.ep_list);
+ }
}
diff --git a/drivers/usb/usbssp/gadget.c b/drivers/usb/usbssp/gadget.c
index 338ec2ec18b1..195f5777cf8a 100644
--- a/drivers/usb/usbssp/gadget.c
+++ b/drivers/usb/usbssp/gadget.c
@@ -103,6 +103,83 @@ int usbssp_halt(struct usbssp_udc *usbssp_data)
return ret;
}

+/*
+ * Set the run bit and wait for the device to be running.
+ */
+int usbssp_start(struct usbssp_udc *usbssp_data)
+{
+ u32 temp;
+ int ret;
+
+ temp = readl(&usbssp_data->op_regs->command);
+ temp |= (CMD_RUN | CMD_DEVEN);
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
+ "// Turn on USBSSP, cmd = 0x%x.", temp);
+ writel(temp, &usbssp_data->op_regs->command);
+
+ /*
+ * Wait for the HCHalted Staus bit to be 0 to indicate the device is
+ * running.
+ */
+ ret = usbssp_handshake(&usbssp_data->op_regs->status,
+ STS_HALT, 0, USBSSP_MAX_HALT_USEC);
+
+ if (ret == -ETIMEDOUT)
+ dev_err(usbssp_data->dev, "Device took too long to start, waited %u microseconds.\n",
+ USBSSP_MAX_HALT_USEC);
+ if (!ret)
+ /* clear state flags. Including dying, halted or removing */
+ usbssp_data->usbssp_state = 0;
+
+ return ret;
+}
+
+/*
+ * Reset a halted DC.
+ *
+ * This resets pipelines, timers, counters, state machines, etc.
+ * Transactions will be terminated immediately, and operational registers
+ * will be set to their defaults.
+ */
+int usbssp_reset(struct usbssp_udc *usbssp_data)
+{
+ u32 command;
+ u32 state;
+ int ret;
+
+ state = readl(&usbssp_data->op_regs->status);
+
+ if (state == ~(u32)0) {
+ dev_warn(usbssp_data->dev, "Device not accessible, reset failed.\n");
+ return -ENODEV;
+ }
+
+ if ((state & STS_HALT) == 0) {
+ dev_warn(usbssp_data->dev, "DC not halted, aborting reset.\n");
+ return 0;
+ }
+
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init, "// Reset the DC");
+ command = readl(&usbssp_data->op_regs->command);
+ command |= CMD_RESET;
+ writel(command, &usbssp_data->op_regs->command);
+
+ ret = usbssp_handshake(&usbssp_data->op_regs->command,
+ CMD_RESET, 0, 10 * 1000 * 1000);
+
+ if (ret)
+ return ret;
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
+ "Wait for controller to be ready for doorbell rings");
+ /*
+ * USBSSP cannot write to any doorbells or operational registers other
+ * than status until the "Controller Not Ready" flag is cleared.
+ */
+ ret = usbssp_handshake(&usbssp_data->op_regs->status,
+ STS_CNR, 0, 10 * 1000 * 1000);
+
+ return ret;
+}

/*
* Initialize memory for gadget driver and USBSSP (one-time init).
@@ -179,8 +256,7 @@ int usbssp_gen_setup(struct usbssp_udc *usbssp_data)

dev_dbg(usbssp_data->dev, "Resetting Device Controller\n");
/* Reset the internal DC memory state and registers. */
- /*TODO: add implementation of usbssp_reset function*/
- //retval = usbssp_reset(usbssp_data);
+ retval = usbssp_reset(usbssp_data);
if (retval)
return retval;
dev_dbg(usbssp_data->dev, "Reset complete\n");
@@ -244,8 +320,7 @@ int usbssp_gadget_init(struct usbssp_udc *usbssp_data)
BUILD_BUG_ON(sizeof(struct usbssp_run_regs) != (8+8*128)*32/8);

/* fill gadget fields */
- /*TODO: implements usbssp_gadget_ops object*/
- //usbssp_data->gadget.ops = &usbssp_gadget_ops;
+ usbssp_data->gadget.ops = &usbssp_gadget_ops;
usbssp_data->gadget.name = "usbssp-gadget";
usbssp_data->gadget.max_speed = USB_SPEED_SUPER_PLUS;
usbssp_data->gadget.speed = USB_SPEED_UNKNOWN;
@@ -288,6 +363,7 @@ int usbssp_gadget_init(struct usbssp_udc *usbssp_data)
usbssp_halt(usbssp_data);
/*TODO add implementation of usbssp_reset function*/
//usbssp_reset(usbssp_data);
+ usbssp_reset(usbssp_data);
/*TODO add implementation of freeing memory*/
//usbssp_mem_cleanup(usbssp_data);
err3:
--
2.17.1


2018-07-19 18:01:43

by Pawel Laszczak

[permalink] [raw]
Subject: [PATCH 12/31] usb: usbssp: added functions for queuing commands.

This patch defines a set of functions used to send
commands to command ring.

Commands added to Command Ring are handled sequentially
by hardware. After completion, controller should
add appropriate event to Event Ring.

Controller specification said that command may not be
completed. For this reason, driver should start
watchdog timer before arming Command Ring.

Signed-off-by: Pawel Laszczak <[email protected]>
---
drivers/usb/usbssp/gadget-ring.c | 538 ++++++++++++++++++++++++++++++-
drivers/usb/usbssp/gadget.h | 6 +
2 files changed, 543 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/usbssp/gadget-ring.c b/drivers/usb/usbssp/gadget-ring.c
index 3075909c2e31..f6a6269e071d 100644
--- a/drivers/usb/usbssp/gadget-ring.c
+++ b/drivers/usb/usbssp/gadget-ring.c
@@ -10,10 +10,55 @@
* Origin: Copyright (C) 2008 Intel Corp
*/

+/*
+ * Ring initialization rules:
+ * 1. Each segment is initialized to zero, except for link TRBs.
+ * 2. Ring cycle state = 0. This represents Producer Cycle State (PCS) or
+ * Consumer Cycle State (CCS), depending on ring function.
+ * 3. Enqueue pointer = dequeue pointer = address of first TRB in the segment.
+ *
+ * Ring behavior rules:
+ * 1. A ring is empty if enqueue == dequeue. This means there will always be at
+ * least one free TRB in the ring. This is useful if you want to turn that
+ * into a link TRB and expand the ring.
+ * 2. When incrementing an enqueue or dequeue pointer, if the next TRB is a
+ * link TRB, then load the pointer with the address in the link TRB. If the
+ * link TRB had its toggle bit set, you may need to update the ring cycle
+ * state (see cycle bit rules). You may have to do this multiple times
+ * until you reach a non-link TRB.
+ * 3. A ring is full if enqueue++ (for the definition of increment above)
+ * equals the dequeue pointer.
+ *
+ * Cycle bit rules:
+ * 1. When a consumer increments a dequeue pointer and encounters a toggle bit
+ * in a link TRB, it must toggle the ring cycle state.
+ * 2. When a producer increments an enqueue pointer and encounters a toggle bit
+ * in a link TRB, it must toggle the ring cycle state.
+ *
+ * Producer rules:
+ * 1. Check if ring is full before you enqueue.
+ * 2. Write the ring cycle state to the cycle bit in the TRB you're enqueuing.
+ * Update enqueue pointer between each write (which may update the ring
+ * cycle state).
+ * 3. Notify consumer. If SW is producer, it rings the doorbell for command
+ * and endpoint rings. If DC is the producer for the event ring,
+ * and it generates an interrupt according to interrupt modulation rules.
+ *
+ * Consumer rules:
+ * 1. Check if TRB belongs to you. If the cycle bit == your ring cycle state,
+ * the TRB is owned by the consumer.
+ * 2. Update dequeue pointer (which may update the ring cycle state) and
+ * continue processing TRBs until you reach a TRB which is not owned by you.
+ * 3. Notify the producer. SW is the consumer for the event ring, and it
+ * updates event ring dequeue pointer. DC is the consumer for the command and
+ * endpoint rings; it generates events on the event ring for these.
+ */
+
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/irq.h>
+
#include "gadget-trace.h"
#include "gadget.h"

@@ -35,6 +80,146 @@ dma_addr_t usbssp_trb_virt_to_dma(struct usbssp_segment *seg,
return seg->dma + (segment_offset * sizeof(*trb));
}

+static bool trb_is_link(union usbssp_trb *trb)
+{
+ return TRB_TYPE_LINK_LE32(trb->link.control);
+}
+
+static bool last_trb_on_seg(struct usbssp_segment *seg, union usbssp_trb *trb)
+{
+ return trb == &seg->trbs[TRBS_PER_SEGMENT - 1];
+}
+
+static bool last_trb_on_ring(struct usbssp_ring *ring,
+ struct usbssp_segment *seg,
+ union usbssp_trb *trb)
+{
+ return last_trb_on_seg(seg, trb) && (seg->next == ring->first_seg);
+}
+
+static bool link_trb_toggles_cycle(union usbssp_trb *trb)
+{
+ return le32_to_cpu(trb->link.control) & LINK_TOGGLE;
+}
+
+/*
+ * See Cycle bit rules. SW is the consumer for the event ring only.
+ * Don't make a ring full of link TRBs. That would be dumb and this would loop.
+ */
+void inc_deq(struct usbssp_udc *usbssp_data, struct usbssp_ring *ring)
+{
+ /* event ring doesn't have link trbs, check for last trb */
+ if (ring->type == TYPE_EVENT) {
+ if (!last_trb_on_seg(ring->deq_seg, ring->dequeue)) {
+ ring->dequeue++;
+ goto out;
+ }
+ if (last_trb_on_ring(ring, ring->deq_seg, ring->dequeue))
+ ring->cycle_state ^= 1;
+ ring->deq_seg = ring->deq_seg->next;
+ ring->dequeue = ring->deq_seg->trbs;
+ goto out;
+ }
+
+ /* All other rings have link trbs */
+ if (!trb_is_link(ring->dequeue)) {
+ ring->dequeue++;
+ ring->num_trbs_free++;
+ }
+ while (trb_is_link(ring->dequeue)) {
+ ring->deq_seg = ring->deq_seg->next;
+ ring->dequeue = ring->deq_seg->trbs;
+ }
+out:
+ trace_usbssp_inc_deq(ring);
+}
+
+/*
+ * See Cycle bit rules. SW is the consumer for the event ring only.
+ * Don't make a ring full of link TRBs. That would be dumb and this would loop.
+ *
+ * If we've just enqueued a TRB that is in the middle of a TD (meaning the
+ * chain bit is set), then set the chain bit in all the following link TRBs.
+ * If we've enqueued the last TRB in a TD, make sure the following link TRBs
+ * have their chain bit cleared (so that each Link TRB is a separate TD).
+ *
+ * @more_trbs_coming: Will you enqueue more TRBs before calling
+ * prepare_transfer()?
+ */
+static void inc_enq(struct usbssp_udc *usbssp_data,
+ struct usbssp_ring *ring,
+ bool more_trbs_coming)
+{
+ u32 chain;
+ union usbssp_trb *next;
+
+ chain = le32_to_cpu(ring->enqueue->generic.field[3]) & TRB_CHAIN;
+ /* If this is not event ring, there is one less usable TRB */
+ if (!trb_is_link(ring->enqueue))
+ ring->num_trbs_free--;
+ next = ++(ring->enqueue);
+
+ /* Update the dequeue pointer further if that was a link TRB */
+ while (trb_is_link(next)) {
+
+ /*
+ * If the caller doesn't plan on enqueueing more TDs before
+ * ringing the doorbell, then we don't want to give the link TRB
+ * to the hardware just yet. We'll give the link TRB back in
+ * prepare_ring() just before we enqueue the TD at the top of
+ * the ring.
+ */
+ if (!chain && !more_trbs_coming)
+ break;
+
+ next->link.control &= cpu_to_le32(~TRB_CHAIN);
+ next->link.control |= cpu_to_le32(chain);
+
+ /* Give this link TRB to the hardware */
+ wmb();
+ next->link.control ^= cpu_to_le32(TRB_CYCLE);
+
+ /* Toggle the cycle bit after the last ring segment. */
+ if (link_trb_toggles_cycle(next))
+ ring->cycle_state ^= 1;
+
+ ring->enq_seg = ring->enq_seg->next;
+ ring->enqueue = ring->enq_seg->trbs;
+ next = ring->enqueue;
+ }
+ trace_usbssp_inc_enq(ring);
+}
+
+/*
+ * Check to see if there's room to enqueue num_trbs on the ring and make sure
+ * enqueue pointer will not advance into dequeue segment. See rules above.
+ */
+static inline int room_on_ring(struct usbssp_udc *usbssp_data,
+ struct usbssp_ring *ring,
+ unsigned int num_trbs)
+{
+ int num_trbs_in_deq_seg;
+
+ if (ring->num_trbs_free < num_trbs)
+ return 0;
+
+ if (ring->type != TYPE_COMMAND && ring->type != TYPE_EVENT) {
+ num_trbs_in_deq_seg = ring->dequeue - ring->deq_seg->trbs;
+
+ if (ring->num_trbs_free < num_trbs + num_trbs_in_deq_seg)
+ return 0;
+ }
+
+ return 1;
+}
+
+static bool usbssp_mod_cmd_timer(struct usbssp_udc *usbssp_data,
+ unsigned long delay)
+{
+ return mod_delayed_work(system_wq, &usbssp_data->cmd_timer, delay);
+ return 0;
+}
+
irqreturn_t usbssp_irq(int irq, void *priv)
{
struct usbssp_udc *usbssp_data = (struct usbssp_udc *)priv;
@@ -47,7 +232,6 @@ irqreturn_t usbssp_irq(int irq, void *priv)
return ret;
}

-
void usbssp_handle_command_timeout(struct work_struct *work)
{
/*TODO: implements function*/
@@ -149,3 +333,355 @@ struct usbssp_segment *usbssp_trb_in_td(struct usbssp_udc *usbssp_data,

return NULL;
}
+
+/**** Endpoint Ring Operations ****/
+
+/*
+ * Generic function for queueing a TRB on a ring.
+ * The caller must have checked to make sure there's room on the ring.
+ *
+ * @more_trbs_coming: Will you enqueue more TRBs before calling
+ * prepare_transfer()?
+ */
+static void queue_trb(struct usbssp_udc *usbssp_data, struct usbssp_ring *ring,
+ bool more_trbs_coming, u32 field1, u32 field2,
+ u32 field3, u32 field4)
+{
+ struct usbssp_generic_trb *trb;
+
+ trb = &ring->enqueue->generic;
+
+ dev_dbg(usbssp_data->dev, "Queue TRB at virt: %p, dma: %llx\n", trb,
+ usbssp_trb_virt_to_dma(ring->enq_seg, ring->enqueue));
+
+ trb->field[0] = cpu_to_le32(field1);
+ trb->field[1] = cpu_to_le32(field2);
+ trb->field[2] = cpu_to_le32(field3);
+ trb->field[3] = cpu_to_le32(field4);
+
+ trace_usbssp_queue_trb(ring, trb);
+ inc_enq(usbssp_data, ring, more_trbs_coming);
+}
+
+/*
+ * Does various checks on the endpoint ring, and makes it ready to
+ * queue num_trbs.
+ */
+static int prepare_ring(struct usbssp_udc *usbssp_data,
+ struct usbssp_ring *ep_ring,
+ u32 ep_state, unsigned
+ int num_trbs,
+ gfp_t mem_flags)
+{
+ unsigned int num_trbs_needed;
+
+ /* Make sure the endpoint has been added to USBSSP schedule */
+ switch (ep_state) {
+ case EP_STATE_DISABLED:
+ dev_warn(usbssp_data->dev,
+ "WARN request submitted to disabled ep\n");
+ return -ENOENT;
+ case EP_STATE_ERROR:
+ dev_warn(usbssp_data->dev,
+ "WARN waiting for error on ep to be cleared\n");
+ return -EINVAL;
+ case EP_STATE_HALTED:
+ dev_dbg(usbssp_data->dev,
+ "WARN halted endpoint, queueing request anyway.\n");
+ case EP_STATE_STOPPED:
+ case EP_STATE_RUNNING:
+ break;
+ default:
+ dev_err(usbssp_data->dev,
+ "ERROR unknown endpoint state for ep\n");
+ return -EINVAL;
+ }
+
+ while (1) {
+ if (room_on_ring(usbssp_data, ep_ring, num_trbs))
+ break;
+
+ if (ep_ring == usbssp_data->cmd_ring) {
+ dev_err(usbssp_data->dev,
+ "Do not support expand command ring\n");
+ return -ENOMEM;
+ }
+
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_ring_expansion,
+ "ERROR no room on ep ring, try ring expansion");
+
+ num_trbs_needed = num_trbs - ep_ring->num_trbs_free;
+ if (usbssp_ring_expansion(usbssp_data, ep_ring, num_trbs_needed,
+ mem_flags)) {
+ dev_err(usbssp_data->dev, "Ring expansion failed\n");
+ return -ENOMEM;
+ }
+ }
+
+ while (trb_is_link(ep_ring->enqueue)) {
+
+ ep_ring->enqueue->link.control |= cpu_to_le32(TRB_CHAIN);
+ wmb();
+ ep_ring->enqueue->link.control ^= cpu_to_le32(TRB_CYCLE);
+
+ /* Toggle the cycle bit after the last ring segment. */
+ if (link_trb_toggles_cycle(ep_ring->enqueue))
+ ep_ring->cycle_state ^= 1;
+ ep_ring->enq_seg = ep_ring->enq_seg->next;
+ ep_ring->enqueue = ep_ring->enq_seg->trbs;
+ }
+ return 0;
+}
+
+/**** Command Ring Operations ****/
+/*
+ * Generic function for queueing a command TRB on the command ring.
+ * Check to make sure there's room on the command ring for one command TRB.
+ * Also check that there's room reserved for commands that must not fail.
+ * If this is a command that must not fail, meaning command_must_succeed = TRUE,
+ * then only check for the number of reserved spots.
+ * Don't decrement usbssp_data->cmd_ring_reserved_trbs after we've queued the
+ * TRB because the command event handler may want to resubmit a failed command.
+ */
+static int queue_command(struct usbssp_udc *usbssp_data,
+ struct usbssp_command *cmd,
+ u32 field1, u32 field2,
+ u32 field3, u32 field4,
+ bool command_must_succeed)
+{
+ int reserved_trbs = usbssp_data->cmd_ring_reserved_trbs;
+ int ret;
+
+ if ((usbssp_data->usbssp_state & USBSSP_STATE_DYING) ||
+ (usbssp_data->usbssp_state & USBSSP_STATE_HALTED)) {
+ dev_dbg(usbssp_data->dev,
+ "USBSSP dying or halted, can't queue command\n");
+ return -ESHUTDOWN;
+ }
+
+ if (!command_must_succeed)
+ reserved_trbs++;
+
+ ret = prepare_ring(usbssp_data, usbssp_data->cmd_ring, EP_STATE_RUNNING,
+ reserved_trbs, GFP_ATOMIC);
+ if (ret < 0) {
+ dev_err(usbssp_data->dev,
+ "ERR: No room for command on command ring\n");
+ if (command_must_succeed)
+ dev_err(usbssp_data->dev,
+ "ERR: Reserved TRB counting for "
+ "unfailable commands failed.\n");
+ return ret;
+ }
+
+ cmd->command_trb = usbssp_data->cmd_ring->enqueue;
+
+ /* if there are no other commands queued we start the timeout timer */
+ if (list_empty(&usbssp_data->cmd_list)) {
+ usbssp_data->current_cmd = cmd;
+ usbssp_mod_cmd_timer(usbssp_data, USBSSP_CMD_DEFAULT_TIMEOUT);
+ }
+
+ list_add_tail(&cmd->cmd_list, &usbssp_data->cmd_list);
+
+ queue_trb(usbssp_data, usbssp_data->cmd_ring, false, field1, field2,
+ field3, field4 | usbssp_data->cmd_ring->cycle_state);
+ return 0;
+}
+
+/* Queue a slot enable or disable request on the command ring */
+int usbssp_queue_slot_control(struct usbssp_udc *usbssp_data,
+ struct usbssp_command *cmd,
+ u32 trb_type)
+{
+ return queue_command(usbssp_data, cmd, 0, 0, 0,
+ TRB_TYPE(trb_type) |
+ SLOT_ID_FOR_TRB(usbssp_data->slot_id), false);
+}
+
+/* Queue an address device command TRB */
+int usbssp_queue_address_device(struct usbssp_udc *usbssp_data,
+ struct usbssp_command *cmd,
+ dma_addr_t in_ctx_ptr,
+ enum usbssp_setup_dev setup)
+{
+ return queue_command(usbssp_data, cmd, lower_32_bits(in_ctx_ptr),
+ upper_32_bits(in_ctx_ptr), 0,
+ TRB_TYPE(TRB_ADDR_DEV) |
+ SLOT_ID_FOR_TRB(usbssp_data->slot_id)
+ | (setup == SETUP_CONTEXT_ONLY ? TRB_BSR : 0), false);
+}
+
+int usbssp_queue_vendor_command(struct usbssp_udc *usbssp_data,
+ struct usbssp_command *cmd,
+ u32 field1, u32 field2, u32 field3, u32 field4)
+{
+ return queue_command(usbssp_data, cmd, field1, field2, field3,
+ field4, false);
+}
+
+/* Queue a reset device command TRB */
+int usbssp_queue_reset_device(struct usbssp_udc *usbssp_data,
+ struct usbssp_command *cmd)
+{
+ return queue_command(usbssp_data, cmd, 0, 0, 0,
+ TRB_TYPE(TRB_RESET_DEV) |
+ SLOT_ID_FOR_TRB(usbssp_data->slot_id),
+ false);
+}
+
+/* Queue a configure endpoint command TRB */
+int usbssp_queue_configure_endpoint(struct usbssp_udc *usbssp_data,
+ struct usbssp_command *cmd,
+ dma_addr_t in_ctx_ptr,
+ bool command_must_succeed)
+{
+ return queue_command(usbssp_data, cmd, lower_32_bits(in_ctx_ptr),
+ upper_32_bits(in_ctx_ptr), 0,
+ TRB_TYPE(TRB_CONFIG_EP) |
+ SLOT_ID_FOR_TRB(usbssp_data->slot_id),
+ command_must_succeed);
+}
+
+/* Queue an evaluate context command TRB */
+int usbssp_queue_evaluate_context(struct usbssp_udc *usbssp_data,
+ struct usbssp_command *cmd,
+ dma_addr_t in_ctx_ptr,
+ bool command_must_succeed)
+{
+ return queue_command(usbssp_data, cmd, lower_32_bits(in_ctx_ptr),
+ upper_32_bits(in_ctx_ptr), 0,
+ TRB_TYPE(TRB_EVAL_CONTEXT) |
+ SLOT_ID_FOR_TRB(usbssp_data->slot_id),
+ command_must_succeed);
+}
+
+/*
+ * Suspend is set to indicate "Stop Endpoint Command" is being issued to stop
+ * activity on an endpoint that is about to be suspended.
+ */
+int usbssp_queue_stop_endpoint(struct usbssp_udc *usbssp_data,
+ struct usbssp_command *cmd,
+ unsigned int ep_index, int suspend)
+{
+ u32 trb_slot_id = SLOT_ID_FOR_TRB(usbssp_data->slot_id);
+ u32 trb_ep_index = EP_ID_FOR_TRB(ep_index);
+ u32 type = TRB_TYPE(TRB_STOP_RING);
+ u32 trb_suspend = SUSPEND_PORT_FOR_TRB(suspend);
+
+ return queue_command(usbssp_data, cmd, 0, 0, 0,
+ trb_slot_id | trb_ep_index | type | trb_suspend, false);
+}
+
+/* Set Transfer Ring Dequeue Pointer command */
+void usbssp_queue_new_dequeue_state(struct usbssp_udc *usbssp_data,
+ unsigned int ep_index,
+ struct usbssp_dequeue_state *deq_state)
+{
+ dma_addr_t addr;
+ u32 trb_slot_id = SLOT_ID_FOR_TRB(usbssp_data->slot_id);
+ u32 trb_ep_index = EP_ID_FOR_TRB(ep_index);
+ u32 trb_stream_id = STREAM_ID_FOR_TRB(deq_state->stream_id);
+ u32 trb_sct = 0;
+ u32 type = TRB_TYPE(TRB_SET_DEQ);
+ struct usbssp_ep *ep_priv;
+ struct usbssp_command *cmd;
+ int ret;
+
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_cancel_request,
+ "Set TR Deq Ptr cmd, new deq seg = %p (0x%llx dma), "
+ "new deq ptr = %p (0x%llx dma), new cycle = %u",
+ deq_state->new_deq_seg,
+ (unsigned long long)deq_state->new_deq_seg->dma,
+ deq_state->new_deq_ptr,
+ (unsigned long long)usbssp_trb_virt_to_dma(
+ deq_state->new_deq_seg, deq_state->new_deq_ptr),
+ deq_state->new_cycle_state);
+
+ addr = usbssp_trb_virt_to_dma(deq_state->new_deq_seg,
+ deq_state->new_deq_ptr);
+ if (addr == 0) {
+ dev_warn(usbssp_data->dev, "WARN Cannot submit Set TR Deq Ptr\n");
+ dev_warn(usbssp_data->dev, "WARN deq seg = %p, deq pt = %p\n",
+ deq_state->new_deq_seg, deq_state->new_deq_ptr);
+ return;
+ }
+ ep_priv = &usbssp_data->devs.eps[ep_index];
+ if ((ep_priv->ep_state & SET_DEQ_PENDING)) {
+ dev_warn(usbssp_data->dev, "WARN Cannot submit Set TR Deq Ptr\n");
+ dev_warn(usbssp_data->dev,
+ "A Set TR Deq Ptr command is pending.\n");
+ return;
+ }
+
+ /* This function gets called from contexts where it cannot sleep */
+ cmd = usbssp_alloc_command(usbssp_data, false, GFP_ATOMIC);
+ if (!cmd) {
+ dev_warn(usbssp_data->dev,
+ "WARN Cannot submit Set TR Deq Ptr: ENOMEM\n");
+ return;
+ }
+
+ ep_priv->queued_deq_seg = deq_state->new_deq_seg;
+ ep_priv->queued_deq_ptr = deq_state->new_deq_ptr;
+ if (deq_state->stream_id)
+ trb_sct = SCT_FOR_TRB(SCT_PRI_TR);
+ ret = queue_command(usbssp_data, cmd,
+ lower_32_bits(addr) | trb_sct | deq_state->new_cycle_state,
+ upper_32_bits(addr), trb_stream_id,
+ trb_slot_id | trb_ep_index | type, false);
+ if (ret < 0) {
+ usbssp_free_command(usbssp_data, cmd);
+ return;
+ }
+
+ /*
+ * Stop the TD queueing code from ringing the doorbell until
+ * this command completes. The DC won't set the dequeue pointer
+ * if the ring is running, and ringing the doorbell starts the
+ * ring running.
+ */
+ ep_priv->ep_state |= SET_DEQ_PENDING;
+}
+
+int usbssp_queue_reset_ep(struct usbssp_udc *usbssp_data,
+ struct usbssp_command *cmd,
+ unsigned int ep_index,
+ enum usbssp_ep_reset_type reset_type)
+{
+ u32 trb_slot_id = SLOT_ID_FOR_TRB(usbssp_data->slot_id);
+ u32 trb_ep_index = EP_ID_FOR_TRB(ep_index);
+ u32 type = TRB_TYPE(TRB_RESET_EP);
+
+ if (reset_type == EP_SOFT_RESET)
+ type |= TRB_TSP;
+
+ return queue_command(usbssp_data, cmd, 0, 0, 0,
+ trb_slot_id | trb_ep_index | type, false);
+}
+
+/*
+ * Queue an NOP command TRB
+ */
+int usbssp_queue_nop(struct usbssp_udc *usbssp_data,
+ struct usbssp_command *cmd)
+{
+ return queue_command(usbssp_data, cmd, 0, 0, 0,
+ TRB_TYPE(TRB_CMD_NOOP), false);
+}
+
+/*
+ * Queue a halt endpoint request on the command ring
+ */
+int usbssp_queue_halt_endpoint(struct usbssp_udc *usbssp_data,
+ struct usbssp_command *cmd,
+ unsigned int ep_index)
+{
+ u32 trb_slot_id = SLOT_ID_FOR_TRB(usbssp_data->slot_id);
+ u32 trb_ep_index = EP_ID_FOR_TRB(ep_index);
+
+ return queue_command(usbssp_data, cmd, 0, 0, 0,
+ TRB_TYPE(TRB_HALT_ENDPOINT) | trb_slot_id |
+ trb_ep_index, false);
+}
+
diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h
index 927c34579899..0970c7ddcd38 100644
--- a/drivers/usb/usbssp/gadget.h
+++ b/drivers/usb/usbssp/gadget.h
@@ -1685,7 +1685,13 @@ void usbssp_dbg_trace(struct usbssp_udc *usbssp_data,
/* USBSSP memory management */
void usbssp_mem_cleanup(struct usbssp_udc *usbssp_data);
int usbssp_mem_init(struct usbssp_udc *usbssp_data, gfp_t flags);
+int usbssp_ring_expansion(struct usbssp_udc *usbssp_data,
+ struct usbssp_ring *ring,
+ unsigned int num_trbs, gfp_t flags);

+struct usbssp_command *usbssp_alloc_command(struct usbssp_udc *usbssp_data,
+ bool allocate_completion,
+ gfp_t mem_flags);
void usbssp_free_command(struct usbssp_udc *usbssp_data,
struct usbssp_command *command);

--
2.17.1


2018-07-19 18:01:54

by Pawel Laszczak

[permalink] [raw]
Subject: [PATCH 07/31] usb: usbssp: Initialization - added usbssp_mem_init

Patch adds some stuff regarding initialization of
usbssp driver.

It introduces new file gadget-mem.c which contains
functions responsible for allocating and freeing object
used in driver. It will be gradually expended with
new functionality.

To limits the size of this patch not all functions are
implemented - some of them are empty end will be implemented
in next patch.

Signed-off-by: Pawel Laszczak <[email protected]>
---
drivers/usb/usbssp/Makefile | 2 +-
drivers/usb/usbssp/gadget-ext-caps.h | 46 ++
drivers/usb/usbssp/gadget-mem.c | 622 +++++++++++++++++++++++++++
drivers/usb/usbssp/gadget-ring.c | 6 +
drivers/usb/usbssp/gadget.c | 3 +-
drivers/usb/usbssp/gadget.h | 5 +
6 files changed, 681 insertions(+), 3 deletions(-)
create mode 100644 drivers/usb/usbssp/gadget-mem.c

diff --git a/drivers/usb/usbssp/Makefile b/drivers/usb/usbssp/Makefile
index 0606f3c63cd0..4f5f12ab6420 100644
--- a/drivers/usb/usbssp/Makefile
+++ b/drivers/usb/usbssp/Makefile
@@ -4,7 +4,7 @@ CFLAGS_gadget-trace.o := -I$(src)

obj-$(CONFIG_USB_USBSSP_GADGET) += usbssp.o
usbssp-y := usbssp-plat.o gadget-ring.o \
- gadget.o
+ gadget.o gadget-mem.o \
gadget-dbg.o

ifneq ($(CONFIG_TRACING),)
diff --git a/drivers/usb/usbssp/gadget-ext-caps.h b/drivers/usb/usbssp/gadget-ext-caps.h
index 86c0ce331037..478cb0b6a098 100644
--- a/drivers/usb/usbssp/gadget-ext-caps.h
+++ b/drivers/usb/usbssp/gadget-ext-caps.h
@@ -54,3 +54,49 @@

/* true: Controller Not Ready to accept doorbell or op reg writes after reset */
#define USBSSP_STS_CNR BIT(11)
+
+#include <linux/io.h>
+
+/**
+ * Find the offset of the extended capabilities with capability ID id.
+ *
+ * @base PCI MMIO registers base address.
+ * @start address at which to start looking, (0 or HCC_PARAMS to start at
+ * beginning of list)
+ * @id Extended capability ID to search for.
+ *
+ * Returns the offset of the next matching extended capability structure.
+ * Some capabilities can occur several times,
+ * e.g., the USBSSP_EXT_CAPS_PROTOCOL, and this provides a way to find them all.
+ */
+
+static inline int usbssp_find_next_ext_cap(void __iomem *base,
+ u32 start, int id)
+{
+ u32 val;
+ u32 next;
+ u32 offset;
+
+ offset = start;
+ if (!start || start == USBSSP_HCC_PARAMS_OFFSET) {
+ val = readl(base + USBSSP_HCC_PARAMS_OFFSET);
+ if (val == ~0)
+ return 0;
+ offset = USBSSP_HCC_EXT_CAPS(val) << 2;
+ if (!offset)
+ return 0;
+ };
+
+ do {
+ val = readl(base + offset);
+ if (val == ~0)
+ return 0;
+ if (USBSSP_EXT_CAPS_ID(val) == id && offset != start)
+ return offset;
+
+ next = USBSSP_EXT_CAPS_NEXT(val);
+ offset += next << 2;
+ } while (next);
+
+ return 0;
+}
diff --git a/drivers/usb/usbssp/gadget-mem.c b/drivers/usb/usbssp/gadget-mem.c
new file mode 100644
index 000000000000..11bbf18dacba
--- /dev/null
+++ b/drivers/usb/usbssp/gadget-mem.c
@@ -0,0 +1,622 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USBSSP device controller driver
+ *
+ * Copyright (C) 2018 Cadence.
+ *
+ * Author: Pawel Laszczak
+ *
+ * A lot of code based on Linux XHCI driver.
+ * Origin: Copyright (C) 2008 Intel Corp.
+ */
+
+#include <linux/usb.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/dmapool.h>
+#include <linux/dma-mapping.h>
+#include "gadget.h"
+#include "gadget-trace.h"
+
+/**
+ * Create a new ring with zero or more segments.
+ *
+ * Link each segment together into a ring.
+ * Set the end flag and the cycle toggle bit on the last segment.
+ * See section 4.9.1 and figures 15 and 16.
+ */
+static struct usbssp_ring *usbssp_ring_alloc(struct usbssp_udc *usbssp_data,
+ unsigned int num_segs,
+ unsigned int cycle_state,
+ enum usbssp_ring_type type,
+ unsigned int max_packet,
+ gfp_t flags)
+{
+ /*TODO: implements function*/
+ return NULL;
+}
+
+struct usbssp_container_ctx *usbssp_alloc_container_ctx(
+ struct usbssp_udc *usbssp_data,
+ int type, gfp_t flags)
+{
+ struct usbssp_container_ctx *ctx;
+
+ if ((type != USBSSP_CTX_TYPE_DEVICE) && (type != USBSSP_CTX_TYPE_INPUT))
+ return NULL;
+
+ ctx = kzalloc(sizeof(*ctx), flags);
+ if (!ctx)
+ return NULL;
+
+ ctx->type = type;
+ ctx->size = HCC_64BYTE_CONTEXT(usbssp_data->hcc_params) ? 2048 : 1024;
+ if (type == USBSSP_CTX_TYPE_INPUT)
+ ctx->size += CTX_SIZE(usbssp_data->hcc_params);
+
+ ctx->bytes = dma_pool_zalloc(usbssp_data->device_pool,
+ flags, &ctx->dma);
+
+ if (!ctx->bytes) {
+ kfree(ctx);
+ return NULL;
+ }
+ return ctx;
+}
+
+struct usbssp_command *usbssp_alloc_command(struct usbssp_udc *usbssp_data,
+ bool allocate_completion,
+ gfp_t mem_flags)
+{
+ struct usbssp_command *command;
+
+ command = kzalloc(sizeof(*command), mem_flags);
+ if (!command)
+ return NULL;
+
+ if (allocate_completion) {
+ command->completion =
+ kzalloc(sizeof(struct completion), mem_flags);
+ if (!command->completion) {
+ kfree(command);
+ return NULL;
+ }
+ init_completion(command->completion);
+ }
+
+ command->status = 0;
+ INIT_LIST_HEAD(&command->cmd_list);
+
+ return command;
+}
+
+struct usbssp_command *usbssp_alloc_command_with_ctx(
+ struct usbssp_udc *usbssp_data,
+ bool allocate_completion,
+ gfp_t mem_flags)
+{
+ struct usbssp_command *command;
+
+ command = usbssp_alloc_command(usbssp_data,
+ allocate_completion, mem_flags);
+ if (!command)
+ return NULL;
+
+ command->in_ctx = usbssp_alloc_container_ctx(usbssp_data,
+ USBSSP_CTX_TYPE_INPUT, mem_flags);
+ if (!command->in_ctx) {
+ kfree(command->completion);
+ kfree(command);
+ return NULL;
+ }
+ return command;
+}
+
+void usbssp_request_free_priv(struct usbssp_request *priv_req)
+{
+ if (priv_req)
+ kfree(priv_req->td);
+}
+
+
+int usbssp_alloc_erst(struct usbssp_udc *usbssp_data,
+ struct usbssp_ring *evt_ring,
+ struct usbssp_erst *erst,
+ gfp_t flags)
+{
+ size_t size;
+ unsigned int val;
+ struct usbssp_segment *seg;
+ struct usbssp_erst_entry *entry;
+
+ size = sizeof(struct usbssp_erst_entry) * evt_ring->num_segs;
+ erst->entries = dma_zalloc_coherent(usbssp_data->dev,
+ size, &erst->erst_dma_addr, flags);
+ if (!erst->entries)
+ return -ENOMEM;
+
+ erst->num_entries = evt_ring->num_segs;
+
+ seg = evt_ring->first_seg;
+ for (val = 0; val < evt_ring->num_segs; val++) {
+ entry = &erst->entries[val];
+ entry->seg_addr = cpu_to_le64(seg->dma);
+ entry->seg_size = cpu_to_le32(TRBS_PER_SEGMENT);
+ entry->rsvd = 0;
+ seg = seg->next;
+ }
+
+ return 0;
+}
+
+void usbssp_mem_cleanup(struct usbssp_udc *usbssp_data)
+{
+ /*TODO: implements functions*/
+}
+
+
+static void usbssp_set_event_deq(struct usbssp_udc *usbssp_data)
+{
+ u64 temp;
+ dma_addr_t deq;
+
+ deq = usbssp_trb_virt_to_dma(usbssp_data->event_ring->deq_seg,
+ usbssp_data->event_ring->dequeue);
+ if (deq == 0 && !in_interrupt())
+ dev_warn(usbssp_data->dev,
+ "WARN something wrong with SW event ring dequeue ptr.\n");
+
+ /* Update USBSSP event ring dequeue pointer */
+ temp = usbssp_read_64(usbssp_data, &usbssp_data->ir_set->erst_dequeue);
+ temp &= ERST_PTR_MASK;
+ /*
+ * Don't clear the EHB bit (which is RW1C) because
+ * there might be more events to service.
+ */
+ temp &= ~ERST_EHB;
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
+ "// Write event ring dequeue pointer, preserving EHB bit");
+ usbssp_write_64(usbssp_data, ((u64) deq & (u64) ~ERST_PTR_MASK) | temp,
+ &usbssp_data->ir_set->erst_dequeue);
+}
+
+static void usbssp_add_in_port(struct usbssp_udc *usbssp_data,
+ unsigned int num_ports,
+ __le32 __iomem *addr,
+ int max_caps)
+{
+ u32 temp, port_offset, port_count;
+ int i;
+ u8 major_revision;
+ struct usbssp_ports *rport;
+
+ temp = readl(addr);
+ major_revision = USBSSP_EXT_PORT_MAJOR(temp);
+
+ if (major_revision == 0x03) {
+ rport = &usbssp_data->usb3_rhub;
+ } else if (major_revision <= 0x02) {
+ rport = &usbssp_data->usb2_rhub;
+ } else {
+ dev_warn(usbssp_data->dev, "Ignoring unknown port speed, "
+ "Ext Cap %p, revision = 0x%x\n",
+ addr, major_revision);
+ /* Ignoring port protocol we can't understand. */
+ return;
+ }
+
+ rport->maj_rev = USBSSP_EXT_PORT_MAJOR(temp);
+ rport->min_rev = USBSSP_EXT_PORT_MINOR(temp);
+
+ /* Port offset and count in the third dword.*/
+ temp = readl(addr + 2);
+ port_offset = USBSSP_EXT_PORT_OFF(temp);
+ port_count = USBSSP_EXT_PORT_COUNT(temp);
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
+ "Ext Cap %p, port offset = %u, count = %u, revision = 0x%x",
+ addr, port_offset, port_count, major_revision);
+
+ if (port_count > 1) {
+ dev_warn(usbssp_data->dev,
+ "DC support only single port but it detect %d ports",
+ port_count);
+ port_count = 1;
+ }
+
+ /* Port count includes the current port offset */
+ if (port_offset == 0 || (port_offset + port_count - 1) > num_ports)
+ return;
+
+ rport->psi_count = USBSSP_EXT_PORT_PSIC(temp);
+ if (rport->psi_count) {
+ rport->psi = kcalloc(rport->psi_count, sizeof(*rport->psi),
+ GFP_KERNEL);
+ if (!rport->psi)
+ rport->psi_count = 0;
+
+ rport->psi_uid_count++;
+ for (i = 0; i < rport->psi_count; i++) {
+ rport->psi[i] = readl(addr + 4 + i);
+
+ /*
+ * count unique ID values, two consecutive entries can
+ * have the same ID if link is assymetric
+ */
+ if (i && (USBSSP_EXT_PORT_PSIV(rport->psi[i]) !=
+ USBSSP_EXT_PORT_PSIV(rport->psi[i - 1])))
+ rport->psi_uid_count++;
+
+ dev_dbg(usbssp_data->dev,
+ "PSIV:%d PSIE:%d PLT:%d PFD:%d LP:%d PSIM:%d\n",
+ USBSSP_EXT_PORT_PSIV(rport->psi[i]),
+ USBSSP_EXT_PORT_PSIE(rport->psi[i]),
+ USBSSP_EXT_PORT_PLT(rport->psi[i]),
+ USBSSP_EXT_PORT_PFD(rport->psi[i]),
+ USBSSP_EXT_PORT_LP(rport->psi[i]),
+ USBSSP_EXT_PORT_PSIM(rport->psi[i]));
+ }
+ }
+
+ /* cache usb2 port capabilities */
+ if (major_revision < 0x03 && usbssp_data->num_ext_caps < max_caps)
+ usbssp_data->ext_caps[usbssp_data->num_ext_caps++] = temp;
+
+ if (major_revision != 0x03) {
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
+ "USBSSP: support USB2 software lpm");
+ usbssp_data->sw_lpm_support = 1;
+ if (temp & USBSSP_HLC) {
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
+ "USBSSP: support USB2 hardware lpm");
+ usbssp_data->hw_lpm_support = 1;
+ }
+ }
+
+ usbssp_data->port_array[port_offset-1] = major_revision;
+ if (major_revision == 0x03)
+ usbssp_data->num_usb3_ports++;
+ else
+ usbssp_data->num_usb2_ports++;
+}
+
+/*
+ * Scan the Extended Capabilities for the "Supported Protocol Capabilities" that
+ * specify what speeds each port is supposed to be.
+ */
+static int usbssp_setup_port_arrays(struct usbssp_udc *usbssp_data, gfp_t flags)
+{
+ void __iomem *base;
+ u32 offset;
+ u32 port3offset = 0;
+ u32 port2offset = 0;
+ unsigned int num_ports;
+ int i;
+ int cap_count = 0;
+ u32 cap_start;
+
+ num_ports = HCS_MAX_PORTS(usbssp_data->hcs_params1);
+
+ /*
+ * USBSSP can support only two ports - one for USB2.0 and
+ * second for USB3.0
+ */
+ if (num_ports > MAX_USBSSP_PORTS) {
+ dev_err(usbssp_data->dev,
+ "USBSSP-Dev can't support more then %d ports\n",
+ MAX_USBSSP_PORTS);
+ return -EINVAL;
+ }
+
+ usbssp_data->port_array =
+ kzalloc(sizeof(*usbssp_data->port_array)*num_ports, flags);
+ if (!usbssp_data->port_array)
+ return -ENOMEM;
+
+ base = &usbssp_data->cap_regs->hc_capbase;
+
+ cap_start = usbssp_find_next_ext_cap(base, 0, USBSSP_EXT_CAPS_PROTOCOL);
+ if (!cap_start) {
+ dev_err(usbssp_data->dev,
+ "No Ext. Cap. registers, unable to set up ports\n");
+ return -ENODEV;
+ }
+
+ offset = cap_start;
+
+ /* count extended protocol capability entries for later caching */
+ while (offset) {
+ u32 temp;
+ u8 major_revision;
+
+ temp = readl(base + offset);
+ major_revision = USBSSP_EXT_PORT_MAJOR(temp);
+
+ if (major_revision == 0x03 && port3offset == 0)
+ port3offset = offset;
+ else if (major_revision <= 0x02 && port2offset == 0)
+ port2offset = offset;
+
+ cap_count++;
+
+ offset = usbssp_find_next_ext_cap(base, offset,
+ USBSSP_EXT_CAPS_PROTOCOL);
+ }
+
+ if (cap_count > MAX_USBSSP_PORTS) {
+ dev_err(usbssp_data->dev, "Too many Ext. Cap. registers\n");
+ return -EINVAL;
+ }
+
+ if (!port3offset && !port2offset) {
+ dev_warn(usbssp_data->dev, "No ports on the USBSSP?\n");
+ return -ENODEV;
+ }
+
+ usbssp_data->ext_caps =
+ kzalloc(sizeof(*usbssp_data->ext_caps) * cap_count, flags);
+ if (!usbssp_data->ext_caps)
+ return -ENOMEM;
+
+ /** if exist add USB3 port*/
+ if (port3offset)
+ usbssp_add_in_port(usbssp_data, num_ports,
+ base + port3offset, cap_count);
+
+ /** add USB2 port*/
+ if (port2offset)
+ usbssp_add_in_port(usbssp_data, num_ports,
+ base + port2offset, cap_count);
+
+ if (usbssp_data->num_usb2_ports == 0 &&
+ usbssp_data->num_usb3_ports == 0) {
+ dev_warn(usbssp_data->dev, "No ports on the USBSSP?\n");
+ return -ENODEV;
+ }
+
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
+ "Found %u USB 2.0 ports and %u USB 3.0 ports.",
+ usbssp_data->num_usb2_ports,
+ usbssp_data->num_usb3_ports);
+
+ /*Only one port USB3.0 and USB2.0 can be supported by USBSSP_DEV*/
+ if (usbssp_data->num_usb3_ports > 1) {
+ dev_err(usbssp_data->dev, "Limiting USB 3.0 ports to 1\n");
+ return -EINVAL;
+ }
+
+ if (usbssp_data->num_usb2_ports > 1) {
+ dev_err(usbssp_data->dev, "Limiting USB 2.0 ports to 1\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Note we could have only USB 3.0 ports, or USB 2.0 ports.
+ */
+ if (usbssp_data->num_usb2_ports) {
+ for (i = 0; i < num_ports; i++) {
+
+ if (usbssp_data->port_array[i] == 0x03)
+ continue;
+ usbssp_data->usb2_ports =
+ &usbssp_data->op_regs->port_status_base +
+ NUM_PORT_REGS*i;
+
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
+ "USB 2.0 port at index %u, addr = %p",
+ i, usbssp_data->usb2_ports);
+ }
+ }
+
+ if (usbssp_data->num_usb3_ports) {
+
+ for (i = 0; i < num_ports; i++)
+ if (usbssp_data->port_array[i] == 0x03) {
+ usbssp_data->usb3_ports =
+ &usbssp_data->op_regs->port_status_base +
+ NUM_PORT_REGS*i;
+
+ usbssp_dbg_trace(usbssp_data,
+ trace_usbssp_dbg_init,
+ "USB 3.0 port at index %u, addr = %p",
+ i, usbssp_data->usb3_ports);
+ }
+ }
+
+ return 0;
+}
+
+int usbssp_mem_init(struct usbssp_udc *usbssp_data, gfp_t flags)
+{
+ dma_addr_t dma;
+ struct device *dev = usbssp_data->dev;
+ unsigned int val, val2;
+ u64 val_64;
+ u32 page_size;
+ int i, ret;
+
+ INIT_LIST_HEAD(&usbssp_data->cmd_list);
+
+ /* init command timeout work */
+ INIT_DELAYED_WORK(&usbssp_data->cmd_timer,
+ usbssp_handle_command_timeout);
+ init_completion(&usbssp_data->cmd_ring_stop_completion);
+
+ page_size = readl(&usbssp_data->op_regs->page_size);
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
+ "Supported page size register = 0x%x", page_size);
+ for (i = 0; i < 16; i++) {
+ if ((0x1 & page_size) != 0)
+ break;
+ page_size = page_size >> 1;
+ }
+ if (i < 16)
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
+ "Supported page size of %iK", (1 << (i+12)) / 1024);
+ else
+ dev_warn(usbssp_data->dev, "WARN: no supported page size\n");
+
+ /*
+ * Use 4K pages, since that's common and the minimum the
+ * USBSSP supports
+ */
+ usbssp_data->page_shift = 12;
+ usbssp_data->page_size = 1 << usbssp_data->page_shift;
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
+ "USBSSP page size set to %iK",
+ usbssp_data->page_size / 1024);
+
+ /* In device mode this value should be equal 1*/
+ val = DEV_HCS_MAX_SLOTS(readl(&usbssp_data->cap_regs->hcs_params1));
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
+ "// USBSSP can handle at most %d device slots.", val);
+
+ /*device should have only 1 slot*/
+ if (val > DEV_MAX_SLOTS)
+ pr_err("Invalid number of supported slots");
+
+ val2 = readl(&usbssp_data->op_regs->config_reg);
+ val |= (val2 & ~DEV_HCS_SLOTS_MASK);
+
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
+ "// Setting Max device slots reg = 0x%x.", val);
+ writel(val, &usbssp_data->op_regs->config_reg);
+
+ /*
+ * Doorbell array must be physically contiguous
+ * and 64-byte (cache line) aligned.
+ */
+ usbssp_data->dcbaa = dma_alloc_coherent(dev,
+ sizeof(*usbssp_data->dcbaa), &dma, GFP_KERNEL);
+ if (!usbssp_data->dcbaa)
+ goto fail;
+ memset(usbssp_data->dcbaa, 0, sizeof *(usbssp_data->dcbaa));
+ usbssp_data->dcbaa->dma = dma;
+
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
+ "// DCBA array address = 0x%llx (DMA), %p (virt)",
+ (unsigned long long)usbssp_data->dcbaa->dma,
+ usbssp_data->dcbaa);
+ usbssp_write_64(usbssp_data, dma, &usbssp_data->op_regs->dcbaa_ptr);
+
+ /*
+ * Initialize the ring segment pool. The ring must be a contiguous
+ * structure comprised of TRBs. The TRBs must be 16 byte aligned,
+ * however, the command ring segment needs 64-byte aligned segments
+ * and our use of dma addresses in the trb_address_map radix tree needs
+ * TRB_SEGMENT_SIZE alignment, so we pick the greater alignment need.
+ */
+ usbssp_data->segment_pool = dma_pool_create("USBSSP ring segments", dev,
+ TRB_SEGMENT_SIZE, TRB_SEGMENT_SIZE,
+ usbssp_data->page_size);
+
+ usbssp_data->device_pool =
+ dma_pool_create("USBSSP input/output contexts", dev,
+ 2112, 64, usbssp_data->page_size);
+ if (!usbssp_data->segment_pool || !usbssp_data->device_pool)
+ goto fail;
+
+ /*
+ * Linear stream context arrays don't have any boundary restrictions,
+ * and only need to be 16-byte aligned.
+ */
+ usbssp_data->small_streams_pool =
+ dma_pool_create("USBSSP 256 byte stream ctx arrays",
+ dev, SMALL_STREAM_ARRAY_SIZE, 16, 0);
+ usbssp_data->medium_streams_pool =
+ dma_pool_create("USBSSP 1KB stream ctx arrays",
+ dev, MEDIUM_STREAM_ARRAY_SIZE, 16, 0);
+
+ /*
+ * Any stream context array bigger than MEDIUM_STREAM_ARRAY_SIZE
+ * will be allocated with dma_alloc_coherent()
+ */
+ if (!usbssp_data->small_streams_pool ||
+ !usbssp_data->medium_streams_pool)
+ goto fail;
+
+ /* Set up the command ring to have one segments for now. */
+ usbssp_data->cmd_ring = usbssp_ring_alloc(usbssp_data, 1, 1,
+ TYPE_COMMAND, 0, flags);
+ if (!usbssp_data->cmd_ring)
+ goto fail;
+
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
+ "Allocated command ring at %p", usbssp_data->cmd_ring);
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
+ "First segment DMA is 0x%llx",
+ (unsigned long long)usbssp_data->cmd_ring->first_seg->dma);
+
+ /* Set the address in the Command Ring Control register */
+ val_64 = usbssp_read_64(usbssp_data, &usbssp_data->op_regs->cmd_ring);
+ val_64 = (val_64 & (u64) CMD_RING_RSVD_BITS) |
+ (usbssp_data->cmd_ring->first_seg->dma & (u64) ~CMD_RING_RSVD_BITS) |
+ usbssp_data->cmd_ring->cycle_state;
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
+ "// Setting command ring address to 0x%x", val_64);
+ usbssp_write_64(usbssp_data, val_64, &usbssp_data->op_regs->cmd_ring);
+
+ val = readl(&usbssp_data->cap_regs->db_off);
+ val &= DBOFF_MASK;
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
+ "// Doorbell array is located at offset 0x%x"
+ " from cap regs base addr", val);
+ usbssp_data->dba = (void __iomem *) usbssp_data->cap_regs + val;
+
+ /* Set ir_set to interrupt register set 0 */
+ usbssp_data->ir_set = &usbssp_data->run_regs->ir_set[0];
+
+ /*
+ * Event ring setup: Allocate a normal ring, but also setup
+ * the event ring segment table (ERST).
+ */
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
+ "// Allocating event ring");
+ usbssp_data->event_ring = usbssp_ring_alloc(usbssp_data,
+ ERST_NUM_SEGS, 1, TYPE_EVENT, 0, flags);
+ if (!usbssp_data->event_ring)
+ goto fail;
+
+ ret = usbssp_alloc_erst(usbssp_data, usbssp_data->event_ring,
+ &usbssp_data->erst, flags);
+ if (ret)
+ goto fail;
+
+ /* set ERST count with the number of entries in the segment table */
+ val = readl(&usbssp_data->ir_set->erst_size);
+ val &= ERST_SIZE_MASK;
+ val |= ERST_NUM_SEGS;
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
+ "// Write ERST size = %i to ir_set 0 (some bits preserved)",
+ val);
+ writel(val, &usbssp_data->ir_set->erst_size);
+
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
+ "// Set ERST entries to point to event ring.");
+
+ /* set the segment table base address */
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
+ "// Set ERST base address for ir_set 0 = 0x%llx",
+ (unsigned long long)usbssp_data->erst.erst_dma_addr);
+ val_64 = usbssp_read_64(usbssp_data, &usbssp_data->ir_set->erst_base);
+ val_64 &= ERST_PTR_MASK;
+ val_64 |= (usbssp_data->erst.erst_dma_addr & (u64) ~ERST_PTR_MASK);
+ usbssp_write_64(usbssp_data, val_64, &usbssp_data->ir_set->erst_base);
+
+ /* Set the event ring dequeue address */
+ usbssp_set_event_deq(usbssp_data);
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
+ "Wrote ERST address to ir_set 0.");
+
+ if (usbssp_setup_port_arrays(usbssp_data, flags))
+ goto fail;
+
+ return 0;
+
+fail:
+ dev_warn(usbssp_data->dev, "Couldn't initialize memory\n");
+ usbssp_halt(usbssp_data);
+ usbssp_reset(usbssp_data);
+ usbssp_mem_cleanup(usbssp_data);
+ return -ENOMEM;
+}
+
diff --git a/drivers/usb/usbssp/gadget-ring.c b/drivers/usb/usbssp/gadget-ring.c
index d1da59306d02..69cf478c222b 100644
--- a/drivers/usb/usbssp/gadget-ring.c
+++ b/drivers/usb/usbssp/gadget-ring.c
@@ -46,3 +46,9 @@ irqreturn_t usbssp_irq(int irq, void *priv)
spin_unlock_irqrestore(&usbssp_data->lock, flags);
return ret;
}
+
+
+void usbssp_handle_command_timeout(struct work_struct *work)
+{
+ /*TODO: implements function*/
+}
diff --git a/drivers/usb/usbssp/gadget.c b/drivers/usb/usbssp/gadget.c
index 195f5777cf8a..534fb75740f7 100644
--- a/drivers/usb/usbssp/gadget.c
+++ b/drivers/usb/usbssp/gadget.c
@@ -195,8 +195,7 @@ int usbssp_init(struct usbssp_udc *usbssp_data)
spin_lock_init(&usbssp_data->lock);
spin_lock_init(&usbssp_data->irq_thread_lock);

- /*TODO: memory initialization*/
- //retval = usbssp_mem_init(usbssp_data, GFP_KERNEL);
+ retval = usbssp_mem_init(usbssp_data, GFP_KERNEL);

return retval;
}
diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h
index 5d8918f8da84..4a9a1ca2e9b5 100644
--- a/drivers/usb/usbssp/gadget.h
+++ b/drivers/usb/usbssp/gadget.h
@@ -1682,12 +1682,16 @@ static inline void usbssp_write_64(struct usbssp_udc *usbssp_data,
void usbssp_dbg_trace(struct usbssp_udc *usbssp_data,
void (*trace)(struct va_format *),
const char *fmt, ...);
+/* USBSSP memory management */
+void usbssp_mem_cleanup(struct usbssp_udc *usbssp_data);
+int usbssp_mem_init(struct usbssp_udc *usbssp_data, gfp_t flags);
/* USBSSP Device controller glue */
void usbssp_bottom_irq(struct work_struct *work);
int usbssp_init(struct usbssp_udc *usbssp_data);
void usbssp_stop(struct usbssp_udc *usbssp_data);
int usbssp_handshake(void __iomem *ptr, u32 mask, u32 done, int usec);
void usbssp_quiesce(struct usbssp_udc *usbssp_data);
+int usbssp_halt(struct usbssp_udc *usbssp_data);
extern int usbssp_reset(struct usbssp_udc *usbssp_data);

int usbssp_suspend(struct usbssp_udc *usbssp_data, bool do_wakeup);
@@ -1698,6 +1702,7 @@ irqreturn_t usbssp_irq(int irq, void *priv);
/* USBSSP ring, segment, TRB, and TD functions */
dma_addr_t usbssp_trb_virt_to_dma(struct usbssp_segment *seg,
union usbssp_trb *trb);
+void usbssp_handle_command_timeout(struct work_struct *work);

/* USBSSP gadget interface*/
int usbssp_gadget_init(struct usbssp_udc *usbssp_data);
--
2.17.1


2018-07-19 18:02:10

by Pawel Laszczak

[permalink] [raw]
Subject: [PATCH 29/31] usb: usbssp: added support for LPM.

Patch implements LPM functionality for port working in HS mode.

Signed-off-by: Pawel Laszczak <[email protected]>
---
drivers/usb/usbssp/gadget-port.c | 2 +-
drivers/usb/usbssp/gadget-ring.c | 11 +++++++
drivers/usb/usbssp/gadget.c | 49 ++++++++++++++++++++++++++++++++
drivers/usb/usbssp/gadget.h | 4 +++
4 files changed, 65 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/usbssp/gadget-port.c b/drivers/usb/usbssp/gadget-port.c
index 2c4d28070cab..05a4eb2fd8bf 100644
--- a/drivers/usb/usbssp/gadget-port.c
+++ b/drivers/usb/usbssp/gadget-port.c
@@ -10,9 +10,9 @@
* Origin: Copyright (C) 2008 Intel Corp
*/

+#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <asm/unaligned.h>
-
#include "gadget-trace.h"
#include "gadget.h"

diff --git a/drivers/usb/usbssp/gadget-ring.c b/drivers/usb/usbssp/gadget-ring.c
index e7afd185d4ad..4abc2293f249 100644
--- a/drivers/usb/usbssp/gadget-ring.c
+++ b/drivers/usb/usbssp/gadget-ring.c
@@ -3018,6 +3018,17 @@ int usbssp_queue_ctrl_tx(struct usbssp_udc *usbssp_data,
USB_STATE_CONFIGURED);
}

+ if (usbssp_data->bos_event_detected) {
+ usbssp_data->bos_event_detected = 0;
+ usb_gadget_unmap_request_by_dev(usbssp_data->dev,
+ &req_priv->request,
+ dep->direction);
+ usbssp_set_usb2_hardware_lpm(usbssp_data,
+ &req_priv->request, 1);
+ ret = usb_gadget_map_request_by_dev(usbssp_data->dev,
+ &req_priv->request, dep->direction);
+ }
+
/* 1 TRB for data, 1 for status */
if (usbssp_data->three_stage_setup)
num_trbs = 2;
diff --git a/drivers/usb/usbssp/gadget.c b/drivers/usb/usbssp/gadget.c
index 378828d10a2e..029d6313d94c 100644
--- a/drivers/usb/usbssp/gadget.c
+++ b/drivers/usb/usbssp/gadget.c
@@ -1702,6 +1702,55 @@ int usbssp_enable_device(struct usbssp_udc *usbssp_data)
return usbssp_setup_device(usbssp_data, SETUP_CONTEXT_ONLY);
}

+int usbssp_set_usb2_hardware_lpm(struct usbssp_udc *usbssp_data,
+ struct usb_request *req, int enable)
+{
+ __le32 __iomem *pm_addr;
+ u32 pm_val, field;
+ int besl;
+
+ struct usb_ext_cap_descriptor *usb_ext = req->buf + USB_DT_BOS_SIZE;
+
+ if (usbssp_data->port_major_revision >= 3 ||
+ !usbssp_data->hw_lpm_support ||
+ !usbssp_data->gadget.lpm_capable)
+ return -EPERM;
+
+ if (usb_ext->bDescriptorType != USB_DT_DEVICE_CAPABILITY ||
+ usb_ext->bDevCapabilityType != USB_CAP_TYPE_EXT) {
+ return -EPERM;
+ }
+ pm_addr = usbssp_data->usb2_ports + PORTPMSC;
+ pm_val = readl(pm_addr);
+ field = le32_to_cpu(usb_ext->bmAttributes);
+
+ dev_dbg(usbssp_data->dev, "%s port %d USB2 hardware LPM\n",
+ enable ? "enable" : "disable", usbssp_data->devs.port_num);
+
+ if (enable) {
+ /*
+ * If device doesn't have a preferred BESL value use a
+ * default one . See USBSSP_DEFAULT_BESL definition in gadget.h
+ */
+ if ((field & USB_BESL_SUPPORT) &&
+ (field & USB_BESL_BASELINE_VALID))
+ besl = USB_GET_BESL_BASELINE(field);
+ else
+ besl = USBSSP_DEFAULT_BESL;
+
+ pm_val &= ~(PORT_BESL_MASK | PORT_HLE_MASK);
+ pm_val |= PORT_RBESL(besl) | PORT_HLE | 3 /*L1S set to 3*/;
+ pr_err("usbssp_set_usb2_hardware_lpm7 %08x\n", pm_val);
+ writel(pm_val, pm_addr);
+ /* flush write */
+ readl(pm_addr);
+ } else {
+ pm_val &= ~(PORT_HLE | PORT_BESL_MASK | PORT_L1S_MASK);
+ pm_val |= PORT_L1S_HLE0_STALL;
+ writel(pm_val, pm_addr);
+ }
+ return 0;
+}

int usbssp_get_frame(struct usbssp_udc *usbssp_data)
{
diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h
index 3a223b89efe6..59d7ef573d96 100644
--- a/drivers/usb/usbssp/gadget.h
+++ b/drivers/usb/usbssp/gadget.h
@@ -1756,6 +1756,10 @@ int usbssp_alloc_dev(struct usbssp_udc *usbssp_data);
void usbssp_free_dev(struct usbssp_udc *usbssp_data);
int usbssp_address_device(struct usbssp_udc *usbssp_data);
int usbssp_enable_device(struct usbssp_udc *usbssp_data);
+
+int usbssp_set_usb2_hardware_lpm(struct usbssp_udc *usbsssp_data,
+ struct usb_request *req, int enable);
+
/* USBSSP ring, segment, TRB, and TD functions */
dma_addr_t usbssp_trb_virt_to_dma(struct usbssp_segment *seg,
union usbssp_trb *trb);
--
2.17.1


2018-07-19 18:02:33

by Pawel Laszczak

[permalink] [raw]
Subject: [PATCH 31/31] usb: usbssp: add pci to platform driver wrapper.

Patch adds PCI specific glue drivier that creaties and registers in
system usbssp platform device. Thanks to this we can use
the existing usbssp platform driver for usbssp controller
build on PCI board

Signed-off-by: Pawel Laszczak <[email protected]>
---
drivers/usb/usbssp/Kconfig | 10 +-
drivers/usb/usbssp/Makefile | 3 +
drivers/usb/usbssp/usbssp-pci-wrap.c | 226 +++++++++++++++++++++++++++
3 files changed, 238 insertions(+), 1 deletion(-)
create mode 100644 drivers/usb/usbssp/usbssp-pci-wrap.c

diff --git a/drivers/usb/usbssp/Kconfig b/drivers/usb/usbssp/Kconfig
index ee20b01753dc..bada82e5d223 100644
--- a/drivers/usb/usbssp/Kconfig
+++ b/drivers/usb/usbssp/Kconfig
@@ -17,5 +17,13 @@ config USB_USBSSP_GADGET
depends on USB_GADGET=y || USB_GADGET=USB_USBSSP
help
Select this when you want to use USBSSP in gadget mode only,
-endif

+config USB_USBSSP_PCI
+ tristate "PCIe-based Platforms"
+ depends on USB_PCI && ACPI
+ default USB_USBSSP
+ help
+ If you're using the USBSSP Core IP with a PCIe, please say
+ 'Y' or 'M' here.
+
+endif
diff --git a/drivers/usb/usbssp/Makefile b/drivers/usb/usbssp/Makefile
index b267fadcb104..fbf5d55500a4 100644
--- a/drivers/usb/usbssp/Makefile
+++ b/drivers/usb/usbssp/Makefile
@@ -10,3 +10,6 @@ usbssp-y := usbssp-plat.o gadget-ring.o \
ifneq ($(CONFIG_TRACING),)
usbssp-y += gadget-trace.o
endif
+
+obj-$(CONFIG_USB_USBSSP_PCI) += usbssp-pci.o
+usbssp-pci-y := usbssp-pci-wrap.o
diff --git a/drivers/usb/usbssp/usbssp-pci-wrap.c b/drivers/usb/usbssp/usbssp-pci-wrap.c
new file mode 100644
index 000000000000..10c0189c61f7
--- /dev/null
+++ b/drivers/usb/usbssp/usbssp-pci-wrap.c
@@ -0,0 +1,226 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USBSSP device controller driver - PCIe wrapper
+ *
+ * Copyright (C) 2018 Cadence.
+ *
+ * Author: Pawel Laszczak
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+
+struct usbssp_pci {
+ struct platform_device *plat_cdns;
+ struct pci_dev *otg;
+ struct pci_dev *hg_dev;
+ struct resource res[5];
+};
+
+struct usbssp_pci usbssp;
+
+/**
+ * usbssp_pci_probe - Probe function for Cadence USB wrapper driver
+ * @pdev: platform device object
+ * @id: pci device id
+ *
+ * Returns 0 on success otherwise negative errno
+ */
+static int usbssp_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ int rv = 0;
+ struct platform_device *usbssp_plat;
+
+ if (pdev->devfn > 1)
+ return 0;
+
+ if (!id)
+ return -EINVAL;
+
+ if (pcim_enable_device(pdev) < 0) {
+ dev_err(&pdev->dev, "failed to enable PCI device\n");
+ return -ENOMEM;
+ }
+
+ pci_set_master(pdev);
+
+ /*
+ * function 0: device(BAR_2) + host(BAR_0)
+ * function 1: otg(BAR_0))cdns3_remove_roles
+ */
+ if (pdev->devfn == 0 || pdev->devfn == 1) {
+ if (!usbssp.plat_cdns) {
+ usbssp_plat = platform_device_alloc("usbssp-dev",
+ PLATFORM_DEVID_AUTO);
+ memset(usbssp.res, 0x00, sizeof(struct resource) *
+ ARRAY_SIZE(usbssp.res));
+ usbssp.plat_cdns = usbssp_plat;
+
+ if (!usbssp_plat->dev.dma_mask) {
+ dma_coerce_mask_and_coherent(&usbssp_plat->dev,
+ DMA_BIT_MASK(32));
+ }
+ }
+ } else
+ return -EINVAL;
+
+ /* for GADGET device function number is 0 */
+ if (pdev->devfn == 0) {
+ usbssp.hg_dev = pdev;
+ //device resource
+ usbssp.res[0].start = pci_resource_start(pdev, 2);
+ usbssp.res[0].end = pci_resource_end(pdev, 2);
+ usbssp.res[0].name = "usbssp-dev-regs";
+ usbssp.res[0].flags = IORESOURCE_MEM;
+
+ /* Interrupt resource*/
+ usbssp.res[1].start = pdev->irq;
+ usbssp.res[1].name = "usbssp-dev-irq";
+ usbssp.res[1].flags = IORESOURCE_IRQ;
+ } else if (pdev->devfn == 1) {
+ usbssp.otg = pdev;
+ //OTG
+ usbssp.res[2].start = pci_resource_start(pdev, 0);
+ usbssp.res[2].end = pci_resource_end(pdev, 0);
+ usbssp.res[2].name = "otg";
+ usbssp.res[2].flags = IORESOURCE_MEM;
+
+ usbssp.res[3].start = pci_resource_start(pdev, 1);
+ usbssp.res[3].end = pci_resource_end(pdev, 1);
+ usbssp.res[3].name = "debug1";
+ usbssp.res[3].flags = IORESOURCE_MEM;
+
+ usbssp.res[4].start = pci_resource_start(pdev, 2);
+ usbssp.res[4].end = pci_resource_end(pdev, 2);
+ usbssp.res[4].name = "debug2";
+ usbssp.res[4].flags = IORESOURCE_MEM;
+ } else {
+ return -EIO;
+ }
+
+ if (usbssp.otg && usbssp.hg_dev) {
+
+ struct pci_dev *pci_hg = usbssp.hg_dev;
+ struct platform_device *plat_dev = usbssp.plat_cdns;
+
+ rv = platform_device_add_resources(plat_dev, usbssp.res,
+ ARRAY_SIZE(usbssp.res));
+ if (rv) {
+ dev_err(&plat_dev->dev,
+ "couldn't add resources to cdns device\n");
+ return rv;
+ }
+
+ rv = platform_device_add(plat_dev);
+
+ if (rv) {
+ dev_err(&pci_hg->dev,
+ "failed to register cdns device\n");
+ platform_device_put(plat_dev);
+ return rv;
+ }
+ }
+
+ pci_set_drvdata(pdev, &usbssp);
+
+ return rv;
+}
+
+void usbssp_pci_remove(struct pci_dev *pdev)
+{
+ struct usbssp_pci *usbssp = pci_get_drvdata(pdev);
+
+ if (pdev->devfn > 1)
+ return;
+
+ if (pdev->devfn == 1)
+ usbssp->otg = NULL;
+ else if (pdev->devfn == 0)
+ usbssp->hg_dev = NULL;
+
+ if (!usbssp->hg_dev && !usbssp->otg)
+ platform_device_unregister(usbssp->plat_cdns);
+}
+
+#if CONFIG_PM || CONFIG_PM_SLEEP
+static int usbssp_generic_suspend(struct usbssp_pci *usbssp, int param)
+{
+ /*TODO*/
+ return -ENOSYS;
+}
+
+static int usbssp_generic_resume(struct usbssp_pci *usbssp, int param)
+{
+ /*TODO*/
+ return -ENOSYS;
+}
+
+#endif /*CONFIG_PM || CONFIG_PM_SLEEP*/
+
+#ifdef CONFIG_PM
+static int usbssp_runtime_suspend(struct device *dev)
+{
+ struct usbssp_pci *usbssp = dev_get_drvdata(dev);
+
+ return usbssp_generic_suspend(usbssp, 0);
+}
+
+static int usbssp_runtime_resume(struct device *dev)
+{
+ struct usbssp_pci *usbssp = dev_get_drvdata(dev);
+
+ return usbssp_generic_resume(usbssp, 0);
+}
+#endif /* CONFIG_PM */
+
+#ifdef CONFIG_PM_SLEEP
+static int usbssp_pci_suspend(struct device *dev)
+{
+ struct usbssp_pci *usbssp = dev_get_drvdata(dev);
+
+ return usbssp_generic_suspend(usbssp, 0);
+}
+
+static int usbssp_pci_resume(struct device *dev)
+{
+ struct usbssp_pci *usbssp = dev_get_drvdata(dev);
+
+ return usbssp_generic_resume(usbssp, 0);
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops usbssp_pci_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(usbssp_runtime_suspend, usbssp_runtime_resume)
+ SET_RUNTIME_PM_OPS(usbssp_pci_suspend, usbssp_pci_resume, NULL)
+};
+
+#define PCI_VENDOR_ID_CDZ 0x17CD
+static const struct pci_device_id usbssp_pci_ids[] = {
+ {PCI_VDEVICE(CDZ, 0x0100), },
+ { /* terminate list */}
+};
+
+MODULE_DEVICE_TABLE(pci, usbssp_pci_ids);
+
+static struct pci_driver usbssp_pci_driver = {
+ .name = "usbssp-pci",
+ .id_table = &usbssp_pci_ids[0],
+ .probe = usbssp_pci_probe,
+ .remove = usbssp_pci_remove,
+ .driver = {
+ .pm = &usbssp_pci_dev_pm_ops,
+ }
+};
+
+MODULE_AUTHOR("Pawel Laszczak <[email protected]>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Cadence USBSSP PCI Glue Layer");
+
+module_pci_driver(usbssp_pci_driver);
+
--
2.17.1


2018-07-19 18:02:37

by Pawel Laszczak

[permalink] [raw]
Subject: [PATCH 28/31] usb: usbssp: implemented usbssp_gadget_ep_disable function.

Patch implements function responsible for disabling
USB endpoint. This function is called
from USB core gadget during handling SET_CONFIGURATION or
SET_INTERFACE request, or after such events as USB_RESET or detaching
device from host.

Signed-off-by: Pawel Laszczak <[email protected]>
---
drivers/usb/usbssp/gadget-if.c | 44 +++++++++++++++++--
drivers/usb/usbssp/gadget-mem.c | 21 ++++++++-
drivers/usb/usbssp/gadget.c | 75 +++++++++++++++++++++++++++++++++
drivers/usb/usbssp/gadget.h | 2 +
4 files changed, 137 insertions(+), 5 deletions(-)

diff --git a/drivers/usb/usbssp/gadget-if.c b/drivers/usb/usbssp/gadget-if.c
index afc4f32ec8af..d5ad204612fa 100644
--- a/drivers/usb/usbssp/gadget-if.c
+++ b/drivers/usb/usbssp/gadget-if.c
@@ -86,13 +86,49 @@ static int usbssp_gadget_ep_enable(struct usb_ep *ep,

int usbssp_gadget_ep_disable(struct usb_ep *ep)
{
- struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
- int ret = 0;
+ struct usbssp_ep *ep_priv;
+ struct usbssp_udc *usbssp_data;
+ int ep_index = 0;
+ int ret;
+ int irq_disabled_locally = 0;
+ unsigned long flags = 0;
+ struct usbssp_request *req_priv;
+
+ ep_priv = to_usbssp_ep(ep);
+ usbssp_data = ep_priv->usbssp_data;
+ ep_index = usbssp_get_endpoint_index(ep_priv->endpoint.desc);

- if (!ep_priv)
+ if (!(ep_priv->ep_state & USBSSP_EP_ENABLED)) {
+ dev_dbg(usbssp_data->dev, "%s is already disabled\n",
+ ep_priv->name);
return -EINVAL;
+ }
+
+ usbssp_g_lock(irq_disabled_locally, flags);
+
+ ep_priv->ep_state |= USBSSP_EP_DISABLE_PENDING;
+
+ /*dequeue all USB request from endpoint*/
+ list_for_each_entry(req_priv, &ep_priv->pending_list, list) {
+ usbssp_dequeue(ep_priv, req_priv);
+ }
+
+ ret = usbssp_drop_endpoint(usbssp_data, &usbssp_data->gadget, ep_priv);
+ if (ret)
+ goto finish;
+
+ ret = usbssp_check_bandwidth(usbssp_data, &usbssp_data->gadget);
+ if (ret)
+ goto finish;

- /*TODO: implements this function*/
+ ep_priv->ep_state &= ~USBSSP_EP_ENABLED;
+
+finish:
+ ep_priv->ep_state &= ~USBSSP_EP_DISABLE_PENDING;
+ dev_dbg(usbssp_data->dev, "%s disable endpoint %s\n", ep_priv->name,
+ (ret == 0) ? "success" : "failed");
+
+ usbssp_g_unlock(irq_disabled_locally, flags);
return ret;
}

diff --git a/drivers/usb/usbssp/gadget-mem.c b/drivers/usb/usbssp/gadget-mem.c
index 8438596ecf48..217ce46bff8b 100644
--- a/drivers/usb/usbssp/gadget-mem.c
+++ b/drivers/usb/usbssp/gadget-mem.c
@@ -873,7 +873,7 @@ static unsigned int usbssp_microframes_to_exponent(struct usb_gadget *g,
}

static unsigned int usbssp_parse_microframe_interval(struct usb_gadget *g,
- struct usbssp_ep *dep)
+ struct usbssp_ep *dep)
{
if (dep->endpoint.desc->bInterval == 0)
return 0;
@@ -1103,6 +1103,25 @@ int usbssp_endpoint_init(struct usbssp_udc *usbssp_data,
return 0;
}

+void usbssp_endpoint_zero(struct usbssp_udc *usbssp_data,
+ struct usbssp_device *dev_priv,
+ struct usbssp_ep *ep)
+{
+ unsigned int ep_index;
+ struct usbssp_ep_ctx *ep_ctx;
+
+ ep_index = usbssp_get_endpoint_index(ep->endpoint.desc);
+ ep_ctx = usbssp_get_ep_ctx(usbssp_data, dev_priv->in_ctx, ep_index);
+
+ ep_ctx->ep_info = 0;
+ ep_ctx->ep_info2 = 0;
+ ep_ctx->deq = 0;
+ ep_ctx->tx_info = 0;
+ /*
+ * Don't free the endpoint ring until the set interface or configuration
+ * request succeeds.
+ */
+}
struct usbssp_command *usbssp_alloc_command(struct usbssp_udc *usbssp_data,
bool allocate_completion,
gfp_t mem_flags)
diff --git a/drivers/usb/usbssp/gadget.c b/drivers/usb/usbssp/gadget.c
index f5b0659b6f2d..378828d10a2e 100644
--- a/drivers/usb/usbssp/gadget.c
+++ b/drivers/usb/usbssp/gadget.c
@@ -715,6 +715,81 @@ int usbssp_dequeue(struct usbssp_ep *ep_priv, struct usbssp_request *req_priv)
return ret;
}

+/*
+ * Drop an endpoint from a new bandwidth configuration for this device.
+ * Only one call to this function is allowed per endpoint before
+ * check_bandwidth() or reset_bandwidth() must be called.
+ * A call to usbssp_drop_endpoint() followed by a call to usbssp_add_endpoint()
+ * will add the endpoint to the schedule with possibly new parameters
+ * denoted by a different endpoint descriptor in usbssp_ep.
+ * A call to usbssp_add_endpoint() followed by a call to
+ * usbsssp_drop_endpoint() is not allowed.
+ */
+int usbssp_drop_endpoint(struct usbssp_udc *usbssp_data, struct usb_gadget *g,
+ struct usbssp_ep *dep)
+{
+ struct usbssp_container_ctx *in_ctx, *out_ctx;
+ struct usbssp_input_control_ctx *ctrl_ctx;
+ unsigned int ep_index;
+ struct usbssp_ep_ctx *ep_ctx;
+ u32 drop_flag;
+ u32 new_add_flags, new_drop_flags;
+ int ret;
+
+ ret = usbssp_check_args(usbssp_data, dep, 1, true, __func__);
+ if (ret <= 0)
+ return ret;
+
+ if (usbssp_data->usbssp_state & USBSSP_STATE_DYING)
+ return -ENODEV;
+
+ drop_flag = usbssp_get_endpoint_flag(dep->endpoint.desc);
+ if (drop_flag == SLOT_FLAG || drop_flag == EP0_FLAG) {
+ dev_dbg(usbssp_data->dev, "USBSSP %s - can't drop slot or ep 0 %#x\n",
+ __func__, drop_flag);
+ return 0;
+ }
+
+ in_ctx = usbssp_data->devs.in_ctx;
+ out_ctx = usbssp_data->devs.out_ctx;
+ ctrl_ctx = usbssp_get_input_control_ctx(in_ctx);
+ if (!ctrl_ctx) {
+ dev_warn(usbssp_data->dev, "%s: Could not get input context, bad type.\n",
+ __func__);
+ return 0;
+ }
+
+ ep_index = usbssp_get_endpoint_index(dep->endpoint.desc);
+ ep_ctx = usbssp_get_ep_ctx(usbssp_data, out_ctx, ep_index);
+
+ /*
+ *If the controller already knows the endpoint is disabled,
+ * or the USBSSP driver has noted it is disabled, ignore this request
+ */
+ if ((GET_EP_CTX_STATE(ep_ctx) == EP_STATE_DISABLED) ||
+ le32_to_cpu(ctrl_ctx->drop_flags) &
+ usbssp_get_endpoint_flag(dep->endpoint.desc)) {
+ /* Do not warn when called after a usb_device_reset */
+ if (usbssp_data->devs.eps[ep_index].ring != NULL)
+ dev_warn(usbssp_data->dev, "USBSSP %s called with disabled ep %p\n",
+ __func__, dep);
+ return 0;
+ }
+
+ ctrl_ctx->drop_flags |= cpu_to_le32(drop_flag);
+ new_drop_flags = le32_to_cpu(ctrl_ctx->drop_flags);
+
+ ctrl_ctx->add_flags &= cpu_to_le32(~drop_flag);
+ new_add_flags = le32_to_cpu(ctrl_ctx->add_flags);
+
+ usbssp_endpoint_zero(usbssp_data, &usbssp_data->devs, dep);
+
+ dev_dbg(usbssp_data->dev, "drop ep 0x%x, new drop flags = %#x, new add flags = %#x\n",
+ (unsigned int) dep->endpoint.desc->bEndpointAddress,
+ (unsigned int) new_drop_flags,
+ (unsigned int) new_add_flags);
+ return 0;
+}

/**
* Add an endpoint to a new possible bandwidth configuration for this device.
diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h
index c4075e765dcc..3a223b89efe6 100644
--- a/drivers/usb/usbssp/gadget.h
+++ b/drivers/usb/usbssp/gadget.h
@@ -1694,6 +1694,8 @@ void usbssp_copy_ep0_dequeue_into_input_ctx(struct usbssp_udc *usbssp_data);
unsigned int usbssp_get_endpoint_index(const struct usb_endpoint_descriptor *desc);
unsigned int usbssp_get_endpoint_address(unsigned int ep_index);
unsigned int usbssp_last_valid_endpoint(u32 added_ctxs);
+void usbssp_endpoint_zero(struct usbssp_udc *usbssp_data,
+ struct usbssp_device *dev_priv, struct usbssp_ep *ep);
int usbssp_endpoint_init(struct usbssp_udc *usbssp_data,
struct usbssp_device *dev_priv,
struct usbssp_ep *dep,
--
2.17.1


2018-07-19 18:02:46

by Pawel Laszczak

[permalink] [raw]
Subject: [PATCH 03/31] usb: usbssp: Add trace events used in driver

This patch creates a set of event class. For each event class this patch
defines some trace events.

1. event class: usbssp_log_msg
- usbssp_dbg_address - debug message used in function handling
Address Device Command.
- usbssp_dbg_context_change - debug messages related to contexti
updates performed with Configure Endpoint and Evaluate Contexti
command.
- usbssp_dbg_reset_ep - debug messages associated with resetting i
an endpoint.
- usbssp_dbg_cancel_request - debug messages related to the removal
of a cancelled Request.
- usbssp_dbg_init - debug messages related to the initialization
and unload.
- usbssp_dbg_ring_expansion - debug messages related to ring
expansion.

2. event class: usbssp_log_ctx
usbssp_address_ctx - debug message related to address Device command,
that can be used to display the context data filed.

3. event class: usbssp_log_trb
- usbssp_handle_event - debug message related to handling Events TRB.
- usbssp_handle_command - debug message related to handling C
ommand TRB.
- usbssp_handle_transfer - debug message related to handling
Transfer TRB.
- usbssp_queue_trb - debug message related to queuing TRB.

4. event class: usbssp_log_priv_dev
This class event tracing at least part of an usbssp_device lifetime,
and for now it already exposes quite a bit of valuable information.

- usbssp_alloc_priv_device - debug message related to allocating
device.
- usbssp_free_priv_device - debug message related to the freeing
device.
- usbssp_setup_addressable_priv_device - debug message related to the
initialization of usbssp_device object.
- usbssp_setup_device - debug message related to Address Device
command.
- usbssp_stop_device - debug message related to stooping device.

5. event class: usbssp_log_request
This will help us figuring out information related to usb_request
object.

- usbssp_request_enqueue - debug message used during enqueuing new
request.
- usbssp_request_giveback - debug message related to
usbssp_request_giveback function
- usbssp_request_dequeue - debug message related to dequeuing request.
- usbssp_alloc_request - debug message related to allocation
new request.
- usbssp_free_request - debug message related to freeing request.

6. event class: usbssp_log_ep_ctx
With these event class, we can track what's happening with the
HW while executing each and every command. It will give us visibility
into how the endpoint contexts are being modified by controller
which can bring insight into problems while debugging.

- usbssp_remove_request - debug message related to removing request.
- usbssp_handle_cmd_stop_ep - debug message related to Stop Endpointi
command.
- usbssp_handle_cmd_set_deq_ep - debug message releted to Set Dequeue
Endpoint command,
- usbssp_handle_cmd_reset_ep - debug message related to Reset Endpoint
command.
- usbssp_handle_cmd_config_ep - debug message related to Endpoint
Configure command.

7. event class: usbssp_log_slot_ctx
With these event class, we can track what's happening with the HW
while executing each and every command. It will give us visibility
into how the slot contexts are being modified by controller which
can bring insight into problems while debugging.

- usbssp_alloc_dev - debug message related to allocating device.
- usbssp_free_dev - debug message related to freeing device.
- usbssp_handle_cmd_disable_slot - debug message related to
Disable Slot command.
- usbssp_reset_device - debug message related to resetting device.
- usbssp_setup_device_slot - debug message related to c
onfiguring slot.
- usbssp_handle_cmd_addr_dev - debug message related to Address Device
command.
- usbssp_handle_cmd_reset_dev - debug message related to Reset Device
command.
- usbssp_handle_cmd_set_deq - debug message related to
Set Dequeue command.
- usbssp_configure_endpoint - debug message used during configuring
endpoints.

8. event class: usbssp_log_ring
This event class defines the events used for tracing the change
of all kinds of rings used by an usbssp device. An usbssp ring
is basically a memory block shared between software and hardware.
By tracing changes of rings, it makes the life easier for.

- debugging hardware or software problems.
- usbssp_ring_alloc - debug message related to allocation ring.
- usbssp_ring_free - debug message related to freeing ring.
- usbssp_ring_expansion - debug message related with expansion ring.
- usbssp_inc_enq - debug message related with incrementing
dequeue pointer.
- usbssp_inc_deq - debug message related with decrementing
dequeue pointer.

9. event class: usbssp_log_portsc
Track the port status each time we get a port status change event.

- usbssp_handle_port_status - debug message used in
handle_port_status function.

Signed-off-by: Pawel Laszczak <[email protected]>
---
drivers/usb/usbssp/gadget-trace.c | 13 +
drivers/usb/usbssp/gadget-trace.h | 482 ++++++++++++++++++++++++++++++
2 files changed, 495 insertions(+)
create mode 100644 drivers/usb/usbssp/gadget-trace.c
create mode 100644 drivers/usb/usbssp/gadget-trace.h

diff --git a/drivers/usb/usbssp/gadget-trace.c b/drivers/usb/usbssp/gadget-trace.c
new file mode 100644
index 000000000000..9994ad09eb4d
--- /dev/null
+++ b/drivers/usb/usbssp/gadget-trace.c
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USBSSP device controller driver
+ *
+ * Copyright (C) 2018 Cadence.
+ *
+ * Author: Pawel Laszczak
+ * A lot of code based on Linux XHCI driver.
+ * Origin: Copyright (C) 2008 Intel Corp.
+ */
+
+#define CREATE_TRACE_POINTS
+#include "gadget-trace.h"
diff --git a/drivers/usb/usbssp/gadget-trace.h b/drivers/usb/usbssp/gadget-trace.h
new file mode 100644
index 000000000000..667246c344ba
--- /dev/null
+++ b/drivers/usb/usbssp/gadget-trace.h
@@ -0,0 +1,482 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USBSSP device controller driver
+ *
+ * Copyright (C) 2018 Cadence.
+ *
+ * Author: Pawel Laszczak
+ *
+ * A lot of code based on Linux XHCI driver.
+ * Origin: Copyright (C) 2008 Intel Corp.
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM usbssp-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 usbssp_dev
+
+#if !defined(__USBSSP_DEV_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define __USBSSP_DEV_TRACE_H
+
+#include <linux/tracepoint.h>
+#include "gadget.h"
+
+#define USBSSP_DEV_MSG_MAX 500
+
+DECLARE_EVENT_CLASS(usbssp_log_msg,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf),
+ TP_STRUCT__entry(__dynamic_array(char, msg, USBSSP_DEV_MSG_MAX)),
+ TP_fast_assign(
+ vsnprintf(__get_str(msg), USBSSP_DEV_MSG_MAX, vaf->fmt, *vaf->va);
+ ),
+ TP_printk("%s", __get_str(msg))
+);
+
+DEFINE_EVENT(usbssp_log_msg, usbssp_dbg_address,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(usbssp_log_msg, usbssp_dbg_context_change,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(usbssp_log_msg, usbssp_dbg_reset_ep,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(usbssp_log_msg, usbssp_dbg_cancel_request,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(usbssp_log_msg, usbssp_dbg_init,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(usbssp_log_msg, usbssp_dbg_ring_expansion,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf)
+);
+
+DECLARE_EVENT_CLASS(usbssp_log_ctx,
+ TP_PROTO(struct usbssp_udc *usbssp_data, struct usbssp_container_ctx *ctx,
+ unsigned int ep_num),
+ TP_ARGS(usbssp_data, ctx, ep_num),
+ TP_STRUCT__entry(
+ __field(int, ctx_64)
+ __field(unsigned int, ctx_type)
+ __field(dma_addr_t, ctx_dma)
+ __field(u8 *, ctx_va)
+ __field(unsigned int, ctx_ep_num)
+ __field(int, slot_id)
+ __dynamic_array(u32, ctx_data,
+ ((HCC_64BYTE_CONTEXT(usbssp_data->hcc_params) + 1) * 8) *
+ ((ctx->type == USBSSP_CTX_TYPE_INPUT) + ep_num + 1))
+ ),
+ TP_fast_assign(
+ __entry->ctx_64 = HCC_64BYTE_CONTEXT(usbssp_data->hcc_params);
+ __entry->ctx_type = ctx->type;
+ __entry->ctx_dma = ctx->dma;
+ __entry->ctx_va = ctx->bytes;
+ __entry->slot_id = usbssp_data->slot_id;
+ __entry->ctx_ep_num = ep_num;
+ memcpy(__get_dynamic_array(ctx_data), ctx->bytes,
+ ((HCC_64BYTE_CONTEXT(usbssp_data->hcc_params) + 1) * 32) *
+ ((ctx->type == USBSSP_CTX_TYPE_INPUT) + ep_num + 1));
+ ),
+ TP_printk("\nctx_64=%d, ctx_type=%u, ctx_dma=@%llx, ctx_va=@%p",
+ __entry->ctx_64, __entry->ctx_type,
+ (unsigned long long) __entry->ctx_dma, __entry->ctx_va
+ )
+);
+
+DEFINE_EVENT(usbssp_log_ctx, usbssp_address_ctx,
+ TP_PROTO(struct usbssp_udc *usbssp_data, struct usbssp_container_ctx *ctx,
+ unsigned int ep_num),
+ TP_ARGS(usbssp_data, ctx, ep_num)
+);
+
+
+DECLARE_EVENT_CLASS(usbssp_log_trb,
+ TP_PROTO(struct usbssp_ring *ring, struct usbssp_generic_trb *trb),
+ TP_ARGS(ring, trb),
+ TP_STRUCT__entry(
+ __field(u32, type)
+ __field(u32, field0)
+ __field(u32, field1)
+ __field(u32, field2)
+ __field(u32, field3)
+ ),
+ TP_fast_assign(
+ __entry->type = ring->type;
+ __entry->field0 = le32_to_cpu(trb->field[0]);
+ __entry->field1 = le32_to_cpu(trb->field[1]);
+ __entry->field2 = le32_to_cpu(trb->field[2]);
+ __entry->field3 = le32_to_cpu(trb->field[3]);
+ ),
+ TP_printk("%s: %s", usbssp_ring_type_string(__entry->type),
+ usbssp_decode_trb(__entry->field0, __entry->field1,
+ __entry->field2, __entry->field3)
+ )
+);
+
+DEFINE_EVENT(usbssp_log_trb, usbssp_handle_event,
+ TP_PROTO(struct usbssp_ring *ring, struct usbssp_generic_trb *trb),
+ TP_ARGS(ring, trb)
+);
+
+DEFINE_EVENT(usbssp_log_trb, usbssp_handle_command,
+ TP_PROTO(struct usbssp_ring *ring, struct usbssp_generic_trb *trb),
+ TP_ARGS(ring, trb)
+);
+
+DEFINE_EVENT(usbssp_log_trb, usbssp_handle_transfer,
+ TP_PROTO(struct usbssp_ring *ring, struct usbssp_generic_trb *trb),
+ TP_ARGS(ring, trb)
+);
+
+DEFINE_EVENT(usbssp_log_trb, usbssp_queue_trb,
+ TP_PROTO(struct usbssp_ring *ring, struct usbssp_generic_trb *trb),
+ TP_ARGS(ring, trb)
+);
+
+DECLARE_EVENT_CLASS(usbssp_log_priv_dev,
+ TP_PROTO(struct usbssp_device *priv_dev),
+ TP_ARGS(priv_dev),
+ TP_STRUCT__entry(
+ __field(struct usbssp_device *, priv_dev)
+ __field(struct usb_gadget *, gadget)
+ __field(unsigned long long, out_ctx)
+ __field(unsigned long long, in_ctx)
+ __field(u8, port_num)
+ ),
+ TP_fast_assign(
+ __entry->priv_dev = priv_dev;
+ __entry->gadget = priv_dev->gadget;
+ __entry->in_ctx = (unsigned long long) priv_dev->in_ctx->dma;
+ __entry->out_ctx = (unsigned long long) priv_dev->out_ctx->dma;
+ __entry->port_num = priv_dev->port_num;
+ ),
+ TP_printk("priv_dev %p gadget %p ctx %llx | %llx, port %d ",
+ __entry->priv_dev, __entry->gadget, __entry->in_ctx, __entry->out_ctx,
+ __entry->port_num
+ )
+);
+
+DEFINE_EVENT(usbssp_log_priv_dev, usbssp_alloc_priv_device,
+ TP_PROTO(struct usbssp_device *vdev),
+ TP_ARGS(vdev)
+);
+
+DEFINE_EVENT(usbssp_log_priv_dev, usbssp_free_priv_device,
+ TP_PROTO(struct usbssp_device *vdev),
+ TP_ARGS(vdev)
+);
+
+DEFINE_EVENT(usbssp_log_priv_dev, usbssp_setup_device,
+ TP_PROTO(struct usbssp_device *vdev),
+ TP_ARGS(vdev)
+);
+
+DEFINE_EVENT(usbssp_log_priv_dev, usbssp_setup_addressable_priv_device,
+ TP_PROTO(struct usbssp_device *vdev),
+ TP_ARGS(vdev)
+);
+
+DEFINE_EVENT(usbssp_log_priv_dev, usbssp_stop_device,
+ TP_PROTO(struct usbssp_device *vdev),
+ TP_ARGS(vdev)
+);
+
+DECLARE_EVENT_CLASS(usbssp_log_request,
+ TP_PROTO(struct usb_request *request),
+ TP_ARGS(request),
+ TP_STRUCT__entry(
+ __field(struct usb_request *, request)
+ __field(void *, buf)
+ __field(unsigned int, length)
+ __field(dma_addr_t, dma)
+ __field(struct scatterlist*, sg)
+ __field(unsigned int, num_sgs)
+ __field(unsigned int, num_mapped_sgs)
+ __field(unsigned int, stream_id)
+ __field(unsigned int, no_interrupt)
+ __field(unsigned int, zero)
+ __field(unsigned int, short_not_ok)
+ __field(unsigned int, dma_mapped)
+ __field(int, status)
+ __field(unsigned int, actual)
+ ),
+ TP_fast_assign(
+ __entry->request = request;
+ __entry->buf = request->buf;
+ __entry->length = request->length;
+ __entry->dma = request->dma;
+ __entry->sg = request->sg;
+ __entry->num_sgs = request->num_sgs;
+ __entry->num_mapped_sgs = request->num_mapped_sgs;
+ __entry->stream_id = request->stream_id;
+ __entry->no_interrupt = request->no_interrupt;
+ __entry->zero = request->zero;
+ __entry->short_not_ok = request->short_not_ok;
+ __entry->dma_mapped = 0 /*request->dma_mapped*/;
+ __entry->status = request->status;
+ __entry->actual = request->actual;
+ ),
+
+ TP_printk("req %p; buf %p, len %d, dma %llx, sg %p, num_sg %d, num_m_sg %d,"
+ "stream_id %d, no_int %x, zero %x, short_not_ok %x, dma_mapped %x, "
+ "status %d, actual %d",
+ __entry->request,
+ __entry->buf, __entry->length, __entry->dma, __entry->sg,
+ __entry->num_sgs, __entry->num_mapped_sgs, __entry->stream_id,
+ __entry->no_interrupt, __entry->zero, __entry->short_not_ok,
+ __entry->dma_mapped, __entry->status, __entry->actual
+ )
+
+);
+
+DEFINE_EVENT(usbssp_log_request, usbssp_request_enqueue,
+ TP_PROTO(struct usb_request *request),
+ TP_ARGS(request)
+);
+
+DEFINE_EVENT(usbssp_log_request, usbssp_request_giveback,
+ TP_PROTO(struct usb_request *request),
+ TP_ARGS(request)
+);
+
+DEFINE_EVENT(usbssp_log_request, usbssp_request_dequeue,
+ TP_PROTO(struct usb_request *request),
+ TP_ARGS(request)
+);
+
+DEFINE_EVENT(usbssp_log_request, usbssp_alloc_request,
+ TP_PROTO(struct usb_request *request),
+ TP_ARGS(request)
+);
+
+DEFINE_EVENT(usbssp_log_request, usbssp_free_request,
+ TP_PROTO(struct usb_request *request),
+ TP_ARGS(request)
+);
+
+DECLARE_EVENT_CLASS(usbssp_log_ep_ctx,
+ TP_PROTO(struct usbssp_ep_ctx *ctx),
+ TP_ARGS(ctx),
+ TP_STRUCT__entry(
+ __field(u32, info)
+ __field(u32, info2)
+ __field(u64, deq)
+ __field(u32, tx_info)
+ ),
+ TP_fast_assign(
+ __entry->info = le32_to_cpu(ctx->ep_info);
+ __entry->info2 = le32_to_cpu(ctx->ep_info2);
+ __entry->deq = le64_to_cpu(ctx->deq);
+ __entry->tx_info = le32_to_cpu(ctx->tx_info);
+ ),
+ TP_printk("%s", usbssp_decode_ep_context(__entry->info,
+ __entry->info2, __entry->deq, __entry->tx_info)
+ )
+);
+
+DEFINE_EVENT(usbssp_log_ep_ctx, usbssp_remove_request,
+ TP_PROTO(struct usbssp_ep_ctx *ctx),
+ TP_ARGS(ctx)
+);
+
+DEFINE_EVENT(usbssp_log_ep_ctx, usbssp_handle_cmd_stop_ep,
+ TP_PROTO(struct usbssp_ep_ctx *ctx),
+ TP_ARGS(ctx)
+);
+
+DEFINE_EVENT(usbssp_log_ep_ctx, usbssp_handle_cmd_set_deq_ep,
+ TP_PROTO(struct usbssp_ep_ctx *ctx),
+ TP_ARGS(ctx)
+);
+
+DEFINE_EVENT(usbssp_log_ep_ctx, usbssp_handle_cmd_reset_ep,
+ TP_PROTO(struct usbssp_ep_ctx *ctx),
+ TP_ARGS(ctx)
+);
+
+DEFINE_EVENT(usbssp_log_ep_ctx, usbssp_handle_cmd_config_ep,
+ TP_PROTO(struct usbssp_ep_ctx *ctx),
+ TP_ARGS(ctx)
+);
+
+DECLARE_EVENT_CLASS(usbssp_log_slot_ctx,
+ TP_PROTO(struct usbssp_slot_ctx *ctx),
+ TP_ARGS(ctx),
+ TP_STRUCT__entry(
+ __field(u32, info)
+ __field(u32, info2)
+ __field(u32, int_target)
+ __field(u32, state)
+ ),
+ TP_fast_assign(
+ __entry->info = le32_to_cpu(ctx->dev_info);
+ __entry->info2 = le32_to_cpu(ctx->dev_info2);
+ __entry->int_target = le64_to_cpu(ctx->int_target);
+ __entry->state = le32_to_cpu(ctx->dev_state);
+ ),
+ TP_printk("%s", usbssp_decode_slot_context(__entry->info,
+ __entry->info2, __entry->int_target,
+ __entry->state)
+ )
+);
+
+DEFINE_EVENT(usbssp_log_slot_ctx, usbssp_alloc_dev,
+ TP_PROTO(struct usbssp_slot_ctx *ctx),
+ TP_ARGS(ctx)
+);
+
+DEFINE_EVENT(usbssp_log_slot_ctx, usbssp_free_dev,
+ TP_PROTO(struct usbssp_slot_ctx *ctx),
+ TP_ARGS(ctx)
+);
+
+DEFINE_EVENT(usbssp_log_slot_ctx, usbssp_handle_cmd_disable_slot,
+ TP_PROTO(struct usbssp_slot_ctx *ctx),
+ TP_ARGS(ctx)
+);
+
+DEFINE_EVENT(usbssp_log_slot_ctx, usbssp_reset_device,
+ TP_PROTO(struct usbssp_slot_ctx *ctx),
+ TP_ARGS(ctx)
+);
+
+DEFINE_EVENT(usbssp_log_slot_ctx, usbssp_setup_device_slot,
+ TP_PROTO(struct usbssp_slot_ctx *ctx),
+ TP_ARGS(ctx)
+);
+
+DEFINE_EVENT(usbssp_log_slot_ctx, usbssp_handle_cmd_addr_dev,
+ TP_PROTO(struct usbssp_slot_ctx *ctx),
+ TP_ARGS(ctx)
+);
+
+DEFINE_EVENT(usbssp_log_slot_ctx, usbssp_handle_cmd_reset_dev,
+ TP_PROTO(struct usbssp_slot_ctx *ctx),
+ TP_ARGS(ctx)
+);
+
+DEFINE_EVENT(usbssp_log_slot_ctx, usbssp_handle_cmd_set_deq,
+ TP_PROTO(struct usbssp_slot_ctx *ctx),
+ TP_ARGS(ctx)
+);
+
+DEFINE_EVENT(usbssp_log_slot_ctx, usbssp_configure_endpoint,
+ TP_PROTO(struct usbssp_slot_ctx *ctx),
+ TP_ARGS(ctx)
+);
+
+DECLARE_EVENT_CLASS(usbssp_log_ring,
+ TP_PROTO(struct usbssp_ring *ring),
+ TP_ARGS(ring),
+ TP_STRUCT__entry(
+ __field(u32, type)
+ __field(void *, ring)
+ __field(dma_addr_t, enq)
+ __field(dma_addr_t, deq)
+ __field(dma_addr_t, enq_seg)
+ __field(dma_addr_t, deq_seg)
+ __field(unsigned int, num_segs)
+ __field(unsigned int, stream_id)
+ __field(unsigned int, cycle_state)
+ __field(unsigned int, num_trbs_free)
+ __field(unsigned int, bounce_buf_len)
+ ),
+ TP_fast_assign(
+ __entry->ring = ring;
+ __entry->type = ring->type;
+ __entry->num_segs = ring->num_segs;
+ __entry->stream_id = ring->stream_id;
+ __entry->enq_seg = ring->enq_seg->dma;
+ __entry->deq_seg = ring->deq_seg->dma;
+ __entry->cycle_state = ring->cycle_state;
+ __entry->num_trbs_free = ring->num_trbs_free;
+ __entry->bounce_buf_len = ring->bounce_buf_len;
+ __entry->enq = usbssp_trb_virt_to_dma(ring->enq_seg, ring->enqueue);
+ __entry->deq = usbssp_trb_virt_to_dma(ring->deq_seg, ring->dequeue);
+ ),
+ TP_printk("%s %p: enq %pad(%pad) deq %pad(%pad) segs %d stream %d free_trbs %d bounce %d cycle %d",
+ usbssp_ring_type_string(__entry->type), __entry->ring,
+ &__entry->enq, &__entry->enq_seg,
+ &__entry->deq, &__entry->deq_seg,
+ __entry->num_segs,
+ __entry->stream_id,
+ __entry->num_trbs_free,
+ __entry->bounce_buf_len,
+ __entry->cycle_state
+ )
+);
+
+DEFINE_EVENT(usbssp_log_ring, usbssp_ring_alloc,
+ TP_PROTO(struct usbssp_ring *ring),
+ TP_ARGS(ring)
+);
+
+DEFINE_EVENT(usbssp_log_ring, usbssp_ring_free,
+ TP_PROTO(struct usbssp_ring *ring),
+ TP_ARGS(ring)
+);
+
+DEFINE_EVENT(usbssp_log_ring, usbssp_ring_expansion,
+ TP_PROTO(struct usbssp_ring *ring),
+ TP_ARGS(ring)
+);
+
+DEFINE_EVENT(usbssp_log_ring, usbssp_inc_enq,
+ TP_PROTO(struct usbssp_ring *ring),
+ TP_ARGS(ring)
+);
+
+DEFINE_EVENT(usbssp_log_ring, usbssp_inc_deq,
+ TP_PROTO(struct usbssp_ring *ring),
+ TP_ARGS(ring)
+);
+
+DECLARE_EVENT_CLASS(usbssp_log_portsc,
+ TP_PROTO(u32 portnum, u32 portsc),
+ TP_ARGS(portnum, portsc),
+ TP_STRUCT__entry(
+ __field(u32, portnum)
+ __field(u32, portsc)
+ ),
+ TP_fast_assign(
+ __entry->portnum = portnum;
+ __entry->portsc = portsc;
+ ),
+ TP_printk("port-%d: %s",
+ __entry->portnum,
+ usbssp_decode_portsc(__entry->portsc)
+ )
+);
+
+DEFINE_EVENT(usbssp_log_portsc, usbssp_handle_port_status,
+ TP_PROTO(u32 portnum, u32 portsc),
+ TP_ARGS(portnum, portsc)
+);
+
+#endif /* __USBSSP_TRACE_H */
+/* this part must be outside header guard */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE gadget-trace
+
+#include <trace/define_trace.h>
--
2.17.1


2018-07-19 18:03:04

by Pawel Laszczak

[permalink] [raw]
Subject: [PATCH 19/31] usb: usbssp: added support for USB enumeration process.

Patch implements a set of functions used during
USB device enumeration process. Added code analyzes
received SETUP packet and decides whether packet should
be handled by usbssp driver or should be delegated
to gadget core driver.

This patch also introduces usbssp_ep0_stall function that
sends STALL to host when received SETUP packet can't be
handled.

Signed-off-by: Pawel Laszczak <[email protected]>
---
drivers/usb/usbssp/gadget-ep0.c | 511 ++++++++++++++++++++++++++++++-
drivers/usb/usbssp/gadget-if.c | 32 ++
drivers/usb/usbssp/gadget-ring.c | 18 ++
drivers/usb/usbssp/gadget.c | 43 ++-
drivers/usb/usbssp/gadget.h | 9 +
5 files changed, 611 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/usbssp/gadget-ep0.c b/drivers/usb/usbssp/gadget-ep0.c
index c889a3102740..3b7066875d46 100644
--- a/drivers/usb/usbssp/gadget-ep0.c
+++ b/drivers/usb/usbssp/gadget-ep0.c
@@ -15,9 +15,518 @@
#include <linux/usb/composite.h>
#include "gadget-trace.h"

+static void usbssp_ep0_stall(struct usbssp_udc *usbssp_data)
+{
+ struct usbssp_ep *dep;
+ int ret = 0;
+
+ dep = &usbssp_data->devs.eps[0];
+ if (usbssp_data->three_stage_setup) {
+ dev_dbg(usbssp_data->dev, "Send STALL on Data Stage\n");
+ ret = usbssp_halt_endpoint(usbssp_data, dep, true);
+
+ /*
+ * Finishing SETUP transfer by removing request
+ * from pending list
+ */
+ if (!list_empty(&dep->pending_list)) {
+ struct usbssp_request *req;
+
+ req = next_request(&dep->pending_list);
+ usbssp_giveback_request_in_irq(usbssp_data,
+ req->td, -ECONNRESET);
+ dep->ep_state = USBSSP_EP_ENABLED;
+ }
+ } else {
+ dev_dbg(usbssp_data->dev, "Send STALL on Status Stage\n");
+ dep->ep_state |= EP0_HALTED_STATUS;
+ usbssp_status_stage(usbssp_data);
+ }
+ usbssp_data->delayed_status = false;
+}
+
+static int usbssp_ep0_delegate_req(struct usbssp_udc *usbssp_data,
+ struct usb_ctrlrequest *ctrl)
+{
+ int ret;
+
+ dev_dbg(usbssp_data->dev, "Delagate request to gadget driver\n");
+ spin_unlock(&usbssp_data->irq_thread_lock);
+
+ ret = usbssp_data->gadget_driver->setup(&usbssp_data->gadget, ctrl);
+ spin_lock(&usbssp_data->irq_thread_lock);
+
+ return ret;
+}
+
+static int usbssp_ep0_set_config(struct usbssp_udc *usbssp_data,
+ struct usb_ctrlrequest *ctrl)
+{
+ enum usb_device_state state = usbssp_data->gadget.state;
+ u32 cfg;
+ int ret;
+
+ cfg = le16_to_cpu(ctrl->wValue);
+ switch (state) {
+ case USB_STATE_DEFAULT:
+ dev_err(usbssp_data->dev,
+ "Error: Set Config request from Default state\n");
+ return -EINVAL;
+ case USB_STATE_ADDRESS:
+ dev_dbg(usbssp_data->dev,
+ "Set Configuration from addressed state\n");
+ ret = usbssp_ep0_delegate_req(usbssp_data, ctrl);
+ /* if the cfg matches and the cfg is non zero */
+ if (cfg && (!ret || (ret == USB_GADGET_DELAYED_STATUS))) {
+ /*
+ * only change state if set_config has already
+ * been processed. If gadget driver returns
+ * USB_GADGET_DELAYED_STATUS, we will wait
+ * to change the state on the next usbssp_enqueue()
+ */
+ if (ret == 0) {
+ dev_info(usbssp_data->dev,
+ "Device has been configured\n");
+ usb_gadget_set_state(&usbssp_data->gadget,
+ USB_STATE_CONFIGURED);
+ }
+ }
+ break;
+ case USB_STATE_CONFIGURED:
+ dev_dbg(usbssp_data->dev,
+ "Set Configuration from Configured state\n");
+ ret = usbssp_ep0_delegate_req(usbssp_data, ctrl);
+ if (!cfg && !ret) {
+ dev_info(usbssp_data->dev, "reconfigured device\n");
+ usb_gadget_set_state(&usbssp_data->gadget,
+ USB_STATE_ADDRESS);
+ }
+ break;
+ default:
+ dev_err(usbssp_data->dev,
+ "Set Configuration - incorrect device state\n");
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int usbssp_ep0_set_address(struct usbssp_udc *usbssp_data,
+ struct usb_ctrlrequest *ctrl)
+{
+ enum usb_device_state state = usbssp_data->gadget.state;
+ u32 addr;
+ unsigned int slot_state;
+ struct usbssp_slot_ctx *slot_ctx;
+ int dev_state = 0;
+
+ addr = le16_to_cpu(ctrl->wValue);
+ if (addr > 127) {
+ dev_err(usbssp_data->dev, "invalid device address %d\n", addr);
+ return -EINVAL;
+ }
+
+ slot_ctx = usbssp_get_slot_ctx(usbssp_data, usbssp_data->devs.out_ctx);
+ dev_state = GET_SLOT_STATE(le32_to_cpu(slot_ctx->dev_state));
+
+ if (state == USB_STATE_CONFIGURED) {
+ dev_err(usbssp_data->dev,
+ "can't SetAddress() from Configured State\n");
+ return -EINVAL;
+ }
+
+ usbssp_data->device_address = le16_to_cpu(ctrl->wValue);
+
+ slot_ctx = usbssp_get_slot_ctx(usbssp_data, usbssp_data->devs.out_ctx);
+ slot_state = GET_SLOT_STATE(le32_to_cpu(slot_ctx->dev_state));
+
+ if (slot_state == SLOT_STATE_ADDRESSED) {
+ /*Reset Device Command*/
+ usbssp_data->defered_event &= ~EVENT_USB_RESET;
+ queue_work(usbssp_data->bottom_irq_wq,
+ &usbssp_data->bottom_irq);
+ usbssp_reset_device(usbssp_data);
+ }
+
+ /*set device address*/
+ usbssp_address_device(usbssp_data);
+
+ if (addr)
+ usb_gadget_set_state(&usbssp_data->gadget, USB_STATE_ADDRESS);
+ else
+ usb_gadget_set_state(&usbssp_data->gadget, USB_STATE_DEFAULT);
+ return 0;
+}
+
int usbssp_status_stage(struct usbssp_udc *usbssp_data)
{
- /*TODO; function must to be implemented*/
+ struct usbssp_ring *ep_ring;
+ int ret;
+ struct usbssp_ep *dep;
+
+ dep = &usbssp_data->devs.eps[0];
+ ep_ring = usbssp_data->devs.eps[0].ring;
+
+ dev_dbg(usbssp_data->dev, "Enqueue Status Stage\n");
+ usbssp_data->ep0state = USBSSP_EP0_STATUS_PHASE;
+ usbssp_data->usb_req_ep0_in.request.length = 0;
+ ret = usbssp_enqueue(usbssp_data->usb_req_ep0_in.dep,
+ &usbssp_data->usb_req_ep0_in);
+ return ret;
+}
+
+
+static int usbssp_ep0_handle_feature_u1(struct usbssp_udc *usbssp_data,
+ enum usb_device_state state, int set)
+{
+ __le32 __iomem *port_regs;
+ u32 temp;
+
+ if (state != USB_STATE_CONFIGURED) {
+ dev_err(usbssp_data->dev,
+ "Error: can't change U1 - incorrect device state\n");
+ return -EINVAL;
+ }
+
+ if ((usbssp_data->gadget.speed != USB_SPEED_SUPER) &&
+ (usbssp_data->gadget.speed != USB_SPEED_SUPER_PLUS)) {
+ dev_err(usbssp_data->dev,
+ "Error: U1 is supported only for SS and SSP\n");
+ return -EINVAL;
+ }
+
+ port_regs = usbssp_get_port_io_addr(usbssp_data);
+
+ temp = readl(port_regs+PORTPMSC);
+ temp &= ~PORT_U1_TIMEOUT_MASK;
+
+ if (set)
+ temp |= PORT_U1_TIMEOUT(1);
+ else
+ temp |= PORT_U1_TIMEOUT(0);
+
+ dev_info(usbssp_data->dev, "U1 %s\n", set ? "enabled" : "disabled");
+ writel(temp, port_regs+PORTPMSC);
+
+ usbssp_status_stage(usbssp_data);
+ return 0;
+}
+
+static int usbssp_ep0_handle_feature_u2(struct usbssp_udc *usbssp_data,
+ enum usb_device_state state, int set)
+{
+ __le32 __iomem *port_regs;
+ u32 temp;
+
+ if (state != USB_STATE_CONFIGURED) {
+ dev_err(usbssp_data->dev,
+ "Error: can't change U2 - incorrect device state\n");
+ return -EINVAL;
+ }
+ if ((usbssp_data->gadget.speed != USB_SPEED_SUPER) &&
+ (usbssp_data->gadget.speed != USB_SPEED_SUPER_PLUS)) {
+ dev_err(usbssp_data->dev,
+ "Error: U2 is supported only for SS and SSP\n");
+ return -EINVAL;
+ }
+
+ port_regs = usbssp_get_port_io_addr(usbssp_data);
+ temp = readl(port_regs+PORTPMSC);
+ temp &= ~PORT_U1_TIMEOUT_MASK;
+
+ if (set)
+ temp |= PORT_U2_TIMEOUT(1);
+ else
+ temp |= PORT_U2_TIMEOUT(0);
+
+ writel(temp, port_regs+PORTPMSC);
+ dev_info(usbssp_data->dev, "U2 %s\n", set ? "enabled" : "disabled");
+
+ usbssp_status_stage(usbssp_data);
+ return 0;
+}
+
+static int usbssp_ep0_handle_feature_device(struct usbssp_udc *usbssp_data,
+ struct usb_ctrlrequest *ctrl,
+ int set)
+{
+ enum usb_device_state state;
+ u32 wValue;
+ u32 wIndex;
+ int ret = 0;
+
+ wValue = le16_to_cpu(ctrl->wValue);
+ wIndex = le16_to_cpu(ctrl->wIndex);
+ state = usbssp_data->gadget.state;
+
+ switch (wValue) {
+ case USB_DEVICE_REMOTE_WAKEUP:
+ usbssp_data->remote_wakeup_allowed = (set) ? 1 : 0;
+ break;
+ /*
+ * 9.4.1 says only only for SS, in AddressState only for
+ * default control pipe
+ */
+ case USB_DEVICE_U1_ENABLE:
+ ret = usbssp_ep0_handle_feature_u1(usbssp_data, state, set);
+ break;
+ case USB_DEVICE_U2_ENABLE:
+ ret = usbssp_ep0_handle_feature_u2(usbssp_data, state, set);
+ break;
+ case USB_DEVICE_LTM_ENABLE:
+ ret = -EINVAL;
+ break;
+ default:
+ dev_err(usbssp_data->dev, "%s Feature Request not supported\n",
+ (set) ? "Set" : "Clear");
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int usbssp_ep0_handle_feature_intf(struct usbssp_udc *usbssp_data,
+ struct usb_ctrlrequest *ctrl,
+ int set)
+{
+ u32 wValue;
+ int ret = 0;
+
+ wValue = le16_to_cpu(ctrl->wValue);
+
+ switch (wValue) {
+ case USB_INTRF_FUNC_SUSPEND:
+ /*TODO: suspend device */
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int usbssp_ep0_handle_feature_endpoint(struct usbssp_udc *usbssp_data,
+ struct usb_ctrlrequest *ctrl, int set)
+{
+ struct usbssp_ep *dep;
+ u32 wValue, wIndex;
+ unsigned int ep_index = 0;
+ struct usbssp_ring *ep_ring;
+ struct usbssp_td *td;
+
+ wValue = le16_to_cpu(ctrl->wValue);
+ wIndex = le16_to_cpu(ctrl->wIndex);
+ ep_index = ((wIndex & USB_ENDPOINT_NUMBER_MASK) << 1);
+
+ if ((wIndex & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)
+ ep_index -= 1;
+
+ dep = &usbssp_data->devs.eps[ep_index];
+ ep_ring = dep->ring;
+
+ switch (wValue) {
+ case USB_ENDPOINT_HALT:
+ if (set == 0 && (dep->ep_state & USBSSP_EP_WEDGE))
+ break;
+
+ usbssp_halt_endpoint(usbssp_data, dep, set);
+
+ td = list_first_entry(&ep_ring->td_list, struct usbssp_td,
+ td_list);
+
+ usbssp_cleanup_halted_endpoint(usbssp_data, ep_index,
+ ep_ring->stream_id, td,
+ EP_HARD_RESET);
+ break;
+ default:
+ dev_warn(usbssp_data->dev, "WARN Incorrect wValue %04x\n",
+ wValue);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int usbssp_ep0_handle_feature(struct usbssp_udc *usbssp_data,
+ struct usb_ctrlrequest *ctrl, int set)
+{
+ u32 recip;
+ int ret;
+
+ recip = ctrl->bRequestType & USB_RECIP_MASK;
+
+ switch (recip) {
+ case USB_RECIP_DEVICE:
+ ret = usbssp_ep0_handle_feature_device(usbssp_data, ctrl, set);
+ break;
+ case USB_RECIP_INTERFACE:
+ ret = usbssp_ep0_handle_feature_intf(usbssp_data, ctrl, set);
+ break;
+ case USB_RECIP_ENDPOINT:
+ ret = usbssp_ep0_handle_feature_endpoint(usbssp_data,
+ ctrl, set);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int usbssp_ep0_set_sel(struct usbssp_udc *usbssp_data,
+ struct usb_ctrlrequest *ctrl)
+{
+ struct usbssp_ep *dep;
+ enum usb_device_state state = usbssp_data->gadget.state;
+ u16 wLength;
+ int ret = 0;
+
+ if (state == USB_STATE_DEFAULT)
+ return -EINVAL;
+
+ wLength = le16_to_cpu(ctrl->wLength);
+
+ if (wLength != 6) {
+ dev_err(usbssp_data->dev, "Set SEL should be 6 bytes, got %d\n",
+ wLength);
+ return -EINVAL;
+ }
+
+ /*
+ * To handle Set SEL we need to receive 6 bytes from Host. So let's
+ * queue a usb_request for 6 bytes.
+ */
+ dep = &usbssp_data->devs.eps[0];
+
+ usbssp_data->usb_req_ep0_in.request.length = 0x6;
+ usbssp_data->usb_req_ep0_in.request.buf = usbssp_data->setup_buf;
+
+ ret = usbssp_enqueue(usbssp_data->usb_req_ep0_in.dep,
+ &usbssp_data->usb_req_ep0_in);
+ if (ret) {
+ dev_err(usbssp_data->dev, "Error in Set Sel\n");
+ return ret;
+ }
return 0;
}

+static int usbssp_ep0_std_request(struct usbssp_udc *usbssp_data,
+ struct usb_ctrlrequest *ctrl)
+{
+ int ret = 0;
+
+ usbssp_data->bos_event_detected = 0;
+
+ switch (ctrl->bRequest) {
+ case USB_REQ_GET_STATUS:
+ dev_info(usbssp_data->dev, "Request GET_STATUS\n");
+ /*TODO:*/
+ //ret = usbssp_ep0_handle_status(usbssp_data, ctrl);
+ break;
+ case USB_REQ_CLEAR_FEATURE:
+ dev_info(usbssp_data->dev, "Request CLEAR_FEATURE\n");
+ ret = usbssp_ep0_handle_feature(usbssp_data, ctrl, 0);
+ break;
+ case USB_REQ_SET_FEATURE:
+ dev_info(usbssp_data->dev, "Request SET_FEATURE\n");
+ ret = usbssp_ep0_handle_feature(usbssp_data, ctrl, 1);
+ break;
+ case USB_REQ_SET_ADDRESS:
+ dev_info(usbssp_data->dev, "Request SET_ADDRESS\n");
+ ret = usbssp_ep0_set_address(usbssp_data, ctrl);
+ break;
+ case USB_REQ_SET_CONFIGURATION:
+ dev_info(usbssp_data->dev, "Request SET_CONFIGURATION\n");
+ ret = usbssp_ep0_set_config(usbssp_data, ctrl);
+ break;
+ case USB_REQ_SET_SEL:
+ dev_info(usbssp_data->dev, "Request SET_SEL\n");
+ ret = usbssp_ep0_set_sel(usbssp_data, ctrl);
+ break;
+ case USB_REQ_SET_ISOCH_DELAY:
+ dev_info(usbssp_data->dev, "Request SET_ISOCH_DELAY\n");
+ /*TODO:*/
+ //ret = usbssp_ep0_set_isoch_delay(usbssp_data, ctrl);
+ break;
+ default:
+ if ((le16_to_cpu(ctrl->wValue) >> 8) == USB_DT_BOS &&
+ ctrl->bRequest == USB_REQ_GET_DESCRIPTOR) {
+ /*
+ * It will be handled after Status Stage phase
+ * in usbssp_gadget_giveback
+ */
+ usbssp_data->bos_event_detected = true;
+ }
+ ret = usbssp_ep0_delegate_req(usbssp_data, ctrl);
+ break;
+ }
+ return ret;
+}
+
+int usbssp_setup_analyze(struct usbssp_udc *usbssp_data)
+{
+ int ret = -EINVAL;
+ struct usb_ctrlrequest *ctrl = &usbssp_data->setup;
+ u32 len = 0;
+ struct usbssp_device *priv_dev;
+
+ ctrl = &usbssp_data->setup;
+
+ dev_info(usbssp_data->dev,
+ "SETUP BRT: %02x BR: %02x V: %04x I: %04x L: %04x\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ le16_to_cpu(ctrl->wValue), le16_to_cpu(ctrl->wIndex),
+ le16_to_cpu(ctrl->wLength));
+
+ if (!usbssp_data->gadget_driver)
+ goto out;
+
+ priv_dev = &usbssp_data->devs;
+
+ /*
+ * First of all, if endpoint 0 was halted driver has to
+ * recovery it.
+ */
+ if (priv_dev->eps[0].ep_state & EP_HALTED) {
+ dev_dbg(usbssp_data->dev,
+ "Ep0 Halted - restoring to nomral state\n");
+ usbssp_halt_endpoint(usbssp_data, &priv_dev->eps[0], 0);
+ }
+
+ /*
+ * Finishing previous SETUP transfer by removing request from
+ * list and informing upper layer
+ */
+ if (!list_empty(&priv_dev->eps[0].pending_list)) {
+ struct usbssp_request *req;
+
+ dev_dbg(usbssp_data->dev,
+ "Deleting previous Setup transaction\n");
+ req = next_request(&priv_dev->eps[0].pending_list);
+ usbssp_dequeue(&priv_dev->eps[0], req);
+ }
+
+ len = le16_to_cpu(ctrl->wLength);
+ if (!len) {
+ usbssp_data->three_stage_setup = false;
+ usbssp_data->ep0_expect_in = false;
+ } else {
+ usbssp_data->three_stage_setup = true;
+ usbssp_data->ep0_expect_in =
+ !!(ctrl->bRequestType & USB_DIR_IN);
+ }
+
+ if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
+ ret = usbssp_ep0_std_request(usbssp_data, ctrl);
+ else
+ ret = usbssp_ep0_delegate_req(usbssp_data, ctrl);
+
+ if (ret == USB_GADGET_DELAYED_STATUS) {
+ dev_dbg(usbssp_data->dev, "Status Stage delayed\n");
+ usbssp_data->delayed_status = true;
+ }
+
+out:
+ if (ret < 0)
+ usbssp_ep0_stall(usbssp_data);
+
+ return ret;
+}
diff --git a/drivers/usb/usbssp/gadget-if.c b/drivers/usb/usbssp/gadget-if.c
index ef466aa47f56..dbff5a400676 100644
--- a/drivers/usb/usbssp/gadget-if.c
+++ b/drivers/usb/usbssp/gadget-if.c
@@ -133,6 +133,38 @@ static const struct usb_ep_ops usbssp_gadget_ep_ops = {
.set_wedge = usbssp_gadget_ep_set_wedge,
};

+void usbssp_gadget_giveback(struct usbssp_ep *ep_priv,
+ struct usbssp_request *req_priv, int status)
+{
+ struct usbssp_udc *usbssp_data = ep_priv->usbssp_data;
+
+ list_del(&req_priv->list);
+
+ if (req_priv->request.status == -EINPROGRESS)
+ req_priv->request.status = status;
+
+ usb_gadget_unmap_request_by_dev(usbssp_data->dev,
+ &req_priv->request, req_priv->direction);
+
+ trace_usbssp_request_giveback(&req_priv->request);
+
+ if (in_interrupt())
+ spin_unlock(&usbssp_data->lock);
+ else
+ spin_unlock(&usbssp_data->irq_thread_lock);
+
+ if (req_priv != &usbssp_data->usb_req_ep0_in) {
+ usb_gadget_giveback_request(&ep_priv->endpoint,
+ &req_priv->request);
+ }
+
+ if (in_interrupt())
+ spin_lock(&usbssp_data->lock);
+ else
+ spin_lock(&usbssp_data->irq_thread_lock);
+
+}
+
static struct usb_endpoint_descriptor usbssp_gadget_ep0_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
diff --git a/drivers/usb/usbssp/gadget-ring.c b/drivers/usb/usbssp/gadget-ring.c
index 59ba92494a56..1b57f5180115 100644
--- a/drivers/usb/usbssp/gadget-ring.c
+++ b/drivers/usb/usbssp/gadget-ring.c
@@ -310,6 +310,24 @@ static void ring_doorbell_for_active_rings(struct usbssp_udc *usbssp_data,
}
}

+/* Must be called with usbssp_data->lock held in interrupt context
+ * or usbssp_data->irq_thread_lock from thread conext (defered interrupt)
+ */
+void usbssp_giveback_request_in_irq(struct usbssp_udc *usbssp_data,
+ struct usbssp_td *cur_td,
+ int status)
+{
+ struct usb_request *req;
+ struct usbssp_request *req_priv;
+
+ req_priv = cur_td->priv_request;
+ req = &req_priv->request;
+
+ usbssp_request_free_priv(req_priv);
+
+ usbssp_gadget_giveback(req_priv->dep, req_priv, status);
+}
+
/*
* When we get a command completion for a Stop Endpoint Command, we need to
* stop timer and clear EP_STOP_CMD_PENDING flag.
diff --git a/drivers/usb/usbssp/gadget.c b/drivers/usb/usbssp/gadget.c
index 5102dd31c881..2fd6d27ef0bd 100644
--- a/drivers/usb/usbssp/gadget.c
+++ b/drivers/usb/usbssp/gadget.c
@@ -94,7 +94,9 @@ void usbssp_bottom_irq(struct work_struct *work)

/*handle setup packet*/
if (usbssp_data->defered_event & EVENT_SETUP_PACKET) {
- /*TODO: implement handling of SETUP packet*/
+ dev_dbg(usbssp_data->dev, "Beginning handling SETUP packet\n");
+ usbssp_data->defered_event &= ~EVENT_SETUP_PACKET;
+ usbssp_setup_analyze(usbssp_data);
}

spin_unlock_irqrestore(&usbssp_data->irq_thread_lock,
@@ -397,6 +399,45 @@ static int usbssp_check_args(struct usbssp_udc *usbssp_data,
return 1;
}

+int usbssp_enqueue(struct usbssp_ep *dep, struct usbssp_request *req_priv)
+{
+ /*TODO: this function must be implemented*/
+ return 0;
+}
+
+/*
+ * Remove the request's TD from the endpoint ring. This may cause the DC to stop
+ * USB transfers, potentially stopping in the middle of a TRB buffer. The DC
+ * should pick up where it left off in the TD, unless a Set Transfer Ring
+ * Dequeue Pointer is issued.
+ *
+ * The TRBs that make up the buffers for the canceled request will be "removed"
+ * from the ring. Since the ring is a contiguous structure, they can't be
+ * physically removed. Instead, there are two options:
+ *
+ * 1) If the DC is in the middle of processing the request to be canceled, we
+ * simply move the ring's dequeue pointer past those TRBs using the Set
+ * Transfer Ring Dequeue Pointer command. This will be the common case,
+ * when drivers timeout on the last submitted request and attempt to cancel.
+ *
+ * 2) If the DC is in the middle of a different TD, we turn the TRBs into a
+ * series of 1-TRB transfer no-op TDs. (No-ops shouldn't be chained.) The
+ * DC will need to invalidate the any TRBs it has cached after the stop
+ * endpoint command.
+ *
+ * 3) The TD may have completed by the time the Stop Endpoint Command
+ * completes, so software needs to handle that case too.
+ *
+ * This function should protect against the TD enqueueing code ringing the
+ * doorbell while this code is waiting for a Stop Endpoint command to complete.
+ *
+ */
+int usbssp_dequeue(struct usbssp_ep *ep_priv, struct usbssp_request *req_priv)
+{
+ /*TODO: this function must be implemented*/
+ return 0;
+}
+
int usbssp_halt_endpoint(struct usbssp_udc *usbssp_data, struct usbssp_ep *dep,
int value)
{
diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h
index ea110667e964..d582c9dbe6b4 100644
--- a/drivers/usb/usbssp/gadget.h
+++ b/drivers/usb/usbssp/gadget.h
@@ -1703,6 +1703,7 @@ struct usbssp_ring *usbssp_stream_id_to_ring(struct usbssp_device *dev,
struct usbssp_command *usbssp_alloc_command(struct usbssp_udc *usbssp_data,
bool allocate_completion,
gfp_t mem_flags);
+void usbssp_request_free_priv(struct usbssp_request *req_priv);
void usbssp_free_command(struct usbssp_udc *usbssp_data,
struct usbssp_command *command);

@@ -1790,6 +1791,10 @@ int usbssp_gadget_init(struct usbssp_udc *usbssp_data);
int usbssp_gadget_exit(struct usbssp_udc *usbssp_data);
void usbssp_gadget_free_endpoint(struct usbssp_udc *usbssp_data);
int usbssp_gadget_init_endpoint(struct usbssp_udc *usbssp_data);
+void usbssp_gadget_giveback(struct usbssp_ep *ep_priv,
+ struct usbssp_request *req_priv, int status);
+int usbssp_enqueue(struct usbssp_ep *dep, struct usbssp_request *req_priv);
+int usbssp_dequeue(struct usbssp_ep *dep, struct usbssp_request *req_priv);
unsigned int usbssp_port_speed(unsigned int port_status);
void usbssp_gadget_reset_interrupt(struct usbssp_udc *usbssp_data);
void usbssp_gadget_disconnect_interrupt(struct usbssp_udc *usbssp_data);
@@ -1798,6 +1803,7 @@ int usbssp_halt_endpoint(struct usbssp_udc *usbssp_data,
struct usbssp_ep *dep, int value);
int usbssp_cmd_stop_ep(struct usbssp_udc *usbssp_data, struct usb_gadget *g,
struct usbssp_ep *ep_priv);
+int usbssp_setup_analyze(struct usbssp_udc *usbssp_data);
int usbssp_status_stage(struct usbssp_udc *usbssp_data);

int usbssp_reset_device(struct usbssp_udc *usbssp_data);
@@ -2275,4 +2281,7 @@ struct usbssp_udc;

__le32 __iomem *usbssp_get_port_io_addr(struct usbssp_udc *usbssp_data);

+void usbssp_giveback_request_in_irq(struct usbssp_udc *usbssp_data,
+ struct usbssp_td *cur_td, int status);
+
#endif /* __LINUX_USBSSP_GADGET_H */
--
2.17.1


2018-07-19 18:03:10

by Pawel Laszczak

[permalink] [raw]
Subject: [PATCH 18/31] usb: usbssp: added handling of Port Reset event.

Patch adds functionality used during resetting USB port.
During this process driver must finish all queued transfers,
and enter the controller to default state.

Signed-off-by: Pawel Laszczak <[email protected]>
---
drivers/usb/usbssp/gadget-dbg.c | 9 +
drivers/usb/usbssp/gadget-mem.c | 101 +++++++++++
drivers/usb/usbssp/gadget.c | 305 +++++++++++++++++++++++++++++++-
drivers/usb/usbssp/gadget.h | 13 ++
4 files changed, 427 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/usbssp/gadget-dbg.c b/drivers/usb/usbssp/gadget-dbg.c
index 277617d1a996..a425469d95d9 100644
--- a/drivers/usb/usbssp/gadget-dbg.c
+++ b/drivers/usb/usbssp/gadget-dbg.c
@@ -12,6 +12,15 @@

#include "gadget.h"

+char *usbssp_get_slot_state(struct usbssp_udc *usbssp_data,
+ struct usbssp_container_ctx *ctx)
+{
+ struct usbssp_slot_ctx *slot_ctx = usbssp_get_slot_ctx(usbssp_data, ctx);
+ int state = GET_SLOT_STATE(le32_to_cpu(slot_ctx->dev_state));
+
+ return usbssp_slot_state_string(state);
+}
+
void usbssp_dbg_trace(struct usbssp_udc *usbssp_data,
void (*trace)(struct va_format *),
const char *fmt, ...)
diff --git a/drivers/usb/usbssp/gadget-mem.c b/drivers/usb/usbssp/gadget-mem.c
index 2c220582a5f8..858bee77b0dc 100644
--- a/drivers/usb/usbssp/gadget-mem.c
+++ b/drivers/usb/usbssp/gadget-mem.c
@@ -707,6 +707,107 @@ int usbssp_alloc_priv_device(struct usbssp_udc *usbssp_data, gfp_t flags)
return 0;
}

+void usbssp_copy_ep0_dequeue_into_input_ctx(struct usbssp_udc *usbssp_data)
+{
+ struct usbssp_device *priv_dev;
+ struct usbssp_ep_ctx *ep0_ctx;
+ struct usbssp_ring *ep_ring;
+
+ priv_dev = &usbssp_data->devs;
+ ep0_ctx = usbssp_get_ep_ctx(usbssp_data, priv_dev->in_ctx, 0);
+ ep_ring = priv_dev->eps[0].ring;
+
+ /*
+ * We don't keep track of the dequeue pointer very well after a
+ * Set TR dequeue pointer, so we're setting the dequeue pointer of the
+ * device to our enqueue pointer. This should only be called after a
+ * configured device has reset, so all control transfers should have
+ * been completed or cancelled before the reset.
+ */
+ ep0_ctx->deq = cpu_to_le64(usbssp_trb_virt_to_dma(ep_ring->enq_seg,
+ ep_ring->enqueue) | ep_ring->cycle_state);
+}
+
+/* Setup an DC private device for a Set Address command */
+int usbssp_setup_addressable_priv_dev(struct usbssp_udc *usbssp_data)
+{
+ struct usbssp_device *dev_priv;
+ struct usbssp_ep_ctx *ep0_ctx;
+ struct usbssp_slot_ctx *slot_ctx;
+ u32 max_packets;
+
+ dev_priv = &usbssp_data->devs;
+ /* Slot ID 0 is reserved */
+ if (usbssp_data->slot_id == 0 || !dev_priv->gadget) {
+ dev_warn(usbssp_data->dev,
+ "Slot ID %d is not assigned to this device\n",
+ usbssp_data->slot_id);
+ return -EINVAL;
+ }
+
+ ep0_ctx = usbssp_get_ep_ctx(usbssp_data, dev_priv->in_ctx, 0);
+ slot_ctx = usbssp_get_slot_ctx(usbssp_data, dev_priv->in_ctx);
+
+ /* 3) Only the control endpoint is valid - one endpoint context */
+ slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(1));
+
+ switch (dev_priv->gadget->speed) {
+ case USB_SPEED_SUPER_PLUS:
+ slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_SSP);
+ max_packets = MAX_PACKET(512);
+ break;
+ case USB_SPEED_SUPER:
+ slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_SS);
+ max_packets = MAX_PACKET(512);
+ break;
+ case USB_SPEED_HIGH:
+ slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_HS);
+ max_packets = MAX_PACKET(64);
+ break;
+ case USB_SPEED_FULL:
+ slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_FS);
+ max_packets = MAX_PACKET(64);
+ break;
+ case USB_SPEED_LOW:
+ slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_LS);
+ max_packets = MAX_PACKET(8);
+ break;
+ case USB_SPEED_WIRELESS:
+ dev_dbg(usbssp_data->dev,
+ "USBSSP doesn't support wireless speeds\n");
+ return -EINVAL;
+ default:
+ /* Speed was not set , this shouldn't happen. */
+ return -EINVAL;
+ }
+
+ if (!usbssp_data->devs.port_num)
+ return -EINVAL;
+
+ slot_ctx->dev_info2 |=
+ cpu_to_le32(ROOT_DEV_PORT(usbssp_data->devs.port_num));
+ slot_ctx->dev_state |= (usbssp_data->device_address & DEV_ADDR_MASK);
+
+ ep0_ctx->tx_info = EP_AVG_TRB_LENGTH(0x8);
+
+
+ /* Step 4 - ring already allocated */
+ /* Step 5 */
+ ep0_ctx->ep_info2 = cpu_to_le32(EP_TYPE(CTRL_EP));
+
+ /* EP 0 can handle "burst" sizes of 1, so Max Burst Size field is 0 */
+ ep0_ctx->ep_info2 |= cpu_to_le32(MAX_BURST(0) | ERROR_COUNT(3) |
+ max_packets);
+
+ ep0_ctx->deq = cpu_to_le64(dev_priv->eps[0].ring->first_seg->dma |
+ dev_priv->eps[0].ring->cycle_state);
+
+ trace_usbssp_setup_addressable_priv_device(dev_priv);
+ /* Steps 7 and 8 were done in usbssp_alloc_priv_device() */
+
+ return 0;
+}
+
struct usbssp_command *usbssp_alloc_command(struct usbssp_udc *usbssp_data,
bool allocate_completion,
gfp_t mem_flags)
diff --git a/drivers/usb/usbssp/gadget.c b/drivers/usb/usbssp/gadget.c
index 4dac1b3cbb85..5102dd31c881 100644
--- a/drivers/usb/usbssp/gadget.c
+++ b/drivers/usb/usbssp/gadget.c
@@ -69,7 +69,27 @@ void usbssp_bottom_irq(struct work_struct *work)
}

if (usbssp_data->defered_event & EVENT_USB_RESET) {
- /*TODO: implement handling of USB_RESET*/
+ __le32 __iomem *port_regs;
+ u32 temp;
+
+ dev_dbg(usbssp_data->dev, "Beginning USB reset device sequence\n");
+
+ /*Reset Device Command*/
+ usbssp_data->defered_event &= ~EVENT_USB_RESET;
+ usbssp_reset_device(usbssp_data);
+ usbssp_data->devs.eps[0].ep_state |= USBSSP_EP_ENABLED;
+ usbssp_data->defered_event &= ~EVENT_DEV_CONNECTED;
+
+ usbssp_enable_device(usbssp_data);
+ if ((usbssp_data->gadget.speed == USB_SPEED_SUPER) ||
+ (usbssp_data->gadget.speed == USB_SPEED_SUPER_PLUS)) {
+ dev_dbg(usbssp_data->dev, "Set U1/U2 enable\n");
+ port_regs = usbssp_get_port_io_addr(usbssp_data);
+ temp = readl(port_regs+PORTPMSC);
+ temp &= ~(PORT_U1_TIMEOUT_MASK | PORT_U2_TIMEOUT_MASK);
+ temp |= PORT_U1_TIMEOUT(1) | PORT_U2_TIMEOUT(1);
+ writel(temp, port_regs+PORTPMSC);
+ }
}

/*handle setup packet*/
@@ -488,6 +508,108 @@ int usbssp_halt_endpoint(struct usbssp_udc *usbssp_data, struct usbssp_ep *dep,
return 0;
}

+/*
+ * This submits a Reset Device Command, which will set the device state to 0,
+ * set the device address to 0, and disable all the endpoints except the default
+ * control endpoint. The USB core should come back and call
+ * usbssp_address_device(), and then re-set up the configuration.
+ *
+ * Wait for the Reset Device command to finish. Remove all structures
+ * associated with the endpoints that were disabled. Clear the input device
+ * structure? Reset the control endpoint 0 max packet size?
+ */
+int usbssp_reset_device(struct usbssp_udc *usbssp_data)
+{
+ struct usbssp_device *dev_priv;
+ struct usbssp_command *reset_device_cmd;
+ struct usbssp_slot_ctx *slot_ctx;
+ int slot_state;
+ int ret = 0;
+
+ ret = usbssp_check_args(usbssp_data, NULL, 0, false, __func__);
+ if (ret <= 0)
+ return ret;
+
+ dev_priv = &usbssp_data->devs;
+
+ /* If device is not setup, there is no point in resetting it */
+ slot_ctx = usbssp_get_slot_ctx(usbssp_data, dev_priv->out_ctx);
+ slot_state = GET_SLOT_STATE(le32_to_cpu(slot_ctx->dev_state));
+ pr_info("usbssp_reset_deviceslot_stated\n");
+ if (slot_state == SLOT_STATE_DISABLED ||
+ slot_state == SLOT_STATE_ENABLED ||
+ slot_state == SLOT_STATE_DEFAULT) {
+ dev_dbg(usbssp_data->dev,
+ "Slot in DISABLED/ENABLED state - reset not allowed\n");
+ return 0;
+ }
+
+ trace_usbssp_reset_device(slot_ctx);
+
+ dev_dbg(usbssp_data->dev, "Resetting device with slot ID %u\n",
+ usbssp_data->slot_id);
+
+ /* Allocate the command structure that holds the struct completion.*/
+ reset_device_cmd = usbssp_alloc_command(usbssp_data, true, GFP_ATOMIC);
+
+ if (!reset_device_cmd) {
+ dev_dbg(usbssp_data->dev,
+ "Couldn't allocate command structure.\n");
+ return -ENOMEM;
+ }
+
+ /* Attempt to submit the Reset Device command to the command ring */
+ ret = usbssp_queue_reset_device(usbssp_data, reset_device_cmd);
+ if (ret) {
+ dev_dbg(usbssp_data->dev,
+ "FIXME: allocate a command ring segment\n");
+ goto command_cleanup;
+ }
+ usbssp_ring_cmd_db(usbssp_data);
+
+ spin_unlock_irqrestore(&usbssp_data->irq_thread_lock,
+ usbssp_data->irq_thread_flag);
+
+ /* Wait for the Reset Device command to finish */
+ wait_for_completion(reset_device_cmd->completion);
+ spin_lock_irqsave(&usbssp_data->irq_thread_lock,
+ usbssp_data->irq_thread_flag);
+
+ /*
+ * The Reset Device command can't fail, according to spec,
+ * unless we tried to reset a slot ID that wasn't enabled,
+ * or the device wasn't in the addressed or configured state.
+ */
+ ret = reset_device_cmd->status;
+ switch (ret) {
+ case COMP_COMMAND_ABORTED:
+ case COMP_COMMAND_RING_STOPPED:
+ dev_warn(usbssp_data->dev,
+ "Timeout waiting for reset device command\n");
+ ret = -ETIME;
+ goto command_cleanup;
+ case COMP_SLOT_NOT_ENABLED_ERROR: /*completion code for bad slot ID */
+ case COMP_CONTEXT_STATE_ERROR: /* completion code for same thing */
+ dev_dbg(usbssp_data->dev, "Not freeing device rings.\n");
+ ret = 0;
+ goto command_cleanup;
+ case COMP_SUCCESS:
+ dev_dbg(usbssp_data->dev, "Successful reset device command.\n");
+ break;
+ default:
+ dev_warn(usbssp_data->dev, "Unknown completion code %u for "
+ "reset device command.\n", ret);
+ ret = -EINVAL;
+ goto command_cleanup;
+ }
+
+ ret = 0;
+
+command_cleanup:
+ usbssp_free_command(usbssp_data, reset_device_cmd);
+ return ret;
+}
+
/*
* At this point, the struct usb_device is about to go away, the device has
* disconnected, and all traffic has been stopped and the endpoints have been
@@ -598,6 +720,187 @@ int usbssp_alloc_dev(struct usbssp_udc *usbssp_data)
return 0;
}

+/*
+ * Issue an Address Device command
+ */
+static int usbssp_setup_device(struct usbssp_udc *usbssp_data,
+ enum usbssp_setup_dev setup)
+{
+ const char *act = setup == SETUP_CONTEXT_ONLY ? "context" : "address";
+ struct usbssp_device *dev_priv;
+ int ret = 0;
+ struct usbssp_slot_ctx *slot_ctx;
+ struct usbssp_input_control_ctx *ctrl_ctx;
+ u64 temp_64;
+ struct usbssp_command *command = NULL;
+ int dev_state = 0;
+ int slot_id = usbssp_data->slot_id;
+
+ if (usbssp_data->usbssp_state) {/* dying, removing or halted */
+ ret = -ESHUTDOWN;
+ goto out;
+ }
+
+ if (!slot_id) {
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_address,
+ "Bad Slot ID %d", slot_id);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ dev_priv = &usbssp_data->devs;
+
+ slot_ctx = usbssp_get_slot_ctx(usbssp_data, dev_priv->out_ctx);
+ trace_usbssp_setup_device_slot(slot_ctx);
+
+ dev_state = GET_SLOT_STATE(le32_to_cpu(slot_ctx->dev_state));
+
+ if (setup == SETUP_CONTEXT_ONLY) {
+ if (dev_state == SLOT_STATE_DEFAULT) {
+ dev_dbg(usbssp_data->dev,
+ "Slot already in default state\n");
+ goto out;
+ }
+ }
+
+ command = usbssp_alloc_command(usbssp_data, true, GFP_ATOMIC);
+ if (!command) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ command->in_ctx = dev_priv->in_ctx;
+
+ slot_ctx = usbssp_get_slot_ctx(usbssp_data, dev_priv->in_ctx);
+ ctrl_ctx = usbssp_get_input_control_ctx(dev_priv->in_ctx);
+
+ if (!ctrl_ctx) {
+ dev_warn(usbssp_data->dev,
+ "%s: Could not get input context, bad type.\n",
+ __func__);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * If this is the first Set Address (BSR=0) or driver trays
+ * transition to Default (BSR=1) since device plug-in or
+ * priv device reallocation after a resume with an USBSSP power loss,
+ * then set up the slot context or update device address in slot
+ * context.
+ */
+ if (!slot_ctx->dev_info || dev_state == SLOT_STATE_DEFAULT)
+ usbssp_setup_addressable_priv_dev(usbssp_data);
+
+ if (dev_state == SLOT_STATE_DEFAULT)
+ usbssp_copy_ep0_dequeue_into_input_ctx(usbssp_data);
+
+ ctrl_ctx->add_flags = cpu_to_le32(SLOT_FLAG | EP0_FLAG);
+ ctrl_ctx->drop_flags = 0;
+
+ trace_usbssp_address_ctx(usbssp_data, dev_priv->in_ctx,
+ le32_to_cpu(slot_ctx->dev_info) >> 27);
+
+ ret = usbssp_queue_address_device(usbssp_data, command,
+ dev_priv->in_ctx->dma, setup);
+
+ if (ret) {
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_address,
+ "Prabably command ring segment is full");
+ goto out;
+ }
+
+ usbssp_ring_cmd_db(usbssp_data);
+
+ spin_unlock_irqrestore(&usbssp_data->irq_thread_lock,
+ usbssp_data->irq_thread_flag);
+ wait_for_completion(command->completion);
+ spin_lock_irqsave(&usbssp_data->irq_thread_lock,
+ usbssp_data->irq_thread_flag);
+
+ switch (command->status) {
+ case COMP_COMMAND_ABORTED:
+ case COMP_COMMAND_RING_STOPPED:
+ dev_warn(usbssp_data->dev,
+ "Timeout while waiting for setup device command\n");
+ ret = -ETIME;
+ break;
+ case COMP_CONTEXT_STATE_ERROR:
+ case COMP_SLOT_NOT_ENABLED_ERROR:
+ dev_err(usbssp_data->dev,
+ "Setup ERROR: setup %s command for slot %d.\n",
+ act, slot_id);
+ ret = -EINVAL;
+ break;
+ case COMP_INCOMPATIBLE_DEVICE_ERROR:
+ dev_warn(usbssp_data->dev,
+ "ERROR: Incompatible device for setup %s command\n",
+ act);
+ ret = -ENODEV;
+ break;
+ case COMP_SUCCESS:
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_address,
+ "Successful setup %s command", act);
+ break;
+ default:
+ dev_err(usbssp_data->dev,
+ "ERROR: unexpected setup %s command completion code 0x%x.\n",
+ act, command->status);
+
+ trace_usbssp_address_ctx(usbssp_data, dev_priv->out_ctx, 1);
+ ret = -EINVAL;
+ break;
+ }
+
+ if (ret)
+ goto out;
+
+ temp_64 = usbssp_read_64(usbssp_data, &usbssp_data->op_regs->dcbaa_ptr);
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_address,
+ "Op regs DCBAA ptr = %#016llx", temp_64);
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_address,
+ "Slot ID %d dcbaa entry @%p = %#016llx",
+ slot_id, &usbssp_data->dcbaa->dev_context_ptrs[slot_id],
+ (unsigned long long)
+ le64_to_cpu(usbssp_data->dcbaa->dev_context_ptrs[slot_id]));
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_address,
+ "Output Context DMA address = %#08llx",
+ (unsigned long long)dev_priv->out_ctx->dma);
+
+ trace_usbssp_address_ctx(usbssp_data, dev_priv->in_ctx,
+ le32_to_cpu(slot_ctx->dev_info) >> 27);
+
+ slot_ctx = usbssp_get_slot_ctx(usbssp_data, dev_priv->out_ctx);
+ trace_usbssp_address_ctx(usbssp_data, dev_priv->out_ctx,
+ le32_to_cpu(slot_ctx->dev_info) >> 27);
+ /* Zero the input context control for later use */
+ ctrl_ctx->add_flags = 0;
+ ctrl_ctx->drop_flags = 0;
+
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_address,
+ "Internal device address = %d",
+ le32_to_cpu(slot_ctx->dev_state) & DEV_ADDR_MASK);
+
+ if (setup == SETUP_CONTEXT_ADDRESS)
+ usbssp_status_stage(usbssp_data);
+out:
+ if (command) {
+ kfree(command->completion);
+ kfree(command);
+ }
+ return ret;
+}
+
+int usbssp_address_device(struct usbssp_udc *usbssp_data)
+{
+ return usbssp_setup_device(usbssp_data, SETUP_CONTEXT_ADDRESS);
+}
+
+int usbssp_enable_device(struct usbssp_udc *usbssp_data)
+{
+ return usbssp_setup_device(usbssp_data, SETUP_CONTEXT_ONLY);
+}
+
int usbssp_gen_setup(struct usbssp_udc *usbssp_data)
{
int retval;
diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h
index 64168ea16dbf..ea110667e964 100644
--- a/drivers/usb/usbssp/gadget.h
+++ b/drivers/usb/usbssp/gadget.h
@@ -1679,6 +1679,8 @@ static inline void usbssp_write_64(struct usbssp_udc *usbssp_data,
}

/* USBSSP memory management */
+char *usbssp_get_slot_state(struct usbssp_udc *usbssp_data,
+ struct usbssp_container_ctx *ctx);
void usbssp_dbg_trace(struct usbssp_udc *usbssp_data,
void (*trace)(struct va_format *),
const char *fmt, ...);
@@ -1687,6 +1689,8 @@ void usbssp_mem_cleanup(struct usbssp_udc *usbssp_data);
int usbssp_mem_init(struct usbssp_udc *usbssp_data, gfp_t flags);
void usbssp_free_priv_device(struct usbssp_udc *usbssp_data);
int usbssp_alloc_priv_device(struct usbssp_udc *usbssp_data, gfp_t flags);
+int usbssp_setup_addressable_priv_dev(struct usbssp_udc *usbssp_data);
+void usbssp_copy_ep0_dequeue_into_input_ctx(struct usbssp_udc *usbssp_data);
unsigned int usbssp_get_endpoint_index(const struct usb_endpoint_descriptor *desc);
unsigned int usbssp_last_valid_endpoint(u32 added_ctxs);
int usbssp_ring_expansion(struct usbssp_udc *usbssp_data,
@@ -1724,6 +1728,8 @@ irqreturn_t usbssp_irq(int irq, void *priv);

int usbssp_alloc_dev(struct usbssp_udc *usbssp_data);
void usbssp_free_dev(struct usbssp_udc *usbssp_data);
+int usbssp_address_device(struct usbssp_udc *usbssp_data);
+int usbssp_enable_device(struct usbssp_udc *usbssp_data);
/* USBSSP ring, segment, TRB, and TD functions */
dma_addr_t usbssp_trb_virt_to_dma(struct usbssp_segment *seg,
union usbssp_trb *trb);
@@ -1738,6 +1744,10 @@ int usbssp_is_vendor_info_code(struct usbssp_udc *usbssp_data,
void usbssp_ring_cmd_db(struct usbssp_udc *usbssp_data);
int usbssp_queue_slot_control(struct usbssp_udc *usbssp_data,
struct usbssp_command *cmd, u32 trb_type);
+int usbssp_queue_address_device(struct usbssp_udc *usbssp_data,
+ struct usbssp_command *cmd,
+ dma_addr_t in_ctx_ptr,
+ enum usbssp_setup_dev setup);
int usbssp_queue_stop_endpoint(struct usbssp_udc *usbssp_data,
struct usbssp_command *cmd,
unsigned int ep_index, int suspend);
@@ -1753,6 +1763,8 @@ void usbssp_cleanup_halted_endpoint(struct usbssp_udc *usbssp_data,
int usbssp_queue_halt_endpoint(struct usbssp_udc *usbssp_data,
struct usbssp_command *cmd,
unsigned int ep_index);
+int usbssp_queue_reset_device(struct usbssp_udc *usbssp_data,
+ struct usbssp_command *cmd);
void usbssp_handle_command_timeout(struct work_struct *work);

void usbssp_cleanup_command_queue(struct usbssp_udc *usbssp_data);
@@ -1788,6 +1800,7 @@ int usbssp_cmd_stop_ep(struct usbssp_udc *usbssp_data, struct usb_gadget *g,
struct usbssp_ep *ep_priv);
int usbssp_status_stage(struct usbssp_udc *usbssp_data);

+int usbssp_reset_device(struct usbssp_udc *usbssp_data);
static inline char *usbssp_slot_state_string(u32 state)
{
switch (state) {
--
2.17.1


2018-07-19 18:03:11

by Pawel Laszczak

[permalink] [raw]
Subject: [PATCH 25/31] usb: usbssp: added implementation of usbssp interface.

Patch add implementation of most function from usb_ep_ops and
usbssp_gadget_ops objects. The implementation of
usbssp_gadget_ep_enable and usbssp_gadget_ep_disable functions
will be added as separate patch.

Patch also adds usbssp_g_lock and usbssp_g_unlock macros.
They are responsible for proper handling of semaphores.
Some functions belonging to usb_ep_ops and usbssp_gadget_ops
can be invoked from some different context.
In usbssp driver we have Hard Irq interrupt context and deferred interrupt
context (thread context). Additionally, driver in thread context can
calls commands on which ends it must wait and must enable
interrupts to detect completion.
Therefor when driver is waiting for command completion, the new event can
be reported and driver starts handling it in Hard Irq context.
Therefor driver use two separate SpinLock objects. The first usbssp->lock
is used in Hard Irq context and second usbssp->irq_thread_lock is used
in kernel thread context.

Signed-off-by: Pawel Laszczak <[email protected]>
---
drivers/usb/usbssp/gadget-if.c | 182 +++++++++++++++++++++++++--------
drivers/usb/usbssp/gadget.c | 104 +++++++++++++++++++
2 files changed, 246 insertions(+), 40 deletions(-)

diff --git a/drivers/usb/usbssp/gadget-if.c b/drivers/usb/usbssp/gadget-if.c
index dbff5a400676..376e03b7ef1f 100644
--- a/drivers/usb/usbssp/gadget-if.c
+++ b/drivers/usb/usbssp/gadget-if.c
@@ -7,11 +7,35 @@
* Author: Pawel Laszczak
*
*/
-
+#include <linux/pm_runtime.h>
#include <linux/usb/gadget.h>
#include <linux/usb/composite.h>
#include "gadget.h"

+#define usbssp_g_lock(flag, save_flags) { \
+ if (in_interrupt()) {\
+ spin_lock_irqsave(&usbssp_data->lock, save_flags); \
+ } else { \
+ if (!irqs_disabled()) { \
+ spin_lock_irqsave(&usbssp_data->irq_thread_lock,\
+ usbssp_data->irq_thread_flag);\
+ flag = 1; \
+ } else \
+ spin_lock(&usbssp_data->irq_thread_lock); \
+ } }
+
+
+#define usbssp_g_unlock(flag, save_flags) { \
+ if (in_interrupt()) \
+ spin_unlock_irqrestore(&usbssp_data->lock, save_flags); \
+ else { \
+ if (flag) \
+ spin_unlock_irqrestore(&usbssp_data->irq_thread_lock,\
+ usbssp_data->irq_thread_flag);\
+ else \
+ spin_unlock(&usbssp_data->irq_thread_lock); \
+ } }
+
static int usbssp_gadget_ep_enable(struct usb_ep *ep,
const struct usb_endpoint_descriptor *desc)
{
@@ -40,24 +64,27 @@ int usbssp_gadget_ep_disable(struct usb_ep *ep)
static struct usb_request *usbssp_gadget_ep_alloc_request(struct usb_ep *ep,
gfp_t gfp_flags)
{
+ struct usbssp_request *req_priv;
struct usbssp_ep *ep_priv = to_usbssp_ep(ep);

- if (!ep_priv)
+ req_priv = kzalloc(sizeof(*req_priv), gfp_flags);
+ if (!req_priv)
return NULL;

- /*TODO: implements this function*/
- return NULL;
+ req_priv->epnum = ep_priv->number;
+ req_priv->dep = ep_priv;
+
+ trace_usbssp_alloc_request(&req_priv->request);
+ return &req_priv->request;
}

static void usbssp_gadget_ep_free_request(struct usb_ep *ep,
struct usb_request *request)
{
- struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
+ struct usbssp_request *req_priv = to_usbssp_request(request);

- if (!ep_priv)
- return;
-
- /*TODO: implements this function*/
+ trace_usbssp_free_request(&req_priv->request);
+ kfree(req_priv);
}

static int usbssp_gadget_ep_queue(struct usb_ep *ep,
@@ -65,12 +92,32 @@ static int usbssp_gadget_ep_queue(struct usb_ep *ep,
gfp_t gfp_flags)
{
struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
- int ret = 0;
+ struct usbssp_request *req_priv = to_usbssp_request(request);
+ struct usbssp_udc *usbssp_data = ep_priv->usbssp_data;
+ unsigned long flags = 0;
+ int irq_disabled_locally = 0;
+ int ret;
+
+ if (!ep_priv->endpoint.desc) {
+ dev_err(usbssp_data->dev,
+ "%s: can't queue to disabled endpoint\n",
+ ep_priv->name);
+ return -ESHUTDOWN;
+ }

- if (!ep_priv)
- return -EINVAL;
+ if ((ep_priv->ep_state & USBSSP_EP_DISABLE_PENDING ||
+ !(ep_priv->ep_state & USBSSP_EP_ENABLED))) {
+ dev_err(usbssp_data->dev,
+ "%s: can't queue to disabled endpoint\n",
+ ep_priv->name);
+ ret = -ESHUTDOWN;
+ goto out;
+ }

- /*TODO: implements this function*/
+ usbssp_g_lock(irq_disabled_locally, flags);
+ ret = usbssp_enqueue(ep_priv, req_priv);
+ usbssp_g_unlock(irq_disabled_locally, flags);
+out:
return ret;
}

@@ -78,36 +125,53 @@ static int usbssp_gadget_ep_dequeue(struct usb_ep *ep,
struct usb_request *request)
{
struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
- int ret = 0;
+ struct usbssp_request *req_priv = to_usbssp_request(request);
+ struct usbssp_udc *usbssp_data = ep_priv->usbssp_data;
+ unsigned long flags = 0;
+ int ret;
+ int irq_disabled_locally = 0;
+
+ if (!ep_priv->endpoint.desc) {
+ dev_err(usbssp_data->dev,
+ "%s: can't queue to disabled endpoint\n",
+ ep_priv->name);
+ return -ESHUTDOWN;
+ }

- if (!ep_priv)
- return -EINVAL;
+ usbssp_g_lock(irq_disabled_locally, flags);
+ ret = usbssp_dequeue(ep_priv, req_priv);
+ usbssp_g_unlock(irq_disabled_locally, flags);

- /*TODO: implements this function*/
return ret;
}

static int usbssp_gadget_ep_set_halt(struct usb_ep *ep, int value)
{
struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
- int ret = 0;
-
- if (!ep_priv)
- return -EINVAL;
+ struct usbssp_udc *usbssp_data = ep_priv->usbssp_data;
+ int ret;
+ int irq_disabled_locally = 0;
+ unsigned long flags = 0;

- /*TODO: implements this function*/
+ usbssp_g_lock(irq_disabled_locally, flags);
+ ret = usbssp_halt_endpoint(usbssp_data, ep_priv, value);
+ usbssp_g_unlock(irq_disabled_locally, flags);
return ret;
}

static int usbssp_gadget_ep_set_wedge(struct usb_ep *ep)
{
struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
- int ret = 0;
+ struct usbssp_udc *usbssp_data = ep_priv->usbssp_data;
+ unsigned long flags = 0;
+ int ret;
+ int irq_disabled_locally = 0;

- if (!ep_priv)
- return -EINVAL;
+ usbssp_g_lock(irq_disabled_locally, flags);
+ ep_priv->ep_state |= USBSSP_EP_WEDGE;
+ ret = usbssp_halt_endpoint(usbssp_data, ep_priv, 1);
+ usbssp_g_unlock(irq_disabled_locally, flags);

- /*TODO: implements this function*/
return ret;
}

@@ -182,19 +246,39 @@ static int usbssp_gadget_start(struct usb_gadget *g,
usbssp_data->gadget.name,
usbssp_data->gadget_driver->driver.name);
ret = -EBUSY;
+ goto err1;
}

- /*TODO: add implementation*/
+ usbssp_data->gadget_driver = driver;
+
+ if (pm_runtime_active(usbssp_data->dev)) {
+ usbssp_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
+ usbssp_data->ep0state = USBSSP_EP0_UNCONNECTED;
+ ret = usbssp_run(usbssp_data);
+ if (ret < 0)
+ goto err1;
+ }
+ return 0;
+err1:
return ret;
}

static int usbssp_gadget_stop(struct usb_gadget *g)
{
+ unsigned long flags = 0;
+ int irq_disabled_locally = 0;
struct usbssp_udc *usbssp_data = gadget_to_usbssp(g);

- if (!usbssp_data)
- return -EINVAL;
- /*TODO: add implementation*/
+ usbssp_g_lock(irq_disabled_locally, flags);
+ if (pm_runtime_suspended(usbssp_data->dev))
+ goto out;
+
+ usbssp_free_dev(usbssp_data);
+ usbssp_stop(usbssp_data);
+out:
+ usbssp_data->gadget_driver = NULL;
+ usbssp_g_unlock(irq_disabled_locally, flags);
+
return 0;
}

@@ -202,33 +286,51 @@ static int usbssp_gadget_get_frame(struct usb_gadget *g)
{
struct usbssp_udc *usbssp_data = gadget_to_usbssp(g);

- if (!usbssp_data)
- return -EINVAL;
-
- /*TODO: add implementation*/
- return 0;
+ return usbssp_get_frame(usbssp_data);
}

static int usbssp_gadget_wakeup(struct usb_gadget *g)
{
struct usbssp_udc *usbssp_data = gadget_to_usbssp(g);
+ unsigned long flags = 0;
+ int irq_disabled_locally = 0;
+ __le32 __iomem *port_regs;
+ u32 temp;
+
+ if (!usbssp_data->port_remote_wakeup)
+ return -EINVAL;

- if (!usbssp_data)
+ if (!usbssp_data->port_suspended)
return -EINVAL;

- /*TODO: add implementation*/
+ usbssp_g_lock(irq_disabled_locally, flags);
+
+ port_regs = usbssp_get_port_io_addr(usbssp_data);
+ temp = readl(port_regs+PORTPMSC);
+
+ if (!(temp & PORT_RWE))
+ return 0;
+
+ temp = readl(port_regs + PORTSC);
+
+ temp &= ~PORT_PLS_MASK;
+ writel(temp, port_regs + PORTPMSC);
+ usbssp_g_unlock(irq_disabled_locally, flags);
return 0;
}

static int usbssp_gadget_set_selfpowered(struct usb_gadget *g,
int is_selfpowered)
{
+ unsigned long flags = 0;
+ int irq_disabled_locally = 0;
struct usbssp_udc *usbssp_data = gadget_to_usbssp(g);

- if (!usbssp_data)
- return -EINVAL;
+ usbssp_g_lock(irq_disabled_locally, flags);
+
+ g->is_selfpowered = !!is_selfpowered;
+ usbssp_g_unlock(irq_disabled_locally, flags);

- /*TODO: add implementation*/
return 0;
}

diff --git a/drivers/usb/usbssp/gadget.c b/drivers/usb/usbssp/gadget.c
index fe373a7b7198..0180ecfdaf9c 100644
--- a/drivers/usb/usbssp/gadget.c
+++ b/drivers/usb/usbssp/gadget.c
@@ -261,6 +261,22 @@ int usbssp_reset(struct usbssp_udc *usbssp_data)
return ret;
}

+static inline int usbssp_try_enable_msi(struct usbssp_udc *usbssp_data)
+{
+ usbssp_data->msi_enabled = 1;
+ return 0;
+}
+
+static inline void usbssp_cleanup_msix(struct usbssp_udc *usbssp_data)
+{
+ usbssp_data->msi_enabled = 0;
+}
+
+static inline void usbssp_msix_sync_irqs(struct usbssp_udc *usbssp_data)
+{
+ /*TODO*/
+}
+
/*
* Initialize memory for gadget driver and USBSSP (one-time init).
*
@@ -280,6 +296,88 @@ int usbssp_init(struct usbssp_udc *usbssp_data)
return retval;
}

+/*-------------------------------------------------------------------------*/
+/*
+ * Start the USBSSP after it was halted.
+ *
+ * This function is called by the usbssp_gadget_start function when the
+ * gadget driver is started. Its opposite is usbssp_stop().
+ *
+ * usbssp_init() must be called once before this function can be called.
+ * Reset the USBSSP, enable device slot contexts, program DCBAAP, and
+ * set command ring pointer and event ring pointer.
+ */
+int usbssp_run(struct usbssp_udc *usbssp_data)
+{
+ u32 temp;
+ u64 temp_64;
+ int ret;
+ __le32 __iomem *portsc;
+ u32 portsc_val = 0;
+ int i = 0;
+
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init, "Driver running");
+
+ ret = usbssp_try_enable_msi(usbssp_data);
+ if (ret)
+ return ret;
+
+ temp_64 = usbssp_read_64(usbssp_data,
+ &usbssp_data->ir_set->erst_dequeue);
+ temp_64 &= ~ERST_PTR_MASK;
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
+ "ERST deq = 64'h%0lx",
+ (unsigned long int) temp_64);
+
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
+ "// Set the interrupt modulation register");
+ temp = readl(&usbssp_data->ir_set->irq_control);
+ temp &= ~ER_IRQ_INTERVAL_MASK;
+ temp |= (usbssp_data->imod_interval / 250) & ER_IRQ_INTERVAL_MASK;
+ writel(temp, &usbssp_data->ir_set->irq_control);
+
+ /*enable USB2 port*/
+ for (i = 0; i < usbssp_data->num_usb2_ports; i++) {
+ portsc = usbssp_data->usb2_ports + PORTSC;
+ portsc_val = readl(portsc) & ~PORT_PLS_MASK;
+ portsc_val = portsc_val | (5 << 5) | PORT_LINK_STROBE;
+ writel(portsc_val, portsc);
+ }
+
+ /*enable USB3.0 port*/
+ for (i = 0; i < usbssp_data->num_usb3_ports; i++) {
+ portsc = usbssp_data->usb3_ports + PORTSC;
+ portsc_val = readl(portsc) & ~PORT_PLS_MASK;
+ portsc_val = portsc_val | (5 << 5) | PORT_LINK_STROBE;
+ writel(portsc_val, portsc);
+ }
+
+ if (usbssp_start(usbssp_data)) {
+ usbssp_halt(usbssp_data);
+ return -ENODEV;
+ }
+
+ /* Set the USBSSP state before we enable the irqs */
+ temp = readl(&usbssp_data->op_regs->command);
+ temp |= (CMD_EIE);
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
+ "// Enable interrupts, cmd = 0x%x.", temp);
+ writel(temp, &usbssp_data->op_regs->command);
+
+ temp = readl(&usbssp_data->ir_set->irq_pending);
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
+ "// Enabling event ring interrupter %p by writing 0x%x to irq_pending",
+ usbssp_data->ir_set, (unsigned int) ER_IRQ_ENABLE(temp));
+ writel(ER_IRQ_ENABLE(temp), &usbssp_data->ir_set->irq_pending);
+
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
+ "Finished usbssp_run for USBSSP controller");
+
+ usbssp_data->cmd_ring_state = CMD_RING_STATE_RUNNING;
+
+ return 0;
+}
+
/*
* Stop USBSSP controller.
*
@@ -1098,6 +1196,12 @@ int usbssp_enable_device(struct usbssp_udc *usbssp_data)
return usbssp_setup_device(usbssp_data, SETUP_CONTEXT_ONLY);
}

+
+int usbssp_get_frame(struct usbssp_udc *usbssp_data)
+{
+ return readl(&usbssp_data->run_regs->microframe_index) >> 3;
+}
+
int usbssp_gen_setup(struct usbssp_udc *usbssp_data)
{
int retval;
--
2.17.1


2018-07-19 18:03:27

by Pawel Laszczak

[permalink] [raw]
Subject: [PATCH 17/31] usb: usbssp: added implementation of usbssp_halt_endpoint function.

Patch adds functionality for halting and clearing halt condition on
USB HW endpoint. To halt endpoint driver must first enter it to
stopped state.
To stop and halt endpoint driver uses Stop Endpoint and Halt Endpoint
commands. To clear halted state driver can uses Reset Endpoint command.
After clearing halt condition driver can rearm transfer on endpoint.

Signed-off-by: Pawel Laszczak <[email protected]>
---
drivers/usb/usbssp/Makefile | 2 +-
drivers/usb/usbssp/gadget-ep0.c | 23 +++++
drivers/usb/usbssp/gadget-ring.c | 129 ++++++++++++++++++++++++++
drivers/usb/usbssp/gadget.c | 152 ++++++++++++++++++++++++++++++-
drivers/usb/usbssp/gadget.h | 17 ++++
5 files changed, 321 insertions(+), 2 deletions(-)
create mode 100644 drivers/usb/usbssp/gadget-ep0.c

diff --git a/drivers/usb/usbssp/Makefile b/drivers/usb/usbssp/Makefile
index f867124f286c..b267fadcb104 100644
--- a/drivers/usb/usbssp/Makefile
+++ b/drivers/usb/usbssp/Makefile
@@ -5,7 +5,7 @@ CFLAGS_gadget-trace.o := -I$(src)
obj-$(CONFIG_USB_USBSSP_GADGET) += usbssp.o
usbssp-y := usbssp-plat.o gadget-ring.o \
gadget.o gadget-mem.o gadget-port.o \
- gadget-dbg.o
+ gadget-dbg.o gadget-ep0.o

ifneq ($(CONFIG_TRACING),)
usbssp-y += gadget-trace.o
diff --git a/drivers/usb/usbssp/gadget-ep0.c b/drivers/usb/usbssp/gadget-ep0.c
new file mode 100644
index 000000000000..c889a3102740
--- /dev/null
+++ b/drivers/usb/usbssp/gadget-ep0.c
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USBSSP device controller driver
+ *
+ * Copyright (C) 2018 Cadence.
+ *
+ * Author: Pawel Laszczak
+ *
+ * A lot of code based on Linux XHCI driver.
+ * Origin: Copyright (C) 2008 Intel Corp
+ */
+
+#include <linux/list.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/composite.h>
+#include "gadget-trace.h"
+
+int usbssp_status_stage(struct usbssp_udc *usbssp_data)
+{
+ /*TODO; function must to be implemented*/
+ return 0;
+}
+
diff --git a/drivers/usb/usbssp/gadget-ring.c b/drivers/usb/usbssp/gadget-ring.c
index f3ee1c4d82dc..59ba92494a56 100644
--- a/drivers/usb/usbssp/gadget-ring.c
+++ b/drivers/usb/usbssp/gadget-ring.c
@@ -1082,6 +1082,74 @@ struct usbssp_segment *usbssp_trb_in_td(struct usbssp_udc *usbssp_data,
return NULL;
}

+void usbssp_cleanup_halted_endpoint(struct usbssp_udc *usbssp_data,
+ unsigned int ep_index,
+ unsigned int stream_id,
+ struct usbssp_td *td,
+ enum usbssp_ep_reset_type reset_type)
+{
+ struct usbssp_command *command;
+ struct usbssp_ep_ctx *ep_ctx;
+ int interrupt_disabled_locally;
+
+ ep_ctx = usbssp_get_ep_ctx(usbssp_data, usbssp_data->devs.out_ctx,
+ ep_index);
+
+ if (GET_EP_CTX_STATE(ep_ctx) != EP_STATE_HALTED) {
+ dev_dbg(usbssp_data->dev,
+ "Endpint index %d is not in halted state.\n",
+ ep_index);
+ usbssp_status_stage(usbssp_data);
+ return;
+ }
+
+ command = usbssp_alloc_command(usbssp_data, true, GFP_ATOMIC);
+ if (!command)
+ return;
+
+ usbssp_queue_reset_ep(usbssp_data, command, ep_index,
+ reset_type);
+
+ usbssp_ring_cmd_db(usbssp_data);
+
+ if (irqs_disabled()) {
+ spin_unlock_irqrestore(&usbssp_data->irq_thread_lock,
+ usbssp_data->irq_thread_flag);
+ interrupt_disabled_locally = 1;
+ } else {
+ spin_unlock(&usbssp_data->irq_thread_lock);
+ }
+
+ wait_for_completion(command->completion);
+
+ if (interrupt_disabled_locally)
+ spin_lock_irqsave(&usbssp_data->irq_thread_lock,
+ usbssp_data->irq_thread_flag);
+ else
+ spin_lock(&usbssp_data->irq_thread_lock);
+
+ usbssp_free_command(usbssp_data, command);
+ if (ep_index != 0)
+ usbssp_status_stage(usbssp_data);
+}
+
+int usbssp_is_vendor_info_code(struct usbssp_udc *usbssp_data,
+ unsigned int trb_comp_code)
+{
+ if (trb_comp_code >= 224 && trb_comp_code <= 255) {
+ /*
+ * Vendor defined "informational" completion code,
+ * treat as not-an-error.
+ */
+ dev_dbg(usbssp_data->dev,
+ "Vendor defined info completion code %u\n",
+ trb_comp_code);
+ dev_dbg(usbssp_data->dev, "Treating code as success.\n");
+ return 1;
+ }
+ return 0;
+}
+
/*
* If this function returns an error condition, it means it got a Transfer
* event with a corrupted Slot ID, Endpoint ID, or TRB DMA address.
@@ -1408,6 +1476,67 @@ static int prepare_ring(struct usbssp_udc *usbssp_data,
return 0;
}

+/* Stop endpoint after disconnecting device.*/
+int usbssp_cmd_stop_ep(struct usbssp_udc *usbssp_data, struct usb_gadget *g,
+ struct usbssp_ep *ep_priv)
+{
+ int ret = 0;
+ struct usbssp_command *command;
+ unsigned int ep_index;
+ struct usbssp_container_ctx *out_ctx;
+ struct usbssp_ep_ctx *ep_ctx;
+ int interrupt_disabled_locally = 0;
+
+ ep_index = usbssp_get_endpoint_index(ep_priv->endpoint.desc);
+
+ if ((ep_priv->ep_state & EP_STOP_CMD_PENDING)) {
+ dev_dbg(usbssp_data->dev,
+ "Stop endpoint command on %s (index: %d) is pending\n",
+ ep_priv->name, ep_index);
+ return 0;
+ }
+
+ command = usbssp_alloc_command(usbssp_data, true, GFP_ATOMIC);
+ if (!command)
+ return -ENOMEM;
+
+ ep_priv->ep_state |= EP_STOP_CMD_PENDING;
+
+ usbssp_queue_stop_endpoint(usbssp_data, command,
+ ep_index, 0);
+ usbssp_ring_cmd_db(usbssp_data);
+
+ out_ctx = usbssp_data->devs.out_ctx;
+ ep_ctx = usbssp_get_ep_ctx(usbssp_data, out_ctx, ep_index);
+
+ if (irqs_disabled()) {
+ spin_unlock_irqrestore(&usbssp_data->irq_thread_lock,
+ usbssp_data->irq_thread_flag);
+ interrupt_disabled_locally = 1;
+ } else {
+ spin_unlock(&usbssp_data->irq_thread_lock);
+ }
+
+ /* Wait for last stop endpoint command to finish */
+ wait_for_completion(command->completion);
+
+ if (interrupt_disabled_locally)
+ spin_lock_irqsave(&usbssp_data->irq_thread_lock,
+ usbssp_data->irq_thread_flag);
+ else
+ spin_lock(&usbssp_data->irq_thread_lock);
+
+ if (command->status == COMP_COMMAND_ABORTED ||
+ command->status == COMP_COMMAND_RING_STOPPED) {
+ dev_warn(usbssp_data->dev,
+ "Timeout while waiting for stop endpoint command\n");
+ ret = -ETIME;
+ }
+
+ usbssp_free_command(usbssp_data, command);
+ return ret;
+}
+
/**** Command Ring Operations ****/
/*
* Generic function for queueing a command TRB on the command ring.
diff --git a/drivers/usb/usbssp/gadget.c b/drivers/usb/usbssp/gadget.c
index 32d095b32e9f..4dac1b3cbb85 100644
--- a/drivers/usb/usbssp/gadget.c
+++ b/drivers/usb/usbssp/gadget.c
@@ -320,6 +320,29 @@ int usbssp_resume(struct usbssp_udc *usbssp_data, bool hibernated)

#endif /* CONFIG_PM */

+/**
+ * usbssp_get_endpoint_index - Find the index for an endpoint given its
+ * descriptor.Use the return value to right shift 1 for the bitmask.
+ *
+ * Index = (epnum * 2) + direction - 1,
+ * where direction = 0 for OUT, 1 for IN.
+ * For control endpoints, the IN index is used (OUT index is unused), so
+ * index = (epnum * 2) + direction - 1 = (epnum * 2) + 1 - 1 = (epnum * 2)
+ */
+unsigned int usbssp_get_endpoint_index(
+ const struct usb_endpoint_descriptor *desc)
+{
+ unsigned int index;
+
+ if (usb_endpoint_xfer_control(desc)) {
+ index = (unsigned int) (usb_endpoint_num(desc)*2);
+ } else {
+ index = (unsigned int) (usb_endpoint_num(desc)*2) +
+ (usb_endpoint_dir_in(desc) ? 1 : 0) - 1;
+ }
+ return index;
+}
+
/* Compute the last valid endpoint context index. Basically, this is the
* endpoint index plus one. For slot contexts with more than valid endpoint,
* we find the most significant bit set in the added contexts flags.
@@ -331,10 +354,137 @@ unsigned int usbssp_last_valid_endpoint(u32 added_ctxs)
return fls(added_ctxs) - 1;
}

+/* Returns 1 if the arguments are OK;
+ * returns -EINVAL for NULL pointers.
+ */
+static int usbssp_check_args(struct usbssp_udc *usbssp_data,
+ struct usbssp_ep *ep, int check_ep,
+ bool check_dev_priv, const char *func)
+{
+ struct usbssp_device *dev_priv;
+
+ if (!usbssp_data || (check_ep && !ep)) {
+ pr_debug("USBSSP %s called with invalid args\n", func);
+ return -EINVAL;
+ }
+
+ if (check_dev_priv)
+ dev_priv = &usbssp_data->devs;
+
+ if (usbssp_data->usbssp_state & USBSSP_STATE_HALTED)
+ return -ENODEV;
+
+ return 1;
+}
+
int usbssp_halt_endpoint(struct usbssp_udc *usbssp_data, struct usbssp_ep *dep,
int value)
{
- /*TODO: implement this function*/
+ int ret = 1;
+ struct usbssp_device *dev_priv;
+ struct usbssp_command *command;
+ unsigned int ep_index;
+ int interrupt_disabled_locally = 0;
+
+ ret = usbssp_check_args(usbssp_data, NULL, 0, true, __func__);
+ if (ret <= 0)
+ return ret;
+
+ if ((usbssp_data->usbssp_state & USBSSP_STATE_DYING) ||
+ (usbssp_data->usbssp_state & USBSSP_STATE_REMOVING))
+ return -ENODEV;
+
+ dev_priv = &usbssp_data->devs;
+ ep_index = usbssp_get_endpoint_index(dep->endpoint.desc);
+
+ command = usbssp_alloc_command(usbssp_data, true, GFP_ATOMIC);
+
+ if (!command)
+ return -ENOMEM;
+
+ if (value) {
+ dep->ep_state |= EP_HALTED;
+
+ ret = usbssp_cmd_stop_ep(usbssp_data,
+ &usbssp_data->gadget, dep);
+ if (ret < 0) {
+ dev_err(usbssp_data->dev,
+ "Command Stop Endpoint failed 1\n");
+ return ret;
+ }
+
+ ret = usbssp_queue_halt_endpoint(usbssp_data, command,
+ ep_index);
+
+ if (ret < 0) {
+ dev_err(usbssp_data->dev,
+ "Command Halt Endpoint failed\n");
+ goto command_cleanup;
+ }
+
+ usbssp_ring_cmd_db(usbssp_data);
+
+ if (irqs_disabled()) {
+ spin_unlock_irqrestore(&usbssp_data->irq_thread_lock,
+ usbssp_data->irq_thread_flag);
+ interrupt_disabled_locally = 1;
+ } else {
+ spin_unlock(&usbssp_data->irq_thread_lock);
+ }
+
+ /* Wait for last stop endpoint command to finish */
+ wait_for_completion(command->completion);
+
+ if (interrupt_disabled_locally)
+ spin_lock_irqsave(&usbssp_data->irq_thread_lock,
+ usbssp_data->irq_thread_flag);
+ else
+ spin_lock(&usbssp_data->irq_thread_lock);
+
+ } else {
+ struct usbssp_td *td;
+
+ /*
+ * Issue a reset endpoint command to clear the device side
+ * halt, followed by a set dequeue command to move the
+ * dequeue pointer past the TD.
+ */
+ td = list_first_entry(&dep->ring->td_list, struct usbssp_td,
+ td_list);
+
+ usbssp_cleanup_halted_endpoint(usbssp_data, ep_index,
+ dep->ring->stream_id, td,
+ EP_HARD_RESET);
+
+ goto command_cleanup;
+ }
+
+ ret = command->status;
+
+ switch (ret) {
+ case COMP_COMMAND_ABORTED:
+ case COMP_COMMAND_RING_STOPPED:
+ dev_warn(usbssp_data->dev,
+ "Timeout waiting for Halt Endpoint command\n");
+ ret = -ETIME;
+ goto command_cleanup;
+ case COMP_SUCCESS:
+ dev_dbg(usbssp_data->dev, "Successful Halt Endpoint command.\n");
+ break;
+ default:
+ if (usbssp_is_vendor_info_code(usbssp_data, ret))
+ break;
+ dev_warn(usbssp_data->dev, "Unknown completion code %u for "
+ "Halt Endpoint command.\n", ret);
+ ret = -EINVAL;
+ goto command_cleanup;
+ }
+
+command_cleanup:
+ kfree(command->completion);
+ kfree(command);
+ return ret;
+
return 0;
}

diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h
index 1827781125bd..64168ea16dbf 100644
--- a/drivers/usb/usbssp/gadget.h
+++ b/drivers/usb/usbssp/gadget.h
@@ -1687,6 +1687,7 @@ void usbssp_mem_cleanup(struct usbssp_udc *usbssp_data);
int usbssp_mem_init(struct usbssp_udc *usbssp_data, gfp_t flags);
void usbssp_free_priv_device(struct usbssp_udc *usbssp_data);
int usbssp_alloc_priv_device(struct usbssp_udc *usbssp_data, gfp_t flags);
+unsigned int usbssp_get_endpoint_index(const struct usb_endpoint_descriptor *desc);
unsigned int usbssp_last_valid_endpoint(u32 added_ctxs);
int usbssp_ring_expansion(struct usbssp_udc *usbssp_data,
struct usbssp_ring *ring,
@@ -1731,12 +1732,27 @@ struct usbssp_segment *usbssp_trb_in_td(struct usbssp_udc *usbssp_data,
union usbssp_trb *start_trb,
union usbssp_trb *end_trb,
dma_addr_t suspect_dma, bool debug);
+
+int usbssp_is_vendor_info_code(struct usbssp_udc *usbssp_data,
+ unsigned int trb_comp_code);
void usbssp_ring_cmd_db(struct usbssp_udc *usbssp_data);
int usbssp_queue_slot_control(struct usbssp_udc *usbssp_data,
struct usbssp_command *cmd, u32 trb_type);
int usbssp_queue_stop_endpoint(struct usbssp_udc *usbssp_data,
struct usbssp_command *cmd,
unsigned int ep_index, int suspend);
+int usbssp_queue_reset_ep(struct usbssp_udc *usbssp_data,
+ struct usbssp_command *cmd,
+ unsigned int ep_index,
+ enum usbssp_ep_reset_type reset_type);
+void usbssp_cleanup_halted_endpoint(struct usbssp_udc *usbssp_data,
+ unsigned int ep_index,
+ unsigned int stream_id,
+ struct usbssp_td *td,
+ enum usbssp_ep_reset_type reset_type);
+int usbssp_queue_halt_endpoint(struct usbssp_udc *usbssp_data,
+ struct usbssp_command *cmd,
+ unsigned int ep_index);
void usbssp_handle_command_timeout(struct work_struct *work);

void usbssp_cleanup_command_queue(struct usbssp_udc *usbssp_data);
@@ -1770,6 +1786,7 @@ int usbssp_halt_endpoint(struct usbssp_udc *usbssp_data,
struct usbssp_ep *dep, int value);
int usbssp_cmd_stop_ep(struct usbssp_udc *usbssp_data, struct usb_gadget *g,
struct usbssp_ep *ep_priv);
+int usbssp_status_stage(struct usbssp_udc *usbssp_data);

static inline char *usbssp_slot_state_string(u32 state)
{
--
2.17.1


2018-07-19 18:03:31

by Pawel Laszczak

[permalink] [raw]
Subject: [PATCH 09/31] usb: usbssp: add implementation of usbssp_mem_cleanup

Patch add implementation of usbssp_mem_cleanup and
all other functions used during cleaning driver during
unloading module.

Signed-off-by: Pawel Laszczak <[email protected]>
---
drivers/usb/usbssp/gadget-mem.c | 182 ++++++++++++++++++++++++++++++-
drivers/usb/usbssp/gadget-ring.c | 21 ++++
drivers/usb/usbssp/gadget.c | 3 +-
drivers/usb/usbssp/gadget.h | 10 ++
4 files changed, 213 insertions(+), 3 deletions(-)

diff --git a/drivers/usb/usbssp/gadget-mem.c b/drivers/usb/usbssp/gadget-mem.c
index 06a59febbff2..ecb6e1bbd212 100644
--- a/drivers/usb/usbssp/gadget-mem.c
+++ b/drivers/usb/usbssp/gadget-mem.c
@@ -495,6 +495,105 @@ struct usbssp_container_ctx *usbssp_alloc_container_ctx(
return ctx;
}

+void usbssp_free_container_ctx(struct usbssp_udc *usbssp_data,
+ struct usbssp_container_ctx *ctx)
+{
+ if (!ctx)
+ return;
+ dma_pool_free(usbssp_data->device_pool, ctx->bytes, ctx->dma);
+ kfree(ctx);
+}
+
+/***************** Streams structures manipulation *************************/
+static void usbssp_free_stream_ctx(struct usbssp_udc *usbssp_data,
+ unsigned int num_stream_ctxs,
+ struct usbssp_stream_ctx *stream_ctx,
+ dma_addr_t dma)
+{
+ struct device *dev = usbssp_data->dev;
+ size_t size = sizeof(struct usbssp_stream_ctx) * num_stream_ctxs;
+
+ if (size > MEDIUM_STREAM_ARRAY_SIZE)
+ dma_free_coherent(dev, size, stream_ctx, dma);
+ else if (size <= SMALL_STREAM_ARRAY_SIZE)
+ return dma_pool_free(usbssp_data->small_streams_pool,
+ stream_ctx, dma);
+ else
+ return dma_pool_free(usbssp_data->medium_streams_pool,
+ stream_ctx, dma);
+}
+
+/**
+ * Frees all stream contexts associated with the endpoint,
+ *
+ * Caller should fix the endpoint context streams fields.
+ */
+void usbssp_free_stream_info(struct usbssp_udc *usbssp_data,
+ struct usbssp_stream_info *stream_info)
+{
+ int cur_stream;
+ struct usbssp_ring *cur_ring;
+
+ if (!stream_info)
+ return;
+
+ for (cur_stream = 1; cur_stream < stream_info->num_streams;
+ cur_stream++) {
+ cur_ring = stream_info->stream_rings[cur_stream];
+ if (cur_ring) {
+ usbssp_ring_free(usbssp_data, cur_ring);
+ stream_info->stream_rings[cur_stream] = NULL;
+ }
+ }
+
+ usbssp_free_command(usbssp_data, stream_info->free_streams_command);
+ usbssp_data->cmd_ring_reserved_trbs--;
+ if (stream_info->stream_ctx_array)
+ usbssp_free_stream_ctx(usbssp_data,
+ stream_info->num_stream_ctxs,
+ stream_info->stream_ctx_array,
+ stream_info->ctx_array_dma);
+
+ kfree(stream_info->stream_rings);
+ kfree(stream_info);
+}
+
+/***************** Device context manipulation *************************/
+
+/* All the usbssp_tds in the ring's TD list should be freed at this point.*/
+void usbssp_free_priv_device(struct usbssp_udc *usbssp_data)
+{
+ struct usbssp_device *dev;
+ int i;
+
+ /* if slot_id = 0 then no device slot is used */
+ if (usbssp_data->slot_id == 0)
+ return;
+
+ dev = &usbssp_data->devs;
+ trace_usbssp_free_priv_device(dev);
+
+ usbssp_data->dcbaa->dev_context_ptrs[usbssp_data->slot_id] = 0;
+ if (!dev)
+ return;
+
+ for (i = 0; i < 31; ++i) {
+ if (dev->eps[i].ring)
+ usbssp_ring_free(usbssp_data, dev->eps[i].ring);
+
+ if (dev->eps[i].stream_info)
+ usbssp_free_stream_info(usbssp_data,
+ dev->eps[i].stream_info);
+ }
+
+ if (dev->in_ctx)
+ usbssp_free_container_ctx(usbssp_data, dev->in_ctx);
+ if (dev->out_ctx)
+ usbssp_free_container_ctx(usbssp_data, dev->out_ctx);
+
+ usbssp_data->slot_id = 0;
+}
+
struct usbssp_command *usbssp_alloc_command(struct usbssp_udc *usbssp_data,
bool allocate_completion,
gfp_t mem_flags)
@@ -549,6 +648,13 @@ void usbssp_request_free_priv(struct usbssp_request *priv_req)
kfree(priv_req->td);
}

+void usbssp_free_command(struct usbssp_udc *usbssp_data,
+ struct usbssp_command *command)
+{
+ usbssp_free_container_ctx(usbssp_data, command->in_ctx);
+ kfree(command->completion);
+ kfree(command);
+}

int usbssp_alloc_erst(struct usbssp_udc *usbssp_data,
struct usbssp_ring *evt_ring,
@@ -580,9 +686,83 @@ int usbssp_alloc_erst(struct usbssp_udc *usbssp_data,
return 0;
}

+void usbssp_free_erst(struct usbssp_udc *usbssp_data, struct usbssp_erst *erst)
+{
+ size_t size;
+ struct device *dev = usbssp_data->dev;
+
+ size = sizeof(struct usbssp_erst_entry) * (erst->num_entries);
+ if (erst->entries)
+ dma_free_coherent(dev, size, erst->entries,
+ erst->erst_dma_addr);
+ erst->entries = NULL;
+}
+
void usbssp_mem_cleanup(struct usbssp_udc *usbssp_data)
{
- /*TODO: implements functions*/
+ struct device *dev = usbssp_data->dev;
+ int num_ports;
+
+ cancel_delayed_work_sync(&usbssp_data->cmd_timer);
+ cancel_work_sync(&usbssp_data->bottom_irq);
+
+ /* Free the Event Ring Segment Table and the actual Event Ring */
+ usbssp_free_erst(usbssp_data, &usbssp_data->erst);
+
+ if (usbssp_data->event_ring)
+ usbssp_ring_free(usbssp_data, usbssp_data->event_ring);
+ usbssp_data->event_ring = NULL;
+ usbssp_dbg_trace(usbssp_data,
+ trace_usbssp_dbg_init, "Freed event ring");
+
+ if (usbssp_data->cmd_ring)
+ usbssp_ring_free(usbssp_data, usbssp_data->cmd_ring);
+ usbssp_data->cmd_ring = NULL;
+ usbssp_dbg_trace(usbssp_data,
+ trace_usbssp_dbg_init, "Freed command ring");
+ usbssp_cleanup_command_queue(usbssp_data);
+
+ num_ports = HCS_MAX_PORTS(usbssp_data->hcs_params1);
+
+ usbssp_free_priv_device(usbssp_data);
+
+ dma_pool_destroy(usbssp_data->segment_pool);
+ usbssp_data->segment_pool = NULL;
+ usbssp_dbg_trace(usbssp_data,
+ trace_usbssp_dbg_init, "Freed segment pool");
+ dma_pool_destroy(usbssp_data->device_pool);
+ usbssp_data->device_pool = NULL;
+ usbssp_dbg_trace(usbssp_data,
+ trace_usbssp_dbg_init, "Freed device context pool");
+ dma_pool_destroy(usbssp_data->small_streams_pool);
+ usbssp_data->small_streams_pool = NULL;
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
+ "Freed small stream array pool");
+
+ dma_pool_destroy(usbssp_data->medium_streams_pool);
+ usbssp_data->medium_streams_pool = NULL;
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
+ "Freed medium stream array pool");
+
+ if (usbssp_data->dcbaa)
+ dma_free_coherent(dev, sizeof(*usbssp_data->dcbaa),
+ usbssp_data->dcbaa, usbssp_data->dcbaa->dma);
+
+ usbssp_data->dcbaa = NULL;
+
+ usbssp_data->cmd_ring_reserved_trbs = 0;
+ usbssp_data->num_usb2_ports = 0;
+ usbssp_data->num_usb3_ports = 0;
+ usbssp_data->num_active_eps = 0;
+ kfree(usbssp_data->port_array);
+ kfree(usbssp_data->ext_caps);
+ usbssp_data->usb2_ports = NULL;
+ usbssp_data->usb3_ports = NULL;
+ usbssp_data->port_array = NULL;
+ usbssp_data->ext_caps = NULL;
+
+ usbssp_data->page_size = 0;
+ usbssp_data->page_shift = 0;
}


diff --git a/drivers/usb/usbssp/gadget-ring.c b/drivers/usb/usbssp/gadget-ring.c
index 69cf478c222b..7c4b6b7b7b0a 100644
--- a/drivers/usb/usbssp/gadget-ring.c
+++ b/drivers/usb/usbssp/gadget-ring.c
@@ -52,3 +52,24 @@ void usbssp_handle_command_timeout(struct work_struct *work)
{
/*TODO: implements function*/
}
+
+static void usbssp_complete_del_and_free_cmd(struct usbssp_command *cmd,
+ u32 status)
+{
+ list_del(&cmd->cmd_list);
+
+ if (cmd->completion) {
+ cmd->status = status;
+ complete(cmd->completion);
+ } else {
+ kfree(cmd);
+ }
+}
+
+void usbssp_cleanup_command_queue(struct usbssp_udc *usbssp_data)
+{
+ struct usbssp_command *cur_cmd, *tmp_cmd;
+
+ list_for_each_entry_safe(cur_cmd, tmp_cmd, &usbssp_data->cmd_list, cmd_list)
+ usbssp_complete_del_and_free_cmd(cur_cmd, COMP_COMMAND_ABORTED);
+}
diff --git a/drivers/usb/usbssp/gadget.c b/drivers/usb/usbssp/gadget.c
index 534fb75740f7..915983bc400f 100644
--- a/drivers/usb/usbssp/gadget.c
+++ b/drivers/usb/usbssp/gadget.c
@@ -363,8 +363,7 @@ int usbssp_gadget_init(struct usbssp_udc *usbssp_data)
/*TODO add implementation of usbssp_reset function*/
//usbssp_reset(usbssp_data);
usbssp_reset(usbssp_data);
- /*TODO add implementation of freeing memory*/
- //usbssp_mem_cleanup(usbssp_data);
+ usbssp_mem_cleanup(usbssp_data);
err3:
return ret;
}
diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h
index 4a9a1ca2e9b5..9dba86a0274a 100644
--- a/drivers/usb/usbssp/gadget.h
+++ b/drivers/usb/usbssp/gadget.h
@@ -1685,6 +1685,15 @@ void usbssp_dbg_trace(struct usbssp_udc *usbssp_data,
/* USBSSP memory management */
void usbssp_mem_cleanup(struct usbssp_udc *usbssp_data);
int usbssp_mem_init(struct usbssp_udc *usbssp_data, gfp_t flags);
+
+void usbssp_free_command(struct usbssp_udc *usbssp_data,
+ struct usbssp_command *command);
+
+struct usbssp_container_ctx *usbssp_alloc_container_ctx(
+ struct usbssp_udc *usbssp_data,
+ int type, gfp_t flags);
+void usbssp_free_container_ctx(struct usbssp_udc *usbssp_data,
+ struct usbssp_container_ctx *ctx);
/* USBSSP Device controller glue */
void usbssp_bottom_irq(struct work_struct *work);
int usbssp_init(struct usbssp_udc *usbssp_data);
@@ -1704,6 +1713,7 @@ dma_addr_t usbssp_trb_virt_to_dma(struct usbssp_segment *seg,
union usbssp_trb *trb);
void usbssp_handle_command_timeout(struct work_struct *work);

+void usbssp_cleanup_command_queue(struct usbssp_udc *usbssp_data);
/* USBSSP gadget interface*/
int usbssp_gadget_init(struct usbssp_udc *usbssp_data);
int usbssp_gadget_exit(struct usbssp_udc *usbssp_data);
--
2.17.1


2018-07-19 18:03:37

by Pawel Laszczak

[permalink] [raw]
Subject: [PATCH 24/31] usb: usbssp: added detecting command timeout.

Patch add functionality responsible for detecting command timeout.
Because command can fail without any command completion event, driver
must handles such case in proper way. For this reason, driver implements
mechanism measuring the command time. If time expired driver aborts
last performed command.

Signed-off-by: Pawel Laszczak <[email protected]>
---
drivers/usb/usbssp/gadget-ring.c | 157 ++++++++++++++++++++++++++++++-
drivers/usb/usbssp/gadget.h | 1 +
2 files changed, 154 insertions(+), 4 deletions(-)

diff --git a/drivers/usb/usbssp/gadget-ring.c b/drivers/usb/usbssp/gadget-ring.c
index 8cc6e4247eef..e7afd185d4ad 100644
--- a/drivers/usb/usbssp/gadget-ring.c
+++ b/drivers/usb/usbssp/gadget-ring.c
@@ -289,6 +289,103 @@ static bool usbssp_mod_cmd_timer(struct usbssp_udc *usbssp_data,
return 0;
}

+static struct usbssp_command *usbssp_next_queued_cmd(struct usbssp_udc *usbssp_data)
+{
+ return list_first_entry_or_null(&usbssp_data->cmd_list,
+ struct usbssp_command,
+ cmd_list);
+}
+
+/*
+ * Turn all commands on command ring with status set to "aborted" to no-op trbs.
+ * If there are other commands waiting then restart the ring and kick the timer.
+ * This must be called with command ring stopped and usbssp_data->lock held.
+ */
+static void usbssp_handle_stopped_cmd_ring(struct usbssp_udc *usbssp_data,
+ struct usbssp_command *cur_cmd)
+{
+ struct usbssp_command *i_cmd;
+
+ /* Turn all aborted commands in list to no-ops, then restart */
+ list_for_each_entry(i_cmd, &usbssp_data->cmd_list, cmd_list) {
+
+ if (i_cmd->status != COMP_COMMAND_ABORTED)
+ continue;
+
+ i_cmd->status = COMP_COMMAND_RING_STOPPED;
+
+ dev_dbg(usbssp_data->dev, "Turn aborted command %p to no-op\n",
+ i_cmd->command_trb);
+
+ trb_to_noop(i_cmd->command_trb, TRB_CMD_NOOP);
+
+ /*
+ * caller waiting for completion is called when command
+ * completion event is received for these no-op commands
+ */
+ }
+
+ usbssp_data->cmd_ring_state = CMD_RING_STATE_RUNNING;
+
+ /* ring command ring doorbell to restart the command ring */
+ if ((usbssp_data->cmd_ring->dequeue != usbssp_data->cmd_ring->enqueue) &&
+ !(usbssp_data->usbssp_state & USBSSP_STATE_DYING)) {
+ usbssp_data->current_cmd = cur_cmd;
+ usbssp_mod_cmd_timer(usbssp_data, USBSSP_CMD_DEFAULT_TIMEOUT);
+ usbssp_ring_cmd_db(usbssp_data);
+ }
+}
+
+/* Must be called with usbssp_data->lock held, releases and aquires lock back */
+static int usbssp_abort_cmd_ring(struct usbssp_udc *usbssp_data,
+ unsigned long flags)
+{
+ u64 temp_64;
+ int ret;
+
+ dev_dbg(usbssp_data->dev, "Abort command ring\n");
+ reinit_completion(&usbssp_data->cmd_ring_stop_completion);
+
+ temp_64 = usbssp_read_64(usbssp_data, &usbssp_data->op_regs->cmd_ring);
+ usbssp_write_64(usbssp_data, temp_64 | CMD_RING_ABORT,
+ &usbssp_data->op_regs->cmd_ring);
+
+ /*
+ * Spec says software should also time the
+ * completion of the Command Abort operation. If CRR is not negated in 5
+ * seconds then driver handles it as if device died (-ENODEV).
+ */
+ ret = usbssp_handshake(&usbssp_data->op_regs->cmd_ring,
+ CMD_RING_RUNNING, 0, 5 * 1000 * 1000);
+
+ if (ret < 0) {
+ dev_err(usbssp_data->dev,
+ "Abort failed to stop command ring: %d\n", ret);
+ usbssp_halt(usbssp_data);
+ usbssp_udc_died(usbssp_data);
+ return ret;
+ }
+
+ /*
+ * Writing the CMD_RING_ABORT bit should cause a cmd completion event,
+ * Wait 2 secs (arbitrary number).
+ */
+ spin_unlock_irqrestore(&usbssp_data->lock, flags);
+ ret = wait_for_completion_timeout(
+ &usbssp_data->cmd_ring_stop_completion,
+ msecs_to_jiffies(2000));
+ spin_lock_irqsave(&usbssp_data->lock, flags);
+ if (!ret) {
+ dev_dbg(usbssp_data->dev,
+ "No stop event for abort, ring start fail?\n");
+ usbssp_cleanup_command_queue(usbssp_data);
+ } else {
+ usbssp_handle_stopped_cmd_ring(usbssp_data,
+ usbssp_next_queued_cmd(usbssp_data));
+ }
+ return 0;
+}
+
void usbssp_ring_ep_doorbell(struct usbssp_udc *usbssp_data,
unsigned int ep_index,
unsigned int stream_id)
@@ -1060,10 +1157,6 @@ static void usbssp_handle_cmd_reset_dev(struct usbssp_udc *usbssp_data,
dev_warn(usbssp_data->dev, "Reset device command completion\n");
}

-void usbssp_handle_command_timeout(struct work_struct *work)
-{
- /*TODO: implements function*/
-}

static void usbssp_complete_del_and_free_cmd(struct usbssp_command *cmd,
u32 status)
@@ -1086,6 +1179,62 @@ void usbssp_cleanup_command_queue(struct usbssp_udc *usbssp_data)
usbssp_complete_del_and_free_cmd(cur_cmd, COMP_COMMAND_ABORTED);
}

+void usbssp_handle_command_timeout(struct work_struct *work)
+{
+ struct usbssp_udc *usbssp_data;
+ unsigned long flags;
+ u64 hw_ring_state;
+
+ usbssp_data = container_of(to_delayed_work(work), struct usbssp_udc,
+ cmd_timer);
+
+ spin_lock_irqsave(&usbssp_data->lock, flags);
+
+ /*
+ * If timeout work is pending, or current_cmd is NULL, it means we
+ * raced with command completion. Command is handled so just return.
+ */
+ if (!usbssp_data->current_cmd ||
+ delayed_work_pending(&usbssp_data->cmd_timer)) {
+ spin_unlock_irqrestore(&usbssp_data->lock, flags);
+ return;
+ }
+ /* mark this command to be cancelled */
+ usbssp_data->current_cmd->status = COMP_COMMAND_ABORTED;
+
+ /* Make sure command ring is running before aborting it */
+ hw_ring_state = usbssp_read_64(usbssp_data,
+ &usbssp_data->op_regs->cmd_ring);
+
+ if (hw_ring_state == ~(u64)0) {
+ usbssp_udc_died(usbssp_data);
+ goto time_out_completed;
+ }
+
+ if ((usbssp_data->cmd_ring_state & CMD_RING_STATE_RUNNING) &&
+ (hw_ring_state & CMD_RING_RUNNING)) {
+ /* Prevent new doorbell, and start command abort */
+ usbssp_data->cmd_ring_state = CMD_RING_STATE_ABORTED;
+ dev_dbg(usbssp_data->dev, "Command timeout\n");
+ usbssp_abort_cmd_ring(usbssp_data, flags);
+ goto time_out_completed;
+ }
+
+ /* device disconnected. Bail out */
+ if (usbssp_data->usbssp_state & USBSSP_STATE_REMOVING) {
+ dev_dbg(usbssp_data->dev, "device removed, ring start fail?\n");
+ usbssp_cleanup_command_queue(usbssp_data);
+ goto time_out_completed;
+ }
+
+ /* command timeout on stopped ring, ring can't be aborted */
+ dev_dbg(usbssp_data->dev, "Command timeout on stopped ring\n");
+ usbssp_handle_stopped_cmd_ring(usbssp_data, usbssp_data->current_cmd);
+
+time_out_completed:
+ spin_unlock_irqrestore(&usbssp_data->lock, flags);
+}
+
static void handle_cmd_completion(struct usbssp_udc *usbssp_data,
struct usbssp_event_cmd *event)
{
diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h
index c4e440db6b23..6737cb05cd27 100644
--- a/drivers/usb/usbssp/gadget.h
+++ b/drivers/usb/usbssp/gadget.h
@@ -1803,6 +1803,7 @@ void usbssp_set_link_state(struct usbssp_udc *usbssp_data,
void usbssp_test_and_clear_bit(struct usbssp_udc *usbssp_data,
__le32 __iomem *port_regs, u32 port_bit);

+void usbssp_udc_died(struct usbssp_udc *usbssp_data);
/* USBSSP DC contexts */
struct usbssp_input_control_ctx *usbssp_get_input_control_ctx(
struct usbssp_container_ctx *ctx);
--
2.17.1


2018-07-19 18:03:38

by Pawel Laszczak

[permalink] [raw]
Subject: [PATCH 27/31] usb: usbssp: implements usbssp_gadget_ep_enable function

Patch implements function responsible for enabling
and configuring USB endpoint. This function is called
from USB core gadget during handling SET_CONFIGURATION
and SET_INTERFACE request.

Signed-off-by: Pawel Laszczak <[email protected]>
---
drivers/usb/usbssp/gadget-if.c | 41 ++++-
drivers/usb/usbssp/gadget-mem.c | 286 ++++++++++++++++++++++++++++++++
drivers/usb/usbssp/gadget.c | 271 +++++++++++++++++++++++++++++-
drivers/usb/usbssp/gadget.h | 10 ++
4 files changed, 604 insertions(+), 4 deletions(-)

diff --git a/drivers/usb/usbssp/gadget-if.c b/drivers/usb/usbssp/gadget-if.c
index 376e03b7ef1f..afc4f32ec8af 100644
--- a/drivers/usb/usbssp/gadget-if.c
+++ b/drivers/usb/usbssp/gadget-if.c
@@ -39,13 +39,48 @@
static int usbssp_gadget_ep_enable(struct usb_ep *ep,
const struct usb_endpoint_descriptor *desc)
{
- struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
+ struct usbssp_ep *ep_priv;
+ struct usbssp_udc *usbssp_data;
int ret = 0;
+ int irq_disabled_locally = 0;
+ unsigned long flags = 0;

- if (!ep_priv)
+ if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) {
+ pr_err("invalid parameters\n");
return -EINVAL;
+ }

- /*TODO: implements this function*/
+ ep_priv = to_usbssp_ep(ep);
+ usbssp_data = ep_priv->usbssp_data;
+
+ if (!desc->wMaxPacketSize) {
+ dev_dbg(usbssp_data->dev, "missing wMaxPacketSize\n");
+ return -EINVAL;
+ }
+
+ if (ep_priv->ep_state & USBSSP_EP_ENABLED) {
+ dev_dbg(usbssp_data->dev, "%s is already enabled\n",
+ ep_priv->name);
+ return -EINVAL;
+ }
+
+ usbssp_g_lock(irq_disabled_locally, flags);
+ ret = usbssp_add_endpoint(usbssp_data, ep_priv);
+ if (ret < 0)
+ goto finish;
+
+ ep_priv->ep_state |= USBSSP_EP_ENABLED;
+
+ /*Update bandwidth information*/
+ ret = usbssp_check_bandwidth(usbssp_data, &usbssp_data->gadget);
+
+ if (ret < 0)
+ ep_priv->ep_state &= ~USBSSP_EP_ENABLED;
+
+finish:
+ dev_dbg(usbssp_data->dev, "%s enable endpoint %s\n", ep_priv->name,
+ (ret == 0) ? "success" : "failed");
+ usbssp_g_unlock(irq_disabled_locally, flags);
return ret;
}

diff --git a/drivers/usb/usbssp/gadget-mem.c b/drivers/usb/usbssp/gadget-mem.c
index d5d732b94454..8438596ecf48 100644
--- a/drivers/usb/usbssp/gadget-mem.c
+++ b/drivers/usb/usbssp/gadget-mem.c
@@ -817,6 +817,292 @@ int usbssp_setup_addressable_priv_dev(struct usbssp_udc *usbssp_data)
return 0;
}

+/*
+ * Convert interval expressed as 2^(bInterval - 1) == interval into
+ * straight exponent value 2^n == interval.
+ *
+ */
+static unsigned int usbssp_parse_exponent_interval(struct usb_gadget *g,
+ struct usbssp_ep *dep)
+{
+ unsigned int interval;
+
+ interval = clamp_val(dep->endpoint.desc->bInterval, 1, 16) - 1;
+ if (interval != dep->endpoint.desc->bInterval - 1)
+ dev_warn(&g->dev,
+ "ep %#x - rounding interval to %d %sframes\n",
+ dep->endpoint.desc->bEndpointAddress,
+ 1 << interval,
+ g->speed == USB_SPEED_FULL ? "" : "micro");
+
+ if (g->speed == USB_SPEED_FULL) {
+ /*
+ * Full speed isoc endpoints specify interval in frames,
+ * not microframes. We are using microframes everywhere,
+ * so adjust accordingly.
+ */
+ interval += 3; /* 1 frame = 2^3 uframes */
+ }
+
+ return interval;
+}
+
+/*
+ * Convert bInterval expressed in microframes (in 1-255 range) to exponent of
+ * microframes, rounded down to nearest power of 2.
+ */
+static unsigned int usbssp_microframes_to_exponent(struct usb_gadget *g,
+ struct usbssp_ep *dep,
+ unsigned int desc_interval,
+ unsigned int min_exponent,
+ unsigned int max_exponent)
+{
+ unsigned int interval;
+
+ interval = fls(desc_interval) - 1;
+ interval = clamp_val(interval, min_exponent, max_exponent);
+ if ((1 << interval) != desc_interval)
+ dev_dbg(&g->dev,
+ "ep %#x - rounding interval to %d microframes,"
+ "ep desc says %d microframes\n",
+ dep->endpoint.desc->bEndpointAddress,
+ 1 << interval,
+ desc_interval);
+
+ return interval;
+}
+
+static unsigned int usbssp_parse_microframe_interval(struct usb_gadget *g,
+ struct usbssp_ep *dep)
+{
+ if (dep->endpoint.desc->bInterval == 0)
+ return 0;
+ return usbssp_microframes_to_exponent(g, dep,
+ dep->endpoint.desc->bInterval, 0, 15);
+}
+
+
+static unsigned int usbssp_parse_frame_interval(struct usb_gadget *g,
+ struct usbssp_ep *dep)
+{
+
+ return usbssp_microframes_to_exponent(g, dep,
+ dep->endpoint.desc->bInterval * 8,
+ 3, 10);
+}
+
+/*
+ * Return the polling or NAK interval.
+ *
+ * The polling interval is expressed in "microframes". If DC's Interval field
+ * is set to N, it will service the endpoint every 2^(Interval)*125us.
+ *
+ * The NAK interval is one NAK per 1 to 255 microframes, or no NAKs if interval
+ * is set to 0.
+ */
+static unsigned int usbssp_get_endpoint_interval(struct usb_gadget *g,
+ struct usbssp_ep *dep)
+{
+ unsigned int interval = 0;
+
+ switch (g->speed) {
+ case USB_SPEED_HIGH:
+ /* Max NAK rate */
+ if (usb_endpoint_xfer_control(dep->endpoint.desc) ||
+ usb_endpoint_xfer_bulk(dep->endpoint.desc)) {
+ interval = usbssp_parse_microframe_interval(g, dep);
+ break;
+ }
+ /* Fall through - SS and HS isoc/int have same decoding */
+ case USB_SPEED_SUPER_PLUS:
+ case USB_SPEED_SUPER:
+ if (usb_endpoint_xfer_int(dep->endpoint.desc) ||
+ usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
+ interval = usbssp_parse_exponent_interval(g, dep);
+ }
+ break;
+
+ case USB_SPEED_FULL:
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
+ interval = usbssp_parse_exponent_interval(g, dep);
+ break;
+ }
+ /*
+ * Fall through for interrupt endpoint interval decoding
+ * since it uses the same rules as low speed interrupt
+ * endpoints.
+ */
+ case USB_SPEED_LOW:
+ if (usb_endpoint_xfer_int(dep->endpoint.desc) ||
+ usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
+ interval = usbssp_parse_frame_interval(g, dep);
+ }
+ break;
+
+ default:
+ BUG();
+ }
+ return interval;
+}
+
+/*
+ * The "Mult" field in the endpoint context is only set for SuperSpeed isoc eps.
+ * High speed endpoint descriptors can define "the number of additional
+ * transaction opportunities per microframe", but that goes in the Max Burst
+ * endpoint context field.
+ */
+static u32 usbssp_get_endpoint_mult(struct usb_gadget *g,
+ struct usbssp_ep *dep)
+{
+ if (g->speed < USB_SPEED_SUPER ||
+ !usb_endpoint_xfer_isoc(dep->endpoint.desc))
+ return 0;
+
+ return dep->endpoint.comp_desc->bmAttributes;
+}
+
+static u32 usbssp_get_endpoint_max_burst(struct usb_gadget *g,
+ struct usbssp_ep *dep)
+{
+ /* Super speed and Plus have max burst in ep companion desc */
+ if (g->speed >= USB_SPEED_SUPER)
+ return dep->endpoint.comp_desc->bMaxBurst;
+
+ if (g->speed == USB_SPEED_HIGH &&
+ (usb_endpoint_xfer_isoc(dep->endpoint.desc) ||
+ usb_endpoint_xfer_int(dep->endpoint.desc)))
+ return (usb_endpoint_maxp(dep->endpoint.desc) & 0x1800) >> 11;
+
+ return 0;
+}
+
+static u32 usbssp_get_endpoint_type(const struct usb_endpoint_descriptor *desc)
+{
+ int in;
+
+ in = usb_endpoint_dir_in(desc);
+
+ switch (usb_endpoint_type(desc)) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ return CTRL_EP;
+ case USB_ENDPOINT_XFER_BULK:
+ return in ? BULK_IN_EP : BULK_OUT_EP;
+ case USB_ENDPOINT_XFER_ISOC:
+ return in ? ISOC_IN_EP : ISOC_OUT_EP;
+ case USB_ENDPOINT_XFER_INT:
+ return in ? INT_IN_EP : INT_OUT_EP;
+ }
+ return 0;
+}
+
+/*
+ * Return the maximum endpoint service interval time (ESIT) payload.
+ * Basically, this is the maxpacket size, multiplied by the burst size
+ * and mult size.
+ */
+static u32 usbssp_get_max_esit_payload(struct usb_gadget *g,
+ struct usbssp_ep *dep)
+{
+ int max_burst;
+ int max_packet;
+
+ /* Only applies for interrupt or isochronous endpoints*/
+ if (usb_endpoint_xfer_control(dep->endpoint.desc) ||
+ usb_endpoint_xfer_bulk(dep->endpoint.desc))
+ return 0;
+
+ /* SuperSpeedPlus Isoc ep sending over 48k per esit*/
+
+ if ((g->speed >= USB_SPEED_SUPER_PLUS) &&
+ USB_SS_SSP_ISOC_COMP(dep->endpoint.desc->bmAttributes))
+ return le32_to_cpu(dep->endpoint.comp_desc->wBytesPerInterval);
+ /* SuperSpeed or SuperSpeedPlus Isoc ep with less than 48k per esit */
+ else if (g->speed >= USB_SPEED_SUPER)
+ return le16_to_cpu(dep->endpoint.comp_desc->wBytesPerInterval);
+
+ max_packet = usb_endpoint_maxp(dep->endpoint.desc);
+ max_burst = usb_endpoint_maxp_mult(dep->endpoint.desc);
+ /* A 0 in max burst means 1 transfer per ESIT */
+ return max_packet * max_burst;
+}
+
+/*
+ * Set up an endpoint with one ring segment. Do not allocate stream rings.
+ * Drivers will have to call usb_alloc_streams() to do that.
+ */
+int usbssp_endpoint_init(struct usbssp_udc *usbssp_data,
+ struct usbssp_device *dev_priv,
+ struct usbssp_ep *dep,
+ gfp_t mem_flags)
+{
+ unsigned int ep_index;
+ struct usbssp_ep_ctx *ep_ctx;
+ struct usbssp_ring *ep_ring;
+ unsigned int max_packet;
+ enum usbssp_ring_type ring_type;
+ u32 max_esit_payload;
+ u32 endpoint_type;
+ unsigned int max_burst;
+ unsigned int interval;
+ unsigned int mult;
+ unsigned int avg_trb_len;
+ unsigned int err_count = 0;
+
+ ep_index = usbssp_get_endpoint_index(dep->endpoint.desc);
+ ep_ctx = usbssp_get_ep_ctx(usbssp_data, dev_priv->in_ctx, ep_index);
+
+ endpoint_type = usbssp_get_endpoint_type(dep->endpoint.desc);
+ if (!endpoint_type)
+ return -EINVAL;
+
+ ring_type = usb_endpoint_type(dep->endpoint.desc);
+
+ /*
+ * Get values to fill the endpoint context, mostly from ep descriptor.
+ * The average TRB buffer lengt for bulk endpoints is unclear as we
+ * have no clue on scatter gather list entry size. For Isoc and Int,
+ * set it to max available.
+ */
+ max_esit_payload = usbssp_get_max_esit_payload(&usbssp_data->gadget, dep);
+ interval = usbssp_get_endpoint_interval(&usbssp_data->gadget, dep);
+ mult = usbssp_get_endpoint_mult(&usbssp_data->gadget, dep);
+ max_packet = GET_MAX_PACKET(usb_endpoint_maxp(dep->endpoint.desc));
+ max_burst = usbssp_get_endpoint_max_burst(&usbssp_data->gadget, dep);
+ avg_trb_len = max_esit_payload;
+
+ /* Allow 3 retries for everything but isoc, set CErr = 3 */
+ if (!usb_endpoint_xfer_isoc(dep->endpoint.desc))
+ err_count = 3;
+ if (usb_endpoint_xfer_bulk(dep->endpoint.desc) &&
+ usbssp_data->gadget.speed == USB_SPEED_HIGH)
+ max_packet = 512;
+ /* DC spec indicates that ctrl ep avg TRB Length should be 8 */
+ if (usb_endpoint_xfer_control(dep->endpoint.desc))
+ avg_trb_len = 8;
+
+ /* Set up the endpoint ring */
+ dev_priv->eps[ep_index].new_ring = usbssp_ring_alloc(usbssp_data, 2, 1,
+ ring_type, max_packet,
+ mem_flags);
+
+ dev_priv->eps[ep_index].skip = false;
+ ep_ring = dev_priv->eps[ep_index].new_ring;
+
+ /* Fill the endpoint context */
+ ep_ctx->ep_info = cpu_to_le32(EP_MAX_ESIT_PAYLOAD_HI(max_esit_payload) |
+ EP_INTERVAL(interval) | EP_MULT(mult));
+ ep_ctx->ep_info2 = cpu_to_le32(EP_TYPE(endpoint_type) |
+ MAX_PACKET(max_packet) | MAX_BURST(max_burst) |
+ ERROR_COUNT(err_count));
+ ep_ctx->deq = cpu_to_le64(ep_ring->first_seg->dma |
+ ep_ring->cycle_state);
+
+ ep_ctx->tx_info = cpu_to_le32(EP_MAX_ESIT_PAYLOAD_LO(max_esit_payload) |
+ EP_AVG_TRB_LENGTH(avg_trb_len));
+
+ return 0;
+}
+
struct usbssp_command *usbssp_alloc_command(struct usbssp_udc *usbssp_data,
bool allocate_completion,
gfp_t mem_flags)
diff --git a/drivers/usb/usbssp/gadget.c b/drivers/usb/usbssp/gadget.c
index d5ed2c48e3fa..f5b0659b6f2d 100644
--- a/drivers/usb/usbssp/gadget.c
+++ b/drivers/usb/usbssp/gadget.c
@@ -463,6 +463,29 @@ unsigned int usbssp_get_endpoint_index(
return index;
}

+/**
+ * The reverse operation to usbssp_get_endpoint_index.
+ * Calculate the USB endpoint address from the USBSSP endpoint index.
+ */
+unsigned int usbssp_get_endpoint_address(unsigned int ep_index)
+{
+ unsigned int number = DIV_ROUND_UP(ep_index, 2);
+ unsigned int direction = ep_index % 2 ? USB_DIR_OUT : USB_DIR_IN;
+
+ return direction | number;
+}
+
+/**
+ * Find the flag for this endpoint (for use in the control context). Use the
+ * endpoint index to create a bitmask. The slot context is bit 0, endpoint 0 is
+ * bit 1, etc.
+ */
+unsigned int usbssp_get_endpoint_flag(
+ const struct usb_endpoint_descriptor *desc)
+{
+ return 1 << (usbssp_get_endpoint_index(desc) + 1);
+}
+
/* Compute the last valid endpoint context index. Basically, this is the
* endpoint index plus one. For slot contexts with more than valid endpoint,
* we find the most significant bit set in the added contexts flags.
@@ -692,6 +715,132 @@ int usbssp_dequeue(struct usbssp_ep *ep_priv, struct usbssp_request *req_priv)
return ret;
}

+
+/**
+ * Add an endpoint to a new possible bandwidth configuration for this device.
+ * Only one call to this function is allowed per endpoint before
+ * check_bandwidth() or reset_bandwidth() must be called.
+ * A call to usbssp_drop_endpoint() followed by a call to
+ * usbssp_add_endpoint() will add the endpoint to the schedule with possibly
+ * new parameters denoted by different endpoint descriptor in usbssp_ep.
+ * A call to usbssp_add_endpoint() followed by a call to usbssp_drop_endpoint()
+ * is not allowed.
+ *
+ */
+int usbssp_add_endpoint(struct usbssp_udc *usbssp_data, struct usbssp_ep *dep)
+{
+ const struct usb_endpoint_descriptor *desc = dep->endpoint.desc;
+ struct usbssp_container_ctx *in_ctx;
+ unsigned int ep_index;
+ struct usbssp_input_control_ctx *ctrl_ctx;
+ u32 added_ctxs;
+ u32 new_add_flags, new_drop_flags;
+ struct usbssp_device *dev_priv;
+ int ret = 0;
+
+ ret = usbssp_check_args(usbssp_data, dep, 1, true, __func__);
+ if (ret <= 0)
+ return ret;
+
+ if (usbssp_data->usbssp_state & USBSSP_STATE_DYING)
+ return -ENODEV;
+
+ added_ctxs = usbssp_get_endpoint_flag(desc);
+ if (added_ctxs == SLOT_FLAG || added_ctxs == EP0_FLAG) {
+ dev_dbg(usbssp_data->dev, "USBSSP %s - can't add slot or ep 0 %#x\n",
+ __func__, added_ctxs);
+ return 0;
+ }
+
+ dev_priv = &usbssp_data->devs;
+ in_ctx = dev_priv->in_ctx;
+ ctrl_ctx = usbssp_get_input_control_ctx(in_ctx);
+ if (!ctrl_ctx) {
+ dev_warn(usbssp_data->dev, "%s: Could not get input context, bad type.\n",
+ __func__);
+ return 0;
+ }
+
+ ep_index = usbssp_get_endpoint_index(desc);
+
+ /*
+ * If this endpoint is already in use, and the upper layers are trying
+ * to add it again without dropping it, reject the addition.
+ */
+ if (dev_priv->eps[ep_index].ring &&
+ !(le32_to_cpu(ctrl_ctx->drop_flags) & added_ctxs)) {
+ dev_warn(usbssp_data->dev,
+ "Trying to add endpoint 0x%x without dropping it.\n",
+ (unsigned int) desc->bEndpointAddress);
+ return -EINVAL;
+ }
+
+ /*
+ * If already noted the endpoint is enabled,
+ * ignore this request.
+ */
+ if (le32_to_cpu(ctrl_ctx->add_flags) & added_ctxs) {
+ dev_warn(usbssp_data->dev, "USBSSP %s called with enabled ep %p\n",
+ __func__, dep);
+ return 0;
+ }
+
+ if (usbssp_endpoint_init(usbssp_data, dev_priv, dep, GFP_ATOMIC) < 0) {
+ dev_dbg(usbssp_data->dev, "%s - could not initialize ep %#x\n",
+ __func__, desc->bEndpointAddress);
+ return -ENOMEM;
+ }
+
+ ctrl_ctx->add_flags |= cpu_to_le32(added_ctxs);
+ new_add_flags = le32_to_cpu(ctrl_ctx->add_flags);
+ new_drop_flags = le32_to_cpu(ctrl_ctx->drop_flags);
+
+ dev_dbg(usbssp_data->dev,
+ "add ep 0x%x, new drop flags = %#x, new add flags = %#x\n",
+ (unsigned int) desc->bEndpointAddress,
+ (unsigned int) new_drop_flags,
+ (unsigned int) new_add_flags);
+ return 0;
+}
+
+static void usbssp_zero_in_ctx(struct usbssp_udc *usbssp_data,
+ struct usbssp_device *dev_priv)
+{
+ struct usbssp_input_control_ctx *ctrl_ctx;
+ struct usbssp_ep_ctx *ep_ctx;
+ struct usbssp_slot_ctx *slot_ctx;
+ int i;
+
+ ctrl_ctx = usbssp_get_input_control_ctx(dev_priv->in_ctx);
+ if (!ctrl_ctx) {
+ dev_warn(usbssp_data->dev,
+ "%s: Could not get input context, bad type.\n",
+ __func__);
+ return;
+ }
+
+ /*
+ * When a device's add flag and drop flag are zero, any subsequent
+ * configure endpoint command will leave that endpoint's state
+ * untouched. Make sure we don't leave any old state in the input
+ * endpoint contexts.
+ */
+ ctrl_ctx->drop_flags = 0;
+ ctrl_ctx->add_flags = 0;
+ slot_ctx = usbssp_get_slot_ctx(usbssp_data, dev_priv->in_ctx);
+ slot_ctx->dev_info &= cpu_to_le32(~LAST_CTX_MASK);
+
+ /* Endpoint 0 is always valid */
+ slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(1));
+ for (i = 1; i < 31; ++i) {
+ ep_ctx = usbssp_get_ep_ctx(usbssp_data, dev_priv->in_ctx, i);
+ ep_ctx->ep_info = 0;
+ ep_ctx->ep_info2 = 0;
+ ep_ctx->deq = 0;
+ ep_ctx->tx_info = 0;
+ }
+}
+
static int usbssp_configure_endpoint_result(struct usbssp_udc *usbssp_data,
struct usb_gadget *g,
u32 *cmd_status)
@@ -854,6 +1003,21 @@ static int usbssp_configure_endpoint(struct usbssp_udc *usbssp_data,
return ret;
}

+static void usbssp_check_bw_drop_ep_streams(struct usbssp_udc *usbssp_data,
+ struct usbssp_device *vdev, int i)
+{
+ struct usbssp_ep *ep = &vdev->eps[i];
+
+ if (ep->ep_state & EP_HAS_STREAMS) {
+ dev_warn(usbssp_data->dev,
+ "WARN: endpoint 0x%02x has streams on set_interface, freeing streams.\n",
+ usbssp_get_endpoint_address(i));
+ usbssp_free_stream_info(usbssp_data, ep->stream_info);
+ ep->stream_info = NULL;
+ ep->ep_state &= ~EP_HAS_STREAMS;
+ }
+}
+
int usbssp_halt_endpoint(struct usbssp_udc *usbssp_data, struct usbssp_ep *dep,
int value)
{
@@ -961,10 +1125,115 @@ int usbssp_halt_endpoint(struct usbssp_udc *usbssp_data, struct usbssp_ep *dep,
kfree(command->completion);
kfree(command);
return ret;
+}

- return 0;
+/**
+ * Called after one or more calls to usbssp_add_endpoint() or
+ * usbssp_drop_endpoint(). If this call fails, the driver is expected
+ * to call usbssp_reset_bandwidth().
+ */
+int usbssp_check_bandwidth(struct usbssp_udc *usbssp_data, struct usb_gadget *g)
+{
+ int i;
+ int ret = 0;
+ struct usbssp_device *dev_priv;
+ struct usbssp_input_control_ctx *ctrl_ctx;
+ struct usbssp_slot_ctx *slot_ctx;
+ struct usbssp_command *command;
+
+ ret = usbssp_check_args(usbssp_data, NULL, 0, true, __func__);
+ if (ret <= 0)
+ return ret;
+
+ if ((usbssp_data->usbssp_state & USBSSP_STATE_DYING) ||
+ (usbssp_data->usbssp_state & USBSSP_STATE_REMOVING))
+ return -ENODEV;
+
+ dev_priv = &usbssp_data->devs;
+
+ command = usbssp_alloc_command(usbssp_data, true, GFP_ATOMIC);
+ if (!command)
+ return -ENOMEM;
+
+ command->in_ctx = dev_priv->in_ctx;
+
+ ctrl_ctx = usbssp_get_input_control_ctx(command->in_ctx);
+ if (!ctrl_ctx) {
+ dev_warn(usbssp_data->dev,
+ "%s: Could not get input context, bad type.\n",
+ __func__);
+ ret = -ENOMEM;
+ goto command_cleanup;
+ }
+ ctrl_ctx->add_flags |= cpu_to_le32(SLOT_FLAG);
+ ctrl_ctx->add_flags &= cpu_to_le32(~EP0_FLAG);
+ ctrl_ctx->drop_flags &= cpu_to_le32(~(SLOT_FLAG | EP0_FLAG));
+
+ /* Don't issue the command if there's no endpoints to update.*/
+ if (ctrl_ctx->add_flags == cpu_to_le32(SLOT_FLAG) &&
+ ctrl_ctx->drop_flags == 0) {
+ ret = 0;
+ goto command_cleanup;
+ }
+
+ /* Fix up Context Entries field. Minimum value is EP0 == BIT(1). */
+ slot_ctx = usbssp_get_slot_ctx(usbssp_data, dev_priv->in_ctx);
+ for (i = 31; i >= 1; i--) {
+ __le32 le32 = cpu_to_le32(BIT(i));
+
+ if ((dev_priv->eps[i-1].ring && !(ctrl_ctx->drop_flags & le32))
+ || (ctrl_ctx->add_flags & le32) || i == 1) {
+ slot_ctx->dev_info &= cpu_to_le32(~LAST_CTX_MASK);
+ slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(i));
+ break;
+ }
+ }
+
+ ret = usbssp_configure_endpoint(usbssp_data, g, command,
+ false, false);
+
+ if (ret)
+ /* Caller should call reset_bandwidth() */
+ goto command_cleanup;
+
+ /* Free any rings that were dropped, but not changed. */
+ for (i = 1; i < 31; ++i) {
+ if ((le32_to_cpu(ctrl_ctx->drop_flags) & (1 << (i + 1))) &&
+ !(le32_to_cpu(ctrl_ctx->add_flags) & (1 << (i + 1)))) {
+ usbssp_free_endpoint_ring(usbssp_data, dev_priv, i);
+ usbssp_check_bw_drop_ep_streams(usbssp_data,
+ dev_priv, i);
+ }
+ }
+
+ usbssp_zero_in_ctx(usbssp_data, dev_priv);
+
+ /*
+ * Install any rings for completely new endpoints or changed endpoints,
+ * and free any old rings from changed endpoints.
+ */
+ for (i = 1; i < 31; ++i) {
+ if (!dev_priv->eps[i].new_ring)
+ continue;
+
+ /*
+ * Only free the old ring if it exists.
+ * It may not if this is the first add of an endpoint.
+ */
+ if (dev_priv->eps[i].ring)
+ usbssp_free_endpoint_ring(usbssp_data, dev_priv, i);
+
+ usbssp_check_bw_drop_ep_streams(usbssp_data, dev_priv, i);
+ dev_priv->eps[i].ring = dev_priv->eps[i].new_ring;
+ dev_priv->eps[i].new_ring = NULL;
+ }
+command_cleanup:
+ kfree(command->completion);
+ kfree(command);
+ return ret;
}

+
/*
* This submits a Reset Device Command, which will set the device state to 0,
* set the device address to 0, and disable all the endpoints except the default
diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h
index 81d7fe44519a..c4075e765dcc 100644
--- a/drivers/usb/usbssp/gadget.h
+++ b/drivers/usb/usbssp/gadget.h
@@ -1694,6 +1694,11 @@ void usbssp_copy_ep0_dequeue_into_input_ctx(struct usbssp_udc *usbssp_data);
unsigned int usbssp_get_endpoint_index(const struct usb_endpoint_descriptor *desc);
unsigned int usbssp_get_endpoint_address(unsigned int ep_index);
unsigned int usbssp_last_valid_endpoint(u32 added_ctxs);
+int usbssp_endpoint_init(struct usbssp_udc *usbssp_data,
+ struct usbssp_device *dev_priv,
+ struct usbssp_ep *dep,
+ gfp_t mem_flags);
+
int usbssp_ring_expansion(struct usbssp_udc *usbssp_data,
struct usbssp_ring *ring,
unsigned int num_trbs, gfp_t flags);
@@ -1705,6 +1710,9 @@ struct usbssp_ring *usbssp_dma_to_transfer_ring(struct usbssp_ep *ep,
struct usbssp_ring *usbssp_stream_id_to_ring(struct usbssp_device *dev,
unsigned int ep_index,
unsigned int stream_id);
+void usbssp_free_endpoint_ring(struct usbssp_udc *usbssp_data,
+ struct usbssp_device *dev_priv,
+ unsigned int ep_index);
void usbssp_free_stream_info(struct usbssp_udc *usbssp_data,
struct usbssp_stream_info *stream_info);
struct usbssp_ring *usbssp_dma_to_transfer_ring(
@@ -1857,6 +1865,8 @@ int usbssp_setup_analyze(struct usbssp_udc *usbssp_data);
int usbssp_status_stage(struct usbssp_udc *usbssp_data);

int usbssp_reset_device(struct usbssp_udc *usbssp_data);
+int usbssp_check_bandwidth(struct usbssp_udc *usbssp_data,
+ struct usb_gadget *g);

static inline struct usbssp_ring *usbssp_request_to_transfer_ring(
struct usbssp_udc *usbssp_data,
--
2.17.1


2018-07-19 18:03:42

by Pawel Laszczak

[permalink] [raw]
Subject: [PATCH 02/31] usb: usbssp: Added some decoding functions.

This patch add additional functions that converts some fields to string.

For example function usbssp_trb_comp_code_string take completion
code value and return string describing completion code.

Signed-off-by: Pawel Laszczak <[email protected]>
---
drivers/usb/usbssp/gadget.h | 580 ++++++++++++++++++++++++++++++++++++
1 file changed, 580 insertions(+)

diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h
index 49e7271187cc..b5c17603af78 100644
--- a/drivers/usb/usbssp/gadget.h
+++ b/drivers/usb/usbssp/gadget.h
@@ -930,6 +930,73 @@ struct usbssp_transfer_event {
#define COMP_UNDEFINED_ERROR 33
#define COMP_INVALID_STREAM_ID_ERROR 34

+static inline const char *usbssp_trb_comp_code_string(u8 status)
+{
+ switch (status) {
+ case COMP_INVALID:
+ return "Invalid";
+ case COMP_SUCCESS:
+ return "Success";
+ case COMP_DATA_BUFFER_ERROR:
+ return "Data Buffer Error";
+ case COMP_BABBLE_DETECTED_ERROR:
+ return "Babble Detected";
+ case COMP_USB_TRANSACTION_ERROR:
+ return "USB Transaction Error";
+ case COMP_TRB_ERROR:
+ return "TRB Error";
+ case COMP_RESOURCE_ERROR:
+ return "Resource Error";
+ case COMP_NO_SLOTS_AVAILABLE_ERROR:
+ return "No Slots Available Error";
+ case COMP_INVALID_STREAM_TYPE_ERROR:
+ return "Invalid Stream Type Error";
+ case COMP_SLOT_NOT_ENABLED_ERROR:
+ return "Slot Not Enabled Error";
+ case COMP_ENDPOINT_NOT_ENABLED_ERROR:
+ return "Endpoint Not Enabled Error";
+ case COMP_SHORT_PACKET:
+ return "Short Packet";
+ case COMP_RING_UNDERRUN:
+ return "Ring Underrun";
+ case COMP_RING_OVERRUN:
+ return "Ring Overrun";
+ case COMP_VF_EVENT_RING_FULL_ERROR:
+ return "VF Event Ring Full Error";
+ case COMP_PARAMETER_ERROR:
+ return "Parameter Error";
+ case COMP_CONTEXT_STATE_ERROR:
+ return "Context State Error";
+ case COMP_EVENT_RING_FULL_ERROR:
+ return "Event Ring Full Error";
+ case COMP_INCOMPATIBLE_DEVICE_ERROR:
+ return "Incompatible Device Error";
+ case COMP_MISSED_SERVICE_ERROR:
+ return "Missed Service Error";
+ case COMP_COMMAND_RING_STOPPED:
+ return "Command Ring Stopped";
+ case COMP_COMMAND_ABORTED:
+ return "Command Aborted";
+ case COMP_STOPPED:
+ return "Stopped";
+ case COMP_STOPPED_LENGTH_INVALID:
+ return "Stopped - Length Invalid";
+ case COMP_STOPPED_SHORT_PACKET:
+ return "Stopped - Short Packet";
+ case COMP_MAX_EXIT_LATENCY_TOO_LARGE_ERROR:
+ return "Max Exit Latency Too Large Error";
+ case COMP_ISOCH_BUFFER_OVERRUN:
+ return "Isoch Buffer Overrun";
+ case COMP_EVENT_LOST_ERROR:
+ return "Event Lost Error";
+ case COMP_UNDEFINED_ERROR:
+ return "Undefined Error";
+ case COMP_INVALID_STREAM_ID_ERROR:
+ return "Invalid Stream ID Error";
+ default:
+ return "Unknown!!";
+ }
+}
struct usbssp_link_trb {
/* 64-bit segment pointer*/
__le64 segment_ptr;
@@ -1292,6 +1359,27 @@ enum usbssp_ring_type {
TYPE_EVENT,
};

+static inline const char *usbssp_ring_type_string(enum usbssp_ring_type type)
+{
+ switch (type) {
+ case TYPE_CTRL:
+ return "CTRL";
+ case TYPE_ISOC:
+ return "ISOC";
+ case TYPE_BULK:
+ return "BULK";
+ case TYPE_INTR:
+ return "INTR";
+ case TYPE_STREAM:
+ return "STREAM";
+ case TYPE_COMMAND:
+ return "CMD";
+ case TYPE_EVENT:
+ return "EVENT";
+ }
+
+ return "UNKNOWN";
+}
struct usbssp_ring {
struct usbssp_segment *first_seg;
struct usbssp_segment *last_seg;
@@ -1568,4 +1656,496 @@ struct usbssp_udc {

#define USBSSP_CFC_DELAY 10

+
+/*
+ * Registers should always be accessed with double word or quad word accesses.
+ *
+ * Registers with 64-bit address pointers should be written to with
+ * dword accesses by writing the low dword first (ptr[0]), then the high dword
+ * (ptr[1]) second. DC implementations that do not support 64-bit address
+ * pointers will ignore the high dword, and write order is irrelevant.
+ */
+static inline u64 usbssp_read_64(const struct usbssp_udc *usbssp_data,
+ __le64 __iomem *regs)
+{
+ return lo_hi_readq(regs);
+}
+
+static inline void usbssp_write_64(struct usbssp_udc *usbssp_data,
+ const u64 val, __le64 __iomem *regs)
+{
+ lo_hi_writeq(val, regs);
+}
+static inline char *usbssp_slot_state_string(u32 state)
+{
+ switch (state) {
+ case SLOT_STATE_ENABLED:
+ return "enabled/disabled";
+ case SLOT_STATE_DEFAULT:
+ return "default";
+ case SLOT_STATE_ADDRESSED:
+ return "addressed";
+ case SLOT_STATE_CONFIGURED:
+ return "configured";
+ default:
+ return "reserved";
+ }
+}
+
+static inline const char *usbssp_decode_trb(u32 field0, u32 field1, u32 field2,
+ u32 field3)
+{
+ static char str[256];
+ int type = TRB_FIELD_TO_TYPE(field3);
+
+ switch (type) {
+ case TRB_LINK:
+ sprintf(str,
+ "LINK %08x%08x intr %d type '%s' flags %c:%c:%c:%c",
+ field1, field0, GET_INTR_TARGET(field2),
+ usbssp_trb_type_string(type),
+ field3 & TRB_IOC ? 'I' : 'i',
+ field3 & TRB_CHAIN ? 'C' : 'c',
+ field3 & TRB_TC ? 'T' : 't',
+ field3 & TRB_CYCLE ? 'C' : 'c');
+ break;
+ case TRB_TRANSFER:
+ case TRB_COMPLETION:
+ case TRB_PORT_STATUS:
+ case TRB_DOORBELL:
+ case TRB_HC_EVENT:
+ case TRB_DEV_NOTE:
+ case TRB_MFINDEX_WRAP:
+ sprintf(str,
+ "TRB %08x%08x status '%s' len %d slot %d ep %d:=:"
+ "type '%s' flags %c:%c",
+ field1, field0,
+ usbssp_trb_comp_code_string(GET_COMP_CODE(field2)),
+ EVENT_TRB_LEN(field2), TRB_TO_SLOT_ID(field3),
+ /* Macro decrements 1, maybe it shouldn't?!? */
+ TRB_TO_EP_INDEX(field3) + 1,
+ usbssp_trb_type_string(type),
+ field3 & EVENT_DATA ? 'E' : 'e',
+ field3 & TRB_CYCLE ? 'C' : 'c');
+
+ break;
+ case TRB_SETUP:
+ sprintf(str, "bRequestType %02x bRequest %02x wValue %02x%02x "
+ "wIndex %02x%02x wLength %d length %d "
+ "TD size %d intr %d type '%s' flags %c:%c:%c",
+ field0 & 0xff,
+ (field0 & 0xff00) >> 8,
+ (field0 & 0xff000000) >> 24,
+ (field0 & 0xff0000) >> 16,
+ (field1 & 0xff00) >> 8,
+ field1 & 0xff,
+ (field1 & 0xff000000) >> 16 |
+ (field1 & 0xff0000) >> 16,
+ TRB_LEN(field2), GET_TD_SIZE(field2),
+ GET_INTR_TARGET(field2),
+ usbssp_trb_type_string(type),
+ field3 & TRB_IDT ? 'I' : 'i',
+ field3 & TRB_IOC ? 'I' : 'i',
+ field3 & TRB_CYCLE ? 'C' : 'c');
+ break;
+ case TRB_DATA:
+ sprintf(str, "Buffer %08x%08x length %d TD size %d intr %d "
+ "type '%s' flags %c:%c:%c:%c:%c:%c:%c",
+ field1, field0, TRB_LEN(field2),
+ GET_TD_SIZE(field2),
+ GET_INTR_TARGET(field2),
+ usbssp_trb_type_string(type),
+ field3 & TRB_IDT ? 'I' : 'i',
+ field3 & TRB_IOC ? 'I' : 'i',
+ field3 & TRB_CHAIN ? 'C' : 'c',
+ field3 & TRB_NO_SNOOP ? 'S' : 's',
+ field3 & TRB_ISP ? 'I' : 'i',
+ field3 & TRB_ENT ? 'E' : 'e',
+ field3 & TRB_CYCLE ? 'C' : 'c');
+ break;
+ case TRB_STATUS:
+ sprintf(str, "Buffer %08x%08x length %d TD size %d intr"
+ "%d type '%s' flags %c:%c:%c:%c",
+ field1, field0, TRB_LEN(field2),
+ GET_TD_SIZE(field2),
+ GET_INTR_TARGET(field2),
+ usbssp_trb_type_string(type),
+ field3 & TRB_IOC ? 'I' : 'i',
+ field3 & TRB_CHAIN ? 'C' : 'c',
+ field3 & TRB_ENT ? 'E' : 'e',
+ field3 & TRB_CYCLE ? 'C' : 'c');
+ break;
+ case TRB_NORMAL:
+ case TRB_ISOC:
+ case TRB_EVENT_DATA:
+ case TRB_TR_NOOP:
+ sprintf(str,
+ "Buffer %08x%08x length %d TD size %d intr %d "
+ "type '%s' flags %c:%c:%c:%c:%c:%c:%c:%c",
+ field1, field0, TRB_LEN(field2), GET_TD_SIZE(field2),
+ GET_INTR_TARGET(field2),
+ usbssp_trb_type_string(type),
+ field3 & TRB_BEI ? 'B' : 'b',
+ field3 & TRB_IDT ? 'I' : 'i',
+ field3 & TRB_IOC ? 'I' : 'i',
+ field3 & TRB_CHAIN ? 'C' : 'c',
+ field3 & TRB_NO_SNOOP ? 'S' : 's',
+ field3 & TRB_ISP ? 'I' : 'i',
+ field3 & TRB_ENT ? 'E' : 'e',
+ field3 & TRB_CYCLE ? 'C' : 'c');
+ break;
+
+ case TRB_CMD_NOOP:
+ case TRB_ENABLE_SLOT:
+ sprintf(str,
+ "%s: flags %c",
+ usbssp_trb_type_string(type),
+ field3 & TRB_CYCLE ? 'C' : 'c');
+ break;
+ case TRB_DISABLE_SLOT:
+ sprintf(str,
+ "%s: slot %d flags %c",
+ usbssp_trb_type_string(type),
+ TRB_TO_SLOT_ID(field3),
+ field3 & TRB_CYCLE ? 'C' : 'c');
+ break;
+ case TRB_ADDR_DEV:
+ sprintf(str,
+ "%s: ctx %08x%08x slot %d flags %c:%c",
+ usbssp_trb_type_string(type),
+ field1, field0,
+ TRB_TO_SLOT_ID(field3),
+ field3 & TRB_BSR ? 'B' : 'b',
+ field3 & TRB_CYCLE ? 'C' : 'c');
+ break;
+ case TRB_CONFIG_EP:
+ sprintf(str,
+ "%s: ctx %08x%08x slot %d flags %c:%c",
+ usbssp_trb_type_string(type),
+ field1, field0,
+ TRB_TO_SLOT_ID(field3),
+ field3 & TRB_DC ? 'D' : 'd',
+ field3 & TRB_CYCLE ? 'C' : 'c');
+ break;
+ case TRB_EVAL_CONTEXT:
+ sprintf(str,
+ "%s: ctx %08x%08x slot %d flags %c",
+ usbssp_trb_type_string(type),
+ field1, field0,
+ TRB_TO_SLOT_ID(field3),
+ field3 & TRB_CYCLE ? 'C' : 'c');
+ break;
+ case TRB_RESET_EP:
+ case TRB_HALT_ENDPOINT:
+ case TRB_FLUSH_ENDPOINT:
+ sprintf(str,
+ "%s: ctx %08x%08x slot %d ep %d flags %c",
+ usbssp_trb_type_string(type),
+ field1, field0,
+ TRB_TO_SLOT_ID(field3),
+ /* Macro decrements 1, maybe it shouldn't?!? */
+ TRB_TO_EP_INDEX(field3) + 1,
+ field3 & TRB_CYCLE ? 'C' : 'c');
+ break;
+ case TRB_STOP_RING:
+ sprintf(str,
+ "%s: slot %d sp %d ep %d flags %c",
+ usbssp_trb_type_string(type),
+ TRB_TO_SLOT_ID(field3),
+ TRB_TO_SUSPEND_PORT(field3),
+ /* Macro decrements 1, maybe it shouldn't?!? */
+ TRB_TO_EP_INDEX(field3) + 1,
+ field3 & TRB_CYCLE ? 'C' : 'c');
+ break;
+ case TRB_SET_DEQ:
+ sprintf(str,
+ "%s: deq %08x%08x stream %d slot %d ep %d flags %c",
+ usbssp_trb_type_string(type),
+ field1, field0,
+ TRB_TO_STREAM_ID(field2),
+ TRB_TO_SLOT_ID(field3),
+ /* Macro decrements 1, maybe it shouldn't?!? */
+ TRB_TO_EP_INDEX(field3) + 1,
+ field3 & TRB_CYCLE ? 'C' : 'c');
+ break;
+ case TRB_RESET_DEV:
+ sprintf(str,
+ "%s: slot %d flags %c",
+ usbssp_trb_type_string(type),
+ TRB_TO_SLOT_ID(field3),
+ field3 & TRB_CYCLE ? 'C' : 'c');
+ break;
+ case TRB_FORCE_EVENT:
+ sprintf(str,
+ "%s: event %08x%08x vf intr %d vf id %d flags %c",
+ usbssp_trb_type_string(type),
+ field1, field0,
+ TRB_TO_VF_INTR_TARGET(field2),
+ TRB_TO_VF_ID(field3),
+ field3 & TRB_CYCLE ? 'C' : 'c');
+ break;
+ case TRB_SET_LT:
+ sprintf(str,
+ "%s: belt %d flags %c",
+ usbssp_trb_type_string(type),
+ TRB_TO_BELT(field3),
+ field3 & TRB_CYCLE ? 'C' : 'c');
+ break;
+ case TRB_FORCE_HEADER:
+ sprintf(str,
+ "%s: info %08x%08x%08x pkt type %d roothub port %d flags %c",
+ usbssp_trb_type_string(type),
+ field2, field1, field0 & 0xffffffe0,
+ TRB_TO_PACKET_TYPE(field0),
+ TRB_TO_DEV_PORT(field3),
+ field3 & TRB_CYCLE ? 'C' : 'c');
+ break;
+ case TRB_ENDPOINT_NRDY:
+ sprintf(str,
+ "%s: flags %c",
+ usbssp_trb_type_string(type),
+ field3 & TRB_CYCLE ? 'C' : 'c');
+ default:
+ sprintf(str,
+ "type '%s' -> raw %08x %08x %08x %08x",
+ usbssp_trb_type_string(type),
+ field0, field1, field2, field3);
+ }
+
+ return str;
+}
+
+static inline const char *usbssp_decode_slot_context(u32 info, u32 info2,
+ u32 int_target, u32 state)
+{
+ static char str[1024];
+ u32 speed;
+ int ret = 0;
+
+ speed = info & DEV_SPEED;
+
+ ret = sprintf(str, "%s Ctx Entries %ld MEL %d us",
+ ({ char *s;
+ switch (speed) {
+ case SLOT_SPEED_FS:
+ s = "full-speed";
+ break;
+ case SLOT_SPEED_LS:
+ s = "low-speed";
+ break;
+ case SLOT_SPEED_HS:
+ s = "high-speed";
+ break;
+ case SLOT_SPEED_SS:
+ s = "super-speed";
+ break;
+ case SLOT_SPEED_SSP:
+ s = "super-speed plus";
+ break;
+ default:
+ s = "UNKNOWN speed";
+ } s; }),
+ (info & LAST_CTX_MASK) >> 27,
+ info2 & MAX_EXIT);
+
+ ret += sprintf(str + ret, " [Intr %d] Addr %ld State %s",
+ GET_INTR_TARGET(int_target),
+ state & DEV_ADDR_MASK,
+ usbssp_slot_state_string(GET_SLOT_STATE(state)));
+
+ return str;
+}
+
+
+static inline const char *usbssp_portsc_link_state_string(u32 portsc)
+{
+ switch (portsc & PORT_PLS_MASK) {
+ case XDEV_U0:
+ return "U0";
+ case XDEV_U1:
+ return "U1";
+ case XDEV_U2:
+ return "U2";
+ case XDEV_U3:
+ return "U3";
+ case XDEV_DISABLED:
+ return "Disabled";
+ case XDEV_RXDETECT:
+ return "RxDetect";
+ case XDEV_INACTIVE:
+ return "Inactive";
+ case XDEV_POLLING:
+ return "Polling";
+ case XDEV_RECOVERY:
+ return "Recovery";
+ case XDEV_HOT_RESET:
+ return "Hot Reset";
+ case XDEV_COMP_MODE:
+ return "Compliance mode";
+ case XDEV_TEST_MODE:
+ return "Test mode";
+ case XDEV_RESUME:
+ return "Resume";
+ default:
+ break;
+ }
+ return "Unknown";
+}
+
+static inline const char *usbssp_decode_portsc(u32 portsc)
+{
+ static char str[256];
+ int ret;
+
+ ret = sprintf(str, "%s %s %s Link:%s PortSpeed:%d ",
+ portsc & PORT_POWER ? "Powered" : "Powered-off",
+ portsc & PORT_CONNECT ? "Connected" : "Not-connected",
+ portsc & PORT_PE ? "Enabled" : "Disabled",
+ usbssp_portsc_link_state_string(portsc),
+ DEV_PORT_SPEED(portsc));
+
+ if (portsc & PORT_OC)
+ ret += sprintf(str + ret, "OverCurrent ");
+ if (portsc & PORT_RESET)
+ ret += sprintf(str + ret, "In-Reset ");
+
+ ret += sprintf(str + ret, "Change: ");
+ if (portsc & PORT_CSC)
+ ret += sprintf(str + ret, "CSC ");
+ if (portsc & PORT_PEC)
+ ret += sprintf(str + ret, "PEC ");
+ if (portsc & PORT_WRC)
+ ret += sprintf(str + ret, "WRC ");
+ if (portsc & PORT_OCC)
+ ret += sprintf(str + ret, "OCC ");
+ if (portsc & PORT_RC)
+ ret += sprintf(str + ret, "PRC ");
+ if (portsc & PORT_PLC)
+ ret += sprintf(str + ret, "PLC ");
+ if (portsc & PORT_CEC)
+ ret += sprintf(str + ret, "CEC ");
+ ret += sprintf(str + ret, "Wake: ");
+ if (portsc & PORT_WKCONN_E)
+ ret += sprintf(str + ret, "WCE ");
+ if (portsc & PORT_WKDISC_E)
+ ret += sprintf(str + ret, "WDE ");
+ if (portsc & PORT_WKOC_E)
+ ret += sprintf(str + ret, "WOE ");
+
+ return str;
+}
+
+static inline const char *usbssp_ep_state_string(u8 state)
+{
+ switch (state) {
+ case EP_STATE_DISABLED:
+ return "disabled";
+ case EP_STATE_RUNNING:
+ return "running";
+ case EP_STATE_HALTED:
+ return "halted";
+ case EP_STATE_STOPPED:
+ return "stopped";
+ case EP_STATE_ERROR:
+ return "error";
+ default:
+ return "INVALID";
+ }
+}
+
+static inline const char *usbssp_ep_type_string(u8 type)
+{
+ switch (type) {
+ case ISOC_OUT_EP:
+ return "Isoc OUT";
+ case BULK_OUT_EP:
+ return "Bulk OUT";
+ case INT_OUT_EP:
+ return "Int OUT";
+ case CTRL_EP:
+ return "Ctrl";
+ case ISOC_IN_EP:
+ return "Isoc IN";
+ case BULK_IN_EP:
+ return "Bulk IN";
+ case INT_IN_EP:
+ return "Int IN";
+ default:
+ return "INVALID";
+ }
+}
+
+static inline const char *usbssp_decode_ep_context(u32 info, u32 info2, u64 deq,
+ u32 tx_info)
+{
+ static char str[1024];
+ int ret;
+
+ u32 esit;
+ u16 maxp;
+ u16 avg;
+
+ u8 max_pstr;
+ u8 ep_state;
+ u8 interval;
+ u8 ep_type;
+ u8 burst;
+ u8 cerr;
+ u8 mult;
+
+ bool lsa;
+ bool hid;
+
+ esit = CTX_TO_MAX_ESIT_PAYLOAD_HI(info) << 16 |
+ CTX_TO_MAX_ESIT_PAYLOAD(tx_info);
+
+ ep_state = info & EP_STATE_MASK;
+ max_pstr = CTX_TO_EP_MAXPSTREAMS(info);
+ interval = CTX_TO_EP_INTERVAL(info);
+ mult = CTX_TO_EP_MULT(info) + 1;
+ lsa = !!(info & EP_HAS_LSA);
+
+ cerr = (info2 & (3 << 1)) >> 1;
+ ep_type = CTX_TO_EP_TYPE(info2);
+ hid = !!(info2 & (1 << 7));
+ burst = CTX_TO_MAX_BURST(info2);
+ maxp = MAX_PACKET_DECODED(info2);
+
+ avg = EP_AVG_TRB_LENGTH(tx_info);
+
+ ret = sprintf(str, "State %s mult %d max P. Streams %d %s",
+ usbssp_ep_state_string(ep_state), mult,
+ max_pstr, lsa ? "LSA " : "");
+
+ ret += sprintf(str + ret, "interval %d us max ESIT payload %d CErr %d ",
+ (1 << interval) * 125, esit, cerr);
+
+ ret += sprintf(str + ret, "Type %s %sburst %d maxp %d deq %016llx ",
+ usbssp_ep_type_string(ep_type), hid ? "HID" : "",
+ burst, maxp, deq);
+
+ ret += sprintf(str + ret, "avg trb len %d", avg);
+
+ return str;
+}
+
+/**
+ * next_request - gets the next request on the given list
+ * @list: the request list to operate on
+ *
+ * Caller should take care of locking. This function return %NULL or the first
+ * request available on @list.
+ */
+static inline struct usbssp_request *next_request(struct list_head *list)
+{
+ return list_first_entry_or_null(list, struct usbssp_request, list);
+}
+
+struct usbssp_udc;
+#define to_usbssp_ep(ep) (container_of(ep, struct usbssp_ep, endpoint))
+#define gadget_to_usbssp(g) (container_of(g, struct usbssp_udc, gadget))
+#define request_to_usbssp_request(r) (container_of(r, struct usbssp_request, request))
+
+#define to_usbssp_request(r) (container_of(r, struct usbssp_request, request))
+
#endif /* __LINUX_USBSSP_GADGET_H */
--
2.17.1


2018-07-19 18:03:53

by Pawel Laszczak

[permalink] [raw]
Subject: [PATCH 20/31] usb: usbssp: added queuing procedure for control transfer.

Patch implements generic use in driver usbssp_enqueue function.
All requests queuing in driver must be send with it help.

It also adds specific for control transfer usbssp_queue_ctrl_tx function
that prepares TRB, adds them to EP0 transfer ring and set doorbell.

Signed-off-by: Pawel Laszczak <[email protected]>
---
drivers/usb/usbssp/gadget-ring.c | 272 +++++++++++++++++++++++++++++++
drivers/usb/usbssp/gadget.c | 115 ++++++++++++-
drivers/usb/usbssp/gadget.h | 29 ++++
3 files changed, 414 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/usbssp/gadget-ring.c b/drivers/usb/usbssp/gadget-ring.c
index 1b57f5180115..a0cfce0dc49d 100644
--- a/drivers/usb/usbssp/gadget-ring.c
+++ b/drivers/usb/usbssp/gadget-ring.c
@@ -310,6 +310,43 @@ static void ring_doorbell_for_active_rings(struct usbssp_udc *usbssp_data,
}
}

+/* Get the right ring for the given ep_index and stream_id.
+ * If the endpoint supports streams, boundary check the USB request's stream ID.
+ * If the endpoint doesn't support streams, return the singular endpoint ring.
+ */
+struct usbssp_ring *usbssp_triad_to_transfer_ring(struct usbssp_udc *usbssp_data,
+ unsigned int ep_index,
+ unsigned int stream_id)
+{
+ struct usbssp_ep *ep;
+
+ ep = &usbssp_data->devs.eps[ep_index];
+
+ /* Common case: no streams */
+ if (!(ep->ep_state & EP_HAS_STREAMS))
+ return ep->ring;
+
+ if (stream_id == 0) {
+ dev_warn(usbssp_data->dev,
+ "WARN: ep index %u has streams, "
+ "but USB Request has no stream ID.\n",
+ ep_index);
+ return NULL;
+ }
+
+ if (stream_id < ep->stream_info->num_streams)
+ return ep->stream_info->stream_rings[stream_id];
+
+ dev_warn(usbssp_data->dev,
+ "WARN: ep index %u has "
+ "stream IDs 1 to %u allocated, "
+ "but stream ID %u is requested.\n",
+ ep_index,
+ ep->stream_info->num_streams - 1,
+ stream_id);
+ return NULL;
+}
+
/* Must be called with usbssp_data->lock held in interrupt context
* or usbssp_data->irq_thread_lock from thread conext (defered interrupt)
*/
@@ -1494,6 +1531,232 @@ static int prepare_ring(struct usbssp_udc *usbssp_data,
return 0;
}

+static int prepare_transfer(struct usbssp_udc *usbssp_data,
+ struct usbssp_device *dev_priv,
+ unsigned int ep_index,
+ unsigned int stream_id,
+ unsigned int num_trbs,
+ struct usbssp_request *req_priv,
+ unsigned int td_index,
+ gfp_t mem_flags)
+{
+ int ret;
+ struct usbssp_td *td;
+ struct usbssp_ring *ep_ring;
+ struct usbssp_ep_ctx *ep_ctx = usbssp_get_ep_ctx(usbssp_data,
+ dev_priv->out_ctx, ep_index);
+
+ ep_ring = usbssp_stream_id_to_ring(dev_priv, ep_index, stream_id);
+
+ if (!ep_ring) {
+ dev_dbg(usbssp_data->dev,
+ "Can't prepare ring for bad stream ID %u\n",
+ stream_id);
+ return -EINVAL;
+ }
+
+ ret = prepare_ring(usbssp_data, ep_ring, GET_EP_CTX_STATE(ep_ctx),
+ num_trbs, mem_flags);
+
+ if (ret)
+ return ret;
+
+ td = &req_priv->td[td_index];
+ INIT_LIST_HEAD(&td->td_list);
+
+ td->priv_request = req_priv;
+ /* Add this TD to the tail of the endpoint ring's TD list */
+ list_add_tail(&td->td_list, &ep_ring->td_list);
+ td->start_seg = ep_ring->enq_seg;
+ td->first_trb = ep_ring->enqueue;
+
+ return 0;
+}
+
+/*
+ * USBSSP uses normal TRBs for both bulk and interrupt. When the interrupt
+ * endpoint is to be serviced, the DC will consume (at most) one TD. A TD
+ * (comprised of sg list entries) can take several service intervals to
+ * transmit.
+ */
+int usbssp_queue_intr_tx(struct usbssp_udc *usbssp_data, gfp_t mem_flags,
+ struct usbssp_request *req_priv, unsigned int ep_index)
+{
+ struct usbssp_ep_ctx *ep_ctx;
+
+ ep_ctx = usbssp_get_ep_ctx(usbssp_data, usbssp_data->devs.out_ctx,
+ ep_index);
+
+ return usbssp_queue_bulk_tx(usbssp_data, mem_flags, req_priv, ep_index);
+}
+
+/*
+ * For USBSSP controllers, TD size is the number of max packet sized
+ * packets remaining in the TD (*not* including this TRB).
+ *
+ * Total TD packet count = total_packet_count =
+ * DIV_ROUND_UP(TD size in bytes / wMaxPacketSize)
+ *
+ * Packets transferred up to and including this TRB = packets_transferred =
+ * rounddown(total bytes transferred including this TRB / wMaxPacketSize)
+ *
+ * TD size = total_packet_count - packets_transferred
+ *
+ * For USBSSP it must fit in bits 21:17, so it can't be bigger than 31.
+ * This is taken care of in the TRB_TD_SIZE() macro
+ *
+ * The last TRB in a TD must have the TD size set to zero.
+ */
+static u32 usbssp_td_remainder(struct usbssp_udc *usbssp_data,
+ int transferred,
+ int trb_buff_len,
+ unsigned int td_total_len,
+ struct usbssp_request *req_priv,
+ bool more_trbs_coming)
+{
+ u32 maxp, total_packet_count;
+
+ /* One TRB with a zero-length data packet. */
+ if (!more_trbs_coming || (transferred == 0 && trb_buff_len == 0) ||
+ trb_buff_len == td_total_len)
+ return 0;
+
+ maxp = usb_endpoint_maxp(req_priv->dep->endpoint.desc);
+ total_packet_count = DIV_ROUND_UP(td_total_len, maxp);
+
+ /* Queuing functions don't count the current TRB into transferred */
+ return (total_packet_count - ((transferred + trb_buff_len) / maxp));
+}
+
+int usbssp_queue_bulk_tx(struct usbssp_udc *usbssp_data,
+ gfp_t mem_flags,
+ struct usbssp_request *req_priv,
+ unsigned int ep_index)
+{
+ /*TODO: function musb be implemented*/
+ return 0;
+}
+
+int usbssp_queue_ctrl_tx(struct usbssp_udc *usbssp_data,
+ gfp_t mem_flags,
+ struct usbssp_request *req_priv,
+ unsigned int ep_index)
+{
+ struct usbssp_ring *ep_ring;
+ int num_trbs;
+ int ret;
+ struct usbssp_generic_trb *start_trb;
+ int start_cycle;
+ u32 field, length_field, remainder;
+ struct usbssp_td *td;
+ struct usbssp_ep *dep = req_priv->dep;
+
+ ep_ring = usbssp_request_to_transfer_ring(usbssp_data, req_priv);
+ if (!ep_ring)
+ return -EINVAL;
+
+ if (usbssp_data->delayed_status) {
+ dev_dbg(usbssp_data->dev, "Queue CTRL: delayed finished\n");
+ usbssp_data->delayed_status = false;
+ usb_gadget_set_state(&usbssp_data->gadget,
+ USB_STATE_CONFIGURED);
+ }
+
+ /* 1 TRB for data, 1 for status */
+ if (usbssp_data->three_stage_setup)
+ num_trbs = 2;
+ else
+ num_trbs = 1;
+
+ ret = prepare_transfer(usbssp_data, &usbssp_data->devs,
+ req_priv->epnum, req_priv->request.stream_id,
+ num_trbs, req_priv, 0, mem_flags);
+
+ if (ret < 0)
+ return ret;
+
+ td = &req_priv->td[0];
+
+ /*
+ * Don't give the first TRB to the hardware (by toggling the cycle bit)
+ * until we've finished creating all the other TRBs. The ring's cycle
+ * state may change as we enqueue the other TRBs, so save it too.
+ */
+ start_trb = &ep_ring->enqueue->generic;
+ start_cycle = ep_ring->cycle_state;
+
+ /* If there's data, queue data TRBs */
+ /* Only set interrupt on short packet for OUT endpoints */
+
+ if (usbssp_data->ep0_expect_in)
+ field = TRB_TYPE(TRB_DATA) | TRB_IOC;
+ else
+ field = TRB_ISP | TRB_TYPE(TRB_DATA) | TRB_IOC;
+
+ if (req_priv->request.length > 0) {
+ remainder = usbssp_td_remainder(usbssp_data, 0,
+ req_priv->request.length,
+ req_priv->request.length,
+ req_priv, 1);
+
+ length_field = TRB_LEN(req_priv->request.length) |
+ TRB_TD_SIZE(remainder) |
+ TRB_INTR_TARGET(0);
+
+ if (usbssp_data->ep0_expect_in)
+ field |= TRB_DIR_IN;
+
+ queue_trb(usbssp_data, ep_ring, true,
+ lower_32_bits(req_priv->request.dma),
+ upper_32_bits(req_priv->request.dma),
+ length_field,
+ field | ep_ring->cycle_state |
+ TRB_SETUPID(usbssp_data->setupId) |
+ usbssp_data->setup_speed);
+ usbssp_data->ep0state = USBSSP_EP0_DATA_PHASE;
+ }
+
+ /* Save the DMA address of the last TRB in the TD */
+ td->last_trb = ep_ring->enqueue;
+
+ /* Queue status TRB*/
+
+ if (req_priv->request.length > 0 && usbssp_data->ep0_expect_in)
+ field = TRB_DIR_IN;
+ else
+ field = 0;
+
+ if (req_priv->request.length == 0)
+ field |= ep_ring->cycle_state;
+ else
+ field |= (ep_ring->cycle_state ^ 1);
+
+ if (dep->ep_state & EP0_HALTED_STATUS) {
+ /*
+ * If endpoint should be halted in Status Stage then
+ * driver shall set TRB_SETUPSTAT_STALL bit
+ */
+ dev_dbg(usbssp_data->dev,
+ "Status Stage phase prepared with STALL bit\n");
+ dep->ep_state &= ~EP0_HALTED_STATUS;
+ field |= TRB_SETUPSTAT(TRB_SETUPSTAT_STALL);
+ } else {
+ field |= TRB_SETUPSTAT(TRB_SETUPSTAT_ACK);
+ }
+
+ queue_trb(usbssp_data, ep_ring, false,
+ 0,
+ 0,
+ TRB_INTR_TARGET(0),
+ /* Event on completion */
+ field | TRB_IOC | TRB_SETUPID(usbssp_data->setupId) |
+ TRB_TYPE(TRB_STATUS) | usbssp_data->setup_speed);
+
+ usbssp_ring_ep_doorbell(usbssp_data, ep_index,
+ req_priv->request.stream_id);
+ return 0;
+}
+
/* Stop endpoint after disconnecting device.*/
int usbssp_cmd_stop_ep(struct usbssp_udc *usbssp_data, struct usb_gadget *g,
struct usbssp_ep *ep_priv)
@@ -1555,6 +1818,15 @@ int usbssp_cmd_stop_ep(struct usbssp_udc *usbssp_data, struct usb_gadget *g,
return ret;
}

+int usbssp_queue_isoc_tx_prepare(struct usbssp_udc *usbssp_data,
+ gfp_t mem_flags,
+ struct usbssp_request *req_priv,
+ unsigned int ep_index)
+{
+ /*TODO: function must be implemented*/
+ return 0;
+}
+
/**** Command Ring Operations ****/
/*
* Generic function for queueing a command TRB on the command ring.
diff --git a/drivers/usb/usbssp/gadget.c b/drivers/usb/usbssp/gadget.c
index 2fd6d27ef0bd..e2751693404d 100644
--- a/drivers/usb/usbssp/gadget.c
+++ b/drivers/usb/usbssp/gadget.c
@@ -401,8 +401,119 @@ static int usbssp_check_args(struct usbssp_udc *usbssp_data,

int usbssp_enqueue(struct usbssp_ep *dep, struct usbssp_request *req_priv)
{
- /*TODO: this function must be implemented*/
- return 0;
+ int ret = 0;
+ unsigned int ep_index;
+ unsigned int ep_state;
+ const struct usb_endpoint_descriptor *desc;
+ struct usbssp_udc *usbssp_data = dep->usbssp_data;
+ int num_tds;
+
+ if (usbssp_check_args(usbssp_data, dep, true, true, __func__) <= 0)
+ return -EINVAL;
+
+ if (!dep->endpoint.desc) {
+ dev_err(usbssp_data->dev, "%s: can't queue to disabled endpoint\n",
+ dep->name);
+ return -ESHUTDOWN;
+ }
+
+ if (WARN(req_priv->dep != dep, "request %p belongs to '%s'\n",
+ &req_priv->request, req_priv->dep->name)) {
+ dev_err(usbssp_data->dev, "%s: reequest %p belongs to '%s'\n",
+ dep->name, &req_priv->request, req_priv->dep->name);
+ return -EINVAL;
+ }
+
+ if (!list_empty(&dep->pending_list) && req_priv->epnum == 0) {
+ dev_warn(usbssp_data->dev,
+ "Ep0 has incomplete previous transfer'\n");
+ return -EBUSY;
+ }
+
+ //pm_runtime_get(usbssp_data->dev);
+ req_priv->request.actual = 0;
+ req_priv->request.status = -EINPROGRESS;
+ req_priv->direction = dep->direction;
+ req_priv->epnum = dep->number;
+
+ desc = req_priv->dep->endpoint.desc;
+ ep_index = usbssp_get_endpoint_index(desc);
+ ep_state = usbssp_data->devs.eps[ep_index].ep_state;
+ req_priv->sg = req_priv->request.sg;
+
+ req_priv->num_pending_sgs = req_priv->request.num_mapped_sgs;
+ dev_info(usbssp_data->dev, "SG list addr: %p with %d elements.\n",
+ req_priv->sg, req_priv->num_pending_sgs);
+
+ list_add_tail(&req_priv->list, &dep->pending_list);
+
+ if (req_priv->num_pending_sgs > 0)
+ num_tds = req_priv->num_pending_sgs;
+ else
+ num_tds = 1;
+
+ if (req_priv->request.zero && req_priv->request.length &&
+ (req_priv->request.length & (dep->endpoint.maxpacket == 0))) {
+ num_tds++;
+ }
+
+ ret = usb_gadget_map_request_by_dev(usbssp_data->dev,
+ &req_priv->request,
+ dep->direction);
+
+ if (ret) {
+ dev_err(usbssp_data->dev, "Can't map request to DMA\n");
+ goto req_del;
+ }
+
+ /*allocating memory for transfer descriptors*/
+ req_priv->td = kzalloc(num_tds * sizeof(struct usbssp_td), GFP_ATOMIC);
+
+ if (!req_priv->td) {
+ ret = -ENOMEM;
+ goto free_priv;
+ }
+
+ if (ep_state & (EP_GETTING_STREAMS | EP_GETTING_NO_STREAMS)) {
+ dev_warn(usbssp_data->dev, "WARN: Can't enqueue USB Request, "
+ "ep in streams transition state %x\n",
+ ep_state);
+ ret = -EINVAL;
+ goto free_priv;
+ }
+
+ req_priv->num_tds = num_tds;
+ req_priv->num_tds_done = 0;
+ trace_usbssp_request_enqueue(&req_priv->request);
+
+ switch (usb_endpoint_type(desc)) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ ret = usbssp_queue_ctrl_tx(usbssp_data, GFP_ATOMIC, req_priv,
+ ep_index);
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ ret = usbssp_queue_bulk_tx(usbssp_data, GFP_ATOMIC, req_priv,
+ ep_index);
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ ret = usbssp_queue_intr_tx(usbssp_data, GFP_ATOMIC, req_priv,
+ ep_index);
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ ret = usbssp_queue_isoc_tx_prepare(usbssp_data, GFP_ATOMIC,
+ req_priv, ep_index);
+ }
+
+ if (ret < 0) {
+free_priv:
+ usb_gadget_unmap_request_by_dev(usbssp_data->dev,
+ &req_priv->request, dep->direction);
+ usbssp_request_free_priv(req_priv);
+
+req_del:
+ list_del(&req_priv->list);
+ }
+ return ret;
}

/*
diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h
index d582c9dbe6b4..0477eb0f354c 100644
--- a/drivers/usb/usbssp/gadget.h
+++ b/drivers/usb/usbssp/gadget.h
@@ -1752,6 +1752,20 @@ int usbssp_queue_address_device(struct usbssp_udc *usbssp_data,
int usbssp_queue_stop_endpoint(struct usbssp_udc *usbssp_data,
struct usbssp_command *cmd,
unsigned int ep_index, int suspend);
+int usbssp_queue_ctrl_tx(struct usbssp_udc *usbssp_data, gfp_t mem_flags,
+ struct usbssp_request *req_priv,
+ unsigned int ep_index);
+
+int usbssp_queue_bulk_tx(struct usbssp_udc *usbssp_data, gfp_t mem_flags,
+ struct usbssp_request *req_priv,
+ unsigned int ep_index);
+int usbssp_queue_intr_tx(struct usbssp_udc *usbssp_data, gfp_t mem_flags,
+ struct usbssp_request *req_priv,
+ unsigned int ep_index);
+int usbssp_queue_isoc_tx_prepare(
+ struct usbssp_udc *usbssp_data, gfp_t mem_flags,
+ struct usbssp_request *req_priv,
+ unsigned int ep_index);
int usbssp_queue_reset_ep(struct usbssp_udc *usbssp_data,
struct usbssp_command *cmd,
unsigned int ep_index,
@@ -1784,6 +1798,10 @@ struct usbssp_slot_ctx *usbssp_get_slot_ctx(struct usbssp_udc *usbssp_data,
struct usbssp_ep_ctx *usbssp_get_ep_ctx(struct usbssp_udc *usbssp_data,
struct usbssp_container_ctx *ctx,
unsigned int ep_index);
+struct usbssp_ring *usbssp_triad_to_transfer_ring(
+ struct usbssp_udc *usbssp_data,
+ unsigned int ep_index,
+ unsigned int stream_id);
/* USBSSP gadget interface*/
void usbssp_suspend_gadget(struct usbssp_udc *usbssp_data);
void usbssp_resume_gadget(struct usbssp_udc *usbssp_data);
@@ -1807,6 +1825,17 @@ int usbssp_setup_analyze(struct usbssp_udc *usbssp_data);
int usbssp_status_stage(struct usbssp_udc *usbssp_data);

int usbssp_reset_device(struct usbssp_udc *usbssp_data);
+
+static inline struct usbssp_ring *usbssp_request_to_transfer_ring(
+ struct usbssp_udc *usbssp_data,
+ struct usbssp_request *req_priv)
+{
+ return usbssp_triad_to_transfer_ring(usbssp_data,
+ usbssp_get_endpoint_index(req_priv->dep->endpoint.desc),
+ req_priv->request.stream_id);
+}
+
+
static inline char *usbssp_slot_state_string(u32 state)
{
switch (state) {
--
2.17.1


2018-07-19 18:04:01

by Pawel Laszczak

[permalink] [raw]
Subject: [PATCH 13/31] usb: usbssp: addec procedure for handlin Port Status Change events.

This patch implement handle_port_status function and all related to
it other functions.

It is called in interrupt context from usbssp_irq function.

handle_port_status function is responsible for handling all events
related to USB port.

To correct handle some port events driver needs to add commands to
Command Ring. These commands are processed by HW. Each command
can take some time and driver need to wait for completion.
Therefor driver can’t handle all events in interrupt context and
Some of them must be deferred and will be handled in separate thread.
This additional thread will be introduced in separate patch.

Patch also introduces gadget-port.c file that contains functions related
to USB port, such as detecting port speed and setting link state.

Signed-off-by: Pawel Laszczak <[email protected]>
---
drivers/usb/usbssp/Makefile | 2 +-
drivers/usb/usbssp/gadget-if.c | 59 +++++
drivers/usb/usbssp/gadget-port.c | 102 ++++++++
drivers/usb/usbssp/gadget-ring.c | 414 ++++++++++++++++++++++++++++++-
drivers/usb/usbssp/gadget.h | 13 +
5 files changed, 582 insertions(+), 8 deletions(-)
create mode 100644 drivers/usb/usbssp/gadget-port.c

diff --git a/drivers/usb/usbssp/Makefile b/drivers/usb/usbssp/Makefile
index 4f5f12ab6420..f867124f286c 100644
--- a/drivers/usb/usbssp/Makefile
+++ b/drivers/usb/usbssp/Makefile
@@ -4,7 +4,7 @@ CFLAGS_gadget-trace.o := -I$(src)

obj-$(CONFIG_USB_USBSSP_GADGET) += usbssp.o
usbssp-y := usbssp-plat.o gadget-ring.o \
- gadget.o gadget-mem.o \
+ gadget.o gadget-mem.o gadget-port.o \
gadget-dbg.o

ifneq ($(CONFIG_TRACING),)
diff --git a/drivers/usb/usbssp/gadget-if.c b/drivers/usb/usbssp/gadget-if.c
index 70def978b085..6f42ad33979d 100644
--- a/drivers/usb/usbssp/gadget-if.c
+++ b/drivers/usb/usbssp/gadget-if.c
@@ -287,3 +287,62 @@ void usbssp_gadget_free_endpoint(struct usbssp_udc *usbssp_data)
list_del(&ep_priv->endpoint.ep_list);
}
}
+
+void usbssp_suspend_gadget(struct usbssp_udc *usbssp_data)
+{
+ if (usbssp_data->gadget_driver && usbssp_data->gadget_driver->suspend) {
+ spin_unlock(&usbssp_data->lock);
+ usbssp_data->gadget_driver->suspend(&usbssp_data->gadget);
+ spin_lock(&usbssp_data->lock);
+ }
+}
+
+void usbssp_resume_gadget(struct usbssp_udc *usbssp_data)
+{
+ if (usbssp_data->gadget_driver && usbssp_data->gadget_driver->resume) {
+ spin_unlock(&usbssp_data->lock);
+ usbssp_data->gadget_driver->resume(&usbssp_data->gadget);
+ spin_lock(&usbssp_data->lock);
+ }
+}
+
+static void usbssp_reset_gadget(struct usbssp_udc *usbssp_data)
+{
+ if (!usbssp_data->gadget_driver)
+ return;
+
+ if (usbssp_data->gadget.speed != USB_SPEED_UNKNOWN) {
+ spin_unlock(&usbssp_data->lock);
+ usb_gadget_udc_reset(&usbssp_data->gadget,
+ usbssp_data->gadget_driver);
+ spin_lock(&usbssp_data->lock);
+ }
+}
+void usbssp_gadget_reset_interrupt(struct usbssp_udc *usbssp_data)
+{
+ usbssp_reset_gadget(usbssp_data);
+ switch (usbssp_data->gadget.speed) {
+ case USB_SPEED_SUPER_PLUS:
+ usbssp_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
+ usbssp_data->gadget.ep0->maxpacket = 512;
+ break;
+ case USB_SPEED_SUPER:
+ usbssp_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
+ usbssp_data->gadget.ep0->maxpacket = 512;
+ break;
+ case USB_SPEED_HIGH:
+ usbssp_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64);
+ usbssp_data->gadget.ep0->maxpacket = 64;
+ break;
+ case USB_SPEED_FULL:
+ usbssp_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64);
+ usbssp_data->gadget.ep0->maxpacket = 64;
+ break;
+ case USB_SPEED_LOW:
+ usbssp_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(8);
+ usbssp_data->gadget.ep0->maxpacket = 8;
+ break;
+ default:
+ break;
+ }
+}
diff --git a/drivers/usb/usbssp/gadget-port.c b/drivers/usb/usbssp/gadget-port.c
new file mode 100644
index 000000000000..09ea5b574ae0
--- /dev/null
+++ b/drivers/usb/usbssp/gadget-port.c
@@ -0,0 +1,102 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USBSSP device controller driver
+ *
+ * Copyright (C) 2018 Cadence.
+ *
+ * Author: Pawel Laszczak
+ *
+ * A lot of code based on Linux XHCI driver.
+ * Origin: Copyright (C) 2008 Intel Corp
+ */
+
+#include <linux/slab.h>
+#include <asm/unaligned.h>
+
+#include "gadget-trace.h"
+#include "gadget.h"
+
+unsigned int usbssp_port_speed(unsigned int port_status)
+{
+ /*Detect gadget speed*/
+ if (DEV_SUPERSPEEDPLUS(port_status))
+ return USB_SPEED_SUPER_PLUS;
+ else if (DEV_SUPERSPEED(port_status))
+ return USB_SPEED_SUPER;
+ else if (DEV_HIGHSPEED(port_status))
+ return USB_SPEED_HIGH;
+ else if (DEV_FULLSPEED(port_status))
+ return USB_SPEED_FULL;
+ else if (DEV_LOWSPEED(port_status))
+ return USB_SPEED_LOW;
+
+ /*if device is detached then speed will be USB_SPEED_UNKNOWN*/
+ return USB_SPEED_UNKNOWN;
+}
+
+/*
+ * These bits are Read Only (RO) and should be saved and written to the
+ * registers: 0, 3, 10:13, 30
+ * connect status, over-current status and port speed.
+ * connect status and port speed are also sticky - meaning they're in
+ * the AUX well and they aren't changed by a hot and warm.
+ */
+#define USBSSP_PORT_RO (PORT_CONNECT | PORT_OC | DEV_SPEED_MASK)
+
+/*
+ * These bits are RW; writing a 0 clears the bit, writing a 1 sets the bit:
+ * bits 5:8, 9, 14:15, 25:27
+ * link state, port power, port indicator state, "wake on" enable state
+ */
+#define USBSSP_PORT_RWS (PORT_PLS_MASK | PORT_POWER | PORT_WKCONN_E | \
+ PORT_WKDISC_E | PORT_WKOC_E)
+
+/*
+ * Given a port state, this function returns a value that would result in the
+ * port being in the same state, if the value was written to the port status
+ * control register.
+ * Save Read Only (RO) bits and save read/write bits where
+ * writing a 0 clears the bit and writing a 1 sets the bit (RWS).
+ * For all other types (RW1S, RW1CS, RW, and RZ), writing a '0' has no effect.
+ */
+u32 usbssp_port_state_to_neutral(u32 state)
+{
+ /* Save read-only status and port state */
+ return (state & USBSSP_PORT_RO) | (state & USBSSP_PORT_RWS);
+}
+
+__le32 __iomem *usbssp_get_port_io_addr(struct usbssp_udc *usbssp_data)
+{
+ if (usbssp_data->port_major_revision == 0x03)
+ return usbssp_data->usb3_ports;
+ else
+ return usbssp_data->usb2_ports;
+}
+
+void usbssp_set_link_state(struct usbssp_udc *usbssp_data,
+ __le32 __iomem *port_regs,
+ u32 link_state)
+{
+ u32 temp;
+
+ temp = readl(port_regs);
+ temp = usbssp_port_state_to_neutral(temp);
+ temp &= ~PORT_PLS_MASK;
+ temp |= PORT_LINK_STROBE | link_state;
+ writel(temp, port_regs);
+}
+
+/* Test and clear port RWC bit */
+void usbssp_test_and_clear_bit(struct usbssp_udc *usbssp_data,
+ __le32 __iomem *port_regs,
+ u32 port_bit)
+{
+ u32 temp;
+
+ temp = readl(port_regs);
+ if (temp & port_bit) {
+ temp = usbssp_port_state_to_neutral(temp);
+ temp |= port_bit;
+ writel(temp, port_regs);
+ }
+}
diff --git a/drivers/usb/usbssp/gadget-ring.c b/drivers/usb/usbssp/gadget-ring.c
index f6a6269e071d..6679d8ec7152 100644
--- a/drivers/usb/usbssp/gadget-ring.c
+++ b/drivers/usb/usbssp/gadget-ring.c
@@ -102,6 +102,11 @@ static bool link_trb_toggles_cycle(union usbssp_trb *trb)
return le32_to_cpu(trb->link.control) & LINK_TOGGLE;
}

+static void inc_td_cnt(struct usbssp_request *priv_req)
+{
+ priv_req->num_tds_done++;
+}
+
/*
* See Cycle bit rules. SW is the consumer for the event ring only.
* Don't make a ring full of link TRBs. That would be dumb and this would loop.
@@ -220,18 +225,82 @@ static bool usbssp_mod_cmd_timer(struct usbssp_udc *usbssp_data,
return 0;
}

-irqreturn_t usbssp_irq(int irq, void *priv)
+static void usbssp_kill_ring_requests(struct usbssp_udc *usbssp_data,
+ struct usbssp_ring *ring)
{
- struct usbssp_udc *usbssp_data = (struct usbssp_udc *)priv;
- irqreturn_t ret = IRQ_NONE;
- unsigned long flags;
+ struct usbssp_td *cur_td;
+ struct usbssp_td *tmp;

- spin_lock_irqsave(&usbssp_data->lock, flags);
+ list_for_each_entry_safe(cur_td, tmp, &ring->td_list, td_list) {
+ list_del_init(&cur_td->td_list);
+ inc_td_cnt(cur_td->priv_request);
+ }
+}

- spin_unlock_irqrestore(&usbssp_data->lock, flags);
- return ret;
+void usbssp_kill_endpoint_request(struct usbssp_udc *usbssp_data,
+ int ep_index)
+{
+ struct usbssp_ep *ep;
+ struct usbssp_ring *ring;
+
+ ep = &usbssp_data->devs.eps[ep_index];
+ if ((ep->ep_state & EP_HAS_STREAMS) ||
+ (ep->ep_state & EP_GETTING_NO_STREAMS)) {
+ int stream_id;
+
+ for (stream_id = 0; stream_id < ep->stream_info->num_streams;
+ stream_id++) {
+
+ ring = ep->stream_info->stream_rings[stream_id];
+ if (!ring)
+ continue;
+
+ usbssp_dbg_trace(usbssp_data,
+ trace_usbssp_dbg_cancel_request,
+ "Killing Requests for slot ID %u,"
+ "ep index %u, stream %u",
+ usbssp_data->slot_id, ep_index, stream_id + 1);
+ usbssp_kill_ring_requests(usbssp_data, ring);
+ }
+ } else {
+ ring = ep->ring;
+ if (!ring)
+ return;
+
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_cancel_request,
+ "Killing Requests for slot ID %u, ep index %u",
+ usbssp_data->slot_id, ep_index);
+ usbssp_kill_ring_requests(usbssp_data, ring);
+ }
}

+/*
+ * USBSSP controller died, register read returns 0xffffffff
+ * Complete pending commands, mark them ABORTED.
+ * USB requests need to be given back as gadget core might be waiting with
+ * device lock held for the Requests to finish during device disconnect,
+ * blocking device remove.
+ *
+ */
+void usbssp_udc_died(struct usbssp_udc *usbssp_data)
+{
+ int i;
+
+ if (usbssp_data->usbssp_state & USBSSP_STATE_DYING)
+ return;
+
+ dev_err(usbssp_data->dev,
+ "USBSSP controller not responding, assume dead\n");
+ usbssp_data->usbssp_state |= USBSSP_STATE_DYING;
+
+ usbssp_cleanup_command_queue(usbssp_data);
+
+ /* return any pending requests, remove may be waiting for them */
+ for (i = 0; i < 31; i++)
+ usbssp_kill_endpoint_request(usbssp_data, i);
+}
+
+
void usbssp_handle_command_timeout(struct work_struct *work)
{
/*TODO: implements function*/
@@ -258,6 +327,166 @@ void usbssp_cleanup_command_queue(struct usbssp_udc *usbssp_data)
usbssp_complete_del_and_free_cmd(cur_cmd, COMP_COMMAND_ABORTED);
}

+static void handle_port_status(struct usbssp_udc *usbssp_data,
+ union usbssp_trb *event)
+{
+ u32 port_id;
+ u32 portsc, cmd_regs;
+ int max_ports;
+ u8 major_revision;
+ __le32 __iomem *port_regs;
+
+ /* Port status change events always have a successful completion code */
+ if (GET_COMP_CODE(le32_to_cpu(event->generic.field[2])) != COMP_SUCCESS)
+ dev_err(usbssp_data->dev,
+ "WARN: USBSSP returned failed port status event\n");
+
+
+ port_id = GET_PORT_ID(le32_to_cpu(event->generic.field[0]));
+ dev_dbg(usbssp_data->dev,
+ "Port Status Change Event for port %d\n", port_id);
+
+ usbssp_data->devs.port_num = port_id;
+ max_ports = HCS_MAX_PORTS(usbssp_data->hcs_params1);
+
+ if ((port_id <= 0) || (port_id > max_ports)) {
+ dev_err(usbssp_data->dev, "Invalid port id %d\n", port_id);
+ inc_deq(usbssp_data, usbssp_data->event_ring);
+ return;
+ }
+
+ if (!usbssp_data->port_major_revision) {
+ /*
+ * Figure out to which USB port device is attached:
+ * is it a USB 3.0 port or a USB 2.0/1.1 port?
+ */
+ major_revision = usbssp_data->port_array[port_id - 1];
+
+ if (major_revision == 0) {
+ dev_warn(usbssp_data->dev, "Event for port %u not in "
+ "Extended Capabilities, ignoring.\n",
+ port_id);
+ goto cleanup;
+ }
+
+ usbssp_data->port_major_revision = major_revision;
+ }
+
+ port_regs = usbssp_get_port_io_addr(usbssp_data);
+
+ portsc = readl(port_regs);
+ trace_usbssp_handle_port_status(usbssp_data->devs.port_num, portsc);
+ usbssp_data->gadget.speed = usbssp_port_speed(portsc);
+ dev_dbg(usbssp_data->dev, "PORTSC info: %s\n",
+ usbssp_decode_portsc(portsc));
+
+ if ((portsc & PORT_PLC) && (portsc & PORT_PLS_MASK) == XDEV_RESUME) {
+ dev_dbg(usbssp_data->dev, "port resume event for port %d\n",
+ port_id);
+ cmd_regs = readl(&usbssp_data->op_regs->command);
+ if (!(cmd_regs & CMD_RUN)) {
+ dev_warn(usbssp_data->dev, "DC is not running.\n");
+ goto cleanup;
+ }
+ if (DEV_SUPERSPEED_ANY(portsc)) {
+ dev_dbg(usbssp_data->dev, "remote wake SS port %d\n",
+ port_id);
+ usbssp_test_and_clear_bit(usbssp_data, port_regs,
+ PORT_PLC);
+ usbssp_set_link_state(usbssp_data, port_regs, XDEV_U0);
+ usbssp_resume_gadget(usbssp_data);
+ goto cleanup;
+ }
+ }
+
+ if ((portsc & PORT_PLC) && (portsc & PORT_PLS_MASK) == XDEV_U0 &&
+ DEV_SUPERSPEED_ANY(portsc)) {
+ dev_dbg(usbssp_data->dev, "resume SS port %d\n", port_id);
+ usbssp_test_and_clear_bit(usbssp_data, port_regs, PORT_PLC);
+ }
+
+ if ((portsc & PORT_PLC) && (portsc & PORT_PLS_MASK) == XDEV_U1 &&
+ DEV_SUPERSPEED_ANY(portsc)) {
+ dev_dbg(usbssp_data->dev, "suspend U1 SS port %d\n", port_id);
+ usbssp_test_and_clear_bit(usbssp_data, port_regs, PORT_PLC);
+ usbssp_suspend_gadget(usbssp_data);
+ }
+
+ if ((portsc & PORT_PLC) && ((portsc & PORT_PLS_MASK) == XDEV_U2 ||
+ (portsc & PORT_PLS_MASK) == XDEV_U3)) {
+ dev_dbg(usbssp_data->dev, "resume SS port %d finished\n",
+ port_id);
+ usbssp_test_and_clear_bit(usbssp_data, port_regs, PORT_PLC);
+ usbssp_suspend_gadget(usbssp_data);
+ }
+
+ /*Attach device */
+ if ((portsc & PORT_CSC) && (portsc & PORT_CONNECT)) {
+ dev_dbg(usbssp_data->dev, "Port status change: Device Attached\n");
+ usbssp_data->defered_event |= EVENT_DEV_CONNECTED;
+ queue_work(usbssp_data->bottom_irq_wq,
+ &usbssp_data->bottom_irq);
+ usbssp_test_and_clear_bit(usbssp_data, port_regs, PORT_CSC);
+ }
+
+ /*Detach device*/
+ if ((portsc & PORT_CSC) && !(portsc & PORT_CONNECT)) {
+ dev_dbg(usbssp_data->dev,
+ "Port status change: Device Deattached\n");
+ usbssp_data->defered_event |= EVENT_DEV_DISCONECTED;
+ queue_work(usbssp_data->bottom_irq_wq,
+ &usbssp_data->bottom_irq);
+ usbssp_test_and_clear_bit(usbssp_data, port_regs, PORT_CSC);
+ }
+
+ /*Port Reset Change - port is in reset state */
+ if ((portsc & PORT_RC) && (portsc & PORT_RESET)) {
+ dev_dbg(usbssp_data->dev,
+ "Port status change: Port reset signaling detected\n");
+ usbssp_test_and_clear_bit(usbssp_data, port_regs, PORT_RC);
+ }
+
+ /*Port Reset Change - port is not in reset state */
+ if ((portsc & PORT_RC) && !(portsc & PORT_RESET)) {
+ dev_dbg(usbssp_data->dev,
+ "Port status change: Port reset completion detected\n");
+ usbssp_gadget_reset_interrupt(usbssp_data);
+ usbssp_data->defered_event |= EVENT_USB_RESET;
+ queue_work(usbssp_data->bottom_irq_wq,
+ &usbssp_data->bottom_irq);
+ usbssp_test_and_clear_bit(usbssp_data, port_regs, PORT_RC);
+ }
+
+ /*Port Warm Reset Change*/
+ if (portsc & PORT_WRC) {
+ dev_dbg(usbssp_data->dev,
+ "Port status change: Port Warm Reset detected\n");
+ usbssp_test_and_clear_bit(usbssp_data, port_regs, PORT_WRC);
+ }
+
+ /*Port Over-Curretn Change*/
+ if (portsc & PORT_OCC) {
+ dev_dbg(usbssp_data->dev,
+ "Port status change: Port Over Current detected\n");
+ usbssp_test_and_clear_bit(usbssp_data, port_regs, PORT_OCC);
+ }
+
+ /*Port Configure Error Change*/
+ if (portsc & PORT_CEC) {
+ dev_dbg(usbssp_data->dev,
+ "Port status change: Port Configure Error detected\n");
+ usbssp_test_and_clear_bit(usbssp_data, port_regs, PORT_CEC);
+ }
+
+ if (usbssp_data->port_major_revision == 0x02)
+ usbssp_test_and_clear_bit(usbssp_data, port_regs,
+ PORT_PLC);
+
+cleanup:
+ /* Update event ring dequeue pointer before dropping the lock */
+ inc_deq(usbssp_data, usbssp_data->event_ring);
+}
+
/*
* This TD is defined by the TRBs starting at start_trb in start_seg and ending
* at end_trb, which may be in another segment. If the suspect DMA address is a
@@ -334,6 +563,177 @@ struct usbssp_segment *usbssp_trb_in_td(struct usbssp_udc *usbssp_data,
return NULL;
}

+/*
+ * This function handles all events on the event ring.
+ * Function can defers handling of some events to kernel thread.
+ * Returns >0 for "possibly more events to process" (caller should call again),
+ * otherwise 0 if done. In future, <0 returns should indicate error code.
+ */
+int usbssp_handle_event(struct usbssp_udc *usbssp_data)
+{
+ union usbssp_trb *event;
+ int update_ptrs = 1;
+ __le32 cycle_bit;
+
+ if (!usbssp_data->event_ring || !usbssp_data->event_ring->dequeue) {
+ dev_err(usbssp_data->dev, "ERROR event ring not ready\n");
+ return -ENOMEM;
+ }
+
+ event = usbssp_data->event_ring->dequeue;
+
+ cycle_bit = (le32_to_cpu(event->event_cmd.flags) & TRB_CYCLE);
+ /* Does the USBSSP or Driver own the TRB? */
+ if (cycle_bit != usbssp_data->event_ring->cycle_state)
+ return 0;
+
+ trace_usbssp_handle_event(usbssp_data->event_ring, &event->generic);
+
+ /*
+ * Barrier between reading the TRB_CYCLE (valid) flag above and any
+ * speculative reads of the event's flags/data below.
+ */
+ rmb();
+
+ switch ((le32_to_cpu(event->event_cmd.flags) & TRB_TYPE_BITMASK)) {
+ case TRB_TYPE(TRB_PORT_STATUS):
+ handle_port_status(usbssp_data, event);
+ update_ptrs = 0;
+ break;
+ default:
+ dev_warn(usbssp_data->dev, "ERROR unknown event type %ld\n",
+ TRB_FIELD_TO_TYPE(
+ le32_to_cpu(event->event_cmd.flags)));
+ }
+
+
+ /*
+ * Any of the above functions may drop and re-acquire the lock, so check
+ * to make sure a watchdog timer didn't mark the device as
+ * non-responsive.
+ */
+ if (usbssp_data->usbssp_state & USBSSP_STATE_DYING) {
+ dev_dbg(usbssp_data->dev, "USBSSP device dying, returning from "
+ "event handle.\n");
+ return 0;
+ }
+
+ if (update_ptrs) {
+ /* Update SW event ring dequeue pointer */
+ inc_deq(usbssp_data, usbssp_data->event_ring);
+ }
+
+ /*
+ * Are there more items on the event ring? Caller will call us again to
+ * check.
+ */
+ return 1;
+}
+
+irqreturn_t usbssp_irq(int irq, void *priv)
+{
+ struct usbssp_udc *usbssp_data = (struct usbssp_udc *)priv;
+ union usbssp_trb *event_ring_deq;
+ irqreturn_t ret = IRQ_NONE;
+ unsigned long flags;
+ dma_addr_t deq;
+ u64 temp_64;
+ u32 status;
+
+ spin_lock_irqsave(&usbssp_data->lock, flags);
+
+ /*
+ * Check if the USBSSP controller generated the interrupt,
+ * or the irq is shared
+ */
+ status = readl(&usbssp_data->op_regs->status);
+ if (status == ~(u32)0) {
+ usbssp_udc_died(usbssp_data);
+ ret = IRQ_HANDLED;
+ goto out;
+ }
+
+ if (!(status & STS_EINT))
+ goto out;
+
+ if (status & STS_FATAL) {
+ dev_warn(usbssp_data->dev, "WARNING: Device Controller Error\n");
+ usbssp_halt(usbssp_data);
+ ret = IRQ_HANDLED;
+ goto out;
+ }
+
+ /*
+ * Clear the op reg interrupt status first,
+ * so we can receive interrupts from other MSI-X interrupters.
+ * Write 1 to clear the interrupt status.
+ */
+ status |= STS_EINT;
+ writel(status, &usbssp_data->op_regs->status);
+
+ if (usbssp_data->msi_enabled) {
+ u32 irq_pending;
+
+ irq_pending = readl(&usbssp_data->ir_set->irq_pending);
+ irq_pending |= IMAN_IP;
+ writel(irq_pending, &usbssp_data->ir_set->irq_pending);
+ }
+
+ if (usbssp_data->usbssp_state & USBSSP_STATE_DYING ||
+ usbssp_data->usbssp_state & USBSSP_STATE_HALTED) {
+ dev_dbg(usbssp_data->dev,
+ "USBSSP controller dying, ignoring interrupt. "
+ "Shouldn't IRQs be disabled?\n");
+ /*
+ * Clear the event handler busy flag (RW1C);
+ * the event ring should be empty.
+ */
+ temp_64 = usbssp_read_64(usbssp_data,
+ &usbssp_data->ir_set->erst_dequeue);
+ usbssp_write_64(usbssp_data, temp_64 | ERST_EHB,
+ &usbssp_data->ir_set->erst_dequeue);
+ ret = IRQ_HANDLED;
+ goto out;
+ }
+
+ event_ring_deq = usbssp_data->event_ring->dequeue;
+
+ while ((ret = usbssp_handle_event(usbssp_data)) == 1) {
+ }
+
+ temp_64 = usbssp_read_64(usbssp_data,
+ &usbssp_data->ir_set->erst_dequeue);
+ /* If necessary, update the HW's version of the event ring deq ptr. */
+ if (event_ring_deq != usbssp_data->event_ring->dequeue) {
+
+ deq = usbssp_trb_virt_to_dma(usbssp_data->event_ring->deq_seg,
+ usbssp_data->event_ring->dequeue);
+
+ if (deq == 0)
+ dev_warn(usbssp_data->dev,
+ "WARN something wrong with SW event "
+ "ring dequeue ptr.\n");
+ /* Update USBSSP event ring dequeue pointer */
+ temp_64 &= ERST_PTR_MASK;
+ temp_64 |= ((u64) deq & (u64) ~ERST_PTR_MASK);
+ }
+
+ /* Clear the event handler busy flag (RW1C); event ring is empty. */
+ temp_64 |= ERST_EHB;
+ usbssp_write_64(usbssp_data, temp_64,
+ &usbssp_data->ir_set->erst_dequeue);
+ ret = IRQ_HANDLED;
+
+out:
+ spin_unlock_irqrestore(&usbssp_data->lock, flags);
+ return ret;
+}
+
+irqreturn_t usbssp_msi_irq(int irq, void *usbssp_data)
+{
+ return usbssp_irq(irq, usbssp_data);
+}
+
/**** Endpoint Ring Operations ****/

/*
diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h
index 0970c7ddcd38..33eccffc885d 100644
--- a/drivers/usb/usbssp/gadget.h
+++ b/drivers/usb/usbssp/gadget.h
@@ -1725,11 +1725,22 @@ struct usbssp_segment *usbssp_trb_in_td(struct usbssp_udc *usbssp_data,
void usbssp_handle_command_timeout(struct work_struct *work);

void usbssp_cleanup_command_queue(struct usbssp_udc *usbssp_data);
+/* USBSSP port code */
+void usbssp_set_link_state(struct usbssp_udc *usbssp_data,
+ __le32 __iomem *port_regs, u32 link_state);
+
+void usbssp_test_and_clear_bit(struct usbssp_udc *usbssp_data,
+ __le32 __iomem *port_regs, u32 port_bit);
+
/* USBSSP gadget interface*/
+void usbssp_suspend_gadget(struct usbssp_udc *usbssp_data);
+void usbssp_resume_gadget(struct usbssp_udc *usbssp_data);
int usbssp_gadget_init(struct usbssp_udc *usbssp_data);
int usbssp_gadget_exit(struct usbssp_udc *usbssp_data);
void usbssp_gadget_free_endpoint(struct usbssp_udc *usbssp_data);
int usbssp_gadget_init_endpoint(struct usbssp_udc *usbssp_data);
+unsigned int usbssp_port_speed(unsigned int port_status);
+void usbssp_gadget_reset_interrupt(struct usbssp_udc *usbssp_data);

static inline char *usbssp_slot_state_string(u32 state)
{
@@ -2203,4 +2214,6 @@ struct usbssp_udc;

#define to_usbssp_request(r) (container_of(r, struct usbssp_request, request))

+__le32 __iomem *usbssp_get_port_io_addr(struct usbssp_udc *usbssp_data);
+
#endif /* __LINUX_USBSSP_GADGET_H */
--
2.17.1


2018-07-19 18:04:15

by Pawel Laszczak

[permalink] [raw]
Subject: [PATCH 10/31] usb: usbssp: added usbssp_trb_in_td function.

Patch adds usbssp_trb_in_td function. This function checks if given
TRB object belongs to TD.

Patch also add procedure for testing this function and some testing
cases.

Signed-off-by: Pawel Laszczak <[email protected]>
---
drivers/usb/usbssp/gadget-mem.c | 168 +++++++++++++++++++++++++++++++
drivers/usb/usbssp/gadget-ring.c | 76 ++++++++++++++
drivers/usb/usbssp/gadget.h | 5 +
3 files changed, 249 insertions(+)

diff --git a/drivers/usb/usbssp/gadget-mem.c b/drivers/usb/usbssp/gadget-mem.c
index ecb6e1bbd212..fef83b6b6cf0 100644
--- a/drivers/usb/usbssp/gadget-mem.c
+++ b/drivers/usb/usbssp/gadget-mem.c
@@ -765,6 +765,170 @@ void usbssp_mem_cleanup(struct usbssp_udc *usbssp_data)
usbssp_data->page_shift = 0;
}

+static int usbssp_test_trb_in_td(struct usbssp_udc *usbssp_data,
+ struct usbssp_segment *input_seg,
+ union usbssp_trb *start_trb,
+ union usbssp_trb *end_trb,
+ dma_addr_t input_dma,
+ struct usbssp_segment *result_seg,
+ char *test_name, int test_number)
+{
+ unsigned long long start_dma;
+ unsigned long long end_dma;
+ struct usbssp_segment *seg;
+
+ start_dma = usbssp_trb_virt_to_dma(input_seg, start_trb);
+ end_dma = usbssp_trb_virt_to_dma(input_seg, end_trb);
+
+ seg = usbssp_trb_in_td(usbssp_data, input_seg, start_trb,
+ end_trb, input_dma, false);
+
+ if (seg != result_seg) {
+ dev_warn(usbssp_data->dev, "WARN: %s TRB math test %d failed!\n",
+ test_name, test_number);
+ dev_warn(usbssp_data->dev, "Tested TRB math w/ seg %p and "
+ "input DMA 0x%llx\n",
+ input_seg,
+ (unsigned long long) input_dma);
+ dev_warn(usbssp_data->dev, "starting TRB %p (0x%llx DMA), "
+ "ending TRB %p (0x%llx DMA)\n",
+ start_trb, start_dma,
+ end_trb, end_dma);
+ dev_warn(usbssp_data->dev, "Expected seg %p, got seg %p\n",
+ result_seg, seg);
+
+ usbssp_trb_in_td(usbssp_data, input_seg, start_trb,
+ end_trb, input_dma, true);
+ return -1;
+ }
+ return 0;
+}
+
+/* TRB math checks for usbssp_trb_in_td(), using the command and event rings. */
+static int usbssp_check_trb_in_td_math(struct usbssp_udc *usbssp_data)
+{
+ struct {
+ dma_addr_t input_dma;
+ struct usbssp_segment *result_seg;
+ } simple_test_vector[] = {
+ /* A zeroed DMA field should fail */
+ { 0, NULL },
+ /* One TRB before the ring start should fail */
+ { usbssp_data->event_ring->first_seg->dma - 16, NULL },
+ /* One byte before the ring start should fail */
+ { usbssp_data->event_ring->first_seg->dma - 1, NULL },
+ /* Starting TRB should succeed */
+ { usbssp_data->event_ring->first_seg->dma,
+ usbssp_data->event_ring->first_seg },
+ /* Ending TRB should succeed */
+ { usbssp_data->event_ring->first_seg->dma +
+ (TRBS_PER_SEGMENT - 1)*16,
+ usbssp_data->event_ring->first_seg },
+ /* One byte after the ring end should fail */
+ { usbssp_data->event_ring->first_seg->dma +
+ (TRBS_PER_SEGMENT - 1)*16 + 1, NULL },
+ /* One TRB after the ring end should fail */
+ { usbssp_data->event_ring->first_seg->dma +
+ (TRBS_PER_SEGMENT)*16, NULL },
+ /* An address of all ones should fail */
+ { (dma_addr_t) (~0), NULL },
+ };
+ struct {
+ struct usbssp_segment *input_seg;
+ union usbssp_trb *start_trb;
+ union usbssp_trb *end_trb;
+ dma_addr_t input_dma;
+ struct usbssp_segment *result_seg;
+ } complex_test_vector[] = {
+ /* Test feeding a valid DMA address from a different ring */
+ { .input_seg = usbssp_data->event_ring->first_seg,
+ .start_trb = usbssp_data->event_ring->first_seg->trbs,
+ .end_trb = &usbssp_data->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1],
+ .input_dma = usbssp_data->cmd_ring->first_seg->dma,
+ .result_seg = NULL,
+ },
+ /* Test feeding a valid end TRB from a different ring */
+ { .input_seg = usbssp_data->event_ring->first_seg,
+ .start_trb = usbssp_data->event_ring->first_seg->trbs,
+ .end_trb = &usbssp_data->cmd_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1],
+ .input_dma = usbssp_data->cmd_ring->first_seg->dma,
+ .result_seg = NULL,
+ },
+ /* Test feeding a valid start and end TRB from a different ring */
+ { .input_seg = usbssp_data->event_ring->first_seg,
+ .start_trb = usbssp_data->cmd_ring->first_seg->trbs,
+ .end_trb = &usbssp_data->cmd_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1],
+ .input_dma = usbssp_data->cmd_ring->first_seg->dma,
+ .result_seg = NULL,
+ },
+ /* TRB in this ring, but after this TD */
+ { .input_seg = usbssp_data->event_ring->first_seg,
+ .start_trb = &usbssp_data->event_ring->first_seg->trbs[0],
+ .end_trb = &usbssp_data->event_ring->first_seg->trbs[3],
+ .input_dma = usbssp_data->event_ring->first_seg->dma + 4*16,
+ .result_seg = NULL,
+ },
+ /* TRB in this ring, but before this TD */
+ { .input_seg = usbssp_data->event_ring->first_seg,
+ .start_trb = &usbssp_data->event_ring->first_seg->trbs[3],
+ .end_trb = &usbssp_data->event_ring->first_seg->trbs[6],
+ .input_dma = usbssp_data->event_ring->first_seg->dma + 2*16,
+ .result_seg = NULL,
+ },
+ /* TRB in this ring, but after this wrapped TD */
+ { .input_seg = usbssp_data->event_ring->first_seg,
+ .start_trb = &usbssp_data->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 3],
+ .end_trb = &usbssp_data->event_ring->first_seg->trbs[1],
+ .input_dma = usbssp_data->event_ring->first_seg->dma + 2*16,
+ .result_seg = NULL,
+ },
+ /* TRB in this ring, but before this wrapped TD */
+ { .input_seg = usbssp_data->event_ring->first_seg,
+ .start_trb = &usbssp_data->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 3],
+ .end_trb = &usbssp_data->event_ring->first_seg->trbs[1],
+ .input_dma = usbssp_data->event_ring->first_seg->dma + (TRBS_PER_SEGMENT - 4)*16,
+ .result_seg = NULL,
+ },
+ /* TRB not in this ring, and we have a wrapped TD */
+ { .input_seg = usbssp_data->event_ring->first_seg,
+ .start_trb = &usbssp_data->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 3],
+ .end_trb = &usbssp_data->event_ring->first_seg->trbs[1],
+ .input_dma = usbssp_data->cmd_ring->first_seg->dma + 2*16,
+ .result_seg = NULL,
+ },
+ };
+
+ unsigned int num_tests;
+ int i, ret;
+
+ num_tests = ARRAY_SIZE(simple_test_vector);
+ for (i = 0; i < num_tests; i++) {
+ ret = usbssp_test_trb_in_td(usbssp_data,
+ usbssp_data->event_ring->first_seg,
+ usbssp_data->event_ring->first_seg->trbs,
+ &usbssp_data->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1],
+ simple_test_vector[i].input_dma,
+ simple_test_vector[i].result_seg,
+ "Simple", i);
+ if (ret < 0)
+ return ret;
+ }
+
+ num_tests = ARRAY_SIZE(complex_test_vector);
+ for (i = 0; i < num_tests; i++) {
+ ret = usbssp_test_trb_in_td(usbssp_data,
+ complex_test_vector[i].input_seg,
+ complex_test_vector[i].start_trb,
+ complex_test_vector[i].end_trb,
+ complex_test_vector[i].input_dma,
+ complex_test_vector[i].result_seg,
+ "Complex", i);
+ if (ret < 0)
+ return ret;
+ }
+ dev_dbg(usbssp_data->dev, "TRB math tests passed.\n");
+ return 0;
+}

static void usbssp_set_event_deq(struct usbssp_udc *usbssp_data)
{
@@ -1187,6 +1351,10 @@ int usbssp_mem_init(struct usbssp_udc *usbssp_data, gfp_t flags)
if (!usbssp_data->event_ring)
goto fail;

+ /*invoke check procedure for usbssp_trb_in_td function*/
+ if (usbssp_check_trb_in_td_math(usbssp_data) < 0)
+ goto fail;
+
ret = usbssp_alloc_erst(usbssp_data, usbssp_data->event_ring,
&usbssp_data->erst, flags);
if (ret)
diff --git a/drivers/usb/usbssp/gadget-ring.c b/drivers/usb/usbssp/gadget-ring.c
index 7c4b6b7b7b0a..3075909c2e31 100644
--- a/drivers/usb/usbssp/gadget-ring.c
+++ b/drivers/usb/usbssp/gadget-ring.c
@@ -73,3 +73,79 @@ void usbssp_cleanup_command_queue(struct usbssp_udc *usbssp_data)
list_for_each_entry_safe(cur_cmd, tmp_cmd, &usbssp_data->cmd_list, cmd_list)
usbssp_complete_del_and_free_cmd(cur_cmd, COMP_COMMAND_ABORTED);
}
+
+/*
+ * This TD is defined by the TRBs starting at start_trb in start_seg and ending
+ * at end_trb, which may be in another segment. If the suspect DMA address is a
+ * TRB in this TD, this function returns that TRB's segment. Otherwise it
+ * returns 0.
+ */
+struct usbssp_segment *usbssp_trb_in_td(struct usbssp_udc *usbssp_data,
+ struct usbssp_segment *start_seg,
+ union usbssp_trb *start_trb,
+ union usbssp_trb *end_trb,
+ dma_addr_t suspect_dma,
+ bool debug)
+{
+ dma_addr_t start_dma;
+ dma_addr_t end_seg_dma;
+ dma_addr_t end_trb_dma;
+ struct usbssp_segment *cur_seg;
+
+ start_dma = usbssp_trb_virt_to_dma(start_seg, start_trb);
+ cur_seg = start_seg;
+
+ do {
+ if (start_dma == 0)
+ return NULL;
+ /* We may get an event for a Link TRB in the middle of a TD */
+ end_seg_dma = usbssp_trb_virt_to_dma(cur_seg,
+ &cur_seg->trbs[TRBS_PER_SEGMENT - 1]);
+ /* If the end TRB isn't in this segment, this is set to 0 */
+ end_trb_dma = usbssp_trb_virt_to_dma(cur_seg, end_trb);
+
+ if (debug)
+ dev_warn(usbssp_data->dev,
+ "Looking for event-dma %016llx trb-start"
+ "%016llx trb-end %016llx seg-start %016llx"
+ " seg-end %016llx\n",
+ (unsigned long long)suspect_dma,
+ (unsigned long long)start_dma,
+ (unsigned long long)end_trb_dma,
+ (unsigned long long)cur_seg->dma,
+ (unsigned long long)end_seg_dma);
+
+ if (end_trb_dma > 0) {
+ /*
+ * The end TRB is in this segment, so suspect should
+ * be here
+ */
+ if (start_dma <= end_trb_dma) {
+ if (suspect_dma >= start_dma &&
+ suspect_dma <= end_trb_dma)
+ return cur_seg;
+ } else {
+ /*
+ * Case for one segment with a
+ * TD wrapped around to the top
+ */
+ if ((suspect_dma >= start_dma &&
+ suspect_dma <= end_seg_dma) ||
+ (suspect_dma >= cur_seg->dma &&
+ suspect_dma <= end_trb_dma))
+ return cur_seg;
+ }
+ return NULL;
+ } else {
+ /* Might still be somewhere in this segment */
+ if (suspect_dma >= start_dma &&
+ suspect_dma <= end_seg_dma)
+ return cur_seg;
+ }
+
+ cur_seg = cur_seg->next;
+ start_dma = usbssp_trb_virt_to_dma(cur_seg, &cur_seg->trbs[0]);
+ } while (cur_seg != start_seg);
+
+ return NULL;
+}
diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h
index 9dba86a0274a..927c34579899 100644
--- a/drivers/usb/usbssp/gadget.h
+++ b/drivers/usb/usbssp/gadget.h
@@ -1711,6 +1711,11 @@ irqreturn_t usbssp_irq(int irq, void *priv);
/* USBSSP ring, segment, TRB, and TD functions */
dma_addr_t usbssp_trb_virt_to_dma(struct usbssp_segment *seg,
union usbssp_trb *trb);
+struct usbssp_segment *usbssp_trb_in_td(struct usbssp_udc *usbssp_data,
+ struct usbssp_segment *start_seg,
+ union usbssp_trb *start_trb,
+ union usbssp_trb *end_trb,
+ dma_addr_t suspect_dma, bool debug);
void usbssp_handle_command_timeout(struct work_struct *work);

void usbssp_cleanup_command_queue(struct usbssp_udc *usbssp_data);
--
2.17.1


2018-07-19 18:04:17

by Pawel Laszczak

[permalink] [raw]
Subject: [PATCH 08/31] usb: usbssp: Added ring and segment handling functions.

Patch add generic function used for handling rings in driver.

USBSSP controller use three types of ring:
- command ring - is used for handling commands. In this case driver
is producer and HW is consumer.
- event ring - ring for reporting events to driver. In this kind of
ring HW is producer and driver is consumer.
- transfer ring - ring used for queuing transfers. In this case
software is producer and hardware is consumer.

Driver has only single event and transfer ring, but it uses many of
transfer rings. Each endpoint has own transfer rings.
If endpoint use streams than have many transfer rings for such endpoint.

Each ring can contain segments and each segment contains
Transfer Request blocks (TRB). The set of elements create
Transfer Descriptor (TD).

For more information please refer to XHCI specification.

Signed-off-by: Pawel Laszczak <[email protected]>
---
drivers/usb/usbssp/gadget-mem.c | 433 +++++++++++++++++++++++++++++++-
1 file changed, 432 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/usbssp/gadget-mem.c b/drivers/usb/usbssp/gadget-mem.c
index 11bbf18dacba..06a59febbff2 100644
--- a/drivers/usb/usbssp/gadget-mem.c
+++ b/drivers/usb/usbssp/gadget-mem.c
@@ -18,6 +18,347 @@
#include "gadget.h"
#include "gadget-trace.h"

+/*
+ * Allocates a generic ring segment from the ring pool, sets the dma address,
+ * initializes the segment to zero, and sets the private next pointer to NULL.
+ *
+ * "All components of all Command and Transfer TRBs shall be initialized to '0'"
+ */
+static struct usbssp_segment *usbssp_segment_alloc(struct usbssp_udc *usbssp_data,
+ unsigned int cycle_state,
+ unsigned int max_packet,
+ gfp_t flags)
+{
+ struct usbssp_segment *seg;
+ dma_addr_t dma;
+ int i;
+
+ seg = kzalloc(sizeof(*seg), flags);
+ if (!seg)
+ return NULL;
+
+ seg->trbs = dma_pool_zalloc(usbssp_data->segment_pool, flags, &dma);
+ if (!seg->trbs) {
+ kfree(seg);
+ return NULL;
+ }
+
+ if (max_packet) {
+ seg->bounce_buf = kzalloc(max_packet, flags | GFP_DMA);
+ if (!seg->bounce_buf) {
+ dma_pool_free(usbssp_data->segment_pool,
+ seg->trbs, dma);
+ kfree(seg);
+ return NULL;
+ }
+ }
+
+ /* If the cycle state is 0, set the cycle bit to 1 for all the TRBs */
+ if (cycle_state == 0) {
+ for (i = 0; i < TRBS_PER_SEGMENT; i++)
+ seg->trbs[i].link.control |= cpu_to_le32(TRB_CYCLE);
+ }
+ seg->dma = dma;
+ seg->next = NULL;
+
+ return seg;
+}
+
+static void usbssp_segment_free(struct usbssp_udc *usbssp_data,
+ struct usbssp_segment *seg)
+{
+ if (seg->trbs) {
+ dma_pool_free(usbssp_data->segment_pool, seg->trbs, seg->dma);
+ seg->trbs = NULL;
+ }
+ kfree(seg->bounce_buf);
+ kfree(seg);
+}
+
+static void usbssp_free_segments_for_ring(struct usbssp_udc *usbssp_data,
+ struct usbssp_segment *first)
+{
+ struct usbssp_segment *seg;
+
+ seg = first->next;
+ while (seg != first) {
+ struct usbssp_segment *next = seg->next;
+
+ usbssp_segment_free(usbssp_data, seg);
+ seg = next;
+ }
+ usbssp_segment_free(usbssp_data, first);
+}
+
+/*
+ * Make the prev segment point to the next segment.
+ *
+ * Change the last TRB in the prev segment to be a Link TRB which points to the
+ * DMA address of the next segment. The caller needs to set any Link TRB
+ * related flags, such as End TRB, Toggle Cycle, and no snoop.
+ */
+static void usbssp_link_segments(struct usbssp_udc *usbssp_data,
+ struct usbssp_segment *prev,
+ struct usbssp_segment *next,
+ enum usbssp_ring_type type)
+{
+ u32 val;
+
+ if (!prev || !next)
+ return;
+ prev->next = next;
+ if (type != TYPE_EVENT) {
+ prev->trbs[TRBS_PER_SEGMENT-1].link.segment_ptr =
+ cpu_to_le64(next->dma);
+
+ /*
+ * Set the last TRB in the segment to have a TRB type ID
+ * of Link TRB
+ */
+ val = le32_to_cpu(prev->trbs[TRBS_PER_SEGMENT-1].link.control);
+ val &= ~TRB_TYPE_BITMASK;
+ val |= TRB_TYPE(TRB_LINK);
+ prev->trbs[TRBS_PER_SEGMENT-1].link.control = cpu_to_le32(val);
+ }
+}
+
+/*
+ * Link the ring to the new segments.
+ * Set Toggle Cycle for the new ring if needed.
+ */
+static void usbssp_link_rings(struct usbssp_udc *usbssp_data,
+ struct usbssp_ring *ring,
+ struct usbssp_segment *first,
+ struct usbssp_segment *last,
+ unsigned int num_segs)
+{
+ struct usbssp_segment *next;
+
+ if (!ring || !first || !last)
+ return;
+
+ next = ring->enq_seg->next;
+ usbssp_link_segments(usbssp_data, ring->enq_seg, first, ring->type);
+ usbssp_link_segments(usbssp_data, last, next, ring->type);
+ ring->num_segs += num_segs;
+ ring->num_trbs_free += (TRBS_PER_SEGMENT - 1) * num_segs;
+
+ if (ring->type != TYPE_EVENT && ring->enq_seg == ring->last_seg) {
+ ring->last_seg->trbs[TRBS_PER_SEGMENT-1].link.control
+ &= ~cpu_to_le32(LINK_TOGGLE);
+ last->trbs[TRBS_PER_SEGMENT-1].link.control
+ |= cpu_to_le32(LINK_TOGGLE);
+ ring->last_seg = last;
+ }
+}
+
+/*
+ * We need a radix tree for mapping physical addresses of TRBs to which stream
+ * ID they belong to. We need to do this because the device controller won't
+ * tell us which stream ring the TRB came from. We could store the stream ID
+ * in an event data TRB, but that doesn't help us for the cancellation case,
+ * since the endpoint may stop before it reaches that event data TRB.
+ *
+ * The radix tree maps the upper portion of the TRB DMA address to a ring
+ * segment that has the same upper portion of DMA addresses. For example,
+ * say I have segments of size 1KB, that are always 1KB aligned. A segment may
+ * start at 0x10c91000 and end at 0x10c913f0. If I use the upper 10 bits, the
+ * key to the stream ID is 0x43244. I can use the DMA address of the TRB to
+ * pass the radix tree a key to get the right stream ID:
+ *
+ * 0x10c90fff >> 10 = 0x43243
+ * 0x10c912c0 >> 10 = 0x43244
+ * 0x10c91400 >> 10 = 0x43245
+ *
+ * Obviously, only those TRBs with DMA addresses that are within the segment
+ * will make the radix tree return the stream ID for that ring.
+ *
+ * Caveats for the radix tree:
+ *
+ * The radix tree uses an unsigned long as a key pair. On 32-bit systems, an
+ * unsigned long will be 32-bits; on a 64-bit system an unsigned long will be
+ * 64-bits. Since we only request 32-bit DMA addresses, we can use that as the
+ * key on 32-bit or 64-bit systems (it would also be fine if we asked for 64-bit
+ * PCI DMA addresses on a 64-bit system). There might be a problem on 32-bit
+ * extended systems (where the DMA address can be bigger than 32-bits),
+ * if we allow the PCI dma mask to be bigger than 32-bits. So don't do that.
+ */
+static int usbssp_insert_segment_mapping(struct radix_tree_root *trb_address_map,
+ struct usbssp_ring *ring,
+ struct usbssp_segment *seg,
+ gfp_t mem_flags)
+{
+ unsigned long key;
+ int ret;
+
+ key = (unsigned long)(seg->dma >> TRB_SEGMENT_SHIFT);
+
+ /* Skip any segments that were already added. */
+ if (radix_tree_lookup(trb_address_map, key))
+ return 0;
+
+ ret = radix_tree_maybe_preload(mem_flags);
+ if (ret)
+ return ret;
+
+ ret = radix_tree_insert(trb_address_map, key, ring);
+ radix_tree_preload_end();
+ return ret;
+}
+
+static void usbssp_remove_segment_mapping(struct radix_tree_root *trb_address_map,
+ struct usbssp_segment *seg)
+{
+ unsigned long key;
+
+ key = (unsigned long)(seg->dma >> TRB_SEGMENT_SHIFT);
+ if (radix_tree_lookup(trb_address_map, key))
+ radix_tree_delete(trb_address_map, key);
+}
+
+static int usbssp_update_stream_segment_mapping(
+ struct radix_tree_root *trb_address_map,
+ struct usbssp_ring *ring,
+ struct usbssp_segment *first_seg,
+ struct usbssp_segment *last_seg,
+ gfp_t mem_flags)
+{
+ struct usbssp_segment *seg;
+ struct usbssp_segment *failed_seg;
+ int ret;
+
+ if (WARN_ON_ONCE(trb_address_map == NULL))
+ return 0;
+
+ seg = first_seg;
+ do {
+ ret = usbssp_insert_segment_mapping(trb_address_map,
+ ring, seg, mem_flags);
+ if (ret)
+ goto remove_streams;
+ if (seg == last_seg)
+ return 0;
+ seg = seg->next;
+ } while (seg != first_seg);
+
+ return 0;
+
+remove_streams:
+ failed_seg = seg;
+ seg = first_seg;
+ do {
+ usbssp_remove_segment_mapping(trb_address_map, seg);
+ if (seg == failed_seg)
+ return ret;
+ seg = seg->next;
+ } while (seg != first_seg);
+
+ return ret;
+}
+
+static void usbssp_remove_stream_mapping(struct usbssp_ring *ring)
+{
+ struct usbssp_segment *seg;
+
+ if (WARN_ON_ONCE(ring->trb_address_map == NULL))
+ return;
+
+ seg = ring->first_seg;
+ do {
+ usbssp_remove_segment_mapping(ring->trb_address_map, seg);
+ seg = seg->next;
+ } while (seg != ring->first_seg);
+}
+
+void usbssp_ring_free(struct usbssp_udc *usbssp_data, struct usbssp_ring *ring)
+{
+ if (!ring)
+ return;
+
+ trace_usbssp_ring_free(ring);
+
+ if (ring->first_seg) {
+ if (ring->type == TYPE_STREAM)
+ usbssp_remove_stream_mapping(ring);
+ usbssp_free_segments_for_ring(usbssp_data, ring->first_seg);
+ }
+
+ kfree(ring);
+}
+
+static void usbssp_initialize_ring_info(struct usbssp_ring *ring,
+ unsigned int cycle_state)
+{
+ /* The ring is empty, so the enqueue pointer == dequeue pointer */
+ ring->enqueue = ring->first_seg->trbs;
+ ring->enq_seg = ring->first_seg;
+ ring->dequeue = ring->enqueue;
+ ring->deq_seg = ring->first_seg;
+
+ /*
+ * The ring is initialized to 0. The producer must write 1 to the cycle
+ * bit to handover ownership of the TRB, so PCS = 1. The consumer must
+ * compare CCS to the cycle bit to check ownership, so CCS = 1.
+ *
+ * New rings are initialized with cycle state equal to 1; if we are
+ * handling ring expansion, set the cycle state equal to the old ring.
+ */
+ ring->cycle_state = cycle_state;
+
+ /*
+ * Each segment has a link TRB, and leave an extra TRB for SW
+ * accounting purpose
+ */
+ ring->num_trbs_free = ring->num_segs * (TRBS_PER_SEGMENT - 1) - 1;
+}
+
+/* Allocate segments and link them for a ring */
+static int usbssp_alloc_segments_for_ring(struct usbssp_udc *usbssp_data,
+ struct usbssp_segment **first,
+ struct usbssp_segment **last,
+ unsigned int num_segs,
+ unsigned int cycle_state,
+ enum usbssp_ring_type type,
+ unsigned int max_packet,
+ gfp_t flags)
+{
+ struct usbssp_segment *prev;
+
+ /*allocation first segment */
+ prev = usbssp_segment_alloc(usbssp_data, cycle_state,
+ max_packet, flags);
+ if (!prev)
+ return -ENOMEM;
+ num_segs--;
+
+ *first = prev;
+ /*allocation all other segments*/
+ while (num_segs > 0) {
+ struct usbssp_segment *next;
+
+ next = usbssp_segment_alloc(usbssp_data, cycle_state,
+ max_packet, flags);
+ if (!next) {
+ prev = *first;
+ /*Free all reserved segment*/
+ while (prev) {
+ next = prev->next;
+ usbssp_segment_free(usbssp_data, prev);
+ prev = next;
+ }
+ return -ENOMEM;
+ }
+ usbssp_link_segments(usbssp_data, prev, next, type);
+
+ prev = next;
+ num_segs--;
+ }
+ usbssp_link_segments(usbssp_data, prev, *first, type);
+ *last = prev;
+
+ return 0;
+}
+
/**
* Create a new ring with zero or more segments.
*
@@ -32,10 +373,100 @@ static struct usbssp_ring *usbssp_ring_alloc(struct usbssp_udc *usbssp_data,
unsigned int max_packet,
gfp_t flags)
{
- /*TODO: implements function*/
+ struct usbssp_ring *ring;
+ int ret;
+
+ ring = kzalloc(sizeof *(ring), flags);
+ if (!ring)
+ return NULL;
+
+ ring->num_segs = num_segs;
+ ring->bounce_buf_len = max_packet;
+ INIT_LIST_HEAD(&ring->td_list);
+ ring->type = type;
+ if (num_segs == 0)
+ return ring;
+
+ ret = usbssp_alloc_segments_for_ring(usbssp_data, &ring->first_seg,
+ &ring->last_seg, num_segs, cycle_state, type,
+ max_packet, flags);
+ if (ret)
+ goto fail;
+
+ /* Only event ring does not use link TRB */
+ if (type != TYPE_EVENT) {
+ ring->last_seg->trbs[TRBS_PER_SEGMENT - 1].link.control |=
+ cpu_to_le32(LINK_TOGGLE);
+ }
+ usbssp_initialize_ring_info(ring, cycle_state);
+ trace_usbssp_ring_alloc(ring);
+ return ring;
+fail:
+ kfree(ring);
return NULL;
}

+void usbssp_free_endpoint_ring(struct usbssp_udc *usbssp_data,
+ struct usbssp_device *dev_priv,
+ unsigned int ep_index)
+{
+ usbssp_ring_free(usbssp_data, dev_priv->eps[ep_index].ring);
+ dev_priv->eps[ep_index].ring = NULL;
+}
+
+/*
+ * Expand an existing ring.
+ * Allocate a new ring which has same segment numbers and link the two rings.
+ */
+int usbssp_ring_expansion(struct usbssp_udc *usbssp_data,
+ struct usbssp_ring *ring,
+ unsigned int num_trbs, gfp_t flags)
+{
+ struct usbssp_segment *first;
+ struct usbssp_segment *last;
+ unsigned int num_segs;
+ unsigned int num_segs_needed;
+ int ret;
+
+ num_segs_needed = (num_trbs + (TRBS_PER_SEGMENT - 1) - 1) /
+ (TRBS_PER_SEGMENT - 1);
+
+ /* Allocate number of segments we needed, or double the ring size */
+ num_segs = ring->num_segs > num_segs_needed ?
+ ring->num_segs : num_segs_needed;
+
+ ret = usbssp_alloc_segments_for_ring(usbssp_data, &first, &last,
+ num_segs, ring->cycle_state, ring->type,
+ ring->bounce_buf_len, flags);
+ if (ret)
+ return -ENOMEM;
+
+ if (ring->type == TYPE_STREAM)
+ ret = usbssp_update_stream_segment_mapping(
+ ring->trb_address_map, ring, first,
+ last, flags);
+ if (ret) {
+ struct usbssp_segment *next;
+
+ do {
+ next = first->next;
+ usbssp_segment_free(usbssp_data, first);
+ if (first == last)
+ break;
+ first = next;
+ } while (true);
+ return ret;
+ }
+
+ usbssp_link_rings(usbssp_data, ring, first, last, num_segs);
+ trace_usbssp_ring_expansion(ring);
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_ring_expansion,
+ "ring expansion succeed, now has %d segments",
+ ring->num_segs);
+
+ return 0;
+}
+
struct usbssp_container_ctx *usbssp_alloc_container_ctx(
struct usbssp_udc *usbssp_data,
int type, gfp_t flags)
--
2.17.1


2018-07-19 18:04:34

by Pawel Laszczak

[permalink] [raw]
Subject: [PATCH 15/31] usb: usbssp: added device controller error, transfer and SETUP completion event.

Patch adds some other completion events used in driver.

The Device Controller Error event is used for detecting
Event Ring Full error.

Transfer event will be implemented in later patches.

SETUP event handles SETUP packet received from host. In interrupt context
driver only preserve the SETUP packet, then postpone handling
it for later. We can't handle all Setup packet in interrupt
context because some of them needs to call command that take some time.

Signed-off-by: Pawel Laszczak <[email protected]>
---
drivers/usb/usbssp/gadget-ring.c | 57 ++++++++++++++++++++++++++++++--
1 file changed, 55 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/usbssp/gadget-ring.c b/drivers/usb/usbssp/gadget-ring.c
index c704b939b48a..eab7676fa744 100644
--- a/drivers/usb/usbssp/gadget-ring.c
+++ b/drivers/usb/usbssp/gadget-ring.c
@@ -1070,6 +1070,18 @@ struct usbssp_segment *usbssp_trb_in_td(struct usbssp_udc *usbssp_data,
return NULL;
}

+/*
+ * If this function returns an error condition, it means it got a Transfer
+ * event with a corrupted Slot ID, Endpoint ID, or TRB DMA address.
+ * At this point, the USBSSP controller is probably hosed and should be reset.
+ */
+static int handle_tx_event(struct usbssp_udc *usbssp_data,
+ struct usbssp_transfer_event *event)
+{
+ /*TODO: implement function handling transfer event*/
+ return 0;
+}
+
/*
* This function handles all events on the event ring.
* Function can defers handling of some events to kernel thread.
@@ -1080,7 +1092,9 @@ int usbssp_handle_event(struct usbssp_udc *usbssp_data)
{
union usbssp_trb *event;
int update_ptrs = 1;
+ int ret = 0;
__le32 cycle_bit;
+ unsigned int trb_comp_code;

if (!usbssp_data->event_ring || !usbssp_data->event_ring->dequeue) {
dev_err(usbssp_data->dev, "ERROR event ring not ready\n");
@@ -1110,6 +1124,45 @@ int usbssp_handle_event(struct usbssp_udc *usbssp_data)
handle_port_status(usbssp_data, event);
update_ptrs = 0;
break;
+ case TRB_TYPE(TRB_TRANSFER):
+ ret = handle_tx_event(usbssp_data, &event->trans_event);
+
+ if (ret >= 0)
+ update_ptrs = 0;
+ break;
+ case TRB_TYPE(TRB_SETUP): {
+ usbssp_data->ep0state = USBSSP_EP0_SETUP_PHASE;
+ usbssp_data->setupId = TRB_SETUPID_TO_TYPE(event->trans_event.flags);
+ usbssp_data->setup_speed = TRB_SETUP_SPEEDID(event->trans_event.flags);
+
+ usbssp_data->setup = *((struct usb_ctrlrequest *)&event->trans_event.buffer);
+
+ dev_dbg(usbssp_data->dev,
+ "Setup packet (id: %d) defered to thread\n",
+ usbssp_data->setupId);
+
+ usbssp_data->defered_event |= EVENT_SETUP_PACKET;
+ queue_work(usbssp_data->bottom_irq_wq,
+ &usbssp_data->bottom_irq);
+ break;
+ }
+
+ case TRB_TYPE(TRB_HC_EVENT):
+ trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->generic.field[2]));
+ dev_warn(usbssp_data->dev,
+ "Device Controller Error detected with error code 0x%02x\n",
+ trb_comp_code);
+ /* Look for common error cases */
+ switch (trb_comp_code) {
+ case COMP_EVENT_RING_FULL_ERROR:
+ dev_dbg(usbssp_data->dev,
+ "Error: Event Ring Full\n");
+ break;
+ default:
+ dev_dbg(usbssp_data->dev,
+ "Not supported completion code\n");
+ }
+ break;
default:
dev_warn(usbssp_data->dev, "ERROR unknown event type %ld\n",
TRB_FIELD_TO_TYPE(
@@ -1123,8 +1176,8 @@ int usbssp_handle_event(struct usbssp_udc *usbssp_data)
* non-responsive.
*/
if (usbssp_data->usbssp_state & USBSSP_STATE_DYING) {
- dev_dbg(usbssp_data->dev, "USBSSP device dying, returning from "
- "event handle.\n");
+ dev_dbg(usbssp_data->dev,
+ "USBSSP device dying, returning from event handle.\n");
return 0;
}

--
2.17.1


2018-07-19 18:04:44

by Pawel Laszczak

[permalink] [raw]
Subject: [PATCH 16/31] usb: usbssp: added connect/disconnect procedures.

Patch adds functionality responsible for handling CONNECT/DISCONNECT
event. This event will be reported after attached/detached USB
device to/from USB port.

To complete this procedure usbssp_halt_endpoint function must to be
implemented. This will be added in next patch.

Signed-off-by: Pawel Laszczak <[email protected]>
---
drivers/usb/usbssp/gadget-if.c | 16 +++
drivers/usb/usbssp/gadget-mem.c | 73 +++++++++++++
drivers/usb/usbssp/gadget-port.c | 92 ++++++++++++++++
drivers/usb/usbssp/gadget-ring.c | 12 +++
drivers/usb/usbssp/gadget.c | 178 ++++++++++++++++++++++++++++++-
drivers/usb/usbssp/gadget.h | 17 +++
6 files changed, 386 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/usbssp/gadget-if.c b/drivers/usb/usbssp/gadget-if.c
index 6f42ad33979d..ef466aa47f56 100644
--- a/drivers/usb/usbssp/gadget-if.c
+++ b/drivers/usb/usbssp/gadget-if.c
@@ -288,6 +288,16 @@ void usbssp_gadget_free_endpoint(struct usbssp_udc *usbssp_data)
}
}

+static void usbssp_disconnect_gadget(struct usbssp_udc *usbssp_data)
+{
+ if (usbssp_data->gadget_driver &&
+ usbssp_data->gadget_driver->disconnect) {
+ spin_unlock(&usbssp_data->irq_thread_lock);
+ usbssp_data->gadget_driver->disconnect(&usbssp_data->gadget);
+ spin_lock(&usbssp_data->irq_thread_lock);
+ }
+}
+
void usbssp_suspend_gadget(struct usbssp_udc *usbssp_data)
{
if (usbssp_data->gadget_driver && usbssp_data->gadget_driver->suspend) {
@@ -318,6 +328,12 @@ static void usbssp_reset_gadget(struct usbssp_udc *usbssp_data)
spin_lock(&usbssp_data->lock);
}
}
+
+void usbssp_gadget_disconnect_interrupt(struct usbssp_udc *usbssp_data)
+{
+ usbssp_disconnect_gadget(usbssp_data);
+}
+
void usbssp_gadget_reset_interrupt(struct usbssp_udc *usbssp_data)
{
usbssp_reset_gadget(usbssp_data);
diff --git a/drivers/usb/usbssp/gadget-mem.c b/drivers/usb/usbssp/gadget-mem.c
index 5708a0090ead..2c220582a5f8 100644
--- a/drivers/usb/usbssp/gadget-mem.c
+++ b/drivers/usb/usbssp/gadget-mem.c
@@ -643,6 +643,70 @@ void usbssp_free_priv_device(struct usbssp_udc *usbssp_data)
usbssp_data->slot_id = 0;
}

+int usbssp_alloc_priv_device(struct usbssp_udc *usbssp_data, gfp_t flags)
+{
+ struct usbssp_device *priv_dev;
+
+ /* Slot ID 0 is reserved */
+ if (usbssp_data->slot_id == 0) {
+ dev_warn(usbssp_data->dev, "Bad Slot ID %d\n",
+ usbssp_data->slot_id);
+ return 0;
+ }
+
+ priv_dev = &usbssp_data->devs;
+
+ /* Allocate the (output) device context that will be
+ * used in the USBSSP.
+ */
+ priv_dev->out_ctx = usbssp_alloc_container_ctx(usbssp_data,
+ USBSSP_CTX_TYPE_DEVICE, flags);
+
+ if (!priv_dev->out_ctx)
+ goto fail;
+
+ dev_dbg(usbssp_data->dev, "Slot %d output ctx = 0x%llx (dma)\n",
+ usbssp_data->slot_id,
+ (unsigned long long)priv_dev->out_ctx->dma);
+
+ /* Allocate the (input) device context for address device command */
+ priv_dev->in_ctx = usbssp_alloc_container_ctx(usbssp_data,
+ USBSSP_CTX_TYPE_INPUT, flags);
+
+ if (!priv_dev->in_ctx)
+ goto fail;
+
+ dev_dbg(usbssp_data->dev, "Slot %d input ctx = 0x%llx (dma)\n",
+ usbssp_data->slot_id,
+ (unsigned long long)priv_dev->in_ctx->dma);
+
+ /* Allocate endpoint 0 ring */
+ priv_dev->eps[0].ring = usbssp_ring_alloc(usbssp_data, 2, 1,
+ TYPE_CTRL, 0, flags);
+ if (!priv_dev->eps[0].ring)
+ goto fail;
+
+ priv_dev->gadget = &usbssp_data->gadget;
+
+ /* Point to output device context in dcbaa. */
+ usbssp_data->dcbaa->dev_context_ptrs[usbssp_data->slot_id] =
+ cpu_to_le64(priv_dev->out_ctx->dma);
+ dev_dbg(usbssp_data->dev, "Set slot id %d dcbaa entry %p to 0x%llx\n",
+ usbssp_data->slot_id,
+ &usbssp_data->dcbaa->dev_context_ptrs[usbssp_data->slot_id],
+ le64_to_cpu(usbssp_data->dcbaa->dev_context_ptrs[usbssp_data->slot_id]));
+
+ trace_usbssp_alloc_priv_device(priv_dev);
+ return 1;
+fail:
+ if (priv_dev->in_ctx)
+ usbssp_free_container_ctx(usbssp_data, priv_dev->in_ctx);
+ if (priv_dev->out_ctx)
+ usbssp_free_container_ctx(usbssp_data, priv_dev->out_ctx);
+
+ return 0;
+}
+
struct usbssp_command *usbssp_alloc_command(struct usbssp_udc *usbssp_data,
bool allocate_completion,
gfp_t mem_flags)
@@ -754,6 +818,7 @@ void usbssp_mem_cleanup(struct usbssp_udc *usbssp_data)

cancel_delayed_work_sync(&usbssp_data->cmd_timer);
cancel_work_sync(&usbssp_data->bottom_irq);
+ destroy_workqueue(usbssp_data->bottom_irq_wq);

/* Free the Event Ring Segment Table and the actual Event Ring */
usbssp_free_erst(usbssp_data, &usbssp_data->erst);
@@ -1265,6 +1330,14 @@ int usbssp_mem_init(struct usbssp_udc *usbssp_data, gfp_t flags)
usbssp_handle_command_timeout);
init_completion(&usbssp_data->cmd_ring_stop_completion);

+ usbssp_data->bottom_irq_wq =
+ create_singlethread_workqueue(dev_name(usbssp_data->dev));
+
+ if (!usbssp_data->bottom_irq_wq)
+ goto fail;
+
+ INIT_WORK(&usbssp_data->bottom_irq, usbssp_bottom_irq);
+
page_size = readl(&usbssp_data->op_regs->page_size);
usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
"Supported page size register = 0x%x", page_size);
diff --git a/drivers/usb/usbssp/gadget-port.c b/drivers/usb/usbssp/gadget-port.c
index 09ea5b574ae0..2c4d28070cab 100644
--- a/drivers/usb/usbssp/gadget-port.c
+++ b/drivers/usb/usbssp/gadget-port.c
@@ -65,6 +65,98 @@ u32 usbssp_port_state_to_neutral(u32 state)
return (state & USBSSP_PORT_RO) | (state & USBSSP_PORT_RWS);
}

+/*
+ * Stop device
+ * It issues stop endpoint command for EP 0 to 30. And wait the last command
+ * to complete.
+ */
+int usbssp_stop_device(struct usbssp_udc *usbssp_data, int suspend)
+{
+ struct usbssp_device *priv_dev;
+ struct usbssp_ep_ctx *ep_ctx;
+ int ret = 0;
+ int i;
+
+ ret = 0;
+ priv_dev = &usbssp_data->devs;
+
+ trace_usbssp_stop_device(priv_dev);
+
+ if (usbssp_data->gadget.state < USB_STATE_ADDRESS) {
+ dev_dbg(usbssp_data->dev,
+ "Device is not yet in USB_STATE_ADDRESS state\n");
+ goto stop_ep0;
+ }
+
+ for (i = LAST_EP_INDEX; i > 0; i--) {
+ if (priv_dev->eps[i].ring && priv_dev->eps[i].ring->dequeue) {
+ struct usbssp_command *command;
+
+ if (priv_dev->eps[i].ep_state & EP_HALTED) {
+ dev_dbg(usbssp_data->dev,
+ "ep_index %d is in halted state "
+ "- ep state: %x\n",
+ i, priv_dev->eps[i].ep_state);
+ usbssp_halt_endpoint(usbssp_data,
+ &priv_dev->eps[i], 0);
+ }
+
+ ep_ctx = usbssp_get_ep_ctx(usbssp_data,
+ priv_dev->out_ctx, i);
+
+ if (GET_EP_CTX_STATE(ep_ctx) != EP_STATE_RUNNING) {
+ dev_dbg(usbssp_data->dev,
+ "ep_index %d is already stopped.\n", i);
+ continue;
+ }
+
+ if (priv_dev->eps[i].ep_state & EP_STOP_CMD_PENDING) {
+ dev_dbg(usbssp_data->dev,
+ "Stop endpoint command is pending "
+ "for ep_index %d.\n", i);
+ continue;
+ }
+
+ /*
+ * Device was disconnected so endpoint should be disabled
+ * and transfer ring stopped.
+ */
+ priv_dev->eps[i].ep_state |= EP_STOP_CMD_PENDING |
+ USBSSP_EP_DISABLE_PENDING;
+
+ command = usbssp_alloc_command(usbssp_data, false,
+ GFP_ATOMIC);
+ if (!command)
+ return -ENOMEM;
+
+ ret = usbssp_queue_stop_endpoint(usbssp_data,
+ command, i, suspend);
+ if (ret) {
+ usbssp_free_command(usbssp_data, command);
+ return ret;
+ }
+ }
+ }
+
+stop_ep0:
+ if (priv_dev->eps[0].ep_state & EP_HALTED) {
+ dev_dbg(usbssp_data->dev,
+ "ep_index 0 is in halted state - ep state: %x\n",
+ priv_dev->eps[i].ep_state);
+ ret = usbssp_halt_endpoint(usbssp_data, &priv_dev->eps[0], 0);
+ } else {
+ /*
+ * Device was disconnected so endpoint should be disabled
+ * and transfer ring stopped.
+ */
+ priv_dev->eps[0].ep_state &= ~USBSSP_EP_ENABLED;
+ ret = usbssp_cmd_stop_ep(usbssp_data, &usbssp_data->gadget,
+ &priv_dev->eps[0]);
+ }
+
+ return ret;
+}
+
__le32 __iomem *usbssp_get_port_io_addr(struct usbssp_udc *usbssp_data)
{
if (usbssp_data->port_major_revision == 0x03)
diff --git a/drivers/usb/usbssp/gadget-ring.c b/drivers/usb/usbssp/gadget-ring.c
index eab7676fa744..f3ee1c4d82dc 100644
--- a/drivers/usb/usbssp/gadget-ring.c
+++ b/drivers/usb/usbssp/gadget-ring.c
@@ -218,6 +218,18 @@ static inline int room_on_ring(struct usbssp_udc *usbssp_data,
return 1;
}

+/* Ring the device controller doorbell after placing a command on the ring */
+void usbssp_ring_cmd_db(struct usbssp_udc *usbssp_data)
+{
+ if (!(usbssp_data->cmd_ring_state & CMD_RING_STATE_RUNNING))
+ return;
+
+ dev_dbg(usbssp_data->dev, "// Ding dong command ring!\n");
+ writel(DB_VALUE_CMD, &usbssp_data->dba->doorbell[0]);
+ /* Flush PCI posted writes */
+ readl(&usbssp_data->dba->doorbell[0]);
+}
+
static bool usbssp_mod_cmd_timer(struct usbssp_udc *usbssp_data,
unsigned long delay)
{
diff --git a/drivers/usb/usbssp/gadget.c b/drivers/usb/usbssp/gadget.c
index 6b3dc973c0d9..32d095b32e9f 100644
--- a/drivers/usb/usbssp/gadget.c
+++ b/drivers/usb/usbssp/gadget.c
@@ -23,6 +23,64 @@
#include "gadget-trace.h"
#include "gadget.h"

+void usbssp_bottom_irq(struct work_struct *work)
+{
+ struct usbssp_udc *usbssp_data = container_of(work, struct usbssp_udc,
+ bottom_irq);
+
+ if (usbssp_data->usbssp_state & USBSSP_STATE_DYING) {
+ dev_err(usbssp_data->dev, "Device controller dying\n");
+ return;
+ }
+
+ mutex_lock(&usbssp_data->mutex);
+ spin_lock_irqsave(&usbssp_data->irq_thread_lock,
+ usbssp_data->irq_thread_flag);
+
+ if (usbssp_data->defered_event & EVENT_DEV_DISCONECTED) {
+ dev_dbg(usbssp_data->dev, "Disconnecting device sequence\n");
+ usbssp_data->defered_event &= ~EVENT_DEV_DISCONECTED;
+ usbssp_data->usbssp_state |= USBSSP_STATE_DISCONNECT_PENDING;
+ usbssp_stop_device(usbssp_data, 0);
+
+ usbssp_gadget_disconnect_interrupt(usbssp_data);
+ usbssp_data->gadget.speed = USB_SPEED_UNKNOWN;
+ usb_gadget_set_state(&usbssp_data->gadget, USB_STATE_NOTATTACHED);
+
+ dev_dbg(usbssp_data->dev, "Wait for disconnect\n");
+
+ spin_unlock_irqrestore(&usbssp_data->irq_thread_lock,
+ usbssp_data->irq_thread_flag);
+ /*fixme: should be replaced by wait_for_completion*/
+ msleep(200);
+ spin_lock_irqsave(&usbssp_data->irq_thread_lock,
+ usbssp_data->irq_thread_flag);
+ }
+
+ if (usbssp_data->defered_event & EVENT_DEV_CONNECTED) {
+ dev_dbg(usbssp_data->dev, "Connecting device sequence\n");
+ if (usbssp_data->usbssp_state & USBSSP_STATE_DISCONNECT_PENDING) {
+ usbssp_free_dev(usbssp_data);
+ usbssp_data->usbssp_state &= ~USBSSP_STATE_DISCONNECT_PENDING;
+ }
+
+ usbssp_data->defered_event &= ~EVENT_DEV_CONNECTED;
+ usbssp_alloc_dev(usbssp_data);
+ }
+
+ if (usbssp_data->defered_event & EVENT_USB_RESET) {
+ /*TODO: implement handling of USB_RESET*/
+ }
+
+ /*handle setup packet*/
+ if (usbssp_data->defered_event & EVENT_SETUP_PACKET) {
+ /*TODO: implement handling of SETUP packet*/
+ }
+
+ spin_unlock_irqrestore(&usbssp_data->irq_thread_lock,
+ usbssp_data->irq_thread_flag);
+ mutex_unlock(&usbssp_data->mutex);
+}

/*
* usbssp_handshake - spin reading dc until handshake completes or fails
@@ -273,6 +331,123 @@ unsigned int usbssp_last_valid_endpoint(u32 added_ctxs)
return fls(added_ctxs) - 1;
}

+int usbssp_halt_endpoint(struct usbssp_udc *usbssp_data, struct usbssp_ep *dep,
+ int value)
+{
+ /*TODO: implement this function*/
+ return 0;
+}
+
+/*
+ * At this point, the struct usb_device is about to go away, the device has
+ * disconnected, and all traffic has been stopped and the endpoints have been
+ * disabled. Free any DC data structures associated with that device.
+ */
+void usbssp_free_dev(struct usbssp_udc *usbssp_data)
+{
+ struct usbssp_device *priv_dev;
+ int i, ret;
+ struct usbssp_slot_ctx *slot_ctx;
+
+ priv_dev = &usbssp_data->devs;
+ slot_ctx = usbssp_get_slot_ctx(usbssp_data, priv_dev->out_ctx);
+ trace_usbssp_free_dev(slot_ctx);
+
+ for (i = 0; i < 31; ++i)
+ priv_dev->eps[i].ep_state &= ~EP_STOP_CMD_PENDING;
+
+ ret = usbssp_disable_slot(usbssp_data);
+ if (ret)
+ usbssp_free_priv_device(usbssp_data);
+}
+
+int usbssp_disable_slot(struct usbssp_udc *usbssp_data)
+{
+ struct usbssp_command *command;
+ u32 state;
+ int ret = 0;
+
+ command = usbssp_alloc_command(usbssp_data, false, GFP_ATOMIC);
+ if (!command)
+ return -ENOMEM;
+
+ /* Don't disable the slot if the device controller is dead. */
+ state = readl(&usbssp_data->op_regs->status);
+ if (state == 0xffffffff ||
+ (usbssp_data->usbssp_state & USBSSP_STATE_DYING) ||
+ (usbssp_data->usbssp_state & USBSSP_STATE_HALTED)) {
+ kfree(command);
+ return -ENODEV;
+ }
+
+ ret = usbssp_queue_slot_control(usbssp_data, command, TRB_DISABLE_SLOT);
+ if (ret) {
+ kfree(command);
+ return ret;
+ }
+ usbssp_ring_cmd_db(usbssp_data);
+ return ret;
+}
+
+/*
+ * Returns 0 if the DC n out of device slots, the Enable Slot command
+ * timed out, or allocating memory failed. Returns 1 on success.
+ */
+int usbssp_alloc_dev(struct usbssp_udc *usbssp_data)
+{
+ int ret, slot_id;
+ struct usbssp_command *command;
+ struct usbssp_slot_ctx *slot_ctx;
+
+ command = usbssp_alloc_command(usbssp_data, true, GFP_ATOMIC);
+
+ if (!command)
+ return -ENOMEM;
+
+ ret = usbssp_queue_slot_control(usbssp_data, command, TRB_ENABLE_SLOT);
+
+ if (ret) {
+ usbssp_free_command(usbssp_data, command);
+ return ret;
+ }
+
+ usbssp_ring_cmd_db(usbssp_data);
+ spin_unlock_irqrestore(&usbssp_data->irq_thread_lock,
+ usbssp_data->irq_thread_flag);
+ wait_for_completion(command->completion);
+ spin_lock_irqsave(&usbssp_data->irq_thread_lock,
+ usbssp_data->irq_thread_flag);
+
+ slot_id = usbssp_data->slot_id;
+
+ if (!slot_id || command->status != COMP_SUCCESS) {
+ dev_err(usbssp_data->dev,
+ "Error while assigning device slot ID\n");
+ usbssp_free_command(usbssp_data, command);
+ return 0;
+ }
+
+ usbssp_free_command(usbssp_data, command);
+
+ if (!usbssp_alloc_priv_device(usbssp_data, GFP_ATOMIC)) {
+ dev_warn(usbssp_data->dev,
+ "Could not allocate usbssp_device data structures\n");
+ goto disable_slot;
+ }
+
+ slot_ctx = usbssp_get_slot_ctx(usbssp_data, usbssp_data->devs.out_ctx);
+ trace_usbssp_alloc_dev(slot_ctx);
+
+ return 1;
+
+disable_slot:
+ ret = usbssp_disable_slot(usbssp_data);
+ if (ret)
+ usbssp_free_priv_device(usbssp_data);
+
+ return 0;
+}
+
int usbssp_gen_setup(struct usbssp_udc *usbssp_data)
{
int retval;
@@ -420,7 +595,6 @@ int usbssp_gadget_exit(struct usbssp_udc *usbssp_data)

usb_del_gadget_udc(&usbssp_data->gadget);
usbssp_gadget_free_endpoint(usbssp_data);
- /*TODO: add usbssp_stop implementation*/
- //usbssp_stop(usbssp_data);
+ usbssp_stop(usbssp_data);
return ret;
}
diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h
index 145371eee47d..1827781125bd 100644
--- a/drivers/usb/usbssp/gadget.h
+++ b/drivers/usb/usbssp/gadget.h
@@ -1685,6 +1685,8 @@ void usbssp_dbg_trace(struct usbssp_udc *usbssp_data,
/* USBSSP memory management */
void usbssp_mem_cleanup(struct usbssp_udc *usbssp_data);
int usbssp_mem_init(struct usbssp_udc *usbssp_data, gfp_t flags);
+void usbssp_free_priv_device(struct usbssp_udc *usbssp_data);
+int usbssp_alloc_priv_device(struct usbssp_udc *usbssp_data, gfp_t flags);
unsigned int usbssp_last_valid_endpoint(u32 added_ctxs);
int usbssp_ring_expansion(struct usbssp_udc *usbssp_data,
struct usbssp_ring *ring,
@@ -1712,12 +1714,15 @@ int usbssp_handshake(void __iomem *ptr, u32 mask, u32 done, int usec);
void usbssp_quiesce(struct usbssp_udc *usbssp_data);
int usbssp_halt(struct usbssp_udc *usbssp_data);
extern int usbssp_reset(struct usbssp_udc *usbssp_data);
+int usbssp_disable_slot(struct usbssp_udc *usbssp_data);

int usbssp_suspend(struct usbssp_udc *usbssp_data, bool do_wakeup);
int usbssp_resume(struct usbssp_udc *usbssp_data, bool hibernated);

irqreturn_t usbssp_irq(int irq, void *priv);

+int usbssp_alloc_dev(struct usbssp_udc *usbssp_data);
+void usbssp_free_dev(struct usbssp_udc *usbssp_data);
/* USBSSP ring, segment, TRB, and TD functions */
dma_addr_t usbssp_trb_virt_to_dma(struct usbssp_segment *seg,
union usbssp_trb *trb);
@@ -1726,6 +1731,12 @@ struct usbssp_segment *usbssp_trb_in_td(struct usbssp_udc *usbssp_data,
union usbssp_trb *start_trb,
union usbssp_trb *end_trb,
dma_addr_t suspect_dma, bool debug);
+void usbssp_ring_cmd_db(struct usbssp_udc *usbssp_data);
+int usbssp_queue_slot_control(struct usbssp_udc *usbssp_data,
+ struct usbssp_command *cmd, u32 trb_type);
+int usbssp_queue_stop_endpoint(struct usbssp_udc *usbssp_data,
+ struct usbssp_command *cmd,
+ unsigned int ep_index, int suspend);
void usbssp_handle_command_timeout(struct work_struct *work);

void usbssp_cleanup_command_queue(struct usbssp_udc *usbssp_data);
@@ -1753,6 +1764,12 @@ void usbssp_gadget_free_endpoint(struct usbssp_udc *usbssp_data);
int usbssp_gadget_init_endpoint(struct usbssp_udc *usbssp_data);
unsigned int usbssp_port_speed(unsigned int port_status);
void usbssp_gadget_reset_interrupt(struct usbssp_udc *usbssp_data);
+void usbssp_gadget_disconnect_interrupt(struct usbssp_udc *usbssp_data);
+int usbssp_stop_device(struct usbssp_udc *usbssp_data, int suspend);
+int usbssp_halt_endpoint(struct usbssp_udc *usbssp_data,
+ struct usbssp_ep *dep, int value);
+int usbssp_cmd_stop_ep(struct usbssp_udc *usbssp_data, struct usb_gadget *g,
+ struct usbssp_ep *ep_priv);

static inline char *usbssp_slot_state_string(u32 state)
{
--
2.17.1


2018-07-19 18:04:47

by Pawel Laszczak

[permalink] [raw]
Subject: [PATCH 01/31] usb: usbssp: Defined register maps and other useful structures.

It is a first patch introduce Cadence USBSSP DRD
controller. This patch is related to device side.

Device part of USBSSP controller base on standard
XHCI specification.

File define macros used bye USBSSP controller,
structures holding and grouping registers, and other
object that are used by device controller.

Register map include:
struct usbssp_cap_regs - Capabilities Register Set.
struct usbssp_op_regs - Operational Register Set.
struct usbssp_intr_reg - Interrupter Register Set.
struct usbssp_run_regs - Runtime Register Set.

Object used by hardware includes:
struct usbssp_doorbell_array - array used for arming command and
transfer rings.
struct usbssp_slot_ctx - holds information related to Slot Context.
struct usbssp_ep_ctx - hold information related to Endpoint Context.
struct usbssp_input_control_ctx - hold information about
Input Control Context.
struct usbssp_link_trb - define link TRB.
struct usbssp_transfer_event - define Transfer Event TRB.
struct usbssp_event_cmd - define Command Event TRB.
struct usbssp_generic_trb - generic used TRB object.

Patch also add some high level object that hold information
used in driver.

Some of them are:
struct usbssp_udc - main object in driver which is passed
as parameter to most of function in driver and allows
to access to all other structures.
struct usbssp_ep - holds information related to USB endpoint.
struct usbssp_command - describe command send to Event Ring.
struct usbssp_device - holds information relate do USB device.
struct usbssp_segment - holds information describing segments of TRBs.
struct usbssp_td - hold information about Transfer Descriptor.
struct usbssp_ring - holds information related to Transfer, Event or
Command ring.
struct usbssp_request - holds information related to single transfer.

Signed-off-by: Pawel Laszczak <[email protected]>
---
drivers/usb/usbssp/gadget.h | 1571 +++++++++++++++++++++++++++++++++++
1 file changed, 1571 insertions(+)
create mode 100644 drivers/usb/usbssp/gadget.h

diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h
new file mode 100644
index 000000000000..49e7271187cc
--- /dev/null
+++ b/drivers/usb/usbssp/gadget.h
@@ -0,0 +1,1571 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USBSSP device controller driver
+ *
+ * Copyright (C) 2018 Cadence.
+ *
+ * Author: Pawel Laszczak
+ *
+ * A lot of code based on Linux XHCI driver.
+ * Origin: Copyright (C) 2008 Intel Corp.
+ */
+
+#ifndef __LINUX_USBSSP_GADGET_H
+#define __LINUX_USBSSP_GADGET_H
+
+#include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/usb/gadget.h>
+
+/* Max number slots - only 1 is allowed */
+#define DEV_MAX_SLOTS 1
+
+/* max ports for USBSSP-Dev - only 2 are allowed*/
+#define MAX_USBSSP_PORTS 2
+
+#define USBSSP_EP0_SETUP_SIZE 512
+
+/*16 for in and 16 for out */
+#define USBSSP_ENDPOINTS_NUM 32
+
+/* HCSPARAMS1 - hcs_params1 - bitmasks */
+/* bits 0:7, Max Device Slots */
+#define DEV_HCS_MAX_SLOTS(p) (((p) >> 0) & 0xff)
+#define DEV_HCS_SLOTS_MASK 0xff
+
+/*
+ * USBSSP register interface.
+ */
+
+/**
+ * struct usbssp_cap_regs - USBSSP Registers.
+ * @hc_capbase: length of the capabilities register and DC version number
+ * @hcs_params1: HCSPARAMS1 - Structural Parameters 1
+ * @hcs_params2: HCSPARAMS2 - Structural Parameters 2
+ * @hcs_params3: HCSPARAMS3 - Structural Parameters 3
+ * @hcc_params: HCCPARAMS - Capability Parameters
+ * @db_off: DBOFF - Doorbell array offset
+ * @run_regs_off: RTSOFF - Runtime register space offset
+ * @hcc_params2: HCCPARAMS2 Capability Parameters 2,
+ */
+struct usbssp_cap_regs {
+ __le32 hc_capbase;
+ __le32 hcs_params1;
+ __le32 hcs_params2;
+ __le32 hcs_params3;
+ __le32 hcc_params;
+ __le32 db_off;
+ __le32 run_regs_off;
+ __le32 hcc_params2;
+ /* Reserved up to (CAPLENGTH - 0x1C) */
+};
+
+/* hc_capbase bitmasks */
+/* bits 7:0 - how long is the Capabilities register */
+#define HC_LENGTH(p) USBSSP_HC_LENGTH(p)
+/* bits 31:16 */
+#define HC_VERSION(p) (((p) >> 16) & 0xffff)
+
+/* HCSPARAMS1 - hcs_params1 - bitmasks */
+/* bits 0:7, Max Device Slots */
+#define HCS_MAX_SLOTS(p) (((p) >> 0) & 0xff)
+#define HCS_SLOTS_MASK GENMASK(7, 0)
+/* bits 8:18, Max Interrupters */
+#define HCS_MAX_INTRS(p) (((p) >> 8) & 0x7ff)
+/* bits 24:31, Max Ports - max value is 0x7F = 127 ports */
+#define HCS_MAX_PORTS(p) (((p) >> 24) & 0x7f)
+
+/* HCSPARAMS2 - hcs_params2 - bitmasks */
+/* bits 0:3, Isochronous Scheduling Threshold (IST)*/
+#define HCS_IST(p) (((p) >> 0) & 0xf)
+/* bits 4:7, max number of Event Ring segments */
+#define HCS_ERST_MAX(p) (((p) >> 4) & 0xf)
+/* bits 21:25 Hi 5 bits of Scratchpad buffers SW must allocate for the HW */
+/* bit 26 Scratchpad restore - for save/restore HW state - not used yet */
+/* bits 27:31 Lo 5 bits of Scratchpad buffers SW must allocate for the HW */
+#define HCS_MAX_SCRATCHPAD(p) ((((p) >> 16) & 0x3e0) | (((p) >> 27) & 0x1f))
+
+/* HCSPARAMS3 - hcs_params3 - bitmasks */
+/* bits 0:7, Max U1 to U0 latency for the roothub ports */
+#define HCS_U1_LATENCY(p) (((p) >> 0) & 0xff)
+/* bits 16:31, Max U2 to U0 latency for the roothub ports */
+#define HCS_U2_LATENCY(p) (((p) >> 16) & 0xffff)
+
+/* HCCPARAMS - hcc_params - bitmasks */
+/* true: DC can use 64-bit address pointers */
+#define HCC_64BIT_ADDR(p) ((p) & BIT(0))
+/* true: DC uses 64-byte Device Context structures*/
+#define HCC_64BYTE_CONTEXT(p) ((p) & BIT(2))
+/* true: DC has port indicators */
+#define HCS_INDICATOR(p) ((p) & BIT(4))
+/* true: no secondary Stream ID Support */
+#define HCC_NSS(p) ((p) & BIT(7))
+/*true: DC parse All Event Data*/
+#define HCC_PAE(p) ((p) & BIT(8))
+/* true: DC supports Stopped - Short Packet */
+#define HCC_SPC(p) ((p) & BIT(9))
+/*true: DC support a stopped EDTLA Capability (SEC) */
+#define HCC_SEC(p) ((p) & BIT(10))
+/* true: DC has Contiguous Frame ID Capability */
+#define HCC_CFC(p) ((p) & BIT(11))
+/* Max size for Primary Stream Arrays - 2^(n+1), where n is bits 12:15 */
+#define HCC_MAX_PSA(p) (1 << ((((p) >> 12) & 0xf) + 1))
+/* Extended Capabilities pointer from PCI base - section 5.3.6 */
+#define HCC_EXT_CAPS(p) USBSSP_HCC_EXT_CAPS(p)
+
+#define CTX_SIZE(_hcc) (HCC_64BYTE_CONTEXT(_hcc) ? 64 : 32)
+
+/* db_off bitmask - bits 0:1 reserved */
+#define DBOFF_MASK (~0x3)
+
+/* run_regs_off bitmask - bits 0:4 reserved */
+#define RTSOFF_MASK (~GENMASK(4, 0))
+
+/* HCCPARAMS2 - hcc_params2 - bitmasks */
+/* true: DC supports U3 entry Capability */
+#define HCC2_U3C(p) ((p) & BIT(0))
+/* true: DC supports Force Save context Capability */
+#define HCC2_FSC(p) ((p) & BIT(2))
+/* true: DC supports Compliance Transition Capability */
+#define HCC2_CTC(p) ((p) & BIT(3))
+/* true: DC support Large ESIT payload Capability > 48k */
+#define HCC2_LEC(p) ((p) & BIT(4))
+/* true: DC support Extended TBC Capability, Isoc burst count > 65535 */
+#define HCC2_ETC(p) ((p) & BIT(6))
+
+/* Number of registers per port */
+#define NUM_PORT_REGS 4
+
+#define PORTSC 0
+#define PORTPMSC 1
+#define PORTLI 2
+#define PORTHLPMC 3
+
+/**
+ * struct usbssp_op_regs - Device Controller Operational Registers.
+ * @command: USBCMD - DC command register
+ * @status: USBSTS - DC status register
+ * @page_size: This indicates the page size that the device controller
+ * supports. If bit n is set, the DC supports a page size
+ * of 2^(n+12), up to a 128MB page size.
+ * 4K is the minimum page size.
+ * @cmd_ring: CRP - 64-bit Command Ring Pointer
+ * @dcbaa_ptr: DCBAAP - 64-bit Device Context Base Address Array Pointer
+ * @config_reg: CONFIG - Configure Register
+ * @port_status_base: PORTSCn - base address for Port Status and Control
+ * Each port has a Port Status and Control register,
+ * followed by a Port Power Management Status and Control
+ * register, a Port Link Info register, and a reserved
+ * register.
+ * @port_power_base: PORTPMSCn - base address for
+ * Port Power Management Status and Control
+ * @port_link_base: PORTLIn - base address for Port Link Info (current
+ * Link PM state and control) for USB 2.1 and USB 3.0
+ * devices.
+ */
+struct usbssp_op_regs {
+ __le32 command;
+ __le32 status;
+ __le32 page_size;
+ __le32 reserved1;
+ __le32 reserved2;
+ __le32 dev_notification;
+ __le64 cmd_ring;
+ /* rsvd: offset 0x20-2F */
+ __le32 reserved3[4];
+ __le64 dcbaa_ptr;
+ __le32 config_reg;
+ /* rsvd: offset 0x3C-3FF */
+ __le32 reserved4[241];
+ /* port 1 registers, which serve as a base address for other ports */
+ __le32 port_status_base;
+ __le32 port_power_base;
+ __le32 port_link_base;
+ __le32 reserved5;
+ /* registers for ports 2-255 */
+ __le32 reserved6[NUM_PORT_REGS*254];
+};
+
+/* USBCMD - USB command - command bitmasks */
+/* start/stop DC execution - do not write unless DC is halted*/
+#define CMD_RUN USBSSP_CMD_RUN
+
+/*
+ * Reset DC - resets internal DC state machine and all registers
+ * (except PCI config regs).
+ */
+#define CMD_RESET BIT(1)
+/* Event Interrupt Enable - a '1' allows interrupts from the device controller*/
+#define CMD_EIE USBSSP_CMD_EIE
+/* Device System Error Interrupt Enable - get out-of-band signal for DC errors*/
+#define CMD_HSEIE USBSSP_CMD_HSEIE
+/* device controller save/restore state. */
+#define CMD_CSS BIT(8)
+#define CMD_CRS BIT(9)
+/* Enable Wrap Event - '1' means DC generates an event when MFINDEX wraps. */
+#define CMD_EWE USBSSP_CMD_EWE
+/* bit 14 Extended TBC Enable, changes Isoc TRB fields to support larger TBC */
+#define CMD_ETE BIT(14)
+/*bit 13 CEM Enable (CME) */
+#define CMD_CEM BIT(13)
+/* Device Enable bit */
+#define CMD_DEVEN BIT(15)
+/* bits 16:31 are reserved (and should be preserved on writes). */
+
+/* IMAN - Interrupt Management Register */
+#define IMAN_IE BIT(1)
+#define IMAN_IP BIT(0)
+
+/* USBSTS - USB status - status bitmasks */
+/* DC not running - set to 1 when run/stop bit is cleared. */
+#define STS_HALT USBSSP_STS_HALT
+/* serious error, e.g. PCI parity error. The DC will clear the run/stop bit. */
+#define STS_FATAL BIT(2)
+/* event interrupt - clear this prior to clearing any IP flags in IR set*/
+#define STS_EINT BIT(3)
+/* port change detect */
+#define STS_PORT BIT(4)
+/* bits 5:7 reserved and zeroed */
+/* save state status - '1' means DC is saving state */
+#define STS_SAVE BIT(8)
+/* restore state status - '1' means DC is restoring state */
+#define STS_RESTORE BIT(9)
+/* true: save or restore error */
+#define STS_SRE BIT(10)
+/* true: Controller Not Ready to accept doorbell or op reg writes after reset */
+#define STS_CNR USBSSP_STS_CNR
+/* true: internal Device Controller Error - SW needs to reset and reinitialize*/
+#define STS_HCE BIT(12)
+/* bits 13:31 reserved and should be preserved */
+
+/* CRCR - Command Ring Control Register - cmd_ring bitmasks */
+/* bit 0 is the command ring cycle state */
+/* stop ring operation after completion of the currently executing command */
+#define CMD_RING_PAUSE BIT(1)
+/* stop ring immediately - abort the currently executing command */
+#define CMD_RING_ABORT BIT(2)
+/* true: command ring is running */
+#define CMD_RING_RUNNING BIT(3)
+/* bits 4:5 reserved and should be preserved */
+/* Command Ring pointer - bit mask for the lower 32 bits. */
+#define CMD_RING_RSVD_BITS (0x3f)
+
+/* CONFIG - Configure Register - config_reg bitmasks */
+/* bits 0:7 - maximum number of device slots enabled (NumSlotsEn) */
+#define MAX_DEVS(p) ((p) & 0xff)
+/* bit 8: U3 Entry Enabled, assert PLC when controller enters U3*/
+#define CONFIG_U3E BIT(8)
+/* bit 9: Configuration Information Enable */
+#define CONFIG_CIE BIT(9)
+/* bits 10:31 - reserved and should be preserved */
+
+/* PORTSC - Port Status and Control Register - port_status_base bitmasks */
+/* true: device connected */
+#define PORT_CONNECT BIT(0)
+/* true: port enabled */
+#define PORT_PE BIT(1)
+/* bit 2 reserved and zeroed */
+/* true: port has an over-current condition */
+#define PORT_OC BIT(3)
+/* true: port reset signaling asserted */
+#define PORT_RESET BIT(4)
+
+/*
+ * Port Link State - bits 5:8
+ * A read gives the current link PM state of the port,
+ * a write with Link State Write Strobe set sets the link state.
+ */
+#define PORT_PLS_MASK GENMASK(8, 5)
+#define XDEV_U0 (0x0 << 5)
+#define XDEV_U1 (0x1 << 5)
+#define XDEV_U2 (0x2 << 5)
+#define XDEV_U3 (0x3 << 5)
+#define XDEV_DISABLED (0x4 << 5)
+#define XDEV_RXDETECT (0x5 << 5)
+#define XDEV_INACTIVE (0x6 << 5)
+#define XDEV_POLLING (0x7 << 5)
+#define XDEV_RECOVERY (0x8 << 5)
+#define XDEV_HOT_RESET (0x9 << 5)
+#define XDEV_COMP_MODE (0xa << 5)
+#define XDEV_TEST_MODE (0xb << 5)
+#define XDEV_RESUME (0xf << 5)
+
+/* true: port has power (see HCC_PPC) */
+#define PORT_POWER BIT(9)
+
+/*
+ * bits 10:13 indicate device speed:
+ * 0 - undefined speed - port hasn't be initialized by a reset yet
+ * 1 - full speed
+ * 2 - low speed
+ * 3 - high speed
+ * 4 - super speed
+ * 5-15 reserved
+ */
+#define DEV_SPEED_MASK GENMASK(13, 10)
+#define XDEV_FS (0x1 << 10)
+#define XDEV_LS (0x2 << 10)
+#define XDEV_HS (0x3 << 10)
+#define XDEV_SS (0x4 << 10)
+#define XDEV_SSP (0x5 << 10)
+#define DEV_UNDEFSPEED(p) (((p) & DEV_SPEED_MASK) == (0x0<<10))
+#define DEV_FULLSPEED(p) (((p) & DEV_SPEED_MASK) == XDEV_FS)
+#define DEV_LOWSPEED(p) (((p) & DEV_SPEED_MASK) == XDEV_LS)
+#define DEV_HIGHSPEED(p) (((p) & DEV_SPEED_MASK) == XDEV_HS)
+#define DEV_SUPERSPEED(p) (((p) & DEV_SPEED_MASK) == XDEV_SS)
+#define DEV_SUPERSPEEDPLUS(p) (((p) & DEV_SPEED_MASK) == XDEV_SSP)
+#define DEV_SUPERSPEED_ANY(p) (((p) & DEV_SPEED_MASK) >= XDEV_SS)
+#define DEV_PORT_SPEED(p) (((p) >> 10) & 0x0f)
+
+/* Bits 20:23 in the Slot Context are twice */
+#define SLOT_SPEED_FS (XDEV_FS << 10)
+#define SLOT_SPEED_LS (XDEV_LS << 10)
+#define SLOT_SPEED_HS (XDEV_HS << 10)
+#define SLOT_SPEED_SS (XDEV_SS << 10)
+#define SLOT_SPEED_SSP (XDEV_SSP << 10)
+
+/* Port Link State Write Strobe - set this when changing link state */
+#define PORT_LINK_STROBE BIT(16)
+/* true: connect status change */
+#define PORT_CSC BIT(17)
+/* true: port enable change */
+#define PORT_PEC BIT(18)
+/*
+ * true: warm reset for a USB 3.0 device is done. A "hot" reset puts the port
+ * into an enabled state, and the device into the default state. A "warm" reset
+ * also resets the link, forcing the device through the link training sequence.
+ * SW can also look at the Port Reset register to see when warm reset is done.
+ */
+#define PORT_WRC BIT(19)
+/* true: over-current change */
+#define PORT_OCC BIT(20)
+/* true: reset change - 1 to 0 transition of PORT_RESET */
+#define PORT_RC BIT(21)
+/*
+ * port link status change - set on some port link state transitions:
+ * Transition Reason
+ * ------------------------------------------------------------------------------
+ * - U3 to Resume Wakeup signaling from a device
+ * - Resume to Recovery to U0 USB 3.0 device resume
+ * - Resume to U0 USB 2.0 device resume
+ * - U3 to Recovery to U0 Software resume of USB 3.0 device complete
+ * - U3 to U0 Software resume of USB 2.0 device complete
+ * - U2 to U0 L1 resume of USB 2.1 device complete
+ * - U0 to U0 L1 entry rejection by USB 2.1 device
+ * - U0 to disabled L1 entry error with USB 2.1 device
+ * - Any state to inactive Error on USB 3.0 port
+ */
+#define PORT_PLC BIT(22)
+/* port configure error change - port failed to configure its link partner */
+#define PORT_CEC BIT(23)
+/* wake on connect (enable) */
+#define PORT_WKCONN_E BIT(25)
+/* wake on disconnect (enable) */
+#define PORT_WKDISC_E BIT(26)
+/* wake on over-current (enable) */
+#define PORT_WKOC_E BIT(27)
+/* bits 28:30 reserved */
+/* Indicates if Warm Reset is being received*/
+#define PORT_WR BIT(31)
+
+/* We mark duplicate entries with -1 */
+#define DUPLICATE_ENTRY ((u8)(-1))
+
+/* Port Power Management Status and Control - port_power_base bitmasks */
+/*
+ * Inactivity timer value for transitions into U1, in microseconds.
+ * Timeout can be up to 127us. 0xFF means an infinite timeout.
+ */
+#define PORT_U1_TIMEOUT(p) ((p) & 0xff)
+#define PORT_U1_TIMEOUT_MASK GENMASK(7, 0)
+/* Inactivity timer value for transitions into U2 */
+#define PORT_U2_TIMEOUT(p) (((p) & 0xff) << 8)
+#define PORT_U2_TIMEOUT_MASK GENMASK(14, 8)
+/* Bits 24:31 for port testing */
+
+/* USB2 Protocol PORTSPMSC */
+#define PORT_L1S_MASK GENMASK(2, 0)
+#define PORT_L1S_SUCCESS 1
+#define PORT_RWE BIT(3)
+#define PORT_HIRD(p) (((p) & 0xf) << 4)
+#define PORT_HIRD_MASK GENMASK(7, 4)
+#define PORT_L1DS_MASK GENMASK(11, 8)
+#define PORT_L1DS(p) (((p) & 0xff) << 8)
+#define PORT_HLE BIT(16)
+#define PORT_TEST_MODE_SHIFT 28
+
+/* USB3 Protocol PORTLI Port Link Information */
+#define PORT_RX_LANES(p) (((p) >> 16) & 0xf)
+#define PORT_TX_LANES(p) (((p) >> 20) & 0xf)
+
+/* USB2 Protocol PORTHLPMC */
+#define PORT_HIRDM(p) ((p) & 3)
+#define PORT_L1_TIMEOUT(p) (((p) & 0xff) << 2)
+#define PORT_BESLD(p) (((p) & 0xf) << 10)
+
+/* use 512 microseconds as USB2 LPM L1 default timeout. */
+#define USBSSP_L1_TIMEOUT 512
+
+#define USBSSP_DEFAULT_BESL 4
+
+/**
+ * struct usbssp_intr_reg - Interrupt Register Set
+ * @irq_pending: IMAN - Interrupt Management Register. Used to enable
+ * interrupts and check for pending interrupts.
+ * @irq_control: IMOD - Interrupt Moderation Register.
+ * Used to throttle interrupts.
+ * @erst_size: Number of segments in the Event Ring Segment
+ * Table (ERST).
+ * @erst_base: ERST base address.
+ * @erst_dequeue: Event ring dequeue pointer.
+ *
+ * Each interrupter (defined by a MSI-X vector) has an event ring and an Event
+ * Ring Segment Table (ERST) associated with it. The event ring is comprised of
+ * multiple segments of the same size. The DC places events on the ring and
+ * "updates the Cycle bit in the TRBs to indicate to software the current
+ * position of the Enqueue Pointer." The driver processes those events and
+ * updates the dequeue pointer.
+ */
+struct usbssp_intr_reg {
+ __le32 irq_pending;
+ __le32 irq_control;
+ __le32 erst_size;
+ __le32 rsvd;
+ __le64 erst_base;
+ __le64 erst_dequeue;
+};
+
+/* irq_pending bitmasks */
+#define ER_IRQ_PENDING(p) ((p) & 0x1)
+/* bits 2:31 need to be preserved */
+/* THIS IS BUGGY - FIXME - IP IS WRITE 1 TO CLEAR */
+#define ER_IRQ_CLEAR(p) ((p) & 0xfffffffe)
+#define ER_IRQ_ENABLE(p) ((ER_IRQ_CLEAR(p)) | 0x2)
+#define ER_IRQ_DISABLE(p) ((ER_IRQ_CLEAR(p)) & ~(0x2))
+
+/* irq_control bitmasks */
+/*
+ * Minimum interval between interrupts (in 250ns intervals). The interval
+ * between interrupts will be longer if there are no events on the event ring.
+ * Default is 4000 (1 ms).
+ */
+#define ER_IRQ_INTERVAL_MASK GENMASK(15, 0)
+/* Counter used to count down the time to the next interrupt - HW use only */
+#define ER_IRQ_COUNTER_MASK GENMASK(31, 16)
+
+/* erst_size bitmasks */
+/* Preserve bits 16:31 of erst_size */
+#define ERST_SIZE_MASK GENMASK(31, 16)
+
+/* erst_dequeue bitmasks */
+/*
+ * Dequeue ERST Segment Index (DESI) - Segment number (or alias)
+ * where the current dequeue pointer lies. This is an optional HW hint.
+ */
+#define ERST_DESI_MASK GENMASK(2, 0)
+/*
+ * Event Handler Busy (EHB) - is the event ring scheduled to be serviced by
+ * a work queue (or delayed service routine)?
+ */
+#define ERST_EHB BIT(3)
+#define ERST_PTR_MASK GENMASK(3, 0)
+
+/**
+ * struct usbssp_run_regs
+ * @microframe_index: MFINDEX - current microframe number
+ *
+ * Device Controller Runtime Registers:
+ * "Software should read and write these registers using only Dword (32 bit)
+ * or larger accesses"
+ */
+struct usbssp_run_regs {
+ __le32 microframe_index;
+ __le32 rsvd[7];
+ struct usbssp_intr_reg ir_set[128];
+};
+
+/**
+ * struct doorbell_array
+ *
+ * Bits 0 - 7: Endpoint target
+ * Bits 8 - 15: RsvdZ
+ * Bits 16 - 31: Stream ID
+ *
+ */
+struct usbssp_doorbell_array {
+ __le32 doorbell[2];
+};
+
+#define DB_VALUE(ep, stream) ((((ep) + 1) & 0xff) | ((stream) << 16))
+#define DB_VALUE_EP0_OUT(ep, stream) ((ep) & 0xff)
+#define DB_VALUE_CMD 0x00000000
+
+/**
+ * struct usbssp_protocol_caps
+ * @revision: major revision, minor revision, capability ID,
+ * and next capability pointer.
+ * @name_string:Four ASCII characters to say which spec this DC
+ * follows, typically "USB ".
+ * @port_info: Port offset, count, and protocol-defined information.
+ */
+struct usbssp_protocol_caps {
+ u32 revision;
+ u32 name_string;
+ u32 port_info;
+};
+
+#define USBSSP_EXT_PORT_MAJOR(x) (((x) >> 24) & 0xff)
+#define USBSSP_EXT_PORT_MINOR(x) (((x) >> 16) & 0xff)
+#define USBSSP_EXT_PORT_PSIC(x) (((x) >> 28) & 0x0f)
+#define USBSSP_EXT_PORT_OFF(x) ((x) & 0xff)
+#define USBSSP_EXT_PORT_COUNT(x) (((x) >> 8) & 0xff)
+
+#define USBSSP_EXT_PORT_PSIV(x) (((x) >> 0) & 0x0f)
+#define USBSSP_EXT_PORT_PSIE(x) (((x) >> 4) & 0x03)
+#define USBSSP_EXT_PORT_PLT(x) (((x) >> 6) & 0x03)
+#define USBSSP_EXT_PORT_PFD(x) (((x) >> 8) & 0x01)
+#define USBSSP_EXT_PORT_LP(x) (((x) >> 14) & 0x03)
+#define USBSSP_EXT_PORT_PSIM(x) (((x) >> 16) & 0xffff)
+
+#define PLT_MASK GENMASK(7, 6)
+#define PLT_SYM (0x00 << 6)
+#define PLT_ASYM_RX (0x02 << 6)
+#define PLT_ASYM_TX (0x03 << 6)
+
+/**
+ * struct usbssp_container_ctx
+ * @type: Type of context. Used to calculated offsets to contained contexts.
+ * @size: Size of the context data
+ * @bytes: The raw context data given to HW
+ * @dma: dma address of the bytes
+ *
+ * Represents either a Device or Input context. Holds a pointer to the raw
+ * memory used for the context (bytes) and dma address of it (dma).
+ */
+struct usbssp_container_ctx {
+ unsigned int type;
+#define USBSSP_CTX_TYPE_DEVICE 0x1
+#define USBSSP_CTX_TYPE_INPUT 0x2
+ int size;
+ u8 *bytes;
+ dma_addr_t dma;
+};
+
+/**
+ * struct usbssp_slot_ctx
+ * @dev_info: device speed, and last valid endpoint
+ * @dev_info2: Max exit latency for device number
+ * @int_target: interrupter target number
+ * @dev_state: slot state and device address
+ *
+ * Slot Context - This assumes the DC uses 32-byte context
+ * structures. If the DC uses 64-byte contexts, there is an additional 32 bytes
+ * reserved at the end of the slot context for DC internal use.
+ */
+struct usbssp_slot_ctx {
+ __le32 dev_info;
+ __le32 dev_info2;
+ __le32 int_target;
+ __le32 dev_state;
+ /* offset 0x10 to 0x1f reserved for DC internal use */
+ __le32 reserved[4];
+};
+
+/* dev_info bitmasks */
+/* Device speed - values defined by PORTSC Device Speed field - 20:23 */
+#define DEV_SPEED GENMASK(23, 20)
+#define GET_DEV_SPEED(n) (((n) & DEV_SPEED) >> 20)
+/* bit 24-26 reserved */
+/* Index of the last valid endpoint context in this device context - 27:31 */
+#define LAST_CTX_MASK GENMASK(31, 27)
+#define LAST_CTX(p) ((p) << 27)
+#define LAST_CTX_TO_EP_NUM(p) (((p) >> 27) - 1)
+#define SLOT_FLAG BIT(0)
+#define EP0_FLAG BIT(1)
+
+/* dev_info2 bitmasks */
+/* Max Exit Latency (ms) - worst case time to wake up all links in dev path */
+#define MAX_EXIT (0xffff)
+/* Root device port number that is needed to access the USB device */
+#define ROOT_DEV_PORT(p) (((p) & 0xff) << 16)
+#define DEVINFO_TO_ROOT_DEV_PORT(p) (((p) >> 16) & 0xff)
+
+/* dev_state bitmasks */
+/* USB device address - assigned by the usbssp */
+#define DEV_ADDR_MASK GENMASK(7, 0)
+/* bits 8:26 reserved */
+/* Slot state */
+#define SLOT_STATE GENMASK(31, 27)
+#define GET_SLOT_STATE(p) (((p) & (0x1f << 27)) >> 27)
+
+#define SLOT_STATE_DISABLED 0
+#define SLOT_STATE_ENABLED SLOT_STATE_DISABLED
+#define SLOT_STATE_DEFAULT 1
+#define SLOT_STATE_ADDRESSED 2
+#define SLOT_STATE_CONFIGURED 3
+
+/**
+ * struct usbssp_ep_ctx
+ * @ep_info: endpoint state, streams, mult, and interval information.
+ * @ep_info2: information on endpoint type, max packet size, max burst size,
+ * error count, and whether the DC will force an event for all
+ * transactions.
+ * @deq: 64-bit ring dequeue pointer address. If the endpoint only
+ * defines one stream, this points to the endpoint transfer ring.
+ * Otherwise, it points to a stream context array, which has a
+ * ring pointer for each flow.
+ * @tx_info: Average TRB lengths for the endpoint ring and
+ * max payload within an Endpoint Service Interval Time (ESIT).
+ *
+ * Endpoint Context - This assumes the DC uses 32-byte context
+ * structures. If the DC uses 64-byte contexts, there is an additional 32 bytes
+ * reserved at the end of the endpoint context for DC internal use.
+ */
+struct usbssp_ep_ctx {
+ __le32 ep_info;
+ __le32 ep_info2;
+ __le64 deq;
+ __le32 tx_info;
+ /* offset 0x14 - 0x1f reserved for DC internal use */
+ __le32 reserved[3];
+};
+
+/* ep_info bitmasks */
+/*
+ * Endpoint State - bits 0:2
+ * 0 - disabled
+ * 1 - running
+ * 2 - halted due to halt condition - ok to manipulate endpoint ring
+ * 3 - stopped
+ * 4 - TRB error
+ * 5-7 - reserved
+ */
+#define EP_STATE_MASK GENMASK(3, 0)
+#define EP_STATE_DISABLED 0
+#define EP_STATE_RUNNING 1
+#define EP_STATE_HALTED 2
+#define EP_STATE_STOPPED 3
+#define EP_STATE_ERROR 4
+#define GET_EP_CTX_STATE(ctx) (le32_to_cpu((ctx)->ep_info) & EP_STATE_MASK)
+
+/* Mult - Max number of burtst within an interval, in EP companion desc. */
+#define EP_MULT(p) (((p) & 0x3) << 8)
+#define CTX_TO_EP_MULT(p) (((p) >> 8) & 0x3)
+/* bits 10:14 are Max Primary Streams */
+/* bit 15 is Linear Stream Array */
+/* Interval - period between requests to an endpoint - 125u increments. */
+#define EP_INTERVAL(p) (((p) & 0xff) << 16)
+#define EP_INTERVAL_TO_UFRAMES(p) (1 << (((p) >> 16) & 0xff))
+#define CTX_TO_EP_INTERVAL(p) (((p) >> 16) & 0xff)
+#define EP_MAXPSTREAMS_MASK GENMASK(14, 10)
+#define EP_MAXPSTREAMS(p) (((p) << 10) & EP_MAXPSTREAMS_MASK)
+#define CTX_TO_EP_MAXPSTREAMS(p) (((p) & EP_MAXPSTREAMS_MASK) >> 10)
+/* Endpoint is set up with a Linear Stream Array (vs. Secondary Stream Array) */
+#define EP_HAS_LSA BIT(15)
+#define CTX_TO_MAX_ESIT_PAYLOAD_HI(p) (((p) >> 24) & 0xff)
+
+/* ep_info2 bitmasks */
+/*
+ * Force Event - generate transfer events for all TRBs for this endpoint
+ * This will tell the DC to ignore the IOC and ISP flags (for debugging only).
+ */
+#define FORCE_EVENT (0x1)
+#define ERROR_COUNT(p) (((p) & 0x3) << 1)
+#define CTX_TO_EP_TYPE(p) (((p) >> 3) & 0x7)
+#define EP_TYPE(p) ((p) << 3)
+#define ISOC_OUT_EP 1
+#define BULK_OUT_EP 2
+#define INT_OUT_EP 3
+#define CTRL_EP 4
+#define ISOC_IN_EP 5
+#define BULK_IN_EP 6
+#define INT_IN_EP 7
+/* bit 6 reserved */
+/* bit 7 is Device Initiate Disable - for disabling stream selection */
+#define MAX_BURST(p) (((p)&0xff) << 8)
+#define CTX_TO_MAX_BURST(p) (((p) >> 8) & 0xff)
+#define MAX_PACKET(p) (((p)&0xffff) << 16)
+#define MAX_PACKET_MASK GENMASK(31, 16)
+#define MAX_PACKET_DECODED(p) (((p) >> 16) & 0xffff)
+
+/*
+ * Get max packet size from ep desc. Bit 10..0 specify the max packet size.
+ * USB2.0 spec 9.6.6.
+ */
+#define GET_MAX_PACKET(p) ((p) & 0x7ff)
+
+/* tx_info bitmasks */
+#define EP_AVG_TRB_LENGTH(p) ((p) & 0xffff)
+#define EP_MAX_ESIT_PAYLOAD_LO(p) (((p) & 0xffff) << 16)
+#define EP_MAX_ESIT_PAYLOAD_HI(p) ((((p) >> 16) & 0xff) << 24)
+#define CTX_TO_MAX_ESIT_PAYLOAD(p) (((p) >> 16) & 0xffff)
+
+/* deq bitmasks */
+#define EP_CTX_CYCLE_MASK BIT(0)
+#define SCTX_DEQ_MASK (~0xfL)
+
+/**
+ * struct usbssp_input_control_context
+ * Input control context;
+ *
+ * @drop_context: set the bit of the endpoint context you want to disable
+ * @add_context: set the bit of the endpoint context you want to enable
+ */
+struct usbssp_input_control_ctx {
+ __le32 drop_flags;
+ __le32 add_flags;
+ __le32 rsvd2[6];
+};
+
+#define EP_IS_ADDED(ctrl_ctx, i) \
+ (le32_to_cpu(ctrl_ctx->add_flags) & (1 << (i + 1)))
+#define EP_IS_DROPPED(ctrl_ctx, i) \
+ (le32_to_cpu(ctrl_ctx->drop_flags) & (1 << (i + 1)))
+
+/*
+ * Represents everything that is needed to issue a command on the command ring.
+ * It's useful to pre-allocate these for commands that cannot fail due to
+ * out-of-memory errors, like freeing streams.
+ */
+struct usbssp_command {
+ /* Input context for changing device state */
+ struct usbssp_container_ctx *in_ctx;
+ u32 status;
+ /*
+ * If completion is null, no one is waiting on this command
+ * and the structure can be freed after the command completes.
+ */
+ struct completion *completion;
+ union usbssp_trb *command_trb;
+ struct list_head cmd_list;
+};
+
+/* drop context bitmasks */
+#define DROP_EP(x) BIT(x)
+/* add context bitmasks */
+#define ADD_EP(x) BIT(x)
+
+struct usbssp_stream_ctx {
+ /* 64-bit stream ring address, cycle state, and stream type */
+ __le64 stream_ring;
+ /* offset 0x14 - 0x1f reserved for DC internal use */
+ __le32 reserved[2];
+};
+
+/* Stream Context Types - bits 3:1 of stream ctx deq ptr */
+#define SCT_FOR_CTX(p) (((p) & 0x7) << 1)
+/* Secondary stream array type, dequeue pointer is to a transfer ring */
+#define SCT_SEC_TR 0
+/* Primary stream array type, dequeue pointer is to a transfer ring */
+#define SCT_PRI_TR 1
+/* Dequeue pointer is for a secondary stream array (SSA) with 8 entries */
+#define SCT_SSA_8 2
+#define SCT_SSA_16 3
+#define SCT_SSA_32 4
+#define SCT_SSA_64 5
+#define SCT_SSA_128 6
+#define SCT_SSA_256 7
+
+/* Assume no secondary streams for now */
+struct usbssp_stream_info {
+ struct usbssp_ring **stream_rings;
+ /* Number of streams, including stream 0 (which drivers can't use) */
+ unsigned int num_streams;
+ /*
+ * The stream context array may be bigger than
+ * the number of streams the driver asked for
+ */
+ struct usbssp_stream_ctx *stream_ctx_array;
+ unsigned int num_stream_ctxs;
+ dma_addr_t ctx_array_dma;
+ /* For mapping physical TRB addresses to segments in stream rings */
+ struct radix_tree_root trb_address_map;
+ struct usbssp_command *free_streams_command;
+};
+
+#define SMALL_STREAM_ARRAY_SIZE 256
+#define MEDIUM_STREAM_ARRAY_SIZE 1024
+
+struct usbssp_ep {
+ struct usb_ep endpoint;
+ struct list_head pending_list;
+ struct usbssp_udc *usbssp_data;
+
+ u8 number;
+ u8 type;
+ u32 interval;
+ char name[20];
+ u8 direction;
+ u8 stream_capable;
+
+ struct usbssp_ring *ring;
+ /* Related to endpoints that are configured to use stream IDs only */
+ struct usbssp_stream_info *stream_info;
+ /*
+ * Temporary storage in case the configure endpoint command fails and we
+ * have to restore the device state to the previous state
+ */
+ struct usbssp_ring *new_ring;
+ unsigned int ep_state;
+
+#define SET_DEQ_PENDING BIT(0)
+#define EP_HALTED BIT(1) /* For stall handling */
+#define EP_STOP_CMD_PENDING BIT(2) /* For USB request cancellation */
+/* Transitioning the endpoint to using streams, don't enqueue request */
+#define EP_GETTING_STREAMS BIT(3)
+#define EP_HAS_STREAMS BIT(4)
+/* Transitioning the endpoint to not using streams, don't enqueue requests */
+#define EP_GETTING_NO_STREAMS BIT(5)
+#define USBSSP_EP_ENABLED BIT(6)
+#define USBSSP_EP_WEDGE BIT(8)
+#define USBSSP_EP_BUSY BIT(9)
+#define USBSSP_EP_CONF_PENDING BIT(10)
+#define USBSSP_EP_DISABLE_PENDING BIT(11)
+#define EP0_HALTED_STATUS BIT(12) /*For stall handling of Status Stage*/
+
+ struct usbssp_td *stopped_td;
+ unsigned int stopped_stream;
+
+ /*
+ * Dequeue pointer and dequeue segment for a submitted Set TR Dequeue
+ * command. We'll need to update the ring's dequeue segment and dequeue
+ * pointer after the command completes.
+ */
+ struct usbssp_segment *queued_deq_seg;
+ union usbssp_trb *queued_deq_ptr;
+ /*
+ * Sometimes the DC can not process isochronous endpoint ring quickly
+ * enough, and it will miss some isoc tds on the ring and generate
+ * a Missed Service Error Event.
+ * Set skip flag when receive a Missed Service Error Event and
+ * process the missed tds on the endpoint ring.
+ */
+ bool skip;
+ /* Isoch Frame ID checking storage */
+ int next_frame_id;
+ /* Use new Isoch TRB layout needed for extended TBC support */
+ bool use_extended_tbc;
+};
+
+
+struct usbssp_device {
+ struct usb_gadget *gadget;
+
+ /*
+ * Commands to the hardware are passed an "input context" that
+ * tells the hardware what to change in its data structures.
+ * The hardware will return changes in an "output context" that
+ * software must allocate for the hardware. We need to keep
+ * track of input and output contexts separately because
+ * these commands might fail and we don't trust the hardware.
+ */
+ struct usbssp_container_ctx *out_ctx;
+ /* Used for addressing devices and configuration changes */
+ struct usbssp_container_ctx *in_ctx;
+ struct usbssp_ep eps[USBSSP_ENDPOINTS_NUM];
+ u8 port_num;
+ /* The current max exit latency for the enabled USB3 link states. */
+ u16 current_mel;
+ u8 usb2_hw_lpm_capable:1;
+ /* Used for the debugfs interfaces. */
+ void *debugfs_private;
+};
+
+/**
+ * struct usbssp_device_context_array
+ * @dev_context_ptr: array of 64-bit DMA addresses for device contexts
+ */
+struct usbssp_device_context_array {
+ /* 64-bit device addresses; we only write 32-bit addresses */
+ __le64 dev_context_ptrs[DEV_MAX_SLOTS+1];
+ /* private pointers */
+ dma_addr_t dma;
+};
+
+struct usbssp_transfer_event {
+ /* 64-bit buffer address, or immediate data */
+ __le64 buffer;
+ __le32 transfer_len;
+ /* This field is interpreted differently based on the type of TRB */
+ __le32 flags;
+};
+
+/* Transfer event TRB length bit mask */
+/* bits 0:23 */
+#define EVENT_TRB_LEN(p) ((p) & 0xffffff)
+
+/** Transfer Event bit fields **/
+#define TRB_TO_EP_ID(p) (((p) >> 16) & 0x1f)
+
+/* Completion Code - only applicable for some types of TRBs */
+#define COMP_CODE_MASK (0xff << 24)
+#define GET_COMP_CODE(p) (((p) & COMP_CODE_MASK) >> 24)
+#define COMP_INVALID 0
+#define COMP_SUCCESS 1
+#define COMP_DATA_BUFFER_ERROR 2
+#define COMP_BABBLE_DETECTED_ERROR 3
+#define COMP_USB_TRANSACTION_ERROR 4
+#define COMP_TRB_ERROR 5
+#define COMP_RESOURCE_ERROR 7
+#define COMP_NO_SLOTS_AVAILABLE_ERROR 9
+#define COMP_INVALID_STREAM_TYPE_ERROR 10
+#define COMP_SLOT_NOT_ENABLED_ERROR 11
+#define COMP_ENDPOINT_NOT_ENABLED_ERROR 12
+#define COMP_SHORT_PACKET 13
+#define COMP_RING_UNDERRUN 14
+#define COMP_RING_OVERRUN 15
+#define COMP_VF_EVENT_RING_FULL_ERROR 16
+#define COMP_PARAMETER_ERROR 17
+#define COMP_CONTEXT_STATE_ERROR 19
+#define COMP_EVENT_RING_FULL_ERROR 21
+#define COMP_INCOMPATIBLE_DEVICE_ERROR 22
+#define COMP_MISSED_SERVICE_ERROR 23
+#define COMP_COMMAND_RING_STOPPED 24
+#define COMP_COMMAND_ABORTED 25
+#define COMP_STOPPED 26
+#define COMP_STOPPED_LENGTH_INVALID 27
+#define COMP_STOPPED_SHORT_PACKET 28
+#define COMP_MAX_EXIT_LATENCY_TOO_LARGE_ERROR 29
+#define COMP_ISOCH_BUFFER_OVERRUN 31
+#define COMP_EVENT_LOST_ERROR 32
+#define COMP_UNDEFINED_ERROR 33
+#define COMP_INVALID_STREAM_ID_ERROR 34
+
+struct usbssp_link_trb {
+ /* 64-bit segment pointer*/
+ __le64 segment_ptr;
+ __le32 intr_target;
+ __le32 control;
+};
+
+/* control bitfields */
+#define LINK_TOGGLE BIT(1)
+
+/* Command completion event TRB */
+struct usbssp_event_cmd {
+ /* Pointer to command TRB, or the value passed by the event data trb */
+ __le64 cmd_trb;
+ __le32 status;
+ __le32 flags;
+};
+
+/* flags bitmasks */
+
+/* Address device - disable SetAddress */
+#define TRB_BSR BIT(9)
+
+/* Configure Endpoint - Deconfigure */
+#define TRB_DC BIT(9)
+
+/* Stop Ring - Transfer State Preserve */
+#define TRB_TSP BIT(9)
+
+enum usbssp_ep_reset_type {
+ EP_HARD_RESET,
+ EP_SOFT_RESET,
+};
+
+/* Force Event */
+#define TRB_TO_VF_INTR_TARGET(p) (((p) & (0x3ff << 22)) >> 22)
+#define TRB_TO_VF_ID(p) (((p) & (0xff << 16)) >> 16)
+
+/* Set Latency Tolerance Value */
+#define TRB_TO_BELT(p) (((p) & (0xfff << 16)) >> 16)
+
+/* Get Port Bandwidth */
+#define TRB_TO_DEV_SPEED(p) (((p) & (0xf << 16)) >> 16)
+
+/* Force Header */
+#define TRB_TO_PACKET_TYPE(p) ((p) & 0x1f)
+#define TRB_TO_DEV_PORT(p) (((p) & (0xff << 24)) >> 24)
+
+enum usbssp_setup_dev {
+ SETUP_CONTEXT_ONLY,
+ SETUP_CONTEXT_ADDRESS,
+};
+
+/* bits 16:23 are the virtual function ID */
+/* bits 24:31 are the slot ID */
+#define TRB_TO_SLOT_ID(p) (((p) & (0xff<<24)) >> 24)
+#define SLOT_ID_FOR_TRB(p) (((p) & 0xff) << 24)
+
+/* Stop Endpoint TRB - ep_index to endpoint ID for this TRB */
+#define TRB_TO_EP_INDEX(p) ((((p) & (0x1f << 16)) >> 16) - 1)
+#define EP_ID_FOR_TRB(p) ((((p) + 1) & 0x1f) << 16)
+
+#define SUSPEND_PORT_FOR_TRB(p) (((p) & 1) << 23)
+#define TRB_TO_SUSPEND_PORT(p) (((p) & (1 << 23)) >> 23)
+#define LAST_EP_INDEX 30
+
+/* Set TR Dequeue Pointer command TRB fields. */
+#define TRB_TO_STREAM_ID(p) ((((p) & (0xffff << 16)) >> 16))
+#define STREAM_ID_FOR_TRB(p) ((((p)) & 0xffff) << 16)
+#define SCT_FOR_TRB(p) (((p) << 1) & 0x7)
+
+/* Link TRB specific fields */
+#define TRB_TC BIT(1)
+
+/* Port Status Change Event TRB fields */
+/* Port ID - bits 31:24 */
+#define GET_PORT_ID(p) (((p) & (0xff << 24)) >> 24)
+
+#define EVENT_DATA BIT(2)
+
+/* Normal TRB fields */
+/* transfer_len bitmasks - bits 0:16 */
+#define TRB_LEN(p) ((p) & 0x1ffff)
+/* TD Size, packets remaining in this TD, bits 21:17 (5 bits, so max 31) */
+#define TRB_TD_SIZE(p) (min((p), (u32)31) << 17)
+#define GET_TD_SIZE(p) (((p) & 0x3e0000) >> 17)
+/* DC uses the TD_SIZE field for TBC if Extended TBC is enabled (ETE) */
+#define TRB_TD_SIZE_TBC(p) (min((p), (u32)31) << 17)
+/* Interrupter Target - which MSI-X vector to target the completion event at */
+#define TRB_INTR_TARGET(p) (((p) & 0x3ff) << 22)
+#define GET_INTR_TARGET(p) (((p) >> 22) & 0x3ff)
+/* Total burst count field, Rsvdz on DC with Extended TBC enabled (ETE) */
+#define TRB_TBC(p) (((p) & 0x3) << 7)
+#define TRB_TLBPC(p) (((p) & 0xf) << 16)
+
+/* Cycle bit - indicates TRB ownership by DC or driver*/
+#define TRB_CYCLE BIT(0)
+/*
+ * Force next event data TRB to be evaluated before task switch.
+ * Used to pass OS data back after a TD completes.
+ */
+#define TRB_ENT BIT(1)
+/* Interrupt on short packet */
+#define TRB_ISP BIT(2)
+/* Set PCIe no snoop attribute */
+#define TRB_NO_SNOOP BIT(3)
+/* Chain multiple TRBs into a TD */
+#define TRB_CHAIN BIT(4)
+/* Interrupt on completion */
+#define TRB_IOC BIT(5)
+/* The buffer pointer contains immediate data */
+#define TRB_IDT BIT(6)
+/* Block Event Interrupt */
+#define TRB_BEI BIT(9)
+
+/* Control transfer TRB specific fields */
+#define TRB_DIR_IN BIT(16)
+#define TRB_TX_TYPE(p) ((p) << 16)
+#define TRB_DATA_OUT 2
+#define TRB_DATA_IN 3
+
+/* TRB bit mask in Data Stage TRB */
+#define TRB_SETUPID_BITMASK (0x300)
+#define TRB_SETUPID(p) ((p) << 8)
+#define TRB_SETUPID_TO_TYPE(p) (((p) & TRB_SETUPID_BITMASK) >> 8)
+
+#define TRB_SETUP_SPEEDID_USB3 0x1
+#define TRB_SETUP_SPEEDID_USB2 0x0
+#define TRB_SETUP_SPEEDID(p) ((p) & (1 << 7))
+
+#define TRB_SETUPSTAT_ACK 0x1
+#define TRB_SETUPSTAT_STALL 0x0
+#define TRB_SETUPSTAT(p) ((p) << 6)
+
+/* Isochronous TRB specific fields */
+#define TRB_SIA BIT(31)
+#define TRB_FRAME_ID(p) (((p) & 0x7ff) << 20)
+
+struct usbssp_generic_trb {
+ __le32 field[4];
+};
+
+union usbssp_trb {
+ struct usbssp_link_trb link;
+ struct usbssp_transfer_event trans_event;
+ struct usbssp_event_cmd event_cmd;
+ struct usbssp_generic_trb generic;
+};
+
+/* 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 */
+/* bulk, interrupt, isoc scatter/gather, and control data stage */
+#define TRB_NORMAL 1
+/* setup stage for control transfers */
+#define TRB_SETUP 2
+/* data stage for control transfers */
+#define TRB_DATA 3
+/* status stage for control transfers */
+#define TRB_STATUS 4
+/* isoc transfers */
+#define TRB_ISOC 5
+/* TRB for linking ring segments */
+#define TRB_LINK 6
+#define TRB_EVENT_DATA 7
+/* Transfer Ring No-op (not for the command ring) */
+#define TRB_TR_NOOP 8
+
+/* Command TRBs */
+/* Enable Slot Command */
+#define TRB_ENABLE_SLOT 9
+/* Disable Slot Command */
+#define TRB_DISABLE_SLOT 10
+/* Address Device Command */
+#define TRB_ADDR_DEV 11
+/* Configure Endpoint Command */
+#define TRB_CONFIG_EP 12
+/* Evaluate Context Command */
+#define TRB_EVAL_CONTEXT 13
+/* Reset Endpoint Command */
+#define TRB_RESET_EP 14
+/* Stop Transfer Ring Command */
+#define TRB_STOP_RING 15
+/* Set Transfer Ring Dequeue Pointer Command */
+#define TRB_SET_DEQ 16
+/* Reset Device Command */
+#define TRB_RESET_DEV 17
+/* Force Event Command (opt) */
+#define TRB_FORCE_EVENT 18
+/* Set Latency Tolerance Value Command (opt) */
+#define TRB_SET_LT 20
+/* Force Header Command - generate a transaction or link management packet */
+#define TRB_FORCE_HEADER 22
+/* No-op Command - not for transfer rings */
+#define TRB_CMD_NOOP 23
+/* TRB IDs 24-31 reserved */
+
+/* Event TRBS */
+/* Transfer Event */
+#define TRB_TRANSFER 32
+/* Command Completion Event */
+#define TRB_COMPLETION 33
+/* Port Status Change Event */
+#define TRB_PORT_STATUS 34
+/* Doorbell Event (opt) */
+#define TRB_DOORBELL 36
+/* Device Controller Event */
+#define TRB_HC_EVENT 37
+/* Device Notification Event - device sent function wake notification */
+#define TRB_DEV_NOTE 38
+/* MFINDEX Wrap Event - microframe counter wrapped */
+#define TRB_MFINDEX_WRAP 39
+/* TRB IDs 40-47 reserved */
+/* Endpoint Not Ready Event */
+#define TRB_ENDPOINT_NRDY 48
+/* TRB IDs 49-53 reserved */
+/* Halt Endpoint Command */
+#define TRB_HALT_ENDPOINT 54
+/* Flush Endpoint Command */
+#define TRB_FLUSH_ENDPOINT 58
+
+static inline const char *usbssp_trb_type_string(u8 type)
+{
+ switch (type) {
+ case TRB_NORMAL:
+ return "Normal";
+ case TRB_SETUP:
+ return "Setup Stage";
+ case TRB_DATA:
+ return "Data Stage";
+ case TRB_STATUS:
+ return "Status Stage";
+ case TRB_ISOC:
+ return "Isoch";
+ case TRB_LINK:
+ return "Link";
+ case TRB_EVENT_DATA:
+ return "Event Data";
+ case TRB_TR_NOOP:
+ return "No-Op";
+ case TRB_ENABLE_SLOT:
+ return "Enable Slot Command";
+ case TRB_DISABLE_SLOT:
+ return "Disable Slot Command";
+ case TRB_ADDR_DEV:
+ return "Address Device Command";
+ case TRB_CONFIG_EP:
+ return "Configure Endpoint Command";
+ case TRB_EVAL_CONTEXT:
+ return "Evaluate Context Command";
+ case TRB_RESET_EP:
+ return "Reset Endpoint Command";
+ case TRB_STOP_RING:
+ return "Stop Ring Command";
+ case TRB_SET_DEQ:
+ return "Set TR Dequeue Pointer Command";
+ case TRB_RESET_DEV:
+ return "Reset Device Command";
+ case TRB_FORCE_EVENT:
+ return "Force Event Command";
+ case TRB_SET_LT:
+ return "Set Latency Tolerance Value Command";
+ case TRB_FORCE_HEADER:
+ return "Force Header Command";
+ case TRB_CMD_NOOP:
+ return "No-Op Command";
+ case TRB_TRANSFER:
+ return "Transfer Event";
+ case TRB_COMPLETION:
+ return "Command Completion Event";
+ case TRB_PORT_STATUS:
+ return "Port Status Change Event";
+ case TRB_DOORBELL:
+ return "Doorbell Event";
+ case TRB_HC_EVENT:
+ return "Device Controller Event";
+ case TRB_DEV_NOTE:
+ return "Device Notification Event";
+ case TRB_MFINDEX_WRAP:
+ return "MFINDEX Wrap Event";
+ case TRB_ENDPOINT_NRDY:
+ return "Endpoint Not ready";
+ case TRB_HALT_ENDPOINT:
+ return "Halt Endpoint";
+ case TRB_FLUSH_ENDPOINT:
+ return "FLush Endpoint";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+#define TRB_TYPE_LINK(x) (((x) & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK))
+/* Above, but for __le32 types -- can avoid work by swapping constants: */
+#define TRB_TYPE_LINK_LE32(x) (((x) & cpu_to_le32(TRB_TYPE_BITMASK)) == \
+ cpu_to_le32(TRB_TYPE(TRB_LINK)))
+#define TRB_TYPE_NOOP_LE32(x) (((x) & cpu_to_le32(TRB_TYPE_BITMASK)) == \
+ cpu_to_le32(TRB_TYPE(TRB_TR_NOOP)))
+
+/*
+ * TRBS_PER_SEGMENT must be a multiple of 4,
+ * since the command ring is 64-byte aligned.
+ * It must also be greater than 16.
+ */
+#define TRBS_PER_SEGMENT 16
+/* Allow two commands + a link TRB, along with any reserved command TRBs */
+#define MAX_RSVD_CMD_TRBS (TRBS_PER_SEGMENT - 3)
+#define TRB_SEGMENT_SIZE (TRBS_PER_SEGMENT * 16)
+#define TRB_SEGMENT_SHIFT (ilog2(TRB_SEGMENT_SIZE))
+/* TRB buffer pointers can't cross 64KB boundaries */
+#define TRB_MAX_BUFF_SHIFT 16
+#define TRB_MAX_BUFF_SIZE (1 << TRB_MAX_BUFF_SHIFT)
+/* How much data is left before the 64KB boundary? */
+#define TRB_BUFF_LEN_UP_TO_BOUNDARY(addr) (TRB_MAX_BUFF_SIZE - \
+ (addr & (TRB_MAX_BUFF_SIZE - 1)))
+
+struct usbssp_segment {
+ union usbssp_trb *trbs;
+ /* private to DC */
+ struct usbssp_segment *next;
+ dma_addr_t dma;
+ /* Max packet sized bounce buffer for td-fragmant alignment */
+ dma_addr_t bounce_dma;
+ void *bounce_buf;
+ unsigned int bounce_offs;
+ unsigned int bounce_len;
+};
+
+struct usbssp_td {
+ struct list_head td_list;
+ struct usbssp_request *priv_request;
+ struct usbssp_segment *start_seg;
+ union usbssp_trb *first_trb;
+ union usbssp_trb *last_trb;
+ struct usbssp_segment *bounce_seg;
+ /* actual_length of the request has already been set */
+ bool request_length_set;
+};
+
+
+/* DC command default timeout value */
+#define USBSSP_CMD_DEFAULT_TIMEOUT (5 * HZ)
+
+struct usbssp_dequeue_state {
+ struct usbssp_segment *new_deq_seg;
+ union usbssp_trb *new_deq_ptr;
+ int new_cycle_state;
+ unsigned int stream_id;
+};
+
+enum usbssp_ring_type {
+ TYPE_CTRL = 0,
+ TYPE_ISOC,
+ TYPE_BULK,
+ TYPE_INTR,
+ TYPE_STREAM,
+ TYPE_COMMAND,
+ TYPE_EVENT,
+};
+
+struct usbssp_ring {
+ struct usbssp_segment *first_seg;
+ struct usbssp_segment *last_seg;
+ union usbssp_trb *enqueue;
+ struct usbssp_segment *enq_seg;
+ union usbssp_trb *dequeue;
+ struct usbssp_segment *deq_seg;
+ struct list_head td_list;
+ /*
+ * Write the cycle state into the TRB cycle field to give ownership of
+ * the TRB to the device controller (if we are the producer),
+ * or to check if we own the TRB (if we are the consumer).
+ */
+ u32 cycle_state;
+ unsigned int stream_id;
+ unsigned int num_segs;
+ unsigned int num_trbs_free;
+ unsigned int num_trbs_free_temp;
+ unsigned int bounce_buf_len;
+ enum usbssp_ring_type type;
+ bool last_td_was_short;
+ struct radix_tree_root *trb_address_map;
+};
+
+struct usbssp_erst_entry {
+ /* 64-bit event ring segment address */
+ __le64 seg_addr;
+ __le32 seg_size;
+ /* Set to zero */
+ __le32 rsvd;
+};
+
+struct usbssp_erst {
+ struct usbssp_erst_entry *entries;
+ unsigned int num_entries;
+ /* usbssp_udc->event_ring keeps track of segment dma addresses */
+ dma_addr_t erst_dma_addr;
+ /* Num entries the ERST can contain */
+ unsigned int erst_size;
+};
+
+struct usbssp_scratchpad {
+ u64 *sp_array;
+ dma_addr_t sp_dma;
+ void **sp_buffers;
+};
+
+struct usbssp_request {
+ /*number of TDs associated with this request*/
+ int num_tds;
+ /*number of actually handled TDs*/
+ int num_tds_done;
+ struct usbssp_td *td;
+
+ struct usb_request request;
+ struct list_head list;
+ struct usbssp_ep *dep;
+
+ struct scatterlist *sg;
+ unsigned int num_pending_sgs;
+ u8 epnum;
+ unsigned direction:1;
+ unsigned mapped:1;
+ uint32_t start_frame;
+ int stream_id;
+};
+
+
+/*
+ * Each segment table entry is 4*32bits long. 1K seems like an ok size:
+ * (1K bytes * 8bytes/bit) / (4*32 bits) = 64 segment entries in the table,
+ * meaning 64 ring segments.
+ * Initial allocated size of the ERST, in number of entries
+ */
+#define ERST_NUM_SEGS 1
+/* Initial allocated size of the ERST, in number of entries */
+#define ERST_SIZE 64
+/* Initial number of event segment rings allocated */
+#define ERST_ENTRIES 1
+/* Poll every 60 seconds */
+#define POLL_TIMEOUT 60
+
+
+struct s3_save {
+ u32 command;
+ u32 dev_nt;
+ u64 dcbaa_ptr;
+ u32 config_reg;
+ u32 irq_pending;
+ u32 irq_control;
+ u32 erst_size;
+ u64 erst_base;
+ u64 erst_dequeue;
+};
+
+enum usbssp_ep0_state {
+ USBSSP_EP0_UNCONNECTED = 0,
+ USBSSP_EP0_SETUP_PHASE,
+ USBSSP_EP0_DATA_PHASE,
+ USBSSP_EP0_STATUS_PHASE,
+};
+
+struct usbssp_ports {
+ u8 maj_rev;
+ u8 min_rev;
+ u32 *psi; /* array of protocol speed ID entries */
+ u8 psi_count;
+ u8 psi_uid_count;
+};
+
+struct usbssp_udc {
+ struct device *dev;
+ struct usb_gadget gadget;
+ struct usb_gadget_driver *gadget_driver;
+
+ unsigned int irq; /* irq allocated */
+ void __iomem *regs; /* device memory/io */
+ resource_size_t rsrc_start; /* memory/io resource start */
+ resource_size_t rsrc_len; /* memory/io resource length */
+ u8 msi_enabled;
+
+ /* USBSSP Registers */
+ struct usbssp_cap_regs __iomem *cap_regs;
+ struct usbssp_op_regs __iomem *op_regs;
+ struct usbssp_run_regs __iomem *run_regs;
+ struct usbssp_doorbell_array __iomem *dba;
+ /* current interrupter register set */
+ struct usbssp_intr_reg __iomem *ir_set;
+
+ /* Cached register copies of read-only USBSSP data */
+ __u32 hcs_params1;
+ __u32 hcs_params2;
+ __u32 hcs_params3;
+ __u32 hcc_params;
+ __u32 hcc_params2;
+
+ unsigned int num_endpoints;
+
+ u8 setupId;
+ u8 setup_speed;
+ enum usbssp_ep0_state ep0state;
+ /*three state or two state setup */
+ u8 ep0_expect_in;
+ struct usbssp_request usb_req_ep0_in;
+ u8 three_stage_setup;
+ u32 delayed_status;
+ /*temporary buffer for setup packet*/
+ struct usb_ctrlrequest setup;
+ void *setup_buf;
+ u8 device_address;
+ u8 bos_event_detected :1;
+
+ uint8_t defered_event;
+#define EVENT_DEV_CONNECTED 1
+#define EVENT_DEV_DISCONECTED 2
+#define EVENT_SETUP_PACKET 4
+#define EVENT_USB_RESET 8
+ int remote_wakeup_allowed;
+
+ spinlock_t lock;
+ spinlock_t irq_thread_lock;
+ unsigned long irq_thread_flag;
+
+ /* packed release number */
+ u16 hci_version;
+ u8 max_slots;
+ u8 max_interrupters;
+ u8 max_ports;
+ u8 isoc_threshold;
+ /* imod_interval in ns (I * 250ns) */
+ u32 imod_interval;
+
+ /*revision of current used port*/
+ u8 port_major_revision;
+ /* 4KB min, 128MB max */
+ int page_size;
+ /* Valid values are 12 to 20, inclusive */
+ int page_shift;
+ /* msi-x vectors */
+ int msix_count;
+ struct msix_entry *msix_entries;
+
+ /* data structures */
+ struct usbssp_device_context_array *dcbaa;
+ struct usbssp_ring *cmd_ring;
+ unsigned int cmd_ring_state;
+
+#define CMD_RING_STATE_RUNNING BIT(0)
+#define CMD_RING_STATE_ABORTED BIT(1)
+#define CMD_RING_STATE_STOPPED BIT(2)
+
+ struct list_head cmd_list;
+ unsigned int cmd_ring_reserved_trbs;
+ struct delayed_work cmd_timer;
+ struct work_struct bottom_irq;
+ struct workqueue_struct *bottom_irq_wq;
+ struct completion cmd_ring_stop_completion;
+ struct usbssp_command *current_cmd;
+ struct usbssp_ring *event_ring;
+ struct usbssp_erst erst;
+ /* Scratchpad */
+ struct usbssp_scratchpad *scratchpad;
+
+ /* slot enabling and address device helpers */
+ /* these are not thread safe so use mutex */
+ struct mutex mutex;
+ int slot_id;
+
+ /* Internal mirror of the HW's dcbaa */
+ struct usbssp_device devs;
+
+ /* DMA pools */
+ struct dma_pool *device_pool;
+ struct dma_pool *segment_pool;
+ struct dma_pool *small_streams_pool;
+ struct dma_pool *medium_streams_pool;
+
+ unsigned int usbssp_state;
+
+ u32 command;
+ struct s3_save s3;
+
+ /*
+ * Device controller is dying - not responding to commands.
+ *
+ * DC interrupts have been disabled and a watchdog timer
+ * will (or has already) halt the device controller, and complete all
+ * requests with an -ESHUTDOWN code. Any code that sees this status
+ * (other than the timer that set it) should stop touching
+ * hardware immediately. Interrupt handlers should return
+ * immediately when they see this status.
+ */
+#define USBSSP_STATE_DYING BIT(0)
+#define USBSSP_STATE_HALTED BIT(1)
+#define USBSSP_STATE_REMOVING BIT(2)
+#define USBSSP_STATE_DISCONNECT_PENDING BIT(4)
+#define USBSSP_STATE_DISCONNECTED BIT(8)
+
+ unsigned int num_active_eps;
+
+ /* Is each DC port a USB 3.0, USB 2.0, or USB 1.1 port? */
+ u8 *port_array;
+ /* Pointers to USB 3.0 PORTSC registers */
+ __le32 __iomem *usb3_ports;
+ unsigned int num_usb3_ports;
+ /* Pointers to USB 2.0 PORTSC registers */
+ __le32 __iomem *usb2_ports;
+ unsigned int num_usb2_ports;
+ struct usbssp_ports usb2_rhub;
+ struct usbssp_ports usb3_rhub;
+ /* support software LPM */
+ unsigned sw_lpm_support:1;
+ /* support USB2 hardware LPM */
+ unsigned hw_lpm_support:1;
+ /* cached usb2 extended protocol capabilities */
+ u32 *ext_caps;
+ unsigned int num_ext_caps;
+
+ u32 port_suspended;
+ u32 port_remote_wakeup;
+ u16 test_mode;
+
+ struct dentry *debugfs_root;
+ struct dentry *debugfs_slots;
+ struct list_head regset_list;
+};
+
+#define GET_PORT_RRBESL(p) (((p) >> 17) & 0xf)
+#define PORT_RBESL(p) (((p) & 0xf) << 4)
+#define PORT_BESL_MASK (0xf << 4)
+#define PORT_HLE_MASK BIT(6)
+
+#define PORT_L1S_HLE0_STALL 1
+
+#define USBSSP_CFC_DELAY 10
+
+#endif /* __LINUX_USBSSP_GADGET_H */
--
2.17.1


2018-07-19 18:04:53

by Pawel Laszczak

[permalink] [raw]
Subject: [PATCH 04/31] usb: usbssp: Added USBSSP platform driver

This patch adds platform driver that is entry point for loading and
unloading usbssp.ko modules.
It also adds information about this driver to drivers/usb/Kconfig
and drivers/usb/Makefile files and create Kconfig and Makefile
files in drivers/usb/usbssp directory.

Patch also adds template for some function ivokked from
usbssp_plat.c file. These function will be implemented in next patches.

This patch also introduce usbssp_trb_virt_to_dma that converts
virtual address of TRB's to DMA address. In this moment this
function is used only in gadget-trace.h.

From this moment the driver can be compiled.

Signed-off-by: Pawel Laszczak <[email protected]>
---
drivers/usb/Kconfig | 2 +
drivers/usb/Makefile | 2 +
drivers/usb/usbssp/Kconfig | 21 ++++
drivers/usb/usbssp/Makefile | 11 ++
drivers/usb/usbssp/gadget-ring.c | 48 ++++++++
drivers/usb/usbssp/gadget.c | 64 +++++++++++
drivers/usb/usbssp/gadget.h | 16 ++-
drivers/usb/usbssp/usbssp-plat.c | 186 +++++++++++++++++++++++++++++++
8 files changed, 349 insertions(+), 1 deletion(-)
create mode 100644 drivers/usb/usbssp/Kconfig
create mode 100644 drivers/usb/usbssp/Makefile
create mode 100644 drivers/usb/usbssp/gadget-ring.c
create mode 100644 drivers/usb/usbssp/gadget.c
create mode 100644 drivers/usb/usbssp/usbssp-plat.c

diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index f699abab1787..dc05f384c34c 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -110,6 +110,8 @@ source "drivers/usb/mtu3/Kconfig"

source "drivers/usb/musb/Kconfig"

+source "drivers/usb/usbssp/Kconfig"
+
source "drivers/usb/dwc3/Kconfig"

source "drivers/usb/dwc2/Kconfig"
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 060643a1b5c8..b1cd5f83d440 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -8,6 +8,8 @@
obj-$(CONFIG_USB) += core/
obj-$(CONFIG_USB_SUPPORT) += phy/

+obj-$(CONFIG_USB_USBSSP) += usbssp/
+
obj-$(CONFIG_USB_DWC3) += dwc3/
obj-$(CONFIG_USB_DWC2) += dwc2/
obj-$(CONFIG_USB_ISP1760) += isp1760/
diff --git a/drivers/usb/usbssp/Kconfig b/drivers/usb/usbssp/Kconfig
new file mode 100644
index 000000000000..ee20b01753dc
--- /dev/null
+++ b/drivers/usb/usbssp/Kconfig
@@ -0,0 +1,21 @@
+config USB_USBSSP
+ tristate "Cadence USBSSP DRD Controller"
+ depends on (USB || USB_GADGET) && HAS_DMA
+ select USB_USBSSP_GADGET
+ help
+ Say Y here if your system has a cadence USBSSP dual-role controller.
+ It supports: dual-role switch Host-only, and Peripheral-only.
+
+ If you choose to build this driver is a dynamically linked
+ module, the module will be called usbssp.ko.
+
+if USB_USBSSP
+
+config USB_USBSSP_GADGET
+ tristate "Gadget only mode"
+ default USB_USBSSP
+ depends on USB_GADGET=y || USB_GADGET=USB_USBSSP
+ help
+ Select this when you want to use USBSSP in gadget mode only,
+endif
+
diff --git a/drivers/usb/usbssp/Makefile b/drivers/usb/usbssp/Makefile
new file mode 100644
index 000000000000..d85f15afb51c
--- /dev/null
+++ b/drivers/usb/usbssp/Makefile
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0
+# define_trace.h needs to know how to find our header
+CFLAGS_gadget-trace.o := -I$(src)
+
+obj-$(CONFIG_USB_USBSSP_GADGET) += usbssp.o
+usbssp-y := usbssp-plat.o gadget-ring.o \
+ gadget.o
+
+ifneq ($(CONFIG_TRACING),)
+ usbssp-y += gadget-trace.o
+endif
diff --git a/drivers/usb/usbssp/gadget-ring.c b/drivers/usb/usbssp/gadget-ring.c
new file mode 100644
index 000000000000..d1da59306d02
--- /dev/null
+++ b/drivers/usb/usbssp/gadget-ring.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USBSSP device controller driver
+ *
+ * Copyright (C) 2018 Cadence.
+ *
+ * Author: Pawel Laszczak
+ *
+ * A lot of code based on Linux XHCI driver.
+ * Origin: Copyright (C) 2008 Intel Corp
+ */
+
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/irq.h>
+#include "gadget-trace.h"
+#include "gadget.h"
+
+/*
+ * Returns zero if the TRB isn't in this segment, otherwise it returns the DMA
+ * address of the TRB.
+ */
+dma_addr_t usbssp_trb_virt_to_dma(struct usbssp_segment *seg,
+ union usbssp_trb *trb)
+{
+ unsigned long segment_offset;
+
+ if (!seg || !trb || trb < seg->trbs)
+ return 0;
+ /* offset in TRBs */
+ segment_offset = trb - seg->trbs;
+ if (segment_offset >= TRBS_PER_SEGMENT)
+ return 0;
+ return seg->dma + (segment_offset * sizeof(*trb));
+}
+
+irqreturn_t usbssp_irq(int irq, void *priv)
+{
+ struct usbssp_udc *usbssp_data = (struct usbssp_udc *)priv;
+ irqreturn_t ret = IRQ_NONE;
+ unsigned long flags;
+
+ spin_lock_irqsave(&usbssp_data->lock, flags);
+
+ spin_unlock_irqrestore(&usbssp_data->lock, flags);
+ return ret;
+}
diff --git a/drivers/usb/usbssp/gadget.c b/drivers/usb/usbssp/gadget.c
new file mode 100644
index 000000000000..2f60d7dd1fe4
--- /dev/null
+++ b/drivers/usb/usbssp/gadget.c
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USBSSP device controller driver
+ *
+ * Copyright (C) 2018 Cadence.
+ *
+ * Author: Pawel Laszczak
+ *
+ * A lot of code based on Linux XHCI driver.
+ * Origin: Copyright (C) 2008 Intel Corp
+ */
+
+#include <linux/pci.h>
+#include <linux/irq.h>
+#include <linux/log2.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/dmi.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+
+#include "gadget-trace.h"
+#include "gadget.h"
+
+#ifdef CONFIG_PM
+/*
+ * Stop DC (not bus-specific)
+ *
+ * This is called when the machine transition into S3/S4 mode.
+ *
+ */
+int usbssp_suspend(struct usbssp_udc *usbssp_data, bool do_wakeup)
+{
+ /*TODO*/
+ return -ENOSYS;
+}
+
+/*
+ * start DC (not bus-specific)
+ *
+ * This is called when the machine transition from S3/S4 mode.
+ *
+ */
+int usbssp_resume(struct usbssp_udc *usbssp_data, bool hibernated)
+{
+ /*TODO*/
+ return -ENOSYS;
+}
+
+#endif /* CONFIG_PM */
+
+int usbssp_gadget_init(struct usbssp_udc *usbssp_data)
+{
+ int ret;
+ return ret;
+}
+
+int usbssp_gadget_exit(struct usbssp_udc *usbssp_data)
+{
+ int ret = 0;
+
+ return ret;
+}
diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h
index b5c17603af78..55e20795d900 100644
--- a/drivers/usb/usbssp/gadget.h
+++ b/drivers/usb/usbssp/gadget.h
@@ -9,7 +9,6 @@
* A lot of code based on Linux XHCI driver.
* Origin: Copyright (C) 2008 Intel Corp.
*/
-
#ifndef __LINUX_USBSSP_GADGET_H
#define __LINUX_USBSSP_GADGET_H

@@ -1676,6 +1675,21 @@ static inline void usbssp_write_64(struct usbssp_udc *usbssp_data,
{
lo_hi_writeq(val, regs);
}
+
+/* USBSSP Device controller glue */
+int usbssp_suspend(struct usbssp_udc *usbssp_data, bool do_wakeup);
+int usbssp_resume(struct usbssp_udc *usbssp_data, bool hibernated);
+
+irqreturn_t usbssp_irq(int irq, void *priv);
+
+/* USBSSP ring, segment, TRB, and TD functions */
+dma_addr_t usbssp_trb_virt_to_dma(struct usbssp_segment *seg,
+ union usbssp_trb *trb);
+
+/* USBSSP gadget interface*/
+int usbssp_gadget_init(struct usbssp_udc *usbssp_data);
+int usbssp_gadget_exit(struct usbssp_udc *usbssp_data);
+
static inline char *usbssp_slot_state_string(u32 state)
{
switch (state) {
diff --git a/drivers/usb/usbssp/usbssp-plat.c b/drivers/usb/usbssp/usbssp-plat.c
new file mode 100644
index 000000000000..c048044148aa
--- /dev/null
+++ b/drivers/usb/usbssp/usbssp-plat.c
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USBSSP device controller driver
+ *
+ * Copyright (C) 2018 Cadence.
+ *
+ * Author: Pawel Laszczak
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/usb/phy.h>
+#include <linux/slab.h>
+#include <linux/acpi.h>
+
+#include "gadget.h"
+
+#define DRIVER_AUTHOR "Pawel Laszczak"
+#define DRIVER_DESC "USBSSP Device Controller (USBSSP) Driver"
+
+#ifdef CONFIG_OF
+
+static const struct of_device_id usbssp_dev_of_match[] = {
+ {
+ .compatible = "Cadence, usbssp-dev",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, usbssp_dev_of_match);
+#endif
+
+int usbssp_is_platform(void)
+{
+ return 1;
+}
+
+static int usbssp_plat_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ struct usbssp_udc *usbssp_data;
+ int ret = 0;
+ int irq;
+ struct device *sysdev;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "Incorrect IRQ number\n");
+ return -ENODEV;
+ }
+
+ usbssp_data = devm_kzalloc(dev, sizeof(*usbssp_data), GFP_KERNEL);
+ if (!usbssp_data)
+ return -ENOMEM;
+
+ for (sysdev = &pdev->dev; sysdev; sysdev = sysdev->parent) {
+ if (is_of_node(sysdev->fwnode) ||
+ is_acpi_device_node(sysdev->fwnode))
+ break;
+#ifdef CONFIG_PCI
+ else if (sysdev->bus == &pci_bus_type)
+ break;
+#endif
+ }
+
+ if (!sysdev)
+ sysdev = &pdev->dev;
+
+ /* Try to set 64-bit DMA first */
+ if (WARN_ON(!dev->dma_mask))
+ /* Platform did not initialize dma_mask */
+ ret = dma_coerce_mask_and_coherent(dev,
+ DMA_BIT_MASK(64));
+ else
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
+
+ /* If seting 64-bit DMA mask fails, fall back to 32-bit DMA mask */
+ if (ret) {
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ usbssp_data->regs = devm_ioremap_resource(dev, res);
+
+ if (IS_ERR(usbssp_data->regs)) {
+ ret = PTR_ERR(usbssp_data->regs);
+ return ret;
+ }
+
+ usbssp_data->rsrc_start = res->start;
+ usbssp_data->rsrc_len = resource_size(res);
+
+ ret = devm_request_irq(dev, irq, usbssp_irq, IRQF_SHARED,
+ dev_name(dev), usbssp_data);
+
+ if (ret < 0)
+ return ret;
+
+ usbssp_data->irq = irq;
+ usbssp_data->dev = dev;
+ platform_set_drvdata(pdev, usbssp_data);
+ ret = usbssp_gadget_init(usbssp_data);
+
+ return ret;
+}
+
+static int usbssp_plat_remove(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct usbssp_udc *usbssp_data;
+
+ usbssp_data = (struct usbssp_udc *)platform_get_drvdata(pdev);
+ ret = usbssp_gadget_exit(usbssp_data);
+ return ret;
+
+}
+
+static int __maybe_unused usbssp_plat_suspend(struct device *dev)
+{
+ struct usbssp_udc *usbssp_data = dev_get_drvdata(dev);
+
+ return usbssp_suspend(usbssp_data, device_may_wakeup(dev));
+}
+
+static int __maybe_unused usbssp_plat_resume(struct device *dev)
+{
+ struct usbssp_udc *usbssp_data = dev_get_drvdata(dev);
+
+ return usbssp_resume(usbssp_data, 0);
+}
+
+static int __maybe_unused usbssp_plat_runtime_suspend(struct device *dev)
+{
+ struct usbssp_udc *usbssp_data = dev_get_drvdata(dev);
+
+ return usbssp_suspend(usbssp_data, true);
+}
+
+static int __maybe_unused usbssp_plat_runtime_resume(struct device *dev)
+{
+ struct usbssp_udc *usbssp_data = dev_get_drvdata(dev);
+
+ return usbssp_resume(usbssp_data, 0);
+}
+
+static const struct dev_pm_ops usbssp_plat_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(usbssp_plat_suspend, usbssp_plat_resume)
+
+ SET_RUNTIME_PM_OPS(usbssp_plat_runtime_suspend,
+ usbssp_plat_runtime_resume,
+ NULL)
+};
+
+static struct platform_driver usbssp_driver = {
+ .probe = usbssp_plat_probe,
+ .remove = usbssp_plat_remove,
+ .driver = {
+ .name = "usbssp-dev",
+ .pm = &usbssp_plat_pm_ops,
+ .of_match_table = of_match_ptr(usbssp_dev_of_match),
+ },
+};
+
+static int __init usbssp_plat_init(void)
+{
+ return platform_driver_register(&usbssp_driver);
+}
+module_init(usbssp_plat_init);
+
+static void __exit usbssp_plat_exit(void)
+{
+ platform_driver_unregister(&usbssp_driver);
+}
+module_exit(usbssp_plat_exit);
+
+MODULE_ALIAS("platform:usbss-gadget");
+MODULE_DESCRIPTION("USBSSP' Device Controller (USBSSP) Driver");
+MODULE_LICENSE("GPL v2");
--
2.17.1


2018-07-19 18:05:03

by Pawel Laszczak

[permalink] [raw]
Subject: [PATCH 14/31] usb: usbssp: added procedure handling command completion events.

This patch extends the usbssp_handle_event function with a new case,
that is responsible for servicing command completion events.

For this purpose, it adds handle_cmd_completion function which in turn
invokes some other functions depending of handling command type.

Signed-off-by: Pawel Laszczak <[email protected]>
---
drivers/usb/usbssp/gadget-mem.c | 53 +++-
drivers/usb/usbssp/gadget-ring.c | 510 +++++++++++++++++++++++++++++++
drivers/usb/usbssp/gadget.c | 11 +
drivers/usb/usbssp/gadget.h | 12 +
4 files changed, 584 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/usbssp/gadget-mem.c b/drivers/usb/usbssp/gadget-mem.c
index fef83b6b6cf0..5708a0090ead 100644
--- a/drivers/usb/usbssp/gadget-mem.c
+++ b/drivers/usb/usbssp/gadget-mem.c
@@ -504,6 +504,38 @@ void usbssp_free_container_ctx(struct usbssp_udc *usbssp_data,
kfree(ctx);
}

+struct usbssp_input_control_ctx *usbssp_get_input_control_ctx(
+ struct usbssp_container_ctx *ctx)
+{
+ if (ctx->type != USBSSP_CTX_TYPE_INPUT)
+ return NULL;
+
+ return (struct usbssp_input_control_ctx *)ctx->bytes;
+}
+
+struct usbssp_slot_ctx *usbssp_get_slot_ctx(struct usbssp_udc *usbssp_data,
+ struct usbssp_container_ctx *ctx)
+{
+ if (ctx->type == USBSSP_CTX_TYPE_DEVICE)
+ return (struct usbssp_slot_ctx *)ctx->bytes;
+
+ return (struct usbssp_slot_ctx *) (ctx->bytes +
+ CTX_SIZE(usbssp_data->hcc_params));
+}
+
+struct usbssp_ep_ctx *usbssp_get_ep_ctx(struct usbssp_udc *usbssp_data,
+ struct usbssp_container_ctx *ctx,
+ unsigned int ep_index)
+{
+ /* increment ep index by offset of start of ep ctx array */
+ ep_index++;
+ if (ctx->type == USBSSP_CTX_TYPE_INPUT)
+ ep_index++;
+
+ return (struct usbssp_ep_ctx *) (ctx->bytes +
+ (ep_index * CTX_SIZE(usbssp_data->hcc_params)));
+}
+
/***************** Streams structures manipulation *************************/
static void usbssp_free_stream_ctx(struct usbssp_udc *usbssp_data,
unsigned int num_stream_ctxs,
@@ -523,8 +555,25 @@ static void usbssp_free_stream_ctx(struct usbssp_udc *usbssp_data,
stream_ctx, dma);
}

-/**
- * Frees all stream contexts associated with the endpoint,
+struct usbssp_ring *usbssp_stream_id_to_ring(struct usbssp_device *dev,
+ unsigned int ep_index,
+ unsigned int stream_id)
+{
+ struct usbssp_ep *ep = &dev->eps[ep_index];
+
+ if (stream_id == 0)
+ return ep->ring;
+
+ if (!ep->stream_info)
+ return NULL;
+
+ if (stream_id > ep->stream_info->num_streams)
+ return NULL;
+ return ep->stream_info->stream_rings[stream_id];
+}
+
+/*
+ * Frees all stream contexts associated with the endpoint,
*
* Caller should fix the endpoint context streams fields.
*/
diff --git a/drivers/usb/usbssp/gadget-ring.c b/drivers/usb/usbssp/gadget-ring.c
index 6679d8ec7152..c704b939b48a 100644
--- a/drivers/usb/usbssp/gadget-ring.c
+++ b/drivers/usb/usbssp/gadget-ring.c
@@ -225,6 +225,107 @@ static bool usbssp_mod_cmd_timer(struct usbssp_udc *usbssp_data,
return 0;
}

+void usbssp_ring_ep_doorbell(struct usbssp_udc *usbssp_data,
+ unsigned int ep_index,
+ unsigned int stream_id)
+{
+ __le32 __iomem *db_addr =
+ &usbssp_data->dba->doorbell[usbssp_data->slot_id];
+ struct usbssp_ep *ep = &usbssp_data->devs.eps[ep_index];
+ unsigned int ep_state = ep->ep_state;
+ unsigned int db_value;
+
+ /*
+ * Don't ring the doorbell for this endpoint if there are pending
+ * cancellations because we don't want to interrupt processing.
+ * We don't want to restart any stream rings if there's a set dequeue
+ * pointer command pending because the device can choose to start any
+ * stream once the endpoint is on the HW schedule.
+ * Also we don't want restart any endpoint if endpoint is halted or
+ * disabled and also if endpoint disabling is pending.
+ */
+ if ((ep_state & EP_STOP_CMD_PENDING) ||
+ (ep_state & SET_DEQ_PENDING) ||
+ (ep_state & EP_HALTED) ||
+ !(ep_state & USBSSP_EP_ENABLED) ||
+ (ep_state & USBSSP_EP_DISABLE_PENDING))
+ return;
+
+ if (ep_index == 0 && !usbssp_data->ep0_expect_in &&
+ usbssp_data->ep0state == USBSSP_EP0_DATA_PHASE)
+ db_value = DB_VALUE_EP0_OUT(ep_index, stream_id);
+ else
+ db_value = DB_VALUE(ep_index, stream_id);
+
+ dev_dbg(usbssp_data->dev, "// Ding dong transfer ring for %s!"
+ " - [DB addr/DB val]: [%p/%08x]\n",
+ usbssp_data->devs.eps[ep_index].name, db_addr,
+ db_value);
+
+ writel(db_value, db_addr);
+ /*
+ * The CPU has better things to do at this point than wait for a
+ * write-posting flush. It'll get there soon enough.
+ */
+}
+
+/* Ring the doorbell for any rings with pending USB requests */
+static void ring_doorbell_for_active_rings(struct usbssp_udc *usbssp_data,
+ unsigned int ep_index)
+{
+ unsigned int stream_id;
+ struct usbssp_ep *ep;
+
+ ep = &usbssp_data->devs.eps[ep_index];
+
+ dev_dbg(usbssp_data->dev, "Ring all active ring for %s\n",
+ ep->name);
+
+ /* A ring has pending Request if its TD list is not empty */
+ if (!(ep->ep_state & EP_HAS_STREAMS)) {
+ if (ep->ring && !(list_empty(&ep->ring->td_list)))
+ usbssp_ring_ep_doorbell(usbssp_data, ep_index, 0);
+ return;
+ }
+
+ for (stream_id = 1; stream_id < ep->stream_info->num_streams;
+ stream_id++) {
+ struct usbssp_stream_info *stream_info = ep->stream_info;
+
+ if (!list_empty(&stream_info->stream_rings[stream_id]->td_list))
+ usbssp_ring_ep_doorbell(usbssp_data, ep_index,
+ stream_id);
+ }
+}
+
+/*
+ * When we get a command completion for a Stop Endpoint Command, we need to
+ * stop timer and clear EP_STOP_CMD_PENDING flag.
+ */
+static void usbssp_handle_cmd_stop_ep(struct usbssp_udc *usbssp_data,
+ union usbssp_trb *trb,
+ struct usbssp_event_cmd *event)
+{
+ unsigned int ep_index;
+ struct usbssp_ep *ep;
+ struct usbssp_ep_ctx *ep_ctx;
+ struct usbssp_device *priv_dev;
+
+ ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3]));
+ ep = &usbssp_data->devs.eps[ep_index];
+
+ dev_dbg(usbssp_data->dev,
+ "CMD stop endpoint completion for ep index: %d - %s\n",
+ ep_index, ep->name);
+
+
+ priv_dev = &usbssp_data->devs;
+ ep_ctx = usbssp_get_ep_ctx(usbssp_data, priv_dev->out_ctx, ep_index);
+ trace_usbssp_handle_cmd_stop_ep(ep_ctx);
+
+ ep->ep_state &= ~EP_STOP_CMD_PENDING;
+}
+
static void usbssp_kill_ring_requests(struct usbssp_udc *usbssp_data,
struct usbssp_ring *ring)
{
@@ -300,6 +401,262 @@ void usbssp_udc_died(struct usbssp_udc *usbssp_data)
usbssp_kill_endpoint_request(usbssp_data, i);
}

+static void update_ring_for_set_deq_completion(struct usbssp_udc *usbssp_data,
+ struct usbssp_device *dev,
+ struct usbssp_ring *ep_ring,
+ unsigned int ep_index)
+{
+ union usbssp_trb *dequeue_temp;
+ int num_trbs_free_temp;
+ bool revert = false;
+
+ num_trbs_free_temp = ep_ring->num_trbs_free;
+ dequeue_temp = ep_ring->dequeue;
+
+ if (trb_is_link(ep_ring->dequeue)) {
+ ep_ring->deq_seg = ep_ring->deq_seg->next;
+ ep_ring->dequeue = ep_ring->deq_seg->trbs;
+ }
+
+ while (ep_ring->dequeue != dev->eps[ep_index].queued_deq_ptr) {
+ /* We have more usable TRBs */
+ ep_ring->num_trbs_free++;
+ ep_ring->dequeue++;
+ if (trb_is_link(ep_ring->dequeue)) {
+ if (ep_ring->dequeue ==
+ dev->eps[ep_index].queued_deq_ptr)
+ break;
+ ep_ring->deq_seg = ep_ring->deq_seg->next;
+ ep_ring->dequeue = ep_ring->deq_seg->trbs;
+ }
+ if (ep_ring->dequeue == dequeue_temp) {
+ revert = true;
+ break;
+ }
+ }
+
+ if (revert) {
+ dev_dbg(usbssp_data->dev, "Unable to find new dequeue pointer\n");
+ ep_ring->num_trbs_free = num_trbs_free_temp;
+ }
+}
+
+/*
+ * When we get a completion for a Set Transfer Ring Dequeue Pointer command,
+ * we need to clear the set deq pending flag in the endpoint ring state, so that
+ * the TD queueing code can ring the doorbell again. We also need to ring the
+ * endpoint doorbell to restart the ring
+ */
+static void usbssp_handle_cmd_set_deq(struct usbssp_udc *usbssp_data,
+ union usbssp_trb *trb, u32 cmd_comp_code)
+{
+ unsigned int ep_index;
+ unsigned int stream_id;
+ struct usbssp_ring *ep_ring;
+ struct usbssp_device *dev;
+ struct usbssp_ep *ep;
+ struct usbssp_ep_ctx *ep_ctx;
+ struct usbssp_slot_ctx *slot_ctx;
+
+ ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3]));
+ stream_id = TRB_TO_STREAM_ID(le32_to_cpu(trb->generic.field[2]));
+ dev = &usbssp_data->devs;
+ ep = &dev->eps[ep_index];
+
+ ep_ring = usbssp_stream_id_to_ring(dev, ep_index, stream_id);
+ if (!ep_ring) {
+ dev_warn(usbssp_data->dev,
+ "WARN Set TR deq ptr command for freed stream ID %u\n",
+ stream_id);
+ goto cleanup;
+ }
+
+ ep_ctx = usbssp_get_ep_ctx(usbssp_data, dev->out_ctx, ep_index);
+ slot_ctx = usbssp_get_slot_ctx(usbssp_data, dev->out_ctx);
+ trace_usbssp_handle_cmd_set_deq(slot_ctx);
+ trace_usbssp_handle_cmd_set_deq_ep(ep_ctx);
+
+ if (cmd_comp_code != COMP_SUCCESS) {
+ unsigned int ep_state;
+ unsigned int slot_state;
+
+ switch (cmd_comp_code) {
+ case COMP_TRB_ERROR:
+ dev_warn(usbssp_data->dev,
+ "WARN Set TR Deq Ptr cmd invalid because of "
+ "stream ID configuration\n");
+ break;
+ case COMP_CONTEXT_STATE_ERROR:
+ dev_warn(usbssp_data->dev, "WARN Set TR Deq Ptr cmd "
+ "failed due to incorrect slot or ep state.\n");
+ ep_state = GET_EP_CTX_STATE(ep_ctx);
+ slot_state = le32_to_cpu(slot_ctx->dev_state);
+ slot_state = GET_SLOT_STATE(slot_state);
+ usbssp_dbg_trace(usbssp_data,
+ trace_usbssp_dbg_cancel_request,
+ "Slot state = %u, EP state = %u",
+ slot_state, ep_state);
+ break;
+ case COMP_SLOT_NOT_ENABLED_ERROR:
+ dev_warn(usbssp_data->dev,
+ "WARN Set TR Deq Ptr cmd failed because"
+ " slot %u was not enabled.\n",
+ usbssp_data->slot_id);
+ break;
+ default:
+ dev_warn(usbssp_data->dev, "WARN Set TR Deq Ptr cmd with"
+ " unknown completion code of %u.\n",
+ cmd_comp_code);
+ break;
+ }
+
+ } else {
+ u64 deq;
+ /* deq ptr is written to the stream ctx for streams */
+ if (ep->ep_state & EP_HAS_STREAMS) {
+ struct usbssp_stream_ctx *ctx =
+ &ep->stream_info->stream_ctx_array[stream_id];
+ deq = le64_to_cpu(ctx->stream_ring) & SCTX_DEQ_MASK;
+ } else {
+ deq = le64_to_cpu(ep_ctx->deq) & ~EP_CTX_CYCLE_MASK;
+ }
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_cancel_request,
+ "Successful Set TR Deq Ptr cmd, deq = @%08llx", deq);
+ if (usbssp_trb_virt_to_dma(ep->queued_deq_seg,
+ ep->queued_deq_ptr) == deq) {
+
+ /*
+ * Update the ring's dequeue segment and dequeue pointer
+ * to reflect the new position.
+ */
+ update_ring_for_set_deq_completion(usbssp_data, dev,
+ ep_ring, ep_index);
+ } else {
+ dev_warn(usbssp_data->dev,
+ "Mismatch between completed Set TR Deq "
+ "Ptr command & DC internal state.\n");
+ dev_warn(usbssp_data->dev,
+ "ep deq seg = %p, deq ptr = %p\n",
+ ep->queued_deq_seg, ep->queued_deq_ptr);
+ }
+ }
+
+cleanup:
+ dev->eps[ep_index].ep_state &= ~SET_DEQ_PENDING;
+ dev->eps[ep_index].queued_deq_seg = NULL;
+ dev->eps[ep_index].queued_deq_ptr = NULL;
+ /* Restart any rings with pending requests */
+ ring_doorbell_for_active_rings(usbssp_data, ep_index);
+}
+
+
+static void usbssp_handle_cmd_reset_ep(struct usbssp_udc *usbssp_data,
+ union usbssp_trb *trb,
+ u32 cmd_comp_code)
+{
+ struct usbssp_ep *dep;
+ struct usbssp_ep_ctx *ep_ctx;
+ unsigned int ep_index;
+
+ ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3]));
+ ep_ctx = usbssp_get_ep_ctx(usbssp_data, usbssp_data->devs.out_ctx,
+ ep_index);
+ trace_usbssp_handle_cmd_reset_ep(ep_ctx);
+
+ /*
+ * This command will only fail if the endpoint wasn't halted,
+ * but we don't care.
+ */
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_reset_ep,
+ "Ignoring reset ep completion code of %u",
+ cmd_comp_code);
+
+ dep = &usbssp_data->devs.eps[ep_index];
+
+ /* Clear our internal halted state */
+ dep->ep_state &= ~EP_HALTED;
+
+ ring_doorbell_for_active_rings(usbssp_data, ep_index);
+}
+
+static void usbssp_handle_cmd_enable_slot(struct usbssp_udc *usbssp_data,
+ int slot_id,
+ struct usbssp_command *command,
+ u32 cmd_comp_code)
+{
+ if (cmd_comp_code == COMP_SUCCESS) {
+ dev_dbg(usbssp_data->dev,
+ "CMD enable slot complition successfully "
+ "- slto id: %d\n", slot_id);
+ usbssp_data->slot_id = slot_id;
+ } else {
+ dev_dbg(usbssp_data->dev, "CMD enable slot complition failed\n");
+ usbssp_data->slot_id = 0;
+ }
+}
+
+static void usbssp_handle_cmd_disable_slot(struct usbssp_udc *usbssp_data)
+{
+ struct usbssp_device *dev_priv;
+ struct usbssp_slot_ctx *slot_ctx;
+
+ dev_dbg(usbssp_data->dev, "CMD disable slot complition\n");
+
+ dev_priv = &usbssp_data->devs;
+ if (!dev_priv)
+ return;
+
+ usbssp_data->slot_id = 0;
+ slot_ctx = usbssp_get_slot_ctx(usbssp_data, dev_priv->out_ctx);
+ trace_usbssp_handle_cmd_disable_slot(slot_ctx);
+}
+
+static void usbssp_handle_cmd_config_ep(struct usbssp_udc *usbssp_data,
+ struct usbssp_event_cmd *event,
+ u32 cmd_comp_code)
+{
+ struct usbssp_device *priv_dev;
+ struct usbssp_input_control_ctx *ctrl_ctx;
+ struct usbssp_ep_ctx *ep_ctx;
+ unsigned int ep_index;
+ u32 add_flags, drop_flags;
+
+ /*
+ * Configure endpoint commands can come, becaouse device
+ * receive USB_SET_CONFIGURATION or SET_INTERFACE request,
+ * or because the HW needed an extra configure endpoint
+ * command after a reset or disconnect event.
+ */
+ priv_dev = &usbssp_data->devs;
+ ctrl_ctx = usbssp_get_input_control_ctx(priv_dev->in_ctx);
+ if (!ctrl_ctx) {
+ dev_warn(usbssp_data->dev,
+ "Could not get input context, bad type.\n");
+ return;
+ }
+
+ add_flags = le32_to_cpu(ctrl_ctx->add_flags);
+ drop_flags = le32_to_cpu(ctrl_ctx->drop_flags);
+ /* Input ctx add_flags are the endpoint index plus one */
+ ep_index = usbssp_last_valid_endpoint(add_flags) - 1;
+
+ ep_ctx = usbssp_get_ep_ctx(usbssp_data, priv_dev->out_ctx, ep_index);
+ trace_usbssp_handle_cmd_config_ep(ep_ctx);
+}
+
+static void usbssp_handle_cmd_reset_dev(struct usbssp_udc *usbssp_data,
+ struct usbssp_event_cmd *event)
+{
+ struct usbssp_device *dev_priv;
+ struct usbssp_slot_ctx *slot_ctx;
+
+ dev_priv = &usbssp_data->devs;
+ slot_ctx = usbssp_get_slot_ctx(usbssp_data, dev_priv->out_ctx);
+ trace_usbssp_handle_cmd_reset_dev(slot_ctx);
+ dev_dbg(usbssp_data->dev, "Completed reset device command.\n");
+ if (!usbssp_data->devs.gadget)
+ dev_warn(usbssp_data->dev, "Reset device command completion\n");
+}

void usbssp_handle_command_timeout(struct work_struct *work)
{
@@ -327,6 +684,156 @@ void usbssp_cleanup_command_queue(struct usbssp_udc *usbssp_data)
usbssp_complete_del_and_free_cmd(cur_cmd, COMP_COMMAND_ABORTED);
}

+static void handle_cmd_completion(struct usbssp_udc *usbssp_data,
+ struct usbssp_event_cmd *event)
+{
+ int slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
+ u64 cmd_dma;
+ dma_addr_t cmd_dequeue_dma;
+ u32 cmd_comp_code;
+ union usbssp_trb *cmd_trb;
+ struct usbssp_command *cmd;
+ u32 cmd_type;
+
+ cmd_dma = le64_to_cpu(event->cmd_trb);
+ cmd_trb = usbssp_data->cmd_ring->dequeue;
+
+ trace_usbssp_handle_command(usbssp_data->cmd_ring, &cmd_trb->generic);
+
+ cmd_dequeue_dma = usbssp_trb_virt_to_dma(usbssp_data->cmd_ring->deq_seg,
+ cmd_trb);
+
+ /*
+ * Check whether the completion event is for our internal kept
+ * command.
+ */
+ if (!cmd_dequeue_dma || cmd_dma != (u64)cmd_dequeue_dma) {
+ dev_warn(usbssp_data->dev,
+ "ERROR mismatched command completion event\n");
+ return;
+ }
+
+ cmd = list_entry(usbssp_data->cmd_list.next, struct usbssp_command,
+ cmd_list);
+
+ cancel_delayed_work(&usbssp_data->cmd_timer);
+
+ cmd_comp_code = GET_COMP_CODE(le32_to_cpu(event->status));
+
+ /* If CMD ring stopped we own the trbs between enqueue and dequeue */
+ if (cmd_comp_code == COMP_COMMAND_RING_STOPPED) {
+ complete_all(&usbssp_data->cmd_ring_stop_completion);
+ return;
+ }
+
+ if (cmd->command_trb != usbssp_data->cmd_ring->dequeue) {
+ dev_err(usbssp_data->dev,
+ "Command completion event does not match command\n");
+ return;
+ }
+
+ /*
+ * device aborted the command ring, check if the current command was
+ * supposed to be aborted, otherwise continue normally.
+ * The command ring is stopped now, but the DC will issue a Command
+ * Ring Stopped event which will cause us to restart it.
+ */
+ if (cmd_comp_code == COMP_COMMAND_ABORTED) {
+ usbssp_data->cmd_ring_state = CMD_RING_STATE_STOPPED;
+
+ if (cmd->status == COMP_COMMAND_ABORTED) {
+ if (usbssp_data->current_cmd == cmd)
+ usbssp_data->current_cmd = NULL;
+ goto event_handled;
+ }
+ }
+
+ cmd_type = TRB_FIELD_TO_TYPE(le32_to_cpu(cmd_trb->generic.field[3]));
+ switch (cmd_type) {
+ case TRB_ENABLE_SLOT:
+ usbssp_handle_cmd_enable_slot(usbssp_data, slot_id,
+ cmd, cmd_comp_code);
+ break;
+ case TRB_DISABLE_SLOT:
+ usbssp_handle_cmd_disable_slot(usbssp_data);
+ break;
+ case TRB_CONFIG_EP:
+ if (!cmd->completion)
+ usbssp_handle_cmd_config_ep(usbssp_data, event,
+ cmd_comp_code);
+ break;
+ case TRB_EVAL_CONTEXT:
+ break;
+ case TRB_ADDR_DEV: {
+ struct usbssp_slot_ctx *slot_ctx;
+
+ slot_ctx = usbssp_get_slot_ctx(usbssp_data,
+ usbssp_data->devs.out_ctx);
+ trace_usbssp_handle_cmd_addr_dev(slot_ctx);
+ break;
+ }
+ case TRB_STOP_RING:
+ WARN_ON(slot_id != TRB_TO_SLOT_ID(
+ le32_to_cpu(cmd_trb->generic.field[3])));
+ usbssp_handle_cmd_stop_ep(usbssp_data, cmd_trb, event);
+ break;
+ case TRB_SET_DEQ:
+ WARN_ON(slot_id != TRB_TO_SLOT_ID(
+ le32_to_cpu(cmd_trb->generic.field[3])));
+ usbssp_handle_cmd_set_deq(usbssp_data, cmd_trb, cmd_comp_code);
+ break;
+ case TRB_CMD_NOOP:
+ /* Is this an aborted command turned to NO-OP? */
+ if (cmd->status == COMP_COMMAND_RING_STOPPED)
+ cmd_comp_code = COMP_COMMAND_RING_STOPPED;
+ break;
+ case TRB_HALT_ENDPOINT:
+ if (cmd->status == COMP_COMMAND_RING_STOPPED)
+ cmd_comp_code = COMP_COMMAND_RING_STOPPED;
+ break;
+ case TRB_FLUSH_ENDPOINT:
+ if (cmd->status == COMP_COMMAND_RING_STOPPED)
+ cmd_comp_code = COMP_COMMAND_RING_STOPPED;
+ break;
+ case TRB_RESET_EP:
+ WARN_ON(slot_id != TRB_TO_SLOT_ID(
+ le32_to_cpu(cmd_trb->generic.field[3])));
+ usbssp_handle_cmd_reset_ep(usbssp_data, cmd_trb, cmd_comp_code);
+ break;
+ case TRB_RESET_DEV:
+ /*
+ * SLOT_ID field in reset device cmd completion event TRB is 0.
+ * Use the SLOT_ID from the command TRB instead.
+ */
+ slot_id = TRB_TO_SLOT_ID(
+ le32_to_cpu(cmd_trb->generic.field[3]));
+
+ WARN_ON(slot_id != 0);
+ usbssp_handle_cmd_reset_dev(usbssp_data, event);
+ break;
+ case TRB_FORCE_HEADER:
+ break;
+ default:
+ /* Skip over unknown commands on the event ring */
+ dev_info(usbssp_data->dev, "INFO unknown command type %d\n",
+ cmd_type);
+ break;
+ }
+
+ /* restart timer if this wasn't the last command */
+ if (!list_is_singular(&usbssp_data->cmd_list)) {
+ usbssp_data->current_cmd = list_first_entry(&cmd->cmd_list,
+ struct usbssp_command, cmd_list);
+ usbssp_mod_cmd_timer(usbssp_data, USBSSP_CMD_DEFAULT_TIMEOUT);
+ } else if (usbssp_data->current_cmd == cmd) {
+ usbssp_data->current_cmd = NULL;
+ }
+
+event_handled:
+ usbssp_complete_del_and_free_cmd(cmd, cmd_comp_code);
+ inc_deq(usbssp_data, usbssp_data->cmd_ring);
+}
+
static void handle_port_status(struct usbssp_udc *usbssp_data,
union usbssp_trb *event)
{
@@ -596,6 +1103,9 @@ int usbssp_handle_event(struct usbssp_udc *usbssp_data)
rmb();

switch ((le32_to_cpu(event->event_cmd.flags) & TRB_TYPE_BITMASK)) {
+ case TRB_TYPE(TRB_COMPLETION):
+ handle_cmd_completion(usbssp_data, &event->event_cmd);
+ break;
case TRB_TYPE(TRB_PORT_STATUS):
handle_port_status(usbssp_data, event);
update_ptrs = 0;
diff --git a/drivers/usb/usbssp/gadget.c b/drivers/usb/usbssp/gadget.c
index 2996b1d3baf7..6b3dc973c0d9 100644
--- a/drivers/usb/usbssp/gadget.c
+++ b/drivers/usb/usbssp/gadget.c
@@ -262,6 +262,17 @@ int usbssp_resume(struct usbssp_udc *usbssp_data, bool hibernated)

#endif /* CONFIG_PM */

+/* Compute the last valid endpoint context index. Basically, this is the
+ * endpoint index plus one. For slot contexts with more than valid endpoint,
+ * we find the most significant bit set in the added contexts flags.
+ * e.g. ep 1 IN (with epnum 0x81) => added_ctxs = 0b1000
+ * fls(0b1000) = 4, but the endpoint context index is 3, so subtract one.
+ */
+unsigned int usbssp_last_valid_endpoint(u32 added_ctxs)
+{
+ return fls(added_ctxs) - 1;
+}
+
int usbssp_gen_setup(struct usbssp_udc *usbssp_data)
{
int retval;
diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h
index 33eccffc885d..145371eee47d 100644
--- a/drivers/usb/usbssp/gadget.h
+++ b/drivers/usb/usbssp/gadget.h
@@ -1685,9 +1685,13 @@ void usbssp_dbg_trace(struct usbssp_udc *usbssp_data,
/* USBSSP memory management */
void usbssp_mem_cleanup(struct usbssp_udc *usbssp_data);
int usbssp_mem_init(struct usbssp_udc *usbssp_data, gfp_t flags);
+unsigned int usbssp_last_valid_endpoint(u32 added_ctxs);
int usbssp_ring_expansion(struct usbssp_udc *usbssp_data,
struct usbssp_ring *ring,
unsigned int num_trbs, gfp_t flags);
+struct usbssp_ring *usbssp_stream_id_to_ring(struct usbssp_device *dev,
+ unsigned int ep_index,
+ unsigned int stream_id);

struct usbssp_command *usbssp_alloc_command(struct usbssp_udc *usbssp_data,
bool allocate_completion,
@@ -1732,6 +1736,14 @@ void usbssp_set_link_state(struct usbssp_udc *usbssp_data,
void usbssp_test_and_clear_bit(struct usbssp_udc *usbssp_data,
__le32 __iomem *port_regs, u32 port_bit);

+/* USBSSP DC contexts */
+struct usbssp_input_control_ctx *usbssp_get_input_control_ctx(
+ struct usbssp_container_ctx *ctx);
+struct usbssp_slot_ctx *usbssp_get_slot_ctx(struct usbssp_udc *usbssp_data,
+ struct usbssp_container_ctx *ctx);
+struct usbssp_ep_ctx *usbssp_get_ep_ctx(struct usbssp_udc *usbssp_data,
+ struct usbssp_container_ctx *ctx,
+ unsigned int ep_index);
/* USBSSP gadget interface*/
void usbssp_suspend_gadget(struct usbssp_udc *usbssp_data);
void usbssp_resume_gadget(struct usbssp_udc *usbssp_data);
--
2.17.1


2018-07-19 18:05:18

by Pawel Laszczak

[permalink] [raw]
Subject: [PATCH 11/31] usb: usbssp: added function for stopping driver.

This function is called by the gadget core when the USBSSP driver
is removed. Disable device contexts, disable IRQs, and quiesce the DC.
Reset the DC, finish any completed transactions, and cleanup memory.

Signed-off-by: Pawel Laszczak <[email protected]>
---
drivers/usb/usbssp/gadget.c | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)

diff --git a/drivers/usb/usbssp/gadget.c b/drivers/usb/usbssp/gadget.c
index 915983bc400f..2996b1d3baf7 100644
--- a/drivers/usb/usbssp/gadget.c
+++ b/drivers/usb/usbssp/gadget.c
@@ -200,6 +200,41 @@ int usbssp_init(struct usbssp_udc *usbssp_data)
return retval;
}

+/*
+ * Stop USBSSP controller.
+ *
+ * This function is called by the gadget core when the USBSSP driver is removed.
+ * Its opposite is usbssp_run().
+ *
+ * Disable device contexts, disable IRQs, and quiesce the DC.
+ * Reset the DC, finish any completed transactions, and cleanup memory.
+ */
+void usbssp_stop(struct usbssp_udc *usbssp_data)
+{
+ u32 temp;
+
+ spin_lock_irq(&usbssp_data->lock);
+ usbssp_data->usbssp_state |= USBSSP_STATE_HALTED;
+ usbssp_data->cmd_ring_state = CMD_RING_STATE_STOPPED;
+ usbssp_halt(usbssp_data);
+ usbssp_reset(usbssp_data);
+ spin_unlock_irq(&usbssp_data->lock);
+
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
+ "// Disabling event ring interrupts");
+ temp = readl(&usbssp_data->op_regs->status);
+ writel((temp & ~0x1fff) | STS_EINT, &usbssp_data->op_regs->status);
+ temp = readl(&usbssp_data->ir_set->irq_pending);
+ writel(ER_IRQ_DISABLE(temp), &usbssp_data->ir_set->irq_pending);
+
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
+ "cleaning up memory");
+ usbssp_mem_cleanup(usbssp_data);
+ usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
+ "usbssp_stop completed - status = %x",
+ readl(&usbssp_data->op_regs->status));
+}
+
#ifdef CONFIG_PM
/*
* Stop DC (not bus-specific)
--
2.17.1


2018-08-01 11:29:47

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH 00/31] Introduced new Cadence USBSSP DRD Driver

Hi Pawel,

On 19/07/18 20:57, Pawel Laszczak wrote:
> This patch set introduce new Cadence USBSSP DRD driver
> to linux kernel.
>
> The Cadence USBSSP DRD Driver s a highly
> configurable IP Core which can be
> instantiated as Dual-Role Device (DRD),
> Peripheral Only and Host Only (XHCI)
> configurations.
>
> The current driver has been validated with
> FPGA burned. We have support for PCIe
> bus, which is used on FPGA prototyping.
>
> The host site of USBSSP controller is compliance
> with XHCI specification, so it works with
> standard XHCI linux driver.
>
> Also, device side of USBSSP controller was designed
> in such way to looks like XHCI. It means that
> most of logic of USBSSP controller is also compliance
> with XHCI specification.
>
> Consequently, the USBSSP driver for peripheral mode
> is very similar to XHCI driver.
>
> This version of driver supports only Device mode but
> DRD and Host mode will be added in the future.
>

Based on the posting date this series looks like v3.

You should add the version prefix to the patches next time.
e.g. [PATCH v4 ...]

It is also recommended to add a Change-log in the cover letter
explaining what changes were made in each revision.

There are a bunch of checkpatch warnings in this series.
You can use ./scripts/checkpatch.pl on the patches and fix the
issues.

> ---
>
> Pawel Laszczak (31):
> usb: usbssp: Defined register maps and other useful structures.
> usb: usbssp: Added some decoding functions.
> usb: usbssp: Add trace events used in driver
> usb: usbssp: Added USBSSP platform driver
> usb: usbssp: Added first part of initialization sequence.
> usb: usbssp: added template functions used by upper layer.
> usb: usbssp: Initialization - added usbssp_mem_init
> usb: usbssp: Added ring and segment handling functions.
> usb: usbssp: add implementation of usbssp_mem_cleanup
> usb: usbssp: added usbssp_trb_in_td function.
> usb: usbssp: added function for stopping driver.
> usb: usbssp: added functions for queuing commands.
> usb: usbssp: addec procedure for handlin Port Status Change events.
> usb: usbssp: added procedure handling command completion events.
> usb: usbssp: added device controller error, transfer and SETUP
> completion event.
> usb: usbssp: added connect/disconnect procedures.
> usb: usbssp: added implementation of usbssp_halt_endpoint function.
> usb: usbssp: added handling of Port Reset event.
> usb: usbssp: added support for USB enumeration process.
> usb: usbssp: added queuing procedure for control transfer.
> usb: usbssp: added queuing procedure for BULK and INT transfer.
> usb: usbssp: added procedure removing request from transfer ring
> usb: usbssp: added implementation of transfer events.
> usb: usbssp: added detecting command timeout.
> usb: usbssp: added implementation of usbssp interface.
> usb: usbssp: added endpoint configuration functionality.
> usb: usbssp: implements usbssp_gadget_ep_enable function
> usb: usbssp: implemented usbssp_gadget_ep_disable function.
> usb: usbssp: added support for LPM.
> usb: usbssp: added support for TEST_MODE.
> usb: usbssp: add pci to platform driver wrapper.
>
> drivers/usb/Kconfig | 2 +
> drivers/usb/Makefile | 2 +
> drivers/usb/usbssp/Kconfig | 29 +
> drivers/usb/usbssp/Makefile | 15 +
> drivers/usb/usbssp/gadget-dbg.c | 39 +
> drivers/usb/usbssp/gadget-ep0.c | 571 +++++
> drivers/usb/usbssp/gadget-ext-caps.h | 102 +
> drivers/usb/usbssp/gadget-if.c | 569 +++++
> drivers/usb/usbssp/gadget-mem.c | 1938 +++++++++++++++
> drivers/usb/usbssp/gadget-port.c | 287 +++
> drivers/usb/usbssp/gadget-ring.c | 3452 ++++++++++++++++++++++++++
> drivers/usb/usbssp/gadget-trace.c | 13 +
> drivers/usb/usbssp/gadget-trace.h | 482 ++++
> drivers/usb/usbssp/gadget.c | 1909 ++++++++++++++
> drivers/usb/usbssp/gadget.h | 2373 ++++++++++++++++++
> drivers/usb/usbssp/usbssp-pci-wrap.c | 226 ++
> drivers/usb/usbssp/usbssp-plat.c | 186 ++
> 17 files changed, 12195 insertions(+)
> create mode 100644 drivers/usb/usbssp/Kconfig
> create mode 100644 drivers/usb/usbssp/Makefile
> create mode 100644 drivers/usb/usbssp/gadget-dbg.c
> create mode 100644 drivers/usb/usbssp/gadget-ep0.c
> create mode 100644 drivers/usb/usbssp/gadget-ext-caps.h
> create mode 100644 drivers/usb/usbssp/gadget-if.c
> create mode 100644 drivers/usb/usbssp/gadget-mem.c
> create mode 100644 drivers/usb/usbssp/gadget-port.c
> create mode 100644 drivers/usb/usbssp/gadget-ring.c
> create mode 100644 drivers/usb/usbssp/gadget-trace.c
> create mode 100644 drivers/usb/usbssp/gadget-trace.h
> create mode 100644 drivers/usb/usbssp/gadget.c
> create mode 100644 drivers/usb/usbssp/gadget.h
> create mode 100644 drivers/usb/usbssp/usbssp-pci-wrap.c
> create mode 100644 drivers/usb/usbssp/usbssp-plat.c
>

--
cheers,
-roger

Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki

2018-08-01 12:25:54

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH 04/31] usb: usbssp: Added USBSSP platform driver

Hi,

On 19/07/18 20:57, Pawel Laszczak wrote:
> This patch adds platform driver that is entry point for loading and
> unloading usbssp.ko modules.
> It also adds information about this driver to drivers/usb/Kconfig
> and drivers/usb/Makefile files and create Kconfig and Makefile
> files in drivers/usb/usbssp directory.
>
> Patch also adds template for some function ivokked from

s/ivokked/invoked

> usbssp_plat.c file. These function will be implemented in next patches.
>
> This patch also introduce usbssp_trb_virt_to_dma that converts
> virtual address of TRB's to DMA address. In this moment this
> function is used only in gadget-trace.h.

s/"In this moment"/"At the moment"

>
> From this moment the driver can be compiled.
>
> Signed-off-by: Pawel Laszczak <[email protected]>
> ---
> drivers/usb/Kconfig | 2 +
> drivers/usb/Makefile | 2 +
> drivers/usb/usbssp/Kconfig | 21 ++++
> drivers/usb/usbssp/Makefile | 11 ++
> drivers/usb/usbssp/gadget-ring.c | 48 ++++++++
> drivers/usb/usbssp/gadget.c | 64 +++++++++++
> drivers/usb/usbssp/gadget.h | 16 ++-
> drivers/usb/usbssp/usbssp-plat.c | 186 +++++++++++++++++++++++++++++++
> 8 files changed, 349 insertions(+), 1 deletion(-)
> create mode 100644 drivers/usb/usbssp/Kconfig
> create mode 100644 drivers/usb/usbssp/Makefile
> create mode 100644 drivers/usb/usbssp/gadget-ring.c
> create mode 100644 drivers/usb/usbssp/gadget.c
> create mode 100644 drivers/usb/usbssp/usbssp-plat.c
>

Build fails at this patch with error [1]. Building should never fail at any patch in the series.


> diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
> index f699abab1787..dc05f384c34c 100644
> --- a/drivers/usb/Kconfig
> +++ b/drivers/usb/Kconfig
> @@ -110,6 +110,8 @@ source "drivers/usb/mtu3/Kconfig"
>
> source "drivers/usb/musb/Kconfig"
>
> +source "drivers/usb/usbssp/Kconfig"
> +
> source "drivers/usb/dwc3/Kconfig"
>
> source "drivers/usb/dwc2/Kconfig"
> diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
> index 060643a1b5c8..b1cd5f83d440 100644
> --- a/drivers/usb/Makefile
> +++ b/drivers/usb/Makefile
> @@ -8,6 +8,8 @@
> obj-$(CONFIG_USB) += core/
> obj-$(CONFIG_USB_SUPPORT) += phy/
>
> +obj-$(CONFIG_USB_USBSSP) += usbssp/
> +
> obj-$(CONFIG_USB_DWC3) += dwc3/
> obj-$(CONFIG_USB_DWC2) += dwc2/
> obj-$(CONFIG_USB_ISP1760) += isp1760/
> diff --git a/drivers/usb/usbssp/Kconfig b/drivers/usb/usbssp/Kconfig
> new file mode 100644
> index 000000000000..ee20b01753dc
> --- /dev/null
> +++ b/drivers/usb/usbssp/Kconfig
> @@ -0,0 +1,21 @@
> +config USB_USBSSP

Do you want to choose a better Kconfig symbol name? USB is repeated twice
in USB_USBSSP.

I'd recommend to add something signifying Cadence in the symbol

some examples

USB_CADSSP, USB_CSSP

> + tristate "Cadence USBSSP DRD Controller"
> + depends on (USB || USB_GADGET) && HAS_DMA
> + select USB_USBSSP_GADGET

Not good to select a symbol that has dependencies.

> + help
> + Say Y here if your system has a cadence USBSSP dual-role controller.
> + It supports: dual-role switch Host-only, and Peripheral-only.
> +
> + If you choose to build this driver is a dynamically linked
> + module, the module will be called usbssp.ko.
> +
> +if USB_USBSSP
> +
> +config USB_USBSSP_GADGET
> + tristate "Gadget only mode"
> + default USB_USBSSP
> + depends on USB_GADGET=y || USB_GADGET=USB_USBSSP
> + help
> + Select this when you want to use USBSSP in gadget mode only,

s/,/.

> +endif
> +
> diff --git a/drivers/usb/usbssp/Makefile b/drivers/usb/usbssp/Makefile
> new file mode 100644
> index 000000000000..d85f15afb51c
> --- /dev/null
> +++ b/drivers/usb/usbssp/Makefile
> @@ -0,0 +1,11 @@
> +# SPDX-License-Identifier: GPL-2.0
> +# define_trace.h needs to know how to find our header
> +CFLAGS_gadget-trace.o := -I$(src)
> +
> +obj-$(CONFIG_USB_USBSSP_GADGET) += usbssp.o
> +usbssp-y := usbssp-plat.o gadget-ring.o \
> + gadget.o
> +
> +ifneq ($(CONFIG_TRACING),)
> + usbssp-y += gadget-trace.o
> +endif
> diff --git a/drivers/usb/usbssp/gadget-ring.c b/drivers/usb/usbssp/gadget-ring.c
> new file mode 100644
> index 000000000000..d1da59306d02
> --- /dev/null
> +++ b/drivers/usb/usbssp/gadget-ring.c
> @@ -0,0 +1,48 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * USBSSP device controller driver
> + *
> + * Copyright (C) 2018 Cadence.
> + *
> + * Author: Pawel Laszczak
> + *
> + * A lot of code based on Linux XHCI driver.
> + * Origin: Copyright (C) 2008 Intel Corp
> + */
> +
> +#include <linux/scatterlist.h>
> +#include <linux/slab.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/irq.h>
> +#include "gadget-trace.h"
> +#include "gadget.h"
> +
> +/*
> + * Returns zero if the TRB isn't in this segment, otherwise it returns the DMA
> + * address of the TRB.
> + */
> +dma_addr_t usbssp_trb_virt_to_dma(struct usbssp_segment *seg,
> + union usbssp_trb *trb)
> +{
> + unsigned long segment_offset;
> +
> + if (!seg || !trb || trb < seg->trbs)
> + return 0;
> + /* offset in TRBs */
> + segment_offset = trb - seg->trbs;
> + if (segment_offset >= TRBS_PER_SEGMENT)
> + return 0;
> + return seg->dma + (segment_offset * sizeof(*trb));
> +}
> +
> +irqreturn_t usbssp_irq(int irq, void *priv)
> +{
> + struct usbssp_udc *usbssp_data = (struct usbssp_udc *)priv;
> + irqreturn_t ret = IRQ_NONE;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&usbssp_data->lock, flags);
> +
> + spin_unlock_irqrestore(&usbssp_data->lock, flags);
> + return ret;
> +}
> diff --git a/drivers/usb/usbssp/gadget.c b/drivers/usb/usbssp/gadget.c
> new file mode 100644
> index 000000000000..2f60d7dd1fe4
> --- /dev/null
> +++ b/drivers/usb/usbssp/gadget.c
> @@ -0,0 +1,64 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * USBSSP device controller driver
> + *
> + * Copyright (C) 2018 Cadence.
> + *
> + * Author: Pawel Laszczak
> + *
> + * A lot of code based on Linux XHCI driver.
> + * Origin: Copyright (C) 2008 Intel Corp
> + */
> +
> +#include <linux/pci.h>
> +#include <linux/irq.h>
> +#include <linux/log2.h>
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/slab.h>
> +#include <linux/dmi.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/delay.h>
> +
> +#include "gadget-trace.h"
> +#include "gadget.h"
> +
> +#ifdef CONFIG_PM
> +/*
> + * Stop DC (not bus-specific)
> + *
> + * This is called when the machine transition into S3/S4 mode.
> + *
> + */
> +int usbssp_suspend(struct usbssp_udc *usbssp_data, bool do_wakeup)
> +{
> + /*TODO*/
> + return -ENOSYS;
> +}
> +
> +/*
> + * start DC (not bus-specific)
> + *
> + * This is called when the machine transition from S3/S4 mode.
> + *
> + */
> +int usbssp_resume(struct usbssp_udc *usbssp_data, bool hibernated)
> +{
> + /*TODO*/
> + return -ENOSYS;
> +}
> +
> +#endif /* CONFIG_PM */
> +
> +int usbssp_gadget_init(struct usbssp_udc *usbssp_data)
> +{
> + int ret;
> + return ret;
ret is not initialized before returning.
Maybe just
return 0;

> +}
> +
> +int usbssp_gadget_exit(struct usbssp_udc *usbssp_data)
> +{
> + int ret = 0;
> +
> + return ret;

return 0;

> +}
> diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h
> index b5c17603af78..55e20795d900 100644
> --- a/drivers/usb/usbssp/gadget.h
> +++ b/drivers/usb/usbssp/gadget.h
> @@ -9,7 +9,6 @@
> * A lot of code based on Linux XHCI driver.
> * Origin: Copyright (C) 2008 Intel Corp.
> */
> -

unnecessary blank line removal

> #ifndef __LINUX_USBSSP_GADGET_H
> #define __LINUX_USBSSP_GADGET_H
>
> @@ -1676,6 +1675,21 @@ static inline void usbssp_write_64(struct usbssp_udc *usbssp_data,
> {
> lo_hi_writeq(val, regs);
> }
> +
> +/* USBSSP Device controller glue */
> +int usbssp_suspend(struct usbssp_udc *usbssp_data, bool do_wakeup);
> +int usbssp_resume(struct usbssp_udc *usbssp_data, bool hibernated);
> +
> +irqreturn_t usbssp_irq(int irq, void *priv);
> +
> +/* USBSSP ring, segment, TRB, and TD functions */
> +dma_addr_t usbssp_trb_virt_to_dma(struct usbssp_segment *seg,
> + union usbssp_trb *trb);
> +
> +/* USBSSP gadget interface*/
> +int usbssp_gadget_init(struct usbssp_udc *usbssp_data);
> +int usbssp_gadget_exit(struct usbssp_udc *usbssp_data);
> +
> static inline char *usbssp_slot_state_string(u32 state)
> {
> switch (state) {
> diff --git a/drivers/usb/usbssp/usbssp-plat.c b/drivers/usb/usbssp/usbssp-plat.c

Is this file meant only for gadget controller or later even for host controller?
If only for gadget then this could be just called gadget-plat.c

> new file mode 100644
> index 000000000000..c048044148aa
> --- /dev/null
> +++ b/drivers/usb/usbssp/usbssp-plat.c
> @@ -0,0 +1,186 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * USBSSP device controller driver
> + *
> + * Copyright (C) 2018 Cadence.
> + *
> + * Author: Pawel Laszczak
> + *
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/module.h>
> +#include <linux/pci.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/usb/phy.h>
> +#include <linux/slab.h>
> +#include <linux/acpi.h>
> +
> +#include "gadget.h"
> +
> +#define DRIVER_AUTHOR "Pawel Laszczak"
> +#define DRIVER_DESC "USBSSP Device Controller (USBSSP) Driver"
> +
> +#ifdef CONFIG_OF
> +
> +static const struct of_device_id usbssp_dev_of_match[] = {
> + {
> + .compatible = "Cadence, usbssp-dev",

Avoid upper-case in compatible strings.

> + },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, usbssp_dev_of_match);
> +#endif
> +
> +int usbssp_is_platform(void)
> +{
> + return 1;
> +}
> +
> +static int usbssp_plat_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct resource *res;
> + struct usbssp_udc *usbssp_data;
> + int ret = 0;
> + int irq;
> + struct device *sysdev;
> +
> + irq = platform_get_irq(pdev, 0);
> + if (irq < 0) {
> + dev_err(&pdev->dev, "Incorrect IRQ number\n");

IRQ number might be correct but might be some other issue.
You could just say "couldn't get IRQ"

> + return -ENODEV;

Also, we don't want to print any error message if we got a -EPROBE_DEFER.
And we need to return that instead of -ENODEV for deferred probing to work.

> + }

So how about
if (irq < 0) {
if (irq != -EPROBE_DEFER)
dev_err(&pdev->dev, "couldn't get IRQ\n")

return irq;
}

> +
> + usbssp_data = devm_kzalloc(dev, sizeof(*usbssp_data), GFP_KERNEL);
> + if (!usbssp_data)
> + return -ENOMEM;
> +
> + for (sysdev = &pdev->dev; sysdev; sysdev = sysdev->parent) {
> + if (is_of_node(sysdev->fwnode) ||
> + is_acpi_device_node(sysdev->fwnode))
> + break;
> +#ifdef CONFIG_PCI
> + else if (sysdev->bus == &pci_bus_type)
> + break;
> +#endif
> + }

It is hard to understand what is this for loop doing exactly.

xhci-plat.c seems to have this comment. You should add it above as well.
/*
* sysdev must point to a device that is known to the system firmware
* or PCI hardware. We handle these three cases here:
* 1. xhci_plat comes from firmware
* 2. xhci_plat is child of a device from firmware (dwc3-plat)
* 3. xhci_plat is grandchild of a pci device (dwc3-pci)
*/


> +
> + if (!sysdev)
> + sysdev = &pdev->dev;
> +
> + /* Try to set 64-bit DMA first */
> + if (WARN_ON(!dev->dma_mask))
> + /* Platform did not initialize dma_mask */
> + ret = dma_coerce_mask_and_coherent(dev,
> + DMA_BIT_MASK(64));
> + else
> + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
> +
> + /* If seting 64-bit DMA mask fails, fall back to 32-bit DMA mask */
> + if (ret) {
> + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
> + if (ret)
> + return ret;
> + }
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + usbssp_data->regs = devm_ioremap_resource(dev, res);
> +
> + if (IS_ERR(usbssp_data->regs)) {
dev_err() ?

> + ret = PTR_ERR(usbssp_data->regs);
> + return ret;
> + }
> +
> + usbssp_data->rsrc_start = res->start;
> + usbssp_data->rsrc_len = resource_size(res);
> +
> + ret = devm_request_irq(dev, irq, usbssp_irq, IRQF_SHARED,
> + dev_name(dev), usbssp_data);

devm_request_threaded_irq() ?

> +
> + if (ret < 0)
> + return ret;
> +
> + usbssp_data->irq = irq;
> + usbssp_data->dev = dev;
> + platform_set_drvdata(pdev, usbssp_data);
> + ret = usbssp_gadget_init(usbssp_data);
> +
> + return ret;
> +}
> +
> +static int usbssp_plat_remove(struct platform_device *pdev)
> +{
> + int ret = 0;
> + struct usbssp_udc *usbssp_data;
> +
> + usbssp_data = (struct usbssp_udc *)platform_get_drvdata(pdev);
> + ret = usbssp_gadget_exit(usbssp_data);
> + return ret;
> +

move this blank line above return ret;
> +}
> +
> +static int __maybe_unused usbssp_plat_suspend(struct device *dev)
> +{
> + struct usbssp_udc *usbssp_data = dev_get_drvdata(dev);
> +
> + return usbssp_suspend(usbssp_data, device_may_wakeup(dev));
> +}
> +
> +static int __maybe_unused usbssp_plat_resume(struct device *dev)
> +{
> + struct usbssp_udc *usbssp_data = dev_get_drvdata(dev);
> +
> + return usbssp_resume(usbssp_data, 0);
> +}
> +
> +static int __maybe_unused usbssp_plat_runtime_suspend(struct device *dev)
> +{
> + struct usbssp_udc *usbssp_data = dev_get_drvdata(dev);
> +
> + return usbssp_suspend(usbssp_data, true);
> +}
> +
> +static int __maybe_unused usbssp_plat_runtime_resume(struct device *dev)
> +{
> + struct usbssp_udc *usbssp_data = dev_get_drvdata(dev);
> +
> + return usbssp_resume(usbssp_data, 0);
> +}
> +
> +static const struct dev_pm_ops usbssp_plat_pm_ops = {
> + SET_SYSTEM_SLEEP_PM_OPS(usbssp_plat_suspend, usbssp_plat_resume)
> +
> + SET_RUNTIME_PM_OPS(usbssp_plat_runtime_suspend,
> + usbssp_plat_runtime_resume,
> + NULL)
> +};
> +
> +static struct platform_driver usbssp_driver = {
> + .probe = usbssp_plat_probe,
> + .remove = usbssp_plat_remove,
> + .driver = {
> + .name = "usbssp-dev",
> + .pm = &usbssp_plat_pm_ops,
> + .of_match_table = of_match_ptr(usbssp_dev_of_match),
> + },
> +};
> +
> +static int __init usbssp_plat_init(void)
> +{
> + return platform_driver_register(&usbssp_driver);
> +}
> +module_init(usbssp_plat_init);
> +
> +static void __exit usbssp_plat_exit(void)
> +{
> + platform_driver_unregister(&usbssp_driver);
> +}
> +module_exit(usbssp_plat_exit);
> +
> +MODULE_ALIAS("platform:usbss-gadget");
usbssp-gadget?

Why did you choose a different name for compatible? "usbssp-dev"
Would be nice to have it consistent.

> +MODULE_DESCRIPTION("USBSSP' Device Controller (USBSSP) Driver");

USBSSP, 2 times?

> +MODULE_LICENSE("GPL v2");
>


[1] build error

CC [M] drivers/usb/usbssp/gadget-trace.o
In file included from drivers/usb/usbssp/gadget-trace.h:27:0,
from drivers/usb/usbssp/gadget-trace.c:13:
drivers/usb/usbssp/gadget.h:1683:1: error: unknown type name ‘irqreturn_t’
irqreturn_t usbssp_irq(int irq, void *priv);
^~~~~~~~~~~
In file included from ./include/trace/trace_events.h:394:0,
from ./include/trace/define_trace.h:96,
from drivers/usb/usbssp/gadget-trace.h:482,
from drivers/usb/usbssp/gadget-trace.c:13:
drivers/usb/usbssp/./gadget-trace.h: In function ‘trace_raw_output_usbssp_log_request’:
drivers/usb/usbssp/./gadget-trace.h:201:477: warning: format ‘%llx’ expects argument of type ‘long long unsigned int’, but argument 6 has type ‘dma_addr_t {aka unsigned int}’ [-Wformat=]
DECLARE_EVENT_CLASS(usbssp_log_request,
^
scripts/Makefile.build:317: recipe for target 'drivers/usb/usbssp/gadget-trace.o' failed
make[3]: *** [drivers/usb/usbssp/gadget-trace.o] Error 1
scripts/Makefile.build:558: recipe for target 'drivers/usb/usbssp' failed
make[2]: *** [drivers/usb/usbssp] Error 2
scripts/Makefile.build:558: recipe for target 'drivers/usb' failed
make[1]: *** [drivers/usb] Error 2
Makefile:1029: recipe for target 'drivers' failed
make: *** [drivers] Error 2

--
cheers,
-roger

Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki

2018-08-02 04:32:05

by Pawel Laszczak

[permalink] [raw]
Subject: RE: [PATCH 00/31] Introduced new Cadence USBSSP DRD Driver

> > This patch set introduce new Cadence USBSSP DRD driver to linux
> > kernel.
> >
> > The Cadence USBSSP DRD Driver s a highly configurable IP Core which
> > can be instantiated as Dual-Role Device (DRD), Peripheral Only and
> > Host Only (XHCI) configurations.
> >
> > The current driver has been validated with FPGA burned. We have
> > support for PCIe bus, which is used on FPGA prototyping.
> >
> > The host site of USBSSP controller is compliance with XHCI
> > specification, so it works with standard XHCI linux driver.
> >
> > Also, device side of USBSSP controller was designed in such way to
> > looks like XHCI. It means that most of logic of USBSSP controller is
> > also compliance with XHCI specification.
> >
> > Consequently, the USBSSP driver for peripheral mode is very similar to
> > XHCI driver.
> >
> > This version of driver supports only Device mode but DRD and Host mode
> > will be added in the future.
> >
>
> Based on the posting date this series looks like v3.
>
> You should add the version prefix to the patches next time.
> e.g. [PATCH v4 ...]
>
I wanted to start versioning driver after this set patch will be approved.
Also I have plan to add change-log after approval this patch set.
I now treat this patch set as Initial version of driver.
If my approach is incorrect please correct me ??
Maybe before approval the better versioning will be [PATCH vrc4] ?

> It is also recommended to add a Change-log in the cover letter explaining
> what changes were made in each revision.
>
I

> There are a bunch of checkpatch warnings in this series.
> You can use ./scripts/checkpatch.pl on the patches and fix the issues.
Yes I know, but most of them are related to "line over 80 characters"
and " quoted string split across lines" warnings. The first group I could correct
but then I will make readability worse.
To eliminate the second group I will have to enlarge the first group of warnings and
the number of characters in single line will be bigger than 100.
> > ---
> >
> > Pawel Laszczak (31):
> > usb: usbssp: Defined register maps and other useful structures.
> > usb: usbssp: Added some decoding functions.
> > usb: usbssp: Add trace events used in driver
> > usb: usbssp: Added USBSSP platform driver
> > usb: usbssp: Added first part of initialization sequence.
> > usb: usbssp: added template functions used by upper layer.
> > usb: usbssp: Initialization - added usbssp_mem_init
> > usb: usbssp: Added ring and segment handling functions.
> > usb: usbssp: add implementation of usbssp_mem_cleanup
> > usb: usbssp: added usbssp_trb_in_td function.
> > usb: usbssp: added function for stopping driver.
> > usb: usbssp: added functions for queuing commands.
> > usb: usbssp: addec procedure for handlin Port Status Change events.
> > usb: usbssp: added procedure handling command completion events.
> > usb: usbssp: added device controller error, transfer and SETUP
> > completion event.
> > usb: usbssp: added connect/disconnect procedures.
> > usb: usbssp: added implementation of usbssp_halt_endpoint function.
> > usb: usbssp: added handling of Port Reset event.
> > usb: usbssp: added support for USB enumeration process.
> > usb: usbssp: added queuing procedure for control transfer.
> > usb: usbssp: added queuing procedure for BULK and INT transfer.
> > usb: usbssp: added procedure removing request from transfer ring
> > usb: usbssp: added implementation of transfer events.
> > usb: usbssp: added detecting command timeout.
> > usb: usbssp: added implementation of usbssp interface.
> > usb: usbssp: added endpoint configuration functionality.
> > usb: usbssp: implements usbssp_gadget_ep_enable function
> > usb: usbssp: implemented usbssp_gadget_ep_disable function.
> > usb: usbssp: added support for LPM.
> > usb: usbssp: added support for TEST_MODE.
> > usb: usbssp: add pci to platform driver wrapper.
> >
> > drivers/usb/Kconfig | 2 +
> > drivers/usb/Makefile | 2 +
> > drivers/usb/usbssp/Kconfig | 29 +
> > drivers/usb/usbssp/Makefile | 15 +
> > drivers/usb/usbssp/gadget-dbg.c | 39 +
> > drivers/usb/usbssp/gadget-ep0.c | 571 +++++
> > drivers/usb/usbssp/gadget-ext-caps.h | 102 +
> > drivers/usb/usbssp/gadget-if.c | 569 +++++
> > drivers/usb/usbssp/gadget-mem.c | 1938 +++++++++++++++
> > drivers/usb/usbssp/gadget-port.c | 287 +++
> > drivers/usb/usbssp/gadget-ring.c | 3452 ++++++++++++++++++++++++++
> > drivers/usb/usbssp/gadget-trace.c | 13 +
> > drivers/usb/usbssp/gadget-trace.h | 482 ++++
> > drivers/usb/usbssp/gadget.c | 1909 ++++++++++++++
> > drivers/usb/usbssp/gadget.h | 2373 ++++++++++++++++++
> > drivers/usb/usbssp/usbssp-pci-wrap.c | 226 ++
> > drivers/usb/usbssp/usbssp-plat.c | 186 ++
> > 17 files changed, 12195 insertions(+) create mode 100644
> > drivers/usb/usbssp/Kconfig create mode 100644
> > drivers/usb/usbssp/Makefile create mode 100644
> > drivers/usb/usbssp/gadget-dbg.c create mode 100644
> > drivers/usb/usbssp/gadget-ep0.c create mode 100644
> > drivers/usb/usbssp/gadget-ext-caps.h
> > create mode 100644 drivers/usb/usbssp/gadget-if.c create mode 100644
> > drivers/usb/usbssp/gadget-mem.c create mode 100644
> > drivers/usb/usbssp/gadget-port.c create mode 100644
> > drivers/usb/usbssp/gadget-ring.c create mode 100644
> > drivers/usb/usbssp/gadget-trace.c create mode 100644
> > drivers/usb/usbssp/gadget-trace.h create mode 100644
> > drivers/usb/usbssp/gadget.c create mode 100644
> > drivers/usb/usbssp/gadget.h create mode 100644
> > drivers/usb/usbssp/usbssp-pci-wrap.c
> > create mode 100644 drivers/usb/usbssp/usbssp-plat.c
> >
>
> --
cheers,
Pawel

2018-08-02 06:27:40

by Pawel Laszczak

[permalink] [raw]
Subject: RE: [PATCH 04/31] usb: usbssp: Added USBSSP platform driver

> > This patch adds platform driver that is entry point for loading and
> > unloading usbssp.ko modules.
> > It also adds information about this driver to drivers/usb/Kconfig and
> > drivers/usb/Makefile files and create Kconfig and Makefile files in
> > drivers/usb/usbssp directory.
> >
> > Patch also adds template for some function ivokked from
>
> s/ivokked/invoked
>
> > usbssp_plat.c file. These function will be implemented in next patches.
> >
> > This patch also introduce usbssp_trb_virt_to_dma that converts virtual
> > address of TRB's to DMA address. In this moment this function is used
> > only in gadget-trace.h.
>
> s/"In this moment"/"At the moment"
>
> >
> > From this moment the driver can be compiled.
> >
> > Signed-off-by: Pawel Laszczak <[email protected]>
> > ---
> > drivers/usb/Kconfig | 2 +
> > drivers/usb/Makefile | 2 +
> > drivers/usb/usbssp/Kconfig | 21 ++++
> > drivers/usb/usbssp/Makefile | 11 ++
> > drivers/usb/usbssp/gadget-ring.c | 48 ++++++++
> > drivers/usb/usbssp/gadget.c | 64 +++++++++++
> > drivers/usb/usbssp/gadget.h | 16 ++-
> > drivers/usb/usbssp/usbssp-plat.c | 186
> > +++++++++++++++++++++++++++++++
> > 8 files changed, 349 insertions(+), 1 deletion(-) create mode 100644
> > drivers/usb/usbssp/Kconfig create mode 100644
> > drivers/usb/usbssp/Makefile create mode 100644
> > drivers/usb/usbssp/gadget-ring.c create mode 100644
> > drivers/usb/usbssp/gadget.c create mode 100644
> > drivers/usb/usbssp/usbssp-plat.c
> >
>
> Build fails at this patch with error [1]. Building should never fail at any patch
> in the series.
>
Yes it's true. There is a lack of #include <linux/irq.h> in drivers/usb/usbssp/gadget.h.

The compilation works correct starting from "0007-usb-usbssp-Initialization-added-usbssp_mem_init.patch"
Between 0004 and 0007 there is a problem in drivers/usb/usbssp/Makefile (lack of "\").

Should I prepare a new series or I should wait for other comments ?

>
> > diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index
> > f699abab1787..dc05f384c34c 100644
> > --- a/drivers/usb/Kconfig
> > +++ b/drivers/usb/Kconfig
> > @@ -110,6 +110,8 @@ source "drivers/usb/mtu3/Kconfig"
> >
> > source "drivers/usb/musb/Kconfig"
> >
> > +source "drivers/usb/usbssp/Kconfig"
> > +
> > source "drivers/usb/dwc3/Kconfig"
> >
> > source "drivers/usb/dwc2/Kconfig"
> > diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index
> > 060643a1b5c8..b1cd5f83d440 100644
> > --- a/drivers/usb/Makefile
> > +++ b/drivers/usb/Makefile
> > @@ -8,6 +8,8 @@
> > obj-$(CONFIG_USB) += core/
> > obj-$(CONFIG_USB_SUPPORT) += phy/
> >
> > +obj-$(CONFIG_USB_USBSSP) += usbssp/
> > +
> > obj-$(CONFIG_USB_DWC3) += dwc3/
> > obj-$(CONFIG_USB_DWC2) += dwc2/
> > obj-$(CONFIG_USB_ISP1760) += isp1760/
> > diff --git a/drivers/usb/usbssp/Kconfig b/drivers/usb/usbssp/Kconfig
> > new file mode 100644 index 000000000000..ee20b01753dc
> > --- /dev/null
> > +++ b/drivers/usb/usbssp/Kconfig
> > @@ -0,0 +1,21 @@
> > +config USB_USBSSP
>
> Do you want to choose a better Kconfig symbol name? USB is repeated twice
> in USB_USBSSP.
>
> I'd recommend to add something signifying Cadence in the symbol
>
> some examples
>
> USB_CADSSP, USB_CSSP

Ok, I will change this to USB_CSSP

> > + tristate "Cadence USBSSP DRD Controller"
> > + depends on (USB || USB_GADGET) && HAS_DMA
> > + select USB_USBSSP_GADGET
>
> Not good to select a symbol that has dependencies.
>
> > + help
> > + Say Y here if your system has a cadence USBSSP dual-role
> controller.
> > + It supports: dual-role switch Host-only, and Peripheral-only.
> > +
> > + If you choose to build this driver is a dynamically linked
> > + module, the module will be called usbssp.ko.
> > +
> > +if USB_USBSSP
> > +
> > +config USB_USBSSP_GADGET
> > + tristate "Gadget only mode"
> > + default USB_USBSSP
> > + depends on USB_GADGET=y || USB_GADGET=USB_USBSSP
> > + help
> > + Select this when you want to use USBSSP in gadget mode only,
>
> s/,/.
>
> > +endif
> > +
> > diff --git a/drivers/usb/usbssp/Makefile b/drivers/usb/usbssp/Makefile
> > new file mode 100644 index 000000000000..d85f15afb51c
> > --- /dev/null
> > +++ b/drivers/usb/usbssp/Makefile
> > @@ -0,0 +1,11 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +# define_trace.h needs to know how to find our header
> > +CFLAGS_gadget-trace.o := -I$(src)
> > +
> > +obj-$(CONFIG_USB_USBSSP_GADGET) += usbssp.o
> > +usbssp-y := usbssp-plat.o gadget-ring.o \
> > + gadget.o
> > +
> > +ifneq ($(CONFIG_TRACING),)
> > + usbssp-y += gadget-trace.o
> > +endif
> > diff --git a/drivers/usb/usbssp/gadget-ring.c
> > b/drivers/usb/usbssp/gadget-ring.c
> > new file mode 100644
> > index 000000000000..d1da59306d02
> > --- /dev/null
> > +++ b/drivers/usb/usbssp/gadget-ring.c
> > @@ -0,0 +1,48 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * USBSSP device controller driver
> > + *
> > + * Copyright (C) 2018 Cadence.
> > + *
> > + * Author: Pawel Laszczak
> > + *
> > + * A lot of code based on Linux XHCI driver.
> > + * Origin: Copyright (C) 2008 Intel Corp */
> > +
> > +#include <linux/scatterlist.h>
> > +#include <linux/slab.h>
> > +#include <linux/dma-mapping.h>
> > +#include <linux/irq.h>
> > +#include "gadget-trace.h"
> > +#include "gadget.h"
> > +
> > +/*
> > + * Returns zero if the TRB isn't in this segment, otherwise it
> > +returns the DMA
> > + * address of the TRB.
> > + */
> > +dma_addr_t usbssp_trb_virt_to_dma(struct usbssp_segment *seg,
> > + union usbssp_trb *trb)
> > +{
> > + unsigned long segment_offset;
> > +
> > + if (!seg || !trb || trb < seg->trbs)
> > + return 0;
> > + /* offset in TRBs */
> > + segment_offset = trb - seg->trbs;
> > + if (segment_offset >= TRBS_PER_SEGMENT)
> > + return 0;
> > + return seg->dma + (segment_offset * sizeof(*trb)); }
> > +
> > +irqreturn_t usbssp_irq(int irq, void *priv) {
> > + struct usbssp_udc *usbssp_data = (struct usbssp_udc *)priv;
> > + irqreturn_t ret = IRQ_NONE;
> > + unsigned long flags;
> > +
> > + spin_lock_irqsave(&usbssp_data->lock, flags);
> > +
> > + spin_unlock_irqrestore(&usbssp_data->lock, flags);
> > + return ret;
> > +}
> > diff --git a/drivers/usb/usbssp/gadget.c b/drivers/usb/usbssp/gadget.c
> > new file mode 100644 index 000000000000..2f60d7dd1fe4
> > --- /dev/null
> > +++ b/drivers/usb/usbssp/gadget.c
> > @@ -0,0 +1,64 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * USBSSP device controller driver
> > + *
> > + * Copyright (C) 2018 Cadence.
> > + *
> > + * Author: Pawel Laszczak
> > + *
> > + * A lot of code based on Linux XHCI driver.
> > + * Origin: Copyright (C) 2008 Intel Corp */
> > +
> > +#include <linux/pci.h>
> > +#include <linux/irq.h>
> > +#include <linux/log2.h>
> > +#include <linux/module.h>
> > +#include <linux/moduleparam.h>
> > +#include <linux/slab.h>
> > +#include <linux/dmi.h>
> > +#include <linux/dma-mapping.h>
> > +#include <linux/delay.h>
> > +
> > +#include "gadget-trace.h"
> > +#include "gadget.h"
> > +
> > +#ifdef CONFIG_PM
> > +/*
> > + * Stop DC (not bus-specific)
> > + *
> > + * This is called when the machine transition into S3/S4 mode.
> > + *
> > + */
> > +int usbssp_suspend(struct usbssp_udc *usbssp_data, bool do_wakeup) {
> > + /*TODO*/
> > + return -ENOSYS;
> > +}
> > +
> > +/*
> > + * start DC (not bus-specific)
> > + *
> > + * This is called when the machine transition from S3/S4 mode.
> > + *
> > + */
> > +int usbssp_resume(struct usbssp_udc *usbssp_data, bool hibernated) {
> > + /*TODO*/
> > + return -ENOSYS;
> > +}
> > +
> > +#endif /* CONFIG_PM */
> > +
> > +int usbssp_gadget_init(struct usbssp_udc *usbssp_data) {
> > + int ret;
> > + return ret;
> ret is not initialized before returning.
> Maybe just
> return 0;
I left this compiler warning because the next patches will add implementation of this function and then warning disappear.
I trayed to prepare this series of patches in a way to avoid deletion line in next patches in series.

> > +}
> > +
> > +int usbssp_gadget_exit(struct usbssp_udc *usbssp_data) {
> > + int ret = 0;
> > +
> > + return ret;
>
> return 0;
>
> > +}
> > diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h
> > index b5c17603af78..55e20795d900 100644
> > --- a/drivers/usb/usbssp/gadget.h
> > +++ b/drivers/usb/usbssp/gadget.h
> > @@ -9,7 +9,6 @@
> > * A lot of code based on Linux XHCI driver.
> > * Origin: Copyright (C) 2008 Intel Corp.
> > */
> > -
>
> unnecessary blank line removal
>
> > #ifndef __LINUX_USBSSP_GADGET_H
> > #define __LINUX_USBSSP_GADGET_H
> >
> > @@ -1676,6 +1675,21 @@ static inline void usbssp_write_64(struct
> > usbssp_udc *usbssp_data, {
> > lo_hi_writeq(val, regs);
> > }
> > +
> > +/* USBSSP Device controller glue */
> > +int usbssp_suspend(struct usbssp_udc *usbssp_data, bool do_wakeup);
> > +int usbssp_resume(struct usbssp_udc *usbssp_data, bool hibernated);
> > +
> > +irqreturn_t usbssp_irq(int irq, void *priv);
> > +
> > +/* USBSSP ring, segment, TRB, and TD functions */ dma_addr_t
> > +usbssp_trb_virt_to_dma(struct usbssp_segment *seg,
> > + union usbssp_trb *trb);
> > +
> > +/* USBSSP gadget interface*/
> > +int usbssp_gadget_init(struct usbssp_udc *usbssp_data); int
> > +usbssp_gadget_exit(struct usbssp_udc *usbssp_data);
> > +
> > static inline char *usbssp_slot_state_string(u32 state) {
> > switch (state) {
> > diff --git a/drivers/usb/usbssp/usbssp-plat.c
> > b/drivers/usb/usbssp/usbssp-plat.c
>
> Is this file meant only for gadget controller or later even for host controller?
> If only for gadget then this could be just called gadget-plat.c

I'm not sure at this moment. I have not wondered at now how this driver will
be integrated with XHCI driver and DRD driver.


> > new file mode 100644
> > index 000000000000..c048044148aa
> > --- /dev/null
> > +++ b/drivers/usb/usbssp/usbssp-plat.c
> > @@ -0,0 +1,186 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * USBSSP device controller driver
> > + *
> > + * Copyright (C) 2018 Cadence.
> > + *
> > + * Author: Pawel Laszczak
> > + *
> > + */
> > +
> > +#include <linux/clk.h>
> > +#include <linux/dma-mapping.h>
> > +#include <linux/module.h>
> > +#include <linux/pci.h>
> > +#include <linux/of.h>
> > +#include <linux/of_device.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/usb/phy.h>
> > +#include <linux/slab.h>
> > +#include <linux/acpi.h>
> > +
> > +#include "gadget.h"
> > +
> > +#define DRIVER_AUTHOR "Pawel Laszczak"
> > +#define DRIVER_DESC "USBSSP Device Controller (USBSSP) Driver"
> > +
> > +#ifdef CONFIG_OF
> > +
> > +static const struct of_device_id usbssp_dev_of_match[] = {
> > + {
> > + .compatible = "Cadence, usbssp-dev",
>
> Avoid upper-case in compatible strings.
>
> > + },
> > + {},
> > +};
> > +MODULE_DEVICE_TABLE(of, usbssp_dev_of_match); #endif
> > +
> > +int usbssp_is_platform(void)
> > +{
> > + return 1;
> > +}
> > +
> > +static int usbssp_plat_probe(struct platform_device *pdev) {
> > + struct device *dev = &pdev->dev;
> > + struct resource *res;
> > + struct usbssp_udc *usbssp_data;
> > + int ret = 0;
> > + int irq;
> > + struct device *sysdev;
> > +
> > + irq = platform_get_irq(pdev, 0);
> > + if (irq < 0) {
> > + dev_err(&pdev->dev, "Incorrect IRQ number\n");
>
> IRQ number might be correct but might be some other issue.
> You could just say "couldn't get IRQ"
>
> > + return -ENODEV;
>
> Also, we don't want to print any error message if we got a -EPROBE_DEFER.
> And we need to return that instead of -ENODEV for deferred probing to
> work.
>
> > + }
>
> So how about
> if (irq < 0) {
> if (irq != -EPROBE_DEFER)
> dev_err(&pdev->dev, "couldn't get IRQ\n")
>
> return irq;
> }
>
> > +
> > + usbssp_data = devm_kzalloc(dev, sizeof(*usbssp_data),
> GFP_KERNEL);
> > + if (!usbssp_data)
> > + return -ENOMEM;
> > +
> > + for (sysdev = &pdev->dev; sysdev; sysdev = sysdev->parent) {
> > + if (is_of_node(sysdev->fwnode) ||
> > + is_acpi_device_node(sysdev->fwnode))
> > + break;
> > +#ifdef CONFIG_PCI
> > + else if (sysdev->bus == &pci_bus_type)
> > + break;
> > +#endif
> > + }
>
> It is hard to understand what is this for loop doing exactly.
>
> xhci-plat.c seems to have this comment. You should add it above as well.
> /*
> * sysdev must point to a device that is known to the system firmware
> * or PCI hardware. We handle these three cases here:
> * 1. xhci_plat comes from firmware
> * 2. xhci_plat is child of a device from firmware (dwc3-plat)
> * 3. xhci_plat is grandchild of a pci device (dwc3-pci)
> */
>
>
> > +
> > + if (!sysdev)
> > + sysdev = &pdev->dev;
> > +
> > + /* Try to set 64-bit DMA first */
> > + if (WARN_ON(!dev->dma_mask))
> > + /* Platform did not initialize dma_mask */
> > + ret = dma_coerce_mask_and_coherent(dev,
> > + DMA_BIT_MASK(64));
> > + else
> > + ret = dma_set_mask_and_coherent(dev,
> DMA_BIT_MASK(64));
> > +
> > + /* If seting 64-bit DMA mask fails, fall back to 32-bit DMA mask */
> > + if (ret) {
> > + ret = dma_set_mask_and_coherent(dev,
> DMA_BIT_MASK(32));
> > + if (ret)
> > + return ret;
> > + }
> > +
> > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > + usbssp_data->regs = devm_ioremap_resource(dev, res);
> > +
> > + if (IS_ERR(usbssp_data->regs)) {
> dev_err() ?
>
> > + ret = PTR_ERR(usbssp_data->regs);
> > + return ret;
> > + }
> > +
> > + usbssp_data->rsrc_start = res->start;
> > + usbssp_data->rsrc_len = resource_size(res);
> > +
> > + ret = devm_request_irq(dev, irq, usbssp_irq, IRQF_SHARED,
> > + dev_name(dev), usbssp_data);
>
> devm_request_threaded_irq() ?
Currently for deferred interrupts I use workqueue thread. Some time ago I tried use this function
but I have some problem with it. I have plan to replace devm_request_irq with devm_request_threaded_irq.

In USBSSP controller for device side I have big problem with handling commands. Handling command works like
in XHCI controller. After invoking command driver has to wait for completion so it should sleep for some time.
During waiting (e.g wait_for_completion) we need to enable the interrupts because we need to detect the completion event. Additionally some API function from USBSSP gadget driver are invoked by upper layer with disabled interrupts.
In this situation we can't enable interrupt before invoking command and driver has to detect completion in other way.
The concept taken from the host is not a perfect for device driver where most driver should works in interrupt context,

> > +
> > + if (ret < 0)
> > + return ret;
> > +
> > + usbssp_data->irq = irq;
> > + usbssp_data->dev = dev;
> > + platform_set_drvdata(pdev, usbssp_data);
> > + ret = usbssp_gadget_init(usbssp_data);
> > +
> > + return ret;
> > +}
> > +
> > +static int usbssp_plat_remove(struct platform_device *pdev) {
> > + int ret = 0;
> > + struct usbssp_udc *usbssp_data;
> > +
> > + usbssp_data = (struct usbssp_udc *)platform_get_drvdata(pdev);
> > + ret = usbssp_gadget_exit(usbssp_data);
> > + return ret;
> > +
>
> move this blank line above return ret;
> > +}
> > +
> > +static int __maybe_unused usbssp_plat_suspend(struct device *dev) {
> > + struct usbssp_udc *usbssp_data = dev_get_drvdata(dev);
> > +
> > + return usbssp_suspend(usbssp_data, device_may_wakeup(dev)); }
> > +
> > +static int __maybe_unused usbssp_plat_resume(struct device *dev) {
> > + struct usbssp_udc *usbssp_data = dev_get_drvdata(dev);
> > +
> > + return usbssp_resume(usbssp_data, 0); }
> > +
> > +static int __maybe_unused usbssp_plat_runtime_suspend(struct device
> > +*dev) {
> > + struct usbssp_udc *usbssp_data = dev_get_drvdata(dev);
> > +
> > + return usbssp_suspend(usbssp_data, true); }
> > +
> > +static int __maybe_unused usbssp_plat_runtime_resume(struct device
> > +*dev) {
> > + struct usbssp_udc *usbssp_data = dev_get_drvdata(dev);
> > +
> > + return usbssp_resume(usbssp_data, 0); }
> > +
> > +static const struct dev_pm_ops usbssp_plat_pm_ops = {
> > + SET_SYSTEM_SLEEP_PM_OPS(usbssp_plat_suspend,
> usbssp_plat_resume)
> > +
> > + SET_RUNTIME_PM_OPS(usbssp_plat_runtime_suspend,
> > + usbssp_plat_runtime_resume,
> > + NULL)
> > +};
> > +
> > +static struct platform_driver usbssp_driver = {
> > + .probe = usbssp_plat_probe,
> > + .remove = usbssp_plat_remove,
> > + .driver = {
> > + .name = "usbssp-dev",
> > + .pm = &usbssp_plat_pm_ops,
> > + .of_match_table = of_match_ptr(usbssp_dev_of_match),
> > + },
> > +};
> > +
> > +static int __init usbssp_plat_init(void) {
> > + return platform_driver_register(&usbssp_driver);
> > +}
> > +module_init(usbssp_plat_init);
> > +
> > +static void __exit usbssp_plat_exit(void) {
> > + platform_driver_unregister(&usbssp_driver);
> > +}
> > +module_exit(usbssp_plat_exit);
> > +
> > +MODULE_ALIAS("platform:usbss-gadget");
> usbssp-gadget?
>
> Why did you choose a different name for compatible? "usbssp-dev"
> Would be nice to have it consistent.
>
> > +MODULE_DESCRIPTION("USBSSP' Device Controller (USBSSP) Driver");
>
> USBSSP, 2 times?
>
> > +MODULE_LICENSE("GPL v2");
> >
I will correct this.
>
> [1] build error
>
> CC [M] drivers/usb/usbssp/gadget-trace.o In file included from
> drivers/usb/usbssp/gadget-trace.h:27:0,
> from drivers/usb/usbssp/gadget-trace.c:13:
> drivers/usb/usbssp/gadget.h:1683:1: error: unknown type name
> ‘irqreturn_t’
> irqreturn_t usbssp_irq(int irq, void *priv); ^~~~~~~~~~~ In file included from
> ./include/trace/trace_events.h:394:0,
> from ./include/trace/define_trace.h:96,
> from drivers/usb/usbssp/gadget-trace.h:482,
> from drivers/usb/usbssp/gadget-trace.c:13:
> drivers/usb/usbssp/./gadget-trace.h: In function
> ‘trace_raw_output_usbssp_log_request’:
> drivers/usb/usbssp/./gadget-trace.h:201:477: warning: format ‘%llx’ expects
> argument of type ‘long long unsigned int’, but argument 6 has type
> ‘dma_addr_t {aka unsigned int}’ [-Wformat=]
> DECLARE_EVENT_CLASS(usbssp_log_request,
>
> ^
> scripts/Makefile.build:317: recipe for target 'drivers/usb/usbssp/gadget-
> trace.o' failed
> make[3]: *** [drivers/usb/usbssp/gadget-trace.o] Error 1
> scripts/Makefile.build:558: recipe for target 'drivers/usb/usbssp' failed
> make[2]: *** [drivers/usb/usbssp] Error 2
> scripts/Makefile.build:558: recipe for target 'drivers/usb' failed
> make[1]: *** [drivers/usb] Error 2
> Makefile:1029: recipe for target 'drivers' failed
> make: *** [drivers] Error 2
>
> --
cheers,
Pawel

2018-08-02 13:25:37

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH 00/31] Introduced new Cadence USBSSP DRD Driver

On 02/08/18 07:26, Pawel Laszczak wrote:
>>> This patch set introduce new Cadence USBSSP DRD driver to linux
>>> kernel.
>>>
>>> The Cadence USBSSP DRD Driver s a highly configurable IP Core which
>>> can be instantiated as Dual-Role Device (DRD), Peripheral Only and
>>> Host Only (XHCI) configurations.
>>>
>>> The current driver has been validated with FPGA burned. We have
>>> support for PCIe bus, which is used on FPGA prototyping.
>>>
>>> The host site of USBSSP controller is compliance with XHCI
>>> specification, so it works with standard XHCI linux driver.
>>>
>>> Also, device side of USBSSP controller was designed in such way to
>>> looks like XHCI. It means that most of logic of USBSSP controller is
>>> also compliance with XHCI specification.
>>>
>>> Consequently, the USBSSP driver for peripheral mode is very similar to
>>> XHCI driver.
>>>
>>> This version of driver supports only Device mode but DRD and Host mode
>>> will be added in the future.
>>>
>>
>> Based on the posting date this series looks like v3.
>>
>> You should add the version prefix to the patches next time.
>> e.g. [PATCH v4 ...]
>>
> I wanted to start versioning driver after this set patch will be approved.
> Also I have plan to add change-log after approval this patch set.
> I now treat this patch set as Initial version of driver.
> If my approach is incorrect please correct me ??
> Maybe before approval the better versioning will be [PATCH vrc4] ?

You should prefix patch with RFC.

[RFC PATCH v4]

Drop the RFC and reset version to 1 when things are ready for integration.

>
>> It is also recommended to add a Change-log in the cover letter explaining
>> what changes were made in each revision.
>>
> I
>
>> There are a bunch of checkpatch warnings in this series.
>> You can use ./scripts/checkpatch.pl on the patches and fix the issues.
> Yes I know, but most of them are related to "line over 80 characters"
> and " quoted string split across lines" warnings. The first group I could correct
> but then I will make readability worse.
> To eliminate the second group I will have to enlarge the first group of warnings and
> the number of characters in single line will be bigger than 100.

Readability is preferred over getting rid of "line over 80 char" warning.
But it is maintainer's call here.

>>> ---
>>>
>>> Pawel Laszczak (31):
>>> usb: usbssp: Defined register maps and other useful structures.
>>> usb: usbssp: Added some decoding functions.
>>> usb: usbssp: Add trace events used in driver
>>> usb: usbssp: Added USBSSP platform driver
>>> usb: usbssp: Added first part of initialization sequence.
>>> usb: usbssp: added template functions used by upper layer.
>>> usb: usbssp: Initialization - added usbssp_mem_init
>>> usb: usbssp: Added ring and segment handling functions.
>>> usb: usbssp: add implementation of usbssp_mem_cleanup
>>> usb: usbssp: added usbssp_trb_in_td function.
>>> usb: usbssp: added function for stopping driver.
>>> usb: usbssp: added functions for queuing commands.
>>> usb: usbssp: addec procedure for handlin Port Status Change events.
>>> usb: usbssp: added procedure handling command completion events.
>>> usb: usbssp: added device controller error, transfer and SETUP
>>> completion event.
>>> usb: usbssp: added connect/disconnect procedures.
>>> usb: usbssp: added implementation of usbssp_halt_endpoint function.
>>> usb: usbssp: added handling of Port Reset event.
>>> usb: usbssp: added support for USB enumeration process.
>>> usb: usbssp: added queuing procedure for control transfer.
>>> usb: usbssp: added queuing procedure for BULK and INT transfer.
>>> usb: usbssp: added procedure removing request from transfer ring
>>> usb: usbssp: added implementation of transfer events.
>>> usb: usbssp: added detecting command timeout.
>>> usb: usbssp: added implementation of usbssp interface.
>>> usb: usbssp: added endpoint configuration functionality.
>>> usb: usbssp: implements usbssp_gadget_ep_enable function
>>> usb: usbssp: implemented usbssp_gadget_ep_disable function.
>>> usb: usbssp: added support for LPM.
>>> usb: usbssp: added support for TEST_MODE.
>>> usb: usbssp: add pci to platform driver wrapper.
>>>
>>> drivers/usb/Kconfig | 2 +
>>> drivers/usb/Makefile | 2 +
>>> drivers/usb/usbssp/Kconfig | 29 +
>>> drivers/usb/usbssp/Makefile | 15 +
>>> drivers/usb/usbssp/gadget-dbg.c | 39 +
>>> drivers/usb/usbssp/gadget-ep0.c | 571 +++++
>>> drivers/usb/usbssp/gadget-ext-caps.h | 102 +
>>> drivers/usb/usbssp/gadget-if.c | 569 +++++
>>> drivers/usb/usbssp/gadget-mem.c | 1938 +++++++++++++++
>>> drivers/usb/usbssp/gadget-port.c | 287 +++
>>> drivers/usb/usbssp/gadget-ring.c | 3452 ++++++++++++++++++++++++++
>>> drivers/usb/usbssp/gadget-trace.c | 13 +
>>> drivers/usb/usbssp/gadget-trace.h | 482 ++++
>>> drivers/usb/usbssp/gadget.c | 1909 ++++++++++++++
>>> drivers/usb/usbssp/gadget.h | 2373 ++++++++++++++++++
>>> drivers/usb/usbssp/usbssp-pci-wrap.c | 226 ++
>>> drivers/usb/usbssp/usbssp-plat.c | 186 ++
>>> 17 files changed, 12195 insertions(+) create mode 100644
>>> drivers/usb/usbssp/Kconfig create mode 100644
>>> drivers/usb/usbssp/Makefile create mode 100644
>>> drivers/usb/usbssp/gadget-dbg.c create mode 100644
>>> drivers/usb/usbssp/gadget-ep0.c create mode 100644
>>> drivers/usb/usbssp/gadget-ext-caps.h
>>> create mode 100644 drivers/usb/usbssp/gadget-if.c create mode 100644
>>> drivers/usb/usbssp/gadget-mem.c create mode 100644
>>> drivers/usb/usbssp/gadget-port.c create mode 100644
>>> drivers/usb/usbssp/gadget-ring.c create mode 100644
>>> drivers/usb/usbssp/gadget-trace.c create mode 100644
>>> drivers/usb/usbssp/gadget-trace.h create mode 100644
>>> drivers/usb/usbssp/gadget.c create mode 100644
>>> drivers/usb/usbssp/gadget.h create mode 100644
>>> drivers/usb/usbssp/usbssp-pci-wrap.c
>>> create mode 100644 drivers/usb/usbssp/usbssp-plat.c
>>>
>>
>> --
> cheers,
> Pawel
>

--
cheers,
-roger

Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki

2018-08-02 13:29:35

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH 04/31] usb: usbssp: Added USBSSP platform driver

On 02/08/18 09:25, Pawel Laszczak wrote:
>>> This patch adds platform driver that is entry point for loading and
>>> unloading usbssp.ko modules.
>>> It also adds information about this driver to drivers/usb/Kconfig and
>>> drivers/usb/Makefile files and create Kconfig and Makefile files in
>>> drivers/usb/usbssp directory.
>>>
>>> Patch also adds template for some function ivokked from
>>
>> s/ivokked/invoked
>>
>>> usbssp_plat.c file. These function will be implemented in next patches.
>>>
>>> This patch also introduce usbssp_trb_virt_to_dma that converts virtual
>>> address of TRB's to DMA address. In this moment this function is used
>>> only in gadget-trace.h.
>>
>> s/"In this moment"/"At the moment"
>>
>>>
>>> From this moment the driver can be compiled.
>>>
>>> Signed-off-by: Pawel Laszczak <[email protected]>
>>> ---
>>> drivers/usb/Kconfig | 2 +
>>> drivers/usb/Makefile | 2 +
>>> drivers/usb/usbssp/Kconfig | 21 ++++
>>> drivers/usb/usbssp/Makefile | 11 ++
>>> drivers/usb/usbssp/gadget-ring.c | 48 ++++++++
>>> drivers/usb/usbssp/gadget.c | 64 +++++++++++
>>> drivers/usb/usbssp/gadget.h | 16 ++-
>>> drivers/usb/usbssp/usbssp-plat.c | 186
>>> +++++++++++++++++++++++++++++++
>>> 8 files changed, 349 insertions(+), 1 deletion(-) create mode 100644
>>> drivers/usb/usbssp/Kconfig create mode 100644
>>> drivers/usb/usbssp/Makefile create mode 100644
>>> drivers/usb/usbssp/gadget-ring.c create mode 100644
>>> drivers/usb/usbssp/gadget.c create mode 100644
>>> drivers/usb/usbssp/usbssp-plat.c
>>>
>>
>> Build fails at this patch with error [1]. Building should never fail at any patch
>> in the series.
>>
> Yes it's true. There is a lack of #include <linux/irq.h> in drivers/usb/usbssp/gadget.h.
>
> The compilation works correct starting from "0007-usb-usbssp-Initialization-added-usbssp_mem_init.patch"
> Between 0004 and 0007 there is a problem in drivers/usb/usbssp/Makefile (lack of "\").
>
> Should I prepare a new series or I should wait for other comments ?

I think you should wait. I haven't reviewed the other patches yet.

>
>>
>>> diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index
>>> f699abab1787..dc05f384c34c 100644
>>> --- a/drivers/usb/Kconfig
>>> +++ b/drivers/usb/Kconfig
>>> @@ -110,6 +110,8 @@ source "drivers/usb/mtu3/Kconfig"
>>>
>>> source "drivers/usb/musb/Kconfig"
>>>
>>> +source "drivers/usb/usbssp/Kconfig"
>>> +
>>> source "drivers/usb/dwc3/Kconfig"
>>>
>>> source "drivers/usb/dwc2/Kconfig"
>>> diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index
>>> 060643a1b5c8..b1cd5f83d440 100644
>>> --- a/drivers/usb/Makefile
>>> +++ b/drivers/usb/Makefile
>>> @@ -8,6 +8,8 @@
>>> obj-$(CONFIG_USB) += core/
>>> obj-$(CONFIG_USB_SUPPORT) += phy/
>>>
>>> +obj-$(CONFIG_USB_USBSSP) += usbssp/
>>> +
>>> obj-$(CONFIG_USB_DWC3) += dwc3/
>>> obj-$(CONFIG_USB_DWC2) += dwc2/
>>> obj-$(CONFIG_USB_ISP1760) += isp1760/
>>> diff --git a/drivers/usb/usbssp/Kconfig b/drivers/usb/usbssp/Kconfig
>>> new file mode 100644 index 000000000000..ee20b01753dc
>>> --- /dev/null
>>> +++ b/drivers/usb/usbssp/Kconfig
>>> @@ -0,0 +1,21 @@
>>> +config USB_USBSSP
>>
>> Do you want to choose a better Kconfig symbol name? USB is repeated twice
>> in USB_USBSSP.
>>
>> I'd recommend to add something signifying Cadence in the symbol
>>
>> some examples
>>
>> USB_CADSSP, USB_CSSP
>
> Ok, I will change this to USB_CSSP
>
>>> + tristate "Cadence USBSSP DRD Controller"
>>> + depends on (USB || USB_GADGET) && HAS_DMA
>>> + select USB_USBSSP_GADGET
>>
>> Not good to select a symbol that has dependencies.
>>
>>> + help
>>> + Say Y here if your system has a cadence USBSSP dual-role
>> controller.
>>> + It supports: dual-role switch Host-only, and Peripheral-only.
>>> +
>>> + If you choose to build this driver is a dynamically linked
>>> + module, the module will be called usbssp.ko.
>>> +
>>> +if USB_USBSSP
>>> +
>>> +config USB_USBSSP_GADGET
>>> + tristate "Gadget only mode"
>>> + default USB_USBSSP
>>> + depends on USB_GADGET=y || USB_GADGET=USB_USBSSP
>>> + help
>>> + Select this when you want to use USBSSP in gadget mode only,
>>
>> s/,/.
>>
>>> +endif
>>> +
>>> diff --git a/drivers/usb/usbssp/Makefile b/drivers/usb/usbssp/Makefile
>>> new file mode 100644 index 000000000000..d85f15afb51c
>>> --- /dev/null
>>> +++ b/drivers/usb/usbssp/Makefile
>>> @@ -0,0 +1,11 @@
>>> +# SPDX-License-Identifier: GPL-2.0
>>> +# define_trace.h needs to know how to find our header
>>> +CFLAGS_gadget-trace.o := -I$(src)
>>> +
>>> +obj-$(CONFIG_USB_USBSSP_GADGET) += usbssp.o
>>> +usbssp-y := usbssp-plat.o gadget-ring.o \
>>> + gadget.o
>>> +
>>> +ifneq ($(CONFIG_TRACING),)
>>> + usbssp-y += gadget-trace.o
>>> +endif
>>> diff --git a/drivers/usb/usbssp/gadget-ring.c
>>> b/drivers/usb/usbssp/gadget-ring.c
>>> new file mode 100644
>>> index 000000000000..d1da59306d02
>>> --- /dev/null
>>> +++ b/drivers/usb/usbssp/gadget-ring.c
>>> @@ -0,0 +1,48 @@
>>> +// SPDX-License-Identifier: GPL-2.0
>>> +/*
>>> + * USBSSP device controller driver
>>> + *
>>> + * Copyright (C) 2018 Cadence.
>>> + *
>>> + * Author: Pawel Laszczak
>>> + *
>>> + * A lot of code based on Linux XHCI driver.
>>> + * Origin: Copyright (C) 2008 Intel Corp */
>>> +
>>> +#include <linux/scatterlist.h>
>>> +#include <linux/slab.h>
>>> +#include <linux/dma-mapping.h>
>>> +#include <linux/irq.h>
>>> +#include "gadget-trace.h"
>>> +#include "gadget.h"
>>> +
>>> +/*
>>> + * Returns zero if the TRB isn't in this segment, otherwise it
>>> +returns the DMA
>>> + * address of the TRB.
>>> + */
>>> +dma_addr_t usbssp_trb_virt_to_dma(struct usbssp_segment *seg,
>>> + union usbssp_trb *trb)
>>> +{
>>> + unsigned long segment_offset;
>>> +
>>> + if (!seg || !trb || trb < seg->trbs)
>>> + return 0;
>>> + /* offset in TRBs */
>>> + segment_offset = trb - seg->trbs;
>>> + if (segment_offset >= TRBS_PER_SEGMENT)
>>> + return 0;
>>> + return seg->dma + (segment_offset * sizeof(*trb)); }
>>> +
>>> +irqreturn_t usbssp_irq(int irq, void *priv) {
>>> + struct usbssp_udc *usbssp_data = (struct usbssp_udc *)priv;
>>> + irqreturn_t ret = IRQ_NONE;
>>> + unsigned long flags;
>>> +
>>> + spin_lock_irqsave(&usbssp_data->lock, flags);
>>> +
>>> + spin_unlock_irqrestore(&usbssp_data->lock, flags);
>>> + return ret;
>>> +}
>>> diff --git a/drivers/usb/usbssp/gadget.c b/drivers/usb/usbssp/gadget.c
>>> new file mode 100644 index 000000000000..2f60d7dd1fe4
>>> --- /dev/null
>>> +++ b/drivers/usb/usbssp/gadget.c
>>> @@ -0,0 +1,64 @@
>>> +// SPDX-License-Identifier: GPL-2.0
>>> +/*
>>> + * USBSSP device controller driver
>>> + *
>>> + * Copyright (C) 2018 Cadence.
>>> + *
>>> + * Author: Pawel Laszczak
>>> + *
>>> + * A lot of code based on Linux XHCI driver.
>>> + * Origin: Copyright (C) 2008 Intel Corp */
>>> +
>>> +#include <linux/pci.h>
>>> +#include <linux/irq.h>
>>> +#include <linux/log2.h>
>>> +#include <linux/module.h>
>>> +#include <linux/moduleparam.h>
>>> +#include <linux/slab.h>
>>> +#include <linux/dmi.h>
>>> +#include <linux/dma-mapping.h>
>>> +#include <linux/delay.h>
>>> +
>>> +#include "gadget-trace.h"
>>> +#include "gadget.h"
>>> +
>>> +#ifdef CONFIG_PM
>>> +/*
>>> + * Stop DC (not bus-specific)
>>> + *
>>> + * This is called when the machine transition into S3/S4 mode.
>>> + *
>>> + */
>>> +int usbssp_suspend(struct usbssp_udc *usbssp_data, bool do_wakeup) {
>>> + /*TODO*/
>>> + return -ENOSYS;
>>> +}
>>> +
>>> +/*
>>> + * start DC (not bus-specific)
>>> + *
>>> + * This is called when the machine transition from S3/S4 mode.
>>> + *
>>> + */
>>> +int usbssp_resume(struct usbssp_udc *usbssp_data, bool hibernated) {
>>> + /*TODO*/
>>> + return -ENOSYS;
>>> +}
>>> +
>>> +#endif /* CONFIG_PM */
>>> +
>>> +int usbssp_gadget_init(struct usbssp_udc *usbssp_data) {
>>> + int ret;
>>> + return ret;
>> ret is not initialized before returning.
>> Maybe just
>> return 0;
> I left this compiler warning because the next patches will add implementation of this function and then warning disappear.
> I trayed to prepare this series of patches in a way to avoid deletion line in next patches in series.
>

It is perfectly fine to delete things in a patch. But compiler warnings shouldn't be there.

>>> +}
>>> +
>>> +int usbssp_gadget_exit(struct usbssp_udc *usbssp_data) {
>>> + int ret = 0;
>>> +
>>> + return ret;
>>
>> return 0;
>>
>>> +}
>>> diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h
>>> index b5c17603af78..55e20795d900 100644
>>> --- a/drivers/usb/usbssp/gadget.h
>>> +++ b/drivers/usb/usbssp/gadget.h
>>> @@ -9,7 +9,6 @@
>>> * A lot of code based on Linux XHCI driver.
>>> * Origin: Copyright (C) 2008 Intel Corp.
>>> */
>>> -
>>
>> unnecessary blank line removal
>>
>>> #ifndef __LINUX_USBSSP_GADGET_H
>>> #define __LINUX_USBSSP_GADGET_H
>>>
>>> @@ -1676,6 +1675,21 @@ static inline void usbssp_write_64(struct
>>> usbssp_udc *usbssp_data, {
>>> lo_hi_writeq(val, regs);
>>> }
>>> +
>>> +/* USBSSP Device controller glue */
>>> +int usbssp_suspend(struct usbssp_udc *usbssp_data, bool do_wakeup);
>>> +int usbssp_resume(struct usbssp_udc *usbssp_data, bool hibernated);
>>> +
>>> +irqreturn_t usbssp_irq(int irq, void *priv);
>>> +
>>> +/* USBSSP ring, segment, TRB, and TD functions */ dma_addr_t
>>> +usbssp_trb_virt_to_dma(struct usbssp_segment *seg,
>>> + union usbssp_trb *trb);
>>> +
>>> +/* USBSSP gadget interface*/
>>> +int usbssp_gadget_init(struct usbssp_udc *usbssp_data); int
>>> +usbssp_gadget_exit(struct usbssp_udc *usbssp_data);
>>> +
>>> static inline char *usbssp_slot_state_string(u32 state) {
>>> switch (state) {
>>> diff --git a/drivers/usb/usbssp/usbssp-plat.c
>>> b/drivers/usb/usbssp/usbssp-plat.c
>>
>> Is this file meant only for gadget controller or later even for host controller?
>> If only for gadget then this could be just called gadget-plat.c
>
> I'm not sure at this moment. I have not wondered at now how this driver will
> be integrated with XHCI driver and DRD driver.
>

OK.
>
>>> new file mode 100644
>>> index 000000000000..c048044148aa
>>> --- /dev/null
>>> +++ b/drivers/usb/usbssp/usbssp-plat.c
>>> @@ -0,0 +1,186 @@
>>> +// SPDX-License-Identifier: GPL-2.0
>>> +/*
>>> + * USBSSP device controller driver
>>> + *
>>> + * Copyright (C) 2018 Cadence.
>>> + *
>>> + * Author: Pawel Laszczak
>>> + *
>>> + */
>>> +
>>> +#include <linux/clk.h>
>>> +#include <linux/dma-mapping.h>
>>> +#include <linux/module.h>
>>> +#include <linux/pci.h>
>>> +#include <linux/of.h>
>>> +#include <linux/of_device.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/usb/phy.h>
>>> +#include <linux/slab.h>
>>> +#include <linux/acpi.h>
>>> +
>>> +#include "gadget.h"
>>> +
>>> +#define DRIVER_AUTHOR "Pawel Laszczak"
>>> +#define DRIVER_DESC "USBSSP Device Controller (USBSSP) Driver"
>>> +
>>> +#ifdef CONFIG_OF
>>> +
>>> +static const struct of_device_id usbssp_dev_of_match[] = {
>>> + {
>>> + .compatible = "Cadence, usbssp-dev",
>>
>> Avoid upper-case in compatible strings.
>>
>>> + },
>>> + {},
>>> +};
>>> +MODULE_DEVICE_TABLE(of, usbssp_dev_of_match); #endif
>>> +
>>> +int usbssp_is_platform(void)
>>> +{
>>> + return 1;
>>> +}
>>> +
>>> +static int usbssp_plat_probe(struct platform_device *pdev) {
>>> + struct device *dev = &pdev->dev;
>>> + struct resource *res;
>>> + struct usbssp_udc *usbssp_data;
>>> + int ret = 0;
>>> + int irq;
>>> + struct device *sysdev;
>>> +
>>> + irq = platform_get_irq(pdev, 0);
>>> + if (irq < 0) {
>>> + dev_err(&pdev->dev, "Incorrect IRQ number\n");
>>
>> IRQ number might be correct but might be some other issue.
>> You could just say "couldn't get IRQ"
>>
>>> + return -ENODEV;
>>
>> Also, we don't want to print any error message if we got a -EPROBE_DEFER.
>> And we need to return that instead of -ENODEV for deferred probing to
>> work.
>>
>>> + }
>>
>> So how about
>> if (irq < 0) {
>> if (irq != -EPROBE_DEFER)
>> dev_err(&pdev->dev, "couldn't get IRQ\n")
>>
>> return irq;
>> }
>>
>>> +
>>> + usbssp_data = devm_kzalloc(dev, sizeof(*usbssp_data),
>> GFP_KERNEL);
>>> + if (!usbssp_data)
>>> + return -ENOMEM;
>>> +
>>> + for (sysdev = &pdev->dev; sysdev; sysdev = sysdev->parent) {
>>> + if (is_of_node(sysdev->fwnode) ||
>>> + is_acpi_device_node(sysdev->fwnode))
>>> + break;
>>> +#ifdef CONFIG_PCI
>>> + else if (sysdev->bus == &pci_bus_type)
>>> + break;
>>> +#endif
>>> + }
>>
>> It is hard to understand what is this for loop doing exactly.
>>
>> xhci-plat.c seems to have this comment. You should add it above as well.
>> /*
>> * sysdev must point to a device that is known to the system firmware
>> * or PCI hardware. We handle these three cases here:
>> * 1. xhci_plat comes from firmware
>> * 2. xhci_plat is child of a device from firmware (dwc3-plat)
>> * 3. xhci_plat is grandchild of a pci device (dwc3-pci)
>> */
>>
>>
>>> +
>>> + if (!sysdev)
>>> + sysdev = &pdev->dev;
>>> +
>>> + /* Try to set 64-bit DMA first */
>>> + if (WARN_ON(!dev->dma_mask))
>>> + /* Platform did not initialize dma_mask */
>>> + ret = dma_coerce_mask_and_coherent(dev,
>>> + DMA_BIT_MASK(64));
>>> + else
>>> + ret = dma_set_mask_and_coherent(dev,
>> DMA_BIT_MASK(64));
>>> +
>>> + /* If seting 64-bit DMA mask fails, fall back to 32-bit DMA mask */
>>> + if (ret) {
>>> + ret = dma_set_mask_and_coherent(dev,
>> DMA_BIT_MASK(32));
>>> + if (ret)
>>> + return ret;
>>> + }
>>> +
>>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>> + usbssp_data->regs = devm_ioremap_resource(dev, res);
>>> +
>>> + if (IS_ERR(usbssp_data->regs)) {
>> dev_err() ?
>>
>>> + ret = PTR_ERR(usbssp_data->regs);
>>> + return ret;
>>> + }
>>> +
>>> + usbssp_data->rsrc_start = res->start;
>>> + usbssp_data->rsrc_len = resource_size(res);
>>> +
>>> + ret = devm_request_irq(dev, irq, usbssp_irq, IRQF_SHARED,
>>> + dev_name(dev), usbssp_data);
>>
>> devm_request_threaded_irq() ?
> Currently for deferred interrupts I use workqueue thread. Some time ago I tried use this function
> but I have some problem with it. I have plan to replace devm_request_irq with devm_request_threaded_irq.
>
> In USBSSP controller for device side I have big problem with handling commands. Handling command works like
> in XHCI controller. After invoking command driver has to wait for completion so it should sleep for some time.
> During waiting (e.g wait_for_completion) we need to enable the interrupts because we need to detect the completion event. Additionally some API function from USBSSP gadget driver are invoked by upper layer with disabled interrupts.
> In this situation we can't enable interrupt before invoking command and driver has to detect completion in other way.
> The concept taken from the host is not a perfect for device driver where most driver should works in interrupt context,
>

Felipe? any ideas here?

>>> +
>>> + if (ret < 0)
>>> + return ret;
>>> +
>>> + usbssp_data->irq = irq;
>>> + usbssp_data->dev = dev;
>>> + platform_set_drvdata(pdev, usbssp_data);
>>> + ret = usbssp_gadget_init(usbssp_data);
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +static int usbssp_plat_remove(struct platform_device *pdev) {
>>> + int ret = 0;
>>> + struct usbssp_udc *usbssp_data;
>>> +
>>> + usbssp_data = (struct usbssp_udc *)platform_get_drvdata(pdev);
>>> + ret = usbssp_gadget_exit(usbssp_data);
>>> + return ret;
>>> +
>>
>> move this blank line above return ret;
>>> +}
>>> +
>>> +static int __maybe_unused usbssp_plat_suspend(struct device *dev) {
>>> + struct usbssp_udc *usbssp_data = dev_get_drvdata(dev);
>>> +
>>> + return usbssp_suspend(usbssp_data, device_may_wakeup(dev)); }
>>> +
>>> +static int __maybe_unused usbssp_plat_resume(struct device *dev) {
>>> + struct usbssp_udc *usbssp_data = dev_get_drvdata(dev);
>>> +
>>> + return usbssp_resume(usbssp_data, 0); }
>>> +
>>> +static int __maybe_unused usbssp_plat_runtime_suspend(struct device
>>> +*dev) {
>>> + struct usbssp_udc *usbssp_data = dev_get_drvdata(dev);
>>> +
>>> + return usbssp_suspend(usbssp_data, true); }
>>> +
>>> +static int __maybe_unused usbssp_plat_runtime_resume(struct device
>>> +*dev) {
>>> + struct usbssp_udc *usbssp_data = dev_get_drvdata(dev);
>>> +
>>> + return usbssp_resume(usbssp_data, 0); }
>>> +
>>> +static const struct dev_pm_ops usbssp_plat_pm_ops = {
>>> + SET_SYSTEM_SLEEP_PM_OPS(usbssp_plat_suspend,
>> usbssp_plat_resume)
>>> +
>>> + SET_RUNTIME_PM_OPS(usbssp_plat_runtime_suspend,
>>> + usbssp_plat_runtime_resume,
>>> + NULL)
>>> +};
>>> +
>>> +static struct platform_driver usbssp_driver = {
>>> + .probe = usbssp_plat_probe,
>>> + .remove = usbssp_plat_remove,
>>> + .driver = {
>>> + .name = "usbssp-dev",
>>> + .pm = &usbssp_plat_pm_ops,
>>> + .of_match_table = of_match_ptr(usbssp_dev_of_match),
>>> + },
>>> +};
>>> +
>>> +static int __init usbssp_plat_init(void) {
>>> + return platform_driver_register(&usbssp_driver);
>>> +}
>>> +module_init(usbssp_plat_init);
>>> +
>>> +static void __exit usbssp_plat_exit(void) {
>>> + platform_driver_unregister(&usbssp_driver);
>>> +}
>>> +module_exit(usbssp_plat_exit);
>>> +
>>> +MODULE_ALIAS("platform:usbss-gadget");
>> usbssp-gadget?
>>
>> Why did you choose a different name for compatible? "usbssp-dev"
>> Would be nice to have it consistent.
>>
>>> +MODULE_DESCRIPTION("USBSSP' Device Controller (USBSSP) Driver");
>>
>> USBSSP, 2 times?
>>
>>> +MODULE_LICENSE("GPL v2");
>>>
> I will correct this.
>>
>> [1] build error
>>
>> CC [M] drivers/usb/usbssp/gadget-trace.o In file included from
>> drivers/usb/usbssp/gadget-trace.h:27:0,
>> from drivers/usb/usbssp/gadget-trace.c:13:
>> drivers/usb/usbssp/gadget.h:1683:1: error: unknown type name
>> ‘irqreturn_t’
>> irqreturn_t usbssp_irq(int irq, void *priv); ^~~~~~~~~~~ In file included from
>> ./include/trace/trace_events.h:394:0,
>> from ./include/trace/define_trace.h:96,
>> from drivers/usb/usbssp/gadget-trace.h:482,
>> from drivers/usb/usbssp/gadget-trace.c:13:
>> drivers/usb/usbssp/./gadget-trace.h: In function
>> ‘trace_raw_output_usbssp_log_request’:
>> drivers/usb/usbssp/./gadget-trace.h:201:477: warning: format ‘%llx’ expects
>> argument of type ‘long long unsigned int’, but argument 6 has type
>> ‘dma_addr_t {aka unsigned int}’ [-Wformat=]
>> DECLARE_EVENT_CLASS(usbssp_log_request,
>>
>> ^
>> scripts/Makefile.build:317: recipe for target 'drivers/usb/usbssp/gadget-
>> trace.o' failed
>> make[3]: *** [drivers/usb/usbssp/gadget-trace.o] Error 1
>> scripts/Makefile.build:558: recipe for target 'drivers/usb/usbssp' failed
>> make[2]: *** [drivers/usb/usbssp] Error 2
>> scripts/Makefile.build:558: recipe for target 'drivers/usb' failed
>> make[1]: *** [drivers/usb] Error 2
>> Makefile:1029: recipe for target 'drivers' failed
>> make: *** [drivers] Error 2
>>
>> --
> cheers,
> Pawel
>

--
cheers,
-roger

Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki

2018-08-03 10:19:09

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH 05/31] usb: usbssp: Added first part of initialization sequence.

Hi,

On 19/07/18 20:57, Pawel Laszczak wrote:
> Patch adds some initialization function. The initialization sequence
> is quite complicated and this patch implements it only partially.
> Initialization will be completed in next few patches.
>
> Patch introduce three new files:
> 1. gadget-dbg.c - file contains functions used for debugging purpose.
> 2. gadget-ext-caps.h - holds macro definition related to
> Extended Capabilities
> 3. gadget-if - file implements stuff related to upper layer
> (e.g usb_ep_ops, usb_gadget_ops interface).
>
> Signed-off-by: Pawel Laszczak <[email protected]>
> ---
> drivers/usb/usbssp/Makefile | 1 +
> drivers/usb/usbssp/gadget-dbg.c | 30 ++++
> drivers/usb/usbssp/gadget-ext-caps.h | 53 ++++++
> drivers/usb/usbssp/gadget-if.c | 24 +++
> drivers/usb/usbssp/gadget.c | 242 +++++++++++++++++++++++++++
> drivers/usb/usbssp/gadget.h | 15 ++
> 6 files changed, 365 insertions(+)
> create mode 100644 drivers/usb/usbssp/gadget-dbg.c
> create mode 100644 drivers/usb/usbssp/gadget-ext-caps.h
> create mode 100644 drivers/usb/usbssp/gadget-if.c
>
> diff --git a/drivers/usb/usbssp/Makefile b/drivers/usb/usbssp/Makefile
> index d85f15afb51c..0606f3c63cd0 100644
> --- a/drivers/usb/usbssp/Makefile
> +++ b/drivers/usb/usbssp/Makefile
> @@ -5,6 +5,7 @@ CFLAGS_gadget-trace.o := -I$(src)
> obj-$(CONFIG_USB_USBSSP_GADGET) += usbssp.o
> usbssp-y := usbssp-plat.o gadget-ring.o \
> gadget.o
> + gadget-dbg.o
>
> ifneq ($(CONFIG_TRACING),)
> usbssp-y += gadget-trace.o
> diff --git a/drivers/usb/usbssp/gadget-dbg.c b/drivers/usb/usbssp/gadget-dbg.c
> new file mode 100644
> index 000000000000..277617d1a996
> --- /dev/null
> +++ b/drivers/usb/usbssp/gadget-dbg.c
> @@ -0,0 +1,30 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * USBSSP device controller driver
> + *
> + * Copyright (C) 2018 Cadence.
> + *
> + * Author: Pawel Laszczak
> + *
> + * A lot of code based on Linux XHCI driver.
> + * Origin: Copyright (C) 2008 Intel Corp
> + */
> +
> +#include "gadget.h"
> +
> +void usbssp_dbg_trace(struct usbssp_udc *usbssp_data,
> + void (*trace)(struct va_format *),
> + const char *fmt, ...)
> +{
> + struct va_format vaf;
> + va_list args;
> +
> + va_start(args, fmt);
> + vaf.fmt = fmt;
> + vaf.va = &args;
> + dev_dbg(usbssp_data->dev, "%pV\n", &vaf);

dev_dbg will mess up with timings to be useful.
Why not just use one of the trace events you defined in gadget-trace.h?

> + trace(&vaf);
> + va_end(args);
> +}
> +EXPORT_SYMBOL_GPL(usbssp_dbg_trace);
> +
> diff --git a/drivers/usb/usbssp/gadget-ext-caps.h b/drivers/usb/usbssp/gadget-ext-caps.h
> new file mode 100644
> index 000000000000..2bf327046376
> --- /dev/null
> +++ b/drivers/usb/usbssp/gadget-ext-caps.h
> @@ -0,0 +1,53 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * USBSSP device controller driver
> + *
> + * Copyright (C) 2018 Cadence.
> + *
> + * Author: Pawel Laszczak
> + *
> + * A lot of code based on Linux XHCI driver.
> + * Origin: Copyright (C) 2008 Intel Corp
> + */
> +
> +/* Up to 16 ms to halt an DC */
> +#define USBSSP_MAX_HALT_USEC (16*1000)
> +
> +/* DC not running - set to 1 when run/stop bit is cleared. */
> +#define USBSSP_STS_HALT BIT(0)
> +
> +/* HCCPARAMS offset from PCI base address */
> +#define USBSSP_HCC_PARAMS_OFFSET 0x10
> +/* HCCPARAMS contains the first extended capability pointer */
> +#define USBSSP_HCC_EXT_CAPS(p) (((p)>>16)&0xffff)
> +
> +/* Command and Status registers offset from the Operational Registers address */
> +#define USBSSP_CMD_OFFSET 0x00
> +#define USBSSP_STS_OFFSET 0x04
> +
> +/* Capability Register */
> +/* bits 7:0 - how long is the Capabilities register */
> +#define USBSSP_HC_LENGTH(p) (((p)>>00)&0x00ff)
> +
> +/* Extended capability register fields */
> +#define USBSSP_EXT_CAPS_ID(p) (((p)>>0)&0xff)
> +#define USBSSP_EXT_CAPS_NEXT(p) (((p)>>8)&0xff)
> +#define v_EXT_CAPS_VAL(p) ((p)>>16)
> +/* Extended capability IDs - ID 0 reserved */
> +#define USBSSP_EXT_CAPS_PROTOCOL 2
> +
> +/* USB 2.0 hardware LMP capability*/
> +#define USBSSP_HLC BIT(19)
> +#define USBSSP_BLC BIT(20)
> +
> +/* command register values to disable interrupts and halt the DC */
> +/* start/stop DC execution - do not write unless DC is halted*/
> +#define USBSSP_CMD_RUN BIT(0)
> +/* Event Interrupt Enable - get irq when EINT bit is set in USBSTS register */
> +#define USBSSP_CMD_EIE BIT(2)
> +/* Host System Error Interrupt Enable - get irq when HSEIE bit set in USBSTS */
> +#define USBSSP_CMD_HSEIE BIT(3)
> +/* Enable Wrap Event - '1' means DC generates an event when MFINDEX wraps. */
> +#define USBSSP_CMD_EWE BIT(10)
> +
> +#define USBSSP_IRQS (USBSSP_CMD_EIE | USBSSP_CMD_HSEIE | USBSSP_CMD_EWE)
> diff --git a/drivers/usb/usbssp/gadget-if.c b/drivers/usb/usbssp/gadget-if.c
> new file mode 100644
> index 000000000000..d53e0fb65299
> --- /dev/null
> +++ b/drivers/usb/usbssp/gadget-if.c
> @@ -0,0 +1,24 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * USBSSP device controller driver
> + *
> + * Copyright (C) 2018 Cadence.
> + *
> + * Author: Pawel Laszczak
> + *
> + */
> +
> +#include <linux/usb/gadget.h>
> +#include <linux/usb/composite.h>
> +#include "gadget.h"
> +
> +int usbssp_gadget_init_endpoint(struct usbssp_udc *usbssp_data)
> +{
> + /*TODO: it has to be implemented*/
> + return 0;
> +}
> +
> +void usbssp_gadget_free_endpoint(struct usbssp_udc *usbssp_data)
> +{
> + /*TODO: it has to be implemented*/
> +}
> diff --git a/drivers/usb/usbssp/gadget.c b/drivers/usb/usbssp/gadget.c
> index 2f60d7dd1fe4..338ec2ec18b1 100644
> --- a/drivers/usb/usbssp/gadget.c
> +++ b/drivers/usb/usbssp/gadget.c
> @@ -23,6 +23,107 @@
> #include "gadget-trace.h"
> #include "gadget.h"
>
> +
> +/*

/**

> + * usbssp_handshake - spin reading dc until handshake completes or fails
> + * @ptr: address of dc 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 usbssp_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;
> +}
> +
> +/*
> + * Disable interrupts and begin the DC halting process.
> + */
> +void usbssp_quiesce(struct usbssp_udc *usbssp_data)
> +{
> + u32 halted;
> + u32 cmd;
> + u32 mask;
> +
> + mask = ~(u32)(USBSSP_IRQS);
> +
> + halted = readl(&usbssp_data->op_regs->status) & STS_HALT;
> + if (!halted)
> + mask &= ~CMD_RUN;
> +
> + cmd = readl(&usbssp_data->op_regs->command);
> + cmd &= mask;
> + writel(cmd, &usbssp_data->op_regs->command);
> +}
> +
> +/*
> + * Force DC into halt state.
> + *
> + * Disable any IRQs and clear the run/stop bit.
> + * USBSSP will complete any current and actively pipelined transactions, and
> + * should halt within 16 ms of the run/stop bit being cleared.
> + * Read DC Halted bit in the status register to see when the DC is finished.
> + */
> +int usbssp_halt(struct usbssp_udc *usbssp_data)
> +{
> + int ret;
> +
> + usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
> + "// Halt the USBSSP");
> + usbssp_quiesce(usbssp_data);
> +
> + ret = usbssp_handshake(&usbssp_data->op_regs->status,
> + STS_HALT, STS_HALT, USBSSP_MAX_HALT_USEC);
> +
> + if (!ret) {
> + dev_warn(usbssp_data->dev, "Device halt failed, %d\n", ret);
> + return ret;
> + }
> +
> + usbssp_data->usbssp_state |= USBSSP_STATE_HALTED;
> + usbssp_data->cmd_ring_state = CMD_RING_STATE_STOPPED;
> + return ret;
> +}
> +
> +
> +/*
> + * Initialize memory for gadget driver and USBSSP (one-time init).
> + *
> + * Program the PAGESIZE register, initialize the device context array, create
> + * device contexts, set up a command ring segment (or two?), create event
> + * ring (one for now).
> + */
> +int usbssp_init(struct usbssp_udc *usbssp_data)
> +{
> + int retval = 0;
> +
> + spin_lock_init(&usbssp_data->lock);
> + spin_lock_init(&usbssp_data->irq_thread_lock);
> +
> + /*TODO: memory initialization*/
> + //retval = usbssp_mem_init(usbssp_data, GFP_KERNEL);
> +
> + return retval;
> +}
> +
> #ifdef CONFIG_PM
> /*
> * Stop DC (not bus-specific)
> @@ -50,9 +151,146 @@ int usbssp_resume(struct usbssp_udc *usbssp_data, bool hibernated)
>
> #endif /* CONFIG_PM */
>
> +int usbssp_gen_setup(struct usbssp_udc *usbssp_data)
> +{
> + int retval;
> +
> + mutex_init(&usbssp_data->mutex);
> +
> + usbssp_data->cap_regs = usbssp_data->regs;
> + usbssp_data->op_regs = usbssp_data->regs +
> + HC_LENGTH(readl(&usbssp_data->cap_regs->hc_capbase));
> +
> + usbssp_data->run_regs = usbssp_data->regs +
> + (readl(&usbssp_data->cap_regs->run_regs_off) & RTSOFF_MASK);
> + /* Cache read-only capability registers */
> + usbssp_data->hcs_params1 = readl(&usbssp_data->cap_regs->hcs_params1);
> + usbssp_data->hcs_params2 = readl(&usbssp_data->cap_regs->hcs_params2);
> + usbssp_data->hcs_params3 = readl(&usbssp_data->cap_regs->hcs_params3);
> + usbssp_data->hcc_params = readl(&usbssp_data->cap_regs->hc_capbase);
> + usbssp_data->hci_version = HC_VERSION(usbssp_data->hcc_params);
> + usbssp_data->hcc_params = readl(&usbssp_data->cap_regs->hcc_params);
> + usbssp_data->hcc_params2 = readl(&usbssp_data->cap_regs->hcc_params2);
> +
> + /* Make sure the Device Controller is halted. */
> + retval = usbssp_halt(usbssp_data);
> + if (retval)
> + return retval;
> +
> + dev_dbg(usbssp_data->dev, "Resetting Device Controller\n");

do you really need all these dev_dbg prints? You should just fit them to one
of the trace event classes that you already have or add a new one if needed.

> + /* Reset the internal DC memory state and registers. */
> + /*TODO: add implementation of usbssp_reset function*/
> + //retval = usbssp_reset(usbssp_data);
> + if (retval)
> + return retval;
> + dev_dbg(usbssp_data->dev, "Reset complete\n");
> +
> + /* Set dma_mask and coherent_dma_mask to 64-bits,
> + * if USBSSP supports 64-bit addressing
> + */
> + if (HCC_64BIT_ADDR(usbssp_data->hcc_params) &&
> + !dma_set_mask(usbssp_data->dev, DMA_BIT_MASK(64))) {
> + dev_dbg(usbssp_data->dev, "Enabling 64-bit DMA addresses.\n");
> + dma_set_coherent_mask(usbssp_data->dev, DMA_BIT_MASK(64));
> + } else {
> + /*
> + * This is to avoid error in cases where a 32-bit USB
> + * controller is used on a 64-bit capable system.
> + */
> + retval = dma_set_mask(usbssp_data->dev, DMA_BIT_MASK(32));
> + if (retval)
> + return retval;
> + dev_dbg(usbssp_data->dev, "Enabling 32-bit DMA addresses.\n");
> + dma_set_coherent_mask(usbssp_data->dev, DMA_BIT_MASK(32));
> + }
> +
> + /* Initialize USBSSP controller data structures. */
> + retval = usbssp_init(usbssp_data);
> + if (retval)
> + return retval;
> +
> + dev_info(usbssp_data->dev, "USBSSP params 0x%08x USBSSP version 0x%x\n",
> + usbssp_data->hcc_params, usbssp_data->hci_version);
> +
> + return 0;
> +}
> +
> +/*
> + * gadget-if.c file is part of gadget.c file and implements interface
> + * for gadget driver
> + */
> +#include "gadget-if.c"
> +

All includes should be together at the beginning of the file.

> int usbssp_gadget_init(struct usbssp_udc *usbssp_data)
> {
> int ret;
> +
> + /*
> + * Check the compiler generated sizes of structures that must be laid
> + * out in specific ways for hardware access.
> + */
> + BUILD_BUG_ON(sizeof(struct usbssp_doorbell_array) != 2*32/8);
> + BUILD_BUG_ON(sizeof(struct usbssp_slot_ctx) != 8*32/8);
> + BUILD_BUG_ON(sizeof(struct usbssp_ep_ctx) != 8*32/8);
> + /* usbssp_device has eight fields, and also
> + * embeds one usbssp_slot_ctx and 31 usbssp_ep_ctx
> + */
> + BUILD_BUG_ON(sizeof(struct usbssp_stream_ctx) != 4*32/8);
> + BUILD_BUG_ON(sizeof(union usbssp_trb) != 4*32/8);
> + BUILD_BUG_ON(sizeof(struct usbssp_erst_entry) != 4*32/8);
> + BUILD_BUG_ON(sizeof(struct usbssp_cap_regs) != 8*32/8);
> + BUILD_BUG_ON(sizeof(struct usbssp_intr_reg) != 8*32/8);
> + /* usbssp_run_regs has eight fields and embeds 128 usbssp_intr_regs */
> + BUILD_BUG_ON(sizeof(struct usbssp_run_regs) != (8+8*128)*32/8);
> +
> + /* fill gadget fields */
> + /*TODO: implements usbssp_gadget_ops object*/
> + //usbssp_data->gadget.ops = &usbssp_gadget_ops;
> + usbssp_data->gadget.name = "usbssp-gadget";
> + usbssp_data->gadget.max_speed = USB_SPEED_SUPER_PLUS;
> + usbssp_data->gadget.speed = USB_SPEED_UNKNOWN;
> + usbssp_data->gadget.sg_supported = true;
> + usbssp_data->gadget.lpm_capable = 1;
> +
> + usbssp_data->setup_buf = kzalloc(USBSSP_EP0_SETUP_SIZE, GFP_KERNEL);
> + if (!usbssp_data->setup_buf)
> + return -ENOMEM;
> +
> + /*USBSSP support not aligned buffer but this option
> + * improve performance of this controller.
> + */

Multi-line comment formatting. Checkpach should complain.

> + usbssp_data->gadget.quirk_ep_out_aligned_size = true;
> + ret = usbssp_gen_setup(usbssp_data);
> + if (ret < 0) {
> + dev_err(usbssp_data->dev,
> + "Generic initialization failed with error code%d\n",
> + ret);
> + goto err3;
> + }
> +
> + ret = usbssp_gadget_init_endpoint(usbssp_data);
> + if (ret < 0) {
> + dev_err(usbssp_data->dev, "failed to initialize endpoints\n");
> + goto err1;
> + }
> +
> + ret = usb_add_gadget_udc(usbssp_data->dev, &usbssp_data->gadget);
> +
> + if (ret) {
> + dev_err(usbssp_data->dev, "failed to register udc\n");
> + goto err2;
> + }
> +
> + return ret;
> +err2:
> + usbssp_gadget_free_endpoint(usbssp_data);
> +err1:
> + usbssp_halt(usbssp_data);
> + /*TODO add implementation of usbssp_reset function*/
> + //usbssp_reset(usbssp_data);
> + /*TODO add implementation of freeing memory*/
> + //usbssp_mem_cleanup(usbssp_data);
> +err3:
> return ret;
> }
>
> @@ -60,5 +298,9 @@ int usbssp_gadget_exit(struct usbssp_udc *usbssp_data)
> {
> int ret = 0;
>
> + usb_del_gadget_udc(&usbssp_data->gadget);
> + usbssp_gadget_free_endpoint(usbssp_data);
> + /*TODO: add usbssp_stop implementation*/
> + //usbssp_stop(usbssp_data);
> return ret;
> }
> diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h
> index 55e20795d900..5d8918f8da84 100644
> --- a/drivers/usb/usbssp/gadget.h
> +++ b/drivers/usb/usbssp/gadget.h
> @@ -12,8 +12,10 @@
> #ifndef __LINUX_USBSSP_GADGET_H
> #define __LINUX_USBSSP_GADGET_H
>
> +#include <linux/irq.h>
> #include <linux/io-64-nonatomic-lo-hi.h>
> #include <linux/usb/gadget.h>
> +#include "gadget-ext-caps.h"
>
> /* Max number slots - only 1 is allowed */
> #define DEV_MAX_SLOTS 1
> @@ -1676,7 +1678,18 @@ static inline void usbssp_write_64(struct usbssp_udc *usbssp_data,
> lo_hi_writeq(val, regs);
> }
>
> +/* USBSSP memory management */
> +void usbssp_dbg_trace(struct usbssp_udc *usbssp_data,
> + void (*trace)(struct va_format *),
> + const char *fmt, ...);

what has this function to do with memory management?

> /* USBSSP Device controller glue */
> +void usbssp_bottom_irq(struct work_struct *work);

usbssp_bottom_irq() wasn't defined in this patch.

> +int usbssp_init(struct usbssp_udc *usbssp_data);
> +void usbssp_stop(struct usbssp_udc *usbssp_data);
> +int usbssp_handshake(void __iomem *ptr, u32 mask, u32 done, int usec);
> +void usbssp_quiesce(struct usbssp_udc *usbssp_data);
> +extern int usbssp_reset(struct usbssp_udc *usbssp_data);
> +
> int usbssp_suspend(struct usbssp_udc *usbssp_data, bool do_wakeup);
> int usbssp_resume(struct usbssp_udc *usbssp_data, bool hibernated);
>
> @@ -1689,6 +1702,8 @@ dma_addr_t usbssp_trb_virt_to_dma(struct usbssp_segment *seg,
> /* USBSSP gadget interface*/
> int usbssp_gadget_init(struct usbssp_udc *usbssp_data);
> int usbssp_gadget_exit(struct usbssp_udc *usbssp_data);
> +void usbssp_gadget_free_endpoint(struct usbssp_udc *usbssp_data);
> +int usbssp_gadget_init_endpoint(struct usbssp_udc *usbssp_data);
>
> static inline char *usbssp_slot_state_string(u32 state)
> {
>

--
cheers,
-roger

Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki

2018-08-03 10:44:24

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH 06/31] usb: usbssp: added template functions used by upper layer.

On 19/07/18 20:57, Pawel Laszczak wrote:
> Patch adds some functionality for initialization process.
> It adds to driver usbssp_reset and usbssp_start function.
>
> Next elements added are objects usbssp_gadget_ep0_ops,
> usbssp_gadget_ep_ops and usbssp_gadget_ops. These objects
> constitute the interface used by gadget subsystem.
> At this moment functions related to these objects are empty
> and do nothing.
>
> This patch also implements usbssp_gadget_init_endpoint and
> usbssp_gadget_free_endpoint used during initialization.
>
> Signed-off-by: Pawel Laszczak <[email protected]>
> ---
> drivers/usb/usbssp/gadget-ext-caps.h | 3 +
> drivers/usb/usbssp/gadget-if.c | 269 ++++++++++++++++++++++++++-
> drivers/usb/usbssp/gadget.c | 84 ++++++++-
> 3 files changed, 350 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/usb/usbssp/gadget-ext-caps.h b/drivers/usb/usbssp/gadget-ext-caps.h
> index 2bf327046376..86c0ce331037 100644
> --- a/drivers/usb/usbssp/gadget-ext-caps.h
> +++ b/drivers/usb/usbssp/gadget-ext-caps.h
> @@ -51,3 +51,6 @@
> #define USBSSP_CMD_EWE BIT(10)
>
> #define USBSSP_IRQS (USBSSP_CMD_EIE | USBSSP_CMD_HSEIE | USBSSP_CMD_EWE)
> +
> +/* true: Controller Not Ready to accept doorbell or op reg writes after reset */
> +#define USBSSP_STS_CNR BIT(11)
> diff --git a/drivers/usb/usbssp/gadget-if.c b/drivers/usb/usbssp/gadget-if.c
> index d53e0fb65299..70def978b085 100644
> --- a/drivers/usb/usbssp/gadget-if.c
> +++ b/drivers/usb/usbssp/gadget-if.c
> @@ -12,13 +12,278 @@
> #include <linux/usb/composite.h>
> #include "gadget.h"
>
> +static int usbssp_gadget_ep_enable(struct usb_ep *ep,
> + const struct usb_endpoint_descriptor *desc)
> +{
> + struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
> + int ret = 0;
> +
> + if (!ep_priv)
> + return -EINVAL;
> +
> + /*TODO: implements this function*/
> + return ret;
> +}
> +
> +int usbssp_gadget_ep_disable(struct usb_ep *ep)
> +{
> + struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
> + int ret = 0;
> +
> + if (!ep_priv)
> + return -EINVAL;
> +
> + /*TODO: implements this function*/
> + return ret;
> +}
> +
> +static struct usb_request *usbssp_gadget_ep_alloc_request(struct usb_ep *ep,
> + gfp_t gfp_flags)
> +{
> + struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
> +
> + if (!ep_priv)
> + return NULL;
> +
> + /*TODO: implements this function*/
> + return NULL;
> +}
> +
> +static void usbssp_gadget_ep_free_request(struct usb_ep *ep,
> + struct usb_request *request)
> +{
> + struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
> +
> + if (!ep_priv)
> + return;
> +
> + /*TODO: implements this function*/
> +}
> +
> +static int usbssp_gadget_ep_queue(struct usb_ep *ep,
> + struct usb_request *request,
> + gfp_t gfp_flags)
> +{
> + struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
> + int ret = 0;
> +
> + if (!ep_priv)
> + return -EINVAL;
> +
> + /*TODO: implements this function*/
> + return ret;
> +}
> +
> +static int usbssp_gadget_ep_dequeue(struct usb_ep *ep,
> + struct usb_request *request)
> +{
> + struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
> + int ret = 0;
> +
> + if (!ep_priv)
> + return -EINVAL;
> +
> + /*TODO: implements this function*/
> + return ret;
> +}
> +
> +static int usbssp_gadget_ep_set_halt(struct usb_ep *ep, int value)
> +{
> + struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
> + int ret = 0;
> +
> + if (!ep_priv)
> + return -EINVAL;
> +
> + /*TODO: implements this function*/
> + return ret;
> +}
> +
> +static int usbssp_gadget_ep_set_wedge(struct usb_ep *ep)
> +{
> + struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
> + int ret = 0;
> +
> + if (!ep_priv)
> + return -EINVAL;
> +
> + /*TODO: implements this function*/
> + return ret;
> +}
> +
> +static const struct usb_ep_ops usbssp_gadget_ep0_ops = {
> + .enable = usbssp_gadget_ep_enable,
> + .disable = usbssp_gadget_ep_disable,
> + .alloc_request = usbssp_gadget_ep_alloc_request,
> + .free_request = usbssp_gadget_ep_free_request,
> + .queue = usbssp_gadget_ep_queue,
> + .dequeue = usbssp_gadget_ep_dequeue,
> + .set_halt = usbssp_gadget_ep_set_halt,
> + .set_wedge = usbssp_gadget_ep_set_wedge,
> +};
> +
> +static const struct usb_ep_ops usbssp_gadget_ep_ops = {
> + .enable = usbssp_gadget_ep_enable,
> + .disable = usbssp_gadget_ep_disable,
> + .alloc_request = usbssp_gadget_ep_alloc_request,
> + .free_request = usbssp_gadget_ep_free_request,
> + .queue = usbssp_gadget_ep_queue,
> + .dequeue = usbssp_gadget_ep_dequeue,
> + .set_halt = usbssp_gadget_ep_set_halt,
> + .set_wedge = usbssp_gadget_ep_set_wedge,
> +};
> +
> +static struct usb_endpoint_descriptor usbssp_gadget_ep0_desc = {
> + .bLength = USB_DT_ENDPOINT_SIZE,
> + .bDescriptorType = USB_DT_ENDPOINT,
> + .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
> +};
> +
> +static int usbssp_gadget_start(struct usb_gadget *g,
> + struct usb_gadget_driver *driver)
> +{
> + struct usbssp_udc *usbssp_data = gadget_to_usbssp(g);
> + int ret = 0;
> +
> + if (usbssp_data->gadget_driver) {
> + dev_err(usbssp_data->dev, "%s is already bound to %s\n",
> + usbssp_data->gadget.name,
> + usbssp_data->gadget_driver->driver.name);
> + ret = -EBUSY;
> + }
> +
> + /*TODO: add implementation*/
> + return ret;
> +}
> +
> +static int usbssp_gadget_stop(struct usb_gadget *g)
> +{
> + struct usbssp_udc *usbssp_data = gadget_to_usbssp(g);
> +
> + if (!usbssp_data)
> + return -EINVAL;
> + /*TODO: add implementation*/
> + return 0;
> +}
> +
> +static int usbssp_gadget_get_frame(struct usb_gadget *g)
> +{
> + struct usbssp_udc *usbssp_data = gadget_to_usbssp(g);
> +
> + if (!usbssp_data)
> + return -EINVAL;
> +
> + /*TODO: add implementation*/
> + return 0;
> +}
> +
> +static int usbssp_gadget_wakeup(struct usb_gadget *g)
> +{
> + struct usbssp_udc *usbssp_data = gadget_to_usbssp(g);
> +
> + if (!usbssp_data)
> + return -EINVAL;
> +
> + /*TODO: add implementation*/
> + return 0;
> +}
> +
> +static int usbssp_gadget_set_selfpowered(struct usb_gadget *g,
> + int is_selfpowered)
> +{
> + struct usbssp_udc *usbssp_data = gadget_to_usbssp(g);
> +
> + if (!usbssp_data)
> + return -EINVAL;
> +
> + /*TODO: add implementation*/
> + return 0;
> +}
> +
> +static const struct usb_gadget_ops usbssp_gadget_ops = {
> + .get_frame = usbssp_gadget_get_frame,
> + .wakeup = usbssp_gadget_wakeup,
> + .set_selfpowered = usbssp_gadget_set_selfpowered,
> + .udc_start = usbssp_gadget_start,
> + .udc_stop = usbssp_gadget_stop,
> +};
> +
> int usbssp_gadget_init_endpoint(struct usbssp_udc *usbssp_data)

Since we're initializing all endpoints this function should be named usbssp_gadget_init_endpoints().

> {
> - /*TODO: it has to be implemented*/
> + int i = 0;
> + struct usbssp_ep *ep_priv;
> +
> + usbssp_data->num_endpoints = USBSSP_ENDPOINTS_NUM;
> + INIT_LIST_HEAD(&usbssp_data->gadget.ep_list);
> +
> + for (i = 1; i < usbssp_data->num_endpoints; i++) {
> + bool direction = i & 1; /*start from OUT endpoint*/
> + u8 epnum = (i >> 1);
> +
> + ep_priv = &usbssp_data->devs.eps[i-1];
> + ep_priv->usbssp_data = usbssp_data;
> + ep_priv->number = epnum;
> + ep_priv->direction = direction; /*0 for OUT, 1 for IN*/
> +
> + snprintf(ep_priv->name, sizeof(ep_priv->name), "ep%d%s", epnum,
> + (ep_priv->direction) ? "in" : "out");
> +
> + ep_priv->endpoint.name = ep_priv->name;
> +
> + if (ep_priv->number < 2) {
> + ep_priv->endpoint.desc = &usbssp_gadget_ep0_desc;
> + ep_priv->endpoint.comp_desc = NULL;
> + }
> +
> + if (epnum == 0) {
> + /*EP0 is bidirectional endpoint*/
> + usb_ep_set_maxpacket_limit(&ep_priv->endpoint, 512);
> + dev_dbg(usbssp_data->dev,
> + "Initializing %s, MaxPack: %04x Type: Ctrl\n",
> + ep_priv->name, 512);
> + ep_priv->endpoint.maxburst = 1;
> + ep_priv->endpoint.ops = &usbssp_gadget_ep0_ops;
> + ep_priv->endpoint.caps.type_control = true;
> +
> + usbssp_data->usb_req_ep0_in.epnum = ep_priv->number;
> + usbssp_data->usb_req_ep0_in.dep = ep_priv;
> +
> + if (!epnum)
> + usbssp_data->gadget.ep0 = &ep_priv->endpoint;
> + } else {
> + usb_ep_set_maxpacket_limit(&ep_priv->endpoint, 1024);
> + ep_priv->endpoint.maxburst = 15;
> + ep_priv->endpoint.ops = &usbssp_gadget_ep_ops;
> + list_add_tail(&ep_priv->endpoint.ep_list,
> + &usbssp_data->gadget.ep_list);
> + ep_priv->endpoint.caps.type_iso = true;
> + ep_priv->endpoint.caps.type_bulk = true;
> + ep_priv->endpoint.caps.type_int = true;
> +
> + }
> +
> + ep_priv->endpoint.caps.dir_in = direction;
> + ep_priv->endpoint.caps.dir_out = !direction;

Since you start from 1, ep0 will only be initialized as dir_in.
Is it better to represent EP0in and EP0out separately?
If so you can start i from 0 and use
ep_priv = &usbssp_data->devs.eps[i];

This should fix the direction. eps[0] will be ep0out and eps[1] will be ep0in.

> +
> + dev_dbg(usbssp_data->dev, "Init %s, MaxPack: %04x SupType:"
> + " INT/BULK/ISOC , SupDir %s\n",
> + ep_priv->name, 1024,
> + (ep_priv->endpoint.caps.dir_in) ? "IN" : "OUT");
> +
> + INIT_LIST_HEAD(&ep_priv->pending_list);
> + }
> return 0;
> }
>
> void usbssp_gadget_free_endpoint(struct usbssp_udc *usbssp_data)
> {
> - /*TODO: it has to be implemented*/
> + int i;
> + struct usbssp_ep *ep_priv;
> +
> + for (i = 0; i < usbssp_data->num_endpoints; i++) {
> + ep_priv = &usbssp_data->devs.eps[i];
> +

if you start i from 1 then you can skip the if().

> + if (ep_priv->number != 0)
> + list_del(&ep_priv->endpoint.ep_list);
> + }
> }
> diff --git a/drivers/usb/usbssp/gadget.c b/drivers/usb/usbssp/gadget.c
> index 338ec2ec18b1..195f5777cf8a 100644
> --- a/drivers/usb/usbssp/gadget.c
> +++ b/drivers/usb/usbssp/gadget.c
> @@ -103,6 +103,83 @@ int usbssp_halt(struct usbssp_udc *usbssp_data)
> return ret;
> }
>
> +/*
> + * Set the run bit and wait for the device to be running.
> + */
> +int usbssp_start(struct usbssp_udc *usbssp_data)
> +{
> + u32 temp;
> + int ret;
> +
> + temp = readl(&usbssp_data->op_regs->command);
> + temp |= (CMD_RUN | CMD_DEVEN);
> + usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
> + "// Turn on USBSSP, cmd = 0x%x.", temp);
> + writel(temp, &usbssp_data->op_regs->command);
> +
> + /*
> + * Wait for the HCHalted Staus bit to be 0 to indicate the device is
> + * running.
> + */
> + ret = usbssp_handshake(&usbssp_data->op_regs->status,
> + STS_HALT, 0, USBSSP_MAX_HALT_USEC);
> +
> + if (ret == -ETIMEDOUT)
> + dev_err(usbssp_data->dev, "Device took too long to start, waited %u microseconds.\n",
> + USBSSP_MAX_HALT_USEC);
> + if (!ret)
> + /* clear state flags. Including dying, halted or removing */
> + usbssp_data->usbssp_state = 0;
> +
> + return ret;
> +}
> +
> +/*
> + * Reset a halted DC.
> + *
> + * This resets pipelines, timers, counters, state machines, etc.
> + * Transactions will be terminated immediately, and operational registers
> + * will be set to their defaults.
> + */
> +int usbssp_reset(struct usbssp_udc *usbssp_data)
> +{
> + u32 command;
> + u32 state;
> + int ret;
> +
> + state = readl(&usbssp_data->op_regs->status);
> +
> + if (state == ~(u32)0) {
> + dev_warn(usbssp_data->dev, "Device not accessible, reset failed.\n");
> + return -ENODEV;
> + }
> +
> + if ((state & STS_HALT) == 0) {
> + dev_warn(usbssp_data->dev, "DC not halted, aborting reset.\n");

DC is not a familiar abbreviation. Mabe just use Controller? or Device controller.

> + return 0;
> + }
> +
> + usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init, "// Reset the DC");
> + command = readl(&usbssp_data->op_regs->command);
> + command |= CMD_RESET;
> + writel(command, &usbssp_data->op_regs->command);
> +
> + ret = usbssp_handshake(&usbssp_data->op_regs->command,
> + CMD_RESET, 0, 10 * 1000 * 1000);
> +
> + if (ret)
> + return ret;
> + usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
> + "Wait for controller to be ready for doorbell rings");
> + /*
> + * USBSSP cannot write to any doorbells or operational registers other
> + * than status until the "Controller Not Ready" flag is cleared.
> + */
> + ret = usbssp_handshake(&usbssp_data->op_regs->status,
> + STS_CNR, 0, 10 * 1000 * 1000);
> +
> + return ret;
> +}
>
> /*
> * Initialize memory for gadget driver and USBSSP (one-time init).
> @@ -179,8 +256,7 @@ int usbssp_gen_setup(struct usbssp_udc *usbssp_data)
>
> dev_dbg(usbssp_data->dev, "Resetting Device Controller\n");
> /* Reset the internal DC memory state and registers. */
> - /*TODO: add implementation of usbssp_reset function*/
> - //retval = usbssp_reset(usbssp_data);
> + retval = usbssp_reset(usbssp_data);
> if (retval)
> return retval;
> dev_dbg(usbssp_data->dev, "Reset complete\n");
> @@ -244,8 +320,7 @@ int usbssp_gadget_init(struct usbssp_udc *usbssp_data)
> BUILD_BUG_ON(sizeof(struct usbssp_run_regs) != (8+8*128)*32/8);
>
> /* fill gadget fields */
> - /*TODO: implements usbssp_gadget_ops object*/
> - //usbssp_data->gadget.ops = &usbssp_gadget_ops;
> + usbssp_data->gadget.ops = &usbssp_gadget_ops;
> usbssp_data->gadget.name = "usbssp-gadget";
> usbssp_data->gadget.max_speed = USB_SPEED_SUPER_PLUS;
> usbssp_data->gadget.speed = USB_SPEED_UNKNOWN;
> @@ -288,6 +363,7 @@ int usbssp_gadget_init(struct usbssp_udc *usbssp_data)
> usbssp_halt(usbssp_data);
> /*TODO add implementation of usbssp_reset function*/
> //usbssp_reset(usbssp_data);
> + usbssp_reset(usbssp_data);
> /*TODO add implementation of freeing memory*/
> //usbssp_mem_cleanup(usbssp_data);
> err3:
>

--
cheers,
-roger

Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki

2018-08-04 06:38:25

by Pawel Laszczak

[permalink] [raw]
Subject: RE: [PATCH 06/31] usb: usbssp: added template functions used by upper layer.

> > Patch adds some functionality for initialization process.
> > It adds to driver usbssp_reset and usbssp_start function.
> >
> > Next elements added are objects usbssp_gadget_ep0_ops,
> > usbssp_gadget_ep_ops and usbssp_gadget_ops. These objects
> > constitute the interface used by gadget subsystem.
> > At this moment functions related to these objects are empty
> > and do nothing.
> >
> > This patch also implements usbssp_gadget_init_endpoint and
> > usbssp_gadget_free_endpoint used during initialization.
> >
> > Signed-off-by: Pawel Laszczak <[email protected]>
> > ---
> > drivers/usb/usbssp/gadget-ext-caps.h | 3 +
> > drivers/usb/usbssp/gadget-if.c | 269 ++++++++++++++++++++++++++-
> > drivers/usb/usbssp/gadget.c | 84 ++++++++-
> > 3 files changed, 350 insertions(+), 6 deletions(-)
> >
> > diff --git a/drivers/usb/usbssp/gadget-ext-caps.h b/drivers/usb/usbssp/gadget-ext-caps.h
> > index 2bf327046376..86c0ce331037 100644
> > --- a/drivers/usb/usbssp/gadget-ext-caps.h
> > +++ b/drivers/usb/usbssp/gadget-ext-caps.h
> > @@ -51,3 +51,6 @@
> > #define USBSSP_CMD_EWE BIT(10)
> >
> > #define USBSSP_IRQS (USBSSP_CMD_EIE | USBSSP_CMD_HSEIE | USBSSP_CMD_EWE)
> > +
> > +/* true: Controller Not Ready to accept doorbell or op reg writes after reset */
> > +#define USBSSP_STS_CNR BIT(11)
> > diff --git a/drivers/usb/usbssp/gadget-if.c b/drivers/usb/usbssp/gadget-if.c
> > index d53e0fb65299..70def978b085 100644
> > --- a/drivers/usb/usbssp/gadget-if.c
> > +++ b/drivers/usb/usbssp/gadget-if.c
> > @@ -12,13 +12,278 @@
> > #include <linux/usb/composite.h>
> > #include "gadget.h"
> >
> > +static int usbssp_gadget_ep_enable(struct usb_ep *ep,
> > + const struct usb_endpoint_descriptor *desc)
> > +{
> > + struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
> > + int ret = 0;
> > +
> > + if (!ep_priv)
> > + return -EINVAL;
> > +
> > + /*TODO: implements this function*/
> > + return ret;
> > +}
> > +
> > +int usbssp_gadget_ep_disable(struct usb_ep *ep)
> > +{
> > + struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
> > + int ret = 0;
> > +
> > + if (!ep_priv)
> > + return -EINVAL;
> > +
> > + /*TODO: implements this function*/
> > + return ret;
> > +}
> > +
> > +static struct usb_request *usbssp_gadget_ep_alloc_request(struct usb_ep *ep,
> > + gfp_t gfp_flags)
> > +{
> > + struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
> > +
> > + if (!ep_priv)
> > + return NULL;
> > +
> > + /*TODO: implements this function*/
> > + return NULL;
> > +}
> > +
> > +static void usbssp_gadget_ep_free_request(struct usb_ep *ep,
> > + struct usb_request *request)
> > +{
> > + struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
> > +
> > + if (!ep_priv)
> > + return;
> > +
> > + /*TODO: implements this function*/
> > +}
> > +
> > +static int usbssp_gadget_ep_queue(struct usb_ep *ep,
> > + struct usb_request *request,
> > + gfp_t gfp_flags)
> > +{
> > + struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
> > + int ret = 0;
> > +
> > + if (!ep_priv)
> > + return -EINVAL;
> > +
> > + /*TODO: implements this function*/
> > + return ret;
> > +}
> > +
> > +static int usbssp_gadget_ep_dequeue(struct usb_ep *ep,
> > + struct usb_request *request)
> > +{
> > + struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
> > + int ret = 0;
> > +
> > + if (!ep_priv)
> > + return -EINVAL;
> > +
> > + /*TODO: implements this function*/
> > + return ret;
> > +}
> > +
> > +static int usbssp_gadget_ep_set_halt(struct usb_ep *ep, int value)
> > +{
> > + struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
> > + int ret = 0;
> > +
> > + if (!ep_priv)
> > + return -EINVAL;
> > +
> > + /*TODO: implements this function*/
> > + return ret;
> > +}
> > +
> > +static int usbssp_gadget_ep_set_wedge(struct usb_ep *ep)
> > +{
> > + struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
> > + int ret = 0;
> > +
> > + if (!ep_priv)
> > + return -EINVAL;
> > +
> > + /*TODO: implements this function*/
> > + return ret;
> > +}
> > +
> > +static const struct usb_ep_ops usbssp_gadget_ep0_ops = {
> > + .enable = usbssp_gadget_ep_enable,
> > + .disable = usbssp_gadget_ep_disable,
> > + .alloc_request = usbssp_gadget_ep_alloc_request,
> > + .free_request = usbssp_gadget_ep_free_request,
> > + .queue = usbssp_gadget_ep_queue,
> > + .dequeue = usbssp_gadget_ep_dequeue,
> > + .set_halt = usbssp_gadget_ep_set_halt,
> > + .set_wedge = usbssp_gadget_ep_set_wedge,
> > +};
> > +
> > +static const struct usb_ep_ops usbssp_gadget_ep_ops = {
> > + .enable = usbssp_gadget_ep_enable,
> > + .disable = usbssp_gadget_ep_disable,
> > + .alloc_request = usbssp_gadget_ep_alloc_request,
> > + .free_request = usbssp_gadget_ep_free_request,
> > + .queue = usbssp_gadget_ep_queue,
> > + .dequeue = usbssp_gadget_ep_dequeue,
> > + .set_halt = usbssp_gadget_ep_set_halt,
> > + .set_wedge = usbssp_gadget_ep_set_wedge,
> > +};
> > +
> > +static struct usb_endpoint_descriptor usbssp_gadget_ep0_desc = {
> > + .bLength = USB_DT_ENDPOINT_SIZE,
> > + .bDescriptorType = USB_DT_ENDPOINT,
> > + .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
> > +};
> > +
> > +static int usbssp_gadget_start(struct usb_gadget *g,
> > + struct usb_gadget_driver *driver)
> > +{
> > + struct usbssp_udc *usbssp_data = gadget_to_usbssp(g);
> > + int ret = 0;
> > +
> > + if (usbssp_data->gadget_driver) {
> > + dev_err(usbssp_data->dev, "%s is already bound to %s\n",
> > + usbssp_data->gadget.name,
> > + usbssp_data->gadget_driver->driver.name);
> > + ret = -EBUSY;
> > + }
> > +
> > + /*TODO: add implementation*/
> > + return ret;
> > +}
> > +
> > +static int usbssp_gadget_stop(struct usb_gadget *g)
> > +{
> > + struct usbssp_udc *usbssp_data = gadget_to_usbssp(g);
> > +
> > + if (!usbssp_data)
> > + return -EINVAL;
> > + /*TODO: add implementation*/
> > + return 0;
> > +}
> > +
> > +static int usbssp_gadget_get_frame(struct usb_gadget *g)
> > +{
> > + struct usbssp_udc *usbssp_data = gadget_to_usbssp(g);
> > +
> > + if (!usbssp_data)
> > + return -EINVAL;
> > +
> > + /*TODO: add implementation*/
> > + return 0;
> > +}
> > +
> > +static int usbssp_gadget_wakeup(struct usb_gadget *g)
> > +{
> > + struct usbssp_udc *usbssp_data = gadget_to_usbssp(g);
> > +
> > + if (!usbssp_data)
> > + return -EINVAL;
> > +
> > + /*TODO: add implementation*/
> > + return 0;
> > +}
> > +
> > +static int usbssp_gadget_set_selfpowered(struct usb_gadget *g,
> > + int is_selfpowered)
> > +{
> > + struct usbssp_udc *usbssp_data = gadget_to_usbssp(g);
> > +
> > + if (!usbssp_data)
> > + return -EINVAL;
> > +
> > + /*TODO: add implementation*/
> > + return 0;
> > +}
> > +
> > +static const struct usb_gadget_ops usbssp_gadget_ops = {
> > + .get_frame = usbssp_gadget_get_frame,
> > + .wakeup = usbssp_gadget_wakeup,
> > + .set_selfpowered = usbssp_gadget_set_selfpowered,
> > + .udc_start = usbssp_gadget_start,
> > + .udc_stop = usbssp_gadget_stop,
> > +};
> > +
> > int usbssp_gadget_init_endpoint(struct usbssp_udc *usbssp_data)
>
> Since we're initializing all endpoints this function should be named usbssp_gadget_init_endpoints().
>
> > {
> > - /*TODO: it has to be implemented*/
> > + int i = 0;
> > + struct usbssp_ep *ep_priv;
> > +
> > + usbssp_data->num_endpoints = USBSSP_ENDPOINTS_NUM;
> > + INIT_LIST_HEAD(&usbssp_data->gadget.ep_list);
> > +
> > + for (i = 1; i < usbssp_data->num_endpoints; i++) {
> > + bool direction = i & 1; /*start from OUT endpoint*/
> > + u8 epnum = (i >> 1);
> > +
> > + ep_priv = &usbssp_data->devs.eps[i-1];
> > + ep_priv->usbssp_data = usbssp_data;
> > + ep_priv->number = epnum;
> > + ep_priv->direction = direction; /*0 for OUT, 1 for IN*/
> > +
> > + snprintf(ep_priv->name, sizeof(ep_priv->name), "ep%d%s", epnum,
> > + (ep_priv->direction) ? "in" : "out");
> > +
> > + ep_priv->endpoint.name = ep_priv->name;
> > +
> > + if (ep_priv->number < 2) {
> > + ep_priv->endpoint.desc = &usbssp_gadget_ep0_desc;
> > + ep_priv->endpoint.comp_desc = NULL;
> > + }
> > +
> > + if (epnum == 0) {
> > + /*EP0 is bidirectional endpoint*/
> > + usb_ep_set_maxpacket_limit(&ep_priv->endpoint, 512);
> > + dev_dbg(usbssp_data->dev,
> > + "Initializing %s, MaxPack: %04x Type: Ctrl\n",
> > + ep_priv->name, 512);
> > + ep_priv->endpoint.maxburst = 1;
> > + ep_priv->endpoint.ops = &usbssp_gadget_ep0_ops;
> > + ep_priv->endpoint.caps.type_control = true;
> > +
> > + usbssp_data->usb_req_ep0_in.epnum = ep_priv->number;
> > + usbssp_data->usb_req_ep0_in.dep = ep_priv;
> > +
> > + if (!epnum)
> > + usbssp_data->gadget.ep0 = &ep_priv->endpoint;
> > + } else {
> > + usb_ep_set_maxpacket_limit(&ep_priv->endpoint, 1024);
> > + ep_priv->endpoint.maxburst = 15;
> > + ep_priv->endpoint.ops = &usbssp_gadget_ep_ops;
> > + list_add_tail(&ep_priv->endpoint.ep_list,
> > + &usbssp_data->gadget.ep_list);
> > + ep_priv->endpoint.caps.type_iso = true;
> > + ep_priv->endpoint.caps.type_bulk = true;
> > + ep_priv->endpoint.caps.type_int = true;
> > +
> > + }
> > +
> > + ep_priv->endpoint.caps.dir_in = direction;
> > + ep_priv->endpoint.caps.dir_out = !direction;
>
> Since you start from 1, ep0 will only be initialized as dir_in.
> Is it better to represent EP0in and EP0out separately?
> If so you can start i from 0 and use
> ep_priv = &usbssp_data->devs.eps[i];
>
> This should fix the direction. eps[0] will be ep0out and eps[1] will be ep0in.

I wanted to be consistent with Device Context Data Structure from
XHCI specification (EP Context 0 BiDir, EP Context 1 OUT, EP Context 1 IN ...).

I little change this function, and now it look like this:
int usbssp_gadget_init_endpoints(struct usbssp_udc *usbssp_data)
{
int i = 0;
struct usbssp_ep *ep_priv;

usbssp_data->num_endpoints = USBSSP_ENDPOINTS_NUM;
INIT_LIST_HEAD(&usbssp_data->gadget.ep_list);

for (i = 0; i < usbssp_data->num_endpoints; i++) {
bool direction = i & 1; /*start from OUT endpoint*/
u8 epnum = ((i + 1) >> 1);

ep_priv = &usbssp_data->devs.eps[i];
ep_priv->usbssp_data = usbssp_data;
ep_priv->number = epnum;
ep_priv->direction = direction; /*0 for OUT, 1 for IN*/

/*
*Ep0 is bidirectional so Ep0In and Ep0Out is represented by
* usbssp_data->devs.eps[0]
*/
if (epnum == 0) {
snprintf(ep_priv->name, sizeof(ep_priv->name), "ep%d%s",
epnum, "BiDir");

usb_ep_set_maxpacket_limit(&ep_priv->endpoint, 512);
ep_priv->endpoint.maxburst = 1;
ep_priv->endpoint.ops = &usbssp_gadget_ep0_ops;
ep_priv->endpoint.desc = &usbssp_gadget_ep0_desc;

ep_priv->endpoint.comp_desc = NULL;
ep_priv->endpoint.caps.type_control = true;
ep_priv->endpoint.caps.dir_in = true;
ep_priv->endpoint.caps.dir_out = true;

usbssp_data->usb_req_ep0_in.epnum = ep_priv->number;
usbssp_data->usb_req_ep0_in.dep = ep_priv;
usbssp_data->gadget.ep0 = &ep_priv->endpoint;

} else {
snprintf(ep_priv->name, sizeof(ep_priv->name), "ep%d%s",
epnum, (ep_priv->direction) ? "in" : "out");

usb_ep_set_maxpacket_limit(&ep_priv->endpoint, 1024);
ep_priv->endpoint.maxburst = 15;
ep_priv->endpoint.ops = &usbssp_gadget_ep_ops;
list_add_tail(&ep_priv->endpoint.ep_list,
&usbssp_data->gadget.ep_list);

ep_priv->endpoint.caps.type_iso = true;
ep_priv->endpoint.caps.type_bulk = true;
ep_priv->endpoint.caps.type_int = true;

ep_priv->endpoint.caps.dir_in = direction;
ep_priv->endpoint.caps.dir_out = !direction;
}

ep_priv->endpoint.name = ep_priv->name;

dev_dbg(usbssp_data->dev, "Init %s, MaxPack: %04x SupType: "
"CTRL: %s, INT: %s, BULK: %s, ISOC %s, "
"SupDir IN: %s, OUT: %s\n",
ep_priv->name, 1024,
(ep_priv->endpoint.caps.type_control) ? "yes" : "no",
(ep_priv->endpoint.caps.type_int) ? "yes" : "no",
(ep_priv->endpoint.caps.type_bulk) ? "yes" : "no",
(ep_priv->endpoint.caps.type_iso) ? "yes" : "no",
(ep_priv->endpoint.caps.dir_in) ? "yes" : "no",
(ep_priv->endpoint.caps.dir_out) ? "yes" : "no");

INIT_LIST_HEAD(&ep_priv->pending_list);
}

return 0;
}

> > +
> > + dev_dbg(usbssp_data->dev, "Init %s, MaxPack: %04x SupType:"
> > + " INT/BULK/ISOC , SupDir %s\n",
> > + ep_priv->name, 1024,
> > + (ep_priv->endpoint.caps.dir_in) ? "IN" : "OUT");
> > +
> > + INIT_LIST_HEAD(&ep_priv->pending_list);
> > + }
> > return 0;
> > }
> >
> > void usbssp_gadget_free_endpoint(struct usbssp_udc *usbssp_data)
> > {
> > - /*TODO: it has to be implemented*/
> > + int i;
> > + struct usbssp_ep *ep_priv;
> > +
> > + for (i = 0; i < usbssp_data->num_endpoints; i++) {
> > + ep_priv = &usbssp_data->devs.eps[i];
> > +
>
> if you start i from 1 then you can skip the if().
>
> > + if (ep_priv->number != 0)
> > + list_del(&ep_priv->endpoint.ep_list);
> > + }
> > }
> > diff --git a/drivers/usb/usbssp/gadget.c b/drivers/usb/usbssp/gadget.c
> > index 338ec2ec18b1..195f5777cf8a 100644
> > --- a/drivers/usb/usbssp/gadget.c
> > +++ b/drivers/usb/usbssp/gadget.c
> > @@ -103,6 +103,83 @@ int usbssp_halt(struct usbssp_udc *usbssp_data)
> > return ret;
> > }
> >
> > +/*
> > + * Set the run bit and wait for the device to be running.
> > + */
> > +int usbssp_start(struct usbssp_udc *usbssp_data)
> > +{
> > + u32 temp;
> > + int ret;
> > +
> > + temp = readl(&usbssp_data->op_regs->command);
> > + temp |= (CMD_RUN | CMD_DEVEN);
> > + usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
> > + "// Turn on USBSSP, cmd = 0x%x.", temp);
> > + writel(temp, &usbssp_data->op_regs->command);
> > +
> > + /*
> > + * Wait for the HCHalted Staus bit to be 0 to indicate the device is
> > + * running.
> > + */
> > + ret = usbssp_handshake(&usbssp_data->op_regs->status,
> > + STS_HALT, 0, USBSSP_MAX_HALT_USEC);
> > +
> > + if (ret == -ETIMEDOUT)
> > + dev_err(usbssp_data->dev, "Device took too long to start, waited %u microseconds.\n",
> > + USBSSP_MAX_HALT_USEC);
> > + if (!ret)
> > + /* clear state flags. Including dying, halted or removing */
> > + usbssp_data->usbssp_state = 0;
> > +
> > + return ret;
> > +}
> > +
> > +/*
> > + * Reset a halted DC.
> > + *
> > + * This resets pipelines, timers, counters, state machines, etc.
> > + * Transactions will be terminated immediately, and operational registers
> > + * will be set to their defaults.
> > + */
> > +int usbssp_reset(struct usbssp_udc *usbssp_data)
> > +{
> > + u32 command;
> > + u32 state;
> > + int ret;
> > +
> > + state = readl(&usbssp_data->op_regs->status);
> > +
> > + if (state == ~(u32)0) {
> > + dev_warn(usbssp_data->dev, "Device not accessible, reset failed.\n");
> > + return -ENODEV;
> > + }
> > +
> > + if ((state & STS_HALT) == 0) {
> > + dev_warn(usbssp_data->dev, "DC not halted, aborting reset.\n");
>
> DC is not a familiar abbreviation. Mabe just use Controller? or Device controller.

In xhci driver is often used HC for host controller. In device driver I change this abbreviation
to DC. I will review the patches and change this to Controller.

>
> > + return 0;
> > + }
> > +
> > + usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init, "// Reset the DC");
> > + command = readl(&usbssp_data->op_regs->command);
> > + command |= CMD_RESET;
> > + writel(command, &usbssp_data->op_regs->command);
> > +
> > + ret = usbssp_handshake(&usbssp_data->op_regs->command,
> > + CMD_RESET, 0, 10 * 1000 * 1000);
> > +
> > + if (ret)
> > + return ret;
> > + usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
> > + "Wait for controller to be ready for doorbell rings");
> > + /*
> > + * USBSSP cannot write to any doorbells or operational registers other
> > + * than status until the "Controller Not Ready" flag is cleared.
> > + */
> > + ret = usbssp_handshake(&usbssp_data->op_regs->status,
> > + STS_CNR, 0, 10 * 1000 * 1000);
> > +
> > + return ret;
> > +}
> >
> > /*
> > * Initialize memory for gadget driver and USBSSP (one-time init).
> > @@ -179,8 +256,7 @@ int usbssp_gen_setup(struct usbssp_udc *usbssp_data)
> >
> > dev_dbg(usbssp_data->dev, "Resetting Device Controller\n");
> > /* Reset the internal DC memory state and registers. */
> > - /*TODO: add implementation of usbssp_reset function*/
> > - //retval = usbssp_reset(usbssp_data);
> > + retval = usbssp_reset(usbssp_data);
> > if (retval)
> > return retval;
> > dev_dbg(usbssp_data->dev, "Reset complete\n");
> > @@ -244,8 +320,7 @@ int usbssp_gadget_init(struct usbssp_udc *usbssp_data)
> > BUILD_BUG_ON(sizeof(struct usbssp_run_regs) != (8+8*128)*32/8);
> >
> > /* fill gadget fields */
> > - /*TODO: implements usbssp_gadget_ops object*/
> > - //usbssp_data->gadget.ops = &usbssp_gadget_ops;
> > + usbssp_data->gadget.ops = &usbssp_gadget_ops;
> > usbssp_data->gadget.name = "usbssp-gadget";
> > usbssp_data->gadget.max_speed = USB_SPEED_SUPER_PLUS;
> > usbssp_data->gadget.speed = USB_SPEED_UNKNOWN;
> > @@ -288,6 +363,7 @@ int usbssp_gadget_init(struct usbssp_udc *usbssp_data)
> > usbssp_halt(usbssp_data);
> > /*TODO add implementation of usbssp_reset function*/
> > //usbssp_reset(usbssp_data);
> > + usbssp_reset(usbssp_data);
> > /*TODO add implementation of freeing memory*/
> > //usbssp_mem_cleanup(usbssp_data);
> > err3:
> >
>
> --
> cheers,
> -roger
>
> Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
> Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki

Cheers
Pawel

2018-08-06 08:59:16

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH 06/31] usb: usbssp: added template functions used by upper layer.



On 04/08/18 09:37, Pawel Laszczak wrote:
>>> Patch adds some functionality for initialization process.
>>> It adds to driver usbssp_reset and usbssp_start function.
>>>
>>> Next elements added are objects usbssp_gadget_ep0_ops,
>>> usbssp_gadget_ep_ops and usbssp_gadget_ops. These objects
>>> constitute the interface used by gadget subsystem.
>>> At this moment functions related to these objects are empty
>>> and do nothing.
>>>
>>> This patch also implements usbssp_gadget_init_endpoint and
>>> usbssp_gadget_free_endpoint used during initialization.
>>>
>>> Signed-off-by: Pawel Laszczak <[email protected]>
>>> ---
>>> drivers/usb/usbssp/gadget-ext-caps.h | 3 +
>>> drivers/usb/usbssp/gadget-if.c | 269 ++++++++++++++++++++++++++-
>>> drivers/usb/usbssp/gadget.c | 84 ++++++++-
>>> 3 files changed, 350 insertions(+), 6 deletions(-)
>>>
>>> diff --git a/drivers/usb/usbssp/gadget-ext-caps.h b/drivers/usb/usbssp/gadget-ext-caps.h
>>> index 2bf327046376..86c0ce331037 100644
>>> --- a/drivers/usb/usbssp/gadget-ext-caps.h
>>> +++ b/drivers/usb/usbssp/gadget-ext-caps.h
>>> @@ -51,3 +51,6 @@
>>> #define USBSSP_CMD_EWE BIT(10)
>>>
>>> #define USBSSP_IRQS (USBSSP_CMD_EIE | USBSSP_CMD_HSEIE | USBSSP_CMD_EWE)
>>> +
>>> +/* true: Controller Not Ready to accept doorbell or op reg writes after reset */
>>> +#define USBSSP_STS_CNR BIT(11)
>>> diff --git a/drivers/usb/usbssp/gadget-if.c b/drivers/usb/usbssp/gadget-if.c
>>> index d53e0fb65299..70def978b085 100644
>>> --- a/drivers/usb/usbssp/gadget-if.c
>>> +++ b/drivers/usb/usbssp/gadget-if.c
>>> @@ -12,13 +12,278 @@
>>> #include <linux/usb/composite.h>
>>> #include "gadget.h"
>>>
>>> +static int usbssp_gadget_ep_enable(struct usb_ep *ep,
>>> + const struct usb_endpoint_descriptor *desc)
>>> +{
>>> + struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
>>> + int ret = 0;
>>> +
>>> + if (!ep_priv)
>>> + return -EINVAL;
>>> +
>>> + /*TODO: implements this function*/
>>> + return ret;
>>> +}
>>> +
>>> +int usbssp_gadget_ep_disable(struct usb_ep *ep)
>>> +{
>>> + struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
>>> + int ret = 0;
>>> +
>>> + if (!ep_priv)
>>> + return -EINVAL;
>>> +
>>> + /*TODO: implements this function*/
>>> + return ret;
>>> +}
>>> +
>>> +static struct usb_request *usbssp_gadget_ep_alloc_request(struct usb_ep *ep,
>>> + gfp_t gfp_flags)
>>> +{
>>> + struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
>>> +
>>> + if (!ep_priv)
>>> + return NULL;
>>> +
>>> + /*TODO: implements this function*/
>>> + return NULL;
>>> +}
>>> +
>>> +static void usbssp_gadget_ep_free_request(struct usb_ep *ep,
>>> + struct usb_request *request)
>>> +{
>>> + struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
>>> +
>>> + if (!ep_priv)
>>> + return;
>>> +
>>> + /*TODO: implements this function*/
>>> +}
>>> +
>>> +static int usbssp_gadget_ep_queue(struct usb_ep *ep,
>>> + struct usb_request *request,
>>> + gfp_t gfp_flags)
>>> +{
>>> + struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
>>> + int ret = 0;
>>> +
>>> + if (!ep_priv)
>>> + return -EINVAL;
>>> +
>>> + /*TODO: implements this function*/
>>> + return ret;
>>> +}
>>> +
>>> +static int usbssp_gadget_ep_dequeue(struct usb_ep *ep,
>>> + struct usb_request *request)
>>> +{
>>> + struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
>>> + int ret = 0;
>>> +
>>> + if (!ep_priv)
>>> + return -EINVAL;
>>> +
>>> + /*TODO: implements this function*/
>>> + return ret;
>>> +}
>>> +
>>> +static int usbssp_gadget_ep_set_halt(struct usb_ep *ep, int value)
>>> +{
>>> + struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
>>> + int ret = 0;
>>> +
>>> + if (!ep_priv)
>>> + return -EINVAL;
>>> +
>>> + /*TODO: implements this function*/
>>> + return ret;
>>> +}
>>> +
>>> +static int usbssp_gadget_ep_set_wedge(struct usb_ep *ep)
>>> +{
>>> + struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
>>> + int ret = 0;
>>> +
>>> + if (!ep_priv)
>>> + return -EINVAL;
>>> +
>>> + /*TODO: implements this function*/
>>> + return ret;
>>> +}
>>> +
>>> +static const struct usb_ep_ops usbssp_gadget_ep0_ops = {
>>> + .enable = usbssp_gadget_ep_enable,
>>> + .disable = usbssp_gadget_ep_disable,
>>> + .alloc_request = usbssp_gadget_ep_alloc_request,
>>> + .free_request = usbssp_gadget_ep_free_request,
>>> + .queue = usbssp_gadget_ep_queue,
>>> + .dequeue = usbssp_gadget_ep_dequeue,
>>> + .set_halt = usbssp_gadget_ep_set_halt,
>>> + .set_wedge = usbssp_gadget_ep_set_wedge,
>>> +};
>>> +
>>> +static const struct usb_ep_ops usbssp_gadget_ep_ops = {
>>> + .enable = usbssp_gadget_ep_enable,
>>> + .disable = usbssp_gadget_ep_disable,
>>> + .alloc_request = usbssp_gadget_ep_alloc_request,
>>> + .free_request = usbssp_gadget_ep_free_request,
>>> + .queue = usbssp_gadget_ep_queue,
>>> + .dequeue = usbssp_gadget_ep_dequeue,
>>> + .set_halt = usbssp_gadget_ep_set_halt,
>>> + .set_wedge = usbssp_gadget_ep_set_wedge,
>>> +};
>>> +
>>> +static struct usb_endpoint_descriptor usbssp_gadget_ep0_desc = {
>>> + .bLength = USB_DT_ENDPOINT_SIZE,
>>> + .bDescriptorType = USB_DT_ENDPOINT,
>>> + .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
>>> +};
>>> +
>>> +static int usbssp_gadget_start(struct usb_gadget *g,
>>> + struct usb_gadget_driver *driver)
>>> +{
>>> + struct usbssp_udc *usbssp_data = gadget_to_usbssp(g);
>>> + int ret = 0;
>>> +
>>> + if (usbssp_data->gadget_driver) {
>>> + dev_err(usbssp_data->dev, "%s is already bound to %s\n",
>>> + usbssp_data->gadget.name,
>>> + usbssp_data->gadget_driver->driver.name);
>>> + ret = -EBUSY;
>>> + }
>>> +
>>> + /*TODO: add implementation*/
>>> + return ret;
>>> +}
>>> +
>>> +static int usbssp_gadget_stop(struct usb_gadget *g)
>>> +{
>>> + struct usbssp_udc *usbssp_data = gadget_to_usbssp(g);
>>> +
>>> + if (!usbssp_data)
>>> + return -EINVAL;
>>> + /*TODO: add implementation*/
>>> + return 0;
>>> +}
>>> +
>>> +static int usbssp_gadget_get_frame(struct usb_gadget *g)
>>> +{
>>> + struct usbssp_udc *usbssp_data = gadget_to_usbssp(g);
>>> +
>>> + if (!usbssp_data)
>>> + return -EINVAL;
>>> +
>>> + /*TODO: add implementation*/
>>> + return 0;
>>> +}
>>> +
>>> +static int usbssp_gadget_wakeup(struct usb_gadget *g)
>>> +{
>>> + struct usbssp_udc *usbssp_data = gadget_to_usbssp(g);
>>> +
>>> + if (!usbssp_data)
>>> + return -EINVAL;
>>> +
>>> + /*TODO: add implementation*/
>>> + return 0;
>>> +}
>>> +
>>> +static int usbssp_gadget_set_selfpowered(struct usb_gadget *g,
>>> + int is_selfpowered)
>>> +{
>>> + struct usbssp_udc *usbssp_data = gadget_to_usbssp(g);
>>> +
>>> + if (!usbssp_data)
>>> + return -EINVAL;
>>> +
>>> + /*TODO: add implementation*/
>>> + return 0;
>>> +}
>>> +
>>> +static const struct usb_gadget_ops usbssp_gadget_ops = {
>>> + .get_frame = usbssp_gadget_get_frame,
>>> + .wakeup = usbssp_gadget_wakeup,
>>> + .set_selfpowered = usbssp_gadget_set_selfpowered,
>>> + .udc_start = usbssp_gadget_start,
>>> + .udc_stop = usbssp_gadget_stop,
>>> +};
>>> +
>>> int usbssp_gadget_init_endpoint(struct usbssp_udc *usbssp_data)
>>
>> Since we're initializing all endpoints this function should be named usbssp_gadget_init_endpoints().
>>
>>> {
>>> - /*TODO: it has to be implemented*/
>>> + int i = 0;
>>> + struct usbssp_ep *ep_priv;
>>> +
>>> + usbssp_data->num_endpoints = USBSSP_ENDPOINTS_NUM;
>>> + INIT_LIST_HEAD(&usbssp_data->gadget.ep_list);
>>> +
>>> + for (i = 1; i < usbssp_data->num_endpoints; i++) {
>>> + bool direction = i & 1; /*start from OUT endpoint*/
>>> + u8 epnum = (i >> 1);
>>> +
>>> + ep_priv = &usbssp_data->devs.eps[i-1];
>>> + ep_priv->usbssp_data = usbssp_data;
>>> + ep_priv->number = epnum;
>>> + ep_priv->direction = direction; /*0 for OUT, 1 for IN*/
>>> +
>>> + snprintf(ep_priv->name, sizeof(ep_priv->name), "ep%d%s", epnum,
>>> + (ep_priv->direction) ? "in" : "out");
>>> +
>>> + ep_priv->endpoint.name = ep_priv->name;
>>> +
>>> + if (ep_priv->number < 2) {
>>> + ep_priv->endpoint.desc = &usbssp_gadget_ep0_desc;
>>> + ep_priv->endpoint.comp_desc = NULL;
>>> + }
>>> +
>>> + if (epnum == 0) {
>>> + /*EP0 is bidirectional endpoint*/
>>> + usb_ep_set_maxpacket_limit(&ep_priv->endpoint, 512);
>>> + dev_dbg(usbssp_data->dev,
>>> + "Initializing %s, MaxPack: %04x Type: Ctrl\n",
>>> + ep_priv->name, 512);
>>> + ep_priv->endpoint.maxburst = 1;
>>> + ep_priv->endpoint.ops = &usbssp_gadget_ep0_ops;
>>> + ep_priv->endpoint.caps.type_control = true;
>>> +
>>> + usbssp_data->usb_req_ep0_in.epnum = ep_priv->number;
>>> + usbssp_data->usb_req_ep0_in.dep = ep_priv;
>>> +
>>> + if (!epnum)
>>> + usbssp_data->gadget.ep0 = &ep_priv->endpoint;
>>> + } else {
>>> + usb_ep_set_maxpacket_limit(&ep_priv->endpoint, 1024);
>>> + ep_priv->endpoint.maxburst = 15;
>>> + ep_priv->endpoint.ops = &usbssp_gadget_ep_ops;
>>> + list_add_tail(&ep_priv->endpoint.ep_list,
>>> + &usbssp_data->gadget.ep_list);
>>> + ep_priv->endpoint.caps.type_iso = true;
>>> + ep_priv->endpoint.caps.type_bulk = true;
>>> + ep_priv->endpoint.caps.type_int = true;
>>> +
>>> + }
>>> +
>>> + ep_priv->endpoint.caps.dir_in = direction;
>>> + ep_priv->endpoint.caps.dir_out = !direction;
>>
>> Since you start from 1, ep0 will only be initialized as dir_in.
>> Is it better to represent EP0in and EP0out separately?
>> If so you can start i from 0 and use
>> ep_priv = &usbssp_data->devs.eps[i];
>>
>> This should fix the direction. eps[0] will be ep0out and eps[1] will be ep0in.
>
> I wanted to be consistent with Device Context Data Structure from
> XHCI specification (EP Context 0 BiDir, EP Context 1 OUT, EP Context 1 IN ...).
>
> I little change this function, and now it look like this:
> int usbssp_gadget_init_endpoints(struct usbssp_udc *usbssp_data)
> {
> int i = 0;
> struct usbssp_ep *ep_priv;
>
> usbssp_data->num_endpoints = USBSSP_ENDPOINTS_NUM;
> INIT_LIST_HEAD(&usbssp_data->gadget.ep_list);
>
> for (i = 0; i < usbssp_data->num_endpoints; i++) {
> bool direction = i & 1; /*start from OUT endpoint*/
> u8 epnum = ((i + 1) >> 1);
>
> ep_priv = &usbssp_data->devs.eps[i];
> ep_priv->usbssp_data = usbssp_data;
> ep_priv->number = epnum;
> ep_priv->direction = direction; /*0 for OUT, 1 for IN*/
>
> /*
> *Ep0 is bidirectional so Ep0In and Ep0Out is represented by
> * usbssp_data->devs.eps[0]
> */
> if (epnum == 0) {
> snprintf(ep_priv->name, sizeof(ep_priv->name), "ep%d%s",
> epnum, "BiDir");
>
> usb_ep_set_maxpacket_limit(&ep_priv->endpoint, 512);
> ep_priv->endpoint.maxburst = 1;
> ep_priv->endpoint.ops = &usbssp_gadget_ep0_ops;
> ep_priv->endpoint.desc = &usbssp_gadget_ep0_desc;
>
> ep_priv->endpoint.comp_desc = NULL;
> ep_priv->endpoint.caps.type_control = true;
> ep_priv->endpoint.caps.dir_in = true;
> ep_priv->endpoint.caps.dir_out = true;
>
> usbssp_data->usb_req_ep0_in.epnum = ep_priv->number;
> usbssp_data->usb_req_ep0_in.dep = ep_priv;
> usbssp_data->gadget.ep0 = &ep_priv->endpoint;
>
> } else {
> snprintf(ep_priv->name, sizeof(ep_priv->name), "ep%d%s",
> epnum, (ep_priv->direction) ? "in" : "out");
>
> usb_ep_set_maxpacket_limit(&ep_priv->endpoint, 1024);
> ep_priv->endpoint.maxburst = 15;
> ep_priv->endpoint.ops = &usbssp_gadget_ep_ops;
> list_add_tail(&ep_priv->endpoint.ep_list,
> &usbssp_data->gadget.ep_list);
>
> ep_priv->endpoint.caps.type_iso = true;
> ep_priv->endpoint.caps.type_bulk = true;
> ep_priv->endpoint.caps.type_int = true;
>
> ep_priv->endpoint.caps.dir_in = direction;
> ep_priv->endpoint.caps.dir_out = !direction;
> }
>
> ep_priv->endpoint.name = ep_priv->name;

I think this is much better now.

>
> dev_dbg(usbssp_data->dev, "Init %s, MaxPack: %04x SupType: "
> "CTRL: %s, INT: %s, BULK: %s, ISOC %s, "
> "SupDir IN: %s, OUT: %s\n",
> ep_priv->name, 1024,
> (ep_priv->endpoint.caps.type_control) ? "yes" : "no",
> (ep_priv->endpoint.caps.type_int) ? "yes" : "no",
> (ep_priv->endpoint.caps.type_bulk) ? "yes" : "no",
> (ep_priv->endpoint.caps.type_iso) ? "yes" : "no",
> (ep_priv->endpoint.caps.dir_in) ? "yes" : "no",
> (ep_priv->endpoint.caps.dir_out) ? "yes" : "no");
>

In some places you use usbssp_dbg_trace() and at others you use dev_dbg().
Is it better to stick with one throughout?

> INIT_LIST_HEAD(&ep_priv->pending_list);
> }
>
> return 0;
> }
>
>>> +
>>> + dev_dbg(usbssp_data->dev, "Init %s, MaxPack: %04x SupType:"
>>> + " INT/BULK/ISOC , SupDir %s\n",
>>> + ep_priv->name, 1024,
>>> + (ep_priv->endpoint.caps.dir_in) ? "IN" : "OUT");
>>> +
>>> + INIT_LIST_HEAD(&ep_priv->pending_list);
>>> + }
>>> return 0;
>>> }
>>>
>>> void usbssp_gadget_free_endpoint(struct usbssp_udc *usbssp_data)
>>> {
>>> - /*TODO: it has to be implemented*/
>>> + int i;
>>> + struct usbssp_ep *ep_priv;
>>> +
>>> + for (i = 0; i < usbssp_data->num_endpoints; i++) {
>>> + ep_priv = &usbssp_data->devs.eps[i];
>>> +
>>
>> if you start i from 1 then you can skip the if().
>>
>>> + if (ep_priv->number != 0)
>>> + list_del(&ep_priv->endpoint.ep_list);
>>> + }
>>> }
>>> diff --git a/drivers/usb/usbssp/gadget.c b/drivers/usb/usbssp/gadget.c
>>> index 338ec2ec18b1..195f5777cf8a 100644
>>> --- a/drivers/usb/usbssp/gadget.c
>>> +++ b/drivers/usb/usbssp/gadget.c
>>> @@ -103,6 +103,83 @@ int usbssp_halt(struct usbssp_udc *usbssp_data)
>>> return ret;
>>> }
>>>
>>> +/*
>>> + * Set the run bit and wait for the device to be running.
>>> + */
>>> +int usbssp_start(struct usbssp_udc *usbssp_data)
>>> +{
>>> + u32 temp;
>>> + int ret;
>>> +
>>> + temp = readl(&usbssp_data->op_regs->command);
>>> + temp |= (CMD_RUN | CMD_DEVEN);
>>> + usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
>>> + "// Turn on USBSSP, cmd = 0x%x.", temp);
>>> + writel(temp, &usbssp_data->op_regs->command);
>>> +
>>> + /*
>>> + * Wait for the HCHalted Staus bit to be 0 to indicate the device is
>>> + * running.
>>> + */
>>> + ret = usbssp_handshake(&usbssp_data->op_regs->status,
>>> + STS_HALT, 0, USBSSP_MAX_HALT_USEC);
>>> +
>>> + if (ret == -ETIMEDOUT)
>>> + dev_err(usbssp_data->dev, "Device took too long to start, waited %u microseconds.\n",
>>> + USBSSP_MAX_HALT_USEC);
>>> + if (!ret)
>>> + /* clear state flags. Including dying, halted or removing */
>>> + usbssp_data->usbssp_state = 0;
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +/*
>>> + * Reset a halted DC.
>>> + *
>>> + * This resets pipelines, timers, counters, state machines, etc.
>>> + * Transactions will be terminated immediately, and operational registers
>>> + * will be set to their defaults.
>>> + */
>>> +int usbssp_reset(struct usbssp_udc *usbssp_data)
>>> +{
>>> + u32 command;
>>> + u32 state;
>>> + int ret;
>>> +
>>> + state = readl(&usbssp_data->op_regs->status);
>>> +
>>> + if (state == ~(u32)0) {
>>> + dev_warn(usbssp_data->dev, "Device not accessible, reset failed.\n");
>>> + return -ENODEV;
>>> + }
>>> +
>>> + if ((state & STS_HALT) == 0) {
>>> + dev_warn(usbssp_data->dev, "DC not halted, aborting reset.\n");
>>
>> DC is not a familiar abbreviation. Mabe just use Controller? or Device controller.
>
> In xhci driver is often used HC for host controller. In device driver I change this abbreviation
> to DC. I will review the patches and change this to Controller.
>
>>
>>> + return 0;
>>> + }
>>> +
>>> + usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init, "// Reset the DC");
>>> + command = readl(&usbssp_data->op_regs->command);
>>> + command |= CMD_RESET;
>>> + writel(command, &usbssp_data->op_regs->command);
>>> +
>>> + ret = usbssp_handshake(&usbssp_data->op_regs->command,
>>> + CMD_RESET, 0, 10 * 1000 * 1000);
>>> +
>>> + if (ret)
>>> + return ret;
>>> + usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
>>> + "Wait for controller to be ready for doorbell rings");
>>> + /*
>>> + * USBSSP cannot write to any doorbells or operational registers other
>>> + * than status until the "Controller Not Ready" flag is cleared.
>>> + */
>>> + ret = usbssp_handshake(&usbssp_data->op_regs->status,
>>> + STS_CNR, 0, 10 * 1000 * 1000);
>>> +
>>> + return ret;
>>> +}
>>>
>>> /*
>>> * Initialize memory for gadget driver and USBSSP (one-time init).
>>> @@ -179,8 +256,7 @@ int usbssp_gen_setup(struct usbssp_udc *usbssp_data)
>>>
>>> dev_dbg(usbssp_data->dev, "Resetting Device Controller\n");
>>> /* Reset the internal DC memory state and registers. */
>>> - /*TODO: add implementation of usbssp_reset function*/
>>> - //retval = usbssp_reset(usbssp_data);
>>> + retval = usbssp_reset(usbssp_data);
>>> if (retval)
>>> return retval;
>>> dev_dbg(usbssp_data->dev, "Reset complete\n");
>>> @@ -244,8 +320,7 @@ int usbssp_gadget_init(struct usbssp_udc *usbssp_data)
>>> BUILD_BUG_ON(sizeof(struct usbssp_run_regs) != (8+8*128)*32/8);
>>>
>>> /* fill gadget fields */
>>> - /*TODO: implements usbssp_gadget_ops object*/
>>> - //usbssp_data->gadget.ops = &usbssp_gadget_ops;
>>> + usbssp_data->gadget.ops = &usbssp_gadget_ops;
>>> usbssp_data->gadget.name = "usbssp-gadget";
>>> usbssp_data->gadget.max_speed = USB_SPEED_SUPER_PLUS;
>>> usbssp_data->gadget.speed = USB_SPEED_UNKNOWN;
>>> @@ -288,6 +363,7 @@ int usbssp_gadget_init(struct usbssp_udc *usbssp_data)
>>> usbssp_halt(usbssp_data);
>>> /*TODO add implementation of usbssp_reset function*/
>>> //usbssp_reset(usbssp_data);
>>> + usbssp_reset(usbssp_data);
>>> /*TODO add implementation of freeing memory*/
>>> //usbssp_mem_cleanup(usbssp_data);
>>> err3:
>>>
>>
>> --
>> cheers,
>> -roger
>>
>> Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
>> Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki
>
> Cheers
> Pawel
>

--
cheers,
-roger

Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki

2018-08-06 08:59:20

by Pawel Laszczak

[permalink] [raw]
Subject: RE: [PATCH 05/31] usb: usbssp: Added first part of initialization sequence.

Hi,
>
> Hi,
>
> On 19/07/18 20:57, Pawel Laszczak wrote:
> > Patch adds some initialization function. The initialization sequence
> > is quite complicated and this patch implements it only partially.
> > Initialization will be completed in next few patches.
> >
> > Patch introduce three new files:
> > 1. gadget-dbg.c - file contains functions used for debugging purpose.
> > 2. gadget-ext-caps.h - holds macro definition related to
> > Extended Capabilities
> > 3. gadget-if - file implements stuff related to upper layer
> > (e.g usb_ep_ops, usb_gadget_ops interface).
> >
> > Signed-off-by: Pawel Laszczak <[email protected]>
> > ---
> > drivers/usb/usbssp/Makefile | 1 +
> > drivers/usb/usbssp/gadget-dbg.c | 30 ++++
> > drivers/usb/usbssp/gadget-ext-caps.h | 53 ++++++
> > drivers/usb/usbssp/gadget-if.c | 24 +++
> > drivers/usb/usbssp/gadget.c | 242 +++++++++++++++++++++++++++
> > drivers/usb/usbssp/gadget.h | 15 ++
> > 6 files changed, 365 insertions(+)
> > create mode 100644 drivers/usb/usbssp/gadget-dbg.c
> > create mode 100644 drivers/usb/usbssp/gadget-ext-caps.h
> > create mode 100644 drivers/usb/usbssp/gadget-if.c
> >
> > diff --git a/drivers/usb/usbssp/Makefile b/drivers/usb/usbssp/Makefile
> > index d85f15afb51c..0606f3c63cd0 100644
> > --- a/drivers/usb/usbssp/Makefile
> > +++ b/drivers/usb/usbssp/Makefile
> > @@ -5,6 +5,7 @@ CFLAGS_gadget-trace.o := -I$(src)
> > obj-$(CONFIG_USB_USBSSP_GADGET) += usbssp.o
> > usbssp-y := usbssp-plat.o gadget-ring.o \
> > gadget.o
> > + gadget-dbg.o
> >
> > ifneq ($(CONFIG_TRACING),)
> > usbssp-y += gadget-trace.o
> > diff --git a/drivers/usb/usbssp/gadget-dbg.c b/drivers/usb/usbssp/gadget-dbg.c
> > new file mode 100644
> > index 000000000000..277617d1a996
> > --- /dev/null
> > +++ b/drivers/usb/usbssp/gadget-dbg.c
> > @@ -0,0 +1,30 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * USBSSP device controller driver
> > + *
> > + * Copyright (C) 2018 Cadence.
> > + *
> > + * Author: Pawel Laszczak
> > + *
> > + * A lot of code based on Linux XHCI driver.
> > + * Origin: Copyright (C) 2008 Intel Corp
> > + */
> > +
> > +#include "gadget.h"
> > +
> > +void usbssp_dbg_trace(struct usbssp_udc *usbssp_data,
> > + void (*trace)(struct va_format *),
> > + const char *fmt, ...)
> > +{
> > + struct va_format vaf;
> > + va_list args;
> > +
> > + va_start(args, fmt);
> > + vaf.fmt = fmt;
> > + vaf.va = &args;
> > + dev_dbg(usbssp_data->dev, "%pV\n", &vaf);
>
> dev_dbg will mess up with timings to be useful.
> Why not just use one of the trace events you defined in gadget-trace.h?

Function is equivalent of xhci_dbg_trace from XHCI driver.
It is a function of general use.
The problem with trace is that trace log is deleted after rebooting system.
Using trace log is problematic during developing and debugging driver.
Do you know whether trace log is saved anywhere on disk ?

> > + trace(&vaf);
> > + va_end(args);
> > +}
> > +EXPORT_SYMBOL_GPL(usbssp_dbg_trace);
> > +
> > diff --git a/drivers/usb/usbssp/gadget-ext-caps.h b/drivers/usb/usbssp/gadget-ext-caps.h
> > new file mode 100644
> > index 000000000000..2bf327046376
> > --- /dev/null
> > +++ b/drivers/usb/usbssp/gadget-ext-caps.h
> > @@ -0,0 +1,53 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * USBSSP device controller driver
> > + *
> > + * Copyright (C) 2018 Cadence.
> > + *
> > + * Author: Pawel Laszczak
> > + *
> > + * A lot of code based on Linux XHCI driver.
> > + * Origin: Copyright (C) 2008 Intel Corp
> > + */
> > +
> > +/* Up to 16 ms to halt an DC */
> > +#define USBSSP_MAX_HALT_USEC (16*1000)
> > +
> > +/* DC not running - set to 1 when run/stop bit is cleared. */
> > +#define USBSSP_STS_HALT BIT(0)
> > +
> > +/* HCCPARAMS offset from PCI base address */
> > +#define USBSSP_HCC_PARAMS_OFFSET 0x10
> > +/* HCCPARAMS contains the first extended capability pointer */
> > +#define USBSSP_HCC_EXT_CAPS(p) (((p)>>16)&0xffff)
> > +
> > +/* Command and Status registers offset from the Operational Registers address */
> > +#define USBSSP_CMD_OFFSET 0x00
> > +#define USBSSP_STS_OFFSET 0x04
> > +
> > +/* Capability Register */
> > +/* bits 7:0 - how long is the Capabilities register */
> > +#define USBSSP_HC_LENGTH(p) (((p)>>00)&0x00ff)
> > +
> > +/* Extended capability register fields */
> > +#define USBSSP_EXT_CAPS_ID(p) (((p)>>0)&0xff)
> > +#define USBSSP_EXT_CAPS_NEXT(p) (((p)>>8)&0xff)
> > +#define v_EXT_CAPS_VAL(p) ((p)>>16)
> > +/* Extended capability IDs - ID 0 reserved */
> > +#define USBSSP_EXT_CAPS_PROTOCOL 2
> > +
> > +/* USB 2.0 hardware LMP capability*/
> > +#define USBSSP_HLC BIT(19)
> > +#define USBSSP_BLC BIT(20)
> > +
> > +/* command register values to disable interrupts and halt the DC */
> > +/* start/stop DC execution - do not write unless DC is halted*/
> > +#define USBSSP_CMD_RUN BIT(0)
> > +/* Event Interrupt Enable - get irq when EINT bit is set in USBSTS register */
> > +#define USBSSP_CMD_EIE BIT(2)
> > +/* Host System Error Interrupt Enable - get irq when HSEIE bit set in USBSTS */
> > +#define USBSSP_CMD_HSEIE BIT(3)
> > +/* Enable Wrap Event - '1' means DC generates an event when MFINDEX wraps. */
> > +#define USBSSP_CMD_EWE BIT(10)
> > +
> > +#define USBSSP_IRQS (USBSSP_CMD_EIE | USBSSP_CMD_HSEIE | USBSSP_CMD_EWE)
> > diff --git a/drivers/usb/usbssp/gadget-if.c b/drivers/usb/usbssp/gadget-if.c
> > new file mode 100644
> > index 000000000000..d53e0fb65299
> > --- /dev/null
> > +++ b/drivers/usb/usbssp/gadget-if.c
> > @@ -0,0 +1,24 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * USBSSP device controller driver
> > + *
> > + * Copyright (C) 2018 Cadence.
> > + *
> > + * Author: Pawel Laszczak
> > + *
> > + */
> > +
> > +#include <linux/usb/gadget.h>
> > +#include <linux/usb/composite.h>
> > +#include "gadget.h"
> > +
> > +int usbssp_gadget_init_endpoint(struct usbssp_udc *usbssp_data)
> > +{
> > + /*TODO: it has to be implemented*/
> > + return 0;
> > +}
> > +
> > +void usbssp_gadget_free_endpoint(struct usbssp_udc *usbssp_data)
> > +{
> > + /*TODO: it has to be implemented*/
> > +}
> > diff --git a/drivers/usb/usbssp/gadget.c b/drivers/usb/usbssp/gadget.c
> > index 2f60d7dd1fe4..338ec2ec18b1 100644
> > --- a/drivers/usb/usbssp/gadget.c
> > +++ b/drivers/usb/usbssp/gadget.c
> > @@ -23,6 +23,107 @@
> > #include "gadget-trace.h"
> > #include "gadget.h"
> >
> > +
> > +/*
>
> /**
>
> > + * usbssp_handshake - spin reading dc until handshake completes or fails
> > + * @ptr: address of dc 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 usbssp_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;
> > +}
> > +
> > +/*
> > + * Disable interrupts and begin the DC halting process.
> > + */
> > +void usbssp_quiesce(struct usbssp_udc *usbssp_data)
> > +{
> > + u32 halted;
> > + u32 cmd;
> > + u32 mask;
> > +
> > + mask = ~(u32)(USBSSP_IRQS);
> > +
> > + halted = readl(&usbssp_data->op_regs->status) & STS_HALT;
> > + if (!halted)
> > + mask &= ~CMD_RUN;
> > +
> > + cmd = readl(&usbssp_data->op_regs->command);
> > + cmd &= mask;
> > + writel(cmd, &usbssp_data->op_regs->command);
> > +}
> > +
> > +/*
> > + * Force DC into halt state.
> > + *
> > + * Disable any IRQs and clear the run/stop bit.
> > + * USBSSP will complete any current and actively pipelined transactions, and
> > + * should halt within 16 ms of the run/stop bit being cleared.
> > + * Read DC Halted bit in the status register to see when the DC is finished.
> > + */
> > +int usbssp_halt(struct usbssp_udc *usbssp_data)
> > +{
> > + int ret;
> > +
> > + usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
> > + "// Halt the USBSSP");
> > + usbssp_quiesce(usbssp_data);
> > +
> > + ret = usbssp_handshake(&usbssp_data->op_regs->status,
> > + STS_HALT, STS_HALT, USBSSP_MAX_HALT_USEC);
> > +
> > + if (!ret) {
> > + dev_warn(usbssp_data->dev, "Device halt failed, %d\n", ret);
> > + return ret;
> > + }
> > +
> > + usbssp_data->usbssp_state |= USBSSP_STATE_HALTED;
> > + usbssp_data->cmd_ring_state = CMD_RING_STATE_STOPPED;
> > + return ret;
> > +}
> > +
> > +
> > +/*
> > + * Initialize memory for gadget driver and USBSSP (one-time init).
> > + *
> > + * Program the PAGESIZE register, initialize the device context array, create
> > + * device contexts, set up a command ring segment (or two?), create event
> > + * ring (one for now).
> > + */
> > +int usbssp_init(struct usbssp_udc *usbssp_data)
> > +{
> > + int retval = 0;
> > +
> > + spin_lock_init(&usbssp_data->lock);
> > + spin_lock_init(&usbssp_data->irq_thread_lock);
> > +
> > + /*TODO: memory initialization*/
> > + //retval = usbssp_mem_init(usbssp_data, GFP_KERNEL);
> > +
> > + return retval;
> > +}
> > +
> > #ifdef CONFIG_PM
> > /*
> > * Stop DC (not bus-specific)
> > @@ -50,9 +151,146 @@ int usbssp_resume(struct usbssp_udc *usbssp_data, bool hibernated)
> >
> > #endif /* CONFIG_PM */
> >
> > +int usbssp_gen_setup(struct usbssp_udc *usbssp_data)
> > +{
> > + int retval;
> > +
> > + mutex_init(&usbssp_data->mutex);
> > +
> > + usbssp_data->cap_regs = usbssp_data->regs;
> > + usbssp_data->op_regs = usbssp_data->regs +
> > + HC_LENGTH(readl(&usbssp_data->cap_regs->hc_capbase));
> > +
> > + usbssp_data->run_regs = usbssp_data->regs +
> > + (readl(&usbssp_data->cap_regs->run_regs_off) & RTSOFF_MASK);
> > + /* Cache read-only capability registers */
> > + usbssp_data->hcs_params1 = readl(&usbssp_data->cap_regs->hcs_params1);
> > + usbssp_data->hcs_params2 = readl(&usbssp_data->cap_regs->hcs_params2);
> > + usbssp_data->hcs_params3 = readl(&usbssp_data->cap_regs->hcs_params3);
> > + usbssp_data->hcc_params = readl(&usbssp_data->cap_regs->hc_capbase);
> > + usbssp_data->hci_version = HC_VERSION(usbssp_data->hcc_params);
> > + usbssp_data->hcc_params = readl(&usbssp_data->cap_regs->hcc_params);
> > + usbssp_data->hcc_params2 = readl(&usbssp_data->cap_regs->hcc_params2);
> > +
> > + /* Make sure the Device Controller is halted. */
> > + retval = usbssp_halt(usbssp_data);
> > + if (retval)
> > + return retval;
> > +
> > + dev_dbg(usbssp_data->dev, "Resetting Device Controller\n");
>
> do you really need all these dev_dbg prints? You should just fit them to one
> of the trace event classes that you already have or add a new one if needed.

They are not very useful . I deleted them.

> > + /* Reset the internal DC memory state and registers. */
> > + /*TODO: add implementation of usbssp_reset function*/
> > + //retval = usbssp_reset(usbssp_data);
> > + if (retval)
> > + return retval;
> > + dev_dbg(usbssp_data->dev, "Reset complete\n");
> > +
> > + /* Set dma_mask and coherent_dma_mask to 64-bits,
> > + * if USBSSP supports 64-bit addressing
> > + */
> > + if (HCC_64BIT_ADDR(usbssp_data->hcc_params) &&
> > + !dma_set_mask(usbssp_data->dev, DMA_BIT_MASK(64))) {
> > + dev_dbg(usbssp_data->dev, "Enabling 64-bit DMA addresses.\n");
> > + dma_set_coherent_mask(usbssp_data->dev, DMA_BIT_MASK(64));
> > + } else {
> > + /*
> > + * This is to avoid error in cases where a 32-bit USB
> > + * controller is used on a 64-bit capable system.
> > + */
> > + retval = dma_set_mask(usbssp_data->dev, DMA_BIT_MASK(32));
> > + if (retval)
> > + return retval;
> > + dev_dbg(usbssp_data->dev, "Enabling 32-bit DMA addresses.\n");
> > + dma_set_coherent_mask(usbssp_data->dev, DMA_BIT_MASK(32));
> > + }
> > +
> > + /* Initialize USBSSP controller data structures. */
> > + retval = usbssp_init(usbssp_data);
> > + if (retval)
> > + return retval;
> > +
> > + dev_info(usbssp_data->dev, "USBSSP params 0x%08x USBSSP version 0x%x\n",
> > + usbssp_data->hcc_params, usbssp_data->hci_version);
> > +
> > + return 0;
> > +}
> > +
> > +/*
> > + * gadget-if.c file is part of gadget.c file and implements interface
> > + * for gadget driver
> > + */
> > +#include "gadget-if.c"
> > +
>
> All includes should be together at the beginning of the file.

Greg Kroach-Harman wrote about including .c files:
"Ugh, I know USB hcd drivers love to include .c files in the middle of
them, but do we have to continue that crazy practice in newer drivers?
Is it really necessary?"

I forgot to change this, I will remove this and I will add all necessary declaration to gadget.h file.

> > int usbssp_gadget_init(struct usbssp_udc *usbssp_data)
> > {
> > int ret;
> > +
> > + /*
> > + * Check the compiler generated sizes of structures that must be laid
> > + * out in specific ways for hardware access.
> > + */
> > + BUILD_BUG_ON(sizeof(struct usbssp_doorbell_array) != 2*32/8);
> > + BUILD_BUG_ON(sizeof(struct usbssp_slot_ctx) != 8*32/8);
> > + BUILD_BUG_ON(sizeof(struct usbssp_ep_ctx) != 8*32/8);
> > + /* usbssp_device has eight fields, and also
> > + * embeds one usbssp_slot_ctx and 31 usbssp_ep_ctx
> > + */
> > + BUILD_BUG_ON(sizeof(struct usbssp_stream_ctx) != 4*32/8);
> > + BUILD_BUG_ON(sizeof(union usbssp_trb) != 4*32/8);
> > + BUILD_BUG_ON(sizeof(struct usbssp_erst_entry) != 4*32/8);
> > + BUILD_BUG_ON(sizeof(struct usbssp_cap_regs) != 8*32/8);
> > + BUILD_BUG_ON(sizeof(struct usbssp_intr_reg) != 8*32/8);
> > + /* usbssp_run_regs has eight fields and embeds 128 usbssp_intr_regs */
> > + BUILD_BUG_ON(sizeof(struct usbssp_run_regs) != (8+8*128)*32/8);
> > +
> > + /* fill gadget fields */
> > + /*TODO: implements usbssp_gadget_ops object*/
> > + //usbssp_data->gadget.ops = &usbssp_gadget_ops;
> > + usbssp_data->gadget.name = "usbssp-gadget";
> > + usbssp_data->gadget.max_speed = USB_SPEED_SUPER_PLUS;
> > + usbssp_data->gadget.speed = USB_SPEED_UNKNOWN;
> > + usbssp_data->gadget.sg_supported = true;
> > + usbssp_data->gadget.lpm_capable = 1;
> > +
> > + usbssp_data->setup_buf = kzalloc(USBSSP_EP0_SETUP_SIZE, GFP_KERNEL);
> > + if (!usbssp_data->setup_buf)
> > + return -ENOMEM;
> > +
> > + /*USBSSP support not aligned buffer but this option
> > + * improve performance of this controller.
> > + */
>
> Multi-line comment formatting. Checkpach should complain.
It doesn't complain about this case. I don't know why.

> > + usbssp_data->gadget.quirk_ep_out_aligned_size = true;
> > + ret = usbssp_gen_setup(usbssp_data);
> > + if (ret < 0) {
> > + dev_err(usbssp_data->dev,
> > + "Generic initialization failed with error code%d\n",
> > + ret);
> > + goto err3;
> > + }
> > +
> > + ret = usbssp_gadget_init_endpoint(usbssp_data);
> > + if (ret < 0) {
> > + dev_err(usbssp_data->dev, "failed to initialize endpoints\n");
> > + goto err1;
> > + }
> > +
> > + ret = usb_add_gadget_udc(usbssp_data->dev, &usbssp_data->gadget);
> > +
> > + if (ret) {
> > + dev_err(usbssp_data->dev, "failed to register udc\n");
> > + goto err2;
> > + }
> > +
> > + return ret;
> > +err2:
> > + usbssp_gadget_free_endpoint(usbssp_data);
> > +err1:
> > + usbssp_halt(usbssp_data);
> > + /*TODO add implementation of usbssp_reset function*/
> > + //usbssp_reset(usbssp_data);
> > + /*TODO add implementation of freeing memory*/
> > + //usbssp_mem_cleanup(usbssp_data);
> > +err3:
> > return ret;
> > }
> >
> > @@ -60,5 +298,9 @@ int usbssp_gadget_exit(struct usbssp_udc *usbssp_data)
> > {
> > int ret = 0;
> >
> > + usb_del_gadget_udc(&usbssp_data->gadget);
> > + usbssp_gadget_free_endpoint(usbssp_data);
> > + /*TODO: add usbssp_stop implementation*/
> > + //usbssp_stop(usbssp_data);
> > return ret;
> > }
> > diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h
> > index 55e20795d900..5d8918f8da84 100644
> > --- a/drivers/usb/usbssp/gadget.h
> > +++ b/drivers/usb/usbssp/gadget.h
> > @@ -12,8 +12,10 @@
> > #ifndef __LINUX_USBSSP_GADGET_H
> > #define __LINUX_USBSSP_GADGET_H
> >
> > +#include <linux/irq.h>
> > #include <linux/io-64-nonatomic-lo-hi.h>
> > #include <linux/usb/gadget.h>
> > +#include "gadget-ext-caps.h"
> >
> > /* Max number slots - only 1 is allowed */
> > #define DEV_MAX_SLOTS 1
> > @@ -1676,7 +1678,18 @@ static inline void usbssp_write_64(struct usbssp_udc *usbssp_data,
> > lo_hi_writeq(val, regs);
> > }
> >
> > +/* USBSSP memory management */
> > +void usbssp_dbg_trace(struct usbssp_udc *usbssp_data,
> > + void (*trace)(struct va_format *),
> > + const char *fmt, ...);
>
> what has this function to do with memory management?
>
Nothing,

> > /* USBSSP Device controller glue */
> > +void usbssp_bottom_irq(struct work_struct *work);
>
> usbssp_bottom_irq() wasn't defined in this patch.
Removed from this patch

> > +int usbssp_init(struct usbssp_udc *usbssp_data);
> > +void usbssp_stop(struct usbssp_udc *usbssp_data);
> > +int usbssp_handshake(void __iomem *ptr, u32 mask, u32 done, int usec);
> > +void usbssp_quiesce(struct usbssp_udc *usbssp_data);
> > +extern int usbssp_reset(struct usbssp_udc *usbssp_data);
> > +
> > int usbssp_suspend(struct usbssp_udc *usbssp_data, bool do_wakeup);
> > int usbssp_resume(struct usbssp_udc *usbssp_data, bool hibernated);
> >
> > @@ -1689,6 +1702,8 @@ dma_addr_t usbssp_trb_virt_to_dma(struct usbssp_segment *seg,
> > /* USBSSP gadget interface*/
> > int usbssp_gadget_init(struct usbssp_udc *usbssp_data);
> > int usbssp_gadget_exit(struct usbssp_udc *usbssp_data);
> > +void usbssp_gadget_free_endpoint(struct usbssp_udc *usbssp_data);
> > +int usbssp_gadget_init_endpoint(struct usbssp_udc *usbssp_data);
> >
> > static inline char *usbssp_slot_state_string(u32 state)
> > {
> >
>
> --
> cheers,
> -roger
>
> Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
> Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki

thanks
cheers,
Pawel

2018-08-06 10:34:32

by Roger Quadros

[permalink] [raw]
Subject: Re: [PATCH 05/31] usb: usbssp: Added first part of initialization sequence.



On 06/08/18 11:57, Pawel Laszczak wrote:
> Hi,
>>
>> Hi,
>>
>> On 19/07/18 20:57, Pawel Laszczak wrote:
>>> Patch adds some initialization function. The initialization sequence
>>> is quite complicated and this patch implements it only partially.
>>> Initialization will be completed in next few patches.
>>>
>>> Patch introduce three new files:
>>> 1. gadget-dbg.c - file contains functions used for debugging purpose.
>>> 2. gadget-ext-caps.h - holds macro definition related to
>>> Extended Capabilities
>>> 3. gadget-if - file implements stuff related to upper layer
>>> (e.g usb_ep_ops, usb_gadget_ops interface).
>>>
>>> Signed-off-by: Pawel Laszczak <[email protected]>
>>> ---
>>> drivers/usb/usbssp/Makefile | 1 +
>>> drivers/usb/usbssp/gadget-dbg.c | 30 ++++
>>> drivers/usb/usbssp/gadget-ext-caps.h | 53 ++++++
>>> drivers/usb/usbssp/gadget-if.c | 24 +++
>>> drivers/usb/usbssp/gadget.c | 242 +++++++++++++++++++++++++++
>>> drivers/usb/usbssp/gadget.h | 15 ++
>>> 6 files changed, 365 insertions(+)
>>> create mode 100644 drivers/usb/usbssp/gadget-dbg.c
>>> create mode 100644 drivers/usb/usbssp/gadget-ext-caps.h
>>> create mode 100644 drivers/usb/usbssp/gadget-if.c
>>>
>>> diff --git a/drivers/usb/usbssp/Makefile b/drivers/usb/usbssp/Makefile
>>> index d85f15afb51c..0606f3c63cd0 100644
>>> --- a/drivers/usb/usbssp/Makefile
>>> +++ b/drivers/usb/usbssp/Makefile
>>> @@ -5,6 +5,7 @@ CFLAGS_gadget-trace.o := -I$(src)
>>> obj-$(CONFIG_USB_USBSSP_GADGET) += usbssp.o
>>> usbssp-y := usbssp-plat.o gadget-ring.o \
>>> gadget.o
>>> + gadget-dbg.o
>>>
>>> ifneq ($(CONFIG_TRACING),)
>>> usbssp-y += gadget-trace.o
>>> diff --git a/drivers/usb/usbssp/gadget-dbg.c b/drivers/usb/usbssp/gadget-dbg.c
>>> new file mode 100644
>>> index 000000000000..277617d1a996
>>> --- /dev/null
>>> +++ b/drivers/usb/usbssp/gadget-dbg.c
>>> @@ -0,0 +1,30 @@
>>> +// SPDX-License-Identifier: GPL-2.0
>>> +/*
>>> + * USBSSP device controller driver
>>> + *
>>> + * Copyright (C) 2018 Cadence.
>>> + *
>>> + * Author: Pawel Laszczak
>>> + *
>>> + * A lot of code based on Linux XHCI driver.
>>> + * Origin: Copyright (C) 2008 Intel Corp
>>> + */
>>> +
>>> +#include "gadget.h"
>>> +
>>> +void usbssp_dbg_trace(struct usbssp_udc *usbssp_data,
>>> + void (*trace)(struct va_format *),
>>> + const char *fmt, ...)
>>> +{
>>> + struct va_format vaf;
>>> + va_list args;
>>> +
>>> + va_start(args, fmt);
>>> + vaf.fmt = fmt;
>>> + vaf.va = &args;
>>> + dev_dbg(usbssp_data->dev, "%pV\n", &vaf);
>>
>> dev_dbg will mess up with timings to be useful.
>> Why not just use one of the trace events you defined in gadget-trace.h?
>
> Function is equivalent of xhci_dbg_trace from XHCI driver.
> It is a function of general use.
> The problem with trace is that trace log is deleted after rebooting system.
> Using trace log is problematic during developing and debugging driver.

OK. But I think trace log is more useful for I/O related debugging as it
keeps the timing impact minimal. For things that don't come in the I/O path
you could use dev_dbg().

> Do you know whether trace log is saved anywhere on disk ?
>
I think trace_cmd allows you to record on the disk or even send it over the network.
https://lwn.net/Articles/410200/

>>> + trace(&vaf);
>>> + va_end(args);
>>> +}
>>> +EXPORT_SYMBOL_GPL(usbssp_dbg_trace);
>>> +

<snip>

>>> int usbssp_gadget_init(struct usbssp_udc *usbssp_data)
>>> {
>>> int ret;
>>> +
>>> + /*
>>> + * Check the compiler generated sizes of structures that must be laid
>>> + * out in specific ways for hardware access.
>>> + */
>>> + BUILD_BUG_ON(sizeof(struct usbssp_doorbell_array) != 2*32/8);
>>> + BUILD_BUG_ON(sizeof(struct usbssp_slot_ctx) != 8*32/8);
>>> + BUILD_BUG_ON(sizeof(struct usbssp_ep_ctx) != 8*32/8);
>>> + /* usbssp_device has eight fields, and also
>>> + * embeds one usbssp_slot_ctx and 31 usbssp_ep_ctx
>>> + */
>>> + BUILD_BUG_ON(sizeof(struct usbssp_stream_ctx) != 4*32/8);
>>> + BUILD_BUG_ON(sizeof(union usbssp_trb) != 4*32/8);
>>> + BUILD_BUG_ON(sizeof(struct usbssp_erst_entry) != 4*32/8);
>>> + BUILD_BUG_ON(sizeof(struct usbssp_cap_regs) != 8*32/8);
>>> + BUILD_BUG_ON(sizeof(struct usbssp_intr_reg) != 8*32/8);
>>> + /* usbssp_run_regs has eight fields and embeds 128 usbssp_intr_regs */
>>> + BUILD_BUG_ON(sizeof(struct usbssp_run_regs) != (8+8*128)*32/8);
>>> +
>>> + /* fill gadget fields */
>>> + /*TODO: implements usbssp_gadget_ops object*/
>>> + //usbssp_data->gadget.ops = &usbssp_gadget_ops;
>>> + usbssp_data->gadget.name = "usbssp-gadget";
>>> + usbssp_data->gadget.max_speed = USB_SPEED_SUPER_PLUS;
>>> + usbssp_data->gadget.speed = USB_SPEED_UNKNOWN;
>>> + usbssp_data->gadget.sg_supported = true;
>>> + usbssp_data->gadget.lpm_capable = 1;
>>> +
>>> + usbssp_data->setup_buf = kzalloc(USBSSP_EP0_SETUP_SIZE, GFP_KERNEL);
>>> + if (!usbssp_data->setup_buf)
>>> + return -ENOMEM;
>>> +
>>> + /*USBSSP support not aligned buffer but this option
>>> + * improve performance of this controller.
>>> + */
>>
>> Multi-line comment formatting. Checkpach should complain.
> It doesn't complain about this case. I don't know why.
>

Can you try with --strict option to checkpatch?

>>> + usbssp_data->gadget.quirk_ep_out_aligned_size = true;
>>> + ret = usbssp_gen_setup(usbssp_data);
>>> + if (ret < 0) {
>>> + dev_err(usbssp_data->dev,
>>> + "Generic initialization failed with error code%d\n",
>>> + ret);
>>> + goto err3;
>>> + }
>>> +
>>> + ret = usbssp_gadget_init_endpoint(usbssp_data);
>>> + if (ret < 0) {
>>> + dev_err(usbssp_data->dev, "failed to initialize endpoints\n");
>>> + goto err1;
>>> + }
>>> +
>>> + ret = usb_add_gadget_udc(usbssp_data->dev, &usbssp_data->gadget);
>>> +
>>> + if (ret) {
>>> + dev_err(usbssp_data->dev, "failed to register udc\n");
>>> + goto err2;
>>> + }
>>> +
>>> + return ret;
>>> +err2:
>>> + usbssp_gadget_free_endpoint(usbssp_data);
>>> +err1:
>>> + usbssp_halt(usbssp_data);
>>> + /*TODO add implementation of usbssp_reset function*/
>>> + //usbssp_reset(usbssp_data);
>>> + /*TODO add implementation of freeing memory*/
>>> + //usbssp_mem_cleanup(usbssp_data);
>>> +err3:
>>> return ret;
>>> }
>>>
>>> @@ -60,5 +298,9 @@ int usbssp_gadget_exit(struct usbssp_udc *usbssp_data)
>>> {
>>> int ret = 0;
>>>
>>> + usb_del_gadget_udc(&usbssp_data->gadget);
>>> + usbssp_gadget_free_endpoint(usbssp_data);
>>> + /*TODO: add usbssp_stop implementation*/
>>> + //usbssp_stop(usbssp_data);
>>> return ret;
>>> }
>>> diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h
>>> index 55e20795d900..5d8918f8da84 100644
>>> --- a/drivers/usb/usbssp/gadget.h
>>> +++ b/drivers/usb/usbssp/gadget.h
>>> @@ -12,8 +12,10 @@
>>> #ifndef __LINUX_USBSSP_GADGET_H
>>> #define __LINUX_USBSSP_GADGET_H
>>>
>>> +#include <linux/irq.h>
>>> #include <linux/io-64-nonatomic-lo-hi.h>
>>> #include <linux/usb/gadget.h>
>>> +#include "gadget-ext-caps.h"
>>>
>>> /* Max number slots - only 1 is allowed */
>>> #define DEV_MAX_SLOTS 1
>>> @@ -1676,7 +1678,18 @@ static inline void usbssp_write_64(struct usbssp_udc *usbssp_data,
>>> lo_hi_writeq(val, regs);
>>> }
>>>
>>> +/* USBSSP memory management */
>>> +void usbssp_dbg_trace(struct usbssp_udc *usbssp_data,
>>> + void (*trace)(struct va_format *),
>>> + const char *fmt, ...);
>>
>> what has this function to do with memory management?
>>
> Nothing,
>
>>> /* USBSSP Device controller glue */
>>> +void usbssp_bottom_irq(struct work_struct *work);
>>
>> usbssp_bottom_irq() wasn't defined in this patch.
> Removed from this patch
>
>>> +int usbssp_init(struct usbssp_udc *usbssp_data);
>>> +void usbssp_stop(struct usbssp_udc *usbssp_data);
>>> +int usbssp_handshake(void __iomem *ptr, u32 mask, u32 done, int usec);
>>> +void usbssp_quiesce(struct usbssp_udc *usbssp_data);
>>> +extern int usbssp_reset(struct usbssp_udc *usbssp_data);
>>> +
>>> int usbssp_suspend(struct usbssp_udc *usbssp_data, bool do_wakeup);
>>> int usbssp_resume(struct usbssp_udc *usbssp_data, bool hibernated);
>>>
>>> @@ -1689,6 +1702,8 @@ dma_addr_t usbssp_trb_virt_to_dma(struct usbssp_segment *seg,
>>> /* USBSSP gadget interface*/
>>> int usbssp_gadget_init(struct usbssp_udc *usbssp_data);
>>> int usbssp_gadget_exit(struct usbssp_udc *usbssp_data);
>>> +void usbssp_gadget_free_endpoint(struct usbssp_udc *usbssp_data);
>>> +int usbssp_gadget_init_endpoint(struct usbssp_udc *usbssp_data);
>>>
>>> static inline char *usbssp_slot_state_string(u32 state)
>>> {
>>>
>>

--
cheers,
-roger

Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki

2018-08-06 12:04:39

by Pawel Laszczak

[permalink] [raw]
Subject: RE: [PATCH 05/31] usb: usbssp: Added first part of initialization sequence.

Hi,
> > Hi,
> >>
> >> Hi,
> >>
> >> On 19/07/18 20:57, Pawel Laszczak wrote:
> >>> Patch adds some initialization function. The initialization sequence
> >>> is quite complicated and this patch implements it only partially.
> >>> Initialization will be completed in next few patches.
> >>>
> >>> Patch introduce three new files:
> >>> 1. gadget-dbg.c - file contains functions used for debugging purpose.
> >>> 2. gadget-ext-caps.h - holds macro definition related to
> >>> Extended Capabilities
> >>> 3. gadget-if - file implements stuff related to upper layer
> >>> (e.g usb_ep_ops, usb_gadget_ops interface).
> >>>
> >>> Signed-off-by: Pawel Laszczak <[email protected]>
> >>> ---
> >>> drivers/usb/usbssp/Makefile | 1 +
> >>> drivers/usb/usbssp/gadget-dbg.c | 30 ++++
> >>> drivers/usb/usbssp/gadget-ext-caps.h | 53 ++++++
> >>> drivers/usb/usbssp/gadget-if.c | 24 +++
> >>> drivers/usb/usbssp/gadget.c | 242 +++++++++++++++++++++++++++
> >>> drivers/usb/usbssp/gadget.h | 15 ++
> >>> 6 files changed, 365 insertions(+)
> >>> create mode 100644 drivers/usb/usbssp/gadget-dbg.c
> >>> create mode 100644 drivers/usb/usbssp/gadget-ext-caps.h
> >>> create mode 100644 drivers/usb/usbssp/gadget-if.c
> >>>
> >>> diff --git a/drivers/usb/usbssp/Makefile b/drivers/usb/usbssp/Makefile
> >>> index d85f15afb51c..0606f3c63cd0 100644
> >>> --- a/drivers/usb/usbssp/Makefile
> >>> +++ b/drivers/usb/usbssp/Makefile
> >>> @@ -5,6 +5,7 @@ CFLAGS_gadget-trace.o := -I$(src)
> >>> obj-$(CONFIG_USB_USBSSP_GADGET) += usbssp.o
> >>> usbssp-y := usbssp-plat.o gadget-ring.o \
> >>> gadget.o
> >>> + gadget-dbg.o
> >>>
> >>> ifneq ($(CONFIG_TRACING),)
> >>> usbssp-y += gadget-trace.o
> >>> diff --git a/drivers/usb/usbssp/gadget-dbg.c b/drivers/usb/usbssp/gadget-dbg.c
> >>> new file mode 100644
> >>> index 000000000000..277617d1a996
> >>> --- /dev/null
> >>> +++ b/drivers/usb/usbssp/gadget-dbg.c
> >>> @@ -0,0 +1,30 @@
> >>> +// SPDX-License-Identifier: GPL-2.0
> >>> +/*
> >>> + * USBSSP device controller driver
> >>> + *
> >>> + * Copyright (C) 2018 Cadence.
> >>> + *
> >>> + * Author: Pawel Laszczak
> >>> + *
> >>> + * A lot of code based on Linux XHCI driver.
> >>> + * Origin: Copyright (C) 2008 Intel Corp
> >>> + */
> >>> +
> >>> +#include "gadget.h"
> >>> +
> >>> +void usbssp_dbg_trace(struct usbssp_udc *usbssp_data,
> >>> + void (*trace)(struct va_format *),
> >>> + const char *fmt, ...)
> >>> +{
> >>> + struct va_format vaf;
> >>> + va_list args;
> >>> +
> >>> + va_start(args, fmt);
> >>> + vaf.fmt = fmt;
> >>> + vaf.va = &args;
> >>> + dev_dbg(usbssp_data->dev, "%pV\n", &vaf);
> >>
> >> dev_dbg will mess up with timings to be useful.
> >> Why not just use one of the trace events you defined in gadget-trace.h?
> >
> > Function is equivalent of xhci_dbg_trace from XHCI driver.
> > It is a function of general use.
> > The problem with trace is that trace log is deleted after rebooting system.
> > Using trace log is problematic during developing and debugging driver.
>
> OK. But I think trace log is more useful for I/O related debugging as it
> keeps the timing impact minimal. For things that don't come in the I/O path
> you could use dev_dbg().
>
> > Do you know whether trace log is saved anywhere on disk ?
> >
> I think trace_cmd allows you to record on the disk or even send it over the network.
> https://urldefense.proofpoint.com/v2/url?u=https-
> 3A__lwn.net_Articles_410200_&d=DwICaQ&c=aUq983L2pue2FqKFoP6PGHMJQyoJ7kl3s3GZ-_haXqY&r=e1OgxfvkL0qo9XO6fX1gscva-
> w03uSYC1nIyxl89-
> rI&m=6MvsISpCCpIMBnuBegpBOtoNlpzPXgvFMxqIROGhRVw&s=N3v03AhO0AP6HlVTyDvCRQqpJx4kcnqdw5appuOYG6U&e=

Thanks, I will try it.

>
> >>> + trace(&vaf);
> >>> + va_end(args);
> >>> +}
> >>> +EXPORT_SYMBOL_GPL(usbssp_dbg_trace);
> >>> +
>
> <snip>
>
> >>> int usbssp_gadget_init(struct usbssp_udc *usbssp_data)
> >>> {
> >>> int ret;
> >>> +
> >>> + /*
> >>> + * Check the compiler generated sizes of structures that must be laid
> >>> + * out in specific ways for hardware access.
> >>> + */
> >>> + BUILD_BUG_ON(sizeof(struct usbssp_doorbell_array) != 2*32/8);
> >>> + BUILD_BUG_ON(sizeof(struct usbssp_slot_ctx) != 8*32/8);
> >>> + BUILD_BUG_ON(sizeof(struct usbssp_ep_ctx) != 8*32/8);
> >>> + /* usbssp_device has eight fields, and also
> >>> + * embeds one usbssp_slot_ctx and 31 usbssp_ep_ctx
> >>> + */
> >>> + BUILD_BUG_ON(sizeof(struct usbssp_stream_ctx) != 4*32/8);
> >>> + BUILD_BUG_ON(sizeof(union usbssp_trb) != 4*32/8);
> >>> + BUILD_BUG_ON(sizeof(struct usbssp_erst_entry) != 4*32/8);
> >>> + BUILD_BUG_ON(sizeof(struct usbssp_cap_regs) != 8*32/8);
> >>> + BUILD_BUG_ON(sizeof(struct usbssp_intr_reg) != 8*32/8);
> >>> + /* usbssp_run_regs has eight fields and embeds 128 usbssp_intr_regs */
> >>> + BUILD_BUG_ON(sizeof(struct usbssp_run_regs) != (8+8*128)*32/8);
> >>> +
> >>> + /* fill gadget fields */
> >>> + /*TODO: implements usbssp_gadget_ops object*/
> >>> + //usbssp_data->gadget.ops = &usbssp_gadget_ops;
> >>> + usbssp_data->gadget.name = "usbssp-gadget";
> >>> + usbssp_data->gadget.max_speed = USB_SPEED_SUPER_PLUS;
> >>> + usbssp_data->gadget.speed = USB_SPEED_UNKNOWN;
> >>> + usbssp_data->gadget.sg_supported = true;
> >>> + usbssp_data->gadget.lpm_capable = 1;
> >>> +
> >>> + usbssp_data->setup_buf = kzalloc(USBSSP_EP0_SETUP_SIZE, GFP_KERNEL);
> >>> + if (!usbssp_data->setup_buf)
> >>> + return -ENOMEM;
> >>> +
> >>> + /*USBSSP support not aligned buffer but this option
> >>> + * improve performance of this controller.
> >>> + */
> >>
> >> Multi-line comment formatting. Checkpach should complain.
> > It doesn't complain about this case. I don't know why.
> >
>
> Can you try with --strict option to checkpatch?

With -options I see this warning and also many others related
to "spaces preferred"

I have to check all patches again.
>
> >>> + usbssp_data->gadget.quirk_ep_out_aligned_size = true;
> >>> + ret = usbssp_gen_setup(usbssp_data);
> >>> + if (ret < 0) {
> >>> + dev_err(usbssp_data->dev,
> >>> + "Generic initialization failed with error code%d\n",
> >>> + ret);
> >>> + goto err3;
> >>> + }
> >>> +
> >>> + ret = usbssp_gadget_init_endpoint(usbssp_data);
> >>> + if (ret < 0) {
> >>> + dev_err(usbssp_data->dev, "failed to initialize endpoints\n");
> >>> + goto err1;
> >>> + }
> >>> +
> >>> + ret = usb_add_gadget_udc(usbssp_data->dev, &usbssp_data->gadget);
> >>> +
> >>> + if (ret) {
> >>> + dev_err(usbssp_data->dev, "failed to register udc\n");
> >>> + goto err2;
> >>> + }
> >>> +
> >>> + return ret;
> >>> +err2:
> >>> + usbssp_gadget_free_endpoint(usbssp_data);
> >>> +err1:
> >>> + usbssp_halt(usbssp_data);
> >>> + /*TODO add implementation of usbssp_reset function*/
> >>> + //usbssp_reset(usbssp_data);
> >>> + /*TODO add implementation of freeing memory*/
> >>> + //usbssp_mem_cleanup(usbssp_data);
> >>> +err3:
> >>> return ret;
> >>> }
> >>>
> >>> @@ -60,5 +298,9 @@ int usbssp_gadget_exit(struct usbssp_udc *usbssp_data)
> >>> {
> >>> int ret = 0;
> >>>
> >>> + usb_del_gadget_udc(&usbssp_data->gadget);
> >>> + usbssp_gadget_free_endpoint(usbssp_data);
> >>> + /*TODO: add usbssp_stop implementation*/
> >>> + //usbssp_stop(usbssp_data);
> >>> return ret;
> >>> }
> >>> diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h
> >>> index 55e20795d900..5d8918f8da84 100644
> >>> --- a/drivers/usb/usbssp/gadget.h
> >>> +++ b/drivers/usb/usbssp/gadget.h
> >>> @@ -12,8 +12,10 @@
> >>> #ifndef __LINUX_USBSSP_GADGET_H
> >>> #define __LINUX_USBSSP_GADGET_H
> >>>
> >>> +#include <linux/irq.h>
> >>> #include <linux/io-64-nonatomic-lo-hi.h>
> >>> #include <linux/usb/gadget.h>
> >>> +#include "gadget-ext-caps.h"
> >>>
> >>> /* Max number slots - only 1 is allowed */
> >>> #define DEV_MAX_SLOTS 1
> >>> @@ -1676,7 +1678,18 @@ static inline void usbssp_write_64(struct usbssp_udc *usbssp_data,
> >>> lo_hi_writeq(val, regs);
> >>> }
> >>>
> >>> +/* USBSSP memory management */
> >>> +void usbssp_dbg_trace(struct usbssp_udc *usbssp_data,
> >>> + void (*trace)(struct va_format *),
> >>> + const char *fmt, ...);
> >>
> >> what has this function to do with memory management?
> >>
> > Nothing,
> >
> >>> /* USBSSP Device controller glue */
> >>> +void usbssp_bottom_irq(struct work_struct *work);
> >>
> >> usbssp_bottom_irq() wasn't defined in this patch.
> > Removed from this patch
> >
> >>> +int usbssp_init(struct usbssp_udc *usbssp_data);
> >>> +void usbssp_stop(struct usbssp_udc *usbssp_data);
> >>> +int usbssp_handshake(void __iomem *ptr, u32 mask, u32 done, int usec);
> >>> +void usbssp_quiesce(struct usbssp_udc *usbssp_data);
> >>> +extern int usbssp_reset(struct usbssp_udc *usbssp_data);
> >>> +
> >>> int usbssp_suspend(struct usbssp_udc *usbssp_data, bool do_wakeup);
> >>> int usbssp_resume(struct usbssp_udc *usbssp_data, bool hibernated);
> >>>
> >>> @@ -1689,6 +1702,8 @@ dma_addr_t usbssp_trb_virt_to_dma(struct usbssp_segment *seg,
> >>> /* USBSSP gadget interface*/
> >>> int usbssp_gadget_init(struct usbssp_udc *usbssp_data);
> >>> int usbssp_gadget_exit(struct usbssp_udc *usbssp_data);
> >>> +void usbssp_gadget_free_endpoint(struct usbssp_udc *usbssp_data);
> >>> +int usbssp_gadget_init_endpoint(struct usbssp_udc *usbssp_data);
> >>>
> >>> static inline char *usbssp_slot_state_string(u32 state)
> >>> {
> >>>
> >>
>
> --
> cheers,
> -roger
>
> Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
> Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki
cheers,
Pawell

2018-08-06 13:15:51

by Pawel Laszczak

[permalink] [raw]
Subject: RE: [PATCH 06/31] usb: usbssp: added template functions used by upper layer.

>
> On 04/08/18 09:37, Pawel Laszczak wrote:
> >>> Patch adds some functionality for initialization process.
> >>> It adds to driver usbssp_reset and usbssp_start function.
> >>>
> >>> Next elements added are objects usbssp_gadget_ep0_ops,
> >>> usbssp_gadget_ep_ops and usbssp_gadget_ops. These objects
> >>> constitute the interface used by gadget subsystem.
> >>> At this moment functions related to these objects are empty
> >>> and do nothing.
> >>>
> >>> This patch also implements usbssp_gadget_init_endpoint and
> >>> usbssp_gadget_free_endpoint used during initialization.
> >>>
> >>> Signed-off-by: Pawel Laszczak <[email protected]>
> >>> ---
> >>> drivers/usb/usbssp/gadget-ext-caps.h | 3 +
> >>> drivers/usb/usbssp/gadget-if.c | 269 ++++++++++++++++++++++++++-
> >>> drivers/usb/usbssp/gadget.c | 84 ++++++++-
> >>> 3 files changed, 350 insertions(+), 6 deletions(-)
> >>>
> >>> diff --git a/drivers/usb/usbssp/gadget-ext-caps.h b/drivers/usb/usbssp/gadget-ext-caps.h
> >>> index 2bf327046376..86c0ce331037 100644
> >>> --- a/drivers/usb/usbssp/gadget-ext-caps.h
> >>> +++ b/drivers/usb/usbssp/gadget-ext-caps.h
> >>> @@ -51,3 +51,6 @@
> >>> #define USBSSP_CMD_EWE BIT(10)
> >>>
> >>> #define USBSSP_IRQS (USBSSP_CMD_EIE | USBSSP_CMD_HSEIE | USBSSP_CMD_EWE)
> >>> +
> >>> +/* true: Controller Not Ready to accept doorbell or op reg writes after reset */
> >>> +#define USBSSP_STS_CNR BIT(11)
> >>> diff --git a/drivers/usb/usbssp/gadget-if.c b/drivers/usb/usbssp/gadget-if.c
> >>> index d53e0fb65299..70def978b085 100644
> >>> --- a/drivers/usb/usbssp/gadget-if.c
> >>> +++ b/drivers/usb/usbssp/gadget-if.c
> >>> @@ -12,13 +12,278 @@
> >>> #include <linux/usb/composite.h>
> >>> #include "gadget.h"
> >>>
> >>> +static int usbssp_gadget_ep_enable(struct usb_ep *ep,
> >>> + const struct usb_endpoint_descriptor *desc)
> >>> +{
> >>> + struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
> >>> + int ret = 0;
> >>> +
> >>> + if (!ep_priv)
> >>> + return -EINVAL;
> >>> +
> >>> + /*TODO: implements this function*/
> >>> + return ret;
> >>> +}
> >>> +
> >>> +int usbssp_gadget_ep_disable(struct usb_ep *ep)
> >>> +{
> >>> + struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
> >>> + int ret = 0;
> >>> +
> >>> + if (!ep_priv)
> >>> + return -EINVAL;
> >>> +
> >>> + /*TODO: implements this function*/
> >>> + return ret;
> >>> +}
> >>> +
> >>> +static struct usb_request *usbssp_gadget_ep_alloc_request(struct usb_ep *ep,
> >>> + gfp_t gfp_flags)
> >>> +{
> >>> + struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
> >>> +
> >>> + if (!ep_priv)
> >>> + return NULL;
> >>> +
> >>> + /*TODO: implements this function*/
> >>> + return NULL;
> >>> +}
> >>> +
> >>> +static void usbssp_gadget_ep_free_request(struct usb_ep *ep,
> >>> + struct usb_request *request)
> >>> +{
> >>> + struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
> >>> +
> >>> + if (!ep_priv)
> >>> + return;
> >>> +
> >>> + /*TODO: implements this function*/
> >>> +}
> >>> +
> >>> +static int usbssp_gadget_ep_queue(struct usb_ep *ep,
> >>> + struct usb_request *request,
> >>> + gfp_t gfp_flags)
> >>> +{
> >>> + struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
> >>> + int ret = 0;
> >>> +
> >>> + if (!ep_priv)
> >>> + return -EINVAL;
> >>> +
> >>> + /*TODO: implements this function*/
> >>> + return ret;
> >>> +}
> >>> +
> >>> +static int usbssp_gadget_ep_dequeue(struct usb_ep *ep,
> >>> + struct usb_request *request)
> >>> +{
> >>> + struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
> >>> + int ret = 0;
> >>> +
> >>> + if (!ep_priv)
> >>> + return -EINVAL;
> >>> +
> >>> + /*TODO: implements this function*/
> >>> + return ret;
> >>> +}
> >>> +
> >>> +static int usbssp_gadget_ep_set_halt(struct usb_ep *ep, int value)
> >>> +{
> >>> + struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
> >>> + int ret = 0;
> >>> +
> >>> + if (!ep_priv)
> >>> + return -EINVAL;
> >>> +
> >>> + /*TODO: implements this function*/
> >>> + return ret;
> >>> +}
> >>> +
> >>> +static int usbssp_gadget_ep_set_wedge(struct usb_ep *ep)
> >>> +{
> >>> + struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
> >>> + int ret = 0;
> >>> +
> >>> + if (!ep_priv)
> >>> + return -EINVAL;
> >>> +
> >>> + /*TODO: implements this function*/
> >>> + return ret;
> >>> +}
> >>> +
> >>> +static const struct usb_ep_ops usbssp_gadget_ep0_ops = {
> >>> + .enable = usbssp_gadget_ep_enable,
> >>> + .disable = usbssp_gadget_ep_disable,
> >>> + .alloc_request = usbssp_gadget_ep_alloc_request,
> >>> + .free_request = usbssp_gadget_ep_free_request,
> >>> + .queue = usbssp_gadget_ep_queue,
> >>> + .dequeue = usbssp_gadget_ep_dequeue,
> >>> + .set_halt = usbssp_gadget_ep_set_halt,
> >>> + .set_wedge = usbssp_gadget_ep_set_wedge,
> >>> +};
> >>> +
> >>> +static const struct usb_ep_ops usbssp_gadget_ep_ops = {
> >>> + .enable = usbssp_gadget_ep_enable,
> >>> + .disable = usbssp_gadget_ep_disable,
> >>> + .alloc_request = usbssp_gadget_ep_alloc_request,
> >>> + .free_request = usbssp_gadget_ep_free_request,
> >>> + .queue = usbssp_gadget_ep_queue,
> >>> + .dequeue = usbssp_gadget_ep_dequeue,
> >>> + .set_halt = usbssp_gadget_ep_set_halt,
> >>> + .set_wedge = usbssp_gadget_ep_set_wedge,
> >>> +};
> >>> +
> >>> +static struct usb_endpoint_descriptor usbssp_gadget_ep0_desc = {
> >>> + .bLength = USB_DT_ENDPOINT_SIZE,
> >>> + .bDescriptorType = USB_DT_ENDPOINT,
> >>> + .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
> >>> +};
> >>> +
> >>> +static int usbssp_gadget_start(struct usb_gadget *g,
> >>> + struct usb_gadget_driver *driver)
> >>> +{
> >>> + struct usbssp_udc *usbssp_data = gadget_to_usbssp(g);
> >>> + int ret = 0;
> >>> +
> >>> + if (usbssp_data->gadget_driver) {
> >>> + dev_err(usbssp_data->dev, "%s is already bound to %s\n",
> >>> + usbssp_data->gadget.name,
> >>> + usbssp_data->gadget_driver->driver.name);
> >>> + ret = -EBUSY;
> >>> + }
> >>> +
> >>> + /*TODO: add implementation*/
> >>> + return ret;
> >>> +}
> >>> +
> >>> +static int usbssp_gadget_stop(struct usb_gadget *g)
> >>> +{
> >>> + struct usbssp_udc *usbssp_data = gadget_to_usbssp(g);
> >>> +
> >>> + if (!usbssp_data)
> >>> + return -EINVAL;
> >>> + /*TODO: add implementation*/
> >>> + return 0;
> >>> +}
> >>> +
> >>> +static int usbssp_gadget_get_frame(struct usb_gadget *g)
> >>> +{
> >>> + struct usbssp_udc *usbssp_data = gadget_to_usbssp(g);
> >>> +
> >>> + if (!usbssp_data)
> >>> + return -EINVAL;
> >>> +
> >>> + /*TODO: add implementation*/
> >>> + return 0;
> >>> +}
> >>> +
> >>> +static int usbssp_gadget_wakeup(struct usb_gadget *g)
> >>> +{
> >>> + struct usbssp_udc *usbssp_data = gadget_to_usbssp(g);
> >>> +
> >>> + if (!usbssp_data)
> >>> + return -EINVAL;
> >>> +
> >>> + /*TODO: add implementation*/
> >>> + return 0;
> >>> +}
> >>> +
> >>> +static int usbssp_gadget_set_selfpowered(struct usb_gadget *g,
> >>> + int is_selfpowered)
> >>> +{
> >>> + struct usbssp_udc *usbssp_data = gadget_to_usbssp(g);
> >>> +
> >>> + if (!usbssp_data)
> >>> + return -EINVAL;
> >>> +
> >>> + /*TODO: add implementation*/
> >>> + return 0;
> >>> +}
> >>> +
> >>> +static const struct usb_gadget_ops usbssp_gadget_ops = {
> >>> + .get_frame = usbssp_gadget_get_frame,
> >>> + .wakeup = usbssp_gadget_wakeup,
> >>> + .set_selfpowered = usbssp_gadget_set_selfpowered,
> >>> + .udc_start = usbssp_gadget_start,
> >>> + .udc_stop = usbssp_gadget_stop,
> >>> +};
> >>> +
> >>> int usbssp_gadget_init_endpoint(struct usbssp_udc *usbssp_data)
> >>
> >> Since we're initializing all endpoints this function should be named usbssp_gadget_init_endpoints().
> >>
> >>> {
> >>> - /*TODO: it has to be implemented*/
> >>> + int i = 0;
> >>> + struct usbssp_ep *ep_priv;
> >>> +
> >>> + usbssp_data->num_endpoints = USBSSP_ENDPOINTS_NUM;
> >>> + INIT_LIST_HEAD(&usbssp_data->gadget.ep_list);
> >>> +
> >>> + for (i = 1; i < usbssp_data->num_endpoints; i++) {
> >>> + bool direction = i & 1; /*start from OUT endpoint*/
> >>> + u8 epnum = (i >> 1);
> >>> +
> >>> + ep_priv = &usbssp_data->devs.eps[i-1];
> >>> + ep_priv->usbssp_data = usbssp_data;
> >>> + ep_priv->number = epnum;
> >>> + ep_priv->direction = direction; /*0 for OUT, 1 for IN*/
> >>> +
> >>> + snprintf(ep_priv->name, sizeof(ep_priv->name), "ep%d%s", epnum,
> >>> + (ep_priv->direction) ? "in" : "out");
> >>> +
> >>> + ep_priv->endpoint.name = ep_priv->name;
> >>> +
> >>> + if (ep_priv->number < 2) {
> >>> + ep_priv->endpoint.desc = &usbssp_gadget_ep0_desc;
> >>> + ep_priv->endpoint.comp_desc = NULL;
> >>> + }
> >>> +
> >>> + if (epnum == 0) {
> >>> + /*EP0 is bidirectional endpoint*/
> >>> + usb_ep_set_maxpacket_limit(&ep_priv->endpoint, 512);
> >>> + dev_dbg(usbssp_data->dev,
> >>> + "Initializing %s, MaxPack: %04x Type: Ctrl\n",
> >>> + ep_priv->name, 512);
> >>> + ep_priv->endpoint.maxburst = 1;
> >>> + ep_priv->endpoint.ops = &usbssp_gadget_ep0_ops;
> >>> + ep_priv->endpoint.caps.type_control = true;
> >>> +
> >>> + usbssp_data->usb_req_ep0_in.epnum = ep_priv->number;
> >>> + usbssp_data->usb_req_ep0_in.dep = ep_priv;
> >>> +
> >>> + if (!epnum)
> >>> + usbssp_data->gadget.ep0 = &ep_priv->endpoint;
> >>> + } else {
> >>> + usb_ep_set_maxpacket_limit(&ep_priv->endpoint, 1024);
> >>> + ep_priv->endpoint.maxburst = 15;
> >>> + ep_priv->endpoint.ops = &usbssp_gadget_ep_ops;
> >>> + list_add_tail(&ep_priv->endpoint.ep_list,
> >>> + &usbssp_data->gadget.ep_list);
> >>> + ep_priv->endpoint.caps.type_iso = true;
> >>> + ep_priv->endpoint.caps.type_bulk = true;
> >>> + ep_priv->endpoint.caps.type_int = true;
> >>> +
> >>> + }
> >>> +
> >>> + ep_priv->endpoint.caps.dir_in = direction;
> >>> + ep_priv->endpoint.caps.dir_out = !direction;
> >>
> >> Since you start from 1, ep0 will only be initialized as dir_in.
> >> Is it better to represent EP0in and EP0out separately?
> >> If so you can start i from 0 and use
> >> ep_priv = &usbssp_data->devs.eps[i];
> >>
> >> This should fix the direction. eps[0] will be ep0out and eps[1] will be ep0in.
> >
> > I wanted to be consistent with Device Context Data Structure from
> > XHCI specification (EP Context 0 BiDir, EP Context 1 OUT, EP Context 1 IN ...).
> >
> > I little change this function, and now it look like this:
> > int usbssp_gadget_init_endpoints(struct usbssp_udc *usbssp_data)
> > {
> > int i = 0;
> > struct usbssp_ep *ep_priv;
> >
> > usbssp_data->num_endpoints = USBSSP_ENDPOINTS_NUM;
> > INIT_LIST_HEAD(&usbssp_data->gadget.ep_list);
> >
> > for (i = 0; i < usbssp_data->num_endpoints; i++) {
> > bool direction = i & 1; /*start from OUT endpoint*/
> > u8 epnum = ((i + 1) >> 1);
> >
> > ep_priv = &usbssp_data->devs.eps[i];
> > ep_priv->usbssp_data = usbssp_data;
> > ep_priv->number = epnum;
> > ep_priv->direction = direction; /*0 for OUT, 1 for IN*/
> >
> > /*
> > *Ep0 is bidirectional so Ep0In and Ep0Out is represented by
> > * usbssp_data->devs.eps[0]
> > */
> > if (epnum == 0) {
> > snprintf(ep_priv->name, sizeof(ep_priv->name), "ep%d%s",
> > epnum, "BiDir");
> >
> > usb_ep_set_maxpacket_limit(&ep_priv->endpoint, 512);
> > ep_priv->endpoint.maxburst = 1;
> > ep_priv->endpoint.ops = &usbssp_gadget_ep0_ops;
> > ep_priv->endpoint.desc = &usbssp_gadget_ep0_desc;
> >
> > ep_priv->endpoint.comp_desc = NULL;
> > ep_priv->endpoint.caps.type_control = true;
> > ep_priv->endpoint.caps.dir_in = true;
> > ep_priv->endpoint.caps.dir_out = true;
> >
> > usbssp_data->usb_req_ep0_in.epnum = ep_priv->number;
> > usbssp_data->usb_req_ep0_in.dep = ep_priv;
> > usbssp_data->gadget.ep0 = &ep_priv->endpoint;
> >
> > } else {
> > snprintf(ep_priv->name, sizeof(ep_priv->name), "ep%d%s",
> > epnum, (ep_priv->direction) ? "in" : "out");
> >
> > usb_ep_set_maxpacket_limit(&ep_priv->endpoint, 1024);
> > ep_priv->endpoint.maxburst = 15;
> > ep_priv->endpoint.ops = &usbssp_gadget_ep_ops;
> > list_add_tail(&ep_priv->endpoint.ep_list,
> > &usbssp_data->gadget.ep_list);
> >
> > ep_priv->endpoint.caps.type_iso = true;
> > ep_priv->endpoint.caps.type_bulk = true;
> > ep_priv->endpoint.caps.type_int = true;
> >
> > ep_priv->endpoint.caps.dir_in = direction;
> > ep_priv->endpoint.caps.dir_out = !direction;
> > }
> >
> > ep_priv->endpoint.name = ep_priv->name;
>
> I think this is much better now.
>
> >
> > dev_dbg(usbssp_data->dev, "Init %s, MaxPack: %04x SupType: "
> > "CTRL: %s, INT: %s, BULK: %s, ISOC %s, "
> > "SupDir IN: %s, OUT: %s\n",
> > ep_priv->name, 1024,
> > (ep_priv->endpoint.caps.type_control) ? "yes" : "no",
> > (ep_priv->endpoint.caps.type_int) ? "yes" : "no",
> > (ep_priv->endpoint.caps.type_bulk) ? "yes" : "no",
> > (ep_priv->endpoint.caps.type_iso) ? "yes" : "no",
> > (ep_priv->endpoint.caps.dir_in) ? "yes" : "no",
> > (ep_priv->endpoint.caps.dir_out) ? "yes" : "no");
> >
>
> In some places you use usbssp_dbg_trace() and at others you use dev_dbg().
> Is it better to stick with one throughout?

Yes, it true, It would be better. I will think about that.

> > INIT_LIST_HEAD(&ep_priv->pending_list);
> > }
> >
> > return 0;
> > }
> >
> >>> +
> >>> + dev_dbg(usbssp_data->dev, "Init %s, MaxPack: %04x SupType:"
> >>> + " INT/BULK/ISOC , SupDir %s\n",
> >>> + ep_priv->name, 1024,
> >>> + (ep_priv->endpoint.caps.dir_in) ? "IN" : "OUT");
> >>> +
> >>> + INIT_LIST_HEAD(&ep_priv->pending_list);
> >>> + }
> >>> return 0;
> >>> }
> >>>
> >>> void usbssp_gadget_free_endpoint(struct usbssp_udc *usbssp_data)
> >>> {
> >>> - /*TODO: it has to be implemented*/
> >>> + int i;
> >>> + struct usbssp_ep *ep_priv;
> >>> +
> >>> + for (i = 0; i < usbssp_data->num_endpoints; i++) {
> >>> + ep_priv = &usbssp_data->devs.eps[i];
> >>> +
> >>
> >> if you start i from 1 then you can skip the if().
> >>
> >>> + if (ep_priv->number != 0)
> >>> + list_del(&ep_priv->endpoint.ep_list);
> >>> + }
> >>> }
> >>> diff --git a/drivers/usb/usbssp/gadget.c b/drivers/usb/usbssp/gadget.c
> >>> index 338ec2ec18b1..195f5777cf8a 100644
> >>> --- a/drivers/usb/usbssp/gadget.c
> >>> +++ b/drivers/usb/usbssp/gadget.c
> >>> @@ -103,6 +103,83 @@ int usbssp_halt(struct usbssp_udc *usbssp_data)
> >>> return ret;
> >>> }
> >>>
> >>> +/*
> >>> + * Set the run bit and wait for the device to be running.
> >>> + */
> >>> +int usbssp_start(struct usbssp_udc *usbssp_data)
> >>> +{
> >>> + u32 temp;
> >>> + int ret;
> >>> +
> >>> + temp = readl(&usbssp_data->op_regs->command);
> >>> + temp |= (CMD_RUN | CMD_DEVEN);
> >>> + usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
> >>> + "// Turn on USBSSP, cmd = 0x%x.", temp);
> >>> + writel(temp, &usbssp_data->op_regs->command);
> >>> +
> >>> + /*
> >>> + * Wait for the HCHalted Staus bit to be 0 to indicate the device is
> >>> + * running.
> >>> + */
> >>> + ret = usbssp_handshake(&usbssp_data->op_regs->status,
> >>> + STS_HALT, 0, USBSSP_MAX_HALT_USEC);
> >>> +
> >>> + if (ret == -ETIMEDOUT)
> >>> + dev_err(usbssp_data->dev, "Device took too long to start, waited %u microseconds.\n",
> >>> + USBSSP_MAX_HALT_USEC);
> >>> + if (!ret)
> >>> + /* clear state flags. Including dying, halted or removing */
> >>> + usbssp_data->usbssp_state = 0;
> >>> +
> >>> + return ret;
> >>> +}
> >>> +
> >>> +/*
> >>> + * Reset a halted DC.
> >>> + *
> >>> + * This resets pipelines, timers, counters, state machines, etc.
> >>> + * Transactions will be terminated immediately, and operational registers
> >>> + * will be set to their defaults.
> >>> + */
> >>> +int usbssp_reset(struct usbssp_udc *usbssp_data)
> >>> +{
> >>> + u32 command;
> >>> + u32 state;
> >>> + int ret;
> >>> +
> >>> + state = readl(&usbssp_data->op_regs->status);
> >>> +
> >>> + if (state == ~(u32)0) {
> >>> + dev_warn(usbssp_data->dev, "Device not accessible, reset failed.\n");
> >>> + return -ENODEV;
> >>> + }
> >>> +
> >>> + if ((state & STS_HALT) == 0) {
> >>> + dev_warn(usbssp_data->dev, "DC not halted, aborting reset.\n");
> >>
> >> DC is not a familiar abbreviation. Mabe just use Controller? or Device controller.
> >
> > In xhci driver is often used HC for host controller. In device driver I change this abbreviation
> > to DC. I will review the patches and change this to Controller.
> >
> >>
> >>> + return 0;
> >>> + }
> >>> +
> >>> + usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init, "// Reset the DC");
> >>> + command = readl(&usbssp_data->op_regs->command);
> >>> + command |= CMD_RESET;
> >>> + writel(command, &usbssp_data->op_regs->command);
> >>> +
> >>> + ret = usbssp_handshake(&usbssp_data->op_regs->command,
> >>> + CMD_RESET, 0, 10 * 1000 * 1000);
> >>> +
> >>> + if (ret)
> >>> + return ret;
> >>> + usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_init,
> >>> + "Wait for controller to be ready for doorbell rings");
> >>> + /*
> >>> + * USBSSP cannot write to any doorbells or operational registers other
> >>> + * than status until the "Controller Not Ready" flag is cleared.
> >>> + */
> >>> + ret = usbssp_handshake(&usbssp_data->op_regs->status,
> >>> + STS_CNR, 0, 10 * 1000 * 1000);
> >>> +
> >>> + return ret;
> >>> +}
> >>>
> >>> /*
> >>> * Initialize memory for gadget driver and USBSSP (one-time init).
> >>> @@ -179,8 +256,7 @@ int usbssp_gen_setup(struct usbssp_udc *usbssp_data)
> >>>
> >>> dev_dbg(usbssp_data->dev, "Resetting Device Controller\n");
> >>> /* Reset the internal DC memory state and registers. */
> >>> - /*TODO: add implementation of usbssp_reset function*/
> >>> - //retval = usbssp_reset(usbssp_data);
> >>> + retval = usbssp_reset(usbssp_data);
> >>> if (retval)
> >>> return retval;
> >>> dev_dbg(usbssp_data->dev, "Reset complete\n");
> >>> @@ -244,8 +320,7 @@ int usbssp_gadget_init(struct usbssp_udc *usbssp_data)
> >>> BUILD_BUG_ON(sizeof(struct usbssp_run_regs) != (8+8*128)*32/8);
> >>>
> >>> /* fill gadget fields */
> >>> - /*TODO: implements usbssp_gadget_ops object*/
> >>> - //usbssp_data->gadget.ops = &usbssp_gadget_ops;
> >>> + usbssp_data->gadget.ops = &usbssp_gadget_ops;
> >>> usbssp_data->gadget.name = "usbssp-gadget";
> >>> usbssp_data->gadget.max_speed = USB_SPEED_SUPER_PLUS;
> >>> usbssp_data->gadget.speed = USB_SPEED_UNKNOWN;
> >>> @@ -288,6 +363,7 @@ int usbssp_gadget_init(struct usbssp_udc *usbssp_data)
> >>> usbssp_halt(usbssp_data);
> >>> /*TODO add implementation of usbssp_reset function*/
> >>> //usbssp_reset(usbssp_data);
> >>> + usbssp_reset(usbssp_data);
> >>> /*TODO add implementation of freeing memory*/
> >>> //usbssp_mem_cleanup(usbssp_data);
> >>> err3:
> >>>
> >>
> >> --
> >> cheers,
> >> -roger
> >>
> >> Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
> >> Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki
> >
> > Cheers
> > Pawel
> >
>
> --
> cheers,
> -roger
>
> Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
> Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki

cheers
Pawel

2018-08-17 21:08:02

by Bin Liu

[permalink] [raw]
Subject: Re: [PATCH 00/31] Introduced new Cadence USBSSP DRD Driver

Hi,

On Thu, Jul 19, 2018 at 06:57:33PM +0100, Pawel Laszczak wrote:
> This patch set introduce new Cadence USBSSP DRD driver
> to linux kernel.

Does the driver have to use 'usbssp' as the controller prefix? Is 'ssp'
acceptable? It would make the tons of variable names shorter and the
code much cleaner to read.

Regards,
-Bin.

2018-08-21 16:19:44

by Pawel Laszczak

[permalink] [raw]
Subject: RE: [PATCH 00/31] Introduced new Cadence USBSSP DRD Driver

Hi,

USBSSP it's the project name and it was the reason for such prefix.

I agree with you that the shorter prefix could be better and we could get rid of some warnings reported by checkpatch. Currently we have more important issues related to testing and debugging driver so decision about change I leave for the feature. Such modification could be made as separate single patch.

Cheers,
Pawel
>
> Hi,
>
> On Thu, Jul 19, 2018 at 06:57:33PM +0100, Pawel Laszczak wrote:
> > This patch set introduce new Cadence USBSSP DRD driver
> > to linux kernel.
>
> Does the driver have to use 'usbssp' as the controller prefix? Is 'ssp'
> acceptable? It would make the tons of variable names shorter and the
> code much cleaner to read.
>
> Regards,
> -Bin.

2018-09-10 18:18:51

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH 00/31] Introduced new Cadence USBSSP DRD Driver

On Thu, Jul 19, 2018 at 06:57:33PM +0100, Pawel Laszczak wrote:
> This patch set introduce new Cadence USBSSP DRD driver
> to linux kernel.

Is this the v2 submission? If so, please always say so and properly
document somewhere what changed from v1 to this one.

thanks,

greg k-h

2018-09-10 18:20:55

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH 02/31] usb: usbssp: Added some decoding functions.

On Thu, Jul 19, 2018 at 06:57:35PM +0100, Pawel Laszczak wrote:
> This patch add additional functions that converts some fields to string.
>
> For example function usbssp_trb_comp_code_string take completion
> code value and return string describing completion code.
>
> Signed-off-by: Pawel Laszczak <[email protected]>
> ---
> drivers/usb/usbssp/gadget.h | 580 ++++++++++++++++++++++++++++++++++++
> 1 file changed, 580 insertions(+)
>
> diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h
> index 49e7271187cc..b5c17603af78 100644
> --- a/drivers/usb/usbssp/gadget.h
> +++ b/drivers/usb/usbssp/gadget.h
> @@ -930,6 +930,73 @@ struct usbssp_transfer_event {
> #define COMP_UNDEFINED_ERROR 33
> #define COMP_INVALID_STREAM_ID_ERROR 34
>
> +static inline const char *usbssp_trb_comp_code_string(u8 status)

<snip>


You have _giant_ inline functions here, why?

Please just put this all in a .c file and let the linker properly handle
things. You do not want to duplicate all of these crazy strings all
over the place where ever you call these functions.

And I am guessing this is only for some sort of "debugging" mode? If
so, shouldn't there be a way to not even build this in? Some systems
are very space constrained...

thanks,

greg k-h

2018-09-10 18:20:55

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH 00/31] Introduced new Cadence USBSSP DRD Driver

On Tue, Aug 21, 2018 at 02:50:19PM +0000, Pawel Laszczak wrote:
> Hi,
>
> USBSSP it's the project name and it was the reason for such prefix.
>
> I agree with you that the shorter prefix could be better and we could get rid of some warnings reported by checkpatch. Currently we have more important issues related to testing and debugging driver so decision about change I leave for the feature. Such modification could be made as separate single patch.

No, get it right at the beginning please. Having "my function names are
too long" errors right when you first submit the code implies that you
don't really care about meeting our normal coding style rules.

Now of course I _know_ you don't mean to imply that at all, right? :)

thanks,

greg k-h

2018-09-10 18:21:47

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH 00/31] Introduced new Cadence USBSSP DRD Driver

On Thu, Aug 02, 2018 at 04:26:45AM +0000, Pawel Laszczak wrote:
> > > This patch set introduce new Cadence USBSSP DRD driver to linux
> > > kernel.
> > >
> > > The Cadence USBSSP DRD Driver s a highly configurable IP Core which
> > > can be instantiated as Dual-Role Device (DRD), Peripheral Only and
> > > Host Only (XHCI) configurations.
> > >
> > > The current driver has been validated with FPGA burned. We have
> > > support for PCIe bus, which is used on FPGA prototyping.
> > >
> > > The host site of USBSSP controller is compliance with XHCI
> > > specification, so it works with standard XHCI linux driver.
> > >
> > > Also, device side of USBSSP controller was designed in such way to
> > > looks like XHCI. It means that most of logic of USBSSP controller is
> > > also compliance with XHCI specification.
> > >
> > > Consequently, the USBSSP driver for peripheral mode is very similar to
> > > XHCI driver.
> > >
> > > This version of driver supports only Device mode but DRD and Host mode
> > > will be added in the future.
> > >
> >
> > Based on the posting date this series looks like v3.
> >
> > You should add the version prefix to the patches next time.
> > e.g. [PATCH v4 ...]
> >
> I wanted to start versioning driver after this set patch will be approved.

What does that mean? No one "approves" a patch series and then you
start numbering it.

Have you read the kernel documentation for how to properly create and
submit a kernel patch series and write a good changelog?

thanks,

greg k-h

2018-09-11 05:49:39

by Felipe Balbi

[permalink] [raw]
Subject: Re: [PATCH 02/31] usb: usbssp: Added some decoding functions.


Hi,

Greg Kroah-Hartman <[email protected]> writes:
> On Thu, Jul 19, 2018 at 06:57:35PM +0100, Pawel Laszczak wrote:
>> This patch add additional functions that converts some fields to string.
>>
>> For example function usbssp_trb_comp_code_string take completion
>> code value and return string describing completion code.
>>
>> Signed-off-by: Pawel Laszczak <[email protected]>
>> ---
>> drivers/usb/usbssp/gadget.h | 580 ++++++++++++++++++++++++++++++++++++
>> 1 file changed, 580 insertions(+)
>>
>> diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h
>> index 49e7271187cc..b5c17603af78 100644
>> --- a/drivers/usb/usbssp/gadget.h
>> +++ b/drivers/usb/usbssp/gadget.h
>> @@ -930,6 +930,73 @@ struct usbssp_transfer_event {
>> #define COMP_UNDEFINED_ERROR 33
>> #define COMP_INVALID_STREAM_ID_ERROR 34
>>
>> +static inline const char *usbssp_trb_comp_code_string(u8 status)
>
> <snip>
>
>
> You have _giant_ inline functions here, why?
>
> Please just put this all in a .c file and let the linker properly handle
> things. You do not want to duplicate all of these crazy strings all
> over the place where ever you call these functions.
>
> And I am guessing this is only for some sort of "debugging" mode? If
> so, shouldn't there be a way to not even build this in? Some systems
> are very space constrained...

many of them seem to be a straight copy from xhci.

--
balbi

2018-09-11 08:12:57

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH 02/31] usb: usbssp: Added some decoding functions.

On Tue, Sep 11, 2018 at 08:48:43AM +0300, Felipe Balbi wrote:
>
> Hi,
>
> Greg Kroah-Hartman <[email protected]> writes:
> > On Thu, Jul 19, 2018 at 06:57:35PM +0100, Pawel Laszczak wrote:
> >> This patch add additional functions that converts some fields to string.
> >>
> >> For example function usbssp_trb_comp_code_string take completion
> >> code value and return string describing completion code.
> >>
> >> Signed-off-by: Pawel Laszczak <[email protected]>
> >> ---
> >> drivers/usb/usbssp/gadget.h | 580 ++++++++++++++++++++++++++++++++++++
> >> 1 file changed, 580 insertions(+)
> >>
> >> diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h
> >> index 49e7271187cc..b5c17603af78 100644
> >> --- a/drivers/usb/usbssp/gadget.h
> >> +++ b/drivers/usb/usbssp/gadget.h
> >> @@ -930,6 +930,73 @@ struct usbssp_transfer_event {
> >> #define COMP_UNDEFINED_ERROR 33
> >> #define COMP_INVALID_STREAM_ID_ERROR 34
> >>
> >> +static inline const char *usbssp_trb_comp_code_string(u8 status)
> >
> > <snip>
> >
> >
> > You have _giant_ inline functions here, why?
> >
> > Please just put this all in a .c file and let the linker properly handle
> > things. You do not want to duplicate all of these crazy strings all
> > over the place where ever you call these functions.
> >
> > And I am guessing this is only for some sort of "debugging" mode? If
> > so, shouldn't there be a way to not even build this in? Some systems
> > are very space constrained...
>
> many of them seem to be a straight copy from xhci.

Which doesn't mean it's a great model to copy :)

Let's learn from our past mistakes, having had to try to slim down a
kernel for a limited memory system is a chore that no one should have to
manually hack up the source tree just to accomplish it.

thanks,

greg k-h

2018-09-11 09:04:19

by Pawel Laszczak

[permalink] [raw]
Subject: RE: [PATCH 02/31] usb: usbssp: Added some decoding functions.

HI, All

Please stop review of this series. It's a pity of your time.

Probably the design of USBSSP device controller will be significantly changed and simplified, so
I will have to create new driver.

The planed change include:
- simplification and change registers map
- removing command queue
- simplification in input/output endpoint context
- and other.


It was not my decision. Thanks for all your comments.
I will take them into account in new driver.

Cheers,
Pawell




>On Tue, Sep 11, 2018 at 08:48:43AM +0300, Felipe Balbi wrote:
>>
>> Hi,
>>
>> Greg Kroah-Hartman <[email protected]> writes:
>> > On Thu, Jul 19, 2018 at 06:57:35PM +0100, Pawel Laszczak wrote:
>> >> This patch add additional functions that converts some fields to string.
>> >>
>> >> For example function usbssp_trb_comp_code_string take completion
>> >> code value and return string describing completion code.
>> >>
>> >> Signed-off-by: Pawel Laszczak <[email protected]>
>> >> ---
>> >> drivers/usb/usbssp/gadget.h | 580 ++++++++++++++++++++++++++++++++++++
>> >> 1 file changed, 580 insertions(+)
>> >>
>> >> diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h
>> >> index 49e7271187cc..b5c17603af78 100644
>> >> --- a/drivers/usb/usbssp/gadget.h
>> >> +++ b/drivers/usb/usbssp/gadget.h
>> >> @@ -930,6 +930,73 @@ struct usbssp_transfer_event {
>> >> #define COMP_UNDEFINED_ERROR 33
>> >> #define COMP_INVALID_STREAM_ID_ERROR 34
>> >>
>> >> +static inline const char *usbssp_trb_comp_code_string(u8 status)
>> >
>> > <snip>
>> >
>> >
>> > You have _giant_ inline functions here, why?
>> >
>> > Please just put this all in a .c file and let the linker properly handle
>> > things. You do not want to duplicate all of these crazy strings all
>> > over the place where ever you call these functions.
>> >
>> > And I am guessing this is only for some sort of "debugging" mode? If
>> > so, shouldn't there be a way to not even build this in? Some systems
>> > are very space constrained...
>>
>> many of them seem to be a straight copy from xhci.
>
>Which doesn't mean it's a great model to copy :)
>
>Let's learn from our past mistakes, having had to try to slim down a
>kernel for a limited memory system is a chore that no one should have to
>manually hack up the source tree just to accomplish it.
>
>thanks,
>
>greg k-h