diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-02-11 10:56:48 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-02-11 10:56:48 -0800 |
commit | aa7ed01f93ff7e149cad46f13f66b269d59c9bc0 (patch) | |
tree | ab46a44f3c83c75e1c81f211acd0d68ffe60dd7c /drivers | |
parent | 7796c11c728ad40ba4151d559a949c002deffb9a (diff) | |
parent | 017210d1c0dc2e2d3b142985cb31d90b98dc0f0f (diff) |
Merge tag 'mmc-v3.20-1' of git://git.linaro.org/people/ulf.hansson/mmc
Pull MMC updates from Ulf Hansson:
"MMC core:
- Support for MMC power sequences.
- SDIO function devicetree subnode parsing.
- Refactor the hardware reset routines and enable it for SD cards.
- Various code quality improvements, especially for slot-gpio.
MMC host:
- dw_mmc: Various fixes and cleanups.
- dw_mmc: Convert to mmc_send_tuning().
- moxart: Fix probe logic.
- sdhci: Various fixes and cleanups
- sdhci: Asynchronous request handling support.
- sdhci-pxav3: Various fixes and cleanups.
- sdhci-tegra: Fixes for T114, T124 and T132.
- rtsx: Various fixes and cleanups.
- rtsx: Support for SDIO.
- sdhi/tmio: Refactor and cleanup of header files.
- omap_hsmmc: Use slot-gpio and common MMC DT parser.
- Make all hosts to deal with errors from mmc_of_parse().
- sunxi: Various fixes and cleanups.
- sdhci: Support for Fujitsu SDHCI controller f_sdh30"
* tag 'mmc-v3.20-1' of git://git.linaro.org/people/ulf.hansson/mmc: (117 commits)
mmc: sdhci-s3c: solve problem with sleeping in atomic context
mmc: pwrseq: add driver for emmc hardware reset
mmc: moxart: fix probe logic
mmc: core: Invoke mmc_pwrseq_post_power_on() prior MMC_POWER_ON state
mmc: pwrseq_simple: Add optional reference clock support
mmc: pwrseq: Document optional clock for the simple power sequence
mmc: pwrseq_simple: Extend to support more pins
mmc: pwrseq: Document that simple sequence support more than one GPIO
mmc: Add hardware dependencies for sdhci-pxav3 and sdhci-pxav2
mmc: sdhci-pxav3: Modify clock settings for the SDR50 and DDR50 modes
mmc: sdhci-pxav3: Extend binding with SDIO3 conf reg for the Armada 38x
mmc: sdhci-pxav3: Fix Armada 38x controller's caps according to erratum ERR-7878951
mmc: sdhci-pxav3: Fix SDR50 and DDR50 capabilities for the Armada 38x flavor
mmc: sdhci: switch voltage before sdhci_set_ios in runtime resume
mmc: tegra: Write xfer_mode, CMD regs in together
mmc: Resolve BKOPS compatability issue
mmc: sdhci-pxav3: fix setting of pdata->clk_delay_cycles
mmc: dw_mmc: rockchip: remove incorrect __exit_p()
mmc: dw_mmc: exynos: remove incorrect __exit_p()
mmc: Fix menuconfig alignment of MMC_SDHCI_* options
...
Diffstat (limited to 'drivers')
52 files changed, 1964 insertions, 1112 deletions
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 4409d79ed650..c69afb5e264e 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -2147,7 +2147,7 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card, */ snprintf(md->disk->disk_name, sizeof(md->disk->disk_name), - "mmcblk%d%s", md->name_idx, subname ? subname : ""); + "mmcblk%u%s", md->name_idx, subname ? subname : ""); if (mmc_card_mmc(card)) blk_queue_logical_block_size(md->queue.queue, @@ -2193,7 +2193,6 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card, static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card) { sector_t size; - struct mmc_blk_data *md; if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) { /* @@ -2209,9 +2208,8 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card) size = card->csd.capacity << (card->csd.read_blkbits - 9); } - md = mmc_blk_alloc_req(card, &card->dev, size, false, NULL, + return mmc_blk_alloc_req(card, &card->dev, size, false, NULL, MMC_BLK_DATA_AREA_MAIN); - return md; } static int mmc_blk_alloc_part(struct mmc_card *card, diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c index 0a7430f94d29..7dac4695163b 100644 --- a/drivers/mmc/card/mmc_test.c +++ b/drivers/mmc/card/mmc_test.c @@ -2342,20 +2342,16 @@ static int mmc_test_hw_reset(struct mmc_test_card *test) struct mmc_host *host = card->host; int err; - err = mmc_hw_reset_check(host); + if (!mmc_card_mmc(card) || !mmc_can_reset(card)) + return RESULT_UNSUP_CARD; + + err = mmc_hw_reset(host); if (!err) return RESULT_OK; + else if (err == -EOPNOTSUPP) + return RESULT_UNSUP_HOST; - if (err == -ENOSYS) - return RESULT_FAIL; - - if (err != -EOPNOTSUPP) - return err; - - if (!mmc_can_reset(card)) - return RESULT_UNSUP_CARD; - - return RESULT_UNSUP_HOST; + return RESULT_FAIL; } static const struct mmc_test_case mmc_test_cases[] = { diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile index 38ed210ce2f3..2c25138f28b7 100644 --- a/drivers/mmc/core/Makefile +++ b/drivers/mmc/core/Makefile @@ -8,5 +8,5 @@ mmc_core-y := core.o bus.o host.o \ sdio.o sdio_ops.o sdio_bus.o \ sdio_cis.o sdio_io.o sdio_irq.o \ quirks.o slot-gpio.o - +mmc_core-$(CONFIG_OF) += pwrseq.o pwrseq_simple.o pwrseq_emmc.o mmc_core-$(CONFIG_DEBUG_FS) += debugfs.o diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index 86d271148528..c5ef10065a4a 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -16,6 +16,7 @@ #include <linux/err.h> #include <linux/slab.h> #include <linux/stat.h> +#include <linux/of.h> #include <linux/pm_runtime.h> #include <linux/mmc/card.h> @@ -321,6 +322,8 @@ int mmc_add_card(struct mmc_card *card) #endif mmc_init_context_info(card->host); + card->dev.of_node = mmc_of_find_child_device(card->host, 0); + ret = device_add(&card->dev); if (ret) return ret; @@ -349,6 +352,7 @@ void mmc_remove_card(struct mmc_card *card) mmc_hostname(card->host), card->rca); } device_del(&card->dev); + of_node_put(card->dev.of_node); } put_device(&card->dev); diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 9584bffa8b22..23f10f72e5f3 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -40,6 +40,7 @@ #include "bus.h" #include "host.h" #include "sdio_bus.h" +#include "pwrseq.h" #include "mmc_ops.h" #include "sd_ops.h" @@ -185,13 +186,14 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) EXPORT_SYMBOL(mmc_request_done); -static void -mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) +static int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) { #ifdef CONFIG_MMC_DEBUG unsigned int i, sz; struct scatterlist *sg; #endif + if (mmc_card_removed(host->card)) + return -ENOMEDIUM; if (mrq->sbc) { pr_debug("<%s: starting CMD%u arg %08x flags %08x>\n", @@ -251,6 +253,8 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) mmc_host_clk_hold(host); led_trigger_event(host->led, LED_FULL); host->ops->request(host, mrq); + + return 0; } /** @@ -271,7 +275,7 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception) BUG_ON(!card); - if (!card->ext_csd.bkops_en || mmc_card_doing_bkops(card)) + if (!card->ext_csd.man_bkops_en || mmc_card_doing_bkops(card)) return; err = mmc_read_bkops_status(card); @@ -345,29 +349,34 @@ static void mmc_wait_done(struct mmc_request *mrq) */ static int __mmc_start_data_req(struct mmc_host *host, struct mmc_request *mrq) { + int err; + mrq->done = mmc_wait_data_done; mrq->host = host; - if (mmc_card_removed(host->card)) { - mrq->cmd->error = -ENOMEDIUM; + + err = mmc_start_request(host, mrq); + if (err) { + mrq->cmd->error = err; mmc_wait_data_done(mrq); - return -ENOMEDIUM; } - mmc_start_request(host, mrq); - return 0; + return err; } static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq) { + int err; + init_completion(&mrq->completion); mrq->done = mmc_wait_done; - if (mmc_card_removed(host->card)) { - mrq->cmd->error = -ENOMEDIUM; + + err = mmc_start_request(host, mrq); + if (err) { + mrq->cmd->error = err; complete(&mrq->completion); - return -ENOMEDIUM; } - mmc_start_request(host, mrq); - return 0; + + return err; } /* @@ -1077,6 +1086,30 @@ void mmc_set_ungated(struct mmc_host *host) } #endif +int mmc_execute_tuning(struct mmc_card *card) +{ + struct mmc_host *host = card->host; + u32 opcode; + int err; + + if (!host->ops->execute_tuning) + return 0; + + if (mmc_card_mmc(card)) + opcode = MMC_SEND_TUNING_BLOCK_HS200; + else + opcode = MMC_SEND_TUNING_BLOCK; + + mmc_host_clk_hold(host); + err = host->ops->execute_tuning(host, opcode); + mmc_host_clk_release(host); + + if (err) + pr_err("%s: tuning execution failed\n", mmc_hostname(host)); + + return err; +} + /* * Change the bus mode (open drain/push-pull) of a host. */ @@ -1232,6 +1265,34 @@ EXPORT_SYMBOL(mmc_of_parse_voltage); #endif /* CONFIG_OF */ +static int mmc_of_get_func_num(struct device_node *node) +{ + u32 reg; + int ret; + + ret = of_property_read_u32(node, "reg", ®); + if (ret < 0) + return ret; + + return reg; +} + +struct device_node *mmc_of_find_child_device(struct mmc_host *host, + unsigned func_num) +{ + struct device_node *node; + + if (!host->parent || !host->parent->of_node) + return NULL; + + for_each_child_of_node(host->parent->of_node, node) { + if (mmc_of_get_func_num(node) == func_num) + return node; + } + + return NULL; +} + #ifdef CONFIG_REGULATOR /** @@ -1555,6 +1616,8 @@ void mmc_power_up(struct mmc_host *host, u32 ocr) mmc_host_clk_hold(host); + mmc_pwrseq_pre_power_on(host); + host->ios.vdd = fls(ocr) - 1; host->ios.power_mode = MMC_POWER_UP; /* Set initial state and call mmc_set_ios */ @@ -1574,6 +1637,8 @@ void mmc_power_up(struct mmc_host *host, u32 ocr) */ mmc_delay(10); + mmc_pwrseq_post_power_on(host); + host->ios.clock = host->f_init; host->ios.power_mode = MMC_POWER_ON; @@ -1595,6 +1660,8 @@ void mmc_power_off(struct mmc_host *host) mmc_host_clk_hold(host); + mmc_pwrseq_power_off(host); + host->ios.clock = 0; host->ios.vdd = 0; @@ -2245,67 +2312,28 @@ static void mmc_hw_reset_for_init(struct mmc_host *host) mmc_host_clk_release(host); } -int mmc_can_reset(struct mmc_card *card) -{ - u8 rst_n_function; - - if (!mmc_card_mmc(card)) - return 0; - rst_n_function = card->ext_csd.rst_n_function; - if ((rst_n_function & EXT_CSD_RST_N_EN_MASK) != EXT_CSD_RST_N_ENABLED) - return 0; - return 1; -} -EXPORT_SYMBOL(mmc_can_reset); - -static int mmc_do_hw_reset(struct mmc_host *host, int check) +int mmc_hw_reset(struct mmc_host *host) { - struct mmc_card *card = host->card; - - if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset) - return -EOPNOTSUPP; + int ret; - if (!card) + if (!host->card) return -EINVAL; - if (!mmc_can_reset(card)) + mmc_bus_get(host); + if (!host->bus_ops || host->bus_dead || !host->bus_ops->reset) { + mmc_bus_put(host); return -EOPNOTSUPP; - - mmc_host_clk_hold(host); - mmc_set_clock(host, host->f_init); - - host->ops->hw_reset(host); - - /* If the reset has happened, then a status command will fail */ - if (check) { - u32 status; - - if (!mmc_send_status(card, &status)) { - mmc_host_clk_release(host); - return -ENOSYS; - } } - /* Set initial state and call mmc_set_ios */ - mmc_set_initial_state(host); + ret = host->bus_ops->reset(host); + mmc_bus_put(host); - mmc_host_clk_release(host); + pr_warn("%s: tried to reset card\n", mmc_hostname(host)); - return host->bus_ops->power_restore(host); -} - -int mmc_hw_reset(struct mmc_host *host) -{ - return mmc_do_hw_reset(host, 0); + return ret; } EXPORT_SYMBOL(mmc_hw_reset); -int mmc_hw_reset_check(struct mmc_host *host) -{ - return mmc_do_hw_reset(host, 1); -} -EXPORT_SYMBOL(mmc_hw_reset_check); - static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq) { host->f_init = freq; diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index d76597c65e3a..cfba3c05aab1 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -27,11 +27,15 @@ struct mmc_bus_ops { int (*power_restore)(struct mmc_host *); int (*alive)(struct mmc_host *); int (*shutdown)(struct mmc_host *); + int (*reset)(struct mmc_host *); }; void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops); void mmc_detach_bus(struct mmc_host *host); +struct device_node *mmc_of_find_child_device(struct mmc_host *host, + unsigned func_num); + void mmc_init_erase(struct mmc_card *card); void mmc_set_chip_select(struct mmc_host *host, int mode); @@ -82,5 +86,8 @@ void mmc_add_card_debugfs(struct mmc_card *card); void mmc_remove_card_debugfs(struct mmc_card *card); void mmc_init_context_info(struct mmc_host *host); + +int mmc_execute_tuning(struct mmc_card *card); + #endif diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 270d58a4c43d..8be0df758e68 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -29,13 +29,20 @@ #include "core.h" #include "host.h" +#include "slot-gpio.h" +#include "pwrseq.h" #define cls_dev_to_mmc_host(d) container_of(d, struct mmc_host, class_dev) +static DEFINE_IDR(mmc_host_idr); +static DEFINE_SPINLOCK(mmc_host_lock); + static void mmc_host_classdev_release(struct device *dev) { struct mmc_host *host = cls_dev_to_mmc_host(dev); - mutex_destroy(&host->slot.lock); + spin_lock(&mmc_host_lock); + idr_remove(&mmc_host_idr, host->index); + spin_unlock(&mmc_host_lock); kfree(host); } @@ -54,9 +61,6 @@ void mmc_unregister_host_class(void) class_unregister(&mmc_host_class); } -static DEFINE_IDR(mmc_host_idr); -static DEFINE_SPINLOCK(mmc_host_lock); - #ifdef CONFIG_MMC_CLKGATE static ssize_t clkgate_delay_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -367,16 +371,10 @@ int mmc_of_parse(struct mmc_host *host) ret = mmc_gpiod_request_cd(host, "cd", 0, true, 0, &cd_gpio_invert); - if (ret) { - if (ret == -EPROBE_DEFER) - return ret; - if (ret != -ENOENT) { - dev_err(host->parent, - "Failed to request CD GPIO: %d\n", - ret); - } - } else + if (!ret) dev_info(host->parent, "Got CD GPIO\n"); + else if (ret != -ENOENT) + return ret; /* * There are two ways to flag that the CD line is inverted: @@ -397,16 +395,10 @@ int mmc_of_parse(struct mmc_host *host) ro_cap_invert = of_property_read_bool(np, "wp-inverted"); ret = mmc_gpiod_request_ro(host, "wp", 0, false, 0, &ro_gpio_invert); - if (ret) { - if (ret == -EPROBE_DEFER) - goto out; - if (ret != -ENOENT) { - dev_err(host->parent, - "Failed to request WP GPIO: %d\n", - ret); - } - } else + if (!ret) dev_info(host->parent, "Got WP GPIO\n"); + else if (ret != -ENOENT) + return ret; /* See the comment on CD inversion above */ if (ro_cap_invert ^ ro_gpio_invert) @@ -457,11 +449,7 @@ int mmc_of_parse(struct mmc_host *host) host->dsr_req = 0; } - return 0; - -out: - mmc_gpio_free_cd(host); - return ret; + return mmc_pwrseq_alloc(host); } EXPORT_SYMBOL(mmc_of_parse); @@ -491,8 +479,10 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) host->index = err; spin_unlock(&mmc_host_lock); idr_preload_end(); - if (err < 0) - goto free; + if (err < 0) { + kfree(host); + return NULL; + } dev_set_name(&host->class_dev, "mmc%d", host->index); @@ -501,10 +491,12 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) host->class_dev.class = &mmc_host_class; device_initialize(&host->class_dev); - mmc_host_clk_init(host); + if (mmc_gpio_alloc(host)) { + put_device(&host->class_dev); + return NULL; + } - mutex_init(&host->slot.lock); - host->slot.cd_irq = -EINVAL; + mmc_host_clk_init(host); spin_lock_init(&host->lock); init_waitqueue_head(&host->wq); @@ -525,10 +517,6 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) host->max_blk_count = PAGE_CACHE_SIZE / 512; return host; - -free: - kfree(host); - return NULL; } EXPORT_SYMBOL(mmc_alloc_host); @@ -601,10 +589,7 @@ EXPORT_SYMBOL(mmc_remove_host); */ void mmc_free_host(struct mmc_host *host) { - spin_lock(&mmc_host_lock); - idr_remove(&mmc_host_idr, host->index); - spin_unlock(&mmc_host_lock); - + mmc_pwrseq_free(host); put_device(&host->class_dev); } diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 7466ce098e60..1d41e8541f38 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -483,11 +483,13 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd) /* check whether the eMMC card supports BKOPS */ if (ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) { card->ext_csd.bkops = 1; - card->ext_csd.bkops_en = ext_csd[EXT_CSD_BKOPS_EN]; + card->ext_csd.man_bkops_en = + (ext_csd[EXT_CSD_BKOPS_EN] & + EXT_CSD_MANUAL_BKOPS_MASK); card->ext_csd.raw_bkops_status = ext_csd[EXT_CSD_BKOPS_STATUS]; - if (!card->ext_csd.bkops_en) - pr_info("%s: BKOPS_EN bit is not set\n", + if (!card->ext_csd.man_bkops_en) + pr_info("%s: MAN_BKOPS_EN bit is not set\n", mmc_hostname(card->host)); } @@ -1155,38 +1157,6 @@ bus_speed: return err; } -const u8 tuning_blk_pattern_4bit[MMC_TUNING_BLK_PATTERN_4BIT_SIZE] = { - 0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc, - 0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef, - 0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb, - 0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef, - 0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c, - 0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee, - 0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff, - 0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde, -}; -EXPORT_SYMBOL(tuning_blk_pattern_4bit); - -const u8 tuning_blk_pattern_8bit[MMC_TUNING_BLK_PATTERN_8BIT_SIZE] = { - 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, - 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc, - 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff, - 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff, - 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd, - 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, - 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff, - 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, - 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, - 0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, - 0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, - 0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, - 0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, - 0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, -}; -EXPORT_SYMBOL(tuning_blk_pattern_8bit); - /* * Execute tuning sequence to seek the proper bus operating * conditions for HS200 and HS400, which sends CMD21 to the device. @@ -1194,7 +1164,6 @@ EXPORT_SYMBOL(tuning_blk_pattern_8bit); static int mmc_hs200_tuning(struct mmc_card *card) { struct mmc_host *host = card->host; - int err = 0; /* * Timing should be adjusted to the HS400 target @@ -1205,18 +1174,7 @@ static int mmc_hs200_tuning(struct mmc_card *card) if (host->ops->prepare_hs400_tuning) host->ops->prepare_hs400_tuning(host, &host->ios); - if (host->ops->execute_tuning) { - mmc_host_clk_hold(host); - err = host->ops->execute_tuning(host, - MMC_SEND_TUNING_BLOCK_HS200); - mmc_host_clk_release(host); - - if (err) - pr_err("%s: tuning execution failed\n", - mmc_hostname(host)); - } - - return err; + return mmc_execute_tuning(card); } /* @@ -1297,6 +1255,12 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } /* + * Call the optional HC's init_card function to handle quirks. + */ + if (host->ops->init_card) + host->ops->init_card(host, card); + + /* * For native busses: set card RCA and quit open drain mode. */ if (!mmc_host_is_spi(host)) { @@ -1821,6 +1785,46 @@ static int mmc_power_restore(struct mmc_host *host) return ret; } +int mmc_can_reset(struct mmc_card *card) +{ + u8 rst_n_function; + + rst_n_function = card->ext_csd.rst_n_function; + if ((rst_n_function & EXT_CSD_RST_N_EN_MASK) != EXT_CSD_RST_N_ENABLED) + return 0; + return 1; +} +EXPORT_SYMBOL(mmc_can_reset); + +static int mmc_reset(struct mmc_host *host) +{ + struct mmc_card *card = host->card; + u32 status; + + if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset) + return -EOPNOTSUPP; + + if (!mmc_can_reset(card)) + return -EOPNOTSUPP; + + mmc_host_clk_hold(host); + mmc_set_clock(host, host->f_init); + + host->ops->hw_reset(host); + + /* If the reset has happened, then a status command will fail */ + if (!mmc_send_status(card, &status)) { + mmc_host_clk_release(host); + return -ENOSYS; + } + + /* Set initial state and call mmc_set_ios */ + mmc_set_initial_state(host); + mmc_host_clk_release(host); + + return mmc_power_restore(host); +} + static const struct mmc_bus_ops mmc_ops = { .remove = mmc_remove, .detect = mmc_detect, @@ -1831,6 +1835,7 @@ static const struct mmc_bus_ops mmc_ops = { .power_restore = mmc_power_restore, .alive = mmc_alive, .shutdown = mmc_shutdown, + .reset = mmc_reset, }; /* diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 3b044c5b029c..0ea042dc7443 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/dr |