/*
 * Sliff - Scrutiny library foundation - Driver helpers
 *
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * NO WARRANTY
 * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
 * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
 * solely responsible for determining the appropriateness of using and
 * distributing the Program and assumes all risks associated with its
 * exercise of rights under this Agreement, including but not limited to
 * the risks and costs of program errors, damage to or loss of data,
 * programs or equipment, and unavailability or interruption of operations.

 * DISCLAIMER OF LIABILITY
 * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), 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 OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
 * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
 * USA.
 *
 * Authors: Ganesh Ramachandran <ganesh.ramachandran@broadcom.com>
 *          Rajesh Ravi <rajesh.ravi@broadcom.com>
 */

#define SLIFF_DRIVER

#include <asm/io.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/mm.h>
#include <linux/delay.h>
#include <linux/compat.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/dmapool.h>
#include <linux/device.h>
#include <asm/dma-mapping.h>
#include <asm-generic/delay.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0)
#include <asm/set_memory.h>
#endif



#if LINUX_VERSION_CODE <= KERNEL_VERSION(4,12,1)
#include <asm/uaccess.h>
#else
#include <linux/uaccess.h>
#endif

#include "SliffDriver.h"

#ifdef DRIVER_SUPPORT_NIC
#include "bnxt_hsi.h"
#include "bnxt_pci_info.h"
#endif


#define PCI_DEVFN(slot, func)   ((((slot) & 0x1f) << 3) | ((func) & 0x07))
#define PCI_SLOT(devfn)         (((devfn) >> 3) & 0x1f)
#define PCI_FUNC(devfn)         ((devfn) & 0x07)
/***********************************************
 *               Globals
 **********************************************/
// Global ECAM 64-bit address from ACPI table
//
// Probing is only enabled on i386 or x64 platforms since it requires
// parsing ACPI tables.  This is not supported on non-x86 platforms.
//
#if (defined(__i386__) || defined(__x86_64__))
    static U32 gAcpiAddrECAM[3] = { 0, 0, ACPI_PCIE_NOT_PROBED };
#else
    static U32 gAcpiAddrECAM[3] = { 0, 0, ACPI_PCIE_ALWAYS_USE_OS };
#endif

#ifdef DRIVER_SUPPORT_NIC
static SLIFF_DRV_INFO *pmgdev;

bool is_owner_drv_loaded(struct pci_dev *pdev)
{
    struct module *owner;
    void   *drv_data;

    if(!pdev)
    {
        return 0;
    }

    /* Use pdev->driver directly instead of pci_dev_driver() API as
     * it returns a compatible driver even when pdev->driver = 0 */
    if(!pdev->driver)
    {
        return 0;
    }

    drv_data = pci_get_drvdata(pdev);

    owner = pdev->driver->driver.owner;
    if(!owner)
    {
        return 0;
    }

    if((owner->state == MODULE_STATE_LIVE) && drv_data)
    {
        return 1;
    }

    return 0;
}

static void sdDriverFreeBnxtDevice(SLIFF_DRV_INFO *pmgdev, int domain, int b, int df)
{
    SLIFF_DRV_BNXT_DEV_INFO *pinfo;
    struct pci_dev *PtrDev = NULL;
    struct mutex *pinfo_lock;

    mutex_lock(&pmgdev->lock);
    pinfo = pmgdev->pinfo[b][df];
    /*
     * TODO: if(domain != pinfo->domain)
                pinfo = search_dev_info(pmgdev, domain, b, df);
     */
    pmgdev->pinfo[b][df] = NULL;
    pinfo_lock = &pmgdev->info_lock[b][df];
    mutex_unlock(&pmgdev->lock);

    mutex_lock(pinfo_lock);
    if (pinfo) {
        PtrDev = pinfo->PtrDev;
        if (pinfo->fw_health) {
            kfree(pinfo->fw_health);
            pinfo->fw_health = NULL;
        }
        /* Free DMA buffers */
        if(pinfo->resp_virt_addr && pinfo->resp_dma_addr)
        {
            dma_free_coherent(&PtrDev->dev, HWRM_MAX_RESP_LEN,
                            pinfo->resp_virt_addr,
                            pinfo->resp_dma_addr);
        }
        if(pinfo->short_cmd_req_virt_addr && pinfo->short_cmd_req_dma_addr)
        {
            dma_free_coherent(&PtrDev->dev, HWRM_MAX_REQ_LEN,
                            pinfo->short_cmd_req_virt_addr,
                            pinfo->short_cmd_req_dma_addr);
        }

        if(pinfo->bar0) {
            pci_iounmap(PtrDev, pinfo->bar0);
        }

        if(pinfo->bar1) {
            pci_iounmap(PtrDev, pinfo->bar1);
        }

        pinfo->bar0 = pinfo->bar1 = NULL;

#ifdef FACTORY_MODE_SUPPORT
        if(pinfo->resource_region_acquired)
        {
            pci_release_regions(PtrDev);
            pinfo->resource_region_acquired = 0;
        }
#endif
        /*Don't move the device to disable state when bnxt_en is alive*/
        if(atomic_read(&PtrDev->enable_cnt) > 1 || (!is_owner_drv_loaded(PtrDev))) {
            for (;pinfo->enabled_pci_dev; pinfo->enabled_pci_dev--)
            pci_disable_device(PtrDev);
        }

        kfree(pinfo);
    }

    mutex_unlock(pinfo_lock);

    return;
}

static int sliff_pcidev_event(struct notifier_block *nb,
                unsigned long action, void *data)
{
    struct device *dev = data;
    struct pci_dev *pdev = to_pci_dev(dev);
    SLIFF_DRV_BNXT_DEV_INFO *pinfo;
    struct mutex *pinfo_lock;

    if (!pdev)
        return 0;

    switch (action) 
    {
        case BUS_NOTIFY_ADD_DEVICE:
            return 0;
        case BUS_NOTIFY_DEL_DEVICE:
            mutex_lock(&pmgdev->lock);
            pinfo = pmgdev->pinfo[pdev->bus->number][pdev->devfn];
            pinfo_lock = &pmgdev->info_lock[pdev->bus->number][pdev->devfn];
            mutex_unlock(&pmgdev->lock);
            mutex_lock(pinfo_lock);
            if (pinfo)
            {
                pinfo->deleted = 1;
                printk("Sliff : device delete Notification b:%d df:%d\n", pdev->bus->number, pdev->devfn);
            }
            mutex_unlock(pinfo_lock);

            return 0;
        default:
            return 0;
    }
    return 0;
}

static struct notifier_block sliff_register_pcidevice_notifier = {
        .notifier_call = sliff_pcidev_event,
};

#endif

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
static void sleep_jiffies(unsigned long jiffies)
{
    set_current_state(TASK_UNINTERRUPTIBLE);
    schedule_timeout(jiffies);
}
#endif

static int sdiInitialize (void)
{
    int status;
#ifdef DRIVER_SUPPORT_NIC
    int j, k;
#endif

    pr_debug("sliff - %s: <enter>\n", __FUNCTION__);

    status = alloc_chrdev_region (&DriverMajorMinor, 0, 1, "sliff");

    if (status)
    {
        printk (KERN_ERR "sliff - %s: alloc_chrdev_region failed\n", __FUNCTION__);

        return status;
    }

#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,4,0)
    chrdev_class = class_create("sliffClass");
#else
    chrdev_class = class_create(THIS_MODULE, "sliffClass");
#endif

    if (NULL == chrdev_class)
    {
        printk (KERN_ERR "sliff - %s: class_create failed\n", __FUNCTION__);

        goto unreg_chrdev;
    }

    DriverMajor = MAJOR (DriverMajorMinor);
    DriverMinor = MINOR (DriverMajorMinor);
    DeviceNumber = MKDEV (DriverMajor, DriverMinor);

    printk (KERN_INFO "Broadcom generic character interface driver for PCI access");
    printk (KERN_INFO "sliff - %s: Major Number %d Minor Number %d \n", __FUNCTION__, DriverMajor, DriverMinor);

    DeviceControl = cdev_alloc();

    if (NULL == DeviceControl)
    {
        printk (KERN_ERR "sliff - %s:  cdev_alloc failed\n", __FUNCTION__);
        status = -1;
        goto unreg_chrdev;
    }

    DeviceControl->owner = THIS_MODULE;
    DeviceControl->ops   = &SliffDriverOperations;

    status = (cdev_add (DeviceControl, DeviceNumber, 1));

    if (status)
    {
        printk (KERN_ERR "sliff - %s: Failed adding driver Error code '%d'\n", __FUNCTION__, status);
        goto dstry_class;
    }

    if (NULL == device_create (chrdev_class, NULL, DeviceNumber, NULL, "sliff"))
    {
        printk (KERN_ERR "sliff - %s: device_create failed\n", __FUNCTION__);
        status = -1;
        goto del_cdev;
    }

    printk (KERN_INFO "sliff - version %s:loaded\n", SLIFF_DRIVER_VERSION);

#ifdef DRIVER_SUPPORT_NIC
    pmgdev = kzalloc(sizeof(*pmgdev), GFP_KERNEL);
    if (!pmgdev)
        return -EINVAL;

    mutex_init(&pmgdev->lock);

    for(j = 0; j < MAX_BUS; j++)
    {
        for(k = 0; k < MAX_DEVFN; k++)
        {
            mutex_init(&pmgdev->info_lock[j][k]);
        }
    }

    /* register for PCI device events */
    status = bus_register_notifier(&pci_bus_type, &sliff_register_pcidevice_notifier);
    if (status) {
        pr_err("Sliff : Cannot register to PCI events\n");
        kfree(pmgdev);
        goto del_cdev;
    }
#endif

    /*
     * Return success.
     */
    return (0);

del_cdev:
    cdev_del (DeviceControl);

dstry_class:
    class_destroy (chrdev_class);

unreg_chrdev:
    unregister_chrdev_region (DriverMajorMinor, 1);

    pr_debug("sliff - %s: <exit>\n", __FUNCTION__);
    return status;
}

#ifdef DRIVER_SUPPORT_NIC
static inline void sdDriver_map_fw_health_reg(SLIFF_DRV_BNXT_DEV_INFO *pinfo, u32 reg)
{
	writel(reg & BNXT_GRC_BASE_MASK, pinfo->bar0 +
					 BNXT_GRCPF_REG_WINDOW_BASE_OUT +
					 BNXT_FW_HEALTH_WIN_MAP_OFF);
}

