]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
mlx4: add mstflint secure boot access kernel support
authorQing Huang <qing.huang@oracle.com>
Tue, 16 Jan 2018 19:27:32 +0000 (11:27 -0800)
committerJack Vogel <jack.vogel@oracle.com>
Wed, 24 Jan 2018 19:28:17 +0000 (11:28 -0800)
Source files are copied from:
https://github.com/Mellanox/mstflint.git master_devel
(under kernel sub-directory - changes up to commit
a8a84fae7459aba01ff6ad6cbc40622b7615b110)

Due to recent security changes in UEK4, mstflint FW tool may
lose the ability to access CX3 HCAs in Secure Boot enabled
enviroment. This kernel patch in addition to an enhanced
version of mstflint tool will let us regain the ability to
manage CX3 FW when Secure Boot is enabled while running latest
UEK4 kernels.

Orabug: 27424392

Signed-off-by: Qing Huang <qing.huang@oracle.com>
Reviewed-by: Rao Shoaib <rao.shoaib@oracle.com>
15 files changed:
drivers/net/ethernet/mellanox/Kconfig
drivers/net/ethernet/mellanox/Makefile
drivers/net/ethernet/mellanox/mstflint_access/Kconfig [new file with mode: 0644]
drivers/net/ethernet/mellanox/mstflint_access/Makefile [new file with mode: 0644]
drivers/net/ethernet/mellanox/mstflint_access/README.txt [new file with mode: 0644]
drivers/net/ethernet/mellanox/mstflint_access/mst.h [new file with mode: 0644]
drivers/net/ethernet/mellanox/mstflint_access/mst_kernel.h [new file with mode: 0644]
drivers/net/ethernet/mellanox/mstflint_access/mst_main.c [new file with mode: 0644]
drivers/net/ethernet/mellanox/mstflint_access/mst_vpd.c [new file with mode: 0644]
uek-rpm/ol6/config-x86_64
uek-rpm/ol6/config-x86_64-debug
uek-rpm/ol6/nano_modules.list
uek-rpm/ol7/config-x86_64
uek-rpm/ol7/config-x86_64-debug
uek-rpm/ol7/nano_modules.list

index 2b17cde21e673520ea2d508408f159dd0f214aca..e3f672c0e6588a69a9085bbf521940e14280d674 100644 (file)
@@ -21,5 +21,6 @@ if NET_VENDOR_MELLANOX
 source "drivers/net/ethernet/mellanox/mlx4/Kconfig"
 source "drivers/net/ethernet/mellanox/mlx4_vnic/Kconfig"
 source "drivers/net/ethernet/mellanox/mlx5/core/Kconfig"
+source "drivers/net/ethernet/mellanox/mstflint_access/Kconfig"
 
 endif # NET_VENDOR_MELLANOX
index 3b8fe1963f81afa7a41cab75503919d69ddd6634..f639284b13248b30dab0be38988b87a82d2cdff9 100644 (file)
@@ -5,3 +5,4 @@
 obj-$(CONFIG_MLX4_CORE) += mlx4/
 obj-$(CONFIG_MLX4_VNIC) += mlx4_vnic/
 obj-$(CONFIG_MLX5_CORE) += mlx5/core/
