Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 187cf3e2 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "scsi: ufs-qcom: add debug prints for test bus"

parents e18d9cff 15b8dd65
Loading
Loading
Loading
Loading
+179 −0
Original line number Diff line number Diff line
@@ -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)
@@ -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)
{
@@ -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:
+157 −2
Original line number Diff line number Diff line
@@ -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];

@@ -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)
@@ -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;

@@ -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;
@@ -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);
}

+28 −2
Original line number Diff line number Diff line
@@ -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,
@@ -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)
@@ -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 |\
@@ -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,
@@ -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;
@@ -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