1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) Meta Platforms, Inc. and affiliates. */
3
4 #include <linux/pci.h>
5 #include <linux/types.h>
6
7 #include "fbnic.h"
8 #include "fbnic_netdev.h"
9 #include "fbnic_txrx.h"
10
fbnic_fw_msix_intr(int __always_unused irq,void * data)11 static irqreturn_t fbnic_fw_msix_intr(int __always_unused irq, void *data)
12 {
13 struct fbnic_dev *fbd = (struct fbnic_dev *)data;
14
15 fbnic_mbx_poll(fbd);
16
17 fbnic_wr32(fbd, FBNIC_INTR_MASK_CLEAR(0), 1u << FBNIC_FW_MSIX_ENTRY);
18
19 return IRQ_HANDLED;
20 }
21
__fbnic_fw_enable_mbx(struct fbnic_dev * fbd,int vector)22 static int __fbnic_fw_enable_mbx(struct fbnic_dev *fbd, int vector)
23 {
24 int err;
25
26 /* Initialize mailbox and attempt to poll it into ready state */
27 fbnic_mbx_init(fbd);
28 err = fbnic_mbx_poll_tx_ready(fbd);
29 if (err) {
30 dev_warn(fbd->dev, "FW mailbox did not enter ready state\n");
31 return err;
32 }
33
34 /* Enable interrupt and unmask the vector */
35 enable_irq(vector);
36 fbnic_wr32(fbd, FBNIC_INTR_MASK_CLEAR(0), 1u << FBNIC_FW_MSIX_ENTRY);
37
38 return 0;
39 }
40
41 /**
42 * fbnic_fw_request_mbx - Configure and initialize Firmware Mailbox
43 * @fbd: Pointer to device to initialize
44 *
45 * This function will allocate the IRQ and then reinitialize the mailbox
46 * starting communication between the host and firmware.
47 *
48 * Return: non-zero on failure.
49 **/
fbnic_fw_request_mbx(struct fbnic_dev * fbd)50 int fbnic_fw_request_mbx(struct fbnic_dev *fbd)
51 {
52 struct pci_dev *pdev = to_pci_dev(fbd->dev);
53 int vector, err;
54
55 WARN_ON(fbd->fw_msix_vector);
56
57 vector = pci_irq_vector(pdev, FBNIC_FW_MSIX_ENTRY);
58 if (vector < 0)
59 return vector;
60
61 /* Request the IRQ for FW Mailbox vector. */
62 err = request_threaded_irq(vector, NULL, &fbnic_fw_msix_intr,
63 IRQF_ONESHOT | IRQF_NO_AUTOEN,
64 dev_name(fbd->dev), fbd);
65 if (err)
66 return err;
67
68 /* Initialize mailbox and attempt to poll it into ready state */
69 err = __fbnic_fw_enable_mbx(fbd, vector);
70 if (err)
71 free_irq(vector, fbd);
72
73 fbd->fw_msix_vector = vector;
74
75 return err;
76 }
77
78 /**
79 * fbnic_fw_disable_mbx - Temporarily place mailbox in standby state
80 * @fbd: Pointer to device
81 *
82 * Shutdown the mailbox by notifying the firmware to stop sending us logs, mask
83 * and synchronize the IRQ, and then clean up the rings.
84 **/
fbnic_fw_disable_mbx(struct fbnic_dev * fbd)85 static void fbnic_fw_disable_mbx(struct fbnic_dev *fbd)
86 {
87 /* Disable interrupt and synchronize the IRQ */
88 disable_irq(fbd->fw_msix_vector);
89
90 /* Mask the vector */
91 fbnic_wr32(fbd, FBNIC_INTR_MASK_SET(0), 1u << FBNIC_FW_MSIX_ENTRY);
92
93 /* Make sure disabling logs message is sent, must be done here to
94 * avoid risk of completing without a running interrupt.
95 */
96 fbnic_mbx_flush_tx(fbd);
97 fbnic_mbx_clean(fbd);
98 }
99
100 /**
101 * fbnic_fw_free_mbx - Disable mailbox and place it in standby state
102 * @fbd: Pointer to device to disable
103 *
104 * This function will disable the mailbox interrupt, free any messages still
105 * in the mailbox and place it into a disabled state. The firmware is
106 * expected to see the update and assume that the host is in the reset state.
107 **/
fbnic_fw_free_mbx(struct fbnic_dev * fbd)108 void fbnic_fw_free_mbx(struct fbnic_dev *fbd)
109 {
110 /* Vector has already been freed */
111 if (!fbd->fw_msix_vector)
112 return;
113
114 fbnic_fw_disable_mbx(fbd);
115
116 /* Free the vector */
117 free_irq(fbd->fw_msix_vector, fbd);
118 fbd->fw_msix_vector = 0;
119 }
120
fbnic_pcs_msix_intr(int __always_unused irq,void * data)121 static irqreturn_t fbnic_pcs_msix_intr(int __always_unused irq, void *data)
122 {
123 struct fbnic_dev *fbd = data;
124 struct fbnic_net *fbn;
125
126 if (fbd->mac->pcs_get_link_event(fbd) == FBNIC_LINK_EVENT_NONE) {
127 fbnic_wr32(fbd, FBNIC_INTR_MASK_CLEAR(0),
128 1u << FBNIC_PCS_MSIX_ENTRY);
129 return IRQ_HANDLED;
130 }
131
132 fbn = netdev_priv(fbd->netdev);
133
134 phylink_pcs_change(&fbn->phylink_pcs, false);
135
136 return IRQ_HANDLED;
137 }
138
139 /**
140 * fbnic_pcs_request_irq - Configure the PCS to enable it to advertise link
141 * @fbd: Pointer to device to initialize
142 *
143 * This function provides basic bringup for the MAC/PCS IRQ. For now the IRQ
144 * will remain disabled until we start the MAC/PCS/PHY logic via phylink.
145 *
146 * Return: non-zero on failure.
147 **/
fbnic_pcs_request_irq(struct fbnic_dev * fbd)148 int fbnic_pcs_request_irq(struct fbnic_dev *fbd)
149 {
150 struct pci_dev *pdev = to_pci_dev(fbd->dev);
151 int vector, err;
152
153 WARN_ON(fbd->pcs_msix_vector);
154
155 vector = pci_irq_vector(pdev, FBNIC_PCS_MSIX_ENTRY);
156 if (vector < 0)
157 return vector;
158
159 /* Request the IRQ for PCS link vector.
160 * Map PCS cause to it, and unmask it
161 */
162 err = request_irq(vector, &fbnic_pcs_msix_intr, 0,
163 fbd->netdev->name, fbd);
164 if (err)
165 return err;
166
167 /* Map and enable interrupt, unmask vector after link is configured */
168 fbnic_wr32(fbd, FBNIC_INTR_MSIX_CTRL(FBNIC_INTR_MSIX_CTRL_PCS_IDX),
169 FBNIC_PCS_MSIX_ENTRY | FBNIC_INTR_MSIX_CTRL_ENABLE);
170
171 fbd->pcs_msix_vector = vector;
172
173 return 0;
174 }
175
176 /**
177 * fbnic_pcs_free_irq - Teardown the PCS IRQ to prepare for stopping
178 * @fbd: Pointer to device that is stopping
179 *
180 * This function undoes the work done in fbnic_pcs_request_irq and prepares
181 * the device to no longer receive traffic on the host interface.
182 **/
fbnic_pcs_free_irq(struct fbnic_dev * fbd)183 void fbnic_pcs_free_irq(struct fbnic_dev *fbd)
184 {
185 /* Vector has already been freed */
186 if (!fbd->pcs_msix_vector)
187 return;
188
189 /* Disable interrupt */
190 fbnic_wr32(fbd, FBNIC_INTR_MSIX_CTRL(FBNIC_INTR_MSIX_CTRL_PCS_IDX),
191 FBNIC_PCS_MSIX_ENTRY);
192 fbnic_wrfl(fbd);
193
194 /* Synchronize IRQ to prevent race that would unmask vector */
195 synchronize_irq(fbd->pcs_msix_vector);
196
197 /* Mask the vector */
198 fbnic_wr32(fbd, FBNIC_INTR_MASK_SET(0), 1u << FBNIC_PCS_MSIX_ENTRY);
199
200 /* Free the vector */
201 free_irq(fbd->pcs_msix_vector, fbd);
202 fbd->pcs_msix_vector = 0;
203 }
204
fbnic_synchronize_irq(struct fbnic_dev * fbd,int nr)205 void fbnic_synchronize_irq(struct fbnic_dev *fbd, int nr)
206 {
207 struct pci_dev *pdev = to_pci_dev(fbd->dev);
208 int irq = pci_irq_vector(pdev, nr);
209
210 if (irq < 0)
211 return;
212
213 synchronize_irq(irq);
214 }
215
fbnic_request_irq(struct fbnic_dev * fbd,int nr,irq_handler_t handler,unsigned long flags,const char * name,void * data)216 int fbnic_request_irq(struct fbnic_dev *fbd, int nr, irq_handler_t handler,
217 unsigned long flags, const char *name, void *data)
218 {
219 struct pci_dev *pdev = to_pci_dev(fbd->dev);
220 int irq = pci_irq_vector(pdev, nr);
221
222 if (irq < 0)
223 return irq;
224
225 return request_irq(irq, handler, flags, name, data);
226 }
227
fbnic_free_irq(struct fbnic_dev * fbd,int nr,void * data)228 void fbnic_free_irq(struct fbnic_dev *fbd, int nr, void *data)
229 {
230 struct pci_dev *pdev = to_pci_dev(fbd->dev);
231 int irq = pci_irq_vector(pdev, nr);
232
233 if (irq < 0)
234 return;
235
236 free_irq(irq, data);
237 }
238
fbnic_napi_name_irqs(struct fbnic_dev * fbd)239 void fbnic_napi_name_irqs(struct fbnic_dev *fbd)
240 {
241 unsigned int i;
242
243 for (i = 0; i < ARRAY_SIZE(fbd->napi_irq); i++)
244 snprintf(fbd->napi_irq[i].name,
245 sizeof(fbd->napi_irq[i].name),
246 "%s-TxRx-%u", fbd->netdev->name, i);
247 }
248
fbnic_napi_request_irq(struct fbnic_dev * fbd,struct fbnic_napi_vector * nv)249 int fbnic_napi_request_irq(struct fbnic_dev *fbd,
250 struct fbnic_napi_vector *nv)
251 {
252 struct fbnic_net *fbn = netdev_priv(fbd->netdev);
253 int i = fbnic_napi_idx(nv);
254 int err;
255
256 if (!fbd->napi_irq[i].users) {
257 err = fbnic_request_irq(fbd, nv->v_idx,
258 fbnic_msix_clean_rings, 0,
259 fbd->napi_irq[i].name,
260 &fbn->napi[i]);
261 if (err)
262 return err;
263 }
264
265 fbd->napi_irq[i].users++;
266 return 0;
267 }
268
fbnic_napi_free_irq(struct fbnic_dev * fbd,struct fbnic_napi_vector * nv)269 void fbnic_napi_free_irq(struct fbnic_dev *fbd,
270 struct fbnic_napi_vector *nv)
271 {
272 struct fbnic_net *fbn = netdev_priv(fbd->netdev);
273 int i = fbnic_napi_idx(nv);
274
275 if (--fbd->napi_irq[i].users)
276 return;
277
278 fbnic_free_irq(fbd, nv->v_idx, &fbn->napi[i]);
279 }
280
fbnic_free_irqs(struct fbnic_dev * fbd)281 void fbnic_free_irqs(struct fbnic_dev *fbd)
282 {
283 struct pci_dev *pdev = to_pci_dev(fbd->dev);
284
285 fbd->num_irqs = 0;
286
287 pci_free_irq_vectors(pdev);
288 }
289
fbnic_alloc_irqs(struct fbnic_dev * fbd)290 int fbnic_alloc_irqs(struct fbnic_dev *fbd)
291 {
292 unsigned int wanted_irqs = FBNIC_NON_NAPI_VECTORS;
293 struct pci_dev *pdev = to_pci_dev(fbd->dev);
294 int num_irqs;
295
296 wanted_irqs += min_t(unsigned int, num_online_cpus(), FBNIC_MAX_RXQS);
297 num_irqs = pci_alloc_irq_vectors(pdev, FBNIC_NON_NAPI_VECTORS + 1,
298 wanted_irqs, PCI_IRQ_MSIX);
299 if (num_irqs < 0) {
300 dev_err(fbd->dev, "Failed to allocate MSI-X entries\n");
301 return num_irqs;
302 }
303
304 if (num_irqs < wanted_irqs)
305 dev_warn(fbd->dev, "Allocated %d IRQs, expected %d\n",
306 num_irqs, wanted_irqs);
307
308 fbd->num_irqs = num_irqs;
309
310 return 0;
311 }
312