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