diff options
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/Makefile | 1 | ||||
-rw-r--r-- | drivers/mmc/arm_pl180_mmci.c | 1 | ||||
-rw-r--r-- | drivers/mmc/bcm2835_sdhci.c | 189 | ||||
-rw-r--r-- | drivers/mmc/bfin_sdh.c | 69 | ||||
-rw-r--r-- | drivers/mmc/davinci_mmc.c | 1 | ||||
-rw-r--r-- | drivers/mmc/fsl_esdhc.c | 1 | ||||
-rw-r--r-- | drivers/mmc/ftsdc010_esdhc.c | 1 | ||||
-rw-r--r-- | drivers/mmc/gen_atmel_mci.c | 1 | ||||
-rw-r--r-- | drivers/mmc/mmc.c | 29 | ||||
-rw-r--r-- | drivers/mmc/mmc_spi.c | 1 | ||||
-rw-r--r-- | drivers/mmc/mxcmmc.c | 1 | ||||
-rw-r--r-- | drivers/mmc/mxsmmc.c | 21 | ||||
-rw-r--r-- | drivers/mmc/omap_hsmmc.c | 107 | ||||
-rw-r--r-- | drivers/mmc/sdhci.c | 1 | ||||
-rw-r--r-- | drivers/mmc/sh_mmcif.c | 1 | ||||
-rw-r--r-- | drivers/mmc/tegra_mmc.c | 227 |
16 files changed, 532 insertions, 120 deletions
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 65791aa..1d6faa2 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -43,6 +43,7 @@ COBJS-$(CONFIG_MXS_MMC) += mxsmmc.o COBJS-$(CONFIG_OMAP_HSMMC) += omap_hsmmc.o COBJS-$(CONFIG_PXA_MMC_GENERIC) += pxa_mmc_gen.o COBJS-$(CONFIG_SDHCI) += sdhci.o +COBJS-$(CONFIG_BCM2835_SDHCI) += bcm2835_sdhci.o COBJS-$(CONFIG_S5P_SDHCI) += s5p_sdhci.o COBJS-$(CONFIG_SH_MMCIF) += sh_mmcif.o COBJS-$(CONFIG_TEGRA_MMC) += tegra_mmc.o diff --git a/drivers/mmc/arm_pl180_mmci.c b/drivers/mmc/arm_pl180_mmci.c index af1380a..ab2e81e 100644 --- a/drivers/mmc/arm_pl180_mmci.c +++ b/drivers/mmc/arm_pl180_mmci.c @@ -377,6 +377,7 @@ int arm_pl180_mmci_init(struct pl180_mmc_host *host) dev->set_ios = host_set_ios; dev->init = mmc_host_reset; dev->getcd = NULL; + dev->getwp = NULL; dev->host_caps = host->caps; dev->voltages = host->voltages; dev->f_min = host->clock_min; diff --git a/drivers/mmc/bcm2835_sdhci.c b/drivers/mmc/bcm2835_sdhci.c new file mode 100644 index 0000000..b0afc3c --- /dev/null +++ b/drivers/mmc/bcm2835_sdhci.c @@ -0,0 +1,189 @@ +/* + * This code was extracted from: + * git://github.com/gonzoua/u-boot-pi.git master + * and hence presumably (C) 2012 Oleksandr Tymoshenko + * + * Tweaks for U-Boot upstreaming + * (C) 2012 Stephen Warren + * + * Portions (e.g. read/write macros, concepts for back-to-back register write + * timing workarounds) obviously extracted from the Linux kernel at: + * https://github.com/raspberrypi/linux.git rpi-3.6.y + * + * The Linux kernel code has the following (c) and license, which is hence + * propagated to Oleksandr's tree and here: + * + * Support for SDHCI device on 2835 + * Based on sdhci-bcm2708.c (c) 2010 Broadcom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Supports: + * SDHCI platform device - Arasan SD controller in BCM2708 + * + * Inspired by sdhci-pci.c, by Pierre Ossman + */ + +#include <common.h> +#include <malloc.h> +#include <sdhci.h> + +/* 400KHz is max freq for card ID etc. Use that as min */ +#define MIN_FREQ 400000 + +struct bcm2835_sdhci_host { + struct sdhci_host host; + uint twoticks_delay; + ulong last_write; +}; + +static inline struct bcm2835_sdhci_host *to_bcm(struct sdhci_host *host) +{ + return (struct bcm2835_sdhci_host *)host; +} + +static inline void bcm2835_sdhci_raw_writel(struct sdhci_host *host, u32 val, + int reg) +{ + struct bcm2835_sdhci_host *bcm_host = to_bcm(host); + + /* + * The Arasan has a bugette whereby it may lose the content of + * successive writes to registers that are within two SD-card clock + * cycles of each other (a clock domain crossing problem). + * It seems, however, that the data register does not have this problem. + * (Which is just as well - otherwise we'd have to nobble the DMA engine + * too) + */ + while (get_timer(bcm_host->last_write) < bcm_host->twoticks_delay) + ; + + writel(val, host->ioaddr + reg); + bcm_host->last_write = get_timer(0); +} + +static inline u32 bcm2835_sdhci_raw_readl(struct sdhci_host *host, int reg) +{ + return readl(host->ioaddr + reg); +} + +static void bcm2835_sdhci_writel(struct sdhci_host *host, u32 val, int reg) +{ + bcm2835_sdhci_raw_writel(host, val, reg); +} + +static void bcm2835_sdhci_writew(struct sdhci_host *host, u16 val, int reg) +{ + static u32 shadow; + u32 oldval = (reg == SDHCI_COMMAND) ? shadow : + bcm2835_sdhci_raw_readl(host, reg & ~3); + u32 word_num = (reg >> 1) & 1; + u32 word_shift = word_num * 16; + u32 mask = 0xffff << word_shift; + u32 newval = (oldval & ~mask) | (val << word_shift); + + if (reg == SDHCI_TRANSFER_MODE) + shadow = newval; + else + bcm2835_sdhci_raw_writel(host, newval, reg & ~3); +} + +static void bcm2835_sdhci_writeb(struct sdhci_host *host, u8 val, int reg) +{ + u32 oldval = bcm2835_sdhci_raw_readl(host, reg & ~3); + u32 byte_num = reg & 3; + u32 byte_shift = byte_num * 8; + u32 mask = 0xff << byte_shift; + u32 newval = (oldval & ~mask) | (val << byte_shift); + + bcm2835_sdhci_raw_writel(host, newval, reg & ~3); +} + +static u32 bcm2835_sdhci_readl(struct sdhci_host *host, int reg) +{ + u32 val = bcm2835_sdhci_raw_readl(host, reg); + + return val; +} + +static u16 bcm2835_sdhci_readw(struct sdhci_host *host, int reg) +{ + u32 val = bcm2835_sdhci_raw_readl(host, (reg & ~3)); + u32 word_num = (reg >> 1) & 1; + u32 word_shift = word_num * 16; + u32 word = (val >> word_shift) & 0xffff; + + return word; +} + +static u8 bcm2835_sdhci_readb(struct sdhci_host *host, int reg) +{ + u32 val = bcm2835_sdhci_raw_readl(host, (reg & ~3)); + u32 byte_num = reg & 3; + u32 byte_shift = byte_num * 8; + u32 byte = (val >> byte_shift) & 0xff; + + return byte; +} + +static const struct sdhci_ops bcm2835_ops = { + .write_l = bcm2835_sdhci_writel, + .write_w = bcm2835_sdhci_writew, + .write_b = bcm2835_sdhci_writeb, + .read_l = bcm2835_sdhci_readl, + .read_w = bcm2835_sdhci_readw, + .read_b = bcm2835_sdhci_readb, +}; + +int bcm2835_sdhci_init(u32 regbase, u32 emmc_freq) +{ + struct bcm2835_sdhci_host *bcm_host; + struct sdhci_host *host; + + bcm_host = malloc(sizeof(*bcm_host)); + if (!bcm_host) { + printf("sdhci_host malloc fail!\n"); + return 1; + } + + /* + * See the comments in bcm2835_sdhci_raw_writel(). + * + * This should probably be dynamically calculated based on the actual + * frequency. However, this is the longest we'll have to wait, and + * doesn't seem to slow access down too much, so the added complexity + * doesn't seem worth it for now. + * + * 1/MIN_FREQ is (max) time per tick of eMMC clock. + * 2/MIN_FREQ is time for two ticks. + * Multiply by 1000000 to get uS per two ticks. + * +1 for hack rounding. + */ + bcm_host->twoticks_delay = ((2 * 1000000) / MIN_FREQ) + 1; + bcm_host->last_write = 0; + + host = &bcm_host->host; + host->name = "bcm2835_sdhci"; + host->ioaddr = (void *)regbase; + host->quirks = SDHCI_QUIRK_BROKEN_VOLTAGE | SDHCI_QUIRK_BROKEN_R1B | + SDHCI_QUIRK_WAIT_SEND_CMD; + host->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; + host->ops = &bcm2835_ops; + + host->version = sdhci_readw(host, SDHCI_HOST_VERSION); + add_sdhci(host, emmc_freq, MIN_FREQ); + + return 0; +} diff --git a/drivers/mmc/bfin_sdh.c b/drivers/mmc/bfin_sdh.c index 8d59d46..2631174 100644 --- a/drivers/mmc/bfin_sdh.c +++ b/drivers/mmc/bfin_sdh.c @@ -19,9 +19,7 @@ #include <asm/mach-common/bits/sdh.h> #include <asm/mach-common/bits/dma.h> -#if defined(__ADSPBF50x__) || defined(__ADSPBF51x__) -# define bfin_read_SDH_PWR_CTL bfin_read_RSI_PWR_CONTROL -# define bfin_write_SDH_PWR_CTL bfin_write_RSI_PWR_CONTROL +#if defined(__ADSPBF50x__) || defined(__ADSPBF51x__) || defined(__ADSPBF60x__) # define bfin_read_SDH_CLK_CTL bfin_read_RSI_CLK_CONTROL # define bfin_write_SDH_CLK_CTL bfin_write_RSI_CLK_CONTROL # define bfin_write_SDH_ARGUMENT bfin_write_RSI_ARGUMENT @@ -38,10 +36,21 @@ # define bfin_write_SDH_STATUS_CLR bfin_write_RSI_STATUSCL # define bfin_read_SDH_CFG bfin_read_RSI_CONFIG # define bfin_write_SDH_CFG bfin_write_RSI_CONFIG +# if defined(__ADSPBF60x__) +# define bfin_read_SDH_BLK_SIZE bfin_read_RSI_BLKSZ +# define bfin_write_SDH_BLK_SIZE bfin_write_RSI_BLKSZ +# define bfin_write_DMA_START_ADDR bfin_write_DMA10_START_ADDR +# define bfin_write_DMA_X_COUNT bfin_write_DMA10_X_COUNT +# define bfin_write_DMA_X_MODIFY bfin_write_DMA10_X_MODIFY +# define bfin_write_DMA_CONFIG bfin_write_DMA10_CONFIG +# else +# define bfin_read_SDH_PWR_CTL bfin_read_RSI_PWR_CONTROL +# define bfin_write_SDH_PWR_CTL bfin_write_RSI_PWR_CONTROL # define bfin_write_DMA_START_ADDR bfin_write_DMA4_START_ADDR # define bfin_write_DMA_X_COUNT bfin_write_DMA4_X_COUNT # define bfin_write_DMA_X_MODIFY bfin_write_DMA4_X_MODIFY # define bfin_write_DMA_CONFIG bfin_write_DMA4_CONFIG +# endif # define PORTMUX_PINS \ { P_RSI_DATA0, P_RSI_DATA1, P_RSI_DATA2, P_RSI_DATA3, P_RSI_CMD, P_RSI_CLK, 0 } #elif defined(__ADSPBF54x__) @@ -70,6 +79,9 @@ sdh_send_cmd(struct mmc *mmc, struct mmc_cmd *mmc_cmd) sdh_cmd |= CMD_RSP; if (flags & MMC_RSP_136) sdh_cmd |= CMD_L_RSP; +#ifdef RSI_BLKSZ + sdh_cmd |= CMD_DATA0_BUSY; +#endif bfin_write_SDH_ARGUMENT(arg); bfin_write_SDH_COMMAND(sdh_cmd); @@ -104,6 +116,12 @@ sdh_send_cmd(struct mmc *mmc, struct mmc_cmd *mmc_cmd) bfin_write_SDH_STATUS_CLR(CMD_SENT_STAT | CMD_RESP_END_STAT | CMD_TIMEOUT_STAT | CMD_CRC_FAIL_STAT); +#ifdef RSI_BLKSZ + /* wait till card ready */ + while (!(bfin_read_RSI_ESTAT() & SD_CARD_READY)) + continue; + bfin_write_RSI_ESTAT(SD_CARD_READY); +#endif return ret; } @@ -113,16 +131,19 @@ static int sdh_setup_data(struct mmc *mmc, struct mmc_data *data) { u16 data_ctl = 0; u16 dma_cfg = 0; - int ret = 0; unsigned long data_size = data->blocksize * data->blocks; /* Don't support write yet. */ if (data->flags & MMC_DATA_WRITE) return UNUSABLE_ERR; +#ifndef RSI_BLKSZ data_ctl |= ((ffs(data_size) - 1) << 4); +#else + bfin_write_SDH_BLK_SIZE(data_size); +#endif data_ctl |= DTX_DIR; bfin_write_SDH_DATA_CTL(data_ctl); - dma_cfg = WDSIZE_32 | RESTART | WNR | DMAEN; + dma_cfg = WDSIZE_32 | PSIZE_32 | RESTART | WNR | DMAEN; bfin_write_SDH_DATA_TIMER(-1); @@ -137,7 +158,7 @@ static int sdh_setup_data(struct mmc *mmc, struct mmc_data *data) /* kick off transfer */ bfin_write_SDH_DATA_CTL(bfin_read_SDH_DATA_CTL() | DTX_DMA_E | DTX_E); - return ret; + return 0; } @@ -147,13 +168,23 @@ static int bfin_sdh_request(struct mmc *mmc, struct mmc_cmd *cmd, u32 status; int ret = 0; + if (data) { + ret = sdh_setup_data(mmc, data); + if (ret) + return ret; + } + ret = sdh_send_cmd(mmc, cmd); if (ret) { + bfin_write_SDH_COMMAND(0); + bfin_write_DMA_CONFIG(0); + bfin_write_SDH_DATA_CTL(0); + SSYNC(); printf("sending CMD%d failed\n", cmd->cmdidx); return ret; } + if (data) { - ret = sdh_setup_data(mmc, data); do { udelay(1); status = bfin_read_SDH_STATUS(); @@ -208,10 +239,12 @@ static void bfin_sdh_set_ios(struct mmc *mmc) if (mmc->bus_width == 4) { cfg = bfin_read_SDH_CFG(); - cfg &= ~0x80; - cfg |= 0x40; +#ifndef RSI_BLKSZ + cfg &= ~PD_SDDAT3; +#endif + cfg |= PUP_SDDAT3; bfin_write_SDH_CFG(cfg); - clk_ctl |= WIDE_BUS; + clk_ctl |= WIDE_BUS_4; } bfin_write_SDH_CLK_CTL(clk_ctl); sdh_set_clk(mmc->clock); @@ -220,20 +253,23 @@ static void bfin_sdh_set_ios(struct mmc *mmc) static int bfin_sdh_init(struct mmc *mmc) { const unsigned short pins[] = PORTMUX_PINS; - u16 pwr_ctl = 0; + int ret; /* Initialize sdh controller */ - peripheral_request_list(pins, "bfin_sdh"); + ret = peripheral_request_list(pins, "bfin_sdh"); + if (ret < 0) + return ret; #if defined(__ADSPBF54x__) bfin_write_DMAC1_PERIMUX(bfin_read_DMAC1_PERIMUX() | 0x1); #endif bfin_write_SDH_CFG(bfin_read_SDH_CFG() | CLKS_EN); /* Disable card detect pin */ bfin_write_SDH_CFG((bfin_read_SDH_CFG() & 0x1F) | 0x60); - - pwr_ctl |= ROD_CTL; - pwr_ctl |= PWR_ON; - bfin_write_SDH_PWR_CTL(pwr_ctl); +#ifndef RSI_BLKSZ + bfin_write_SDH_PWR_CTL(PWR_ON | ROD_CTL); +#else + bfin_write_SDH_CFG(bfin_read_SDH_CFG() | PWR_ON); +#endif return 0; } @@ -251,6 +287,7 @@ int bfin_mmc_init(bd_t *bis) mmc->set_ios = bfin_sdh_set_ios; mmc->init = bfin_sdh_init; mmc->getcd = NULL; + mmc->getwp = NULL; mmc->host_caps = MMC_MODE_4BIT; mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; diff --git a/drivers/mmc/davinci_mmc.c b/drivers/mmc/davinci_mmc.c index ee8f261..e2379e3 100644 --- a/drivers/mmc/davinci_mmc.c +++ b/drivers/mmc/davinci_mmc.c @@ -388,6 +388,7 @@ int davinci_mmc_init(bd_t *bis, struct davinci_mmc *host) mmc->set_ios = dmmc_set_ios; mmc->init = dmmc_init; mmc->getcd = NULL; + mmc->getwp = NULL; mmc->f_min = 200000; mmc->f_max = 25000000; diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c index b90f3e7..54b5363 100644 --- a/drivers/mmc/fsl_esdhc.c +++ b/drivers/mmc/fsl_esdhc.c @@ -552,6 +552,7 @@ int fsl_esdhc_initialize(bd_t *bis, struct fsl_esdhc_cfg *cfg) mmc->set_ios = esdhc_set_ios; mmc->init = esdhc_init; mmc->getcd = esdhc_getcd; + mmc->getwp = NULL; voltage_caps = 0; caps = regs->hostcapblt; diff --git a/drivers/mmc/ftsdc010_esdhc.c b/drivers/mmc/ftsdc010_esdhc.c index f1702fe..42f0e0c 100644 --- a/drivers/mmc/ftsdc010_esdhc.c +++ b/drivers/mmc/ftsdc010_esdhc.c @@ -666,6 +666,7 @@ int ftsdc010_mmc_init(int dev_index) mmc->set_ios = ftsdc010_set_ios; mmc->init = ftsdc010_core_init; mmc->getcd = NULL; + mmc->getwp = NULL; mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; diff --git a/drivers/mmc/gen_atmel_mci.c b/drivers/mmc/gen_atmel_mci.c index 67b2dbe..70a9f91 100644 --- a/drivers/mmc/gen_atmel_mci.c +++ b/drivers/mmc/gen_atmel_mci.c @@ -349,6 +349,7 @@ int atmel_mci_init(void *regs) mmc->set_ios = mci_set_ios; mmc->init = mci_init; mmc->getcd = NULL; + mmc->getwp = NULL; /* need to be able to pass these in on a board by board basis */ mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 72e8ce6..d732581 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -40,6 +40,27 @@ static struct list_head mmc_devices; static int cur_dev_num = -1; +int __weak board_mmc_getwp(struct mmc *mmc) +{ + return -1; +} + +int mmc_getwp(struct mmc *mmc) +{ + int wp; + + wp = board_mmc_getwp(mmc); + + if (wp < 0) { + if (mmc->getwp) + wp = mmc->getwp(mmc); + else + wp = 0; + } + + return wp; +} + int __board_mmc_getcd(struct mmc *mmc) { return -1; } @@ -675,8 +696,12 @@ int mmc_getcd(struct mmc *mmc) cd = board_mmc_getcd(mmc); - if ((cd < 0) && mmc->getcd) - cd = mmc->getcd(mmc); + if (cd < 0) { + if (mmc->getcd) + cd = mmc->getcd(mmc); + else + cd = 1; + } return cd; } diff --git a/drivers/mmc/mmc_spi.c b/drivers/mmc/mmc_spi.c index 11ba532..fe6a5a1 100644 --- a/drivers/mmc/mmc_spi.c +++ b/drivers/mmc/mmc_spi.c @@ -273,6 +273,7 @@ struct mmc *mmc_spi_init(uint bus, uint cs, uint speed, uint mode) mmc->set_ios = mmc_spi_set_ios; mmc->init = mmc_spi_init_p; mmc->getcd = NULL; + mmc->getwp = NULL; mmc->host_caps = MMC_MODE_SPI; mmc->voltages = MMC_SPI_VOLTAGE; diff --git a/drivers/mmc/mxcmmc.c b/drivers/mmc/mxcmmc.c index d58c18b..4f99617 100644 --- a/drivers/mmc/mxcmmc.c +++ b/drivers/mmc/mxcmmc.c @@ -499,6 +499,7 @@ static int mxcmci_initialize(bd_t *bis) mmc->set_ios = mxcmci_set_ios; mmc->init = mxcmci_init; mmc->getcd = NULL; + mmc->getwp = NULL; mmc->host_caps = MMC_MODE_4BIT; host->base = (struct mxcmci_regs *)CONFIG_MXC_MCI_REGS_BASE; diff --git a/drivers/mmc/mxsmmc.c b/drivers/mmc/mxsmmc.c index a72f66c..a89660f 100644 --- a/drivers/mmc/mxsmmc.c +++ b/drivers/mmc/mxsmmc.c @@ -53,12 +53,6 @@ struct mxsmmc_priv { struct mxs_dma_desc *desc; }; -#if defined(CONFIG_MX23) -static const unsigned int mxsmmc_id_offset = 1; -#elif defined(CONFIG_MX28) -static const unsigned int mxsmmc_id_offset = 0; -#endif - #define MXSMMC_MAX_TIMEOUT 10000 #define MXSMMC_SMALL_TRANSFER 512 @@ -137,7 +131,7 @@ static int mxsmmc_send_cmd_dma(struct mxsmmc_priv *priv, struct mmc_data *data) priv->desc->cmd.data |= MXS_DMA_DESC_IRQ | MXS_DMA_DESC_DEC_SEM | (data_count << MXS_DMA_DESC_BYTES_OFFSET); - dmach = MXS_DMA_CHANNEL_AHB_APBH_SSP0 + priv->id + mxsmmc_id_offset; + dmach = MXS_DMA_CHANNEL_AHB_APBH_SSP0 + priv->id; mxs_dma_desc_append(dmach, priv->desc); if (mxs_dma_go(dmach)) { bounce_buffer_stop(&bbstate); @@ -390,15 +384,9 @@ int mxsmmc_initialize(bd_t *bis, int id, int (*wp)(int), int (*cd)(int)) struct mmc *mmc = NULL; struct mxsmmc_priv *priv = NULL; int ret; -#if defined(CONFIG_MX23) - const unsigned int mxsmmc_max_id = 2; - const unsigned int mxsmmc_clk_id = 0; -#elif defined(CONFIG_MX28) - const unsigned int mxsmmc_max_id = 4; - const unsigned int mxsmmc_clk_id = id; -#endif + const unsigned int mxsmmc_clk_id = mxs_ssp_clock_by_bus(id); - if (id >= mxsmmc_max_id) + if (!mxs_ssp_bus_id_valid(id)) return -ENODEV; mmc = malloc(sizeof(struct mmc)); @@ -418,7 +406,7 @@ int mxsmmc_initialize(bd_t *bis, int id, int (*wp)(int), int (*cd)(int)) return -ENOMEM; } - ret = mxs_dma_init_channel(id + mxsmmc_id_offset); + ret = mxs_dma_init_channel(MXS_DMA_CHANNEL_AHB_APBH_SSP0 + id); if (ret) return ret; @@ -432,6 +420,7 @@ int mxsmmc_initialize(bd_t *bis, int id, int (*wp)(int), int (*cd)(int)) mmc->set_ios = mxsmmc_set_ios; mmc->init = mxsmmc_init; mmc->getcd = NULL; + mmc->getwp = NULL; mmc->priv = priv; mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index afd9b30..166744c 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -30,6 +30,7 @@ #include <twl4030.h> #include <twl6030.h> #include <twl6035.h> +#include <asm/gpio.h> #include <asm/io.h> #include <asm/arch/mmc_host_def.h> #include <asm/arch/sys_proto.h> @@ -38,30 +39,71 @@ #define SYSCTL_SRC (1 << 25) #define SYSCTL_SRD (1 << 26) +struct omap_hsmmc_data { + struct hsmmc *base_addr; + int cd_gpio; + int wp_gpio; +}; + /* If we fail after 1 second wait, something is really bad */ #define MAX_RETRY_MS 1000 static int mmc_read_data(struct hsmmc *mmc_base, char *buf, unsigned int size); static int mmc_write_data(struct hsmmc *mmc_base, const char *buf, unsigned int siz); -static struct mmc hsmmc_dev[2]; +static struct mmc hsmmc_dev[3]; +static struct omap_hsmmc_data hsmmc_dev_data[3]; + +#if (defined(CONFIG_OMAP_GPIO) && !defined(CONFIG_SPL_BUILD)) || \ + (defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_GPIO_SUPPORT)) +static int omap_mmc_setup_gpio_in(int gpio, const char *label) +{ + if (!gpio_is_valid(gpio)) + return -1; + + if (gpio_request(gpio, label) < 0) + return -1; + + if (gpio_direction_input(gpio) < 0) + return -1; + + return gpio; +} + +static int omap_mmc_getcd(struct mmc *mmc) +{ + int cd_gpio = ((struct omap_hsmmc_data *)mmc->priv)->cd_gpio; + return gpio_get_value(cd_gpio); +} + +static int omap_mmc_getwp(struct mmc *mmc) +{ + int wp_gpio = ((struct omap_hsmmc_data *)mmc->priv)->wp_gpio; + return gpio_get_value(wp_gpio); +} +#else +static inline int omap_mmc_setup_gpio_in(int gpio, const char *label) +{ + return -1; +} + +#define omap_mmc_getcd NULL +#define omap_mmc_getwp NULL +#endif #if defined(CONFIG_OMAP44XX) && defined(CONFIG_TWL6030_POWER) static void omap4_vmmc_pbias_config(struct mmc *mmc) { u32 value = 0; - struct omap_sys_ctrl_regs *const ctrl = - (struct omap_sys_ctrl_regs *) SYSCTRL_GENERAL_CORE_BASE; - - value = readl(&ctrl->control_pbiaslite); + value = readl((*ctrl)->control_pbiaslite); value &= ~(MMC1_PBIASLITE_PWRDNZ | MMC1_PWRDNZ); - writel(value, &ctrl->control_pbiaslite); + writel(value, (*ctrl)->control_pbiaslite); /* set VMMC to 3V */ twl6030_power_mmc_init(); - value = readl(&ctrl->control_pbiaslite); + value = readl((*ctrl)->control_pbiaslite); value |= MMC1_PBIASLITE_VMODE | MMC1_PBIASLITE_PWRDNZ | MMC1_PWRDNZ; - writel(value, &ctrl->control_pbiaslite); + writel(value, (*ctrl)->control_pbiaslite); } #endif @@ -69,26 +111,24 @@ static void omap4_vmmc_pbias_config(struct mmc *mmc) static void omap5_pbias_config(struct mmc *mmc) { u32 value = 0; - struct omap_sys_ctrl_regs *const ctrl = - (struct omap_sys_ctrl_regs *) SYSCTRL_GENERAL_CORE_BASE; - value = readl(&ctrl->control_pbias); + value = readl((*ctrl)->control_pbias); value &= ~(SDCARD_PWRDNZ | SDCARD_BIAS_PWRDNZ); value |= SDCARD_BIAS_HIZ_MODE; - writel(value, &ctrl->control_pbias); + writel(value, (*ctrl)->control_pbias); twl6035_mmc1_poweron_ldo(); - value = readl(&ctrl->control_pbias); + value = readl((*ctrl)->control_pbias); value &= ~SDCARD_BIAS_HIZ_MODE; value |= SDCARD_PBIASLITE_VMODE | SDCARD_PWRDNZ | SDCARD_BIAS_PWRDNZ; - writel(value, &ctrl->control_pbias); + writel(value, (*ctrl)->control_pbias); - value = readl(&ctrl->control_pbias); + value = readl((*ctrl)->control_pbias); if (value & (1 << 23)) { value &= ~(SDCARD_PWRDNZ | SDCARD_BIAS_PWRDNZ); value |= SDCARD_BIAS_HIZ_MODE; - writel(value, &ctrl->control_pbias); + writel(value, (*ctrl)->control_pbias); } } #endif @@ -177,11 +217,12 @@ void mmc_init_stream(struct hsmmc *mmc_base) static int mmc_init_setup(struct mmc *mmc) { - struct hsmmc *mmc_base = (struct hsmmc *)mmc->priv; + struct hsmmc *mmc_base; unsigned int reg_val; unsigned int dsor; ulong start; + mmc_base = ((struct omap_hsmmc_data *)mmc->priv)->base_addr; mmc_board_init(mmc); writel(readl(&mmc_base->sysconfig) | MMC_SOFTRESET, @@ -262,10 +303,11 @@ static void mmc_reset_controller_fsm(struct hsmmc *mmc_base, u32 bit) static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) { - struct hsmmc *mmc_base = (struct hsmmc *)mmc->priv; + struct hsmmc *mmc_base; unsigned int flags, mmc_stat; ulong start; + mmc_base = ((struct omap_hsmmc_data *)mmc->priv)->base_addr; start = get_timer(0); while ((readl(&mmc_base->pstate) & (DATI_MASK | CMDI_MASK)) != 0) { if (get_timer(0) - start > MAX_RETRY_MS) { @@ -489,10 +531,11 @@ static int mmc_write_data(struct hsmmc *mmc_base, const char *buf, static void mmc_set_ios(struct mmc *mmc) { - struct hsmmc *mmc_base = (struct hsmmc *)mmc->priv; + struct hsmmc *mmc_base; unsigned int dsor = 0; ulong start; + mmc_base = ((struct omap_hsmmc_data *)mmc->priv)->base_addr; /* configue bus width */ switch (mmc->bus_width) { case 8: @@ -540,36 +583,44 @@ static void mmc_set_ios(struct mmc *mmc) writel(readl(&mmc_base->sysctl) | CEN_ENABLE, &mmc_base->sysctl); } -int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max) +int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max, int cd_gpio, + int wp_gpio) { - struct mmc *mmc; - - mmc = &hsmmc_dev[dev_index]; + struct mmc *mmc = &hsmmc_dev[dev_index]; + struct omap_hsmmc_data *priv_data = &hsmmc_dev_data[dev_index]; sprintf(mmc->name, "OMAP SD/MMC"); mmc->send_cmd = mmc_send_cmd; mmc->set_ios = mmc_set_ios; mmc->init = mmc_init_setup; - mmc->getcd = NULL; + mmc->priv = priv_data; switch (dev_index) { case 0: - mmc->priv = (struct hsmmc *)OMAP_HSMMC1_BASE; + priv_data->base_addr = (struct hsmmc *)OMAP_HSMMC1_BASE; break; #ifdef OMAP_HSMMC2_BASE case 1: - mmc->priv = (struct hsmmc *)OMAP_HSMMC2_BASE; + priv_data->base_addr = (struct hsmmc *)OMAP_HSMMC2_BASE; break; #endif #ifdef OMAP_HSMMC3_BASE case 2: - mmc->priv = (struct hsmmc *)OMAP_HSMMC3_BASE; + priv_data->base_addr = (struct hsmmc *)OMAP_HSMMC3_BASE; break; #endif default: - mmc->priv = (struct hsmmc *)OMAP_HSMMC1_BASE; + priv_data->base_addr = (struct hsmmc *)OMAP_HSMMC1_BASE; return 1; } + priv_data->cd_gpio = omap_mmc_setup_gpio_in(cd_gpio, "mmc_cd"); + if (priv_data->cd_gpio != -1) + mmc->getcd = omap_mmc_getcd; + + priv_data->wp_gpio = omap_mmc_setup_gpio_in(wp_gpio, "mmc_wp"); + if (priv_data->wp_gpio != -1) + mmc->getwp = omap_mmc_getwp; + mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; mmc->host_caps = (MMC_MODE_4BIT | MMC_MODE_HS_52MHz | MMC_MODE_HS | MMC_MODE_HC) & ~host_caps_mask; diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 551b423..1eaea04 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -440,6 +440,7 @@ int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk) mmc->set_ios = sdhci_set_ios; mmc->init = sdhci_init; mmc->getcd = NULL; + mmc->getwp = NULL; caps = sdhci_readl(host, SDHCI_CAPABILITIES); #ifdef CONFIG_MMC_SDMA diff --git a/drivers/mmc/sh_mmcif.c b/drivers/mmc/sh_mmcif.c index 4588568..011d4f3 100644 --- a/drivers/mmc/sh_mmcif.c +++ b/drivers/mmc/sh_mmcif.c @@ -599,6 +599,7 @@ int mmcif_mmc_init(void) mmc->set_ios = sh_mmcif_set_ios; mmc->init = sh_mmcif_init; mmc->getcd = NULL; + mmc->getwp = NULL; host->regs = (struct sh_mmcif_regs *)CONFIG_SH_MMCIF_ADDR; host->clk = CONFIG_SH_MMCIF_CLK; mmc->priv = host; diff --git a/drivers/mmc/tegra_mmc.c b/drivers/mmc/tegra_mmc.c index d749ab0..e86bc68 100644 --- a/drivers/mmc/tegra_mmc.c +++ b/drivers/mmc/tegra_mmc.c @@ -2,7 +2,7 @@ * (C) Copyright 2009 SAMSUNG Electronics * Minkyu Kang <mk7.kang@samsung.com> * Jaehoon Chung <jh80.chung@samsung.com> - * Portions Copyright 2011-2012 NVIDIA Corporation + * Portions Copyright 2011-2013 NVIDIA Corporation * * 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 @@ -28,43 +28,45 @@ #include <asm/arch-tegra/tegra_mmc.h> #include <mmc.h> -/* support 4 mmc hosts */ -struct mmc mmc_dev[4]; -struct mmc_host mmc_host[4]; +DECLARE_GLOBAL_DATA_PTR; +struct mmc mmc_dev[MAX_HOSTS]; +struct mmc_host mmc_host[MAX_HOSTS]; -/** - * Get the host address and peripheral ID for a device. Devices are numbered - * from 0 to 3. - * - * @param host Structure to fill in (base, reg, mmc_id) - * @param dev_index Device index (0-3) - */ -static void tegra_get_setup(struct mmc_host *host, int dev_index) +#ifndef CONFIG_OF_CONTROL +#error "Please enable device tree support to use this driver" +#endif + +static void mmc_set_power(struct mmc_host *host, unsigned short power) { - debug("tegra_get_setup: dev_index = %d\n", dev_index); - - switch (dev_index) { - case 1: - host->base = TEGRA_SDMMC3_BASE; - host->mmc_id = PERIPH_ID_SDMMC3; - break; - case 2: - host->base = TEGRA_SDMMC2_BASE; - host->mmc_id = PERIPH_ID_SDMMC2; - break; - case 3: - host->base = TEGRA_SDMMC1_BASE; - host->mmc_id = PERIPH_ID_SDMMC1; - break; - case 0: - default: - host->base = TEGRA_SDMMC4_BASE; - host->mmc_id = PERIPH_ID_SDMMC4; - break; + u8 pwr = 0; + debug("%s: power = %x\n", __func__, power); + + if (power != (unsigned short)-1) { + switch (1 << power) { + case MMC_VDD_165_195: + pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V1_8; + break; + case MMC_VDD_29_30: + case MMC_VDD_30_31: + pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_0; + break; + case MMC_VDD_32_33: + case MMC_VDD_33_34: + pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_3; + break; + } } + debug("%s: pwr = %X\n", __func__, pwr); - host->reg = (struct tegra_mmc *)host->base; + /* Set the bus voltage first (if any) */ + writeb(pwr, &host->reg->pwrcon); + if (pwr == 0) + return; + + /* Now enable bus power */ + pwr |= TEGRA_MMC_PWRCTL_SD_BUS_POWER; + writeb(pwr, &host->reg->pwrcon); } static void mmc_prepare_data(struct mmc_host *host, struct mmc_data *data, @@ -363,8 +365,7 @@ static void mmc_change_clock(struct mmc_host *host, uint clock) debug(" mmc_change_clock called\n"); /* - * Change Tegra SDMMCx clock divisor here. Source is 216MHz, - * PLLP_OUT0 + * Change Tegra SDMMCx clock divisor here. Source is PLLP_OUT0 */ if (clock == 0) goto out; @@ -439,7 +440,7 @@ static void mmc_set_ios(struct mmc *mmc) debug("mmc_set_ios: hostctl = %08X\n", ctrl); } -static void mmc_reset(struct mmc_host *host) +static void mmc_reset(struct mmc_host *host, struct mmc *mmc) { unsigned int timeout; debug(" mmc_reset called\n"); @@ -465,6 +466,14 @@ static void mmc_reset(struct mmc_host *host) timeout--; udelay(1000); } + + /* Set SD bus voltage & enable bus power */ + mmc_set_power(host, fls(mmc->voltages) - 1); + debug("%s: power control = %02X, host control = %02X\n", __func__, + readb(&host->reg->pwrcon), readb(&host->reg->hostctl)); + + /* Make sure SDIO pads are set up */ + pad_init_mmc(host); } static int mmc_core_init(struct mmc *mmc) @@ -473,7 +482,7 @@ static int mmc_core_init(struct mmc *mmc) unsigned int mask; debug(" mmc_core_init called\n"); - mmc_reset(host); + mmc_reset(host, mmc); host->version = readw(&host->reg->hcver); debug("host version = %x\n", host->version); @@ -518,41 +527,43 @@ int tegra_mmc_getcd(struct mmc *mmc) debug("tegra_mmc_getcd called\n"); - if (host->cd_gpio >= 0) - return !gpio_get_value(host->cd_gpio); + if (fdt_gpio_isvalid(&host->cd_gpio)) + return fdtdec_get_gpio(&host->cd_gpio); return 1; } -int tegra_mmc_init(int dev_index, int bus_width, int pwr_gpio, int cd_gpio) +static int do_mmc_init(int dev_index) { struct mmc_host *host; char gpusage[12]; /* "SD/MMCn PWR" or "SD/MMCn CD" */ struct mmc *mmc; - debug(" tegra_mmc_init: index %d, bus width %d " - "pwr_gpio %d cd_gpio %d\n", - dev_index, bus_width, pwr_gpio, cd_gpio); - + /* DT should have been read & host config filled in */ host = &mmc_host[dev_index]; + if (!host->enabled) + return -1; - host->clock = 0; - host->pwr_gpio = pwr_gpio; - host->cd_gpio = cd_gpio; - tegra_get_setup(host, dev_index); + debug(" do_mmc_init: index %d, bus width %d " + "pwr_gpio %d cd_gpio %d\n", + dev_index, host->width, + host->pwr_gpio.gpio, host->cd_gpio.gpio); + host->clock = 0; clock_start_periph_pll(host->mmc_id, CLOCK_ID_PERIPH, 20000000); - if (host->pwr_gpio >= 0) { + if (fdt_gpio_isvalid(&host->pwr_gpio)) { sprintf(gpusage, "SD/MMC%d PWR", dev_index); - gpio_request(host->pwr_gpio, gpusage); - gpio_direction_output(host->pwr_gpio, 1); + gpio_request(host->pwr_gpio.gpio, gpusage); + gpio_direction_output(host->pwr_gpio.gpio, 1); + debug(" Power GPIO name = %s\n", host->pwr_gpio.name); } - if (host->cd_gpio >= 0) { + if (fdt_gpio_isvalid(&host->cd_gpio)) { sprintf(gpusage, "SD/MMC%d CD", dev_index); - gpio_request(host->cd_gpio, gpusage); - gpio_direction_input(host->cd_gpio); + gpio_request(host->cd_gpio.gpio, gpusage); + gpio_direction_input(host->cd_gpio.gpio); + debug(" CD GPIO name = %s\n", host->cd_gpio.name); } mmc = &mmc_dev[dev_index]; @@ -563,12 +574,13 @@ int tegra_mmc_init(int dev_index, int bus_width, int pwr_gpio, int cd_gpio) mmc->set_ios = mmc_set_ios; mmc->init = mmc_core_init; mmc->getcd = tegra_mmc_getcd; + mmc->getwp = NULL; mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; mmc->host_caps = 0; - if (bus_width == 8) + if (host->width == 8) mmc->host_caps |= MMC_MODE_8BIT; - if (bus_width >= 4) + if (host->width >= 4) mmc->host_caps |= MMC_MODE_4BIT; mmc->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS | MMC_MODE_HC; @@ -577,8 +589,6 @@ int tegra_mmc_init(int dev_index, int bus_width, int pwr_gpio, int cd_gpio) * low-speed SDIO card frequency (actually 400KHz) * max freq is highest HS eMMC clock as per the SD/MMC spec * (actually 52MHz) - * Both of these are the closest equivalents w/216MHz source - * clock and Tegra SDMMC divisors. */ mmc->f_min = 375000; mmc->f_max = 48000000; @@ -587,3 +597,104 @@ int tegra_mmc_init(int dev_index, int bus_width, int pwr_gpio, int cd_gpio) return 0; } + +/** + * Get the host address and peripheral ID for a node. + * + * @param blob fdt blob + * @param node Device index (0-3) + * @param host Structure to fill in (reg, width, mmc_id) + */ +static int mmc_get_config(const void *blob, int node, struct mmc_host *host) +{ + debug("%s: node = %d\n", __func__, node); + + host->enabled = fdtdec_get_is_enabled(blob, node); + + host->reg = (struct tegra_mmc *)fdtdec_get_addr(blob, node, "reg"); + if ((fdt_addr_t)host->reg == FDT_ADDR_T_NONE) { + debug("%s: no sdmmc base reg info found\n", __func__); + return -FDT_ERR_NOTFOUND; + } + + host->mmc_id = clock_decode_periph_id(blob, node); + if (host->mmc_id == PERIPH_ID_NONE) { + debug("%s: could not decode periph id\n", __func__); + return -FDT_ERR_NOTFOUND; + } + + /* + * NOTE: mmc->bus_width is determined by mmc.c dynamically. + * TBD: Override it with this value? + */ + host->width = fdtdec_get_int(blob, node, "bus-width", 0); + if (!host->width) + debug("%s: no sdmmc width found\n", __func__); + + /* These GPIOs are optional */ + fdtdec_decode_gpio(blob, node, "cd-gpios", &host->cd_gpio); + fdtdec_decode_gpio(blob, node, "wp-gpios", &host->wp_gpio); + fdtdec_decode_gpio(blob, node, "power-gpios", &host->pwr_gpio); + + debug("%s: found controller at %p, width = %d, periph_id = %d\n", + __func__, host->reg, host->width, host->mmc_id); + return 0; +} + +/* + * Process a list of nodes, adding them to our list of SDMMC ports. + * + * @param blob fdt blob + * @param node_list list of nodes to process (any <=0 are ignored) + * @param count number of nodes to process + * @return 0 if ok, -1 on error + */ +static int process_nodes(const void *blob, int node_list[], int count) +{ + struct mmc_host *host; + int i, node; + + debug("%s: count = %d\n", __func__, count); + + /* build mmc_host[] for each controller */ + for (i = 0; i < count; i++) { + node = node_list[i]; + if (node <= 0) + continue; + + host = &mmc_host[i]; + host->id = i; + + if (mmc_get_config(blob, node, host)) { + printf("%s: failed to decode dev %d\n", __func__, i); + return -1; + } + do_mmc_init(i); + } + return 0; +} + +void tegra_mmc_init(void) +{ + int node_list[MAX_HOSTS], count; + const void *blob = gd->fdt_blob; + debug("%s entry\n", __func__); + + /* See if any Tegra30 MMC controllers are present */ + count = fdtdec_find_aliases_for_id(blob, "sdhci", + COMPAT_NVIDIA_TEGRA30_SDMMC, node_list, MAX_HOSTS); + debug("%s: count of T30 sdhci nodes is %d\n", __func__, count); + if (process_nodes(blob, node_list, count)) { + printf("%s: Error processing T30 mmc node(s)!\n", __func__); + return; + } + + /* Now look for any Tegra20 MMC controllers */ + count = fdtdec_find_aliases_for_id(blob, "sdhci", + COMPAT_NVIDIA_TEGRA20_SDMMC, node_list, MAX_HOSTS); + debug("%s: count of T20 sdhci nodes is %d\n", __func__, count); + if (process_nodes(blob, node_list, count)) { + printf("%s: Error processing T20 mmc node(s)!\n", __func__); + return; + } +} |