From 62722036410b7887a04cf6705effb3122fb9b549 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Majewski?= Date: Mon, 12 Mar 2012 22:07:18 +0000 Subject: mmc:fix: Set mmc width according to MMC host capabilities This patch sets the MMC width according to the MMC host capabilities. It turned out, that there are some targets (e.g. GONI), which are able to read data from SPI only at 4 bit mode. This patch restricts the width number according to the MMC host. Signed-off-by: Lukasz Majewski Signed-off-by: Kyungmin Park Cc: Andy Fleming diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 596732e..aebe578 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1199,7 +1199,9 @@ int mmc_startup(struct mmc *mmc) else mmc_set_clock(mmc, 25000000); } else { - for (width = EXT_CSD_BUS_WIDTH_8; width >= 0; width--) { + width = ((mmc->host_caps & MMC_MODE_MASK_WIDTH_BITS) >> + MMC_MODE_WIDTH_BITS_SHIFT); + for (; width >= 0; width--) { /* Set the card to use 4 bit*/ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, width); diff --git a/include/mmc.h b/include/mmc.h index f52df70..ee16349 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -47,6 +47,9 @@ #define MMC_MODE_SPI 0x400 #define MMC_MODE_HC 0x800 +#define MMC_MODE_MASK_WIDTH_BITS (MMC_MODE_4BIT | MMC_MODE_8BIT) +#define MMC_MODE_WIDTH_BITS_SHIFT 8 + #define SD_DATA_4BIT 0x00040000 #define IS_SD(x) (x->version & SD_VERSION_SD) -- cgit v0.10.2 From b1e725f2c6e2d8f9855a0b4cba508a2abe748b4d Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas Date: Mon, 19 Mar 2012 03:50:53 +0000 Subject: mmc: omap: follow TRM procedure to power on cards According to OMAP3 TRM, PBIASLITEPWRDNZ bits must be cleared while MMC power supply is being enabled and is ramping up (those bits might be left set by the previous bootloader). It doesn't say what happens if this procedure is violated, but better not to risk here and do things as required. Signed-off-by: Grazvydas Ignotas Acked-by: Tom Rini diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 2400db2..2b12929 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -62,15 +62,21 @@ static void omap4_vmmc_pbias_config(struct mmc *mmc) unsigned char mmc_board_init(struct mmc *mmc) { -#if defined(CONFIG_TWL4030_POWER) - twl4030_power_mmc_init(); -#endif - #if defined(CONFIG_OMAP34XX) t2_t *t2_base = (t2_t *)T2_BASE; struct prcm *prcm_base = (struct prcm *)PRCM_BASE; + u32 pbias_lite; - writel(readl(&t2_base->pbias_lite) | PBIASLITEPWRDNZ1 | + pbias_lite = readl(&t2_base->pbias_lite); + pbias_lite &= ~(PBIASLITEPWRDNZ1 | PBIASLITEPWRDNZ0); + writel(pbias_lite, &t2_base->pbias_lite); +#endif +#if defined(CONFIG_TWL4030_POWER) + twl4030_power_mmc_init(); + mdelay(100); /* ramp-up delay from Linux code */ +#endif +#if defined(CONFIG_OMAP34XX) + writel(pbias_lite | PBIASLITEPWRDNZ1 | PBIASSPEEDCTRL0 | PBIASLITEPWRDNZ0, &t2_base->pbias_lite); -- cgit v0.10.2 From 15ceb1de81e282426d8988264469508fb5dbd192 Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas Date: Mon, 19 Mar 2012 12:11:43 +0000 Subject: mmc: omap: improve stat wait message The message didn't state that it's waiting for STAT to _clear_, and printing the STAT value itself can help to identify problems. Signed-off-by: Grazvydas Ignotas Acked-by: Tom Rini diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 2b12929..a6bb5fa 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -215,7 +215,8 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, start = get_timer(0); while (readl(&mmc_base->stat)) { if (get_timer(0) - start > MAX_RETRY_MS) { - printf("%s: timedout waiting for stat!\n", __func__); + printf("%s: timedout waiting for STAT (%x) to clear\n", + __func__, readl(&mmc_base->stat)); return TIMEOUT; } } -- cgit v0.10.2 From 25c719e2c3247ef0bab28ee562f978e2ce9211e3 Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas Date: Mon, 19 Mar 2012 12:12:06 +0000 Subject: mmc: omap: handle controller errors properly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to OMAP3 TRM, when the controller reports certain errors, driver must perform a software reset. This is done by setting a bit in SYSCTL and waiting it to clear: - SRC on command timeout (CTO) - SRD on data errors (DTO, DCRC and DEB) This fixes a problem seen on OMAP3 pandora board with some cards that won't work with a message printed multiple times: timedout waiting on cmd inhibit to clear Code loosely based on Linux omap_hsmmc driver. Signed-off-by: Grazvydas Ignotas Tested-by: Tom Rini Tested-by: Pali Rohár diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index a6bb5fa..f2a7a78 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -33,6 +33,10 @@ #include #include +/* common definitions for all OMAPs */ +#define SYSCTL_SRC (1 << 25) +#define SYSCTL_SRD (1 << 26) + /* If we fail after 1 second wait, something is really bad */ #define MAX_RETRY_MS 1000 @@ -195,6 +199,27 @@ static int mmc_init_setup(struct mmc *mmc) return 0; } +/* + * MMC controller internal finite state machine reset + * + * Used to reset command or data internal state machines, using respectively + * SRC or SRD bit of SYSCTL register + */ +static void mmc_reset_controller_fsm(struct hsmmc *mmc_base, u32 bit) +{ + ulong start; + + mmc_reg_out(&mmc_base->sysctl, bit, bit); + + start = get_timer(0); + while ((readl(&mmc_base->sysctl) & bit) != 0) { + if (get_timer(0) - start > MAX_RETRY_MS) { + printf("%s: timedout waiting for sysctl %x to clear\n", + __func__, bit); + return; + } + } +} static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) @@ -284,9 +309,10 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, } } while (!mmc_stat); - if ((mmc_stat & IE_CTO) != 0) + if ((mmc_stat & IE_CTO) != 0) { + mmc_reset_controller_fsm(mmc_base, SYSCTL_SRC); return TIMEOUT; - else if ((mmc_stat & ERRI_MASK) != 0) + } else if ((mmc_stat & ERRI_MASK) != 0) return -1; if (mmc_stat & CC_MASK) { @@ -337,6 +363,9 @@ static int mmc_read_data(struct hsmmc *mmc_base, char *buf, unsigned int size) } } while (mmc_stat == 0); + if ((mmc_stat & (IE_DTO | IE_DCRC | IE_DEB)) != 0) + mmc_reset_controller_fsm(mmc_base, SYSCTL_SRD); + if ((mmc_stat & ERRI_MASK) != 0) return 1; @@ -389,6 +418,9 @@ static int mmc_write_data(struct hsmmc *mmc_base, const char *buf, } } while (mmc_stat == 0); + if ((mmc_stat & (IE_DTO | IE_DCRC | IE_DEB)) != 0) + mmc_reset_controller_fsm(mmc_base, SYSCTL_SRD); + if ((mmc_stat & ERRI_MASK) != 0) return 1; -- cgit v0.10.2 From ffdea5dab902b522f8744234184ffa11a4e275c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Bie=C3=9Fmann?= Date: Mon, 19 Mar 2012 23:13:18 +0000 Subject: include/mmc.h: remove struct mmc_csd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The outdated struct mmc_csd was only used by old atmel_mci driver which was removed in c9abb4260c30fbfd51bb2cd551e7426e2ae15b66. Signed-off-by: Andreas Bießmann cc: Andy Fleming diff --git a/include/mmc.h b/include/mmc.h index ee16349..2305986 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -208,56 +208,6 @@ struct mmc_cid { char pnm[7]; }; -/* - * WARNING! - * - * This structure is used by atmel_mci.c only. - * It works for the AVR32 architecture but NOT - * for ARM/AT91 architectures. - * Its use is highly depreciated. - * After the atmel_mci.c driver for AVR32 has - * been replaced this structure will be removed. - */ -struct mmc_csd -{ - u8 csd_structure:2, - spec_vers:4, - rsvd1:2; - u8 taac; - u8 nsac; - u8 tran_speed; - u16 ccc:12, - read_bl_len:4; - u64 read_bl_partial:1, - write_blk_misalign:1, - read_blk_misalign:1, - dsr_imp:1, - rsvd2:2, - c_size:12, - vdd_r_curr_min:3, - vdd_r_curr_max:3, - vdd_w_curr_min:3, - vdd_w_curr_max:3, - c_size_mult:3, - sector_size:5, - erase_grp_size:5, - wp_grp_size:5, - wp_grp_enable:1, - default_ecc:2, - r2w_factor:3, - write_bl_len:4, - write_bl_partial:1, - rsvd3:5; - u8 file_format_grp:1, - copy:1, - perm_write_protect:1, - tmp_write_protect:1, - file_format:2, - ecc:2; - u8 crc:7; - u8 one:1; -}; - struct mmc_cmd { ushort cmdidx; uint resp_type; -- cgit v0.10.2 From 7a5b80297bc6cef0c10e5f57ac0450678dc7bc5e Mon Sep 17 00:00:00 2001 From: Dirk Behme Date: Mon, 26 Mar 2012 03:13:05 +0000 Subject: mmc: fsl_esdhc: Poll until card is not busy anymore This patch imports parts of two patches from the Freescale U-Boot with the following commit messages: ENGR00156405 ESDHC: Add workaround for auto-clock gate errata ENGcm03648 http://git.freescale.com/git/cgit.cgi/imx/uboot-imx.git/commit/drivers/mmc/imx_esdhc.c?h=imx_v2009.08_12.01.01&id=e436525a70fe47623d346bc7d9f08f12ff8ad787 The errata, not applicable to USDHC, causes ESDHC to shut off clock to the card when auto-clock gating is enabled for commands with busy signalling and no data phase. The card might require the clock to exit the busy state, so the workaround is to disable the auto-clock gate bits in SYSCTL register for such commands. The workaround also entails polling on DAT0 bit in the PRSSTAT register to learn when busy state is complete. Auto-clock gating is re-enabled at the end of busy state. ENGR00156670-1 ESDHC/USDHC: Remove delay before each cmd and some bug fixes http://git.freescale.com/git/cgit.cgi/imx/uboot-imx.git/commit/drivers/mmc/imx_esdhc.c?h=imx_v2009.08_12.01.01&id=a77c6fec8596891be96b2cdbc742c9824844b92a Removed delay of 10 ms before each command. There should not be a need to have this delay after the ENGR00156405 patch that polls until card is not busy anymore before proceeding to next cmd. This patch imports the polling part of both patches. The auto-clock gating code don't apply for i.MX6 as implemented in these two patches. SYSCTL_RSTA was defined twice. Remove one definition. Signed-off-by: Dirk Behme CC: Andy Fleming CC: Fabio Estevam CC: Stefano Babic diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c index a2f35e3..07370b5 100644 --- a/drivers/mmc/fsl_esdhc.c +++ b/drivers/mmc/fsl_esdhc.c @@ -307,19 +307,56 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) #else esdhc_write32(®s->xfertyp, xfertyp); #endif + + /* Mask all irqs */ + esdhc_write32(®s->irqsigen, 0); + /* Wait for the command to complete */ - while (!(esdhc_read32(®s->irqstat) & IRQSTAT_CC)) + while (!(esdhc_read32(®s->irqstat) & (IRQSTAT_CC | IRQSTAT_CTOE))) ; irqstat = esdhc_read32(®s->irqstat); esdhc_write32(®s->irqstat, irqstat); + /* Reset CMD and DATA portions on error */ + if (irqstat & (CMD_ERR | IRQSTAT_CTOE)) { + esdhc_write32(®s->sysctl, esdhc_read32(®s->sysctl) | + SYSCTL_RSTC); + while (esdhc_read32(®s->sysctl) & SYSCTL_RSTC) + ; + + if (data) { + esdhc_write32(®s->sysctl, + esdhc_read32(®s->sysctl) | + SYSCTL_RSTD); + while ((esdhc_read32(®s->sysctl) & SYSCTL_RSTD)) + ; + } + } + if (irqstat & CMD_ERR) return COMM_ERR; if (irqstat & IRQSTAT_CTOE) return TIMEOUT; + /* Workaround for ESDHC errata ENGcm03648 */ + if (!data && (cmd->resp_type & MMC_RSP_BUSY)) { + int timeout = 2500; + + /* Poll on DATA0 line for cmd with busy signal for 250 ms */ + while (timeout > 0 && !(esdhc_read32(®s->prsstat) & + PRSSTAT_DAT0)) { + udelay(100); + timeout--; + } + + if (timeout <= 0) { + printf("Timeout waiting for DAT0 to go high!\n"); + return TIMEOUT; + } + } + /* Copy the response to the response buffer */ if (cmd->resp_type & MMC_RSP_136) { u32 cmdrsp3, cmdrsp2, cmdrsp1, cmdrsp0; diff --git a/include/fsl_esdhc.h b/include/fsl_esdhc.h index 8418bf7..0e26558 100644 --- a/include/fsl_esdhc.h +++ b/include/fsl_esdhc.h @@ -34,12 +34,13 @@ #define SYSCTL_INITA 0x08000000 #define SYSCTL_TIMEOUT_MASK 0x000f0000 #define SYSCTL_CLOCK_MASK 0x0000fff0 -#define SYSCTL_RSTA 0x01000000 #define SYSCTL_CKEN 0x00000008 #define SYSCTL_PEREN 0x00000004 #define SYSCTL_HCKEN 0x00000002 #define SYSCTL_IPGEN 0x00000001 #define SYSCTL_RSTA 0x01000000 +#define SYSCTL_RSTC 0x02000000 +#define SYSCTL_RSTD 0x04000000 #define IRQSTAT 0x0002e030 #define IRQSTAT_DMAE (0x10000000) @@ -85,6 +86,7 @@ #define IRQSTATEN_CC (0x00000001) #define PRSSTAT 0x0002e024 +#define PRSSTAT_DAT0 (0x01000000) #define PRSSTAT_CLSL (0x00800000) #define PRSSTAT_WPSPL (0x00080000) #define PRSSTAT_CDPL (0x00040000) -- cgit v0.10.2 From e7205905e7b9a483c412eefd2aea290a7eee567b Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 8 Apr 2012 18:50:18 +0000 Subject: i.MX28: Lower the amount of blocks transfered in one DMA cycle Some MMC cards, like my ancient 32.0MB SanDisk RS-MMC cards had issue if b_max was set to 0x40 and DMA was enabled. Lower this value to 0x20, which allows these cards to work too. Signed-off-by: Marek Vasut Cc: Stefano Babic Cc: Wolfgang Denk Cc: Detlev Zundel Cc: Fabio Estevam diff --git a/drivers/mmc/mxsmmc.c b/drivers/mmc/mxsmmc.c index 35c6bda..6572e95 100644 --- a/drivers/mmc/mxsmmc.c +++ b/drivers/mmc/mxsmmc.c @@ -406,7 +406,7 @@ int mxsmmc_initialize(bd_t *bis, int id, int (*wp)(int)) */ mmc->f_min = 400000; mmc->f_max = mxc_get_clock(MXC_SSP0_CLK + id) * 1000 / 2; - mmc->b_max = 0x40; + mmc->b_max = 0x20; mmc_register(mmc); return 0; -- cgit v0.10.2 From 3a6383207be3f71b39004e64464a6e99290b16fa Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Mon, 23 Apr 2012 02:36:25 +0000 Subject: mmc: sdhci: add the quirk for broken r1b response When response type is R1b, mask value is added the SDHCI_INT_DAT_END. but in while(), didn't check that flag. So sdhci controller didn't work fine. CMD6 didn't always complete. So add the quirks for broken r1b response and add the timeout value to prevent the infinite loop. Signed-off-by: Jaehoon Chung Signed-off-by: Kyungmin Park Acked-by: Lei Wen diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index fc904b5..7790a1e 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -128,6 +128,7 @@ int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, int trans_bytes = 0, is_aligned = 1; u32 mask, flags, mode; unsigned int timeout, start_addr = 0; + unsigned int retry = 10000; /* Wait max 10 ms */ timeout = 10; @@ -210,8 +211,19 @@ int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, stat = sdhci_readl(host, SDHCI_INT_STATUS); if (stat & SDHCI_INT_ERROR) break; + if (--retry == 0) + break; } while ((stat & mask) != mask); + if (retry == 0) { + if (host->quirks & SDHCI_QUIRK_BROKEN_R1B) + return 0; + else { + printf("Timeout for status update!\n"); + return TIMEOUT; + } + } + if ((stat & (SDHCI_INT_ERROR | mask)) == mask) { sdhci_cmd_done(host, cmd); sdhci_writel(host, mask, SDHCI_INT_STATUS); diff --git a/include/sdhci.h b/include/sdhci.h index 800f9d9..a2415ba 100644 --- a/include/sdhci.h +++ b/include/sdhci.h @@ -216,6 +216,7 @@ */ #define SDHCI_QUIRK_32BIT_DMA_ADDR (1 << 0) #define SDHCI_QUIRK_REG32_RW (1 << 1) +#define SDHCI_QUIRK_BROKEN_R1B (1 << 2) /* to make gcc happy */ struct sdhci_host; -- cgit v0.10.2 From 236bfecff8091356d81e79da3281b9e4af967468 Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Mon, 23 Apr 2012 02:36:26 +0000 Subject: mmc: add the quirk to use the sdhci for samsung-soc To support the Samsung-SoC, added the basically functions. Samsung-SoC didn't used the SDHCI_CTRL_HISPD. And added set_control_reg callback for s3c64xx. Signed-off-by: Jaehoon Chung Signed-off-by: Kyungmin Park Acked-by: Lei Wen diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 7790a1e..1709643 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -337,6 +337,9 @@ void sdhci_set_ios(struct mmc *mmc) u32 ctrl; struct sdhci_host *host = (struct sdhci_host *)mmc->priv; + if (host->set_control_reg) + host->set_control_reg(host); + if (mmc->clock != host->clock) sdhci_set_clock(mmc, mmc->clock); @@ -360,6 +363,9 @@ void sdhci_set_ios(struct mmc *mmc) else ctrl &= ~SDHCI_CTRL_HISPD; + if (host->quirks & SDHCI_QUIRK_NO_HISPD_BIT) + ctrl &= ~SDHCI_CTRL_HISPD; + sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); } @@ -443,9 +449,15 @@ int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk) mmc->voltages |= MMC_VDD_29_30 | MMC_VDD_30_31; if (caps & SDHCI_CAN_VDD_180) mmc->voltages |= MMC_VDD_165_195; + + if (host->quirks & SDHCI_QUIRK_BROKEN_VOLTAGE) + mmc->voltages |= host->voltages; + mmc->host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT; if (caps & SDHCI_CAN_DO_8BIT) mmc->host_caps |= MMC_MODE_8BIT; + if (host->host_caps) + mmc->host_caps |= host->host_caps; sdhci_reset(host, SDHCI_RESET_ALL); mmc_register(mmc); diff --git a/include/sdhci.h b/include/sdhci.h index a2415ba..9d37183 100644 --- a/include/sdhci.h +++ b/include/sdhci.h @@ -217,6 +217,8 @@ #define SDHCI_QUIRK_32BIT_DMA_ADDR (1 << 0) #define SDHCI_QUIRK_REG32_RW (1 << 1) #define SDHCI_QUIRK_BROKEN_R1B (1 << 2) +#define SDHCI_QUIRK_NO_HISPD_BIT (1 << 3) +#define SDHCI_QUIRK_BROKEN_VOLTAGE (1 << 4) /* to make gcc happy */ struct sdhci_host; @@ -241,10 +243,14 @@ struct sdhci_host { char *name; void *ioaddr; unsigned int quirks; + unsigned int host_caps; unsigned int version; unsigned int clock; struct mmc *mmc; const struct sdhci_ops *ops; + + void (*set_control_reg)(struct sdhci_host *host); + uint voltages; }; #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS -- cgit v0.10.2 From 442d55685e1e2310d546044a6519ae73e4ba348a Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Mon, 23 Apr 2012 02:36:28 +0000 Subject: mmc: support the sdhci instead of s5p_mmc for samsung-soc In driver mmc, generic s5p_sdhci code is implemented. s5p_mmc file is dupulicated. we are good that use the generic sdhci. This patch supported the sdhci for Samsung-SoC. Signed-off-by: Jaehoon Chung Signed-off-by: Kyungmin Park Acked-by: Lei Wen Acked-by: Minkyu Kang diff --git a/arch/arm/include/asm/arch-exynos/mmc.h b/arch/arm/include/asm/arch-exynos/mmc.h index 30f82b8..0f701c9 100644 --- a/arch/arm/include/asm/arch-exynos/mmc.h +++ b/arch/arm/include/asm/arch-exynos/mmc.h @@ -21,53 +21,54 @@ #ifndef __ASM_ARCH_MMC_H_ #define __ASM_ARCH_MMC_H_ -#ifndef __ASSEMBLY__ -struct s5p_mmc { - unsigned int sysad; - unsigned short blksize; - unsigned short blkcnt; - unsigned int argument; - unsigned short trnmod; - unsigned short cmdreg; - unsigned int rspreg0; - unsigned int rspreg1; - unsigned int rspreg2; - unsigned int rspreg3; - unsigned int bdata; - unsigned int prnsts; - unsigned char hostctl; - unsigned char pwrcon; - unsigned char blkgap; - unsigned char wakcon; - unsigned short clkcon; - unsigned char timeoutcon; - unsigned char swrst; - unsigned int norintsts; /* errintsts */ - unsigned int norintstsen; /* errintstsen */ - unsigned int norintsigen; /* errintsigen */ - unsigned short acmd12errsts; - unsigned char res1[2]; - unsigned int capareg; - unsigned char res2[4]; - unsigned int maxcurr; - unsigned char res3[0x34]; - unsigned int control2; - unsigned int control3; - unsigned char res4[4]; - unsigned int control4; - unsigned char res5[0x6e]; - unsigned short hcver; - unsigned char res6[0xFF00]; -}; +#define SDHCI_CONTROL2 0x80 +#define SDHCI_CONTROL3 0x84 +#define SDHCI_CONTROL4 0x8C -struct mmc_host { - struct s5p_mmc *reg; - unsigned int version; /* SDHCI spec. version */ - unsigned int clock; /* Current clock (MHz) */ - int dev_index; -}; +#define SDHCI_CTRL2_ENSTAASYNCCLR (1 << 31) +#define SDHCI_CTRL2_ENCMDCNFMSK (1 << 30) +#define SDHCI_CTRL2_CDINVRXD3 (1 << 29) +#define SDHCI_CTRL2_SLCARDOUT (1 << 28) -int s5p_mmc_init(int dev_index, int bus_width); +#define SDHCI_CTRL2_FLTCLKSEL_MASK (0xf << 24) +#define SDHCI_CTRL2_FLTCLKSEL_SHIFT (24) +#define SDHCI_CTRL2_FLTCLKSEL(_x) ((_x) << 24) -#endif /* __ASSEMBLY__ */ +#define SDHCI_CTRL2_LVLDAT_MASK (0xff << 16) +#define SDHCI_CTRL2_LVLDAT_SHIFT (16) +#define SDHCI_CTRL2_LVLDAT(_x) ((_x) << 16) + +#define SDHCI_CTRL2_ENFBCLKTX (1 << 15) +#define SDHCI_CTRL2_ENFBCLKRX (1 << 14) +#define SDHCI_CTRL2_SDCDSEL (1 << 13) +#define SDHCI_CTRL2_SDSIGPC (1 << 12) +#define SDHCI_CTRL2_ENBUSYCHKTXSTART (1 << 11) + +#define SDHCI_CTRL2_DFCNT_MASK(_x) ((_x) << 9) +#define SDHCI_CTRL2_DFCNT_SHIFT (9) + +#define SDHCI_CTRL2_ENCLKOUTHOLD (1 << 8) +#define SDHCI_CTRL2_RWAITMODE (1 << 7) +#define SDHCI_CTRL2_DISBUFRD (1 << 6) +#define SDHCI_CTRL2_SELBASECLK_MASK(_x) ((_x) << 4) +#define SDHCI_CTRL2_SELBASECLK_SHIFT (4) +#define SDHCI_CTRL2_PWRSYNC (1 << 3) +#define SDHCI_CTRL2_ENCLKOUTMSKCON (1 << 1) +#define SDHCI_CTRL2_HWINITFIN (1 << 0) + +#define SDHCI_CTRL3_FCSEL3 (1 << 31) +#define SDHCI_CTRL3_FCSEL2 (1 << 23) +#define SDHCI_CTRL3_FCSEL1 (1 << 15) +#define SDHCI_CTRL3_FCSEL0 (1 << 7) + +#define SDHCI_CTRL4_DRIVE_MASK(_x) ((_x) << 16) +#define SDHCI_CTRL4_DRIVE_SHIFT (16) + +int s5p_sdhci_init(u32 regbase, u32 max_clk, u32 min_clk, u32 quirks); + +static inline unsigned int s5p_mmc_init(int index, int bus_width) +{ + unsigned int base = samsung_get_base_mmc() + (0x10000 * index); + return s5p_sdhci_init(base, 52000000, 400000, index); +} #endif diff --git a/arch/arm/include/asm/arch-s5pc1xx/mmc.h b/arch/arm/include/asm/arch-s5pc1xx/mmc.h index adef4ee..0f701c9 100644 --- a/arch/arm/include/asm/arch-s5pc1xx/mmc.h +++ b/arch/arm/include/asm/arch-s5pc1xx/mmc.h @@ -21,53 +21,54 @@ #ifndef __ASM_ARCH_MMC_H_ #define __ASM_ARCH_MMC_H_ -#ifndef __ASSEMBLY__ -struct s5p_mmc { - unsigned int sysad; - unsigned short blksize; - unsigned short blkcnt; - unsigned int argument; - unsigned short trnmod; - unsigned short cmdreg; - unsigned int rspreg0; - unsigned int rspreg1; - unsigned int rspreg2; - unsigned int rspreg3; - unsigned int bdata; - unsigned int prnsts; - unsigned char hostctl; - unsigned char pwrcon; - unsigned char blkgap; - unsigned char wakcon; - unsigned short clkcon; - unsigned char timeoutcon; - unsigned char swrst; - unsigned int norintsts; /* errintsts */ - unsigned int norintstsen; /* errintstsen */ - unsigned int norintsigen; /* errintsigen */ - unsigned short acmd12errsts; - unsigned char res1[2]; - unsigned int capareg; - unsigned char res2[4]; - unsigned int maxcurr; - unsigned char res3[0x34]; - unsigned int control2; - unsigned int control3; - unsigned char res4[4]; - unsigned int control4; - unsigned char res5[0x6e]; - unsigned short hcver; - unsigned char res6[0xFFF00]; -}; +#define SDHCI_CONTROL2 0x80 +#define SDHCI_CONTROL3 0x84 +#define SDHCI_CONTROL4 0x8C -struct mmc_host { - struct s5p_mmc *reg; - unsigned int version; /* SDHCI spec. version */ - unsigned int clock; /* Current clock (MHz) */ - int dev_index; -}; +#define SDHCI_CTRL2_ENSTAASYNCCLR (1 << 31) +#define SDHCI_CTRL2_ENCMDCNFMSK (1 << 30) +#define SDHCI_CTRL2_CDINVRXD3 (1 << 29) +#define SDHCI_CTRL2_SLCARDOUT (1 << 28) -int s5p_mmc_init(int dev_index, int bus_width); +#define SDHCI_CTRL2_FLTCLKSEL_MASK (0xf << 24) +#define SDHCI_CTRL2_FLTCLKSEL_SHIFT (24) +#define SDHCI_CTRL2_FLTCLKSEL(_x) ((_x) << 24) -#endif /* __ASSEMBLY__ */ +#define SDHCI_CTRL2_LVLDAT_MASK (0xff << 16) +#define SDHCI_CTRL2_LVLDAT_SHIFT (16) +#define SDHCI_CTRL2_LVLDAT(_x) ((_x) << 16) + +#define SDHCI_CTRL2_ENFBCLKTX (1 << 15) +#define SDHCI_CTRL2_ENFBCLKRX (1 << 14) +#define SDHCI_CTRL2_SDCDSEL (1 << 13) +#define SDHCI_CTRL2_SDSIGPC (1 << 12) +#define SDHCI_CTRL2_ENBUSYCHKTXSTART (1 << 11) + +#define SDHCI_CTRL2_DFCNT_MASK(_x) ((_x) << 9) +#define SDHCI_CTRL2_DFCNT_SHIFT (9) + +#define SDHCI_CTRL2_ENCLKOUTHOLD (1 << 8) +#define SDHCI_CTRL2_RWAITMODE (1 << 7) +#define SDHCI_CTRL2_DISBUFRD (1 << 6) +#define SDHCI_CTRL2_SELBASECLK_MASK(_x) ((_x) << 4) +#define SDHCI_CTRL2_SELBASECLK_SHIFT (4) +#define SDHCI_CTRL2_PWRSYNC (1 << 3) +#define SDHCI_CTRL2_ENCLKOUTMSKCON (1 << 1) +#define SDHCI_CTRL2_HWINITFIN (1 << 0) + +#define SDHCI_CTRL3_FCSEL3 (1 << 31) +#define SDHCI_CTRL3_FCSEL2 (1 << 23) +#define SDHCI_CTRL3_FCSEL1 (1 << 15) +#define SDHCI_CTRL3_FCSEL0 (1 << 7) + +#define SDHCI_CTRL4_DRIVE_MASK(_x) ((_x) << 16) +#define SDHCI_CTRL4_DRIVE_SHIFT (16) + +int s5p_sdhci_init(u32 regbase, u32 max_clk, u32 min_clk, u32 quirks); + +static inline unsigned int s5p_mmc_init(int index, int bus_width) +{ + unsigned int base = samsung_get_base_mmc() + (0x10000 * index); + return s5p_sdhci_init(base, 52000000, 400000, index); +} #endif diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index c245352..a8e681c 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -39,8 +39,8 @@ COBJS-$(CONFIG_MXS_MMC) += mxsmmc.o COBJS-$(CONFIG_OMAP_HSMMC) += omap_hsmmc.o COBJS-$(CONFIG_PXA_MMC) += pxa_mmc.o COBJS-$(CONFIG_PXA_MMC_GENERIC) += pxa_mmc_gen.o -COBJS-$(CONFIG_S5P_MMC) += s5p_mmc.o COBJS-$(CONFIG_SDHCI) += sdhci.o +COBJS-$(CONFIG_S5P_SDHCI) += s5p_sdhci.o COBJS-$(CONFIG_SH_MMCIF) += sh_mmcif.o COBJS-$(CONFIG_TEGRA2_MMC) += tegra2_mmc.o diff --git a/drivers/mmc/s5p_mmc.c b/drivers/mmc/s5p_mmc.c deleted file mode 100644 index 4ae3aaf..0000000 --- a/drivers/mmc/s5p_mmc.c +++ /dev/null @@ -1,490 +0,0 @@ -/* - * (C) Copyright 2009 SAMSUNG Electronics - * Minkyu Kang - * Jaehoon Chung - * - * 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 -#include -#include -#include -#include - -/* support 4 mmc hosts */ -struct mmc mmc_dev[4]; -struct mmc_host mmc_host[4]; - -static inline struct s5p_mmc *s5p_get_base_mmc(int dev_index) -{ - unsigned long offset = dev_index * sizeof(struct s5p_mmc); - return (struct s5p_mmc *)(samsung_get_base_mmc() + offset); -} - -static void mmc_prepare_data(struct mmc_host *host, struct mmc_data *data) -{ - unsigned char ctrl; - - debug("data->dest: %08x\n", (u32)data->dest); - writel((u32)data->dest, &host->reg->sysad); - /* - * DMASEL[4:3] - * 00 = Selects SDMA - * 01 = Reserved - * 10 = Selects 32-bit Address ADMA2 - * 11 = Selects 64-bit Address ADMA2 - */ - ctrl = readb(&host->reg->hostctl); - ctrl &= ~(3 << 3); - writeb(ctrl, &host->reg->hostctl); - - /* We do not handle DMA boundaries, so set it to max (512 KiB) */ - writew((7 << 12) | (data->blocksize & 0xFFF), &host->reg->blksize); - writew(data->blocks, &host->reg->blkcnt); -} - -static void mmc_set_transfer_mode(struct mmc_host *host, struct mmc_data *data) -{ - unsigned short mode; - - /* - * TRNMOD - * MUL1SIN0[5] : Multi/Single Block Select - * RD1WT0[4] : Data Transfer Direction Select - * 1 = read - * 0 = write - * ENACMD12[2] : Auto CMD12 Enable - * ENBLKCNT[1] : Block Count Enable - * ENDMA[0] : DMA Enable - */ - mode = (1 << 1) | (1 << 0); - if (data->blocks > 1) - mode |= (1 << 5); - if (data->flags & MMC_DATA_READ) - mode |= (1 << 4); - - writew(mode, &host->reg->trnmod); -} - -static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, - struct mmc_data *data) -{ - struct mmc_host *host = (struct mmc_host *)mmc->priv; - int flags, i; - unsigned int timeout; - unsigned int mask; - unsigned int retry = 0x100000; - - /* Wait max 10 ms */ - timeout = 10; - - /* - * PRNSTS - * CMDINHDAT[1] : Command Inhibit (DAT) - * CMDINHCMD[0] : Command Inhibit (CMD) - */ - mask = (1 << 0); - if ((data != NULL) || (cmd->resp_type & MMC_RSP_BUSY)) - mask |= (1 << 1); - - /* - * We shouldn't wait for data inihibit for stop commands, even - * though they might use busy signaling - */ - if (data) - mask &= ~(1 << 1); - - while (readl(&host->reg->prnsts) & mask) { - if (timeout == 0) { - printf("%s: timeout error\n", __func__); - return -1; - } - timeout--; - udelay(1000); - } - - if (data) - mmc_prepare_data(host, data); - - debug("cmd->arg: %08x\n", cmd->cmdarg); - writel(cmd->cmdarg, &host->reg->argument); - - if (data) - mmc_set_transfer_mode(host, data); - - if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY)) - return -1; - - /* - * CMDREG - * CMDIDX[13:8] : Command index - * DATAPRNT[5] : Data Present Select - * ENCMDIDX[4] : Command Index Check Enable - * ENCMDCRC[3] : Command CRC Check Enable - * RSPTYP[1:0] - * 00 = No Response - * 01 = Length 136 - * 10 = Length 48 - * 11 = Length 48 Check busy after response - */ - if (!(cmd->resp_type & MMC_RSP_PRESENT)) - flags = 0; - else if (cmd->resp_type & MMC_RSP_136) - flags = (1 << 0); - else if (cmd->resp_type & MMC_RSP_BUSY) - flags = (3 << 0); - else - flags = (2 << 0); - - if (cmd->resp_type & MMC_RSP_CRC) - flags |= (1 << 3); - if (cmd->resp_type & MMC_RSP_OPCODE) - flags |= (1 << 4); - if (data) - flags |= (1 << 5); - - debug("cmd: %d\n", cmd->cmdidx); - - writew((cmd->cmdidx << 8) | flags, &host->reg->cmdreg); - - for (i = 0; i < retry; i++) { - mask = readl(&host->reg->norintsts); - /* Command Complete */ - if (mask & (1 << 0)) { - if (!data) - writel(mask, &host->reg->norintsts); - break; - } - } - - if (i == retry) { - printf("%s: waiting for status update\n", __func__); - return TIMEOUT; - } - - if (mask & (1 << 16)) { - /* Timeout Error */ - debug("timeout: %08x cmd %d\n", mask, cmd->cmdidx); - return TIMEOUT; - } else if (mask & (1 << 15)) { - /* Error Interrupt */ - debug("error: %08x 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. */ - for (i = 0; i < 4; i++) { - unsigned int offset = - (unsigned int)(&host->reg->rspreg3 - i); - cmd->response[i] = readl(offset) << 8; - - if (i != 3) { - cmd->response[i] |= - readb(offset - 1); - } - debug("cmd->resp[%d]: %08x\n", - i, cmd->response[i]); - } - } else if (cmd->resp_type & MMC_RSP_BUSY) { - for (i = 0; i < retry; i++) { - /* PRNTDATA[23:20] : DAT[3:0] Line Signal */ - if (readl(&host->reg->prnsts) - & (1 << 20)) /* DAT[0] */ - break; - } - - if (i == retry) { - printf("%s: card is still busy\n", __func__); - return TIMEOUT; - } - - cmd->response[0] = readl(&host->reg->rspreg0); - debug("cmd->resp[0]: %08x\n", cmd->response[0]); - } else { - cmd->response[0] = readl(&host->reg->rspreg0); - debug("cmd->resp[0]: %08x\n", cmd->response[0]); - } - } - - if (data) { - while (1) { - mask = readl(&host->reg->norintsts); - - if (mask & (1 << 15)) { - /* Error Interrupt */ - writel(mask, &host->reg->norintsts); - printf("%s: error during transfer: 0x%08x\n", - __func__, mask); - return -1; - } else if (mask & (1 << 3)) { - /* - * DMA Interrupt, restart the transfer where - * it was interrupted. - */ - unsigned int address = readl(&host->reg->sysad); - - debug("DMA end\n"); - writel((1 << 3), &host->reg->norintsts); - writel(address, &host->reg->sysad); - } else if (mask & (1 << 1)) { - /* Transfer Complete */ - debug("r/w is done\n"); - break; - } - } - writel(mask, &host->reg->norintsts); - } - - udelay(1000); - return 0; -} - -static void mmc_change_clock(struct mmc_host *host, uint clock) -{ - int div; - unsigned short clk; - unsigned long timeout; - unsigned long ctrl2; - - /* - * SELBASECLK[5:4] - * 00/01 = HCLK - * 10 = EPLL - * 11 = XTI or XEXTCLK - */ - ctrl2 = readl(&host->reg->control2); - ctrl2 &= ~(3 << 4); - ctrl2 |= (2 << 4); - writel(ctrl2, &host->reg->control2); - - writew(0, &host->reg->clkcon); - - /* XXX: we assume that clock is between 40MHz and 50MHz */ - if (clock == 0) - goto out; - else if (clock <= 400000) - div = 0x100; - else if (clock <= 20000000) - div = 4; - else if (clock <= 26000000) - div = 2; - else - div = 1; - debug("div: %d\n", div); - - div >>= 1; - /* - * CLKCON - * SELFREQ[15:8] : base clock divied by value - * ENSDCLK[2] : SD Clock Enable - * STBLINTCLK[1] : Internal Clock Stable - * ENINTCLK[0] : Internal Clock Enable - */ - clk = (div << 8) | (1 << 0); - writew(clk, &host->reg->clkcon); - - set_mmc_clk(host->dev_index, div); - - /* Wait max 10 ms */ - timeout = 10; - while (!(readw(&host->reg->clkcon) & (1 << 1))) { - if (timeout == 0) { - printf("%s: timeout error\n", __func__); - return; - } - timeout--; - udelay(1000); - } - - clk |= (1 << 2); - writew(clk, &host->reg->clkcon); - -out: - host->clock = clock; -} - -static void mmc_set_ios(struct mmc *mmc) -{ - struct mmc_host *host = mmc->priv; - unsigned char ctrl; - unsigned long val; - - debug("bus_width: %x, clock: %d\n", mmc->bus_width, mmc->clock); - - /* - * SELCLKPADDS[17:16] - * 00 = 2mA - * 01 = 4mA - * 10 = 7mA - * 11 = 9mA - */ - writel(0x3 << 16, &host->reg->control4); - - val = readl(&host->reg->control2); - val &= (0x3 << 4); - - val |= (1 << 31) | /* write status clear async mode enable */ - (1 << 30) | /* command conflict mask enable */ - (1 << 14) | /* Feedback Clock Enable for Rx Clock */ - (1 << 8); /* SDCLK hold enable */ - - writel(val, &host->reg->control2); - - /* - * FCSEL1[15] FCSEL0[7] - * FCSel[1:0] : Rx Feedback Clock Delay Control - * Inverter delay means10ns delay if SDCLK 50MHz setting - * 01 = Delay1 (basic delay) - * 11 = Delay2 (basic delay + 2ns) - * 00 = Delay3 (inverter delay) - * 10 = Delay4 (inverter delay + 2ns) - */ - writel(0x8080, &host->reg->control3); - - mmc_change_clock(host, mmc->clock); - - ctrl = readb(&host->reg->hostctl); - - /* - * WIDE8[5] - * 0 = Depend on WIDE4 - * 1 = 8-bit mode - * WIDE4[1] - * 1 = 4-bit mode - * 0 = 1-bit mode - */ - if (mmc->bus_width == 8) - ctrl |= (1 << 5); - else if (mmc->bus_width == 4) - ctrl |= (1 << 1); - else - ctrl &= ~(1 << 1); - - /* - * OUTEDGEINV[2] - * 1 = Riging edge output - * 0 = Falling edge output - */ - ctrl &= ~(1 << 2); - - writeb(ctrl, &host->reg->hostctl); -} - -static void mmc_reset(struct mmc_host *host) -{ - unsigned int timeout; - - /* - * RSTALL[0] : Software reset for all - * 1 = reset - * 0 = work - */ - writeb((1 << 0), &host->reg->swrst); - - host->clock = 0; - - /* Wait max 100 ms */ - timeout = 100; - - /* hw clears the bit when it's done */ - while (readb(&host->reg->swrst) & (1 << 0)) { - if (timeout == 0) { - printf("%s: timeout error\n", __func__); - return; - } - timeout--; - udelay(1000); - } -} - -static int mmc_core_init(struct mmc *mmc) -{ - struct mmc_host *host = (struct mmc_host *)mmc->priv; - unsigned int mask; - - mmc_reset(host); - - host->version = readw(&host->reg->hcver); - - /* mask all */ - writel(0xffffffff, &host->reg->norintstsen); - writel(0xffffffff, &host->reg->norintsigen); - - writeb(0xe, &host->reg->timeoutcon); /* TMCLK * 2^27 */ - - /* - * NORMAL Interrupt Status Enable Register init - * [5] ENSTABUFRDRDY : Buffer Read Ready Status Enable - * [4] ENSTABUFWTRDY : Buffer write Ready Status Enable - * [3] ENSTADMAINT : DMA Interrupt Status Enable - * [1] ENSTASTANSCMPLT : Transfre Complete Status Enable - * [0] ENSTACMDCMPLT : Command Complete Status Enable - */ - mask = readl(&host->reg->norintstsen); - mask &= ~(0xffff); - mask |= (1 << 5) | (1 << 4) | (1 << 3) | (1 << 1) | (1 << 0); - writel(mask, &host->reg->norintstsen); - - /* - * NORMAL Interrupt Signal Enable Register init - * [1] ENSTACMDCMPLT : Transfer Complete Signal Enable - */ - mask = readl(&host->reg->norintsigen); - mask &= ~(0xffff); - mask |= (1 << 1); - writel(mask, &host->reg->norintsigen); - - return 0; -} - -static int s5p_mmc_initialize(int dev_index, int bus_width) -{ - struct mmc *mmc; - - mmc = &mmc_dev[dev_index]; - - sprintf(mmc->name, "SAMSUNG SD/MMC"); - mmc->priv = &mmc_host[dev_index]; - mmc->send_cmd = mmc_send_cmd; - mmc->set_ios = mmc_set_ios; - mmc->init = mmc_core_init; - mmc->getcd = NULL; - - mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; - if (bus_width == 8) - mmc->host_caps = MMC_MODE_8BIT; - else - mmc->host_caps = MMC_MODE_4BIT; - mmc->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS | MMC_MODE_HC; - - mmc->f_min = 400000; - mmc->f_max = 52000000; - - mmc_host[dev_index].dev_index = dev_index; - mmc_host[dev_index].clock = 0; - mmc_host[dev_index].reg = s5p_get_base_mmc(dev_index); - mmc->b_max = 0; - mmc_register(mmc); - - return 0; -} - -int s5p_mmc_init(int dev_index, int bus_width) -{ - return s5p_mmc_initialize(dev_index, bus_width); -} diff --git a/drivers/mmc/s5p_sdhci.c b/drivers/mmc/s5p_sdhci.c new file mode 100644 index 0000000..1d4481b --- /dev/null +++ b/drivers/mmc/s5p_sdhci.c @@ -0,0 +1,98 @@ +/* + * (C) Copyright 2012 SAMSUNG Electronics + * Jaehoon Chung + * + * 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 +#include +#include +#include + +static char *S5P_NAME = "SAMSUNG SDHCI"; +static void s5p_sdhci_set_control_reg(struct sdhci_host *host) +{ + unsigned long val, ctrl; + /* + * SELCLKPADDS[17:16] + * 00 = 2mA + * 01 = 4mA + * 10 = 7mA + * 11 = 9mA + */ + sdhci_writel(host, SDHCI_CTRL4_DRIVE_MASK(0x3), SDHCI_CONTROL4); + + val = sdhci_readl(host, SDHCI_CONTROL2); + val &= SDHCI_CTRL2_SELBASECLK_SHIFT; + + val |= SDHCI_CTRL2_ENSTAASYNCCLR | + SDHCI_CTRL2_ENCMDCNFMSK | + SDHCI_CTRL2_ENFBCLKRX | + SDHCI_CTRL2_ENCLKOUTHOLD; + + sdhci_writel(host, val, SDHCI_CONTROL2); + + /* + * FCSEL3[31] FCSEL2[23] FCSEL1[15] FCSEL0[7] + * FCSel[1:0] : Rx Feedback Clock Delay Control + * Inverter delay means10ns delay if SDCLK 50MHz setting + * 01 = Delay1 (basic delay) + * 11 = Delay2 (basic delay + 2ns) + * 00 = Delay3 (inverter delay) + * 10 = Delay4 (inverter delay + 2ns) + */ + val = SDHCI_CTRL3_FCSEL3 | SDHCI_CTRL3_FCSEL1; + sdhci_writel(host, val, SDHCI_CONTROL3); + + /* + * SELBASECLK[5:4] + * 00/01 = HCLK + * 10 = EPLL + * 11 = XTI or XEXTCLK + */ + ctrl = sdhci_readl(host, SDHCI_CONTROL2); + ctrl &= ~SDHCI_CTRL2_SELBASECLK_MASK(0x3); + ctrl |= SDHCI_CTRL2_SELBASECLK_MASK(0x2); + sdhci_writel(host, ctrl, SDHCI_CONTROL2); +} + +int s5p_sdhci_init(u32 regbase, u32 max_clk, u32 min_clk, u32 quirks) +{ + struct sdhci_host *host = NULL; + host = (struct sdhci_host *)malloc(sizeof(struct sdhci_host)); + if (!host) { + printf("sdhci__host malloc fail!\n"); + return 1; + } + + host->name = S5P_NAME; + host->ioaddr = (void *)regbase; + host->quirks = quirks; + + host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_VOLTAGE; + host->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; + if (quirks & SDHCI_QUIRK_REG32_RW) + host->version = sdhci_readl(host, SDHCI_HOST_VERSION - 2) >> 16; + else + host->version = sdhci_readw(host, SDHCI_HOST_VERSION); + + host->set_control_reg = &s5p_sdhci_set_control_reg; + + host->host_caps = MMC_MODE_HC; + + add_sdhci(host, max_clk, min_clk); + return 0; +} -- cgit v0.10.2 From 7d2d58b4e63a8307f713e6b17fdb9b2413548574 Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Mon, 23 Apr 2012 02:36:29 +0000 Subject: ARM: SAMSUNG: support sdhci controller To support sdhci controller, remove the CONFIG_S5P_MMC.. Instead, use the CONFIG_S5P_SDHCI/CONFIG_SDHCI. Signed-off-by: Jaehoon Chung Signed-off-by: Kyungmin Park Acked-by: Lei Wen Acked-by: Minkyu Kang diff --git a/include/configs/origen.h b/include/configs/origen.h index 8ede825..367f991 100644 --- a/include/configs/origen.h +++ b/include/configs/origen.h @@ -69,9 +69,10 @@ #define EXYNOS4_DEFAULT_UART_OFFSET 0x020000 /* SD/MMC configuration */ -#define CONFIG_GENERIC_MMC 1 -#define CONFIG_MMC 1 -#define CONFIG_S5P_MMC 1 +#define CONFIG_GENERIC_MMC +#define CONFIG_MMC +#define CONFIG_SDHCI +#define CONFIG_S5P_SDHCI /* PWM */ #define CONFIG_PWM 1 diff --git a/include/configs/s5p_goni.h b/include/configs/s5p_goni.h index 56b5547..e133a17 100644 --- a/include/configs/s5p_goni.h +++ b/include/configs/s5p_goni.h @@ -63,9 +63,10 @@ #define CONFIG_BAUDRATE 115200 /* MMC */ -#define CONFIG_GENERIC_MMC 1 -#define CONFIG_MMC 1 -#define CONFIG_S5P_MMC 1 +#define CONFIG_GENERIC_MMC +#define CONFIG_MMC +#define CONFIG_SDHCI +#define CONFIG_S5P_SDHCI /* PWM */ #define CONFIG_PWM 1 diff --git a/include/configs/s5pc210_universal.h b/include/configs/s5pc210_universal.h index 1301275..00db374 100644 --- a/include/configs/s5pc210_universal.h +++ b/include/configs/s5pc210_universal.h @@ -66,9 +66,10 @@ #define CONFIG_BAUDRATE 115200 /* MMC */ -#define CONFIG_GENERIC_MMC 1 -#define CONFIG_MMC 1 -#define CONFIG_S5P_MMC 1 +#define CONFIG_GENERIC_MMC +#define CONFIG_MMC +#define CONFIG_SDHCI +#define CONFIG_S5P_SDHCI /* PWM */ #define CONFIG_PWM 1 diff --git a/include/configs/smdk5250.h b/include/configs/smdk5250.h index 9659f9e..0f63040 100644 --- a/include/configs/smdk5250.h +++ b/include/configs/smdk5250.h @@ -78,7 +78,8 @@ /* SD/MMC configuration */ #define CONFIG_GENERIC_MMC #define CONFIG_MMC -#define CONFIG_S5P_MMC +#define CONFIG_SDHCI +#define CONFIG_S5P_SDHCI #define CONFIG_BOARD_EARLY_INIT_F diff --git a/include/configs/smdkv310.h b/include/configs/smdkv310.h index 93c25da..702134b 100644 --- a/include/configs/smdkv310.h +++ b/include/configs/smdkv310.h @@ -68,9 +68,10 @@ #define EXYNOS4_DEFAULT_UART_OFFSET 0x010000 /* SD/MMC configuration */ -#define CONFIG_GENERIC_MMC 1 -#define CONFIG_MMC 1 -#define CONFIG_S5P_MMC 1 +#define CONFIG_GENERIC_MMC +#define CONFIG_MMC +#define CONFIG_SDHCI +#define CONFIG_S5P_SDHCI /* PWM */ #define CONFIG_PWM 1 diff --git a/include/configs/trats.h b/include/configs/trats.h index 10f11d9..71b9393 100644 --- a/include/configs/trats.h +++ b/include/configs/trats.h @@ -74,7 +74,8 @@ /* MMC */ #define CONFIG_GENERIC_MMC #define CONFIG_MMC -#define CONFIG_S5P_MMC +#define CONFIG_S5P_SDHCI +#define CONFIG_SDHCI /* PWM */ #define CONFIG_PWM -- cgit v0.10.2