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 */
os_get_processor_config(pqisrc_softstate_t * softs)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 */
os_get_intr_config(pqisrc_softstate_t * softs)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
os_eventtaskqueue_enqueue(pqisrc_softstate_t * sc)103 void os_eventtaskqueue_enqueue(pqisrc_softstate_t *sc)
104 {
105 taskqueue_enqueue(taskqueue_swi, &sc->os_specific.event_task);
106 }
107
pqisrc_event_worker(void * arg1,int arg2)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 */
shared_ithread_routine(void * arg)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 */
common_ithread_routine(void * arg)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
event_ithread_routine(void * arg)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 */
register_legacy_intr(pqisrc_softstate_t * softs)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 */
register_msix_intr(pqisrc_softstate_t * softs)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 */
os_setup_intr(pqisrc_softstate_t * softs)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 */
deregister_pqi_intx(pqisrc_softstate_t * softs)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 */
deregister_pqi_msix(pqisrc_softstate_t * softs)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 */
os_destroy_intr(pqisrc_softstate_t * softs)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 */
os_free_intr_config(pqisrc_softstate_t * softs)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