/*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright © 2021-2022 Dmitry Salychev * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include /* * The DPAA2 MAC driver. * * For every DPAA2 MAC, there is an MC object named DPMAC, for MDIO and link * state updates. The DPMAC virtualizes the MDIO interface, so each PHY driver * may see a private interface (removing the need for synchronization in GPP on * the multiplexed MDIO hardware). */ #include #include #include #include #include #include #include #include #include #include #include #include "pcib_if.h" #include "pci_if.h" #include "dpaa2_mc.h" #include "dpaa2_ni.h" #include "dpaa2_mcp.h" #include "dpaa2_swp.h" #include "dpaa2_swp_if.h" #include "dpaa2_cmd_if.h" /* Index of the only DPMAC IRQ. */ #define DPMAC_IRQ_INDEX 0 /* DPMAC IRQ statuses. */ #define DPMAC_IRQ_LINK_CFG_REQ 0x00000001 /* change in requested link config. */ #define DPMAC_IRQ_LINK_CHANGED 0x00000002 /* link state changed */ #define DPMAC_IRQ_LINK_UP_REQ 0x00000004 /* link up request */ #define DPMAC_IRQ_LINK_DOWN_REQ 0x00000008 /* link down request */ #define DPMAC_IRQ_EP_CHANGED 0x00000010 /* DPAA2 endpoint dis/connected */ /* DPAA2 MAC resource specification. */ struct resource_spec dpaa2_mac_spec[] = { /* * DPMCP resources. * * NOTE: MC command portals (MCPs) are used to send commands to, and * receive responses from, the MC firmware. One portal per DPMAC. */ #define MCP_RES_NUM (1u) #define MCP_RID_OFF (0u) #define MCP_RID(rid) ((rid) + MCP_RID_OFF) /* --- */ { DPAA2_DEV_MCP, MCP_RID(0), RF_ACTIVE | RF_SHAREABLE | RF_OPTIONAL }, /* --- */ RESOURCE_SPEC_END }; /* Interrupt configuration routines. */ static int dpaa2_mac_setup_irq(device_t); static int dpaa2_mac_setup_msi(struct dpaa2_mac_softc *); /* Subroutines to get text representation. */ static const char *dpaa2_mac_ethif_to_str(enum dpaa2_mac_eth_if); static const char *dpaa2_mac_link_type_to_str(enum dpaa2_mac_link_type); /* Interrupt handlers */ static void dpaa2_mac_intr(void *arg); static int dpaa2_mac_probe(device_t dev) { /* DPIO device will be added by a parent resource container itself. */ device_set_desc(dev, "DPAA2 MAC"); return (BUS_PROBE_DEFAULT); } static int dpaa2_mac_attach(device_t dev) { device_t pdev = device_get_parent(dev); device_t child = dev; device_t mcp_dev; struct dpaa2_mac_softc *sc = device_get_softc(dev); struct dpaa2_devinfo *rcinfo = device_get_ivars(pdev); struct dpaa2_devinfo *dinfo = device_get_ivars(dev); struct dpaa2_devinfo *mcp_dinfo; struct dpaa2_cmd cmd; uint16_t rc_token, mac_token; int error; sc->dev = dev; memset(sc->addr, 0, ETHER_ADDR_LEN); error = bus_alloc_resources(sc->dev, dpaa2_mac_spec, sc->res); if (error) { device_printf(dev, "%s: failed to allocate resources: " "error=%d\n", __func__, error); goto err_exit; } /* Obtain MC portal. */ mcp_dev = (device_t) rman_get_start(sc->res[MCP_RID(0)]); mcp_dinfo = device_get_ivars(mcp_dev); dinfo->portal = mcp_dinfo->portal; DPAA2_CMD_INIT(&cmd); error = DPAA2_CMD_RC_OPEN(dev, child, &cmd, rcinfo->id, &rc_token); if (error) { device_printf(dev, "%s: failed to open DPRC: error=%d\n", __func__, error); goto err_exit; } error = DPAA2_CMD_MAC_OPEN(dev, child, &cmd, dinfo->id, &mac_token); if (error) { device_printf(dev, "%s: failed to open DPMAC: id=%d, error=%d\n", __func__, dinfo->id, error); goto close_rc; } error = DPAA2_CMD_MAC_GET_ATTRIBUTES(dev, child, &cmd, &sc->attr); if (error) { device_printf(dev, "%s: failed to get DPMAC attributes: id=%d, " "error=%d\n", __func__, dinfo->id, error); goto close_mac; } error = DPAA2_CMD_MAC_GET_ADDR(dev, child, &cmd, sc->addr); if (error) { device_printf(dev, "%s: failed to get physical address: " "error=%d\n", __func__, error); } if (bootverbose) { device_printf(dev, "ether %6D\n", sc->addr, ":"); device_printf(dev, "max_rate=%d, eth_if=%s, link_type=%s\n", sc->attr.max_rate, dpaa2_mac_ethif_to_str(sc->attr.eth_if), dpaa2_mac_link_type_to_str(sc->attr.link_type)); } error = dpaa2_mac_setup_irq(dev); if (error) { device_printf(dev, "%s: failed to setup IRQs: error=%d\n", __func__, error); goto close_mac; } (void)DPAA2_CMD_MAC_CLOSE(dev, child, DPAA2_CMD_TK(&cmd, mac_token)); (void)DPAA2_CMD_RC_CLOSE(dev, child, DPAA2_CMD_TK(&cmd, rc_token)); return (0); close_mac: (void)DPAA2_CMD_MAC_CLOSE(dev, child, DPAA2_CMD_TK(&cmd, mac_token)); close_rc: (void)DPAA2_CMD_RC_CLOSE(dev, child, DPAA2_CMD_TK(&cmd, rc_token)); err_exit: return (ENXIO); } static int dpaa2_mac_detach(device_t dev) { /* TBD */ return (0); } /** * @brief Configure DPMAC object to generate interrupts. */ static int dpaa2_mac_setup_irq(device_t dev) { device_t pdev = device_get_parent(dev); device_t child = dev; struct dpaa2_mac_softc *sc = device_get_softc(dev); struct dpaa2_devinfo *rcinfo = device_get_ivars(pdev); struct dpaa2_devinfo *dinfo = device_get_ivars(dev); struct dpaa2_cmd cmd; uint16_t rc_token, mac_token; uint32_t irq_mask; int error; error = dpaa2_mac_setup_msi(sc); if (error) { device_printf(dev, "%s: failed to allocate MSI\n", __func__); goto err_exit; } if ((sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid[0], RF_ACTIVE | RF_SHAREABLE)) == NULL) { device_printf(dev, "%s: failed to allocate IRQ resource\n", __func__); error = ENXIO; goto err_exit; } if (bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET | INTR_MPSAFE, NULL, dpaa2_mac_intr, sc, &sc->intr)) { device_printf(dev, "%s: failed to setup IRQ resource\n", __func__); error = ENXIO; goto err_exit; } DPAA2_CMD_INIT(&cmd); error = DPAA2_CMD_RC_OPEN(dev, child, &cmd, rcinfo->id, &rc_token); if (error) { device_printf(dev, "%s: failed to open DPRC: error=%d\n", __func__, error); goto err_exit; } error = DPAA2_CMD_MAC_OPEN(dev, child, &cmd, dinfo->id, &mac_token); if (error) { device_printf(dev, "%s: failed to open DPMAC: id=%d, error=%d\n", __func__, dinfo->id, error); goto close_rc; } irq_mask = DPMAC_IRQ_LINK_CFG_REQ | DPMAC_IRQ_LINK_CHANGED | DPMAC_IRQ_LINK_UP_REQ | DPMAC_IRQ_LINK_DOWN_REQ | DPMAC_IRQ_EP_CHANGED; error = DPAA2_CMD_MAC_SET_IRQ_MASK(dev, child, &cmd, DPMAC_IRQ_INDEX, irq_mask); if (error) { device_printf(dev, "%s: failed to set IRQ mask\n", __func__); goto close_mac; } error = DPAA2_CMD_MAC_SET_IRQ_ENABLE(dev, child, &cmd, DPMAC_IRQ_INDEX, true); if (error) { device_printf(dev, "%s: failed to enable IRQ\n", __func__); goto close_mac; } (void)DPAA2_CMD_MAC_CLOSE(dev, child, DPAA2_CMD_TK(&cmd, mac_token)); (void)DPAA2_CMD_RC_CLOSE(dev, child, DPAA2_CMD_TK(&cmd, rc_token)); return (0); close_mac: (void)DPAA2_CMD_MAC_CLOSE(dev, child, DPAA2_CMD_TK(&cmd, mac_token)); close_rc: (void)DPAA2_CMD_RC_CLOSE(dev, child, DPAA2_CMD_TK(&cmd, rc_token)); err_exit: return (error); } /** * @brief Allocate MSI interrupts for DPMAC. */ static int dpaa2_mac_setup_msi(struct dpaa2_mac_softc *sc) { int val; val = pci_msi_count(sc->dev); if (val < DPAA2_MAC_MSI_COUNT) device_printf(sc->dev, "MSI: actual=%d, expected=%d\n", val, DPAA2_MAC_MSI_COUNT); val = MIN(val, DPAA2_MAC_MSI_COUNT); if (pci_alloc_msi(sc->dev, &val) != 0) return (EINVAL); for (int i = 0; i < val; i++) sc->irq_rid[i] = i + 1; return (0); } static void dpaa2_mac_intr(void *arg) { struct dpaa2_mac_softc *sc = (struct dpaa2_mac_softc *) arg; device_t pdev = device_get_parent(sc->dev); device_t dev = sc->dev; device_t child = dev; struct dpaa2_devinfo *rcinfo = device_get_ivars(pdev); struct dpaa2_devinfo *dinfo = device_get_ivars(dev); struct dpaa2_cmd cmd; uint32_t status = ~0u; /* clear all IRQ status bits */ uint16_t rc_token, mac_token; int error; DPAA2_CMD_INIT(&cmd); error = DPAA2_CMD_RC_OPEN(dev, child, &cmd, rcinfo->id, &rc_token); if (error) { device_printf(dev, "%s: failed to open DPRC: error=%d\n", __func__, error); goto err_exit; } error = DPAA2_CMD_MAC_OPEN(dev, child, &cmd, dinfo->id, &mac_token); if (error) { device_printf(dev, "%s: failed to open DPMAC: id=%d, error=%d\n", __func__, dinfo->id, error); goto close_rc; } error = DPAA2_CMD_MAC_GET_IRQ_STATUS(dev, child, &cmd, DPMAC_IRQ_INDEX, &status); if (error) { device_printf(sc->dev, "%s: failed to obtain IRQ status: " "error=%d\n", __func__, error); } (void)DPAA2_CMD_MAC_CLOSE(dev, child, DPAA2_CMD_TK(&cmd, mac_token)); close_rc: (void)DPAA2_CMD_RC_CLOSE(dev, child, DPAA2_CMD_TK(&cmd, rc_token)); err_exit: return; } static const char * dpaa2_mac_ethif_to_str(enum dpaa2_mac_eth_if eth_if) { switch (eth_if) { case DPAA2_MAC_ETH_IF_MII: return ("MII"); case DPAA2_MAC_ETH_IF_RMII: return ("RMII"); case DPAA2_MAC_ETH_IF_SMII: return ("SMII"); case DPAA2_MAC_ETH_IF_GMII: return ("GMII"); case DPAA2_MAC_ETH_IF_RGMII: return ("RGMII"); case DPAA2_MAC_ETH_IF_SGMII: return ("SGMII"); case DPAA2_MAC_ETH_IF_QSGMII: return ("QSGMII"); case DPAA2_MAC_ETH_IF_XAUI: return ("XAUI"); case DPAA2_MAC_ETH_IF_XFI: return ("XFI"); case DPAA2_MAC_ETH_IF_CAUI: return ("CAUI"); case DPAA2_MAC_ETH_IF_1000BASEX: return ("1000BASE-X"); case DPAA2_MAC_ETH_IF_USXGMII: return ("USXGMII"); default: return ("unknown"); } } static const char * dpaa2_mac_link_type_to_str(enum dpaa2_mac_link_type link_type) { switch (link_type) { case DPAA2_MAC_LINK_TYPE_NONE: return ("NONE"); case DPAA2_MAC_LINK_TYPE_FIXED: return ("FIXED"); case DPAA2_MAC_LINK_TYPE_PHY: return ("PHY"); case DPAA2_MAC_LINK_TYPE_BACKPLANE: return ("BACKPLANE"); default: return ("unknown"); } } static device_method_t dpaa2_mac_methods[] = { /* Device interface */ DEVMETHOD(device_probe, dpaa2_mac_probe), DEVMETHOD(device_attach, dpaa2_mac_attach), DEVMETHOD(device_detach, dpaa2_mac_detach), DEVMETHOD_END }; static driver_t dpaa2_mac_driver = { "dpaa2_mac", dpaa2_mac_methods, sizeof(struct dpaa2_mac_softc), }; DRIVER_MODULE(dpaa2_mac, dpaa2_rc, dpaa2_mac_driver, 0, 0);