From 4265900e0be653f5b78baf2816857ef57cf1332f Mon Sep 17 00:00:00 2001 From: Saugata Das Date: Wed, 21 Dec 2011 13:09:17 +0530 Subject: mmc: MMC-4.5 Data Tag Support MMC-4.5 data tag feature will be used to store the file system meta-data in the tagged region of eMMC. This will improve the write and subsequent read transfer time for the meta data. Signed-off-by: Saugata Das Tested-by: Venkatraman S Signed-off-by: Chris Ball --- drivers/mmc/card/block.c | 21 ++++++++++++++++----- drivers/mmc/core/mmc.c | 14 ++++++++++++++ 2 files changed, 30 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index e5a3c7b6dedb..f2020d37c658 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -1080,6 +1080,7 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq, struct mmc_blk_request *brq = &mqrq->brq; struct request *req = mqrq->req; struct mmc_blk_data *md = mq->data; + bool do_data_tag; /* * Reliable writes are used to implement Forced Unit Access and @@ -1155,6 +1156,16 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq, if (do_rel_wr) mmc_apply_rel_rw(brq, card, req); + /* + * Data tag is used only during writing meta data to speed + * up write and any subsequent read of this meta data + */ + do_data_tag = (card->ext_csd.data_tag_unit_size) && + (req->cmd_flags & REQ_META) && + (rq_data_dir(req) == WRITE) && + ((brq->data.blocks * brq->data.blksz) >= + card->ext_csd.data_tag_unit_size); + /* * Pre-defined multi-block transfers are preferable to * open ended-ones (and necessary for reliable writes). @@ -1173,13 +1184,13 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq, * We'll avoid using CMD23-bounded multiblock writes for * these, while retaining features like reliable writes. */ - - if ((md->flags & MMC_BLK_CMD23) && - mmc_op_multi(brq->cmd.opcode) && - (do_rel_wr || !(card->quirks & MMC_QUIRK_BLK_NO_CMD23))) { + if ((md->flags & MMC_BLK_CMD23) && mmc_op_multi(brq->cmd.opcode) && + (do_rel_wr || !(card->quirks & MMC_QUIRK_BLK_NO_CMD23) || + do_data_tag)) { brq->sbc.opcode = MMC_SET_BLOCK_COUNT; brq->sbc.arg = brq->data.blocks | - (do_rel_wr ? (1 << 31) : 0); + (do_rel_wr ? (1 << 31) : 0) | + (do_data_tag ? (1 << 29) : 0); brq->sbc.flags = MMC_RSP_R1 | MMC_CMD_AC; brq->mrq.sbc = &brq->sbc; } diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 2b9ed1401dc4..6defddd795f6 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -519,6 +519,20 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) ext_csd[EXT_CSD_CACHE_SIZE + 1] << 8 | ext_csd[EXT_CSD_CACHE_SIZE + 2] << 16 | ext_csd[EXT_CSD_CACHE_SIZE + 3] << 24; + + if (ext_csd[EXT_CSD_DATA_SECTOR_SIZE] == 1) + card->ext_csd.data_sector_size = 4096; + else + card->ext_csd.data_sector_size = 512; + + if ((ext_csd[EXT_CSD_DATA_TAG_SUPPORT] & 1) && + (ext_csd[EXT_CSD_TAG_UNIT_SIZE] <= 8)) { + card->ext_csd.data_tag_unit_size = + ((unsigned int) 1 << ext_csd[EXT_CSD_TAG_UNIT_SIZE]) * + (card->ext_csd.data_sector_size); + } else { + card->ext_csd.data_tag_unit_size = 0; + } } out: -- cgit v1.2.3 From 356ac2cf066174aecc3380f82fabe8a01f046c3f Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Fri, 13 Jan 2012 17:31:32 +0900 Subject: mmc: dw_mmc: add support for eMMC Power Off Notify feature This patch adds the capability of power off notify for dw-mmc controller. In order to use Girish's patch: mmc: core: Add Power Off Notify Feature eMMC 4.5 Signed-off-by: Jaehoon Chung Signed-off-by: Kyungmin Park Acked-by: Will Newton Signed-off-by: Chris Ball --- drivers/mmc/host/dw_mmc.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 8bec1c36b159..1dfcdef452b9 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1720,6 +1720,11 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id) if (host->pdata->quirks & DW_MCI_QUIRK_HIGHSPEED) mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; + if (mmc->caps2 & MMC_CAP2_POWEROFF_NOTIFY) + mmc->power_notify_type = MMC_HOST_PW_NOTIFY_SHORT; + else + mmc->power_notify_type = MMC_HOST_PW_NOTIFY_NONE; + #ifdef CONFIG_MMC_DW_IDMAC mmc->max_segs = host->ring_size; mmc->max_blk_size = 65536; -- cgit v1.2.3 From 62ca8034d940439490a450ae7e2b7702591e3192 Mon Sep 17 00:00:00 2001 From: Shashidhar Hiremath Date: Fri, 13 Jan 2012 16:04:57 +0530 Subject: mmc: Support of PCI mode in the dw_mmc driver Support of PCI mode for the dw_mmc driver. This Patch adds the support for the scenario where the Synopsys Designware IP is present on the PCI bus. The patch adds the minimal modifications necessary for the driver to work on PCI platform. Also added separate files for PCI and PLATFORM modes of operation. Signed-off-by: Shashidhar Hiremath Acked-by: James Hogan Signed-off-by: Chris Ball --- drivers/mmc/host/Kconfig | 25 +++++++ drivers/mmc/host/Makefile | 2 + drivers/mmc/host/dw_mmc-pci.c | 158 ++++++++++++++++++++++++++++++++++++++++ drivers/mmc/host/dw_mmc-pltfm.c | 134 ++++++++++++++++++++++++++++++++++ drivers/mmc/host/dw_mmc.c | 155 ++++++++++++++------------------------- drivers/mmc/host/dw_mmc.h | 7 ++ include/linux/mmc/dw_mmc.h | 8 +- 7 files changed, 385 insertions(+), 104 deletions(-) create mode 100644 drivers/mmc/host/dw_mmc-pci.c create mode 100644 drivers/mmc/host/dw_mmc-pltfm.c (limited to 'drivers') diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 00fcbed1afd2..0c9b3b12652b 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -533,6 +533,31 @@ config MMC_DW_IDMAC Designware Mobile Storage IP block. This disables the external DMA interface. +config MMC_DW_PLTFM + tristate "Synopsys Designware MCI Support as platform device" + depends on MMC_DW + default y + help + This selects the common helper functions support for Host Controller + Interface based platform driver. Please select this option if the IP + is present as a platform device. This is the common interface for the + Synopsys Designware IP. + + If you have a controller with this interface, say Y or M here. + + If unsure, say Y. + +config MMC_DW_PCI + tristate "Synopsys Designware MCI support on PCI bus" + depends on MMC_DW && PCI + help + This selects the PCI bus for the Synopsys Designware Mobile Storage IP. + Select this option if the IP is present on PCI platform. + + If you have a controller with this interface, say Y or M here. + + If unsure, say N. + config MMC_SH_MMCIF tristate "SuperH Internal MMCIF support" depends on MMC_BLOCK && (SUPERH || ARCH_SHMOBILE) diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 745f8fce2519..3e7e26d08073 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -39,6 +39,8 @@ obj-$(CONFIG_MMC_CB710) += cb710-mmc.o obj-$(CONFIG_MMC_VIA_SDMMC) += via-sdmmc.o obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o obj-$(CONFIG_MMC_DW) += dw_mmc.o +obj-$(CONFIG_MMC_DW_PLTFM) += dw_mmc-pltfm.o +obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o obj-$(CONFIG_MMC_VUB300) += vub300.o diff --git a/drivers/mmc/host/dw_mmc-pci.c b/drivers/mmc/host/dw_mmc-pci.c new file mode 100644 index 000000000000..dc0d25a013e0 --- /dev/null +++ b/drivers/mmc/host/dw_mmc-pci.c @@ -0,0 +1,158 @@ +/* + * Synopsys DesignWare Multimedia Card PCI Interface driver + * + * Copyright (C) 2012 Vayavya Labs Pvt. 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dw_mmc.h" + +#define PCI_BAR_NO 2 +#define COMPLETE_BAR 0 +#define SYNOPSYS_DW_MCI_VENDOR_ID 0x700 +#define SYNOPSYS_DW_MCI_DEVICE_ID 0x1107 +/* Defining the Capabilities */ +#define DW_MCI_CAPABILITIES (MMC_CAP_4_BIT_DATA | MMC_CAP_MMC_HIGHSPEED |\ + MMC_CAP_SD_HIGHSPEED | MMC_CAP_8_BIT_DATA |\ + MMC_CAP_SDIO_IRQ) + +static struct dw_mci_board pci_board_data = { + .num_slots = 1, + .caps = DW_MCI_CAPABILITIES, + .bus_hz = 33 * 1000 * 1000, + .detect_delay_ms = 200, + .fifo_depth = 32, +}; + +static int __devinit dw_mci_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *entries) +{ + struct dw_mci *host; + int ret; + + ret = pci_enable_device(pdev); + if (ret) + return ret; + if (pci_request_regions(pdev, "dw_mmc_pci")) { + ret = -ENODEV; + goto err_disable_dev; + } + + host = kzalloc(sizeof(struct dw_mci), GFP_KERNEL); + if (!host) { + ret = -ENOMEM; + goto err_release; + } + + host->irq = pdev->irq; + host->irq_flags = IRQF_SHARED; + host->dev = pdev->dev; + host->pdata = &pci_board_data; + + host->regs = pci_iomap(pdev, PCI_BAR_NO, COMPLETE_BAR); + if (!host->regs) { + ret = -EIO; + goto err_unmap; + } + + pci_set_drvdata(pdev, host); + ret = dw_mci_probe(host); + if (ret) + goto err_probe_failed; + return ret; + +err_probe_failed: + pci_iounmap(pdev, host->regs); +err_unmap: + kfree(host); +err_release: + pci_release_regions(pdev); +err_disable_dev: + pci_disable_device(pdev); + return ret; +} + +static void __devexit dw_mci_pci_remove(struct pci_dev *pdev) +{ + struct dw_mci *host = pci_get_drvdata(pdev); + + dw_mci_remove(host); + pci_set_drvdata(pdev, NULL); + pci_release_regions(pdev); + pci_iounmap(pdev, host->regs); + kfree(host); + pci_disable_device(pdev); +} + +#ifdef CONFIG_PM_SLEEP +static int dw_mci_pci_suspend(struct device *dev) +{ + int ret; + struct pci_dev *pdev = to_pci_dev(dev); + struct dw_mci *host = pci_get_drvdata(pdev); + + ret = dw_mci_suspend(host); + return ret; +} + +static int dw_mci_pci_resume(struct device *dev) +{ + int ret; + struct pci_dev *pdev = to_pci_dev(dev); + struct dw_mci *host = pci_get_drvdata(pdev); + + ret = dw_mci_resume(host); + return ret; +} +#else +#define dw_mci_pci_suspend NULL +#define dw_mci_pci_resume NULL +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(dw_mci_pci_pmops, dw_mci_pci_suspend, dw_mci_pci_resume); + +static DEFINE_PCI_DEVICE_TABLE(dw_mci_pci_id) = { + { PCI_DEVICE(SYNOPSYS_DW_MCI_VENDOR_ID, SYNOPSYS_DW_MCI_DEVICE_ID) }, + {} +}; +MODULE_DEVICE_TABLE(pci, dw_mci_pci_id); + +static struct pci_driver dw_mci_pci_driver = { + .name = "dw_mmc_pci", + .id_table = dw_mci_pci_id, + .probe = dw_mci_pci_probe, + .remove = dw_mci_pci_remove, + .driver = { + .pm = &dw_mci_pci_pmops + }, +}; + +static int __init dw_mci_init(void) +{ + return pci_register_driver(&dw_mci_pci_driver); +} + +static void __exit dw_mci_exit(void) +{ + pci_unregister_driver(&dw_mci_pci_driver); +} + +module_init(dw_mci_init); +module_exit(dw_mci_exit); + +MODULE_DESCRIPTION("DW Multimedia Card PCI Interface driver"); +MODULE_AUTHOR("Shashidhar Hiremath "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c new file mode 100644 index 000000000000..92ec3eb3aae7 --- /dev/null +++ b/drivers/mmc/host/dw_mmc-pltfm.c @@ -0,0 +1,134 @@ +/* + * Synopsys DesignWare Multimedia Card Interface driver + * + * Copyright (C) 2009 NXP Semiconductors + * Copyright (C) 2009, 2010 Imagination Technologies 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dw_mmc.h" + +static int dw_mci_pltfm_probe(struct platform_device *pdev) +{ + struct dw_mci *host; + struct resource *regs; + int ret; + + host = kzalloc(sizeof(struct dw_mci), GFP_KERNEL); + if (!host) + return -ENOMEM; + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) { + ret = -ENXIO; + goto err_free; + } + + host->irq = platform_get_irq(pdev, 0); + if (host->irq < 0) { + ret = host->irq; + goto err_free; + } + + host->dev = pdev->dev; + host->irq_flags = 0; + host->pdata = pdev->dev.platform_data; + ret = -ENOMEM; + host->regs = ioremap(regs->start, resource_size(regs)); + if (!host->regs) + goto err_free; + platform_set_drvdata(pdev, host); + ret = dw_mci_probe(host); + if (ret) + goto err_out; + return ret; +err_out: + iounmap(host->regs); +err_free: + kfree(host); + return ret; +} + +static int __exit dw_mci_pltfm_remove(struct platform_device *pdev) +{ + struct dw_mci *host = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + dw_mci_remove(host); + iounmap(host->regs); + kfree(host); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +/* + * TODO: we should probably disable the clock to the card in the suspend path. + */ +static int dw_mci_pltfm_suspend(struct device *dev) +{ + int ret; + struct dw_mci *host = dev_get_drvdata(dev); + + ret = dw_mci_suspend(host); + if (ret) + return ret; + + return 0; +} + +static int dw_mci_pltfm_resume(struct device *dev) +{ + int ret; + struct dw_mci *host = dev_get_drvdata(dev); + + ret = dw_mci_resume(host); + if (ret) + return ret; + + return 0; +} +#else +#define dw_mci_pltfm_suspend NULL +#define dw_mci_pltfm_resume NULL +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(dw_mci_pltfm_pmops, dw_mci_pltfm_suspend, dw_mci_pltfm_resume); + +static struct platform_driver dw_mci_pltfm_driver = { + .remove = __exit_p(dw_mci_pltfm_remove), + .driver = { + .name = "dw_mmc", + .pm = &dw_mci_pltfm_pmops, + }, +}; + +static int __init dw_mci_init(void) +{ + return platform_driver_probe(&dw_mci_pltfm_driver, dw_mci_pltfm_probe); +} + +static void __exit dw_mci_exit(void) +{ + platform_driver_unregister(&dw_mci_pltfm_driver); +} + +module_init(dw_mci_init); +module_exit(dw_mci_exit); + +MODULE_DESCRIPTION("DW Multimedia Card Interface driver"); +MODULE_AUTHOR("NXP Semiconductor VietNam"); +MODULE_AUTHOR("Imagination Technologies Ltd"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 1dfcdef452b9..987a26549711 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -268,7 +268,7 @@ static void dw_mci_start_command(struct dw_mci *host, struct mmc_command *cmd, u32 cmd_flags) { host->cmd = cmd; - dev_vdbg(&host->pdev->dev, + dev_vdbg(&host->dev, "start command: ARGR=0x%08x CMDR=0x%08x\n", cmd->arg, cmd_flags); @@ -301,7 +301,7 @@ static void dw_mci_dma_cleanup(struct dw_mci *host) struct mmc_data *data = host->data; if (data) - dma_unmap_sg(&host->pdev->dev, data->sg, data->sg_len, + dma_unmap_sg(&host->dev, data->sg, data->sg_len, ((data->flags & MMC_DATA_WRITE) ? DMA_TO_DEVICE : DMA_FROM_DEVICE)); } @@ -326,7 +326,7 @@ static void dw_mci_idmac_complete_dma(struct dw_mci *host) { struct mmc_data *data = host->data; - dev_vdbg(&host->pdev->dev, "DMA complete\n"); + dev_vdbg(&host->dev, "DMA complete\n"); host->dma_ops->cleanup(host); @@ -462,10 +462,10 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) else direction = DMA_TO_DEVICE; - sg_len = dma_map_sg(&host->pdev->dev, data->sg, data->sg_len, + sg_len = dma_map_sg(&host->dev, data->sg, data->sg_len, direction); - dev_vdbg(&host->pdev->dev, + dev_vdbg(&host->dev, "sd sg_cpu: %#lx sg_dma: %#lx sg_len: %d\n", (unsigned long)host->sg_cpu, (unsigned long)host->sg_dma, sg_len); @@ -821,12 +821,12 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) slot = list_entry(host->queue.next, struct dw_mci_slot, queue_node); list_del(&slot->queue_node); - dev_vdbg(&host->pdev->dev, "list not empty: %s is next\n", + dev_vdbg(&host->dev, "list not empty: %s is next\n", mmc_hostname(slot->mmc)); host->state = STATE_SENDING_CMD; dw_mci_start_request(host, slot); } else { - dev_vdbg(&host->pdev->dev, "list empty\n"); + dev_vdbg(&host->dev, "list empty\n"); host->state = STATE_IDLE; } @@ -965,7 +965,7 @@ static void dw_mci_tasklet_func(unsigned long priv) data->bytes_xfered = 0; data->error = -ETIMEDOUT; } else { - dev_err(&host->pdev->dev, + dev_err(&host->dev, "data FIFO error " "(status=%08x)\n", status); @@ -1682,7 +1682,7 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id) struct mmc_host *mmc; struct dw_mci_slot *slot; - mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), &host->pdev->dev); + mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), &host->dev); if (!mmc) return -ENOMEM; @@ -1794,10 +1794,10 @@ static void dw_mci_cleanup_slot(struct dw_mci_slot *slot, unsigned int id) static void dw_mci_init_dma(struct dw_mci *host) { /* Alloc memory for sg translation */ - host->sg_cpu = dma_alloc_coherent(&host->pdev->dev, PAGE_SIZE, + host->sg_cpu = dma_alloc_coherent(&host->dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL); if (!host->sg_cpu) { - dev_err(&host->pdev->dev, "%s: could not alloc DMA memory\n", + dev_err(&host->dev, "%s: could not alloc DMA memory\n", __func__); goto no_dma; } @@ -1805,7 +1805,7 @@ static void dw_mci_init_dma(struct dw_mci *host) /* Determine which DMA interface to use */ #ifdef CONFIG_MMC_DW_IDMAC host->dma_ops = &dw_mci_idmac_ops; - dev_info(&host->pdev->dev, "Using internal DMA controller.\n"); + dev_info(&host->dev, "Using internal DMA controller.\n"); #endif if (!host->dma_ops) @@ -1813,12 +1813,12 @@ static void dw_mci_init_dma(struct dw_mci *host) if (host->dma_ops->init) { if (host->dma_ops->init(host)) { - dev_err(&host->pdev->dev, "%s: Unable to initialize " + dev_err(&host->dev, "%s: Unable to initialize " "DMA Controller.\n", __func__); goto no_dma; } } else { - dev_err(&host->pdev->dev, "DMA initialization not found.\n"); + dev_err(&host->dev, "DMA initialization not found.\n"); goto no_dma; } @@ -1826,7 +1826,7 @@ static void dw_mci_init_dma(struct dw_mci *host) return; no_dma: - dev_info(&host->pdev->dev, "Using PIO mode.\n"); + dev_info(&host->dev, "Using PIO mode.\n"); host->use_dma = 0; return; } @@ -1852,61 +1852,37 @@ static bool mci_wait_reset(struct device *dev, struct dw_mci *host) return false; } -static int dw_mci_probe(struct platform_device *pdev) +int dw_mci_probe(struct dw_mci *host) { - struct dw_mci *host; - struct resource *regs; - struct dw_mci_board *pdata; - int irq, ret, i, width; + int width, i, ret = 0; u32 fifo_size; - regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!regs) - return -ENXIO; - - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - - host = kzalloc(sizeof(struct dw_mci), GFP_KERNEL); - if (!host) - return -ENOMEM; - - host->pdev = pdev; - host->pdata = pdata = pdev->dev.platform_data; - if (!pdata || !pdata->init) { - dev_err(&pdev->dev, + if (!host->pdata || !host->pdata->init) { + dev_err(&host->dev, "Platform data must supply init function\n"); - ret = -ENODEV; - goto err_freehost; + return -ENODEV; } - if (!pdata->select_slot && pdata->num_slots > 1) { - dev_err(&pdev->dev, + if (!host->pdata->select_slot && host->pdata->num_slots > 1) { + dev_err(&host->dev, "Platform data must supply select_slot function\n"); - ret = -ENODEV; - goto err_freehost; + return -ENODEV; } - if (!pdata->bus_hz) { - dev_err(&pdev->dev, + if (!host->pdata->bus_hz) { + dev_err(&host->dev, "Platform data must supply bus speed\n"); - ret = -ENODEV; - goto err_freehost; + return -ENODEV; } - host->bus_hz = pdata->bus_hz; - host->quirks = pdata->quirks; + host->bus_hz = host->pdata->bus_hz; + host->quirks = host->pdata->quirks; spin_lock_init(&host->lock); INIT_LIST_HEAD(&host->queue); - ret = -ENOMEM; - host->regs = ioremap(regs->start, resource_size(regs)); - if (!host->regs) - goto err_freehost; - host->dma_ops = pdata->dma_ops; + host->dma_ops = host->pdata->dma_ops; dw_mci_init_dma(host); /* @@ -1936,7 +1912,7 @@ static int dw_mci_probe(struct platform_device *pdev) } /* Reset all blocks */ - if (!mci_wait_reset(&pdev->dev, host)) { + if (!mci_wait_reset(&host->dev, host)) { ret = -ENODEV; goto err_dmaunmap; } @@ -1979,13 +1955,10 @@ static int dw_mci_probe(struct platform_device *pdev) if (!dw_mci_card_workqueue) goto err_dmaunmap; INIT_WORK(&host->card_work, dw_mci_work_routine_card); - - ret = request_irq(irq, dw_mci_interrupt, 0, "dw-mci", host); + ret = request_irq(host->irq, dw_mci_interrupt, host->irq_flags, "dw-mci", host); if (ret) goto err_workqueue; - platform_set_drvdata(pdev, host); - if (host->pdata->num_slots) host->num_slots = host->pdata->num_slots; else @@ -2005,7 +1978,7 @@ static int dw_mci_probe(struct platform_device *pdev) * Need to check the version-id and set data-offset for DATA register. */ host->verid = SDMMC_GET_VERID(mci_readl(host, VERID)); - dev_info(&pdev->dev, "Version ID is %04x\n", host->verid); + dev_info(&host->dev, "Version ID is %04x\n", host->verid); if (host->verid < DW_MMC_240A) host->data_offset = DATA_OFFSET; @@ -2022,12 +1995,12 @@ static int dw_mci_probe(struct platform_device *pdev) DW_MCI_ERROR_FLAGS | SDMMC_INT_CD); mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); /* Enable mci interrupt */ - dev_info(&pdev->dev, "DW MMC controller at irq %d, " + dev_info(&host->dev, "DW MMC controller at irq %d, " "%d bit host data width, " "%u deep fifo\n", - irq, width, fifo_size); + host->irq, width, fifo_size); if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) - dev_info(&pdev->dev, "Internal DMAC interrupt fix enabled.\n"); + dev_info(&host->dev, "Internal DMAC interrupt fix enabled.\n"); return 0; @@ -2038,7 +2011,7 @@ err_init_slot: dw_mci_cleanup_slot(host->slot[i], i); i--; } - free_irq(irq, host); + free_irq(host->irq, host); err_workqueue: destroy_workqueue(dw_mci_card_workqueue); @@ -2046,33 +2019,26 @@ err_workqueue: err_dmaunmap: if (host->use_dma && host->dma_ops->exit) host->dma_ops->exit(host); - dma_free_coherent(&host->pdev->dev, PAGE_SIZE, + dma_free_coherent(&host->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); - iounmap(host->regs); if (host->vmmc) { regulator_disable(host->vmmc); regulator_put(host->vmmc); } - - -err_freehost: - kfree(host); return ret; } +EXPORT_SYMBOL(dw_mci_probe); -static int __exit dw_mci_remove(struct platform_device *pdev) +void dw_mci_remove(struct dw_mci *host) { - struct dw_mci *host = platform_get_drvdata(pdev); int i; mci_writel(host, RINTSTS, 0xFFFFFFFF); mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */ - platform_set_drvdata(pdev, NULL); - for (i = 0; i < host->num_slots; i++) { - dev_dbg(&pdev->dev, "remove slot %d\n", i); + dev_dbg(&host->dev, "remove slot %d\n", i); if (host->slot[i]) dw_mci_cleanup_slot(host->slot[i], i); } @@ -2081,9 +2047,9 @@ static int __exit dw_mci_remove(struct platform_device *pdev) mci_writel(host, CLKENA, 0); mci_writel(host, CLKSRC, 0); - free_irq(platform_get_irq(pdev, 0), host); + free_irq(host->irq, host); destroy_workqueue(dw_mci_card_workqueue); - dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); + dma_free_coherent(&host->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); if (host->use_dma && host->dma_ops->exit) host->dma_ops->exit(host); @@ -2093,20 +2059,18 @@ static int __exit dw_mci_remove(struct platform_device *pdev) regulator_put(host->vmmc); } - iounmap(host->regs); - - kfree(host); - return 0; } +EXPORT_SYMBOL(dw_mci_remove); + + #ifdef CONFIG_PM_SLEEP /* * TODO: we should probably disable the clock to the card in the suspend path. */ -static int dw_mci_suspend(struct device *dev) +int dw_mci_suspend(struct dw_mci *host) { - int i, ret; - struct dw_mci *host = dev_get_drvdata(dev); + int i, ret = 0; for (i = 0; i < host->num_slots; i++) { struct dw_mci_slot *slot = host->slot[i]; @@ -2128,11 +2092,11 @@ static int dw_mci_suspend(struct device *dev) return 0; } +EXPORT_SYMBOL(dw_mci_suspend); -static int dw_mci_resume(struct device *dev) +int dw_mci_resume(struct dw_mci *host) { int i, ret; - struct dw_mci *host = dev_get_drvdata(dev); if (host->vmmc) regulator_enable(host->vmmc); @@ -2140,7 +2104,7 @@ static int dw_mci_resume(struct device *dev) if (host->dma_ops->init) host->dma_ops->init(host); - if (!mci_wait_reset(dev, host)) { + if (!mci_wait_reset(&host->dev, host)) { ret = -ENODEV; return ret; } @@ -2162,32 +2126,19 @@ static int dw_mci_resume(struct device *dev) if (ret < 0) return ret; } - return 0; } -#else -#define dw_mci_suspend NULL -#define dw_mci_resume NULL +EXPORT_SYMBOL(dw_mci_resume); #endif /* CONFIG_PM_SLEEP */ -static SIMPLE_DEV_PM_OPS(dw_mci_pmops, dw_mci_suspend, dw_mci_resume); - -static struct platform_driver dw_mci_driver = { - .remove = __exit_p(dw_mci_remove), - .driver = { - .name = "dw_mmc", - .pm = &dw_mci_pmops, - }, -}; - static int __init dw_mci_init(void) { - return platform_driver_probe(&dw_mci_driver, dw_mci_probe); + printk(KERN_INFO "Synopsys Designware Multimedia Card Interface Driver"); + return 0; } static void __exit dw_mci_exit(void) { - platform_driver_unregister(&dw_mci_driver); } module_init(dw_mci_init); diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index df392a1143f2..15c27e17c23f 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -175,4 +175,11 @@ (*(volatile u64 __force *)((dev)->regs + SDMMC_##reg) = (value)) #endif +extern int dw_mci_probe(struct dw_mci *host); +extern void dw_mci_remove(struct dw_mci *host); +#ifdef CONFIG_PM +extern int dw_mci_suspend(struct dw_mci *host); +extern int dw_mci_resume(struct dw_mci *host); +#endif + #endif /* _DW_MMC_H_ */ diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h index aae5d1f1bb39..8f66e28f5a0f 100644 --- a/include/linux/mmc/dw_mmc.h +++ b/include/linux/mmc/dw_mmc.h @@ -76,7 +76,7 @@ struct mmc_data; * @num_slots: Number of slots available. * @verid: Denote Version ID. * @data_offset: Set the offset of DATA register according to VERID. - * @pdev: Platform device associated with the MMC controller. + * @dev: Device associated with the MMC controller. * @pdata: Platform data associated with the MMC controller. * @slot: Slots sharing this MMC controller. * @fifo_depth: depth of FIFO. @@ -87,6 +87,8 @@ struct mmc_data; * @push_data: Pointer to FIFO push function. * @pull_data: Pointer to FIFO pull function. * @quirks: Set of quirks that apply to specific versions of the IP. + * @irq_flags: The flags to be passed to request_irq. + * @irq: The irq value to be passed to request_irq. * * Locking * ======= @@ -153,7 +155,7 @@ struct dw_mci { u32 fifoth_val; u16 verid; u16 data_offset; - struct platform_device *pdev; + struct device dev; struct dw_mci_board *pdata; struct dw_mci_slot *slot[MAX_MCI_SLOTS]; @@ -174,6 +176,8 @@ struct dw_mci { u32 quirks; struct regulator *vmmc; /* Power regulator */ + unsigned long irq_flags; /* IRQ flags */ + unsigned int irq; }; /* DMA ops for Internal/External DMAC interface */ -- cgit v1.2.3 From a39e5746e429b1dae1a71b3ddb05fd82916447c9 Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Sat, 4 Feb 2012 17:00:27 -0500 Subject: mmc: dw_mmc: Override blk_settings with platdata on IDMAC When use the IDMAC, we can also want to use the pdata->blk-setting. So if pdata->blk-setting is unset, use the default value. if not, use the pdata->blk-setting. Signed-off-by: Jaehoon Chung Signed-off-by: Kyungmin Park Acked-by: Will Newton Signed-off-by: Chris Ball --- drivers/mmc/host/dw_mmc.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 987a26549711..d1edf15b29fc 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1725,13 +1725,6 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id) else mmc->power_notify_type = MMC_HOST_PW_NOTIFY_NONE; -#ifdef CONFIG_MMC_DW_IDMAC - mmc->max_segs = host->ring_size; - mmc->max_blk_size = 65536; - mmc->max_blk_count = host->ring_size; - mmc->max_seg_size = 0x1000; - mmc->max_req_size = mmc->max_seg_size * mmc->max_blk_count; -#else if (host->pdata->blk_settings) { mmc->max_segs = host->pdata->blk_settings->max_segs; mmc->max_blk_size = host->pdata->blk_settings->max_blk_size; @@ -1740,13 +1733,20 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id) mmc->max_seg_size = host->pdata->blk_settings->max_seg_size; } else { /* Useful defaults if platform data is unset. */ +#ifdef CONFIG_MMC_DW_IDMAC + mmc->max_segs = host->ring_size; + mmc->max_blk_size = 65536; + mmc->max_blk_count = host->ring_size; + mmc->max_seg_size = 0x1000; + mmc->max_req_size = mmc->max_seg_size * mmc->max_blk_count; +#else mmc->max_segs = 64; mmc->max_blk_size = 65536; /* BLKSIZ is 16 bits */ mmc->max_blk_count = 512; mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; mmc->max_seg_size = mmc->max_req_size; - } #endif /* CONFIG_MMC_DW_IDMAC */ + } host->vmmc = regulator_get(mmc_dev(mmc), "vmmc"); if (IS_ERR(host->vmmc)) { -- cgit v1.2.3 From a1b13b4e18d62ea60aad9554f35d79c51328b8b6 Mon Sep 17 00:00:00 2001 From: Chris Ball Date: Mon, 6 Feb 2012 00:43:59 -0500 Subject: mmc: sdhci: Add platform suspend/resume hooks. Some platforms require saving/restoring registers across suspend/resume; this hook allows them to do that inside their driver. Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 6 ++++++ drivers/mmc/host/sdhci.h | 2 ++ 2 files changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 8d66706824a6..1f5888b42b2c 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2379,6 +2379,9 @@ int sdhci_suspend_host(struct sdhci_host *host) int ret; bool has_tuning_timer; + if (host->ops->platform_suspend) + host->ops->platform_suspend(host); + sdhci_disable_card_detection(host); /* Disable tuning since we are suspending */ @@ -2429,6 +2432,9 @@ int sdhci_resume_host(struct sdhci_host *host) ret = mmc_resume_host(host->mmc); sdhci_enable_card_detection(host); + if (host->ops->platform_resume) + host->ops->platform_resume(host); + /* Set the re-tuning expiration flag */ if ((host->version >= SDHCI_SPEC_300) && host->tuning_count && (host->tuning_mode == SDHCI_TUNING_MODE_1)) diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index ad265b96b75b..f761f23d2a28 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -275,6 +275,8 @@ struct sdhci_ops { void (*platform_reset_exit)(struct sdhci_host *host, u8 mask); int (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs); void (*hw_reset)(struct sdhci_host *host); + void (*platform_suspend)(struct sdhci_host *host); + void (*platform_resume)(struct sdhci_host *host); }; #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS -- cgit v1.2.3 From 192b5372421766f62fce20d2db2deb19a58e2cfc Mon Sep 17 00:00:00 2001 From: Jerry Huang Date: Sat, 4 Feb 2012 17:13:13 -0500 Subject: mmc: esdhc: Implement power management for ESDHC For FSL ESDHC controllers, when entering sleep the controller will power off, therefore the registers will lose their values, and the driver should save the value of registers during suspend and write them back during resume. Signed-off-by: Jerry Huang Signed-off-by: Jiang Yutang Acked-by: Anton Vorontsov Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-of-esdhc.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 5d876ff86f37..2ef52f47def8 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -114,6 +114,20 @@ static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host) return pltfm_host->clock / 256 / 16; } +#ifdef CONFIG_PM +static u32 esdhc_proctl; +static void esdhc_of_suspend(struct sdhci_host *host) +{ + esdhc_proctl = sdhci_be32bs_readl(host, SDHCI_HOST_CONTROL); +} + +static void esdhc_of_resume(struct sdhci_host *host) +{ + esdhc_of_enable_dma(host); + sdhci_be32bs_writel(host, esdhc_proctl, SDHCI_HOST_CONTROL); +} +#endif + static struct sdhci_ops sdhci_esdhc_ops = { .read_l = sdhci_be32bs_readl, .read_w = esdhc_readw, @@ -125,6 +139,10 @@ static struct sdhci_ops sdhci_esdhc_ops = { .enable_dma = esdhc_of_enable_dma, .get_max_clock = esdhc_of_get_max_clock, .get_min_clock = esdhc_of_get_min_clock, +#ifdef CONFIG_PM + .platform_suspend = esdhc_of_suspend, + .platform_resume = esdhc_of_resume, +#endif }; static struct sdhci_pltfm_data sdhci_esdhc_pdata = { -- cgit v1.2.3 From 3e44a1a7d22cdc44c98569fedbd993985bfd64d3 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 1 Feb 2012 16:30:55 -0700 Subject: mmc: sdhci-tegra: Explicitly support Tegra30 Tegra30 differs from Tegra20 in a number of ways. This patch implements a minimal set of differences in order to get the Cardhu board's SD slot and eMMC working. Given the diffs between the mainline sdhci-tegra.c and our downstream versions, I expect we'll eventually need to add many more differences, hence the seemingly heavy-weight addition of the soc_data structure. * Tegra30 doesn't need to override register access to SDHCI_HOST_VERSION or SDHCI_INT_ENABLE. * Tegra30 needs quirk SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK. Signed-off-by: Stephen Warren Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-tegra.c | 100 +++++++++++++++++++++++++++++++++++------ 1 file changed, 87 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index 78a36eba4df0..ccbca0ba8c39 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,19 @@ #include "sdhci-pltfm.h" +#define NVQUIRK_FORCE_SDHCI_SPEC_200 BIT(0) +#define NVQUIRK_ENABLE_BLOCK_GAP_DET BIT(1) + +struct sdhci_tegra_soc_data { + struct sdhci_pltfm_data *pdata; + u32 nvquirks; +}; + +struct sdhci_tegra { + const struct tegra_sdhci_platform_data *plat; + const struct sdhci_tegra_soc_data *soc_data; +}; + static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg) { u32 val; @@ -47,7 +61,12 @@ static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg) static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg) { - if (unlikely(reg == SDHCI_HOST_VERSION)) { + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_tegra *tegra_host = pltfm_host->priv; + const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; + + if (unlikely((soc_data->nvquirks & NVQUIRK_FORCE_SDHCI_SPEC_200) && + (reg == SDHCI_HOST_VERSION))) { /* Erratum: Version register is invalid in HW. */ return SDHCI_SPEC_200; } @@ -57,6 +76,10 @@ static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg) static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg) { + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_tegra *tegra_host = pltfm_host->priv; + const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; + /* Seems like we're getting spurious timeout and crc errors, so * disable signalling of them. In case of real errors software * timers should take care of eventually detecting them. @@ -66,7 +89,8 @@ static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg) writel(val, host->ioaddr + reg); - if (unlikely(reg == SDHCI_INT_ENABLE)) { + if (unlikely((soc_data->nvquirks & NVQUIRK_ENABLE_BLOCK_GAP_DET) && + (reg == SDHCI_INT_ENABLE))) { /* Erratum: Must enable block gap interrupt detection */ u8 gap_ctrl = readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL); if (val & SDHCI_INT_CARD_INT) @@ -77,10 +101,11 @@ static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg) } } -static unsigned int tegra_sdhci_get_ro(struct sdhci_host *sdhci) +static unsigned int tegra_sdhci_get_ro(struct sdhci_host *host) { - struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci); - struct tegra_sdhci_platform_data *plat = pltfm_host->priv; + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_tegra *tegra_host = pltfm_host->priv; + const struct tegra_sdhci_platform_data *plat = tegra_host->plat; if (!gpio_is_valid(plat->wp_gpio)) return -1; @@ -99,7 +124,8 @@ static irqreturn_t carddetect_irq(int irq, void *data) static int tegra_sdhci_8bit(struct sdhci_host *host, int bus_width) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); - struct tegra_sdhci_platform_data *plat = pltfm_host->priv; + struct sdhci_tegra *tegra_host = pltfm_host->priv; + const struct tegra_sdhci_platform_data *plat = tegra_host->plat; u32 ctrl; ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); @@ -125,16 +151,44 @@ static struct sdhci_ops tegra_sdhci_ops = { .platform_8bit_width = tegra_sdhci_8bit, }; -static struct sdhci_pltfm_data sdhci_tegra_pdata = { +#ifdef CONFIG_ARCH_TEGRA_2x_SOC +static struct sdhci_pltfm_data sdhci_tegra20_pdata = { + .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | + SDHCI_QUIRK_SINGLE_POWER_WRITE | + SDHCI_QUIRK_NO_HISPD_BIT | + SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC, + .ops = &tegra_sdhci_ops, +}; + +static struct sdhci_tegra_soc_data soc_data_tegra20 = { + .pdata = &sdhci_tegra20_pdata, + .nvquirks = NVQUIRK_FORCE_SDHCI_SPEC_200 | + NVQUIRK_ENABLE_BLOCK_GAP_DET, +}; +#endif + +#ifdef CONFIG_ARCH_TEGRA_3x_SOC +static struct sdhci_pltfm_data sdhci_tegra30_pdata = { .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | + SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | SDHCI_QUIRK_SINGLE_POWER_WRITE | SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC, .ops = &tegra_sdhci_ops, }; +static struct sdhci_tegra_soc_data soc_data_tegra30 = { + .pdata = &sdhci_tegra30_pdata, +}; +#endif + static const struct of_device_id sdhci_tegra_dt_match[] __devinitdata = { - { .compatible = "nvidia,tegra20-sdhci", }, +#ifdef CONFIG_ARCH_TEGRA_3x_SOC + { .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 }, +#endif +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + { .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 }, +#endif {} }; MODULE_DEVICE_TABLE(of, sdhci_dt_ids); @@ -165,13 +219,22 @@ static struct tegra_sdhci_platform_data * __devinit sdhci_tegra_dt_parse_pdata( static int __devinit sdhci_tegra_probe(struct platform_device *pdev) { + const struct of_device_id *match; + const struct sdhci_tegra_soc_data *soc_data; + struct sdhci_host *host; struct sdhci_pltfm_host *pltfm_host; struct tegra_sdhci_platform_data *plat; - struct sdhci_host *host; + struct sdhci_tegra *tegra_host; struct clk *clk; int rc; - host = sdhci_pltfm_init(pdev, &sdhci_tegra_pdata); + match = of_match_device(sdhci_tegra_dt_match, &pdev->dev); + if (match) + soc_data = match->data; + else + soc_data = &soc_data_tegra20; + + host = sdhci_pltfm_init(pdev, soc_data->pdata); if (IS_ERR(host)) return PTR_ERR(host); @@ -188,7 +251,17 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev) goto err_no_plat; } - pltfm_host->priv = plat; + tegra_host = devm_kzalloc(&pdev->dev, sizeof(*tegra_host), GFP_KERNEL); + if (!tegra_host) { + dev_err(mmc_dev(host->mmc), "failed to allocate tegra_host\n"); + rc = -ENOMEM; + goto err_no_plat; + } + + tegra_host->plat = plat; + tegra_host->soc_data = soc_data; + + pltfm_host->priv = tegra_host; if (gpio_is_valid(plat->power_gpio)) { rc = gpio_request(plat->power_gpio, "sdhci_power"); @@ -284,7 +357,8 @@ static int __devexit sdhci_tegra_remove(struct platform_device *pdev) { struct sdhci_host *host = platform_get_drvdata(pdev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); - struct tegra_sdhci_platform_data *plat = pltfm_host->priv; + struct sdhci_tegra *tegra_host = pltfm_host->priv; + const struct tegra_sdhci_platform_data *plat = tegra_host->plat; int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff); sdhci_remove_host(host, dead); @@ -327,5 +401,5 @@ static struct platform_driver sdhci_tegra_driver = { module_platform_driver(sdhci_tegra_driver); MODULE_DESCRIPTION("SDHCI driver for Tegra"); -MODULE_AUTHOR(" Google, Inc."); +MODULE_AUTHOR("Google, Inc."); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 9aa514089ed4f3a943ab9445e23e03a7be3044e6 Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Mon, 6 Feb 2012 16:55:07 +0900 Subject: mmc: dw_mmc: Add support for pre_req and post_req This patch implements pre_req and post_req in dw_mmc to support asynchronous mmc request. Signed-off-by: Seungwon Jeon Acked-by: Jaehoon Chung Acked-by: Will Newton Signed-off-by: Chris Ball --- drivers/mmc/host/dw_mmc.c | 129 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 102 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index d1edf15b29fc..7d6ad6cd4a50 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -296,14 +296,24 @@ static void dw_mci_stop_dma(struct dw_mci *host) } #ifdef CONFIG_MMC_DW_IDMAC +static int dw_mci_get_dma_dir(struct mmc_data *data) +{ + if (data->flags & MMC_DATA_WRITE) + return DMA_TO_DEVICE; + else + return DMA_FROM_DEVICE; +} + static void dw_mci_dma_cleanup(struct dw_mci *host) { struct mmc_data *data = host->data; if (data) - dma_unmap_sg(&host->dev, data->sg, data->sg_len, - ((data->flags & MMC_DATA_WRITE) - ? DMA_TO_DEVICE : DMA_FROM_DEVICE)); + if (!data->host_cookie) + dma_unmap_sg(&host->dev, + data->sg, + data->sg_len, + dw_mci_get_dma_dir(data)); } static void dw_mci_idmac_stop_dma(struct dw_mci *host) @@ -419,26 +429,15 @@ static int dw_mci_idmac_init(struct dw_mci *host) return 0; } -static struct dw_mci_dma_ops dw_mci_idmac_ops = { - .init = dw_mci_idmac_init, - .start = dw_mci_idmac_start_dma, - .stop = dw_mci_idmac_stop_dma, - .complete = dw_mci_idmac_complete_dma, - .cleanup = dw_mci_dma_cleanup, -}; -#endif /* CONFIG_MMC_DW_IDMAC */ - -static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) +static int dw_mci_pre_dma_transfer(struct dw_mci *host, + struct mmc_data *data, + bool next) { struct scatterlist *sg; - unsigned int i, direction, sg_len; - u32 temp; - - host->using_dma = 0; + unsigned int i, sg_len; - /* If we don't have a channel, we can't do DMA */ - if (!host->use_dma) - return -ENODEV; + if (!next && data->host_cookie) + return data->host_cookie; /* * We don't do DMA on "complex" transfers, i.e. with @@ -447,6 +446,7 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) */ if (data->blocks * data->blksz < DW_MCI_DMA_THRESHOLD) return -EINVAL; + if (data->blksz & 3) return -EINVAL; @@ -455,15 +455,88 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) return -EINVAL; } - host->using_dma = 1; + sg_len = dma_map_sg(&host->dev, + data->sg, + data->sg_len, + dw_mci_get_dma_dir(data)); + if (sg_len == 0) + return -EINVAL; - if (data->flags & MMC_DATA_READ) - direction = DMA_FROM_DEVICE; - else - direction = DMA_TO_DEVICE; + if (next) + data->host_cookie = sg_len; + + return sg_len; +} + +static struct dw_mci_dma_ops dw_mci_idmac_ops = { + .init = dw_mci_idmac_init, + .start = dw_mci_idmac_start_dma, + .stop = dw_mci_idmac_stop_dma, + .complete = dw_mci_idmac_complete_dma, + .cleanup = dw_mci_dma_cleanup, +}; +#else +static int dw_mci_pre_dma_transfer(struct dw_mci *host, + struct mmc_data *data, + bool next) +{ + return -ENOSYS; +} +#endif /* CONFIG_MMC_DW_IDMAC */ + +static void dw_mci_pre_req(struct mmc_host *mmc, + struct mmc_request *mrq, + bool is_first_req) +{ + struct dw_mci_slot *slot = mmc_priv(mmc); + struct mmc_data *data = mrq->data; + + if (!slot->host->use_dma || !data) + return; + + if (data->host_cookie) { + data->host_cookie = 0; + return; + } + + if (dw_mci_pre_dma_transfer(slot->host, mrq->data, 1) < 0) + data->host_cookie = 0; +} + +static void dw_mci_post_req(struct mmc_host *mmc, + struct mmc_request *mrq, + int err) +{ + struct dw_mci_slot *slot = mmc_priv(mmc); + struct mmc_data *data = mrq->data; + + if (!slot->host->use_dma || !data) + return; + + if (data->host_cookie) + dma_unmap_sg(&slot->host->dev, + data->sg, + data->sg_len, + dw_mci_get_dma_dir(data)); + data->host_cookie = 0; +} + +static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) +{ + int sg_len; + u32 temp; - sg_len = dma_map_sg(&host->dev, data->sg, data->sg_len, - direction); + host->using_dma = 0; + + /* If we don't have a channel, we can't do DMA */ + if (!host->use_dma) + return -ENODEV; + + sg_len = dw_mci_pre_dma_transfer(host, data, 0); + if (sg_len < 0) + return sg_len; + + host->using_dma = 1; dev_vdbg(&host->dev, "sd sg_cpu: %#lx sg_dma: %#lx sg_len: %d\n", @@ -800,6 +873,8 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) static const struct mmc_host_ops dw_mci_ops = { .request = dw_mci_request, + .pre_req = dw_mci_pre_req, + .post_req = dw_mci_post_req, .set_ios = dw_mci_set_ios, .get_ro = dw_mci_get_ro, .get_cd = dw_mci_get_cd, -- cgit v1.2.3 From 7b2fd4f23f72c13a78c0892d330dde305ef2fb80 Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Tue, 7 Feb 2012 14:13:10 +0900 Subject: mmc: core: add a debug message for SET_BLOCK_COUNT This patch is added just debug message. Almost features need to use the CMD23. But we didn't see the debug message for sbc. If sbc's message can see, should be help for debugging. (We can check whether use the cmd23 or not.) Signed-off-by: Jaehoon Chung Signed-off-by: Kyungmin Park Signed-off-by: Chris Ball --- drivers/mmc/core/core.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 132378b89d76..faa0af10d334 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -188,6 +188,12 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) struct scatterlist *sg; #endif + if (mrq->sbc) { + pr_debug("<%s: starting CMD%u arg %08x flags %08x>\n", + mmc_hostname(host), mrq->sbc->opcode, + mrq->sbc->arg, mrq->sbc->flags); + } + pr_debug("%s: starting CMD%u arg %08x flags %08x\n", mmc_hostname(host), mrq->cmd->opcode, mrq->cmd->arg, mrq->cmd->flags); -- cgit v1.2.3 From f060bc9c12d28c9a561fdd8cf1d60bdefd92db2d Mon Sep 17 00:00:00 2001 From: Jerry Huang Date: Tue, 14 Feb 2012 14:05:37 +0800 Subject: mmc: esdhc: Workaround for data crc error on p1010rdb SD card read was failing (data crc error) on some cards at maximum possible frequency on P1010 (CCB frequency set to 400MHz). Some clock deviations are also observed at this frequency. Hence reduced the mmc clock freq. Signed-off-by: Priyanka Jain Singed-off-by: Jerry Huang Acked-by: Anton Vorontsov Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-of-esdhc.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 2ef52f47def8..f8eb1fb0c921 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -1,7 +1,7 @@ /* * Freescale eSDHC controller driver. * - * Copyright (c) 2007, 2010 Freescale Semiconductor, Inc. + * Copyright (c) 2007, 2010, 2012 Freescale Semiconductor, Inc. * Copyright (c) 2009 MontaVista Software, Inc. * * Authors: Xiaobo Xie @@ -14,6 +14,7 @@ */ #include +#include #include #include #include @@ -114,6 +115,20 @@ static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host) return pltfm_host->clock / 256 / 16; } +static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) +{ + /* Workaround to reduce the clock frequency for p1010 esdhc */ + if (of_find_compatible_node(NULL, NULL, "fsl,p1010-esdhc")) { + if (clock > 20000000) + clock -= 5000000; + if (clock > 40000000) + clock -= 5000000; + } + + /* Set the clock */ + esdhc_set_clock(host, clock); +} + #ifdef CONFIG_PM static u32 esdhc_proctl; static void esdhc_of_suspend(struct sdhci_host *host) @@ -135,7 +150,7 @@ static struct sdhci_ops sdhci_esdhc_ops = { .write_l = sdhci_be32bs_writel, .write_w = esdhc_writew, .write_b = esdhc_writeb, - .set_clock = esdhc_set_clock, + .set_clock = esdhc_of_set_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 e3891dc5c5b10dfa07ec02e3d1f6739c5e26d4a8 Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Tue, 14 Feb 2012 17:33:03 +0900 Subject: mmc: dw_mmc: shift with slot-id for CLKENA register In CLKENA register, can support 16-SD card clocks. If support correctly, must shift with slot-id. Signed-off-by: Jaehoon Chung Signed-off-by: Kyungmin Park Acked-by: Will Newton Signed-off-by: Chris Ball --- drivers/mmc/host/dw_mmc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 7d6ad6cd4a50..f9457e5ed9b7 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -652,8 +652,8 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot) SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); /* enable clock */ - mci_writel(host, CLKENA, SDMMC_CLKEN_ENABLE | - SDMMC_CLKEN_LOW_PWR); + mci_writel(host, CLKENA, ((SDMMC_CLKEN_ENABLE | + SDMMC_CLKEN_LOW_PWR) << slot->id)); /* inform CIU */ mci_send_cmd(slot, -- cgit v1.2.3 From b81cfc413047181a8f2599cacf927dc09a81a123 Mon Sep 17 00:00:00 2001 From: Ludovic Desroches Date: Thu, 9 Feb 2012 16:33:54 +0100 Subject: mmc: atmel-mci: fix typo Signed-off-by: Ludovic Desroches Signed-off-by: Nicolas Ferre Signed-off-by: Chris Ball --- drivers/mmc/host/atmel-mci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index e4449a54ae8f..390863e7efbd 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -1975,7 +1975,7 @@ static bool atmci_configure_dma(struct atmel_mci *host) return false; } else { dev_info(&host->pdev->dev, - "Using %s for DMA transfers\n", + "using %s for DMA transfers\n", dma_chan_name(host->dma.chan)); return true; } -- cgit v1.2.3 From d418ed87c4e3ecf45d42718c4ead91180997681f Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Sun, 19 Feb 2012 13:20:33 +0100 Subject: ARM: OMAP: hsmmc: add max_freq field External circuitry like level shifters may limit the maximum operation speed of the hsmmc controller. Add a field to struct omap2_hsmmc_info so boards can adjust the setting on demand. Signed-off-by: Daniel Mack Acked-by: Tony Lindgren Signed-off-by: Chris Ball --- arch/arm/mach-omap2/hsmmc.c | 1 + arch/arm/mach-omap2/hsmmc.h | 2 ++ drivers/mmc/host/omap_hsmmc.c | 8 ++++++-- 3 files changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/arch/arm/mach-omap2/hsmmc.c b/arch/arm/mach-omap2/hsmmc.c index 19dd1657245c..9abab0f52f79 100644 --- a/arch/arm/mach-omap2/hsmmc.c +++ b/arch/arm/mach-omap2/hsmmc.c @@ -316,6 +316,7 @@ static int omap_hsmmc_pdata_init(struct omap2_hsmmc_info *c, mmc->slots[0].pm_caps = c->pm_caps; mmc->slots[0].internal_clock = !c->ext_clock; mmc->dma_mask = 0xffffffff; + mmc->max_freq = c->max_freq; if (cpu_is_omap44xx()) mmc->reg_offset = OMAP4_MMC_REG_OFFSET; else diff --git a/arch/arm/mach-omap2/hsmmc.h b/arch/arm/mach-omap2/hsmmc.h index c4409730c4bb..0017ae24995e 100644 --- a/arch/arm/mach-omap2/hsmmc.h +++ b/arch/arm/mach-omap2/hsmmc.h @@ -26,6 +26,8 @@ struct omap2_hsmmc_info { char *name; /* or NULL for default */ struct device *dev; /* returned: pointer to mmc adapter */ int ocr_mask; /* temporary HACK */ + int max_freq; /* maximum clock, if constrained by external + * circuitry, or 0 for default */ /* Remux (pad configuration) when powering on/off */ void (*remux)(struct device *dev, int slot, int power_on); /* init some special card */ diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index fd0c661bbad3..36e7f5bc58a3 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -1912,8 +1912,12 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) if (mmc_slot(host).vcc_aux_disable_is_sleep) mmc_slot(host).no_off = 1; - mmc->f_min = OMAP_MMC_MIN_CLOCK; - mmc->f_max = OMAP_MMC_MAX_CLOCK; + mmc->f_min = OMAP_MMC_MIN_CLOCK; + + if (pdata->max_freq > 0) + mmc->f_max = pdata->max_freq; + else + mmc->f_max = OMAP_MMC_MAX_CLOCK; spin_lock_init(&host->irq_lock); -- cgit v1.2.3 From 9beee912a3c6e017f03ccefd826b57a6a46c0e31 Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Thu, 16 Feb 2012 11:19:38 +0900 Subject: mmc: dw_mmc: fix compile error when CONFIG_MMC_DW_IDMAC is disabled When disable CONFIG_MMC_DW_IDMAC, can see the compiler error. Because in dw_mci_post_req(), called the dw_mci_get_dma_dir(). But that function is in #ifdef CONFIG_MMC_DW_IDMAC. I think that function is generic function. Not need the CONFIG_MMC_DW_IDMAC. Signed-off-by: Jaehoon Chung Signed-off-by: Kyungmin Park Acked-by: Seungwon Jeon Acked-by: James Hogan Signed-off-by: Chris Ball --- drivers/mmc/host/dw_mmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index f9457e5ed9b7..692c3ba30335 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -295,7 +295,6 @@ static void dw_mci_stop_dma(struct dw_mci *host) } } -#ifdef CONFIG_MMC_DW_IDMAC static int dw_mci_get_dma_dir(struct mmc_data *data) { if (data->flags & MMC_DATA_WRITE) @@ -304,6 +303,7 @@ static int dw_mci_get_dma_dir(struct mmc_data *data) return DMA_FROM_DEVICE; } +#ifdef CONFIG_MMC_DW_IDMAC static void dw_mci_dma_cleanup(struct dw_mci *host) { struct mmc_data *data = host->data; -- cgit v1.2.3 From 885c3e800cf99db3391247776bfa2d262b21a72b Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Mon, 20 Feb 2012 11:01:43 +0900 Subject: mmc: dw_mmc: Regression fix for non-IDMAC DMA 3ec7699d3bb1b0ee7 ("mmc: dw_mmc: Add support for pre_req and post_req") broke non-IDMAC DMA, because dw_mci_pre_dma_transfer() is valid only if using internal DMA. In case of using other DMA it returns -ENOSYS. It prevents the DMA operations. This patch makes dw_mci_pre_dma_transfer() effective in all DMA cases again. Reported-by: James Hogan Signed-off-by: Seungwon Jeon Reviewed-by: Namjae Jeon Acked-by: James Hogan Signed-off-by: Chris Ball --- drivers/mmc/host/dw_mmc.c | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 692c3ba30335..bf3c9b456aaf 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -429,6 +429,15 @@ static int dw_mci_idmac_init(struct dw_mci *host) return 0; } +static struct dw_mci_dma_ops dw_mci_idmac_ops = { + .init = dw_mci_idmac_init, + .start = dw_mci_idmac_start_dma, + .stop = dw_mci_idmac_stop_dma, + .complete = dw_mci_idmac_complete_dma, + .cleanup = dw_mci_dma_cleanup, +}; +#endif /* CONFIG_MMC_DW_IDMAC */ + static int dw_mci_pre_dma_transfer(struct dw_mci *host, struct mmc_data *data, bool next) @@ -468,22 +477,6 @@ static int dw_mci_pre_dma_transfer(struct dw_mci *host, return sg_len; } -static struct dw_mci_dma_ops dw_mci_idmac_ops = { - .init = dw_mci_idmac_init, - .start = dw_mci_idmac_start_dma, - .stop = dw_mci_idmac_stop_dma, - .complete = dw_mci_idmac_complete_dma, - .cleanup = dw_mci_dma_cleanup, -}; -#else -static int dw_mci_pre_dma_transfer(struct dw_mci *host, - struct mmc_data *data, - bool next) -{ - return -ENOSYS; -} -#endif /* CONFIG_MMC_DW_IDMAC */ - static void dw_mci_pre_req(struct mmc_host *mmc, struct mmc_request *mrq, bool is_first_req) -- cgit v1.2.3 From f0cc9cf99326926fd76f77645c48d16d647802eb Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 6 Feb 2012 10:42:39 +0100 Subject: mmc: core: Detect card removal on I/O error To prevent I/O as soon as possible at card removal, a new detect work is re-scheduled without a delay to let a rescan remove the card device as soon as possible. Additionally, MMC_CAP2_DETECT_ON_ERR can now be used to handle "slowly" removed cards that a scheduled detect work did not detect as removed. To prevent further I/O requests for these lingering removed cards, check if card has been removed and then schedule a detect work to properly remove it. Signed-off-by: Ulf Hansson Reviewed-by: Namjae Jeon Acked-by: Linus Walleij Signed-off-by: Chris Ball --- drivers/mmc/core/core.c | 24 +++++++++++++++++++++--- include/linux/mmc/host.h | 1 + 2 files changed, 22 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index faa0af10d334..436409f7f7bd 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2121,18 +2121,36 @@ int _mmc_detect_card_removed(struct mmc_host *host) int mmc_detect_card_removed(struct mmc_host *host) { struct mmc_card *card = host->card; + int ret; WARN_ON(!host->claimed); + + if (!card) + return 1; + + ret = mmc_card_removed(card); /* * The card will be considered unchanged unless we have been asked to * detect a change or host requires polling to provide card detection. */ - if (card && !host->detect_change && !(host->caps & MMC_CAP_NEEDS_POLL)) - return mmc_card_removed(card); + if (!host->detect_change && !(host->caps & MMC_CAP_NEEDS_POLL) && + !(host->caps2 & MMC_CAP2_DETECT_ON_ERR)) + return ret; host->detect_change = 0; + if (!ret) { + ret = _mmc_detect_card_removed(host); + if (ret && (host->caps2 & MMC_CAP2_DETECT_ON_ERR)) { + /* + * Schedule a detect work as soon as possible to let a + * rescan handle the card removal. + */ + cancel_delayed_work(&host->detect); + mmc_detect_change(host, 0); + } + } - return _mmc_detect_card_removed(host); + return ret; } EXPORT_SYMBOL(mmc_detect_card_removed); diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index ee2b0363c040..d1d3743fde90 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -258,6 +258,7 @@ struct mmc_host { #define MMC_CAP2_HS200 (MMC_CAP2_HS200_1_8V_SDR | \ MMC_CAP2_HS200_1_2V_SDR) #define MMC_CAP2_BROKEN_VOLTAGE (1 << 7) /* Use the broken voltage */ +#define MMC_CAP2_DETECT_ON_ERR (1 << 8) /* On I/O err check card removal */ mmc_pm_flag_t pm_caps; /* supported pm features */ unsigned int power_notify_type; -- cgit v1.2.3 From b7bf773b0eaac633276a8c3b2867db4d8b64c04a Mon Sep 17 00:00:00 2001 From: Balaji T K Date: Wed, 7 Mar 2012 09:55:30 -0500 Subject: mmc: omap_hsmmc: use platform_get_resource_byname for tx/rx DMA channels Git rid of hardcoded tx/rx DMA channels based on pdev->id and use platform_get_resource_byname() to retrieve them instead. Signed-off-by: Balaji T K Signed-off-by: Rajendra Nayak Tested-by: Venkatraman S Signed-off-by: Chris Ball --- drivers/mmc/host/omap_hsmmc.c | 35 +++++++++++------------------------ 1 file changed, 11 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 36e7f5bc58a3..a6f19f17f0d8 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -1981,32 +1981,19 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) omap_hsmmc_conf_bus_power(host); - /* Select DMA lines */ - switch (host->id) { - case OMAP_MMC1_DEVID: - host->dma_line_tx = OMAP24XX_DMA_MMC1_TX; - host->dma_line_rx = OMAP24XX_DMA_MMC1_RX; - break; - case OMAP_MMC2_DEVID: - host->dma_line_tx = OMAP24XX_DMA_MMC2_TX; - host->dma_line_rx = OMAP24XX_DMA_MMC2_RX; - break; - case OMAP_MMC3_DEVID: - host->dma_line_tx = OMAP34XX_DMA_MMC3_TX; - host->dma_line_rx = OMAP34XX_DMA_MMC3_RX; - break; - case OMAP_MMC4_DEVID: - host->dma_line_tx = OMAP44XX_DMA_MMC4_TX; - host->dma_line_rx = OMAP44XX_DMA_MMC4_RX; - break; - case OMAP_MMC5_DEVID: - host->dma_line_tx = OMAP44XX_DMA_MMC5_TX; - host->dma_line_rx = OMAP44XX_DMA_MMC5_RX; - break; - default: - dev_err(mmc_dev(host->mmc), "Invalid MMC id\n"); + res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx"); + if (!res) { + dev_err(mmc_dev(host->mmc), "cannot get DMA TX channel\n"); + goto err_irq; + } + host->dma_line_tx = res->start; + + res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx"); + if (!res) { + dev_err(mmc_dev(host->mmc), "cannot get DMA RX channel\n"); goto err_irq; } + host->dma_line_rx = res->start; /* Request IRQ for MMC operations */ ret = request_irq(host->irq, omap_hsmmc_irq, 0, -- cgit v1.2.3 From 80d17f3a1bd8b5045fedb7dee6b41208497c72d0 Mon Sep 17 00:00:00 2001 From: Balaji T K Date: Wed, 7 Mar 2012 09:55:30 -0500 Subject: mmc: omap_hsmmc: remove unused .set_sleep function set_sleep seems to be unused in omap_hsmmc driver. so get rid of it. Signed-off-by: Balaji T K Signed-off-by: Rajendra Nayak Tested-by: Venkatraman S Signed-off-by: Chris Ball --- arch/arm/plat-omap/include/plat/mmc.h | 2 -- drivers/mmc/host/omap_hsmmc.c | 60 ----------------------------------- 2 files changed, 62 deletions(-) (limited to 'drivers') diff --git a/arch/arm/plat-omap/include/plat/mmc.h b/arch/arm/plat-omap/include/plat/mmc.h index f75946c3293d..7a38750c0079 100644 --- a/arch/arm/plat-omap/include/plat/mmc.h +++ b/arch/arm/plat-omap/include/plat/mmc.h @@ -137,8 +137,6 @@ struct omap_mmc_platform_data { int (*set_power)(struct device *dev, int slot, int power_on, int vdd); int (*get_ro)(struct device *dev, int slot); - int (*set_sleep)(struct device *dev, int slot, int sleep, - int vdd, int cardsleep); void (*remux)(struct device *dev, int slot, int power_on); /* Call back before enabling / disabling regulators */ void (*before_set_reg)(struct device *dev, int slot, diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index a6f19f17f0d8..3f77fd94e5d8 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -324,61 +324,6 @@ static int omap_hsmmc_4_set_power(struct device *dev, int slot, int power_on, return 0; } -static int omap_hsmmc_1_set_sleep(struct device *dev, int slot, int sleep, - int vdd, int cardsleep) -{ - struct omap_hsmmc_host *host = - platform_get_drvdata(to_platform_device(dev)); - int mode = sleep ? REGULATOR_MODE_STANDBY : REGULATOR_MODE_NORMAL; - - return regulator_set_mode(host->vcc, mode); -} - -static int omap_hsmmc_235_set_sleep(struct device *dev, int slot, int sleep, - int vdd, int cardsleep) -{ - struct omap_hsmmc_host *host = - platform_get_drvdata(to_platform_device(dev)); - int err, mode; - - /* - * If we don't see a Vcc regulator, assume it's a fixed - * voltage always-on regulator. - */ - if (!host->vcc) - return 0; - - mode = sleep ? REGULATOR_MODE_STANDBY : REGULATOR_MODE_NORMAL; - - if (!host->vcc_aux) - return regulator_set_mode(host->vcc, mode); - - if (cardsleep) { - /* VCC can be turned off if card is asleep */ - if (sleep) - err = mmc_regulator_set_ocr(host->mmc, host->vcc, 0); - else - err = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd); - } else - err = regulator_set_mode(host->vcc, mode); - if (err) - return err; - - if (!mmc_slot(host).vcc_aux_disable_is_sleep) - return regulator_set_mode(host->vcc_aux, mode); - - if (sleep) - return regulator_disable(host->vcc_aux); - else - return regulator_enable(host->vcc_aux); -} - -static int omap_hsmmc_4_set_sleep(struct device *dev, int slot, int sleep, - int vdd, int cardsleep) -{ - return 0; -} - static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) { struct regulator *reg; @@ -389,18 +334,15 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) case OMAP_MMC1_DEVID: /* On-chip level shifting via PBIAS0/PBIAS1 */ mmc_slot(host).set_power = omap_hsmmc_1_set_power; - mmc_slot(host).set_sleep = omap_hsmmc_1_set_sleep; break; case OMAP_MMC2_DEVID: case OMAP_MMC3_DEVID: case OMAP_MMC5_DEVID: /* Off-chip level shifting, or none */ mmc_slot(host).set_power = omap_hsmmc_235_set_power; - mmc_slot(host).set_sleep = omap_hsmmc_235_set_sleep; break; case OMAP_MMC4_DEVID: mmc_slot(host).set_power = omap_hsmmc_4_set_power; - mmc_slot(host).set_sleep = omap_hsmmc_4_set_sleep; default: pr_err("MMC%d configuration not supported!\n", host->id); return -EINVAL; @@ -462,7 +404,6 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) err: mmc_slot(host).set_power = NULL; - mmc_slot(host).set_sleep = NULL; return ret; } @@ -471,7 +412,6 @@ static void omap_hsmmc_reg_put(struct omap_hsmmc_host *host) regulator_put(host->vcc); regulator_put(host->vcc_aux); mmc_slot(host).set_power = NULL; - mmc_slot(host).set_sleep = NULL; } static inline int omap_hsmmc_have_reg(void) -- cgit v1.2.3 From c2200efb5d7ca7922e5b9e430598473941d5eb0b Mon Sep 17 00:00:00 2001 From: Balaji T K Date: Wed, 7 Mar 2012 09:55:30 -0500 Subject: mmc: omap_hsmmc: Use OMAP_HSMMC_SUPPORTS_DUAL_VOLT flag to remove host->id based hardcoding Use OMAP_HSMMC_SUPPORTS_DUAL_VOLT flag instead of host->id for identifying SD bus voltage capabilities. Signed-off-by: Balaji T K Signed-off-by: Rajendra Nayak Tested-by: Venkatraman S Signed-off-by: Chris Ball --- drivers/mmc/host/omap_hsmmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 3f77fd94e5d8..cfb4b48c212b 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -650,7 +650,7 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host) OMAP_HSMMC_WRITE(host->base, SYSCONFIG, OMAP_HSMMC_READ(host->base, SYSCONFIG) | AUTOIDLE); - if (host->id == OMAP_MMC1_DEVID) { + if (host->pdata->controller_flags & OMAP_HSMMC_SUPPORTS_DUAL_VOLT) { if (host->power_mode != MMC_POWER_OFF && (1 << ios->vdd) <= MMC_VDD_23_24) hctl = SDVS18; -- cgit v1.2.3 From 69b07ecee936c49f8b7a2099b4276fec7dff6827 Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Wed, 7 Mar 2012 09:55:30 -0500 Subject: mmc: omap_hsmmc: Get rid of omap_hsmmc_1_set_power function Use omap_hsmmc_235_set_poweri() (now renamed as omap_hsmmc_set_power()) for MMC1 instance as well and get rid of omap_hsmmc_1_set_power() completely. omap_hsmmc_235_set_power() seems to be implemented as a superset of omap_hsmmc_1_set_power() with additional functionality implemented based on additional checks and hence should just work for MMC1 as well. Signed-off-by: Rajendra Nayak Tested-by: Venkatraman S Tested-by: Balaji T K Signed-off-by: Chris Ball --- drivers/mmc/host/omap_hsmmc.c | 30 +++--------------------------- 1 file changed, 3 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index cfb4b48c212b..11e8c8fe46dc 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -241,28 +241,7 @@ static int omap_hsmmc_resume_cdirq(struct device *dev, int slot) #ifdef CONFIG_REGULATOR -static int omap_hsmmc_1_set_power(struct device *dev, int slot, int power_on, - int vdd) -{ - struct omap_hsmmc_host *host = - platform_get_drvdata(to_platform_device(dev)); - int ret; - - if (mmc_slot(host).before_set_reg) - mmc_slot(host).before_set_reg(dev, slot, power_on, vdd); - - if (power_on) - ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd); - else - 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); - - return ret; -} - -static int omap_hsmmc_235_set_power(struct device *dev, int slot, int power_on, +static int omap_hsmmc_set_power(struct device *dev, int slot, int power_on, int vdd) { struct omap_hsmmc_host *host = @@ -332,14 +311,11 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) switch (host->id) { case OMAP_MMC1_DEVID: - /* On-chip level shifting via PBIAS0/PBIAS1 */ - mmc_slot(host).set_power = omap_hsmmc_1_set_power; - break; case OMAP_MMC2_DEVID: case OMAP_MMC3_DEVID: case OMAP_MMC5_DEVID: - /* Off-chip level shifting, or none */ - mmc_slot(host).set_power = omap_hsmmc_235_set_power; + /* On-chip level shifting via PBIAS0/PBIAS1 */ + mmc_slot(host).set_power = omap_hsmmc_set_power; break; case OMAP_MMC4_DEVID: mmc_slot(host).set_power = omap_hsmmc_4_set_power; -- cgit v1.2.3 From 1cb9af49a1ba2d7fc1c7236df5513449dda30a12 Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Wed, 7 Mar 2012 09:55:31 -0500 Subject: mmc: omap_hsmmc: Get rid of omap_hsmmc_4_set_power function Now that omap_hsmmc_set_power() already has a check to return 0 if !host->vcc, it seems like it can be used even on MMC4 instead of the dummy omap_hsmmc_4_set_power(). This also helps get rid of all the host->id based check to populate the right function for on-chip/external level shifting and use omap_hsmmc_set_power() for all MMC modules. Signed-off-by: Rajendra Nayak Tested-by: Venkatraman S Tested-by: Balaji T K Signed-off-by: Chris Ball --- drivers/mmc/host/omap_hsmmc.c | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 11e8c8fe46dc..e9215c264988 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -297,32 +297,13 @@ static int omap_hsmmc_set_power(struct device *dev, int slot, int power_on, return ret; } -static int omap_hsmmc_4_set_power(struct device *dev, int slot, int power_on, - int vdd) -{ - return 0; -} - static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) { struct regulator *reg; int ret = 0; int ocr_value = 0; - switch (host->id) { - case OMAP_MMC1_DEVID: - case OMAP_MMC2_DEVID: - case OMAP_MMC3_DEVID: - case OMAP_MMC5_DEVID: - /* On-chip level shifting via PBIAS0/PBIAS1 */ - mmc_slot(host).set_power = omap_hsmmc_set_power; - break; - case OMAP_MMC4_DEVID: - mmc_slot(host).set_power = omap_hsmmc_4_set_power; - default: - pr_err("MMC%d configuration not supported!\n", host->id); - return -EINVAL; - } + mmc_slot(host).set_power = omap_hsmmc_set_power; reg = regulator_get(host->dev, "vmmc"); if (IS_ERR(reg)) { -- cgit v1.2.3 From e3f1adb63cfa6f6ef6b4e63a546e13210c5f3cb2 Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Wed, 7 Mar 2012 09:55:31 -0500 Subject: mmc: omap_hsmmc: Don't expect MMC1 to always have vmmc supply MMC1 is not the only instance that can be used/wired for SD. So remove this assumption from the driver. Now that all the mmc id based usage is removed, get rid of all the DEVID defines and also the 'id' field from the omap_hsmmc_host structure. Signed-off-by: Rajendra Nayak Tested-by: Venkatraman S Tested-by: Balaji T K Signed-off-by: Chris Ball --- drivers/mmc/host/omap_hsmmc.c | 31 ++----------------------------- 1 file changed, 2 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index e9215c264988..e5501704b2db 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -106,17 +106,6 @@ #define SOFTRESET (1 << 1) #define RESETDONE (1 << 0) -/* - * FIXME: Most likely all the data using these _DEVID defines should come - * from the platform_data, or implemented in controller and slot specific - * functions. - */ -#define OMAP_MMC1_DEVID 0 -#define OMAP_MMC2_DEVID 1 -#define OMAP_MMC3_DEVID 2 -#define OMAP_MMC4_DEVID 3 -#define OMAP_MMC5_DEVID 4 - #define MMC_AUTOSUSPEND_DELAY 100 #define MMC_TIMEOUT_MS 20 #define OMAP_MMC_MIN_CLOCK 400000 @@ -164,7 +153,6 @@ struct omap_hsmmc_host { void __iomem *base; resource_size_t mapbase; spinlock_t irq_lock; /* Prevent races with irq handler */ - unsigned int id; unsigned int dma_len; unsigned int dma_sg_idx; unsigned char bus_mode; @@ -300,7 +288,6 @@ static int omap_hsmmc_set_power(struct device *dev, int slot, int power_on, static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) { struct regulator *reg; - int ret = 0; int ocr_value = 0; mmc_slot(host).set_power = omap_hsmmc_set_power; @@ -308,15 +295,6 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) reg = regulator_get(host->dev, "vmmc"); if (IS_ERR(reg)) { dev_dbg(host->dev, "vmmc regulator missing\n"); - /* - * HACK: until fixed.c regulator is usable, - * we don't require a main regulator - * for MMC2 or MMC3 - */ - if (host->id == OMAP_MMC1_DEVID) { - ret = PTR_ERR(reg); - goto err; - } } else { host->vcc = reg; ocr_value = mmc_regulator_get_ocrmask(reg); @@ -324,8 +302,8 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) mmc_slot(host).ocr_mask = ocr_value; } else { if (!(mmc_slot(host).ocr_mask & ocr_value)) { - pr_err("MMC%d ocrmask %x is not supported\n", - host->id, mmc_slot(host).ocr_mask); + pr_err("MMC ocrmask %x is not supported\n", + mmc_slot(host).ocr_mask); mmc_slot(host).ocr_mask = 0; return -EINVAL; } @@ -358,10 +336,6 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) } return 0; - -err: - mmc_slot(host).set_power = NULL; - return ret; } static void omap_hsmmc_reg_put(struct omap_hsmmc_host *host) @@ -1791,7 +1765,6 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) host->dev->dma_mask = &pdata->dma_mask; host->dma_ch = -1; host->irq = irq; - host->id = pdev->id; host->slot_id = 0; host->mapbase = res->start; host->base = ioremap(host->mapbase, SZ_4K); -- cgit v1.2.3 From e23cd53c799694d0dc1d6a66370201ad9c181bae Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 22 Feb 2012 13:16:09 +0100 Subject: mmc: tmio_mmc: do not enable card hotplug interrupts, if unused If TMIO MMC is used in polling mode, or the card is non-removable, or card-detection is performed, using an external interrupt, there is no need to enable controller native card hotplug interrupts. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/tmio_mmc_pio.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index e21988901c36..5444d533322c 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -304,6 +304,7 @@ static int tmio_mmc_start_command(struct tmio_mmc_host *host, struct mmc_command { struct mmc_data *data = host->data; int c = cmd->opcode; + u32 irq_mask = TMIO_MASK_CMD; /* Command 12 is handled by hardware */ if (cmd->opcode == 12 && !cmd->arg) { @@ -339,7 +340,9 @@ static int tmio_mmc_start_command(struct tmio_mmc_host *host, struct mmc_command c |= TRANSFER_READ; } - tmio_mmc_enable_mmc_irqs(host, TMIO_MASK_CMD); + if (!host->native_hotplug) + irq_mask &= ~(TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT); + tmio_mmc_enable_mmc_irqs(host, irq_mask); /* Fire off the command */ sd_ctrl_write32(host, CTL_ARG_REG, cmd->arg); @@ -963,6 +966,8 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host, irq_mask |= TMIO_MASK_READOP; if (!_host->chan_tx) irq_mask |= TMIO_MASK_WRITEOP; + if (!_host->native_hotplug) + irq_mask &= ~(TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT); tmio_mmc_enable_mmc_irqs(_host, irq_mask); -- cgit v1.2.3 From 4b1a61705a563edb951b8fd9734bcb14286a7888 Mon Sep 17 00:00:00 2001 From: Shiraz Hashim Date: Fri, 24 Feb 2012 11:55:35 +0530 Subject: mmc: sdhci-spear: add pm callbacks to support hibernation Signed-off-by: Shiraz Hashim Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-spear.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci-spear.c b/drivers/mmc/host/sdhci-spear.c index b7f8b33c5f19..6dfa82e03c7e 100644 --- a/drivers/mmc/host/sdhci-spear.c +++ b/drivers/mmc/host/sdhci-spear.c @@ -300,20 +300,15 @@ static int sdhci_resume(struct device *dev) return sdhci_resume_host(host); } - -const struct dev_pm_ops sdhci_pm_ops = { - .suspend = sdhci_suspend, - .resume = sdhci_resume, -}; #endif +static SIMPLE_DEV_PM_OPS(sdhci_pm_ops, sdhci_suspend, sdhci_resume); + static struct platform_driver sdhci_driver = { .driver = { .name = "sdhci", .owner = THIS_MODULE, -#ifdef CONFIG_PM .pm = &sdhci_pm_ops, -#endif }, .probe = sdhci_probe, .remove = __devexit_p(sdhci_remove), -- cgit v1.2.3 From 907d2e7cc7ebba4ab398422a7f0435e1802be65b Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 29 Feb 2012 09:17:21 +0200 Subject: mmc: start removing enable / disable API Most parts of the enable / disable API are no longer used and can be removed. Signed-off-by: Adrian Hunter Tested-by: Venkatraman S Tested-by: Jaehoon Chung Signed-off-by: Chris Ball --- arch/arm/mach-exynos/mach-nuri.c | 5 +- arch/arm/mach-exynos/mach-universal_c210.c | 9 +- drivers/mmc/core/core.c | 187 +++-------------------------- drivers/mmc/core/host.c | 1 - drivers/mmc/core/host.h | 1 - drivers/mmc/host/davinci_mmc.c | 4 - drivers/mmc/host/omap_hsmmc.c | 15 +-- include/linux/mmc/core.h | 1 - include/linux/mmc/host.h | 46 +------ 9 files changed, 27 insertions(+), 242 deletions(-) (limited to 'drivers') diff --git a/arch/arm/mach-exynos/mach-nuri.c b/arch/arm/mach-exynos/mach-nuri.c index aa37179d776c..3cdabd39f97c 100644 --- a/arch/arm/mach-exynos/mach-nuri.c +++ b/arch/arm/mach-exynos/mach-nuri.c @@ -109,7 +109,7 @@ static struct s3c_sdhci_platdata nuri_hsmmc0_data __initdata = { .max_width = 8, .host_caps = (MMC_CAP_8_BIT_DATA | MMC_CAP_4_BIT_DATA | MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED | - MMC_CAP_DISABLE | MMC_CAP_ERASE), + MMC_CAP_ERASE), .cd_type = S3C_SDHCI_CD_PERMANENT, .clk_type = S3C_SDHCI_CLK_DIV_EXTERNAL, }; @@ -148,8 +148,7 @@ static struct platform_device emmc_fixed_voltage = { static struct s3c_sdhci_platdata nuri_hsmmc2_data __initdata = { .max_width = 4, .host_caps = MMC_CAP_4_BIT_DATA | - MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED | - MMC_CAP_DISABLE, + MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED, .ext_cd_gpio = EXYNOS4_GPX3(3), /* XEINT_27 */ .ext_cd_gpio_invert = 1, .cd_type = S3C_SDHCI_CD_GPIO, diff --git a/arch/arm/mach-exynos/mach-universal_c210.c b/arch/arm/mach-exynos/mach-universal_c210.c index b2d495b31094..87e3dcf714b5 100644 --- a/arch/arm/mach-exynos/mach-universal_c210.c +++ b/arch/arm/mach-exynos/mach-universal_c210.c @@ -736,8 +736,7 @@ static struct platform_device universal_gpio_keys = { static struct s3c_sdhci_platdata universal_hsmmc0_data __initdata = { .max_width = 8, .host_caps = (MMC_CAP_8_BIT_DATA | MMC_CAP_4_BIT_DATA | - MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED | - MMC_CAP_DISABLE), + MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED), .cd_type = S3C_SDHCI_CD_PERMANENT, .clk_type = S3C_SDHCI_CLK_DIV_EXTERNAL, }; @@ -775,8 +774,7 @@ static struct platform_device mmc0_fixed_voltage = { static struct s3c_sdhci_platdata universal_hsmmc2_data __initdata = { .max_width = 4, .host_caps = MMC_CAP_4_BIT_DATA | - MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED | - MMC_CAP_DISABLE, + MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED, .ext_cd_gpio = EXYNOS4_GPX3(4), /* XEINT_28 */ .ext_cd_gpio_invert = 1, .cd_type = S3C_SDHCI_CD_GPIO, @@ -787,8 +785,7 @@ static struct s3c_sdhci_platdata universal_hsmmc2_data __initdata = { static struct s3c_sdhci_platdata universal_hsmmc3_data __initdata = { .max_width = 4, .host_caps = MMC_CAP_4_BIT_DATA | - MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED | - MMC_CAP_DISABLE, + MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED, .cd_type = S3C_SDHCI_CD_EXTERNAL, }; diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 436409f7f7bd..9b56674ddc2a 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -604,105 +604,6 @@ unsigned int mmc_align_data_size(struct mmc_card *card, unsigned int sz) } EXPORT_SYMBOL(mmc_align_data_size); -/** - * mmc_host_enable - enable a host. - * @host: mmc host to enable - * - * Hosts that support power saving can use the 'enable' and 'disable' - * methods to exit and enter power saving states. For more information - * see comments for struct mmc_host_ops. - */ -int mmc_host_enable(struct mmc_host *host) -{ - if (!(host->caps & MMC_CAP_DISABLE)) - return 0; - - if (host->en_dis_recurs) - return 0; - - if (host->nesting_cnt++) - return 0; - - cancel_delayed_work_sync(&host->disable); - - if (host->enabled) - return 0; - - if (host->ops->enable) { - int err; - - host->en_dis_recurs = 1; - mmc_host_clk_hold(host); - err = host->ops->enable(host); - mmc_host_clk_release(host); - host->en_dis_recurs = 0; - - if (err) { - pr_debug("%s: enable error %d\n", - mmc_hostname(host), err); - return err; - } - } - host->enabled = 1; - return 0; -} -EXPORT_SYMBOL(mmc_host_enable); - -static int mmc_host_do_disable(struct mmc_host *host, int lazy) -{ - if (host->ops->disable) { - int err; - - host->en_dis_recurs = 1; - mmc_host_clk_hold(host); - err = host->ops->disable(host, lazy); - mmc_host_clk_release(host); - host->en_dis_recurs = 0; - - if (err < 0) { - pr_debug("%s: disable error %d\n", - mmc_hostname(host), err); - return err; - } - if (err > 0) { - unsigned long delay = msecs_to_jiffies(err); - - mmc_schedule_delayed_work(&host->disable, delay); - } - } - host->enabled = 0; - return 0; -} - -/** - * mmc_host_disable - disable a host. - * @host: mmc host to disable - * - * Hosts that support power saving can use the 'enable' and 'disable' - * methods to exit and enter power saving states. For more information - * see comments for struct mmc_host_ops. - */ -int mmc_host_disable(struct mmc_host *host) -{ - int err; - - if (!(host->caps & MMC_CAP_DISABLE)) - return 0; - - if (host->en_dis_recurs) - return 0; - - if (--host->nesting_cnt) - return 0; - - if (!host->enabled) - return 0; - - err = mmc_host_do_disable(host, 0); - return err; -} -EXPORT_SYMBOL(mmc_host_disable); - /** * __mmc_claim_host - exclusively claim a host * @host: mmc host to claim @@ -741,8 +642,8 @@ int __mmc_claim_host(struct mmc_host *host, atomic_t *abort) wake_up(&host->wq); spin_unlock_irqrestore(&host->lock, flags); remove_wait_queue(&host->wq, &wait); - if (!stop) - mmc_host_enable(host); + if (host->ops->enable && !stop && host->claim_cnt == 1) + host->ops->enable(host); return stop; } @@ -767,21 +668,28 @@ int mmc_try_claim_host(struct mmc_host *host) claimed_host = 1; } spin_unlock_irqrestore(&host->lock, flags); + if (host->ops->enable && claimed_host && host->claim_cnt == 1) + host->ops->enable(host); return claimed_host; } EXPORT_SYMBOL(mmc_try_claim_host); /** - * mmc_do_release_host - release a claimed host + * mmc_release_host - release a host * @host: mmc host to release * - * If you successfully claimed a host, this function will - * release it again. + * Release a MMC host, allowing others to claim the host + * for their operations. */ -void mmc_do_release_host(struct mmc_host *host) +void mmc_release_host(struct mmc_host *host) { unsigned long flags; + WARN_ON(!host->claimed); + + if (host->ops->disable && host->claim_cnt == 1) + host->ops->disable(host); + spin_lock_irqsave(&host->lock, flags); if (--host->claim_cnt) { /* Release for nested claim */ @@ -793,67 +701,6 @@ void mmc_do_release_host(struct mmc_host *host) wake_up(&host->wq); } } -EXPORT_SYMBOL(mmc_do_release_host); - -void mmc_host_deeper_disable(struct work_struct *work) -{ - struct mmc_host *host = - container_of(work, struct mmc_host, disable.work); - - /* If the host is claimed then we do not want to disable it anymore */ - if (!mmc_try_claim_host(host)) - return; - mmc_host_do_disable(host, 1); - mmc_do_release_host(host); -} - -/** - * mmc_host_lazy_disable - lazily disable a host. - * @host: mmc host to disable - * - * Hosts that support power saving can use the 'enable' and 'disable' - * methods to exit and enter power saving states. For more information - * see comments for struct mmc_host_ops. - */ -int mmc_host_lazy_disable(struct mmc_host *host) -{ - if (!(host->caps & MMC_CAP_DISABLE)) - return 0; - - if (host->en_dis_recurs) - return 0; - - if (--host->nesting_cnt) - return 0; - - if (!host->enabled) - return 0; - - if (host->disable_delay) { - mmc_schedule_delayed_work(&host->disable, - msecs_to_jiffies(host->disable_delay)); - return 0; - } else - return mmc_host_do_disable(host, 1); -} -EXPORT_SYMBOL(mmc_host_lazy_disable); - -/** - * mmc_release_host - release a host - * @host: mmc host to release - * - * Release a MMC host, allowing others to claim the host - * for their operations. - */ -void mmc_release_host(struct mmc_host *host) -{ - WARN_ON(!host->claimed); - - mmc_host_lazy_disable(host); - - mmc_do_release_host(host); -} - EXPORT_SYMBOL(mmc_release_host); /* @@ -2227,8 +2074,6 @@ void mmc_stop_host(struct mmc_host *host) spin_unlock_irqrestore(&host->lock, flags); #endif - if (host->caps & MMC_CAP_DISABLE) - cancel_delayed_work(&host->disable); cancel_delayed_work_sync(&host->detect); mmc_flush_scheduled_work(); @@ -2423,13 +2268,11 @@ int mmc_suspend_host(struct mmc_host *host) { int err = 0; - if (host->caps & MMC_CAP_DISABLE) - cancel_delayed_work(&host->disable); cancel_delayed_work(&host->detect); mmc_flush_scheduled_work(); if (mmc_try_claim_host(host)) { err = mmc_cache_ctrl(host, 0); - mmc_do_release_host(host); + mmc_release_host(host); } else { err = -EBUSY; } @@ -2450,7 +2293,7 @@ int mmc_suspend_host(struct mmc_host *host) if (host->bus_ops->suspend) { err = host->bus_ops->suspend(host); } - mmc_do_release_host(host); + mmc_release_host(host); if (err == -ENOSYS || !host->bus_ops->resume) { /* diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index c3704e293a7b..91c84c7a1829 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -330,7 +330,6 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) spin_lock_init(&host->lock); init_waitqueue_head(&host->wq); INIT_DELAYED_WORK(&host->detect, mmc_rescan); - INIT_DELAYED_WORK_DEFERRABLE(&host->disable, mmc_host_deeper_disable); #ifdef CONFIG_PM host->pm_notify.notifier_call = mmc_pm_notify; #endif diff --git a/drivers/mmc/core/host.h b/drivers/mmc/core/host.h index 08a7852ade44..f2ab9e578126 100644 --- a/drivers/mmc/core/host.h +++ b/drivers/mmc/core/host.h @@ -14,7 +14,6 @@ int mmc_register_host_class(void); void mmc_unregister_host_class(void); -void mmc_host_deeper_disable(struct work_struct *work); #endif diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c index 64a8325a4a8a..8de9c9bdba0b 100644 --- a/drivers/mmc/host/davinci_mmc.c +++ b/drivers/mmc/host/davinci_mmc.c @@ -1418,17 +1418,14 @@ static int davinci_mmcsd_suspend(struct device *dev) struct mmc_davinci_host *host = platform_get_drvdata(pdev); int ret; - mmc_host_enable(host->mmc); ret = mmc_suspend_host(host->mmc); if (!ret) { writel(0, host->base + DAVINCI_MMCIM); mmc_davinci_reset_ctrl(host, 1); - mmc_host_disable(host->mmc); clk_disable(host->clk); host->suspended = 1; } else { host->suspended = 0; - mmc_host_disable(host->mmc); } return ret; @@ -1444,7 +1441,6 @@ static int davinci_mmcsd_resume(struct device *dev) return 0; clk_enable(host->clk); - mmc_host_enable(host->mmc); mmc_davinci_reset_ctrl(host, 0); ret = mmc_resume_host(host->mmc); diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index e5501704b2db..98adf0c51e05 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -167,7 +167,6 @@ struct omap_hsmmc_host { int got_dbclk; int response_busy; int context_loss; - int dpm_state; int vdd; int protect_card; int reqs_blocked; @@ -1619,7 +1618,7 @@ static int omap_hsmmc_enable_fclk(struct mmc_host *mmc) return 0; } -static int omap_hsmmc_disable_fclk(struct mmc_host *mmc, int lazy) +static int omap_hsmmc_disable_fclk(struct mmc_host *mmc) { struct omap_hsmmc_host *host = mmc_priv(mmc); @@ -1653,15 +1652,8 @@ static int omap_hsmmc_regs_show(struct seq_file *s, void *data) if (host->pdata->get_context_loss_count) context_loss = host->pdata->get_context_loss_count(host->dev); - seq_printf(s, "mmc%d:\n" - " enabled:\t%d\n" - " dpm_state:\t%d\n" - " nesting_cnt:\t%d\n" - " ctx_loss:\t%d:%d\n" - "\nregs:\n", - mmc->index, mmc->enabled ? 1 : 0, - host->dpm_state, mmc->nesting_cnt, - host->context_loss, context_loss); + seq_printf(s, "mmc%d:\n ctx_loss:\t%d:%d\n\nregs:\n", + mmc->index, host->context_loss, context_loss); if (host->suspended) { seq_printf(s, "host suspended, can't read registers\n"); @@ -1800,7 +1792,6 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) omap_hsmmc_context_save(host); - mmc->caps |= MMC_CAP_DISABLE; if (host->pdata->controller_flags & OMAP_HSMMC_BROKEN_MULTIBLOCK_READ) { dev_info(&pdev->dev, "multiblock reads disabled due to 35xx erratum 2.1.1.128; MMC read performance may suffer\n"); mmc->caps2 |= MMC_CAP2_NO_MULTI_READ; diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index 87a976cc5654..2bfa589764de 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -175,7 +175,6 @@ extern unsigned int mmc_align_data_size(struct mmc_card *, unsigned int); extern int __mmc_claim_host(struct mmc_host *host, atomic_t *abort); extern void mmc_release_host(struct mmc_host *host); -extern void mmc_do_release_host(struct mmc_host *host); extern int mmc_try_claim_host(struct mmc_host *host); extern int mmc_flush_cache(struct mmc_card *); diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index d1d3743fde90..e05bd241c676 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -80,34 +80,11 @@ struct mmc_ios { struct mmc_host_ops { /* - * Hosts that support power saving can use the 'enable' and 'disable' - * methods to exit and enter power saving states. 'enable' is called - * when the host is claimed and 'disable' is called (or scheduled with - * a delay) when the host is released. The 'disable' is scheduled if - * the disable delay set by 'mmc_set_disable_delay()' is non-zero, - * otherwise 'disable' is called immediately. 'disable' may be - * scheduled repeatedly, to permit ever greater power saving at the - * expense of ever greater latency to re-enable. Rescheduling is - * determined by the return value of the 'disable' method. A positive - * value gives the delay in milliseconds. - * - * In the case where a host function (like set_ios) may be called - * with or without the host claimed, enabling and disabling can be - * done directly and will nest correctly. Call 'mmc_host_enable()' and - * 'mmc_host_lazy_disable()' for this purpose, but note that these - * functions must be paired. - * - * Alternatively, 'mmc_host_enable()' may be paired with - * 'mmc_host_disable()' which calls 'disable' immediately. In this - * case the 'disable' method will be called with 'lazy' set to 0. - * This is mainly useful for error paths. - * - * Because lazy disable may be called from a work queue, the 'disable' - * method must claim the host when 'lazy' != 0, which will work - * correctly because recursion is detected and handled. + * 'enable' is called when the host is claimed and 'disable' is called + * when the host is released. 'enable' and 'disable' are deprecated. */ int (*enable)(struct mmc_host *host); - int (*disable)(struct mmc_host *host, int lazy); + int (*disable)(struct mmc_host *host); /* * It is optional for the host to implement pre_req and post_req in * order to support double buffering of requests (prepare one @@ -218,7 +195,7 @@ struct mmc_host { #define MMC_CAP_SPI (1 << 4) /* Talks only SPI protocols */ #define MMC_CAP_NEEDS_POLL (1 << 5) /* Needs polling for card-detection */ #define MMC_CAP_8_BIT_DATA (1 << 6) /* Can the host do 8 bit transfers */ -#define MMC_CAP_DISABLE (1 << 7) /* Can the host be disabled */ + #define MMC_CAP_NONREMOVABLE (1 << 8) /* Nonremovable e.g. eMMC */ #define MMC_CAP_WAIT_WHILE_BUSY (1 << 9) /* Waits while card is busy */ #define MMC_CAP_ERASE (1 << 10) /* Allow erase/trim commands */ @@ -301,13 +278,7 @@ struct mmc_host { unsigned int removed:1; /* host is being removed */ #endif - /* Only used with MMC_CAP_DISABLE */ - int enabled; /* host is enabled */ int rescan_disable; /* disable card detection */ - int nesting_cnt; /* "enable" nesting count */ - int en_dis_recurs; /* detect recursion */ - unsigned int disable_delay; /* disable delay in msecs */ - struct delayed_work disable; /* disabling work */ struct mmc_card *card; /* device attached to this host */ @@ -407,17 +378,8 @@ int mmc_card_awake(struct mmc_host *host); int mmc_card_sleep(struct mmc_host *host); int mmc_card_can_sleep(struct mmc_host *host); -int mmc_host_enable(struct mmc_host *host); -int mmc_host_disable(struct mmc_host *host); -int mmc_host_lazy_disable(struct mmc_host *host); int mmc_pm_notify(struct notifier_block *notify_block, unsigned long, void *); -static inline void mmc_set_disable_delay(struct mmc_host *host, - unsigned int disable_delay) -{ - host->disable_delay = disable_delay; -} - /* Module parameter */ extern bool mmc_assume_removable; -- cgit v1.2.3 From 956d9fd5eb3cbb322440844ed341145707bd71f8 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 5 Mar 2012 15:52:43 +0100 Subject: mmc: core: Clean up after mmc_pre_req if card was removed Make sure mmc_start_req cancels the prepared job, if the request was prevented to be started due to the card has been removed. This bug was introduced in commit: mmc: allow upper layers to know immediately if card has been removed Signed-off-by: Ulf Hansson Reviewed-by: Per Forlin Tested-by: Jaehoon Chung Signed-off-by: Chris Ball --- drivers/mmc/core/core.c | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 9b56674ddc2a..14f262e9246d 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -249,16 +249,17 @@ static void mmc_wait_done(struct mmc_request *mrq) complete(&mrq->completion); } -static void __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq) +static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq) { init_completion(&mrq->completion); mrq->done = mmc_wait_done; if (mmc_card_removed(host->card)) { mrq->cmd->error = -ENOMEDIUM; complete(&mrq->completion); - return; + return -ENOMEDIUM; } mmc_start_request(host, mrq); + return 0; } static void mmc_wait_for_req_done(struct mmc_host *host, @@ -342,6 +343,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, struct mmc_async_req *areq, int *error) { int err = 0; + int start_err = 0; struct mmc_async_req *data = host->areq; /* Prepare a new request */ @@ -351,30 +353,23 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, if (host->areq) { mmc_wait_for_req_done(host, host->areq->mrq); err = host->areq->err_check(host->card, host->areq); - if (err) { - /* post process the completed failed request */ - mmc_post_req(host, host->areq->mrq, 0); - if (areq) - /* - * Cancel the new prepared request, because - * it can't run until the failed - * request has been properly handled. - */ - mmc_post_req(host, areq->mrq, -EINVAL); - - host->areq = NULL; - goto out; - } } - if (areq) - __mmc_start_req(host, areq->mrq); + if (!err && areq) + start_err = __mmc_start_req(host, areq->mrq); if (host->areq) mmc_post_req(host, host->areq->mrq, 0); - host->areq = areq; - out: + /* Cancel a prepared request if it was not started. */ + if ((err || start_err) && areq) + mmc_post_req(host, areq->mrq, -EINVAL); + + if (err) + host->areq = NULL; + else + host->areq = areq; + if (error) *error = err; return data; -- cgit v1.2.3 From 37f6190d54f4d90b886784fadbb9cf82d5c8b1ef Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Thu, 8 Mar 2012 23:41:35 -0500 Subject: mmc: omap_hsmmc: Fix "Unbalanced pm_runtime_enable!" warning Otherwise we can get following warning when re-loading the omap_hsmmc driver module when gpio_twl4030 module is not loaded: omap_hsmmc omap_hsmmc.0: Unbalanced pm_runtime_enable! omap_hsmmc omap_hsmmc.0: Unable to grab MMC CD IRQ omap_hsmmc: probe of omap_hsmmc.0 failed with error -22 Signed-off-by: Tony Lindgren Signed-off-by: Chris Ball --- drivers/mmc/host/omap_hsmmc.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 98adf0c51e05..94d463859e9b 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -1935,6 +1935,7 @@ err_irq_cd_init: err_irq: pm_runtime_mark_last_busy(host->dev); pm_runtime_put_autosuspend(host->dev); + pm_runtime_disable(host->dev); clk_put(host->fclk); if (host->got_dbclk) { clk_disable(host->dbclk); -- cgit v1.2.3 From 52d0974e36761fd1daf1eb02cfb2444a7b200087 Mon Sep 17 00:00:00 2001 From: Subhash Jadavani Date: Tue, 6 Mar 2012 17:59:12 +0530 Subject: mmc: core: hs200 fixes This patch fixes following issues when HS200 is enabled: 1. If executing_tuning() host ops is called without mmc_host_clk_hold(), card clocks might get turned off (if MMC_CLK_GATING is enabled) while execute_tuning() is in progress. So this patch makes sure that execute_tuning() is called with mmc_host_clk_hold(). 2. If host timing mode is set to HS200 mode, there should not be any communication with the card until execute_tuning() is completed. But there is a chance that CMD6 might be sent to enable set HPI_EN (of HPI_MGMT field in EXT_CSD) before execute_tuning() is called. So this patch moves this operation after execute_tuning() is completed. Signed-off-by: Subhash Jadavani Reviewed-by: girish.shivananjappa@linaro.org Signed-off-by: Chris Ball --- drivers/mmc/core/mmc.c | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 6defddd795f6..d7d8a93c3503 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1046,22 +1046,6 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } } - /* - * Enable HPI feature (if supported) - */ - if (card->ext_csd.hpi) { - err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_HPI_MGMT, 1, 0); - if (err && err != -EBADMSG) - goto free_card; - if (err) { - pr_warning("%s: Enabling HPI failed\n", - mmc_hostname(card->host)); - err = 0; - } else - card->ext_csd.hpi_en = 1; - } - /* * Compute bus speed. */ @@ -1111,9 +1095,12 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, * 4. execute tuning for HS200 */ if ((host->caps2 & MMC_CAP2_HS200) && - card->host->ops->execute_tuning) + card->host->ops->execute_tuning) { + mmc_host_clk_hold(card->host); err = card->host->ops->execute_tuning(card->host, MMC_SEND_TUNING_BLOCK_HS200); + mmc_host_clk_release(card->host); + } if (err) { pr_warning("%s: tuning execution failed\n", mmc_hostname(card->host)); @@ -1232,6 +1219,23 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } } + /* + * Enable HPI feature (if supported) + */ + if (card->ext_csd.hpi) { + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_HPI_MGMT, 1, + card->ext_csd.generic_cmd6_time); + if (err && err != -EBADMSG) + goto free_card; + if (err) { + pr_warning("%s: Enabling HPI failed\n", + mmc_hostname(card->host)); + err = 0; + } else + card->ext_csd.hpi_en = 1; + } + /* * If cache size is higher than 0, this indicates * the existence of cache and it can be turned on. -- cgit v1.2.3 From 183f796b5f701f8ab530ab232e4c27aef527bd76 Mon Sep 17 00:00:00 2001 From: "Shimoda, Yoshihiro" Date: Wed, 7 Mar 2012 17:37:10 +0900 Subject: mmc: sh_mmcif: fix MMC_GEN_CMD setting The MMC_GEN_CMD (CMD56) doesn't need to check busy signal. So, the patch fixes the setting. Signed-off-by: Yoshihiro Shimoda Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 75a485448796..60f205708f54 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -746,7 +746,6 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host, case MMC_SET_WRITE_PROT: case MMC_CLR_WRITE_PROT: case MMC_ERASE: - case MMC_GEN_CMD: tmp |= CMD_SET_RBSY; break; } @@ -829,7 +828,6 @@ static void sh_mmcif_start_cmd(struct sh_mmcif_host *host, case MMC_SET_WRITE_PROT: case MMC_CLR_WRITE_PROT: case MMC_ERASE: - case MMC_GEN_CMD: mask = MASK_START_CMD | MASK_MRBSYE; break; default: -- cgit v1.2.3 From 2cecdf002300bca86ce83b17a60f907d5a4ce7dc Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Thu, 23 Feb 2012 17:02:20 +0530 Subject: mmc: omap_hsmmc: convert all pr_* to dev_* Convert all instances of pr_* prints within the driver to instead use dev_* prints. Reported-by: Russell King Signed-off-by: Rajendra Nayak Signed-off-by: Chris Ball --- drivers/mmc/host/omap_hsmmc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 94d463859e9b..0306aea329d4 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -301,7 +301,7 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) mmc_slot(host).ocr_mask = ocr_value; } else { if (!(mmc_slot(host).ocr_mask & ocr_value)) { - pr_err("MMC ocrmask %x is not supported\n", + dev_err(host->dev, "ocrmask %x is not supported\n", mmc_slot(host).ocr_mask); mmc_slot(host).ocr_mask = 0; return -EINVAL; @@ -1131,14 +1131,14 @@ static void omap_hsmmc_protect_card(struct omap_hsmmc_host *host) host->reqs_blocked = 0; if (mmc_slot(host).get_cover_state(host->dev, host->slot_id)) { if (host->protect_card) { - pr_info("%s: cover is closed, " + dev_info(host->dev, "%s: cover is closed, " "card is now accessible\n", mmc_hostname(host->mmc)); host->protect_card = 0; } } else { if (!host->protect_card) { - pr_info("%s: cover is open, " + dev_info(host->dev, "%s: cover is open, " "card is now inaccessible\n", mmc_hostname(host->mmc)); host->protect_card = 1; @@ -1275,7 +1275,7 @@ static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host, if (!next && data->host_cookie && data->host_cookie != host->next_data.cookie) { - pr_warning("[%s] invalid cookie: data->host_cookie %d" + dev_warn(host->dev, "[%s] invalid cookie: data->host_cookie %d" " host->next_data.cookie %d\n", __func__, data->host_cookie, host->next_data.cookie); data->host_cookie = 0; -- cgit v1.2.3 From 83bb24aaa4af2ec7c127cc2a5fa7ca9f64cdd13d Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 13 Mar 2012 05:19:13 -0400 Subject: mmc: core: add high-capacity erase size capability flag Let drivers specify the use of high-capacity erase size. Signed-off-by: Adrian Hunter Signed-off-by: Chris Ball --- drivers/mmc/core/mmc.c | 3 ++- include/linux/mmc/host.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index d7d8a93c3503..02914d609a91 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -952,7 +952,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, * If enhanced_area_en is TRUE, host needs to enable ERASE_GRP_DEF * bit. This bit will be lost every time after a reset or power off. */ - if (card->ext_csd.enhanced_area_en) { + if (card->ext_csd.enhanced_area_en || + (card->ext_csd.rev >= 3 && (host->caps2 & MMC_CAP2_HC_ERASE_SZ))) { err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_ERASE_GROUP_DEF, 1, card->ext_csd.generic_cmd6_time); diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index e05bd241c676..33a4d08d810e 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -236,6 +236,7 @@ struct mmc_host { MMC_CAP2_HS200_1_2V_SDR) #define MMC_CAP2_BROKEN_VOLTAGE (1 << 7) /* Use the broken voltage */ #define MMC_CAP2_DETECT_ON_ERR (1 << 8) /* On I/O err check card removal */ +#define MMC_CAP2_HC_ERASE_SZ (1 << 9) /* High-capacity erase size */ mmc_pm_flag_t pm_caps; /* supported pm features */ unsigned int power_notify_type; -- cgit v1.2.3 From da721cf7808f9731454757d704f0aa69ae2f1aae Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 7 Feb 2012 14:48:53 +0200 Subject: mmc: sdhci-pci: select HC erase size for Medfield eMMC Signed-off-by: Adrian Hunter Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-pci.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 6ebdc4010e7c..9952535671f2 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -244,7 +244,8 @@ static inline void sdhci_pci_remove_own_cd(struct sdhci_pci_slot *slot) static int mfd_emmc_probe_slot(struct sdhci_pci_slot *slot) { slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE; - slot->host->mmc->caps2 = MMC_CAP2_BOOTPART_NOACC; + slot->host->mmc->caps2 |= MMC_CAP2_BOOTPART_NOACC | + MMC_CAP2_HC_ERASE_SZ; return 0; } -- cgit v1.2.3 From 6308d2905bd30290d90f28730828fd572161dd11 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 7 Feb 2012 14:48:54 +0200 Subject: mmc: sdhci: add quirk for keeping card power during suspend Add quirk SDHCI_QUIRK2_HOST_OFF_CARD_ON to cater for the case when the card keeps power during suspend but the host controller does not i.e. the card power is not controlled by the host controller. In that case, the controller must be fully reset on resume. Signed-off-by: Adrian Hunter Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 13 +++++++++++-- include/linux/mmc/sdhci.h | 2 ++ 2 files changed, 13 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 1f5888b42b2c..80cc7847c531 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2426,8 +2426,17 @@ int sdhci_resume_host(struct sdhci_host *host) if (ret) return ret; - sdhci_init(host, (host->mmc->pm_flags & MMC_PM_KEEP_POWER)); - mmiowb(); + if ((host->mmc->pm_flags & MMC_PM_KEEP_POWER) && + (host->quirks2 & SDHCI_QUIRK2_HOST_OFF_CARD_ON)) { + /* Card keeps power but host controller does not */ + sdhci_init(host, 0); + host->pwr = 0; + host->clock = 0; + sdhci_do_set_ios(host, &host->mmc->ios); + } else { + sdhci_init(host, (host->mmc->pm_flags & MMC_PM_KEEP_POWER)); + mmiowb(); + } ret = mmc_resume_host(host->mmc); sdhci_enable_card_detection(host); diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h index c750f85177d9..e9051e1cb1ce 100644 --- a/include/linux/mmc/sdhci.h +++ b/include/linux/mmc/sdhci.h @@ -90,6 +90,8 @@ struct sdhci_host { unsigned int quirks2; /* More deviations from spec. */ +#define SDHCI_QUIRK2_HOST_OFF_CARD_ON (1<<0) + int irq; /* Device IRQ */ void __iomem *ioaddr; /* Mapped address */ -- cgit v1.2.3 From f3c55a7b5d7f2d40c805a62174bb733b55875523 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 7 Feb 2012 14:48:55 +0200 Subject: mmc: sdhci-pci: add SDHCI_QUIRK2_HOST_OFF_CARD_ON for Medfield SDIO SDIO is powered separately from the host controller, so the card can remain on while the host controller is powered off during suspend. Signed-off-by: Adrian Hunter Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-pci.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 9952535671f2..3cb9abf67d82 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -47,6 +47,7 @@ struct sdhci_pci_slot; struct sdhci_pci_fixes { unsigned int quirks; + unsigned int quirks2; bool allow_runtime_pm; int (*probe) (struct sdhci_pci_chip *); @@ -73,6 +74,7 @@ struct sdhci_pci_chip { struct pci_dev *pdev; unsigned int quirks; + unsigned int quirks2; bool allow_runtime_pm; const struct sdhci_pci_fixes *fixes; @@ -272,6 +274,7 @@ static const struct sdhci_pci_fixes sdhci_intel_mfd_sd = { static const struct sdhci_pci_fixes sdhci_intel_mfd_sdio = { .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, + .quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON, .allow_runtime_pm = true, .probe_slot = mfd_sdio_probe_slot, }; @@ -1207,6 +1210,7 @@ static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot( host->hw_name = "PCI"; host->ops = &sdhci_pci_ops; host->quirks = chip->quirks; + host->quirks2 = chip->quirks2; host->irq = pdev->irq; @@ -1366,6 +1370,7 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev, chip->fixes = (const struct sdhci_pci_fixes *)ent->driver_data; if (chip->fixes) { chip->quirks = chip->fixes->quirks; + chip->quirks2 = chip->fixes->quirks2; chip->allow_runtime_pm = chip->fixes->allow_runtime_pm; } chip->num_slots = slots; -- cgit v1.2.3 From 46856a68dcb5f67c779d211fd6bcb5d7a2a7f19b Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Mon, 12 Mar 2012 20:32:37 +0530 Subject: mmc: omap_hsmmc: Convert hsmmc driver to use device tree Define dt bindings for the ti-omap-hsmmc, and adapt the driver to extract data (which was earlier passed as platform_data) from device tree. Signed-off-by: Rajendra Nayak Acked-by: Rob Herring Signed-off-by: Chris Ball --- .../devicetree/bindings/mmc/ti-omap-hsmmc.txt | 33 ++++++++++ drivers/mmc/host/omap_hsmmc.c | 73 ++++++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt new file mode 100644 index 000000000000..dbd4368ab8cc --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt @@ -0,0 +1,33 @@ +* TI Highspeed MMC host controller for OMAP + +The Highspeed MMC Host Controller on TI OMAP family +provides an interface for MMC, SD, and SDIO types of memory cards. + +Required properties: +- compatible: + Should be "ti,omap2-hsmmc", for OMAP2 controllers + Should be "ti,omap3-hsmmc", for OMAP3 controllers + Should be "ti,omap4-hsmmc", for OMAP4 controllers +- ti,hwmods: Must be "mmc", n is controller instance starting 1 +- reg : should contain hsmmc registers location and length + +Optional properties: +ti,dual-volt: boolean, supports dual voltage cards +-supply: phandle to the regulator device tree node +"supply-name" examples are "vmmc", "vmmc_aux" etc +ti,bus-width: Number of data lines, default assumed is 1 if the property is missing. +cd-gpios: GPIOs for card detection +wp-gpios: GPIOs for write protection +ti,non-removable: non-removable slot (like eMMC) +ti,needs-special-reset: Requires a special softreset sequence + +Example: + mmc1: mmc@0x4809c000 { + compatible = "ti,omap4-hsmmc"; + reg = <0x4809c000 0x400>; + ti,hwmods = "mmc1"; + ti,dual-volt; + ti,bus-width = <4>; + vmmc-supply = <&vmmc>; /* phandle to regulator node */ + ti,non-removable; + }; diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 0306aea329d4..e0808d4a7681 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -26,6 +26,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -1710,6 +1713,65 @@ static void omap_hsmmc_debugfs(struct mmc_host *mmc) #endif +#ifdef CONFIG_OF +static u16 omap4_reg_offset = 0x100; + +static const struct of_device_id omap_mmc_of_match[] = { + { + .compatible = "ti,omap2-hsmmc", + }, + { + .compatible = "ti,omap3-hsmmc", + }, + { + .compatible = "ti,omap4-hsmmc", + .data = &omap4_reg_offset, + }, + {}, +} +MODULE_DEVICE_TABLE(of, omap_mmc_of_match); + +static struct omap_mmc_platform_data *of_get_hsmmc_pdata(struct device *dev) +{ + struct omap_mmc_platform_data *pdata; + struct device_node *np = dev->of_node; + u32 bus_width; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; /* out of memory */ + + if (of_find_property(np, "ti,dual-volt", NULL)) + pdata->controller_flags |= OMAP_HSMMC_SUPPORTS_DUAL_VOLT; + + /* This driver only supports 1 slot */ + pdata->nr_slots = 1; + pdata->slots[0].switch_pin = of_get_named_gpio(np, "cd-gpios", 0); + pdata->slots[0].gpio_wp = of_get_named_gpio(np, "wp-gpios", 0); + + if (of_find_property(np, "ti,non-removable", NULL)) { + pdata->slots[0].nonremovable = true; + pdata->slots[0].no_regulator_off_init = true; + } + of_property_read_u32(np, "ti,bus-width", &bus_width); + if (bus_width == 4) + pdata->slots[0].caps |= MMC_CAP_4_BIT_DATA; + else if (bus_width == 8) + pdata->slots[0].caps |= MMC_CAP_8_BIT_DATA; + + if (of_find_property(np, "ti,needs-special-reset", NULL)) + pdata->slots[0].features |= HSMMC_HAS_UPDATED_RESET; + + return pdata; +} +#else +static inline struct omap_mmc_platform_data + *of_get_hsmmc_pdata(struct device *dev) +{ + return NULL; +} +#endif + static int __init omap_hsmmc_probe(struct platform_device *pdev) { struct omap_mmc_platform_data *pdata = pdev->dev.platform_data; @@ -1717,6 +1779,16 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) struct omap_hsmmc_host *host = NULL; struct resource *res; int ret, irq; + const struct of_device_id *match; + + match = of_match_device(of_match_ptr(omap_mmc_of_match), &pdev->dev); + if (match) { + pdata = of_get_hsmmc_pdata(&pdev->dev); + if (match->data) { + u16 *offsetp = match->data; + pdata->reg_offset = *offsetp; + } + } if (pdata == NULL) { dev_err(&pdev->dev, "Platform Data is missing\n"); @@ -2122,6 +2194,7 @@ static struct platform_driver omap_hsmmc_driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, .pm = &omap_hsmmc_dev_pm_ops, + .of_match_table = of_match_ptr(omap_mmc_of_match), }, }; -- cgit v1.2.3 From 1f84b71b3fa834faa87e14c8dc5d5a7c1fa084e8 Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Mon, 12 Mar 2012 20:32:38 +0530 Subject: mmc: omap_hsmmc: Avoid a regulator voltage change with dt When booting with Device tree, the omap_hsmmc driver does not program the pbias cell (inside OMAP control module) during a regulator voltage change. In case of non-dt boot, this is handled using callbacks from within platform_data and implemented in machine code. To be able to do this with device tree, without invoking any machine code, a OMAP control module driver is needed which is yet missing. The pbias cell is used to provide a 1.8v or 3.0v reference to the mmc/sd/sdio1 interface supporting both 1.8v and 3.0v voltages. Until a OMAP control module driver is available to handle this, when booting with a device tree blob, never change the regulator voltage which might then require a pbias cell re-program. There are 2 instances where in the mmc regulator voltage can be changed. -1- when the regulator is turned OFF. -2- when attempting a switch to 1.8v from 3.0v for dual volt cards This patch avoids a voltage change in both cases when booting from device tree, and hence compromises on power savings. Once the OMAP control module driver is available and hsmmc driver is modified to then do pbias programming even when booting with device tree, these limitaions can be removed to achieve better power savings. Signed-off-by: Rajendra Nayak Tested-by: Balaji T K Signed-off-by: Chris Ball --- drivers/mmc/host/omap_hsmmc.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index e0808d4a7681..47adb161d3ad 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -244,6 +244,13 @@ static int omap_hsmmc_set_power(struct device *dev, int slot, int power_on, */ if (!host->vcc) return 0; + /* + * With DT, never turn OFF the regulator. This is because + * the pbias cell programming support is still missing when + * booting with Device tree + */ + if (of_have_populated_dt() && !vdd) + return 0; if (mmc_slot(host).before_set_reg) mmc_slot(host).before_set_reg(dev, slot, power_on, vdd); @@ -1536,7 +1543,13 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) * of external transceiver; but they all handle 1.8V. */ if ((OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET) && - (ios->vdd == DUAL_VOLT_OCR_BIT)) { + (ios->vdd == DUAL_VOLT_OCR_BIT) && + /* + * With pbias cell programming missing, this + * can't be allowed when booting with device + * tree. + */ + (!of_have_populated_dt())) { /* * The mmc_select_voltage fn of the core does * not seem to set the power_mode to -- cgit v1.2.3 From be7b5622e608189894c2c440c3fb0138f122071f Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Sun, 11 Mar 2012 23:39:58 +0200 Subject: mmc: davinci: Eliminate spurious interrupts The davinci mmc interrupt handler fills the fifo, as long as the DXRDY or DRRDY bits are set in the status register. If interrupts fire during this loop, they will be handled by the handler, but the interrupt controller will still buffer these. As a result, the handler will be called again to serve these needlessly. In order to avoid these spurious interrupts, keep interrupts masked while filling the fifo. Signed-off-by: Ido Yariv Tested-by: Rajashekhara, Sudhakar Signed-off-by: Chris Ball --- drivers/mmc/host/davinci_mmc.c | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c index 8de9c9bdba0b..1e076fb06ac3 100644 --- a/drivers/mmc/host/davinci_mmc.c +++ b/drivers/mmc/host/davinci_mmc.c @@ -1009,12 +1009,33 @@ static irqreturn_t mmc_davinci_irq(int irq, void *dev_id) * by read. So, it is not unbouned loop even in the case of * non-dma. */ - while (host->bytes_left && (status & (MMCST0_DXRDY | MMCST0_DRRDY))) { - davinci_fifo_data_trans(host, rw_threshold); - status = readl(host->base + DAVINCI_MMCST0); - if (!status) - break; - qstatus |= status; + if (host->bytes_left && (status & (MMCST0_DXRDY | MMCST0_DRRDY))) { + unsigned long im_val; + + /* + * If interrupts fire during the following loop, they will be + * handled by the handler, but the PIC will still buffer these. + * As a result, the handler will be called again to serve these + * needlessly. In order to avoid these spurious interrupts, + * keep interrupts masked during the loop. + */ + im_val = readl(host->base + DAVINCI_MMCIM); + writel(0, host->base + DAVINCI_MMCIM); + + do { + davinci_fifo_data_trans(host, rw_threshold); + status = readl(host->base + DAVINCI_MMCST0); + qstatus |= status; + } while (host->bytes_left && + (status & (MMCST0_DXRDY | MMCST0_DRRDY))); + + /* + * If an interrupt is pending, it is assumed it will fire when + * it is unmasked. This assumption is also taken when the MMCIM + * is first set. Otherwise, writing to MMCIM after reading the + * status is race-prone. + */ + writel(im_val, host->base + DAVINCI_MMCIM); } if (qstatus & MMCST0_DATDNE) { -- cgit v1.2.3 From ee698f503442741ca394d55c77b61c9080537686 Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Sun, 11 Mar 2012 23:39:59 +0200 Subject: mmc: davinci: Poll status for small size transfers For small size non-dma sdio transactions, it is sometimes better to poll the mmc host and avoid interrupts altogether. Polling lowers the number of interrupts and context switches. Tests have shown that for small transactions, only a few polling iterations are needed, so this is worth while. Signed-off-by: Ido Yariv Tested-by: Rajashekhara, Sudhakar Signed-off-by: Chris Ball --- drivers/mmc/host/davinci_mmc.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c index 1e076fb06ac3..c1f3673ae1ef 100644 --- a/drivers/mmc/host/davinci_mmc.c +++ b/drivers/mmc/host/davinci_mmc.c @@ -160,6 +160,16 @@ module_param(rw_threshold, uint, S_IRUGO); MODULE_PARM_DESC(rw_threshold, "Read/Write threshold. Default = 32"); +static unsigned poll_threshold = 128; +module_param(poll_threshold, uint, S_IRUGO); +MODULE_PARM_DESC(poll_threshold, + "Polling transaction size threshold. Default = 128"); + +static unsigned poll_loopcount = 32; +module_param(poll_loopcount, uint, S_IRUGO); +MODULE_PARM_DESC(poll_loopcount, + "Maximum polling loop count. Default = 32"); + static unsigned __initdata use_dma = 1; module_param(use_dma, uint, 0); MODULE_PARM_DESC(use_dma, "Whether to use DMA or not. Default = 1"); @@ -193,6 +203,7 @@ struct mmc_davinci_host { bool use_dma; bool do_dma; bool sdio_int; + bool active_request; /* Scatterlist DMA uses one or more parameter RAM entries: * the main one (associated with rxdma or txdma) plus zero or @@ -219,6 +230,7 @@ struct mmc_davinci_host { #endif }; +static irqreturn_t mmc_davinci_irq(int irq, void *dev_id); /* PIO only */ static void mmc_davinci_sg_to_buf(struct mmc_davinci_host *host) @@ -376,7 +388,20 @@ static void mmc_davinci_start_command(struct mmc_davinci_host *host, writel(cmd->arg, host->base + DAVINCI_MMCARGHL); writel(cmd_reg, host->base + DAVINCI_MMCCMD); - writel(im_val, host->base + DAVINCI_MMCIM); + + host->active_request = true; + + if (!host->do_dma && host->bytes_left <= poll_threshold) { + u32 count = poll_loopcount; + + while (host->active_request && count--) { + mmc_davinci_irq(0, host); + cpu_relax(); + } + } + + if (host->active_request) + writel(im_val, host->base + DAVINCI_MMCIM); } /*----------------------------------------------------------------------*/ @@ -915,6 +940,7 @@ mmc_davinci_xfer_done(struct mmc_davinci_host *host, struct mmc_data *data) if (!data->stop || (host->cmd && host->cmd->error)) { mmc_request_done(host->mmc, data->mrq); writel(0, host->base + DAVINCI_MMCIM); + host->active_request = false; } else mmc_davinci_start_command(host, data->stop); } @@ -942,6 +968,7 @@ static void mmc_davinci_cmd_done(struct mmc_davinci_host *host, cmd->mrq->cmd->retries = 0; mmc_request_done(host->mmc, cmd->mrq); writel(0, host->base + DAVINCI_MMCIM); + host->active_request = false; } } -- cgit v1.2.3 From 2378975bd5ed583da555c7e5dee121663b7d5f46 Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Mon, 12 Mar 2012 08:57:27 +0900 Subject: mmc: core: warn when card doesn't support HPI Someone could use send_hpi_cmd() on a card that doesn't advertise support for HPI. Then maybe didn't work fine. Because card->ext_csd.hpi_cmd didn't set. So if card didn't support hpi, return the warning message. And CMD12's flags is MMC_RSP_R1B. Signed-off-by: Jaehoon Chung Signed-off-by: Kyungmin Park Signed-off-by: Chris Ball --- drivers/mmc/core/mmc_ops.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 4d41fa984c93..69370f494e05 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -553,18 +553,22 @@ int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status) { struct mmc_command cmd = {0}; unsigned int opcode; - unsigned int flags; int err; + if (!card->ext_csd.hpi) { + pr_warning("%s: Card didn't support HPI command\n", + mmc_hostname(card->host)); + return -EINVAL; + } + opcode = card->ext_csd.hpi_cmd; if (opcode == MMC_STOP_TRANSMISSION) - flags = MMC_RSP_R1 | MMC_CMD_AC; + cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; else if (opcode == MMC_SEND_STATUS) - flags = MMC_RSP_R1 | MMC_CMD_AC; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; cmd.opcode = opcode; cmd.arg = card->rca << 16 | 1; - cmd.flags = flags; cmd.cmd_timeout_ms = card->ext_csd.out_of_int_time; err = mmc_wait_for_cmd(card->host, &cmd, 0); -- cgit v1.2.3 From e6039832bed9a9b967796d7021f17f25b625b616 Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Tue, 13 Mar 2012 18:16:40 +0100 Subject: mmc: sdhci-pci: Add MSI support Signed-off-by: Alexander Stein Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-pci.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 3cb9abf67d82..bc666a4ed604 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -1385,6 +1385,8 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev, slots = chip->num_slots; /* Quirk may have changed this */ + pci_enable_msi(pdev); + for (i = 0; i < slots; i++) { slot = sdhci_pci_probe_slot(pdev, chip, first_bar, i); if (IS_ERR(slot)) { @@ -1403,6 +1405,8 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev, return 0; free: + pci_disable_msi(pdev); + pci_set_drvdata(pdev, NULL); kfree(chip); @@ -1425,6 +1429,8 @@ static void __devexit sdhci_pci_remove(struct pci_dev *pdev) for (i = 0; i < chip->num_slots; i++) sdhci_pci_remove_slot(chip->slots[i]); + pci_disable_msi(pdev); + pci_set_drvdata(pdev, NULL); kfree(chip); } -- cgit v1.2.3 From 6379b2375a0c5a6ad437616a4018e6b8fd95e97c Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Wed, 14 Mar 2012 09:52:10 +0100 Subject: mmc: sdhci: check interrupt flags in ISR again When using MSI it is possible that a new MSI is sent while an earlier MSI is currently handled. In this case SDHCI_INT_STATUS only contains SDHCI_INT_RESPONSE and the ISR would not be called again. But at the end of the ISR SDHCI_INT_DATA_END is now also pending which would be ignored. Fix this by rereading the interrupt flags in the ISR until no interrupt we care is pending. Signed-off-by: Alexander Stein Acked-by: Adrian Hunter Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 80cc7847c531..8262cadfdab7 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2267,8 +2267,8 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) { irqreturn_t result; struct sdhci_host *host = dev_id; - u32 intmask; - int cardint = 0; + u32 intmask, unexpected = 0; + int cardint = 0, max_loops = 16; spin_lock(&host->lock); @@ -2286,6 +2286,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) goto out; } +again: DBG("*** %s got interrupt: 0x%08x\n", mmc_hostname(host->mmc), intmask); @@ -2344,19 +2345,23 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) intmask &= ~SDHCI_INT_CARD_INT; if (intmask) { - pr_err("%s: Unexpected interrupt 0x%08x.\n", - mmc_hostname(host->mmc), intmask); - sdhci_dumpregs(host); - + unexpected |= intmask; sdhci_writel(host, intmask, SDHCI_INT_STATUS); } result = IRQ_HANDLED; - mmiowb(); + intmask = sdhci_readl(host, SDHCI_INT_STATUS); + if (intmask && --max_loops) + goto again; out: spin_unlock(&host->lock); + if (unexpected) { + pr_err("%s: Unexpected interrupt 0x%08x.\n", + mmc_hostname(host->mmc), unexpected); + sdhci_dumpregs(host); + } /* * We have to delay this as it calls back into the driver. */ -- cgit v1.2.3 From 296e0b0357e09fdc6f307953da51c0e5da5b84e7 Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Wed, 14 Mar 2012 08:38:58 +0100 Subject: mmc: sdhci-pci: allow 8-bit bus width for Intel PCH Signed-off-by: Alexander Stein Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-pci.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index bc666a4ed604..fbbebe251e01 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -28,6 +28,12 @@ #include "sdhci.h" +/* + * PCI device IDs + */ +#define PCI_DEVICE_ID_INTEL_PCH_SDIO0 0x8809 +#define PCI_DEVICE_ID_INTEL_PCH_SDIO1 0x880a + /* * PCI registers */ @@ -174,6 +180,12 @@ static int mrst_hc_probe(struct sdhci_pci_chip *chip) return 0; } +static int pch_hc_probe_slot(struct sdhci_pci_slot *slot) +{ + slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA; + return 0; +} + #ifdef CONFIG_PM_RUNTIME static irqreturn_t sdhci_pci_sd_cd(int irq, void *dev_id) @@ -285,6 +297,11 @@ static const struct sdhci_pci_fixes sdhci_intel_mfd_emmc = { .probe_slot = mfd_emmc_probe_slot, }; +static const struct sdhci_pci_fixes sdhci_intel_pch_sdio = { + .quirks = SDHCI_QUIRK_BROKEN_ADMA, + .probe_slot = pch_hc_probe_slot, +}; + /* O2Micro extra registers */ #define O2_SD_LOCK_WP 0xD3 #define O2_SD_MULTI_VCC3V 0xEE @@ -820,6 +837,22 @@ static const struct pci_device_id pci_ids[] __devinitdata = { .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_emmc, }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_PCH_SDIO0, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_pch_sdio, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_PCH_SDIO1, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_pch_sdio, + }, + { .vendor = PCI_VENDOR_ID_O2, .device = PCI_DEVICE_ID_O2_8120, -- cgit v1.2.3 From c9b0546a59293cabf54c85e1218da595af3274ff Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 9 Feb 2012 22:57:07 +0100 Subject: mmc: simplify mmc_cd_gpio_request() by removing two parameters Calculate the IRQ number, using gpio_to_irq() and use fixed flags: trigger on both edges. This makes two out of four arguments of the mmc_cd_gpio_request() function redundant. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/core/cd-gpio.c | 13 +++++++++---- include/linux/mmc/cd-gpio.h | 3 +-- 2 files changed, 10 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/core/cd-gpio.c b/drivers/mmc/core/cd-gpio.c index 082202ae4a03..29de31e260dd 100644 --- a/drivers/mmc/core/cd-gpio.c +++ b/drivers/mmc/core/cd-gpio.c @@ -28,13 +28,17 @@ static irqreturn_t mmc_cd_gpio_irqt(int irq, void *dev_id) return IRQ_HANDLED; } -int mmc_cd_gpio_request(struct mmc_host *host, unsigned int gpio, - unsigned int irq, unsigned long flags) +int mmc_cd_gpio_request(struct mmc_host *host, unsigned int gpio) { size_t len = strlen(dev_name(host->parent)) + 4; - struct mmc_cd_gpio *cd = kmalloc(sizeof(*cd) + len, GFP_KERNEL); + struct mmc_cd_gpio *cd; + int irq = gpio_to_irq(gpio); int ret; + if (irq < 0) + return irq; + + cd = kmalloc(sizeof(*cd) + len, GFP_KERNEL); if (!cd) return -ENOMEM; @@ -45,7 +49,8 @@ int mmc_cd_gpio_request(struct mmc_host *host, unsigned int gpio, goto egpioreq; ret = request_threaded_irq(irq, NULL, mmc_cd_gpio_irqt, - flags, cd->label, host); + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + cd->label, host); if (ret < 0) goto eirqreq; diff --git a/include/linux/mmc/cd-gpio.h b/include/linux/mmc/cd-gpio.h index a8e469783318..cefaba038ccb 100644 --- a/include/linux/mmc/cd-gpio.h +++ b/include/linux/mmc/cd-gpio.h @@ -12,8 +12,7 @@ #define MMC_CD_GPIO_H struct mmc_host; -int mmc_cd_gpio_request(struct mmc_host *host, unsigned int gpio, - unsigned int irq, unsigned long flags); +int mmc_cd_gpio_request(struct mmc_host *host, unsigned int gpio); void mmc_cd_gpio_free(struct mmc_host *host); #endif -- cgit v1.2.3 From 2b1ac5c2caccbfd43bd616321cbbe21eb33c7879 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 9 Feb 2012 22:57:08 +0100 Subject: mmc: tmio: calculate the native hotplug condition only once The condition, whether we have to use the native TMIO card hotplug detection interrupt, is rather complex, it is better to only calculate it once and store in the private data. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/tmio_mmc.h | 1 + drivers/mmc/host/tmio_mmc_pio.c | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index f96c536d130a..8531d8d44fc1 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -86,6 +86,7 @@ struct tmio_mmc_host { spinlock_t lock; /* protect host private data */ unsigned long last_req_ts; struct mutex ios_lock; /* protect set_ios() context */ + bool native_hotplug; }; int tmio_mmc_host_probe(struct tmio_mmc_host **host, diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 5444d533322c..49f7f218cee1 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -916,6 +916,10 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host, else mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; + _host->native_hotplug = !(pdata->flags & TMIO_MMC_HAS_COLD_CD || + mmc->caps & MMC_CAP_NEEDS_POLL || + mmc->caps & MMC_CAP_NONREMOVABLE); + pdata->power = false; pm_runtime_enable(&pdev->dev); ret = pm_runtime_resume(&pdev->dev); @@ -934,9 +938,7 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host, * additionally ensure that in case 2) the tmio mmc hardware stays * powered on during runtime for the card detection to work. */ - if (!(pdata->flags & TMIO_MMC_HAS_COLD_CD - || mmc->caps & MMC_CAP_NEEDS_POLL - || mmc->caps & MMC_CAP_NONREMOVABLE)) + if (_host->native_hotplug) pm_runtime_get_noresume(&pdev->dev); tmio_mmc_clk_stop(_host); @@ -996,9 +998,7 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host) * the controller, the runtime PM is suspended and pdata->power == false, * so, our .runtime_resume() will not try to detect a card in the slot. */ - if (host->pdata->flags & TMIO_MMC_HAS_COLD_CD - || host->mmc->caps & MMC_CAP_NEEDS_POLL - || host->mmc->caps & MMC_CAP_NONREMOVABLE) + if (!host->native_hotplug) pm_runtime_get_sync(&pdev->dev); dev_pm_qos_hide_latency_limit(&pdev->dev); -- cgit v1.2.3 From c8be24c2afd3ed2445bbf8f542af35a9787fc0e8 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 9 Feb 2012 22:57:09 +0100 Subject: mmc: tmio_mmc: support the generic MMC GPIO card hotplug helper If the platform specifies the TMIO_MMC_HAS_COLD_CD flag, use the generic MMC GPIO card hotplug helper. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/tmio_mmc.h | 4 --- drivers/mmc/host/tmio_mmc_pio.c | 69 ++++++++++++++++++----------------------- include/linux/mfd/tmio.h | 25 +++++++++++---- 3 files changed, 50 insertions(+), 48 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index 8531d8d44fc1..ede2f4e5b952 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -53,10 +53,6 @@ struct tmio_mmc_host { void (*set_pwr)(struct platform_device *host, int state); void (*set_clk_div)(struct platform_device *host, int state); - int pm_error; - /* recognise system-wide suspend in runtime PM methods */ - bool pm_global; - /* pio related stuff */ struct scatterlist *sg_ptr; struct scatterlist *sg_orig; diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 49f7f218cee1..42970ab6e803 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -791,8 +792,10 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) spin_unlock_irqrestore(&host->lock, flags); /* - * pdata->power == false only if COLD_CD is available, otherwise only - * in short time intervals during probing or resuming + * pdata->power toggles between false and true in both cases - either + * or not the controller can be runtime-suspended during inactivity. + * But if the controller has to be kept on, the runtime-pm usage_count + * is kept positive, so no suspending actually takes place. */ if (ios->power_mode == MMC_POWER_ON && ios->clock) { if (!pdata->power) { @@ -916,7 +919,7 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host, else mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; - _host->native_hotplug = !(pdata->flags & TMIO_MMC_HAS_COLD_CD || + _host->native_hotplug = !(pdata->flags & TMIO_MMC_USE_GPIO_CD || mmc->caps & MMC_CAP_NEEDS_POLL || mmc->caps & MMC_CAP_NONREMOVABLE); @@ -933,8 +936,9 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host, * 3) a worker thread polls the sdhi - indicated by MMC_CAP_NEEDS_POLL * 4) the medium is non-removable - indicated by MMC_CAP_NONREMOVABLE * - * While we increment the rtpm counter for all scenarios when the mmc - * core activates us by calling an appropriate set_ios(), we must + * While we increment the runtime PM counter for all scenarios when + * the mmc core activates us by calling an appropriate set_ios(), we + * must additionally ensure that in case 2) the tmio mmc hardware stays * additionally ensure that in case 2) the tmio mmc hardware stays * powered on during runtime for the card detection to work. */ @@ -973,6 +977,14 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host, tmio_mmc_enable_mmc_irqs(_host, irq_mask); + if (pdata->flags & TMIO_MMC_USE_GPIO_CD) { + ret = mmc_cd_gpio_request(mmc, pdata->cd_gpio); + if (ret < 0) { + tmio_mmc_host_remove(_host); + return ret; + } + } + *host = _host; return 0; @@ -990,20 +1002,22 @@ EXPORT_SYMBOL(tmio_mmc_host_probe); void tmio_mmc_host_remove(struct tmio_mmc_host *host) { struct platform_device *pdev = host->pdev; + struct tmio_mmc_data *pdata = host->pdata; + struct mmc_host *mmc = host->mmc; + + if (pdata->flags & TMIO_MMC_USE_GPIO_CD) + /* + * This means we can miss a card-eject, but this is anyway + * possible, because of delayed processing of hotplug events. + */ + mmc_cd_gpio_free(mmc); - /* - * We don't have to manipulate pdata->power here: if there is a card in - * the slot, the runtime PM is active and our .runtime_resume() will not - * be run. If there is no card in the slot and the platform can suspend - * the controller, the runtime PM is suspended and pdata->power == false, - * so, our .runtime_resume() will not try to detect a card in the slot. - */ if (!host->native_hotplug) pm_runtime_get_sync(&pdev->dev); dev_pm_qos_hide_latency_limit(&pdev->dev); - mmc_remove_host(host->mmc); + mmc_remove_host(mmc); cancel_work_sync(&host->done); cancel_delayed_work_sync(&host->delayed_reset_work); tmio_mmc_release_dma(host); @@ -1012,7 +1026,7 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host) pm_runtime_disable(&pdev->dev); iounmap(host->ctl); - mmc_free_host(host->mmc); + mmc_free_host(mmc); } EXPORT_SYMBOL(tmio_mmc_host_remove); @@ -1026,8 +1040,6 @@ int tmio_mmc_host_suspend(struct device *dev) if (!ret) tmio_mmc_disable_mmc_irqs(host, TMIO_MASK_ALL); - host->pm_error = pm_runtime_put_sync(dev); - return ret; } EXPORT_SYMBOL(tmio_mmc_host_suspend); @@ -1037,20 +1049,10 @@ int tmio_mmc_host_resume(struct device *dev) struct mmc_host *mmc = dev_get_drvdata(dev); struct tmio_mmc_host *host = mmc_priv(mmc); - /* The MMC core will perform the complete set up */ - host->pdata->power = false; - - host->pm_global = true; - if (!host->pm_error) - pm_runtime_get_sync(dev); - - if (host->pm_global) { - /* Runtime PM resume callback didn't run */ - tmio_mmc_reset(host); - tmio_mmc_enable_dma(host, true); - host->pm_global = false; - } + tmio_mmc_reset(host); + tmio_mmc_enable_dma(host, true); + /* The MMC core will perform the complete set up */ return mmc_resume_host(mmc); } EXPORT_SYMBOL(tmio_mmc_host_resume); @@ -1067,19 +1069,10 @@ int tmio_mmc_host_runtime_resume(struct device *dev) { struct mmc_host *mmc = dev_get_drvdata(dev); struct tmio_mmc_host *host = mmc_priv(mmc); - struct tmio_mmc_data *pdata = host->pdata; tmio_mmc_reset(host); tmio_mmc_enable_dma(host, true); - if (pdata->power) { - /* Only entered after a card-insert interrupt */ - if (!mmc->card) - tmio_mmc_set_ios(mmc, &mmc->ios); - mmc_detect_change(mmc, msecs_to_jiffies(100)); - } - host->pm_global = false; - return 0; } EXPORT_SYMBOL(tmio_mmc_host_runtime_resume); diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h index 0dc98044d8b7..5a197de4aac9 100644 --- a/include/linux/mfd/tmio.h +++ b/include/linux/mfd/tmio.h @@ -1,8 +1,10 @@ #ifndef MFD_TMIO_H #define MFD_TMIO_H +#include #include #include +#include #include #include @@ -64,8 +66,8 @@ #define TMIO_MMC_SDIO_IRQ (1 << 2) /* * Some platforms can detect card insertion events with controller powered - * down, in which case they have to call tmio_mmc_cd_wakeup() to power up the - * controller and report the event to the driver. + * down, using a GPIO IRQ, in which case they have to fill in cd_irq, cd_gpio, + * and cd_flags fields of struct tmio_mmc_data. */ #define TMIO_MMC_HAS_COLD_CD (1 << 3) /* @@ -73,6 +75,12 @@ * idle before writing to some registers. */ #define TMIO_MMC_HAS_IDLE_WAIT (1 << 4) +/* + * A GPIO is used for card hotplug detection. We need an extra flag for this, + * because 0 is a valid GPIO number too, and requiring users to specify + * cd_gpio < 0 to disable GPIO hotplug would break backwards compatibility. + */ +#define TMIO_MMC_USE_GPIO_CD (1 << 5) int tmio_core_mmc_enable(void __iomem *cnf, int shift, unsigned long base); int tmio_core_mmc_resume(void __iomem *cnf, int shift, unsigned long base); @@ -98,18 +106,23 @@ struct tmio_mmc_data { struct tmio_mmc_dma *dma; struct device *dev; bool power; + unsigned int cd_gpio; void (*set_pwr)(struct platform_device *host, int state); void (*set_clk_div)(struct platform_device *host, int state); int (*get_cd)(struct platform_device *host); int (*write16_hook)(struct tmio_mmc_host *host, int addr); }; +/* + * This function is deprecated and will be removed soon. Please, convert your + * platform to use drivers/mmc/core/cd-gpio.c + */ +#include static inline void tmio_mmc_cd_wakeup(struct tmio_mmc_data *pdata) { - if (pdata && !pdata->power) { - pdata->power = true; - pm_runtime_get(pdata->dev); - } + if (pdata) + mmc_detect_change(dev_get_drvdata(pdata->dev), + msecs_to_jiffies(100)); } /* -- cgit v1.2.3 From 58126c878b4a4f658015e383614bafb6331e46d3 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 9 Feb 2012 22:57:10 +0100 Subject: mmc: sh_mobile_sdhi: pass card hotplug GPIO number to TMIO MMC To use TMIO MMC driver ability to interface to the generic MMC GPIO card hotplug detection helper, the SDHI driver has to pass the GPIO number from its own platform data. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mobile_sdhi.c | 3 ++- include/linux/mmc/sh_mobile_sdhi.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index 58da3c44acc5..cb279b4e501b 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -129,6 +129,7 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev) mmc_data->write16_hook = sh_mobile_sdhi_write16_hook; mmc_data->ocr_mask = p->tmio_ocr_mask; mmc_data->capabilities |= p->tmio_caps; + mmc_data->cd_gpio = p->cd_gpio; if (p->dma_slave_tx > 0 && p->dma_slave_rx > 0) { priv->param_tx.slave_id = p->dma_slave_tx; @@ -211,7 +212,7 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev) dev_info(&pdev->dev, "%s base at 0x%08lx clock rate %u MHz\n", mmc_hostname(host->mmc), (unsigned long) - (platform_get_resource(pdev,IORESOURCE_MEM, 0)->start), + (platform_get_resource(pdev, IORESOURCE_MEM, 0)->start), mmc_data->hclk / 1000000); return ret; diff --git a/include/linux/mmc/sh_mobile_sdhi.h b/include/linux/mmc/sh_mobile_sdhi.h index 71b805451bd8..082a736a9bfb 100644 --- a/include/linux/mmc/sh_mobile_sdhi.h +++ b/include/linux/mmc/sh_mobile_sdhi.h @@ -16,6 +16,7 @@ struct sh_mobile_sdhi_info { unsigned long tmio_flags; unsigned long tmio_caps; u32 tmio_ocr_mask; /* available MMC voltages */ + unsigned int cd_gpio; struct tmio_mmc_data *pdata; void (*set_pwr)(struct platform_device *pdev, int state); int (*get_cd)(struct platform_device *pdev); -- cgit v1.2.3 From c391e1b9ebfe31514fa95a0cdd30c2cbc9652c89 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 9 Feb 2012 22:57:13 +0100 Subject: mmc: tmio_mmc: power status flag doesn't have to be exposed in platform data The controller power status flag does not have to be accessed from the hot-plug detection code any more, it can now be removed from the platform data and put in the controller private struct. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/tmio_mmc.h | 3 +++ drivers/mmc/host/tmio_mmc_pio.c | 13 ++++++------- include/linux/mfd/tmio.h | 1 - 3 files changed, 9 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index ede2f4e5b952..edfcfd767511 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -49,6 +49,9 @@ struct tmio_mmc_host { struct mmc_host *mmc; unsigned int sdio_irq_enabled; + /* Controller power state */ + bool power; + /* Callbacks for clock / power control */ void (*set_pwr)(struct platform_device *host, int state); void (*set_clk_div)(struct platform_device *host, int state); diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 42970ab6e803..9f22681a37bd 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -762,7 +762,6 @@ fail: static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct tmio_mmc_host *host = mmc_priv(mmc); - struct tmio_mmc_data *pdata = host->pdata; unsigned long flags; mutex_lock(&host->ios_lock); @@ -792,15 +791,15 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) spin_unlock_irqrestore(&host->lock, flags); /* - * pdata->power toggles between false and true in both cases - either + * host->power toggles between false and true in both cases - either * or not the controller can be runtime-suspended during inactivity. * But if the controller has to be kept on, the runtime-pm usage_count * is kept positive, so no suspending actually takes place. */ if (ios->power_mode == MMC_POWER_ON && ios->clock) { - if (!pdata->power) { + if (!host->power) { pm_runtime_get_sync(&host->pdev->dev); - pdata->power = true; + host->power = true; } tmio_mmc_set_clock(host, ios->clock); /* power up SD bus */ @@ -811,8 +810,8 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) } else if (ios->power_mode != MMC_POWER_UP) { if (host->set_pwr && ios->power_mode == MMC_POWER_OFF) host->set_pwr(host->pdev, 0); - if (pdata->power) { - pdata->power = false; + if (host->power) { + host->power = false; pm_runtime_put(&host->pdev->dev); } tmio_mmc_clk_stop(host); @@ -923,7 +922,7 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host, mmc->caps & MMC_CAP_NEEDS_POLL || mmc->caps & MMC_CAP_NONREMOVABLE); - pdata->power = false; + _host->power = false; pm_runtime_enable(&pdev->dev); ret = pm_runtime_resume(&pdev->dev); if (ret < 0) diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h index 5a197de4aac9..f5171dbf8850 100644 --- a/include/linux/mfd/tmio.h +++ b/include/linux/mfd/tmio.h @@ -105,7 +105,6 @@ struct tmio_mmc_data { u32 ocr_mask; /* available voltages */ struct tmio_mmc_dma *dma; struct device *dev; - bool power; unsigned int cd_gpio; void (*set_pwr)(struct platform_device *host, int state); void (*set_clk_div)(struct platform_device *host, int state); -- cgit v1.2.3 From 5bd99c375e141b8dd12e18c45617158f98920e57 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 9 Feb 2012 22:57:14 +0100 Subject: mmc: tmio_mmc: remove unused sdio_irq_enabled flag The sdio_irq_enabled member of struct tmio_mmc_host is a left-over from the previously removed SDIO IRQ workaround. It is no longer needed and can now be removed too. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/tmio_mmc.h | 1 - drivers/mmc/host/tmio_mmc_pio.c | 2 -- 2 files changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index edfcfd767511..d857f5c6e7d9 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -47,7 +47,6 @@ struct tmio_mmc_host { struct mmc_request *mrq; struct mmc_data *data; struct mmc_host *mmc; - unsigned int sdio_irq_enabled; /* Controller power state */ bool power; diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 9f22681a37bd..950824a00717 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -128,7 +128,6 @@ static void tmio_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) struct tmio_mmc_host *host = mmc_priv(mmc); if (enable) { - host->sdio_irq_enabled = 1; host->sdio_irq_mask = TMIO_SDIO_MASK_ALL & ~TMIO_SDIO_STAT_IOIRQ; sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0001); @@ -137,7 +136,6 @@ static void tmio_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) host->sdio_irq_mask = TMIO_SDIO_MASK_ALL; sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, host->sdio_irq_mask); sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0000); - host->sdio_irq_enabled = 0; } } -- cgit v1.2.3 From 996bc8aebd2cd5b6d4c5d85085f171fa2447f364 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 9 Feb 2012 22:57:15 +0100 Subject: mmc: sh_mobile_sdhi: do not manage PM clocks manually On sh-mobile platforms the MMC clock frequency for the TMIO MMC unit is obtained from the same clock, as the one, that runtime power-manages the controller. The SDHI glue code has to access that clock directly, bypassing the runtime PM framework, to get its frequency, but it shouldn't enable or disable it. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mobile_sdhi.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index cb279b4e501b..f91c3aa40d1d 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -117,8 +117,6 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev) goto eclkget; } - clk_enable(priv->clk); - mmc_data->hclk = clk_get_rate(priv->clk); mmc_data->set_pwr = sh_mobile_sdhi_set_pwr; mmc_data->get_cd = sh_mobile_sdhi_get_cd; @@ -233,7 +231,6 @@ eirq_sdio: eirq_card_detect: tmio_mmc_host_remove(host); eprobe: - clk_disable(priv->clk); clk_put(priv->clk); eclkget: kfree(priv); @@ -259,7 +256,6 @@ static int sh_mobile_sdhi_remove(struct platform_device *pdev) free_irq(irq, host); } - clk_disable(priv->clk); clk_put(priv->clk); kfree(priv); -- cgit v1.2.3 From 4932bd64a2bb4e80b79efb20c4736ac3b30ba7fe Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 9 Feb 2012 22:57:16 +0100 Subject: mmc: tmio: cosmetic: prettify the tmio_mmc_set_ios() function Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/tmio_mmc_pio.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 950824a00717..9a7996ade58e 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -760,6 +760,7 @@ fail: static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct tmio_mmc_host *host = mmc_priv(mmc); + struct device *dev = &host->pdev->dev; unsigned long flags; mutex_lock(&host->ios_lock); @@ -767,13 +768,13 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) spin_lock_irqsave(&host->lock, flags); if (host->mrq) { if (IS_ERR(host->mrq)) { - dev_dbg(&host->pdev->dev, + dev_dbg(dev, "%s.%d: concurrent .set_ios(), clk %u, mode %u\n", current->comm, task_pid_nr(current), ios->clock, ios->power_mode); host->mrq = ERR_PTR(-EINTR); } else { - dev_dbg(&host->pdev->dev, + dev_dbg(dev, "%s.%d: CMD%u active since %lu, now %lu!\n", current->comm, task_pid_nr(current), host->mrq->cmd->opcode, host->last_req_ts, jiffies); @@ -796,7 +797,7 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) */ if (ios->power_mode == MMC_POWER_ON && ios->clock) { if (!host->power) { - pm_runtime_get_sync(&host->pdev->dev); + pm_runtime_get_sync(dev); host->power = true; } tmio_mmc_set_clock(host, ios->clock); @@ -810,7 +811,7 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) host->set_pwr(host->pdev, 0); if (host->power) { host->power = false; - pm_runtime_put(&host->pdev->dev); + pm_runtime_put(dev); } tmio_mmc_clk_stop(host); } -- cgit v1.2.3 From e82b4ac94e5c43fad51b975aed058858ceda1f0f Mon Sep 17 00:00:00 2001 From: Bastian Hecht Date: Fri, 16 Mar 2012 12:19:29 -0400 Subject: mmc: sh_mobile_sdhi: add a callback for board specific init code Some boards need a preliminary setup stage to prepare the sdhi controller. Signed-off-by: Bastian Hecht Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mobile_sdhi.c | 13 +++++++++++++ include/linux/mmc/sh_mobile_sdhi.h | 4 ++++ 2 files changed, 17 insertions(+) (limited to 'drivers') diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index f91c3aa40d1d..07ff3fee77b3 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -109,6 +109,12 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev) mmc_data = &priv->mmc_data; p->pdata = mmc_data; + if (p->init) { + ret = p->init(pdev); + if (ret) + goto einit; + } + snprintf(clk_name, sizeof(clk_name), "sdhi%d", pdev->id); priv->clk = clk_get(&pdev->dev, clk_name); if (IS_ERR(priv->clk)) { @@ -233,6 +239,9 @@ eirq_card_detect: eprobe: clk_put(priv->clk); eclkget: + if (p->cleanup) + p->cleanup(pdev); +einit: kfree(priv); return ret; } @@ -257,6 +266,10 @@ static int sh_mobile_sdhi_remove(struct platform_device *pdev) } clk_put(priv->clk); + + if (p->cleanup) + p->cleanup(pdev); + kfree(priv); return 0; diff --git a/include/linux/mmc/sh_mobile_sdhi.h b/include/linux/mmc/sh_mobile_sdhi.h index 082a736a9bfb..686b85bfb7a5 100644 --- a/include/linux/mmc/sh_mobile_sdhi.h +++ b/include/linux/mmc/sh_mobile_sdhi.h @@ -20,6 +20,10 @@ struct sh_mobile_sdhi_info { struct tmio_mmc_data *pdata; void (*set_pwr)(struct platform_device *pdev, int state); int (*get_cd)(struct platform_device *pdev); + + /* callbacks for board specific setup code */ + int (*init)(struct platform_device *pdev); + void (*cleanup)(struct platform_device *pdev); }; #endif /* LINUX_MMC_SH_MOBILE_SDHI_H */ -- cgit v1.2.3 From 7f524217439cc17da74523582c303cced432713e Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 9 Mar 2012 10:16:00 +0100 Subject: mmc: sh_mobile_sdhi: support modular mmc-core with non-standard hotplug Currently if a platform wants to implement a non-standard card-detection method, it would need to call tmio_mmc_cd_wakeup(), which is an inline function, calling mmc_detect_change(). For this the platform would have to link mmc_core statically into the kernel, losing the ability to build it as a module. This patch adds a callback to the sh_mobile_sdhi driver, which eliminates this dependency. Signed-off-by: Guennadi Liakhovetski Acked-by: Magnus Damm Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mobile_sdhi.c | 11 ++++++++++- include/linux/mmc/sh_mobile_sdhi.h | 11 ++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index 07ff3fee77b3..934b68e9efc3 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -90,6 +90,15 @@ static int sh_mobile_sdhi_write16_hook(struct tmio_mmc_host *host, int addr) return 0; } +static void sh_mobile_sdhi_cd_wakeup(const struct platform_device *pdev) +{ + mmc_detect_change(dev_get_drvdata(&pdev->dev), msecs_to_jiffies(100)); +} + +static const struct sh_mobile_sdhi_ops sdhi_ops = { + .cd_wakeup = sh_mobile_sdhi_cd_wakeup, +}; + static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev) { struct sh_mobile_sdhi *priv; @@ -110,7 +119,7 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev) p->pdata = mmc_data; if (p->init) { - ret = p->init(pdev); + ret = p->init(pdev, &sdhi_ops); if (ret) goto einit; } diff --git a/include/linux/mmc/sh_mobile_sdhi.h b/include/linux/mmc/sh_mobile_sdhi.h index 686b85bfb7a5..e94e620aeddc 100644 --- a/include/linux/mmc/sh_mobile_sdhi.h +++ b/include/linux/mmc/sh_mobile_sdhi.h @@ -10,6 +10,14 @@ struct tmio_mmc_data; #define SH_MOBILE_SDHI_IRQ_SDCARD "sdcard" #define SH_MOBILE_SDHI_IRQ_SDIO "sdio" +/** + * struct sh_mobile_sdhi_ops - SDHI driver callbacks + * @cd_wakeup: trigger a card-detection run + */ +struct sh_mobile_sdhi_ops { + void (*cd_wakeup)(const struct platform_device *pdev); +}; + struct sh_mobile_sdhi_info { int dma_slave_tx; int dma_slave_rx; @@ -22,7 +30,8 @@ struct sh_mobile_sdhi_info { int (*get_cd)(struct platform_device *pdev); /* callbacks for board specific setup code */ - int (*init)(struct platform_device *pdev); + int (*init)(struct platform_device *pdev, + const struct sh_mobile_sdhi_ops *ops); void (*cleanup)(struct platform_device *pdev); }; -- cgit v1.2.3