All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jaehoon Chung <jh80.chung@samsung.com>
To: u-boot@lists.denx.de
Subject: [U-Boot] [PATCH V2] MMC: DWMMC: Add DWMMC driver
Date: Tue, 12 Jun 2012 17:37:32 +0900	[thread overview]
Message-ID: <4FD6FFCC.6070801@samsung.com> (raw)
In-Reply-To: <CANuQgHFmA1KO0tP1zD6BazadMtnM6KHpLfQPoQg15wvDv6xDmQ@mail.gmail.com>

Hi Rajeshwari,

Before applied this patch, it must apply your patch for PINMUX. right?

Best Regards,
Jaehoon Chung

On 06/12/2012 03:14 PM, Chander Kashyap wrote:

> Hi,
> 
> On 11 June 2012 19:26, Rajeshwari Birje <rajeshwari.birje@gmail.com> wrote:
>> Hi  All,
>>
>> ccing Jaehoon Chung
>>
>> Regards,
>> Rajeshwari Shinde.
>>
>>
>> On Mon, Jun 11, 2012 at 6:18 PM, Rajeshwari Shinde
>> <rajeshwari.s@samsung.com> wrote:
>>> Add DWMMC driver support and resgister description for same.
>>>
>>> Signed-off-by: Alim Akhtar <alim.akhtar@samsung.com>
>>> Signed-off-by: Terry Lambert <tlambert@chromium.org>
>>> Signed-off-by: Rajeshwari Shinde <rajeshwari.s@samsung.com>
>>> ---
>>> Changes in V2:
>>>        - Incorporated comments from Jaehung Chung.
>>>        - Renamed MSHCI to DWMMC through out the driver.
>>>        - Renamed files to exynos_dwmmc from exynos_mshc.
>>>        - Removed major hard codings of values.
>>>        - Wrote dw_mci_writel and dw_mci_readl functions for writel and readl.
>>>        - Removed structure of registers and defined each one separately.
>>>  orch/arm/include/asm/arch-exynos/exynos_dwmmc.h |  229 +++++++++
>>>  drivers/mmc/Makefile                            |    1 +
>>>  drivers/mmc/exynos_dwmmc.c                      |  566 +++++++++++++++++++++++
>>>  3 files changed, 796 insertions(+), 0 deletions(-)
>>>  create mode 100644 arch/arm/include/asm/arch-exynos/exynos_dwmmc.h
>>>  create mode 100644 drivers/mmc/exynos_dwmmc.c
>>>
>>> diff --git a/arch/arm/include/asm/arch-exynos/exynos_dwmmc.h b/arch/arm/include/asm/arch-exynos/exynos_dwmmc.h
>>> new file mode 100644
>>> index 0000000..349bd75
>>> --- /dev/null
>>> +++ b/arch/arm/include/asm/arch-exynos/exynos_dwmmc.h
>>> @@ -0,0 +1,229 @@
>>> +/*
>>> + * (C) Copyright 2012 SAMSUNG Electronics
>>> + * Abhilash Kesavan <a.kesavan@samsung.com>
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify
>>> + * it under the terms of the GNU General Public License as published by
>>> + * the Free Software Foundation; either version 2 of the License, or
>>> + * (at your option) any later version.
>>> + *
>>> + * This program is distributed in the hope that it will be useful,
>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>>> + * GNU General Public License for more details.
>>> + *
>>> + * You should have received a copy of the GNU General Public License
>>> + * along with this program; if not, write to the Free Software
>>> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
>>> + *
>>> + */
>>> +#ifndef __ASM_ARCH_COMMON_DWMMC_H
>>> +#define __ASM_ARCH_COMMON_DWMMC_H
>>> +
>>> +#include <asm/arch/pinmux.h>
>>> +
>>> +#ifndef __ASSEMBLY__
>>> +struct dw_mci_host {
>>> +       void                    *ioaddr;
>>> +       unsigned int            clock;  /* Current clock in MHz */
>>> +       enum periph_id          peripheral;
>>> +       unsigned int            verid;  /* SDHCI spec. version */
>>> +       unsigned int            data_offset;    /* DATA offset */
>>> +};
>>> +
>>> +/*
>>> + * Struct idma
>>> + * Holds the descriptor list
>>> + */
>>> +struct dw_mci_idmac {
>>> +       u32     des0;
>>> +       u32     des1;
>>> +       u32     des2;
>>> +       u32     des3;
>>> +};
>>> +
> #endif
>>> +/*  Control Register  Register */
>>> +#define DWMCI_CONTROL  0x00
>>> +#define CTRL_RESET     (0x1 << 0)
>>> +#define FIFO_RESET     (0x1 << 1)
>>> +#define DMA_RESET      (0x1 << 2)
>>> +#define DMA_ENABLE     (0x1 << 5)
>>> +#define SEND_AS_CCSD   (0x1 << 10)
>>> +#define ENABLE_IDMAC    (0x1 << 25)
>>> +
>>> +/*  Power Enable Register */
>>> +#define DWMCI_PWREN    0x04
>>> +#define POWER_ENABLE   (0x1 << 0)
>>> +
>>> +#define DWMCI_CLKDIV   0x08
>>> +#define DWMCI_CLKSRC   0x0c
>>> +
>>> +/*  Clock Enable Register */
>>> +#define DWMCI_CLKENA   0x10
>>> +#define CLK_ENABLE     (0x1 << 0)
>>> +#define CLK_DISABLE    (0x0 << 0)
>>> +
>>> +/* Timeout Register */
>>> +#define DWMCI_TMOUT    0x14
>>> +#define TMOUT_MAX      0xffffffff
>>> +
>>> +/*  Card Type Register */
>>> +#define DWMCI_CTYPE            0x18
>>> +#define PORT0_CARD_WIDTH1      0
>>> +#define PORT0_CARD_WIDTH4      (0x1 << 0)
>>> +#define PORT0_CARD_WIDTH8      (0x1 << 16)
>>> +
>>> +#define DWMCI_BLKSIZE          0x1c
>>> +#define DWMCI_BYTCNT           0x20
>>> +
>>> +/*  Interrupt Mask Register */
>>> +#define DWMCI_INTMASK  0x24
>>> +#define INTMSK_ALL     0xffffffff
>>> +#define INTMSK_RE      (0x1 << 1)
>>> +#define INTMSK_CDONE   (0x1 << 2)
>>> +#define INTMSK_DTO     (0x1 << 3)
>>> +#define INTMSK_DCRC    (0x1 << 7)
>>> +#define INTMSK_RTO     (0x1 << 8)
>>> +#define INTMSK_DRTO    (0x1 << 9)
>>> +#define INTMSK_HTO     (0x1 << 10)
>>> +#define INTMSK_FRUN    (0x1 << 11)
>>> +#define INTMSK_HLE     (0x1 << 12)
>>> +#define INTMSK_SBE     (0x1 << 13)
>>> +#define INTMSK_ACD     (0x1 << 14)
>>> +#define INTMSK_EBE     (0x1 << 15)
>>> +
>>> +#define DWMCI_CMDARG   0x28
>>> +
>>> +/* Command Register */
>>> +#define DWMCI_CMD              0x2c
>>> +#define CMD_RESP_EXP_BIT       (0x1 << 6)
>>> +#define CMD_RESP_LENGTH_BIT    (0x1 << 7)
>>> +#define CMD_CHECK_CRC_BIT      (0x1 << 8)
>>> +#define CMD_DATA_EXP_BIT       (0x1 << 9)
>>> +#define CMD_RW_BIT             (0x1 << 10)
>>> +#define CMD_SENT_AUTO_STOP_BIT (0x1 << 12)
>>> +#define CMD_WAIT_PRV_DAT_BIT   (0x1 << 13)
>>> +#define CMD_SEND_CLK_ONLY      (0x1 << 21)
>>> +#define CMD_USE_HOLD_REG       (0x1 << 29)
>>> +#define CMD_STRT_BIT           (0x1 << 31)
>>> +#define CMD_ONLY_CLK           (CMD_STRT_BIT | CMD_SEND_CLK_ONLY | \
>>> +                               CMD_WAIT_PRV_DAT_BIT)
>>> +
>>> +#define DWMCI_RESP0            0x30
>>> +#define DWMCI_RESP1            0x34
>>> +#define DWMCI_RESP2            0x38
>>> +#define DWMCI_RESP3            0x3c
>>> +
>>> +#define DWMCI_MINTSTS          0x40
>>> +
>>> +/*  Raw Interrupt Register */
>>> +#define DWMCI_RINTSTS  0x44
>>> +#define DATA_ERR       (INTMSK_EBE | INTMSK_SBE | INTMSK_HLE | \
>>> +                       INTMSK_FRUN | INTMSK_EBE | INTMSK_DCRC)
>>> +#define DATA_TOUT      (INTMSK_HTO | INTMSK_DRTO)
>>> +
>>> +/*  Status Register */
>>> +#define DWMCI_STATUS   0x48
>>> +#define DATA_BUSY      (0x1 << 9)
>>> +
>>> +/*  FIFO Threshold Watermark Register */
>>> +#define DWMCI_FIFOTH   0x4c
>>> +#define TX_WMARK       (0xFFF << 0)
>>> +#define RX_WMARK       (0xFFF << 16)
>>> +#define MSIZE_MASK     (0x7 << 28)
>>> +
>>> +#define DWMCI_CDETECT  0x50
>>> +#define DWMCI_WRTORT   0x54
>>> +#define DWMCI_GPIO     0x58
>>> +#define DWMCI_TCBCNT   0x5c
>>> +#define DWMCI_TBBCNT   0x60
>>> +#define DWMCI_DEBENCE  0x64
>>> +#define DWMCI_USRID    0x68
>>> +#define DWMCI_VERID    0x6c
>>> +#define DWMCI_HCON     0x70
>>> +#define DWMCI_UHS_REG  0x74
>>> +#define DWMCI_RST_n    0x78
>>> +
>>> +/* DW DMA Mutiple Transaction Size */
>>> +#define MSIZE_8                (2 << 28)
>>> +
>>> +/*  Bus Mode Register */
>>> +#define DWMCI_BMOD             0x80
>>> +#define BMOD_IDMAC_RESET       (0x1 << 0)
>>> +#define BMOD_IDMAC_FB          (0x1 << 1)
>>> +#define BMOD_IDMAC_ENABLE      (0x1 << 7)
>>> +
>>> +#define DWMCI_PLDMND           0x84
>>> +#define DWMCI_DBADDR           0x88
>>> +
>>> +/* IDMAC bits */
>>> +#define DWMCI_IDSTS            0x8c
>>> +#define DWMCI_IDMAC_OWN                (0x1 << 31)
>>> +#define DWMCI_IDMAC_CH         (0x1 << 4)
>>> +#define DWMCI_IDMAC_FS         (0x1 << 3)
>>> +#define DWMCI_IDMAC_LD         (0x1 << 2)
>>> +
>>> +#define DWMCI_IDINTEN          0x90
>>> +#define DWMCI_DSCADDR          0x94
>>> +#define DWMCI_BUFADDR          0x98
>>> +
>>> +/* CLKSEL bits*/
>>> +#define DWMCI_CLKSEL           0x9c
>>> +#define SELCLK_SAMPLE_1PHASE_Shift     (0x1 << 0)
>>> +#define SELCLK_DRV_3PHASE_SHIFT                (0x3 << 16)
>>> +#define SELCLK_DRV_2PHASE_SHIFT                (0x2 << 16)
>>> +#define SELCLK_DIV_RATIO               (0x3 << 24)
>>> +
>>> +#define DWMCI_CARDTHRCTL       0x100
>>> +
>>> +/*
>>> + * Data offset is difference according to Version
>>> + * Lower than 2.40a : data register offest is 0x100
>>> + */
>>> +#define DW_MMC_240A            0x240a
>>> +#define DATA_OFFSET            0x100
>>> +#define DATA_240A_OFFSET       0x200
>>> +
>>> +#define MAX_DWMMC_CLOCK                52000000 /* Max limit is 52MHz */
>>> +#define MIN_DWMMC_CLOCK                400000 /* Lower limit is 400KHz */
>>> +#define COMMAND_TIMEOUT                10000
>>> +#define TIMEOUT_MS             100
>>> +#define MAXCLKDIV              0xff
>>> +
>>> +/* Version ID register define */
>>> +#define GET_VERID(x)   ((x) & 0xFFFF)
>>> +
>>> +static inline void dw_mci_writel(struct dw_mci_host *host, u32 val, int reg)
>>> +{
>>> +       writel(val, host->ioaddr + reg);
>>> +}
>>> +
>>> +static inline void dw_mci_writew(struct dw_mci_host *host, u16 val, int reg)
>>> +{
>>> +       writew(val, host->ioaddr + reg);
>>> +}
>>> +
>>> +static inline void dw_mci_writeb(struct dw_mci_host *host, u8 val, int reg)
>>> +{
>>> +       writeb(val, host->ioaddr + reg);
>>> +}
>>> +
>>> +static inline u32 dw_mci_readl(struct dw_mci_host *host, int reg)
>>> +{
>>> +       return readl(host->ioaddr + reg);
>>> +}
>>> +
>>> +static inline u16 dw_mci_readw(struct dw_mci_host *host, int reg)
>>> +{
>>> +       return readw(host->ioaddr + reg);
>>> +}
>>> +
>>> +static inline u8 dw_mci_readb(struct dw_mci_host *host, int reg)
>>> +{
>>> +       return readb(host->ioaddr + reg);
>>> +}
>>> +
>>> +int dw_mci_init(enum periph_id periph_id, int bus_width);
>>> +
>>> +#endif /* __ASSEMBLY__ */
> remove at this place after structure declaration.
>>> +#endif /* __ASM_ARCH_COMMON_DWMMC_H */
>>> diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
>>> index c245352..cf0be05 100644
>>> --- a/drivers/mmc/Makefile
>>> +++ b/drivers/mmc/Makefile
>>> @@ -27,6 +27,7 @@ LIB   := $(obj)libmmc.o
>>>
>>>  COBJS-$(CONFIG_BFIN_SDH) += bfin_sdh.o
>>>  COBJS-$(CONFIG_DAVINCI_MMC) += davinci_mmc.o
>>> +COBJS-$(CONFIG_EXYNOS_DWMMC) += exynos_dwmmc.o
>>>  COBJS-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o
>>>  COBJS-$(CONFIG_FTSDC010) += ftsdc010_esdhc.o
>>>  COBJS-$(CONFIG_GENERIC_MMC) += mmc.o
>>> diff --git a/drivers/mmc/exynos_dwmmc.c b/drivers/mmc/exynos_dwmmc.c
>>> new file mode 100644
>>> index 0000000..96f6ceb
>>> --- /dev/null
>>> +++ b/drivers/mmc/exynos_dwmmc.c
>>> @@ -0,0 +1,566 @@
>>> +/*
>>> + * (C) Copyright 2012 Samsung Electronics Co. Ltd
>>> + *
>>> + * See file CREDITS for list of people who contributed to this
>>> + * project.
>>> + *
>>> + * This program is free software; you can redistribute it and/or
>>> + * modify it under the terms of the GNU General Public License as
>>> + * published by the Free Software Foundation; either version 2 of
>>> + * the License, or (at your option) any later version.
>>> + *
>>> + * This program is distributed in the hope that it will be useful,
>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>>> + * GNU General Public License for more details.
>>> + *
>>> + * You should have received a copy of the GNU General Public License
>>> + * along with this program; if not, write to the Free Software
>>> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
>>> + * MA 02111-1307 USA
>>> + */
>>> +
>>> +#include <common.h>
>>> +#include <mmc.h>
>>> +#include <asm/errno.h>
>>> +#include <asm/arch/clk.h>
>>> +#include <asm/arch/cpu.h>
>>> +#include <asm/arch/exynos_dwmmc.h>
>>> +#include <asm/arch/pinmux.h>
>>> +
>>> +/* support 4 mmc hosts */
>>> +enum {
>>> +       MAX_MMC_HOSTS   = 4
>>> +};
>>> +
>>> +static struct mmc dw_mci_dev[MAX_MMC_HOSTS];
>>> +static struct dw_mci_host dw_mci_host[MAX_MMC_HOSTS];
>>> +static int num_devs;
>>> +
>>> +/**
>>> + * Set bits of DWMMC host control register.
>>> + *
>>> + * @param host DWMMC host
>>> + * @param bits bits to be set
>>> + * @return 0 on success, TIMEOUT on failure
>>> + */
>>> +static int dw_mci_setbits(struct dw_mci_host *host, unsigned int bits)
>>> +{
>>> +       ulong start;
>>> +
>>> +       setbits_le32(host->ioaddr + DWMCI_CONTROL, bits);
>>> +
>>> +       start = get_timer(0);
>>> +       while (dw_mci_readl(host, DWMCI_CONTROL) & bits) {
>>> +               if (get_timer(start) > TIMEOUT_MS) {
>>> +                       debug("Set bits failed\n");
>>> +                       return TIMEOUT;
>>> +               }
>>> +       }
>>> +       return 0;
>>> +}
>>> +
>>> +/**
>>> + * Reset DWMMC host control register.
>>> + *
>>> + * @param host DWMMC host
>>> + * @return 0 on success, TIMEOUT on failure
>>> + */
>>> +static int dw_mci_reset_all(struct dw_mci_host *host)
>>> +{
>>> +       ulong start;
>>> +
>>> +       /*
>>> +       * Before we reset ciu check the DATA0 line.  If it is low and
>>> +       * we resets the ciu then we might see some errors.
>>> +       */
>>> +       start = get_timer(0);
>>> +       while (dw_mci_readl(host, DWMCI_STATUS) & DATA_BUSY) {
>>> +               if (get_timer(start) > TIMEOUT_MS) {
>>> +                       debug("Controller did not release"
>>> +                               "data0 before ciu reset\n");
>>> +                       return TIMEOUT;
>>> +               }
>>> +       }
>>> +       return dw_mci_setbits(host, CTRL_RESET | FIFO_RESET | DMA_RESET);
>>> +}
>>> +
>>> +static void dw_mci_set_mdma_desc(u8 *desc_vir, u8 *desc_phy,
>>> +               unsigned int des0, unsigned int des1, unsigned int des2)
>>> +{
>>> +       struct dw_mci_idmac *desc = (struct dw_mci_idmac *)desc_vir;
>>> +
>>> +       desc->des0 = des0;
>>> +       desc->des1 = des1;
>>> +       desc->des2 = des2;
>>> +       desc->des3 = (unsigned int)desc_phy + sizeof(struct dw_mci_idmac);
>>> +}
>>> +
>>> +static int dw_mci_prepare_data(struct dw_mci_host *host, struct mmc_data *data)
>>> +{
>>> +       unsigned int i, data_cnt, des_flag, blksz;
>>> +       int err;
>>> +       ulong data_start, data_end;
>>> +       static struct dw_mci_idmac idmac_desc[0x10000];
>>> +       struct dw_mci_idmac *pdesc_dmac;
>>> +
>>> +       err = dw_mci_setbits(host, FIFO_RESET);
>>> +       if (err) {
>>> +               debug("Fail to reset FIFO\n");
>>> +               return err;
>>> +       }
>>> +
>>> +       pdesc_dmac = idmac_desc;
>>> +       blksz = data->blocksize;
>>> +       data_cnt = data->blocks;
>>> +
>>> +       for  (i = 0;; i++) {
>>> +               des_flag = (DWMCI_IDMAC_OWN | DWMCI_IDMAC_CH);
>>> +               des_flag |= (i == 0) ? DWMCI_IDMAC_FS : 0;
>>> +               if (data_cnt <= 8) {
>>> +                       des_flag |= DWMCI_IDMAC_LD;
>>> +                       dw_mci_set_mdma_desc((u8 *)pdesc_dmac,
>>> +                       (u8 *)virt_to_phys(pdesc_dmac),
>>> +                       des_flag, blksz * data_cnt,
>>> +                       (unsigned int)(virt_to_phys(data->dest)) +
>>> +                       (unsigned int)(i * 0x1000));
>>> +                       break;
>>> +               }
>>> +               /* max transfer size is 4KB per descriptor */
>>> +               dw_mci_set_mdma_desc((u8 *)pdesc_dmac,
>>> +                       (u8 *)virt_to_phys(pdesc_dmac),
>>> +                       des_flag, blksz * 8,
>>> +                       virt_to_phys(data->dest) +
>>> +                       (unsigned int)(i * 0x1000));
>>> +
>>> +               data_cnt -= 8;
>>> +               pdesc_dmac++;
>>> +       }
>>> +
>>> +       data_start = (ulong)idmac_desc;
>>> +       data_end = (ulong)pdesc_dmac;
>>> +       flush_dcache_range(data_start, data_end + ARCH_DMA_MINALIGN);
>>> +
>>> +       data_start = (ulong)data->dest;
>>> +       data_end  = (ulong)(data->dest + data->blocks * data->blocksize);
>>> +       flush_dcache_range(data_start, data_end);
>>> +
>>> +       dw_mci_writel(host, (unsigned int)virt_to_phys(idmac_desc),
>>> +                               DWMCI_DBADDR);
>>> +
>>> +       /* enable the Internal DMA Controller */
>>> +       setbits_le32(host->ioaddr + DWMCI_CONTROL, ENABLE_IDMAC |
>>> +                                                       DMA_ENABLE);
>>> +       setbits_le32(host->ioaddr + DWMCI_BMOD, BMOD_IDMAC_ENABLE |
>>> +                                                       BMOD_IDMAC_FB);
>>> +
>>> +       dw_mci_writel(host, data->blocksize, DWMCI_BLKSIZE);
>>> +       dw_mci_writel(host, data->blocksize * data->blocks, DWMCI_BYTCNT);
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +static int dw_mci_set_transfer_mode(struct dw_mci_host *host,
>>> +       struct mmc_data *data)
>>> +{
>>> +       int mode = CMD_DATA_EXP_BIT;
>>> +
>>> +       if (data->blocks > 1)
>>> +               mode |= CMD_SENT_AUTO_STOP_BIT;
>>> +       if (data->flags & MMC_DATA_WRITE)
>>> +               mode |= CMD_RW_BIT;
>>> +
>>> +       return mode;
>>> +}
>>> +
>>> +/*
>>> + * Sends a command out on the bus.
>>> + *
>>> + * @param mmc  mmc device
>>> + * @param cmd  mmc_cmd to be sent on bus
>>> + * @param data mmc data to be sent (optional)
>>> + *
>>> + * @return     return 0 if ok, else error number
>>> + */
>>> +static int dw_mci_send_command(struct mmc *mmc, struct mmc_cmd *cmd,
>>> +               struct mmc_data *data)
>>> +{
>>> +       struct dw_mci_host *host = mmc->priv;
>>> +
>>> +       int flags = 0, i, err;
>>> +       unsigned int mask;
>>> +       ulong start, data_start, data_end;
>>> +
>>> +       /*
>>> +       * We shouldn't wait for data inihibit for stop commands, even
>>> +       * though they might use busy signaling
>>> +       */
>>> +       start = get_timer(0);
>>> +       while (dw_mci_readl(host, DWMCI_STATUS) & DATA_BUSY) {
>>> +               if (get_timer(start) > COMMAND_TIMEOUT) {
>>> +                       debug("timeout on data busy\n");
>>> +                       return TIMEOUT;
>>> +               }
>>> +       }
>>> +
>>> +       if (dw_mci_readl(host, DWMCI_RINTSTS)) {
>>> +               if ((dw_mci_readl(host, DWMCI_RINTSTS) &
>>> +                               (INTMSK_CDONE | INTMSK_ACD)) == 0)
>>> +                       debug("there are pending interrupts 0x%x\n",
>>> +                               dw_mci_readl(host, DWMCI_RINTSTS));
>>> +       }
>>> +       /* It clears all pending interrupts before sending a command*/
>>> +       dw_mci_writel(host, INTMSK_ALL, DWMCI_RINTSTS);
>>> +
>>> +       if (data) {
>>> +               err = dw_mci_prepare_data(host, data);
>>> +               if (err) {
>>> +                       debug("fail to prepare data\n");
>>> +                       return err;
>>> +               }
>>> +       }
>>> +
>>> +       dw_mci_writel(host, cmd->cmdarg, DWMCI_CMDARG);
>>> +
>>> +       if (data)
>>> +               flags = dw_mci_set_transfer_mode(host, data);
>>> +
>>> +       if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY))
>>> +               /* this is out of SD spec */
>>> +               return -1;
>>> +
>>> +       if (cmd->resp_type & MMC_RSP_PRESENT) {
>>> +               flags |= CMD_RESP_EXP_BIT;
>>> +               if (cmd->resp_type & MMC_RSP_136)
>>> +                       flags |= CMD_RESP_LENGTH_BIT;
>>> +       }
>>> +
>>> +       if (cmd->resp_type & MMC_RSP_CRC)
>>> +               flags |= CMD_CHECK_CRC_BIT;
>>> +       flags |= (cmd->cmdidx | CMD_STRT_BIT | CMD_USE_HOLD_REG |
>>> +                       CMD_WAIT_PRV_DAT_BIT);
>>> +
>>> +       mask = dw_mci_readl(host, DWMCI_CMD);
>>> +       if (mask & CMD_STRT_BIT)
>>> +               debug("cmd busy, current cmd: %d", cmd->cmdidx);
>>> +
>>> +       dw_mci_writel(host, flags, DWMCI_CMD);
>>> +       /* wait for command complete by busy waiting. */
>>> +       for (i = 0; i < COMMAND_TIMEOUT; i++) {
>>> +               mask = dw_mci_readl(host, DWMCI_RINTSTS);
>>> +               if (mask & INTMSK_CDONE) {
>>> +                       if (!data)
>>> +                               dw_mci_writel(host, mask, DWMCI_RINTSTS);
>>> +                       break;
>>> +               }
>>> +       }
>>> +       /* timeout for command complete. */
>>> +       if (COMMAND_TIMEOUT == i) {
>>> +               debug("timeout waiting for status update\n");
>>> +               return TIMEOUT;
>>> +       }
>>> +
>>> +       if (mask & INTMSK_RTO) {
>>> +               if (((cmd->cmdidx == MMC_CMD_SEND_EXT_CSD ||
>>> +                       cmd->cmdidx == MMC_CMD_APP_CMD)) == 0) {
>>> +                       debug("response timeout error: 0x%x cmd: %d\n",
>>> +                               mask, cmd->cmdidx);
>>> +               }
>>> +                       return TIMEOUT;
>>> +       } else if (mask & INTMSK_RE) {
>>> +               debug("response error: 0x%x cmd: %d\n", mask, cmd->cmdidx);
>>> +               return -1;
>>> +       }
>>> +       if (cmd->resp_type & MMC_RSP_PRESENT) {
>>> +               if (cmd->resp_type & MMC_RSP_136) {
>>> +                       /* CRC is stripped so we need to do some shifting. */
>>> +                               cmd->response[0] = dw_mci_readl(host,
>>> +                                                               DWMCI_RESP3);
>>> +                               cmd->response[1] = dw_mci_readl(host,
>>> +                                                               DWMCI_RESP2);
>>> +                               cmd->response[2] = dw_mci_readl(host,
>>> +                                                               DWMCI_RESP1);
>>> +                               cmd->response[3] = dw_mci_readl(host,
>>> +                                                               DWMCI_RESP0);
>>> +               } else {
>>> +                       cmd->response[0] = dw_mci_readl(host, DWMCI_RESP0);
>>> +                       debug("\tcmd->response[0]: 0x%08x\n", cmd->response[0]);
>>> +               }
>>> +       }
>>> +
>>> +       if (data) {
>>> +               while (!(mask & (DATA_ERR | DATA_TOUT | INTMSK_DTO)))
>>> +                       mask = dw_mci_readl(host, DWMCI_RINTSTS);
>>> +               dw_mci_writel(host, mask, DWMCI_RINTSTS);
>>> +               if (data->flags & MMC_DATA_READ) {
>>> +                       data_start = (ulong)data->dest;
>>> +                       data_end = (ulong)data->dest +
>>> +                                       data->blocks * data->blocksize;
>>> +                       invalidate_dcache_range(data_start, data_end);
>>> +               }
>>> +               if (mask & (DATA_ERR | DATA_TOUT)) {
>>> +                       debug("error during transfer: 0x%x\n", mask);
>>> +                       /* make sure disable IDMAC and IDMAC_Interrupts */
>>> +                       dw_mci_writel(host, (dw_mci_readl(host, DWMCI_CONTROL) &
>>> +                       ~(DMA_ENABLE | ENABLE_IDMAC)), DWMCI_CONTROL);
>>> +                       /* mask all interrupt source of IDMAC */
>>> +                       dw_mci_writel(host, 0, DWMCI_IDINTEN);
>>> +                       return -1;
>>> +               } else if (mask & INTMSK_DTO) {
>>> +                       debug("dwmmc dma interrupt end\n");
>>> +               } else {
>>> +                       debug("unexpected condition 0x%x\n", mask);
>>> +               }
>>> +               /* make sure disable IDMAC and IDMAC_Interrupts */
>>> +               dw_mci_writel(host, (dw_mci_readl(host, DWMCI_CONTROL) &
>>> +                                       ~(DMA_ENABLE | ENABLE_IDMAC)),
>>> +                                       DWMCI_CONTROL);
>>> +               /* mask all interrupt source of IDMAC */
>>> +               dw_mci_writel(host, 0, DWMCI_IDINTEN);
>>> +       }
>>> +
>>> +       udelay(100);
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +/*
>>> + * ON/OFF host controller clock
>>> + *
>>> + * @param host         pointer to dw_mci_host
>>> + * @param val          to enable/disable clock
>>> + */
>>> +static void dw_mci_clock_onoff(struct dw_mci_host *host, int val)
>>> +{
>>> +
>>> +       if (val)
>>> +               dw_mci_writel(host, CLK_ENABLE, DWMCI_CLKENA);
>>> +       else
>>> +               dw_mci_writel(host, CLK_DISABLE, DWMCI_CLKENA);
>>> +
>>> +       dw_mci_writel(host, 0, DWMCI_CMD);
>>> +       dw_mci_writel(host, CMD_ONLY_CLK, DWMCI_CMD);
>>> +}
>>> +
>>> +/*
>>> + * change host controller clock
>>> + *
>>> + * @param host         pointer to dw_mci_host
>>> + * @param clock                request clock
>>> + */
>>> +static void  dw_mci_change_clock(struct dw_mci_host *host, uint clock)
>>> +{
>>> +       int div;
>>> +       u32 sclk_mshc;
>>> +
>>> +       if (clock == host->clock)
>>> +               return;
>>> +
>>> +       /* If Input clock is higher than maximum mshc clock */
>>> +       if (clock > MAX_DWMMC_CLOCK) {
>>> +               debug("Input clock is too high\n");
>>> +               clock = MAX_DWMMC_CLOCK;
>>> +       }
>>> +
>>> +       /* disable the clock before changing it */
>>> +       dw_mci_clock_onoff(host, CLK_DISABLE);
>>> +
>>> +       /* get the clock division */
>>> +       if (host->peripheral == PERIPH_ID_SDMMC4)
>>> +               sclk_mshc = get_dw_mci_clk_div(host->peripheral) / 2;
>>> +       else
>>> +               sclk_mshc = get_dw_mci_clk_div(host->peripheral) / 4;
>>> +
>>> +       /* CLKDIV */
>>> +       for (div = 1 ; div <= MAXCLKDIV; div++) {
>>> +               if ((sclk_mshc / (2 * div)) <= clock) {
>>> +                       dw_mci_writel(host, div, DWMCI_CLKDIV);
>>> +                       break;
>>> +               }
>>> +       }
>>> +
>>> +       dw_mci_writel(host, 0, DWMCI_CMD);
>>> +       dw_mci_writel(host, CMD_ONLY_CLK, DWMCI_CMD);
>>> +
>>> +       dw_mci_writel(host, dw_mci_readl(host, DWMCI_CMD) &
>>> +                                       (~CMD_SEND_CLK_ONLY),
>>> +                                       DWMCI_CMD);
>>> +
>>> +       dw_mci_clock_onoff(host, CLK_ENABLE);
>>> +       host->clock = clock;
>>> +}
>>> +
>>> +/*
>>> + * Set ios for host controller clock
>>> + *
>>> + * This sets the card bus width and clksel
>>> + */
>>> +static void dw_mci_set_ios(struct mmc *mmc)
>>> +{
>>> +       struct dw_mci_host *host = mmc->priv;
>>> +       int val;
>>> +
>>> +       debug("bus_width: %x, clock: %d\n", mmc->bus_width, mmc->clock);
>>> +
>>> +       if (mmc->clock > 0)
>>> +               dw_mci_change_clock(host, mmc->clock);
>>> +
>>> +       if (mmc->bus_width == 8)
>>> +               dw_mci_writel(host, PORT0_CARD_WIDTH8, DWMCI_CTYPE);
>>> +       else if (mmc->bus_width == 4)
>>> +               dw_mci_writel(host, PORT0_CARD_WIDTH4, DWMCI_CTYPE);
>>> +       else
>>> +               dw_mci_writel(host, PORT0_CARD_WIDTH1, DWMCI_CTYPE);
>>> +
>>> +       val = dw_mci_readl(host, DWMCI_CLKSEL);
>>> +       if (host->peripheral == PERIPH_ID_SDMMC0)
>>> +               val |= (SELCLK_SAMPLE_1PHASE_Shift | SELCLK_DRV_3PHASE_SHIFT |
>>> +                       SELCLK_DIV_RATIO);
>>> +       if (host->peripheral == PERIPH_ID_SDMMC2)
>>> +               val |= (SELCLK_SAMPLE_1PHASE_Shift | SELCLK_DRV_2PHASE_SHIFT |
>>> +                       SELCLK_DIV_RATIO);
>>> +       if (host->peripheral == PERIPH_ID_SDMMC4)
>>> +               val |= (SELCLK_SAMPLE_1PHASE_Shift | SELCLK_DRV_2PHASE_SHIFT);
>>> +
>>> +       dw_mci_writel(host, val, DWMCI_CLKSEL);
>>> +}
>>> +
>>> +/*
>>> + * Fifo init for host controller
>>> + */
>>> +static void dw_mci_fifo_init(struct dw_mci_host *host)
>>> +{
>>> +       int fifo_val, fifo_depth, fifo_threshold;
>>> +
>>> +       fifo_val = dw_mci_readl(host, DWMCI_FIFOTH);
>>> +
>>> +       /* Power-on value of RX_WMark is FIFO_DEPTH-1 */
>>> +       fifo_depth = 1 + ((fifo_val >> 16) & 0x7ff);
>>> +       fifo_threshold = fifo_depth / 2;
>>> +
>>> +       fifo_val &= ~(RX_WMARK | TX_WMARK | MSIZE_MASK);
>>> +       fifo_val |= (fifo_threshold | (fifo_threshold << 16) | MSIZE_8);
>>> +       dw_mci_writel(host, fifo_val, DWMCI_FIFOTH);
>>> +}
>>> +
>>> +
>>> +static int dw_mci_reset(struct dw_mci_host *host)
>>> +{
>>> +       int err;
>>> +
>>> +       /* power on the card */
>>> +       dw_mci_writel(host, POWER_ENABLE, DWMCI_PWREN);
>>> +
>>> +       err = dw_mci_reset_all(host);
>>> +       if (err)
>>> +               return err;
>>> +
>>> +       dw_mci_fifo_init(host);
>>> +
>>> +       /* clear all pending interrupts */
>>> +       dw_mci_writel(host, INTMSK_ALL, DWMCI_RINTSTS);
>>> +
>>> +       /* interrupts are not used, disable all */
>>> +       dw_mci_writel(host, 0, DWMCI_INTMASK);
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +static int dw_mci_initialize(struct mmc *mmc)
>>> +{
>>> +       struct dw_mci_host *host = (struct dw_mci_host *)mmc->priv;
>>> +       unsigned int ier;
>>> +       int err;
>>> +
>>> +       err = dw_mci_reset(host);
>>> +       if (err)
>>> +               return err;
>>> +
>>> +       /* enumerate at 400KHz */
>>> +       dw_mci_change_clock(host, MIN_DWMMC_CLOCK);
>>> +
>>> +       /* set auto stop command */
>>> +       ier = dw_mci_readl(host, DWMCI_CONTROL);
>>> +       ier |= SEND_AS_CCSD;
>>> +       dw_mci_writel(host, ier, DWMCI_CONTROL);
>>> +
>>> +       /* set 1bit card mode */
>>> +       dw_mci_writel(host, PORT0_CARD_WIDTH1, DWMCI_CTYPE);
>>> +
>>> +       dw_mci_writel(host, 0xfffff, DWMCI_DEBENCE);
>>> +
>>> +       /* set bus mode register for IDMAC */
>>> +       dw_mci_writel(host, BMOD_IDMAC_RESET, DWMCI_BMOD);
>>> +
>>> +       dw_mci_writel(host, 0x0, DWMCI_IDINTEN);
>>> +
>>> +       /* set the max timeout for data and response */
>>> +       dw_mci_writel(host, TMOUT_MAX, DWMCI_TMOUT);
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +int dw_mci_init(enum periph_id periph_id, int bus_width)
>>> +{
>>> +       struct dw_mci_host *mmc_host;
>>> +       struct mmc *mmc;
>>> +
>>> +       if (num_devs == MAX_MMC_HOSTS) {
>>> +               debug("%s: Too many hosts\n", __func__);
>>> +               return -1;
>>> +       }
>>> +
>>> +       /* set the clock for dwmmc controller */
>>> +       if (set_dw_mci_clk_div(periph_id)) {
>>> +               debug("clock_set_dw_mci failed\n");
>>> +               return -EINVAL;
>>> +       }
>>> +
>>> +       mmc = &dw_mci_dev[num_devs];
>>> +       mmc_host = &dw_mci_host[num_devs];
>>> +
>>> +       sprintf(mmc->name, "DWMMC%d", num_devs);
>>> +       num_devs++;
>>> +
>>> +       mmc->priv = mmc_host;
>>> +       mmc->send_cmd = dw_mci_send_command;
>>> +       mmc->set_ios = dw_mci_set_ios;
>>> +       mmc->init = dw_mci_initialize;
>>> +
>>> +       /*
>>> +       * In 2.40a spec, Data offset is changed.
>>> +       * Need to check the version-id and set data-offset for DATA register.
>>> +       */
>>> +       mmc_host->verid = GET_VERID(dw_mci_readl(mmc_host, DWMCI_VERID));
>>> +       debug("Version ID is %04x\n", mmc_host->verid);
>>> +
>>> +       if (mmc_host->verid < DW_MMC_240A)
>>> +               mmc_host->data_offset = DATA_OFFSET;
>>> +       else
>>> +               mmc_host->data_offset = DATA_240A_OFFSET;
>>> +
>>> +       mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
>>> +       mmc->host_caps = MMC_MODE_HS_52MHz | MMC_MODE_HS | MMC_MODE_HC;
>>> +
>>> +       if (bus_width == 8)
>>> +               mmc->host_caps |= MMC_MODE_8BIT;
>>> +       else
>>> +               mmc->host_caps |= MMC_MODE_4BIT;
>>> +
>>> +       mmc->f_min = MIN_DWMMC_CLOCK;
>>> +       mmc->f_max = MAX_DWMMC_CLOCK;
>>> +
>>> +       exynos_pinmux_config(periph_id,
>>> +                       bus_width == 8 ? PINMUX_FLAG_8BIT_MODE : 0);
>>> +
>>> +       mmc_host->clock = 0;
>>> +       mmc_host->peripheral = periph_id;
>>> +       mmc_host->ioaddr = (void *)samsung_get_base_dwmmc();
>>> +       mmc->b_max = 1;
>>> +       mmc_register(mmc);
>>> +       mmc->block_dev.removable = 1;
>>> +       debug("dwmmc: periph_id=%d, width=%d, ioaddr=%p\n",
>>> +             periph_id, bus_width, mmc_host->ioaddr);
>>> +
>>> +       return 0;
>>> +}
>>> --
>>> 1.7.4.4
>>>
>>> _______________________________________________
>>> U-Boot mailing list
>>> U-Boot 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
> 
> 
> 

  reply	other threads:[~2012-06-12  8:37 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-06-11 12:48 [U-Boot] [PATCH V2] MMC: DWMMC: Add DWMMC driver Rajeshwari Shinde
2012-06-11 13:56 ` Rajeshwari Birje
2012-06-12  6:14   ` Chander Kashyap
2012-06-12  8:37     ` Jaehoon Chung [this message]
2012-06-12  9:33       ` Rajeshwari Birje
2012-06-14 13:36         ` Jaehoon Chung
2012-06-15 11:15           ` Rajeshwari Birje
2012-08-30 16:45           ` Andy Fleming
2012-06-12  9:35     ` Rajeshwari Birje

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4FD6FFCC.6070801@samsung.com \
    --to=jh80.chung@samsung.com \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.