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