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>
This commit is contained in:
parent
9b3fc21837
commit
14761caeee
@ -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…
Reference in New Issue
Block a user