#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"
-#ifndef CONFIG_DM_MMC_OPS
+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;
}
#endif
-#ifndef CONFIG_DM_MMC_OPS
+#if !CONFIG_IS_ENABLED(DM_MMC_OPS)
int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
{
int ret;
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;
{
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;
}
-#ifndef CONFIG_DM_MMC_OPS
+#if !CONFIG_IS_ENABLED(DM_MMC_OPS)
int mmc_getcd(struct mmc *mmc)
{
int cd;
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,
};
-#ifndef CONFIG_DM_MMC_OPS
+#if !CONFIG_IS_ENABLED(DM_MMC_OPS)
static void mmc_set_ios(struct mmc *mmc)
{
if (mmc->cfg->ops->set_ios)
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
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)
{
/* we pretend there's no card when init is NULL */
no_card = mmc_getcd(mmc) == 0;
-#ifndef CONFIG_DM_MMC_OPS
+#if !CONFIG_IS_ENABLED(DM_MMC_OPS)
no_card = no_card || (mmc->cfg->ops->init == NULL);
#endif
if (no_card) {
#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;
-#ifdef CONFIG_DM_MMC_OPS
+#if CONFIG_IS_ENABLED(DM_MMC_OPS)
/* The device has already been probed ready for use */
#else
/* made sure it's not NULL earlier */
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