mft: Initial copy of mft source

Add initial mft source of version-4.26.1-3 from
https://www.mellanox.com/downloads/MFT/mft-4.26.1-3-arm64-deb.tgz

bug 4192483
bug 4312056

Change-Id: I77c0af297c9833c3dcbcdfc89b316042548b9af8
Signed-off-by: Bharath H S <bhs@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3069977
(cherry picked from commit d65df2a986469950aab3f323bbee3a3aee0c0308)
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3069972
GVS: Gerrit_Virtual_Submit <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Laxman Dewangan <ldewangan@nvidia.com>
This commit is contained in:
Bharath H S
2024-02-02 10:53:24 -08:00
committed by mobile promotions
parent f451b88fd7
commit bf89e2d98e
38 changed files with 5307 additions and 0 deletions

View File

@@ -0,0 +1,34 @@
KVERSION ?= $(shell uname -r)
CPU_ARCH ?= $(shell uname -m)
# Oracle Linux OS.
ifneq ($(shell if (echo $(KVERSION) | grep -qE 'uek'); then \
echo "YES"; else echo ""; fi),)
override WITH_MAKE_PARAMS += ctf-dir=$(CWD)/.ctf
endif
PACKAGE_NAME = nnt-driver
PACKAGE_VERSION = 1.0.0
PACKAGE_RC = 1
%: %.in
sed \
-e 's/@PACKAGE_NAME@/$(PACKAGE_NAME)/g' \
-e 's/@PACKAGE_VERSION@/$(PACKAGE_VERSION)/g' \
-e 's/@PACKAGE_RC@/$(PACKAGE_RC)/g' \
<$< >$@
ifneq ($(findstring ppc64, $(CPU_ARCH)),)
obj-m += mst_ppc_pci_reset.o
endif
obj-m += nnt_driver.o
nnt_driver-objs += nnt_device.o nnt_dma.o nnt_pci_conf_access.o \
nnt_pci_conf_access_no_vsec.o nnt_memory_access.o \
nnt_ioctl.o
all:
make -C /lib/modules/$(KVERSION)/build M=$(PWD) CONFIG_CTF= CONFIG_CC_STACKPROTECTOR_STRONG= $(WITH_MAKE_PARAMS) modules
clean:
make -C /lib/modules/$(KVERSION)/build M=$(PWD) clean

View File

@@ -0,0 +1,42 @@
#ifndef NNT_DEFS_H
#define NNT_DEFS_H
#include <linux/kernel.h>
#include <linux/fs.h>
/* Passing MFT flag argument */
extern int is_mft_package;
extern struct driver_info nnt_driver_info;
#define NNT_DRIVER_NAME "nnt_driver"
#define NNT_CLASS_NAME "nnt_class"
#define NNT_DEVICE_PREFIX "mt"
#define NNT_DRIVER "NNT Driver::"
#define CHECK_PCI_READ_ERROR(error, address) \
if (error) { \
printk(KERN_ERR "Failed to read from address: %x\n", address); \
goto ReturnOnFinished; \
}
#define CHECK_PCI_WRITE_ERROR(error, address, data) \
if (error) { \
printk(KERN_ERR "Failed to write to address: %x, data: %x\n", address, data); \
goto ReturnOnFinished; \
}
#define CHECK_ERROR(error) \
if (error) { \
goto ReturnOnFinished; \
}
struct driver_info {
dev_t device_number;
int contiguous_device_numbers;
struct class* class_driver;
};
#endif

View File

