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); 377