1/******************************************************************************
2
3  Copyright (c) 2013-2018, Intel Corporation
4  All rights reserved.
5
6  Redistribution and use in source and binary forms, with or without
7  modification, are permitted provided that the following conditions are met:
8
9   1. Redistributions of source code must retain the above copyright notice,
10      this list of conditions and the following disclaimer.
11
12   2. Redistributions in binary form must reproduce the above copyright
13      notice, this list of conditions and the following disclaimer in the
14      documentation and/or other materials provided with the distribution.
15
16   3. Neither the name of the Intel Corporation nor the names of its
17      contributors may be used to endorse or promote products derived from
18      this software without specific prior written permission.
19
20  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  POSSIBILITY OF SUCH DAMAGE.
31
32******************************************************************************/
33
34#include "ixl.h"
35#include "ixl_pf.h"
36#include "ixl_iw.h"
37#include "ixl_iw_int.h"
38
39#ifdef	IXL_IW
40
41#define IXL_IW_VEC_BASE(pf)	((pf)->msix - (pf)->iw_msix)
42#define IXL_IW_VEC_COUNT(pf)	((pf)->iw_msix)
43#define IXL_IW_VEC_LIMIT(pf)	((pf)->msix)
44
45extern int ixl_enable_iwarp;
46
47static struct ixl_iw_state ixl_iw;
48static int ixl_iw_ref_cnt;
49
50static void
51ixl_iw_pf_msix_reset(struct ixl_pf *pf)
52{
53	struct i40e_hw *hw = &pf->hw;
54	u32 reg;
55	int vec;
56
57	for (vec = IXL_IW_VEC_BASE(pf); vec < IXL_IW_VEC_LIMIT(pf); vec++) {
58		reg = I40E_PFINT_LNKLSTN_FIRSTQ_INDX_MASK;
59		wr32(hw, I40E_PFINT_LNKLSTN(vec - 1), reg);
60	}
61
62	return;
63}
64
65static void
66ixl_iw_invoke_op(void *context, int pending)
67{
68	struct ixl_iw_pf_entry *pf_entry = (struct ixl_iw_pf_entry *)context;
69	struct ixl_iw_pf info;
70	bool initialize;
71	int err;
72
73	INIT_DEBUGOUT("begin");
74
75	mtx_lock(&ixl_iw.mtx);
76	if ((pf_entry->state.iw_scheduled == IXL_IW_PF_STATE_ON) &&
77	    (pf_entry->state.iw_current == IXL_IW_PF_STATE_OFF))
78		initialize = true;
79	else if ((pf_entry->state.iw_scheduled == IXL_IW_PF_STATE_OFF) &&
80	         (pf_entry->state.iw_current == IXL_IW_PF_STATE_ON))
81		initialize = false;
82	else {
83		/* nothing to be done, so finish here */
84		mtx_unlock(&ixl_iw.mtx);
85		return;
86	}
87	info = pf_entry->pf_info;
88	mtx_unlock(&ixl_iw.mtx);
89
90	if (initialize) {
91		err = ixl_iw.ops->init(&info);
92		if (err)
93			device_printf(pf_entry->pf->dev,
94				"%s: failed to initialize iwarp (err %d)\n",
95				__func__, err);
96		else
97			pf_entry->state.iw_current = IXL_IW_PF_STATE_ON;
98	} else {
99		err = ixl_iw.ops->stop(&info);
100		if (err)
101			device_printf(pf_entry->pf->dev,
102				"%s: failed to stop iwarp (err %d)\n",
103				__func__, err);
104		else {
105			ixl_iw_pf_msix_reset(pf_entry->pf);
106			pf_entry->state.iw_current = IXL_IW_PF_STATE_OFF;
107		}
108	}
109	return;
110}
111
112static void
113ixl_iw_uninit(void)
114{
115	INIT_DEBUGOUT("begin");
116
117	mtx_destroy(&ixl_iw.mtx);
118
119	return;
120}
121
122static void
123ixl_iw_init(void)
124{
125	INIT_DEBUGOUT("begin");
126
127	LIST_INIT(&ixl_iw.pfs);
128	mtx_init(&ixl_iw.mtx, "ixl_iw_pfs", NULL, MTX_DEF);
129	ixl_iw.registered = false;
130
131	return;
132}
133
134/******************************************************************************
135 * if_ixl internal API
136 *****************************************************************************/
137
138int
139ixl_iw_pf_init(struct ixl_pf *pf)
140{
141	struct ixl_iw_pf_entry *pf_entry;
142	struct ixl_iw_pf *pf_info;
143	int err = 0;
144
145	INIT_DEBUGOUT("begin");
146
147	mtx_lock(&ixl_iw.mtx);
148
149	LIST_FOREACH(pf_entry, &ixl_iw.pfs, node)
150		if (pf_entry->pf == pf)
151			break;
152	if (pf_entry == NULL) {
153		/* attempt to initialize PF not yet attached - sth is wrong */
154		device_printf(pf->dev, "%s: PF not found\n", __func__);
155		err = ENOENT;
156		goto out;
157	}
158
159	pf_info = &pf_entry->pf_info;
160
161	pf_info->handle	= (void *)pf;
162
163	pf_info->ifp		= pf->vsi.ifp;
164	pf_info->dev		= pf->dev;
165	pf_info->pci_mem	= pf->pci_mem;
166	pf_info->pf_id		= pf->hw.pf_id;
167	pf_info->mtu		= if_getmtu(pf->vsi.ifp);
168
169	pf_info->iw_msix.count	= IXL_IW_VEC_COUNT(pf);
170	pf_info->iw_msix.base	= IXL_IW_VEC_BASE(pf);
171
172	for (int i = 0; i < IXL_IW_MAX_USER_PRIORITY; i++)
173		pf_info->qs_handle[i] = le16_to_cpu(pf->vsi.info.qs_handle[0]);
174
175	pf_entry->state.pf = IXL_IW_PF_STATE_ON;
176	if (ixl_iw.registered) {
177		pf_entry->state.iw_scheduled = IXL_IW_PF_STATE_ON;
178		taskqueue_enqueue(ixl_iw.tq, &pf_entry->iw_task);
179	}
180
181out:
182	mtx_unlock(&ixl_iw.mtx);
183
184	return (err);
185}
186
187void
188ixl_iw_pf_stop(struct ixl_pf *pf)
189{
190	struct ixl_iw_pf_entry *pf_entry;
191
192	INIT_DEBUGOUT("begin");
193
194	mtx_lock(&ixl_iw.mtx);
195
196	LIST_FOREACH(pf_entry, &ixl_iw.pfs, node)
197		if (pf_entry->pf == pf)
198			break;
199	if (pf_entry == NULL) {
200		/* attempt to stop PF which has not been attached - sth is wrong */
201		device_printf(pf->dev, "%s: PF not found\n", __func__);
202		goto out;
203	}
204
205	pf_entry->state.pf = IXL_IW_PF_STATE_OFF;
206	if (pf_entry->state.iw_scheduled == IXL_IW_PF_STATE_ON) {
207		pf_entry->state.iw_scheduled = IXL_IW_PF_STATE_OFF;
208		if (ixl_iw.registered)
209			taskqueue_enqueue(ixl_iw.tq, &pf_entry->iw_task);
210	}
211
212out:
213	mtx_unlock(&ixl_iw.mtx);
214
215	return;
216}
217
218int
219ixl_iw_pf_attach(struct ixl_pf *pf)
220{
221	struct ixl_iw_pf_entry *pf_entry;
222	int err = 0;
223
224	INIT_DEBUGOUT("begin");
225
226	if (ixl_iw_ref_cnt == 0)
227		ixl_iw_init();
228
229	mtx_lock(&ixl_iw.mtx);
230
231	LIST_FOREACH(pf_entry, &ixl_iw.pfs, node)
232		if (pf_entry->pf == pf) {
233			device_printf(pf->dev, "%s: PF already exists\n",
234			    __func__);
235			err = EEXIST;
236			goto out;
237		}
238
239	pf_entry = malloc(sizeof(struct ixl_iw_pf_entry),
240			M_IXL, M_NOWAIT | M_ZERO);
241	if (pf_entry == NULL) {
242		device_printf(pf->dev,
243		    "%s: failed to allocate memory to attach new PF\n",
244		    __func__);
245		err = ENOMEM;
246		goto out;
247	}
248	pf_entry->pf = pf;
249	pf_entry->state.pf		= IXL_IW_PF_STATE_OFF;
250	pf_entry->state.iw_scheduled	= IXL_IW_PF_STATE_OFF;
251	pf_entry->state.iw_current	= IXL_IW_PF_STATE_OFF;
252
253	LIST_INSERT_HEAD(&ixl_iw.pfs, pf_entry, node);
254	ixl_iw_ref_cnt++;
255
256	TASK_INIT(&pf_entry->iw_task, 0, ixl_iw_invoke_op, pf_entry);
257out:
258	mtx_unlock(&ixl_iw.mtx);
259
260	return (err);
261}
262
263int
264ixl_iw_pf_detach(struct ixl_pf *pf)
265{
266	struct ixl_iw_pf_entry *pf_entry;
267	int err = 0;
268
269	INIT_DEBUGOUT("begin");
270
271	mtx_lock(&ixl_iw.mtx);
272
273	LIST_FOREACH(pf_entry, &ixl_iw.pfs, node)
274		if (pf_entry->pf == pf)
275			break;
276	if (pf_entry == NULL) {
277		/* attempt to stop PF which has not been attached - sth is wrong */
278		device_printf(pf->dev, "%s: PF not found\n", __func__);
279		err = ENOENT;
280		goto out;
281	}
282
283	if (pf_entry->state.pf != IXL_IW_PF_STATE_OFF) {
284		/* attempt to detach PF which has not yet been stopped - sth is wrong */
285		device_printf(pf->dev, "%s: failed - PF is still active\n",
286		    __func__);
287		err = EBUSY;
288		goto out;
289	}
290	LIST_REMOVE(pf_entry, node);
291	free(pf_entry, M_IXL);
292	ixl_iw_ref_cnt--;
293
294out:
295	mtx_unlock(&ixl_iw.mtx);
296
297	if (ixl_iw_ref_cnt == 0)
298		ixl_iw_uninit();
299
300	return (err);
301}
302
303
304/******************************************************************************
305 * API exposed to iw_ixl module
306 *****************************************************************************/
307
308int
309ixl_iw_pf_reset(void *pf_handle)
310{
311	struct ixl_pf *pf = (struct ixl_pf *)pf_handle;
312
313	INIT_DEBUGOUT("begin");
314
315	IXL_PF_LOCK(pf);
316	ixl_init_locked(pf);
317	IXL_PF_UNLOCK(pf);
318
319	return (0);
320}
321
322int
323ixl_iw_pf_msix_init(void *pf_handle,
324	struct ixl_iw_msix_mapping *msix_info)
325{
326	struct ixl_pf *pf = (struct ixl_pf *)pf_handle;
327	struct i40e_hw *hw = &pf->hw;
328	u32 reg;
329	int vec, i;
330
331	INIT_DEBUGOUT("begin");
332
333	if ((msix_info->aeq_vector < IXL_IW_VEC_BASE(pf)) ||
334	    (msix_info->aeq_vector >= IXL_IW_VEC_LIMIT(pf))) {
335		printf("%s: invalid MSI-X vector (%i) for AEQ\n",
336		    __func__, msix_info->aeq_vector);
337		return (EINVAL);
338	}
339	reg = I40E_PFINT_AEQCTL_CAUSE_ENA_MASK |
340		(msix_info->aeq_vector << I40E_PFINT_AEQCTL_MSIX_INDX_SHIFT) |
341		(msix_info->itr_indx << I40E_PFINT_AEQCTL_ITR_INDX_SHIFT);
342	wr32(hw, I40E_PFINT_AEQCTL, reg);
343
344	for (vec = IXL_IW_VEC_BASE(pf); vec < IXL_IW_VEC_LIMIT(pf); vec++) {
345		for (i = 0; i < msix_info->ceq_cnt; i++)
346			if (msix_info->ceq_vector[i] == vec)
347				break;
348		if (i == msix_info->ceq_cnt) {
349			/* this vector has no CEQ mapped */
350			reg = I40E_PFINT_LNKLSTN_FIRSTQ_INDX_MASK;
351			wr32(hw, I40E_PFINT_LNKLSTN(vec - 1), reg);
352		} else {
353			reg = (i & I40E_PFINT_LNKLSTN_FIRSTQ_INDX_MASK) |
354			    (I40E_QUEUE_TYPE_PE_CEQ <<
355			    I40E_PFINT_LNKLSTN_FIRSTQ_TYPE_SHIFT);
356			wr32(hw, I40E_PFINT_LNKLSTN(vec - 1), reg);
357
358			reg = I40E_PFINT_CEQCTL_CAUSE_ENA_MASK |
359			    (vec << I40E_PFINT_CEQCTL_MSIX_INDX_SHIFT) |
360			    (msix_info->itr_indx <<
361			    I40E_PFINT_CEQCTL_ITR_INDX_SHIFT) |
362			    (IXL_QUEUE_EOL <<
363			    I40E_PFINT_CEQCTL_NEXTQ_INDX_SHIFT);
364			wr32(hw, I40E_PFINT_CEQCTL(i), reg);
365		}
366	}
367
368	return (0);
369}
370
371int
372ixl_iw_register(struct ixl_iw_ops *ops)
373{
374	struct ixl_iw_pf_entry *pf_entry;
375	int err = 0;
376	int iwarp_cap_on_pfs = 0;
377
378	INIT_DEBUGOUT("begin");
379	LIST_FOREACH(pf_entry, &ixl_iw.pfs, node)
380		iwarp_cap_on_pfs += pf_entry->pf->hw.func_caps.iwarp;
381	if (!iwarp_cap_on_pfs && ixl_enable_iwarp) {
382		printf("%s: the device is not iwarp-capable, registering dropped\n",
383		    __func__);
384		return (ENODEV);
385	}
386	if (ixl_enable_iwarp == 0) {
387		printf("%s: enable_iwarp is off, registering dropped\n",
388		    __func__);
389		return (EACCES);
390	}
391
392	if ((ops->init == NULL) || (ops->stop == NULL)) {
393		printf("%s: invalid iwarp driver ops\n", __func__);
394		return (EINVAL);
395	}
396
397	mtx_lock(&ixl_iw.mtx);
398	if (ixl_iw.registered) {
399		printf("%s: iwarp driver already registered\n", __func__);
400		err = (EBUSY);
401		goto out;
402	}
403	ixl_iw.registered = true;
404	mtx_unlock(&ixl_iw.mtx);
405
406	ixl_iw.tq = taskqueue_create("ixl_iw", M_NOWAIT,
407		taskqueue_thread_enqueue, &ixl_iw.tq);
408	if (ixl_iw.tq == NULL) {
409		printf("%s: failed to create queue\n", __func__);
410		ixl_iw.registered = false;
411		return (ENOMEM);
412	}
413	taskqueue_start_threads(&ixl_iw.tq, 1, PI_NET, "ixl iw");
414
415	ixl_iw.ops = malloc(sizeof(struct ixl_iw_ops),
416			M_IXL, M_NOWAIT | M_ZERO);
417	if (ixl_iw.ops == NULL) {
418		printf("%s: failed to allocate memory\n", __func__);
419		taskqueue_free(ixl_iw.tq);
420		ixl_iw.registered = false;
421		return (ENOMEM);
422	}
423
424	ixl_iw.ops->init = ops->init;
425	ixl_iw.ops->stop = ops->stop;
426
427	mtx_lock(&ixl_iw.mtx);
428	LIST_FOREACH(pf_entry, &ixl_iw.pfs, node)
429		if (pf_entry->state.pf == IXL_IW_PF_STATE_ON) {
430			pf_entry->state.iw_scheduled = IXL_IW_PF_STATE_ON;
431			taskqueue_enqueue(ixl_iw.tq, &pf_entry->iw_task);
432		}
433out:
434	mtx_unlock(&ixl_iw.mtx);
435
436	return (err);
437}
438
439int
440ixl_iw_unregister(void)
441{
442	struct ixl_iw_pf_entry *pf_entry;
443	int iwarp_cap_on_pfs = 0;
444
445	INIT_DEBUGOUT("begin");
446
447	LIST_FOREACH(pf_entry, &ixl_iw.pfs, node)
448		iwarp_cap_on_pfs += pf_entry->pf->hw.func_caps.iwarp;
449	if (!iwarp_cap_on_pfs && ixl_enable_iwarp) {
450		printf("%s: attempt to unregister driver when no iwarp-capable device present\n",
451		    __func__);
452		return (ENODEV);
453	}
454
455	if (ixl_enable_iwarp == 0) {
456		printf("%s: attempt to unregister driver when enable_iwarp is off\n",
457		    __func__);
458		return (ENODEV);
459	}
460	mtx_lock(&ixl_iw.mtx);
461
462	if (!ixl_iw.registered) {
463		printf("%s: failed - iwarp driver has not been registered\n",
464		    __func__);
465		mtx_unlock(&ixl_iw.mtx);
466		return (ENOENT);
467	}
468
469	LIST_FOREACH(pf_entry, &ixl_iw.pfs, node)
470		if (pf_entry->state.iw_scheduled == IXL_IW_PF_STATE_ON) {
471			pf_entry->state.iw_scheduled = IXL_IW_PF_STATE_OFF;
472			taskqueue_enqueue(ixl_iw.tq, &pf_entry->iw_task);
473		}
474
475	ixl_iw.registered = false;
476
477	mtx_unlock(&ixl_iw.mtx);
478
479	LIST_FOREACH(pf_entry, &ixl_iw.pfs, node)
480		taskqueue_drain(ixl_iw.tq, &pf_entry->iw_task);
481	taskqueue_free(ixl_iw.tq);
482	ixl_iw.tq = NULL;
483	free(ixl_iw.ops, M_IXL);
484	ixl_iw.ops = NULL;
485
486	return (0);
487}
488
489#endif /* IXL_IW */
490