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
This commit is contained in:
Wolfgang Denk 2012-05-20 21:20:50 +02:00
commit 6bc337fb13
19 changed files with 339 additions and 659 deletions

View file

@ -21,53 +21,54 @@
#ifndef __ASM_ARCH_MMC_H_ #ifndef __ASM_ARCH_MMC_H_
#define __ASM_ARCH_MMC_H_ #define __ASM_ARCH_MMC_H_
#ifndef __ASSEMBLY__ #define SDHCI_CONTROL2 0x80
struct s5p_mmc { #define SDHCI_CONTROL3 0x84
unsigned int sysad; #define SDHCI_CONTROL4 0x8C
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];
};
struct mmc_host { #define SDHCI_CTRL2_ENSTAASYNCCLR (1 << 31)
struct s5p_mmc *reg; #define SDHCI_CTRL2_ENCMDCNFMSK (1 << 30)
unsigned int version; /* SDHCI spec. version */ #define SDHCI_CTRL2_CDINVRXD3 (1 << 29)
unsigned int clock; /* Current clock (MHz) */ #define SDHCI_CTRL2_SLCARDOUT (1 << 28)
int dev_index;
};
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 #endif

View file

@ -21,53 +21,54 @@
#ifndef __ASM_ARCH_MMC_H_ #ifndef __ASM_ARCH_MMC_H_
#define __ASM_ARCH_MMC_H_ #define __ASM_ARCH_MMC_H_
#ifndef __ASSEMBLY__ #define SDHCI_CONTROL2 0x80
struct s5p_mmc { #define SDHCI_CONTROL3 0x84
unsigned int sysad; #define SDHCI_CONTROL4 0x8C
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];
};
struct mmc_host { #define SDHCI_CTRL2_ENSTAASYNCCLR (1 << 31)
struct s5p_mmc *reg; #define SDHCI_CTRL2_ENCMDCNFMSK (1 << 30)
unsigned int version; /* SDHCI spec. version */ #define SDHCI_CTRL2_CDINVRXD3 (1 << 29)
unsigned int clock; /* Current clock (MHz) */ #define SDHCI_CTRL2_SLCARDOUT (1 << 28)
int dev_index;
};
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 #endif

View file

@ -39,8 +39,8 @@ COBJS-$(CONFIG_MXS_MMC) += mxsmmc.o
COBJS-$(CONFIG_OMAP_HSMMC) += omap_hsmmc.o COBJS-$(CONFIG_OMAP_HSMMC) += omap_hsmmc.o
COBJS-$(CONFIG_PXA_MMC) += pxa_mmc.o COBJS-$(CONFIG_PXA_MMC) += pxa_mmc.o
COBJS-$(CONFIG_PXA_MMC_GENERIC) += pxa_mmc_gen.o COBJS-$(CONFIG_PXA_MMC_GENERIC) += pxa_mmc_gen.o
COBJS-$(CONFIG_S5P_MMC) += s5p_mmc.o
COBJS-$(CONFIG_SDHCI) += sdhci.o COBJS-$(CONFIG_SDHCI) += sdhci.o
COBJS-$(CONFIG_S5P_SDHCI) += s5p_sdhci.o
COBJS-$(CONFIG_SH_MMCIF) += sh_mmcif.o COBJS-$(CONFIG_SH_MMCIF) += sh_mmcif.o
COBJS-$(CONFIG_TEGRA2_MMC) += tegra2_mmc.o COBJS-$(CONFIG_TEGRA2_MMC) += tegra2_mmc.o

View file

@ -307,19 +307,56 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
#else #else
esdhc_write32(&regs->xfertyp, xfertyp); esdhc_write32(&regs->xfertyp, xfertyp);
#endif #endif
/* Mask all irqs */
esdhc_write32(&regs->irqsigen, 0);
/* Wait for the command to complete */ /* 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); irqstat = esdhc_read32(&regs->irqstat);
esdhc_write32(&regs->irqstat, 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) if (irqstat & CMD_ERR)
return COMM_ERR; return COMM_ERR;
if (irqstat & IRQSTAT_CTOE) if (irqstat & IRQSTAT_CTOE)
return TIMEOUT; 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 */ /* Copy the response to the response buffer */
if (cmd->resp_type & MMC_RSP_136) { if (cmd->resp_type & MMC_RSP_136) {
u32 cmdrsp3, cmdrsp2, cmdrsp1, cmdrsp0; u32 cmdrsp3, cmdrsp2, cmdrsp1, cmdrsp0;

View file

@ -1199,7 +1199,9 @@ int mmc_startup(struct mmc *mmc)
else else
mmc_set_clock(mmc, 25000000); mmc_set_clock(mmc, 25000000);
} else { } 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*/ /* Set the card to use 4 bit*/
err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BUS_WIDTH, width); EXT_CSD_BUS_WIDTH, width);

