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

Commit 95b3be3d authored by Ram Prakash Gupta's avatar Ram Prakash Gupta
Browse files

mmc: sdhci-msm: Add SDCC debug feature



With latest release of HPG for SDCC, some debug features
were added to get useful information at hardware level at
time of any issue.

This change adds a provision to enter/exit SDCC debug mode.
And it dumps below debug info when SDHC encounters any issue:
- fsm history of sdhc
- newly added ADMA descriptor info.
- sdcc iib history.

And exits from auto-recovery disable upon dumping sdcc registers
so that FSM would move back to idle state. And enable testbus bit
to disable internal clk gating as per sdcc HPG to support debug
feature.

Change-Id: Ie490fa5cd255ee6ba8dbaa6d08075dfa17f25093
Signed-off-by: default avatarRam Prakash Gupta <rampraka@codeaurora.org>
parent ffda1ab8
Loading
Loading
Loading
Loading
+151 −0
Original line number Diff line number Diff line
@@ -1210,6 +1210,78 @@ static void sdhci_msm_set_mmc_drv_type(struct sdhci_host *host, u32 opcode,
			drv_type);
}

#define IPCAT_MINOR_MASK(val) ((val & 0x0fff0000) >> 0x10)

/* Enter sdcc debug mode */
void sdhci_msm_enter_dbg_mode(struct sdhci_host *host)
{
	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
	struct sdhci_msm_host *msm_host = pltfm_host->priv;
	struct platform_device *pdev = msm_host->pdev;
	u32 enable_dbg_feature = 0;
	u32 minor;

	minor = IPCAT_MINOR_MASK(readl_relaxed(host->ioaddr +
				SDCC_IP_CATALOG));
	if (minor < 2 || msm_host->debug_mode_enabled)
		return;

	/* Enable debug mode */
	writel_relaxed(ENABLE_DBG,
			host->ioaddr + SDCC_TESTBUS_CONFIG);
	writel_relaxed(DUMMY,
			host->ioaddr + SDCC_DEBUG_EN_DIS_REG);
	writel_relaxed((readl_relaxed(host->ioaddr +
			SDCC_TESTBUS_CONFIG) | TESTBUS_EN),
			host->ioaddr + SDCC_TESTBUS_CONFIG);

	if (minor >= 2)
		enable_dbg_feature |= FSM_HISTORY |
			AUTO_RECOVERY_DISABLE |
			MM_TRIGGER_DISABLE |
			IIB_EN;

	/* Enable particular feature */
	writel_relaxed((readl_relaxed(host->ioaddr +
			SDCC_DEBUG_FEATURE_CFG_REG) | enable_dbg_feature),
			host->ioaddr + SDCC_DEBUG_FEATURE_CFG_REG);

	/* Read back to ensure write went through */
	readl_relaxed(host->ioaddr +
			SDCC_DEBUG_FEATURE_CFG_REG);
	msm_host->debug_mode_enabled = true;

	dev_info(&pdev->dev, "Debug feature enabled 0x%08x\n",
			readl_relaxed(host->ioaddr +
			SDCC_DEBUG_FEATURE_CFG_REG));
}

/* Exit sdcc debug mode */
void sdhci_msm_exit_dbg_mode(struct sdhci_host *host)
{
	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
	struct sdhci_msm_host *msm_host = pltfm_host->priv;
	struct platform_device *pdev = msm_host->pdev;
	u32 minor;

	minor = IPCAT_MINOR_MASK(readl_relaxed(host->ioaddr +
				SDCC_IP_CATALOG));
	if (minor < 2 || !msm_host->debug_mode_enabled)
		return;

	/* Exit debug mode */
	writel_relaxed(DISABLE_DBG,
			host->ioaddr + SDCC_TESTBUS_CONFIG);
	writel_relaxed(DUMMY,
			host->ioaddr + SDCC_DEBUG_EN_DIS_REG);

	msm_host->debug_mode_enabled = false;

	dev_dbg(&pdev->dev, "Debug feature disabled 0x%08x\n",
			readl_relaxed(host->ioaddr +
			SDCC_DEBUG_FEATURE_CFG_REG));
}

