summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-07-10 22:16:00 +0400
committerLinus Torvalds <torvalds@linux-foundation.org>2013-07-10 22:16:00 +0400
commitbfffbea1aaeeb1eb6500c83ff9653416daa5b490 (patch)
tree1325ecb176b7a3d04924d6a1563b15ad6085d36a
parent34ae0a6f05aee9f51fca17001b4a90703d434ae1 (diff)
parent01ebea1b411aafc8eab440bf1d2037f01bbed99b (diff)
downloadlinux-bfffbea1aaeeb1eb6500c83ff9653416daa5b490.tar.xz
Merge tag 'mmc-updates-for-3.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc
Pull MMC updates from Chris Ball: "MMC highlights for 3.11: Core: - Add support for eMMC 5.1 devices - Add MMC_CAP_AGGRESSIVE_PM capability for aggressive power management of eMMC/SD between requests, using runtime PM - Add an ioctl to perform the eMMC 4.5 Sanitize command. Sample code at: git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc-utils.git Drivers: - dw_mmc: Add support for Rockchip's Cortex-A9 SoCs - dw_mmc: Add support for Altera SoCFPGAs - sdhci-esdhc-imx: Add support for 8-bit bus width, non-removable cards - sdhci-bcm-kona: New driver for Broadcom Kona (281xx) SoCs - sdhi/tmio: Add DT DMA support" * tag 'mmc-updates-for-3.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc: (87 commits) mmc: bcm281xx SDHCI driver mmc: sdhci: add card_event callback to sdhci mmc: core: Fixup Oops for SDIO shutdown mmc: sdhci-pci: add another device id mmc: esdhc: Fix bug when writing to SDHCI_HOST_CONTROL register mmc: esdhc: Add support for 8-bit bus width and non-removable card mmc: core: production year for eMMC 4.41 and later mmc: omap: remove unnecessary #if 0's mmc: sdhci: fix ctrl_2 on super-speed selection mmc: dw_mmc-pltfm: add Rockchip variant mmc: dw_mmc-pltfm: move probe and remove below dt match table mmc: dw_mmc-pltfm: remove static from dw_mci_pltfm_remove mmc: sdhci-acpi: add support for eMMC hardware reset for HID 80860F14 mmc: sdhci-pci: add support for eMMC hardware reset for BYT eMMC. mmc: dw_mmc: Add support DW SD/MMC driver on SOCFPGA mmc: sdhci: fix caps2 for HS200 sdhci-pxav3: Fix runtime PM initialization mmc: core: Add DT-bindings for MMC_CAP2_FULL_PWR_CYCLE mmc: core: Invent MMC_CAP2_FULL_PWR_CYCLE mmc: core: Enable power_off_notify for eMMC shutdown sequence ...
-rw-r--r--Documentation/devicetree/bindings/mmc/mmc.txt1
-rw-r--r--Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt23
-rw-r--r--Documentation/devicetree/bindings/mmc/synopsis-dw-mshc.txt20
-rw-r--r--arch/arm/configs/bcm_defconfig7
-rw-r--r--drivers/mmc/card/block.c130
-rw-r--r--drivers/mmc/card/mmc_test.c5
-rw-r--r--drivers/mmc/card/queue.c2
-rw-r--r--drivers/mmc/core/bus.c52
-rw-r--r--drivers/mmc/core/core.c200
-rw-r--r--drivers/mmc/core/core.h7
-rw-r--r--drivers/mmc/core/debugfs.c8
-rw-r--r--drivers/mmc/core/host.c32
-rw-r--r--drivers/mmc/core/mmc.c249
-rw-r--r--drivers/mmc/core/mmc_ops.c36
-rw-r--r--drivers/mmc/core/mmc_ops.h1
-rw-r--r--drivers/mmc/core/sd.c68
-rw-r--r--drivers/mmc/core/sdio.c76
-rw-r--r--drivers/mmc/host/Kconfig19
-rw-r--r--drivers/mmc/host/Makefile2
-rw-r--r--drivers/mmc/host/android-goldfish.c2
-rw-r--r--drivers/mmc/host/atmel-mci.c14
-rw-r--r--drivers/mmc/host/au1xmmc.c1
-rw-r--r--drivers/mmc/host/bfin_sdh.c2
-rw-r--r--drivers/mmc/host/cb710-mmc.c2
-rw-r--r--drivers/mmc/host/cb710-mmc.h2
-rw-r--r--drivers/mmc/host/davinci_mmc.c1
-rw-r--r--drivers/mmc/host/dw_mmc-exynos.c2
-rw-r--r--drivers/mmc/host/dw_mmc-pci.c58
-rw-r--r--drivers/mmc/host/dw_mmc-pltfm.c73
-rw-r--r--drivers/mmc/host/dw_mmc-socfpga.c140
-rw-r--r--drivers/mmc/host/dw_mmc.c65
-rw-r--r--drivers/mmc/host/dw_mmc.h3
-rw-r--r--drivers/mmc/host/jz4740_mmc.c180
-rw-r--r--drivers/mmc/host/mvsdio.c74
-rw-r--r--drivers/mmc/host/mxcmmc.c6
-rw-r--r--drivers/mmc/host/mxs-mmc.c11
-rw-r--r--drivers/mmc/host/omap.c18
-rw-r--r--drivers/mmc/host/omap_hsmmc.c2
-rw-r--r--drivers/mmc/host/pxamci.c2
-rw-r--r--drivers/mmc/host/rtsx_pci_sdmmc.c2
-rw-r--r--drivers/mmc/host/sdhci-acpi.c96
-rw-r--r--drivers/mmc/host/sdhci-bcm-kona.c348
-rw-r--r--drivers/mmc/host/sdhci-bcm2835.c2
-rw-r--r--drivers/mmc/host/sdhci-cns3xxx.c2
-rw-r--r--drivers/mmc/host/sdhci-dove.c2
-rw-r--r--drivers/mmc/host/sdhci-esdhc-imx.c30
-rw-r--r--drivers/mmc/host/sdhci-esdhc.h16
-rw-r--r--drivers/mmc/host/sdhci-of-esdhc.c63
-rw-r--r--drivers/mmc/host/sdhci-of-hlwd.c2
-rw-r--r--drivers/mmc/host/sdhci-pci.c43
-rw-r--r--drivers/mmc/host/sdhci-pltfm.c23
-rw-r--r--drivers/mmc/host/sdhci-pltfm.h14
-rw-r--r--drivers/mmc/host/sdhci-pxav2.c4
-rw-r--r--drivers/mmc/host/sdhci-pxav3.c18
-rw-r--r--drivers/mmc/host/sdhci-s3c.c1
-rw-r--r--drivers/mmc/host/sdhci-sirf.c59
-rw-r--r--drivers/mmc/host/sdhci-spear.c2
-rw-r--r--drivers/mmc/host/sdhci-tegra.c11
-rw-r--r--drivers/mmc/host/sdhci.c63
-rw-r--r--drivers/mmc/host/sdhci.h1
-rw-r--r--drivers/mmc/host/sh_mmcif.c12
-rw-r--r--drivers/mmc/host/sh_mobile_sdhi.c34
-rw-r--r--drivers/mmc/host/tmio_mmc.c2
-rw-r--r--drivers/mmc/host/tmio_mmc.h21
-rw-r--r--drivers/mmc/host/tmio_mmc_dma.c48
-rw-r--r--drivers/mmc/host/tmio_mmc_pio.c40
-rw-r--r--drivers/mmc/host/wmt-sdmmc.c2
-rw-r--r--include/linux/mfd/tmio.h5
-rw-r--r--include/linux/mmc/card.h11
-rw-r--r--include/linux/mmc/core.h5
-rw-r--r--include/linux/mmc/host.h12
-rw-r--r--include/linux/mmc/sdhci.h6
-rw-r--r--include/linux/platform_data/mmc-esdhc-imx.h1
73 files changed, 1807 insertions, 790 deletions
diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt b/Documentation/devicetree/bindings/mmc/mmc.txt
index 85aada2263d5..458b57f199af 100644
--- a/Documentation/devicetree/bindings/mmc/mmc.txt
+++ b/Documentation/devicetree/bindings/mmc/mmc.txt
@@ -28,6 +28,7 @@ Optional properties:
- cap-mmc-highspeed: MMC high-speed timing is supported
- cap-power-off-card: powering off the card is safe
- cap-sdio-irq: enable SDIO IRQ signalling on this interface
+- full-pwr-cycle: full power cycle of the card is supported
*NOTE* on CD and WP polarity. To use common for all SD/MMC host controllers line
polarity properties, we have to fix the meaning of the "normal" and "inverted"
diff --git a/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt
new file mode 100644
index 000000000000..8a3d91d47b6a
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt
@@ -0,0 +1,23 @@
+* Rockchip specific extensions to the Synopsis Designware Mobile
+ Storage Host Controller
+
+The Synopsis designware mobile storage host controller is used to interface
+a SoC with storage medium such as eMMC or SD/MMC cards. This file documents
+differences between the core Synopsis dw mshc controller properties described
+by synopsis-dw-mshc.txt and the properties used by the Rockchip specific
+extensions to the Synopsis Designware Mobile Storage Host Controller.
+
+Required Properties:
+
+* compatible: should be
+ - "rockchip,rk2928-dw-mshc": for Rockchip RK2928 and following
+
+Example:
+
+ rkdwmmc0@12200000 {
+ compatible = "rockchip,rk2928-dw-mshc";
+ reg = <0x12200000 0x1000>;
+ interrupts = <0 75 0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
diff --git a/Documentation/devicetree/bindings/mmc/synopsis-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/synopsis-dw-mshc.txt
index 1180d7814af8..cdcebea9c6f5 100644
--- a/Documentation/devicetree/bindings/mmc/synopsis-dw-mshc.txt
+++ b/Documentation/devicetree/bindings/mmc/synopsis-dw-mshc.txt
@@ -39,6 +39,19 @@ Required Properties:
Optional properties:
+* clocks: from common clock binding: handle to biu and ciu clocks for the
+ bus interface unit clock and the card interface unit clock.
+
+* clock-names: from common clock binding: Shall be "biu" and "ciu".
+ If the biu clock is missing we'll simply skip enabling it. If the
+ ciu clock is missing we'll just assume that the clock is running at
+ clock-frequency. It is an error to omit both the ciu clock and the
+ clock-frequency.
+
+* clock-frequency: should be the frequency (in Hz) of the ciu clock. If this
+ is specified and the ciu clock is specified then we'll try to set the ciu
+ clock to this at probe time.
+
* num-slots: specifies the number of slots supported by the controller.
The number of physical slots actually used could be equal or less than the
value specified by num-slots. If this property is not specified, the value
@@ -55,6 +68,9 @@ Optional properties:
* broken-cd: as documented in mmc core bindings.
+* vmmc-supply: The phandle to the regulator to use for vmmc. If this is
+ specified we'll defer probe until we can find this regulator.
+
Aliases:
- All the MSHC controller nodes should be represented in the aliases node using
@@ -67,6 +83,8 @@ board specific portions as listed below.
dwmmc0@12200000 {
compatible = "snps,dw-mshc";
+ clocks = <&clock 351>, <&clock 132>;
+ clock-names = "biu", "ciu";
reg = <0x12200000 0x1000>;
interrupts = <0 75 0>;
#address-cells = <1>;
@@ -74,11 +92,13 @@ board specific portions as listed below.
};
dwmmc0@12200000 {
+ clock-frequency = <400000000>;
num-slots = <1>;
supports-highspeed;
broken-cd;
fifo-depth = <0x80>;
card-detect-delay = <200>;
+ vmmc-supply = <&buck8>;
slot@0 {
reg = <0>;
diff --git a/arch/arm/configs/bcm_defconfig b/arch/arm/configs/bcm_defconfig
index e3bf2d65618e..65edf6d47215 100644
--- a/arch/arm/configs/bcm_defconfig
+++ b/arch/arm/configs/bcm_defconfig
@@ -78,6 +78,13 @@ CONFIG_BACKLIGHT_LCD_SUPPORT=y
CONFIG_LCD_CLASS_DEVICE=y
CONFIG_BACKLIGHT_CLASS_DEVICE=y
# CONFIG_USB_SUPPORT is not set
+CONFIG_MMC=y
+CONFIG_MMC_UNSAFE_RESUME=y
+CONFIG_MMC_BLOCK_MINORS=32
+CONFIG_MMC_TEST=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_BCM_KONA=y
CONFIG_NEW_LEDS=y
CONFIG_LEDS_CLASS=y
CONFIG_LEDS_TRIGGERS=y
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index dd27b0783d52..cd0b7f4a1ff2 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -34,6 +34,7 @@
#include <linux/delay.h>
#include <linux/capability.h>
#include <linux/compat.h>
+#include <linux/pm_runtime.h>
#include <linux/mmc/ioctl.h>
#include <linux/mmc/card.h>
@@ -58,6 +59,8 @@ MODULE_ALIAS("mmc:block");
#define INAND_CMD38_ARG_SECTRIM1 0x81
#define INAND_CMD38_ARG_SECTRIM2 0x88
#define MMC_BLK_TIMEOUT_MS (10 * 60 * 1000) /* 10 minute timeout */
+#define MMC_SANITIZE_REQ_TIMEOUT 240000
+#define MMC_EXTRACT_INDEX_FROM_ARG(x) ((x & 0x00FF0000) >> 16)
#define mmc_req_rel_wr(req) (((req->cmd_flags & REQ_FUA) || \
(req->cmd_flags & REQ_META)) && \
@@ -222,7 +225,7 @@ static ssize_t power_ro_lock_store(struct device *dev,
md = mmc_blk_get(dev_to_disk(dev));
card = md->queue.card;
- mmc_claim_host(card->host);
+ mmc_get_card(card);
ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BOOT_WP,
card->ext_csd.boot_ro_lock |
@@ -233,7 +236,7 @@ static ssize_t power_ro_lock_store(struct device *dev,
else
card->ext_csd.boot_ro_lock |= EXT_CSD_BOOT_WP_B_PWR_WP_EN;
- mmc_release_host(card->host);
+ mmc_put_card(card);
if (!ret) {
pr_info("%s: Locking boot partition ro until next power on\n",
@@ -408,6 +411,35 @@ static int ioctl_rpmb_card_status_poll(struct mmc_card *card, u32 *status,
return err;
}
+static int ioctl_do_sanitize(struct mmc_card *card)
+{
+ int err;
+
+ if (!(mmc_can_sanitize(card) &&
+ (card->host->caps2 & MMC_CAP2_SANITIZE))) {
+ pr_warn("%s: %s - SANITIZE is not supported\n",
+ mmc_hostname(card->host), __func__);
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ pr_debug("%s: %s - SANITIZE IN PROGRESS...\n",
+ mmc_hostname(card->host), __func__);
+
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_SANITIZE_START, 1,
+ MMC_SANITIZE_REQ_TIMEOUT);
+
+ if (err)
+ pr_err("%s: %s - EXT_CSD_SANITIZE_START failed. err=%d\n",
+ mmc_hostname(card->host), __func__, err);
+
+ pr_debug("%s: %s - SANITIZE COMPLETED\n", mmc_hostname(card->host),
+ __func__);
+out:
+ return err;
+}
+
static int mmc_blk_ioctl_cmd(struct block_device *bdev,
struct mmc_ioc_cmd __user *ic_ptr)
{
@@ -491,7 +523,7 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
mrq.cmd = &cmd;
- mmc_claim_host(card->host);
+ mmc_get_card(card);
err = mmc_blk_part_switch(card, md);
if (err)
@@ -510,6 +542,17 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
goto cmd_rel_host;
}
+ if ((MMC_EXTRACT_INDEX_FROM_ARG(cmd.arg) == EXT_CSD_SANITIZE_START) &&
+ (cmd.opcode == MMC_SWITCH)) {
+ err = ioctl_do_sanitize(card);
+
+ if (err)
+ pr_err("%s: ioctl_do_sanitize() failed. err = %d",
+ __func__, err);
+
+ goto cmd_rel_host;
+ }
+
mmc_wait_for_req(card->host, &mrq);
if (cmd.error) {
@@ -558,7 +601,7 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
}
cmd_rel_host:
- mmc_release_host(card->host);
+ mmc_put_card(card);
cmd_done:
mmc_blk_put(md);
@@ -939,10 +982,10 @@ static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq,
{
struct mmc_blk_data *md = mq->data;
struct mmc_card *card = md->queue.card;
- unsigned int from, nr, arg, trim_arg, erase_arg;
+ unsigned int from, nr, arg;
int err = 0, type = MMC_BLK_SECDISCARD;
- if (!(mmc_can_secure_erase_trim(card) || mmc_can_sanitize(card))) {
+ if (!(mmc_can_secure_erase_trim(card))) {
err = -EOPNOTSUPP;
goto out;
}
@@ -950,23 +993,11 @@ static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq,
from = blk_rq_pos(req);
nr = blk_rq_sectors(req);
- /* The sanitize operation is supported at v4.5 only */
- if (mmc_can_sanitize(card)) {
- erase_arg = MMC_ERASE_ARG;
- trim_arg = MMC_TRIM_ARG;
- } else {
- erase_arg = MMC_SECURE_ERASE_ARG;
- trim_arg = MMC_SECURE_TRIM1_ARG;
- }
+ if (mmc_can_trim(card) && !mmc_erase_group_aligned(card, from, nr))
+ arg = MMC_SECURE_TRIM1_ARG;
+ else
+ arg = MMC_SECURE_ERASE_ARG;
- if (mmc_erase_group_aligned(card, from, nr))
- arg = erase_arg;
- else if (mmc_can_trim(card))
- arg = trim_arg;
- else {
- err = -EINVAL;
- goto out;
- }
retry:
if (card->quirks & MMC_QUIRK_INAND_CMD38) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
@@ -1002,9 +1033,6 @@ retry:
goto out;
}
- if (mmc_can_sanitize(card))
- err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_SANITIZE_START, 1, 0);
out_retry:
if (err && !mmc_blk_reset(md, card->host, type))
goto retry;
@@ -1895,7 +1923,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
if (req && !mq->mqrq_prev->req)
/* claim host only for the first request */
- mmc_claim_host(card->host);
+ mmc_get_card(card);
ret = mmc_blk_part_switch(card, md);
if (ret) {
@@ -1939,7 +1967,7 @@ out:
* In case sepecial request, there is no reentry to
* the 'mmc_blk_issue_rq' with 'mqrq_prev->req'.
*/
- mmc_release_host(card->host);
+ mmc_put_card(card);
return ret;
}
@@ -2158,6 +2186,14 @@ static void mmc_blk_remove_req(struct mmc_blk_data *md)
struct mmc_card *card;
if (md) {
+ /*
+ * Flush remaining requests and free queues. It
+ * is freeing the queue that stops new requests
+ * from being accepted.
+ */
+ mmc_cleanup_queue(&md->queue);
+ if (md->flags & MMC_BLK_PACKED_CMD)
+ mmc_packed_clean(&md->queue);
card = md->queue.card;
if (md->disk->flags & GENHD_FL_UP) {
device_remove_file(disk_to_dev(md->disk), &md->force_ro);
@@ -2166,14 +2202,8 @@ static void mmc_blk_remove_req(struct mmc_blk_data *md)
device_remove_file(disk_to_dev(md->disk),
&md->power_ro_lock);
- /* Stop new requests from getting into the queue */
del_gendisk(md->disk);
}
-
- /* Then flush out any already in there */
- mmc_cleanup_queue(&md->queue);
- if (md->flags & MMC_BLK_PACKED_CMD)
- mmc_packed_clean(&md->queue);
mmc_blk_put(md);
}
}
@@ -2336,6 +2366,19 @@ static int mmc_blk_probe(struct mmc_card *card)
if (mmc_add_disk(part_md))
goto out;
}
+
+ pm_runtime_set_autosuspend_delay(&card->dev, 3000);
+ pm_runtime_use_autosuspend(&card->dev);
+
+ /*
+ * Don't enable runtime PM for SD-combo cards here. Leave that
+ * decision to be taken during the SDIO init sequence instead.
+ */
+ if (card->type != MMC_TYPE_SD_COMBO) {
+ pm_runtime_set_active(&card->dev);
+ pm_runtime_enable(&card->dev);
+ }
+
return 0;
out:
@@ -2349,20 +2392,24 @@ static void mmc_blk_remove(struct mmc_card *card)
struct mmc_blk_data *md = mmc_get_drvdata(card);
mmc_blk_remove_parts(card, md);
+ pm_runtime_get_sync(&card->dev);
mmc_claim_host(card->host);
mmc_blk_part_switch(card, md);
mmc_release_host(card->host);
+ if (card->type != MMC_TYPE_SD_COMBO)
+ pm_runtime_disable(&card->dev);
+ pm_runtime_put_noidle(&card->dev);
mmc_blk_remove_req(md);
mmc_set_drvdata(card, NULL);
}
-#ifdef CONFIG_PM
-static int mmc_blk_suspend(struct mmc_card *card)
+static int _mmc_blk_suspend(struct mmc_card *card)
{
struct mmc_blk_data *part_md;
struct mmc_blk_data *md = mmc_get_drvdata(card);
if (md) {
+ pm_runtime_get_sync(&card->dev);
mmc_queue_suspend(&md->queue);
list_for_each_entry(part_md, &md->part, part) {
mmc_queue_suspend(&part_md->queue);
@@ -2371,6 +2418,17 @@ static int mmc_blk_suspend(struct mmc_card *card)
return 0;
}
+static void mmc_blk_shutdown(struct mmc_card *card)
+{
+ _mmc_blk_suspend(card);
+}
+
+#ifdef CONFIG_PM
+static int mmc_blk_suspend(struct mmc_card *card)
+{
+ return _mmc_blk_suspend(card);
+}
+
static int mmc_blk_resume(struct mmc_card *card)
{
struct mmc_blk_data *part_md;
@@ -2386,6 +2444,7 @@ static int mmc_blk_resume(struct mmc_card *card)
list_for_each_entry(part_md, &md->part, part) {
mmc_queue_resume(&part_md->queue);
}
+ pm_runtime_put(&card->dev);
}
return 0;
}
@@ -2402,6 +2461,7 @@ static struct mmc_driver mmc_driver = {
.remove = mmc_blk_remove,
.suspend = mmc_blk_suspend,
.resume = mmc_blk_resume,
+ .shutdown = mmc_blk_shutdown,
};
static int __init mmc_blk_init(void)
diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c
index 759714ed6bee..a69df5216274 100644
--- a/drivers/mmc/card/mmc_test.c
+++ b/drivers/mmc/card/mmc_test.c
@@ -3025,12 +3025,17 @@ static void mmc_test_remove(struct mmc_card *card)
mmc_test_free_dbgfs_file(card);
}
+static void mmc_test_shutdown(struct mmc_card *card)
+{
+}
+
static struct mmc_driver mmc_driver = {
.drv = {
.name = "mmc_test",
},
.probe = mmc_test_probe,
.remove = mmc_test_remove,
+ .shutdown = mmc_test_shutdown,
};
static int __init mmc_test_init(void)
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index 9447a0e970d1..fa9632eb63f1 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -173,7 +173,7 @@ static void mmc_queue_setup_discard(struct request_queue *q,
/* granularity must not be greater than max. discard */
if (card->pref_erase > max_discard)
q->limits.discard_granularity = 0;
- if (mmc_can_secure_erase_trim(card) || mmc_can_sanitize(card))
+ if (mmc_can_secure_erase_trim(card))
queue_flag_set_unlocked(QUEUE_FLAG_SECDISCARD, q);
}
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index 9d5c71125576..704bf66f5873 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -122,15 +122,39 @@ static int mmc_bus_remove(struct device *dev)
return 0;
}
+static void mmc_bus_shutdown(struct device *dev)
+{
+ struct mmc_driver *drv = to_mmc_driver(dev->driver);
+ struct mmc_card *card = mmc_dev_to_card(dev);
+ struct mmc_host *host = card->host;
+ int ret;
+
+ if (dev->driver && drv->shutdown)
+ drv->shutdown(card);
+
+ if (host->bus_ops->shutdown) {
+ ret = host->bus_ops->shutdown(host);
+ if (ret)
+ pr_warn("%s: error %d during shutdown\n",
+ mmc_hostname(host), ret);
+ }
+}
+
#ifdef CONFIG_PM_SLEEP
static int mmc_bus_suspend(struct device *dev)
{
struct mmc_driver *drv = to_mmc_driver(dev->driver);
struct mmc_card *card = mmc_dev_to_card(dev);
- int ret = 0;
+ struct mmc_host *host = card->host;
+ int ret;
- if (dev->driver && drv->suspend)
+ if (dev->driver && drv->suspend) {
ret = drv->suspend(card);
+ if (ret)
+ return ret;
+ }
+
+ ret = host->bus_ops->suspend(host);
return ret;
}
@@ -138,10 +162,17 @@ static int mmc_bus_resume(struct device *dev)
{
struct mmc_driver *drv = to_mmc_driver(dev->driver);
struct mmc_card *card = mmc_dev_to_card(dev);
- int ret = 0;
+ struct mmc_host *host = card->host;
+ int ret;
+
+ ret = host->bus_ops->resume(host);
+ if (ret)
+ pr_warn("%s: error %d during resume (card was removed?)\n",
+ mmc_hostname(host), ret);
if (dev->driver && drv->resume)
ret = drv->resume(card);
+
return ret;
}
#endif
@@ -151,15 +182,25 @@ static int mmc_bus_resume(struct device *dev)
static int mmc_runtime_suspend(struct device *dev)
{
struct mmc_card *card = mmc_dev_to_card(dev);
+ struct mmc_host *host = card->host;
+ int ret = 0;
+
+ if (host->bus_ops->runtime_suspend)
+ ret = host->bus_ops->runtime_suspend(host);
- return mmc_power_save_host(card->host);
+ return ret;
}
static int mmc_runtime_resume(struct device *dev)
{
struct mmc_card *card = mmc_dev_to_card(dev);
+ struct mmc_host *host = card->host;
+ int ret = 0;
+
+ if (host->bus_ops->runtime_resume)
+ ret = host->bus_ops->runtime_resume(host);
- return mmc_power_restore_host(card->host);
+ return ret;
}
static int mmc_runtime_idle(struct device *dev)
@@ -182,6 +223,7 @@ static struct bus_type mmc_bus_type = {
.uevent = mmc_bus_uevent,
.probe = mmc_bus_probe,
.remove = mmc_bus_remove,
+ .shutdown = mmc_bus_shutdown,
.pm = &mmc_bus_pm_ops,
};
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index c40396f23202..49a5bca418bd 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -402,6 +402,7 @@ static int mmc_wait_for_data_req_done(struct mmc_host *host,
context_info->is_done_rcv = false;
context_info->is_new_req = false;
cmd = mrq->cmd;
+
if (!cmd->error || !cmd->retries ||
mmc_card_removed(host->card)) {
err = host->areq->err_check(host->card,
@@ -436,6 +437,24 @@ static void mmc_wait_for_req_done(struct mmc_host *host,
wait_for_completion(&mrq->completion);
cmd = mrq->cmd;
+
+ /*
+ * If host has timed out waiting for the sanitize
+ * to complete, card might be still in programming state
+ * so let's try to bring the card out of programming
+ * state.
+ */
+ if (cmd->sanitize_busy && cmd->error == -ETIMEDOUT) {
+ if (!mmc_interrupt_hpi(host->card)) {
+ pr_warning("%s: %s: Interrupted sanitize\n",
+ mmc_hostname(host), __func__);
+ cmd->error = 0;
+ break;
+ } else {
+ pr_err("%s: %s: Failed to interrupt sanitize\n",
+ mmc_hostname(host), __func__);
+ }
+ }
if (!cmd->error || !cmd->retries ||
mmc_card_removed(host->card))
break;
@@ -952,6 +971,29 @@ void mmc_release_host(struct mmc_host *host)
EXPORT_SYMBOL(mmc_release_host);
/*
+ * This is a helper function, which fetches a runtime pm reference for the
+ * card device and also claims the host.
+ */
+void mmc_get_card(struct mmc_card *card)
+{
+ pm_runtime_get_sync(&card->dev);
+ mmc_claim_host(card->host);
+}
+EXPORT_SYMBOL(mmc_get_card);
+
+/*
+ * This is a helper function, which releases the host and drops the runtime
+ * pm reference for the card device.
+ */
+void mmc_put_card(struct mmc_card *card)
+{
+ mmc_release_host(card->host);
+ pm_runtime_mark_last_busy(&card->dev);
+ pm_runtime_put_autosuspend(&card->dev);
+}
+EXPORT_SYMBOL(mmc_put_card);
+
+/*
* Internal function that does the actual ios call to the host driver,
* optionally printing some debug output.
*/
@@ -1459,7 +1501,7 @@ void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type)
* If a host does all the power sequencing itself, ignore the
* initial MMC_POWER_UP stage.
*/
-static void mmc_power_up(struct mmc_host *host)
+void mmc_power_up(struct mmc_host *host)
{
int bit;
@@ -2325,14 +2367,13 @@ int mmc_detect_card_removed(struct mmc_host *host)
* The card will be considered unchanged unless we have been asked to
* detect a change or host requires polling to provide card detection.
*/
- if (!host->detect_change && !(host->caps & MMC_CAP_NEEDS_POLL) &&
- !(host->caps2 & MMC_CAP2_DETECT_ON_ERR))
+ if (!host->detect_change && !(host->caps & MMC_CAP_NEEDS_POLL))
return ret;
host->detect_change = 0;
if (!ret) {
ret = _mmc_detect_card_removed(host);
- if (ret && (host->caps2 & MMC_CAP2_DETECT_ON_ERR)) {
+ if (ret && (host->caps & MMC_CAP_NEEDS_POLL)) {
/*
* Schedule a detect work as soon as possible to let a
* rescan handle the card removal.
@@ -2442,9 +2483,7 @@ void mmc_stop_host(struct mmc_host *host)
mmc_bus_get(host);
if (host->bus_ops && !host->bus_dead) {
/* Calling bus_ops->remove() with a claimed host can deadlock */
- if (host->bus_ops->remove)
- host->bus_ops->remove(host);
-
+ host->bus_ops->remove(host);
mmc_claim_host(host);
mmc_detach_bus(host);
mmc_power_off(host);
@@ -2509,52 +2548,6 @@ int mmc_power_restore_host(struct mmc_host *host)
}
EXPORT_SYMBOL(mmc_power_restore_host);
-int mmc_card_awake(struct mmc_host *host)
-{
- int err = -ENOSYS;
-
- if (host->caps2 & MMC_CAP2_NO_SLEEP_CMD)
- return 0;
-
- mmc_bus_get(host);
-
- if (host->bus_ops && !host->bus_dead && host->bus_ops->awake)
- err = host->bus_ops->awake(host);
-
- mmc_bus_put(host);
-
- return err;
-}
-EXPORT_SYMBOL(mmc_card_awake);
-
-int mmc_card_sleep(struct mmc_host *host)
-{
- int err = -ENOSYS;
-
- if (host->caps2 & MMC_CAP2_NO_SLEEP_CMD)
- return 0;
-
- mmc_bus_get(host);
-
- if (host->bus_ops && !host->bus_dead && host->bus_ops->sleep)
- err = host->bus_ops->sleep(host);
-
- mmc_bus_put(host);
-
- return err;
-}
-EXPORT_SYMBOL(mmc_card_sleep);
-
-int mmc_card_can_sleep(struct mmc_host *host)
-{
- struct mmc_card *card = host->card;
-
- if (card && mmc_card_mmc(card) && card->ext_csd.rev >= 3)
- return 1;
- return 0;
-}
-EXPORT_SYMBOL(mmc_card_can_sleep);
-
/*
* Flush the cache to the non-volatile storage.
*/
@@ -2626,48 +2619,9 @@ EXPORT_SYMBOL(mmc_cache_ctrl);
*/
int mmc_suspend_host(struct mmc_host *host)
{
- int err = 0;
-
- cancel_delayed_work(&host->detect);
- mmc_flush_scheduled_work();
-
- mmc_bus_get(host);
- if (host->bus_ops && !host->bus_dead) {
- if (host->bus_ops->suspend) {
- if (mmc_card_doing_bkops(host->card)) {
- err = mmc_stop_bkops(host->card);
- if (err)
- goto out;
- }
- err = host->bus_ops->suspend(host);
- }
-
- if (err == -ENOSYS || !host->bus_ops->resume) {
- /*
- * We simply "remove" the card in this case.
- * It will be redetected on resume. (Calling
- * bus_ops->remove() with a claimed host can
- * deadlock.)
- */
- if (host->bus_ops->remove)
- host->bus_ops->remove(host);
- mmc_claim_host(host);
- mmc_detach_bus(host);
- mmc_power_off(host);
- mmc_release_host(host);
- host->pm_flags = 0;
- err = 0;
- }
- }
- mmc_bus_put(host);
-
- if (!err && !mmc_card_keep_power(host))
- mmc_power_off(host);
-
-out:
- return err;
+ /* This function is deprecated */
+ return 0;
}
-
EXPORT_SYMBOL(mmc_suspend_host);
/**
@@ -2676,39 +2630,8 @@ EXPORT_SYMBOL(mmc_suspend_host);
*/
int mmc_resume_host(struct mmc_host *host)
{
- int err = 0;
-
- mmc_bus_get(host);
- if (host->bus_ops && !host->bus_dead) {
- if (!mmc_card_keep_power(host)) {
- mmc_power_up(host);
- mmc_select_voltage(host, host->ocr);
- /*
- * Tell runtime PM core we just powered up the card,
- * since it still believes the card is powered off.
- * Note that currently runtime PM is only enabled
- * for SDIO cards that are MMC_CAP_POWER_OFF_CARD
- */
- if (mmc_card_sdio(host->card) &&
- (host->caps & MMC_CAP_POWER_OFF_CARD)) {
- pm_runtime_disable(&host->card->dev);
- pm_runtime_set_active(&host->card->dev);
- pm_runtime_enable(&host->card->dev);
- }
- }
- BUG_ON(!host->bus_ops->resume);
- err = host->bus_ops->resume(host);
- if (err) {
- pr_warning("%s: error %d during resume "
- "(card was removed?)\n",
- mmc_hostname(host), err);
- err = 0;
- }
- }
- host->pm_flags &= ~MMC_PM_KEEP_POWER;
- mmc_bus_put(host);
-
- return err;
+ /* This function is deprecated */
+ return 0;
}
EXPORT_SYMBOL(mmc_resume_host);
@@ -2727,29 +2650,22 @@ int mmc_pm_notify(struct notifier_block *notify_block,
switch (mode) {
case PM_HIBERNATION_PREPARE:
case PM_SUSPEND_PREPARE:
- if (host->card && mmc_card_mmc(host->card) &&
- mmc_card_doing_bkops(host->card)) {
- err = mmc_stop_bkops(host->card);
- if (err) {
- pr_err("%s: didn't stop bkops\n",
- mmc_hostname(host));
- return err;
- }
- mmc_card_clr_doing_bkops(host->card);
- }
-
spin_lock_irqsave(&host->lock, flags);
host->rescan_disable = 1;
spin_unlock_irqrestore(&host->lock, flags);
cancel_delayed_work_sync(&host->detect);
- if (!host->bus_ops || host->bus_ops->suspend)
+ if (!host->bus_ops)
break;
- /* Calling bus_ops->remove() with a claimed host can deadlock */
- if (host->bus_ops->remove)
- host->bus_ops->remove(host);
+ /* Validate prerequisites for suspend */
+ if (host->bus_ops->pre_suspend)
+ err = host->bus_ops->pre_suspend(host);
+ if (!err && host->bus_ops->suspend)
+ break;
+ /* Calling bus_ops->remove() with a claimed host can deadlock */
+ host->bus_ops->remove(host);
mmc_claim_host(host);
mmc_detach_bus(host);
mmc_power_off(host);
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index b9f18a2a8874..5345d156493e 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -16,15 +16,17 @@
#define MMC_CMD_RETRIES 3
struct mmc_bus_ops {
- int (*awake)(struct mmc_host *);
- int (*sleep)(struct mmc_host *);
void (*remove)(struct mmc_host *);
void (*detect)(struct mmc_host *);
+ int (*pre_suspend)(struct mmc_host *);
int (*suspend)(struct mmc_host *);
int (*resume)(struct mmc_host *);
+ int (*runtime_suspend)(struct mmc_host *);
+ int (*runtime_resume)(struct mmc_host *);
int (*power_save)(struct mmc_host *);
int (*power_restore)(struct mmc_host *);
int (*alive)(struct mmc_host *);
+ int (*shutdown)(struct mmc_host *);
};
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
@@ -44,6 +46,7 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage);
int __mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage);
void mmc_set_timing(struct mmc_host *host, unsigned int timing);
void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type);
+void mmc_power_up(struct mmc_host *host);
void mmc_power_off(struct mmc_host *host);
void mmc_power_cycle(struct mmc_host *host);
diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c
index 35c2f85b1956..54829c0ed000 100644
--- a/drivers/mmc/core/debugfs.c
+++ b/drivers/mmc/core/debugfs.c
@@ -258,13 +258,13 @@ static int mmc_dbg_card_status_get(void *data, u64 *val)
u32 status;
int ret;
- mmc_claim_host(card->host);
+ mmc_get_card(card);
ret = mmc_send_status(data, &status);
if (!ret)
*val = status;
- mmc_release_host(card->host);
+ mmc_put_card(card);
return ret;
}
@@ -291,9 +291,9 @@ static int mmc_ext_csd_open(struct inode *inode, struct file *filp)
goto out_free;
}
- mmc_claim_host(card->host);
+ mmc_get_card(card);
err = mmc_send_ext_csd(card, ext_csd);
- mmc_release_host(card->host);
+ mmc_put_card(card);
if (err)
goto out_free;
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 2a3593d9f87d..6fb6f77450cb 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -306,7 +306,7 @@ static inline void mmc_host_clk_sysfs_init(struct mmc_host *host)
* parse the properties and set respective generic mmc-host flags and
* parameters.
*/
-void mmc_of_parse(struct mmc_host *host)
+int mmc_of_parse(struct mmc_host *host)
{
struct device_node *np;
u32 bus_width;
@@ -315,7 +315,7 @@ void mmc_of_parse(struct mmc_host *host)
int len, ret, gpio;
if (!host->parent || !host->parent->of_node)
- return;
+ return 0;
np = host->parent->of_node;
@@ -338,6 +338,7 @@ void mmc_of_parse(struct mmc_host *host)
default:
dev_err(host->parent,
"Invalid \"bus-width\" value %ud!\n", bus_width);
+ return -EINVAL;
}
/* f_max is obtained from the optional "max-frequency" property */
@@ -367,18 +368,22 @@ void mmc_of_parse(struct mmc_host *host)
host->caps |= MMC_CAP_NEEDS_POLL;
gpio = of_get_named_gpio_flags(np, "cd-gpios", 0, &flags);
+ if (gpio == -EPROBE_DEFER)
+ return gpio;
if (gpio_is_valid(gpio)) {
if (!(flags & OF_GPIO_ACTIVE_LOW))
gpio_inv_cd = true;
ret = mmc_gpio_request_cd(host, gpio);
- if (ret < 0)
+ if (ret < 0) {
dev_err(host->parent,
"Failed to request CD GPIO #%d: %d!\n",
gpio, ret);
- else
+ return ret;
+ } else {
dev_info(host->parent, "Got CD GPIO #%d.\n",
gpio);
+ }
}
if (explicit_inv_cd ^ gpio_inv_cd)
@@ -389,14 +394,23 @@ void mmc_of_parse(struct mmc_host *host)
explicit_inv_wp = of_property_read_bool(np, "wp-inverted");
gpio = of_get_named_gpio_flags(np, "wp-gpios", 0, &flags);
+ if (gpio == -EPROBE_DEFER) {
+ ret = -EPROBE_DEFER;
+ goto out;
+ }
if (gpio_is_valid(gpio)) {
if (!(flags & OF_GPIO_ACTIVE_LOW))
gpio_inv_wp = true;
ret = mmc_gpio_request_ro(host, gpio);
- if (ret < 0)
+ if (ret < 0) {
dev_err(host->parent,
"Failed to request WP GPIO: %d!\n", ret);
+ goto out;
+ } else {
+ dev_info(host->parent, "Got WP GPIO #%d.\n",
+ gpio);
+ }
}
if (explicit_inv_wp ^ gpio_inv_wp)
host->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
@@ -409,10 +423,18 @@ void mmc_of_parse(struct mmc_host *host)
host->caps |= MMC_CAP_POWER_OFF_CARD;
if (of_find_property(np, "cap-sdio-irq", &len))
host->caps |= MMC_CAP_SDIO_IRQ;
+ if (of_find_property(np, "full-pwr-cycle", &len))
+ host->caps2 |= MMC_CAP2_FULL_PWR_CYCLE;
if (of_find_property(np, "keep-power-in-suspend", &len))
host->pm_caps |= MMC_PM_KEEP_POWER;
if (of_find_property(np, "enable-sdio-wakeup", &len))
host->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
+
+ return 0;
+
+out:
+ mmc_gpio_free_cd(host);
+ return ret;
}
EXPORT_SYMBOL(mmc_of_parse);
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 0cbd1effe960..6d02012a1d0b 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -293,7 +293,7 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
}
card->ext_csd.rev = ext_csd[EXT_CSD_REV];
- if (card->ext_csd.rev > 6) {
+ if (card->ext_csd.rev > 7) {
pr_err("%s: unrecognised EXT_CSD revision %d\n",
mmc_hostname(card->host), card->ext_csd.rev);
err = -EINVAL;
@@ -461,9 +461,31 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
*/
card->ext_csd.boot_ro_lock = ext_csd[EXT_CSD_BOOT_WP];
card->ext_csd.boot_ro_lockable = true;
+
+ /* Save power class values */
+ card->ext_csd.raw_pwr_cl_52_195 =
+ ext_csd[EXT_CSD_PWR_CL_52_195];
+ card->ext_csd.raw_pwr_cl_26_195 =
+ ext_csd[EXT_CSD_PWR_CL_26_195];
+ card->ext_csd.raw_pwr_cl_52_360 =
+ ext_csd[EXT_CSD_PWR_CL_52_360];
+ card->ext_csd.raw_pwr_cl_26_360 =
+ ext_csd[EXT_CSD_PWR_CL_26_360];
+ card->ext_csd.raw_pwr_cl_200_195 =
+ ext_csd[EXT_CSD_PWR_CL_200_195];
+ card->ext_csd.raw_pwr_cl_200_360 =
+ ext_csd[EXT_CSD_PWR_CL_200_360];
+ card->ext_csd.raw_pwr_cl_ddr_52_195 =
+ ext_csd[EXT_CSD_PWR_CL_DDR_52_195];
+ card->ext_csd.raw_pwr_cl_ddr_52_360 =
+ ext_csd[EXT_CSD_PWR_CL_DDR_52_360];
}
if (card->ext_csd.rev >= 5) {
+ /* Adjust production date as per JEDEC JESD84-B451 */
+ if (card->cid.year < 2010)
+ card->cid.year += 16;
+
/* check whether the eMMC card supports BKOPS */
if (ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) {
card->ext_csd.bkops = 1;
@@ -607,7 +629,23 @@ static int mmc_compare_ext_csds(struct mmc_card *card, unsigned bus_width)
(card->ext_csd.raw_sectors[2] ==
bw_ext_csd[EXT_CSD_SEC_CNT + 2]) &&
(card->ext_csd.raw_sectors[3] ==
- bw_ext_csd[EXT_CSD_SEC_CNT + 3]));
+ bw_ext_csd[EXT_CSD_SEC_CNT + 3]) &&
+ (card->ext_csd.raw_pwr_cl_52_195 ==
+ bw_ext_csd[EXT_CSD_PWR_CL_52_195]) &&
+ (card->ext_csd.raw_pwr_cl_26_195 ==
+ bw_ext_csd[EXT_CSD_PWR_CL_26_195]) &&
+ (card->ext_csd.raw_pwr_cl_52_360 ==
+ bw_ext_csd[EXT_CSD_PWR_CL_52_360]) &&
+ (card->ext_csd.raw_pwr_cl_26_360 ==
+ bw_ext_csd[EXT_CSD_PWR_CL_26_360]) &&
+ (card->ext_csd.raw_pwr_cl_200_195 ==
+ bw_ext_csd[EXT_CSD_PWR_CL_200_195]) &&
+ (card->ext_csd.raw_pwr_cl_200_360 ==
+ bw_ext_csd[EXT_CSD_PWR_CL_200_360]) &&
+ (card->ext_csd.raw_pwr_cl_ddr_52_195 ==
+ bw_ext_csd[EXT_CSD_PWR_CL_DDR_52_195]) &&
+ (card->ext_csd.raw_pwr_cl_ddr_52_360 ==
+ bw_ext_csd[EXT_CSD_PWR_CL_DDR_52_360]));
if (err)
err = -EINVAL;
@@ -676,11 +714,10 @@ static struct device_type mmc_type = {
* mmc_switch command.
*/
static int mmc_select_powerclass(struct mmc_card *card,
- unsigned int bus_width, u8 *ext_csd)
+ unsigned int bus_width)
{
int err = 0;
- unsigned int pwrclass_val;
- unsigned int index = 0;
+ unsigned int pwrclass_val = 0;
struct mmc_host *host;
BUG_ON(!card);
@@ -688,9 +725,6 @@ static int mmc_select_powerclass(struct mmc_card *card,
host = card->host;
BUG_ON(!host);
- if (ext_csd == NULL)
- return 0;
-
/* Power class selection is supported for versions >= 4.0 */
if (card->csd.mmca_vsn < CSD_SPEC_VER_4)
return 0;
@@ -702,13 +736,13 @@ static int mmc_select_powerclass(struct mmc_card *card,
switch (1 << host->ios.vdd) {
case MMC_VDD_165_195:
if (host->ios.clock <= 26000000)
- index = EXT_CSD_PWR_CL_26_195;
+ pwrclass_val = card->ext_csd.raw_pwr_cl_26_195;
else if (host->ios.clock <= 52000000)
- index = (bus_width <= EXT_CSD_BUS_WIDTH_8) ?
- EXT_CSD_PWR_CL_52_195 :
- EXT_CSD_PWR_CL_DDR_52_195;
+ pwrclass_val = (bus_width <= EXT_CSD_BUS_WIDTH_8) ?
+ card->ext_csd.raw_pwr_cl_52_195 :
+ card->ext_csd.raw_pwr_cl_ddr_52_195;
else if (host->ios.clock <= 200000000)
- index = EXT_CSD_PWR_CL_200_195;
+ pwrclass_val = card->ext_csd.raw_pwr_cl_200_195;
break;
case MMC_VDD_27_28:
case MMC_VDD_28_29:
@@ -720,13 +754,13 @@ static int mmc_select_powerclass(struct mmc_card *card,
case MMC_VDD_34_35:
case MMC_VDD_35_36:
if (host->ios.clock <= 26000000)
- index = EXT_CSD_PWR_CL_26_360;
+ pwrclass_val = card->ext_csd.raw_pwr_cl_26_360;
else if (host->ios.clock <= 52000000)
- index = (bus_width <= EXT_CSD_BUS_WIDTH_8) ?
- EXT_CSD_PWR_CL_52_360 :
- EXT_CSD_PWR_CL_DDR_52_360;
+ pwrclass_val = (bus_width <= EXT_CSD_BUS_WIDTH_8) ?
+ card->ext_csd.raw_pwr_cl_52_360 :
+ card->ext_csd.raw_pwr_cl_ddr_52_360;
else if (host->ios.clock <= 200000000)
- index = EXT_CSD_PWR_CL_200_360;
+ pwrclass_val = card->ext_csd.raw_pwr_cl_200_360;
break;
default:
pr_warning("%s: Voltage range not supported "
@@ -734,8 +768,6 @@ static int mmc_select_powerclass(struct mmc_card *card,
return -EINVAL;
}
- pwrclass_val = ext_csd[index];
-
if (bus_width & (EXT_CSD_BUS_WIDTH_8 | EXT_CSD_DDR_BUS_WIDTH_8))
pwrclass_val = (pwrclass_val & EXT_CSD_PWR_CL_8BIT_MASK) >>
EXT_CSD_PWR_CL_8BIT_SHIFT;
@@ -1013,11 +1045,9 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
}
/*
- * If the host supports the power_off_notify capability then
- * set the notification byte in the ext_csd register of device
+ * Enable power_off_notification byte in the ext_csd register
*/
- if ((host->caps2 & MMC_CAP2_POWEROFF_NOTIFY) &&
- (card->ext_csd.rev >= 6)) {
+ if (card->ext_csd.rev >= 6) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_POWER_OFF_NOTIFICATION,
EXT_CSD_POWER_ON,
@@ -1131,7 +1161,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ?
EXT_CSD_BUS_WIDTH_8 : EXT_CSD_BUS_WIDTH_4;
- err = mmc_select_powerclass(card, ext_csd_bits, ext_csd);
+ err = mmc_select_powerclass(card, ext_csd_bits);
if (err)
pr_warning("%s: power class selection to bus width %d"
" failed\n", mmc_hostname(card->host),
@@ -1164,8 +1194,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
bus_width = bus_widths[idx];
if (bus_width == MMC_BUS_WIDTH_1)
ddr = 0; /* no DDR for 1-bit width */
- err = mmc_select_powerclass(card, ext_csd_bits[idx][0],
- ext_csd);
+ err = mmc_select_powerclass(card, ext_csd_bits[idx][0]);
if (err)
pr_warning("%s: power class selection to "
"bus width %d failed\n",
@@ -1195,8 +1224,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
}
if (!err && ddr) {
- err = mmc_select_powerclass(card, ext_csd_bits[idx][1],
- ext_csd);
+ err = mmc_select_powerclass(card, ext_csd_bits[idx][1]);
if (err)
pr_warning("%s: power class selection to "
"bus width %d ddr %d failed\n",
@@ -1321,6 +1349,45 @@ err:
return err;
}
+static int mmc_can_sleep(struct mmc_card *card)
+{
+ return (card && card->ext_csd.rev >= 3);
+}
+
+static int mmc_sleep(struct mmc_host *host)
+{
+ struct mmc_command cmd = {0};
+ struct mmc_card *card = host->card;
+ int err;
+
+ if (host->caps2 & MMC_CAP2_NO_SLEEP_CMD)
+ return 0;
+
+ err = mmc_deselect_cards(host);
+ if (err)
+ return err;
+
+ cmd.opcode = MMC_SLEEP_AWAKE;
+ cmd.arg = card->rca << 16;
+ cmd.arg |= 1 << 15;
+
+ cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
+ err = mmc_wait_for_cmd(host, &cmd, 0);
+ if (err)
+ return err;
+
+ /*
+ * If the host does not wait while the card signals busy, then we will
+ * will have to wait the sleep/awake timeout. Note, we cannot use the
+ * SEND_STATUS command to poll the status because that command (and most
+ * others) is invalid while the card sleeps.
+ */
+ if (!(host->caps & MMC_CAP_WAIT_WHILE_BUSY))
+ mmc_delay(DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000));
+
+ return err;
+}
+
static int mmc_can_poweroff_notify(const struct mmc_card *card)
{
return card &&
@@ -1380,14 +1447,14 @@ static void mmc_detect(struct mmc_host *host)
BUG_ON(!host);
BUG_ON(!host->card);
- mmc_claim_host(host);
+ mmc_get_card(host->card);
/*
* Just check if our card has been removed.
*/
err = _mmc_detect_card_removed(host);
- mmc_release_host(host);
+ mmc_put_card(host->card);
if (err) {
mmc_remove(host);
@@ -1399,36 +1466,60 @@ static void mmc_detect(struct mmc_host *host)
}
}
-/*
- * Suspend callback from host.
- */
-static int mmc_suspend(struct mmc_host *host)
+static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
{
int err = 0;
+ unsigned int notify_type = is_suspend ? EXT_CSD_POWER_OFF_SHORT :
+ EXT_CSD_POWER_OFF_LONG;
BUG_ON(!host);
BUG_ON(!host->card);
mmc_claim_host(host);
+ if (mmc_card_doing_bkops(host->card)) {
+ err = mmc_stop_bkops(host->card);
+ if (err)
+ goto out;
+ }
+
err = mmc_cache_ctrl(host, 0);
if (err)
goto out;
- if (mmc_can_poweroff_notify(host->card))
- err = mmc_poweroff_notify(host->card, EXT_CSD_POWER_OFF_SHORT);
- else if (mmc_card_can_sleep(host))
- err = mmc_card_sleep(host);
+ if (mmc_can_poweroff_notify(host->card) &&
+ ((host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) || !is_suspend))
+ err = mmc_poweroff_notify(host->card, notify_type);
+ else if (mmc_can_sleep(host->card))
+ err = mmc_sleep(host);
else if (!mmc_host_is_spi(host))
err = mmc_deselect_cards(host);
host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200);
+ if (!err)
+ mmc_power_off(host);
out:
mmc_release_host(host);
return err;
}
/*
+ * Suspend callback from host.
+ */
+static int mmc_suspend(struct mmc_host *host)
+{
+ return _mmc_suspend(host, true);
+}
+
+/*
+ * Shutdown callback
+ */
+static int mmc_shutdown(struct mmc_host *host)
+{
+ return _mmc_suspend(host, false);
+}
+
+/*
* Resume callback from host.
*
* This function tries to determine if the same card is still present
@@ -1442,74 +1533,94 @@ static int mmc_resume(struct mmc_host *host)
BUG_ON(!host->card);
mmc_claim_host(host);
+ mmc_power_up(host);
+ mmc_select_voltage(host, host->ocr);
err = mmc_init_card(host, host->ocr, host->card);
mmc_release_host(host);
return err;
}
-static int mmc_power_restore(struct mmc_host *host)
+
+/*
+ * Callback for runtime_suspend.
+ */
+static int mmc_runtime_suspend(struct mmc_host *host)
{
- int ret;
+ int err;
+
+ if (!(host->caps & MMC_CAP_AGGRESSIVE_PM))
+ return 0;
- host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200);
mmc_claim_host(host);
- ret = mmc_init_card(host, host->ocr, host->card);
- mmc_release_host(host);
- return ret;
+ err = mmc_suspend(host);
+ if (err) {
+ pr_err("%s: error %d doing aggessive suspend\n",
+ mmc_hostname(host), err);
+ goto out;
+ }
+ mmc_power_off(host);
+
+out:
+ mmc_release_host(host);
+ return err;
}
-static int mmc_sleep(struct mmc_host *host)
+/*
+ * Callback for runtime_resume.
+ */
+static int mmc_runtime_resume(struct mmc_host *host)
{
- struct mmc_card *card = host->card;
- int err = -ENOSYS;
+ int err;
- if (card && card->ext_csd.rev >= 3) {
- err = mmc_card_sleepawake(host, 1);
- if (err < 0)
- pr_debug("%s: Error %d while putting card into sleep",
- mmc_hostname(host), err);
- }
+ if (!(host->caps & MMC_CAP_AGGRESSIVE_PM))
+ return 0;
- return err;
+ mmc_claim_host(host);
+
+ mmc_power_up(host);
+ err = mmc_resume(host);
+ if (err)
+ pr_err("%s: error %d doing aggessive resume\n",
+ mmc_hostname(host), err);
+
+ mmc_release_host(host);
+ return 0;
}
-static int mmc_awake(struct mmc_host *host)
+static int mmc_power_restore(struct mmc_host *host)
{
- struct mmc_card *card = host->card;
- int err = -ENOSYS;
+ int ret;
- if (card && card->ext_csd.rev >= 3) {
- err = mmc_card_sleepawake(host, 0);
- if (err < 0)
- pr_debug("%s: Error %d while awaking sleeping card",
- mmc_hostname(host), err);
- }
+ host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200);
+ mmc_claim_host(host);
+ ret = mmc_init_card(host, host->ocr, host->card);
+ mmc_release_host(host);
- return err;
+ return ret;
}
static const struct mmc_bus_ops mmc_ops = {
- .awake = mmc_awake,
- .sleep = mmc_sleep,
.remove = mmc_remove,
.detect = mmc_detect,
.suspend = NULL,
.resume = NULL,
.power_restore = mmc_power_restore,
.alive = mmc_alive,
+ .shutdown = mmc_shutdown,
};
static const struct mmc_bus_ops mmc_ops_unsafe = {
- .awake = mmc_awake,
- .sleep = mmc_sleep,
.remove = mmc_remove,
.detect = mmc_detect,
.suspend = mmc_suspend,
.resume = mmc_resume,
+ .runtime_suspend = mmc_runtime_suspend,
+ .runtime_resume = mmc_runtime_resume,
.power_restore = mmc_power_restore,
.alive = mmc_alive,
+ .shutdown = mmc_shutdown,
};
static void mmc_attach_bus_ops(struct mmc_host *host)
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index 49f04bc9d0eb..837fc7386e23 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -59,40 +59,6 @@ int mmc_deselect_cards(struct mmc_host *host)
return _mmc_select_card(host, NULL);
}
-int mmc_card_sleepawake(struct mmc_host *host, int sleep)
-{
- struct mmc_command cmd = {0};
- struct mmc_card *card = host->card;
- int err;
-
- if (sleep)
- mmc_deselect_cards(host);
-
- cmd.opcode = MMC_SLEEP_AWAKE;
- cmd.arg = card->rca << 16;
- if (sleep)
- cmd.arg |= 1 << 15;
-
- cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
- err = mmc_wait_for_cmd(host, &cmd, 0);
- if (err)
- return err;
-
- /*
- * If the host does not wait while the card signals busy, then we will
- * will have to wait the sleep/awake timeout. Note, we cannot use the
- * SEND_STATUS command to poll the status because that command (and most
- * others) is invalid while the card sleeps.
- */
- if (!(host->caps & MMC_CAP_WAIT_WHILE_BUSY))
- mmc_delay(DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000));
-
- if (!sleep)
- err = mmc_select_card(card);
-
- return err;
-}
-
int mmc_go_idle(struct mmc_host *host)
{
int err;
@@ -431,6 +397,8 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
cmd.cmd_timeout_ms = timeout_ms;
+ if (index == EXT_CSD_SANITIZE_START)
+ cmd.sanitize_busy = true;
err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
if (err)
diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h
index 3dd8941c2980..80ae9f4e0293 100644
--- a/drivers/mmc/core/mmc_ops.h
+++ b/drivers/mmc/core/mmc_ops.h
@@ -24,7 +24,6 @@ int mmc_send_status(struct mmc_card *card, u32 *status);
int mmc_send_cid(struct mmc_host *host, u32 *cid);
int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp);
int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
-int mmc_card_sleepawake(struct mmc_host *host, int sleep);
int mmc_bus_test(struct mmc_card *card, u8 bus_width);
int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status);
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 9e645e19cec6..176d125f5b57 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -646,8 +646,13 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
if (err)
goto out;
- /* SPI mode doesn't define CMD19 */
- if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning) {
+ /*
+ * SPI mode doesn't define CMD19 and tuning is only valid for SDR50 and
+ * SDR104 mode SD-cards. Note that tuning is mandatory for SDR104.
+ */
+ if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning &&
+ (card->sd_bus_speed == UHS_SDR50_BUS_SPEED ||
+ card->sd_bus_speed == UHS_SDR104_BUS_SPEED)) {
mmc_host_clk_hold(card->host);
err = card->host->ops->execute_tuning(card->host,
MMC_SEND_TUNING_BLOCK);
@@ -1037,14 +1042,14 @@ static void mmc_sd_detect(struct mmc_host *host)
BUG_ON(!host);
BUG_ON(!host->card);
- mmc_claim_host(host);
+ mmc_get_card(host->card);
/*
* Just check if our card has been removed.
*/
err = _mmc_detect_card_removed(host);
- mmc_release_host(host);
+ mmc_put_card(host->card);
if (err) {
mmc_sd_remove(host);
@@ -1070,6 +1075,8 @@ static int mmc_sd_suspend(struct mmc_host *host)
if (!mmc_host_is_spi(host))
err = mmc_deselect_cards(host);
host->card->state &= ~MMC_STATE_HIGHSPEED;
+ if (!err)
+ mmc_power_off(host);
mmc_release_host(host);
return err;
@@ -1089,12 +1096,61 @@ static int mmc_sd_resume(struct mmc_host *host)
BUG_ON(!host->card);
mmc_claim_host(host);
+ mmc_power_up(host);
+ mmc_select_voltage(host, host->ocr);
err = mmc_sd_init_card(host, host->ocr, host->card);
mmc_release_host(host);
return err;
}
+/*
+ * Callback for runtime_suspend.
+ */
+static int mmc_sd_runtime_suspend(struct mmc_host *host)
+{
+ int err;
+
+ if (!(host->caps & MMC_CAP_AGGRESSIVE_PM))
+ return 0;
+
+ mmc_claim_host(host);
+
+ err = mmc_sd_suspend(host);
+ if (err) {
+ pr_err("%s: error %d doing aggessive suspend\n",
+ mmc_hostname(host), err);
+ goto out;
+ }
+ mmc_power_off(host);
+
+out:
+ mmc_release_host(host);
+ return err;
+}
+
+/*
+ * Callback for runtime_resume.
+ */
+static int mmc_sd_runtime_resume(struct mmc_host *host)
+{
+ int err;
+
+ if (!(host->caps & MMC_CAP_AGGRESSIVE_PM))
+ return 0;
+
+ mmc_claim_host(host);
+
+ mmc_power_up(host);
+ err = mmc_sd_resume(host);
+ if (err)
+ pr_err("%s: error %d doing aggessive resume\n",
+ mmc_hostname(host), err);
+
+ mmc_release_host(host);
+ return 0;
+}
+
static int mmc_sd_power_restore(struct mmc_host *host)
{
int ret;
@@ -1114,15 +1170,19 @@ static const struct mmc_bus_ops mmc_sd_ops = {
.resume = NULL,
.power_restore = mmc_sd_power_restore,
.alive = mmc_sd_alive,
+ .shutdown = mmc_sd_suspend,
};
static const struct mmc_bus_ops mmc_sd_ops_unsafe = {
.remove = mmc_sd_remove,
.detect = mmc_sd_detect,
+ .runtime_suspend = mmc_sd_runtime_suspend,
+ .runtime_resume = mmc_sd_runtime_resume,
.suspend = mmc_sd_suspend,
.resume = mmc_sd_resume,
.power_restore = mmc_sd_power_restore,
.alive = mmc_sd_alive,
+ .shutdown = mmc_sd_suspend,
};
static void mmc_sd_attach_bus_ops(struct mmc_host *host)
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index 6889a821c1da..80d89cff7306 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -563,10 +563,18 @@ static int mmc_sdio_init_uhs_card(struct mmc_card *card)
if (err)
goto out;
- /* Initialize and start re-tuning timer */
- if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning)
+ /*
+ * SPI mode doesn't define CMD19 and tuning is only valid for SDR50 and
+ * SDR104 mode SD-cards. Note that tuning is mandatory for SDR104.
+ */
+ if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning &&
+ ((card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR50) ||
+ (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR104))) {
+ mmc_host_clk_hold(card->host);
err = card->host->ops->execute_tuning(card->host,
MMC_SEND_TUNING_BLOCK);
+ mmc_host_clk_release(card->host);
+ }
out:
@@ -902,11 +910,11 @@ out:
}
/*
- * SDIO suspend. We need to suspend all functions separately.
+ * SDIO pre_suspend. We need to suspend all functions separately.
* Therefore all registered functions must have drivers with suspend
* and resume methods. Failing that we simply remove the whole card.
*/
-static int mmc_sdio_suspend(struct mmc_host *host)
+static int mmc_sdio_pre_suspend(struct mmc_host *host)
{
int i, err = 0;
@@ -917,8 +925,26 @@ static int mmc_sdio_suspend(struct mmc_host *host)
if (!pmops || !pmops->suspend || !pmops->resume) {
/* force removal of entire card in that case */
err = -ENOSYS;
- } else
- err = pmops->suspend(&func->dev);
+ break;
+ }
+ }
+ }
+
+ return err;
+}
+
+/*
+ * SDIO suspend. Suspend all functions separately.
+ */
+static int mmc_sdio_suspend(struct mmc_host *host)
+{
+ int i, err = 0;
+
+ for (i = 0; i < host->card->sdio_funcs; i++) {
+ struct sdio_func *func = host->card->sdio_func[i];
+ if (func && sdio_func_present(func) && func->dev.driver) {
+ const struct dev_pm_ops *pmops = func->dev.driver->pm;
+ err = pmops->suspend(&func->dev);
if (err)
break;
}
@@ -937,6 +963,9 @@ static int mmc_sdio_suspend(struct mmc_host *host)
mmc_release_host(host);
}
+ if (!err && !mmc_card_keep_power(host))
+ mmc_power_off(host);
+
return err;
}
@@ -950,6 +979,23 @@ static int mmc_sdio_resume(struct mmc_host *host)
/* Basic card reinitialization. */
mmc_claim_host(host);
+ /* Restore power if needed */
+ if (!mmc_card_keep_power(host)) {
+ mmc_power_up(host);
+ mmc_select_voltage(host, host->ocr);
+ /*
+ * Tell runtime PM core we just powered up the card,
+ * since it still believes the card is powered off.
+ * Note that currently runtime PM is only enabled
+ * for SDIO cards that are MMC_CAP_POWER_OFF_CARD
+ */
+ if (host->caps & MMC_CAP_POWER_OFF_CARD) {
+ pm_runtime_disable(&host->card->dev);
+ pm_runtime_set_active(&host->card->dev);
+ pm_runtime_enable(&host->card->dev);
+ }
+ }
+
/* No need to reinitialize powered-resumed nonremovable cards */
if (mmc_card_is_removable(host) || !mmc_card_keep_power(host)) {
sdio_reset(host);
@@ -987,6 +1033,7 @@ static int mmc_sdio_resume(struct mmc_host *host)
}
}
+ host->pm_flags &= ~MMC_PM_KEEP_POWER;
return err;
}
@@ -1051,11 +1098,28 @@ out:
return ret;
}
+static int mmc_sdio_runtime_suspend(struct mmc_host *host)
+{
+ /* No references to the card, cut the power to it. */
+ mmc_power_off(host);
+ return 0;
+}
+
+static int mmc_sdio_runtime_resume(struct mmc_host *host)
+{
+ /* Restore power and re-initialize. */
+ mmc_power_up(host);
+ return mmc_sdio_power_restore(host);
+}
+
static const struct mmc_bus_ops mmc_sdio_ops = {
.remove = mmc_sdio_remove,
.detect = mmc_sdio_detect,
+ .pre_suspend = mmc_sdio_pre_suspend,
.suspend = mmc_sdio_suspend,
.resume = mmc_sdio_resume,
+ .runtime_suspend = mmc_sdio_runtime_suspend,
+ .runtime_resume = mmc_sdio_runtime_resume,
.power_restore = mmc_sdio_power_restore,
.alive = mmc_sdio_alive,
};
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 9ab8f8dee942..8a4c066787d7 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -249,6 +249,17 @@ config MMC_SDHCI_S3C_DMA
YMMV.
+config MMC_SDHCI_BCM_KONA
+ tristate "SDHCI support on Broadcom KONA platform"
+ depends on ARCH_BCM
+ select MMC_SDHCI_PLTFM
+ help
+ This selects the Broadcom Kona Secure Digital Host Controller
+ Interface(SDHCI) support.
+ This is used in Broadcom mobile SoCs.
+
+ If you have a controller with this interface, say Y or M here.
+
config MMC_SDHCI_BCM2835
tristate "SDHCI platform support for the BCM2835 SD/MMC Controller"
depends on ARCH_BCM2835
@@ -556,6 +567,14 @@ config MMC_DW_EXYNOS
Synopsys DesignWare Memory Card Interface driver. Select this option
for platforms based on Exynos4 and Exynos5 SoC's.
+config MMC_DW_SOCFPGA
+ tristate "SOCFPGA specific extensions for Synopsys DW Memory Card Interface"
+ depends on MMC_DW
+ select MMC_DW_PLTFM
+ help
+ This selects support for Altera SoCFPGA specific extensions to the
+ Synopsys DesignWare Memory Card Interface driver.
+
config MMC_DW_PCI
tristate "Synopsys Designware MCI support on PCI bus"
depends on MMC_DW && PCI
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index cd3228075553..d422e2167e19 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -42,6 +42,7 @@ obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o
obj-$(CONFIG_MMC_DW) += dw_mmc.o
obj-$(CONFIG_MMC_DW_PLTFM) += dw_mmc-pltfm.o
obj-$(CONFIG_MMC_DW_EXYNOS) += dw_mmc-exynos.o
+obj-$(CONFIG_MMC_DW_SOCFPGA) += dw_mmc-socfpga.o
obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o
obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o
obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o
@@ -60,6 +61,7 @@ obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o
obj-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci-tegra.o
obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o
obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o
+obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o
obj-$(CONFIG_MMC_SDHCI_BCM2835) += sdhci-bcm2835.o
ifeq ($(CONFIG_CB710_DEBUG),y)
diff --git a/drivers/mmc/host/android-goldfish.c b/drivers/mmc/host/android-goldfish.c
index 7780c14704c4..8b4e20a3f16c 100644
--- a/drivers/mmc/host/android-goldfish.c
+++ b/drivers/mmc/host/android-goldfish.c
@@ -546,8 +546,6 @@ static int goldfish_mmc_remove(struct platform_device *pdev)
{
struct goldfish_mmc_host *host = platform_get_drvdata(pdev);
- platform_set_drvdata(pdev, NULL);
-
BUG_ON(host == NULL);
mmc_remove_host(host->mmc);
diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c
index aca59d93d5a9..bdb84da74952 100644
--- a/drivers/mmc/host/atmel-mci.c
+++ b/drivers/mmc/host/atmel-mci.c
@@ -40,8 +40,6 @@
#include <asm/io.h>
#include <asm/unaligned.h>
-#include <mach/cpu.h>
-
#include "atmel-mci-regs.h"
#define ATMCI_DATA_ERROR_FLAGS (ATMCI_DCRCE | ATMCI_DTOE | ATMCI_OVRE | ATMCI_UNRE)
@@ -2475,8 +2473,6 @@ static int __exit atmci_remove(struct platform_device *pdev)
struct atmel_mci *host = platform_get_drvdata(pdev);
unsigned int i;
- platform_set_drvdata(pdev, NULL);
-
if (host->buffer)
dma_free_coherent(&pdev->dev, host->buf_size,
host->buffer, host->buf_phys_addr);
@@ -2504,7 +2500,7 @@ static int __exit atmci_remove(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int atmci_suspend(struct device *dev)
{
struct atmel_mci *host = dev_get_drvdata(dev);
@@ -2559,17 +2555,15 @@ static int atmci_resume(struct device *dev)
return ret;
}
-static SIMPLE_DEV_PM_OPS(atmci_pm, atmci_suspend, atmci_resume);
-#define ATMCI_PM_OPS (&atmci_pm)
-#else
-#define ATMCI_PM_OPS NULL
#endif
+static SIMPLE_DEV_PM_OPS(atmci_pm, atmci_suspend, atmci_resume);
+
static struct platform_driver atmci_driver = {
.remove = __exit_p(atmci_remove),
.driver = {
.name = "atmel_mci",
- .pm = ATMCI_PM_OPS,
+ .pm = &atmci_pm,
.of_match_table = of_match_ptr(atmci_dt_ids),
},
};
diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c
index 127a8fade4da..df9becdd2e99 100644
--- a/drivers/mmc/host/au1xmmc.c
+++ b/drivers/mmc/host/au1xmmc.c
@@ -1149,7 +1149,6 @@ static int au1xmmc_remove(struct platform_device *pdev)
kfree(host->ioarea);
mmc_free_host(host->mmc);
- platform_set_drvdata(pdev, NULL);
}
return 0;
}
diff --git a/drivers/mmc/host/bfin_sdh.c b/drivers/mmc/host/bfin_sdh.c
index fb4348c5b6ac..94fae2f1baaf 100644
--- a/drivers/mmc/host/bfin_sdh.c
+++ b/drivers/mmc/host/bfin_sdh.c
@@ -621,8 +621,6 @@ static int sdh_remove(struct platform_device *pdev)
{
struct mmc_host *mmc = platform_get_drvdata(pdev);
- platform_set_drvdata(pdev, NULL);
-
if (mmc) {
struct sdh_host *host = mmc_priv(mmc);
diff --git a/drivers/mmc/host/cb710-mmc.c b/drivers/mmc/host/cb710-mmc.c
index 777ca2046b27..9d6e2b844404 100644
--- a/drivers/mmc/host/cb710-mmc.c
+++ b/drivers/mmc/host/cb710-mmc.c
@@ -703,7 +703,7 @@ static int cb710_mmc_init(struct platform_device *pdev)
if (!mmc)
return -ENOMEM;
- dev_set_drvdata(&pdev->dev, mmc);
+ platform_set_drvdata(pdev, mmc);
/* harmless (maybe) magic */
pci_read_config_dword(chip->pdev, 0x48, &val);
diff --git a/drivers/mmc/host/cb710-mmc.h b/drivers/mmc/host/cb710-mmc.h
index e845c776bdd7..8984ec878fc9 100644
--- a/drivers/mmc/host/cb710-mmc.h
+++ b/drivers/mmc/host/cb710-mmc.h
@@ -24,7 +24,7 @@ struct cb710_mmc_reader {
static inline struct mmc_host *cb710_slot_to_mmc(struct cb710_slot *slot)
{
- return dev_get_drvdata(&slot->pdev.dev);
+ return platform_get_drvdata(&slot->pdev);
}
static inline struct cb710_slot *cb710_mmc_to_slot(struct mmc_host *mmc)
diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c
index 5dfb70c669dc..e9fa87df909c 100644
--- a/drivers/mmc/host/davinci_mmc.c
+++ b/drivers/mmc/host/davinci_mmc.c
@@ -1407,7 +1407,6 @@ static int __exit davinci_mmcsd_remove(struct platform_device *pdev)
{
struct mmc_davinci_host *host = platform_get_drvdata(pdev);
- platform_set_drvdata(pdev, NULL);
if (host) {
mmc_davinci_cpufreq_deregister(host);
diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c
index f013e7e3746b..866edef2e820 100644
--- a/drivers/mmc/host/dw_mmc-exynos.c
+++ b/drivers/mmc/host/dw_mmc-exynos.c
@@ -31,8 +31,6 @@
SDMMC_CLKSEL_CCLK_DRIVE(y) | \
SDMMC_CLKSEL_CCLK_DIVIDER(z))
-#define SDMMC_CMD_USE_HOLD_REG BIT(29)
-
#define EXYNOS4210_FIXED_CIU_CLK_DIV 2
#define EXYNOS4412_FIXED_CIU_CLK_DIV 4
diff --git a/drivers/mmc/host/dw_mmc-pci.c b/drivers/mmc/host/dw_mmc-pci.c
index 083fcd29c9c6..b456b0c35231 100644
--- a/drivers/mmc/host/dw_mmc-pci.c
+++ b/drivers/mmc/host/dw_mmc-pci.c
@@ -21,7 +21,6 @@
#include "dw_mmc.h"
#define PCI_BAR_NO 2
-#define COMPLETE_BAR 0
#define SYNOPSYS_DW_MCI_VENDOR_ID 0x700
#define SYNOPSYS_DW_MCI_DEVICE_ID 0x1107
/* Defining the Capabilities */
@@ -38,51 +37,37 @@ static struct dw_mci_board pci_board_data = {
};
static int dw_mci_pci_probe(struct pci_dev *pdev,
- const struct pci_device_id *entries)
+ const struct pci_device_id *entries)
{
struct dw_mci *host;
int ret;
- ret = pci_enable_device(pdev);
+ ret = pcim_enable_device(pdev);
if (ret)
return ret;
- if (pci_request_regions(pdev, "dw_mmc_pci")) {
- ret = -ENODEV;
- goto err_disable_dev;
- }
- host = kzalloc(sizeof(struct dw_mci), GFP_KERNEL);
- if (!host) {
- ret = -ENOMEM;
- goto err_release;
- }
+ host = devm_kzalloc(&pdev->dev, sizeof(struct dw_mci), GFP_KERNEL);
+ if (!host)
+ return -ENOMEM;
host->irq = pdev->irq;
host->irq_flags = IRQF_SHARED;
host->dev = &pdev->dev;
host->pdata = &pci_board_data;
- host->regs = pci_iomap(pdev, PCI_BAR_NO, COMPLETE_BAR);
- if (!host->regs) {
- ret = -EIO;
- goto err_unmap;
- }
+ ret = pcim_iomap_regions(pdev, 1 << PCI_BAR_NO, pci_name(pdev));
+ if (ret)
+ return ret;
+
+ host->regs = pcim_iomap_table(pdev)[0];
- pci_set_drvdata(pdev, host);
ret = dw_mci_probe(host);
if (ret)
- goto err_probe_failed;
- return ret;
-
-err_probe_failed:
- pci_iounmap(pdev, host->regs);
-err_unmap:
- kfree(host);
-err_release:
- pci_release_regions(pdev);
-err_disable_dev:
- pci_disable_device(pdev);
- return ret;
+ return ret;
+
+ pci_set_drvdata(pdev, host);
+
+ return 0;
}
static void dw_mci_pci_remove(struct pci_dev *pdev)
@@ -90,32 +75,23 @@ static void dw_mci_pci_remove(struct pci_dev *pdev)
struct dw_mci *host = pci_get_drvdata(pdev);
dw_mci_remove(host);
- pci_set_drvdata(pdev, NULL);
- pci_release_regions(pdev);
- pci_iounmap(pdev, host->regs);
- kfree(host);
- pci_disable_device(pdev);
}
#ifdef CONFIG_PM_SLEEP
static int dw_mci_pci_suspend(struct device *dev)
{
- int ret;
struct pci_dev *pdev = to_pci_dev(dev);
struct dw_mci *host = pci_get_drvdata(pdev);
- ret = dw_mci_suspend(host);
- return ret;
+ return dw_mci_suspend(host);
}
static int dw_mci_pci_resume(struct device *dev)
{
- int ret;
struct pci_dev *pdev = to_pci_dev(dev);
struct dw_mci *host = pci_get_drvdata(pdev);
- ret = dw_mci_resume(host);
- return ret;
+ return dw_mci_resume(host);
}
#else
#define dw_mci_pci_suspend NULL
diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c
index 41c27b74b003..ee525565aa77 100644
--- a/drivers/mmc/host/dw_mmc-pltfm.c
+++ b/drivers/mmc/host/dw_mmc-pltfm.c
@@ -24,8 +24,17 @@
#include "dw_mmc.h"
+static void dw_mci_rockchip_prepare_command(struct dw_mci *host, u32 *cmdr)
+{
+ *cmdr |= SDMMC_CMD_USE_HOLD_REG;
+}
+
+static const struct dw_mci_drv_data rockchip_drv_data = {
+ .prepare_command = dw_mci_rockchip_prepare_command,
+};
+
int dw_mci_pltfm_register(struct platform_device *pdev,
- const struct dw_mci_drv_data *drv_data)
+ const struct dw_mci_drv_data *drv_data)
{
struct dw_mci *host;
struct resource *regs;
@@ -35,10 +44,6 @@ int dw_mci_pltfm_register(struct platform_device *pdev,
if (!host)
return -ENOMEM;
- regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!regs)
- return -ENXIO;
-
host->irq = platform_get_irq(pdev, 0);
if (host->irq < 0)
return host->irq;
@@ -47,6 +52,8 @@ int dw_mci_pltfm_register(struct platform_device *pdev,
host->dev = &pdev->dev;
host->irq_flags = 0;
host->pdata = pdev->dev.platform_data;
+
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
host->regs = devm_ioremap_resource(&pdev->dev, regs);
if (IS_ERR(host->regs))
return PTR_ERR(host->regs);
@@ -58,52 +65,26 @@ int dw_mci_pltfm_register(struct platform_device *pdev,
}
platform_set_drvdata(pdev, host);
- ret = dw_mci_probe(host);
- return ret;
+ return dw_mci_probe(host);
}
EXPORT_SYMBOL_GPL(dw_mci_pltfm_register);
-static int dw_mci_pltfm_probe(struct platform_device *pdev)
-{
- return dw_mci_pltfm_register(pdev, NULL);
-}
-
-static int dw_mci_pltfm_remove(struct platform_device *pdev)
-{
- struct dw_mci *host = platform_get_drvdata(pdev);
-
- platform_set_drvdata(pdev, NULL);
- dw_mci_remove(host);
- return 0;
-}
-EXPORT_SYMBOL_GPL(dw_mci_pltfm_remove);
-
#ifdef CONFIG_PM_SLEEP
/*
* TODO: we should probably disable the clock to the card in the suspend path.
*/
static int dw_mci_pltfm_suspend(struct device *dev)
{
- int ret;
struct dw_mci *host = dev_get_drvdata(dev);
- ret = dw_mci_suspend(host);
- if (ret)
- return ret;
-
- return 0;
+ return dw_mci_suspend(host);
}
static int dw_mci_pltfm_resume(struct device *dev)
{
- int ret;
struct dw_mci *host = dev_get_drvdata(dev);
- ret = dw_mci_resume(host);
- if (ret)
- return ret;
-
- return 0;
+ return dw_mci_resume(host);
}
#else
#define dw_mci_pltfm_suspend NULL
@@ -115,10 +96,34 @@ EXPORT_SYMBOL_GPL(dw_mci_pltfm_pmops);
static const struct of_device_id dw_mci_pltfm_match[] = {
{ .compatible = "snps,dw-mshc", },
+ { .compatible = "rockchip,rk2928-dw-mshc",
+ .data = &rockchip_drv_data },
{},
};
MODULE_DEVICE_TABLE(of, dw_mci_pltfm_match);
+static int dw_mci_pltfm_probe(struct platform_device *pdev)
+{
+ const struct dw_mci_drv_data *drv_data = NULL;
+ const struct of_device_id *match;
+
+ if (pdev->dev.of_node) {
+ match = of_match_node(dw_mci_pltfm_match, pdev->dev.of_node);
+ drv_data = match->data;
+ }
+
+ return dw_mci_pltfm_register(pdev, drv_data);
+}
+
+int dw_mci_pltfm_remove(struct platform_device *pdev)
+{
+ struct dw_mci *host = platform_get_drvdata(pdev);
+
+ dw_mci_remove(host);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dw_mci_pltfm_remove);
+
static struct platform_driver dw_mci_pltfm_driver = {
.probe = dw_mci_pltfm_probe,
.remove = dw_mci_pltfm_remove,
diff --git a/drivers/mmc/host/dw_mmc-socfpga.c b/drivers/mmc/host/dw_mmc-socfpga.c
new file mode 100644
index 000000000000..14b5961a851c
--- /dev/null
+++ b/drivers/mmc/host/dw_mmc-socfpga.c
@@ -0,0 +1,140 @@
+/*
+ * Altera SoCFPGA Specific Extensions for Synopsys DW Multimedia Card Interface
+ * driver
+ *
+ * Copyright (C) 2012, Samsung Electronics Co., Ltd.
+ * Copyright (C) 2013 Altera Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Taken from dw_mmc-exynos.c
+ */
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/dw_mmc.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include "dw_mmc.h"
+#include "dw_mmc-pltfm.h"
+
+#define SYSMGR_SDMMCGRP_CTRL_OFFSET 0x108
+#define DRV_CLK_PHASE_SHIFT_SEL_MASK 0x7
+#define SYSMGR_SDMMC_CTRL_SET(smplsel, drvsel) \
+ ((((smplsel) & 0x7) << 3) | (((drvsel) & 0x7) << 0))
+
+/* SOCFPGA implementation specific driver private data */
+struct dw_mci_socfpga_priv_data {
+ u8 ciu_div; /* card interface unit divisor */
+ u32 hs_timing; /* bitmask for CIU clock phase shift */
+ struct regmap *sysreg; /* regmap for system manager register */
+};
+
+static int dw_mci_socfpga_priv_init(struct dw_mci *host)
+{
+ struct dw_mci_socfpga_priv_data *priv;
+
+ priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(host->dev, "mem alloc failed for private data\n");
+ return -ENOMEM;
+ }
+
+ priv->sysreg = syscon_regmap_lookup_by_compatible("altr,sys-mgr");
+ if (IS_ERR(priv->sysreg)) {
+ dev_err(host->dev, "regmap for altr,sys-mgr lookup failed.\n");
+ return PTR_ERR(priv->sysreg);
+ }
+ host->priv = priv;
+
+ return 0;
+}
+
+static int dw_mci_socfpga_setup_clock(struct dw_mci *host)
+{
+ struct dw_mci_socfpga_priv_data *priv = host->priv;
+
+ clk_disable_unprepare(host->ciu_clk);
+ regmap_write(priv->sysreg, SYSMGR_SDMMCGRP_CTRL_OFFSET,
+ priv->hs_timing);
+ clk_prepare_enable(host->ciu_clk);
+
+ host->bus_hz /= (priv->ciu_div + 1);
+ return 0;
+}
+
+static void dw_mci_socfpga_prepare_command(struct dw_mci *host, u32 *cmdr)
+{
+ struct dw_mci_socfpga_priv_data *priv = host->priv;
+
+ if (priv->hs_timing & DRV_CLK_PHASE_SHIFT_SEL_MASK)
+ *cmdr |= SDMMC_CMD_USE_HOLD_REG;
+}
+
+static int dw_mci_socfpga_parse_dt(struct dw_mci *host)
+{
+ struct dw_mci_socfpga_priv_data *priv = host->priv;
+ struct device_node *np = host->dev->of_node;
+ u32 timing[2];
+ u32 div = 0;
+ int ret;
+
+ ret = of_property_read_u32(np, "altr,dw-mshc-ciu-div", &div);
+ if (ret)
+ dev_info(host->dev, "No dw-mshc-ciu-div specified, assuming 1");
+ priv->ciu_div = div;
+
+ ret = of_property_read_u32_array(np,
+ "altr,dw-mshc-sdr-timing", timing, 2);
+ if (ret)
+ return ret;
+
+ priv->hs_timing = SYSMGR_SDMMC_CTRL_SET(timing[0], timing[1]);
+ return 0;
+}
+
+static const struct dw_mci_drv_data socfpga_drv_data = {
+ .init = dw_mci_socfpga_priv_init,
+ .setup_clock = dw_mci_socfpga_setup_clock,
+ .prepare_command = dw_mci_socfpga_prepare_command,
+ .parse_dt = dw_mci_socfpga_parse_dt,
+};
+
+static const struct of_device_id dw_mci_socfpga_match[] = {
+ { .compatible = "altr,socfpga-dw-mshc",
+ .data = &socfpga_drv_data, },
+ {},
+};
+MODULE_DEVICE_TABLE(of, dw_mci_socfpga_match);
+
+int dw_mci_socfpga_probe(struct platform_device *pdev)
+{
+ const struct dw_mci_drv_data *drv_data;
+ const struct of_device_id *match;
+
+ match = of_match_node(dw_mci_socfpga_match, pdev->dev.of_node);
+ drv_data = match->data;
+ return dw_mci_pltfm_register(pdev, drv_data);
+}
+
+static struct platform_driver dw_mci_socfpga_pltfm_driver = {
+ .probe = dw_mci_socfpga_probe,
+ .remove = __exit_p(dw_mci_pltfm_remove),
+ .driver = {
+ .name = "dwmmc_socfpga",
+ .of_match_table = of_match_ptr(dw_mci_socfpga_match),
+ .pm = &dw_mci_pltfm_pmops,
+ },
+};
+
+module_platform_driver(dw_mci_socfpga_pltfm_driver);
+
+MODULE_DESCRIPTION("Altera SOCFPGA Specific DW-MSHC Driver Extension");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:dwmmc-socfpga");
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index bc3a1bc4940f..ee5f1676f14e 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -39,7 +39,7 @@
#include "dw_mmc.h"
/* Common flag combinations */
-#define DW_MCI_DATA_ERROR_FLAGS (SDMMC_INT_DTO | SDMMC_INT_DCRC | \
+#define DW_MCI_DATA_ERROR_FLAGS (SDMMC_INT_DRTO | SDMMC_INT_DCRC | \
SDMMC_INT_HTO | SDMMC_INT_SBE | \
SDMMC_INT_EBE)
#define DW_MCI_CMD_ERROR_FLAGS (SDMMC_INT_RTO | SDMMC_INT_RCRC | \
@@ -51,6 +51,11 @@
#define DW_MCI_DMA_THRESHOLD 16
#ifdef CONFIG_MMC_DW_IDMAC
+#define IDMAC_INT_CLR (SDMMC_IDMAC_INT_AI | SDMMC_IDMAC_INT_NI | \
+ SDMMC_IDMAC_INT_CES | SDMMC_IDMAC_INT_DU | \
+ SDMMC_IDMAC_INT_FBE | SDMMC_IDMAC_INT_RI | \
+ SDMMC_IDMAC_INT_TI)
+
struct idmac_desc {
u32 des0; /* Control Descriptor */
#define IDMAC_DES0_DIC BIT(1)
@@ -433,6 +438,7 @@ static int dw_mci_idmac_init(struct dw_mci *host)
mci_writel(host, BMOD, SDMMC_IDMAC_SWRESET);
/* Mask out interrupts - get Tx & Rx complete only */
+ mci_writel(host, IDSTS, IDMAC_INT_CLR);
mci_writel(host, IDINTEN, SDMMC_IDMAC_INT_NI | SDMMC_IDMAC_INT_RI |
SDMMC_IDMAC_INT_TI);
@@ -1087,7 +1093,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
status = host->data_status;
if (status & DW_MCI_DATA_ERROR_FLAGS) {
- if (status & SDMMC_INT_DTO) {
+ if (status & SDMMC_INT_DRTO) {
data->error = -ETIMEDOUT;
} else if (status & SDMMC_INT_DCRC) {
data->error = -EILSEQ;
@@ -1985,19 +1991,6 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
#endif /* CONFIG_MMC_DW_IDMAC */
}
- host->vmmc = devm_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;
- } else {
- ret = regulator_enable(host->vmmc);
- if (ret) {
- dev_err(host->dev,
- "failed to enable regulator: %d\n", ret);
- goto err_setup_bus;
- }
- }
-
if (dw_mci_get_cd(mmc))
set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
else
@@ -2124,6 +2117,7 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
struct device_node *np = dev->of_node;
const struct dw_mci_drv_data *drv_data = host->drv_data;
int idx, ret;
+ u32 clock_frequency;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) {
@@ -2150,6 +2144,9 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
of_property_read_u32(np, "card-detect-delay", &pdata->detect_delay_ms);
+ if (!of_property_read_u32(np, "clock-frequency", &clock_frequency))
+ pdata->bus_hz = clock_frequency;
+
if (drv_data && drv_data->parse_dt) {
ret = drv_data->parse_dt(host);
if (ret)
@@ -2207,18 +2204,23 @@ int dw_mci_probe(struct dw_mci *host)
host->ciu_clk = devm_clk_get(host->dev, "ciu");
if (IS_ERR(host->ciu_clk)) {
dev_dbg(host->dev, "ciu clock not available\n");
+ host->bus_hz = host->pdata->bus_hz;
} else {
ret = clk_prepare_enable(host->ciu_clk);
if (ret) {
dev_err(host->dev, "failed to enable ciu clock\n");
goto err_clk_biu;
}
- }
- if (IS_ERR(host->ciu_clk))
- host->bus_hz = host->pdata->bus_hz;
- else
+ if (host->pdata->bus_hz) {
+ ret = clk_set_rate(host->ciu_clk, host->pdata->bus_hz);
+ if (ret)
+ dev_warn(host->dev,
+ "Unable to set bus rate to %ul\n",
+ host->pdata->bus_hz);
+ }
host->bus_hz = clk_get_rate(host->ciu_clk);
+ }
if (drv_data && drv_data->setup_clock) {
ret = drv_data->setup_clock(host);
@@ -2229,11 +2231,29 @@ int dw_mci_probe(struct dw_mci *host)
}
}
+ host->vmmc = devm_regulator_get(host->dev, "vmmc");
+ if (IS_ERR(host->vmmc)) {
+ ret = PTR_ERR(host->vmmc);
+ if (ret == -EPROBE_DEFER)
+ goto err_clk_ciu;
+
+ dev_info(host->dev, "no vmmc regulator found: %d\n", ret);
+ host->vmmc = NULL;
+ } else {
+ ret = regulator_enable(host->vmmc);
+ if (ret) {
+ if (ret != -EPROBE_DEFER)
+ dev_err(host->dev,
+ "regulator_enable fail: %d\n", ret);
+ goto err_clk_ciu;
+ }
+ }
+
if (!host->bus_hz) {
dev_err(host->dev,
"Platform data must supply bus speed\n");
ret = -ENODEV;
- goto err_clk_ciu;
+ goto err_regulator;
}
host->quirks = host->pdata->quirks;
@@ -2321,8 +2341,10 @@ int dw_mci_probe(struct dw_mci *host)
tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host);
host->card_workqueue = alloc_workqueue("dw-mci-card",
WQ_MEM_RECLAIM | WQ_NON_REENTRANT, 1);
- if (!host->card_workqueue)
+ if (!host->card_workqueue) {
+ ret = -ENOMEM;
goto err_dmaunmap;
+ }
INIT_WORK(&host->card_work, dw_mci_work_routine_card);
ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt,
host->irq_flags, "dw-mci", host);
@@ -2378,6 +2400,7 @@ err_dmaunmap:
if (host->use_dma && host->dma_ops->exit)
host->dma_ops->exit(host);
+err_regulator:
if (host->vmmc)
regulator_disable(host->vmmc);
diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
index 0b74189e7ee7..81b29941c5b9 100644
--- a/drivers/mmc/host/dw_mmc.h
+++ b/drivers/mmc/host/dw_mmc.h
@@ -98,7 +98,7 @@
#define SDMMC_INT_HLE BIT(12)
#define SDMMC_INT_FRUN BIT(11)
#define SDMMC_INT_HTO BIT(10)
-#define SDMMC_INT_DTO BIT(9)
+#define SDMMC_INT_DRTO BIT(9)
#define SDMMC_INT_RTO BIT(8)
#define SDMMC_INT_DCRC BIT(7)
#define SDMMC_INT_RCRC BIT(6)
@@ -111,6 +111,7 @@
#define SDMMC_INT_ERROR 0xbfc2
/* Command register defines */
#define SDMMC_CMD_START BIT(31)
+#define SDMMC_CMD_USE_HOLD_REG BIT(29)
#define SDMMC_CMD_CCS_EXP BIT(23)
#define SDMMC_CMD_CEATA_RD BIT(22)
#define SDMMC_CMD_UPD_CLK BIT(21)
diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c
index 2391c6b7a4bb..0308c9f1cf52 100644
--- a/drivers/mmc/host/jz4740_mmc.c
+++ b/drivers/mmc/host/jz4740_mmc.c
@@ -14,6 +14,7 @@
*/
#include <linux/mmc/host.h>
+#include <linux/mmc/slot-gpio.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/irq.h>
@@ -120,7 +121,6 @@ struct jz4740_mmc_host {
int irq;
int card_detect_irq;
- struct resource *mem;
void __iomem *base;
struct mmc_request *req;
struct mmc_command *cmd;
@@ -231,6 +231,14 @@ static void jz4740_mmc_transfer_check_state(struct jz4740_mmc_host *host,
host->req->cmd->error = -EIO;
data->error = -EIO;
}
+ } else if (status & JZ_MMC_STATUS_READ_ERROR_MASK) {
+ if (status & (JZ_MMC_STATUS_TIMEOUT_READ)) {
+ host->req->cmd->error = -ETIMEDOUT;
+ data->error = -ETIMEDOUT;
+ } else {
+ host->req->cmd->error = -EIO;
+ data->error = -EIO;
+ }
}
}
@@ -560,11 +568,6 @@ static irqreturn_t jz_mmc_irq(int irq, void *devid)
if (cmd->data)
cmd->data->error = -EIO;
cmd->error = -EIO;
- } else if (status & (JZ_MMC_STATUS_CRC_READ_ERROR |
- JZ_MMC_STATUS_CRC_WRITE_ERROR)) {
- if (cmd->data)
- cmd->data->error = -EIO;
- cmd->error = -EIO;
}
jz4740_mmc_set_irq_enabled(host, irq_reg, false);
@@ -626,7 +629,7 @@ static void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
gpio_set_value(host->pdata->gpio_power,
!host->pdata->power_active_low);
host->cmdat |= JZ_MMC_CMDAT_INIT;
- clk_enable(host->clk);
+ clk_prepare_enable(host->clk);
break;
case MMC_POWER_ON:
break;
@@ -634,7 +637,7 @@ static void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (gpio_is_valid(host->pdata->gpio_power))
gpio_set_value(host->pdata->gpio_power,
host->pdata->power_active_low);
- clk_disable(host->clk);
+ clk_disable_unprepare(host->clk);
break;
}
@@ -650,35 +653,6 @@ static void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
}
}
-static int jz4740_mmc_get_ro(struct mmc_host *mmc)
-{
- struct jz4740_mmc_host *host = mmc_priv(mmc);
- if (!gpio_is_valid(host->pdata->gpio_read_only))
- return -ENOSYS;
-
- return gpio_get_value(host->pdata->gpio_read_only) ^
- host->pdata->read_only_active_low;
-}
-
-static int jz4740_mmc_get_cd(struct mmc_host *mmc)
-{
- struct jz4740_mmc_host *host = mmc_priv(mmc);
- if (!gpio_is_valid(host->pdata->gpio_card_detect))
- return -ENOSYS;
-
- return gpio_get_value(host->pdata->gpio_card_detect) ^
- host->pdata->card_detect_active_low;
-}
-
-static irqreturn_t jz4740_mmc_card_detect_irq(int irq, void *devid)
-{
- struct jz4740_mmc_host *host = devid;
-
- mmc_detect_change(host->mmc, HZ / 2);
-
- return IRQ_HANDLED;
-}
-
static void jz4740_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
{
struct jz4740_mmc_host *host = mmc_priv(mmc);
@@ -688,8 +662,8 @@ static void jz4740_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
static const struct mmc_host_ops jz4740_mmc_ops = {
.request = jz4740_mmc_request,
.set_ios = jz4740_mmc_set_ios,
- .get_ro = jz4740_mmc_get_ro,
- .get_cd = jz4740_mmc_get_cd,
+ .get_ro = mmc_gpio_get_ro,
+ .get_cd = mmc_gpio_get_cd,
.enable_sdio_irq = jz4740_mmc_enable_sdio_irq,
};
@@ -724,58 +698,34 @@ static int jz4740_mmc_request_gpio(struct device *dev, int gpio,
return 0;
}
-static int jz4740_mmc_request_gpios(struct platform_device *pdev)
+static int jz4740_mmc_request_gpios(struct mmc_host *mmc,
+ struct platform_device *pdev)
{
- int ret;
struct jz4740_mmc_platform_data *pdata = pdev->dev.platform_data;
+ int ret = 0;
if (!pdata)
return 0;
- ret = jz4740_mmc_request_gpio(&pdev->dev, pdata->gpio_card_detect,
- "MMC detect change", false, 0);
- if (ret)
- goto err;
-
- ret = jz4740_mmc_request_gpio(&pdev->dev, pdata->gpio_read_only,
- "MMC read only", false, 0);
- if (ret)
- goto err_free_gpio_card_detect;
-
- ret = jz4740_mmc_request_gpio(&pdev->dev, pdata->gpio_power,
- "MMC read only", true, pdata->power_active_low);
- if (ret)
- goto err_free_gpio_read_only;
-
- return 0;
-
-err_free_gpio_read_only:
- if (gpio_is_valid(pdata->gpio_read_only))
- gpio_free(pdata->gpio_read_only);
-err_free_gpio_card_detect:
- if (gpio_is_valid(pdata->gpio_card_detect))
- gpio_free(pdata->gpio_card_detect);
-err:
- return ret;
-}
-
-static int jz4740_mmc_request_cd_irq(struct platform_device *pdev,
- struct jz4740_mmc_host *host)
-{
- struct jz4740_mmc_platform_data *pdata = pdev->dev.platform_data;
+ if (!pdata->card_detect_active_low)
+ mmc->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH;
+ if (!pdata->read_only_active_low)
+ mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
- if (!gpio_is_valid(pdata->gpio_card_detect))
- return 0;
+ if (gpio_is_valid(pdata->gpio_card_detect)) {
+ ret = mmc_gpio_request_cd(mmc, pdata->gpio_card_detect);
+ if (ret)
+ return ret;
+ }
- host->card_detect_irq = gpio_to_irq(pdata->gpio_card_detect);
- if (host->card_detect_irq < 0) {
- dev_warn(&pdev->dev, "Failed to get card detect irq\n");
- return 0;
+ if (gpio_is_valid(pdata->gpio_read_only)) {
+ ret = mmc_gpio_request_ro(mmc, pdata->gpio_read_only);
+ if (ret)
+ return ret;
}
- return request_irq(host->card_detect_irq, jz4740_mmc_card_detect_irq,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
- "MMC card detect", host);
+ return jz4740_mmc_request_gpio(&pdev->dev, pdata->gpio_power,
+ "MMC read only", true, pdata->power_active_low);
}
static void jz4740_mmc_free_gpios(struct platform_device *pdev)
@@ -787,10 +737,6 @@ static void jz4740_mmc_free_gpios(struct platform_device *pdev)
if (gpio_is_valid(pdata->gpio_power))
gpio_free(pdata->gpio_power);
- if (gpio_is_valid(pdata->gpio_read_only))
- gpio_free(pdata->gpio_read_only);
- if (gpio_is_valid(pdata->gpio_card_detect))
- gpio_free(pdata->gpio_card_detect);
}
static inline size_t jz4740_mmc_num_pins(struct jz4740_mmc_host *host)
@@ -808,6 +754,7 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
struct mmc_host *mmc;
struct jz4740_mmc_host *host;
struct jz4740_mmc_platform_data *pdata;
+ struct resource *res;
pdata = pdev->dev.platform_data;
@@ -827,42 +774,28 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
goto err_free_host;
}
- host->clk = clk_get(&pdev->dev, "mmc");
+ host->clk = devm_clk_get(&pdev->dev, "mmc");
if (IS_ERR(host->clk)) {
ret = PTR_ERR(host->clk);
dev_err(&pdev->dev, "Failed to get mmc clock\n");
goto err_free_host;
}
- host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!host->mem) {
- ret = -ENOENT;
- dev_err(&pdev->dev, "Failed to get base platform memory\n");
- goto err_clk_put;
- }
-
- host->mem = request_mem_region(host->mem->start,
- resource_size(host->mem), pdev->name);
- if (!host->mem) {
- ret = -EBUSY;
- dev_err(&pdev->dev, "Failed to request base memory region\n");
- goto err_clk_put;
- }
-
- host->base = ioremap_nocache(host->mem->start, resource_size(host->mem));
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ host->base = devm_ioremap_resource(&pdev->dev, res);
if (!host->base) {
ret = -EBUSY;
dev_err(&pdev->dev, "Failed to ioremap base memory\n");
- goto err_release_mem_region;
+ goto err_free_host;
}
ret = jz_gpio_bulk_request(jz4740_mmc_pins, jz4740_mmc_num_pins(host));
if (ret) {
dev_err(&pdev->dev, "Failed to request mmc pins: %d\n", ret);
- goto err_iounmap;
+ goto err_free_host;
}
- ret = jz4740_mmc_request_gpios(pdev);
+ ret = jz4740_mmc_request_gpios(mmc, pdev);
if (ret)
goto err_gpio_bulk_free;
@@ -885,17 +818,11 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
spin_lock_init(&host->lock);
host->irq_mask = 0xffff;
- ret = jz4740_mmc_request_cd_irq(pdev, host);
- if (ret) {
- dev_err(&pdev->dev, "Failed to request card detect irq\n");
- goto err_free_gpios;
- }
-
ret = request_threaded_irq(host->irq, jz_mmc_irq, jz_mmc_irq_worker, 0,
dev_name(&pdev->dev), host);
if (ret) {
dev_err(&pdev->dev, "Failed to request irq: %d\n", ret);
- goto err_free_card_detect_irq;
+ goto err_free_gpios;
}
jz4740_mmc_reset(host);
@@ -918,21 +845,11 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
err_free_irq:
free_irq(host->irq, host);
-err_free_card_detect_irq:
- if (host->card_detect_irq >= 0)
- free_irq(host->card_detect_irq, host);
err_free_gpios:
jz4740_mmc_free_gpios(pdev);
err_gpio_bulk_free:
jz_gpio_bulk_free(jz4740_mmc_pins, jz4740_mmc_num_pins(host));
-err_iounmap:
- iounmap(host->base);
-err_release_mem_region:
- release_mem_region(host->mem->start, resource_size(host->mem));
-err_clk_put:
- clk_put(host->clk);
err_free_host:
- platform_set_drvdata(pdev, NULL);
mmc_free_host(mmc);
return ret;
@@ -949,24 +866,16 @@ static int jz4740_mmc_remove(struct platform_device *pdev)
mmc_remove_host(host->mmc);
free_irq(host->irq, host);
- if (host->card_detect_irq >= 0)
- free_irq(host->card_detect_irq, host);
jz4740_mmc_free_gpios(pdev);
jz_gpio_bulk_free(jz4740_mmc_pins, jz4740_mmc_num_pins(host));
- iounmap(host->base);
- release_mem_region(host->mem->start, resource_size(host->mem));
-
- clk_put(host->clk);
-
- platform_set_drvdata(pdev, NULL);
mmc_free_host(host->mmc);
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int jz4740_mmc_suspend(struct device *dev)
{
@@ -990,13 +899,8 @@ static int jz4740_mmc_resume(struct device *dev)
return 0;
}
-const struct dev_pm_ops jz4740_mmc_pm_ops = {
- .suspend = jz4740_mmc_suspend,
- .resume = jz4740_mmc_resume,
- .poweroff = jz4740_mmc_suspend,
- .restore = jz4740_mmc_resume,
-};
-
+static SIMPLE_DEV_PM_OPS(jz4740_mmc_pm_ops, jz4740_mmc_suspend,
+ jz4740_mmc_resume);
#define JZ4740_MMC_PM_OPS (&jz4740_mmc_pm_ops)
#else
#define JZ4740_MMC_PM_OPS NULL
diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c
index 8960fc846c77..4ddd83f98658 100644
--- a/drivers/mmc/host/mvsdio.c
+++ b/drivers/mmc/host/mvsdio.c
@@ -35,7 +35,7 @@
#define DRIVER_NAME "mvsdio"
-static int maxfreq = MVSD_CLOCKRATE_MAX;
+static int maxfreq;
static int nodma;
struct mvsd_host {
@@ -685,7 +685,6 @@ static int __init mvsd_probe(struct platform_device *pdev)
const struct mbus_dram_target_info *dram;
struct resource *r;
int ret, irq;
- int gpio_card_detect, gpio_write_protect;
struct pinctrl *pinctrl;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -718,6 +717,20 @@ static int __init mvsd_probe(struct platform_device *pdev)
if (!IS_ERR(host->clk))
clk_prepare_enable(host->clk);
+ mmc->ops = &mvsd_ops;
+
+ mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+
+ mmc->f_min = DIV_ROUND_UP(host->base_clock, MVSD_BASE_DIV_MAX);
+ mmc->f_max = MVSD_CLOCKRATE_MAX;
+
+ mmc->max_blk_size = 2048;
+ mmc->max_blk_count = 65535;
+
+ mmc->max_segs = 1;
+ mmc->max_seg_size = mmc->max_blk_size * mmc->max_blk_count;
+ mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
+
if (np) {
if (IS_ERR(host->clk)) {
dev_err(&pdev->dev, "DT platforms must have a clock associated\n");
@@ -726,35 +739,38 @@ static int __init mvsd_probe(struct platform_device *pdev)
}
host->base_clock = clk_get_rate(host->clk) / 2;
- gpio_card_detect = of_get_named_gpio(np, "cd-gpios", 0);
- gpio_write_protect = of_get_named_gpio(np, "wp-gpios", 0);
+ ret = mmc_of_parse(mmc);
+ if (ret < 0)
+ goto out;
} else {
const struct mvsdio_platform_data *mvsd_data;
+
mvsd_data = pdev->dev.platform_data;
if (!mvsd_data) {
ret = -ENXIO;
goto out;
}
+ mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ |
+ MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
host->base_clock = mvsd_data->clock / 2;
- gpio_card_detect = mvsd_data->gpio_card_detect ? : -EINVAL;
- gpio_write_protect = mvsd_data->gpio_write_protect ? : -EINVAL;
- }
-
- mmc->ops = &mvsd_ops;
-
- mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
- mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ |
- MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
-
- mmc->f_min = DIV_ROUND_UP(host->base_clock, MVSD_BASE_DIV_MAX);
- mmc->f_max = maxfreq;
+ /* GPIO 0 regarded as invalid for backward compatibility */
+ if (mvsd_data->gpio_card_detect &&
+ gpio_is_valid(mvsd_data->gpio_card_detect)) {
+ ret = mmc_gpio_request_cd(mmc,
+ mvsd_data->gpio_card_detect);
+ if (ret)
+ goto out;
+ } else {
+ mmc->caps |= MMC_CAP_NEEDS_POLL;
+ }
- mmc->max_blk_size = 2048;
- mmc->max_blk_count = 65535;
+ if (mvsd_data->gpio_write_protect &&
+ gpio_is_valid(mvsd_data->gpio_write_protect))
+ mmc_gpio_request_ro(mmc, mvsd_data->gpio_write_protect);
+ }
- mmc->max_segs = 1;
- mmc->max_seg_size = mmc->max_blk_size * mmc->max_blk_count;
- mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
+ if (maxfreq)
+ mmc->f_max = maxfreq;
spin_lock_init(&host->lock);
@@ -777,15 +793,6 @@ static int __init mvsd_probe(struct platform_device *pdev)
goto out;
}
- if (gpio_is_valid(gpio_card_detect)) {
- ret = mmc_gpio_request_cd(mmc, gpio_card_detect);
- if (ret)
- goto out;
- } else
- mmc->caps |= MMC_CAP_NEEDS_POLL;
-
- mmc_gpio_request_ro(mmc, gpio_write_protect);
-
setup_timer(&host->timer, mvsd_timeout_timer, (unsigned long)host);
platform_set_drvdata(pdev, mmc);
ret = mmc_add_host(mmc);
@@ -793,10 +800,10 @@ static int __init mvsd_probe(struct platform_device *pdev)
goto out;
if (!(mmc->caps & MMC_CAP_NEEDS_POLL))
- dev_notice(&pdev->dev, "using GPIO %d for card detection\n",
- gpio_card_detect);
+ dev_notice(&pdev->dev, "using GPIO for card detection\n");
else
- dev_notice(&pdev->dev, "lacking card detect (fall back to polling)\n");
+ dev_notice(&pdev->dev,
+ "lacking card detect (fall back to polling)\n");
return 0;
out:
@@ -827,7 +834,6 @@ static int __exit mvsd_remove(struct platform_device *pdev)
clk_disable_unprepare(host->clk);
mmc_free_host(mmc);
- platform_set_drvdata(pdev, NULL);
return 0;
}
diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c
index d5036353bddc..c174c6a0d224 100644
--- a/drivers/mmc/host/mxcmmc.c
+++ b/drivers/mmc/host/mxcmmc.c
@@ -1067,7 +1067,9 @@ static int mxcmci_probe(struct platform_device *pdev)
goto out_release_mem;
}
- mmc_of_parse(mmc);
+ ret = mmc_of_parse(mmc);
+ if (ret)
+ goto out_free;
mmc->ops = &mxcmci_ops;
/* For devicetree parsing, the bus width is read from devicetree */
@@ -1219,8 +1221,6 @@ static int mxcmci_remove(struct platform_device *pdev)
struct mmc_host *mmc = platform_get_drvdata(pdev);
struct mxcmci_host *host = mmc_priv(mmc);
- platform_set_drvdata(pdev, NULL);
-
mmc_remove_host(mmc);
if (host->vcc)
diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c
index 4278a1787d08..f38d75f46f78 100644
--- a/drivers/mmc/host/mxs-mmc.c
+++ b/drivers/mmc/host/mxs-mmc.c
@@ -41,7 +41,6 @@
#include <linux/gpio.h>
#include <linux/regulator/consumer.h>
#include <linux/module.h>
-#include <linux/pinctrl/consumer.h>
#include <linux/stmp_device.h>
#include <linux/spi/mxs-spi.h>
@@ -580,7 +579,6 @@ static int mxs_mmc_probe(struct platform_device *pdev)
struct mxs_mmc_host *host;
struct mmc_host *mmc;
struct resource *iores;
- struct pinctrl *pinctrl;
int ret = 0, irq_err;
struct regulator *reg_vmmc;
enum of_gpio_flags flags;
@@ -620,12 +618,6 @@ static int mxs_mmc_probe(struct platform_device *pdev)
}
}
- pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
- if (IS_ERR(pinctrl)) {
- ret = PTR_ERR(pinctrl);
- goto out_mmc_free;
- }
-
ssp->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(ssp->clk)) {
ret = PTR_ERR(ssp->clk);
@@ -639,6 +631,7 @@ static int mxs_mmc_probe(struct platform_device *pdev)
if (!ssp->dmach) {
dev_err(mmc_dev(host->mmc),
"%s: failed to request dma\n", __func__);
+ ret = -ENODEV;
goto out_clk_put;
}
@@ -708,8 +701,6 @@ static int mxs_mmc_remove(struct platform_device *pdev)
mmc_remove_host(mmc);
- platform_set_drvdata(pdev, NULL);
-
if (ssp->dmach)
dma_release_channel(ssp->dmach);
diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c
index 4254975f931d..b94f38ec2a83 100644
--- a/drivers/mmc/host/omap.c
+++ b/drivers/mmc/host/omap.c
@@ -1413,33 +1413,17 @@ static int mmc_omap_probe(struct platform_device *pdev)
else
sig = host->id == 0 ? OMAP_DMA_MMC_TX : OMAP_DMA_MMC2_TX;
host->dma_tx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
-#if 0
- if (!host->dma_tx) {
- dev_err(host->dev, "unable to obtain TX DMA engine channel %u\n",
- sig);
- goto err_dma;
- }
-#else
if (!host->dma_tx)
dev_warn(host->dev, "unable to obtain TX DMA engine channel %u\n",
sig);
-#endif
if (mmc_omap2())
sig = host->id == 0 ? OMAP24XX_DMA_MMC1_RX : OMAP24XX_DMA_MMC2_RX;
else
sig = host->id == 0 ? OMAP_DMA_MMC_RX : OMAP_DMA_MMC2_RX;
host->dma_rx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
-#if 0
- if (!host->dma_rx) {
- dev_err(host->dev, "unable to obtain RX DMA engine channel %u\n",
- sig);
- goto err_dma;
- }
-#else
if (!host->dma_rx)
dev_warn(host->dev, "unable to obtain RX DMA engine channel %u\n",
sig);
-#endif
ret = request_irq(host->irq, mmc_omap_irq, 0, DRIVER_NAME, host);
if (ret)
@@ -1500,8 +1484,6 @@ static int mmc_omap_remove(struct platform_device *pdev)
struct mmc_omap_host *host = platform_get_drvdata(pdev);
int i;
- platform_set_drvdata(pdev, NULL);
-
BUG_ON(host == NULL);
for (i = 0; i < host->nr_slots; i++)
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index eccedc7d06a4..1865321465c4 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -2047,7 +2047,6 @@ err_irq:
}
err1:
iounmap(host->base);
- platform_set_drvdata(pdev, NULL);
mmc_free_host(mmc);
err_alloc:
omap_hsmmc_gpio_free(pdata);
@@ -2093,7 +2092,6 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res)
release_mem_region(res->start, resource_size(res));
- platform_set_drvdata(pdev, NULL);
return 0;
}
diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c
index 2b2f65ada22e..847b1996ce8e 100644
--- a/drivers/mmc/host/pxamci.c
+++ b/drivers/mmc/host/pxamci.c
@@ -834,8 +834,6 @@ static int pxamci_remove(struct platform_device *pdev)
struct mmc_host *mmc = platform_get_drvdata(pdev);
int gpio_cd = -1, gpio_ro = -1, gpio_power = -1;
- platform_set_drvdata(pdev, NULL);
-
if (mmc) {
struct pxamci_host *host = mmc_priv(mmc);
diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c
index ad13f4240c49..82a35b91cdbc 100644
--- a/drivers/mmc/host/rtsx_pci_sdmmc.c
+++ b/drivers/mmc/host/rtsx_pci_sdmmc.c
@@ -1316,8 +1316,6 @@ static int rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev)
mmc_remove_host(mmc);
mmc_free_host(mmc);
- platform_set_drvdata(pdev, NULL);
-
dev_dbg(&(pdev->dev),
": Realtek PCI-E SDMMC controller has been removed\n");
diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c
index 706d9cb1a49e..cdd4ce0d7c90 100644
--- a/drivers/mmc/host/sdhci-acpi.c
+++ b/drivers/mmc/host/sdhci-acpi.c
@@ -31,10 +31,13 @@
#include <linux/bitops.h>
#include <linux/types.h>
#include <linux/err.h>
+#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/acpi.h>
+#include <linux/acpi_gpio.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
+#include <linux/delay.h>
#include <linux/mmc/host.h>
#include <linux/mmc/pm.h>
@@ -83,12 +86,37 @@ static int sdhci_acpi_enable_dma(struct sdhci_host *host)
return 0;
}
+static void sdhci_acpi_int_hw_reset(struct sdhci_host *host)
+{
+ u8 reg;
+
+ reg = sdhci_readb(host, SDHCI_POWER_CONTROL);
+ reg |= 0x10;
+ sdhci_writeb(host, reg, SDHCI_POWER_CONTROL);
+ /* For eMMC, minimum is 1us but give it 9us for good measure */
+ udelay(9);
+ reg &= ~0x10;
+ sdhci_writeb(host, reg, SDHCI_POWER_CONTROL);
+ /* For eMMC, minimum is 200us but give it 300us for good measure */
+ usleep_range(300, 1000);
+}
+
static const struct sdhci_ops sdhci_acpi_ops_dflt = {
.enable_dma = sdhci_acpi_enable_dma,
};
+static const struct sdhci_ops sdhci_acpi_ops_int = {
+ .enable_dma = sdhci_acpi_enable_dma,
+ .hw_reset = sdhci_acpi_int_hw_reset,
+};
+
+static const struct sdhci_acpi_chip sdhci_acpi_chip_int = {
+ .ops = &sdhci_acpi_ops_int,
+};
+
static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = {
- .caps = MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE,
+ .chip = &sdhci_acpi_chip_int,
+ .caps = MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE | MMC_CAP_HW_RESET,
.caps2 = MMC_CAP2_HC_ERASE_SZ,
.flags = SDHCI_ACPI_RUNTIME_PM,
};
@@ -101,6 +129,8 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = {
};
static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sd = {
+ .flags = SDHCI_ACPI_SD_CD | SDHCI_ACPI_RUNTIME_PM,
+ .quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON,
};
struct sdhci_acpi_uid_slot {
@@ -161,6 +191,59 @@ static const struct sdhci_acpi_slot *sdhci_acpi_get_slot(acpi_handle handle,
return slot;
}
+#ifdef CONFIG_PM_RUNTIME
+
+static irqreturn_t sdhci_acpi_sd_cd(int irq, void *dev_id)
+{
+ mmc_detect_change(dev_id, msecs_to_jiffies(200));
+ return IRQ_HANDLED;
+}
+
+static int sdhci_acpi_add_own_cd(struct device *dev, int gpio,
+ struct mmc_host *mmc)
+{
+ unsigned long flags;
+ int err, irq;
+
+ if (gpio < 0) {
+ err = gpio;
+ goto out;
+ }
+
+ err = devm_gpio_request_one(dev, gpio, GPIOF_DIR_IN, "sd_cd");
+ if (err)
+ goto out;
+
+ irq = gpio_to_irq(gpio);
+ if (irq < 0) {
+ err = irq;
+ goto out_free;
+ }
+
+ flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
+ err = devm_request_irq(dev, irq, sdhci_acpi_sd_cd, flags, "sd_cd", mmc);
+ if (err)
+ goto out_free;
+
+ return 0;
+
+out_free:
+ devm_gpio_free(dev, gpio);
+out:
+ dev_warn(dev, "failed to setup card detect wake up\n");
+ return err;
+}
+
+#else
+
+static int sdhci_acpi_add_own_cd(struct device *dev, int gpio,
+ struct mmc_host *mmc)
+{
+ return 0;
+}
+
+#endif
+
static int sdhci_acpi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -171,7 +254,7 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
struct resource *iomem;
resource_size_t len;
const char *hid;
- int err;
+ int err, gpio;
if (acpi_bus_get_device(handle, &device))
return -ENODEV;
@@ -196,6 +279,8 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
if (IS_ERR(host))
return PTR_ERR(host);
+ gpio = acpi_get_gpio_by_index(dev, 0, NULL);
+
c = sdhci_priv(host);
c->host = host;
c->slot = sdhci_acpi_get_slot(handle, hid);
@@ -251,6 +336,11 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
if (err)
goto err_free;
+ if (sdhci_acpi_flag(c, SDHCI_ACPI_SD_CD)) {
+ if (sdhci_acpi_add_own_cd(dev, gpio, host->mmc))
+ c->use_runtime_pm = false;
+ }
+
if (c->use_runtime_pm) {
pm_runtime_set_active(dev);
pm_suspend_ignore_children(dev, 1);
@@ -262,7 +352,6 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
return 0;
err_free:
- platform_set_drvdata(pdev, NULL);
sdhci_free_host(c->host);
return err;
}
@@ -281,7 +370,6 @@ static int sdhci_acpi_remove(struct platform_device *pdev)
dead = (sdhci_readl(c->host, SDHCI_INT_STATUS) == ~0);
sdhci_remove_host(c->host, dead);
- platform_set_drvdata(pdev, NULL);
sdhci_free_host(c->host);
return 0;
diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c
new file mode 100644
index 000000000000..87175f9817c2
--- /dev/null
+++ b/drivers/mmc/host/sdhci-bcm-kona.c
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2013 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/highmem.h>
+#include <linux/platform_device.h>
+#include <linux/mmc/host.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/version.h>
+#include <linux/mmc/slot-gpio.h>
+
+#include "sdhci-pltfm.h"
+#include "sdhci.h"
+
+#define SDHCI_SOFT_RESET 0x01000000
+#define KONA_SDHOST_CORECTRL 0x8000
+#define KONA_SDHOST_CD_PINCTRL 0x00000008
+#define KONA_SDHOST_STOP_HCLK 0x00000004
+#define KONA_SDHOST_RESET 0x00000002
+#define KONA_SDHOST_EN 0x00000001
+
+#define KONA_SDHOST_CORESTAT 0x8004
+#define KONA_SDHOST_WP 0x00000002
+#define KONA_SDHOST_CD_SW 0x00000001
+
+#define KONA_SDHOST_COREIMR 0x8008
+#define KONA_SDHOST_IP 0x00000001
+
+#define KONA_SDHOST_COREISR 0x800C
+#define KONA_SDHOST_COREIMSR 0x8010
+#define KONA_SDHOST_COREDBG1 0x8014
+#define KONA_SDHOST_COREGPO_MASK 0x8018
+
+#define SD_DETECT_GPIO_DEBOUNCE_128MS 128
+
+#define KONA_MMC_AUTOSUSPEND_DELAY (50)
+
+struct sdhci_bcm_kona_dev {
+ struct mutex write_lock; /* protect back to back writes */
+};
+
+
+static int sdhci_bcm_kona_sd_reset(struct sdhci_host *host)
+{
+ unsigned int val;
+ unsigned long timeout;
+
+ /* This timeout should be sufficent for core to reset */
+ timeout = jiffies + msecs_to_jiffies(100);
+
+ /* reset the host using the top level reset */
+ val = sdhci_readl(host, KONA_SDHOST_CORECTRL);
+ val |= KONA_SDHOST_RESET;
+ sdhci_writel(host, val, KONA_SDHOST_CORECTRL);
+
+ while (!(sdhci_readl(host, KONA_SDHOST_CORECTRL) & KONA_SDHOST_RESET)) {
+ if (time_is_before_jiffies(timeout)) {
+ pr_err("Error: sd host is stuck in reset!!!\n");
+ return -EFAULT;
+ }
+ }
+
+ /* bring the host out of reset */
+ val = sdhci_readl(host, KONA_SDHOST_CORECTRL);
+ val &= ~KONA_SDHOST_RESET;
+
+ /*
+ * Back-to-Back register write needs a delay of 1ms at bootup (min 10uS)
+ * Back-to-Back writes to same register needs delay when SD bus clock
+ * is very low w.r.t AHB clock, mainly during boot-time and during card
+ * insert-removal.
+ */
+ usleep_range(1000, 5000);
+ sdhci_writel(host, val, KONA_SDHOST_CORECTRL);
+
+ return 0;
+}
+
+static void sdhci_bcm_kona_sd_init(struct sdhci_host *host)
+{
+ unsigned int val;
+
+ /* enable the interrupt from the IP core */
+ val = sdhci_readl(host, KONA_SDHOST_COREIMR);
+ val |= KONA_SDHOST_IP;
+ sdhci_writel(host, val, KONA_SDHOST_COREIMR);
+
+ /* Enable the AHB clock gating module to the host */
+ val = sdhci_readl(host, KONA_SDHOST_CORECTRL);
+ val |= KONA_SDHOST_EN;
+
+ /*
+ * Back-to-Back register write needs a delay of 1ms at bootup (min 10uS)
+ * Back-to-Back writes to same register needs delay when SD bus clock
+ * is very low w.r.t AHB clock, mainly during boot-time and during card
+ * insert-removal.
+ */
+ usleep_range(1000, 5000);
+ sdhci_writel(host, val, KONA_SDHOST_CORECTRL);
+}
+
+/*
+ * Software emulation of the SD card insertion/removal. Set insert=1 for insert
+ * and insert=0 for removal. The card detection is done by GPIO. For Broadcom
+ * IP to function properly the bit 0 of CORESTAT register needs to be set/reset
+ * to generate the CD IRQ handled in sdhci.c which schedules card_tasklet.
+ */
+static int sdhci_bcm_kona_sd_card_emulate(struct sdhci_host *host, int insert)
+{
+ struct sdhci_pltfm_host *pltfm_priv = sdhci_priv(host);
+ struct sdhci_bcm_kona_dev *kona_dev = sdhci_pltfm_priv(pltfm_priv);
+ u32 val;
+
+ /*
+ * Back-to-Back register write needs a delay of min 10uS.
+ * Back-to-Back writes to same register needs delay when SD bus clock
+ * is very low w.r.t AHB clock, mainly during boot-time and during card
+ * insert-removal.
+ * We keep 20uS
+ */
+ mutex_lock(&kona_dev->write_lock);
+ udelay(20);
+ val = sdhci_readl(host, KONA_SDHOST_CORESTAT);
+
+ if (insert) {
+ int ret;
+
+ ret = mmc_gpio_get_ro(host->mmc);
+ if (ret >= 0)
+ val = (val & ~KONA_SDHOST_WP) |
+ ((ret) ? KONA_SDHOST_WP : 0);
+
+ val |= KONA_SDHOST_CD_SW;
+ sdhci_writel(host, val, KONA_SDHOST_CORESTAT);
+ } else {
+ val &= ~KONA_SDHOST_CD_SW;
+ sdhci_writel(host, val, KONA_SDHOST_CORESTAT);
+ }
+ mutex_unlock(&kona_dev->write_lock);
+
+ return 0;
+}
+
+/*
+ * SD card interrupt event callback
+ */
+void sdhci_bcm_kona_card_event(struct sdhci_host *host)
+{
+ if (mmc_gpio_get_cd(host->mmc) > 0) {
+ dev_dbg(mmc_dev(host->mmc),
+ "card inserted\n");
+ sdhci_bcm_kona_sd_card_emulate(host, 1);
+ } else {
+ dev_dbg(mmc_dev(host->mmc),
+ "card removed\n");
+ sdhci_bcm_kona_sd_card_emulate(host, 0);
+ }
+}
+
+/*
+ * Get the base clock. Use central clock source for now. Not sure if different
+ * clock speed to each dev is allowed
+ */
+static unsigned int sdhci_bcm_kona_get_max_clk(struct sdhci_host *host)
+{
+ struct sdhci_bcm_kona_dev *kona_dev;
+ struct sdhci_pltfm_host *pltfm_priv = sdhci_priv(host);
+ kona_dev = sdhci_pltfm_priv(pltfm_priv);
+
+ return host->mmc->f_max;
+}
+
+static unsigned int sdhci_bcm_kona_get_timeout_clock(struct sdhci_host *host)
+{
+ return sdhci_bcm_kona_get_max_clk(host);
+}
+
+static void sdhci_bcm_kona_init_74_clocks(struct sdhci_host *host,
+ u8 power_mode)
+{
+ /*
+ * JEDEC and SD spec specify supplying 74 continuous clocks to
+ * device after power up. With minimum bus (100KHz) that
+ * that translates to 740us
+ */
+ if (power_mode != MMC_POWER_OFF)
+ udelay(740);
+}
+
+static struct sdhci_ops sdhci_bcm_kona_ops = {
+ .get_max_clock = sdhci_bcm_kona_get_max_clk,
+ .get_timeout_clock = sdhci_bcm_kona_get_timeout_clock,
+ .platform_send_init_74_clocks = sdhci_bcm_kona_init_74_clocks,
+ .card_event = sdhci_bcm_kona_card_event,
+};
+
+static struct sdhci_pltfm_data sdhci_pltfm_data_kona = {
+ .ops = &sdhci_bcm_kona_ops,
+ .quirks = SDHCI_QUIRK_NO_CARD_NO_RESET |
+ SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | SDHCI_QUIRK_32BIT_DMA_ADDR |
+ SDHCI_QUIRK_32BIT_DMA_SIZE | SDHCI_QUIRK_32BIT_ADMA_SIZE |
+ SDHCI_QUIRK_FORCE_BLK_SZ_2048 |
+ SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
+};
+
+static const struct of_device_id sdhci_bcm_kona_of_match[] __initdata = {
+ { .compatible = "bcm,kona-sdhci"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, sdhci_bcm_kona_of_match);
+
+static int __init sdhci_bcm_kona_probe(struct platform_device *pdev)
+{
+ struct sdhci_bcm_kona_dev *kona_dev = NULL;
+ struct sdhci_pltfm_host *pltfm_priv;
+ struct device *dev = &pdev->dev;
+ struct sdhci_host *host;
+ int ret;
+
+ ret = 0;
+
+ host = sdhci_pltfm_init(pdev, &sdhci_pltfm_data_kona,
+ sizeof(*kona_dev));
+ if (IS_ERR(host))
+ return PTR_ERR(host);
+
+ dev_dbg(dev, "%s: inited. IOADDR=%p\n", __func__, host->ioaddr);
+
+ pltfm_priv = sdhci_priv(host);
+
+ kona_dev = sdhci_pltfm_priv(pltfm_priv);
+ mutex_init(&kona_dev->write_lock);
+
+ mmc_of_parse(host->mmc);
+
+ if (!host->mmc->f_max) {
+ dev_err(&pdev->dev, "Missing max-freq for SDHCI cfg\n");
+ ret = -ENXIO;
+ goto err_pltfm_free;
+ }
+
+ dev_dbg(dev, "non-removable=%c\n",
+ (host->mmc->caps & MMC_CAP_NONREMOVABLE) ? 'Y' : 'N');
+ dev_dbg(dev, "cd_gpio %c, wp_gpio %c\n",
+ (mmc_gpio_get_cd(host->mmc) != -ENOSYS) ? 'Y' : 'N',
+ (mmc_gpio_get_ro(host->mmc) != -ENOSYS) ? 'Y' : 'N');
+
+ if (host->mmc->caps | MMC_CAP_NONREMOVABLE)
+ host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+
+ dev_dbg(dev, "is_8bit=%c\n",
+ (host->mmc->caps | MMC_CAP_8_BIT_DATA) ? 'Y' : 'N');
+
+ ret = sdhci_bcm_kona_sd_reset(host);
+ if (ret)
+ goto err_pltfm_free;
+
+ sdhci_bcm_kona_sd_init(host);
+
+ ret = sdhci_add_host(host);
+ if (ret) {
+ dev_err(dev, "Failed sdhci_add_host\n");
+ goto err_reset;
+ }
+
+ /* if device is eMMC, emulate card insert right here */
+ if (host->mmc->caps | MMC_CAP_NONREMOVABLE) {
+ ret = sdhci_bcm_kona_sd_card_emulate(host, 1);
+ if (ret) {
+ dev_err(dev,
+ "unable to emulate card insertion\n");
+ goto err_remove_host;
+ }
+ }
+ /*
+ * Since the card detection GPIO interrupt is configured to be
+ * edge sensitive, check the initial GPIO value here, emulate
+ * only if the card is present
+ */
+ if (mmc_gpio_get_cd(host->mmc) > 0)
+ sdhci_bcm_kona_sd_card_emulate(host, 1);
+
+ dev_dbg(dev, "initialized properly\n");
+ return 0;
+
+err_remove_host:
+ sdhci_remove_host(host, 0);
+
+err_reset:
+ sdhci_bcm_kona_sd_reset(host);
+
+err_pltfm_free:
+ sdhci_pltfm_free(pdev);
+
+ dev_err(dev, "Probing of sdhci-pltfm failed: %d\n", ret);
+ return ret;
+}
+
+static int __exit sdhci_bcm_kona_remove(struct platform_device *pdev)
+{
+ struct sdhci_host *host = platform_get_drvdata(pdev);
+ int dead;
+ u32 scratch;
+
+ dead = 0;
+ scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
+ if (scratch == (u32)-1)
+ dead = 1;
+ sdhci_remove_host(host, dead);
+
+ sdhci_free_host(host);
+
+ return 0;
+}
+
+static struct platform_driver sdhci_bcm_kona_driver = {
+ .driver = {
+ .name = "sdhci-kona",
+ .owner = THIS_MODULE,
+ .pm = SDHCI_PLTFM_PMOPS,
+ .of_match_table = of_match_ptr(sdhci_bcm_kona_of_match),
+ },
+ .probe = sdhci_bcm_kona_probe,
+ .remove = __exit_p(sdhci_bcm_kona_remove),
+};
+module_platform_driver(sdhci_bcm_kona_driver);
+
+MODULE_DESCRIPTION("SDHCI driver for Broadcom Kona platform");
+MODULE_AUTHOR("Broadcom");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/sdhci-bcm2835.c b/drivers/mmc/host/sdhci-bcm2835.c
index d49bc958c8ba..0584a1c788b8 100644
--- a/drivers/mmc/host/sdhci-bcm2835.c
+++ b/drivers/mmc/host/sdhci-bcm2835.c
@@ -148,7 +148,7 @@ static int bcm2835_sdhci_probe(struct platform_device *pdev)
struct sdhci_pltfm_host *pltfm_host;
int ret;
- host = sdhci_pltfm_init(pdev, &bcm2835_sdhci_pdata);
+ host = sdhci_pltfm_init(pdev, &bcm2835_sdhci_pdata, 0);
if (IS_ERR(host))
return PTR_ERR(host);
diff --git a/drivers/mmc/host/sdhci-cns3xxx.c b/drivers/mmc/host/sdhci-cns3xxx.c
index 8ebb6b650f3f..f2cc26633cb2 100644
--- a/drivers/mmc/host/sdhci-cns3xxx.c
+++ b/drivers/mmc/host/sdhci-cns3xxx.c
@@ -96,7 +96,7 @@ static const struct sdhci_pltfm_data sdhci_cns3xxx_pdata = {
static int sdhci_cns3xxx_probe(struct platform_device *pdev)
{
- return sdhci_pltfm_register(pdev, &sdhci_cns3xxx_pdata);
+ return sdhci_pltfm_register(pdev, &sdhci_cns3xxx_pdata, 0);
}
static int sdhci_cns3xxx_remove(struct platform_device *pdev)
diff --git a/drivers/mmc/host/sdhci-dove.c b/drivers/mmc/host/sdhci-dove.c
index 15e7803040f1..8424839660f8 100644
--- a/drivers/mmc/host/sdhci-dove.c
+++ b/drivers/mmc/host/sdhci-dove.c
@@ -130,7 +130,7 @@ static int sdhci_dove_probe(struct platform_device *pdev)
gpio_direction_input(priv->gpio_cd);
}
- host = sdhci_pltfm_init(pdev, &sdhci_dove_pdata);
+ host = sdhci_pltfm_init(pdev, &sdhci_dove_pdata, 0);
if (IS_ERR(host)) {
ret = PTR_ERR(host);
goto err_sdhci_pltfm_init;
diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index d5f0d59e1310..1dd5ba858754 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -384,6 +384,20 @@ static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)
}
}
+static unsigned int esdhc_pltfm_get_max_clock(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct pltfm_imx_data *imx_data = pltfm_host->priv;
+ struct esdhc_platform_data *boarddata = &imx_data->boarddata;
+
+ u32 f_host = clk_get_rate(pltfm_host->clk);
+
+ if (boarddata->f_max && (boarddata->f_max < f_host))
+ return boarddata->f_max;
+ else
+ return f_host;
+}
+
static unsigned int esdhc_pltfm_get_min_clock(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -391,6 +405,14 @@ static unsigned int esdhc_pltfm_get_min_clock(struct sdhci_host *host)
return clk_get_rate(pltfm_host->clk) / 256 / 16;
}
+static inline void esdhc_pltfm_set_clock(struct sdhci_host *host,
+ unsigned int clock)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+
+ esdhc_set_clock(host, clock, clk_get_rate(pltfm_host->clk));
+}
+
static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -438,8 +460,8 @@ static const struct sdhci_ops sdhci_esdhc_ops = {
.write_l = esdhc_writel_le,
.write_w = esdhc_writew_le,
.write_b = esdhc_writeb_le,
- .set_clock = esdhc_set_clock,
- .get_max_clock = sdhci_pltfm_clk_get_max_clock,
+ .set_clock = esdhc_pltfm_set_clock,
+ .get_max_clock = esdhc_pltfm_get_max_clock,
.get_min_clock = esdhc_pltfm_get_min_clock,
.get_ro = esdhc_pltfm_get_ro,
.platform_bus_width = esdhc_pltfm_bus_width,
@@ -482,6 +504,8 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
of_property_read_u32(np, "bus-width", &boarddata->max_bus_width);
+ of_property_read_u32(np, "max-frequency", &boarddata->f_max);
+
return 0;
}
#else
@@ -503,7 +527,7 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
int err;
struct pltfm_imx_data *imx_data;
- host = sdhci_pltfm_init(pdev, &sdhci_esdhc_imx_pdata);
+ host = sdhci_pltfm_init(pdev, &sdhci_esdhc_imx_pdata, 0);
if (IS_ERR(host))
return PTR_ERR(host);
diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h
index d25f9ab9a54d..a2a06420e463 100644
--- a/drivers/mmc/host/sdhci-esdhc.h
+++ b/drivers/mmc/host/sdhci-esdhc.h
@@ -36,13 +36,21 @@
/* pltfm-specific */
#define ESDHC_HOST_CONTROL_LE 0x20
+/*
+ * P2020 interpretation of the SDHCI_HOST_CONTROL register
+ */
+#define ESDHC_CTRL_4BITBUS (0x1 << 1)
+#define ESDHC_CTRL_8BITBUS (0x2 << 1)
+#define ESDHC_CTRL_BUSWIDTH_MASK (0x3 << 1)
+
/* OF-specific */
#define ESDHC_DMA_SYSCTL 0x40c
#define ESDHC_DMA_SNOOP 0x00000040
#define ESDHC_HOST_CONTROL_RES 0x05
-static inline void esdhc_set_clock(struct sdhci_host *host, unsigned int clock)
+static inline void esdhc_set_clock(struct sdhci_host *host, unsigned int clock,
+ unsigned int host_clock)
{
int pre_div = 2;
int div = 1;
@@ -56,14 +64,14 @@ static inline void esdhc_set_clock(struct sdhci_host *host, unsigned int clock)
| ESDHC_CLOCK_MASK);
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
- while (host->max_clk / pre_div / 16 > clock && pre_div < 256)
+ while (host_clock / pre_div / 16 > clock && pre_div < 256)
pre_div *= 2;
- while (host->max_clk / pre_div / div > clock && div < 16)
+ while (host_clock / pre_div / div > clock && div < 16)
div++;
dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n",
- clock, host->max_clk / pre_div / div);
+ clock, host_clock / pre_div / div);
pre_div >>= 1;
div--;
diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c
index 5e68adc2461e..15039e2d1c12 100644
--- a/drivers/mmc/host/sdhci-of-esdhc.c
+++ b/drivers/mmc/host/sdhci-of-esdhc.c
@@ -13,6 +13,7 @@
* your option) any later version.
*/
+#include <linux/err.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/delay.h>
@@ -120,6 +121,13 @@ static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg)
if (reg == SDHCI_HOST_CONTROL) {
u32 dma_bits;
+ /*
+ * If host control register is not standard, exit
+ * this function
+ */
+ if (host->quirks2 & SDHCI_QUIRK2_BROKEN_HOST_CONTROL)
+ return;
+
/* DMA select is 22,23 bits in Protocol Control Register */
dma_bits = (val & SDHCI_CTRL_DMA_MASK) << 5;
clrsetbits_be32(host->ioaddr + reg , SDHCI_CTRL_DMA_MASK << 5,
@@ -200,7 +208,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
}
/* Set the clock */
- esdhc_set_clock(host, clock);
+ esdhc_set_clock(host, clock, host->max_clk);
}
#ifdef CONFIG_PM
@@ -230,6 +238,30 @@ static void esdhc_of_platform_init(struct sdhci_host *host)
host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ;
}
+static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width)
+{
+ u32 ctrl;
+
+ switch (width) {
+ case MMC_BUS_WIDTH_8:
+ ctrl = ESDHC_CTRL_8BITBUS;
+ break;
+
+ case MMC_BUS_WIDTH_4:
+ ctrl = ESDHC_CTRL_4BITBUS;
+ break;
+
+ default:
+ ctrl = 0;
+ break;
+ }
+
+ clrsetbits_be32(host->ioaddr + SDHCI_HOST_CONTROL,
+ ESDHC_CTRL_BUSWIDTH_MASK, ctrl);
+
+ return 0;
+}
+
static const struct sdhci_ops sdhci_esdhc_ops = {
.read_l = esdhc_readl,
.read_w = esdhc_readw,
@@ -247,6 +279,7 @@ static const struct sdhci_ops sdhci_esdhc_ops = {
.platform_resume = esdhc_of_resume,
#endif
.adma_workaround = esdhci_of_adma_workaround,
+ .platform_bus_width = esdhc_pltfm_bus_width,
};
static const struct sdhci_pltfm_data sdhci_esdhc_pdata = {
@@ -262,7 +295,33 @@ static const struct sdhci_pltfm_data sdhci_esdhc_pdata = {
static int sdhci_esdhc_probe(struct platform_device *pdev)
{
- return sdhci_pltfm_register(pdev, &sdhci_esdhc_pdata);
+ struct sdhci_host *host;
+ struct device_node *np;
+ int ret;
+
+ host = sdhci_pltfm_init(pdev, &sdhci_esdhc_pdata, 0);
+ if (IS_ERR(host))
+ return PTR_ERR(host);
+
+ sdhci_get_of_property(pdev);
+
+ np = pdev->dev.of_node;
+ if (of_device_is_compatible(np, "fsl,p2020-esdhc")) {
+ /*
+ * Freescale messed up with P2020 as it has a non-standard
+ * host control register
+ */
+ host->quirks2 |= SDHCI_QUIRK2_BROKEN_HOST_CONTROL;
+ }
+
+ /* call to generic mmc_of_parse to support additional capabilities */
+ mmc_of_parse(host->mmc);
+
+ ret = sdhci_add_host(host);
+ if (ret)
+ sdhci_pltfm_free(pdev);
+
+ return ret;
}
static int sdhci_esdhc_remove(struct platform_device *pdev)
diff --git a/drivers/mmc/host/sdhci-of-hlwd.c b/drivers/mmc/host/sdhci-of-hlwd.c
index 200a6a9fa805..57c514a81ca5 100644
--- a/drivers/mmc/host/sdhci-of-hlwd.c
+++ b/drivers/mmc/host/sdhci-of-hlwd.c
@@ -68,7 +68,7 @@ static const struct sdhci_pltfm_data sdhci_hlwd_pdata = {
static int sdhci_hlwd_probe(struct platform_device *pdev)
{
- return sdhci_pltfm_register(pdev, &sdhci_hlwd_pdata);
+ return sdhci_pltfm_register(pdev, &sdhci_hlwd_pdata, 0);
}
static int sdhci_hlwd_remove(struct platform_device *pdev)
diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c
index 701d06d0e1fb..d7d6bc8968d2 100644
--- a/drivers/mmc/host/sdhci-pci.c
+++ b/drivers/mmc/host/sdhci-pci.c
@@ -36,6 +36,7 @@
#define PCI_DEVICE_ID_INTEL_BYT_EMMC 0x0f14
#define PCI_DEVICE_ID_INTEL_BYT_SDIO 0x0f15
#define PCI_DEVICE_ID_INTEL_BYT_SD 0x0f16
+#define PCI_DEVICE_ID_INTEL_BYT_EMMC2 0x0f50
/*
* PCI registers
@@ -77,6 +78,8 @@ struct sdhci_pci_slot {
int rst_n_gpio;
int cd_gpio;
int cd_irq;
+
+ void (*hw_reset)(struct sdhci_host *host);
};
struct sdhci_pci_chip {
@@ -307,10 +310,27 @@ static const struct sdhci_pci_fixes sdhci_intel_pch_sdio = {
.probe_slot = pch_hc_probe_slot,
};
+static void sdhci_pci_int_hw_reset(struct sdhci_host *host)
+{
+ u8 reg;
+
+ reg = sdhci_readb(host, SDHCI_POWER_CONTROL);
+ reg |= 0x10;
+ sdhci_writeb(host, reg, SDHCI_POWER_CONTROL);
+ /* For eMMC, minimum is 1us but give it 9us for good measure */
+ udelay(9);
+ reg &= ~0x10;
+ sdhci_writeb(host, reg, SDHCI_POWER_CONTROL);
+ /* For eMMC, minimum is 200us but give it 300us for good measure */
+ usleep_range(300, 1000);
+}
+
static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot)
{
- slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE;
+ slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE |
+ MMC_CAP_HW_RESET;
slot->host->mmc->caps2 |= MMC_CAP2_HC_ERASE_SZ;
+ slot->hw_reset = sdhci_pci_int_hw_reset;
return 0;
}
@@ -332,6 +352,8 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = {
};
static const struct sdhci_pci_fixes sdhci_intel_byt_sd = {
+ .quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON,
+ .allow_runtime_pm = true,
};
/* O2Micro extra registers */
@@ -910,6 +932,14 @@ static const struct pci_device_id pci_ids[] = {
},
{
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = PCI_DEVICE_ID_INTEL_BYT_EMMC2,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = (kernel_ulong_t)&sdhci_intel_byt_emmc,
+ },
+
+ {
.vendor = PCI_VENDOR_ID_O2,
.device = PCI_DEVICE_ID_O2_8120,
.subvendor = PCI_ANY_ID,
@@ -1014,7 +1044,7 @@ static int sdhci_pci_bus_width(struct sdhci_host *host, int width)
return 0;
}
-static void sdhci_pci_hw_reset(struct sdhci_host *host)
+static void sdhci_pci_gpio_hw_reset(struct sdhci_host *host)
{
struct sdhci_pci_slot *slot = sdhci_priv(host);
int rst_n_gpio = slot->rst_n_gpio;
@@ -1029,6 +1059,14 @@ static void sdhci_pci_hw_reset(struct sdhci_host *host)
usleep_range(300, 1000);
}
+static void sdhci_pci_hw_reset(struct sdhci_host *host)
+{
+ struct sdhci_pci_slot *slot = sdhci_priv(host);
+
+ if (slot->hw_reset)
+ slot->hw_reset(host);
+}
+
static const struct sdhci_ops sdhci_pci_ops = {
.enable_dma = sdhci_pci_enable_dma,
.platform_bus_width = sdhci_pci_bus_width,
@@ -1326,6 +1364,7 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
if (!gpio_request(slot->rst_n_gpio, "eMMC_reset")) {
gpio_direction_output(slot->rst_n_gpio, 1);
slot->host->mmc->caps |= MMC_CAP_HW_RESET;
+ slot->hw_reset = sdhci_pci_gpio_hw_reset;
} else {
dev_warn(&pdev->dev, "failed to request rst_n_gpio\n");
slot->rst_n_gpio = -EINVAL;
diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c
index cd0f1f68e261..e2065a44dffc 100644
--- a/drivers/mmc/host/sdhci-pltfm.c
+++ b/drivers/mmc/host/sdhci-pltfm.c
@@ -115,10 +115,10 @@ void sdhci_get_of_property(struct platform_device *pdev) {}
EXPORT_SYMBOL_GPL(sdhci_get_of_property);
struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev,
- const struct sdhci_pltfm_data *pdata)
+ const struct sdhci_pltfm_data *pdata,
+ size_t priv_size)
{
struct sdhci_host *host;
- struct sdhci_pltfm_host *pltfm_host;
struct device_node *np = pdev->dev.of_node;
struct resource *iomem;
int ret;
@@ -134,24 +134,27 @@ struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev,
/* Some PCI-based MFD need the parent here */
if (pdev->dev.parent != &platform_bus && !np)
- host = sdhci_alloc_host(pdev->dev.parent, sizeof(*pltfm_host));
+ host = sdhci_alloc_host(pdev->dev.parent,
+ sizeof(struct sdhci_pltfm_host) + priv_size);
else
- host = sdhci_alloc_host(&pdev->dev, sizeof(*pltfm_host));
+ host = sdhci_alloc_host(&pdev->dev,
+ sizeof(struct sdhci_pltfm_host) + priv_size);
if (IS_ERR(host)) {
ret = PTR_ERR(host);
goto err;
}
- pltfm_host = sdhci_priv(host);
-
host->hw_name = dev_name(&pdev->dev);
if (pdata && pdata->ops)
host->ops = pdata->ops;
else
host->ops = &sdhci_pltfm_ops;
- if (pdata)
+ if (pdata) {
host->quirks = pdata->quirks;
+ host->quirks2 = pdata->quirks2;
+ }
+
host->irq = platform_get_irq(pdev, 0);
if (!request_mem_region(iomem->start, resource_size(iomem),
@@ -197,17 +200,17 @@ void sdhci_pltfm_free(struct platform_device *pdev)
iounmap(host->ioaddr);
release_mem_region(iomem->start, resource_size(iomem));
sdhci_free_host(host);
- platform_set_drvdata(pdev, NULL);
}
EXPORT_SYMBOL_GPL(sdhci_pltfm_free);
int sdhci_pltfm_register(struct platform_device *pdev,
- const struct sdhci_pltfm_data *pdata)
+ const struct sdhci_pltfm_data *pdata,
+ size_t priv_size)
{
struct sdhci_host *host;
int ret = 0;
- host = sdhci_pltfm_init(pdev, pdata);
+ host = sdhci_pltfm_init(pdev, pdata, priv_size);
if (IS_ERR(host))
return PTR_ERR(host);
diff --git a/drivers/mmc/host/sdhci-pltfm.h b/drivers/mmc/host/sdhci-pltfm.h
index 1210ed1b0c60..e15ced79f7ed 100644
--- a/drivers/mmc/host/sdhci-pltfm.h
+++ b/drivers/mmc/host/sdhci-pltfm.h
@@ -18,6 +18,7 @@
struct sdhci_pltfm_data {
const struct sdhci_ops *ops;
unsigned int quirks;
+ unsigned int quirks2;
};
struct sdhci_pltfm_host {
@@ -27,6 +28,8 @@ struct sdhci_pltfm_host {
/* migrate from sdhci_of_host */
unsigned int clock;
u16 xfer_mode_shadow;
+
+ unsigned long private[0] ____cacheline_aligned;
};
#ifdef CONFIG_MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER
@@ -91,15 +94,22 @@ static inline void sdhci_be32bs_writeb(struct sdhci_host *host, u8 val, int reg)
extern void sdhci_get_of_property(struct platform_device *pdev);
extern struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev,
- const struct sdhci_pltfm_data *pdata);
+ const struct sdhci_pltfm_data *pdata,
+ size_t priv_size);
extern void sdhci_pltfm_free(struct platform_device *pdev);
extern int sdhci_pltfm_register(struct platform_device *pdev,
- const struct sdhci_pltfm_data *pdata);
+ const struct sdhci_pltfm_data *pdata,
+ size_t priv_size);
extern int sdhci_pltfm_unregister(struct platform_device *pdev);
extern unsigned int sdhci_pltfm_clk_get_max_clock(struct sdhci_host *host);
+static inline void *sdhci_pltfm_priv(struct sdhci_pltfm_host *host)
+{
+ return (void *)host->private;
+}
+
#ifdef CONFIG_PM
extern const struct dev_pm_ops sdhci_pltfm_pmops;
#define SDHCI_PLTFM_PMOPS (&sdhci_pltfm_pmops)
diff --git a/drivers/mmc/host/sdhci-pxav2.c b/drivers/mmc/host/sdhci-pxav2.c
index 6a3f702a38a6..d51e061ec576 100644
--- a/drivers/mmc/host/sdhci-pxav2.c
+++ b/drivers/mmc/host/sdhci-pxav2.c
@@ -175,7 +175,7 @@ static int sdhci_pxav2_probe(struct platform_device *pdev)
if (!pxa)
return -ENOMEM;
- host = sdhci_pltfm_init(pdev, NULL);
+ host = sdhci_pltfm_init(pdev, NULL, 0);
if (IS_ERR(host)) {
kfree(pxa);
return PTR_ERR(host);
@@ -253,8 +253,6 @@ static int sdhci_pxav2_remove(struct platform_device *pdev)
sdhci_pltfm_free(pdev);
kfree(pxa);
- platform_set_drvdata(pdev, NULL);
-
return 0;
}
diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c
index 1ae358e0662d..bf99359a3a90 100644
--- a/drivers/mmc/host/sdhci-pxav3.c
+++ b/drivers/mmc/host/sdhci-pxav3.c
@@ -230,7 +230,7 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
if (!pxa)
return -ENOMEM;
- host = sdhci_pltfm_init(pdev, &sdhci_pxav3_pdata);
+ host = sdhci_pltfm_init(pdev, &sdhci_pxav3_pdata, 0);
if (IS_ERR(host)) {
kfree(pxa);
return PTR_ERR(host);
@@ -252,7 +252,9 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
match = of_match_device(of_match_ptr(sdhci_pxav3_of_match), &pdev->dev);
if (match) {
- mmc_of_parse(host->mmc);
+ ret = mmc_of_parse(host->mmc);
+ if (ret)
+ goto err_of_parse;
sdhci_get_of_property(pdev);
pdata = pxav3_get_mmc_pdata(dev);
} else if (pdata) {
@@ -285,18 +287,15 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
}
}
- pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
+ pm_runtime_get_sync(&pdev->dev);
pm_runtime_set_autosuspend_delay(&pdev->dev, PXAV3_RPM_DELAY_MS);
pm_runtime_use_autosuspend(&pdev->dev);
pm_suspend_ignore_children(&pdev->dev, 1);
- pm_runtime_get_noresume(&pdev->dev);
ret = sdhci_add_host(host);
if (ret) {
dev_err(&pdev->dev, "failed to add host\n");
- pm_runtime_forbid(&pdev->dev);
- pm_runtime_disable(&pdev->dev);
goto err_add_host;
}
@@ -313,10 +312,13 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
return 0;
+err_of_parse:
+err_cd_req:
err_add_host:
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
clk_disable_unprepare(clk);
clk_put(clk);
-err_cd_req:
err_clk_get:
sdhci_pltfm_free(pdev);
kfree(pxa);
@@ -339,8 +341,6 @@ static int sdhci_pxav3_remove(struct platform_device *pdev)
sdhci_pltfm_free(pdev);
kfree(pxa);
- platform_set_drvdata(pdev, NULL);
-
return 0;
}
diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c
index c6f6246a4933..926aaf6acc67 100644
--- a/drivers/mmc/host/sdhci-s3c.c
+++ b/drivers/mmc/host/sdhci-s3c.c
@@ -745,7 +745,6 @@ static int sdhci_s3c_remove(struct platform_device *pdev)
clk_disable_unprepare(sc->clk_io);
sdhci_free_host(host);
- platform_set_drvdata(pdev, NULL);
return 0;
}
diff --git a/drivers/mmc/host/sdhci-sirf.c b/drivers/mmc/host/sdhci-sirf.c
index 09805af0526d..62a4a835acc6 100644
--- a/drivers/mmc/host/sdhci-sirf.c
+++ b/drivers/mmc/host/sdhci-sirf.c
@@ -13,7 +13,6 @@
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/mmc/slot-gpio.h>
-#include <linux/pinctrl/consumer.h>
#include "sdhci-pltfm.h"
struct sdhci_sirf_priv {
@@ -24,7 +23,7 @@ struct sdhci_sirf_priv {
static unsigned int sdhci_sirf_get_max_clk(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
- struct sdhci_sirf_priv *priv = pltfm_host->priv;
+ struct sdhci_sirf_priv *priv = sdhci_pltfm_priv(pltfm_host);
return clk_get_rate(priv->clk);
}
@@ -46,47 +45,35 @@ static int sdhci_sirf_probe(struct platform_device *pdev)
struct sdhci_host *host;
struct sdhci_pltfm_host *pltfm_host;
struct sdhci_sirf_priv *priv;
- struct pinctrl *pinctrl;
+ struct clk *clk;
+ int gpio_cd;
int ret;
- pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
- if (IS_ERR(pinctrl)) {
- dev_err(&pdev->dev, "unable to get pinmux");
- return PTR_ERR(pinctrl);
- }
-
- priv = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_sirf_priv),
- GFP_KERNEL);
- if (!priv) {
- dev_err(&pdev->dev, "unable to allocate private data");
- return -ENOMEM;
- }
-
- priv->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(priv->clk)) {
+ clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(clk)) {
dev_err(&pdev->dev, "unable to get clock");
- return PTR_ERR(priv->clk);
+ return PTR_ERR(clk);
}
- if (pdev->dev.of_node) {
- priv->gpio_cd = of_get_named_gpio(pdev->dev.of_node,
- "cd-gpios", 0);
- } else {
- priv->gpio_cd = -EINVAL;
- }
+ if (pdev->dev.of_node)
+ gpio_cd = of_get_named_gpio(pdev->dev.of_node, "cd-gpios", 0);
+ else
+ gpio_cd = -EINVAL;
- host = sdhci_pltfm_init(pdev, &sdhci_sirf_pdata);
- if (IS_ERR(host)) {
- ret = PTR_ERR(host);
- goto err_sdhci_pltfm_init;
- }
+ host = sdhci_pltfm_init(pdev, &sdhci_sirf_pdata, sizeof(struct sdhci_sirf_priv));
+ if (IS_ERR(host))
+ return PTR_ERR(host);
pltfm_host = sdhci_priv(host);
- pltfm_host->priv = priv;
+ priv = sdhci_pltfm_priv(pltfm_host);
+ priv->clk = clk;
+ priv->gpio_cd = gpio_cd;
sdhci_get_of_property(pdev);
- clk_prepare_enable(priv->clk);
+ ret = clk_prepare_enable(priv->clk);
+ if (ret)
+ goto err_clk_prepare;
ret = sdhci_add_host(host);
if (ret)
@@ -111,8 +98,8 @@ err_request_cd:
sdhci_remove_host(host, 0);
err_sdhci_add:
clk_disable_unprepare(priv->clk);
+err_clk_prepare:
sdhci_pltfm_free(pdev);
-err_sdhci_pltfm_init:
return ret;
}
@@ -120,7 +107,7 @@ static int sdhci_sirf_remove(struct platform_device *pdev)
{
struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
- struct sdhci_sirf_priv *priv = pltfm_host->priv;
+ struct sdhci_sirf_priv *priv = sdhci_pltfm_priv(pltfm_host);
sdhci_pltfm_unregister(pdev);
@@ -136,7 +123,7 @@ static int sdhci_sirf_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
- struct sdhci_sirf_priv *priv = pltfm_host->priv;
+ struct sdhci_sirf_priv *priv = sdhci_pltfm_priv(pltfm_host);
int ret;
ret = sdhci_suspend_host(host);
@@ -152,7 +139,7 @@ static int sdhci_sirf_resume(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
- struct sdhci_sirf_priv *priv = pltfm_host->priv;
+ struct sdhci_sirf_priv *priv = sdhci_pltfm_priv(pltfm_host);
int ret;
ret = clk_enable(priv->clk);
diff --git a/drivers/mmc/host/sdhci-spear.c b/drivers/mmc/host/sdhci-spear.c
index 7ae5b3ae7bad..2dba9f8d1760 100644
--- a/drivers/mmc/host/sdhci-spear.c
+++ b/drivers/mmc/host/sdhci-spear.c
@@ -258,7 +258,6 @@ static int sdhci_probe(struct platform_device *pdev)
return 0;
set_drvdata:
- platform_set_drvdata(pdev, NULL);
sdhci_remove_host(host, 1);
free_host:
sdhci_free_host(host);
@@ -278,7 +277,6 @@ static int sdhci_remove(struct platform_device *pdev)
int dead = 0;
u32 scratch;
- platform_set_drvdata(pdev, NULL);
scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
if (scratch == (u32)-1)
dead = 1;
diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
index e0dba74cff98..5b7b2eba8a54 100644
--- a/drivers/mmc/host/sdhci-tegra.c
+++ b/drivers/mmc/host/sdhci-tegra.c
@@ -205,7 +205,7 @@ static const struct of_device_id sdhci_tegra_dt_match[] = {
};
MODULE_DEVICE_TABLE(of, sdhci_tegra_dt_match);
-static void sdhci_tegra_parse_dt(struct device *dev)
+static int sdhci_tegra_parse_dt(struct device *dev)
{
struct device_node *np = dev->of_node;
struct sdhci_host *host = dev_get_drvdata(dev);
@@ -213,7 +213,7 @@ static void sdhci_tegra_parse_dt(struct device *dev)
struct sdhci_tegra *tegra_host = pltfm_host->priv;
tegra_host->power_gpio = of_get_named_gpio(np, "power-gpios", 0);
- mmc_of_parse(host->mmc);
+ return mmc_of_parse(host->mmc);
}
static int sdhci_tegra_probe(struct platform_device *pdev)
@@ -231,7 +231,7 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
return -EINVAL;
soc_data = match->data;
- host = sdhci_pltfm_init(pdev, soc_data->pdata);
+ host = sdhci_pltfm_init(pdev, soc_data->pdata, 0);
if (IS_ERR(host))
return PTR_ERR(host);
pltfm_host = sdhci_priv(host);
@@ -245,7 +245,9 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
tegra_host->soc_data = soc_data;
pltfm_host->priv = tegra_host;
- sdhci_tegra_parse_dt(&pdev->dev);
+ rc = sdhci_tegra_parse_dt(&pdev->dev);
+ if (rc)
+ goto err_parse_dt;
if (gpio_is_valid(tegra_host->power_gpio)) {
rc = gpio_request(tegra_host->power_gpio, "sdhci_power");
@@ -279,6 +281,7 @@ err_clk_get:
if (gpio_is_valid(tegra_host->power_gpio))
gpio_free(tegra_host->power_gpio);
err_power_req:
+err_parse_dt:
err_alloc_tegra_host:
sdhci_pltfm_free(pdev);
return rc;
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 2ea429c27714..a78bd4f3aecc 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -58,6 +58,8 @@ static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
#ifdef CONFIG_PM_RUNTIME
static int sdhci_runtime_pm_get(struct sdhci_host *host);
static int sdhci_runtime_pm_put(struct sdhci_host *host);
+static void sdhci_runtime_pm_bus_on(struct sdhci_host *host);
+static void sdhci_runtime_pm_bus_off(struct sdhci_host *host);
#else
static inline int sdhci_runtime_pm_get(struct sdhci_host *host)
{
@@ -67,6 +69,12 @@ static inline int sdhci_runtime_pm_put(struct sdhci_host *host)
{
return 0;
}
+static void sdhci_runtime_pm_bus_on(struct sdhci_host *host)
+{
+}
+static void sdhci_runtime_pm_bus_off(struct sdhci_host *host)
+{
+}
#endif
static void sdhci_dumpregs(struct sdhci_host *host)
@@ -192,8 +200,12 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask)
sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET);
- if (mask & SDHCI_RESET_ALL)
+ if (mask & SDHCI_RESET_ALL) {
host->clock = 0;
+ /* Reset-all turns off SD Bus Power */
+ if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON)
+ sdhci_runtime_pm_bus_off(host);
+ }
/* Wait max 100 ms */
timeout = 100;
@@ -1268,6 +1280,8 @@ static int sdhci_set_power(struct sdhci_host *host, unsigned short power)
if (pwr == 0) {
sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
+ if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON)
+ sdhci_runtime_pm_bus_off(host);
return 0;
}
@@ -1289,6 +1303,9 @@ static int sdhci_set_power(struct sdhci_host *host, unsigned short power)
sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
+ if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON)
+ sdhci_runtime_pm_bus_on(host);
+
/*
* Some controllers need an extra 10ms delay of 10ms before they
* can apply clock after applying power
@@ -1526,16 +1543,15 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
/* Select Bus Speed Mode for host */
ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
- if (ios->timing == MMC_TIMING_MMC_HS200)
- ctrl_2 |= SDHCI_CTRL_HS_SDR200;
+ if ((ios->timing == MMC_TIMING_MMC_HS200) ||
+ (ios->timing == MMC_TIMING_UHS_SDR104))
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
else if (ios->timing == MMC_TIMING_UHS_SDR12)
ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
else if (ios->timing == MMC_TIMING_UHS_SDR25)
ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
else if (ios->timing == MMC_TIMING_UHS_SDR50)
ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
- else if (ios->timing == MMC_TIMING_UHS_SDR104)
- ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
else if (ios->timing == MMC_TIMING_UHS_DDR50)
ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
@@ -1846,7 +1862,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
*/
if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) &&
(host->flags & SDHCI_SDR50_NEEDS_TUNING ||
- host->flags & SDHCI_HS200_NEEDS_TUNING))
+ host->flags & SDHCI_SDR104_NEEDS_TUNING))
requires_tuning_nonuhs = true;
if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR104) ||
@@ -2046,11 +2062,14 @@ static void sdhci_card_event(struct mmc_host *mmc)
struct sdhci_host *host = mmc_priv(mmc);
unsigned long flags;
+ /* First check if client has provided their own card event */
+ if (host->ops->card_event)
+ host->ops->card_event(host);
+
spin_lock_irqsave(&host->lock, flags);
/* Check host->mrq first in case we are runtime suspended */
- if (host->mrq &&
- !(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) {
+ if (host->mrq && !sdhci_do_get_cd(host)) {
pr_err("%s: Card removed during transfer!\n",
mmc_hostname(host->mmc));
pr_err("%s: Resetting controller.\n",
@@ -2625,6 +2644,22 @@ static int sdhci_runtime_pm_put(struct sdhci_host *host)
return pm_runtime_put_autosuspend(host->mmc->parent);
}
+static void sdhci_runtime_pm_bus_on(struct sdhci_host *host)
+{
+ if (host->runtime_suspended || host->bus_on)
+ return;
+ host->bus_on = true;
+ pm_runtime_get_noresume(host->mmc->parent);
+}
+
+static void sdhci_runtime_pm_bus_off(struct sdhci_host *host)
+{
+ if (host->runtime_suspended || !host->bus_on)
+ return;
+ host->bus_on = false;
+ pm_runtime_put_noidle(host->mmc->parent);
+}
+
int sdhci_runtime_suspend_host(struct sdhci_host *host)
{
unsigned long flags;
@@ -2962,9 +2997,13 @@ int sdhci_add_host(struct sdhci_host *host)
mmc->caps |= MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25;
/* SDR104 supports also implies SDR50 support */
- if (caps[1] & SDHCI_SUPPORT_SDR104)
+ if (caps[1] & SDHCI_SUPPORT_SDR104) {
mmc->caps |= MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_SDR50;
- else if (caps[1] & SDHCI_SUPPORT_SDR50)
+ /* SD3.0: SDR104 is supported so (for eMMC) the caps2
+ * field can be promoted to support HS200.
+ */
+ mmc->caps2 |= MMC_CAP2_HS200;
+ } else if (caps[1] & SDHCI_SUPPORT_SDR50)
mmc->caps |= MMC_CAP_UHS_SDR50;
if (caps[1] & SDHCI_SUPPORT_DDR50)
@@ -2974,9 +3013,9 @@ int sdhci_add_host(struct sdhci_host *host)
if (caps[1] & SDHCI_USE_SDR50_TUNING)
host->flags |= SDHCI_SDR50_NEEDS_TUNING;
- /* Does the host need tuning for HS200? */
+ /* Does the host need tuning for SDR104 / HS200? */
if (mmc->caps2 & MMC_CAP2_HS200)
- host->flags |= SDHCI_HS200_NEEDS_TUNING;
+ host->flags |= SDHCI_SDR104_NEEDS_TUNING;
/* Driver Type(s) (A, C, D) supported by the host */
if (caps[1] & SDHCI_DRIVER_TYPE_A)
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 379e09d9f3c1..b037f188fe44 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -294,6 +294,7 @@ struct sdhci_ops {
void (*platform_resume)(struct sdhci_host *host);
void (*adma_workaround)(struct sdhci_host *host, u32 intmask);
void (*platform_init)(struct sdhci_host *host);
+ void (*card_event)(struct sdhci_host *host);
};
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c
index ba76a532ae30..6706b5e3b974 100644
--- a/drivers/mmc/host/sh_mmcif.c
+++ b/drivers/mmc/host/sh_mmcif.c
@@ -1244,7 +1244,8 @@ static irqreturn_t sh_mmcif_intr(int irq, void *dev_id)
u32 state;
state = sh_mmcif_readl(host->addr, MMCIF_CE_INT);
- sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~state);
+ sh_mmcif_writel(host->addr, MMCIF_CE_INT,
+ ~(state & sh_mmcif_readl(host->addr, MMCIF_CE_INT_MASK)));
sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state & MASK_CLEAN);
if (state & ~MASK_CLEAN)
@@ -1369,7 +1370,11 @@ static int sh_mmcif_probe(struct platform_device *pdev)
ret = -ENOMEM;
goto ealloch;
}
- mmc_of_parse(mmc);
+
+ ret = mmc_of_parse(mmc);
+ if (ret < 0)
+ goto eofparse;
+
host = mmc_priv(mmc);
host->mmc = mmc;
host->addr = reg;
@@ -1464,6 +1469,7 @@ eclkupdate:
clk_put(host->hclk);
eclkget:
pm_runtime_disable(&pdev->dev);
+eofparse:
mmc_free_host(mmc);
ealloch:
iounmap(reg);
@@ -1501,8 +1507,6 @@ static int sh_mmcif_remove(struct platform_device *pdev)
if (irq[1] >= 0)
free_irq(irq[1], host);
- platform_set_drvdata(pdev, NULL);
-
clk_disable(host->hclk);
mmc_free_host(host->mmc);
pm_runtime_put_sync(&pdev->dev);
diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c
index fe90853900b4..ebea749297c2 100644
--- a/drivers/mmc/host/sh_mobile_sdhi.c
+++ b/drivers/mmc/host/sh_mobile_sdhi.c
@@ -46,14 +46,12 @@ static const struct sh_mobile_sdhi_of_data sh_mobile_sdhi_of_cfg[] = {
struct sh_mobile_sdhi {
struct clk *clk;
struct tmio_mmc_data mmc_data;
- struct sh_dmae_slave param_tx;
- struct sh_dmae_slave param_rx;
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 mmc_host *mmc = platform_get_drvdata(pdev);
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);
@@ -66,7 +64,7 @@ static int sh_mobile_sdhi_clk_enable(struct platform_device *pdev, unsigned int
static void sh_mobile_sdhi_clk_disable(struct platform_device *pdev)
{
- struct mmc_host *mmc = dev_get_drvdata(&pdev->dev);
+ struct mmc_host *mmc = platform_get_drvdata(pdev);
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);
@@ -121,7 +119,7 @@ static int sh_mobile_sdhi_write16_hook(struct tmio_mmc_host *host, int addr)
static void sh_mobile_sdhi_cd_wakeup(const struct platform_device *pdev)
{
- mmc_detect_change(dev_get_drvdata(&pdev->dev), msecs_to_jiffies(100));
+ mmc_detect_change(platform_get_drvdata(pdev), msecs_to_jiffies(100));
}
static const struct sh_mobile_sdhi_ops sdhi_ops = {
@@ -146,6 +144,7 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
struct tmio_mmc_host *host;
int irq, ret, i = 0;
bool multiplexed_isr = true;
+ struct tmio_mmc_dma *dma_priv;
priv = devm_kzalloc(&pdev->dev, sizeof(struct sh_mobile_sdhi), GFP_KERNEL);
if (priv == NULL) {
@@ -154,6 +153,7 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
}
mmc_data = &priv->mmc_data;
+ dma_priv = &priv->dma_priv;
if (p) {
if (p->init) {
@@ -186,15 +186,23 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
mmc_data->get_cd = sh_mobile_sdhi_get_cd;
if (p->dma_slave_tx > 0 && p->dma_slave_rx > 0) {
- priv->param_tx.shdma_slave.slave_id = p->dma_slave_tx;
- priv->param_rx.shdma_slave.slave_id = p->dma_slave_rx;
- priv->dma_priv.chan_priv_tx = &priv->param_tx.shdma_slave;
- priv->dma_priv.chan_priv_rx = &priv->param_rx.shdma_slave;
- priv->dma_priv.alignment_shift = 1; /* 2-byte alignment */
- mmc_data->dma = &priv->dma_priv;
+ /*
+ * Yes, we have to provide slave IDs twice to TMIO:
+ * once as a filter parameter and once for channel
+ * configuration as an explicit slave ID
+ */
+ dma_priv->chan_priv_tx = (void *)p->dma_slave_tx;
+ dma_priv->chan_priv_rx = (void *)p->dma_slave_rx;
+ dma_priv->slave_id_tx = p->dma_slave_tx;
+ dma_priv->slave_id_rx = p->dma_slave_rx;
}
}
+ dma_priv->alignment_shift = 1; /* 2-byte alignment */
+ dma_priv->filter = shdma_chan_filter;
+
+ mmc_data->dma = dma_priv;
+
/*
* All SDHI blocks support 2-byte and larger block sizes in 4-bit
* bus width mode.
@@ -265,8 +273,10 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
}
/* There must be at least one IRQ source */
- if (!i)
+ if (!i) {
+ ret = irq;
goto eirq;
+ }
}
dev_info(&pdev->dev, "%s base at 0x%08lx clock rate %u MHz\n",
diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c
index 139212e79cde..8860d4d2bc22 100644
--- a/drivers/mmc/host/tmio_mmc.c
+++ b/drivers/mmc/host/tmio_mmc.c
@@ -112,8 +112,6 @@ static int tmio_mmc_remove(struct platform_device *pdev)
const struct mfd_cell *cell = mfd_get_cell(pdev);
struct mmc_host *mmc = platform_get_drvdata(pdev);
- platform_set_drvdata(pdev, NULL);
-
if (mmc) {
struct tmio_mmc_host *host = mmc_priv(mmc);
free_irq(platform_get_irq(pdev, 0), host);
diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h
index d857f5c6e7d9..86fd21e00099 100644
--- a/drivers/mmc/host/tmio_mmc.h
+++ b/drivers/mmc/host/tmio_mmc.h
@@ -40,6 +40,22 @@
struct tmio_mmc_data;
+/*
+ * We differentiate between the following 3 power states:
+ * 1. card slot powered off, controller stopped. This is used, when either there
+ * is no card in the slot, or the card really has to be powered down.
+ * 2. card slot powered on, controller stopped. This is used, when a card is in
+ * the slot, but no activity is currently taking place. This is a power-
+ * saving mode with card-state preserved. This state can be entered, e.g.
+ * when MMC clock-gating is used.
+ * 3. card slot powered on, controller running. This is the actual active state.
+ */
+enum tmio_mmc_power {
+ TMIO_MMC_OFF_STOP, /* card power off, controller stopped */
+ TMIO_MMC_ON_STOP, /* card power on, controller stopped */
+ TMIO_MMC_ON_RUN, /* card power on, controller running */
+};
+
struct tmio_mmc_host {
void __iomem *ctl;
unsigned long bus_shift;
@@ -48,8 +64,8 @@ struct tmio_mmc_host {
struct mmc_data *data;
struct mmc_host *mmc;
- /* Controller power state */
- bool power;
+ /* Controller and card power state */
+ enum tmio_mmc_power power;
/* Callbacks for clock / power control */
void (*set_pwr)(struct platform_device *host, int state);
@@ -85,6 +101,7 @@ struct tmio_mmc_host {
unsigned long last_req_ts;
struct mutex ios_lock; /* protect set_ios() context */
bool native_hotplug;
+ bool resuming;
};
int tmio_mmc_host_probe(struct tmio_mmc_host **host,
diff --git a/drivers/mmc/host/tmio_mmc_dma.c b/drivers/mmc/host/tmio_mmc_dma.c
index fff928604859..47bdb8fa341b 100644
--- a/drivers/mmc/host/tmio_mmc_dma.c
+++ b/drivers/mmc/host/tmio_mmc_dma.c
@@ -261,42 +261,62 @@ out:
spin_unlock_irq(&host->lock);
}
-/* It might be necessary to make filter MFD specific */
-static bool tmio_mmc_filter(struct dma_chan *chan, void *arg)
-{
- dev_dbg(chan->device->dev, "%s: slave data %p\n", __func__, arg);
- chan->private = arg;
- return true;
-}
-
void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdata)
{
/* We can only either use DMA for both Tx and Rx or not use it at all */
- if (!pdata->dma)
+ if (!pdata->dma || (!host->pdev->dev.of_node &&
+ (!pdata->dma->chan_priv_tx || !pdata->dma->chan_priv_rx)))
return;
if (!host->chan_tx && !host->chan_rx) {
+ struct resource *res = platform_get_resource(host->pdev,
+ IORESOURCE_MEM, 0);
+ struct dma_slave_config cfg = {};
dma_cap_mask_t mask;
+ int ret;
+
+ if (!res)
+ return;
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
- host->chan_tx = dma_request_channel(mask, tmio_mmc_filter,
- pdata->dma->chan_priv_tx);
+ host->chan_tx = dma_request_slave_channel_compat(mask,
+ pdata->dma->filter, pdata->dma->chan_priv_tx,
+ &host->pdev->dev, "tx");
dev_dbg(&host->pdev->dev, "%s: TX: got channel %p\n", __func__,
host->chan_tx);
if (!host->chan_tx)
return;
- host->chan_rx = dma_request_channel(mask, tmio_mmc_filter,
- pdata->dma->chan_priv_rx);
+ if (pdata->dma->chan_priv_tx)
+ cfg.slave_id = pdata->dma->slave_id_tx;
+ cfg.direction = DMA_MEM_TO_DEV;
+ cfg.dst_addr = res->start + (CTL_SD_DATA_PORT << host->bus_shift);
+ cfg.src_addr = 0;
+ ret = dmaengine_slave_config(host->chan_tx, &cfg);
+ if (ret < 0)
+ goto ecfgtx;
+
+ host->chan_rx = dma_request_slave_channel_compat(mask,
+ pdata->dma->filter, pdata->dma->chan_priv_rx,
+ &host->pdev->dev, "rx");
dev_dbg(&host->pdev->dev, "%s: RX: got channel %p\n", __func__,
host->chan_rx);
if (!host->chan_rx)
goto ereqrx;
+ if (pdata->dma->chan_priv_rx)
+ cfg.slave_id = pdata->dma->slave_id_rx;
+ cfg.direction = DMA_DEV_TO_MEM;
+ cfg.src_addr = cfg.dst_addr;
+ cfg.dst_addr = 0;
+ ret = dmaengine_slave_config(host->chan_rx, &cfg);
+ if (ret < 0)
+ goto ecfgrx;
+
host->bounce_buf = (u8 *)__get_free_page(GFP_KERNEL | GFP_DMA);
if (!host->bounce_buf)
goto ebouncebuf;
@@ -310,9 +330,11 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat
return;
ebouncebuf:
+ecfgrx:
dma_release_channel(host->chan_rx);
host->chan_rx = NULL;
ereqrx:
+ecfgtx:
dma_release_channel(host->chan_tx);
host->chan_tx = NULL;
}
diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c
index f508ecb5b8a7..b72edb72f7d2 100644
--- a/drivers/mmc/host/tmio_mmc_pio.c
+++ b/drivers/mmc/host/tmio_mmc_pio.c
@@ -859,32 +859,45 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
* is kept positive, so no suspending actually takes place.
*/
if (ios->power_mode == MMC_POWER_ON && ios->clock) {
- if (!host->power) {
+ if (host->power != TMIO_MMC_ON_RUN) {
tmio_mmc_clk_update(mmc);
pm_runtime_get_sync(dev);
+ if (host->resuming) {
+ tmio_mmc_reset(host);
+ host->resuming = false;
+ }
}
+ if (host->power == TMIO_MMC_OFF_STOP)
+ tmio_mmc_reset(host);
tmio_mmc_set_clock(host, ios->clock);
- if (!host->power) {
+ if (host->power == TMIO_MMC_OFF_STOP)
/* power up SD card and the bus */
tmio_mmc_power_on(host, ios->vdd);
- host->power = true;
- }
+ host->power = TMIO_MMC_ON_RUN;
/* start bus clock */
tmio_mmc_clk_start(host);
} else if (ios->power_mode != MMC_POWER_UP) {
- if (host->power) {
- struct tmio_mmc_data *pdata = host->pdata;
- if (ios->power_mode == MMC_POWER_OFF)
+ struct tmio_mmc_data *pdata = host->pdata;
+ unsigned int old_power = host->power;
+
+ if (old_power != TMIO_MMC_OFF_STOP) {
+ if (ios->power_mode == MMC_POWER_OFF) {
tmio_mmc_power_off(host);
+ host->power = TMIO_MMC_OFF_STOP;
+ } else {
+ host->power = TMIO_MMC_ON_STOP;
+ }
+ }
+
+ if (old_power == TMIO_MMC_ON_RUN) {
tmio_mmc_clk_stop(host);
- host->power = false;
pm_runtime_put(dev);
if (pdata->clk_disable)
pdata->clk_disable(host->pdev);
}
}
- if (host->power) {
+ if (host->power != TMIO_MMC_OFF_STOP) {
switch (ios->bus_width) {
case MMC_BUS_WIDTH_1:
sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x80e0);
@@ -988,7 +1001,9 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host,
if (!mmc)
return -ENOMEM;
- mmc_of_parse(mmc);
+ ret = mmc_of_parse(mmc);
+ if (ret < 0)
+ goto host_free;
pdata->dev = &pdev->dev;
_host = mmc_priv(mmc);
@@ -1025,7 +1040,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host,
mmc->caps & MMC_CAP_NONREMOVABLE ||
mmc->slot.cd_irq >= 0);
- _host->power = false;
+ _host->power = TMIO_MMC_OFF_STOP;
pm_runtime_enable(&pdev->dev);
ret = pm_runtime_resume(&pdev->dev);
if (ret < 0)
@@ -1154,10 +1169,10 @@ int tmio_mmc_host_resume(struct device *dev)
struct mmc_host *mmc = dev_get_drvdata(dev);
struct tmio_mmc_host *host = mmc_priv(mmc);
- tmio_mmc_reset(host);
tmio_mmc_enable_dma(host, true);
/* The MMC core will perform the complete set up */
+ host->resuming = true;
return mmc_resume_host(mmc);
}
EXPORT_SYMBOL(tmio_mmc_host_resume);
@@ -1175,7 +1190,6 @@ int tmio_mmc_host_runtime_resume(struct device *dev)
struct mmc_host *mmc = dev_get_drvdata(dev);
struct tmio_mmc_host *host = mmc_priv(mmc);
- tmio_mmc_reset(host);
tmio_mmc_enable_dma(host, true);
return 0;
diff --git a/drivers/mmc/host/wmt-sdmmc.c b/drivers/mmc/host/wmt-sdmmc.c
index 442f5766ffca..34231d5168fc 100644
--- a/drivers/mmc/host/wmt-sdmmc.c
+++ b/drivers/mmc/host/wmt-sdmmc.c
@@ -927,8 +927,6 @@ static int wmt_mci_remove(struct platform_device *pdev)
mmc_free_host(mmc);
- platform_set_drvdata(pdev, NULL);
-
dev_info(&pdev->dev, "WMT MCI device removed\n");
return 0;
diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h
index 99bf3e665997..ce3511326f80 100644
--- a/include/linux/mfd/tmio.h
+++ b/include/linux/mfd/tmio.h
@@ -81,10 +81,15 @@ int tmio_core_mmc_resume(void __iomem *cnf, int shift, unsigned long base);
void tmio_core_mmc_pwr(void __iomem *cnf, int shift, int state);
void tmio_core_mmc_clk_div(void __iomem *cnf, int shift, int state);
+struct dma_chan;
+
struct tmio_mmc_dma {
void *chan_priv_tx;
void *chan_priv_rx;
+ int slave_id_tx;
+ int slave_id_rx;
int alignment_shift;
+ bool (*filter)(struct dma_chan *chan, void *arg);
};
struct tmio_mmc_host;
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index f31725ba49f3..842de3e21e70 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -94,7 +94,11 @@ struct mmc_ext_csd {
u8 raw_ext_csd_structure; /* 194 */
u8 raw_card_type; /* 196 */
u8 out_of_int_time; /* 198 */
- u8 raw_s_a_timeout; /* 217 */
+ u8 raw_pwr_cl_52_195; /* 200 */
+ u8 raw_pwr_cl_26_195; /* 201 */
+ u8 raw_pwr_cl_52_360; /* 202 */
+ u8 raw_pwr_cl_26_360; /* 203 */
+ u8 raw_s_a_timeout; /* 217 */
u8 raw_hc_erase_gap_size; /* 221 */
u8 raw_erase_timeout_mult; /* 223 */
u8 raw_hc_erase_grp_size; /* 224 */
@@ -102,6 +106,10 @@ struct mmc_ext_csd {
u8 raw_sec_erase_mult; /* 230 */
u8 raw_sec_feature_support;/* 231 */
u8 raw_trim_mult; /* 232 */
+ u8 raw_pwr_cl_200_195; /* 236 */
+ u8 raw_pwr_cl_200_360; /* 237 */
+ u8 raw_pwr_cl_ddr_52_195; /* 238 */
+ u8 raw_pwr_cl_ddr_52_360; /* 239 */
u8 raw_bkops_status; /* 246 */
u8 raw_sectors[4]; /* 212 - 4 bytes */
@@ -512,6 +520,7 @@ struct mmc_driver {
void (*remove)(struct mmc_card *);
int (*suspend)(struct mmc_card *);
int (*resume)(struct mmc_card *);
+ void (*shutdown)(struct mmc_card *);
};
extern int mmc_register_driver(struct mmc_driver *);
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 39613b9a6fc5..443243b241d5 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -96,6 +96,8 @@ struct mmc_command {
*/
unsigned int cmd_timeout_ms; /* in milliseconds */
+ /* Set this flag only for blocking sanitize request */
+ bool sanitize_busy;
struct mmc_data *data; /* data segment associated with cmd */
struct mmc_request *mrq; /* associated request */
@@ -188,6 +190,9 @@ extern int __mmc_claim_host(struct mmc_host *host, atomic_t *abort);
extern void mmc_release_host(struct mmc_host *host);
extern int mmc_try_claim_host(struct mmc_host *host);
+extern void mmc_get_card(struct mmc_card *card);
+extern void mmc_put_card(struct mmc_card *card);
+
extern int mmc_flush_cache(struct mmc_card *);
extern int mmc_detect_card_removed(struct mmc_host *host);
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index e326ae2882a0..3b0c33ae13e1 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -239,7 +239,7 @@ struct mmc_host {
#define MMC_CAP_SPI (1 << 4) /* Talks only SPI protocols */
#define MMC_CAP_NEEDS_POLL (1 << 5) /* Needs polling for card-detection */
#define MMC_CAP_8_BIT_DATA (1 << 6) /* Can the host do 8 bit transfers */
-
+#define MMC_CAP_AGGRESSIVE_PM (1 << 7) /* Suspend (e)MMC/SD at idle */
#define MMC_CAP_NONREMOVABLE (1 << 8) /* Nonremovable e.g. eMMC */
#define MMC_CAP_WAIT_WHILE_BUSY (1 << 9) /* Waits while card is busy */
#define MMC_CAP_ERASE (1 << 10) /* Allow erase/trim commands */
@@ -264,7 +264,7 @@ struct mmc_host {
#define MMC_CAP2_BOOTPART_NOACC (1 << 0) /* Boot partition no access */
#define MMC_CAP2_CACHE_CTRL (1 << 1) /* Allow cache control */
-#define MMC_CAP2_POWEROFF_NOTIFY (1 << 2) /* Notify poweroff supported */
+#define MMC_CAP2_FULL_PWR_CYCLE (1 << 2) /* Can do full power cycle */
#define MMC_CAP2_NO_MULTI_READ (1 << 3) /* Multiblock reads don't work */
#define MMC_CAP2_NO_SLEEP_CMD (1 << 4) /* Don't allow sleep command */
#define MMC_CAP2_HS200_1_8V_SDR (1 << 5) /* can support */
@@ -272,7 +272,6 @@ struct mmc_host {
#define MMC_CAP2_HS200 (MMC_CAP2_HS200_1_8V_SDR | \
MMC_CAP2_HS200_1_2V_SDR)
#define MMC_CAP2_BROKEN_VOLTAGE (1 << 7) /* Use the broken voltage */
-#define MMC_CAP2_DETECT_ON_ERR (1 << 8) /* On I/O err check card removal */
#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 */
@@ -281,6 +280,7 @@ struct mmc_host {
#define MMC_CAP2_PACKED_CMD (MMC_CAP2_PACKED_RD | \
MMC_CAP2_PACKED_WR)
#define MMC_CAP2_NO_PRESCAN_POWERUP (1 << 14) /* Don't power up before scan */
+#define MMC_CAP2_SANITIZE (1 << 15) /* Support Sanitize */
mmc_pm_flag_t pm_caps; /* supported pm features */
@@ -369,7 +369,7 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *);
int mmc_add_host(struct mmc_host *);
void mmc_remove_host(struct mmc_host *);
void mmc_free_host(struct mmc_host *);
-void mmc_of_parse(struct mmc_host *host);
+int mmc_of_parse(struct mmc_host *host);
static inline void *mmc_priv(struct mmc_host *host)
{
@@ -425,10 +425,6 @@ static inline int mmc_regulator_get_supply(struct mmc_host *mmc)
}
#endif
-int mmc_card_awake(struct mmc_host *host);
-int mmc_card_sleep(struct mmc_host *host);
-int mmc_card_can_sleep(struct mmc_host *host);
-
int mmc_pm_notify(struct notifier_block *notify_block, unsigned long, void *);
/* Module parameter */
diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
index b838ffc49e4a..e3c6a74d980a 100644
--- a/include/linux/mmc/sdhci.h
+++ b/include/linux/mmc/sdhci.h
@@ -95,6 +95,9 @@ struct sdhci_host {
/* The system physically doesn't support 1.8v, even if the host does */
#define SDHCI_QUIRK2_NO_1_8_V (1<<2)
#define SDHCI_QUIRK2_PRESET_VALUE_BROKEN (1<<3)
+#define SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON (1<<4)
+/* Controller has a non-standard host control register */
+#define SDHCI_QUIRK2_BROKEN_HOST_CONTROL (1<<5)
int irq; /* Device IRQ */
void __iomem *ioaddr; /* Mapped address */
@@ -126,7 +129,7 @@ struct sdhci_host {
#define SDHCI_AUTO_CMD23 (1<<7) /* Auto CMD23 support */
#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_SDR104_NEEDS_TUNING (1<<10) /* SDR104/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 */
@@ -139,6 +142,7 @@ struct sdhci_host {
u8 pwr; /* Current voltage */
bool runtime_suspended; /* Host is runtime suspended */
+ bool bus_on; /* Bus power prevents runtime suspend */
struct mmc_request *mrq; /* Current request */
struct mmc_command *cmd; /* Current command */
diff --git a/include/linux/platform_data/mmc-esdhc-imx.h b/include/linux/platform_data/mmc-esdhc-imx.h
index b4a0521ce411..d44912d81578 100644
--- a/include/linux/platform_data/mmc-esdhc-imx.h
+++ b/include/linux/platform_data/mmc-esdhc-imx.h
@@ -40,5 +40,6 @@ struct esdhc_platform_data {
enum wp_types wp_type;
enum cd_types cd_type;
int max_bus_width;
+ unsigned int f_max;
};
#endif /* __ASM_ARCH_IMX_ESDHC_H */