View file

@ -406,7 +406,7 @@ int mxsmmc_initialize(bd_t *bis, int id, int (*wp)(int))
*/ */
mmc->f_min = 400000; mmc->f_min = 400000;
mmc->f_max = mxc_get_clock(MXC_SSP0_CLK + id) * 1000 / 2; mmc->f_max = mxc_get_clock(MXC_SSP0_CLK + id) * 1000 / 2;
mmc->b_max = 0x40; mmc->b_max = 0x20;
mmc_register(mmc); mmc_register(mmc);
return 0; return 0;

View file

@ -33,6 +33,10 @@
#include <asm/arch/mmc_host_def.h> #include <asm/arch/mmc_host_def.h>
#include <asm/arch/sys_proto.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 */ /* If we fail after 1 second wait, something is really bad */
#define MAX_RETRY_MS 1000 #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) unsigned char mmc_board_init(struct mmc *mmc)
{ {
#if defined(CONFIG_TWL4030_POWER)
twl4030_power_mmc_init();
#endif
#if defined(CONFIG_OMAP34XX) #if defined(CONFIG_OMAP34XX)
t2_t *t2_base = (t2_t *)T2_BASE; t2_t *t2_base = (t2_t *)T2_BASE;
struct prcm *prcm_base = (struct prcm *)PRCM_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, PBIASSPEEDCTRL0 | PBIASLITEPWRDNZ0,
&t2_base->pbias_lite); &t2_base->pbias_lite);
@ -189,6 +199,27 @@ static int mmc_init_setup(struct mmc *mmc)
return 0; 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, static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
struct mmc_data *data) struct mmc_data *data)
@ -209,7 +240,8 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
start = get_timer(0); start = get_timer(0);
while (readl(&mmc_base->stat)) { while (readl(&mmc_base->stat)) {
if (get_timer(0) - start > MAX_RETRY_MS) { 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; return TIMEOUT;
} }
} }
@ -277,9 +309,10 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
} }
} while (!mmc_stat); } 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; return TIMEOUT;
else if ((mmc_stat & ERRI_MASK) != 0) } else if ((mmc_stat & ERRI_MASK) != 0)
return -1; return -1;
if (mmc_stat & CC_MASK) { 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); } 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) if ((mmc_stat & ERRI_MASK) != 0)
return 1; return 1;
@ -382,6 +418,9 @@ static int mmc_write_data(struct hsmmc *mmc_base, const char *buf,
} }
} while (mmc_stat == 0); } 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) if ((mmc_stat & ERRI_MASK) != 0)
return 1; return 1;

View file