static int sdDriver_alloc_fw_health(SLIFF_DRV_BNXT_DEV_INFO *pinfo)
{
	if (pinfo->fw_health)
		return 0;

	pinfo->fw_health = kzalloc(sizeof(*pinfo->fw_health), GFP_KERNEL);
	if (!pinfo->fw_health)
		return -ENOMEM;

	mutex_init(&pinfo->fw_health->lock);
	return 0;
}

u32 sdDriver_fw_health_readl(SLIFF_DRV_BNXT_DEV_INFO *pinfo, int reg_idx)
{
	struct bnxt_fw_health *fw_health = pinfo->fw_health;
	u32 reg = fw_health->regs[reg_idx];
	u32 reg_type, reg_off, val = 0;

	if(!fw_health) return (u32)-1;

	reg_type = BNXT_FW_HEALTH_REG_TYPE(reg);
	reg_off = BNXT_FW_HEALTH_REG_OFF(reg);
	switch (reg_type) {
	case BNXT_FW_HEALTH_REG_TYPE_CFG:
		pci_read_config_dword(pinfo->PtrDev, reg_off, &val);
		break;
	case BNXT_FW_HEALTH_REG_TYPE_GRC:
		reg_off = fw_health->mapped_regs[reg_idx];
		fallthrough;
		/* fall through */
	case BNXT_FW_HEALTH_REG_TYPE_BAR0:
		val = readl(pinfo->bar0 + reg_off);
		break;
	case BNXT_FW_HEALTH_REG_TYPE_BAR1:
		/*val = readl(pinfo->bar1 + reg_off);*/
		/* status through BAR1 is not always supported. Eg.when FW is not alive */
		return (u32)-1;
		break;
	}
	if (reg_idx == BNXT_FW_RESET_INPROG_REG)
		val &= fw_health->fw_reset_inprog_reg_mask;
	return val;
}

static void sdDriver_try_map_fw_health_reg(SLIFF_DRV_BNXT_DEV_INFO *pinfo)
{
	void __iomem *hs;
	u32 status_loc;
	u32 reg_type;
	u32 sig;

	if (pinfo->fw_health)
		pinfo->fw_health->status_reliable = false;
	else
		return;

	sdDriver_map_fw_health_reg(pinfo, HCOMM_STATUS_STRUCT_LOC);
	hs = pinfo->bar0 + BNXT_FW_HEALTH_WIN_OFF(HCOMM_STATUS_STRUCT_LOC);

	sig = readl(hs + offsetof(struct hcomm_status, sig_ver));
	if ((sig & HCOMM_STATUS_SIGNATURE_MASK) != HCOMM_STATUS_SIGNATURE_VAL) {
		if (!pinfo->chip_num) {
			sdDriver_map_fw_health_reg(pinfo, BNXT_GRC_REG_BASE);
			pinfo->chip_num = readl(pinfo->bar0 +
					     BNXT_FW_HEALTH_WIN_BASE +
					     BNXT_GRC_REG_CHIP_NUM);
		}
		if (!BNXT_CHIP_P5(pinfo))
			return;

		status_loc = BNXT_GRC_REG_STATUS_P5 |
			     BNXT_FW_HEALTH_REG_TYPE_BAR0;
	} else {
		status_loc = readl(hs + offsetof(struct hcomm_status,
						 fw_status_loc));
	}

	if (sdDriver_alloc_fw_health(pinfo)) {
		pr_alert("no memory for firmware status checks\n");
		return;
	}

	pinfo->fw_health->regs[BNXT_FW_HEALTH_REG] = status_loc;
	reg_type = BNXT_FW_HEALTH_REG_TYPE(status_loc);
	if (reg_type == BNXT_FW_HEALTH_REG_TYPE_GRC) {
		sdDriver_map_fw_health_reg(pinfo, status_loc);
		pinfo->fw_health->mapped_regs[BNXT_FW_HEALTH_REG] =
			BNXT_FW_HEALTH_WIN_OFF(status_loc);
	}

	pinfo->fw_health->status_reliable = true;
}

#ifdef COMPLETE_FW_HEALTH_REG_SUPPORT
static int sdDriver_map_fw_health_regs(SLIFF_DRV_BNXT_DEV_INFO *pinfo)
{
	struct bnxt_fw_health *fw_health = pinfo->fw_health;
	u32 reg_base = 0xffffffff;
	int i;

	if(!fw_health) {
		return -1;
	}

	pinfo->fw_health->status_reliable = false;
	pinfo->fw_health->resets_reliable = false;
	/* Only pre-map the monitoring GRC registers using window 3 */
	for (i = 0; i < 4; i++) {
		u32 reg = fw_health->regs[i];

		if (BNXT_FW_HEALTH_REG_TYPE(reg) != BNXT_FW_HEALTH_REG_TYPE_GRC)
			continue;
		if (reg_base == 0xffffffff)
			reg_base = reg & BNXT_GRC_BASE_MASK;
		if ((reg & BNXT_GRC_BASE_MASK) != reg_base)
			return -ERANGE;
		fw_health->mapped_regs[i] = BNXT_FW_HEALTH_WIN_OFF(reg);
	}
	pinfo->fw_health->status_reliable = true;
	pinfo->fw_health->resets_reliable = true;
	if (reg_base == 0xffffffff)
		return 0;

	sdDriver_map_fw_health_reg(pinfo, reg_base);
	return 0;
}

static int sdDriver_check_fw_health_status(SLIFF_DRV_BNXT_DEV_INFO *pinfo)
{
	if (pinfo->fw_health && pinfo->fw_health->status_reliable) {
		int  rc =  -ENODEV;
		u32 sts;

		sts = sdDriver_read_fw_health_reg(pinfo);

		if (BNXT_FW_IS_HEALTHY(sts)) {
			pr_debug("Firmware is healthy!\n");
			return 0;
		} else {
			pr_err("Firmware not responding, status: 0x%x\n", sts);
		}

		if (BNXT_FW_IS_BOOTING(sts)) {
			pr_err("Firmware is not ready it's still booting!\n");
		}
		else if (BNXT_FW_IS_RECOVERING(sts)) {
			pr_err("Firmware is not ready it's still recovering!\n");
			/* return success otherwise HWRM commands in recovery path fail breaking repaving */
			return 0;
		}

		if (sts & FW_STATUS_REG_CRASHED_NO_MASTER) {
			pr_info("Firmware recovery via OP-TEE requested\n");
		}

		return rc;
	}

	return -ENODEV;
}
#endif

static U32 sdDriver_read_fw_health_reg(SLIFF_DRV_BNXT_DEV_INFO *pinfo)
{
	return sdDriver_fw_health_readl(pinfo, BNXT_FW_HEALTH_REG);
}

static inline bool
sdDriver_hwrm_wait_must_abort(SLIFF_DRV_BNXT_DEV_INFO *pinfo, u32 req_type, u32 *fw_status)
{
	if (req_type == HWRM_VER_GET)
		return false;

	if (!pinfo->fw_health || !pinfo->fw_health->status_reliable)
		return false;

	*fw_status = sdDriver_read_fw_health_reg(pinfo);
	return *fw_status && !BNXT_FW_IS_HEALTHY(*fw_status);
}


static void sdDriverFreeBnxtDev(SLIFF_DRV_INFO *pmgdev)
{
    int domain,b, df;
    for (domain = 0; domain <MAX_SEG; domain++){
    for(b = 0; b < MAX_BUS; b++) {
        for(df = 0; df < MAX_DEVFN; df++) {
                sdDriverFreeBnxtDevice(pmgdev,domain, b, df);
                }
        }
    }

    return;
}
#endif

static void sdiExit (void)
{
    pr_debug("sliff - %s: <enter>\n", __FUNCTION__);

#ifdef DRIVER_SUPPORT_NIC
    sdDriverFreeBnxtDev(pmgdev);
    kfree(pmgdev);
#endif
    device_destroy(chrdev_class, DeviceNumber);
    cdev_del(DeviceControl);
    class_destroy(chrdev_class);
    unregister_chrdev_region (DriverMajorMinor, 1);

#ifdef DRIVER_SUPPORT_NIC
    bus_unregister_notifier(&pci_bus_type, &sliff_register_pcidevice_notifier);
#endif
    printk (KERN_INFO "sliff - version:%s unloaded\n", SLIFF_DRIVER_VERSION);
    pr_debug("sliff - %s: <exit>\n", __FUNCTION__);
}

int sdiMemoryMap (struct file *file, struct vm_area_struct *vma)
{
    //pr_debug("sliff - %s: <enter>\n", __FUNCTION__);

    //vma->flags |= VM_LOCKED;

    if (remap_pfn_range (vma, vma->vm_start, vma->vm_pgoff, vma->vm_end-vma->vm_start, pgprot_noncached(vma->vm_page_prot)))
    {
        return (-EAGAIN);
    }

    //pr_debug(KERN_INFO "sliff - %s: <exit>\n", __FUNCTION__);

    return (0);

}

int sdDriverFreeContiguousMemory (PTR_SLIFF_DRIVER_MEMORY PtrMemory)
{

    struct pci_dev *ptrDev = NULL;

    ptrDev = scrutiny_pci_get_domain_bus_and_slot (PtrMemory->Domain,
                                                   PtrMemory->Bus,
                                                   PCI_DEVFN (PtrMemory->Slot, PtrMemory->Function));


    if (PtrMemory == NULL)
    {
        printk (KERN_ERR "sliff - <exit> Invalid param. Memory param is null.\n");

        return (-1);

    }

    if (PtrMemory->MapSize < 1 || PtrMemory->MapSize % 4 != 0)
    {

        printk (KERN_ERR "sliff - <exit> Can't allocate invalid memory params.\n");
        printk (KERN_ERR "Memory size should not be zero and should be dword aligned '%08d'.\n", PtrMemory->MapSize);

        return (-1);

    }

    /*
     * Convert the physical address to the virtual address space
     */

    if(!PtrMemory->VirtualAddress)
    {
        PtrMemory->VirtualAddress = (long long unsigned int)phys_to_virt (PtrMemory->PhysicalAddress);
    }

    /*
     * Free the memory here.
     */

    /*Don't use kfree () to free DMA memory */
    dma_free_coherent(&ptrDev->dev, PtrMemory->MapSize,
            (void *)PtrMemory->VirtualAddress,
            PtrMemory->PhysicalAddress);

    return (0);
}