int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
{
	unsigned long flags;
@@ -1244,6 +1316,7 @@ int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
	 */
	if (msm_host->tuning_in_progress)
		return 0;
	sdhci_msm_exit_dbg_mode(host);
	msm_host->tuning_in_progress = true;
	pr_debug("%s: Enter %s\n", mmc_hostname(mmc), __func__);

@@ -1437,6 +1510,7 @@ int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
kfree:
	kfree(data_buf);
out:
	sdhci_msm_enter_dbg_mode(host);
	spin_lock_irqsave(&host->lock, flags);
	if (!rc)
		msm_host->tuning_done = true;
@@ -3995,6 +4069,73 @@ static void sdhci_msm_cqe_dump_debug_ram(struct sdhci_host *host)
	pr_err("-------------------------\n");
}

#define DUMP_FSM readl_relaxed(host->ioaddr + SDCC_DEBUG_FSM_TRACE_RD_REG)
#define MAX_FSM 16

void sdhci_msm_dump_fsm_history(struct sdhci_host *host)
{
	u32 sel_fsm;

	pr_err("----------- FSM REGISTER DUMP -----------\n");
	/* select fsm to dump */
	for (sel_fsm = 0; sel_fsm <= MAX_FSM; sel_fsm++) {
		writel_relaxed(sel_fsm, host->ioaddr +
				SDCC_DEBUG_FSM_TRACE_CFG_REG);
		pr_err(": selected fsm is 0x%08x\n",
				readl_relaxed(host->ioaddr +
				SDCC_DEBUG_FSM_TRACE_CFG_REG));
		/* dump selected fsm history */
		pr_err("0x%08x 0x%08x 0x%08x 0x%08x\n",
				readl_relaxed(host->ioaddr +
					SDCC_DEBUG_FSM_TRACE_RD_REG),
				readl_relaxed(host->ioaddr +
					SDCC_DEBUG_FSM_TRACE_RD_REG),
				readl_relaxed(host->ioaddr +
					SDCC_DEBUG_FSM_TRACE_RD_REG),
				readl_relaxed(host->ioaddr +
					SDCC_DEBUG_FSM_TRACE_RD_REG));
		pr_err("0x%08x 0x%08x 0x%08x\n",
				readl_relaxed(host->ioaddr +
					SDCC_DEBUG_FSM_TRACE_RD_REG),
				readl_relaxed(host->ioaddr +
					SDCC_DEBUG_FSM_TRACE_RD_REG),
				readl_relaxed(host->ioaddr +
					SDCC_DEBUG_FSM_TRACE_RD_REG));
	}
	/* Flush all fsm history */
	writel_relaxed(DUMMY, host->ioaddr +
			SDCC_DEBUG_FSM_TRACE_FIFO_FLUSH_REG);

	/* Exit from Auto recovery disable to move FSMs to idle state */
	writel_relaxed(DUMMY, host->ioaddr +
			SDCC_DEBUG_ERROR_STATE_EXIT_REG);

}

void sdhci_msm_dump_desc_history(struct sdhci_host *host)
{
	pr_err("----------- DESC HISTORY DUMP -----------\n");
	pr_err("Current Desc Addr: 0x%08x | Info: 0x%08x\n",
			readl_relaxed(host->ioaddr + SDCC_CURR_DESC_ADDR),
			readl_relaxed(host->ioaddr + SDCC_CURR_DESC_INFO));
	pr_err("Processed Desc1 Addr: 0x%08x | Info: 0x%08x\n",
			readl_relaxed(host->ioaddr + SDCC_PROC_DESC0_ADDR),
			readl_relaxed(host->ioaddr + SDCC_PROC_DESC0_INFO));
	pr_err("Processed Desc2 Addr: 0x%08x | Info: 0x%08x\n",
			readl_relaxed(host->ioaddr + SDCC_PROC_DESC1_ADDR),
			readl_relaxed(host->ioaddr + SDCC_PROC_DESC1_INFO));
}