@ -1,490 +0,0 @@
/*
* (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]);
} else {
cmd->response[0] = readl(&host->reg->rspreg0);
debug("cmd->resp[0]: %08x\n", cmd->response[0]);
}
}
if (data) {
while (1) {
mask = readl(&host->reg->norintsts);
if (mask & (1 << 15)) {
/* Error Interrupt */
writel(mask, &host->reg->norintsts);
printf("%s: error during transfer: 0x%08x\n",
__func__, mask);
return -1;
} else if (mask & (1 << 3)) {
/*
* DMA Interrupt, restart the transfer where
* it was interrupted.
*/
unsigned int address = readl(&host->reg->sysad);
debug("DMA end\n");
writel((1 << 3), &host->reg->norintsts);
writel(address, &host->reg->sysad);
} else if (mask & (1 << 1)) {
/* Transfer Complete */
debug("r/w is done\n");
break;
}
}
writel(mask, &host->reg->norintsts);
}
udelay(1000);
return 0;
}
static void mmc_change_clock(struct mmc_host *host, uint clock)
{
int div;
unsigned short clk;
unsigned long timeout;
unsigned long ctrl2;
/*
* SELBASECLK[5:4]
* 00/01 = HCLK
* 10 = EPLL
* 11 = XTI or XEXTCLK
*/
ctrl2 = readl(&host->reg->control2);
ctrl2 &= ~(3 << 4);
ctrl2 |= (2 << 4);
writel(ctrl2, &host->reg->control2);
writew(0, &host->reg->clkcon);
/* XXX: we assume that clock is between 40MHz and 50MHz */
if (clock == 0)
goto out;
else if (clock <= 400000)
div = 0x100;
else if (clock <= 20000000)
div = 4;
else if (clock <= 26000000)
div = 2;
else
div = 1;
debug("div: %d\n", div);
div >>= 1;
/*
* CLKCON
* SELFREQ[15:8] : base clock divied by value
* ENSDCLK[2] : SD Clock Enable
* STBLINTCLK[1] : Internal Clock Stable
* ENINTCLK[0] : Internal Clock Enable
*/
clk = (div << 8) | (1 << 0);
writew(clk, &host->reg->clkcon);
set_mmc_clk(host->dev_index, div);
/* Wait max 10 ms */
timeout = 10;
while (!(readw(&host->reg->clkcon) & (1 << 1))) {
if (timeout == 0) {
printf("%s: timeout error\n", __func__);
return;
}
timeout--;
udelay(1000);
}
clk |= (1 << 2);
writew(clk, &host->reg->clkcon);
out:
host->clock = clock;
}
static void mmc_set_ios(struct mmc *mmc)
{
struct mmc_host *host = mmc->priv;
unsigned char ctrl;
unsigned long val;
debug("bus_width: %x, clock: %d\n", mmc->bus_width, mmc->clock);
/*
* SELCLKPADDS[17:16]
* 00 = 2mA
* 01 = 4mA
* 10 = 7mA
* 11 = 9mA
*/
writel(0x3 << 16, &host->reg->control4);
val = readl(&host->reg->control2);
val &= (0x3 << 4);
val |= (1 << 31) | /* write status clear async mode enable */
(1 << 30) | /* command conflict mask enable */
(1 << 14) | /* Feedback Clock Enable for Rx Clock */
(1 << 8); /* SDCLK hold enable */
writel(val, &host->reg->control2);
/*
* FCSEL1[15] FCSEL0[7]
* FCSel[1:0] : Rx Feedback Clock Delay Control
* Inverter delay means10ns delay if SDCLK 50MHz setting
* 01 = Delay1 (basic delay)
* 11 = Delay2 (basic delay + 2ns)
* 00 = Delay3 (inverter delay)
* 10 = Delay4 (inverter delay + 2ns)
*/
writel(0x8080, &host->reg->control3);
mmc_change_clock(host, mmc->clock);
ctrl = readb(&host->reg->hostctl);
/*
* WIDE8[5]
* 0 = Depend on WIDE4
* 1 = 8-bit mode
* WIDE4[1]
* 1 = 4-bit mode
* 0 = 1-bit mode
*/
if (mmc->bus_width == 8)
ctrl |= (1 << 5);
else if (mmc->bus_width == 4)
ctrl |= (1 << 1);
else
ctrl &= ~(1 << 1);
/*
* OUTEDGEINV[2]
* 1 = Riging edge output
* 0 = Falling edge output
*/
ctrl &= ~(1 << 2);
writeb(ctrl, &host->reg->hostctl);
}
static void mmc_reset(struct mmc_host *host)
{
unsigned int timeout;
/*
* RSTALL[0] : Software reset for all
* 1 = reset
* 0 = work
*/
writeb((1 << 0), &host->reg->swrst);
host->clock = 0;
/* Wait max 100 ms */
timeout = 100;
/* hw clears the bit when it's done */
while (readb(&host->reg->swrst) & (1 << 0)) {
if (timeout == 0) {
printf("%s: timeout error\n", __func__);
return;
}
timeout--;
udelay(1000);
}
}
static int mmc_core_init(struct mmc *mmc)
{
struct mmc_host *host = (struct mmc_host *)mmc->priv;
unsigned int mask;
mmc_reset(host);
host->version = readw(&host->reg->hcver);
/* mask all */
writel(0xffffffff, &host->reg->norintstsen);
writel(0xffffffff, &host->reg->norintsigen);
writeb(0xe, &host->reg->timeoutcon); /* TMCLK * 2^27 */
/*
* NORMAL Interrupt Status Enable Register init
* [5] ENSTABUFRDRDY : Buffer Read Ready Status Enable
* [4] ENSTABUFWTRDY : Buffer write Ready Status Enable
* [3] ENSTADMAINT : DMA Interrupt Status Enable
* [1] ENSTASTANSCMPLT : Transfre Complete Status Enable
* [0] ENSTACMDCMPLT : Command Complete Status Enable
*/
mask = readl(&host->reg->norintstsen);
mask &= ~(0xffff);
mask |= (1 << 5) | (1 << 4) | (1 << 3) | (1 << 1) | (1 << 0);
writel(mask, &host->reg->norintstsen);
/*
* NORMAL Interrupt Signal Enable Register init
* [1] ENSTACMDCMPLT : Transfer Complete Signal Enable
*/
mask = readl(&host->reg->norintsigen);
mask &= ~(0xffff);
mask |= (1 << 1);
writel(mask, &host->reg->norintsigen);
return 0;
}
static int s5p_mmc_initialize(int dev_index, int bus_width)
{
struct mmc *mmc;
mmc = &mmc_dev[dev_index];
sprintf(mmc->name, "SAMSUNG SD/MMC");
mmc->priv = &mmc_host[dev_index];
mmc->send_cmd = mmc_send_cmd;
mmc->set_ios = mmc_set_ios;
mmc->init = mmc_core_init;
mmc->getcd = NULL;
mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
if (bus_width == 8)
mmc->host_caps = MMC_MODE_8BIT;
else
mmc->host_caps = MMC_MODE_4BIT;
mmc->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS | MMC_MODE_HC;
mmc->f_min = 400000;
mmc->f_max = 52000000;
mmc_host[dev_index].dev_index = dev_index;
mmc_host[dev_index].clock = 0;
mmc_host[dev_index].reg = s5p_get_base_mmc(dev_index);
mmc->b_max = 0;
mmc_register(mmc);
return 0;
}
int s5p_mmc_init(int dev_index, int bus_width)
{
return s5p_mmc_initialize(dev_index, bus_width);
}