int sdDriverAllocateContiguousMemory (PTR_SLIFF_DRIVER_MEMORY PtrMemory)
{

    struct pci_dev              *ptrDev = NULL;
    void                        *ptrCPUMemory = NULL;
    dma_addr_t                  dma_handle;

    printk (KERN_INFO "SliffDriver - %s: <enter>\n", __FUNCTION__);

    ptrDev = scrutiny_pci_get_domain_bus_and_slot (PtrMemory->Domain,
                                                   PtrMemory->Bus,
                                                   PCI_DEVFN (PtrMemory->Slot, PtrMemory->Function));

    if (ptrDev == NULL)
    {
        printk (KERN_ERR "SliffDriver - Unable to get PCI device information \n");

        return (-1);
    }

    if (PtrMemory == NULL)
    {
        printk (KERN_ERR "SliffDriver - <exit> Invalid param. Memory param is null.\n");

        return (-1);

    }

    if (PtrMemory->MapSize < 1 || PtrMemory->MapSize % 4 != 0)
    {

        printk (KERN_ERR "SliffDriver - <exit> Can't allocate invalid memory params.\n");
        printk (KERN_ERR "Memory size should not be zero and should be dword aligned '%08d'.\n", PtrMemory->MapSize);

        return (-1);

    }

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,12,27)
    if (dma_set_mask_and_coherent(&ptrDev->dev, DMA_BIT_MASK(64)) != 0 &&
        dma_set_mask_and_coherent(&ptrDev->dev, DMA_BIT_MASK(32)) != 0)
    {
        printk (KERN_ERR "System does not support DMA, aborting\n");
        return (-EIO);
    }
#endif

    ptrCPUMemory = dma_alloc_coherent (&ptrDev->dev, PtrMemory->MapSize, &dma_handle, GFP_KERNEL);

    if (ptrCPUMemory)
    {

        memset (ptrCPUMemory, 0, PtrMemory->MapSize);

#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0)

#if defined(CONFIG_ARM64) || defined(CONFIG_ARM)
		//set_memory_rw ((unsigned long) ptrCPUMemory, (PtrMemory->MapSize / PAGE_SIZE));
#else
        set_memory_uc ((unsigned long) ptrCPUMemory, (PtrMemory->MapSize / PAGE_SIZE));
#endif
#endif

        PtrMemory->PhysicalAddress = dma_handle;
        PtrMemory->VirtualAddress  = (unsigned long long) ptrCPUMemory;

        printk (KERN_INFO "SliffDriver - Virtual Address 0x%llx && Physical Address 0x%llx, Size = %08x.\n", PtrMemory->VirtualAddress,
                                                                                                             PtrMemory->PhysicalAddress,
                                                                                                             PtrMemory->MapSize);
        return (0);
    }

    else
    {
        return (-1);
    }

}


void sdDriverProbeForEcam (void)
{
    U8              strID[9];
    U8             *ptrEntry;
    U8             *ptrAddress;
    U8             *ptrVaBiosRom;
    U8             *ptrVaRSDT;
    U8             *ptrVaTable;
    U8             *ptrAcpiAddrRSDP;
    U8             *ptrAcpiAddrRSDT;
    U16             numEntries;
    U32             value;
    BOOLEAN         bFound;

    ACPI_RSDT_v1_0  acpiRsdt;

    //printk (KERN_INFO "sliff - %s: <enter>\n", __FUNCTION__);

    // Do not probe again if previously did
    if (gAcpiAddrECAM[2] != ACPI_PCIE_NOT_PROBED)
    {
       return;
    }

    // Default to ACPI and/or ECAM not detected
    gAcpiAddrECAM[2] = ACPI_PCIE_ALWAYS_USE_OS;

    // Default to ECAM not found
    bFound = FALSE;

    // Initialize virtual addresses
    ptrVaBiosRom = NULL;
    ptrVaRSDT    = NULL;

    // Mark end of string
    strID[8] = '\0';

    // Map BIOS ROM into kernel space
    ptrVaBiosRom = ioremap (BIOS_MEM_START,
                            (BIOS_MEM_END - BIOS_MEM_START)
                            );

    if (ptrVaBiosRom == NULL)
    {
        goto _ExitScrutinyProbeForEcam;
    }

    // Set physical and virtual starting addresses
    ptrAcpiAddrRSDP = (U8*) BIOS_MEM_START;
    ptrAddress  = ptrVaBiosRom;

    // Scan system ROM for ACPI RSDP pointer
    do
    {
        // Read 8-bytes
        *(U32*)strID       = PHYS_MEM_READ_32 ((U32*)ptrAddress );
        *(U32*)(strID + 4) = PHYS_MEM_READ_32 ((U32*)(ptrAddress + 4) );

        // Check for header signature
        if (memcmp ("RSD PTR ",
                    strID,
                    8       // 8 bytes
                    ) == 0)
        {
            bFound = TRUE;
        }
        else
        {
            // Increment to next 16-byte boundary
            ptrAddress        += 16;
            ptrAcpiAddrRSDP += 16;
        }
    }
    while (!bFound && (ptrAcpiAddrRSDP < (U8*) BIOS_MEM_END));

    if (!bFound)
    {
        printk (KERN_ERR "sliff - ACPI not detected\n");

        goto _ExitScrutinyProbeForEcam;
    }

    // Reset flag
    bFound = FALSE;

    // Get ACPI revision
    value = PHYS_MEM_READ_8 ((U8*)(ptrAddress + 15));

    // Store RSDT address
    ptrAcpiAddrRSDT = (U8*) ((VOID*)(unsigned long)(PHYS_MEM_READ_32 ((U32*)(ptrAddress + 16))));

    // Map RSDT table

#if defined (CONFIG_ARM) || defined(CONFIG_ARM64)
    ptrVaRSDT = ioremap ((unsigned long)(ptrAcpiAddrRSDT), 1024);
#else
    ptrVaRSDT = ioremap_prot ((unsigned long)(ptrAcpiAddrRSDT), 1024, 0);
#endif    

    if (ptrVaRSDT == NULL)
    {
        goto _ExitScrutinyProbeForEcam;
    }

    // Get RSDT size
    acpiRsdt.Length = PHYS_MEM_READ_32( (U32*)(ptrVaRSDT + 4) );

    if (acpiRsdt.Length == 0)
    {
        printk(KERN_ERR "sliff - Unable to read RSDT table length \n");

        goto _ExitScrutinyProbeForEcam;
    }

    // Calculate number of entries
    numEntries = (U16)((acpiRsdt.Length - sizeof (ACPI_RSDT_v1_0)) / sizeof (U32));

    if (numEntries > 100)
    {
        printk (KERN_ERR "sliff - Unable to determine RSDT entry count\n");

        goto _ExitScrutinyProbeForEcam;
    }

    // Get address of first entry
    ptrEntry = ptrVaRSDT + sizeof (ACPI_RSDT_v1_0);

    // Parse entry pointers for MCFG table
    while (numEntries != 0)
    {
        // Get address of entry
        ptrAddress = (U8*)((VOID*)(unsigned long)(PHYS_MEM_READ_32 ((U32*)ptrEntry)));

        // Map table
#if defined (CONFIG_ARM) || defined(CONFIG_ARM64)
        ptrVaTable = ioremap ((unsigned long)(ptrAddress), 200);
#else
        ptrVaTable = ioremap_prot ((unsigned long)(ptrAddress), 200, 0);
#endif


        if (ptrVaTable == NULL)
        {
            goto _ExitScrutinyProbeForEcam;
        }

        // Get table signature
        value = PHYS_MEM_READ_32 ((U32*) ptrVaTable);

        printk (KERN_INFO "sliff - %c%c%c%c table at %08lX\n",
            (char)(value >>  0),
            (char)(value >>  8),
            (char)(value >> 16),
            (char)(value >> 24),
            (unsigned long)(ptrAddress));

        // Check if MCFG table
        if (memcmp( "MCFG", &value, sizeof (U32) ) == 0)
        {
            // Get 64-bit base address of Enhanced Config Access Mechanism
            gAcpiAddrECAM[0] = PHYS_MEM_READ_32 ((U32*)(ptrVaTable + 44));
            gAcpiAddrECAM[1] = PHYS_MEM_READ_32 ((U32*)(ptrVaTable + 48));

            bFound = TRUE;

            // Flag ok to use ECAM
            gAcpiAddrECAM[2] = ACPI_PCIE_BYPASS_OS_OK;
        }

        // Unmap table
        iounmap (ptrVaTable );

        // Get address of next entry
        ptrEntry += sizeof(U32);

        // Decrement count
        numEntries--;
    }

_ExitScrutinyProbeForEcam:

    // Release the BIOS ROM mapping
    if (ptrVaBiosRom != NULL)
    {
        iounmap (ptrVaBiosRom);
    }

    // Release RSDT mapping
    if (ptrVaRSDT != NULL)
    {
        iounmap (ptrVaRSDT);
    }

    if (bFound)
    {
        printk (KERN_INFO "sliff - PCIe ECAM at %02X_%08X\n",
                (unsigned int)gAcpiAddrECAM[1],
                (unsigned int)gAcpiAddrECAM[0]);

        // For newer Linux kernels, default to using OS
        if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18))
        {
            printk (KERN_INFO "sliff - Will default to OS for PCIe reg accesses\n");

            gAcpiAddrECAM[2] = ACPI_PCIE_DEFAULT_TO_OS;
        }
    }
    else
    {
        printk (KERN_INFO "sliff - MCFG entry not found (PCIe ECAM not supported)\n");
    }

    //printk (KERN_INFO "sliff - %s: <exit>\n", __FUNCTION__);
}

static int sdDriverDiscoverPciDevices (struct pci_dev **PtrDev, SCRUTINY_DRIVER_PCI_CFG_SPACE Karg)
{
    //printk (KERN_INFO "sliff - %s: <enter>\n", __FUNCTION__);

    *PtrDev = scrutiny_pci_get_domain_bus_and_slot ( Karg.Domain,
                                                    Karg.Bus,
                                                    PCI_DEVFN (Karg.Device, Karg.Function)
                                                  );


    if (*PtrDev != NULL)
    {
        //printk (KERN_INFO "sliff - <exit> PCI devices discovered.\n");

        return 0;
    }

    //unable to find the right device
    //printk (KERN_ERR "sliff - %s: <exit>\n", __FUNCTION__);

    return (-1);
}

