[U-Boot] [PATCH V2] MMC: DWMMC: Add DWMMC driver
Jaehoon Chung
jh80.chung at samsung.com
Thu Jun 14 15:36:05 CEST 2012
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 at 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 at gmail.com> wrote:
>>>> Hi All,
>>>>
>>>> ccing Jaehoon Chung
>>>>
>>>> Regards,
>>>> Rajeshwari Shinde.
>>>>
>>>>
>>>> On Mon, Jun 11, 2012 at 6:18 PM, Rajeshwari Shinde
>>>> <rajeshwari.s at samsung.com> wrote:
>>>>> Add DWMMC driver support and resgister description for same.
>>>>>
>>>>> Signed-off-by: Alim Akhtar <alim.akhtar at samsung.com>
>>>>> Signed-off-by: Terry Lambert <tlambert at chromium.org>
>>>>> Signed-off-by: Rajeshwari Shinde <rajeshwari.s at 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 at 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 at lists.denx.de
>>>>> http://lists.denx.de/mailman/listinfo/u-boot
>>>> _______________________________________________
>>>> U-Boot mailing list
>>>> U-Boot at lists.denx.de
>>>> http://lists.denx.de/mailman/listinfo/u-boot
>>>
>>>
>>>
>>
>>
> _______________________________________________
> U-Boot mailing list
> U-Boot at lists.denx.de
> http://lists.denx.de/mailman/listinfo/u-boot
>
More information about the U-Boot
mailing list