diff options
Diffstat (limited to 'drivers/mmc/host')
-rw-r--r-- | drivers/mmc/host/Kconfig | 20 | ||||
-rw-r--r-- | drivers/mmc/host/Makefile | 7 | ||||
-rw-r--r-- | drivers/mmc/host/at91_mci.c | 1 | ||||
-rw-r--r-- | drivers/mmc/host/imxmmc.c | 3 | ||||
-rw-r--r-- | drivers/mmc/host/mmc_spi.c | 60 | ||||
-rw-r--r-- | drivers/mmc/host/mmci.c | 62 | ||||
-rw-r--r-- | drivers/mmc/host/mmci.h | 22 | ||||
-rw-r--r-- | drivers/mmc/host/msm_sdcc.c | 62 | ||||
-rw-r--r-- | drivers/mmc/host/msm_sdcc.h | 6 | ||||
-rw-r--r-- | drivers/mmc/host/omap_hsmmc.c | 50 | ||||
-rw-r--r-- | drivers/mmc/host/s3cmci.c | 2 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci-cns3xxx.c | 97 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci-of-core.c | 12 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci-pci.c | 49 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci-pltfm.c | 17 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci-pltfm.h | 18 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci-s3c.c | 131 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci.c | 56 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci.h | 12 | ||||
-rw-r--r-- | drivers/mmc/host/sdricoh_cs.c | 5 | ||||
-rw-r--r-- | drivers/mmc/host/tmio_mmc.c | 7 | ||||
-rw-r--r-- | drivers/mmc/host/tmio_mmc.h | 13 |
22 files changed, 545 insertions, 167 deletions
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index d25e22cee4c..68d12794cfd 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -121,9 +121,18 @@ config MMC_SDHCI_PLTFM If unsure, say N. +config MMC_SDHCI_CNS3XXX + bool "SDHCI support on the Cavium Networks CNS3xxx SoC" + depends on ARCH_CNS3XXX + depends on MMC_SDHCI_PLTFM + help + This selects the SDHCI support for CNS3xxx System-on-Chip devices. + + If unsure, say N. + config MMC_SDHCI_S3C tristate "SDHCI support on Samsung S3C SoC" - depends on MMC_SDHCI && (PLAT_S3C24XX || PLAT_S3C64XX) + depends on MMC_SDHCI && PLAT_SAMSUNG help This selects the Secure Digital Host Controller Interface (SDHCI) often referrered to as the HSMMC block in some of the Samsung S3C @@ -247,12 +256,13 @@ config MMC_IMX If unsure, say N. -config MMC_MSM7X00A - tristate "Qualcomm MSM 7X00A SDCC Controller Support" - depends on MMC && ARCH_MSM && !ARCH_MSM7X30 +config MMC_MSM + tristate "Qualcomm SDCC Controller Support" + depends on MMC && ARCH_MSM help This provides support for the SD/MMC cell found in the - MSM 7X00A controllers from Qualcomm. + MSM and QSD SOCs from Qualcomm. The controller also has + support for SDIO devices. config MMC_MXC tristate "Freescale i.MX2/3 Multimedia Card Interface support" diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index f4e53c98d94..840bcb52d82 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -12,7 +12,6 @@ obj-$(CONFIG_MMC_IMX) += imxmmc.o obj-$(CONFIG_MMC_MXC) += mxcmmc.o obj-$(CONFIG_MMC_SDHCI) += sdhci.o obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o -obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-pltfm.o obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o obj-$(CONFIG_MMC_SDHCI_SPEAR) += sdhci-spear.o obj-$(CONFIG_MMC_WBSD) += wbsd.o @@ -22,7 +21,7 @@ obj-$(CONFIG_MMC_OMAP_HS) += omap_hsmmc.o obj-$(CONFIG_MMC_AT91) += at91_mci.o obj-$(CONFIG_MMC_ATMELMCI) += atmel-mci.o obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o -obj-$(CONFIG_MMC_MSM7X00A) += msm_sdcc.o +obj-$(CONFIG_MMC_MSM) += msm_sdcc.o obj-$(CONFIG_MMC_MVSDIO) += mvsdio.o obj-$(CONFIG_MMC_DAVINCI) += davinci_mmc.o obj-$(CONFIG_MMC_SPI) += mmc_spi.o @@ -38,6 +37,10 @@ obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o +obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-platform.o +sdhci-platform-y := sdhci-pltfm.o +sdhci-platform-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.o + obj-$(CONFIG_MMC_SDHCI_OF) += sdhci-of.o sdhci-of-y := sdhci-of-core.o sdhci-of-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o diff --git a/drivers/mmc/host/at91_mci.c b/drivers/mmc/host/at91_mci.c index 5f3a599ead0..87226cd202a 100644 --- a/drivers/mmc/host/at91_mci.c +++ b/drivers/mmc/host/at91_mci.c @@ -66,6 +66,7 @@ #include <linux/clk.h> #include <linux/atmel_pdc.h> #include <linux/gfp.h> +#include <linux/highmem.h> #include <linux/mmc/host.h> diff --git a/drivers/mmc/host/imxmmc.c b/drivers/mmc/host/imxmmc.c index 9a68ff4353a..5a950b16d9e 100644 --- a/drivers/mmc/host/imxmmc.c +++ b/drivers/mmc/host/imxmmc.c @@ -148,11 +148,12 @@ static int imxmci_start_clock(struct imxmci_host *host) while (delay--) { reg = readw(host->base + MMC_REG_STATUS); - if (reg & STATUS_CARD_BUS_CLK_RUN) + if (reg & STATUS_CARD_BUS_CLK_RUN) { /* Check twice before cut */ reg = readw(host->base + MMC_REG_STATUS); if (reg & STATUS_CARD_BUS_CLK_RUN) return 0; + } if (test_bit(IMXMCI_PEND_STARTED_b, &host->pending_events)) return 0; diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index 7b0f3ef50f9..62a35822003 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -182,7 +182,7 @@ mmc_spi_readbytes(struct mmc_spi_host *host, unsigned len) host->data_dma, sizeof(*host->data), DMA_FROM_DEVICE); - status = spi_sync(host->spi, &host->readback); + status = spi_sync_locked(host->spi, &host->readback); if (host->dma_dev) dma_sync_single_for_cpu(host->dma_dev, @@ -541,7 +541,7 @@ mmc_spi_command_send(struct mmc_spi_host *host, host->data_dma, sizeof(*host->data), DMA_BIDIRECTIONAL); } - status = spi_sync(host->spi, &host->m); + status = spi_sync_locked(host->spi, &host->m); if (host->dma_dev) dma_sync_single_for_cpu(host->dma_dev, @@ -685,7 +685,7 @@ mmc_spi_writeblock(struct mmc_spi_host *host, struct spi_transfer *t, host->data_dma, sizeof(*scratch), DMA_BIDIRECTIONAL); - status = spi_sync(spi, &host->m); + status = spi_sync_locked(spi, &host->m); if (status != 0) { dev_dbg(&spi->dev, "write error (%d)\n", status); @@ -822,7 +822,7 @@ mmc_spi_readblock(struct mmc_spi_host *host, struct spi_transfer *t, DMA_FROM_DEVICE); } - status = spi_sync(spi, &host->m); + status = spi_sync_locked(spi, &host->m); if (host->dma_dev) { dma_sync_single_for_cpu(host->dma_dev, @@ -1018,7 +1018,7 @@ mmc_spi_data_do(struct mmc_spi_host *host, struct mmc_command *cmd, host->data_dma, sizeof(*scratch), DMA_BIDIRECTIONAL); - tmp = spi_sync(spi, &host->m); + tmp = spi_sync_locked(spi, &host->m); if (host->dma_dev) dma_sync_single_for_cpu(host->dma_dev, @@ -1084,6 +1084,9 @@ static void mmc_spi_request(struct mmc_host *mmc, struct mmc_request *mrq) } #endif + /* request exclusive bus access */ + spi_bus_lock(host->spi->master); + /* issue command; then optionally data and stop */ status = mmc_spi_command_send(host, mrq, mrq->cmd, mrq->data != NULL); if (status == 0 && mrq->data) { @@ -1094,6 +1097,9 @@ static void mmc_spi_request(struct mmc_host *mmc, struct mmc_request *mrq) mmc_cs_off(host); } + /* release the bus */ + spi_bus_unlock(host->spi->master); + mmc_request_done(host->mmc, mrq); } @@ -1290,23 +1296,6 @@ mmc_spi_detect_irq(int irq, void *mmc) return IRQ_HANDLED; } -struct count_children { - unsigned n; - struct bus_type *bus; -}; - -static int maybe_count_child(struct device *dev, void *c) -{ - struct count_children *ccp = c; - - if (dev->bus == ccp->bus) { - if (ccp->n) - return -EBUSY; - ccp->n++; - } - return 0; -} - static int mmc_spi_probe(struct spi_device *spi) { void *ones; @@ -1338,32 +1327,6 @@ static int mmc_spi_probe(struct spi_device *spi) return status; } - /* We can use the bus safely iff nobody else will interfere with us. - * Most commands consist of one SPI message to issue a command, then - * several more to collect its response, then possibly more for data - * transfer. Clocking access to other devices during that period will - * corrupt the command execution. - * - * Until we have software primitives which guarantee non-interference, - * we'll aim for a hardware-level guarantee. - * - * REVISIT we can't guarantee another device won't be added later... - */ - if (spi->master->num_chipselect > 1) { - struct count_children cc; - - cc.n = 0; - cc.bus = spi->dev.bus; - status = device_for_each_child(spi->dev.parent, &cc, - maybe_count_child); - if (status < 0) { - dev_err(&spi->dev, "can't share SPI bus\n"); - return status; - } - - dev_warn(&spi->dev, "ASSUMING SPI bus stays unshared!\n"); - } - /* We need a supply of ones to transmit. This is the only time * the CPU touches these, so cache coherency isn't a concern. * @@ -1536,6 +1499,7 @@ static int __devexit mmc_spi_remove(struct spi_device *spi) #if defined(CONFIG_OF) static struct of_device_id mmc_spi_of_match_table[] __devinitdata = { { .compatible = "mmc-spi-slot", }, + {}, }; #endif diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 840b301b567..f2e02d7d9f3 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -41,23 +41,35 @@ static unsigned int fmax = 515633; * @clkreg: default value for MCICLOCK register * @clkreg_enable: enable value for MMCICLOCK register * @datalength_bits: number of bits in the MMCIDATALENGTH register + * @fifosize: number of bytes that can be written when MMCI_TXFIFOEMPTY + * is asserted (likewise for RX) + * @fifohalfsize: number of bytes that can be written when MCI_TXFIFOHALFEMPTY + * is asserted (likewise for RX) */ struct variant_data { unsigned int clkreg; unsigned int clkreg_enable; unsigned int datalength_bits; + unsigned int fifosize; + unsigned int fifohalfsize; }; static struct variant_data variant_arm = { + .fifosize = 16 * 4, + .fifohalfsize = 8 * 4, .datalength_bits = 16, }; static struct variant_data variant_u300 = { + .fifosize = 16 * 4, + .fifohalfsize = 8 * 4, .clkreg_enable = 1 << 13, /* HWFCEN */ .datalength_bits = 16, }; static struct variant_data variant_ux500 = { + .fifosize = 30 * 4, + .fifohalfsize = 8 * 4, .clkreg = MCI_CLK_ENABLE, .clkreg_enable = 1 << 14, /* HWFCEN */ .datalength_bits = 24, @@ -138,6 +150,7 @@ static void mmci_init_sg(struct mmci_host *host, struct mmc_data *data) static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) { + struct variant_data *variant = host->variant; unsigned int datactrl, timeout, irqmask; unsigned long long clks; void __iomem *base; @@ -173,7 +186,7 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) * If we have less than a FIFOSIZE of bytes to transfer, * trigger a PIO interrupt as soon as any data is available. */ - if (host->size < MCI_FIFOSIZE) + if (host->size < variant->fifosize) irqmask |= MCI_RXDATAAVLBLMASK; } else { /* @@ -332,13 +345,15 @@ static int mmci_pio_read(struct mmci_host *host, char *buffer, unsigned int rema static int mmci_pio_write(struct mmci_host *host, char *buffer, unsigned int remain, u32 status) { + struct variant_data *variant = host->variant; void __iomem *base = host->base; char *ptr = buffer; do { unsigned int count, maxcnt; - maxcnt = status & MCI_TXFIFOEMPTY ? MCI_FIFOSIZE : MCI_FIFOHALFSIZE; + maxcnt = status & MCI_TXFIFOEMPTY ? + variant->fifosize : variant->fifohalfsize; count = min(remain, maxcnt); writesl(base + MMCIFIFO, ptr, count >> 2); @@ -362,6 +377,7 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id) { struct mmci_host *host = dev_id; struct sg_mapping_iter *sg_miter = &host->sg_miter; + struct variant_data *variant = host->variant; void __iomem *base = host->base; unsigned long flags; u32 status; @@ -420,7 +436,7 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id) * If we're nearing the end of the read, switch to * "any data available" mode. */ - if (status & MCI_RXACTIVE && host->size < MCI_FIFOSIZE) + if (status & MCI_RXACTIVE && host->size < variant->fifosize) writel(MCI_RXDATAAVLBLMASK, base + MMCIMASK1); /* @@ -564,18 +580,23 @@ static int mmci_get_ro(struct mmc_host *mmc) if (host->gpio_wp == -ENOSYS) return -ENOSYS; - return gpio_get_value(host->gpio_wp); + return gpio_get_value_cansleep(host->gpio_wp); } static int mmci_get_cd(struct mmc_host *mmc) { struct mmci_host *host = mmc_priv(mmc); + struct mmci_platform_data *plat = host->plat; unsigned int status; - if (host->gpio_cd == -ENOSYS) - status = host->plat->status(mmc_dev(host->mmc)); - else - status = !gpio_get_value(host->gpio_cd); + if (host->gpio_cd == -ENOSYS) { + if (!plat->status) + return 1; /* Assume always present */ + + status = plat->status(mmc_dev(host->mmc)); + } else + status = !!gpio_get_value_cansleep(host->gpio_cd) + ^ plat->cd_invert; /* * Use positive logic throughout - status is zero for no card, @@ -584,6 +605,15 @@ static int mmci_get_cd(struct mmc_host *mmc) return status; } +static irqreturn_t mmci_cd_irq(int irq, void *dev_id) +{ + struct mmci_host *host = dev_id; + + mmc_detect_change(host->mmc, msecs_to_jiffies(500)); + + return IRQ_HANDLED; +} + static const struct mmc_host_ops mmci_ops = { .request = mmci_request, .set_ios = mmci_set_ios, @@ -620,6 +650,7 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id) host->gpio_wp = -ENOSYS; host->gpio_cd = -ENOSYS; + host->gpio_cd_irq = -1; host->hw_designer = amba_manf(dev); host->hw_revision = amba_rev(dev); @@ -699,7 +730,6 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id) if (host->vcc == NULL) mmc->ocr_avail = plat->ocr_mask; mmc->caps = plat->capabilities; - mmc->caps |= MMC_CAP_NEEDS_POLL; /* * We can do SGIO @@ -744,6 +774,12 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id) host->gpio_cd = plat->gpio_cd; else if (ret != -ENOSYS) goto err_gpio_cd; + + ret = request_any_context_irq(gpio_to_irq(plat->gpio_cd), + mmci_cd_irq, 0, + DRIVER_NAME " (cd)", host); + if (ret >= 0) + host->gpio_cd_irq = gpio_to_irq(plat->gpio_cd); } if (gpio_is_valid(plat->gpio_wp)) { ret = gpio_request(plat->gpio_wp, DRIVER_NAME " (wp)"); @@ -755,6 +791,10 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id) goto err_gpio_wp; } + if ((host->plat->status || host->gpio_cd != -ENOSYS) + && host->gpio_cd_irq < 0) + mmc->caps |= MMC_CAP_NEEDS_POLL; + ret = request_irq(dev->irq[0], mmci_irq, IRQF_SHARED, DRIVER_NAME " (cmd)", host); if (ret) goto unmap; @@ -781,6 +821,8 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id) if (host->gpio_wp != -ENOSYS) gpio_free(host->gpio_wp); err_gpio_wp: + if (host->gpio_cd_irq >= 0) + free_irq(host->gpio_cd_irq, host); if (host->gpio_cd != -ENOSYS) gpio_free(host->gpio_cd); err_gpio_cd: @@ -819,6 +861,8 @@ static int __devexit mmci_remove(struct amba_device *dev) if (host->gpio_wp != -ENOSYS) gpio_free(host->gpio_wp); + if (host->gpio_cd_irq >= 0) + free_irq(host->gpio_cd_irq, host); if (host->gpio_cd != -ENOSYS) gpio_free(host->gpio_cd); diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index 68970cfb81e..4ae887fc018 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -54,10 +54,16 @@ #define MCI_DPSM_MODE (1 << 2) #define MCI_DPSM_DMAENABLE (1 << 3) #define MCI_DPSM_BLOCKSIZE (1 << 4) -#define MCI_DPSM_RWSTART (1 << 8) -#define MCI_DPSM_RWSTOP (1 << 9) -#define MCI_DPSM_RWMOD (1 << 10) -#define MCI_DPSM_SDIOEN (1 << 11) +/* Control register extensions in the ST Micro U300 and Ux500 versions */ +#define MCI_ST_DPSM_RWSTART (1 << 8) +#define MCI_ST_DPSM_RWSTOP (1 << 9) +#define MCI_ST_DPSM_RWMOD (1 << 10) +#define MCI_ST_DPSM_SDIOEN (1 << 11) +/* Control register extensions in the ST Micro Ux500 versions */ +#define MCI_ST_DPSM_DMAREQCTL (1 << 12) +#define MCI_ST_DPSM_DBOOTMODEEN (1 << 13) +#define MCI_ST_DPSM_BUSYMODE (1 << 14) +#define MCI_ST_DPSM_DDRMODE (1 << 15) #define MMCIDATACNT 0x030 #define MMCISTATUS 0x034 @@ -133,13 +139,6 @@ MCI_DATATIMEOUTMASK|MCI_TXUNDERRUNMASK|MCI_RXOVERRUNMASK| \ MCI_CMDRESPENDMASK|MCI_CMDSENTMASK|MCI_DATABLOCKENDMASK) -/* - * The size of the FIFO in bytes. - */ -#define MCI_FIFOSIZE (16*4) - -#define MCI_FIFOHALFSIZE (MCI_FIFOSIZE / 2) - #define NR_SG 16 struct clk; @@ -154,6 +153,7 @@ struct mmci_host { struct clk *clk; int gpio_cd; int gpio_wp; + int gpio_cd_irq; unsigned int data_xfered; diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 24e09454e52..ff7752348b1 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -160,18 +160,7 @@ msmsdcc_stop_data(struct msmsdcc_host *host) uint32_t msmsdcc_fifo_addr(struct msmsdcc_host *host) { - switch (host->pdev_id) { - case 1: - return MSM_SDC1_PHYS + MMCIFIFO; - case 2: - return MSM_SDC2_PHYS + MMCIFIFO; - case 3: - return MSM_SDC3_PHYS + MMCIFIFO; - case 4: - return MSM_SDC4_PHYS + MMCIFIFO; - } - BUG(); - return 0; + return host->memres->start + MMCIFIFO; } static inline void @@ -1057,26 +1046,10 @@ msmsdcc_init_dma(struct msmsdcc_host *host) return 0; } -#ifdef CONFIG_MMC_MSM7X00A_RESUME_IN_WQ -static void -do_resume_work(struct work_struct *work) -{ - struct msmsdcc_host *host = - container_of(work, struct msmsdcc_host, resume_task); - struct mmc_host *mmc = host->mmc; - - if (mmc) { - mmc_resume_host(mmc); - if (host->stat_irq) - enable_irq(host->stat_irq); - } -} -#endif - static int msmsdcc_probe(struct platform_device *pdev) { - struct mmc_platform_data *plat = pdev->dev.platform_data; + struct msm_mmc_platform_data *plat = pdev->dev.platform_data; struct msmsdcc_host *host; struct mmc_host *mmc; struct resource *cmd_irqres = NULL; @@ -1145,15 +1118,6 @@ msmsdcc_probe(struct platform_device *pdev) host->dmares = dmares; spin_lock_init(&host->lock); -#ifdef CONFIG_MMC_EMBEDDED_SDIO - if (plat->embedded_sdio) - mmc_set_embedded_sdio_data(mmc, - &plat->embedded_sdio->cis, - &plat->embedded_sdio->cccr, - plat->embedded_sdio->funcs, - plat->embedded_sdio->num_funcs); -#endif - /* * Setup DMA */ @@ -1314,6 +1278,24 @@ msmsdcc_probe(struct platform_device *pdev) return ret; } +#ifdef CONFIG_PM +#ifdef CONFIG_MMC_MSM7X00A_RESUME_IN_WQ +static void +do_resume_work(struct work_struct *work) +{ + struct msmsdcc_host *host = + container_of(work, struct msmsdcc_host, resume_task); + struct mmc_host *mmc = host->mmc; + + if (mmc) { + mmc_resume_host(mmc); + if (host->stat_irq) + enable_irq(host->stat_irq); + } +} +#endif + + static int msmsdcc_suspend(struct platform_device *dev, pm_message_t state) { @@ -1358,6 +1340,10 @@ msmsdcc_resume(struct platform_device *dev) } return 0; } +#else +#define msmsdcc_suspend 0 +#define msmsdcc_resume 0 +#endif static struct platform_driver msmsdcc_driver = { .probe = msmsdcc_probe, diff --git a/drivers/mmc/host/msm_sdcc.h b/drivers/mmc/host/msm_sdcc.h index da0039c9285..ff2b0f74f6f 100644 --- a/drivers/mmc/host/msm_sdcc.h +++ b/drivers/mmc/host/msm_sdcc.h @@ -225,7 +225,7 @@ struct msmsdcc_host { u32 pwr; u32 saved_irq0mask; /* MMCIMASK0 reg value */ - struct mmc_platform_data *plat; + struct msm_mmc_platform_data *plat; struct timer_list timer; unsigned int oldstat; @@ -235,10 +235,6 @@ struct msmsdcc_host { int cmdpoll; struct msmsdcc_stats stats; -#ifdef CONFIG_MMC_MSM7X00A_RESUME_IN_WQ - struct work_struct resume_task; -#endif - /* Command parameters */ unsigned int cmd_timeout; unsigned int cmd_pio_irqmask; diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index b032828c612..4526d2791f2 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -28,6 +28,7 @@ #include <linux/clk.h> #include <linux/mmc/host.h> #include <linux/mmc/core.h> +#include <linux/mmc/mmc.h> #include <linux/io.h> #include <linux/semaphore.h> #include <linux/gpio.h> @@ -78,6 +79,7 @@ #define INT_EN_MASK 0x307F0033 #define BWR_ENABLE (1 << 4) #define BRR_ENABLE (1 << 5) +#define DTO_ENABLE (1 << 20) #define INIT_STREAM (1 << 1) #define DP_SELECT (1 << 21) #define DDIR (1 << 4) @@ -523,7 +525,8 @@ static void omap_hsmmc_stop_clock(struct omap_hsmmc_host *host) dev_dbg(mmc_dev(host->mmc), "MMC Clock is not stoped\n"); } -static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host) +static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host, + struct mmc_command *cmd) { unsigned int irq_mask; @@ -532,6 +535,10 @@ static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host) else irq_mask = INT_EN_MASK; + /* Disable timeout for erases */ + if (cmd->opcode == MMC_ERASE) + irq_mask &= ~DTO_ENABLE; + OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR); OMAP_HSMMC_WRITE(host->base, ISE, irq_mask); OMAP_HSMMC_WRITE(host->base, IE, irq_mask); @@ -782,7 +789,7 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd, mmc_hostname(host->mmc), cmd->opcode, cmd->arg); host->cmd = cmd; - omap_hsmmc_enable_irq(host); + omap_hsmmc_enable_irq(host, cmd); host->response_busy = 0; if (cmd->flags & MMC_RSP_PRESENT) { @@ -1273,8 +1280,11 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data) struct mmc_data *data = host->mrq->data; int dma_ch, req_in_progress; - if (ch_status & OMAP2_DMA_MISALIGNED_ERR_IRQ) - dev_dbg(mmc_dev(host->mmc), "MISALIGNED_ADRS_ERR\n"); + if (!(ch_status & OMAP_DMA_BLOCK_IRQ)) { + dev_warn(mmc_dev(host->mmc), "unexpected dma status %x\n", + ch_status); + return; + } spin_lock(&host->irq_lock); if (host->dma_ch < 0) { @@ -1598,6 +1608,14 @@ static int omap_hsmmc_get_ro(struct mmc_host *mmc) return mmc_slot(host).get_ro(host->dev, 0); } +static void omap_hsmmc_init_card(struct mmc_host *mmc, struct mmc_card *card) +{ + struct omap_hsmmc_host *host = mmc_priv(mmc); + + if (mmc_slot(host).init_card) + mmc_slot(host).init_card(card); +} + static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host) { u32 hctl, capa, value; @@ -1869,6 +1887,7 @@ static const struct mmc_host_ops omap_hsmmc_ops = { .set_ios = omap_hsmmc_set_ios, .get_cd = omap_hsmmc_get_cd, .get_ro = omap_hsmmc_get_ro, + .init_card = omap_hsmmc_init_card, /* NYET -- enable_sdio_irq */ }; @@ -1879,6 +1898,7 @@ static const struct mmc_host_ops omap_hsmmc_ps_ops = { .set_ios = omap_hsmmc_set_ios, .get_cd = omap_hsmmc_get_cd, .get_ro = omap_hsmmc_get_ro, + .init_card = omap_hsmmc_init_card, /* NYET -- enable_sdio_irq */ }; @@ -2094,12 +2114,25 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) mmc->max_seg_size = mmc->max_req_size; mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED | - MMC_CAP_WAIT_WHILE_BUSY; + MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_ERASE; - if (mmc_slot(host).wires >= 8) + switch (mmc_slot(host).wires) { + case 8: mmc->caps |= MMC_CAP_8_BIT_DATA; - else if (mmc_slot(host).wires >= 4) + /* Fall through */ + case 4: mmc->caps |= MMC_CAP_4_BIT_DATA; + break; + case 1: + /* Nothing to crib here */ + case 0: + /* Assuming nothing was given by board, Core use's 1-Bit */ + break; + default: + /* Completely unexpected.. Core goes with 1-Bit Width */ + dev_crit(mmc_dev(host->mmc), "Invalid width %d\n used!" + "using 1 instead\n", mmc_slot(host).wires); + } if (mmc_slot(host).nonremovable) mmc->caps |= MMC_CAP_NONREMOVABLE; @@ -2272,7 +2305,6 @@ static int omap_hsmmc_suspend(struct device *dev) int ret = 0; struct platform_device *pdev = to_platform_device(dev); struct omap_hsmmc_host *host = platform_get_drvdata(pdev); - pm_message_t state = PMSG_SUSPEND; /* unused by MMC core */ if (host && host->suspended) return 0; @@ -2291,8 +2323,8 @@ static int omap_hsmmc_suspend(struct device *dev) } } cancel_work_sync(&host->mmc_carddetect_work); - mmc_host_enable(host->mmc); ret = mmc_suspend_host(host->mmc); + mmc_host_enable(host->mmc); if (ret == 0) { omap_hsmmc_disable_irq(host); OMAP_HSMMC_WRITE(host->base, HCTL, diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c index 2e16e0a90a5..976330de379 100644 --- a/drivers/mmc/host/s3cmci.c +++ b/drivers/mmc/host/s3cmci.c @@ -1600,7 +1600,7 @@ static int __devinit s3cmci_probe(struct platform_device *pdev) host->pio_active = XFER_NONE; #ifdef CONFIG_MMC_S3C_PIODMA - host->dodma = host->pdata->dma; + host->dodma = host->pdata->use_dma; #endif host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); diff --git a/drivers/mmc/host/sdhci-cns3xxx.c b/drivers/mmc/host/sdhci-cns3xxx.c new file mode 100644 index 00000000000..b7050b380d5 --- /dev/null +++ b/drivers/mmc/host/sdhci-cns3xxx.c @@ -0,0 +1,97 @@ +/* + * SDHCI support for CNS3xxx SoC + * + * Copyright 2008 Cavium Networks + * Copyright 2010 MontaVista Software, LLC. + * + * Authors: Scott Shu + * Anton Vorontsov <avorontsov@mvista.com> + * + * 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. + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/mmc/host.h> +#include <linux/sdhci-pltfm.h> +#include <mach/cns3xxx.h> +#include "sdhci.h" +#include "sdhci-pltfm.h" + +static unsigned int sdhci_cns3xxx_get_max_clk(struct sdhci_host *host) +{ + return 150000000; +} + +static void sdhci_cns3xxx_set_clock(struct sdhci_host *host, unsigned int clock) +{ + struct device *dev = mmc_dev(host->mmc); + int div = 1; + u16 clk; + unsigned long timeout; + + if (clock == host->clock) + return; + + sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); + + if (clock == 0) + goto out; + + while (host->max_clk / div > clock) { + /* + * On CNS3xxx divider grows linearly up to 4, and then + * exponentially up to 256. + */ + if (div < 4) + div += 1; + else if (div < 256) + div *= 2; + else + break; + } + + dev_dbg(dev, "desired SD clock: %d, actual: %d\n", + clock, host->max_clk / div); + + /* Divide by 3 is special. */ + if (div != 3) + div >>= 1; + + clk = div << SDHCI_DIVIDER_SHIFT; + clk |= SDHCI_CLOCK_INT_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + + timeout = 20; + while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL)) + & SDHCI_CLOCK_INT_STABLE)) { + if (timeout == 0) { + dev_warn(dev, "clock is unstable"); + break; + } + timeout--; + mdelay(1); + } + + clk |= SDHCI_CLOCK_CARD_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); +out: + host->clock = clock; +} + +static struct sdhci_ops sdhci_cns3xxx_ops = { + .get_max_clock = sdhci_cns3xxx_get_max_clk, + .set_clock = sdhci_cns3xxx_set_clock, +}; + +struct sdhci_pltfm_data sdhci_cns3xxx_pdata = { + .ops = &sdhci_cns3xxx_ops, + .quirks = SDHCI_QUIRK_BROKEN_DMA | + SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | + SDHCI_QUIRK_INVERTED_WRITE_PROTECT | + SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | + SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | + SDHCI_QUIRK_NONSTANDARD_CLOCK, +}; diff --git a/drivers/mmc/host/sdhci-of-core.c b/drivers/mmc/host/sdhci-of-core.c index a2e9820cd42..c51b71174c1 100644 --- a/drivers/mmc/host/sdhci-of-core.c +++ b/drivers/mmc/host/sdhci-of-core.c @@ -85,14 +85,14 @@ void sdhci_be32bs_writeb(struct sdhci_host *host, u8 val, int reg) #ifdef CONFIG_PM -static int sdhci_of_suspend(struct of_device *ofdev, pm_message_t state) +static int sdhci_of_suspend(struct platform_device *ofdev, pm_message_t state) { struct sdhci_host *host = dev_get_drvdata(&ofdev->dev); return mmc_suspend_host(host->mmc); } -static int sdhci_of_resume(struct of_device *ofdev) +static int sdhci_of_resume(struct platform_device *ofdev) { struct sdhci_host *host = dev_get_drvdata(&ofdev->dev); @@ -115,7 +115,7 @@ static bool __devinit sdhci_of_wp_inverted(struct device_node *np) return machine_is(mpc837x_rdb) || machine_is(mpc837x_mds); } -static int __devinit sdhci_of_probe(struct of_device *ofdev, +static int __devinit sdhci_of_probe(struct platform_device *ofdev, const struct of_device_id *match) { struct device_node *np = ofdev->dev.of_node; @@ -154,6 +154,10 @@ static int __devinit sdhci_of_probe(struct of_device *ofdev, host->ops = &sdhci_of_data->ops; } + if (of_get_property(np, "sdhci,auto-cmd12", NULL)) + host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12; + + if (of_get_property(np, "sdhci,1-bit-only", NULL)) host->quirks |= SDHCI_QUIRK_FORCE_1_BIT_DATA; @@ -179,7 +183,7 @@ err_addr_map: return ret; } -static int __devexit sdhci_of_remove(struct of_device *ofdev) +static int __devexit sdhci_of_remove(struct platform_device *ofdev) { struct sdhci_host *host = dev_get_drvdata(&ofdev->dev); diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 65483fdea45..e8aa99deae9 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -17,6 +17,7 @@ #include <linux/pci.h> #include <linux/dma-mapping.h> #include <linux/slab.h> +#include <linux/device.h> #include <linux/mmc/host.h> @@ -84,7 +85,30 @@ static int ricoh_probe(struct sdhci_pci_chip *chip) if (chip->pdev->subsystem_vendor == PCI_VENDOR_ID_SAMSUNG || chip->pdev->subsystem_vendor == PCI_VENDOR_ID_SONY) chip->quirks |= SDHCI_QUIRK_NO_CARD_NO_RESET; + return 0; +} + +static int ricoh_mmc_probe_slot(struct sdhci_pci_slot *slot) +{ + slot->host->caps = + ((0x21 << SDHCI_TIMEOUT_CLK_SHIFT) + & SDHCI_TIMEOUT_CLK_MASK) | + + ((0x21 << SDHCI_CLOCK_BASE_SHIFT) + & SDHCI_CLOCK_BASE_MASK) | + SDHCI_TIMEOUT_CLK_UNIT | + SDHCI_CAN_VDD_330 | + SDHCI_CAN_DO_SDMA; + return 0; +} + +static int ricoh_mmc_resume(struct sdhci_pci_chip *chip) +{ + /* Apply a delay to allow controller to settle */ + /* Otherwise it becomes confused if card state changed + during suspend */ + msleep(500); return 0; } @@ -95,6 +119,15 @@ static const struct sdhci_pci_fixes sdhci_ricoh = { SDHCI_QUIRK_CLOCK_BEFORE_RESET, }; +static const struct sdhci_pci_fixes sdhci_ricoh_mmc = { + .probe_slot = ricoh_mmc_probe_slot, + .resume = ricoh_mmc_resume, + .quirks = SDHCI_QUIRK_32BIT_DMA_ADDR | + SDHCI_QUIRK_CLOCK_BEFORE_RESET | + SDHCI_QUIRK_NO_CARD_NO_RESET | + SDHCI_QUIRK_MISSING_CAPS +}; + static const struct sdhci_pci_fixes sdhci_ene_712 = { .quirks = SDHCI_QUIRK_SINGLE_POWER_WRITE | SDHCI_QUIRK_BROKEN_DMA, @@ -374,6 +407,22 @@ static const struct pci_device_id pci_ids[] __devinitdata = { }, { + .vendor = PCI_VENDOR_ID_RICOH, + .device = 0x843, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_ricoh_mmc, + }, + + { + .vendor = PCI_VENDOR_ID_RICOH, + .device = 0xe822, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_ricoh_mmc, + }, + + { .vendor = PCI_VENDOR_ID_ENE, .device = PCI_DEVICE_ID_ENE_CB712_SD, .subvendor = PCI_ANY_ID, diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index b6ee0d71969..e045e3c61dd 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -24,6 +24,7 @@ #include <linux/delay.h> #include <linux/highmem.h> +#include <linux/mod_devicetable.h> #include <linux/platform_device.h> #include <linux/mmc/host.h> @@ -32,6 +33,7 @@ #include <linux/sdhci-pltfm.h> #include "sdhci.h" +#include "sdhci-pltfm.h" /*****************************************************************************\ * * @@ -51,10 +53,14 @@ static struct sdhci_ops sdhci_pltfm_ops = { static int __devinit sdhci_pltfm_probe(struct platform_device *pdev) { struct sdhci_pltfm_data *pdata = pdev->dev.platform_data; + const struct platform_device_id *platid = platform_get_device_id(pdev); struct sdhci_host *host; struct resource *iomem; int ret; + if (!pdata && platid && platid->driver_data) + pdata = (void *)platid->driver_data; + iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!iomem) { ret = -ENOMEM; @@ -150,6 +156,15 @@ static int __devexit sdhci_pltfm_remove(struct platform_device *pdev) return 0; } +static const struct platform_device_id sdhci_pltfm_ids[] = { + { "sdhci", }, +#ifdef CONFIG_MMC_SDHCI_CNS3XXX + { "sdhci-cns3xxx", (kernel_ulong_t)&sdhci_cns3xxx_pdata }, +#endif + { }, +}; +MODULE_DEVICE_TABLE(platform, sdhci_pltfm_ids); + static struct platform_driver sdhci_pltfm_driver = { .driver = { .name = "sdhci", @@ -157,6 +172,7 @@ static struct platform_driver sdhci_pltfm_driver = { }, .probe = sdhci_pltfm_probe, .remove = __devexit_p(sdhci_pltfm_remove), + .id_table = sdhci_pltfm_ids, }; /*****************************************************************************\ @@ -181,4 +197,3 @@ module_exit(sdhci_drv_exit); MODULE_DESCRIPTION("Secure Digital Host Controller Interface platform driver"); MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:sdhci"); diff --git a/drivers/mmc/host/sdhci-pltfm.h b/drivers/mmc/host/sdhci-pltfm.h new file mode 100644 index 00000000000..900f32902f7 --- /dev/null +++ b/drivers/mmc/host/sdhci-pltfm.h @@ -0,0 +1,18 @@ +/* + * Copyright 2010 MontaVista Software, LLC. + * + * Author: Anton Vorontsov <avorontsov@ru.mvista.com> + * + * 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. + */ + +#ifndef _DRIVERS_MMC_SDHCI_PLTFM_H +#define _DRIVERS_MMC_SDHCI_PLTFM_H + +#include <linux/sdhci-pltfm.h> + +extern struct sdhci_pltfm_data sdhci_cns3xxx_pdata; + +#endif /* _DRIVERS_MMC_SDHCI_PLTFM_H */ diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index ad30f074ee1..aacb862ecc8 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -18,6 +18,7 @@ #include <linux/slab.h> #include <linux/clk.h> #include <linux/io.h> +#include <linux/gpio.h> #include <linux/mmc/host.h> @@ -44,6 +45,8 @@ struct sdhci_s3c { struct resource *ioarea; struct s3c_sdhci_platdata *pdata; unsigned int cur_clk; + int ext_cd_irq; + int ext_cd_gpio; struct clk *clk_io; struct clk *clk_bus[MAX_BUS_CLK]; @@ -110,11 +113,6 @@ static unsigned int sdhci_s3c_get_max_clk(struct sdhci_host *host) return max; } -static unsigned int sdhci_s3c_get_timeout_clk(struct sdhci_host *host) -{ - return sdhci_s3c_get_max_clk(host) / 1000000; -} - /** * sdhci_s3c_consider_clock - consider one the bus clocks for current setting * @ourhost: Our SDHCI instance. @@ -188,7 +186,6 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock) ourhost->cur_clk = best_src; host->max_clk = clk_get_rate(clk); - host->timeout_clk = sdhci_s3c_get_timeout_clk(host); ctrl = readl(host->ioaddr + S3C_SDHCI_CONTROL2); ctrl &= ~S3C_SDHCI_CTRL2_SELBASECLK_MASK; @@ -209,12 +206,95 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock) } } +/** + * sdhci_s3c_get_min_clock - callback to get minimal supported clock value + * @host: The SDHCI host being queried + * + * To init mmc host properly a minimal clock value is needed. For high system + * bus clock's values the standard formula gives values out of allowed range. + * The clock still can be set to lower values, if clock source other then + * system bus is selected. +*/ +static unsigned int sdhci_s3c_get_min_clock(struct sdhci_host *host) +{ + struct sdhci_s3c *ourhost = to_s3c(host); + unsigned int delta, min = UINT_MAX; + int src; + + for (src = 0; src < MAX_BUS_CLK; src++) { + delta = sdhci_s3c_consider_clock(ourhost, src, 0); + if (delta == UINT_MAX) + continue; + /* delta is a negative value in this case */ + if (-delta < min) + min = -delta; + } + return min; +} + static struct sdhci_ops sdhci_s3c_ops = { .get_max_clock = sdhci_s3c_get_max_clk, - .get_timeout_clock = sdhci_s3c_get_timeout_clk, .set_clock = sdhci_s3c_set_clock, + .get_min_clock = sdhci_s3c_get_min_clock, }; +static void sdhci_s3c_notify_change(struct platform_device *dev, int state) +{ + struct sdhci_host *host = platform_get_drvdata(dev); + unsigned long flags; + + if (host) { + spin_lock_irqsave(&host->lock, flags); + if (state) { + dev_dbg(&dev->dev, "card inserted.\n"); + host->flags &= ~SDHCI_DEVICE_DEAD; + host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; + } else { + dev_dbg(&dev->dev, "card removed.\n"); + host->flags |= SDHCI_DEVICE_DEAD; + host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION; + } + tasklet_schedule(&host->card_tasklet); + spin_unlock_irqrestore(&host->lock, flags); + } +} + +static irqreturn_t sdhci_s3c_gpio_card_detect_thread(int irq, void *dev_id) +{ + struct sdhci_s3c *sc = dev_id; + int status = gpio_get_value(sc->ext_cd_gpio); + if (sc->pdata->ext_cd_gpio_invert) + status = !status; + sdhci_s3c_notify_change(sc->pdev, status); + return IRQ_HANDLED; +} + +static void sdhci_s3c_setup_card_detect_gpio(struct sdhci_s3c *sc) +{ + struct s3c_sdhci_platdata *pdata = sc->pdata; + struct device *dev = &sc->pdev->dev; + + if (gpio_request(pdata->ext_cd_gpio, "SDHCI EXT CD") == 0) { + sc->ext_cd_gpio = pdata->ext_cd_gpio; + sc->ext_cd_irq = gpio_to_irq(pdata->ext_cd_gpio); + if (sc->ext_cd_irq && + request_threaded_irq(sc->ext_cd_irq, NULL, + sdhci_s3c_gpio_card_detect_thread, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + dev_name(dev), sc) == 0) { + int status = gpio_get_value(sc->ext_cd_gpio); + if (pdata->ext_cd_gpio_invert) + status = !status; + sdhci_s3c_notify_change(sc->pdev, status); + } else { + dev_warn(dev, "cannot request irq for card detect\n"); + sc->ext_cd_irq = 0; + } + } else { + dev_err(dev, "cannot request gpio for card detect\n"); + } +} + static int __devinit sdhci_s3c_probe(struct platform_device *pdev) { struct s3c_sdhci_platdata *pdata = pdev->dev.platform_data; @@ -252,6 +332,7 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) sc->host = host; sc->pdev = pdev; sc->pdata = pdata; + sc->ext_cd_gpio = -1; /* invalid gpio number */ platform_set_drvdata(pdev, host); @@ -318,6 +399,7 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) /* Setup quirks for the controller */ host->quirks |= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC; + host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT; #ifndef CONFIG_MMC_SDHCI_S3C_DMA @@ -332,15 +414,34 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) * SDHCI block, or a missing configuration that needs to be set. */ host->quirks |= SDHCI_QUIRK_NO_BUSY_IRQ; + if (pdata->cd_type == S3C_SDHCI_CD_NONE || + pdata->cd_type == S3C_SDHCI_CD_PERMANENT) + host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; + + if (pdata->cd_type == S3C_SDHCI_CD_PERMANENT) + host->mmc->caps = MMC_CAP_NONREMOVABLE; + host->quirks |= (SDHCI_QUIRK_32BIT_DMA_ADDR | SDHCI_QUIRK_32BIT_DMA_SIZE); + /* HSMMC on Samsung SoCs uses SDCLK as timeout clock */ + host->quirks |= SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK; + ret = sdhci_add_host(host); if (ret) { dev_err(dev, "sdhci_add_host() failed\n"); goto err_add_host; } + /* The following two methods of card detection might call + sdhci_s3c_notify_change() immediately, so they can be called + only after sdhci_add_host(). Setup errors are ignored. */ + if (pdata->cd_type == S3C_SDHCI_CD_EXTERNAL && pdata->ext_cd_init) + pdata->ext_cd_init(&sdhci_s3c_notify_change); + if (pdata->cd_type == S3C_SDHCI_CD_GPIO && + gpio_is_valid(pdata->ext_cd_gpio)) + sdhci_s3c_setup_card_detect_gpio(sc); + return 0; err_add_host: @@ -365,15 +466,27 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) static int __devexit sdhci_s3c_remove(struct platform_device *pdev) { + struct s3c_sdhci_platdata *pdata = pdev->dev.platform_data; struct sdhci_host *host = platform_get_drvdata(pdev); struct sdhci_s3c *sc = sdhci_priv(host); int ptr; + if (pdata->cd_type == S3C_SDHCI_CD_EXTERNAL && pdata->ext_cd_cleanup) + pdata->ext_cd_cleanup(&sdhci_s3c_notify_change); + + if (sc->ext_cd_irq) + free_irq(sc->ext_cd_irq, sc); + + if (gpio_is_valid(sc->ext_cd_gpio)) + gpio_free(sc->ext_cd_gpio); + sdhci_remove_host(host, 1); for (ptr = 0; ptr < 3; ptr++) { - clk_disable(sc->clk_bus[ptr]); - clk_put(sc->clk_bus[ptr]); + if (sc->clk_bus[ptr]) { + clk_disable(sc->clk_bus[ptr]); + clk_put(sc->clk_bus[ptr]); + } } clk_disable(sc->clk_io); clk_put(sc->clk_io); diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index c6d1bd8d4ac..401527d273b 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -19,6 +19,7 @@ #include <linux/dma-mapping.h> #include <linux/slab.h> #include <linux/scatterlist.h> +#include <linux/regulator/consumer.h> #include <linux/leds.h> @@ -817,8 +818,12 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host, WARN_ON(!host->data); mode = SDHCI_TRNS_BLK_CNT_EN; - if (data->blocks > 1) - mode |= SDHCI_TRNS_MULTI; + if (data->blocks > 1) { + if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12) + mode |= SDHCI_TRNS_MULTI | SDHCI_TRNS_ACMD12; + else + mode |= SDHCI_TRNS_MULTI; + } if (data->flags & MMC_DATA_READ) mode |= SDHCI_TRNS_READ; if (host->flags & SDHCI_REQ_USE_DMA) @@ -1108,6 +1113,12 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) #ifndef SDHCI_USE_LEDS_CLASS sdhci_activate_led(host); #endif + if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12) { + if (mrq->stop) { + mrq->data->stop = NULL; + mrq->stop = NULL; + } + } host->mrq = mrq; @@ -1159,12 +1170,18 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); + if (ios->bus_width == MMC_BUS_WIDTH_8) + ctrl |= SDHCI_CTRL_8BITBUS; + else + ctrl &= ~SDHCI_CTRL_8BITBUS; + if (ios->bus_width == MMC_BUS_WIDTH_4) ctrl |= SDHCI_CTRL_4BITBUS; else ctrl &= ~SDHCI_CTRL_4BITBUS; - if (ios->timing == MMC_TIMING_SD_HS) + if (ios->timing == MMC_TIMING_SD_HS && + !(host->quirks & SDHCI_QUIRK_NO_HISPD_BIT)) ctrl |= SDHCI_CTRL_HISPD; else ctrl &= ~SDHCI_CTRL_HISPD; @@ -1603,7 +1620,10 @@ int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state) free_irq(host->irq, host); - return 0; + if (host->vmmc) + ret = regulator_disable(host->vmmc); + + return ret; } EXPORT_SYMBOL_GPL(sdhci_suspend_host); @@ -1612,6 +1632,13 @@ int sdhci_resume_host(struct sdhci_host *host) { int ret; + if (host->vmmc) { + int ret = regulator_enable(host->vmmc); + if (ret) + return ret; + } + + if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { if (host->ops->enable_dma) host->ops->enable_dma(host); @@ -1687,7 +1714,8 @@ int sdhci_add_host(struct sdhci_host *host) host->version); } - caps = sdhci_readl(host, SDHCI_CAPABILITIES); + caps = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ? host->caps : + sdhci_readl(host, SDHCI_CAPABILITIES); if (host->quirks & SDHCI_QUIRK_FORCE_DMA) host->flags |= SDHCI_USE_SDMA; @@ -1785,13 +1813,12 @@ int sdhci_add_host(struct sdhci_host *host) * Set host parameters. */ mmc->ops = &sdhci_ops; - if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK && - host->ops->set_clock && host->ops->get_min_clock) + if (host->ops->get_min_clock) mmc->f_min = host->ops->get_min_clock(host); else mmc->f_min = host->max_clk / 256; mmc->f_max = host->max_clk; - mmc->caps = MMC_CAP_SDIO_IRQ; + mmc->caps |= MMC_CAP_SDIO_IRQ; if (!(host->quirks & SDHCI_QUIRK_FORCE_1_BIT_DATA)) mmc->caps |= MMC_CAP_4_BIT_DATA; @@ -1884,6 +1911,14 @@ int sdhci_add_host(struct sdhci_host *host) if (ret) goto untasklet; + host->vmmc = regulator_get(mmc_dev(mmc), "vmmc"); + if (IS_ERR(host->vmmc)) { + printk(KERN_INFO "%s: no vmmc regulator found\n", mmc_hostname(mmc)); + host->vmmc = NULL; + } else { + regulator_enable(host->vmmc); + } + sdhci_init(host, 0); #ifdef CONFIG_MMC_DEBUG @@ -1968,6 +2003,11 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) tasklet_kill(&host->card_tasklet); tasklet_kill(&host->finish_tasklet); + if (host->vmmc) { + regulator_disable(host->vmmc); + regulator_put(host->vmmc); + } + kfree(host->adma_desc); kfree(host->align_buffer); diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index c8468134adc..d316bc79b63 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -72,6 +72,7 @@ #define SDHCI_CTRL_ADMA1 0x08 #define SDHCI_CTRL_ADMA32 0x10 #define SDHCI_CTRL_ADMA64 0x18 +#define SDHCI_CTRL_8BITBUS 0x20 #define SDHCI_POWER_CONTROL 0x29 #define SDHCI_POWER_ON 0x01 @@ -240,12 +241,20 @@ struct sdhci_host { #define SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN (1<<25) /* Controller cannot support End Attribute in NOP ADMA descriptor */ #define SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC (1<<26) +/* Controller is missing device caps. Use caps provided by host */ +#define SDHCI_QUIRK_MISSING_CAPS (1<<27) +/* Controller uses Auto CMD12 command to stop the transfer */ +#define SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12 (1<<28) +/* Controller doesn't have HISPD bit field in HI-SPEED SD card */ +#define SDHCI_QUIRK_NO_HISPD_BIT (1<<29) int irq; /* Device IRQ */ void __iomem * ioaddr; /* Mapped address */ const struct sdhci_ops *ops; /* Low level hw interface */ + struct regulator *vmmc; /* Power regulator */ + /* Internal data */ struct mmc_host *mmc; /* MMC structure */ u64 dma_mask; /* custom DMA mask */ @@ -292,6 +301,8 @@ struct sdhci_host { struct timer_list timer; /* Timer for timeouts */ + unsigned int caps; /* Alternative capabilities */ + unsigned long private[0] ____cacheline_aligned; }; @@ -407,6 +418,7 @@ static inline void *sdhci_priv(struct sdhci_host *host) return (void *)host->private; } +extern void sdhci_card_detect(struct sdhci_host *host); extern int sdhci_add_host(struct sdhci_host *host); extern void sdhci_remove_host(struct sdhci_host *host, int dead); diff --git a/drivers/mmc/host/sdricoh_cs.c b/drivers/mmc/host/sdricoh_cs.c index 7aa65bb2af4..f472c2714eb 100644 --- a/drivers/mmc/host/sdricoh_cs.c +++ b/drivers/mmc/host/sdricoh_cs.c @@ -30,7 +30,6 @@ #include <linux/ioport.h> #include <linux/scatterlist.h> -#include <pcmcia/cs.h> #include <pcmcia/cistpl.h> #include <pcmcia/ds.h> #include <linux/io.h> @@ -536,9 +535,7 @@ static int sdricoh_pcmcia_resume(struct pcmcia_device *link) #endif static struct pcmcia_driver sdricoh_driver = { - .drv = { - .name = DRIVER_NAME, - }, + .name = DRIVER_NAME, .probe = sdricoh_pcmcia_probe, .remove = sdricoh_pcmcia_detach, .id_table = pcmcia_ids, diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c index ee7d0a5a51c..69d98e3bf6a 100644 --- a/drivers/mmc/host/tmio_mmc.c +++ b/drivers/mmc/host/tmio_mmc.c @@ -164,6 +164,7 @@ tmio_mmc_start_command(struct tmio_mmc_host *host, struct mmc_command *cmd) static void tmio_mmc_pio_irq(struct tmio_mmc_host *host) { struct mmc_data *data = host->data; + void *sg_virt; unsigned short *buf; unsigned int count; unsigned long flags; @@ -173,8 +174,8 @@ static void tmio_mmc_pio_irq(struct tmio_mmc_host *host) return; } - buf = (unsigned short *)(tmio_mmc_kmap_atomic(host, &flags) + - host->sg_off); + sg_virt = tmio_mmc_kmap_atomic(host->sg_ptr, &flags); + buf = (unsigned short *)(sg_virt + host->sg_off); count = host->sg_ptr->length - host->sg_off; if (count > data->blksz) @@ -191,7 +192,7 @@ static void tmio_mmc_pio_irq(struct tmio_mmc_host *host) host->sg_off += count; - tmio_mmc_kunmap_atomic(host, &flags); + tmio_mmc_kunmap_atomic(sg_virt, &flags); if (host->sg_off == host->sg_ptr->length) tmio_mmc_next_sg(host); diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index 64f7d5dfc10..0fedc78e3ea 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -82,10 +82,7 @@ #define ack_mmc_irqs(host, i) \ do { \ - u32 mask;\ - mask = sd_ctrl_read32((host), CTL_STATUS); \ - mask &= ~((i) & TMIO_MASK_IRQ); \ - sd_ctrl_write32((host), CTL_STATUS, mask); \ + sd_ctrl_write32((host), CTL_STATUS, ~(i)); \ } while (0) @@ -177,19 +174,17 @@ static inline int tmio_mmc_next_sg(struct tmio_mmc_host *host) return --host->sg_len; } -static inline char *tmio_mmc_kmap_atomic(struct tmio_mmc_host *host, +static inline char *tmio_mmc_kmap_atomic(struct scatterlist *sg, unsigned long *flags) { - struct scatterlist *sg = host->sg_ptr; - local_irq_save(*flags); return kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset; } -static inline void tmio_mmc_kunmap_atomic(struct tmio_mmc_host *host, +static inline void tmio_mmc_kunmap_atomic(void *virt, unsigned long *flags) { - kunmap_atomic(sg_page(host->sg_ptr), KM_BIO_SRC_IRQ); + kunmap_atomic(virt, KM_BIO_SRC_IRQ); local_irq_restore(*flags); } |