void sdhci_msm_dump_iib(struct sdhci_host *host)
{
	u32 iter;

	pr_err("----------- IIB HISTORY DUMP -----------\n");
	for (iter = 0; iter < 8; iter++)
		pr_err("0x%08x\n", readl_relaxed(host->ioaddr +
			SDCC_DEBUG_IIB_REG + (iter * 4)));
}

void sdhci_msm_dump_vendor_regs(struct sdhci_host *host)
{
	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -4052,6 +4193,14 @@ void sdhci_msm_dump_vendor_regs(struct sdhci_host *host)
			msm_host_offset->CORE_VENDOR_SPEC_FUNC2),
		readl_relaxed(host->ioaddr +
			msm_host_offset->CORE_VENDOR_SPEC3));

	if (msm_host->debug_mode_enabled) {
		sdhci_msm_dump_fsm_history(host);
		sdhci_msm_dump_desc_history(host);
	}
	/* Debug feature enable not must for iib */
	sdhci_msm_dump_iib(host);

	/*
	 * tbsel indicates [2:0] bits and tbsel2 indicates [7:4] bits
	 * of CORE_TESTBUS_CONFIG register.
@@ -4768,6 +4917,8 @@ static struct sdhci_ops sdhci_msm_ops = {
	.get_current_limit = sdhci_msm_get_current_limit,
	.notify_load = sdhci_msm_notify_load,
	.irq = sdhci_msm_cqe_irq,
	.enter_dbg_mode = sdhci_msm_enter_dbg_mode,
	.exit_dbg_mode = sdhci_msm_exit_dbg_mode,
};

static void sdhci_set_default_hw_caps(struct sdhci_msm_host *msm_host,
+62 −0
Original line number Diff line number Diff line
@@ -11,6 +11,67 @@
#include <linux/pm_qos.h>
#include "sdhci-pltfm.h"

/* check IP CATALOG version */
#define SDCC_IP_CATALOG 0x328

/* DBG register offsets */
#define SDCC_TESTBUS_CONFIG 0x32C
#define SDCC_DEBUG_EN_DIS_REG 0x390
#define SDCC_DEBUG_FEATURE_CFG_REG 0x394
#define SDCC_DEBUG_FSM_TRACE_CFG_REG 0x398
#define SDCC_DEBUG_FSM_TRACE_RD_REG 0x39C
#define SDCC_DEBUG_FSM_TRACE_FIFO_FLUSH_REG 0x3A0
#define SDCC_DEBUG_PANIC_ERROR_EN_REG 0x3A4
#define SDCC_DEBUG_ERROR_STATE_EXIT_REG 0x3B8
#define SDCC_CURR_DESC_ADDR 0x3EC
#define SDCC_CURR_DESC_INFO 0x3F0
#define SDCC_PROC_DESC0_ADDR 0x3E4
#define SDCC_PROC_DESC0_INFO 0x3E8
#define SDCC_PROC_DESC1_ADDR 0x3DC
#define SDCC_PROC_DESC1_INFO 0x3E0
#define SDCC_DEBUG_IIB_REG 0x980
#define SDCC_DEBUG_MASK_PATTERN_REG 0x3C0
#define SDCC_DEBUG_MATCH_PATTERN_REG 0x3C4
#define SDCC_DEBUG_MM_TB_CFG_REG 0x3BC

#define ENABLE_DBG 0x35350000
#define DISABLE_DBG 0x26260000

#define DUMMY 0x1 /* value doesn't matter */