static int sdDriverWriteRegister (U8 Domain, U8 Bus, U8 Device, U8 Function, U16 Offset, U32 Value)
{
    unsigned long address;
    VOID *ptrKernelValue;

    //printk (KERN_INFO "sliff - %s: <enter>\n", __FUNCTION__);

    if (Domain != 0)
    {
        printk (KERN_ERR "sliff - %s: <exit>, PCI Domain 0 only supported \n", __FUNCTION__);

        return -1;
    }

    if (Offset & 0x3)
    {
        printk (KERN_ERR "sliff - %s: <exit> Offset value set to 3 \n", __FUNCTION__);

        return -1;
    }

    if (gAcpiAddrECAM[2] == ACPI_PCIE_ALWAYS_USE_OS)
    {
        printk (KERN_ERR "sliff - %s: <exit> ACPI always set to use OS\n", __FUNCTION__);

        return -1;
    }


    address = ( (unsigned long) gAcpiAddrECAM[1] << 32) |
                (gAcpiAddrECAM[0]      <<  0) |
                (Bus                   << 20) |
                (Device                  << 15) |
                (Function              << 12) |
                (Offset                <<  0);


    ptrKernelValue = ioremap ((unsigned long) address, sizeof (unsigned long));

    if (ptrKernelValue == NULL)
    {
        printk (KERN_ERR "sliff - %s: <exit>\n", __FUNCTION__);

        return -1;
    }

    PHYS_MEM_WRITE_32 (ptrKernelValue, Value);

    // Release the mapping
    iounmap (ptrKernelValue);

    //printk (KERN_INFO "sliff - %s: <exit>\n", __FUNCTION__);

    return 0;

}

static int sdDriverWriteByLocation (struct pci_dev *PtrDev, U8 Domain, U8 Bus, U8 Device, U8 Function, U16 Offset, U32 Value)
{
	int rc;
	
    rc = pci_write_config_dword (PtrDev, Offset, Value );

    if (rc != 0)
    {
        return -1;
    }
	
	return 0;
}

static int sdDriverReadRegister (U8 Domain, U8 Bus, U8 Device, U8 Function, U16 Offset, U32 *PtrValue)
{
    unsigned long address;
    unsigned long value;
    VOID *ptrKernelValue;

    //printk (KERN_INFO "sliff - %s: <entry>\n", __FUNCTION__);

    *PtrValue = (U32) - 1;

    if (Domain != 0)
    {
        printk (KERN_ERR "sliff - %s: <exit>, PCI Domain 0 only supported \n", __FUNCTION__);

        return -1;
    }

    if (Offset & 0x3)
    {
        printk (KERN_ERR "sliff - %s: <exit>\n", __FUNCTION__);

        return -1;
    }

    if (gAcpiAddrECAM[2] == ACPI_PCIE_ALWAYS_USE_OS)
    {
        printk (KERN_ERR "sliff - %s: <exit>\n", __FUNCTION__);

        return -1;
    }


    address = ( (unsigned long) gAcpiAddrECAM[1] << 32) |
                (gAcpiAddrECAM[0]      <<  0) |
                (Bus                   << 20) |
                (Device                  << 15) |
                (Function              << 12) |
                (Offset                <<  0);

    ptrKernelValue = ioremap ((unsigned long) address, sizeof (unsigned long));

    if (ptrKernelValue == NULL)
    {
        printk (KERN_ERR "sliff - %s: <exit>\n", __FUNCTION__);

        return -1;
    }

    //TO_DO: we need to read U8, U16
    value = PHYS_MEM_READ_32 (ptrKernelValue);

    // Release the mapping
    iounmap (ptrKernelValue);

    *PtrValue = value;

    //printk (KERN_INFO "sliff - %s: <exit>\n", __FUNCTION__);

    return 0;
}

static int sdDriverReadByLocation (struct pci_dev *PtrDev, U8 Domain, U8 Bus, U8 Device, U8 Function, U16 Offset, U32 *PtrValue)
{
    int             rc;

    if (PtrValue == NULL)
    {
        return -1;
    }

	rc = pci_read_config_dword (PtrDev, Offset, (U32*) PtrValue);

	if (rc != 0)
	{
		return -1;
	}

	return 0;

}


static int sdiDriverWrite (struct pci_dev *PtrDev, SCRUTINY_DRIVER_PCI_CFG_SPACE Karg)
{
    if ((Karg.Offset >= 0x100) &&
        (gAcpiAddrECAM[2] != ACPI_PCIE_DEFAULT_TO_OS) &&
        (gAcpiAddrECAM[2] != ACPI_PCIE_ALWAYS_USE_OS))
    {
        return (sdDriverWriteRegister (Karg.Domain,
                                       Karg.Bus,
                                       Karg.Device,
                                       Karg.Function,
                                       Karg.Offset,
                                       Karg.Data[0]));

    }

    else
    {

        return (sdDriverWriteByLocation (PtrDev,
                                         Karg.Domain,
                                         Karg.Bus,
                                         Karg.Device,
                                         Karg.Function,
                                         Karg.Offset,
                                         Karg.Data[0]));
    }

}



static int sdiDriverRead (struct pci_dev *PtrDev, SCRUTINY_DRIVER_PCI_CFG_SPACE *Karg)
{

    if ((Karg->Offset >= 0x100) &&
        (gAcpiAddrECAM[2] != ACPI_PCIE_DEFAULT_TO_OS) &&
        (gAcpiAddrECAM[2] != ACPI_PCIE_ALWAYS_USE_OS))
    {
        return (sdDriverReadRegister (Karg->Domain,
                                      Karg->Bus,
                                      Karg->Device,
                                      Karg->Function,
                                      Karg->Offset,
                                      &Karg->Data[0]));

    }

    else
    {

        return (sdDriverReadByLocation (PtrDev,
                                        Karg->Domain,
                                        Karg->Bus,
                                        Karg->Device,
                                        Karg->Function,
                                        Karg->Offset,
                                        &Karg->Data[0]));
    }

}

#ifdef DRIVER_SUPPORT_NIC

static SLIFF_DRV_BNXT_DEV_INFO *sdDriver_find_bnxt_dev(int domain, unsigned int bus,
                      unsigned int devfn)
{
    SLIFF_DRV_BNXT_DEV_INFO *pinfo = NULL;
    struct pci_dev *PtrDev = NULL;
    struct mutex *pinfo_lock;

    /*
     * #DCSG01331865 - if device is removed pmgdev->pinfo[] may be out of sync
     * check for bus & devfn alos to make sure correct bnxt_dev is used.
     */
    mutex_lock(&pmgdev->lock);
    pinfo_lock = &pmgdev->info_lock[bus][devfn];
    pinfo      = pmgdev->pinfo[bus][devfn];
    mutex_unlock(&pmgdev->lock);

    mutex_lock(pinfo_lock);
    if (pinfo) {
        /* If device is removed, free up and return NULL */
        if (pinfo->deleted)
        {
            mutex_unlock(pinfo_lock);
            sdDriverFreeBnxtDevice(pmgdev, domain, bus, devfn);
            return NULL;
        }

        if (domain == pinfo->Domain  &&
            bus == pinfo->Bus &&
            devfn == pinfo->DevFn) {
            pr_debug("[S:B:D:F = %04x:%02x:%02x:%x] Matched.\n",
                                 domain, bus, PCI_SLOT (devfn), PCI_FUNC (devfn));
            PtrDev = pinfo->PtrDev = pci_get_domain_bus_and_slot(domain, bus, devfn);
        }
        else
        {
            pr_debug("[S:B:D:F = %04x:%02x:%02x:%x] Collision, Assign new value.\n",
                                 domain, bus, PCI_SLOT (devfn), PCI_FUNC (devfn));
            pinfo = NULL;
	    /* unlock mutex as sdDriverFreeBnxtDevice takes it */
	    mutex_unlock(pinfo_lock);
            sdDriverFreeBnxtDevice(pmgdev,domain, bus, devfn);
	    return pinfo;
        }
    }

    mutex_unlock(pinfo_lock);
    return pinfo;
}

static SLIFF_DRV_BNXT_DEV_INFO *sdDriver_init_bnxt_dev(int domain, unsigned int bus,
                      unsigned int devfn)
{
    int rc = -ENODEV;
    SLIFF_DRV_BNXT_DEV_INFO *pinfo;
    unsigned int idx;
    struct pci_dev *PtrDev;
    U8 dev, func;

    pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL);
    if (!pinfo)
        return ERR_PTR(-ENOMEM);

    pinfo->PtrDev = pci_get_domain_bus_and_slot(domain, bus, devfn);
    if (!pinfo->PtrDev) {
        pr_err("[S:B:D:F = %04x:%02x:%02x:%x]Failed to get PtrDev\n",
               domain, bus, PCI_SLOT (devfn), PCI_FUNC (devfn));
        goto err;
    }

    PtrDev        = pinfo->PtrDev;
    dev           = PCI_SLOT (PtrDev->devfn);
    func          = PCI_FUNC (PtrDev->devfn);
    pinfo->Domain = domain;
    pinfo->Bus    = bus;
    pinfo->DevFn  = devfn;

    if ((!pci_is_enabled(PtrDev)) && (is_owner_drv_loaded(PtrDev)))
    {
        rc = -EIO;
        goto err;
    }

#if UNTOUCHED_PCI_REF_CNT
    if (!pci_is_enabled(PtrDev))
#endif
    {
        /* Don't enable when bnxt_en purposefully & forcefully diabled 
         * PCI device, Eg. for error recovery */
            if(pci_enable_device(PtrDev)){
                goto err;
            }
            else{
                pinfo->enabled_pci_dev++;
            }
    }

    if (!(pci_resource_flags(PtrDev, 0) & IORESOURCE_MEM)) {
        dev_err(&PtrDev->dev,
            "Cannot find PCI device base address, aborting\n");
        rc = -ENODEV;
        goto err_out_disable;
    }

#if defined(CONFIG_PCI_IOV)
    if (!PtrDev->is_virtfn)
#endif
        if (!(pci_resource_flags(PtrDev, 2) & IORESOURCE_MEM))
        {
            dev_err(&PtrDev->dev, "Cannot find 2nd PCI device base address, aborting\n");
            rc = -ENODEV;
            /* goto err_out_disable; */
        }


