summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mmc/mmc.c334
1 files changed, 134 insertions, 200 deletions
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index 6299635..6fc3be2 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -35,11 +35,18 @@
#include <linux/list.h>
#include <mmc.h>
#include <div64.h>
-#include <fsl_esdhc.h>
static struct list_head mmc_devices;
static int cur_dev_num = -1;
+int __board_mmc_getcd(u8 *cd, struct mmc *mmc)
+{
+ return -1;
+}
+
+int board_mmc_getcd(u8 *cd, struct mmc *mmc)__attribute__((weak,
+ alias("__board_mmc_getcd")));
+
static int mmc_send_cmd(struct mmc *mmc,
struct mmc_cmd *cmd, struct mmc_data *data)
{
@@ -76,18 +83,63 @@ struct mmc *find_mmc_device(int dev_num)
}
static ulong
-mmc_bwrite(int dev_num, ulong start, lbaint_t blkcnt, const void*src)
+mmc_write_blocks(struct mmc *mmc, ulong start, lbaint_t blkcnt, const void*src)
{
struct mmc_cmd cmd;
struct mmc_data data;
- int err;
- int stoperr = 0;
- struct mmc *mmc = find_mmc_device(dev_num);
- int blklen;
- lbaint_t blk_offset = 0, blk_left = blkcnt;
+ if ((start + blkcnt) > mmc->block_dev.lba) {
+ printf("MMC: block number 0x%lx exceeds max(0x%lx)\n",
+ start + blkcnt, mmc->block_dev.lba);
+ return 0;
+ }
+
+ if (blkcnt > 1)
+ cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK;
+ else
+ cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK;
+
+ if (mmc->high_capacity)
+ cmd.cmdarg = start;
+ else
+ cmd.cmdarg = start * mmc->write_bl_len;
+
+ cmd.resp_type = MMC_RSP_R1;
+ cmd.flags = 0;
+
+ data.src = src;
+ data.blocks = blkcnt;
+ data.blocksize = mmc->write_bl_len;
+ data.flags = MMC_DATA_WRITE;
+
+ if (mmc_send_cmd(mmc, &cmd, &data)) {
+ printf("mmc write failed\n");
+ return 0;
+ }
+
+ if (blkcnt > 1) {
+ cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
+ cmd.cmdarg = 0;
+ cmd.resp_type = MMC_RSP_R1b;
+ cmd.flags = 0;
+ if (mmc_send_cmd(mmc, &cmd, NULL)) {
+ printf("mmc fail to send stop cmd\n");
+ return 0;
+ }
+ }
+
+ return blkcnt;
+}
+
+static ulong
+mmc_bwrite(int dev_num, ulong start, lbaint_t blkcnt, const void *src)
+{
+ lbaint_t cur, blocks_todo = blkcnt;
+ int err = 0, blklen;
+
+ struct mmc *mmc = find_mmc_device(dev_num);
if (!mmc)
- return -1;
+ return 0;
blklen = mmc->write_bl_len;
@@ -104,144 +156,79 @@ mmc_bwrite(int dev_num, ulong start, lbaint_t blkcnt, const void*src)
}
do {
- cmd.cmdidx = (blk_left > 1) \
- ? MMC_CMD_WRITE_MULTIPLE_BLOCK \
- : MMC_CMD_WRITE_SINGLE_BLOCK;
-
- cmd.cmdarg = (mmc->high_capacity) \
- ? (start + blk_offset) \
- : ((start + blk_offset) * blklen);
-
- cmd.resp_type = MMC_RSP_R1;
- cmd.flags = 0;
-
- data.src = src + blk_offset * blklen;
- data.blocks = (blk_left > MAX_BLK_CNT) \
- ? MAX_BLK_CNT : blk_left;
- data.blocksize = blklen;
- data.flags = MMC_DATA_WRITE;
-
- err = mmc_send_cmd(mmc, &cmd, &data);
-
- if (err) {
- puts("mmc write failed\n\r");
- return err;
- }
-
- if (blk_left > 1) {
- cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
- cmd.cmdarg = 0;
- cmd.resp_type = MMC_RSP_R1b;
- cmd.flags = 0;
- stoperr = mmc_send_cmd(mmc, &cmd, NULL);
- }
-
- if (blk_left > MAX_BLK_CNT) {
- blk_left -= MAX_BLK_CNT;
- blk_offset += MAX_BLK_CNT;
- } else
- break;
- } while (blk_left > 0);
+ /*
+ * The 65535 constraint comes from some hardware has
+ * only 16 bit width block number counter
+ */
+ cur = (blocks_todo > 65535) ? 65535 : blocks_todo;
+ if (mmc_write_blocks(mmc, start, cur, src) != cur)
+ return 0;
+ blocks_todo -= cur;
+ start += cur;
+ src += cur * mmc->write_bl_len;
+ } while (blocks_todo > 0);
return blkcnt;
}
-static int mmc_read_block(struct mmc *mmc, void *dst, uint blocknum)
+static int mmc_read_blocks(struct mmc *mmc, void *dst,
+ ulong start, lbaint_t blkcnt)
{
struct mmc_cmd cmd;
struct mmc_data data;
- cmd.cmdidx = MMC_CMD_READ_SINGLE_BLOCK;
+ if (blkcnt > 1)
+ cmd.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK;
+ else
+ cmd.cmdidx = MMC_CMD_READ_SINGLE_BLOCK;
if (mmc->high_capacity)
- cmd.cmdarg = blocknum;
+ cmd.cmdarg = start;
else
- cmd.cmdarg = blocknum * mmc->read_bl_len;
+ cmd.cmdarg = start * mmc->read_bl_len;
cmd.resp_type = MMC_RSP_R1;
cmd.flags = 0;
data.dest = dst;
- data.blocks = 1;
+ data.blocks = blkcnt;
data.blocksize = mmc->read_bl_len;
data.flags = MMC_DATA_READ;
- return mmc_send_cmd(mmc, &cmd, &data);
-}
-
-int mmc_read(struct mmc *mmc, u64 src, uchar *dst, int size)
-{
- char *buffer;
- int i;
- int blklen = mmc->read_bl_len;
- int startblock = lldiv(src, mmc->read_bl_len);
- int endblock = lldiv(src + size - 1, mmc->read_bl_len);
- int err = 0;
-
- if (mmc->bus_width == EMMC_MODE_4BIT_DDR ||
- mmc->bus_width == EMMC_MODE_8BIT_DDR)
- blklen = 512;
-
- /* Make a buffer big enough to hold all the blocks we might read */
- buffer = malloc(blklen);
-
- if (!buffer) {
- printf("Could not allocate buffer for MMC read!\n");
- return -1;
- }
-
- if (mmc->bus_width == EMMC_MODE_4BIT_DDR ||
- mmc->bus_width == EMMC_MODE_8BIT_DDR)
- err = 0;
- else {
- /* We always do full block reads from the card */
- err = mmc_set_blocklen(mmc, mmc->read_bl_len);
- }
-
- if (err)
- goto free_buffer;
-
- for (i = startblock; i <= endblock; i++) {
- int segment_size;
- int offset;
-
- err = mmc_read_block(mmc, buffer, i);
-
- if (err)
- goto free_buffer;
-
- /*
- * The first block may not be aligned, so we
- * copy from the desired point in the block
- */
- offset = (src & (blklen - 1));
- segment_size = MIN(blklen - offset, size);
-
- memcpy(dst, buffer + offset, segment_size);
+ if (mmc_send_cmd(mmc, &cmd, &data))
+ return 0;
- dst += segment_size;
- src += segment_size;
- size -= segment_size;
+ if (blkcnt > 1) {
+ cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
+ cmd.cmdarg = 0;
+ cmd.resp_type = MMC_RSP_R1b;
+ cmd.flags = 0;
+ if (mmc_send_cmd(mmc, &cmd, NULL)) {
+ printf("mmc fail to send stop cmd\n");
+ return 0;
+ }
}
-free_buffer:
- free(buffer);
-
- return err;
+ return blkcnt;
}
static ulong mmc_bread(int dev_num, ulong start, lbaint_t blkcnt, void *dst)
{
- struct mmc_cmd cmd;
- struct mmc_data data;
- int err;
- int stoperr = 0;
- struct mmc *mmc = find_mmc_device(dev_num);
- int blklen;
- lbaint_t blk_offset = 0, blk_left = blkcnt;
+ lbaint_t cur, blocks_todo = blkcnt;
+ int err = 0, blklen;
+
+ if (blkcnt == 0)
+ return 0;
+ struct mmc *mmc = find_mmc_device(dev_num);
if (!mmc)
- return -1;
+ return 0;
+
+ if ((start + blkcnt) > mmc->block_dev.lba) {
+ printf("MMC: block number 0x%lx exceeds max(0x%lx)\n",
+ start + blkcnt, mmc->block_dev.lba);
+ return 0;
+ }
if (mmc->bus_width == EMMC_MODE_4BIT_DDR ||
mmc->bus_width == EMMC_MODE_8BIT_DDR) {
@@ -258,49 +245,21 @@ static ulong mmc_bread(int dev_num, ulong start, lbaint_t blkcnt, void *dst)
}
do {
- cmd.cmdidx = (blk_left > 1) \
- ? MMC_CMD_READ_MULTIPLE_BLOCK \
- : MMC_CMD_READ_SINGLE_BLOCK;
-
- cmd.cmdarg = (mmc->high_capacity) \
- ? (start + blk_offset) \
- : ((start + blk_offset) * blklen);
-
- cmd.resp_type = MMC_RSP_R1;
- cmd.flags = 0;
-
- data.dest = dst + blk_offset * blklen;
- data.blocks = (blk_left > MAX_BLK_CNT) ? MAX_BLK_CNT : blk_left;
- data.blocksize = blklen;
- data.flags = MMC_DATA_READ;
-
- err = mmc_send_cmd(mmc, &cmd, &data);
-
- if (err) {
- puts("mmc read failed\n\r");
- return err;
- }
-
- if (blk_left > 1) {
- cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
- cmd.cmdarg = 0;
- cmd.resp_type = MMC_RSP_R1b;
- cmd.flags = 0;
- stoperr = mmc_send_cmd(mmc, &cmd, NULL);
- }
-
- if (blk_left > MAX_BLK_CNT) {
- blk_left -= MAX_BLK_CNT;
- blk_offset += MAX_BLK_CNT;
- } else
- break;
- } while (blk_left > 0);
+ /*
+ * The 65535 constraint comes from some hardware has
+ * only 16 bit width block number counter
+ */
+ cur = (blocks_todo > 65535) ? 65535 : blocks_todo;
+ if (mmc_read_blocks(mmc, dst, start, cur) != cur)
+ return 0;
+ blocks_todo -= cur;
+ start += cur;
+ dst += cur * mmc->read_bl_len;
+ } while (blocks_todo > 0);
return blkcnt;
}
-#define CARD_STATE(r) ((u32)((r) & 0x1e00) >> 9)
-
static int mmc_go_idle(struct mmc *mmc)
{
struct mmc_cmd cmd;
@@ -478,13 +437,8 @@ static int mmc_change_freq(struct mmc *mmc)
if (err)
goto err_rtn;
- if (mmc->high_capacity) {
- mmc->capacity = ext_csd[EXT_CSD_SEC_CNT + 3] << 24 |
- ext_csd[EXT_CSD_SEC_CNT + 2] << 16 |
- ext_csd[EXT_CSD_SEC_CNT + 1] << 8 |
- ext_csd[EXT_CSD_SEC_CNT];
- mmc->capacity *= 512;
- }
+ if (ext_csd[212] || ext_csd[213] || ext_csd[214] || ext_csd[215])
+ mmc->high_capacity = 1;
cardtype = ext_csd[EXT_CSD_CARD_TYPE] & 0xf;
@@ -680,7 +634,7 @@ static void mmc_set_ios(struct mmc *mmc)
mmc->set_ios(mmc);
}
-static void mmc_set_clock(struct mmc *mmc, uint clock)
+void mmc_set_clock(struct mmc *mmc, uint clock)
{
if (clock > mmc->f_max)
clock = mmc->f_max;
@@ -897,6 +851,7 @@ static int mmc_startup(struct mmc *mmc)
uint mult, freq;
u64 cmult, csize;
struct mmc_cmd cmd;
+ char ext_csd[512];
/* Put the Card in Identify Mode */
cmd.cmdidx = MMC_CMD_ALL_SEND_CID;
@@ -983,25 +938,10 @@ static int mmc_startup(struct mmc *mmc)
else
mmc->write_bl_len = 1 << ((cmd.response[3] >> 22) & 0xf);
- if (IS_SD(mmc)) {
- int csd_struct = (cmd.response[0] >> 30) & 0x3;
-
- switch (csd_struct) {
- case 1:
- csize = (mmc->csd[1] & 0x3f) << 16
- | (mmc->csd[2] & 0xffff0000) >> 16;
- cmult = 8;
- break;
- case 0:
- default:
- if (0 != csd_struct)
- printf("unrecognised CSD structure version %d\n",
- csd_struct);
- csize = (mmc->csd[1] & 0x3ff) << 2
- | (mmc->csd[2] & 0xc0000000) >> 30;
- cmult = (mmc->csd[2] & 0x00038000) >> 15;
- break;
- }
+ if (mmc->high_capacity) {
+ csize = (mmc->csd[1] & 0x3f) << 16
+ | (mmc->csd[2] & 0xffff0000) >> 16;
+ cmult = 8;
} else {
csize = (mmc->csd[1] & 0x3ff) << 2
| (mmc->csd[2] & 0xc0000000) >> 30;
@@ -1027,6 +967,16 @@ static int mmc_startup(struct mmc *mmc)
if (err)
return err;
+ if (!IS_SD(mmc) && (mmc->version >= MMC_VERSION_4)) {
+ /* check ext_csd version and capacity */
+ err = mmc_send_ext_csd(mmc, ext_csd);
+ if (!err & (ext_csd[192] >= 2)) {
+ mmc->capacity = ext_csd[212] << 0 | ext_csd[213] << 8 |
+ ext_csd[214] << 16 | ext_csd[215] << 24;
+ mmc->capacity *= 512;
+ }
+ }
+
if (IS_SD(mmc))
err = sd_change_freq(mmc);
else
@@ -1172,22 +1122,6 @@ int mmc_register(struct mmc *mmc)
mmc->block_dev.removable = 1;
mmc->block_dev.block_read = mmc_bread;
mmc->block_dev.block_write = mmc_bwrite;
-#if defined(CONFIG_DOS_PARTITION)
- mmc->block_dev.part_type = PART_TYPE_DOS;
- mmc->block_dev.type = DEV_TYPE_HARDDISK;
-#elif defined(CONFIG_MAC_PARTITION)
- mmc->block_dev.part_type = PART_TYPE_MAC;
- mmc->block_dev.type = DEV_TYPE_HARDDISK;
-#elif defined(CONFIG_ISO_PARTITION)
- mmc->block_dev.part_type = PART_TYPE_ISO;
- mmc->block_dev.type = DEV_TYPE_HARDDISK;
-#elif defined(CONFIG_AMIGA_PARTITION)
- mmc->block_dev.part_type = PART_TYPE_AMIGA;
- mmc->block_dev.type = DEV_TYPE_HARDDISK;
-#elif defined(CONFIG_EFI_PARTITION)
- mmc->block_dev.part_type = PART_TYPE_EFI;
- mmc->block_dev.type = DEV_TYPE_HARDDISK;
-#endif
INIT_LIST_HEAD (&mmc->link);