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 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 return (ENXIO);
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 /* Allocate a command to send to MC hardware. */
140 error = dpaa2_mcp_init_command(&sc->cmd, DPAA2_CMD_DEF);
141 if (error) {
142 device_printf(dev, "Failed to allocate dpaa2_cmd: error=%d\n",
143 error);
144 goto err_exit;
145 }
146
147 /* Open resource container and DPMAC object. */
148 error = DPAA2_CMD_RC_OPEN(dev, child, sc->cmd, rcinfo->id,
149 &sc->rc_token);
150 if (error) {
151 device_printf(dev, "Failed to open DPRC: error=%d\n", error);
152 goto err_free_cmd;
153 }
154 error = DPAA2_CMD_MAC_OPEN(dev, child, sc->cmd, dinfo->id,
155 &sc->mac_token);
156 if (error) {
157 device_printf(dev, "Failed to open DPMAC: id=%d, error=%d\n",
158 dinfo->id, error);
159 goto err_close_rc;
160 }
161
162 error = DPAA2_CMD_MAC_GET_ATTRIBUTES(dev, child, sc->cmd, &sc->attr);
163 if (error) {
164 device_printf(dev, "Failed to get DPMAC attributes: id=%d, "
165 "error=%d\n", dinfo->id, error);
166 goto err_close_mac;
167 }
168 error = DPAA2_CMD_MAC_GET_ADDR(dev, child, sc->cmd, sc->addr);
169 if (error)
170 device_printf(dev, "Failed to get physical address: error=%d\n",
171 error);
172 /*
173 * TODO: Enable debug output via sysctl.
174 */
175 if (bootverbose) {
176 device_printf(dev, "ether %6D\n", sc->addr, ":");
177 device_printf(dev, "max_rate=%d, eth_if=%s, link_type=%s\n",
178 sc->attr.max_rate,
179 dpaa2_mac_ethif_to_str(sc->attr.eth_if),
180 dpaa2_mac_link_type_to_str(sc->attr.link_type));
181 }
182
183 error = dpaa2_mac_setup_irq(dev);
184 if (error) {
185 device_printf(dev, "Failed to setup IRQs: error=%d\n", error);
186 goto err_close_mac;
187 }
188
189 return (0);
190
191 err_close_mac:
192 DPAA2_CMD_MAC_CLOSE(dev, child, dpaa2_mcp_tk(sc->cmd, sc->mac_token));
193 err_close_rc:
194 DPAA2_CMD_RC_CLOSE(dev, child, dpaa2_mcp_tk(sc->cmd, sc->rc_token));
195 err_free_cmd:
196 dpaa2_mcp_free_command(sc->cmd);
197 err_exit:
198 return (ENXIO);
199 }
200
201 static int
202 dpaa2_mac_detach(device_t dev)
203 {
204 device_t child = dev;
205 struct dpaa2_mac_softc *sc = device_get_softc(dev);
206
207 DPAA2_CMD_MAC_CLOSE(dev, child, dpaa2_mcp_tk(sc->cmd, sc->mac_token));
208 DPAA2_CMD_RC_CLOSE(dev, child, dpaa2_mcp_tk(sc->cmd, sc->rc_token));
209 dpaa2_mcp_free_command(sc->cmd);
210
211 sc->cmd = NULL;
212 sc->rc_token = 0;
213 sc->mac_token = 0;
214
215 return (0);
216 }
217
218 /**
219 * @brief Configure DPMAC object to generate interrupts.
220 */
221 static int
222 dpaa2_mac_setup_irq(device_t dev)
223 {
224 device_t child = dev;
225 struct dpaa2_mac_softc *sc = device_get_softc(dev);
226 struct dpaa2_cmd *cmd = sc->cmd;
227 uint16_t mac_token = sc->mac_token;
228 uint32_t irq_mask;
229 int error;
230
231 /* Configure IRQs. */
232 error = dpaa2_mac_setup_msi(sc);
233 if (error) {
234 device_printf(dev, "Failed to allocate MSI\n");
235 return (error);
236 }
237 if ((sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
238 &sc->irq_rid[0], RF_ACTIVE | RF_SHAREABLE)) == NULL) {
239 device_printf(dev, "Failed to allocate IRQ resource\n");
240 return (ENXIO);
241 }
242 if (bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET | INTR_MPSAFE,
243 NULL, dpaa2_mac_intr, sc, &sc->intr)) {
244 device_printf(dev, "Failed to setup IRQ resource\n");
245 return (ENXIO);
246 }
247
248 /* Configure DPNI to generate interrupts. */
249 irq_mask =
250 DPMAC_IRQ_LINK_CFG_REQ |
251 DPMAC_IRQ_LINK_CHANGED |
252 DPMAC_IRQ_LINK_UP_REQ |
253 DPMAC_IRQ_LINK_DOWN_REQ |
254 DPMAC_IRQ_EP_CHANGED;
255 error = DPAA2_CMD_MAC_SET_IRQ_MASK(dev, child, dpaa2_mcp_tk(cmd,
256 mac_token), DPMAC_IRQ_INDEX, irq_mask);
257 if (error) {
258 device_printf(dev, "Failed to set IRQ mask\n");
259 return (error);
260 }
261
262 /* Enable IRQ. */
263 error = DPAA2_CMD_MAC_SET_IRQ_ENABLE(dev, child, cmd, DPMAC_IRQ_INDEX,
264 true);
265 if (error) {
266 device_printf(dev, "Failed to enable IRQ\n");
267 return (error);
268 }
269
270 return (0);
271 }
272
273 /**
274 * @brief Allocate MSI interrupts for DPMAC.
275 */
276 static int
277 dpaa2_mac_setup_msi(struct dpaa2_mac_softc *sc)
278 {
279 int val;
280
281 val = pci_msi_count(sc->dev);
282 if (val < DPAA2_MAC_MSI_COUNT)
283 device_printf(sc->dev, "MSI: actual=%d, expected=%d\n", val,
284 DPAA2_MAC_MSI_COUNT);
285 val = MIN(val, DPAA2_MAC_MSI_COUNT);
286
287 if (pci_alloc_msi(sc->dev, &val) != 0)
288 return (EINVAL);
289
290 for (int i = 0; i < val; i++)
291 sc->irq_rid[i] = i + 1;
292
293 return (0);
294 }
295
296 static void
297 dpaa2_mac_intr(void *arg)
298 {
299 struct dpaa2_mac_softc *sc = (struct dpaa2_mac_softc *) arg;
300 device_t child = sc->dev;
301 uint32_t status = ~0u; /* clear all IRQ status bits */
302 int error;
303
304 error = DPAA2_CMD_MAC_GET_IRQ_STATUS(sc->dev, child,
305 dpaa2_mcp_tk(sc->cmd, sc->mac_token), DPMAC_IRQ_INDEX, &status);
306 if (error)
307 device_printf(sc->dev, "%s: failed to obtain IRQ status: "
308 "error=%d\n", __func__, error);
309 }
310
311 static const char *
312 dpaa2_mac_ethif_to_str(enum dpaa2_mac_eth_if eth_if)
313 {
314 switch (eth_if) {
315 case DPAA2_MAC_ETH_IF_MII:
316 return ("MII");
317 case DPAA2_MAC_ETH_IF_RMII:
318 return ("RMII");
319 case DPAA2_MAC_ETH_IF_SMII:
320 return ("SMII");
321 case DPAA2_MAC_ETH_IF_GMII:
322 return ("GMII");
323 case DPAA2_MAC_ETH_IF_RGMII:
324 return ("RGMII");
325 case DPAA2_MAC_ETH_IF_SGMII:
326 return ("SGMII");
327 case DPAA2_MAC_ETH_IF_QSGMII:
328 return ("QSGMII");
329 case DPAA2_MAC_ETH_IF_XAUI:
330 return ("XAUI");
331 case DPAA2_MAC_ETH_IF_XFI:
332 return ("XFI");
333 case DPAA2_MAC_ETH_IF_CAUI:
334 return ("CAUI");
335 case DPAA2_MAC_ETH_IF_1000BASEX:
336 return ("1000BASE-X");
337 case DPAA2_MAC_ETH_IF_USXGMII:
338 return ("USXGMII");
339 default:
340 return ("unknown");
341 }
342 }
343
344 static const char *
345 dpaa2_mac_link_type_to_str(enum dpaa2_mac_link_type link_type)
346 {
347 switch (link_type) {
348 case DPAA2_MAC_LINK_TYPE_NONE:
349 return ("NONE");
350 case DPAA2_MAC_LINK_TYPE_FIXED:
351 return ("FIXED");
352 case DPAA2_MAC_LINK_TYPE_PHY:
353 return ("PHY");
354 case DPAA2_MAC_LINK_TYPE_BACKPLANE:
355 return ("BACKPLANE");
356 default:
357 return ("unknown");
358 }
359 }
360
361 static device_method_t dpaa2_mac_methods[] = {
362 /* Device interface */
363 DEVMETHOD(device_probe, dpaa2_mac_probe),
364 DEVMETHOD(device_attach, dpaa2_mac_attach),
365 DEVMETHOD(device_detach, dpaa2_mac_detach),
366
367 DEVMETHOD_END
368 };
369
370 static driver_t dpaa2_mac_driver = {
371 "dpaa2_mac",
372 dpaa2_mac_methods,
373 sizeof(struct dpaa2_mac_softc),
374 };
375
376 DRIVER_MODULE(dpaa2_mac, dpaa2_rc, dpaa2_mac_driver, 0, 0);
Cache object: 782d33439e5316e6893d99a764056f3c
|