mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-07 04:12:04 -04:00
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: Bao D. Nguyen <nguyenb@codeaurora.org>
This commit is contained in:
parent
1d86c274d4
commit
459180cc35
5 changed files with 174 additions and 9 deletions
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue