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

Commit 459180cc authored by Bao D. Nguyen's avatar Bao D. Nguyen
Browse files

mmc: sdhci-msm: Add SD card's err_stat sysfs entry



Add sysfs entry to query SD card errors statistics.
This feature is useful for debug and testing.

Change-Id: I15f2599683cbb811ad16796dc43fb34de019506c
Signed-off-by: default avatarBao D. Nguyen <nguyenb@codeaurora.org>
parent 1d86c274
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -3032,6 +3032,9 @@ void mmc_rescan(struct work_struct *work)
		if (freqs[i] <= host->f_min)
			break;
	}
#if defined(CONFIG_SDC_QTI)
	host->err_stats[MMC_ERR_CMD_TIMEOUT] = 0;
#endif
	mmc_release_host(host);

 out:
+8 −0
Original line number Diff line number Diff line
@@ -672,11 +672,19 @@ int mmc_send_tuning(struct mmc_host *host, u32 opcode, int *cmd_error)

	if (cmd.error) {
		err = cmd.error;
#if defined(CONFIG_SDC_QTI)
		/* Ignore crc errors occurred during tuning */
		if (host->err_stats[MMC_ERR_CMD_CRC])
			host->err_stats[MMC_ERR_CMD_CRC]--;
#endif
		goto out;
	}

	if (data.error) {
		err = data.error;
#if defined(CONFIG_SDC_QTI)
		 host->err_stats[MMC_ERR_DAT_CRC]--;
#endif
		goto out;
	}

+103 −1
Original line number Diff line number Diff line
@@ -3586,6 +3586,106 @@ static int sdhci_msm_setup_ice_clk(struct sdhci_msm_host *msm_host,
	return ret;
}

#if defined(CONFIG_SDC_QTI)
static ssize_t err_state_show(struct device *dev,
			struct device_attribute *attr, char *buf)
{
	struct sdhci_host *host = dev_get_drvdata(dev);

	if (!host || !host->mmc)
		return -EINVAL;

	return scnprintf(buf, PAGE_SIZE, "%d\n", !!host->mmc->err_occurred);
}

static ssize_t err_state_store(struct device *dev,
				struct device_attribute *attr,
				const char *buf, size_t count)
{
	struct sdhci_host *host = dev_get_drvdata(dev);
	unsigned int val;

	if (kstrtouint(buf, 0, &val))
		return -EINVAL;
	if (!host || !host->mmc)
		return -EINVAL;

	host->mmc->err_occurred = !!val;
	if (!val)
		memset(host->mmc->err_stats, 0, sizeof(host->mmc->err_stats));

	return count;
}
static DEVICE_ATTR_RW(err_state);

static ssize_t err_stats_show(struct device *dev,
				struct device_attribute *attr, char *buf)
{
	struct sdhci_host *host = dev_get_drvdata(dev);
	char tmp[100];

	if (!host || !host->mmc)
		return -EINVAL;

	scnprintf(tmp, sizeof(tmp), "# Command Timeout Error: %d\n",
		host->mmc->err_stats[MMC_ERR_CMD_TIMEOUT]);
	strlcpy(buf, tmp, PAGE_SIZE);

	scnprintf(tmp, sizeof(tmp), "# Command CRC Error: %d\n",
		host->mmc->err_stats[MMC_ERR_CMD_CRC]);
	strlcat(buf, tmp, PAGE_SIZE);

	scnprintf(tmp, sizeof(tmp), "# Data Timeout Error: %d\n",
		host->mmc->err_stats[MMC_ERR_DAT_TIMEOUT]);
	strlcat(buf, tmp, PAGE_SIZE);

	scnprintf(tmp, sizeof(tmp), "# Data CRC Error: %d\n",
		host->mmc->err_stats[MMC_ERR_DAT_CRC]);
	strlcat(buf, tmp, PAGE_SIZE);

	scnprintf(tmp, sizeof(tmp), "# Auto-Cmd Error: %d\n",
		host->mmc->err_stats[MMC_ERR_ADMA]);
	strlcat(buf, tmp, PAGE_SIZE);

