Message ID | 1339418906-20590-1-git-send-email-rajeshwari.s@samsung.com |
---|---|
State | New |
Headers | show |
Hi All, ccing Jaehoon Chung Regards, Rajeshwari Shinde. On Mon, Jun 11, 2012 at 6:18 PM, Rajeshwari Shinde <rajeshwari.s@samsung.com> wrote: > Add DWMMC driver support and resgister description for same. > > Signed-off-by: Alim Akhtar <alim.akhtar@samsung.com> > Signed-off-by: Terry Lambert <tlambert@chromium.org> > Signed-off-by: Rajeshwari Shinde <rajeshwari.s@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.kesavan@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; > +}; > + > +/* 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__ */ > +#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
Hi, On 11 June 2012 19:26, Rajeshwari Birje <rajeshwari.birje@gmail.com> wrote: > Hi All, > > ccing Jaehoon Chung > > Regards, > Rajeshwari Shinde. > > > On Mon, Jun 11, 2012 at 6:18 PM, Rajeshwari Shinde > <rajeshwari.s@samsung.com> wrote: >> Add DWMMC driver support and resgister description for same. >> >> Signed-off-by: Alim Akhtar <alim.akhtar@samsung.com> >> Signed-off-by: Terry Lambert <tlambert@chromium.org> >> Signed-off-by: Rajeshwari Shinde <rajeshwari.s@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.kesavan@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
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.birje@gmail.com> wrote: >> Hi All, >> >> ccing Jaehoon Chung >> >> Regards, >> Rajeshwari Shinde. >> >> >> On Mon, Jun 11, 2012 at 6:18 PM, Rajeshwari Shinde >> <rajeshwari.s@samsung.com> wrote: >>> Add DWMMC driver support and resgister description for same. >>> >>> Signed-off-by: Alim Akhtar <alim.akhtar@samsung.com> >>> Signed-off-by: Terry Lambert <tlambert@chromium.org> >>> Signed-off-by: Rajeshwari Shinde <rajeshwari.s@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.kesavan@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 > > >
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.chung@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.birje@gmail.com> wrote: >>> Hi All, >>> >>> ccing Jaehoon Chung >>> >>> Regards, >>> Rajeshwari Shinde. >>> >>> >>> On Mon, Jun 11, 2012 at 6:18 PM, Rajeshwari Shinde >>> <rajeshwari.s@samsung.com> wrote: >>>> Add DWMMC driver support and resgister description for same. >>>> >>>> Signed-off-by: Alim Akhtar <alim.akhtar@samsung.com> >>>> Signed-off-by: Terry Lambert <tlambert@chromium.org> >>>> Signed-off-by: Rajeshwari Shinde <rajeshwari.s@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.kesavan@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 >> >> >> > >
Hi Chander, Thank you for comments will do the correction. Regards, Rajeshwari Shinde. On Tue, Jun 12, 2012 at 11:44 AM, Chander Kashyap <chander.kashyap@linaro.org> wrote: > Hi, > > On 11 June 2012 19:26, Rajeshwari Birje <rajeshwari.birje@gmail.com> wrote: >> Hi All, >> >> ccing Jaehoon Chung >> >> Regards, >> Rajeshwari Shinde. >> >> >> On Mon, Jun 11, 2012 at 6:18 PM, Rajeshwari Shinde >> <rajeshwari.s@samsung.com> wrote: >>> Add DWMMC driver support and resgister description for same. >>> >>> Signed-off-by: Alim Akhtar <alim.akhtar@samsung.com> >>> Signed-off-by: Terry Lambert <tlambert@chromium.org> >>> Signed-off-by: Rajeshwari Shinde <rajeshwari.s@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.kesavan@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 > > > > -- > with warm regards, > Chander Kashyap
Hi Rajeshwari, This patch has too many dependence with other patches. (Pinmux and PeripID, patches for MSHCI setting). And as i mentioned, designWare controller isn't exynos specific. I think good that separate two files. (dw_mmc.c and exynos_dw_mmc.c) Like this...dw_mmc.c is generic code and exynos_dw_mmc.c is samsung specific code.. If you want, I will send to you patch that related with them. (based-on your patch) And Added some comment On 06/12/2012 06:33 PM, Rajeshwari Birje wrote: > 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.chung@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.birje@gmail.com> wrote: >>>> Hi All, >>>> >>>> ccing Jaehoon Chung >>>> >>>> Regards, >>>> Rajeshwari Shinde. >>>> >>>> >>>> On Mon, Jun 11, 2012 at 6:18 PM, Rajeshwari Shinde >>>> <rajeshwari.s@samsung.com> wrote: >>>>> Add DWMMC driver support and resgister description for same. >>>>> >>>>> Signed-off-by: Alim Akhtar <alim.akhtar@samsung.com> >>>>> Signed-off-by: Terry Lambert <tlambert@chromium.org> >>>>> Signed-off-by: Rajeshwari Shinde <rajeshwari.s@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.kesavan@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; mmc->priv's type is void type. I think good that use (struct dw_mci_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; >>>>> + } >>>>> + } >>>>> + What do the below condition? just debugging? i didn't understand why need this condition. >>>>> + 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); Also need not this condition. Debugging point? >>>>> + >>>>> + 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); >>>>> + } What do you check with MMC_CMD_SEND_EXT_CSD or MMC_CMD_APP_CMD? Also Debugging? >>>>> + 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); Fix the indent >>>>> + } 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 didn't enable dcache? >>>>> + } >>>>> + 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; Also... Best Regards, Jaehoon Chung >>>>> + 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 >
Hi Jaehoon Chung, Thank you for comments. On Thu, Jun 14, 2012 at 7:06 PM, Jaehoon Chung <jh80.chung@samsung.com> wrote: > Hi Rajeshwari, > > This patch has too many dependence with other patches. > (Pinmux and PeripID, patches for MSHCI setting). > And as i mentioned, designWare controller isn't exynos specific. > > I think good that separate two files. (dw_mmc.c and exynos_dw_mmc.c) > Like this...dw_mmc.c is generic code and exynos_dw_mmc.c is samsung specific code.. > If you want, I will send to you patch that related with them. (based-on your patch) > -- Ok. Will do the change and send the patch for review. > And Added some comment > > On 06/12/2012 06:33 PM, Rajeshwari Birje wrote: > >> 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.chung@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.birje@gmail.com> wrote: >>>>> Hi All, >>>>> >>>>> ccing Jaehoon Chung >>>>> >>>>> Regards, >>>>> Rajeshwari Shinde. >>>>> >>>>> >>>>> On Mon, Jun 11, 2012 at 6:18 PM, Rajeshwari Shinde >>>>> <rajeshwari.s@samsung.com> wrote: >>>>>> Add DWMMC driver support and resgister description for same. >>>>>> >>>>>> Signed-off-by: Alim Akhtar <alim.akhtar@samsung.com> >>>>>> Signed-off-by: Terry Lambert <tlambert@chromium.org> >>>>>> Signed-off-by: Rajeshwari Shinde <rajeshwari.s@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.kesavan@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; > > mmc->priv's type is void type. I think good that use (struct dw_mci_host *)mmc->priv; -- will correct this > >>>>>> + >>>>>> + 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; >>>>>> + } >>>>>> + } >>>>>> + > > > What do the below condition? just debugging? i didn't understand why need this condition. > -- yes it is added for debugging purpose. >>>>>> + 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); > > Also need not this condition. Debugging point? > -- yes it is added for debugging purpose. >>>>>> + >>>>>> + 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); >>>>>> + } > > What do you check with MMC_CMD_SEND_EXT_CSD or MMC_CMD_APP_CMD? Also Debugging? > -- yes it is added for debugging purpose. >>>>>> + 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); > > Fix the indent -- will do so > >>>>>> + } 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 didn't enable dcache? -- if cache not enabled it has a blank inplementation and will not do anything. > >>>>>> + } >>>>>> + 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; > > Also... -- will correct this. > > Best Regards, > Jaehoon Chung > >>>>>> + 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 >> > >
On Thu, Jun 14, 2012 at 6:36 AM, Jaehoon Chung <jh80.chung@samsung.com> wrote: > Hi Rajeshwari, > > This patch has too many dependence with other patches. > (Pinmux and PeripID, patches for MSHCI setting). > And as i mentioned, designWare controller isn't exynos specific. > > I think good that separate two files. (dw_mmc.c and exynos_dw_mmc.c) > Like this...dw_mmc.c is generic code and exynos_dw_mmc.c is samsung specific code.. > If you want, I will send to you patch that related with them. (based-on your patch) > > And Added some comment [...] Everyone, please remove any unnecessary context when you reply to patches (or any email). When I attempt to read your review comments in patchworks, I have to scroll for pages, carefully, to find them. Just quote enough of the patch so everyone can understand what code your comments are about. >>>>>> +static int dw_mci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, >>>>>> + struct mmc_data *data) >>>>>> +{ >>>>>> + struct dw_mci_host *host = mmc->priv; > > mmc->priv's type is void type. I think good that use (struct dw_mci_host *)mmc->priv; Casting a void * is unnecessary, and pollutes the code with redundant information. The purpose of requiring explicit casts is to make sure one doesn't make an unintended implicit cast between two different types. But a void * is intentionally typeless, and the explicit cast provides no extra checking. Andy
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.kesavan@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; +}; + +/* 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__ */ +#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; +}