
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@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot