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/drivers/mmc/core/mmc_ops.c @@ -23,6 +23,36 @@ #define MMC_OPS_TIMEOUT_MS (10 * 60 * 1000) /* 10 minute timeout */ +static const u8 tuning_blk_pattern_4bit[] = { + 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, +}; + +static const u8 tuning_blk_pattern_8bit[] = { + 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, +}; + static inline int __mmc_send_status(struct mmc_card *card, u32 *status, bool ignore_crc) { diff --git a/drivers/mmc/core/pwrseq.c b/drivers/mmc/core/pwrseq.c new file mode 100644 index 000000000000..862356123d78 --- /dev/null +++ b/drivers/mmc/core/pwrseq.c @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2014 Linaro Ltd + * + * Author: Ulf Hansson <ulf.hansson@linaro.org> + * + * License terms: GNU General Public License (GPL) version 2 + * + * MMC power sequence management + */ +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/of.h> +#include <linux/of_platform.h> + +#include <linux/mmc/host.h> + +#include "pwrseq.h" + +struct mmc_pwrseq_match { + const char *compatible; + int (*alloc)(struct mmc_host *host, struct device *dev); +}; + +static struct mmc_pwrseq_match pwrseq_match[] = { + { + .compatible = "mmc-pwrseq-simple", + .alloc = mmc_pwrseq_simple_alloc, + }, { + .compatible = "mmc-pwrseq-emmc", + .alloc = mmc_pwrseq_emmc_alloc, + }, +}; + +static struct mmc_pwrseq_match *mmc_pwrseq_find(struct device_node *np) +{ + struct mmc_pwrseq_match *match = ERR_PTR(-ENODEV); + int i; + + for (i = 0; i < ARRAY_SIZE(pwrseq_match); i++) { + if (of_device_is_compatible(np, pwrseq_match[i].compatible)) { + match = &pwrseq_match[i]; + break; + } + } + + return match; +} + +int mmc_pwrseq_alloc(struct mmc_host *host) +{ + struct platform_device *pdev; + struct device_node *np; + struct mmc_pwrseq_match *match; + int ret = 0; + + np = of_parse_phandle(host->parent->of_node, "mmc-pwrseq", 0); + if (!np) + return 0; + + pdev = of_find_device_by_node(np); + if (!pdev) { + ret = -ENODEV; + goto err; + } + + match = mmc_pwrseq_find(np); + if (IS_ERR(match)) { + ret = PTR_ERR(match); + goto err; + } + + ret = match->alloc(host, &pdev->dev); + if (!ret) + dev_info(host->parent, "allocated mmc-pwrseq\n"); + +err: + of_node_put(np); + return ret; +} + +void mmc_pwrseq_pre_power_on(struct mmc_host *host) +{ + struct mmc_pwrseq *pwrseq = host->pwrseq; + + if (pwrseq && pwrseq->ops && pwrseq->ops->pre_power_on) + pwrseq->ops->pre_power_on(host); +} + +void mmc_pwrseq_post_power_on(struct mmc_host *host) +{ + struct mmc_pwrseq *pwrseq = host->pwrseq; + + if (pwrseq && pwrseq->ops && pwrseq->ops->post_power_on) + pwrseq->ops->post_power_on(host); +} + +void mmc_pwrseq_power_off(struct mmc_host *host) +{ + struct mmc_pwrseq *pwrseq = host->pwrseq; + + if (pwrseq && pwrseq->ops && pwrseq->ops->power_off) + pwrseq->ops->power_off(host); +} + +void mmc_pwrseq_free(struct mmc_host *host) +{ + struct mmc_pwrseq *pwrseq = host->pwrseq; + + if (pwrseq && pwrseq->ops && pwrseq->ops->free) + pwrseq->ops->free(host); +} diff --git a/drivers/mmc/core/pwrseq.h b/drivers/mmc/core/pwrseq.h new file mode 100644 index 000000000000..aba3409e8d6e --- /dev/null +++ b/drivers/mmc/core/pwrseq.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2014 Linaro Ltd + * + * Author: Ulf Hansson <ulf.hansson@linaro.org> + * + * License terms: GNU General Public License (GPL) version 2 + */ +#ifndef _MMC_CORE_PWRSEQ_H +#define _MMC_CORE_PWRSEQ_H + +struct mmc_pwrseq_ops { + void (*pre_power_on)(struct mmc_host *host); + void (*post_power_on)(struct mmc_host *host); + void (*power_off)(struct mmc_host *host); + void (*free)(struct mmc_host *host); +}; + +struct mmc_pwrseq { + struct mmc_pwrseq_ops *ops; +}; + +#ifdef CONFIG_OF + +int mmc_pwrseq_alloc(struct mmc_host *host); +void mmc_pwrseq_pre_power_on(struct mmc_host *host); +void mmc_pwrseq_post_power_on(struct mmc_host *host); +void mmc_pwrseq_power_off(struct mmc_host *host); +void mmc_pwrseq_free(struct mmc_host *host); + +int mmc_pwrseq_simple_alloc(struct mmc_host *host, struct device *dev); +int mmc_pwrseq_emmc_alloc(struct mmc_host *host, struct device *dev); + +#else + +static inline int mmc_pwrseq_alloc(struct mmc_host *host) { return 0; } +static inline void mmc_pwrseq_pre_power_on(struct mmc_host *host) {} +static inline void mmc_pwrseq_post_power_on(struct mmc_host *host) {} +static inline void mmc_pwrseq_power_off(struct mmc_host *host) {} +static inline void mmc_pwrseq_free(struct mmc_host *host) {} + +#endif + +#endif diff --git a/drivers/mmc/core/pwrseq_emmc.c b/drivers/mmc/core/pwrseq_emmc.c new file mode 100644 index 000000000000..a2d545904fbf --- /dev/null +++ b/drivers/mmc/core/pwrseq_emmc.c @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2015, Samsung Electronics Co., Ltd. + * + * Author: Marek Szyprowski <m.szyprowski@samsung.com> + * + * License terms: GNU General Public License (GPL) version 2 + * + * Simple eMMC hardware reset provider + */ +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/gpio/consumer.h> +#include <linux/reboot.h> + +#include <linux/mmc/host.h> + +#include "pwrseq.h" + +struct mmc_pwrseq_emmc { + struct mmc_pwrseq pwrseq; + struct notifier_block reset_nb; + struct gpio_desc *reset_gpio; +}; + +static void __mmc_pwrseq_emmc_reset(struct mmc_pwrseq_emmc *pwrseq) +{ + gpiod_set_value(pwrseq->reset_gpio, 1); + udelay(1); + gpiod_set_value(pwrseq->reset_gpio, 0); + udelay(200); +} + +static void mmc_pwrseq_emmc_reset(struct mmc_host *host) +{ + struct mmc_pwrseq_emmc *pwrseq = container_of(host->pwrseq, + struct mmc_pwrseq_emmc, pwrseq); + + __mmc_pwrseq_emmc_reset(pwrseq); +} + +static void mmc_pwrseq_emmc_free(struct mmc_host *host) +{ + struct mmc_pwrseq_emmc *pwrseq = container_of(host->pwrseq, + struct mmc_pwrseq_emmc, pwrseq); + + unregister_restart_handler(&pwrseq->reset_nb); + gpiod_put(pwrseq->reset_gpio); + kfree(pwrseq); + host->pwrseq = NULL; +} + +static struct mmc_pwrseq_ops mmc_pwrseq_emmc_ops = { + .post_power_on = mmc_pwrseq_emmc_reset, + .free = mmc_pwrseq_emmc_free, +}; + +static int mmc_pwrseq_emmc_reset_nb(struct notifier_block *this, + unsigned long mode, void *cmd) +{ + struct mmc_pwrseq_emmc *pwrseq = container_of(this, + struct mmc_pwrseq_emmc, reset_nb); + + __mmc_pwrseq_emmc_reset(pwrseq); + return NOTIFY_DONE; +} + +int mmc_pwrseq_emmc_alloc(struct mmc_host *host, struct device *dev) +{ + struct mmc_pwrseq_emmc *pwrseq; + int ret = 0; + + pwrseq = kzalloc(sizeof(struct mmc_pwrseq_emmc), GFP_KERNEL); + if (!pwrseq) + return -ENOMEM; + + pwrseq->reset_gpio = gpiod_get_index(dev, "reset", 0, GPIOD_OUT_LOW); + if (IS_ERR(pwrseq->reset_gpio)) { + ret = PTR_ERR(pwrseq->reset_gpio); + goto free; + } + + /* + * register reset handler to ensure emmc reset also from + * emergency_reboot(), priority 129 schedules it just before + * system reboot + */ + pwrseq->reset_nb.notifier_call = mmc_pwrseq_emmc_reset_nb; + pwrseq->reset_nb.priority = 129; + register_restart_handler(&pwrseq->reset_nb); + + pwrseq->pwrseq.ops = &mmc_pwrseq_emmc_ops; + host->pwrseq = &pwrseq->pwrseq; + + return 0; +free: + kfree(pwrseq); + return ret; +} diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c new file mode 100644 index 000000000000..e9f1d8d84613 --- /dev/null +++ b/drivers/mmc/core/pwrseq_simple.c @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2014 Linaro Ltd + * + * Author: Ulf Hansson <ulf.hansson@linaro.org> + * + * License terms: GNU General Public License (GPL) version 2 + * + * Simple MMC power sequence management + */ +#include <linux/clk.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/of_gpio.h> +#include <linux/gpio/consumer.h> + +#include <linux/mmc/host.h> + +#include "pwrseq.h" + +struct mmc_pwrseq_simple { + struct mmc_pwrseq pwrseq; + bool clk_enabled; + struct clk *ext_clk; + int nr_gpios; + struct gpio_desc *reset_gpios[0]; +}; + +static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq, + int value) +{ + int i; + + for (i = 0; i < pwrseq->nr_gpios; i++) + if (!IS_ERR(pwrseq->reset_gpios[i])) + gpiod_set_value_cansleep(pwrseq->reset_gpios[i], value); +} + +static void mmc_pwrseq_simple_pre_power_on(struct mmc_host *host) +{ + struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq, + struct mmc_pwrseq_simple, pwrseq); + + if (!IS_ERR(pwrseq->ext_clk) && !pwrseq->clk_enabled) { + clk_prepare_enable(pwrseq->ext_clk); + pwrseq->clk_enabled = true; + } + + mmc_pwrseq_simple_set_gpios_value(pwrseq, 1); +} + +static void mmc_pwrseq_simple_post_power_on(struct mmc_host *host) +{ + struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq, + struct mmc_pwrseq_simple, pwrseq); + + mmc_pwrseq_simple_set_gpios_value(pwrseq, 0); +} + +static void mmc_pwrseq_simple_power_off(struct mmc_host *host) +{ + struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq, + struct mmc_pwrseq_simple, pwrseq); + + mmc_pwrseq_simple_set_gpios_value(pwrseq, 1); + + if (!IS_ERR(pwrseq->ext_clk) && pwrseq->clk_enabled) { + clk_disable_unprepare(pwrseq->ext_clk); + pwrseq->clk_enabled = false; + } +} + +static void mmc_pwrseq_simple_free(struct mmc_host *host) +{ + struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq, + struct mmc_pwrseq_simple, pwrseq); + int i; + + for (i = 0; i < pwrseq->nr_gpios; i++) + if (!IS_ERR(pwrseq->reset_gpios[i])) + gpiod_put(pwrseq->reset_gpios[i]); + + if (!IS_ERR(pwrseq->ext_clk)) + clk_put(pwrseq->ext_clk); + + kfree(pwrseq); + host->pwrseq = NULL; +} + +static struct mmc_pwrseq_ops mmc_pwrseq_simple_ops = { + .pre_power_on = mmc_pwrseq_simple_pre_power_on, + .post_power_on = mmc_pwrseq_simple_post_power_on, + .power_off = mmc_pwrseq_simple_power_off, + .free = mmc_pwrseq_simple_free, +}; + +int mmc_pwrseq_simple_alloc(struct mmc_host *host, struct device *dev) +{ + struct mmc_pwrseq_simple *pwrseq; + int i, nr_gpios, ret = 0; + + nr_gpios = of_gpio_named_count(dev->of_node, "reset-gpios"); + if (nr_gpios < 0) + nr_gpios = 0; + + pwrseq = kzalloc(sizeof(struct mmc_pwrseq_simple) + nr_gpios * + sizeof(struct gpio_desc *), GFP_KERNEL); + if (!pwrseq) + return -ENOMEM; + + pwrseq->ext_clk = clk_get(dev, "ext_clock"); + if (IS_ERR(pwrseq->ext_clk) && + PTR_ERR(pwrseq->ext_clk) != -ENOENT) { + ret = PTR_ERR(pwrseq->ext_clk); + goto free; + } + + for (i = 0; i < nr_gpios; i++) { + pwrseq->reset_gpios[i] = gpiod_get_index(dev, "reset", i, + GPIOD_OUT_HIGH); + if (IS_ERR(pwrseq->reset_gpios[i]) && + PTR_ERR(pwrseq->reset_gpios[i]) != -ENOENT && + PTR_ERR(pwrseq->reset_gpios[i]) != -ENOSYS) { + ret = PTR_ERR(pwrseq->reset_gpios[i]); + + while (--i) + gpiod_put(pwrseq->reset_gpios[i]); + + goto clk_put; + } + } + + pwrseq->nr_gpios = nr_gpios; + pwrseq->pwrseq.ops = &mmc_pwrseq_simple_ops; + host->pwrseq = &pwrseq->pwrseq; + + return 0; +clk_put: + if (!IS_ERR(pwrseq->ext_clk)) + clk_put(pwrseq->ext_clk); +free: + kfree(pwrseq); + return ret; +} diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index d90a6de7901d..ad4d43eae99d 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -660,15 +660,10 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card) * SPI mode doesn't define CMD19 and tuning is only valid for SDR50 and * SDR104 mode SD-cards. Note that tuning is mandatory for SDR104. */ - if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning && - (card->sd_bus_speed == UHS_SDR50_BUS_SPEED || - card->sd_bus_speed == UHS_SDR104_BUS_SPEED)) { - mmc_host_clk_hold(card->host); - err = card->host->ops->execute_tuning(card->host, - MMC_SEND_TUNING_BLOCK); - mmc_host_clk_release(card->host); - } - + if (!mmc_host_is_spi(card->host) && + (card->sd_bus_speed == UHS_SDR50_BUS_SPEED || + card->sd_bus_speed == UHS_SDR104_BUS_SPEED)) + err = mmc_execute_tuning(card); out: kfree(status); @@ -933,6 +928,12 @@ static int mmc_sd_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: get card RCA and quit open drain mode. */ if (!mmc_host_is_spi(host)) { @@ -1191,6 +1192,12 @@ static int mmc_sd_power_restore(struct mmc_host *host) return ret; } +static int mmc_sd_reset(struct mmc_host *host) +{ + mmc_power_cycle(host, host->card->ocr); + return mmc_sd_power_restore(host); +} + static const struct mmc_bus_ops mmc_sd_ops = { .remove = mmc_sd_remove, .detect = mmc_sd_detect, @@ -1201,6 +1208,7 @@ static const struct mmc_bus_ops mmc_sd_ops = { .power_restore = mmc_sd_power_restore, .alive = mmc_sd_alive, .shutdown = mmc_sd_suspend, + .reset = mmc_sd_reset, }; /* @@ -1271,4 +1279,3 @@ err: return err; } - diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index fd0750b5a634..ce6cc47206b0 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -567,17 +567,11 @@ static int mmc_sdio_init_uhs_card(struct mmc_card *card) * SPI mode doesn't define CMD19 and tuning is only valid for SDR50 and * SDR104 mode SD-cards. Note that tuning is mandatory for SDR104. */ - if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning && - ((card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR50) || - (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR104))) { - mmc_host_clk_hold(card->host); - err = card->host->ops->execute_tuning(card->host, - MMC_SEND_TUNING_BLOCK); - mmc_host_clk_release(card->host); - } - + if (!mmc_host_is_spi(card->host) && + ((card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR50) || + (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR104))) + err = mmc_execute_tuning(card); out: - return err; } diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c index 60885316afba..bee02e644d62 100644 --- a/drivers/mmc/core/sdio_bus.c +++ b/drivers/mmc/core/sdio_bus.c @@ -22,7 +22,9 @@ #include <linux/mmc/card.h> #include <linux/mmc/host.h> #include <linux/mmc/sdio_func.h> +#include <linux/of.h> +#include "core.h" #include "sdio_cis.h" #include "sdio_bus.h" @@ -295,6 +297,13 @@ static void sdio_acpi_set_handle(struct sdio_func *func) static inline void sdio_acpi_set_handle(struct sdio_func *func) {} #endif +static void sdio_set_of_node(struct sdio_func *func) +{ + struct mmc_host *host = func->card->host; + + func->dev.of_node = mmc_of_find_child_device(host, func->num); +} + /* * Register a new SDIO function with the driver model. */ @@ -304,6 +313,7 @@ int sdio_add_func(struct sdio_func *func) dev_set_name(&func->dev, "%s:%d", mmc_card_id(func->card), func->num); + sdio_set_of_node(func); sdio_acpi_set_handle(func); ret = device_add(&func->dev); if (ret == 0) { @@ -327,6 +337,7 @@ void sdio_remove_func(struct sdio_func *func) dev_pm_domain_detach(&func->dev, false); device_del(&func->dev); + of_node_put(func->dev.of_node); put_device(&func->dev); } diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c index 69bbf2adb329..27117ba47073 100644 --- a/drivers/mmc/core/slot-gpio.c +++ b/drivers/mmc/core/slot-gpio.c @@ -18,11 +18,14 @@ #include <linux/module.h> #include <linux/slab.h> +#include "slot-gpio.h" + struct mmc_gpio { struct gpio_desc *ro_gpio; struct gpio_desc *cd_gpio; bool override_ro_active_level; bool override_cd_active_level; + irqreturn_t (*cd_gpio_isr)(int irq, void *dev_id); char *ro_label; char cd_label[0]; }; @@ -38,32 +41,20 @@ static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id) return IRQ_HANDLED; } -static int mmc_gpio_alloc(struct mmc_host *host) +int mmc_gpio_alloc(struct mmc_host *host) { size_t len = strlen(dev_name(host->parent)) + 4; - struct mmc_gpio *ctx; - - mutex_lock(&host->slot.lock); - - ctx = host->slot.handler_priv; - if (!ctx) { - /* - * devm_kzalloc() can be called after device_initialize(), even - * before device_add(), i.e., between mmc_alloc_host() and - * mmc_add_host() - */ - ctx = devm_kzalloc(&host->class_dev, sizeof(*ctx) + 2 * len, - GFP_KERNEL); - if (ctx) { - ctx->ro_label = ctx->cd_label + len; - snprintf(ctx->cd_label, len, "%s cd", dev_name(host->parent)); - snprintf(ctx->ro_label, len, "%s ro", dev_name(host->parent)); - host->slot.handler_priv = ctx; - } + struct mmc_gpio *ctx = devm_kzalloc(host->parent, + sizeof(*ctx) + 2 * len, GFP_KERNEL); + + if (ctx) { + ctx->ro_label = ctx->cd_label + len; + snprintf(ctx->cd_label, len, "%s cd", dev_name(host->parent)); + snprintf(ctx->ro_label, len, "%s ro", dev_name(host->parent)); + host->slot.handler_priv = ctx; + host->slot.cd_irq = -EINVAL; } - mutex_unlock(&host->slot.lock); - return ctx ? 0 : -ENOMEM; } @@ -103,29 +94,19 @@ EXPORT_SYMBOL(mmc_gpio_get_cd); * @gpio: gpio number requested * * As devm_* managed functions are used in mmc_gpio_request_ro(), client - * drivers do not need to explicitly call mmc_gpio_free_ro() for freeing up, - * if the requesting and freeing are only needed at probing and unbinding time - * for once. However, if client drivers do something special like runtime - * switching for write-protection, they are responsible for calling - * mmc_gpio_request_ro() and mmc_gpio_free_ro() as a pair on their own. + * drivers do not need to worry about freeing up memory. * * Returns zero on success, else an error. */ int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio) { - struct mmc_gpio *ctx; + struct mmc_gpio *ctx = host->slot.handler_priv; int ret; if (!gpio_is_valid(gpio)) return -EINVAL; - ret = mmc_gpio_alloc(host); - if (ret < 0) - return ret; - - ctx = host->slot.handler_priv; - - ret = devm_gpio_request_one(&host->class_dev, gpio, GPIOF_DIR_IN, + ret = devm_gpio_request_one(host->parent, gpio, GPIOF_DIR_IN, ctx->ro_label); if (ret < 0) return ret; @@ -156,8 +137,10 @@ void mmc_gpiod_request_cd_irq(struct mmc_host *host) irq = -EINVAL; if (irq >= 0) { - ret = devm_request_threaded_irq(&host->class_dev, irq, - NULL, mmc_gpio_cd_irqt, + if (!ctx->cd_gpio_isr) + ctx->cd_gpio_isr = mmc_gpio_cd_irqt; + ret = devm_request_threaded_irq(host->parent, irq, + NULL, ctx->cd_gpio_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, ctx->cd_label, host); if (ret < 0) @@ -171,6 +154,19 @@ void mmc_gpiod_request_cd_irq(struct mmc_host *host) } EXPORT_SYMBOL(mmc_gpiod_request_cd_irq); +/* Register an alternate interrupt service routine for + * the card-detect GPIO. + */ +void mmc_gpio_set_cd_isr(struct mmc_host *host, + irqreturn_t (*isr)(int irq, void *dev_id)) +{ + struct mmc_gpio *ctx = host->slot.handler_priv; + + WARN_ON(ctx->cd_gpio_isr); + ctx->cd_gpio_isr = isr; +} +EXPORT_SYMBOL(mmc_gpio_set_cd_isr); + /** * mmc_gpio_request_cd - request a gpio for card-detection * @host: mmc host @@ -178,11 +174,7 @@ EXPORT_SYMBOL(mmc_gpiod_request_cd_irq); * @debounce: debounce time in microseconds * * As devm_* managed functions are used in mmc_gpio_request_cd(), client - * drivers do not need to explicitly call mmc_gpio_free_cd() for freeing up, - * if the requesting and freeing are only needed at probing and unbinding time - * for once. However, if client drivers do something special like runtime - * switching for card-detection, they are responsible for calling - * mmc_gpio_request_cd() and mmc_gpio_free_cd() as a pair on their own. + * drivers do not need to worry about freeing up memory. * * If GPIO debouncing is desired, set the debounce parameter to a non-zero * value. The caller is responsible for ensuring that the GPIO driver associated @@ -193,16 +185,10 @@ EXPORT_SYMBOL(mmc_gpiod_request_cd_irq); int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio, unsigned int debounce) { - struct mmc_gpio *ctx; + struct mmc_gpio *ctx = host->slot.handler_priv; int ret; - ret = mmc_gpio_alloc(host); - if (ret < 0) - return ret; - - ctx = host->slot.handler_priv; - - ret = devm_gpio_request_one(&host->class_dev, gpio, GPIOF_DIR_IN, + ret = devm_gpio_request_one(host->parent, gpio, GPIOF_DIR_IN, ctx->cd_label); if (ret < 0) /* @@ -226,55 +212,6 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio, EXPORT_SYMBOL(mmc_gpio_request_cd); /** - * mmc_gpio_free_ro - free the write-protection gpio - * @host: mmc host - * - * It's provided only for cases that client drivers need to manually free - * up the write-protection gpio requested by mmc_gpio_request_ro(). - */ -void mmc_gpio_free_ro(struct mmc_host *host) -{ - struct mmc_gpio *ctx = host->slot.handler_priv; - int gpio; - - if (!ctx || !ctx->ro_gpio) - return; - - gpio = desc_to_gpio(ctx->ro_gpio); - ctx->ro_gpio = NULL; - - devm_gpio_free(&host->class_dev, gpio); -} -EXPORT_SYMBOL(mmc_gpio_free_ro); - -/** - * mmc_gpio_free_cd - free the card-detection gpio - * @host: mmc host - * - * It's provided only for cases that client drivers need to manually free - * up the card-detection gpio requested by mmc_gpio_request_cd(). - */ -void mmc_gpio_free_cd(struct mmc_host *host) -{ - struct mmc_gpio *ctx = host->slot.handler_priv; - int gpio; - - if (!ctx || !ctx->cd_gpio) - return; - - if (host->slot.cd_irq >= 0) { - devm_free_irq(&host->class_dev, host->slot.cd_irq, host); - host->slot.cd_irq = -EINVAL; - } - - gpio = desc_to_gpio(ctx->cd_gpio); - ctx->cd_gpio = NULL; - - devm_gpio_free(&host->class_dev, gpio); -} -EXPORT_SYMBOL(mmc_gpio_free_cd); - -/** * mmc_gpiod_request_cd - request a gpio descriptor for card-detection * @host: mmc host * @con_id: function within the GPIO consumer @@ -285,8 +222,7 @@ EXPORT_SYMBOL(mmc_gpio_free_cd); * to NULL to ignore * * Use this function in place of mmc_gpio_request_cd() to use the GPIO - * descriptor API. Note that it is paired with mmc_gpiod_free_cd() not - * mmc_gpio_free_cd(). Note also that it must be called prior to mmc_add_host() + * descriptor API. Note that it must be called prior to mmc_add_host() * otherwise the caller must also call mmc_gpiod_request_cd_irq(). * * Returns zero on success, else an error. @@ -295,16 +231,10 @@ int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id, unsigned int idx, bool override_active_level, unsigned int debounce, bool *gpio_invert) { - struct mmc_gpio *ctx; + struct mmc_gpio *ctx = host->slot.handler_priv; struct gpio_desc *desc; int ret; - ret = mmc_gpio_alloc(host); - if (ret < 0) - return ret; - - ctx = host->slot.handler_priv; - if (!con_id) con_id = ctx->cd_label; @@ -339,8 +269,7 @@ EXPORT_SYMBOL(mmc_gpiod_request_cd); * set to NULL to ignore * * Use this function in place of mmc_gpio_request_ro() to use the GPIO - * descriptor API. Note that it is paired with mmc_gpiod_free_ro() not - * mmc_gpio_free_ro(). + * descriptor API. * * Returns zero on success, else an error. */ @@ -348,16 +277,10 @@ int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id, unsigned int idx, bool override_active_level, unsigned int debounce, bool *gpio_invert) { - struct mmc_gpio *ctx; + struct mmc_gpio *ctx = host->slot.handler_priv; struct gpio_desc *desc; int ret; - ret = mmc_gpio_alloc(host); - if (ret < 0) - return ret; - - ctx = host->slot.handler_priv; - if (!con_id) con_id = ctx->ro_label; @@ -380,28 +303,3 @@ int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id, return 0; } EXPORT_SYMBOL(mmc_gpiod_request_ro); - -/** - * mmc_gpiod_free_cd - free the card-detection gpio descriptor - * @host: mmc host - * - * It's provided only for cases that client drivers need to manually free - * up the card-detection gpio requested by mmc_gpiod_request_cd(). - */ -void mmc_gpiod_free_cd(struct mmc_host *host) -{ - struct mmc_gpio *ctx = host->slot.handler_priv; - - if (!ctx || !ctx->cd_gpio) - return; - - if (host->slot.cd_irq >= 0) { - devm_free_irq(&host->class_dev, host->slot.cd_irq, host); - host->slot.cd_irq = -EINVAL; - } - - devm_gpiod_put(host->parent, ctx->cd_gpio); - - ctx->cd_gpio = NULL; -} -EXPORT_SYMBOL(mmc_gpiod_free_cd); diff --git a/drivers/mmc/core/slot-gpio.h b/drivers/mmc/core/slot-gpio.h new file mode 100644 index 000000000000..8c1854dc5d58 --- /dev/null +++ b/drivers/mmc/core/slot-gpio.h @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2014 Linaro Ltd + * + * Author: Ulf Hansson <ulf.hansson@linaro.org> + * + * License terms: GNU General Public License (GPL) version 2 + */ +#ifndef _MMC_CORE_SLOTGPIO_H +#define _MMC_CORE_SLOTGPIO_H + +int mmc_gpio_alloc(struct mmc_host *host); + +#endif diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 2d6fbdd11803..61ac63a3776a 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -57,6 +57,7 @@ config MMC_SDHCI_IO_ACCESSORS config MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER bool + depends on MMC_SDHCI select MMC_SDHCI_IO_ACCESSORS help This option is selected by drivers running on big endian hosts @@ -82,6 +83,7 @@ config MMC_SDHCI_PCI config MMC_RICOH_MMC bool "Ricoh MMC Controller Disabler" depends on MMC_SDHCI_PCI + default y help This adds a pci quirk to disable Ricoh MMC Controller. This proprietary controller is unnecessary because the SDHCI driver @@ -228,6 +230,7 @@ config MMC_SDHCI_PXAV3 tristate "Marvell MMP2 SD Host Controller support (PXAV3)" depends on CLKDEV_LOOKUP depends on MMC_SDHCI_PLTFM + depends on ARCH_MMP || COMPILE_TEST default CPU_MMP2 help This selects the Marvell(R) PXAV3 SD Host Controller. @@ -240,6 +243,7 @@ config MMC_SDHCI_PXAV2 tristate "Marvell PXA9XX SD Host Controller support (PXAV2)" depends on CLKDEV_LOOKUP depends on MMC_SDHCI_PLTFM + depends on ARCH_MMP || COMPILE_TEST default CPU_PXA910 help This selects the Marvell(R) PXAV2 SD Host Controller. @@ -292,6 +296,17 @@ config MMC_SDHCI_BCM2835 If unsure, say N. +config MMC_SDHCI_F_SDH30 + tristate "SDHCI support for Fujitsu Semiconductor F_SDH30" + depends on MMC_SDHCI_PLTFM + depends on OF + help + This selects the Secure Digital Host Controller Interface (SDHCI) + Needed by some Fujitsu SoC for MMC / SD / SDIO support. + If you have a controller with this interface, say Y or M here. + + If unsure, say N. + config MMC_MOXART tristate "MOXART SD/MMC Host Controller support" depends on ARCH_MOXART && MMC diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index f7b0a77cf419..6a7cfe0de332 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_MMC_SDHCI_PXAV3) += sdhci-pxav3.o obj-$(CONFIG_MMC_SDHCI_PXAV2) += sdhci-pxav2.o obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o obj-$(CONFIG_MMC_SDHCI_SIRF) += sdhci-sirf.o +obj-$(CONFIG_MMC_SDHCI_F_SDH30) += sdhci_f_sdh30.o obj-$(CONFIG_MMC_SDHCI_SPEAR) += sdhci-spear.o obj-$(CONFIG_MMC_WBSD) += wbsd.o obj-$(CONFIG_MMC_AU1X) += au1xmmc.o diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index 509365cb22c6..fe32948c6114 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -21,43 +21,7 @@ #include "dw_mmc.h" #include "dw_mmc-pltfm.h" - -#define NUM_PINS(x) (x + 2) - -#define SDMMC_CLKSEL 0x09C -#define SDMMC_CLKSEL64 0x0A8 -#define SDMMC_CLKSEL_CCLK_SAMPLE(x) (((x) & 7) << 0) -#define SDMMC_CLKSEL_CCLK_DRIVE(x) (((x) & 7) << 16) -#define SDMMC_CLKSEL_CCLK_DIVIDER(x) (((x) & 7) << 24) -#define SDMMC_CLKSEL_GET_DRV_WD3(x) (((x) >> 16) & 0x7) -#define SDMMC_CLKSEL_TIMING(x, y, z) (SDMMC_CLKSEL_CCLK_SAMPLE(x) | \ - SDMMC_CLKSEL_CCLK_DRIVE(y) | \ - SDMMC_CLKSEL_CCLK_DIVIDER(z)) -#define SDMMC_CLKSEL_WAKEUP_INT BIT(11) - -#define EXYNOS4210_FIXED_CIU_CLK_DIV 2 -#define EXYNOS4412_FIXED_CIU_CLK_DIV 4 - -/* Block number in eMMC */ -#define DWMCI_BLOCK_NUM 0xFFFFFFFF - -#define SDMMC_EMMCP_BASE 0x1000 -#define SDMMC_MPSECURITY (SDMMC_EMMCP_BASE + 0x0010) -#define SDMMC_MPSBEGIN0 (SDMMC_EMMCP_BASE + 0x0200) -#define SDMMC_MPSEND0 (SDMMC_EMMCP_BASE + 0x0204) -#define SDMMC_MPSCTRL0 (SDMMC_EMMCP_BASE + 0x020C) - -/* SMU control bits */ -#define DWMCI_MPSCTRL_SECURE_READ_BIT BIT(7) -#define DWMCI_MPSCTRL_SECURE_WRITE_BIT BIT(6) -#define DWMCI_MPSCTRL_NON_SECURE_READ_BIT BIT(5) -#define DWMCI_MPSCTRL_NON_SECURE_WRITE_BIT BIT(4) -#define DWMCI_MPSCTRL_USE_FUSE_KEY BIT(3) -#define DWMCI_MPSCTRL_ECB_MODE BIT(2) -#define DWMCI_MPSCTRL_ENCRYPTION BIT(1) -#define DWMCI_MPSCTRL_VALID BIT(0) - -#define EXYNOS_CCLKIN_MIN 50000000 /* unit: HZ */ +#include "dw_mmc-exynos.h" /* Variations in Exynos specific dw-mshc controller */ enum dw_mci_exynos_type { @@ -114,11 +78,11 @@ static int dw_mci_exynos_priv_init(struct dw_mci *host) if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420_SMU || priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) { mci_writel(host, MPSBEGIN0, 0); - mci_writel(host, MPSEND0, DWMCI_BLOCK_NUM); - mci_writel(host, MPSCTRL0, DWMCI_MPSCTRL_SECURE_WRITE_BIT | - DWMCI_MPSCTRL_NON_SECURE_READ_BIT | - DWMCI_MPSCTRL_VALID | - DWMCI_MPSCTRL_NON_SECURE_WRITE_BIT); + mci_writel(host, MPSEND0, SDMMC_ENDING_SEC_NR_MAX); + mci_writel(host, MPSCTRL0, SDMMC_MPSCTRL_SECURE_WRITE_BIT | + SDMMC_MPSCTRL_NON_SECURE_READ_BIT | + SDMMC_MPSCTRL_VALID | + SDMMC_MPSCTRL_NON_SECURE_WRITE_BIT); } return 0; @@ -127,9 +91,9 @@ static int dw_mci_exynos_priv_init(struct dw_mci *host) static int dw_mci_exynos_setup_clock(struct dw_mci *host) { struct dw_mci_exynos_priv_data *priv = host->priv; - unsigned long rate = clk_get_rate(host->ciu_clk); - host->bus_hz = rate / (priv->ciu_div + 1); + host->bus_hz /= (priv->ciu_div + 1); + return 0; } @@ -232,8 +196,11 @@ static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios) mci_writel(host, CLKSEL, priv->sdr_timing); } - /* Don't care if wanted clock is zero */ - if (!wanted) + /* + * Don't care if wanted clock is zero or + * ciu clock is unavailable + */ + if (!wanted || IS_ERR(host->ciu_clk)) return; /* Guaranteed minimum frequency for cclkin */ @@ -263,10 +230,8 @@ static int dw_mci_exynos_parse_dt(struct dw_mci *host) int ret; priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) { - dev_err(host->dev, "mem alloc failed for private data\n"); + if (!priv) return -ENOMEM; - } for (idx = 0; idx < ARRAY_SIZE(exynos_compat); idx++) { if (of_device_is_compatible(np, exynos_compat[idx].compatible)) @@ -375,64 +340,23 @@ out: return loc; } -static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode, - struct dw_mci_tuning_data *tuning_data) +static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot) { struct dw_mci *host = slot->host; struct mmc_host *mmc = slot->mmc; - const u8 *blk_pattern = tuning_data->blk_pattern; - u8 *blk_test; - unsigned int blksz = tuning_data->blksz; u8 start_smpl, smpl, candiates = 0; s8 found = -1; int ret = 0; - blk_test = kmalloc(blksz, GFP_KERNEL); - if (!blk_test) - return -ENOMEM; - start_smpl = dw_mci_exynos_get_clksmpl(host); do { - struct mmc_request mrq = {NULL}; - struct mmc_command cmd = {0}; - struct mmc_command stop = {0}; - struct mmc_data data = {0}; - struct scatterlist sg; - - cmd.opcode = opcode; - cmd.arg = 0; - cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; - - stop.opcode = MMC_STOP_TRANSMISSION; - stop.arg = 0; - stop.flags = MMC_RSP_R1B | MMC_CMD_AC; - - data.blksz = blksz; - data.blocks = 1; - data.flags = MMC_DATA_READ; - data.sg = &sg; - data.sg_len = 1; - - sg_init_one(&sg, blk_test, blksz); - mrq.cmd = &cmd; - mrq.stop = &stop; - mrq.data = &data; - host->mrq = &mrq; - mci_writel(host, TMOUT, ~0); smpl = dw_mci_exynos_move_next_clksmpl(host); - mmc_wait_for_req(mmc, &mrq); + if (!mmc_send_tuning(mmc)) + candiates |= (1 << smpl); - if (!cmd.error && !data.error) { - if (!memcmp(blk_pattern, blk_test, blksz)) - candiates |= (1 << smpl); - } else { - dev_dbg(host->dev, - "Tuning error: cmd.error:%d, data.error:%d\n", - cmd.error, data.error); - } } while (start_smpl != smpl); found = dw_mci_exynos_get_best_clksmpl(candiates); @@ -441,7 +365,6 @@ static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode, else ret = -EIO; - kfree(blk_test); return ret; } @@ -499,7 +422,7 @@ static const struct dev_pm_ops dw_mci_exynos_pmops = { static struct platform_driver dw_mci_exynos_pltfm_driver = { .probe = dw_mci_exynos_probe, - .remove = __exit_p(dw_mci_pltfm_remove), + .remove = dw_mci_pltfm_remove, .driver = { .name = "dwmmc_exynos", .of_match_table = dw_mci_exynos_match, diff --git a/drivers/mmc/host/dw_mmc-exynos.h b/drivers/mmc/host/dw_mmc-exynos.h new file mode 100644 index 000000000000..7872ce586b55 --- /dev/null +++ b/drivers/mmc/host/dw_mmc-exynos.h @@ -0,0 +1,56 @@ +/* + * Exynos Specific Extensions for Synopsys DW Multimedia Card Interface driver + * + * Copyright (C) 2012-2014 Samsung Electronics Co., Ltd. + * + * 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 _DW_MMC_EXYNOS_H_ +#define _DW_MMC_EXYNOS_H_ + +/* Extended Register's Offset */ +#define SDMMC_CLKSEL 0x09C +#define SDMMC_CLKSEL64 0x0A8 + +/* CLKSEL register defines */ +#define SDMMC_CLKSEL_CCLK_SAMPLE(x) (((x) & 7) << 0) +#define SDMMC_CLKSEL_CCLK_DRIVE(x) (((x) & 7) << 16) +#define SDMMC_CLKSEL_CCLK_DIVIDER(x) (((x) & 7) << 24) +#define SDMMC_CLKSEL_GET_DRV_WD3(x) (((x) >> 16) & 0x7) +#define SDMMC_CLKSEL_TIMING(x, y, z) (SDMMC_CLKSEL_CCLK_SAMPLE(x) | \ + SDMMC_CLKSEL_CCLK_DRIVE(y) | \ + SDMMC_CLKSEL_CCLK_DIVIDER(z)) +#define SDMMC_CLKSEL_WAKEUP_INT BIT(11) + +/* Protector Register */ +#define SDMMC_EMMCP_BASE 0x1000 +#define SDMMC_MPSECURITY (SDMMC_EMMCP_BASE + 0x0010) +#define SDMMC_MPSBEGIN0 (SDMMC_EMMCP_BASE + 0x0200) +#define SDMMC_MPSEND0 (SDMMC_EMMCP_BASE + 0x0204) +#define SDMMC_MPSCTRL0 (SDMMC_EMMCP_BASE + 0x020C) + +/* SMU control defines */ +#define SDMMC_MPSCTRL_SECURE_READ_BIT BIT(7) +#define SDMMC_MPSCTRL_SECURE_WRITE_BIT BIT(6) +#define SDMMC_MPSCTRL_NON_SECURE_READ_BIT BIT(5) +#define SDMMC_MPSCTRL_NON_SECURE_WRITE_BIT BIT(4) +#define SDMMC_MPSCTRL_USE_FUSE_KEY BIT(3) +#define SDMMC_MPSCTRL_ECB_MODE BIT(2) +#define SDMMC_MPSCTRL_ENCRYPTION BIT(1) +#define SDMMC_MPSCTRL_VALID BIT(0) + +/* Maximum number of Ending sector */ +#define SDMMC_ENDING_SEC_NR_MAX 0xFFFFFFFF + +/* Fixed clock divider */ +#define EXYNOS4210_FIXED_CIU_CLK_DIV 2 +#define EXYNOS4412_FIXED_CIU_CLK_DIV 4 + +/* Minimal required clock frequency for cclkin, unit: HZ */ +#define EXYNOS_CCLKIN_MIN 50000000 + +#endif /* _DW_MMC_EXYNOS_H_ */ diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index 5650ac488cf3..e2a726a503ee 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -133,7 +133,7 @@ static SIMPLE_DEV_PM_OPS(dw_mci_rockchip_pmops, static struct platform_driver dw_mci_rockchip_pltfm_driver = { .probe = dw_mci_rockchip_probe, - .remove = __exit_p(dw_mci_pltfm_remove), + .remove = dw_mci_pltfm_remove, .driver = { .name = "dwmmc_rockchip", .of_match_table = dw_mci_rockchip_match, diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 67c04518ec4c..4d2e3c2e1830 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -27,6 +27,7 @@ #include <linux/stat.h> #include <linux/delay.h> #include <linux/irq.h> +#include <linux/mmc/card.h> #include <linux/mmc/host.h> #include <linux/mmc/mmc.h> #include <linux/mmc/sd.h> @@ -313,7 +314,9 @@ static u32 dw_mci_prep_stop_abort(struct dw_mci *host, struct mmc_command *cmd) if (cmdr == MMC_READ_SINGLE_BLOCK || cmdr == MMC_READ_MULTIPLE_BLOCK || cmdr == MMC_WRITE_BLOCK || - cmdr == MMC_WRITE_MULTIPLE_BLOCK) { + cmdr == MMC_WRITE_MULTIPLE_BLOCK || + cmdr == MMC_SEND_TUNING_BLOCK || + cmdr == MMC_SEND_TUNING_BLOCK_HS200) { stop->opcode = MMC_STOP_TRANSMISSION; stop->arg = 0; stop->flags = MMC_RSP_R1B | MMC_CMD_AC; @@ -758,6 +761,7 @@ disable: static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) { + unsigned long irqflags; int sg_len; u32 temp; @@ -794,9 +798,11 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) mci_writel(host, CTRL, temp); /* Disable RX/TX IRQs, let DMA handle it */ + spin_lock_irqsave(&host->irq_lock, irqflags); temp = mci_readl(host, INTMASK); temp &= ~(SDMMC_INT_RXDR | SDMMC_INT_TXDR); mci_writel(host, INTMASK, temp); + spin_unlock_irqrestore(&host->irq_lock, irqflags); host->dma_ops->start(host, sg_len); @@ -805,6 +811,7 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data) { + unsigned long irqflags; u32 temp; data->error = -EINPROGRESS; @@ -833,9 +840,12 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data) host->part_buf_count = 0; mci_writel(host, RINTSTS, SDMMC_INT_TXDR | SDMMC_INT_RXDR); + + spin_lock_irqsave(&host->irq_lock, irqflags); temp = mci_readl(host, INTMASK); temp |= SDMMC_INT_TXDR | SDMMC_INT_RXDR; mci_writel(host, INTMASK, temp); + spin_unlock_irqrestore(&host->irq_lock, irqflags); temp = mci_readl(host, CTRL); temp &= ~SDMMC_CTRL_DMA_ENABLE; @@ -926,7 +936,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) /* enable clock; only low power if no SDIO */ clk_en_a = SDMMC_CLKEN_ENABLE << slot->id; - if (!(mci_readl(host, INTMASK) & SDMMC_INT_SDIO(slot->sdio_id))) + if (!test_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags)) clk_en_a |= SDMMC_CLKEN_LOW_PWR << slot->id; mci_writel(host, CLKENA, clk_en_a); @@ -1109,6 +1119,12 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) return; } } + set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags); + regs = mci_readl(slot->host, PWREN); + regs |= (1 << slot->id); + mci_writel(slot->host, PWREN, regs); + break; + case MMC_POWER_ON: if (!IS_ERR(mmc->supply.vqmmc) && !slot->host->vqmmc_enabled) { ret = regulator_enable(mmc->supply.vqmmc); if (ret < 0) @@ -1117,10 +1133,6 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) else slot->host->vqmmc_enabled = true; } - set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags); - regs = mci_readl(slot->host, PWREN); - regs |= (1 << slot->id); - mci_writel(slot->host, PWREN, regs); break; case MMC_POWER_OFF: if (!IS_ERR(mmc->supply.vmmc)) @@ -1245,27 +1257,37 @@ static int dw_mci_get_cd(struct mmc_host *mmc) return present; } -/* - * Disable lower power mode. - * - * Low power mode will stop the card clock when idle. According to the - * description of the CLKENA register we should disable low power mode - * for SDIO cards if we need SDIO interrupts to work. - * - * This function is fast if low power mode is already disabled. - */ -static void dw_mci_disable_low_power(struct dw_mci_slot *slot) +static void dw_mci_init_card(struct mmc_host *mmc, struct mmc_card *card) { + struct dw_mci_slot *slot = mmc_priv(mmc); struct dw_mci *host = slot->host; - u32 clk_en_a; - const u32 clken_low_pwr = SDMMC_CLKEN_LOW_PWR << slot->id; - clk_en_a = mci_readl(host, CLKENA); + /* + * Low power mode will stop the card clock when idle. According to the + * description of the CLKENA register we should disable low power mode + * for SDIO cards if we need SDIO interrupts to work. + */ + if (mmc->caps & MMC_CAP_SDIO_IRQ) { + const u32 clken_low_pwr = SDMMC_CLKEN_LOW_PWR << slot->id; + u32 clk_en_a_old; + u32 clk_en_a; - if (clk_en_a & clken_low_pwr) { - mci_writel(host, CLKENA, clk_en_a & ~clken_low_pwr); - mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | - SDMMC_CMD_PRV_DAT_WAIT, 0); + clk_en_a_old = mci_readl(host, CLKENA); + + if (card->type == MMC_TYPE_SDIO || + card->type == MMC_TYPE_SD_COMBO) { + set_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags); + clk_en_a = clk_en_a_old & ~clken_low_pwr; + } else { + clear_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags); + clk_en_a = clk_en_a_old | clken_low_pwr; + } + + if (clk_en_a != clk_en_a_old) { + mci_writel(host, CLKENA, clk_en_a); + mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | + SDMMC_CMD_PRV_DAT_WAIT, 0); + } } } @@ -1273,25 +1295,20 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) { struct dw_mci_slot *slot = mmc_priv(mmc); struct dw_mci *host = slot->host; + unsigned long irqflags; u32 int_mask; + spin_lock_irqsave(&host->irq_lock, irqflags); + /* Enable/disable Slot Specific SDIO interrupt */ int_mask = mci_readl(host, INTMASK); - if (enb) { - /* - * Turn off low power mode if it was enabled. This is a bit of - * a heavy operation and we disable / enable IRQs a lot, so - * we'll leave low power mode disabled and it will get - * re-enabled again in dw_mci_setup_bus(). - */ - dw_mci_disable_low_power(slot); + if (enb) + int_mask |= SDMMC_INT_SDIO(slot->sdio_id); + else + int_mask &= ~SDMMC_INT_SDIO(slot->sdio_id); + mci_writel(host, INTMASK, int_mask); - mci_writel(host, INTMASK, - (int_mask | SDMMC_INT_SDIO(slot->sdio_id))); - } else { - mci_writel(host, INTMASK, - (int_mask & ~SDMMC_INT_SDIO(slot->sdio_id))); - } + spin_unlock_irqrestore(&host->irq_lock, irqflags); } static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode) @@ -1299,30 +1316,10 @@ static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode) struct dw_mci_slot *slot = mmc_priv(mmc); struct dw_mci *host = slot->host; const struct dw_mci_drv_data *drv_data = host->drv_data; - struct dw_mci_tuning_data tuning_data; int err = -ENOSYS; - if (opcode == MMC_SEND_TUNING_BLOCK_HS200) { - if (mmc->ios.bus_width == MMC_BUS_WIDTH_8) { - tuning_data.blk_pattern = tuning_blk_pattern_8bit; - tuning_data.blksz = sizeof(tuning_blk_pattern_8bit); - } else if (mmc->ios.bus_width == MMC_BUS_WIDTH_4) { - tuning_data.blk_pattern = tuning_blk_pattern_4bit; - tuning_data.blksz = sizeof(tuning_blk_pattern_4bit); - } else { - return -EINVAL; - } - } else if (opcode == MMC_SEND_TUNING_BLOCK) { - tuning_data.blk_pattern = tuning_blk_pattern_4bit; - tuning_data.blksz = sizeof(tuning_blk_pattern_4bit); - } else { - dev_err(host->dev, - "Undefined command(%d) for tuning\n", opcode); - return -EINVAL; - } - if (drv_data && drv_data->execute_tuning) - err = drv_data->execute_tuning(slot, opcode, &tuning_data); + err = drv_data->execute_tuning(slot); return err; } @@ -1337,7 +1334,7 @@ static const struct mmc_host_ops dw_mci_ops = { .execute_tuning = dw_mci_execute_tuning, .card_busy = dw_mci_card_busy, .start_signal_voltage_switch = dw_mci_switch_voltage, - + .init_card = dw_mci_init_card, }; static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) @@ -2319,9 +2316,9 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) #ifdef CONFIG_MMC_DW_IDMAC mmc->max_segs = host->ring_size; mmc->max_blk_size = 65536; - mmc->max_blk_count = host->ring_size; mmc->max_seg_size = 0x1000; - mmc->max_req_size = mmc->max_seg_size * mmc->max_blk_count; + mmc->max_req_size = mmc->max_seg_size * host->ring_size; + mmc->max_blk_count = mmc->max_req_size / 512; #else mmc->max_segs = 64; mmc->max_blk_size = 65536; /* BLKSIZ is 16 bits */ @@ -2533,10 +2530,8 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) u32 clock_frequency; pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) { - dev_err(dev, "could not allocate memory for pdata\n"); + if (!pdata) return ERR_PTR(-ENOMEM); - } /* find out number of slots supported */ if (of_property_read_u32(dev->of_node, "num-slots", @@ -2660,6 +2655,7 @@ int dw_mci_probe(struct dw_mci *host) host->quirks = host->pdata->quirks; spin_lock_init(&host->lock); + spin_lock_init(&host->irq_lock); INIT_LIST_HEAD(&host->queue); /* diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 0d0f7a271d63..18c4afe683b8 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -244,15 +244,11 @@ struct dw_mci_slot { unsigned long flags; #define DW_MMC_CARD_PRESENT 0 #define DW_MMC_CARD_NEED_INIT 1 +#define DW_MMC_CARD_NO_LOW_PWR 2 int id; int sdio_id; }; -struct dw_mci_tuning_data { - const u8 *blk_pattern; - unsigned int blksz; -}; - /** * dw_mci driver data - dw-mshc implementation specific driver data. * @caps: mmc subsystem specified capabilities of the controller(s). @@ -274,7 +270,6 @@ struct dw_mci_drv_data { void (*prepare_command)(struct dw_mci *host, u32 *cmdr); void (*set_ios)(struct dw_mci *host, struct mmc_ios *ios); int (*parse_dt)(struct dw_mci *host); - int (*execute_tuning)(struct dw_mci_slot *slot, u32 opcode, - struct dw_mci_tuning_data *tuning_data); + int (*execute_tuning)(struct dw_mci_slot *slot); }; #endif /* _DW_MMC_H_ */ diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 8232e9a02d40..7fe16194ebc8 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -430,7 +430,6 @@ static void mmci_init_sg(struct mmci_host *host, struct mmc_data *data) static void mmci_dma_setup(struct mmci_host *host) { const char *rxname, *txname; - dma_cap_mask_t mask; struct variant_data *variant = host->variant; host->dma_rx_channel = dma_request_slave_channel(mmc_dev(host->mmc), "rx"); @@ -439,10 +438,6 @@ static void mmci_dma_setup(struct mmci_host *host) /* initialize pre request cookie */ host->next_data.cookie = 1; - /* Try to acquire a generic DMA engine slave channel */ - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - /* * If only an RX channel is specified, the driver will * attempt to use it bidirectionally, however if it is @@ -1739,10 +1734,10 @@ static int mmci_probe(struct amba_device *dev, pm_runtime_set_autosuspend_delay(&dev->dev, 50); pm_runtime_use_autosuspend(&dev->dev); - pm_runtime_put(&dev->dev); mmc_add_host(mmc); + pm_runtime_put(&dev->dev); return 0; clk_disable: diff --git a/drivers/mmc/host/moxart-mmc.c b/drivers/mmc/host/moxart-mmc.c index f3e18d08e852..006f1862444b 100644 --- a/drivers/mmc/host/moxart-mmc.c +++ b/drivers/mmc/host/moxart-mmc.c @@ -17,6 +17,7 @@ #include <linux/init.h> #include <linux/platform_device.h> #include <linux/delay.h> +#include <linux/errno.h> #include <linux/interrupt.h> #include <linux/blkdev.h> #include <linux/dma-mapping.h> @@ -562,7 +563,6 @@ static int moxart_probe(struct platform_device *pdev) struct dma_slave_config cfg; struct clk *clk; void __iomem *reg_mmc; - dma_cap_mask_t mask; int irq, ret; u32 i; @@ -586,9 +586,8 @@ static int moxart_probe(struct platform_device *pdev) goto out; } - clk = of_clk_get(node, 0); + clk = devm_clk_get(dev, NULL); if (IS_ERR(clk)) { - dev_err(dev, "of_clk_get failed\n"); ret = PTR_ERR(clk); goto out; } @@ -599,10 +598,9 @@ static int moxart_probe(struct platform_device *pdev) goto out; } - mmc_of_parse(mmc); - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); + ret = mmc_of_parse(mmc); + if (ret) + goto out; host = mmc_priv(mmc); host->mmc = mmc; @@ -611,8 +609,8 @@ static int moxart_probe(struct platform_device *pdev) host->timeout = msecs_to_jiffies(1000); host->sysclk = clk_get_rate(clk); host->fifo_width = readl(host->base + REG_FEATURE) << 2; - host->dma_chan_tx = of_dma_request_slave_channel(node, "tx"); - host->dma_chan_rx = of_dma_request_slave_channel(node, "rx"); + host->dma_chan_tx = dma_request_slave_channel_reason(dev, "tx"); + host->dma_chan_rx = dma_request_slave_channel_reason(dev, "rx"); spin_lock_init(&host->lock); @@ -622,6 +620,11 @@ static int moxart_probe(struct platform_device *pdev) mmc->ocr_avail = 0xffff00; /* Support 2.0v - 3.6v power. */ if (IS_ERR(host->dma_chan_tx) || IS_ERR(host->dma_chan_rx)) { + if (PTR_ERR(host->dma_chan_tx) == -EPROBE_DEFER || + PTR_ERR(host->dma_chan_rx) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto out; + } dev_dbg(dev, "PIO mode transfer enabled\n"); host->have_dma = false; } else { @@ -700,9 +703,6 @@ static int moxart_remove(struct platform_device *pdev) writel(readl(host->base + REG_CLOCK_CONTROL) | CLK_OFF, host->base + REG_CLOCK_CONTROL); } - - kfree(host); - return 0; } diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c index 4f8618f4522d..a448498e3af2 100644 --- a/drivers/mmc/host/mvsdio.c +++ b/drivers/mmc/host/mvsdio.c @@ -25,7 +25,6 @@ #include <linux/of_irq.h> #include <linux/mmc/host.h> #include <linux/mmc/slot-gpio.h> -#include <linux/pinctrl/consumer.h> #include <asm/sizes.h> #include <asm/unaligned.h> @@ -704,7 +703,6 @@ static int mvsd_probe(struct platform_device *pdev) const struct mbus_dram_target_info *dram; struct resource *r; int ret, irq; - struct pinctrl *pinctrl; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); @@ -721,10 +719,6 @@ static int mvsd_probe(struct platform_device *pdev) host->mmc = mmc; host->dev = &pdev->dev; - pinctrl = devm_pinctrl_get_select_default(&pdev->dev); - if (IS_ERR(pinctrl)) - dev_warn(&pdev->dev, "no pins associated\n"); - /* * Some non-DT platforms do not pass a clock, and the clock * frequency is passed through platform_data. On DT platforms, @@ -828,8 +822,6 @@ static int mvsd_probe(struct platform_device *pdev) out: if (mmc) { - mmc_gpio_free_cd(mmc); - mmc_gpio_free_ro(mmc); if (!IS_ERR(host->clk)) clk_disable_unprepare(host->clk); mmc_free_host(mmc); @@ -844,8 +836,6 @@ static int mvsd_remove(struct platform_device *pdev) struct mvsd_host *host = mmc_priv(mmc); - mmc_gpio_free_cd(mmc); - mmc_gpio_free_ro(mmc); mmc_remove_host(mmc); del_timer_sync(&host->timer); mvsd_power_down(host); diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index 60c4ca97a727..a82411a2c024 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -677,8 +677,7 @@ static int mxs_mmc_probe(struct platform_device *pdev) return 0; out_free_dma: - if (ssp->dmach) - dma_release_channel(ssp->dmach); + dma_release_channel(ssp->dmach); out_clk_disable: clk_disable_unprepare(ssp->clk); out_mmc_free: diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 7c71dcdcba8b..f84cfb01716d 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -36,6 +36,7 @@ #include <linux/mmc/host.h> #include <linux/mmc/core.h> #include <linux/mmc/mmc.h> +#include <linux/mmc/slot-gpio.h> #include <linux/io.h> #include <linux/irq.h> #include <linux/gpio.h> @@ -251,55 +252,24 @@ static void omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host); static int omap_hsmmc_card_detect(struct device *dev) { struct omap_hsmmc_host *host = dev_get_drvdata(dev); - struct omap_hsmmc_platform_data *mmc = host->pdata; - /* NOTE: assumes card detect signal is active-low */ - return !gpio_get_value_cansleep(mmc->switch_pin); + return mmc_gpio_get_cd(host->mmc); } static int omap_hsmmc_get_wp(struct device *dev) { struct omap_hsmmc_host *host = dev_get_drvdata(dev); - struct omap_hsmmc_platform_data *mmc = host->pdata; - /* NOTE: assumes write protect signal is active-high */ - return gpio_get_value_cansleep(mmc->gpio_wp); + return mmc_gpio_get_ro(host->mmc); } static int omap_hsmmc_get_cover_state(struct device *dev) { struct omap_hsmmc_host *host = dev_get_drvdata(dev); - struct omap_hsmmc_platform_data *mmc = host->pdata; - /* NOTE: assumes card detect signal is active-low */ - return !gpio_get_value_cansleep(mmc->switch_pin); + return mmc_gpio_get_cd(host->mmc); } -#ifdef CONFIG_PM - -static int omap_hsmmc_suspend_cdirq(struct device *dev) -{ - struct omap_hsmmc_host *host = dev_get_drvdata(dev); - - disable_irq(host->card_detect_irq); - return 0; -} - -static int omap_hsmmc_resume_cdirq(struct device *dev) -{ - struct omap_hsmmc_host *host = dev_get_drvdata(dev); - - enable_irq(host->card_detect_irq); - return 0; -} - -#else - -#define omap_hsmmc_suspend_cdirq NULL -#define omap_hsmmc_resume_cdirq NULL - -#endif - #ifdef CONFIG_REGULATOR static int omap_hsmmc_set_power(struct device *dev, int power_on, int vdd) @@ -464,7 +434,10 @@ static inline int omap_hsmmc_have_reg(void) #endif -static int omap_hsmmc_gpio_init(struct omap_hsmmc_host *host, +static irqreturn_t omap_hsmmc_detect(int irq, void *dev_id); + +static int omap_hsmmc_gpio_init(struct mmc_host *mmc, + struct omap_hsmmc_host *host, struct omap_hsmmc_platform_data *pdata) { int ret; @@ -477,46 +450,24 @@ static int omap_hsmmc_gpio_init(struct omap_hsmmc_host *host, host->card_detect = omap_hsmmc_card_detect; host->card_detect_irq = gpio_to_irq(pdata->switch_pin); - ret = gpio_request(pdata->switch_pin, "mmc_cd"); + mmc_gpio_set_cd_isr(mmc, omap_hsmmc_detect); + ret = mmc_gpio_request_cd(mmc, pdata->switch_pin, 0); if (ret) return ret; - ret = gpio_direction_input(pdata->switch_pin); - if (ret) - goto err_free_sp; } else { pdata->switch_pin = -EINVAL; } if (gpio_is_valid(pdata->gpio_wp)) { host->get_ro = omap_hsmmc_get_wp; - ret = gpio_request(pdata->gpio_wp, "mmc_wp"); - if (ret) - goto err_free_cd; - ret = gpio_direction_input(pdata->gpio_wp); + ret = mmc_gpio_request_ro(mmc, pdata->gpio_wp); if (ret) - goto err_free_wp; + return ret; } else { pdata->gpio_wp = -EINVAL; } return 0; - -err_free_wp: - gpio_free(pdata->gpio_wp); -err_free_cd: - if (gpio_is_valid(pdata->switch_pin)) -err_free_sp: - gpio_free(pdata->switch_pin); - return ret; -} - -static void omap_hsmmc_gpio_free(struct omap_hsmmc_host *host, - struct omap_hsmmc_platform_data *pdata) -{ - if (gpio_is_valid(pdata->gpio_wp)) - gpio_free(pdata->gpio_wp); - if (gpio_is_valid(pdata->switch_pin)) - gpio_free(pdata->switch_pin); } /* @@ -1978,13 +1929,6 @@ static struct omap_hsmmc_platform_data *of_get_hsmmc_pdata(struct device *dev) { struct omap_hsmmc_platform_data *pdata; struct device_node *np = dev->of_node; - u32 bus_width, max_freq; - int cd_gpio, wp_gpio; - - cd_gpio = of_get_named_gpio(np, "cd-gpios", 0); - wp_gpio = of_get_named_gpio(np, "wp-gpios", 0); - if (cd_gpio == -EPROBE_DEFER || wp_gpio == -EPROBE_DEFER) - return ERR_PTR(-EPROBE_DEFER); pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) @@ -1993,34 +1937,20 @@ static struct omap_hsmmc_platform_data *of_get_hsmmc_pdata(struct device *dev) if (of_find_property(np, "ti,dual-volt", NULL)) pdata->controller_flags |= OMAP_HSMMC_SUPPORTS_DUAL_VOLT; - pdata->switch_pin = cd_gpio; - pdata->gpio_wp = wp_gpio; + pdata->switch_pin = -EINVAL; + pdata->gpio_wp = -EINVAL; if (of_find_property(np, "ti,non-removable", NULL)) { pdata->nonremovable = true; pdata->no_regulator_off_init = true; } - of_property_read_u32(np, "bus-width", &bus_width); - if (bus_width == 4) - pdata->caps |= MMC_CAP_4_BIT_DATA; - else if (bus_width == 8) - pdata->caps |= MMC_CAP_8_BIT_DATA; if (of_find_property(np, "ti,needs-special-reset", NULL)) pdata->features |= HSMMC_HAS_UPDATED_RESET; - if (!of_property_read_u32(np, "max-frequency", &max_freq)) - pdata->max_freq = max_freq; - if (of_find_property(np, "ti,needs-special-hs-handling", NULL)) pdata->features |= HSMMC_HAS_HSPE_SUPPORT; - if (of_find_property(np, "keep-power-in-suspend", NULL)) - pdata->pm_caps |= MMC_PM_KEEP_POWER; - - if (of_find_property(np, "enable-sdio-wakeup", NULL)) - pdata->pm_caps |= MMC_PM_WAKE_SDIO_IRQ; - return pdata; } #else @@ -2078,6 +2008,10 @@ static int omap_hsmmc_probe(struct platform_device *pdev) goto err; } + ret = mmc_of_parse(mmc); + if (ret) + goto err1; + host = mmc_priv(mmc); host->mmc = mmc; host->pdata = pdata; @@ -2091,7 +2025,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev) host->next_data.cookie = 1; host->pbias_enabled = 0; - ret = omap_hsmmc_gpio_init(host, pdata); + ret = omap_hsmmc_gpio_init(mmc, host, pdata); if (ret) goto err_gpio; @@ -2106,7 +2040,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev) if (pdata->max_freq > 0) mmc->f_max = pdata->max_freq; - else + else if (mmc->f_max == 0) mmc->f_max = OMAP_MMC_MAX_CLOCK; spin_lock_init(&host->irq_lock); @@ -2160,7 +2094,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev) if (mmc_pdata(host)->nonremovable) mmc->caps |= MMC_CAP_NONREMOVABLE; - mmc->pm_caps = mmc_pdata(host)->pm_caps; + mmc->pm_caps |= mmc_pdata(host)->pm_caps; omap_hsmmc_conf_bus_power(host); @@ -2222,22 +2156,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev) mmc->ocr_avail = mmc_pdata(host)->ocr_mask; - /* Request IRQ for card detect */ - if (host->card_detect_irq) { - ret = devm_request_threaded_irq(&pdev->dev, - host->card_detect_irq, - NULL, omap_hsmmc_detect, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - mmc_hostname(mmc), host); - if (ret) { - dev_err(mmc_dev(host->mmc), - "Unable to grab MMC CD IRQ\n"); - goto err_irq_cd; - } - host->suspend = omap_hsmmc_suspend_cdirq; - host->resume = omap_hsmmc_resume_cdirq; - } - omap_hsmmc_disable_irq(host); /* @@ -2276,7 +2194,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev) err_slot_name: mmc_remove_host(mmc); -err_irq_cd: if (host->use_reg) omap_hsmmc_reg_put(host); err_irq: @@ -2289,7 +2206,6 @@ err_irq: if (host->dbclk) clk_disable_unprepare(host->dbclk); err1: - omap_hsmmc_gpio_free(host, pdata); err_gpio: mmc_free_host(mmc); err: @@ -2315,32 +2231,12 @@ static int omap_hsmmc_remove(struct platform_device *pdev) if (host->dbclk) clk_disable_unprepare(host->dbclk); - omap_hsmmc_gpio_free(host, host->pdata); mmc_free_host(host->mmc); return 0; } #ifdef CONFIG_PM -static int omap_hsmmc_prepare(struct device *dev) -{ - struct omap_hsmmc_host *host = dev_get_drvdata(dev); - - if (host->suspend) - return host->suspend(dev); - - return 0; -} - -static void omap_hsmmc_complete(struct device *dev) -{ - struct omap_hsmmc_host *host = dev_get_drvdata(dev); - - if (host->resume) - host->resume(dev); - -} - static int omap_hsmmc_suspend(struct device *dev) { struct omap_hsmmc_host *host = dev_get_drvdata(dev); @@ -2398,8 +2294,6 @@ static int omap_hsmmc_resume(struct device *dev) } #else -#define omap_hsmmc_prepare NULL -#define omap_hsmmc_complete NULL #define omap_hsmmc_suspend NULL #define omap_hsmmc_resume NULL #endif @@ -2484,8 +2378,6 @@ static int omap_hsmmc_runtime_resume(struct device *dev) static struct dev_pm_ops omap_hsmmc_dev_pm_ops = { .suspend = omap_hsmmc_suspend, .resume = omap_hsmmc_resume, - .prepare = omap_hsmmc_prepare, - .complete = omap_hsmmc_complete, .runtime_suspend = omap_hsmmc_runtime_suspend, .runtime_resume = omap_hsmmc_runtime_resume, }; diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index c70b602f8f1e..1d3d6c4bfdc6 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -28,6 +28,7 @@ #include <linux/mmc/host.h> #include <linux/mmc/mmc.h> #include <linux/mmc/sd.h> +#include <linux/mmc/sdio.h> #include <linux/mmc/card.h> #include <linux/mfd/rtsx_pci.h> #include <asm/unaligned.h> @@ -53,9 +54,9 @@ struct realtek_pci_sdmmc { #define SDMMC_POWER_ON 1 #define SDMMC_POWER_OFF 0 - unsigned int sg_count; + int sg_count; s32 cookie; - unsigned int cookie_sg_count; + int cookie_sg_count; bool using_cookie; }; @@ -71,30 +72,83 @@ static inline void sd_clear_error(struct realtek_pci_sdmmc *host) } #ifdef DEBUG -static void sd_print_debug_regs(struct realtek_pci_sdmmc *host) +static void dump_reg_range(struct realtek_pci_sdmmc *host, u16 start, u16 end) { - struct rtsx_pcr *pcr = host->pcr; - u16 i; - u8 *ptr; + u16 len = end - start + 1; + int i; + u8 data[8]; + + for (i = 0; i < len; i += 8) { + int j; + int n = min(8, len - i); + + memset(&data, 0, sizeof(data)); + for (j = 0; j < n; j++) + rtsx_pci_read_register(host->pcr, start + i + j, + data + j); + dev_dbg(sdmmc_dev(host), "0x%04X(%d): %8ph\n", + start + i, n, data); + } +} - /* Print SD host internal registers */ - rtsx_pci_init_cmd(pcr); - for (i = 0xFDA0; i <= 0xFDAE; i++) - rtsx_pci_add_cmd(pcr, READ_REG_CMD, i, 0, 0); - for (i = 0xFD52; i <= 0xFD69; i++) - rtsx_pci_add_cmd(pcr, READ_REG_CMD, i, 0, 0); - rtsx_pci_send_cmd(pcr, 100); - - ptr = rtsx_pci_get_cmd_data(pcr); - for (i = 0xFDA0; i <= 0xFDAE; i++) - dev_dbg(sdmmc_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++)); - for (i = 0xFD52; i <= 0xFD69; i++) - dev_dbg(sdmmc_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++)); +static void sd_print_debug_regs(struct realtek_pci_sdmmc *host) +{ + dump_reg_range(host, 0xFDA0, 0xFDB3); + dump_reg_range(host, 0xFD52, 0xFD69); } #else #define sd_print_debug_regs(host) #endif /* DEBUG */ +static inline int sd_get_cd_int(struct realtek_pci_sdmmc *host) +{ + return rtsx_pci_readl(host->pcr, RTSX_BIPR) & SD_EXIST; +} + +static void sd_cmd_set_sd_cmd(struct rtsx_pcr *pcr, struct mmc_command *cmd) +{ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD0, 0xFF, + SD_CMD_START | cmd->opcode); + rtsx_pci_write_be32(pcr, SD_CMD1, cmd->arg); +} + +static void sd_cmd_set_data_len(struct rtsx_pcr *pcr, u16 blocks, u16 blksz) +{ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_L, 0xFF, blocks); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_H, 0xFF, blocks >> 8); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, blksz); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_H, 0xFF, blksz >> 8); +} + +static int sd_response_type(struct mmc_command *cmd) +{ + switch (mmc_resp_type(cmd)) { + case MMC_RSP_NONE: + return SD_RSP_TYPE_R0; + case MMC_RSP_R1: + return SD_RSP_TYPE_R1; + case MMC_RSP_R1 & ~MMC_RSP_CRC: + return SD_RSP_TYPE_R1 | SD_NO_CHECK_CRC7; + case MMC_RSP_R1B: + return SD_RSP_TYPE_R1b; + case MMC_RSP_R2: + return SD_RSP_TYPE_R2; + case MMC_RSP_R3: + return SD_RSP_TYPE_R3; + default: + return -EINVAL; + } +} + +static int sd_status_index(int resp_type) +{ + if (resp_type == SD_RSP_TYPE_R0) + return 0; + else if (resp_type == SD_RSP_TYPE_R2) + return 16; + + return 5; +} /* * sd_pre_dma_transfer - do dma_map_sg() or using cookie * @@ -166,123 +220,6 @@ static void sdmmc_post_req(struct mmc_host *mmc, struct mmc_request *mrq, data->host_cookie = 0; } -static int sd_read_data(struct realtek_pci_sdmmc *host, u8 *cmd, u16 byte_cnt, - u8 *buf, int buf_len, int timeout) -{ - struct rtsx_pcr *pcr = host->pcr; - int err, i; - u8 trans_mode; - - dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD%d\n", __func__, cmd[0] - 0x40); - - if (!buf) - buf_len = 0; - - if ((cmd[0] & 0x3F) == MMC_SEND_TUNING_BLOCK) - trans_mode = SD_TM_AUTO_TUNING; - else - trans_mode = SD_TM_NORMAL_READ; - - rtsx_pci_init_cmd(pcr); - - for (i = 0; i < 5; i++) - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD0 + i, 0xFF, cmd[i]); - - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, (u8)byte_cnt); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_H, - 0xFF, (u8)(byte_cnt >> 8)); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_L, 0xFF, 1); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_H, 0xFF, 0); - - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF, - SD_CALCULATE_CRC7 | SD_CHECK_CRC16 | - SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_6); - if (trans_mode != SD_TM_AUTO_TUNING) - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, - CARD_DATA_SOURCE, 0x01, PINGPONG_BUFFER); - - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER, - 0xFF, trans_mode | SD_TRANSFER_START); - rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER, - SD_TRANSFER_END, SD_TRANSFER_END); - - err = rtsx_pci_send_cmd(pcr, timeout); - if (err < 0) { - sd_print_debug_regs(host); - dev_dbg(sdmmc_dev(host), - "rtsx_pci_send_cmd fail (err = %d)\n", err); - return err; - } - - if (buf && buf_len) { - err = rtsx_pci_read_ppbuf(pcr, buf, buf_len); - if (err < 0) { - dev_dbg(sdmmc_dev(host), - "rtsx_pci_read_ppbuf fail (err = %d)\n", err); - return err; - } - } - - return 0; -} - -static int sd_write_data(struct realtek_pci_sdmmc *host, u8 *cmd, u16 byte_cnt, - u8 *buf, int buf_len, int timeout) -{ - struct rtsx_pcr *pcr = host->pcr; - int err, i; - u8 trans_mode; - - if (!buf) - buf_len = 0; - - if (buf && buf_len) { - err = rtsx_pci_write_ppbuf(pcr, buf, buf_len); - if (err < 0) { - dev_dbg(sdmmc_dev(host), - "rtsx_pci_write_ppbuf fail (err = %d)\n", err); - return err; - } - } - - trans_mode = cmd ? SD_TM_AUTO_WRITE_2 : SD_TM_AUTO_WRITE_3; - rtsx_pci_init_cmd(pcr); - - if (cmd) { - dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d\n", __func__, - cmd[0] - 0x40); - - for (i = 0; i < 5; i++) - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, - SD_CMD0 + i, 0xFF, cmd[i]); - } - - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, (u8)byte_cnt); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_H, - 0xFF, (u8)(byte_cnt >> 8)); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_L, 0xFF, 1); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_H, 0xFF, 0); - - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF, - SD_CALCULATE_CRC7 | SD_CHECK_CRC16 | - SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_6); - - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER, 0xFF, - trans_mode | SD_TRANSFER_START); - rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER, - SD_TRANSFER_END, SD_TRANSFER_END); - - err = rtsx_pci_send_cmd(pcr, timeout); - if (err < 0) { - sd_print_debug_regs(host); - dev_dbg(sdmmc_dev(host), - "rtsx_pci_send_cmd fail (err = %d)\n", err); - return err; - } - - return 0; -} - static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host, struct mmc_command *cmd) { @@ -293,47 +230,18 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host, int timeout = 100; int i; u8 *ptr; - int stat_idx = 0; - u8 rsp_type; - int rsp_len = 5; + int rsp_type; + int stat_idx; bool clock_toggled = false; dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n", __func__, cmd_idx, arg); - /* Response type: - * R0 - * R1, R5, R6, R7 - * R1b - * R2 - * R3, R4 - */ - switch (mmc_resp_type(cmd)) { - case MMC_RSP_NONE: - rsp_type = SD_RSP_TYPE_R0; - rsp_len = 0; - break; - case MMC_RSP_R1: - rsp_type = SD_RSP_TYPE_R1; - break; - case MMC_RSP_R1 & ~MMC_RSP_CRC: - rsp_type = SD_RSP_TYPE_R1 | SD_NO_CHECK_CRC7; - break; - case MMC_RSP_R1B: - rsp_type = SD_RSP_TYPE_R1b; - break; - case MMC_RSP_R2: - rsp_type = SD_RSP_TYPE_R2; - rsp_len = 16; - break; - case MMC_RSP_R3: - rsp_type = SD_RSP_TYPE_R3; - break; - default: - dev_dbg(sdmmc_dev(host), "cmd->flag is not valid\n"); - err = -EINVAL; + rsp_type = sd_response_type(cmd); + if (rsp_type < 0) goto out; - } + + stat_idx = sd_status_index(rsp_type); if (rsp_type == SD_RSP_TYPE_R1b) timeout = 3000; @@ -348,13 +256,7 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host, } rtsx_pci_init_cmd(pcr); - - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD0, 0xFF, 0x40 | cmd_idx); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD1, 0xFF, (u8)(arg >> 24)); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD2, 0xFF, (u8)(arg >> 16)); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD3, 0xFF, (u8)(arg >> 8)); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD4, 0xFF, (u8)arg); - + sd_cmd_set_sd_cmd(pcr, cmd); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF, rsp_type); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, PINGPONG_BUFFER); @@ -368,12 +270,10 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host, /* Read data from ping-pong buffer */ for (i = PPBUF_BASE2; i < PPBUF_BASE2 + 16; i++) rtsx_pci_add_cmd(pcr, READ_REG_CMD, (u16)i, 0, 0); - stat_idx = 16; } else if (rsp_type != SD_RSP_TYPE_R0) { /* Read data from SD_CMDx registers */ for (i = SD_CMD0; i <= SD_CMD4; i++) rtsx_pci_add_cmd(pcr, READ_REG_CMD, (u16)i, 0, 0); - stat_idx = 5; } rtsx_pci_add_cmd(pcr, READ_REG_CMD, SD_STAT1, 0, 0); @@ -438,71 +338,213 @@ out: SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0); } -static int sd_rw_multi(struct realtek_pci_sdmmc *host, struct mmc_request *mrq) +static int sd_read_data(struct realtek_pci_sdmmc *host, struct mmc_command *cmd, + u16 byte_cnt, u8 *buf, int buf_len, int timeout) +{ + struct rtsx_pcr *pcr = host->pcr; + int err; + u8 trans_mode; + + dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n", + __func__, cmd->opcode, cmd->arg); + + if (!buf) + buf_len = 0; + + if (cmd->opcode == MMC_SEND_TUNING_BLOCK) + trans_mode = SD_TM_AUTO_TUNING; + else + trans_mode = SD_TM_NORMAL_READ; + + rtsx_pci_init_cmd(pcr); + sd_cmd_set_sd_cmd(pcr, cmd); + sd_cmd_set_data_len(pcr, 1, byte_cnt); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF, + SD_CALCULATE_CRC7 | SD_CHECK_CRC16 | + SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_6); + if (trans_mode != SD_TM_AUTO_TUNING) + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, + CARD_DATA_SOURCE, 0x01, PINGPONG_BUFFER); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER, + 0xFF, trans_mode | SD_TRANSFER_START); + rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER, + SD_TRANSFER_END, SD_TRANSFER_END); + + err = rtsx_pci_send_cmd(pcr, timeout); + if (err < 0) { + sd_print_debug_regs(host); + dev_dbg(sdmmc_dev(host), + "rtsx_pci_send_cmd fail (err = %d)\n", err); + return err; + } + + if (buf && buf_len) { + err = rtsx_pci_read_ppbuf(pcr, buf, buf_len); + if (err < 0) { + dev_dbg(sdmmc_dev(host), + "rtsx_pci_read_ppbuf fail (err = %d)\n", err); + return err; + } + } + + return 0; +} + +static int sd_write_data(struct realtek_pci_sdmmc *host, + struct mmc_command *cmd, u16 byte_cnt, u8 *buf, int buf_len, + int timeout) +{ + struct rtsx_pcr *pcr = host->pcr; + int err; + + dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n", + __func__, cmd->opcode, cmd->arg); + + if (!buf) + buf_len = 0; + + sd_send_cmd_get_rsp(host, cmd); + if (cmd->error) + return cmd->error; + + if (buf && buf_len) { + err = rtsx_pci_write_ppbuf(pcr, buf, buf_len); + if (err < 0) { + dev_dbg(sdmmc_dev(host), + "rtsx_pci_write_ppbuf fail (err = %d)\n", err); + return err; + } + } + + rtsx_pci_init_cmd(pcr); + sd_cmd_set_data_len(pcr, 1, byte_cnt); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF, + SD_CALCULATE_CRC7 | SD_CHECK_CRC16 | + SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_0); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER, 0xFF, + SD_TRANSFER_START | SD_TM_AUTO_WRITE_3); + rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER, + SD_TRANSFER_END, SD_TRANSFER_END); + + err = rtsx_pci_send_cmd(pcr, timeout); + if (err < 0) { + sd_print_debug_regs(host); + dev_dbg(sdmmc_dev(host), + "rtsx_pci_send_cmd fail (err = %d)\n", err); + return err; + } + + return 0; +} + +static int sd_read_long_data(struct realtek_pci_sdmmc *host, + struct mmc_request *mrq) { struct rtsx_pcr *pcr = host->pcr; struct mmc_host *mmc = host->mmc; struct mmc_card *card = mmc->card; + struct mmc_command *cmd = mrq->cmd; struct mmc_data *data = mrq->data; int uhs = mmc_card_uhs(card); - int read = (data->flags & MMC_DATA_READ) ? 1 : 0; - u8 cfg2, trans_mode; + u8 cfg2 = 0; int err; + int resp_type; size_t data_len = data->blksz * data->blocks; - if (read) { - cfg2 = SD_CALCULATE_CRC7 | SD_CHECK_CRC16 | - SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_0; - trans_mode = SD_TM_AUTO_READ_3; - } else { - cfg2 = SD_NO_CALCULATE_CRC7 | SD_CHECK_CRC16 | - SD_NO_WAIT_BUSY_END | SD_NO_CHECK_CRC7 | SD_RSP_LEN_0; - trans_mode = SD_TM_AUTO_WRITE_3; - } + dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n", + __func__, cmd->opcode, cmd->arg); + + resp_type = sd_response_type(cmd); + if (resp_type < 0) + return resp_type; if (!uhs) cfg2 |= SD_NO_CHECK_WAIT_CRC_TO; rtsx_pci_init_cmd(pcr); - - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, 0x00); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_H, 0xFF, 0x02); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_L, - 0xFF, (u8)data->blocks); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_H, - 0xFF, (u8)(data->blocks >> 8)); - + sd_cmd_set_sd_cmd(pcr, cmd); + sd_cmd_set_data_len(pcr, data->blocks, data->blksz); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, IRQSTAT0, DMA_DONE_INT, DMA_DONE_INT); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC3, - 0xFF, (u8)(data_len >> 24)); + 0xFF, (u8)(data_len >> 24)); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC2, - 0xFF, (u8)(data_len >> 16)); + 0xFF, (u8)(data_len >> 16)); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC1, - 0xFF, (u8)(data_len >> 8)); + 0xFF, (u8)(data_len >> 8)); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC0, 0xFF, (u8)data_len); - if (read) { - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMACTL, - 0x03 | DMA_PACK_SIZE_MASK, - DMA_DIR_FROM_CARD | DMA_EN | DMA_512); - } else { - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMACTL, - 0x03 | DMA_PACK_SIZE_MASK, - DMA_DIR_TO_CARD | DMA_EN | DMA_512); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMACTL, + 0x03 | DMA_PACK_SIZE_MASK, + DMA_DIR_FROM_CARD | DMA_EN | DMA_512); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE, + 0x01, RING_BUFFER); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF, cfg2 | resp_type); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER, 0xFF, + SD_TRANSFER_START | SD_TM_AUTO_READ_2); + rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER, + SD_TRANSFER_END, SD_TRANSFER_END); + rtsx_pci_send_cmd_no_wait(pcr); + + err = rtsx_pci_dma_transfer(pcr, data->sg, host->sg_count, 1, 10000); + if (err < 0) { + sd_print_debug_regs(host); + sd_clear_error(host); + return err; } + return 0; +} + +static int sd_write_long_data(struct realtek_pci_sdmmc *host, + struct mmc_request *mrq) +{ + struct rtsx_pcr *pcr = host->pcr; + struct mmc_host *mmc = host->mmc; + struct mmc_card *card = mmc->card; + struct mmc_command *cmd = mrq->cmd; + struct mmc_data *data = mrq->data; + int uhs = mmc_card_uhs(card); + u8 cfg2; + int err; + size_t data_len = data->blksz * data->blocks; + + sd_send_cmd_get_rsp(host, cmd); + if (cmd->error) + return cmd->error; + + dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n", + __func__, cmd->opcode, cmd->arg); + + cfg2 = SD_NO_CALCULATE_CRC7 | SD_CHECK_CRC16 | + SD_NO_WAIT_BUSY_END | SD_NO_CHECK_CRC7 | SD_RSP_LEN_0; + + if (!uhs) + cfg2 |= SD_NO_CHECK_WAIT_CRC_TO; + + rtsx_pci_init_cmd(pcr); + sd_cmd_set_data_len(pcr, data->blocks, data->blksz); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, IRQSTAT0, + DMA_DONE_INT, DMA_DONE_INT); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC3, + 0xFF, (u8)(data_len >> 24)); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC2, + 0xFF, (u8)(data_len >> 16)); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC1, + 0xFF, (u8)(data_len >> 8)); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC0, 0xFF, (u8)data_len); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMACTL, + 0x03 | DMA_PACK_SIZE_MASK, + DMA_DIR_TO_CARD | DMA_EN | DMA_512); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, RING_BUFFER); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF, cfg2); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER, 0xFF, - trans_mode | SD_TRANSFER_START); + SD_TRANSFER_START | SD_TM_AUTO_WRITE_3); rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER, SD_TRANSFER_END, SD_TRANSFER_END); - rtsx_pci_send_cmd_no_wait(pcr); - - err = rtsx_pci_dma_transfer(pcr, data->sg, host->sg_count, read, 10000); + err = rtsx_pci_dma_transfer(pcr, data->sg, host->sg_count, 0, 10000); if (err < 0) { sd_clear_error(host); return err; @@ -511,6 +553,23 @@ static int sd_rw_multi(struct realtek_pci_sdmmc *host, struct mmc_request *mrq) return 0; } +static int sd_rw_multi(struct realtek_pci_sdmmc *host, struct mmc_request *mrq) +{ + struct mmc_data *data = mrq->data; + + if (host->sg_count < 0) { + data->error = host->sg_count; + dev_dbg(sdmmc_dev(host), "%s: sg_count = %d is invalid\n", + __func__, host->sg_count); + return data->error; + } + + if (data->flags & MMC_DATA_READ) + return sd_read_long_data(host, mrq); + + return sd_write_long_data(host, mrq); +} + static inline void sd_enable_initial_mode(struct realtek_pci_sdmmc *host) { rtsx_pci_write_register(host->pcr, SD_CFG1, @@ -528,10 +587,7 @@ static void sd_normal_rw(struct realtek_pci_sdmmc *host, { struct mmc_command *cmd = mrq->cmd; struct mmc_data *data = mrq->data; - u8 _cmd[5], *buf; - - _cmd[0] = 0x40 | (u8)cmd->opcode; - put_unaligned_be32(cmd->arg, (u32 *)(&_cmd[1])); + u8 *buf; buf = kzalloc(data->blksz, GFP_NOIO); if (!buf) { @@ -543,7 +599,7 @@ static void sd_normal_rw(struct realtek_pci_sdmmc *host, if (host->initial_mode) sd_disable_initial_mode(host); - cmd->error = sd_read_data(host, _cmd, (u16)data->blksz, buf, + cmd->error = sd_read_data(host, cmd, (u16)data->blksz, buf, data->blksz, 200); if (host->initial_mode) @@ -553,7 +609,7 @@ static void sd_normal_rw(struct realtek_pci_sdmmc *host, } else { sg_copy_to_buffer(data->sg, data->sg_len, buf, data->blksz); - cmd->error = sd_write_data(host, _cmd, (u16)data->blksz, buf, + cmd->error = sd_write_data(host, cmd, (u16)data->blksz, buf, data->blksz, 200); } @@ -653,14 +709,14 @@ static int sd_tuning_rx_cmd(struct realtek_pci_sdmmc *host, u8 opcode, u8 sample_point) { int err; - u8 cmd[5] = {0}; + struct mmc_command cmd = {0}; err = sd_change_phase(host, sample_point, true); if (err < 0) return err; - cmd[0] = 0x40 | opcode; - err = sd_read_data(host, cmd, 0x40, NULL, 0, 100); + cmd.opcode = opcode; + err = sd_read_data(host, &cmd, 0x40, NULL, 0, 100); if (err < 0) { /* Wait till SD DATA IDLE */ sd_wait_data_idle(host); @@ -727,6 +783,12 @@ static int sd_tuning_rx(struct realtek_pci_sdmmc *host, u8 opcode) return 0; } +static inline int sdio_extblock_cmd(struct mmc_command *cmd, + struct mmc_data *data) +{ + return (cmd->opcode == SD_IO_RW_EXTENDED) && (data->blksz == 512); +} + static inline int sd_rw_cmd(struct mmc_command *cmd) { return mmc_op_multi(cmd->opcode) || @@ -748,7 +810,7 @@ static void sd_request(struct work_struct *work) unsigned int data_size = 0; int err; - if (host->eject) { + if (host->eject || !sd_get_cd_int(host)) { cmd->error = -ENOMEDIUM; goto finish; } @@ -776,17 +838,15 @@ static void sd_request(struct work_struct *work) if (mrq->data) data_size = data->blocks * data->blksz; - if (!data_size || sd_rw_cmd(cmd)) { + if (!data_size) { sd_send_cmd_get_rsp(host, cmd); + } else if (sd_rw_cmd(cmd) || sdio_extblock_cmd(cmd, data)) { + cmd->error = sd_rw_multi(host, mrq); + if (!host->using_cookie) + sdmmc_post_req(host->mmc, host->mrq, 0); - if (!cmd->error && data_size) { - sd_rw_multi(host, mrq); - if (!host->using_cookie) - sdmmc_post_req(host->mmc, host->mrq, 0); - - if (mmc_op_multi(cmd->opcode) && mrq->stop) - sd_send_cmd_get_rsp(host, mrq->stop); - } + if (mmc_op_multi(cmd->opcode) && mrq->stop) + sd_send_cmd_get_rsp(host, mrq->stop); } else { sd_normal_rw(host, mrq); } @@ -801,8 +861,10 @@ static void sd_request(struct work_struct *work) mutex_unlock(&pcr->pcr_mutex); finish: - if (cmd->error) - dev_dbg(sdmmc_dev(host), "cmd->error = %d\n", cmd->error); + if (cmd->error) { + dev_dbg(sdmmc_dev(host), "CMD %d 0x%08x error(%d)\n", + cmd->opcode, cmd->arg, cmd->error); + } mutex_lock(&host->host_mutex); host->mrq = NULL; @@ -820,7 +882,7 @@ static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq) host->mrq = mrq; mutex_unlock(&host->host_mutex); - if (sd_rw_cmd(mrq->cmd)) + if (sd_rw_cmd(mrq->cmd) || sdio_extblock_cmd(mrq->cmd, data)) host->using_cookie = sd_pre_dma_transfer(host, data, false); queue_work(host->workq, &host->work); @@ -1066,7 +1128,7 @@ static int sdmmc_get_cd(struct mmc_host *mmc) u32 val; if (host->eject) - return -ENOMEDIUM; + return cd; mutex_lock(&pcr->pcr_mutex); @@ -1317,6 +1379,7 @@ static void rtsx_pci_sdmmc_card_event(struct platform_device *pdev) { struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev); + host->cookie = -1; mmc_detect_change(host->mmc, 0); } @@ -1349,6 +1412,7 @@ static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev) host->pcr = pcr; host->mmc = mmc; host->pdev = pdev; + host->cookie = -1; host->power_state = SDMMC_POWER_OFF; INIT_WORK(&host->work, sd_request); platform_set_drvdata(pdev, host); diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index 970314e0aac8..a45ed39d062c 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -158,7 +158,7 @@ static int sdhci_acpi_emmc_probe_slot(struct platform_device *pdev, host = c->host; - /* Platform specific code during emmc proble slot goes here */ + /* Platform specific code during emmc probe slot goes here */ if (hid && uid && !strcmp(hid, "80860F14") && !strcmp(uid, "1") && sdhci_readl(host, SDHCI_CAPABILITIES) == 0x446cc8b2 && @@ -179,7 +179,7 @@ static int sdhci_acpi_sdio_probe_slot(struct platform_device *pdev, host = c->host; - /* Platform specific code during emmc proble slot goes here */ + /* Platform specific code during sdio probe slot goes here */ return 0; } @@ -195,7 +195,7 @@ static int sdhci_acpi_sd_probe_slot(struct platform_device *pdev, host = c->host; - /* Platform specific code during emmc proble slot goes here */ + /* Platform specific code during sd probe slot goes here */ return 0; } @@ -448,18 +448,13 @@ static int sdhci_acpi_runtime_resume(struct device *dev) return sdhci_runtime_resume_host(c->host); } -static int sdhci_acpi_runtime_idle(struct device *dev) -{ - return 0; -} - #endif static const struct dev_pm_ops sdhci_acpi_pm_ops = { .suspend = sdhci_acpi_suspend, .resume = sdhci_acpi_resume, SET_RUNTIME_PM_OPS(sdhci_acpi_runtime_suspend, - sdhci_acpi_runtime_resume, sdhci_acpi_runtime_idle) + sdhci_acpi_runtime_resume, NULL) }; static struct platform_driver sdhci_acpi_driver = { diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c index e7e4fbdcbfe0..34bb8f92586e 100644 --- a/drivers/mmc/host/sdhci-bcm-kona.c +++ b/drivers/mmc/host/sdhci-bcm-kona.c @@ -254,7 +254,9 @@ static int sdhci_bcm_kona_probe(struct platform_device *pdev) kona_dev = sdhci_pltfm_priv(pltfm_priv); mutex_init(&kona_dev->write_lock); - mmc_of_parse(host->mmc); + ret = mmc_of_parse(host->mmc); + if (ret) + goto err_pltfm_free; if (!host->mmc->f_max) { dev_err(&pdev->dev, "Missing max-freq for SDHCI cfg\n"); diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index af1f7c0f9545..10ef8244a239 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1080,10 +1080,10 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) goto disable_clk; pm_runtime_set_active(&pdev->dev); - pm_runtime_enable(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, 50); pm_runtime_use_autosuspend(&pdev->dev); pm_suspend_ignore_children(&pdev->dev, 1); + pm_runtime_enable(&pdev->dev); return 0; @@ -1103,16 +1103,15 @@ static int sdhci_esdhc_imx_remove(struct platform_device *pdev) struct pltfm_imx_data *imx_data = pltfm_host->priv; int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff); - sdhci_remove_host(host, dead); - - pm_runtime_dont_use_autosuspend(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); - if (!IS_ENABLED(CONFIG_PM)) { - clk_disable_unprepare(imx_data->clk_per); - clk_disable_unprepare(imx_data->clk_ipg); - clk_disable_unprepare(imx_data->clk_ahb); - } + sdhci_remove_host(host, dead); + + clk_disable_unprepare(imx_data->clk_per); + clk_disable_unprepare(imx_data->clk_ipg); + clk_disable_unprepare(imx_data->clk_ahb); sdhci_pltfm_free(pdev); diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 8872c85c63d4..17fe02ed6672 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -276,6 +276,14 @@ static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width) ESDHC_CTRL_BUSWIDTH_MASK, ctrl); } +static void esdhc_reset(struct sdhci_host *host, u8 mask) +{ + sdhci_reset(host, mask); + + sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); + sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); +} + static const struct sdhci_ops sdhci_esdhc_ops = { .read_l = esdhc_readl, .read_w = esdhc_readw, @@ -290,7 +298,7 @@ static const struct sdhci_ops sdhci_esdhc_ops = { .platform_init = esdhc_of_platform_init, .adma_workaround = esdhci_of_adma_workaround, .set_bus_width = esdhc_pltfm_set_bus_width, - .reset = sdhci_reset, + .reset = esdhc_reset, .set_uhs_signaling = sdhci_set_uhs_signaling, }; @@ -362,13 +370,19 @@ static int sdhci_esdhc_probe(struct platform_device *pdev) } /* call to generic mmc_of_parse to support additional capabilities */ - mmc_of_parse(host->mmc); + ret = mmc_of_parse(host->mmc); + if (ret) + goto err; + mmc_of_parse_voltage(np, &host->ocr_mask); ret = sdhci_add_host(host); if (ret) - sdhci_pltfm_free(pdev); + goto err; + return 0; + err: + sdhci_pltfm_free(pdev); return ret; } diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 4f38554ce679..29eaff78238e 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -1367,11 +1367,6 @@ static int sdhci_pci_runtime_resume(struct device *dev) return 0; } -static int sdhci_pci_runtime_idle(struct device *dev) -{ - return 0; -} - #else /* CONFIG_PM */ #define sdhci_pci_suspend NULL @@ -1383,7 +1378,7 @@ static const struct dev_pm_ops sdhci_pci_pm_ops = { .suspend = sdhci_pci_suspend, .resume = sdhci_pci_resume, SET_RUNTIME_PM_OPS(sdhci_pci_runtime_suspend, - sdhci_pci_runtime_resume, sdhci_pci_runtime_idle) + sdhci_pci_runtime_resume, NULL) }; /*****************************************************************************\ diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index ca3424e7ef71..b5103a247bc1 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -62,6 +62,7 @@ struct sdhci_pxa { struct clk *clk_core; struct clk *clk_io; u8 power_mode; + void __iomem *sdio3_conf_reg; }; /* @@ -72,6 +73,14 @@ struct sdhci_pxa { #define SDHCI_WINDOW_BASE(i) (0x84 + ((i) << 3)) #define SDHCI_MAX_WIN_NUM 8 +/* + * Fields below belong to SDIO3 Configuration Register (third register + * region for the Armada 38x flavor) + */ + +#define SDIO3_CONF_CLK_INV BIT(0) +#define SDIO3_CONF_SD_FB_CLK BIT(2) + static int mv_conf_mbus_windows(struct platform_device *pdev, const struct mbus_dram_target_info *dram) { @@ -118,6 +127,51 @@ static int mv_conf_mbus_windows(struct platform_device *pdev, return 0; } +static int armada_38x_quirks(struct platform_device *pdev, + struct sdhci_host *host) +{ + struct device_node *np = pdev->dev.of_node; + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_pxa *pxa = pltfm_host->priv; + struct resource *res; + + host->quirks |= SDHCI_QUIRK_MISSING_CAPS; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "conf-sdio3"); + if (res) { + pxa->sdio3_conf_reg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pxa->sdio3_conf_reg)) + return PTR_ERR(pxa->sdio3_conf_reg); + } else { + /* + * According to erratum 'FE-2946959' both SDR50 and DDR50 + * modes require specific clock adjustments in SDIO3 + * Configuration register, if the adjustment is not done, + * remove them from the capabilities. + */ + host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1); + host->caps1 &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_DDR50); + + dev_warn(&pdev->dev, "conf-sdio3 register not found: disabling SDR50 and DDR50 modes.\nConsider updating your dtb\n"); + } + + /* + * According to erratum 'ERR-7878951' Armada 38x SDHCI + * controller has different capabilities than the ones shown + * in its registers + */ + host->caps = sdhci_readl(host, SDHCI_CAPABILITIES); + if (of_property_read_bool(np, "no-1-8-v")) { + host->caps &= ~SDHCI_CAN_VDD_180; + host->mmc->caps &= ~MMC_CAP_1_8V_DDR; + } else { + host->caps &= ~SDHCI_CAN_VDD_330; + } + host->caps1 &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_USE_SDR50_TUNING); + + return 0; +} + static void pxav3_reset(struct sdhci_host *host, u8 mask) { struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc)); @@ -194,6 +248,8 @@ static void pxav3_gen_init_74_clocks(struct sdhci_host *host, u8 power_mode) static void pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) { + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_pxa *pxa = pltfm_host->priv; u16 ctrl_2; /* @@ -223,6 +279,24 @@ static void pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) break; } + /* + * Update SDIO3 Configuration register according to erratum + * FE-2946959 + */ + if (pxa->sdio3_conf_reg) { + u8 reg_val = readb(pxa->sdio3_conf_reg); + + if (uhs == MMC_TIMING_UHS_SDR50 || + uhs == MMC_TIMING_UHS_DDR50) { + reg_val &= ~SDIO3_CONF_CLK_INV; + reg_val |= SDIO3_CONF_SD_FB_CLK; + } else { + reg_val |= SDIO3_CONF_CLK_INV; + reg_val &= ~SDIO3_CONF_SD_FB_CLK; + } + writeb(reg_val, pxa->sdio3_conf_reg); + } + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); dev_dbg(mmc_dev(host->mmc), "%s uhs = %d, ctrl_2 = %04X\n", @@ -268,8 +342,8 @@ static struct sdhci_pxa_platdata *pxav3_get_mmc_pdata(struct device *dev) if (!pdata) return NULL; - of_property_read_u32(np, "mrvl,clk-delay-cycles", &clk_delay_cycles); - if (clk_delay_cycles > 0) + if (!of_property_read_u32(np, "mrvl,clk-delay-cycles", + &clk_delay_cycles)) pdata->clk_delay_cycles = clk_delay_cycles; return pdata; @@ -318,15 +392,18 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) if (!IS_ERR(pxa->clk_core)) clk_prepare_enable(pxa->clk_core); + /* enable 1/8V DDR capable */ + host->mmc->caps |= MMC_CAP_1_8V_DDR; + if (of_device_is_compatible(np, "marvell,armada-380-sdhci")) { + ret = armada_38x_quirks(pdev, host); + if (ret < 0) + goto err_clk_get; ret = mv_conf_mbus_windows(pdev, mv_mbus_dram_info()); if (ret < 0) goto err_mbus_win; } - /* enable 1/8V DDR capable */ - host->mmc->caps |= MMC_CAP_1_8V_DDR; - match = of_match_device(of_match_ptr(sdhci_pxav3_of_match), &pdev->dev); if (match) { ret = mmc_of_parse(host->mmc); @@ -365,10 +442,11 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) } } - pm_runtime_enable(&pdev->dev); - pm_runtime_get_sync(&pdev->dev); + pm_runtime_get_noresume(&pdev->dev); + pm_runtime_set_active(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, PXAV3_RPM_DELAY_MS); pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_enable(&pdev->dev); pm_suspend_ignore_children(&pdev->dev, 1); ret = sdhci_add_host(host); @@ -391,14 +469,13 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) return 0; err_add_host: - pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); err_of_parse: err_cd_req: err_mbus_win: clk_disable_unprepare(pxa->clk_io); - if (!IS_ERR(pxa->clk_core)) - clk_disable_unprepare(pxa->clk_core); + clk_disable_unprepare(pxa->clk_core); err_clk_get: sdhci_pltfm_free(pdev); return ret; @@ -411,12 +488,13 @@ static int sdhci_pxav3_remove(struct platform_device *pdev) struct sdhci_pxa *pxa = pltfm_host->priv; pm_runtime_get_sync(&pdev->dev); - sdhci_remove_host(host, 1); pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + + sdhci_remove_host(host, 1); clk_disable_unprepare(pxa->clk_io); - if (!IS_ERR(pxa->clk_core)) - clk_disable_unprepare(pxa->clk_core); + clk_disable_unprepare(pxa->clk_core); sdhci_pltfm_free(pdev); @@ -457,11 +535,11 @@ static int sdhci_pxav3_runtime_suspend(struct device *dev) struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_pxa *pxa = pltfm_host->priv; - unsigned long flags; + int ret; - spin_lock_irqsave(&host->lock, flags); - host->runtime_suspended = true; - spin_unlock_irqrestore(&host->lock, flags); + ret = sdhci_runtime_suspend_host(host); + if (ret) + return ret; clk_disable_unprepare(pxa->clk_io); if (!IS_ERR(pxa->clk_core)) @@ -475,17 +553,12 @@ static int sdhci_pxav3_runtime_resume(struct device *dev) struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_pxa *pxa = pltfm_host->priv; - unsigned long flags; clk_prepare_enable(pxa->clk_io); if (!IS_ERR(pxa->clk_core)) clk_prepare_enable(pxa->clk_core); - spin_lock_irqsave(&host->lock, flags); - host->runtime_suspended = false; - spin_unlock_irqrestore(&host->lock, flags); - - return 0; + return sdhci_runtime_resume_host(host); } #endif diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index c45b8932d843..c6d2dd7317c1 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -12,6 +12,7 @@ * published by the Free Software Foundation. */ +#include <linux/spinlock.h> #include <linux/delay.h> #include <linux/dma-mapping.h> #include <linux/platform_device.h> @@ -312,7 +313,14 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock) sdhci_s3c_set_clock(host, clock); + /* Reset SD Clock Enable */ + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + clk &= ~SDHCI_CLOCK_CARD_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + + spin_unlock_irq(&host->lock); ret = clk_set_rate(ourhost->clk_bus[ourhost->cur_clk], clock); + spin_lock_irq(&host->lock); if (ret != 0) { dev_err(dev, "%s: failed to set clock rate %uHz\n", mmc_hostname(host->mmc), clock); @@ -607,7 +615,9 @@ static int sdhci_s3c_probe(struct platform_device *pdev) pm_runtime_use_autosuspend(&pdev->dev); pm_suspend_ignore_children(&pdev->dev, 1); - mmc_of_parse(host->mmc); + ret = mmc_of_parse(host->mmc); + if (ret) + goto err_req_regs; ret = sdhci_add_host(host); if (ret) { diff --git a/drivers/mmc/host/sdhci-sirf.c b/drivers/mmc/host/sdhci-sirf.c index dd29d47c07aa..f6f82ec3618d 100644 --- a/drivers/mmc/host/sdhci-sirf.c +++ b/drivers/mmc/host/sdhci-sirf.c @@ -15,7 +15,9 @@ #include <linux/mmc/slot-gpio.h> #include "sdhci-pltfm.h" +#define SDHCI_CLK_DELAY_SETTING 0x4C #define SDHCI_SIRF_8BITBUS BIT(3) +#define SIRF_TUNING_COUNT 128 struct sdhci_sirf_priv { struct clk *clk; @@ -49,7 +51,76 @@ static void sdhci_sirf_set_bus_width(struct sdhci_host *host, int width) sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); } +static int sdhci_sirf_execute_tuning(struct sdhci_host *host, u32 opcode) +{ + int tuning_seq_cnt = 3; + u8 phase, tuned_phases[SIRF_TUNING_COUNT]; + u8 tuned_phase_cnt = 0; + int rc, longest_range = 0; + int start = -1, end = 0, tuning_value = -1, range = 0; + u16 clock_setting; + struct mmc_host *mmc = host->mmc; + + clock_setting = sdhci_readw(host, SDHCI_CLK_DELAY_SETTING); + clock_setting &= ~0x3fff; + +retry: + phase = 0; + do { + sdhci_writel(host, + clock_setting | phase | (phase << 7) | (phase << 16), + SDHCI_CLK_DELAY_SETTING); + + if (!mmc_send_tuning(mmc)) { + /* Tuning is successful at this tuning point */ + tuned_phases[tuned_phase_cnt++] = phase; + dev_dbg(mmc_dev(mmc), "%s: Found good phase = %d\n", + mmc_hostname(mmc), phase); + if (start == -1) + start = phase; + end = phase; + range++; + if (phase == (SIRF_TUNING_COUNT - 1) + && range > longest_range) + tuning_value = (start + end) / 2; + } else { + dev_dbg(mmc_dev(mmc), "%s: Found bad phase = %d\n", + mmc_hostname(mmc), phase); + if (range > longest_range) { + tuning_value = (start + end) / 2; + longest_range = range; + } + start = -1; + end = range = 0; + } + } while (++phase < ARRAY_SIZE(tuned_phases)); + + if (tuned_phase_cnt && tuning_value > 0) { + /* + * Finally set the selected phase in delay + * line hw block. + */ + phase = tuning_value; + sdhci_writel(host, + clock_setting | phase | (phase << 7) | (phase << 16), + SDHCI_CLK_DELAY_SETTING); + + dev_dbg(mmc_dev(mmc), "%s: Setting the tuning phase to %d\n", + mmc_hostname(mmc), phase); + } else { + if (--tuning_seq_cnt) + goto retry; + /* Tuning failed */ + dev_dbg(mmc_dev(mmc), "%s: No tuning point found\n", + mmc_hostname(mmc)); + rc = -EIO; + } + + return rc; +} + static struct sdhci_ops sdhci_sirf_ops = { + .platform_execute_tuning = sdhci_sirf_execute_tuning, .set_clock = sdhci_set_clock, .get_max_clock = sdhci_sirf_get_max_clk, .set_bus_width = sdhci_sirf_set_bus_width, @@ -138,9 +209,6 @@ static int sdhci_sirf_remove(struct platform_device *pdev) sdhci_pltfm_unregister(pdev); - if (gpio_is_valid(priv->gpio_cd)) - mmc_gpio_free_cd(host->mmc); - clk_disable_unprepare(priv->clk); return 0; } diff --git a/drivers/mmc/host/sdhci-st.c b/drivers/mmc/host/sdhci-st.c index 328f348c7243..882b07e9667e 100644 --- a/drivers/mmc/host/sdhci-st.c +++ b/drivers/mmc/host/sdhci-st.c @@ -78,10 +78,9 @@ static int sdhci_st_probe(struct platform_device *pdev) } ret = mmc_of_parse(host->mmc); - if (ret) { dev_err(&pdev->dev, "Failed mmc_of_parse\n"); - return ret; + goto err_of; } clk_prepare_enable(clk); @@ -108,6 +107,7 @@ static int sdhci_st_probe(struct platform_device *pdev) err_out: clk_disable_unprepare(clk); +err_of: sdhci_pltfm_free(pdev); return ret; diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index 59797106af93..f3778d58d1cd 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -41,6 +41,7 @@ #define NVQUIRK_DISABLE_SDR50 BIT(3) #define NVQUIRK_DISABLE_SDR104 BIT(4) #define NVQUIRK_DISABLE_DDR50 BIT(5) +#define NVQUIRK_SHADOW_XFER_MODE_REG BIT(6) struct sdhci_tegra_soc_data { const struct sdhci_pltfm_data *pdata; @@ -67,6 +68,31 @@ static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg) return readw(host->ioaddr + reg); } +static void tegra_sdhci_writew(struct sdhci_host *host, u16 val, int reg) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_tegra *tegra_host = pltfm_host->priv; + const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; + + if (soc_data->nvquirks & NVQUIRK_SHADOW_XFER_MODE_REG) { + switch (reg) { + case SDHCI_TRANSFER_MODE: + /* + * Postpone this write, we must do it together with a + * command write that is down below. + */ + pltfm_host->xfer_mode_shadow = val; + return; + case SDHCI_COMMAND: + writel((val << 16) | pltfm_host->xfer_mode_shadow, + host->ioaddr + SDHCI_TRANSFER_MODE); + return; + } + } + + writew(val, host->ioaddr + reg); +} + static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -147,6 +173,7 @@ static void tegra_sdhci_set_bus_width(struct sdhci_host *host, int bus_width) static const struct sdhci_ops tegra_sdhci_ops = { .get_ro = tegra_sdhci_get_ro, .read_w = tegra_sdhci_readw, + .write_w = tegra_sdhci_writew, .write_l = tegra_sdhci_writel, .set_clock = sdhci_set_clock, .set_bus_width = tegra_sdhci_set_bus_width, @@ -201,7 +228,8 @@ static struct sdhci_tegra_soc_data soc_data_tegra114 = { .pdata = &sdhci_tegra114_pdata, .nvquirks = NVQUIRK_DISABLE_SDR50 | NVQUIRK_DISABLE_DDR50 | - NVQUIRK_DISABLE_SDR104, + NVQUIRK_DISABLE_SDR104 | + NVQUIRK_SHADOW_XFER_MODE_REG, }; static const struct of_device_id sdhci_tegra_dt_match[] = { diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index f1a488ee432f..0ad412a4876f 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -53,6 +53,9 @@ static void sdhci_finish_command(struct sdhci_host *); static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode); static void sdhci_tuning_timer(unsigned long data); static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable); +static int sdhci_pre_dma_transfer(struct sdhci_host *host, + struct mmc_data *data, + struct sdhci_host_next *next); #ifdef CONFIG_PM static int sdhci_runtime_pm_get(struct sdhci_host *host); @@ -505,9 +508,8 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, goto fail; BUG_ON(host->align_addr & host->align_mask); - host->sg_count = dma_map_sg(mmc_dev(host->mmc), - data->sg, data->sg_len, direction); - if (host->sg_count == 0) + host->sg_count = sdhci_pre_dma_transfer(host, data, NULL); + if (host->sg_count < 0) goto unmap_align; desc = host->adma_table; @@ -531,8 +533,6 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, if (offset) { if (data->flags & MMC_DATA_WRITE) { buffer = sdhci_kmap_atomic(sg, &flags); - WARN_ON(((long)buffer & (PAGE_SIZE - 1)) > - (PAGE_SIZE - offset)); memcpy(align, buffer, offset); sdhci_kunmap_atomic(buffer, &flags); } @@ -639,8 +639,6 @@ static void sdhci_adma_table_post(struct sdhci_host *host, (sg_dma_address(sg) & host->align_mask); buffer = sdhci_kmap_atomic(sg, &flags); - WARN_ON(((long)buffer & (PAGE_SIZE - 1)) > - (PAGE_SIZE - size)); memcpy(buffer, align, size); sdhci_kunmap_atomic(buffer, &flags); @@ -649,8 +647,9 @@ static void sdhci_adma_table_post(struct sdhci_host *host, } } - dma_unmap_sg(mmc_dev(host->mmc), data->sg, - data->sg_len, direction); + if (!data->host_cookie) + dma_unmap_sg(mmc_dev(host->mmc), data->sg, + data->sg_len, direction); } static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd) @@ -846,11 +845,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) } else { int sg_cnt; - sg_cnt = dma_map_sg(mmc_dev(host->mmc), - data->sg, data->sg_len, - (data->flags & MMC_DATA_READ) ? - DMA_FROM_DEVICE : - DMA_TO_DEVICE); + sg_cnt = sdhci_pre_dma_transfer(host, data, NULL); if (sg_cnt == 0) { /* * This only happens when someone fed @@ -909,7 +904,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) static void sdhci_set_transfer_mode(struct sdhci_host *host, struct mmc_command *cmd) { - u16 mode; + u16 mode = 0; struct mmc_data *data = cmd->data; if (data == NULL) { @@ -927,9 +922,11 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host, WARN_ON(!host->data); - mode = SDHCI_TRNS_BLK_CNT_EN; + if (!(host->quirks2 & SDHCI_QUIRK2_SUPPORT_SINGLE)) + mode = SDHCI_TRNS_BLK_CNT_EN; + if (mmc_op_multi(cmd->opcode) || data->blocks > 1) { - mode |= SDHCI_TRNS_MULTI; + mode = SDHCI_TRNS_BLK_CNT_EN | SDHCI_TRNS_MULTI; /* * If we are sending CMD23, CMD12 never gets sent * on successful completion (so no Auto-CMD12). @@ -963,8 +960,10 @@ static void sdhci_finish_data(struct sdhci_host *host) if (host->flags & SDHCI_USE_ADMA) sdhci_adma_table_post(host, data); else { - dma_unmap_sg(mmc_dev(host->mmc), data->sg, - data->sg_len, (data->flags & MMC_DATA_READ) ? + if (!data->host_cookie) + dma_unmap_sg(mmc_dev(host->mmc), + data->sg, data->sg_len, + (data->flags & MMC_DATA_READ) ? DMA_FROM_DEVICE : DMA_TO_DEVICE); } } @@ -1630,7 +1629,7 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) * signalling timeout and CRC errors even on CMD0. Resetting * it on each ios seems to solve the problem. */ - if(host->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS) + if (host->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS) sdhci_do_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); mmiowb(); @@ -1832,6 +1831,10 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, ctrl |= SDHCI_CTRL_VDD_180; sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); + /* Some controller need to do more when switching */ + if (host->ops->voltage_switch) + host->ops->voltage_switch(host); + /* 1.8V regulator output should be stable within 5 ms */ ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); if (ctrl & SDHCI_CTRL_VDD_180) @@ -1960,6 +1963,8 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); ctrl |= SDHCI_CTRL_EXEC_TUNING; + if (host->quirks2 & SDHCI_QUIRK2_TUNING_WORK_AROUND) + ctrl |= SDHCI_CTRL_TUNED_CLK; sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); /* @@ -2129,6 +2134,77 @@ static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable) } } +static void sdhci_post_req(struct mmc_host *mmc, struct mmc_request *mrq, + int err) +{ + struct sdhci_host *host = mmc_priv(mmc); + struct mmc_data *data = mrq->data; + + if (host->flags & SDHCI_REQ_USE_DMA) { + if (data->host_cookie) + dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, + data->flags & MMC_DATA_WRITE ? + DMA_TO_DEVICE : DMA_FROM_DEVICE); + mrq->data->host_cookie = 0; + } +} + +static int sdhci_pre_dma_transfer(struct sdhci_host *host, + struct mmc_data *data, + struct sdhci_host_next *next) +{ + int sg_count; + + if (!next && data->host_cookie && + data->host_cookie != host->next_data.cookie) { + pr_debug(DRIVER_NAME "[%s] invalid cookie: %d, next-cookie %d\n", + __func__, data->host_cookie, host->next_data.cookie); + data->host_cookie = 0; + } + + /* Check if next job is already prepared */ + if (next || + (!next && data->host_cookie != host->next_data.cookie)) { + sg_count = dma_map_sg(mmc_dev(host->mmc), data->sg, + data->sg_len, + data->flags & MMC_DATA_WRITE ? + DMA_TO_DEVICE : DMA_FROM_DEVICE); + + } else { + sg_count = host->next_data.sg_count; + host->next_data.sg_count = 0; + } + + + if (sg_count == 0) + return -EINVAL; + + if (next) { + next->sg_count = sg_count; + data->host_cookie = ++next->cookie < 0 ? 1 : next->cookie; + } else + host->sg_count = sg_count; + + return sg_count; +} + +static void sdhci_pre_req(struct mmc_host *mmc, struct mmc_request *mrq, + bool is_first_req) +{ + struct sdhci_host *host = mmc_priv(mmc); + + if (mrq->data->host_cookie) { + mrq->data->host_cookie = 0; + return; + } + + if (host->flags & SDHCI_REQ_USE_DMA) + if (sdhci_pre_dma_transfer(host, + mrq->data, + &host->next_data) < 0) + mrq->data->host_cookie = 0; +} + static void sdhci_card_event(struct mmc_host *mmc) { struct sdhci_host *host = mmc_priv(mmc); @@ -2162,6 +2238,8 @@ static void sdhci_card_event(struct mmc_host *mmc) static const struct mmc_host_ops sdhci_ops = { .request = sdhci_request, + .post_req = sdhci_post_req, + .pre_req = sdhci_pre_req, .set_ios = sdhci_set_ios, .get_cd = sdhci_get_cd, .get_ro = sdhci_get_ro, @@ -2793,9 +2871,9 @@ int sdhci_runtime_resume_host(struct sdhci_host *host) /* Force clock and power re-program */ host->pwr = 0; host->clock = 0; + sdhci_do_start_signal_voltage_switch(host, &host->mmc->ios); sdhci_do_set_ios(host, &host->mmc->ios); - sdhci_do_start_signal_voltage_switch(host, &host->mmc->ios); if ((host_flags & SDHCI_PV_ENABLED) && !(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN)) { spin_lock_irqsave(&host->lock, flags); @@ -3019,6 +3097,7 @@ int sdhci_add_host(struct sdhci_host *host) host->max_clk = host->ops->get_max_clock(host); } + host->next_data.cookie = 1; /* * In case of Host Controller v3.00, find out whether clock * multiplier is supported. @@ -3338,9 +3417,9 @@ int sdhci_add_host(struct sdhci_host *host) setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host); - if (host->version >= SDHCI_SPEC_300) { - init_waitqueue_head(&host->buf_ready_int); + init_waitqueue_head(&host->buf_ready_int); + if (host->version >= SDHCI_SPEC_300) { /* Initialize re-tuning timer */ init_timer(&host->tuning_timer); host->tuning_timer.data = (unsigned long)host; diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 41a2c34299ed..0315e1844330 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -339,6 +339,7 @@ struct sdhci_ops { void (*adma_workaround)(struct sdhci_host *host, u32 intmask); void (*platform_init)(struct sdhci_host *host); void (*card_event)(struct sdhci_host *host); + void (*voltage_switch)(struct sdhci_host *host); }; #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS diff --git a/drivers/mmc/host/sdhci_f_sdh30.c b/drivers/mmc/host/sdhci_f_sdh30.c new file mode 100644 index 000000000000..2fe8b91481b3 --- /dev/null +++ b/drivers/mmc/host/sdhci_f_sdh30.c @@ -0,0 +1,237 @@ +/* + * linux/drivers/mmc/host/sdhci_f_sdh30.c + * + * Copyright (C) 2013 - 2015 Fujitsu Semiconductor, Ltd + * Vincent Yang <vincent.yang@tw.fujitsu.com> + * Copyright (C) 2015 Linaro Ltd Andy Green <andy.green@linaro.org> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + */ + +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/clk.h> + +#include "sdhci-pltfm.h" + +/* F_SDH30 extended Controller registers */ +#define F_SDH30_AHB_CONFIG 0x100 +#define F_SDH30_AHB_BIGED 0x00000040 +#define F_SDH30_BUSLOCK_DMA 0x00000020 +#define F_SDH30_BUSLOCK_EN 0x00000010 +#define F_SDH30_SIN 0x00000008 +#define F_SDH30_AHB_INCR_16 0x00000004 +#define F_SDH30_AHB_INCR_8 0x00000002 +#define F_SDH30_AHB_INCR_4 0x00000001 + +#define F_SDH30_TUNING_SETTING 0x108 +#define F_SDH30_CMD_CHK_DIS 0x00010000 + +#define F_SDH30_IO_CONTROL2 0x114 +#define F_SDH30_CRES_O_DN 0x00080000 +#define F_SDH30_MSEL_O_1_8 0x00040000 + +#define F_SDH30_ESD_CONTROL 0x124 +#define F_SDH30_EMMC_RST 0x00000002 +#define F_SDH30_EMMC_HS200 0x01000000 + +#define F_SDH30_CMD_DAT_DELAY 0x200 + +#define F_SDH30_MIN_CLOCK 400000 + +struct f_sdhost_priv { + struct clk *clk_iface; + struct clk *clk; + u32 vendor_hs200; + struct device *dev; +}; + +void sdhci_f_sdh30_soft_voltage_switch(struct sdhci_host *host) +{ + struct f_sdhost_priv *priv = sdhci_priv(host); + u32 ctrl = 0; + + usleep_range(2500, 3000); + ctrl = sdhci_readl(host, F_SDH30_IO_CONTROL2); + ctrl |= F_SDH30_CRES_O_DN; + sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2); + ctrl |= F_SDH30_MSEL_O_1_8; + sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2); + + ctrl &= ~F_SDH30_CRES_O_DN; + sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2); + usleep_range(2500, 3000); + + if (priv->vendor_hs200) { + dev_info(priv->dev, "%s: setting hs200\n", __func__); + ctrl = sdhci_readl(host, F_SDH30_ESD_CONTROL); + ctrl |= priv->vendor_hs200; + sdhci_writel(host, ctrl, F_SDH30_ESD_CONTROL); + } + + ctrl = sdhci_readl(host, F_SDH30_TUNING_SETTING); + ctrl |= F_SDH30_CMD_CHK_DIS; + sdhci_writel(host, ctrl, F_SDH30_TUNING_SETTING); +} + +unsigned int sdhci_f_sdh30_get_min_clock(struct sdhci_host *host) +{ + return F_SDH30_MIN_CLOCK; +} + +void sdhci_f_sdh30_reset(struct sdhci_host *host, u8 mask) +{ + if (sdhci_readw(host, SDHCI_CLOCK_CONTROL) == 0) + sdhci_writew(host, 0xBC01, SDHCI_CLOCK_CONTROL); + + sdhci_reset(host, mask); +} + +static const struct sdhci_ops sdhci_f_sdh30_ops = { + .voltage_switch = sdhci_f_sdh30_soft_voltage_switch, + .get_min_clock = sdhci_f_sdh30_get_min_clock, + .reset = sdhci_f_sdh30_reset, + .set_clock = sdhci_set_clock, + .set_bus_width = sdhci_set_bus_width, + .set_uhs_signaling = sdhci_set_uhs_signaling, +}; + +static int sdhci_f_sdh30_probe(struct platform_device *pdev) +{ + struct sdhci_host *host; + struct device *dev = &pdev->dev; + struct resource *res; + int irq, ctrl = 0, ret = 0; + struct f_sdhost_priv *priv; + u32 reg = 0; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev, "%s: no irq specified\n", __func__); + return irq; + } + + host = sdhci_alloc_host(dev, sizeof(struct sdhci_host) + + sizeof(struct f_sdhost_priv)); + if (IS_ERR(host)) + return PTR_ERR(host); + + priv = sdhci_priv(host); + priv->dev = dev; + + host->quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | + SDHCI_QUIRK_INVERTED_WRITE_PROTECT; + host->quirks2 = SDHCI_QUIRK2_SUPPORT_SINGLE | + SDHCI_QUIRK2_TUNING_WORK_AROUND; + + ret = mmc_of_parse(host->mmc); + if (ret) + goto err; + + platform_set_drvdata(pdev, host); + + sdhci_get_of_property(pdev); + host->hw_name = "f_sdh30"; + host->ops = &sdhci_f_sdh30_ops; + host->irq = irq; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + host->ioaddr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(host->ioaddr)) { + ret = PTR_ERR(host->ioaddr); + goto err; + } + + priv->clk_iface = devm_clk_get(&pdev->dev, "iface"); + if (IS_ERR(priv->clk_iface)) { + ret = PTR_ERR(priv->clk_iface); + goto err; + } + + ret = clk_prepare_enable(priv->clk_iface); + if (ret) + goto err; + + priv->clk = devm_clk_get(&pdev->dev, "core"); + if (IS_ERR(priv->clk)) { + ret = PTR_ERR(priv->clk); + goto err_clk; + } + + ret = clk_prepare_enable(priv->clk); + if (ret) + goto err_clk; + + /* init vendor specific regs */ + ctrl = sdhci_readw(host, F_SDH30_AHB_CONFIG); + ctrl |= F_SDH30_SIN | F_SDH30_AHB_INCR_16 | F_SDH30_AHB_INCR_8 | + F_SDH30_AHB_INCR_4; + ctrl &= ~(F_SDH30_AHB_BIGED | F_SDH30_BUSLOCK_EN); + sdhci_writew(host, ctrl, F_SDH30_AHB_CONFIG); + + reg = sdhci_readl(host, F_SDH30_ESD_CONTROL); + sdhci_writel(host, reg & ~F_SDH30_EMMC_RST, F_SDH30_ESD_CONTROL); + msleep(20); + sdhci_writel(host, reg | F_SDH30_EMMC_RST, F_SDH30_ESD_CONTROL); + + reg = sdhci_readl(host, SDHCI_CAPABILITIES); + if (reg & SDHCI_CAN_DO_8BIT) + priv->vendor_hs200 = F_SDH30_EMMC_HS200; + + ret = sdhci_add_host(host); + if (ret) + goto err_add_host; + + return 0; + +err_add_host: + clk_disable_unprepare(priv->clk); +err_clk: + clk_disable_unprepare(priv->clk_iface); +err: + sdhci_free_host(host); + return ret; +} + +static int sdhci_f_sdh30_remove(struct platform_device *pdev) +{ + struct sdhci_host *host = platform_get_drvdata(pdev); + struct f_sdhost_priv *priv = sdhci_priv(host); + + sdhci_remove_host(host, readl(host->ioaddr + SDHCI_INT_STATUS) == + 0xffffffff); + + clk_disable_unprepare(priv->clk_iface); + clk_disable_unprepare(priv->clk); + + sdhci_free_host(host); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static const struct of_device_id f_sdh30_dt_ids[] = { + { .compatible = "fujitsu,mb86s70-sdhci-3.0" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, f_sdh30_dt_ids); + +static struct platform_driver sdhci_f_sdh30_driver = { + .driver = { + .name = "f_sdh30", + .of_match_table = f_sdh30_dt_ids, + .pm = SDHCI_PLTFM_PMOPS, + }, + .probe = sdhci_f_sdh30_probe, + .remove = sdhci_f_sdh30_remove, +}; + +module_platform_driver(sdhci_f_sdh30_driver); + +MODULE_DESCRIPTION("F_SDH30 SD Card Controller driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("FUJITSU SEMICONDUCTOR LTD."); +MODULE_ALIAS("platform:f_sdh30"); diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index 00c8ebdf8ec7..6906a905cd54 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -35,10 +35,13 @@ #define EXT_ACC 0xe4 +#define host_to_priv(host) container_of((host)->pdata, struct sh_mobile_sdhi, mmc_data) + struct sh_mobile_sdhi_of_data { unsigned long tmio_flags; unsigned long capabilities; unsigned long capabilities2; + enum dma_slave_buswidth dma_buswidth; dma_addr_t dma_rx_offset; }; @@ -58,6 +61,7 @@ static const struct sh_mobile_sdhi_of_data of_rcar_gen2_compatible = { .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE | TMIO_MMC_CLK_ACTUAL, .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ, + .dma_buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES, .dma_rx_offset = 0x2000, }; @@ -84,16 +88,43 @@ struct sh_mobile_sdhi { struct tmio_mmc_dma dma_priv; }; +static void sh_mobile_sdhi_sdbuf_width(struct tmio_mmc_host *host, int width) +{ + u32 val; + + /* + * see also + * sh_mobile_sdhi_of_data :: dma_buswidth + */ + switch (sd_ctrl_read16(host, CTL_VERSION)) { + case 0x490C: + val = (width == 32) ? 0x0001 : 0x0000; + break; + case 0xCB0D: + val = (width == 32) ? 0x0000 : 0x0001; + break; + default: + /* nothing to do */ + return; + } + + sd_ctrl_write16(host, EXT_ACC, val); +} + static int sh_mobile_sdhi_clk_enable(struct platform_device *pdev, unsigned int *f) { struct mmc_host *mmc = platform_get_drvdata(pdev); struct tmio_mmc_host *host = mmc_priv(mmc); - struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data); + struct sh_mobile_sdhi *priv = host_to_priv(host); int ret = clk_prepare_enable(priv->clk); if (ret < 0) return ret; *f = clk_get_rate(priv->clk); + + /* enable 16bit data access on SDBUF as default */ + sh_mobile_sdhi_sdbuf_width(host, 16); + return 0; } @@ -101,7 +132,7 @@ static void sh_mobile_sdhi_clk_disable(struct platform_device *pdev) { struct mmc_host *mmc = platform_get_drvdata(pdev); struct tmio_mmc_host *host = mmc_priv(mmc); - struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data); + struct sh_mobile_sdhi *priv = host_to_priv(host); clk_disable_unprepare(priv->clk); } @@ -113,7 +144,7 @@ static int sh_mobile_sdhi_wait_idle(struct tmio_mmc_host *host) udelay(1); if (!timeout) { - dev_warn(host->pdata->dev, "timeout waiting for SD bus idle\n"); + dev_warn(&host->pdev->dev, "timeout waiting for SD bus idle\n"); return -EBUSY; } @@ -156,14 +187,13 @@ static int sh_mobile_sdhi_multi_io_quirk(struct mmc_card *card, return blk_size; } -static void sh_mobile_sdhi_cd_wakeup(const struct platform_device *pdev) +static void sh_mobile_sdhi_enable_dma(struct tmio_mmc_host *host, bool enable) { - mmc_detect_change(platform_get_drvdata(pdev), msecs_to_jiffies(100)); -} + sd_ctrl_write16(host, CTL_DMA_ENABLE, enable ? 2 : 0); -static const struct sh_mobile_sdhi_ops sdhi_ops = { - .cd_wakeup = sh_mobile_sdhi_cd_wakeup, -}; + /* enable 32bit access if DMA mode if possibile */ + sh_mobile_sdhi_sdbuf_width(host, enable ? 32 : 16); +} static int sh_mobile_sdhi_probe(struct platform_device *pdev) { @@ -177,7 +207,6 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) int irq, ret, i = 0; bool multiplexed_isr = true; struct tmio_mmc_dma *dma_priv; - u16 ver; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) @@ -192,26 +221,31 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) mmc_data = &priv->mmc_data; dma_priv = &priv->dma_priv; - if (p) { - if (p->init) { - ret = p->init(pdev, &sdhi_ops); - if (ret) - return ret; - } - } - priv->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(priv->clk)) { ret = PTR_ERR(priv->clk); dev_err(&pdev->dev, "cannot get clock: %d\n", ret); - goto eclkget; + goto eprobe; } - mmc_data->clk_enable = sh_mobile_sdhi_clk_enable; - mmc_data->clk_disable = sh_mobile_sdhi_clk_disable; + host = tmio_mmc_host_alloc(pdev); + if (!host) { + ret = -ENOMEM; + goto eprobe; + } + + host->dma = dma_priv; + host->write16_hook = sh_mobile_sdhi_write16_hook; + host->clk_enable = sh_mobile_sdhi_clk_enable; + host->clk_disable = sh_mobile_sdhi_clk_disable; + host->multi_io_quirk = sh_mobile_sdhi_multi_io_quirk; + /* SD control register space size is 0x100, 0x200 for bus_shift=1 */ + if (resource_size(res) > 0x100) + host->bus_shift = 1; + else + host->bus_shift = 0; + mmc_data->capabilities = MMC_CAP_MMC_HIGHSPEED; - mmc_data->write16_hook = sh_mobile_sdhi_write16_hook; - mmc_data->multi_io_quirk = sh_mobile_sdhi_multi_io_quirk; if (p) { mmc_data->flags = p->tmio_flags; mmc_data->ocr_mask = p->tmio_ocr_mask; @@ -231,11 +265,10 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) dma_priv->slave_id_rx = p->dma_slave_rx; } } - - dma_priv->alignment_shift = 1; /* 2-byte alignment */ dma_priv->filter = shdma_chan_filter; + dma_priv->enable = sh_mobile_sdhi_enable_dma; - mmc_data->dma = dma_priv; + mmc_data->alignment_shift = 1; /* 2-byte alignment */ /* * All SDHI blocks support 2-byte and larger block sizes in 4-bit @@ -258,33 +291,18 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) */ mmc_data->flags |= TMIO_MMC_SDIO_STATUS_QUIRK; - /* - * All SDHI have DMA control register - */ - mmc_data->flags |= TMIO_MMC_HAVE_CTL_DMA_REG; - if (of_id && of_id->data) { const struct sh_mobile_sdhi_of_data *of_data = of_id->data; mmc_data->flags |= of_data->tmio_flags; mmc_data->capabilities |= of_data->capabilities; mmc_data->capabilities2 |= of_data->capabilities2; - dma_priv->dma_rx_offset = of_data->dma_rx_offset; + mmc_data->dma_rx_offset = of_data->dma_rx_offset; + dma_priv->dma_buswidth = of_data->dma_buswidth; } - /* SD control register space size is 0x100, 0x200 for bus_shift=1 */ - mmc_data->bus_shift = resource_size(res) >> 9; - - ret = tmio_mmc_host_probe(&host, pdev, mmc_data); + ret = tmio_mmc_host_probe(host, mmc_data); if (ret < 0) - goto eprobe; - - /* - * FIXME: - * this Workaround can be more clever method - */ - ver = sd_ctrl_read16(host, CTL_VERSION); - if (ver == 0xCB0D) - sd_ctrl_write16(host, EXT_ACC, 1); + goto efree; /* * Allow one or more specific (named) ISRs or @@ -351,10 +369,9 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) eirq: tmio_mmc_host_remove(host); +efree: + tmio_mmc_host_free(host); eprobe: -eclkget: - if (p && p->cleanup) - p->cleanup(pdev); return ret; } @@ -362,13 +379,9 @@ static int sh_mobile_sdhi_remove(struct platform_device *pdev) { struct mmc_host *mmc = platform_get_drvdata(pdev); struct tmio_mmc_host *host = mmc_priv(mmc); - struct sh_mobile_sdhi_info *p = pdev->dev.platform_data; tmio_mmc_host_remove(host); - if (p && p->cleanup) - p->cleanup(pdev); - return 0; } diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index 15cb8b7ffc34..6af0a28ba37d 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -252,7 +252,7 @@ static int sunxi_mmc_reset_host(struct sunxi_mmc_host *host) unsigned long expire = jiffies + msecs_to_jiffies(250); u32 rval; - mmc_writel(host, REG_CMDR, SDXC_HARDWARE_RESET); + mmc_writel(host, REG_GCTRL, SDXC_HARDWARE_RESET); do { rval = mmc_readl(host, REG_GCTRL); } while (time_before(jiffies, expire) && (rval & SDXC_HARDWARE_RESET)); @@ -310,7 +310,9 @@ static void sunxi_mmc_init_idma_des(struct sunxi_mmc_host *host, } pdes[0].config |= SDXC_IDMAC_DES0_FD; - pdes[i - 1].config = SDXC_IDMAC_DES0_OWN | SDXC_IDMAC_DES0_LD; + pdes[i - 1].config |= SDXC_IDMAC_DES0_LD | SDXC_IDMAC_DES0_ER; + pdes[i - 1].config &= ~SDXC_IDMAC_DES0_DIC; + pdes[i - 1].buf_addr_ptr2 = 0; /* * Avoid the io-store starting the idmac hitting io-mem before the @@ -570,6 +572,15 @@ static irqreturn_t sunxi_mmc_handle_manual_stop(int irq, void *dev_id) } dev_err(mmc_dev(host->mmc), "data error, sending stop command\n"); + + /* + * We will never have more than one outstanding request, + * and we do not complete the request until after + * we've cleared host->manual_stop_mrq so we do not need to + * spin lock this function. + * Additionally we have wait states within this function + * so having it in a lock is a very bad idea. + */ sunxi_mmc_send_manual_stop(host, mrq); spin_lock_irqsave(&host->lock, iflags); @@ -616,7 +627,7 @@ static int sunxi_mmc_oclk_onoff(struct sunxi_mmc_host *host, u32 oclk_en) static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host, struct mmc_ios *ios) { - u32 rate, oclk_dly, rval, sclk_dly, src_clk; + u32 rate, oclk_dly, rval, sclk_dly; int ret; rate = clk_round_rate(host->clk_mmc, ios->clock); @@ -661,14 +672,6 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host, sclk_dly = 4; } - src_clk = clk_get_rate(clk_get_parent(host->clk_mmc)); - if (src_clk >= 300000000 && src_clk <= 400000000) { - if (oclk_dly) - oclk_dly--; - if (sclk_dly) - sclk_dly--; - } - clk_sunxi_mmc_phase_control(host->clk_mmc, sclk_dly, oclk_dly); return sunxi_mmc_oclk_onoff(host, 1); @@ -766,6 +769,7 @@ static void sunxi_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) unsigned long iflags; u32 imask = SDXC_INTERRUPT_ERROR_BIT; u32 cmd_val = SDXC_START | (cmd->opcode & 0x3f); + bool wait_dma = host->wait_dma; int ret; /* Check for set_ios errors (should never happen) */ @@ -816,7 +820,7 @@ static void sunxi_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) if (cmd->data->flags & MMC_DATA_WRITE) cmd_val |= SDXC_WRITE; else - host->wait_dma = true; + wait_dma = true; } else { imask |= SDXC_COMMAND_DONE; } @@ -850,6 +854,7 @@ static void sunxi_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) } host->mrq = mrq; + host->wait_dma = wait_dma; mmc_writel(host, REG_IMASK, host->sdio_imask | imask); mmc_writel(host, REG_CARG, cmd->arg); mmc_writel(host, REG_CMDR, cmd_val); diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c index 2ca0afaab792..f746df493892 100644 --- a/drivers/mmc/host/tmio_mmc.c +++ b/drivers/mmc/host/tmio_mmc.c @@ -88,14 +88,19 @@ static int tmio_mmc_probe(struct platform_device *pdev) if (!res) return -EINVAL; - /* SD control register space size is 0x200, 0x400 for bus_shift=1 */ - pdata->bus_shift = resource_size(res) >> 10; pdata->flags |= TMIO_MMC_HAVE_HIGH_REG; - ret = tmio_mmc_host_probe(&host, pdev, pdata); - if (ret) + host = tmio_mmc_host_alloc(pdev); + if (!host) goto cell_disable; + /* SD control register space size is 0x200, 0x400 for bus_shift=1 */ + host->bus_shift = resource_size(res) >> 10; + + ret = tmio_mmc_host_probe(host, pdata); + if (ret) + goto host_free; + ret = request_irq(irq, tmio_mmc_irq, IRQF_TRIGGER_FALLING, dev_name(&pdev->dev), host); if (ret) @@ -108,6 +113,8 @@ static int tmio_mmc_probe(struct platform_device *pdev) host_remove: tmio_mmc_host_remove(host); +host_free: + tmio_mmc_host_free(host); cell_disable: if (cell->disable) cell->disable(pdev); diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index a34ecbe1c1ad..fc3805ed69d1 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -16,6 +16,7 @@ #ifndef TMIO_MMC_H #define TMIO_MMC_H +#include <linux/dmaengine.h> #include <linux/highmem.h> #include <linux/mmc/tmio.h> #include <linux/mutex.h> @@ -39,6 +40,17 @@ #define TMIO_MASK_IRQ (TMIO_MASK_READOP | TMIO_MASK_WRITEOP | TMIO_MASK_CMD) struct tmio_mmc_data; +struct tmio_mmc_host; + +struct tmio_mmc_dma { + void *chan_priv_tx; + void *chan_priv_rx; + int slave_id_tx; + int slave_id_rx; + enum dma_slave_buswidth dma_buswidth; + bool (*filter)(struct dma_chan *chan, void *arg); + void (*enable)(struct tmio_mmc_host *host, bool enable); +}; struct tmio_mmc_host { void __iomem *ctl; @@ -56,9 +68,11 @@ struct tmio_mmc_host { struct scatterlist *sg_orig; unsigned int sg_len; unsigned int sg_off; + unsigned long bus_shift; struct platform_device *pdev; struct tmio_mmc_data *pdata; + struct tmio_mmc_dma *dma; /* DMA support */ bool force_pio; @@ -83,10 +97,17 @@ struct tmio_mmc_host { struct mutex ios_lock; /* protect set_ios() context */ bool native_hotplug; bool sdio_irq_enabled; + + int (*write16_hook)(struct tmio_mmc_host *host, int addr); + int (*clk_enable)(struct platform_device *pdev, unsigned int *f); + void (*clk_disable)(struct platform_device *pdev); + int (*multi_io_quirk)(struct mmc_card *card, + unsigned int direction, int blk_size); }; -int tmio_mmc_host_probe(struct tmio_mmc_host **host, - struct platform_device *pdev, +struct tmio_mmc_host *tmio_mmc_host_alloc(struct platform_device *pdev); +void tmio_mmc_host_free(struct tmio_mmc_host *host); +int tmio_mmc_host_probe(struct tmio_mmc_host *host, struct tmio_mmc_data *pdata); void tmio_mmc_host_remove(struct tmio_mmc_host *host); void tmio_mmc_do_data_irq(struct tmio_mmc_host *host); @@ -151,19 +172,19 @@ int tmio_mmc_host_runtime_resume(struct device *dev); static inline u16 sd_ctrl_read16(struct tmio_mmc_host *host, int addr) { - return readw(host->ctl + (addr << host->pdata->bus_shift)); + return readw(host->ctl + (addr << host->bus_shift)); } static inline void sd_ctrl_read16_rep(struct tmio_mmc_host *host, int addr, u16 *buf, int count) { - readsw(host->ctl + (addr << host->pdata->bus_shift), buf, count); + readsw(host->ctl + (addr << host->bus_shift), buf, count); } static inline u32 sd_ctrl_read32(struct tmio_mmc_host *host, int addr) { - return readw(host->ctl + (addr << host->pdata->bus_shift)) | - readw(host->ctl + ((addr + 2) << host->pdata->bus_shift)) << 16; + return readw(host->ctl + (addr << host->bus_shift)) | + readw(host->ctl + ((addr + 2) << host->bus_shift)) << 16; } static inline void sd_ctrl_write16(struct tmio_mmc_host *host, int addr, u16 val) @@ -171,21 +192,21 @@ static inline void sd_ctrl_write16(struct tmio_mmc_host *host, int addr, u16 val /* If there is a hook and it returns non-zero then there * is an error and the write should be skipped */ - if (host->pdata->write16_hook && host->pdata->write16_hook(host, addr)) + if (host->write16_hook && host->write16_hook(host, addr)) return; - writew(val, host->ctl + (addr << host->pdata->bus_shift)); + writew(val, host->ctl + (addr << host->bus_shift)); } static inline void sd_ctrl_write16_rep(struct tmio_mmc_host *host, int addr, u16 *buf, int count) { - writesw(host->ctl + (addr << host->pdata->bus_shift), buf, count); + writesw(host->ctl + (addr << host->bus_shift), buf, count); } static inline void sd_ctrl_write32(struct tmio_mmc_host *host, int addr, u32 val) { - writew(val, host->ctl + (addr << host->pdata->bus_shift)); - writew(val >> 16, host->ctl + ((addr + 2) << host->pdata->bus_shift)); + writew(val, host->ctl + (addr << host->bus_shift)); + writew(val >> 16, host->ctl + ((addr + 2) << host->bus_shift)); } diff --git a/drivers/mmc/host/tmio_mmc_dma.c b/drivers/mmc/host/tmio_mmc_dma.c index 7d077388b9eb..331bb618e398 100644 --- a/drivers/mmc/host/tmio_mmc_dma.c +++ b/drivers/mmc/host/tmio_mmc_dma.c @@ -28,8 +28,8 @@ void tmio_mmc_enable_dma(struct tmio_mmc_host *host, bool enable) if (!host->chan_tx || !host->chan_rx) return; - if (host->pdata->flags & TMIO_MMC_HAVE_CTL_DMA_REG) - sd_ctrl_write16(host, CTL_DMA_ENABLE, enable ? 2 : 0); + if (host->dma->enable) + host->dma->enable(host, enable); } void tmio_mmc_abort_dma(struct tmio_mmc_host *host) @@ -49,11 +49,10 @@ static void tmio_mmc_start_dma_rx(struct tmio_mmc_host *host) struct scatterlist *sg = host->sg_ptr, *sg_tmp; struct dma_async_tx_descriptor *desc = NULL; struct dma_chan *chan = host->chan_rx; - struct tmio_mmc_data *pdata = host->pdata; dma_cookie_t cookie; int ret, i; bool aligned = true, multiple = true; - unsigned int align = (1 << pdata->dma->alignment_shift) - 1; + unsigned int align = (1 << host->pdata->alignment_shift) - 1; for_each_sg(sg, sg_tmp, host->sg_len, i) { if (sg_tmp->offset & align) @@ -126,11 +125,10 @@ static void tmio_mmc_start_dma_tx(struct tmio_mmc_host *host) struct scatterlist *sg = host->sg_ptr, *sg_tmp; struct dma_async_tx_descriptor *desc = NULL; struct dma_chan *chan = host->chan_tx; - struct tmio_mmc_data *pdata = host->pdata; dma_cookie_t cookie; int ret, i; bool aligned = true, multiple = true; - unsigned int align = (1 << pdata->dma->alignment_shift) - 1; + unsigned int align = (1 << host->pdata->alignment_shift) - 1; for_each_sg(sg, sg_tmp, host->sg_len, i) { if (sg_tmp->offset & align) @@ -262,8 +260,8 @@ out: void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdata) { /* We can only either use DMA for both Tx and Rx or not use it at all */ - if (!pdata->dma || (!host->pdev->dev.of_node && - (!pdata->dma->chan_priv_tx || !pdata->dma->chan_priv_rx))) + if (!host->dma || (!host->pdev->dev.of_node && + (!host->dma->chan_priv_tx || !host->dma->chan_priv_rx))) return; if (!host->chan_tx && !host->chan_rx) { @@ -280,7 +278,7 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat dma_cap_set(DMA_SLAVE, mask); host->chan_tx = dma_request_slave_channel_compat(mask, - pdata->dma->filter, pdata->dma->chan_priv_tx, + host->dma->filter, host->dma->chan_priv_tx, &host->pdev->dev, "tx"); dev_dbg(&host->pdev->dev, "%s: TX: got channel %p\n", __func__, host->chan_tx); @@ -288,18 +286,20 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat if (!host->chan_tx) return; - if (pdata->dma->chan_priv_tx) - cfg.slave_id = pdata->dma->slave_id_tx; + if (host->dma->chan_priv_tx) + cfg.slave_id = host->dma->slave_id_tx; cfg.direction = DMA_MEM_TO_DEV; - cfg.dst_addr = res->start + (CTL_SD_DATA_PORT << host->pdata->bus_shift); - cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + cfg.dst_addr = res->start + (CTL_SD_DATA_PORT << host->bus_shift); + cfg.dst_addr_width = host->dma->dma_buswidth; + if (!cfg.dst_addr_width) + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; cfg.src_addr = 0; ret = dmaengine_slave_config(host->chan_tx, &cfg); if (ret < 0) goto ecfgtx; host->chan_rx = dma_request_slave_channel_compat(mask, - pdata->dma->filter, pdata->dma->chan_priv_rx, + host->dma->filter, host->dma->chan_priv_rx, &host->pdev->dev, "rx"); dev_dbg(&host->pdev->dev, "%s: RX: got channel %p\n", __func__, host->chan_rx); @@ -307,11 +307,13 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat if (!host->chan_rx) goto ereqrx; - if (pdata->dma->chan_priv_rx) - cfg.slave_id = pdata->dma->slave_id_rx; + if (host->dma->chan_priv_rx) + cfg.slave_id = host->dma->slave_id_rx; cfg.direction = DMA_DEV_TO_MEM; - cfg.src_addr = cfg.dst_addr + pdata->dma->dma_rx_offset; - cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + cfg.src_addr = cfg.dst_addr + host->pdata->dma_rx_offset; + cfg.src_addr_width = host->dma->dma_buswidth; + if (!cfg.src_addr_width) + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; cfg.dst_addr = 0; ret = dmaengine_slave_config(host->chan_rx, &cfg); if (ret < 0) diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 250bf8c9f998..a31c3573d386 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -835,13 +835,12 @@ fail: static int tmio_mmc_clk_update(struct tmio_mmc_host *host) { struct mmc_host *mmc = host->mmc; - struct tmio_mmc_data *pdata = host->pdata; int ret; - if (!pdata->clk_enable) + if (!host->clk_enable) return -ENOTSUPP; - ret = pdata->clk_enable(host->pdev, &mmc->f_max); + ret = host->clk_enable(host->pdev, &mmc->f_max); if (!ret) mmc->f_min = mmc->f_max / 512; @@ -1005,10 +1004,9 @@ static int tmio_multi_io_quirk(struct mmc_card *card, unsigned int direction, int blk_size) { struct tmio_mmc_host *host = mmc_priv(card->host); - struct tmio_mmc_data *pdata = host->pdata; - if (pdata->multi_io_quirk) - return pdata->multi_io_quirk(card, direction, blk_size); + if (host->multi_io_quirk) + return host->multi_io_quirk(card, direction, blk_size); return blk_size; } @@ -1054,12 +1052,37 @@ static void tmio_mmc_of_parse(struct platform_device *pdev, pdata->flags |= TMIO_MMC_WRPROTECT_DISABLE; } -int tmio_mmc_host_probe(struct tmio_mmc_host **host, - struct platform_device *pdev, - struct tmio_mmc_data *pdata) +struct tmio_mmc_host* +tmio_mmc_host_alloc(struct platform_device *pdev) { - struct tmio_mmc_host *_host; + struct tmio_mmc_host *host; struct mmc_host *mmc; + + mmc = mmc_alloc_host(sizeof(struct tmio_mmc_host), &pdev->dev); + if (!mmc) + return NULL; + + host = mmc_priv(mmc); + host->mmc = mmc; + host->pdev = pdev; + + return host; +} +EXPORT_SYMBOL(tmio_mmc_host_alloc); + +void tmio_mmc_host_free(struct tmio_mmc_host *host) +{ + mmc_free_host(host->mmc); + + host->mmc = NULL; +} +EXPORT_SYMBOL(tmio_mmc_host_free); + +int tmio_mmc_host_probe(struct tmio_mmc_host *_host, + struct tmio_mmc_data *pdata) +{ + struct platform_device *pdev = _host->pdev; + struct mmc_host *mmc = _host->mmc; struct resource *res_ctl; int ret; u32 irq_mask = TMIO_MASK_CMD; @@ -1067,25 +1090,17 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host, tmio_mmc_of_parse(pdev, pdata); if (!(pdata->flags & TMIO_MMC_HAS_IDLE_WAIT)) - pdata->write16_hook = NULL; + _host->write16_hook = NULL; res_ctl = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res_ctl) return -EINVAL; - mmc = mmc_alloc_host(sizeof(struct tmio_mmc_host), &pdev->dev); - if (!mmc) - return -ENOMEM; - ret = mmc_of_parse(mmc); if (ret < 0) goto host_free; - pdata->dev = &pdev->dev; - _host = mmc_priv(mmc); _host->pdata = pdata; - _host->mmc = mmc; - _host->pdev = pdev; platform_set_drvdata(pdev, mmc); _host->set_pwr = pdata->set_pwr; @@ -1192,12 +1207,9 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host, mmc_gpiod_request_cd_irq(mmc); } - *host = _host; - return 0; host_free: - mmc_free_host(mmc); return ret; } @@ -1222,7 +1234,6 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host) pm_runtime_disable(&pdev->dev); iounmap(host->ctl); - mmc_free_host(mmc); } EXPORT_SYMBOL(tmio_mmc_host_remove); @@ -1237,8 +1248,8 @@ int tmio_mmc_host_runtime_suspend(struct device *dev) if (host->clk_cache) tmio_mmc_clk_stop(host); - if (host->pdata->clk_disable) - host->pdata->clk_disable(host->pdev); + if (host->clk_disable) + host->clk_disable(host->pdev); return 0; } diff --git a/drivers/mmc/host/toshsd.c b/drivers/mmc/host/toshsd.c index 4666262edaca..e2cdd5fb1423 100644 --- a/drivers/mmc/host/toshsd.c +++ b/drivers/mmc/host/toshsd.c @@ -176,7 +176,8 @@ static irqreturn_t toshsd_thread_irq(int irq, void *dev_id) spin_lock_irqsave(&host->lock, flags); if (!sg_miter_next(sg_miter)) - return IRQ_HANDLED; + goto done; + buf = sg_miter->addr; /* Ensure we dont read more than one block. The chip will interrupt us @@ -198,6 +199,7 @@ static irqreturn_t toshsd_thread_irq(int irq, void *dev_id) sg_miter->consumed = count; sg_miter_stop(sg_miter); +done: spin_unlock_irqrestore(&host->lock, flags); return IRQ_HANDLED; @@ -699,18 +701,7 @@ static struct pci_driver toshsd_driver = { .driver.pm = &toshsd_pm_ops, }; -static int __init toshsd_drv_init(void) -{ - return pci_register_driver(&toshsd_driver); -} - -static void __exit toshsd_drv_exit(void) -{ - pci_unregister_driver(&toshsd_driver); -} - -module_init(toshsd_drv_init); -module_exit(toshsd_drv_exit); +module_pci_driver(toshsd_driver); MODULE_AUTHOR("Ondrej Zary, Richard Betts"); MODULE_DESCRIPTION("Toshiba PCI Secure Digital Host Controller Interface driver"); diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c index 4262296c12fa..fbabbb82b354 100644 --- a/drivers/mmc/host/vub300.c +++ b/drivers/mmc/host/vub300.c @@ -659,7 +659,7 @@ static void __vub300_irqpoll_response(struct vub300_mmc_host *vub300) static void __do_poll(struct vub300_mmc_host *vub300) { /* cmd_mutex is held by vub300_pollwork_thread */ - long commretval; + unsigned long commretval; mod_timer(&vub300->inactivity_timer, jiffies + HZ); init_completion(&vub300->irqpoll_complete); send_irqpoll(vub300); @@ -671,8 +671,6 @@ static void __do_poll(struct vub300_mmc_host *vub300) vub300->usb_timed_out = 1; usb_kill_urb(vub300->command_out_urb); usb_kill_urb(vub300->command_res_urb); - } else if (commretval < 0) { - vub300_queue_poll_work(vub300, 1); } else { /* commretval > 0 */ __vub300_irqpoll_response(vub300); } |