diff mbox

[v3,7/8] scsi: ufs-qcom: add debug prints for test bus

Message ID 1440335381-5122-8-git-send-email-ygardi@codeaurora.org (mailing list archive)
State New, archived
Headers show

Commit Message

Yaniv Gardi Aug. 23, 2015, 1:09 p.m. UTC
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 <ygardi@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(-)

Comments

Yaniv Gardi Aug. 25, 2015, 11:54 a.m. UTC | #1
Reviewed-by: Akinobu Mita <akinobu.mita@gmail.com>


> 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 <ygardi@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-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

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 */