aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSamsung OSRC <osrc@samsung.com>2013-01-08 23:54:22 +0100
committerAndrew Dodd <atd7@cornell.edu>2013-01-08 21:59:46 -0500
commitda8461692362317a8ffce4d4646953985fcf4e1d (patch)
treeee2cd8c091f04768bb9d12c019ac9917194d0857
parent126b49a7dfaf964d4c82e8b2a70524ef1de27cba (diff)
downloadkernel_samsung_smdk4412-da8461692362317a8ffce4d4646953985fcf4e1d.tar.gz
kernel_samsung_smdk4412-da8461692362317a8ffce4d4646953985fcf4e1d.tar.bz2
kernel_samsung_smdk4412-da8461692362317a8ffce4d4646953985fcf4e1d.zip
mmc: Soft-patch MoviNAND VTU00M (16GB) eMMC failure
Signed-off-by: Andrei F <luxneb@gmail.com>
-rw-r--r--drivers/mmc/core/mmc.c34
-rw-r--r--drivers/mmc/core/mmc_ops.c201
-rw-r--r--include/linux/mmc/card.h2
-rw-r--r--include/linux/mmc/core.h2
4 files changed, 239 insertions, 0 deletions
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 <linux/mmc/host.h>
#include <linux/mmc/card.h>
#include <linux/mmc/mmc.h>
+#include <linux/string.h>
#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