* [U-Boot] [PATCH V2] MMC: DWMMC: Add DWMMC driver
@ 2012-06-11 12:48 Rajeshwari Shinde
2012-06-11 13:56 ` Rajeshwari Birje
0 siblings, 1 reply; 9+ messages in thread
From: Rajeshwari Shinde @ 2012-06-11 12:48 UTC (permalink / raw)
To: u-boot
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;
+};
+
+/* 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__ */
+#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
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [U-Boot] [PATCH V2] MMC: DWMMC: Add DWMMC driver
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
0 siblings, 1 reply; 9+ messages in thread
From: Rajeshwari Birje @ 2012-06-11 13:56 UTC (permalink / raw)
To: u-boot
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;
> +};
> +
> +/* ?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__ */
> +#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
^ permalink raw reply [flat|nested] 9+ messages in thread
* [U-Boot] [PATCH V2] MMC: DWMMC: Add DWMMC driver
2012-06-11 13:56 ` Rajeshwari Birje
@ 2012-06-12 6:14 ` Chander Kashyap
2012-06-12 8:37 ` Jaehoon Chung
2012-06-12 9:35 ` Rajeshwari Birje
0 siblings, 2 replies; 9+ messages in thread
From: Chander Kashyap @ 2012-06-12 6:14 UTC (permalink / raw)
To: u-boot
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
--
with warm regards,
Chander Kashyap
^ permalink raw reply [flat|nested] 9+ messages in thread
* [U-Boot] [PATCH V2] MMC: DWMMC: Add DWMMC driver
2012-06-12 6:14 ` Chander Kashyap
@ 2012-06-12 8:37 ` Jaehoon Chung
2012-06-12 9:33 ` Rajeshwari Birje
2012-06-12 9:35 ` Rajeshwari Birje
1 sibling, 1 reply; 9+ messages in thread
From: Jaehoon Chung @ 2012-06-12 8:37 UTC (permalink / raw)
To: u-boot
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
>
>
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* [U-Boot] [PATCH V2] MMC: DWMMC: Add DWMMC driver
2012-06-12 8:37 ` Jaehoon Chung
@ 2012-06-12 9:33 ` Rajeshwari Birje
2012-06-14 13:36 ` Jaehoon Chung
0 siblings, 1 reply; 9+ messages in thread
From: Rajeshwari Birje @ 2012-06-12 9:33 UTC (permalink / raw)
To: u-boot
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@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@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
>>
>>
>>
>
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* [U-Boot] [PATCH V2] MMC: DWMMC: Add DWMMC driver
2012-06-12 6:14 ` Chander Kashyap
2012-06-12 8:37 ` Jaehoon Chung
@ 2012-06-12 9:35 ` Rajeshwari Birje
1 sibling, 0 replies; 9+ messages in thread
From: Rajeshwari Birje @ 2012-06-12 9:35 UTC (permalink / raw)
To: u-boot
Hi Chander,
Thank you for comments will do the correction.
Regards,
Rajeshwari Shinde.
On Tue, Jun 12, 2012 at 11:44 AM, Chander Kashyap
<chander.kashyap@linaro.org> 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
>
>
>
> --
> with warm regards,
> Chander Kashyap
^ permalink raw reply [flat|nested] 9+ messages in thread
* [U-Boot] [PATCH V2] MMC: DWMMC: Add DWMMC driver
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
0 siblings, 2 replies; 9+ messages in thread
From: Jaehoon Chung @ 2012-06-14 13:36 UTC (permalink / raw)
To: u-boot
Hi Rajeshwari,
This patch has too many dependence with other patches.
(Pinmux and PeripID, patches for MSHCI setting).
And as i mentioned, designWare controller isn't exynos specific.
I think good that separate two files. (dw_mmc.c and exynos_dw_mmc.c)
Like this...dw_mmc.c is generic code and exynos_dw_mmc.c is samsung specific code..
If you want, I will send to you patch that related with them. (based-on your patch)
And Added some comment
On 06/12/2012 06:33 PM, Rajeshwari Birje wrote:
> Hi Jaehoon Chung,
>
> Yes you need to apply the following patchset
> http://comments.gmane.org/gmane.comp.boot-loaders.u-boot/132754
>
> Regards,
> Rajeshwari Shinde.
>
> On Tue, Jun 12, 2012 at 2:07 PM, Jaehoon Chung <jh80.chung@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@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;
mmc->priv's type is void type. I think good that use (struct dw_mci_host *)mmc->priv;
>>>>> +
>>>>> + int flags = 0, i, err;
>>>>> + unsigned int mask;
>>>>> + ulong start, data_start, data_end;
>>>>> +
>>>>> + /*
>>>>> + * We shouldn't wait for data inihibit for stop commands, even
>>>>> + * though they might use busy signaling
>>>>> + */
>>>>> + start = get_timer(0);
>>>>> + while (dw_mci_readl(host, DWMCI_STATUS) & DATA_BUSY) {
>>>>> + if (get_timer(start) > COMMAND_TIMEOUT) {
>>>>> + debug("timeout on data busy\n");
>>>>> + return TIMEOUT;
>>>>> + }
>>>>> + }
>>>>> +
What do the below condition? just debugging? i didn't understand why need this condition.
>>>>> + if (dw_mci_readl(host, DWMCI_RINTSTS)) {
>>>>> + if ((dw_mci_readl(host, DWMCI_RINTSTS) &
>>>>> + (INTMSK_CDONE | INTMSK_ACD)) == 0)
>>>>> + debug("there are pending interrupts 0x%x\n",
>>>>> + dw_mci_readl(host, DWMCI_RINTSTS));
>>>>> + }
>>>>> + /* It clears all pending interrupts before sending a command*/
>>>>> + dw_mci_writel(host, INTMSK_ALL, DWMCI_RINTSTS);
>>>>> +
>>>>> + if (data) {
>>>>> + err = dw_mci_prepare_data(host, data);
>>>>> + if (err) {
>>>>> + debug("fail to prepare data\n");
>>>>> + return err;
>>>>> + }
>>>>> + }
>>>>> +
>>>>> + dw_mci_writel(host, cmd->cmdarg, DWMCI_CMDARG);
>>>>> +
>>>>> + if (data)
>>>>> + flags = dw_mci_set_transfer_mode(host, data);
>>>>> +
>>>>> + if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY))
>>>>> + /* this is out of SD spec */
>>>>> + return -1;
>>>>> +
>>>>> + if (cmd->resp_type & MMC_RSP_PRESENT) {
>>>>> + flags |= CMD_RESP_EXP_BIT;
>>>>> + if (cmd->resp_type & MMC_RSP_136)
>>>>> + flags |= CMD_RESP_LENGTH_BIT;
>>>>> + }
>>>>> +
>>>>> + if (cmd->resp_type & MMC_RSP_CRC)
>>>>> + flags |= CMD_CHECK_CRC_BIT;
>>>>> + flags |= (cmd->cmdidx | CMD_STRT_BIT | CMD_USE_HOLD_REG |
>>>>> + CMD_WAIT_PRV_DAT_BIT);
>>>>> +
>>>>> + mask = dw_mci_readl(host, DWMCI_CMD);
>>>>> + if (mask & CMD_STRT_BIT)
>>>>> + debug("cmd busy, current cmd: %d", cmd->cmdidx);
Also need not this condition. Debugging point?
>>>>> +
>>>>> + dw_mci_writel(host, flags, DWMCI_CMD);
>>>>> + /* wait for command complete by busy waiting. */
>>>>> + for (i = 0; i < COMMAND_TIMEOUT; i++) {
>>>>> + mask = dw_mci_readl(host, DWMCI_RINTSTS);
>>>>> + if (mask & INTMSK_CDONE) {
>>>>> + if (!data)
>>>>> + dw_mci_writel(host, mask, DWMCI_RINTSTS);
>>>>> + break;
>>>>> + }
>>>>> + }
>>>>> + /* timeout for command complete. */
>>>>> + if (COMMAND_TIMEOUT == i) {
>>>>> + debug("timeout waiting for status update\n");
>>>>> + return TIMEOUT;
>>>>> + }
>>>>> +
>>>>> + if (mask & INTMSK_RTO) {
>>>>> + if (((cmd->cmdidx == MMC_CMD_SEND_EXT_CSD ||
>>>>> + cmd->cmdidx == MMC_CMD_APP_CMD)) == 0) {
>>>>> + debug("response timeout error: 0x%x cmd: %d\n",
>>>>> + mask, cmd->cmdidx);
>>>>> + }
What do you check with MMC_CMD_SEND_EXT_CSD or MMC_CMD_APP_CMD? Also Debugging?
>>>>> + return TIMEOUT;
>>>>> + } else if (mask & INTMSK_RE) {
>>>>> + debug("response error: 0x%x cmd: %d\n", mask, cmd->cmdidx);
>>>>> + return -1;
>>>>> + }
>>>>> + if (cmd->resp_type & MMC_RSP_PRESENT) {
>>>>> + if (cmd->resp_type & MMC_RSP_136) {
>>>>> + /* CRC is stripped so we need to do some shifting. */
>>>>> + cmd->response[0] = dw_mci_readl(host,
>>>>> + DWMCI_RESP3);
>>>>> + cmd->response[1] = dw_mci_readl(host,
>>>>> + DWMCI_RESP2);
>>>>> + cmd->response[2] = dw_mci_readl(host,
>>>>> + DWMCI_RESP1);
>>>>> + cmd->response[3] = dw_mci_readl(host,
>>>>> + DWMCI_RESP0);
Fix the indent
>>>>> + } else {
>>>>> + cmd->response[0] = dw_mci_readl(host, DWMCI_RESP0);
>>>>> + debug("\tcmd->response[0]: 0x%08x\n", cmd->response[0]);
>>>>> + }
>>>>> + }
>>>>> +
>>>>> + if (data) {
>>>>> + while (!(mask & (DATA_ERR | DATA_TOUT | INTMSK_DTO)))
>>>>> + mask = dw_mci_readl(host, DWMCI_RINTSTS);
>>>>> + dw_mci_writel(host, mask, DWMCI_RINTSTS);
>>>>> + if (data->flags & MMC_DATA_READ) {
>>>>> + data_start = (ulong)data->dest;
>>>>> + data_end = (ulong)data->dest +
>>>>> + data->blocks * data->blocksize;
>>>>> + invalidate_dcache_range(data_start, data_end);
If didn't enable dcache?
>>>>> + }
>>>>> + if (mask & (DATA_ERR | DATA_TOUT)) {
>>>>> + debug("error during transfer: 0x%x\n", mask);
>>>>> + /* make sure disable IDMAC and IDMAC_Interrupts */
>>>>> + dw_mci_writel(host, (dw_mci_readl(host, DWMCI_CONTROL) &
>>>>> + ~(DMA_ENABLE | ENABLE_IDMAC)), DWMCI_CONTROL);
>>>>> + /* mask all interrupt source of IDMAC */
>>>>> + dw_mci_writel(host, 0, DWMCI_IDINTEN);
>>>>> + return -1;
>>>>> + } else if (mask & INTMSK_DTO) {
>>>>> + debug("dwmmc dma interrupt end\n");
>>>>> + } else {
>>>>> + debug("unexpected condition 0x%x\n", mask);
>>>>> + }
>>>>> + /* make sure disable IDMAC and IDMAC_Interrupts */
>>>>> + dw_mci_writel(host, (dw_mci_readl(host, DWMCI_CONTROL) &
>>>>> + ~(DMA_ENABLE | ENABLE_IDMAC)),
>>>>> + DWMCI_CONTROL);
>>>>> + /* mask all interrupt source of IDMAC */
>>>>> + dw_mci_writel(host, 0, DWMCI_IDINTEN);
>>>>> + }
>>>>> +
>>>>> + udelay(100);
>>>>> +
>>>>> + return 0;
>>>>> +}
>>>>> +
>>>>> +/*
>>>>> + * ON/OFF host controller clock
>>>>> + *
>>>>> + * @param host pointer to dw_mci_host
>>>>> + * @param val to enable/disable clock
>>>>> + */
>>>>> +static void dw_mci_clock_onoff(struct dw_mci_host *host, int val)
>>>>> +{
>>>>> +
>>>>> + if (val)
>>>>> + dw_mci_writel(host, CLK_ENABLE, DWMCI_CLKENA);
>>>>> + else
>>>>> + dw_mci_writel(host, CLK_DISABLE, DWMCI_CLKENA);
>>>>> +
>>>>> + dw_mci_writel(host, 0, DWMCI_CMD);
>>>>> + dw_mci_writel(host, CMD_ONLY_CLK, DWMCI_CMD);
>>>>> +}
>>>>> +
>>>>> +/*
>>>>> + * change host controller clock
>>>>> + *
>>>>> + * @param host pointer to dw_mci_host
>>>>> + * @param clock request clock
>>>>> + */
>>>>> +static void dw_mci_change_clock(struct dw_mci_host *host, uint clock)
>>>>> +{
>>>>> + int div;
>>>>> + u32 sclk_mshc;
>>>>> +
>>>>> + if (clock == host->clock)
>>>>> + return;
>>>>> +
>>>>> + /* If Input clock is higher than maximum mshc clock */
>>>>> + if (clock > MAX_DWMMC_CLOCK) {
>>>>> + debug("Input clock is too high\n");
>>>>> + clock = MAX_DWMMC_CLOCK;
>>>>> + }
>>>>> +
>>>>> + /* disable the clock before changing it */
>>>>> + dw_mci_clock_onoff(host, CLK_DISABLE);
>>>>> +
>>>>> + /* get the clock division */
>>>>> + if (host->peripheral == PERIPH_ID_SDMMC4)
>>>>> + sclk_mshc = get_dw_mci_clk_div(host->peripheral) / 2;
>>>>> + else
>>>>> + sclk_mshc = get_dw_mci_clk_div(host->peripheral) / 4;
>>>>> +
>>>>> + /* CLKDIV */
>>>>> + for (div = 1 ; div <= MAXCLKDIV; div++) {
>>>>> + if ((sclk_mshc / (2 * div)) <= clock) {
>>>>> + dw_mci_writel(host, div, DWMCI_CLKDIV);
>>>>> + break;
>>>>> + }
>>>>> + }
>>>>> +
>>>>> + dw_mci_writel(host, 0, DWMCI_CMD);
>>>>> + dw_mci_writel(host, CMD_ONLY_CLK, DWMCI_CMD);
>>>>> +
>>>>> + dw_mci_writel(host, dw_mci_readl(host, DWMCI_CMD) &
>>>>> + (~CMD_SEND_CLK_ONLY),
>>>>> + DWMCI_CMD);
>>>>> +
>>>>> + dw_mci_clock_onoff(host, CLK_ENABLE);
>>>>> + host->clock = clock;
>>>>> +}
>>>>> +
>>>>> +/*
>>>>> + * Set ios for host controller clock
>>>>> + *
>>>>> + * This sets the card bus width and clksel
>>>>> + */
>>>>> +static void dw_mci_set_ios(struct mmc *mmc)
>>>>> +{
>>>>> + struct dw_mci_host *host = mmc->priv;
Also...
Best Regards,
Jaehoon Chung
>>>>> + int val;
>>>>> +
>>>>> + debug("bus_width: %x, clock: %d\n", mmc->bus_width, mmc->clock);
>>>>> +
>>>>> + if (mmc->clock > 0)
>>>>> + dw_mci_change_clock(host, mmc->clock);
>>>>> +
>>>>> + if (mmc->bus_width == 8)
>>>>> + dw_mci_writel(host, PORT0_CARD_WIDTH8, DWMCI_CTYPE);
>>>>> + else if (mmc->bus_width == 4)
>>>>> + dw_mci_writel(host, PORT0_CARD_WIDTH4, DWMCI_CTYPE);
>>>>> + else
>>>>> + dw_mci_writel(host, PORT0_CARD_WIDTH1, DWMCI_CTYPE);
>>>>> +
>>>>> + val = dw_mci_readl(host, DWMCI_CLKSEL);
>>>>> + if (host->peripheral == PERIPH_ID_SDMMC0)
>>>>> + val |= (SELCLK_SAMPLE_1PHASE_Shift | SELCLK_DRV_3PHASE_SHIFT |
>>>>> + SELCLK_DIV_RATIO);
>>>>> + if (host->peripheral == PERIPH_ID_SDMMC2)
>>>>> + val |= (SELCLK_SAMPLE_1PHASE_Shift | SELCLK_DRV_2PHASE_SHIFT |
>>>>> + SELCLK_DIV_RATIO);
>>>>> + if (host->peripheral == PERIPH_ID_SDMMC4)
>>>>> + val |= (SELCLK_SAMPLE_1PHASE_Shift | SELCLK_DRV_2PHASE_SHIFT);
>>>>> +
>>>>> + dw_mci_writel(host, val, DWMCI_CLKSEL);
>>>>> +}
>>>>> +
>>>>> +/*
>>>>> + * Fifo init for host controller
>>>>> + */
>>>>> +static void dw_mci_fifo_init(struct dw_mci_host *host)
>>>>> +{
>>>>> + int fifo_val, fifo_depth, fifo_threshold;
>>>>> +
>>>>> + fifo_val = dw_mci_readl(host, DWMCI_FIFOTH);
>>>>> +
>>>>> + /* Power-on value of RX_WMark is FIFO_DEPTH-1 */
>>>>> + fifo_depth = 1 + ((fifo_val >> 16) & 0x7ff);
>>>>> + fifo_threshold = fifo_depth / 2;
>>>>> +
>>>>> + fifo_val &= ~(RX_WMARK | TX_WMARK | MSIZE_MASK);
>>>>> + fifo_val |= (fifo_threshold | (fifo_threshold << 16) | MSIZE_8);
>>>>> + dw_mci_writel(host, fifo_val, DWMCI_FIFOTH);
>>>>> +}
>>>>> +
>>>>> +
>>>>> +static int dw_mci_reset(struct dw_mci_host *host)
>>>>> +{
>>>>> + int err;
>>>>> +
>>>>> + /* power on the card */
>>>>> + dw_mci_writel(host, POWER_ENABLE, DWMCI_PWREN);
>>>>> +
>>>>> + err = dw_mci_reset_all(host);
>>>>> + if (err)
>>>>> + return err;
>>>>> +
>>>>> + dw_mci_fifo_init(host);
>>>>> +
>>>>> + /* clear all pending interrupts */
>>>>> + dw_mci_writel(host, INTMSK_ALL, DWMCI_RINTSTS);
>>>>> +
>>>>> + /* interrupts are not used, disable all */
>>>>> + dw_mci_writel(host, 0, DWMCI_INTMASK);
>>>>> +
>>>>> + return 0;
>>>>> +}
>>>>> +
>>>>> +static int dw_mci_initialize(struct mmc *mmc)
>>>>> +{
>>>>> + struct dw_mci_host *host = (struct dw_mci_host *)mmc->priv;
>>>>> + unsigned int ier;
>>>>> + int err;
>>>>> +
>>>>> + err = dw_mci_reset(host);
>>>>> + if (err)
>>>>> + return err;
>>>>> +
>>>>> + /* enumerate at 400KHz */
>>>>> + dw_mci_change_clock(host, MIN_DWMMC_CLOCK);
>>>>> +
>>>>> + /* set auto stop command */
>>>>> + ier = dw_mci_readl(host, DWMCI_CONTROL);
>>>>> + ier |= SEND_AS_CCSD;
>>>>> + dw_mci_writel(host, ier, DWMCI_CONTROL);
>>>>> +
>>>>> + /* set 1bit card mode */
>>>>> + dw_mci_writel(host, PORT0_CARD_WIDTH1, DWMCI_CTYPE);
>>>>> +
>>>>> + dw_mci_writel(host, 0xfffff, DWMCI_DEBENCE);
>>>>> +
>>>>> + /* set bus mode register for IDMAC */
>>>>> + dw_mci_writel(host, BMOD_IDMAC_RESET, DWMCI_BMOD);
>>>>> +
>>>>> + dw_mci_writel(host, 0x0, DWMCI_IDINTEN);
>>>>> +
>>>>> + /* set the max timeout for data and response */
>>>>> + dw_mci_writel(host, TMOUT_MAX, DWMCI_TMOUT);
>>>>> +
>>>>> + return 0;
>>>>> +}
>>>>> +
>>>>> +int dw_mci_init(enum periph_id periph_id, int bus_width)
>>>>> +{
>>>>> + struct dw_mci_host *mmc_host;
>>>>> + struct mmc *mmc;
>>>>> +
>>>>> + if (num_devs == MAX_MMC_HOSTS) {
>>>>> + debug("%s: Too many hosts\n", __func__);
>>>>> + return -1;
>>>>> + }
>>>>> +
>>>>> + /* set the clock for dwmmc controller */
>>>>> + if (set_dw_mci_clk_div(periph_id)) {
>>>>> + debug("clock_set_dw_mci failed\n");
>>>>> + return -EINVAL;
>>>>> + }
>>>>> +
>>>>> + mmc = &dw_mci_dev[num_devs];
>>>>> + mmc_host = &dw_mci_host[num_devs];
>>>>> +
>>>>> + sprintf(mmc->name, "DWMMC%d", num_devs);
>>>>> + num_devs++;
>>>>> +
>>>>> + mmc->priv = mmc_host;
>>>>> + mmc->send_cmd = dw_mci_send_command;
>>>>> + mmc->set_ios = dw_mci_set_ios;
>>>>> + mmc->init = dw_mci_initialize;
>>>>> +
>>>>> + /*
>>>>> + * In 2.40a spec, Data offset is changed.
>>>>> + * Need to check the version-id and set data-offset for DATA register.
>>>>> + */
>>>>> + mmc_host->verid = GET_VERID(dw_mci_readl(mmc_host, DWMCI_VERID));
>>>>> + debug("Version ID is %04x\n", mmc_host->verid);
>>>>> +
>>>>> + if (mmc_host->verid < DW_MMC_240A)
>>>>> + mmc_host->data_offset = DATA_OFFSET;
>>>>> + else
>>>>> + mmc_host->data_offset = DATA_240A_OFFSET;
>>>>> +
>>>>> + mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
>>>>> + mmc->host_caps = MMC_MODE_HS_52MHz | MMC_MODE_HS | MMC_MODE_HC;
>>>>> +
>>>>> + if (bus_width == 8)
>>>>> + mmc->host_caps |= MMC_MODE_8BIT;
>>>>> + else
>>>>> + mmc->host_caps |= MMC_MODE_4BIT;
>>>>> +
>>>>> + mmc->f_min = MIN_DWMMC_CLOCK;
>>>>> + mmc->f_max = MAX_DWMMC_CLOCK;
>>>>> +
>>>>> + exynos_pinmux_config(periph_id,
>>>>> + bus_width == 8 ? PINMUX_FLAG_8BIT_MODE : 0);
>>>>> +
>>>>> + mmc_host->clock = 0;
>>>>> + mmc_host->peripheral = periph_id;
>>>>> + mmc_host->ioaddr = (void *)samsung_get_base_dwmmc();
>>>>> + mmc->b_max = 1;
>>>>> + mmc_register(mmc);
>>>>> + mmc->block_dev.removable = 1;
>>>>> + debug("dwmmc: periph_id=%d, width=%d, ioaddr=%p\n",
>>>>> + periph_id, bus_width, mmc_host->ioaddr);
>>>>> +
>>>>> + return 0;
>>>>> +}
>>>>> --
>>>>> 1.7.4.4
>>>>>
>>>>> _______________________________________________
>>>>> U-Boot mailing list
>>>>> U-Boot at lists.denx.de
>>>>> http://lists.denx.de/mailman/listinfo/u-boot
>>>> _______________________________________________
>>>> U-Boot mailing list
>>>> U-Boot at lists.denx.de
>>>> http://lists.denx.de/mailman/listinfo/u-boot
>>>
>>>
>>>
>>
>>
> _______________________________________________
> U-Boot mailing list
> U-Boot at lists.denx.de
> http://lists.denx.de/mailman/listinfo/u-boot
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* [U-Boot] [PATCH V2] MMC: DWMMC: Add DWMMC driver
2012-06-14 13:36 ` Jaehoon Chung
@ 2012-06-15 11:15 ` Rajeshwari Birje
2012-08-30 16:45 ` Andy Fleming
1 sibling, 0 replies; 9+ messages in thread
From: Rajeshwari Birje @ 2012-06-15 11:15 UTC (permalink / raw)
To: u-boot
Hi Jaehoon Chung,
Thank you for comments.
On Thu, Jun 14, 2012 at 7:06 PM, Jaehoon Chung <jh80.chung@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@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@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;
>
> 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
>>
>
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* [U-Boot] [PATCH V2] MMC: DWMMC: Add DWMMC driver
2012-06-14 13:36 ` Jaehoon Chung
2012-06-15 11:15 ` Rajeshwari Birje
@ 2012-08-30 16:45 ` Andy Fleming
1 sibling, 0 replies; 9+ messages in thread
From: Andy Fleming @ 2012-08-30 16:45 UTC (permalink / raw)
To: u-boot
On Thu, Jun 14, 2012 at 6:36 AM, Jaehoon Chung <jh80.chung@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)
>
> And Added some comment
[...]
Everyone, please remove any unnecessary context when you reply to
patches (or any email). When I attempt to read your review comments in
patchworks, I have to scroll for pages, carefully, to find them. Just
quote enough of the patch so everyone can understand what code your
comments are about.
>>>>>> +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;
Casting a void * is unnecessary, and pollutes the code with redundant
information. The purpose of requiring explicit casts is to make sure
one doesn't make an unintended implicit cast between two different
types. But a void * is intentionally typeless, and the explicit cast
provides no extra checking.
Andy
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2012-08-30 16:45 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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
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
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox