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

Rajeshwari Birje rajeshwari.birje at gmail.com
Fri Jun 15 13:15:09 CEST 2012


Hi Jaehoon Chung,

Thank you for comments.

On Thu, Jun 14, 2012 at 7:06 PM, Jaehoon Chung <jh80.chung at 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 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;
-- 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 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