]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/smartpqi/smartpqi_intr.c
More cleanup in response queue and reset code.
[FreeBSD/FreeBSD.git] / sys / dev / smartpqi / smartpqi_intr.c
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 }