[U-Boot] [PATCH V2] MMC: DWMMC: Add DWMMC driver

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; +}

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 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

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
participants (5)
-
Andy Fleming
-
Chander Kashyap
-
Jaehoon Chung
-
Rajeshwari Birje
-
Rajeshwari Shinde