From 0f506a96693d8ad4aeccbd98dbd54a3c7357cfa8 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 20 Jun 2012 19:10:35 +0200 Subject: [PATCH 01/62] mmc: tmio: use MMC opcode defines instead of numbers mmc.h defines macros for most frequently used MMC opcodes. Use them instead of hard-coded numbers. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/tmio_mmc_pio.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 9a7996ade58e..0ad3917cafd1 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -305,8 +306,8 @@ static int tmio_mmc_start_command(struct tmio_mmc_host *host, struct mmc_command int c = cmd->opcode; u32 irq_mask = TMIO_MASK_CMD; - /* Command 12 is handled by hardware */ - if (cmd->opcode == 12 && !cmd->arg) { + /* CMD12 is handled by hardware */ + if (cmd->opcode == MMC_STOP_TRANSMISSION && !cmd->arg) { sd_ctrl_write16(host, CTL_STOP_INTERNAL_ACTION, 0x001); return 0; } @@ -449,7 +450,7 @@ void tmio_mmc_do_data_irq(struct tmio_mmc_host *host) } if (stop) { - if (stop->opcode == 12 && !stop->arg) + if (stop->opcode == MMC_STOP_TRANSMISSION && !stop->arg) sd_ctrl_write16(host, CTL_STOP_INTERNAL_ACTION, 0x000); else BUG(); From fd0ea65d3e675e479e022b6cfc9ebe1864c76afc Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 30 Apr 2012 23:31:57 +0200 Subject: [PATCH 02/62] mmc: extend and rename cd-gpio helpers to handle more slot GPIO functions GPIOs can be used in MMC/SD-card slots not only for hotplug detection, but also to implement the write-protection pin. Rename cd-gpio helpers to slot-gpio to make addition of further slot GPIO functions possible. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/core/Makefile | 2 +- drivers/mmc/core/{cd-gpio.c => slot-gpio.c} | 48 ++++++++++---------- drivers/mmc/host/tmio_mmc_pio.c | 6 +-- include/linux/mmc/{cd-gpio.h => slot-gpio.h} | 8 ++-- 4 files changed, 32 insertions(+), 32 deletions(-) rename drivers/mmc/core/{cd-gpio.c => slot-gpio.c} (53%) rename include/linux/mmc/{cd-gpio.h => slot-gpio.h} (68%) diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile index dca4428380f1..38ed210ce2f3 100644 --- a/drivers/mmc/core/Makefile +++ b/drivers/mmc/core/Makefile @@ -7,6 +7,6 @@ mmc_core-y := core.o bus.o host.o \ mmc.o mmc_ops.o sd.o sd_ops.o \ sdio.o sdio_ops.o sdio_bus.o \ sdio_cis.o sdio_io.o sdio_irq.o \ - quirks.o cd-gpio.o + quirks.o slot-gpio.o mmc_core-$(CONFIG_DEBUG_FS) += debugfs.o diff --git a/drivers/mmc/core/cd-gpio.c b/drivers/mmc/core/slot-gpio.c similarity index 53% rename from drivers/mmc/core/cd-gpio.c rename to drivers/mmc/core/slot-gpio.c index 8f5dc08d6598..979671053436 100644 --- a/drivers/mmc/core/cd-gpio.c +++ b/drivers/mmc/core/slot-gpio.c @@ -12,72 +12,72 @@ #include #include #include -#include #include +#include #include #include -struct mmc_cd_gpio { - unsigned int gpio; - char label[0]; +struct mmc_gpio { + unsigned int cd_gpio; + char cd_label[0]; }; -static irqreturn_t mmc_cd_gpio_irqt(int irq, void *dev_id) +static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id) { /* Schedule a card detection after a debounce timeout */ mmc_detect_change(dev_id, msecs_to_jiffies(100)); return IRQ_HANDLED; } -int mmc_cd_gpio_request(struct mmc_host *host, unsigned int gpio) +int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio) { size_t len = strlen(dev_name(host->parent)) + 4; - struct mmc_cd_gpio *cd; + struct mmc_gpio *ctx; int irq = gpio_to_irq(gpio); int ret; if (irq < 0) return irq; - cd = kmalloc(sizeof(*cd) + len, GFP_KERNEL); - if (!cd) + ctx = kmalloc(sizeof(*ctx) + len, GFP_KERNEL); + if (!ctx) return -ENOMEM; - snprintf(cd->label, len, "%s cd", dev_name(host->parent)); + snprintf(ctx->cd_label, len, "%s cd", dev_name(host->parent)); - ret = gpio_request_one(gpio, GPIOF_DIR_IN, cd->label); + ret = gpio_request_one(gpio, GPIOF_DIR_IN, ctx->cd_label); if (ret < 0) goto egpioreq; - ret = request_threaded_irq(irq, NULL, mmc_cd_gpio_irqt, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | - IRQF_ONESHOT, cd->label, host); + ret = request_threaded_irq(irq, NULL, mmc_gpio_cd_irqt, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + ctx->cd_label, host); if (ret < 0) goto eirqreq; - cd->gpio = gpio; + ctx->cd_gpio = gpio; host->hotplug.irq = irq; - host->hotplug.handler_priv = cd; + host->hotplug.handler_priv = ctx; return 0; eirqreq: gpio_free(gpio); egpioreq: - kfree(cd); + kfree(ctx); return ret; } -EXPORT_SYMBOL(mmc_cd_gpio_request); +EXPORT_SYMBOL(mmc_gpio_request_cd); -void mmc_cd_gpio_free(struct mmc_host *host) +void mmc_gpio_free_cd(struct mmc_host *host) { - struct mmc_cd_gpio *cd = host->hotplug.handler_priv; + struct mmc_gpio *ctx = host->hotplug.handler_priv; - if (!cd) + if (!ctx) return; free_irq(host->hotplug.irq, host); - gpio_free(cd->gpio); - kfree(cd); + gpio_free(ctx->cd_gpio); + kfree(ctx); } -EXPORT_SYMBOL(mmc_cd_gpio_free); +EXPORT_SYMBOL(mmc_gpio_free_cd); diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 0ad3917cafd1..7ffc489bed35 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -34,9 +34,9 @@ #include #include #include -#include #include #include +#include #include #include #include @@ -977,7 +977,7 @@ 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); + ret = mmc_gpio_request_cd(mmc, pdata->cd_gpio); if (ret < 0) { tmio_mmc_host_remove(_host); return ret; @@ -1009,7 +1009,7 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host) * 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); + mmc_gpio_free_cd(mmc); if (!host->native_hotplug) pm_runtime_get_sync(&pdev->dev); diff --git a/include/linux/mmc/cd-gpio.h b/include/linux/mmc/slot-gpio.h similarity index 68% rename from include/linux/mmc/cd-gpio.h rename to include/linux/mmc/slot-gpio.h index cefaba038ccb..edfaa3254373 100644 --- a/include/linux/mmc/cd-gpio.h +++ b/include/linux/mmc/slot-gpio.h @@ -8,11 +8,11 @@ * published by the Free Software Foundation. */ -#ifndef MMC_CD_GPIO_H -#define MMC_CD_GPIO_H +#ifndef MMC_SLOT_GPIO_H +#define MMC_SLOT_GPIO_H struct mmc_host; -int mmc_cd_gpio_request(struct mmc_host *host, unsigned int gpio); -void mmc_cd_gpio_free(struct mmc_host *host); +int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio); +void mmc_gpio_free_cd(struct mmc_host *host); #endif From 6af9e96e052a6d1a760c60cb340c5a6584cb92db Mon Sep 17 00:00:00 2001 From: Venkatraman S Date: Fri, 22 Jun 2012 11:42:36 +0530 Subject: [PATCH 03/62] mmc: core: Fix the HPI execution sequence mmc_execute_hpi should send the HPI command only once, and only if the card is in PRG state. According to eMMC spec, the command's completion time is not dependent on OUT_OF_INTERRUPT_TIME. Only the transition out of PRG STATE is guarded by OUT_OF_INTERRUPT_TIME - which is defined to begin at the end of sending the command itself. Specify the default timeout for the actual sending of HPI command, and then use OUT_OF_INTERRUPT_TIME to wait for the transition out of PRG state. Reported-by: Alex Lemberg Signed-off-by: Venkatraman S Reviewed-by: Namjae Jeon Reviewed-by: Subhash Jadavani Acked-by: Jaehoon Chung Signed-off-by: Chris Ball --- drivers/mmc/core/core.c | 55 ++++++++++++++++++++++---------------- drivers/mmc/core/mmc_ops.c | 1 - 2 files changed, 32 insertions(+), 24 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 0b6141d29dbd..a7e2c4b2f18c 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -404,6 +404,7 @@ int mmc_interrupt_hpi(struct mmc_card *card) { int err; u32 status; + unsigned long prg_wait; BUG_ON(!card); @@ -419,30 +420,38 @@ int mmc_interrupt_hpi(struct mmc_card *card) goto out; } - /* - * If the card status is in PRG-state, we can send the HPI command. - */ - if (R1_CURRENT_STATE(status) == R1_STATE_PRG) { - do { - /* - * We don't know when the HPI command will finish - * processing, so we need to resend HPI until out - * of prg-state, and keep checking the card status - * with SEND_STATUS. If a timeout error occurs when - * sending the HPI command, we are already out of - * prg-state. - */ - err = mmc_send_hpi_cmd(card, &status); - if (err) - pr_debug("%s: abort HPI (%d error)\n", - mmc_hostname(card->host), err); + switch (R1_CURRENT_STATE(status)) { + case R1_STATE_IDLE: + case R1_STATE_READY: + case R1_STATE_STBY: + /* + * In idle states, HPI is not needed and the caller + * can issue the next intended command immediately + */ + goto out; + case R1_STATE_PRG: + break; + default: + /* In all other states, it's illegal to issue HPI */ + pr_debug("%s: HPI cannot be sent. Card state=%d\n", + mmc_hostname(card->host), R1_CURRENT_STATE(status)); + err = -EINVAL; + goto out; + } - err = mmc_send_status(card, &status); - if (err) - break; - } while (R1_CURRENT_STATE(status) == R1_STATE_PRG); - } else - pr_debug("%s: Left prg-state\n", mmc_hostname(card->host)); + err = mmc_send_hpi_cmd(card, &status); + if (err) + goto out; + + prg_wait = jiffies + msecs_to_jiffies(card->ext_csd.out_of_int_time); + do { + err = mmc_send_status(card, &status); + + if (!err && R1_CURRENT_STATE(status) == R1_STATE_TRAN) + break; + if (time_after(jiffies, prg_wait)) + err = -ETIMEDOUT; + } while (!err); out: mmc_release_host(card->host); diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 69370f494e05..0ed2cc5f35b6 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -569,7 +569,6 @@ int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status) cmd.opcode = opcode; cmd.arg = card->rca << 16 | 1; - cmd.cmd_timeout_ms = card->ext_csd.out_of_int_time; err = mmc_wait_for_cmd(card->host, &cmd, 0); if (err) { From ecf8b5d0a378a0f922ffca27bd0a9101f246fa11 Mon Sep 17 00:00:00 2001 From: Subhash Jadavani Date: Thu, 7 Jun 2012 15:46:58 +0530 Subject: [PATCH 04/62] mmc: block: replace __blk_end_request() with blk_end_request() For completing any block request, MMC block driver is calling: spin_lock_irq(queue) __blk_end_request() spin_unlock_irq(queue) But if we analyze the sources of latency in kernel using ftrace, __blk_end_request() function at times may take up to 6.5ms with spinlock held and irq disabled. __blk_end_request() calls couple of functions and ftrace output shows that blk_update_bidi_request() function is almost taking 6ms. There are 2 function to end the current request: ___blk_end_request() and blk_end_request(). Both these functions do same thing except that blk_end_request() function doesn't take up the spinlock while calling the blk_update_bidi_request(). This patch replaces all __blk_end_request() calls with blk_end_request() and __blk_end_request_all() calls with blk_end_request_all(). Testing done: 20 process concurrent read/write on sd card and eMMC. Ran this test for almost a day on multicore system and no errors observed. This change is not meant for improving MMC throughput; it's basically about becoming fair to other threads/interrupts in the system. By holding spin lock and interrupts disabled for longer duration, we won't allow other threads/interrupts to run at all. Actually slight performance degradation at file system level can be expected as we are not holding the spin lock during blk_update_bidi_request() which means our mmcqd thread may get preempted for other high priority thread or any interrupt in the system. These are performance numbers (100MB file write) with eMMC running in DDR mode: Without this patch: Name of the Test Value Unit LMDD Read Test 53.79 MBPS LMDD Write Test 18.86 MBPS IOZONE Read Test 51.65 MBPS IOZONE Write Test 24.36 MBPS With this patch: Name of the Test Value Unit LMDD Read Test 52.94 MBPS LMDD Write Test 16.70 MBPS IOZONE Read Test 52.08 MBPS IOZONE Write Test 23.29 MBPS Read numbers are fine. Write numbers are bit down (especially LMDD write), may be because write requests normally have large transfer size and which means there are chances that while mmcq is executing blk_update_bidi_request(), it may get interrupted by interrupts or other high priority thread. Signed-off-by: Subhash Jadavani Reviewed-by: Namjae Jeon Signed-off-by: Chris Ball --- drivers/mmc/card/block.c | 36 +++++++++--------------------------- 1 file changed, 9 insertions(+), 27 deletions(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 276d21ce6bc1..f1c84decb192 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -850,9 +850,7 @@ out: goto retry; if (!err) mmc_blk_reset_success(md, type); - spin_lock_irq(&md->lock); - __blk_end_request(req, err, blk_rq_bytes(req)); - spin_unlock_irq(&md->lock); + blk_end_request(req, err, blk_rq_bytes(req)); return err ? 0 : 1; } @@ -934,9 +932,7 @@ out_retry: if (!err) mmc_blk_reset_success(md, type); out: - spin_lock_irq(&md->lock); - __blk_end_request(req, err, blk_rq_bytes(req)); - spin_unlock_irq(&md->lock); + blk_end_request(req, err, blk_rq_bytes(req)); return err ? 0 : 1; } @@ -951,9 +947,7 @@ static int mmc_blk_issue_flush(struct mmc_queue *mq, struct request *req) if (ret) ret = -EIO; - spin_lock_irq(&md->lock); - __blk_end_request_all(req, ret); - spin_unlock_irq(&md->lock); + blk_end_request_all(req, ret); return ret ? 0 : 1; } @@ -1252,14 +1246,10 @@ static int mmc_blk_cmd_err(struct mmc_blk_data *md, struct mmc_card *card, blocks = mmc_sd_num_wr_blocks(card); if (blocks != (u32)-1) { - spin_lock_irq(&md->lock); - ret = __blk_end_request(req, 0, blocks << 9); - spin_unlock_irq(&md->lock); + ret = blk_end_request(req, 0, blocks << 9); } } else { - spin_lock_irq(&md->lock); - ret = __blk_end_request(req, 0, brq->data.bytes_xfered); - spin_unlock_irq(&md->lock); + ret = blk_end_request(req, 0, brq->data.bytes_xfered); } return ret; } @@ -1311,10 +1301,8 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) * A block was successfully transferred. */ mmc_blk_reset_success(md, type); - spin_lock_irq(&md->lock); - ret = __blk_end_request(req, 0, + ret = blk_end_request(req, 0, brq->data.bytes_xfered); - spin_unlock_irq(&md->lock); /* * If the blk_end_request function returns non-zero even * though all data has been transferred and no errors @@ -1364,10 +1352,8 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) * time, so we only reach here after trying to * read a single sector. */ - spin_lock_irq(&md->lock); - ret = __blk_end_request(req, -EIO, + ret = blk_end_request(req, -EIO, brq->data.blksz); - spin_unlock_irq(&md->lock); if (!ret) goto start_new_req; break; @@ -1388,12 +1374,10 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) return 1; cmd_abort: - spin_lock_irq(&md->lock); if (mmc_card_removed(card)) req->cmd_flags |= REQ_QUIET; while (ret) - ret = __blk_end_request(req, -EIO, blk_rq_cur_bytes(req)); - spin_unlock_irq(&md->lock); + ret = blk_end_request(req, -EIO, blk_rq_cur_bytes(req)); start_new_req: if (rqc) { @@ -1417,9 +1401,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) ret = mmc_blk_part_switch(card, md); if (ret) { if (req) { - spin_lock_irq(&md->lock); - __blk_end_request_all(req, -EIO); - spin_unlock_irq(&md->lock); + blk_end_request_all(req, -EIO); } ret = 0; goto out; From 45a6b32e24003d9749d8c6d1c83dfb64d6994ca7 Mon Sep 17 00:00:00 2001 From: Chris Ball Date: Mon, 11 Jun 2012 09:39:12 -0400 Subject: [PATCH 05/62] mmc: core: Export regulator_* functions as GPL The regulator API functions we're wrapping are exported as GPL, so our wrappers for the same functions should be too. Reported-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/core/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index a7e2c4b2f18c..28b1ffaf0bd1 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -950,7 +950,7 @@ int mmc_regulator_get_ocrmask(struct regulator *supply) return result; } -EXPORT_SYMBOL(mmc_regulator_get_ocrmask); +EXPORT_SYMBOL_GPL(mmc_regulator_get_ocrmask); /** * mmc_regulator_set_ocr - set regulator to match host->ios voltage @@ -1020,7 +1020,7 @@ int mmc_regulator_set_ocr(struct mmc_host *mmc, "could not set regulator OCR (%d)\n", result); return result; } -EXPORT_SYMBOL(mmc_regulator_set_ocr); +EXPORT_SYMBOL_GPL(mmc_regulator_set_ocr); #endif /* CONFIG_REGULATOR */ From 4efafee02ff604627234d42a3ff9f331e3605c9d Mon Sep 17 00:00:00 2001 From: Chris Ball Date: Mon, 11 Jun 2012 21:48:16 -0400 Subject: [PATCH 06/62] mmc: dt: Deduplicate binding docs by referencing mmc.txt Now that we have common bindings for MMC, rewrite the individual bindings to inherit from mmc.txt and describe their differences. Acked-by: Stephen Warren Acked-by: Arnd Bergmann Signed-off-by: Chris Ball --- .../devicetree/bindings/mmc/fsl-esdhc.txt | 25 ++++++++++--------- .../devicetree/bindings/mmc/fsl-imx-esdhc.txt | 8 +++--- .../devicetree/bindings/mmc/mmc-spi-slot.txt | 8 +++--- .../devicetree/bindings/mmc/mmci.txt | 12 +++------ .../devicetree/bindings/mmc/mxs-mmc.txt | 8 +++--- .../devicetree/bindings/mmc/nvidia-sdhci.txt | 8 +++--- .../devicetree/bindings/mmc/ti-omap-hsmmc.txt | 7 +++--- 7 files changed, 33 insertions(+), 43 deletions(-) diff --git a/Documentation/devicetree/bindings/mmc/fsl-esdhc.txt b/Documentation/devicetree/bindings/mmc/fsl-esdhc.txt index 0d93b4b0e0e3..bd9be0b5bc20 100644 --- a/Documentation/devicetree/bindings/mmc/fsl-esdhc.txt +++ b/Documentation/devicetree/bindings/mmc/fsl-esdhc.txt @@ -3,21 +3,22 @@ The Enhanced Secure Digital Host Controller provides an interface for MMC, SD, and SDIO types of memory cards. +This file documents differences between the core properties described +by mmc.txt and the properties used by the sdhci-esdhc driver. + Required properties: - - compatible : should be - "fsl,-esdhc", "fsl,esdhc" - - reg : should contain eSDHC registers location and length. - - interrupts : should contain eSDHC interrupt. - interrupt-parent : interrupt source phandle. - clock-frequency : specifies eSDHC base clock frequency. - - sdhci,wp-inverted : (optional) specifies that eSDHC controller - reports inverted write-protect state; New devices should use - the generic "wp-inverted" property. - - sdhci,1-bit-only : (optional) specifies that a controller can - only handle 1-bit data transfers. New devices should use the - generic "bus-width = <1>" property. - - sdhci,auto-cmd12: (optional) specifies that a controller can - only handle auto CMD12. + +Optional properties: + - sdhci,wp-inverted : specifies that eSDHC controller reports + inverted write-protect state; New devices should use the generic + "wp-inverted" property. + - sdhci,1-bit-only : specifies that a controller can only handle + 1-bit data transfers. New devices should use the generic + "bus-width = <1>" property. + - sdhci,auto-cmd12: specifies that a controller can only handle auto + CMD12. Example: diff --git a/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt b/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt index fea541ee8b34..70cd49b1caa8 100644 --- a/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt +++ b/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt @@ -3,17 +3,15 @@ The Enhanced Secure Digital Host Controller on Freescale i.MX family provides an interface for MMC, SD, and SDIO types of memory cards. +This file documents differences between the core properties described +by mmc.txt and the properties used by the sdhci-esdhc-imx driver. + Required properties: - compatible : Should be "fsl,-esdhc" -- reg : Should contain eSDHC registers location and length -- interrupts : Should contain eSDHC interrupt Optional properties: -- non-removable : Indicate the card is wired to host permanently - fsl,cd-internal : Indicate to use controller internal card detection - fsl,wp-internal : Indicate to use controller internal write protection -- cd-gpios : Specify GPIOs for card detection -- wp-gpios : Specify GPIOs for write protection Examples: diff --git a/Documentation/devicetree/bindings/mmc/mmc-spi-slot.txt b/Documentation/devicetree/bindings/mmc/mmc-spi-slot.txt index d64aea5a4203..0e5e2ec4001d 100644 --- a/Documentation/devicetree/bindings/mmc/mmc-spi-slot.txt +++ b/Documentation/devicetree/bindings/mmc/mmc-spi-slot.txt @@ -1,8 +1,9 @@ MMC/SD/SDIO slot directly connected to a SPI bus +This file documents differences between the core properties described +by mmc.txt and the properties used by the mmc_spi driver. + Required properties: -- compatible : should be "mmc-spi-slot". -- reg : should specify SPI address (chip-select number). - spi-max-frequency : maximum frequency for this device (Hz). - voltage-ranges : two cells are required, first cell specifies minimum slot voltage (mV), second cell specifies maximum slot voltage (mV). @@ -11,8 +12,7 @@ Required properties: Optional properties: - gpios : may specify GPIOs in this order: Card-Detect GPIO, Write-Protect GPIO. Note that this does not follow the - binding from mmc.txt, for historic reasons. -- interrupts : the interrupt of a card detect interrupt. + binding from mmc.txt, for historical reasons. - interrupt-parent : the phandle for the interrupt controller that services interrupts for this device. diff --git a/Documentation/devicetree/bindings/mmc/mmci.txt b/Documentation/devicetree/bindings/mmc/mmci.txt index 14a81d526118..2b584cae352a 100644 --- a/Documentation/devicetree/bindings/mmc/mmci.txt +++ b/Documentation/devicetree/bindings/mmc/mmci.txt @@ -1,19 +1,15 @@ * ARM PrimeCell MultiMedia Card Interface (MMCI) PL180/1 -The ARM PrimeCell MMCI PL180 and PL181 provides and interface for +The ARM PrimeCell MMCI PL180 and PL181 provides an interface for reading and writing to MultiMedia and SD cards alike. +This file documents differences between the core properties described +by mmc.txt and the properties used by the mmci driver. + Required properties: - compatible : contains "arm,pl18x", "arm,primecell". -- reg : contains pl18x registers and length. -- interrupts : contains the device IRQ(s). - arm,primecell-periphid : contains the PrimeCell Peripheral ID. Optional properties: -- wp-gpios : contains any write protect (ro) gpios -- cd-gpios : contains any card detection gpios -- cd-inverted : indicates whether the cd gpio is inverted -- max-frequency : contains the maximum operating frequency -- bus-width : number of data lines, can be <1>, <4>, or <8> - mmc-cap-mmc-highspeed : indicates whether MMC is high speed capable - mmc-cap-sd-highspeed : indicates whether SD is high speed capable diff --git a/Documentation/devicetree/bindings/mmc/mxs-mmc.txt b/Documentation/devicetree/bindings/mmc/mxs-mmc.txt index 14d870a9e3db..54949f6faede 100644 --- a/Documentation/devicetree/bindings/mmc/mxs-mmc.txt +++ b/Documentation/devicetree/bindings/mmc/mxs-mmc.txt @@ -3,16 +3,14 @@ The Freescale MXS Synchronous Serial Ports (SSP) can act as a MMC controller to support MMC, SD, and SDIO types of memory cards. +This file documents differences between the core properties in mmc.txt +and the properties used by the mxsmmc driver. + Required properties: - compatible: Should be "fsl,-mmc". The supported chips include imx23 and imx28. -- reg: Should contain registers location and length - interrupts: Should contain ERROR and DMA interrupts - fsl,ssp-dma-channel: APBH DMA channel for the SSP -- bus-width: Number of data lines, can be <1>, <4>, or <8> - -Optional properties: -- wp-gpios: Specify GPIOs for write protection Examples: diff --git a/Documentation/devicetree/bindings/mmc/nvidia-sdhci.txt b/Documentation/devicetree/bindings/mmc/nvidia-sdhci.txt index f77c3031607f..c6d7b11db9eb 100644 --- a/Documentation/devicetree/bindings/mmc/nvidia-sdhci.txt +++ b/Documentation/devicetree/bindings/mmc/nvidia-sdhci.txt @@ -3,15 +3,13 @@ This controller on Tegra family SoCs provides an interface for MMC, SD, and SDIO types of memory cards. +This file documents differences between the core properties described +by mmc.txt and the properties used by the sdhci-tegra driver. + Required properties: - compatible : Should be "nvidia,-sdhci" -- reg : Should contain SD/MMC registers location and length -- interrupts : Should contain SD/MMC interrupt -- bus-width : Number of data lines, can be <1>, <4>, or <8> Optional properties: -- cd-gpios : Specify GPIOs for card detection -- wp-gpios : Specify GPIOs for write protection - power-gpios : Specify GPIOs for power control Example: diff --git a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt index 8a53958c9a9f..be76a23b34c4 100644 --- a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt +++ b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt @@ -3,21 +3,20 @@ The Highspeed MMC Host Controller on TI OMAP family provides an interface for MMC, SD, and SDIO types of memory cards. +This file documents differences between the core properties described +by mmc.txt and the properties used by the omap_hsmmc driver. + 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 -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 From ed3efc1ccc4421865b64d902cd410f6733c85a49 Mon Sep 17 00:00:00 2001 From: Chris Ball Date: Mon, 11 Jun 2012 22:06:43 -0400 Subject: [PATCH 07/62] mmc: dt: Add reg/interrupts to mmc.txt for clarity. Even though these aren't parsed by the MMC driver, make clear that they're required by the OF core. (Also, a few typo fixes.) Signed-off-by: Chris Ball --- Documentation/devicetree/bindings/mmc/mmc.txt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt b/Documentation/devicetree/bindings/mmc/mmc.txt index 6e70dcde0a71..8a6811f4a02f 100644 --- a/Documentation/devicetree/bindings/mmc/mmc.txt +++ b/Documentation/devicetree/bindings/mmc/mmc.txt @@ -2,13 +2,17 @@ These properties are common to multiple MMC host controllers. Any host that requires the respective functionality should implement them using these definitions. +Interpreted by the OF core: +- reg: Registers location and length. +- interrupts: Interrupts used by the MMC controller. + Required properties: - bus-width: Number of data lines, can be <1>, <4>, or <8> Optional properties: -- cd-gpios : Specify GPIOs for card detection, see gpio binding -- wp-gpios : Specify GPIOs for write protection, see gpio binding -- cd-inverted: when present, polarity on the wp gpio line is inverted +- cd-gpios: Specify GPIOs for card detection, see gpio binding +- wp-gpios: Specify GPIOs for write protection, see gpio binding +- cd-inverted: when present, polarity on the cd gpio line is inverted - wp-inverted: when present, polarity on the wp gpio line is inverted - non-removable: non-removable slot (like eMMC) - max-frequency: maximum operating clock frequency From b650352dd3df36164e3427bff3f33bc06ac47642 Mon Sep 17 00:00:00 2001 From: Chris Ball Date: Tue, 10 Apr 2012 22:34:33 -0400 Subject: [PATCH 08/62] mmc: sdhci-pxa: Add device tree support Tested on an OLPC XO-1.75. (MMP2, sdhci-pxav3, CONFIG_MACH_MMP2_DT=y) Signed-off-by: Chris Ball Acked-by: Haojian Zhuang --- .../devicetree/bindings/mmc/sdhci-pxa.txt | 21 ++++++++ drivers/mmc/host/sdhci-pxav2.c | 54 +++++++++++++++++++ drivers/mmc/host/sdhci-pxav3.c | 52 ++++++++++++++++++ 3 files changed, 127 insertions(+) create mode 100644 Documentation/devicetree/bindings/mmc/sdhci-pxa.txt diff --git a/Documentation/devicetree/bindings/mmc/sdhci-pxa.txt b/Documentation/devicetree/bindings/mmc/sdhci-pxa.txt new file mode 100644 index 000000000000..dbe98a3c183a --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/sdhci-pxa.txt @@ -0,0 +1,21 @@ +* Marvell sdhci-pxa v2/v3 controller + +This file documents differences between the core properties in mmc.txt +and the properties used by the sdhci-pxav2 and sdhci-pxav3 drivers. + +Required properties: +- compatible: Should be "mrvl,pxav2-mmc" or "mrvl,pxav3-mmc". + +Optional properties: +- mrvl,clk-delay-cycles: Specify a number of cycles to delay for tuning. + +Example: + +sdhci@d4280800 { + compatible = "mrvl,pxav3-mmc"; + reg = <0xd4280800 0x800>; + bus-width = <8>; + interrupts = <27>; + non-removable; + mrvl,clk-delay-cycles = <31>; +}; diff --git a/drivers/mmc/host/sdhci-pxav2.c b/drivers/mmc/host/sdhci-pxav2.c index dbb75bfbcffb..b6ee8857e226 100644 --- a/drivers/mmc/host/sdhci-pxav2.c +++ b/drivers/mmc/host/sdhci-pxav2.c @@ -28,6 +28,9 @@ #include #include #include +#include +#include + #include "sdhci.h" #include "sdhci-pltfm.h" @@ -121,6 +124,48 @@ static struct sdhci_ops pxav2_sdhci_ops = { .platform_8bit_width = pxav2_mmc_set_width, }; +#ifdef CONFIG_OF +static const struct of_device_id sdhci_pxav2_of_match[] = { + { + .compatible = "mrvl,pxav2-mmc", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, sdhci_pxav2_of_match); + +static struct sdhci_pxa_platdata *pxav2_get_mmc_pdata(struct device *dev) +{ + struct sdhci_pxa_platdata *pdata; + struct device_node *np = dev->of_node; + u32 bus_width; + u32 clk_delay_cycles; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; + + if (of_find_property(np, "non-removable", NULL)) + pdata->flags |= PXA_FLAG_CARD_PERMANENT; + + of_property_read_u32(np, "bus-width", &bus_width); + if (bus_width == 8) + pdata->flags |= PXA_FLAG_SD_8_BIT_CAPABLE_SLOT; + + of_property_read_u32(np, "mrvl,clk-delay-cycles", &clk_delay_cycles); + if (clk_delay_cycles > 0) { + pdata->clk_delay_sel = 1; + pdata->clk_delay_cycles = clk_delay_cycles; + } + + return pdata; +} +#else +static inline struct sdhci_pxa_platdata *pxav2_get_mmc_pdata(struct device *dev) +{ + return NULL; +} +#endif + static int __devinit sdhci_pxav2_probe(struct platform_device *pdev) { struct sdhci_pltfm_host *pltfm_host; @@ -128,6 +173,8 @@ static int __devinit sdhci_pxav2_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct sdhci_host *host = NULL; struct sdhci_pxa *pxa = NULL; + const struct of_device_id *match; + int ret; struct clk *clk; @@ -156,6 +203,10 @@ static int __devinit sdhci_pxav2_probe(struct platform_device *pdev) | SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN; + match = of_match_device(of_match_ptr(sdhci_pxav2_of_match), &pdev->dev); + if (match) { + pdata = pxav2_get_mmc_pdata(dev); + } if (pdata) { if (pdata->flags & PXA_FLAG_CARD_PERMANENT) { /* on-chip device */ @@ -218,6 +269,9 @@ static struct platform_driver sdhci_pxav2_driver = { .driver = { .name = "sdhci-pxav2", .owner = THIS_MODULE, +#ifdef CONFIG_OF + .of_match_table = sdhci_pxav2_of_match, +#endif .pm = SDHCI_PLTFM_PMOPS, }, .probe = sdhci_pxav2_probe, diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index f29695683556..07fe3834fe0b 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -28,6 +28,9 @@ #include #include #include +#include +#include + #include "sdhci.h" #include "sdhci-pltfm.h" @@ -164,6 +167,46 @@ static struct sdhci_ops pxav3_sdhci_ops = { .platform_send_init_74_clocks = pxav3_gen_init_74_clocks, }; +#ifdef CONFIG_OF +static const struct of_device_id sdhci_pxav3_of_match[] = { + { + .compatible = "mrvl,pxav3-mmc", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, sdhci_pxav3_of_match); + +static struct sdhci_pxa_platdata *pxav3_get_mmc_pdata(struct device *dev) +{ + struct sdhci_pxa_platdata *pdata; + struct device_node *np = dev->of_node; + u32 bus_width; + u32 clk_delay_cycles; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; + + if (of_find_property(np, "non-removable", NULL)) + pdata->flags |= PXA_FLAG_CARD_PERMANENT; + + of_property_read_u32(np, "bus-width", &bus_width); + if (bus_width == 8) + pdata->flags |= PXA_FLAG_SD_8_BIT_CAPABLE_SLOT; + + of_property_read_u32(np, "mrvl,clk-delay-cycles", &clk_delay_cycles); + if (clk_delay_cycles > 0) + pdata->clk_delay_cycles = clk_delay_cycles; + + return pdata; +} +#else +static inline struct sdhci_pxa_platdata *pxav3_get_mmc_pdata(struct device *dev) +{ + return NULL; +} +#endif + static int __devinit sdhci_pxav3_probe(struct platform_device *pdev) { struct sdhci_pltfm_host *pltfm_host; @@ -171,6 +214,8 @@ static int __devinit sdhci_pxav3_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct sdhci_host *host = NULL; struct sdhci_pxa *pxa = NULL; + const struct of_device_id *match; + int ret; struct clk *clk; @@ -202,6 +247,10 @@ static int __devinit sdhci_pxav3_probe(struct platform_device *pdev) /* enable 1/8V DDR capable */ host->mmc->caps |= MMC_CAP_1_8V_DDR; + match = of_match_device(of_match_ptr(sdhci_pxav3_of_match), &pdev->dev); + if (match) + pdata = pxav3_get_mmc_pdata(dev); + if (pdata) { if (pdata->flags & PXA_FLAG_CARD_PERMANENT) { /* on-chip device */ @@ -263,6 +312,9 @@ static int __devexit sdhci_pxav3_remove(struct platform_device *pdev) static struct platform_driver sdhci_pxav3_driver = { .driver = { .name = "sdhci-pxav3", +#ifdef CONFIG_OF + .of_match_table = sdhci_pxav3_of_match, +#endif .owner = THIS_MODULE, .pm = SDHCI_PLTFM_PMOPS, }, From 94c6cee91bebfc17596243b6a5f4fe910feec426 Mon Sep 17 00:00:00 2001 From: Girish K S Date: Tue, 12 Jun 2012 15:28:22 +0530 Subject: [PATCH 09/62] mmc: dw_mmc: Add check for IDMAC configuration In the current dwmmc driver there is support for selecting IDMAC from the menu config option. If the support for IDMAC is enabled in the menu config and the hardware configuration register's DMA_INTERFACE field is 0, the driver will still try to do the DMA initialization. The dw_mci_idmac_init function currently implemented returns only success indicating that the DMA initialization is always successful. This patch adds a check for existence of the DMA IP to allow the DMA initialization. Signed-off-by: Girish K S Acked-by: Will Newton Signed-off-by: Chris Ball --- drivers/mmc/host/dw_mmc.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 1ca5e72ceb65..5a600b3b3199 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -405,11 +405,23 @@ static void dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len) static int dw_mci_idmac_init(struct dw_mci *host) { struct idmac_desc *p; - int i; + int i, dma_support; /* Number of descriptors in the ring buffer */ host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc); + /* Check if Hardware Configuration Register has support for DMA */ + dma_support = (mci_readl(host, HCON) >> 16) & 0x3; + + if (!dma_support || dma_support > 2) { + dev_err(&host->dev, + "Host Controller does not support IDMA Tx.\n"); + host->dma_ops = NULL; + return -ENODEV; + } + + dev_info(&host->dev, "Using internal DMA controller.\n"); + /* Forward link the descriptor list */ for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; i++, p++) p->des3 = host->sg_dma + (sizeof(struct idmac_desc) * (i + 1)); @@ -1876,7 +1888,6 @@ 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->dev, "Using internal DMA controller.\n"); #endif if (!host->dma_ops) From 7541ca98477862e2e9988c6c2ceadbdccefa9d77 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 12 Jun 2012 22:56:09 +0200 Subject: [PATCH 10/62] mmc: sh_mmcif: Support MMC_SLEEP_AWAKE command The MMC_SLEEP_AWAKE and SD_IO_SEND_OP_COND commands share the same opcode. SD_IO_SEND_OP_COND isn't supported by the SH MMCIF, but MMC_SLEEP_AWAKE is. Discriminate between the two commands using the command flags, and reject SD_IO_SEND_OP_COND only. Signed-off-by: Laurent Pinchart Acked-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 724b35e85a26..e32da1126e8d 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -892,21 +892,15 @@ static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq) switch (mrq->cmd->opcode) { /* MMCIF does not support SD/SDIO command */ - case SD_IO_SEND_OP_COND: + case MMC_SLEEP_AWAKE: /* = SD_IO_SEND_OP_COND (5) */ + case MMC_SEND_EXT_CSD: /* = SD_SEND_IF_COND (8) */ + if ((mrq->cmd->flags & MMC_CMD_MASK) != MMC_CMD_BCR) + break; case MMC_APP_CMD: host->state = STATE_IDLE; mrq->cmd->error = -ETIMEDOUT; mmc_request_done(mmc, mrq); return; - case MMC_SEND_EXT_CSD: /* = SD_SEND_IF_COND (8) */ - if (!mrq->data) { - /* send_if_cond cmd (not support) */ - host->state = STATE_IDLE; - mrq->cmd->error = -ETIMEDOUT; - mmc_request_done(mmc, mrq); - return; - } - break; default: break; } From 6de707f200f73af7a58b58b3a5b956cff7b6e228 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 12 Jun 2012 23:29:35 +0200 Subject: [PATCH 11/62] mmc: tmio: Don't access hardware registers after stopping clocks The tmio_mmc_set_ios() function configures the MMC power, clock and bus width. When the mmc core requests the driver to power off the card, we inform runtime PM, that the controller can be suspended. This can lead to the MSTP clock being turned off. Writing to any 16-bit hardware registers with the MSTP clock off leads to timeouts and errors being printed to the kernel log. This can occur both when stopping the MMC clock and when configuring the bus width. To fix this, stop the MMC clock before calling put_runtime_pm(), and skip bus width configuration when power is off. Signed-off-by: Laurent Pinchart Tested-by: Kuninori Morimoto Tested-by: Simon Horman Acked-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/tmio_mmc_pio.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 7ffc489bed35..4f26edf07204 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -811,19 +811,21 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (host->set_pwr && ios->power_mode == MMC_POWER_OFF) host->set_pwr(host->pdev, 0); if (host->power) { + tmio_mmc_clk_stop(host); host->power = false; pm_runtime_put(dev); } - tmio_mmc_clk_stop(host); } - switch (ios->bus_width) { - case MMC_BUS_WIDTH_1: - sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x80e0); - break; - case MMC_BUS_WIDTH_4: - sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x00e0); - break; + if (host->power) { + switch (ios->bus_width) { + case MMC_BUS_WIDTH_1: + sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x80e0); + break; + case MMC_BUS_WIDTH_4: + sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x00e0); + break; + } } /* Let things settle. delay taken from winCE driver */ From e137788dd115dd9d21759a768dba5fff9685e587 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 20 Jun 2012 02:28:43 -0400 Subject: [PATCH 12/62] mmc: add a function to get regulators, supplying card's power Add a function to get regulators, supplying card's Vdd and Vccq on a specific host. If a Vdd supplying regulator is found, the function checks, whether a valid OCR mask can be obtained from it. The Vccq regulator is optional. A failure to get it is not fatal. Signed-off-by: Guennadi Liakhovetski Reviwed-by: Mark Brown Acked-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/core/core.c | 23 +++++++++++++++++++++++ include/linux/mmc/host.h | 16 ++++++++++++++-- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 28b1ffaf0bd1..8d00aef9523e 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1022,6 +1022,29 @@ int mmc_regulator_set_ocr(struct mmc_host *mmc, } EXPORT_SYMBOL_GPL(mmc_regulator_set_ocr); +int mmc_regulator_get_supply(struct mmc_host *mmc) +{ + struct device *dev = mmc_dev(mmc); + struct regulator *supply; + int ret; + + supply = devm_regulator_get(dev, "vmmc"); + mmc->supply.vmmc = supply; + mmc->supply.vqmmc = devm_regulator_get(dev, "vqmmc"); + + if (IS_ERR(supply)) + return PTR_ERR(supply); + + ret = mmc_regulator_get_ocrmask(supply); + if (ret > 0) + mmc->ocr_avail = ret; + else + dev_warn(mmc_dev(mmc), "Failed getting OCR mask: %d\n", ret); + + return 0; +} +EXPORT_SYMBOL_GPL(mmc_regulator_get_supply); + #endif /* CONFIG_REGULATOR */ /* diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 0707d228d7f1..9deb725799e7 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -155,6 +155,13 @@ struct mmc_hotplug { void *handler_priv; }; +struct regulator; + +struct mmc_supply { + struct regulator *vmmc; /* Card power supply */ + struct regulator *vqmmc; /* Optional Vccq supply */ +}; + struct mmc_host { struct device *parent; struct device class_dev; @@ -309,6 +316,7 @@ struct mmc_host { #ifdef CONFIG_REGULATOR bool regulator_enabled; /* regulator state */ #endif + struct mmc_supply supply; struct dentry *debugfs_root; @@ -357,13 +365,12 @@ static inline void mmc_signal_sdio_irq(struct mmc_host *host) wake_up_process(host->sdio_irq_thread); } -struct regulator; - #ifdef CONFIG_REGULATOR int mmc_regulator_get_ocrmask(struct regulator *supply); int mmc_regulator_set_ocr(struct mmc_host *mmc, struct regulator *supply, unsigned short vdd_bit); +int mmc_regulator_get_supply(struct mmc_host *mmc); #else static inline int mmc_regulator_get_ocrmask(struct regulator *supply) { @@ -376,6 +383,11 @@ static inline int mmc_regulator_set_ocr(struct mmc_host *mmc, { return 0; } + +static inline int mmc_regulator_get_supply(struct mmc_host *mmc) +{ + return 0; +} #endif int mmc_card_awake(struct mmc_host *host); From 3bfe619dc365e439936ed6dae36506944c44c18c Mon Sep 17 00:00:00 2001 From: Jonathan Kliegman Date: Thu, 14 Jun 2012 13:31:55 -0400 Subject: [PATCH 13/62] mmc: dw_mmc: Fix null dma_ops access when use_dma is false host->dma_ops is not valid if host->usa dma is 0 so protect host->dma_ops reference in dw_mci_resume Signed-off-by: Jonathan Kliegman Acked-by: Jaehoon Chung Acked-by: Will Newton Signed-off-by: Chris Ball --- drivers/mmc/host/dw_mmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 5a600b3b3199..72dc3cde646d 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -2186,7 +2186,7 @@ int dw_mci_resume(struct dw_mci *host) return ret; } - if (host->dma_ops->init) + if (host->use_dma && host->dma_ops->init) host->dma_ops->init(host); /* Restore the old value at FIFOTH register */ From e0337cc8b02fcb3f725746735db84d6d1b6b9196 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 20 Jun 2012 19:10:30 +0200 Subject: [PATCH 14/62] mmc: tmio: don't needlessly enable interrupts during probing We don't have to force-enable MMC interrupts in our .probe() method, mmc_add_host() will trigger card detection, that will enable all the necessary interrupts. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/tmio_mmc_pio.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 4f26edf07204..4318c1a74ba0 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -951,6 +951,17 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host, _host->sdcard_irq_mask = sd_ctrl_read32(_host, CTL_IRQ_MASK); tmio_mmc_disable_mmc_irqs(_host, TMIO_MASK_ALL); + + /* Unmask the IRQs we want to know about */ + if (!_host->chan_rx) + 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); + + _host->sdcard_irq_mask &= ~irq_mask; + if (pdata->flags & TMIO_MMC_SDIO_IRQ) tmio_mmc_enable_sdio_irq(mmc, 0); @@ -968,16 +979,6 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host, dev_pm_qos_expose_latency_limit(&pdev->dev, 100); - /* Unmask the IRQs we want to know about */ - if (!_host->chan_rx) - 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); - if (pdata->flags & TMIO_MMC_USE_GPIO_CD) { ret = mmc_gpio_request_cd(mmc, pdata->cd_gpio); if (ret < 0) { From 8c102a964655b1a8df41b1f9e2355657471a45e3 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 20 Jun 2012 19:10:31 +0200 Subject: [PATCH 15/62] mmc: tmio: add callbacks to enable-update and disable the interface clock Every time the clock is enabled after possibly being disabled, we have to re-read its frequency and update our configuration. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/tmio_mmc_pio.c | 35 ++++++++++++++++++++++++++++++--- include/linux/mfd/tmio.h | 3 +++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 4318c1a74ba0..c6c0334a20e1 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -752,6 +752,22 @@ fail: mmc_request_done(mmc, mrq); } +static int tmio_mmc_clk_update(struct mmc_host *mmc) +{ + struct tmio_mmc_host *host = mmc_priv(mmc); + struct tmio_mmc_data *pdata = host->pdata; + int ret; + + if (!pdata->clk_enable) + return -ENOTSUPP; + + ret = pdata->clk_enable(host->pdev, &mmc->f_max); + if (!ret) + mmc->f_min = mmc->f_max / 512; + + return ret; +} + /* Set MMC clock / power. * Note: This controller uses a simple divider scheme therefore it cannot * run a MMC card at full speed (20MHz). The max clock is 24MHz on SD, but as @@ -798,6 +814,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) { + tmio_mmc_clk_update(mmc); pm_runtime_get_sync(dev); host->power = true; } @@ -811,9 +828,12 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (host->set_pwr && ios->power_mode == MMC_POWER_OFF) host->set_pwr(host->pdev, 0); if (host->power) { + struct tmio_mmc_data *pdata = host->pdata; tmio_mmc_clk_stop(host); host->power = false; pm_runtime_put(dev); + if (pdata->clk_disable) + pdata->clk_disable(host->pdev); } } @@ -907,8 +927,6 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host, mmc->ops = &tmio_mmc_ops; mmc->caps = MMC_CAP_4_BIT_DATA | pdata->capabilities; - mmc->f_max = pdata->hclk; - mmc->f_min = mmc->f_max / 512; mmc->max_segs = 32; mmc->max_blk_size = 512; mmc->max_blk_count = (PAGE_CACHE_SIZE / mmc->max_blk_size) * @@ -930,6 +948,11 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host, if (ret < 0) goto pm_disable; + if (tmio_mmc_clk_update(mmc) < 0) { + mmc->f_max = pdata->hclk; + mmc->f_min = mmc->f_max / 512; + } + /* * There are 4 different scenarios for the card detection: * 1) an external gpio irq handles the cd (best for power savings) @@ -975,7 +998,13 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host, /* See if we also get DMA */ tmio_mmc_request_dma(_host, pdata); - mmc_add_host(mmc); + ret = mmc_add_host(mmc); + if (pdata->clk_disable) + pdata->clk_disable(pdev); + if (ret < 0) { + tmio_mmc_host_remove(_host); + return ret; + } dev_pm_qos_expose_latency_limit(&pdev->dev, 100); diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h index f5171dbf8850..b332c4c7857b 100644 --- a/include/linux/mfd/tmio.h +++ b/include/linux/mfd/tmio.h @@ -110,6 +110,9 @@ struct tmio_mmc_data { 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); + /* clock management callbacks */ + int (*clk_enable)(struct platform_device *pdev, unsigned int *f); + void (*clk_disable)(struct platform_device *pdev); }; /* From 56c492879318f43b1a7911675ff30fb0ede123b5 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 20 Jun 2012 19:10:32 +0200 Subject: [PATCH 16/62] mmc: sdhi: implement tmio-mmc clock enable-update and disable callbacks Instead of delivering one static clock frequency value, read from the hardware during probing, enable the tmio-mmc driver to re-read the frequency dynamically. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mobile_sdhi.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index 934b68e9efc3..d8b167c8493a 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -39,6 +39,27 @@ struct sh_mobile_sdhi { struct tmio_mmc_dma dma_priv; }; +static int sh_mobile_sdhi_clk_enable(struct platform_device *pdev, unsigned int *f) +{ + struct mmc_host *mmc = dev_get_drvdata(&pdev->dev); + struct tmio_mmc_host *host = mmc_priv(mmc); + struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data); + int ret = clk_enable(priv->clk); + if (ret < 0) + return ret; + + *f = clk_get_rate(priv->clk); + return 0; +} + +static void sh_mobile_sdhi_clk_disable(struct platform_device *pdev) +{ + struct mmc_host *mmc = dev_get_drvdata(&pdev->dev); + struct tmio_mmc_host *host = mmc_priv(mmc); + struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data); + clk_disable(priv->clk); +} + static void sh_mobile_sdhi_set_pwr(struct platform_device *pdev, int state) { struct sh_mobile_sdhi_info *p = pdev->dev.platform_data; @@ -132,9 +153,10 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev) goto eclkget; } - 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; + mmc_data->clk_enable = sh_mobile_sdhi_clk_enable; + mmc_data->clk_disable = sh_mobile_sdhi_clk_disable; mmc_data->capabilities = MMC_CAP_MMC_HIGHSPEED; if (p) { mmc_data->flags = p->tmio_flags; From b958a67c6bbe0a886bebc618aac7a059cdbab418 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 20 Jun 2012 19:10:33 +0200 Subject: [PATCH 17/62] mmc: tmio: add regulator support Currently the TMIO MMC driver derives the OCR mask from the platform data and uses a platform callback to turn card power on and off. This patch adds regulator support to the driver. Signed-off-by: Guennadi Liakhovetski Cc: Liam Girdwood Cc: Mark Brown Signed-off-by: Chris Ball --- drivers/mmc/host/tmio_mmc_pio.c | 37 ++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index c6c0334a20e1..b5c32b40ebc1 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -768,6 +768,18 @@ static int tmio_mmc_clk_update(struct mmc_host *mmc) return ret; } +static void tmio_mmc_set_power(struct tmio_mmc_host *host, struct mmc_ios *ios) +{ + struct mmc_host *mmc = host->mmc; + + if (host->set_pwr) + host->set_pwr(host->pdev, ios->power_mode != MMC_POWER_OFF); + if (!IS_ERR(mmc->supply.vmmc)) + /* Errors ignored... */ + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, + ios->power_mode ? ios->vdd : 0); +} + /* Set MMC clock / power. * Note: This controller uses a simple divider scheme therefore it cannot * run a MMC card at full speed (20MHz). The max clock is 24MHz on SD, but as @@ -820,13 +832,12 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) } tmio_mmc_set_clock(host, ios->clock); /* power up SD bus */ - if (host->set_pwr) - host->set_pwr(host->pdev, 1); + tmio_mmc_set_power(host, ios); /* start bus clock */ tmio_mmc_clk_start(host); } 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 (ios->power_mode == MMC_POWER_OFF) + tmio_mmc_set_power(host, ios); if (host->power) { struct tmio_mmc_data *pdata = host->pdata; tmio_mmc_clk_stop(host); @@ -888,6 +899,19 @@ static const struct mmc_host_ops tmio_mmc_ops = { .enable_sdio_irq = tmio_mmc_enable_sdio_irq, }; +static void tmio_mmc_init_ocr(struct tmio_mmc_host *host) +{ + struct tmio_mmc_data *pdata = host->pdata; + struct mmc_host *mmc = host->mmc; + + mmc_regulator_get_supply(mmc); + + if (!mmc->ocr_avail) + mmc->ocr_avail = pdata->ocr_mask ? : MMC_VDD_32_33 | MMC_VDD_33_34; + else if (pdata->ocr_mask) + dev_warn(mmc_dev(mmc), "Platform OCR mask is ignored\n"); +} + int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host, struct platform_device *pdev, struct tmio_mmc_data *pdata) @@ -933,10 +957,7 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host, mmc->max_segs; mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; mmc->max_seg_size = mmc->max_req_size; - if (pdata->ocr_mask) - mmc->ocr_avail = pdata->ocr_mask; - else - mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; + tmio_mmc_init_ocr(_host); _host->native_hotplug = !(pdata->flags & TMIO_MMC_USE_GPIO_CD || mmc->caps & MMC_CAP_NEEDS_POLL || From 71da63e767c393c0ebbd3c65df428df0ce42a16c Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 20 Jun 2012 19:10:34 +0200 Subject: [PATCH 18/62] mmc: sdhi: do not install dummy callbacks Currently the SDHI glue for the TMIO MMC driver installs dummy .get_cd() and .set_pwr() callbacks even if the platform didn't supply them. This is not necessary, since the TMIO MMC driver itself checks for NULL callbacks. This is also dubious if the platform provides a regulator for SD-card power switching. It is better to only install those callbacks, if they are really provided by the platform. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mobile_sdhi.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index d8b167c8493a..42f07fa6e043 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -64,18 +64,14 @@ static void sh_mobile_sdhi_set_pwr(struct platform_device *pdev, int state) { struct sh_mobile_sdhi_info *p = pdev->dev.platform_data; - if (p && p->set_pwr) - p->set_pwr(pdev, state); + p->set_pwr(pdev, state); } static int sh_mobile_sdhi_get_cd(struct platform_device *pdev) { struct sh_mobile_sdhi_info *p = pdev->dev.platform_data; - if (p && p->get_cd) - return p->get_cd(pdev); - else - return -ENOSYS; + return p->get_cd(pdev); } static int sh_mobile_sdhi_wait_idle(struct tmio_mmc_host *host) @@ -153,8 +149,10 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev) goto eclkget; } - mmc_data->set_pwr = sh_mobile_sdhi_set_pwr; - mmc_data->get_cd = sh_mobile_sdhi_get_cd; + if (p->set_pwr) + mmc_data->set_pwr = sh_mobile_sdhi_set_pwr; + if (p->get_cd) + mmc_data->get_cd = sh_mobile_sdhi_get_cd; mmc_data->clk_enable = sh_mobile_sdhi_clk_enable; mmc_data->clk_disable = sh_mobile_sdhi_clk_disable; mmc_data->capabilities = MMC_CAP_MMC_HIGHSPEED; From 944640d0fffb2ee2961a24c3747b1fc87c4157a8 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 20 Jun 2012 19:10:36 +0200 Subject: [PATCH 19/62] mmc: tmio: remove a duplicated comment line Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/tmio_mmc_pio.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index b5c32b40ebc1..b204012fe7a9 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -984,7 +984,6 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host, * 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. */ if (_host->native_hotplug) From e1aae2eb3f7c446a2680a3a0ccd05aa50521b4e2 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 19 Apr 2012 16:15:52 +0200 Subject: [PATCH 20/62] mmc: sh_mmcif: simplify and use meaningful label names in error-handling A check for NULL platform data can be conveniently made in the very beginning of probing. Replace numbered error-handling labels in .probe() with meaningful names to make any future reorganisation simpler. Signed-off-by: Guennadi Liakhovetski Reviewed-by: Simon Horman Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 41 ++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index e32da1126e8d..d6ffb0557537 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -1241,11 +1241,16 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) int ret = 0, irq[2]; struct mmc_host *mmc; struct sh_mmcif_host *host; - struct sh_mmcif_plat_data *pd; + struct sh_mmcif_plat_data *pd = pdev->dev.platform_data; struct resource *res; void __iomem *reg; char clk_name[8]; + if (!pd) { + dev_err(&pdev->dev, "sh_mmcif plat data error.\n"); + return -ENXIO; + } + irq[0] = platform_get_irq(pdev, 0); irq[1] = platform_get_irq(pdev, 1); if (irq[0] < 0 || irq[1] < 0) { @@ -1262,16 +1267,11 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) dev_err(&pdev->dev, "ioremap error.\n"); return -ENOMEM; } - pd = pdev->dev.platform_data; - if (!pd) { - dev_err(&pdev->dev, "sh_mmcif plat data error.\n"); - ret = -ENXIO; - goto clean_up; - } + mmc = mmc_alloc_host(sizeof(struct sh_mmcif_host), &pdev->dev); if (!mmc) { ret = -ENOMEM; - goto clean_up; + goto ealloch; } host = mmc_priv(mmc); host->mmc = mmc; @@ -1283,7 +1283,7 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) if (IS_ERR(host->hclk)) { dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name); ret = PTR_ERR(host->hclk); - goto clean_up1; + goto eclkget; } clk_enable(host->hclk); host->clk = clk_get_rate(host->hclk); @@ -1313,7 +1313,7 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) ret = pm_runtime_resume(&pdev->dev); if (ret < 0) - goto clean_up2; + goto eresume; INIT_DELAYED_WORK(&host->timeout_work, mmcif_timeout_work); @@ -1322,17 +1322,17 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) ret = request_threaded_irq(irq[0], sh_mmcif_intr, sh_mmcif_irqt, 0, "sh_mmc:error", host); if (ret) { dev_err(&pdev->dev, "request_irq error (sh_mmc:error)\n"); - goto clean_up3; + goto ereqirq0; } ret = request_threaded_irq(irq[1], sh_mmcif_intr, sh_mmcif_irqt, 0, "sh_mmc:int", host); if (ret) { dev_err(&pdev->dev, "request_irq error (sh_mmc:int)\n"); - goto clean_up4; + goto ereqirq1; } ret = mmc_add_host(mmc); if (ret < 0) - goto clean_up5; + goto emmcaddh; dev_pm_qos_expose_latency_limit(&pdev->dev, 100); @@ -1341,20 +1341,19 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) sh_mmcif_readl(host->addr, MMCIF_CE_VERSION) & 0x0000ffff); return ret; -clean_up5: +emmcaddh: free_irq(irq[1], host); -clean_up4: +ereqirq1: free_irq(irq[0], host); -clean_up3: +ereqirq0: pm_runtime_suspend(&pdev->dev); -clean_up2: +eresume: pm_runtime_disable(&pdev->dev); clk_disable(host->hclk); -clean_up1: +eclkget: mmc_free_host(mmc); -clean_up: - if (reg) - iounmap(reg); +ealloch: + iounmap(reg); return ret; } From b289174ff70a591545a054d52ae081a75a73f085 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 19 Apr 2012 18:02:05 +0200 Subject: [PATCH 21/62] mmc: sh_mmcif: fix clock management Regardless of whether the MMC bus clock is the same as the PM clock on this specific interface, it has to be managed separately. Its proper management should also include enabling and disabling of the clock, whenever the interface is becoming active or going idle respectively. Signed-off-by: Guennadi Liakhovetski Reviewed-by: Simon Horman Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 46 ++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index d6ffb0557537..6a93b0466854 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -942,6 +942,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) } if (host->power) { pm_runtime_put(&host->pd->dev); + clk_disable(host->hclk); host->power = false; if (p->down_pwr && ios->power_mode == MMC_POWER_OFF) p->down_pwr(host->pd); @@ -954,6 +955,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (!host->power) { if (p->set_pwr) p->set_pwr(host->pd, ios->power_mode); + clk_enable(host->hclk); pm_runtime_get_sync(&host->pd->dev); host->power = true; sh_mmcif_sync_reset(host); @@ -1278,22 +1280,11 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) host->addr = reg; host->timeout = 1000; - snprintf(clk_name, sizeof(clk_name), "mmc%d", pdev->id); - host->hclk = clk_get(&pdev->dev, clk_name); - if (IS_ERR(host->hclk)) { - dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name); - ret = PTR_ERR(host->hclk); - goto eclkget; - } - clk_enable(host->hclk); - host->clk = clk_get_rate(host->hclk); host->pd = pdev; spin_lock_init(&host->lock); mmc->ops = &sh_mmcif_ops; - mmc->f_max = host->clk / 2; - mmc->f_min = host->clk / 512; if (pd->ocr) mmc->ocr_avail = pd->ocr; mmc->caps = MMC_CAP_MMC_HIGHSPEED; @@ -1305,18 +1296,30 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) mmc->max_blk_count = mmc->max_req_size / mmc->max_blk_size; mmc->max_seg_size = mmc->max_req_size; - sh_mmcif_sync_reset(host); platform_set_drvdata(pdev, host); pm_runtime_enable(&pdev->dev); host->power = false; + snprintf(clk_name, sizeof(clk_name), "mmc%d", pdev->id); + host->hclk = clk_get(&pdev->dev, clk_name); + if (IS_ERR(host->hclk)) { + ret = PTR_ERR(host->hclk); + dev_err(&pdev->dev, "cannot get clock \"%s\": %d\n", clk_name, ret); + goto eclkget; + } + clk_enable(host->hclk); + host->clk = clk_get_rate(host->hclk); + mmc->f_max = host->clk / 2; + mmc->f_min = host->clk / 512; + ret = pm_runtime_resume(&pdev->dev); if (ret < 0) goto eresume; INIT_DELAYED_WORK(&host->timeout_work, mmcif_timeout_work); + sh_mmcif_sync_reset(host); sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); ret = request_threaded_irq(irq[0], sh_mmcif_intr, sh_mmcif_irqt, 0, "sh_mmc:error", host); @@ -1330,6 +1333,7 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) goto ereqirq1; } + clk_disable(host->hclk); ret = mmc_add_host(mmc); if (ret < 0) goto emmcaddh; @@ -1348,9 +1352,10 @@ ereqirq1: ereqirq0: pm_runtime_suspend(&pdev->dev); eresume: - pm_runtime_disable(&pdev->dev); clk_disable(host->hclk); + clk_put(host->hclk); eclkget: + pm_runtime_disable(&pdev->dev); mmc_free_host(mmc); ealloch: iounmap(reg); @@ -1363,6 +1368,7 @@ static int __devexit sh_mmcif_remove(struct platform_device *pdev) int irq[2]; host->dying = true; + clk_enable(host->hclk); pm_runtime_get_sync(&pdev->dev); dev_pm_qos_hide_latency_limit(&pdev->dev); @@ -1388,9 +1394,9 @@ static int __devexit sh_mmcif_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); - clk_disable(host->hclk); mmc_free_host(host->mmc); pm_runtime_put_sync(&pdev->dev); + clk_disable(host->hclk); pm_runtime_disable(&pdev->dev); return 0; @@ -1399,24 +1405,18 @@ static int __devexit sh_mmcif_remove(struct platform_device *pdev) #ifdef CONFIG_PM static int sh_mmcif_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct sh_mmcif_host *host = platform_get_drvdata(pdev); + struct sh_mmcif_host *host = dev_get_drvdata(dev); int ret = mmc_suspend_host(host->mmc); - if (!ret) { + if (!ret) sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); - clk_disable(host->hclk); - } return ret; } static int sh_mmcif_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct sh_mmcif_host *host = platform_get_drvdata(pdev); - - clk_enable(host->hclk); + struct sh_mmcif_host *host = dev_get_drvdata(dev); return mmc_resume_host(host->mmc); } From a6609267107ecc5598b79aa353036c1f57e7257e Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 19 Apr 2012 18:02:50 +0200 Subject: [PATCH 22/62] mmc: sh_mmcif: re-read the clock frequency every time it is turned on With aggressive clock gating the clock can be disabled during interface inactivity. During this time its frequency can be changed by another its user. Therefore when the interface is activated again and the clock is re-enabled, its frequency has to be re-read. Signed-off-by: Guennadi Liakhovetski Reviewed-by: Simon Horman Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 6a93b0466854..3ffb92fc084e 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -910,6 +910,19 @@ static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq) sh_mmcif_start_cmd(host, mrq); } +static int sh_mmcif_clk_update(struct sh_mmcif_host *host) +{ + int ret = clk_enable(host->hclk); + + if (!ret) { + host->clk = clk_get_rate(host->hclk); + host->mmc->f_max = host->clk / 2; + host->mmc->f_min = host->clk / 512; + } + + return ret; +} + static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct sh_mmcif_host *host = mmc_priv(mmc); @@ -955,7 +968,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (!host->power) { if (p->set_pwr) p->set_pwr(host->pd, ios->power_mode); - clk_enable(host->hclk); + sh_mmcif_clk_update(host); pm_runtime_get_sync(&host->pd->dev); host->power = true; sh_mmcif_sync_reset(host); @@ -1308,10 +1321,9 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) dev_err(&pdev->dev, "cannot get clock \"%s\": %d\n", clk_name, ret); goto eclkget; } - clk_enable(host->hclk); - host->clk = clk_get_rate(host->hclk); - mmc->f_max = host->clk / 2; - mmc->f_min = host->clk / 512; + ret = sh_mmcif_clk_update(host); + if (ret < 0) + goto eclkupdate; ret = pm_runtime_resume(&pdev->dev); if (ret < 0) @@ -1353,6 +1365,7 @@ ereqirq0: pm_runtime_suspend(&pdev->dev); eresume: clk_disable(host->hclk); +eclkupdate: clk_put(host->hclk); eclkget: pm_runtime_disable(&pdev->dev); From e2ee996eaad64c149cb7cfd344789c7e0c1577dc Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 20 Apr 2012 09:01:05 +0200 Subject: [PATCH 23/62] mmc: sh_mmcif: remove redundant .down_pwr() callback From the original version of sh_mmcif the .set_pwr() callback has only been used to turn the card's power on, and the .down_pwr() callback has been used to turn it off. .set_pwr() can be used for both these tasks, which is also how it is implemented by the only user of this API: the SH7724 ecovec board. Signed-off-by: Guennadi Liakhovetski Reviewed-by: Simon Horman Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 3ffb92fc084e..468027665ede 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -957,8 +957,8 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) pm_runtime_put(&host->pd->dev); clk_disable(host->hclk); host->power = false; - if (p->down_pwr && ios->power_mode == MMC_POWER_OFF) - p->down_pwr(host->pd); + if (p->set_pwr && ios->power_mode == MMC_POWER_OFF) + p->set_pwr(host->pd, 0); } host->state = STATE_IDLE; return; From 7d17baa05da6a2e64ee15011cdf4319bd3e0ff61 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 20 Apr 2012 18:27:13 +0200 Subject: [PATCH 24/62] mmc: sh_mmcif: add regulator support Add regulator support to the sh_mmcif driver, but also preserve the current power-callback. Also note, that the card power is not switched off during clock gating periods, hence there's no need to power it on every time the card is re-activated. Signed-off-by: Guennadi Liakhovetski Reviwed-by: Mark Brown Reviewed-by: Simon Horman Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 38 ++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 468027665ede..204bcedc2164 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -923,10 +923,22 @@ static int sh_mmcif_clk_update(struct sh_mmcif_host *host) return ret; } +static void sh_mmcif_set_power(struct sh_mmcif_host *host, struct mmc_ios *ios) +{ + struct sh_mmcif_plat_data *pd = host->pd->dev.platform_data; + struct mmc_host *mmc = host->mmc; + + if (pd->set_pwr) + pd->set_pwr(host->pd, ios->power_mode != MMC_POWER_OFF); + if (!IS_ERR(mmc->supply.vmmc)) + /* Errors ignored... */ + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, + ios->power_mode ? ios->vdd : 0); +} + static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct sh_mmcif_host *host = mmc_priv(mmc); - struct sh_mmcif_plat_data *p = host->pd->dev.platform_data; unsigned long flags; spin_lock_irqsave(&host->lock, flags); @@ -944,6 +956,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) sh_mmcif_request_dma(host, host->pd->dev.platform_data); host->card_present = true; } + sh_mmcif_set_power(host, ios); } else if (ios->power_mode == MMC_POWER_OFF || !ios->clock) { /* clock stop */ sh_mmcif_clock_control(host, 0); @@ -957,8 +970,8 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) pm_runtime_put(&host->pd->dev); clk_disable(host->hclk); host->power = false; - if (p->set_pwr && ios->power_mode == MMC_POWER_OFF) - p->set_pwr(host->pd, 0); + if (ios->power_mode == MMC_POWER_OFF) + sh_mmcif_set_power(host, ios); } host->state = STATE_IDLE; return; @@ -966,8 +979,6 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (ios->clock) { if (!host->power) { - if (p->set_pwr) - p->set_pwr(host->pd, ios->power_mode); sh_mmcif_clk_update(host); pm_runtime_get_sync(&host->pd->dev); host->power = true; @@ -1251,6 +1262,19 @@ static void mmcif_timeout_work(struct work_struct *work) mmc_request_done(host->mmc, mrq); } +static void sh_mmcif_init_ocr(struct sh_mmcif_host *host) +{ + struct sh_mmcif_plat_data *pd = host->pd->dev.platform_data; + struct mmc_host *mmc = host->mmc; + + mmc_regulator_get_supply(mmc); + + if (!mmc->ocr_avail) + mmc->ocr_avail = pd->ocr; + else if (pd->ocr) + dev_warn(mmc_dev(mmc), "Platform OCR mask is ignored\n"); +} + static int __devinit sh_mmcif_probe(struct platform_device *pdev) { int ret = 0, irq[2]; @@ -1298,8 +1322,8 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) spin_lock_init(&host->lock); mmc->ops = &sh_mmcif_ops; - if (pd->ocr) - mmc->ocr_avail = pd->ocr; + sh_mmcif_init_ocr(host); + mmc->caps = MMC_CAP_MMC_HIGHSPEED; if (pd->caps) mmc->caps |= pd->caps; From 27410ee7e391ce650d6d0242805f080599be7ad7 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 1 May 2012 15:40:15 +0200 Subject: [PATCH 25/62] mmc: core: use a more generic name for slot function types and fields struct mmc_host::hotplug is becoming a generic hook for slot functions. Rename it accordingly. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/core/host.c | 2 ++ drivers/mmc/core/slot-gpio.c | 8 ++++---- include/linux/mmc/host.h | 17 ++++++++++++++--- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 91c84c7a1829..b8c5290571f1 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -327,6 +327,8 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) mmc_host_clk_init(host); + host->slot.cd_irq = -EINVAL; + spin_lock_init(&host->lock); init_waitqueue_head(&host->wq); INIT_DELAYED_WORK(&host->detect, mmc_rescan); diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c index 979671053436..468e5a0e5126 100644 --- a/drivers/mmc/core/slot-gpio.c +++ b/drivers/mmc/core/slot-gpio.c @@ -56,8 +56,8 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio) goto eirqreq; ctx->cd_gpio = gpio; - host->hotplug.irq = irq; - host->hotplug.handler_priv = ctx; + host->slot.cd_irq = irq; + host->slot.handler_priv = ctx; return 0; @@ -71,12 +71,12 @@ EXPORT_SYMBOL(mmc_gpio_request_cd); void mmc_gpio_free_cd(struct mmc_host *host) { - struct mmc_gpio *ctx = host->hotplug.handler_priv; + struct mmc_gpio *ctx = host->slot.handler_priv; if (!ctx) return; - free_irq(host->hotplug.irq, host); + free_irq(host->slot.cd_irq, host); gpio_free(ctx->cd_gpio); kfree(ctx); } diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 9deb725799e7..90b6a38b0374 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -150,8 +150,19 @@ struct mmc_async_req { int (*err_check) (struct mmc_card *, struct mmc_async_req *); }; -struct mmc_hotplug { - unsigned int irq; +/** + * struct mmc_slot - MMC slot functions + * + * @cd_irq: MMC/SD-card slot hotplug detection IRQ or -EINVAL + * @handler_priv: MMC/SD-card slot context + * + * Some MMC/SD host controllers implement slot-functions like card and + * write-protect detection natively. However, a large number of controllers + * leave these functions to the CPU. This struct provides a hook to attach + * such slot-function drivers. + */ +struct mmc_slot { + int cd_irq; void *handler_priv; }; @@ -297,7 +308,7 @@ struct mmc_host { struct delayed_work detect; int detect_change; /* card detect flag */ - struct mmc_hotplug hotplug; + struct mmc_slot slot; const struct mmc_bus_ops *bus_ops; /* current bus driver */ unsigned int bus_refs; /* reference counter */ From 5c08d7fae0815cd163a98e05c8d94fc0de77ff67 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 1 May 2012 15:49:52 +0200 Subject: [PATCH 26/62] mmc: add two capability flags for CD and WP signal polarity To handle CD and WP SD/MMC slot pins we need generic flags to specify their polarity. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- include/linux/mmc/host.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 90b6a38b0374..c1a03eed1d17 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -256,6 +256,8 @@ struct mmc_host { #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 */ +#define MMC_CAP2_CD_ACTIVE_HIGH (1 << 10) /* Card-detect signal active high */ +#define MMC_CAP2_RO_ACTIVE_HIGH (1 << 11) /* Write-protect signal active high */ mmc_pm_flag_t pm_caps; /* supported pm features */ unsigned int power_notify_type; From befe4048d8d20483a62636e20f3dbffebf85a1c1 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 1 May 2012 16:27:25 +0200 Subject: [PATCH 27/62] mmc: add CD GPIO polling support to slot functions A simple extension of mmc slot functions add support for CD GPIO polling for cases where the GPIO cannot produce interrupts, or where this is not desired for some reason. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/core/slot-gpio.c | 56 +++++++++++++++++++++++++++-------- include/linux/mmc/slot-gpio.h | 2 ++ 2 files changed, 45 insertions(+), 13 deletions(-) diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c index 468e5a0e5126..92cba02c04be 100644 --- a/drivers/mmc/core/slot-gpio.c +++ b/drivers/mmc/core/slot-gpio.c @@ -18,7 +18,7 @@ #include struct mmc_gpio { - unsigned int cd_gpio; + int cd_gpio; char cd_label[0]; }; @@ -29,6 +29,18 @@ static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id) return IRQ_HANDLED; } +int mmc_gpio_get_cd(struct mmc_host *host) +{ + struct mmc_gpio *ctx = host->slot.handler_priv; + + if (!ctx || !gpio_is_valid(ctx->cd_gpio)) + return -ENOSYS; + + return !gpio_get_value_cansleep(ctx->cd_gpio) ^ + !!(host->caps2 & MMC_CAP2_CD_ACTIVE_HIGH); +} +EXPORT_SYMBOL(mmc_gpio_get_cd); + int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio) { size_t len = strlen(dev_name(host->parent)) + 4; @@ -36,9 +48,6 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio) int irq = gpio_to_irq(gpio); int ret; - if (irq < 0) - return irq; - ctx = kmalloc(sizeof(*ctx) + len, GFP_KERNEL); if (!ctx) return -ENOMEM; @@ -49,20 +58,32 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio) if (ret < 0) goto egpioreq; - ret = request_threaded_irq(irq, NULL, mmc_gpio_cd_irqt, + /* + * Even if gpio_to_irq() returns a valid IRQ number, the platform might + * still prefer to poll, e.g., because that IRQ number is already used + * by another unit and cannot be shared. + */ + if (irq >= 0 && host->caps & MMC_CAP_NEEDS_POLL) + irq = -EINVAL; + + if (irq >= 0) { + ret = request_threaded_irq(irq, NULL, mmc_gpio_cd_irqt, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, ctx->cd_label, host); - if (ret < 0) - goto eirqreq; + if (ret < 0) + irq = ret; + } + + host->slot.cd_irq = irq; + + if (irq < 0) + host->caps |= MMC_CAP_NEEDS_POLL; ctx->cd_gpio = gpio; - host->slot.cd_irq = irq; host->slot.handler_priv = ctx; return 0; -eirqreq: - gpio_free(gpio); egpioreq: kfree(ctx); return ret; @@ -72,12 +93,21 @@ EXPORT_SYMBOL(mmc_gpio_request_cd); void mmc_gpio_free_cd(struct mmc_host *host) { struct mmc_gpio *ctx = host->slot.handler_priv; + int gpio; - if (!ctx) + if (!ctx || !gpio_is_valid(ctx->cd_gpio)) return; - free_irq(host->slot.cd_irq, host); - gpio_free(ctx->cd_gpio); + if (host->slot.cd_irq >= 0) { + free_irq(host->slot.cd_irq, host); + host->slot.cd_irq = -EINVAL; + } + + gpio = ctx->cd_gpio; + ctx->cd_gpio = -EINVAL; + + gpio_free(gpio); + host->slot.handler_priv = NULL; kfree(ctx); } EXPORT_SYMBOL(mmc_gpio_free_cd); diff --git a/include/linux/mmc/slot-gpio.h b/include/linux/mmc/slot-gpio.h index edfaa3254373..1a977d7ba3ba 100644 --- a/include/linux/mmc/slot-gpio.h +++ b/include/linux/mmc/slot-gpio.h @@ -12,6 +12,8 @@ #define MMC_SLOT_GPIO_H struct mmc_host; + +int mmc_gpio_get_cd(struct mmc_host *host); int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio); void mmc_gpio_free_cd(struct mmc_host *host); From a7d1a1ebd8f5858a812ac3d5fbbc178b4959a63b Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 1 May 2012 16:51:38 +0200 Subject: [PATCH 28/62] mmc: core: convert slot functions to managed allocation This prepares for the addition of further slot functions. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/core/host.c | 2 ++ drivers/mmc/core/slot-gpio.c | 51 +++++++++++++++++++++++++++--------- include/linux/mmc/host.h | 3 +++ 3 files changed, 43 insertions(+), 13 deletions(-) diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index b8c5290571f1..74cf29a504f4 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -32,6 +32,7 @@ static void mmc_host_classdev_release(struct device *dev) { struct mmc_host *host = cls_dev_to_mmc_host(dev); + mutex_destroy(&host->slot.lock); kfree(host); } @@ -327,6 +328,7 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) mmc_host_clk_init(host); + mutex_init(&host->slot.lock); host->slot.cd_irq = -EINVAL; spin_lock_init(&host->lock); diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c index 92cba02c04be..41689da14b8d 100644 --- a/drivers/mmc/core/slot-gpio.c +++ b/drivers/mmc/core/slot-gpio.c @@ -29,6 +29,34 @@ static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id) return IRQ_HANDLED; } +static int mmc_gpio_alloc(struct mmc_host *host) +{ + size_t len = strlen(dev_name(host->parent)) + 4; + struct mmc_gpio *ctx; + + mutex_lock(&host->slot.lock); + + ctx = host->slot.handler_priv; + if (!ctx) { + /* + * devm_kzalloc() can be called after device_initialize(), even + * before device_add(), i.e., between mmc_alloc_host() and + * mmc_add_host() + */ + ctx = devm_kzalloc(&host->class_dev, sizeof(*ctx) + len, + GFP_KERNEL); + if (ctx) { + snprintf(ctx->cd_label, len, "%s cd", dev_name(host->parent)); + ctx->cd_gpio = -EINVAL; + host->slot.handler_priv = ctx; + } + } + + mutex_unlock(&host->slot.lock); + + return ctx ? 0 : -ENOMEM; +} + int mmc_gpio_get_cd(struct mmc_host *host) { struct mmc_gpio *ctx = host->slot.handler_priv; @@ -43,20 +71,24 @@ EXPORT_SYMBOL(mmc_gpio_get_cd); int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio) { - size_t len = strlen(dev_name(host->parent)) + 4; struct mmc_gpio *ctx; int irq = gpio_to_irq(gpio); int ret; - ctx = kmalloc(sizeof(*ctx) + len, GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ret = mmc_gpio_alloc(host); + if (ret < 0) + return ret; - snprintf(ctx->cd_label, len, "%s cd", dev_name(host->parent)); + ctx = host->slot.handler_priv; ret = gpio_request_one(gpio, GPIOF_DIR_IN, ctx->cd_label); if (ret < 0) - goto egpioreq; + /* + * don't bother freeing memory. It might still get used by other + * slot functions, in any case it will be freed, when the device + * is destroyed. + */ + return ret; /* * Even if gpio_to_irq() returns a valid IRQ number, the platform might @@ -80,13 +112,8 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio) host->caps |= MMC_CAP_NEEDS_POLL; ctx->cd_gpio = gpio; - host->slot.handler_priv = ctx; return 0; - -egpioreq: - kfree(ctx); - return ret; } EXPORT_SYMBOL(mmc_gpio_request_cd); @@ -107,7 +134,5 @@ void mmc_gpio_free_cd(struct mmc_host *host) ctx->cd_gpio = -EINVAL; gpio_free(gpio); - host->slot.handler_priv = NULL; - kfree(ctx); } EXPORT_SYMBOL(mmc_gpio_free_cd); diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index c1a03eed1d17..65c64ee578a7 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -11,6 +11,7 @@ #define LINUX_MMC_HOST_H #include +#include #include #include #include @@ -154,6 +155,7 @@ struct mmc_async_req { * struct mmc_slot - MMC slot functions * * @cd_irq: MMC/SD-card slot hotplug detection IRQ or -EINVAL + * @lock: protect the @handler_priv pointer * @handler_priv: MMC/SD-card slot context * * Some MMC/SD host controllers implement slot-functions like card and @@ -163,6 +165,7 @@ struct mmc_async_req { */ struct mmc_slot { int cd_irq; + struct mutex lock; void *handler_priv; }; From 5aa7dad305594ea30d21e23b3036565042adf50c Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 1 May 2012 16:59:38 +0200 Subject: [PATCH 29/62] mmc: core: add WP pin handler to slot functions Card Write-Protect pin is often implemented, using a GPIO, which makes it simple to provide a generic handler for it. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/core/slot-gpio.c | 52 ++++++++++++++++++++++++++++++++++- include/linux/mmc/slot-gpio.h | 4 +++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c index 41689da14b8d..058242916cef 100644 --- a/drivers/mmc/core/slot-gpio.c +++ b/drivers/mmc/core/slot-gpio.c @@ -18,7 +18,9 @@ #include struct mmc_gpio { + int ro_gpio; int cd_gpio; + char *ro_label; char cd_label[0]; }; @@ -43,11 +45,14 @@ static int mmc_gpio_alloc(struct mmc_host *host) * before device_add(), i.e., between mmc_alloc_host() and * mmc_add_host() */ - ctx = devm_kzalloc(&host->class_dev, sizeof(*ctx) + len, + ctx = devm_kzalloc(&host->class_dev, sizeof(*ctx) + 2 * len, GFP_KERNEL); if (ctx) { + ctx->ro_label = ctx->cd_label + len; snprintf(ctx->cd_label, len, "%s cd", dev_name(host->parent)); + snprintf(ctx->ro_label, len, "%s ro", dev_name(host->parent)); ctx->cd_gpio = -EINVAL; + ctx->ro_gpio = -EINVAL; host->slot.handler_priv = ctx; } } @@ -57,6 +62,18 @@ static int mmc_gpio_alloc(struct mmc_host *host) return ctx ? 0 : -ENOMEM; } +int mmc_gpio_get_ro(struct mmc_host *host) +{ + struct mmc_gpio *ctx = host->slot.handler_priv; + + if (!ctx || !gpio_is_valid(ctx->ro_gpio)) + return -ENOSYS; + + return !gpio_get_value_cansleep(ctx->ro_gpio) ^ + !!(host->caps2 & MMC_CAP2_RO_ACTIVE_HIGH); +} +EXPORT_SYMBOL(mmc_gpio_get_ro); + int mmc_gpio_get_cd(struct mmc_host *host) { struct mmc_gpio *ctx = host->slot.handler_priv; @@ -69,6 +86,24 @@ int mmc_gpio_get_cd(struct mmc_host *host) } EXPORT_SYMBOL(mmc_gpio_get_cd); +int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio) +{ + struct mmc_gpio *ctx; + int ret; + + if (!gpio_is_valid(gpio)) + return -EINVAL; + + ret = mmc_gpio_alloc(host); + if (ret < 0) + return ret; + + ctx = host->slot.handler_priv; + + return gpio_request_one(gpio, GPIOF_DIR_IN, ctx->ro_label); +} +EXPORT_SYMBOL(mmc_gpio_request_ro); + int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio) { struct mmc_gpio *ctx; @@ -117,6 +152,21 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio) } EXPORT_SYMBOL(mmc_gpio_request_cd); +void mmc_gpio_free_ro(struct mmc_host *host) +{ + struct mmc_gpio *ctx = host->slot.handler_priv; + int gpio; + + if (!ctx || !gpio_is_valid(ctx->ro_gpio)) + return; + + gpio = ctx->ro_gpio; + ctx->ro_gpio = -EINVAL; + + gpio_free(gpio); +} +EXPORT_SYMBOL(mmc_gpio_free_ro); + void mmc_gpio_free_cd(struct mmc_host *host) { struct mmc_gpio *ctx = host->slot.handler_priv; diff --git a/include/linux/mmc/slot-gpio.h b/include/linux/mmc/slot-gpio.h index 1a977d7ba3ba..7d88d27bfafa 100644 --- a/include/linux/mmc/slot-gpio.h +++ b/include/linux/mmc/slot-gpio.h @@ -13,6 +13,10 @@ struct mmc_host; +int mmc_gpio_get_ro(struct mmc_host *host); +int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio); +void mmc_gpio_free_ro(struct mmc_host *host); + int mmc_gpio_get_cd(struct mmc_host *host); int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio); void mmc_gpio_free_cd(struct mmc_host *host); From 02cb3221d5bb351ad9f7469453dcca7594a0fabf Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 23 May 2012 10:44:37 +0200 Subject: [PATCH 30/62] mmc: tmio: support caps2 flags Allow tmio mmc glue drivers to pass mmc_host::caps2 flags down to the mmc layer. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/tmio_mmc_pio.c | 1 + include/linux/mfd/tmio.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index b204012fe7a9..f8df02111794 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -951,6 +951,7 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host, mmc->ops = &tmio_mmc_ops; mmc->caps = MMC_CAP_4_BIT_DATA | pdata->capabilities; + mmc->caps2 = pdata->capabilities2; mmc->max_segs = 32; mmc->max_blk_size = 512; mmc->max_blk_count = (PAGE_CACHE_SIZE / mmc->max_blk_size) * diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h index b332c4c7857b..d83af39815ab 100644 --- a/include/linux/mfd/tmio.h +++ b/include/linux/mfd/tmio.h @@ -101,6 +101,7 @@ struct tmio_mmc_host; struct tmio_mmc_data { unsigned int hclk; unsigned long capabilities; + unsigned long capabilities2; unsigned long flags; u32 ocr_mask; /* available voltages */ struct tmio_mmc_dma *dma; From d7d8d500bc03891c4a86da49858c46e2db256581 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 23 May 2012 11:05:33 +0200 Subject: [PATCH 31/62] mmc: sh_mobile_sdhi: support caps2 flags Let SDHI platforms specify mmc_host::caps2 flags in their platform data. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mobile_sdhi.c | 1 + include/linux/mmc/sh_mobile_sdhi.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index 42f07fa6e043..1e7c5c46201d 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -162,6 +162,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->capabilities2 |= p->tmio_caps2; mmc_data->cd_gpio = p->cd_gpio; if (p->dma_slave_tx > 0 && p->dma_slave_rx > 0) { diff --git a/include/linux/mmc/sh_mobile_sdhi.h b/include/linux/mmc/sh_mobile_sdhi.h index e94e620aeddc..b65679ffa880 100644 --- a/include/linux/mmc/sh_mobile_sdhi.h +++ b/include/linux/mmc/sh_mobile_sdhi.h @@ -23,6 +23,7 @@ struct sh_mobile_sdhi_info { int dma_slave_rx; unsigned long tmio_flags; unsigned long tmio_caps; + unsigned long tmio_caps2; u32 tmio_ocr_mask; /* available MMC voltages */ unsigned int cd_gpio; struct tmio_mmc_data *pdata; From 3071cafb7f6f9cbb52b1b7eb308c8b45cae0dcf8 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 1 May 2012 17:11:56 +0200 Subject: [PATCH 32/62] mmc: tmio: use generic GPIO CD and WP handlers The tmio-mmc driver is already using the generic GPIO CD handler in IRQ mode. This patch adds support for CD polling mode and also checks for availability of a WP GPIO. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/tmio_mmc_pio.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index f8df02111794..0d8a9bbe30be 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -875,6 +875,9 @@ static int tmio_mmc_get_ro(struct mmc_host *mmc) { struct tmio_mmc_host *host = mmc_priv(mmc); struct tmio_mmc_data *pdata = host->pdata; + int ret = mmc_gpio_get_ro(mmc); + if (ret >= 0) + return ret; return !((pdata->flags & TMIO_MMC_WRPROTECT_DISABLE) || (sd_ctrl_read32(host, CTL_STATUS) & TMIO_STAT_WRPROTECT)); @@ -884,6 +887,9 @@ static int tmio_mmc_get_cd(struct mmc_host *mmc) { struct tmio_mmc_host *host = mmc_priv(mmc); struct tmio_mmc_data *pdata = host->pdata; + int ret = mmc_gpio_get_cd(mmc); + if (ret >= 0) + return ret; if (!pdata->get_cd) return -ENOSYS; From c7bb4487a3474c03986758595fcae1cfb771b3b0 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 1 May 2012 18:03:43 +0200 Subject: [PATCH 33/62] mmc: sdhi: add OF support, make platform data optional Add primitive support for OF to the SDHI TMIO glue, which also makes it necessary to be able to run without platform data. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mobile_sdhi.c | 35 ++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index 1e7c5c46201d..a842939e4655 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -133,12 +134,14 @@ 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, &sdhi_ops); - if (ret) - goto einit; + if (p) { + p->pdata = mmc_data; + if (p->init) { + ret = p->init(pdev, &sdhi_ops); + if (ret) + goto einit; + } } snprintf(clk_name, sizeof(clk_name), "sdhi%d", pdev->id); @@ -149,10 +152,6 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev) goto eclkget; } - if (p->set_pwr) - mmc_data->set_pwr = sh_mobile_sdhi_set_pwr; - if (p->get_cd) - mmc_data->get_cd = sh_mobile_sdhi_get_cd; mmc_data->clk_enable = sh_mobile_sdhi_clk_enable; mmc_data->clk_disable = sh_mobile_sdhi_clk_disable; mmc_data->capabilities = MMC_CAP_MMC_HIGHSPEED; @@ -164,6 +163,10 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev) mmc_data->capabilities |= p->tmio_caps; mmc_data->capabilities2 |= p->tmio_caps2; mmc_data->cd_gpio = p->cd_gpio; + if (p->set_pwr) + mmc_data->set_pwr = sh_mobile_sdhi_set_pwr; + if (p->get_cd) + mmc_data->get_cd = sh_mobile_sdhi_get_cd; if (p->dma_slave_tx > 0 && p->dma_slave_rx > 0) { priv->param_tx.slave_id = p->dma_slave_tx; @@ -269,7 +272,7 @@ eirq_card_detect: eprobe: clk_put(priv->clk); eclkget: - if (p->cleanup) + if (p && p->cleanup) p->cleanup(pdev); einit: kfree(priv); @@ -284,7 +287,8 @@ static int sh_mobile_sdhi_remove(struct platform_device *pdev) struct sh_mobile_sdhi_info *p = pdev->dev.platform_data; int i = 0, irq; - p->pdata = NULL; + if (p) + p->pdata = NULL; tmio_mmc_host_remove(host); @@ -297,7 +301,7 @@ static int sh_mobile_sdhi_remove(struct platform_device *pdev) clk_put(priv->clk); - if (p->cleanup) + if (p && p->cleanup) p->cleanup(pdev); kfree(priv); @@ -312,11 +316,18 @@ static const struct dev_pm_ops tmio_mmc_dev_pm_ops = { .runtime_resume = tmio_mmc_host_runtime_resume, }; +static const struct of_device_id sh_mobile_sdhi_of_match[] = { + { .compatible = "renesas,shmobile-sdhi" }, + { } +}; +MODULE_DEVICE_TABLE(of, sh_mobile_sdhi_of_match); + static struct platform_driver sh_mobile_sdhi_driver = { .driver = { .name = "sh_mobile_sdhi", .owner = THIS_MODULE, .pm = &tmio_mmc_dev_pm_ops, + .of_match_table = sh_mobile_sdhi_of_match, }, .probe = sh_mobile_sdhi_probe, .remove = __devexit_p(sh_mobile_sdhi_remove), From bf68a812f06ca40bccfa2e792055141f2c3948c7 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 1 May 2012 18:18:16 +0200 Subject: [PATCH 34/62] mmc: sh-mmcif: add OF support, make platform data optional Add primitive OF support to the sh-mmcif driver, which also makes it necessary to be able to run without platform data. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 204bcedc2164..68b31f7c290b 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -54,6 +54,7 @@ #include #include #include +#include #include #include #include @@ -384,6 +385,9 @@ static void sh_mmcif_request_dma(struct sh_mmcif_host *host, struct sh_dmae_slave *tx, *rx; host->dma_active = false; + if (!pdata) + return; + /* We can only either use DMA for both Tx and Rx or not use it at all */ if (pdata->dma) { dev_warn(&host->pd->dev, @@ -444,13 +448,14 @@ static void sh_mmcif_release_dma(struct sh_mmcif_host *host) static void sh_mmcif_clock_control(struct sh_mmcif_host *host, unsigned int clk) { struct sh_mmcif_plat_data *p = host->pd->dev.platform_data; + bool sup_pclk = p ? p->sup_pclk : false; sh_mmcif_bitclr(host, MMCIF_CE_CLK_CTRL, CLK_ENABLE); sh_mmcif_bitclr(host, MMCIF_CE_CLK_CTRL, CLK_CLEAR); if (!clk) return; - if (p->sup_pclk && clk == host->clk) + if (sup_pclk && clk == host->clk) sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_SUP_PCLK); else sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_CLEAR & @@ -928,7 +933,7 @@ static void sh_mmcif_set_power(struct sh_mmcif_host *host, struct mmc_ios *ios) struct sh_mmcif_plat_data *pd = host->pd->dev.platform_data; struct mmc_host *mmc = host->mmc; - if (pd->set_pwr) + if (pd && pd->set_pwr) pd->set_pwr(host->pd, ios->power_mode != MMC_POWER_OFF); if (!IS_ERR(mmc->supply.vmmc)) /* Errors ignored... */ @@ -996,7 +1001,7 @@ static int sh_mmcif_get_cd(struct mmc_host *mmc) struct sh_mmcif_host *host = mmc_priv(mmc); struct sh_mmcif_plat_data *p = host->pd->dev.platform_data; - if (!p->get_cd) + if (!p || !p->get_cd) return -ENOSYS; else return p->get_cd(host->pd); @@ -1269,6 +1274,9 @@ static void sh_mmcif_init_ocr(struct sh_mmcif_host *host) mmc_regulator_get_supply(mmc); + if (!pd) + return; + if (!mmc->ocr_avail) mmc->ocr_avail = pd->ocr; else if (pd->ocr) @@ -1285,11 +1293,6 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) void __iomem *reg; char clk_name[8]; - if (!pd) { - dev_err(&pdev->dev, "sh_mmcif plat data error.\n"); - return -ENXIO; - } - irq[0] = platform_get_irq(pdev, 0); irq[1] = platform_get_irq(pdev, 1); if (irq[0] < 0 || irq[1] < 0) { @@ -1325,7 +1328,7 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) sh_mmcif_init_ocr(host); mmc->caps = MMC_CAP_MMC_HIGHSPEED; - if (pd->caps) + if (pd && pd->caps) mmc->caps |= pd->caps; mmc->max_segs = 32; mmc->max_blk_size = 512; @@ -1462,6 +1465,12 @@ static int sh_mmcif_resume(struct device *dev) #define sh_mmcif_resume NULL #endif /* CONFIG_PM */ +static const struct of_device_id mmcif_of_match[] = { + { .compatible = "renesas,sh-mmcif" }, + { } +}; +MODULE_DEVICE_TABLE(of, mmcif_of_match); + static const struct dev_pm_ops sh_mmcif_dev_pm_ops = { .suspend = sh_mmcif_suspend, .resume = sh_mmcif_resume, @@ -1473,6 +1482,8 @@ static struct platform_driver sh_mmcif_driver = { .driver = { .name = DRIVER_NAME, .pm = &sh_mmcif_dev_pm_ops, + .owner = THIS_MODULE, + .of_match_table = mmcif_of_match, }, }; From d9adcc12860d76cf3401c6ab7c0406b15b356b7a Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 14 Jun 2012 10:17:39 +0200 Subject: [PATCH 35/62] mmc: prohibit card detection when host is not ready Currently mmc host drivers have to decide whether to enable card detection before calling mmc_add_host() -- in which case a card insertion event can arrive before the host has been completely initialised -- or after mmc_add_host(), in which case the initial card detection can be problematic. This patch adds an explicit indication of when card detection should not be carried out. With it in place enabling card detection before calling mmc_add_host() should be safe. Similarly, disabling it again after calling mmc_remove_host() will avoid any races. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/core/core.c | 2 ++ drivers/mmc/core/host.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 8d00aef9523e..9503cabc96f1 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2107,6 +2107,7 @@ void mmc_rescan(struct work_struct *work) void mmc_start_host(struct mmc_host *host) { host->f_init = max(freqs[0], host->f_min); + host->rescan_disable = 0; mmc_power_up(host); mmc_detect_change(host, 0); } @@ -2120,6 +2121,7 @@ void mmc_stop_host(struct mmc_host *host) spin_unlock_irqrestore(&host->lock, flags); #endif + host->rescan_disable = 1; cancel_delayed_work_sync(&host->detect); mmc_flush_scheduled_work(); diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 74cf29a504f4..597f189b4427 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -313,6 +313,8 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) if (!host) return NULL; + /* scanning will be enabled when we're ready */ + host->rescan_disable = 1; spin_lock(&mmc_host_lock); err = idr_get_new(&mmc_host_idr, host, &host->index); spin_unlock(&mmc_host_lock); From e480606ad43bb72fd82a9bd99cdcf21829a6e9c0 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 14 Jun 2012 14:24:35 +0200 Subject: [PATCH 36/62] mmc: sh_mmcif: support generic card-detection Extend the sh_mmcif driver to support GPIO card detection, provided by the slot function module. The original .get_cd() platform callback is also preserved for now. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 18 ++++++++++++++++++ include/linux/mmc/sh_mmcif.h | 2 ++ 2 files changed, 20 insertions(+) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 68b31f7c290b..b2af7136cd27 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -54,6 +54,7 @@ #include #include #include +#include #include #include #include @@ -1000,6 +1001,10 @@ static int sh_mmcif_get_cd(struct mmc_host *mmc) { struct sh_mmcif_host *host = mmc_priv(mmc); struct sh_mmcif_plat_data *p = host->pd->dev.platform_data; + int ret = mmc_gpio_get_cd(mmc); + + if (ret >= 0) + return ret; if (!p || !p->get_cd) return -ENOSYS; @@ -1372,6 +1377,12 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) goto ereqirq1; } + if (pd && pd->use_cd_gpio) { + ret = mmc_gpio_request_cd(mmc, pd->cd_gpio); + if (ret < 0) + goto erqcd; + } + clk_disable(host->hclk); ret = mmc_add_host(mmc); if (ret < 0) @@ -1385,6 +1396,9 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) return ret; emmcaddh: + if (pd && pd->use_cd_gpio) + mmc_gpio_free_cd(mmc); +erqcd: free_irq(irq[1], host); ereqirq1: free_irq(irq[0], host); @@ -1405,6 +1419,7 @@ ealloch: static int __devexit sh_mmcif_remove(struct platform_device *pdev) { struct sh_mmcif_host *host = platform_get_drvdata(pdev); + struct sh_mmcif_plat_data *pd = pdev->dev.platform_data; int irq[2]; host->dying = true; @@ -1413,6 +1428,9 @@ static int __devexit sh_mmcif_remove(struct platform_device *pdev) dev_pm_qos_hide_latency_limit(&pdev->dev); + if (pd && pd->use_cd_gpio) + mmc_gpio_free_cd(host->mmc); + mmc_remove_host(host->mmc); sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); diff --git a/include/linux/mmc/sh_mmcif.h b/include/linux/mmc/sh_mmcif.h index 05f0e3db1c12..c2f73cbb4d5c 100644 --- a/include/linux/mmc/sh_mmcif.h +++ b/include/linux/mmc/sh_mmcif.h @@ -44,6 +44,8 @@ struct sh_mmcif_plat_data { struct sh_mmcif_dma *dma; /* Deprecated. Instead */ unsigned int slave_id_tx; /* use embedded slave_id_[tr]x */ unsigned int slave_id_rx; + bool use_cd_gpio : 1; + unsigned int cd_gpio; u8 sup_pclk; /* 1 :SH7757, 0: SH7724/SH7372 */ unsigned long caps; u32 ocr; From bad37e1ac6b1a73ed30702b24a45c27c4f53aada Mon Sep 17 00:00:00 2001 From: Philip Rakity Date: Sun, 27 May 2012 18:36:44 -0700 Subject: [PATCH 37/62] mmc: sdhci: if MAX_CURRENT is 0, try getting current from regulator The sd host controller spec indicates the the MAX_CURRENT value may be returned as 0. In this case other methods need to be used to return the current. If 0 is returned and there is a regulator, ask the regulator for how much current is available. Signed-off-by: Philip Rakity Signed-off-by: Mark F. Brown Reviewed-by: Aaron Lu Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 28 ++++++++++++++++++++++------ drivers/mmc/host/sdhci.h | 1 + 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index f4b8b4db3a9a..a0853d03b330 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2837,6 +2837,13 @@ int sdhci_add_host(struct sdhci_host *host) SDHCI_RETUNING_MODE_SHIFT; ocr_avail = 0; + + host->vmmc = regulator_get(mmc_dev(mmc), "vmmc"); + if (IS_ERR(host->vmmc)) { + pr_info("%s: no vmmc regulator found\n", mmc_hostname(mmc)); + host->vmmc = NULL; + } + /* * According to SD Host Controller spec v3.00, if the Host System * can afford more than 150mA, Host Driver should set XPC to 1. Also @@ -2845,6 +2852,21 @@ int sdhci_add_host(struct sdhci_host *host) * value. */ max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT); + if (!max_current_caps && host->vmmc) { + u32 curr = regulator_get_current_limit(host->vmmc); + if (curr > 0) { + + /* convert to SDHCI_MAX_CURRENT format */ + curr = curr/1000; /* convert to mA */ + curr = curr/SDHCI_MAX_CURRENT_MULTIPLIER; + + curr = min_t(u32, curr, SDHCI_MAX_CURRENT_LIMIT); + max_current_caps = + (curr << SDHCI_MAX_CURRENT_330_SHIFT) | + (curr << SDHCI_MAX_CURRENT_300_SHIFT) | + (curr << SDHCI_MAX_CURRENT_180_SHIFT); + } + } if (caps[0] & SDHCI_CAN_VDD_330) { int max_current_330; @@ -2995,12 +3017,6 @@ int sdhci_add_host(struct sdhci_host *host) if (ret) goto untasklet; - host->vmmc = regulator_get(mmc_dev(mmc), "vmmc"); - if (IS_ERR(host->vmmc)) { - pr_info("%s: no vmmc regulator found\n", mmc_hostname(mmc)); - host->vmmc = NULL; - } - sdhci_init(host, 0); #ifdef CONFIG_MMC_DEBUG diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index f761f23d2a28..97653ea8942b 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -205,6 +205,7 @@ #define SDHCI_CAPABILITIES_1 0x44 #define SDHCI_MAX_CURRENT 0x48 +#define SDHCI_MAX_CURRENT_LIMIT 0xFF #define SDHCI_MAX_CURRENT_330_MASK 0x0000FF #define SDHCI_MAX_CURRENT_330_SHIFT 0 #define SDHCI_MAX_CURRENT_300_MASK 0x00FF00 From 0aa6770000bafa65c17cf44b6619d328d4fc79b3 Mon Sep 17 00:00:00 2001 From: Philip Rakity Date: Sun, 27 May 2012 18:36:33 -0700 Subject: [PATCH 38/62] mmc: sdhci: only set 200mA support for 1.8v if 200mA is available max_current_caps can return 0 if not available from the sd controller. If no regulator is present or the regulator specifies a current less then 200ma, we no longer still set the 200mA caps bit anyway. Signed-off-by: Philip Rakity Reviewed-by: Aaron Lu Signed-off-by: Chris Ball --- drivers/mmc/core/sd.c | 23 ++++++++++++----------- drivers/mmc/host/sdhci.c | 2 +- include/linux/mmc/card.h | 1 + 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index b2b43f624b9e..b0b9e372f5da 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -553,13 +553,13 @@ static int sd_set_bus_speed_mode(struct mmc_card *card, u8 *status) static int sd_set_current_limit(struct mmc_card *card, u8 *status) { - int current_limit = 0; + int current_limit = SD_SET_CURRENT_NO_CHANGE; int err; /* * Current limit switch is only defined for SDR50, SDR104, and DDR50 - * bus speed modes. For other bus speed modes, we set the default - * current limit of 200mA. + * bus speed modes. For other bus speed modes, we do not change the + * current limit. */ if ((card->sd_bus_speed == UHS_SDR50_BUS_SPEED) || (card->sd_bus_speed == UHS_SDR104_BUS_SPEED) || @@ -595,17 +595,18 @@ static int sd_set_current_limit(struct mmc_card *card, u8 *status) if (card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_200) current_limit = SD_SET_CURRENT_LIMIT_200; } - } else - current_limit = SD_SET_CURRENT_LIMIT_200; + } - err = mmc_sd_switch(card, 1, 3, current_limit, status); - if (err) - return err; + if (current_limit != SD_SET_CURRENT_NO_CHANGE) { + err = mmc_sd_switch(card, 1, 3, current_limit, status); + if (err) + return err; - if (((status[15] >> 4) & 0x0F) != current_limit) - pr_warning("%s: Problem setting current limit!\n", - mmc_hostname(card->host)); + if (((status[15] >> 4) & 0x0F) != current_limit) + pr_warning("%s: Problem setting current limit!\n", + mmc_hostname(card->host)); + } return 0; } diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index a0853d03b330..8f61f8d6e0ca 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2914,7 +2914,7 @@ int sdhci_add_host(struct sdhci_host *host) mmc->caps |= MMC_CAP_MAX_CURRENT_600; else if (max_current_180 >= 400) mmc->caps |= MMC_CAP_MAX_CURRENT_400; - else + else if (max_current_180 >= 200) mmc->caps |= MMC_CAP_MAX_CURRENT_200; } diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index d76513b5b263..111aca5e97f3 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -149,6 +149,7 @@ struct sd_switch_caps { #define SD_SET_CURRENT_LIMIT_400 1 #define SD_SET_CURRENT_LIMIT_600 2 #define SD_SET_CURRENT_LIMIT_800 3 +#define SD_SET_CURRENT_NO_CHANGE (-1) #define SD_MAX_CURRENT_200 (1 << SD_SET_CURRENT_LIMIT_200) #define SD_MAX_CURRENT_400 (1 << SD_SET_CURRENT_LIMIT_400) From 68737043bb155b42d54943d25153b901fd16e535 Mon Sep 17 00:00:00 2001 From: Philip Rakity Date: Fri, 8 Jun 2012 12:26:13 -0700 Subject: [PATCH 39/62] mmc: only support voltage (vdd) that regulator agrees with If we are using a regulator the SD Host Controller and the regulator should agree about the voltages supported. Use the common subset that is supported. Signed-off-by: Philip Rakity Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 8f61f8d6e0ca..f76736b50bc7 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2844,6 +2844,23 @@ int sdhci_add_host(struct sdhci_host *host) host->vmmc = NULL; } +#ifdef CONFIG_REGULATOR + if (host->vmmc) { + ret = regulator_is_supported_voltage(host->vmmc, 3300000, + 3300000); + if ((ret <= 0) || (!(caps[0] & SDHCI_CAN_VDD_330))) + caps[0] &= ~SDHCI_CAN_VDD_330; + ret = regulator_is_supported_voltage(host->vmmc, 3000000, + 3000000); + if ((ret <= 0) || (!(caps[0] & SDHCI_CAN_VDD_300))) + caps[0] &= ~SDHCI_CAN_VDD_300; + ret = regulator_is_supported_voltage(host->vmmc, 1800000, + 1800000); + if ((ret <= 0) || (!(caps[0] & SDHCI_CAN_VDD_180))) + caps[0] &= ~SDHCI_CAN_VDD_180; + } +#endif /* CONFIG_REGULATOR */ + /* * According to SD Host Controller spec v3.00, if the Host System * can afford more than 150mA, Host Driver should set XPC to 1. Also From b63b5e819d5b21ae493c17c356018ffa98d3ee1c Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 2 Jul 2012 18:55:13 +0100 Subject: [PATCH 40/62] mmc: core: correct invalid error checking The effect of the existing code is that we continue blindly when we should warn about an invalid allocation unit. Reported-by: dcb314@hotmail.com Resolves-bug: https://bugzilla.kernel.org/show_bug.cgi?id=44061 Signed-off-by: Alan Cox Signed-off-by: Chris Ball --- drivers/mmc/core/sd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index b0b9e372f5da..33c6f26c68d0 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -244,7 +244,7 @@ static int mmc_read_ssr(struct mmc_card *card) * bitfield positions accordingly. */ au = UNSTUFF_BITS(ssr, 428 - 384, 4); - if (au > 0 || au <= 9) { + if (au > 0 && au <= 9) { card->ssr.au = 1 << (au + 4); es = UNSTUFF_BITS(ssr, 408 - 384, 16); et = UNSTUFF_BITS(ssr, 402 - 384, 6); From 4dc5a79f1350718ac31e4ab87eebce112b117a34 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Tue, 26 Jun 2012 16:38:57 +0800 Subject: [PATCH 41/62] mmc: mxs-mmc: enable regulator for mmc slot If bootloader or platform initialization code does not enable the power supply to mmc slot, we need to do it in mmc driver. Signed-off-by: Shawn Guo Signed-off-by: Chris Ball --- drivers/mmc/host/mxs-mmc.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index 277161d279b8..2247c50f9869 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -707,6 +707,7 @@ static int mxs_mmc_probe(struct platform_device *pdev) struct pinctrl *pinctrl; int ret = 0, irq_err, irq_dma; dma_cap_mask_t mask; + struct regulator *reg_vmmc; iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0); @@ -747,6 +748,16 @@ static int mxs_mmc_probe(struct platform_device *pdev) host->mmc = mmc; host->sdio_irq_en = 0; + reg_vmmc = devm_regulator_get(&pdev->dev, "vmmc"); + if (!IS_ERR(reg_vmmc)) { + ret = regulator_enable(reg_vmmc); + if (ret) { + dev_err(&pdev->dev, + "Failed to enable vmmc regulator: %d\n", ret); + goto out_mmc_free; + } + } + pinctrl = devm_pinctrl_get_select_default(&pdev->dev); if (IS_ERR(pinctrl)) { ret = PTR_ERR(pinctrl); From bd6a8c30faf19b4e7abace93fb89efd6d72607ec Mon Sep 17 00:00:00 2001 From: Philip Rakity Date: Wed, 27 Jun 2012 21:49:27 -0700 Subject: [PATCH 42/62] mmc: sdhci: Allow caps[1] to be set via SDHCI_QUIRK_MISSING_CAPS Currently only the capability_0 register can be set if SDHCI_QUIRK_MISSING_CAPS is defined. This is a problem when the capability_1 register also needs changing. Use the quirk SDHCI_QUIRK_MISSING_CAPS to allow both registers to be set. Redefining caps[1] is useful when the board design does not support 1.8v vccq so UHS modes are not available. The code that calls sdhci_add_host can then detect this condition and adjust the caps so the UHS mode will not be attempted on UHS cards. Signed-off-by: Philip Rakity Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 8 +++++--- include/linux/mmc/sdhci.h | 3 ++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index f76736b50bc7..206b52fec443 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2584,7 +2584,7 @@ EXPORT_SYMBOL_GPL(sdhci_alloc_host); int sdhci_add_host(struct sdhci_host *host) { struct mmc_host *mmc; - u32 caps[2]; + u32 caps[2] = {0, 0}; u32 max_current_caps; unsigned int ocr_avail; int ret; @@ -2614,8 +2614,10 @@ int sdhci_add_host(struct sdhci_host *host) caps[0] = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ? host->caps : sdhci_readl(host, SDHCI_CAPABILITIES); - caps[1] = (host->version >= SDHCI_SPEC_300) ? - sdhci_readl(host, SDHCI_CAPABILITIES_1) : 0; + if (host->version >= SDHCI_SPEC_300) + caps[1] = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ? + host->caps1 : + sdhci_readl(host, SDHCI_CAPABILITIES_1); if (host->quirks & SDHCI_QUIRK_FORCE_DMA) host->flags |= SDHCI_USE_SDMA; diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h index e9051e1cb1ce..d989b511c9ab 100644 --- a/include/linux/mmc/sdhci.h +++ b/include/linux/mmc/sdhci.h @@ -155,7 +155,8 @@ struct sdhci_host { struct timer_list timer; /* Timer for timeouts */ - unsigned int caps; /* Alternative capabilities */ + unsigned int caps; /* Alternative CAPABILITY_0 */ + unsigned int caps1; /* Alternative CAPABILITY_1 */ unsigned int ocr_avail_sdio; /* OCR bit masks */ unsigned int ocr_avail_sd; From b9929f0eb466089e14389a7d467196c4e179dc6a Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Sat, 30 Jun 2012 14:24:22 +0200 Subject: [PATCH 43/62] mmc: s3cmci: Convert s3cmci driver to gpiolib API The s3c2410_gpio* calls are obsolete and have been scheduled for removal since several kernel releases. Remove them and use common gpiolib API. This patch is a prerequisite for removal of the S3C24XX SoC specific arch/arm/plat-samsung/include/gpio-fns.h header. Tested on Micro2440-SDK. Signed-off-by: Sylwester Nawrocki Signed-off-by: Chris Ball --- drivers/mmc/host/s3cmci.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c index c3622a69f432..bd5a5cce122c 100644 --- a/drivers/mmc/host/s3cmci.c +++ b/drivers/mmc/host/s3cmci.c @@ -26,7 +26,6 @@ #include #include -#include #include @@ -1237,12 +1236,9 @@ static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) switch (ios->power_mode) { case MMC_POWER_ON: case MMC_POWER_UP: - s3c2410_gpio_cfgpin(S3C2410_GPE(5), S3C2410_GPE5_SDCLK); - s3c2410_gpio_cfgpin(S3C2410_GPE(6), S3C2410_GPE6_SDCMD); - s3c2410_gpio_cfgpin(S3C2410_GPE(7), S3C2410_GPE7_SDDAT0); - s3c2410_gpio_cfgpin(S3C2410_GPE(8), S3C2410_GPE8_SDDAT1); - s3c2410_gpio_cfgpin(S3C2410_GPE(9), S3C2410_GPE9_SDDAT2); - s3c2410_gpio_cfgpin(S3C2410_GPE(10), S3C2410_GPE10_SDDAT3); + /* Configure GPE5...GPE10 pins in SD mode */ + s3c_gpio_cfgall_range(S3C2410_GPE(5), 6, S3C_GPIO_SFN(2), + S3C_GPIO_PULL_NONE); if (host->pdata->set_power) host->pdata->set_power(ios->power_mode, ios->vdd); From 0fc81ee3102efedd4571e28372d2242c694c67ec Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 2 Jul 2012 14:26:15 +0100 Subject: [PATCH 44/62] mmc: sdhci: Report failure reasons for all cases in sdhci_add_host() For most error conditions sdhci_add_host() will print a diagnostic message indicating why it failed but there are a few cases where this does not happen. Add error messages in these cases to aid diagnosis. Signed-off-by: Mark Brown Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 206b52fec443..101f9d43196f 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -3033,8 +3033,11 @@ int sdhci_add_host(struct sdhci_host *host) ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED, mmc_hostname(mmc), host); - if (ret) + if (ret) { + pr_err("%s: Failed to request IRQ %d: %d\n", + mmc_hostname(mmc), host->irq, ret); goto untasklet; + } sdhci_init(host, 0); @@ -3051,8 +3054,11 @@ int sdhci_add_host(struct sdhci_host *host) host->led.brightness_set = sdhci_led_control; ret = led_classdev_register(mmc_dev(mmc), &host->led); - if (ret) + if (ret) { + pr_err("%s: Failed to register LED device: %d\n", + mmc_hostname(mmc), ret); goto reset; + } #endif mmiowb(); From 55fc05b7414274f17795cd0e8a3b1546f3649d5e Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Tue, 3 Jul 2012 23:13:39 +0100 Subject: [PATCH 45/62] mmc: sdhci-pci: CaFe has broken card detection At http://dev.laptop.org/ticket/11980 we have determined that the Marvell CaFe SDHCI controller reports bad card presence during resume. It reports that no card is present even when it is. This is a regression -- resume worked back around 2.6.37. Around 400ms after resuming, a "card inserted" interrupt is generated, at which point it starts reporting presence. Work around this hardware oddity by setting the SDHCI_QUIRK_BROKEN_CARD_DETECTION flag. Thanks to Chris Ball for helping with diagnosis. Signed-off-by: Daniel Drake [stable@: please apply to 3.0+] Cc: Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-pci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 69ef0beae104..504da715a41a 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -157,6 +157,7 @@ static const struct sdhci_pci_fixes sdhci_ene_714 = { static const struct sdhci_pci_fixes sdhci_cafe = { .quirks = SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER | SDHCI_QUIRK_NO_BUSY_IRQ | + SDHCI_QUIRK_BROKEN_CARD_DETECTION | SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, }; From 473b095a72a95ba719905b1f2e82cd18d099a427 Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Tue, 3 Jul 2012 17:27:49 +0800 Subject: [PATCH 46/62] mmc: sdhci: fix incorrect command used in tuning For SD hosts using retuning mode 1, when retuning timer expired, it will need to do retuning in sdhci_request before processing the actual request. But the retuning command is fixed: cmd19 for SD card and cmd21 for eMMC card, so we can't use the original request's command to do the tuning. And since the tuning command depends on the card type attached to the host, we will need to know the card type to use the correct tuning command. Signed-off-by: Aaron Lu Reviewed-by: Philip Rakity Cc: stable [3.3+] Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 101f9d43196f..12015151fab9 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -27,6 +27,7 @@ #include #include +#include #include "sdhci.h" @@ -1245,6 +1246,7 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) struct sdhci_host *host; bool present; unsigned long flags; + u32 tuning_opcode; host = mmc_priv(mmc); @@ -1292,8 +1294,12 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) */ if ((host->flags & SDHCI_NEEDS_RETUNING) && !(present_state & (SDHCI_DOING_WRITE | SDHCI_DOING_READ))) { + /* eMMC uses cmd21 while sd and sdio use cmd19 */ + tuning_opcode = mmc->card->type == MMC_TYPE_MMC ? + MMC_SEND_TUNING_BLOCK_HS200 : + MMC_SEND_TUNING_BLOCK; spin_unlock_irqrestore(&host->lock, flags); - sdhci_execute_tuning(mmc, mrq->cmd->opcode); + sdhci_execute_tuning(mmc, tuning_opcode); spin_lock_irqsave(&host->lock, flags); /* Restore original mmc_request structure */ From b67c6b411dcf17199e1dfdf18da8b1c6caf73e6e Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Fri, 29 Jun 2012 16:17:31 +0800 Subject: [PATCH 47/62] mmc: sdhci: restore host settings when card is removed Some of the host settings are affected by different cards inserted, e.g. when an UHS-I card is inserted, the SDHCI_NEEDS_RETUING flag might be set when the tuning timer expired and host's max_blk_count will be reduced to make sure the data transfer for a command does not exceed 4MiB to meet the retuning mode 1's requirement. When the card is removed, we should restore the original setting of the host since we can't be sure the next card being inserted will still be an UHS-I card that needs tuning. The original setting include its max_blk_count and no set of the flag of SDHCI_NEEDS_RETUNING. Signed-off-by: Aaron Lu Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 12015151fab9..b0a5629dea3e 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -245,6 +245,18 @@ static void sdhci_init(struct sdhci_host *host, int soft) static void sdhci_reinit(struct sdhci_host *host) { sdhci_init(host, 0); + /* + * Retuning stuffs are affected by different cards inserted and only + * applicable to UHS-I cards. So reset these fields to their initial + * value when card is removed. + */ + if (host->version >= SDHCI_SPEC_300 && host->tuning_count && + host->tuning_mode == SDHCI_TUNING_MODE_1) { + del_timer_sync(&host->tuning_timer); + host->flags &= ~SDHCI_NEEDS_RETUNING; + host->mmc->max_blk_count = + (host->quirks & SDHCI_QUIRK_NO_MULTIBLOCK) ? 1 : 65535; + } sdhci_enable_card_detection(host); } From a39ca6ae0a08994a59d017a445939e164bc77fd5 Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Tue, 3 Jul 2012 14:16:11 +0800 Subject: [PATCH 48/62] mmc: core: Simplify and fix for SD switch processing In mmc_read_switch, just do a one time mode 0 switch command to get the support bits information, no need to do multiple times as the support bits do not change with different arguments. And no need to check current limit support bits, as these bits are fixed according to the signal voltage. If the signal voltage is 1.8V, the support bits would be 0xf and if the signal voltage is 3.3V, the support bits would be 0x01. We will check host's ability to set the current limit. Signed-off-by: Aaron Lu Signed-off-by: Chris Ball --- drivers/mmc/core/sd.c | 94 ++++++++++--------------------------------- 1 file changed, 22 insertions(+), 72 deletions(-) diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 33c6f26c68d0..8460568e5213 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -290,8 +290,12 @@ static int mmc_read_switch(struct mmc_card *card) return -ENOMEM; } - /* Find out the supported Bus Speed Modes. */ - err = mmc_sd_switch(card, 0, 0, 1, status); + /* + * Find out the card's support bits with a mode 0 operation. + * The argument does not matter, as the support bits do not + * change with the arguments. + */ + err = mmc_sd_switch(card, 0, 0, 0, status); if (err) { /* * If the host or the card can't do the switch, @@ -312,46 +316,8 @@ static int mmc_read_switch(struct mmc_card *card) if (card->scr.sda_spec3) { card->sw_caps.sd3_bus_mode = status[13]; - - /* Find out Driver Strengths supported by the card */ - err = mmc_sd_switch(card, 0, 2, 1, status); - if (err) { - /* - * If the host or the card can't do the switch, - * fail more gracefully. - */ - if (err != -EINVAL && err != -ENOSYS && err != -EFAULT) - goto out; - - pr_warning("%s: problem reading " - "Driver Strength.\n", - mmc_hostname(card->host)); - err = 0; - - goto out; - } - + /* Driver Strengths supported by the card */ card->sw_caps.sd3_drv_type = status[9]; - - /* Find out Current Limits supported by the card */ - err = mmc_sd_switch(card, 0, 3, 1, status); - if (err) { - /* - * If the host or the card can't do the switch, - * fail more gracefully. - */ - if (err != -EINVAL && err != -ENOSYS && err != -EFAULT) - goto out; - - pr_warning("%s: problem reading " - "Current Limit.\n", - mmc_hostname(card->host)); - err = 0; - - goto out; - } - - card->sw_caps.sd3_curr_limit = status[7]; } out: @@ -560,41 +526,24 @@ static int sd_set_current_limit(struct mmc_card *card, u8 *status) * Current limit switch is only defined for SDR50, SDR104, and DDR50 * bus speed modes. For other bus speed modes, we do not change the * current limit. + * We only check host's capability here, if we set a limit that is + * higher than the card's maximum current, the card will be using its + * maximum current, e.g. if the card's maximum current is 300ma, and + * when we set current limit to 200ma, the card will draw 200ma, and + * when we set current limit to 400/600/800ma, the card will draw its + * maximum 300ma from the host. */ if ((card->sd_bus_speed == UHS_SDR50_BUS_SPEED) || (card->sd_bus_speed == UHS_SDR104_BUS_SPEED) || (card->sd_bus_speed == UHS_DDR50_BUS_SPEED)) { - if (card->host->caps & MMC_CAP_MAX_CURRENT_800) { - if (card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_800) - current_limit = SD_SET_CURRENT_LIMIT_800; - else if (card->sw_caps.sd3_curr_limit & - SD_MAX_CURRENT_600) - current_limit = SD_SET_CURRENT_LIMIT_600; - else if (card->sw_caps.sd3_curr_limit & - SD_MAX_CURRENT_400) - current_limit = SD_SET_CURRENT_LIMIT_400; - else if (card->sw_caps.sd3_curr_limit & - SD_MAX_CURRENT_200) - current_limit = SD_SET_CURRENT_LIMIT_200; - } else if (card->host->caps & MMC_CAP_MAX_CURRENT_600) { - if (card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_600) - current_limit = SD_SET_CURRENT_LIMIT_600; - else if (card->sw_caps.sd3_curr_limit & - SD_MAX_CURRENT_400) - current_limit = SD_SET_CURRENT_LIMIT_400; - else if (card->sw_caps.sd3_curr_limit & - SD_MAX_CURRENT_200) - current_limit = SD_SET_CURRENT_LIMIT_200; - } else if (card->host->caps & MMC_CAP_MAX_CURRENT_400) { - if (card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_400) - current_limit = SD_SET_CURRENT_LIMIT_400; - else if (card->sw_caps.sd3_curr_limit & - SD_MAX_CURRENT_200) - current_limit = SD_SET_CURRENT_LIMIT_200; - } else if (card->host->caps & MMC_CAP_MAX_CURRENT_200) { - if (card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_200) - current_limit = SD_SET_CURRENT_LIMIT_200; - } + if (card->host->caps & MMC_CAP_MAX_CURRENT_800) + current_limit = SD_SET_CURRENT_LIMIT_800; + else if (card->host->caps & MMC_CAP_MAX_CURRENT_600) + current_limit = SD_SET_CURRENT_LIMIT_600; + else if (card->host->caps & MMC_CAP_MAX_CURRENT_400) + current_limit = SD_SET_CURRENT_LIMIT_400; + else if (card->host->caps & MMC_CAP_MAX_CURRENT_200) + current_limit = SD_SET_CURRENT_LIMIT_200; } if (current_limit != SD_SET_CURRENT_NO_CHANGE) { @@ -607,6 +556,7 @@ static int sd_set_current_limit(struct mmc_card *card, u8 *status) mmc_hostname(card->host)); } + return 0; } From aa6439daddf579b93ace8b45956a416234c3854c Mon Sep 17 00:00:00 2001 From: Liu Chuansheng Date: Mon, 9 Jul 2012 06:26:42 -0400 Subject: [PATCH 49/62] mmc: sdio: Change pr_warning to pr_warn_ratelimited When debugging one bad issue, got lots of pr_warning messages "queuing unknown CIS tuple" which caused a printk storm and flooded the console. This patch changes the pr_warning to use pr_warn_ratelimited. Signed-off-by: Liu Chuansheng Signed-off-by: Chris Ball --- drivers/mmc/core/sdio_cis.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/core/sdio_cis.c b/drivers/mmc/core/sdio_cis.c index f1c7ed8f4d85..8e94e555b788 100644 --- a/drivers/mmc/core/sdio_cis.c +++ b/drivers/mmc/core/sdio_cis.c @@ -313,7 +313,7 @@ static int sdio_read_cis(struct mmc_card *card, struct sdio_func *func) if (ret == -ENOENT) { /* warn about unknown tuples */ - pr_warning("%s: queuing unknown" + pr_warn_ratelimited("%s: queuing unknown" " CIS tuple 0x%02x (%u bytes)\n", mmc_hostname(card->host), tpl_code, tpl_link); From 973905feab85416784f36cc94b868392fd465ef4 Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Wed, 4 Jul 2012 13:29:09 +0800 Subject: [PATCH 50/62] mmc: sdhci: Introduce new flag SDHCI_USING_RETUNING_TIMER Add a new flag of SDHCI_USING_RETUNING_TIMER to represent if the host is using a retuning timer for the card inserted. This flag is set when the host does tuning the first time for the card and the host's retuning mode is 1. This flag is used afterwards whenever needs to decide if the host is currently using a retuning timer. This flag is cleared when the card is removed in sdhci_reinit. The set/clear of the flag and the start/stop of the retuning timer is associated with the card's init/remove time, so there is no need to touch it when the host is to be removed as at that time the card should have already been removed. Signed-off-by: Aaron Lu Reviewed-by: Girish K S Reviewed-by: Philip Rakity Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 30 ++++++++++++------------------ include/linux/mmc/sdhci.h | 1 + 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index b0a5629dea3e..3ec418212894 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -250,8 +250,9 @@ static void sdhci_reinit(struct sdhci_host *host) * applicable to UHS-I cards. So reset these fields to their initial * value when card is removed. */ - if (host->version >= SDHCI_SPEC_300 && host->tuning_count && - host->tuning_mode == SDHCI_TUNING_MODE_1) { + if (host->flags & SDHCI_USING_RETUNING_TIMER) { + host->flags &= ~SDHCI_USING_RETUNING_TIMER; + del_timer_sync(&host->tuning_timer); host->flags &= ~SDHCI_NEEDS_RETUNING; host->mmc->max_blk_count = @@ -1873,6 +1874,7 @@ out: */ if (!(host->flags & SDHCI_NEEDS_RETUNING) && host->tuning_count && (host->tuning_mode == SDHCI_TUNING_MODE_1)) { + host->flags |= SDHCI_USING_RETUNING_TIMER; mod_timer(&host->tuning_timer, jiffies + host->tuning_count * HZ); /* Tuning mode 1 limits the maximum data length to 4MB */ @@ -1890,10 +1892,10 @@ out: * try tuning again at a later time, when the re-tuning timer expires. * So for these controllers, we return 0. Since there might be other * controllers who do not have this capability, we return error for - * them. + * them. SDHCI_USING_RETUNING_TIMER means the host is currently using + * a retuning timer to do the retuning for the card. */ - if (err && host->tuning_count && - host->tuning_mode == SDHCI_TUNING_MODE_1) + if (err && (host->flags & SDHCI_USING_RETUNING_TIMER)) err = 0; sdhci_clear_set_irqs(host, SDHCI_INT_DATA_AVAIL, ier); @@ -2400,7 +2402,6 @@ out: int sdhci_suspend_host(struct sdhci_host *host) { int ret; - bool has_tuning_timer; if (host->ops->platform_suspend) host->ops->platform_suspend(host); @@ -2408,16 +2409,14 @@ int sdhci_suspend_host(struct sdhci_host *host) sdhci_disable_card_detection(host); /* Disable tuning since we are suspending */ - has_tuning_timer = host->version >= SDHCI_SPEC_300 && - host->tuning_count && host->tuning_mode == SDHCI_TUNING_MODE_1; - if (has_tuning_timer) { + if (host->flags & SDHCI_USING_RETUNING_TIMER) { del_timer_sync(&host->tuning_timer); host->flags &= ~SDHCI_NEEDS_RETUNING; } ret = mmc_suspend_host(host->mmc); if (ret) { - if (has_tuning_timer) { + if (host->flags & SDHCI_USING_RETUNING_TIMER) { host->flags |= SDHCI_NEEDS_RETUNING; mod_timer(&host->tuning_timer, jiffies + host->tuning_count * HZ); @@ -2468,8 +2467,7 @@ int sdhci_resume_host(struct sdhci_host *host) 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)) + if (host->flags & SDHCI_USING_RETUNING_TIMER) host->flags |= SDHCI_NEEDS_RETUNING; return ret; @@ -2508,8 +2506,7 @@ int sdhci_runtime_suspend_host(struct sdhci_host *host) int ret = 0; /* Disable tuning since we are suspending */ - if (host->version >= SDHCI_SPEC_300 && - host->tuning_mode == SDHCI_TUNING_MODE_1) { + if (host->flags & SDHCI_USING_RETUNING_TIMER) { del_timer_sync(&host->tuning_timer); host->flags &= ~SDHCI_NEEDS_RETUNING; } @@ -2550,8 +2547,7 @@ int sdhci_runtime_resume_host(struct sdhci_host *host) sdhci_do_enable_preset_value(host, true); /* Set the re-tuning expiration flag */ - if ((host->version >= SDHCI_SPEC_300) && host->tuning_count && - (host->tuning_mode == SDHCI_TUNING_MODE_1)) + if (host->flags & SDHCI_USING_RETUNING_TIMER) host->flags |= SDHCI_NEEDS_RETUNING; spin_lock_irqsave(&host->lock, flags); @@ -3140,8 +3136,6 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) free_irq(host->irq, host); del_timer_sync(&host->timer); - if (host->version >= SDHCI_SPEC_300) - del_timer_sync(&host->tuning_timer); tasklet_kill(&host->card_tasklet); tasklet_kill(&host->finish_tasklet); diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h index d989b511c9ab..ac83b105bedd 100644 --- a/include/linux/mmc/sdhci.h +++ b/include/linux/mmc/sdhci.h @@ -122,6 +122,7 @@ struct sdhci_host { #define SDHCI_PV_ENABLED (1<<8) /* Preset value enabled */ #define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq enabled */ #define SDHCI_HS200_NEEDS_TUNING (1<<10) /* HS200 needs tuning */ +#define SDHCI_USING_RETUNING_TIMER (1<<11) /* Host is using a retuning timer for the card */ unsigned int version; /* SDHCI spec. version */ From eb6d5ae1c918cefbfe06f6f96e1a75f2179d82ce Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Thu, 5 Jul 2012 22:06:13 +0100 Subject: [PATCH 51/62] mmc: sdhci: poll for card even when card is logically unremovable The Marvell CaFe is now marked as having bad card detection to fix a problem during system resume. Now on the OLPC XO-1 we are facing the issue that the card is marked as logically unremovable (via MMC_UNSAFE_RESUME), which means that mmc_card_is_removable considers the card non-removable. The existing code logic decides not to poll for card presence in this case, and card detection is also disabled because of the quirk being set. This means that no SD cards are detected when inserted after boot. Refine the logic to enable card presence polling in the case when a card is logically unremovable, only avoiding the poll in the case when the card is physically non-removable (denoted with MMC_CAP_NONREMOVABLE). Signed-off-by: Daniel Drake Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 3ec418212894..bc1de70360c8 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2795,7 +2795,7 @@ int sdhci_add_host(struct sdhci_host *host) mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) && - mmc_card_is_removable(mmc)) + !(host->mmc->caps & MMC_CAP_NONREMOVABLE)) mmc->caps |= MMC_CAP_NEEDS_POLL; /* Any UHS-I mode in caps implies SDR12 and SDR25 support. */ From 41b4e9a194f69b1c945038c559ea407a6b383e81 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Fri, 6 Jul 2012 11:58:33 +0200 Subject: [PATCH 52/62] mmc: atmel-mci: fix incorrect setting of host->data to NULL Setting host->data to NULL is incorrect sequence in STATE_SENDING_STOP state of FSM: This early setting leads to the skip of dma_unmap_sg() in atmci_dma_cleanup() which is a bug. Idea taken from dw_mmc by Seungwon Jeon. Signed-off-by: Nicolas Ferre Cc: Seungwon Jeon Acked-by: Ludovic Desroches Signed-off-by: Chris Ball --- drivers/mmc/host/atmel-mci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index f2c115e06438..84c4ac4d5192 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -1685,7 +1685,6 @@ static void atmci_tasklet_func(unsigned long priv) dev_dbg(&host->pdev->dev, "FSM: cmd ready\n"); host->cmd = NULL; - host->data = NULL; data->bytes_xfered = data->blocks * data->blksz; data->error = 0; atmci_command_complete(host, mrq->stop); @@ -1699,6 +1698,7 @@ static void atmci_tasklet_func(unsigned long priv) atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY); state = STATE_WAITING_NOTBUSY; } + host->data = NULL; break; case STATE_END_REQUEST: From 8a4de07e05e7bedc894c2de3b3b04673d6d840ec Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Fri, 6 Jul 2012 12:11:51 +0200 Subject: [PATCH 53/62] mmc: atmel-mci: modify CLKDIV displaying in debugfs Modify clock division displaying in debugfs for matching the new CLKDIV,CLKODD user interface arrangement. Is using the has_odd_clk_div property to choose the proper format. Signed-off-by: Nicolas Ferre Acked-by: Ludovic Desroches Signed-off-by: Chris Ball --- drivers/mmc/host/atmel-mci.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 84c4ac4d5192..322412cec4ee 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -391,11 +391,17 @@ static int atmci_regs_show(struct seq_file *s, void *v) clk_disable(host->mck); spin_unlock_bh(&host->lock); - seq_printf(s, "MR:\t0x%08x%s%s CLKDIV=%u\n", + seq_printf(s, "MR:\t0x%08x%s%s ", buf[ATMCI_MR / 4], buf[ATMCI_MR / 4] & ATMCI_MR_RDPROOF ? " RDPROOF" : "", - buf[ATMCI_MR / 4] & ATMCI_MR_WRPROOF ? " WRPROOF" : "", - buf[ATMCI_MR / 4] & 0xff); + buf[ATMCI_MR / 4] & ATMCI_MR_WRPROOF ? " WRPROOF" : ""); + if (host->caps.has_odd_clk_div) + seq_printf(s, "{CLKDIV,CLKODD}=%u\n", + ((buf[ATMCI_MR / 4] & 0xff) << 1) + | ((buf[ATMCI_MR / 4] >> 16) & 1)); + else + seq_printf(s, "CLKDIV=%u\n", + (buf[ATMCI_MR / 4] & 0xff)); seq_printf(s, "DTOR:\t0x%08x\n", buf[ATMCI_DTOR / 4]); seq_printf(s, "SDCR:\t0x%08x\n", buf[ATMCI_SDCR / 4]); seq_printf(s, "ARGR:\t0x%08x\n", buf[ATMCI_ARGR / 4]); From 38cfc2f71cec9508bdd3a11e576738c2182418d0 Mon Sep 17 00:00:00 2001 From: Philip Rakity Date: Wed, 27 Jun 2012 09:15:00 -0700 Subject: [PATCH 54/62] mmc: sdhci: When a UHS switch fails, cycle power if regulator is used Power needs to be removed from the card when switching to 1.8v fails. If a regulator is used to control vmmc we need to turn the regulator off and then back on otherwise power will not be removed from the card. Signed-off-by: Philip Rakity Reviewed-by: Aaron Lu Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index bc1de70360c8..d9966568143b 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1682,11 +1682,15 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, pwr = sdhci_readb(host, SDHCI_POWER_CONTROL); pwr &= ~SDHCI_POWER_ON; sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); + if (host->vmmc) + regulator_disable(host->vmmc); /* Wait for 1ms as per the spec */ usleep_range(1000, 1500); pwr |= SDHCI_POWER_ON; sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); + if (host->vmmc) + regulator_enable(host->vmmc); pr_info(DRIVER_NAME ": Switching to 1.8V signalling " "voltage failed, retrying with S18R set to 0\n"); From 94c18149451e9ae91a669bc33370273327d7da81 Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Wed, 27 Jun 2012 14:19:54 +0530 Subject: [PATCH 55/62] mmc: omap_hsmmc: add clk_prepare and clk_unprepare In preparation for OMAP moving to the Common Clock Framework (CCF) add clk_prepare() and clk_unprepare() for the hsmmc clocks. Signed-off-by: Rajendra Nayak Acked-by: Venkatraman S Signed-off-by: Chris Ball --- drivers/mmc/host/omap_hsmmc.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 389a3eedfc24..a9227a7a9d69 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -1089,7 +1089,7 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd) /* Disable the clocks */ pm_runtime_put_sync(host->dev); if (host->dbclk) - clk_disable(host->dbclk); + clk_disable_unprepare(host->dbclk); /* Turn the power off */ ret = mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0); @@ -1100,7 +1100,7 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd) vdd); pm_runtime_get_sync(host->dev); if (host->dbclk) - clk_enable(host->dbclk); + clk_prepare_enable(host->dbclk); if (ret != 0) goto err; @@ -1899,7 +1899,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev) if (IS_ERR(host->dbclk)) { dev_warn(mmc_dev(host->mmc), "Failed to get debounce clk\n"); host->dbclk = NULL; - } else if (clk_enable(host->dbclk) != 0) { + } else if (clk_prepare_enable(host->dbclk) != 0) { dev_warn(mmc_dev(host->mmc), "Failed to enable debounce clk\n"); clk_put(host->dbclk); host->dbclk = NULL; @@ -2023,7 +2023,7 @@ err_irq: pm_runtime_disable(host->dev); clk_put(host->fclk); if (host->dbclk) { - clk_disable(host->dbclk); + clk_disable_unprepare(host->dbclk); clk_put(host->dbclk); } err1: @@ -2058,7 +2058,7 @@ static int __devexit omap_hsmmc_remove(struct platform_device *pdev) pm_runtime_disable(host->dev); clk_put(host->fclk); if (host->dbclk) { - clk_disable(host->dbclk); + clk_disable_unprepare(host->dbclk); clk_put(host->dbclk); } @@ -2116,7 +2116,7 @@ static int omap_hsmmc_suspend(struct device *dev) } if (host->dbclk) - clk_disable(host->dbclk); + clk_disable_unprepare(host->dbclk); err: pm_runtime_put_sync(host->dev); return ret; @@ -2137,7 +2137,7 @@ static int omap_hsmmc_resume(struct device *dev) pm_runtime_get_sync(host->dev); if (host->dbclk) - clk_enable(host->dbclk); + clk_prepare_enable(host->dbclk); if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER)) omap_hsmmc_conf_bus_power(host); From 55c4665ea0a42fd6427826bfce96eb4b0389262a Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Wed, 4 Jul 2012 13:31:48 +0800 Subject: [PATCH 56/62] mmc: sd: Fix sd current limit setting Host has different current capabilities at different voltages, we need to record these settings seperately. The defined voltages are 1.8/3.0/3.3. For other voltages, we do not touch current limit setting. Before we set the current limit for the sd card, find out the host's operating voltage first and then find out the current capabilities of the host at that voltage to set the current limit. Signed-off-by: Aaron Lu Reviewed-by: Philip Rakity Signed-off-by: Chris Ball --- drivers/mmc/core/sd.c | 69 +++++++++++++++++++++++++++++++--------- drivers/mmc/host/sdhci.c | 31 ++---------------- include/linux/mmc/host.h | 10 ++---- 3 files changed, 60 insertions(+), 50 deletions(-) diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 8460568e5213..441bdf472c99 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -517,15 +517,54 @@ static int sd_set_bus_speed_mode(struct mmc_card *card, u8 *status) return 0; } +/* Get host's max current setting at its current voltage */ +static u32 sd_get_host_max_current(struct mmc_host *host) +{ + u32 voltage, max_current; + + voltage = 1 << host->ios.vdd; + switch (voltage) { + case MMC_VDD_165_195: + max_current = host->max_current_180; + break; + case MMC_VDD_29_30: + case MMC_VDD_30_31: + max_current = host->max_current_300; + break; + case MMC_VDD_32_33: + case MMC_VDD_33_34: + max_current = host->max_current_330; + break; + default: + max_current = 0; + } + + return max_current; +} + static int sd_set_current_limit(struct mmc_card *card, u8 *status) { int current_limit = SD_SET_CURRENT_NO_CHANGE; int err; + u32 max_current; /* * Current limit switch is only defined for SDR50, SDR104, and DDR50 * bus speed modes. For other bus speed modes, we do not change the * current limit. + */ + if ((card->sd_bus_speed != UHS_SDR50_BUS_SPEED) && + (card->sd_bus_speed != UHS_SDR104_BUS_SPEED) && + (card->sd_bus_speed != UHS_DDR50_BUS_SPEED)) + return 0; + + /* + * Host has different current capabilities when operating at + * different voltages, so find out its max current first. + */ + max_current = sd_get_host_max_current(card->host); + + /* * We only check host's capability here, if we set a limit that is * higher than the card's maximum current, the card will be using its * maximum current, e.g. if the card's maximum current is 300ma, and @@ -533,18 +572,14 @@ static int sd_set_current_limit(struct mmc_card *card, u8 *status) * when we set current limit to 400/600/800ma, the card will draw its * maximum 300ma from the host. */ - if ((card->sd_bus_speed == UHS_SDR50_BUS_SPEED) || - (card->sd_bus_speed == UHS_SDR104_BUS_SPEED) || - (card->sd_bus_speed == UHS_DDR50_BUS_SPEED)) { - if (card->host->caps & MMC_CAP_MAX_CURRENT_800) - current_limit = SD_SET_CURRENT_LIMIT_800; - else if (card->host->caps & MMC_CAP_MAX_CURRENT_600) - current_limit = SD_SET_CURRENT_LIMIT_600; - else if (card->host->caps & MMC_CAP_MAX_CURRENT_400) - current_limit = SD_SET_CURRENT_LIMIT_400; - else if (card->host->caps & MMC_CAP_MAX_CURRENT_200) - current_limit = SD_SET_CURRENT_LIMIT_200; - } + if (max_current >= 800) + current_limit = SD_SET_CURRENT_LIMIT_800; + else if (max_current >= 600) + current_limit = SD_SET_CURRENT_LIMIT_600; + else if (max_current >= 400) + current_limit = SD_SET_CURRENT_LIMIT_400; + else if (max_current >= 200) + current_limit = SD_SET_CURRENT_LIMIT_200; if (current_limit != SD_SET_CURRENT_NO_CHANGE) { err = mmc_sd_switch(card, 1, 3, current_limit, status); @@ -677,6 +712,7 @@ struct device_type sd_type = { int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr) { int err; + u32 max_current; /* * Since we're changing the OCR value, we seem to @@ -704,9 +740,12 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr) MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50)) ocr |= SD_OCR_S18R; - /* If the host can supply more than 150mA, XPC should be set to 1. */ - if (host->caps & (MMC_CAP_SET_XPC_330 | MMC_CAP_SET_XPC_300 | - MMC_CAP_SET_XPC_180)) + /* + * If the host can supply more than 150mA at current voltage, + * XPC should be set to 1. + */ + max_current = sd_get_host_max_current(host); + if (max_current > 150) ocr |= SD_OCR_XPC; try_again: diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index d9966568143b..9a11dc39921c 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2906,53 +2906,28 @@ int sdhci_add_host(struct sdhci_host *host) } if (caps[0] & SDHCI_CAN_VDD_330) { - int max_current_330; - ocr_avail |= MMC_VDD_32_33 | MMC_VDD_33_34; - max_current_330 = ((max_current_caps & + mmc->max_current_330 = ((max_current_caps & SDHCI_MAX_CURRENT_330_MASK) >> SDHCI_MAX_CURRENT_330_SHIFT) * SDHCI_MAX_CURRENT_MULTIPLIER; - - if (max_current_330 > 150) - mmc->caps |= MMC_CAP_SET_XPC_330; } if (caps[0] & SDHCI_CAN_VDD_300) { - int max_current_300; - ocr_avail |= MMC_VDD_29_30 | MMC_VDD_30_31; - max_current_300 = ((max_current_caps & + mmc->max_current_300 = ((max_current_caps & SDHCI_MAX_CURRENT_300_MASK) >> SDHCI_MAX_CURRENT_300_SHIFT) * SDHCI_MAX_CURRENT_MULTIPLIER; - - if (max_current_300 > 150) - mmc->caps |= MMC_CAP_SET_XPC_300; } if (caps[0] & SDHCI_CAN_VDD_180) { - int max_current_180; - ocr_avail |= MMC_VDD_165_195; - max_current_180 = ((max_current_caps & + mmc->max_current_180 = ((max_current_caps & SDHCI_MAX_CURRENT_180_MASK) >> SDHCI_MAX_CURRENT_180_SHIFT) * SDHCI_MAX_CURRENT_MULTIPLIER; - - if (max_current_180 > 150) - mmc->caps |= MMC_CAP_SET_XPC_180; - - /* Maximum current capabilities of the host at 1.8V */ - if (max_current_180 >= 800) - mmc->caps |= MMC_CAP_MAX_CURRENT_800; - else if (max_current_180 >= 600) - mmc->caps |= MMC_CAP_MAX_CURRENT_600; - else if (max_current_180 >= 400) - mmc->caps |= MMC_CAP_MAX_CURRENT_400; - else if (max_current_180 >= 200) - mmc->caps |= MMC_CAP_MAX_CURRENT_200; } mmc->ocr_avail = ocr_avail; diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 65c64ee578a7..f578a71d82a6 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -189,6 +189,9 @@ struct mmc_host { u32 ocr_avail_sd; /* SD-specific OCR */ u32 ocr_avail_mmc; /* MMC-specific OCR */ struct notifier_block pm_notify; + u32 max_current_330; + u32 max_current_300; + u32 max_current_180; #define MMC_VDD_165_195 0x00000080 /* VDD voltage 1.65 - 1.95 */ #define MMC_VDD_20_21 0x00000100 /* VDD voltage 2.0 ~ 2.1 */ @@ -232,16 +235,9 @@ struct mmc_host { #define MMC_CAP_UHS_SDR50 (1 << 17) /* Host supports UHS SDR50 mode */ #define MMC_CAP_UHS_SDR104 (1 << 18) /* Host supports UHS SDR104 mode */ #define MMC_CAP_UHS_DDR50 (1 << 19) /* Host supports UHS DDR50 mode */ -#define MMC_CAP_SET_XPC_330 (1 << 20) /* Host supports >150mA current at 3.3V */ -#define MMC_CAP_SET_XPC_300 (1 << 21) /* Host supports >150mA current at 3.0V */ -#define MMC_CAP_SET_XPC_180 (1 << 22) /* Host supports >150mA current at 1.8V */ #define MMC_CAP_DRIVER_TYPE_A (1 << 23) /* Host supports Driver Type A */ #define MMC_CAP_DRIVER_TYPE_C (1 << 24) /* Host supports Driver Type C */ #define MMC_CAP_DRIVER_TYPE_D (1 << 25) /* Host supports Driver Type D */ -#define MMC_CAP_MAX_CURRENT_200 (1 << 26) /* Host max current limit is 200mA */ -#define MMC_CAP_MAX_CURRENT_400 (1 << 27) /* Host max current limit is 400mA */ -#define MMC_CAP_MAX_CURRENT_600 (1 << 28) /* Host max current limit is 600mA */ -#define MMC_CAP_MAX_CURRENT_800 (1 << 29) /* Host max current limit is 800mA */ #define MMC_CAP_CMD23 (1 << 30) /* CMD23 supported. */ #define MMC_CAP_HW_RESET (1 << 31) /* Hardware reset */ From 108ecc4cf9c46a4caabaf18efc42d19818c95b70 Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Tue, 10 Jul 2012 16:55:37 +0800 Subject: [PATCH 57/62] mmc: core: reset signal voltage on power up Add a call to mmc_set_signal_voltage() to set signal voltage to 3.3v in mmc_power_up so that we do not need to touch signal voltage setting in mmc/sd/sdio init functions and rescan function. For mmc/sd cards, when doing a suspend/resume cycle, consider the unsafe resume case, the card will lose its power and when powered on again, we will set signal voltage to 3.3v in mmc_power_up before its resume function gets called, which will re-init the card. And for sdio cards, when doing a suspend/resume cycle, consider the unsafe resume case, the card will either lose its power or not depending on if it wants to wakeup the host. If power is not maintained, it is the same case as mmc/sd cards. If power is maintained, mmc_power_up will not be called and the card's signal voltage will remain at the last setting. Signed-off-by: Aaron Lu Tested-by: Venkatraman S Signed-off-by: Chris Ball --- drivers/mmc/core/core.c | 6 +++--- drivers/mmc/core/mmc.c | 3 --- drivers/mmc/core/sd.c | 3 --- drivers/mmc/core/sdio.c | 7 ------- 4 files changed, 3 insertions(+), 16 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 9503cabc96f1..8ac5246e2ab2 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1212,6 +1212,9 @@ static void mmc_power_up(struct mmc_host *host) host->ios.timing = MMC_TIMING_LEGACY; mmc_set_ios(host); + /* Set signal voltage to 3.3V */ + mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330, false); + /* * This delay should be sufficient to allow the power supply * to reach the minimum voltage. @@ -1963,9 +1966,6 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq) */ mmc_hw_reset_for_init(host); - /* Initialization should be done at 3.3 V I/O voltage. */ - mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330, 0); - /* * sdio_reset sends CMD52 to reset card. Since we do not know * if the card is being re-initialized, just send it. CMD52 diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 4f4489aa6bae..396b25891bb9 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -818,9 +818,6 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, if (!mmc_host_is_spi(host)) mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN); - /* Initialization should be done at 3.3 V I/O voltage. */ - mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330, 0); - /* * Since we're changing the OCR value, we seem to * need to tell some cards to go back to the idle diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 441bdf472c99..74972c241dff 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -901,9 +901,6 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, BUG_ON(!host); WARN_ON(!host->claimed); - /* The initialization should be done at 3.3 V I/O voltage. */ - mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330, 0); - err = mmc_sd_get_cid(host, ocr, cid, &rocr); if (err) return err; diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 41c5fd8848f4..d4619e2ec030 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -591,9 +591,6 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, * Inform the card of the voltage */ if (!powered_resume) { - /* The initialization should be done at 3.3 V I/O voltage. */ - mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330, 0); - err = mmc_send_io_op_cond(host, host->ocr, &ocr); if (err) goto err; @@ -1006,10 +1003,6 @@ static int mmc_sdio_power_restore(struct mmc_host *host) * restore the correct voltage setting of the card. */ - /* The initialization should be done at 3.3 V I/O voltage. */ - if (!mmc_card_keep_power(host)) - mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330, 0); - sdio_reset(host); mmc_go_idle(host); mmc_send_if_cond(host, host->ocr_avail); From 9a0985b78d70209db1e0d870f7be80272a0e7642 Mon Sep 17 00:00:00 2001 From: Wilson Callan Date: Thu, 19 Jul 2012 02:49:16 -0400 Subject: [PATCH 58/62] mmc: esdhc: Fix DMA_MASK to not break mx25 DMA access Patch to not set reserved bits in i.MX25 PROCTL register. DMA stops working if those bits get set. Signed-off-by: Wilson Callan Acked-by: Sascha Hauer Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-esdhc-imx.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index ebbe984e5d00..e23f8134591c 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -299,6 +299,8 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg) { + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct pltfm_imx_data *imx_data = pltfm_host->priv; u32 new_val; switch (reg) { @@ -315,8 +317,11 @@ static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg) SDHCI_CTRL_D3CD); /* ensure the endianess */ new_val |= ESDHC_HOST_CONTROL_LE; - /* DMA mode bits are shifted */ - new_val |= (val & SDHCI_CTRL_DMA_MASK) << 5; + /* bits 8&9 are reserved on mx25 */ + if (!is_imx25_esdhc(imx_data)) { + /* DMA mode bits are shifted */ + new_val |= (val & SDHCI_CTRL_DMA_MASK) << 5; + } esdhc_clrset_le(host, 0xffff, new_val, reg); return; From b6e76f10afbdc510df9288ab4df3e94a89c20909 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 19 Jul 2012 11:11:39 -0400 Subject: [PATCH 59/62] mmc: mxs-mmc: Add wp-inverted property The write-protect GPIO is inverted on some boards. Handle such case. Signed-off-by: Marek Vasut Signed-off-by: Chris Ball --- drivers/mmc/host/mxs-mmc.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index 2247c50f9869..a51f9309ffbb 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -164,16 +164,23 @@ struct mxs_mmc_host { spinlock_t lock; int sdio_irq_en; int wp_gpio; + bool wp_inverted; }; static int mxs_mmc_get_ro(struct mmc_host *mmc) { struct mxs_mmc_host *host = mmc_priv(mmc); + int ret; if (!gpio_is_valid(host->wp_gpio)) return -EINVAL; - return gpio_get_value(host->wp_gpio); + ret = gpio_get_value(host->wp_gpio); + + if (host->wp_inverted) + ret = !ret; + + return ret; } static int mxs_mmc_get_cd(struct mmc_host *mmc) @@ -708,6 +715,7 @@ static int mxs_mmc_probe(struct platform_device *pdev) int ret = 0, irq_err, irq_dma; dma_cap_mask_t mask; struct regulator *reg_vmmc; + enum of_gpio_flags flags; iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0); @@ -796,7 +804,10 @@ static int mxs_mmc_probe(struct platform_device *pdev) mmc->caps |= MMC_CAP_4_BIT_DATA; else if (bus_width == 8) mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA; - host->wp_gpio = of_get_named_gpio(np, "wp-gpios", 0); + host->wp_gpio = of_get_named_gpio_flags(np, "wp-gpios", 0, + &flags); + if (flags & OF_GPIO_ACTIVE_LOW) + host->wp_inverted = 1; } else { if (pdata->flags & SLOTF_8_BIT_CAPABLE) mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA; From 9c17d08ca183b92c9cfab6537069914a5e0e7e81 Mon Sep 17 00:00:00 2001 From: Kevin Hilman Date: Tue, 10 Jul 2012 16:40:56 -0700 Subject: [PATCH 60/62] mmc: omap_hsmmc: ensure probe returns error upon resource failure If platform_get_resource_by_name() fails, driver probe is aborted an should return an error so the driver is not bound to the device. However, in the current error path of platform_get_resource_by_name(), probe returns zero since the return value (ret) is not properly set. With a zero return value, the driver core assumes probe was successful and will bind the driver to the device. Fix this by ensuring that probe returns an error code in this failure path. Signed-off-by: Kevin Hilman Acked-by: Venkatraman S Signed-off-by: Chris Ball --- drivers/mmc/host/omap_hsmmc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index a9227a7a9d69..bc28627af66b 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -1931,6 +1931,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev) res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx"); if (!res) { dev_err(mmc_dev(host->mmc), "cannot get DMA TX channel\n"); + ret = -ENXIO; goto err_irq; } host->dma_line_tx = res->start; @@ -1938,6 +1939,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev) res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx"); if (!res) { dev_err(mmc_dev(host->mmc), "cannot get DMA RX channel\n"); + ret = -ENXIO; goto err_irq; } host->dma_line_rx = res->start; From a9ca1d5477a89c1833214a01463190f7bd77d8a9 Mon Sep 17 00:00:00 2001 From: Sebastian Hesselbarth Date: Sat, 21 Jul 2012 19:26:19 -0400 Subject: [PATCH 61/62] mmc: sdhci-dove: Add SDHCI_QUIRK_NO_HISPD_BIT The sdio controller on dove doesn't have a bit to indicate high-speed. With the quirk set it fixes accessing high-speed sdcards. Signed-off-by: Sebastian Hesselbarth Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-dove.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-dove.c b/drivers/mmc/host/sdhci-dove.c index 177f697b5835..9c8536881796 100644 --- a/drivers/mmc/host/sdhci-dove.c +++ b/drivers/mmc/host/sdhci-dove.c @@ -66,7 +66,8 @@ static struct sdhci_pltfm_data sdhci_dove_pdata = { .quirks = SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER | SDHCI_QUIRK_NO_BUSY_IRQ | SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | - SDHCI_QUIRK_FORCE_DMA, + SDHCI_QUIRK_FORCE_DMA | + SDHCI_QUIRK_NO_HISPD_BIT, }; static int __devinit sdhci_dove_probe(struct platform_device *pdev) From 30b87c60e9cbfcdfce22aa652fc1e5d061fcb733 Mon Sep 17 00:00:00 2001 From: Sebastian Hesselbarth Date: Thu, 5 Jul 2012 12:14:01 +0200 Subject: [PATCH 62/62] mmc: sdhci-dove: Prepare for common clock framework As mach-dove is moving towards common clock framework prepare the sdhci driver to grab its clock. Signed-off-by: Sebastian Hesselbarth Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-dove.c | 48 ++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-dove.c b/drivers/mmc/host/sdhci-dove.c index 9c8536881796..a6e53a1ebb08 100644 --- a/drivers/mmc/host/sdhci-dove.c +++ b/drivers/mmc/host/sdhci-dove.c @@ -20,11 +20,17 @@ */ #include +#include +#include #include #include #include "sdhci-pltfm.h" +struct sdhci_dove_priv { + struct clk *clk; +}; + static u16 sdhci_dove_readw(struct sdhci_host *host, int reg) { u16 ret; @@ -72,11 +78,51 @@ static struct sdhci_pltfm_data sdhci_dove_pdata = { static int __devinit sdhci_dove_probe(struct platform_device *pdev) { - return sdhci_pltfm_register(pdev, &sdhci_dove_pdata); + struct sdhci_host *host; + struct sdhci_pltfm_host *pltfm_host; + struct sdhci_dove_priv *priv; + int ret; + + ret = sdhci_pltfm_register(pdev, &sdhci_dove_pdata); + if (ret) + goto sdhci_dove_register_fail; + + priv = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_dove_priv), + GFP_KERNEL); + if (!priv) { + dev_err(&pdev->dev, "unable to allocate private data"); + ret = -ENOMEM; + goto sdhci_dove_allocate_fail; + } + + host = platform_get_drvdata(pdev); + pltfm_host = sdhci_priv(host); + pltfm_host->priv = priv; + + priv->clk = clk_get(&pdev->dev, NULL); + if (!IS_ERR(priv->clk)) + clk_prepare_enable(priv->clk); + return 0; + +sdhci_dove_allocate_fail: + sdhci_pltfm_unregister(pdev); +sdhci_dove_register_fail: + return ret; } static int __devexit sdhci_dove_remove(struct platform_device *pdev) { + struct sdhci_host *host = platform_get_drvdata(pdev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_dove_priv *priv = pltfm_host->priv; + + if (priv->clk) { + if (!IS_ERR(priv->clk)) { + clk_disable_unprepare(priv->clk); + clk_put(priv->clk); + } + devm_kfree(&pdev->dev, priv->clk); + } return sdhci_pltfm_unregister(pdev); }