From 3303b194b0dc65894c28abe7331c20ad17bca6cb Mon Sep 17 00:00:00 2001 From: Philip Rakity Date: Sat, 19 Mar 2011 14:10:33 -0400 Subject: mmc: Ensure linux starts in eMMC user partition uBoot sometimes leaves eMMC pointing to the private boot partition. Ensure we always start looking at the user partition. Signed-off-by: Philip Rakity Signed-off-by: Bruce Clemens Signed-off-by: Mark F. Brown Signed-off-by: Chris Ball --- drivers/mmc/core/mmc.c | 10 ++++++++++ include/linux/mmc/card.h | 1 + include/linux/mmc/mmc.h | 1 + 3 files changed, 12 insertions(+) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 14e95f39a7bf..2d4880025d1b 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -288,6 +288,7 @@ static int mmc_read_ext_csd(struct mmc_card *card) if (card->ext_csd.rev >= 3) { u8 sa_shift = ext_csd[EXT_CSD_S_A_TIMEOUT]; + card->ext_csd.bootconfig = ext_csd[EXT_CSD_BOOT_CONFIG]; /* Sleep / awake timeout in 100ns units */ if (sa_shift > 0 && sa_shift <= 0x17) @@ -567,6 +568,15 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } } + /* + * Ensure eMMC user default partition is enabled + */ + if (card->ext_csd.bootconfig & 0x7) { + card->ext_csd.bootconfig &= ~0x7; + mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BOOT_CONFIG, + card->ext_csd.bootconfig); + } + /* * Activate high speed (if supported) */ diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index adb4888248be..557b73263390 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -45,6 +45,7 @@ struct mmc_ext_csd { u8 rev; u8 erase_group_def; u8 sec_feature_support; + u8 bootconfig; unsigned int sa_timeout; /* Units: 100ns */ unsigned int hs_max_dtr; unsigned int sectors; diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index 264ba5451e3b..b5ec88fd1352 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -256,6 +256,7 @@ struct _mmc_csd { #define EXT_CSD_PARTITION_ATTRIBUTE 156 /* R/W */ #define EXT_CSD_PARTITION_SUPPORT 160 /* RO */ #define EXT_CSD_ERASE_GROUP_DEF 175 /* R/W */ +#define EXT_CSD_BOOT_CONFIG 179 /* R/W */ #define EXT_CSD_ERASED_MEM_CONT 181 /* RO */ #define EXT_CSD_BUS_WIDTH 183 /* R/W */ #define EXT_CSD_HS_TIMING 185 /* R/W */ -- cgit v1.2.3 From e82c4c442f16bf3e4f67b024b3f6b1e7e64dc98a Mon Sep 17 00:00:00 2001 From: John Ogness Date: Fri, 25 Mar 2011 09:47:37 +0100 Subject: mmc: remove redundant irq disabling There is no need to disable irq's when using the sg_copy_*_buffer() functions because those functions do that already. There are also no races for the mm_queue struct here that would require the irq's to be disabled before calling sg_copy_*_buffer(). Signed-off-by: John Ogness Signed-off-by: Chris Ball --- drivers/mmc/card/queue.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c index 2ae727568df9..c07322c2658c 100644 --- a/drivers/mmc/card/queue.c +++ b/drivers/mmc/card/queue.c @@ -343,18 +343,14 @@ unsigned int mmc_queue_map_sg(struct mmc_queue *mq) */ void mmc_queue_bounce_pre(struct mmc_queue *mq) { - unsigned long flags; - if (!mq->bounce_buf) return; if (rq_data_dir(mq->req) != WRITE) return; - local_irq_save(flags); sg_copy_to_buffer(mq->bounce_sg, mq->bounce_sg_len, mq->bounce_buf, mq->sg[0].length); - local_irq_restore(flags); } /* @@ -363,17 +359,13 @@ void mmc_queue_bounce_pre(struct mmc_queue *mq) */ void mmc_queue_bounce_post(struct mmc_queue *mq) { - unsigned long flags; - if (!mq->bounce_buf) return; if (rq_data_dir(mq->req) != READ) return; - local_irq_save(flags); sg_copy_from_buffer(mq->bounce_sg, mq->bounce_sg_len, mq->bounce_buf, mq->sg[0].length); - local_irq_restore(flags); } -- cgit v1.2.3 From 656a4eaad4ad3f6820cefc8a321f8fdc2a8e46ca Mon Sep 17 00:00:00 2001 From: Venkat Rao Date: Fri, 25 Mar 2011 20:37:47 -0400 Subject: mmc: tegra: add pm_flags Enable fast bcm4329 WIFI suspend/resume on Tegra2 board. This patch allows the mach-tegra support to tell the tegra MMC host controller to NOT turn off power for the MMC controller the WIFI part lives behind. Thus bcm4329 firmware doesn't need to be reloaded. Signed-off-by: Venkat Rao Tested-by: Grant Grundler Reviewed-by: Olof Johansson Acked-by: Olof Johansson Signed-off-by: Chris Ball --- arch/arm/mach-tegra/include/mach/sdhci.h | 1 + drivers/mmc/host/sdhci-tegra.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/arch/arm/mach-tegra/include/mach/sdhci.h b/arch/arm/mach-tegra/include/mach/sdhci.h index 3ad086e859c3..4231bc7b8652 100644 --- a/arch/arm/mach-tegra/include/mach/sdhci.h +++ b/arch/arm/mach-tegra/include/mach/sdhci.h @@ -24,6 +24,7 @@ struct tegra_sdhci_platform_data { int wp_gpio; int power_gpio; int is_8bit; + int pm_flags; }; #endif diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index f7e1f964395f..343c97edba32 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -184,6 +184,8 @@ static int tegra_sdhci_pltfm_init(struct sdhci_host *host, clk_enable(clk); pltfm_host->clk = clk; + host->mmc->pm_caps = plat->pm_flags; + if (plat->is_8bit) host->mmc->caps |= MMC_CAP_8_BIT_DATA; -- cgit v1.2.3 From a6b23eb3d531b2f1cb99c5989694723e63ba17f0 Mon Sep 17 00:00:00 2001 From: Chris Ball Date: Tue, 29 Mar 2011 00:46:12 -0400 Subject: mmc: sdhci-pci: Fix error case in sdhci_pci_probe_slot() If pci_ioremap_bar() fails during probe, we "goto release;" and free the host, but then we return 0 -- which tells sdhci_pci_probe() that the probe succeeded. Since we think the probe succeeded, when we unload sdhci we'll go to sdhci_pci_remove_slot() and it will try to dereference slot->host, which is now NULL because we freed it in the error path earlier. The patch simply sets ret appropriately, so that sdhci_pci_probe() will detect the failure immediately and bail out. Signed-off-by: Chris Ball Cc: --- drivers/mmc/host/sdhci-pci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index a136be706347..f8b5f37007b2 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -957,6 +957,7 @@ static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot( host->ioaddr = pci_ioremap_bar(pdev, bar); if (!host->ioaddr) { dev_err(&pdev->dev, "failed to remap registers\n"); + ret = -ENOMEM; goto release; } -- cgit v1.2.3 From 689937e4db6f3b6602a121ef1cb409d8bac4ca1d Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Thu, 31 Mar 2011 09:31:19 +0800 Subject: mmc: sdhci: change CONFIG of MMC_SDHCI_TEGRA from 'tristate' to 'bool' This config option is used to decide whether tegra sdhci support should be build into the sdhci-platform module. So setting it 'm' is nonsense, since we can't build part of an module as a module. The effect for 'm' will probably be the same as for 'n'. Signed-off-by: Shawn Guo Signed-off-by: Chris Ball --- drivers/mmc/host/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 94df40531c38..b981715130c6 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -154,7 +154,7 @@ config MMC_SDHCI_DOVE If unsure, say N. config MMC_SDHCI_TEGRA - tristate "SDHCI platform support for the Tegra SD/MMC Controller" + bool "SDHCI platform support for the Tegra SD/MMC Controller" depends on MMC_SDHCI_PLTFM && ARCH_TEGRA select MMC_SDHCI_IO_ACCESSORS help -- cgit v1.2.3 From 67f68cb827c89f338ff28313e6079d4c5ad5f71c Mon Sep 17 00:00:00 2001 From: Andrei Warkentin Date: Thu, 31 Mar 2011 18:40:00 -0500 Subject: mmc: Reliable write support. Allows reliable writes to be used for MMC writes. Reliable writes are used to service write REQ_FUA/REQ_META requests. Handles both the legacy and the enhanced reliable write support in MMC cards. Signed-off-by: Andrei Warkentin Reviewed-by: Arnd Bergmann Signed-off-by: Chris Ball --- drivers/mmc/card/block.c | 81 +++++++++++++++++++++++++++++++++++++++++++++--- drivers/mmc/core/mmc.c | 5 +++ include/linux/mmc/card.h | 2 ++ include/linux/mmc/mmc.h | 4 +++ 4 files changed, 88 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 61d233a7c118..91a676773608 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -48,6 +48,10 @@ MODULE_ALIAS("mmc:block"); #endif #define MODULE_PARAM_PREFIX "mmcblk." +#define REL_WRITES_SUPPORTED(card) (mmc_card_mmc((card)) && \ + (((card)->ext_csd.rel_param & EXT_CSD_WR_REL_PARAM_EN) || \ + ((card)->ext_csd.rel_sectors))) + static DEFINE_MUTEX(block_mutex); /* @@ -331,6 +335,57 @@ out: return err ? 0 : 1; } +static int mmc_blk_issue_flush(struct mmc_queue *mq, struct request *req) +{ + struct mmc_blk_data *md = mq->data; + + /* + * No-op, only service this because we need REQ_FUA for reliable + * writes. + */ + spin_lock_irq(&md->lock); + __blk_end_request_all(req, 0); + spin_unlock_irq(&md->lock); + + return 1; +} + +/* + * Reformat current write as a reliable write, supporting + * both legacy and the enhanced reliable write MMC cards. + * In each transfer we'll handle only as much as a single + * reliable write can handle, thus finish the request in + * partial completions. + */ +static inline int mmc_apply_rel_rw(struct mmc_blk_request *brq, + struct mmc_card *card, + struct request *req) +{ + int err; + struct mmc_command set_count; + + if (!(card->ext_csd.rel_param & EXT_CSD_WR_REL_PARAM_EN)) { + /* Legacy mode imposes restrictions on transfers. */ + if (!IS_ALIGNED(brq->cmd.arg, card->ext_csd.rel_sectors)) + brq->data.blocks = 1; + + if (brq->data.blocks > card->ext_csd.rel_sectors) + brq->data.blocks = card->ext_csd.rel_sectors; + else if (brq->data.blocks < card->ext_csd.rel_sectors) + brq->data.blocks = 1; + } + + memset(&set_count, 0, sizeof(struct mmc_command)); + set_count.opcode = MMC_SET_BLOCK_COUNT; + set_count.arg = brq->data.blocks | (1 << 31); + set_count.flags = MMC_RSP_R1 | MMC_CMD_AC; + err = mmc_wait_for_cmd(card->host, &set_count, 0); + if (err) + printk(KERN_ERR "%s: error %d SET_BLOCK_COUNT\n", + req->rq_disk->disk_name, err); + return err; +} + static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req) { struct mmc_blk_data *md = mq->data; @@ -338,6 +393,15 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req) struct mmc_blk_request brq; int ret = 1, disable_multi = 0; + /* + * Reliable writes are used to implement Forced Unit Access and + * REQ_META accesses, and are supported only on MMCs. + */ + bool do_rel_wr = ((req->cmd_flags & REQ_FUA) || + (req->cmd_flags & REQ_META)) && + (rq_data_dir(req) == WRITE) && + REL_WRITES_SUPPORTED(card); + mmc_claim_host(card->host); do { @@ -374,12 +438,14 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req) if (disable_multi && brq.data.blocks > 1) brq.data.blocks = 1; - if (brq.data.blocks > 1) { + if (brq.data.blocks > 1 || do_rel_wr) { /* SPI multiblock writes terminate using a special - * token, not a STOP_TRANSMISSION request. + * token, not a STOP_TRANSMISSION request. Reliable + * writes use SET_BLOCK_COUNT and do not use a + * STOP_TRANSMISSION request either. */ - if (!mmc_host_is_spi(card->host) - || rq_data_dir(req) == READ) + if ((!mmc_host_is_spi(card->host) && !do_rel_wr) || + rq_data_dir(req) == READ) brq.mrq.stop = &brq.stop; readcmd = MMC_READ_MULTIPLE_BLOCK; writecmd = MMC_WRITE_MULTIPLE_BLOCK; @@ -396,6 +462,9 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req) brq.data.flags |= MMC_DATA_WRITE; } + if (do_rel_wr && mmc_apply_rel_rw(&brq, card, req)) + goto cmd_err; + mmc_set_data_timeout(&brq.data, card); brq.data.sg = mq->sg; @@ -565,6 +634,8 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) return mmc_blk_issue_secdiscard_rq(mq, req); else return mmc_blk_issue_discard_rq(mq, req); + } else if (req->cmd_flags & REQ_FLUSH) { + return mmc_blk_issue_flush(mq, req); } else { return mmc_blk_issue_rw_rq(mq, req); } @@ -622,6 +693,8 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card) md->disk->queue = md->queue.queue; md->disk->driverfs_dev = &card->dev; set_disk_ro(md->disk, md->read_only); + if (REL_WRITES_SUPPORTED(card)) + blk_queue_flush(md->queue.queue, REQ_FLUSH | REQ_FUA); /* * As discussed on lkml, GENHD_FL_REMOVABLE should: diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 2d4880025d1b..caba751bb58a 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -300,6 +300,8 @@ static int mmc_read_ext_csd(struct mmc_card *card) ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT]; card->ext_csd.hc_erase_size = ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] << 10; + + card->ext_csd.rel_sectors = ext_csd[EXT_CSD_REL_WR_SEC_C]; } if (card->ext_csd.rev >= 4) { @@ -351,6 +353,9 @@ static int mmc_read_ext_csd(struct mmc_card *card) ext_csd[EXT_CSD_TRIM_MULT]; } + if (card->ext_csd.rev >= 5) + card->ext_csd.rel_param = ext_csd[EXT_CSD_WR_REL_PARAM]; + if (ext_csd[EXT_CSD_ERASED_MEM_CONT]) card->erased_byte = 0xFF; else diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 557b73263390..c4e96fa5fb2b 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -45,6 +45,8 @@ struct mmc_ext_csd { u8 rev; u8 erase_group_def; u8 sec_feature_support; + u8 rel_sectors; + u8 rel_param; u8 bootconfig; unsigned int sa_timeout; /* Units: 100ns */ unsigned int hs_max_dtr; diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index b5ec88fd1352..390aa6eef676 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -255,6 +255,7 @@ struct _mmc_csd { #define EXT_CSD_PARTITION_ATTRIBUTE 156 /* R/W */ #define EXT_CSD_PARTITION_SUPPORT 160 /* RO */ +#define EXT_CSD_WR_REL_PARAM 166 /* RO */ #define EXT_CSD_ERASE_GROUP_DEF 175 /* R/W */ #define EXT_CSD_BOOT_CONFIG 179 /* R/W */ #define EXT_CSD_ERASED_MEM_CONT 181 /* RO */ @@ -265,6 +266,7 @@ struct _mmc_csd { #define EXT_CSD_CARD_TYPE 196 /* RO */ #define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */ #define EXT_CSD_S_A_TIMEOUT 217 /* RO */ +#define EXT_CSD_REL_WR_SEC_C 222 /* RO */ #define EXT_CSD_HC_WP_GRP_SIZE 221 /* RO */ #define EXT_CSD_ERASE_TIMEOUT_MULT 223 /* RO */ #define EXT_CSD_HC_ERASE_GRP_SIZE 224 /* RO */ @@ -277,6 +279,8 @@ struct _mmc_csd { * EXT_CSD field definitions */ +#define EXT_CSD_WR_REL_PARAM_EN (1<<2) + #define EXT_CSD_CMD_SET_NORMAL (1<<0) #define EXT_CSD_CMD_SET_SECURE (1<<1) #define EXT_CSD_CMD_SET_CPSECURE (1<<2) -- cgit v1.2.3 From f6f3d42fb3014b1787c10457cc71ad083cd12162 Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Tue, 5 Apr 2011 17:43:20 +0300 Subject: mmc: mmc_card_keep_power cleanups mmc_card_is_powered_resumed is a mouthful; instead, simply use mmc_card_keep_power, which also better explains the purpose of the macro. Employ mmc_card_keep_power() where possible. Signed-off-by: Ohad Ben-Cohen Signed-off-by: Chris Ball --- drivers/mmc/core/core.c | 4 ++-- drivers/mmc/core/sdio.c | 10 +++++----- include/linux/mmc/host.h | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 1f453acc8682..c2350e474159 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1746,7 +1746,7 @@ int mmc_suspend_host(struct mmc_host *host) } mmc_bus_put(host); - if (!err && !(host->pm_flags & MMC_PM_KEEP_POWER)) + if (!err && !mmc_card_keep_power(host)) mmc_power_off(host); return err; @@ -1764,7 +1764,7 @@ int mmc_resume_host(struct mmc_host *host) mmc_bus_get(host); if (host->bus_ops && !host->bus_dead) { - if (!(host->pm_flags & MMC_PM_KEEP_POWER)) { + if (!mmc_card_keep_power(host)) { mmc_power_up(host); mmc_select_voltage(host, host->ocr); /* diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index db0f0b44d684..0f7d4362d213 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -625,7 +625,7 @@ static int mmc_sdio_suspend(struct mmc_host *host) } } - if (!err && host->pm_flags & MMC_PM_KEEP_POWER) { + if (!err && mmc_card_keep_power(host)) { mmc_claim_host(host); sdio_disable_wide(host->card); mmc_release_host(host); @@ -645,10 +645,10 @@ static int mmc_sdio_resume(struct mmc_host *host) mmc_claim_host(host); /* No need to reinitialize powered-resumed nonremovable cards */ - if (mmc_card_is_removable(host) || !mmc_card_is_powered_resumed(host)) + if (mmc_card_is_removable(host) || !mmc_card_keep_power(host)) err = mmc_sdio_init_card(host, host->ocr, host->card, - (host->pm_flags & MMC_PM_KEEP_POWER)); - else if (mmc_card_is_powered_resumed(host)) { + mmc_card_keep_power(host)); + else if (mmc_card_keep_power(host)) { /* We may have switched to 1-bit mode during suspend */ err = sdio_enable_4bit_bus(host->card); if (err > 0) { @@ -691,7 +691,7 @@ static int mmc_sdio_power_restore(struct mmc_host *host) mmc_claim_host(host); ret = mmc_sdio_init_card(host, host->ocr, host->card, - (host->pm_flags & MMC_PM_KEEP_POWER)); + mmc_card_keep_power(host)); if (!ret && host->sdio_irqs) mmc_signal_sdio_irq(host); mmc_release_host(host); diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index bcb793ec7374..cccb5cf9d160 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -321,7 +321,7 @@ static inline int mmc_card_is_removable(struct mmc_host *host) return !(host->caps & MMC_CAP_NONREMOVABLE) && mmc_assume_removable; } -static inline int mmc_card_is_powered_resumed(struct mmc_host *host) +static inline int mmc_card_keep_power(struct mmc_host *host) { return host->pm_flags & MMC_PM_KEEP_POWER; } -- cgit v1.2.3 From 5b772f0d7e1e33e216221c0409329b9f6e678aad Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Tue, 5 Apr 2011 17:43:21 +0300 Subject: mmc: do not switch to 1-bit mode if not required 6b5eda36 followed SDIO spec part E1 section 8, which states that in case SDIO interrupts are being used to wake up a suspended host, then it is required to switch to 1-bit mode before stopping the clock. Before switching to 1-bit mode (or back to 4-bit mode on resume), make sure that SDIO interrupts are really being used to wake the host. This is helpful for devices which have an external irq line (e.g. wl1271), and do not use SDIO interrupts to wake up the host. In this case, switching to 1-bit mode (and back to 4-bit mode on resume) is not necessary. Reported-by: Eliad Peller Signed-off-by: Ohad Ben-Cohen Signed-off-by: Chris Ball --- drivers/mmc/core/sdio.c | 4 ++-- include/linux/mmc/host.h | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 0f7d4362d213..4221670bf82e 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -625,7 +625,7 @@ static int mmc_sdio_suspend(struct mmc_host *host) } } - if (!err && mmc_card_keep_power(host)) { + if (!err && mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) { mmc_claim_host(host); sdio_disable_wide(host->card); mmc_release_host(host); @@ -648,7 +648,7 @@ static int mmc_sdio_resume(struct mmc_host *host) if (mmc_card_is_removable(host) || !mmc_card_keep_power(host)) err = mmc_sdio_init_card(host, host->ocr, host->card, mmc_card_keep_power(host)); - else if (mmc_card_keep_power(host)) { + else if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) { /* We may have switched to 1-bit mode during suspend */ err = sdio_enable_4bit_bus(host->card); if (err > 0) { diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index cccb5cf9d160..4f705eb1eca1 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -326,5 +326,9 @@ static inline int mmc_card_keep_power(struct mmc_host *host) return host->pm_flags & MMC_PM_KEEP_POWER; } +static inline int mmc_card_wake_sdio_irq(struct mmc_host *host) +{ + return host->pm_flags & MMC_PM_WAKE_SDIO_IRQ; +} #endif -- cgit v1.2.3 From 86771731550bccade10e2d8d1f1db8f11b0f226f Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Tue, 5 Apr 2011 17:50:14 +0300 Subject: mmc: add MMC_QUIRK_NONSTD_FUNC_IF Introduce MMC_QUIRK_NONSTD_FUNC_IF to ignore the "SDIO Standard Function interface code" as indicated by the card's FBR, and instead treat all functions as non-standard interfaces. This is required to prevent standard drivers from facing errors when trying to communicate with SDIO cards that erroneously indicate standard function interface codes. Signed-off-by: Ohad Ben-Cohen Signed-off-by: Chris Ball --- drivers/mmc/core/sdio.c | 6 ++++++ include/linux/mmc/card.h | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 4221670bf82e..c3c2bd0874e8 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "core.h" #include "bus.h" @@ -31,6 +32,11 @@ static int sdio_read_fbr(struct sdio_func *func) int ret; unsigned char data; + if (mmc_card_nonstd_func_interface(func->card)) { + func->class = SDIO_CLASS_NONE; + return 0; + } + ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_FBR_BASE(func->num) + SDIO_FBR_STD_IF, 0, &data); if (ret) diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index c4e96fa5fb2b..317a0029e7d7 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -128,6 +128,7 @@ struct mmc_card { #define MMC_QUIRK_NONSTD_SDIO (1<<2) /* non-standard SDIO card attached */ /* (missing CIA registers) */ #define MMC_QUIRK_BROKEN_CLK_GATING (1<<3) /* clock gating the sdio bus will make card fail */ +#define MMC_QUIRK_NONSTD_FUNC_IF (1<<4) /* SDIO card has nonstd function interfaces */ unsigned int erase_size; /* erase size in sectors */ unsigned int erase_shift; /* if erase unit is power 2 */ @@ -183,6 +184,11 @@ static inline int mmc_blksz_for_byte_mode(const struct mmc_card *c) return c->quirks & MMC_QUIRK_BLKSZ_FOR_BYTE_MODE; } +static inline int mmc_card_nonstd_func_interface(const struct mmc_card *c) +{ + return c->quirks & MMC_QUIRK_NONSTD_FUNC_IF; +} + #define mmc_card_name(c) ((c)->cid.prod_name) #define mmc_card_id(c) (dev_name(&(c)->dev)) -- cgit v1.2.3 From 70b2331191dc651d665d446ebfec1431f46ddf7a Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Tue, 5 Apr 2011 17:50:15 +0300 Subject: mmc: quirks: wl1271 is MMC_QUIRK_NONSTD_FUNC_IF Tell SDIO core to ignore the standard SDIO function interface codes indicated by the wl1271. This is required because the wl1271 erroneously indicates its first function as a standard Bluetooth SDIO interface, and that drives btsdio mad. Signed-off-by: Ohad Ben-Cohen Signed-off-by: Chris Ball --- drivers/mmc/core/quirks.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mmc/core/quirks.c b/drivers/mmc/core/quirks.c index 11118b74eb20..19573981d934 100644 --- a/drivers/mmc/core/quirks.c +++ b/drivers/mmc/core/quirks.c @@ -64,6 +64,8 @@ static const struct mmc_fixup mmc_fixup_methods[] = { add_quirk_for_sdio_devices, MMC_QUIRK_BROKEN_CLK_GATING }, { SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271, remove_quirk, MMC_QUIRK_BROKEN_CLK_GATING }, + { SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271, + add_quirk, MMC_QUIRK_NONSTD_FUNC_IF }, { 0 } }; -- cgit v1.2.3 From 18732728367f60360a8c025152cb83bd8ca2580c Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Tue, 5 Apr 2011 18:02:25 +0300 Subject: mmc: add MMC_QUIRK_DISABLE_CD 006ebd5d introduced sdio_disable_cd(), which disconnects the pull-up resistor on CD/DAT[3] (pin 1) of the card. Make it possible to start using sdio_disable_cd() by introducing MMC_QUIRK_DISABLE_CD. Signed-off-by: Ohad Ben-Cohen Signed-off-by: Chris Ball --- drivers/mmc/core/sdio.c | 2 +- include/linux/mmc/card.h | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index c3c2bd0874e8..a5840c0de2e4 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -187,7 +187,7 @@ static int sdio_disable_cd(struct mmc_card *card) int ret; u8 ctrl; - if (!card->cccr.disable_cd) + if (!mmc_card_disable_cd(card)) return 0; ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_IF, 0, &ctrl); diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 317a0029e7d7..2a7e54970c93 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -129,6 +129,7 @@ struct mmc_card { /* (missing CIA registers) */ #define MMC_QUIRK_BROKEN_CLK_GATING (1<<3) /* clock gating the sdio bus will make card fail */ #define MMC_QUIRK_NONSTD_FUNC_IF (1<<4) /* SDIO card has nonstd function interfaces */ +#define MMC_QUIRK_DISABLE_CD (1<<5) /* disconnect CD/DAT[3] resistor */ unsigned int erase_size; /* erase size in sectors */ unsigned int erase_shift; /* if erase unit is power 2 */ @@ -184,6 +185,11 @@ static inline int mmc_blksz_for_byte_mode(const struct mmc_card *c) return c->quirks & MMC_QUIRK_BLKSZ_FOR_BYTE_MODE; } +static inline int mmc_card_disable_cd(const struct mmc_card *c) +{ + return c->quirks & MMC_QUIRK_DISABLE_CD; +} + static inline int mmc_card_nonstd_func_interface(const struct mmc_card *c) { return c->quirks & MMC_QUIRK_NONSTD_FUNC_IF; -- cgit v1.2.3 From 72daf3e87d6a1791331d3975eace2e290c2f229e Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Tue, 5 Apr 2011 18:02:26 +0300 Subject: mmc: quirks: wl1271 is MMC_QUIRK_DISABLE_CD The wl12xx device supports disconnecting the pull-up resistor on CD/DAT[3] (pin 1) of the card. Tell SDIO core to disconnect that resistor during card init, since we don't need it at that point (and anyway all hosts shall provide pull-up resistors on all data lines DAT[3:0] as described in section 6 of the SD physical specification). As a result, this may save some power, but it's also generally healthy since it prevents both ends from pulling up that pin, which results in undesirable asymmetric physical bus. Signed-off-by: Ohad Ben-Cohen Signed-off-by: Chris Ball --- drivers/mmc/core/quirks.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mmc/core/quirks.c b/drivers/mmc/core/quirks.c index 19573981d934..a4c42edc6cb3 100644 --- a/drivers/mmc/core/quirks.c +++ b/drivers/mmc/core/quirks.c @@ -66,6 +66,8 @@ static const struct mmc_fixup mmc_fixup_methods[] = { remove_quirk, MMC_QUIRK_BROKEN_CLK_GATING }, { SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271, add_quirk, MMC_QUIRK_NONSTD_FUNC_IF }, + { SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271, + add_quirk, MMC_QUIRK_DISABLE_CD }, { 0 } }; -- cgit v1.2.3 From a1e81e6536161cf39d2902ba05865f8e4276a6c0 Mon Sep 17 00:00:00 2001 From: Michał Mirosław Date: Sat, 9 Apr 2011 08:16:47 +0200 Subject: mmc: core: mmc_add_card(): fix missing break in switch statement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes a cosmetic bug that affects printk() for SD-combo cards. Reported-by: Prashanth Bhat Signed-off-by: Michał Mirosław Signed-off-by: Chris Ball --- drivers/mmc/core/bus.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index 63667a8f140c..d6d62fd07ee9 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -284,6 +284,7 @@ int mmc_add_card(struct mmc_card *card) type = "SD-combo"; if (mmc_card_blockaddr(card)) type = "SDHC-combo"; + break; default: type = "?"; break; -- cgit v1.2.3 From 9a9dd34415b4e50cbbaafdc69b27f0f90cc3f0d1 Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Mon, 11 Apr 2011 17:00:44 -0400 Subject: mmc: omap: Fix possible NULL pointer deref Either OMAP_MMC_STAT_CARD_ERR or OMAP_MMC_STAT_END_OF_CMD might fire if there is no host->cmd pointer. Check for a valid host->cmd pointer before calling mmc_omap_cmd_done(). Signed-off-by: Michael Buesch Acked-by: Tony Lindgren Signed-off-by: Chris Ball --- drivers/mmc/host/omap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index 2e032f0e8cf4..a6c329040140 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -832,7 +832,7 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) return IRQ_HANDLED; } - if (end_command) + if (end_command && host->cmd) mmc_omap_cmd_done(host, host->cmd); if (host->data != NULL) { if (transfer_error) -- cgit v1.2.3 From 91be3d76aa3e53e398fdaa12662a2d85767c8311 Mon Sep 17 00:00:00 2001 From: Ameya Palande <2ameya@gmail.com> Date: Tue, 5 Apr 2011 21:13:13 +0300 Subject: mmc: sdhci-pci: Fix checkpatch.pl errors This patch fixes 21 errors and 6 warnings reported by checkpatch.pl. Signed-off-by: Ameya Palande <2ameya@gmail.com> Reviewed-by: Wolfram Sang Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-pci.c | 44 +++++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index f8b5f37007b2..82c5dc412679 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -18,11 +18,9 @@ #include #include #include - #include - -#include -#include +#include +#include #include "sdhci.h" @@ -46,14 +44,14 @@ struct sdhci_pci_slot; struct sdhci_pci_fixes { unsigned int quirks; - int (*probe)(struct sdhci_pci_chip*); + int (*probe) (struct sdhci_pci_chip *); - int (*probe_slot)(struct sdhci_pci_slot*); - void (*remove_slot)(struct sdhci_pci_slot*, int); + int (*probe_slot) (struct sdhci_pci_slot *); + void (*remove_slot) (struct sdhci_pci_slot *, int); - int (*suspend)(struct sdhci_pci_chip*, + int (*suspend) (struct sdhci_pci_chip *, pm_message_t); - int (*resume)(struct sdhci_pci_chip*); + int (*resume) (struct sdhci_pci_chip *); }; struct sdhci_pci_slot { @@ -402,7 +400,7 @@ static int jmicron_suspend(struct sdhci_pci_chip *chip, pm_message_t state) if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC || chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_ESD) { - for (i = 0;i < chip->num_slots;i++) + for (i = 0; i < chip->num_slots; i++) jmicron_enable_mmc(chip->slots[i]->host, 0); } @@ -415,7 +413,7 @@ static int jmicron_resume(struct sdhci_pci_chip *chip) if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC || chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_ESD) { - for (i = 0;i < chip->num_slots;i++) + for (i = 0; i < chip->num_slots; i++) jmicron_enable_mmc(chip->slots[i]->host, 1); } @@ -798,7 +796,7 @@ static struct sdhci_ops sdhci_pci_ops = { #ifdef CONFIG_PM -static int sdhci_pci_suspend (struct pci_dev *pdev, pm_message_t state) +static int sdhci_pci_suspend(struct pci_dev *pdev, pm_message_t state) { struct sdhci_pci_chip *chip; struct sdhci_pci_slot *slot; @@ -810,7 +808,7 @@ static int sdhci_pci_suspend (struct pci_dev *pdev, pm_message_t state) if (!chip) return 0; - for (i = 0;i < chip->num_slots;i++) { + for (i = 0; i < chip->num_slots; i++) { slot = chip->slots[i]; if (!slot) continue; @@ -818,7 +816,7 @@ static int sdhci_pci_suspend (struct pci_dev *pdev, pm_message_t state) ret = sdhci_suspend_host(slot->host, state); if (ret) { - for (i--;i >= 0;i--) + for (i--; i >= 0; i--) sdhci_resume_host(chip->slots[i]->host); return ret; } @@ -833,7 +831,7 @@ static int sdhci_pci_suspend (struct pci_dev *pdev, pm_message_t state) if (chip->fixes && chip->fixes->suspend) { ret = chip->fixes->suspend(chip, state); if (ret) { - for (i = chip->num_slots - 1;i >= 0;i--) + for (i = chip->num_slots - 1; i >= 0; i--) sdhci_resume_host(chip->slots[i]->host); return ret; } @@ -855,7 +853,7 @@ static int sdhci_pci_suspend (struct pci_dev *pdev, pm_message_t state) return 0; } -static int sdhci_pci_resume (struct pci_dev *pdev) +static int sdhci_pci_resume(struct pci_dev *pdev) { struct sdhci_pci_chip *chip; struct sdhci_pci_slot *slot; @@ -877,7 +875,7 @@ static int sdhci_pci_resume (struct pci_dev *pdev) return ret; } - for (i = 0;i < chip->num_slots;i++) { + for (i = 0; i < chip->num_slots; i++) { slot = chip->slots[i]; if (!slot) continue; @@ -1059,7 +1057,7 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev, } chip->pdev = pdev; - chip->fixes = (const struct sdhci_pci_fixes*)ent->driver_data; + chip->fixes = (const struct sdhci_pci_fixes *)ent->driver_data; if (chip->fixes) chip->quirks = chip->fixes->quirks; chip->num_slots = slots; @@ -1074,10 +1072,10 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev, slots = chip->num_slots; /* Quirk may have changed this */ - for (i = 0;i < slots;i++) { + for (i = 0; i < slots; i++) { slot = sdhci_pci_probe_slot(pdev, chip, first_bar + i); if (IS_ERR(slot)) { - for (i--;i >= 0;i--) + for (i--; i >= 0; i--) sdhci_pci_remove_slot(chip->slots[i]); ret = PTR_ERR(slot); goto free; @@ -1105,7 +1103,7 @@ static void __devexit sdhci_pci_remove(struct pci_dev *pdev) chip = pci_get_drvdata(pdev); if (chip) { - for (i = 0;i < chip->num_slots; i++) + for (i = 0; i < chip->num_slots; i++) sdhci_pci_remove_slot(chip->slots[i]); pci_set_drvdata(pdev, NULL); @@ -1116,9 +1114,9 @@ static void __devexit sdhci_pci_remove(struct pci_dev *pdev) } static struct pci_driver sdhci_driver = { - .name = "sdhci-pci", + .name = "sdhci-pci", .id_table = pci_ids, - .probe = sdhci_pci_probe, + .probe = sdhci_pci_probe, .remove = __devexit_p(sdhci_pci_remove), .suspend = sdhci_pci_suspend, .resume = sdhci_pci_resume, -- cgit v1.2.3 From 4deaa9f88e6aeefd06e45e54c381ac56cfbe34d9 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 11 Apr 2011 06:11:29 +0200 Subject: mmc: core: fix memory leak in mmc_add_host led_trigger_register_simple() allocates memory which must not be leaked in the error-path of mmc_add_host. Move it past the only error-check in the function. Signed-off-by: Wolfram Sang Signed-off-by: Chris Ball --- drivers/mmc/core/host.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 461e6a17fb90..b29d3e8fd3a2 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -325,12 +325,12 @@ int mmc_add_host(struct mmc_host *host) WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) && !host->ops->enable_sdio_irq); - led_trigger_register_simple(dev_name(&host->class_dev), &host->led); - err = device_add(&host->class_dev); if (err) return err; + led_trigger_register_simple(dev_name(&host->class_dev), &host->led); + #ifdef CONFIG_DEBUG_FS mmc_add_host_debugfs(host); #endif -- cgit v1.2.3 From 0041365bcf93c76b033570540a10b0dc38b5c21a Mon Sep 17 00:00:00 2001 From: Andrei Warkentin Date: Mon, 11 Apr 2011 17:02:15 -0500 Subject: mmc: quirks: Extends card quirks with MMC/SD quirks matching the CID. The current mechanism is SDIO-only. This allows us to create function-specific quirks, without creating messy Kconfig dependencies, or polluting core/ with function-specific code. Signed-off-by: Andrei Warkentin Acked-by: Arnd Bergmann Signed-off-by: Chris Ball --- drivers/mmc/core/core.h | 2 - drivers/mmc/core/quirks.c | 93 +++++++++++++++++++++-------------------------- drivers/mmc/core/sdio.c | 2 +- include/linux/mmc/card.h | 91 +++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 133 insertions(+), 55 deletions(-) diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 20b1c0831eac..ca1fdde29df6 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -61,8 +61,6 @@ int mmc_attach_mmc(struct mmc_host *host); int mmc_attach_sd(struct mmc_host *host); int mmc_attach_sdio(struct mmc_host *host); -void mmc_fixup_device(struct mmc_card *card); - /* Module parameters */ extern int use_spi_crc; diff --git a/drivers/mmc/core/quirks.c b/drivers/mmc/core/quirks.c index a4c42edc6cb3..3a596217029e 100644 --- a/drivers/mmc/core/quirks.c +++ b/drivers/mmc/core/quirks.c @@ -1,7 +1,8 @@ /* - * This file contains work-arounds for many known sdio hardware - * bugs. + * This file contains work-arounds for many known SD/MMC + * and SDIO hardware bugs. * + * Copyright (c) 2011 Andrei Warkentin * Copyright (c) 2011 Pierre Tardy * Inspired from pci fixup code: * Copyright (c) 1999 Martin Mares @@ -11,34 +12,14 @@ #include #include #include -#include -/* - * The world is not perfect and supplies us with broken mmc/sdio devices. - * For at least a part of these bugs we need a work-around - */ - -struct mmc_fixup { - u16 vendor, device; /* You can use SDIO_ANY_ID here of course */ - void (*vendor_fixup)(struct mmc_card *card, int data); - int data; -}; - -/* - * This hook just adds a quirk unconditionnally - */ -static void __maybe_unused add_quirk(struct mmc_card *card, int data) -{ - card->quirks |= data; -} +#ifndef SDIO_VENDOR_ID_TI +#define SDIO_VENDOR_ID_TI 0x0097 +#endif -/* - * This hook just removes a quirk unconditionnally - */ -static void __maybe_unused remove_quirk(struct mmc_card *card, int data) -{ - card->quirks &= ~data; -} +#ifndef SDIO_DEVICE_ID_TI_WL1271 +#define SDIO_DEVICE_ID_TI_WL1271 0x4076 +#endif /* * This hook just adds a quirk for all sdio devices @@ -49,37 +30,47 @@ static void add_quirk_for_sdio_devices(struct mmc_card *card, int data) card->quirks |= data; } -#ifndef SDIO_VENDOR_ID_TI -#define SDIO_VENDOR_ID_TI 0x0097 -#endif - -#ifndef SDIO_DEVICE_ID_TI_WL1271 -#define SDIO_DEVICE_ID_TI_WL1271 0x4076 -#endif - static const struct mmc_fixup mmc_fixup_methods[] = { /* by default sdio devices are considered CLK_GATING broken */ /* good cards will be whitelisted as they are tested */ - { SDIO_ANY_ID, SDIO_ANY_ID, - add_quirk_for_sdio_devices, MMC_QUIRK_BROKEN_CLK_GATING }, - { SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271, - remove_quirk, MMC_QUIRK_BROKEN_CLK_GATING }, - { SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271, - add_quirk, MMC_QUIRK_NONSTD_FUNC_IF }, - { SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271, - add_quirk, MMC_QUIRK_DISABLE_CD }, - { 0 } + SDIO_FIXUP(SDIO_ANY_ID, SDIO_ANY_ID, + add_quirk_for_sdio_devices, + MMC_QUIRK_BROKEN_CLK_GATING), + + SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271, + remove_quirk, MMC_QUIRK_BROKEN_CLK_GATING), + + SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271, + add_quirk, MMC_QUIRK_NONSTD_FUNC_IF), + + SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271, + add_quirk, MMC_QUIRK_DISABLE_CD), + + END_FIXUP }; -void mmc_fixup_device(struct mmc_card *card) +void mmc_fixup_device(struct mmc_card *card, const struct mmc_fixup *table) { const struct mmc_fixup *f; + u64 rev = cid_rev_card(card); + + /* Non-core specific workarounds. */ + if (!table) + table = mmc_fixup_methods; - for (f = mmc_fixup_methods; f->vendor_fixup; f++) { - if ((f->vendor == card->cis.vendor - || f->vendor == (u16) SDIO_ANY_ID) && - (f->device == card->cis.device - || f->device == (u16) SDIO_ANY_ID)) { + for (f = table; f->vendor_fixup; f++) { + if ((f->manfid == CID_MANFID_ANY || + f->manfid == card->cid.manfid) && + (f->oemid == CID_OEMID_ANY || + f->oemid == card->cid.oemid) && + (f->name == CID_NAME_ANY || + !strncmp(f->name, card->cid.prod_name, + sizeof(card->cid.prod_name))) && + (f->cis_vendor == card->cis.vendor || + f->cis_vendor == (u16) SDIO_ANY_ID) && + (f->cis_device == card->cis.device || + f->cis_device == (u16) SDIO_ANY_ID) && + rev >= f->rev_start && rev <= f->rev_end) { dev_dbg(&card->dev, "calling %pF\n", f->vendor_fixup); f->vendor_fixup(card, f->data); } diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index a5840c0de2e4..1e6095961500 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -472,7 +472,7 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, card = oldcard; } - mmc_fixup_device(card); + mmc_fixup_device(card, NULL); if (card->type == MMC_TYPE_SD_COMBO) { err = mmc_sd_setup_card(host, card, oldcard != NULL); diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 2a7e54970c93..c6513175f7f1 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -11,6 +11,7 @@ #define LINUX_MMC_CARD_H #include +#include struct mmc_cid { unsigned int manfid; @@ -157,7 +158,92 @@ struct mmc_card { struct dentry *debugfs_root; }; -void mmc_fixup_device(struct mmc_card *dev); +/* + * The world is not perfect and supplies us with broken mmc/sdio devices. + * For at least some of these bugs we need a work-around. + */ + +struct mmc_fixup { + /* CID-specific fields. */ + const char *name; + + /* Valid revision range */ + u64 rev_start, rev_end; + + unsigned int manfid; + unsigned short oemid; + + /* SDIO-specfic fields. You can use SDIO_ANY_ID here of course */ + u16 cis_vendor, cis_device; + + void (*vendor_fixup)(struct mmc_card *card, int data); + int data; +}; + +#define CID_MANFID_ANY (-1ul) +#define CID_OEMID_ANY ((unsigned short) -1) +#define CID_NAME_ANY (NULL) + +#define END_FIXUP { 0 } + +#define _FIXUP_EXT(_name, _manfid, _oemid, _rev_start, _rev_end, \ + _cis_vendor, _cis_device, \ + _fixup, _data) \ + { \ + .name = (_name), \ + .manfid = (_manfid), \ + .oemid = (_oemid), \ + .rev_start = (_rev_start), \ + .rev_end = (_rev_end), \ + .cis_vendor = (_cis_vendor), \ + .cis_device = (_cis_device), \ + .vendor_fixup = (_fixup), \ + .data = (_data), \ + } + +#define MMC_FIXUP_REV(_name, _manfid, _oemid, _rev_start, _rev_end, \ + _fixup, _data) \ + _FIXUP_EXT(_name, _manfid, \ + _oemid, _rev_start, _rev_end, \ + SDIO_ANY_ID, SDIO_ANY_ID, \ + _fixup, _data) \ + +#define MMC_FIXUP(_name, _manfid, _oemid, _fixup, _data) \ + MMC_FIXUP_REV(_name, _manfid, _oemid, 0, -1ull, _fixup, _data) + +#define SDIO_FIXUP(_vendor, _device, _fixup, _data) \ + _FIXUP_EXT(CID_NAME_ANY, CID_MANFID_ANY, \ + CID_OEMID_ANY, 0, -1ull, \ + _vendor, _device, \ + _fixup, _data) \ + +#define cid_rev(hwrev, fwrev, year, month) \ + (((u64) hwrev) << 40 | \ + ((u64) fwrev) << 32 | \ + ((u64) year) << 16 | \ + ((u64) month)) + +#define cid_rev_card(card) \ + cid_rev(card->cid.hwrev, \ + card->cid.fwrev, \ + card->cid.year, \ + card->cid.month) + +/* + * This hook just adds a quirk unconditionally. + */ +static inline void __maybe_unused add_quirk(struct mmc_card *card, int data) +{ + card->quirks |= data; +} + +/* + * This hook just removes a quirk unconditionally. + */ +static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data) +{ + card->quirks &= ~data; +} #define mmc_card_mmc(c) ((c)->type == MMC_TYPE_MMC) #define mmc_card_sd(c) ((c)->type == MMC_TYPE_SD) @@ -218,4 +304,7 @@ struct mmc_driver { extern int mmc_register_driver(struct mmc_driver *); extern void mmc_unregister_driver(struct mmc_driver *); +extern void mmc_fixup_device(struct mmc_card *card, + const struct mmc_fixup *table); + #endif -- cgit v1.2.3 From 20c4b36997c23851985fcbbb98513a72c7495ddc Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 12 Apr 2011 12:59:09 -0400 Subject: mmc: quirks: fix truncation warnings Fix data truncation warnings: .manfid is not unsigned long: drivers/mmc/core/quirks.c:36: warning: large integer implicitly truncated to unsigned type drivers/mmc/core/quirks.c:40: warning: large integer implicitly truncated to unsigned type drivers/mmc/core/quirks.c:43: warning: large integer implicitly truncated to unsigned type drivers/mmc/core/quirks.c:46: warning: large integer implicitly truncated to unsigned type Signed-off-by: Randy Dunlap Signed-off-by: Chris Ball --- include/linux/mmc/card.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index c6513175f7f1..937f852cf01e 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -180,7 +180,7 @@ struct mmc_fixup { int data; }; -#define CID_MANFID_ANY (-1ul) +#define CID_MANFID_ANY (-1u) #define CID_OEMID_ANY ((unsigned short) -1) #define CID_NAME_ANY (NULL) -- cgit v1.2.3 From 7a9af3f4e31364069605722c29c5f9c6f0bcc3b8 Mon Sep 17 00:00:00 2001 From: Andrei Warkentin Date: Mon, 11 Apr 2011 16:13:41 -0500 Subject: mmc: core: Rename erase_timeout to cmd_timeout_ms. Renames erase_timeout to cmd_timeout_ms inside struct mmc_command. First step to making host honor timeouts for non-data-transfer commands. Cleans up erase timeout code. Signed-off-by: Andrei Warkentin Signed-off-by: Chris Ball --- drivers/mmc/core/core.c | 39 +++++++++++++++++++++------------------ include/linux/mmc/core.h | 2 +- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index c2350e474159..5178d5daa5f4 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1187,9 +1187,8 @@ void mmc_init_erase(struct mmc_card *card) } } -static void mmc_set_mmc_erase_timeout(struct mmc_card *card, - struct mmc_command *cmd, - unsigned int arg, unsigned int qty) +static unsigned int mmc_mmc_erase_timeout(struct mmc_card *card, + unsigned int arg, unsigned int qty) { unsigned int erase_timeout; @@ -1246,38 +1245,42 @@ static void mmc_set_mmc_erase_timeout(struct mmc_card *card, if (mmc_host_is_spi(card->host) && erase_timeout < 1000) erase_timeout = 1000; - cmd->erase_timeout = erase_timeout; + return erase_timeout; } -static void mmc_set_sd_erase_timeout(struct mmc_card *card, - struct mmc_command *cmd, unsigned int arg, - unsigned int qty) +static unsigned int mmc_sd_erase_timeout(struct mmc_card *card, + unsigned int arg, + unsigned int qty) { + unsigned int erase_timeout; + if (card->ssr.erase_timeout) { /* Erase timeout specified in SD Status Register (SSR) */ - cmd->erase_timeout = card->ssr.erase_timeout * qty + - card->ssr.erase_offset; + erase_timeout = card->ssr.erase_timeout * qty + + card->ssr.erase_offset; } else { /* * Erase timeout not specified in SD Status Register (SSR) so * use 250ms per write block. */ - cmd->erase_timeout = 250 * qty; + erase_timeout = 250 * qty; } /* Must not be less than 1 second */ - if (cmd->erase_timeout < 1000) - cmd->erase_timeout = 1000; + if (erase_timeout < 1000) + erase_timeout = 1000; + + return erase_timeout; } -static void mmc_set_erase_timeout(struct mmc_card *card, - struct mmc_command *cmd, unsigned int arg, - unsigned int qty) +static unsigned int mmc_erase_timeout(struct mmc_card *card, + unsigned int arg, + unsigned int qty) { if (mmc_card_sd(card)) - mmc_set_sd_erase_timeout(card, cmd, arg, qty); + return mmc_sd_erase_timeout(card, arg, qty); else - mmc_set_mmc_erase_timeout(card, cmd, arg, qty); + return mmc_mmc_erase_timeout(card, arg, qty); } static int mmc_do_erase(struct mmc_card *card, unsigned int from, @@ -1351,7 +1354,7 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from, cmd.opcode = MMC_ERASE; cmd.arg = arg; cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; - mmc_set_erase_timeout(card, &cmd, arg, qty); + cmd.cmd_timeout_ms = mmc_erase_timeout(card, arg, qty); err = mmc_wait_for_cmd(card->host, &cmd, 0); if (err) { printk(KERN_ERR "mmc_erase: erase error %d, status %#x\n", diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index 07f27af4dba5..811e96e8d1fe 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -92,7 +92,7 @@ struct mmc_command { * actively failing requests */ - unsigned int erase_timeout; /* in milliseconds */ + unsigned int cmd_timeout_ms; /* in milliseconds */ struct mmc_data *data; /* data segment associated with cmd */ struct mmc_request *mrq; /* associated request */ -- cgit v1.2.3 From 373e6a446e0c8e67e4207e8db20e8b62f6aacc8d Mon Sep 17 00:00:00 2001 From: Andrei Warkentin Date: Mon, 11 Apr 2011 16:13:42 -0500 Subject: mmc: sdhci: R1B command handling + MMC_CAP_ERASE. ERASE command needs R1B response, so fix R1B-type command handling for SDHCI controller. For non-DAT commands using a busy response, the cmd->cmd_timeout_ms (in ms) field is used for timeout calculations. Based on patch by Chuanxiao Dong Signed-off-by: Andrei Warkentin Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 9e15f41f87be..36a0063c7a2c 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -40,7 +40,6 @@ static unsigned int debug_quirks = 0; -static void sdhci_prepare_data(struct sdhci_host *, struct mmc_data *); static void sdhci_finish_data(struct sdhci_host *); static void sdhci_send_command(struct sdhci_host *, struct mmc_command *); @@ -591,9 +590,10 @@ static void sdhci_adma_table_post(struct sdhci_host *host, data->sg_len, direction); } -static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_data *data) +static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd) { u8 count; + struct mmc_data *data = cmd->data; unsigned target_timeout, current_timeout; /* @@ -605,12 +605,16 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_data *data) if (host->quirks & SDHCI_QUIRK_BROKEN_TIMEOUT_VAL) return 0xE; - /* timeout in us */ - target_timeout = data->timeout_ns / 1000 + - data->timeout_clks / host->clock; + /* Unspecified timeout, assume max */ + if (!data && !cmd->cmd_timeout_ms) + return 0xE; - if (host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK) - host->timeout_clk = host->clock / 1000; + /* timeout in us */ + if (!data) + target_timeout = cmd->cmd_timeout_ms * 1000; + else + target_timeout = data->timeout_ns / 1000 + + data->timeout_clks / host->clock; /* * Figure out needed cycles. @@ -632,8 +636,8 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_data *data) } if (count >= 0xF) { - printk(KERN_WARNING "%s: Too large timeout requested!\n", - mmc_hostname(host->mmc)); + printk(KERN_WARNING "%s: Too large timeout requested for CMD%d!\n", + mmc_hostname(host->mmc), cmd->opcode); count = 0xE; } @@ -651,15 +655,21 @@ static void sdhci_set_transfer_irqs(struct sdhci_host *host) sdhci_clear_set_irqs(host, dma_irqs, pio_irqs); } -static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) +static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) { u8 count; u8 ctrl; + struct mmc_data *data = cmd->data; int ret; WARN_ON(host->data); - if (data == NULL) + if (data || (cmd->flags & MMC_RSP_BUSY)) { + count = sdhci_calc_timeout(host, cmd); + sdhci_writeb(host, count, SDHCI_TIMEOUT_CONTROL); + } + + if (!data) return; /* Sanity checks */ @@ -670,9 +680,6 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) host->data = data; host->data_early = 0; - count = sdhci_calc_timeout(host, data); - sdhci_writeb(host, count, SDHCI_TIMEOUT_CONTROL); - if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) host->flags |= SDHCI_REQ_USE_DMA; @@ -920,7 +927,7 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) host->cmd = cmd; - sdhci_prepare_data(host, cmd->data); + sdhci_prepare_data(host, cmd); sdhci_writel(host, cmd->arg, SDHCI_ARGUMENT); @@ -1867,6 +1874,9 @@ int sdhci_add_host(struct sdhci_host *host) if (caps & SDHCI_TIMEOUT_CLK_UNIT) host->timeout_clk *= 1000; + if (host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK) + host->timeout_clk = host->clock / 1000; + /* * Set host parameters. */ @@ -1879,7 +1889,7 @@ int sdhci_add_host(struct sdhci_host *host) mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_200; mmc->f_max = host->max_clk; - mmc->caps |= MMC_CAP_SDIO_IRQ; + mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE; /* * A controller may support 8-bit width, but the board itself -- cgit v1.2.3 From 5ac430931610ceb09648f3b0e6ce77a86cdcb70a Mon Sep 17 00:00:00 2001 From: Andrei Warkentin Date: Mon, 11 Apr 2011 16:13:43 -0500 Subject: mmc: core: Allow setting CMD timeout for CMD6 (SWITCH). CMD6 is an R1B-type command, where DAT is used as busy. Depending on register written using CMD6, timeout value can be different as per spec. Signed-off-by: Andrei Warkentin Signed-off-by: Chris Ball --- drivers/mmc/core/mmc.c | 14 ++++++++------ drivers/mmc/core/mmc_ops.c | 16 +++++++++++++++- drivers/mmc/core/mmc_ops.h | 1 - include/linux/mmc/core.h | 1 + 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index caba751bb58a..a5cf42345026 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -548,7 +548,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, */ if (card->ext_csd.enhanced_area_en) { err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_ERASE_GROUP_DEF, 1); + EXT_CSD_ERASE_GROUP_DEF, 1, 0); if (err && err != -EBADMSG) goto free_card; @@ -579,7 +579,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, if (card->ext_csd.bootconfig & 0x7) { card->ext_csd.bootconfig &= ~0x7; mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BOOT_CONFIG, - card->ext_csd.bootconfig); + card->ext_csd.bootconfig, 0); } /* @@ -588,7 +588,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, if ((card->ext_csd.hs_max_dtr != 0) && (host->caps & MMC_CAP_MMC_HIGHSPEED)) { err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_HS_TIMING, 1); + EXT_CSD_HS_TIMING, 1, 0); if (err && err != -EBADMSG) goto free_card; @@ -655,7 +655,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ddr = 0; /* no DDR for 1-bit width */ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, - ext_csd_bits[idx][0]); + ext_csd_bits[idx][0], + 0); if (!err) { mmc_set_bus_width_ddr(card->host, bus_width, MMC_SDR_MODE); @@ -674,8 +675,9 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, if (!err && ddr) { err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_BUS_WIDTH, - ext_csd_bits[idx][1]); + EXT_CSD_BUS_WIDTH, + ext_csd_bits[idx][1], + 0); } if (err) { printk(KERN_WARNING "%s: switch to bus width %d ddr %d " diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 60842f878ded..5f1891933a96 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -387,7 +387,19 @@ int mmc_spi_set_crc(struct mmc_host *host, int use_crc) return err; } -int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value) +/** + * mmc_switch - modify EXT_CSD register + * @card: the MMC card associated with the data transfer + * @set: cmd set values + * @index: EXT_CSD register index + * @value: value to program into EXT_CSD register + * @timeout_ms: timeout (ms) for operation performed by register write, + * timeout of zero implies maximum possible timeout + * + * Modifies the EXT_CSD register for selected card. + */ +int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, + unsigned int timeout_ms) { int err; struct mmc_command cmd; @@ -404,6 +416,7 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value) (value << 8) | set; cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; + cmd.cmd_timeout_ms = timeout_ms; err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); if (err) @@ -433,6 +446,7 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value) return 0; } +EXPORT_SYMBOL_GPL(mmc_switch); int mmc_send_status(struct mmc_card *card, u32 *status) { diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h index e6d44b8a18db..9276946fa5b7 100644 --- a/drivers/mmc/core/mmc_ops.h +++ b/drivers/mmc/core/mmc_ops.h @@ -20,7 +20,6 @@ int mmc_all_send_cid(struct mmc_host *host, u32 *cid); int mmc_set_relative_addr(struct mmc_card *card); int mmc_send_csd(struct mmc_card *card, u32 *csd); int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd); -int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value); 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); diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index 811e96e8d1fe..f8e4bcbd2846 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -135,6 +135,7 @@ extern void mmc_wait_for_req(struct mmc_host *, struct mmc_request *); extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int); extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *, struct mmc_command *, int); +extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int); #define MMC_ERASE_ARG 0x00000000 #define MMC_SECURE_ERASE_ARG 0x80000000 -- cgit v1.2.3 From e87e280111cc9d84015a2e7a6f437af45f455979 Mon Sep 17 00:00:00 2001 From: Andrei Warkentin Date: Mon, 11 Apr 2011 18:10:24 -0500 Subject: mmc: card: block.c cleanup for host claim/release. Move host claim/release into mmc_blk_issue_rq. (This is helpful so that selecting partition only has to happen in one place for these commands.) Signed-off-by: Andrei Warkentin Signed-off-by: Chris Ball --- drivers/mmc/card/block.c | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 91a676773608..3e9082b729ff 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -273,8 +273,6 @@ static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req) unsigned int from, nr, arg; int err = 0; - mmc_claim_host(card->host); - if (!mmc_can_erase(card)) { err = -EOPNOTSUPP; goto out; @@ -294,8 +292,6 @@ out: __blk_end_request(req, err, blk_rq_bytes(req)); spin_unlock_irq(&md->lock); - mmc_release_host(card->host); - return err ? 0 : 1; } @@ -307,8 +303,6 @@ static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq, unsigned int from, nr, arg; int err = 0; - mmc_claim_host(card->host); - if (!mmc_can_secure_erase_trim(card)) { err = -EOPNOTSUPP; goto out; @@ -330,8 +324,6 @@ out: __blk_end_request(req, err, blk_rq_bytes(req)); spin_unlock_irq(&md->lock); - mmc_release_host(card->host); - return err ? 0 : 1; } @@ -402,8 +394,6 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req) (rq_data_dir(req) == WRITE) && REL_WRITES_SUPPORTED(card); - mmc_claim_host(card->host); - do { struct mmc_command cmd; u32 readcmd, writecmd, status = 0; @@ -589,8 +579,6 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req) spin_unlock_irq(&md->lock); } while (ret); - mmc_release_host(card->host); - return 1; cmd_err: @@ -617,8 +605,6 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req) spin_unlock_irq(&md->lock); } - mmc_release_host(card->host); - spin_lock_irq(&md->lock); while (ret) ret = __blk_end_request(req, -EIO, blk_rq_cur_bytes(req)); @@ -629,16 +615,25 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req) static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) { + int ret; + struct mmc_blk_data *md = mq->data; + struct mmc_card *card = md->queue.card; + + mmc_claim_host(card->host); + if (req->cmd_flags & REQ_DISCARD) { if (req->cmd_flags & REQ_SECURE) - return mmc_blk_issue_secdiscard_rq(mq, req); + ret = mmc_blk_issue_secdiscard_rq(mq, req); else - return mmc_blk_issue_discard_rq(mq, req); + ret = mmc_blk_issue_discard_rq(mq, req); } else if (req->cmd_flags & REQ_FLUSH) { - return mmc_blk_issue_flush(mq, req); + ret = mmc_blk_issue_flush(mq, req); } else { - return mmc_blk_issue_rw_rq(mq, req); + ret = mmc_blk_issue_rw_rq(mq, req); } + + mmc_release_host(card->host); + return ret; } static inline int mmc_blk_readonly(struct mmc_card *card) -- cgit v1.2.3 From 6fb1f8d4f93773dfd3ad31ee2607b0107b38cb35 Mon Sep 17 00:00:00 2001 From: Andrei Warkentin Date: Mon, 11 Apr 2011 18:10:25 -0500 Subject: mmc: MMC boot partitions support. Allows device MMC boot partitions to be accessed. MMC partitions are treated effectively as separate block devices on the same MMC card. Signed-off-by: Andrei Warkentin Acked-by: Arnd Bergmann Signed-off-by: Chris Ball --- Documentation/mmc/00-INDEX | 2 + Documentation/mmc/mmc-dev-attrs.txt | 10 ++ Documentation/mmc/mmc-dev-parts.txt | 27 ++++ drivers/mmc/card/block.c | 274 +++++++++++++++++++++++++++++++----- drivers/mmc/core/mmc.c | 22 ++- include/linux/mmc/card.h | 4 +- include/linux/mmc/mmc.h | 8 +- 7 files changed, 305 insertions(+), 42 deletions(-) create mode 100644 Documentation/mmc/mmc-dev-parts.txt diff --git a/Documentation/mmc/00-INDEX b/Documentation/mmc/00-INDEX index fca586f5b853..93dd7a714075 100644 --- a/Documentation/mmc/00-INDEX +++ b/Documentation/mmc/00-INDEX @@ -2,3 +2,5 @@ - this file mmc-dev-attrs.txt - info on SD and MMC device attributes +mmc-dev-parts.txt + - info on SD and MMC device partitions diff --git a/Documentation/mmc/mmc-dev-attrs.txt b/Documentation/mmc/mmc-dev-attrs.txt index ff2bd685bced..8898a95b41e5 100644 --- a/Documentation/mmc/mmc-dev-attrs.txt +++ b/Documentation/mmc/mmc-dev-attrs.txt @@ -1,3 +1,13 @@ +SD and MMC Block Device Attributes +================================== + +These attributes are defined for the block devices associated with the +SD or MMC device. + +The following attributes are read/write. + + force_ro Enforce read-only access even if write protect switch is off. + SD and MMC Device Attributes ============================ diff --git a/Documentation/mmc/mmc-dev-parts.txt b/Documentation/mmc/mmc-dev-parts.txt new file mode 100644 index 000000000000..2db28b8e662f --- /dev/null +++ b/Documentation/mmc/mmc-dev-parts.txt @@ -0,0 +1,27 @@ +SD and MMC Device Partitions +============================ + +Device partitions are additional logical block devices present on the +SD/MMC device. + +As of this writing, MMC boot partitions as supported and exposed as +/dev/mmcblkXboot0 and /dev/mmcblkXboot1, where X is the index of the +parent /dev/mmcblkX. + +MMC Boot Partitions +=================== + +Read and write access is provided to the two MMC boot partitions. Due to +the sensitive nature of the boot partition contents, which often store +a bootloader or bootloader configuration tables crucial to booting the +platform, write access is disabled by default to reduce the chance of +accidental bricking. + +To enable write access to /dev/mmcblkXbootY, disable the forced read-only +access with: + +echo 0 > /sys/block/mmcblkXbootY/force_ro + +To re-enable read-only access: + +echo 1 > /sys/block/mmcblkXbootY/force_ro diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 3e9082b729ff..1e6bd910e79e 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -76,9 +76,19 @@ struct mmc_blk_data { spinlock_t lock; struct gendisk *disk; struct mmc_queue queue; + struct list_head part; unsigned int usage; unsigned int read_only; + unsigned int part_type; + + /* + * Only set in main mmc_blk_data associated + * with mmc_card with mmc_set_drvdata, and keeps + * track of the current selected device partition. + */ + unsigned int part_curr; + struct device_attribute force_ro; }; static DEFINE_MUTEX(open_lock); @@ -101,17 +111,22 @@ static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk) return md; } +static inline int mmc_get_devidx(struct gendisk *disk) +{ + int devmaj = MAJOR(disk_devt(disk)); + int devidx = MINOR(disk_devt(disk)) / perdev_minors; + + if (!devmaj) + devidx = disk->first_minor / perdev_minors; + return devidx; +} + static void mmc_blk_put(struct mmc_blk_data *md) { mutex_lock(&open_lock); md->usage--; if (md->usage == 0) { - int devmaj = MAJOR(disk_devt(md->disk)); - int devidx = MINOR(disk_devt(md->disk)) / perdev_minors; - - if (!devmaj) - devidx = md->disk->first_minor / perdev_minors; - + int devidx = mmc_get_devidx(md->disk); blk_cleanup_queue(md->queue.queue); __clear_bit(devidx, dev_use); @@ -122,6 +137,38 @@ static void mmc_blk_put(struct mmc_blk_data *md) mutex_unlock(&open_lock); } +static ssize_t force_ro_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int ret; + struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev)); + + ret = snprintf(buf, PAGE_SIZE, "%d", + get_disk_ro(dev_to_disk(dev)) ^ + md->read_only); + mmc_blk_put(md); + return ret; +} + +static ssize_t force_ro_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + char *end; + struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev)); + unsigned long set = simple_strtoul(buf, &end, 0); + if (end == buf) { + ret = -EINVAL; + goto out; + } + + set_disk_ro(dev_to_disk(dev), set || md->read_only); + ret = count; +out: + mmc_blk_put(md); + return ret; +} + static int mmc_blk_open(struct block_device *bdev, fmode_t mode) { struct mmc_blk_data *md = mmc_blk_get(bdev->bd_disk); @@ -176,6 +223,29 @@ struct mmc_blk_request { struct mmc_data data; }; +static inline int mmc_blk_part_switch(struct mmc_card *card, + struct mmc_blk_data *md) +{ + int ret; + struct mmc_blk_data *main_md = mmc_get_drvdata(card); + if (main_md->part_curr == md->part_type) + return 0; + + if (mmc_card_mmc(card)) { + card->ext_csd.part_config &= ~EXT_CSD_PART_CONFIG_ACC_MASK; + card->ext_csd.part_config |= md->part_type; + + ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_PART_CONFIG, card->ext_csd.part_config, + card->ext_csd.part_time); + if (ret) + return ret; +} + + main_md->part_curr = md->part_type; + return 0; +} + static u32 mmc_sd_num_wr_blocks(struct mmc_card *card) { int err; @@ -620,6 +690,11 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) struct mmc_card *card = md->queue.card; mmc_claim_host(card->host); + ret = mmc_blk_part_switch(card, md); + if (ret) { + ret = 0; + goto out; + } if (req->cmd_flags & REQ_DISCARD) { if (req->cmd_flags & REQ_SECURE) @@ -632,6 +707,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) ret = mmc_blk_issue_rw_rq(mq, req); } +out: mmc_release_host(card->host); return ret; } @@ -642,7 +718,11 @@ static inline int mmc_blk_readonly(struct mmc_card *card) !(card->csd.cmdclass & CCC_BLOCK_WRITE); } -static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card) +static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card, + struct device *parent, + sector_t size, + bool default_ro, + const char *subname) { struct mmc_blk_data *md; int devidx, ret; @@ -658,7 +738,6 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card) goto out; } - /* * Set the read-only status based on the supported commands * and the write protect switch. @@ -672,6 +751,7 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card) } spin_lock_init(&md->lock); + INIT_LIST_HEAD(&md->part); md->usage = 1; ret = mmc_init_queue(&md->queue, card, &md->lock); @@ -686,8 +766,8 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card) md->disk->fops = &mmc_bdops; md->disk->private_data = md; md->disk->queue = md->queue.queue; - md->disk->driverfs_dev = &card->dev; - set_disk_ro(md->disk, md->read_only); + md->disk->driverfs_dev = parent; + set_disk_ro(md->disk, md->read_only || default_ro); if (REL_WRITES_SUPPORTED(card)) blk_queue_flush(md->queue.queue, REQ_FLUSH | REQ_FUA); @@ -703,33 +783,97 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card) * messages to tell when the card is present. */ - snprintf(md->disk->disk_name, sizeof(md->disk->disk_name), - "mmcblk%d", devidx); + if (subname) + snprintf(md->disk->disk_name, sizeof(md->disk->disk_name), + "mmcblk%d%s", + mmc_get_devidx(dev_to_disk(parent)), subname); + else + snprintf(md->disk->disk_name, sizeof(md->disk->disk_name), + "mmcblk%d", devidx); blk_queue_logical_block_size(md->queue.queue, 512); + set_capacity(md->disk, size); + return md; + + err_putdisk: + put_disk(md->disk); + err_kfree: + kfree(md); + out: + return ERR_PTR(ret); +} + +static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card) +{ + sector_t size; + struct mmc_blk_data *md; if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) { /* * The EXT_CSD sector count is in number or 512 byte * sectors. */ - set_capacity(md->disk, card->ext_csd.sectors); + size = card->ext_csd.sectors; } else { /* * The CSD capacity field is in units of read_blkbits. * set_capacity takes units of 512 bytes. */ - set_capacity(md->disk, - card->csd.capacity << (card->csd.read_blkbits - 9)); + size = card->csd.capacity << (card->csd.read_blkbits - 9); } + + md = mmc_blk_alloc_req(card, &card->dev, size, false, NULL); return md; +} - err_putdisk: - put_disk(md->disk); - err_kfree: - kfree(md); - out: - return ERR_PTR(ret); +static int mmc_blk_alloc_part(struct mmc_card *card, + struct mmc_blk_data *md, + unsigned int part_type, + sector_t size, + bool default_ro, + const char *subname) +{ + char cap_str[10]; + struct mmc_blk_data *part_md; + + part_md = mmc_blk_alloc_req(card, disk_to_dev(md->disk), size, default_ro, + subname); + if (IS_ERR(part_md)) + return PTR_ERR(part_md); + part_md->part_type = part_type; + list_add(&part_md->part, &md->part); + + string_get_size((u64)get_capacity(part_md->disk) << 9, STRING_UNITS_2, + cap_str, sizeof(cap_str)); + printk(KERN_INFO "%s: %s %s partition %u %s\n", + part_md->disk->disk_name, mmc_card_id(card), + mmc_card_name(card), part_md->part_type, cap_str); + return 0; +} + +static int mmc_blk_alloc_parts(struct mmc_card *card, struct mmc_blk_data *md) +{ + int ret = 0; + + if (!mmc_card_mmc(card)) + return 0; + + if (card->ext_csd.boot_size) { + ret = mmc_blk_alloc_part(card, md, EXT_CSD_PART_CONFIG_ACC_BOOT0, + card->ext_csd.boot_size >> 9, + true, + "boot0"); + if (ret) + return ret; + ret = mmc_blk_alloc_part(card, md, EXT_CSD_PART_CONFIG_ACC_BOOT1, + card->ext_csd.boot_size >> 9, + true, + "boot1"); + if (ret) + return ret; + } + + return ret; } static int @@ -750,9 +894,54 @@ mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card) return 0; } +static void mmc_blk_remove_req(struct mmc_blk_data *md) +{ + if (md) { + if (md->disk->flags & GENHD_FL_UP) { + device_remove_file(disk_to_dev(md->disk), &md->force_ro); + + /* Stop new requests from getting into the queue */ + del_gendisk(md->disk); + } + + /* Then flush out any already in there */ + mmc_cleanup_queue(&md->queue); + mmc_blk_put(md); + } +} + +static void mmc_blk_remove_parts(struct mmc_card *card, + struct mmc_blk_data *md) +{ + struct list_head *pos, *q; + struct mmc_blk_data *part_md; + + list_for_each_safe(pos, q, &md->part) { + part_md = list_entry(pos, struct mmc_blk_data, part); + list_del(pos); + mmc_blk_remove_req(part_md); + } +} + +static int mmc_add_disk(struct mmc_blk_data *md) +{ + int ret; + + add_disk(md->disk); + md->force_ro.show = force_ro_show; + md->force_ro.store = force_ro_store; + md->force_ro.attr.name = "force_ro"; + md->force_ro.attr.mode = S_IRUGO | S_IWUSR; + ret = device_create_file(disk_to_dev(md->disk), &md->force_ro); + if (ret) + del_gendisk(md->disk); + + return ret; +} + static int mmc_blk_probe(struct mmc_card *card) { - struct mmc_blk_data *md; + struct mmc_blk_data *md, *part_md; int err; char cap_str[10]; @@ -776,14 +965,22 @@ static int mmc_blk_probe(struct mmc_card *card) md->disk->disk_name, mmc_card_id(card), mmc_card_name(card), cap_str, md->read_only ? "(ro)" : ""); + if (mmc_blk_alloc_parts(card, md)) + goto out; + mmc_set_drvdata(card, md); - add_disk(md->disk); + if (mmc_add_disk(md)) + goto out; + + list_for_each_entry(part_md, &md->part, part) { + if (mmc_add_disk(part_md)) + goto out; + } return 0; out: - mmc_cleanup_queue(&md->queue); - mmc_blk_put(md); - + mmc_blk_remove_parts(card, md); + mmc_blk_remove_req(md); return err; } @@ -791,36 +988,43 @@ static void mmc_blk_remove(struct mmc_card *card) { struct mmc_blk_data *md = mmc_get_drvdata(card); - if (md) { - /* Stop new requests from getting into the queue */ - del_gendisk(md->disk); - - /* Then flush out any already in there */ - mmc_cleanup_queue(&md->queue); - - mmc_blk_put(md); - } + mmc_blk_remove_parts(card, md); + mmc_blk_remove_req(md); mmc_set_drvdata(card, NULL); } #ifdef CONFIG_PM static int mmc_blk_suspend(struct mmc_card *card, pm_message_t state) { + struct mmc_blk_data *part_md; struct mmc_blk_data *md = mmc_get_drvdata(card); if (md) { mmc_queue_suspend(&md->queue); + list_for_each_entry(part_md, &md->part, part) { + mmc_queue_suspend(&part_md->queue); + } } return 0; } static int mmc_blk_resume(struct mmc_card *card) { + struct mmc_blk_data *part_md; struct mmc_blk_data *md = mmc_get_drvdata(card); if (md) { mmc_blk_set_blksize(md, card); + + /* + * Resume involves the card going into idle state, + * so current partition is always the main one. + */ + md->part_curr = md->part_type; mmc_queue_resume(&md->queue); + list_for_each_entry(part_md, &md->part, part) { + mmc_queue_resume(&part_md->queue); + } } return 0; } diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index a5cf42345026..ba1c8788f847 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -288,7 +288,10 @@ static int mmc_read_ext_csd(struct mmc_card *card) if (card->ext_csd.rev >= 3) { u8 sa_shift = ext_csd[EXT_CSD_S_A_TIMEOUT]; - card->ext_csd.bootconfig = ext_csd[EXT_CSD_BOOT_CONFIG]; + card->ext_csd.part_config = ext_csd[EXT_CSD_PART_CONFIG]; + + /* EXT_CSD value is in units of 10ms, but we store in ms */ + card->ext_csd.part_time = 10 * ext_csd[EXT_CSD_PART_SWITCH_TIME]; /* Sleep / awake timeout in 100ns units */ if (sa_shift > 0 && sa_shift <= 0x17) @@ -302,6 +305,12 @@ static int mmc_read_ext_csd(struct mmc_card *card) ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] << 10; card->ext_csd.rel_sectors = ext_csd[EXT_CSD_REL_WR_SEC_C]; + + /* + * There are two boot regions of equal size, defined in + * multiples of 128K. + */ + card->ext_csd.boot_size = ext_csd[EXT_CSD_BOOT_MULT] << 17; } if (card->ext_csd.rev >= 4) { @@ -576,10 +585,13 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, /* * Ensure eMMC user default partition is enabled */ - if (card->ext_csd.bootconfig & 0x7) { - card->ext_csd.bootconfig &= ~0x7; - mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BOOT_CONFIG, - card->ext_csd.bootconfig, 0); + if (card->ext_csd.part_config & EXT_CSD_PART_CONFIG_ACC_MASK) { + card->ext_csd.part_config &= ~EXT_CSD_PART_CONFIG_ACC_MASK; + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONFIG, + card->ext_csd.part_config, + card->ext_csd.part_time); + if (err && err != -EBADMSG) + goto free_card; } /* diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 937f852cf01e..0c7a58b14343 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -48,7 +48,8 @@ struct mmc_ext_csd { u8 sec_feature_support; u8 rel_sectors; u8 rel_param; - u8 bootconfig; + u8 part_config; + unsigned int part_time; /* Units: ms */ unsigned int sa_timeout; /* Units: 100ns */ unsigned int hs_max_dtr; unsigned int sectors; @@ -61,6 +62,7 @@ struct mmc_ext_csd { bool enhanced_area_en; /* enable bit */ unsigned long long enhanced_area_offset; /* Units: Byte */ unsigned int enhanced_area_size; /* Units: KB */ + unsigned int boot_size; /* in bytes */ }; struct sd_scr { diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index 390aa6eef676..373b2bf5e5b5 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -257,19 +257,21 @@ struct _mmc_csd { #define EXT_CSD_PARTITION_SUPPORT 160 /* RO */ #define EXT_CSD_WR_REL_PARAM 166 /* RO */ #define EXT_CSD_ERASE_GROUP_DEF 175 /* R/W */ -#define EXT_CSD_BOOT_CONFIG 179 /* R/W */ +#define EXT_CSD_PART_CONFIG 179 /* R/W */ #define EXT_CSD_ERASED_MEM_CONT 181 /* RO */ #define EXT_CSD_BUS_WIDTH 183 /* R/W */ #define EXT_CSD_HS_TIMING 185 /* R/W */ #define EXT_CSD_REV 192 /* RO */ #define EXT_CSD_STRUCTURE 194 /* RO */ #define EXT_CSD_CARD_TYPE 196 /* RO */ +#define EXT_CSD_PART_SWITCH_TIME 199 /* RO */ #define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */ #define EXT_CSD_S_A_TIMEOUT 217 /* RO */ #define EXT_CSD_REL_WR_SEC_C 222 /* RO */ #define EXT_CSD_HC_WP_GRP_SIZE 221 /* RO */ #define EXT_CSD_ERASE_TIMEOUT_MULT 223 /* RO */ #define EXT_CSD_HC_ERASE_GRP_SIZE 224 /* RO */ +#define EXT_CSD_BOOT_MULT 226 /* RO */ #define EXT_CSD_SEC_TRIM_MULT 229 /* RO */ #define EXT_CSD_SEC_ERASE_MULT 230 /* RO */ #define EXT_CSD_SEC_FEATURE_SUPPORT 231 /* RO */ @@ -281,6 +283,10 @@ struct _mmc_csd { #define EXT_CSD_WR_REL_PARAM_EN (1<<2) +#define EXT_CSD_PART_CONFIG_ACC_MASK (0x7) +#define EXT_CSD_PART_CONFIG_ACC_BOOT0 (0x1) +#define EXT_CSD_PART_CONFIG_ACC_BOOT1 (0x2) + #define EXT_CSD_CMD_SET_NORMAL (1<<0) #define EXT_CSD_CMD_SET_SECURE (1<<1) #define EXT_CSD_CMD_SET_CPSECURE (1<<2) -- cgit v1.2.3 From f5e5f1e35feaf21bf50ed6b6fc2823097e00fd20 Mon Sep 17 00:00:00 2001 From: Andrei Warkentin Date: Mon, 11 Apr 2011 19:11:04 -0400 Subject: mmc: quirks: Support for block quirks. Block quirks implemented using core/quirks.c support. Signed-off-by: Andrei Warkentin Signed-off-by: Chris Ball --- drivers/mmc/card/block.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 1e6bd910e79e..288d27394ef9 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -939,6 +939,11 @@ static int mmc_add_disk(struct mmc_blk_data *md) return ret; } +static const struct mmc_fixup blk_fixups[] = +{ + END_FIXUP +}; + static int mmc_blk_probe(struct mmc_card *card) { struct mmc_blk_data *md, *part_md; @@ -969,6 +974,8 @@ static int mmc_blk_probe(struct mmc_card *card) goto out; mmc_set_drvdata(card, md); + mmc_fixup_device(card, blk_fixups); + if (mmc_add_disk(md)) goto out; -- cgit v1.2.3 From a08ed287ca48ae8338d5dbc15fcbd4e82b055409 Mon Sep 17 00:00:00 2001 From: Mikko Vinni Date: Tue, 12 Apr 2011 09:36:18 -0400 Subject: mmc: sdhci: work around broken dma boundary behavior Some SD host controllers (noticed on an integrated JMicron SD reader on an HP Pavilion dv5-1250eo laptop) don't update the dma address register before signaling a dma interrupt due to a dma boundary. Update the register manually to the next boundary (by default 512KiB), at which the transfer stopped. As long as each transfer is at most 512KiB in size (guaranteed by a BUG_ON in sdhci_prepare_data()) and the boundary is kept at the default value, this fix is needed at most once per transfer. Smaller boundaries are taken care of by counting the transferred bytes. Fixes: https://bugzilla.kernel.org/show_bug.cgi?id=28462 Signed-off-by: Mikko Vinni Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 30 +++++++++++++++++++++++++----- drivers/mmc/host/sdhci.h | 6 ++++++ 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 36a0063c7a2c..94793f2256f1 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -679,6 +679,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) host->data = data; host->data_early = 0; + host->data->bytes_xfered = 0; if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) host->flags |= SDHCI_REQ_USE_DMA; @@ -814,8 +815,9 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) sdhci_set_transfer_irqs(host); - /* We do not handle DMA boundaries, so set it to max (512 KiB) */ - sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, data->blksz), SDHCI_BLOCK_SIZE); + /* Set the DMA boundary value and block size */ + sdhci_writew(host, SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG, + data->blksz), SDHCI_BLOCK_SIZE); sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT); } @@ -1551,10 +1553,28 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) * We currently don't do anything fancy with DMA * boundaries, but as we can't disable the feature * we need to at least restart the transfer. + * + * According to the spec sdhci_readl(host, SDHCI_DMA_ADDRESS) + * should return a valid address to continue from, but as + * some controllers are faulty, don't trust them. */ - if (intmask & SDHCI_INT_DMA_END) - sdhci_writel(host, sdhci_readl(host, SDHCI_DMA_ADDRESS), - SDHCI_DMA_ADDRESS); + if (intmask & SDHCI_INT_DMA_END) { + u32 dmastart, dmanow; + dmastart = sg_dma_address(host->data->sg); + dmanow = dmastart + host->data->bytes_xfered; + /* + * Force update to the next DMA block boundary. + */ + dmanow = (dmanow & + ~(SDHCI_DEFAULT_BOUNDARY_SIZE - 1)) + + SDHCI_DEFAULT_BOUNDARY_SIZE; + host->data->bytes_xfered = dmanow - dmastart; + DBG("%s: DMA base 0x%08x, transferred 0x%06x bytes," + " next 0x%08x\n", + mmc_hostname(host->mmc), dmastart, + host->data->bytes_xfered, dmanow); + sdhci_writel(host, dmanow, SDHCI_DMA_ADDRESS); + } if (intmask & SDHCI_INT_DATA_END) { if (host->cmd) { diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 25e8bde600d1..85750a94c612 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -202,6 +202,12 @@ #define SDHCI_MAX_DIV_SPEC_200 256 #define SDHCI_MAX_DIV_SPEC_300 2046 +/* + * Host SDMA buffer boundary. Valid values from 4K to 512K in powers of 2. + */ +#define SDHCI_DEFAULT_BOUNDARY_SIZE (512 * 1024) +#define SDHCI_DEFAULT_BOUNDARY_ARG (ilog2(SDHCI_DEFAULT_BOUNDARY_SIZE) - 12) + struct sdhci_ops { #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS u32 (*read_l)(struct sdhci_host *host, int reg); -- cgit v1.2.3 From 48be56e727db24d4790ca1a4c5a083cdba582232 Mon Sep 17 00:00:00 2001 From: Andrei Warkentin Date: Tue, 12 Apr 2011 15:06:53 -0500 Subject: mmc: quirks: Fix erase/trim for certain SanDisk cards. CMD38 argument is passed through EXT_CSD[113]. Signed-off-by: Andrei Warkentin Signed-off-by: Chris Ball --- drivers/mmc/card/block.c | 43 ++++++++++++++++++++++++++++++++++++++++++- include/linux/mmc/card.h | 1 + 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 288d27394ef9..c20995323348 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -48,6 +48,13 @@ MODULE_ALIAS("mmc:block"); #endif #define MODULE_PARAM_PREFIX "mmcblk." +#define INAND_CMD38_ARG_EXT_CSD 113 +#define INAND_CMD38_ARG_ERASE 0x00 +#define INAND_CMD38_ARG_TRIM 0x01 +#define INAND_CMD38_ARG_SECERASE 0x80 +#define INAND_CMD38_ARG_SECTRIM1 0x81 +#define INAND_CMD38_ARG_SECTRIM2 0x88 + #define REL_WRITES_SUPPORTED(card) (mmc_card_mmc((card)) && \ (((card)->ext_csd.rel_param & EXT_CSD_WR_REL_PARAM_EN) || \ ((card)->ext_csd.rel_sectors))) @@ -356,6 +363,16 @@ static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req) else arg = MMC_ERASE_ARG; + if (card->quirks & MMC_QUIRK_INAND_CMD38) { + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + INAND_CMD38_ARG_EXT_CSD, + arg == MMC_TRIM_ARG ? + INAND_CMD38_ARG_TRIM : + INAND_CMD38_ARG_ERASE, + 0); + if (err) + goto out; + } err = mmc_erase(card, from, nr, arg); out: spin_lock_irq(&md->lock); @@ -386,9 +403,28 @@ static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq, else arg = MMC_SECURE_ERASE_ARG; + if (card->quirks & MMC_QUIRK_INAND_CMD38) { + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + INAND_CMD38_ARG_EXT_CSD, + arg == MMC_SECURE_TRIM1_ARG ? + INAND_CMD38_ARG_SECTRIM1 : + INAND_CMD38_ARG_SECERASE, + 0); + if (err) + goto out; + } err = mmc_erase(card, from, nr, arg); - if (!err && arg == MMC_SECURE_TRIM1_ARG) + if (!err && arg == MMC_SECURE_TRIM1_ARG) { + if (card->quirks & MMC_QUIRK_INAND_CMD38) { + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + INAND_CMD38_ARG_EXT_CSD, + INAND_CMD38_ARG_SECTRIM2, + 0); + if (err) + goto out; + } err = mmc_erase(card, from, nr, MMC_SECURE_TRIM2_ARG); + } out: spin_lock_irq(&md->lock); __blk_end_request(req, err, blk_rq_bytes(req)); @@ -941,6 +977,11 @@ static int mmc_add_disk(struct mmc_blk_data *md) static const struct mmc_fixup blk_fixups[] = { + MMC_FIXUP("SEM02G", 0x2, 0x100, add_quirk, MMC_QUIRK_INAND_CMD38), + MMC_FIXUP("SEM04G", 0x2, 0x100, add_quirk, MMC_QUIRK_INAND_CMD38), + MMC_FIXUP("SEM08G", 0x2, 0x100, add_quirk, MMC_QUIRK_INAND_CMD38), + MMC_FIXUP("SEM16G", 0x2, 0x100, add_quirk, MMC_QUIRK_INAND_CMD38), + MMC_FIXUP("SEM32G", 0x2, 0x100, add_quirk, MMC_QUIRK_INAND_CMD38), END_FIXUP }; diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 0c7a58b14343..72a98681ef47 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -133,6 +133,7 @@ struct mmc_card { #define MMC_QUIRK_BROKEN_CLK_GATING (1<<3) /* clock gating the sdio bus will make card fail */ #define MMC_QUIRK_NONSTD_FUNC_IF (1<<4) /* SDIO card has nonstd function interfaces */ #define MMC_QUIRK_DISABLE_CD (1<<5) /* disconnect CD/DAT[3] resistor */ +#define MMC_QUIRK_INAND_CMD38 (1<<6) /* iNAND devices have broken CMD38 */ unsigned int erase_size; /* erase size in sectors */ unsigned int erase_shift; /* if erase unit is power 2 */ -- cgit v1.2.3 From a1359e1839fcbb3b0b59267b7127d1d0b325e375 Mon Sep 17 00:00:00 2001 From: Andrei Warkentin Date: Wed, 13 Apr 2011 15:33:12 -0500 Subject: mmc: core: Fix use of uninitialized data in mmc_cmd_app. mmc_cmd_app did not zero out mmc_command on stack. Reported-by: Chuanxiao Dong Signed-off-by: Andrei Warkentin Signed-off-by: Chris Ball --- drivers/mmc/core/sd_ops.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c index 76af349c14b4..71fdb07fd8de 100644 --- a/drivers/mmc/core/sd_ops.c +++ b/drivers/mmc/core/sd_ops.c @@ -29,6 +29,8 @@ static int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card) BUG_ON(!host); BUG_ON(card && (card->host != host)); + memset(&cmd, 0, sizeof(struct mmc_command)); + cmd.opcode = MMC_APP_CMD; if (card) { -- cgit v1.2.3 From 41378e4cd511baad668e9f7f968f6fe3bc9da3d1 Mon Sep 17 00:00:00 2001 From: Chris Ball Date: Wed, 13 Apr 2011 16:25:30 -0400 Subject: mmc: core: Fix use of uninitialized data in mmc_send_if_cond. mmc_send_if_cond did not zero out mmc_command on stack. Signed-off-by: Chris Ball --- drivers/mmc/core/sd_ops.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c index 71fdb07fd8de..da508497a6e0 100644 --- a/drivers/mmc/core/sd_ops.c +++ b/drivers/mmc/core/sd_ops.c @@ -201,6 +201,8 @@ int mmc_send_if_cond(struct mmc_host *host, u32 ocr) static const u8 test_pattern = 0xAA; u8 result_pattern; + memset(&cmd, 0, sizeof(struct mmc_command)); + /* * To support SD 2.0 cards, we must always invoke SD_SEND_IF_COND * before SD_APP_OP_COND. This command will harmlessly fail for -- cgit v1.2.3 From 9cb9f995d19fb3ba0d9570e0586434ede303380d Mon Sep 17 00:00:00 2001 From: Chris Ball Date: Wed, 13 Apr 2011 16:28:34 -0400 Subject: mmc: card: Fix use of uninitialized data in mmc_blk_issue_rw_rq. mmc_blk_issue_rw_rq did not zero out mmc_command on stack. Signed-off-by: Chris Ball --- drivers/mmc/card/block.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index c20995323348..9e30cf6db53c 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -504,6 +504,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req) struct mmc_command cmd; u32 readcmd, writecmd, status = 0; + memset(&cmd, 0, sizeof(struct mmc_command)); memset(&brq, 0, sizeof(struct mmc_blk_request)); brq.mrq.cmd = &brq.cmd; brq.mrq.data = &brq.data; -- cgit v1.2.3