Skip to contentSkip to conversation

MMC: DWMMC: Add DWMMC driver

PATCH New Rajeshwari Shinde 5 participants 9 messages Jun 11, 2012 – Aug 30, 2012

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

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
new file mode 100644
index 0000000..349bd75
@@ -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
index c245352..cf0be05 100644
@@ -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
#1 lore raw

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.

---
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
new file mode 100644
index 0000000..349bd75
@@ -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
index c245352..cf0be05 100644
@@ -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 -1
new file mode 100644
index 0000000..96f6ceb
@@ -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

#2 lore raw

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.

---
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 +36 -0
new file mode 100644
index 0000000..349bd75
@@ -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
index c245352..cf0be05 100644
@@ -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 -1
new file mode 100644
index 0000000..96f6ceb
@@ -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
#3 lore raw

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.

---
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 +36 -0
new file mode 100644
index 0000000..349bd75
@@ -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
index c245352..cf0be05 100644
@@ -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 -1
new file mode 100644
index 0000000..96f6ceb
@@ -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

#4 lore raw

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 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.

---
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 +36 -0
new file mode 100644
index 0000000..349bd75
@@ -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
index c245352..cf0be05 100644
@@ -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 -1
new file mode 100644
index 0000000..96f6ceb
@@ -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

#5 lore raw

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/132754

Regards,
Rajeshwari Shinde.

Quoted 477 lines · Jaehoon Chung

Hi Rajeshwari,

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

Best Regards,
Jaehoon Chung

Quoted 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.

---
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 +36 -0
new file mode 100644
index 0000000..349bd75
@@ -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
index c245352..cf0be05 100644
@@ -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 +180 -0
new file mode 100644
index 0000000..96f6ceb
@@ -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

#6 lore raw

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.c and exynos_dw_mmc.c) Like this...dw_mmc.c is generic code and exynos_dw_mmc.c is samsung specific code.. If you want, I will send to you patch that related with them. (based-on your patch)

-- Ok. Will do the change and send the patch for review.

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/132754

Regards,
Rajeshwari Shinde.

Quoted 479 lines · Jaehoon Chung

Hi Rajeshwari,

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

Best Regards,
Jaehoon Chung

Quoted 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.

---
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 +36 -0
new file mode 100644
index 0000000..349bd75
@@ -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
index c245352..cf0be05 100644
@@ -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 +182 -0
new file mode 100644
index 0000000..96f6ceb
@@ -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_CSD or MMC_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 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

#7 lore raw

PATCH v2 1/1 MMC: DWMMC: Add DWMMC driver

In reply to Jaehoon Chung
Quoted 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.c and exynos_dw_mmc.c) Like this...dw_mmc.c is generic code and exynos_dw_mmc.c is samsung specific code.. If you want, I will send to you patch that related with them. (based-on your patch)

And Added some comment

[...]

Everyone, please remove any unnecessary context when you reply to patches (or any email). When I attempt to read your review comments in patchworks, I have to scroll for pages, carefully, to find them. Just quote enough of the patch so everyone can understand what code your comments are about.

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

#8 lore raw

PATCH v2 1/1 MMC: DWMMC: Add DWMMC driver

In reply to Chander Kashyap

Hi 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.

---
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 +36 -0
new file mode 100644
index 0000000..349bd75
@@ -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
index c245352..cf0be05 100644
@@ -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 -1
new file mode 100644
index 0000000..96f6ceb
@@ -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