+obj-$(CONFIG_MLX4_VNIC) += mstflint_access/
diff --git a/drivers/net/ethernet/mellanox/mstflint_access/Kconfig b/drivers/net/ethernet/mellanox/mstflint_access/Kconfig
new file mode 100644 (file)
index 0000000..b555d9d
--- /dev/null
@@ -0,0 +1,6 @@
+config MSTFLINT_ACCESS
+       tristate "Mellanox Technologies MSTFLINT Secure Boot Access support"
+       select MLX4_CORE
+       help
+         Mellanox Technologies MSTFLINT_ACCESS functionality.
+
diff --git a/drivers/net/ethernet/mellanox/mstflint_access/Makefile b/drivers/net/ethernet/mellanox/mstflint_access/Makefile
new file mode 100644 (file)
index 0000000..792d43b
--- /dev/null
@@ -0,0 +1,3 @@
+obj-$(CONFIG_MSTFLINT_ACCESS)  += mstflint_access.o
+
+mstflint_access-y :=  mst_main.o    mst_vpd.o
diff --git a/drivers/net/ethernet/mellanox/mstflint_access/README.txt b/drivers/net/ethernet/mellanox/mstflint_access/README.txt
new file mode 100644 (file)
index 0000000..71a6811
--- /dev/null
@@ -0,0 +1,23 @@
+MELLANOX TECHNOLOGIES LTD.\r
+\r
+\r
+*******************************\r
+MSTFLINT DRIVER FOR SECURE BOOT\r
+*******************************\r
+\r
+This kernel was developed to enable mstflint to work under secure boot enabled systems.\r
+\r
+To work with this driver perform the following operations:\r
+\r
+1. Run make\r
+2. Using signtool, sign the generated mstflint_driver.ko (the key must be trusted by the system)\r
+3. Load the driver:\r
+    > insmod ./mstflint_access.ko\r
+4. Run your tools with the DBDF device name format:\r
+    > mstflint -d 05:00.0 q\r
+    > mstmcra 05:00.0 0xf0014\r
+\r
+*Limitation - mstflint driver supports only ConnectX-3/ConnectX-3Pro devices.\r
+\r
+\r
+**Note - a spec file for generating source RPM from the sources will be added at a later stage    \r
diff --git a/drivers/net/ethernet/mellanox/mstflint_access/mst.h b/drivers/net/ethernet/mellanox/mstflint_access/mst.h
new file mode 100644 (file)
index 0000000..9ac122f
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2011-2014 Mellanox Technologies, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _MST_H_
+#define _MST_H_
+
+#include <linux/ioctl.h>
+
+
+/****************************************************/
+#define MST_MEMORY_SIZE                                (1024*1024)
+#define MST_BLOCK_SIZE                         64
+
+#define MST_PARAMS_MAGIC                       0xD0
+#define MST_BYTE_ACCESS_MAGIC          0xD1
+#define MST_BLOCK_ACCESS_MAGIC         0xD2
+#define MST_PCICONF_MAGIC                      0xD3
+#define MST_PCIMEM_MAGIC                       0xD4
+#define MST_CONNECTX_WA_MAGIC          0xD5
+#define MST_VPD_MAGIC                          0xD6
+
+
+/****************************************************/
+/* GET PARAMS */
+#define MST_PARAMS _IOR(MST_PARAMS_MAGIC, 1, struct mst_params)
+
+struct mst_params {
+       unsigned int domain;
+       unsigned int bus;
+       unsigned int slot;
+       unsigned int func;
+       unsigned int bar;
+       unsigned int device;
+    unsigned int vendor;
+       unsigned int subsystem_device;
+    unsigned int subsystem_vendor;
+};
+
+/****************************************************/
+/* BYTE ACCESS */
+#define MST_READ4 _IOR(MST_BYTE_ACCESS_MAGIC, 1, struct mst_read4_st)
+
+struct mst_read4_st {
+       unsigned int offset;
+       u32 data;                               /* OUT */
+};
+
+
+#define MST_WRITE4 _IOW(MST_BYTE_ACCESS_MAGIC, 2, struct mst_write4_st)
+
+struct mst_write4_st {
+       unsigned int offset;
+       u32 data;
+};
+
+
+#define PCICONF_READ4  MST_READ4
+#define PCICONF_WRITE4 MST_WRITE4
+#define PCIMEM_READ4   MST_READ4
+#define PCIMEM_WRITE4  MST_WRITE4
+
+
+/****************************************************/
+/* BLOCK ACCESS */
+#define PCIMEM_READ_BLOCK _IOR(MST_BLOCK_ACCESS_MAGIC, 1, struct mst_read_block_st)
+
+struct mst_read_block_st {
+       unsigned int offset;
+       unsigned int size;                      /* in bytes */
+       u32 data[MST_BLOCK_SIZE];               /* OUT */
+};
+
+
+#define PCIMEM_WRITE_BLOCK _IOW(MST_BLOCK_ACCESS_MAGIC, 2, struct mst_write_block_st)
+
+struct mst_write_block_st {
+       unsigned int offset;
+       unsigned int size;                      /* in bytes */
+       u32 data[MST_BLOCK_SIZE];
+};
+
+
+/****************************************************/
+/*
+ * INIT / STOP Conf Access
+ * Used to change conf registers on the fly,
+ * by default we set the conf register to default values
+ */
+#define PCICONF_INIT _IOC(_IOC_NONE, MST_PCICONF_MAGIC, 1, \
+                         sizeof(struct mst_pciconf_init_st))
+
+struct mst_pciconf_init_st {
+       unsigned int domain;
+       unsigned int bus;
+       unsigned int devfn;
+       /* Byte offsets in configuration space */
+       unsigned int addr_reg;
+       unsigned int data_reg;
+};
+
+
+#define PCICONF_STOP _IOC(_IOC_NONE, MST_PCICONF_MAGIC, 2, 0)
+
+
+/****************************************************/
+/*
+ * INIT / STOP Memory Access
+ * Used to change bar number and map the new bar on the fly,
+ * by default we set and map bar to default bar number per device
+ */
+#define PCIMEM_INIT _IOC(_IOC_NONE, MST_PCIMEM_MAGIC, 1, \
+                        sizeof(struct mst_mem_init_st))
+
+struct mst_mem_init_st {
+       unsigned int bar;
+};
+
+
+#define PCIMEM_STOP _IOC(_IOC_NONE, MST_PCIMEM_MAGIC, 2, 0)
+
+
+/****************************************************/
+/* CONNECTX ORDERING WA */
+#define CONNECTX_WA_BASE 0xf0384       /* SEM BASE ADDR. SEM 0xf0380 is reserved for external tools usage. */
+#define CONNECTX_WA_SIZE 3                     /* Size in entries */
+
+#define PCI_CONNECTX_WA _IOR(MST_CONNECTX_WA_MAGIC, 1, u_int32_t)
+
+struct mst_connectx_wa {
+       u32 connectx_wa_slot_p1;        /* connectx used slot plus 1. zero means unused */
+};
+
+
+/****************************************************/
+/* VPD ACCESS */
+#define PCICONF_VPD_READ4 _IOR(MST_VPD_MAGIC, 1, struct mst_vpd_read4_st)
+struct mst_vpd_read4_st {
+       unsigned int offset;    /* IN - must be aligned to DWORD */
+       unsigned int timeout;   /* IN - timeout in milliseconds or zero for default timeout */
+       u32 data;                               /* OUT */
+
+};
+
+#define PCICONF_VPD_WRITE4 _IOW(MST_VPD_MAGIC, 2, struct mst_vpd_write4_st)
+struct mst_vpd_write4_st {
+       unsigned int offset;    /* IN - must be aligned to DWORD */
+       unsigned int timeout;   /* IN - timeout in milliseconds or zero for default timeout */
+       u32 data;                               /* IN */
+};
+
+
+#endif /* _MST_H_ */
diff --git a/drivers/net/ethernet/mellanox/mstflint_access/mst_kernel.h b/drivers/net/ethernet/mellanox/mstflint_access/mst_kernel.h
new file mode 100644 (file)
index 0000000..e7d7924
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2011-2014 Mellanox Technologies, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+
+#ifndef _MST_KERNEL_H_
+#define _MST_KERNEL_H_
+
+
+/****************************************************/
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ioctl.h>
+#include <asm/uaccess.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+#include <linux/byteorder/generic.h>
+#include<linux/cdev.h>
+
+#include "mst.h"
+
+
+/****************************************************/
+#define DRV_VERSION            "2.0.0"
+#define DRV_RELDATE            "Nov-27-2012"
+
+
+/****************************************************/
+/* defines */
+#define MST_NAME_SIZE                          30
+#define MST_PREFIX                                     "  MST::  "
+
+#define MST_DEVICE_PREFIX              "mt"
+#define MST_PCICONF_DEVICE_NAME     "_mstconf"
+#define MST_PCIMEM_DEVICE_NAME      "_mstcr"
+
+#define MST_MELLANOX_PCI_VENDOR                0x15b3
+
+#define MST_CONF_ADDR_REG                      88
+#define MST_CONF_DATA_REG                      92
+
+#define MST_VPD_DEFAULT_TOUT           2000    /* milli seconds */
+
+#define mst_err(format, arg...)        \
+       printk(KERN_ERR "%s: %s %d: " format, MST_PREFIX, __func__, __LINE__, ## arg)
+#define mst_info(format, arg...)       \
+       printk(KERN_INFO "%s: %s %d: " format, MST_PREFIX, __func__, __LINE__, ## arg)
+
+
+/****************************************************/
+/* new types */
+enum dev_type {
+       PCICONF,
+       PCIMEM
+};
+
+struct mst_dev_data {
+       int                                     addr_reg;                               /* PCICONF address register */
+       int                                     data_reg;                               /* PCICONF data register */
+       unsigned int            bar;                                    /* PCIMEM bar */
+       void                            *hw_addr;                               /* PCIMEM memory start */
+       char                            name[MST_NAME_SIZE];    /* name of character device */
+       enum dev_type           type;                                   /* type of device */
+       struct pci_dev          *pci_dev;                               /* device pci struct in kernel */
+       struct list_head        list;                                   /* list of mst_devices */
+       struct mutex            lock;                                   /* device lock */
+       int                                     vpd_cap_addr;                   /* addr VPD capability */
+       int                                     major;                                  /* device major number */
+       int                                     initialized;                    /* indicate if init done */
+
+    dev_t               my_dev;
+    struct cdev         mcdev;
+    struct class        *cl;
+
+       unsigned char           connectx_wa_slots;              /* wa for pci bug */
+};
+
+
+/****************************************************/
+int pci_read4_vpd(struct mst_dev_data *dev, unsigned int timeout,
+               unsigned offset, u32 *buf);
+
+int pci_write4_vpd(struct mst_dev_data *dev, unsigned int timeout,
+               unsigned offset, u32 buf);
+
+
+#endif /* _MST_KERNEL_H_ */
+
+
diff --git a/drivers/net/ethernet/mellanox/mstflint_access/mst_main.c b/drivers/net/ethernet/mellanox/mstflint_access/mst_main.c
new file mode 100644 (file)
index 0000000..a92eb48
--- /dev/null
@@ -0,0 +1,965 @@
+/*
+ * Copyright (c) 2011-2014 Mellanox Technologies, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <asm/io.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
+#include <linux/uaccess.h>
+#else
+#include <asm/uaccess.h>
+#endif
+#include <linux/pci.h>
+#include <linux/fs.h>
+#include "mst_kernel.h"
+
+
+/****************************************************/
+MODULE_AUTHOR("Mahmoud Hasan");
+MODULE_DESCRIPTION("MST Module");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_VERSION(DRV_VERSION " ("DRV_RELDATE")");
+
+
+/****************************************************/
+/* globals variables */
+static const char mst_driver_version[] =  DRV_VERSION;
+static const char mst_driver_string[]  = "Mellanox Technologies Software Tools Driver";
+
+LIST_HEAD(mst_devices);
+
+static struct pci_device_id mst_livefish_pci_table[] = {
+       { PCI_DEVICE(MST_MELLANOX_PCI_VENDOR, 0x01f6) },        /* MT27500 [ConnectX-3 Flash Recovery] */
+       { PCI_DEVICE(MST_MELLANOX_PCI_VENDOR, 0x01f8) },        /* MT27520 [ConnectX-3 Pro Flash Recovery] */
+       { 0, }
+};
+
+static struct pci_device_id mst_bar1_pci_table[] = {
+       { PCI_DEVICE(MST_MELLANOX_PCI_VENDOR, 0x01011) },       /* MT27600 [ConnectX-IB] */
+       { PCI_DEVICE(MST_MELLANOX_PCI_VENDOR, 0x01ff) },        /* MT27600 [ConnectX-IB Flash Recovery] */
+       { 0, }
+};
+
+static struct pci_device_id supported_pci_devices[] = {
+       { PCI_DEVICE(MST_MELLANOX_PCI_VENDOR, 4099) },  /* MT27600 [ConnectX-IB] */
+       { PCI_DEVICE(MST_MELLANOX_PCI_VENDOR, 4103) },  /* MT27600 [ConnectX-IB Flash Recovery] */
+       { 0, }
+};
+
+
+/****************************************************/
+static int mst_open(struct inode *inode, struct file *file)
+{
+       struct mst_file_data *md = NULL;
+
+        md = kmalloc(sizeof(struct mst_connectx_wa), GFP_KERNEL);
+        if (!md) {
+                return -ERESTARTSYS;
+        }
+
+        memset(md, 0, sizeof(struct mst_connectx_wa));
+
+        file->private_data = md;
+
+        return 0;
+}
+
+
+/****************************************************/
+static int mst_release(struct inode *inode, struct file *file)
+{
+       int res                                         = 0;
+       struct mst_dev_data *dev        = NULL;
+       struct mst_dev_data *cur        = NULL;
+       unsigned int slot_mask;
+       struct mst_connectx_wa *md      = file->private_data;
+
+       /*
+        * make sure the device is available since it
+        * could be removed by hotplug event
+        * if available grab its lock
+        */
+       list_for_each_entry(cur, &mst_devices, list) {
+               if (cur->major == imajor(inode)) {
+                       dev = cur;
+                       mutex_lock(&dev->lock);
+                       break;
+               }
+       }
+
+       if (!dev) {
+               mst_err("failed to find device with major=%d\n",
+                               imajor(inode));
+                               res = -ENODEV;
+                               goto out;
+       }
+
+       slot_mask = ~(1 << (md->connectx_wa_slot_p1 - 1));
+       dev->connectx_wa_slots &= slot_mask;
+
+       /*
+        * mst_info("CONNECTX_WA: Released slot %u. Current slots: %02x\n",
+        *                      md->connectx_wa_slot_p1 - 1, dev->connectx_wa_slots);
+        */
+       md->connectx_wa_slot_p1 = 0;
+       mutex_unlock(&dev->lock);
+
+       kfree(file->private_data);
+       file->private_data = NULL;
+
+out:
+       return res;
+}
+
+
+/****************************************************/
+static ssize_t mst_read(struct file *file, char *buf, size_t count,
+                       loff_t *f_pos)
+{
+       mst_err("not implemented\n");
+       return 0;
+}
+
+
+/****************************************************/
+static ssize_t mst_write(struct file *file, const char *buf, size_t count,
+                        loff_t *f_pos)
+{
+       mst_err("not implemented\n");
+       return 0;
+}
+
+
+/****************************************************/
+static inline void print_opcode(void)
+{
+       mst_info("MST_PARAMS=%lx\n", MST_PARAMS);
+
+       mst_info("PCICONF_READ4=%lx\n", PCICONF_READ4);
+       mst_info("PCICONF_WRITE4=%lx\n", PCICONF_WRITE4);
+       mst_info("PCIMEM_READ4=%lx\n",  PCIMEM_READ4);
+       mst_info("PCIMEM_WRITE4=%lx\n", PCIMEM_WRITE4);
+
+       mst_info("PCIMEM_READ_BLOCK=%lx\n", PCIMEM_READ_BLOCK);
+       mst_info("PCIMEM_WRITE_BLOCK=%lx\n", PCIMEM_WRITE_BLOCK);
+
+       mst_info("PCICONF_INIT=%lx\n", PCICONF_INIT);
+       mst_info("PCICONF_STOP=%x\n", PCICONF_STOP);
+
+       mst_info("PCIMEM_INIT=%lx\n", PCIMEM_INIT);
+       mst_info("PCIMEM_STOP=%x\n", PCIMEM_STOP);
+
+       mst_info("PCI_CONNECTX_WA=%lx\n", PCI_CONNECTX_WA);
+
+       mst_info("PCICONF_VPD_READ4=%lx\n", PCICONF_VPD_READ4);
+       mst_info("PCICONF_VPD_WRITE4=%lx\n", PCICONF_VPD_WRITE4);
+}
+
+
+/****************************************************/
+/*
+ * mst_ioctl
+ *
+ * @opcode:
+ *  MST_PARAMS - get the device parameters
+ *
+ *  PCICONF_READ4     - read 4 bytes from configuration space
+ *  PCICONF_WRITE4    - write 4 bytes to configuration space
+ *  PCIMEM_READ4      - read 4 bytes from memory access
+ *  PCIMEM_WRITE4     - write 4 bytes to memory access
+ *
+ *  PCIMEM_READ_BLOCK - read a block of data from pci memory,
+ *                     size is expressed as num of unsigned integers
+ *  PCIMEM_WRITE_BLOCK - write a block of data to pci memory,
+ *                     size is expressed as num of unsigned integers
+
+ *  PCICONF_INIT       - initialize a new PCICONF device
+ *  PCICONF_STOP       - stop a PCICONF device
+ *
+ *  PCIMEM_INIT        - initialize a new PCIMEM device
+ *  PCIMEM_STOP        - stop a PCIMEM device
+ *
+ *  PCI_CONNECTX_WA      - connectx workaround for
+ *           pci reads passing writes
+ *
+ * RETURN VALUE:
+ *   0 upon success
+ *   -EINVAL if opcode is invalid
+ *   -ENODEV if device is not initialized
+ *   -EPERM  if operation does not match device type
+ *   -EFAULT if there was a problem with hardware operation
+ *
+ */
+static int mst_ioctl(struct inode *inode, struct file *file,
+                    unsigned int opcode, unsigned long input)
+{
+
+       int res                                         = 0;
+       struct mst_dev_data *dev        = NULL;
+       struct mst_dev_data *cur        = NULL;
+       void *user_buf                          = (void *)input;
+
+       /*
+        * In MEM mapped data flow there is no need to lock the semaphore.
+        * Since the HW handles the requests in PCI level thus no need
+        * for serializing (HW is capable of handling parallel requests)
+        */
+#define IS_LOCK_NEEDED(dev) \
+       (!(dev->type == PCIMEM && \
+       (opcode == MST_READ4 || opcode == MST_WRITE4)))
+
+       /*
+        * make sure the device is available since it
+        * could be removed by hotplug event
+        * if available grab its lock
+        */
+       list_for_each_entry(cur, &mst_devices, list) {
+               if (cur->major == imajor(inode)) {
+                       dev = cur;
+                       if (IS_LOCK_NEEDED(dev))
+                               mutex_lock(&dev->lock);
+                       break;
+               }
+       }
+
+       if (!dev) {
+               mst_err("failed to find device with major=%d\n",
+                      imajor(inode));
+               res = -ENODEV;
+               goto fin_err;
+       }
+
+
+       switch (opcode) {
+       case MST_PARAMS: {
+               struct mst_params paramst;
+
+               if (!dev->initialized) {
+                       mst_err("device is not initialized\n");
+                       res = -ENODEV;
+                       goto fin;
+               }
+
+               paramst.domain                          = pci_domain_nr(dev->pci_dev->bus);
+               paramst.bus                             = dev->pci_dev->bus->number;
+               paramst.slot                            = PCI_SLOT(dev->pci_dev->devfn);
+               paramst.func                            = PCI_FUNC(dev->pci_dev->devfn);
+               paramst.bar                             = dev->bar;
+               paramst.device                          = dev->pci_dev->device;
+               paramst.vendor                          = dev->pci_dev->vendor;
+               paramst.subsystem_device        = dev->pci_dev->subsystem_device;
+               paramst.subsystem_vendor        = dev->pci_dev->subsystem_vendor;
+
+               if (copy_to_user(user_buf, &paramst, sizeof(struct mst_params))) {
+                       res = -EFAULT;
+                       goto fin;
+               }
+               break;
+       }
+
+       case MST_READ4: {
+               u32 out;
+               u32 *dataout = NULL;
+               struct mst_read4_st readst;
+
+               if (!dev->initialized) {
+                       mst_err("device is not initialized\n");
+                       res = -ENODEV;
+                       goto fin;
+               }
+
+               if (copy_from_user(&readst, user_buf, sizeof(struct mst_read4_st))) {
+                       res = -EFAULT;
+                       goto fin;
+               }
+
+               switch (dev->type) {
+               case PCICONF:
+                       /* write the wanted address to addr register */
+                       res = pci_write_config_dword(dev->pci_dev, dev->addr_reg, readst.offset);
+                       if (res) {
+                               mst_err("pci_write_config_dword failed\n");
+                               goto fin;
+                       }
+
+                       /* read the result from data register */
+                       res = pci_read_config_dword(dev->pci_dev, dev->data_reg, &out);
+                       if (res) {
+                               mst_err("pci_read_config_dword failed\n");
+                               goto fin;
+                       }
+                       break;
+
+               case PCIMEM:
+                       if ((readst.offset + sizeof(u32)) > MST_MEMORY_SIZE) {
+                               mst_err("accesing invalid address\n");
+                               res = -EINVAL;
+                               goto fin;
+                       }
+
+                       /* read from hardware */
+                       out = ioread32(dev->hw_addr + readst.offset);
+
+                       /* endianness conversion - we noticed that we need to swap always */
+                        be32_to_cpus(&out);
+                        out = cpu_to_le32(out);
+                       break;
+               }
+
+               /* retrieve to user */
+               dataout = &((struct mst_read4_st *)user_buf)->data;
+               if (copy_to_user(dataout, &out, sizeof(u32))) {
+                       res = -EFAULT;
+                       goto fin;
+               }
+               break;
+       }
+
+       case MST_WRITE4: {
+               struct mst_write4_st writest;
+
+               if (!dev->initialized) {
+                       mst_err("device is not initialized\n");
+                       res = -ENODEV;
+                       goto fin;
+               }
+
+               if (copy_from_user(&writest, user_buf, sizeof(struct mst_write4_st))) {
+                       res = -EFAULT;
+                       goto fin;
+               }
+
+               switch (dev->type) {
+               case PCICONF:
+                       /* write the destination address to addr register */
+                       res = pci_write_config_dword(dev->pci_dev, dev->addr_reg, writest.offset);
+                       if (res) {
+                               mst_err("pci_write_config_dword failed\n");
+                               goto fin;
+                       }
+
+                       /* write the data to data register */
+                       res = pci_write_config_dword(dev->pci_dev, dev->data_reg, writest.data);
+                       if (res) {
+                               mst_err("pci_write_config_dword failed\n");
+                               goto fin;
+                       }
+                       break;
+
+               case PCIMEM:
+                       if ((writest.offset + sizeof(u32)) > MST_MEMORY_SIZE) {
+                               mst_err("Accesing invalid address\n");
+                               res = -EINVAL;
+                               goto fin;
+                       }
+
+                       /* endianness conversion - we noticed that we need to swap always */
+                       cpu_to_be32s(&(writest.data));
+                        writest.data = cpu_to_le32(writest.data);
+
+                       /* write to hardware */
+                       iowrite32(writest.data, dev->hw_addr + writest.offset);
+                       break;
+               }
+
+               break;
+       }
+
+       case PCIMEM_READ_BLOCK: {
+               int i                   = 0;
+               u32 *data               = NULL;
+               u32 *dataout    = NULL;
+               struct mst_read_block_st readst;
+
+               if (!dev->initialized) {
+                       mst_err("device is not initialized\n");
+                       res = -ENODEV;
+                       goto fin;
+               }
+
+               if (dev->type != PCIMEM) {
+                       mst_err("wrong type for device\n");
+                       res = -EPERM;
+                       goto fin;
+               }
+
+               if (copy_from_user(&readst, user_buf, sizeof(struct mst_read_block_st))) {
+                       res = -EFAULT;
+                       goto fin;
+               }
+
+               if (readst.size % sizeof(u32)) {
+                       mst_err("invalid size. size should be in bytes and devide sizeof(u32)\n");
+                       res = -EINVAL;
+                       goto fin;
+               }
+
+               if ((readst.offset + readst.size) > MST_MEMORY_SIZE) {
+                       mst_err("accesing invalid address\n");
+                       res = -EINVAL;
+                       goto fin;
+               }
+
+               data = kzalloc(readst.size, GFP_KERNEL);
+               if (!data) {
+                       res = -ENOMEM;
+                       goto fin;
+               }
+
+               /* read from hardware */
+               memcpy_fromio(data, dev->hw_addr + readst.offset, readst.size);
+
+               /* endianness conversion */
+               for (i = 0; i < (readst.size / sizeof(u32)); ++i)
+                      be32_to_cpus(&(data[i]));
+
+               /* retrieve to user */
+               dataout = ((struct mst_read_block_st *)user_buf)->data;
+               if (copy_to_user(dataout, data, readst.size)) {
+                       res = -EFAULT;
+                       kfree(data);
+                       goto fin;
+               }
+
+               kfree(data);
+               break;
+       }
+
+       case PCIMEM_WRITE_BLOCK: {
+               int i = 0;
+               struct mst_write_block_st writest;
+
+               if (!dev->initialized) {
+                       mst_err("device is not initialized\n");
+                       res = -ENODEV;
+                       goto fin;
+               }
+
+               if (dev->type != PCIMEM) {
+                       mst_err("wrong type for device\n");
+                       res = -EPERM;
+                       goto fin;
+               }
+
+               if (copy_from_user(&writest, user_buf, sizeof(struct mst_write_block_st))) {
+                       res = -EFAULT;
+                       goto fin;
+               }
+
+               if (writest.size % sizeof(u32)) {
+                       mst_err("invalid size. size should be in bytes and devide sizeof(u32)\n");
+                       res = -EINVAL;
+                       goto fin;
+               }
+
+               if ((writest.offset + writest.size) > MST_MEMORY_SIZE) {
+                       mst_err("accesing invalid address\n");
+                       res = -EINVAL;
+                       goto fin;
+               }
+
+               /* endianness conversion */
+               for (i = 0; i < (writest.size / sizeof(u32)) ; ++i)
+                       cpu_to_be32s(&(writest.data[i]));
+
+               /* write to hardware */
+               memcpy_toio(dev->hw_addr + writest.offset, writest.data, writest.size);
+
+               break;
+       }
+
+       case PCICONF_INIT: {
+               struct mst_pciconf_init_st initst;
+
+               if (dev->initialized) {
+                       mst_err("device already initialized\n");
+                       res = ENODEV;
+                       goto fin;
+               }
+
+               if (dev->type != PCICONF) {
+                       mst_err("wrong type for device\n");
+                       res = -EPERM;
+                       goto fin;
+               }
+
+
+               if (copy_from_user(&initst, user_buf, sizeof(struct mst_pciconf_init_st))) {
+                       res = -EFAULT;
+                       goto fin;
+               }
+
+               dev->addr_reg = initst.addr_reg;
+               dev->data_reg = initst.data_reg;
+               dev->initialized = 1;
+               break;
+       }
+
+       case PCICONF_STOP: {
+               if (!dev->initialized) {
+                       mst_err("device is not initialized\n");
+                       res = -ENODEV;
+                       goto fin;
+               }
+
+               if (dev->type != PCICONF) {
+                       mst_err("wrong type for device\n");
+                       res = -EPERM;
+                       goto fin;
+               }
+
+               dev->initialized = 0;
+               break;
+       }
+
+
+       case PCIMEM_INIT: {
+               struct mst_mem_init_st initst;
+               unsigned long resource_start;
+
+               if (dev->initialized) {
+                       mst_err("device already initialized\n");
+                       res = ENODEV;
+                       goto fin;
+               }
+
+               if (dev->type != PCIMEM) {
+                       mst_err("wrong type for device\n");
+                       res = -EPERM;
+                       goto fin;
+               }
+
+               if (copy_from_user(&initst, user_buf, sizeof(struct mst_mem_init_st))) {
+                       res = -EFAULT;
+                       goto fin;
+               }
+
+               /* unmap previously mapped device if it was not stopped properly */
+               if (dev->hw_addr) {
+                       iounmap(cur->hw_addr);
+                       dev->hw_addr = NULL;
+               }
+
+               dev->bar = initst.bar;
+               resource_start = pci_resource_start(dev->pci_dev, dev->bar);
+
+               dev->hw_addr = ioremap(resource_start, MST_MEMORY_SIZE);
+
+               if (dev->hw_addr <= 0) {
+                       mst_err("could not map device memory\n");
+                       res = -EFAULT;
+                       goto fin;
+               }
+
+               dev->initialized = 1;
+               break;
+       }
+
+       case PCIMEM_STOP: {
+               if (!dev->initialized) {
+                       mst_err("device is not initialized\n");
+                       res = -ENODEV;
+                       goto fin;
+               }
+
+               if (dev->type != PCIMEM) {
+                       mst_err("wrong type for device\n");
+                       res = -EPERM;
+                       goto fin;
+               }
+
+               if (cur->hw_addr)
+                       iounmap(cur->hw_addr);
+
+               cur->hw_addr = NULL;
+               dev->initialized = 0;
+               break;
+       }
+
+       case PCI_CONNECTX_WA: {
+               struct mst_connectx_wa *md = file->private_data;
+               unsigned int slot_mask;
+
+               if (!dev->initialized) {
+                       mst_err("device is not initialized\n");
+                       res = -ENODEV;
+                       goto fin;
+               }
+
+               /* slot exists */
+               if (md->connectx_wa_slot_p1) {
+                       mst_err("slot exits for file %s, slot:0x%x\n",
+                                       dev->name, md->connectx_wa_slot_p1);
+                       res = -EPERM;
+                       goto fin;
+               }
+
+               /* find first un(set) bit. and remember the slot */
+               md->connectx_wa_slot_p1 = ffs(~dev->connectx_wa_slots);
+               if (md->connectx_wa_slot_p1 == 0 || md->connectx_wa_slot_p1 > CONNECTX_WA_SIZE) {
+                       res = -ENOLCK;
+                       goto fin;
+               }
+
+               slot_mask = 1 << (md->connectx_wa_slot_p1 - 1);
+               /* set the slot as taken */
+               dev->connectx_wa_slots |= slot_mask;
+
+               /*
+                * mst_info("CONNECTX_WA: Took slot %u. Current slots: %02x\n",
+                *                      md->connectx_wa_slot_p1 - 1, dev->connectx_wa_slots);
+                */
+               if (copy_to_user(user_buf, md, sizeof(struct mst_connectx_wa))) {
+                       res = -EFAULT;
+                       goto fin;
+               }
+               break;
+       }
+
+       case PCICONF_VPD_READ4: {
+               u32 out;
+               u32 *dataout = NULL;
+               struct mst_vpd_read4_st readst;
+
+               if (!dev->initialized) {
+                       mst_err("device is not initialized\n");
+                       res = ENODEV;
+                       goto fin;
+               }
+
+               if (dev->type != PCICONF) {
+                       mst_err("wrong type for device\n");
+                       res = -EPERM;
+                       goto fin;
+               }
+
+               if (copy_from_user(&readst, user_buf, sizeof(struct mst_vpd_read4_st))) {
+                       res = -EFAULT;
+                       goto fin;
+               }
+
+               res = pci_read4_vpd(dev, readst.timeout, readst.offset, &out);
+               if (res) {
+                       goto fin;
+               }
+
+               /* retrieve to user - we noticed that we need to swap always */
+               dataout = &((struct mst_vpd_read4_st *)user_buf)->data;
+                out = le32_to_cpu(out);
+               if (copy_to_user(dataout, &out, sizeof(u32))) {
+                       res = -EFAULT;
+                       goto fin;
+               }
+               break;
+       }
+
+       case PCICONF_VPD_WRITE4: {
+               struct mst_vpd_write4_st writest;
+
+               if (!dev->initialized) {
+                       mst_err("device is not initialized\n");
+                       res = ENODEV;
+                       goto fin;
+               }
+
+               if (dev->type != PCICONF) {
+                       mst_err("wrong type for device\n");
+                       res = -EPERM;
+                       goto fin;
+               }
+
+               if (copy_from_user(&writest, user_buf, sizeof(struct mst_vpd_write4_st))) {
+                       res = -EFAULT;
+                       goto fin;
+               }
+                writest.data = le32_to_cpu(writest.data);
+               res = pci_write4_vpd(dev, writest.timeout, writest.offset, writest.data);
+               if (res) {
+                       goto fin;
+               }
+               break;
+       }
+
+       default: {
+               mst_err("incorrect opcode = %x available opcodes:\n", opcode);
+               print_opcode();
+               res = -EINVAL;
+               break;
+       }
+       }
+
+fin:
+       if (IS_LOCK_NEEDED(dev))
+               mutex_unlock(&dev->lock);
+fin_err:
+       return res;
+}
+
+#if HAVE_COMPAT_IOCTL
+static long compat_ioctl (struct file *f, unsigned int o, unsigned long d)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0)
+    struct inode *n = f->f_dentry->d_inode;
+#else
+    struct inode *n = f->f_path.dentry->d_inode;
+#endif
+       return mst_ioctl(n, f, o, d);
+}
+#endif
+
+#ifdef HAVE_UNLOCKED_IOCTL
+static long unlocked_ioctl (struct file *f, unsigned int o, unsigned long d)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0)
+    struct inode *n = f->f_dentry->d_inode;
+#else
+    struct inode *n = f->f_path.dentry->d_inode;
+#endif
+
+       return mst_ioctl(n, f, o, d);
+}
+#endif
+
+/****************************************************/
+static inline const char *dev_type_to_str(enum dev_type type)
+{
+       switch (type) {
+       case PCICONF:
+               return "PCICONF";
+       case PCIMEM:
+               return "PCIMEM";
+       default:
+               return "UNKNOWN";
+       }
+}
+
+
+/****************************************************/
+static const struct file_operations mst_fops = {
+       .read           = mst_read,
+       .write          = mst_write,
+
+#ifdef HAVE_UNLOCKED_IOCTL
+       .unlocked_ioctl = unlocked_ioctl,
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)
+       .ioctl          = mst_ioctl,
+#endif
+
+#if HAVE_COMPAT_IOCTL
+       .compat_ioctl   = compat_ioctl,
+#endif
+
+       .open           = mst_open,
+       .release        = mst_release,
+       .owner          = THIS_MODULE,
+};
+
+static struct mst_dev_data *mst_device_create(enum dev_type type,
+               struct pci_dev *pdev)
+{
+       struct mst_dev_data *dev = NULL;
+        char dbdf[20];
+
+       dev = kzalloc(sizeof(struct mst_dev_data), GFP_KERNEL);
+       if (!dev) {
+               mst_err("failed allocating new %s device with id=0x%x\n",
+                               dev_type_to_str(type),
+                               pdev->device);
+               return NULL;
+       }
+
+        sprintf(dbdf, "%4.4x:%2.2x:%2.2x.%1.1x", pci_domain_nr(pdev->bus), pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
+       switch (type) {
+       case PCICONF:
+               dev->addr_reg   = MST_CONF_ADDR_REG;
+               dev->data_reg   = MST_CONF_DATA_REG;
+               dev->bar        = 0;            /* invalid */
+               dev->hw_addr    = NULL;         /* invalid */
+               snprintf(dev->name,
+                               MST_NAME_SIZE,
+                               "%s" MST_PCICONF_DEVICE_NAME,
+                                dbdf);
+               break;
+       case PCIMEM:
+               dev->addr_reg   = 0;            /* invalid */
+               dev->data_reg   = 0;            /* invalid */
+               dev->bar        = pci_match_id(mst_bar1_pci_table, pdev) ? 1 : 0;
+               dev->hw_addr    = ioremap(pci_resource_start(pdev, dev->bar),
+                               MST_MEMORY_SIZE);
+               if (dev->hw_addr <= 0) {
+                       mst_err("could not map device memory\n");
+                       goto out;
+               }
+
+               snprintf(dev->name,
+                               MST_NAME_SIZE,
+                               "%s" MST_PCIMEM_DEVICE_NAME,
+                               dbdf);
+               break;
+       default:
+               mst_err("failed to %s, unknown device type 0x%x\n",
+                               __func__, dev->type);
+               goto out;
+       }
+
+       dev->type                       = type;
+       dev->pci_dev            = pdev;
+       mutex_init(&dev->lock);
+
+       dev->vpd_cap_addr = pci_find_capability(pdev, PCI_CAP_ID_VPD);
+
+    if (alloc_chrdev_region(&dev->my_dev, 0, 1, dev->name)) {
+        mst_err("failed to allocate chrdev_region\n");
+    }
+    if ( (dev->cl = class_create( THIS_MODULE, dev->name ) ) == NULL ) {
+        printk(KERN_ALERT "Class creation failed\n");
+        unregister_chrdev_region(dev->my_dev, 1);
+        goto out;
+    }
+
+    if( device_create(dev->cl, NULL, dev->my_dev, NULL, dev->name) == NULL) {
+        printk(KERN_ALERT "Device creation failed\n");
+        class_destroy(dev->cl);
+        unregister_chrdev_region(dev->my_dev , 1);
+        goto out;
+    }
+
+    dev->major = MAJOR(dev->my_dev);
+    cdev_init(&dev->mcdev, &mst_fops);
+    cdev_add(&dev->mcdev, dev->my_dev, 1); //TODO check if cdev_add fails
+
+       dev->initialized = 1;
+       list_add_tail(&dev->list, &mst_devices);
+
+       return dev;
+out:
+       kfree(dev);
+       return NULL;
+}
+
+
+static void mst_device_destroy(struct mst_dev_data *dev)
+{
+       if (dev->hw_addr) {
+               iounmap(dev->hw_addr);
+       }
+
+        cdev_del(&dev->mcdev);
+        device_destroy(dev->cl, dev->my_dev);
+        class_destroy(dev->cl);
+        unregister_chrdev_region(dev->my_dev, 1);
+       list_del(&dev->list);
+       kfree(dev);
+}
+
+
+
+/****************************************************/
+static int __init mst_init(void)
+{
+       int device_exists                       = 0;
+       struct pci_dev  *pdev           = NULL;
+       struct mst_dev_data *dev        = NULL;
+       struct mst_dev_data *cur        = NULL;
+
+       mst_info("%s - version %s\n", mst_driver_string, mst_driver_version);
+
+       while ((pdev = pci_get_device(MST_MELLANOX_PCI_VENDOR, PCI_ANY_ID, pdev)) != NULL) {
+        if (!pci_match_id(supported_pci_devices, pdev) && !pci_match_id(mst_livefish_pci_table, pdev)) {
+            continue;
+        }
+               device_exists = 0;
+               list_for_each_entry(cur, &mst_devices, list) {
+                       if (cur->pci_dev->bus->number == pdev->bus->number) {
+                               device_exists = 1;      /* device already exists */
+                               break;
+                       }
+               }
+               if (device_exists) {
+                       continue;
+               }
+
+               /* skip virtual fucntion */
+               if (PCI_FUNC(pdev->devfn)) {
+                       continue;
+               }
+
+               /* found new device */
+               mst_info("found device - "
+                               "domain=0x%x, bus=0x%x, slot=0x%x, func=0x%x, vendor=0x%x, device=0x%x\n",
+                               pci_domain_nr(pdev->bus),
+                               pdev->bus->number,
+                               PCI_SLOT(pdev->devfn),
+                               PCI_FUNC(pdev->devfn),
+                               pdev->vendor,
+                               pdev->device);
+
+               /* create PCICONF for this device */
+               dev = mst_device_create(PCICONF, pdev);
+               if (!dev) {
+                       mst_err("failed to mst_device_create\n");
+                       continue; /* PCICONF creation failed, no point creating a PCIMEM device */
+               }
+
+               /*
+                * for livefish devices we only allocate PCICONF
+                * for non livefish both PCICONF and PCIMEM
+                */
+               if (!pci_match_id(mst_livefish_pci_table, pdev)) {
+                       /* create new mst_device for PCIMEM */
+                       dev = mst_device_create(PCIMEM, pdev);
+                       if (!dev) {
+                               mst_err("failed to mst_device_create\n");
+                               continue;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static void __exit mst_cleanup(void)
+{
+       struct mst_dev_data *cur, *temp;
+
+       /* free all mst_devices */
+       list_for_each_entry_safe(cur, temp, &mst_devices, list) {
+               mst_device_destroy(cur);
+       }
+}
+
+
+/****************************************************/
+module_init(mst_init);
+module_exit(mst_cleanup);
diff --git a/drivers/net/ethernet/mellanox/mstflint_access/mst_vpd.c b/drivers/net/ethernet/mellanox/mstflint_access/mst_vpd.c
new file mode 100644 (file)
index 0000000..36ebae1
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2011-2014 Mellanox Technologies, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/highmem.h>
+#include <linux/sched.h>
+#include "mst_kernel.h"
+
+
+/****************************************************/
+int pci_read4_vpd(struct mst_dev_data *dev, unsigned int timeout,
+               unsigned offset, u32 *buf)
+{
+       struct pci_dev *pci_dev = dev->pci_dev;
+       int vpd_cap = dev->vpd_cap_addr;
+       unsigned long end;
+       uint16_t addr;
+       int done, res = 0;
+
+
+       if (!vpd_cap) {
+               mst_err("device %s not support Vital Product Data\n", dev->name);
+               return -ENODEV;
+       }
+
+       if (!timeout) {
+               timeout = MST_VPD_DEFAULT_TOUT;
+       }
+
+       /* sets F bit to zero and write VPD addr */
+       addr = (0x7fff & offset);
+       res = pci_write_config_word(pci_dev, vpd_cap + PCI_VPD_ADDR, addr);
+       if (res) {
+               mst_err("pci_write_config_dword failed\n");
+               return res;
+       }
+
+       /* wait for data until F bit is set with one */
+       addr = 0x0;
+       done = 0;
+
+       end = msecs_to_jiffies(timeout) + jiffies;
+       while (time_before(jiffies, end)) {
+               res = pci_read_config_word(pci_dev, vpd_cap + PCI_VPD_ADDR, &addr);
+               if (res) {
+                       mst_err("pci_read_config_word failed\n");
+                       return res;
+               }
+
+               if (addr & 0x8000) {
+                       done = 1;
+                       break;
+               }
+
+               cond_resched();
+       }
+
+       if (!done) {
+               return -ETIMEDOUT;
+       }
+
+       /* read data */
+       res = pci_read_config_dword(pci_dev, vpd_cap + PCI_VPD_DATA, buf);
+       if (res) {
+               mst_err("pci_read_config_word failed\n");
+               return res;
+       }
+
+       return res;
+}
+EXPORT_SYMBOL(pci_read4_vpd);
+
+int pci_write4_vpd(struct mst_dev_data *dev, unsigned int timeout,
+               unsigned offset, u32 buf)
+{
+       struct pci_dev *pci_dev = dev->pci_dev;
+       int vpd_cap = dev->vpd_cap_addr;
+       unsigned long end;
+       uint16_t addr;
+       int done, res = 0;
+
+
+       if (!vpd_cap) {
+               mst_err("device %s not support Vital Product Data\n", dev->name);
+               return -ENODEV;
+       }
+
+       if (!timeout) {
+               timeout = MST_VPD_DEFAULT_TOUT;
+       }
+
+       /* write data */
+       res = pci_write_config_dword(pci_dev, vpd_cap + PCI_VPD_DATA, buf);
+       if (res) {
+               mst_err("pci_read_config_word failed\n");
+               return res;
+       }
+
+       /* sets F bit to one and write VPD addr */
+       addr = 0x8000 | (0x7ffff & offset);
+       res = pci_write_config_word(pci_dev, vpd_cap + PCI_VPD_ADDR, addr);
+       if (res) {
+               mst_err("pci_write_config_dword failed\n");
+               return res;
+       }
+
+       /* wait for data until F bit is set with zero */
+       addr = 0x0;
+       done = 0;
+
+       end = msecs_to_jiffies(timeout) + jiffies;
+       while (time_before(jiffies, end)) {
+               res = pci_read_config_word(pci_dev, vpd_cap + PCI_VPD_ADDR, &addr);
+               if (res) {
+                       mst_err("pci_read_config_word failed\n");
+                       return res;
+               }
+
+               if (!(addr & 0x8000)) {
+                       done = 1;
+                       break;
+               }
+
+               cond_resched();
+       }
+
+       if (!done) {
+               return -ETIMEDOUT;
+       }
+
+       return res;
+}
+EXPORT_SYMBOL(pci_write4_vpd);
index d8764c9ba2001ee9ee2817d32a81cb176dc56438..9fea003df41bf0abe2306e685a849186c2ced90e 100644 (file)
@@ -2369,6 +2369,7 @@ CONFIG_MLX4_DEBUG=y
 CONFIG_MLX4_VNIC=m
 # CONFIG_MLX4_VNIC_DEBUG is not set
 CONFIG_MLX5_CORE=m
+CONFIG_MSTFLINT_ACCESS=m
 CONFIG_NET_VENDOR_MICREL=y
 # CONFIG_KS8842 is not set
 # CONFIG_KS8851_MLL is not set
index 1ad059c9f8025ed556caeeffd698653acdb5b3ef..f2b2feeb19cb5f6ce3638783919ff238489702c4 100644 (file)
@@ -2356,6 +2356,7 @@ CONFIG_MLX4_DEBUG=y
 CONFIG_MLX4_VNIC=m
 # CONFIG_MLX4_VNIC_DEBUG is not set
 CONFIG_MLX5_CORE=m
+CONFIG_MSTFLINT_ACCESS=m
 CONFIG_NET_VENDOR_MICREL=y
 # CONFIG_KS8842 is not set
 # CONFIG_KS8851_MLL is not set
index 9645ed6e79817b57e50ddbb106c6504dc52790f2..6ce491b56bde0171e923404c2ed0fbf6a2719e09 100644 (file)
@@ -348,6 +348,7 @@ kernel/drivers/net/ethernet/mellanox/mlx4/mlx4_en.ko
 kernel/drivers/net/ethernet/mellanox/mlx4/mlx4_core.ko
 kernel/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.ko
 kernel/drivers/net/ethernet/mellanox/mlx4_vnic/mlx4_vnic.ko
+kernel/drivers/net/ethernet/mellanox/mstflint_access/mstflint_access.ko
 kernel/drivers/net/ethernet/cisco/enic/enic.ko
 kernel/drivers/net/ethernet/ethoc.ko
 kernel/drivers/net/ethernet/8390/ne2k-pci.ko
index d50635337168b69efd427cdf15d5f4892561dcff..5a20058642f8b129f038ae9cc900709f350ebb21 100644 (file)
@@ -2387,6 +2387,7 @@ CONFIG_MLX4_DEBUG=y
 CONFIG_MLX4_VNIC=m
 # CONFIG_MLX4_VNIC_DEBUG is not set
 CONFIG_MLX5_CORE=m
+CONFIG_MSTFLINT_ACCESS=m
 CONFIG_NET_VENDOR_MICREL=y
 # CONFIG_KS8842 is not set
 # CONFIG_KS8851_MLL is not set
index f33f685d9472c2cff3b6d1c127c08fe01dbb0d00..f34691bc6d1b33e20bbd5139822398441baf175f 100644 (file)
@@ -2374,6 +2374,7 @@ CONFIG_MLX4_DEBUG=y
 CONFIG_MLX4_VNIC=m
 # CONFIG_MLX4_VNIC_DEBUG is not set
 CONFIG_MLX5_CORE=m
+CONFIG_MSTFLINT_ACCESS=m
 CONFIG_NET_VENDOR_MICREL=y
 # CONFIG_KS8842 is not set
 # CONFIG_KS8851_MLL is not set
index defaebbfefa81556bcc10175bc46d1cd32de81ed..181327e88bff3dc136bd73a1c97aa42fe4bcd001 100644 (file)
@@ -340,6 +340,7 @@ kernel/drivers/net/ethernet/mellanox/mlx4/mlx4_en.ko
 kernel/drivers/net/ethernet/mellanox/mlx4/mlx4_core.ko
 kernel/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.ko
 kernel/drivers/net/ethernet/mellanox/mlx4_vnic/mlx4_vnic.ko
+kernel/drivers/net/ethernet/mellanox/mstflint_access/mstflint_access.ko
 kernel/drivers/net/ethernet/cisco/enic/enic.ko
 kernel/drivers/net/ethernet/ethoc.ko
 kernel/drivers/net/ethernet/dnet.ko