diff options
author | Dov Levenglick <dovl@codeaurora.org> | 2015-01-05 16:48:33 +0200 |
---|---|---|
committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-22 10:58:31 -0700 |
commit | 4229a8a3ed2c668e0b95e7791b422cd019ed6758 (patch) | |
tree | a65c4badfb53654b936cb1170676c167ecdab187 | |
parent | 7d17fba879bb36459d82844567380cd1745129d6 (diff) |
scsi: ufs-qcom: expose debug registers by debugfs
Add support for exposing debug registers via debugfs. This
can be used for runtime debugging of failures/errors without
the need to add more prints to the console.
In order to avoid unnecessary code duplication, the function
for dumping registers to a file (in debugfs.c) has been
slightly modified and exposed as an external function.
Signed-off-by: Dov Levenglick <dovl@codeaurora.org>
Change-Id: I77f9f3a6d041f805a93d095446f3e8077977036f
-rw-r--r-- | drivers/scsi/ufs/debugfs.c | 32 | ||||
-rw-r--r-- | drivers/scsi/ufs/debugfs.h | 8 | ||||
-rw-r--r-- | drivers/scsi/ufs/qcom-debugfs.c | 45 | ||||
-rw-r--r-- | drivers/scsi/ufs/ufs-qcom.c | 68 | ||||
-rw-r--r-- | include/linux/scsi/ufs/ufs-qcom.h | 4 |
5 files changed, 119 insertions, 38 deletions
diff --git a/drivers/scsi/ufs/debugfs.c b/drivers/scsi/ufs/debugfs.c index 4fd197cace5c..4ee7d8379602 100644 --- a/drivers/scsi/ufs/debugfs.c +++ b/drivers/scsi/ufs/debugfs.c @@ -181,7 +181,7 @@ static void ufsdbg_setup_fault_injection(struct ufs_hba *hba) } #endif /* CONFIG_UFS_FAULT_INJECTION */ -#define BUFF_LINE_CAPACITY 16 +#define BUFF_LINE_SIZE 16 /* Must be a multiplication of sizeof(u32) */ #define TAB_CHARS 8 static int ufsdbg_tag_stats_show(struct seq_file *file, void *data) @@ -436,20 +436,28 @@ exit: return ret; } -static void -ufsdbg_pr_buf_to_std(struct seq_file *file, void *buff, int size, char *str) +void ufsdbg_pr_buf_to_std(struct ufs_hba *hba, int offset, int num_regs, + char *str, void *priv) { int i; char linebuf[38]; - int lines = size/BUFF_LINE_CAPACITY + - (size % BUFF_LINE_CAPACITY ? 1 : 0); + int size = num_regs * sizeof(u32); + int lines = size / BUFF_LINE_SIZE + + (size % BUFF_LINE_SIZE ? 1 : 0); + struct seq_file *file = priv; + + if (!hba || !file) { + pr_err("%s called with NULL pointer\n", __func__); + return; + } for (i = 0; i < lines; i++) { - hex_dump_to_buffer(buff + i * BUFF_LINE_CAPACITY, - BUFF_LINE_CAPACITY, BUFF_LINE_CAPACITY, 4, + hex_dump_to_buffer(hba->mmio_base + offset + i * BUFF_LINE_SIZE, + min(BUFF_LINE_SIZE, size), BUFF_LINE_SIZE, 4, linebuf, sizeof(linebuf), false); - seq_printf(file, "%s [%x]: %s\n", str, i * BUFF_LINE_CAPACITY, + seq_printf(file, "%s [%x]: %s\n", str, i * BUFF_LINE_SIZE, linebuf); + size -= BUFF_LINE_SIZE/sizeof(u32); } } @@ -459,8 +467,8 @@ static int ufsdbg_host_regs_show(struct seq_file *file, void *data) ufshcd_hold(hba, false); pm_runtime_get_sync(hba->dev); - ufsdbg_pr_buf_to_std(file, hba->mmio_base, UFSHCI_REG_SPACE_SIZE, - "host regs"); + ufsdbg_pr_buf_to_std(hba, 0, UFSHCI_REG_SPACE_SIZE / sizeof(u32), + "host regs", file); pm_runtime_put_sync(hba->dev); ufshcd_release(hba, false); return 0; @@ -778,12 +786,12 @@ static ssize_t ufsdbg_power_mode_write(struct file *file, struct ufs_hba *hba = file->f_mapping->host->i_private; struct ufs_pa_layer_attr pwr_mode; struct ufs_pa_layer_attr final_pwr_mode; - char pwr_mode_str[BUFF_LINE_CAPACITY] = {0}; + char pwr_mode_str[BUFF_LINE_SIZE] = {0}; loff_t buff_pos = 0; int ret; int idx = 0; - ret = simple_write_to_buffer(pwr_mode_str, BUFF_LINE_CAPACITY, + ret = simple_write_to_buffer(pwr_mode_str, BUFF_LINE_SIZE, &buff_pos, ubuf, cnt); pwr_mode.gear_rx = pwr_mode_str[idx++] - '0'; diff --git a/drivers/scsi/ufs/debugfs.h b/drivers/scsi/ufs/debugfs.h index 0c481310921c..cf82be23fb47 100644 --- a/drivers/scsi/ufs/debugfs.h +++ b/drivers/scsi/ufs/debugfs.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -28,6 +28,8 @@ void ufsdbg_add_debugfs(struct ufs_hba *hba); void ufsdbg_remove_debugfs(struct ufs_hba *hba); void ufsdbg_fail_request(struct ufs_hba *hba, u32 *intr_status); +void ufsdbg_pr_buf_to_std(struct ufs_hba *hba, int offset, int num_regs, + char *str, void *priv); #else void ufsdbg_add_debugfs(struct ufs_hba *hba) { @@ -38,5 +40,9 @@ void ufsdbg_remove_debugfs(struct ufs_hba *hba) void ufsdbg_fail_request(struct ufs_hba *hba, u32 *intr_status) { } +void ufsdbg_pr_buf_to_std(struct ufs_hba *hba, int offset, int num_regs, + char *str, void *priv) +{ +} #endif #endif /* End of Header */ diff --git a/drivers/scsi/ufs/qcom-debugfs.c b/drivers/scsi/ufs/qcom-debugfs.c index 835ea46d88e5..4cfa5a5a3ed3 100644 --- a/drivers/scsi/ufs/qcom-debugfs.c +++ b/drivers/scsi/ufs/qcom-debugfs.c @@ -15,6 +15,7 @@ #include <linux/debugfs.h> #include <linux/scsi/ufs/ufs-qcom.h> #include "qcom-debugfs.h" +#include "debugfs.h" #define TESTBUS_CFG_BUFF_LINE_SIZE sizeof("0xXY, 0xXY") @@ -184,6 +185,39 @@ DEFINE_SIMPLE_ATTRIBUTE(ufs_qcom_dbg_testbus_bus_ops, NULL, "%llu\n"); +static int ufs_qcom_dbg_dbg_regs_show(struct seq_file *file, void *data) +{ + struct ufs_qcom_host *host = (struct ufs_qcom_host *)file->private; + bool dbg_print_reg = !!(host->dbg_print_en & + UFS_QCOM_DBG_PRINT_REGS_EN); + + ufshcd_hold(host->hba, false); + pm_runtime_get_sync(host->hba->dev); + + /* Temporarily override the debug print enable */ + host->dbg_print_en |= UFS_QCOM_DBG_PRINT_REGS_EN; + ufs_qcom_print_hw_debug_reg_all(host->hba, file, ufsdbg_pr_buf_to_std); + /* Restore previous debug print enable value */ + if (!dbg_print_reg) + host->dbg_print_en &= ~UFS_QCOM_DBG_PRINT_REGS_EN; + + pm_runtime_put_sync(host->hba->dev); + ufshcd_release(host->hba, false); + + return 0; +} + +static int ufs_qcom_dbg_dbg_regs_open(struct inode *inode, + struct file *file) +{ + return single_open(file, ufs_qcom_dbg_dbg_regs_show, + inode->i_private); +} + +static const struct file_operations ufs_qcom_dbg_dbg_regs_desc = { + .open = ufs_qcom_dbg_dbg_regs_open, + .read = seq_read, +}; void ufs_qcom_dbg_add_debugfs(struct ufs_hba *hba, struct dentry *root) { @@ -262,6 +296,17 @@ void ufs_qcom_dbg_add_debugfs(struct ufs_hba *hba, struct dentry *root) goto err; } + host->debugfs_files.dbg_regs = + debugfs_create_file("debug-regs", S_IRUSR, + host->debugfs_files.debugfs_root, host, + &ufs_qcom_dbg_dbg_regs_desc); + if (!host->debugfs_files.dbg_regs) { + dev_err(host->hba->dev, + "%s: failed create dbg_regs debugfs entry\n", + __func__); + goto err; + } + return; err: diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c index 482873d98c11..69077678a198 100644 --- a/drivers/scsi/ufs/ufs-qcom.c +++ b/drivers/scsi/ufs/ufs-qcom.c @@ -72,6 +72,12 @@ static void ufs_qcom_dump_regs(struct ufs_hba *hba, int offset, int len, 16, 4, hba->mmio_base + offset, len * 4, false); } +static void ufs_qcom_dump_regs_wrapper(struct ufs_hba *hba, int offset, int len, + char *prefix, void *priv) +{ + ufs_qcom_dump_regs(hba, offset, len, prefix); +} + static int ufs_qcom_get_connected_tx_lanes(struct ufs_hba *hba, u32 *tx_lanes) { int err = 0; @@ -1398,44 +1404,56 @@ out: return ret; } -static void ufs_qcom_print_hw_debug_reg_all(struct ufs_hba *hba) +void ufs_qcom_print_hw_debug_reg_all(struct ufs_hba *hba, void *priv, + void (*print_fn)(struct ufs_hba *hba, int offset, int num_regs, + char *str, void *priv)) { u32 reg; - struct ufs_qcom_host *host = hba->priv; + struct ufs_qcom_host *host; + + if (unlikely(!hba)) { + pr_err("%s: hba is NULL\n", __func__); + return; + } + if (unlikely(!print_fn)) { + dev_err(hba->dev, "%s: print_fn is NULL\n", __func__); + return; + } + host = hba->priv; if (!(host->dbg_print_en & UFS_QCOM_DBG_PRINT_REGS_EN)) return; - ufs_qcom_dump_regs(hba, UFS_UFS_DBG_RD_REG_OCSC, 44, - "UFS_UFS_DBG_RD_REG_OCSC "); + print_fn(hba, UFS_UFS_DBG_RD_REG_OCSC, 44, + "UFS_UFS_DBG_RD_REG_OCSC ", priv); reg = ufshcd_readl(hba, REG_UFS_CFG1); reg |= UFS_BIT(17); ufshcd_writel(hba, reg, REG_UFS_CFG1); - ufs_qcom_dump_regs(hba, UFS_UFS_DBG_RD_EDTL_RAM, 32, - "UFS_UFS_DBG_RD_EDTL_RAM "); - ufs_qcom_dump_regs(hba, UFS_UFS_DBG_RD_DESC_RAM, 128, - "UFS_UFS_DBG_RD_DESC_RAM "); - ufs_qcom_dump_regs(hba, UFS_UFS_DBG_RD_PRDT_RAM, 64, - "UFS_UFS_DBG_RD_PRDT_RAM "); + print_fn(hba, UFS_UFS_DBG_RD_EDTL_RAM, 32, + "UFS_UFS_DBG_RD_EDTL_RAM ", priv); + print_fn(hba, UFS_UFS_DBG_RD_DESC_RAM, 128, + "UFS_UFS_DBG_RD_DESC_RAM ", priv); + print_fn(hba, UFS_UFS_DBG_RD_PRDT_RAM, 64, + "UFS_UFS_DBG_RD_PRDT_RAM ", priv); ufshcd_writel(hba, (reg & ~UFS_BIT(17)), REG_UFS_CFG1); - ufs_qcom_dump_regs(hba, UFS_DBG_RD_REG_UAWM, 4, - "UFS_DBG_RD_REG_UAWM "); - ufs_qcom_dump_regs(hba, UFS_DBG_RD_REG_UARM, 4, - "UFS_DBG_RD_REG_UARM "); - ufs_qcom_dump_regs(hba, UFS_DBG_RD_REG_TXUC, 48, - "UFS_DBG_RD_REG_TXUC "); - ufs_qcom_dump_regs(hba, UFS_DBG_RD_REG_RXUC, 27, - "UFS_DBG_RD_REG_RXUC "); - ufs_qcom_dump_regs(hba, UFS_DBG_RD_REG_DFC, 19, - "UFS_DBG_RD_REG_DFC "); - ufs_qcom_dump_regs(hba, UFS_DBG_RD_REG_TRLUT, 34, - "UFS_DBG_RD_REG_TRLUT "); - ufs_qcom_dump_regs(hba, UFS_DBG_RD_REG_TMRLUT, 9, - "UFS_DBG_RD_REG_TMRLUT "); + print_fn(hba, UFS_DBG_RD_REG_UAWM, 4, + "UFS_DBG_RD_REG_UAWM ", priv); + print_fn(hba, UFS_DBG_RD_REG_UARM, 4, + "UFS_DBG_RD_REG_UARM ", priv); + print_fn(hba, UFS_DBG_RD_REG_TXUC, 48, + "UFS_DBG_RD_REG_TXUC ", priv); + print_fn(hba, UFS_DBG_RD_REG_RXUC, 27, + "UFS_DBG_RD_REG_RXUC ", priv); + print_fn(hba, UFS_DBG_RD_REG_DFC, 19, + "UFS_DBG_RD_REG_DFC ", priv); + print_fn(hba, UFS_DBG_RD_REG_TRLUT, 34, + "UFS_DBG_RD_REG_TRLUT ", priv); + print_fn(hba, UFS_DBG_RD_REG_TMRLUT, 9, + "UFS_DBG_RD_REG_TMRLUT ", priv); } static void ufs_qcom_enable_test_bus(struct ufs_qcom_host *host) @@ -1574,7 +1592,7 @@ static void ufs_qcom_dump_dbg_regs(struct ufs_hba *hba) ufs_qcom_dump_regs(hba, REG_UFS_SYS1CLK_1US, 16, "HCI Vendor Specific Registers "); - ufs_qcom_print_hw_debug_reg_all(hba); + ufs_qcom_print_hw_debug_reg_all(hba, NULL, ufs_qcom_dump_regs_wrapper); ufs_qcom_testbus_read(hba); ufs_qcom_ice_print_regs(host); } diff --git a/include/linux/scsi/ufs/ufs-qcom.h b/include/linux/scsi/ufs/ufs-qcom.h index c2df38d4f559..fc8b2c98f191 100644 --- a/include/linux/scsi/ufs/ufs-qcom.h +++ b/include/linux/scsi/ufs/ufs-qcom.h @@ -218,6 +218,7 @@ struct qcom_debugfs_files { struct dentry *testbus_en; struct dentry *testbus_cfg; struct dentry *testbus_bus; + struct dentry *dbg_regs; }; #endif @@ -263,6 +264,9 @@ struct ufs_qcom_host { #define ufs_qcom_is_link_hibern8(hba) ufshcd_is_link_hibern8(hba) int ufs_qcom_testbus_config(struct ufs_qcom_host *host); +void ufs_qcom_print_hw_debug_reg_all(struct ufs_hba *hba, void *priv, + void (*print_fn)(struct ufs_hba *hba, int offset, int num_regs, + char *str, void *priv)); #define MAX_PROP_NAME 32 #define VDDA_PHY_MIN_UV 1000000 |