18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Linux network driver for QLogic BR-series Converged Network Adapter.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci/*
68c2ecf20Sopenharmony_ci * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
78c2ecf20Sopenharmony_ci * Copyright (c) 2014-2015 QLogic Corporation
88c2ecf20Sopenharmony_ci * All rights reserved
98c2ecf20Sopenharmony_ci * www.qlogic.com
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include "bnad.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci/*
178c2ecf20Sopenharmony_ci * BNA debufs interface
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci * To access the interface, debugfs file system should be mounted
208c2ecf20Sopenharmony_ci * if not already mounted using:
218c2ecf20Sopenharmony_ci *	mount -t debugfs none /sys/kernel/debug
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci * BNA Hierarchy:
248c2ecf20Sopenharmony_ci *	- bna/pci_dev:<pci_name>
258c2ecf20Sopenharmony_ci * where the pci_name corresponds to the one under /sys/bus/pci/drivers/bna
268c2ecf20Sopenharmony_ci *
278c2ecf20Sopenharmony_ci * Debugging service available per pci_dev:
288c2ecf20Sopenharmony_ci *	fwtrc:  To collect current firmware trace.
298c2ecf20Sopenharmony_ci *	fwsave: To collect last saved fw trace as a result of firmware crash.
308c2ecf20Sopenharmony_ci *	regwr:  To write one word to chip register
318c2ecf20Sopenharmony_ci *	regrd:  To read one or more words from chip register.
328c2ecf20Sopenharmony_ci */
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistruct bnad_debug_info {
358c2ecf20Sopenharmony_ci	char *debug_buffer;
368c2ecf20Sopenharmony_ci	void *i_private;
378c2ecf20Sopenharmony_ci	int buffer_len;
388c2ecf20Sopenharmony_ci};
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic int
418c2ecf20Sopenharmony_cibnad_debugfs_open_fwtrc(struct inode *inode, struct file *file)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	struct bnad *bnad = inode->i_private;
448c2ecf20Sopenharmony_ci	struct bnad_debug_info *fw_debug;
458c2ecf20Sopenharmony_ci	unsigned long flags;
468c2ecf20Sopenharmony_ci	int rc;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	fw_debug = kzalloc(sizeof(struct bnad_debug_info), GFP_KERNEL);
498c2ecf20Sopenharmony_ci	if (!fw_debug)
508c2ecf20Sopenharmony_ci		return -ENOMEM;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	fw_debug->buffer_len = BNA_DBG_FWTRC_LEN;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	fw_debug->debug_buffer = kzalloc(fw_debug->buffer_len, GFP_KERNEL);
558c2ecf20Sopenharmony_ci	if (!fw_debug->debug_buffer) {
568c2ecf20Sopenharmony_ci		kfree(fw_debug);
578c2ecf20Sopenharmony_ci		fw_debug = NULL;
588c2ecf20Sopenharmony_ci		return -ENOMEM;
598c2ecf20Sopenharmony_ci	}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	spin_lock_irqsave(&bnad->bna_lock, flags);
628c2ecf20Sopenharmony_ci	rc = bfa_nw_ioc_debug_fwtrc(&bnad->bna.ioceth.ioc,
638c2ecf20Sopenharmony_ci			fw_debug->debug_buffer,
648c2ecf20Sopenharmony_ci			&fw_debug->buffer_len);
658c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&bnad->bna_lock, flags);
668c2ecf20Sopenharmony_ci	if (rc != BFA_STATUS_OK) {
678c2ecf20Sopenharmony_ci		kfree(fw_debug->debug_buffer);
688c2ecf20Sopenharmony_ci		fw_debug->debug_buffer = NULL;
698c2ecf20Sopenharmony_ci		kfree(fw_debug);
708c2ecf20Sopenharmony_ci		fw_debug = NULL;
718c2ecf20Sopenharmony_ci		netdev_warn(bnad->netdev, "failed to collect fwtrc\n");
728c2ecf20Sopenharmony_ci		return -ENOMEM;
738c2ecf20Sopenharmony_ci	}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	file->private_data = fw_debug;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	return 0;
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic int
818c2ecf20Sopenharmony_cibnad_debugfs_open_fwsave(struct inode *inode, struct file *file)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	struct bnad *bnad = inode->i_private;
848c2ecf20Sopenharmony_ci	struct bnad_debug_info *fw_debug;
858c2ecf20Sopenharmony_ci	unsigned long flags;
868c2ecf20Sopenharmony_ci	int rc;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	fw_debug = kzalloc(sizeof(struct bnad_debug_info), GFP_KERNEL);
898c2ecf20Sopenharmony_ci	if (!fw_debug)
908c2ecf20Sopenharmony_ci		return -ENOMEM;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	fw_debug->buffer_len = BNA_DBG_FWTRC_LEN;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	fw_debug->debug_buffer = kzalloc(fw_debug->buffer_len, GFP_KERNEL);
958c2ecf20Sopenharmony_ci	if (!fw_debug->debug_buffer) {
968c2ecf20Sopenharmony_ci		kfree(fw_debug);
978c2ecf20Sopenharmony_ci		fw_debug = NULL;
988c2ecf20Sopenharmony_ci		return -ENOMEM;
998c2ecf20Sopenharmony_ci	}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	spin_lock_irqsave(&bnad->bna_lock, flags);
1028c2ecf20Sopenharmony_ci	rc = bfa_nw_ioc_debug_fwsave(&bnad->bna.ioceth.ioc,
1038c2ecf20Sopenharmony_ci			fw_debug->debug_buffer,
1048c2ecf20Sopenharmony_ci			&fw_debug->buffer_len);
1058c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&bnad->bna_lock, flags);
1068c2ecf20Sopenharmony_ci	if (rc != BFA_STATUS_OK && rc != BFA_STATUS_ENOFSAVE) {
1078c2ecf20Sopenharmony_ci		kfree(fw_debug->debug_buffer);
1088c2ecf20Sopenharmony_ci		fw_debug->debug_buffer = NULL;
1098c2ecf20Sopenharmony_ci		kfree(fw_debug);
1108c2ecf20Sopenharmony_ci		fw_debug = NULL;
1118c2ecf20Sopenharmony_ci		netdev_warn(bnad->netdev, "failed to collect fwsave\n");
1128c2ecf20Sopenharmony_ci		return -ENOMEM;
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	file->private_data = fw_debug;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	return 0;
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic int
1218c2ecf20Sopenharmony_cibnad_debugfs_open_reg(struct inode *inode, struct file *file)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	struct bnad_debug_info *reg_debug;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	reg_debug = kzalloc(sizeof(struct bnad_debug_info), GFP_KERNEL);
1268c2ecf20Sopenharmony_ci	if (!reg_debug)
1278c2ecf20Sopenharmony_ci		return -ENOMEM;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	reg_debug->i_private = inode->i_private;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	file->private_data = reg_debug;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	return 0;
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic int
1378c2ecf20Sopenharmony_cibnad_get_debug_drvinfo(struct bnad *bnad, void *buffer, u32 len)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	struct bnad_drvinfo *drvinfo = (struct bnad_drvinfo *) buffer;
1408c2ecf20Sopenharmony_ci	struct bnad_iocmd_comp fcomp;
1418c2ecf20Sopenharmony_ci	unsigned long flags = 0;
1428c2ecf20Sopenharmony_ci	int ret = BFA_STATUS_FAILED;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	/* Get IOC info */
1458c2ecf20Sopenharmony_ci	spin_lock_irqsave(&bnad->bna_lock, flags);
1468c2ecf20Sopenharmony_ci	bfa_nw_ioc_get_attr(&bnad->bna.ioceth.ioc, &drvinfo->ioc_attr);
1478c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&bnad->bna_lock, flags);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	/* Retrieve CEE related info */
1508c2ecf20Sopenharmony_ci	fcomp.bnad = bnad;
1518c2ecf20Sopenharmony_ci	fcomp.comp_status = 0;
1528c2ecf20Sopenharmony_ci	init_completion(&fcomp.comp);
1538c2ecf20Sopenharmony_ci	spin_lock_irqsave(&bnad->bna_lock, flags);
1548c2ecf20Sopenharmony_ci	ret = bfa_nw_cee_get_attr(&bnad->bna.cee, &drvinfo->cee_attr,
1558c2ecf20Sopenharmony_ci				bnad_cb_completion, &fcomp);
1568c2ecf20Sopenharmony_ci	if (ret != BFA_STATUS_OK) {
1578c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&bnad->bna_lock, flags);
1588c2ecf20Sopenharmony_ci		goto out;
1598c2ecf20Sopenharmony_ci	}
1608c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&bnad->bna_lock, flags);
1618c2ecf20Sopenharmony_ci	wait_for_completion(&fcomp.comp);
1628c2ecf20Sopenharmony_ci	drvinfo->cee_status = fcomp.comp_status;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	/* Retrieve flash partition info */
1658c2ecf20Sopenharmony_ci	fcomp.comp_status = 0;
1668c2ecf20Sopenharmony_ci	reinit_completion(&fcomp.comp);
1678c2ecf20Sopenharmony_ci	spin_lock_irqsave(&bnad->bna_lock, flags);
1688c2ecf20Sopenharmony_ci	ret = bfa_nw_flash_get_attr(&bnad->bna.flash, &drvinfo->flash_attr,
1698c2ecf20Sopenharmony_ci				bnad_cb_completion, &fcomp);
1708c2ecf20Sopenharmony_ci	if (ret != BFA_STATUS_OK) {
1718c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&bnad->bna_lock, flags);
1728c2ecf20Sopenharmony_ci		goto out;
1738c2ecf20Sopenharmony_ci	}
1748c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&bnad->bna_lock, flags);
1758c2ecf20Sopenharmony_ci	wait_for_completion(&fcomp.comp);
1768c2ecf20Sopenharmony_ci	drvinfo->flash_status = fcomp.comp_status;
1778c2ecf20Sopenharmony_ciout:
1788c2ecf20Sopenharmony_ci	return ret;
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistatic int
1828c2ecf20Sopenharmony_cibnad_debugfs_open_drvinfo(struct inode *inode, struct file *file)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	struct bnad *bnad = inode->i_private;
1858c2ecf20Sopenharmony_ci	struct bnad_debug_info *drv_info;
1868c2ecf20Sopenharmony_ci	int rc;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	drv_info = kzalloc(sizeof(struct bnad_debug_info), GFP_KERNEL);
1898c2ecf20Sopenharmony_ci	if (!drv_info)
1908c2ecf20Sopenharmony_ci		return -ENOMEM;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	drv_info->buffer_len = sizeof(struct bnad_drvinfo);
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	drv_info->debug_buffer = kzalloc(drv_info->buffer_len, GFP_KERNEL);
1958c2ecf20Sopenharmony_ci	if (!drv_info->debug_buffer) {
1968c2ecf20Sopenharmony_ci		kfree(drv_info);
1978c2ecf20Sopenharmony_ci		drv_info = NULL;
1988c2ecf20Sopenharmony_ci		return -ENOMEM;
1998c2ecf20Sopenharmony_ci	}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	mutex_lock(&bnad->conf_mutex);
2028c2ecf20Sopenharmony_ci	rc = bnad_get_debug_drvinfo(bnad, drv_info->debug_buffer,
2038c2ecf20Sopenharmony_ci				drv_info->buffer_len);
2048c2ecf20Sopenharmony_ci	mutex_unlock(&bnad->conf_mutex);
2058c2ecf20Sopenharmony_ci	if (rc != BFA_STATUS_OK) {
2068c2ecf20Sopenharmony_ci		kfree(drv_info->debug_buffer);
2078c2ecf20Sopenharmony_ci		drv_info->debug_buffer = NULL;
2088c2ecf20Sopenharmony_ci		kfree(drv_info);
2098c2ecf20Sopenharmony_ci		drv_info = NULL;
2108c2ecf20Sopenharmony_ci		netdev_warn(bnad->netdev, "failed to collect drvinfo\n");
2118c2ecf20Sopenharmony_ci		return -ENOMEM;
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	file->private_data = drv_info;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	return 0;
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci/* Changes the current file position */
2208c2ecf20Sopenharmony_cistatic loff_t
2218c2ecf20Sopenharmony_cibnad_debugfs_lseek(struct file *file, loff_t offset, int orig)
2228c2ecf20Sopenharmony_ci{
2238c2ecf20Sopenharmony_ci	struct bnad_debug_info *debug = file->private_data;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	if (!debug)
2268c2ecf20Sopenharmony_ci		return -EINVAL;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	return fixed_size_llseek(file, offset, orig, debug->buffer_len);
2298c2ecf20Sopenharmony_ci}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_cistatic ssize_t
2328c2ecf20Sopenharmony_cibnad_debugfs_read(struct file *file, char __user *buf,
2338c2ecf20Sopenharmony_ci		  size_t nbytes, loff_t *pos)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	struct bnad_debug_info *debug = file->private_data;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	if (!debug || !debug->debug_buffer)
2388c2ecf20Sopenharmony_ci		return 0;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	return simple_read_from_buffer(buf, nbytes, pos,
2418c2ecf20Sopenharmony_ci				debug->debug_buffer, debug->buffer_len);
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci#define BFA_REG_CT_ADDRSZ	(0x40000)
2458c2ecf20Sopenharmony_ci#define BFA_REG_CB_ADDRSZ	(0x20000)
2468c2ecf20Sopenharmony_ci#define BFA_REG_ADDRSZ(__ioc)	\
2478c2ecf20Sopenharmony_ci	((u32)(bfa_asic_id_ctc(bfa_ioc_devid(__ioc)) ?  \
2488c2ecf20Sopenharmony_ci	 BFA_REG_CT_ADDRSZ : BFA_REG_CB_ADDRSZ))
2498c2ecf20Sopenharmony_ci#define BFA_REG_ADDRMSK(__ioc)	(BFA_REG_ADDRSZ(__ioc) - 1)
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci/*
2528c2ecf20Sopenharmony_ci * Function to check if the register offset passed is valid.
2538c2ecf20Sopenharmony_ci */
2548c2ecf20Sopenharmony_cistatic int
2558c2ecf20Sopenharmony_cibna_reg_offset_check(struct bfa_ioc *ioc, u32 offset, u32 len)
2568c2ecf20Sopenharmony_ci{
2578c2ecf20Sopenharmony_ci	u8 area;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	/* check [16:15] */
2608c2ecf20Sopenharmony_ci	area = (offset >> 15) & 0x7;
2618c2ecf20Sopenharmony_ci	if (area == 0) {
2628c2ecf20Sopenharmony_ci		/* PCIe core register */
2638c2ecf20Sopenharmony_ci		if (offset + (len << 2) > 0x8000)	/* 8k dwords or 32KB */
2648c2ecf20Sopenharmony_ci			return BFA_STATUS_EINVAL;
2658c2ecf20Sopenharmony_ci	} else if (area == 0x1) {
2668c2ecf20Sopenharmony_ci		/* CB 32 KB memory page */
2678c2ecf20Sopenharmony_ci		if (offset + (len << 2) > 0x10000)	/* 8k dwords or 32KB */
2688c2ecf20Sopenharmony_ci			return BFA_STATUS_EINVAL;
2698c2ecf20Sopenharmony_ci	} else {
2708c2ecf20Sopenharmony_ci		/* CB register space 64KB */
2718c2ecf20Sopenharmony_ci		if (offset + (len << 2) > BFA_REG_ADDRMSK(ioc))
2728c2ecf20Sopenharmony_ci			return BFA_STATUS_EINVAL;
2738c2ecf20Sopenharmony_ci	}
2748c2ecf20Sopenharmony_ci	return BFA_STATUS_OK;
2758c2ecf20Sopenharmony_ci}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_cistatic ssize_t
2788c2ecf20Sopenharmony_cibnad_debugfs_read_regrd(struct file *file, char __user *buf,
2798c2ecf20Sopenharmony_ci			size_t nbytes, loff_t *pos)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	struct bnad_debug_info *regrd_debug = file->private_data;
2828c2ecf20Sopenharmony_ci	struct bnad *bnad = (struct bnad *)regrd_debug->i_private;
2838c2ecf20Sopenharmony_ci	ssize_t rc;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	if (!bnad->regdata)
2868c2ecf20Sopenharmony_ci		return 0;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	rc = simple_read_from_buffer(buf, nbytes, pos,
2898c2ecf20Sopenharmony_ci			bnad->regdata, bnad->reglen);
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	if ((*pos + nbytes) >= bnad->reglen) {
2928c2ecf20Sopenharmony_ci		kfree(bnad->regdata);
2938c2ecf20Sopenharmony_ci		bnad->regdata = NULL;
2948c2ecf20Sopenharmony_ci		bnad->reglen = 0;
2958c2ecf20Sopenharmony_ci	}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	return rc;
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_cistatic ssize_t
3018c2ecf20Sopenharmony_cibnad_debugfs_write_regrd(struct file *file, const char __user *buf,
3028c2ecf20Sopenharmony_ci		size_t nbytes, loff_t *ppos)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	struct bnad_debug_info *regrd_debug = file->private_data;
3058c2ecf20Sopenharmony_ci	struct bnad *bnad = (struct bnad *)regrd_debug->i_private;
3068c2ecf20Sopenharmony_ci	struct bfa_ioc *ioc = &bnad->bna.ioceth.ioc;
3078c2ecf20Sopenharmony_ci	int rc, i;
3088c2ecf20Sopenharmony_ci	u32 addr, len;
3098c2ecf20Sopenharmony_ci	u32 *regbuf;
3108c2ecf20Sopenharmony_ci	void __iomem *rb, *reg_addr;
3118c2ecf20Sopenharmony_ci	unsigned long flags;
3128c2ecf20Sopenharmony_ci	void *kern_buf;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	/* Copy the user space buf */
3158c2ecf20Sopenharmony_ci	kern_buf = memdup_user(buf, nbytes);
3168c2ecf20Sopenharmony_ci	if (IS_ERR(kern_buf))
3178c2ecf20Sopenharmony_ci		return PTR_ERR(kern_buf);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	rc = sscanf(kern_buf, "%x:%x", &addr, &len);
3208c2ecf20Sopenharmony_ci	if (rc < 2 || len > UINT_MAX >> 2) {
3218c2ecf20Sopenharmony_ci		netdev_warn(bnad->netdev, "failed to read user buffer\n");
3228c2ecf20Sopenharmony_ci		kfree(kern_buf);
3238c2ecf20Sopenharmony_ci		return -EINVAL;
3248c2ecf20Sopenharmony_ci	}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	kfree(kern_buf);
3278c2ecf20Sopenharmony_ci	kfree(bnad->regdata);
3288c2ecf20Sopenharmony_ci	bnad->reglen = 0;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	bnad->regdata = kzalloc(len << 2, GFP_KERNEL);
3318c2ecf20Sopenharmony_ci	if (!bnad->regdata)
3328c2ecf20Sopenharmony_ci		return -ENOMEM;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	bnad->reglen = len << 2;
3358c2ecf20Sopenharmony_ci	rb = bfa_ioc_bar0(ioc);
3368c2ecf20Sopenharmony_ci	addr &= BFA_REG_ADDRMSK(ioc);
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	/* offset and len sanity check */
3398c2ecf20Sopenharmony_ci	rc = bna_reg_offset_check(ioc, addr, len);
3408c2ecf20Sopenharmony_ci	if (rc) {
3418c2ecf20Sopenharmony_ci		netdev_warn(bnad->netdev, "failed reg offset check\n");
3428c2ecf20Sopenharmony_ci		kfree(bnad->regdata);
3438c2ecf20Sopenharmony_ci		bnad->regdata = NULL;
3448c2ecf20Sopenharmony_ci		bnad->reglen = 0;
3458c2ecf20Sopenharmony_ci		return -EINVAL;
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	reg_addr = rb + addr;
3498c2ecf20Sopenharmony_ci	regbuf =  (u32 *)bnad->regdata;
3508c2ecf20Sopenharmony_ci	spin_lock_irqsave(&bnad->bna_lock, flags);
3518c2ecf20Sopenharmony_ci	for (i = 0; i < len; i++) {
3528c2ecf20Sopenharmony_ci		*regbuf = readl(reg_addr);
3538c2ecf20Sopenharmony_ci		regbuf++;
3548c2ecf20Sopenharmony_ci		reg_addr += sizeof(u32);
3558c2ecf20Sopenharmony_ci	}
3568c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&bnad->bna_lock, flags);
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	return nbytes;
3598c2ecf20Sopenharmony_ci}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_cistatic ssize_t
3628c2ecf20Sopenharmony_cibnad_debugfs_write_regwr(struct file *file, const char __user *buf,
3638c2ecf20Sopenharmony_ci		size_t nbytes, loff_t *ppos)
3648c2ecf20Sopenharmony_ci{
3658c2ecf20Sopenharmony_ci	struct bnad_debug_info *debug = file->private_data;
3668c2ecf20Sopenharmony_ci	struct bnad *bnad = (struct bnad *)debug->i_private;
3678c2ecf20Sopenharmony_ci	struct bfa_ioc *ioc = &bnad->bna.ioceth.ioc;
3688c2ecf20Sopenharmony_ci	int rc;
3698c2ecf20Sopenharmony_ci	u32 addr, val;
3708c2ecf20Sopenharmony_ci	void __iomem *reg_addr;
3718c2ecf20Sopenharmony_ci	unsigned long flags;
3728c2ecf20Sopenharmony_ci	void *kern_buf;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	/* Copy the user space buf */
3758c2ecf20Sopenharmony_ci	kern_buf = memdup_user(buf, nbytes);
3768c2ecf20Sopenharmony_ci	if (IS_ERR(kern_buf))
3778c2ecf20Sopenharmony_ci		return PTR_ERR(kern_buf);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	rc = sscanf(kern_buf, "%x:%x", &addr, &val);
3808c2ecf20Sopenharmony_ci	if (rc < 2) {
3818c2ecf20Sopenharmony_ci		netdev_warn(bnad->netdev, "failed to read user buffer\n");
3828c2ecf20Sopenharmony_ci		kfree(kern_buf);
3838c2ecf20Sopenharmony_ci		return -EINVAL;
3848c2ecf20Sopenharmony_ci	}
3858c2ecf20Sopenharmony_ci	kfree(kern_buf);
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	addr &= BFA_REG_ADDRMSK(ioc); /* offset only 17 bit and word align */
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	/* offset and len sanity check */
3908c2ecf20Sopenharmony_ci	rc = bna_reg_offset_check(ioc, addr, 1);
3918c2ecf20Sopenharmony_ci	if (rc) {
3928c2ecf20Sopenharmony_ci		netdev_warn(bnad->netdev, "failed reg offset check\n");
3938c2ecf20Sopenharmony_ci		return -EINVAL;
3948c2ecf20Sopenharmony_ci	}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	reg_addr = (bfa_ioc_bar0(ioc)) + addr;
3978c2ecf20Sopenharmony_ci	spin_lock_irqsave(&bnad->bna_lock, flags);
3988c2ecf20Sopenharmony_ci	writel(val, reg_addr);
3998c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&bnad->bna_lock, flags);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	return nbytes;
4028c2ecf20Sopenharmony_ci}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_cistatic int
4058c2ecf20Sopenharmony_cibnad_debugfs_release(struct inode *inode, struct file *file)
4068c2ecf20Sopenharmony_ci{
4078c2ecf20Sopenharmony_ci	struct bnad_debug_info *debug = file->private_data;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	if (!debug)
4108c2ecf20Sopenharmony_ci		return 0;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	file->private_data = NULL;
4138c2ecf20Sopenharmony_ci	kfree(debug);
4148c2ecf20Sopenharmony_ci	return 0;
4158c2ecf20Sopenharmony_ci}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_cistatic int
4188c2ecf20Sopenharmony_cibnad_debugfs_buffer_release(struct inode *inode, struct file *file)
4198c2ecf20Sopenharmony_ci{
4208c2ecf20Sopenharmony_ci	struct bnad_debug_info *debug = file->private_data;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	if (!debug)
4238c2ecf20Sopenharmony_ci		return 0;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	kfree(debug->debug_buffer);
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	file->private_data = NULL;
4288c2ecf20Sopenharmony_ci	kfree(debug);
4298c2ecf20Sopenharmony_ci	debug = NULL;
4308c2ecf20Sopenharmony_ci	return 0;
4318c2ecf20Sopenharmony_ci}
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_cistatic const struct file_operations bnad_debugfs_op_fwtrc = {
4348c2ecf20Sopenharmony_ci	.owner		=	THIS_MODULE,
4358c2ecf20Sopenharmony_ci	.open		=	bnad_debugfs_open_fwtrc,
4368c2ecf20Sopenharmony_ci	.llseek		=	bnad_debugfs_lseek,
4378c2ecf20Sopenharmony_ci	.read		=	bnad_debugfs_read,
4388c2ecf20Sopenharmony_ci	.release	=	bnad_debugfs_buffer_release,
4398c2ecf20Sopenharmony_ci};
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_cistatic const struct file_operations bnad_debugfs_op_fwsave = {
4428c2ecf20Sopenharmony_ci	.owner		=	THIS_MODULE,
4438c2ecf20Sopenharmony_ci	.open		=	bnad_debugfs_open_fwsave,
4448c2ecf20Sopenharmony_ci	.llseek		=	bnad_debugfs_lseek,
4458c2ecf20Sopenharmony_ci	.read		=	bnad_debugfs_read,
4468c2ecf20Sopenharmony_ci	.release	=	bnad_debugfs_buffer_release,
4478c2ecf20Sopenharmony_ci};
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_cistatic const struct file_operations bnad_debugfs_op_regrd = {
4508c2ecf20Sopenharmony_ci	.owner		=       THIS_MODULE,
4518c2ecf20Sopenharmony_ci	.open		=	bnad_debugfs_open_reg,
4528c2ecf20Sopenharmony_ci	.llseek		=	bnad_debugfs_lseek,
4538c2ecf20Sopenharmony_ci	.read		=	bnad_debugfs_read_regrd,
4548c2ecf20Sopenharmony_ci	.write		=	bnad_debugfs_write_regrd,
4558c2ecf20Sopenharmony_ci	.release	=	bnad_debugfs_release,
4568c2ecf20Sopenharmony_ci};
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_cistatic const struct file_operations bnad_debugfs_op_regwr = {
4598c2ecf20Sopenharmony_ci	.owner		=	THIS_MODULE,
4608c2ecf20Sopenharmony_ci	.open		=	bnad_debugfs_open_reg,
4618c2ecf20Sopenharmony_ci	.llseek		=	bnad_debugfs_lseek,
4628c2ecf20Sopenharmony_ci	.write		=	bnad_debugfs_write_regwr,
4638c2ecf20Sopenharmony_ci	.release	=	bnad_debugfs_release,
4648c2ecf20Sopenharmony_ci};
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_cistatic const struct file_operations bnad_debugfs_op_drvinfo = {
4678c2ecf20Sopenharmony_ci	.owner		=	THIS_MODULE,
4688c2ecf20Sopenharmony_ci	.open		=	bnad_debugfs_open_drvinfo,
4698c2ecf20Sopenharmony_ci	.llseek		=	bnad_debugfs_lseek,
4708c2ecf20Sopenharmony_ci	.read		=	bnad_debugfs_read,
4718c2ecf20Sopenharmony_ci	.release	=	bnad_debugfs_buffer_release,
4728c2ecf20Sopenharmony_ci};
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_cistruct bnad_debugfs_entry {
4758c2ecf20Sopenharmony_ci	const char *name;
4768c2ecf20Sopenharmony_ci	umode_t  mode;
4778c2ecf20Sopenharmony_ci	const struct file_operations *fops;
4788c2ecf20Sopenharmony_ci};
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_cistatic const struct bnad_debugfs_entry bnad_debugfs_files[] = {
4818c2ecf20Sopenharmony_ci	{ "fwtrc",  S_IFREG | 0444, &bnad_debugfs_op_fwtrc, },
4828c2ecf20Sopenharmony_ci	{ "fwsave", S_IFREG | 0444, &bnad_debugfs_op_fwsave, },
4838c2ecf20Sopenharmony_ci	{ "regrd",  S_IFREG | 0644, &bnad_debugfs_op_regrd, },
4848c2ecf20Sopenharmony_ci	{ "regwr",  S_IFREG | 0200, &bnad_debugfs_op_regwr, },
4858c2ecf20Sopenharmony_ci	{ "drvinfo", S_IFREG | 0444, &bnad_debugfs_op_drvinfo, },
4868c2ecf20Sopenharmony_ci};
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_cistatic struct dentry *bna_debugfs_root;
4898c2ecf20Sopenharmony_cistatic atomic_t bna_debugfs_port_count;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci/* Initialize debugfs interface for BNA */
4928c2ecf20Sopenharmony_civoid
4938c2ecf20Sopenharmony_cibnad_debugfs_init(struct bnad *bnad)
4948c2ecf20Sopenharmony_ci{
4958c2ecf20Sopenharmony_ci	const struct bnad_debugfs_entry *file;
4968c2ecf20Sopenharmony_ci	char name[64];
4978c2ecf20Sopenharmony_ci	int i;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	/* Setup the BNA debugfs root directory*/
5008c2ecf20Sopenharmony_ci	if (!bna_debugfs_root) {
5018c2ecf20Sopenharmony_ci		bna_debugfs_root = debugfs_create_dir("bna", NULL);
5028c2ecf20Sopenharmony_ci		atomic_set(&bna_debugfs_port_count, 0);
5038c2ecf20Sopenharmony_ci		if (!bna_debugfs_root) {
5048c2ecf20Sopenharmony_ci			netdev_warn(bnad->netdev,
5058c2ecf20Sopenharmony_ci				    "debugfs root dir creation failed\n");
5068c2ecf20Sopenharmony_ci			return;
5078c2ecf20Sopenharmony_ci		}
5088c2ecf20Sopenharmony_ci	}
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	/* Setup the pci_dev debugfs directory for the port */
5118c2ecf20Sopenharmony_ci	snprintf(name, sizeof(name), "pci_dev:%s", pci_name(bnad->pcidev));
5128c2ecf20Sopenharmony_ci	if (!bnad->port_debugfs_root) {
5138c2ecf20Sopenharmony_ci		bnad->port_debugfs_root =
5148c2ecf20Sopenharmony_ci			debugfs_create_dir(name, bna_debugfs_root);
5158c2ecf20Sopenharmony_ci		if (!bnad->port_debugfs_root) {
5168c2ecf20Sopenharmony_ci			netdev_warn(bnad->netdev,
5178c2ecf20Sopenharmony_ci				    "debugfs root dir creation failed\n");
5188c2ecf20Sopenharmony_ci			return;
5198c2ecf20Sopenharmony_ci		}
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci		atomic_inc(&bna_debugfs_port_count);
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(bnad_debugfs_files); i++) {
5248c2ecf20Sopenharmony_ci			file = &bnad_debugfs_files[i];
5258c2ecf20Sopenharmony_ci			bnad->bnad_dentry_files[i] =
5268c2ecf20Sopenharmony_ci					debugfs_create_file(file->name,
5278c2ecf20Sopenharmony_ci							file->mode,
5288c2ecf20Sopenharmony_ci							bnad->port_debugfs_root,
5298c2ecf20Sopenharmony_ci							bnad,
5308c2ecf20Sopenharmony_ci							file->fops);
5318c2ecf20Sopenharmony_ci			if (!bnad->bnad_dentry_files[i]) {
5328c2ecf20Sopenharmony_ci				netdev_warn(bnad->netdev,
5338c2ecf20Sopenharmony_ci					    "create %s entry failed\n",
5348c2ecf20Sopenharmony_ci					    file->name);
5358c2ecf20Sopenharmony_ci				return;
5368c2ecf20Sopenharmony_ci			}
5378c2ecf20Sopenharmony_ci		}
5388c2ecf20Sopenharmony_ci	}
5398c2ecf20Sopenharmony_ci}
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci/* Uninitialize debugfs interface for BNA */
5428c2ecf20Sopenharmony_civoid
5438c2ecf20Sopenharmony_cibnad_debugfs_uninit(struct bnad *bnad)
5448c2ecf20Sopenharmony_ci{
5458c2ecf20Sopenharmony_ci	int i;
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(bnad_debugfs_files); i++) {
5488c2ecf20Sopenharmony_ci		if (bnad->bnad_dentry_files[i]) {
5498c2ecf20Sopenharmony_ci			debugfs_remove(bnad->bnad_dentry_files[i]);
5508c2ecf20Sopenharmony_ci			bnad->bnad_dentry_files[i] = NULL;
5518c2ecf20Sopenharmony_ci		}
5528c2ecf20Sopenharmony_ci	}
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	/* Remove the pci_dev debugfs directory for the port */
5558c2ecf20Sopenharmony_ci	if (bnad->port_debugfs_root) {
5568c2ecf20Sopenharmony_ci		debugfs_remove(bnad->port_debugfs_root);
5578c2ecf20Sopenharmony_ci		bnad->port_debugfs_root = NULL;
5588c2ecf20Sopenharmony_ci		atomic_dec(&bna_debugfs_port_count);
5598c2ecf20Sopenharmony_ci	}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	/* Remove the BNA debugfs root directory */
5628c2ecf20Sopenharmony_ci	if (atomic_read(&bna_debugfs_port_count) == 0) {
5638c2ecf20Sopenharmony_ci		debugfs_remove(bna_debugfs_root);
5648c2ecf20Sopenharmony_ci		bna_debugfs_root = NULL;
5658c2ecf20Sopenharmony_ci	}
5668c2ecf20Sopenharmony_ci}
567