| /* |
| * Copyright (C) 2005 - 2015 Emulex |
| * All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License version 2 |
| * as published by the Free Software Foundation. The full GNU General |
| * Public License is included in this distribution in the file called COPYING. |
| * |
| * Contact Information: |
| * linux-drivers@emulex.com |
| * |
| * Emulex |
| * 3333 Susan Street |
| * Costa Mesa, CA 92626 |
| */ |
| |
| #include <linux/mutex.h> |
| #include <linux/list.h> |
| #include <linux/netdevice.h> |
| #include <linux/module.h> |
| |
| #include "be.h" |
| #include "be_cmds.h" |
| |
| static struct ocrdma_driver *ocrdma_drv; |
| static LIST_HEAD(be_adapter_list); |
| static DEFINE_MUTEX(be_adapter_list_lock); |
| |
| static void _be_roce_dev_add(struct be_adapter *adapter) |
| { |
| struct be_dev_info dev_info; |
| int i, num_vec; |
| struct pci_dev *pdev = adapter->pdev; |
| |
| if (!ocrdma_drv) |
| return; |
| |
| if (ocrdma_drv->be_abi_version != BE_ROCE_ABI_VERSION) { |
| dev_warn(&pdev->dev, "Cannot initialize RoCE due to ocrdma ABI mismatch\n"); |
| return; |
| } |
| |
| if (pdev->device == OC_DEVICE_ID5) { |
| /* only msix is supported on these devices */ |
| if (!msix_enabled(adapter)) |
| return; |
| /* DPP region address and length */ |
| dev_info.dpp_unmapped_addr = pci_resource_start(pdev, 2); |
| dev_info.dpp_unmapped_len = pci_resource_len(pdev, 2); |
| } else { |
| dev_info.dpp_unmapped_addr = 0; |
| dev_info.dpp_unmapped_len = 0; |
| } |
| dev_info.pdev = adapter->pdev; |
| dev_info.db = adapter->db; |
| dev_info.unmapped_db = adapter->roce_db.io_addr; |
| dev_info.db_page_size = adapter->roce_db.size; |
| dev_info.db_total_size = adapter->roce_db.total_size; |
| dev_info.netdev = adapter->netdev; |
| memcpy(dev_info.mac_addr, adapter->netdev->dev_addr, ETH_ALEN); |
| dev_info.dev_family = adapter->sli_family; |
| if (msix_enabled(adapter)) { |
| /* provide all the vectors, so that EQ creation response |
| * can decide which one to use. |
| */ |
| num_vec = adapter->num_msix_vec + adapter->num_msix_roce_vec; |
| dev_info.intr_mode = BE_INTERRUPT_MODE_MSIX; |
| dev_info.msix.num_vectors = min(num_vec, MAX_MSIX_VECTORS); |
| /* provide start index of the vector, |
| * so in case of linear usage, |
| * it can use the base as starting point. |
| */ |
| dev_info.msix.start_vector = adapter->num_evt_qs; |
| for (i = 0; i < dev_info.msix.num_vectors; i++) { |
| dev_info.msix.vector_list[i] = |
| adapter->msix_entries[i].vector; |
| } |
| } else { |
| dev_info.msix.num_vectors = 0; |
| dev_info.intr_mode = BE_INTERRUPT_MODE_INTX; |
| } |
| adapter->ocrdma_dev = ocrdma_drv->add(&dev_info); |
| } |
| |
| void be_roce_dev_add(struct be_adapter *adapter) |
| { |
| if (be_roce_supported(adapter)) { |
| INIT_LIST_HEAD(&adapter->entry); |
| mutex_lock(&be_adapter_list_lock); |
| list_add_tail(&adapter->entry, &be_adapter_list); |
| |
| /* invoke add() routine of roce driver only if |
| * valid driver registered with add method and add() is not yet |
| * invoked on a given adapter. |
| */ |
| _be_roce_dev_add(adapter); |
| mutex_unlock(&be_adapter_list_lock); |
| } |
| } |
| |
| static void _be_roce_dev_remove(struct be_adapter *adapter) |
| { |
| if (ocrdma_drv && ocrdma_drv->remove && adapter->ocrdma_dev) |
| ocrdma_drv->remove(adapter->ocrdma_dev); |
| adapter->ocrdma_dev = NULL; |
| } |
| |
| void be_roce_dev_remove(struct be_adapter *adapter) |
| { |
| if (be_roce_supported(adapter)) { |
| mutex_lock(&be_adapter_list_lock); |
| _be_roce_dev_remove(adapter); |
| list_del(&adapter->entry); |
| mutex_unlock(&be_adapter_list_lock); |
| } |
| } |
| |
| static void _be_roce_dev_open(struct be_adapter *adapter) |
| { |
| if (ocrdma_drv && adapter->ocrdma_dev && |
| ocrdma_drv->state_change_handler) |
| ocrdma_drv->state_change_handler(adapter->ocrdma_dev, |
| BE_DEV_UP); |
| } |
| |
| void be_roce_dev_open(struct be_adapter *adapter) |
| { |
| if (be_roce_supported(adapter)) { |
| mutex_lock(&be_adapter_list_lock); |
| _be_roce_dev_open(adapter); |
| mutex_unlock(&be_adapter_list_lock); |
| } |
| } |
| |
| static void _be_roce_dev_close(struct be_adapter *adapter) |
| { |
| if (ocrdma_drv && adapter->ocrdma_dev && |
| ocrdma_drv->state_change_handler) |
| ocrdma_drv->state_change_handler(adapter->ocrdma_dev, |
| BE_DEV_DOWN); |
| } |
| |
| void be_roce_dev_close(struct be_adapter *adapter) |
| { |
| if (be_roce_supported(adapter)) { |
| mutex_lock(&be_adapter_list_lock); |
| _be_roce_dev_close(adapter); |
| mutex_unlock(&be_adapter_list_lock); |
| } |
| } |
| |
| void be_roce_dev_shutdown(struct be_adapter *adapter) |
| { |
| if (be_roce_supported(adapter)) { |
| mutex_lock(&be_adapter_list_lock); |
| if (ocrdma_drv && adapter->ocrdma_dev && |
| ocrdma_drv->state_change_handler) |
| ocrdma_drv->state_change_handler(adapter->ocrdma_dev, |
| BE_DEV_SHUTDOWN); |
| mutex_unlock(&be_adapter_list_lock); |
| } |
| } |
| |
| int be_roce_register_driver(struct ocrdma_driver *drv) |
| { |
| struct be_adapter *dev; |
| |
| mutex_lock(&be_adapter_list_lock); |
| if (ocrdma_drv) { |
| mutex_unlock(&be_adapter_list_lock); |
| return -EINVAL; |
| } |
| ocrdma_drv = drv; |
| list_for_each_entry(dev, &be_adapter_list, entry) { |
| struct net_device *netdev; |
| |
| _be_roce_dev_add(dev); |
| netdev = dev->netdev; |
| if (netif_running(netdev) && netif_oper_up(netdev)) |
| _be_roce_dev_open(dev); |
| } |
| mutex_unlock(&be_adapter_list_lock); |
| return 0; |
| } |
| EXPORT_SYMBOL(be_roce_register_driver); |
| |
| void be_roce_unregister_driver(struct ocrdma_driver *drv) |
| { |
| struct be_adapter *dev; |
| |
| mutex_lock(&be_adapter_list_lock); |
| list_for_each_entry(dev, &be_adapter_list, entry) { |
| if (dev->ocrdma_dev) |
| _be_roce_dev_remove(dev); |
| } |
| ocrdma_drv = NULL; |
| mutex_unlock(&be_adapter_list_lock); |
| } |
| EXPORT_SYMBOL(be_roce_unregister_driver); |