MMC: DWMMC: Add DWMMC driver
Subsystems (3)
PATCH v2 1/1 MMC: DWMMC: Add DWMMC driver
Add DWMMC driver support and resgister description for same.
---
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
arch/arm/include/asm/arch-exynos/exynos_dwmmc.h new file +229 -0
@@ -0,0 +1,229 @@ +/* +* (C) Copyright 2012 SAMSUNG Electronics +* Abhilash Kesavan <[email protected]> +* +* 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 */
drivers/mmc/Makefile +1 -0
@@ -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
drivers/mmc/exynos_dwmmc.c new file +566 -0
566 lines not on this page · raw message
Signature · 1 line
1.7.4.4
PATCH v2 1/1 MMC: DWMMC: Add DWMMC driver
Hi All,
ccing Jaehoon Chung
Regards,
Rajeshwari Shinde.
Quoted 846 lines · Rajeshwari Shinde
Add DWMMC driver support and resgister description for same.
Signed-off-by Alim Akhtar <[email protected]>Signed-off-by Terry Lambert <[email protected]>Signed-off-by Rajeshwari Shinde <[email protected]>---
Changes in V2:
? ? ? ?- Incorporated comments from Jaehung Chung.
? ? ? ?- Renamed MSHCI to DWMMC through out the driver.
? ? ? ?- Renamed files toexynos_dwmmcfromexynos_mshc.
? ? ? ?- Removed major hard codings of values.
? ? ? ?- Wrotedw_mci_writelanddw_mci_readlfunctions 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 100644drivers/mmc/exynos_dwmmc.carch/arm/include/asm/arch-exynos/exynos_dwmmc.h new file +229
-0@@ -0,0 +1,229 @@ +/* +* (C) Copyright 2012 SAMSUNG Electronics +* Abhilash Kesavan <[email protected]> +* +* 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 */ drivers/mmc/Makefile +0
-0@@ -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.odrivers/mmc/exynos_dwmmc.c new file +566
-1@@ -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
PATCH v2 1/1 MMC: DWMMC: Add DWMMC driver
Hi,
Quoted 72 lines · Rajeshwari Birje
Hi ?All,
ccing Jaehoon Chung
Regards,
Rajeshwari Shinde.Quoted 62 lines · Rajeshwari Shinde
Add DWMMC driver support and resgister description for same.
Signed-off-by Alim Akhtar <[email protected]>Signed-off-by Terry Lambert <[email protected]>Signed-off-by Rajeshwari Shinde <[email protected]>---
Changes in V2:
? ? ? ?- Incorporated comments from Jaehung Chung.
? ? ? ?- Renamed MSHCI to DWMMC through out the driver.
? ? ? ?- Renamed files toexynos_dwmmcfromexynos_mshc.
? ? ? ?- Removed major hard codings of values.
? ? ? ?- Wrotedw_mci_writelanddw_mci_readlfunctions 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 100644drivers/mmc/exynos_dwmmc.carch/arm/include/asm/arch-exynos/exynos_dwmmc.h new file +36
-0@@ -0,0 +1,229 @@ +/* +* (C) Copyright 2012 SAMSUNG Electronics +* Abhilash Kesavan <[email protected]> +* +* 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
Quoted 8 lines
arch/arm/include/asm/arch-exynos/exynos_dwmmc.h line 44
Quoted 8 lines
arch/arm/include/asm/arch-exynos/exynos_dwmmc.h new file +8
-0+*/ +struct dw_mci_idmac { + ? ? ? u32 ? ? des0; + ? ? ? u32 ? ? des1; + ? ? ? u32 ? ? des2; + ? ? ? u32 ? ? des3; +}; +
#endif
Quoted 176 lines
Quoted 176 lines
arch/arm/include/asm/arch-exynos/exynos_dwmmc.h new file +176
-0+/* ?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); +} +
Quoted 8 lines
arch/arm/include/asm/arch-exynos/exynos_dwmmc.h line 228
Quoted 8 lines
arch/arm/include/asm/arch-exynos/exynos_dwmmc.h new file +8
-0+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.
Quoted 596 lines
Quoted 588 lines
arch/arm/include/asm/arch-exynos/exynos_dwmmc.h new file +1
-0+#endif /* __ASM_ARCH_COMMON_DWMMC_H */ drivers/mmc/Makefile +0
-0@@ -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.odrivers/mmc/exynos_dwmmc.c new file +566
-1@@ -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
Quoted 4 lines
_______________________________________________
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
Signature · 2 lines
with warm regards, Chander Kashyap
PATCH v2 1/1 MMC: DWMMC: Add DWMMC driver
Hi Rajeshwari,
Before applied this patch, it must apply your patch for PINMUX. right?
Best Regards,
Jaehoon Chung
Quoted 868 lines · Chander Kashyap
Hi,
Quoted 72 lines · Rajeshwari Birje
Hi All,
ccing Jaehoon Chung
Regards,
Rajeshwari Shinde.Quoted 62 lines · Rajeshwari Shinde
Add DWMMC driver support and resgister description for same.
Signed-off-by Alim Akhtar <[email protected]>Signed-off-by Terry Lambert <[email protected]>Signed-off-by Rajeshwari Shinde <[email protected]>---
Changes in V2:
- Incorporated comments from Jaehung Chung.
- Renamed MSHCI to DWMMC through out the driver.
- Renamed files toexynos_dwmmcfromexynos_mshc.
- Removed major hard codings of values.
- Wrotedw_mci_writelanddw_mci_readlfunctions 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.carch/arm/include/asm/arch-exynos/exynos_dwmmc.h new file +36
-0@@ -0,0 +1,229 @@ +/* +* (C) Copyright 2012 SAMSUNG Electronics +* Abhilash Kesavan <[email protected]> +* +* 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 Quoted 8 lines
arch/arm/include/asm/arch-exynos/exynos_dwmmc.h line 44
Quoted 8 lines
arch/arm/include/asm/arch-exynos/exynos_dwmmc.h new file +8
-0+*/ +struct dw_mci_idmac { +u32 des0; +u32 des1; +u32 des2; +u32 des3; +}; +#endif
Quoted 176 lines
Quoted 176 lines
arch/arm/include/asm/arch-exynos/exynos_dwmmc.h new file +176
-0+/* 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); +} +Quoted 8 lines
arch/arm/include/asm/arch-exynos/exynos_dwmmc.h line 228
Quoted 8 lines
arch/arm/include/asm/arch-exynos/exynos_dwmmc.h new file +8
-0+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.
Quoted 596 lines
Quoted 588 lines
arch/arm/include/asm/arch-exynos/exynos_dwmmc.h new file +1
-0+#endif /* __ASM_ARCH_COMMON_DWMMC_H */ drivers/mmc/Makefile +1
-0@@ -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.odrivers/mmc/exynos_dwmmc.c new file +566
-1@@ -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
Quoted 4 lines
_______________________________________________
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
PATCH v2 1/1 MMC: DWMMC: Add DWMMC driver
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.
Quoted 879 lines · Jaehoon Chung
Hi Rajeshwari,
Before applied this patch, it must apply your patch for PINMUX. right?
Best Regards,
Jaehoon ChungQuoted 868 lines · Chander Kashyap
Hi,
Quoted 72 lines · Rajeshwari Birje
Hi ?All,
ccing Jaehoon Chung
Regards,
Rajeshwari Shinde.Quoted 62 lines · Rajeshwari Shinde
Add DWMMC driver support and resgister description for same.
Signed-off-by Alim Akhtar <[email protected]>Signed-off-by Terry Lambert <[email protected]>Signed-off-by Rajeshwari Shinde <[email protected]>---
Changes in V2:
? ? ? ?- Incorporated comments from Jaehung Chung.
? ? ? ?- Renamed MSHCI to DWMMC through out the driver.
? ? ? ?- Renamed files toexynos_dwmmcfromexynos_mshc.
? ? ? ?- Removed major hard codings of values.
? ? ? ?- Wrotedw_mci_writelanddw_mci_readlfunctions 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 100644drivers/mmc/exynos_dwmmc.carch/arm/include/asm/arch-exynos/exynos_dwmmc.h new file +36
-0@@ -0,0 +1,229 @@ +/* +* (C) Copyright 2012 SAMSUNG Electronics +* Abhilash Kesavan <[email protected]> +* +* 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 Quoted 8 lines
arch/arm/include/asm/arch-exynos/exynos_dwmmc.h line 44
Quoted 8 lines
arch/arm/include/asm/arch-exynos/exynos_dwmmc.h new file +8
-0+*/ +struct dw_mci_idmac { + ? ? ? u32 ? ? des0; + ? ? ? u32 ? ? des1; + ? ? ? u32 ? ? des2; + ? ? ? u32 ? ? des3; +}; +#endif
Quoted 176 lines
Quoted 176 lines
arch/arm/include/asm/arch-exynos/exynos_dwmmc.h new file +176
-0+/* ?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); +} +Quoted 8 lines
arch/arm/include/asm/arch-exynos/exynos_dwmmc.h line 228
Quoted 8 lines
arch/arm/include/asm/arch-exynos/exynos_dwmmc.h new file +8
-0+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.
Quoted 596 lines
Quoted 588 lines
arch/arm/include/asm/arch-exynos/exynos_dwmmc.h new file +1
-0+#endif /* __ASM_ARCH_COMMON_DWMMC_H */ drivers/mmc/Makefile +0
-0@@ -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.odrivers/mmc/exynos_dwmmc.c new file +566
-1@@ -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
Quoted 4 lines
_______________________________________________
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
PATCH v2 1/1 MMC: DWMMC: Add DWMMC driver
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
Quoted 486 lines · Rajeshwari Birje
Hi Jaehoon Chung,
Yes you need to apply the following patchset
http://comments.gmane.org/gmane.comp.boot-loaders.u-boot/132754Regards,
Rajeshwari Shinde.Quoted 477 lines · Jaehoon Chung
Hi Rajeshwari,
Before applied this patch, it must apply your patch for PINMUX. right?
Best Regards,
Jaehoon ChungQuoted 468 lines · Chander Kashyap
Hi,
Quoted 72 lines · Rajeshwari Birje
Hi All,
ccing Jaehoon Chung
Regards,
Rajeshwari Shinde.Quoted 62 lines · Rajeshwari Shinde
Add DWMMC driver support and resgister description for same.
Signed-off-by Alim Akhtar <[email protected]>Signed-off-by Terry Lambert <[email protected]>Signed-off-by Rajeshwari Shinde <[email protected]>---
Changes in V2:
- Incorporated comments from Jaehung Chung.
- Renamed MSHCI to DWMMC through out the driver.
- Renamed files toexynos_dwmmcfromexynos_mshc.
- Removed major hard codings of values.
- Wrotedw_mci_writelanddw_mci_readlfunctions 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.carch/arm/include/asm/arch-exynos/exynos_dwmmc.h new file +36
-0@@ -0,0 +1,229 @@ +/* +* (C) Copyright 2012 SAMSUNG Electronics +* Abhilash Kesavan <[email protected]> +* +* 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 Quoted 8 lines
arch/arm/include/asm/arch-exynos/exynos_dwmmc.h line 44
Quoted 8 lines
arch/arm/include/asm/arch-exynos/exynos_dwmmc.h new file +8
-0+*/ +struct dw_mci_idmac { +u32 des0; +u32 des1; +u32 des2; +u32 des3; +}; +#endif
Quoted 176 lines
Quoted 176 lines
arch/arm/include/asm/arch-exynos/exynos_dwmmc.h new file +176
-0+/* 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); +} +Quoted 8 lines
arch/arm/include/asm/arch-exynos/exynos_dwmmc.h line 228
Quoted 8 lines
arch/arm/include/asm/arch-exynos/exynos_dwmmc.h new file +8
-0+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.
Quoted 199 lines
Quoted 199 lines
arch/arm/include/asm/arch-exynos/exynos_dwmmc.h new file +1
-0+#endif /* __ASM_ARCH_COMMON_DWMMC_H */ drivers/mmc/Makefile +1
-0@@ -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.odrivers/mmc/exynos_dwmmc.c new file +180
-0@@ -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
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
drivers/mmc/exynos_dwmmc.c line 188
Quoted 8 lines
drivers/mmc/exynos_dwmmc.c new file +8
-0+* @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;
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
drivers/mmc/exynos_dwmmc.c new file +9
-0+ +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 +*/
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
drivers/mmc/exynos_dwmmc.c line 205
Quoted 8 lines
drivers/mmc/exynos_dwmmc.c new file +8
-0+ 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.
Quoted 32 lines
Quoted 32 lines
Quoted 32 lines
Quoted 32 lines
Quoted 32 lines
drivers/mmc/exynos_dwmmc.c new file +32
-0+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 ; + } +
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
drivers/mmc/exynos_dwmmc.c line 245
Quoted 8 lines
drivers/mmc/exynos_dwmmc.c new file +8
-0+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?
Quoted 15 lines
Quoted 15 lines
Quoted 15 lines
Quoted 15 lines
Quoted 15 lines
drivers/mmc/exynos_dwmmc.c new file +15
-0+ +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 ;
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
drivers/mmc/exynos_dwmmc.c line 268
Quoted 8 lines
drivers/mmc/exynos_dwmmc.c new file +8
-0+ } + +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?
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
drivers/mmc/exynos_dwmmc.c new file +8
-0+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. */
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
drivers/mmc/exynos_dwmmc.c line 284
Quoted 8 lines
drivers/mmc/exynos_dwmmc.c new file +8
-0+ 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
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
drivers/mmc/exynos_dwmmc.c new file +7
-0+ }else { + cmd->response[0 ] =dw_mci_readl (host,DWMCI_RESP0 ); +debug ("\tcmd->response[0]: 0x%08x\n" , cmd->response[0 ]); + } + } + +if (data) {
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
drivers/mmc/exynos_dwmmc.c line 299
Quoted 8 lines
drivers/mmc/exynos_dwmmc.c new file +8
-0+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?
Quoted 93 lines
Quoted 93 lines
Quoted 93 lines
Quoted 93 lines
Quoted 93 lines
drivers/mmc/exynos_dwmmc.c new file +93
-0+ } +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; +} +
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
drivers/mmc/exynos_dwmmc.c line 400
Quoted 8 lines
drivers/mmc/exynos_dwmmc.c new file +8
-0+/* +* 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
Quoted 187 lines
Quoted 169 lines
Quoted 169 lines
Quoted 169 lines
Quoted 169 lines
drivers/mmc/exynos_dwmmc.c new file +166
-1+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
Quoted 13 lines
Quoted 11 lines
Quoted 8 lines
Quoted 4 lines
_______________________________________________
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
PATCH v2 1/1 MMC: DWMMC: Add DWMMC driver
Hi Jaehoon Chung,
Thank you for comments.
Quoted 10 lines · Jaehoon Chung
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.candexynos_dw_mmc.c) Likethis...dw_mmc.cis generic code andexynos_dw_mmc.cis 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.
Quoted 492 lines
And Added some comment
Quoted 488 lines · Rajeshwari Birje
Hi Jaehoon Chung,
Yes you need to apply the following patchset
http://comments.gmane.org/gmane.comp.boot-loaders.u-boot/132754Regards,
Rajeshwari Shinde.Quoted 479 lines · Jaehoon Chung
Hi Rajeshwari,
Before applied this patch, it must apply your patch for PINMUX. right?
Best Regards,
Jaehoon ChungQuoted 470 lines · Chander Kashyap
Hi,
Quoted 72 lines · Rajeshwari Birje
Hi ?All,
ccing Jaehoon Chung
Regards,
Rajeshwari Shinde.Quoted 62 lines · Rajeshwari Shinde
Add DWMMC driver support and resgister description for same.
Signed-off-by Alim Akhtar <[email protected]>Signed-off-by Terry Lambert <[email protected]>Signed-off-by Rajeshwari Shinde <[email protected]>---
Changes in V2:
? ? ? ?- Incorporated comments from Jaehung Chung.
? ? ? ?- Renamed MSHCI to DWMMC through out the driver.
? ? ? ?- Renamed files toexynos_dwmmcfromexynos_mshc.
? ? ? ?- Removed major hard codings of values.
? ? ? ?- Wrotedw_mci_writelanddw_mci_readlfunctions 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 100644drivers/mmc/exynos_dwmmc.carch/arm/include/asm/arch-exynos/exynos_dwmmc.h new file +36
-0@@ -0,0 +1,229 @@ +/* +* (C) Copyright 2012 SAMSUNG Electronics +* Abhilash Kesavan <[email protected]> +* +* 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 Quoted 8 lines
arch/arm/include/asm/arch-exynos/exynos_dwmmc.h line 44
Quoted 8 lines
arch/arm/include/asm/arch-exynos/exynos_dwmmc.h new file +8
-0+*/ +struct dw_mci_idmac { + ? ? ? u32 ? ? des0; + ? ? ? u32 ? ? des1; + ? ? ? u32 ? ? des2; + ? ? ? u32 ? ? des3; +}; +#endif
Quoted 176 lines
Quoted 176 lines
arch/arm/include/asm/arch-exynos/exynos_dwmmc.h new file +176
-0+/* ?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); +} +Quoted 8 lines
arch/arm/include/asm/arch-exynos/exynos_dwmmc.h line 228
Quoted 8 lines
arch/arm/include/asm/arch-exynos/exynos_dwmmc.h new file +8
-0+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.
Quoted 201 lines
Quoted 201 lines
arch/arm/include/asm/arch-exynos/exynos_dwmmc.h new file +1
-0+#endif /* __ASM_ARCH_COMMON_DWMMC_H */ drivers/mmc/Makefile +0
-0@@ -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.odrivers/mmc/exynos_dwmmc.c new file +182
-0@@ -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) + *
Quoted 8 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
drivers/mmc/exynos_dwmmc.c
Quoted 6 lines
drivers/mmc/exynos_dwmmc.c new file +6
-0+* @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
Quoted 13 lines
Quoted 13 lines
Quoted 13 lines
Quoted 13 lines
Quoted 13 lines
Quoted 13 lines
drivers/mmc/exynos_dwmmc.c new file +12
-0+ + ? ? ? 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) {
Quoted 9 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
drivers/mmc/exynos_dwmmc.c line 205
Quoted 5 lines
drivers/mmc/exynos_dwmmc.c new file +5
-0+ ? ? ? ? ? ? ? ? ? ? ? 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.
Quoted 34 lines
Quoted 34 lines
Quoted 34 lines
Quoted 34 lines
Quoted 34 lines
Quoted 34 lines
drivers/mmc/exynos_dwmmc.c new file +34
-0+ ? ? ? 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;
Quoted 9 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
drivers/mmc/exynos_dwmmc.c line 245
Quoted 6 lines
drivers/mmc/exynos_dwmmc.c new file +6
-0+ ? ? ? 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.
Quoted 17 lines
Quoted 17 lines
Quoted 17 lines
Quoted 17 lines
Quoted 17 lines
Quoted 17 lines
drivers/mmc/exynos_dwmmc.c new file +17
-0+ + ? ? ? 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; + ? ? ? } +
Quoted 9 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
drivers/mmc/exynos_dwmmc.c line 268
Quoted 6 lines
drivers/mmc/exynos_dwmmc.c new file +6
-0+ ? ? ? 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_CSDorMMC_CMD_APP_CMD? Also Debugging?
-- yes it is added for debugging purpose.
Quoted 17 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
drivers/mmc/exynos_dwmmc.c new file +8
-0+ ? ? ? ? ? ? ? ? ? ? ? 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. */Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
drivers/mmc/exynos_dwmmc.c line 284
Quoted 8 lines
drivers/mmc/exynos_dwmmc.c new file +8
-0+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 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);
Quoted 1 line
Fix the indent
-- will do so
Quoted 17 lines
Quoted 16 lines
Quoted 16 lines
Quoted 16 lines
Quoted 16 lines
Quoted 6 lines
drivers/mmc/exynos_dwmmc.c new file +5
-0+ ? ? ? ? ? ? ? } else { + ? ? ? ? ? ? ? ? ? ? ? cmd->response[0] = dw_mci_readl(host, DWMCI_RESP0); + ? ? ? ? ? ? ? ? ? ? ? debug("\tcmd->response[0]: 0x%08x\n", cmd->response[0]); + ? ? ? ? ? ? ? } + ? ? ? }drivers/mmc/exynos_dwmmc.c line 299
Quoted 10 lines
drivers/mmc/exynos_dwmmc.c new file +10
-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);
Quoted 1 line
If didn't enable dcache?
-- if cache not enabled it has a blank inplementation and will not do anything.
Quoted 96 lines
Quoted 96 lines
Quoted 96 lines
Quoted 96 lines
Quoted 96 lines
Quoted 96 lines
drivers/mmc/exynos_dwmmc.c new file +95
-0+ ? ? ? ? ? ? ? } + ? ? ? ? ? ? ? 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
Quoted 8 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
drivers/mmc/exynos_dwmmc.c
Quoted 6 lines
drivers/mmc/exynos_dwmmc.c new file +6
-0+* +* 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.
Quoted 193 lines
Best Regards,
Jaehoon ChungQuoted 187 lines
Quoted 169 lines
Quoted 169 lines
Quoted 169 lines
Quoted 169 lines
drivers/mmc/exynos_dwmmc.c new file +166
-1+ ? ? ? 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
Quoted 13 lines
Quoted 11 lines
Quoted 8 lines
Quoted 4 lines
_______________________________________________
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
PATCH v2 1/1 MMC: DWMMC: Add DWMMC driver
In reply to Jaehoon ChungQuoted 11 lines · Jaehoon Chung
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.candexynos_dw_mmc.c) Likethis...dw_mmc.cis generic code andexynos_dw_mmc.cis 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.
Quoted 6 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
drivers/mmc/exynos_dwmmc.c line 188
Quoted 4 lines
drivers/mmc/exynos_dwmmc.c new file +4
-0+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
PATCH v2 1/1 MMC: DWMMC: Add DWMMC driver
In reply to Chander KashyapHi Chander,
Thank you for comments will do the correction.
Regards,
Rajeshwari Shinde.
Quoted 871 lines · Chander Kashyap
Hi,
Quoted 72 lines · Rajeshwari Birje
Hi ?All,
ccing Jaehoon Chung
Regards,
Rajeshwari Shinde.Quoted 62 lines · Rajeshwari Shinde
Add DWMMC driver support and resgister description for same.
Signed-off-by Alim Akhtar <[email protected]>Signed-off-by Terry Lambert <[email protected]>Signed-off-by Rajeshwari Shinde <[email protected]>---
Changes in V2:
? ? ? ?- Incorporated comments from Jaehung Chung.
? ? ? ?- Renamed MSHCI to DWMMC through out the driver.
? ? ? ?- Renamed files toexynos_dwmmcfromexynos_mshc.
? ? ? ?- Removed major hard codings of values.
? ? ? ?- Wrotedw_mci_writelanddw_mci_readlfunctions 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 100644drivers/mmc/exynos_dwmmc.carch/arm/include/asm/arch-exynos/exynos_dwmmc.h new file +36
-0@@ -0,0 +1,229 @@ +/* +* (C) Copyright 2012 SAMSUNG Electronics +* Abhilash Kesavan <[email protected]> +* +* 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 Quoted 8 lines
arch/arm/include/asm/arch-exynos/exynos_dwmmc.h line 44
Quoted 8 lines
arch/arm/include/asm/arch-exynos/exynos_dwmmc.h new file +8
-0+*/ +struct dw_mci_idmac { + ? ? ? u32 ? ? des0; + ? ? ? u32 ? ? des1; + ? ? ? u32 ? ? des2; + ? ? ? u32 ? ? des3; +}; +#endif
Quoted 176 lines
Quoted 176 lines
arch/arm/include/asm/arch-exynos/exynos_dwmmc.h new file +176
-0+/* ?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); +} +Quoted 8 lines
arch/arm/include/asm/arch-exynos/exynos_dwmmc.h line 228
Quoted 8 lines
arch/arm/include/asm/arch-exynos/exynos_dwmmc.h new file +8
-0+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.
Quoted 588 lines
Quoted 588 lines
arch/arm/include/asm/arch-exynos/exynos_dwmmc.h new file +1
-0+#endif /* __ASM_ARCH_COMMON_DWMMC_H */ drivers/mmc/Makefile +0
-0@@ -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.odrivers/mmc/exynos_dwmmc.c new file +566
-1@@ -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
Quoted 8 lines
Quoted 4 lines
_______________________________________________
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