diff options
author | Scott Anderson <saa@android.com> | 2012-03-20 08:41:58 -0700 |
---|---|---|
committer | Ziyann <jaraidaniel@gmail.com> | 2014-11-26 21:10:53 +0100 |
commit | 3cd4f37e5fef9cebee81d09a7c8cb70d3bfc23e8 (patch) | |
tree | 008b30b0065660d305f87f5b607a55e0bb707d3f /drivers/mmc | |
parent | 91bbab001bfa2139fdd69d3c5a431e925520b0a8 (diff) | |
download | kernel_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/Kconfig | 6 | ||||
-rw-r--r-- | drivers/mmc/core/mmc.c | 26 | ||||
-rw-r--r-- | drivers/mmc/core/quirks.c | 151 |
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 */ |