98
drivers/mmc/s5p_sdhci.c Normal file
View file

@ -0,0 +1,98 @@
/*
* (C) Copyright 2012 SAMSUNG Electronics
* 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 <malloc.h>
#include <sdhci.h>
#include <asm/arch/mmc.h>
static char *S5P_NAME = "SAMSUNG SDHCI";
static void s5p_sdhci_set_control_reg(struct sdhci_host *host)
{
unsigned long val, ctrl;
/*
* SELCLKPADDS[17:16]
* 00 = 2mA
* 01 = 4mA
* 10 = 7mA
* 11 = 9mA
*/
sdhci_writel(host, SDHCI_CTRL4_DRIVE_MASK(0x3), SDHCI_CONTROL4);
val = sdhci_readl(host, SDHCI_CONTROL2);
val &= SDHCI_CTRL2_SELBASECLK_SHIFT;
val |= SDHCI_CTRL2_ENSTAASYNCCLR |
SDHCI_CTRL2_ENCMDCNFMSK |
SDHCI_CTRL2_ENFBCLKRX |
SDHCI_CTRL2_ENCLKOUTHOLD;
sdhci_writel(host, val, SDHCI_CONTROL2);
/*
* FCSEL3[31] FCSEL2[23] FCSEL1[15] FCSEL0[7]
* FCSel[1:0] : Rx Feedback Clock Delay Control
* Inverter delay means10ns delay if SDCLK 50MHz setting
* 01 = Delay1 (basic delay)
* 11 = Delay2 (basic delay + 2ns)
* 00 = Delay3 (inverter delay)
* 10 = Delay4 (inverter delay + 2ns)
*/
val = SDHCI_CTRL3_FCSEL3 | SDHCI_CTRL3_FCSEL1;
sdhci_writel(host, val, SDHCI_CONTROL3);
/*
* SELBASECLK[5:4]
* 00/01 = HCLK
* 10 = EPLL
* 11 = XTI or XEXTCLK
*/
ctrl = sdhci_readl(host, SDHCI_CONTROL2);
ctrl &= ~SDHCI_CTRL2_SELBASECLK_MASK(0x3);
ctrl |= SDHCI_CTRL2_SELBASECLK_MASK(0x2);
sdhci_writel(host, ctrl, SDHCI_CONTROL2);
}
int s5p_sdhci_init(u32 regbase, u32 max_clk, u32 min_clk, u32 quirks)
{
struct sdhci_host *host = NULL;
host = (struct sdhci_host *)malloc(sizeof(struct sdhci_host));
if (!host) {
printf("sdhci__host malloc fail!\n");
return 1;
}
host->name = S5P_NAME;
host->ioaddr = (void *)regbase;
host->quirks = quirks;
host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_VOLTAGE;
host->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
if (quirks & SDHCI_QUIRK_REG32_RW)
host->version = sdhci_readl(host, SDHCI_HOST_VERSION - 2) >> 16;
else
host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
host->set_control_reg = &s5p_sdhci_set_control_reg;
host->host_caps = MMC_MODE_HC;
add_sdhci(host, max_clk, min_clk);
return 0;
}

