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 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 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 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 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 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 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 * 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 * 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