Hi All, ccing Jaehoon Chung
Regards, Rajeshwari Shinde. On Mon, Jun 11, 2012 at 6:18 PM, Rajeshwari Shinde <[email protected]> wrote: > Add DWMMC driver support and resgister description for same. > > Signed-off-by: Alim Akhtar <[email protected]> > Signed-off-by: Terry Lambert <[email protected]> > Signed-off-by: Rajeshwari Shinde <[email protected]> > --- > 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 <[email protected]> > + * > + * 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 > [email protected] > http://lists.denx.de/mailman/listinfo/u-boot _______________________________________________ U-Boot mailing list [email protected] http://lists.denx.de/mailman/listinfo/u-boot

