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