|
@@ -25,6 +25,7 @@
|
|
|
#include <config.h>
|
|
|
#include <common.h>
|
|
|
#include <malloc.h>
|
|
|
+#include <memalign.h>
|
|
|
#include <mmc.h>
|
|
|
#include <part.h>
|
|
|
#include <i2c.h>
|
|
@@ -71,11 +72,45 @@ struct omap_hsmmc_data {
|
|
|
int wp_gpio;
|
|
|
#endif
|
|
|
#endif
|
|
|
+ u8 controller_flags;
|
|
|
+#ifndef CONFIG_OMAP34XX
|
|
|
+ struct omap_hsmmc_adma_desc *adma_desc_table;
|
|
|
+ uint desc_slot;
|
|
|
+#endif
|
|
|
+};
|
|
|
+
|
|
|
+#ifndef CONFIG_OMAP34XX
|
|
|
+struct omap_hsmmc_adma_desc {
|
|
|
+ u8 attr;
|
|
|
+ u8 reserved;
|
|
|
+ u16 len;
|
|
|
+ u32 addr;
|
|
|
};
|
|
|
|
|
|
+#define ADMA_MAX_LEN 63488
|
|
|
+
|
|
|
+/* Decriptor table defines */
|
|
|
+#define ADMA_DESC_ATTR_VALID BIT(0)
|
|
|
+#define ADMA_DESC_ATTR_END BIT(1)
|
|
|
+#define ADMA_DESC_ATTR_INT BIT(2)
|
|
|
+#define ADMA_DESC_ATTR_ACT1 BIT(4)
|
|
|
+#define ADMA_DESC_ATTR_ACT2 BIT(5)
|
|
|
+
|
|
|
+#define ADMA_DESC_TRANSFER_DATA ADMA_DESC_ATTR_ACT2
|
|
|
+#define ADMA_DESC_LINK_DESC (ADMA_DESC_ATTR_ACT1 | ADMA_DESC_ATTR_ACT2)
|
|
|
+#endif
|
|
|
+
|
|
|
/* If we fail after 1 second wait, something is really bad */
|
|
|
#define MAX_RETRY_MS 1000
|
|
|
|
|
|
+/* DMA transfers can take a long time if a lot a data is transferred.
|
|
|
+ * The timeout must take in account the amount of data. Let's assume
|
|
|
+ * that the time will never exceed 333 ms per MB (in other word we assume
|
|
|
+ * that the bandwidth is always above 3MB/s).
|
|
|
+ */
|
|
|
+#define DMA_TIMEOUT_PER_MB 333
|
|
|
+#define OMAP_HSMMC_USE_ADMA BIT(2)
|
|
|
+
|
|
|
static int mmc_read_data(struct hsmmc *mmc_base, char *buf, unsigned int size);
|
|
|
static int mmc_write_data(struct hsmmc *mmc_base, const char *buf,
|
|
|
unsigned int siz);
|
|
@@ -242,6 +277,11 @@ static int omap_hsmmc_init_setup(struct mmc *mmc)
|
|
|
return -ETIMEDOUT;
|
|
|
}
|
|
|
}
|
|
|
+#ifndef CONFIG_OMAP34XX
|
|
|
+ reg_val = readl(&mmc_base->hl_hwinfo);
|
|
|
+ if (reg_val & MADMA_EN)
|
|
|
+ priv->controller_flags |= OMAP_HSMMC_USE_ADMA;
|
|
|
+#endif
|
|
|
writel(DTW_1_BITMODE | SDBP_PWROFF | SDVS_3V0, &mmc_base->hctl);
|
|
|
writel(readl(&mmc_base->capa) | VS30_3V0SUP | VS18_1V8SUP,
|
|
|
&mmc_base->capa);
|
|
@@ -269,8 +309,8 @@ static int omap_hsmmc_init_setup(struct mmc *mmc)
|
|
|
writel(readl(&mmc_base->hctl) | SDBP_PWRON, &mmc_base->hctl);
|
|
|
|
|
|
writel(IE_BADA | IE_CERR | IE_DEB | IE_DCRC | IE_DTO | IE_CIE |
|
|
|
- IE_CEB | IE_CCRC | IE_CTO | IE_BRR | IE_BWR | IE_TC | IE_CC,
|
|
|
- &mmc_base->ie);
|
|
|
+ IE_CEB | IE_CCRC | IE_ADMAE | IE_CTO | IE_BRR | IE_BWR | IE_TC |
|
|
|
+ IE_CC, &mmc_base->ie);
|
|
|
|
|
|
mmc_init_stream(mmc_base);
|
|
|
|
|
@@ -322,6 +362,118 @@ static void mmc_reset_controller_fsm(struct hsmmc *mmc_base, u32 bit)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+#ifndef CONFIG_OMAP34XX
|
|
|
+static void omap_hsmmc_adma_desc(struct mmc *mmc, char *buf, u16 len, bool end)
|
|
|
+{
|
|
|
+ struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc);
|
|
|
+ struct omap_hsmmc_adma_desc *desc;
|
|
|
+ u8 attr;
|
|
|
+
|
|
|
+ desc = &priv->adma_desc_table[priv->desc_slot];
|
|
|
+
|
|
|
+ attr = ADMA_DESC_ATTR_VALID | ADMA_DESC_TRANSFER_DATA;
|
|
|
+ if (!end)
|
|
|
+ priv->desc_slot++;
|
|
|
+ else
|
|
|
+ attr |= ADMA_DESC_ATTR_END;
|
|
|
+
|
|
|
+ desc->len = len;
|
|
|
+ desc->addr = (u32)buf;
|
|
|
+ desc->reserved = 0;
|
|
|
+ desc->attr = attr;
|
|
|
+}
|
|
|
+
|
|
|
+static void omap_hsmmc_prepare_adma_table(struct mmc *mmc,
|
|
|
+ struct mmc_data *data)
|
|
|
+{
|
|
|
+ uint total_len = data->blocksize * data->blocks;
|
|
|
+ uint desc_count = DIV_ROUND_UP(total_len, ADMA_MAX_LEN);
|
|
|
+ struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc);
|
|
|
+ int i = desc_count;
|
|
|
+ char *buf;
|
|
|
+
|
|
|
+ priv->desc_slot = 0;
|
|
|
+ priv->adma_desc_table = (struct omap_hsmmc_adma_desc *)
|
|
|
+ memalign(ARCH_DMA_MINALIGN, desc_count *
|
|
|
+ sizeof(struct omap_hsmmc_adma_desc));
|
|
|
+
|
|
|
+ if (data->flags & MMC_DATA_READ)
|
|
|
+ buf = data->dest;
|
|
|
+ else
|
|
|
+ buf = (char *)data->src;
|
|
|
+
|
|
|
+ while (--i) {
|
|
|
+ omap_hsmmc_adma_desc(mmc, buf, ADMA_MAX_LEN, false);
|
|
|
+ buf += ADMA_MAX_LEN;
|
|
|
+ total_len -= ADMA_MAX_LEN;
|
|
|
+ }
|
|
|
+
|
|
|
+ omap_hsmmc_adma_desc(mmc, buf, total_len, true);
|
|
|
+
|
|
|
+ flush_dcache_range((long)priv->adma_desc_table,
|
|
|
+ (long)priv->adma_desc_table +
|
|
|
+ ROUND(desc_count *
|
|
|
+ sizeof(struct omap_hsmmc_adma_desc),
|
|
|
+ ARCH_DMA_MINALIGN));
|
|
|
+}
|
|
|
+
|
|
|
+static void omap_hsmmc_prepare_data(struct mmc *mmc, struct mmc_data *data)
|
|
|
+{
|
|
|
+ struct hsmmc *mmc_base;
|
|
|
+ struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc);
|
|
|
+ u32 val;
|
|
|
+ char *buf;
|
|
|
+
|
|
|
+ mmc_base = priv->base_addr;
|
|
|
+ omap_hsmmc_prepare_adma_table(mmc, data);
|
|
|
+
|
|
|
+ if (data->flags & MMC_DATA_READ)
|
|
|
+ buf = data->dest;
|
|
|
+ else
|
|
|
+ buf = (char *)data->src;
|
|
|
+
|
|
|
+ val = readl(&mmc_base->hctl);
|
|
|
+ val |= DMA_SELECT;
|
|
|
+ writel(val, &mmc_base->hctl);
|
|
|
+
|
|
|
+ val = readl(&mmc_base->con);
|
|
|
+ val |= DMA_MASTER;
|
|
|
+ writel(val, &mmc_base->con);
|
|
|
+
|
|
|
+ writel((u32)priv->adma_desc_table, &mmc_base->admasal);
|
|
|
+
|
|
|
+ flush_dcache_range((u32)buf,
|
|
|
+ (u32)buf +
|
|
|
+ ROUND(data->blocksize * data->blocks,
|
|
|
+ ARCH_DMA_MINALIGN));
|
|
|
+}
|
|
|
+
|
|
|
+static void omap_hsmmc_dma_cleanup(struct mmc *mmc)
|
|
|
+{
|
|
|
+ struct hsmmc *mmc_base;
|
|
|
+ struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc);
|
|
|
+ u32 val;
|
|
|
+
|
|
|
+ mmc_base = priv->base_addr;
|
|
|
+
|
|
|
+ val = readl(&mmc_base->con);
|
|
|
+ val &= ~DMA_MASTER;
|
|
|
+ writel(val, &mmc_base->con);
|
|
|
+
|
|
|
+ val = readl(&mmc_base->hctl);
|
|
|
+ val &= ~DMA_SELECT;
|
|
|
+ writel(val, &mmc_base->hctl);
|
|
|
+
|
|
|
+ kfree(priv->adma_desc_table);
|
|
|
+}
|
|
|
+#else
|
|
|
+#define omap_hsmmc_adma_desc
|
|
|
+#define omap_hsmmc_prepare_adma_table
|
|
|
+#define omap_hsmmc_prepare_data
|
|
|
+#define omap_hsmmc_dma_cleanup
|
|
|
+#endif
|
|
|
+
|
|
|
#if !CONFIG_IS_ENABLED(DM_MMC)
|
|
|
static int omap_hsmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
|
|
|
struct mmc_data *data)
|
|
@@ -332,6 +484,10 @@ static int omap_hsmmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
|
|
|
struct mmc_data *data)
|
|
|
{
|
|
|
struct omap_hsmmc_data *priv = dev_get_priv(dev);
|
|
|
+#ifndef CONFIG_OMAP34XX
|
|
|
+ struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
|
|
|
+ struct mmc *mmc = upriv->mmc;
|
|
|
+#endif
|
|
|
#endif
|
|
|
struct hsmmc *mmc_base;
|
|
|
unsigned int flags, mmc_stat;
|
|
@@ -405,6 +561,14 @@ static int omap_hsmmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
|
|
|
flags |= (DP_DATA | DDIR_READ);
|
|
|
else
|
|
|
flags |= (DP_DATA | DDIR_WRITE);
|
|
|
+
|
|
|
+#ifndef CONFIG_OMAP34XX
|
|
|
+ if ((priv->controller_flags & OMAP_HSMMC_USE_ADMA) &&
|
|
|
+ !mmc_is_tuning_cmd(cmd->cmdidx)) {
|
|
|
+ omap_hsmmc_prepare_data(mmc, data);
|
|
|
+ flags |= DE_ENABLE;
|
|
|
+ }
|
|
|
+#endif
|
|
|
}
|
|
|
|
|
|
writel(cmd->cmdarg, &mmc_base->arg);
|
|
@@ -414,7 +578,7 @@ static int omap_hsmmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
|
|
|
start = get_timer(0);
|
|
|
do {
|
|
|
mmc_stat = readl(&mmc_base->stat);
|
|
|
- if (get_timer(0) - start > MAX_RETRY_MS) {
|
|
|
+ if (get_timer(start) > MAX_RETRY_MS) {
|
|
|
printf("%s : timeout: No status update\n", __func__);
|
|
|
return -ETIMEDOUT;
|
|
|
}
|
|
@@ -441,6 +605,41 @@ static int omap_hsmmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+#ifndef CONFIG_OMAP34XX
|
|
|
+ if ((priv->controller_flags & OMAP_HSMMC_USE_ADMA) && data &&
|
|
|
+ !mmc_is_tuning_cmd(cmd->cmdidx)) {
|
|
|
+ u32 sz_mb, timeout;
|
|
|
+
|
|
|
+ if (mmc_stat & IE_ADMAE) {
|
|
|
+ omap_hsmmc_dma_cleanup(mmc);
|
|
|
+ return -EIO;
|
|
|
+ }
|
|
|
+
|
|
|
+ sz_mb = DIV_ROUND_UP(data->blocksize * data->blocks, 1 << 20);
|
|
|
+ timeout = sz_mb * DMA_TIMEOUT_PER_MB;
|
|
|
+ if (timeout < MAX_RETRY_MS)
|
|
|
+ timeout = MAX_RETRY_MS;
|
|
|
+
|
|
|
+ start = get_timer(0);
|
|
|
+ do {
|
|
|
+ mmc_stat = readl(&mmc_base->stat);
|
|
|
+ if (mmc_stat & TC_MASK) {
|
|
|
+ writel(readl(&mmc_base->stat) | TC_MASK,
|
|
|
+ &mmc_base->stat);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (get_timer(start) > timeout) {
|
|
|
+ printf("%s : DMA timeout: No status update\n",
|
|
|
+ __func__);
|
|
|
+ return -ETIMEDOUT;
|
|
|
+ }
|
|
|
+ } while (1);
|
|
|
+
|
|
|
+ omap_hsmmc_dma_cleanup(mmc);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
if (data && (data->flags & MMC_DATA_READ)) {
|
|
|
mmc_read_data(mmc_base, data->dest,
|
|
|
data->blocksize * data->blocks);
|