View file

@ -128,6 +128,7 @@ int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd,
int trans_bytes = 0, is_aligned = 1; int trans_bytes = 0, is_aligned = 1;
u32 mask, flags, mode; u32 mask, flags, mode;
unsigned int timeout, start_addr = 0; unsigned int timeout, start_addr = 0;
unsigned int retry = 10000;
/* Wait max 10 ms */ /* Wait max 10 ms */
timeout = 10; timeout = 10;
@ -210,8 +211,19 @@ int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd,
stat = sdhci_readl(host, SDHCI_INT_STATUS); stat = sdhci_readl(host, SDHCI_INT_STATUS);
if (stat & SDHCI_INT_ERROR) if (stat & SDHCI_INT_ERROR)
break; break;
if (--retry == 0)
break;
} while ((stat & mask) != mask); } while ((stat & mask) != mask);
if (retry == 0) {
if (host->quirks & SDHCI_QUIRK_BROKEN_R1B)
return 0;
else {
printf("Timeout for status update!\n");
return TIMEOUT;
}
}
if ((stat & (SDHCI_INT_ERROR | mask)) == mask) { if ((stat & (SDHCI_INT_ERROR | mask)) == mask) {
sdhci_cmd_done(host, cmd); sdhci_cmd_done(host, cmd);
sdhci_writel(host, mask, SDHCI_INT_STATUS); sdhci_writel(host, mask, SDHCI_INT_STATUS);
@ -325,6 +337,9 @@ void sdhci_set_ios(struct mmc *mmc)
u32 ctrl; u32 ctrl;
struct sdhci_host *host = (struct sdhci_host *)mmc->priv; struct sdhci_host *host = (struct sdhci_host *)mmc->priv;
if (host->set_control_reg)
host->set_control_reg(host);
if (mmc->clock != host->clock) if (mmc->clock != host->clock)
sdhci_set_clock(mmc, mmc->clock); sdhci_set_clock(mmc, mmc->clock);
@ -348,6 +363,9 @@ void sdhci_set_ios(struct mmc *mmc)
else else
ctrl &= ~SDHCI_CTRL_HISPD; ctrl &= ~SDHCI_CTRL_HISPD;
if (host->quirks & SDHCI_QUIRK_NO_HISPD_BIT)
ctrl &= ~SDHCI_CTRL_HISPD;
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
} }
@ -431,9 +449,15 @@ int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk)
mmc->voltages |= MMC_VDD_29_30 | MMC_VDD_30_31; mmc->voltages |= MMC_VDD_29_30 | MMC_VDD_30_31;
if (caps & SDHCI_CAN_VDD_180) if (caps & SDHCI_CAN_VDD_180)
mmc->voltages |= MMC_VDD_165_195; mmc->voltages |= MMC_VDD_165_195;
if (host->quirks & SDHCI_QUIRK_BROKEN_VOLTAGE)
mmc->voltages |= host->voltages;
mmc->host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT; mmc->host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT;
if (caps & SDHCI_CAN_DO_8BIT) if (caps & SDHCI_CAN_DO_8BIT)
mmc->host_caps |= MMC_MODE_8BIT; mmc->host_caps |= MMC_MODE_8BIT;
if (host->host_caps)
mmc->host_caps |= host->host_caps;
sdhci_reset(host, SDHCI_RESET_ALL); sdhci_reset(host, SDHCI_RESET_ALL);
mmc_register(mmc); mmc_register(mmc);

View file