@@ -0,0 +1,626 @@
#include "nnt_device.h"
#include "nnt_device_defs.h"
#include "nnt_defs.h"
#include "nnt_pci_conf_access.h"
#include "nnt_pci_conf_access_no_vsec.h"
#include "nnt_memory_access.h"
#include <linux/module.h>
MODULE_LICENSE("GPL");
/* Device list to check if device is available
since it could be removed by hotplug event. */
LIST_HEAD(nnt_device_list);
int get_nnt_device(struct file* file, struct nnt_device** nnt_device)
{
int error_code = 0;
if (!file->private_data)
{
error_code = -EINVAL;
}
else
{
*nnt_device = file->private_data;
}
return error_code;
}
void set_private_data_open(struct file* file)
{
struct nnt_device* current_nnt_device = NULL;
struct nnt_device* temp_nnt_device = NULL;
int minor = iminor(file_inode(file));
/* Set private data to nnt structure. */
list_for_each_entry_safe(current_nnt_device, temp_nnt_device, &nnt_device_list, entry)
{
if ((minor == current_nnt_device->device_minor_number) && current_nnt_device->device_enabled)
{
file->private_data = current_nnt_device;
return;
}
}
}
int set_private_data_bc(struct file* file, unsigned int bus, unsigned int devfn, unsigned int domain)
{
struct nnt_device* current_nnt_device = NULL;
struct nnt_device* temp_nnt_device = NULL;
int minor = iminor(file_inode(file));
unsigned int current_function;
unsigned int current_device;
/* Set private data to nnt structure. */
list_for_each_entry_safe(current_nnt_device, temp_nnt_device, &nnt_device_list, entry)
{
struct pci_bus* pci_bus = pci_find_bus(current_nnt_device->dbdf.domain, current_nnt_device->dbdf.bus);
if (!pci_bus)
{
return -ENXIO;
}
current_nnt_device->pci_device = pci_get_slot(pci_bus, current_nnt_device->dbdf.devfn);
if (!current_nnt_device->pci_device)
{
return -ENXIO;
}
current_function = PCI_FUNC(current_nnt_device->dbdf.devfn);
current_device = PCI_SLOT(current_nnt_device->dbdf.devfn);
if ((current_nnt_device->dbdf.bus == bus) && (current_device == PCI_SLOT(devfn)) &&
(current_function == PCI_FUNC(devfn)) && (current_nnt_device->dbdf.domain == domain))
{
current_nnt_device->device_minor_number = minor;
current_nnt_device->device_enabled = true;
file->private_data = current_nnt_device;
return 0;
}
}
return -EINVAL;
}
int set_private_data(struct file* file)
{
struct nnt_device* current_nnt_device = NULL;
struct nnt_device* temp_nnt_device = NULL;
int minor = iminor(file_inode(file));
/* Set private data to nnt structure. */
list_for_each_entry_safe(current_nnt_device, temp_nnt_device, &nnt_device_list, entry)
{
if (current_nnt_device->device_minor_number == minor)
{
file->private_data = current_nnt_device;
return 0;
}
}
printk(KERN_ERR "failed to find device with minor=%d\n", minor);
return -EINVAL;
}
int create_file_name_mstflint(struct pci_dev* pci_device, struct nnt_device* nnt_dev, enum nnt_device_type device_type)
{
sprintf(nnt_dev->device_name, "%4.4x:%2.2x:%2.2x.%1.1x_%s", pci_domain_nr(pci_device->bus), pci_device->bus->number,
PCI_SLOT(pci_device->devfn), PCI_FUNC(pci_device->devfn),
(device_type == NNT_PCICONF) ? MSTFLINT_PCICONF_DEVICE_NAME : MSTFLINT_MEMORY_DEVICE_NAME);
printk(KERN_DEBUG
"MSTFlint device name created: id: %d, slot id: %d, device name: /dev/%s domain: 0x%x bus: 0x%x\n",
pci_device->device, PCI_FUNC(pci_device->devfn), nnt_dev->device_name, pci_domain_nr(pci_device->bus),
pci_device->bus->number);
return 0;
}
int create_file_name_mft(struct pci_dev* pci_device, struct nnt_device* nnt_dev, enum nnt_device_type device_type)
{
sprintf(nnt_dev->device_name, "mst/mt%d_%s0.%x", pci_device->device,
(device_type == NNT_PCICONF) ? MFT_PCICONF_DEVICE_NAME : MFT_MEMORY_DEVICE_NAME,
PCI_FUNC(pci_device->devfn));
printk(KERN_DEBUG "MFT device name created: id: %d, slot id: %d, device name: /dev/%s domain: 0x%x bus: 0x%x\n",
pci_device->device, PCI_FUNC(pci_device->devfn), nnt_dev->device_name, pci_domain_nr(pci_device->bus),
pci_device->bus->number);
return 0;
}
int nnt_device_structure_init(struct nnt_device** nnt_device)
{
/* Allocate nnt device structure. */
*nnt_device = kzalloc(sizeof(struct nnt_device), GFP_KERNEL);
if (!(*nnt_device))
{
return -ENOMEM;
}
/* initialize nnt structure. */
memset(*nnt_device, 0, sizeof(struct nnt_device));
return 0;
}
int create_nnt_device(struct pci_dev* pci_device, enum nnt_device_type device_type, int is_alloc_chrdev_region)
{
struct nnt_device* nnt_device = NULL;
int error_code = 0;
/* Allocate nnt device info structure. */
if ((error_code = nnt_device_structure_init(&nnt_device)) != 0)
goto ReturnOnError;
if (is_alloc_chrdev_region)
{
/* Build the device file name of MSTFlint. */
if ((error_code = create_file_name_mstflint(pci_device, nnt_device, device_type)) != 0)
goto ReturnOnError;
}
else
{
/* Build the device file name of MFT. */
if ((error_code = create_file_name_mft(pci_device, nnt_device, device_type)) != 0)
goto ReturnOnError;
}
nnt_device->dbdf.bus = pci_device->bus->number;
nnt_device->dbdf.devfn = pci_device->devfn;
nnt_device->dbdf.domain = pci_domain_nr(pci_device->bus);
nnt_device->pci_device = pci_device;
nnt_device->device_type = device_type;
/* Add the nnt device structure to the list. */
list_add_tail(&nnt_device->entry, &nnt_device_list);
return error_code;
ReturnOnError:
if (nnt_device)
{
kfree(nnt_device);
}
return error_code;
}
int check_pci_id_range(unsigned short pci_device_id, unsigned short id_range_start)
{
return (pci_device_id >= id_range_start) && (pci_device_id <= (id_range_start + 100));
}
int is_connectx(unsigned short pci_device_id)
{
return check_pci_id_range(pci_device_id, CONNECTX3_PCI_ID);
}
int is_connectx3(unsigned short pci_device_id)
{
return pci_device_id == CONNECTX3_PCI_ID || pci_device_id == CONNECTX3PRO_PCI_ID;
}
int is_bluefield(unsigned short pci_device_id)
{
return check_pci_id_range(pci_device_id, BLUEFIELD_PCI_ID) ||
check_pci_id_range(pci_device_id, BLUEFIELD_DPU_AUX_PCI_ID);
}
int is_pcie_switch(unsigned short pci_device_id)
{
return check_pci_id_range(pci_device_id, SCHRODINGER_PCI_ID);
}
int is_quantum(unsigned short pci_device_id)
{
return check_pci_id_range(pci_device_id, QUANTUM_PCI_ID);
}
int is_spectrum(unsigned short pci_device_id)
{
return (pci_device_id == SPECTRUM_PCI_ID) || (check_pci_id_range(pci_device_id, SPECTRUM2_PCI_ID));
}
int is_switch_ib(unsigned short pci_device_id)
{
return pci_device_id == SWITCHIB_PCI_ID || pci_device_id == SWITCHIB2_PCI_ID;
}
int is_bw00(unsigned short pci_device_id)
{
return check_pci_id_range(pci_device_id, BW00_PCI_ID);
}
int is_bw02(unsigned short pci_device_id)
{
return check_pci_id_range(pci_device_id, BW02_PCI_ID);
}
int is_livefish_device(unsigned short pci_device_id)
{
return pci_device_id >= CONNECTX3_LIVEFISH_ID && pci_device_id < CONNECTX3_PCI_ID;
}
int is_nic(unsigned short pci_device_id)
{
return is_connectx(pci_device_id) || is_bluefield(pci_device_id);
}
int is_switch(unsigned short pci_device_id)
{
return is_pcie_switch(pci_device_id) || is_quantum(pci_device_id) || is_spectrum(pci_device_id) ||
is_switch_ib(pci_device_id) || is_bw00(pci_device_id) || is_bw02(pci_device_id);
}
int is_toolspf(unsigned short pci_device_id)
{
return is_nic(pci_device_id - 4000) || is_switch(pci_device_id - 4000);
}
int is_pciconf_device(unsigned short pci_device_id)
{
return is_nic(pci_device_id) || is_toolspf(pci_device_id) || is_livefish_device(pci_device_id) ||
is_switch(pci_device_id);
}
int is_pcicr_device(unsigned short pci_device_id)
{
return (is_switch(pci_device_id) || is_toolspf(pci_device_id) || is_connectx3(pci_device_id)) &&
(!is_livefish_device(pci_device_id));
}
int create_device_file(struct nnt_device* current_nnt_device,
dev_t device_number,
int minor,
struct file_operations* fop,
int is_alloc_chrdev_region)
{
struct device* device = NULL;
int error = 0;
int count = 1;
/* NNT driver will create the device file
once we stop support backward compatibility. */
current_nnt_device->device_minor_number = -1;
current_nnt_device->device_number = device_number;
current_nnt_device->mcdev.owner = THIS_MODULE;
mutex_init(&current_nnt_device->lock);
if (!is_alloc_chrdev_region)
{
goto ReturnOnFinished;
}
// Create device with a new minor number.
current_nnt_device->device_minor_number = minor;
current_nnt_device->device_number = MKDEV(MAJOR(device_number), minor);
current_nnt_device->device_enabled = true;
current_nnt_device->connectx_wa_slot_p1 = 0;
/* Create device node. */
device = device_create(nnt_driver_info.class_driver, NULL, current_nnt_device->device_number, NULL,
current_nnt_device->device_name);
if (!device)
{
printk(KERN_ERR "Device creation failed\n");
error = -EINVAL;
goto ReturnOnFinished;
}
/* Init new device. */
cdev_init(&current_nnt_device->mcdev, fop);
/* Add device to the system. */
error = cdev_add(&current_nnt_device->mcdev, current_nnt_device->device_number, count);
if (error)
{
goto ReturnOnFinished;
}
ReturnOnFinished:
return error;
}
int check_if_vsec_supported(struct nnt_device* nnt_device)
{
int error = 0;
error = nnt_device->access.init(nnt_device);
CHECK_ERROR(error);
if (!nnt_device->pciconf_device.vsec_fully_supported)
{
nnt_device->device_type = NNT_PCICONF_NO_VSEC;
nnt_device->access.read = read_pciconf_no_vsec;
nnt_device->access.write = write_pciconf_no_vsec;
nnt_device->access.init = init_pciconf_no_vsec;
}
ReturnOnFinished:
return error;
}
int is_mellanox_vendor_type(struct nnt_device* current_nnt_device, unsigned int vsec_address)
{
u_int8_t vendor_type = 0;
int error = 0;
/* Read the capability type field */
if ((error = pci_read_config_byte(current_nnt_device->pci_device, (vsec_address + PCI_TYPE_OFFSET), &vendor_type)))
{
printk(KERN_ERR "Reading VSEC type failed with error %d\n", error);
return 0;
}
if (vendor_type == MELLANOX_VSEC_TYPE)
{
return 1;
}
return 0;
}
unsigned int get_mellanox_vsec_address(struct nnt_device* current_nnt_device)
{
unsigned int vsec_address = 0;
/* Look for the Mellanox VSEC address. if Mellanox VSEC isn't supported the address should be 0 */
vsec_address = pci_find_capability(current_nnt_device->pci_device, VSEC_CAPABILITY_ADDRESS);
if (!vsec_address || is_mellanox_vendor_type(current_nnt_device, vsec_address))
{
return vsec_address;
}
/* if found a non-Mellanox type VSEC, iterate of the next available VSECs*/
while (
(vsec_address = pci_find_next_capability(current_nnt_device->pci_device, vsec_address, VSEC_CAPABILITY_ADDRESS)))
{
if (is_mellanox_vendor_type(current_nnt_device, vsec_address))
{
return vsec_address;
}
}
/* if Mellanox VSEC address was not found, return 0 */
return 0;
}
int create_devices(dev_t device_number, struct file_operations* fop, int is_alloc_chrdev_region)
{
struct nnt_device* current_nnt_device = NULL;
struct nnt_device* temp_nnt_device = NULL;
int minor = 0;
int error = 0;
/* Create necessary number of the devices. */
list_for_each_entry_safe(current_nnt_device, temp_nnt_device, &nnt_device_list, entry)
{
/* Create the device file. */
if ((error = create_device_file(current_nnt_device, device_number, minor, fop, is_alloc_chrdev_region)) != 0)
goto ReturnOnFinished;
/* Members initialization. */
current_nnt_device->pciconf_device.vendor_specific_capability = get_mellanox_vsec_address(current_nnt_device);
current_nnt_device->vpd_capability_address =
pci_find_capability(current_nnt_device->pci_device, PCI_CAP_ID_VPD);
if (!current_nnt_device->pciconf_device.vendor_specific_capability)
{
current_nnt_device->device_type = NNT_PCICONF_NO_VSEC;
}
switch (current_nnt_device->device_type)
{
case NNT_PCICONF:
current_nnt_device->access.read = read_pciconf;
current_nnt_device->access.write = write_pciconf;
current_nnt_device->access.init = init_pciconf;
error = check_if_vsec_supported(current_nnt_device);
CHECK_ERROR(error);
break;
case NNT_PCICONF_NO_VSEC:
current_nnt_device->access.read = read_pciconf_no_vsec;
current_nnt_device->access.write = write_pciconf_no_vsec;
current_nnt_device->access.init = init_pciconf_no_vsec;
break;
case NNT_PCI_MEMORY:
current_nnt_device->access.read = read_memory;
current_nnt_device->access.write = write_memory;
current_nnt_device->access.init = init_memory;
break;
}
if (is_alloc_chrdev_region)
{
error = current_nnt_device->access.init(current_nnt_device);
}
minor++;
}
ReturnOnFinished:
return error;
}
int create_nnt_devices(dev_t device_number,
int is_alloc_chrdev_region,
struct file_operations* fop,
enum nnt_device_type_flag nnt_device_flag,
unsigned int vendor_id,
int with_unknown)
{
struct pci_dev* pci_device = NULL;
int error_code = 0;
/* Find all Nvidia PCI devices. */
while ((pci_device = pci_get_device(vendor_id, PCI_ANY_ID, pci_device)) != NULL)
{
if ((nnt_device_flag == NNT_PCICONF_DEVICES) || (nnt_device_flag == NNT_ALL_DEVICES))
{
/* Create pciconf device. */
if (with_unknown || is_pciconf_device(pci_device->device))
{
if ((error_code = create_nnt_device(pci_device, NNT_PCICONF, is_alloc_chrdev_region)) != 0)
{
printk(KERN_ERR "Failed to create pci conf device\n");
goto ReturnOnFinished;
}
}
}
if ((nnt_device_flag == NNT_PCI_DEVICES) || (nnt_device_flag == NNT_ALL_DEVICES))
{
/* Create pci memory device. */
if (with_unknown || is_pcicr_device(pci_device->device))
{
if ((error_code = create_nnt_device(pci_device, NNT_PCI_MEMORY, is_alloc_chrdev_region)) != 0)
{
printk(KERN_ERR "Failed to create pci memory device\n");
goto ReturnOnFinished;
}
}
}
}
/* Create the devices. */
if ((error_code = create_devices(device_number, fop, is_alloc_chrdev_region)) != 0)
{
return error_code;
}
ReturnOnFinished:
return error_code;
}
int find_all_vendor_devices(unsigned int vendor_id)
{
struct pci_dev* pci_device = NULL;
int contiguous_device_numbers = 0;
while ((pci_device = pci_get_device(vendor_id, PCI_ANY_ID, pci_device)) != NULL)
{
contiguous_device_numbers++;
}
return contiguous_device_numbers;
}
int get_amount_of_nvidia_devices(void)
{
int contiguous_device_numbers = 0;
/* Find all Mellanox & Nvidia PCI devices. */
contiguous_device_numbers +=
find_all_vendor_devices(NNT_MELLANOX_PCI_VENDOR) + find_all_vendor_devices(NNT_NVIDIA_PCI_VENDOR);
return contiguous_device_numbers;
}
int mutex_lock_nnt(struct file* file)
{
struct nnt_device* nnt_device;
if (!file)
{
return 1;
}
nnt_device = file->private_data;
if (!nnt_device)
{
return -EINVAL;
}
mutex_lock(&nnt_device->lock);
return 0;
}
void mutex_unlock_nnt(struct file* file)
{
struct nnt_device* nnt_device = file->private_data;
if (nnt_device)
{
mutex_unlock(&nnt_device->lock);
}
}
void destroy_nnt_devices(int is_alloc_chrdev_region)
{
struct nnt_device* current_nnt_device;
struct nnt_device* temp_nnt_device;
/* free all nnt_devices */
list_for_each_entry_safe(current_nnt_device, temp_nnt_device, &nnt_device_list, entry)
{
/* Character device is no longer, it must be properly destroyed. */
if (is_alloc_chrdev_region)
{
cdev_del(&current_nnt_device->mcdev);
device_destroy(nnt_driver_info.class_driver, current_nnt_device->device_number);
}
list_del(&current_nnt_device->entry);
kfree(current_nnt_device);
}
}
void destroy_nnt_devices_bc(void)
{
struct nnt_device* current_nnt_device;
struct nnt_device* temp_nnt_device;
/* free all nnt_devices */
list_for_each_entry_safe(current_nnt_device, temp_nnt_device, &nnt_device_list, entry)
{
/* Character device is no longer, it must be properly destroyed. */
list_del(&current_nnt_device->entry);
kfree(current_nnt_device);
}
}
int destroy_nnt_device_bc(struct nnt_device* nnt_device)
{
struct nnt_device* current_nnt_device;
struct nnt_device* temp_nnt_device;
unsigned int current_function;
unsigned int current_device;
/* Set private data to nnt structure. */
list_for_each_entry_safe(current_nnt_device, temp_nnt_device, &nnt_device_list, entry)
{
struct pci_bus* pci_bus = pci_find_bus(current_nnt_device->dbdf.domain, current_nnt_device->dbdf.bus);
if (!pci_bus)
{
return -ENXIO;
}
current_nnt_device->pci_device = pci_get_slot(pci_bus, current_nnt_device->dbdf.devfn);
if (!current_nnt_device->pci_device)
{
return -ENXIO;
}
current_function = PCI_FUNC(current_nnt_device->dbdf.devfn);
current_device = PCI_SLOT(current_nnt_device->dbdf.devfn);
if ((current_nnt_device->dbdf.bus == nnt_device->dbdf.bus) &&
(current_device == PCI_SLOT(nnt_device->dbdf.devfn)) &&
(current_function == PCI_FUNC(nnt_device->dbdf.devfn)) &&
(current_nnt_device->dbdf.domain == nnt_device->dbdf.domain))
{
/* Character device is no longer, it must be properly disabled. */
current_nnt_device->device_enabled = false;
printk(KERN_DEBUG "Device removed: domain: %d, bus: %d, device:%d, function:%d \n",
current_nnt_device->dbdf.domain, current_nnt_device->dbdf.bus, current_device, current_function);
return 0;
}
}
return 0;
}

View File

@@ -0,0 +1,26 @@
#ifndef NNT_DEVICE_H
#define NNT_DEVICE_H
#include <linux/pci.h>
#include "nnt_device_defs.h"
int create_nnt_devices(dev_t device_number,
int is_alloc_chrdev_region,
struct file_operations* fop,
enum nnt_device_type_flag nnt_device_flag,
unsigned int vendor_id,
int with_unknown);
void destroy_nnt_devices(int is_alloc_chrdev_region);
void destroy_nnt_devices_bc(void);
int destroy_nnt_device_bc(struct nnt_device* nnt_device);
int is_pciconf_device(unsigned short pci_device_id);
int is_pcicr_device(unsigned short pci_device_id);
int get_amount_of_nvidia_devices(void);
int set_private_data(struct file* file);
void set_private_data_open(struct file* file);
int set_private_data_bc(struct file* file, unsigned int bus, unsigned int devfn, unsigned int domain);
int get_nnt_device(struct file* file, struct nnt_device** nnt_device);
int mutex_lock_nnt(struct file* file);
void mutex_unlock_nnt(struct file* file);
#endif

View File

@@ -0,0 +1,169 @@
#ifndef NNT_DEVICE_DEFS_H
#define NNT_DEVICE_DEFS_H
#include <linux/pci.h>
#include <linux/list.h>
#include <linux/cdev.h>
#include "nnt_ioctl_defs.h"
#define NNT_DEVICE_ID_OFFSET 0xf0014
#define NNT_WO_REG_ADDR_DATA 0xbadacce5
#define NNT_NAME_SIZE 75
#define NNT_CONF_ADDRES_REGISETER 88
#define NNT_CONF_DATA_REGISTER 92
#define MELLANOX_VSEC_TYPE 0
#define PCI_TYPE_OFFSET 0x03
#define PCI_SEMAPHORE_OFFSET 0x0c
#define PCI_ADDRESS_OFFSET 0x10
#define PCI_DATA_OFFSET 0x14
#define NNT_MEMORY_SIZE 1024 * 1024
#define VSEC_CAPABILITY_ADDRESS 0x9
#define MSTFLINT_PCICONF_DEVICE_NAME "mstconf"
#define MSTFLINT_MEMORY_DEVICE_NAME "mstcr"
#define MFT_PCICONF_DEVICE_NAME "pciconf"
#define MFT_MEMORY_DEVICE_NAME "pci_cr"
#define MST_BC_BUFFER_SIZE 256
#define MST_BC_MAX_MINOR 256
// Mellanox Vendor ID.
#define NNT_MELLANOX_PCI_VENDOR 0x15b3
// NVIDIA Vendor ID.
#define NNT_NVIDIA_PCI_VENDOR 0x10de
// Livefish Device ID range.
#define CONNECTX3_LIVEFISH_ID 502
// PCI Device IDs.
#define CONNECTX3_PCI_ID 4099
#define CONNECTX3PRO_PCI_ID 4103
#define CONNECTIB_PCI_ID 4113
#define CONNECTX4_PCI_ID 4115
#define CONNECTX4LX_PCI_ID 4117
#define CONNECTX5_PCI_ID 4119
#define CONNECTX5EX_PCI_ID 4121
#define CONNECTX6_PCI_ID 4123
#define CONNECTX6DX_PCI_ID 4125
#define CONNECTX6LX_PCI_ID 4127
#define CONNECTX7_PCI_ID 4129
#define CONNECTX8_PCI_ID 4131
#define SCHRODINGER_PCI_ID 6518
#define FREYSA_PCI_ID 6521
#define BLUEFIELD_PCI_ID 41682
#define BLUEFIELD2_PCI_ID 41686
#define BLUEFIELD_DPU_AUX_PCI_ID 49873
#define BLUEFIELD3_PCI_ID 41692
#define BLUEFIELD4_PCI_ID 41694
#define SWITCHIB_PCI_ID 52000
#define SWITCHIB2_PCI_ID 53000
#define QUANTUM_PCI_ID 54000
#define QUANTUM2_PCI_ID 54002
#define QUANTUM3_PCI_ID 54004
#define SPECTRUM_PCI_ID 52100
#define SPECTRUM2_PCI_ID 53100
#define SPECTRUM3_PCI_ID 53104
#define SPECTRUM4_PCI_ID 53120
#define BW00_PCI_ID 10496
#define BW02_PCI_ID 10624
enum nnt_device_type_flag
{
NNT_PCICONF_DEVICES = 0x01,
NNT_PCI_DEVICES,
NNT_ALL_DEVICES
};
struct nnt_dma_page
{
struct page** page_list;
dma_addr_t* dma_address_list;
};
enum nnt_device_type
{
NNT_PCICONF,
NNT_PCICONF_NO_VSEC,
NNT_PCI_MEMORY
};
struct supported_address_space
{
int cr_space;
int icmd;
int semaphore;
};
/* Forward declaration */
struct nnt_device;
typedef int (*callback_read)(struct nnt_device* nnt_device, struct nnt_rw_operation* read_operation);
typedef int (*callback_write)(struct nnt_device* nnt_device, struct nnt_rw_operation* write_operation);
typedef int (*callback_init)(struct nnt_device* nnt_device);
struct nnt_access_callback
{
callback_read read;
callback_write write;
callback_init init;
};
struct nnt_device_pciconf
{
struct supported_address_space address_space;
unsigned int address_register;
unsigned int data_register;
unsigned int semaphore_offset;
unsigned int data_offset;
unsigned int address_offset;
unsigned int vendor_specific_capability;
unsigned int vsec_capability_mask;
unsigned int vsec_fully_supported;
};
struct nnt_device_pci
{
unsigned long long bar_address;
unsigned int bar_size;
};
struct nnt_device_memory
{
unsigned int pci_memory_bar_address;
unsigned int connectx_wa_slot_p1;
void* hardware_memory_address;
};
struct nnt_device_dbdf
{
unsigned int domain;
unsigned int bus;
unsigned int devfn;
};
struct nnt_device
{
struct nnt_device_pciconf pciconf_device;
struct nnt_device_memory memory_device;
struct nnt_access_callback access;
struct nnt_device_pci device_pci;
struct pci_dev* pci_device;
struct nnt_dma_page dma_page;
struct list_head entry;
struct cdev mcdev;
struct mutex lock;
struct nnt_device_dbdf dbdf;
enum nnt_device_type device_type;
int vpd_capability_address;
int device_minor_number;
int wo_address;
int buffer_used_bc;
int device_enabled;
char device_name[NNT_NAME_SIZE];
char buffer_bc[MST_BC_BUFFER_SIZE];
unsigned int connectx_wa_slot_p1;
unsigned char connectx_wa_slots;
dev_t device_number;
};
#endif

View File

