Hi Jaehoon Chung, Thank you for comments.
On Thu, Jun 14, 2012 at 7:06 PM, Jaehoon Chung <jh80.ch...@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.ch...@samsung.com> >> wrote: >>> Hi Rajeshwari, >>> >>> Before applied this patch, it must apply your patch for PINMUX. right? >>> >>> Best Regards, >>> Jaehoon Chung >>> >>> On 06/12/2012 03:14 PM, Chander Kashyap wrote: >>> >>>> Hi, >>>> >>>> On 11 June 2012 19:26, Rajeshwari Birje <rajeshwari.bi...@gmail.com> wrote: >>>>> Hi All, >>>>> >>>>> ccing Jaehoon Chung >>>>> >>>>> Regards, >>>>> Rajeshwari Shinde. >>>>> >>>>> >>>>> On Mon, Jun 11, 2012 at 6:18 PM, Rajeshwari Shinde >>>>> <rajeshwar...@samsung.com> wrote: >>>>>> Add DWMMC driver support and resgister description for same. >>>>>> >>>>>> Signed-off-by: Alim Akhtar <alim.akh...@samsung.com> >>>>>> Signed-off-by: Terry Lambert <tlamb...@chromium.org> >>>>>> Signed-off-by: Rajeshwari Shinde <rajeshwar...@samsung.com> >>>>>> --- >>>>>> Changes in V2: >>>>>> - Incorporated comments from Jaehung Chung. >>>>>> - Renamed MSHCI to DWMMC through out the driver. >>>>>> - Renamed files to exynos_dwmmc from exynos_mshc. >>>>>> - Removed major hard codings of values. >>>>>> - Wrote dw_mci_writel and dw_mci_readl functions for writel and >>>>>> readl. >>>>>> - Removed structure of registers and defined each one separately. >>>>>> orch/arm/include/asm/arch-exynos/exynos_dwmmc.h | 229 +++++++++ >>>>>> drivers/mmc/Makefile | 1 + >>>>>> drivers/mmc/exynos_dwmmc.c | 566 >>>>>> +++++++++++++++++++++++ >>>>>> 3 files changed, 796 insertions(+), 0 deletions(-) >>>>>> create mode 100644 arch/arm/include/asm/arch-exynos/exynos_dwmmc.h >>>>>> create mode 100644 drivers/mmc/exynos_dwmmc.c >>>>>> >>>>>> diff --git a/arch/arm/include/asm/arch-exynos/exynos_dwmmc.h >>>>>> b/arch/arm/include/asm/arch-exynos/exynos_dwmmc.h >>>>>> new file mode 100644 >>>>>> index 0000000..349bd75 >>>>>> --- /dev/null >>>>>> +++ b/arch/arm/include/asm/arch-exynos/exynos_dwmmc.h >>>>>> @@ -0,0 +1,229 @@ >>>>>> +/* >>>>>> + * (C) Copyright 2012 SAMSUNG Electronics >>>>>> + * Abhilash Kesavan <a.kesa...@samsung.com> >>>>>> + * >>>>>> + * This program is free software; you can redistribute it and/or modify >>>>>> + * it under the terms of the GNU General Public License as published by >>>>>> + * the Free Software Foundation; either version 2 of the License, or >>>>>> + * (at your option) any later version. >>>>>> + * >>>>>> + * This program is distributed in the hope that it will be useful, >>>>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >>>>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >>>>>> + * GNU General Public License for more details. >>>>>> + * >>>>>> + * You should have received a copy of the GNU General Public License >>>>>> + * along with this program; if not, write to the Free Software >>>>>> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 >>>>>> USA >>>>>> + * >>>>>> + */ >>>>>> +#ifndef __ASM_ARCH_COMMON_DWMMC_H >>>>>> +#define __ASM_ARCH_COMMON_DWMMC_H >>>>>> + >>>>>> +#include <asm/arch/pinmux.h> >>>>>> + >>>>>> +#ifndef __ASSEMBLY__ >>>>>> +struct dw_mci_host { >>>>>> + void *ioaddr; >>>>>> + unsigned int clock; /* Current clock in MHz */ >>>>>> + enum periph_id peripheral; >>>>>> + unsigned int verid; /* SDHCI spec. version */ >>>>>> + unsigned int data_offset; /* DATA offset */ >>>>>> +}; >>>>>> + >>>>>> +/* >>>>>> + * Struct idma >>>>>> + * Holds the descriptor list >>>>>> + */ >>>>>> +struct dw_mci_idmac { >>>>>> + u32 des0; >>>>>> + u32 des1; >>>>>> + u32 des2; >>>>>> + u32 des3; >>>>>> +}; >>>>>> + >>>> #endif >>>>>> +/* Control Register Register */ >>>>>> +#define DWMCI_CONTROL 0x00 >>>>>> +#define CTRL_RESET (0x1 << 0) >>>>>> +#define FIFO_RESET (0x1 << 1) >>>>>> +#define DMA_RESET (0x1 << 2) >>>>>> +#define DMA_ENABLE (0x1 << 5) >>>>>> +#define SEND_AS_CCSD (0x1 << 10) >>>>>> +#define ENABLE_IDMAC (0x1 << 25) >>>>>> + >>>>>> +/* Power Enable Register */ >>>>>> +#define DWMCI_PWREN 0x04 >>>>>> +#define POWER_ENABLE (0x1 << 0) >>>>>> + >>>>>> +#define DWMCI_CLKDIV 0x08 >>>>>> +#define DWMCI_CLKSRC 0x0c >>>>>> + >>>>>> +/* Clock Enable Register */ >>>>>> +#define DWMCI_CLKENA 0x10 >>>>>> +#define CLK_ENABLE (0x1 << 0) >>>>>> +#define CLK_DISABLE (0x0 << 0) >>>>>> + >>>>>> +/* Timeout Register */ >>>>>> +#define DWMCI_TMOUT 0x14 >>>>>> +#define TMOUT_MAX 0xffffffff >>>>>> + >>>>>> +/* Card Type Register */ >>>>>> +#define DWMCI_CTYPE 0x18 >>>>>> +#define PORT0_CARD_WIDTH1 0 >>>>>> +#define PORT0_CARD_WIDTH4 (0x1 << 0) >>>>>> +#define PORT0_CARD_WIDTH8 (0x1 << 16) >>>>>> + >>>>>> +#define DWMCI_BLKSIZE 0x1c >>>>>> +#define DWMCI_BYTCNT 0x20 >>>>>> + >>>>>> +/* Interrupt Mask Register */ >>>>>> +#define DWMCI_INTMASK 0x24 >>>>>> +#define INTMSK_ALL 0xffffffff >>>>>> +#define INTMSK_RE (0x1 << 1) >>>>>> +#define INTMSK_CDONE (0x1 << 2) >>>>>> +#define INTMSK_DTO (0x1 << 3) >>>>>> +#define INTMSK_DCRC (0x1 << 7) >>>>>> +#define INTMSK_RTO (0x1 << 8) >>>>>> +#define INTMSK_DRTO (0x1 << 9) >>>>>> +#define INTMSK_HTO (0x1 << 10) >>>>>> +#define INTMSK_FRUN (0x1 << 11) >>>>>> +#define INTMSK_HLE (0x1 << 12) >>>>>> +#define INTMSK_SBE (0x1 << 13) >>>>>> +#define INTMSK_ACD (0x1 << 14) >>>>>> +#define INTMSK_EBE (0x1 << 15) >>>>>> + >>>>>> +#define DWMCI_CMDARG 0x28 >>>>>> + >>>>>> +/* Command Register */ >>>>>> +#define DWMCI_CMD 0x2c >>>>>> +#define CMD_RESP_EXP_BIT (0x1 << 6) >>>>>> +#define CMD_RESP_LENGTH_BIT (0x1 << 7) >>>>>> +#define CMD_CHECK_CRC_BIT (0x1 << 8) >>>>>> +#define CMD_DATA_EXP_BIT (0x1 << 9) >>>>>> +#define CMD_RW_BIT (0x1 << 10) >>>>>> +#define CMD_SENT_AUTO_STOP_BIT (0x1 << 12) >>>>>> +#define CMD_WAIT_PRV_DAT_BIT (0x1 << 13) >>>>>> +#define CMD_SEND_CLK_ONLY (0x1 << 21) >>>>>> +#define CMD_USE_HOLD_REG (0x1 << 29) >>>>>> +#define CMD_STRT_BIT (0x1 << 31) >>>>>> +#define CMD_ONLY_CLK (CMD_STRT_BIT | CMD_SEND_CLK_ONLY | \ >>>>>> + CMD_WAIT_PRV_DAT_BIT) >>>>>> + >>>>>> +#define DWMCI_RESP0 0x30 >>>>>> +#define DWMCI_RESP1 0x34 >>>>>> +#define DWMCI_RESP2 0x38 >>>>>> +#define DWMCI_RESP3 0x3c >>>>>> + >>>>>> +#define DWMCI_MINTSTS 0x40 >>>>>> + >>>>>> +/* Raw Interrupt Register */ >>>>>> +#define DWMCI_RINTSTS 0x44 >>>>>> +#define DATA_ERR (INTMSK_EBE | INTMSK_SBE | INTMSK_HLE | \ >>>>>> + INTMSK_FRUN | INTMSK_EBE | INTMSK_DCRC) >>>>>> +#define DATA_TOUT (INTMSK_HTO | INTMSK_DRTO) >>>>>> + >>>>>> +/* Status Register */ >>>>>> +#define DWMCI_STATUS 0x48 >>>>>> +#define DATA_BUSY (0x1 << 9) >>>>>> + >>>>>> +/* FIFO Threshold Watermark Register */ >>>>>> +#define DWMCI_FIFOTH 0x4c >>>>>> +#define TX_WMARK (0xFFF << 0) >>>>>> +#define RX_WMARK (0xFFF << 16) >>>>>> +#define MSIZE_MASK (0x7 << 28) >>>>>> + >>>>>> +#define DWMCI_CDETECT 0x50 >>>>>> +#define DWMCI_WRTORT 0x54 >>>>>> +#define DWMCI_GPIO 0x58 >>>>>> +#define DWMCI_TCBCNT 0x5c >>>>>> +#define DWMCI_TBBCNT 0x60 >>>>>> +#define DWMCI_DEBENCE 0x64 >>>>>> +#define DWMCI_USRID 0x68 >>>>>> +#define DWMCI_VERID 0x6c >>>>>> +#define DWMCI_HCON 0x70 >>>>>> +#define DWMCI_UHS_REG 0x74 >>>>>> +#define DWMCI_RST_n 0x78 >>>>>> + >>>>>> +/* DW DMA Mutiple Transaction Size */ >>>>>> +#define MSIZE_8 (2 << 28) >>>>>> + >>>>>> +/* Bus Mode Register */ >>>>>> +#define DWMCI_BMOD 0x80 >>>>>> +#define BMOD_IDMAC_RESET (0x1 << 0) >>>>>> +#define BMOD_IDMAC_FB (0x1 << 1) >>>>>> +#define BMOD_IDMAC_ENABLE (0x1 << 7) >>>>>> + >>>>>> +#define DWMCI_PLDMND 0x84 >>>>>> +#define DWMCI_DBADDR 0x88 >>>>>> + >>>>>> +/* IDMAC bits */ >>>>>> +#define DWMCI_IDSTS 0x8c >>>>>> +#define DWMCI_IDMAC_OWN (0x1 << 31) >>>>>> +#define DWMCI_IDMAC_CH (0x1 << 4) >>>>>> +#define DWMCI_IDMAC_FS (0x1 << 3) >>>>>> +#define DWMCI_IDMAC_LD (0x1 << 2) >>>>>> + >>>>>> +#define DWMCI_IDINTEN 0x90 >>>>>> +#define DWMCI_DSCADDR 0x94 >>>>>> +#define DWMCI_BUFADDR 0x98 >>>>>> + >>>>>> +/* CLKSEL bits*/ >>>>>> +#define DWMCI_CLKSEL 0x9c >>>>>> +#define SELCLK_SAMPLE_1PHASE_Shift (0x1 << 0) >>>>>> +#define SELCLK_DRV_3PHASE_SHIFT (0x3 << 16) >>>>>> +#define SELCLK_DRV_2PHASE_SHIFT (0x2 << 16) >>>>>> +#define SELCLK_DIV_RATIO (0x3 << 24) >>>>>> + >>>>>> +#define DWMCI_CARDTHRCTL 0x100 >>>>>> + >>>>>> +/* >>>>>> + * Data offset is difference according to Version >>>>>> + * Lower than 2.40a : data register offest is 0x100 >>>>>> + */ >>>>>> +#define DW_MMC_240A 0x240a >>>>>> +#define DATA_OFFSET 0x100 >>>>>> +#define DATA_240A_OFFSET 0x200 >>>>>> + >>>>>> +#define MAX_DWMMC_CLOCK 52000000 /* Max limit is 52MHz */ >>>>>> +#define MIN_DWMMC_CLOCK 400000 /* Lower limit is 400KHz >>>>>> */ >>>>>> +#define COMMAND_TIMEOUT 10000 >>>>>> +#define TIMEOUT_MS 100 >>>>>> +#define MAXCLKDIV 0xff >>>>>> + >>>>>> +/* Version ID register define */ >>>>>> +#define GET_VERID(x) ((x) & 0xFFFF) >>>>>> + >>>>>> +static inline void dw_mci_writel(struct dw_mci_host *host, u32 val, int >>>>>> reg) >>>>>> +{ >>>>>> + writel(val, host->ioaddr + reg); >>>>>> +} >>>>>> + >>>>>> +static inline void dw_mci_writew(struct dw_mci_host *host, u16 val, int >>>>>> reg) >>>>>> +{ >>>>>> + writew(val, host->ioaddr + reg); >>>>>> +} >>>>>> + >>>>>> +static inline void dw_mci_writeb(struct dw_mci_host *host, u8 val, int >>>>>> reg) >>>>>> +{ >>>>>> + writeb(val, host->ioaddr + reg); >>>>>> +} >>>>>> + >>>>>> +static inline u32 dw_mci_readl(struct dw_mci_host *host, int reg) >>>>>> +{ >>>>>> + return readl(host->ioaddr + reg); >>>>>> +} >>>>>> + >>>>>> +static inline u16 dw_mci_readw(struct dw_mci_host *host, int reg) >>>>>> +{ >>>>>> + return readw(host->ioaddr + reg); >>>>>> +} >>>>>> + >>>>>> +static inline u8 dw_mci_readb(struct dw_mci_host *host, int reg) >>>>>> +{ >>>>>> + return readb(host->ioaddr + reg); >>>>>> +} >>>>>> + >>>>>> +int dw_mci_init(enum periph_id periph_id, int bus_width); >>>>>> + >>>>>> +#endif /* __ASSEMBLY__ */ >>>> remove at this place after structure declaration. >>>>>> +#endif /* __ASM_ARCH_COMMON_DWMMC_H */ >>>>>> diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile >>>>>> index c245352..cf0be05 100644 >>>>>> --- a/drivers/mmc/Makefile >>>>>> +++ b/drivers/mmc/Makefile >>>>>> @@ -27,6 +27,7 @@ LIB := $(obj)libmmc.o >>>>>> >>>>>> COBJS-$(CONFIG_BFIN_SDH) += bfin_sdh.o >>>>>> COBJS-$(CONFIG_DAVINCI_MMC) += davinci_mmc.o >>>>>> +COBJS-$(CONFIG_EXYNOS_DWMMC) += exynos_dwmmc.o >>>>>> COBJS-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o >>>>>> COBJS-$(CONFIG_FTSDC010) += ftsdc010_esdhc.o >>>>>> COBJS-$(CONFIG_GENERIC_MMC) += mmc.o >>>>>> diff --git a/drivers/mmc/exynos_dwmmc.c b/drivers/mmc/exynos_dwmmc.c >>>>>> new file mode 100644 >>>>>> index 0000000..96f6ceb >>>>>> --- /dev/null >>>>>> +++ b/drivers/mmc/exynos_dwmmc.c >>>>>> @@ -0,0 +1,566 @@ >>>>>> +/* >>>>>> + * (C) Copyright 2012 Samsung Electronics Co. Ltd >>>>>> + * >>>>>> + * See file CREDITS for list of people who contributed to this >>>>>> + * project. >>>>>> + * >>>>>> + * This program is free software; you can redistribute it and/or >>>>>> + * modify it under the terms of the GNU General Public License as >>>>>> + * published by the Free Software Foundation; either version 2 of >>>>>> + * the License, or (at your option) any later version. >>>>>> + * >>>>>> + * This program is distributed in the hope that it will be useful, >>>>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >>>>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >>>>>> + * GNU General Public License for more details. >>>>>> + * >>>>>> + * You should have received a copy of the GNU General Public License >>>>>> + * along with this program; if not, write to the Free Software >>>>>> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, >>>>>> + * MA 02111-1307 USA >>>>>> + */ >>>>>> + >>>>>> +#include <common.h> >>>>>> +#include <mmc.h> >>>>>> +#include <asm/errno.h> >>>>>> +#include <asm/arch/clk.h> >>>>>> +#include <asm/arch/cpu.h> >>>>>> +#include <asm/arch/exynos_dwmmc.h> >>>>>> +#include <asm/arch/pinmux.h> >>>>>> + >>>>>> +/* support 4 mmc hosts */ >>>>>> +enum { >>>>>> + MAX_MMC_HOSTS = 4 >>>>>> +}; >>>>>> + >>>>>> +static struct mmc dw_mci_dev[MAX_MMC_HOSTS]; >>>>>> +static struct dw_mci_host dw_mci_host[MAX_MMC_HOSTS]; >>>>>> +static int num_devs; >>>>>> + >>>>>> +/** >>>>>> + * Set bits of DWMMC host control register. >>>>>> + * >>>>>> + * @param host DWMMC host >>>>>> + * @param bits bits to be set >>>>>> + * @return 0 on success, TIMEOUT on failure >>>>>> + */ >>>>>> +static int dw_mci_setbits(struct dw_mci_host *host, unsigned int bits) >>>>>> +{ >>>>>> + ulong start; >>>>>> + >>>>>> + setbits_le32(host->ioaddr + DWMCI_CONTROL, bits); >>>>>> + >>>>>> + start = get_timer(0); >>>>>> + while (dw_mci_readl(host, DWMCI_CONTROL) & bits) { >>>>>> + if (get_timer(start) > TIMEOUT_MS) { >>>>>> + debug("Set bits failed\n"); >>>>>> + return TIMEOUT; >>>>>> + } >>>>>> + } >>>>>> + return 0; >>>>>> +} >>>>>> + >>>>>> +/** >>>>>> + * Reset DWMMC host control register. >>>>>> + * >>>>>> + * @param host DWMMC host >>>>>> + * @return 0 on success, TIMEOUT on failure >>>>>> + */ >>>>>> +static int dw_mci_reset_all(struct dw_mci_host *host) >>>>>> +{ >>>>>> + ulong start; >>>>>> + >>>>>> + /* >>>>>> + * Before we reset ciu check the DATA0 line. If it is low and >>>>>> + * we resets the ciu then we might see some errors. >>>>>> + */ >>>>>> + start = get_timer(0); >>>>>> + while (dw_mci_readl(host, DWMCI_STATUS) & DATA_BUSY) { >>>>>> + if (get_timer(start) > TIMEOUT_MS) { >>>>>> + debug("Controller did not release" >>>>>> + "data0 before ciu reset\n"); >>>>>> + return TIMEOUT; >>>>>> + } >>>>>> + } >>>>>> + return dw_mci_setbits(host, CTRL_RESET | FIFO_RESET | DMA_RESET); >>>>>> +} >>>>>> + >>>>>> +static void dw_mci_set_mdma_desc(u8 *desc_vir, u8 *desc_phy, >>>>>> + unsigned int des0, unsigned int des1, unsigned int des2) >>>>>> +{ >>>>>> + struct dw_mci_idmac *desc = (struct dw_mci_idmac *)desc_vir; >>>>>> + >>>>>> + desc->des0 = des0; >>>>>> + desc->des1 = des1; >>>>>> + desc->des2 = des2; >>>>>> + desc->des3 = (unsigned int)desc_phy + sizeof(struct >>>>>> dw_mci_idmac); >>>>>> +} >>>>>> + >>>>>> +static int dw_mci_prepare_data(struct dw_mci_host *host, struct >>>>>> mmc_data *data) >>>>>> +{ >>>>>> + unsigned int i, data_cnt, des_flag, blksz; >>>>>> + int err; >>>>>> + ulong data_start, data_end; >>>>>> + static struct dw_mci_idmac idmac_desc[0x10000]; >>>>>> + struct dw_mci_idmac *pdesc_dmac; >>>>>> + >>>>>> + err = dw_mci_setbits(host, FIFO_RESET); >>>>>> + if (err) { >>>>>> + debug("Fail to reset FIFO\n"); >>>>>> + return err; >>>>>> + } >>>>>> + >>>>>> + pdesc_dmac = idmac_desc; >>>>>> + blksz = data->blocksize; >>>>>> + data_cnt = data->blocks; >>>>>> + >>>>>> + for (i = 0;; i++) { >>>>>> + des_flag = (DWMCI_IDMAC_OWN | DWMCI_IDMAC_CH); >>>>>> + des_flag |= (i == 0) ? DWMCI_IDMAC_FS : 0; >>>>>> + if (data_cnt <= 8) { >>>>>> + des_flag |= DWMCI_IDMAC_LD; >>>>>> + dw_mci_set_mdma_desc((u8 *)pdesc_dmac, >>>>>> + (u8 *)virt_to_phys(pdesc_dmac), >>>>>> + des_flag, blksz * data_cnt, >>>>>> + (unsigned int)(virt_to_phys(data->dest)) + >>>>>> + (unsigned int)(i * 0x1000)); >>>>>> + break; >>>>>> + } >>>>>> + /* max transfer size is 4KB per descriptor */ >>>>>> + dw_mci_set_mdma_desc((u8 *)pdesc_dmac, >>>>>> + (u8 *)virt_to_phys(pdesc_dmac), >>>>>> + des_flag, blksz * 8, >>>>>> + virt_to_phys(data->dest) + >>>>>> + (unsigned int)(i * 0x1000)); >>>>>> + >>>>>> + data_cnt -= 8; >>>>>> + pdesc_dmac++; >>>>>> + } >>>>>> + >>>>>> + data_start = (ulong)idmac_desc; >>>>>> + data_end = (ulong)pdesc_dmac; >>>>>> + flush_dcache_range(data_start, data_end + ARCH_DMA_MINALIGN); >>>>>> + >>>>>> + data_start = (ulong)data->dest; >>>>>> + data_end = (ulong)(data->dest + data->blocks * data->blocksize); >>>>>> + flush_dcache_range(data_start, data_end); >>>>>> + >>>>>> + dw_mci_writel(host, (unsigned int)virt_to_phys(idmac_desc), >>>>>> + DWMCI_DBADDR); >>>>>> + >>>>>> + /* enable the Internal DMA Controller */ >>>>>> + setbits_le32(host->ioaddr + DWMCI_CONTROL, ENABLE_IDMAC | >>>>>> + DMA_ENABLE); >>>>>> + setbits_le32(host->ioaddr + DWMCI_BMOD, BMOD_IDMAC_ENABLE | >>>>>> + BMOD_IDMAC_FB); >>>>>> + >>>>>> + dw_mci_writel(host, data->blocksize, DWMCI_BLKSIZE); >>>>>> + dw_mci_writel(host, data->blocksize * data->blocks, >>>>>> DWMCI_BYTCNT); >>>>>> + >>>>>> + return 0; >>>>>> +} >>>>>> + >>>>>> +static int dw_mci_set_transfer_mode(struct dw_mci_host *host, >>>>>> + struct mmc_data *data) >>>>>> +{ >>>>>> + int mode = CMD_DATA_EXP_BIT; >>>>>> + >>>>>> + if (data->blocks > 1) >>>>>> + mode |= CMD_SENT_AUTO_STOP_BIT; >>>>>> + if (data->flags & MMC_DATA_WRITE) >>>>>> + mode |= CMD_RW_BIT; >>>>>> + >>>>>> + return mode; >>>>>> +} >>>>>> + >>>>>> +/* >>>>>> + * Sends a command out on the bus. >>>>>> + * >>>>>> + * @param mmc mmc device >>>>>> + * @param cmd mmc_cmd to be sent on bus >>>>>> + * @param data mmc data to be sent (optional) >>>>>> + * >>>>>> + * @return return 0 if ok, else error number >>>>>> + */ >>>>>> +static int dw_mci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, >>>>>> + struct mmc_data *data) >>>>>> +{ >>>>>> + struct dw_mci_host *host = mmc->priv; > > 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 >> > > _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot