From 6da24b786ed1963a7f872c1899627968c76d17d7 Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Tue, 10 Aug 2010 18:01:36 -0700 Subject: mmc: recognize CSD structure The eMMC spec 4.4 and 4.3 + additional feature chips has CSD structure version 3 and version 3 have to check the CSD_STRUCTURE byte in the EXT_CSD register. Also fix EXT_CSD revision message. [akpm@linux-foundation.org: fix comment, per Chris Ball] Signed-off-by: Kyungmin Park Cc: Adrian Hunter Cc: Chris Ball Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mmc/card.h | 1 + include/linux/mmc/mmc.h | 1 + 2 files changed, 2 insertions(+) (limited to 'include/linux/mmc') diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index d02d2c6e0cfe..c83c7a7303fd 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -24,6 +24,7 @@ struct mmc_cid { }; struct mmc_csd { + unsigned char structure; unsigned char mmca_vsn; unsigned short cmdclass; unsigned short tacc_clks; diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index 8a49cbf0376d..52ce98866287 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -254,6 +254,7 @@ struct _mmc_csd { #define EXT_CSD_BUS_WIDTH 183 /* R/W */ #define EXT_CSD_HS_TIMING 185 /* R/W */ #define EXT_CSD_CARD_TYPE 196 /* RO */ +#define EXT_CSD_STRUCTURE 194 /* RO */ #define EXT_CSD_REV 192 /* RO */ #define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */ #define EXT_CSD_S_A_TIMEOUT 217 -- cgit v1.2.3 From 7310ece86ad7da027f85a37a0638164118a5d12f Mon Sep 17 00:00:00 2001 From: Michal Miroslaw Date: Tue, 10 Aug 2010 18:01:40 -0700 Subject: mmc: implement SD-combo (IO+mem) support Signed-off-by: Michal Miroslaw Cc: Adrian Hunter Cc: Chris Ball Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/mmc/core/bus.c | 9 ++++ drivers/mmc/core/core.c | 12 ++++- drivers/mmc/core/sdio.c | 135 +++++++++++++++++++++++++++++++++++++++-------- include/linux/mmc/card.h | 1 + 4 files changed, 134 insertions(+), 23 deletions(-) (limited to 'include/linux/mmc') diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index 49d9dcaeca49..7cd9749dc21d 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -37,6 +37,8 @@ static ssize_t mmc_type_show(struct device *dev, return sprintf(buf, "SD\n"); case MMC_TYPE_SDIO: return sprintf(buf, "SDIO\n"); + case MMC_TYPE_SD_COMBO: + return sprintf(buf, "SDcombo\n"); default: return -EFAULT; } @@ -74,6 +76,9 @@ mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env) case MMC_TYPE_SDIO: type = "SDIO"; break; + case MMC_TYPE_SD_COMBO: + type = "SDcombo"; + break; default: type = NULL; } @@ -239,6 +244,10 @@ int mmc_add_card(struct mmc_card *card) case MMC_TYPE_SDIO: type = "SDIO"; break; + case MMC_TYPE_SD_COMBO: + type = "SD-combo"; + if (mmc_card_blockaddr(card)) + type = "SDHC-combo"; default: type = "?"; break; diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 569e94da844c..b69ce91b11e1 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1099,8 +1099,15 @@ void mmc_rescan(struct work_struct *work) */ err = mmc_send_io_op_cond(host, 0, &ocr); if (!err) { - if (mmc_attach_sdio(host, ocr)) - mmc_power_off(host); + if (mmc_attach_sdio(host, ocr)) { + mmc_claim_host(host); + /* try SDMEM (but not MMC) even if SDIO is broken */ + if (mmc_send_app_op_cond(host, 0, &ocr)) + goto out_fail; + + if (mmc_attach_sd(host, ocr)) + mmc_power_off(host); + } goto out; } @@ -1124,6 +1131,7 @@ void mmc_rescan(struct work_struct *work) goto out; } +out_fail: mmc_release_host(host); mmc_power_off(host); diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 47d1708810bd..b0b6ce93e519 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -160,9 +160,7 @@ static int sdio_enable_wide(struct mmc_card *card) if (ret) return ret; - mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4); - - return 0; + return 1; } /* @@ -222,10 +220,34 @@ static int sdio_disable_wide(struct mmc_card *card) return 0; } + +static int sdio_enable_4bit_bus(struct mmc_card *card) +{ + int err; + + if (card->type == MMC_TYPE_SDIO) + return sdio_enable_wide(card); + + 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) + return err; + } else + return 0; + + err = sdio_enable_wide(card); + if (err <= 0) + mmc_app_set_bus_width(card, MMC_BUS_WIDTH_1); + + return err; +} + + /* * Test if the card supports high-speed mode and, if so, switch to it. */ -static int sdio_enable_hs(struct mmc_card *card) +static int mmc_sdio_switch_hs(struct mmc_card *card, int enable) { int ret; u8 speed; @@ -240,7 +262,10 @@ static int sdio_enable_hs(struct mmc_card *card) if (ret) return ret; - speed |= SDIO_SPEED_EHS; + if (enable) + speed |= SDIO_SPEED_EHS; + else + speed &= ~SDIO_SPEED_EHS; ret = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_SPEED, speed, NULL); if (ret) @@ -249,6 +274,24 @@ static int sdio_enable_hs(struct mmc_card *card) return 1; } +/* + * Enable SDIO/combo card's high-speed mode. Return 0/1 if [not]supported. + */ +static int sdio_enable_hs(struct mmc_card *card) +{ + int ret; + + ret = mmc_sdio_switch_hs(card, true); + if (ret <= 0 || card->type == MMC_TYPE_SDIO) + return ret; + + ret = mmc_sd_switch_hs(card); + if (ret <= 0) + mmc_sdio_switch_hs(card, false); + + return ret; +} + static unsigned mmc_sdio_get_max_clock(struct mmc_card *card) { unsigned max_dtr; @@ -265,6 +308,9 @@ static unsigned mmc_sdio_get_max_clock(struct mmc_card *card) max_dtr = card->cis.max_dtr; } + if (card->type == MMC_TYPE_SD_COMBO) + max_dtr = min(max_dtr, mmc_sd_get_max_clock(card)); + return max_dtr; } @@ -310,7 +356,24 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, goto err; } - card->type = MMC_TYPE_SDIO; + err = mmc_sd_get_cid(host, host->ocr & ocr, card->raw_cid); + + if (!err) { + card->type = MMC_TYPE_SD_COMBO; + + if (oldcard && (oldcard->type != MMC_TYPE_SD_COMBO || + memcmp(card->raw_cid, oldcard->raw_cid, sizeof(card->raw_cid)) != 0)) { + mmc_remove_card(card); + return -ENOENT; + } + } else { + card->type = MMC_TYPE_SDIO; + + if (oldcard && oldcard->type != MMC_TYPE_SDIO) { + mmc_remove_card(card); + return -ENOENT; + } + } /* * Call the optional HC's init_card function to handle quirks. @@ -329,6 +392,17 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); } + /* + * Read CSD, before selecting the card + */ + if (!oldcard && card->type == MMC_TYPE_SD_COMBO) { + err = mmc_sd_get_csd(host, card); + if (err) + return err; + + mmc_decode_cid(card); + } + /* * Select card, as all following commands rely on that. */ @@ -356,14 +430,33 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, int same = (card->cis.vendor == oldcard->cis.vendor && card->cis.device == oldcard->cis.device); mmc_remove_card(card); - if (!same) { - err = -ENOENT; - goto err; - } + if (!same) + return -ENOENT; + card = oldcard; return 0; } + if (card->type == MMC_TYPE_SD_COMBO) { + err = mmc_sd_setup_card(host, card, oldcard != NULL); + /* handle as SDIO-only card if memory init failed */ + if (err) { + mmc_go_idle(host); + if (mmc_host_is_spi(host)) + /* should not fail, as it worked previously */ + mmc_spi_set_crc(host, use_spi_crc); + card->type = MMC_TYPE_SDIO; + } else + card->dev.type = &sd_type; + } + + /* + * If needed, disconnect card detection pull-up resistor. + */ + err = sdio_disable_cd(card); + if (err) + goto remove; + /* * Switch to high-speed (if supported). */ @@ -381,8 +474,10 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, /* * Switch to wider bus (if supported). */ - err = sdio_enable_wide(card); - if (err) + err = sdio_enable_4bit_bus(card); + if (err > 0) + mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4); + else if (err) goto remove; if (!oldcard) @@ -496,9 +591,14 @@ static int mmc_sdio_resume(struct mmc_host *host) mmc_claim_host(host); err = mmc_sdio_init_card(host, host->ocr, host->card, (host->pm_flags & MMC_PM_KEEP_POWER)); - if (!err) + if (!err) { /* We may have switched to 1-bit mode during suspend. */ - err = sdio_enable_wide(host->card); + err = sdio_enable_4bit_bus(host->card); + if (err > 0) { + mmc_set_bus_width(host, MMC_BUS_WIDTH_4); + err = 0; + } + } if (!err && host->sdio_irqs) mmc_signal_sdio_irq(host); mmc_release_host(host); @@ -582,13 +682,6 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr) funcs = (ocr & 0x70000000) >> 28; card->sdio_funcs = 0; - /* - * If needed, disconnect card detection pull-up resistor. - */ - err = sdio_disable_cd(card); - if (err) - goto remove; - /* * Initialize (but don't add) all present functions. */ diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index c83c7a7303fd..340d391aecbb 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -93,6 +93,7 @@ struct mmc_card { #define MMC_TYPE_MMC 0 /* MMC card */ #define MMC_TYPE_SD 1 /* SD card */ #define MMC_TYPE_SDIO 2 /* SDIO card */ +#define MMC_TYPE_SD_COMBO 3 /* SD combo (IO+mem) card */ unsigned int state; /* (our) card state */ #define MMC_STATE_PRESENT (1<<0) /* present in sysfs */ #define MMC_STATE_READONLY (1<<1) /* card is read-only */ -- cgit v1.2.3 From 4c2ef25fe0b847d2ae818f74758ddb0be1c27d8e Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Tue, 10 Aug 2010 18:01:41 -0700 Subject: mmc: fix all hangs related to mmc/sd card insert/removal during suspend/resume If you don't use CONFIG_MMC_UNSAFE_RESUME, as soon as you attempt to suspend, the card will be removed, therefore this patch doesn't change the behavior of this option. However the removal will be done by pm notifier, which runs while userspace is still not frozen and thus can freely use del_gendisk, without the risk of deadlock which would happen otherwise. Card detect workqueue is now disabled while userspace is frozen, Therefore if you do use CONFIG_MMC_UNSAFE_RESUME, and remove the card during suspend, the removal will be detected as soon as userspace is unfrozen, again at the moment it is safe to call del_gendisk. Tested with and without CONFIG_MMC_UNSAFE_RESUME with suspend and hibernate. [akpm@linux-foundation.org: clean up function prototype] [akpm@linux-foundation.org: fix CONFIG_PM-n linkage, small cleanups] [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Maxim Levitsky Cc: David Brownell Cc: Alan Stern Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/mmc/core/core.c | 83 +++++++++++++++++++++++++++++++++--------------- drivers/mmc/core/host.c | 4 +++ include/linux/mmc/host.h | 3 ++ 3 files changed, 64 insertions(+), 26 deletions(-) (limited to 'include/linux/mmc') diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index b69ce91b11e1..83240faa1dc8 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1057,6 +1057,17 @@ void mmc_rescan(struct work_struct *work) container_of(work, struct mmc_host, detect.work); u32 ocr; int err; + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + + if (host->rescan_disable) { + spin_unlock_irqrestore(&host->lock, flags); + return; + } + + spin_unlock_irqrestore(&host->lock, flags); + mmc_bus_get(host); @@ -1274,19 +1285,6 @@ int mmc_suspend_host(struct mmc_host *host) if (host->bus_ops && !host->bus_dead) { if (host->bus_ops->suspend) err = host->bus_ops->suspend(host); - if (err == -ENOSYS || !host->bus_ops->resume) { - /* - * We simply "remove" the card in this case. - * It will be redetected on resume. - */ - if (host->bus_ops->remove) - host->bus_ops->remove(host); - mmc_claim_host(host); - mmc_detach_bus(host); - mmc_release_host(host); - host->pm_flags = 0; - err = 0; - } } mmc_bus_put(host); @@ -1318,28 +1316,61 @@ int mmc_resume_host(struct mmc_host *host) printk(KERN_WARNING "%s: error %d during resume " "(card was removed?)\n", mmc_hostname(host), err); - if (host->bus_ops->remove) - host->bus_ops->remove(host); - mmc_claim_host(host); - mmc_detach_bus(host); - mmc_release_host(host); - /* no need to bother upper layers */ err = 0; } } mmc_bus_put(host); - /* - * We add a slight delay here so that resume can progress - * in parallel. - */ - mmc_detect_change(host, 1); - return err; } - EXPORT_SYMBOL(mmc_resume_host); +/* Do the card removal on suspend if card is assumed removeable + * Do that in pm notifier while userspace isn't yet frozen, so we will be able + to sync the card. +*/ +int mmc_pm_notify(struct notifier_block *notify_block, + unsigned long mode, void *unused) +{ + struct mmc_host *host = container_of( + notify_block, struct mmc_host, pm_notify); + unsigned long flags; + + + switch (mode) { + case PM_HIBERNATION_PREPARE: + case PM_SUSPEND_PREPARE: + + spin_lock_irqsave(&host->lock, flags); + host->rescan_disable = 1; + spin_unlock_irqrestore(&host->lock, flags); + cancel_delayed_work_sync(&host->detect); + + if (!host->bus_ops || host->bus_ops->suspend) + break; + + mmc_claim_host(host); + + if (host->bus_ops->remove) + host->bus_ops->remove(host); + + mmc_detach_bus(host); + mmc_release_host(host); + host->pm_flags = 0; + break; + + case PM_POST_SUSPEND: + case PM_POST_HIBERNATION: + + spin_lock_irqsave(&host->lock, flags); + host->rescan_disable = 0; + spin_unlock_irqrestore(&host->lock, flags); + mmc_detect_change(host, 0); + + } + + return 0; +} #endif static int __init mmc_init(void) diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 47353909e345..0efe631e50ca 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -17,6 +17,7 @@ #include #include #include +#include #include @@ -85,6 +86,7 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) init_waitqueue_head(&host->wq); INIT_DELAYED_WORK(&host->detect, mmc_rescan); INIT_DELAYED_WORK_DEFERRABLE(&host->disable, mmc_host_deeper_disable); + host->pm_notify.notifier_call = mmc_pm_notify; /* * By default, hosts do not support SGIO or large requests. @@ -133,6 +135,7 @@ int mmc_add_host(struct mmc_host *host) #endif mmc_start_host(host); + register_pm_notifier(&host->pm_notify); return 0; } @@ -149,6 +152,7 @@ EXPORT_SYMBOL(mmc_add_host); */ void mmc_remove_host(struct mmc_host *host) { + unregister_pm_notifier(&host->pm_notify); mmc_stop_host(host); #ifdef CONFIG_DEBUG_FS diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index f65913c9f5a4..513ff0376b09 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -124,6 +124,7 @@ struct mmc_host { unsigned int f_min; unsigned int f_max; u32 ocr_avail; + struct notifier_block pm_notify; #define MMC_VDD_165_195 0x00000080 /* VDD voltage 1.65 - 1.95 */ #define MMC_VDD_20_21 0x00000100 /* VDD voltage 2.0 ~ 2.1 */ @@ -183,6 +184,7 @@ struct mmc_host { /* Only used with MMC_CAP_DISABLE */ int enabled; /* host is enabled */ + int rescan_disable; /* disable card detection */ int nesting_cnt; /* "enable" nesting count */ int en_dis_recurs; /* detect recursion */ unsigned int disable_delay; /* disable delay in msecs */ @@ -257,6 +259,7 @@ int mmc_card_can_sleep(struct mmc_host *host); int mmc_host_enable(struct mmc_host *host); int mmc_host_disable(struct mmc_host *host); int mmc_host_lazy_disable(struct mmc_host *host); +int mmc_pm_notify(struct notifier_block *notify_block, unsigned long, void *); static inline void mmc_set_disable_delay(struct mmc_host *host, unsigned int disable_delay) -- cgit v1.2.3 From 6f51be3d37dff73cf8db771df4169f4c2f1cbf66 Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas Date: Tue, 10 Aug 2010 18:01:50 -0700 Subject: sdio: allow non-standard SDIO cards There are some chips (like TI WL12xx series) that can be interfaced over SDIO but don't support the SDIO specification, meaning that they are missing CIA (Common I/O Area) with all it's registers. Current Linux SDIO implementation relies on those registers to identify and configure the card, so non-standard cards can not function and cause lots of warnings from the core when it reads invalid data from non-existent registers. After this patch, init_card() host callback can now set new quirk MMC_QUIRK_NONSTD_SDIO, which means that SDIO core should not try to access any standard SDIO registers and rely on init_card() to fill all SDIO structures instead. As those cards are usually embedded chips, all the required information can be obtained from machine board files by the host driver when it's called through init_card() callback. Signed-off-by: Grazvydas Ignotas Cc: Adrian Hunter Cc: Tony Lindgren Cc: Bob Copeland Cc: Kalle Valo Cc: Madhusudhan Chikkature Cc: Kishore Kadiyala Cc: Russell King Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/mmc/core/sdio.c | 36 ++++++++++++++++++++++++++++++------ include/linux/mmc/card.h | 2 ++ 2 files changed, 32 insertions(+), 6 deletions(-) (limited to 'include/linux/mmc') diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index b0b6ce93e519..bd2755e8d9a3 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -63,13 +63,19 @@ static int sdio_init_func(struct mmc_card *card, unsigned int fn) func->num = fn; - ret = sdio_read_fbr(func); - if (ret) - goto fail; + if (!(card->quirks & MMC_QUIRK_NONSTD_SDIO)) { + ret = sdio_read_fbr(func); + if (ret) + goto fail; - ret = sdio_read_func_cis(func); - if (ret) - goto fail; + ret = sdio_read_func_cis(func); + if (ret) + goto fail; + } else { + func->vendor = func->card->cis.vendor; + func->device = func->card->cis.device; + func->max_blksize = func->card->cis.blksize; + } card->sdio_func[fn - 1] = func; @@ -412,6 +418,23 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, goto remove; } + if (card->quirks & MMC_QUIRK_NONSTD_SDIO) { + /* + * This is non-standard SDIO device, meaning it doesn't + * have any CIA (Common I/O area) registers present. + * It's host's responsibility to fill cccr and cis + * structures in init_card(). + */ + mmc_set_clock(host, card->cis.max_dtr); + + if (card->cccr.high_speed) { + mmc_card_set_highspeed(card); + mmc_set_timing(card->host, MMC_TIMING_SD_HS); + } + + goto finish; + } + /* * Read the common registers. */ @@ -480,6 +503,7 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, else if (err) goto remove; +finish: if (!oldcard) host->card = card; return 0; diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 340d391aecbb..4d893eaf8174 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -103,6 +103,8 @@ struct mmc_card { #define MMC_QUIRK_LENIENT_FN0 (1<<0) /* allow SDIO FN0 writes outside of the VS CCCR range */ #define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1) /* use func->cur_blksize */ /* for byte mode */ +#define MMC_QUIRK_NONSTD_SDIO (1<<2) /* non-standard SDIO card attached */ + /* (missing CIA registers) */ u32 raw_cid[4]; /* raw card CID */ u32 raw_csd[4]; /* raw card CSD */ -- cgit v1.2.3 From dfe86cba7676d58db8de7e623f5e72f1b0d3ca35 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 11 Aug 2010 14:17:46 -0700 Subject: mmc: add erase, secure erase, trim and secure trim operations SD/MMC cards tend to support an erase operation. In addition, eMMC v4.4 cards can support secure erase, trim and secure trim operations that are all variants of the basic erase command. SD/MMC device attributes "erase_size" and "preferred_erase_size" have been added. "erase_size" is the minimum size, in bytes, of an erase operation. For MMC, "erase_size" is the erase group size reported by the card. Note that "erase_size" does not apply to trim or secure trim operations where the minimum size is always one 512 byte sector. For SD, "erase_size" is 512 if the card is block-addressed, 0 otherwise. SD/MMC cards can erase an arbitrarily large area up to and including the whole card. When erasing a large area it may be desirable to do it in smaller chunks for three reasons: 1. A single erase command will make all other I/O on the card wait. This is not a problem if the whole card is being erased, but erasing one partition will make I/O for another partition on the same card wait for the duration of the erase - which could be a several minutes. 2. To be able to inform the user of erase progress. 3. The erase timeout becomes too large to be very useful. Because the erase timeout contains a margin which is multiplied by the size of the erase area, the value can end up being several minutes for large areas. "erase_size" is not the most efficient unit to erase (especially for SD where it is just one sector), hence "preferred_erase_size" provides a good chunk size for erasing large areas. For MMC, "preferred_erase_size" is the high-capacity erase size if a card specifies one, otherwise it is based on the capacity of the card. For SD, "preferred_erase_size" is the allocation unit size specified by the card. "preferred_erase_size" is in bytes. Signed-off-by: Adrian Hunter Acked-by: Jens Axboe Cc: Kyungmin Park Cc: Madhusudhan Chikkature Cc: Christoph Hellwig Cc: Ben Gardiner Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/00-INDEX | 2 + Documentation/mmc/00-INDEX | 4 + Documentation/mmc/mmc-dev-attrs.txt | 56 ++++++ drivers/mmc/core/core.c | 346 ++++++++++++++++++++++++++++++++++++ drivers/mmc/core/core.h | 2 + drivers/mmc/core/mmc.c | 47 ++++- drivers/mmc/core/sd.c | 82 +++++++++ drivers/mmc/core/sd_ops.c | 48 +++++ drivers/mmc/core/sd_ops.h | 1 + include/linux/mmc/card.h | 20 +++ include/linux/mmc/core.h | 19 ++ include/linux/mmc/host.h | 1 + include/linux/mmc/mmc.h | 26 ++- include/linux/mmc/sd.h | 5 + 14 files changed, 651 insertions(+), 8 deletions(-) create mode 100644 Documentation/mmc/00-INDEX create mode 100644 Documentation/mmc/mmc-dev-attrs.txt (limited to 'include/linux/mmc') diff --git a/Documentation/00-INDEX b/Documentation/00-INDEX index 9e642c5bf526..8dfc6708a257 100644 --- a/Documentation/00-INDEX +++ b/Documentation/00-INDEX @@ -232,6 +232,8 @@ memory.txt - info on typical Linux memory problems. mips/ - directory with info about Linux on MIPS architecture. +mmc/ + - directory with info about the MMC subsystem mono.txt - how to execute Mono-based .NET binaries with the help of BINFMT_MISC. mutex-design.txt diff --git a/Documentation/mmc/00-INDEX b/Documentation/mmc/00-INDEX new file mode 100644 index 000000000000..fca586f5b853 --- /dev/null +++ b/Documentation/mmc/00-INDEX @@ -0,0 +1,4 @@ +00-INDEX + - this file +mmc-dev-attrs.txt + - info on SD and MMC device attributes diff --git a/Documentation/mmc/mmc-dev-attrs.txt b/Documentation/mmc/mmc-dev-attrs.txt new file mode 100644 index 000000000000..ff2bd685bced --- /dev/null +++ b/Documentation/mmc/mmc-dev-attrs.txt @@ -0,0 +1,56 @@ +SD and MMC Device Attributes +============================ + +All attributes are read-only. + + cid Card Identifaction Register + csd Card Specific Data Register + scr SD Card Configuration Register (SD only) + date Manufacturing Date (from CID Register) + fwrev Firmware/Product Revision (from CID Register) (SD and MMCv1 only) + hwrev Hardware/Product Revision (from CID Register) (SD and MMCv1 only) + manfid Manufacturer ID (from CID Register) + name Product Name (from CID Register) + oemid OEM/Application ID (from CID Register) + serial Product Serial Number (from CID Register) + erase_size Erase group size + preferred_erase_size Preferred erase size + +Note on Erase Size and Preferred Erase Size: + + "erase_size" is the minimum size, in bytes, of an erase + operation. For MMC, "erase_size" is the erase group size + reported by the card. Note that "erase_size" does not apply + to trim or secure trim operations where the minimum size is + always one 512 byte sector. For SD, "erase_size" is 512 + if the card is block-addressed, 0 otherwise. + + SD/MMC cards can erase an arbitrarily large area up to and + including the whole card. When erasing a large area it may + be desirable to do it in smaller chunks for three reasons: + 1. A single erase command will make all other I/O on + the card wait. This is not a problem if the whole card + is being erased, but erasing one partition will make + I/O for another partition on the same card wait for the + duration of the erase - which could be a several + minutes. + 2. To be able to inform the user of erase progress. + 3. The erase timeout becomes too large to be very + useful. Because the erase timeout contains a margin + which is multiplied by the size of the erase area, + the value can end up being several minutes for large + areas. + + "erase_size" is not the most efficient unit to erase + (especially for SD where it is just one sector), + hence "preferred_erase_size" provides a good chunk + size for erasing large areas. + + For MMC, "preferred_erase_size" is the high-capacity + erase size if a card specifies one, otherwise it is + based on the capacity of the card. + + For SD, "preferred_erase_size" is the allocation unit + size specified by the card. + + "preferred_erase_size" is in bytes. diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 83240faa1dc8..5db49b124ffa 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1050,6 +1050,352 @@ void mmc_detect_change(struct mmc_host *host, unsigned long delay) EXPORT_SYMBOL(mmc_detect_change); +void mmc_init_erase(struct mmc_card *card) +{ + unsigned int sz; + + if (is_power_of_2(card->erase_size)) + card->erase_shift = ffs(card->erase_size) - 1; + else + card->erase_shift = 0; + + /* + * It is possible to erase an arbitrarily large area of an SD or MMC + * card. That is not desirable because it can take a long time + * (minutes) potentially delaying more important I/O, and also the + * timeout calculations become increasingly hugely over-estimated. + * Consequently, 'pref_erase' is defined as a guide to limit erases + * to that size and alignment. + * + * For SD cards that define Allocation Unit size, limit erases to one + * Allocation Unit at a time. For MMC cards that define High Capacity + * Erase Size, whether it is switched on or not, limit to that size. + * Otherwise just have a stab at a good value. For modern cards it + * will end up being 4MiB. Note that if the value is too small, it + * can end up taking longer to erase. + */ + if (mmc_card_sd(card) && card->ssr.au) { + card->pref_erase = card->ssr.au; + card->erase_shift = ffs(card->ssr.au) - 1; + } else if (card->ext_csd.hc_erase_size) { + card->pref_erase = card->ext_csd.hc_erase_size; + } else { + sz = (card->csd.capacity << (card->csd.read_blkbits - 9)) >> 11; + if (sz < 128) + card->pref_erase = 512 * 1024 / 512; + else if (sz < 512) + card->pref_erase = 1024 * 1024 / 512; + else if (sz < 1024) + card->pref_erase = 2 * 1024 * 1024 / 512; + else + card->pref_erase = 4 * 1024 * 1024 / 512; + if (card->pref_erase < card->erase_size) + card->pref_erase = card->erase_size; + else { + sz = card->pref_erase % card->erase_size; + if (sz) + card->pref_erase += card->erase_size - sz; + } + } +} + +static void mmc_set_mmc_erase_timeout(struct mmc_card *card, + struct mmc_command *cmd, + unsigned int arg, unsigned int qty) +{ + unsigned int erase_timeout; + + if (card->ext_csd.erase_group_def & 1) { + /* High Capacity Erase Group Size uses HC timeouts */ + if (arg == MMC_TRIM_ARG) + erase_timeout = card->ext_csd.trim_timeout; + else + erase_timeout = card->ext_csd.hc_erase_timeout; + } else { + /* CSD Erase Group Size uses write timeout */ + unsigned int mult = (10 << card->csd.r2w_factor); + unsigned int timeout_clks = card->csd.tacc_clks * mult; + unsigned int timeout_us; + + /* Avoid overflow: e.g. tacc_ns=80000000 mult=1280 */ + if (card->csd.tacc_ns < 1000000) + timeout_us = (card->csd.tacc_ns * mult) / 1000; + else + timeout_us = (card->csd.tacc_ns / 1000) * mult; + + /* + * ios.clock is only a target. The real clock rate might be + * less but not that much less, so fudge it by multiplying by 2. + */ + timeout_clks <<= 1; + timeout_us += (timeout_clks * 1000) / + (card->host->ios.clock / 1000); + + erase_timeout = timeout_us / 1000; + + /* + * Theoretically, the calculation could underflow so round up + * to 1ms in that case. + */ + if (!erase_timeout) + erase_timeout = 1; + } + + /* Multiplier for secure operations */ + if (arg & MMC_SECURE_ARGS) { + if (arg == MMC_SECURE_ERASE_ARG) + erase_timeout *= card->ext_csd.sec_erase_mult; + else + erase_timeout *= card->ext_csd.sec_trim_mult; + } + + erase_timeout *= qty; + + /* + * Ensure at least a 1 second timeout for SPI as per + * 'mmc_set_data_timeout()' + */ + if (mmc_host_is_spi(card->host) && erase_timeout < 1000) + erase_timeout = 1000; + + cmd->erase_timeout = erase_timeout; +} + +static void mmc_set_sd_erase_timeout(struct mmc_card *card, + struct mmc_command *cmd, unsigned int arg, + unsigned int qty) +{ + 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; + } else { + /* + * Erase timeout not specified in SD Status Register (SSR) so + * use 250ms per write block. + */ + cmd->erase_timeout = 250 * qty; + } + + /* Must not be less than 1 second */ + if (cmd->erase_timeout < 1000) + cmd->erase_timeout = 1000; +} + +static void mmc_set_erase_timeout(struct mmc_card *card, + struct mmc_command *cmd, unsigned int arg, + unsigned int qty) +{ + if (mmc_card_sd(card)) + mmc_set_sd_erase_timeout(card, cmd, arg, qty); + else + mmc_set_mmc_erase_timeout(card, cmd, arg, qty); +} + +static int mmc_do_erase(struct mmc_card *card, unsigned int from, + unsigned int to, unsigned int arg) +{ + struct mmc_command cmd; + unsigned int qty = 0; + int err; + + /* + * qty is used to calculate the erase timeout which depends on how many + * erase groups (or allocation units in SD terminology) are affected. + * We count erasing part of an erase group as one erase group. + * For SD, the allocation units are always a power of 2. For MMC, the + * erase group size is almost certainly also power of 2, but it does not + * seem to insist on that in the JEDEC standard, so we fall back to + * division in that case. SD may not specify an allocation unit size, + * in which case the timeout is based on the number of write blocks. + * + * Note that the timeout for secure trim 2 will only be correct if the + * number of erase groups specified is the same as the total of all + * preceding secure trim 1 commands. Since the power may have been + * lost since the secure trim 1 commands occurred, it is generally + * impossible to calculate the secure trim 2 timeout correctly. + */ + if (card->erase_shift) + qty += ((to >> card->erase_shift) - + (from >> card->erase_shift)) + 1; + else if (mmc_card_sd(card)) + qty += to - from + 1; + else + qty += ((to / card->erase_size) - + (from / card->erase_size)) + 1; + + if (!mmc_card_blockaddr(card)) { + from <<= 9; + to <<= 9; + } + + memset(&cmd, 0, sizeof(struct mmc_command)); + if (mmc_card_sd(card)) + cmd.opcode = SD_ERASE_WR_BLK_START; + else + cmd.opcode = MMC_ERASE_GROUP_START; + cmd.arg = from; + cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC; + err = mmc_wait_for_cmd(card->host, &cmd, 0); + if (err) { + printk(KERN_ERR "mmc_erase: group start error %d, " + "status %#x\n", err, cmd.resp[0]); + err = -EINVAL; + goto out; + } + + memset(&cmd, 0, sizeof(struct mmc_command)); + if (mmc_card_sd(card)) + cmd.opcode = SD_ERASE_WR_BLK_END; + else + cmd.opcode = MMC_ERASE_GROUP_END; + cmd.arg = to; + cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC; + err = mmc_wait_for_cmd(card->host, &cmd, 0); + if (err) { + printk(KERN_ERR "mmc_erase: group end error %d, status %#x\n", + err, cmd.resp[0]); + err = -EINVAL; + goto out; + } + + memset(&cmd, 0, sizeof(struct mmc_command)); + 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); + err = mmc_wait_for_cmd(card->host, &cmd, 0); + if (err) { + printk(KERN_ERR "mmc_erase: erase error %d, status %#x\n", + err, cmd.resp[0]); + err = -EIO; + goto out; + } + + if (mmc_host_is_spi(card->host)) + goto out; + + do { + memset(&cmd, 0, sizeof(struct mmc_command)); + cmd.opcode = MMC_SEND_STATUS; + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + /* Do not retry else we can't see errors */ + err = mmc_wait_for_cmd(card->host, &cmd, 0); + if (err || (cmd.resp[0] & 0xFDF92000)) { + printk(KERN_ERR "error %d requesting status %#x\n", + err, cmd.resp[0]); + err = -EIO; + goto out; + } + } while (!(cmd.resp[0] & R1_READY_FOR_DATA) || + R1_CURRENT_STATE(cmd.resp[0]) == 7); +out: + return err; +} + +/** + * mmc_erase - erase sectors. + * @card: card to erase + * @from: first sector to erase + * @nr: number of sectors to erase + * @arg: erase command argument (SD supports only %MMC_ERASE_ARG) + * + * Caller must claim host before calling this function. + */ +int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr, + unsigned int arg) +{ + unsigned int rem, to = from + nr; + + if (!(card->host->caps & MMC_CAP_ERASE) || + !(card->csd.cmdclass & CCC_ERASE)) + return -EOPNOTSUPP; + + if (!card->erase_size) + return -EOPNOTSUPP; + + if (mmc_card_sd(card) && arg != MMC_ERASE_ARG) + return -EOPNOTSUPP; + + if ((arg & MMC_SECURE_ARGS) && + !(card->ext_csd.sec_feature_support & EXT_CSD_SEC_ER_EN)) + return -EOPNOTSUPP; + + if ((arg & MMC_TRIM_ARGS) && + !(card->ext_csd.sec_feature_support & EXT_CSD_SEC_GB_CL_EN)) + return -EOPNOTSUPP; + + if (arg == MMC_SECURE_ERASE_ARG) { + if (from % card->erase_size || nr % card->erase_size) + return -EINVAL; + } + + if (arg == MMC_ERASE_ARG) { + rem = from % card->erase_size; + if (rem) { + rem = card->erase_size - rem; + from += rem; + if (nr > rem) + nr -= rem; + else + return 0; + } + rem = nr % card->erase_size; + if (rem) + nr -= rem; + } + + if (nr == 0) + return 0; + + to = from + nr; + + if (to <= from) + return -EINVAL; + + /* 'from' and 'to' are inclusive */ + to -= 1; + + return mmc_do_erase(card, from, to, arg); +} +EXPORT_SYMBOL(mmc_erase); + +int mmc_can_erase(struct mmc_card *card) +{ + if ((card->host->caps & MMC_CAP_ERASE) && + (card->csd.cmdclass & CCC_ERASE) && card->erase_size) + return 1; + return 0; +} +EXPORT_SYMBOL(mmc_can_erase); + +int mmc_can_trim(struct mmc_card *card) +{ + if (card->ext_csd.sec_feature_support & EXT_CSD_SEC_GB_CL_EN) + return 1; + return 0; +} +EXPORT_SYMBOL(mmc_can_trim); + +int mmc_can_secure_erase_trim(struct mmc_card *card) +{ + if (card->ext_csd.sec_feature_support & EXT_CSD_SEC_ER_EN) + return 1; + return 0; +} +EXPORT_SYMBOL(mmc_can_secure_erase_trim); + +int mmc_erase_group_aligned(struct mmc_card *card, unsigned int from, + unsigned int nr) +{ + if (!card->erase_size) + return 0; + if (from % card->erase_size || nr % card->erase_size) + return 0; + return 1; +} +EXPORT_SYMBOL(mmc_erase_group_aligned); void mmc_rescan(struct work_struct *work) { diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index a811c52a1659..9d9eef50e5d1 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -29,6 +29,8 @@ struct mmc_bus_ops { void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops); void mmc_detach_bus(struct mmc_host *host); +void mmc_init_erase(struct mmc_card *card); + void mmc_set_chip_select(struct mmc_host *host, int mode); void mmc_set_clock(struct mmc_host *host, unsigned int hz); void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index ccba3869c029..6909a54c39be 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -108,13 +108,23 @@ static int mmc_decode_cid(struct mmc_card *card) return 0; } +static void mmc_set_erase_size(struct mmc_card *card) +{ + if (card->ext_csd.erase_group_def & 1) + card->erase_size = card->ext_csd.hc_erase_size; + else + card->erase_size = card->csd.erase_size; + + mmc_init_erase(card); +} + /* * Given a 128-bit response, decode to our card CSD structure. */ static int mmc_decode_csd(struct mmc_card *card) { struct mmc_csd *csd = &card->csd; - unsigned int e, m; + unsigned int e, m, a, b; u32 *resp = card->raw_csd; /* @@ -152,6 +162,13 @@ static int mmc_decode_csd(struct mmc_card *card) csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4); csd->write_partial = UNSTUFF_BITS(resp, 21, 1); + if (csd->write_blkbits >= 9) { + a = UNSTUFF_BITS(resp, 42, 5); + b = UNSTUFF_BITS(resp, 37, 5); + csd->erase_size = (a + 1) * (b + 1); + csd->erase_size <<= csd->write_blkbits - 9; + } + return 0; } @@ -261,8 +278,30 @@ static int mmc_read_ext_csd(struct mmc_card *card) if (sa_shift > 0 && sa_shift <= 0x17) card->ext_csd.sa_timeout = 1 << ext_csd[EXT_CSD_S_A_TIMEOUT]; + card->ext_csd.erase_group_def = + ext_csd[EXT_CSD_ERASE_GROUP_DEF]; + card->ext_csd.hc_erase_timeout = 300 * + ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT]; + card->ext_csd.hc_erase_size = + ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] << 10; + } + + if (card->ext_csd.rev >= 4) { + card->ext_csd.sec_trim_mult = + ext_csd[EXT_CSD_SEC_TRIM_MULT]; + card->ext_csd.sec_erase_mult = + ext_csd[EXT_CSD_SEC_ERASE_MULT]; + card->ext_csd.sec_feature_support = + ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT]; + card->ext_csd.trim_timeout = 300 * + ext_csd[EXT_CSD_TRIM_MULT]; } + if (ext_csd[EXT_CSD_ERASED_MEM_CONT]) + card->erased_byte = 0xFF; + else + card->erased_byte = 0x0; + out: kfree(ext_csd); @@ -274,6 +313,8 @@ MMC_DEV_ATTR(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1], MMC_DEV_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1], card->raw_csd[2], card->raw_csd[3]); MMC_DEV_ATTR(date, "%02d/%04d\n", card->cid.month, card->cid.year); +MMC_DEV_ATTR(erase_size, "%u\n", card->erase_size << 9); +MMC_DEV_ATTR(preferred_erase_size, "%u\n", card->pref_erase << 9); MMC_DEV_ATTR(fwrev, "0x%x\n", card->cid.fwrev); MMC_DEV_ATTR(hwrev, "0x%x\n", card->cid.hwrev); MMC_DEV_ATTR(manfid, "0x%06x\n", card->cid.manfid); @@ -285,6 +326,8 @@ static struct attribute *mmc_std_attrs[] = { &dev_attr_cid.attr, &dev_attr_csd.attr, &dev_attr_date.attr, + &dev_attr_erase_size.attr, + &dev_attr_preferred_erase_size.attr, &dev_attr_fwrev.attr, &dev_attr_hwrev.attr, &dev_attr_manfid.attr, @@ -421,6 +464,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, err = mmc_read_ext_csd(card); if (err) goto free_card; + /* Erase size depends on CSD and Extended CSD */ + mmc_set_erase_size(card); } /* diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index e6d7d9fab446..0f5241085557 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -119,6 +119,13 @@ static int mmc_decode_csd(struct mmc_card *card) csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3); csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4); csd->write_partial = UNSTUFF_BITS(resp, 21, 1); + + if (UNSTUFF_BITS(resp, 46, 1)) { + csd->erase_size = 1; + } else if (csd->write_blkbits >= 9) { + csd->erase_size = UNSTUFF_BITS(resp, 39, 7) + 1; + csd->erase_size <<= csd->write_blkbits - 9; + } break; case 1: /* @@ -147,6 +154,7 @@ static int mmc_decode_csd(struct mmc_card *card) csd->r2w_factor = 4; /* Unused */ csd->write_blkbits = 9; csd->write_partial = 0; + csd->erase_size = 1; break; default: printk(KERN_ERR "%s: unrecognised CSD structure version %d\n", @@ -154,6 +162,8 @@ static int mmc_decode_csd(struct mmc_card *card) return -EINVAL; } + card->erase_size = csd->erase_size; + return 0; } @@ -179,9 +189,67 @@ 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 (UNSTUFF_BITS(resp, 55, 1)) + card->erased_byte = 0xFF; + else + card->erased_byte = 0x0; + return 0; } +/* + * Fetch and process SD Status register. + */ +static int mmc_read_ssr(struct mmc_card *card) +{ + unsigned int au, es, et, eo; + int err, i; + u32 *ssr; + + if (!(card->csd.cmdclass & CCC_APP_SPEC)) { + printk(KERN_WARNING "%s: card lacks mandatory SD Status " + "function.\n", mmc_hostname(card->host)); + return 0; + } + + ssr = kmalloc(64, GFP_KERNEL); + if (!ssr) + return -ENOMEM; + + err = mmc_app_sd_status(card, ssr); + if (err) { + printk(KERN_WARNING "%s: problem reading SD Status " + "register.\n", mmc_hostname(card->host)); + err = 0; + goto out; + } + + for (i = 0; i < 16; i++) + ssr[i] = be32_to_cpu(ssr[i]); + + /* + * UNSTUFF_BITS only works with four u32s so we have to offset the + * bitfield positions accordingly. + */ + au = UNSTUFF_BITS(ssr, 428 - 384, 4); + if (au > 0 || au <= 9) { + card->ssr.au = 1 << (au + 4); + es = UNSTUFF_BITS(ssr, 408 - 384, 16); + et = UNSTUFF_BITS(ssr, 402 - 384, 6); + eo = UNSTUFF_BITS(ssr, 400 - 384, 2); + if (es && et) { + card->ssr.erase_timeout = (et * 1000) / es; + card->ssr.erase_offset = eo * 1000; + } + } else { + printk(KERN_WARNING "%s: SD Status: Invalid Allocation Unit " + "size.\n", mmc_hostname(card->host)); + } +out: + kfree(ssr); + return err; +} + /* * Fetches and decodes switch information */ @@ -289,6 +357,8 @@ MMC_DEV_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1], card->raw_csd[2], card->raw_csd[3]); MMC_DEV_ATTR(scr, "%08x%08x\n", card->raw_scr[0], card->raw_scr[1]); MMC_DEV_ATTR(date, "%02d/%04d\n", card->cid.month, card->cid.year); +MMC_DEV_ATTR(erase_size, "%u\n", card->erase_size << 9); +MMC_DEV_ATTR(preferred_erase_size, "%u\n", card->pref_erase << 9); MMC_DEV_ATTR(fwrev, "0x%x\n", card->cid.fwrev); MMC_DEV_ATTR(hwrev, "0x%x\n", card->cid.hwrev); MMC_DEV_ATTR(manfid, "0x%06x\n", card->cid.manfid); @@ -302,6 +372,8 @@ static struct attribute *sd_std_attrs[] = { &dev_attr_csd.attr, &dev_attr_scr.attr, &dev_attr_date.attr, + &dev_attr_erase_size.attr, + &dev_attr_preferred_erase_size.attr, &dev_attr_fwrev.attr, &dev_attr_hwrev.attr, &dev_attr_manfid.attr, @@ -396,6 +468,16 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card, if (err) return err; + /* + * Fetch and process SD Status register. + */ + err = mmc_read_ssr(card); + if (err) + return err; + + /* Erase init depends on CSD and SSR */ + mmc_init_erase(card); + /* * Fetch switch information from card. */ diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c index 63772e7e7608..797cdb5887fd 100644 --- a/drivers/mmc/core/sd_ops.c +++ b/drivers/mmc/core/sd_ops.c @@ -346,3 +346,51 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group, return 0; } +int mmc_app_sd_status(struct mmc_card *card, void *ssr) +{ + int err; + struct mmc_request mrq; + struct mmc_command cmd; + struct mmc_data data; + struct scatterlist sg; + + BUG_ON(!card); + BUG_ON(!card->host); + BUG_ON(!ssr); + + /* NOTE: caller guarantees ssr is heap-allocated */ + + err = mmc_app_cmd(card->host, card); + if (err) + 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; + mrq.data = &data; + + cmd.opcode = SD_APP_SD_STATUS; + cmd.arg = 0; + cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_ADTC; + + data.blksz = 64; + data.blocks = 1; + data.flags = MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; + + sg_init_one(&sg, ssr, 64); + + mmc_set_data_timeout(&data, card); + + mmc_wait_for_req(card->host, &mrq); + + if (cmd.error) + return cmd.error; + if (data.error) + return data.error; + + return 0; +} diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h index 9742d8a30664..ffc2305d905f 100644 --- a/drivers/mmc/core/sd_ops.h +++ b/drivers/mmc/core/sd_ops.h @@ -19,6 +19,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 mmc_sd_switch(struct mmc_card *card, int mode, int group, u8 value, u8 *resp); +int mmc_app_sd_status(struct mmc_card *card, void *ssr); #endif diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 4d893eaf8174..6b7525099e56 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -31,6 +31,7 @@ struct mmc_csd { unsigned int tacc_ns; unsigned int r2w_factor; unsigned int max_dtr; + unsigned int erase_size; /* In sectors */ unsigned int read_blkbits; unsigned int write_blkbits; unsigned int capacity; @@ -42,9 +43,16 @@ struct mmc_csd { struct mmc_ext_csd { u8 rev; + u8 erase_group_def; + u8 sec_feature_support; unsigned int sa_timeout; /* Units: 100ns */ unsigned int hs_max_dtr; unsigned int sectors; + unsigned int hc_erase_size; /* In sectors */ + unsigned int hc_erase_timeout; /* In milliseconds */ + unsigned int sec_trim_mult; /* Secure trim multiplier */ + unsigned int sec_erase_mult; /* Secure erase multiplier */ + unsigned int trim_timeout; /* In milliseconds */ }; struct sd_scr { @@ -54,6 +62,12 @@ struct sd_scr { #define SD_SCR_BUS_WIDTH_4 (1<<2) }; +struct sd_ssr { + unsigned int au; /* In sectors */ + unsigned int erase_timeout; /* In milliseconds */ + unsigned int erase_offset; /* In milliseconds */ +}; + struct sd_switch_caps { unsigned int hs_max_dtr; }; @@ -106,6 +120,11 @@ struct mmc_card { #define MMC_QUIRK_NONSTD_SDIO (1<<2) /* non-standard SDIO card attached */ /* (missing CIA registers) */ + unsigned int erase_size; /* erase size in sectors */ + unsigned int erase_shift; /* if erase unit is power 2 */ + unsigned int pref_erase; /* in sectors */ + u8 erased_byte; /* value of erased bytes */ + u32 raw_cid[4]; /* raw card CID */ u32 raw_csd[4]; /* raw card CSD */ u32 raw_scr[2]; /* raw card SCR */ @@ -113,6 +132,7 @@ struct mmc_card { struct mmc_csd csd; /* card specific */ struct mmc_ext_csd ext_csd; /* mmc v4 extended card specific */ struct sd_scr scr; /* extra SD information */ + struct sd_ssr ssr; /* yet more SD information */ struct sd_switch_caps sw_caps; /* switch (CMD6) caps */ unsigned int sdio_funcs; /* number of SDIO functions */ diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index e4898e9eeb59..7429033acb66 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -92,6 +92,8 @@ struct mmc_command { * actively failing requests */ + unsigned int erase_timeout; /* in milliseconds */ + struct mmc_data *data; /* data segment associated with cmd */ struct mmc_request *mrq; /* associated request */ }; @@ -134,6 +136,23 @@ extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int); extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *, struct mmc_command *, int); +#define MMC_ERASE_ARG 0x00000000 +#define MMC_SECURE_ERASE_ARG 0x80000000 +#define MMC_TRIM_ARG 0x00000001 +#define MMC_SECURE_TRIM1_ARG 0x80000001 +#define MMC_SECURE_TRIM2_ARG 0x80008000 + +#define MMC_SECURE_ARGS 0x80000000 +#define MMC_TRIM_ARGS 0x00008001 + +extern int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr, + unsigned int arg); +extern int mmc_can_erase(struct mmc_card *card); +extern int mmc_can_trim(struct mmc_card *card); +extern int mmc_can_secure_erase_trim(struct mmc_card *card); +extern int mmc_erase_group_aligned(struct mmc_card *card, unsigned int from, + unsigned int nr); + extern void mmc_set_data_timeout(struct mmc_data *, const struct mmc_card *); extern unsigned int mmc_align_data_size(struct mmc_card *, unsigned int); diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 513ff0376b09..1575b52c3bfa 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -156,6 +156,7 @@ struct mmc_host { #define MMC_CAP_DISABLE (1 << 7) /* Can the host be disabled */ #define MMC_CAP_NONREMOVABLE (1 << 8) /* Nonremovable e.g. eMMC */ #define MMC_CAP_WAIT_WHILE_BUSY (1 << 9) /* Waits while card is busy */ +#define MMC_CAP_ERASE (1 << 10) /* Allow erase/trim commands */ mmc_pm_flag_t pm_caps; /* supported pm features */ diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index 52ce98866287..dd11ae51fb68 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -251,13 +251,21 @@ struct _mmc_csd { * EXT_CSD fields */ -#define EXT_CSD_BUS_WIDTH 183 /* R/W */ -#define EXT_CSD_HS_TIMING 185 /* R/W */ -#define EXT_CSD_CARD_TYPE 196 /* RO */ -#define EXT_CSD_STRUCTURE 194 /* RO */ -#define EXT_CSD_REV 192 /* RO */ -#define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */ -#define EXT_CSD_S_A_TIMEOUT 217 +#define EXT_CSD_ERASE_GROUP_DEF 175 /* R/W */ +#define EXT_CSD_ERASED_MEM_CONT 181 /* RO */ +#define EXT_CSD_BUS_WIDTH 183 /* R/W */ +#define EXT_CSD_HS_TIMING 185 /* R/W */ +#define EXT_CSD_REV 192 /* RO */ +#define EXT_CSD_STRUCTURE 194 /* RO */ +#define EXT_CSD_CARD_TYPE 196 /* RO */ +#define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */ +#define EXT_CSD_S_A_TIMEOUT 217 /* RO */ +#define EXT_CSD_ERASE_TIMEOUT_MULT 223 /* RO */ +#define EXT_CSD_HC_ERASE_GRP_SIZE 224 /* RO */ +#define EXT_CSD_SEC_TRIM_MULT 229 /* RO */ +#define EXT_CSD_SEC_ERASE_MULT 230 /* RO */ +#define EXT_CSD_SEC_FEATURE_SUPPORT 231 /* RO */ +#define EXT_CSD_TRIM_MULT 232 /* RO */ /* * EXT_CSD field definitions @@ -275,6 +283,10 @@ struct _mmc_csd { #define EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */ #define EXT_CSD_BUS_WIDTH_8 2 /* Card is in 8 bit mode */ +#define EXT_CSD_SEC_ER_EN BIT(0) +#define EXT_CSD_SEC_BD_BLK_EN BIT(2) +#define EXT_CSD_SEC_GB_CL_EN BIT(4) + /* * MMC_SWITCH access modes */ diff --git a/include/linux/mmc/sd.h b/include/linux/mmc/sd.h index f310062cffb4..3fd85e088cc3 100644 --- a/include/linux/mmc/sd.h +++ b/include/linux/mmc/sd.h @@ -21,8 +21,13 @@ /* class 10 */ #define SD_SWITCH 6 /* adtc [31:0] See below R1 */ + /* class 5 */ +#define SD_ERASE_WR_BLK_START 32 /* ac [31:0] data addr R1 */ +#define SD_ERASE_WR_BLK_END 33 /* ac [31:0] data addr R1 */ + /* Application commands */ #define SD_APP_SET_BUS_WIDTH 6 /* ac [1:0] bus width R1 */ +#define SD_APP_SD_STATUS 13 /* adtc R1 */ #define SD_APP_SEND_NUM_WR_BLKS 22 /* adtc R1 */ #define SD_APP_OP_COND 41 /* bcr [31:0] OCR R3 */ #define SD_APP_SEND_SCR 51 /* adtc R1 */ -- cgit v1.2.3 From f3c65b2870f2481f3646bc410a58a12989ecc704 Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Thu, 9 Sep 2010 16:37:24 -0700 Subject: mmc: avoid getting CID on SDIO-only cards The introduction of support for SD combo cards breaks the initialization of all CSR SDIO chips. The GO_IDLE (CMD0) in mmc_sd_get_cid() causes CSR chips to be reset (this is non-standard behavior). When initializing an SDIO card check for a combo card by using the memory present bit in the R4 response to IO_SEND_OP_COND (CMD5). This avoids the call to mmc_sd_get_cid() on an SDIO-only card. Signed-off-by: David Vrabel Acked-by: Michal Mirolaw Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/mmc/core/sdio.c | 5 ++--- include/linux/mmc/sdio.h | 2 ++ 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'include/linux/mmc') diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index bd2755e8d9a3..f332c52968b7 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -362,9 +362,8 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, goto err; } - err = mmc_sd_get_cid(host, host->ocr & ocr, card->raw_cid); - - if (!err) { + if (ocr & R4_MEMORY_PRESENT + && mmc_sd_get_cid(host, host->ocr & ocr, card->raw_cid) == 0) { card->type = MMC_TYPE_SD_COMBO; if (oldcard && (oldcard->type != MMC_TYPE_SD_COMBO || diff --git a/include/linux/mmc/sdio.h b/include/linux/mmc/sdio.h index 329a8faa6e37..245cdacee544 100644 --- a/include/linux/mmc/sdio.h +++ b/include/linux/mmc/sdio.h @@ -38,6 +38,8 @@ * [8:0] Byte/block count */ +#define R4_MEMORY_PRESENT (1 << 27) + /* SDIO status in R5 Type -- cgit v1.2.3 From fb3d8eb47ce377d6d7a8fc58b8046ea9eb376a28 Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Mon, 9 Aug 2010 17:42:21 -0400 Subject: Bluetooth: Support SDIO devices that are AMP controllers Signed-off-by: David Vrabel Signed-off-by: Marcel Holtmann Signed-off-by: Gustavo F. Padovan --- drivers/bluetooth/btsdio.c | 8 ++++++++ include/linux/mmc/sdio_ids.h | 1 + 2 files changed, 9 insertions(+) (limited to 'include/linux/mmc') diff --git a/drivers/bluetooth/btsdio.c b/drivers/bluetooth/btsdio.c index 76e5127884f0..792e32d29a1d 100644 --- a/drivers/bluetooth/btsdio.c +++ b/drivers/bluetooth/btsdio.c @@ -46,6 +46,9 @@ static const struct sdio_device_id btsdio_table[] = { /* Generic Bluetooth Type-B SDIO device */ { SDIO_DEVICE_CLASS(SDIO_CLASS_BT_B) }, + /* Generic Bluetooth AMP controller */ + { SDIO_DEVICE_CLASS(SDIO_CLASS_BT_AMP) }, + { } /* Terminating entry */ }; @@ -329,6 +332,11 @@ static int btsdio_probe(struct sdio_func *func, hdev->bus = HCI_SDIO; hdev->driver_data = data; + if (id->class == SDIO_CLASS_BT_AMP) + hdev->dev_type = HCI_AMP; + else + hdev->dev_type = HCI_BREDR; + data->hdev = hdev; SET_HCIDEV_DEV(hdev, &func->dev); diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h index 33b2ea09a4ad..a36ab3bc7b03 100644 --- a/include/linux/mmc/sdio_ids.h +++ b/include/linux/mmc/sdio_ids.h @@ -18,6 +18,7 @@ #define SDIO_CLASS_PHS 0x06 /* PHS standard interface */ #define SDIO_CLASS_WLAN 0x07 /* WLAN interface */ #define SDIO_CLASS_ATA 0x08 /* Embedded SDIO-ATA std interface */ +#define SDIO_CLASS_BT_AMP 0x09 /* Type-A Bluetooth AMP interface */ /* * Vendors and devices. Sort key: vendor first, device next. -- cgit v1.2.3 From a36274e0184193e393fb82957925c3981a6b0477 Mon Sep 17 00:00:00 2001 From: "Martin K. Petersen" Date: Fri, 10 Sep 2010 01:33:59 -0400 Subject: mmc: Remove distinction between hw and phys segments We have deprecated the distinction between hardware and physical segments in the block layer. Consolidate the two limits into one in drivers/mmc/. Signed-off-by: Martin K. Petersen Signed-off-by: Chris Ball --- drivers/mmc/card/queue.c | 8 ++++---- drivers/mmc/core/host.c | 3 +-- drivers/mmc/host/at91_mci.c | 3 +-- drivers/mmc/host/atmel-mci.c | 3 +-- drivers/mmc/host/au1xmmc.c | 2 +- drivers/mmc/host/bfin_sdh.c | 2 +- drivers/mmc/host/davinci_mmc.c | 8 +++----- drivers/mmc/host/imxmmc.c | 3 +-- drivers/mmc/host/jz4740_mmc.c | 3 +-- drivers/mmc/host/mmc_spi.c | 3 +-- drivers/mmc/host/mmci.c | 3 +-- drivers/mmc/host/msm_sdcc.c | 3 +-- drivers/mmc/host/mvsdio.c | 3 +-- drivers/mmc/host/mxcmmc.c | 3 +-- drivers/mmc/host/omap.c | 3 +-- drivers/mmc/host/omap_hsmmc.c | 3 +-- drivers/mmc/host/pxamci.c | 2 +- drivers/mmc/host/s3cmci.c | 3 +-- drivers/mmc/host/sdhci.c | 7 +++---- drivers/mmc/host/sh_mmcif.c | 3 +-- drivers/mmc/host/tifm_sd.c | 3 +-- drivers/mmc/host/via-sdmmc.c | 3 +-- drivers/mmc/host/wbsd.c | 3 +-- include/linux/mmc/host.h | 3 +-- 24 files changed, 31 insertions(+), 52 deletions(-) (limited to 'include/linux/mmc') diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c index 9c0b42bfe089..7c3392e50722 100644 --- a/drivers/mmc/card/queue.c +++ b/drivers/mmc/card/queue.c @@ -146,7 +146,7 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock } #ifdef CONFIG_MMC_BLOCK_BOUNCE - if (host->max_hw_segs == 1) { + if (host->max_segs == 1) { unsigned int bouncesz; bouncesz = MMC_QUEUE_BOUNCESZ; @@ -196,16 +196,16 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock blk_queue_bounce_limit(mq->queue, limit); blk_queue_max_hw_sectors(mq->queue, min(host->max_blk_count, host->max_req_size / 512)); - blk_queue_max_segments(mq->queue, host->max_hw_segs); + blk_queue_max_segments(mq->queue, host->max_segs); blk_queue_max_segment_size(mq->queue, host->max_seg_size); mq->sg = kmalloc(sizeof(struct scatterlist) * - host->max_phys_segs, GFP_KERNEL); + host->max_segs, GFP_KERNEL); if (!mq->sg) { ret = -ENOMEM; goto cleanup_queue; } - sg_init_table(mq->sg, host->max_phys_segs); + sg_init_table(mq->sg, host->max_segs); } init_MUTEX(&mq->thread_sem); diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index d80cfdc8edd2..10b8af27e03a 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -94,8 +94,7 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) * By default, hosts do not support SGIO or large requests. * They have to set these according to their abilities. */ - host->max_hw_segs = 1; - host->max_phys_segs = 1; + host->max_segs = 1; host->max_seg_size = PAGE_CACHE_SIZE; host->max_req_size = PAGE_CACHE_SIZE; diff --git a/drivers/mmc/host/at91_mci.c b/drivers/mmc/host/at91_mci.c index 339e5913e5cc..591ab540b407 100644 --- a/drivers/mmc/host/at91_mci.c +++ b/drivers/mmc/host/at91_mci.c @@ -947,8 +947,7 @@ static int __init at91_mci_probe(struct platform_device *pdev) mmc->max_blk_size = MCI_MAXBLKSIZE; mmc->max_blk_count = MCI_BLKATONCE; mmc->max_req_size = MCI_BUFSIZE; - mmc->max_phys_segs = MCI_BLKATONCE; - mmc->max_hw_segs = MCI_BLKATONCE; + mmc->max_segs = MCI_BLKATONCE; mmc->max_seg_size = MCI_BUFSIZE; host = mmc_priv(mmc); diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 1d4e5464ea2f..301351a5d838 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -1618,8 +1618,7 @@ static int __init atmci_init_slot(struct atmel_mci *host, if (slot_data->bus_width >= 4) mmc->caps |= MMC_CAP_4_BIT_DATA; - mmc->max_hw_segs = 64; - mmc->max_phys_segs = 64; + mmc->max_segs = 64; mmc->max_req_size = 32768 * 512; mmc->max_blk_size = 32768; mmc->max_blk_count = 512; diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c index e14b866b270f..41e5a60493ad 100644 --- a/drivers/mmc/host/au1xmmc.c +++ b/drivers/mmc/host/au1xmmc.c @@ -998,7 +998,7 @@ static int __devinit au1xmmc_probe(struct platform_device *pdev) mmc->f_max = 24000000; mmc->max_seg_size = AU1XMMC_DESCRIPTOR_SIZE; - mmc->max_phys_segs = AU1XMMC_DESCRIPTOR_COUNT; + mmc->max_segs = AU1XMMC_DESCRIPTOR_COUNT; mmc->max_blk_size = 2048; mmc->max_blk_count = 512; diff --git a/drivers/mmc/host/bfin_sdh.c b/drivers/mmc/host/bfin_sdh.c index 4b0e677d7295..bac7d62866b7 100644 --- a/drivers/mmc/host/bfin_sdh.c +++ b/drivers/mmc/host/bfin_sdh.c @@ -469,7 +469,7 @@ static int __devinit sdh_probe(struct platform_device *pdev) } mmc->ops = &sdh_ops; - mmc->max_phys_segs = 32; + mmc->max_segs = 32; mmc->max_seg_size = 1 << 16; mmc->max_blk_size = 1 << 11; mmc->max_blk_count = 1 << 11; diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c index 33d9f1b00862..e15547cf701f 100644 --- a/drivers/mmc/host/davinci_mmc.c +++ b/drivers/mmc/host/davinci_mmc.c @@ -138,7 +138,7 @@ /* * One scatterlist dma "segment" is at most MAX_CCNT rw_threshold units, * and we handle up to MAX_NR_SG segments. MMC_BLOCK_BOUNCE kicks in only - * for drivers with max_hw_segs == 1, making the segments bigger (64KB) + * for drivers with max_segs == 1, making the segments bigger (64KB) * than the page or two that's otherwise typical. nr_sg (passed from * platform data) == 16 gives at least the same throughput boost, using * EDMA transfer linkage instead of spending CPU time copying pages. @@ -1239,8 +1239,7 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev) * Each hw_seg uses one EDMA parameter RAM slot, always one * channel and then usually some linked slots. */ - mmc->max_hw_segs = 1 + host->n_link; - mmc->max_phys_segs = mmc->max_hw_segs; + mmc->max_segs = 1 + host->n_link; /* EDMA limit per hw segment (one or two MBytes) */ mmc->max_seg_size = MAX_CCNT * rw_threshold; @@ -1250,8 +1249,7 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev) mmc->max_blk_count = 65535; /* NBLK is 16 bits */ mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; - dev_dbg(mmc_dev(host->mmc), "max_phys_segs=%d\n", mmc->max_phys_segs); - dev_dbg(mmc_dev(host->mmc), "max_hw_segs=%d\n", mmc->max_hw_segs); + dev_dbg(mmc_dev(host->mmc), "max_segs=%d\n", mmc->max_segs); dev_dbg(mmc_dev(host->mmc), "max_blk_size=%d\n", mmc->max_blk_size); dev_dbg(mmc_dev(host->mmc), "max_req_size=%d\n", mmc->max_req_size); dev_dbg(mmc_dev(host->mmc), "max_seg_size=%d\n", mmc->max_seg_size); diff --git a/drivers/mmc/host/imxmmc.c b/drivers/mmc/host/imxmmc.c index 5a950b16d9e6..881f7ba545ae 100644 --- a/drivers/mmc/host/imxmmc.c +++ b/drivers/mmc/host/imxmmc.c @@ -966,8 +966,7 @@ static int __init imxmci_probe(struct platform_device *pdev) mmc->caps = MMC_CAP_4_BIT_DATA; /* MMC core transfer sizes tunable parameters */ - mmc->max_hw_segs = 64; - mmc->max_phys_segs = 64; + mmc->max_segs = 64; mmc->max_seg_size = 64*512; /* default PAGE_CACHE_SIZE */ mmc->max_req_size = 64*512; /* default PAGE_CACHE_SIZE */ mmc->max_blk_size = 2048; diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c index ad4f9870e3ca..b3a0ab0e4c2b 100644 --- a/drivers/mmc/host/jz4740_mmc.c +++ b/drivers/mmc/host/jz4740_mmc.c @@ -876,8 +876,7 @@ static int __devinit jz4740_mmc_probe(struct platform_device* pdev) mmc->max_blk_count = (1 << 15) - 1; mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; - mmc->max_phys_segs = 128; - mmc->max_hw_segs = 128; + mmc->max_segs = 128; mmc->max_seg_size = mmc->max_req_size; host->mmc = mmc; diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index 62a35822003e..5b0b50636ec4 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -1345,8 +1345,7 @@ static int mmc_spi_probe(struct spi_device *spi) mmc->ops = &mmc_spi_ops; mmc->max_blk_size = MMC_SPI_BLOCKSIZE; - mmc->max_hw_segs = MMC_SPI_BLOCKSATONCE; - mmc->max_phys_segs = MMC_SPI_BLOCKSATONCE; + mmc->max_segs = MMC_SPI_BLOCKSATONCE; mmc->max_req_size = MMC_SPI_BLOCKSATONCE * MMC_SPI_BLOCKSIZE; mmc->max_blk_count = MMC_SPI_BLOCKSATONCE; diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index f2e02d7d9f3d..5f2e72d38b5d 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -734,8 +734,7 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id) /* * We can do SGIO */ - mmc->max_hw_segs = 16; - mmc->max_phys_segs = NR_SG; + mmc->max_segs = NR_SG; /* * Since only a certain number of bits are valid in the data length diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index ff7752348b11..1290d14c5839 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -1164,8 +1164,7 @@ msmsdcc_probe(struct platform_device *pdev) mmc->caps |= MMC_CAP_SDIO_IRQ; mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED; - mmc->max_phys_segs = NR_SG; - mmc->max_hw_segs = NR_SG; + mmc->max_segs = NR_SG; mmc->max_blk_size = 4096; /* MCI_DATA_CTL BLOCKSIZE up to 4096 */ mmc->max_blk_count = 65536; diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c index 366eefa77c5a..a5bf60e01af4 100644 --- a/drivers/mmc/host/mvsdio.c +++ b/drivers/mmc/host/mvsdio.c @@ -742,8 +742,7 @@ static int __init mvsd_probe(struct platform_device *pdev) mmc->max_blk_size = 2048; mmc->max_blk_count = 65535; - mmc->max_hw_segs = 1; - mmc->max_phys_segs = 1; + mmc->max_segs = 1; mmc->max_seg_size = mmc->max_blk_size * mmc->max_blk_count; mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c index 350f78e86245..bdd2cbb87cba 100644 --- a/drivers/mmc/host/mxcmmc.c +++ b/drivers/mmc/host/mxcmmc.c @@ -790,8 +790,7 @@ static int mxcmci_probe(struct platform_device *pdev) mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ; /* MMC core transfer sizes tunable parameters */ - mmc->max_hw_segs = 64; - mmc->max_phys_segs = 64; + mmc->max_segs = 64; mmc->max_blk_size = 2048; mmc->max_blk_count = 65535; mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index d98ddcfac5e5..0c7e37f496ef 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -1335,8 +1335,7 @@ static int __init mmc_omap_new_slot(struct mmc_omap_host *host, int id) * NOTE max_seg_size assumption that small blocks aren't * normally used (except e.g. for reading SD registers). */ - mmc->max_phys_segs = 32; - mmc->max_hw_segs = 32; + mmc->max_segs = 32; mmc->max_blk_size = 2048; /* BLEN is 11 bits (+1) */ mmc->max_blk_count = 2048; /* NBLK is 11 bits (+1) */ mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 4526d2791f29..03c26e026506 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -2105,8 +2105,7 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) /* Since we do only SG emulation, we can have as many segs * as we want. */ - mmc->max_phys_segs = 1024; - mmc->max_hw_segs = 1024; + mmc->max_segs = 1024; mmc->max_blk_size = 512; /* Block Length at max can be 1024 */ mmc->max_blk_count = 0xFFFF; /* No. of Blocks is 16 bits */ diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index 0a4e43f37140..b7dfcac31e8a 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -576,7 +576,7 @@ static int pxamci_probe(struct platform_device *pdev) * We can do SG-DMA, but we don't because we never know how much * data we successfully wrote to the card. */ - mmc->max_phys_segs = NR_SG; + mmc->max_segs = NR_SG; /* * Our hardware DMA can handle a maximum of one page per SG entry. diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c index 976330de379e..1ccd4b256cee 100644 --- a/drivers/mmc/host/s3cmci.c +++ b/drivers/mmc/host/s3cmci.c @@ -1736,8 +1736,7 @@ static int __devinit s3cmci_probe(struct platform_device *pdev) mmc->max_req_size = 4095 * 512; mmc->max_seg_size = mmc->max_req_size; - mmc->max_phys_segs = 128; - mmc->max_hw_segs = 128; + mmc->max_segs = 128; dbg(host, dbg_debug, "probe: mode:%s mapped mci_base:%p irq:%u irq_cd:%u dma:%u.\n", diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 401527d273b5..f608626c4821 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1850,12 +1850,11 @@ int sdhci_add_host(struct sdhci_host *host) * can do scatter/gather or not. */ if (host->flags & SDHCI_USE_ADMA) - mmc->max_hw_segs = 128; + mmc->max_segs = 128; else if (host->flags & SDHCI_USE_SDMA) - mmc->max_hw_segs = 1; + mmc->max_segs = 1; else /* PIO */ - mmc->max_hw_segs = 128; - mmc->max_phys_segs = 128; + mmc->max_segs = 128; /* * Maximum number of sectors in one transfer. Limited by DMA boundary diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 5d3f824bb5a3..0f06b8002814 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -846,8 +846,7 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) mmc->caps = MMC_CAP_MMC_HIGHSPEED; if (pd->caps) mmc->caps |= pd->caps; - mmc->max_phys_segs = 128; - mmc->max_hw_segs = 128; + mmc->max_segs = 128; mmc->max_blk_size = 512; mmc->max_blk_count = 65535; mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c index cec99958b652..457c26ea09de 100644 --- a/drivers/mmc/host/tifm_sd.c +++ b/drivers/mmc/host/tifm_sd.c @@ -978,11 +978,10 @@ static int tifm_sd_probe(struct tifm_dev *sock) mmc->f_max = 24000000; mmc->max_blk_count = 2048; - mmc->max_hw_segs = mmc->max_blk_count; + mmc->max_segs = mmc->max_blk_count; mmc->max_blk_size = min(TIFM_MMCSD_MAX_BLOCK_SIZE, PAGE_SIZE); mmc->max_seg_size = mmc->max_blk_count * mmc->max_blk_size; mmc->max_req_size = mmc->max_seg_size; - mmc->max_phys_segs = mmc->max_hw_segs; sock->card_event = tifm_sd_card_event; sock->data_event = tifm_sd_data_event; diff --git a/drivers/mmc/host/via-sdmmc.c b/drivers/mmc/host/via-sdmmc.c index 19f2d72dbca5..9ed84ddb4780 100644 --- a/drivers/mmc/host/via-sdmmc.c +++ b/drivers/mmc/host/via-sdmmc.c @@ -1050,8 +1050,7 @@ static void via_init_mmc_host(struct via_crdr_mmc_host *host) mmc->ops = &via_sdc_ops; /*Hardware cannot do scatter lists*/ - mmc->max_hw_segs = 1; - mmc->max_phys_segs = 1; + mmc->max_segs = 1; mmc->max_blk_size = VIA_CRDR_MAX_BLOCK_LENGTH; mmc->max_blk_count = VIA_CRDR_MAX_BLOCK_COUNT; diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c index 0012f5d13d28..7fca0a386ba0 100644 --- a/drivers/mmc/host/wbsd.c +++ b/drivers/mmc/host/wbsd.c @@ -1235,8 +1235,7 @@ static int __devinit wbsd_alloc_mmc(struct device *dev) * Maximum number of segments. Worst case is one sector per segment * so this will be 64kB/512. */ - mmc->max_hw_segs = 128; - mmc->max_phys_segs = 128; + mmc->max_segs = 128; /* * Maximum request size. Also limited by 64KiB buffer. diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 1575b52c3bfa..ded401703762 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -162,8 +162,7 @@ struct mmc_host { /* host specific block data */ unsigned int max_seg_size; /* see blk_queue_max_segment_size */ - unsigned short max_hw_segs; /* see blk_queue_max_hw_segments */ - unsigned short max_phys_segs; /* see blk_queue_max_phys_segments */ + unsigned short max_segs; /* see blk_queue_max_segments */ unsigned short unused; unsigned int max_req_size; /* maximum number of bytes in one req */ unsigned int max_blk_size; /* maximum size of one mmc block */ -- cgit v1.2.3 From 453722b9f7366e5b8b46101358dd7bcaef62b59d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 20 Aug 2010 10:46:46 +0300 Subject: mmc: make mmc_dev_to_card() macro public Conversion from struct device to struct mmc_card is used more than in one place. Due to this it's better to have public macro for such thing. Signed-off-by: Andy Shevchenko Cc: Adrian Hunter Cc: Signed-off-by: Andrew Morton Signed-off-by: Chris Ball --- drivers/mmc/card/mmc_test.c | 4 +--- drivers/mmc/core/bus.c | 1 - drivers/mmc/core/bus.h | 2 +- include/linux/mmc/card.h | 2 ++ 4 files changed, 4 insertions(+), 5 deletions(-) (limited to 'include/linux/mmc') diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c index 5dd8576b5c18..82d39bd4f55f 100644 --- a/drivers/mmc/card/mmc_test.c +++ b/drivers/mmc/card/mmc_test.c @@ -1935,12 +1935,10 @@ static ssize_t mmc_test_show(struct device *dev, static ssize_t mmc_test_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct mmc_card *card; + struct mmc_card *card = dev_to_mmc_card(dev); struct mmc_test_card *test; int testcase; - card = container_of(dev, struct mmc_card, dev); - testcase = simple_strtol(buf, NULL, 10); test = kzalloc(sizeof(struct mmc_test_card), GFP_KERNEL); diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index 7cd9749dc21d..27326c411735 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -22,7 +22,6 @@ #include "sdio_cis.h" #include "bus.h" -#define dev_to_mmc_card(d) container_of(d, struct mmc_card, dev) #define to_mmc_driver(d) container_of(d, struct mmc_driver, drv) static ssize_t mmc_type_show(struct device *dev, diff --git a/drivers/mmc/core/bus.h b/drivers/mmc/core/bus.h index 18178766ab46..7813954e3199 100644 --- a/drivers/mmc/core/bus.h +++ b/drivers/mmc/core/bus.h @@ -14,7 +14,7 @@ #define MMC_DEV_ATTR(name, fmt, args...) \ static ssize_t mmc_##name##_show (struct device *dev, struct device_attribute *attr, char *buf) \ { \ - struct mmc_card *card = container_of(dev, struct mmc_card, dev); \ + struct mmc_card *card = dev_to_mmc_card(dev); \ return sprintf(buf, fmt, args); \ } \ static DEVICE_ATTR(name, S_IRUGO, mmc_##name##_show, NULL) diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 6b7525099e56..71acf19ecaf3 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -173,6 +173,8 @@ static inline int mmc_blksz_for_byte_mode(const struct mmc_card *c) #define mmc_card_name(c) ((c)->cid.prod_name) #define mmc_card_id(c) (dev_name(&(c)->dev)) +#define dev_to_mmc_card(d) container_of(d, struct mmc_card, dev) + #define mmc_list_to_card(l) container_of(l, struct mmc_card, node) #define mmc_get_drvdata(c) dev_get_drvdata(&(c)->dev) #define mmc_set_drvdata(c,d) dev_set_drvdata(&(c)->dev, d) -- cgit v1.2.3 From 265cdc900ce93c0cd2465d751fe75ff2e55e126e Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 17 Sep 2010 20:32:25 -0400 Subject: mmc: rename dev_to_mmc_card() to mmc_dev_to_card() Global symbols should use their subsystem name in a prefixed fashion. Signed-off-by: Andy Shevchenko Cc: Signed-off-by: Andrew Morton Signed-off-by: Chris Ball --- drivers/mmc/card/mmc_test.c | 2 +- drivers/mmc/core/bus.c | 14 +++++++------- drivers/mmc/core/bus.h | 2 +- include/linux/mmc/card.h | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) (limited to 'include/linux/mmc') diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c index 82d39bd4f55f..b992725ecbc9 100644 --- a/drivers/mmc/card/mmc_test.c +++ b/drivers/mmc/card/mmc_test.c @@ -1935,7 +1935,7 @@ static ssize_t mmc_test_show(struct device *dev, static ssize_t mmc_test_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct mmc_card *card = dev_to_mmc_card(dev); + struct mmc_card *card = mmc_dev_to_card(dev); struct mmc_test_card *test; int testcase; diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index 27326c411735..e70bd6641cee 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -27,7 +27,7 @@ static ssize_t mmc_type_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct mmc_card *card = dev_to_mmc_card(dev); + struct mmc_card *card = mmc_dev_to_card(dev); switch (card->type) { case MMC_TYPE_MMC: @@ -61,7 +61,7 @@ static int mmc_bus_match(struct device *dev, struct device_driver *drv) static int mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env) { - struct mmc_card *card = dev_to_mmc_card(dev); + struct mmc_card *card = mmc_dev_to_card(dev); const char *type; int retval = 0; @@ -104,7 +104,7 @@ mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env) static int mmc_bus_probe(struct device *dev) { struct mmc_driver *drv = to_mmc_driver(dev->driver); - struct mmc_card *card = dev_to_mmc_card(dev); + struct mmc_card *card = mmc_dev_to_card(dev); return drv->probe(card); } @@ -112,7 +112,7 @@ static int mmc_bus_probe(struct device *dev) static int mmc_bus_remove(struct device *dev) { struct mmc_driver *drv = to_mmc_driver(dev->driver); - struct mmc_card *card = dev_to_mmc_card(dev); + struct mmc_card *card = mmc_dev_to_card(dev); drv->remove(card); @@ -122,7 +122,7 @@ static int mmc_bus_remove(struct device *dev) static int mmc_bus_suspend(struct device *dev, pm_message_t state) { struct mmc_driver *drv = to_mmc_driver(dev->driver); - struct mmc_card *card = dev_to_mmc_card(dev); + struct mmc_card *card = mmc_dev_to_card(dev); int ret = 0; if (dev->driver && drv->suspend) @@ -133,7 +133,7 @@ static int mmc_bus_suspend(struct device *dev, pm_message_t state) static int mmc_bus_resume(struct device *dev) { struct mmc_driver *drv = to_mmc_driver(dev->driver); - struct mmc_card *card = dev_to_mmc_card(dev); + struct mmc_card *card = mmc_dev_to_card(dev); int ret = 0; if (dev->driver && drv->resume) @@ -188,7 +188,7 @@ EXPORT_SYMBOL(mmc_unregister_driver); static void mmc_release_card(struct device *dev) { - struct mmc_card *card = dev_to_mmc_card(dev); + struct mmc_card *card = mmc_dev_to_card(dev); sdio_free_common_cis(card); diff --git a/drivers/mmc/core/bus.h b/drivers/mmc/core/bus.h index 7813954e3199..00a19710b6b4 100644 --- a/drivers/mmc/core/bus.h +++ b/drivers/mmc/core/bus.h @@ -14,7 +14,7 @@ #define MMC_DEV_ATTR(name, fmt, args...) \ static ssize_t mmc_##name##_show (struct device *dev, struct device_attribute *attr, char *buf) \ { \ - struct mmc_card *card = dev_to_mmc_card(dev); \ + struct mmc_card *card = mmc_dev_to_card(dev); \ return sprintf(buf, fmt, args); \ } \ static DEVICE_ATTR(name, S_IRUGO, mmc_##name##_show, NULL) diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 71acf19ecaf3..7bd49234cd88 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -173,7 +173,7 @@ static inline int mmc_blksz_for_byte_mode(const struct mmc_card *c) #define mmc_card_name(c) ((c)->cid.prod_name) #define mmc_card_id(c) (dev_name(&(c)->dev)) -#define dev_to_mmc_card(d) container_of(d, struct mmc_card, dev) +#define mmc_dev_to_card(d) container_of(d, struct mmc_card, dev) #define mmc_list_to_card(l) container_of(l, struct mmc_card, node) #define mmc_get_drvdata(c) dev_get_drvdata(&(c)->dev) -- cgit v1.2.3 From 71d7d3d190fe77588269a8febf93cd739bd91eb3 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Mon, 27 Sep 2010 09:42:19 +0100 Subject: mmc: Add helper function to check if a card is removable There are two checks that need to be made when determining whether a card is removable. A host controller may set MMC_CAP_NONREMOVABLE if the controller does not support removing cards (e.g. eMMC), in which case the card is physically non-removable. Also the 'mmc_assume_removable' module parameter can be configured at module load time, in which case the card may be logically non-removable. A helper function keeps the logic in one place so that code always checks both conditions. Because this new function is likely to be called from modules we now need to export the mmc_assume_removable symbol. Signed-off-by: Matt Fleming Acked-by: Kyungmin Park Tested-by: Jaehoon Chung Acked-by: Wolfram Sang Signed-off-by: Chris Ball --- drivers/mmc/core/core.c | 1 + drivers/mmc/core/core.h | 1 - drivers/mmc/core/mmc.c | 2 +- drivers/mmc/core/sd.c | 2 +- include/linux/mmc/host.h | 8 ++++++++ 5 files changed, 11 insertions(+), 3 deletions(-) (limited to 'include/linux/mmc') diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 09eee6df0653..ab4446c428be 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -58,6 +58,7 @@ int mmc_assume_removable; #else int mmc_assume_removable = 1; #endif +EXPORT_SYMBOL(mmc_assume_removable); module_param_named(removable, mmc_assume_removable, bool, 0644); MODULE_PARM_DESC( removable, diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 9d9eef50e5d1..a2ca770ca89b 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -58,7 +58,6 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr); /* Module parameters */ extern int use_spi_crc; -extern int mmc_assume_removable; /* Debugfs information for hosts and cards */ void mmc_add_host_debugfs(struct mmc_host *host); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 6909a54c39be..6570c03f9c76 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -685,7 +685,7 @@ static void mmc_attach_bus_ops(struct mmc_host *host) { const struct mmc_bus_ops *bus_ops; - if (host->caps & MMC_CAP_NONREMOVABLE || !mmc_assume_removable) + if (!mmc_card_is_removable(host)) bus_ops = &mmc_ops_unsafe; else bus_ops = &mmc_ops; diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 0f5241085557..bc745e1237bf 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -750,7 +750,7 @@ static void mmc_sd_attach_bus_ops(struct mmc_host *host) { const struct mmc_bus_ops *bus_ops; - if (host->caps & MMC_CAP_NONREMOVABLE || !mmc_assume_removable) + if (!mmc_card_is_removable(host)) bus_ops = &mmc_sd_ops_unsafe; else bus_ops = &mmc_sd_ops; diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index ded401703762..2e0fe623df90 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -267,5 +267,13 @@ static inline void mmc_set_disable_delay(struct mmc_host *host, host->disable_delay = disable_delay; } +/* Module parameter */ +extern int mmc_assume_removable; + +static inline int mmc_card_is_removable(struct mmc_host *host) +{ + return !(host->caps & MMC_CAP_NONREMOVABLE) && mmc_assume_removable; +} + #endif -- cgit v1.2.3 From 88ae8b866488031b0e2fc05a27440fefec5e6927 Mon Sep 17 00:00:00 2001 From: Hein Tibosch Date: Mon, 6 Sep 2010 09:37:19 +0800 Subject: mmc: Make ID freq configurable In the latest releases of the mmc driver, the freq during initialization is set to a fixed 400 Khz. This was reportedly too fast for several users. As there doesn't seem to be an ideal frequency which-works-for-all, Pierre suggested to let the driver try several frequencies. This patch implements that idea. It will try mmc-initialization using several frequencies from an array 400, 300, 200 and 100. In case SDIO is broken, it'll still try to detect SDMEM, also at different freqs. Signed-off-by: Hein Tibosch Cc: Pierre Ossman Reviewed-by: Chris Ball Tested-by: Chris Ball Cc: Ben Nizette Cc: Sascha Hauer Cc: Adrian Hunter Cc: Matt Fleming Cc: Signed-off-by: Andrew Morton Signed-off-by: Chris Ball --- drivers/mmc/core/core.c | 102 ++++++++++++++++++++++++++--------------------- include/linux/mmc/host.h | 1 + 2 files changed, 58 insertions(+), 45 deletions(-) (limited to 'include/linux/mmc') diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index ab4446c428be..222466df66ff 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -908,12 +908,7 @@ static void mmc_power_up(struct mmc_host *host) */ mmc_delay(10); - if (host->f_min > 400000) { - pr_warning("%s: Minimum clock frequency too high for " - "identification mode\n", mmc_hostname(host)); - host->ios.clock = host->f_min; - } else - host->ios.clock = 400000; + host->ios.clock = host->f_init; host->ios.power_mode = MMC_POWER_ON; mmc_set_ios(host); @@ -1405,6 +1400,8 @@ void mmc_rescan(struct work_struct *work) u32 ocr; int err; unsigned long flags; + int i; + const unsigned freqs[] = { 400000, 300000, 200000, 100000 }; spin_lock_irqsave(&host->lock, flags); @@ -1444,55 +1441,70 @@ void mmc_rescan(struct work_struct *work) if (host->ops->get_cd && host->ops->get_cd(host) == 0) goto out; - mmc_claim_host(host); + for (i = 0; i < ARRAY_SIZE(freqs); i++) { + mmc_claim_host(host); - mmc_power_up(host); - sdio_reset(host); - mmc_go_idle(host); + if (freqs[i] >= host->f_min) + host->f_init = freqs[i]; + else if (!i || freqs[i-1] > host->f_min) + host->f_init = host->f_min; + else { + mmc_release_host(host); + goto out; + } + pr_info("%s: %s: trying to init card at %u Hz\n", + mmc_hostname(host), __func__, host->f_init); - mmc_send_if_cond(host, host->ocr_avail); + mmc_power_up(host); + sdio_reset(host); + mmc_go_idle(host); - /* - * First we search for SDIO... - */ - err = mmc_send_io_op_cond(host, 0, &ocr); - if (!err) { - if (mmc_attach_sdio(host, ocr)) { - mmc_claim_host(host); - /* try SDMEM (but not MMC) even if SDIO is broken */ - if (mmc_send_app_op_cond(host, 0, &ocr)) - goto out_fail; + mmc_send_if_cond(host, host->ocr_avail); + /* + * First we search for SDIO... + */ + err = mmc_send_io_op_cond(host, 0, &ocr); + if (!err) { + if (mmc_attach_sdio(host, ocr)) { + mmc_claim_host(host); + /* + * Try SDMEM (but not MMC) even if SDIO + * is broken. + */ + if (mmc_send_app_op_cond(host, 0, &ocr)) + goto out_fail; + + if (mmc_attach_sd(host, ocr)) + mmc_power_off(host); + } + goto out; + } + + /* + * ...then normal SD... + */ + err = mmc_send_app_op_cond(host, 0, &ocr); + if (!err) { if (mmc_attach_sd(host, ocr)) mmc_power_off(host); + goto out; } - goto out; - } - /* - * ...then normal SD... - */ - err = mmc_send_app_op_cond(host, 0, &ocr); - if (!err) { - if (mmc_attach_sd(host, ocr)) - mmc_power_off(host); - goto out; - } - - /* - * ...and finally MMC. - */ - err = mmc_send_op_cond(host, 0, &ocr); - if (!err) { - if (mmc_attach_mmc(host, ocr)) - mmc_power_off(host); - goto out; - } + /* + * ...and finally MMC. + */ + err = mmc_send_op_cond(host, 0, &ocr); + if (!err) { + if (mmc_attach_mmc(host, ocr)) + mmc_power_off(host); + goto out; + } out_fail: - mmc_release_host(host); - mmc_power_off(host); - + mmc_release_host(host); + mmc_power_off(host); + } out: if (host->caps & MMC_CAP_NEEDS_POLL) mmc_schedule_delayed_work(&host->detect, HZ); diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 2e0fe623df90..20be040649a9 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -123,6 +123,7 @@ struct mmc_host { const struct mmc_host_ops *ops; unsigned int f_min; unsigned int f_max; + unsigned int f_init; u32 ocr_avail; struct notifier_block pm_notify; -- cgit v1.2.3 From 99fc5131018cbdc3cf42ce09fb394a4e8b053c74 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 29 Sep 2010 01:08:27 -0400 Subject: mmc: Move regulator handling closer to core After discovering a problem in regulator reference counting I took Mark Brown's advice to move the reference count into the MMC core by making the regulator status a member of struct mmc_host. I took this opportunity to also implement NULL versions of the regulator functions so as to rid the driver code from some ugly #ifdef CONFIG_REGULATOR clauses. Signed-off-by: Linus Walleij Reviewed-by: Mark Brown Cc: Liam Girdwood Cc: Tony Lindgren Cc: Adrian Hunter Cc: Robert Jarzmik Cc: Sundar Iyer Cc: Daniel Mack Cc: Pierre Ossman Cc: Matt Fleming Cc: David Brownell Cc: Russell King Cc: Eric Miao Cc: Cliff Brake Cc: Jarkko Lavinen Cc: Signed-off-by: Andrew Morton Signed-off-by: Chris Ball --- drivers/mmc/core/core.c | 26 ++++++++++++++++---------- drivers/mmc/host/mmci.c | 28 ++++++++++++++++++---------- drivers/mmc/host/omap_hsmmc.c | 21 +++++++++++++-------- drivers/mmc/host/pxamci.c | 41 +++++++++++++++++++++++++++++++++-------- include/linux/mmc/host.h | 22 +++++++++++++++++++++- 5 files changed, 101 insertions(+), 37 deletions(-) (limited to 'include/linux/mmc') diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index c5e3c9bf6fdd..46029d5c0364 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -772,8 +772,9 @@ EXPORT_SYMBOL(mmc_regulator_get_ocrmask); /** * mmc_regulator_set_ocr - set regulator to match host->ios voltage - * @vdd_bit: zero for power off, else a bit number (host->ios.vdd) + * @mmc: the host to regulate * @supply: regulator to use + * @vdd_bit: zero for power off, else a bit number (host->ios.vdd) * * Returns zero on success, else negative errno. * @@ -781,15 +782,12 @@ EXPORT_SYMBOL(mmc_regulator_get_ocrmask); * a particular supply voltage. This would normally be called from the * set_ios() method. */ -int mmc_regulator_set_ocr(struct regulator *supply, unsigned short vdd_bit) +int mmc_regulator_set_ocr(struct mmc_host *mmc, + struct regulator *supply, + unsigned short vdd_bit) { int result = 0; int min_uV, max_uV; - int enabled; - - enabled = regulator_is_enabled(supply); - if (enabled < 0) - return enabled; if (vdd_bit) { int tmp; @@ -820,17 +818,25 @@ int mmc_regulator_set_ocr(struct regulator *supply, unsigned short vdd_bit) else result = 0; - if (result == 0 && !enabled) + if (result == 0 && !mmc->regulator_enabled) { result = regulator_enable(supply); - } else if (enabled) { + if (!result) + mmc->regulator_enabled = true; + } + } else if (mmc->regulator_enabled) { result = regulator_disable(supply); + if (result == 0) + mmc->regulator_enabled = false; } + if (result) + dev_err(mmc_dev(mmc), + "could not set regulator OCR (%d)\n", result); return result; } EXPORT_SYMBOL(mmc_regulator_set_ocr); -#endif +#endif /* CONFIG_REGULATOR */ /* * Mask off any voltages we don't support and select diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 5f2e72d38b5d..87b4fc6c98c2 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -523,19 +523,27 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) struct mmci_host *host = mmc_priv(mmc); u32 pwr = 0; unsigned long flags; + int ret; switch (ios->power_mode) { case MMC_POWER_OFF: - if(host->vcc && - regulator_is_enabled(host->vcc)) - regulator_disable(host->vcc); + if (host->vcc) + ret = mmc_regulator_set_ocr(mmc, host->vcc, 0); break; case MMC_POWER_UP: -#ifdef CONFIG_REGULATOR - if (host->vcc) - /* This implicitly enables the regulator */ - mmc_regulator_set_ocr(host->vcc, ios->vdd); -#endif + if (host->vcc) { + ret = mmc_regulator_set_ocr(mmc, host->vcc, ios->vdd); + if (ret) { + dev_err(mmc_dev(mmc), "unable to set OCR\n"); + /* + * The .set_ios() function in the mmc_host_ops + * struct return void, and failing to set the + * power should be rare so we print an error + * and return here. + */ + return; + } + } if (host->plat->vdd_handler) pwr |= host->plat->vdd_handler(mmc_dev(mmc), ios->vdd, ios->power_mode); @@ -869,8 +877,8 @@ static int __devexit mmci_remove(struct amba_device *dev) clk_disable(host->clk); clk_put(host->clk); - if (regulator_is_enabled(host->vcc)) - regulator_disable(host->vcc); + if (host->vcc) + mmc_regulator_set_ocr(mmc, host->vcc, 0); regulator_put(host->vcc); mmc_free_host(mmc); diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 03c26e026506..8c863ccb1bfe 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -250,9 +250,9 @@ static int omap_hsmmc_1_set_power(struct device *dev, int slot, int power_on, mmc_slot(host).before_set_reg(dev, slot, power_on, vdd); if (power_on) - ret = mmc_regulator_set_ocr(host->vcc, vdd); + ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd); else - ret = mmc_regulator_set_ocr(host->vcc, 0); + ret = mmc_regulator_set_ocr(host->mmc, host->vcc, 0); if (mmc_slot(host).after_set_reg) mmc_slot(host).after_set_reg(dev, slot, power_on, vdd); @@ -291,18 +291,23 @@ static int omap_hsmmc_23_set_power(struct device *dev, int slot, int power_on, * chips/cards need an interface voltage rail too. */ if (power_on) { - ret = mmc_regulator_set_ocr(host->vcc, vdd); + ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd); /* Enable interface voltage rail, if needed */ if (ret == 0 && host->vcc_aux) { ret = regulator_enable(host->vcc_aux); if (ret < 0) - ret = mmc_regulator_set_ocr(host->vcc, 0); + ret = mmc_regulator_set_ocr(host->mmc, + host->vcc, 0); } } else { + /* Shut down the rail */ if (host->vcc_aux) ret = regulator_disable(host->vcc_aux); - if (ret == 0) - ret = mmc_regulator_set_ocr(host->vcc, 0); + if (!ret) { + /* Then proceed to shut down the local regulator */ + ret = mmc_regulator_set_ocr(host->mmc, + host->vcc, 0); + } } if (mmc_slot(host).after_set_reg) @@ -343,9 +348,9 @@ static int omap_hsmmc_23_set_sleep(struct device *dev, int slot, int sleep, if (cardsleep) { /* VCC can be turned off if card is asleep */ if (sleep) - err = mmc_regulator_set_ocr(host->vcc, 0); + err = mmc_regulator_set_ocr(host->mmc, host->vcc, 0); else - err = mmc_regulator_set_ocr(host->vcc, vdd); + err = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd); } else err = regulator_set_mode(host->vcc, mode); if (err) diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index b7dfcac31e8a..7257738fd7da 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -99,14 +99,25 @@ static inline void pxamci_init_ocr(struct pxamci_host *host) } } -static inline void pxamci_set_power(struct pxamci_host *host, unsigned int vdd) +static inline int pxamci_set_power(struct pxamci_host *host, + unsigned char power_mode, + unsigned int vdd) { int on; -#ifdef CONFIG_REGULATOR - if (host->vcc) - mmc_regulator_set_ocr(host->vcc, vdd); -#endif + if (host->vcc) { + int ret; + + if (power_mode == MMC_POWER_UP) { + ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd); + if (ret) + return ret; + } else if (power_mode == MMC_POWER_OFF) { + ret = mmc_regulator_set_ocr(host->mmc, host->vcc, 0); + if (ret) + return ret; + } + } if (!host->vcc && host->pdata && gpio_is_valid(host->pdata->gpio_power)) { on = ((1 << vdd) & host->pdata->ocr_mask); @@ -115,6 +126,8 @@ static inline void pxamci_set_power(struct pxamci_host *host, unsigned int vdd) } if (!host->vcc && host->pdata && host->pdata->setpower) host->pdata->setpower(mmc_dev(host->mmc), vdd); + + return 0; } static void pxamci_stop_clock(struct pxamci_host *host) @@ -490,9 +503,21 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) } if (host->power_mode != ios->power_mode) { + int ret; + host->power_mode = ios->power_mode; - pxamci_set_power(host, ios->vdd); + ret = pxamci_set_power(host, ios->power_mode, ios->vdd); + if (ret) { + dev_err(mmc_dev(mmc), "unable to set power\n"); + /* + * The .set_ios() function in the mmc_host_ops + * struct return void, and failing to set the + * power should be rare so we print an error and + * return here. + */ + return; + } if (ios->power_mode == MMC_POWER_ON) host->cmdat |= CMDAT_INIT; @@ -503,8 +528,8 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) else host->cmdat &= ~CMDAT_SD_4DAT; - pr_debug("PXAMCI: clkrt = %x cmdat = %x\n", - host->clkrt, host->cmdat); + dev_dbg(mmc_dev(mmc), "PXAMCI: clkrt = %x cmdat = %x\n", + host->clkrt, host->cmdat); } static void pxamci_enable_sdio_irq(struct mmc_host *host, int enable) diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 20be040649a9..ccac56ae1286 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -212,6 +212,10 @@ struct mmc_host { struct led_trigger *led; /* activity led */ #endif +#ifdef CONFIG_REGULATOR + bool regulator_enabled; /* regulator state */ +#endif + struct dentry *debugfs_root; unsigned long private[0] ____cacheline_aligned; @@ -250,8 +254,24 @@ static inline void mmc_signal_sdio_irq(struct mmc_host *host) struct regulator; +#ifdef CONFIG_REGULATOR int mmc_regulator_get_ocrmask(struct regulator *supply); -int mmc_regulator_set_ocr(struct regulator *supply, unsigned short vdd_bit); +int mmc_regulator_set_ocr(struct mmc_host *mmc, + struct regulator *supply, + unsigned short vdd_bit); +#else +static inline int mmc_regulator_get_ocrmask(struct regulator *supply) +{ + return 0; +} + +static inline int mmc_regulator_set_ocr(struct mmc_host *mmc, + struct regulator *supply, + unsigned short vdd_bit) +{ + return 0; +} +#endif int mmc_card_awake(struct mmc_host *host); int mmc_card_sleep(struct mmc_host *host); -- cgit v1.2.3 From dfc13e8402c75e7c2e0a52e123c0500a3259866b Mon Sep 17 00:00:00 2001 From: Hanumath Prasad Date: Thu, 30 Sep 2010 17:37:23 -0400 Subject: mmc: MMC 4.4 DDR support Add support for Dual Data Rate MMC cards as defined in the 4.4 specification. Signed-off-by: Hanumath Prasad Cc: linux-mmc@vger.kernel.org Acked-by: Linus Walleij Tested-by Zhangfei Gao Signed-off-by: Chris Ball --- drivers/mmc/card/block.c | 10 +++++++--- drivers/mmc/core/mmc.c | 37 +++++++++++++++++++++++++++++++++++-- include/linux/mmc/card.h | 4 ++++ include/linux/mmc/core.h | 1 + include/linux/mmc/host.h | 4 ++++ include/linux/mmc/mmc.h | 10 +++++++++- 6 files changed, 60 insertions(+), 6 deletions(-) (limited to 'include/linux/mmc') diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 759a105cb216..aab593480975 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -373,7 +373,8 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req) readcmd = MMC_READ_SINGLE_BLOCK; writecmd = MMC_WRITE_BLOCK; } - + if (mmc_card_ddr_mode(card)) + brq.data.flags |= MMC_DDR_MODE; if (rq_data_dir(req) == READ) { brq.cmd.opcode = readcmd; brq.data.flags |= MMC_DATA_READ; @@ -655,8 +656,11 @@ mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card) struct mmc_command cmd; int err; - /* Block-addressed cards ignore MMC_SET_BLOCKLEN. */ - if (mmc_card_blockaddr(card)) + /* + * Block-addressed and ddr mode supported cards + * ignore MMC_SET_BLOCKLEN. + */ + if (mmc_card_blockaddr(card) || mmc_card_ddr_mode(card)) return 0; mmc_claim_host(card->host); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 6570c03f9c76..66c4a59fee5f 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -258,6 +258,21 @@ static int mmc_read_ext_csd(struct mmc_card *card) } switch (ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_MASK) { + case EXT_CSD_CARD_TYPE_DDR_52 | EXT_CSD_CARD_TYPE_52 | + EXT_CSD_CARD_TYPE_26: + card->ext_csd.hs_max_dtr = 52000000; + card->ext_csd.card_type = EXT_CSD_CARD_TYPE_DDR_52; + break; + case EXT_CSD_CARD_TYPE_DDR_1_2V | EXT_CSD_CARD_TYPE_52 | + EXT_CSD_CARD_TYPE_26: + card->ext_csd.hs_max_dtr = 52000000; + card->ext_csd.card_type = EXT_CSD_CARD_TYPE_DDR_1_2V; + break; + case EXT_CSD_CARD_TYPE_DDR_1_8V | EXT_CSD_CARD_TYPE_52 | + EXT_CSD_CARD_TYPE_26: + card->ext_csd.hs_max_dtr = 52000000; + card->ext_csd.card_type = EXT_CSD_CARD_TYPE_DDR_1_8V; + break; case EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26: card->ext_csd.hs_max_dtr = 52000000; break; @@ -502,6 +517,18 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, mmc_set_clock(host, max_dtr); + /* + * Activate DDR50 mode (if supported). + */ + 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))) + mmc_card_set_ddr_mode(card); + else if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_2V) + && (host->caps & (MMC_CAP_1_2V_DDR))) + mmc_card_set_ddr_mode(card); + } + /* * Activate wide bus (if supported). */ @@ -510,10 +537,16 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, unsigned ext_csd_bit, bus_width; if (host->caps & MMC_CAP_8_BIT_DATA) { - ext_csd_bit = EXT_CSD_BUS_WIDTH_8; + if (mmc_card_ddr_mode(card)) + ext_csd_bit = EXT_CSD_DDR_BUS_WIDTH_8; + else + ext_csd_bit = EXT_CSD_BUS_WIDTH_8; bus_width = MMC_BUS_WIDTH_8; } else { - ext_csd_bit = EXT_CSD_BUS_WIDTH_4; + if (mmc_card_ddr_mode(card)) + ext_csd_bit = EXT_CSD_DDR_BUS_WIDTH_4; + else + ext_csd_bit = EXT_CSD_BUS_WIDTH_4; bus_width = MMC_BUS_WIDTH_4; } diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 7bd49234cd88..8ce082781ccb 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -48,6 +48,7 @@ struct mmc_ext_csd { unsigned int sa_timeout; /* Units: 100ns */ unsigned int hs_max_dtr; unsigned int sectors; + unsigned int card_type; unsigned int hc_erase_size; /* In sectors */ unsigned int hc_erase_timeout; /* In milliseconds */ unsigned int sec_trim_mult; /* Secure trim multiplier */ @@ -113,6 +114,7 @@ struct mmc_card { #define MMC_STATE_READONLY (1<<1) /* card is read-only */ #define MMC_STATE_HIGHSPEED (1<<2) /* card is in high speed mode */ #define MMC_STATE_BLOCKADDR (1<<3) /* card uses block-addressing */ +#define MMC_STATE_HIGHSPEED_DDR (1<<4) /* card is in high speed mode */ unsigned int quirks; /* card quirks */ #define MMC_QUIRK_LENIENT_FN0 (1<<0) /* allow SDIO FN0 writes outside of the VS CCCR range */ #define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1) /* use func->cur_blksize */ @@ -154,11 +156,13 @@ struct mmc_card { #define mmc_card_readonly(c) ((c)->state & MMC_STATE_READONLY) #define mmc_card_highspeed(c) ((c)->state & MMC_STATE_HIGHSPEED) #define mmc_card_blockaddr(c) ((c)->state & MMC_STATE_BLOCKADDR) +#define mmc_card_ddr_mode(c) ((c)->state & MMC_STATE_HIGHSPEED_DDR) #define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT) #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY) #define mmc_card_set_highspeed(c) ((c)->state |= MMC_STATE_HIGHSPEED) #define mmc_card_set_blockaddr(c) ((c)->state |= MMC_STATE_BLOCKADDR) +#define mmc_card_set_ddr_mode(c) ((c)->state |= MMC_STATE_HIGHSPEED_DDR) static inline int mmc_card_lenient_fn0(const struct mmc_card *c) { diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index 7429033acb66..d0fbcacab52c 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -109,6 +109,7 @@ struct mmc_data { #define MMC_DATA_WRITE (1 << 8) #define MMC_DATA_READ (1 << 9) #define MMC_DATA_STREAM (1 << 10) +#define MMC_DDR_MODE (1 << 11) unsigned int bytes_xfered; diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index ccac56ae1286..6711eb8715ba 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -158,6 +158,10 @@ struct mmc_host { #define MMC_CAP_NONREMOVABLE (1 << 8) /* Nonremovable e.g. eMMC */ #define MMC_CAP_WAIT_WHILE_BUSY (1 << 9) /* Waits while card is busy */ #define MMC_CAP_ERASE (1 << 10) /* Allow erase/trim commands */ +#define MMC_CAP_1_8V_DDR (1 << 11) /* can support */ + /* DDR mode at 1.8V */ +#define MMC_CAP_1_2V_DDR (1 << 12) /* can support */ + /* DDR mode at 1.2V */ mmc_pm_flag_t pm_caps; /* supported pm features */ diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index dd11ae51fb68..956fbd877692 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -277,11 +277,19 @@ struct _mmc_csd { #define EXT_CSD_CARD_TYPE_26 (1<<0) /* Card can run at 26MHz */ #define EXT_CSD_CARD_TYPE_52 (1<<1) /* Card can run at 52MHz */ -#define EXT_CSD_CARD_TYPE_MASK 0x3 /* Mask out reserved and DDR bits */ +#define EXT_CSD_CARD_TYPE_MASK 0xF /* Mask out reserved bits */ +#define EXT_CSD_CARD_TYPE_DDR_1_8V (1<<2) /* Card can run at 52MHz */ + /* DDR mode @1.8V or 3V I/O */ +#define EXT_CSD_CARD_TYPE_DDR_1_2V (1<<3) /* Card can run at 52MHz */ + /* DDR mode @1.2V I/O */ +#define EXT_CSD_CARD_TYPE_DDR_52 (EXT_CSD_CARD_TYPE_DDR_1_8V \ + | EXT_CSD_CARD_TYPE_DDR_1_2V) #define EXT_CSD_BUS_WIDTH_1 0 /* Card is in 1 bit mode */ #define EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */ #define EXT_CSD_BUS_WIDTH_8 2 /* Card is in 8 bit mode */ +#define EXT_CSD_DDR_BUS_WIDTH_4 5 /* Card is in 4 bit DDR mode */ +#define EXT_CSD_DDR_BUS_WIDTH_8 6 /* Card is in 8 bit DDR mode */ #define EXT_CSD_SEC_ER_EN BIT(0) #define EXT_CSD_SEC_BD_BLK_EN BIT(2) -- cgit v1.2.3 From 0f8d8ea64ec7c77ca5beb59534d386fe0235961a Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 24 Aug 2010 13:20:26 +0300 Subject: mmc: Fixes for Dual Data Rate (DDR) support The DDR support patch needs the following fixes: - The block driver does not need to know about DDR, any more than it needs to know about bus width. - Not only the card must be switched to DDR mode. The host controller must also be configured, which is done through the 'set_ios()' function. - Do not set the DDR mode state until after the switch command is successful. - Setting block length is not supported in DDR mode. Make that a core function and change the other place it is used (mmc_test) also. Signed-off-by: Adrian Hunter Signed-off-by: Chris Ball --- drivers/mmc/card/block.c | 19 +++---------------- drivers/mmc/card/mmc_test.c | 12 +----------- drivers/mmc/core/bus.c | 6 ++++-- drivers/mmc/core/core.c | 28 ++++++++++++++++++++++++++-- drivers/mmc/core/core.h | 1 + drivers/mmc/core/mmc.c | 21 +++++++++++---------- include/linux/mmc/core.h | 3 ++- include/linux/mmc/host.h | 5 +++++ 8 files changed, 53 insertions(+), 42 deletions(-) (limited to 'include/linux/mmc') diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index aab593480975..a9970504cabb 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -373,8 +373,6 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req) readcmd = MMC_READ_SINGLE_BLOCK; writecmd = MMC_WRITE_BLOCK; } - if (mmc_card_ddr_mode(card)) - brq.data.flags |= MMC_DDR_MODE; if (rq_data_dir(req) == READ) { brq.cmd.opcode = readcmd; brq.data.flags |= MMC_DATA_READ; @@ -653,26 +651,15 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card) static int mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card) { - struct mmc_command cmd; int err; - /* - * Block-addressed and ddr mode supported cards - * ignore MMC_SET_BLOCKLEN. - */ - if (mmc_card_blockaddr(card) || mmc_card_ddr_mode(card)) - return 0; - mmc_claim_host(card->host); - cmd.opcode = MMC_SET_BLOCKLEN; - cmd.arg = 512; - cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC; - err = mmc_wait_for_cmd(card->host, &cmd, 5); + err = mmc_set_blocklen(card, 512); mmc_release_host(card->host); if (err) { - printk(KERN_ERR "%s: unable to set block size to %d: %d\n", - md->disk->disk_name, cmd.arg, err); + printk(KERN_ERR "%s: unable to set block size to 512: %d\n", + md->disk->disk_name, err); return -EINVAL; } diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c index c38a3a84a455..21adc27f4132 100644 --- a/drivers/mmc/card/mmc_test.c +++ b/drivers/mmc/card/mmc_test.c @@ -155,17 +155,7 @@ struct mmc_test_card { */ static int mmc_test_set_blksize(struct mmc_test_card *test, unsigned size) { - struct mmc_command cmd; - int ret; - - cmd.opcode = MMC_SET_BLOCKLEN; - cmd.arg = size; - cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; - ret = mmc_wait_for_cmd(test->card->host, &cmd, 0); - if (ret) - return ret; - - return 0; + return mmc_set_blocklen(test->card, size); } /* diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index e70bd6641cee..da3c01b214ec 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -253,14 +253,16 @@ int mmc_add_card(struct mmc_card *card) } if (mmc_host_is_spi(card->host)) { - printk(KERN_INFO "%s: new %s%s card on SPI\n", + printk(KERN_INFO "%s: new %s%s%s card on SPI\n", mmc_hostname(card->host), mmc_card_highspeed(card) ? "high speed " : "", + mmc_card_ddr_mode(card) ? "DDR " : "", type); } else { - printk(KERN_INFO "%s: new %s%s card at address %04x\n", + printk(KERN_INFO "%s: new %s%s%s card at address %04x\n", mmc_hostname(card->host), mmc_card_highspeed(card) ? "high speed " : "", + mmc_card_ddr_mode(card) ? "DDR " : "", type, card->rca); } diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 46029d5c0364..7cb352b3b247 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -651,14 +651,23 @@ void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode) } /* - * Change data bus width of a host. + * Change data bus width and DDR mode of a host. */ -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, int ddr) { host->ios.bus_width = width; + host->ios.ddr = ddr ? MMC_DDR_MODE : MMC_SDR_MODE; 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, 0); +} + /** * mmc_vdd_to_ocrbitnum - Convert a voltage to the OCR bit number * @vdd: voltage (mV) @@ -1399,6 +1408,21 @@ int mmc_erase_group_aligned(struct mmc_card *card, unsigned int from, } EXPORT_SYMBOL(mmc_erase_group_aligned); +int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen) +{ + struct mmc_command cmd; + + 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; + return mmc_wait_for_cmd(card->host, &cmd, 5); +} +EXPORT_SYMBOL(mmc_set_blocklen); + void mmc_rescan(struct work_struct *work) { struct mmc_host *host = diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index a2ca770ca89b..13240d128a69 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -35,6 +35,7 @@ void mmc_set_chip_select(struct mmc_host *host, int mode); void mmc_set_clock(struct mmc_host *host, unsigned int hz); 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, int ddr); u32 mmc_select_voltage(struct mmc_host *host, u32 ocr); void mmc_set_timing(struct mmc_host *host, unsigned int timing); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 66c4a59fee5f..3ea58ce773ff 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -375,7 +375,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, struct mmc_card *oldcard) { struct mmc_card *card; - int err; + int err, ddr = 0; u32 cid[4]; unsigned int max_dtr; @@ -518,32 +518,32 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, mmc_set_clock(host, max_dtr); /* - * Activate DDR50 mode (if supported). + * Indicate DDR mode (if supported). */ 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))) - mmc_card_set_ddr_mode(card); + ddr = 1; else if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_2V) && (host->caps & (MMC_CAP_1_2V_DDR))) - mmc_card_set_ddr_mode(card); + ddr = 1; } /* - * Activate wide bus (if supported). + * Activate wide bus and DDR (if supported). */ if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) && (host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) { unsigned ext_csd_bit, bus_width; if (host->caps & MMC_CAP_8_BIT_DATA) { - if (mmc_card_ddr_mode(card)) + if (ddr) ext_csd_bit = EXT_CSD_DDR_BUS_WIDTH_8; else ext_csd_bit = EXT_CSD_BUS_WIDTH_8; bus_width = MMC_BUS_WIDTH_8; } else { - if (mmc_card_ddr_mode(card)) + if (ddr) ext_csd_bit = EXT_CSD_DDR_BUS_WIDTH_4; else ext_csd_bit = EXT_CSD_BUS_WIDTH_4; @@ -557,12 +557,13 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, goto free_card; if (err) { - printk(KERN_WARNING "%s: switch to bus width %d " + printk(KERN_WARNING "%s: switch to bus width %d ddr %d " "failed\n", mmc_hostname(card->host), - 1 << bus_width); + 1 << bus_width, ddr); err = 0; } else { - mmc_set_bus_width(card->host, bus_width); + mmc_card_set_ddr_mode(card); + mmc_set_bus_width_ddr(card->host, bus_width, ddr); } } diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index d0fbcacab52c..64e013f1cfb8 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -109,7 +109,6 @@ struct mmc_data { #define MMC_DATA_WRITE (1 << 8) #define MMC_DATA_READ (1 << 9) #define MMC_DATA_STREAM (1 << 10) -#define MMC_DDR_MODE (1 << 11) unsigned int bytes_xfered; @@ -154,6 +153,8 @@ extern int mmc_can_secure_erase_trim(struct mmc_card *card); extern int mmc_erase_group_aligned(struct mmc_card *card, unsigned int from, unsigned int nr); +extern int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen); + extern void mmc_set_data_timeout(struct mmc_data *, const struct mmc_card *); extern unsigned int mmc_align_data_size(struct mmc_card *, unsigned int); diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 6711eb8715ba..c4fb1c5efc44 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -50,6 +50,11 @@ struct mmc_ios { #define MMC_TIMING_LEGACY 0 #define MMC_TIMING_MMC_HS 1 #define MMC_TIMING_SD_HS 2 + + unsigned char ddr; /* dual data rate used */ + +#define MMC_SDR_MODE 0 +#define MMC_DDR_MODE 1 }; struct mmc_host_ops { -- cgit v1.2.3 From 49e3b5a44f8abd33c8693edc575c6d06a210d778 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 11 Oct 2010 12:43:50 +0300 Subject: mmc: refine DDR support One flaw with DDR support is that MMC core does not inform the driver which DDR mode it has selected. This patch expands the ios->ddr flag to do that. Signed-off-by: Adrian Hunter Signed-off-by: Chris Ball --- drivers/mmc/core/core.c | 7 ++++--- drivers/mmc/core/core.h | 3 ++- drivers/mmc/core/mmc.c | 6 +++--- include/linux/mmc/host.h | 3 ++- 4 files changed, 11 insertions(+), 8 deletions(-) (limited to 'include/linux/mmc') diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 7cb352b3b247..3eb7a9be6d8d 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -653,10 +653,11 @@ void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode) /* * Change data bus width and DDR mode of a host. */ -void mmc_set_bus_width_ddr(struct mmc_host *host, unsigned int width, int ddr) +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_DDR_MODE : MMC_SDR_MODE; + host->ios.ddr = ddr; mmc_set_ios(host); } @@ -665,7 +666,7 @@ void mmc_set_bus_width_ddr(struct mmc_host *host, unsigned int width, int ddr) */ void mmc_set_bus_width(struct mmc_host *host, unsigned int width) { - mmc_set_bus_width_ddr(host, width, 0); + mmc_set_bus_width_ddr(host, width, MMC_SDR_MODE); } /** diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 13240d128a69..a971b0667aad 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -35,7 +35,8 @@ void mmc_set_chip_select(struct mmc_host *host, int mode); void mmc_set_clock(struct mmc_host *host, unsigned int hz); 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, int ddr); +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); void mmc_set_timing(struct mmc_host *host, unsigned int timing); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 3ea58ce773ff..df2a817303b4 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -375,7 +375,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, struct mmc_card *oldcard) { struct mmc_card *card; - int err, ddr = 0; + int err, ddr = MMC_SDR_MODE; u32 cid[4]; unsigned int max_dtr; @@ -523,10 +523,10 @@ 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))) - ddr = 1; + 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))) - ddr = 1; + ddr = MMC_1_2V_DDR_MODE; } /* diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index c4fb1c5efc44..69ee1ebe4302 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -54,7 +54,8 @@ struct mmc_ios { unsigned char ddr; /* dual data rate used */ #define MMC_SDR_MODE 0 -#define MMC_DDR_MODE 1 +#define MMC_1_2V_DDR_MODE 1 +#define MMC_1_8V_DDR_MODE 2 }; struct mmc_host_ops { -- cgit v1.2.3 From 1978fda85dfdb53623dddb4ec126163a61ab3933 Mon Sep 17 00:00:00 2001 From: Giuseppe Cavallaro Date: Tue, 28 Sep 2010 10:41:29 +0200 Subject: mmc: sdhci: split up sdhci.h for sdhci-pltfm users Some platforms based on sdhci-pltfm need to set their own quirks. Previously to this patch, the quirks were in drivers/mmc/host/sdhci.h. This patch splits drivers/mmc/host/sdhci.h into two parts: * drivers/mmc/host/sdhci.h includes the HC registers and I/O accessors. * include/linux/mmc/sdhci.h includes the sdhci structure and quirks. Instead of including drivers/mmc/host/sdhci.h, -pltfm drivers should now include include/linux/mmc/sdhci.h and include/linux/sdhci-pltfm.h. This patch avoids adding/changing the calls/flags in the sdhci_pltfm_data structure. It has been tested on STM platforms (e.g. STx7106, STx7108, STx5206) where the driver is configured and used as shown in the example below: [snip] static int mmc_pad_resources(struct sdhci_host *sdhci) { if (!devm_stm_pad_claim(sdhci->mmc->parent, &stx7108_mmc_pad_config, dev_name(sdhci->mmc->parent))) return -ENODEV; return 0; } static struct sdhci_pltfm_data stx7108_mmc_platform_data = { .init = mmc_pad_resources, .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, }; static struct platform_device stx7108_mmc_device = { .name = "sdhci", [snip] Note: drivers/mmc/host/sdhci.h now also includes linux/mmc/sdhci.h, and no modifications should be needed on other sdhci- drivers. Signed-off-by: Giuseppe Cavallaro Reviewed-by: Wolfram Sang Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.h | 138 +++----------------------------------------- include/linux/mmc/sdhci.h | 144 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 151 insertions(+), 131 deletions(-) create mode 100644 include/linux/mmc/sdhci.h (limited to 'include/linux/mmc') diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 112543ae36c9..410ee8aa04d4 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -1,6 +1,8 @@ /* * linux/drivers/mmc/host/sdhci.h - Secure Digital Host Controller Interface driver * + * Header file for Host Controller registers and I/O accessors. + * * Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved. * * This program is free software; you can redistribute it and/or modify @@ -8,14 +10,16 @@ * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. */ -#ifndef __SDHCI_H -#define __SDHCI_H +#ifndef __SDHCI_HW_H +#define __SDHCI_HW_H #include #include #include #include +#include + /* * Controller registers */ @@ -192,134 +196,6 @@ #define SDHCI_MAX_DIV_SPEC_200 256 #define SDHCI_MAX_DIV_SPEC_300 2046 -struct sdhci_ops; - -struct sdhci_host { - /* Data set by hardware interface driver */ - const char *hw_name; /* Hardware bus name */ - - unsigned int quirks; /* Deviations from spec. */ - -/* Controller doesn't honor resets unless we touch the clock register */ -#define SDHCI_QUIRK_CLOCK_BEFORE_RESET (1<<0) -/* Controller has bad caps bits, but really supports DMA */ -#define SDHCI_QUIRK_FORCE_DMA (1<<1) -/* Controller doesn't like to be reset when there is no card inserted. */ -#define SDHCI_QUIRK_NO_CARD_NO_RESET (1<<2) -/* Controller doesn't like clearing the power reg before a change */ -#define SDHCI_QUIRK_SINGLE_POWER_WRITE (1<<3) -/* Controller has flaky internal state so reset it on each ios change */ -#define SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS (1<<4) -/* Controller has an unusable DMA engine */ -#define SDHCI_QUIRK_BROKEN_DMA (1<<5) -/* Controller has an unusable ADMA engine */ -#define SDHCI_QUIRK_BROKEN_ADMA (1<<6) -/* Controller can only DMA from 32-bit aligned addresses */ -#define SDHCI_QUIRK_32BIT_DMA_ADDR (1<<7) -/* Controller can only DMA chunk sizes that are a multiple of 32 bits */ -#define SDHCI_QUIRK_32BIT_DMA_SIZE (1<<8) -/* Controller can only ADMA chunks that are a multiple of 32 bits */ -#define SDHCI_QUIRK_32BIT_ADMA_SIZE (1<<9) -/* Controller needs to be reset after each request to stay stable */ -#define SDHCI_QUIRK_RESET_AFTER_REQUEST (1<<10) -/* Controller needs voltage and power writes to happen separately */ -#define SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER (1<<11) -/* Controller provides an incorrect timeout value for transfers */ -#define SDHCI_QUIRK_BROKEN_TIMEOUT_VAL (1<<12) -/* Controller has an issue with buffer bits for small transfers */ -#define SDHCI_QUIRK_BROKEN_SMALL_PIO (1<<13) -/* Controller does not provide transfer-complete interrupt when not busy */ -#define SDHCI_QUIRK_NO_BUSY_IRQ (1<<14) -/* Controller has unreliable card detection */ -#define SDHCI_QUIRK_BROKEN_CARD_DETECTION (1<<15) -/* Controller reports inverted write-protect state */ -#define SDHCI_QUIRK_INVERTED_WRITE_PROTECT (1<<16) -/* Controller has nonstandard clock management */ -#define SDHCI_QUIRK_NONSTANDARD_CLOCK (1<<17) -/* Controller does not like fast PIO transfers */ -#define SDHCI_QUIRK_PIO_NEEDS_DELAY (1<<18) -/* Controller losing signal/interrupt enable states after reset */ -#define SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET (1<<19) -/* Controller has to be forced to use block size of 2048 bytes */ -#define SDHCI_QUIRK_FORCE_BLK_SZ_2048 (1<<20) -/* Controller cannot do multi-block transfers */ -#define SDHCI_QUIRK_NO_MULTIBLOCK (1<<21) -/* Controller can only handle 1-bit data transfers */ -#define SDHCI_QUIRK_FORCE_1_BIT_DATA (1<<22) -/* Controller needs 10ms delay between applying power and clock */ -#define SDHCI_QUIRK_DELAY_AFTER_POWER (1<<23) -/* Controller uses SDCLK instead of TMCLK for data timeouts */ -#define SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK (1<<24) -/* Controller reports wrong base clock capability */ -#define SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN (1<<25) -/* Controller cannot support End Attribute in NOP ADMA descriptor */ -#define SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC (1<<26) -/* Controller is missing device caps. Use caps provided by host */ -#define SDHCI_QUIRK_MISSING_CAPS (1<<27) -/* Controller uses Auto CMD12 command to stop the transfer */ -#define SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12 (1<<28) -/* Controller doesn't have HISPD bit field in HI-SPEED SD card */ -#define SDHCI_QUIRK_NO_HISPD_BIT (1<<29) - - int irq; /* Device IRQ */ - void __iomem * ioaddr; /* Mapped address */ - - const struct sdhci_ops *ops; /* Low level hw interface */ - - struct regulator *vmmc; /* Power regulator */ - - /* Internal data */ - struct mmc_host *mmc; /* MMC structure */ - u64 dma_mask; /* custom DMA mask */ - -#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) - struct led_classdev led; /* LED control */ - char led_name[32]; -#endif - - spinlock_t lock; /* Mutex */ - - int flags; /* Host attributes */ -#define SDHCI_USE_SDMA (1<<0) /* Host is SDMA capable */ -#define SDHCI_USE_ADMA (1<<1) /* Host is ADMA capable */ -#define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */ -#define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */ - - unsigned int version; /* SDHCI spec. version */ - - unsigned int max_clk; /* Max possible freq (MHz) */ - unsigned int timeout_clk; /* Timeout freq (KHz) */ - - unsigned int clock; /* Current clock (MHz) */ - u8 pwr; /* Current voltage */ - - struct mmc_request *mrq; /* Current request */ - struct mmc_command *cmd; /* Current command */ - struct mmc_data *data; /* Current data request */ - unsigned int data_early:1; /* Data finished before cmd */ - - struct sg_mapping_iter sg_miter; /* SG state for PIO */ - unsigned int blocks; /* remaining PIO blocks */ - - int sg_count; /* Mapped sg entries */ - - u8 *adma_desc; /* ADMA descriptor table */ - u8 *align_buffer; /* Bounce buffer */ - - dma_addr_t adma_addr; /* Mapped ADMA descr. table */ - dma_addr_t align_addr; /* Mapped bounce buffer */ - - struct tasklet_struct card_tasklet; /* Tasklet structures */ - struct tasklet_struct finish_tasklet; - - struct timer_list timer; /* Timer for timeouts */ - - unsigned int caps; /* Alternative capabilities */ - - unsigned long private[0] ____cacheline_aligned; -}; - - struct sdhci_ops { #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS u32 (*read_l)(struct sdhci_host *host, int reg); @@ -440,4 +316,4 @@ extern int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state); extern int sdhci_resume_host(struct sdhci_host *host); #endif -#endif /* __SDHCI_H */ +#endif /* __SDHCI_HW_H */ diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h new file mode 100644 index 000000000000..1fdc673f2396 --- /dev/null +++ b/include/linux/mmc/sdhci.h @@ -0,0 +1,144 @@ +/* + * linux/include/linux/mmc/sdhci.h - Secure Digital Host Controller Interface + * + * Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ +#ifndef __SDHCI_H +#define __SDHCI_H + +#include +#include +#include +#include +#include + +struct sdhci_host { + /* Data set by hardware interface driver */ + const char *hw_name; /* Hardware bus name */ + + unsigned int quirks; /* Deviations from spec. */ + +/* Controller doesn't honor resets unless we touch the clock register */ +#define SDHCI_QUIRK_CLOCK_BEFORE_RESET (1<<0) +/* Controller has bad caps bits, but really supports DMA */ +#define SDHCI_QUIRK_FORCE_DMA (1<<1) +/* Controller doesn't like to be reset when there is no card inserted. */ +#define SDHCI_QUIRK_NO_CARD_NO_RESET (1<<2) +/* Controller doesn't like clearing the power reg before a change */ +#define SDHCI_QUIRK_SINGLE_POWER_WRITE (1<<3) +/* Controller has flaky internal state so reset it on each ios change */ +#define SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS (1<<4) +/* Controller has an unusable DMA engine */ +#define SDHCI_QUIRK_BROKEN_DMA (1<<5) +/* Controller has an unusable ADMA engine */ +#define SDHCI_QUIRK_BROKEN_ADMA (1<<6) +/* Controller can only DMA from 32-bit aligned addresses */ +#define SDHCI_QUIRK_32BIT_DMA_ADDR (1<<7) +/* Controller can only DMA chunk sizes that are a multiple of 32 bits */ +#define SDHCI_QUIRK_32BIT_DMA_SIZE (1<<8) +/* Controller can only ADMA chunks that are a multiple of 32 bits */ +#define SDHCI_QUIRK_32BIT_ADMA_SIZE (1<<9) +/* Controller needs to be reset after each request to stay stable */ +#define SDHCI_QUIRK_RESET_AFTER_REQUEST (1<<10) +/* Controller needs voltage and power writes to happen separately */ +#define SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER (1<<11) +/* Controller provides an incorrect timeout value for transfers */ +#define SDHCI_QUIRK_BROKEN_TIMEOUT_VAL (1<<12) +/* Controller has an issue with buffer bits for small transfers */ +#define SDHCI_QUIRK_BROKEN_SMALL_PIO (1<<13) +/* Controller does not provide transfer-complete interrupt when not busy */ +#define SDHCI_QUIRK_NO_BUSY_IRQ (1<<14) +/* Controller has unreliable card detection */ +#define SDHCI_QUIRK_BROKEN_CARD_DETECTION (1<<15) +/* Controller reports inverted write-protect state */ +#define SDHCI_QUIRK_INVERTED_WRITE_PROTECT (1<<16) +/* Controller has nonstandard clock management */ +#define SDHCI_QUIRK_NONSTANDARD_CLOCK (1<<17) +/* Controller does not like fast PIO transfers */ +#define SDHCI_QUIRK_PIO_NEEDS_DELAY (1<<18) +/* Controller losing signal/interrupt enable states after reset */ +#define SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET (1<<19) +/* Controller has to be forced to use block size of 2048 bytes */ +#define SDHCI_QUIRK_FORCE_BLK_SZ_2048 (1<<20) +/* Controller cannot do multi-block transfers */ +#define SDHCI_QUIRK_NO_MULTIBLOCK (1<<21) +/* Controller can only handle 1-bit data transfers */ +#define SDHCI_QUIRK_FORCE_1_BIT_DATA (1<<22) +/* Controller needs 10ms delay between applying power and clock */ +#define SDHCI_QUIRK_DELAY_AFTER_POWER (1<<23) +/* Controller uses SDCLK instead of TMCLK for data timeouts */ +#define SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK (1<<24) +/* Controller reports wrong base clock capability */ +#define SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN (1<<25) +/* Controller cannot support End Attribute in NOP ADMA descriptor */ +#define SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC (1<<26) +/* Controller is missing device caps. Use caps provided by host */ +#define SDHCI_QUIRK_MISSING_CAPS (1<<27) +/* Controller uses Auto CMD12 command to stop the transfer */ +#define SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12 (1<<28) +/* Controller doesn't have HISPD bit field in HI-SPEED SD card */ +#define SDHCI_QUIRK_NO_HISPD_BIT (1<<29) + + int irq; /* Device IRQ */ + void __iomem *ioaddr; /* Mapped address */ + + const struct sdhci_ops *ops; /* Low level hw interface */ + + struct regulator *vmmc; /* Power regulator */ + + /* Internal data */ + struct mmc_host *mmc; /* MMC structure */ + u64 dma_mask; /* custom DMA mask */ + +#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) + struct led_classdev led; /* LED control */ + char led_name[32]; +#endif + + spinlock_t lock; /* Mutex */ + + int flags; /* Host attributes */ +#define SDHCI_USE_SDMA (1<<0) /* Host is SDMA capable */ +#define SDHCI_USE_ADMA (1<<1) /* Host is ADMA capable */ +#define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */ +#define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */ + + unsigned int version; /* SDHCI spec. version */ + + unsigned int max_clk; /* Max possible freq (MHz) */ + unsigned int timeout_clk; /* Timeout freq (KHz) */ + + unsigned int clock; /* Current clock (MHz) */ + u8 pwr; /* Current voltage */ + + struct mmc_request *mrq; /* Current request */ + struct mmc_command *cmd; /* Current command */ + struct mmc_data *data; /* Current data request */ + unsigned int data_early:1; /* Data finished before cmd */ + + struct sg_mapping_iter sg_miter; /* SG state for PIO */ + unsigned int blocks; /* remaining PIO blocks */ + + int sg_count; /* Mapped sg entries */ + + u8 *adma_desc; /* ADMA descriptor table */ + u8 *align_buffer; /* Bounce buffer */ + + dma_addr_t adma_addr; /* Mapped ADMA descr. table */ + dma_addr_t align_addr; /* Mapped bounce buffer */ + + struct tasklet_struct card_tasklet; /* Tasklet structures */ + struct tasklet_struct finish_tasklet; + + struct timer_list timer; /* Timer for timeouts */ + + unsigned int caps; /* Alternative capabilities */ + + unsigned long private[0] ____cacheline_aligned; +}; +#endif /* __SDHCI_H */ -- cgit v1.2.3 From 12ae637f081a7a05144af65802a7b492b9162660 Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Sat, 2 Oct 2010 13:54:06 +0200 Subject: mmc: propagate power save/restore ops return value Allow power save/restore and their relevant mmc_bus_ops handlers exit with a return value. Signed-off-by: Ohad Ben-Cohen Tested-by: Luciano Coelho Signed-off-by: Chris Ball --- drivers/mmc/core/core.c | 20 ++++++++++++++------ drivers/mmc/core/core.h | 4 ++-- drivers/mmc/core/mmc.c | 8 ++++++-- drivers/mmc/core/sd.c | 8 ++++++-- include/linux/mmc/host.h | 4 ++-- 5 files changed, 30 insertions(+), 14 deletions(-) (limited to 'include/linux/mmc') diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 3eb7a9be6d8d..8f86d702e46e 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1583,37 +1583,45 @@ void mmc_stop_host(struct mmc_host *host) mmc_power_off(host); } -void mmc_power_save_host(struct mmc_host *host) +int mmc_power_save_host(struct mmc_host *host) { + int ret = 0; + mmc_bus_get(host); if (!host->bus_ops || host->bus_dead || !host->bus_ops->power_restore) { mmc_bus_put(host); - return; + return -EINVAL; } if (host->bus_ops->power_save) - host->bus_ops->power_save(host); + ret = host->bus_ops->power_save(host); mmc_bus_put(host); mmc_power_off(host); + + return ret; } EXPORT_SYMBOL(mmc_power_save_host); -void mmc_power_restore_host(struct mmc_host *host) +int mmc_power_restore_host(struct mmc_host *host) { + int ret; + mmc_bus_get(host); if (!host->bus_ops || host->bus_dead || !host->bus_ops->power_restore) { mmc_bus_put(host); - return; + return -EINVAL; } mmc_power_up(host); - host->bus_ops->power_restore(host); + ret = host->bus_ops->power_restore(host); mmc_bus_put(host); + + return ret; } EXPORT_SYMBOL(mmc_power_restore_host); diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index a971b0667aad..77240cd11bcf 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -22,8 +22,8 @@ struct mmc_bus_ops { void (*detect)(struct mmc_host *); int (*suspend)(struct mmc_host *); int (*resume)(struct mmc_host *); - void (*power_save)(struct mmc_host *); - void (*power_restore)(struct mmc_host *); + int (*power_save)(struct mmc_host *); + int (*power_restore)(struct mmc_host *); }; void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index df2a817303b4..995261f7fd70 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -657,12 +657,16 @@ static int mmc_resume(struct mmc_host *host) return err; } -static void mmc_power_restore(struct mmc_host *host) +static int mmc_power_restore(struct mmc_host *host) { + int ret; + host->card->state &= ~MMC_STATE_HIGHSPEED; mmc_claim_host(host); - mmc_init_card(host, host->ocr, host->card); + ret = mmc_init_card(host, host->ocr, host->card); mmc_release_host(host); + + return ret; } static int mmc_sleep(struct mmc_host *host) diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index bc745e1237bf..49da4dffd28e 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -722,12 +722,16 @@ static int mmc_sd_resume(struct mmc_host *host) return err; } -static void mmc_sd_power_restore(struct mmc_host *host) +static int mmc_sd_power_restore(struct mmc_host *host) { + int ret; + host->card->state &= ~MMC_STATE_HIGHSPEED; mmc_claim_host(host); - mmc_sd_init_card(host, host->ocr, host->card); + ret = mmc_sd_init_card(host, host->ocr, host->card); mmc_release_host(host); + + return ret; } static const struct mmc_bus_ops mmc_sd_ops = { diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 69ee1ebe4302..6d87f68ce4b6 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -250,8 +250,8 @@ static inline void *mmc_priv(struct mmc_host *host) extern int mmc_suspend_host(struct mmc_host *); extern int mmc_resume_host(struct mmc_host *); -extern void mmc_power_save_host(struct mmc_host *host); -extern void mmc_power_restore_host(struct mmc_host *host); +extern int mmc_power_save_host(struct mmc_host *host); +extern int mmc_power_restore_host(struct mmc_host *host); extern void mmc_detect_change(struct mmc_host *, unsigned long delay); extern void mmc_request_done(struct mmc_host *, struct mmc_request *); -- cgit v1.2.3 From d3b993dcc11cd291e6908ed02b9db99970220952 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 15 Oct 2010 12:21:00 +0200 Subject: mmc: sdhci-pltfm: move .h file into appropriate subdir MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make use of the include/linux/mmc directory. Signed-off-by: Wolfram Sang Acked-by: Anton Vorontsov Tested-by: Eric Bénard Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-cns3xxx.c | 2 +- drivers/mmc/host/sdhci-pltfm.c | 2 +- drivers/mmc/host/sdhci-pltfm.h | 2 +- include/linux/mmc/sdhci-pltfm.h | 35 +++++++++++++++++++++++++++++++++++ include/linux/sdhci-pltfm.h | 35 ----------------------------------- 5 files changed, 38 insertions(+), 38 deletions(-) create mode 100644 include/linux/mmc/sdhci-pltfm.h delete mode 100644 include/linux/sdhci-pltfm.h (limited to 'include/linux/mmc') diff --git a/drivers/mmc/host/sdhci-cns3xxx.c b/drivers/mmc/host/sdhci-cns3xxx.c index b7050b380d5f..9ebd1d7759dc 100644 --- a/drivers/mmc/host/sdhci-cns3xxx.c +++ b/drivers/mmc/host/sdhci-cns3xxx.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include "sdhci.h" #include "sdhci-pltfm.h" diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index 730fdf54ecb0..685202be2778 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -30,7 +30,7 @@ #include #include -#include +#include #include "sdhci.h" #include "sdhci-pltfm.h" diff --git a/drivers/mmc/host/sdhci-pltfm.h b/drivers/mmc/host/sdhci-pltfm.h index 93a031973f62..562b92957f18 100644 --- a/drivers/mmc/host/sdhci-pltfm.h +++ b/drivers/mmc/host/sdhci-pltfm.h @@ -13,7 +13,7 @@ #include #include -#include +#include struct sdhci_pltfm_host { struct clk *clk; diff --git a/include/linux/mmc/sdhci-pltfm.h b/include/linux/mmc/sdhci-pltfm.h new file mode 100644 index 000000000000..0239bd70241e --- /dev/null +++ b/include/linux/mmc/sdhci-pltfm.h @@ -0,0 +1,35 @@ +/* + * Platform data declarations for the sdhci-pltfm driver. + * + * Copyright (c) 2010 MontaVista Software, LLC. + * + * Author: Anton Vorontsov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ + +#ifndef _SDHCI_PLTFM_H +#define _SDHCI_PLTFM_H + +struct sdhci_ops; +struct sdhci_host; + +/** + * struct sdhci_pltfm_data - SDHCI platform-specific information & hooks + * @ops: optional pointer to the platform-provided SDHCI ops + * @quirks: optional SDHCI quirks + * @init: optional hook that is called during device probe, before the + * driver tries to access any SDHCI registers + * @exit: optional hook that is called during device removal + */ +struct sdhci_pltfm_data { + struct sdhci_ops *ops; + unsigned int quirks; + int (*init)(struct sdhci_host *host); + void (*exit)(struct sdhci_host *host); +}; + +#endif /* _SDHCI_PLTFM_H */ diff --git a/include/linux/sdhci-pltfm.h b/include/linux/sdhci-pltfm.h deleted file mode 100644 index 0239bd70241e..000000000000 --- a/include/linux/sdhci-pltfm.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Platform data declarations for the sdhci-pltfm driver. - * - * Copyright (c) 2010 MontaVista Software, LLC. - * - * Author: Anton Vorontsov - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - */ - -#ifndef _SDHCI_PLTFM_H -#define _SDHCI_PLTFM_H - -struct sdhci_ops; -struct sdhci_host; - -/** - * struct sdhci_pltfm_data - SDHCI platform-specific information & hooks - * @ops: optional pointer to the platform-provided SDHCI ops - * @quirks: optional SDHCI quirks - * @init: optional hook that is called during device probe, before the - * driver tries to access any SDHCI registers - * @exit: optional hook that is called during device removal - */ -struct sdhci_pltfm_data { - struct sdhci_ops *ops; - unsigned int quirks; - int (*init)(struct sdhci_host *host); - void (*exit)(struct sdhci_host *host); -}; - -#endif /* _SDHCI_PLTFM_H */ -- cgit v1.2.3 From 012994f4fa5fc7663b51fa921c85c0a352339b24 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 15 Oct 2010 12:21:02 +0200 Subject: mmc: sdhci_pltfm: pass more data on custom init call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The custom init call may need more data to perform its job, so we pass it a pointer to pdata, too. Also, always use the platform_id specific data even if platform_data is present. Doing that, platform_data can additionally be parsed by init() for board-specific information (via sdhci->mmc->parent). (Note: the old behaviour was that you could override the platform_id specific data with your own. However, one can still do this by using the "sdhci" id instead of "sdhci-".) Signed-off-by: Wolfram Sang Tested-by: Eric Bénard Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-pltfm.c | 8 +++++--- include/linux/mmc/sdhci-pltfm.h | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'include/linux/mmc') diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index 685202be2778..00e8a8ab638e 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -52,15 +52,17 @@ static struct sdhci_ops sdhci_pltfm_ops = { static int __devinit sdhci_pltfm_probe(struct platform_device *pdev) { - struct sdhci_pltfm_data *pdata = pdev->dev.platform_data; const struct platform_device_id *platid = platform_get_device_id(pdev); + struct sdhci_pltfm_data *pdata; struct sdhci_host *host; struct sdhci_pltfm_host *pltfm_host; struct resource *iomem; int ret; - if (!pdata && platid && platid->driver_data) + if (platid && platid->driver_data) pdata = (void *)platid->driver_data; + else + pdata = pdev->dev.platform_data; iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!iomem) { @@ -109,7 +111,7 @@ static int __devinit sdhci_pltfm_probe(struct platform_device *pdev) } if (pdata && pdata->init) { - ret = pdata->init(host); + ret = pdata->init(host, pdata); if (ret) goto err_plat_init; } diff --git a/include/linux/mmc/sdhci-pltfm.h b/include/linux/mmc/sdhci-pltfm.h index 0239bd70241e..548d59d404cb 100644 --- a/include/linux/mmc/sdhci-pltfm.h +++ b/include/linux/mmc/sdhci-pltfm.h @@ -28,7 +28,7 @@ struct sdhci_host; struct sdhci_pltfm_data { struct sdhci_ops *ops; unsigned int quirks; - int (*init)(struct sdhci_host *host); + int (*init)(struct sdhci_host *host, struct sdhci_pltfm_data *pdata); void (*exit)(struct sdhci_host *host); }; -- cgit v1.2.3 From 777271d0f33da306575ef776c75f66fc27246bf0 Mon Sep 17 00:00:00 2001 From: Arnd Hannemann Date: Tue, 24 Aug 2010 17:27:01 +0200 Subject: mmc: Allow the platform to specify the sh_mmcif get_cd handler In some platforms (e.g. AP4EVB) the card detect pin of a slot is not directly connected to the sh_mmcif controller, so that polling needs to be used. To overcome the overhead induced by querying the controller on each poll cycle, card detection can be handled in the platform code more efficiently. This patch exposes a get_cd hook for that purpose. Signed-off-by: Arnd Hannemann Tested-by: Yusuke Goda Signed-off-by: Samuel Ortiz --- drivers/mmc/host/sh_mmcif.c | 12 ++++++++++++ include/linux/mmc/sh_mmcif.h | 1 + 2 files changed, 13 insertions(+) (limited to 'include/linux/mmc') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 0f06b8002814..ddd09840520b 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -710,9 +710,21 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) host->bus_width = ios->bus_width; } +static int sh_mmcif_get_cd(struct mmc_host *mmc) +{ + struct sh_mmcif_host *host = mmc_priv(mmc); + struct sh_mmcif_plat_data *p = host->pd->dev.platform_data; + + if (!p->get_cd) + return -ENOSYS; + else + return p->get_cd(host->pd); +} + static struct mmc_host_ops sh_mmcif_ops = { .request = sh_mmcif_request, .set_ios = sh_mmcif_set_ios, + .get_cd = sh_mmcif_get_cd, }; static void sh_mmcif_detect(struct mmc_host *mmc) diff --git a/include/linux/mmc/sh_mmcif.h b/include/linux/mmc/sh_mmcif.h index d4a2ebbdab4b..d19e2114fd86 100644 --- a/include/linux/mmc/sh_mmcif.h +++ b/include/linux/mmc/sh_mmcif.h @@ -34,6 +34,7 @@ struct sh_mmcif_plat_data { void (*set_pwr)(struct platform_device *pdev, int state); void (*down_pwr)(struct platform_device *pdev); + int (*get_cd)(struct platform_device *pdef); u8 sup_pclk; /* 1 :SH7757, 0: SH7724/SH7372 */ unsigned long caps; u32 ocr; -- cgit v1.2.3 From 2f6ba5792ce9e4a731baeb976ccc72e0cf43d20b Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Thu, 4 Nov 2010 12:21:25 +0900 Subject: mmc: sh_mmcif: Convert extern inline to static inline. Presently the extern inline case results in a compiler warning on ARM due to the memory barrier definition used in the I/O routines. These ultimately all want to be static inline anyways, so just convert them all in place. Signed-off-by: Paul Mundt --- include/linux/mmc/sh_mmcif.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'include/linux/mmc') diff --git a/include/linux/mmc/sh_mmcif.h b/include/linux/mmc/sh_mmcif.h index d19e2114fd86..5c99da1078aa 100644 --- a/include/linux/mmc/sh_mmcif.h +++ b/include/linux/mmc/sh_mmcif.h @@ -59,19 +59,19 @@ struct sh_mmcif_plat_data { #define MMCIF_CE_HOST_STS2 0x0000004C #define MMCIF_CE_VERSION 0x0000007C -extern inline u32 sh_mmcif_readl(void __iomem *addr, int reg) +static inline u32 sh_mmcif_readl(void __iomem *addr, int reg) { return readl(addr + reg); } -extern inline void sh_mmcif_writel(void __iomem *addr, int reg, u32 val) +static inline void sh_mmcif_writel(void __iomem *addr, int reg, u32 val) { writel(val, addr + reg); } #define SH_MMCIF_BBS 512 /* boot block size */ -extern inline void sh_mmcif_boot_cmd_send(void __iomem *base, +static inline void sh_mmcif_boot_cmd_send(void __iomem *base, unsigned long cmd, unsigned long arg) { sh_mmcif_writel(base, MMCIF_CE_INT, 0); @@ -79,7 +79,7 @@ extern inline void sh_mmcif_boot_cmd_send(void __iomem *base, sh_mmcif_writel(base, MMCIF_CE_CMD_SET, cmd); } -extern inline int sh_mmcif_boot_cmd_poll(void __iomem *base, unsigned long mask) +static inline int sh_mmcif_boot_cmd_poll(void __iomem *base, unsigned long mask) { unsigned long tmp; int cnt; @@ -95,14 +95,14 @@ extern inline int sh_mmcif_boot_cmd_poll(void __iomem *base, unsigned long mask) return -1; } -extern inline int sh_mmcif_boot_cmd(void __iomem *base, +static inline int sh_mmcif_boot_cmd(void __iomem *base, unsigned long cmd, unsigned long arg) { sh_mmcif_boot_cmd_send(base, cmd, arg); return sh_mmcif_boot_cmd_poll(base, 0x00010000); } -extern inline int sh_mmcif_boot_do_read_single(void __iomem *base, +static inline int sh_mmcif_boot_do_read_single(void __iomem *base, unsigned int block_nr, unsigned long *buf) { @@ -125,7 +125,7 @@ extern inline int sh_mmcif_boot_do_read_single(void __iomem *base, return 0; } -extern inline int sh_mmcif_boot_do_read(void __iomem *base, +static inline int sh_mmcif_boot_do_read(void __iomem *base, unsigned long first_block, unsigned long nr_blocks, void *buf) @@ -143,7 +143,7 @@ extern inline int sh_mmcif_boot_do_read(void __iomem *base, return ret; } -extern inline void sh_mmcif_boot_init(void __iomem *base) +static inline void sh_mmcif_boot_init(void __iomem *base) { unsigned long tmp; @@ -177,7 +177,7 @@ extern inline void sh_mmcif_boot_init(void __iomem *base) sh_mmcif_boot_cmd(base, 0x03400040, 0x00010000); } -extern inline void sh_mmcif_boot_slurp(void __iomem *base, +static inline void sh_mmcif_boot_slurp(void __iomem *base, unsigned char *buf, unsigned long no_bytes) { -- cgit v1.2.3 From ed919b0125b26dcc052e44836f66e7e1f5c49c7e Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Fri, 19 Nov 2010 09:29:09 +0200 Subject: mmc: sdio: fix runtime PM anomalies by introducing MMC_CAP_POWER_OFF_CARD Some board/card/host configurations are not capable of powering off the card after boot. To support such configurations, and to allow smoother transition to runtime PM behavior, MMC_CAP_POWER_OFF_CARD is added, so hosts need to explicitly indicate whether it's OK to power off their cards after boot. SDIO core will enable runtime PM for a card only if that cap is set. As a result, the card will be powered down after boot, and will only be powered up again when a driver is loaded (and then it's up to the driver to decide whether power will be kept or not). This will prevent sdio_bus_probe() failures with setups that do not support powering off the card. Reported-and-tested-by: Daniel Drake Reported-and-tested-by: Arnd Hannemann Signed-off-by: Ohad Ben-Cohen Signed-off-by: Chris Ball --- drivers/mmc/core/sdio.c | 37 +++++++++++++++++++++++-------------- drivers/mmc/core/sdio_bus.c | 33 ++++++++++++++++++++++----------- include/linux/mmc/host.h | 1 + 3 files changed, 46 insertions(+), 25 deletions(-) (limited to 'include/linux/mmc') diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 42a949b723b8..efef5f94ac42 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -547,9 +547,11 @@ static void mmc_sdio_detect(struct mmc_host *host) BUG_ON(!host->card); /* Make sure card is powered before detecting it */ - err = pm_runtime_get_sync(&host->card->dev); - if (err < 0) - goto out; + if (host->caps & MMC_CAP_POWER_OFF_CARD) { + err = pm_runtime_get_sync(&host->card->dev); + if (err < 0) + goto out; + } mmc_claim_host(host); @@ -571,7 +573,8 @@ static void mmc_sdio_detect(struct mmc_host *host) * is about to show up at this point, the _sync variant is * desirable anyway. */ - pm_runtime_put_sync(&host->card->dev); + if (host->caps & MMC_CAP_POWER_OFF_CARD) + pm_runtime_put_sync(&host->card->dev); out: if (err) { @@ -728,16 +731,21 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr) card = host->card; /* - * Let runtime PM core know our card is active + * Enable runtime PM only if supported by host+card+board */ - err = pm_runtime_set_active(&card->dev); - if (err) - goto remove; + if (host->caps & MMC_CAP_POWER_OFF_CARD) { + /* + * Let runtime PM core know our card is active + */ + err = pm_runtime_set_active(&card->dev); + if (err) + goto remove; - /* - * Enable runtime PM for this card - */ - pm_runtime_enable(&card->dev); + /* + * Enable runtime PM for this card + */ + pm_runtime_enable(&card->dev); + } /* * The number of functions on the card is encoded inside @@ -755,9 +763,10 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr) goto remove; /* - * Enable Runtime PM for this func + * Enable Runtime PM for this func (if supported) */ - pm_runtime_enable(&card->sdio_func[i]->dev); + if (host->caps & MMC_CAP_POWER_OFF_CARD) + pm_runtime_enable(&card->sdio_func[i]->dev); } mmc_release_host(host); diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c index 2716c7ab6bbf..203da443e339 100644 --- a/drivers/mmc/core/sdio_bus.c +++ b/drivers/mmc/core/sdio_bus.c @@ -17,6 +17,7 @@ #include #include +#include #include #include "sdio_cis.h" @@ -132,9 +133,11 @@ static int sdio_bus_probe(struct device *dev) * it should call pm_runtime_put_noidle() in its probe routine and * pm_runtime_get_noresume() in its remove routine. */ - ret = pm_runtime_get_sync(dev); - if (ret < 0) - goto out; + if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD) { + ret = pm_runtime_get_sync(dev); + if (ret < 0) + goto out; + } /* Set the default block size so the driver is sure it's something * sensible. */ @@ -151,7 +154,8 @@ static int sdio_bus_probe(struct device *dev) return 0; disable_runtimepm: - pm_runtime_put_noidle(dev); + if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD) + pm_runtime_put_noidle(dev); out: return ret; } @@ -160,12 +164,14 @@ static int sdio_bus_remove(struct device *dev) { struct sdio_driver *drv = to_sdio_driver(dev->driver); struct sdio_func *func = dev_to_sdio_func(dev); - int ret; + int ret = 0; /* Make sure card is powered before invoking ->remove() */ - ret = pm_runtime_get_sync(dev); - if (ret < 0) - goto out; + if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD) { + ret = pm_runtime_get_sync(dev); + if (ret < 0) + goto out; + } drv->remove(func); @@ -178,10 +184,12 @@ static int sdio_bus_remove(struct device *dev) } /* First, undo the increment made directly above */ - pm_runtime_put_noidle(dev); + if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD) + pm_runtime_put_noidle(dev); /* Then undo the runtime PM settings in sdio_bus_probe() */ - pm_runtime_put_noidle(dev); + if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD) + pm_runtime_put_noidle(dev); out: return ret; @@ -191,6 +199,8 @@ out: static int sdio_bus_pm_prepare(struct device *dev) { + struct sdio_func *func = dev_to_sdio_func(dev); + /* * Resume an SDIO device which was suspended at run time at this * point, in order to allow standard SDIO suspend/resume paths @@ -212,7 +222,8 @@ static int sdio_bus_pm_prepare(struct device *dev) * since there is little point in failing system suspend if a * device can't be resumed. */ - pm_runtime_resume(dev); + if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD) + pm_runtime_resume(dev); return 0; } diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 6d87f68ce4b6..30f6fad99a58 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -168,6 +168,7 @@ struct mmc_host { /* DDR mode at 1.8V */ #define MMC_CAP_1_2V_DDR (1 << 12) /* can support */ /* DDR mode at 1.2V */ +#define MMC_CAP_POWER_OFF_CARD (1 << 13) /* Can power off after boot */ mmc_pm_flag_t pm_caps; /* supported pm features */ -- cgit v1.2.3