@ -69,9 +69,10 @@
#define EXYNOS4_DEFAULT_UART_OFFSET 0x020000 #define EXYNOS4_DEFAULT_UART_OFFSET 0x020000
/* SD/MMC configuration */ /* SD/MMC configuration */
#define CONFIG_GENERIC_MMC 1 #define CONFIG_GENERIC_MMC
#define CONFIG_MMC 1 #define CONFIG_MMC
#define CONFIG_S5P_MMC 1 #define CONFIG_SDHCI
#define CONFIG_S5P_SDHCI
/* PWM */ /* PWM */
#define CONFIG_PWM 1 #define CONFIG_PWM 1

View file

@ -63,9 +63,10 @@
#define CONFIG_BAUDRATE 115200 #define CONFIG_BAUDRATE 115200
/* MMC */ /* MMC */
#define CONFIG_GENERIC_MMC 1 #define CONFIG_GENERIC_MMC
#define CONFIG_MMC 1 #define CONFIG_MMC
#define CONFIG_S5P_MMC 1 #define CONFIG_SDHCI
#define CONFIG_S5P_SDHCI
/* PWM */ /* PWM */
#define CONFIG_PWM 1 #define CONFIG_PWM 1

View file

@ -66,9 +66,10 @@
#define CONFIG_BAUDRATE 115200 #define CONFIG_BAUDRATE 115200
/* MMC */ /* MMC */
#define CONFIG_GENERIC_MMC 1 #define CONFIG_GENERIC_MMC
#define CONFIG_MMC 1 #define CONFIG_MMC
#define CONFIG_S5P_MMC 1 #define CONFIG_SDHCI
#define CONFIG_S5P_SDHCI
/* PWM */ /* PWM */
#define CONFIG_PWM 1 #define CONFIG_PWM 1

View file

@ -78,7 +78,8 @@
/* SD/MMC configuration */ /* SD/MMC configuration */
#define CONFIG_GENERIC_MMC #define CONFIG_GENERIC_MMC
#define CONFIG_MMC #define CONFIG_MMC
#define CONFIG_S5P_MMC #define CONFIG_SDHCI
#define CONFIG_S5P_SDHCI
#define CONFIG_BOARD_EARLY_INIT_F #define CONFIG_BOARD_EARLY_INIT_F

View file

@ -68,9 +68,10 @@
#define EXYNOS4_DEFAULT_UART_OFFSET 0x010000 #define EXYNOS4_DEFAULT_UART_OFFSET 0x010000
/* SD/MMC configuration */ /* SD/MMC configuration */
#define CONFIG_GENERIC_MMC 1 #define CONFIG_GENERIC_MMC
#define CONFIG_MMC 1 #define CONFIG_MMC
#define CONFIG_S5P_MMC 1 #define CONFIG_SDHCI
#define CONFIG_S5P_SDHCI
/* PWM */ /* PWM */
#define CONFIG_PWM 1 #define CONFIG_PWM 1

View file

@ -74,7 +74,8 @@
/* MMC */ /* MMC */
#define CONFIG_GENERIC_MMC #define CONFIG_GENERIC_MMC
#define CONFIG_MMC #define CONFIG_MMC
#define CONFIG_S5P_MMC #define CONFIG_S5P_SDHCI
#define CONFIG_SDHCI
/* PWM */ /* PWM */
#define CONFIG_PWM #define CONFIG_PWM

View file

