Adds support for configuring and reading the test bus and debug
registers. This change also adds another vops in order to print the
debug registers.

Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>

---
 drivers/scsi/ufs/ufs-qcom.c | 165 +++++++++++++++++++++++++++++++++++++++++++-
 drivers/scsi/ufs/ufs-qcom.h |  37 +++++++++-
 drivers/scsi/ufs/ufshcd.c   |   2 +
 drivers/scsi/ufs/ufshcd.h   |   8 +++
 4 files changed, 208 insertions(+), 4 deletions(-)

diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index 8027435..4d19c49 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -23,6 +23,24 @@
 #include "unipro.h"
 #include "ufs-qcom.h"
 #include "ufshci.h"
+#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];
 
@@ -30,6 +48,15 @@ static void ufs_qcom_get_speed_mode(struct ufs_pa_layer_attr 
*p, char *result);
 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 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)
+{
+       print_hex_dump(KERN_ERR, prefix,
+                       len > 4 ? DUMP_PREFIX_OFFSET : DUMP_PREFIX_NONE,
+                       16, 4, (void __force *)hba->mmio_base + offset,
+                       len * 4, false);
+}
 
 static int ufs_qcom_get_connected_tx_lanes(struct ufs_hba *hba, u32 *tx_lanes)
 {
@@ -996,6 +1023,15 @@ 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 |= 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;
 
 out_disable_phy:
@@ -1025,12 +1061,134 @@ void ufs_qcom_clk_scale_notify(struct ufs_hba *hba)
 
        if (!dev_req_params)
                return;
+}
+
+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_minor > 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;
 
-       ufs_qcom_cfg_timers(hba, dev_req_params->gear_rx,
-                               dev_req_params->pwr_rx,
-                               dev_req_params->hs_rate);
+       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;
+
+       pm_runtime_get_sync(host->hba->dev);
+       ufshcd_hold(host->hba, false);
+       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);
+       ufshcd_release(host->hba);
+       pm_runtime_put_sync(host->hba->dev);
+
+       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)
+{
+       ufs_qcom_dump_regs(hba, REG_UFS_SYS1CLK_1US, 16,
+                       "HCI Vendor Specific Registers ");
+
+       ufs_qcom_testbus_read(hba);
+}
 /**
  * struct ufs_hba_qcom_vops - UFS QCOM specific variant operations
  *
@@ -1049,6 +1207,7 @@ static struct ufs_hba_variant_ops ufs_hba_qcom_vops = {
        .pwr_change_notify      = ufs_qcom_pwr_change_notify,
        .suspend                = ufs_qcom_suspend,
        .resume                 = ufs_qcom_resume,
+       .dbg_register_dump      = ufs_qcom_dump_dbg_regs,
 };
 
 /**
diff --git a/drivers/scsi/ufs/ufs-qcom.h b/drivers/scsi/ufs/ufs-qcom.h
index db2c0a0..1b71a1b 100644
--- a/drivers/scsi/ufs/ufs-qcom.h
+++ b/drivers/scsi/ufs/ufs-qcom.h
@@ -58,6 +58,16 @@ 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,
+
+};
+
+/* QCOM UFS host controller vendor specific debug registers */
+enum {
        UFS_DBG_RD_REG_UAWM                     = 0x100,
        UFS_DBG_RD_REG_UARM                     = 0x200,
        UFS_DBG_RD_REG_TXUC                     = 0x300,
@@ -73,6 +83,9 @@ enum {
        UFS_UFS_DBG_RD_EDTL_RAM                 = 0x1900,
 };
 
+#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)
@@ -83,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 |\
@@ -106,6 +122,15 @@ enum ufs_qcom_phy_init_type {
        UFS_PHY_INIT_CFG_RESTORE,
 };
 
+/* 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_TEST_BUS_EN)
+
 static inline void
 ufs_qcom_get_controller_revision(struct ufs_hba *hba,
                                 u8 *major, u16 *minor, u16 *step)
@@ -157,8 +182,13 @@ struct ufs_hw_version {
        u16 minor;
        u8 major;
 };
-struct ufs_qcom_host {
 
+struct ufs_qcom_testbus {
+       u8 select_major;
+       u8 select_minor;
+};
+
+struct ufs_qcom_host {
        /*
         * Set this capability if host controller supports the QUniPro mode
         * and if driver wants the Host controller to operate in QUniPro mode.
@@ -179,12 +209,17 @@ struct ufs_qcom_host {
        bool is_lane_clks_enabled;
 
        struct ufs_hw_version hw_ver;
+       /* 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);
+
 static inline bool ufs_qcom_cap_qunipro(struct ufs_qcom_host *host)
 {
        if (host->caps & UFS_QCOM_CAP_QUNIPRO)
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 2ef9834..52f9dad 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -625,6 +625,7 @@ start:
 out:
        return rc;
 }
+EXPORT_SYMBOL_GPL(ufshcd_hold);
 
 static void ufshcd_gate_work(struct work_struct *work)
 {
@@ -712,6 +713,7 @@ void ufshcd_release(struct ufs_hba *hba)
        __ufshcd_release(hba);
        spin_unlock_irqrestore(hba->host->host_lock, flags);
 }
+EXPORT_SYMBOL_GPL(ufshcd_release);
 
 static ssize_t ufshcd_clkgate_delay_show(struct device *dev,
                struct device_attribute *attr, char *buf)
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index f2aa47e..471c667 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -259,6 +259,7 @@ struct ufs_pwr_mode_info {
  *                     to be set.
  * @suspend: called during host controller PM callback
  * @resume: called during host controller PM callback
+ * @dbg_register_dump: used to dump controller debug information
  */
 struct ufs_hba_variant_ops {
        const char *name;
@@ -275,6 +276,7 @@ struct ufs_hba_variant_ops {
                                        struct ufs_pa_layer_attr *);
        int     (*suspend)(struct ufs_hba *, enum ufs_pm_op);
        int     (*resume)(struct ufs_hba *, enum ufs_pm_op);
+       void    (*dbg_register_dump)(struct ufs_hba *hba);
 };
 
 /* clock gating state  */
@@ -773,4 +775,10 @@ static inline int ufshcd_vops_resume(struct ufs_hba *hba, 
enum ufs_pm_op op)
        return 0;
 }
 
+static inline void ufshcd_vops_dbg_register_dump(struct ufs_hba *hba)
+{
+       if (hba->vops && hba->vops->dbg_register_dump)
+               hba->vops->dbg_register_dump(hba);
+}
+
 #endif /* End of Header */
-- 
1.8.5.2

-- 
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of 
Code Aurora Forum, hosted by The Linux Foundation
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to