diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-07-22 16:36:08 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-07-22 16:36:08 -0700 |
commit | a6be1fcbc57f95bb47ef3c8e4ee3d83731b8f21e (patch) | |
tree | 88d762edfd97e7cdb0b41b8fe20c1ddb204c28c9 | |
parent | 5b160bd426946c85f32b15e5d34d62d2618a5a87 (diff) | |
parent | 30b87c60e9cbfcdfce22aa652fc1e5d061fcb733 (diff) | |
download | linux-a6be1fcbc57f95bb47ef3c8e4ee3d83731b8f21e.tar.bz2 |
Merge tag 'mmc-merge-for-3.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc
Pull MMC updates from Chris Ball:
"MMC highlights for 3.6:
Core:
- Rename cd-gpio to slot-gpio and extend it to support more slot GPIO
functions, such as write-protect.
- Add a function to get regulators (Vdd and Vccq) for a host.
Drivers:
- sdhci-pxav2, sdhci-pxav3: Add device tree support.
- sdhi: Add device tree support.
- sh_mmcif: Add support for regulators, device tree, slot-gpio.
- tmio: Add regulator support, use slot-gpio."
* tag 'mmc-merge-for-3.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc: (62 commits)
mmc: sdhci-dove: Prepare for common clock framework
mmc: sdhci-dove: Add SDHCI_QUIRK_NO_HISPD_BIT
mmc: omap_hsmmc: ensure probe returns error upon resource failure
mmc: mxs-mmc: Add wp-inverted property
mmc: esdhc: Fix DMA_MASK to not break mx25 DMA access
mmc: core: reset signal voltage on power up
mmc: sd: Fix sd current limit setting
mmc: omap_hsmmc: add clk_prepare and clk_unprepare
mmc: sdhci: When a UHS switch fails, cycle power if regulator is used
mmc: atmel-mci: modify CLKDIV displaying in debugfs
mmc: atmel-mci: fix incorrect setting of host->data to NULL
mmc: sdhci: poll for card even when card is logically unremovable
mmc: sdhci: Introduce new flag SDHCI_USING_RETUNING_TIMER
mmc: sdio: Change pr_warning to pr_warn_ratelimited
mmc: core: Simplify and fix for SD switch processing
mmc: sdhci: restore host settings when card is removed
mmc: sdhci: fix incorrect command used in tuning
mmc: sdhci-pci: CaFe has broken card detection
mmc: sdhci: Report failure reasons for all cases in sdhci_add_host()
mmc: s3cmci: Convert s3cmci driver to gpiolib API
...
43 files changed, 1057 insertions, 519 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,<chip>-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,<chip>-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/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 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,<chip>-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,<chip>-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/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/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>", 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-name>-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 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; 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/cd-gpio.c deleted file mode 100644 index 8f5dc08d6598..000000000000 --- a/drivers/mmc/core/cd-gpio.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Generic GPIO card-detect helper - * - * Copyright (C) 2011, Guennadi Liakhovetski <g.liakhovetski@gmx.de> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include <linux/err.h> -#include <linux/gpio.h> -#include <linux/interrupt.h> -#include <linux/jiffies.h> -#include <linux/mmc/cd-gpio.h> -#include <linux/mmc/host.h> -#include <linux/module.h> -#include <linux/slab.h> - -struct mmc_cd_gpio { - unsigned int gpio; - char label[0]; -}; - -static irqreturn_t mmc_cd_gpio_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) -{ - size_t len = strlen(dev_name(host->parent)) + 4; - struct mmc_cd_gpio *cd; - int irq = gpio_to_irq(gpio); - int ret; - - if (irq < 0) - return irq; - - cd = kmalloc(sizeof(*cd) + len, GFP_KERNEL); - if (!cd) - return -ENOMEM; - - snprintf(cd->label, len, "%s cd", dev_name(host->parent)); - - ret = gpio_request_one(gpio, GPIOF_DIR_IN, 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); - if (ret < 0) - goto eirqreq; - - cd->gpio = gpio; - host->hotplug.irq = irq; - host->hotplug.handler_priv = cd; - - return 0; - -eirqreq: - gpio_free(gpio); -egpioreq: - kfree(cd); - return ret; -} -EXPORT_SYMBOL(mmc_cd_gpio_request); - -void mmc_cd_gpio_free(struct mmc_host *host) -{ - struct mmc_cd_gpio *cd = host->hotplug.handler_priv; - - if (!cd) - return; - - free_irq(host->hotplug.irq, host); - gpio_free(cd->gpio); - kfree(cd); -} -EXPORT_SYMBOL(mmc_cd_gpio_free); diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 0b6141d29dbd..8ac5246e2ab2 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); @@ -941,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 @@ -1011,7 +1020,30 @@ 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); + +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 */ @@ -1180,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. @@ -1931,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 @@ -2075,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); } @@ -2088,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 91c84c7a1829..597f189b4427 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); } @@ -312,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); @@ -327,6 +330,9 @@ 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); init_waitqueue_head(&host->wq); INIT_DELAYED_WORK(&host->detect, mmc_rescan); 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/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) { diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index b2b43f624b9e..74972c241dff 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); @@ -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: @@ -551,60 +517,80 @@ 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 = 0; + 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 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) || - (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; - } - } else + 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 + * 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 (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; - 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; } @@ -726,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 @@ -753,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: @@ -911,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); 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); diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c new file mode 100644 index 000000000000..058242916cef --- /dev/null +++ b/drivers/mmc/core/slot-gpio.c @@ -0,0 +1,188 @@ +/* + * Generic GPIO card-detect helper + * + * Copyright (C) 2011, Guennadi Liakhovetski <g.liakhovetski@gmx.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/jiffies.h> +#include <linux/mmc/host.h> +#include <linux/mmc/slot-gpio.h> +#include <linux/module.h> +#include <linux/slab.h> + +struct mmc_gpio { + int ro_gpio; + int cd_gpio; + char *ro_label; + char cd_label[0]; +}; + +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; +} + +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) + 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; + } + } + + mutex_unlock(&host->slot.lock); + + 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; + + 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_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; + int irq = gpio_to_irq(gpio); + int ret; + + ret = mmc_gpio_alloc(host); + if (ret < 0) + return ret; + + ctx = host->slot.handler_priv; + + ret = gpio_request_one(gpio, GPIOF_DIR_IN, ctx->cd_label); + if (ret < 0) + /* + * 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 + * 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) + irq = ret; + } + + host->slot.cd_irq = irq; + + if (irq < 0) + host->caps |= MMC_CAP_NEEDS_POLL; + + ctx->cd_gpio = gpio; + + return 0; +} +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; + int gpio; + + if (!ctx || !gpio_is_valid(ctx->cd_gpio)) + return; + + 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); +} +EXPORT_SYMBOL(mmc_gpio_free_cd); diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index f2c115e06438..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]); @@ -1685,7 +1691,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 +1704,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: diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 1ca5e72ceb65..72dc3cde646d 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) @@ -2175,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 */ diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index 277161d279b8..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) @@ -707,6 +714,8 @@ 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; + enum of_gpio_flags flags; iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0); @@ -747,6 +756,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); @@ -785,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; diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 389a3eedfc24..bc28627af66b 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; @@ -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; @@ -2023,7 +2025,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 +2060,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 +2118,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 +2139,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); 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 <mach/dma.h> #include <mach/regs-sdi.h> -#include <mach/regs-gpio.h> #include <plat/mci.h> @@ -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); diff --git a/drivers/mmc/host/sdhci-dove.c b/drivers/mmc/host/sdhci-dove.c index 177f697b5835..a6e53a1ebb08 100644 --- a/drivers/mmc/host/sdhci-dove.c +++ b/drivers/mmc/host/sdhci-dove.c @@ -20,11 +20,17 @@ */ #include <linux/io.h> +#include <linux/clk.h> +#include <linux/err.h> #include <linux/module.h> #include <linux/mmc/host.h> #include "sdhci-pltfm.h" +struct sdhci_dove_priv { + struct clk *clk; +}; + static u16 sdhci_dove_readw(struct sdhci_host *host, int reg) { u16 ret; @@ -66,16 +72,57 @@ 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) { - 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); } 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; 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, }; 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 <linux/mmc/host.h> #include <linux/platform_data/pxa_sdhci.h> #include <linux/slab.h> +#include <linux/of.h> +#include <linux/of_device.h> + #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 <linux/slab.h> #include <linux/delay.h> #include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> + #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, }, diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index f4b8b4db3a9a..9a11dc39921c 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -27,6 +27,7 @@ #include <linux/mmc/mmc.h> #include <linux/mmc/host.h> +#include <linux/mmc/card.h> #include "sdhci.h" @@ -244,6 +245,19 @@ 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->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 = + (host->quirks & SDHCI_QUIRK_NO_MULTIBLOCK) ? 1 : 65535; + } sdhci_enable_card_detection(host); } @@ -1245,6 +1259,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 +1307,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 */ @@ -1663,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"); @@ -1855,6 +1878,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 */ @@ -1872,10 +1896,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); @@ -2382,7 +2406,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); @@ -2390,16 +2413,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); @@ -2450,8 +2471,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; @@ -2490,8 +2510,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; } @@ -2532,8 +2551,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); @@ -2584,7 +2602,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 +2632,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; @@ -2779,7 +2799,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. */ @@ -2837,6 +2857,30 @@ 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; + } + +#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 @@ -2845,55 +2889,45 @@ 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; - 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 - mmc->caps |= MMC_CAP_MAX_CURRENT_200; } mmc->ocr_avail = ocr_avail; @@ -2992,13 +3026,10 @@ 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; - - 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); @@ -3016,8 +3047,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(); @@ -3081,8 +3115,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/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 diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 724b35e85a26..b2af7136cd27 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -54,6 +54,8 @@ #include <linux/mmc/mmc.h> #include <linux/mmc/sdio.h> #include <linux/mmc/sh_mmcif.h> +#include <linux/mmc/slot-gpio.h> +#include <linux/mod_devicetable.h> #include <linux/pagemap.h> #include <linux/platform_device.h> #include <linux/pm_qos.h> @@ -384,6 +386,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 +449,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 & @@ -892,21 +898,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; } @@ -916,10 +916,35 @@ 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_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 && 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); @@ -937,6 +962,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); @@ -948,9 +974,10 @@ 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); + if (ios->power_mode == MMC_POWER_OFF) + sh_mmcif_set_power(host, ios); } host->state = STATE_IDLE; return; @@ -958,8 +985,7 @@ 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; sh_mmcif_sync_reset(host); @@ -975,8 +1001,12 @@ 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->get_cd) + if (!p || !p->get_cd) return -ENOSYS; else return p->get_cd(host->pd); @@ -1242,12 +1272,28 @@ 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 (!pd) + return; + + 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]; 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]; @@ -1268,42 +1314,26 @@ 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; 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 clean_up1; - } - 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; + 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; @@ -1311,34 +1341,52 @@ 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; + } + ret = sh_mmcif_clk_update(host); + if (ret < 0) + goto eclkupdate; + ret = pm_runtime_resume(&pdev->dev); if (ret < 0) - goto clean_up2; + 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); 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; + } + + 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) - goto clean_up5; + goto emmcaddh; dev_pm_qos_expose_latency_limit(&pdev->dev, 100); @@ -1347,33 +1395,42 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) sh_mmcif_readl(host->addr, MMCIF_CE_VERSION) & 0x0000ffff); return ret; -clean_up5: +emmcaddh: + if (pd && pd->use_cd_gpio) + mmc_gpio_free_cd(mmc); +erqcd: free_irq(irq[1], host); -clean_up4: +ereqirq1: free_irq(irq[0], host); -clean_up3: +ereqirq0: pm_runtime_suspend(&pdev->dev); -clean_up2: - pm_runtime_disable(&pdev->dev); +eresume: clk_disable(host->hclk); -clean_up1: +eclkupdate: + clk_put(host->hclk); +eclkget: + pm_runtime_disable(&pdev->dev); mmc_free_host(mmc); -clean_up: - if (reg) - iounmap(reg); +ealloch: + iounmap(reg); return ret; } 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; + clk_enable(host->hclk); pm_runtime_get_sync(&pdev->dev); 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); @@ -1395,9 +1452,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; @@ -1406,24 +1463,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); } @@ -1432,6 +1483,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, @@ -1443,6 +1500,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, }, }; diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index 934b68e9efc3..a842939e4655 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -21,6 +21,7 @@ #include <linux/kernel.h> #include <linux/clk.h> #include <linux/slab.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/mmc/host.h> @@ -39,22 +40,39 @@ 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; - 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) @@ -116,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); @@ -132,9 +152,8 @@ 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; @@ -142,7 +161,12 @@ 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->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; @@ -248,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); @@ -263,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); @@ -276,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); @@ -291,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), diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 9a7996ade58e..0d8a9bbe30be 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -34,8 +34,9 @@ #include <linux/io.h> #include <linux/irq.h> #include <linux/mfd/tmio.h> -#include <linux/mmc/cd-gpio.h> #include <linux/mmc/host.h> +#include <linux/mmc/mmc.h> +#include <linux/mmc/slot-gpio.h> #include <linux/mmc/tmio.h> #include <linux/module.h> #include <linux/pagemap.h> @@ -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(); @@ -751,6 +752,34 @@ 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; +} + +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 @@ -797,32 +826,37 @@ 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; } 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); host->power = false; pm_runtime_put(dev); + if (pdata->clk_disable) + pdata->clk_disable(host->pdev); } - 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 */ @@ -841,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)); @@ -850,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; @@ -865,6 +905,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) @@ -904,18 +957,14 @@ 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->caps2 = pdata->capabilities2; mmc->max_segs = 32; mmc->max_blk_size = 512; mmc->max_blk_count = (PAGE_CACHE_SIZE / mmc->max_blk_size) * 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 || @@ -927,6 +976,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) @@ -937,7 +991,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) @@ -948,6 +1001,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); @@ -961,22 +1025,18 @@ 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); - /* 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_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; @@ -1008,7 +1068,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/mfd/tmio.h b/include/linux/mfd/tmio.h index f5171dbf8850..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; @@ -110,6 +111,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); }; /* 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) diff --git a/include/linux/mmc/cd-gpio.h b/include/linux/mmc/cd-gpio.h deleted file mode 100644 index cefaba038ccb..000000000000 --- a/include/linux/mmc/cd-gpio.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Generic GPIO card-detect helper header - * - * Copyright (C) 2011, Guennadi Liakhovetski <g.liakhovetski@gmx.de> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef MMC_CD_GPIO_H -#define MMC_CD_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); - -#endif diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 0707d228d7f1..f578a71d82a6 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -11,6 +11,7 @@ #define LINUX_MMC_HOST_H #include <linux/leds.h> +#include <linux/mutex.h> #include <linux/sched.h> #include <linux/device.h> #include <linux/fault-inject.h> @@ -150,11 +151,31 @@ 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 + * @lock: protect the @handler_priv pointer + * @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; + struct mutex lock; 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; @@ -168,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 */ @@ -211,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 */ @@ -238,6 +255,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; @@ -290,7 +309,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 */ @@ -309,6 +328,7 @@ struct mmc_host { #ifdef CONFIG_REGULATOR bool regulator_enabled; /* regulator state */ #endif + struct mmc_supply supply; struct dentry *debugfs_root; @@ -357,13 +377,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 +395,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); diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h index e9051e1cb1ce..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 */ @@ -155,7 +156,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; 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; 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; diff --git a/include/linux/mmc/slot-gpio.h b/include/linux/mmc/slot-gpio.h new file mode 100644 index 000000000000..7d88d27bfafa --- /dev/null +++ b/include/linux/mmc/slot-gpio.h @@ -0,0 +1,24 @@ +/* + * Generic GPIO card-detect helper header + * + * Copyright (C) 2011, Guennadi Liakhovetski <g.liakhovetski@gmx.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef MMC_SLOT_GPIO_H +#define MMC_SLOT_GPIO_H + +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); + +#endif |