ca1a1de267663e484662cae460fc715218a80b75
[platform/kernel/linux-starfive.git] / drivers / net / ethernet / intel / ice / ice_irq.c
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (C) 2023, Intel Corporation. */
3
4 #include "ice.h"
5 #include "ice_lib.h"
6 #include "ice_irq.h"
7
8 /**
9  * ice_reduce_msix_usage - Reduce usage of MSI-X vectors
10  * @pf: board private structure
11  * @v_remain: number of remaining MSI-X vectors to be distributed
12  *
13  * Reduce the usage of MSI-X vectors when entire request cannot be fulfilled.
14  * pf->num_lan_msix and pf->num_rdma_msix values are set based on number of
15  * remaining vectors.
16  */
17 static void ice_reduce_msix_usage(struct ice_pf *pf, int v_remain)
18 {
19         int v_rdma;
20
21         if (!ice_is_rdma_ena(pf)) {
22                 pf->num_lan_msix = v_remain;
23                 return;
24         }
25
26         /* RDMA needs at least 1 interrupt in addition to AEQ MSIX */
27         v_rdma = ICE_RDMA_NUM_AEQ_MSIX + 1;
28
29         if (v_remain < ICE_MIN_LAN_TXRX_MSIX + ICE_MIN_RDMA_MSIX) {
30                 dev_warn(ice_pf_to_dev(pf), "Not enough MSI-X vectors to support RDMA.\n");
31                 clear_bit(ICE_FLAG_RDMA_ENA, pf->flags);
32
33                 pf->num_rdma_msix = 0;
34                 pf->num_lan_msix = ICE_MIN_LAN_TXRX_MSIX;
35         } else if ((v_remain < ICE_MIN_LAN_TXRX_MSIX + v_rdma) ||
36                    (v_remain - v_rdma < v_rdma)) {
37                 /* Support minimum RDMA and give remaining vectors to LAN MSIX
38                  */
39                 pf->num_rdma_msix = ICE_MIN_RDMA_MSIX;
40                 pf->num_lan_msix = v_remain - ICE_MIN_RDMA_MSIX;
41         } else {
42                 /* Split remaining MSIX with RDMA after accounting for AEQ MSIX
43                  */
44                 pf->num_rdma_msix = (v_remain - ICE_RDMA_NUM_AEQ_MSIX) / 2 +
45                                     ICE_RDMA_NUM_AEQ_MSIX;
46                 pf->num_lan_msix = v_remain - pf->num_rdma_msix;
47         }
48 }
49
50 /**
51  * ice_ena_msix_range - Request a range of MSIX vectors from the OS
52  * @pf: board private structure
53  *
54  * Compute the number of MSIX vectors wanted and request from the OS. Adjust
55  * device usage if there are not enough vectors. Return the number of vectors
56  * reserved or negative on failure.
57  */
58 static int ice_ena_msix_range(struct ice_pf *pf)
59 {
60         int num_cpus, hw_num_msix, v_other, v_wanted, v_actual;
61         struct device *dev = ice_pf_to_dev(pf);
62         int err;
63
64         hw_num_msix = pf->hw.func_caps.common_cap.num_msix_vectors;
65         num_cpus = num_online_cpus();
66
67         /* LAN miscellaneous handler */
68         v_other = ICE_MIN_LAN_OICR_MSIX;
69
70         /* Flow Director */
71         if (test_bit(ICE_FLAG_FD_ENA, pf->flags))
72                 v_other += ICE_FDIR_MSIX;
73
74         /* switchdev */
75         v_other += ICE_ESWITCH_MSIX;
76
77         v_wanted = v_other;
78
79         /* LAN traffic */
80         pf->num_lan_msix = num_cpus;
81         v_wanted += pf->num_lan_msix;
82
83         /* RDMA auxiliary driver */
84         if (ice_is_rdma_ena(pf)) {
85                 pf->num_rdma_msix = num_cpus + ICE_RDMA_NUM_AEQ_MSIX;
86                 v_wanted += pf->num_rdma_msix;
87         }
88
89         if (v_wanted > hw_num_msix) {
90                 int v_remain;
91
92                 dev_warn(dev, "not enough device MSI-X vectors. wanted = %d, available = %d\n",
93                          v_wanted, hw_num_msix);
94
95                 if (hw_num_msix < ICE_MIN_MSIX) {
96                         err = -ERANGE;
97                         goto exit_err;
98                 }
99
100                 v_remain = hw_num_msix - v_other;
101                 if (v_remain < ICE_MIN_LAN_TXRX_MSIX) {
102                         v_other = ICE_MIN_MSIX - ICE_MIN_LAN_TXRX_MSIX;
103                         v_remain = ICE_MIN_LAN_TXRX_MSIX;
104                 }
105
106                 ice_reduce_msix_usage(pf, v_remain);
107                 v_wanted = pf->num_lan_msix + pf->num_rdma_msix + v_other;
108
109                 dev_notice(dev, "Reducing request to %d MSI-X vectors for LAN traffic.\n",
110                            pf->num_lan_msix);
111                 if (ice_is_rdma_ena(pf))
112                         dev_notice(dev, "Reducing request to %d MSI-X vectors for RDMA.\n",
113                                    pf->num_rdma_msix);
114         }
115
116         /* actually reserve the vectors */
117         v_actual = pci_alloc_irq_vectors(pf->pdev, ICE_MIN_MSIX, v_wanted,
118                                          PCI_IRQ_MSIX);
119         if (v_actual < 0) {
120                 dev_err(dev, "unable to reserve MSI-X vectors\n");
121                 err = v_actual;
122                 goto exit_err;
123         }
124
125         if (v_actual < v_wanted) {
126                 dev_warn(dev, "not enough OS MSI-X vectors. requested = %d, obtained = %d\n",
127                          v_wanted, v_actual);
128
129                 if (v_actual < ICE_MIN_MSIX) {
130                         /* error if we can't get minimum vectors */
131                         pci_free_irq_vectors(pf->pdev);
132                         err = -ERANGE;
133                         goto exit_err;
134                 } else {
135                         int v_remain = v_actual - v_other;
136
137                         if (v_remain < ICE_MIN_LAN_TXRX_MSIX)
138                                 v_remain = ICE_MIN_LAN_TXRX_MSIX;
139
140                         ice_reduce_msix_usage(pf, v_remain);
141
142                         dev_notice(dev, "Enabled %d MSI-X vectors for LAN traffic.\n",
143                                    pf->num_lan_msix);
144
145                         if (ice_is_rdma_ena(pf))
146                                 dev_notice(dev, "Enabled %d MSI-X vectors for RDMA.\n",
147                                            pf->num_rdma_msix);
148                 }
149         }
150
151         return v_actual;
152
153 exit_err:
154         pf->num_rdma_msix = 0;
155         pf->num_lan_msix = 0;
156         return err;
157 }
158
159 /**
160  * ice_clear_interrupt_scheme - Undo things done by ice_init_interrupt_scheme
161  * @pf: board private structure
162  */
163 void ice_clear_interrupt_scheme(struct ice_pf *pf)
164 {
165         pci_free_irq_vectors(pf->pdev);
166
167         if (pf->irq_tracker) {
168                 devm_kfree(ice_pf_to_dev(pf), pf->irq_tracker);
169                 pf->irq_tracker = NULL;
170         }
171 }
172
173 /**
174  * ice_init_interrupt_scheme - Determine proper interrupt scheme
175  * @pf: board private structure to initialize
176  */
177 int ice_init_interrupt_scheme(struct ice_pf *pf)
178 {
179         int vectors;
180
181         vectors = ice_ena_msix_range(pf);
182
183         if (vectors < 0)
184                 return vectors;
185
186         /* set up vector assignment tracking */
187         pf->irq_tracker = devm_kzalloc(ice_pf_to_dev(pf),
188                                        struct_size(pf->irq_tracker, list,
189                                                    vectors),
190                                        GFP_KERNEL);
191         if (!pf->irq_tracker) {
192                 pci_free_irq_vectors(pf->pdev);
193                 return -ENOMEM;
194         }
195
196         /* populate SW interrupts pool with number of OS granted IRQs. */
197         pf->irq_tracker->num_entries = (u16)vectors;
198         pf->irq_tracker->end = pf->irq_tracker->num_entries;
199
200         return 0;
201 }
202
203 /**
204  * ice_alloc_irq - Allocate new interrupt vector
205  * @pf: board private structure
206  *
207  * Allocate new interrupt vector for a given owner id.
208  * return struct msi_map with interrupt details and track
209  * allocated interrupt appropriately.
210  *
211  * This function mimics individual interrupt allocation,
212  * even interrupts are actually already allocated with
213  * pci_alloc_irq_vectors. Individual allocation helps
214  * to track interrupts and simplifies interrupt related
215  * handling.
216  *
217  * On failure, return map with negative .index. The caller
218  * is expected to check returned map index.
219  *
220  */
221 struct msi_map ice_alloc_irq(struct ice_pf *pf)
222 {
223         struct msi_map map = { .index = -ENOENT };
224         int entry;
225
226         entry = ice_get_res(pf, pf->irq_tracker);
227         if (entry < 0)
228                 return map;
229
230         map.index = entry;
231         map.virq = pci_irq_vector(pf->pdev, map.index);
232
233         return map;
234 }
235
236 /**
237  * ice_free_irq - Free interrupt vector
238  * @pf: board private structure
239  * @map: map with interrupt details
240  *
241  * Remove allocated interrupt from the interrupt tracker
242  */
243 void ice_free_irq(struct ice_pf *pf, struct msi_map map)
244 {
245         ice_free_res(pf->irq_tracker, map.index);
246 }