#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,12,27)
    if (dma_set_mask_and_coherent(&PtrDev->dev, DMA_BIT_MASK(64)) != 0 &&
        dma_set_mask_and_coherent(&PtrDev->dev, DMA_BIT_MASK(32)) != 0) {
        dev_err(&PtrDev->dev, "System does not support DMA, aborting\n");
        rc = -EIO;
        goto err_out;
    }
#endif


#ifdef CONFIG_PCI_IOV
    if (!PtrDev->is_virtfn)
#endif
        if (pci_find_capability(PtrDev, PCI_CAP_ID_EXP) == 0) {
            dev_err(&PtrDev->dev, "Cannot find PCI Express capability, aborting...\n");
            rc = -EIO;
            goto err_out;
        }

    pci_set_master (PtrDev);

    if (PtrDev->vendor != PCI_VENDOR_ID_BROADCOM) {
        pr_err( "[S:B:D:F = %04x:%02x:%02x:%x] Not a Broadcom PCI device\n", domain, bus, dev, func);
        goto err_out;
    }

    for (idx = 0; (bnxt_pci_tbl[idx].device != 0) && (idx < sizeof(bnxt_pci_tbl)); idx++) {
        if (PtrDev->device == bnxt_pci_tbl[idx].device) {
            pr_debug("[S:B:D:F = %04x:%02x:%02x:%x] Sliff:Found valid/known NIC PCI device\n",
                     domain, bus, dev, func);
            break;
        }
    }

    if ((bnxt_pci_tbl[idx].device == 0) || (idx >= sizeof(bnxt_pci_tbl))) {
        pr_err( "[S:B:D:F = %04x:%02x:%02x:%x] Could not find valid/supported NIC PCI device\n", domain, bus, dev, func);
        goto err_out;
    }

    /* TODO: Allocate 1 PAGE_SIZE for future requirements */
    /* Attache the response dma buffer */
    pinfo->resp_virt_addr = dma_alloc_coherent(&PtrDev->dev,
                        HWRM_MAX_RESP_LEN,
                        &pinfo->resp_dma_addr,
                        GFP_KERNEL | __GFP_ZERO);
    if ((!pinfo->resp_virt_addr) || (!pinfo->resp_dma_addr)) {
        pr_err("Failed to alloc mem for data\n");
        rc = -EFAULT;
        goto err_out;
    }

    /* TBD: Allocate 1 PAGE_SIZE for future requirements */
    pinfo->short_cmd_req_virt_addr = dma_alloc_coherent(&PtrDev->dev,
                        HWRM_MAX_REQ_LEN,
                        &pinfo->short_cmd_req_dma_addr,
                        GFP_KERNEL | __GFP_ZERO);
    if ((!pinfo->short_cmd_req_virt_addr) || (!pinfo->short_cmd_req_dma_addr)) {
        pr_err("Failed to alloc mem for data\n");
        rc = -EFAULT;
        goto err_out;
    }

    pinfo->bar0 = pci_ioremap_bar(PtrDev, 0);
    /* BAR1 mapping is not always supported. Eg.when FW is not alive */
    /* pinfo->bar1 = pci_ioremap_bar(PtrDev, 4); */
    pinfo->bar1 = NULL;

    if (!pinfo->bar0) {
        pr_err( "[S:B:D:F = %04x:%02x:%02x:%x] Can't map device registers, aborting ...\n", domain, bus, dev, func);
        rc = -EFAULT;
        goto err_out;
    }

    pr_debug( "[S:B:D:F = %04x:%02x:%02x:%x] trying to map fw_health_reg ...\n", domain, bus, dev, func);
    sdDriver_try_map_fw_health_reg(pinfo);

    return pinfo;

    /* Don't reorder the following labels */
err_out:

err_out_disable:

#if UNTOUCHED_PCI_REF_CNT
    if (!pci_is_enabled(PtrDev))
#endif
    {
       pci_disable_device(PtrDev);
       pinfo->enabled_pci_dev++;

    }

err:
    kfree(pinfo);

    return ERR_PTR(rc);
}

#if 0
static u32 sdDriver_bnxt_mg_get_hash_key(u32 bus, u32 devfn)
{
    return ((bus * PRIME_1 + devfn) * PRIME_2) % MAX_PDEV;
}
#endif

static void sdDriver_get_tgt_info(int target_id, unsigned int bus,
                unsigned int devfn, struct cmd_tgt_info *tgt_info)
{
    mutex_lock(&pmgdev->lock);
    switch (target_id) {
    case HWRM_TARGET_ID_BONO:
        tgt_info->bar_offset = BNXT_GRCPF_REG_BONO_COMM;
        tgt_info->seq_id = pmgdev->tgtid_bono;
        pmgdev->tgtid_bono += 1;
        break;
    case HWRM_TARGET_ID_KONG:
        tgt_info->bar_offset = BNXT_GRCPF_REG_KONG_COMM;
        tgt_info->seq_id = pmgdev->tgtid_kong;
        pmgdev->tgtid_kong += 1;
        break;
    default:
        /* default target is assumed chimp */
        tgt_info->bar_offset = BNXT_GRCPF_REG_CHIMP_COMM;
        tgt_info->seq_id = pmgdev->tgtid_chimp[bus][devfn];
        pmgdev->tgtid_chimp[bus][devfn]++;
        break;
    };
    mutex_unlock(&pmgdev->lock);

    tgt_info->db_offset = tgt_info->bar_offset + 0x100;

    return;
}

