linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Pawel Laszczak <pawell@cadence.com>
To: unlisted-recipients:; (no To-header on input)
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	<linux-usb@vger.kernel.org>, Felipe Balbi <balbi@kernel.org>,
	<linux-kernel@vger.kernel.org>, <ltyrala@cadence.com>,
	<adouglas@cadence.com>, <pawell@cadence.com>
Subject: [PATCH 13/31] usb: usbssp: addec procedure for handlin Port Status Change events.
Date: Thu, 12 Jul 2018 06:47:10 +0100	[thread overview]
Message-ID: <1531374448-26532-14-git-send-email-pawell@cadence.com> (raw)
In-Reply-To: <1531374448-26532-1-git-send-email-pawell@cadence.com>

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 <pawell@cadence.com>
---
 drivers/usb/usbssp/Makefile      |   2 +-
 drivers/usb/usbssp/gadget-if.c   |  59 +++++
 drivers/usb/usbssp/gadget-port.c | 101 ++++++++
 drivers/usb/usbssp/gadget-ring.c | 426 ++++++++++++++++++++++++++++++-
 drivers/usb/usbssp/gadget.h      |  13 +
 5 files changed, 593 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 884352e80779..22479a6378c4 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 c3fa3be7d494..28118c1a0250 100644
--- a/drivers/usb/usbssp/gadget-if.c
+++ b/drivers/usb/usbssp/gadget-if.c
@@ -286,3 +286,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..c9c8aae369ba
--- /dev/null
+++ b/drivers/usb/usbssp/gadget-port.c
@@ -0,0 +1,101 @@
+// 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 e2fb81259ca1..46d43f3933f7 100644
--- a/drivers/usb/usbssp/gadget-ring.c
+++ b/drivers/usb/usbssp/gadget-ring.c
@@ -101,6 +101,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.
@@ -219,18 +224,83 @@ 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;
+
+	usbssp_err(usbssp_data,
+			"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*/
@@ -257,6 +327,177 @@ void usbssp_cleanup_command_queue(struct usbssp_udc *usbssp_data)
 		usbssp_complete_del_and_free_cmd(cur_cmd, COMP_COMMAND_ABORTED);
 }
 
