int cd_gpio;
int wp_gpio;
#endif
+#endif
+#if CONFIG_IS_ENABLED(DM_MMC)
+ uint iov;
+ enum bus_mode mode;
#endif
u8 controller_flags;
#ifndef CONFIG_OMAP34XX
/* If we fail after 1 second wait, something is really bad */
#define MAX_RETRY_MS 1000
+#define MMC_TIMEOUT_MS 20
/* 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 bandwidth is always above 3MB/s).
*/
#define DMA_TIMEOUT_PER_MB 333
+#define OMAP_HSMMC_SUPPORTS_DUAL_VOLT BIT(0)
+#define OMAP_HSMMC_NO_1_8_V BIT(1)
#define OMAP_HSMMC_USE_ADMA BIT(2)
static int mmc_read_data(struct hsmmc *mmc_base, char *buf, unsigned int size);
unsigned int siz);
static void omap_hsmmc_start_clock(struct hsmmc *mmc_base);
static void omap_hsmmc_stop_clock(struct hsmmc *mmc_base);
+static void mmc_reset_controller_fsm(struct hsmmc *mmc_base, u32 bit);
static inline struct omap_hsmmc_data *omap_hsmmc_get_data(struct mmc *mmc)
{
writel(readl(&mmc_base->con) & ~INIT_INITSTREAM, &mmc_base->con);
}
+#if CONFIG_IS_ENABLED(DM_MMC)
+static void omap_hsmmc_set_timing(struct mmc *mmc)
+{
+ u32 val;
+ struct hsmmc *mmc_base;
+ struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc);
+
+ mmc_base = priv->base_addr;
+
+ val = readl(&mmc_base->ac12);
+ val &= ~AC12_UHSMC_MASK;
+ priv->mode = mmc->selected_mode;
+
+ if (mmc_is_mode_ddr(priv->mode))
+ writel(readl(&mmc_base->con) | DDR, &mmc_base->con);
+ else
+ writel(readl(&mmc_base->con) & ~DDR, &mmc_base->con);
+
+ switch (priv->mode) {
+ case MMC_HS_200:
+ case UHS_SDR104:
+ val |= AC12_UHSMC_SDR104;
+ break;
+ case UHS_SDR50:
+ val |= AC12_UHSMC_SDR50;
+ break;
+ case MMC_DDR_52:
+ case UHS_DDR50:
+ val |= AC12_UHSMC_DDR50;
+ break;
+ case SD_HS:
+ case MMC_HS_52:
+ case UHS_SDR25:
+ val |= AC12_UHSMC_SDR25;
+ break;
+ case MMC_LEGACY:
+ case MMC_HS:
+ case SD_LEGACY:
+ case UHS_SDR12:
+ val |= AC12_UHSMC_SDR12;
+ break;
+ default:
+ val |= AC12_UHSMC_RES;
+ break;
+ }
+ writel(val, &mmc_base->ac12);
+}
+
+static void omap_hsmmc_conf_bus_power(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->hctl) & ~SDVS_MASK;
+
+ switch (priv->iov) {
+ case IOV_3V3:
+ val |= SDVS_3V3;
+ break;
+ case IOV_3V0:
+ val |= SDVS_3V0;
+ break;
+ case IOV_1V8:
+ val |= SDVS_1V8;
+ break;
+ }
+
+ writel(val, &mmc_base->hctl);
+}
+
+static void omap_hsmmc_set_capabilities(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->capa);
+
+ if (priv->controller_flags & OMAP_HSMMC_SUPPORTS_DUAL_VOLT) {
+ val |= (VS30_3V0SUP | VS18_1V8SUP);
+ priv->iov = IOV_3V0;
+ } else if (priv->controller_flags & OMAP_HSMMC_NO_1_8_V) {
+ val |= VS30_3V0SUP;
+ val &= ~VS18_1V8SUP;
+ priv->iov = IOV_3V0;
+ } else {
+ val |= VS18_1V8SUP;
+ val &= ~VS30_3V0SUP;
+ priv->iov = IOV_1V8;
+ }
+
+ writel(val, &mmc_base->capa);
+}
+
+#ifdef MMC_SUPPORTS_TUNING
+static void omap_hsmmc_disable_tuning(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->ac12);
+ val &= ~(AC12_SCLK_SEL);
+ writel(val, &mmc_base->ac12);
+
+ val = readl(&mmc_base->dll);
+ val &= ~(DLL_FORCE_VALUE | DLL_SWT);
+ writel(val, &mmc_base->dll);
+}
+
+static void omap_hsmmc_set_dll(struct mmc *mmc, int count)
+{
+ int i;
+ 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->dll);
+ val |= DLL_FORCE_VALUE;
+ val &= ~(DLL_FORCE_SR_C_MASK << DLL_FORCE_SR_C_SHIFT);
+ val |= (count << DLL_FORCE_SR_C_SHIFT);
+ writel(val, &mmc_base->dll);
+
+ val |= DLL_CALIB;
+ writel(val, &mmc_base->dll);
+ for (i = 0; i < 1000; i++) {
+ if (readl(&mmc_base->dll) & DLL_CALIB)
+ break;
+ }
+ val &= ~DLL_CALIB;
+ writel(val, &mmc_base->dll);
+}
+
+static int omap_hsmmc_execute_tuning(struct udevice *dev, uint opcode)
+{
+ struct omap_hsmmc_data *priv = dev_get_priv(dev);
+ struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
+ struct mmc *mmc = upriv->mmc;
+ struct hsmmc *mmc_base;
+ u32 val;
+ u8 cur_match, prev_match = 0;
+ int ret;
+ u32 phase_delay = 0;
+ u32 start_window = 0, max_window = 0;
+ u32 length = 0, max_len = 0;
+
+ mmc_base = priv->base_addr;
+ val = readl(&mmc_base->capa2);
+
+ /* clock tuning is not needed for upto 52MHz */
+ if (!((mmc->selected_mode == MMC_HS_200) ||
+ (mmc->selected_mode == UHS_SDR104) ||
+ ((mmc->selected_mode == UHS_SDR50) && (val & CAPA2_TSDR50))))
+ return 0;
+
+ val = readl(&mmc_base->dll);
+ val |= DLL_SWT;
+ writel(val, &mmc_base->dll);
+ while (phase_delay <= MAX_PHASE_DELAY) {
+ omap_hsmmc_set_dll(mmc, phase_delay);
+
+ cur_match = !mmc_send_tuning(mmc, opcode, NULL);
+
+ if (cur_match) {
+ if (prev_match) {
+ length++;
+ } else {
+ start_window = phase_delay;
+ length = 1;
+ }
+ }
+
+ if (length > max_len) {
+ max_window = start_window;
+ max_len = length;
+ }
+
+ prev_match = cur_match;
+ phase_delay += 4;
+ }
+
+ if (!max_len) {
+ ret = -EIO;
+ goto tuning_error;
+ }
+
+ val = readl(&mmc_base->ac12);
+ if (!(val & AC12_SCLK_SEL)) {
+ ret = -EIO;
+ goto tuning_error;
+ }
+
+ phase_delay = max_window + 4 * ((3 * max_len) >> 2);
+ omap_hsmmc_set_dll(mmc, phase_delay);
+
+ mmc_reset_controller_fsm(mmc_base, SYSCTL_SRD);
+ mmc_reset_controller_fsm(mmc_base, SYSCTL_SRC);
+
+ return 0;
+
+tuning_error:
+
+ omap_hsmmc_disable_tuning(mmc);
+ mmc_reset_controller_fsm(mmc_base, SYSCTL_SRD);
+ mmc_reset_controller_fsm(mmc_base, SYSCTL_SRC);
+
+ return ret;
+}
+#endif
+#endif
+
+static void mmc_enable_irq(struct mmc *mmc, struct mmc_cmd *cmd)
+{
+ struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc);
+ struct hsmmc *mmc_base = priv->base_addr;
+ u32 irq_mask = INT_EN_MASK;
+
+ /*
+ * TODO: Errata i802 indicates only DCRC interrupts can occur during
+ * tuning procedure and DCRC should be disabled. But see occurences
+ * of DEB, CIE, CEB, CCRC interupts during tuning procedure. These
+ * interrupts occur along with BRR, so the data is actually in the
+ * buffer. It has to be debugged why these interrutps occur
+ */
+ if (cmd && mmc_is_tuning_cmd(cmd->cmdidx))
+ irq_mask &= ~(IE_DEB | IE_DCRC | IE_CIE | IE_CEB | IE_CCRC);
+
+ writel(irq_mask, &mmc_base->ie);
+}
+
static int omap_hsmmc_init_setup(struct mmc *mmc)
{
struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc);
if (reg_val & MADMA_EN)
priv->controller_flags |= OMAP_HSMMC_USE_ADMA;
#endif
+
+#if CONFIG_IS_ENABLED(DM_MMC)
+ omap_hsmmc_set_capabilities(mmc);
+ omap_hsmmc_conf_bus_power(mmc);
+#else
writel(DTW_1_BITMODE | SDBP_PWROFF | SDVS_3V0, &mmc_base->hctl);
writel(readl(&mmc_base->capa) | VS30_3V0SUP | VS18_1V8SUP,
&mmc_base->capa);
+#endif
reg_val = readl(&mmc_base->con) & RESERVED_MASK;
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_ADMAE | IE_CTO | IE_BRR | IE_BWR | IE_TC |
- IE_CC, &mmc_base->ie);
-
+ mmc_enable_irq(mmc, NULL);
mmc_init_stream(mmc_base);
return 0;
if (!(readl(&mmc_base->sysctl) & bit)) {
start = get_timer(0);
while (!(readl(&mmc_base->sysctl) & bit)) {
- if (get_timer(0) - start > MAX_RETRY_MS)
+ if (get_timer(0) - start > MMC_TIMEOUT_MS)
return;
}
}
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;
#endif
}
+ mmc_enable_irq(mmc, cmd);
+
writel(cmd->cmdarg, &mmc_base->arg);
udelay(20); /* To fix "No status update" error on eMMC */
writel((cmd->cmdidx << 24) | flags, &mmc_base->cmd);
if (priv->clock != mmc->clock)
omap_hsmmc_set_clock(mmc);
+#if CONFIG_IS_ENABLED(DM_MMC)
+ if (priv->mode != mmc->selected_mode)
+ omap_hsmmc_set_timing(mmc);
+#endif
return 0;
}
.get_cd = omap_hsmmc_getcd,
.get_wp = omap_hsmmc_getwp,
#endif
+#ifdef MMC_SUPPORTS_TUNING
+ .execute_tuning = omap_hsmmc_execute_tuning,
+#endif
};
#else
static const struct mmc_ops omap_hsmmc_ops = {
struct mmc_config *cfg = &plat->cfg;
const void *fdt = gd->fdt_blob;
int node = dev_of_offset(dev);
- int val;
+ int ret;
plat->base_addr = map_physmem(devfdt_get_addr(dev),
sizeof(struct hsmmc *),
MAP_NOCACHE);
- cfg->host_caps = MMC_MODE_HS_52MHz | MMC_MODE_HS;
- val = fdtdec_get_int(fdt, node, "bus-width", -1);
- if (val < 0) {
- printf("error: bus-width property missing\n");
- return -ENOENT;
- }
-
- switch (val) {
- case 0x8:
- cfg->host_caps |= MMC_MODE_8BIT;
- case 0x4:
- cfg->host_caps |= MMC_MODE_4BIT;
- break;
- default:
- printf("error: invalid bus-width property\n");
- return -ENOENT;
- }
+ ret = mmc_of_parse(dev, cfg);
+ if (ret < 0)
+ return ret;
+ cfg->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
cfg->f_min = 400000;
- cfg->f_max = fdtdec_get_int(fdt, node, "max-frequency", 52000000);
cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
+ if (fdtdec_get_bool(fdt, node, "ti,dual-volt"))
+ plat->controller_flags |= OMAP_HSMMC_SUPPORTS_DUAL_VOLT;
+ if (fdtdec_get_bool(fdt, node, "no-1-8-v"))
+ plat->controller_flags |= OMAP_HSMMC_NO_1_8_V;
#ifdef OMAP_HSMMC_USE_GPIO
plat->cd_inverted = fdtdec_get_bool(fdt, node, "cd-inverted");