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