504 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.4 * Copyright (c) 2014- QLogic Corporation.5 * All rights reserved6 * www.qlogic.com7 *8 * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.9 */10 11#include <linux/debugfs.h>12#include <linux/export.h>13 14#include "bfad_drv.h"15#include "bfad_im.h"16 17/*18 * BFA debufs interface19 *20 * To access the interface, debugfs file system should be mounted21 * if not already mounted using:22 * mount -t debugfs none /sys/kernel/debug23 *24 * BFA Hierarchy:25 * - bfa/pci_dev:<pci_name>26 * where the pci_name corresponds to the one under /sys/bus/pci/drivers/bfa27 *28 * Debugging service available per pci_dev:29 * fwtrc: To collect current firmware trace.30 * drvtrc: To collect current driver trace31 * fwsave: To collect last saved fw trace as a result of firmware crash.32 * regwr: To write one word to chip register33 * regrd: To read one or more words from chip register.34 */35 36struct bfad_debug_info {37 char *debug_buffer;38 void *i_private;39 int buffer_len;40};41 42static int43bfad_debugfs_open_drvtrc(struct inode *inode, struct file *file)44{45 struct bfad_port_s *port = inode->i_private;46 struct bfad_s *bfad = port->bfad;47 struct bfad_debug_info *debug;48 49 debug = kzalloc(sizeof(struct bfad_debug_info), GFP_KERNEL);50 if (!debug)51 return -ENOMEM;52 53 debug->debug_buffer = (void *) bfad->trcmod;54 debug->buffer_len = sizeof(struct bfa_trc_mod_s);55 56 file->private_data = debug;57 58 return 0;59}60 61static int62bfad_debugfs_open_fwtrc(struct inode *inode, struct file *file)63{64 struct bfad_port_s *port = inode->i_private;65 struct bfad_s *bfad = port->bfad;66 struct bfad_debug_info *fw_debug;67 unsigned long flags;68 int rc;69 70 fw_debug = kzalloc(sizeof(struct bfad_debug_info), GFP_KERNEL);71 if (!fw_debug)72 return -ENOMEM;73 74 fw_debug->buffer_len = sizeof(struct bfa_trc_mod_s);75 76 fw_debug->debug_buffer = vzalloc(fw_debug->buffer_len);77 if (!fw_debug->debug_buffer) {78 kfree(fw_debug);79 printk(KERN_INFO "bfad[%d]: Failed to allocate fwtrc buffer\n",80 bfad->inst_no);81 return -ENOMEM;82 }83 84 spin_lock_irqsave(&bfad->bfad_lock, flags);85 rc = bfa_ioc_debug_fwtrc(&bfad->bfa.ioc,86 fw_debug->debug_buffer,87 &fw_debug->buffer_len);88 spin_unlock_irqrestore(&bfad->bfad_lock, flags);89 if (rc != BFA_STATUS_OK) {90 vfree(fw_debug->debug_buffer);91 fw_debug->debug_buffer = NULL;92 kfree(fw_debug);93 printk(KERN_INFO "bfad[%d]: Failed to collect fwtrc\n",94 bfad->inst_no);95 return -ENOMEM;96 }97 98 file->private_data = fw_debug;99 100 return 0;101}102 103static int104bfad_debugfs_open_fwsave(struct inode *inode, struct file *file)105{106 struct bfad_port_s *port = inode->i_private;107 struct bfad_s *bfad = port->bfad;108 struct bfad_debug_info *fw_debug;109 unsigned long flags;110 int rc;111 112 fw_debug = kzalloc(sizeof(struct bfad_debug_info), GFP_KERNEL);113 if (!fw_debug)114 return -ENOMEM;115 116 fw_debug->buffer_len = sizeof(struct bfa_trc_mod_s);117 118 fw_debug->debug_buffer = vzalloc(fw_debug->buffer_len);119 if (!fw_debug->debug_buffer) {120 kfree(fw_debug);121 printk(KERN_INFO "bfad[%d]: Failed to allocate fwsave buffer\n",122 bfad->inst_no);123 return -ENOMEM;124 }125 126 spin_lock_irqsave(&bfad->bfad_lock, flags);127 rc = bfa_ioc_debug_fwsave(&bfad->bfa.ioc,128 fw_debug->debug_buffer,129 &fw_debug->buffer_len);130 spin_unlock_irqrestore(&bfad->bfad_lock, flags);131 if (rc != BFA_STATUS_OK) {132 vfree(fw_debug->debug_buffer);133 fw_debug->debug_buffer = NULL;134 kfree(fw_debug);135 printk(KERN_INFO "bfad[%d]: Failed to collect fwsave\n",136 bfad->inst_no);137 return -ENOMEM;138 }139 140 file->private_data = fw_debug;141 142 return 0;143}144 145static int146bfad_debugfs_open_reg(struct inode *inode, struct file *file)147{148 struct bfad_debug_info *reg_debug;149 150 reg_debug = kzalloc(sizeof(struct bfad_debug_info), GFP_KERNEL);151 if (!reg_debug)152 return -ENOMEM;153 154 reg_debug->i_private = inode->i_private;155 156 file->private_data = reg_debug;157 158 return 0;159}160 161/* Changes the current file position */162static loff_t163bfad_debugfs_lseek(struct file *file, loff_t offset, int orig)164{165 struct bfad_debug_info *debug = file->private_data;166 return fixed_size_llseek(file, offset, orig,167 debug->buffer_len);168}169 170static ssize_t171bfad_debugfs_read(struct file *file, char __user *buf,172 size_t nbytes, loff_t *pos)173{174 struct bfad_debug_info *debug = file->private_data;175 176 if (!debug || !debug->debug_buffer)177 return 0;178 179 return simple_read_from_buffer(buf, nbytes, pos,180 debug->debug_buffer, debug->buffer_len);181}182 183#define BFA_REG_CT_ADDRSZ (0x40000)184#define BFA_REG_CB_ADDRSZ (0x20000)185#define BFA_REG_ADDRSZ(__ioc) \186 ((u32)(bfa_asic_id_ctc(bfa_ioc_devid(__ioc)) ? \187 BFA_REG_CT_ADDRSZ : BFA_REG_CB_ADDRSZ))188#define BFA_REG_ADDRMSK(__ioc) (BFA_REG_ADDRSZ(__ioc) - 1)189 190static bfa_status_t191bfad_reg_offset_check(struct bfa_s *bfa, u32 offset, u32 len)192{193 u8 area;194 195 /* check [16:15] */196 area = (offset >> 15) & 0x7;197 if (area == 0) {198 /* PCIe core register */199 if ((offset + (len<<2)) > 0x8000) /* 8k dwords or 32KB */200 return BFA_STATUS_EINVAL;201 } else if (area == 0x1) {202 /* CB 32 KB memory page */203 if ((offset + (len<<2)) > 0x10000) /* 8k dwords or 32KB */204 return BFA_STATUS_EINVAL;205 } else {206 /* CB register space 64KB */207 if ((offset + (len<<2)) > BFA_REG_ADDRMSK(&bfa->ioc))208 return BFA_STATUS_EINVAL;209 }210 return BFA_STATUS_OK;211}212 213static ssize_t214bfad_debugfs_read_regrd(struct file *file, char __user *buf,215 size_t nbytes, loff_t *pos)216{217 struct bfad_debug_info *regrd_debug = file->private_data;218 struct bfad_port_s *port = (struct bfad_port_s *)regrd_debug->i_private;219 struct bfad_s *bfad = port->bfad;220 ssize_t rc;221 222 if (!bfad->regdata)223 return 0;224 225 rc = simple_read_from_buffer(buf, nbytes, pos,226 bfad->regdata, bfad->reglen);227 228 if ((*pos + nbytes) >= bfad->reglen) {229 kfree(bfad->regdata);230 bfad->regdata = NULL;231 bfad->reglen = 0;232 }233 234 return rc;235}236 237static ssize_t238bfad_debugfs_write_regrd(struct file *file, const char __user *buf,239 size_t nbytes, loff_t *ppos)240{241 struct bfad_debug_info *regrd_debug = file->private_data;242 struct bfad_port_s *port = (struct bfad_port_s *)regrd_debug->i_private;243 struct bfad_s *bfad = port->bfad;244 struct bfa_s *bfa = &bfad->bfa;245 struct bfa_ioc_s *ioc = &bfa->ioc;246 int addr, rc, i;247 u32 len;248 u32 *regbuf;249 void __iomem *rb, *reg_addr;250 unsigned long flags;251 void *kern_buf;252 253 kern_buf = memdup_user_nul(buf, nbytes);254 if (IS_ERR(kern_buf))255 return PTR_ERR(kern_buf);256 257 rc = sscanf(kern_buf, "%x:%x", &addr, &len);258 if (rc < 2 || len > (UINT_MAX >> 2)) {259 printk(KERN_INFO260 "bfad[%d]: %s failed to read user buf\n",261 bfad->inst_no, __func__);262 kfree(kern_buf);263 return -EINVAL;264 }265 266 kfree(kern_buf);267 kfree(bfad->regdata);268 bfad->regdata = NULL;269 bfad->reglen = 0;270 271 bfad->regdata = kzalloc(len << 2, GFP_KERNEL);272 if (!bfad->regdata) {273 printk(KERN_INFO "bfad[%d]: Failed to allocate regrd buffer\n",274 bfad->inst_no);275 return -ENOMEM;276 }277 278 bfad->reglen = len << 2;279 rb = bfa_ioc_bar0(ioc);280 addr &= BFA_REG_ADDRMSK(ioc);281 282 /* offset and len sanity check */283 rc = bfad_reg_offset_check(bfa, addr, len);284 if (rc) {285 printk(KERN_INFO "bfad[%d]: Failed reg offset check\n",286 bfad->inst_no);287 kfree(bfad->regdata);288 bfad->regdata = NULL;289 bfad->reglen = 0;290 return -EINVAL;291 }292 293 reg_addr = rb + addr;294 regbuf = (u32 *)bfad->regdata;295 spin_lock_irqsave(&bfad->bfad_lock, flags);296 for (i = 0; i < len; i++) {297 *regbuf = readl(reg_addr);298 regbuf++;299 reg_addr += sizeof(u32);300 }301 spin_unlock_irqrestore(&bfad->bfad_lock, flags);302 303 return nbytes;304}305 306static ssize_t307bfad_debugfs_write_regwr(struct file *file, const char __user *buf,308 size_t nbytes, loff_t *ppos)309{310 struct bfad_debug_info *debug = file->private_data;311 struct bfad_port_s *port = (struct bfad_port_s *)debug->i_private;312 struct bfad_s *bfad = port->bfad;313 struct bfa_s *bfa = &bfad->bfa;314 struct bfa_ioc_s *ioc = &bfa->ioc;315 int addr, val, rc;316 void __iomem *reg_addr;317 unsigned long flags;318 void *kern_buf;319 320 kern_buf = memdup_user_nul(buf, nbytes);321 if (IS_ERR(kern_buf))322 return PTR_ERR(kern_buf);323 324 rc = sscanf(kern_buf, "%x:%x", &addr, &val);325 if (rc < 2) {326 printk(KERN_INFO327 "bfad[%d]: %s failed to read user buf\n",328 bfad->inst_no, __func__);329 kfree(kern_buf);330 return -EINVAL;331 }332 kfree(kern_buf);333 334 addr &= BFA_REG_ADDRMSK(ioc); /* offset only 17 bit and word align */335 336 /* offset and len sanity check */337 rc = bfad_reg_offset_check(bfa, addr, 1);338 if (rc) {339 printk(KERN_INFO340 "bfad[%d]: Failed reg offset check\n",341 bfad->inst_no);342 return -EINVAL;343 }344 345 reg_addr = (bfa_ioc_bar0(ioc)) + addr;346 spin_lock_irqsave(&bfad->bfad_lock, flags);347 writel(val, reg_addr);348 spin_unlock_irqrestore(&bfad->bfad_lock, flags);349 350 return nbytes;351}352 353static int354bfad_debugfs_release(struct inode *inode, struct file *file)355{356 struct bfad_debug_info *debug = file->private_data;357 358 if (!debug)359 return 0;360 361 file->private_data = NULL;362 kfree(debug);363 return 0;364}365 366static int367bfad_debugfs_release_fwtrc(struct inode *inode, struct file *file)368{369 struct bfad_debug_info *fw_debug = file->private_data;370 371 if (!fw_debug)372 return 0;373 374 vfree(fw_debug->debug_buffer);375 376 file->private_data = NULL;377 kfree(fw_debug);378 return 0;379}380 381static const struct file_operations bfad_debugfs_op_drvtrc = {382 .owner = THIS_MODULE,383 .open = bfad_debugfs_open_drvtrc,384 .llseek = bfad_debugfs_lseek,385 .read = bfad_debugfs_read,386 .release = bfad_debugfs_release,387};388 389static const struct file_operations bfad_debugfs_op_fwtrc = {390 .owner = THIS_MODULE,391 .open = bfad_debugfs_open_fwtrc,392 .llseek = bfad_debugfs_lseek,393 .read = bfad_debugfs_read,394 .release = bfad_debugfs_release_fwtrc,395};396 397static const struct file_operations bfad_debugfs_op_fwsave = {398 .owner = THIS_MODULE,399 .open = bfad_debugfs_open_fwsave,400 .llseek = bfad_debugfs_lseek,401 .read = bfad_debugfs_read,402 .release = bfad_debugfs_release_fwtrc,403};404 405static const struct file_operations bfad_debugfs_op_regrd = {406 .owner = THIS_MODULE,407 .open = bfad_debugfs_open_reg,408 .llseek = bfad_debugfs_lseek,409 .read = bfad_debugfs_read_regrd,410 .write = bfad_debugfs_write_regrd,411 .release = bfad_debugfs_release,412};413 414static const struct file_operations bfad_debugfs_op_regwr = {415 .owner = THIS_MODULE,416 .open = bfad_debugfs_open_reg,417 .llseek = bfad_debugfs_lseek,418 .write = bfad_debugfs_write_regwr,419 .release = bfad_debugfs_release,420};421 422struct bfad_debugfs_entry {423 const char *name;424 umode_t mode;425 const struct file_operations *fops;426};427 428static const struct bfad_debugfs_entry bfad_debugfs_files[] = {429 { "drvtrc", S_IFREG|S_IRUGO, &bfad_debugfs_op_drvtrc, },430 { "fwtrc", S_IFREG|S_IRUGO, &bfad_debugfs_op_fwtrc, },431 { "fwsave", S_IFREG|S_IRUGO, &bfad_debugfs_op_fwsave, },432 { "regrd", S_IFREG|S_IRUGO|S_IWUSR, &bfad_debugfs_op_regrd, },433 { "regwr", S_IFREG|S_IWUSR, &bfad_debugfs_op_regwr, },434};435 436static struct dentry *bfa_debugfs_root;437static atomic_t bfa_debugfs_port_count;438 439inline void440bfad_debugfs_init(struct bfad_port_s *port)441{442 struct bfad_s *bfad = port->bfad;443 const struct bfad_debugfs_entry *file;444 char name[64];445 int i;446 447 if (!bfa_debugfs_enable)448 return;449 450 /* Setup the BFA debugfs root directory*/451 if (!bfa_debugfs_root) {452 bfa_debugfs_root = debugfs_create_dir("bfa", NULL);453 atomic_set(&bfa_debugfs_port_count, 0);454 }455 456 /* Setup the pci_dev debugfs directory for the port */457 snprintf(name, sizeof(name), "pci_dev:%s", bfad->pci_name);458 if (!port->port_debugfs_root) {459 port->port_debugfs_root =460 debugfs_create_dir(name, bfa_debugfs_root);461 462 atomic_inc(&bfa_debugfs_port_count);463 464 for (i = 0; i < ARRAY_SIZE(bfad_debugfs_files); i++) {465 file = &bfad_debugfs_files[i];466 bfad->bfad_dentry_files[i] =467 debugfs_create_file(file->name,468 file->mode,469 port->port_debugfs_root,470 port,471 file->fops);472 }473 }474 475 return;476}477 478inline void479bfad_debugfs_exit(struct bfad_port_s *port)480{481 struct bfad_s *bfad = port->bfad;482 int i;483 484 for (i = 0; i < ARRAY_SIZE(bfad_debugfs_files); i++) {485 if (bfad->bfad_dentry_files[i]) {486 debugfs_remove(bfad->bfad_dentry_files[i]);487 bfad->bfad_dentry_files[i] = NULL;488 }489 }490 491 /* Remove the pci_dev debugfs directory for the port */492 if (port->port_debugfs_root) {493 debugfs_remove(port->port_debugfs_root);494 port->port_debugfs_root = NULL;495 atomic_dec(&bfa_debugfs_port_count);496 }497 498 /* Remove the BFA debugfs root directory */499 if (atomic_read(&bfa_debugfs_port_count) == 0) {500 debugfs_remove(bfa_debugfs_root);501 bfa_debugfs_root = NULL;502 }503}504