static int sdDriver_bnxt_send_fw_msg(SLIFF_DRV_BNXT_DEV_INFO *pinfo,
                void *req, struct mg_fw_msg *pmsg)
{
    u64  timeout_us, i, timeo_us;

    struct hwrm_resp_hdr *presp_hdr;
    struct hwrm_cmd_hdr *pcmd_hdr;
    struct hwrm_short_cmd short_cmd_hdr;
    struct cmd_tgt_info tgt_info;
    __le16 seq_id = 0;
    int rc = 0, target_id;
    u32 *data   = req, len = 0, sts;
    u16 msg_len = pmsg->len_req;
    u8 *valid   = NULL;
    U8  domain = 0, bus, dev, func;
    struct pci_dev *PtrDev = pinfo->PtrDev;

    pr_debug("msg_len:%u\n", pmsg->len_req);

    if (!pinfo || !req || !pmsg)
    {
       pr_err("NULL pointer argument: pinfo\n");
       return -ENOMEM;
    }

    if (pmsg->len_req > HWRM_MAX_REQ_LEN)
    {
       printk (KERN_DEBUG "HWRM request length exceeds the max value...using short command\n");
       pmsg->is_short_cmd = 1;
    }
#ifdef CONFIG_PCI_DOMAINS_GENERIC
    domain = PtrDev->bus->domain_nr;
#endif
    bus    = PtrDev->bus->number;
    dev    = PCI_SLOT (PtrDev->devfn);
    func   = PCI_FUNC (PtrDev->devfn);


    pcmd_hdr = (struct hwrm_cmd_hdr *)(req);
    /* attach response buffer */
    pcmd_hdr->resp_addr = cpu_to_le64(pinfo->resp_dma_addr);
    pr_debug( "[S:B:D:F = %04x:%02x:%02x:%x] sending HWRM cmd = 0x%x...\n", domain, bus, dev, func, pcmd_hdr->req_type);

    target_id = pcmd_hdr->target_id;

    /*
     * Handling only Chimp
     * TBD: Need to understand how to forward on other Processors.
     */
    #if 0  /* Rakesh - For MTU command needed to disable this*/
    if(target_id > 0) {
        pr_err("HWRM invalid target_id:%d for cmd:0x%x\n",
                target_id, pcmd_hdr->req_type);
        return -EPROTONOSUPPORT;
    }
    #endif

    sdDriver_get_tgt_info(target_id, PtrDev->bus->number, PtrDev->devfn, &tgt_info);
    seq_id = cpu_to_le16(tgt_info.seq_id);
    pcmd_hdr->seq_id = seq_id ;

    if (pmsg->is_short_cmd) {
        /*
         * In short command Application have data to f/w hence
         * copying the same.
         */

        pr_debug("HWRM short cmd\n");
        memset(pinfo->short_cmd_req_virt_addr, 0, HWRM_MAX_REQ_LEN);
        memcpy(pinfo->short_cmd_req_virt_addr, req, pmsg->len_req);
        short_cmd_hdr.req_type  = cpu_to_le16(pcmd_hdr->req_type);
        short_cmd_hdr.signature = cpu_to_le16(SHORT_REQ_SIGNATURE_SHORT_CMD);
        short_cmd_hdr.size      = cpu_to_le16(pmsg->len_req);
        short_cmd_hdr.req_addr  = cpu_to_le64(pinfo->short_cmd_req_dma_addr);
        data = (u32 *)&short_cmd_hdr;
        msg_len = sizeof(short_cmd_hdr);
        /*
         * TBD: How to handle short command
         * Need to check how resp header need to be handled.
         * copy_from_user(pmgdev->hwrm_resp,
                 (void *)short_cmd_hdr.req_addr,
                short_cmd_hdr.size);
         */
    } else {
        /*
         * Handling only Chimp
         * TBD: Need to understand how to forward on other Processors.
         */
        pr_debug("HWRM normal cmd\n");
        target_id = -1;
        pr_debug( "req_type:%x\n", pcmd_hdr->req_type);
        pr_debug( "cmpl_ring:%x\n", pcmd_hdr->cmpl_ring);
        pr_debug( "seq_id:%u\n", pcmd_hdr->seq_id);
        pr_debug( "target_id: %x\n", pcmd_hdr->target_id);
        pr_debug( "resp_addr:%llx\n", pcmd_hdr->resp_addr);
    }

    /*Align to 4B boundary */
    msg_len = ((msg_len + 3) / 4) * 4;

    /* copy data to communication channel*/
    __iowrite32_copy(pinfo->bar0 + tgt_info.bar_offset,
            data, msg_len / 4);

    for (i = 0; i < msg_len / 4; i ++)
        pr_debug("data[%lld]=0x%X  ", i, data[i]);

    pr_debug("\n");

    for (i = msg_len; i < HWRM_MAX_REQ_LEN; i += 4)
        writel(0, pinfo->bar0 + tgt_info.bar_offset + i);

    /* Ensure all buffer gets flushed before doorbell ring */
    wmb();

    /* Ring channel doorbell */
    writel(1, pinfo->bar0 + tgt_info.db_offset);

    /* Wait for timeout or rsp getting updated */
    presp_hdr = (struct hwrm_resp_hdr *)pinfo->resp_virt_addr;

    /* set default time out based on HWRM command type */
    if (!pmsg->timeout)
    {
        switch(pcmd_hdr->req_type)
        {
            case HWRM_FUNC_RESET:
            case HWRM_FW_RESET:
                pmsg->timeout = HWRM_RESET_TIMEOUT;
                break;
            case HWRM_PORT_DSC_DUMP:
                pmsg->timeout = HWRM_PORT_DSC_DUMP_TIMEOUT;
                break;
            case HWRM_NVM_DEFRAG:
            case HWRM_NVM_INSTALL_UPDATE:
                pmsg->timeout = HWRM_NVM_DEFRAG_TIMEOUT;
                break;
            case HWRM_SELFTEST_RETRIEVE_SERDES_DATA:
                pmsg->timeout = HWRM_SELFTEST_RETRIEVE_SERDES_DATA_TIMEOUT;
                break;
            case HWRM_NVM_WRITE:
                pmsg->timeout = HWRM_NVM_DEFRAG_TIMEOUT * 200;
                break;
            default:
                pmsg->timeout = DFLT_HWRM_CMD_TIMEOUT;
                break;
        }
    }

    timeout_us = pmsg->timeout * 1000;
    if (pcmd_hdr->req_type == HWRM_NVM_WRITE)
    {
        timeout_us = pmsg->timeout * 1000 * 200;
    }
    wmb();

    for (i = 0, timeo_us = 0; timeo_us < timeout_us; i++) {
        rmb();
        len = le16_to_cpu(presp_hdr->resp_len);
        valid = ((u8 *)presp_hdr) + len - 1;
        if (len && (presp_hdr->seq_id == seq_id) && (*valid == 1)) {
            /* copy response back to user */
#if HWRM_DBG_DUMP
            print_hex_dump(KERN_INFO, "Res:",
                    DUMP_PREFIX_ADDRESS, 16, 1,
                    presp_hdr, len, 1);
#endif

            if (len > pmsg->len_resp) {
                pr_err( "[S:B:D:F = %04x:%02x:%02x:%x]f/w response[%u] \
                        bytes are more than usr expected \
                        bytes[%u] for HWRM cmd:0x%x valid:%d\n",
                        domain, bus, dev, func, len,
                        pmsg->len_resp, pcmd_hdr->req_type, *valid);
                rc = -EFAULT;
                goto err;
            }

            /* Copy respose to user */
            if (copy_to_user((void __user *)pmsg->usr_resp,
                        presp_hdr, len)) {
                pr_err( "Failed to copy \
                        response to user\n");
                rc = -EFAULT;
                goto err;
            }

            /* Last byte of resp contains valid bit */
            valid = ((u8 *)presp_hdr) + len - 1;
            break;
        }

        /* on first few passes, just barely sleep */
        if (i < HWRM_SHORT_TIMEOUT_COUNTER) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
            usleep_range(HWRM_SHORT_MIN_TIMEOUT, HWRM_SHORT_MAX_TIMEOUT);
#else
            sleep_jiffies(1);
#endif
            timeo_us += HWRM_SHORT_MIN_TIMEOUT;
        } else {
            /* wait atleast HWRM_RECOVERY_TIMEOUT to allow recovering FW */
            if(sdDriver_hwrm_wait_must_abort(pinfo, pcmd_hdr->req_type, &sts)) {
                /* If we wait for HWRM_RECOVERY_TIMEOUT only 1st time during FW unhealthy period,
                 * then repaving path is broken. Maximum cumulative wait time across multiple HWRM
                 * commands during FW unhealthy perid is HWRM_MAX_UNHEALTHY_TIMEOUT*/
                if(timeo_us > (HWRM_RECOVERY_TIMEOUT * 1000) || (pinfo->fw_unhealthy_time > HWRM_MAX_UNHEALTHY_TIMEOUT)) {
                    pinfo->fw_unhealthy_time += HWRM_RECOVERY_TIMEOUT;
                    pr_err("[S:B:D:F = %04x:%02x:%02x:%x]Abandoning HWRM cmd:0x%x w/o waiting\
                            for resp cmpletion due to firmware status: 0x%x\n",
                            domain, bus, dev, func,
                            pcmd_hdr->req_type, sts);
                    goto err;
                }
            }

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
            usleep_range(HWRM_MIN_TIMEOUT, HWRM_MAX_TIMEOUT);
#else
            sleep_jiffies(1);
#endif
            timeo_us += HWRM_MIN_TIMEOUT;
        }
    }

    if (timeo_us >= timeout_us) {
        pr_err("[S:B:D:F = %04x:%02x:%02x:%x]SliffDrv: HWRM Cmd:0x%x timed out [seq:%u, timeout:%llu ms, len:%d]\n",
                domain, bus, dev, func, pcmd_hdr->req_type,
                pcmd_hdr->seq_id, timeo_us / 1000, pmsg->len_req);
        rc = -ETIMEDOUT;
        goto err;
    } else {
        pinfo->fw_unhealthy_time = 0;
    }

    len = le16_to_cpu(presp_hdr->resp_len);

    if(valid) {
        for (i = 0; i < HWRM_VALID_BIT_DELAY_USEC; i++) {
            /* make sure we read from updated DMA memory */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,19,0)
            dma_rmb();
#else
            rmb();
#endif
            if (*valid)
                break;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
            usleep_range(1, 5);
#else
            sleep_jiffies(1);
#endif
        }

        if (i >= HWRM_VALID_BIT_DELAY_USEC) {
            pr_err("[S:B:D:F = %04x:%02x:%02x:%x]SliffError DMA valid timeout \
                    {cmd:0x%x seq:0x%x} len:%d v:%d\n",
                    domain, bus, dev, func,
                    le16_to_cpu(pcmd_hdr->req_type),
                    le16_to_cpu(pcmd_hdr->seq_id), len,
                    *valid);
            rc = -ETIME;
            goto err;
        }

        /* Zero valid bit for compatibility.  Valid bit in an older spec
         * may become a new field in a newer spec.  We must make sure that
         * a new field not implemented by old spec will read zero.
         */
        *valid = 0;
    }

err:
    pr_debug( "%s():[S:B:D:F = %04x:%02x:%02x:%x]  HWRM cmd = 0x%x, rc=%d...\n", __func__, domain, bus, dev, func, pcmd_hdr->req_type, rc);
    return rc;
}

static int32_t sdDriver_bnxt_mg_prepare_dma_operations(SLIFF_DRV_BNXT_DEV_INFO *pinfo,
                           void *preq, struct mg_fw_msg *pmsg)
{
    int32_t rc = 0;
    uint8_t i, num_allocated = 0;
    void *dma_ptr;
    struct hwrm_cmd_hdr *pcmd_hdr;

    if((!preq) || (!pmsg) || (!pinfo))
    {
        return -EINVAL;
    }

    pcmd_hdr = (struct hwrm_cmd_hdr *)(preq);

    for (i = 0; i < pmsg->num_dma_indications; i++) {
        dma_addr_t dma_addr_aligned;

        if (pmsg->dma[i].length == 0 ||
            (pmsg->dma[i].length > MAX_DMA_MEM_SIZE && pcmd_hdr->req_type != HWRM_NVM_WRITE)) {
            pr_err("HWRM Cmd:%0x, Invalid DMA memory length:%d, num_dma_indications=%d\n", pcmd_hdr->req_type,
                        pmsg->dma[i].length, pmsg->num_dma_indications);

            if(pmsg->dma[i].length == 0)
                rc = 0;
            else
                rc = -EINVAL;

            goto err;
        }

        /* Over allocate to  align on 16B boundary */
        pinfo->dma_virt_addr[i] = dma_alloc_coherent(
                          &pinfo->PtrDev->dev,
                          pmsg->dma[i].length + 16,
                          &pinfo->dma_addr[i],
                          GFP_KERNEL);
        if (!pinfo->dma_virt_addr[i]) {
            pr_err( "Failed to allocate memory \
                    for data_addr[%d]\n", i);
            rc = -ENOMEM;
            goto err;
        }
        num_allocated++;

        /* Align on 16B boundary */
        dma_addr_aligned = ALIGN(pinfo->dma_addr[i], 16);
        pinfo->align[i]  = dma_addr_aligned - pinfo->dma_addr[i];

        if (!(pmsg->dma[i].read_or_write)) {
            if (copy_from_user((u8*)pinfo->dma_virt_addr[i] + pinfo->align[i],
                       (void __user *)pmsg->dma[i].data,
                       pmsg->dma[i].length)) {
                pr_err( "Failed to copy data from \
                    user for data_addr[%d]\n", i);
                rc = -EFAULT;
                goto err;
            }
        }


        dma_ptr = preq + pmsg->dma[i].offset;

        if ((PTR_ALIGN(dma_ptr, 8) == dma_ptr) &&
            (pmsg->dma[i].offset < pmsg->len_req)) {
            __le64 *dmap = dma_ptr;
            *dmap = cpu_to_le64(dma_addr_aligned);
        } else {
            pr_err( "Wrong input parameter\n");
            rc = -EINVAL;
            goto err;
        }
    }

    return rc;

err:
	for (i = 0; i < num_allocated; i++) {
		if(pinfo->dma_virt_addr[i] && pinfo->dma_addr[i]) {
			dma_free_coherent(&pinfo->PtrDev->dev,
					  pmsg->dma[i].length + 16,
					  pinfo->dma_virt_addr[i],
					  pinfo->dma_addr[i]);
			pinfo->dma_addr[i]      = 0;
			pinfo->dma_virt_addr[i] = NULL;
		}
	}

	return rc;
}

static int sdDriver_bnxt_mg_process_hwrm(SLIFF_DRV_BNXT_DEV_INFO *pinfo,
                struct bnxt_mg_req *mg_req)
{
    struct pci_dev *PtrDev = pinfo->PtrDev;
    struct mg_fw_msg msg;
    u16    msg_len, dma_len;
    int rc = 0, i;
    void *preq = NULL;
    struct mg_dma_info *dma_ptr = NULL;
    U8     domain = 0, bus, dev, func;

#ifdef CONFIG_PCI_DOMAINS_GENERIC
    domain = PtrDev->bus->domain_nr;
#endif
    bus    = PtrDev->bus->number;
    dev    = PCI_SLOT (PtrDev->devfn);
    func   = PCI_FUNC (PtrDev->devfn);