@@ -0,0 +1,196 @@
#include <linux/kernel.h>
#include <linux/pci.h>
#include "nnt_device_defs.h"
#include "nnt_pci_conf_access_defs.h"
#include "nnt_pci_conf_access.h"
#include "nnt_defs.h"
#include "nnt_ioctl_defs.h"
int dma_mapping_page(unsigned int total_pinned, unsigned int page_mapped_counter,
struct nnt_device* nnt_device, struct page** current_pages,
struct nnt_page_info* page_info)
{
int current_page = total_pinned + page_mapped_counter;
int error_code = 0;
/* Get the dma address. */
nnt_device->dma_page.dma_address_list[current_page] =
dma_map_page(&nnt_device->pci_device->dev, current_pages[current_page],
0, PAGE_SIZE,
DMA_BIDIRECTIONAL);
/* Do we get a valid dma address ? */
if (dma_mapping_error(&nnt_device->pci_device->dev, nnt_device->dma_page.dma_address_list[current_page])) {
printk(KERN_ERR "Failed to get DMA addresses\n");
error_code = -EINVAL;
goto ReturnOnFinsihed;
}
page_info->page_address_array[current_page].dma_address =
nnt_device->dma_page.dma_address_list[current_page];
ReturnOnFinsihed:
return error_code;
}
int pin_user_pages_in_kernel_space(unsigned int total_pages, unsigned int total_pinned,
int* pinned_pages, struct nnt_device* nnt_device,
struct nnt_page_info* page_info, struct page*** current_pages)
{
unsigned long page_pointer_start = page_info->page_pointer_start;
int error_code = 0;
/* Remaining pages to pin. */
int remaining_pages = total_pages - total_pinned;
/* Point to the current offset. */
uint64_t current_page_pointer = page_pointer_start + (total_pinned * PAGE_SIZE);
/* Save the current page. */
*current_pages = nnt_device->dma_page.page_list + total_pinned;
/* Returns number of pages pinned - this may be fewer than the number requested
or -errno in case of error. */
*pinned_pages = get_user_pages_fast(current_page_pointer, remaining_pages,
FOLL_WRITE, *current_pages);
if (*pinned_pages < 1) {
kfree(nnt_device->dma_page.page_list);
error_code = -EFAULT;
}
/* Allocate dma addresses structure. */
if ((nnt_device->dma_page.dma_address_list =
kcalloc(total_pages, sizeof(dma_addr_t), GFP_KERNEL)) == NULL) {
error_code = -ENOMEM;
}
return error_code;
}
int pin_user_memory_in_kernel_space(unsigned int total_pages, struct nnt_device* nnt_device,
struct nnt_page_info* page_info)
{
unsigned int page_mapped_counter= 0;
unsigned int total_pinned = 0;
unsigned int pinned_pages;
struct page** current_pages = NULL;
int error_code = 0;
while (total_pinned < total_pages) {
/* Pinning user pages in kernel space. */
if((error_code =
pin_user_pages_in_kernel_space(total_pages, total_pinned,
&pinned_pages, nnt_device,
page_info, &current_pages)) != 0)
goto ReturnOnFinished;
/* When the parameter 'inter_iommu' is on, we need to set up
a mapping on a pages in order to access the physical address. */
while(page_mapped_counter < pinned_pages)
{
if((error_code =
dma_mapping_page(total_pinned, page_mapped_counter,
nnt_device, current_pages,
page_info)) != 0)
goto ReturnOnFinished;
page_mapped_counter++;
}
/* Advance the memory that already pinned. */
total_pinned += pinned_pages;
}
/* There is a page not pinned in the kernel space ? */
if (total_pinned != total_pages) {
return -EFAULT;
}
ReturnOnFinished:
return error_code;
}
int map_dma_pages(struct nnt_page_info* page_info, struct nnt_device* nnt_device)
{
unsigned int total_pages = page_info->total_pages;
int page_counter = 0;
int error_code = 0;
/* Check if we allow locking memory. */
if (!can_do_mlock()) {
error_code = -EPERM;
goto ReturnOnFinished;
}
/* Allocate the page list. */
if ((nnt_device->dma_page.page_list =
kcalloc(total_pages, sizeof(struct page *), GFP_KERNEL)) == NULL) {
error_code = -ENOMEM;
goto ReturnOnFinished;
}
/* Go over the user memory buffer and pin user pages in kernel space */
if((error_code =
pin_user_memory_in_kernel_space(total_pages, nnt_device,
page_info)) != 0)
goto ReturnOnFinished;
for (page_counter = 0;
page_counter < total_pages;
page_counter++) {
printk(KERN_INFO "Page address structure number: %d, device: %04x:%02x:%02x.%0x\n",
page_counter, pci_domain_nr(nnt_device->pci_device->bus),
nnt_device->pci_device->bus->number, PCI_SLOT(nnt_device->pci_device->devfn),
PCI_FUNC(nnt_device->pci_device->devfn));
}
ReturnOnFinished:
return error_code;
}
int release_dma_pages(struct nnt_page_info* page_info, struct nnt_device* nnt_device)
{
int page_counter;
if (nnt_device->dma_page.page_list == NULL) {
return 0;
}
/* Deallocate the pages. */
for (page_counter = 0;
page_counter < page_info->total_pages;
page_counter++) {
/* DMA activity is finished. */
dma_unmap_page(&nnt_device->pci_device->dev, nnt_device->dma_page.dma_address_list[page_counter],
PAGE_SIZE, DMA_BIDIRECTIONAL);
/* Release the page list. */
set_page_dirty(nnt_device->dma_page.page_list[page_counter]);
put_page(nnt_device->dma_page.page_list[page_counter]);
nnt_device->dma_page.page_list[page_counter] = NULL;
nnt_device->dma_page.dma_address_list[page_counter] = 0;
printk(KERN_INFO "Page structure number: %d was released. device:%04x:%02x:%02x.%0x\n",
page_counter, pci_domain_nr(nnt_device->pci_device->bus),
nnt_device->pci_device->bus->number, PCI_SLOT(nnt_device->pci_device->devfn),
PCI_FUNC(nnt_device->pci_device->devfn));
}
// All the pages are clean.
nnt_device->dma_page.page_list = NULL;
return 0;
}

View File

@@ -0,0 +1,10 @@
#ifndef NNT_DMA_H
#define NNT_DMA_H
#include "nnt_ioctl_defs.h"
#include "nnt_device_defs.h"
int map_dma_pages(struct nnt_page_info* page_info, struct nnt_device* nnt_device);
int release_dma_pages(struct nnt_page_info* page_info, struct nnt_device* nnt_device);
#endif

View File

