/*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (C) 2013 Emulex * All rights reserved. * * 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. * * 3. Neither the name of the Emulex Corporation nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. * * Contact Information: * freebsd-drivers@emulex.com * * Emulex * 3333 Susan Street * Costa Mesa, CA 92626 */ #include "oce_if.h" static int oce_POST(POCE_SOFTC sc); /** * @brief Function to post status * @param sc software handle to the device */ static int oce_POST(POCE_SOFTC sc) { mpu_ep_semaphore_t post_status; int tmo = 60000; /* read semaphore CSR */ post_status.dw0 = OCE_READ_CSR_MPU(sc, csr, MPU_EP_SEMAPHORE(sc)); /* if host is ready then wait for fw ready else send POST */ if (post_status.bits.stage <= POST_STAGE_AWAITING_HOST_RDY) { post_status.bits.stage = POST_STAGE_CHIP_RESET; OCE_WRITE_CSR_MPU(sc, csr, MPU_EP_SEMAPHORE(sc), post_status.dw0); } /* wait for FW ready */ for (;;) { if (--tmo == 0) break; DELAY(1000); post_status.dw0 = OCE_READ_CSR_MPU(sc, csr, MPU_EP_SEMAPHORE(sc)); if (post_status.bits.error) { device_printf(sc->dev, "POST failed: %x\n", post_status.dw0); return ENXIO; } if (post_status.bits.stage == POST_STAGE_ARMFW_READY) return 0; } device_printf(sc->dev, "POST timed out: %x\n", post_status.dw0); return ENXIO; } /** * @brief Function for hardware initialization * @param sc software handle to the device */ int oce_hw_init(POCE_SOFTC sc) { int rc = 0; rc = oce_POST(sc); if (rc) return rc; /* create the bootstrap mailbox */ rc = oce_dma_alloc(sc, sizeof(struct oce_bmbx), &sc->bsmbx, 0); if (rc) { device_printf(sc->dev, "Mailbox alloc failed\n"); return rc; } rc = oce_reset_fun(sc); if (rc) goto error; rc = oce_mbox_init(sc); if (rc) goto error; rc = oce_get_fw_version(sc); if (rc) goto error; rc = oce_get_fw_config(sc); if (rc) goto error; sc->macaddr.size_of_struct = 6; rc = oce_read_mac_addr(sc, 0, 1, MAC_ADDRESS_TYPE_NETWORK, &sc->macaddr); if (rc) goto error; if ((IS_BE(sc) && (sc->flags & OCE_FLAGS_BE3)) || IS_SH(sc)) { rc = oce_mbox_check_native_mode(sc); if (rc) goto error; } else sc->be3_native = 0; return rc; error: oce_dma_free(sc, &sc->bsmbx); device_printf(sc->dev, "Hardware initialisation failed\n"); return rc; } /** * @brief Releases the obtained pci resources * @param sc software handle to the device */ void oce_hw_pci_free(POCE_SOFTC sc) { int pci_cfg_barnum = 0; if (IS_BE(sc) && (sc->flags & OCE_FLAGS_BE2)) pci_cfg_barnum = OCE_DEV_BE2_CFG_BAR; else pci_cfg_barnum = OCE_DEV_CFG_BAR; if (sc->devcfg_res != NULL) { bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(pci_cfg_barnum), sc->devcfg_res); sc->devcfg_res = (struct resource *)NULL; sc->devcfg_btag = (bus_space_tag_t) 0; sc->devcfg_bhandle = (bus_space_handle_t)0; sc->devcfg_vhandle = (void *)NULL; } if (sc->csr_res != NULL) { bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(OCE_PCI_CSR_BAR), sc->csr_res); sc->csr_res = (struct resource *)NULL; sc->csr_btag = (bus_space_tag_t)0; sc->csr_bhandle = (bus_space_handle_t)0; sc->csr_vhandle = (void *)NULL; } if (sc->db_res != NULL) { bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(OCE_PCI_DB_BAR), sc->db_res); sc->db_res = (struct resource *)NULL; sc->db_btag = (bus_space_tag_t)0; sc->db_bhandle = (bus_space_handle_t)0; sc->db_vhandle = (void *)NULL; } } /** * @brief Function to get the PCI capabilities * @param sc software handle to the device */ static void oce_get_pci_capabilities(POCE_SOFTC sc) { uint32_t val; if (pci_find_cap(sc->dev, PCIY_PCIX, &val) == 0) { if (val != 0) sc->flags |= OCE_FLAGS_PCIX; } if (pci_find_cap(sc->dev, PCIY_EXPRESS, &val) == 0) { if (val != 0) { uint16_t link_status = pci_read_config(sc->dev, val + 0x12, 2); sc->flags |= OCE_FLAGS_PCIE; sc->pcie_link_speed = link_status & 0xf; sc->pcie_link_width = (link_status >> 4) & 0x3f; } } if (pci_find_cap(sc->dev, PCIY_MSI, &val) == 0) { if (val != 0) sc->flags |= OCE_FLAGS_MSI_CAPABLE; } if (pci_find_cap(sc->dev, PCIY_MSIX, &val) == 0) { if (val != 0) { val = pci_msix_count(sc->dev); sc->flags |= OCE_FLAGS_MSIX_CAPABLE; } } } /** * @brief Allocate PCI resources. * * @param sc software handle to the device * @returns 0 if successful, or error */ int oce_hw_pci_alloc(POCE_SOFTC sc) { int rr, pci_cfg_barnum = 0; pci_sli_intf_t intf; pci_enable_busmaster(sc->dev); oce_get_pci_capabilities(sc); sc->fn = pci_get_function(sc->dev); /* setup the device config region */ if (IS_BE(sc) && (sc->flags & OCE_FLAGS_BE2)) pci_cfg_barnum = OCE_DEV_BE2_CFG_BAR; else pci_cfg_barnum = OCE_DEV_CFG_BAR; rr = PCIR_BAR(pci_cfg_barnum); if (IS_BE(sc) || IS_SH(sc)) sc->devcfg_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY, &rr, RF_ACTIVE|RF_SHAREABLE); else sc->devcfg_res = bus_alloc_resource_anywhere(sc->dev, SYS_RES_MEMORY, &rr, 32768, RF_ACTIVE|RF_SHAREABLE); if (!sc->devcfg_res) goto error; sc->devcfg_btag = rman_get_bustag(sc->devcfg_res); sc->devcfg_bhandle = rman_get_bushandle(sc->devcfg_res); sc->devcfg_vhandle = rman_get_virtual(sc->devcfg_res); /* Read the SLI_INTF register and determine whether we * can use this port and its features */ intf.dw0 = pci_read_config((sc)->dev,OCE_INTF_REG_OFFSET,4); if (intf.bits.sli_valid != OCE_INTF_VALID_SIG) goto error; if (intf.bits.sli_rev != OCE_INTF_SLI_REV4) { device_printf(sc->dev, "Adapter doesnt support SLI4\n"); goto error; } if (intf.bits.sli_if_type == OCE_INTF_IF_TYPE_1) sc->flags |= OCE_FLAGS_MBOX_ENDIAN_RQD; if (intf.bits.sli_hint1 == OCE_INTF_FUNC_RESET_REQD) sc->flags |= OCE_FLAGS_FUNCRESET_RQD; if (intf.bits.sli_func_type == OCE_INTF_VIRT_FUNC) sc->flags |= OCE_FLAGS_VIRTUAL_PORT; /* Lancer has one BAR (CFG) but BE3 has three (CFG, CSR, DB) */ if (IS_BE(sc) || IS_SH(sc)) { /* set up CSR region */ rr = PCIR_BAR(OCE_PCI_CSR_BAR); sc->csr_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY, &rr, RF_ACTIVE|RF_SHAREABLE); if (!sc->csr_res) goto error; sc->csr_btag = rman_get_bustag(sc->csr_res); sc->csr_bhandle = rman_get_bushandle(sc->csr_res); sc->csr_vhandle = rman_get_virtual(sc->csr_res); /* set up DB doorbell region */ rr = PCIR_BAR(OCE_PCI_DB_BAR); sc->db_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY, &rr, RF_ACTIVE|RF_SHAREABLE); if (!sc->db_res) goto error; sc->db_btag = rman_get_bustag(sc->db_res); sc->db_bhandle = rman_get_bushandle(sc->db_res); sc->db_vhandle = rman_get_virtual(sc->db_res); } return 0; error: oce_hw_pci_free(sc); return ENXIO; } /** * @brief Function for device shutdown * @param sc software handle to the device * @returns 0 on success, error otherwise */ void oce_hw_shutdown(POCE_SOFTC sc) { oce_stats_free(sc); /* disable hardware interrupts */ oce_hw_intr_disable(sc); #if defined(INET6) || defined(INET) /* Free LRO resources */ oce_free_lro(sc); #endif /* Release queue*/ oce_queue_release_all(sc); /*Delete Network Interface*/ oce_delete_nw_interface(sc); /* After fw clean we dont send any cmds to fw.*/ oce_fw_clean(sc); /* release intr resources */ oce_intr_free(sc); /* release PCI resources */ oce_hw_pci_free(sc); /* free mbox specific resources */ LOCK_DESTROY(&sc->bmbx_lock); LOCK_DESTROY(&sc->dev_lock); oce_dma_free(sc, &sc->bsmbx); } /** * @brief Function for creating nw interface. * @param sc software handle to the device * @returns 0 on success, error otherwise */ int oce_create_nw_interface(POCE_SOFTC sc) { int rc; uint32_t capab_flags; uint32_t capab_en_flags; /* interface capabilities to give device when creating interface */ capab_flags = OCE_CAPAB_FLAGS; /* capabilities to enable by default (others set dynamically) */ capab_en_flags = OCE_CAPAB_ENABLE; if (IS_XE201(sc)) { /* LANCER A0 workaround */ capab_en_flags &= ~MBX_RX_IFACE_FLAGS_PASS_L3L4_ERR; capab_flags &= ~MBX_RX_IFACE_FLAGS_PASS_L3L4_ERR; } if (IS_SH(sc) || IS_XE201(sc)) capab_flags |= MBX_RX_IFACE_FLAGS_MULTICAST; if (sc->enable_hwlro) { capab_flags |= MBX_RX_IFACE_FLAGS_LRO; capab_en_flags |= MBX_RX_IFACE_FLAGS_LRO; } /* enable capabilities controlled via driver startup parameters */ if (is_rss_enabled(sc)) capab_en_flags |= MBX_RX_IFACE_FLAGS_RSS; else { capab_en_flags &= ~MBX_RX_IFACE_FLAGS_RSS; capab_flags &= ~MBX_RX_IFACE_FLAGS_RSS; } rc = oce_if_create(sc, capab_flags, capab_en_flags, 0, &sc->macaddr.mac_addr[0], &sc->if_id); if (rc) return rc; atomic_inc_32(&sc->nifs); sc->if_cap_flags = capab_en_flags; /* set default flow control */ rc = oce_set_flow_control(sc, sc->flow_control); if (rc) goto error; rc = oce_rxf_set_promiscuous(sc, sc->promisc); if (rc) goto error; return rc; error: oce_delete_nw_interface(sc); return rc; } /** * @brief Function to delete a nw interface. * @param sc software handle to the device */ void oce_delete_nw_interface(POCE_SOFTC sc) { /* currently only single interface is implmeneted */ if (sc->nifs > 0) { oce_if_del(sc, sc->if_id); atomic_dec_32(&sc->nifs); } } /** * @brief Soft reset. * @param sc software handle to the device * @returns 0 on success, error otherwise */ int oce_pci_soft_reset(POCE_SOFTC sc) { int rc; mpu_ep_control_t ctrl; ctrl.dw0 = OCE_READ_CSR_MPU(sc, csr, MPU_EP_CONTROL); ctrl.bits.cpu_reset = 1; OCE_WRITE_CSR_MPU(sc, csr, MPU_EP_CONTROL, ctrl.dw0); DELAY(50); rc=oce_POST(sc); return rc; } /** * @brief Function for hardware start * @param sc software handle to the device * @returns 0 on success, error otherwise */ int oce_hw_start(POCE_SOFTC sc) { struct link_status link = { 0 }; int rc = 0; rc = oce_get_link_status(sc, &link); if (rc) return 1; if (link.logical_link_status == NTWK_LOGICAL_LINK_UP) { sc->link_status = NTWK_LOGICAL_LINK_UP; if_link_state_change(sc->ifp, LINK_STATE_UP); } else { sc->link_status = NTWK_LOGICAL_LINK_DOWN; if_link_state_change(sc->ifp, LINK_STATE_DOWN); } sc->link_speed = link.phys_port_speed; sc->qos_link_speed = (uint32_t )link.qos_link_speed * 10; rc = oce_start_mq(sc->mq); /* we need to get MCC aync events. So enable intrs and arm first EQ, Other EQs will be armed after interface is UP */ oce_hw_intr_enable(sc); oce_arm_eq(sc, sc->eq[0]->eq_id, 0, TRUE, FALSE); /* Send first mcc cmd and after that we get gracious MCC notifications from FW */ oce_first_mcc_cmd(sc); return rc; } /** * @brief Function for hardware enable interupts. * @param sc software handle to the device */ void oce_hw_intr_enable(POCE_SOFTC sc) { uint32_t reg; reg = OCE_READ_REG32(sc, devcfg, PCICFG_INTR_CTRL); reg |= HOSTINTR_MASK; OCE_WRITE_REG32(sc, devcfg, PCICFG_INTR_CTRL, reg); } /** * @brief Function for hardware disable interupts * @param sc software handle to the device */ void oce_hw_intr_disable(POCE_SOFTC sc) { uint32_t reg; reg = OCE_READ_REG32(sc, devcfg, PCICFG_INTR_CTRL); reg &= ~HOSTINTR_MASK; OCE_WRITE_REG32(sc, devcfg, PCICFG_INTR_CTRL, reg); } static u_int oce_copy_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt) { struct mbx_set_common_iface_multicast *req = arg; if (req->params.req.num_mac == OCE_MAX_MC_FILTER_SIZE) return (0); bcopy(LLADDR(sdl), &req->params.req.mac[req->params.req.num_mac++], ETHER_ADDR_LEN); return (1); } /** * @brief Function for hardware update multicast filter * @param sc software handle to the device */ int oce_hw_update_multicast(POCE_SOFTC sc) { if_t ifp = sc->ifp; struct mbx_set_common_iface_multicast *req = NULL; OCE_DMA_MEM dma; int rc = 0; /* Allocate DMA mem*/ if (oce_dma_alloc(sc, sizeof(struct mbx_set_common_iface_multicast), &dma, 0)) return ENOMEM; req = OCE_DMAPTR(&dma, struct mbx_set_common_iface_multicast); bzero(req, sizeof(struct mbx_set_common_iface_multicast)); if_foreach_llmaddr(ifp, oce_copy_maddr, req); if (req->params.req.num_mac == OCE_MAX_MC_FILTER_SIZE) { /*More multicast addresses than our hardware table So Enable multicast promiscus in our hardware to accept all multicat packets */ req->params.req.promiscuous = 1; } req->params.req.if_id = sc->if_id; rc = oce_update_multicast(sc, &dma); oce_dma_free(sc, &dma); return rc; }