    if (copy_from_user(&msg,
               (void __user *)mg_req->req.hreq,
               sizeof(msg))) {
        pr_err( "Failed to copy data from user\n");
        return -EFAULT;
    }

    if (msg.len_resp > HWRM_MAX_RESP_LEN) {
        pr_err( "response length exceeds max. value\n");
        return -EINVAL;
    }

    /*Align to 4B boundary */
    msg_len = ((msg.len_req + 3) / 4) * 4;
    preq = kzalloc(msg_len, GFP_KERNEL);
    if (!preq)
        return -ENOMEM;

    if (copy_from_user(preq,
               (void __user *)msg.usr_req,
               msg.len_req)) {
        pr_err( "Failed to copy req buffer from user\n");
        rc = -EFAULT;
        goto out0;
    }


    if (msg.num_dma_indications) {
        if (msg.num_dma_indications > MAX_NUM_DMA_INDICATIONS) {
            pr_err( "Invalid DMA indications\n");
            rc = -EINVAL;
            goto out0;
        }

        dma_len = msg.num_dma_indications * sizeof(struct mg_dma_info);
        dma_ptr = kzalloc(dma_len, GFP_KERNEL);
        if (!dma_ptr) {
            pr_err( "Failed to allocate dma info ptr\n");
            rc = -ENOMEM;
            goto out0;
        }

        if (copy_from_user((void *)dma_ptr, (void __user *)(msg.dma), dma_len)) {
            pr_err( "Failed to copy data from user\n");
            rc = -EFAULT;
            goto out1;
        }

        msg.dma = dma_ptr;

        rc = sdDriver_bnxt_mg_prepare_dma_operations(pinfo, preq, &msg);
        if (rc) {
            pr_err( "Failed to perform DMA operaion\n");
            goto out1;
        }
    }

    rc = sdDriver_bnxt_send_fw_msg(pinfo, preq, &msg);
    if (rc) {
        pr_err( "[S:B:D:F = %04x:%02x:%02x:%x]Sliffdrv:Failed to send msg\n", domain, bus, dev, func);
        goto out2;
    }
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,19,0)
    dma_rmb();
#else
            rmb();
#endif

    for (i = 0; i < msg.num_dma_indications; i++) {
        if (dma_ptr[i].read_or_write) {
            if (copy_to_user((void __user *)dma_ptr[i].data,
                     pinfo->dma_virt_addr[i] + pinfo->align[i],
                     dma_ptr[i].length)) {
                pr_err( "Failed to copy dma \
                        data to user\n");
                rc = -EFAULT;
                goto out2;
            }
        }
    }

out2:
    for (i = 0; i < msg.num_dma_indications; i++) {
        if(pinfo->dma_virt_addr[i] && pinfo->dma_addr[i]) {
            dma_free_coherent(&PtrDev->dev, msg.dma[i].length + 16,
                                    pinfo->dma_virt_addr[i],
                                    pinfo->dma_addr[i]);

            pinfo->dma_addr[i]      = 0;
            pinfo->dma_virt_addr[i] = NULL;
        }
    }
out1:
    if (msg.num_dma_indications && dma_ptr) {
        kfree(dma_ptr);
    }
out0:
    if(preq) {
        kfree(preq);
    }

    return rc;
}

#endif