+
+static void handle_vendor_event(struct usbssp_udc *usbssp_data,
+		union usbssp_trb *event)
+{
+	u32 trb_type;
+
+	trb_type = TRB_FIELD_TO_TYPE(le32_to_cpu(event->generic.field[3]));
+	usbssp_dbg(usbssp_data,
+		"Vendor specific event or Babble TRB type = %u\n", trb_type);
+}
+
+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)
+		usbssp_err(usbssp_data,
+			"WARN: USBSSP returned failed port status event\n");
+
+
+	port_id = GET_PORT_ID(le32_to_cpu(event->generic.field[0]));
+	usbssp_dbg(usbssp_data,
+		"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)) {
+		usbssp_err(usbssp_data, "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) {
+			usbssp_warn(usbssp_data, "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);
+	usbssp_dbg(usbssp_data, "PORTSC info: %s\n",
+			usbssp_decode_portsc(portsc));
+
+	if ((portsc & PORT_PLC) && (portsc & PORT_PLS_MASK) == XDEV_RESUME) {
+		usbssp_dbg(usbssp_data, "port resume event for port %d\n",
+				port_id);
+		cmd_regs = readl(&usbssp_data->op_regs->command);
+		if (!(cmd_regs & CMD_RUN)) {
+			usbssp_warn(usbssp_data, "DC is not running.\n");
+			goto cleanup;
+		}
+		if (DEV_SUPERSPEED_ANY(portsc)) {
+			usbssp_dbg(usbssp_data, "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)) {
+		usbssp_dbg(usbssp_data, "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)) {
+		usbssp_dbg(usbssp_data, "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)) {
+		usbssp_dbg(usbssp_data, "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)) {
+		usbssp_dbg(usbssp_data, "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)) {
+		usbssp_dbg(usbssp_data,
+				"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)) {
+		usbssp_dbg(usbssp_data,
+			"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)) {
+		usbssp_dbg(usbssp_data,
+			"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) {
+		usbssp_dbg(usbssp_data,
+			"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) {
+		usbssp_dbg(usbssp_data,
+			"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) {
+		usbssp_dbg(usbssp_data,
+			"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
@@ -330,6 +571,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) {
+		usbssp_err(usbssp_data, "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:
+		if ((le32_to_cpu(event->event_cmd.flags) & TRB_TYPE_BITMASK) >=
+		    TRB_TYPE(48))
+			handle_vendor_event(usbssp_data, event);
+		else
+			usbssp_warn(usbssp_data, "ERROR unknown event type %d\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) {
+		usbssp_dbg(usbssp_data, "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) {
+		usbssp_warn(usbssp_data, "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) {
+		usbssp_dbg(usbssp_data,
+				"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)
+			usbssp_warn(usbssp_data,
+					"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 d0ce20f35ec6..2615eb151925 100644
--- a/drivers/usb/usbssp/gadget.h
+++ b/drivers/usb/usbssp/gadget.h
@@ -1716,11 +1716,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)
 {
@@ -2187,4 +2198,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


  parent reply	other threads:[~2018-07-12  5:51 UTC|newest]

Thread overview: 47+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-07-12  5:46 [PATCH 00/31] Introduced new Cadence USBSSP DRD Driver Pawel Laszczak
2018-07-12  5:46 ` [PATCH 01/31] usb: usbssp: Defined register maps and other useful structures Pawel Laszczak
2018-07-12  6:09   ` Greg Kroah-Hartman
2018-07-12  9:19     ` Pawel Laszczak
2018-07-12 19:20     ` Pawel Laszczak
2018-07-12  5:46 ` [PATCH 02/31] usb: usbssp: Added some decoding functions Pawel Laszczak
2018-07-12  6:10   ` Greg Kroah-Hartman
2018-07-12  6:28     ` Pawel Laszczak
2018-07-12  6:35       ` Greg Kroah-Hartman
2018-07-12 16:43         ` Pawel Laszczak
2018-07-12 17:07           ` Greg Kroah-Hartman
2018-07-12  5:47 ` [PATCH 03/31] usb: usbssp: Add trace events used in driver Pawel Laszczak
2018-07-12  5:47 ` [PATCH 04/31] usb: usbssp: Added USBSSP platform driver Pawel Laszczak
2018-07-12  6:21   ` Greg Kroah-Hartman
2018-07-12 10:40     ` Pawel Laszczak
2018-07-12  5:47 ` [PATCH 05/31] usb: usbssp: Added first part of initialization sequence Pawel Laszczak
2018-07-12  6:27   ` Greg Kroah-Hartman
2018-07-12  9:03     ` Pawel Laszczak
2018-07-12  9:09       ` Greg Kroah-Hartman
2018-07-12  9:47         ` Pawel Laszczak
2018-07-12  5:47 ` [PATCH 06/31] usb: usbssp: added template functions used by upper layer Pawel Laszczak
2018-07-12  5:47 ` [PATCH 07/31] usb: usbssp: Initialization - added usbssp_mem_init Pawel Laszczak
2018-07-12  5:47 ` [PATCH 08/31] usb: usbssp: Added ring and segment handling functions Pawel Laszczak
2018-07-12  5:47 ` [PATCH 09/31] usb: usbssp: add implementation of usbssp_mem_cleanup Pawel Laszczak
2018-07-12  5:47 ` [PATCH 10/31] usb: usbssp: added usbssp_trb_in_td function Pawel Laszczak
2018-07-12  5:47 ` [PATCH 11/31] usb: usbssp: added function for stopping driver Pawel Laszczak
2018-07-12  5:47 ` [PATCH 12/31] usb: usbssp: added functions for queuing commands Pawel Laszczak
2018-07-12  5:47 ` Pawel Laszczak [this message]
2018-07-12  5:47 ` [PATCH 14/31] usb: usbssp: added procedure handling command completion events Pawel Laszczak
2018-07-12  5:47 ` [PATCH 15/31] usb: usbssp: added device controller error, transfer and SETUP completion event Pawel Laszczak
2018-07-12  5:47 ` [PATCH 16/31] usb: usbssp: added connect/disconnect procedures Pawel Laszczak
2018-07-12  5:47 ` [PATCH 17/31] usb: usbssp: added implementation of usbssp_halt_endpoint function Pawel Laszczak
2018-07-12  5:47 ` [PATCH 18/31] usb: usbssp: added handling of Port Reset event Pawel Laszczak
2018-07-12  5:47 ` [PATCH 19/31] usb: usbssp: added support for USB enumeration process Pawel Laszczak
2018-07-12  5:47 ` [PATCH 20/31] usb: usbssp: added queuing procedure for control transfer Pawel Laszczak
2018-07-12  5:47 ` [PATCH 21/31] usb: usbssp: added queuing procedure for BULK and INT transfer Pawel Laszczak
2018-07-12  5:47 ` [PATCH 22/31] usb: usbssp: added procedure removing request from transfer ring Pawel Laszczak
2018-07-12  5:47 ` [PATCH 23/31] usb: usbssp: added implementation of transfer events Pawel Laszczak
2018-07-12  5:47 ` [PATCH 24/31] usb: usbssp: added detecting command timeout Pawel Laszczak
2018-07-12  5:47 ` [PATCH 25/31] usb: usbssp: added implementation of usbssp interface Pawel Laszczak
2018-07-12  5:47 ` [PATCH 26/31] usb: usbssp: added endpoint configuration functionality Pawel Laszczak
2018-07-12  5:47 ` [PATCH 27/31] usb: usbssp: implements usbssp_gadget_ep_enable function Pawel Laszczak
2018-07-12  5:47 ` [PATCH 28/31] usb: usbssp: implemented usbssp_gadget_ep_disable function Pawel Laszczak
2018-07-12  5:47 ` [PATCH 29/31] usb: usbssp: added support for LPM Pawel Laszczak
2018-07-12  5:47 ` [PATCH 30/31] usb: usbssp: added support for TEST_MODE Pawel Laszczak
2018-07-12  5:47 ` [PATCH 31/31] usb: usbssp: add pci to platform driver wrapper Pawel Laszczak
2018-07-19 17:57 [PATCH 00/31] Introduced new Cadence USBSSP DRD Driver Pawel Laszczak
2018-07-19 17:57 ` [PATCH 13/31] usb: usbssp: addec procedure for handlin Port Status Change events Pawel Laszczak

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1531374448-26532-14-git-send-email-pawell@cadence.com \
    --to=pawell@cadence.com \
    --cc=adouglas@cadence.com \
    --cc=balbi@kernel.org \
    --cc=gregkh@linuxfoundation.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-usb@vger.kernel.org \
    --cc=ltyrala@cadence.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).