xref: /freebsd/sys/dev/dpaa2/dpaa2_mac.c (revision fef74e99fc234a687f1a00318ece1834587c4e92)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright © 2021-2022 Dmitry Salychev
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30 
31 /*
32  * The DPAA2 MAC driver.
33  *
34  * For every DPAA2 MAC, there is an MC object named DPMAC, for MDIO and link
35  * state updates. The DPMAC virtualizes the MDIO interface, so each PHY driver
36  * may see a private interface (removing the need for synchronization in GPP on
37  * the multiplexed MDIO hardware).
38  */
39 
40 #include <sys/param.h>
41 #include <sys/kernel.h>
42 #include <sys/bus.h>
43 #include <sys/rman.h>
44 #include <sys/module.h>
45 #include <sys/malloc.h>
46 #include <sys/mutex.h>
47 
48 #include <vm/vm.h>
49 
50 #include <machine/bus.h>
51 #include <machine/resource.h>
52 
53 #include <dev/pci/pcivar.h>
54 
55 #include "pcib_if.h"
56 #include "pci_if.h"
57 
58 #include "dpaa2_mc.h"
59 #include "dpaa2_ni.h"
60 #include "dpaa2_mcp.h"
61 #include "dpaa2_swp.h"
62 #include "dpaa2_swp_if.h"
63 #include "dpaa2_cmd_if.h"
64 
65 /* Index of the only DPMAC IRQ. */
66 #define DPMAC_IRQ_INDEX		0
67 
68 /* DPMAC IRQ statuses. */
69 #define DPMAC_IRQ_LINK_CFG_REQ	0x00000001 /* change in requested link config. */
70 #define DPMAC_IRQ_LINK_CHANGED	0x00000002 /* link state changed */
71 #define DPMAC_IRQ_LINK_UP_REQ	0x00000004 /* link up request */
72 #define DPMAC_IRQ_LINK_DOWN_REQ	0x00000008 /* link down request */
73 #define DPMAC_IRQ_EP_CHANGED	0x00000010 /* DPAA2 endpoint dis/connected */
74 
75 /* DPAA2 MAC resource specification. */
76 struct resource_spec dpaa2_mac_spec[] = {
77 	/*
78 	 * DPMCP resources.
79 	 *
80 	 * NOTE: MC command portals (MCPs) are used to send commands to, and
81 	 *	 receive responses from, the MC firmware. One portal per DPMAC.
82 	 */
83 #define MCP_RES_NUM	(1u)
84 #define MCP_RID_OFF	(0u)
85 #define MCP_RID(rid)	((rid) + MCP_RID_OFF)
86 	/* --- */
87 	{ DPAA2_DEV_MCP, MCP_RID(0), RF_ACTIVE | RF_SHAREABLE | RF_OPTIONAL },
88 	/* --- */
89 	RESOURCE_SPEC_END
90 };
91 
92 /* Interrupt configuration routines. */
93 static int dpaa2_mac_setup_irq(device_t);
94 static int dpaa2_mac_setup_msi(struct dpaa2_mac_softc *);
95 
96 /* Subroutines to get text representation. */
97 static const char *dpaa2_mac_ethif_to_str(enum dpaa2_mac_eth_if);
98 static const char *dpaa2_mac_link_type_to_str(enum dpaa2_mac_link_type);
99 
100 /* Interrupt handlers */
101 static void dpaa2_mac_intr(void *arg);
102 
103 static int
104 dpaa2_mac_probe(device_t dev)
105 {
106 	/* DPIO device will be added by a parent resource container itself. */
107 	device_set_desc(dev, "DPAA2 MAC");
108 	return (BUS_PROBE_DEFAULT);
109 }
110 
111 static int
112 dpaa2_mac_attach(device_t dev)
113 {
114 	device_t pdev = device_get_parent(dev);
115 	device_t child = dev;
116 	device_t mcp_dev;
117 	struct dpaa2_mac_softc *sc = device_get_softc(dev);
118 	struct dpaa2_devinfo *rcinfo = device_get_ivars(pdev);
119 	struct dpaa2_devinfo *dinfo = device_get_ivars(dev);
120 	struct dpaa2_devinfo *mcp_dinfo;
121 	struct dpaa2_cmd cmd;
122 	uint16_t rc_token, mac_token;
123 	int error;
124 
125 	sc->dev = dev;
126 
127 	memset(sc->addr, 0, ETHER_ADDR_LEN);
128 
129 	error = bus_alloc_resources(sc->dev, dpaa2_mac_spec, sc->res);
130 	if (error) {
131 		device_printf(dev, "%s: failed to allocate resources: "
132 		    "error=%d\n", __func__, error);
133 		goto err_exit;
134 	}
135 
136 	/* Obtain MC portal. */
137 	mcp_dev = (device_t) rman_get_start(sc->res[MCP_RID(0)]);
138 	mcp_dinfo = device_get_ivars(mcp_dev);
139 	dinfo->portal = mcp_dinfo->portal;
140 
141 	DPAA2_CMD_INIT(&cmd);
142 
143 	error = DPAA2_CMD_RC_OPEN(dev, child, &cmd, rcinfo->id, &rc_token);
144 	if (error) {
145 		device_printf(dev, "%s: failed to open DPRC: error=%d\n",
146 		    __func__, error);
147 		goto err_exit;
148 	}
149 	error = DPAA2_CMD_MAC_OPEN(dev, child, &cmd, dinfo->id, &mac_token);
150 	if (error) {
151 		device_printf(dev, "%s: failed to open DPMAC: id=%d, error=%d\n",
152 		    __func__, dinfo->id, error);
153 		goto close_rc;
154 	}
155 
156 	error = DPAA2_CMD_MAC_GET_ATTRIBUTES(dev, child, &cmd, &sc->attr);
157 	if (error) {
158 		device_printf(dev, "%s: failed to get DPMAC attributes: id=%d, "
159 		    "error=%d\n", __func__, dinfo->id, error);
160 		goto close_mac;
161 	}
162 	error = DPAA2_CMD_MAC_GET_ADDR(dev, child, &cmd, sc->addr);
163 	if (error) {
164 		device_printf(dev, "%s: failed to get physical address: "
165 		    "error=%d\n", __func__, error);
166 	}
167 
168 	if (bootverbose) {
169 		device_printf(dev, "ether %6D\n", sc->addr, ":");
170 		device_printf(dev, "max_rate=%d, eth_if=%s, link_type=%s\n",
171 		    sc->attr.max_rate,
172 		    dpaa2_mac_ethif_to_str(sc->attr.eth_if),
173 		    dpaa2_mac_link_type_to_str(sc->attr.link_type));
174 	}
175 
176 	error = dpaa2_mac_setup_irq(dev);
177 	if (error) {
178 		device_printf(dev, "%s: failed to setup IRQs: error=%d\n",
179 		    __func__, error);
180 		goto close_mac;
181 	}
182 
183 	(void)DPAA2_CMD_MAC_CLOSE(dev, child, DPAA2_CMD_TK(&cmd, mac_token));
184 	(void)DPAA2_CMD_RC_CLOSE(dev, child, DPAA2_CMD_TK(&cmd, rc_token));
185 	return (0);
186 
187 close_mac:
188 	(void)DPAA2_CMD_MAC_CLOSE(dev, child, DPAA2_CMD_TK(&cmd, mac_token));
189 close_rc:
190 	(void)DPAA2_CMD_RC_CLOSE(dev, child, DPAA2_CMD_TK(&cmd, rc_token));
191 err_exit:
192 	return (ENXIO);
193 }
194 
195 static int
196 dpaa2_mac_detach(device_t dev)
197 {
198 	/* TBD */
199 	return (0);
200 }
201 
202 /**
203  * @brief Configure DPMAC object to generate interrupts.
204  */
205 static int
206 dpaa2_mac_setup_irq(device_t dev)
207 {
208 	device_t pdev = device_get_parent(dev);
209 	device_t child = dev;
210 	struct dpaa2_mac_softc *sc = device_get_softc(dev);
211 	struct dpaa2_devinfo *rcinfo = device_get_ivars(pdev);
212 	struct dpaa2_devinfo *dinfo = device_get_ivars(dev);
213 	struct dpaa2_cmd cmd;
214 	uint16_t rc_token, mac_token;
215 	uint32_t irq_mask;
216 	int error;
217 
218 	error = dpaa2_mac_setup_msi(sc);
219 	if (error) {
220 		device_printf(dev, "%s: failed to allocate MSI\n", __func__);
221 		goto err_exit;
222 	}
223 	if ((sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
224 	    &sc->irq_rid[0], RF_ACTIVE | RF_SHAREABLE)) == NULL) {
225 		device_printf(dev, "%s: failed to allocate IRQ resource\n",
226 		    __func__);
227 		error = ENXIO;
228 		goto err_exit;
229 	}
230 	if (bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET | INTR_MPSAFE,
231 	    NULL, dpaa2_mac_intr, sc, &sc->intr)) {
232 		device_printf(dev, "%s: failed to setup IRQ resource\n",
233 		    __func__);
234 		error = ENXIO;
235 		goto err_exit;
236 	}
237 
238 	DPAA2_CMD_INIT(&cmd);
239 
240 	error = DPAA2_CMD_RC_OPEN(dev, child, &cmd, rcinfo->id, &rc_token);
241 	if (error) {
242 		device_printf(dev, "%s: failed to open DPRC: error=%d\n",
243 		    __func__, error);
244 		goto err_exit;
245 	}
246 	error = DPAA2_CMD_MAC_OPEN(dev, child, &cmd, dinfo->id, &mac_token);
247 	if (error) {
248 		device_printf(dev, "%s: failed to open DPMAC: id=%d, error=%d\n",
249 		    __func__, dinfo->id, error);
250 		goto close_rc;
251 	}
252 
253 	irq_mask =
254 	    DPMAC_IRQ_LINK_CFG_REQ |
255 	    DPMAC_IRQ_LINK_CHANGED |
256 	    DPMAC_IRQ_LINK_UP_REQ |
257 	    DPMAC_IRQ_LINK_DOWN_REQ |
258 	    DPMAC_IRQ_EP_CHANGED;
259 	error = DPAA2_CMD_MAC_SET_IRQ_MASK(dev, child, &cmd, DPMAC_IRQ_INDEX,
260 	    irq_mask);
261 	if (error) {
262 		device_printf(dev, "%s: failed to set IRQ mask\n", __func__);
263 		goto close_mac;
264 	}
265 	error = DPAA2_CMD_MAC_SET_IRQ_ENABLE(dev, child, &cmd, DPMAC_IRQ_INDEX,
266 	    true);
267 	if (error) {
268 		device_printf(dev, "%s: failed to enable IRQ\n", __func__);
269 		goto close_mac;
270 	}
271 
272 	(void)DPAA2_CMD_MAC_CLOSE(dev, child, DPAA2_CMD_TK(&cmd, mac_token));
273 	(void)DPAA2_CMD_RC_CLOSE(dev, child, DPAA2_CMD_TK(&cmd, rc_token));
274 	return (0);
275 
276 close_mac:
277 	(void)DPAA2_CMD_MAC_CLOSE(dev, child, DPAA2_CMD_TK(&cmd, mac_token));
278 close_rc:
279 	(void)DPAA2_CMD_RC_CLOSE(dev, child, DPAA2_CMD_TK(&cmd, rc_token));
280 err_exit:
281 	return (error);
282 }
283 
284 /**
285  * @brief Allocate MSI interrupts for DPMAC.
286  */
287 static int
288 dpaa2_mac_setup_msi(struct dpaa2_mac_softc *sc)
289 {
290 	int val;
291 
292 	val = pci_msi_count(sc->dev);
293 	if (val < DPAA2_MAC_MSI_COUNT)
294 		device_printf(sc->dev, "MSI: actual=%d, expected=%d\n", val,
295 		    DPAA2_MAC_MSI_COUNT);
296 	val = MIN(val, DPAA2_MAC_MSI_COUNT);
297 
298 	if (pci_alloc_msi(sc->dev, &val) != 0)
299 		return (EINVAL);
300 
301 	for (int i = 0; i < val; i++)
302 		sc->irq_rid[i] = i + 1;
303 
304 	return (0);
305 }
306 
307 static void
308 dpaa2_mac_intr(void *arg)
309 {
310 	struct dpaa2_mac_softc *sc = (struct dpaa2_mac_softc *) arg;
311 	device_t pdev = device_get_parent(sc->dev);
312 	device_t dev = sc->dev;
313 	device_t child = dev;
314 	struct dpaa2_devinfo *rcinfo = device_get_ivars(pdev);
315 	struct dpaa2_devinfo *dinfo = device_get_ivars(dev);
316 	struct dpaa2_cmd cmd;
317 	uint32_t status = ~0u; /* clear all IRQ status bits */
318 	uint16_t rc_token, mac_token;
319 	int error;
320 
321 	DPAA2_CMD_INIT(&cmd);
322 
323 	error = DPAA2_CMD_RC_OPEN(dev, child, &cmd, rcinfo->id, &rc_token);
324 	if (error) {
325 		device_printf(dev, "%s: failed to open DPRC: error=%d\n",
326 		    __func__, error);
327 		goto err_exit;
328 	}
329 	error = DPAA2_CMD_MAC_OPEN(dev, child, &cmd, dinfo->id, &mac_token);
330 	if (error) {
331 		device_printf(dev, "%s: failed to open DPMAC: id=%d, error=%d\n",
332 		    __func__, dinfo->id, error);
333 		goto close_rc;
334 	}
335 	error = DPAA2_CMD_MAC_GET_IRQ_STATUS(dev, child, &cmd, DPMAC_IRQ_INDEX,
336 	    &status);
337 	if (error) {
338 		device_printf(sc->dev, "%s: failed to obtain IRQ status: "
339 		    "error=%d\n", __func__, error);
340 	}
341 
342 	(void)DPAA2_CMD_MAC_CLOSE(dev, child, DPAA2_CMD_TK(&cmd, mac_token));
343 close_rc:
344 	(void)DPAA2_CMD_RC_CLOSE(dev, child, DPAA2_CMD_TK(&cmd, rc_token));
345 err_exit:
346 	return;
347 }
348 
349 static const char *
350 dpaa2_mac_ethif_to_str(enum dpaa2_mac_eth_if eth_if)
351 {
352 	switch (eth_if) {
353 	case DPAA2_MAC_ETH_IF_MII:
354 		return ("MII");
355 	case DPAA2_MAC_ETH_IF_RMII:
356 		return ("RMII");
357 	case DPAA2_MAC_ETH_IF_SMII:
358 		return ("SMII");
359 	case DPAA2_MAC_ETH_IF_GMII:
360 		return ("GMII");
361 	case DPAA2_MAC_ETH_IF_RGMII:
362 		return ("RGMII");
363 	case DPAA2_MAC_ETH_IF_SGMII:
364 		return ("SGMII");
365 	case DPAA2_MAC_ETH_IF_QSGMII:
366 		return ("QSGMII");
367 	case DPAA2_MAC_ETH_IF_XAUI:
368 		return ("XAUI");
369 	case DPAA2_MAC_ETH_IF_XFI:
370 		return ("XFI");
371 	case DPAA2_MAC_ETH_IF_CAUI:
372 		return ("CAUI");
373 	case DPAA2_MAC_ETH_IF_1000BASEX:
374 		return ("1000BASE-X");
375 	case DPAA2_MAC_ETH_IF_USXGMII:
376 		return ("USXGMII");
377 	default:
378 		return ("unknown");
379 	}
380 }
381 
382 static const char *
383 dpaa2_mac_link_type_to_str(enum dpaa2_mac_link_type link_type)
384 {
385 	switch (link_type) {
386 	case DPAA2_MAC_LINK_TYPE_NONE:
387 		return ("NONE");
388 	case DPAA2_MAC_LINK_TYPE_FIXED:
389 		return ("FIXED");
390 	case DPAA2_MAC_LINK_TYPE_PHY:
391 		return ("PHY");
392 	case DPAA2_MAC_LINK_TYPE_BACKPLANE:
393 		return ("BACKPLANE");
394 	default:
395 		return ("unknown");
396 	}
397 }
398 
399 static device_method_t dpaa2_mac_methods[] = {
400 	/* Device interface */
401 	DEVMETHOD(device_probe,		dpaa2_mac_probe),
402 	DEVMETHOD(device_attach,	dpaa2_mac_attach),
403 	DEVMETHOD(device_detach,	dpaa2_mac_detach),
404 
405 	DEVMETHOD_END
406 };
407 
408 static driver_t dpaa2_mac_driver = {
409 	"dpaa2_mac",
410 	dpaa2_mac_methods,
411 	sizeof(struct dpaa2_mac_softc),
412 };
413 
414 DRIVER_MODULE(dpaa2_mac, dpaa2_rc, dpaa2_mac_driver, 0, 0);
415