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