/* Panic on Err */
#define BOOT_ACK_REC_EN BIT(0)
#define BOOT_ACK_ERR_EN BIT(1)
#define BOOT_TIMEOUT_EN BIT(2)
#define AUTO_CMD19_TOUT_EN BIT(3)
#define STBITE_EN BIT(4)
#define CTOUT_EN BIT(5)
#define CCRCF_EN BIT(6)
#define CMD_END_BIT_ERR_EN BIT(7)
#define CMD_INDEX_ERR_EN BIT(8)
#define DTOUT_EN BIT(9)
#define DCRCF_EN BIT(10)
#define DATA_END_BIT_ERR_EN BIT(11)
#define CMDQ_HALT_ACK_INT_EN BIT(16)
#define CMDQ_TASK_COMPLETED_INT_EN BIT(17)
#define CMDQ_RESP_ERR_INT_EN BIT(18)
#define CMDQ_TASK_CLEARED_INT_EN BIT(19)
#define CMDQ_GENERAL_CRYPTO_ERROR_EN BIT(20)
#define CMDQ_INVALID_CRYPTO_CFG_ERROR_EN BIT(21)
#define CMDQ_DEVICE_EXCEPTION_INT_EN BIT(22)
#define ADMA_ERROR_EXT_EN BIT(23)
#define HC_NONCQ_ICE_INT_STATUS_MASKED_EN BIT(24)

/* Select debug Feature */
#define FSM_HISTORY BIT(0)
#define PANIC_ALERT BIT(1)
#define AUTO_RECOVERY_DISABLE BIT(2)
#define MM_TRIGGER_DISABLE BIT(3)
#define DESC_HISTORY BIT(4)
#define IIB_EN BIT(6)

#define TESTBUS_EN BIT(31)

/* This structure keeps information per regulator */
struct sdhci_msm_reg_data {
	/* voltage regulator handle */
@@ -267,6 +328,7 @@ struct sdhci_msm_host {
	struct sdhci_msm_dll_hsr *dll_hsr;
	struct sdhci_msm_ice_data ice;
	u32 ice_clk_rate;
	bool debug_mode_enabled;
};

extern char *saved_command_line;
+18 −0
Original line number Diff line number Diff line
@@ -2418,6 +2418,22 @@ static void sdhci_hw_reset(struct mmc_host *mmc)
		host->ops->hw_reset(host);
}

static void sdhci_enter_dbg_mode(struct mmc_host *mmc)
{
	struct sdhci_host *host = mmc_priv(mmc);

	if (host->ops && host->ops->enter_dbg_mode)
		host->ops->enter_dbg_mode(host);
}

static void sdhci_exit_dbg_mode(struct mmc_host *mmc)
{
	struct sdhci_host *host = mmc_priv(mmc);

	if (host->ops && host->ops->exit_dbg_mode)
		host->ops->exit_dbg_mode(host);
}

static void sdhci_enable_sdio_irq_nolock(struct sdhci_host *host, int enable)
{
	u16 ctrl = 0;
@@ -2968,6 +2984,8 @@ static const struct mmc_host_ops sdhci_ops = {
	.get_cd		= sdhci_get_cd,
	.get_ro		= sdhci_get_ro,
	.hw_reset	= sdhci_hw_reset,
	.enter_dbg_mode = sdhci_enter_dbg_mode,
	.exit_dbg_mode = sdhci_exit_dbg_mode,
	.enable_sdio_irq = sdhci_enable_sdio_irq,
	.start_signal_voltage_switch	= sdhci_start_signal_voltage_switch,
	.prepare_hs400_tuning		= sdhci_prepare_hs400_tuning,
+2 −0
Original line number Diff line number Diff line
@@ -747,6 +747,8 @@ struct sdhci_ops {
	void	(*pre_req)(struct sdhci_host *host, struct mmc_request *req);
	void	(*post_req)(struct sdhci_host *host, struct mmc_request *req);
	unsigned int	(*get_current_limit)(struct sdhci_host *host);
	void	(*enter_dbg_mode)(struct sdhci_host *host);
	void	(*exit_dbg_mode)(struct sdhci_host *host);
};

#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
+2 −0
Original line number Diff line number Diff line
@@ -199,6 +199,8 @@ struct mmc_host_ops {
					 unsigned int max_dtr, int host_drv,
					 int card_drv, int *drv_type);
	void	(*hw_reset)(struct mmc_host *host);
	void    (*enter_dbg_mode)(struct mmc_host *host);
	void    (*exit_dbg_mode)(struct mmc_host *host);
	void	(*card_event)(struct mmc_host *host);

	/*