@ -34,12 +34,13 @@
#define SYSCTL_INITA 0x08000000 #define SYSCTL_INITA 0x08000000
#define SYSCTL_TIMEOUT_MASK 0x000f0000 #define SYSCTL_TIMEOUT_MASK 0x000f0000
#define SYSCTL_CLOCK_MASK 0x0000fff0 #define SYSCTL_CLOCK_MASK 0x0000fff0
#define SYSCTL_RSTA 0x01000000
#define SYSCTL_CKEN 0x00000008 #define SYSCTL_CKEN 0x00000008
#define SYSCTL_PEREN 0x00000004 #define SYSCTL_PEREN 0x00000004
#define SYSCTL_HCKEN 0x00000002 #define SYSCTL_HCKEN 0x00000002
#define SYSCTL_IPGEN 0x00000001 #define SYSCTL_IPGEN 0x00000001
#define SYSCTL_RSTA 0x01000000 #define SYSCTL_RSTA 0x01000000
#define SYSCTL_RSTC 0x02000000
#define SYSCTL_RSTD 0x04000000
#define IRQSTAT 0x0002e030 #define IRQSTAT 0x0002e030
#define IRQSTAT_DMAE (0x10000000) #define IRQSTAT_DMAE (0x10000000)
@ -85,6 +86,7 @@
#define IRQSTATEN_CC (0x00000001) #define IRQSTATEN_CC (0x00000001)
#define PRSSTAT 0x0002e024 #define PRSSTAT 0x0002e024
#define PRSSTAT_DAT0 (0x01000000)
#define PRSSTAT_CLSL (0x00800000) #define PRSSTAT_CLSL (0x00800000)
#define PRSSTAT_WPSPL (0x00080000) #define PRSSTAT_WPSPL (0x00080000)
#define PRSSTAT_CDPL (0x00040000) #define PRSSTAT_CDPL (0x00040000)

View file

@ -47,6 +47,9 @@
#define MMC_MODE_SPI 0x400 #define MMC_MODE_SPI 0x400
#define MMC_MODE_HC 0x800 #define MMC_MODE_HC 0x800
#define MMC_MODE_MASK_WIDTH_BITS (MMC_MODE_4BIT | MMC_MODE_8BIT)
#define MMC_MODE_WIDTH_BITS_SHIFT 8
#define SD_DATA_4BIT 0x00040000 #define SD_DATA_4BIT 0x00040000
#define IS_SD(x) (x->version & SD_VERSION_SD) #define IS_SD(x) (x->version & SD_VERSION_SD)
@ -205,56 +208,6 @@ struct mmc_cid {
char pnm[7]; char pnm[7];
}; };
/*
* WARNING!
*
* This structure is used by atmel_mci.c only.
* It works for the AVR32 architecture but NOT
* for ARM/AT91 architectures.
* Its use is highly depreciated.
* After the atmel_mci.c driver for AVR32 has
* been replaced this structure will be removed.
*/
struct mmc_csd
{
u8 csd_structure:2,
spec_vers:4,
rsvd1:2;
u8 taac;
u8 nsac;
u8 tran_speed;
u16 ccc:12,
read_bl_len:4;
u64 read_bl_partial:1,
write_blk_misalign:1,
read_blk_misalign:1,
dsr_imp:1,
rsvd2:2,
c_size:12,
vdd_r_curr_min:3,
vdd_r_curr_max:3,
vdd_w_curr_min:3,
vdd_w_curr_max:3,
c_size_mult:3,
sector_size:5,
erase_grp_size:5,
wp_grp_size:5,
wp_grp_enable:1,
default_ecc:2,
r2w_factor:3,
write_bl_len:4,
write_bl_partial:1,
rsvd3:5;
u8 file_format_grp:1,
copy:1,
perm_write_protect:1,
tmp_write_protect:1,
file_format:2,
ecc:2;
u8 crc:7;
u8 one:1;
};
struct mmc_cmd { struct mmc_cmd {
ushort cmdidx; ushort cmdidx;
uint resp_type; uint resp_type;

View file

@ -216,6 +216,9 @@
*/ */
#define SDHCI_QUIRK_32BIT_DMA_ADDR (1 << 0) #define SDHCI_QUIRK_32BIT_DMA_ADDR (1 << 0)
#define SDHCI_QUIRK_REG32_RW (1 << 1) #define SDHCI_QUIRK_REG32_RW (1 << 1)
#define SDHCI_QUIRK_BROKEN_R1B (1 << 2)
#define SDHCI_QUIRK_NO_HISPD_BIT (1 << 3)
#define SDHCI_QUIRK_BROKEN_VOLTAGE (1 << 4)
/* to make gcc happy */ /* to make gcc happy */
struct sdhci_host; struct sdhci_host;
@ -240,10 +243,14 @@ struct sdhci_host {
char *name; char *name;
void *ioaddr; void *ioaddr;
unsigned int quirks; unsigned int quirks;
unsigned int host_caps;
unsigned int version; unsigned int version;
unsigned int clock; unsigned int clock;
struct mmc *mmc; struct mmc *mmc;
const struct sdhci_ops *ops; const struct sdhci_ops *ops;
void (*set_control_reg)(struct sdhci_host *host);
uint voltages;
}; };
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS