xref: /freebsd/sys/dev/smartpqi/smartpqi_intr.c (revision a64729f5077d77e13b9497cb33ecb3c82e606ee8)
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  */
33 int
34 os_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  */
47 int
48 os_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 
98 void
99 os_eventtaskqueue_enqueue(pqisrc_softstate_t *sc)
100 {
101 	taskqueue_enqueue(taskqueue_swi, &sc->os_specific.event_task);
102 }
103 
104 void
105 pqisrc_event_worker(void *arg1, int arg2)
106 {
107 	pqisrc_ack_all_events(arg1);
108 }
109 
110 /*
111  * ithread routine to handle uniprocessor systems
112  */
113 static void
114 shared_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  */
134 static void
135 common_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 
151 static void
152 event_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  */
171 int
172 register_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  */
216 int
217 register_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  */
329 int
330 os_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  */
356 void
357 deregister_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  */
382 void
383 deregister_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  */
415 int
416 os_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  */
440 void
441 os_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