xref: /linux/drivers/net/ethernet/meta/fbnic/fbnic_irq.c (revision 385f186aba3d2f7122b71d6d4c7e236b9d4e8003)
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 /**
23  * fbnic_fw_enable_mbx - Configure and initialize Firmware Mailbox
24  * @fbd: Pointer to device to initialize
25  *
26  * This function will initialize the firmware mailbox rings, enable the IRQ
27  * and initialize the communication between the Firmware and the host. The
28  * firmware is expected to respond to the initialization by sending an
29  * interrupt essentially notifying the host that it has seen the
30  * initialization and is now synced up.
31  *
32  * Return: non-zero on failure.
33  **/
34 int fbnic_fw_enable_mbx(struct fbnic_dev *fbd)
35 {
36 	u32 vector = fbd->fw_msix_vector;
37 	int err;
38 
39 	/* Request the IRQ for FW Mailbox vector. */
40 	err = request_threaded_irq(vector, NULL, &fbnic_fw_msix_intr,
41 				   IRQF_ONESHOT, dev_name(fbd->dev), fbd);
42 	if (err)
43 		return err;
44 
45 	/* Initialize mailbox and attempt to poll it into ready state */
46 	fbnic_mbx_init(fbd);
47 	err = fbnic_mbx_poll_tx_ready(fbd);
48 	if (err) {
49 		dev_warn(fbd->dev, "FW mailbox did not enter ready state\n");
50 		free_irq(vector, fbd);
51 		return err;
52 	}
53 
54 	/* Enable interrupts */
55 	fbnic_wr32(fbd, FBNIC_INTR_MASK_CLEAR(0), 1u << FBNIC_FW_MSIX_ENTRY);
56 
57 	return 0;
58 }
59 
60 /**
61  * fbnic_fw_disable_mbx - Disable mailbox and place it in standby state
62  * @fbd: Pointer to device to disable
63  *
64  * This function will disable the mailbox interrupt, free any messages still
65  * in the mailbox and place it into a standby state. The firmware is
66  * expected to see the update and assume that the host is in the reset state.
67  **/
68 void fbnic_fw_disable_mbx(struct fbnic_dev *fbd)
69 {
70 	/* Disable interrupt and free vector */
71 	fbnic_wr32(fbd, FBNIC_INTR_MASK_SET(0), 1u << FBNIC_FW_MSIX_ENTRY);
72 
73 	/* Free the vector */
74 	free_irq(fbd->fw_msix_vector, fbd);
75 
76 	/* Make sure disabling logs message is sent, must be done here to
77 	 * avoid risk of completing without a running interrupt.
78 	 */
79 	fbnic_mbx_flush_tx(fbd);
80 
81 	/* Reset the mailboxes to the initialized state */
82 	fbnic_mbx_clean(fbd);
83 }
84 
85 static irqreturn_t fbnic_pcs_msix_intr(int __always_unused irq, void *data)
86 {
87 	struct fbnic_dev *fbd = data;
88 	struct fbnic_net *fbn;
89 
90 	if (fbd->mac->pcs_get_link_event(fbd) == FBNIC_LINK_EVENT_NONE) {
91 		fbnic_wr32(fbd, FBNIC_INTR_MASK_CLEAR(0),
92 			   1u << FBNIC_PCS_MSIX_ENTRY);
93 		return IRQ_HANDLED;
94 	}
95 
96 	fbn = netdev_priv(fbd->netdev);
97 
98 	phylink_pcs_change(&fbn->phylink_pcs, false);
99 
100 	return IRQ_HANDLED;
101 }
102 
103 /**
104  * fbnic_pcs_irq_enable - Configure the MAC to enable it to advertise link
105  * @fbd: Pointer to device to initialize
106  *
107  * This function provides basic bringup for the MAC/PCS IRQ. For now the IRQ
108  * will remain disabled until we start the MAC/PCS/PHY logic via phylink.
109  *
110  * Return: non-zero on failure.
111  **/
112 int fbnic_pcs_irq_enable(struct fbnic_dev *fbd)
113 {
114 	u32 vector = fbd->pcs_msix_vector;
115 	int err;
116 
117 	/* Request the IRQ for MAC link vector.
118 	 * Map MAC cause to it, and unmask it
119 	 */
120 	err = request_irq(vector, &fbnic_pcs_msix_intr, 0,
121 			  fbd->netdev->name, fbd);
122 	if (err)
123 		return err;
124 
125 	fbnic_wr32(fbd, FBNIC_INTR_MSIX_CTRL(FBNIC_INTR_MSIX_CTRL_PCS_IDX),
126 		   FBNIC_PCS_MSIX_ENTRY | FBNIC_INTR_MSIX_CTRL_ENABLE);
127 
128 	return 0;
129 }
130 
131 /**
132  * fbnic_pcs_irq_disable - Teardown the MAC IRQ to prepare for stopping
133  * @fbd: Pointer to device that is stopping
134  *
135  * This function undoes the work done in fbnic_pcs_irq_enable and prepares
136  * the device to no longer receive traffic on the host interface.
137  **/
138 void fbnic_pcs_irq_disable(struct fbnic_dev *fbd)
139 {
140 	/* Disable interrupt */
141 	fbnic_wr32(fbd, FBNIC_INTR_MSIX_CTRL(FBNIC_INTR_MSIX_CTRL_PCS_IDX),
142 		   FBNIC_PCS_MSIX_ENTRY);
143 	fbnic_wr32(fbd, FBNIC_INTR_MASK_SET(0), 1u << FBNIC_PCS_MSIX_ENTRY);
144 
145 	/* Free the vector */
146 	free_irq(fbd->pcs_msix_vector, fbd);
147 }
148 
149 void fbnic_synchronize_irq(struct fbnic_dev *fbd, int nr)
150 {
151 	struct pci_dev *pdev = to_pci_dev(fbd->dev);
152 	int irq = pci_irq_vector(pdev, nr);
153 
154 	if (irq < 0)
155 		return;
156 
157 	synchronize_irq(irq);
158 }
159 
160 int fbnic_request_irq(struct fbnic_dev *fbd, int nr, irq_handler_t handler,
161 		      unsigned long flags, const char *name, void *data)
162 {
163 	struct pci_dev *pdev = to_pci_dev(fbd->dev);
164 	int irq = pci_irq_vector(pdev, nr);
165 
166 	if (irq < 0)
167 		return irq;
168 
169 	return request_irq(irq, handler, flags, name, data);
170 }
171 
172 void fbnic_free_irq(struct fbnic_dev *fbd, int nr, void *data)
173 {
174 	struct pci_dev *pdev = to_pci_dev(fbd->dev);
175 	int irq = pci_irq_vector(pdev, nr);
176 
177 	if (irq < 0)
178 		return;
179 
180 	free_irq(irq, data);
181 }
182 
183 void fbnic_napi_name_irqs(struct fbnic_dev *fbd)
184 {
185 	unsigned int i;
186 
187 	for (i = 0; i < ARRAY_SIZE(fbd->napi_irq); i++)
188 		snprintf(fbd->napi_irq[i].name,
189 			 sizeof(fbd->napi_irq[i].name),
190 			 "%s-TxRx-%u", fbd->netdev->name, i);
191 }
192 
193 int fbnic_napi_request_irq(struct fbnic_dev *fbd,
194 			   struct fbnic_napi_vector *nv)
195 {
196 	struct fbnic_net *fbn = netdev_priv(fbd->netdev);
197 	int i = fbnic_napi_idx(nv);
198 	int err;
199 
200 	if (!fbd->napi_irq[i].users) {
201 		err = fbnic_request_irq(fbd, nv->v_idx,
202 					fbnic_msix_clean_rings,	0,
203 					fbd->napi_irq[i].name,
204 					&fbn->napi[i]);
205 		if (err)
206 			return err;
207 	}
208 
209 	fbd->napi_irq[i].users++;
210 	return 0;
211 }
212 
213 void fbnic_napi_free_irq(struct fbnic_dev *fbd,
214 			 struct fbnic_napi_vector *nv)
215 {
216 	struct fbnic_net *fbn = netdev_priv(fbd->netdev);
217 	int i = fbnic_napi_idx(nv);
218 
219 	if (--fbd->napi_irq[i].users)
220 		return;
221 
222 	fbnic_free_irq(fbd, nv->v_idx, &fbn->napi[i]);
223 }
224 
225 void fbnic_free_irqs(struct fbnic_dev *fbd)
226 {
227 	struct pci_dev *pdev = to_pci_dev(fbd->dev);
228 
229 	fbd->pcs_msix_vector = 0;
230 	fbd->fw_msix_vector = 0;
231 
232 	fbd->num_irqs = 0;
233 
234 	pci_free_irq_vectors(pdev);
235 }
236 
237 int fbnic_alloc_irqs(struct fbnic_dev *fbd)
238 {
239 	unsigned int wanted_irqs = FBNIC_NON_NAPI_VECTORS;
240 	struct pci_dev *pdev = to_pci_dev(fbd->dev);
241 	int num_irqs;
242 
243 	wanted_irqs += min_t(unsigned int, num_online_cpus(), FBNIC_MAX_RXQS);
244 	num_irqs = pci_alloc_irq_vectors(pdev, FBNIC_NON_NAPI_VECTORS + 1,
245 					 wanted_irqs, PCI_IRQ_MSIX);
246 	if (num_irqs < 0) {
247 		dev_err(fbd->dev, "Failed to allocate MSI-X entries\n");
248 		return num_irqs;
249 	}
250 
251 	if (num_irqs < wanted_irqs)
252 		dev_warn(fbd->dev, "Allocated %d IRQs, expected %d\n",
253 			 num_irqs, wanted_irqs);
254 
255 	fbd->num_irqs = num_irqs;
256 
257 	fbd->pcs_msix_vector = pci_irq_vector(pdev, FBNIC_PCS_MSIX_ENTRY);
258 	fbd->fw_msix_vector = pci_irq_vector(pdev, FBNIC_FW_MSIX_ENTRY);
259 
260 	return 0;
261 }
262