From af2a85fd9a4be83469a00f257563dfb77b9daad0 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Mon, 14 Dec 2009 14:10:26 -0500 Subject: mmc: at91_mci.c: use resource_size() [cjb: rebased patch against Linus] Signed-off-by: H Hartley Sweeten Cc: Nicolas Ferre Cc: Acked-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Nicolas Ferre Signed-off-by: Andrew Morton Signed-off-by: Chris Ball --- drivers/mmc/host/at91_mci.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/at91_mci.c b/drivers/mmc/host/at91_mci.c index 87226cd202a5..339e5913e5cc 100644 --- a/drivers/mmc/host/at91_mci.c +++ b/drivers/mmc/host/at91_mci.c @@ -928,7 +928,7 @@ static int __init at91_mci_probe(struct platform_device *pdev) if (!res) return -ENXIO; - if (!request_mem_region(res->start, res->end - res->start + 1, DRIVER_NAME)) + if (!request_mem_region(res->start, resource_size(res), DRIVER_NAME)) return -EBUSY; mmc = mmc_alloc_host(sizeof(struct at91mci_host), &pdev->dev); @@ -1017,7 +1017,7 @@ static int __init at91_mci_probe(struct platform_device *pdev) /* * Map I/O region */ - host->baseaddr = ioremap(res->start, res->end - res->start + 1); + host->baseaddr = ioremap(res->start, resource_size(res)); if (!host->baseaddr) { ret = -ENOMEM; goto fail1; @@ -1093,7 +1093,7 @@ fail4b: fail5: mmc_free_host(mmc); fail6: - release_mem_region(res->start, res->end - res->start + 1); + release_mem_region(res->start, resource_size(res)); dev_err(&pdev->dev, "probe failed, err %d\n", ret); return ret; } @@ -1138,7 +1138,7 @@ static int __exit at91_mci_remove(struct platform_device *pdev) iounmap(host->baseaddr); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, res->end - res->start + 1); + release_mem_region(res->start, resource_size(res)); mmc_free_host(mmc); platform_set_drvdata(pdev, NULL); -- cgit v1.2.3 From e8e3f6ca12d95181ce4718bcb4865c1913eac983 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Mon, 14 Dec 2009 14:11:56 -0500 Subject: mmc: atmel-mci.c: use resource_size() [cjb: rebased patch against Linus] Signed-off-by: H Hartley Sweeten Cc: Signed-off-by: Nicolas Ferre Signed-off-by: Andrew Morton Signed-off-by: Chris Ball --- drivers/mmc/host/atmel-mci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 95ef864ad8f9..1d4e5464ea2f 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -1777,7 +1777,7 @@ static int __init atmci_probe(struct platform_device *pdev) } ret = -ENOMEM; - host->regs = ioremap(regs->start, regs->end - regs->start + 1); + host->regs = ioremap(regs->start, resource_size(regs)); if (!host->regs) goto err_ioremap; -- cgit v1.2.3 From 7a5ea56abc493fd43fb8d5b2f55d18396ec048f1 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Mon, 14 Dec 2009 14:28:06 -0500 Subject: mmc: au1xmmc.c: use resource_size() [cjb: rebased patch against Linus] Signed-off-by: H Hartley Sweeten Cc: Acked-by: Manuel Lauss Signed-off-by: Andrew Morton Signed-off-by: Chris Ball --- drivers/mmc/host/au1xmmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c index c8da5d30a861..e14b866b270f 100644 --- a/drivers/mmc/host/au1xmmc.c +++ b/drivers/mmc/host/au1xmmc.c @@ -964,7 +964,7 @@ static int __devinit au1xmmc_probe(struct platform_device *pdev) goto out1; } - host->ioarea = request_mem_region(r->start, r->end - r->start + 1, + host->ioarea = request_mem_region(r->start, resource_size(r), pdev->name); if (!host->ioarea) { dev_err(&pdev->dev, "mmio already in use\n"); -- cgit v1.2.3 From a36274e0184193e393fb82957925c3981a6b0477 Mon Sep 17 00:00:00 2001 From: "Martin K. Petersen" Date: Fri, 10 Sep 2010 01:33:59 -0400 Subject: mmc: Remove distinction between hw and phys segments We have deprecated the distinction between hardware and physical segments in the block layer. Consolidate the two limits into one in drivers/mmc/. Signed-off-by: Martin K. Petersen Signed-off-by: Chris Ball --- drivers/mmc/host/at91_mci.c | 3 +-- drivers/mmc/host/atmel-mci.c | 3 +-- drivers/mmc/host/au1xmmc.c | 2 +- drivers/mmc/host/bfin_sdh.c | 2 +- drivers/mmc/host/davinci_mmc.c | 8 +++----- drivers/mmc/host/imxmmc.c | 3 +-- drivers/mmc/host/jz4740_mmc.c | 3 +-- drivers/mmc/host/mmc_spi.c | 3 +-- drivers/mmc/host/mmci.c | 3 +-- drivers/mmc/host/msm_sdcc.c | 3 +-- drivers/mmc/host/mvsdio.c | 3 +-- drivers/mmc/host/mxcmmc.c | 3 +-- drivers/mmc/host/omap.c | 3 +-- drivers/mmc/host/omap_hsmmc.c | 3 +-- drivers/mmc/host/pxamci.c | 2 +- drivers/mmc/host/s3cmci.c | 3 +-- drivers/mmc/host/sdhci.c | 7 +++---- drivers/mmc/host/sh_mmcif.c | 3 +-- drivers/mmc/host/tifm_sd.c | 3 +-- drivers/mmc/host/via-sdmmc.c | 3 +-- drivers/mmc/host/wbsd.c | 3 +-- 21 files changed, 25 insertions(+), 44 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/at91_mci.c b/drivers/mmc/host/at91_mci.c index 339e5913e5cc..591ab540b407 100644 --- a/drivers/mmc/host/at91_mci.c +++ b/drivers/mmc/host/at91_mci.c @@ -947,8 +947,7 @@ static int __init at91_mci_probe(struct platform_device *pdev) mmc->max_blk_size = MCI_MAXBLKSIZE; mmc->max_blk_count = MCI_BLKATONCE; mmc->max_req_size = MCI_BUFSIZE; - mmc->max_phys_segs = MCI_BLKATONCE; - mmc->max_hw_segs = MCI_BLKATONCE; + mmc->max_segs = MCI_BLKATONCE; mmc->max_seg_size = MCI_BUFSIZE; host = mmc_priv(mmc); diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 1d4e5464ea2f..301351a5d838 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -1618,8 +1618,7 @@ static int __init atmci_init_slot(struct atmel_mci *host, if (slot_data->bus_width >= 4) mmc->caps |= MMC_CAP_4_BIT_DATA; - mmc->max_hw_segs = 64; - mmc->max_phys_segs = 64; + mmc->max_segs = 64; mmc->max_req_size = 32768 * 512; mmc->max_blk_size = 32768; mmc->max_blk_count = 512; diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c index e14b866b270f..41e5a60493ad 100644 --- a/drivers/mmc/host/au1xmmc.c +++ b/drivers/mmc/host/au1xmmc.c @@ -998,7 +998,7 @@ static int __devinit au1xmmc_probe(struct platform_device *pdev) mmc->f_max = 24000000; mmc->max_seg_size = AU1XMMC_DESCRIPTOR_SIZE; - mmc->max_phys_segs = AU1XMMC_DESCRIPTOR_COUNT; + mmc->max_segs = AU1XMMC_DESCRIPTOR_COUNT; mmc->max_blk_size = 2048; mmc->max_blk_count = 512; diff --git a/drivers/mmc/host/bfin_sdh.c b/drivers/mmc/host/bfin_sdh.c index 4b0e677d7295..bac7d62866b7 100644 --- a/drivers/mmc/host/bfin_sdh.c +++ b/drivers/mmc/host/bfin_sdh.c @@ -469,7 +469,7 @@ static int __devinit sdh_probe(struct platform_device *pdev) } mmc->ops = &sdh_ops; - mmc->max_phys_segs = 32; + mmc->max_segs = 32; mmc->max_seg_size = 1 << 16; mmc->max_blk_size = 1 << 11; mmc->max_blk_count = 1 << 11; diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c index 33d9f1b00862..e15547cf701f 100644 --- a/drivers/mmc/host/davinci_mmc.c +++ b/drivers/mmc/host/davinci_mmc.c @@ -138,7 +138,7 @@ /* * One scatterlist dma "segment" is at most MAX_CCNT rw_threshold units, * and we handle up to MAX_NR_SG segments. MMC_BLOCK_BOUNCE kicks in only - * for drivers with max_hw_segs == 1, making the segments bigger (64KB) + * for drivers with max_segs == 1, making the segments bigger (64KB) * than the page or two that's otherwise typical. nr_sg (passed from * platform data) == 16 gives at least the same throughput boost, using * EDMA transfer linkage instead of spending CPU time copying pages. @@ -1239,8 +1239,7 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev) * Each hw_seg uses one EDMA parameter RAM slot, always one * channel and then usually some linked slots. */ - mmc->max_hw_segs = 1 + host->n_link; - mmc->max_phys_segs = mmc->max_hw_segs; + mmc->max_segs = 1 + host->n_link; /* EDMA limit per hw segment (one or two MBytes) */ mmc->max_seg_size = MAX_CCNT * rw_threshold; @@ -1250,8 +1249,7 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev) mmc->max_blk_count = 65535; /* NBLK is 16 bits */ mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; - dev_dbg(mmc_dev(host->mmc), "max_phys_segs=%d\n", mmc->max_phys_segs); - dev_dbg(mmc_dev(host->mmc), "max_hw_segs=%d\n", mmc->max_hw_segs); + dev_dbg(mmc_dev(host->mmc), "max_segs=%d\n", mmc->max_segs); dev_dbg(mmc_dev(host->mmc), "max_blk_size=%d\n", mmc->max_blk_size); dev_dbg(mmc_dev(host->mmc), "max_req_size=%d\n", mmc->max_req_size); dev_dbg(mmc_dev(host->mmc), "max_seg_size=%d\n", mmc->max_seg_size); diff --git a/drivers/mmc/host/imxmmc.c b/drivers/mmc/host/imxmmc.c index 5a950b16d9e6..881f7ba545ae 100644 --- a/drivers/mmc/host/imxmmc.c +++ b/drivers/mmc/host/imxmmc.c @@ -966,8 +966,7 @@ static int __init imxmci_probe(struct platform_device *pdev) mmc->caps = MMC_CAP_4_BIT_DATA; /* MMC core transfer sizes tunable parameters */ - mmc->max_hw_segs = 64; - mmc->max_phys_segs = 64; + mmc->max_segs = 64; mmc->max_seg_size = 64*512; /* default PAGE_CACHE_SIZE */ mmc->max_req_size = 64*512; /* default PAGE_CACHE_SIZE */ mmc->max_blk_size = 2048; diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c index ad4f9870e3ca..b3a0ab0e4c2b 100644 --- a/drivers/mmc/host/jz4740_mmc.c +++ b/drivers/mmc/host/jz4740_mmc.c @@ -876,8 +876,7 @@ static int __devinit jz4740_mmc_probe(struct platform_device* pdev) mmc->max_blk_count = (1 << 15) - 1; mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; - mmc->max_phys_segs = 128; - mmc->max_hw_segs = 128; + mmc->max_segs = 128; mmc->max_seg_size = mmc->max_req_size; host->mmc = mmc; diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index 62a35822003e..5b0b50636ec4 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -1345,8 +1345,7 @@ static int mmc_spi_probe(struct spi_device *spi) mmc->ops = &mmc_spi_ops; mmc->max_blk_size = MMC_SPI_BLOCKSIZE; - mmc->max_hw_segs = MMC_SPI_BLOCKSATONCE; - mmc->max_phys_segs = MMC_SPI_BLOCKSATONCE; + mmc->max_segs = MMC_SPI_BLOCKSATONCE; mmc->max_req_size = MMC_SPI_BLOCKSATONCE * MMC_SPI_BLOCKSIZE; mmc->max_blk_count = MMC_SPI_BLOCKSATONCE; diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index f2e02d7d9f3d..5f2e72d38b5d 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -734,8 +734,7 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id) /* * We can do SGIO */ - mmc->max_hw_segs = 16; - mmc->max_phys_segs = NR_SG; + mmc->max_segs = NR_SG; /* * Since only a certain number of bits are valid in the data length diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index ff7752348b11..1290d14c5839 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -1164,8 +1164,7 @@ msmsdcc_probe(struct platform_device *pdev) mmc->caps |= MMC_CAP_SDIO_IRQ; mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED; - mmc->max_phys_segs = NR_SG; - mmc->max_hw_segs = NR_SG; + mmc->max_segs = NR_SG; mmc->max_blk_size = 4096; /* MCI_DATA_CTL BLOCKSIZE up to 4096 */ mmc->max_blk_count = 65536; diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c index 366eefa77c5a..a5bf60e01af4 100644 --- a/drivers/mmc/host/mvsdio.c +++ b/drivers/mmc/host/mvsdio.c @@ -742,8 +742,7 @@ static int __init mvsd_probe(struct platform_device *pdev) mmc->max_blk_size = 2048; mmc->max_blk_count = 65535; - mmc->max_hw_segs = 1; - mmc->max_phys_segs = 1; + mmc->max_segs = 1; mmc->max_seg_size = mmc->max_blk_size * mmc->max_blk_count; mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c index 350f78e86245..bdd2cbb87cba 100644 --- a/drivers/mmc/host/mxcmmc.c +++ b/drivers/mmc/host/mxcmmc.c @@ -790,8 +790,7 @@ static int mxcmci_probe(struct platform_device *pdev) mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ; /* MMC core transfer sizes tunable parameters */ - mmc->max_hw_segs = 64; - mmc->max_phys_segs = 64; + mmc->max_segs = 64; mmc->max_blk_size = 2048; mmc->max_blk_count = 65535; mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index d98ddcfac5e5..0c7e37f496ef 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -1335,8 +1335,7 @@ static int __init mmc_omap_new_slot(struct mmc_omap_host *host, int id) * NOTE max_seg_size assumption that small blocks aren't * normally used (except e.g. for reading SD registers). */ - mmc->max_phys_segs = 32; - mmc->max_hw_segs = 32; + mmc->max_segs = 32; mmc->max_blk_size = 2048; /* BLEN is 11 bits (+1) */ mmc->max_blk_count = 2048; /* NBLK is 11 bits (+1) */ mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 4526d2791f29..03c26e026506 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -2105,8 +2105,7 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) /* Since we do only SG emulation, we can have as many segs * as we want. */ - mmc->max_phys_segs = 1024; - mmc->max_hw_segs = 1024; + mmc->max_segs = 1024; mmc->max_blk_size = 512; /* Block Length at max can be 1024 */ mmc->max_blk_count = 0xFFFF; /* No. of Blocks is 16 bits */ diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index 0a4e43f37140..b7dfcac31e8a 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -576,7 +576,7 @@ static int pxamci_probe(struct platform_device *pdev) * We can do SG-DMA, but we don't because we never know how much * data we successfully wrote to the card. */ - mmc->max_phys_segs = NR_SG; + mmc->max_segs = NR_SG; /* * Our hardware DMA can handle a maximum of one page per SG entry. diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c index 976330de379e..1ccd4b256cee 100644 --- a/drivers/mmc/host/s3cmci.c +++ b/drivers/mmc/host/s3cmci.c @@ -1736,8 +1736,7 @@ static int __devinit s3cmci_probe(struct platform_device *pdev) mmc->max_req_size = 4095 * 512; mmc->max_seg_size = mmc->max_req_size; - mmc->max_phys_segs = 128; - mmc->max_hw_segs = 128; + mmc->max_segs = 128; dbg(host, dbg_debug, "probe: mode:%s mapped mci_base:%p irq:%u irq_cd:%u dma:%u.\n", diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 401527d273b5..f608626c4821 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1850,12 +1850,11 @@ int sdhci_add_host(struct sdhci_host *host) * can do scatter/gather or not. */ if (host->flags & SDHCI_USE_ADMA) - mmc->max_hw_segs = 128; + mmc->max_segs = 128; else if (host->flags & SDHCI_USE_SDMA) - mmc->max_hw_segs = 1; + mmc->max_segs = 1; else /* PIO */ - mmc->max_hw_segs = 128; - mmc->max_phys_segs = 128; + mmc->max_segs = 128; /* * Maximum number of sectors in one transfer. Limited by DMA boundary diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 5d3f824bb5a3..0f06b8002814 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -846,8 +846,7 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) mmc->caps = MMC_CAP_MMC_HIGHSPEED; if (pd->caps) mmc->caps |= pd->caps; - mmc->max_phys_segs = 128; - mmc->max_hw_segs = 128; + mmc->max_segs = 128; mmc->max_blk_size = 512; mmc->max_blk_count = 65535; mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c index cec99958b652..457c26ea09de 100644 --- a/drivers/mmc/host/tifm_sd.c +++ b/drivers/mmc/host/tifm_sd.c @@ -978,11 +978,10 @@ static int tifm_sd_probe(struct tifm_dev *sock) mmc->f_max = 24000000; mmc->max_blk_count = 2048; - mmc->max_hw_segs = mmc->max_blk_count; + mmc->max_segs = mmc->max_blk_count; mmc->max_blk_size = min(TIFM_MMCSD_MAX_BLOCK_SIZE, PAGE_SIZE); mmc->max_seg_size = mmc->max_blk_count * mmc->max_blk_size; mmc->max_req_size = mmc->max_seg_size; - mmc->max_phys_segs = mmc->max_hw_segs; sock->card_event = tifm_sd_card_event; sock->data_event = tifm_sd_data_event; diff --git a/drivers/mmc/host/via-sdmmc.c b/drivers/mmc/host/via-sdmmc.c index 19f2d72dbca5..9ed84ddb4780 100644 --- a/drivers/mmc/host/via-sdmmc.c +++ b/drivers/mmc/host/via-sdmmc.c @@ -1050,8 +1050,7 @@ static void via_init_mmc_host(struct via_crdr_mmc_host *host) mmc->ops = &via_sdc_ops; /*Hardware cannot do scatter lists*/ - mmc->max_hw_segs = 1; - mmc->max_phys_segs = 1; + mmc->max_segs = 1; mmc->max_blk_size = VIA_CRDR_MAX_BLOCK_LENGTH; mmc->max_blk_count = VIA_CRDR_MAX_BLOCK_COUNT; diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c index 0012f5d13d28..7fca0a386ba0 100644 --- a/drivers/mmc/host/wbsd.c +++ b/drivers/mmc/host/wbsd.c @@ -1235,8 +1235,7 @@ static int __devinit wbsd_alloc_mmc(struct device *dev) * Maximum number of segments. Worst case is one sector per segment * so this will be 64kB/512. */ - mmc->max_hw_segs = 128; - mmc->max_phys_segs = 128; + mmc->max_segs = 128; /* * Maximum request size. Also limited by 64KiB buffer. -- cgit v1.2.3 From 53f3a9e26ed5a94bd3caf732e7635692cd3c4402 Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Thu, 2 Sep 2010 14:15:08 +0000 Subject: mmc: USB SD Host Controller (USHC) driver Add a driver for USB SD Host Controller devices. These devices are Cypress Astoria chips with firmware compliant with issue 2 of CSR's USHC specification. [cjb: adapt to block layer deprecation of max_{hw,phys}_segs] Signed-off-by: David Vrabel Signed-off-by: Chris Ball --- drivers/mmc/host/Kconfig | 14 ++ drivers/mmc/host/Makefile | 1 + drivers/mmc/host/ushc.c | 566 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 581 insertions(+) create mode 100644 drivers/mmc/host/ushc.c (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 68d12794cfd9..8de7b9e8fc51 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -451,3 +451,17 @@ config MMC_JZ4740 SoCs. If you have a board based on such a SoC and with a SD/MMC slot, say Y or M here. + +config MMC_USHC + tristate "USB SD Host Controller (USHC) support" + depends on USB + help + This selects support for USB SD Host Controllers based on + the Cypress Astoria chip with firmware compliant with CSR's + USB SD Host Controller specification (CS-118793-SP). + + CSR boards with this device include: USB<>SDIO (M1985v2), + and Ultrasira. + + Note: These controllers only support SDIO cards and do not + support MMC or SD memory cards. diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 840bcb52d82f..f7fb7eb234b9 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_MMC_VIA_SDMMC) += via-sdmmc.o 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_USHC) += ushc.o obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-platform.o sdhci-platform-y := sdhci-pltfm.o diff --git a/drivers/mmc/host/ushc.c b/drivers/mmc/host/ushc.c new file mode 100644 index 000000000000..b4ead4a13c98 --- /dev/null +++ b/drivers/mmc/host/ushc.c @@ -0,0 +1,566 @@ +/* + * USB SD Host Controller (USHC) controller driver. + * + * Copyright (C) 2010 Cambridge Silicon Radio Ltd. + * + * 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. + * + * Notes: + * - Only version 2 devices are supported. + * - Version 2 devices only support SDIO cards/devices (R2 response is + * unsupported). + * + * References: + * [USHC] USB SD Host Controller specification (CS-118793-SP) + */ +#include +#include +#include +#include +#include +#include +#include + +enum ushc_request { + USHC_GET_CAPS = 0x00, + USHC_HOST_CTRL = 0x01, + USHC_PWR_CTRL = 0x02, + USHC_CLK_FREQ = 0x03, + USHC_EXEC_CMD = 0x04, + USHC_READ_RESP = 0x05, + USHC_RESET = 0x06, +}; + +enum ushc_request_type { + USHC_GET_CAPS_TYPE = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + USHC_HOST_CTRL_TYPE = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + USHC_PWR_CTRL_TYPE = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + USHC_CLK_FREQ_TYPE = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + USHC_EXEC_CMD_TYPE = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + USHC_READ_RESP_TYPE = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + USHC_RESET_TYPE = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, +}; + +#define USHC_GET_CAPS_VERSION_MASK 0xff +#define USHC_GET_CAPS_3V3 (1 << 8) +#define USHC_GET_CAPS_3V0 (1 << 9) +#define USHC_GET_CAPS_1V8 (1 << 10) +#define USHC_GET_CAPS_HIGH_SPD (1 << 16) + +#define USHC_HOST_CTRL_4BIT (1 << 1) +#define USHC_HOST_CTRL_HIGH_SPD (1 << 0) + +#define USHC_PWR_CTRL_OFF 0x00 +#define USHC_PWR_CTRL_3V3 0x01 +#define USHC_PWR_CTRL_3V0 0x02 +#define USHC_PWR_CTRL_1V8 0x03 + +#define USHC_READ_RESP_BUSY (1 << 4) +#define USHC_READ_RESP_ERR_TIMEOUT (1 << 3) +#define USHC_READ_RESP_ERR_CRC (1 << 2) +#define USHC_READ_RESP_ERR_DAT (1 << 1) +#define USHC_READ_RESP_ERR_CMD (1 << 0) +#define USHC_READ_RESP_ERR_MASK 0x0f + +struct ushc_cbw { + __u8 signature; + __u8 cmd_idx; + __le16 block_size; + __le32 arg; +} __attribute__((packed)); + +#define USHC_CBW_SIGNATURE 'C' + +struct ushc_csw { + __u8 signature; + __u8 status; + __le32 response; +} __attribute__((packed)); + +#define USHC_CSW_SIGNATURE 'S' + +struct ushc_int_data { + u8 status; + u8 reserved[3]; +}; + +#define USHC_INT_STATUS_SDIO_INT (1 << 1) +#define USHC_INT_STATUS_CARD_PRESENT (1 << 0) + + +struct ushc_data { + struct usb_device *usb_dev; + struct mmc_host *mmc; + + struct urb *int_urb; + struct ushc_int_data *int_data; + + struct urb *cbw_urb; + struct ushc_cbw *cbw; + + struct urb *data_urb; + + struct urb *csw_urb; + struct ushc_csw *csw; + + spinlock_t lock; + struct mmc_request *current_req; + u32 caps; + u16 host_ctrl; + unsigned long flags; + u8 last_status; + int clock_freq; +}; + +#define DISCONNECTED 0 +#define INT_EN 1 +#define IGNORE_NEXT_INT 2 + +static void data_callback(struct urb *urb); + +static int ushc_hw_reset(struct ushc_data *ushc) +{ + return usb_control_msg(ushc->usb_dev, usb_sndctrlpipe(ushc->usb_dev, 0), + USHC_RESET, USHC_RESET_TYPE, + 0, 0, NULL, 0, 100); +} + +static int ushc_hw_get_caps(struct ushc_data *ushc) +{ + int ret; + int version; + + ret = usb_control_msg(ushc->usb_dev, usb_rcvctrlpipe(ushc->usb_dev, 0), + USHC_GET_CAPS, USHC_GET_CAPS_TYPE, + 0, 0, &ushc->caps, sizeof(ushc->caps), 100); + if (ret < 0) + return ret; + + ushc->caps = le32_to_cpu(ushc->caps); + + version = ushc->caps & USHC_GET_CAPS_VERSION_MASK; + if (version != 0x02) { + dev_err(&ushc->usb_dev->dev, "controller version %d is not supported\n", version); + return -EINVAL; + } + + return 0; +} + +static int ushc_hw_set_host_ctrl(struct ushc_data *ushc, u16 mask, u16 val) +{ + u16 host_ctrl; + int ret; + + host_ctrl = (ushc->host_ctrl & ~mask) | val; + ret = usb_control_msg(ushc->usb_dev, usb_sndctrlpipe(ushc->usb_dev, 0), + USHC_HOST_CTRL, USHC_HOST_CTRL_TYPE, + host_ctrl, 0, NULL, 0, 100); + if (ret < 0) + return ret; + ushc->host_ctrl = host_ctrl; + return 0; +} + +static void int_callback(struct urb *urb) +{ + struct ushc_data *ushc = urb->context; + u8 status, last_status; + + if (urb->status < 0) + return; + + status = ushc->int_data->status; + last_status = ushc->last_status; + ushc->last_status = status; + + /* + * Ignore the card interrupt status on interrupt transfers that + * were submitted while card interrupts where disabled. + * + * This avoid occasional spurious interrupts when enabling + * interrupts immediately after clearing the source on the card. + */ + + if (!test_and_clear_bit(IGNORE_NEXT_INT, &ushc->flags) + && test_bit(INT_EN, &ushc->flags) + && status & USHC_INT_STATUS_SDIO_INT) { + mmc_signal_sdio_irq(ushc->mmc); + } + + if ((status ^ last_status) & USHC_INT_STATUS_CARD_PRESENT) + mmc_detect_change(ushc->mmc, msecs_to_jiffies(100)); + + if (!test_bit(INT_EN, &ushc->flags)) + set_bit(IGNORE_NEXT_INT, &ushc->flags); + usb_submit_urb(ushc->int_urb, GFP_ATOMIC); +} + +static void cbw_callback(struct urb *urb) +{ + struct ushc_data *ushc = urb->context; + + if (urb->status != 0) { + usb_unlink_urb(ushc->data_urb); + usb_unlink_urb(ushc->csw_urb); + } +} + +static void data_callback(struct urb *urb) +{ + struct ushc_data *ushc = urb->context; + + if (urb->status != 0) + usb_unlink_urb(ushc->csw_urb); +} + +static void csw_callback(struct urb *urb) +{ + struct ushc_data *ushc = urb->context; + struct mmc_request *req = ushc->current_req; + int status; + + status = ushc->csw->status; + + if (urb->status != 0) { + req->cmd->error = urb->status; + } else if (status & USHC_READ_RESP_ERR_CMD) { + if (status & USHC_READ_RESP_ERR_CRC) + req->cmd->error = -EIO; + else + req->cmd->error = -ETIMEDOUT; + } + if (req->data) { + if (status & USHC_READ_RESP_ERR_DAT) { + if (status & USHC_READ_RESP_ERR_CRC) + req->data->error = -EIO; + else + req->data->error = -ETIMEDOUT; + req->data->bytes_xfered = 0; + } else { + req->data->bytes_xfered = req->data->blksz * req->data->blocks; + } + } + + req->cmd->resp[0] = le32_to_cpu(ushc->csw->response); + + mmc_request_done(ushc->mmc, req); +} + +static void ushc_request(struct mmc_host *mmc, struct mmc_request *req) +{ + struct ushc_data *ushc = mmc_priv(mmc); + int ret; + unsigned long flags; + + spin_lock_irqsave(&ushc->lock, flags); + + if (test_bit(DISCONNECTED, &ushc->flags)) { + ret = -ENODEV; + goto out; + } + + /* Version 2 firmware doesn't support the R2 response format. */ + if (req->cmd->flags & MMC_RSP_136) { + ret = -EINVAL; + goto out; + } + + /* The Astoria's data FIFOs don't work with clock speeds < 5MHz so + limit commands with data to 6MHz or more. */ + if (req->data && ushc->clock_freq < 6000000) { + ret = -EINVAL; + goto out; + } + + ushc->current_req = req; + + /* Start cmd with CBW. */ + ushc->cbw->cmd_idx = cpu_to_le16(req->cmd->opcode); + if (req->data) + ushc->cbw->block_size = cpu_to_le16(req->data->blksz); + else + ushc->cbw->block_size = 0; + ushc->cbw->arg = cpu_to_le32(req->cmd->arg); + + ret = usb_submit_urb(ushc->cbw_urb, GFP_ATOMIC); + if (ret < 0) + goto out; + + /* Submit data (if any). */ + if (req->data) { + struct mmc_data *data = req->data; + int pipe; + + if (data->flags & MMC_DATA_READ) + pipe = usb_rcvbulkpipe(ushc->usb_dev, 6); + else + pipe = usb_sndbulkpipe(ushc->usb_dev, 2); + + usb_fill_bulk_urb(ushc->data_urb, ushc->usb_dev, pipe, + sg_virt(data->sg), data->sg->length, + data_callback, ushc); + ret = usb_submit_urb(ushc->data_urb, GFP_ATOMIC); + if (ret < 0) + goto out; + } + + /* Submit CSW. */ + ret = usb_submit_urb(ushc->csw_urb, GFP_ATOMIC); + if (ret < 0) + goto out; + +out: + spin_unlock_irqrestore(&ushc->lock, flags); + if (ret < 0) { + usb_unlink_urb(ushc->cbw_urb); + usb_unlink_urb(ushc->data_urb); + req->cmd->error = ret; + mmc_request_done(mmc, req); + } +} + +static int ushc_set_power(struct ushc_data *ushc, unsigned char power_mode) +{ + u16 voltage; + + switch (power_mode) { + case MMC_POWER_OFF: + voltage = USHC_PWR_CTRL_OFF; + break; + case MMC_POWER_UP: + case MMC_POWER_ON: + voltage = USHC_PWR_CTRL_3V3; + break; + default: + return -EINVAL; + } + + return usb_control_msg(ushc->usb_dev, usb_sndctrlpipe(ushc->usb_dev, 0), + USHC_PWR_CTRL, USHC_PWR_CTRL_TYPE, + voltage, 0, NULL, 0, 100); +} + +static int ushc_set_bus_width(struct ushc_data *ushc, int bus_width) +{ + return ushc_hw_set_host_ctrl(ushc, USHC_HOST_CTRL_4BIT, + bus_width == 4 ? USHC_HOST_CTRL_4BIT : 0); +} + +static int ushc_set_bus_freq(struct ushc_data *ushc, int clk, bool enable_hs) +{ + int ret; + + /* Hardware can't detect interrupts while the clock is off. */ + if (clk == 0) + clk = 400000; + + ret = ushc_hw_set_host_ctrl(ushc, USHC_HOST_CTRL_HIGH_SPD, + enable_hs ? USHC_HOST_CTRL_HIGH_SPD : 0); + if (ret < 0) + return ret; + + ret = usb_control_msg(ushc->usb_dev, usb_sndctrlpipe(ushc->usb_dev, 0), + USHC_CLK_FREQ, USHC_CLK_FREQ_TYPE, + clk & 0xffff, (clk >> 16) & 0xffff, NULL, 0, 100); + if (ret < 0) + return ret; + + ushc->clock_freq = clk; + return 0; +} + +static void ushc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct ushc_data *ushc = mmc_priv(mmc); + + ushc_set_power(ushc, ios->power_mode); + ushc_set_bus_width(ushc, 1 << ios->bus_width); + ushc_set_bus_freq(ushc, ios->clock, ios->timing == MMC_TIMING_SD_HS); +} + +static int ushc_get_cd(struct mmc_host *mmc) +{ + struct ushc_data *ushc = mmc_priv(mmc); + + return !!(ushc->last_status & USHC_INT_STATUS_CARD_PRESENT); +} + +static void ushc_enable_sdio_irq(struct mmc_host *mmc, int enable) +{ + struct ushc_data *ushc = mmc_priv(mmc); + + if (enable) + set_bit(INT_EN, &ushc->flags); + else + clear_bit(INT_EN, &ushc->flags); +} + +static void ushc_clean_up(struct ushc_data *ushc) +{ + usb_free_urb(ushc->int_urb); + usb_free_urb(ushc->csw_urb); + usb_free_urb(ushc->data_urb); + usb_free_urb(ushc->cbw_urb); + + kfree(ushc->int_data); + kfree(ushc->cbw); + kfree(ushc->csw); + + mmc_free_host(ushc->mmc); +} + +static const struct mmc_host_ops ushc_ops = { + .request = ushc_request, + .set_ios = ushc_set_ios, + .get_cd = ushc_get_cd, + .enable_sdio_irq = ushc_enable_sdio_irq, +}; + +static int ushc_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *usb_dev = interface_to_usbdev(intf); + struct mmc_host *mmc; + struct ushc_data *ushc; + int ret = -ENOMEM; + + mmc = mmc_alloc_host(sizeof(struct ushc_data), &intf->dev); + if (mmc == NULL) + return -ENOMEM; + ushc = mmc_priv(mmc); + usb_set_intfdata(intf, ushc); + + ushc->usb_dev = usb_dev; + ushc->mmc = mmc; + + spin_lock_init(&ushc->lock); + + ret = ushc_hw_reset(ushc); + if (ret < 0) + goto err; + + /* Read capabilities. */ + ret = ushc_hw_get_caps(ushc); + if (ret < 0) + goto err; + + mmc->ops = &ushc_ops; + + mmc->f_min = 400000; + mmc->f_max = 50000000; + mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; + mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ; + mmc->caps |= (ushc->caps & USHC_GET_CAPS_HIGH_SPD) ? MMC_CAP_SD_HIGHSPEED : 0; + + mmc->max_seg_size = 512*511; + mmc->max_segs = 1; + mmc->max_req_size = 512*511; + mmc->max_blk_size = 512; + mmc->max_blk_count = 511; + + ushc->int_urb = usb_alloc_urb(0, GFP_KERNEL); + if (ushc->int_urb == NULL) + goto err; + ushc->int_data = kzalloc(sizeof(struct ushc_int_data), GFP_KERNEL); + if (ushc->int_data == NULL) + goto err; + usb_fill_int_urb(ushc->int_urb, ushc->usb_dev, + usb_rcvintpipe(usb_dev, + intf->cur_altsetting->endpoint[0].desc.bEndpointAddress), + ushc->int_data, sizeof(struct ushc_int_data), + int_callback, ushc, + intf->cur_altsetting->endpoint[0].desc.bInterval); + + ushc->cbw_urb = usb_alloc_urb(0, GFP_KERNEL); + if (ushc->cbw_urb == NULL) + goto err; + ushc->cbw = kzalloc(sizeof(struct ushc_cbw), GFP_KERNEL); + if (ushc->cbw == NULL) + goto err; + ushc->cbw->signature = USHC_CBW_SIGNATURE; + + usb_fill_bulk_urb(ushc->cbw_urb, ushc->usb_dev, usb_sndbulkpipe(usb_dev, 2), + ushc->cbw, sizeof(struct ushc_cbw), + cbw_callback, ushc); + + ushc->data_urb = usb_alloc_urb(0, GFP_KERNEL); + if (ushc->data_urb == NULL) + goto err; + + ushc->csw_urb = usb_alloc_urb(0, GFP_KERNEL); + if (ushc->csw_urb == NULL) + goto err; + ushc->csw = kzalloc(sizeof(struct ushc_cbw), GFP_KERNEL); + if (ushc->csw == NULL) + goto err; + usb_fill_bulk_urb(ushc->csw_urb, ushc->usb_dev, usb_rcvbulkpipe(usb_dev, 6), + ushc->csw, sizeof(struct ushc_csw), + csw_callback, ushc); + + ret = mmc_add_host(ushc->mmc); + if (ret) + goto err; + + ret = usb_submit_urb(ushc->int_urb, GFP_KERNEL); + if (ret < 0) { + mmc_remove_host(ushc->mmc); + goto err; + } + + return 0; + +err: + ushc_clean_up(ushc); + return ret; +} + +static void ushc_disconnect(struct usb_interface *intf) +{ + struct ushc_data *ushc = usb_get_intfdata(intf); + + spin_lock_irq(&ushc->lock); + set_bit(DISCONNECTED, &ushc->flags); + spin_unlock_irq(&ushc->lock); + + usb_kill_urb(ushc->int_urb); + usb_kill_urb(ushc->cbw_urb); + usb_kill_urb(ushc->data_urb); + usb_kill_urb(ushc->csw_urb); + + mmc_remove_host(ushc->mmc); + + ushc_clean_up(ushc); +} + +static struct usb_device_id ushc_id_table[] = { + /* CSR USB SD Host Controller */ + { USB_DEVICE(0x0a12, 0x5d10) }, + { }, +}; +MODULE_DEVICE_TABLE(usb, ushc_id_table); + +static struct usb_driver ushc_driver = { + .name = "ushc", + .id_table = ushc_id_table, + .probe = ushc_probe, + .disconnect = ushc_disconnect, +}; + +static int __init ushc_init(void) +{ + return usb_register(&ushc_driver); +} +module_init(ushc_init); + +static void __exit ushc_exit(void) +{ + usb_deregister(&ushc_driver); +} +module_exit(ushc_exit); + +MODULE_DESCRIPTION("USB SD Host Controller driver"); +MODULE_AUTHOR("David Vrabel "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 0957c3339efa333b7895157eb18b9b578394f80c Mon Sep 17 00:00:00 2001 From: "George G. Davis" Date: Thu, 18 Feb 2010 12:32:12 -0500 Subject: mmc: sdhci: Make consistent use of CONFIG_MMC_DEBUG for "DEBUG"ging The "6882a8c sdhci: Add better ADMA error reporting" commit added sdhci_show_adma_error() which is built when DEBUG is defined. Since we already have CONFIG_MMC_DEBUG used elsewhere in this driver, may as well make consistent use of that config knob instead. Signed-off-by: George G. Davis Cc: Ben Dooks Cc: Signed-off-by: Andrew Morton Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index f608626c4821..e0536175185a 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1427,7 +1427,7 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask) sdhci_finish_command(host); } -#ifdef DEBUG +#ifdef CONFIG_MMC_DEBUG static void sdhci_show_adma_error(struct sdhci_host *host) { const char *name = mmc_hostname(host->mmc); -- cgit v1.2.3 From 85105c53b0ce70a277160379f9d89309cefc0bfd Mon Sep 17 00:00:00 2001 From: Zhangfei Gao Date: Fri, 6 Aug 2010 07:10:01 +0800 Subject: mmc: SDHC 3.0: support 10-bit divided clock mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Zhangfei Gao Cc: Michał Mirosław Cc: David Vrabel Reviewed-by: Matt Fleming Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 25 ++++++++++++++++++++----- drivers/mmc/host/sdhci.h | 5 +++++ 2 files changed, 25 insertions(+), 5 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index e0536175185a..4432fec7467a 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1001,13 +1001,28 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) if (clock == 0) goto out; - for (div = 1;div < 256;div *= 2) { - if ((host->max_clk / div) <= clock) - break; + if (host->version >= SDHCI_SPEC_300) { + /* Version 3.00 divisors must be a multiple of 2. */ + if (host->max_clk <= clock) + div = 1; + else { + for (div = 2; div < 2046; div += 2) { + if ((host->max_clk / div) <= clock) + break; + } + } + } else { + /* Version 2.00 divisors must be a power of 2. */ + for (div = 1; div < 256; div *= 2) { + if ((host->max_clk / div) <= clock) + break; + } } div >>= 1; - clk = div << SDHCI_DIVIDER_SHIFT; + clk = (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT; + clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN) + << SDHCI_DIVIDER_HI_SHIFT; clk |= SDHCI_CLOCK_INT_EN; sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); @@ -1708,7 +1723,7 @@ int sdhci_add_host(struct sdhci_host *host) host->version = sdhci_readw(host, SDHCI_HOST_VERSION); host->version = (host->version & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT; - if (host->version > SDHCI_SPEC_200) { + if (host->version > SDHCI_SPEC_300) { printk(KERN_ERR "%s: Unknown controller version (%d). " "You may experience problems.\n", mmc_hostname(mmc), host->version); diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index d316bc79b636..950d4fd90072 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -86,6 +86,10 @@ #define SDHCI_CLOCK_CONTROL 0x2C #define SDHCI_DIVIDER_SHIFT 8 +#define SDHCI_DIVIDER_HI_SHIFT 6 +#define SDHCI_DIV_MASK 0xFF +#define SDHCI_DIV_MASK_LEN 8 +#define SDHCI_DIV_HI_MASK 0x300 #define SDHCI_CLOCK_CARD_EN 0x0004 #define SDHCI_CLOCK_INT_STABLE 0x0002 #define SDHCI_CLOCK_INT_EN 0x0001 @@ -178,6 +182,7 @@ #define SDHCI_SPEC_VER_SHIFT 0 #define SDHCI_SPEC_100 0 #define SDHCI_SPEC_200 1 +#define SDHCI_SPEC_300 2 struct sdhci_ops; -- cgit v1.2.3 From c4687d5f601be3f928b815b46964f7426c31aec7 Mon Sep 17 00:00:00 2001 From: Zhangfei Gao Date: Fri, 20 Aug 2010 14:02:36 -0400 Subject: mmc: SDHC 3.0: Base clock frequency change in spec 3.0 SDHC Spec 3.0: Capabilities Register bits[15-08] are Base Clock Frequency 1.0/2.0: Capabilities Register bits[13-08] are Base Clock Frequency Signed-off-by: Zhangfei Gao Cc: David Vrabel Cc: Matt Fleming Cc: Michal Miroslaw Signed-off-by: Andrew Morton Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 9 +++++++-- drivers/mmc/host/sdhci.h | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 4432fec7467a..ac8b12b18fa4 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1794,8 +1794,13 @@ int sdhci_add_host(struct sdhci_host *host) mmc_dev(host->mmc)->dma_mask = &host->dma_mask; } - host->max_clk = - (caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT; + if (host->version >= SDHCI_SPEC_300) + host->max_clk = (caps & SDHCI_CLOCK_V3_BASE_MASK) + >> SDHCI_CLOCK_BASE_SHIFT; + else + host->max_clk = (caps & SDHCI_CLOCK_BASE_MASK) + >> SDHCI_CLOCK_BASE_SHIFT; + host->max_clk *= 1000000; if (host->max_clk == 0 || host->quirks & SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN) { diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 950d4fd90072..ae28a310a6a1 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -144,6 +144,7 @@ #define SDHCI_TIMEOUT_CLK_SHIFT 0 #define SDHCI_TIMEOUT_CLK_UNIT 0x00000080 #define SDHCI_CLOCK_BASE_MASK 0x00003F00 +#define SDHCI_CLOCK_V3_BASE_MASK 0x0000FF00 #define SDHCI_CLOCK_BASE_SHIFT 8 #define SDHCI_MAX_BLOCK_MASK 0x00030000 #define SDHCI_MAX_BLOCK_SHIFT 16 -- cgit v1.2.3 From 0397526d6ae2518b6c53efd1ff1e81b7d24c91da Mon Sep 17 00:00:00 2001 From: Zhangfei Gao Date: Mon, 20 Sep 2010 15:15:18 -0400 Subject: mmc: SDHC 3.0: correct f_min calculation for SD 3.0 spec While we're at it, add symbols for SDHCI_MAX_DIV_SPEC_{200,300}. Signed-off-by: Zhangfei Gao Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 8 +++++--- drivers/mmc/host/sdhci.h | 7 +++++++ 2 files changed, 12 insertions(+), 3 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index ac8b12b18fa4..e3c9153a87e8 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1006,14 +1006,14 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) if (host->max_clk <= clock) div = 1; else { - for (div = 2; div < 2046; div += 2) { + for (div = 2; div < SDHCI_MAX_DIV_SPEC_300; div += 2) { if ((host->max_clk / div) <= clock) break; } } } else { /* Version 2.00 divisors must be a power of 2. */ - for (div = 1; div < 256; div *= 2) { + for (div = 1; div < SDHCI_MAX_DIV_SPEC_200; div *= 2) { if ((host->max_clk / div) <= clock) break; } @@ -1835,8 +1835,10 @@ int sdhci_add_host(struct sdhci_host *host) mmc->ops = &sdhci_ops; if (host->ops->get_min_clock) mmc->f_min = host->ops->get_min_clock(host); + else if (host->version >= SDHCI_SPEC_300) + mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_300; else - mmc->f_min = host->max_clk / 256; + mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_200; mmc->f_max = host->max_clk; mmc->caps |= MMC_CAP_SDIO_IRQ; diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index ae28a310a6a1..112543ae36c9 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -185,6 +185,13 @@ #define SDHCI_SPEC_200 1 #define SDHCI_SPEC_300 2 +/* + * End of controller registers. + */ + +#define SDHCI_MAX_DIV_SPEC_200 256 +#define SDHCI_MAX_DIV_SPEC_300 2046 + struct sdhci_ops; struct sdhci_host { -- cgit v1.2.3 From a29e7e18bddde778deb2cb101dbceca56b15e05e Mon Sep 17 00:00:00 2001 From: Zhangfei Gao Date: Mon, 16 Aug 2010 21:15:32 -0400 Subject: mmc: sdhci: Enable high-speed support for MMC cards MMC_CAP_MMC_HIGHSPEED allows MMC and eMMC to negotiate up to 50M instead of the previous limit of 25M. Signed-off-by: Zhangfei Gao Acked-by: Matt Fleming Acked-by: Kyungmin Park Signed-off-by: Andrew Morton Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index e3c9153a87e8..4c8631de50cd 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1846,7 +1846,7 @@ int sdhci_add_host(struct sdhci_host *host) mmc->caps |= MMC_CAP_4_BIT_DATA; if (caps & SDHCI_CAN_DO_HISPD) - mmc->caps |= MMC_CAP_SD_HIGHSPEED; + mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) mmc->caps |= MMC_CAP_NEEDS_POLL; -- cgit v1.2.3 From b08caed8659ab27199db51c63a35c5ee067fc7ef Mon Sep 17 00:00:00 2001 From: Giuseppe Cavallaro Date: Mon, 20 Sep 2010 21:22:13 -0400 Subject: mmc: sdhci: add MMC_CAP_8_BIT_DATA in the host capabilities This patch is necessary to gain the performance boost from 8-bit data with the sdhci-stm driver. Signed-off-by: Giuseppe Cavallaro Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 4c8631de50cd..e335f4f43a4b 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1843,7 +1843,7 @@ int sdhci_add_host(struct sdhci_host *host) mmc->caps |= MMC_CAP_SDIO_IRQ; if (!(host->quirks & SDHCI_QUIRK_FORCE_1_BIT_DATA)) - mmc->caps |= MMC_CAP_4_BIT_DATA; + mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA; if (caps & SDHCI_CAN_DO_HISPD) mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; -- cgit v1.2.3 From 412ab659b2bdad6afac8f84daf2a8a393145dcae Mon Sep 17 00:00:00 2001 From: Philip Rakity Date: Wed, 22 Sep 2010 15:25:13 -0700 Subject: mmc: sdhci: print out controller name for register debug In a multi-controller environment it is helpful to know which controller has problems. Signed-off-by: Philip Rakity Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index e335f4f43a4b..829e78a21a0d 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -47,7 +47,8 @@ static void sdhci_finish_command(struct sdhci_host *); static void sdhci_dumpregs(struct sdhci_host *host) { - printk(KERN_DEBUG DRIVER_NAME ": ============== REGISTER DUMP ==============\n"); + printk(KERN_DEBUG DRIVER_NAME ": =========== REGISTER DUMP (%s)===========\n", + mmc_hostname(host->mmc)); printk(KERN_DEBUG DRIVER_NAME ": Sys addr: 0x%08x | Version: 0x%08x\n", sdhci_readl(host, SDHCI_DMA_ADDRESS), -- cgit v1.2.3 From d3c502b84f734b36abdc9d11ec61f00016e98d33 Mon Sep 17 00:00:00 2001 From: matt mooney Date: Fri, 24 Sep 2010 12:17:24 -0700 Subject: mmc: Makefile: Fix EXTRA_CFLAGS assignment The EXTRA_CFLAGS assignment in mmc/Makefile was not accomplishing anything because this flag only has effect on sources at the same level as the makefile (i.e., per directory). Since card/, core/, and host/ rely on MMC_DEBUG, the subdir-ccflags-y variant seems to be the appropriate choice. Signed-off-by: matt mooney Signed-off-by: Chris Ball --- drivers/mmc/host/Makefile | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index f7fb7eb234b9..eae5d62711f7 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -2,10 +2,6 @@ # Makefile for MMC/SD host controller drivers # -ifeq ($(CONFIG_MMC_DEBUG),y) - EXTRA_CFLAGS += -DDEBUG -endif - obj-$(CONFIG_MMC_ARMMMCI) += mmci.o obj-$(CONFIG_MMC_PXA) += pxamci.o obj-$(CONFIG_MMC_IMX) += imxmmc.o -- cgit v1.2.3 From 176d1ed426a2a73a87c62a8aa05f6d002353cd50 Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Mon, 27 Sep 2010 09:42:20 +0100 Subject: mmc: sdhci: disable MMC_CAP_NEEDS_POLL in nonremovable case When a controller requires SDHCI_QUIRK_BROKEN_CARD_DETECTION, we poll for card insertion/removal, and that creates interrupts. There's no need to be doing this if we have a non-removable card. This patch requires cards to be removable before we're willing to set MMC_CAP_NEEDS_POLL. Signed-off-by: Jaehoon Chung Acked-by: Kyungmin Park Acked-by: Matt Fleming Acked-by: Wolfram Sang [cjb: modified changelog and code indentation] Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 829e78a21a0d..96c7f6010d0d 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1849,7 +1849,8 @@ int sdhci_add_host(struct sdhci_host *host) if (caps & SDHCI_CAN_DO_HISPD) mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; - if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) + if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) && + mmc_card_is_removable(mmc)) mmc->caps |= MMC_CAP_NEEDS_POLL; mmc->ocr_avail = 0; -- cgit v1.2.3 From 99fc5131018cbdc3cf42ce09fb394a4e8b053c74 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 29 Sep 2010 01:08:27 -0400 Subject: mmc: Move regulator handling closer to core After discovering a problem in regulator reference counting I took Mark Brown's advice to move the reference count into the MMC core by making the regulator status a member of struct mmc_host. I took this opportunity to also implement NULL versions of the regulator functions so as to rid the driver code from some ugly #ifdef CONFIG_REGULATOR clauses. Signed-off-by: Linus Walleij Reviewed-by: Mark Brown Cc: Liam Girdwood Cc: Tony Lindgren Cc: Adrian Hunter Cc: Robert Jarzmik Cc: Sundar Iyer Cc: Daniel Mack Cc: Pierre Ossman Cc: Matt Fleming Cc: David Brownell Cc: Russell King Cc: Eric Miao Cc: Cliff Brake Cc: Jarkko Lavinen Cc: Signed-off-by: Andrew Morton Signed-off-by: Chris Ball --- drivers/mmc/host/mmci.c | 28 ++++++++++++++++++---------- drivers/mmc/host/omap_hsmmc.c | 21 +++++++++++++-------- drivers/mmc/host/pxamci.c | 41 +++++++++++++++++++++++++++++++++-------- 3 files changed, 64 insertions(+), 26 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 5f2e72d38b5d..87b4fc6c98c2 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -523,19 +523,27 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) struct mmci_host *host = mmc_priv(mmc); u32 pwr = 0; unsigned long flags; + int ret; switch (ios->power_mode) { case MMC_POWER_OFF: - if(host->vcc && - regulator_is_enabled(host->vcc)) - regulator_disable(host->vcc); + if (host->vcc) + ret = mmc_regulator_set_ocr(mmc, host->vcc, 0); break; case MMC_POWER_UP: -#ifdef CONFIG_REGULATOR - if (host->vcc) - /* This implicitly enables the regulator */ - mmc_regulator_set_ocr(host->vcc, ios->vdd); -#endif + if (host->vcc) { + ret = mmc_regulator_set_ocr(mmc, host->vcc, ios->vdd); + if (ret) { + dev_err(mmc_dev(mmc), "unable to set OCR\n"); + /* + * The .set_ios() function in the mmc_host_ops + * struct return void, and failing to set the + * power should be rare so we print an error + * and return here. + */ + return; + } + } if (host->plat->vdd_handler) pwr |= host->plat->vdd_handler(mmc_dev(mmc), ios->vdd, ios->power_mode); @@ -869,8 +877,8 @@ static int __devexit mmci_remove(struct amba_device *dev) clk_disable(host->clk); clk_put(host->clk); - if (regulator_is_enabled(host->vcc)) - regulator_disable(host->vcc); + if (host->vcc) + mmc_regulator_set_ocr(mmc, host->vcc, 0); regulator_put(host->vcc); mmc_free_host(mmc); diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 03c26e026506..8c863ccb1bfe 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -250,9 +250,9 @@ static int omap_hsmmc_1_set_power(struct device *dev, int slot, int power_on, mmc_slot(host).before_set_reg(dev, slot, power_on, vdd); if (power_on) - ret = mmc_regulator_set_ocr(host->vcc, vdd); + ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd); else - ret = mmc_regulator_set_ocr(host->vcc, 0); + ret = mmc_regulator_set_ocr(host->mmc, host->vcc, 0); if (mmc_slot(host).after_set_reg) mmc_slot(host).after_set_reg(dev, slot, power_on, vdd); @@ -291,18 +291,23 @@ static int omap_hsmmc_23_set_power(struct device *dev, int slot, int power_on, * chips/cards need an interface voltage rail too. */ if (power_on) { - ret = mmc_regulator_set_ocr(host->vcc, vdd); + ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd); /* Enable interface voltage rail, if needed */ if (ret == 0 && host->vcc_aux) { ret = regulator_enable(host->vcc_aux); if (ret < 0) - ret = mmc_regulator_set_ocr(host->vcc, 0); + ret = mmc_regulator_set_ocr(host->mmc, + host->vcc, 0); } } else { + /* Shut down the rail */ if (host->vcc_aux) ret = regulator_disable(host->vcc_aux); - if (ret == 0) - ret = mmc_regulator_set_ocr(host->vcc, 0); + if (!ret) { + /* Then proceed to shut down the local regulator */ + ret = mmc_regulator_set_ocr(host->mmc, + host->vcc, 0); + } } if (mmc_slot(host).after_set_reg) @@ -343,9 +348,9 @@ static int omap_hsmmc_23_set_sleep(struct device *dev, int slot, int sleep, if (cardsleep) { /* VCC can be turned off if card is asleep */ if (sleep) - err = mmc_regulator_set_ocr(host->vcc, 0); + err = mmc_regulator_set_ocr(host->mmc, host->vcc, 0); else - err = mmc_regulator_set_ocr(host->vcc, vdd); + err = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd); } else err = regulator_set_mode(host->vcc, mode); if (err) diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index b7dfcac31e8a..7257738fd7da 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -99,14 +99,25 @@ static inline void pxamci_init_ocr(struct pxamci_host *host) } } -static inline void pxamci_set_power(struct pxamci_host *host, unsigned int vdd) +static inline int pxamci_set_power(struct pxamci_host *host, + unsigned char power_mode, + unsigned int vdd) { int on; -#ifdef CONFIG_REGULATOR - if (host->vcc) - mmc_regulator_set_ocr(host->vcc, vdd); -#endif + if (host->vcc) { + int ret; + + if (power_mode == MMC_POWER_UP) { + ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd); + if (ret) + return ret; + } else if (power_mode == MMC_POWER_OFF) { + ret = mmc_regulator_set_ocr(host->mmc, host->vcc, 0); + if (ret) + return ret; + } + } if (!host->vcc && host->pdata && gpio_is_valid(host->pdata->gpio_power)) { on = ((1 << vdd) & host->pdata->ocr_mask); @@ -115,6 +126,8 @@ static inline void pxamci_set_power(struct pxamci_host *host, unsigned int vdd) } if (!host->vcc && host->pdata && host->pdata->setpower) host->pdata->setpower(mmc_dev(host->mmc), vdd); + + return 0; } static void pxamci_stop_clock(struct pxamci_host *host) @@ -490,9 +503,21 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) } if (host->power_mode != ios->power_mode) { + int ret; + host->power_mode = ios->power_mode; - pxamci_set_power(host, ios->vdd); + ret = pxamci_set_power(host, ios->power_mode, ios->vdd); + if (ret) { + dev_err(mmc_dev(mmc), "unable to set power\n"); + /* + * The .set_ios() function in the mmc_host_ops + * struct return void, and failing to set the + * power should be rare so we print an error and + * return here. + */ + return; + } if (ios->power_mode == MMC_POWER_ON) host->cmdat |= CMDAT_INIT; @@ -503,8 +528,8 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) else host->cmdat &= ~CMDAT_SD_4DAT; - pr_debug("PXAMCI: clkrt = %x cmdat = %x\n", - host->clkrt, host->cmdat); + dev_dbg(mmc_dev(mmc), "PXAMCI: clkrt = %x cmdat = %x\n", + host->clkrt, host->cmdat); } static void pxamci_enable_sdio_irq(struct mmc_host *host, int enable) -- cgit v1.2.3 From be8ae09d9a800a40771c93356216678aff9ca577 Mon Sep 17 00:00:00 2001 From: Giuseppe Cavallaro Date: Tue, 28 Sep 2010 10:41:27 +0200 Subject: mmc: sdhci-pltfm: add suspend/resume functions This patch adds the suspend and resume functions in the sdhci-pltfm device driver. Signed-off-by: Giuseppe Cavallaro Reviewed-by: Wolfram Sang Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-pltfm.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index e045e3c61dde..4f126de5f492 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -165,6 +165,25 @@ static const struct platform_device_id sdhci_pltfm_ids[] = { }; MODULE_DEVICE_TABLE(platform, sdhci_pltfm_ids); +#ifdef CONFIG_PM +static int sdhci_pltfm_suspend(struct platform_device *dev, pm_message_t state) +{ + struct sdhci_host *host = platform_get_drvdata(dev); + + return sdhci_suspend_host(host, state); +} + +static int sdhci_pltfm_resume(struct platform_device *dev) +{ + struct sdhci_host *host = platform_get_drvdata(dev); + + return sdhci_resume_host(host); +} +#else +#define sdhci_pltfm_suspend NULL +#define sdhci_pltfm_resume NULL +#endif /* CONFIG_PM */ + static struct platform_driver sdhci_pltfm_driver = { .driver = { .name = "sdhci", @@ -173,6 +192,8 @@ static struct platform_driver sdhci_pltfm_driver = { .probe = sdhci_pltfm_probe, .remove = __devexit_p(sdhci_pltfm_remove), .id_table = sdhci_pltfm_ids, + .suspend = sdhci_pltfm_suspend, + .resume = sdhci_pltfm_resume, }; /*****************************************************************************\ -- cgit v1.2.3 From 8364248a829d50495a796e7561aaf9a6976f846c Mon Sep 17 00:00:00 2001 From: Giuseppe Cavallaro Date: Tue, 28 Sep 2010 10:41:28 +0200 Subject: mmc: sdhci: fix "pwr may be used uninitialized" warning This patch fixes a warning when compiling the sdhci driver: pwr may be used uninitialized in sdhci_set_power Tested with the following compiler versions: 4.2.4 and 4.4.4 Signed-off-by: Giuseppe Cavallaro Reviewed-by: Wolfram Sang Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 96c7f6010d0d..d3f924bcfffd 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1050,11 +1050,9 @@ out: static void sdhci_set_power(struct sdhci_host *host, unsigned short power) { - u8 pwr; + u8 pwr = 0; - if (power == (unsigned short)-1) - pwr = 0; - else { + if (power != (unsigned short)-1) { switch (1 << power) { case MMC_VDD_165_195: pwr = SDHCI_POWER_180; -- cgit v1.2.3 From 1978fda85dfdb53623dddb4ec126163a61ab3933 Mon Sep 17 00:00:00 2001 From: Giuseppe Cavallaro Date: Tue, 28 Sep 2010 10:41:29 +0200 Subject: mmc: sdhci: split up sdhci.h for sdhci-pltfm users Some platforms based on sdhci-pltfm need to set their own quirks. Previously to this patch, the quirks were in drivers/mmc/host/sdhci.h. This patch splits drivers/mmc/host/sdhci.h into two parts: * drivers/mmc/host/sdhci.h includes the HC registers and I/O accessors. * include/linux/mmc/sdhci.h includes the sdhci structure and quirks. Instead of including drivers/mmc/host/sdhci.h, -pltfm drivers should now include include/linux/mmc/sdhci.h and include/linux/sdhci-pltfm.h. This patch avoids adding/changing the calls/flags in the sdhci_pltfm_data structure. It has been tested on STM platforms (e.g. STx7106, STx7108, STx5206) where the driver is configured and used as shown in the example below: [snip] static int mmc_pad_resources(struct sdhci_host *sdhci) { if (!devm_stm_pad_claim(sdhci->mmc->parent, &stx7108_mmc_pad_config, dev_name(sdhci->mmc->parent))) return -ENODEV; return 0; } static struct sdhci_pltfm_data stx7108_mmc_platform_data = { .init = mmc_pad_resources, .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, }; static struct platform_device stx7108_mmc_device = { .name = "sdhci", [snip] Note: drivers/mmc/host/sdhci.h now also includes linux/mmc/sdhci.h, and no modifications should be needed on other sdhci- drivers. Signed-off-by: Giuseppe Cavallaro Reviewed-by: Wolfram Sang Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.h | 138 +++-------------------------------------------- 1 file changed, 7 insertions(+), 131 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 112543ae36c9..410ee8aa04d4 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -1,6 +1,8 @@ /* * linux/drivers/mmc/host/sdhci.h - Secure Digital Host Controller Interface driver * + * Header file for Host Controller registers and I/O accessors. + * * Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved. * * This program is free software; you can redistribute it and/or modify @@ -8,14 +10,16 @@ * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. */ -#ifndef __SDHCI_H -#define __SDHCI_H +#ifndef __SDHCI_HW_H +#define __SDHCI_HW_H #include #include #include #include +#include + /* * Controller registers */ @@ -192,134 +196,6 @@ #define SDHCI_MAX_DIV_SPEC_200 256 #define SDHCI_MAX_DIV_SPEC_300 2046 -struct sdhci_ops; - -struct sdhci_host { - /* Data set by hardware interface driver */ - const char *hw_name; /* Hardware bus name */ - - unsigned int quirks; /* Deviations from spec. */ - -/* Controller doesn't honor resets unless we touch the clock register */ -#define SDHCI_QUIRK_CLOCK_BEFORE_RESET (1<<0) -/* Controller has bad caps bits, but really supports DMA */ -#define SDHCI_QUIRK_FORCE_DMA (1<<1) -/* Controller doesn't like to be reset when there is no card inserted. */ -#define SDHCI_QUIRK_NO_CARD_NO_RESET (1<<2) -/* Controller doesn't like clearing the power reg before a change */ -#define SDHCI_QUIRK_SINGLE_POWER_WRITE (1<<3) -/* Controller has flaky internal state so reset it on each ios change */ -#define SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS (1<<4) -/* Controller has an unusable DMA engine */ -#define SDHCI_QUIRK_BROKEN_DMA (1<<5) -/* Controller has an unusable ADMA engine */ -#define SDHCI_QUIRK_BROKEN_ADMA (1<<6) -/* Controller can only DMA from 32-bit aligned addresses */ -#define SDHCI_QUIRK_32BIT_DMA_ADDR (1<<7) -/* Controller can only DMA chunk sizes that are a multiple of 32 bits */ -#define SDHCI_QUIRK_32BIT_DMA_SIZE (1<<8) -/* Controller can only ADMA chunks that are a multiple of 32 bits */ -#define SDHCI_QUIRK_32BIT_ADMA_SIZE (1<<9) -/* Controller needs to be reset after each request to stay stable */ -#define SDHCI_QUIRK_RESET_AFTER_REQUEST (1<<10) -/* Controller needs voltage and power writes to happen separately */ -#define SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER (1<<11) -/* Controller provides an incorrect timeout value for transfers */ -#define SDHCI_QUIRK_BROKEN_TIMEOUT_VAL (1<<12) -/* Controller has an issue with buffer bits for small transfers */ -#define SDHCI_QUIRK_BROKEN_SMALL_PIO (1<<13) -/* Controller does not provide transfer-complete interrupt when not busy */ -#define SDHCI_QUIRK_NO_BUSY_IRQ (1<<14) -/* Controller has unreliable card detection */ -#define SDHCI_QUIRK_BROKEN_CARD_DETECTION (1<<15) -/* Controller reports inverted write-protect state */ -#define SDHCI_QUIRK_INVERTED_WRITE_PROTECT (1<<16) -/* Controller has nonstandard clock management */ -#define SDHCI_QUIRK_NONSTANDARD_CLOCK (1<<17) -/* Controller does not like fast PIO transfers */ -#define SDHCI_QUIRK_PIO_NEEDS_DELAY (1<<18) -/* Controller losing signal/interrupt enable states after reset */ -#define SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET (1<<19) -/* Controller has to be forced to use block size of 2048 bytes */ -#define SDHCI_QUIRK_FORCE_BLK_SZ_2048 (1<<20) -/* Controller cannot do multi-block transfers */ -#define SDHCI_QUIRK_NO_MULTIBLOCK (1<<21) -/* Controller can only handle 1-bit data transfers */ -#define SDHCI_QUIRK_FORCE_1_BIT_DATA (1<<22) -/* Controller needs 10ms delay between applying power and clock */ -#define SDHCI_QUIRK_DELAY_AFTER_POWER (1<<23) -/* Controller uses SDCLK instead of TMCLK for data timeouts */ -#define SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK (1<<24) -/* Controller reports wrong base clock capability */ -#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 */ - -#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) - struct led_classdev led; /* LED control */ - char led_name[32]; -#endif - - spinlock_t lock; /* Mutex */ - - int flags; /* Host attributes */ -#define SDHCI_USE_SDMA (1<<0) /* Host is SDMA capable */ -#define SDHCI_USE_ADMA (1<<1) /* Host is ADMA capable */ -#define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */ -#define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */ - - unsigned int version; /* SDHCI spec. version */ - - unsigned int max_clk; /* Max possible freq (MHz) */ - unsigned int timeout_clk; /* Timeout freq (KHz) */ - - unsigned int clock; /* Current clock (MHz) */ - u8 pwr; /* Current voltage */ - - struct mmc_request *mrq; /* Current request */ - struct mmc_command *cmd; /* Current command */ - struct mmc_data *data; /* Current data request */ - unsigned int data_early:1; /* Data finished before cmd */ - - struct sg_mapping_iter sg_miter; /* SG state for PIO */ - unsigned int blocks; /* remaining PIO blocks */ - - int sg_count; /* Mapped sg entries */ - - u8 *adma_desc; /* ADMA descriptor table */ - u8 *align_buffer; /* Bounce buffer */ - - dma_addr_t adma_addr; /* Mapped ADMA descr. table */ - dma_addr_t align_addr; /* Mapped bounce buffer */ - - struct tasklet_struct card_tasklet; /* Tasklet structures */ - struct tasklet_struct finish_tasklet; - - struct timer_list timer; /* Timer for timeouts */ - - unsigned int caps; /* Alternative capabilities */ - - unsigned long private[0] ____cacheline_aligned; -}; - - struct sdhci_ops { #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS u32 (*read_l)(struct sdhci_host *host, int reg); @@ -440,4 +316,4 @@ extern int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state); extern int sdhci_resume_host(struct sdhci_host *host); #endif -#endif /* __SDHCI_H */ +#endif /* __SDHCI_HW_H */ -- cgit v1.2.3 From 225d85fe7ddca2985548d98e410f7baf4a5d6260 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 4 Oct 2010 15:24:21 +0100 Subject: mmc: sdhci: Allow the probe handler to override slots Currently we write it to the chip data, but if the probe handler overrides it we ignore the new value and keep using our cached one. Fix this so that a probe handler can adjust the slot count. Signed-off-by: Alan Cox Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-pci.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index e8aa99deae9a..4f5d6d00d338 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -818,6 +818,8 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev, goto free; } + slots = chip->num_slots; /* Quirk may have changed this */ + for (i = 0;i < slots;i++) { slot = sdhci_pci_probe_slot(pdev, chip, first_bar + i); if (IS_ERR(slot)) { -- cgit v1.2.3 From 292290524e54724cab78e7e79a27a3b9a8b11483 Mon Sep 17 00:00:00 2001 From: Xiaochen Shen Date: Mon, 4 Oct 2010 15:24:52 +0100 Subject: mmc: sdhci: Intel Medfield support Basic support for the Intel Medfield devices Give them their own quirks as we will need to update this later. Signed-off-by: Xiaochen Shen Signed-off-by: Alan Cox Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-pci.c | 48 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 4f5d6d00d338..ec4b81dd493f 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -145,6 +145,14 @@ static const struct sdhci_pci_fixes sdhci_cafe = { SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, }; +static const struct sdhci_pci_fixes sdhci_intel_mfd_sd = { + .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, +}; + +static const struct sdhci_pci_fixes sdhci_intel_mfd_emmc_sdio = { + .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, +}; + static int jmicron_pmos(struct sdhci_pci_chip *chip, int on) { u8 scratch; @@ -494,6 +502,46 @@ static const struct pci_device_id pci_ids[] __devinitdata = { .driver_data = (kernel_ulong_t)&sdhci_via, }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_MFD_SD, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_sd, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_MFD_SDIO1, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_emmc_sdio, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_MFD_SDIO2, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_emmc_sdio, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_MFD_EMMC0, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_emmc_sdio, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_MFD_EMMC1, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_emmc_sdio, + }, + { /* Generic SD host controller */ PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00) }, -- cgit v1.2.3 From f9ee3eab528d2d21c33584bfb86ed1ebf317cb09 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 4 Oct 2010 15:25:11 +0100 Subject: mmc: sdhci: Add Moorestown device support This adds the basic identifiers. Due to the various chip quirks it's not enough to make MRST support very useful for earlier steppings but that can follow. (I'm currently trying to verify which steps actually matter outside Intel so I can avoid unneeded stuff going upstream) [Extracted from original development] Signed-off-by: JiebingLi [Folds in fixes] Signed-off-by: Chuanxiao Dong Signed-off-by: Alan Cox Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-pci.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index ec4b81dd493f..55746bac2f44 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -145,6 +145,29 @@ static const struct sdhci_pci_fixes sdhci_cafe = { SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, }; +/* + * ADMA operation is disabled for Moorestown platform due to + * hardware bugs. + */ +static int mrst_hc1_probe(struct sdhci_pci_chip *chip) +{ + /* + * slots number is fixed here for MRST as SDIO3 is never used and has + * hardware bugs. + */ + chip->num_slots = 1; + return 0; +} + +static const struct sdhci_pci_fixes sdhci_intel_mrst_hc0 = { + .quirks = SDHCI_QUIRK_BROKEN_ADMA | SDHCI_QUIRK_NO_HISPD_BIT, +}; + +static const struct sdhci_pci_fixes sdhci_intel_mrst_hc1 = { + .quirks = SDHCI_QUIRK_BROKEN_ADMA | SDHCI_QUIRK_NO_HISPD_BIT, + .probe = mrst_hc1_probe, +}; + static const struct sdhci_pci_fixes sdhci_intel_mfd_sd = { .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, }; @@ -502,6 +525,22 @@ static const struct pci_device_id pci_ids[] __devinitdata = { .driver_data = (kernel_ulong_t)&sdhci_via, }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_MRST_SD0, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_mrst_hc0, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_MRST_SD1, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_mrst_hc1, + }, + { .vendor = PCI_VENDOR_ID_INTEL, .device = PCI_DEVICE_ID_INTEL_MFD_SD, -- cgit v1.2.3 From 3ab9c8dad6444007700b5949ec80cfdc823d31b4 Mon Sep 17 00:00:00 2001 From: Philip Rakity Date: Wed, 6 Oct 2010 11:57:23 -0700 Subject: mmc: sdhci: highspeed: check for mmc as well as sd cards The SD and MMC code set highspeed using different constants. Change the sd driver to recognize this and switch to high speed. Validated code when testing eMMC dual data rate. Signed-off-by: Philip Rakity [cjb: changelog + indentation fixes] Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index d3f924bcfffd..23358d339a68 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1194,8 +1194,9 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) else ctrl &= ~SDHCI_CTRL_4BITBUS; - if (ios->timing == MMC_TIMING_SD_HS && - !(host->quirks & SDHCI_QUIRK_NO_HISPD_BIT)) + if ((ios->timing == MMC_TIMING_SD_HS || + ios->timing == MMC_TIMING_MMC_HS) + && !(host->quirks & SDHCI_QUIRK_NO_HISPD_BIT)) ctrl |= SDHCI_CTRL_HISPD; else ctrl &= ~SDHCI_CTRL_HISPD; -- cgit v1.2.3 From 643a81ff3c5a89ae5c0768f89b29d3e1d08be5c3 Mon Sep 17 00:00:00 2001 From: Philip Rakity Date: Thu, 23 Sep 2010 08:24:32 -0700 Subject: mmc: sdhci: allow for eMMC 74 clock generation by controller Snippet of code for how adaptation layer should handle the call: /* * eMMC spec calls for the host to send 74 clocks to the card * during initialization, right after voltage stabilization. * create the clocks manually right here. */ void generate_init_clocks_A0(struct sdhci_host *host, u8 power_mode) { struct sdhci_mmc_slot *slot = sdhci_priv(host); if (slot->power_mode == MMC_POWER_UP && power_mode == MMC_POWER_ON) { /* controller specific code here */ /* slot->power_mode holds previous power setting */ } slot->power_mode = power_mode; } Signed-off-by: Philip Rakity Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 3 +++ drivers/mmc/host/sdhci.h | 2 ++ 2 files changed, 5 insertions(+) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 23358d339a68..9cb60bac1950 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1182,6 +1182,9 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) else sdhci_set_power(host, ios->vdd); + if (host->ops->platform_send_init_74_clocks) + host->ops->platform_send_init_74_clocks(host, ios->power_mode); + ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); if (ios->bus_width == MMC_BUS_WIDTH_8) diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 410ee8aa04d4..bfcd611d7b77 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -212,6 +212,8 @@ struct sdhci_ops { unsigned int (*get_max_clock)(struct sdhci_host *host); unsigned int (*get_min_clock)(struct sdhci_host *host); unsigned int (*get_timeout_clock)(struct sdhci_host *host); + void (*platform_send_init_74_clocks)(struct sdhci_host *host, + u8 power_mode); }; #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS -- cgit v1.2.3 From 19d614a45ae4beb4712bb50ba3d816ae3fe0f1f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Miros=C5=82aw?= Date: Sun, 21 Mar 2010 07:33:08 +0100 Subject: mmc: cb710: partially demystify clock selection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clock divider selection in partially verified, so document known facts in code. Signed-off-by: Michał Mirosław Signed-off-by: Chris Ball --- drivers/mmc/host/cb710-mmc.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/cb710-mmc.c b/drivers/mmc/host/cb710-mmc.c index ca3bdc831900..ad61c4c11071 100644 --- a/drivers/mmc/host/cb710-mmc.c +++ b/drivers/mmc/host/cb710-mmc.c @@ -25,7 +25,7 @@ static const u8 cb710_src_freq_mhz[16] = { 50, 55, 60, 65, 70, 75, 80, 85 }; -static void cb710_mmc_set_clock(struct mmc_host *mmc, int hz) +static void cb710_mmc_select_clock_divider(struct mmc_host *mmc, int hz) { struct cb710_slot *slot = cb710_mmc_to_slot(mmc); struct pci_dev *pdev = cb710_slot_to_chip(slot)->pdev; @@ -33,8 +33,11 @@ static void cb710_mmc_set_clock(struct mmc_host *mmc, int hz) u32 divider_idx; int src_hz; - /* this is magic, unverifiable for me, unless I get - * MMC card with cables connected to bus signals */ + /* on CB710 in HP nx9500: + * src_freq_idx == 0 + * indexes 1-7 work as written in the table + * indexes 0,8-15 give no clock output + */ pci_read_config_dword(pdev, 0x48, &src_freq_idx); src_freq_idx = (src_freq_idx >> 16) & 0xF; src_hz = cb710_src_freq_mhz[src_freq_idx] * 1000000; @@ -46,13 +49,15 @@ static void cb710_mmc_set_clock(struct mmc_host *mmc, int hz) if (src_freq_idx) divider_idx |= 0x8; + else if (divider_idx == 0) + divider_idx = 1; cb710_pci_update_config_reg(pdev, 0x40, ~0xF0000000, divider_idx << 28); dev_dbg(cb710_slot_dev(slot), - "clock set to %d Hz, wanted %d Hz; flag = %d\n", + "clock set to %d Hz, wanted %d Hz; src_freq_idx = %d, divider_idx = %d|%d\n", src_hz >> cb710_clock_divider_log2[divider_idx & 7], - hz, (divider_idx & 8) != 0); + hz, src_freq_idx, divider_idx & 7, divider_idx & 8); } static void __cb710_mmc_enable_irq(struct cb710_slot *slot, @@ -512,7 +517,7 @@ static int cb710_mmc_powerup(struct cb710_slot *slot) #endif int err; - /* a lot of magic; see comment in cb710_mmc_set_clock() */ + /* a lot of magic for now */ dev_dbg(cb710_slot_dev(slot), "bus powerup\n"); cb710_dump_regs(chip, CB710_DUMP_REGS_MMC); err = cb710_wait_while_busy(slot, CB710_MMC_S2_BUSY_20); @@ -572,7 +577,7 @@ static void cb710_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) struct cb710_mmc_reader *reader = mmc_priv(mmc); int err; - cb710_mmc_set_clock(mmc, ios->clock); + cb710_mmc_select_clock_divider(mmc, ios->clock); if (!cb710_mmc_is_card_inserted(slot)) { dev_dbg(cb710_slot_dev(slot), -- cgit v1.2.3 From 7fcc4ce37f0ccec846e32e1f5ff0647ded74fec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Miros=C5=82aw?= Date: Sun, 21 Mar 2010 07:39:52 +0100 Subject: mmc: cb710: implement get_cd() callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Mirosław Signed-off-by: Chris Ball --- drivers/mmc/host/cb710-mmc.c | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/cb710-mmc.c b/drivers/mmc/host/cb710-mmc.c index ad61c4c11071..7a176b4949e7 100644 --- a/drivers/mmc/host/cb710-mmc.c +++ b/drivers/mmc/host/cb710-mmc.c @@ -100,12 +100,6 @@ static void cb710_mmc_reset_events(struct cb710_slot *slot) cb710_write_port_8(slot, CB710_MMC_STATUS2_PORT, 0xFF); } -static int cb710_mmc_is_card_inserted(struct cb710_slot *slot) -{ - return cb710_read_port_8(slot, CB710_MMC_STATUS3_PORT) - & CB710_MMC_S3_CARD_DETECTED; -} - static void cb710_mmc_enable_4bit_data(struct cb710_slot *slot, int enable) { dev_dbg(cb710_slot_dev(slot), "configuring %d-data-line%s mode\n", @@ -499,13 +493,9 @@ static void cb710_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) reader->mrq = mrq; cb710_mmc_enable_irq(slot, CB710_MMC_IE_TEST_MASK, 0); - if (cb710_mmc_is_card_inserted(slot)) { - if (!cb710_mmc_command(mmc, mrq->cmd) && mrq->stop) - cb710_mmc_command(mmc, mrq->stop); - mdelay(1); - } else { - mrq->cmd->error = -ENOMEDIUM; - } + if (!cb710_mmc_command(mmc, mrq->cmd) && mrq->stop) + cb710_mmc_command(mmc, mrq->stop); + mdelay(1); tasklet_schedule(&reader->finish_req_tasklet); } @@ -579,12 +569,6 @@ static void cb710_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) cb710_mmc_select_clock_divider(mmc, ios->clock); - if (!cb710_mmc_is_card_inserted(slot)) { - dev_dbg(cb710_slot_dev(slot), - "no card inserted - ignoring bus powerup request\n"); - ios->power_mode = MMC_POWER_OFF; - } - if (ios->power_mode != reader->last_power_mode) switch (ios->power_mode) { case MMC_POWER_ON: @@ -624,6 +608,14 @@ static int cb710_mmc_get_ro(struct mmc_host *mmc) & CB710_MMC_S3_WRITE_PROTECTED; } +static int cb710_mmc_get_cd(struct mmc_host *mmc) +{ + struct cb710_slot *slot = cb710_mmc_to_slot(mmc); + + return cb710_read_port_8(slot, CB710_MMC_STATUS3_PORT) + & CB710_MMC_S3_CARD_DETECTED; +} + static int cb710_mmc_irq_handler(struct cb710_slot *slot) { struct mmc_host *mmc = cb710_slot_to_mmc(slot); @@ -669,7 +661,8 @@ static void cb710_mmc_finish_request_tasklet(unsigned long data) static const struct mmc_host_ops cb710_mmc_host = { .request = cb710_mmc_request, .set_ios = cb710_mmc_set_ios, - .get_ro = cb710_mmc_get_ro + .get_ro = cb710_mmc_get_ro, + .get_cd = cb710_mmc_get_cd, }; #ifdef CONFIG_PM -- cgit v1.2.3 From 2abd4f1a439718206e832d7a83b77c88c15a6847 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Miros=C5=82aw?= Date: Sun, 21 Mar 2010 07:41:39 +0100 Subject: mmc: cb710: remove unnecessary msleep() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Mirosław Signed-off-by: Chris Ball --- drivers/mmc/host/cb710-mmc.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/cb710-mmc.c b/drivers/mmc/host/cb710-mmc.c index 7a176b4949e7..5374b6b5a068 100644 --- a/drivers/mmc/host/cb710-mmc.c +++ b/drivers/mmc/host/cb710-mmc.c @@ -495,7 +495,6 @@ static void cb710_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) if (!cb710_mmc_command(mmc, mrq->cmd) && mrq->stop) cb710_mmc_command(mmc, mrq->stop); - mdelay(1); tasklet_schedule(&reader->finish_req_tasklet); } -- cgit v1.2.3 From e0780db9f8ccf2dbca3161dc153bbf54b53cb6af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Miros=C5=82aw?= Date: Sun, 21 Mar 2010 07:44:34 +0100 Subject: mmc: cb710: clear irq handler on init() error path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Mirosław Signed-off-by: Chris Ball --- drivers/mmc/host/cb710-mmc.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/cb710-mmc.c b/drivers/mmc/host/cb710-mmc.c index 5374b6b5a068..2299afc431e0 100644 --- a/drivers/mmc/host/cb710-mmc.c +++ b/drivers/mmc/host/cb710-mmc.c @@ -743,6 +743,7 @@ static int __devinit cb710_mmc_init(struct platform_device *pdev) err_free_mmc: dev_dbg(cb710_slot_dev(slot), "mmc_add_host() failed: %d\n", err); + cb710_set_irq_handler(slot, NULL); mmc_free_host(mmc); return err; } -- cgit v1.2.3 From d34289d80471011a5c71401b3acd13e2737d4738 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Miros=C5=82aw?= Date: Sun, 21 Mar 2010 08:30:19 +0100 Subject: mmc: cb710: remove debugging printk (info duplicated from mmc-core) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Mirosław Signed-off-by: Chris Ball --- drivers/mmc/host/cb710-mmc.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/cb710-mmc.c b/drivers/mmc/host/cb710-mmc.c index 2299afc431e0..66b4ce587f4b 100644 --- a/drivers/mmc/host/cb710-mmc.c +++ b/drivers/mmc/host/cb710-mmc.c @@ -102,8 +102,6 @@ static void cb710_mmc_reset_events(struct cb710_slot *slot) static void cb710_mmc_enable_4bit_data(struct cb710_slot *slot, int enable) { - dev_dbg(cb710_slot_dev(slot), "configuring %d-data-line%s mode\n", - enable ? 4 : 1, enable ? "s" : ""); if (enable) cb710_modify_port_8(slot, CB710_MMC_CONFIG1_PORT, CB710_MMC_C1_4BIT_DATA_BUS, 0); -- cgit v1.2.3 From 9ffca8300abe4bd7260b77b2f82afb01384faabc Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 14 Oct 2010 15:01:43 -0400 Subject: mmc: fix cb710 kconfig dependency warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix kconfig dependency warning to satisfy dependencies: warning: (MMC_CB710 && MMC && PCI) selects CB710_CORE which has unmet direct dependencies (MISC_DEVICES && PCI) Signed-off-by: Randy Dunlap Acked-by: Michał Mirosław Signed-off-by: Chris Ball --- drivers/mmc/host/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 8de7b9e8fc51..0b7024a4ffc2 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -395,6 +395,7 @@ config MMC_TMIO config MMC_CB710 tristate "ENE CB710 MMC/SD Interface support" depends on PCI + select MISC_DEVICES select CB710_CORE help This option enables support for MMC/SD part of ENE CB710/720 Flash -- cgit v1.2.3 From 4b711cb13843f5082e82970dd1e8031383134a65 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 15 Oct 2010 12:20:59 +0200 Subject: mmc: sdhci-pltfm: Add structure for host-specific data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need to carry some information per host, e.g. the clock. Add a structure for it and initialize it in the generic part. Also improve the check for a parent. Signed-off-by: Wolfram Sang Cc: Richard Röjfors Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-pltfm.c | 10 +++++++--- drivers/mmc/host/sdhci-pltfm.h | 7 +++++++ 2 files changed, 14 insertions(+), 3 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index 4f126de5f492..730fdf54ecb0 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -55,6 +55,7 @@ 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 sdhci_pltfm_host *pltfm_host; struct resource *iomem; int ret; @@ -71,16 +72,19 @@ static int __devinit sdhci_pltfm_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Invalid iomem size. You may " "experience problems.\n"); - if (pdev->dev.parent) - host = sdhci_alloc_host(pdev->dev.parent, 0); + /* Some PCI-based MFD need the parent here */ + if (pdev->dev.parent != &platform_bus) + host = sdhci_alloc_host(pdev->dev.parent, sizeof(*pltfm_host)); else - host = sdhci_alloc_host(&pdev->dev, 0); + host = sdhci_alloc_host(&pdev->dev, sizeof(*pltfm_host)); if (IS_ERR(host)) { ret = PTR_ERR(host); goto err; } + pltfm_host = sdhci_priv(host); + host->hw_name = "platform"; if (pdata && pdata->ops) host->ops = pdata->ops; diff --git a/drivers/mmc/host/sdhci-pltfm.h b/drivers/mmc/host/sdhci-pltfm.h index 900f32902f73..93a031973f62 100644 --- a/drivers/mmc/host/sdhci-pltfm.h +++ b/drivers/mmc/host/sdhci-pltfm.h @@ -11,8 +11,15 @@ #ifndef _DRIVERS_MMC_SDHCI_PLTFM_H #define _DRIVERS_MMC_SDHCI_PLTFM_H +#include +#include #include +struct sdhci_pltfm_host { + struct clk *clk; + u32 scratchpad; /* to handle quirks across io-accessor calls */ +}; + extern struct sdhci_pltfm_data sdhci_cns3xxx_pdata; #endif /* _DRIVERS_MMC_SDHCI_PLTFM_H */ -- cgit v1.2.3 From d3b993dcc11cd291e6908ed02b9db99970220952 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 15 Oct 2010 12:21:00 +0200 Subject: mmc: sdhci-pltfm: move .h file into appropriate subdir MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make use of the include/linux/mmc directory. Signed-off-by: Wolfram Sang Acked-by: Anton Vorontsov Tested-by: Eric Bénard Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-cns3xxx.c | 2 +- drivers/mmc/host/sdhci-pltfm.c | 2 +- drivers/mmc/host/sdhci-pltfm.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci-cns3xxx.c b/drivers/mmc/host/sdhci-cns3xxx.c index b7050b380d5f..9ebd1d7759dc 100644 --- a/drivers/mmc/host/sdhci-cns3xxx.c +++ b/drivers/mmc/host/sdhci-cns3xxx.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include "sdhci.h" #include "sdhci-pltfm.h" diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index 730fdf54ecb0..685202be2778 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -30,7 +30,7 @@ #include #include -#include +#include #include "sdhci.h" #include "sdhci-pltfm.h" diff --git a/drivers/mmc/host/sdhci-pltfm.h b/drivers/mmc/host/sdhci-pltfm.h index 93a031973f62..562b92957f18 100644 --- a/drivers/mmc/host/sdhci-pltfm.h +++ b/drivers/mmc/host/sdhci-pltfm.h @@ -13,7 +13,7 @@ #include #include -#include +#include struct sdhci_pltfm_host { struct clk *clk; -- cgit v1.2.3 From 2dfb579c7da171f6153cd58e8fbf7dcfe684778d Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 15 Oct 2010 12:21:01 +0200 Subject: mmc: sdhci: introduce get_ro private write-protect hook MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some controllers handle their write-protection differently. Introduce a callback to be able to handle it, ensuring the same locking takes place for it. Rename the status variable to make it more obvious why the read from the registers needs to be inverted. Signed-off-by: Wolfram Sang Tested-by: Eric Bénard Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 15 +++++++++------ drivers/mmc/host/sdhci.h | 1 + 2 files changed, 10 insertions(+), 6 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 9cb60bac1950..782c0ee3c925 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1223,22 +1223,25 @@ static int sdhci_get_ro(struct mmc_host *mmc) { struct sdhci_host *host; unsigned long flags; - int present; + int is_readonly; host = mmc_priv(mmc); spin_lock_irqsave(&host->lock, flags); if (host->flags & SDHCI_DEVICE_DEAD) - present = 0; + is_readonly = 0; + else if (host->ops->get_ro) + is_readonly = host->ops->get_ro(host); else - present = sdhci_readl(host, SDHCI_PRESENT_STATE); + is_readonly = !(sdhci_readl(host, SDHCI_PRESENT_STATE) + & SDHCI_WRITE_PROTECT); spin_unlock_irqrestore(&host->lock, flags); - if (host->quirks & SDHCI_QUIRK_INVERTED_WRITE_PROTECT) - return !!(present & SDHCI_WRITE_PROTECT); - return !(present & SDHCI_WRITE_PROTECT); + /* This quirk needs to be replaced by a callback-function later */ + return host->quirks & SDHCI_QUIRK_INVERTED_WRITE_PROTECT ? + !is_readonly : is_readonly; } static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable) diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index bfcd611d7b77..b7b8a3b28b01 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -214,6 +214,7 @@ struct sdhci_ops { unsigned int (*get_timeout_clock)(struct sdhci_host *host); void (*platform_send_init_74_clocks)(struct sdhci_host *host, u8 power_mode); + unsigned int (*get_ro)(struct sdhci_host *host); }; #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS -- cgit v1.2.3 From 012994f4fa5fc7663b51fa921c85c0a352339b24 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 15 Oct 2010 12:21:02 +0200 Subject: mmc: sdhci_pltfm: pass more data on custom init call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The custom init call may need more data to perform its job, so we pass it a pointer to pdata, too. Also, always use the platform_id specific data even if platform_data is present. Doing that, platform_data can additionally be parsed by init() for board-specific information (via sdhci->mmc->parent). (Note: the old behaviour was that you could override the platform_id specific data with your own. However, one can still do this by using the "sdhci" id instead of "sdhci-".) Signed-off-by: Wolfram Sang Tested-by: Eric Bénard Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-pltfm.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index 685202be2778..00e8a8ab638e 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -52,15 +52,17 @@ 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_pltfm_data *pdata; struct sdhci_host *host; struct sdhci_pltfm_host *pltfm_host; struct resource *iomem; int ret; - if (!pdata && platid && platid->driver_data) + if (platid && platid->driver_data) pdata = (void *)platid->driver_data; + else + pdata = pdev->dev.platform_data; iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!iomem) { @@ -109,7 +111,7 @@ static int __devinit sdhci_pltfm_probe(struct platform_device *pdev) } if (pdata && pdata->init) { - ret = pdata->init(host); + ret = pdata->init(host, pdata); if (ret) goto err_plat_init; } -- cgit v1.2.3 From 80872e21b0263f016f2edb7b72dd8be5636d7ca7 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 15 Oct 2010 12:21:03 +0200 Subject: mmc: sdhci-of-esdhc: factor out common stuff MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Put everything which can be shared between the OF and platform version of this driver into a local .h file. Signed-off-by: Wolfram Sang Tested-by: Eric Bénard [cjb: fix compile error: sdhci-esdhc.c->sdhci-esdhc.h] Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-esdhc.h | 83 +++++++++++++++++++++++++++++++++++++++ drivers/mmc/host/sdhci-of-esdhc.c | 70 ++++----------------------------- 2 files changed, 91 insertions(+), 62 deletions(-) create mode 100644 drivers/mmc/host/sdhci-esdhc.h (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h new file mode 100644 index 000000000000..afaf1bc4913a --- /dev/null +++ b/drivers/mmc/host/sdhci-esdhc.h @@ -0,0 +1,83 @@ +/* + * Freescale eSDHC controller driver generics for OF and pltfm. + * + * Copyright (c) 2007 Freescale Semiconductor, Inc. + * Copyright (c) 2009 MontaVista Software, Inc. + * Copyright (c) 2010 Pengutronix e.K. + * Author: Wolfram Sang + * + * 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. + */ + +#ifndef _DRIVERS_MMC_SDHCI_ESDHC_H +#define _DRIVERS_MMC_SDHCI_ESDHC_H + +/* + * Ops and quirks for the Freescale eSDHC controller. + */ + +#define ESDHC_DEFAULT_QUIRKS (SDHCI_QUIRK_FORCE_BLK_SZ_2048 | \ + SDHCI_QUIRK_BROKEN_CARD_DETECTION | \ + SDHCI_QUIRK_NO_BUSY_IRQ | \ + SDHCI_QUIRK_NONSTANDARD_CLOCK | \ + SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | \ + SDHCI_QUIRK_PIO_NEEDS_DELAY | \ + SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET | \ + SDHCI_QUIRK_NO_CARD_NO_RESET) + +#define ESDHC_SYSTEM_CONTROL 0x2c +#define ESDHC_CLOCK_MASK 0x0000fff0 +#define ESDHC_PREDIV_SHIFT 8 +#define ESDHC_DIVIDER_SHIFT 4 +#define ESDHC_CLOCK_PEREN 0x00000004 +#define ESDHC_CLOCK_HCKEN 0x00000002 +#define ESDHC_CLOCK_IPGEN 0x00000001 + +/* pltfm-specific */ +#define ESDHC_HOST_CONTROL_LE 0x20 + +/* OF-specific */ +#define ESDHC_DMA_SYSCTL 0x40c +#define ESDHC_DMA_SNOOP 0x00000040 + +#define ESDHC_HOST_CONTROL_RES 0x05 + +static inline void esdhc_set_clock(struct sdhci_host *host, unsigned int clock) +{ + int pre_div = 2; + int div = 1; + u32 temp; + + temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); + temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN + | ESDHC_CLOCK_MASK); + sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); + + if (clock == 0) + goto out; + + while (host->max_clk / pre_div / 16 > clock && pre_div < 256) + pre_div *= 2; + + while (host->max_clk / pre_div / div > clock && div < 16) + div++; + + dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n", + clock, host->max_clk / pre_div / div); + + pre_div >>= 1; + div--; + + temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); + temp |= (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN + | (div << ESDHC_DIVIDER_SHIFT) + | (pre_div << ESDHC_PREDIV_SHIFT)); + sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); + mdelay(100); +out: + host->clock = clock; +} + +#endif /* _DRIVERS_MMC_SDHCI_ESDHC_H */ diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index c8623de13af3..fcd0e1fcba44 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -18,23 +18,7 @@ #include #include "sdhci-of.h" #include "sdhci.h" - -/* - * Ops and quirks for the Freescale eSDHC controller. - */ - -#define ESDHC_DMA_SYSCTL 0x40c -#define ESDHC_DMA_SNOOP 0x00000040 - -#define ESDHC_SYSTEM_CONTROL 0x2c -#define ESDHC_CLOCK_MASK 0x0000fff0 -#define ESDHC_PREDIV_SHIFT 8 -#define ESDHC_DIVIDER_SHIFT 4 -#define ESDHC_CLOCK_PEREN 0x00000004 -#define ESDHC_CLOCK_HCKEN 0x00000002 -#define ESDHC_CLOCK_IPGEN 0x00000001 - -#define ESDHC_HOST_CONTROL_RES 0x05 +#include "sdhci-esdhc.h" static u16 esdhc_readw(struct sdhci_host *host, int reg) { @@ -68,51 +52,20 @@ static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg) sdhci_be32bs_writeb(host, val, reg); } -static void esdhc_set_clock(struct sdhci_host *host, unsigned int clock) -{ - int pre_div = 2; - int div = 1; - - clrbits32(host->ioaddr + ESDHC_SYSTEM_CONTROL, ESDHC_CLOCK_IPGEN | - ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN | ESDHC_CLOCK_MASK); - - if (clock == 0) - goto out; - - while (host->max_clk / pre_div / 16 > clock && pre_div < 256) - pre_div *= 2; - - while (host->max_clk / pre_div / div > clock && div < 16) - div++; - - dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n", - clock, host->max_clk / pre_div / div); - - pre_div >>= 1; - div--; - - setbits32(host->ioaddr + ESDHC_SYSTEM_CONTROL, ESDHC_CLOCK_IPGEN | - ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN | - div << ESDHC_DIVIDER_SHIFT | pre_div << ESDHC_PREDIV_SHIFT); - mdelay(100); -out: - host->clock = clock; -} - -static int esdhc_enable_dma(struct sdhci_host *host) +static int esdhc_of_enable_dma(struct sdhci_host *host) { setbits32(host->ioaddr + ESDHC_DMA_SYSCTL, ESDHC_DMA_SNOOP); return 0; } -static unsigned int esdhc_get_max_clock(struct sdhci_host *host) +static unsigned int esdhc_of_get_max_clock(struct sdhci_host *host) { struct sdhci_of_host *of_host = sdhci_priv(host); return of_host->clock; } -static unsigned int esdhc_get_min_clock(struct sdhci_host *host) +static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host) { struct sdhci_of_host *of_host = sdhci_priv(host); @@ -120,14 +73,7 @@ static unsigned int esdhc_get_min_clock(struct sdhci_host *host) } struct sdhci_of_data sdhci_esdhc = { - .quirks = SDHCI_QUIRK_FORCE_BLK_SZ_2048 | - SDHCI_QUIRK_BROKEN_CARD_DETECTION | - SDHCI_QUIRK_NO_BUSY_IRQ | - SDHCI_QUIRK_NONSTANDARD_CLOCK | - SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | - SDHCI_QUIRK_PIO_NEEDS_DELAY | - SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET | - SDHCI_QUIRK_NO_CARD_NO_RESET, + .quirks = ESDHC_DEFAULT_QUIRKS, .ops = { .read_l = sdhci_be32bs_readl, .read_w = esdhc_readw, @@ -136,8 +82,8 @@ struct sdhci_of_data sdhci_esdhc = { .write_w = esdhc_writew, .write_b = esdhc_writeb, .set_clock = esdhc_set_clock, - .enable_dma = esdhc_enable_dma, - .get_max_clock = esdhc_get_max_clock, - .get_min_clock = esdhc_get_min_clock, + .enable_dma = esdhc_of_enable_dma, + .get_max_clock = esdhc_of_get_max_clock, + .get_min_clock = esdhc_of_get_min_clock, }, }; -- cgit v1.2.3 From 95f25efe0ce22e28d61722d655d2ef582f5f7520 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 15 Oct 2010 12:21:04 +0200 Subject: mmc: sdhci-pltfm: add -pltfm driver for imx35/51 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This driver adds basic support for the esdhc-core found on e.g. imx35/51, as a platform driver. Signed-off-by: Wolfram Sang Acked-by: Anton Vorontsov Tested-by: Eric Bénard Signed-off-by: Chris Ball --- drivers/mmc/host/Kconfig | 10 +++ drivers/mmc/host/Makefile | 1 + drivers/mmc/host/sdhci-esdhc-imx.c | 143 +++++++++++++++++++++++++++++++++++++ drivers/mmc/host/sdhci-pltfm.c | 3 + drivers/mmc/host/sdhci-pltfm.h | 1 + 5 files changed, 158 insertions(+) create mode 100644 drivers/mmc/host/sdhci-esdhc-imx.c (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 0b7024a4ffc2..c9c2520a0c4a 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -130,6 +130,16 @@ config MMC_SDHCI_CNS3XXX If unsure, say N. +config MMC_SDHCI_ESDHC_IMX + bool "SDHCI platform support for the Freescale eSDHC i.MX controller" + depends on MMC_SDHCI_PLTFM && (ARCH_MX25 || ARCH_MX35 || ARCH_MX5) + select MMC_SDHCI_IO_ACCESSORS + help + This selects the Freescale eSDHC controller support on the platform + bus, found on platforms like mx35/51. + + If unsure, say N. + config MMC_SDHCI_S3C tristate "SDHCI support on Samsung S3C SoC" depends on MMC_SDHCI && PLAT_SAMSUNG diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index eae5d62711f7..6c4ac67f739f 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -37,6 +37,7 @@ obj-$(CONFIG_MMC_USHC) += ushc.o obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-platform.o sdhci-platform-y := sdhci-pltfm.o sdhci-platform-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.o +sdhci-platform-$(CONFIG_MMC_SDHCI_ESDHC_IMX) += sdhci-esdhc-imx.o obj-$(CONFIG_MMC_SDHCI_OF) += sdhci-of.o sdhci-of-y := sdhci-of-core.o diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c new file mode 100644 index 000000000000..2e9cca19c90b --- /dev/null +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -0,0 +1,143 @@ +/* + * Freescale eSDHC i.MX controller driver for the platform bus. + * + * derived from the OF-version. + * + * Copyright (c) 2010 Pengutronix e.K. + * Author: Wolfram Sang + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include "sdhci.h" +#include "sdhci-pltfm.h" +#include "sdhci-esdhc.h" + +static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, int reg) +{ + void __iomem *base = host->ioaddr + (reg & ~0x3); + u32 shift = (reg & 0x3) * 8; + + writel(((readl(base) & ~(mask << shift)) | (val << shift)), base); +} + +static u16 esdhc_readw_le(struct sdhci_host *host, int reg) +{ + if (unlikely(reg == SDHCI_HOST_VERSION)) + reg ^= 2; + + return readw(host->ioaddr + reg); +} + +static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + + switch (reg) { + case SDHCI_TRANSFER_MODE: + /* + * Postpone this write, we must do it together with a + * command write that is down below. + */ + pltfm_host->scratchpad = val; + return; + case SDHCI_COMMAND: + writel(val << 16 | pltfm_host->scratchpad, + host->ioaddr + SDHCI_TRANSFER_MODE); + return; + case SDHCI_BLOCK_SIZE: + val &= ~SDHCI_MAKE_BLKSZ(0x7, 0); + break; + } + esdhc_clrset_le(host, 0xffff, val, reg); +} + +static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg) +{ + u32 new_val; + + switch (reg) { + case SDHCI_POWER_CONTROL: + /* + * FSL put some DMA bits here + * If your board has a regulator, code should be here + */ + return; + case SDHCI_HOST_CONTROL: + /* FSL messed up here, so we can just keep those two */ + new_val = val & (SDHCI_CTRL_LED | SDHCI_CTRL_4BITBUS); + /* ensure the endianess */ + new_val |= ESDHC_HOST_CONTROL_LE; + /* DMA mode bits are shifted */ + new_val |= (val & SDHCI_CTRL_DMA_MASK) << 5; + + esdhc_clrset_le(host, 0xffff, new_val, reg); + return; + } + esdhc_clrset_le(host, 0xff, val, reg); +} + +static unsigned int esdhc_pltfm_get_max_clock(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + + return clk_get_rate(pltfm_host->clk); +} + +static unsigned int esdhc_pltfm_get_min_clock(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + + return clk_get_rate(pltfm_host->clk) / 256 / 16; +} + +static int esdhc_pltfm_init(struct sdhci_host *host, struct sdhci_pltfm_data *pdata) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct clk *clk; + + clk = clk_get(mmc_dev(host->mmc), NULL); + if (IS_ERR(clk)) { + dev_err(mmc_dev(host->mmc), "clk err\n"); + return PTR_ERR(clk); + } + clk_enable(clk); + pltfm_host->clk = clk; + + return 0; +} + +static void esdhc_pltfm_exit(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + + clk_disable(pltfm_host->clk); + clk_put(pltfm_host->clk); +} + +static struct sdhci_ops sdhci_esdhc_ops = { + .read_w = esdhc_readw_le, + .write_w = esdhc_writew_le, + .write_b = esdhc_writeb_le, + .set_clock = esdhc_set_clock, + .get_max_clock = esdhc_pltfm_get_max_clock, + .get_min_clock = esdhc_pltfm_get_min_clock, +}; + +struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = { + .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_NO_MULTIBLOCK + | SDHCI_QUIRK_BROKEN_ADMA, + /* ADMA has issues. Might be fixable */ + /* NO_MULTIBLOCK might be MX35 only (Errata: ENGcm07207) */ + .ops = &sdhci_esdhc_ops, + .init = esdhc_pltfm_init, + .exit = esdhc_pltfm_exit, +}; diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index 00e8a8ab638e..0502f89f662b 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -166,6 +166,9 @@ static const struct platform_device_id sdhci_pltfm_ids[] = { { "sdhci", }, #ifdef CONFIG_MMC_SDHCI_CNS3XXX { "sdhci-cns3xxx", (kernel_ulong_t)&sdhci_cns3xxx_pdata }, +#endif +#ifdef CONFIG_MMC_SDHCI_ESDHC_IMX + { "sdhci-esdhc-imx", (kernel_ulong_t)&sdhci_esdhc_imx_pdata }, #endif { }, }; diff --git a/drivers/mmc/host/sdhci-pltfm.h b/drivers/mmc/host/sdhci-pltfm.h index 562b92957f18..c1bfe48af56a 100644 --- a/drivers/mmc/host/sdhci-pltfm.h +++ b/drivers/mmc/host/sdhci-pltfm.h @@ -21,5 +21,6 @@ struct sdhci_pltfm_host { }; extern struct sdhci_pltfm_data sdhci_cns3xxx_pdata; +extern struct sdhci_pltfm_data sdhci_esdhc_imx_pdata; #endif /* _DRIVERS_MMC_SDHCI_PLTFM_H */ -- cgit v1.2.3 From 061c6c847eeb11743e489a16e907b41c6f9042b6 Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Mon, 12 Jul 2010 15:50:56 +0800 Subject: mmc_spi: Recover from CRC errors for r/w operation over SPI. The SPI bus is not reliable for large data transfers on all platforms. The current mmc_spi driver fails SD read/write commands immediately if occasional CRC errors are reported by the SD device. This patch makes the operation recover from CRC errors by repeating the last SD command. The retry count is set to 5 to ensure the driver passes stress tests. Signed-off-by: Sonic Zhang Signed-off-by: Mike Frysinger Signed-off-by: Chris Ball --- drivers/mmc/host/mmc_spi.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index 5b0b50636ec4..fd877f633dd2 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -1055,6 +1055,8 @@ static void mmc_spi_request(struct mmc_host *mmc, struct mmc_request *mrq) { struct mmc_spi_host *host = mmc_priv(mmc); int status = -EINVAL; + int crc_retry = 5; + struct mmc_command stop; #ifdef DEBUG /* MMC core and layered drivers *MUST* issue SPI-aware commands */ @@ -1087,10 +1089,29 @@ static void mmc_spi_request(struct mmc_host *mmc, struct mmc_request *mrq) /* request exclusive bus access */ spi_bus_lock(host->spi->master); +crc_recover: /* issue command; then optionally data and stop */ status = mmc_spi_command_send(host, mrq, mrq->cmd, mrq->data != NULL); if (status == 0 && mrq->data) { mmc_spi_data_do(host, mrq->cmd, mrq->data, mrq->data->blksz); + + /* + * The SPI bus is not always reliable for large data transfers. + * If an occasional crc error is reported by the SD device with + * data read/write over SPI, it may be recovered by repeating + * the last SD command again. The retry count is set to 5 to + * ensure the driver passes stress tests. + */ + if (mrq->data->error == -EILSEQ && crc_retry) { + stop.opcode = MMC_STOP_TRANSMISSION; + stop.arg = 0; + stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; + status = mmc_spi_command_send(host, mrq, &stop, 0); + crc_retry--; + mrq->data->error = 0; + goto crc_recover; + } + if (mrq->stop) status = mmc_spi_command_send(host, mrq, mrq->stop, 0); else -- cgit v1.2.3 From 536ac998f6076a0ae423b1046b85d7690e8b7107 Mon Sep 17 00:00:00 2001 From: Zhangfei Gao Date: Mon, 20 Sep 2010 10:51:28 -0400 Subject: mmc: add new sdhci-pxa driver for Marvell SoCs Support Marvell PXA168/PXA910/MMP2 SD Host Controller. Signed-off-by: Zhangfei Gao Acked-by: Haojian Zhuang Signed-off-by: Chris Ball --- drivers/mmc/host/Kconfig | 12 ++ drivers/mmc/host/Makefile | 1 + drivers/mmc/host/sdhci-pxa.c | 253 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 266 insertions(+) create mode 100644 drivers/mmc/host/sdhci-pxa.c (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index c9c2520a0c4a..40b0fb9a47fb 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -155,6 +155,18 @@ config MMC_SDHCI_S3C If unsure, say N. +config MMC_SDHCI_PXA + tristate "Marvell PXA168/PXA910/MMP2 SD Host Controller support" + depends on ARCH_PXA || ARCH_MMP + select MMC_SDHCI + select MMC_SDHCI_IO_ACCESSORS + help + This selects the Marvell(R) PXA168/PXA910/MMP2 SD Host Controller. + If you have a PXA168/PXA910/MMP2 platform with SD Host Controller + and a card slot, say Y or M here. + + If unsure, say N. + config MMC_SDHCI_SPEAR tristate "SDHCI support on ST SPEAr platform" depends on MMC_SDHCI && PLAT_SPEAR diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 6c4ac67f739f..7b645ff43b30 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -8,6 +8,7 @@ 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_PXA) += sdhci-pxa.o obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o obj-$(CONFIG_MMC_SDHCI_SPEAR) += sdhci-spear.o obj-$(CONFIG_MMC_WBSD) += wbsd.o diff --git a/drivers/mmc/host/sdhci-pxa.c b/drivers/mmc/host/sdhci-pxa.c new file mode 100644 index 000000000000..fc406ac5d193 --- /dev/null +++ b/drivers/mmc/host/sdhci-pxa.c @@ -0,0 +1,253 @@ +/* linux/drivers/mmc/host/sdhci-pxa.c + * + * Copyright (C) 2010 Marvell International Ltd. + * Zhangfei Gao + * Kevin Wang + * Mingwei Wang + * Philip Rakity + * Mark Brown + * + * 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. + */ + +/* Supports: + * SDHCI support for MMP2/PXA910/PXA168 + * + * Refer to sdhci-s3c.c. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "sdhci.h" + +#define DRIVER_NAME "sdhci-pxa" + +#define SD_FIFO_PARAM 0x104 +#define DIS_PAD_SD_CLK_GATE 0x400 + +struct sdhci_pxa { + struct sdhci_host *host; + struct sdhci_pxa_platdata *pdata; + struct clk *clk; + struct resource *res; + + u8 clk_enable; +}; + +/*****************************************************************************\ + * * + * SDHCI core callbacks * + * * +\*****************************************************************************/ +static void set_clock(struct sdhci_host *host, unsigned int clock) +{ + struct sdhci_pxa *pxa = sdhci_priv(host); + u32 tmp = 0; + + if (clock == 0) { + if (pxa->clk_enable) { + clk_disable(pxa->clk); + pxa->clk_enable = 0; + } + } else { + if (0 == pxa->clk_enable) { + if (pxa->pdata->flags & PXA_FLAG_DISABLE_CLOCK_GATING) { + tmp = readl(host->ioaddr + SD_FIFO_PARAM); + tmp |= DIS_PAD_SD_CLK_GATE; + writel(tmp, host->ioaddr + SD_FIFO_PARAM); + } + clk_enable(pxa->clk); + pxa->clk_enable = 1; + } + } +} + +static struct sdhci_ops sdhci_pxa_ops = { + .set_clock = set_clock, +}; + +/*****************************************************************************\ + * * + * Device probing/removal * + * * +\*****************************************************************************/ + +static int __devinit sdhci_pxa_probe(struct platform_device *pdev) +{ + struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data; + struct device *dev = &pdev->dev; + struct sdhci_host *host = NULL; + struct resource *iomem = NULL; + struct sdhci_pxa *pxa = NULL; + int ret, irq; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev, "no irq specified\n"); + return irq; + } + + iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!iomem) { + dev_err(dev, "no memory specified\n"); + return -ENOENT; + } + + host = sdhci_alloc_host(&pdev->dev, sizeof(struct sdhci_pxa)); + if (IS_ERR(host)) { + dev_err(dev, "failed to alloc host\n"); + return PTR_ERR(host); + } + + pxa = sdhci_priv(host); + pxa->host = host; + pxa->pdata = pdata; + pxa->clk_enable = 0; + + pxa->clk = clk_get(dev, "PXA-SDHCLK"); + if (IS_ERR(pxa->clk)) { + dev_err(dev, "failed to get io clock\n"); + ret = PTR_ERR(pxa->clk); + goto out; + } + + pxa->res = request_mem_region(iomem->start, resource_size(iomem), + mmc_hostname(host->mmc)); + if (!pxa->res) { + dev_err(&pdev->dev, "cannot request region\n"); + ret = -EBUSY; + goto out; + } + + host->ioaddr = ioremap(iomem->start, resource_size(iomem)); + if (!host->ioaddr) { + dev_err(&pdev->dev, "failed to remap registers\n"); + ret = -ENOMEM; + goto out; + } + + host->hw_name = "MMC"; + host->ops = &sdhci_pxa_ops; + host->irq = irq; + host->quirks = SDHCI_QUIRK_BROKEN_ADMA | SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; + + if (pdata->quirks) + host->quirks |= pdata->quirks; + + ret = sdhci_add_host(host); + if (ret) { + dev_err(&pdev->dev, "failed to add host\n"); + goto out; + } + + if (pxa->pdata->max_speed) + host->mmc->f_max = pxa->pdata->max_speed; + + platform_set_drvdata(pdev, host); + + return 0; +out: + if (host) { + clk_put(pxa->clk); + if (host->ioaddr) + iounmap(host->ioaddr); + if (pxa->res) + release_mem_region(pxa->res->start, + resource_size(pxa->res)); + sdhci_free_host(host); + } + + return ret; +} + +static int __devexit sdhci_pxa_remove(struct platform_device *pdev) +{ + struct sdhci_host *host = platform_get_drvdata(pdev); + struct sdhci_pxa *pxa = sdhci_priv(host); + int dead = 0; + u32 scratch; + + if (host) { + scratch = readl(host->ioaddr + SDHCI_INT_STATUS); + if (scratch == (u32)-1) + dead = 1; + + sdhci_remove_host(host, dead); + + if (host->ioaddr) + iounmap(host->ioaddr); + if (pxa->res) + release_mem_region(pxa->res->start, + resource_size(pxa->res)); + if (pxa->clk_enable) { + clk_disable(pxa->clk); + pxa->clk_enable = 0; + } + clk_put(pxa->clk); + + sdhci_free_host(host); + platform_set_drvdata(pdev, NULL); + } + + return 0; +} + +#ifdef CONFIG_PM +static int sdhci_pxa_suspend(struct platform_device *dev, pm_message_t state) +{ + struct sdhci_host *host = platform_get_drvdata(dev); + + return sdhci_suspend_host(host, state); +} + +static int sdhci_pxa_resume(struct platform_device *dev) +{ + struct sdhci_host *host = platform_get_drvdata(dev); + + return sdhci_resume_host(host); +} +#else +#define sdhci_pxa_suspend NULL +#define sdhci_pxa_resume NULL +#endif + +static struct platform_driver sdhci_pxa_driver = { + .probe = sdhci_pxa_probe, + .remove = __devexit_p(sdhci_pxa_remove), + .suspend = sdhci_pxa_suspend, + .resume = sdhci_pxa_resume, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; + +/*****************************************************************************\ + * * + * Driver init/exit * + * * +\*****************************************************************************/ + +static int __init sdhci_pxa_init(void) +{ + return platform_driver_register(&sdhci_pxa_driver); +} + +static void __exit sdhci_pxa_exit(void) +{ + platform_driver_unregister(&sdhci_pxa_driver); +} + +module_init(sdhci_pxa_init); +module_exit(sdhci_pxa_exit); + +MODULE_DESCRIPTION("SDH controller driver for PXA168/PXA910/MMP2"); +MODULE_AUTHOR("Zhangfei Gao "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3