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