mmc: omap_hsmmc: Add tuning support

HS200/SDR104 requires tuning command to be sent to the card. Use
the mmc_send_tuning library function to send the tuning
command and configure the internal DLL.

Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
Signed-off-by: Jean-Jacques Hiblot <jjhiblot@ti.com>
master
Jean-Jacques Hiblot 6 years ago committed by Jaehoon Chung
parent 9b3fc21837
commit 14761caeee
  1. 21
      arch/arm/include/asm/omap_mmc.h
  2. 122
      drivers/mmc/omap_hsmmc.c

@ -39,7 +39,9 @@ struct hsmmc {
unsigned int sysstatus; /* 0x14 */
unsigned char res2[0x14];
unsigned int con; /* 0x2C */
unsigned char res3[0xD4];
unsigned int pwcnt; /* 0x30 */
unsigned int dll; /* 0x34 */
unsigned char res3[0xcc];
unsigned int blk; /* 0x104 */
unsigned int arg; /* 0x108 */
unsigned int cmd; /* 0x10C */
@ -56,7 +58,8 @@ struct hsmmc {
unsigned char res4[0x4];
unsigned int ac12; /* 0x13C */
unsigned int capa; /* 0x140 */
unsigned char res5[0x10];
unsigned int capa2; /* 0x144 */
unsigned char res5[0xc];
unsigned int admaes; /* 0x154 */
unsigned int admasal; /* 0x158 */
};
@ -173,6 +176,8 @@ struct omap_hsmmc_plat {
#define IOV_1V8 1800000
#define AC12_ET BIT(22)
#define AC12_V1V8_SIGEN BIT(19)
#define AC12_SCLK_SEL BIT(23)
#define AC12_UHSMC_MASK (7 << 16)
#define AC12_UHSMC_DDR50 (4 << 16)
#define AC12_UHSMC_SDR104 (3 << 16)
@ -199,6 +204,18 @@ struct omap_hsmmc_plat {
/* Clock Configurations and Macros */
#define MMC_CLOCK_REFERENCE 96 /* MHz */
/* DLL */
#define DLL_SWT BIT(20)
#define DLL_FORCE_SR_C_SHIFT 13
#define DLL_FORCE_SR_C_MASK 0x7f
#define DLL_FORCE_VALUE BIT(12)
#define DLL_CALIB BIT(1)
#define MAX_PHASE_DELAY 0x7c
/* CAPA2 */
#define CAPA2_TSDR50 BIT(13)
#define mmc_reg_out(addr, mask, val)\
writel((readl(addr) & (~(mask))) | ((val) & (mask)), (addr))

@ -124,6 +124,7 @@ static int mmc_write_data(struct hsmmc *mmc_base, const char *buf,
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)
{
@ -355,6 +356,124 @@ static void omap_hsmmc_set_capabilities(struct mmc *mmc)
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 int omap_hsmmc_init_setup(struct mmc *mmc)
@ -1050,6 +1169,9 @@ static const struct dm_mmc_ops omap_hsmmc_ops = {
.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 = {

Loading…
Cancel
Save