@@ -0,0 +1,230 @@
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/uaccess.h>
#include <linux/highmem.h>
#include <linux/sched.h>
#include "nnt_dma.h"
#include "nnt_defs.h"
#include "nnt_pci_conf_access.h"
unsigned int mask = 0x7fff;
unsigned int msb_mask = 0x8000;
int dma_pages_ioctl(unsigned int command, void* user_buffer,
struct nnt_device* nnt_device)
{
struct nnt_page_info page_info;
int error_code = 0;
/* Copy the page info structure from user space. */
if (copy_from_user(&page_info, user_buffer,
sizeof(struct nnt_page_info))) {
error_code = -EFAULT;
goto ReturnOnFinished;
}
if (command == NNT_GET_DMA_PAGES) {
if (map_dma_pages(&page_info, nnt_device)) {
goto ReturnOnFinished;
}
/* Return the physical address to the user */
if (copy_to_user(user_buffer, &page_info,
sizeof(struct nnt_page_info)) != 0) {
error_code = -EFAULT;
goto ReturnOnFinished;
}
} else {
error_code =
release_dma_pages(&page_info, nnt_device);
}
ReturnOnFinished:
return error_code;
}
int read_dword_ioctl(void* user_buffer, struct nnt_device* nnt_device)
{
struct nnt_read_dword_from_config_space read_from_cspace;
int error_code = 0;
/* Copy the request from user space. */
if (copy_from_user(&read_from_cspace, user_buffer,
sizeof(struct nnt_read_dword_from_config_space)) != 0) {
error_code = -EFAULT;
goto ReturnOnFinished;
}
/* Read the dword. */
if (read_dword(&read_from_cspace, nnt_device)) {
goto ReturnOnFinished;
}
/* Copy the data to the user space. */
if (copy_to_user(user_buffer, &read_from_cspace,
sizeof(struct nnt_read_dword_from_config_space)) != 0) {
error_code = -EFAULT;
goto ReturnOnFinished;
}
ReturnOnFinished:
return error_code;
}
int get_nnt_device_parameters(struct nnt_device_parameters* nnt_parameters, struct nnt_device* nnt_device)
{
int error = 0;
nnt_parameters->domain = pci_domain_nr(nnt_device->pci_device->bus);
nnt_parameters->bus = nnt_device->pci_device->bus->number;
nnt_parameters->slot = PCI_SLOT(nnt_device->pci_device->devfn);
nnt_parameters->function = PCI_FUNC(nnt_device->pci_device->devfn);
nnt_parameters->pci_memory_bar_address= nnt_device->memory_device.pci_memory_bar_address;
nnt_parameters->device = nnt_device->pci_device->device;
nnt_parameters->vendor = nnt_device->pci_device->vendor;
nnt_parameters->subsystem_device = nnt_device->pci_device->subsystem_device;
nnt_parameters->multifunction = nnt_device->pci_device->multifunction;
nnt_parameters->subsystem_vendor = nnt_device->pci_device->subsystem_vendor;
check_address_space_support(nnt_device);
if (nnt_device->pciconf_device.vendor_specific_capability &&
(nnt_device->pciconf_device.address_space.icmd || nnt_device->pciconf_device.address_space.cr_space ||
nnt_device->pciconf_device.address_space.semaphore)) {
nnt_parameters->vendor_specific_capability = nnt_device->pciconf_device.vendor_specific_capability;
nnt_parameters->vsec_capability_mask = nnt_device->pciconf_device.vsec_capability_mask;
} else {
nnt_parameters->vendor_specific_capability = 0;
}
return error;
}
int pci_connectx_wa(struct nnt_connectx_wa* connectx_wa, struct nnt_device* nnt_device)
{
unsigned int slot_mask;
int error = 0;
/* Is this slot exists ? */
if (nnt_device->memory_device.connectx_wa_slot_p1) {
printk(KERN_DEBUG "Slot exits for file %s, slot:0x%x\n",
nnt_device->device_name, nnt_device->memory_device.connectx_wa_slot_p1);
error = 0;
goto ReturnOnFinished;
}
/* Find first un(set) bit. and remember the slot */
nnt_device->memory_device.connectx_wa_slot_p1= ffs(~nnt_device->memory_device.connectx_wa_slot_p1);
if (nnt_device->memory_device.connectx_wa_slot_p1 == 0 ||
nnt_device->memory_device.connectx_wa_slot_p1 > NNT_CONNECTX_WA_SIZE) {
error = -ENOLCK;
goto ReturnOnFinished;
}
slot_mask = 1 << (nnt_device->memory_device.connectx_wa_slot_p1 - 1);
/* set the slot as taken */
nnt_device->memory_device.connectx_wa_slot_p1 |= slot_mask;
connectx_wa->connectx_wa_slot_p1 = nnt_device->memory_device.connectx_wa_slot_p1;
ReturnOnFinished:
return error;
}
int vpd_read(struct nnt_vpd* vpd, struct nnt_device* nnt_device)
{
unsigned long jiffies_time;
unsigned int address;
unsigned short data;
int is_bit_set = 0;
int error;
/* Sets F bit to zero and write VPD address. */
address = mask & vpd->offset;
error = pci_write_config_word(nnt_device->pci_device, nnt_device->vpd_capability_address + PCI_VPD_ADDR,
address);
CHECK_PCI_WRITE_ERROR(error, nnt_device->vpd_capability_address + PCI_VPD_ADDR,
address);
/* Wait for data until F bit is set with one */
jiffies_time = msecs_to_jiffies(vpd->timeout) + jiffies;
while (time_before(jiffies, jiffies_time)) {
error = pci_read_config_word(nnt_device->pci_device, nnt_device->vpd_capability_address + PCI_VPD_ADDR,
&data);
CHECK_PCI_READ_ERROR(error, nnt_device->vpd_capability_address + PCI_VPD_ADDR);
if (data & msb_mask) {
is_bit_set = 1;
break;
}
cond_resched();
}
if (!is_bit_set) {
printk(KERN_ERR "Failed to retrieve valid data\n");
return -ETIMEDOUT;
}
/* read data */
error = pci_read_config_dword(nnt_device->pci_device, nnt_device->vpd_capability_address + PCI_VPD_DATA,
&vpd->data);
CHECK_PCI_READ_ERROR(error, nnt_device->vpd_capability_address + PCI_VPD_DATA);
ReturnOnFinished:
return error;
}
int vpd_write(struct nnt_vpd* vpd, struct nnt_device* nnt_device)
{
unsigned long jiffies_time;
unsigned int address;
unsigned short data;
int is_bit_set = 0;
int error;
/* Write the user data */
error = pci_write_config_dword(nnt_device->pci_device, nnt_device->vpd_capability_address + PCI_VPD_DATA,
vpd->data);
CHECK_PCI_WRITE_ERROR(error, nnt_device->vpd_capability_address + PCI_VPD_DATA,
vpd->data);
/* sets F bit to one and write VPD addr */
address = msb_mask | (mask & vpd->offset);
error = pci_write_config_word(nnt_device->pci_device, nnt_device->vpd_capability_address + PCI_VPD_ADDR,
address);
CHECK_PCI_WRITE_ERROR(error, nnt_device->vpd_capability_address + PCI_VPD_ADDR,
address);
/* wait for data until F bit is set with zero */
jiffies_time = msecs_to_jiffies(vpd->timeout) + jiffies;
while (time_before(jiffies, jiffies_time)) {
error = pci_read_config_word(nnt_device->pci_device, nnt_device->vpd_capability_address + PCI_VPD_ADDR,
&data);
CHECK_PCI_READ_ERROR(error, nnt_device->vpd_capability_address + PCI_VPD_ADDR);
if (!(data & msb_mask)) {
is_bit_set = 1;
break;
}
cond_resched();
}
if (!is_bit_set) {
printk(KERN_ERR "Failed to retrieve valid data\n");
return -ETIMEDOUT;
}
ReturnOnFinished:
return error;
}

View File

@@ -0,0 +1,14 @@
#ifndef NNT_IOCTL_H
#define NNT_IOCTL_H
#include "nnt_device_defs.h"
int dma_pages_ioctl(unsigned int command, void* user_buffer,
struct nnt_device* nnt_device);
int read_dword_ioctl(unsigned int command, void* user_buffer,
struct nnt_device* nnt_device);
int get_nnt_device_parameters(struct nnt_device_parameters* nnt_parameters, struct nnt_device* nnt_device);
int pci_connectx_wa(struct nnt_connectx_wa* connectx_wa, struct nnt_device* nnt_device);
int vpd_read(struct nnt_vpd* vpd, struct nnt_device* nnt_device);
int vpd_write(struct nnt_vpd* vpd, struct nnt_device* nnt_device);
#endif

View File

@@ -0,0 +1,83 @@
#ifndef NNT_IOCTL_DEFS_H
#define NNT_IOCTL_DEFS_H
#define NNT_MAGIC_NUMBER 0xD3
#define MAX_BUFFER_BLOCK_SIZE 256
#define NNT_MAX_PAGES_SIZE 8
#define NNT_CONNECTX_WA_SIZE 3
#define NNT_WRITE _IOW (NNT_MAGIC_NUMBER, 1, struct nnt_rw_operation)
#define NNT_READ _IOW (NNT_MAGIC_NUMBER, 2, struct nnt_rw_operation)
#define NNT_GET_DMA_PAGES _IOR (NNT_MAGIC_NUMBER, 3, struct nnt_page_info)
#define NNT_RELEASE_DMA_PAGES _IOR (NNT_MAGIC_NUMBER, 4, struct nnt_page_info)
#define NNT_READ_DWORD_FROM_CONFIG_SPACE _IOR (NNT_MAGIC_NUMBER, 5, struct nnt_read_dword_from_config_space)
#define NNT_GET_DEVICE_PARAMETERS _IOR (NNT_MAGIC_NUMBER, 6, struct nnt_device_parameters)
#define NNT_INIT _IOR (NNT_MAGIC_NUMBER, 7, struct nnt_pciconf_init)
#define NNT_PCI_CONNECTX_WA _IOR (NNT_MAGIC_NUMBER, 8, u_int32_t)
#define NNT_VPD_READ _IOR (NNT_MAGIC_NUMBER, 9, struct nnt_vpd)
#define NNT_VPD_WRITE _IOW (NNT_MAGIC_NUMBER, 10, struct nnt_vpd)
struct nnt_vpd {
unsigned int offset;
unsigned int timeout;
unsigned int data;
};
struct nnt_pciconf_init {
unsigned int address_register;
unsigned int address_data_register;
};
struct nnt_device_parameters {
unsigned int domain;
unsigned int bus;
unsigned int slot;
unsigned int function;
unsigned int pci_memory_bar_address;
unsigned int device;
unsigned int vendor;
unsigned int subsystem_device;
unsigned int subsystem_vendor;
unsigned int multifunction;
unsigned int vendor_specific_capability;
unsigned int vsec_capability_mask;
};
struct nnt_page_address {
u_int64_t dma_address;
u_int64_t virtual_address;
};
struct nnt_page_info {
unsigned int total_pages;
unsigned long page_pointer_start;
struct nnt_page_address page_address_array[NNT_MAX_PAGES_SIZE];
};
struct nnt_read_dword_from_config_space {
unsigned int offset;
unsigned int data;
};
struct nnt_rw_operation {
unsigned int address_space;
unsigned int offset;
int size;
unsigned int data[MAX_BUFFER_BLOCK_SIZE / 4];
};
struct nnt_connectx_wa {
unsigned int connectx_wa_slot_p1;
};
#endif

View File

