Split-up accesses into smaller chunks, to improve lifespan, by using 8K reliable writes into 8K Buffer A, instead of 4MB Buffer B, reducing number of 4MB write-erase cycles. Upper and lower bounds should be experimentally found to match the desired performance/reliability characteristics. Signed-off-by: Andrei Warkentin <andreiw@xxxxxxxxxxxx> --- drivers/mmc/card/Kconfig | 31 ++++++++++++++++++++++++ drivers/mmc/card/block-quirks.c | 50 +++++++++++++++++++++++++++++++++++++- 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/card/Kconfig b/drivers/mmc/card/Kconfig index af2800e..5c2f090 100644 --- a/drivers/mmc/card/Kconfig +++ b/drivers/mmc/card/Kconfig @@ -29,6 +29,37 @@ config MMC_BLOCK_QUIRK_TOSHIBA_32NM Say Y if you have a Toshiba 32nm technology flash device, such as MMC32G or MMC16G eMMCs. +config MMC_BLOCK_QUIRK_TOSHIBA_32NM_REL + tristate "Toshiba MMC 32nm reliability quirk" + depends on MMC_BLOCK_QUIRK_TOSHIBA_32NM + default n + help + Say Y if you want to enable the improved reliability workaround + for your Toshiba 32nm parts. This will result in certain-sized + writes to be split up into 8K chunks, to ensure they are placed + in the smaller 8KB buffer instead of the 4MB buffer, which should + reduce the number of flash write-erase cycles and improve + reliability. By default accesses in the 24k-32k range are + split. + +config MMC_BLOCK_QUIRK_TOSHIBA_32NM_REL_L + hex "Toshiba reliability lower bound (in blocks)" + depends on MMC_BLOCK_QUIRK_TOSHIBA_32NM_REL + default "30" + help + Accesses smaller than the lower bound will not be split. + This value should be experimentally found to match load + and performance characteristics. + +config MMC_BLOCK_QUIRK_TOSHIBA_32NM_REL_U + hex "Toshiba reliability upper bound (in blocks)" + depends on MMC_BLOCK_QUIRK_TOSHIBA_32NM_REL + default "40" + help + Accesses bigger than the upper bound will not be split. + This value should be experimentally found to match load + and performance characteristics. + config MMC_BLOCK_BOUNCE bool "Use bounce buffer for simple hosts" depends on MMC_BLOCK diff --git a/drivers/mmc/card/block-quirks.c b/drivers/mmc/card/block-quirks.c index 4afa872..c918e12 100644 --- a/drivers/mmc/card/block-quirks.c +++ b/drivers/mmc/card/block-quirks.c @@ -9,8 +9,10 @@ * */ #include <linux/kernel.h> +#include <linux/blkdev.h> #include <linux/semaphore.h> #include <linux/mmc/card.h> +#include <linux/mmc/mmc.h> #include "queue.h" #include "blk.h" @@ -19,6 +21,11 @@ static int toshiba_32nm_probe(struct mmc_blk_data *md, struct mmc_card *card) { printk(KERN_INFO "Applying Toshiba 32nm workarounds\n"); +#ifdef CONFIG_MMC_BLOCK_QUIRK_TOSHIBA_32NM_REL + printk(KERN_INFO "Toshiba 32nm reliability splits over 0x%x-0x%x blocks\n", + CONFIG_MMC_BLOCK_QUIRK_TOSHIBA_32NM_REL_L, + CONFIG_MMC_BLOCK_QUIRK_TOSHIBA_32NM_REL_U); +#endif /* Page size 8K, this card doesn't like unaligned writes across 8K boundary. */ @@ -29,6 +36,43 @@ static int toshiba_32nm_probe(struct mmc_blk_data *md, struct mmc_card *card) md->write_align_limit = 12288; return 0; } + +#ifdef CONFIG_MMC_BLOCK_QUIRK_TOSHIBA_32NM_REL +static void toshiba_32nm_adjust(struct mmc_queue *mq, + struct request *req, + struct mmc_request *mrq) +{ + + int err; + struct mmc_command cmd; + struct mmc_blk_data *md = mq->data; + struct mmc_card *card = md->queue.card; + + if (rq_data_dir(req) != WRITE) + return; + + if (blk_rq_sectors(req) > CONFIG_MMC_BLOCK_QUIRK_TOSHIBA_32NM_REL_U || + blk_rq_sectors(req) < CONFIG_MMC_BLOCK_QUIRK_TOSHIBA_32NM_REL_L) + return; + + /* 8K chunks */ + if (mrq->data->blocks > 16) + mrq->data->blocks = 16; + + /* + We know what the valid values for this card are, + no need to check EXT_CSD_REL_WR_SEC_C. + */ + cmd.opcode = MMC_SET_BLOCK_COUNT | (1 << 31); + cmd.arg = mrq->data->blocks; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + err = mmc_wait_for_cmd(card->host, &cmd, 0); + if (!err) + mrq->stop = NULL; +} +#else +#define toshiba_32nm_adjust NULL +#endif /* CONFIG_MMC_BLOCK_QUIRK_TOSHIBA_32NM_REL */ #endif /* CONFIG_MMC_BLOCK_QUIRK_TOSHIBA_32NM */ /* @@ -39,8 +83,10 @@ static int toshiba_32nm_probe(struct mmc_blk_data *md, struct mmc_card *card) */ struct mmc_blk_quirk mmc_blk_quirks[] = { #ifdef CONFIG_MMC_BLOCK_QUIRK_TOSHIBA_32NM - MMC_BLK_QUIRK("MMC16G", 0x11, 0x0, toshiba_32nm_probe, NULL), - MMC_BLK_QUIRK("MMC32G", 0x11, 0x0100, toshiba_32nm_probe, NULL), + MMC_BLK_QUIRK("MMC16G", 0x11, 0x0, + toshiba_32nm_probe, toshiba_32nm_adjust), + MMC_BLK_QUIRK("MMC32G", 0x11, 0x0100, + toshiba_32nm_probe, toshiba_32nm_adjust), #endif /* CONFIG_MMC_BLOCK_QUIRK_TOSHIBA_32NM */ }; -- 1.7.0.4 -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html