xref: /src/sys/dev/smartpqi/smartpqi_intr.c (revision c558eca47970c8467ed0494145f08d1879050bbd)
1 /*-
2  * Copyright 2016-2025 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
os_get_processor_config(pqisrc_softstate_t * softs)34 os_get_processor_config(pqisrc_softstate_t *softs)
35 {
36 	DBG_FUNC("IN\n");
37 	softs->num_cpus_online = mp_ncpus;
38 	DBG_FUNC("OUT\n");
39 
40 	return PQI_STATUS_SUCCESS;
41 }
42 
43 /*
44  * Function to get interrupt count and type supported
45  */
46 int
os_get_intr_config(pqisrc_softstate_t * softs)47 os_get_intr_config(pqisrc_softstate_t *softs)
48 {
49 	device_t dev = softs->os_specific.pqi_dev;
50 	int msi_count = pci_msix_count(dev);
51 	int error = BSD_SUCCESS;
52 
53 	DBG_FUNC("IN\n");
54 
55 	if (msi_count > softs->num_cpus_online)
56 		msi_count = softs->num_cpus_online;
57 	if (msi_count > PQI_MAX_MSIX)
58 		msi_count = PQI_MAX_MSIX;
59 	if (msi_count == 0 || (error = pci_alloc_msix(dev, &msi_count)) != 0) {
60 		device_printf(dev, "alloc msix failed - msi_count=%d, err=%d; "
61                                    "will try MSI\n", msi_count, error);
62 		pci_release_msi(dev);
63 	} else {
64 		softs->intr_count = msi_count;
65 		softs->intr_type = INTR_TYPE_MSIX;
66 		softs->os_specific.msi_enabled = TRUE;
67 		device_printf(dev, "using MSI-X interrupts (%d vectors)\n",
68 			msi_count);
69 	}
70 	if (!softs->intr_type) {
71 		msi_count = 1;
72 		if ((error = pci_alloc_msi(dev, &msi_count)) != 0) {
73 			device_printf(dev, "alloc msi failed - err=%d; "
74 				"will use INTx\n", error);
75 			pci_release_msi(dev);
76 		} else {
77 			softs->os_specific.msi_enabled = TRUE;
78 			softs->intr_count = msi_count;
79 			softs->intr_type = INTR_TYPE_MSI;
80 			device_printf(dev, "using MSI interrupts\n");
81 		}
82 	}
83 
84 	if (!softs->intr_type) {
85 		device_printf(dev, "using legacy interrupts\n");
86 		softs->intr_type = INTR_TYPE_FIXED;
87 		softs->intr_count = 1;
88 	}
89 
90 	error = bsd_status_to_pqi_status(BSD_SUCCESS);
91 
92 	DBG_FUNC("OUT\n");
93 
94 	return error;
95 }
96 
97 void
os_eventtaskqueue_enqueue(pqisrc_softstate_t * sc)98 os_eventtaskqueue_enqueue(pqisrc_softstate_t *sc)
99 {
100 	taskqueue_enqueue(taskqueue_swi, &sc->os_specific.event_task);
101 }
102 
103 void
pqisrc_event_worker(void * arg1,int arg2)104 pqisrc_event_worker(void *arg1, int arg2)
105 {
106 	pqisrc_ack_all_events(arg1);
107 }
108 
109 /*
110  * ithread routine to handle uniprocessor systems
111  */
112 static void
shared_ithread_routine(void * arg)113 shared_ithread_routine(void *arg)
114 {
115 	pqi_intr_ctx_t *intr_ctx = (pqi_intr_ctx_t *)arg;
116 	pqisrc_softstate_t *softs = device_get_softc(intr_ctx->pqi_dev);
117 	int oq_id  = intr_ctx->oq_id;
118 
119 	DBG_FUNC("IN\n");
120 
121 	if (!softs)
122 		return;
123 
124 	pqisrc_process_response_queue(softs, oq_id);
125 	pqisrc_process_event_intr_src(softs, oq_id - 1);
126 
127 	DBG_FUNC("OUT\n");
128 }
129 
130 /*
131  * ithread routine to process non event response
132  */
133 static void
common_ithread_routine(void * arg)134 common_ithread_routine(void *arg)
135 {
136 	pqi_intr_ctx_t *intr_ctx = (pqi_intr_ctx_t *)arg;
137 	pqisrc_softstate_t *softs = device_get_softc(intr_ctx->pqi_dev);
138 	int oq_id  = intr_ctx->oq_id;
139 
140 	DBG_FUNC("IN\n");
141 
142 	if (!softs)
143 		return;
144 
145 	pqisrc_process_response_queue(softs, oq_id);
146 
147 	DBG_FUNC("OUT\n");
148 }
149 
150 static void
event_ithread_routine(void * arg)151 event_ithread_routine(void *arg)
152 {
153 	pqi_intr_ctx_t *intr_ctx = (pqi_intr_ctx_t *)arg;
154 	pqisrc_softstate_t *softs = device_get_softc(intr_ctx->pqi_dev);
155 	int oq_id  = intr_ctx->oq_id;
156 
157 	DBG_FUNC("IN\n");
158 
159 	if (!softs)
160 		return;
161 
162 	pqisrc_process_event_intr_src(softs, oq_id);
163 
164 	DBG_FUNC("OUT\n");
165 }
166 
167 /*
168  * Registration of legacy interrupt in case MSI is unsupported
169  */
170 int
register_legacy_intr(pqisrc_softstate_t * softs)171 register_legacy_intr(pqisrc_softstate_t *softs)
172 {
173 	int error = BSD_SUCCESS;
174 	device_t dev;
175 
176 	DBG_FUNC("IN\n");
177 
178 	dev = softs->os_specific.pqi_dev;
179 
180 	softs->os_specific.pqi_irq_rid[0] = 0;
181 	softs->os_specific.pqi_irq[0] = bus_alloc_resource_any(dev, \
182 		SYS_RES_IRQ, &softs->os_specific.pqi_irq_rid[0],
183 		RF_ACTIVE | RF_SHAREABLE);
184 	if (NULL == softs->os_specific.pqi_irq[0]) {
185 		DBG_ERR("Failed to allocate resource for interrupt\n");
186 		return ENXIO;
187 	}
188 	if ((softs->os_specific.msi_ctx = os_mem_alloc(softs,sizeof(pqi_intr_ctx_t))) == NULL) {
189 		DBG_ERR("Failed to allocate memory for msi_ctx\n");
190 		return ENXIO;
191 	}
192 	softs->os_specific.msi_ctx[0].pqi_dev = dev;
193 	/* For Legacy support oq_id should be one */
194 	softs->os_specific.msi_ctx[0].oq_id = 1;
195 
196 	error = bus_setup_intr(dev, softs->os_specific.pqi_irq[0],
197 				INTR_TYPE_CAM | INTR_MPSAFE, \
198 				NULL, shared_ithread_routine,
199 				&softs->os_specific.msi_ctx[0],
200 				&softs->os_specific.intrcookie[0]);
201 	if (error) {
202 		DBG_ERR("Failed to setup legacy interrupt err = %d\n", error);
203 		return error;
204 	}
205 	softs->os_specific.intr_registered[0] = TRUE;
206 
207 	DBG_FUNC("OUT error = %d\n", error);
208 
209 	return error;
210 }
211 
212 /*
213  * Registration of MSIx
214  */
215 int
register_msix_intr(pqisrc_softstate_t * softs)216 register_msix_intr(pqisrc_softstate_t *softs)
217 {
218 	int error = BSD_SUCCESS;
219 	int i = 0;
220 	device_t dev = softs->os_specific.pqi_dev;
221 	int msix_count = softs->intr_count;
222 	size_t msix_size =  sizeof(pqi_intr_ctx_t) * msix_count;
223 
224 	DBG_FUNC("IN\n");
225 
226 	softs->os_specific.msi_ctx = os_mem_alloc(softs, msix_size);
227 	if (!softs->os_specific.msi_ctx) {
228 		DBG_ERR("Memory allocation failed, Requested memory:%lu bytes\n", (unsigned long)msix_size);
229 		return ENXIO;
230 	}
231 
232 	/*Add shared handler */
233 	if (softs->share_opq_and_eventq) {
234 		softs->os_specific.pqi_irq_rid[i] = i+1;
235 		softs->os_specific.pqi_irq[i] = bus_alloc_resource_any(dev, \
236 						SYS_RES_IRQ,
237 						&softs->os_specific.pqi_irq_rid[i],
238 						RF_SHAREABLE |  RF_ACTIVE);
239 		if (NULL == softs->os_specific.pqi_irq[i]) {
240 			DBG_ERR("Failed to allocate \
241 				event interrupt resource\n");
242 			return ENXIO;
243 		}
244 
245 		softs->os_specific.msi_ctx[i].pqi_dev = dev;
246 		softs->os_specific.msi_ctx[i].oq_id = i+1;
247 
248 		error = bus_setup_intr(dev,softs->os_specific.pqi_irq[i],
249 					INTR_TYPE_CAM | INTR_MPSAFE,\
250 					NULL,
251 					shared_ithread_routine,
252 					&softs->os_specific.msi_ctx[i],
253 					&softs->os_specific.intrcookie[i]);
254 
255 		if (error) {
256 			DBG_ERR("Failed to setup interrupt for events r=%d\n",
257 				error);
258 			return error;
259 		}
260 		softs->os_specific.intr_registered[i] = TRUE;
261 	}
262 	else {
263 		/* Add event handler */
264 		softs->os_specific.pqi_irq_rid[i] = i+1;
265 		softs->os_specific.pqi_irq[i] = bus_alloc_resource_any(dev, \
266 						SYS_RES_IRQ,
267 						&softs->os_specific.pqi_irq_rid[i],
268 						RF_SHAREABLE |  RF_ACTIVE);
269 		if (NULL == softs->os_specific.pqi_irq[i]) {
270 			DBG_ERR("Failed to allocate event interrupt resource\n");
271 			return ENXIO;
272 		}
273 
274 		softs->os_specific.msi_ctx[i].pqi_dev = dev;
275 		softs->os_specific.msi_ctx[i].oq_id = i;
276 
277 		error = bus_setup_intr(dev,softs->os_specific.pqi_irq[i],
278 					INTR_TYPE_CAM | INTR_MPSAFE,\
279                        			NULL,
280 					event_ithread_routine,
281 					&softs->os_specific.msi_ctx[i],
282 					&softs->os_specific.intrcookie[i]);
283 		if (error) {
284 			DBG_ERR("Failed to setup interrupt for events err=%d\n",
285 				error);
286 			return error;
287 		}
288 		softs->os_specific.intr_registered[i] = TRUE;
289 		/* Add interrupt handlers*/
290 		for (i = 1; i < msix_count; ++i) {
291 			softs->os_specific.pqi_irq_rid[i] = i+1;
292 			softs->os_specific.pqi_irq[i] = \
293 					bus_alloc_resource_any(dev,
294 					SYS_RES_IRQ,
295 					&softs->os_specific.pqi_irq_rid[i],
296 					RF_SHAREABLE | RF_ACTIVE);
297 			if (NULL == softs->os_specific.pqi_irq[i]) {
298 				DBG_ERR("Failed to allocate \
299 					msi/x interrupt resource\n");
300 				return ENXIO;
301 			}
302 			softs->os_specific.msi_ctx[i].pqi_dev = dev;
303 			softs->os_specific.msi_ctx[i].oq_id = i;
304 			error = bus_setup_intr(dev,
305 					softs->os_specific.pqi_irq[i],
306 					INTR_TYPE_CAM | INTR_MPSAFE,\
307 					NULL,
308 					common_ithread_routine,
309 					&softs->os_specific.msi_ctx[i],
310 					&softs->os_specific.intrcookie[i]);
311 			if (error) {
312 				DBG_ERR("Failed to setup \
313 					msi/x interrupt error = %d\n", error);
314 				return error;
315 			}
316 			softs->os_specific.intr_registered[i] = TRUE;
317 		}
318 	}
319 
320 	DBG_FUNC("OUT error = %d\n", error);
321 
322 	return error;
323 }
324 
325 /*
326  * Setup interrupt depending on the configuration
327  */
328 int
os_setup_intr(pqisrc_softstate_t * softs)329 os_setup_intr(pqisrc_softstate_t *softs)
330 {
331 	int bsd_status, pqi_status;
332 
333 	DBG_FUNC("IN\n");
334 
335 	if (softs->intr_type == INTR_TYPE_FIXED) {
336 		bsd_status = register_legacy_intr(softs);
337 	}
338 	else {
339 		bsd_status = register_msix_intr(softs);
340 	}
341 
342 	if (bsd_status)
343 		DBG_WARN("interrupt registration is failed, error = %d\n", bsd_status);
344 
345 	pqi_status = bsd_status_to_pqi_status(bsd_status);
346 
347 	DBG_FUNC("OUT\n");
348 
349 	return pqi_status;
350 }
351 
352 /*
353  * Deregistration of legacy interrupt
354  */
355 void
deregister_pqi_intx(pqisrc_softstate_t * softs)356 deregister_pqi_intx(pqisrc_softstate_t *softs)
357 {
358 	device_t dev = softs->os_specific.pqi_dev;
359 
360 	DBG_FUNC("IN\n");
361 
362 	if (softs->os_specific.pqi_irq[0] != NULL) {
363 		if (softs->os_specific.intr_registered[0]) {
364 			bus_teardown_intr(dev, softs->os_specific.pqi_irq[0],
365 					softs->os_specific.intrcookie[0]);
366 			softs->os_specific.intr_registered[0] = FALSE;
367 		}
368 		bus_release_resource(dev, SYS_RES_IRQ,
369 			softs->os_specific.pqi_irq_rid[0],
370 			softs->os_specific.pqi_irq[0]);
371 		softs->os_specific.pqi_irq[0] = NULL;
372 		os_mem_free(softs, (char*)softs->os_specific.msi_ctx, sizeof(pqi_intr_ctx_t));
373 	}
374 
375 	DBG_FUNC("OUT\n");
376 }
377 
378 /*
379  * Deregistration of MSIx interrupt
380  */
381 void
deregister_pqi_msix(pqisrc_softstate_t * softs)382 deregister_pqi_msix(pqisrc_softstate_t *softs)
383 {
384 	device_t dev = softs->os_specific.pqi_dev;
385 	int msix_count = softs->intr_count;
386 	int i = 0;
387 
388 	DBG_FUNC("IN\n");
389 
390 	os_mem_free(softs, (char*)softs->os_specific.msi_ctx, sizeof(pqi_intr_ctx_t) * msix_count);
391 	softs->os_specific.msi_ctx = NULL;
392 
393 	for (; i < msix_count; ++i) {
394 		if (softs->os_specific.pqi_irq[i] != NULL) {
395 			if (softs->os_specific.intr_registered[i]) {
396 				bus_teardown_intr(dev,
397 					softs->os_specific.pqi_irq[i],
398 					softs->os_specific.intrcookie[i]);
399 				softs->os_specific.intr_registered[i] = FALSE;
400 			}
401 			bus_release_resource(dev, SYS_RES_IRQ,
402 				softs->os_specific.pqi_irq_rid[i],
403 			softs->os_specific.pqi_irq[i]);
404 			softs->os_specific.pqi_irq[i] = NULL;
405 		}
406 	}
407 
408 	DBG_FUNC("OUT\n");
409 }
410 
411 /*
412  * Function to destroy interrupts registered
413  */
414 int
os_destroy_intr(pqisrc_softstate_t * softs)415 os_destroy_intr(pqisrc_softstate_t *softs)
416 {
417 	device_t dev = softs->os_specific.pqi_dev;
418 
419 	DBG_FUNC("IN\n");
420 
421 	if (softs->intr_type == INTR_TYPE_FIXED) {
422 		deregister_pqi_intx(softs);
423 	} else if (softs->intr_type == INTR_TYPE_MSIX) {
424 		deregister_pqi_msix(softs);
425 	}
426 	if (softs->os_specific.msi_enabled) {
427 		pci_release_msi(dev);
428 		softs->os_specific.msi_enabled = FALSE;
429 	}
430 
431 	DBG_FUNC("OUT\n");
432 
433 	return PQI_STATUS_SUCCESS;
434 }
435 
436 /*
437  * Free interrupt related resources for the adapter
438  */
439 void
os_free_intr_config(pqisrc_softstate_t * softs)440 os_free_intr_config(pqisrc_softstate_t *softs)
441 {
442 	device_t dev = softs->os_specific.pqi_dev;
443 
444 	DBG_FUNC("IN\n");
445 
446         if (softs->os_specific.msi_enabled) {
447                 pci_release_msi(dev);
448                 softs->os_specific.msi_enabled = FALSE;
449         }
450 
451 	DBG_FUNC("OUT\n");
452 }
453