aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc
diff options
context:
space:
mode:
authorScott Anderson <saa@android.com>2012-03-20 08:41:58 -0700
committerZiyann <jaraidaniel@gmail.com>2014-11-26 21:10:53 +0100
commit3cd4f37e5fef9cebee81d09a7c8cb70d3bfc23e8 (patch)
tree008b30b0065660d305f87f5b607a55e0bb707d3f /drivers/mmc
parent91bbab001bfa2139fdd69d3c5a431e925520b0a8 (diff)
downloadkernel_samsung_tuna-3cd4f37e5fef9cebee81d09a7c8cb70d3bfc23e8.zip
kernel_samsung_tuna-3cd4f37e5fef9cebee81d09a7c8cb70d3bfc23e8.tar.gz
kernel_samsung_tuna-3cd4f37e5fef9cebee81d09a7c8cb70d3bfc23e8.tar.bz2
[MMC] Provide access to Samsung's e-MMC Smart Report via sysfs
Added CONFIG_MMC_SAMSUNG_SMART which enables code that will expose Samsung's proprietary e-MMC health information to sysfs. Example output: $ cat /sys/devices/platform/*/*/mmc_host/mmc*/mmc*/samsung_smart version : 0 error mode : Normal super block size : 4194304 super page size : 32768 optimal write size : 32768 read reclaim count : 0 optimal trim size : 262144 number of banks : 2 initial bad blocks per bank : 10,4,0,0 runtime bad blocks per bank : 0,0,0,0 reserved blocks left per bank : 131,137,0,0 all erase counts (min,avg,max): 0,760,65535 SLC erase counts (min,avg,max): 0,5,74 MLC erase counts (min,avg,max): 0,790,65535 Change-Id: Ib476ea5d35264aa94b378f49121d31b20b751f04 Signed-off-by: Scott Anderson <saa@android.com>
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/core/Kconfig6
-rw-r--r--drivers/mmc/core/mmc.c26
-rw-r--r--drivers/mmc/core/quirks.c151
3 files changed, 181 insertions, 2 deletions
diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig
index 85c2e1a..5e9a948 100644
--- a/drivers/mmc/core/Kconfig
+++ b/drivers/mmc/core/Kconfig
@@ -44,3 +44,9 @@ config MMC_PARANOID_SD_INIT
about re-trying SD init requests. This can be a useful
work-around for buggy controllers and hardware. Enable
if you are experiencing issues with SD detection.
+
+config MMC_SAMSUNG_SMART
+ bool "Make Samsung Smart Report available in sysfs"
+ help
+ If you say Y here, code will be added to retrieve the Smart
+ Report from Samsung e-MMC cards and make it available via sysfs.
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 6d9c0796..5a49cc5 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -485,6 +485,21 @@ out:
return err;
}
+#ifdef CONFIG_MMC_SAMSUNG_SMART
+static ssize_t mmc_samsung_smart(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mmc_card *card = mmc_dev_to_card(dev);
+
+ if (card->quirks & MMC_QUIRK_SAMSUNG_SMART)
+ return mmc_samsung_smart_handle(card, buf);
+
+ /* There is no information available for this card. */
+ return 0;
+}
+static DEVICE_ATTR(samsung_smart, S_IRUGO, mmc_samsung_smart, NULL);
+#endif /* CONFIG_MMC_SAMSUNG_SMART */
+
MMC_DEV_ATTR(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1],
card->raw_cid[2], card->raw_cid[3]);
MMC_DEV_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1],
@@ -516,6 +531,9 @@ static struct attribute *mmc_std_attrs[] = {
&dev_attr_serial.attr,
&dev_attr_enhanced_area_offset.attr,
&dev_attr_enhanced_area_size.attr,
+#ifdef CONFIG_MMC_SAMSUNG_SMART
+ &dev_attr_samsung_smart.attr,
+#endif
NULL,
};
@@ -550,6 +568,14 @@ static const struct mmc_fixup mmc_fixups[] = {
MMC_FIXUP_REV("MAG4FA", 0x15, CID_OEMID_ANY,
cid_rev(0, 0x25, 1997, 1), cid_rev(0, 0x25, 2012, 12),
add_quirk_mmc, MMC_QUIRK_SAMSUNG_WL_PATCH),
+#ifdef CONFIG_MMC_SAMSUNG_SMART
+ MMC_FIXUP("VYL00M", 0x15, CID_OEMID_ANY,
+ add_quirk_mmc, MMC_QUIRK_SAMSUNG_SMART),
+ MMC_FIXUP("KYL00M", 0x15, CID_OEMID_ANY,
+ add_quirk_mmc, MMC_QUIRK_SAMSUNG_SMART),
+ MMC_FIXUP("MAG4FA", 0x15, CID_OEMID_ANY,
+ add_quirk_mmc, MMC_QUIRK_SAMSUNG_SMART),
+#endif
END_FIXUP
};
diff --git a/drivers/mmc/core/quirks.c b/drivers/mmc/core/quirks.c
index 9df3d97..48fb238 100644
--- a/drivers/mmc/core/quirks.c
+++ b/drivers/mmc/core/quirks.c
@@ -164,7 +164,7 @@ static int mmc_movi_erase_cmd(struct mmc_card *card,
#define TEST_MMC_FW_PATCHING
-#ifdef TEST_MMC_FW_PATCHING
+#if defined(CONFIG_MMC_SAMSUNG_SMART) || defined(TEST_MMC_FW_PATCHING)
static struct mmc_command wcmd;
static struct mmc_data wdata;
@@ -198,7 +198,7 @@ static int mmc_movi_read_cmd(struct mmc_card *card, u8 *buffer)
return wdata.error;
return 0;
}
-#endif /* TEST_MMC_FW_PATCHING */
+#endif /* CONFIG_MMC_SAMSUNG_SMART || TEST_MMC_FW_PATCHING */
/*
* Copy entire page when wear leveling is happened
@@ -321,3 +321,150 @@ void mmc_fixup_samsung_fw(struct mmc_card *card)
pr_err("%s : Failed to fixup Samsung emmc firmware(%d)\n",
mmc_hostname(card->host), err);
}
+
+#ifdef CONFIG_MMC_SAMSUNG_SMART
+static int mmc_samsung_smart_read(struct mmc_card *card, u8 *rdblock)
+{
+ int err, errx;
+
+ /* enter vendor Smart Report mode */
+ err = mmc_movi_vendor_cmd(card, 0xEFAC62EC);
+ if (err) {
+ pr_err("Failed entering Smart Report mode(1, %d)\n", err);
+ return err;
+ }
+ err = mmc_movi_vendor_cmd(card, 0x0000CCEE);
+ if (err) {
+ pr_err("Failed entering Smart Report mode(2, %d)\n", err);
+ return err;
+ }
+
+ /* read Smart Report */
+ err = mmc_movi_read_cmd(card, rdblock);
+ if (err)
+ pr_err("Failed reading Smart Report(%d)\n", err);
+ /* Do NOT return yet; we must leave Smart Report mode.*/
+
+ /* exit vendor Smart Report mode */
+ errx = mmc_movi_vendor_cmd(card, 0xEFAC62EC);
+ if (errx)
+ pr_err("Failed exiting Smart Report mode(1, %d)\n", errx);
+ else {
+ errx = mmc_movi_vendor_cmd(card, 0x00DECCEE);
+ if (errx)
+ pr_err("Failed exiting Smart Report mode(2, %d)\n",
+ errx);
+ }
+ if (err)
+ return err;
+ return errx;
+}
+ssize_t mmc_samsung_smart_parse(u32 *report, char *for_sysfs)
+{
+ unsigned size = PAGE_SIZE;
+ unsigned wrote;
+ unsigned i;
+ u32 val;
+ char *str;
+ static const struct {
+ char *fmt;
+ unsigned val_index;
+ } to_output[] = {
+ { "super block size : %u\n", 1 },
+ { "super page size : %u\n", 2 },
+ { "optimal write size : %u\n", 3 },
+ { "read reclaim count : %u\n", 20 },
+ { "optimal trim size : %u\n", 21 },
+ { "number of banks : %u\n", 4 },
+ { "initial bad blocks per bank : %u", 5 },
+ { ",%u", 8 },
+ { ",%u", 11 },
+ { ",%u\n", 14 },
+ { "runtime bad blocks per bank : %u", 6 },
+ { ",%u", 9 },
+ { ",%u", 12 },
+ { ",%u\n", 15 },
+ { "reserved blocks left per bank : %u", 7 },
+ { ",%u", 10 },
+ { ",%u", 13 },
+ { ",%u\n", 16 },
+ { "all erase counts (min,avg,max): %u", 18 },
+ { ",%u", 19 },
+ { ",%u\n", 17 },
+ { "SLC erase counts (min,avg,max): %u", 31 },
+ { ",%u", 32 },
+ { ",%u\n", 30 },
+ { "MLC erase counts (min,avg,max): %u", 34 },
+ { ",%u", 35 },
+ { ",%u\n", 33 },
+ };
+
+ /* A version field just in case things change. */
+ wrote = scnprintf(for_sysfs, size,
+ "version : %u\n", 0);
+ size -= wrote;
+ for_sysfs += wrote;
+
+ /* The error mode. */
+ val = le32_to_cpu(report[0]);
+ switch (val) {
+ case 0xD2D2D2D2:
+ str = "Normal";
+ break;
+ case 0x5C5C5C5C:
+ str = "RuntimeFatalError";
+ break;
+ case 0xE1E1E1E1:
+ str = "MetaBrokenError";
+ break;
+ case 0x37373737:
+ str = "OpenFatalError";
+ val = 0; /* Remaining data is unreliable. */
+ break;
+ default:
+ str = "Invalid";
+ val = 0; /* Remaining data is unreliable. */
+ break;
+ }
+ wrote = scnprintf(for_sysfs, size,
+ "error mode : %s\n", str);
+ size -= wrote;
+ for_sysfs += wrote;
+ /* Exit if we can't rely on the remaining data. */
+ if (!val)
+ return PAGE_SIZE - size;
+
+ for (i = 0; i < ARRAY_SIZE(to_output); i++) {
+ wrote = scnprintf(for_sysfs, size, to_output[i].fmt,
+ le32_to_cpu(report[to_output[i].val_index]));
+ size -= wrote;
+ for_sysfs += wrote;
+ }
+
+ return PAGE_SIZE - size;
+}
+ssize_t mmc_samsung_smart_handle(struct mmc_card *card, char *buf)
+{
+ int err;
+ u32 *buffer;
+ ssize_t len;
+
+ buffer = kmalloc(512, GFP_KERNEL);
+ if (!buffer) {
+ pr_err("Failed to alloc memory for Smart Report\n");
+ return 0;
+ }
+
+ mmc_claim_host(card->host);
+ err = mmc_samsung_smart_read(card, (u8 *)buffer);
+ mmc_release_host(card->host);
+
+ if (err)
+ len = 0;
+ else
+ len = mmc_samsung_smart_parse(buffer, buf);
+
+ kfree(buffer);
+ return len;
+}
+#endif /* CONFIG_MMC_SAMSUNG_SMART */