#include <errno.h>
#include <mmc.h>
#include <part.h>
+#include <power/regulator.h>
#include <malloc.h>
#include <memalign.h>
#include <linux/list.h>
#include <div64.h>
#include "mmc_private.h"
+static const unsigned int sd_au_size[] = {
+ 0, SZ_16K / 512, SZ_32K / 512,
+ SZ_64K / 512, SZ_128K / 512, SZ_256K / 512,
+ SZ_512K / 512, SZ_1M / 512, SZ_2M / 512,
+ SZ_4M / 512, SZ_8M / 512, (SZ_8M + SZ_4M) / 512,
+ SZ_16M / 512, (SZ_16M + SZ_8M) / 512, SZ_32M / 512, SZ_64M / 512,
+};
+
+#if CONFIG_IS_ENABLED(MMC_TINY)
+static struct mmc mmc_static;
+struct mmc *find_mmc_device(int dev_num)
+{
+ return &mmc_static;
+}
+
+void mmc_do_preinit(void)
+{
+ struct mmc *m = &mmc_static;
+#ifdef CONFIG_FSL_ESDHC_ADAPTER_IDENT
+ mmc_set_preinit(m, 1);
+#endif
+ if (m->preinit)
+ mmc_start_init(m);
+}
+
+struct blk_desc *mmc_get_blk_desc(struct mmc *mmc)
+{
+ return &mmc->block_dev;
+}
+#endif
+
+#if !CONFIG_IS_ENABLED(DM_MMC_OPS)
__weak int board_mmc_getwp(struct mmc *mmc)
{
return -1;
{
return -1;
}
+#endif
-int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
+#ifdef CONFIG_MMC_TRACE
+void mmmc_trace_before_send(struct mmc *mmc, struct mmc_cmd *cmd)
{
- int ret;
+ printf("CMD_SEND:%d\n", cmd->cmdidx);
+ printf("\t\tARG\t\t\t 0x%08X\n", cmd->cmdarg);
+}
-#ifdef CONFIG_MMC_TRACE
+void mmmc_trace_after_send(struct mmc *mmc, struct mmc_cmd *cmd, int ret)
+{
int i;
u8 *ptr;
- 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);
if (ret) {
printf("\t\tRET\t\t\t %d\n", ret);
} else {
break;
}
}
-#else
- ret = mmc->cfg->ops->send_cmd(mmc, cmd, data);
+}
+
+void mmc_trace_state(struct mmc *mmc, struct mmc_cmd *cmd)
+{
+ int status;
+
+ status = (cmd->response[0] & MMC_STATUS_CURR_STATE) >> 9;
+ printf("CURR STATE:%d\n", status);
+}
#endif
+
+#if !CONFIG_IS_ENABLED(DM_MMC_OPS)
+int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
+{
+ int ret;
+
+ mmmc_trace_before_send(mmc, cmd);
+ ret = mmc->cfg->ops->send_cmd(mmc, cmd, data);
+ mmmc_trace_after_send(mmc, cmd, ret);
+
return ret;
}
+#endif
int mmc_send_status(struct mmc *mmc, int timeout)
{
struct mmc_cmd cmd;
int err, retries = 5;
-#ifdef CONFIG_MMC_TRACE
- int status;
-#endif
cmd.cmdidx = MMC_CMD_SEND_STATUS;
cmd.resp_type = MMC_RSP_R1;
printf("Status Error: 0x%08X\n",
cmd.response[0]);
#endif
- return COMM_ERR;
+ return -ECOMM;
}
} else if (--retries < 0)
return err;
udelay(1000);
}
-#ifdef CONFIG_MMC_TRACE
- status = (cmd.response[0] & MMC_STATUS_CURR_STATE) >> 9;
- printf("CURR STATE:%d\n", status);
-#endif
+ mmc_trace_state(mmc, &cmd);
if (timeout <= 0) {
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
printf("Timeout waiting card ready\n");
#endif
- return TIMEOUT;
+ return -ETIMEDOUT;
}
return 0;
return blkcnt;
}
-#ifdef CONFIG_BLK
+#if CONFIG_IS_ENABLED(BLK)
ulong mmc_bread(struct udevice *dev, lbaint_t start, lbaint_t blkcnt, void *dst)
#else
ulong mmc_bread(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt,
void *dst)
#endif
{
-#ifdef CONFIG_BLK
+#if CONFIG_IS_ENABLED(BLK)
struct blk_desc *block_dev = dev_get_uclass_platdata(dev);
#endif
int dev_num = block_dev->devnum;
if (!mmc)
return 0;
- err = blk_dselect_hwpart(block_dev, block_dev->hwpart);
+ if (CONFIG_IS_ENABLED(MMC_TINY))
+ err = mmc_switch_part(mmc, block_dev->hwpart);
+ else
+ err = blk_dselect_hwpart(block_dev, block_dev->hwpart);
+
if (err < 0)
return 0;
break;
if (timeout-- <= 0)
- return UNUSABLE_ERR;
+ return -EOPNOTSUPP;
udelay(1000);
}
mmc->op_cond_pending = 0;
if (!(mmc->ocr & OCR_BUSY)) {
+ /* Some cards seem to need this */
+ mmc_go_idle(mmc);
+
start = get_timer(0);
while (1) {
err = mmc_send_op_cond_iter(mmc, 1);
if (mmc->ocr & OCR_BUSY)
break;
if (get_timer(start) > timeout)
- return UNUSABLE_ERR;
+ return -EOPNOTSUPP;
udelay(100);
}
}
{
struct mmc_cmd cmd;
int timeout = 1000;
+ int retries = 3;
int ret;
cmd.cmdidx = MMC_CMD_SWITCH;
(index << 16) |
(value << 8);
- ret = mmc_send_cmd(mmc, &cmd, NULL);
+ while (retries > 0) {
+ ret = mmc_send_cmd(mmc, &cmd, NULL);
- /* Waiting for the ready status */
- if (!ret)
- ret = mmc_send_status(mmc, timeout);
+ /* Waiting for the ready status */
+ if (!ret) {
+ ret = mmc_send_status(mmc, timeout);
+ return ret;
+ }
+
+ retries--;
+ }
return ret;
return 0;
}
+#if !CONFIG_IS_ENABLED(DM_MMC_OPS)
int mmc_getcd(struct mmc *mmc)
{
int cd;
return cd;
}
+#endif
static int sd_switch(struct mmc *mmc, int mode, int group, u8 value, u8 *resp)
{
return 0;
}
+static int sd_read_ssr(struct mmc *mmc)
+{
+ int err, i;
+ struct mmc_cmd cmd;
+ ALLOC_CACHE_ALIGN_BUFFER(uint, ssr, 16);
+ struct mmc_data data;
+ int timeout = 3;
+ unsigned int au, eo, et, es;
+
+ cmd.cmdidx = MMC_CMD_APP_CMD;
+ cmd.resp_type = MMC_RSP_R1;
+ cmd.cmdarg = mmc->rca << 16;
+
+ err = mmc_send_cmd(mmc, &cmd, NULL);
+ if (err)
+ return err;
+
+ cmd.cmdidx = SD_CMD_APP_SD_STATUS;
+ cmd.resp_type = MMC_RSP_R1;
+ cmd.cmdarg = 0;
+
+retry_ssr:
+ data.dest = (char *)ssr;
+ data.blocksize = 64;
+ data.blocks = 1;
+ data.flags = MMC_DATA_READ;
+
+ err = mmc_send_cmd(mmc, &cmd, &data);
+ if (err) {
+ if (timeout--)
+ goto retry_ssr;
+
+ return err;
+ }
+
+ for (i = 0; i < 16; i++)
+ ssr[i] = be32_to_cpu(ssr[i]);
+
+ au = (ssr[2] >> 12) & 0xF;
+ if ((au <= 9) || (mmc->version == SD_VERSION_3)) {
+ mmc->ssr.au = sd_au_size[au];
+ es = (ssr[3] >> 24) & 0xFF;
+ es |= (ssr[2] & 0xFF) << 8;
+ et = (ssr[3] >> 18) & 0x3F;
+ if (es && et) {
+ eo = (ssr[3] >> 16) & 0x3;
+ mmc->ssr.erase_timeout = (et * 1000) / es;
+ mmc->ssr.erase_offset = eo * 1000;
+ }
+ } else {
+ debug("Invalid Allocation Unit Size.\n");
+ }
+
+ return 0;
+}
+
/* frequency bases */
/* divided by 10 to be nice to platforms without floating point */
static const int fbase[] = {
80,
};
+#if !CONFIG_IS_ENABLED(DM_MMC_OPS)
static void mmc_set_ios(struct mmc *mmc)
{
if (mmc->cfg->ops->set_ios)
mmc->cfg->ops->set_ios(mmc);
}
+#endif
void mmc_set_clock(struct mmc *mmc, uint clock)
{
struct mmc_cmd cmd;
ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
ALLOC_CACHE_ALIGN_BUFFER(u8, test_csd, MMC_MAX_BLOCK_LEN);
- int timeout = 1000;
bool has_parts = false;
bool part_completed;
struct blk_desc *bdesc;
err = mmc_send_cmd(mmc, &cmd, NULL);
- /* Waiting for the ready status */
- mmc_send_status(mmc, timeout);
-
if (err)
return err;
mmc_set_bus_width(mmc, 4);
}
+ err = sd_read_ssr(mmc);
+ if (err)
+ return err;
+
if (mmc->card_caps & MMC_MODE_HS)
mmc->tran_speed = 50000000;
else
&test_csd[EXT_CSD_SEC_CNT], 4) == 0)
break;
else
- err = SWITCH_ERR;
+ err = -EBADMSG;
}
if (err)
return err;
if ((cmd.response[0] & 0xff) != 0xaa)
- return UNUSABLE_ERR;
+ return -EOPNOTSUPP;
else
mmc->version = SD_VERSION_2;
return 0;
}
+#if !CONFIG_IS_ENABLED(DM_MMC)
/* board-specific MMC power initializations. */
__weak void board_mmc_power_init(void)
{
}
+#endif
+
+static int mmc_power_init(struct mmc *mmc)
+{
+#if CONFIG_IS_ENABLED(DM_MMC)
+#if defined(CONFIG_DM_REGULATOR) && !defined(CONFIG_SPL_BUILD)
+ struct udevice *vmmc_supply;
+ int ret;
+
+ ret = device_get_supply_regulator(mmc->dev, "vmmc-supply",
+ &vmmc_supply);
+ if (ret) {
+ debug("%s: No vmmc supply\n", mmc->dev->name);
+ return 0;
+ }
+
+ ret = regulator_set_enable(vmmc_supply, true);
+ if (ret) {
+ puts("Error enabling VMMC supply\n");
+ return ret;
+ }
+#endif
+#else /* !CONFIG_DM_MMC */
+ /*
+ * Driver model should use a regulator, as above, rather than calling
+ * out to board code.
+ */
+ board_mmc_power_init();
+#endif
+ return 0;
+}
int mmc_start_init(struct mmc *mmc)
{
+ bool no_card;
int err;
/* we pretend there's no card when init is NULL */
- if (mmc_getcd(mmc) == 0 || mmc->cfg->ops->init == NULL) {
+ no_card = mmc_getcd(mmc) == 0;
+#if !CONFIG_IS_ENABLED(DM_MMC_OPS)
+ no_card = no_card || (mmc->cfg->ops->init == NULL);
+#endif
+ if (no_card) {
mmc->has_init = 0;
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
printf("MMC: no card present\n");
#endif
- return NO_CARD_ERR;
+ return -ENOMEDIUM;
}
if (mmc->has_init)
#ifdef CONFIG_FSL_ESDHC_ADAPTER_IDENT
mmc_adapter_card_type_ident();
#endif
- board_mmc_power_init();
+ err = mmc_power_init(mmc);
+ if (err)
+ return err;
+#if CONFIG_IS_ENABLED(DM_MMC_OPS)
+ /* The device has already been probed ready for use */
+#else
/* made sure it's not NULL earlier */
err = mmc->cfg->ops->init(mmc);
-
if (err)
return err;
-
+#endif
mmc->ddr_mode = 0;
mmc_set_bus_width(mmc, 1);
mmc_set_clock(mmc, 1);
err = sd_send_op_cond(mmc);
/* If the command timed out, we check for an MMC card */
- if (err == TIMEOUT) {
+ if (err == -ETIMEDOUT) {
err = mmc_send_op_cond(mmc);
if (err) {
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
printf("Card did not respond to voltage select!\n");
#endif
- return UNUSABLE_ERR;
+ return -EOPNOTSUPP;
}
}
int mmc_init(struct mmc *mmc)
{
int err = 0;
- unsigned start;
-#ifdef CONFIG_DM_MMC
+ __maybe_unused unsigned start;
+#if CONFIG_IS_ENABLED(DM_MMC)
struct mmc_uclass_priv *upriv = dev_get_uclass_priv(mmc->dev);
upriv->mmc = mmc;
if (!err)
err = mmc_complete_init(mmc);
- debug("%s: %d, time %lu\n", __func__, err, get_timer(start));
+ if (err)
+ printf("%s: %d, time %lu\n", __func__, err, get_timer(start));
+
return err;
}
mmc->preinit = preinit;
}
-#if defined(CONFIG_DM_MMC) && defined(CONFIG_SPL_BUILD)
+#if CONFIG_IS_ENABLED(DM_MMC) && defined(CONFIG_SPL_BUILD)
static int mmc_probe(bd_t *bis)
{
return 0;
}
-#elif defined(CONFIG_DM_MMC)
+#elif CONFIG_IS_ENABLED(DM_MMC)
static int mmc_probe(bd_t *bis)
{
int ret, i;
return 0;
initialized = 1;
-#ifndef CONFIG_BLK
+#if !CONFIG_IS_ENABLED(BLK)
+#if !CONFIG_IS_ENABLED(MMC_TINY)
mmc_list_init();
+#endif
#endif
ret = mmc_probe(bis);
if (ret)
mmc_do_preinit();
return 0;
}
+
+#ifdef CONFIG_CMD_BKOPS_ENABLE
+int mmc_set_bkops_enable(struct mmc *mmc)
+{
+ int err;
+ ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
+
+ err = mmc_send_ext_csd(mmc, ext_csd);
+ if (err) {
+ puts("Could not get ext_csd register values\n");
+ return err;
+ }
+
+ if (!(ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1)) {
+ puts("Background operations not supported on device\n");
+ return -EMEDIUMTYPE;
+ }
+
+ if (ext_csd[EXT_CSD_BKOPS_EN] & 0x1) {
+ puts("Background operations already enabled\n");
+ return 0;
+ }
+
+ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BKOPS_EN, 1);
+ if (err) {
+ puts("Failed to enable manual background operations\n");
+ return err;
+ }
+
+ puts("Enabled manual background operations\n");
+
+ return 0;
+}
+#endif