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
>
>
>
next prev parent 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.