]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/smartpqi/smartpqi_intr.c
Upgrade to OpenSSH 7.9p1.
[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 /*
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 }