#include <config.h>
#include <common.h>
#include <command.h>
+#include <dm.h>
+#include <dm/device-internal.h>
#include <errno.h>
#include <mmc.h>
#include <part.h>
#include <malloc.h>
+#include <memalign.h>
#include <linux/list.h>
#include <div64.h>
#include "mmc_private.h"
-static struct list_head mmc_devices;
-static int cur_dev_num = -1;
-
__weak int board_mmc_getwp(struct mmc *mmc)
{
return -1;
printf("CMD_SEND:%d\n", cmd->cmdidx);
printf("\t\tARG\t\t\t 0x%08X\n", cmd->cmdarg);
ret = mmc->cfg->ops->send_cmd(mmc, cmd, data);
- switch (cmd->resp_type) {
+ if (ret) {
+ printf("\t\tRET\t\t\t %d\n", ret);
+ } else {
+ switch (cmd->resp_type) {
case MMC_RSP_NONE:
printf("\t\tMMC_RSP_NONE\n");
break;
default:
printf("\t\tERROR MMC rsp not supported\n");
break;
+ }
}
#else
ret = mmc->cfg->ops->send_cmd(mmc, cmd, data);
if (!mmc_host_is_spi(mmc))
cmd.cmdarg = mmc->rca << 16;
- do {
+ while (1) {
err = mmc_send_cmd(mmc, &cmd, NULL);
if (!err) {
if ((cmd.response[0] & MMC_STATUS_RDY_FOR_DATA) &&
} else if (--retries < 0)
return err;
- udelay(1000);
+ if (timeout-- <= 0)
+ break;
- } while (timeout--);
+ udelay(1000);
+ }
#ifdef CONFIG_MMC_TRACE
status = (cmd.response[0] & MMC_STATUS_CURR_STATE) >> 9;
return mmc_send_cmd(mmc, &cmd, NULL);
}
-struct mmc *find_mmc_device(int dev_num)
-{
- struct mmc *m;
- struct list_head *entry;
-
- list_for_each(entry, &mmc_devices) {
- m = list_entry(entry, struct mmc, link);
-
- if (m->block_dev.dev == dev_num)
- return m;
- }
-
-#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
- printf("MMC Device %d not found\n", dev_num);
-#endif
-
- return NULL;
-}
-
static int mmc_read_blocks(struct mmc *mmc, void *dst, lbaint_t start,
lbaint_t blkcnt)
{
return blkcnt;
}
-static ulong mmc_bread(int dev_num, lbaint_t start, lbaint_t blkcnt, void *dst)
+static ulong mmc_bread(struct blk_desc *block_dev, lbaint_t start,
+ lbaint_t blkcnt, void *dst)
{
+ int dev_num = block_dev->devnum;
+ int err;
lbaint_t cur, blocks_todo = blkcnt;
if (blkcnt == 0)
if (!mmc)
return 0;
- if ((start + blkcnt) > mmc->block_dev.lba) {
+ err = blk_dselect_hwpart(block_dev, block_dev->hwpart);
+ if (err < 0)
+ return 0;
+
+ if ((start + blkcnt) > block_dev->lba) {
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n",
- start + blkcnt, mmc->block_dev.lba);
+ start + blkcnt, block_dev->lba);
#endif
return 0;
}
- if (mmc_set_blocklen(mmc, mmc->read_bl_len))
+ if (mmc_set_blocklen(mmc, mmc->read_bl_len)) {
+ debug("%s: Failed to set blocklen\n", __func__);
return 0;
+ }
do {
cur = (blocks_todo > mmc->cfg->b_max) ?
mmc->cfg->b_max : blocks_todo;
- if(mmc_read_blocks(mmc, dst, start, cur) != cur)
+ if (mmc_read_blocks(mmc, dst, start, cur) != cur) {
+ debug("%s: Failed to read blocks\n", __func__);
return 0;
+ }
blocks_todo -= cur;
start += cur;
dst += cur * mmc->read_bl_len;
int err;
struct mmc_cmd cmd;
- do {
+ while (1) {
cmd.cmdidx = MMC_CMD_APP_CMD;
cmd.resp_type = MMC_RSP_R1;
cmd.cmdarg = 0;
if (err)
return err;
- udelay(1000);
- } while ((!(cmd.response[0] & OCR_BUSY)) && timeout--);
+ if (cmd.response[0] & OCR_BUSY)
+ break;
- if (timeout <= 0)
- return UNUSABLE_ERR;
+ if (timeout-- <= 0)
+ return UNUSABLE_ERR;
+
+ udelay(1000);
+ }
if (mmc->version != SD_VERSION_2)
mmc->version = SD_VERSION_1_0;
return 0;
}
-/* We pass in the cmd since otherwise the init seems to fail */
-static int mmc_send_op_cond_iter(struct mmc *mmc, struct mmc_cmd *cmd,
- int use_arg)
+static int mmc_send_op_cond_iter(struct mmc *mmc, int use_arg)
{
+ struct mmc_cmd cmd;
int err;
- cmd->cmdidx = MMC_CMD_SEND_OP_COND;
- cmd->resp_type = MMC_RSP_R3;
- cmd->cmdarg = 0;
- if (use_arg && !mmc_host_is_spi(mmc)) {
- cmd->cmdarg =
+ cmd.cmdidx = MMC_CMD_SEND_OP_COND;
+ cmd.resp_type = MMC_RSP_R3;
+ cmd.cmdarg = 0;
+ if (use_arg && !mmc_host_is_spi(mmc))
+ cmd.cmdarg = OCR_HCS |
(mmc->cfg->voltages &
- (mmc->op_cond_response & OCR_VOLTAGE_MASK)) |
- (mmc->op_cond_response & OCR_ACCESS_MODE);
+ (mmc->ocr & OCR_VOLTAGE_MASK)) |
+ (mmc->ocr & OCR_ACCESS_MODE);
- if (mmc->cfg->host_caps & MMC_MODE_HC)
- cmd->cmdarg |= OCR_HCS;
- }
- err = mmc_send_cmd(mmc, cmd, NULL);
+ err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
return err;
- mmc->op_cond_response = cmd->response[0];
+ mmc->ocr = cmd.response[0];
return 0;
}
static int mmc_send_op_cond(struct mmc *mmc)
{
- struct mmc_cmd cmd;
int err, i;
/* Some cards seem to need this */
mmc_go_idle(mmc);
/* Asking to the card its capabilities */
- mmc->op_cond_pending = 1;
for (i = 0; i < 2; i++) {
- err = mmc_send_op_cond_iter(mmc, &cmd, i != 0);
+ err = mmc_send_op_cond_iter(mmc, i != 0);
if (err)
return err;
/* exit if not busy (flag seems to be inverted) */
- if (mmc->op_cond_response & OCR_BUSY)
- return 0;
+ if (mmc->ocr & OCR_BUSY)
+ break;
}
- return IN_PROGRESS;
+ mmc->op_cond_pending = 1;
+ return 0;
}
static int mmc_complete_op_cond(struct mmc *mmc)
int err;
mmc->op_cond_pending = 0;
- start = get_timer(0);
- do {
- err = mmc_send_op_cond_iter(mmc, &cmd, 1);
- if (err)
- return err;
- if (get_timer(start) > timeout)
- return UNUSABLE_ERR;
- udelay(100);
- } while (!(mmc->op_cond_response & OCR_BUSY));
+ if (!(mmc->ocr & OCR_BUSY)) {
+ start = get_timer(0);
+ while (1) {
+ err = mmc_send_op_cond_iter(mmc, 1);
+ if (err)
+ return err;
+ if (mmc->ocr & OCR_BUSY)
+ break;
+ if (get_timer(start) > timeout)
+ return UNUSABLE_ERR;
+ udelay(100);
+ }
+ }
if (mmc_host_is_spi(mmc)) { /* read OCR for spi */
cmd.cmdidx = MMC_CMD_SPI_READ_OCR;
if (err)
return err;
+
+ mmc->ocr = cmd.response[0];
}
mmc->version = MMC_VERSION_UNKNOWN;
- mmc->ocr = cmd.response[0];
mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS);
mmc->rca = 1;
return -1;
}
- mmc->block_dev.lba = lldiv(mmc->capacity, mmc->read_bl_len);
+ mmc_get_blk_desc(mmc)->lba = lldiv(mmc->capacity, mmc->read_bl_len);
return 0;
}
-int mmc_select_hwpart(int dev_num, int hwpart)
+static int mmc_switch_part(struct mmc *mmc, unsigned int part_num)
{
- struct mmc *mmc = find_mmc_device(dev_num);
+ int ret;
+
+ ret = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONF,
+ (mmc->part_config & ~PART_ACCESS_MASK)
+ | (part_num & PART_ACCESS_MASK));
+
+ /*
+ * Set the capacity if the switch succeeded or was intended
+ * to return to representing the raw device.
+ */
+ if ((ret == 0) || ((ret == -ENODEV) && (part_num == 0))) {
+ ret = mmc_set_capacity(mmc, part_num);
+ mmc_get_blk_desc(mmc)->hwpart = part_num;
+ }
+
+ return ret;
+}
+
+static int mmc_select_hwpartp(struct blk_desc *desc, int hwpart)
+{
+ struct mmc *mmc = find_mmc_device(desc->devnum);
int ret;
if (!mmc)
return -ENODEV;
- if (mmc->part_num == hwpart)
+ if (mmc->block_dev.hwpart == hwpart)
return 0;
- if (mmc->part_config == MMCPART_NOAVAILABLE) {
- printf("Card doesn't support part_switch\n");
+ if (mmc->part_config == MMCPART_NOAVAILABLE)
return -EMEDIUMTYPE;
- }
- ret = mmc_switch_part(dev_num, hwpart);
+ ret = mmc_switch_part(mmc, hwpart);
if (ret)
return ret;
- mmc->part_num = hwpart;
-
return 0;
}
-
-int mmc_switch_part(int dev_num, unsigned int part_num)
+int mmc_select_hwpart(int dev_num, int hwpart)
{
struct mmc *mmc = find_mmc_device(dev_num);
int ret;
if (!mmc)
- return -1;
+ return -ENODEV;
- ret = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONF,
- (mmc->part_config & ~PART_ACCESS_MASK)
- | (part_num & PART_ACCESS_MASK));
+ if (mmc->block_dev.hwpart == hwpart)
+ return 0;
- /*
- * Set the capacity if the switch succeeded or was intended
- * to return to representing the raw device.
- */
- if ((ret == 0) || ((ret == -ENODEV) && (part_num == 0)))
- ret = mmc_set_capacity(mmc, part_num);
+ if (mmc->part_config == MMCPART_NOAVAILABLE)
+ return -EMEDIUMTYPE;
- return ret;
+ ret = mmc_switch_part(mmc, hwpart);
+ if (ret)
+ return ret;
+
+ return 0;
}
int mmc_hwpart_config(struct mmc *mmc,
mmc->scr[1] = __be32_to_cpu(scr[1]);
switch ((mmc->scr[0] >> 24) & 0xf) {
- case 0:
- mmc->version = SD_VERSION_1_0;
- break;
- case 1:
- mmc->version = SD_VERSION_1_10;
- break;
- case 2:
- mmc->version = SD_VERSION_2;
- if ((mmc->scr[0] >> 15) & 0x1)
- mmc->version = SD_VERSION_3;
- break;
- default:
- mmc->version = SD_VERSION_1_0;
- break;
+ case 0:
+ mmc->version = SD_VERSION_1_0;
+ break;
+ case 1:
+ mmc->version = SD_VERSION_1_10;
+ break;
+ case 2:
+ mmc->version = SD_VERSION_2;
+ if ((mmc->scr[0] >> 15) & 0x1)
+ mmc->version = SD_VERSION_3;
+ break;
+ default:
+ mmc->version = SD_VERSION_1_0;
+ break;
}
if (mmc->scr[0] & SD_DATA_4BIT)
int timeout = 1000;
bool has_parts = false;
bool part_completed;
+ struct blk_desc *bdesc;
#ifdef CONFIG_MMC_SPI_CRC_ON
if (mmc_host_is_spi(mmc)) { /* enable CRC check for spi */
int version = (cmd.response[0] >> 26) & 0xf;
switch (version) {
- case 0:
- mmc->version = MMC_VERSION_1_2;
- break;
- case 1:
- mmc->version = MMC_VERSION_1_4;
- break;
- case 2:
- mmc->version = MMC_VERSION_2_2;
- break;
- case 3:
- mmc->version = MMC_VERSION_3;
- break;
- case 4:
- mmc->version = MMC_VERSION_4;
- break;
- default:
- mmc->version = MMC_VERSION_1_2;
- break;
+ case 0:
+ mmc->version = MMC_VERSION_1_2;
+ break;
+ case 1:
+ mmc->version = MMC_VERSION_1_4;
+ break;
+ case 2:
+ mmc->version = MMC_VERSION_2_2;
+ break;
+ case 3:
+ mmc->version = MMC_VERSION_3;
+ break;
+ case 4:
+ mmc->version = MMC_VERSION_4;
+ break;
+ default:
+ mmc->version = MMC_VERSION_1_2;
+ break;
}
}
mmc->wr_rel_set = ext_csd[EXT_CSD_WR_REL_SET];
}
- err = mmc_set_capacity(mmc, mmc->part_num);
+ err = mmc_set_capacity(mmc, mmc_get_blk_desc(mmc)->hwpart);
if (err)
return err;
}
/* fill in device description */
- mmc->block_dev.lun = 0;
- mmc->block_dev.type = 0;
- mmc->block_dev.blksz = mmc->read_bl_len;
- mmc->block_dev.log2blksz = LOG2(mmc->block_dev.blksz);
- mmc->block_dev.lba = lldiv(mmc->capacity, mmc->read_bl_len);
-#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
- sprintf(mmc->block_dev.vendor, "Man %06x Snr %04x%04x",
+ bdesc = mmc_get_blk_desc(mmc);
+ bdesc->lun = 0;
+ bdesc->hwpart = 0;
+ bdesc->type = 0;
+ bdesc->blksz = mmc->read_bl_len;
+ bdesc->log2blksz = LOG2(bdesc->blksz);
+ bdesc->lba = lldiv(mmc->capacity, mmc->read_bl_len);
+#if !defined(CONFIG_SPL_BUILD) || \
+ (defined(CONFIG_SPL_LIBCOMMON_SUPPORT) && \
+ !defined(CONFIG_USE_TINY_PRINTF))
+ sprintf(bdesc->vendor, "Man %06x Snr %04x%04x",
mmc->cid[0] >> 24, (mmc->cid[2] & 0xffff),
(mmc->cid[3] >> 16) & 0xffff);
- sprintf(mmc->block_dev.product, "%c%c%c%c%c%c", mmc->cid[0] & 0xff,
+ sprintf(bdesc->product, "%c%c%c%c%c%c", mmc->cid[0] & 0xff,
(mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff,
(mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff,
(mmc->cid[2] >> 24) & 0xff);
- sprintf(mmc->block_dev.revision, "%d.%d", (mmc->cid[2] >> 20) & 0xf,
+ sprintf(bdesc->revision, "%d.%d", (mmc->cid[2] >> 20) & 0xf,
(mmc->cid[2] >> 16) & 0xf);
#else
- mmc->block_dev.vendor[0] = 0;
- mmc->block_dev.product[0] = 0;
- mmc->block_dev.revision[0] = 0;
+ bdesc->vendor[0] = 0;
+ bdesc->product[0] = 0;
+ bdesc->revision[0] = 0;
#endif
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBDISK_SUPPORT)
- init_part(&mmc->block_dev);
+ part_init(bdesc);
#endif
return 0;
return -1;
}
+#ifdef CONFIG_BLK
+int mmc_bind(struct udevice *dev, struct mmc *mmc, const struct mmc_config *cfg)
+{
+ struct blk_desc *bdesc;
+ struct udevice *bdev;
+ int ret;
+
+ ret = blk_create_devicef(dev, "mmc_blk", "blk", IF_TYPE_MMC, -1, 512,
+ 0, &bdev);
+ if (ret) {
+ debug("Cannot create block device\n");
+ return ret;
+ }
+ bdesc = dev_get_uclass_platdata(bdev);
+ mmc->cfg = cfg;
+ mmc->priv = dev;
+
+ /* the following chunk was from mmc_register() */
+
+ /* Setup dsr related values */
+ mmc->dsr_imp = 0;
+ mmc->dsr = 0xffffffff;
+ /* Setup the universal parts of the block interface just once */
+ bdesc->if_type = IF_TYPE_MMC;
+ bdesc->removable = 1;
+
+ /* setup initial part type */
+ bdesc->part_type = mmc->cfg->part_type;
+ mmc->dev = dev;
+
+ return 0;
+}
+
+int mmc_unbind(struct udevice *dev)
+{
+ struct udevice *bdev;
+
+ device_find_first_child(dev, &bdev);
+ if (bdev) {
+ device_remove(bdev);
+ device_unbind(bdev);
+ }
+
+ return 0;
+}
+
+#else
struct mmc *mmc_create(const struct mmc_config *cfg, void *priv)
{
+ struct blk_desc *bdesc;
struct mmc *mmc;
/* quick validation */
mmc->dsr_imp = 0;
mmc->dsr = 0xffffffff;
/* Setup the universal parts of the block interface just once */
- mmc->block_dev.if_type = IF_TYPE_MMC;
- mmc->block_dev.dev = cur_dev_num++;
- mmc->block_dev.removable = 1;
- mmc->block_dev.block_read = mmc_bread;
- mmc->block_dev.block_write = mmc_bwrite;
- mmc->block_dev.block_erase = mmc_berase;
+ bdesc = mmc_get_blk_desc(mmc);
+ bdesc->if_type = IF_TYPE_MMC;
+ bdesc->removable = 1;
+ bdesc->devnum = mmc_get_next_devnum();
+ bdesc->block_read = mmc_bread;
+ bdesc->block_write = mmc_bwrite;
+ bdesc->block_erase = mmc_berase;
/* setup initial part type */
- mmc->block_dev.part_type = mmc->cfg->part_type;
-
- INIT_LIST_HEAD(&mmc->link);
-
- list_add_tail(&mmc->link, &mmc_devices);
+ bdesc->part_type = mmc->cfg->part_type;
+ mmc_list_add(mmc);
return mmc;
}
/* only freeing memory for now */
free(mmc);
}
+#endif
-#ifdef CONFIG_PARTITIONS
-block_dev_desc_t *mmc_get_dev(int dev)
+static int mmc_get_dev(int dev, struct blk_desc **descp)
{
struct mmc *mmc = find_mmc_device(dev);
- if (!mmc || mmc_init(mmc))
- return NULL;
+ int ret;
+
+ if (!mmc)
+ return -ENODEV;
+ ret = mmc_init(mmc);
+ if (ret)
+ return ret;
- return &mmc->block_dev;
+ *descp = &mmc->block_dev;
+
+ return 0;
}
-#endif
/* board-specific MMC power initializations. */
__weak void board_mmc_power_init(void)
if (mmc->has_init)
return 0;
+#ifdef CONFIG_FSL_ESDHC_ADAPTER_IDENT
+ mmc_adapter_card_type_ident();
+#endif
board_mmc_power_init();
/* made sure it's not NULL earlier */
return err;
/* The internal partition reset to user partition(0) at every CMD0*/
- mmc->part_num = 0;
+ mmc_get_blk_desc(mmc)->hwpart = 0;
/* Test for SD version 2 */
err = mmc_send_if_cond(mmc);
if (err == TIMEOUT) {
err = mmc_send_op_cond(mmc);
- if (err && err != IN_PROGRESS) {
+ if (err) {
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
printf("Card did not respond to voltage select!\n");
#endif
}
}
- if (err == IN_PROGRESS)
+ if (!err)
mmc->init_in_progress = 1;
return err;
{
int err = 0;
+ mmc->init_in_progress = 0;
if (mmc->op_cond_pending)
err = mmc_complete_op_cond(mmc);
mmc->has_init = 0;
else
mmc->has_init = 1;
- mmc->init_in_progress = 0;
return err;
}
int mmc_init(struct mmc *mmc)
{
- int err = IN_PROGRESS;
+ int err = 0;
unsigned start;
if (mmc->has_init)
if (!mmc->init_in_progress)
err = mmc_start_init(mmc);
- if (!err || err == IN_PROGRESS)
+ if (!err)
err = mmc_complete_init(mmc);
debug("%s: %d, time %lu\n", __func__, err, get_timer(start));
return err;
return -1;
}
-#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
-
-void print_mmc_devices(char separator)
-{
- struct mmc *m;
- struct list_head *entry;
-
- list_for_each(entry, &mmc_devices) {
- m = list_entry(entry, struct mmc, link);
-
- printf("%s: %d", m->cfg->name, m->block_dev.dev);
-
- if (entry->next != &mmc_devices) {
- printf("%c", separator);
- if (separator != '\n')
- puts (" ");
- }
- }
-
- printf("\n");
-}
-
-#else
-void print_mmc_devices(char separator) { }
-#endif
-
-int get_mmc_num(void)
-{
- return cur_dev_num;
-}
-
void mmc_set_preinit(struct mmc *mmc, int preinit)
{
mmc->preinit = preinit;
}
-static void do_preinit(void)
+#if defined(CONFIG_DM_MMC) && defined(CONFIG_SPL_BUILD)
+static int mmc_probe(bd_t *bis)
{
- struct mmc *m;
- struct list_head *entry;
+ return 0;
+}
+#elif defined(CONFIG_DM_MMC)
+static int mmc_probe(bd_t *bis)
+{
+ int ret, i;
+ struct uclass *uc;
+ struct udevice *dev;
- list_for_each(entry, &mmc_devices) {
- m = list_entry(entry, struct mmc, link);
+ ret = uclass_get(UCLASS_MMC, &uc);
+ if (ret)
+ return ret;
- if (m->preinit)
- mmc_start_init(m);
+ /*
+ * Try to add them in sequence order. Really with driver model we
+ * should allow holes, but the current MMC list does not allow that.
+ * So if we request 0, 1, 3 we will get 0, 1, 2.
+ */
+ for (i = 0; ; i++) {
+ ret = uclass_get_device_by_seq(UCLASS_MMC, i, &dev);
+ if (ret == -ENODEV)
+ break;
+ }
+ uclass_foreach_dev(dev, uc) {
+ ret = device_probe(dev);
+ if (ret)
+ printf("%s - probe failed: %d\n", dev->name, ret);
}
+
+ return 0;
}
+#else
+static int mmc_probe(bd_t *bis)
+{
+ if (board_mmc_init(bis) < 0)
+ cpu_mmc_init(bis);
+ return 0;
+}
+#endif
int mmc_initialize(bd_t *bis)
{
- INIT_LIST_HEAD (&mmc_devices);
- cur_dev_num = 0;
+ static int initialized = 0;
+ int ret;
+ if (initialized) /* Avoid initializing mmc multiple times */
+ return 0;
+ initialized = 1;
- if (board_mmc_init(bis) < 0)
- cpu_mmc_init(bis);
+#ifndef CONFIG_BLK
+ mmc_list_init();
+#endif
+ ret = mmc_probe(bis);
+ if (ret)
+ return ret;
#ifndef CONFIG_SPL_BUILD
print_mmc_devices(',');
#endif
- do_preinit();
+ mmc_do_preinit();
return 0;
}
enable);
}
#endif
+
+U_BOOT_LEGACY_BLK(mmc) = {
+ .if_typename = "mmc",
+ .if_type = IF_TYPE_MMC,
+ .max_devs = -1,
+ .get_dev = mmc_get_dev,
+ .select_hwpart = mmc_select_hwpartp,
+};