Hi Jaehoon Chung, Yes you need to apply the following patchset http://comments.gmane.org/gmane.comp.boot-loaders.u-boot/132754
Regards, Rajeshwari Shinde. On Tue, Jun 12, 2012 at 2:07 PM, Jaehoon Chung <jh80.ch...@samsung.com> wrote: > Hi Rajeshwari, > > Before applied this patch, it must apply your patch for PINMUX. right? > > Best Regards, > Jaehoon Chung > > On 06/12/2012 03:14 PM, Chander Kashyap wrote: > >> Hi, >> >> On 11 June 2012 19:26, Rajeshwari Birje <rajeshwari.bi...@gmail.com> wrote: >>> Hi All, >>> >>> ccing Jaehoon Chung >>> >>> Regards, >>> Rajeshwari Shinde. >>> >>> >>> On Mon, Jun 11, 2012 at 6:18 PM, Rajeshwari Shinde >>> <rajeshwar...@samsung.com> wrote: >>>> Add DWMMC driver support and resgister description for same. >>>> >>>> Signed-off-by: Alim Akhtar <alim.akh...@samsung.com> >>>> Signed-off-by: Terry Lambert <tlamb...@chromium.org> >>>> Signed-off-by: Rajeshwari Shinde <rajeshwar...@samsung.com> >>>> --- >>>> Changes in V2: >>>> - Incorporated comments from Jaehung Chung. >>>> - Renamed MSHCI to DWMMC through out the driver. >>>> - Renamed files to exynos_dwmmc from exynos_mshc. >>>> - Removed major hard codings of values. >>>> - Wrote dw_mci_writel and dw_mci_readl functions for writel and >>>> readl. >>>> - Removed structure of registers and defined each one separately. >>>> orch/arm/include/asm/arch-exynos/exynos_dwmmc.h | 229 +++++++++ >>>> drivers/mmc/Makefile | 1 + >>>> drivers/mmc/exynos_dwmmc.c | 566 >>>> +++++++++++++++++++++++ >>>> 3 files changed, 796 insertions(+), 0 deletions(-) >>>> create mode 100644 arch/arm/include/asm/arch-exynos/exynos_dwmmc.h >>>> create mode 100644 drivers/mmc/exynos_dwmmc.c >>>> >>>> diff --git a/arch/arm/include/asm/arch-exynos/exynos_dwmmc.h >>>> b/arch/arm/include/asm/arch-exynos/exynos_dwmmc.h >>>> new file mode 100644 >>>> index 0000000..349bd75 >>>> --- /dev/null >>>> +++ b/arch/arm/include/asm/arch-exynos/exynos_dwmmc.h >>>> @@ -0,0 +1,229 @@ >>>> +/* >>>> + * (C) Copyright 2012 SAMSUNG Electronics >>>> + * Abhilash Kesavan <a.kesa...@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 >>>> + * >>>> + */ >>>> +#ifndef __ASM_ARCH_COMMON_DWMMC_H >>>> +#define __ASM_ARCH_COMMON_DWMMC_H >>>> + >>>> +#include <asm/arch/pinmux.h> >>>> + >>>> +#ifndef __ASSEMBLY__ >>>> +struct dw_mci_host { >>>> + void *ioaddr; >>>> + unsigned int clock; /* Current clock in MHz */ >>>> + enum periph_id peripheral; >>>> + unsigned int verid; /* SDHCI spec. version */ >>>> + unsigned int data_offset; /* DATA offset */ >>>> +}; >>>> + >>>> +/* >>>> + * Struct idma >>>> + * Holds the descriptor list >>>> + */ >>>> +struct dw_mci_idmac { >>>> + u32 des0; >>>> + u32 des1; >>>> + u32 des2; >>>> + u32 des3; >>>> +}; >>>> + >> #endif >>>> +/* Control Register Register */ >>>> +#define DWMCI_CONTROL 0x00 >>>> +#define CTRL_RESET (0x1 << 0) >>>> +#define FIFO_RESET (0x1 << 1) >>>> +#define DMA_RESET (0x1 << 2) >>>> +#define DMA_ENABLE (0x1 << 5) >>>> +#define SEND_AS_CCSD (0x1 << 10) >>>> +#define ENABLE_IDMAC (0x1 << 25) >>>> + >>>> +/* Power Enable Register */ >>>> +#define DWMCI_PWREN 0x04 >>>> +#define POWER_ENABLE (0x1 << 0) >>>> + >>>> +#define DWMCI_CLKDIV 0x08 >>>> +#define DWMCI_CLKSRC 0x0c >>>> + >>>> +/* Clock Enable Register */ >>>> +#define DWMCI_CLKENA 0x10 >>>> +#define CLK_ENABLE (0x1 << 0) >>>> +#define CLK_DISABLE (0x0 << 0) >>>> + >>>> +/* Timeout Register */ >>>> +#define DWMCI_TMOUT 0x14 >>>> +#define TMOUT_MAX 0xffffffff >>>> + >>>> +/* Card Type Register */ >>>> +#define DWMCI_CTYPE 0x18 >>>> +#define PORT0_CARD_WIDTH1 0 >>>> +#define PORT0_CARD_WIDTH4 (0x1 << 0) >>>> +#define PORT0_CARD_WIDTH8 (0x1 << 16) >>>> + >>>> +#define DWMCI_BLKSIZE 0x1c >>>> +#define DWMCI_BYTCNT 0x20 >>>> + >>>> +/* Interrupt Mask Register */ >>>> +#define DWMCI_INTMASK 0x24 >>>> +#define INTMSK_ALL 0xffffffff >>>> +#define INTMSK_RE (0x1 << 1) >>>> +#define INTMSK_CDONE (0x1 << 2) >>>> +#define INTMSK_DTO (0x1 << 3) >>>> +#define INTMSK_DCRC (0x1 << 7) >>>> +#define INTMSK_RTO (0x1 << 8) >>>> +#define INTMSK_DRTO (0x1 << 9) >>>> +#define INTMSK_HTO (0x1 << 10) >>>> +#define INTMSK_FRUN (0x1 << 11) >>>> +#define INTMSK_HLE (0x1 << 12) >>>> +#define INTMSK_SBE (0x1 << 13) >>>> +#define INTMSK_ACD (0x1 << 14) >>>> +#define INTMSK_EBE (0x1 << 15) >>>> + >>>> +#define DWMCI_CMDARG 0x28 >>>> + >>>> +/* Command Register */ >>>> +#define DWMCI_CMD 0x2c >>>> +#define CMD_RESP_EXP_BIT (0x1 << 6) >>>> +#define CMD_RESP_LENGTH_BIT (0x1 << 7) >>>> +#define CMD_CHECK_CRC_BIT (0x1 << 8) >>>> +#define CMD_DATA_EXP_BIT (0x1 << 9) >>>> +#define CMD_RW_BIT (0x1 << 10) >>>> +#define CMD_SENT_AUTO_STOP_BIT (0x1 << 12) >>>> +#define CMD_WAIT_PRV_DAT_BIT (0x1 << 13) >>>> +#define CMD_SEND_CLK_ONLY (0x1 << 21) >>>> +#define CMD_USE_HOLD_REG (0x1 << 29) >>>> +#define CMD_STRT_BIT (0x1 << 31) >>>> +#define CMD_ONLY_CLK (CMD_STRT_BIT | CMD_SEND_CLK_ONLY | \ >>>> + CMD_WAIT_PRV_DAT_BIT) >>>> + >>>> +#define DWMCI_RESP0 0x30 >>>> +#define DWMCI_RESP1 0x34 >>>> +#define DWMCI_RESP2 0x38 >>>> +#define DWMCI_RESP3 0x3c >>>> + >>>> +#define DWMCI_MINTSTS 0x40 >>>> + >>>> +/* Raw Interrupt Register */ >>>> +#define DWMCI_RINTSTS 0x44 >>>> +#define DATA_ERR (INTMSK_EBE | INTMSK_SBE | INTMSK_HLE | \ >>>> + INTMSK_FRUN | INTMSK_EBE | INTMSK_DCRC) >>>> +#define DATA_TOUT (INTMSK_HTO | INTMSK_DRTO) >>>> + >>>> +/* Status Register */ >>>> +#define DWMCI_STATUS 0x48 >>>> +#define DATA_BUSY (0x1 << 9) >>>> + >>>> +/* FIFO Threshold Watermark Register */ >>>> +#define DWMCI_FIFOTH 0x4c >>>> +#define TX_WMARK (0xFFF << 0) >>>> +#define RX_WMARK (0xFFF << 16) >>>> +#define MSIZE_MASK (0x7 << 28) >>>> + >>>> +#define DWMCI_CDETECT 0x50 >>>> +#define DWMCI_WRTORT 0x54 >>>> +#define DWMCI_GPIO 0x58 >>>> +#define DWMCI_TCBCNT 0x5c >>>> +#define DWMCI_TBBCNT 0x60 >>>> +#define DWMCI_DEBENCE 0x64 >>>> +#define DWMCI_USRID 0x68 >>>> +#define DWMCI_VERID 0x6c >>>> +#define DWMCI_HCON 0x70 >>>> +#define DWMCI_UHS_REG 0x74 >>>> +#define DWMCI_RST_n 0x78 >>>> + >>>> +/* DW DMA Mutiple Transaction Size */ >>>> +#define MSIZE_8 (2 << 28) >>>> + >>>> +/* Bus Mode Register */ >>>> +#define DWMCI_BMOD 0x80 >>>> +#define BMOD_IDMAC_RESET (0x1 << 0) >>>> +#define BMOD_IDMAC_FB (0x1 << 1) >>>> +#define BMOD_IDMAC_ENABLE (0x1 << 7) >>>> + >>>> +#define DWMCI_PLDMND 0x84 >>>> +#define DWMCI_DBADDR 0x88 >>>> + >>>> +/* IDMAC bits */ >>>> +#define DWMCI_IDSTS 0x8c >>>> +#define DWMCI_IDMAC_OWN (0x1 << 31) >>>> +#define DWMCI_IDMAC_CH (0x1 << 4) >>>> +#define DWMCI_IDMAC_FS (0x1 << 3) >>>> +#define DWMCI_IDMAC_LD (0x1 << 2) >>>> + >>>> +#define DWMCI_IDINTEN 0x90 >>>> +#define DWMCI_DSCADDR 0x94 >>>> +#define DWMCI_BUFADDR 0x98 >>>> + >>>> +/* CLKSEL bits*/ >>>> +#define DWMCI_CLKSEL 0x9c >>>> +#define SELCLK_SAMPLE_1PHASE_Shift (0x1 << 0) >>>> +#define SELCLK_DRV_3PHASE_SHIFT (0x3 << 16) >>>> +#define SELCLK_DRV_2PHASE_SHIFT (0x2 << 16) >>>> +#define SELCLK_DIV_RATIO (0x3 << 24) >>>> + >>>> +#define DWMCI_CARDTHRCTL 0x100 >>>> + >>>> +/* >>>> + * Data offset is difference according to Version >>>> + * Lower than 2.40a : data register offest is 0x100 >>>> + */ >>>> +#define DW_MMC_240A 0x240a >>>> +#define DATA_OFFSET 0x100 >>>> +#define DATA_240A_OFFSET 0x200 >>>> + >>>> +#define MAX_DWMMC_CLOCK 52000000 /* Max limit is 52MHz */ >>>> +#define MIN_DWMMC_CLOCK 400000 /* Lower limit is 400KHz */ >>>> +#define COMMAND_TIMEOUT 10000 >>>> +#define TIMEOUT_MS 100 >>>> +#define MAXCLKDIV 0xff >>>> + >>>> +/* Version ID register define */ >>>> +#define GET_VERID(x) ((x) & 0xFFFF) >>>> + >>>> +static inline void dw_mci_writel(struct dw_mci_host *host, u32 val, int >>>> reg) >>>> +{ >>>> + writel(val, host->ioaddr + reg); >>>> +} >>>> + >>>> +static inline void dw_mci_writew(struct dw_mci_host *host, u16 val, int >>>> reg) >>>> +{ >>>> + writew(val, host->ioaddr + reg); >>>> +} >>>> + >>>> +static inline void dw_mci_writeb(struct dw_mci_host *host, u8 val, int >>>> reg) >>>> +{ >>>> + writeb(val, host->ioaddr + reg); >>>> +} >>>> + >>>> +static inline u32 dw_mci_readl(struct dw_mci_host *host, int reg) >>>> +{ >>>> + return readl(host->ioaddr + reg); >>>> +} >>>> + >>>> +static inline u16 dw_mci_readw(struct dw_mci_host *host, int reg) >>>> +{ >>>> + return readw(host->ioaddr + reg); >>>> +} >>>> + >>>> +static inline u8 dw_mci_readb(struct dw_mci_host *host, int reg) >>>> +{ >>>> + return readb(host->ioaddr + reg); >>>> +} >>>> + >>>> +int dw_mci_init(enum periph_id periph_id, int bus_width); >>>> + >>>> +#endif /* __ASSEMBLY__ */ >> remove at this place after structure declaration. >>>> +#endif /* __ASM_ARCH_COMMON_DWMMC_H */ >>>> diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile >>>> index c245352..cf0be05 100644 >>>> --- a/drivers/mmc/Makefile >>>> +++ b/drivers/mmc/Makefile >>>> @@ -27,6 +27,7 @@ LIB := $(obj)libmmc.o >>>> >>>> COBJS-$(CONFIG_BFIN_SDH) += bfin_sdh.o >>>> COBJS-$(CONFIG_DAVINCI_MMC) += davinci_mmc.o >>>> +COBJS-$(CONFIG_EXYNOS_DWMMC) += exynos_dwmmc.o >>>> COBJS-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o >>>> COBJS-$(CONFIG_FTSDC010) += ftsdc010_esdhc.o >>>> COBJS-$(CONFIG_GENERIC_MMC) += mmc.o >>>> diff --git a/drivers/mmc/exynos_dwmmc.c b/drivers/mmc/exynos_dwmmc.c >>>> new file mode 100644 >>>> index 0000000..96f6ceb >>>> --- /dev/null >>>> +++ b/drivers/mmc/exynos_dwmmc.c >>>> @@ -0,0 +1,566 @@ >>>> +/* >>>> + * (C) Copyright 2012 Samsung Electronics Co. Ltd >>>> + * >>>> + * See file CREDITS for list of people who contributed to this >>>> + * project. >>>> + * >>>> + * 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/errno.h> >>>> +#include <asm/arch/clk.h> >>>> +#include <asm/arch/cpu.h> >>>> +#include <asm/arch/exynos_dwmmc.h> >>>> +#include <asm/arch/pinmux.h> >>>> + >>>> +/* support 4 mmc hosts */ >>>> +enum { >>>> + MAX_MMC_HOSTS = 4 >>>> +}; >>>> + >>>> +static struct mmc dw_mci_dev[MAX_MMC_HOSTS]; >>>> +static struct dw_mci_host dw_mci_host[MAX_MMC_HOSTS]; >>>> +static int num_devs; >>>> + >>>> +/** >>>> + * Set bits of DWMMC host control register. >>>> + * >>>> + * @param host DWMMC host >>>> + * @param bits bits to be set >>>> + * @return 0 on success, TIMEOUT on failure >>>> + */ >>>> +static int dw_mci_setbits(struct dw_mci_host *host, unsigned int bits) >>>> +{ >>>> + ulong start; >>>> + >>>> + setbits_le32(host->ioaddr + DWMCI_CONTROL, bits); >>>> + >>>> + start = get_timer(0); >>>> + while (dw_mci_readl(host, DWMCI_CONTROL) & bits) { >>>> + if (get_timer(start) > TIMEOUT_MS) { >>>> + debug("Set bits failed\n"); >>>> + return TIMEOUT; >>>> + } >>>> + } >>>> + return 0; >>>> +} >>>> + >>>> +/** >>>> + * Reset DWMMC host control register. >>>> + * >>>> + * @param host DWMMC host >>>> + * @return 0 on success, TIMEOUT on failure >>>> + */ >>>> +static int dw_mci_reset_all(struct dw_mci_host *host) >>>> +{ >>>> + ulong start; >>>> + >>>> + /* >>>> + * Before we reset ciu check the DATA0 line. If it is low and >>>> + * we resets the ciu then we might see some errors. >>>> + */ >>>> + start = get_timer(0); >>>> + while (dw_mci_readl(host, DWMCI_STATUS) & DATA_BUSY) { >>>> + if (get_timer(start) > TIMEOUT_MS) { >>>> + debug("Controller did not release" >>>> + "data0 before ciu reset\n"); >>>> + return TIMEOUT; >>>> + } >>>> + } >>>> + return dw_mci_setbits(host, CTRL_RESET | FIFO_RESET | DMA_RESET); >>>> +} >>>> + >>>> +static void dw_mci_set_mdma_desc(u8 *desc_vir, u8 *desc_phy, >>>> + unsigned int des0, unsigned int des1, unsigned int des2) >>>> +{ >>>> + struct dw_mci_idmac *desc = (struct dw_mci_idmac *)desc_vir; >>>> + >>>> + desc->des0 = des0; >>>> + desc->des1 = des1; >>>> + desc->des2 = des2; >>>> + desc->des3 = (unsigned int)desc_phy + sizeof(struct dw_mci_idmac); >>>> +} >>>> + >>>> +static int dw_mci_prepare_data(struct dw_mci_host *host, struct mmc_data >>>> *data) >>>> +{ >>>> + unsigned int i, data_cnt, des_flag, blksz; >>>> + int err; >>>> + ulong data_start, data_end; >>>> + static struct dw_mci_idmac idmac_desc[0x10000]; >>>> + struct dw_mci_idmac *pdesc_dmac; >>>> + >>>> + err = dw_mci_setbits(host, FIFO_RESET); >>>> + if (err) { >>>> + debug("Fail to reset FIFO\n"); >>>> + return err; >>>> + } >>>> + >>>> + pdesc_dmac = idmac_desc; >>>> + blksz = data->blocksize; >>>> + data_cnt = data->blocks; >>>> + >>>> + for (i = 0;; i++) { >>>> + des_flag = (DWMCI_IDMAC_OWN | DWMCI_IDMAC_CH); >>>> + des_flag |= (i == 0) ? DWMCI_IDMAC_FS : 0; >>>> + if (data_cnt <= 8) { >>>> + des_flag |= DWMCI_IDMAC_LD; >>>> + dw_mci_set_mdma_desc((u8 *)pdesc_dmac, >>>> + (u8 *)virt_to_phys(pdesc_dmac), >>>> + des_flag, blksz * data_cnt, >>>> + (unsigned int)(virt_to_phys(data->dest)) + >>>> + (unsigned int)(i * 0x1000)); >>>> + break; >>>> + } >>>> + /* max transfer size is 4KB per descriptor */ >>>> + dw_mci_set_mdma_desc((u8 *)pdesc_dmac, >>>> + (u8 *)virt_to_phys(pdesc_dmac), >>>> + des_flag, blksz * 8, >>>> + virt_to_phys(data->dest) + >>>> + (unsigned int)(i * 0x1000)); >>>> + >>>> + data_cnt -= 8; >>>> + pdesc_dmac++; >>>> + } >>>> + >>>> + data_start = (ulong)idmac_desc; >>>> + data_end = (ulong)pdesc_dmac; >>>> + flush_dcache_range(data_start, data_end + ARCH_DMA_MINALIGN); >>>> + >>>> + data_start = (ulong)data->dest; >>>> + data_end = (ulong)(data->dest + data->blocks * data->blocksize); >>>> + flush_dcache_range(data_start, data_end); >>>> + >>>> + dw_mci_writel(host, (unsigned int)virt_to_phys(idmac_desc), >>>> + DWMCI_DBADDR); >>>> + >>>> + /* enable the Internal DMA Controller */ >>>> + setbits_le32(host->ioaddr + DWMCI_CONTROL, ENABLE_IDMAC | >>>> + DMA_ENABLE); >>>> + setbits_le32(host->ioaddr + DWMCI_BMOD, BMOD_IDMAC_ENABLE | >>>> + BMOD_IDMAC_FB); >>>> + >>>> + dw_mci_writel(host, data->blocksize, DWMCI_BLKSIZE); >>>> + dw_mci_writel(host, data->blocksize * data->blocks, DWMCI_BYTCNT); >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +static int dw_mci_set_transfer_mode(struct dw_mci_host *host, >>>> + struct mmc_data *data) >>>> +{ >>>> + int mode = CMD_DATA_EXP_BIT; >>>> + >>>> + if (data->blocks > 1) >>>> + mode |= CMD_SENT_AUTO_STOP_BIT; >>>> + if (data->flags & MMC_DATA_WRITE) >>>> + mode |= CMD_RW_BIT; >>>> + >>>> + return mode; >>>> +} >>>> + >>>> +/* >>>> + * Sends a command out on the bus. >>>> + * >>>> + * @param mmc mmc device >>>> + * @param cmd mmc_cmd to be sent on bus >>>> + * @param data mmc data to be sent (optional) >>>> + * >>>> + * @return return 0 if ok, else error number >>>> + */ >>>> +static int dw_mci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, >>>> + struct mmc_data *data) >>>> +{ >>>> + struct dw_mci_host *host = mmc->priv; >>>> + >>>> + int flags = 0, i, err; >>>> + unsigned int mask; >>>> + ulong start, data_start, data_end; >>>> + >>>> + /* >>>> + * We shouldn't wait for data inihibit for stop commands, even >>>> + * though they might use busy signaling >>>> + */ >>>> + start = get_timer(0); >>>> + while (dw_mci_readl(host, DWMCI_STATUS) & DATA_BUSY) { >>>> + if (get_timer(start) > COMMAND_TIMEOUT) { >>>> + debug("timeout on data busy\n"); >>>> + return TIMEOUT; >>>> + } >>>> + } >>>> + >>>> + if (dw_mci_readl(host, DWMCI_RINTSTS)) { >>>> + if ((dw_mci_readl(host, DWMCI_RINTSTS) & >>>> + (INTMSK_CDONE | INTMSK_ACD)) == 0) >>>> + debug("there are pending interrupts 0x%x\n", >>>> + dw_mci_readl(host, DWMCI_RINTSTS)); >>>> + } >>>> + /* It clears all pending interrupts before sending a command*/ >>>> + dw_mci_writel(host, INTMSK_ALL, DWMCI_RINTSTS); >>>> + >>>> + if (data) { >>>> + err = dw_mci_prepare_data(host, data); >>>> + if (err) { >>>> + debug("fail to prepare data\n"); >>>> + return err; >>>> + } >>>> + } >>>> + >>>> + dw_mci_writel(host, cmd->cmdarg, DWMCI_CMDARG); >>>> + >>>> + if (data) >>>> + flags = dw_mci_set_transfer_mode(host, data); >>>> + >>>> + if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & >>>> MMC_RSP_BUSY)) >>>> + /* this is out of SD spec */ >>>> + return -1; >>>> + >>>> + if (cmd->resp_type & MMC_RSP_PRESENT) { >>>> + flags |= CMD_RESP_EXP_BIT; >>>> + if (cmd->resp_type & MMC_RSP_136) >>>> + flags |= CMD_RESP_LENGTH_BIT; >>>> + } >>>> + >>>> + if (cmd->resp_type & MMC_RSP_CRC) >>>> + flags |= CMD_CHECK_CRC_BIT; >>>> + flags |= (cmd->cmdidx | CMD_STRT_BIT | CMD_USE_HOLD_REG | >>>> + CMD_WAIT_PRV_DAT_BIT); >>>> + >>>> + mask = dw_mci_readl(host, DWMCI_CMD); >>>> + if (mask & CMD_STRT_BIT) >>>> + debug("cmd busy, current cmd: %d", cmd->cmdidx); >>>> + >>>> + dw_mci_writel(host, flags, DWMCI_CMD); >>>> + /* wait for command complete by busy waiting. */ >>>> + for (i = 0; i < COMMAND_TIMEOUT; i++) { >>>> + mask = dw_mci_readl(host, DWMCI_RINTSTS); >>>> + if (mask & INTMSK_CDONE) { >>>> + if (!data) >>>> + dw_mci_writel(host, mask, DWMCI_RINTSTS); >>>> + break; >>>> + } >>>> + } >>>> + /* timeout for command complete. */ >>>> + if (COMMAND_TIMEOUT == i) { >>>> + debug("timeout waiting for status update\n"); >>>> + return TIMEOUT; >>>> + } >>>> + >>>> + if (mask & INTMSK_RTO) { >>>> + if (((cmd->cmdidx == MMC_CMD_SEND_EXT_CSD || >>>> + cmd->cmdidx == MMC_CMD_APP_CMD)) == 0) { >>>> + debug("response timeout error: 0x%x cmd: %d\n", >>>> + mask, cmd->cmdidx); >>>> + } >>>> + return TIMEOUT; >>>> + } else if (mask & INTMSK_RE) { >>>> + debug("response error: 0x%x 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. >>>> */ >>>> + cmd->response[0] = dw_mci_readl(host, >>>> + >>>> DWMCI_RESP3); >>>> + cmd->response[1] = dw_mci_readl(host, >>>> + >>>> DWMCI_RESP2); >>>> + cmd->response[2] = dw_mci_readl(host, >>>> + >>>> DWMCI_RESP1); >>>> + cmd->response[3] = dw_mci_readl(host, >>>> + >>>> DWMCI_RESP0); >>>> + } else { >>>> + cmd->response[0] = dw_mci_readl(host, DWMCI_RESP0); >>>> + debug("\tcmd->response[0]: 0x%08x\n", >>>> cmd->response[0]); >>>> + } >>>> + } >>>> + >>>> + if (data) { >>>> + while (!(mask & (DATA_ERR | DATA_TOUT | INTMSK_DTO))) >>>> + mask = dw_mci_readl(host, DWMCI_RINTSTS); >>>> + dw_mci_writel(host, mask, DWMCI_RINTSTS); >>>> + if (data->flags & MMC_DATA_READ) { >>>> + data_start = (ulong)data->dest; >>>> + data_end = (ulong)data->dest + >>>> + data->blocks * data->blocksize; >>>> + invalidate_dcache_range(data_start, data_end); >>>> + } >>>> + if (mask & (DATA_ERR | DATA_TOUT)) { >>>> + debug("error during transfer: 0x%x\n", mask); >>>> + /* make sure disable IDMAC and IDMAC_Interrupts */ >>>> + dw_mci_writel(host, (dw_mci_readl(host, >>>> DWMCI_CONTROL) & >>>> + ~(DMA_ENABLE | ENABLE_IDMAC)), DWMCI_CONTROL); >>>> + /* mask all interrupt source of IDMAC */ >>>> + dw_mci_writel(host, 0, DWMCI_IDINTEN); >>>> + return -1; >>>> + } else if (mask & INTMSK_DTO) { >>>> + debug("dwmmc dma interrupt end\n"); >>>> + } else { >>>> + debug("unexpected condition 0x%x\n", mask); >>>> + } >>>> + /* make sure disable IDMAC and IDMAC_Interrupts */ >>>> + dw_mci_writel(host, (dw_mci_readl(host, DWMCI_CONTROL) & >>>> + ~(DMA_ENABLE | ENABLE_IDMAC)), >>>> + DWMCI_CONTROL); >>>> + /* mask all interrupt source of IDMAC */ >>>> + dw_mci_writel(host, 0, DWMCI_IDINTEN); >>>> + } >>>> + >>>> + udelay(100); >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +/* >>>> + * ON/OFF host controller clock >>>> + * >>>> + * @param host pointer to dw_mci_host >>>> + * @param val to enable/disable clock >>>> + */ >>>> +static void dw_mci_clock_onoff(struct dw_mci_host *host, int val) >>>> +{ >>>> + >>>> + if (val) >>>> + dw_mci_writel(host, CLK_ENABLE, DWMCI_CLKENA); >>>> + else >>>> + dw_mci_writel(host, CLK_DISABLE, DWMCI_CLKENA); >>>> + >>>> + dw_mci_writel(host, 0, DWMCI_CMD); >>>> + dw_mci_writel(host, CMD_ONLY_CLK, DWMCI_CMD); >>>> +} >>>> + >>>> +/* >>>> + * change host controller clock >>>> + * >>>> + * @param host pointer to dw_mci_host >>>> + * @param clock request clock >>>> + */ >>>> +static void dw_mci_change_clock(struct dw_mci_host *host, uint clock) >>>> +{ >>>> + int div; >>>> + u32 sclk_mshc; >>>> + >>>> + if (clock == host->clock) >>>> + return; >>>> + >>>> + /* If Input clock is higher than maximum mshc clock */ >>>> + if (clock > MAX_DWMMC_CLOCK) { >>>> + debug("Input clock is too high\n"); >>>> + clock = MAX_DWMMC_CLOCK; >>>> + } >>>> + >>>> + /* disable the clock before changing it */ >>>> + dw_mci_clock_onoff(host, CLK_DISABLE); >>>> + >>>> + /* get the clock division */ >>>> + if (host->peripheral == PERIPH_ID_SDMMC4) >>>> + sclk_mshc = get_dw_mci_clk_div(host->peripheral) / 2; >>>> + else >>>> + sclk_mshc = get_dw_mci_clk_div(host->peripheral) / 4; >>>> + >>>> + /* CLKDIV */ >>>> + for (div = 1 ; div <= MAXCLKDIV; div++) { >>>> + if ((sclk_mshc / (2 * div)) <= clock) { >>>> + dw_mci_writel(host, div, DWMCI_CLKDIV); >>>> + break; >>>> + } >>>> + } >>>> + >>>> + dw_mci_writel(host, 0, DWMCI_CMD); >>>> + dw_mci_writel(host, CMD_ONLY_CLK, DWMCI_CMD); >>>> + >>>> + dw_mci_writel(host, dw_mci_readl(host, DWMCI_CMD) & >>>> + (~CMD_SEND_CLK_ONLY), >>>> + DWMCI_CMD); >>>> + >>>> + dw_mci_clock_onoff(host, CLK_ENABLE); >>>> + host->clock = clock; >>>> +} >>>> + >>>> +/* >>>> + * Set ios for host controller clock >>>> + * >>>> + * This sets the card bus width and clksel >>>> + */ >>>> +static void dw_mci_set_ios(struct mmc *mmc) >>>> +{ >>>> + struct dw_mci_host *host = mmc->priv; >>>> + int val; >>>> + >>>> + debug("bus_width: %x, clock: %d\n", mmc->bus_width, mmc->clock); >>>> + >>>> + if (mmc->clock > 0) >>>> + dw_mci_change_clock(host, mmc->clock); >>>> + >>>> + if (mmc->bus_width == 8) >>>> + dw_mci_writel(host, PORT0_CARD_WIDTH8, DWMCI_CTYPE); >>>> + else if (mmc->bus_width == 4) >>>> + dw_mci_writel(host, PORT0_CARD_WIDTH4, DWMCI_CTYPE); >>>> + else >>>> + dw_mci_writel(host, PORT0_CARD_WIDTH1, DWMCI_CTYPE); >>>> + >>>> + val = dw_mci_readl(host, DWMCI_CLKSEL); >>>> + if (host->peripheral == PERIPH_ID_SDMMC0) >>>> + val |= (SELCLK_SAMPLE_1PHASE_Shift | >>>> SELCLK_DRV_3PHASE_SHIFT | >>>> + SELCLK_DIV_RATIO); >>>> + if (host->peripheral == PERIPH_ID_SDMMC2) >>>> + val |= (SELCLK_SAMPLE_1PHASE_Shift | >>>> SELCLK_DRV_2PHASE_SHIFT | >>>> + SELCLK_DIV_RATIO); >>>> + if (host->peripheral == PERIPH_ID_SDMMC4) >>>> + val |= (SELCLK_SAMPLE_1PHASE_Shift | >>>> SELCLK_DRV_2PHASE_SHIFT); >>>> + >>>> + dw_mci_writel(host, val, DWMCI_CLKSEL); >>>> +} >>>> + >>>> +/* >>>> + * Fifo init for host controller >>>> + */ >>>> +static void dw_mci_fifo_init(struct dw_mci_host *host) >>>> +{ >>>> + int fifo_val, fifo_depth, fifo_threshold; >>>> + >>>> + fifo_val = dw_mci_readl(host, DWMCI_FIFOTH); >>>> + >>>> + /* Power-on value of RX_WMark is FIFO_DEPTH-1 */ >>>> + fifo_depth = 1 + ((fifo_val >> 16) & 0x7ff); >>>> + fifo_threshold = fifo_depth / 2; >>>> + >>>> + fifo_val &= ~(RX_WMARK | TX_WMARK | MSIZE_MASK); >>>> + fifo_val |= (fifo_threshold | (fifo_threshold << 16) | MSIZE_8); >>>> + dw_mci_writel(host, fifo_val, DWMCI_FIFOTH); >>>> +} >>>> + >>>> + >>>> +static int dw_mci_reset(struct dw_mci_host *host) >>>> +{ >>>> + int err; >>>> + >>>> + /* power on the card */ >>>> + dw_mci_writel(host, POWER_ENABLE, DWMCI_PWREN); >>>> + >>>> + err = dw_mci_reset_all(host); >>>> + if (err) >>>> + return err; >>>> + >>>> + dw_mci_fifo_init(host); >>>> + >>>> + /* clear all pending interrupts */ >>>> + dw_mci_writel(host, INTMSK_ALL, DWMCI_RINTSTS); >>>> + >>>> + /* interrupts are not used, disable all */ >>>> + dw_mci_writel(host, 0, DWMCI_INTMASK); >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +static int dw_mci_initialize(struct mmc *mmc) >>>> +{ >>>> + struct dw_mci_host *host = (struct dw_mci_host *)mmc->priv; >>>> + unsigned int ier; >>>> + int err; >>>> + >>>> + err = dw_mci_reset(host); >>>> + if (err) >>>> + return err; >>>> + >>>> + /* enumerate at 400KHz */ >>>> + dw_mci_change_clock(host, MIN_DWMMC_CLOCK); >>>> + >>>> + /* set auto stop command */ >>>> + ier = dw_mci_readl(host, DWMCI_CONTROL); >>>> + ier |= SEND_AS_CCSD; >>>> + dw_mci_writel(host, ier, DWMCI_CONTROL); >>>> + >>>> + /* set 1bit card mode */ >>>> + dw_mci_writel(host, PORT0_CARD_WIDTH1, DWMCI_CTYPE); >>>> + >>>> + dw_mci_writel(host, 0xfffff, DWMCI_DEBENCE); >>>> + >>>> + /* set bus mode register for IDMAC */ >>>> + dw_mci_writel(host, BMOD_IDMAC_RESET, DWMCI_BMOD); >>>> + >>>> + dw_mci_writel(host, 0x0, DWMCI_IDINTEN); >>>> + >>>> + /* set the max timeout for data and response */ >>>> + dw_mci_writel(host, TMOUT_MAX, DWMCI_TMOUT); >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +int dw_mci_init(enum periph_id periph_id, int bus_width) >>>> +{ >>>> + struct dw_mci_host *mmc_host; >>>> + struct mmc *mmc; >>>> + >>>> + if (num_devs == MAX_MMC_HOSTS) { >>>> + debug("%s: Too many hosts\n", __func__); >>>> + return -1; >>>> + } >>>> + >>>> + /* set the clock for dwmmc controller */ >>>> + if (set_dw_mci_clk_div(periph_id)) { >>>> + debug("clock_set_dw_mci failed\n"); >>>> + return -EINVAL; >>>> + } >>>> + >>>> + mmc = &dw_mci_dev[num_devs]; >>>> + mmc_host = &dw_mci_host[num_devs]; >>>> + >>>> + sprintf(mmc->name, "DWMMC%d", num_devs); >>>> + num_devs++; >>>> + >>>> + mmc->priv = mmc_host; >>>> + mmc->send_cmd = dw_mci_send_command; >>>> + mmc->set_ios = dw_mci_set_ios; >>>> + mmc->init = dw_mci_initialize; >>>> + >>>> + /* >>>> + * In 2.40a spec, Data offset is changed. >>>> + * Need to check the version-id and set data-offset for DATA >>>> register. >>>> + */ >>>> + mmc_host->verid = GET_VERID(dw_mci_readl(mmc_host, DWMCI_VERID)); >>>> + debug("Version ID is %04x\n", mmc_host->verid); >>>> + >>>> + if (mmc_host->verid < DW_MMC_240A) >>>> + mmc_host->data_offset = DATA_OFFSET; >>>> + else >>>> + mmc_host->data_offset = DATA_240A_OFFSET; >>>> + >>>> + mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; >>>> + mmc->host_caps = MMC_MODE_HS_52MHz | MMC_MODE_HS | MMC_MODE_HC; >>>> + >>>> + if (bus_width == 8) >>>> + mmc->host_caps |= MMC_MODE_8BIT; >>>> + else >>>> + mmc->host_caps |= MMC_MODE_4BIT; >>>> + >>>> + mmc->f_min = MIN_DWMMC_CLOCK; >>>> + mmc->f_max = MAX_DWMMC_CLOCK; >>>> + >>>> + exynos_pinmux_config(periph_id, >>>> + bus_width == 8 ? PINMUX_FLAG_8BIT_MODE : 0); >>>> + >>>> + mmc_host->clock = 0; >>>> + mmc_host->peripheral = periph_id; >>>> + mmc_host->ioaddr = (void *)samsung_get_base_dwmmc(); >>>> + mmc->b_max = 1; >>>> + mmc_register(mmc); >>>> + mmc->block_dev.removable = 1; >>>> + debug("dwmmc: periph_id=%d, width=%d, ioaddr=%p\n", >>>> + periph_id, bus_width, mmc_host->ioaddr); >>>> + >>>> + return 0; >>>> +} >>>> -- >>>> 1.7.4.4 >>>> >>>> _______________________________________________ >>>> U-Boot mailing list >>>> U-Boot@lists.denx.de >>>> http://lists.denx.de/mailman/listinfo/u-boot >>> _______________________________________________ >>> U-Boot mailing list >>> U-Boot@lists.denx.de >>> http://lists.denx.de/mailman/listinfo/u-boot >> >> >> > > _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot