diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-10-28 09:33:42 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-10-28 09:33:42 -0700 |
commit | 00ebb6382b8d9c7c15b5f8ad230670d8161d38dd (patch) | |
tree | 23591394b83776953aaf0b382d4c7b09e0ca1e34 /drivers/mmc/host | |
parent | 11cc21f5f5575b9abd14d53a6055ccbf72b67573 (diff) | |
parent | 536ac998f6076a0ae423b1046b85d7690e8b7107 (diff) | |
download | linux-00ebb6382b8d9c7c15b5f8ad230670d8161d38dd.tar.gz linux-00ebb6382b8d9c7c15b5f8ad230670d8161d38dd.tar.bz2 linux-00ebb6382b8d9c7c15b5f8ad230670d8161d38dd.zip |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc: (66 commits)
mmc: add new sdhci-pxa driver for Marvell SoCs
mmc: make number of mmcblk minors configurable
mmc_spi: Recover from CRC errors for r/w operation over SPI.
mmc: sdhci-pltfm: add -pltfm driver for imx35/51
mmc: sdhci-of-esdhc: factor out common stuff
mmc: sdhci_pltfm: pass more data on custom init call
mmc: sdhci: introduce get_ro private write-protect hook
mmc: sdhci-pltfm: move .h file into appropriate subdir
mmc: sdhci-pltfm: Add structure for host-specific data
mmc: fix cb710 kconfig dependency warning
mmc: cb710: remove debugging printk (info duplicated from mmc-core)
mmc: cb710: clear irq handler on init() error path
mmc: cb710: remove unnecessary msleep()
mmc: cb710: implement get_cd() callback
mmc: cb710: partially demystify clock selection
mmc: add a file to debugfs for changing host clock at runtime
mmc: sdhci: allow for eMMC 74 clock generation by controller
mmc: sdhci: highspeed: check for mmc as well as sd cards
mmc: sdhci: Add Moorestown device support
mmc: sdhci: Intel Medfield support
...
Diffstat (limited to 'drivers/mmc/host')
34 files changed, 1445 insertions, 334 deletions
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 1a0261160e56..d618e8673996 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 @@ -145,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 @@ -395,6 +417,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 @@ -451,3 +474,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..7b645ff43b30 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -2,16 +2,13 @@ # 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 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 @@ -36,10 +33,12 @@ 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 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/at91_mci.c b/drivers/mmc/host/at91_mci.c index 87226cd202a5..591ab540b407 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); @@ -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); @@ -1017,7 +1016,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 +1092,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 +1137,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); diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 95ef864ad8f9..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; @@ -1777,7 +1776,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; diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c index c8da5d30a861..41e5a60493ad 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"); @@ -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/cb710-mmc.c b/drivers/mmc/host/cb710-mmc.c index ca3bdc831900..66b4ce587f4b 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, @@ -95,16 +100,8 @@ 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", - enable ? 4 : 1, enable ? "s" : ""); if (enable) cb710_modify_port_8(slot, CB710_MMC_CONFIG1_PORT, CB710_MMC_C1_4BIT_DATA_BUS, 0); @@ -494,13 +491,8 @@ 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); tasklet_schedule(&reader->finish_req_tasklet); } @@ -512,7 +504,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,13 +564,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); - - 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; - } + cb710_mmc_select_clock_divider(mmc, ios->clock); if (ios->power_mode != reader->last_power_mode) switch (ios->power_mode) { @@ -619,6 +605,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); @@ -664,7 +658,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 @@ -746,6 +741,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; } 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..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 @@ -1345,8 +1366,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..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); @@ -734,8 +742,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 @@ -870,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/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 4693e62145a6..e865032a52eb 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) @@ -2130,8 +2135,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..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) @@ -576,7 +601,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-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 <linux/delay.h> #include <linux/device.h> #include <linux/mmc/host.h> -#include <linux/sdhci-pltfm.h> +#include <linux/mmc/sdhci-pltfm.h> #include <mach/cns3xxx.h> #include "sdhci.h" #include "sdhci-pltfm.h" 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 <w.sang@pengutronix.de> + * + * 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 <linux/io.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/mmc/host.h> +#include <linux/mmc/sdhci-pltfm.h> +#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-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 <w.sang@pengutronix.de> + * + * 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 <linux/mmc/host.h> #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, }, }; diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index e8aa99deae9a..55746bac2f44 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -145,6 +145,37 @@ 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, +}; + +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 +525,62 @@ 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, + .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) }, @@ -818,6 +905,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)) { diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index e045e3c61dde..0502f89f662b 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -30,7 +30,7 @@ #include <linux/mmc/host.h> #include <linux/io.h> -#include <linux/sdhci-pltfm.h> +#include <linux/mmc/sdhci-pltfm.h> #include "sdhci.h" #include "sdhci-pltfm.h" @@ -52,14 +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) { @@ -71,16 +74,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; @@ -105,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; } @@ -161,10 +167,32 @@ static const struct platform_device_id sdhci_pltfm_ids[] = { #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 { }, }; 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 +201,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, }; /*****************************************************************************\ diff --git a/drivers/mmc/host/sdhci-pltfm.h b/drivers/mmc/host/sdhci-pltfm.h index 900f32902f73..c1bfe48af56a 100644 --- a/drivers/mmc/host/sdhci-pltfm.h +++ b/drivers/mmc/host/sdhci-pltfm.h @@ -11,8 +11,16 @@ #ifndef _DRIVERS_MMC_SDHCI_PLTFM_H #define _DRIVERS_MMC_SDHCI_PLTFM_H -#include <linux/sdhci-pltfm.h> +#include <linux/clk.h> +#include <linux/types.h> +#include <linux/mmc/sdhci-pltfm.h> + +struct sdhci_pltfm_host { + struct clk *clk; + u32 scratchpad; /* to handle quirks across io-accessor calls */ +}; extern struct sdhci_pltfm_data sdhci_cns3xxx_pdata; +extern struct sdhci_pltfm_data sdhci_esdhc_imx_pdata; #endif /* _DRIVERS_MMC_SDHCI_PLTFM_H */ 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 <zhangfei.gao@marvell.com> + * Kevin Wang <dwang4@marvell.com> + * Mingwei Wang <mwwang@marvell.com> + * Philip Rakity <prakity@marvell.com> + * Mark Brown <markb@marvell.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* Supports: + * SDHCI support for MMP2/PXA910/PXA168 + * + * Refer to sdhci-s3c.c. + */ + +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/mmc/host.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/err.h> +#include <plat/sdhci.h> +#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 <zhangfei.gao@marvell.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 401527d273b5..782c0ee3c925 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), @@ -1001,13 +1002,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 < 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 < SDHCI_MAX_DIV_SPEC_200; 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); @@ -1034,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; @@ -1168,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) @@ -1180,8 +1197,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; @@ -1205,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) @@ -1427,7 +1448,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); @@ -1708,7 +1729,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); @@ -1779,8 +1800,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) { @@ -1815,18 +1841,21 @@ 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; 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->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; @@ -1850,12 +1879,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/sdhci.h b/drivers/mmc/host/sdhci.h index d316bc79b636..b7b8a3b28b01 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 <linux/scatterlist.h> #include <linux/compiler.h> #include <linux/types.h> #include <linux/io.h> +#include <linux/mmc/sdhci.h> + /* * Controller registers */ @@ -86,6 +90,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 @@ -140,6 +148,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 @@ -178,134 +187,14 @@ #define SDHCI_SPEC_VER_SHIFT 0 #define SDHCI_SPEC_100 0 #define SDHCI_SPEC_200 1 +#define SDHCI_SPEC_300 2 -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; -}; +/* + * End of controller registers. + */ +#define SDHCI_MAX_DIV_SPEC_200 256 +#define SDHCI_MAX_DIV_SPEC_300 2046 struct sdhci_ops { #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS @@ -323,6 +212,9 @@ 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); + unsigned int (*get_ro)(struct sdhci_host *host); }; #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS @@ -427,4 +319,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 */ 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/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 <linux/module.h> +#include <linux/usb.h> +#include <linux/kernel.h> +#include <linux/usb.h> +#include <linux/slab.h> +#include <linux/dma-mapping.h> +#include <linux/mmc/host.h> + +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 <david.vrabel@csr.com>"); +MODULE_LICENSE("GPL"); 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. |