/* * BSD LICENSE * * Copyright (C) Cavium networks Ltd. 2016. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 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. * * Neither the name of Cavium networks 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "base/nicvf_plat.h" #include "nicvf_ethdev.h" #include "nicvf_logs.h" static inline int nicvf_atomic_write_link_status(struct rte_eth_dev *dev, struct rte_eth_link *link) { struct rte_eth_link *dst = &dev->data->dev_link; struct rte_eth_link *src = link; if (rte_atomic64_cmpset((uint64_t *)dst, *(uint64_t *)dst, *(uint64_t *)src) == 0) return -1; return 0; } static inline void nicvf_set_eth_link_status(struct nicvf *nic, struct rte_eth_link *link) { link->link_status = nic->link_up; link->link_duplex = ETH_LINK_AUTONEG; if (nic->duplex == NICVF_HALF_DUPLEX) link->link_duplex = ETH_LINK_HALF_DUPLEX; else if (nic->duplex == NICVF_FULL_DUPLEX) link->link_duplex = ETH_LINK_FULL_DUPLEX; link->link_speed = nic->speed; link->link_autoneg = ETH_LINK_SPEED_AUTONEG; } static void nicvf_interrupt(void *arg) { struct nicvf *nic = arg; if (nicvf_reg_poll_interrupts(nic) == NIC_MBOX_MSG_BGX_LINK_CHANGE) { if (nic->eth_dev->data->dev_conf.intr_conf.lsc) nicvf_set_eth_link_status(nic, &nic->eth_dev->data->dev_link); _rte_eth_dev_callback_process(nic->eth_dev, RTE_ETH_EVENT_INTR_LSC); } rte_eal_alarm_set(NICVF_INTR_POLL_INTERVAL_MS * 1000, nicvf_interrupt, nic); } static int nicvf_periodic_alarm_start(struct nicvf *nic) { return rte_eal_alarm_set(NICVF_INTR_POLL_INTERVAL_MS * 1000, nicvf_interrupt, nic); } static int nicvf_periodic_alarm_stop(struct nicvf *nic) { return rte_eal_alarm_cancel(nicvf_interrupt, nic); } /* * Return 0 means link status changed, -1 means not changed */ static int nicvf_dev_link_update(struct rte_eth_dev *dev, int wait_to_complete __rte_unused) { struct rte_eth_link link; struct nicvf *nic = nicvf_pmd_priv(dev); PMD_INIT_FUNC_TRACE(); memset(&link, 0, sizeof(link)); nicvf_set_eth_link_status(nic, &link); return nicvf_atomic_write_link_status(dev, &link); } /* Initialize and register driver with DPDK Application */ static const struct eth_dev_ops nicvf_eth_dev_ops = { .link_update = nicvf_dev_link_update, }; static int nicvf_eth_dev_init(struct rte_eth_dev *eth_dev) { int ret; struct rte_pci_device *pci_dev; struct nicvf *nic = nicvf_pmd_priv(eth_dev); PMD_INIT_FUNC_TRACE(); eth_dev->dev_ops = &nicvf_eth_dev_ops; pci_dev = eth_dev->pci_dev; rte_eth_copy_pci_info(eth_dev, pci_dev); nic->device_id = pci_dev->id.device_id; nic->vendor_id = pci_dev->id.vendor_id; nic->subsystem_device_id = pci_dev->id.subsystem_device_id; nic->subsystem_vendor_id = pci_dev->id.subsystem_vendor_id; nic->eth_dev = eth_dev; PMD_INIT_LOG(DEBUG, "nicvf: device (%x:%x) %u:%u:%u:%u", pci_dev->id.vendor_id, pci_dev->id.device_id, pci_dev->addr.domain, pci_dev->addr.bus, pci_dev->addr.devid, pci_dev->addr.function); nic->reg_base = (uintptr_t)pci_dev->mem_resource[0].addr; if (!nic->reg_base) { PMD_INIT_LOG(ERR, "Failed to map BAR0"); ret = -ENODEV; goto fail; } nicvf_disable_all_interrupts(nic); ret = nicvf_periodic_alarm_start(nic); if (ret) { PMD_INIT_LOG(ERR, "Failed to start period alarm"); goto fail; } ret = nicvf_mbox_check_pf_ready(nic); if (ret) { PMD_INIT_LOG(ERR, "Failed to get ready message from PF"); goto alarm_fail; } else { PMD_INIT_LOG(INFO, "node=%d vf=%d mode=%s sqs=%s loopback_supported=%s", nic->node, nic->vf_id, nic->tns_mode == NIC_TNS_MODE ? "tns" : "tns-bypass", nic->sqs_mode ? "true" : "false", nic->loopback_supported ? "true" : "false" ); } if (nic->sqs_mode) { PMD_INIT_LOG(INFO, "Unsupported SQS VF detected, Detaching..."); /* Detach port by returning Positive error number */ ret = ENOTSUP; goto alarm_fail; } eth_dev->data->mac_addrs = rte_zmalloc("mac_addr", ETHER_ADDR_LEN, 0); if (eth_dev->data->mac_addrs == NULL) { PMD_INIT_LOG(ERR, "Failed to allocate memory for mac addr"); ret = -ENOMEM; goto alarm_fail; } if (is_zero_ether_addr((struct ether_addr *)nic->mac_addr)) eth_random_addr(&nic->mac_addr[0]); ether_addr_copy((struct ether_addr *)nic->mac_addr, ð_dev->data->mac_addrs[0]); ret = nicvf_mbox_set_mac_addr(nic, nic->mac_addr); if (ret) { PMD_INIT_LOG(ERR, "Failed to set mac addr"); goto malloc_fail; } ret = nicvf_base_init(nic); if (ret) { PMD_INIT_LOG(ERR, "Failed to execute nicvf_base_init"); goto malloc_fail; } ret = nicvf_mbox_get_rss_size(nic); if (ret) { PMD_INIT_LOG(ERR, "Failed to get rss table size"); goto malloc_fail; } PMD_INIT_LOG(INFO, "Port %d (%x:%x) mac=%02x:%02x:%02x:%02x:%02x:%02x", eth_dev->data->port_id, nic->vendor_id, nic->device_id, nic->mac_addr[0], nic->mac_addr[1], nic->mac_addr[2], nic->mac_addr[3], nic->mac_addr[4], nic->mac_addr[5]); return 0; malloc_fail: rte_free(eth_dev->data->mac_addrs); alarm_fail: nicvf_periodic_alarm_stop(nic); fail: return ret; } static const struct rte_pci_id pci_id_nicvf_map[] = { { .class_id = RTE_CLASS_ANY_ID, .vendor_id = PCI_VENDOR_ID_CAVIUM, .device_id = PCI_DEVICE_ID_THUNDERX_PASS1_NICVF, .subsystem_vendor_id = PCI_VENDOR_ID_CAVIUM, .subsystem_device_id = PCI_SUB_DEVICE_ID_THUNDERX_PASS1_NICVF, }, { .class_id = RTE_CLASS_ANY_ID, .vendor_id = PCI_VENDOR_ID_CAVIUM, .device_id = PCI_DEVICE_ID_THUNDERX_PASS2_NICVF, .subsystem_vendor_id = PCI_VENDOR_ID_CAVIUM, .subsystem_device_id = PCI_SUB_DEVICE_ID_THUNDERX_PASS2_NICVF, }, { .vendor_id = 0, }, }; static struct eth_driver rte_nicvf_pmd = { .pci_drv = { .name = "rte_nicvf_pmd", .id_table = pci_id_nicvf_map, .drv_flags = RTE_PCI_DRV_NEED_MAPPING | RTE_PCI_DRV_INTR_LSC, }, .eth_dev_init = nicvf_eth_dev_init, .dev_private_size = sizeof(struct nicvf), }; static int rte_nicvf_pmd_init(const char *name __rte_unused, const char *para __rte_unused) { PMD_INIT_FUNC_TRACE(); PMD_INIT_LOG(INFO, "librte_pmd_thunderx nicvf version %s", THUNDERX_NICVF_PMD_VERSION); rte_eth_driver_register(&rte_nicvf_pmd); return 0; } static struct rte_driver rte_nicvf_driver = { .name = "nicvf_driver", .type = PMD_PDEV, .init = rte_nicvf_pmd_init, }; PMD_REGISTER_DRIVER(rte_nicvf_driver);