- fsl_esdhc_imx cleanup
- not send cm13 if send_status is 0.
- Add reinit API
- Add mmc HS400 for fsl_esdhc
- Several cleanup for fsl_esdhc
- Add ADMA2 for sdhci
This commit is contained in:
Tom Rini 2020-10-15 08:20:42 -04:00
commit 9dc6aef8c9
16 changed files with 555 additions and 236 deletions

View file

@ -80,6 +80,8 @@
&esdhc1 { &esdhc1 {
status = "okay"; status = "okay";
mmc-hs200-1_8v; mmc-hs200-1_8v;
mmc-hs400-1_8v;
bus-width = <8>;
}; };
&fspi { &fspi {

View file

@ -41,6 +41,7 @@ CONFIG_DM_I2C=y
CONFIG_I2C_SET_DEFAULT_BUS_NUM=y CONFIG_I2C_SET_DEFAULT_BUS_NUM=y
CONFIG_I2C_DEFAULT_BUS_NUMBER=0 CONFIG_I2C_DEFAULT_BUS_NUMBER=0
CONFIG_DM_MMC=y CONFIG_DM_MMC=y
CONFIG_MMC_HS400_SUPPORT=y
CONFIG_FSL_ESDHC=y CONFIG_FSL_ESDHC=y
CONFIG_MTD=y CONFIG_MTD=y
CONFIG_DM_SPI_FLASH=y CONFIG_DM_SPI_FLASH=y

View file

@ -48,6 +48,7 @@ CONFIG_DM_I2C=y
CONFIG_I2C_SET_DEFAULT_BUS_NUM=y CONFIG_I2C_SET_DEFAULT_BUS_NUM=y
CONFIG_I2C_DEFAULT_BUS_NUMBER=0 CONFIG_I2C_DEFAULT_BUS_NUMBER=0
CONFIG_DM_MMC=y CONFIG_DM_MMC=y
CONFIG_MMC_HS400_SUPPORT=y
CONFIG_FSL_ESDHC=y CONFIG_FSL_ESDHC=y
CONFIG_MTD=y CONFIG_MTD=y
CONFIG_DM_SPI_FLASH=y CONFIG_DM_SPI_FLASH=y

View file

@ -49,6 +49,7 @@ CONFIG_DM_I2C=y
CONFIG_I2C_SET_DEFAULT_BUS_NUM=y CONFIG_I2C_SET_DEFAULT_BUS_NUM=y
CONFIG_I2C_DEFAULT_BUS_NUMBER=0 CONFIG_I2C_DEFAULT_BUS_NUMBER=0
CONFIG_DM_MMC=y CONFIG_DM_MMC=y
CONFIG_MMC_HS400_SUPPORT=y
CONFIG_SUPPORT_EMMC_RPMB=y CONFIG_SUPPORT_EMMC_RPMB=y
CONFIG_FSL_ESDHC=y CONFIG_FSL_ESDHC=y
CONFIG_MTD=y CONFIG_MTD=y

View file

@ -46,6 +46,9 @@ config SPL_DM_MMC
if MMC if MMC
config MMC_SDHCI_ADMA_HELPERS
bool
config MMC_SPI config MMC_SPI
bool "Support for SPI-based MMC controller" bool "Support for SPI-based MMC controller"
depends on DM_MMC && DM_SPI depends on DM_MMC && DM_SPI
@ -445,6 +448,7 @@ config MMC_SDHCI_SDMA
config MMC_SDHCI_ADMA config MMC_SDHCI_ADMA
bool "Support SDHCI ADMA2" bool "Support SDHCI ADMA2"
depends on MMC_SDHCI depends on MMC_SDHCI
select MMC_SDHCI_ADMA_HELPERS
help help
This enables support for the ADMA (Advanced DMA) defined This enables support for the ADMA (Advanced DMA) defined
in the SD Host Controller Standard Specification Version 3.00 in the SD Host Controller Standard Specification Version 3.00
@ -452,6 +456,7 @@ config MMC_SDHCI_ADMA
config SPL_MMC_SDHCI_ADMA config SPL_MMC_SDHCI_ADMA
bool "Support SDHCI ADMA2 in SPL" bool "Support SDHCI ADMA2 in SPL"
depends on MMC_SDHCI depends on MMC_SDHCI
select MMC_SDHCI_ADMA_HELPERS
help help
This enables support for the ADMA (Advanced DMA) defined This enables support for the ADMA (Advanced DMA) defined
in the SD Host Controller Standard Specification Version 3.00 in SPL. in the SD Host Controller Standard Specification Version 3.00 in SPL.
@ -750,6 +755,14 @@ config FSL_ESDHC
This selects support for the eSDHC (Enhanced Secure Digital Host This selects support for the eSDHC (Enhanced Secure Digital Host
Controller) found on numerous Freescale/NXP SoCs. Controller) found on numerous Freescale/NXP SoCs.
config FSL_ESDHC_SUPPORT_ADMA2
bool "enable ADMA2 support"
depends on FSL_ESDHC
select MMC_SDHCI_ADMA_HELPERS
help
This enables support for the ADMA2 transfer mode. If supported by the
eSDHC it will allow 64bit DMA addresses.
config FSL_ESDHC_33V_IO_RELIABILITY_WORKAROUND config FSL_ESDHC_33V_IO_RELIABILITY_WORKAROUND
bool "enable eSDHC workaround for 3.3v IO reliability issue" bool "enable eSDHC workaround for 3.3v IO reliability issue"
depends on FSL_ESDHC && DM_MMC depends on FSL_ESDHC && DM_MMC

View file

@ -6,6 +6,7 @@
obj-y += mmc.o obj-y += mmc.o
obj-$(CONFIG_$(SPL_)DM_MMC) += mmc-uclass.o obj-$(CONFIG_$(SPL_)DM_MMC) += mmc-uclass.o
obj-$(CONFIG_$(SPL_)MMC_WRITE) += mmc_write.o obj-$(CONFIG_$(SPL_)MMC_WRITE) += mmc_write.o
obj-$(CONFIG_MMC_SDHCI_ADMA_HELPERS) += sdhci-adma.o
ifndef CONFIG_$(SPL_)BLK ifndef CONFIG_$(SPL_)BLK
obj-y += mmc_legacy.o obj-y += mmc_legacy.o

View file

@ -26,6 +26,8 @@
#include <dm/device_compat.h> #include <dm/device_compat.h>
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <sdhci.h>
DECLARE_GLOBAL_DATA_PTR; DECLARE_GLOBAL_DATA_PTR;
@ -51,8 +53,9 @@ struct fsl_esdhc {
char reserved1[8]; /* reserved */ char reserved1[8]; /* reserved */
uint fevt; /* Force event register */ uint fevt; /* Force event register */
uint admaes; /* ADMA error status register */ uint admaes; /* ADMA error status register */
uint adsaddr; /* ADMA system address register */ uint adsaddrl; /* ADMA system address low register */
char reserved2[160]; uint adsaddrh; /* ADMA system address high register */
char reserved2[156];
uint hostver; /* Host controller version register */ uint hostver; /* Host controller version register */
char reserved3[4]; /* reserved */ char reserved3[4]; /* reserved */
uint dmaerraddr; /* DMA error address register */ uint dmaerraddr; /* DMA error address register */
@ -60,7 +63,14 @@ struct fsl_esdhc {
uint dmaerrattr; /* DMA error attribute register */ uint dmaerrattr; /* DMA error attribute register */
char reserved5[4]; /* reserved */ char reserved5[4]; /* reserved */
uint hostcapblt2; /* Host controller capabilities register 2 */ uint hostcapblt2; /* Host controller capabilities register 2 */
char reserved6[756]; /* reserved */ char reserved6[8]; /* reserved */
uint tbctl; /* Tuning block control register */
char reserved7[32]; /* reserved */
uint sdclkctl; /* SD clock control register */
uint sdtimingctl; /* SD timing control register */
char reserved8[20]; /* reserved */
uint dllcfg0; /* DLL config 0 register */
char reserved9[680]; /* reserved */
uint esdhcctl; /* eSDHC control register */ uint esdhcctl; /* eSDHC control register */
}; };
@ -91,6 +101,8 @@ struct fsl_esdhc_priv {
struct mmc *mmc; struct mmc *mmc;
#endif #endif
struct udevice *dev; struct udevice *dev;
struct sdhci_adma_desc *adma_desc_table;
dma_addr_t dma_addr;
}; };
/* Return the XFERTYP flags for a given command and data packet */ /* Return the XFERTYP flags for a given command and data packet */
@ -100,15 +112,15 @@ static uint esdhc_xfertyp(struct mmc_cmd *cmd, struct mmc_data *data)
if (data) { if (data) {
xfertyp |= XFERTYP_DPSEL; xfertyp |= XFERTYP_DPSEL;
#ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO if (!IS_ENABLED(CONFIG_SYS_FSL_ESDHC_USE_PIO) &&
cmd->cmdidx != MMC_CMD_SEND_TUNING_BLOCK &&
cmd->cmdidx != MMC_CMD_SEND_TUNING_BLOCK_HS200)
xfertyp |= XFERTYP_DMAEN; xfertyp |= XFERTYP_DMAEN;
#endif
if (data->blocks > 1) { if (data->blocks > 1) {
xfertyp |= XFERTYP_MSBSEL; xfertyp |= XFERTYP_MSBSEL;
xfertyp |= XFERTYP_BCEN; xfertyp |= XFERTYP_BCEN;
#ifdef CONFIG_SYS_FSL_ERRATUM_ESDHC111 if (IS_ENABLED(CONFIG_SYS_FSL_ERRATUM_ESDHC111))
xfertyp |= XFERTYP_AC12EN; xfertyp |= XFERTYP_AC12EN;
#endif
} }
if (data->flags & MMC_DATA_READ) if (data->flags & MMC_DATA_READ)
@ -132,7 +144,6 @@ static uint esdhc_xfertyp(struct mmc_cmd *cmd, struct mmc_data *data)
return XFERTYP_CMD(cmd->cmdidx) | xfertyp; return XFERTYP_CMD(cmd->cmdidx) | xfertyp;
} }
#ifdef CONFIG_SYS_FSL_ESDHC_USE_PIO
/* /*
* PIO Read/Write Mode reduce the performace as DMA is not used in this mode. * PIO Read/Write Mode reduce the performace as DMA is not used in this mode.
*/ */
@ -195,66 +206,83 @@ static void esdhc_pio_read_write(struct fsl_esdhc_priv *priv,
} }
} }
} }
#endif
static int esdhc_setup_data(struct fsl_esdhc_priv *priv, struct mmc *mmc, static void esdhc_setup_watermark_level(struct fsl_esdhc_priv *priv,
struct mmc_data *data) struct mmc_data *data)
{ {
int timeout;
struct fsl_esdhc *regs = priv->esdhc_regs; struct fsl_esdhc *regs = priv->esdhc_regs;
#if defined(CONFIG_FSL_LAYERSCAPE) uint wml_value = data->blocksize / 4;
dma_addr_t addr;
#endif
uint wml_value;
wml_value = data->blocksize/4;
if (data->flags & MMC_DATA_READ) { if (data->flags & MMC_DATA_READ) {
if (wml_value > WML_RD_WML_MAX) if (wml_value > WML_RD_WML_MAX)
wml_value = WML_RD_WML_MAX_VAL; wml_value = WML_RD_WML_MAX_VAL;
esdhc_clrsetbits32(&regs->wml, WML_RD_WML_MASK, wml_value); esdhc_clrsetbits32(&regs->wml, WML_RD_WML_MASK, wml_value);
#ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO
#if defined(CONFIG_FSL_LAYERSCAPE)
addr = virt_to_phys((void *)(data->dest));
if (upper_32_bits(addr))
printf("Error found for upper 32 bits\n");
else
esdhc_write32(&regs->dsaddr, lower_32_bits(addr));
#else
esdhc_write32(&regs->dsaddr, (u32)data->dest);
#endif
#endif
} else { } else {
#ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO
flush_dcache_range((ulong)data->src,
(ulong)data->src+data->blocks
*data->blocksize);
#endif
if (wml_value > WML_WR_WML_MAX) if (wml_value > WML_WR_WML_MAX)
wml_value = WML_WR_WML_MAX_VAL; wml_value = WML_WR_WML_MAX_VAL;
if (!(esdhc_read32(&regs->prsstat) & PRSSTAT_WPSPL)) { esdhc_clrsetbits32(&regs->wml, WML_WR_WML_MASK,
wml_value << 16);
}
}
static void esdhc_setup_dma(struct fsl_esdhc_priv *priv, struct mmc_data *data)
{
uint trans_bytes = data->blocksize * data->blocks;
struct fsl_esdhc *regs = priv->esdhc_regs;
phys_addr_t adma_addr;
void *buf;
if (data->flags & MMC_DATA_WRITE)
buf = (void *)data->src;
else
buf = data->dest;
priv->dma_addr = dma_map_single(buf, trans_bytes,
mmc_get_dma_dir(data));
if (IS_ENABLED(CONFIG_FSL_ESDHC_SUPPORT_ADMA2) &&
priv->adma_desc_table) {
debug("Using ADMA2\n");
/* prefer ADMA2 if it is available */
sdhci_prepare_adma_table(priv->adma_desc_table, data,
priv->dma_addr);
adma_addr = virt_to_phys(priv->adma_desc_table);
esdhc_write32(&regs->adsaddrl, lower_32_bits(adma_addr));
if (IS_ENABLED(CONFIG_DMA_ADDR_T_64BIT))
esdhc_write32(&regs->adsaddrh, upper_32_bits(adma_addr));
esdhc_clrsetbits32(&regs->proctl, PROCTL_DMAS_MASK,
PROCTL_DMAS_ADMA2);
} else {
debug("Using SDMA\n");
if (upper_32_bits(priv->dma_addr))
printf("Cannot use 64 bit addresses with SDMA\n");
esdhc_write32(&regs->dsaddr, lower_32_bits(priv->dma_addr));
esdhc_clrsetbits32(&regs->proctl, PROCTL_DMAS_MASK,
PROCTL_DMAS_SDMA);
}
esdhc_write32(&regs->blkattr, data->blocks << 16 | data->blocksize);
}
static int esdhc_setup_data(struct fsl_esdhc_priv *priv, struct mmc *mmc,
struct mmc_data *data)
{
int timeout;
bool is_write = data->flags & MMC_DATA_WRITE;
struct fsl_esdhc *regs = priv->esdhc_regs;
if (is_write && !(esdhc_read32(&regs->prsstat) & PRSSTAT_WPSPL)) {
printf("Can not write to locked SD card.\n"); printf("Can not write to locked SD card.\n");
return -EINVAL; return -EINVAL;
} }
esdhc_clrsetbits32(&regs->wml, WML_WR_WML_MASK, if (IS_ENABLED(CONFIG_SYS_FSL_ESDHC_USE_PIO))
wml_value << 16); esdhc_setup_watermark_level(priv, data);
#ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO
#if defined(CONFIG_FSL_LAYERSCAPE)
addr = virt_to_phys((void *)(data->src));
if (upper_32_bits(addr))
printf("Error found for upper 32 bits\n");
else else
esdhc_write32(&regs->dsaddr, lower_32_bits(addr)); esdhc_setup_dma(priv, data);
#else
esdhc_write32(&regs->dsaddr, (u32)data->src);
#endif
#endif
}
esdhc_write32(&regs->blkattr, data->blocks << 16 | data->blocksize);
/* Calculate the timeout period for data transactions */ /* Calculate the timeout period for data transactions */
/* /*
@ -287,41 +315,18 @@ static int esdhc_setup_data(struct fsl_esdhc_priv *priv, struct mmc *mmc,
if (timeout < 0) if (timeout < 0)
timeout = 0; timeout = 0;
#ifdef CONFIG_SYS_FSL_ERRATUM_ESDHC_A001 if (IS_ENABLED(CONFIG_SYS_FSL_ERRATUM_ESDHC_A001) &&
if ((timeout == 4) || (timeout == 8) || (timeout == 12)) (timeout == 4 || timeout == 8 || timeout == 12))
timeout++; timeout++;
#endif
#ifdef ESDHCI_QUIRK_BROKEN_TIMEOUT_VALUE if (IS_ENABLED(ESDHCI_QUIRK_BROKEN_TIMEOUT_VALUE))
timeout = 0xE; timeout = 0xE;
#endif
esdhc_clrsetbits32(&regs->sysctl, SYSCTL_TIMEOUT_MASK, timeout << 16); esdhc_clrsetbits32(&regs->sysctl, SYSCTL_TIMEOUT_MASK, timeout << 16);
return 0; return 0;
} }
static void check_and_invalidate_dcache_range
(struct mmc_cmd *cmd,
struct mmc_data *data) {
unsigned start = 0;
unsigned end = 0;
unsigned size = roundup(ARCH_DMA_MINALIGN,
data->blocks*data->blocksize);
#if defined(CONFIG_FSL_LAYERSCAPE)
dma_addr_t addr;
addr = virt_to_phys((void *)(data->dest));
if (upper_32_bits(addr))
printf("Error found for upper 32 bits\n");
else
start = lower_32_bits(addr);
#else
start = (unsigned)data->dest;
#endif
end = start + size;
invalidate_dcache_range(start, end);
}
/* /*
* Sends a command out on the bus. Takes the mmc pointer, * Sends a command out on the bus. Takes the mmc pointer,
* a command pointer, and an optional data pointer. * a command pointer, and an optional data pointer.
@ -336,10 +341,9 @@ static int esdhc_send_cmd_common(struct fsl_esdhc_priv *priv, struct mmc *mmc,
struct fsl_esdhc *regs = priv->esdhc_regs; struct fsl_esdhc *regs = priv->esdhc_regs;
unsigned long start; unsigned long start;
#ifdef CONFIG_SYS_FSL_ERRATUM_ESDHC111 if (IS_ENABLED(CONFIG_SYS_FSL_ERRATUM_ESDHC111) &&
if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
return 0; return 0;
#endif
esdhc_write32(&regs->irqstat, -1); esdhc_write32(&regs->irqstat, -1);
@ -365,9 +369,6 @@ static int esdhc_send_cmd_common(struct fsl_esdhc_priv *priv, struct mmc *mmc,
err = esdhc_setup_data(priv, mmc, data); err = esdhc_setup_data(priv, mmc, data);
if(err) if(err)
return err; return err;
if (data->flags & MMC_DATA_READ)
check_and_invalidate_dcache_range(cmd, data);
} }
/* Figure out the transfer arguments */ /* Figure out the transfer arguments */
@ -380,6 +381,10 @@ static int esdhc_send_cmd_common(struct fsl_esdhc_priv *priv, struct mmc *mmc,
esdhc_write32(&regs->cmdarg, cmd->cmdarg); esdhc_write32(&regs->cmdarg, cmd->cmdarg);
esdhc_write32(&regs->xfertyp, xfertyp); esdhc_write32(&regs->xfertyp, xfertyp);
if (cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK ||
cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK_HS200)
flags = IRQSTAT_BRR;
/* Wait for the command to complete */ /* Wait for the command to complete */
start = get_timer(0); start = get_timer(0);
while (!(esdhc_read32(&regs->irqstat) & flags)) { while (!(esdhc_read32(&regs->irqstat) & flags)) {
@ -436,9 +441,14 @@ static int esdhc_send_cmd_common(struct fsl_esdhc_priv *priv, struct mmc *mmc,
/* Wait until all of the blocks are transferred */ /* Wait until all of the blocks are transferred */
if (data) { if (data) {
#ifdef CONFIG_SYS_FSL_ESDHC_USE_PIO if (IS_ENABLED(CONFIG_SYS_FSL_ESDHC_USE_PIO)) {
esdhc_pio_read_write(priv, data); esdhc_pio_read_write(priv, data);
#else } else {
flags = DATA_COMPLETE;
if (cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK ||
cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK_HS200)
flags = IRQSTAT_BRR;
do { do {
irqstat = esdhc_read32(&regs->irqstat); irqstat = esdhc_read32(&regs->irqstat);
@ -451,17 +461,17 @@ static int esdhc_send_cmd_common(struct fsl_esdhc_priv *priv, struct mmc *mmc,
err = -ECOMM; err = -ECOMM;
goto out; goto out;
} }
} while ((irqstat & DATA_COMPLETE) != DATA_COMPLETE); } while ((irqstat & flags) != flags);
/* /*
* Need invalidate the dcache here again to avoid any * Need invalidate the dcache here again to avoid any
* cache-fill during the DMA operations such as the * cache-fill during the DMA operations such as the
* speculative pre-fetching etc. * speculative pre-fetching etc.
*/ */
if (data->flags & MMC_DATA_READ) { dma_unmap_single(priv->dma_addr,
check_and_invalidate_dcache_range(cmd, data); data->blocks * data->blocksize,
mmc_get_dma_dir(data));
} }
#endif
} }
out: out:
@ -505,6 +515,9 @@ static void set_sysctl(struct fsl_esdhc_priv *priv, struct mmc *mmc, uint clock)
while (sdhc_clk / (div * pre_div) > clock && div < 16) while (sdhc_clk / (div * pre_div) > clock && div < 16)
div++; div++;
mmc->clock = sdhc_clk / pre_div / div;
priv->clock = mmc->clock;
pre_div >>= 1; pre_div >>= 1;
div -= 1; div -= 1;
@ -555,6 +568,86 @@ static void esdhc_clock_control(struct fsl_esdhc_priv *priv, bool enable)
} }
} }
static void esdhc_flush_async_fifo(struct fsl_esdhc_priv *priv)
{
struct fsl_esdhc *regs = priv->esdhc_regs;
u32 time_out;
esdhc_setbits32(&regs->esdhcctl, ESDHCCTL_FAF);
time_out = 20;
while (esdhc_read32(&regs->esdhcctl) & ESDHCCTL_FAF) {
if (time_out == 0) {
printf("fsl_esdhc: Flush asynchronous FIFO timeout.\n");
break;
}
time_out--;
mdelay(1);
}
}
static void esdhc_tuning_block_enable(struct fsl_esdhc_priv *priv,
bool en)
{
struct fsl_esdhc *regs = priv->esdhc_regs;
esdhc_clock_control(priv, false);
esdhc_flush_async_fifo(priv);
if (en)
esdhc_setbits32(&regs->tbctl, TBCTL_TB_EN);
else
esdhc_clrbits32(&regs->tbctl, TBCTL_TB_EN);
esdhc_clock_control(priv, true);
}
static void esdhc_exit_hs400(struct fsl_esdhc_priv *priv)
{
struct fsl_esdhc *regs = priv->esdhc_regs;
esdhc_clrbits32(&regs->sdtimingctl, FLW_CTL_BG);
esdhc_clrbits32(&regs->sdclkctl, CMD_CLK_CTL);
esdhc_clock_control(priv, false);
esdhc_clrbits32(&regs->tbctl, HS400_MODE);
esdhc_clock_control(priv, true);
esdhc_clrbits32(&regs->dllcfg0, DLL_FREQ_SEL | DLL_ENABLE);
esdhc_clrbits32(&regs->tbctl, HS400_WNDW_ADJUST);
esdhc_tuning_block_enable(priv, false);
}
static void esdhc_set_timing(struct fsl_esdhc_priv *priv, enum bus_mode mode)
{
struct fsl_esdhc *regs = priv->esdhc_regs;
/* Exit HS400 mode before setting any other mode */
if (esdhc_read32(&regs->tbctl) & HS400_MODE &&
mode != MMC_HS_400)
esdhc_exit_hs400(priv);
esdhc_clock_control(priv, false);
if (mode == MMC_HS_200)
esdhc_clrsetbits32(&regs->autoc12err, UHSM_MASK,
UHSM_SDR104_HS200);
if (mode == MMC_HS_400) {
esdhc_setbits32(&regs->tbctl, HS400_MODE);
esdhc_setbits32(&regs->sdclkctl, CMD_CLK_CTL);
esdhc_clock_control(priv, true);
if (priv->clock == 200000000)
esdhc_setbits32(&regs->dllcfg0, DLL_FREQ_SEL);
esdhc_setbits32(&regs->dllcfg0, DLL_ENABLE);
esdhc_setbits32(&regs->tbctl, HS400_WNDW_ADJUST);
esdhc_clock_control(priv, false);
esdhc_flush_async_fifo(priv);
}
esdhc_clock_control(priv, true);
}
static int esdhc_set_ios_common(struct fsl_esdhc_priv *priv, struct mmc *mmc) static int esdhc_set_ios_common(struct fsl_esdhc_priv *priv, struct mmc *mmc)
{ {
struct fsl_esdhc *regs = priv->esdhc_regs; struct fsl_esdhc *regs = priv->esdhc_regs;
@ -566,10 +659,16 @@ static int esdhc_set_ios_common(struct fsl_esdhc_priv *priv, struct mmc *mmc)
esdhc_clock_control(priv, true); esdhc_clock_control(priv, true);
} }
if (mmc->selected_mode == MMC_HS_400)
esdhc_tuning_block_enable(priv, true);
/* Set the clock speed */ /* Set the clock speed */
if (priv->clock != mmc->clock) if (priv->clock != mmc->clock)
set_sysctl(priv, mmc, mmc->clock); set_sysctl(priv, mmc, mmc->clock);
/* Set timing */
esdhc_set_timing(priv, mmc->selected_mode);
/* Set the bus width */ /* Set the bus width */
esdhc_clrbits32(&regs->proctl, PROCTL_DTW_4 | PROCTL_DTW_8); esdhc_clrbits32(&regs->proctl, PROCTL_DTW_4 | PROCTL_DTW_8);
@ -608,6 +707,9 @@ static int esdhc_init_common(struct fsl_esdhc_priv *priv, struct mmc *mmc)
return -ETIMEDOUT; return -ETIMEDOUT;
} }
/* Clean TBCTL[TB_EN] which is not able to be reset by reset all */
esdhc_clrbits32(&regs->tbctl, TBCTL_TB_EN);
esdhc_enable_cache_snooping(regs); esdhc_enable_cache_snooping(regs);
esdhc_setbits32(&regs->sysctl, SYSCTL_HCKEN | SYSCTL_IPGEN); esdhc_setbits32(&regs->sysctl, SYSCTL_HCKEN | SYSCTL_IPGEN);
@ -648,12 +750,10 @@ static void fsl_esdhc_get_cfg_common(struct fsl_esdhc_priv *priv,
u32 caps; u32 caps;
caps = esdhc_read32(&regs->hostcapblt); caps = esdhc_read32(&regs->hostcapblt);
#ifdef CONFIG_SYS_FSL_ERRATUM_ESDHC135 if (IS_ENABLED(CONFIG_SYS_FSL_ERRATUM_ESDHC135))
caps &= ~(HOSTCAPBLT_SRS | HOSTCAPBLT_VS18 | HOSTCAPBLT_VS30); caps &= ~(HOSTCAPBLT_SRS | HOSTCAPBLT_VS18 | HOSTCAPBLT_VS30);
#endif if (IS_ENABLED(CONFIG_SYS_FSL_MMC_HAS_CAPBLT_VS33))
#ifdef CONFIG_SYS_FSL_MMC_HAS_CAPBLT_VS33
caps |= HOSTCAPBLT_VS33; caps |= HOSTCAPBLT_VS33;
#endif
if (caps & HOSTCAPBLT_VS18) if (caps & HOSTCAPBLT_VS18)
cfg->voltages |= MMC_VDD_165_195; cfg->voltages |= MMC_VDD_165_195;
if (caps & HOSTCAPBLT_VS30) if (caps & HOSTCAPBLT_VS30)
@ -674,19 +774,18 @@ static void fsl_esdhc_get_cfg_common(struct fsl_esdhc_priv *priv,
#ifdef CONFIG_OF_LIBFDT #ifdef CONFIG_OF_LIBFDT
__weak int esdhc_status_fixup(void *blob, const char *compat) __weak int esdhc_status_fixup(void *blob, const char *compat)
{ {
#ifdef CONFIG_FSL_ESDHC_PIN_MUX if (IS_ENABLED(CONFIG_FSL_ESDHC_PIN_MUX) && !hwconfig("esdhc")) {
if (!hwconfig("esdhc")) {
do_fixup_by_compat(blob, compat, "status", "disabled", do_fixup_by_compat(blob, compat, "status", "disabled",
sizeof("disabled"), 1); sizeof("disabled"), 1);
return 1; return 1;
} }
#endif
return 0; return 0;
} }
#ifdef CONFIG_FSL_ESDHC_33V_IO_RELIABILITY_WORKAROUND
static int fsl_esdhc_get_cd(struct udevice *dev);
#if CONFIG_IS_ENABLED(DM_MMC)
static int fsl_esdhc_get_cd(struct udevice *dev);
static void esdhc_disable_for_no_card(void *blob) static void esdhc_disable_for_no_card(void *blob)
{ {
struct udevice *dev; struct udevice *dev;
@ -705,6 +804,10 @@ static void esdhc_disable_for_no_card(void *blob)
sizeof("disabled"), 1); sizeof("disabled"), 1);
} }
} }
#else
static void esdhc_disable_for_no_card(void *blob)
{
}
#endif #endif
void fdt_fixup_esdhc(void *blob, struct bd_info *bd) void fdt_fixup_esdhc(void *blob, struct bd_info *bd)
@ -713,9 +816,10 @@ void fdt_fixup_esdhc(void *blob, struct bd_info *bd)
if (esdhc_status_fixup(blob, compat)) if (esdhc_status_fixup(blob, compat))
return; return;
#ifdef CONFIG_FSL_ESDHC_33V_IO_RELIABILITY_WORKAROUND
if (IS_ENABLED(CONFIG_FSL_ESDHC_33V_IO_RELIABILITY_WORKAROUND))
esdhc_disable_for_no_card(blob); esdhc_disable_for_no_card(blob);
#endif
do_fixup_by_compat_u32(blob, compat, "clock-frequency", do_fixup_by_compat_u32(blob, compat, "clock-frequency",
gd->arch.sdhc_clk, 1); gd->arch.sdhc_clk, 1);
} }
@ -797,10 +901,9 @@ int fsl_esdhc_initialize(struct bd_info *bis, struct fsl_esdhc_cfg *cfg)
printf("No max bus width provided. Assume 8-bit supported.\n"); printf("No max bus width provided. Assume 8-bit supported.\n");
} }
#ifdef CONFIG_ESDHC_DETECT_8_BIT_QUIRK if (IS_ENABLED(CONFIG_ESDHC_DETECT_8_BIT_QUIRK))
if (CONFIG_ESDHC_DETECT_8_BIT_QUIRK)
mmc_cfg->host_caps &= ~MMC_MODE_8BIT; mmc_cfg->host_caps &= ~MMC_MODE_8BIT;
#endif
mmc_cfg->ops = &esdhc_ops; mmc_cfg->ops = &esdhc_ops;
fsl_esdhc_get_cfg_common(priv, mmc_cfg); fsl_esdhc_get_cfg_common(priv, mmc_cfg);
@ -832,6 +935,7 @@ static int fsl_esdhc_probe(struct udevice *dev)
struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
struct fsl_esdhc_plat *plat = dev_get_platdata(dev); struct fsl_esdhc_plat *plat = dev_get_platdata(dev);
struct fsl_esdhc_priv *priv = dev_get_priv(dev); struct fsl_esdhc_priv *priv = dev_get_priv(dev);
u32 caps, hostver;
fdt_addr_t addr; fdt_addr_t addr;
struct mmc *mmc; struct mmc *mmc;
int ret; int ret;
@ -846,6 +950,21 @@ static int fsl_esdhc_probe(struct udevice *dev)
#endif #endif
priv->dev = dev; priv->dev = dev;
if (IS_ENABLED(CONFIG_FSL_ESDHC_SUPPORT_ADMA2)) {
/*
* Only newer eSDHC controllers can do ADMA2 if the ADMA flag
* is set in the host capabilities register.
*/
caps = esdhc_read32(&priv->esdhc_regs->hostcapblt);
hostver = esdhc_read32(&priv->esdhc_regs->hostver);
if (caps & HOSTCAPBLT_DMAS &&
HOSTVER_VENDOR(hostver) > VENDOR_V_22) {
priv->adma_desc_table = sdhci_adma_init();
if (!priv->adma_desc_table)
debug("Could not allocate ADMA tables, falling back to SDMA\n");
}
}
if (gd->arch.sdhc_per_clk) { if (gd->arch.sdhc_per_clk) {
priv->sdhc_clk = gd->arch.sdhc_per_clk; priv->sdhc_clk = gd->arch.sdhc_per_clk;
priv->is_sdhc_per_clk = true; priv->is_sdhc_per_clk = true;
@ -872,10 +991,10 @@ static int fsl_esdhc_probe(struct udevice *dev)
if (ret) if (ret)
return ret; return ret;
#ifdef CONFIG_FSL_ESDHC_33V_IO_RELIABILITY_WORKAROUND if (IS_ENABLED(CONFIG_FSL_ESDHC_33V_IO_RELIABILITY_WORKAROUND) &&
if (!fsl_esdhc_get_cd(dev)) !fsl_esdhc_get_cd(dev))
esdhc_setbits32(&priv->esdhc_regs->proctl, PROCTL_VOLT_SEL); esdhc_setbits32(&priv->esdhc_regs->proctl, PROCTL_VOLT_SEL);
#endif
return 0; return 0;
} }
@ -907,6 +1026,64 @@ static int fsl_esdhc_set_ios(struct udevice *dev)
return esdhc_set_ios_common(priv, &plat->mmc); return esdhc_set_ios_common(priv, &plat->mmc);
} }
static int fsl_esdhc_reinit(struct udevice *dev)
{
struct fsl_esdhc_plat *plat = dev_get_platdata(dev);
struct fsl_esdhc_priv *priv = dev_get_priv(dev);
return esdhc_init_common(priv, &plat->mmc);
}
#ifdef MMC_SUPPORTS_TUNING
static int fsl_esdhc_execute_tuning(struct udevice *dev, uint32_t opcode)
{
struct fsl_esdhc_plat *plat = dev_get_platdata(dev);
struct fsl_esdhc_priv *priv = dev_get_priv(dev);
struct fsl_esdhc *regs = priv->esdhc_regs;
u32 val, irqstaten;
int i;
esdhc_tuning_block_enable(priv, true);
esdhc_setbits32(&regs->autoc12err, EXECUTE_TUNING);
irqstaten = esdhc_read32(&regs->irqstaten);
esdhc_write32(&regs->irqstaten, IRQSTATEN_BRR);
for (i = 0; i < MAX_TUNING_LOOP; i++) {
mmc_send_tuning(&plat->mmc, opcode, NULL);
mdelay(1);
val = esdhc_read32(&regs->autoc12err);
if (!(val & EXECUTE_TUNING)) {
if (val & SMPCLKSEL)
break;
}
}
esdhc_write32(&regs->irqstaten, irqstaten);
if (i != MAX_TUNING_LOOP) {
if (plat->mmc.hs400_tuning)
esdhc_setbits32(&regs->sdtimingctl, FLW_CTL_BG);
return 0;
}
printf("fsl_esdhc: tuning failed!\n");
esdhc_clrbits32(&regs->autoc12err, SMPCLKSEL);
esdhc_clrbits32(&regs->autoc12err, EXECUTE_TUNING);
esdhc_tuning_block_enable(priv, false);
return -ETIMEDOUT;
}
#endif
int fsl_esdhc_hs400_prepare_ddr(struct udevice *dev)
{
struct fsl_esdhc_priv *priv = dev_get_priv(dev);
esdhc_tuning_block_enable(priv, false);
return 0;
}
static const struct dm_mmc_ops fsl_esdhc_ops = { static const struct dm_mmc_ops fsl_esdhc_ops = {
.get_cd = fsl_esdhc_get_cd, .get_cd = fsl_esdhc_get_cd,
.send_cmd = fsl_esdhc_send_cmd, .send_cmd = fsl_esdhc_send_cmd,
@ -914,6 +1091,8 @@ static const struct dm_mmc_ops fsl_esdhc_ops = {
#ifdef MMC_SUPPORTS_TUNING #ifdef MMC_SUPPORTS_TUNING
.execute_tuning = fsl_esdhc_execute_tuning, .execute_tuning = fsl_esdhc_execute_tuning,
#endif #endif
.reinit = fsl_esdhc_reinit,
.hs400_prepare_ddr = fsl_esdhc_hs400_prepare_ddr,
}; };
static const struct udevice_id fsl_esdhc_ids[] = { static const struct udevice_id fsl_esdhc_ids[] = {

View file

@ -462,13 +462,6 @@ static int esdhc_send_cmd_common(struct fsl_esdhc_priv *priv, struct mmc *mmc,
while (esdhc_read32(&regs->prsstat) & PRSSTAT_DLA) while (esdhc_read32(&regs->prsstat) & PRSSTAT_DLA)
; ;
/* Wait at least 8 SD clock cycles before the next command */
/*
* Note: This is way more than 8 cycles, but 1ms seems to
* resolve timing issues with some cards
*/
udelay(1000);
/* Set up for a data transfer if we have one */ /* Set up for a data transfer if we have one */
if (data) { if (data) {
err = esdhc_setup_data(priv, mmc, data); err = esdhc_setup_data(priv, mmc, data);
@ -729,7 +722,7 @@ static void esdhc_set_strobe_dll(struct mmc *mmc)
u32 val; u32 val;
if (priv->clock > ESDHC_STROBE_DLL_CLK_FREQ) { if (priv->clock > ESDHC_STROBE_DLL_CLK_FREQ) {
writel(ESDHC_STROBE_DLL_CTRL_RESET, &regs->strobe_dllctrl); esdhc_write32(&regs->strobe_dllctrl, ESDHC_STROBE_DLL_CTRL_RESET);
/* /*
* enable strobe dll ctrl and adjust the delay target * enable strobe dll ctrl and adjust the delay target
@ -738,10 +731,10 @@ static void esdhc_set_strobe_dll(struct mmc *mmc)
val = ESDHC_STROBE_DLL_CTRL_ENABLE | val = ESDHC_STROBE_DLL_CTRL_ENABLE |
(priv->strobe_dll_delay_target << (priv->strobe_dll_delay_target <<
ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT); ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT);
writel(val, &regs->strobe_dllctrl); esdhc_write32(&regs->strobe_dllctrl, val);
/* wait 1us to make sure strobe dll status register stable */ /* wait 1us to make sure strobe dll status register stable */
mdelay(1); mdelay(1);
val = readl(&regs->strobe_dllstat); val = esdhc_read32(&regs->strobe_dllstat);
if (!(val & ESDHC_STROBE_DLL_STS_REF_LOCK)) if (!(val & ESDHC_STROBE_DLL_STS_REF_LOCK))
pr_warn("HS400 strobe DLL status REF not lock!\n"); pr_warn("HS400 strobe DLL status REF not lock!\n");
if (!(val & ESDHC_STROBE_DLL_STS_SLV_LOCK)) if (!(val & ESDHC_STROBE_DLL_STS_SLV_LOCK))
@ -755,18 +748,18 @@ static int esdhc_set_timing(struct mmc *mmc)
struct fsl_esdhc *regs = priv->esdhc_regs; struct fsl_esdhc *regs = priv->esdhc_regs;
u32 mixctrl; u32 mixctrl;
mixctrl = readl(&regs->mixctrl); mixctrl = esdhc_read32(&regs->mixctrl);
mixctrl &= ~(MIX_CTRL_DDREN | MIX_CTRL_HS400_EN); mixctrl &= ~(MIX_CTRL_DDREN | MIX_CTRL_HS400_EN);
switch (mmc->selected_mode) { switch (mmc->selected_mode) {
case MMC_LEGACY: case MMC_LEGACY:
esdhc_reset_tuning(mmc); esdhc_reset_tuning(mmc);
writel(mixctrl, &regs->mixctrl); esdhc_write32(&regs->mixctrl, mixctrl);
break; break;
case MMC_HS_400: case MMC_HS_400:
case MMC_HS_400_ES: case MMC_HS_400_ES:
mixctrl |= MIX_CTRL_DDREN | MIX_CTRL_HS400_EN; mixctrl |= MIX_CTRL_DDREN | MIX_CTRL_HS400_EN;
writel(mixctrl, &regs->mixctrl); esdhc_write32(&regs->mixctrl, mixctrl);
esdhc_set_strobe_dll(mmc); esdhc_set_strobe_dll(mmc);
break; break;
case MMC_HS: case MMC_HS:
@ -777,12 +770,12 @@ static int esdhc_set_timing(struct mmc *mmc)
case UHS_SDR25: case UHS_SDR25:
case UHS_SDR50: case UHS_SDR50:
case UHS_SDR104: case UHS_SDR104:
writel(mixctrl, &regs->mixctrl); esdhc_write32(&regs->mixctrl, mixctrl);
break; break;
case UHS_DDR50: case UHS_DDR50:
case MMC_DDR_52: case MMC_DDR_52:
mixctrl |= MIX_CTRL_DDREN; mixctrl |= MIX_CTRL_DDREN;
writel(mixctrl, &regs->mixctrl); esdhc_write32(&regs->mixctrl, mixctrl);
break; break;
default: default:
printf("Not supported %d\n", mmc->selected_mode); printf("Not supported %d\n", mmc->selected_mode);
@ -862,8 +855,8 @@ static int fsl_esdhc_execute_tuning(struct udevice *dev, uint32_t opcode)
struct fsl_esdhc_priv *priv = dev_get_priv(dev); struct fsl_esdhc_priv *priv = dev_get_priv(dev);
struct fsl_esdhc *regs = priv->esdhc_regs; struct fsl_esdhc *regs = priv->esdhc_regs;
struct mmc *mmc = &plat->mmc; struct mmc *mmc = &plat->mmc;
u32 irqstaten = readl(&regs->irqstaten); u32 irqstaten = esdhc_read32(&regs->irqstaten);
u32 irqsigen = readl(&regs->irqsigen); u32 irqsigen = esdhc_read32(&regs->irqsigen);
int i, ret = -ETIMEDOUT; int i, ret = -ETIMEDOUT;
u32 val, mixctrl; u32 val, mixctrl;
@ -873,25 +866,25 @@ static int fsl_esdhc_execute_tuning(struct udevice *dev, uint32_t opcode)
/* This is readw/writew SDHCI_HOST_CONTROL2 when tuning */ /* This is readw/writew SDHCI_HOST_CONTROL2 when tuning */
if (priv->flags & ESDHC_FLAG_STD_TUNING) { if (priv->flags & ESDHC_FLAG_STD_TUNING) {
val = readl(&regs->autoc12err); val = esdhc_read32(&regs->autoc12err);
mixctrl = readl(&regs->mixctrl); mixctrl = esdhc_read32(&regs->mixctrl);
val &= ~MIX_CTRL_SMPCLK_SEL; val &= ~MIX_CTRL_SMPCLK_SEL;
mixctrl &= ~(MIX_CTRL_FBCLK_SEL | MIX_CTRL_AUTO_TUNE_EN); mixctrl &= ~(MIX_CTRL_FBCLK_SEL | MIX_CTRL_AUTO_TUNE_EN);
val |= MIX_CTRL_EXE_TUNE; val |= MIX_CTRL_EXE_TUNE;
mixctrl |= MIX_CTRL_FBCLK_SEL | MIX_CTRL_AUTO_TUNE_EN; mixctrl |= MIX_CTRL_FBCLK_SEL | MIX_CTRL_AUTO_TUNE_EN;
writel(val, &regs->autoc12err); esdhc_write32(&regs->autoc12err, val);
writel(mixctrl, &regs->mixctrl); esdhc_write32(&regs->mixctrl, mixctrl);
} }
/* sdhci_writew(host, SDHCI_TRNS_READ, SDHCI_TRANSFER_MODE); */ /* sdhci_writew(host, SDHCI_TRNS_READ, SDHCI_TRANSFER_MODE); */
mixctrl = readl(&regs->mixctrl); mixctrl = esdhc_read32(&regs->mixctrl);
mixctrl = MIX_CTRL_DTDSEL_READ | (mixctrl & ~MIX_CTRL_SDHCI_MASK); mixctrl = MIX_CTRL_DTDSEL_READ | (mixctrl & ~MIX_CTRL_SDHCI_MASK);
writel(mixctrl, &regs->mixctrl); esdhc_write32(&regs->mixctrl, mixctrl);
writel(IRQSTATEN_BRR, &regs->irqstaten); esdhc_write32(&regs->irqstaten, IRQSTATEN_BRR);
writel(IRQSTATEN_BRR, &regs->irqsigen); esdhc_write32(&regs->irqsigen, IRQSTATEN_BRR);
/* /*
* Issue opcode repeatedly till Execute Tuning is set to 0 or the number * Issue opcode repeatedly till Execute Tuning is set to 0 or the number
@ -902,22 +895,22 @@ static int fsl_esdhc_execute_tuning(struct udevice *dev, uint32_t opcode)
if (opcode == MMC_CMD_SEND_TUNING_BLOCK_HS200) { if (opcode == MMC_CMD_SEND_TUNING_BLOCK_HS200) {
if (mmc->bus_width == 8) if (mmc->bus_width == 8)
writel(0x7080, &regs->blkattr); esdhc_write32(&regs->blkattr, 0x7080);
else if (mmc->bus_width == 4) else if (mmc->bus_width == 4)
writel(0x7040, &regs->blkattr); esdhc_write32(&regs->blkattr, 0x7040);
} else { } else {
writel(0x7040, &regs->blkattr); esdhc_write32(&regs->blkattr, 0x7040);
} }
/* sdhci_writew(host, SDHCI_TRNS_READ, SDHCI_TRANSFER_MODE) */ /* sdhci_writew(host, SDHCI_TRNS_READ, SDHCI_TRANSFER_MODE) */
val = readl(&regs->mixctrl); val = esdhc_read32(&regs->mixctrl);
val = MIX_CTRL_DTDSEL_READ | (val & ~MIX_CTRL_SDHCI_MASK); val = MIX_CTRL_DTDSEL_READ | (val & ~MIX_CTRL_SDHCI_MASK);
writel(val, &regs->mixctrl); esdhc_write32(&regs->mixctrl, val);
/* We are using STD tuning, no need to check return value */ /* We are using STD tuning, no need to check return value */
mmc_send_tuning(mmc, opcode, NULL); mmc_send_tuning(mmc, opcode, NULL);
ctrl = readl(&regs->autoc12err); ctrl = esdhc_read32(&regs->autoc12err);
if ((!(ctrl & MIX_CTRL_EXE_TUNE)) && if ((!(ctrl & MIX_CTRL_EXE_TUNE)) &&
(ctrl & MIX_CTRL_SMPCLK_SEL)) { (ctrl & MIX_CTRL_SMPCLK_SEL)) {
ret = 0; ret = 0;
@ -925,8 +918,8 @@ static int fsl_esdhc_execute_tuning(struct udevice *dev, uint32_t opcode)
} }
} }
writel(irqstaten, &regs->irqstaten); esdhc_write32(&regs->irqstaten, irqstaten);
writel(irqsigen, &regs->irqsigen); esdhc_write32(&regs->irqsigen, irqsigen);
esdhc_stop_tuning(mmc); esdhc_stop_tuning(mmc);
@ -1179,7 +1172,7 @@ static int fsl_esdhc_init(struct fsl_esdhc_priv *priv,
if (priv->vs18_enable) if (priv->vs18_enable)
esdhc_setbits32(&regs->vendorspec, ESDHC_VENDORSPEC_VSELECT); esdhc_setbits32(&regs->vendorspec, ESDHC_VENDORSPEC_VSELECT);
writel(SDHCI_IRQ_EN_BITS, &regs->irqstaten); esdhc_write32(&regs->irqstaten, SDHCI_IRQ_EN_BITS);
cfg = &plat->cfg; cfg = &plat->cfg;
#ifndef CONFIG_DM_MMC #ifndef CONFIG_DM_MMC
memset(cfg, '\0', sizeof(*cfg)); memset(cfg, '\0', sizeof(*cfg));
@ -1260,10 +1253,10 @@ static int fsl_esdhc_init(struct fsl_esdhc_priv *priv,
cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
writel(0, &regs->dllctrl); esdhc_write32(&regs->dllctrl, 0);
if (priv->flags & ESDHC_FLAG_USDHC) { if (priv->flags & ESDHC_FLAG_USDHC) {
if (priv->flags & ESDHC_FLAG_STD_TUNING) { if (priv->flags & ESDHC_FLAG_STD_TUNING) {
u32 val = readl(&regs->tuning_ctrl); u32 val = esdhc_read32(&regs->tuning_ctrl);
val |= ESDHC_STD_TUNING_EN; val |= ESDHC_STD_TUNING_EN;
val &= ~ESDHC_TUNING_START_TAP_MASK; val &= ~ESDHC_TUNING_START_TAP_MASK;
@ -1282,7 +1275,7 @@ static int fsl_esdhc_init(struct fsl_esdhc_priv *priv,
* after the whole tuning procedure always can't get any response. * after the whole tuning procedure always can't get any response.
*/ */
val |= ESDHC_TUNING_CMD_CRC_CHECK_DISABLE; val |= ESDHC_TUNING_CMD_CRC_CHECK_DISABLE;
writel(val, &regs->tuning_ctrl); esdhc_write32(&regs->tuning_ctrl, val);
} }
} }
@ -1648,9 +1641,9 @@ static int fsl_esdhc_set_enhanced_strobe(struct udevice *dev)
struct fsl_esdhc *regs = priv->esdhc_regs; struct fsl_esdhc *regs = priv->esdhc_regs;
u32 m; u32 m;
m = readl(&regs->mixctrl); m = esdhc_read32(&regs->mixctrl);
m |= MIX_CTRL_HS400_ES; m |= MIX_CTRL_HS400_ES;
writel(m, &regs->mixctrl); esdhc_write32(&regs->mixctrl, m);
return 0; return 0;
} }

View file

@ -142,6 +142,21 @@ int mmc_set_enhanced_strobe(struct mmc *mmc)
} }
#endif #endif
int dm_mmc_hs400_prepare_ddr(struct udevice *dev)
{
struct dm_mmc_ops *ops = mmc_get_ops(dev);
if (ops->hs400_prepare_ddr)
return ops->hs400_prepare_ddr(dev);
return 0;
}
int mmc_hs400_prepare_ddr(struct mmc *mmc)
{
return dm_mmc_hs400_prepare_ddr(mmc->dev);
}
int dm_mmc_host_power_cycle(struct udevice *dev) int dm_mmc_host_power_cycle(struct udevice *dev)
{ {
struct dm_mmc_ops *ops = mmc_get_ops(dev); struct dm_mmc_ops *ops = mmc_get_ops(dev);
@ -171,6 +186,21 @@ int mmc_deferred_probe(struct mmc *mmc)
return dm_mmc_deferred_probe(mmc->dev); return dm_mmc_deferred_probe(mmc->dev);
} }
int dm_mmc_reinit(struct udevice *dev)
{
struct dm_mmc_ops *ops = mmc_get_ops(dev);
if (ops->reinit)
return ops->reinit(dev);
return 0;
}
int mmc_reinit(struct mmc *mmc)
{
return dm_mmc_reinit(mmc->dev);
}
int mmc_of_parse(struct udevice *dev, struct mmc_config *cfg) int mmc_of_parse(struct udevice *dev, struct mmc_config *cfg)
{ {
int val; int val;

View file

@ -805,8 +805,10 @@ static int __mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value,
* capable of polling by using mmc_wait_dat0, then rely on waiting the * capable of polling by using mmc_wait_dat0, then rely on waiting the
* stated timeout to be sufficient. * stated timeout to be sufficient.
*/ */
if (ret == -ENOSYS && !send_status) if (ret == -ENOSYS && !send_status) {
mdelay(timeout_ms); mdelay(timeout_ms);
return 0;
}
/* Finally wait until the card is ready or indicates a failure /* Finally wait until the card is ready or indicates a failure
* to switch. It doesn't hurt to use CMD13 here even if send_status * to switch. It doesn't hurt to use CMD13 here even if send_status
@ -1982,7 +1984,9 @@ static int mmc_select_hs400(struct mmc *mmc)
mmc_set_clock(mmc, mmc->tran_speed, false); mmc_set_clock(mmc, mmc->tran_speed, false);
/* execute tuning if needed */ /* execute tuning if needed */
mmc->hs400_tuning = 1;
err = mmc_execute_tuning(mmc, MMC_CMD_SEND_TUNING_BLOCK_HS200); err = mmc_execute_tuning(mmc, MMC_CMD_SEND_TUNING_BLOCK_HS200);
mmc->hs400_tuning = 0;
if (err) { if (err) {
debug("tuning failed\n"); debug("tuning failed\n");
return err; return err;
@ -1991,6 +1995,10 @@ static int mmc_select_hs400(struct mmc *mmc)
/* Set back to HS */ /* Set back to HS */
mmc_set_card_speed(mmc, MMC_HS, true); mmc_set_card_speed(mmc, MMC_HS, true);
err = mmc_hs400_prepare_ddr(mmc);
if (err)
return err;
err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH,
EXT_CSD_BUS_WIDTH_8 | EXT_CSD_DDR_FLAG); EXT_CSD_BUS_WIDTH_8 | EXT_CSD_DDR_FLAG);
if (err) if (err)
@ -2816,13 +2824,17 @@ int mmc_get_op_cond(struct mmc *mmc)
return err; return err;
#if CONFIG_IS_ENABLED(DM_MMC) #if CONFIG_IS_ENABLED(DM_MMC)
/* The device has already been probed ready for use */ /*
* Re-initialization is needed to clear old configuration for
* mmc rescan.
*/
err = mmc_reinit(mmc);
#else #else
/* made sure it's not NULL earlier */ /* made sure it's not NULL earlier */
err = mmc->cfg->ops->init(mmc); err = mmc->cfg->ops->init(mmc);
#endif
if (err) if (err)
return err; return err;
#endif
mmc->ddr_mode = 0; mmc->ddr_mode = 0;
retry: retry:

73
drivers/mmc/sdhci-adma.c Normal file
View file

@ -0,0 +1,73 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* SDHCI ADMA2 helper functions.
*/
#include <common.h>
#include <cpu_func.h>
#include <sdhci.h>
#include <malloc.h>
#include <asm/cache.h>
static void sdhci_adma_desc(struct sdhci_adma_desc *desc,
dma_addr_t addr, u16 len, bool end)
{
u8 attr;
attr = ADMA_DESC_ATTR_VALID | ADMA_DESC_TRANSFER_DATA;
if (end)
attr |= ADMA_DESC_ATTR_END;
desc->attr = attr;
desc->len = len;
desc->reserved = 0;
desc->addr_lo = lower_32_bits(addr);
#ifdef CONFIG_DMA_ADDR_T_64BIT
desc->addr_hi = upper_32_bits(addr);
#endif
}
/**
* sdhci_prepare_adma_table() - Populate the ADMA table
*
* @table: Pointer to the ADMA table
* @data: Pointer to MMC data
* @addr: DMA address to write to or read from
*
* Fill the ADMA table according to the MMC data to read from or write to the
* given DMA address.
* Please note, that the table size depends on CONFIG_SYS_MMC_MAX_BLK_COUNT and
* we don't have to check for overflow.
*/
void sdhci_prepare_adma_table(struct sdhci_adma_desc *table,
struct mmc_data *data, dma_addr_t addr)
{
uint trans_bytes = data->blocksize * data->blocks;
uint desc_count = DIV_ROUND_UP(trans_bytes, ADMA_MAX_LEN);
struct sdhci_adma_desc *desc = table;
int i = desc_count;
while (--i) {
sdhci_adma_desc(desc, addr, ADMA_MAX_LEN, false);
addr += ADMA_MAX_LEN;
trans_bytes -= ADMA_MAX_LEN;
desc++;
}
sdhci_adma_desc(desc, addr, trans_bytes, true);
flush_cache((dma_addr_t)table,
ROUND(desc_count * sizeof(struct sdhci_adma_desc),
ARCH_DMA_MINALIGN));
}
/**
* sdhci_adma_init() - initialize the ADMA descriptor table
*
* @return pointer to the allocated descriptor table or NULL in case of an
* error.
*/
struct sdhci_adma_desc *sdhci_adma_init(void)
{
return memalign(ARCH_DMA_MINALIGN, ADMA_TABLE_SZ);
}

View file

@ -69,57 +69,6 @@ static void sdhci_transfer_pio(struct sdhci_host *host, struct mmc_data *data)
} }
} }
#if CONFIG_IS_ENABLED(MMC_SDHCI_ADMA)
static void sdhci_adma_desc(struct sdhci_host *host, dma_addr_t dma_addr,
u16 len, bool end)
{
struct sdhci_adma_desc *desc;
u8 attr;
desc = &host->adma_desc_table[host->desc_slot];
attr = ADMA_DESC_ATTR_VALID | ADMA_DESC_TRANSFER_DATA;
if (!end)
host->desc_slot++;
else
attr |= ADMA_DESC_ATTR_END;
desc->attr = attr;
desc->len = len;
desc->reserved = 0;
desc->addr_lo = lower_32_bits(dma_addr);
#ifdef CONFIG_DMA_ADDR_T_64BIT
desc->addr_hi = upper_32_bits(dma_addr);
#endif
}
static void sdhci_prepare_adma_table(struct sdhci_host *host,
struct mmc_data *data)
{
uint trans_bytes = data->blocksize * data->blocks;
uint desc_count = DIV_ROUND_UP(trans_bytes, ADMA_MAX_LEN);
int i = desc_count;
dma_addr_t dma_addr = host->start_addr;
host->desc_slot = 0;
while (--i) {
sdhci_adma_desc(host, dma_addr, ADMA_MAX_LEN, false);
dma_addr += ADMA_MAX_LEN;
trans_bytes -= ADMA_MAX_LEN;
}
sdhci_adma_desc(host, dma_addr, trans_bytes, true);
flush_cache((dma_addr_t)host->adma_desc_table,
ROUND(desc_count * sizeof(struct sdhci_adma_desc),
ARCH_DMA_MINALIGN));
}
#elif defined(CONFIG_MMC_SDHCI_SDMA)
static void sdhci_prepare_adma_table(struct sdhci_host *host,
struct mmc_data *data)
{}
#endif
#if (defined(CONFIG_MMC_SDHCI_SDMA) || CONFIG_IS_ENABLED(MMC_SDHCI_ADMA)) #if (defined(CONFIG_MMC_SDHCI_SDMA) || CONFIG_IS_ENABLED(MMC_SDHCI_ADMA))
static void sdhci_prepare_dma(struct sdhci_host *host, struct mmc_data *data, static void sdhci_prepare_dma(struct sdhci_host *host, struct mmc_data *data,
int *is_aligned, int trans_bytes) int *is_aligned, int trans_bytes)
@ -156,8 +105,11 @@ static void sdhci_prepare_dma(struct sdhci_host *host, struct mmc_data *data,
if (host->flags & USE_SDMA) { if (host->flags & USE_SDMA) {
sdhci_writel(host, phys_to_bus((ulong)host->start_addr), sdhci_writel(host, phys_to_bus((ulong)host->start_addr),
SDHCI_DMA_ADDRESS); SDHCI_DMA_ADDRESS);
} else if (host->flags & (USE_ADMA | USE_ADMA64)) { }
sdhci_prepare_adma_table(host, data); #if CONFIG_IS_ENABLED(MMC_SDHCI_ADMA)
else if (host->flags & (USE_ADMA | USE_ADMA64)) {
sdhci_prepare_adma_table(host->adma_desc_table, data,
host->start_addr);
sdhci_writel(host, lower_32_bits(host->adma_addr), sdhci_writel(host, lower_32_bits(host->adma_addr),
SDHCI_ADMA_ADDRESS); SDHCI_ADMA_ADDRESS);
@ -165,6 +117,7 @@ static void sdhci_prepare_dma(struct sdhci_host *host, struct mmc_data *data,
sdhci_writel(host, upper_32_bits(host->adma_addr), sdhci_writel(host, upper_32_bits(host->adma_addr),
SDHCI_ADMA_ADDRESS_HI); SDHCI_ADMA_ADDRESS_HI);
} }
#endif
} }
#else #else
static void sdhci_prepare_dma(struct sdhci_host *host, struct mmc_data *data, static void sdhci_prepare_dma(struct sdhci_host *host, struct mmc_data *data,
@ -770,9 +723,9 @@ int sdhci_setup_cfg(struct mmc_config *cfg, struct sdhci_host *host,
__func__); __func__);
return -EINVAL; return -EINVAL;
} }
host->adma_desc_table = memalign(ARCH_DMA_MINALIGN, ADMA_TABLE_SZ); host->adma_desc_table = sdhci_adma_init();
host->adma_addr = (dma_addr_t)host->adma_desc_table; host->adma_addr = (dma_addr_t)host->adma_desc_table;
#ifdef CONFIG_DMA_ADDR_T_64BIT #ifdef CONFIG_DMA_ADDR_T_64BIT
host->flags |= USE_ADMA64; host->flags |= USE_ADMA64;
#else #else

View file

@ -784,7 +784,6 @@ int sh_sdhi_init(unsigned long addr, int ch, unsigned long quirks)
return ret; return ret;
error: error:
if (host)
free(host); free(host);
return ret; return ret;
} }

View file

@ -74,8 +74,10 @@
#define IRQSTATEN_TC (0x00000002) #define IRQSTATEN_TC (0x00000002)
#define IRQSTATEN_CC (0x00000001) #define IRQSTATEN_CC (0x00000001)
/* eSDHC control register */
#define ESDHCCTL 0x0002e40c #define ESDHCCTL 0x0002e40c
#define ESDHCCTL_PCS (0x00080000) #define ESDHCCTL_PCS (0x00080000)
#define ESDHCCTL_FAF (0x00040000)
#define PRSSTAT 0x0002e024 #define PRSSTAT 0x0002e024
#define PRSSTAT_DAT0 (0x01000000) #define PRSSTAT_DAT0 (0x01000000)
@ -95,6 +97,10 @@
#define PROCTL_DTW_4 0x00000002 #define PROCTL_DTW_4 0x00000002
#define PROCTL_DTW_8 0x00000004 #define PROCTL_DTW_8 0x00000004
#define PROCTL_D3CD 0x00000008 #define PROCTL_D3CD 0x00000008
#define PROCTL_DMAS_MASK 0x00000300
#define PROCTL_DMAS_SDMA 0x00000000
#define PROCTL_DMAS_ADMA1 0x00000100
#define PROCTL_DMAS_ADMA2 0x00000300
#define PROCTL_VOLT_SEL 0x00000400 #define PROCTL_VOLT_SEL 0x00000400
#define CMDARG 0x0002e008 #define CMDARG 0x0002e008
@ -154,6 +160,12 @@
#define BLKATTR_SIZE(x) (x & 0x1fff) #define BLKATTR_SIZE(x) (x & 0x1fff)
#define MAX_BLK_CNT 0x7fff /* so malloc will have enough room with 32M */ #define MAX_BLK_CNT 0x7fff /* so malloc will have enough room with 32M */
/* Auto CMD error status register / system control 2 register */
#define EXECUTE_TUNING 0x00400000
#define SMPCLKSEL 0x00800000
#define UHSM_MASK 0x00070000
#define UHSM_SDR104_HS200 0x00030000
/* Host controller capabilities register */ /* Host controller capabilities register */
#define HOSTCAPBLT_VS18 0x04000000 #define HOSTCAPBLT_VS18 0x04000000
#define HOSTCAPBLT_VS30 0x02000000 #define HOSTCAPBLT_VS30 0x02000000
@ -162,6 +174,33 @@
#define HOSTCAPBLT_DMAS 0x00400000 #define HOSTCAPBLT_DMAS 0x00400000
#define HOSTCAPBLT_HSS 0x00200000 #define HOSTCAPBLT_HSS 0x00200000
/* Tuning block control register */
#define TBCTL_TB_EN 0x00000004
#define HS400_MODE 0x00000010
#define HS400_WNDW_ADJUST 0x00000040
/* SD clock control register */
#define CMD_CLK_CTL 0x00008000
/* SD timing control register */
#define FLW_CTL_BG 0x00008000
/* DLL config 0 register */
#define DLL_ENABLE 0x80000000
#define DLL_FREQ_SEL 0x08000000
#define MAX_TUNING_LOOP 40
#define HOSTVER_VENDOR(x) (((x) >> 8) & 0xff)
#define VENDOR_V_10 0x00
#define VENDOR_V_20 0x10
#define VENDOR_V_21 0x11
#define VENDOR_V_22 0x12
#define VENDOR_V_23 0x13
#define VENDOR_V_30 0x20
#define VENDOR_V_31 0x21
#define VENDOR_V_32 0x22
struct fsl_esdhc_cfg { struct fsl_esdhc_cfg {
phys_addr_t esdhc_base; phys_addr_t esdhc_base;
u32 sdhc_clk; u32 sdhc_clk;
@ -203,10 +242,6 @@ struct fsl_esdhc_cfg {
int fsl_esdhc_mmc_init(struct bd_info *bis); int fsl_esdhc_mmc_init(struct bd_info *bis);
int fsl_esdhc_initialize(struct bd_info *bis, struct fsl_esdhc_cfg *cfg); int fsl_esdhc_initialize(struct bd_info *bis, struct fsl_esdhc_cfg *cfg);
void fdt_fixup_esdhc(void *blob, struct bd_info *bd); void fdt_fixup_esdhc(void *blob, struct bd_info *bd);
#ifdef MMC_SUPPORTS_TUNING
static inline int fsl_esdhc_execute_tuning(struct udevice *dev,
uint32_t opcode) {return 0; }
#endif
#else #else
static inline int fsl_esdhc_mmc_init(struct bd_info *bis) { return -ENOSYS; } static inline int fsl_esdhc_mmc_init(struct bd_info *bis) { return -ENOSYS; }
static inline void fdt_fixup_esdhc(void *blob, struct bd_info *bd) {} static inline void fdt_fixup_esdhc(void *blob, struct bd_info *bd) {}

View file

@ -421,6 +421,14 @@ struct dm_mmc_ops {
* @return 0 if Ok, -ve if error * @return 0 if Ok, -ve if error
*/ */
int (*deferred_probe)(struct udevice *dev); int (*deferred_probe)(struct udevice *dev);
/**
* reinit() - Re-initialization to clear old configuration for
* mmc rescan.
*
* @dev: Device to reinit
* @return 0 if Ok, -ve if error
*/
int (*reinit)(struct udevice *dev);
/** /**
* send_cmd() - Send a command to the MMC device * send_cmd() - Send a command to the MMC device
* *
@ -505,6 +513,14 @@ struct dm_mmc_ops {
* @return maximum number of blocks for this transfer * @return maximum number of blocks for this transfer
*/ */
int (*get_b_max)(struct udevice *dev, void *dst, lbaint_t blkcnt); int (*get_b_max)(struct udevice *dev, void *dst, lbaint_t blkcnt);
/**
* hs400_prepare_ddr - prepare to switch to DDR mode
*
* @dev: Device to check
* @return 0 if success, -ve on error
*/
int (*hs400_prepare_ddr)(struct udevice *dev);
}; };
#define mmc_get_ops(dev) ((struct dm_mmc_ops *)(dev)->driver->ops) #define mmc_get_ops(dev) ((struct dm_mmc_ops *)(dev)->driver->ops)
@ -518,6 +534,7 @@ int dm_mmc_execute_tuning(struct udevice *dev, uint opcode);
int dm_mmc_wait_dat0(struct udevice *dev, int state, int timeout_us); int dm_mmc_wait_dat0(struct udevice *dev, int state, int timeout_us);
int dm_mmc_host_power_cycle(struct udevice *dev); int dm_mmc_host_power_cycle(struct udevice *dev);
int dm_mmc_deferred_probe(struct udevice *dev); int dm_mmc_deferred_probe(struct udevice *dev);
int dm_mmc_reinit(struct udevice *dev);
int dm_mmc_get_b_max(struct udevice *dev, void *dst, lbaint_t blkcnt); int dm_mmc_get_b_max(struct udevice *dev, void *dst, lbaint_t blkcnt);
/* Transition functions for compatibility */ /* Transition functions for compatibility */
@ -529,8 +546,9 @@ int mmc_wait_dat0(struct mmc *mmc, int state, int timeout_us);
int mmc_set_enhanced_strobe(struct mmc *mmc); int mmc_set_enhanced_strobe(struct mmc *mmc);
int mmc_host_power_cycle(struct mmc *mmc); int mmc_host_power_cycle(struct mmc *mmc);
int mmc_deferred_probe(struct mmc *mmc); int mmc_deferred_probe(struct mmc *mmc);
int mmc_reinit(struct mmc *mmc);
int mmc_get_b_max(struct mmc *mmc, void *dst, lbaint_t blkcnt); int mmc_get_b_max(struct mmc *mmc, void *dst, lbaint_t blkcnt);
int mmc_hs400_prepare_ddr(struct mmc *mmc);
#else #else
struct mmc_ops { struct mmc_ops {
int (*send_cmd)(struct mmc *mmc, int (*send_cmd)(struct mmc *mmc,
@ -542,6 +560,11 @@ struct mmc_ops {
int (*host_power_cycle)(struct mmc *mmc); int (*host_power_cycle)(struct mmc *mmc);
int (*get_b_max)(struct mmc *mmc, void *dst, lbaint_t blkcnt); int (*get_b_max)(struct mmc *mmc, void *dst, lbaint_t blkcnt);
}; };
static inline int mmc_hs400_prepare_ddr(struct mmc *mmc)
{
return 0;
}
#endif #endif
struct mmc_config { struct mmc_config {
@ -697,6 +720,7 @@ struct mmc {
* accessing the boot partitions * accessing the boot partitions
*/ */
u32 quirks; u32 quirks;
u8 hs400_tuning;
}; };
struct mmc_hwpart_conf { struct mmc_hwpart_conf {

View file

@ -271,7 +271,6 @@ struct sdhci_ops {
int (*deferred_probe)(struct sdhci_host *host); int (*deferred_probe)(struct sdhci_host *host);
}; };
#if CONFIG_IS_ENABLED(MMC_SDHCI_ADMA)
#define ADMA_MAX_LEN 65532 #define ADMA_MAX_LEN 65532
#ifdef CONFIG_DMA_ADDR_T_64BIT #ifdef CONFIG_DMA_ADDR_T_64BIT
#define ADMA_DESC_LEN 16 #define ADMA_DESC_LEN 16
@ -302,7 +301,7 @@ struct sdhci_adma_desc {
u32 addr_hi; u32 addr_hi;
#endif #endif
} __packed; } __packed;
#endif
struct sdhci_host { struct sdhci_host {
const char *name; const char *name;
void *ioaddr; void *ioaddr;
@ -334,7 +333,6 @@ struct sdhci_host {
dma_addr_t adma_addr; dma_addr_t adma_addr;
#if CONFIG_IS_ENABLED(MMC_SDHCI_ADMA) #if CONFIG_IS_ENABLED(MMC_SDHCI_ADMA)
struct sdhci_adma_desc *adma_desc_table; struct sdhci_adma_desc *adma_desc_table;
uint desc_slot;
#endif #endif
}; };
@ -496,4 +494,8 @@ extern const struct dm_mmc_ops sdhci_ops;
#else #else
#endif #endif
struct sdhci_adma_desc *sdhci_adma_init(void);
void sdhci_prepare_adma_table(struct sdhci_adma_desc *table,
struct mmc_data *data, dma_addr_t addr);
#endif /* __SDHCI_HW_H */ #endif /* __SDHCI_HW_H */