1
0
mirror of https://github.com/corundum/corundum.git synced 2025-01-16 08:12:53 +08:00
corundum/modules/mqnic/mqnic_main.c
Joachim Foerster 0d2e794b74 modules/mqnic: Add link status monitoring
This solution is based on the assumption that, if there are multiple
(mqnic-)ports per (mqnic-)interface, the single netdev, which is currently
associated with one (mqnic-)interface, is assumed to be up, when all ports' TX
and RX status bits are asserted. As soon as one of these bits is deasserted for
any of the involved ports the netdev is assumed to be down.

Module parameter "link_status_poll" specifies the polling interval in
milliseconds. Setting it to 0, disables any form of monitoring. By default we
check once per second, which is a totally arbitrary choice - no special
reasoning.

Signed-off-by: Joachim Foerster <joachim.foerster@missinglinkelectronics.com>
2022-05-23 13:59:46 -07:00

888 lines
24 KiB
C

// SPDX-License-Identifier: BSD-2-Clause-Views
/*
* Copyright 2019-2021, The Regents of the University of California.
* 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.
*
* 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.
*
* The views and conclusions contained in the software and documentation
* are those of the authors and should not be interpreted as representing
* official policies, either expressed or implied, of The Regents of the
* University of California.
*/
#include "mqnic.h"
#include <linux/module.h>
#include <linux/version.h>
#include <linux/delay.h>
#include <linux/rtc.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)
#include <linux/pci-aspm.h>
#endif
MODULE_DESCRIPTION("mqnic driver");
MODULE_AUTHOR("Alex Forencich");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_VERSION(DRIVER_VERSION);
unsigned int mqnic_num_ev_queue_entries = 1024;
unsigned int mqnic_num_tx_queue_entries = 1024;
unsigned int mqnic_num_rx_queue_entries = 1024;
module_param_named(num_ev_queue_entries, mqnic_num_ev_queue_entries, uint, 0444);
MODULE_PARM_DESC(num_ev_queue_entries, "number of entries to allocate per event queue (default: 1024)");
module_param_named(num_tx_queue_entries, mqnic_num_tx_queue_entries, uint, 0444);
MODULE_PARM_DESC(num_tx_queue_entries, "number of entries to allocate per transmit queue (default: 1024)");
module_param_named(num_rx_queue_entries, mqnic_num_rx_queue_entries, uint, 0444);
MODULE_PARM_DESC(num_rx_queue_entries, "number of entries to allocate per receive queue (default: 1024)");
unsigned int mqnic_link_status_poll = MQNIC_LINK_STATUS_POLL_MS;
module_param_named(link_status_poll, mqnic_link_status_poll, uint, 0444);
MODULE_PARM_DESC(link_status_poll,
"link status polling interval, in ms (default: 1000; 0 to turn off)");
#ifdef CONFIG_PCI
static const struct pci_device_id mqnic_pci_id_table[] = {
{PCI_DEVICE(0x1234, 0x1001)},
{PCI_DEVICE(0x5543, 0x1001)},
{0 /* end */ }
};
MODULE_DEVICE_TABLE(pci, mqnic_pci_id_table);
#endif
#ifdef CONFIG_OF
static struct of_device_id mqnic_of_id_table[] = {
{ .compatible = "corundum,mqnic" },
{ },
};
MODULE_DEVICE_TABLE(of, mqnic_of_id_table);
#endif
static LIST_HEAD(mqnic_devices);
static DEFINE_SPINLOCK(mqnic_devices_lock);
static unsigned int mqnic_get_free_id(void)
{
struct mqnic_dev *mqnic;
unsigned int id = 0;
bool available = false;
while (!available) {
available = true;
list_for_each_entry(mqnic, &mqnic_devices, dev_list_node) {
if (mqnic->id == id) {
available = false;
id++;
break;
}
}
}
return id;
}
static void mqnic_assign_id(struct mqnic_dev *mqnic)
{
spin_lock(&mqnic_devices_lock);
mqnic->id = mqnic_get_free_id();
list_add_tail(&mqnic->dev_list_node, &mqnic_devices);
spin_unlock(&mqnic_devices_lock);
snprintf(mqnic->name, sizeof(mqnic->name), DRIVER_NAME "%d", mqnic->id);
}
static void mqnic_free_id(struct mqnic_dev *mqnic)
{
spin_lock(&mqnic_devices_lock);
list_del(&mqnic->dev_list_node);
spin_unlock(&mqnic_devices_lock);
}
static int mqnic_common_setdma(struct mqnic_dev *mqnic)
{
int ret;
struct device *dev = mqnic->dev;
// Set mask
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
if (ret) {
dev_warn(dev, "Warning: failed to set 64 bit PCI DMA mask");
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
if (ret) {
dev_err(dev, "Failed to set PCI DMA mask");
return ret;
}
}
// Set max segment size
dma_set_max_seg_size(dev, DMA_BIT_MASK(32));
return ret;
}
#ifdef CONFIG_OF
static int mqnic_platform_get_mac_address(struct mqnic_dev *mqnic)
{
int ret;
struct device *dev = mqnic->dev;
char mac_base[ETH_ALEN];
struct device_node *np;
u32 inc_idx;
u32 inc;
int k;
/* NOTE: Not being able to get a (base) MAC address shall not be an
* error to fail on intentionally. Thus we are warning, only.
*/
ret = eth_platform_get_mac_address(dev, mac_base);
if (ret) {
dev_warn(dev, "Unable to get MAC address\n");
return 0;
}
np = mqnic->dev->of_node;
if (!np)
return 0;
if (of_property_read_u32(np, MQNIC_PROP_MAC_ADDR_INC_BYTE, &inc_idx))
inc_idx = 5;
if ((inc_idx < 3) || (inc_idx > 5)) {
dev_err(dev, "Invalid property \"" MQNIC_PROP_MAC_ADDR_INC_BYTE "\"\n");
return -EINVAL;
}
ret = of_property_read_u32(np, MQNIC_PROP_MAC_ADDR_INC, &inc);
if (ret == -EINVAL) {
inc = 0;
} else if (ret) {
dev_err(dev, "Invalid property \"" MQNIC_PROP_MAC_ADDR_INC "\"\n");
return ret;
}
if (of_property_read_bool(np, MQNIC_PROP_MAC_ADDR_LOCAL))
mac_base[0] |= BIT(1);
mqnic->mac_count = mqnic->if_count;
for (k = 0; k < mqnic->mac_count; k++) {
memcpy(mqnic->mac_list[k], mac_base, ETH_ALEN);
mqnic->mac_list[k][inc_idx] += inc + k;
}
return 0;
}
static void mqnic_platform_module_eeprom_put(struct mqnic_dev *mqnic)
{
int k;
for (k = 0; k < mqnic->if_count; k++)
if (mqnic->mod_i2c_client)
put_device(&mqnic->mod_i2c_client[k]->dev);
}
static int mqnic_platform_module_eeprom_get(struct mqnic_dev *mqnic)
{
int ret;
struct device *dev = mqnic->dev;
int k;
ret = 0;
if (!dev->of_node)
return 0;
for (k = 0; k < mqnic->if_count; k++) {
struct device_node *np;
struct i2c_client *cl;
/* NOTE: Not being able to get a phandle for module EEPROM shall
* not be an error to fail on intentionally. Thus we are
* warning, only.
*/
np = of_parse_phandle(dev->of_node, MQNIC_PROP_MODULE_EEPROM, k);
if (!np) {
dev_warn(dev, "Missing phandle to module EEPROM for interface %d\n", k);
continue;
}
cl = of_find_i2c_device_by_node(np);
if (!cl) {
ret = -ENOENT;
dev_err(dev, "Failed to find I2C device for module of interface %d\n", k);
of_node_put(np);
break;
} else {
mqnic->mod_i2c_client[k] = cl;
mqnic->mod_i2c_client_count++;
}
of_node_put(np);
}
if (ret)
mqnic_platform_module_eeprom_put(mqnic);
return ret;
}
#endif
static void mqnic_common_remove(struct mqnic_dev *mqnic);
#ifdef CONFIG_AUXILIARY_BUS
static void mqnic_adev_release(struct device *dev)
{
struct mqnic_adev *mqnic_adev = container_of(dev, struct mqnic_adev, adev.dev);
if (mqnic_adev->ptr)
*mqnic_adev->ptr = NULL;
kfree(mqnic_adev);
}
#endif
static int mqnic_common_probe(struct mqnic_dev *mqnic)
{
int ret = 0;
struct device *dev = mqnic->dev;
struct mqnic_reg_block *rb;
struct rtc_time tm;
int k = 0, l = 0;
// Enumerate registers
mqnic->rb_list = mqnic_enumerate_reg_block_list(mqnic->hw_addr, 0, mqnic->hw_regs_size);
if (!mqnic->rb_list) {
dev_err(dev, "Failed to enumerate blocks");
return -EIO;
}
dev_info(dev, "Device-level register blocks:");
for (rb = mqnic->rb_list; rb->regs; rb++)
dev_info(dev, " type 0x%08x (v %d.%d.%d.%d)", rb->type, rb->version >> 24,
(rb->version >> 16) & 0xff, (rb->version >> 8) & 0xff, rb->version & 0xff);
// Read ID registers
mqnic->fw_id_rb = mqnic_find_reg_block(mqnic->rb_list, MQNIC_RB_FW_ID_TYPE, MQNIC_RB_FW_ID_VER, 0);
if (!mqnic->fw_id_rb) {
ret = -EIO;
dev_err(dev, "Error: FW ID block not found");
goto fail_rb_init;
}
mqnic->fpga_id = ioread32(mqnic->fw_id_rb->regs + MQNIC_RB_FW_ID_REG_FPGA_ID);
mqnic->fw_id = ioread32(mqnic->fw_id_rb->regs + MQNIC_RB_FW_ID_REG_FW_ID);
mqnic->fw_ver = ioread32(mqnic->fw_id_rb->regs + MQNIC_RB_FW_ID_REG_FW_VER);
mqnic->board_id = ioread32(mqnic->fw_id_rb->regs + MQNIC_RB_FW_ID_REG_BOARD_ID);
mqnic->board_ver = ioread32(mqnic->fw_id_rb->regs + MQNIC_RB_FW_ID_REG_BOARD_VER);
mqnic->build_date = ioread32(mqnic->fw_id_rb->regs + MQNIC_RB_FW_ID_REG_BUILD_DATE);
mqnic->git_hash = ioread32(mqnic->fw_id_rb->regs + MQNIC_RB_FW_ID_REG_GIT_HASH);
mqnic->rel_info = ioread32(mqnic->fw_id_rb->regs + MQNIC_RB_FW_ID_REG_REL_INFO);
rtc_time64_to_tm(mqnic->build_date, &tm);
dev_info(dev, "FPGA ID: 0x%08x", mqnic->fpga_id);
dev_info(dev, "FW ID: 0x%08x", mqnic->fw_id);
dev_info(dev, "FW version: %d.%d.%d.%d", mqnic->fw_ver >> 24,
(mqnic->fw_ver >> 16) & 0xff,
(mqnic->fw_ver >> 8) & 0xff,
mqnic->fw_ver & 0xff);
dev_info(dev, "Board ID: 0x%08x", mqnic->board_id);
dev_info(dev, "Board version: %d.%d.%d.%d", mqnic->board_ver >> 24,
(mqnic->board_ver >> 16) & 0xff,
(mqnic->board_ver >> 8) & 0xff,
mqnic->board_ver & 0xff);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
dev_info(dev, "Build date: %ptRd %ptRt UTC (raw: 0x%08x)", &tm, &tm, mqnic->build_date);
#else
dev_info(dev, "Build date: %04d-%02d-%02d %02d:%02d:%02d UTC (raw: 0x%08x)",
tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec, mqnic->build_date);
#endif
dev_info(dev, "Git hash: %08x", mqnic->git_hash);
dev_info(dev, "Release info: %08x", mqnic->rel_info);
rb = mqnic_find_reg_block(mqnic->rb_list, MQNIC_RB_APP_INFO_TYPE, MQNIC_RB_APP_INFO_VER, 0);
if (rb) {
mqnic->app_id = ioread32(rb->regs + MQNIC_RB_APP_INFO_REG_ID);
dev_info(dev, "Application ID: 0x%08x", mqnic->app_id);
}
mqnic->phc_rb = mqnic_find_reg_block(mqnic->rb_list, MQNIC_RB_PHC_TYPE, MQNIC_RB_PHC_VER, 0);
// Enumerate interfaces
mqnic->if_rb = mqnic_find_reg_block(mqnic->rb_list, MQNIC_RB_IF_TYPE, MQNIC_RB_IF_VER, 0);
if (!mqnic->if_rb) {
ret = -EIO;
dev_err(dev, "Error: interface block not found");
goto fail_rb_init;
}
mqnic->if_offset = ioread32(mqnic->if_rb->regs + MQNIC_RB_IF_REG_OFFSET);
mqnic->if_count = ioread32(mqnic->if_rb->regs + MQNIC_RB_IF_REG_COUNT);
mqnic->if_stride = ioread32(mqnic->if_rb->regs + MQNIC_RB_IF_REG_STRIDE);
mqnic->if_csr_offset = ioread32(mqnic->if_rb->regs + MQNIC_RB_IF_REG_CSR_OFFSET);
dev_info(dev, "IF offset: 0x%08x", mqnic->if_offset);
dev_info(dev, "IF count: %d", mqnic->if_count);
dev_info(dev, "IF stride: 0x%08x", mqnic->if_stride);
dev_info(dev, "IF CSR offset: 0x%08x", mqnic->if_csr_offset);
// check BAR size
if (mqnic->if_count * mqnic->if_stride > mqnic->hw_regs_size) {
ret = -EIO;
dev_err(dev, "Invalid BAR configuration (%d IF * 0x%x > 0x%llx)",
mqnic->if_count, mqnic->if_stride, mqnic->hw_regs_size);
goto fail_bar_size;
}
if (mqnic->pfdev) {
#ifdef CONFIG_OF
ret = mqnic_platform_get_mac_address(mqnic);
if (ret)
goto fail_board;
ret = mqnic_platform_module_eeprom_get(mqnic);
if (ret)
goto fail_board;
#endif
} else {
// Board-specific init
ret = mqnic_board_init(mqnic);
if (ret) {
dev_err(dev, "Failed to initialize board");
goto fail_board;
}
}
// register PHC
if (mqnic->phc_rb)
mqnic_register_phc(mqnic);
mutex_init(&mqnic->state_lock);
// Set up interfaces
mqnic->dev_port_max = 0;
mqnic->dev_port_limit = MQNIC_MAX_IF;
mqnic->if_count = min_t(u32, mqnic->if_count, MQNIC_MAX_IF);
for (k = 0; k < mqnic->if_count; k++) {
dev_info(dev, "Creating interface %d", k);
ret = mqnic_create_interface(mqnic, &mqnic->interface[k], k, mqnic->hw_addr + k * mqnic->if_stride);
if (ret) {
dev_err(dev, "Failed to create interface: %d", ret);
goto fail_create_if;
}
mqnic->dev_port_max = mqnic->interface[k]->dev_port_max;
}
// pass module I2C clients to interface instances
for (k = 0; k < mqnic->if_count; k++) {
struct mqnic_if *interface = mqnic->interface[k];
interface->mod_i2c_client = mqnic->mod_i2c_client[k];
for (l = 0; l < interface->ndev_count; l++) {
struct mqnic_priv *priv = netdev_priv(interface->ndev[l]);
priv->mod_i2c_client = mqnic->mod_i2c_client[k];
}
}
fail_create_if:
mqnic->misc_dev.minor = MISC_DYNAMIC_MINOR;
mqnic->misc_dev.name = mqnic->name;
mqnic->misc_dev.fops = &mqnic_fops;
mqnic->misc_dev.parent = dev;
ret = misc_register(&mqnic->misc_dev);
if (ret) {
mqnic->misc_dev.this_device = NULL;
dev_err(dev, "misc_register failed: %d\n", ret);
goto fail_miscdev;
}
dev_info(dev, "Registered device %s", mqnic->name);
#ifdef CONFIG_AUXILIARY_BUS
if (mqnic->app_id) {
mqnic->app_adev = kzalloc(sizeof(*mqnic->app_adev), GFP_KERNEL);
if (!mqnic->app_adev) {
ret = -ENOMEM;
goto fail_adev;
}
snprintf(mqnic->app_adev->name, sizeof(mqnic->app_adev->name), "app_%08x", mqnic->app_id);
mqnic->app_adev->adev.id = mqnic->id;
mqnic->app_adev->adev.name = mqnic->app_adev->name;
mqnic->app_adev->adev.dev.parent = dev;
mqnic->app_adev->adev.dev.release = mqnic_adev_release;
mqnic->app_adev->mdev = mqnic;
mqnic->app_adev->ptr = &mqnic->app_adev;
ret = auxiliary_device_init(&mqnic->app_adev->adev);
if (ret) {
kfree(mqnic->app_adev);
mqnic->app_adev = NULL;
goto fail_adev;
}
ret = auxiliary_device_add(&mqnic->app_adev->adev);
if (ret) {
auxiliary_device_uninit(&mqnic->app_adev->adev);
mqnic->app_adev = NULL;
goto fail_adev;
}
dev_info(dev, "Registered auxiliary bus device " DRIVER_NAME ".%s.%d",
mqnic->app_adev->adev.name, mqnic->app_adev->adev.id);
}
#endif
// probe complete
return 0;
// error handling
#ifdef CONFIG_AUXILIARY_BUS
fail_adev:
#endif
fail_miscdev:
fail_board:
fail_bar_size:
fail_rb_init:
mqnic_common_remove(mqnic);
return ret;
}
static void mqnic_common_remove(struct mqnic_dev *mqnic)
{
int k = 0;
#ifdef CONFIG_AUXILIARY_BUS
if (mqnic->app_adev) {
auxiliary_device_delete(&mqnic->app_adev->adev);
auxiliary_device_uninit(&mqnic->app_adev->adev);
}
#endif
if (mqnic->misc_dev.this_device)
misc_deregister(&mqnic->misc_dev);
for (k = 0; k < ARRAY_SIZE(mqnic->interface); k++)
if (mqnic->interface[k])
mqnic_destroy_interface(&mqnic->interface[k]);
mqnic_unregister_phc(mqnic);
if (mqnic->pfdev) {
#ifdef CONFIG_OF
mqnic_platform_module_eeprom_put(mqnic);
#endif
} else {
mqnic_board_deinit(mqnic);
}
if (mqnic->rb_list)
mqnic_free_reg_block_list(mqnic->rb_list);
}
#ifdef CONFIG_PCI
static int mqnic_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
int ret = 0;
struct mqnic_dev *mqnic;
struct device *dev = &pdev->dev;
struct pci_dev *bridge = pci_upstream_bridge(pdev);
dev_info(dev, DRIVER_NAME " PCI probe");
dev_info(dev, " Vendor: 0x%04x", pdev->vendor);
dev_info(dev, " Device: 0x%04x", pdev->device);
dev_info(dev, " Subsystem vendor: 0x%04x", pdev->subsystem_vendor);
dev_info(dev, " Subsystem device: 0x%04x", pdev->subsystem_device);
dev_info(dev, " Class: 0x%06x", pdev->class);
dev_info(dev, " PCI ID: %04x:%02x:%02x.%d", pci_domain_nr(pdev->bus),
pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
if (pdev->pcie_cap) {
u16 devctl;
u32 lnkcap;
u16 lnksta;
pci_read_config_word(pdev, pdev->pcie_cap + PCI_EXP_DEVCTL, &devctl);
pci_read_config_dword(pdev, pdev->pcie_cap + PCI_EXP_LNKCAP, &lnkcap);
pci_read_config_word(pdev, pdev->pcie_cap + PCI_EXP_LNKSTA, &lnksta);
dev_info(dev, " Max payload size: %d bytes",
128 << ((devctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5));
dev_info(dev, " Max read request size: %d bytes",
128 << ((devctl & PCI_EXP_DEVCTL_READRQ) >> 12));
dev_info(dev, " Link capability: gen %d x%d",
lnkcap & PCI_EXP_LNKCAP_SLS, (lnkcap & PCI_EXP_LNKCAP_MLW) >> 4);
dev_info(dev, " Link status: gen %d x%d",
lnksta & PCI_EXP_LNKSTA_CLS, (lnksta & PCI_EXP_LNKSTA_NLW) >> 4);
dev_info(dev, " Relaxed ordering: %s",
devctl & PCI_EXP_DEVCTL_RELAX_EN ? "enabled" : "disabled");
dev_info(dev, " Phantom functions: %s",
devctl & PCI_EXP_DEVCTL_PHANTOM ? "enabled" : "disabled");
dev_info(dev, " Extended tags: %s",
devctl & PCI_EXP_DEVCTL_EXT_TAG ? "enabled" : "disabled");
dev_info(dev, " No snoop: %s",
devctl & PCI_EXP_DEVCTL_NOSNOOP_EN ? "enabled" : "disabled");
}
#ifdef CONFIG_NUMA
dev_info(dev, " NUMA node: %d", pdev->dev.numa_node);
#endif
if (bridge) {
dev_info(dev, " PCI ID (bridge): %04x:%02x:%02x.%d", pci_domain_nr(bridge->bus),
bridge->bus->number, PCI_SLOT(bridge->devfn), PCI_FUNC(bridge->devfn));
}
if (bridge && bridge->pcie_cap) {
u32 lnkcap;
u16 lnksta;
pci_read_config_dword(bridge, bridge->pcie_cap + PCI_EXP_LNKCAP, &lnkcap);
pci_read_config_word(bridge, bridge->pcie_cap + PCI_EXP_LNKSTA, &lnksta);
dev_info(dev, " Link capability (bridge): gen %d x%d",
lnkcap & PCI_EXP_LNKCAP_SLS, (lnkcap & PCI_EXP_LNKCAP_MLW) >> 4);
dev_info(dev, " Link status (bridge): gen %d x%d",
lnksta & PCI_EXP_LNKSTA_CLS, (lnksta & PCI_EXP_LNKSTA_NLW) >> 4);
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
pcie_print_link_status(pdev);
#endif
mqnic = devm_kzalloc(dev, sizeof(*mqnic), GFP_KERNEL);
if (!mqnic)
return -ENOMEM;
mqnic->dev = dev;
mqnic->pdev = pdev;
pci_set_drvdata(pdev, mqnic);
// assign ID and add to list
mqnic_assign_id(mqnic);
// Disable ASPM
pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S |
PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_CLKPM);
// Enable device
ret = pci_enable_device_mem(pdev);
if (ret) {
dev_err(dev, "Failed to enable PCI device");
goto fail_enable_device;
}
// Set DMA properties
ret = mqnic_common_setdma(mqnic);
if (ret)
goto fail_regions;
// Reserve regions
ret = pci_request_regions(pdev, DRIVER_NAME);
if (ret) {
dev_err(dev, "Failed to reserve regions");
goto fail_regions;
}
mqnic->hw_regs_size = pci_resource_len(pdev, 0);
mqnic->hw_regs_phys = pci_resource_start(pdev, 0);
mqnic->app_hw_regs_size = pci_resource_len(pdev, 2);
mqnic->app_hw_regs_phys = pci_resource_start(pdev, 2);
mqnic->ram_hw_regs_size = pci_resource_len(pdev, 4);
mqnic->ram_hw_regs_phys = pci_resource_start(pdev, 4);
// Map BARs
dev_info(dev, "Control BAR size: %llu", mqnic->hw_regs_size);
mqnic->hw_addr = pci_ioremap_bar(pdev, 0);
if (!mqnic->hw_addr) {
ret = -ENOMEM;
dev_err(dev, "Failed to map control BAR");
goto fail_map_bars;
}
if (mqnic->app_hw_regs_size) {
dev_info(dev, "Application BAR size: %llu", mqnic->app_hw_regs_size);
mqnic->app_hw_addr = pci_ioremap_bar(pdev, 2);
if (!mqnic->app_hw_addr) {
ret = -ENOMEM;
dev_err(dev, "Failed to map application BAR");
goto fail_map_bars;
}
}
if (mqnic->ram_hw_regs_size) {
dev_info(dev, "RAM BAR size: %llu", mqnic->ram_hw_regs_size);
mqnic->ram_hw_addr = pci_ioremap_bar(pdev, 4);
if (!mqnic->ram_hw_addr) {
ret = -ENOMEM;
dev_err(dev, "Failed to map RAM BAR");
goto fail_map_bars;
}
}
// Check if device needs to be reset
if (ioread32(mqnic->hw_addr+4) == 0xffffffff) {
ret = -EIO;
dev_err(dev, "Device needs to be reset");
goto fail_reset;
}
// Set up interrupts
ret = mqnic_irq_init_pcie(mqnic);
if (ret) {
dev_err(dev, "Failed to set up interrupts");
goto fail_init_irq;
}
// Enable bus mastering for DMA
pci_set_master(pdev);
// Common init
ret = mqnic_common_probe(mqnic);
if (ret)
goto fail_common;
// probe complete
return 0;
// error handling
fail_common:
pci_clear_master(pdev);
mqnic_irq_deinit_pcie(mqnic);
fail_reset:
fail_init_irq:
fail_map_bars:
if (mqnic->hw_addr)
pci_iounmap(pdev, mqnic->hw_addr);
if (mqnic->app_hw_addr)
pci_iounmap(pdev, mqnic->app_hw_addr);
if (mqnic->ram_hw_addr)
pci_iounmap(pdev, mqnic->ram_hw_addr);
pci_release_regions(pdev);
fail_regions:
pci_disable_device(pdev);
fail_enable_device:
mqnic_free_id(mqnic);
return ret;
}
static void mqnic_pci_remove(struct pci_dev *pdev)
{
struct mqnic_dev *mqnic = pci_get_drvdata(pdev);
dev_info(&pdev->dev, DRIVER_NAME " PCI remove");
mqnic_common_remove(mqnic);
pci_clear_master(pdev);
mqnic_irq_deinit_pcie(mqnic);
if (mqnic->hw_addr)
pci_iounmap(pdev, mqnic->hw_addr);
if (mqnic->app_hw_addr)
pci_iounmap(pdev, mqnic->app_hw_addr);
if (mqnic->ram_hw_addr)
pci_iounmap(pdev, mqnic->ram_hw_addr);
pci_release_regions(pdev);
pci_disable_device(pdev);
mqnic_free_id(mqnic);
}
static void mqnic_pci_shutdown(struct pci_dev *pdev)
{
dev_info(&pdev->dev, DRIVER_NAME " PCI shutdown");
mqnic_pci_remove(pdev);
}
static struct pci_driver mqnic_pci_driver = {
.name = DRIVER_NAME,
.id_table = mqnic_pci_id_table,
.probe = mqnic_pci_probe,
.remove = mqnic_pci_remove,
.shutdown = mqnic_pci_shutdown
};
#endif /* CONFIG_PCI */
static int mqnic_platform_probe(struct platform_device *pdev)
{
int ret;
struct mqnic_dev *mqnic;
struct device *dev = &pdev->dev;
struct resource *res;
dev_info(dev, DRIVER_NAME " platform probe");
#ifdef CONFIG_NUMA
dev_info(dev, " NUMA node: %d", pdev->dev.numa_node);
#endif
mqnic = devm_kzalloc(dev, sizeof(*mqnic), GFP_KERNEL);
if (!mqnic)
return -ENOMEM;
mqnic->dev = dev;
mqnic->pfdev = pdev;
platform_set_drvdata(pdev, mqnic);
// assign ID and add to list
mqnic_assign_id(mqnic);
// Set DMA properties
ret = mqnic_common_setdma(mqnic);
if (ret)
goto fail;
// Reserve and map regions
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
mqnic->hw_regs_size = resource_size(res);
mqnic->hw_regs_phys = res->start;
dev_info(dev, "Control BAR size: %llu", mqnic->hw_regs_size);
mqnic->hw_addr = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(mqnic->hw_addr)) {
ret = PTR_ERR(mqnic->hw_addr);
dev_err(dev, "Failed to map control BAR");
goto fail;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (res) {
void __iomem *hw_addr;
mqnic->app_hw_regs_size = resource_size(res);
mqnic->app_hw_regs_phys = res->start;
dev_info(dev, "Application BAR size: %llu", mqnic->app_hw_regs_size);
hw_addr = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(hw_addr)) {
ret = PTR_ERR(hw_addr);
dev_err(dev, "Failed to map application BAR");
goto fail;
}
mqnic->app_hw_addr = hw_addr;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
if (res) {
void __iomem *hw_addr;
mqnic->ram_hw_regs_size = resource_size(res);
mqnic->ram_hw_regs_phys = res->start;
dev_info(dev, "RAM BAR size: %llu", mqnic->ram_hw_regs_size);
hw_addr = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(hw_addr)) {
ret = PTR_ERR(hw_addr);
dev_err(dev, "Failed to map RAM BAR");
goto fail;
}
mqnic->ram_hw_addr = hw_addr;
}
// Set up interrupts
ret = mqnic_irq_init_platform(mqnic);
if (ret) {
dev_err(dev, "Failed to set up interrupts");
goto fail;
}
// Common init
ret = mqnic_common_probe(mqnic);
if (ret)
goto fail;
// probe complete
return 0;
// error handling
fail:
mqnic_free_id(mqnic);
return ret;
}
static int mqnic_platform_remove(struct platform_device *pdev)
{
struct mqnic_dev *mqnic = platform_get_drvdata(pdev);
dev_info(&pdev->dev, DRIVER_NAME " platform remove");
mqnic_common_remove(mqnic);
mqnic_free_id(mqnic);
return 0;
}
static struct platform_driver mqnic_platform_driver = {
.probe = mqnic_platform_probe,
.remove = mqnic_platform_remove,
.driver = {
.name = DRIVER_NAME,
.of_match_table = of_match_ptr(mqnic_of_id_table),
},
};
static int __init mqnic_init(void)
{
int rc;
#ifdef CONFIG_PCI
rc = pci_register_driver(&mqnic_pci_driver);
if (rc)
return rc;
#endif
rc = platform_driver_register(&mqnic_platform_driver);
if (rc)
goto err;
return 0;
err:
#ifdef CONFIG_PCI
pci_unregister_driver(&mqnic_pci_driver);
#endif
return rc;
}
static void __exit mqnic_exit(void)
{
platform_driver_unregister(&mqnic_platform_driver);
#ifdef CONFIG_PCI
pci_unregister_driver(&mqnic_pci_driver);
#endif
}
module_init(mqnic_init);
module_exit(mqnic_exit);