	scnprintf(tmp, sizeof(tmp), "# ADMA Error: %d\n",
		host->mmc->err_stats[MMC_ERR_ADMA]);
	strlcat(buf, tmp, PAGE_SIZE);

	scnprintf(tmp, sizeof(tmp), "# Tuning Error: %d\n",
		host->mmc->err_stats[MMC_ERR_TUNING]);
	strlcat(buf, tmp, PAGE_SIZE);

	scnprintf(tmp, sizeof(tmp), "# Request Timedout Error: %d\n",
		host->mmc->err_stats[MMC_ERR_REQ_TIMEOUT]);
	strlcat(buf, tmp, PAGE_SIZE);

	return strlen(buf);
}
static DEVICE_ATTR_RO(err_stats);

static struct attribute *sdhci_msm_sysfs_attrs[] = {
	&dev_attr_err_state.attr,
	&dev_attr_err_stats.attr,
	NULL
};

static const struct attribute_group sdhci_msm_sysfs_group = {
	.name = "qcom",
	.attrs = sdhci_msm_sysfs_attrs,
};

static int sdhci_msm_init_sysfs(struct platform_device *pdev)
{
	int ret;

	ret = sysfs_create_group(&pdev->dev.kobj, &sdhci_msm_sysfs_group);
	if (ret)
		dev_err(&pdev->dev, "%s: Failed sdhci_msm sysfs group err=%d\n",
			__func__, ret);
	return ret;
}
#endif

