2021-10-21 14:55:48 -07:00
|
|
|
// SPDX-License-Identifier: BSD-2-Clause-Views
|
2019-07-17 18:13:51 -07:00
|
|
|
/*
|
2023-06-26 11:44:57 -07:00
|
|
|
* Copyright (c) 2019-2023 The Regents of the University of California
|
2021-10-21 14:55:48 -07:00
|
|
|
*/
|
2019-07-17 18:13:51 -07:00
|
|
|
|
|
|
|
#include "mqnic.h"
|
|
|
|
#include <linux/module.h>
|
2020-02-25 12:36:43 -08:00
|
|
|
#include <linux/version.h>
|
2019-07-17 18:13:51 -07:00
|
|
|
#include <linux/delay.h>
|
2023-06-01 23:17:54 -07:00
|
|
|
#include <linux/reset.h>
|
2021-12-29 22:31:46 -08:00
|
|
|
#include <linux/rtc.h>
|
2019-07-17 18:13:51 -07:00
|
|
|
|
2021-10-08 18:31:53 -07:00
|
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)
|
2020-02-25 12:36:43 -08:00
|
|
|
#include <linux/pci-aspm.h>
|
|
|
|
#endif
|
|
|
|
|
2019-07-17 18:13:51 -07:00
|
|
|
MODULE_DESCRIPTION("mqnic driver");
|
|
|
|
MODULE_AUTHOR("Alex Forencich");
|
2021-10-21 14:55:48 -07:00
|
|
|
MODULE_LICENSE("Dual BSD/GPL");
|
2019-07-17 18:13:51 -07:00
|
|
|
MODULE_VERSION(DRIVER_VERSION);
|
|
|
|
|
2023-04-30 21:48:34 -07:00
|
|
|
unsigned int mqnic_num_eq_entries = 1024;
|
|
|
|
unsigned int mqnic_num_txq_entries = 1024;
|
|
|
|
unsigned int mqnic_num_rxq_entries = 1024;
|
2021-10-21 10:43:06 +02:00
|
|
|
|
2023-04-30 21:48:34 -07:00
|
|
|
module_param_named(num_eq_entries, mqnic_num_eq_entries, uint, 0444);
|
|
|
|
MODULE_PARM_DESC(num_eq_entries, "number of entries to allocate per event queue (default: 1024)");
|
|
|
|
module_param_named(num_txq_entries, mqnic_num_txq_entries, uint, 0444);
|
|
|
|
MODULE_PARM_DESC(num_txq_entries, "number of entries to allocate per transmit queue (default: 1024)");
|
|
|
|
module_param_named(num_rxq_entries, mqnic_num_rxq_entries, uint, 0444);
|
|
|
|
MODULE_PARM_DESC(num_rxq_entries, "number of entries to allocate per receive queue (default: 1024)");
|
2021-10-21 10:43:06 +02:00
|
|
|
|
2022-05-13 19:13:41 +02:00
|
|
|
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)");
|
|
|
|
|
|
|
|
|
2021-10-12 19:32:25 +02:00
|
|
|
#ifdef CONFIG_PCI
|
2020-08-18 01:21:06 -07:00
|
|
|
static const struct pci_device_id mqnic_pci_id_table[] = {
|
2021-10-08 18:31:53 -07:00
|
|
|
{PCI_DEVICE(0x1234, 0x1001)},
|
|
|
|
{PCI_DEVICE(0x5543, 0x1001)},
|
|
|
|
{0 /* end */ }
|
2019-07-17 18:13:51 -07:00
|
|
|
};
|
|
|
|
|
2020-08-18 01:21:06 -07:00
|
|
|
MODULE_DEVICE_TABLE(pci, mqnic_pci_id_table);
|
2021-10-12 19:32:25 +02:00
|
|
|
#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
|
2019-07-17 18:13:51 -07:00
|
|
|
|
|
|
|
static LIST_HEAD(mqnic_devices);
|
|
|
|
static DEFINE_SPINLOCK(mqnic_devices_lock);
|
|
|
|
|
|
|
|
static unsigned int mqnic_get_free_id(void)
|
|
|
|
{
|
2021-10-08 18:31:53 -07:00
|
|
|
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;
|
2019-07-17 18:13:51 -07:00
|
|
|
}
|
|
|
|
|
2021-12-23 23:48:57 -08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2021-10-12 19:32:25 +02:00
|
|
|
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
|
|
|
|
|
2022-04-16 23:30:39 -07:00
|
|
|
static void mqnic_common_remove(struct mqnic_dev *mqnic);
|
|
|
|
|
2022-04-21 13:18:00 -07:00
|
|
|
#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
|
|
|
|
|
2021-12-24 00:02:08 -08:00
|
|
|
static int mqnic_common_probe(struct mqnic_dev *mqnic)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
2023-09-12 11:17:24 -07:00
|
|
|
struct devlink *devlink = priv_to_devlink(mqnic);
|
2021-12-24 00:02:08 -08:00
|
|
|
struct device *dev = mqnic->dev;
|
2022-04-24 23:01:15 -07:00
|
|
|
struct mqnic_reg_block *rb;
|
2021-12-29 22:31:46 -08:00
|
|
|
struct rtc_time tm;
|
2021-12-24 00:02:08 -08:00
|
|
|
|
2021-12-25 01:40:08 -08:00
|
|
|
int k = 0, l = 0;
|
2021-12-24 00:02:08 -08:00
|
|
|
|
2023-09-14 15:24:25 -07:00
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0)
|
|
|
|
devlink_register(devlink);
|
|
|
|
#else
|
|
|
|
devlink_register(devlink, dev);
|
|
|
|
#endif
|
|
|
|
|
2021-12-29 22:31:46 -08:00
|
|
|
// Enumerate registers
|
2022-04-24 23:01:15 -07:00
|
|
|
mqnic->rb_list = mqnic_enumerate_reg_block_list(mqnic->hw_addr, 0, mqnic->hw_regs_size);
|
2021-12-29 22:31:46 -08:00
|
|
|
if (!mqnic->rb_list) {
|
|
|
|
dev_err(dev, "Failed to enumerate blocks");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev_info(dev, "Device-level register blocks:");
|
2022-04-22 23:27:15 -07:00
|
|
|
for (rb = mqnic->rb_list; rb->regs; rb++)
|
2022-05-04 09:03:37 -07:00
|
|
|
dev_info(dev, " type 0x%08x (v %d.%d.%d.%d)", rb->type, rb->version >> 24,
|
2021-12-29 22:31:46 -08:00
|
|
|
(rb->version >> 16) & 0xff, (rb->version >> 8) & 0xff, rb->version & 0xff);
|
|
|
|
|
2021-12-24 00:02:08 -08:00
|
|
|
// Read ID registers
|
2022-04-24 23:01:15 -07:00
|
|
|
mqnic->fw_id_rb = mqnic_find_reg_block(mqnic->rb_list, MQNIC_RB_FW_ID_TYPE, MQNIC_RB_FW_ID_VER, 0);
|
2021-12-29 22:31:46 -08:00
|
|
|
|
|
|
|
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);
|
2021-12-24 00:02:08 -08:00
|
|
|
dev_info(dev, "FW ID: 0x%08x", mqnic->fw_id);
|
2021-12-29 22:31:46 -08:00
|
|
|
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);
|
2021-12-24 00:02:08 -08:00
|
|
|
dev_info(dev, "Board ID: 0x%08x", mqnic->board_id);
|
2021-12-29 22:31:46 -08:00
|
|
|
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)
|
2023-09-12 11:16:10 -07:00
|
|
|
snprintf(mqnic->build_date_str, sizeof(mqnic->build_date_str), "%ptRd %ptRt", &tm, &tm);
|
2021-12-29 22:31:46 -08:00
|
|
|
#else
|
2023-09-12 11:16:10 -07:00
|
|
|
snprintf(mqnic->build_date_str, sizeof(mqnic->build_date_str), "%04d-%02d-%02d %02d:%02d:%02d",
|
|
|
|
tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
|
2021-12-29 22:31:46 -08:00
|
|
|
#endif
|
2023-09-12 11:16:10 -07:00
|
|
|
dev_info(dev, "Build date: %s UTC (raw: 0x%08x)", mqnic->build_date_str, mqnic->build_date);
|
2021-12-29 22:31:46 -08:00
|
|
|
dev_info(dev, "Git hash: %08x", mqnic->git_hash);
|
|
|
|
dev_info(dev, "Release info: %08x", mqnic->rel_info);
|
2021-12-24 00:02:08 -08:00
|
|
|
|
2022-04-24 23:01:15 -07:00
|
|
|
rb = mqnic_find_reg_block(mqnic->rb_list, MQNIC_RB_APP_INFO_TYPE, MQNIC_RB_APP_INFO_VER, 0);
|
2022-04-21 13:15:45 -07:00
|
|
|
|
|
|
|
if (rb) {
|
|
|
|
mqnic->app_id = ioread32(rb->regs + MQNIC_RB_APP_INFO_REG_ID);
|
|
|
|
dev_info(dev, "Application ID: 0x%08x", mqnic->app_id);
|
2023-11-12 19:15:40 -08:00
|
|
|
|
|
|
|
if (!mqnic->app_hw_addr) {
|
|
|
|
dev_warn(dev, "Warning: application section present, but application BAR not mapped");
|
|
|
|
}
|
2022-04-21 13:15:45 -07:00
|
|
|
}
|
|
|
|
|
2022-11-23 17:49:14 -08:00
|
|
|
mqnic_clk_info_init(mqnic);
|
2022-11-23 17:49:38 -08:00
|
|
|
mqnic_stats_init(mqnic);
|
2022-11-23 17:49:14 -08:00
|
|
|
|
2022-04-24 23:01:15 -07:00
|
|
|
mqnic->phc_rb = mqnic_find_reg_block(mqnic->rb_list, MQNIC_RB_PHC_TYPE, MQNIC_RB_PHC_VER, 0);
|
2021-12-24 00:02:08 -08:00
|
|
|
|
2021-12-29 22:31:46 -08:00
|
|
|
// Enumerate interfaces
|
2022-04-24 23:01:15 -07:00
|
|
|
mqnic->if_rb = mqnic_find_reg_block(mqnic->rb_list, MQNIC_RB_IF_TYPE, MQNIC_RB_IF_VER, 0);
|
2021-12-24 00:02:08 -08:00
|
|
|
|
2021-12-29 22:31:46 -08:00
|
|
|
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);
|
2021-12-24 00:02:08 -08:00
|
|
|
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) {
|
2021-12-29 22:31:46 -08:00
|
|
|
ret = -EIO;
|
2021-12-24 00:02:08 -08:00
|
|
|
dev_err(dev, "Invalid BAR configuration (%d IF * 0x%x > 0x%llx)",
|
|
|
|
mqnic->if_count, mqnic->if_stride, mqnic->hw_regs_size);
|
2021-12-29 22:31:46 -08:00
|
|
|
goto fail_bar_size;
|
2021-12-24 00:02:08 -08:00
|
|
|
}
|
|
|
|
|
2021-10-12 19:32:25 +02:00
|
|
|
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;
|
|
|
|
}
|
2021-12-24 00:02:08 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// register PHC
|
2021-12-29 22:31:46 -08:00
|
|
|
if (mqnic->phc_rb)
|
2021-12-24 00:02:08 -08:00
|
|
|
mqnic_register_phc(mqnic);
|
|
|
|
|
|
|
|
mutex_init(&mqnic->state_lock);
|
|
|
|
|
|
|
|
// Set up interfaces
|
2023-09-13 16:40:27 -07:00
|
|
|
mqnic->phys_port_max = 0;
|
2021-12-29 14:29:55 -08:00
|
|
|
|
2021-12-24 00:02:08 -08:00
|
|
|
mqnic->if_count = min_t(u32, mqnic->if_count, MQNIC_MAX_IF);
|
|
|
|
|
|
|
|
for (k = 0; k < mqnic->if_count; k++) {
|
2023-04-30 21:57:32 -07:00
|
|
|
struct mqnic_if *interface;
|
2021-12-24 00:02:08 -08:00
|
|
|
dev_info(dev, "Creating interface %d", k);
|
2023-04-30 21:57:32 -07:00
|
|
|
interface = mqnic_create_interface(mqnic, k, mqnic->hw_addr + k * mqnic->if_stride);
|
|
|
|
if (IS_ERR_OR_NULL(interface)) {
|
2021-12-24 00:02:08 -08:00
|
|
|
dev_err(dev, "Failed to create interface: %d", ret);
|
|
|
|
goto fail_create_if;
|
|
|
|
}
|
2023-04-30 21:57:32 -07:00
|
|
|
mqnic->interface[k] = interface;
|
2021-12-24 00:02:08 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// pass module I2C clients to interface instances
|
2021-12-25 01:40:08 -08:00
|
|
|
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];
|
|
|
|
}
|
|
|
|
}
|
2021-12-24 00:02:08 -08:00
|
|
|
|
2022-04-16 23:30:39 -07:00
|
|
|
fail_create_if:
|
2021-12-24 00:02:08 -08:00
|
|
|
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) {
|
2022-04-16 23:30:39 -07:00
|
|
|
mqnic->misc_dev.this_device = NULL;
|
2021-12-24 00:02:08 -08:00
|
|
|
dev_err(dev, "misc_register failed: %d\n", ret);
|
|
|
|
goto fail_miscdev;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev_info(dev, "Registered device %s", mqnic->name);
|
|
|
|
|
2022-04-21 13:18:00 -07:00
|
|
|
#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
|
|
|
|
|
2021-12-24 00:02:08 -08:00
|
|
|
// probe complete
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// error handling
|
2022-05-17 15:36:16 +02:00
|
|
|
#ifdef CONFIG_AUXILIARY_BUS
|
2022-04-21 13:18:00 -07:00
|
|
|
fail_adev:
|
2022-05-17 15:36:16 +02:00
|
|
|
#endif
|
2021-12-24 00:02:08 -08:00
|
|
|
fail_miscdev:
|
2021-12-29 22:31:46 -08:00
|
|
|
fail_board:
|
|
|
|
fail_bar_size:
|
|
|
|
fail_rb_init:
|
2022-04-16 23:30:39 -07:00
|
|
|
mqnic_common_remove(mqnic);
|
2021-12-24 00:02:08 -08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mqnic_common_remove(struct mqnic_dev *mqnic)
|
|
|
|
{
|
2023-09-12 11:17:24 -07:00
|
|
|
struct devlink *devlink = priv_to_devlink(mqnic);
|
2021-12-24 00:02:08 -08:00
|
|
|
int k = 0;
|
|
|
|
|
2022-04-21 13:18:00 -07:00
|
|
|
#ifdef CONFIG_AUXILIARY_BUS
|
|
|
|
if (mqnic->app_adev) {
|
|
|
|
auxiliary_device_delete(&mqnic->app_adev->adev);
|
|
|
|
auxiliary_device_uninit(&mqnic->app_adev->adev);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2022-04-16 23:30:39 -07:00
|
|
|
if (mqnic->misc_dev.this_device)
|
|
|
|
misc_deregister(&mqnic->misc_dev);
|
2021-12-24 00:02:08 -08:00
|
|
|
|
2023-04-30 21:57:32 -07:00
|
|
|
for (k = 0; k < ARRAY_SIZE(mqnic->interface); k++) {
|
|
|
|
if (mqnic->interface[k]) {
|
|
|
|
mqnic_destroy_interface(mqnic->interface[k]);
|
|
|
|
mqnic->interface[k] = NULL;
|
|
|
|
}
|
|
|
|
}
|
2021-12-24 00:02:08 -08:00
|
|
|
|
|
|
|
mqnic_unregister_phc(mqnic);
|
2021-10-12 19:32:25 +02:00
|
|
|
if (mqnic->pfdev) {
|
|
|
|
#ifdef CONFIG_OF
|
|
|
|
mqnic_platform_module_eeprom_put(mqnic);
|
|
|
|
#endif
|
|
|
|
} else {
|
|
|
|
mqnic_board_deinit(mqnic);
|
|
|
|
}
|
2022-04-16 23:30:39 -07:00
|
|
|
if (mqnic->rb_list)
|
2022-04-24 23:01:15 -07:00
|
|
|
mqnic_free_reg_block_list(mqnic->rb_list);
|
2023-09-14 15:24:25 -07:00
|
|
|
|
|
|
|
devlink_unregister(devlink);
|
2021-12-24 00:02:08 -08:00
|
|
|
}
|
|
|
|
|
2021-10-12 19:32:25 +02:00
|
|
|
#ifdef CONFIG_PCI
|
2020-08-18 01:21:06 -07:00
|
|
|
static int mqnic_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
2019-07-17 18:13:51 -07:00
|
|
|
{
|
2021-10-08 18:31:53 -07:00
|
|
|
int ret = 0;
|
|
|
|
struct mqnic_dev *mqnic;
|
2023-09-12 11:17:24 -07:00
|
|
|
struct devlink *devlink;
|
2021-10-08 18:31:53 -07:00
|
|
|
struct device *dev = &pdev->dev;
|
2022-03-15 17:55:29 -07:00
|
|
|
struct pci_dev *bridge = pci_upstream_bridge(pdev);
|
2021-10-08 18:31:53 -07:00
|
|
|
|
|
|
|
dev_info(dev, DRIVER_NAME " PCI probe");
|
|
|
|
dev_info(dev, " Vendor: 0x%04x", pdev->vendor);
|
|
|
|
dev_info(dev, " Device: 0x%04x", pdev->device);
|
2021-11-02 14:40:32 -07:00
|
|
|
dev_info(dev, " Subsystem vendor: 0x%04x", pdev->subsystem_vendor);
|
|
|
|
dev_info(dev, " Subsystem device: 0x%04x", pdev->subsystem_device);
|
2021-10-08 18:31:53 -07:00
|
|
|
dev_info(dev, " Class: 0x%06x", pdev->class);
|
|
|
|
dev_info(dev, " PCI ID: %04x:%02x:%02x.%d", pci_domain_nr(pdev->bus),
|
2021-10-21 13:54:00 -07:00
|
|
|
pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
|
2022-03-15 17:55:29 -07:00
|
|
|
|
2021-10-08 18:31:53 -07:00
|
|
|
if (pdev->pcie_cap) {
|
|
|
|
u16 devctl;
|
|
|
|
u32 lnkcap;
|
2023-06-12 23:52:52 -07:00
|
|
|
u16 lnkctl;
|
2021-10-08 18:31:53 -07:00
|
|
|
u16 lnksta;
|
2021-10-21 13:54:00 -07:00
|
|
|
|
2021-10-08 18:31:53 -07:00
|
|
|
pci_read_config_word(pdev, pdev->pcie_cap + PCI_EXP_DEVCTL, &devctl);
|
|
|
|
pci_read_config_dword(pdev, pdev->pcie_cap + PCI_EXP_LNKCAP, &lnkcap);
|
2023-06-12 23:52:52 -07:00
|
|
|
pci_read_config_word(pdev, pdev->pcie_cap + PCI_EXP_LNKCTL, &lnkctl);
|
2021-10-08 18:31:53 -07:00
|
|
|
pci_read_config_word(pdev, pdev->pcie_cap + PCI_EXP_LNKSTA, &lnksta);
|
2021-10-21 13:54:00 -07:00
|
|
|
|
2021-10-08 18:31:53 -07:00
|
|
|
dev_info(dev, " Max payload size: %d bytes",
|
2021-10-21 13:54:00 -07:00
|
|
|
128 << ((devctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5));
|
2021-10-08 18:31:53 -07:00
|
|
|
dev_info(dev, " Max read request size: %d bytes",
|
2021-10-21 13:54:00 -07:00
|
|
|
128 << ((devctl & PCI_EXP_DEVCTL_READRQ) >> 12));
|
2023-06-12 23:52:52 -07:00
|
|
|
dev_info(dev, " Read completion boundary: %d bytes",
|
|
|
|
lnkctl & PCI_EXP_LNKCTL_RCB ? 128 : 64);
|
2021-10-08 18:31:53 -07:00
|
|
|
dev_info(dev, " Link capability: gen %d x%d",
|
2021-10-21 13:54:00 -07:00
|
|
|
lnkcap & PCI_EXP_LNKCAP_SLS, (lnkcap & PCI_EXP_LNKCAP_MLW) >> 4);
|
2021-10-08 18:31:53 -07:00
|
|
|
dev_info(dev, " Link status: gen %d x%d",
|
2021-10-21 13:54:00 -07:00
|
|
|
lnksta & PCI_EXP_LNKSTA_CLS, (lnksta & PCI_EXP_LNKSTA_NLW) >> 4);
|
2021-10-08 18:31:53 -07:00
|
|
|
dev_info(dev, " Relaxed ordering: %s",
|
2021-10-21 13:54:00 -07:00
|
|
|
devctl & PCI_EXP_DEVCTL_RELAX_EN ? "enabled" : "disabled");
|
2021-10-08 18:31:53 -07:00
|
|
|
dev_info(dev, " Phantom functions: %s",
|
2021-10-21 13:54:00 -07:00
|
|
|
devctl & PCI_EXP_DEVCTL_PHANTOM ? "enabled" : "disabled");
|
2021-10-08 18:31:53 -07:00
|
|
|
dev_info(dev, " Extended tags: %s",
|
2021-10-21 13:54:00 -07:00
|
|
|
devctl & PCI_EXP_DEVCTL_EXT_TAG ? "enabled" : "disabled");
|
2021-10-08 18:31:53 -07:00
|
|
|
dev_info(dev, " No snoop: %s",
|
2021-10-21 13:54:00 -07:00
|
|
|
devctl & PCI_EXP_DEVCTL_NOSNOOP_EN ? "enabled" : "disabled");
|
2021-10-08 18:31:53 -07:00
|
|
|
}
|
2022-03-15 17:55:29 -07:00
|
|
|
|
2021-08-30 01:27:25 -07:00
|
|
|
#ifdef CONFIG_NUMA
|
2021-10-08 18:31:53 -07:00
|
|
|
dev_info(dev, " NUMA node: %d", pdev->dev.numa_node);
|
2021-08-30 01:27:25 -07:00
|
|
|
#endif
|
2022-03-15 17:55:29 -07:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2021-10-08 18:31:53 -07:00
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
|
|
|
|
pcie_print_link_status(pdev);
|
2021-09-24 17:05:35 -07:00
|
|
|
#endif
|
2019-07-17 18:13:51 -07:00
|
|
|
|
2023-09-12 11:17:24 -07:00
|
|
|
devlink = mqnic_devlink_alloc(dev);
|
|
|
|
if (!devlink)
|
2021-10-08 18:31:53 -07:00
|
|
|
return -ENOMEM;
|
|
|
|
|
2023-09-12 11:17:24 -07:00
|
|
|
mqnic = devlink_priv(devlink);
|
2021-10-08 18:31:53 -07:00
|
|
|
mqnic->dev = dev;
|
|
|
|
mqnic->pdev = pdev;
|
|
|
|
pci_set_drvdata(pdev, mqnic);
|
|
|
|
|
|
|
|
// assign ID and add to list
|
2021-12-23 23:48:57 -08:00
|
|
|
mqnic_assign_id(mqnic);
|
2021-10-08 18:31:53 -07:00
|
|
|
|
|
|
|
// Disable ASPM
|
|
|
|
pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S |
|
2021-10-21 13:54:00 -07:00
|
|
|
PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_CLKPM);
|
2021-10-08 18:31:53 -07:00
|
|
|
|
|
|
|
// Enable device
|
|
|
|
ret = pci_enable_device_mem(pdev);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(dev, "Failed to enable PCI device");
|
|
|
|
goto fail_enable_device;
|
|
|
|
}
|
|
|
|
|
2021-10-12 19:32:25 +02:00
|
|
|
// Set DMA properties
|
|
|
|
ret = mqnic_common_setdma(mqnic);
|
|
|
|
if (ret)
|
|
|
|
goto fail_regions;
|
2021-10-08 18:31:53 -07:00
|
|
|
|
|
|
|
// 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
|
2021-12-29 22:31:46 -08:00
|
|
|
if (ioread32(mqnic->hw_addr+4) == 0xffffffff) {
|
2021-10-08 18:31:53 -07:00
|
|
|
ret = -EIO;
|
|
|
|
dev_err(dev, "Device needs to be reset");
|
2021-12-24 00:02:08 -08:00
|
|
|
goto fail_reset;
|
2021-10-08 18:31:53 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Set up interrupts
|
2021-12-12 13:34:33 -08:00
|
|
|
ret = mqnic_irq_init_pcie(mqnic);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(dev, "Failed to set up interrupts");
|
2021-12-29 22:31:46 -08:00
|
|
|
goto fail_init_irq;
|
2021-10-08 18:31:53 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Enable bus mastering for DMA
|
|
|
|
pci_set_master(pdev);
|
|
|
|
|
2021-12-24 00:02:08 -08:00
|
|
|
// Common init
|
|
|
|
ret = mqnic_common_probe(mqnic);
|
|
|
|
if (ret)
|
|
|
|
goto fail_common;
|
2021-10-08 18:31:53 -07:00
|
|
|
|
|
|
|
// probe complete
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// error handling
|
2021-12-24 00:02:08 -08:00
|
|
|
fail_common:
|
2021-10-08 18:31:53 -07:00
|
|
|
pci_clear_master(pdev);
|
2021-12-12 13:34:33 -08:00
|
|
|
mqnic_irq_deinit_pcie(mqnic);
|
2021-12-24 00:02:08 -08:00
|
|
|
fail_reset:
|
2021-12-29 22:31:46 -08:00
|
|
|
fail_init_irq:
|
2019-07-17 18:13:51 -07:00
|
|
|
fail_map_bars:
|
2021-10-08 18:31:53 -07:00
|
|
|
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);
|
2019-07-17 18:13:51 -07:00
|
|
|
fail_regions:
|
2021-10-08 18:31:53 -07:00
|
|
|
pci_disable_device(pdev);
|
2019-07-17 18:13:51 -07:00
|
|
|
fail_enable_device:
|
2021-12-23 23:48:57 -08:00
|
|
|
mqnic_free_id(mqnic);
|
2023-09-12 11:17:24 -07:00
|
|
|
mqnic_devlink_free(devlink);
|
2021-10-08 18:31:53 -07:00
|
|
|
return ret;
|
2019-07-17 18:13:51 -07:00
|
|
|
}
|
|
|
|
|
2020-08-18 01:21:06 -07:00
|
|
|
static void mqnic_pci_remove(struct pci_dev *pdev)
|
2019-07-17 18:13:51 -07:00
|
|
|
{
|
2021-10-08 18:31:53 -07:00
|
|
|
struct mqnic_dev *mqnic = pci_get_drvdata(pdev);
|
2023-09-12 11:17:24 -07:00
|
|
|
struct devlink *devlink = priv_to_devlink(mqnic);
|
2021-10-08 18:31:53 -07:00
|
|
|
|
|
|
|
dev_info(&pdev->dev, DRIVER_NAME " PCI remove");
|
|
|
|
|
2021-12-24 00:02:08 -08:00
|
|
|
mqnic_common_remove(mqnic);
|
2021-10-08 18:31:53 -07:00
|
|
|
|
|
|
|
pci_clear_master(pdev);
|
2021-12-12 13:34:33 -08:00
|
|
|
mqnic_irq_deinit_pcie(mqnic);
|
2021-10-08 18:31:53 -07:00
|
|
|
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);
|
2021-12-23 23:48:57 -08:00
|
|
|
mqnic_free_id(mqnic);
|
2023-09-12 11:17:24 -07:00
|
|
|
mqnic_devlink_free(devlink);
|
2019-07-17 18:13:51 -07:00
|
|
|
}
|
|
|
|
|
2020-08-18 01:21:06 -07:00
|
|
|
static void mqnic_pci_shutdown(struct pci_dev *pdev)
|
2019-07-17 18:13:51 -07:00
|
|
|
{
|
2021-10-08 18:31:53 -07:00
|
|
|
dev_info(&pdev->dev, DRIVER_NAME " PCI shutdown");
|
2019-07-17 18:13:51 -07:00
|
|
|
|
2021-10-08 18:31:53 -07:00
|
|
|
mqnic_pci_remove(pdev);
|
2019-07-17 18:13:51 -07:00
|
|
|
}
|
|
|
|
|
2020-08-18 23:22:55 -07:00
|
|
|
static struct pci_driver mqnic_pci_driver = {
|
2021-10-08 18:31:53 -07:00
|
|
|
.name = DRIVER_NAME,
|
|
|
|
.id_table = mqnic_pci_id_table,
|
|
|
|
.probe = mqnic_pci_probe,
|
|
|
|
.remove = mqnic_pci_remove,
|
|
|
|
.shutdown = mqnic_pci_shutdown
|
2019-07-17 18:13:51 -07:00
|
|
|
};
|
2021-10-12 19:32:25 +02:00
|
|
|
#endif /* CONFIG_PCI */
|
|
|
|
|
|
|
|
static int mqnic_platform_probe(struct platform_device *pdev)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct mqnic_dev *mqnic;
|
2023-09-12 11:17:24 -07:00
|
|
|
struct devlink *devlink;
|
2021-10-12 19:32:25 +02:00
|
|
|
struct device *dev = &pdev->dev;
|
|
|
|
struct resource *res;
|
2023-06-01 23:17:54 -07:00
|
|
|
struct reset_control *rst;
|
2021-10-12 19:32:25 +02:00
|
|
|
|
|
|
|
dev_info(dev, DRIVER_NAME " platform probe");
|
|
|
|
|
|
|
|
#ifdef CONFIG_NUMA
|
|
|
|
dev_info(dev, " NUMA node: %d", pdev->dev.numa_node);
|
|
|
|
#endif
|
|
|
|
|
2023-09-12 11:17:24 -07:00
|
|
|
devlink = mqnic_devlink_alloc(dev);
|
|
|
|
if (!devlink)
|
2021-10-12 19:32:25 +02:00
|
|
|
return -ENOMEM;
|
|
|
|
|
2023-09-12 11:17:24 -07:00
|
|
|
mqnic = devlink_priv(devlink);
|
2021-10-12 19:32:25 +02:00
|
|
|
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;
|
|
|
|
|
2023-06-01 23:17:54 -07:00
|
|
|
// Reset device
|
|
|
|
rst = devm_reset_control_get(dev, NULL);
|
|
|
|
if (IS_ERR(rst)) {
|
|
|
|
dev_warn(dev, "Cannot control device reset");
|
|
|
|
} else {
|
|
|
|
dev_info(dev, "Resetting device");
|
|
|
|
reset_control_assert(rst);
|
|
|
|
udelay(2);
|
|
|
|
reset_control_deassert(rst);
|
|
|
|
}
|
|
|
|
|
2021-10-12 19:32:25 +02:00
|
|
|
// 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);
|
2023-09-12 11:17:24 -07:00
|
|
|
mqnic_devlink_free(devlink);
|
2021-10-12 19:32:25 +02:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mqnic_platform_remove(struct platform_device *pdev)
|
|
|
|
{
|
|
|
|
struct mqnic_dev *mqnic = platform_get_drvdata(pdev);
|
2023-09-12 11:17:24 -07:00
|
|
|
struct devlink *devlink = priv_to_devlink(mqnic);
|
2021-10-12 19:32:25 +02:00
|
|
|
|
|
|
|
dev_info(&pdev->dev, DRIVER_NAME " platform remove");
|
|
|
|
|
|
|
|
mqnic_common_remove(mqnic);
|
|
|
|
|
|
|
|
mqnic_free_id(mqnic);
|
2023-09-12 11:17:24 -07:00
|
|
|
mqnic_devlink_free(devlink);
|
2021-10-12 19:32:25 +02:00
|
|
|
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),
|
|
|
|
},
|
|
|
|
};
|
2019-07-17 18:13:51 -07:00
|
|
|
|
|
|
|
static int __init mqnic_init(void)
|
|
|
|
{
|
2021-10-12 19:32:25 +02:00
|
|
|
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;
|
2019-07-17 18:13:51 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit mqnic_exit(void)
|
|
|
|
{
|
2021-10-12 19:32:25 +02:00
|
|
|
platform_driver_unregister(&mqnic_platform_driver);
|
|
|
|
|
|
|
|
#ifdef CONFIG_PCI
|
2021-10-08 18:31:53 -07:00
|
|
|
pci_unregister_driver(&mqnic_pci_driver);
|
2021-10-12 19:32:25 +02:00
|
|
|
#endif
|
2019-07-17 18:13:51 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
module_init(mqnic_init);
|
|
|
|
module_exit(mqnic_exit);
|