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_mac_msix_intr(int __always_unused irq,void * data)121 static irqreturn_t fbnic_mac_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->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 /* Record link down events */
135 if (!fbd->mac->get_link(fbd, fbn->aui, fbn->fec))
136 phylink_pcs_change(fbn->pcs, false);
137
138 return IRQ_HANDLED;
139 }
140
141 /**
142 * fbnic_mac_request_irq - Configure the MAC to enable it to advertise link
143 * @fbd: Pointer to device to initialize
144 *
145 * This function provides basic bringup for the MAC/PHY IRQ. For now the IRQ
146 * will remain disabled until we start the MAC/PCS/PHY logic via phylink.
147 *
148 * Return: non-zero on failure.
149 **/
fbnic_mac_request_irq(struct fbnic_dev * fbd)150 int fbnic_mac_request_irq(struct fbnic_dev *fbd)
151 {
152 struct pci_dev *pdev = to_pci_dev(fbd->dev);
153 int vector, err;
154
155 WARN_ON(fbd->mac_msix_vector);
156
157 vector = pci_irq_vector(pdev, FBNIC_PCS_MSIX_ENTRY);
158 if (vector < 0)
159 return vector;
160
161 /* Request the IRQ for PCS link vector.
162 * Map PCS cause to it, and unmask it
163 */
164 err = request_irq(vector, &fbnic_mac_msix_intr, 0,
165 fbd->netdev->name, fbd);
166 if (err)
167 return err;
168
169 /* Map and enable interrupt, unmask vector after link is configured */
170 fbnic_wr32(fbd, FBNIC_INTR_MSIX_CTRL(FBNIC_INTR_MSIX_CTRL_PCS_IDX),
171 FBNIC_PCS_MSIX_ENTRY | FBNIC_INTR_MSIX_CTRL_ENABLE);
172
173 fbd->mac_msix_vector = vector;
174
175 return 0;
176 }
177
178 /**
179 * fbnic_mac_free_irq - Teardown the MAC IRQ to prepare for stopping
180 * @fbd: Pointer to device that is stopping
181 *
182 * This function undoes the work done in fbnic_mac_request_irq and prepares
183 * the device to no longer receive traffic on the host interface.
184 **/
fbnic_mac_free_irq(struct fbnic_dev * fbd)185 void fbnic_mac_free_irq(struct fbnic_dev *fbd)
186 {
187 /* Vector has already been freed */
188 if (!fbd->mac_msix_vector)
189 return;
190
191 /* Disable interrupt */
192 fbnic_wr32(fbd, FBNIC_INTR_MSIX_CTRL(FBNIC_INTR_MSIX_CTRL_PCS_IDX),
193 FBNIC_PCS_MSIX_ENTRY);
194 fbnic_wrfl(fbd);
195
196 /* Synchronize IRQ to prevent race that would unmask vector */
197 synchronize_irq(fbd->mac_msix_vector);
198
199 /* Mask the vector */
200 fbnic_wr32(fbd, FBNIC_INTR_MASK_SET(0), 1u << FBNIC_PCS_MSIX_ENTRY);
201
202 /* Free the vector */
203 free_irq(fbd->mac_msix_vector, fbd);
204 fbd->mac_msix_vector = 0;
205 }
206
fbnic_synchronize_irq(struct fbnic_dev * fbd,int nr)207 void fbnic_synchronize_irq(struct fbnic_dev *fbd, int nr)
208 {
209 struct pci_dev *pdev = to_pci_dev(fbd->dev);
210 int irq = pci_irq_vector(pdev, nr);
211
212 if (irq < 0)
213 return;
214
215 synchronize_irq(irq);
216 }
217
fbnic_request_irq(struct fbnic_dev * fbd,int nr,irq_handler_t handler,unsigned long flags,const char * name,void * data)218 int fbnic_request_irq(struct fbnic_dev *fbd, int nr, irq_handler_t handler,
219 unsigned long flags, const char *name, void *data)
220 {
221 struct pci_dev *pdev = to_pci_dev(fbd->dev);
222 int irq = pci_irq_vector(pdev, nr);
223
224 if (irq < 0)
225 return irq;
226
227 return request_irq(irq, handler, flags, name, data);
228 }
229
fbnic_free_irq(struct fbnic_dev * fbd,int nr,void * data)230 void fbnic_free_irq(struct fbnic_dev *fbd, int nr, void *data)
231 {
232 struct pci_dev *pdev = to_pci_dev(fbd->dev);
233 int irq = pci_irq_vector(pdev, nr);
234
235 if (irq < 0)
236 return;
237
238 free_irq(irq, data);
239 }
240
fbnic_napi_name_irqs(struct fbnic_dev * fbd)241 void fbnic_napi_name_irqs(struct fbnic_dev *fbd)
242 {
243 unsigned int i;
244
245 for (i = 0; i < ARRAY_SIZE(fbd->napi_irq); i++)
246 snprintf(fbd->napi_irq[i].name,
247 sizeof(fbd->napi_irq[i].name),
248 "%s-TxRx-%u", fbd->netdev->name, i);
249 }
250
fbnic_napi_request_irq(struct fbnic_dev * fbd,struct fbnic_napi_vector * nv)251 int fbnic_napi_request_irq(struct fbnic_dev *fbd,
252 struct fbnic_napi_vector *nv)
253 {
254 struct fbnic_net *fbn = netdev_priv(fbd->netdev);
255 int i = fbnic_napi_idx(nv);
256 int err;
257
258 if (!fbd->napi_irq[i].users) {
259 err = fbnic_request_irq(fbd, nv->v_idx,
260 fbnic_msix_clean_rings, 0,
261 fbd->napi_irq[i].name,
262 &fbn->napi[i]);
263 if (err)
264 return err;
265 }
266
267 fbd->napi_irq[i].users++;
268 return 0;
269 }
270
fbnic_napi_free_irq(struct fbnic_dev * fbd,struct fbnic_napi_vector * nv)271 void fbnic_napi_free_irq(struct fbnic_dev *fbd,
272 struct fbnic_napi_vector *nv)
273 {
274 struct fbnic_net *fbn = netdev_priv(fbd->netdev);
275 int i = fbnic_napi_idx(nv);
276
277 if (--fbd->napi_irq[i].users)
278 return;
279
280 fbnic_free_irq(fbd, nv->v_idx, &fbn->napi[i]);
281 }
282
fbnic_free_irqs(struct fbnic_dev * fbd)283 void fbnic_free_irqs(struct fbnic_dev *fbd)
284 {
285 struct pci_dev *pdev = to_pci_dev(fbd->dev);
286
287 fbd->num_irqs = 0;
288
289 pci_free_irq_vectors(pdev);
290 }
291
fbnic_alloc_irqs(struct fbnic_dev * fbd)292 int fbnic_alloc_irqs(struct fbnic_dev *fbd)
293 {
294 unsigned int wanted_irqs = FBNIC_NON_NAPI_VECTORS;
295 struct pci_dev *pdev = to_pci_dev(fbd->dev);
296 int num_irqs;
297
298 wanted_irqs += min_t(unsigned int, num_online_cpus(), FBNIC_MAX_RXQS);
299 num_irqs = pci_alloc_irq_vectors(pdev, FBNIC_NON_NAPI_VECTORS + 1,
300 wanted_irqs, PCI_IRQ_MSIX);
301 if (num_irqs < 0) {
302 dev_err(fbd->dev, "Failed to allocate MSI-X entries\n");
303 return num_irqs;
304 }
305
306 if (num_irqs < wanted_irqs)
307 dev_warn(fbd->dev, "Allocated %d IRQs, expected %d\n",
308 num_irqs, wanted_irqs);
309
310 fbd->num_irqs = num_irqs;
311
312 return 0;
313 }
314