From da8461692362317a8ffce4d4646953985fcf4e1d Mon Sep 17 00:00:00 2001 From: Samsung OSRC Date: Tue, 8 Jan 2013 23:54:22 +0100 Subject: mmc: Soft-patch MoviNAND VTU00M (16GB) eMMC failure Signed-off-by: Andrei F --- drivers/mmc/core/mmc.c | 34 ++++++++ drivers/mmc/core/mmc_ops.c | 201 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/mmc/card.h | 2 + include/linux/mmc/core.h | 2 + 4 files changed, 239 insertions(+) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 81a77a8f456..3403f53a52d 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "core.h" #include "bus.h" @@ -107,6 +108,7 @@ static int mmc_decode_cid(struct mmc_card *card) card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8); card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8); card->cid.prod_name[5] = UNSTUFF_BITS(resp, 56, 8); + card->cid.prod_rev = UNSTUFF_BITS(resp, 48, 8); card->cid.serial = UNSTUFF_BITS(resp, 16, 32); card->cid.month = UNSTUFF_BITS(resp, 12, 4); card->cid.year = UNSTUFF_BITS(resp, 8, 4) + 1997; @@ -1492,6 +1494,15 @@ static int mmc_resume(struct mmc_host *host) mmc_claim_host(host); err = mmc_init_card(host, host->ocr, host->card); + + if (host->card->movi_ops == 0x2) { + err = mmc_start_movi_operation(host->card); + if (err) { + pr_warning("%s: movi operation is failed\n", + mmc_hostname(host)); + } + } + mmc_release_host(host); return err; @@ -1504,6 +1515,15 @@ static int mmc_power_restore(struct mmc_host *host) host->card->state &= ~MMC_STATE_HIGHSPEED; mmc_claim_host(host); ret = mmc_init_card(host, host->ocr, host->card); + + if (host->card->movi_ops == 0x2) { + ret = mmc_start_movi_operation(host->card); + if (ret) { + pr_warning("%s: movi operation is failed\n", + mmc_hostname(host)); + } + } + mmc_release_host(host); return ret; @@ -1634,6 +1654,20 @@ int mmc_attach_mmc(struct mmc_host *host) if (err) goto remove_card; + if (!strncmp(host->card->cid.prod_name, "VTU00M", 6) && + (host->card->cid.prod_rev == 0xf1) && + (mmc_start_movi_smart(host->card) == 0x2)) + host->card->movi_ops = 0x2; + + if (host->card->movi_ops == 0x2) { + err = mmc_start_movi_operation(host->card); + if (err) { + pr_warning("%s: movi operation is failed\n", + mmc_hostname(host)); + goto remove_card; + } + } + return 0; remove_card: diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 15b64318f2d..90085a3714a 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -555,6 +555,207 @@ int mmc_bus_test(struct mmc_card *card, u8 bus_width) return err; } +static int mmc_send_cmd(struct mmc_host *host, + u32 opcode, u32 arg, unsigned int flags, u32 *resp) +{ + int err; + struct mmc_command cmd; + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = opcode; + cmd.arg = arg; + cmd.flags = flags; + *resp = 0; + + err = mmc_wait_for_cmd(host, &cmd, 0); + if (!err) + *resp = cmd.resp[0]; + else + printk(KERN_ERR "[CMD%d] FAILED!!\n", cmd.opcode); + + return err; +} + +static int mmc_movi_cmd(struct mmc_host *host, u32 arg) +{ + int err; + u32 resp; + + err = mmc_send_cmd(host, 62, arg, + MMC_RSP_R1B | MMC_CMD_AC, &resp); + mdelay(10); + + if (!err) + do { + err = mmc_send_cmd(host, MMC_SEND_STATUS, + host->card->rca << 16, + MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC, + &resp); + if (err) { + printk(KERN_ERR "CMD13(VC) failed\n"); + break; + } + /*wait until READY_FOR_DATA*/ + } while (!(resp & 1<<8)); + + return err; +} + +static int mmc_movi_erase_cmd(struct mmc_host *host, u32 arg1, u32 arg2) +{ + int err; + u32 resp; + + err = mmc_send_cmd(host, MMC_ERASE_GROUP_START, arg1, + MMC_RSP_R1 | MMC_CMD_AC, &resp); + if (err) + return err; + + err = mmc_send_cmd(host, MMC_ERASE_GROUP_END, arg2, + MMC_RSP_R1 | MMC_CMD_AC, &resp); + if (err) + return err; + + err = mmc_send_cmd(host, MMC_ERASE, 0, + MMC_RSP_R1B | MMC_CMD_AC, &resp); + if (!err) + do { + err = mmc_send_cmd(host, MMC_SEND_STATUS, + host->card->rca << 16, + MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC, + &resp); + if (err) { + printk(KERN_ERR "CMD13(VC) failed\n"); + break; + } + /*wait until READY_FOR_DATA*/ + } while (!(resp & 1<<8)); + + return err; +} + + +static int mmc_movi_read_req(struct mmc_card *card, + void *data_buf, u32 arg, u32 blocks) +{ + struct mmc_request mrq = {0}; + struct mmc_command cmd = {0}; + struct mmc_data data = {0}; + struct scatterlist sg; + + /*send request*/ + mrq.cmd = &cmd; + mrq.data = &data; + + if (blocks > 1) + cmd.opcode = MMC_READ_MULTIPLE_BLOCK; + else + cmd.opcode = MMC_READ_SINGLE_BLOCK; + cmd.arg = arg; + + cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; + + data.blksz = 512; + data.blocks = blocks; + data.flags = MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; + + sg_init_one(&sg, data_buf, data.blksz * data.blocks); + + mmc_set_data_timeout(&data, card); + + mmc_wait_for_req(card->host, &mrq); + + if (cmd.error) + return cmd.error; + + if (data.error) + return data.error; + + return 0; +} + +int mmc_start_movi_smart(struct mmc_card *card) +{ + int err; + u8 data_buf[512]; + u32 date = 0; + + err = mmc_movi_cmd(card->host, 0xEFAC62EC); + if (err) + return err; + + err = mmc_movi_cmd(card->host, 0x0000CCEE); + if (err) + return err; + + err = mmc_movi_read_req(card, (void *)data_buf, 0x1000, 1); + if (err) + return err; + + err = mmc_movi_cmd(card->host, 0xEFAC62EC); + if (err) + return err; + + err = mmc_movi_cmd(card->host, 0x00DECCEE); + if (err) + return err; + + date = ((data_buf[327] << 24) | (data_buf[326] << 16) | + (data_buf[325] << 8) | data_buf[324]); + + if (date != 0x20120413) { + err = -1; + return err; + } + + return 0x2; +} +EXPORT_SYMBOL_GPL(mmc_start_movi_smart); + +int mmc_start_movi_operation(struct mmc_card *card) +{ + int err = 0; + + err = mmc_movi_cmd(card->host, 0xEFAC62EC); + if (err) + return err; + err = mmc_movi_cmd(card->host, 0x10210000); + if (err) + return err; + + err = mmc_movi_erase_cmd(card->host, 0x00040300, 0x4A03B510); + if (err) + return err; + err = mmc_movi_erase_cmd(card->host, 0x00040304, 0x28004790); + if (err) + return err; + err = mmc_movi_erase_cmd(card->host, 0x00040308, 0xE7FED100); + if (err) + return err; + err = mmc_movi_erase_cmd(card->host, 0x0004030C, 0x0000BD10); + if (err) + return err; + err = mmc_movi_erase_cmd(card->host, 0x00040310, 0x00059D73); + if (err) + return err; + err = mmc_movi_erase_cmd(card->host, 0x0005C7EA, 0xFD89F7E3); + if (err) + return err; + + err = mmc_movi_cmd(card->host, 0xEFAC62EC); + if (err) + return err; + err = mmc_movi_cmd(card->host, 0x00DECCEE); + if (err) + return err; + + return err; +} +EXPORT_SYMBOL_GPL(mmc_start_movi_operation); + int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status) { struct mmc_command cmd = {0}; diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 942cd59b297..5eb40a97cee 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -16,6 +16,7 @@ struct mmc_cid { unsigned int manfid; char prod_name[8]; + unsigned short prod_rev; unsigned int serial; unsigned short oemid; unsigned short year; @@ -242,6 +243,7 @@ struct mmc_card { unsigned int sd_bus_speed; /* Bus Speed Mode set for the card */ struct dentry *debugfs_root; + unsigned int movi_ops; }; /* diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index 3d055c36e6a..24bd2d5f2b8 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -146,6 +146,8 @@ extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *, struct mmc_command *, int); extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int); extern int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd); +extern int mmc_start_movi_smart(struct mmc_card *card); +extern int mmc_start_movi_operation(struct mmc_card *card); #define MMC_ERASE_ARG 0x00000000 #define MMC_SECURE_ERASE_ARG 0x80000000 -- cgit v1.2.3