1 // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
3 * Copyright(c) 2018 Intel Corporation.
5 * This file is provided under a dual BSD/GPLv2 license. When using or
6 * redistributing this file, you may do so under either license.
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of version 2 of the GNU General Public License as
12 * published by the Free Software Foundation.
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
21 * Redistribution and use in source and binary forms, with or without
22 * modification, are permitted provided that the following conditions
25 * - Redistributions of source code must retain the above copyright
26 * notice, this list of conditions and the following disclaimer.
27 * - Redistributions in binary form must reproduce the above copyright
28 * notice, this list of conditions and the following disclaimer in
29 * the documentation and/or other materials provided with the
31 * - Neither the name of Intel Corporation nor the names of its
32 * contributors may be used to endorse or promote products derived
33 * from this software without specific prior written permission.
35 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
54 * msix_initialize() - Calculate, request and configure MSIx IRQs
55 * @dd: valid hfi1 devdata
58 int msix_initialize(struct hfi1_devdata
*dd
)
62 struct hfi1_msix_entry
*entries
;
65 * MSIx interrupt count:
66 * one for the general, "slow path" interrupt
67 * one per used SDMA engine
68 * one per kernel receive context
69 * one for each VNIC context
70 * ...any new IRQs should be added here.
72 total
= 1 + dd
->num_sdma
+ dd
->n_krcv_queues
+ dd
->num_vnic_contexts
;
74 if (total
>= CCE_NUM_MSIX_VECTORS
)
77 ret
= pci_alloc_irq_vectors(dd
->pcidev
, total
, total
, PCI_IRQ_MSIX
);
79 dd_dev_err(dd
, "pci_alloc_irq_vectors() failed: %d\n", ret
);
83 entries
= kcalloc(total
, sizeof(*dd
->msix_info
.msix_entries
),
86 pci_free_irq_vectors(dd
->pcidev
);
90 dd
->msix_info
.msix_entries
= entries
;
91 spin_lock_init(&dd
->msix_info
.msix_lock
);
92 bitmap_zero(dd
->msix_info
.in_use_msix
, total
);
93 dd
->msix_info
.max_requested
= total
;
94 dd_dev_info(dd
, "%u MSI-X interrupts allocated\n", total
);
100 * msix_request_irq() - Allocate a free MSIx IRQ
102 * @arg: context information for the IRQ
103 * @handler: IRQ handler
104 * @thread: IRQ thread handler (could be NULL)
105 * @idx: zero base idx if multiple devices are needed
106 * @type: affinty IRQ type
108 * Allocated an MSIx vector if available, and then create the appropriate
109 * meta data needed to keep track of the pci IRQ request.
116 static int msix_request_irq(struct hfi1_devdata
*dd
, void *arg
,
117 irq_handler_t handler
, irq_handler_t thread
,
118 u32 idx
, enum irq_type type
)
123 const char *err_info
;
124 char name
[MAX_NAME_SIZE
];
125 struct hfi1_msix_entry
*me
;
127 /* Allocate an MSIx vector */
128 spin_lock(&dd
->msix_info
.msix_lock
);
129 nr
= find_first_zero_bit(dd
->msix_info
.in_use_msix
,
130 dd
->msix_info
.max_requested
);
131 if (nr
< dd
->msix_info
.max_requested
)
132 __set_bit(nr
, dd
->msix_info
.in_use_msix
);
133 spin_unlock(&dd
->msix_info
.msix_lock
);
135 if (nr
== dd
->msix_info
.max_requested
)
138 /* Specific verification and determine the name */
141 /* general interrupt must be MSIx vector 0 */
143 spin_lock(&dd
->msix_info
.msix_lock
);
144 __clear_bit(nr
, dd
->msix_info
.in_use_msix
);
145 spin_unlock(&dd
->msix_info
.msix_lock
);
146 dd_dev_err(dd
, "Invalid index %lu for GENERAL IRQ\n",
150 snprintf(name
, sizeof(name
), DRIVER_NAME
"_%d", dd
->unit
);
151 err_info
= "general";
154 snprintf(name
, sizeof(name
), DRIVER_NAME
"_%d sdma%d",
159 snprintf(name
, sizeof(name
), DRIVER_NAME
"_%d kctxt%d",
161 err_info
= "receive context";
167 name
[sizeof(name
) - 1] = 0;
169 irq
= pci_irq_vector(dd
->pcidev
, nr
);
170 ret
= pci_request_irq(dd
->pcidev
, nr
, handler
, thread
, arg
, name
);
173 "%s: request for IRQ %d failed, MSIx %d, err %d\n",
174 err_info
, irq
, idx
, ret
);
175 spin_lock(&dd
->msix_info
.msix_lock
);
176 __clear_bit(nr
, dd
->msix_info
.in_use_msix
);
177 spin_unlock(&dd
->msix_info
.msix_lock
);
182 * assign arg after pci_request_irq call, so it will be
185 me
= &dd
->msix_info
.msix_entries
[nr
];
190 /* This is a request, so a failure is not fatal */
191 ret
= hfi1_get_irq_affinity(dd
, me
);
193 dd_dev_err(dd
, "unable to pin IRQ %d\n", ret
);
199 * msix_request_rcd_irq() - Helper function for RCVAVAIL IRQs
200 * @rcd: valid rcd context
203 int msix_request_rcd_irq(struct hfi1_ctxtdata
*rcd
)
207 nr
= msix_request_irq(rcd
->dd
, rcd
, receive_context_interrupt
,
208 receive_context_thread
, rcd
->ctxt
, IRQ_RCVCTXT
);
213 * Set the interrupt register and mask for this
214 * context's interrupt.
216 rcd
->ireg
= (IS_RCVAVAIL_START
+ rcd
->ctxt
) / 64;
217 rcd
->imask
= ((u64
)1) << ((IS_RCVAVAIL_START
+ rcd
->ctxt
) % 64);
219 remap_intr(rcd
->dd
, IS_RCVAVAIL_START
+ rcd
->ctxt
, nr
);
225 * msix_request_smda_ira() - Helper for getting SDMA IRQ resources
226 * @sde: valid sdma engine
229 int msix_request_sdma_irq(struct sdma_engine
*sde
)
233 nr
= msix_request_irq(sde
->dd
, sde
, sdma_interrupt
, NULL
,
234 sde
->this_idx
, IRQ_SDMA
);
238 remap_sdma_interrupts(sde
->dd
, sde
->this_idx
, nr
);
244 * enable_sdma_src() - Helper to enable SDMA IRQ srcs
245 * @dd: valid devdata structure
246 * @i: index of SDMA engine
248 static void enable_sdma_srcs(struct hfi1_devdata
*dd
, int i
)
250 set_intr_bits(dd
, IS_SDMA_START
+ i
, IS_SDMA_START
+ i
, true);
251 set_intr_bits(dd
, IS_SDMA_PROGRESS_START
+ i
,
252 IS_SDMA_PROGRESS_START
+ i
, true);
253 set_intr_bits(dd
, IS_SDMA_IDLE_START
+ i
, IS_SDMA_IDLE_START
+ i
, true);
254 set_intr_bits(dd
, IS_SDMAENG_ERR_START
+ i
, IS_SDMAENG_ERR_START
+ i
,
259 * msix_request_irqs() - Allocate all MSIx IRQs
260 * @dd: valid devdata structure
262 * Helper function to request the used MSIx IRQs.
265 int msix_request_irqs(struct hfi1_devdata
*dd
)
270 ret
= msix_request_irq(dd
, dd
, general_interrupt
, NULL
, 0, IRQ_GENERAL
);
274 for (i
= 0; i
< dd
->num_sdma
; i
++) {
275 struct sdma_engine
*sde
= &dd
->per_sdma
[i
];
277 ret
= msix_request_sdma_irq(sde
);
280 enable_sdma_srcs(sde
->dd
, i
);
283 for (i
= 0; i
< dd
->n_krcv_queues
; i
++) {
284 struct hfi1_ctxtdata
*rcd
= hfi1_rcd_get_by_index_safe(dd
, i
);
287 ret
= msix_request_rcd_irq(rcd
);
297 * msix_free_irq() - Free the specified MSIx resources and IRQ
299 * @msix_intr: MSIx vector to free.
302 void msix_free_irq(struct hfi1_devdata
*dd
, u8 msix_intr
)
304 struct hfi1_msix_entry
*me
;
306 if (msix_intr
>= dd
->msix_info
.max_requested
)
309 me
= &dd
->msix_info
.msix_entries
[msix_intr
];
311 if (!me
->arg
) /* => no irq, no affinity */
314 hfi1_put_irq_affinity(dd
, me
);
315 pci_free_irq(dd
->pcidev
, msix_intr
, me
->arg
);
319 spin_lock(&dd
->msix_info
.msix_lock
);
320 __clear_bit(msix_intr
, dd
->msix_info
.in_use_msix
);
321 spin_unlock(&dd
->msix_info
.msix_lock
);
325 * hfi1_clean_up_msix_interrupts() - Free all MSIx IRQ resources
326 * @dd: valid device data data structure
328 * Free the MSIx and associated PCI resources, if they have been allocated.
330 void msix_clean_up_interrupts(struct hfi1_devdata
*dd
)
333 struct hfi1_msix_entry
*me
= dd
->msix_info
.msix_entries
;
335 /* remove irqs - must happen before disabling/turning off */
336 for (i
= 0; i
< dd
->msix_info
.max_requested
; i
++, me
++)
337 msix_free_irq(dd
, i
);
339 /* clean structures */
340 kfree(dd
->msix_info
.msix_entries
);
341 dd
->msix_info
.msix_entries
= NULL
;
342 dd
->msix_info
.max_requested
= 0;
344 pci_free_irq_vectors(dd
->pcidev
);
348 * msix_vnic_syncrhonize_irq() - Vnic IRQ synchronize
351 void msix_vnic_synchronize_irq(struct hfi1_devdata
*dd
)
355 for (i
= 0; i
< dd
->vnic
.num_ctxt
; i
++) {
356 struct hfi1_ctxtdata
*rcd
= dd
->vnic
.ctxt
[i
];
357 struct hfi1_msix_entry
*me
;
359 me
= &dd
->msix_info
.msix_entries
[rcd
->msix_intr
];
361 synchronize_irq(me
->irq
);