Commit 6bc337fb authored by Wolfgang Denk's avatar Wolfgang Denk
Browse files

Merge branch 'master' of git://git.denx.de/u-boot-mmc

* 'master' of git://git.denx.de/u-boot-mmc:
  ARM: SAMSUNG: support sdhci controller
  mmc: support the sdhci instead of s5p_mmc for samsung-soc
  mmc: add the quirk to use the sdhci for samsung-soc
  mmc: sdhci: add the quirk for broken r1b response
  i.MX28: Lower the amount of blocks transfered in one DMA cycle
  mmc: fsl_esdhc: Poll until card is not busy anymore
  include/mmc.h: remove struct mmc_csd
  mmc: omap: handle controller errors properly
  mmc: omap: improve stat wait message
  mmc: omap: follow TRM procedure to power on cards
  mmc:fix: Set mmc width according to MMC host capabilities
parents f50bf50d 7d2d58b4
......@@ -21,53 +21,54 @@
#ifndef __ASM_ARCH_MMC_H_
#define __ASM_ARCH_MMC_H_
#ifndef __ASSEMBLY__
struct s5p_mmc {
unsigned int sysad;
unsigned short blksize;
unsigned short blkcnt;
unsigned int argument;
unsigned short trnmod;
unsigned short cmdreg;
unsigned int rspreg0;
unsigned int rspreg1;
unsigned int rspreg2;
unsigned int rspreg3;
unsigned int bdata;
unsigned int prnsts;
unsigned char hostctl;
unsigned char pwrcon;
unsigned char blkgap;
unsigned char wakcon;
unsigned short clkcon;
unsigned char timeoutcon;
unsigned char swrst;
unsigned int norintsts; /* errintsts */
unsigned int norintstsen; /* errintstsen */
unsigned int norintsigen; /* errintsigen */
unsigned short acmd12errsts;
unsigned char res1[2];
unsigned int capareg;
unsigned char res2[4];
unsigned int maxcurr;
unsigned char res3[0x34];
unsigned int control2;
unsigned int control3;
unsigned char res4[4];
unsigned int control4;
unsigned char res5[0x6e];
unsigned short hcver;
unsigned char res6[0xFF00];
};
#define SDHCI_CONTROL2 0x80
#define SDHCI_CONTROL3 0x84
#define SDHCI_CONTROL4 0x8C
struct mmc_host {
struct s5p_mmc *reg;
unsigned int version; /* SDHCI spec. version */
unsigned int clock; /* Current clock (MHz) */
int dev_index;
};
#define SDHCI_CTRL2_ENSTAASYNCCLR (1 << 31)
#define SDHCI_CTRL2_ENCMDCNFMSK (1 << 30)
#define SDHCI_CTRL2_CDINVRXD3 (1 << 29)
#define SDHCI_CTRL2_SLCARDOUT (1 << 28)
int s5p_mmc_init(int dev_index, int bus_width);
#define SDHCI_CTRL2_FLTCLKSEL_MASK (0xf << 24)
#define SDHCI_CTRL2_FLTCLKSEL_SHIFT (24)
#define SDHCI_CTRL2_FLTCLKSEL(_x) ((_x) << 24)
#endif /* __ASSEMBLY__ */
#define SDHCI_CTRL2_LVLDAT_MASK (0xff << 16)
#define SDHCI_CTRL2_LVLDAT_SHIFT (16)
#define SDHCI_CTRL2_LVLDAT(_x) ((_x) << 16)
#define SDHCI_CTRL2_ENFBCLKTX (1 << 15)
#define SDHCI_CTRL2_ENFBCLKRX (1 << 14)
#define SDHCI_CTRL2_SDCDSEL (1 << 13)
#define SDHCI_CTRL2_SDSIGPC (1 << 12)
#define SDHCI_CTRL2_ENBUSYCHKTXSTART (1 << 11)
#define SDHCI_CTRL2_DFCNT_MASK(_x) ((_x) << 9)
#define SDHCI_CTRL2_DFCNT_SHIFT (9)
#define SDHCI_CTRL2_ENCLKOUTHOLD (1 << 8)
#define SDHCI_CTRL2_RWAITMODE (1 << 7)
#define SDHCI_CTRL2_DISBUFRD (1 << 6)
#define SDHCI_CTRL2_SELBASECLK_MASK(_x) ((_x) << 4)
#define SDHCI_CTRL2_SELBASECLK_SHIFT (4)
#define SDHCI_CTRL2_PWRSYNC (1 << 3)
#define SDHCI_CTRL2_ENCLKOUTMSKCON (1 << 1)
#define SDHCI_CTRL2_HWINITFIN (1 << 0)
#define SDHCI_CTRL3_FCSEL3 (1 << 31)
#define SDHCI_CTRL3_FCSEL2 (1 << 23)
#define SDHCI_CTRL3_FCSEL1 (1 << 15)
#define SDHCI_CTRL3_FCSEL0 (1 << 7)
#define SDHCI_CTRL4_DRIVE_MASK(_x) ((_x) << 16)
#define SDHCI_CTRL4_DRIVE_SHIFT (16)
int s5p_sdhci_init(u32 regbase, u32 max_clk, u32 min_clk, u32 quirks);
static inline unsigned int s5p_mmc_init(int index, int bus_width)
{
unsigned int base = samsung_get_base_mmc() + (0x10000 * index);
return s5p_sdhci_init(base, 52000000, 400000, index);
}
#endif
......@@ -21,53 +21,54 @@
#ifndef __ASM_ARCH_MMC_H_
#define __ASM_ARCH_MMC_H_
#ifndef __ASSEMBLY__
struct s5p_mmc {
unsigned int sysad;
unsigned short blksize;
unsigned short blkcnt;
unsigned int argument;
unsigned short trnmod;
unsigned short cmdreg;
unsigned int rspreg0;
unsigned int rspreg1;
unsigned int rspreg2;
unsigned int rspreg3;
unsigned int bdata;
unsigned int prnsts;
unsigned char hostctl;
unsigned char pwrcon;
unsigned char blkgap;
unsigned char wakcon;
unsigned short clkcon;
unsigned char timeoutcon;
unsigned char swrst;
unsigned int norintsts; /* errintsts */
unsigned int norintstsen; /* errintstsen */
unsigned int norintsigen; /* errintsigen */
unsigned short acmd12errsts;
unsigned char res1[2];
unsigned int capareg;
unsigned char res2[4];
unsigned int maxcurr;
unsigned char res3[0x34];
unsigned int control2;
unsigned int control3;
unsigned char res4[4];
unsigned int control4;
unsigned char res5[0x6e];
unsigned short hcver;
unsigned char res6[0xFFF00];
};
#define SDHCI_CONTROL2 0x80
#define SDHCI_CONTROL3 0x84
#define SDHCI_CONTROL4 0x8C
struct mmc_host {
struct s5p_mmc *reg;
unsigned int version; /* SDHCI spec. version */
unsigned int clock; /* Current clock (MHz) */
int dev_index;
};
#define SDHCI_CTRL2_ENSTAASYNCCLR (1 << 31)
#define SDHCI_CTRL2_ENCMDCNFMSK (1 << 30)
#define SDHCI_CTRL2_CDINVRXD3 (1 << 29)
#define SDHCI_CTRL2_SLCARDOUT (1 << 28)
int s5p_mmc_init(int dev_index, int bus_width);
#define SDHCI_CTRL2_FLTCLKSEL_MASK (0xf << 24)
#define SDHCI_CTRL2_FLTCLKSEL_SHIFT (24)
#define SDHCI_CTRL2_FLTCLKSEL(_x) ((_x) << 24)
#endif /* __ASSEMBLY__ */
#define SDHCI_CTRL2_LVLDAT_MASK (0xff << 16)
#define SDHCI_CTRL2_LVLDAT_SHIFT (16)
#define SDHCI_CTRL2_LVLDAT(_x) ((_x) << 16)
#define SDHCI_CTRL2_ENFBCLKTX (1 << 15)
#define SDHCI_CTRL2_ENFBCLKRX (1 << 14)
#define SDHCI_CTRL2_SDCDSEL (1 << 13)
#define SDHCI_CTRL2_SDSIGPC (1 << 12)
#define SDHCI_CTRL2_ENBUSYCHKTXSTART (1 << 11)
#define SDHCI_CTRL2_DFCNT_MASK(_x) ((_x) << 9)
#define SDHCI_CTRL2_DFCNT_SHIFT (9)
#define SDHCI_CTRL2_ENCLKOUTHOLD (1 << 8)
#define SDHCI_CTRL2_RWAITMODE (1 << 7)
#define SDHCI_CTRL2_DISBUFRD (1 << 6)
#define SDHCI_CTRL2_SELBASECLK_MASK(_x) ((_x) << 4)
#define SDHCI_CTRL2_SELBASECLK_SHIFT (4)
#define SDHCI_CTRL2_PWRSYNC (1 << 3)
#define SDHCI_CTRL2_ENCLKOUTMSKCON (1 << 1)
#define SDHCI_CTRL2_HWINITFIN (1 << 0)
#define SDHCI_CTRL3_FCSEL3 (1 << 31)
#define SDHCI_CTRL3_FCSEL2 (1 << 23)
#define SDHCI_CTRL3_FCSEL1 (1 << 15)
#define SDHCI_CTRL3_FCSEL0 (1 << 7)
#define SDHCI_CTRL4_DRIVE_MASK(_x) ((_x) << 16)
#define SDHCI_CTRL4_DRIVE_SHIFT (16)
int s5p_sdhci_init(u32 regbase, u32 max_clk, u32 min_clk, u32 quirks);
static inline unsigned int s5p_mmc_init(int index, int bus_width)
{
unsigned int base = samsung_get_base_mmc() + (0x10000 * index);
return s5p_sdhci_init(base, 52000000, 400000, index);
}
#endif
......@@ -39,8 +39,8 @@ COBJS-$(CONFIG_MXS_MMC) += mxsmmc.o
COBJS-$(CONFIG_OMAP_HSMMC) += omap_hsmmc.o
COBJS-$(CONFIG_PXA_MMC) += pxa_mmc.o
COBJS-$(CONFIG_PXA_MMC_GENERIC) += pxa_mmc_gen.o
COBJS-$(CONFIG_S5P_MMC) += s5p_mmc.o
COBJS-$(CONFIG_SDHCI) += sdhci.o
COBJS-$(CONFIG_S5P_SDHCI) += s5p_sdhci.o
COBJS-$(CONFIG_SH_MMCIF) += sh_mmcif.o
COBJS-$(CONFIG_TEGRA2_MMC) += tegra2_mmc.o
......
......@@ -307,19 +307,56 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
#else
esdhc_write32(&regs->xfertyp, xfertyp);
#endif
/* Mask all irqs */
esdhc_write32(&regs->irqsigen, 0);
/* Wait for the command to complete */
while (!(esdhc_read32(&regs->irqstat) & IRQSTAT_CC))
while (!(esdhc_read32(&regs->irqstat) & (IRQSTAT_CC | IRQSTAT_CTOE)))
;
irqstat = esdhc_read32(&regs->irqstat);
esdhc_write32(&regs->irqstat, irqstat);
/* Reset CMD and DATA portions on error */
if (irqstat & (CMD_ERR | IRQSTAT_CTOE)) {
esdhc_write32(&regs->sysctl, esdhc_read32(&regs->sysctl) |
SYSCTL_RSTC);
while (esdhc_read32(&regs->sysctl) & SYSCTL_RSTC)
;
if (data) {
esdhc_write32(&regs->sysctl,
esdhc_read32(&regs->sysctl) |
SYSCTL_RSTD);
while ((esdhc_read32(&regs->sysctl) & SYSCTL_RSTD))
;
}
}
if (irqstat & CMD_ERR)
return COMM_ERR;
if (irqstat & IRQSTAT_CTOE)
return TIMEOUT;
/* Workaround for ESDHC errata ENGcm03648 */
if (!data && (cmd->resp_type & MMC_RSP_BUSY)) {
int timeout = 2500;
/* Poll on DATA0 line for cmd with busy signal for 250 ms */
while (timeout > 0 && !(esdhc_read32(&regs->prsstat) &
PRSSTAT_DAT0)) {
udelay(100);
timeout--;
}
if (timeout <= 0) {
printf("Timeout waiting for DAT0 to go high!\n");
return TIMEOUT;
}
}
/* Copy the response to the response buffer */
if (cmd->resp_type & MMC_RSP_136) {
u32 cmdrsp3, cmdrsp2, cmdrsp1, cmdrsp0;
......
......@@ -1199,7 +1199,9 @@ int mmc_startup(struct mmc *mmc)
else
mmc_set_clock(mmc, 25000000);
} else {
for (width = EXT_CSD_BUS_WIDTH_8; width >= 0; width--) {
width = ((mmc->host_caps & MMC_MODE_MASK_WIDTH_BITS) >>
MMC_MODE_WIDTH_BITS_SHIFT);
for (; width >= 0; width--) {
/* Set the card to use 4 bit*/
err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BUS_WIDTH, width);
......
......@@ -406,7 +406,7 @@ int mxsmmc_initialize(bd_t *bis, int id, int (*wp)(int))
*/
mmc->f_min = 400000;
mmc->f_max = mxc_get_clock(MXC_SSP0_CLK + id) * 1000 / 2;
mmc->b_max = 0x40;
mmc->b_max = 0x20;
mmc_register(mmc);
return 0;
......
......@@ -33,6 +33,10 @@
#include <asm/arch/mmc_host_def.h>
#include <asm/arch/sys_proto.h>
/* common definitions for all OMAPs */
#define SYSCTL_SRC (1 << 25)
#define SYSCTL_SRD (1 << 26)
/* If we fail after 1 second wait, something is really bad */
#define MAX_RETRY_MS 1000
......@@ -62,15 +66,21 @@ static void omap4_vmmc_pbias_config(struct mmc *mmc)
unsigned char mmc_board_init(struct mmc *mmc)
{
#if defined(CONFIG_TWL4030_POWER)
twl4030_power_mmc_init();
#endif
#if defined(CONFIG_OMAP34XX)
t2_t *t2_base = (t2_t *)T2_BASE;
struct prcm *prcm_base = (struct prcm *)PRCM_BASE;
u32 pbias_lite;
writel(readl(&t2_base->pbias_lite) | PBIASLITEPWRDNZ1 |
pbias_lite = readl(&t2_base->pbias_lite);
pbias_lite &= ~(PBIASLITEPWRDNZ1 | PBIASLITEPWRDNZ0);
writel(pbias_lite, &t2_base->pbias_lite);
#endif
#if defined(CONFIG_TWL4030_POWER)
twl4030_power_mmc_init();
mdelay(100); /* ramp-up delay from Linux code */
#endif
#if defined(CONFIG_OMAP34XX)
writel(pbias_lite | PBIASLITEPWRDNZ1 |
PBIASSPEEDCTRL0 | PBIASLITEPWRDNZ0,
&t2_base->pbias_lite);
......@@ -189,6 +199,27 @@ static int mmc_init_setup(struct mmc *mmc)
return 0;
}
/*
* MMC controller internal finite state machine reset
*
* Used to reset command or data internal state machines, using respectively
* SRC or SRD bit of SYSCTL register
*/
static void mmc_reset_controller_fsm(struct hsmmc *mmc_base, u32 bit)
{
ulong start;
mmc_reg_out(&mmc_base->sysctl, bit, bit);
start = get_timer(0);
while ((readl(&mmc_base->sysctl) & bit) != 0) {
if (get_timer(0) - start > MAX_RETRY_MS) {
printf("%s: timedout waiting for sysctl %x to clear\n",
__func__, bit);
return;
}
}
}
static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
struct mmc_data *data)
......@@ -209,7 +240,8 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
start = get_timer(0);
while (readl(&mmc_base->stat)) {
if (get_timer(0) - start > MAX_RETRY_MS) {
printf("%s: timedout waiting for stat!\n", __func__);
printf("%s: timedout waiting for STAT (%x) to clear\n",
__func__, readl(&mmc_base->stat));
return TIMEOUT;
}
}
......@@ -277,9 +309,10 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
}
} while (!mmc_stat);
if ((mmc_stat & IE_CTO) != 0)
if ((mmc_stat & IE_CTO) != 0) {
mmc_reset_controller_fsm(mmc_base, SYSCTL_SRC);
return TIMEOUT;
else if ((mmc_stat & ERRI_MASK) != 0)
} else if ((mmc_stat & ERRI_MASK) != 0)
return -1;
if (mmc_stat & CC_MASK) {
......@@ -330,6 +363,9 @@ static int mmc_read_data(struct hsmmc *mmc_base, char *buf, unsigned int size)
}
} while (mmc_stat == 0);
if ((mmc_stat & (IE_DTO | IE_DCRC | IE_DEB)) != 0)
mmc_reset_controller_fsm(mmc_base, SYSCTL_SRD);
if ((mmc_stat & ERRI_MASK) != 0)
return 1;
......@@ -382,6 +418,9 @@ static int mmc_write_data(struct hsmmc *mmc_base, const char *buf,
}
} while (mmc_stat == 0);
if ((mmc_stat & (IE_DTO | IE_DCRC | IE_DEB)) != 0)
mmc_reset_controller_fsm(mmc_base, SYSCTL_SRD);
if ((mmc_stat & ERRI_MASK) != 0)
return 1;
......
/*
* (C) Copyright 2009 SAMSUNG Electronics
* Minkyu Kang <mk7.kang@samsung.com>
* Jaehoon Chung <jh80.chung@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <common.h>
#include <mmc.h>
#include <asm/io.h>
#include <asm/arch/mmc.h>
#include <asm/arch/clk.h>
/* support 4 mmc hosts */
struct mmc mmc_dev[4];
struct mmc_host mmc_host[4];
static inline struct s5p_mmc *s5p_get_base_mmc(int dev_index)
{
unsigned long offset = dev_index * sizeof(struct s5p_mmc);
return (struct s5p_mmc *)(samsung_get_base_mmc() + offset);
}
static void mmc_prepare_data(struct mmc_host *host, struct mmc_data *data)
{
unsigned char ctrl;
debug("data->dest: %08x\n", (u32)data->dest);
writel((u32)data->dest, &host->reg->sysad);
/*
* DMASEL[4:3]
* 00 = Selects SDMA
* 01 = Reserved
* 10 = Selects 32-bit Address ADMA2
* 11 = Selects 64-bit Address ADMA2
*/
ctrl = readb(&host->reg->hostctl);
ctrl &= ~(3 << 3);
writeb(ctrl, &host->reg->hostctl);
/* We do not handle DMA boundaries, so set it to max (512 KiB) */
writew((7 << 12) | (data->blocksize & 0xFFF), &host->reg->blksize);
writew(data->blocks, &host->reg->blkcnt);
}
static void mmc_set_transfer_mode(struct mmc_host *host, struct mmc_data *data)
{
unsigned short mode;
/*
* TRNMOD
* MUL1SIN0[5] : Multi/Single Block Select
* RD1WT0[4] : Data Transfer Direction Select
* 1 = read
* 0 = write
* ENACMD12[2] : Auto CMD12 Enable
* ENBLKCNT[1] : Block Count Enable
* ENDMA[0] : DMA Enable
*/
mode = (1 << 1) | (1 << 0);
if (data->blocks > 1)
mode |= (1 << 5);
if (data->flags & MMC_DATA_READ)
mode |= (1 << 4);
writew(mode, &host->reg->trnmod);
}
static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
struct mmc_data *data)
{
struct mmc_host *host = (struct mmc_host *)mmc->priv;
int flags, i;
unsigned int timeout;
unsigned int mask;
unsigned int retry = 0x100000;
/* Wait max 10 ms */
timeout = 10;
/*
* PRNSTS
* CMDINHDAT[1] : Command Inhibit (DAT)
* CMDINHCMD[0] : Command Inhibit (CMD)
*/
mask = (1 << 0);
if ((data != NULL) || (cmd->resp_type & MMC_RSP_BUSY))
mask |= (1 << 1);
/*
* We shouldn't wait for data inihibit for stop commands, even
* though they might use busy signaling
*/
if (data)
mask &= ~(1 << 1);
while (readl(&host->reg->prnsts) & mask) {
if (timeout == 0) {
printf("%s: timeout error\n", __func__);
return -1;
}
timeout--;
udelay(1000);
}
if (data)
mmc_prepare_data(host, data);
debug("cmd->arg: %08x\n", cmd->cmdarg);
writel(cmd->cmdarg, &host->reg->argument);
if (data)
mmc_set_transfer_mode(host, data);
if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY))
return -1;
/*
* CMDREG
* CMDIDX[13:8] : Command index
* DATAPRNT[5] : Data Present Select
* ENCMDIDX[4] : Command Index Check Enable
* ENCMDCRC[3] : Command CRC Check Enable
* RSPTYP[1:0]
* 00 = No Response
* 01 = Length 136
* 10 = Length 48
* 11 = Length 48 Check busy after response
*/
if (!(cmd->resp_type & MMC_RSP_PRESENT))
flags = 0;
else if (cmd->resp_type & MMC_RSP_136)
flags = (1 << 0);
else if (cmd->resp_type & MMC_RSP_BUSY)
flags = (3 << 0);
else
flags = (2 << 0);
if (cmd->resp_type & MMC_RSP_CRC)
flags |= (1 << 3);
if (cmd->resp_type & MMC_RSP_OPCODE)
flags |= (1 << 4);
if (data)
flags |= (1 << 5);
debug("cmd: %d\n", cmd->cmdidx);
writew((cmd->cmdidx << 8) | flags, &host->reg->cmdreg);
for (i = 0; i < retry; i++) {
mask = readl(&host->reg->norintsts);
/* Command Complete */
if (mask & (1 << 0)) {
if (!data)
writel(mask, &host->reg->norintsts);
break;
}
}
if (i == retry) {
printf("%s: waiting for status update\n", __func__);
return TIMEOUT;
}
if (mask & (1 << 16)) {
/* Timeout Error */
debug("timeout: %08x cmd %d\n", mask, cmd->cmdidx);
return TIMEOUT;
} else if (mask & (1 << 15)) {
/* Error Interrupt */
debug("error: %08x cmd %d\n", mask, cmd->cmdidx);
return -1;
}
if (cmd->resp_type & MMC_RSP_PRESENT) {
if (cmd->resp_type & MMC_RSP_136) {
/* CRC is stripped so we need to do some shifting. */
for (i = 0; i < 4; i++) {
unsigned int offset =
(unsigned int)(&host->reg->rspreg3 - i);
cmd->response[i] = readl(offset) << 8;
if (i != 3) {
cmd->response[i] |=
readb(offset - 1);
}
debug("cmd->resp[%d]: %08x\n",
i, cmd->response[i]);
}
} else if (cmd->resp_type & MMC_RSP_BUSY) {
for (i = 0; i < retry; i++) {
/* PRNTDATA[23:20] : DAT[3:0] Line Signal */
if (readl(&host->reg->prnsts)
& (1 << 20)) /* DAT[0] */
break;
}
if (i == retry) {
printf("%s: card is still busy\n", __func__);
return TIMEOUT;
}
cmd->response[0] = readl(&host->reg->rspreg0);
debug("cmd->resp[0]: %08x\n", cmd->response[0]);