From 41e2a4893566ced3c46af15df5b727326881e47d 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 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/mmc/core') diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 772d0d0a541b..5c611a6e0080 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) */ -- cgit v1.2.3 From f4c5522b0a8827f39f83f928961d87e081bfe71c 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/core/mmc.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/mmc/core') diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 5c611a6e0080..ae6b8fd38800 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 -- cgit v1.2.3 From a5e9425d2010978c5f85986cc70a9fa0c0d5b912 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 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/mmc/core') 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); -- cgit v1.2.3 From 6b93d01fe5971951911a070f51f412d50e9536dc 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 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/mmc/core') 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) { -- cgit v1.2.3 From eab4068795d670b065164096805cbf15a19e9690 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 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/mmc/core') 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) -- cgit v1.2.3 From 0b4043d70af5871908864fa725821bc8e667542d 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(+) (limited to 'drivers/mmc/core') 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 2059a02dcb84236f9db9197fa9b00418d7b8465b 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 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc/core') 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); -- cgit v1.2.3 From 14e7dc76f24577694f1c121e6ce1fd2f3d46c726 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(+) (limited to 'drivers/mmc/core') 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 f317dfeb86c83d03304a74ce5426a69422b79547 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(-) (limited to 'drivers/mmc/core') diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 2b200c1cfbba..6714ea4e46ea 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -324,12 +324,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 32780cd1350e651e68bdf33b7f5b009d21d5b794 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 +- 3 files changed, 43 insertions(+), 54 deletions(-) (limited to 'drivers/mmc/core') 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); -- cgit v1.2.3 From eaa02f751ff4f8abfc2e55a15c20a5a274244418 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 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) (limited to 'drivers/mmc/core') 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", -- cgit v1.2.3 From d3a8d95dcbb726b9cf0bbc166b2473bdd236c88c 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 - 3 files changed, 23 insertions(+), 8 deletions(-) (limited to 'drivers/mmc/core') diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index ae6b8fd38800..396cb23625d2 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 f3b22bf89cc9..a2bae625332a 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); -- cgit v1.2.3 From 371a689f64b0da140c3bcd3f55305ffa1c3a58ef 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 --- drivers/mmc/core/mmc.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) (limited to 'drivers/mmc/core') diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 396cb23625d2..a2c795e8f9dd 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; } /* -- cgit v1.2.3 From 87f6119e25bbe0fd60e76f599d5fa10cf489519c 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(+) (limited to 'drivers/mmc/core') 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 30574ed143e10dd19295b7d80be55f2cd6764c6f 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(+) (limited to 'drivers/mmc/core') 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 1278dba167f01bb3c6626d16450d31129d041087 Mon Sep 17 00:00:00 2001 From: Chris Ball Date: Wed, 13 Apr 2011 23:40:30 -0400 Subject: mmc: initialize struct mmc_command at declaration time Converts from: struct mmc_command cmd; memset(&cmd, 0, sizeof(struct mmc_command)); to: struct mmc_command cmd = {0}; because it's shorter, as performant, and easier to work out whether initialization has happened. Signed-off-by: Chris Ball --- drivers/mmc/core/core.c | 6 ++---- drivers/mmc/core/mmc_ops.c | 50 ++++++++++++--------------------------------- drivers/mmc/core/sd_ops.c | 29 ++++++++------------------ drivers/mmc/core/sdio_ops.c | 11 +++------- 4 files changed, 26 insertions(+), 70 deletions(-) (limited to 'drivers/mmc/core') diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 5178d5daa5f4..f273ffd6557a 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1286,7 +1286,7 @@ static unsigned int mmc_erase_timeout(struct mmc_card *card, static int mmc_do_erase(struct mmc_card *card, unsigned int from, unsigned int to, unsigned int arg) { - struct mmc_command cmd; + struct mmc_command cmd = {0}; unsigned int qty = 0; int err; @@ -1320,7 +1320,6 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from, to <<= 9; } - memset(&cmd, 0, sizeof(struct mmc_command)); if (mmc_card_sd(card)) cmd.opcode = SD_ERASE_WR_BLK_START; else @@ -1490,12 +1489,11 @@ EXPORT_SYMBOL(mmc_erase_group_aligned); int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen) { - struct mmc_command cmd; + struct mmc_command cmd = {0}; if (mmc_card_blockaddr(card) || mmc_card_ddr_mode(card)) return 0; - memset(&cmd, 0, sizeof(struct mmc_command)); cmd.opcode = MMC_SET_BLOCKLEN; cmd.arg = blocklen; cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC; diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index a2bae625332a..de4e1855b8ed 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -23,12 +23,10 @@ static int _mmc_select_card(struct mmc_host *host, struct mmc_card *card) { int err; - struct mmc_command cmd; + struct mmc_command cmd = {0}; BUG_ON(!host); - memset(&cmd, 0, sizeof(struct mmc_command)); - cmd.opcode = MMC_SELECT_CARD; if (card) { @@ -60,15 +58,13 @@ int mmc_deselect_cards(struct mmc_host *host) int mmc_card_sleepawake(struct mmc_host *host, int sleep) { - struct mmc_command cmd; + struct mmc_command cmd = {0}; struct mmc_card *card = host->card; int err; if (sleep) mmc_deselect_cards(host); - memset(&cmd, 0, sizeof(struct mmc_command)); - cmd.opcode = MMC_SLEEP_AWAKE; cmd.arg = card->rca << 16; if (sleep) @@ -97,7 +93,7 @@ int mmc_card_sleepawake(struct mmc_host *host, int sleep) int mmc_go_idle(struct mmc_host *host) { int err; - struct mmc_command cmd; + struct mmc_command cmd = {0}; /* * Non-SPI hosts need to prevent chipselect going active during @@ -113,8 +109,6 @@ int mmc_go_idle(struct mmc_host *host) mmc_delay(1); } - memset(&cmd, 0, sizeof(struct mmc_command)); - cmd.opcode = MMC_GO_IDLE_STATE; cmd.arg = 0; cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_NONE | MMC_CMD_BC; @@ -135,13 +129,11 @@ int mmc_go_idle(struct mmc_host *host) int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) { - struct mmc_command cmd; + struct mmc_command cmd = {0}; int i, err = 0; BUG_ON(!host); - memset(&cmd, 0, sizeof(struct mmc_command)); - cmd.opcode = MMC_SEND_OP_COND; cmd.arg = mmc_host_is_spi(host) ? 0 : ocr; cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR; @@ -178,13 +170,11 @@ int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) int mmc_all_send_cid(struct mmc_host *host, u32 *cid) { int err; - struct mmc_command cmd; + struct mmc_command cmd = {0}; BUG_ON(!host); BUG_ON(!cid); - memset(&cmd, 0, sizeof(struct mmc_command)); - cmd.opcode = MMC_ALL_SEND_CID; cmd.arg = 0; cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR; @@ -201,13 +191,11 @@ int mmc_all_send_cid(struct mmc_host *host, u32 *cid) int mmc_set_relative_addr(struct mmc_card *card) { int err; - struct mmc_command cmd; + struct mmc_command cmd = {0}; BUG_ON(!card); BUG_ON(!card->host); - memset(&cmd, 0, sizeof(struct mmc_command)); - cmd.opcode = MMC_SET_RELATIVE_ADDR; cmd.arg = card->rca << 16; cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; @@ -223,13 +211,11 @@ static int mmc_send_cxd_native(struct mmc_host *host, u32 arg, u32 *cxd, int opcode) { int err; - struct mmc_command cmd; + struct mmc_command cmd = {0}; BUG_ON(!host); BUG_ON(!cxd); - memset(&cmd, 0, sizeof(struct mmc_command)); - cmd.opcode = opcode; cmd.arg = arg; cmd.flags = MMC_RSP_R2 | MMC_CMD_AC; @@ -248,7 +234,7 @@ mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host, u32 opcode, void *buf, unsigned len) { struct mmc_request mrq; - struct mmc_command cmd; + struct mmc_command cmd = {0}; struct mmc_data data; struct scatterlist sg; void *data_buf; @@ -261,7 +247,6 @@ mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host, return -ENOMEM; memset(&mrq, 0, sizeof(struct mmc_request)); - memset(&cmd, 0, sizeof(struct mmc_command)); memset(&data, 0, sizeof(struct mmc_data)); mrq.cmd = &cmd; @@ -355,11 +340,9 @@ int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd) int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp) { - struct mmc_command cmd; + struct mmc_command cmd = {0}; int err; - memset(&cmd, 0, sizeof(struct mmc_command)); - cmd.opcode = MMC_SPI_READ_OCR; cmd.arg = highcap ? (1 << 30) : 0; cmd.flags = MMC_RSP_SPI_R3; @@ -372,11 +355,9 @@ 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) { - struct mmc_command cmd; + struct mmc_command cmd = {0}; int err; - memset(&cmd, 0, sizeof(struct mmc_command)); - cmd.opcode = MMC_SPI_CRC_ON_OFF; cmd.flags = MMC_RSP_SPI_R1; cmd.arg = use_crc; @@ -402,14 +383,12 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, unsigned int timeout_ms) { int err; - struct mmc_command cmd; + struct mmc_command cmd = {0}; u32 status; BUG_ON(!card); BUG_ON(!card->host); - memset(&cmd, 0, sizeof(struct mmc_command)); - cmd.opcode = MMC_SWITCH; cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | (index << 16) | @@ -451,13 +430,11 @@ EXPORT_SYMBOL_GPL(mmc_switch); int mmc_send_status(struct mmc_card *card, u32 *status) { int err; - struct mmc_command cmd; + struct mmc_command cmd = {0}; BUG_ON(!card); BUG_ON(!card->host); - memset(&cmd, 0, sizeof(struct mmc_command)); - cmd.opcode = MMC_SEND_STATUS; if (!mmc_host_is_spi(card->host)) cmd.arg = card->rca << 16; @@ -481,7 +458,7 @@ mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode, u8 len) { struct mmc_request mrq; - struct mmc_command cmd; + struct mmc_command cmd = {0}; struct mmc_data data; struct scatterlist sg; u8 *data_buf; @@ -512,7 +489,6 @@ mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode, memcpy(data_buf, test_buf, len); memset(&mrq, 0, sizeof(struct mmc_request)); - memset(&cmd, 0, sizeof(struct mmc_command)); memset(&data, 0, sizeof(struct mmc_data)); mrq.cmd = &cmd; diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c index da508497a6e0..dfe9a9c3f958 100644 --- a/drivers/mmc/core/sd_ops.c +++ b/drivers/mmc/core/sd_ops.c @@ -24,13 +24,11 @@ static int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card) { int err; - struct mmc_command cmd; + struct mmc_command cmd = {0}; BUG_ON(!host); BUG_ON(card && (card->host != host)); - memset(&cmd, 0, sizeof(struct mmc_command)); - cmd.opcode = MMC_APP_CMD; if (card) { @@ -121,13 +119,11 @@ EXPORT_SYMBOL(mmc_wait_for_app_cmd); int mmc_app_set_bus_width(struct mmc_card *card, int width) { int err; - struct mmc_command cmd; + struct mmc_command cmd = {0}; BUG_ON(!card); BUG_ON(!card->host); - memset(&cmd, 0, sizeof(struct mmc_command)); - cmd.opcode = SD_APP_SET_BUS_WIDTH; cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; @@ -151,13 +147,11 @@ int mmc_app_set_bus_width(struct mmc_card *card, int width) int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) { - struct mmc_command cmd; + struct mmc_command cmd = {0}; int i, err = 0; BUG_ON(!host); - memset(&cmd, 0, sizeof(struct mmc_command)); - cmd.opcode = SD_APP_OP_COND; if (mmc_host_is_spi(host)) cmd.arg = ocr & (1 << 30); /* SPI only defines one bit */ @@ -196,13 +190,11 @@ int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) int mmc_send_if_cond(struct mmc_host *host, u32 ocr) { - struct mmc_command cmd; + struct mmc_command cmd = {0}; int err; 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 @@ -230,13 +222,11 @@ int mmc_send_if_cond(struct mmc_host *host, u32 ocr) int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca) { int err; - struct mmc_command cmd; + struct mmc_command cmd = {0}; BUG_ON(!host); BUG_ON(!rca); - memset(&cmd, 0, sizeof(struct mmc_command)); - cmd.opcode = SD_SEND_RELATIVE_ADDR; cmd.arg = 0; cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR; @@ -254,7 +244,7 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr) { int err; struct mmc_request mrq; - struct mmc_command cmd; + struct mmc_command cmd = {0}; struct mmc_data data; struct scatterlist sg; void *data_buf; @@ -277,7 +267,6 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr) return -ENOMEM; memset(&mrq, 0, sizeof(struct mmc_request)); - memset(&cmd, 0, sizeof(struct mmc_command)); memset(&data, 0, sizeof(struct mmc_data)); mrq.cmd = &cmd; @@ -317,7 +306,7 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group, u8 value, u8 *resp) { struct mmc_request mrq; - struct mmc_command cmd; + struct mmc_command cmd = {0}; struct mmc_data data; struct scatterlist sg; @@ -330,7 +319,6 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group, value &= 0xF; memset(&mrq, 0, sizeof(struct mmc_request)); - memset(&cmd, 0, sizeof(struct mmc_command)); memset(&data, 0, sizeof(struct mmc_data)); mrq.cmd = &cmd; @@ -366,7 +354,7 @@ int mmc_app_sd_status(struct mmc_card *card, void *ssr) { int err; struct mmc_request mrq; - struct mmc_command cmd; + struct mmc_command cmd = {0}; struct mmc_data data; struct scatterlist sg; @@ -381,7 +369,6 @@ int mmc_app_sd_status(struct mmc_card *card, void *ssr) return err; memset(&mrq, 0, sizeof(struct mmc_request)); - memset(&cmd, 0, sizeof(struct mmc_command)); memset(&data, 0, sizeof(struct mmc_data)); mrq.cmd = &cmd; diff --git a/drivers/mmc/core/sdio_ops.c b/drivers/mmc/core/sdio_ops.c index dea36d9c22e6..f77b1bc318ed 100644 --- a/drivers/mmc/core/sdio_ops.c +++ b/drivers/mmc/core/sdio_ops.c @@ -21,13 +21,11 @@ int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) { - struct mmc_command cmd; + struct mmc_command cmd = {0}; int i, err = 0; BUG_ON(!host); - memset(&cmd, 0, sizeof(struct mmc_command)); - cmd.opcode = SD_IO_SEND_OP_COND; cmd.arg = ocr; cmd.flags = MMC_RSP_SPI_R4 | MMC_RSP_R4 | MMC_CMD_BCR; @@ -70,7 +68,7 @@ int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) static int mmc_io_rw_direct_host(struct mmc_host *host, int write, unsigned fn, unsigned addr, u8 in, u8 *out) { - struct mmc_command cmd; + struct mmc_command cmd = {0}; int err; BUG_ON(!host); @@ -80,8 +78,6 @@ static int mmc_io_rw_direct_host(struct mmc_host *host, int write, unsigned fn, if (addr & ~0x1FFFF) return -EINVAL; - memset(&cmd, 0, sizeof(struct mmc_command)); - cmd.opcode = SD_IO_RW_DIRECT; cmd.arg = write ? 0x80000000 : 0x00000000; cmd.arg |= fn << 28; @@ -126,7 +122,7 @@ int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn, unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz) { struct mmc_request mrq; - struct mmc_command cmd; + struct mmc_command cmd = {0}; struct mmc_data data; struct scatterlist sg; @@ -141,7 +137,6 @@ int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn, return -EINVAL; memset(&mrq, 0, sizeof(struct mmc_request)); - memset(&cmd, 0, sizeof(struct mmc_command)); memset(&data, 0, sizeof(struct mmc_data)); mrq.cmd = &cmd; -- cgit v1.2.3 From a61ad2b49bfce94dfddce828cd9222e4b9e7825b Mon Sep 17 00:00:00 2001 From: Chris Ball Date: Wed, 13 Apr 2011 23:46:05 -0400 Subject: mmc: initialize struct mmc_data at declaration time Converts from: struct mmc_data data; memset(&data, 0, sizeof(struct mmc_data)); to: struct mmc_data data = {0}; because it's shorter, as performant, and easier to work out whether initialization has happened. Signed-off-by: Chris Ball --- drivers/mmc/core/mmc_ops.c | 6 ++---- drivers/mmc/core/sd_ops.c | 9 +++------ drivers/mmc/core/sdio_ops.c | 3 +-- 3 files changed, 6 insertions(+), 12 deletions(-) (limited to 'drivers/mmc/core') diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index de4e1855b8ed..1ed3866e990f 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -235,7 +235,7 @@ mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host, { struct mmc_request mrq; struct mmc_command cmd = {0}; - struct mmc_data data; + struct mmc_data data = {0}; struct scatterlist sg; void *data_buf; @@ -247,7 +247,6 @@ mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host, return -ENOMEM; memset(&mrq, 0, sizeof(struct mmc_request)); - memset(&data, 0, sizeof(struct mmc_data)); mrq.cmd = &cmd; mrq.data = &data; @@ -459,7 +458,7 @@ mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode, { struct mmc_request mrq; struct mmc_command cmd = {0}; - struct mmc_data data; + struct mmc_data data = {0}; struct scatterlist sg; u8 *data_buf; u8 *test_buf; @@ -489,7 +488,6 @@ mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode, memcpy(data_buf, test_buf, len); memset(&mrq, 0, sizeof(struct mmc_request)); - memset(&data, 0, sizeof(struct mmc_data)); mrq.cmd = &cmd; mrq.data = &data; diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c index dfe9a9c3f958..8023f580b901 100644 --- a/drivers/mmc/core/sd_ops.c +++ b/drivers/mmc/core/sd_ops.c @@ -245,7 +245,7 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr) int err; struct mmc_request mrq; struct mmc_command cmd = {0}; - struct mmc_data data; + struct mmc_data data = {0}; struct scatterlist sg; void *data_buf; @@ -267,7 +267,6 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr) return -ENOMEM; memset(&mrq, 0, sizeof(struct mmc_request)); - memset(&data, 0, sizeof(struct mmc_data)); mrq.cmd = &cmd; mrq.data = &data; @@ -307,7 +306,7 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group, { struct mmc_request mrq; struct mmc_command cmd = {0}; - struct mmc_data data; + struct mmc_data data = {0}; struct scatterlist sg; BUG_ON(!card); @@ -319,7 +318,6 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group, value &= 0xF; memset(&mrq, 0, sizeof(struct mmc_request)); - memset(&data, 0, sizeof(struct mmc_data)); mrq.cmd = &cmd; mrq.data = &data; @@ -355,7 +353,7 @@ int mmc_app_sd_status(struct mmc_card *card, void *ssr) int err; struct mmc_request mrq; struct mmc_command cmd = {0}; - struct mmc_data data; + struct mmc_data data = {0}; struct scatterlist sg; BUG_ON(!card); @@ -369,7 +367,6 @@ int mmc_app_sd_status(struct mmc_card *card, void *ssr) return err; memset(&mrq, 0, sizeof(struct mmc_request)); - memset(&data, 0, sizeof(struct mmc_data)); mrq.cmd = &cmd; mrq.data = &data; diff --git a/drivers/mmc/core/sdio_ops.c b/drivers/mmc/core/sdio_ops.c index f77b1bc318ed..147dfc1879b3 100644 --- a/drivers/mmc/core/sdio_ops.c +++ b/drivers/mmc/core/sdio_ops.c @@ -123,7 +123,7 @@ int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn, { struct mmc_request mrq; struct mmc_command cmd = {0}; - struct mmc_data data; + struct mmc_data data = {0}; struct scatterlist sg; BUG_ON(!card); @@ -137,7 +137,6 @@ int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn, return -EINVAL; memset(&mrq, 0, sizeof(struct mmc_request)); - memset(&data, 0, sizeof(struct mmc_data)); mrq.cmd = &cmd; mrq.data = &data; -- cgit v1.2.3 From 24f5b53ba076e983bc64fa48534ca795d7813d51 Mon Sep 17 00:00:00 2001 From: Chris Ball Date: Wed, 13 Apr 2011 23:49:45 -0400 Subject: mmc: initialize struct mmc_request at declaration time Converts from: struct mmc_request mrq; memset(&mrq, 0, sizeof(struct mmc_request)); to: struct mmc_request mrq = {0}; because it's shorter, as performant, and easier to work out whether initialization has happened. Signed-off-by: Chris Ball --- drivers/mmc/core/core.c | 4 +--- drivers/mmc/core/mmc_ops.c | 8 ++------ drivers/mmc/core/sd_ops.c | 14 ++++---------- drivers/mmc/core/sdio_ops.c | 4 +--- 4 files changed, 8 insertions(+), 22 deletions(-) (limited to 'drivers/mmc/core') diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index f273ffd6557a..1dbc18576219 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -236,12 +236,10 @@ EXPORT_SYMBOL(mmc_wait_for_req); */ int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries) { - struct mmc_request mrq; + struct mmc_request mrq = {0}; WARN_ON(!host->claimed); - memset(&mrq, 0, sizeof(struct mmc_request)); - memset(cmd->resp, 0, sizeof(cmd->resp)); cmd->retries = retries; diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 1ed3866e990f..845ce7c533b9 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -233,7 +233,7 @@ static int mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host, u32 opcode, void *buf, unsigned len) { - struct mmc_request mrq; + struct mmc_request mrq = {0}; struct mmc_command cmd = {0}; struct mmc_data data = {0}; struct scatterlist sg; @@ -246,8 +246,6 @@ mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host, if (data_buf == NULL) return -ENOMEM; - memset(&mrq, 0, sizeof(struct mmc_request)); - mrq.cmd = &cmd; mrq.data = &data; @@ -456,7 +454,7 @@ static int mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode, u8 len) { - struct mmc_request mrq; + struct mmc_request mrq = {0}; struct mmc_command cmd = {0}; struct mmc_data data = {0}; struct scatterlist sg; @@ -487,8 +485,6 @@ mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode, if (opcode == MMC_BUS_TEST_W) memcpy(data_buf, test_buf, len); - memset(&mrq, 0, sizeof(struct mmc_request)); - mrq.cmd = &cmd; mrq.data = &data; cmd.opcode = opcode; diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c index 8023f580b901..a206aea5360d 100644 --- a/drivers/mmc/core/sd_ops.c +++ b/drivers/mmc/core/sd_ops.c @@ -66,7 +66,7 @@ static int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card) int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card, struct mmc_command *cmd, int retries) { - struct mmc_request mrq; + struct mmc_request mrq = {0}; int i, err; @@ -243,7 +243,7 @@ int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca) int mmc_app_send_scr(struct mmc_card *card, u32 *scr) { int err; - struct mmc_request mrq; + struct mmc_request mrq = {0}; struct mmc_command cmd = {0}; struct mmc_data data = {0}; struct scatterlist sg; @@ -266,8 +266,6 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr) if (data_buf == NULL) return -ENOMEM; - memset(&mrq, 0, sizeof(struct mmc_request)); - mrq.cmd = &cmd; mrq.data = &data; @@ -304,7 +302,7 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr) int mmc_sd_switch(struct mmc_card *card, int mode, int group, u8 value, u8 *resp) { - struct mmc_request mrq; + struct mmc_request mrq = {0}; struct mmc_command cmd = {0}; struct mmc_data data = {0}; struct scatterlist sg; @@ -317,8 +315,6 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group, mode = !!mode; value &= 0xF; - memset(&mrq, 0, sizeof(struct mmc_request)); - mrq.cmd = &cmd; mrq.data = &data; @@ -351,7 +347,7 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group, int mmc_app_sd_status(struct mmc_card *card, void *ssr) { int err; - struct mmc_request mrq; + struct mmc_request mrq = {0}; struct mmc_command cmd = {0}; struct mmc_data data = {0}; struct scatterlist sg; @@ -366,8 +362,6 @@ int mmc_app_sd_status(struct mmc_card *card, void *ssr) if (err) return err; - memset(&mrq, 0, sizeof(struct mmc_request)); - mrq.cmd = &cmd; mrq.data = &data; diff --git a/drivers/mmc/core/sdio_ops.c b/drivers/mmc/core/sdio_ops.c index 147dfc1879b3..f087d876c573 100644 --- a/drivers/mmc/core/sdio_ops.c +++ b/drivers/mmc/core/sdio_ops.c @@ -121,7 +121,7 @@ int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn, int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn, unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz) { - struct mmc_request mrq; + struct mmc_request mrq = {0}; struct mmc_command cmd = {0}; struct mmc_data data = {0}; struct scatterlist sg; @@ -136,8 +136,6 @@ int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn, if (addr & ~0x1FFFF) return -EINVAL; - memset(&mrq, 0, sizeof(struct mmc_request)); - mrq.cmd = &cmd; mrq.data = &data; -- cgit v1.2.3 From cb87ea28ed9e75a41eb456bfcb547b4e6f10e750 Mon Sep 17 00:00:00 2001 From: John Calixto Date: Tue, 26 Apr 2011 18:56:29 -0400 Subject: mmc: core: Add mmc CMD+ACMD passthrough ioctl Allows appropriately-privileged applications to send CMD (normal) and ACMD (application-specific; preceded with CMD55) commands to cards/devices on the mmc bus. This is primarily useful for enabling the security functionality built in to every SD card. It can also be used as a generic passthrough (e.g. to enable virtual machines to control mmc bus devices directly). However, this use case has not been tested rigorously. Generic passthrough testing was only conducted for a few non-security opcodes to prove the feasibility of the passthrough. Since any opcode can be sent using this passthrough, it is very possible to render the card/device unusable. Applications that use this ioctl must have CAP_SYS_RAWIO. Security commands tested on TI PCIxx12 (SDHCI), Sigma Designs SMP8652 SoC, TI OMAP3621/OMAP3630 SoC, Samsung S5PC110 SoC, Qualcomm MSM7200A SoC. Signed-off-by: John Calixto Reviewed-by: Andrei Warkentin Reviewed-by: Arnd Bergmann Signed-off-by: Chris Ball --- drivers/mmc/core/sd_ops.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/mmc/core') diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c index a206aea5360d..021fed153804 100644 --- a/drivers/mmc/core/sd_ops.c +++ b/drivers/mmc/core/sd_ops.c @@ -21,7 +21,7 @@ #include "core.h" #include "sd_ops.h" -static int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card) +int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card) { int err; struct mmc_command cmd = {0}; @@ -49,6 +49,7 @@ static int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card) return 0; } +EXPORT_SYMBOL_GPL(mmc_app_cmd); /** * mmc_wait_for_app_cmd - start an application command and wait for -- cgit v1.2.3 From f2119df6b764609af4baceb68caf1e848c1c8aa7 Mon Sep 17 00:00:00 2001 From: Arindam Nath Date: Thu, 5 May 2011 12:18:57 +0530 Subject: mmc: sd: add support for signal voltage switch procedure Host Controller v3.00 adds another Capabilities register. Apart from other things, this new register indicates whether the Host Controller supports SDR50, SDR104, and DDR50 UHS-I modes. The spec doesn't mention about explicit support for SDR12 and SDR25 UHS-I modes, so the Host Controller v3.00 should support them by default. Also if the controller supports SDR104 mode, it will also support SDR50 mode as well. So depending on the host support, we set the corresponding MMC_CAP_* flags. One more new register. Host Control2 is added in v3.00, which is used during Signal Voltage Switch procedure described below. Since as per v3.00 spec, UHS-I supported hosts should set S18R to 1, we set S18R (bit 24) of OCR before sending ACMD41. We also need to set XPC (bit 28) of OCR in case the host can supply >150mA. This support is indicated by the Maximum Current Capabilities register of the Host Controller. If the response of ACMD41 has both CCS and S18A set, we start the signal voltage switch procedure, which if successfull, will switch the card from 3.3V signalling to 1.8V signalling. Signal voltage switch procedure adds support for a new command CMD11 in the Physical Layer Spec v3.01. As part of this procedure, we need to set 1.8V Signalling Enable (bit 3) of Host Control2 register, which if remains set after 5ms, means the switch to 1.8V signalling is successfull. Otherwise, we clear bit 24 of OCR and retry the initialization sequence. When we remove the card, and insert the same or another card, we need to make sure that we start with 3.3V signalling voltage. So we call mmc_set_signal_voltage() with MMC_SIGNAL_VOLTAGE_330 set so that we are back to 3.3V signalling voltage before we actually start initializing the card. Tested by Zhangfei Gao with a Toshiba uhs card and general hs card, on mmp2 in SDMA mode. Signed-off-by: Arindam Nath Reviewed-by: Philip Rakity Tested-by: Philip Rakity Acked-by: Zhangfei Gao Signed-off-by: Chris Ball --- drivers/mmc/core/core.c | 32 ++++++++++++++++++++++++++++++++ drivers/mmc/core/core.h | 1 + drivers/mmc/core/sd.c | 36 ++++++++++++++++++++++++++++++++++-- 3 files changed, 67 insertions(+), 2 deletions(-) (limited to 'drivers/mmc/core') diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 1dbc18576219..5005a6323165 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -942,6 +942,38 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr) return ocr; } +int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage) +{ + struct mmc_command cmd = {0}; + int err = 0; + + BUG_ON(!host); + + /* + * Send CMD11 only if the request is to switch the card to + * 1.8V signalling. + */ + if (signal_voltage == MMC_SIGNAL_VOLTAGE_180) { + cmd.opcode = SD_SWITCH_VOLTAGE; + cmd.arg = 0; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + + err = mmc_wait_for_cmd(host, &cmd, 0); + if (err) + return err; + + if (!mmc_host_is_spi(host) && (cmd.resp[0] & R1_ERROR)) + return -EIO; + } + + host->ios.signal_voltage = signal_voltage; + + if (host->ops->start_signal_voltage_switch) + err = host->ops->start_signal_voltage_switch(host, &host->ios); + + return err; +} + /* * Select timing parameters for host. */ diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index ca1fdde29df6..7745dea430b8 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -41,6 +41,7 @@ void mmc_set_bus_width(struct mmc_host *host, unsigned int width); void mmc_set_bus_width_ddr(struct mmc_host *host, unsigned int width, unsigned int ddr); u32 mmc_select_voltage(struct mmc_host *host, u32 ocr); +int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage); void mmc_set_timing(struct mmc_host *host, unsigned int timing); static inline void mmc_delay(unsigned int ms) diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 6dac89fe0535..b0cd285d272a 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -403,6 +403,7 @@ struct device_type sd_type = { int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid) { int err; + u32 rocr; /* * Since we're changing the OCR value, we seem to @@ -420,12 +421,38 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid) */ err = mmc_send_if_cond(host, ocr); if (!err) - ocr |= 1 << 30; + ocr |= SD_OCR_CCS; - err = mmc_send_app_op_cond(host, ocr, NULL); + /* + * If the host supports one of UHS-I modes, request the card + * to switch to 1.8V signaling level. + */ + if (host->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | + MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50)) + ocr |= SD_OCR_S18R; + + /* If the host can supply more than 150mA, XPC should be set to 1. */ + if (host->caps & (MMC_CAP_SET_XPC_330 | MMC_CAP_SET_XPC_300 | + MMC_CAP_SET_XPC_180)) + ocr |= SD_OCR_XPC; + +try_again: + err = mmc_send_app_op_cond(host, ocr, &rocr); if (err) return err; + /* + * In case CCS and S18A in the response is set, start Signal Voltage + * Switch procedure. SPI mode doesn't support CMD11. + */ + if (!mmc_host_is_spi(host) && ((rocr & 0x41000000) == 0x41000000)) { + err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180); + if (err) { + ocr &= ~SD_OCR_S18R; + goto try_again; + } + } + if (mmc_host_is_spi(host)) err = mmc_send_cid(host, cid); else @@ -773,6 +800,11 @@ int mmc_attach_sd(struct mmc_host *host) BUG_ON(!host); WARN_ON(!host->claimed); + /* Make sure we are at 3.3V signalling voltage */ + err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330); + if (err) + return err; + err = mmc_send_app_op_cond(host, 0, &ocr); if (err) return err; -- cgit v1.2.3 From 013909c4ffd16ded4895528b856fd8782df04dc6 Mon Sep 17 00:00:00 2001 From: Arindam Nath Date: Thu, 5 May 2011 12:18:58 +0530 Subject: mmc: sd: query function modes for uhs cards SD cards which conform to Physical Layer Spec v3.01 can support additional Bus Speed Modes, Driver Strength, and Current Limit other than the default values. We use CMD6 mode 0 to read these additional card functions. The values read here will be used during UHS-I initialization steps. Tested by Zhangfei Gao with a Toshiba uhs card and general hs card, on mmp2 in SDMA mode. Signed-off-by: Arindam Nath Reviewed-by: Philip Rakity Tested-by: Philip Rakity Acked-by: Zhangfei Gao Signed-off-by: Chris Ball --- drivers/mmc/core/sd.c | 68 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 58 insertions(+), 10 deletions(-) (limited to 'drivers/mmc/core') diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index b0cd285d272a..8285842f19e9 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -189,6 +189,9 @@ static int mmc_decode_scr(struct mmc_card *card) scr->sda_vsn = UNSTUFF_BITS(resp, 56, 4); scr->bus_widths = UNSTUFF_BITS(resp, 48, 4); + if (scr->sda_vsn == SCR_SPEC_VER_2) + /* Check if Physical Layer Spec v3.0 is supported */ + scr->sda_spec3 = UNSTUFF_BITS(resp, 47, 1); if (UNSTUFF_BITS(resp, 55, 1)) card->erased_byte = 0xFF; @@ -274,29 +277,74 @@ static int mmc_read_switch(struct mmc_card *card) status = kmalloc(64, GFP_KERNEL); if (!status) { printk(KERN_ERR "%s: could not allocate a buffer for " - "switch capabilities.\n", mmc_hostname(card->host)); + "switch capabilities.\n", + mmc_hostname(card->host)); return -ENOMEM; } + /* Find out the supported Bus Speed Modes. */ err = mmc_sd_switch(card, 0, 0, 1, status); if (err) { - /* If the host or the card can't do the switch, - * fail more gracefully. */ - if ((err != -EINVAL) - && (err != -ENOSYS) - && (err != -EFAULT)) + /* + * If the host or the card can't do the switch, + * fail more gracefully. + */ + if (err != -EINVAL && err != -ENOSYS && err != -EFAULT) goto out; - printk(KERN_WARNING "%s: problem reading switch " - "capabilities, performance might suffer.\n", + printk(KERN_WARNING "%s: problem reading Bus Speed modes.\n", mmc_hostname(card->host)); err = 0; goto out; } - if (status[13] & 0x02) - card->sw_caps.hs_max_dtr = 50000000; + if (card->scr.sda_spec3) { + card->sw_caps.sd3_bus_mode = status[13]; + + /* Find out Driver Strengths supported by the card */ + err = mmc_sd_switch(card, 0, 2, 1, status); + if (err) { + /* + * If the host or the card can't do the switch, + * fail more gracefully. + */ + if (err != -EINVAL && err != -ENOSYS && err != -EFAULT) + goto out; + + printk(KERN_WARNING "%s: problem reading " + "Driver Strength.\n", + mmc_hostname(card->host)); + err = 0; + + goto out; + } + + card->sw_caps.sd3_drv_type = status[9]; + + /* Find out Current Limits supported by the card */ + err = mmc_sd_switch(card, 0, 3, 1, status); + if (err) { + /* + * If the host or the card can't do the switch, + * fail more gracefully. + */ + if (err != -EINVAL && err != -ENOSYS && err != -EFAULT) + goto out; + + printk(KERN_WARNING "%s: problem reading " + "Current Limit.\n", + mmc_hostname(card->host)); + err = 0; + + goto out; + } + + card->sw_caps.sd3_curr_limit = status[7]; + } else { + if (status[13] & 0x02) + card->sw_caps.hs_max_dtr = 50000000; + } out: kfree(status); -- cgit v1.2.3 From d6d50a15a2897d4133d536dd4343b5cf21163db3 Mon Sep 17 00:00:00 2001 From: Arindam Nath Date: Thu, 5 May 2011 12:18:59 +0530 Subject: mmc: sd: add support for driver type selection This patch adds support for setting driver strength during UHS-I initialization procedure. Since UHS-I cards set S18A (bit 24) in response to ACMD41, we use this as a base for UHS-I initialization. We modify the parameter list of mmc_sd_get_cid() so that we can save the ROCR from ACMD41 to check whether bit 24 is set. We decide whether the Host Controller supports A, C, or D driver type depending on the Capabilities register. Driver type B is suported by default. We then set the appropriate driver type for the card using CMD6 mode 1. As per Host Controller spec v3.00, we set driver type for the host only if Preset Value Enable in the Host Control2 register is not set. SDHCI_HOST_CONTROL has been renamed to SDHCI_HOST_CONTROL1 to conform to the spec. Tested by Zhangfei Gao with a Toshiba uhs card and general hs card, on mmp2 in SDMA mode. Signed-off-by: Arindam Nath Reviewed-by: Philip Rakity Tested-by: Philip Rakity Acked-by: Zhangfei Gao Signed-off-by: Chris Ball --- drivers/mmc/core/core.c | 9 +++ drivers/mmc/core/core.h | 1 + drivers/mmc/core/sd.c | 152 +++++++++++++++++++++++++++++++++++++++--------- drivers/mmc/core/sd.h | 2 +- drivers/mmc/core/sdio.c | 4 +- 5 files changed, 139 insertions(+), 29 deletions(-) (limited to 'drivers/mmc/core') diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 5005a6323165..61c6c0b8f0e0 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -983,6 +983,15 @@ void mmc_set_timing(struct mmc_host *host, unsigned int timing) mmc_set_ios(host); } +/* + * Select appropriate driver type for host. + */ +void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type) +{ + host->ios.drv_type = drv_type; + mmc_set_ios(host); +} + /* * Apply power to the MMC stack. This is a two-stage process. * First, we enable power to the card without the clock running. diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 7745dea430b8..93f33973de39 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -43,6 +43,7 @@ void mmc_set_bus_width_ddr(struct mmc_host *host, unsigned int width, u32 mmc_select_voltage(struct mmc_host *host, u32 ocr); 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); static inline void mmc_delay(unsigned int ms) { diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 8285842f19e9..5b7c99855635 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -400,6 +400,98 @@ out: return err; } +static int sd_select_driver_type(struct mmc_card *card, u8 *status) +{ + int host_drv_type = 0, card_drv_type = 0; + int err; + + /* + * If the host doesn't support any of the Driver Types A,C or D, + * default Driver Type B is used. + */ + if (!(card->host->caps & (MMC_CAP_DRIVER_TYPE_A | MMC_CAP_DRIVER_TYPE_C + | MMC_CAP_DRIVER_TYPE_D))) + return 0; + + if (card->host->caps & MMC_CAP_DRIVER_TYPE_A) { + host_drv_type = MMC_SET_DRIVER_TYPE_A; + if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_A) + card_drv_type = MMC_SET_DRIVER_TYPE_A; + else if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_B) + card_drv_type = MMC_SET_DRIVER_TYPE_B; + else if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_C) + card_drv_type = MMC_SET_DRIVER_TYPE_C; + } else if (card->host->caps & MMC_CAP_DRIVER_TYPE_C) { + host_drv_type = MMC_SET_DRIVER_TYPE_C; + if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_C) + card_drv_type = MMC_SET_DRIVER_TYPE_C; + } else if (!(card->host->caps & MMC_CAP_DRIVER_TYPE_D)) { + /* + * If we are here, that means only the default driver type + * B is supported by the host. + */ + host_drv_type = MMC_SET_DRIVER_TYPE_B; + if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_B) + card_drv_type = MMC_SET_DRIVER_TYPE_B; + else if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_C) + card_drv_type = MMC_SET_DRIVER_TYPE_C; + } + + err = mmc_sd_switch(card, 1, 2, card_drv_type, status); + if (err) + return err; + + if ((status[15] & 0xF) != card_drv_type) { + printk(KERN_WARNING "%s: Problem setting driver strength!\n", + mmc_hostname(card->host)); + return 0; + } + + mmc_set_driver_type(card->host, host_drv_type); + + return 0; +} + +/* + * UHS-I specific initialization procedure + */ +static int mmc_sd_init_uhs_card(struct mmc_card *card) +{ + int err; + u8 *status; + + if (!card->scr.sda_spec3) + return 0; + + if (!(card->csd.cmdclass & CCC_SWITCH)) + return 0; + + status = kmalloc(64, GFP_KERNEL); + if (!status) { + printk(KERN_ERR "%s: could not allocate a buffer for " + "switch capabilities.\n", mmc_hostname(card->host)); + return -ENOMEM; + } + + /* Set 4-bit bus width */ + if ((card->host->caps & MMC_CAP_4_BIT_DATA) && + (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) { + err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4); + if (err) + goto out; + + mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4); + } + + /* Set the driver strength for the card */ + err = sd_select_driver_type(card, status); + +out: + kfree(status); + + return err; +} + MMC_DEV_ATTR(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1], card->raw_cid[2], card->raw_cid[3]); MMC_DEV_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1], @@ -448,10 +540,9 @@ struct device_type sd_type = { /* * Fetch CID from card. */ -int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid) +int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr) { int err; - u32 rocr; /* * Since we're changing the OCR value, we seem to @@ -485,7 +576,7 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid) ocr |= SD_OCR_XPC; try_again: - err = mmc_send_app_op_cond(host, ocr, &rocr); + err = mmc_send_app_op_cond(host, ocr, rocr); if (err) return err; @@ -493,7 +584,8 @@ try_again: * In case CCS and S18A in the response is set, start Signal Voltage * Switch procedure. SPI mode doesn't support CMD11. */ - if (!mmc_host_is_spi(host) && ((rocr & 0x41000000) == 0x41000000)) { + if (!mmc_host_is_spi(host) && rocr && + ((*rocr & 0x41000000) == 0x41000000)) { err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180); if (err) { ocr &= ~SD_OCR_S18R; @@ -628,11 +720,12 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, struct mmc_card *card; int err; u32 cid[4]; + u32 rocr = 0; BUG_ON(!host); WARN_ON(!host->claimed); - err = mmc_sd_get_cid(host, ocr, cid); + err = mmc_sd_get_cid(host, ocr, cid, &rocr); if (err) return err; @@ -685,30 +778,37 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, if (err) goto free_card; - /* - * Attempt to change to high-speed (if supported) - */ - err = mmc_sd_switch_hs(card); - if (err > 0) - mmc_sd_go_highspeed(card); - else if (err) - goto free_card; - - /* - * Set bus speed. - */ - mmc_set_clock(host, mmc_sd_get_max_clock(card)); - - /* - * Switch to wider bus (if supported). - */ - if ((host->caps & MMC_CAP_4_BIT_DATA) && - (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) { - err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4); + /* Initialization sequence for UHS-I cards */ + if (rocr & SD_ROCR_S18A) { + err = mmc_sd_init_uhs_card(card); if (err) goto free_card; + } else { + /* + * Attempt to change to high-speed (if supported) + */ + err = mmc_sd_switch_hs(card); + if (err > 0) + mmc_sd_go_highspeed(card); + else if (err) + goto free_card; + + /* + * Set bus speed. + */ + mmc_set_clock(host, mmc_sd_get_max_clock(card)); - mmc_set_bus_width(host, MMC_BUS_WIDTH_4); + /* + * Switch to wider bus (if supported). + */ + if ((host->caps & MMC_CAP_4_BIT_DATA) && + (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) { + err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4); + if (err) + goto free_card; + + mmc_set_bus_width(host, MMC_BUS_WIDTH_4); + } } host->card = card; diff --git a/drivers/mmc/core/sd.h b/drivers/mmc/core/sd.h index 3d8800fa7600..4b34b24f3f76 100644 --- a/drivers/mmc/core/sd.h +++ b/drivers/mmc/core/sd.h @@ -5,7 +5,7 @@ extern struct device_type sd_type; -int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid); +int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr); int mmc_sd_get_csd(struct mmc_host *host, struct mmc_card *card); void mmc_decode_cid(struct mmc_card *card); int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card, diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 1e6095961500..4d0c15bfa514 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -369,8 +369,8 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, goto err; } - if (ocr & R4_MEMORY_PRESENT - && mmc_sd_get_cid(host, host->ocr & ocr, card->raw_cid) == 0) { + if ((ocr & R4_MEMORY_PRESENT) && + mmc_sd_get_cid(host, host->ocr & ocr, card->raw_cid, NULL) == 0) { card->type = MMC_TYPE_SD_COMBO; if (oldcard && (oldcard->type != MMC_TYPE_SD_COMBO || -- cgit v1.2.3 From 49c468fcf878d2c86e31920cf54aa90c88418a66 Mon Sep 17 00:00:00 2001 From: Arindam Nath Date: Thu, 5 May 2011 12:19:01 +0530 Subject: mmc: sd: add support for uhs bus speed mode selection This patch adds support for setting UHS-I bus speed mode during UHS-I initialization procedure. Since both the host and card can support more than one bus speed, we select the highest speed based on both of their capabilities. First we set the bus speed mode for the card using CMD6 mode 1, and then we program the host controller to support the required speed mode. We also set High Speed Enable in case one of the UHS-I modes is selected. We take care to reset SD clock before setting UHS mode in the Host Control2 register, and then re-enable it as per the Host Controller spec v3.00. We then set the clock frequency for the UHS-I mode selected. Tested by Zhangfei Gao with a Toshiba uhs card and general hs card, on mmp2 in SDMA mode. Signed-off-by: Arindam Nath Reviewed-by: Philip Rakity Tested-by: Philip Rakity Acked-by: Zhangfei Gao Signed-off-by: Chris Ball --- drivers/mmc/core/sd.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) (limited to 'drivers/mmc/core') diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 5b7c99855635..6970b82171f7 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -452,6 +452,66 @@ static int sd_select_driver_type(struct mmc_card *card, u8 *status) return 0; } +static int sd_set_bus_speed_mode(struct mmc_card *card, u8 *status) +{ + unsigned int bus_speed = 0, timing = 0; + int err; + + /* + * If the host doesn't support any of the UHS-I modes, fallback on + * default speed. + */ + if (!(card->host->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | + MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50))) + return 0; + + if ((card->host->caps & MMC_CAP_UHS_SDR104) && + (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR104)) { + bus_speed = UHS_SDR104_BUS_SPEED; + timing = MMC_TIMING_UHS_SDR104; + card->sw_caps.uhs_max_dtr = UHS_SDR104_MAX_DTR; + } else if ((card->host->caps & MMC_CAP_UHS_DDR50) && + (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_DDR50)) { + bus_speed = UHS_DDR50_BUS_SPEED; + timing = MMC_TIMING_UHS_DDR50; + card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR; + } else if ((card->host->caps & (MMC_CAP_UHS_SDR104 | + MMC_CAP_UHS_SDR50)) && (card->sw_caps.sd3_bus_mode & + SD_MODE_UHS_SDR50)) { + bus_speed = UHS_SDR50_BUS_SPEED; + timing = MMC_TIMING_UHS_SDR50; + card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR; + } else if ((card->host->caps & (MMC_CAP_UHS_SDR104 | + MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25)) && + (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR25)) { + bus_speed = UHS_SDR25_BUS_SPEED; + timing = MMC_TIMING_UHS_SDR25; + card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR; + } else if ((card->host->caps & (MMC_CAP_UHS_SDR104 | + MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25 | + MMC_CAP_UHS_SDR12)) && (card->sw_caps.sd3_bus_mode & + SD_MODE_UHS_SDR12)) { + bus_speed = UHS_SDR12_BUS_SPEED; + timing = MMC_TIMING_UHS_SDR12; + card->sw_caps.uhs_max_dtr = UHS_SDR12_MAX_DTR; + } + + card->sd_bus_speed = bus_speed; + err = mmc_sd_switch(card, 1, 0, bus_speed, status); + if (err) + return err; + + if ((status[16] & 0xF) != bus_speed) + printk(KERN_WARNING "%s: Problem setting bus speed mode!\n", + mmc_hostname(card->host)); + else { + mmc_set_timing(card->host, timing); + mmc_set_clock(card->host, card->sw_caps.uhs_max_dtr); + } + + return 0; +} + /* * UHS-I specific initialization procedure */ @@ -485,6 +545,11 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card) /* Set the driver strength for the card */ err = sd_select_driver_type(card, status); + if (err) + goto out; + + /* Set bus speed mode of the card */ + err = sd_set_bus_speed_mode(card, status); out: kfree(status); -- cgit v1.2.3 From 5371c927bcd06a5c9dd6785bab2d452b87d9abc6 Mon Sep 17 00:00:00 2001 From: Arindam Nath Date: Thu, 5 May 2011 12:19:02 +0530 Subject: mmc: sd: set current limit for uhs cards We decide on the current limit to be set for the card based on the Capability of Host Controller to provide current at 1.8V signalling, and the maximum current limit of the card as indicated by CMD6 mode 0. We then set the current limit for the card using CMD6 mode 1. As per the Physical Layer Spec v3.01, the current limit switch is only applicable for SDR50, SDR104, and DDR50 bus speed modes. For other UHS-I modes, we set the default current limit of 200mA. Tested by Zhangfei Gao with a Toshiba uhs card and general hs card, on mmp2 in SDMA mode. Signed-off-by: Arindam Nath Reviewed-by: Philip Rakity Tested-by: Philip Rakity Acked-by: Zhangfei Gao Signed-off-by: Chris Ball --- drivers/mmc/core/sd.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) (limited to 'drivers/mmc/core') diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 6970b82171f7..8e2d8012e4cb 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -512,6 +512,64 @@ static int sd_set_bus_speed_mode(struct mmc_card *card, u8 *status) return 0; } +static int sd_set_current_limit(struct mmc_card *card, u8 *status) +{ + int current_limit = 0; + int err; + + /* + * Current limit switch is only defined for SDR50, SDR104, and DDR50 + * bus speed modes. For other bus speed modes, we set the default + * current limit of 200mA. + */ + if ((card->sd_bus_speed == UHS_SDR50_BUS_SPEED) || + (card->sd_bus_speed == UHS_SDR104_BUS_SPEED) || + (card->sd_bus_speed == UHS_DDR50_BUS_SPEED)) { + if (card->host->caps & MMC_CAP_MAX_CURRENT_800) { + if (card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_800) + current_limit = SD_SET_CURRENT_LIMIT_800; + else if (card->sw_caps.sd3_curr_limit & + SD_MAX_CURRENT_600) + current_limit = SD_SET_CURRENT_LIMIT_600; + else if (card->sw_caps.sd3_curr_limit & + SD_MAX_CURRENT_400) + current_limit = SD_SET_CURRENT_LIMIT_400; + else if (card->sw_caps.sd3_curr_limit & + SD_MAX_CURRENT_200) + current_limit = SD_SET_CURRENT_LIMIT_200; + } else if (card->host->caps & MMC_CAP_MAX_CURRENT_600) { + if (card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_600) + current_limit = SD_SET_CURRENT_LIMIT_600; + else if (card->sw_caps.sd3_curr_limit & + SD_MAX_CURRENT_400) + current_limit = SD_SET_CURRENT_LIMIT_400; + else if (card->sw_caps.sd3_curr_limit & + SD_MAX_CURRENT_200) + current_limit = SD_SET_CURRENT_LIMIT_200; + } else if (card->host->caps & MMC_CAP_MAX_CURRENT_400) { + if (card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_400) + current_limit = SD_SET_CURRENT_LIMIT_400; + else if (card->sw_caps.sd3_curr_limit & + SD_MAX_CURRENT_200) + current_limit = SD_SET_CURRENT_LIMIT_200; + } else if (card->host->caps & MMC_CAP_MAX_CURRENT_200) { + if (card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_200) + current_limit = SD_SET_CURRENT_LIMIT_200; + } + } else + current_limit = SD_SET_CURRENT_LIMIT_200; + + err = mmc_sd_switch(card, 1, 3, current_limit, status); + if (err) + return err; + + if (((status[15] >> 4) & 0x0F) != current_limit) + printk(KERN_WARNING "%s: Problem setting current limit!\n", + mmc_hostname(card->host)); + + return 0; +} + /* * UHS-I specific initialization procedure */ @@ -550,6 +608,11 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card) /* Set bus speed mode of the card */ err = sd_set_bus_speed_mode(card, status); + if (err) + goto out; + + /* Set current limit for the card */ + err = sd_set_current_limit(card, status); out: kfree(status); -- cgit v1.2.3 From 3a3035114307cd55e024662bb295a87b849f0bd4 Mon Sep 17 00:00:00 2001 From: Arindam Nath Date: Thu, 5 May 2011 12:19:03 +0530 Subject: mmc: sd: report correct speed and capacity of uhs cards Since only UHS-I cards respond with S18A set in response to ACMD41, we set the card as ultra-high-speed after successfull initialization. We need to decide whether a card is SDXC based on the C_SIZE field of CSDv2.0 register. According to Physical Layer spec v3.01, the minimum value of C_SIZE for SDXC card is 00FFFFh. Tested by Zhangfei Gao with a Toshiba uhs card and general hs card, on mmp2 in SDMA mode. Signed-off-by: Arindam Nath Reviewed-by: Philip Rakity Tested-by: Philip Rakity Acked-by: Zhangfei Gao Signed-off-by: Chris Ball --- drivers/mmc/core/bus.c | 11 ++++++++--- drivers/mmc/core/sd.c | 10 +++++++++- 2 files changed, 17 insertions(+), 4 deletions(-) (limited to 'drivers/mmc/core') diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index d6d62fd07ee9..393d817ed040 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -274,8 +274,12 @@ int mmc_add_card(struct mmc_card *card) break; case MMC_TYPE_SD: type = "SD"; - if (mmc_card_blockaddr(card)) - type = "SDHC"; + if (mmc_card_blockaddr(card)) { + if (mmc_card_ext_capacity(card)) + type = "SDXC"; + else + type = "SDHC"; + } break; case MMC_TYPE_SDIO: type = "SDIO"; @@ -299,7 +303,8 @@ int mmc_add_card(struct mmc_card *card) } else { printk(KERN_INFO "%s: new %s%s%s card at address %04x\n", mmc_hostname(card->host), - mmc_card_highspeed(card) ? "high speed " : "", + mmc_sd_card_uhs(card) ? "ultra high speed " : + (mmc_card_highspeed(card) ? "high speed " : ""), mmc_card_ddr_mode(card) ? "DDR " : "", type, card->rca); } diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 8e2d8012e4cb..732c3171ceca 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -130,7 +130,7 @@ static int mmc_decode_csd(struct mmc_card *card) break; case 1: /* - * This is a block-addressed SDHC card. Most + * This is a block-addressed SDHC or SDXC card. Most * interesting fields are unused and have fixed * values. To avoid getting tripped by buggy cards, * we assume those fixed values ourselves. @@ -144,6 +144,11 @@ static int mmc_decode_csd(struct mmc_card *card) e = UNSTUFF_BITS(resp, 96, 3); csd->max_dtr = tran_exp[e] * tran_mant[m]; csd->cmdclass = UNSTUFF_BITS(resp, 84, 12); + csd->c_size = UNSTUFF_BITS(resp, 48, 22); + + /* SDXC cards have a minimum C_SIZE of 0x00FFFF */ + if (csd->c_size >= 0xFFFF) + mmc_card_set_ext_capacity(card); m = UNSTUFF_BITS(resp, 48, 22); csd->capacity = (1 + m) << 10; @@ -911,6 +916,9 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, err = mmc_sd_init_uhs_card(card); if (err) goto free_card; + + /* Card is an ultra-high-speed card */ + mmc_sd_card_set_uhs(card); } else { /* * Attempt to change to high-speed (if supported) -- cgit v1.2.3 From b513ea250eb7c36a8afb3df938d632ca6b4df7cd Mon Sep 17 00:00:00 2001 From: Arindam Nath Date: Thu, 5 May 2011 12:19:04 +0530 Subject: mmc: sd: add support for tuning during uhs initialization Host Controller needs tuning during initialization to operate SDR50 and SDR104 UHS-I cards. Whether SDR50 mode actually needs tuning is indicated by bit 45 of the Host Controller Capabilities register. A new command CMD19 has been defined in the Physical Layer spec v3.01 to request the card to send tuning pattern. We enable Buffer Read Ready interrupt at the very begining of tuning procedure, because that is the only interrupt generated by the Host Controller during tuning. We program the block size to 64 in the Block Size register. We make sure that DMA Enable and Multi Block Select in the Transfer Mode register are set to 0 before actually sending CMD19. The tuning block is sent by the card to the Host Controller using DAT lines, so we set Data Present Select (bit 5) in the Command register. The Host Controller is responsible for doing the verfication of tuning block sent by the card at the hardware level. After sending CMD19, we wait for Buffer Read Ready interrupt. In case we don't receive an interrupt after the specified timeout value, we fall back on fixed sampling clock by setting Execute Tuning (bit 6) and Sampling Clock Select (bit 7) of Host Control2 register to 0. Before exiting the tuning procedure, we disable Buffer Read Ready interrupt and re-enable other interrupts. Tested by Zhangfei Gao with a Toshiba uhs card and general hs card, on mmp2 in SDMA mode. Signed-off-by: Arindam Nath Reviewed-by: Philip Rakity Tested-by: Philip Rakity Acked-by: Zhangfei Gao Signed-off-by: Chris Ball --- drivers/mmc/core/sd.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/mmc/core') diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 732c3171ceca..fc65475a26ee 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -618,6 +618,12 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card) /* Set current limit for the card */ err = sd_set_current_limit(card, status); + if (err) + goto out; + + /* SPI mode doesn't define CMD19 */ + if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning) + err = card->host->ops->execute_tuning(card->host); out: kfree(status); -- cgit v1.2.3 From 4d55c5a13a189a80d40383f02c8026f9a87d7c87 Mon Sep 17 00:00:00 2001 From: Arindam Nath Date: Thu, 5 May 2011 12:19:05 +0530 Subject: mmc: sdhci: enable preset value after uhs initialization According to the Host Controller spec v3.00, setting Preset Value Enable in the Host Control2 register lets SDCLK Frequency Select, Clock Generator Select and Driver Strength Select to be set automatically by the Host Controller based on the UHS-I mode set. This patch enables this feature. Since Preset Value Enable makes sense only for UHS-I cards, we enable this feature after successfull UHS-I initialization. We also reset Preset Value Enable next time before initialization. Tested by Zhangfei Gao with a Toshiba uhs card and general hs card, on mmp2 in SDMA mode. Signed-off-by: Arindam Nath Reviewed-by: Philip Rakity Tested-by: Philip Rakity Acked-by: Zhangfei Gao Signed-off-by: Chris Ball --- drivers/mmc/core/sd.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/mmc/core') diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index fc65475a26ee..b461b290ce25 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -925,6 +925,13 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, /* Card is an ultra-high-speed card */ mmc_sd_card_set_uhs(card); + + /* + * Since initialization is now complete, enable preset + * value registers for UHS-I cards. + */ + if (host->ops->enable_preset_value) + host->ops->enable_preset_value(host, true); } else { /* * Attempt to change to high-speed (if supported) @@ -1095,6 +1102,10 @@ int mmc_attach_sd(struct mmc_host *host) if (err) return err; + /* Disable preset value enable if already set since last time */ + if (host->ops->enable_preset_value) + host->ops->enable_preset_value(host, false); + err = mmc_send_app_op_cond(host, 0, &ocr); if (err) return err; -- cgit v1.2.3 From 06e8935febe687e2a561707d4c7ca4245d261dbe Mon Sep 17 00:00:00 2001 From: Stefan Nilsson XK Date: Wed, 11 May 2011 17:48:05 +0200 Subject: mmc: sdio: optimized SDIO IRQ handling for single irq If there is only 1 function interrupt registered it is possible to improve performance by directly calling the irq handler and avoiding the overhead of reading the CCCR registers. Signed-off-by: Per Forlin Acked-by: Ulf Hansson Reviewed-by: Nicolas Pitre Signed-off-by: Chris Ball --- drivers/mmc/core/sdio_irq.c | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) (limited to 'drivers/mmc/core') diff --git a/drivers/mmc/core/sdio_irq.c b/drivers/mmc/core/sdio_irq.c index b3001617e67d..03ead028d2ce 100644 --- a/drivers/mmc/core/sdio_irq.c +++ b/drivers/mmc/core/sdio_irq.c @@ -31,6 +31,17 @@ static int process_sdio_pending_irqs(struct mmc_card *card) { int i, ret, count; unsigned char pending; + struct sdio_func *func; + + /* + * Optimization, if there is only 1 function interrupt registered + * call irq handler directly + */ + func = card->sdio_single_irq; + if (func) { + func->irq_handler(func); + return 1; + } ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_INTx, 0, &pending); if (ret) { @@ -42,7 +53,7 @@ static int process_sdio_pending_irqs(struct mmc_card *card) count = 0; for (i = 1; i <= 7; i++) { if (pending & (1 << i)) { - struct sdio_func *func = card->sdio_func[i - 1]; + func = card->sdio_func[i - 1]; if (!func) { printk(KERN_WARNING "%s: pending IRQ for " "non-existent function\n", @@ -186,6 +197,24 @@ static int sdio_card_irq_put(struct mmc_card *card) return 0; } +/* If there is only 1 function registered set sdio_single_irq */ +static void sdio_single_irq_set(struct mmc_card *card) +{ + struct sdio_func *func; + int i; + + card->sdio_single_irq = NULL; + if ((card->host->caps & MMC_CAP_SDIO_IRQ) && + card->host->sdio_irqs == 1) + for (i = 0; i < card->sdio_funcs; i++) { + func = card->sdio_func[i]; + if (func && func->irq_handler) { + card->sdio_single_irq = func; + break; + } + } +} + /** * sdio_claim_irq - claim the IRQ for a SDIO function * @func: SDIO function @@ -227,6 +256,7 @@ int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler) ret = sdio_card_irq_get(func->card); if (ret) func->irq_handler = NULL; + sdio_single_irq_set(func->card); return ret; } @@ -251,6 +281,7 @@ int sdio_release_irq(struct sdio_func *func) if (func->irq_handler) { func->irq_handler = NULL; sdio_card_irq_put(func->card); + sdio_single_irq_set(func->card); } ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, ®); -- cgit v1.2.3 From a8e6df7343cf67c9104955da0de70075a6ee1dfd Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Mon, 9 May 2011 11:32:31 +0300 Subject: mmc: core: clear MMC_PM_KEEP_POWER flag on resume Since the MMC_PM_KEEP_POWER flag should be set on each suspend, it should also cleared on each resume. Upon resuming, we have to know if power was kept (for re-initialization, etc.), so clear it just after resuming. Signed-off-by: Eliad Peller Signed-off-by: Chris Ball --- drivers/mmc/core/core.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mmc/core') diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 61c6c0b8f0e0..72e113e7f253 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1829,6 +1829,7 @@ int mmc_resume_host(struct mmc_host *host) err = 0; } } + host->pm_flags &= ~MMC_PM_KEEP_POWER; mmc_bus_put(host); return err; -- cgit v1.2.3 From 261bbd463a091b939770255d559bbc89b1bad568 Mon Sep 17 00:00:00 2001 From: Philip Rakity Date: Fri, 13 May 2011 11:17:17 +0530 Subject: mmc: core: eMMC signal voltage does not use CMD11 eMMC chips do not use CMD11 when changing voltage. Add extra argument to call to indicate if CMD11 needs to be sent. Signed-off-by: Philip Rakity Reviewed-by: Arindam Nath Signed-off-by: Chris Ball --- drivers/mmc/core/core.c | 4 ++-- drivers/mmc/core/core.h | 3 ++- drivers/mmc/core/sd.c | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers/mmc/core') diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 72e113e7f253..94c8d5a9ecae 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -942,7 +942,7 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr) return ocr; } -int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage) +int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, bool cmd11) { struct mmc_command cmd = {0}; int err = 0; @@ -953,7 +953,7 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage) * Send CMD11 only if the request is to switch the card to * 1.8V signalling. */ - if (signal_voltage == MMC_SIGNAL_VOLTAGE_180) { + if ((signal_voltage != MMC_SIGNAL_VOLTAGE_330) && cmd11) { cmd.opcode = SD_SWITCH_VOLTAGE; cmd.arg = 0; cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 93f33973de39..53d23c240324 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -41,7 +41,8 @@ void mmc_set_bus_width(struct mmc_host *host, unsigned int width); void mmc_set_bus_width_ddr(struct mmc_host *host, unsigned int width, unsigned int ddr); u32 mmc_select_voltage(struct mmc_host *host, u32 ocr); -int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage); +int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, + bool cmd11); void mmc_set_timing(struct mmc_host *host, unsigned int timing); void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type); diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index b461b290ce25..596d0b9d30b8 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -725,7 +725,7 @@ try_again: */ if (!mmc_host_is_spi(host) && rocr && ((*rocr & 0x41000000) == 0x41000000)) { - err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180); + err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180, true); if (err) { ocr &= ~SD_OCR_S18R; goto try_again; @@ -1098,7 +1098,7 @@ int mmc_attach_sd(struct mmc_host *host) WARN_ON(!host->claimed); /* Make sure we are at 3.3V signalling voltage */ - err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330); + err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330, false); if (err) return err; -- cgit v1.2.3 From 4c4cb171054230c2e58ed6574d7faa1871c75bbe Mon Sep 17 00:00:00 2001 From: Philip Rakity Date: Fri, 13 May 2011 11:17:18 +0530 Subject: mmc: core: add support for eMMC Dual Data Rate eMMC voltage change not required for 1.8V. 3.3V and 1.8V vcc are capable of doing DDR. vccq of 1.8v is not required. Signed-off-by: Philip Rakity Reviewed-by: Arindam Nath Signed-off-by: Chris Ball --- drivers/mmc/core/core.c | 14 ++------------ drivers/mmc/core/core.h | 2 -- drivers/mmc/core/mmc.c | 35 ++++++++++++++++++++++++++++++----- 3 files changed, 32 insertions(+), 19 deletions(-) (limited to 'drivers/mmc/core') diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 94c8d5a9ecae..7863eedf3d9a 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -717,23 +717,13 @@ void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode) mmc_set_ios(host); } -/* - * Change data bus width and DDR mode of a host. - */ -void mmc_set_bus_width_ddr(struct mmc_host *host, unsigned int width, - unsigned int ddr) -{ - host->ios.bus_width = width; - host->ios.ddr = ddr; - mmc_set_ios(host); -} - /* * Change data bus width of a host. */ void mmc_set_bus_width(struct mmc_host *host, unsigned int width) { - mmc_set_bus_width_ddr(host, width, MMC_SDR_MODE); + host->ios.bus_width = width; + mmc_set_ios(host); } /** diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 53d23c240324..d9411ed2a39b 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -38,8 +38,6 @@ void mmc_ungate_clock(struct mmc_host *host); void mmc_set_ungated(struct mmc_host *host); void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode); void mmc_set_bus_width(struct mmc_host *host, unsigned int width); -void mmc_set_bus_width_ddr(struct mmc_host *host, unsigned int width, - unsigned int ddr); u32 mmc_select_voltage(struct mmc_host *host, u32 ocr); int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, bool cmd11); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index a2c795e8f9dd..0433fe66cbad 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -20,6 +20,7 @@ #include "core.h" #include "bus.h" #include "mmc_ops.h" +#include "sd_ops.h" static const unsigned int tran_exp[] = { 10000, 100000, 1000000, 10000000, @@ -633,10 +634,14 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, */ if (mmc_card_highspeed(card)) { if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_8V) - && (host->caps & (MMC_CAP_1_8V_DDR))) + && ((host->caps & (MMC_CAP_1_8V_DDR | + MMC_CAP_UHS_DDR50)) + == (MMC_CAP_1_8V_DDR | MMC_CAP_UHS_DDR50))) ddr = MMC_1_8V_DDR_MODE; else if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_2V) - && (host->caps & (MMC_CAP_1_2V_DDR))) + && ((host->caps & (MMC_CAP_1_2V_DDR | + MMC_CAP_UHS_DDR50)) + == (MMC_CAP_1_2V_DDR | MMC_CAP_UHS_DDR50))) ddr = MMC_1_2V_DDR_MODE; } @@ -670,8 +675,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ext_csd_bits[idx][0], 0); if (!err) { - mmc_set_bus_width_ddr(card->host, - bus_width, MMC_SDR_MODE); + mmc_set_bus_width(card->host, bus_width); /* * If controller can't handle bus width test, * use the highest bus width to maintain @@ -697,8 +701,29 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, 1 << bus_width, ddr); goto free_card; } else if (ddr) { + /* + * eMMC cards can support 3.3V to 1.2V i/o (vccq) + * signaling. + * + * EXT_CSD_CARD_TYPE_DDR_1_8V means 3.3V or 1.8V vccq. + * + * 1.8V vccq at 3.3V core voltage (vcc) is not required + * in the JEDEC spec for DDR. + * + * Do not force change in vccq since we are obviously + * working and no change to vccq is needed. + * + * WARNING: eMMC rules are NOT the same as SD DDR + */ + if (ddr == EXT_CSD_CARD_TYPE_DDR_1_2V) { + err = mmc_set_signal_voltage(host, + MMC_SIGNAL_VOLTAGE_120, 0); + if (err) + goto err; + } mmc_card_set_ddr_mode(card); - mmc_set_bus_width_ddr(card->host, bus_width, ddr); + mmc_set_timing(card->host, MMC_TIMING_UHS_DDR50); + mmc_set_bus_width(card->host, bus_width); } } -- cgit v1.2.3 From 06b2233a20bf25c8ee57b7c6e13f528309ac6edc Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Thu, 12 May 2011 17:18:59 +0900 Subject: mmc: core: duplicated trial with same freq in mmc_rescan_try_freq() mmc_rescan_try_freq() tries to init two times with the last frequency. For example, if host->f_min is 400KHz, we see the message below: mmc1: mmc_rescan_try_freq: trying to init card at 400000 Hz mmc1: mmc_rescan_try_freq: trying to init card at 400000 Hz Andy Ross says that he didn't try this code on a board with an f_min that exactly matches one of the table entries, which explains why the bug wasn't detected. Signed-off-by: Jaehoon Chung Signed-off-by: Kyungmin Park Cc: Andy Ross Signed-off-by: Chris Ball --- drivers/mmc/core/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc/core') diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 7863eedf3d9a..68091dda3f31 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1608,7 +1608,7 @@ void mmc_rescan(struct work_struct *work) for (i = 0; i < ARRAY_SIZE(freqs); i++) { if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min))) break; - if (freqs[i] < host->f_min) + if (freqs[i] <= host->f_min) break; } mmc_release_host(host); -- cgit v1.2.3 From f0d89972b01798cf9d245dfa1cacfa0ee78a3593 Mon Sep 17 00:00:00 2001 From: Andrei Warkentin Date: Mon, 23 May 2011 15:06:38 -0500 Subject: mmc: core: Block CMD23 support for UHS104/SDXC cards. SD cards operating at UHS104 or better support SET_BLOCK_COUNT. Signed-off-by: Andrei Warkentin Reviewed-by: Arindam Nath Signed-off-by: Chris Ball --- drivers/mmc/core/sd.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/mmc/core') diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 596d0b9d30b8..ff2774128aa9 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -203,6 +203,8 @@ static int mmc_decode_scr(struct mmc_card *card) else card->erased_byte = 0x0; + if (scr->sda_spec3) + scr->cmds = UNSTUFF_BITS(resp, 32, 2); return 0; } -- cgit v1.2.3 From 08ee80cc397ac1a306ca689a22ede954d92d0db1 Mon Sep 17 00:00:00 2001 From: Philip Rakity Date: Tue, 24 May 2011 18:14:58 -0700 Subject: mmc: core: eMMC bus width may not work on all platforms CMD19 -- The offical way to validate bus widths from the JEDEC spec does not work on all platforms. Some platforms that use PCI/PCIe to connect their SD controllers are known to fail. If the quirk MMC_BUS_WIDTH_TEST is not defined we try to figure out the bus width by reading the ext_csd at different bus widths and compare this against the ext_csd read in 1 bit mode. If no ext_csd is available we default to 1 bit operations. Code has been tested on mmp2 against 8 bit eMMC and Transcend 2GB card that is known to not work in 4 bit mode. The physical pins on the card are not present to support 4 bit operation. Signed-off-by: Philip Rakity Signed-off-by: Chris Ball --- drivers/mmc/core/mmc.c | 112 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 102 insertions(+), 10 deletions(-) (limited to 'drivers/mmc/core') diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 0433fe66cbad..2a7e43bc796d 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -174,14 +174,17 @@ static int mmc_decode_csd(struct mmc_card *card) } /* - * Read and decode extended CSD. + * Read extended CSD. */ -static int mmc_read_ext_csd(struct mmc_card *card) +static int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd) { int err; u8 *ext_csd; BUG_ON(!card); + BUG_ON(!new_ext_csd); + + *new_ext_csd = NULL; if (card->csd.mmca_vsn < CSD_SPEC_VER_4) return 0; @@ -199,12 +202,15 @@ static int mmc_read_ext_csd(struct mmc_card *card) err = mmc_send_ext_csd(card, ext_csd); if (err) { + kfree(ext_csd); + *new_ext_csd = NULL; + /* If the host or the card can't do the switch, * fail more gracefully. */ if ((err != -EINVAL) && (err != -ENOSYS) && (err != -EFAULT)) - goto out; + return err; /* * High capacity cards should have this "magic" size @@ -222,9 +228,23 @@ static int mmc_read_ext_csd(struct mmc_card *card) mmc_hostname(card->host)); err = 0; } + } else + *new_ext_csd = ext_csd; - goto out; - } + return err; +} + +/* + * Decode extended CSD. + */ +static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) +{ + int err = 0; + + BUG_ON(!card); + + if (!ext_csd) + return 0; /* Version is coded in the CSD_STRUCTURE byte in the EXT_CSD register */ if (card->csd.structure == 3) { @@ -372,8 +392,69 @@ static int mmc_read_ext_csd(struct mmc_card *card) card->erased_byte = 0x0; out: + return err; +} + +static inline void mmc_free_ext_csd(u8 *ext_csd) +{ kfree(ext_csd); +} + +static int mmc_compare_ext_csds(struct mmc_card *card, u8 *ext_csd, + unsigned bus_width) +{ + u8 *bw_ext_csd; + int err; + + err = mmc_get_ext_csd(card, &bw_ext_csd); + if (err) + return err; + + if ((ext_csd == NULL || bw_ext_csd == NULL)) { + if (bus_width != MMC_BUS_WIDTH_1) + err = -EINVAL; + goto out; + } + + if (bus_width == MMC_BUS_WIDTH_1) + goto out; + + /* only compare read only fields */ + err = (!(ext_csd[EXT_CSD_PARTITION_SUPPORT] == + bw_ext_csd[EXT_CSD_PARTITION_SUPPORT]) && + (ext_csd[EXT_CSD_ERASED_MEM_CONT] == + bw_ext_csd[EXT_CSD_ERASED_MEM_CONT]) && + (ext_csd[EXT_CSD_REV] == + bw_ext_csd[EXT_CSD_REV]) && + (ext_csd[EXT_CSD_STRUCTURE] == + bw_ext_csd[EXT_CSD_STRUCTURE]) && + (ext_csd[EXT_CSD_CARD_TYPE] == + bw_ext_csd[EXT_CSD_CARD_TYPE]) && + (ext_csd[EXT_CSD_S_A_TIMEOUT] == + bw_ext_csd[EXT_CSD_S_A_TIMEOUT]) && + (ext_csd[EXT_CSD_HC_WP_GRP_SIZE] == + bw_ext_csd[EXT_CSD_HC_WP_GRP_SIZE]) && + (ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT] == + bw_ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT]) && + (ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] == + bw_ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]) && + (ext_csd[EXT_CSD_SEC_TRIM_MULT] == + bw_ext_csd[EXT_CSD_SEC_TRIM_MULT]) && + (ext_csd[EXT_CSD_SEC_ERASE_MULT] == + bw_ext_csd[EXT_CSD_SEC_ERASE_MULT]) && + (ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT] == + bw_ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT]) && + (ext_csd[EXT_CSD_TRIM_MULT] == + bw_ext_csd[EXT_CSD_TRIM_MULT]) && + memcmp(&ext_csd[EXT_CSD_SEC_CNT], + &bw_ext_csd[EXT_CSD_SEC_CNT], + 4) != 0); + if (err) + err = -EINVAL; + +out: + mmc_free_ext_csd(bw_ext_csd); return err; } @@ -438,6 +519,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, u32 cid[4]; unsigned int max_dtr; u32 rocr; + u8 *ext_csd = NULL; BUG_ON(!host); WARN_ON(!host->claimed); @@ -536,7 +618,11 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, /* * Fetch and process extended CSD. */ - err = mmc_read_ext_csd(card); + + err = mmc_get_ext_csd(card, &ext_csd); + if (err) + goto free_card; + err = mmc_read_ext_csd(card, ext_csd); if (err) goto free_card; @@ -676,14 +762,18 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, 0); if (!err) { mmc_set_bus_width(card->host, bus_width); + /* * If controller can't handle bus width test, - * use the highest bus width to maintain - * compatibility with previous MMC behavior. + * compare ext_csd previously read in 1 bit mode + * against ext_csd at new bus width */ if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST)) - break; - err = mmc_bus_test(card, bus_width); + err = mmc_compare_ext_csds(card, + ext_csd, + bus_width); + else + err = mmc_bus_test(card, bus_width); if (!err) break; } @@ -730,12 +820,14 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, if (!oldcard) host->card = card; + mmc_free_ext_csd(ext_csd); return 0; free_card: if (!oldcard) mmc_remove_card(card); err: + mmc_free_ext_csd(ext_csd); return err; } -- cgit v1.2.3