1/*-
2 * Copyright 2016-2023 Microchip Technology, Inc. and/or its subsidiaries.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26
27#include "smartpqi_includes.h"
28
29
30/*
31 * Function to get processor count
32 */
33int
34os_get_processor_config(pqisrc_softstate_t *softs)
35{
36	DBG_FUNC("IN\n");
37	softs->num_cpus_online = mp_ncpus;
38	bsd_set_hint_adapter_cpu_config(softs);
39	DBG_FUNC("OUT\n");
40
41	return PQI_STATUS_SUCCESS;
42}
43
44/*
45 * Function to get interrupt count and type supported
46 */
47int
48os_get_intr_config(pqisrc_softstate_t *softs)
49{
50	device_t dev = softs->os_specific.pqi_dev;
51	int msi_count = pci_msix_count(dev);
52	int error = BSD_SUCCESS;
53
54	DBG_FUNC("IN\n");
55
56	if (msi_count > softs->num_cpus_online)
57		msi_count = softs->num_cpus_online;
58	if (msi_count > PQI_MAX_MSIX)
59		msi_count = PQI_MAX_MSIX;
60	if (msi_count == 0 || (error = pci_alloc_msix(dev, &msi_count)) != 0) {
61		device_printf(dev, "alloc msix failed - msi_count=%d, err=%d; "
62                                   "will try MSI\n", msi_count, error);
63		pci_release_msi(dev);
64	} else {
65		softs->intr_count = msi_count;
66		softs->intr_type = INTR_TYPE_MSIX;
67		softs->os_specific.msi_enabled = TRUE;
68		device_printf(dev, "using MSI-X interrupts (%d vectors)\n",
69			msi_count);
70	}
71	if (!softs->intr_type) {
72		msi_count = 1;
73		if ((error = pci_alloc_msi(dev, &msi_count)) != 0) {
74			device_printf(dev, "alloc msi failed - err=%d; "
75				"will use INTx\n", error);
76			pci_release_msi(dev);
77		} else {
78			softs->os_specific.msi_enabled = TRUE;
79			softs->intr_count = msi_count;
80			softs->intr_type = INTR_TYPE_MSI;
81			device_printf(dev, "using MSI interrupts\n");
82		}
83	}
84
85	if (!softs->intr_type) {
86		device_printf(dev, "using legacy interrupts\n");
87		softs->intr_type = INTR_TYPE_FIXED;
88		softs->intr_count = 1;
89	}
90
91	error = bsd_status_to_pqi_status(BSD_SUCCESS);
92
93	DBG_FUNC("OUT\n");
94
95	return error;
96}
97
98void
99os_eventtaskqueue_enqueue(pqisrc_softstate_t *sc)
100{
101	taskqueue_enqueue(taskqueue_swi, &sc->os_specific.event_task);
102}
103
104void
105pqisrc_event_worker(void *arg1, int arg2)
106{
107	pqisrc_ack_all_events(arg1);
108}
109
110/*
111 * ithread routine to handle uniprocessor systems
112 */
113static void
114shared_ithread_routine(void *arg)
115{
116	pqi_intr_ctx_t *intr_ctx = (pqi_intr_ctx_t *)arg;
117	pqisrc_softstate_t *softs = device_get_softc(intr_ctx->pqi_dev);
118	int oq_id  = intr_ctx->oq_id;
119
120	DBG_FUNC("IN\n");
121
122	if (!softs)
123		return;
124
125	pqisrc_process_response_queue(softs, oq_id);
126	pqisrc_process_event_intr_src(softs, oq_id - 1);
127
128	DBG_FUNC("OUT\n");
129}
130
131/*
132 * ithread routine to process non event response
133 */
134static void
135common_ithread_routine(void *arg)
136{
137	pqi_intr_ctx_t *intr_ctx = (pqi_intr_ctx_t *)arg;
138	pqisrc_softstate_t *softs = device_get_softc(intr_ctx->pqi_dev);
139	int oq_id  = intr_ctx->oq_id;
140
141	DBG_FUNC("IN\n");
142
143	if (!softs)
144		return;
145
146	pqisrc_process_response_queue(softs, oq_id);
147
148	DBG_FUNC("OUT\n");
149}
150
151static void
152event_ithread_routine(void *arg)
153{
154	pqi_intr_ctx_t *intr_ctx = (pqi_intr_ctx_t *)arg;
155	pqisrc_softstate_t *softs = device_get_softc(intr_ctx->pqi_dev);
156	int oq_id  = intr_ctx->oq_id;
157
158	DBG_FUNC("IN\n");
159
160	if (!softs)
161		return;
162
163	pqisrc_process_event_intr_src(softs, oq_id);
164
165	DBG_FUNC("OUT\n");
166}
167
168/*
169 * Registration of legacy interrupt in case MSI is unsupported
170 */
171int
172register_legacy_intr(pqisrc_softstate_t *softs)
173{
174	int error = BSD_SUCCESS;
175	device_t dev;
176
177	DBG_FUNC("IN\n");
178
179	dev = softs->os_specific.pqi_dev;
180
181	softs->os_specific.pqi_irq_rid[0] = 0;
182	softs->os_specific.pqi_irq[0] = bus_alloc_resource_any(dev, \
183		SYS_RES_IRQ, &softs->os_specific.pqi_irq_rid[0],
184		RF_ACTIVE | RF_SHAREABLE);
185	if (NULL == softs->os_specific.pqi_irq[0]) {
186		DBG_ERR("Failed to allocate resource for interrupt\n");
187		return ENXIO;
188	}
189	if ((softs->os_specific.msi_ctx = os_mem_alloc(softs,sizeof(pqi_intr_ctx_t))) == NULL) {
190		DBG_ERR("Failed to allocate memory for msi_ctx\n");
191		return ENXIO;
192	}
193	softs->os_specific.msi_ctx[0].pqi_dev = dev;
194	/* For Legacy support oq_id should be one */
195	softs->os_specific.msi_ctx[0].oq_id = 1;
196
197	error = bus_setup_intr(dev, softs->os_specific.pqi_irq[0],
198				INTR_TYPE_CAM | INTR_MPSAFE, \
199				NULL, shared_ithread_routine,
200				&softs->os_specific.msi_ctx[0],
201				&softs->os_specific.intrcookie[0]);
202	if (error) {
203		DBG_ERR("Failed to setup legacy interrupt err = %d\n", error);
204		return error;
205	}
206	softs->os_specific.intr_registered[0] = TRUE;
207
208	DBG_FUNC("OUT error = %d\n", error);
209
210	return error;
211}
212
213/*
214 * Registration of MSIx
215 */
216int
217register_msix_intr(pqisrc_softstate_t *softs)
218{
219	int error = BSD_SUCCESS;
220	int i = 0;
221	device_t dev = softs->os_specific.pqi_dev;
222	int msix_count = softs->intr_count;
223	size_t msix_size =  sizeof(pqi_intr_ctx_t) * msix_count;
224
225	DBG_FUNC("IN\n");
226
227	softs->os_specific.msi_ctx = os_mem_alloc(softs, msix_size);
228	if (!softs->os_specific.msi_ctx) {
229		DBG_ERR("Memory allocation failed, Requested memory:%lu bytes\n", (unsigned long)msix_size);
230		return ENXIO;
231	}
232
233	/*Add shared handler */
234	if (softs->share_opq_and_eventq) {
235		softs->os_specific.pqi_irq_rid[i] = i+1;
236		softs->os_specific.pqi_irq[i] = bus_alloc_resource_any(dev, \
237						SYS_RES_IRQ,
238						&softs->os_specific.pqi_irq_rid[i],
239						RF_SHAREABLE |  RF_ACTIVE);
240		if (NULL == softs->os_specific.pqi_irq[i]) {
241			DBG_ERR("Failed to allocate \
242				event interrupt resource\n");
243			return ENXIO;
244		}
245
246		softs->os_specific.msi_ctx[i].pqi_dev = dev;
247		softs->os_specific.msi_ctx[i].oq_id = i+1;
248
249		error = bus_setup_intr(dev,softs->os_specific.pqi_irq[i],
250					INTR_TYPE_CAM | INTR_MPSAFE,\
251					NULL,
252					shared_ithread_routine,
253					&softs->os_specific.msi_ctx[i],
254					&softs->os_specific.intrcookie[i]);
255
256		if (error) {
257			DBG_ERR("Failed to setup interrupt for events r=%d\n",
258				error);
259			return error;
260		}
261		softs->os_specific.intr_registered[i] = TRUE;
262	}
263	else {
264		/* Add event handler */
265		softs->os_specific.pqi_irq_rid[i] = i+1;
266		softs->os_specific.pqi_irq[i] = bus_alloc_resource_any(dev, \
267						SYS_RES_IRQ,
268						&softs->os_specific.pqi_irq_rid[i],
269						RF_SHAREABLE |  RF_ACTIVE);
270		if (NULL == softs->os_specific.pqi_irq[i]) {
271			DBG_ERR("Failed to allocate event interrupt resource\n");
272			return ENXIO;
273		}
274
275		softs->os_specific.msi_ctx[i].pqi_dev = dev;
276		softs->os_specific.msi_ctx[i].oq_id = i;
277
278		error = bus_setup_intr(dev,softs->os_specific.pqi_irq[i],
279					INTR_TYPE_CAM | INTR_MPSAFE,\
280                       			NULL,
281					event_ithread_routine,
282					&softs->os_specific.msi_ctx[i],
283					&softs->os_specific.intrcookie[i]);
284		if (error) {
285			DBG_ERR("Failed to setup interrupt for events err=%d\n",
286				error);
287			return error;
288		}
289		softs->os_specific.intr_registered[i] = TRUE;
290		/* Add interrupt handlers*/
291		for (i = 1; i < msix_count; ++i) {
292			softs->os_specific.pqi_irq_rid[i] = i+1;
293			softs->os_specific.pqi_irq[i] = \
294					bus_alloc_resource_any(dev,
295					SYS_RES_IRQ,
296					&softs->os_specific.pqi_irq_rid[i],
297					RF_SHAREABLE | RF_ACTIVE);
298			if (NULL == softs->os_specific.pqi_irq[i]) {
299				DBG_ERR("Failed to allocate \
300					msi/x interrupt resource\n");
301				return ENXIO;
302			}
303			softs->os_specific.msi_ctx[i].pqi_dev = dev;
304			softs->os_specific.msi_ctx[i].oq_id = i;
305			error = bus_setup_intr(dev,
306					softs->os_specific.pqi_irq[i],
307					INTR_TYPE_CAM | INTR_MPSAFE,\
308					NULL,
309					common_ithread_routine,
310					&softs->os_specific.msi_ctx[i],
311					&softs->os_specific.intrcookie[i]);
312			if (error) {
313				DBG_ERR("Failed to setup \
314					msi/x interrupt error = %d\n", error);
315				return error;
316			}
317			softs->os_specific.intr_registered[i] = TRUE;
318		}
319	}
320
321	DBG_FUNC("OUT error = %d\n", error);
322
323	return error;
324}
325
326/*
327 * Setup interrupt depending on the configuration
328 */
329int
330os_setup_intr(pqisrc_softstate_t *softs)
331{
332	int bsd_status, pqi_status;
333
334	DBG_FUNC("IN\n");
335
336	if (softs->intr_type == INTR_TYPE_FIXED) {
337		bsd_status = register_legacy_intr(softs);
338	}
339	else {
340		bsd_status = register_msix_intr(softs);
341	}
342
343	if (bsd_status)
344		DBG_WARN("interrupt registration is failed, error = %d\n", bsd_status);
345
346	pqi_status = bsd_status_to_pqi_status(bsd_status);
347
348	DBG_FUNC("OUT\n");
349
350	return pqi_status;
351}
352
353/*
354 * Deregistration of legacy interrupt
355 */
356void
357deregister_pqi_intx(pqisrc_softstate_t *softs)
358{
359	device_t dev = softs->os_specific.pqi_dev;
360
361	DBG_FUNC("IN\n");
362
363	if (softs->os_specific.pqi_irq[0] != NULL) {
364		if (softs->os_specific.intr_registered[0]) {
365			bus_teardown_intr(dev, softs->os_specific.pqi_irq[0],
366					softs->os_specific.intrcookie[0]);
367			softs->os_specific.intr_registered[0] = FALSE;
368		}
369		bus_release_resource(dev, SYS_RES_IRQ,
370			softs->os_specific.pqi_irq_rid[0],
371			softs->os_specific.pqi_irq[0]);
372		softs->os_specific.pqi_irq[0] = NULL;
373		os_mem_free(softs, (char*)softs->os_specific.msi_ctx, sizeof(pqi_intr_ctx_t));
374	}
375
376	DBG_FUNC("OUT\n");
377}
378
379/*
380 * Deregistration of MSIx interrupt
381 */
382void
383deregister_pqi_msix(pqisrc_softstate_t *softs)
384{
385	device_t dev = softs->os_specific.pqi_dev;
386	int msix_count = softs->intr_count;
387	int i = 0;
388
389	DBG_FUNC("IN\n");
390
391	os_mem_free(softs, (char*)softs->os_specific.msi_ctx, sizeof(pqi_intr_ctx_t) * msix_count);
392	softs->os_specific.msi_ctx = NULL;
393
394	for (; i < msix_count; ++i) {
395		if (softs->os_specific.pqi_irq[i] != NULL) {
396			if (softs->os_specific.intr_registered[i]) {
397				bus_teardown_intr(dev,
398					softs->os_specific.pqi_irq[i],
399					softs->os_specific.intrcookie[i]);
400				softs->os_specific.intr_registered[i] = FALSE;
401			}
402			bus_release_resource(dev, SYS_RES_IRQ,
403				softs->os_specific.pqi_irq_rid[i],
404			softs->os_specific.pqi_irq[i]);
405			softs->os_specific.pqi_irq[i] = NULL;
406		}
407	}
408
409	DBG_FUNC("OUT\n");
410}
411
412/*
413 * Function to destroy interrupts registered
414 */
415int
416os_destroy_intr(pqisrc_softstate_t *softs)
417{
418	device_t dev = softs->os_specific.pqi_dev;
419
420	DBG_FUNC("IN\n");
421
422	if (softs->intr_type == INTR_TYPE_FIXED) {
423		deregister_pqi_intx(softs);
424	} else if (softs->intr_type == INTR_TYPE_MSIX) {
425		deregister_pqi_msix(softs);
426	}
427	if (softs->os_specific.msi_enabled) {
428		pci_release_msi(dev);
429		softs->os_specific.msi_enabled = FALSE;
430	}
431
432	DBG_FUNC("OUT\n");
433
434	return PQI_STATUS_SUCCESS;
435}
436
437/*
438 * Free interrupt related resources for the adapter
439 */
440void
441os_free_intr_config(pqisrc_softstate_t *softs)
442{
443	device_t dev = softs->os_specific.pqi_dev;
444
445	DBG_FUNC("IN\n");
446
447        if (softs->os_specific.msi_enabled) {
448                pci_release_msi(dev);
449                softs->os_specific.msi_enabled = FALSE;
450        }
451
452	DBG_FUNC("OUT\n");
453}
454