diff mbox

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

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

Commit Message

Yaniv Gardi Aug. 20, 2015, 1:59 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 | 164 +++++++++++++++++++++++++++++++++++++++++++-
 drivers/scsi/ufs/ufs-qcom.h |  37 +++++++++-
 drivers/scsi/ufs/ufshcd.c   |   2 +
 drivers/scsi/ufs/ufshcd.h   |   8 +++
 4 files changed, 207 insertions(+), 4 deletions(-)

Comments

Akinobu Mita Aug. 20, 2015, 11:42 p.m. UTC | #1
2015-08-20 22:59 GMT+09:00 Yaniv Gardi <ygardi@codeaurora.org>:
> @@ -30,6 +48,14 @@ 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, hba->mmio_base + offset, len * 4, false);
> +}

This causes a sparse warning as __iomem pointer is passed to
print_hex_dump().
--
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
Yaniv Gardi Aug. 21, 2015, 10:23 p.m. UTC | #2
> 2015-08-20 22:59 GMT+09:00 Yaniv Gardi <ygardi@codeaurora.org>:
>> @@ -30,6 +48,14 @@ 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, hba->mmio_base + offset, len * 4, false);
>> +}
>
> This causes a sparse warning as __iomem pointer is passed to
> print_hex_dump().

indeed.
any suggestions how it can be fixed ? I guess I shall try casting.

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


--
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
Akinobu Mita Aug. 22, 2015, 2:56 a.m. UTC | #3
2015-08-22 7:23 GMT+09:00  <ygardi@codeaurora.org>:
>> 2015-08-20 22:59 GMT+09:00 Yaniv Gardi <ygardi@codeaurora.org>:
>>> @@ -30,6 +48,14 @@ 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, hba->mmio_base + offset, len * 4, false);
>>> +}
>>
>> This causes a sparse warning as __iomem pointer is passed to
>> print_hex_dump().
>
> indeed.
> any suggestions how it can be fixed ? I guess I shall try casting.

I think introducing generic print_hex_dump_io() library function
is useful for this and other drivers want to use it.  But for now we can just
create simple local dump function which readl() and print for each register.
--
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
Akinobu Mita Aug. 22, 2015, 3:07 a.m. UTC | #4
2015-08-20 22:59 GMT+09:00 Yaniv Gardi <ygardi@codeaurora.org>:

> +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) {

select_minor should be checked instead of select_major here?

> +               dev_err(host->hba->dev,
> +                       "%s: 0x%05X is not a legal testbus option\n",
> +                       __func__, host->testbus.select_minor);
> +               return false;
> +       }
> +
> +       return true;
> +}
--
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
Yaniv Gardi Aug. 23, 2015, 8:54 a.m. UTC | #5
> 2015-08-20 22:59 GMT+09:00 Yaniv Gardi <ygardi@codeaurora.org>:
>
>> +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) {
>
> select_minor should be checked instead of select_major here?

you are correct. should be select_minor

>
>> +               dev_err(host->hba->dev,
>> +                       "%s: 0x%05X is not a legal testbus option\n",
>> +                       __func__, host->testbus.select_minor);
>> +               return false;
>> +       }
>> +
>> +       return true;
>> +}
> --
> 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
>


--
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 725cd49..adfd3f3 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,14 @@  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, hba->mmio_base + offset, len * 4, false);
+}
 
 static int ufs_qcom_get_connected_tx_lanes(struct ufs_hba *hba, u32 *tx_lanes)
 {
@@ -996,6 +1022,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 +1060,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_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;
 
-	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 +1206,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..cf8957c 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 */