@@ -0,0 +1,295 @@
/*
* Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* BSD-3-Clause
*
* 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 copyright holder 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 HOLDER 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 <linux/module.h>
#include <linux/init.h>
#include <linux/semaphore.h>
#include <linux/uaccess.h>
#include <linux/kernel.h>
#include "nnt_defs.h"
#include "nnt_device.h"
#include "nnt_ioctl.h"
#include "nnt_ioctl_defs.h"
MODULE_AUTHOR("Itay Avraham <itayavr@nvidia.com>");
MODULE_DESCRIPTION("NNT Linux driver (NVIDIA® networking tools driver)");
/* Passing MFT flag argument */
int mft_package = 0;
/* Create the file in sysfs. */
module_param(mft_package, int, S_IRUSR);
struct driver_info nnt_driver_info;
static long nnt_ioctl(struct file* file, unsigned int command, unsigned long argument)
{
void* user_buffer = (void*)argument;
struct nnt_device* nnt_device = NULL;
int error;
/* By convention, any user gets read access
* and is allowed to use the device.
* Commands with no direction are administration
* commands, and you need write permission for this */
if (_IOC_DIR(command) == _IOC_NONE)
{
if (!(file->f_mode & FMODE_WRITE))
{
return -EPERM;
}
}
else
{
if (!(file->f_mode & FMODE_READ))
{
return -EPERM;
}
}
error = mutex_lock_nnt(file);
CHECK_ERROR(error);
/* Get the nnt device structure */
error = get_nnt_device(file, &nnt_device);
if (error)
{
goto ReturnOnFinished;
}
switch (command)
{
case NNT_GET_DMA_PAGES:
case NNT_RELEASE_DMA_PAGES:
error = dma_pages_ioctl(command, user_buffer, nnt_device);
break;
case NNT_READ_DWORD_FROM_CONFIG_SPACE:
error = read_dword_ioctl(command, user_buffer, nnt_device);
break;
case NNT_WRITE:
case NNT_READ:
{
struct nnt_rw_operation rw_operation;
/* Copy the request from user space. */
if (copy_from_user(&rw_operation, user_buffer, sizeof(struct nnt_rw_operation)) != 0)
{
error = -EFAULT;
goto ReturnOnFinished;
}
switch (command)
{
case NNT_WRITE:
nnt_device->access.write(nnt_device, &rw_operation);
break;
case NNT_READ:
nnt_device->access.read(nnt_device, &rw_operation);
break;
}
/* Copy the data to the user space. */
if (copy_to_user(user_buffer, &rw_operation, sizeof(struct nnt_rw_operation)) != 0)
{
error = -EFAULT;
goto ReturnOnFinished;
}
break;
}
case NNT_GET_DEVICE_PARAMETERS:
{
struct nnt_device_parameters nnt_parameters;
error = get_nnt_device_parameters(&nnt_parameters, nnt_device);
/* Copy the data to the user space. */
if (copy_to_user(user_buffer, &nn_parameters, sizeof(struct device_parameters)) != 0)
{
error = -EFAULT;
goto ReturnOnFinished;
}
break;
}
case NNT_INIT:
{
struct nnt_pciconf_init init;
/* Copy the request from user space. */
if (copy_from_user(&init, user_buffer, sizeof(struct nnt_pciconf_init)) != 0)
{
error = -EFAULT;
goto ReturnOnFinished;
}
error = nnt_device->access.init(&init, nnt_device);
break;
}
case NNT_PCI_CONNECTX_WA:
{
struct nnt_connectx_wa connectx_wa;
error = pci_connectx_wa(&connectx_wa, nnt_device);
/* Copy the data to the user space. */
if (copy_to_user(user_buffer, &connectx_wa, sizeof(struct nnt_connectx_wa)) != 0)
{
error = -EFAULT;
goto ReturnOnFinished;
}
break;
}
case NNT_VPD_READ:
case NNT_VPD_WRITE:
{
int vpd_default_timeout = 2000;
struct nnt_vpd vpd;
if (!nnt_device->vpd_capability_address)
{
printk(KERN_ERR "Device %s not support Vital Product Data\n", nnt_device->device_name);
return -ENODEV;
}
/* Copy the request from user space. */
if (copy_from_user(&vpd, user_buffer, sizeof(struct nnt_vpd)) != 0)
{
error = -EFAULT;
goto ReturnOnFinished;
}
if (!vpd.timeout)
{
vpd.timeout = vpd_default_timeout;
}
switch (command)
{
case NNT_VPD_READ:
error = vpd_read(command, &vpd, nnt_device);
break;
case NNT_VPD_WRITE:
error = vpd_write(command, &vpd, nnt_device);
break;
}
break;
}
default:
printk(KERN_ERR "ioctl not implemented, command id: %x\n", command);
error = -EINVAL;
break;
}
ReturnOnFinished:
mutex_unlock_nnt(file);
return error;
}
static int nnt_open(struct inode* inode, struct file* file)
{
if (file->private_data)
{
return 0;
}
return set_private_data(inode, file);
}
struct file_operations fop = {.unlocked_ioctl = ioctl, .open = nnt_open, .owner = THIS_MODULE};
int with_unknown = 0;
module_param(with_unknown, int, S_IRUSR | S_IWUSR);
static int __init nnt_init_module(void)
{
int first_minor_number = 0;
int error = 0;
dev_t device_numbers;
/* Get the amount of the Nvidia devices. */
if ((nnt_driver_info.contiguous_device_numbers = get_amount_of_nvidia_devices()) == 0)
{
printk(KERN_ERR "No devices found\n");
goto ReturnOnFinished;
}
/* Allocate char driver region and assign major number */
if ((error = alloc_chrdev_region(&device_numbers, first_minor_number, nnt_driver_info.contiguous_device_numbers,
NNT_DRIVER_NAME)) != 0)
{
printk(KERN_ERR "failed to allocate chrdev_region\n");
goto CharDeviceAllocated;
}
nnt_driver_info.driver_major_number = MAJOR(device_numbers);
/* create sysfs class. */
if ((nnt_driver_info.class_driver = class_create(THIS_MODULE, NNT_CLASS_NAME)) == NULL)
{
printk(KERN_ERR "Class creation failed\n");
error = -EFAULT;
goto DriverClassAllocated;
}
/* Create device files for MSTflint and MFT */
error = create_nnt_devices(nnt_driver_info.contiguous_device_numbers, device_numbers, mft_package,
NNT_MELLANOX_PCI_VENDOR, &fop) ||
create_nnt_devices(nnt_driver_info.contiguous_device_numbers, device_numbers, mft_package,
NNT_NVIDIA_PCI_VENDOR, &fop);
if ((error) == 0)
{
goto ReturnOnFinished;
}
DriverClassAllocated:
destroy_nnt_devices();
class_destroy(nnt_driver_info.class_driver);
CharDeviceAllocated:
unregister_chrdev_region(nnt_driver_info.driver_major_number, nnt_driver_info.contiguous_device_numbers);
ReturnOnFinished:
return error;
}
static void __exit nnt_cleanup_module(void)
{
destroy_nnt_devices();
class_destroy(nnt_driver_info.class_driver);
unregister_chrdev_region(nnt_driver_info.driver_major_number, nnt_driver_info.contiguous_device_numbers);
}
module_init(nnt_init_module);
module_exit(nnt_cleanup_module);

View File

@@ -0,0 +1,46 @@
#include "nnt_device_defs.h"
#include "nnt_pci_conf_access_defs.h"
#include "nnt_pci_conf_access.h"
#include "nnt_defs.h"
int write_memory(struct nnt_device* nnt_device, struct nnt_rw_operation* write_operation)
{
/* Endianness conversion. */
cpu_to_be32s(write_operation->data);
write_operation->data[0] = cpu_to_le32(write_operation->data[0]);
/* Write to the hardware memory address. */
iowrite32(write_operation->data[0], nnt_device->memory_device.hardware_memory_address + write_operation->offset);
return 0;
}
int read_memory(struct nnt_device* nnt_device, struct nnt_rw_operation* read_operation)
{
/* Read from the hardware memory address. */
read_operation->data[0] = ioread32(nnt_device->memory_device.hardware_memory_address + read_operation->offset);
/* Endianness conversion */
be32_to_cpus(read_operation->data);
read_operation->data[0] = cpu_to_le32(read_operation->data[0]);
return 0;
}
int init_memory(struct nnt_device* nnt_device)
{
nnt_device->memory_device.connectx_wa_slot_p1 = 0;
nnt_device->memory_device.hardware_memory_address =
ioremap(pci_resource_start(nnt_device->pci_device, nnt_device->memory_device.pci_memory_bar_address),
NNT_MEMORY_SIZE);
if (nnt_device->memory_device.hardware_memory_address <= 0) {
printk(KERN_ERR "could not map device memory\n");
}
return 0;
}

View File

@@ -0,0 +1,7 @@
#ifndef NNT_MEMORY_ACCESS_H
#define NNT_MEMORY_ACCESS_H
int read_memory(struct nnt_device* nnt_device, struct nnt_rw_operation* read_operation);
int write_memory(struct nnt_device* nnt_device, struct nnt_rw_operation* write_operation);
int init_memory(struct nnt_device* nnt_device);
#endif

View File