static int sdhci_msm_probe(struct platform_device *pdev)
{
	struct sdhci_host *host;
@@ -3860,7 +3960,9 @@ static int sdhci_msm_probe(struct platform_device *pdev)
	 * value won't be over-written.
	 */
	host->mmc->max_busy_timeout = 0;

#if defined(CONFIG_SDC_QTI)
	sdhci_msm_init_sysfs(pdev);
#endif
	/*
	 * Set platfm_init_done only after sdhci_add_host().
	 * So that we don't turn off vqmmc while we reset sdhc as
+37 −7
Original line number Diff line number Diff line
@@ -108,7 +108,9 @@ void sdhci_dumpregs(struct sdhci_host *host)
				   sdhci_readl(host, SDHCI_ADMA_ADDRESS));
		}
	}

#if defined(CONFIG_SDC_QTI)
	host->mmc->err_occurred = true;
#endif
	SDHCI_DUMP("============================================\n");
}
EXPORT_SYMBOL_GPL(sdhci_dumpregs);
@@ -2880,6 +2882,9 @@ static void sdhci_timeout_timer(struct timer_list *t)
	spin_lock_irqsave(&host->lock, flags);

	if (host->cmd && !sdhci_data_line_cmd(host->cmd)) {
#if defined(CONFIG_SDC_QTI)
		host->mmc->err_stats[MMC_ERR_REQ_TIMEOUT]++;
#endif
		pr_err("%s: Timeout waiting for hardware cmd interrupt.\n",
		       mmc_hostname(host->mmc));
		sdhci_dumpregs(host);
@@ -2902,6 +2907,9 @@ static void sdhci_timeout_data_timer(struct timer_list *t)

	if (host->data || host->data_cmd ||
	    (host->cmd && sdhci_data_line_cmd(host->cmd))) {
#if defined(CONFIG_SDC_QTI)
		host->mmc->err_stats[MMC_ERR_REQ_TIMEOUT]++;
#endif
		pr_err("%s: Timeout waiting for hardware interrupt.\n",
		       mmc_hostname(host->mmc));
		sdhci_dumpregs(host);
@@ -2961,11 +2969,17 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *intmask_p)

	if (intmask & (SDHCI_INT_TIMEOUT | SDHCI_INT_CRC |
		       SDHCI_INT_END_BIT | SDHCI_INT_INDEX)) {
		if (intmask & SDHCI_INT_TIMEOUT)
		if (intmask & SDHCI_INT_TIMEOUT) {
			host->cmd->error = -ETIMEDOUT;
		else
#if defined(CONFIG_SDC_QTI)
			host->mmc->err_stats[MMC_ERR_CMD_TIMEOUT]++;
#endif
		} else {
			host->cmd->error = -EILSEQ;

#if defined(CONFIG_SDC_QTI)
			host->mmc->err_stats[MMC_ERR_CMD_CRC]++;
#endif
		}
		/* Treat data command CRC error the same as data CRC error */
		if (host->cmd->data &&
		    (intmask & (SDHCI_INT_CRC | SDHCI_INT_TIMEOUT)) ==
@@ -2986,7 +3000,9 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *intmask_p)
		int err = (auto_cmd_status & SDHCI_AUTO_CMD_TIMEOUT) ?
			  -ETIMEDOUT :
			  -EILSEQ;

#if defined(CONFIG_SDC_QTI)
		host->mmc->err_stats[MMC_ERR_AUTO_CMD]++;
#endif
		if (mrq->sbc && (host->flags & SDHCI_AUTO_CMD23)) {
			mrq->sbc->error = err;
			__sdhci_finish_mrq(host, mrq);
@@ -3057,6 +3073,9 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
			if (intmask & SDHCI_INT_DATA_TIMEOUT) {
				host->data_cmd = NULL;
				data_cmd->error = -ETIMEDOUT;
#if defined(CONFIG_SDC_QTI)
				host->mmc->err_stats[MMC_ERR_CMD_TIMEOUT]++;
#endif
				__sdhci_finish_mrq(host, data_cmd->mrq);
				return;
			}
@@ -3090,18 +3109,29 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
		return;
	}

	if (intmask & SDHCI_INT_DATA_TIMEOUT)
	if (intmask & SDHCI_INT_DATA_TIMEOUT) {
		host->data->error = -ETIMEDOUT;
#if defined(CONFIG_SDC_QTI)
		host->mmc->err_stats[MMC_ERR_DAT_TIMEOUT]++;
#endif
	}
	else if (intmask & SDHCI_INT_DATA_END_BIT)
		host->data->error = -EILSEQ;
	else if ((intmask & SDHCI_INT_DATA_CRC) &&
		SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND))
			!= MMC_BUS_TEST_R)
			!= MMC_BUS_TEST_R) {
		host->data->error = -EILSEQ;
#if defined(CONFIG_SDC_QTI)
		host->mmc->err_stats[MMC_ERR_DAT_CRC]++;
#endif
	}
	else if (intmask & SDHCI_INT_ADMA_ERROR) {
		pr_err("%s: ADMA error: 0x%08x\n", mmc_hostname(host->mmc),
		       intmask);
		sdhci_adma_show_error(host);
#if defined(CONFIG_SDC_QTI)
		host->mmc->err_stats[MMC_ERR_ADMA]++;
#endif
		host->data->error = -EIO;
		if (host->ops->adma_workaround)
			host->ops->adma_workaround(host, intmask);
+23 −1
Original line number Diff line number Diff line
@@ -82,6 +82,25 @@ struct mmc_ios {

struct mmc_host;

#if defined(CONFIG_SDC_QTI)
enum {
	MMC_ERR_CMD_TIMEOUT,
	MMC_ERR_CMD_CRC,
	MMC_ERR_DAT_TIMEOUT,
	MMC_ERR_DAT_CRC,
	MMC_ERR_AUTO_CMD,
	MMC_ERR_ADMA,
	MMC_ERR_TUNING,
	MMC_ERR_CMDQ_RED,
	MMC_ERR_CMDQ_GCE,
	MMC_ERR_CMDQ_ICCE,
	MMC_ERR_REQ_TIMEOUT,
	MMC_ERR_CMDQ_REQ_TIMEOUT,
	MMC_ERR_ICE_CFG,
	MMC_ERR_MAX,
};
#endif

struct mmc_host_ops {
	/*
	 * It is optional for the host to implement pre_req and post_req in
@@ -518,7 +537,10 @@ struct mmc_host {
	struct mmc_supply	supply;

	struct dentry		*debugfs_root;

#if defined(CONFIG_SDC_QTI)
	bool			err_occurred;
	u32			err_stats[MMC_ERR_MAX];
#endif
	/* Ongoing data transfer that allows commands during transfer */
	struct mmc_request	*ongoing_mrq;