xref: /linux/drivers/net/ethernet/meta/fbnic/fbnic_irq.c (revision 8f7aa3d3c7323f4ca2768a9e74ebbe359c4f8f88)
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 
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 
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  **/
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  **/
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  **/
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 
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  **/
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  **/
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 
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 
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 
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 
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 
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 
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 
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 
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