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