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