diff options
48 files changed, 1324 insertions, 968 deletions
diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt b/Documentation/devicetree/bindings/mmc/mmc.txt index b52628b18a53..bac131169c07 100644 --- a/Documentation/devicetree/bindings/mmc/mmc.txt +++ b/Documentation/devicetree/bindings/mmc/mmc.txt @@ -64,7 +64,31 @@ Optional SDIO properties: - keep-power-in-suspend: Preserves card power during a suspend/resume cycle - enable-sdio-wakeup: Enables wake up of host system on SDIO IRQ assertion -Example: + +Use of Function subnodes +------------------------ + +On embedded systems the cards connected to a host may need additional +properties. These can be specified in subnodes to the host controller node. +The subnodes are identified by the standard 'reg' property. +Which information exactly can be specified depends on the bindings for the +SDIO function driver for the subnode, as specified by the compatible string. + +Required host node properties when using function subnodes: +- #address-cells: should be one. The cell is the slot id. +- #size-cells: should be zero. + +Required function subnode properties: +- compatible: name of SDIO function following generic names recommended practice +- reg: Must contain the SDIO function number of the function this subnode + describes. A value of 0 denotes the memory SD function, values from + 1 to 7 denote the SDIO functions. + + +Examples +-------- + +Basic example: sdhci@ab000000 { compatible = "sdhci"; @@ -78,3 +102,25 @@ sdhci@ab000000 { keep-power-in-suspend; enable-sdio-wakeup; } + +Example with sdio function subnode: + +mmc3: mmc@01c12000 { + #address-cells = <1>; + #size-cells = <0>; + + pinctrl-names = "default"; + pinctrl-0 = <&mmc3_pins_a>; + vmmc-supply = <®_vmmc3>; + bus-width = <4>; + non-removable; + status = "okay"; + + brcmf: bcrmf@1 { + reg = <1>; + compatible = "brcm,bcm43xx-fmac"; + interrupt-parent = <&pio>; + interrupts = <10 8>; /* PH10 / EINT10 */ + interrupt-names = "host-wake"; + }; +}; diff --git a/Documentation/devicetree/bindings/mmc/sdhci-fujitsu.txt b/Documentation/devicetree/bindings/mmc/sdhci-fujitsu.txt new file mode 100644 index 000000000000..de2c53cff4f1 --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/sdhci-fujitsu.txt @@ -0,0 +1,30 @@ +* Fujitsu SDHCI controller + +This file documents differences between the core properties in mmc.txt +and the properties used by the sdhci_f_sdh30 driver. + +Required properties: +- compatible: "fujitsu,mb86s70-sdhci-3.0" +- clocks: Must contain an entry for each entry in clock-names. It is a + list of phandles and clock-specifier pairs. + See ../clocks/clock-bindings.txt for details. +- clock-names: Should contain the following two entries: + "iface" - clock used for sdhci interface + "core" - core clock for sdhci controller + +Optional properties: +- vqmmc-supply: phandle to the regulator device tree node, mentioned + as the VCCQ/VDD_IO supply in the eMMC/SD specs. + +Example: + + sdhci1: mmc@36600000 { + compatible = "fujitsu,mb86s70-sdhci-3.0"; + reg = <0 0x36600000 0x1000>; + interrupts = <0 172 0x4>, + <0 173 0x4>; + bus-width = <4>; + vqmmc-supply = <&vccq_sdhci1>; + clocks = <&clock 2 2 0>, <&clock 2 3 0>; + clock-names = "iface", "core"; + }; diff --git a/arch/arm/mach-omap2/board-omap3pandora.c b/arch/arm/mach-omap2/board-omap3pandora.c index 7f1708738c30..969e1003dd92 100644 --- a/arch/arm/mach-omap2/board-omap3pandora.c +++ b/arch/arm/mach-omap2/board-omap3pandora.c @@ -254,12 +254,14 @@ static void pandora_wl1251_init_card(struct mmc_card *card) * We have TI wl1251 attached to MMC3. Pass this information to * SDIO core because it can't be probed by normal methods. */ - card->quirks |= MMC_QUIRK_NONSTD_SDIO; - card->cccr.wide_bus = 1; - card->cis.vendor = 0x104c; - card->cis.device = 0x9066; - card->cis.blksize = 512; - card->cis.max_dtr = 20000000; + if (card->type == MMC_TYPE_SDIO || card->type == MMC_TYPE_SD_COMBO) { + card->quirks |= MMC_QUIRK_NONSTD_SDIO; + card->cccr.wide_bus = 1; + card->cis.vendor = 0x104c; + card->cis.device = 0x9066; + card->cis.blksize = 512; + card->cis.max_dtr = 20000000; + } } static struct omap2_hsmmc_info omap3pandora_mmc[] = { 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/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..d5c176e87951 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -185,13 +185,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 +252,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; } /** @@ -345,29 +348,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 +1085,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 +1264,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 /** @@ -2245,67 +2305,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); - - mmc_host_clk_release(host); + ret = host->bus_ops->reset(host); + mmc_bus_put(host); - return host->bus_ops->power_restore(host); -} + pr_warn("%s: tried to reset card\n", mmc_hostname(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..07636449b4de 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -29,13 +29,19 @@ #include "core.h" #include "host.h" +#include "slot-gpio.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 +60,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 +370,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 +394,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) @@ -458,10 +449,6 @@ int mmc_of_parse(struct mmc_host *host) } return 0; - -out: - mmc_gpio_free_cd(host); - return ret; } EXPORT_SYMBOL(mmc_of_parse); @@ -491,8 +478,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 +490,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 +516,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 +588,6 @@ 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); - put_device(&host->class_dev); } diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 7466ce098e60..1fc48a280659 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1155,38 +1155,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 +1162,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 +1172,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 +1253,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 +1783,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 +1833,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/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..85df91f55b63 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -292,6 +292,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..12a5eaa647cd 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; } 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.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..88aefa6fa8b1 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1739,10 +1739,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..d2a1ef61ec97 100644 --- a/drivers/mmc/host/moxart-mmc.c +++ b/drivers/mmc/host/moxart-mmc.c @@ -599,7 +599,9 @@ static int moxart_probe(struct platform_device *pdev) goto out; } - mmc_of_parse(mmc); + ret = mmc_of_parse(mmc); + if (ret) + goto out; dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); 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..26c6a7c09c0a 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> @@ -71,30 +72,78 @@ 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 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 +215,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 +225,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 +251,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 +265,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 +333,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 +548,16 @@ 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 (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 +575,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 +587,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 +597,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 +697,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 +771,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) || @@ -776,17 +826,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 +849,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 +870,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); @@ -1317,6 +1367,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 +1400,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..4de39fb6dc2d 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -365,10 +365,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,8 +392,8 @@ 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: @@ -411,8 +412,10 @@ 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)) diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index c45b8932d843..a2828222709c 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -607,7 +607,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.c b/drivers/mmc/host/sdhci.c index f1a488ee432f..c9881ca131d5 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, @@ -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..c1bb156445e4 --- /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); + 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/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/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/include/linux/mmc/core.h b/include/linux/mmc/core.h index cb2b0400d284..160448f920ac 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -182,7 +182,6 @@ extern int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen); extern int mmc_set_blockcount(struct mmc_card *card, unsigned int blockcount, bool is_rel_write); extern int mmc_hw_reset(struct mmc_host *host); -extern int mmc_hw_reset_check(struct mmc_host *host); extern int mmc_can_reset(struct mmc_card *card); extern void mmc_set_data_timeout(struct mmc_data *, const struct mmc_card *); diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h index 42b724e8d503..471fb3116dbe 100644 --- a/include/linux/mmc/dw_mmc.h +++ b/include/linux/mmc/dw_mmc.h @@ -106,6 +106,11 @@ struct mmc_data; * @cur_slot, @mrq and @state. These must always be updated * at the same time while holding @lock. * + * @irq_lock is an irq-safe spinlock protecting the INTMASK register + * to allow the interrupt handler to modify it directly. Held for only long + * enough to read-modify-write INTMASK and no other locks are grabbed when + * holding this one. + * * The @mrq field of struct dw_mci_slot is also protected by @lock, * and must always be written at the same time as the slot is added to * @queue. @@ -125,6 +130,7 @@ struct mmc_data; */ struct dw_mci { spinlock_t lock; + spinlock_t irq_lock; void __iomem *regs; struct scatterlist *sg; diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 9f322706f7cb..b6bf718c3498 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -166,7 +166,6 @@ struct mmc_async_req { * struct mmc_slot - MMC slot functions * * @cd_irq: MMC/SD-card slot hotplug detection IRQ or -EINVAL - * @lock: protect the @handler_priv pointer * @handler_priv: MMC/SD-card slot context * * Some MMC/SD host controllers implement slot-functions like card and @@ -176,7 +175,6 @@ struct mmc_async_req { */ struct mmc_slot { int cd_irq; - struct mutex lock; void *handler_priv; }; diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index 49ad7a943638..fb97b5cc91cd 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -53,11 +53,6 @@ #define MMC_SEND_TUNING_BLOCK 19 /* adtc R1 */ #define MMC_SEND_TUNING_BLOCK_HS200 21 /* adtc R1 */ -#define MMC_TUNING_BLK_PATTERN_4BIT_SIZE 64 -#define MMC_TUNING_BLK_PATTERN_8BIT_SIZE 128 -extern const u8 tuning_blk_pattern_4bit[MMC_TUNING_BLK_PATTERN_4BIT_SIZE]; -extern const u8 tuning_blk_pattern_8bit[MMC_TUNING_BLK_PATTERN_8BIT_SIZE]; - /* class 3 */ #define MMC_WRITE_DAT_UNTIL_STOP 20 /* adtc [31:0] data addr R1 */ diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h index f767a0de611f..c3e3db196738 100644 --- a/include/linux/mmc/sdhci.h +++ b/include/linux/mmc/sdhci.h @@ -17,6 +17,11 @@ #include <linux/io.h> #include <linux/mmc/host.h> +struct sdhci_host_next { + unsigned int sg_count; + s32 cookie; +}; + struct sdhci_host { /* Data set by hardware interface driver */ const char *hw_name; /* Hardware bus name */ @@ -106,6 +111,10 @@ struct sdhci_host { #define SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD (1<<10) /* Capability register bit-63 indicates HS400 support */ #define SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 (1<<11) +/* forced tuned clock */ +#define SDHCI_QUIRK2_TUNING_WORK_AROUND (1<<12) +/* disable the block count for single block transactions */ +#define SDHCI_QUIRK2_SUPPORT_SINGLE (1<<13) int irq; /* Device IRQ */ void __iomem *ioaddr; /* Mapped address */ @@ -203,6 +212,7 @@ struct sdhci_host { #define SDHCI_TUNING_MODE_1 0 struct timer_list tuning_timer; /* Timer for tuning */ + struct sdhci_host_next next_data; unsigned long private[0] ____cacheline_aligned; }; #endif /* LINUX_MMC_SDHCI_H */ diff --git a/include/linux/mmc/slot-gpio.h b/include/linux/mmc/slot-gpio.h index e56fa24c9322..3945a8c9d3cb 100644 --- a/include/linux/mmc/slot-gpio.h +++ b/include/linux/mmc/slot-gpio.h @@ -15,12 +15,10 @@ struct mmc_host; int mmc_gpio_get_ro(struct mmc_host *host); int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio); -void mmc_gpio_free_ro(struct mmc_host *host); int mmc_gpio_get_cd(struct mmc_host *host); int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio, unsigned int debounce); -void mmc_gpio_free_cd(struct mmc_host *host); int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id, unsigned int idx, bool override_active_level, @@ -28,7 +26,8 @@ int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id, 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); -void mmc_gpiod_free_cd(struct mmc_host *host); +void mmc_gpio_set_cd_isr(struct mmc_host *host, + irqreturn_t (*isr)(int irq, void *dev_id)); void mmc_gpiod_request_cd_irq(struct mmc_host *host); #endif diff --git a/include/linux/platform_data/mmc-omap.h b/include/linux/platform_data/mmc-omap.h index 5c188f4e9bec..929469291406 100644 --- a/include/linux/platform_data/mmc-omap.h +++ b/include/linux/platform_data/mmc-omap.h @@ -31,10 +31,6 @@ struct omap_mmc_platform_data { void (*cleanup)(struct device *dev); void (*shutdown)(struct device *dev); - /* To handle board related suspend/resume functionality for MMC */ - int (*suspend)(struct device *dev, int slot); - int (*resume)(struct device *dev, int slot); - /* Return context loss count due to PM states changing */ int (*get_context_loss_count)(struct device *dev); |