static long sdiDriverIoctl (struct file *file, unsigned int cmd, unsigned long arg)
{
    long ret_val = 0;

    /*printk (KERN_INFO "sliff - %s: <enter U32=%d, struct=%d>\n", __FUNCTION__, sizeof (U32), sizeof (SCRUTINY_DRIVER_PCI_CFG_SPACE)); */

    switch (cmd)
    {
#ifdef DRIVER_SUPPORT_NIC
        case SCRUTINY_DRIVER_IOCTL_FLUSH_NIC_DATA:
        {
            /* This ioctl is no longer needed as hotplug/unplug events dynamically take care of
             * clearing device private data*/

            sdDriverFreeBnxtDev(pmgdev);
            return 0;
        }
#endif

        case SCRUTINY_DRIVER_IOCTL_DISCOVER_PCI:
        {
            struct pci_dev *ptrDev = NULL;
            SCRUTINY_DRIVER_PCI_CFG_SPACE karg;
            int ret = -1;

            if (copy_from_user (&karg, (void __user *) arg, sizeof (SCRUTINY_DRIVER_PCI_CFG_SPACE)))
            {

                printk (KERN_ERR "sliff - failure at %s:%d/%s()!\n", __FILE__, __LINE__, __FUNCTION__);

                return (-EFAULT);

            }

            ret = sdDriverDiscoverPciDevices (&ptrDev, karg);

            if (ret != 0)
            {
                return (-ENOENT);
            }

            karg.Bus      = ptrDev->bus->number;
            karg.Device   = PCI_SLOT (ptrDev->devfn);
            karg.Function = PCI_FUNC (ptrDev->devfn);

            if (copy_to_user ((void __user *)arg, &karg, sizeof (SCRUTINY_DRIVER_PCI_CFG_SPACE)))
            {
                printk (KERN_ERR "sliff - failure at %s:%d/%s()!\n", __FILE__, __LINE__, __FUNCTION__);

                return (-EFAULT);
            }

            //printk (KERN_INFO "sliff - %s: <exit>\n", __FUNCTION__);

            return (0);

        }

        case SCRUTINY_DRIVER_IOCTL_ENABLE_PCI:
        {
            struct pci_dev *ptrDev = NULL;
            SCRUTINY_DRIVER_PCI_CFG_SPACE karg;
            int ret = -1;
#ifdef DRIVER_SUPPORT_NIC
            SLIFF_DRV_BNXT_DEV_INFO *pinfo;
            struct mutex            *pinfo_lock;
            U16                      domain = 0;
            int                      is_pci_dev_enabled;
#endif

            if (copy_from_user (&karg, (char *) arg, sizeof (SCRUTINY_DRIVER_PCI_CFG_SPACE)))
            {

                printk (KERN_ERR "sliff - failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__);

                return (-EFAULT);

            }

            ret = sdDriverDiscoverPciDevices (&ptrDev, karg);

            if (ret == -1)
            {
                printk (KERN_ERR "sliff - Unable to discover any PCI device \n");

                return (-ENOENT);
            }

            if (ptrDev == NULL)
            {
                printk (KERN_ERR "sliff - Unable to get PCI device information \n");

                return (-ENODEV);
            }

            if (ptrDev->bus->number != karg.Bus && PCI_SLOT (ptrDev->devfn) != karg.Device && PCI_FUNC (ptrDev->devfn) != karg.Function)
            {
                //user provided bus/dev/fn number did not match so throwing error

                printk (KERN_ERR "sliff - Unable to match bus/dev/fn \n");

                return (-EINVAL);

            }


#ifdef DRIVER_SUPPORT_NIC
#ifdef CONFIG_PCI_DOMAINS_GENERIC
            domain = ptrDev->bus->domain_nr;
#endif
            pinfo = sdDriver_find_bnxt_dev(domain, ptrDev->bus->number, ptrDev->devfn);
            if (!pinfo) {
                /* get PtrDev info */
                pinfo = sdDriver_init_bnxt_dev(domain, ptrDev->bus->number, ptrDev->devfn);
                if (IS_ERR(pinfo)) {
                    pr_err("Failed to get PtrDev\n");
                    return  PTR_ERR(pinfo);
                }

                pinfo->Domain = domain;
                mutex_lock(&pmgdev->lock);
                pmgdev->pinfo[ptrDev->bus->number][ptrDev->devfn] = pinfo;
                mutex_unlock(&pmgdev->lock);
            }

            mutex_lock(&pmgdev->lock);
            pinfo_lock = &pmgdev->info_lock[ptrDev->bus->number][ptrDev->devfn];
            mutex_unlock(&pmgdev->lock);

            mutex_lock(pinfo_lock);

            /*pci_set_power_state (ptrDev, PCI_D0); */

            /* Please don't reorder the following code */
            is_pci_dev_enabled = pci_is_enabled(ptrDev);
#if UNTOUCHED_PCI_REF_CNT
            if (!is_pci_dev_enabled)
#endif
            {
                /* Don't enable when bnxt_en purposefully & forcefully diabled 
                 * PCI device, Eg. for error recovery */
                    if(pci_enable_device(ptrDev)){
                        dev_err(&ptrDev->dev, "Cannot enable PCI device, aborting\n");
                        goto err_out;
                    }
                    else{
                        pinfo->enabled_pci_dev++;
                    }
            }


#ifdef FACTORY_MODE_SUPPORT
            if((!pinfo->resource_region_acquired) && (!is_pci_dev_enabled) && (!is_owner_drv_loaded(ptrDev))) {
                ret = pci_request_regions(ptrDev, DRV_MODULE_NAME);
                if (ret) {
                    dev_err(&ptrDev->dev, "Unable to obtain PCI resources...\n");
                    /* goto err_out_disable; */
                }
                else {
                    pinfo->resource_region_acquired = 1;
                }
            }
#endif

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,12,27)
            if (dma_set_mask_and_coherent(&ptrDev->dev, DMA_BIT_MASK(64)) != 0 &&
                dma_set_mask_and_coherent(&ptrDev->dev, DMA_BIT_MASK(32)) != 0) {
                dev_err(&ptrDev->dev, "System does not support DMA, aborting\n");
                ret = -EIO;
                goto err_out_release;
            }
#endif
            pci_set_master (ptrDev);

            mutex_unlock(pinfo_lock);
            return (0);

/* Don't reorder the following labels */
err_out_release:
#ifdef FACTORY_MODE_SUPPORT
        if(pinfo->resource_region_acquired)
        {
            pci_release_regions(ptrDev);
            pinfo->resource_region_acquired = 0;
        }
#endif

/*err_out_disable: */

#if UNTOUCHED_PCI_REF_CNT
    if (!pci_is_enabled(PtrDev))
#endif
            pci_disable_device(ptrDev);

err_out:
            mutex_unlock(pinfo_lock);
#else
            ret = pci_enable_device(ptrDev);
#endif
            return ret;
        }

        case SCRUTINY_DRIVER_IOCTL_READ_PCI:
        {
            struct pci_dev *ptrDev = NULL;
            SCRUTINY_DRIVER_PCI_CFG_SPACE karg;
            int ret = -1;

            if (copy_from_user (&karg, (char *) arg, sizeof (SCRUTINY_DRIVER_PCI_CFG_SPACE)))
            {

                printk (KERN_ERR "sliff - failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__);

                return (-EFAULT);

            }

            ret = sdDriverDiscoverPciDevices (&ptrDev, karg);

            if (ret == -1)
            {
#if DEBUG
                pr_debug("scrutinyDriver - Unable to identify BRCM device \n");
#endif

                return (-ENOENT);
            }

            if (ptrDev == NULL)
            {
                printk (KERN_ERR "sliff - Unable to get PCI device information \n");

                return (-ENODEV);
            }

            if (ptrDev->bus->number != karg.Bus && PCI_SLOT (ptrDev->devfn) != karg.Device && PCI_FUNC (ptrDev->devfn) != karg.Function)
            {
                //user provided bus/dev/fn number did not match so throwing error

                printk (KERN_ERR "sliff - User provided wrong bus/dev/fn number \n");

                ret = -1;

                return (-EINVAL);

            }

            if (karg.Offset > 0xFF)
            {
                sdDriverProbeForEcam();
            }

            ret = sdiDriverRead (ptrDev, &karg);

            if (ret == -1)
            {
                U16 domain = 0;
#ifdef CONFIG_PCI_DOMAINS_GENERIC
                domain = ptrDev->bus->domain_nr;
#endif
                printk (KERN_DEBUG "[S:B:D:F = %04x:%02x:%02x:%x] scrutinyDriver ioctl:SCRUTINY_DRIVER_IOCTL_READ_PCI: Unable to read offset:%d\n",
                        domain, ptrDev->bus->number, PCI_SLOT (ptrDev->devfn), PCI_FUNC (ptrDev->devfn), karg.Offset);

                return (-EIO);
            }

            ret = copy_to_user ((char *) arg, &karg, sizeof (SCRUTINY_DRIVER_PCI_CFG_SPACE));

            //copy back the read contents back to pass on to arg structure
            if (ret)
            {
                printk (KERN_ERR "sliff - failure at %s:%d/%s() Copy Back=%d!\n", __FILE__, __LINE__, __func__, ret);

                return (-EFAULT);
            }

            return (0);
        }

        case SCRUTINY_DRIVER_IOCTL_WRITE_PCI:
        {
            struct pci_dev *ptrDev = NULL;
            SCRUTINY_DRIVER_PCI_CFG_SPACE karg;
            int ret = -1;

            if (copy_from_user (&karg, (char *) arg, sizeof (SCRUTINY_DRIVER_PCI_CFG_SPACE)))
            {

                printk(KERN_ERR "sliff - failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__);

                return (-EFAULT);

            }

            ret = sdDriverDiscoverPciDevices (&ptrDev, karg);

            if (ret == -1)
            {
                printk (KERN_ERR "sliff - Unable to identify BRCM device \n");

                return (-ENOENT);
            }

            if (ptrDev == NULL)
            {
                printk (KERN_ERR "sliff - Unable to get PCI device information \n");

                return (-ENODEV);
            }

            if (ptrDev->bus->number != karg.Bus && PCI_SLOT (ptrDev->devfn) != karg.Device && PCI_FUNC (ptrDev->devfn) != karg.Function)
            {
                //user provided bus/dev/fn number did not match so throwing error

                printk (KERN_ERR "sliff - User provided wrong bus/dev/fn number \n");

                ret = -1;

                return (-EINVAL);

            }

            if (karg.Offset > 0xFF)
            {
                sdDriverProbeForEcam();
            }

            ret = sdiDriverWrite (ptrDev, karg);

            if (ret == -1)
            {
                int domain = 0;
#ifdef CONFIG_PCI_DOMAINS_GENERIC
                domain = ptrDev->bus->domain_nr;
#endif
                printk (KERN_ERR "[S:B:D:F = %04x:%02x:%02x:%x] scrutinyDriver ioctl:SCRUTINY_DRIVER_IOCTL_WRITE_PCI: Unable to write offset:%d\n",
                        domain, ptrDev->bus->number, PCI_SLOT (ptrDev->devfn), PCI_FUNC (ptrDev->devfn), karg.Offset);

                return (-EIO);
            }

            return (0);
        }


        case SCRUTINY_DRIVER_IOCTL_ALLOCATE_MEMORY:
        {
            //PTR_SLIFF_DRIVER_MEMORY ptrMemory = (PTR_SLIFF_DRIVER_MEMORY) arg;

            int ret = -1;
            SLIFF_DRIVER_MEMORY karg;
            PTR_SLIFF_DRIVER_MEMORY ptrMemory;
            
            if (copy_from_user (&karg, (char *) arg, sizeof (SLIFF_DRIVER_MEMORY)))
            {

                printk ("SliffDriver - failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__);

                return (-EFAULT);

            }

            ptrMemory = (PTR_SLIFF_DRIVER_MEMORY) &karg;

            ret = sdDriverAllocateContiguousMemory (ptrMemory);

            ret = copy_to_user ((char *) arg, &karg, sizeof (SLIFF_DRIVER_MEMORY));

            if (ret)
            {
                printk (KERN_ERR "SliffDriver - failure at %s:%d/%s() Allocate Memory =%d!\n", __FILE__, __LINE__, __func__, ret);

                return (-EFAULT);
            }

            return (ret);

        }

        case SCRUTINY_DRIVER_IOCTL_FREE_MEMORY:
        {
                SLIFF_DRIVER_MEMORY karg;
                PTR_SLIFF_DRIVER_MEMORY ptrMemory ;

                if (copy_from_user (&karg, (char *) arg, sizeof (SLIFF_DRIVER_MEMORY)))
                {
                    printk ("SliffDriver - failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__);
                    return (-EFAULT);
                }
                ptrMemory = &karg;

                return (sdDriverFreeContiguousMemory (ptrMemory));

                return 0;
        }

#ifdef DRIVER_SUPPORT_NIC

        case SCRUTINY_DRIVER_IOCTL_SEND_HWRM:
        {
            int ret = -1;
            SLIFF_DRV_BNXT_DEV_INFO *pinfo;
            struct bnxt_mg_req mg_req;
            struct pci_dev *ptrDev = NULL;
            U32    domain = 0, bus, dev, func;
            struct mutex *pinfo_lock;

            ret = copy_from_user(&mg_req, (void __user *)arg, sizeof(struct bnxt_mg_req));
            if (ret) {
                pr_err("Failed to send %d bytes from the user\n", ret);
                return  -EINVAL;
            }

            domain = mg_req.hdr.seg;
            bus    = mg_req.hdr.bus;
            dev    = PCI_SLOT(mg_req.hdr.devfn);
            func   = PCI_FUNC (mg_req.hdr.devfn);
            pr_debug( "[S:B:D:F = %04x:%02x:%02x:%x] ioctl:SCRUTINY_DRIVER_IOCTL_SEND_HWRM\n", domain, bus, dev, func);

            ptrDev = pci_get_domain_bus_and_slot(domain, bus, mg_req.hdr.devfn);
            if(!ptrDev)
            {
                pr_err( "[S:B:D:F = %04x:%02x:%02x:%x] does not exist.\n", domain, bus, dev, func);
                /* if device was marked for deleted, free it */
                sdDriver_find_bnxt_dev(domain, mg_req.hdr.bus, mg_req.hdr.devfn);
                return (-ENOENT);
            }

            /* idx = sdDriver_bnxt_mg_get_hash_key(mg_req.hdr.bus, mg_req.hdr.devfn); */
            pr_debug("bus: 0x%x devfn: 0x%x, driverIndex: %d\n",
                     mg_req.hdr.bus, mg_req.hdr.devfn, mg_req.hdr.driverIndex);
            pinfo = sdDriver_find_bnxt_dev(domain, mg_req.hdr.bus, mg_req.hdr.devfn);
            mutex_lock(&pmgdev->lock);
            pinfo_lock = &pmgdev->info_lock[mg_req.hdr.bus][mg_req.hdr.devfn];
            mutex_unlock(&pmgdev->lock);

            mutex_lock(pinfo_lock);
            if (!pinfo) {
                /* get PtrDev info */
                pinfo = sdDriver_init_bnxt_dev(domain, mg_req.hdr.bus, mg_req.hdr.devfn);
                if (IS_ERR(pinfo)) {
                    pr_err("Failed to get PtrDev\n");
                    ret = PTR_ERR(pinfo);
		    goto err;
                }

		pinfo->Domain = domain;
                mutex_lock(&pmgdev->lock);
                pmgdev->pinfo[mg_req.hdr.bus][mg_req.hdr.devfn] = pinfo;
                mutex_unlock(&pmgdev->lock);
            }

            ptrDev = pinfo->PtrDev;

#ifdef CONFIG_PCI_DOMAINS_GENERIC
            domain = ptrDev->bus->domain_nr;
#endif
            if (!pci_is_enabled(pinfo->PtrDev)) {
                /* Don't enable when bnxt_en purposefully & forcefully diabled
                 * PCI device, Eg. for error recovery */
                pinfo->enabled_pci_dev = 0;
                if(is_owner_drv_loaded(ptrDev))
                {
                    ret = -EIO;
		    goto err;
                }

                pr_err( "[S:B:D:F = %04x:%02x:%02x:%x] PCI Device in disabled state, enabling...\n",
                        domain, bus, dev, func);

                if(pci_enable_device(pinfo->PtrDev))
                {
                    pr_err( "[S:B:D:F = %04x:%02x:%02x:%x] Failed to enable PCI Device\n", domain, bus, dev, func);
                    ret = -EBADFD;
                    goto err;
                }
                else {
                    pinfo->enabled_pci_dev = 1;
                }
            }

            pr_debug("R->D : Received B:DF - %x:%x", mg_req.hdr.bus, mg_req.hdr.devfn);

            ret = sdDriver_bnxt_mg_process_hwrm(pinfo, &mg_req);

err:
            mutex_unlock(pinfo_lock);
            return ret;

        }

        break;
#endif
        default:
        {
            pr_err("sliff - %s: IOCTL (%x) not supported\n", __FUNCTION__, cmd);

            ret_val = -1;
        }
    }

    return ret_val;
}

/*
 * Register Devices.
 */

module_init(sdiInitialize);
module_exit(sdiExit);

/*
 * Other Declarations
 */

MODULE_AUTHOR("Ganesh Ramachandran (ganesh.ramachandran@broadcom.com) & Rajesh Ravi (rajesh.ravi@broadcom.com)");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Broadcom generic character interface driver for PCI access");
MODULE_VERSION(SLIFF_DRIVER_VERSION);
