Loading drivers/scsi/ufs/qcom-debugfs.c +179 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ #include <linux/scsi/ufs/ufs-qcom.h> #include "qcom-debugfs.h" #define TESTBUS_CFG_BUFF_LINE_SIZE sizeof("0xXY, 0xXY") static void ufs_qcom_dbg_remove_debugfs(struct ufs_qcom_host *host); static int ufs_qcom_dbg_print_en_read(void *data, u64 *attr_val) Loading Loading @@ -48,6 +50,140 @@ DEFINE_SIMPLE_ATTRIBUTE(ufs_qcom_dbg_print_en_ops, ufs_qcom_dbg_print_en_set, "%llu\n"); static int ufs_qcom_dbg_testbus_en_read(void *data, u64 *attr_val) { struct ufs_qcom_host *host = data; bool enabled; if (!host) return -EINVAL; enabled = !!(host->dbg_print_en & UFS_QCOM_DBG_PRINT_TEST_BUS_EN); *attr_val = (u64)enabled; return 0; } static int ufs_qcom_dbg_testbus_en_set(void *data, u64 attr_id) { struct ufs_qcom_host *host = data; if (!host) return -EINVAL; if (!!attr_id) host->dbg_print_en |= UFS_QCOM_DBG_PRINT_TEST_BUS_EN; else host->dbg_print_en &= ~UFS_QCOM_DBG_PRINT_TEST_BUS_EN; return ufs_qcom_testbus_config(host); } DEFINE_SIMPLE_ATTRIBUTE(ufs_qcom_dbg_testbus_en_ops, ufs_qcom_dbg_testbus_en_read, ufs_qcom_dbg_testbus_en_set, "%llu\n"); static int ufs_qcom_dbg_testbus_cfg_show(struct seq_file *file, void *data) { struct ufs_qcom_host *host = (struct ufs_qcom_host *)file->private; seq_printf(file , "Current configuration: major=%d, minor=%d\n\n", host->testbus.select_major, host->testbus.select_minor); /* Print usage */ seq_puts(file, "To change the test-bus configuration, write 'MAJ,MIN' where:\n" "MAJ - major select\n" "MIN - minor select\n\n"); return 0; } static ssize_t ufs_qcom_dbg_testbus_cfg_write(struct file *file, const char __user *ubuf, size_t cnt, loff_t *ppos) { struct ufs_qcom_host *host = file->f_mapping->host->i_private; char configuration[TESTBUS_CFG_BUFF_LINE_SIZE] = {0}; loff_t buff_pos = 0; char *comma; int ret = 0; int major; int minor; cnt = simple_write_to_buffer(configuration, TESTBUS_CFG_BUFF_LINE_SIZE, &buff_pos, ubuf, cnt); if (cnt < 0) { dev_err(host->hba->dev, "%s: failed to read user data\n", __func__); goto out; } comma = strnchr(configuration, TESTBUS_CFG_BUFF_LINE_SIZE, ','); if (!comma || comma == configuration) { dev_err(host->hba->dev, "%s: error in configuration of testbus\n", __func__); ret = -EINVAL; goto out; } if (sscanf(configuration, "%i,%i", &major, &minor) != 2) { dev_err(host->hba->dev, "%s: couldn't parse input to 2 numeric values\n", __func__); ret = -EINVAL; goto out; } host->testbus.select_major = (u8)major; host->testbus.select_minor = (u8)minor; /* * Sanity check of the {major, minor} tuple is done in the * config function */ ret = ufs_qcom_testbus_config(host); if (!ret) dev_dbg(host->hba->dev, "%s: New configuration: major=%d, minor=%d\n", __func__, host->testbus.select_major, host->testbus.select_minor); out: return ret ? ret : cnt; } static int ufs_qcom_dbg_testbus_cfg_open(struct inode *inode, struct file *file) { return single_open(file, ufs_qcom_dbg_testbus_cfg_show, inode->i_private); } static const struct file_operations ufs_qcom_dbg_testbus_cfg_desc = { .open = ufs_qcom_dbg_testbus_cfg_open, .read = seq_read, .write = ufs_qcom_dbg_testbus_cfg_write, }; static int ufs_qcom_dbg_testbus_bus_read(void *data, u64 *attr_val) { struct ufs_qcom_host *host = data; if (!host) return -EINVAL; ufshcd_hold(host->hba, false); pm_runtime_get_sync(host->hba->dev); *attr_val = (u64)ufshcd_readl(host->hba, UFS_TEST_BUS); pm_runtime_put_sync(host->hba->dev); ufshcd_release(host->hba, false); return 0; } DEFINE_SIMPLE_ATTRIBUTE(ufs_qcom_dbg_testbus_bus_ops, ufs_qcom_dbg_testbus_bus_read, NULL, "%llu\n"); void ufs_qcom_dbg_add_debugfs(struct ufs_hba *hba, struct dentry *root) { Loading Loading @@ -83,6 +219,49 @@ void ufs_qcom_dbg_add_debugfs(struct ufs_hba *hba, struct dentry *root) __func__); goto err; } host->debugfs_files.testbus = debugfs_create_dir("testbus", host->debugfs_files.debugfs_root); if (!host->debugfs_files.testbus) { dev_err(host->hba->dev, "%s: failed create testbus directory\n", __func__); goto err; } host->debugfs_files.testbus_en = debugfs_create_file("enable", S_IRUSR | S_IWUSR, host->debugfs_files.testbus, host, &ufs_qcom_dbg_testbus_en_ops); if (!host->debugfs_files.testbus_en) { dev_err(host->hba->dev, "%s: failed create testbus_en debugfs entry\n", __func__); goto err; } host->debugfs_files.testbus_cfg = debugfs_create_file("configuration", S_IRUSR | S_IWUSR, host->debugfs_files.testbus, host, &ufs_qcom_dbg_testbus_cfg_desc); if (!host->debugfs_files.testbus_cfg) { dev_err(host->hba->dev, "%s: failed create testbus_cfg debugfs entry\n", __func__); goto err; } host->debugfs_files.testbus_bus = debugfs_create_file("TEST_BUS", S_IRUSR, host->debugfs_files.testbus, host, &ufs_qcom_dbg_testbus_bus_ops); if (!host->debugfs_files.testbus_bus) { dev_err(host->hba->dev, "%s: failed create testbus_bus debugfs entry\n", __func__); goto err; } return; err: Loading drivers/scsi/ufs/ufs-qcom.c +157 −2 Original line number Diff line number Diff line Loading @@ -32,7 +32,24 @@ #include "ufs-qcom-ice.h" #include "qcom-debugfs.h" #define DEFAULT_UFS_QCOM_DBG_PRINT_EN UFS_QCOM_DBG_PRINT_REGS_EN #define UFS_QCOM_DEFAULT_DBG_PRINT_EN \ (UFS_QCOM_DBG_PRINT_REGS_EN | UFS_QCOM_DBG_PRINT_TEST_BUS_EN) enum { TSTBUS_UAWM, TSTBUS_UARM, TSTBUS_TXUC, TSTBUS_RXUC, TSTBUS_DFC, TSTBUS_TRLUT, TSTBUS_TMRLUT, TSTBUS_OCSC, TSTBUS_UTP_HCI, TSTBUS_COMBINED, TSTBUS_WRAPPER, TSTBUS_UNIPRO, TSTBUS_MAX, }; static struct ufs_qcom_host *ufs_qcom_hosts[MAX_UFS_QCOM_HOSTS]; Loading @@ -41,6 +58,7 @@ static int ufs_qcom_get_bus_vote(struct ufs_qcom_host *host, const char *speed_mode); static int ufs_qcom_set_bus_vote(struct ufs_qcom_host *host, int vote); static int ufs_qcom_update_sec_cfg(struct ufs_hba *hba, bool restore_sec_cfg); static void ufs_qcom_get_default_testbus_cfg(struct ufs_qcom_host *host); static void ufs_qcom_dump_regs(struct ufs_hba *hba, int offset, int len, char *prefix) Loading Loading @@ -1262,7 +1280,14 @@ static int ufs_qcom_init(struct ufs_hba *hba) if (hba->dev->id < MAX_UFS_QCOM_HOSTS) ufs_qcom_hosts[hba->dev->id] = host; host->dbg_print_en |= DEFAULT_UFS_QCOM_DBG_PRINT_EN; host->dbg_print_en |= UFS_QCOM_DEFAULT_DBG_PRINT_EN; ufs_qcom_get_default_testbus_cfg(host); err = ufs_qcom_testbus_config(host); if (err) { dev_warn(dev, "%s: failed to configure the testbus %d\n", __func__, err); err = 0; } goto out; Loading Loading @@ -1404,6 +1429,135 @@ static void ufs_qcom_print_hw_debug_reg_all(struct ufs_hba *hba) "UFS_DBG_RD_REG_TMRLUT "); } static void ufs_qcom_enable_test_bus(struct ufs_qcom_host *host) { if (host->dbg_print_en & UFS_QCOM_DBG_PRINT_TEST_BUS_EN) ufshcd_rmwl(host->hba, TEST_BUS_EN, TEST_BUS_EN, REG_UFS_CFG1); else ufshcd_rmwl(host->hba, TEST_BUS_EN, 0, REG_UFS_CFG1); } static void ufs_qcom_get_default_testbus_cfg(struct ufs_qcom_host *host) { /* provide a legal default configuration */ host->testbus.select_major = TSTBUS_UAWM; host->testbus.select_minor = 1; } static bool ufs_qcom_testbus_cfg_is_ok(struct ufs_qcom_host *host) { if (host->testbus.select_major >= TSTBUS_MAX) { dev_err(host->hba->dev, "%s: UFS_CFG1[TEST_BUS_SEL} may not equal 0x%05X\n", __func__, host->testbus.select_major); return false; } /* * Not performing check for each individual select_major * mappings of select_minor, since there is no harm in * configuring a non-existent select_minor */ if (host->testbus.select_major > 0x1F) { dev_err(host->hba->dev, "%s: 0x%05X is not a legal testbus option\n", __func__, host->testbus.select_minor); return false; } return true; } int ufs_qcom_testbus_config(struct ufs_qcom_host *host) { int reg; int offset; u32 mask = TEST_BUS_SUB_SEL_MASK; if (!host) return -EINVAL; if (!ufs_qcom_testbus_cfg_is_ok(host)) return -EPERM; switch (host->testbus.select_major) { case TSTBUS_UAWM: reg = UFS_TEST_BUS_CTRL_0; offset = 24; break; case TSTBUS_UARM: reg = UFS_TEST_BUS_CTRL_0; offset = 16; break; case TSTBUS_TXUC: reg = UFS_TEST_BUS_CTRL_0; offset = 8; break; case TSTBUS_RXUC: reg = UFS_TEST_BUS_CTRL_0; offset = 0; break; case TSTBUS_DFC: reg = UFS_TEST_BUS_CTRL_1; offset = 24; break; case TSTBUS_TRLUT: reg = UFS_TEST_BUS_CTRL_1; offset = 16; break; case TSTBUS_TMRLUT: reg = UFS_TEST_BUS_CTRL_1; offset = 8; break; case TSTBUS_OCSC: reg = UFS_TEST_BUS_CTRL_1; offset = 0; break; case TSTBUS_WRAPPER: reg = UFS_TEST_BUS_CTRL_2; offset = 16; break; case TSTBUS_COMBINED: reg = UFS_TEST_BUS_CTRL_2; offset = 8; break; case TSTBUS_UTP_HCI: reg = UFS_TEST_BUS_CTRL_2; offset = 0; break; case TSTBUS_UNIPRO: reg = UFS_UNIPRO_CFG; offset = 1; break; /* * No need for a default case, since * ufs_qcom_testbus_cfg_is_ok() checks that the configuration * is legal */ } mask <<= offset; ufshcd_hold(host->hba, false); pm_runtime_get_sync(host->hba->dev); ufshcd_rmwl(host->hba, TEST_BUS_SEL, (u32)host->testbus.select_major << 19, REG_UFS_CFG1); ufshcd_rmwl(host->hba, mask, (u32)host->testbus.select_minor << offset, reg); ufs_qcom_enable_test_bus(host); pm_runtime_put_sync(host->hba->dev); ufshcd_release(host->hba, false); return 0; } static void ufs_qcom_testbus_read(struct ufs_hba *hba) { ufs_qcom_dump_regs(hba, UFS_TEST_BUS, 1, "UFS_TEST_BUS "); } static void ufs_qcom_dump_dbg_regs(struct ufs_hba *hba) { struct ufs_qcom_host *host = hba->priv; Loading @@ -1412,6 +1566,7 @@ static void ufs_qcom_dump_dbg_regs(struct ufs_hba *hba) "HCI Vendor Specific Registers "); ufs_qcom_print_hw_debug_reg_all(hba); ufs_qcom_testbus_read(hba); ufs_qcom_ice_print_regs(host); } Loading include/linux/scsi/ufs/ufs-qcom.h +28 −2 Original line number Diff line number Diff line Loading @@ -61,6 +61,12 @@ enum { REG_UFS_CFG2 = 0xE0, REG_UFS_HW_VERSION = 0xE4, UFS_TEST_BUS = 0xE8, UFS_TEST_BUS_CTRL_0 = 0xEC, UFS_TEST_BUS_CTRL_1 = 0xF0, UFS_TEST_BUS_CTRL_2 = 0xF4, UFS_UNIPRO_CFG = 0xF8, UFS_DBG_RD_REG_UAWM = 0x100, UFS_DBG_RD_REG_UARM = 0x200, UFS_DBG_RD_REG_TXUC = 0x300, Loading @@ -76,6 +82,10 @@ enum { UFS_UFS_DBG_RD_EDTL_RAM = 0x1900, }; /* bit definitions for REG_UFS_CFG1 register */ #define TEST_BUS_EN BIT(18) #define TEST_BUS_SEL GENMASK(22, 19) /* bit definitions for REG_UFS_CFG2 register */ #define UAWM_HW_CGC_EN (1 << 0) #define UARM_HW_CGC_EN (1 << 1) Loading @@ -86,6 +96,9 @@ enum { #define TMRLUT_HW_CGC_EN (1 << 6) #define OCSC_HW_CGC_EN (1 << 7) /* bit definition for UFS_UFS_TEST_BUS_CTRL_n */ #define TEST_BUS_SUB_SEL_MASK 0x1F /* All XXX_SEL fields are 5 bits wide */ #define REG_UFS_CFG2_CGC_EN_ALL (UAWM_HW_CGC_EN | UARM_HW_CGC_EN |\ TXUC_HW_CGC_EN | RXUC_HW_CGC_EN |\ DFC_HW_CGC_EN | TRLUT_HW_CGC_EN |\ Loading Loading @@ -122,10 +135,11 @@ struct ufs_qcom_phy_vreg { /* QCOM UFS debug print bit mask */ #define UFS_QCOM_DBG_PRINT_REGS_EN BIT(0) #define UFS_QCOM_DBG_PRINT_ICE_REGS_EN BIT(1) #define UFS_QCOM_DBG_PRINT_TEST_BUS_EN BIT(2) #define UFS_QCOM_DBG_PRINT_ALL \ (UFS_QCOM_DBG_PRINT_REGS_EN | UFS_QCOM_DBG_PRINT_ICE_REGS_EN) (UFS_QCOM_DBG_PRINT_REGS_EN | UFS_QCOM_DBG_PRINT_ICE_REGS_EN | \ UFS_QCOM_DBG_PRINT_TEST_BUS_EN) static inline void ufs_qcom_get_controller_revision(struct ufs_hba *hba, Loading Loading @@ -200,9 +214,18 @@ struct ufs_hw_version { struct qcom_debugfs_files { struct dentry *debugfs_root; struct dentry *dbg_print_en; struct dentry *testbus; struct dentry *testbus_en; struct dentry *testbus_cfg; struct dentry *testbus_bus; }; #endif struct ufs_qcom_testbus { u8 select_major; u8 select_minor; }; struct ufs_qcom_host { struct phy *generic_phy; struct ufs_hba *hba; Loading @@ -223,12 +246,15 @@ struct ufs_qcom_host { #endif /* Bitmask for enabling debug prints */ u32 dbg_print_en; struct ufs_qcom_testbus testbus; }; #define ufs_qcom_is_link_off(hba) ufshcd_is_link_off(hba) #define ufs_qcom_is_link_active(hba) ufshcd_is_link_active(hba) #define ufs_qcom_is_link_hibern8(hba) ufshcd_is_link_hibern8(hba) int ufs_qcom_testbus_config(struct ufs_qcom_host *host); #define MAX_PROP_NAME 32 #define VDDA_PHY_MIN_UV 1000000 #define VDDA_PHY_MAX_UV 1000000 Loading Loading
drivers/scsi/ufs/qcom-debugfs.c +179 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ #include <linux/scsi/ufs/ufs-qcom.h> #include "qcom-debugfs.h" #define TESTBUS_CFG_BUFF_LINE_SIZE sizeof("0xXY, 0xXY") static void ufs_qcom_dbg_remove_debugfs(struct ufs_qcom_host *host); static int ufs_qcom_dbg_print_en_read(void *data, u64 *attr_val) Loading Loading @@ -48,6 +50,140 @@ DEFINE_SIMPLE_ATTRIBUTE(ufs_qcom_dbg_print_en_ops, ufs_qcom_dbg_print_en_set, "%llu\n"); static int ufs_qcom_dbg_testbus_en_read(void *data, u64 *attr_val) { struct ufs_qcom_host *host = data; bool enabled; if (!host) return -EINVAL; enabled = !!(host->dbg_print_en & UFS_QCOM_DBG_PRINT_TEST_BUS_EN); *attr_val = (u64)enabled; return 0; } static int ufs_qcom_dbg_testbus_en_set(void *data, u64 attr_id) { struct ufs_qcom_host *host = data; if (!host) return -EINVAL; if (!!attr_id) host->dbg_print_en |= UFS_QCOM_DBG_PRINT_TEST_BUS_EN; else host->dbg_print_en &= ~UFS_QCOM_DBG_PRINT_TEST_BUS_EN; return ufs_qcom_testbus_config(host); } DEFINE_SIMPLE_ATTRIBUTE(ufs_qcom_dbg_testbus_en_ops, ufs_qcom_dbg_testbus_en_read, ufs_qcom_dbg_testbus_en_set, "%llu\n"); static int ufs_qcom_dbg_testbus_cfg_show(struct seq_file *file, void *data) { struct ufs_qcom_host *host = (struct ufs_qcom_host *)file->private; seq_printf(file , "Current configuration: major=%d, minor=%d\n\n", host->testbus.select_major, host->testbus.select_minor); /* Print usage */ seq_puts(file, "To change the test-bus configuration, write 'MAJ,MIN' where:\n" "MAJ - major select\n" "MIN - minor select\n\n"); return 0; } static ssize_t ufs_qcom_dbg_testbus_cfg_write(struct file *file, const char __user *ubuf, size_t cnt, loff_t *ppos) { struct ufs_qcom_host *host = file->f_mapping->host->i_private; char configuration[TESTBUS_CFG_BUFF_LINE_SIZE] = {0}; loff_t buff_pos = 0; char *comma; int ret = 0; int major; int minor; cnt = simple_write_to_buffer(configuration, TESTBUS_CFG_BUFF_LINE_SIZE, &buff_pos, ubuf, cnt); if (cnt < 0) { dev_err(host->hba->dev, "%s: failed to read user data\n", __func__); goto out; } comma = strnchr(configuration, TESTBUS_CFG_BUFF_LINE_SIZE, ','); if (!comma || comma == configuration) { dev_err(host->hba->dev, "%s: error in configuration of testbus\n", __func__); ret = -EINVAL; goto out; } if (sscanf(configuration, "%i,%i", &major, &minor) != 2) { dev_err(host->hba->dev, "%s: couldn't parse input to 2 numeric values\n", __func__); ret = -EINVAL; goto out; } host->testbus.select_major = (u8)major; host->testbus.select_minor = (u8)minor; /* * Sanity check of the {major, minor} tuple is done in the * config function */ ret = ufs_qcom_testbus_config(host); if (!ret) dev_dbg(host->hba->dev, "%s: New configuration: major=%d, minor=%d\n", __func__, host->testbus.select_major, host->testbus.select_minor); out: return ret ? ret : cnt; } static int ufs_qcom_dbg_testbus_cfg_open(struct inode *inode, struct file *file) { return single_open(file, ufs_qcom_dbg_testbus_cfg_show, inode->i_private); } static const struct file_operations ufs_qcom_dbg_testbus_cfg_desc = { .open = ufs_qcom_dbg_testbus_cfg_open, .read = seq_read, .write = ufs_qcom_dbg_testbus_cfg_write, }; static int ufs_qcom_dbg_testbus_bus_read(void *data, u64 *attr_val) { struct ufs_qcom_host *host = data; if (!host) return -EINVAL; ufshcd_hold(host->hba, false); pm_runtime_get_sync(host->hba->dev); *attr_val = (u64)ufshcd_readl(host->hba, UFS_TEST_BUS); pm_runtime_put_sync(host->hba->dev); ufshcd_release(host->hba, false); return 0; } DEFINE_SIMPLE_ATTRIBUTE(ufs_qcom_dbg_testbus_bus_ops, ufs_qcom_dbg_testbus_bus_read, NULL, "%llu\n"); void ufs_qcom_dbg_add_debugfs(struct ufs_hba *hba, struct dentry *root) { Loading Loading @@ -83,6 +219,49 @@ void ufs_qcom_dbg_add_debugfs(struct ufs_hba *hba, struct dentry *root) __func__); goto err; } host->debugfs_files.testbus = debugfs_create_dir("testbus", host->debugfs_files.debugfs_root); if (!host->debugfs_files.testbus) { dev_err(host->hba->dev, "%s: failed create testbus directory\n", __func__); goto err; } host->debugfs_files.testbus_en = debugfs_create_file("enable", S_IRUSR | S_IWUSR, host->debugfs_files.testbus, host, &ufs_qcom_dbg_testbus_en_ops); if (!host->debugfs_files.testbus_en) { dev_err(host->hba->dev, "%s: failed create testbus_en debugfs entry\n", __func__); goto err; } host->debugfs_files.testbus_cfg = debugfs_create_file("configuration", S_IRUSR | S_IWUSR, host->debugfs_files.testbus, host, &ufs_qcom_dbg_testbus_cfg_desc); if (!host->debugfs_files.testbus_cfg) { dev_err(host->hba->dev, "%s: failed create testbus_cfg debugfs entry\n", __func__); goto err; } host->debugfs_files.testbus_bus = debugfs_create_file("TEST_BUS", S_IRUSR, host->debugfs_files.testbus, host, &ufs_qcom_dbg_testbus_bus_ops); if (!host->debugfs_files.testbus_bus) { dev_err(host->hba->dev, "%s: failed create testbus_bus debugfs entry\n", __func__); goto err; } return; err: Loading
drivers/scsi/ufs/ufs-qcom.c +157 −2 Original line number Diff line number Diff line Loading @@ -32,7 +32,24 @@ #include "ufs-qcom-ice.h" #include "qcom-debugfs.h" #define DEFAULT_UFS_QCOM_DBG_PRINT_EN UFS_QCOM_DBG_PRINT_REGS_EN #define UFS_QCOM_DEFAULT_DBG_PRINT_EN \ (UFS_QCOM_DBG_PRINT_REGS_EN | UFS_QCOM_DBG_PRINT_TEST_BUS_EN) enum { TSTBUS_UAWM, TSTBUS_UARM, TSTBUS_TXUC, TSTBUS_RXUC, TSTBUS_DFC, TSTBUS_TRLUT, TSTBUS_TMRLUT, TSTBUS_OCSC, TSTBUS_UTP_HCI, TSTBUS_COMBINED, TSTBUS_WRAPPER, TSTBUS_UNIPRO, TSTBUS_MAX, }; static struct ufs_qcom_host *ufs_qcom_hosts[MAX_UFS_QCOM_HOSTS]; Loading @@ -41,6 +58,7 @@ static int ufs_qcom_get_bus_vote(struct ufs_qcom_host *host, const char *speed_mode); static int ufs_qcom_set_bus_vote(struct ufs_qcom_host *host, int vote); static int ufs_qcom_update_sec_cfg(struct ufs_hba *hba, bool restore_sec_cfg); static void ufs_qcom_get_default_testbus_cfg(struct ufs_qcom_host *host); static void ufs_qcom_dump_regs(struct ufs_hba *hba, int offset, int len, char *prefix) Loading Loading @@ -1262,7 +1280,14 @@ static int ufs_qcom_init(struct ufs_hba *hba) if (hba->dev->id < MAX_UFS_QCOM_HOSTS) ufs_qcom_hosts[hba->dev->id] = host; host->dbg_print_en |= DEFAULT_UFS_QCOM_DBG_PRINT_EN; host->dbg_print_en |= UFS_QCOM_DEFAULT_DBG_PRINT_EN; ufs_qcom_get_default_testbus_cfg(host); err = ufs_qcom_testbus_config(host); if (err) { dev_warn(dev, "%s: failed to configure the testbus %d\n", __func__, err); err = 0; } goto out; Loading Loading @@ -1404,6 +1429,135 @@ static void ufs_qcom_print_hw_debug_reg_all(struct ufs_hba *hba) "UFS_DBG_RD_REG_TMRLUT "); } static void ufs_qcom_enable_test_bus(struct ufs_qcom_host *host) { if (host->dbg_print_en & UFS_QCOM_DBG_PRINT_TEST_BUS_EN) ufshcd_rmwl(host->hba, TEST_BUS_EN, TEST_BUS_EN, REG_UFS_CFG1); else ufshcd_rmwl(host->hba, TEST_BUS_EN, 0, REG_UFS_CFG1); } static void ufs_qcom_get_default_testbus_cfg(struct ufs_qcom_host *host) { /* provide a legal default configuration */ host->testbus.select_major = TSTBUS_UAWM; host->testbus.select_minor = 1; } static bool ufs_qcom_testbus_cfg_is_ok(struct ufs_qcom_host *host) { if (host->testbus.select_major >= TSTBUS_MAX) { dev_err(host->hba->dev, "%s: UFS_CFG1[TEST_BUS_SEL} may not equal 0x%05X\n", __func__, host->testbus.select_major); return false; } /* * Not performing check for each individual select_major * mappings of select_minor, since there is no harm in * configuring a non-existent select_minor */ if (host->testbus.select_major > 0x1F) { dev_err(host->hba->dev, "%s: 0x%05X is not a legal testbus option\n", __func__, host->testbus.select_minor); return false; } return true; } int ufs_qcom_testbus_config(struct ufs_qcom_host *host) { int reg; int offset; u32 mask = TEST_BUS_SUB_SEL_MASK; if (!host) return -EINVAL; if (!ufs_qcom_testbus_cfg_is_ok(host)) return -EPERM; switch (host->testbus.select_major) { case TSTBUS_UAWM: reg = UFS_TEST_BUS_CTRL_0; offset = 24; break; case TSTBUS_UARM: reg = UFS_TEST_BUS_CTRL_0; offset = 16; break; case TSTBUS_TXUC: reg = UFS_TEST_BUS_CTRL_0; offset = 8; break; case TSTBUS_RXUC: reg = UFS_TEST_BUS_CTRL_0; offset = 0; break; case TSTBUS_DFC: reg = UFS_TEST_BUS_CTRL_1; offset = 24; break; case TSTBUS_TRLUT: reg = UFS_TEST_BUS_CTRL_1; offset = 16; break; case TSTBUS_TMRLUT: reg = UFS_TEST_BUS_CTRL_1; offset = 8; break; case TSTBUS_OCSC: reg = UFS_TEST_BUS_CTRL_1; offset = 0; break; case TSTBUS_WRAPPER: reg = UFS_TEST_BUS_CTRL_2; offset = 16; break; case TSTBUS_COMBINED: reg = UFS_TEST_BUS_CTRL_2; offset = 8; break; case TSTBUS_UTP_HCI: reg = UFS_TEST_BUS_CTRL_2; offset = 0; break; case TSTBUS_UNIPRO: reg = UFS_UNIPRO_CFG; offset = 1; break; /* * No need for a default case, since * ufs_qcom_testbus_cfg_is_ok() checks that the configuration * is legal */ } mask <<= offset; ufshcd_hold(host->hba, false); pm_runtime_get_sync(host->hba->dev); ufshcd_rmwl(host->hba, TEST_BUS_SEL, (u32)host->testbus.select_major << 19, REG_UFS_CFG1); ufshcd_rmwl(host->hba, mask, (u32)host->testbus.select_minor << offset, reg); ufs_qcom_enable_test_bus(host); pm_runtime_put_sync(host->hba->dev); ufshcd_release(host->hba, false); return 0; } static void ufs_qcom_testbus_read(struct ufs_hba *hba) { ufs_qcom_dump_regs(hba, UFS_TEST_BUS, 1, "UFS_TEST_BUS "); } static void ufs_qcom_dump_dbg_regs(struct ufs_hba *hba) { struct ufs_qcom_host *host = hba->priv; Loading @@ -1412,6 +1566,7 @@ static void ufs_qcom_dump_dbg_regs(struct ufs_hba *hba) "HCI Vendor Specific Registers "); ufs_qcom_print_hw_debug_reg_all(hba); ufs_qcom_testbus_read(hba); ufs_qcom_ice_print_regs(host); } Loading
include/linux/scsi/ufs/ufs-qcom.h +28 −2 Original line number Diff line number Diff line Loading @@ -61,6 +61,12 @@ enum { REG_UFS_CFG2 = 0xE0, REG_UFS_HW_VERSION = 0xE4, UFS_TEST_BUS = 0xE8, UFS_TEST_BUS_CTRL_0 = 0xEC, UFS_TEST_BUS_CTRL_1 = 0xF0, UFS_TEST_BUS_CTRL_2 = 0xF4, UFS_UNIPRO_CFG = 0xF8, UFS_DBG_RD_REG_UAWM = 0x100, UFS_DBG_RD_REG_UARM = 0x200, UFS_DBG_RD_REG_TXUC = 0x300, Loading @@ -76,6 +82,10 @@ enum { UFS_UFS_DBG_RD_EDTL_RAM = 0x1900, }; /* bit definitions for REG_UFS_CFG1 register */ #define TEST_BUS_EN BIT(18) #define TEST_BUS_SEL GENMASK(22, 19) /* bit definitions for REG_UFS_CFG2 register */ #define UAWM_HW_CGC_EN (1 << 0) #define UARM_HW_CGC_EN (1 << 1) Loading @@ -86,6 +96,9 @@ enum { #define TMRLUT_HW_CGC_EN (1 << 6) #define OCSC_HW_CGC_EN (1 << 7) /* bit definition for UFS_UFS_TEST_BUS_CTRL_n */ #define TEST_BUS_SUB_SEL_MASK 0x1F /* All XXX_SEL fields are 5 bits wide */ #define REG_UFS_CFG2_CGC_EN_ALL (UAWM_HW_CGC_EN | UARM_HW_CGC_EN |\ TXUC_HW_CGC_EN | RXUC_HW_CGC_EN |\ DFC_HW_CGC_EN | TRLUT_HW_CGC_EN |\ Loading Loading @@ -122,10 +135,11 @@ struct ufs_qcom_phy_vreg { /* QCOM UFS debug print bit mask */ #define UFS_QCOM_DBG_PRINT_REGS_EN BIT(0) #define UFS_QCOM_DBG_PRINT_ICE_REGS_EN BIT(1) #define UFS_QCOM_DBG_PRINT_TEST_BUS_EN BIT(2) #define UFS_QCOM_DBG_PRINT_ALL \ (UFS_QCOM_DBG_PRINT_REGS_EN | UFS_QCOM_DBG_PRINT_ICE_REGS_EN) (UFS_QCOM_DBG_PRINT_REGS_EN | UFS_QCOM_DBG_PRINT_ICE_REGS_EN | \ UFS_QCOM_DBG_PRINT_TEST_BUS_EN) static inline void ufs_qcom_get_controller_revision(struct ufs_hba *hba, Loading Loading @@ -200,9 +214,18 @@ struct ufs_hw_version { struct qcom_debugfs_files { struct dentry *debugfs_root; struct dentry *dbg_print_en; struct dentry *testbus; struct dentry *testbus_en; struct dentry *testbus_cfg; struct dentry *testbus_bus; }; #endif struct ufs_qcom_testbus { u8 select_major; u8 select_minor; }; struct ufs_qcom_host { struct phy *generic_phy; struct ufs_hba *hba; Loading @@ -223,12 +246,15 @@ struct ufs_qcom_host { #endif /* Bitmask for enabling debug prints */ u32 dbg_print_en; struct ufs_qcom_testbus testbus; }; #define ufs_qcom_is_link_off(hba) ufshcd_is_link_off(hba) #define ufs_qcom_is_link_active(hba) ufshcd_is_link_active(hba) #define ufs_qcom_is_link_hibern8(hba) ufshcd_is_link_hibern8(hba) int ufs_qcom_testbus_config(struct ufs_qcom_host *host); #define MAX_PROP_NAME 32 #define VDDA_PHY_MIN_UV 1000000 #define VDDA_PHY_MAX_UV 1000000 Loading