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