@@ -0,0 +1,430 @@
#include "nnt_device_defs.h"
#include "nnt_pci_conf_access_defs.h"
#include "nnt_pci_conf_access.h"
#include "nnt_defs.h"
#include <linux/delay.h>
#include <linux/uaccess.h>
int clear_vsec_semaphore(struct nnt_device* nnt_device)
{
/* Clear the semaphore. */
int error = pci_write_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.semaphore_offset,
0);
CHECK_PCI_WRITE_ERROR(error, nnt_device->pciconf_device.semaphore_offset,
0)
ReturnOnFinished:
return error;
}
int get_semaphore_ticket(struct nnt_device* nnt_device, unsigned int* lock_value,
unsigned int* counter)
{
unsigned int counter_offset = nnt_device->pciconf_device.vendor_specific_capability + PCI_COUNTER_OFFSET;
int error = 0;
/* Read ticket. */
error = pci_read_config_dword(nnt_device->pci_device, counter_offset,
counter);
CHECK_PCI_READ_ERROR(error, counter_offset);
/* Write to semaphore ticket. */
error = pci_write_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.semaphore_offset,
*counter);
CHECK_PCI_WRITE_ERROR(error, nnt_device->pciconf_device.semaphore_offset,
*counter);
/* Read back semaphore to make sure the
* ticket is equal to semphore */
error = pci_read_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.semaphore_offset,
lock_value);
CHECK_PCI_READ_ERROR(error, nnt_device->pciconf_device.semaphore_offset);
ReturnOnFinished:
return error;
}
int lock_vsec_semaphore(struct nnt_device* nnt_device)
{
unsigned int lock_value = -1;
unsigned int counter = 0;
unsigned int retries = 0;
int error = 0;
do {
if (retries > SEMAPHORE_MAX_RETRIES) {
return -EINVAL;
}
/* Read the semaphore, until we will get 0. */
error = pci_read_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.semaphore_offset,
&lock_value);
CHECK_PCI_READ_ERROR(error, nnt_device->pciconf_device.semaphore_offset);
/* Is semaphore taken ? */
if (lock_value) {
retries++;
udelay(1000);
continue;
}
/* Get semaphore ticket */
error = get_semaphore_ticket(nnt_device, &lock_value,
&counter);
CHECK_ERROR(error);
} while (counter != lock_value);
ReturnOnFinished:
return error;
}
int read_dword(struct nnt_read_dword_from_config_space* read_from_cspace, struct nnt_device* nnt_device)
{
int error = 0;
/* Take semaphore. */
error = lock_vsec_semaphore(nnt_device);
CHECK_ERROR(error);
/* Read dword from config space. */
error = pci_read_config_dword(nnt_device->pci_device, read_from_cspace->offset,
&read_from_cspace->data);
CHECK_PCI_READ_ERROR(error, read_from_cspace->offset);
ReturnOnFinished:
/* Clear semaphore. */
clear_vsec_semaphore(nnt_device);
return error;
}
int wait_on_flag(struct nnt_device* nnt_device, u8 expected_val)
{
unsigned int flag = 0;
int retries = 0;
int error = -1;
for (retries = 0; retries < IFC_MAX_RETRIES; retries++) {
/* Read the flag. */
error = pci_read_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.address_offset,
&flag);
CHECK_PCI_READ_ERROR(error, nnt_device->pciconf_device.address_offset);
flag = EXTRACT(flag, PCI_FLAG_BIT_OFFSET,
1);
if (flag == expected_val) {
return 0;
}
}
ReturnOnFinished:
return error;
}
int set_address_space(struct nnt_device* nnt_device, unsigned int address_space)
{
unsigned int control_offset = nnt_device->pciconf_device.vendor_specific_capability + PCI_CONTROL_OFFSET;
unsigned int value = 0;
int error = 0;
/* Read value from control offset. */
error = pci_read_config_dword(nnt_device->pci_device, control_offset,
&value);
CHECK_PCI_READ_ERROR(error, control_offset);
/* Set the bit address_space indication and write it back. */
value = MERGE(value, address_space,
PCI_SPACE_BIT_OFFSET, PCI_SPACE_BIT_LENGTH);
error = pci_write_config_dword(nnt_device->pci_device, control_offset,
value);
CHECK_PCI_WRITE_ERROR(error, control_offset,
value);
/* Read status and make sure address_space is supported. */
error = pci_read_config_dword(nnt_device->pci_device, control_offset,
&value);
CHECK_PCI_READ_ERROR(error, control_offset);
if (EXTRACT(value, PCI_STATUS_BIT_OFFSET,
PCI_STATUS_BIT_LEN) == 0) {
error = -EINVAL;
}
ReturnOnFinished:
return error;
}
int check_address_space_support(struct nnt_device* nnt_device)
{
int error = 0;
if ((!nnt_device->pciconf_device.vendor_specific_capability) || (!nnt_device->pci_device)) {
return 0;
}
/* Get semaphore ticket */
error = lock_vsec_semaphore(nnt_device);
CHECK_ERROR(error);
/* Is ICMD address space supported ?*/
if(set_address_space(nnt_device, ADDRESS_SPACE_ICMD) == 0) {
nnt_device->pciconf_device.address_space.icmd = 1;
}
/* Is CR Space address space supported ?*/
if(set_address_space(nnt_device, ADDRESS_SPACE_CR_SPACE) == 0) {
nnt_device->pciconf_device.address_space.cr_space = 1;
}
/* Is semaphore address space supported ?*/
if(set_address_space(nnt_device, ADDRESS_SPACE_SEMAPHORE) == 0) {
nnt_device->pciconf_device.address_space.semaphore = 1;
}
ReturnOnFinished:
/* Clear semaphore. */
clear_vsec_semaphore(nnt_device);
return 0;
}
int set_rw_address(unsigned int* offset, unsigned int rw)
{
u32 address = *offset;
/* Last 2 bits must be zero as we only allow 30 bits addresses. */
if (EXTRACT(address, 30,
2)) {
return -1;
}
address = MERGE(address, rw,
PCI_FLAG_BIT_OFFSET, 1);
*offset = address;
return 0;
}
int read(struct nnt_device* nnt_device, unsigned int offset,
unsigned int* data)
{
int error = set_rw_address(&offset, READ_OPERATION);
CHECK_ERROR(error);
/* Write address. */
error = pci_write_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.address_offset,
offset);
CHECK_PCI_WRITE_ERROR(error, nnt_device->pciconf_device.address_offset,
offset);
error = wait_on_flag(nnt_device, 1);
CHECK_ERROR(error);
/* Read data. */
error = pci_read_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.data_offset,
data);
CHECK_PCI_READ_ERROR(error, nnt_device->pciconf_device.data_offset);
ReturnOnFinished:
return error;
}
int read_pciconf(struct nnt_device* nnt_device, struct nnt_rw_operation* read_operation)
{
int counter = 0;
int error = 0;
/* Lock semaphore. */
error = lock_vsec_semaphore(nnt_device);
CHECK_ERROR(error);
/* Is CR Space address space supported ?*/
error = set_address_space(nnt_device, read_operation->address_space);
CHECK_ERROR(error);
for (counter = 0; counter < read_operation->size; counter += 4) {
if (read(nnt_device, read_operation->offset + counter,
&read_operation->data[counter >> 2])) {
error = counter;
goto ReturnOnFinished;
}
}
ReturnOnFinished:
/* Clear semaphore. */
clear_vsec_semaphore(nnt_device);
return error;
}
int write(struct nnt_device* nnt_device, unsigned int offset,
unsigned int data)
{
int error = set_rw_address(&offset, WRITE_OPERATION);
CHECK_ERROR(error);
/* Write data. */
error = pci_write_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.data_offset,
data);
CHECK_PCI_WRITE_ERROR(error,nnt_device->pciconf_device.data_offset,
data);
/* Write address. */
error = pci_write_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.address_offset,
offset);
CHECK_PCI_WRITE_ERROR(error, nnt_device->pciconf_device.address_offset,
offset);
error = wait_on_flag(nnt_device, 0);
ReturnOnFinished:
return error;
}
int write_pciconf(struct nnt_device* nnt_device, struct nnt_rw_operation* write_operation)
{
int counter = 0;
int error = 0;
/* Lock semaphore. */
error = lock_vsec_semaphore(nnt_device);
CHECK_ERROR(error);
/* Is CR Space address space supported ?*/
error = set_address_space(nnt_device, write_operation->address_space);
CHECK_ERROR(error);
for (counter = 0; counter < write_operation->size; counter += 4) {
if (write(nnt_device, write_operation->offset + counter,
write_operation->data[counter >> 2])) {
error = counter;
goto ReturnOnFinished;
}
}
ReturnOnFinished:
/* Clear semaphore. */
clear_vsec_semaphore(nnt_device);
return error;
}
int address_space_to_capability(u_int16_t address_space)
{
switch (address_space) {
case NNT_SPACE_ICMD:
return NNT_VSEC_ICMD_SPACE_SUPPORTED;
case NNT_SPACE_CR_SPACE:
return NNT_VSEC_CRSPACE_SPACE_SUPPORTED;
case NNT_SPACE_ALL_ICMD:
return NNT_VSEC_ALL_ICMD_SPACE_SUPPORTED;
case NNT_SPACE_NODNIC_INIT_SEG:
return NNT_VSEC_NODNIC_INIT_SEG_SPACE_SUPPORTED;
case NNT_SPACE_EXPANSION_ROM:
return NNT_VSEC_EXPANSION_ROM_SPACE_SUPPORTED;
case NNT_SPACE_ND_CR_SPACE:
return NNT_VSEC_ND_CRSPACE_SPACE_SUPPORTED;
case NNT_SPACE_SCAN_CR_SPACE:
return NNT_VSEC_SCAN_CRSPACE_SPACE_SUPPORTED;
case NNT_SPACE_GLOBAL_SEMAPHORE:
return NNT_VSEC_GLOBAL_SEMAPHORE_SPACE_SUPPORTED;
case NNT_SPACE_MAC:
return NNT_VSEC_MAC_SPACE_SUPPORTED;
default:
return 0;
}
}
int get_space_support_status(struct nnt_device* nnt_device, u_int16_t address_space)
{
int status = 0;
if(set_address_space(nnt_device, address_space) == 0) {
status = 1;
}
nnt_device->pciconf_device.vsec_capability_mask |=
(status << address_space_to_capability(address_space));
return status;
}
int init_vsec_capability_mask(struct nnt_device* nnt_device)
{
int error = 0;
/* Lock semaphore. */
error = lock_vsec_semaphore(nnt_device);
CHECK_ERROR(error);
get_space_support_status(nnt_device, NNT_SPACE_ICMD);
get_space_support_status(nnt_device, NNT_SPACE_CR_SPACE);
get_space_support_status(nnt_device, NNT_SPACE_ALL_ICMD);
get_space_support_status(nnt_device, NNT_SPACE_NODNIC_INIT_SEG);
get_space_support_status(nnt_device, NNT_SPACE_EXPANSION_ROM);
get_space_support_status(nnt_device, NNT_SPACE_ND_CR_SPACE);
get_space_support_status(nnt_device, NNT_SPACE_SCAN_CR_SPACE);
get_space_support_status(nnt_device, NNT_SPACE_GLOBAL_SEMAPHORE);
get_space_support_status(nnt_device, NNT_SPACE_MAC);
nnt_device->pciconf_device.vsec_capability_mask |= (1 << NNT_VSEC_INITIALIZED);
ReturnOnFinished:
/* Clear semaphore. */
clear_vsec_semaphore(nnt_device);
return 0;
}
void check_vsec_minimum_support(struct nnt_device* nnt_device)
{
if ((nnt_device->pciconf_device.vsec_capability_mask & (1 << NNT_VSEC_INITIALIZED)) &&
(nnt_device->pciconf_device.vsec_capability_mask & (1 << NNT_VSEC_ICMD_SPACE_SUPPORTED)) &&
(nnt_device->pciconf_device.vsec_capability_mask & (1 << NNT_VSEC_CRSPACE_SPACE_SUPPORTED)) &&
(nnt_device->pciconf_device.vsec_capability_mask & (1 << NNT_VSEC_GLOBAL_SEMAPHORE_SPACE_SUPPORTED))) {
nnt_device->pciconf_device.vsec_fully_supported = 1;
}
}
int init_pciconf(struct nnt_device* nnt_device)
{
int error = 0;
nnt_device->pciconf_device.semaphore_offset =
nnt_device->pciconf_device.vendor_specific_capability + PCI_SEMAPHORE_OFFSET;
nnt_device->pciconf_device.data_offset =
nnt_device->pciconf_device.vendor_specific_capability + PCI_DATA_OFFSET;
nnt_device->pciconf_device.address_offset =
nnt_device->pciconf_device.vendor_specific_capability + PCI_ADDRESS_OFFSET;
error = init_vsec_capability_mask(nnt_device);
check_vsec_minimum_support(nnt_device);
return error;
}

