1325618Ssbruno/*
2325618Ssbruno *   BSD LICENSE
3325618Ssbruno *
4325618Ssbruno *   Copyright(c) 2017 Cavium, Inc.. All rights reserved.
5325618Ssbruno *   All rights reserved.
6325618Ssbruno *
7325618Ssbruno *   Redistribution and use in source and binary forms, with or without
8325618Ssbruno *   modification, are permitted provided that the following conditions
9325618Ssbruno *   are met:
10325618Ssbruno *
11325618Ssbruno *     * Redistributions of source code must retain the above copyright
12325618Ssbruno *       notice, this list of conditions and the following disclaimer.
13325618Ssbruno *     * Redistributions in binary form must reproduce the above copyright
14325618Ssbruno *       notice, this list of conditions and the following disclaimer in
15325618Ssbruno *       the documentation and/or other materials provided with the
16325618Ssbruno *       distribution.
17325618Ssbruno *     * Neither the name of Cavium, Inc. nor the names of its
18325618Ssbruno *       contributors may be used to endorse or promote products derived
19325618Ssbruno *       from this software without specific prior written permission.
20325618Ssbruno *
21325618Ssbruno *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22325618Ssbruno *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23325618Ssbruno *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24325618Ssbruno *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25325618Ssbruno *   OWNER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26325618Ssbruno *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27325618Ssbruno *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28325618Ssbruno *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29325618Ssbruno *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30325618Ssbruno *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31325618Ssbruno *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32325618Ssbruno */
33325618Ssbruno/*$FreeBSD: stable/11/sys/dev/liquidio/base/lio_response_manager.c 325618 2017-11-09 19:52:56Z sbruno $*/
34325618Ssbruno
35325618Ssbruno#include "lio_bsd.h"
36325618Ssbruno#include "lio_common.h"
37325618Ssbruno#include "lio_droq.h"
38325618Ssbruno#include "lio_iq.h"
39325618Ssbruno#include "lio_response_manager.h"
40325618Ssbruno#include "lio_device.h"
41325618Ssbruno#include "lio_main.h"
42325618Ssbruno
43325618Ssbrunostatic void	lio_poll_req_completion(void *arg, int pending);
44325618Ssbruno
45325618Ssbrunoint
46325618Ssbrunolio_setup_response_list(struct octeon_device *oct)
47325618Ssbruno{
48325618Ssbruno	struct lio_tq	*ctq;
49325618Ssbruno	int		i, ret = 0;
50325618Ssbruno
51325618Ssbruno	for (i = 0; i < LIO_MAX_RESPONSE_LISTS; i++) {
52325618Ssbruno		STAILQ_INIT(&oct->response_list[i].head);
53325618Ssbruno		mtx_init(&oct->response_list[i].lock, "response_list_lock",
54325618Ssbruno			 NULL, MTX_DEF);
55325618Ssbruno		atomic_store_rel_int(&oct->response_list[i].pending_req_count,
56325618Ssbruno				     0);
57325618Ssbruno	}
58325618Ssbruno	mtx_init(&oct->cmd_resp_wqlock, "cmd_resp_wqlock", NULL, MTX_DEF);
59325618Ssbruno
60325618Ssbruno	ctq = &oct->dma_comp_tq;
61325618Ssbruno	ctq->tq = taskqueue_create("lio_dma_comp", M_WAITOK,
62325618Ssbruno				   taskqueue_thread_enqueue, &ctq->tq);
63325618Ssbruno	if (ctq->tq == NULL) {
64325618Ssbruno		lio_dev_err(oct, "failed to create wq thread\n");
65325618Ssbruno		return (-ENOMEM);
66325618Ssbruno	}
67325618Ssbruno
68325618Ssbruno	TIMEOUT_TASK_INIT(ctq->tq, &ctq->work, 0, lio_poll_req_completion,
69325618Ssbruno			  (void *)ctq);
70325618Ssbruno	ctq->ctxptr = oct;
71325618Ssbruno
72325618Ssbruno	oct->cmd_resp_state = LIO_DRV_ONLINE;
73325618Ssbruno	taskqueue_start_threads(&ctq->tq, 1, PI_NET, "lio%d_dma_comp",
74325618Ssbruno				oct->octeon_id);
75325618Ssbruno	taskqueue_enqueue_timeout(ctq->tq, &ctq->work, lio_ms_to_ticks(50));
76325618Ssbruno
77325618Ssbruno	return (ret);
78325618Ssbruno}
79325618Ssbruno
80325618Ssbrunovoid
81325618Ssbrunolio_delete_response_list(struct octeon_device *oct)
82325618Ssbruno{
83325618Ssbruno
84325618Ssbruno	if (oct->dma_comp_tq.tq != NULL) {
85325618Ssbruno		while (taskqueue_cancel_timeout(oct->dma_comp_tq.tq,
86325618Ssbruno						&oct->dma_comp_tq.work, NULL))
87325618Ssbruno			taskqueue_drain_timeout(oct->dma_comp_tq.tq,
88325618Ssbruno						&oct->dma_comp_tq.work);
89325618Ssbruno		taskqueue_free(oct->dma_comp_tq.tq);
90325618Ssbruno		oct->dma_comp_tq.tq = NULL;
91325618Ssbruno	}
92325618Ssbruno}
93325618Ssbruno
94325618Ssbrunoint
95325618Ssbrunolio_process_ordered_list(struct octeon_device *octeon_dev,
96325618Ssbruno			 uint32_t force_quit)
97325618Ssbruno{
98325618Ssbruno	struct lio_response_list	*ordered_sc_list;
99325618Ssbruno	struct lio_soft_command		*sc;
100325618Ssbruno	uint64_t			status64;
101325618Ssbruno	uint32_t			status;
102325618Ssbruno	int				request_complete = 0;
103325618Ssbruno	int				resp_to_process;
104325618Ssbruno
105325618Ssbruno	resp_to_process = LIO_MAX_ORD_REQS_TO_PROCESS;
106325618Ssbruno
107325618Ssbruno	ordered_sc_list = &octeon_dev->response_list[LIO_ORDERED_SC_LIST];
108325618Ssbruno
109325618Ssbruno	do {
110325618Ssbruno		mtx_lock(&ordered_sc_list->lock);
111325618Ssbruno
112325618Ssbruno		if (STAILQ_EMPTY(&ordered_sc_list->head)) {
113325618Ssbruno			/*
114325618Ssbruno			 * ordered_sc_list is empty; there is nothing to
115325618Ssbruno			 * process
116325618Ssbruno			 */
117325618Ssbruno			mtx_unlock(&ordered_sc_list->lock);
118325618Ssbruno			return (1);
119325618Ssbruno		}
120325618Ssbruno
121325618Ssbruno		sc = LIO_STAILQ_FIRST_ENTRY(&ordered_sc_list->head,
122325618Ssbruno					    struct lio_soft_command, node);
123325618Ssbruno
124325618Ssbruno		status = LIO_REQUEST_PENDING;
125325618Ssbruno
126325618Ssbruno		/*
127325618Ssbruno		 * check if octeon has finished DMA'ing a response to where
128325618Ssbruno		 * rptr is pointing to
129325618Ssbruno		 */
130325618Ssbruno		status64 = *sc->status_word;
131325618Ssbruno
132325618Ssbruno		if (status64 != COMPLETION_WORD_INIT) {
133325618Ssbruno			/*
134325618Ssbruno			 * This logic ensures that all 64b have been written.
135325618Ssbruno			 * 1. check byte 0 for non-FF
136325618Ssbruno			 * 2. if non-FF, then swap result from BE to host order
137325618Ssbruno			 * 3. check byte 7 (swapped to 0) for non-FF
138325618Ssbruno			 * 4. if non-FF, use the low 32-bit status code
139325618Ssbruno			 * 5. if either byte 0 or byte 7 is FF, don't use status
140325618Ssbruno			 */
141325618Ssbruno			if ((status64 & 0xff) != 0xff) {
142325618Ssbruno				lio_swap_8B_data(&status64, 1);
143325618Ssbruno				if (((status64 & 0xff) != 0xff)) {
144325618Ssbruno					/* retrieve 16-bit firmware status */
145325618Ssbruno					status = (uint32_t)(status64 &
146325618Ssbruno							    0xffffULL);
147325618Ssbruno					if (status) {
148325618Ssbruno						status = LIO_FW_STATUS_CODE(
149325618Ssbruno									status);
150325618Ssbruno					} else {
151325618Ssbruno						/* i.e. no error */
152325618Ssbruno						status = LIO_REQUEST_DONE;
153325618Ssbruno					}
154325618Ssbruno				}
155325618Ssbruno			}
156325618Ssbruno		} else if (force_quit || (sc->timeout &&
157325618Ssbruno			   lio_check_timeout(ticks, sc->timeout))) {
158325618Ssbruno			lio_dev_err(octeon_dev, "%s: cmd failed, timeout (%u, %u)\n",
159325618Ssbruno				    __func__, ticks, sc->timeout);
160325618Ssbruno			status = LIO_REQUEST_TIMEOUT;
161325618Ssbruno		}
162325618Ssbruno
163325618Ssbruno		if (status != LIO_REQUEST_PENDING) {
164325618Ssbruno			/* we have received a response or we have timed out */
165325618Ssbruno			/* remove node from linked list */
166325618Ssbruno			STAILQ_REMOVE(&octeon_dev->response_list
167325618Ssbruno				      [LIO_ORDERED_SC_LIST].head,
168325618Ssbruno				      &sc->node, lio_stailq_node, entries);
169325618Ssbruno			atomic_subtract_int(&octeon_dev->response_list
170325618Ssbruno					    [LIO_ORDERED_SC_LIST].
171325618Ssbruno					    pending_req_count, 1);
172325618Ssbruno			mtx_unlock(&ordered_sc_list->lock);
173325618Ssbruno
174325618Ssbruno			if (sc->callback != NULL)
175325618Ssbruno				sc->callback(octeon_dev, status,
176325618Ssbruno					     sc->callback_arg);
177325618Ssbruno
178325618Ssbruno			request_complete++;
179325618Ssbruno
180325618Ssbruno		} else {
181325618Ssbruno			/* no response yet */
182325618Ssbruno			request_complete = 0;
183325618Ssbruno			mtx_unlock(&ordered_sc_list->lock);
184325618Ssbruno		}
185325618Ssbruno
186325618Ssbruno		/*
187325618Ssbruno		 * If we hit the Max Ordered requests to process every loop,
188325618Ssbruno		 * we quit and let this function be invoked the next time
189325618Ssbruno		 * the poll thread runs to process the remaining requests.
190325618Ssbruno		 * This function can take up the entire CPU if there is no
191325618Ssbruno		 * upper limit to the requests processed.
192325618Ssbruno		 */
193325618Ssbruno		if (request_complete >= resp_to_process)
194325618Ssbruno			break;
195325618Ssbruno	} while (request_complete);
196325618Ssbruno
197325618Ssbruno	return (0);
198325618Ssbruno}
199325618Ssbruno
200325618Ssbrunostatic void
201325618Ssbrunolio_poll_req_completion(void *arg, int pending)
202325618Ssbruno{
203325618Ssbruno	struct lio_tq		*ctq = (struct lio_tq *)arg;
204325618Ssbruno	struct octeon_device	*oct = (struct octeon_device *)ctq->ctxptr;
205325618Ssbruno
206325618Ssbruno	lio_process_ordered_list(oct, 0);
207325618Ssbruno	taskqueue_enqueue_timeout(ctq->tq, &ctq->work, lio_ms_to_ticks(50));
208325618Ssbruno}
209