View File

@@ -0,0 +1,14 @@
#ifndef NNT_PCICONF_H
#define NNT_PCICONF_H
#include "nnt_device_defs.h"
#include "nnt_ioctl_defs.h"
int read_pciconf(struct nnt_device* nnt_device, struct nnt_rw_operation* read_operation);
int write_pciconf(struct nnt_device* nnt_device, struct nnt_rw_operation* write_operation);
int init_pciconf(struct nnt_device* nnt_device);
int read_dword(struct nnt_read_dword_from_config_space* read_from_cspace, struct nnt_device* nnt_device);
int check_address_space_support(struct nnt_device* nnt_device);
#endif

View File

@@ -0,0 +1,64 @@
#ifndef NNT_PCICONF_DEFS_H
#define NNT_PCICONF_DEFS_H
#define ADDRESS_SPACE_CR_SPACE 0x2
#define ADDRESS_SPACE_ICMD 0x3
#define ADDRESS_SPACE_SEMAPHORE 0xa
#define PCI_CONTROL_OFFSET 0x04
#define PCI_COUNTER_OFFSET 0x08
#define SEMAPHORE_MAX_RETRIES 0x1000
#define PCI_SPACE_BIT_OFFSET 0
#define PCI_SPACE_BIT_LENGTH 16
#define PCI_STATUS_BIT_OFFSET 29
#define PCI_STATUS_BIT_LEN 3
#define PCI_FLAG_BIT_OFFSET 31
#define READ_OPERATION 0
#define WRITE_OPERATION 1
#define IFC_MAX_RETRIES 0x10000
#define SEMAPHORE_MAX_RETRIES 0x1000
typedef enum nnt_space_address {
NNT_SPACE_ICMD = 0x1,
NNT_SPACE_CR_SPACE = 0x2,
NNT_SPACE_ALL_ICMD = 0x3,
NNT_SPACE_NODNIC_INIT_SEG = 0x4,
NNT_SPACE_EXPANSION_ROM = 0x5,
NNT_SPACE_ND_CR_SPACE = 0x6,
NNT_SPACE_SCAN_CR_SPACE = 0x7,
NNT_SPACE_GLOBAL_SEMAPHORE = 0xa,
NNT_SPACE_MAC = 0xf
} NNT_SPACE_ADDRESS;
typedef enum nnt_vsec_capability {
NNT_VSEC_INITIALIZED = 0,
NNT_VSEC_ICMD_SPACE_SUPPORTED,
NNT_VSEC_CRSPACE_SPACE_SUPPORTED,
NNT_VSEC_ALL_ICMD_SPACE_SUPPORTED,
NNT_VSEC_NODNIC_INIT_SEG_SPACE_SUPPORTED,
NNT_VSEC_EXPANSION_ROM_SPACE_SUPPORTED,
NNT_VSEC_ND_CRSPACE_SPACE_SUPPORTED,
NNT_VSEC_SCAN_CRSPACE_SPACE_SUPPORTED,
NNT_VSEC_GLOBAL_SEMAPHORE_SPACE_SUPPORTED,
NNT_VSEC_MAC_SPACE_SUPPORTED,
} NNT_VSEC_CAPABILITY;
// BIT Slicing macros
#define ONES32(size) ((size)?(0xffffffff>>(32-(size))):0)
#define MASK32(offset, size) ( ONES32(size)<<(offset))
#define EXTRACT_C(source, offset, size) ((((unsigned int)(source))>>(offset)) & ONES32(size))
#define EXTRACT(src, start, len) (((len) == 32)?(src):EXTRACT_C(src, start, len))
#define MERGE_C(rsrc1, rsrc2, start, len) ((((rsrc2)<<(start)) & (MASK32((start), (len)))) | ((rsrc1) & (~MASK32((start), (len)))))
#define MERGE(rsrc1, rsrc2, start, len) (((len) == 32)?(rsrc2):MERGE_C(rsrc1, rsrc2, start, len))
#endif

View File

@@ -0,0 +1,132 @@
#include "nnt_device_defs.h"
#include "nnt_pci_conf_access_no_vsec.h"
#include "nnt_defs.h"
int read_no_vsec(struct nnt_device* nnt_device, unsigned int offset,
unsigned int* data)
{
int error = 0;
if (nnt_device->wo_address) {
offset |= 0x1;
}
/* Write the wanted address to address register. */
error = pci_write_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.address_register,
offset);
CHECK_PCI_WRITE_ERROR(error, nnt_device->pciconf_device.address_register,
offset);
/* Read the result from data register */
error = pci_read_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.data_register,
data);
CHECK_PCI_READ_ERROR(error, nnt_device->pciconf_device.data_register);
ReturnOnFinished:
return error;
}
int read_pciconf_no_vsec(struct nnt_device* nnt_device, struct nnt_rw_operation* read_operation)
{
int counter = 0;
int error = 0;
for (counter = 0; counter < read_operation->size; counter += 4) {
if (read_no_vsec(nnt_device, read_operation->offset + counter,
&read_operation->data[counter >> 2])) {
error = counter;
goto ReturnOnFinished;
}
}
ReturnOnFinished:
return error;
}
int write_no_vsec(struct nnt_device* nnt_device, unsigned int offset,
unsigned int data)
{
int error = 0;
if (nnt_device->wo_address) {
/* write the data to the data register. */
error = pci_write_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.data_register,
data);
CHECK_PCI_WRITE_ERROR(error, nnt_device->pciconf_device.data_register,
data);
/* Write the destination address to address register. */
error = pci_write_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.address_register,
offset);
CHECK_PCI_WRITE_ERROR(error, nnt_device->pciconf_device.address_register,
offset);
} else {
/* Write the destination address to address register. */
error = pci_write_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.address_register,
offset);
CHECK_PCI_WRITE_ERROR(error, nnt_device->pciconf_device.address_register,
offset);
/* write the data to the data register. */
error = pci_write_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.data_register,
data);
CHECK_PCI_WRITE_ERROR(error, nnt_device->pciconf_device.data_register,
data);
}
ReturnOnFinished:
return error;
}
int write_pciconf_no_vsec(struct nnt_device* nnt_device, struct nnt_rw_operation* write_operation)
{
int counter = 0;
int error = 0;
for (counter = 0; counter < write_operation->size; counter += 4) {
if (write_no_vsec(nnt_device, write_operation->offset + counter,
write_operation->data[counter >> 2])) {
error = counter;
goto ReturnOnFinished;
}
}
ReturnOnFinished:
return error;
}
int is_wo_gw(struct nnt_device* nnt_device)
{
unsigned int data = 0;
int error = 0;
error = pci_write_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.address_register,
NNT_DEVICE_ID_OFFSET);
CHECK_PCI_WRITE_ERROR(error, nnt_device->pciconf_device.address_register,
NNT_DEVICE_ID_OFFSET);
/* Read the result from data register */
error = pci_read_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.address_register,
&data);
CHECK_PCI_READ_ERROR(error, nnt_device->pciconf_device.address_register);
if (data == NNT_WO_REG_ADDR_DATA) {
error = 1;
}
ReturnOnFinished:
return error;
}
int init_pciconf_no_vsec(struct nnt_device* nnt_device)
{
nnt_device->pciconf_device.address_register = NNT_CONF_ADDRES_REGISETER;
nnt_device->pciconf_device.data_register = NNT_CONF_DATA_REGISTER;
nnt_device->wo_address = is_wo_gw(nnt_device);
return 0;
}

View File

@@ -0,0 +1,12 @@
#ifndef NNT_PCICONF_NO_VSEC_H
#define NNT_PCICONF_NO_VSEC_H
#include "nnt_device_defs.h"
#include "nnt_ioctl_defs.h"
int read_pciconf_no_vsec(struct nnt_device* nnt_device, struct nnt_rw_operation* read_operation);
int write_pciconf_no_vsec(struct nnt_device* nnt_device, struct nnt_rw_operation* write_operation);
int init_pciconf_no_vsec(struct nnt_device* nnt_device);
#endif

View File

@@ -0,0 +1,38 @@
#ifndef NNT_DEVICE_LIST_H
#define NNT_DEVICE_LIST_H
#include "nnt_device_defs.h"
static struct pci_device_id pciconf_devices[] = {{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, CONNECTX3_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, CONNECTX3PRO_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, CONNECTIB_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, CONNECTX4_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, CONNECTX4LX_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, CONNECTX5_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, CONNECTX5EX_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, CONNECTX6_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, CONNECTX6DX_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, CONNECTX6LX_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, CONNECTX7_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, CONNECTX8_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, SCHRODINGER_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, FREYSA_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, BLUEFIELD_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, BLUEFIELD2_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, BLUEFIELD3_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, BLUEFIELD4_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, SWITCHIB_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, SWITCHIB2_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, QUANTUM_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, QUANTUM2_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, QUANTUM3_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, SPECTRUM_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, SPECTRUM2_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, SPECTRUM3_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, SPECTRUM4_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, BW00_PCI_ID)},
{
0,
}};
#endif // NNT_DEVICE_LIST_H

View File

@@ -0,0 +1,26 @@
#ifndef NNT_DRIVER_PPC_H
#define NNT_DRIVER_PPC_H
LIST_HEAD(nnt_device_list);
#define NNT_MAXIMUM_DEVICE_NAME_LENGTH 128
#define NNT_MAXIMUM_NUMBER_OF_DEVICES 8
#define NNT_MAXIMUM_POLLING_NUMBER 100
#define NNT_UNKNOWN_DEVICE_ID 0xffff
#define NNT_MINIMUM_WAITING_TIME 100
#define NNT_DEVICE_LIST_SIZE NNT_MAXIMUM_DEVICE_NAME_LENGTH * NNT_MAXIMUM_NUMBER_OF_DEVICES
struct nnt_ppc_device {
struct list_head entry;
struct pci_dev* pci_device;
char* pci_device_dbdf_name;
};
struct nnt_ppc_reset_info {
unsigned int number_of_found_pci_device;
unsigned int number_of_requested_pci_device;
int reset_was_done;
};
#endif