diff options
Diffstat (limited to 'drivers/mmc/host')
53 files changed, 1544 insertions, 510 deletions
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 451135822464..13860656104b 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -14,6 +14,17 @@ config MMC_ARMMMCI If unsure, say N. +config MMC_QCOM_DML + tristate "Qualcomm Data Mover for SD Card Controller" + depends on MMC_ARMMMCI && QCOM_BAM_DMA + default y + help + This selects the Qualcomm Data Mover lite/local on SD Card controller. + This option will enable the dma to work correctly, if you are using + Qcom SOCs and MMC, you would probably need this option to get DMA working. + + if unsure, say N. + config MMC_PXA tristate "Intel PXA25x/26x/27x Multimedia Card Interface support" depends on ARCH_PXA @@ -568,7 +579,8 @@ config SDH_BFIN_MISSING_CMD_PULLUP_WORKAROUND config MMC_DW tristate "Synopsys DesignWare Memory Card Interface" - depends on ARC || ARM + depends on HAS_DMA + depends on ARC || ARM || MIPS || COMPILE_TEST help This selects support for the Synopsys DesignWare Mobile Storage IP block, this provides host support for SD and MMC interfaces, in both @@ -626,6 +638,15 @@ config MMC_DW_PCI If unsure, say N. +config MMC_DW_ROCKCHIP + tristate "Rockchip specific extensions for Synopsys DW Memory Card Interface" + depends on MMC_DW && ARCH_ROCKCHIP + select MMC_DW_PLTFM + help + This selects support for Rockchip SoC specific extensions to the + Synopsys DesignWare Memory Card Interface driver. Select this option + for platforms based on RK3066, RK3188 and RK3288 SoC's. + config MMC_SH_MMCIF tristate "SuperH Internal MMCIF support" depends on MMC_BLOCK && HAS_DMA diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index f211eede8db5..b09ecfb88269 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -3,6 +3,7 @@ # obj-$(CONFIG_MMC_ARMMMCI) += mmci.o +obj-$(CONFIG_MMC_QCOM_DML) += mmci_qcom_dml.o obj-$(CONFIG_MMC_PXA) += pxamci.o obj-$(CONFIG_MMC_MXC) += mxcmmc.o obj-$(CONFIG_MMC_MXS) += mxs-mmc.o @@ -45,6 +46,7 @@ obj-$(CONFIG_MMC_DW_PLTFM) += dw_mmc-pltfm.o obj-$(CONFIG_MMC_DW_EXYNOS) += dw_mmc-exynos.o obj-$(CONFIG_MMC_DW_K3) += dw_mmc-k3.o obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o +obj-$(CONFIG_MMC_DW_ROCKCHIP) += dw_mmc-rockchip.o obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o obj-$(CONFIG_MMC_VUB300) += vub300.o diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index bb585d940901..77250d4b1979 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -17,6 +17,7 @@ #include <linux/gpio.h> #include <linux/init.h> #include <linux/interrupt.h> +#include <linux/io.h> #include <linux/ioport.h> #include <linux/module.h> #include <linux/of.h> @@ -2195,7 +2196,8 @@ static int __init atmci_init_slot(struct atmel_mci *host, /* Assume card is present initially */ set_bit(ATMCI_CARD_PRESENT, &slot->flags); if (gpio_is_valid(slot->detect_pin)) { - if (gpio_request(slot->detect_pin, "mmc_detect")) { + if (devm_gpio_request(&host->pdev->dev, slot->detect_pin, + "mmc_detect")) { dev_dbg(&mmc->class_dev, "no detect pin available\n"); slot->detect_pin = -EBUSY; } else if (gpio_get_value(slot->detect_pin) ^ @@ -2208,7 +2210,8 @@ static int __init atmci_init_slot(struct atmel_mci *host, mmc->caps |= MMC_CAP_NEEDS_POLL; if (gpio_is_valid(slot->wp_pin)) { - if (gpio_request(slot->wp_pin, "mmc_wp")) { + if (devm_gpio_request(&host->pdev->dev, slot->wp_pin, + "mmc_wp")) { dev_dbg(&mmc->class_dev, "no WP pin available\n"); slot->wp_pin = -EBUSY; } @@ -2232,7 +2235,6 @@ static int __init atmci_init_slot(struct atmel_mci *host, dev_dbg(&mmc->class_dev, "could not request IRQ %d for detect pin\n", gpio_to_irq(slot->detect_pin)); - gpio_free(slot->detect_pin); slot->detect_pin = -EBUSY; } } @@ -2242,7 +2244,7 @@ static int __init atmci_init_slot(struct atmel_mci *host, return 0; } -static void __exit atmci_cleanup_slot(struct atmel_mci_slot *slot, +static void atmci_cleanup_slot(struct atmel_mci_slot *slot, unsigned int id) { /* Debugfs stuff is cleaned up by mmc core */ @@ -2257,10 +2259,7 @@ static void __exit atmci_cleanup_slot(struct atmel_mci_slot *slot, free_irq(gpio_to_irq(pin), slot); del_timer_sync(&slot->detect_timer); - gpio_free(pin); } - if (gpio_is_valid(slot->wp_pin)) - gpio_free(slot->wp_pin); slot->host->slot[id] = NULL; mmc_free_host(slot->mmc); @@ -2344,6 +2343,7 @@ static void __init atmci_get_cap(struct atmel_mci *host) /* keep only major version number */ switch (version & 0xf00) { + case 0x600: case 0x500: host->caps.has_odd_clk_div = 1; case 0x400: @@ -2377,7 +2377,7 @@ static int __init atmci_probe(struct platform_device *pdev) struct resource *regs; unsigned int nr_slots; int irq; - int ret; + int ret, i; regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!regs) @@ -2395,7 +2395,7 @@ static int __init atmci_probe(struct platform_device *pdev) if (irq < 0) return irq; - host = kzalloc(sizeof(struct atmel_mci), GFP_KERNEL); + host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); if (!host) return -ENOMEM; @@ -2403,20 +2403,18 @@ static int __init atmci_probe(struct platform_device *pdev) spin_lock_init(&host->lock); INIT_LIST_HEAD(&host->queue); - host->mck = clk_get(&pdev->dev, "mci_clk"); - if (IS_ERR(host->mck)) { - ret = PTR_ERR(host->mck); - goto err_clk_get; - } + host->mck = devm_clk_get(&pdev->dev, "mci_clk"); + if (IS_ERR(host->mck)) + return PTR_ERR(host->mck); - ret = -ENOMEM; - host->regs = ioremap(regs->start, resource_size(regs)); + host->regs = devm_ioremap(&pdev->dev, regs->start, resource_size(regs)); if (!host->regs) - goto err_ioremap; + return -ENOMEM; ret = clk_prepare_enable(host->mck); if (ret) - goto err_request_irq; + return ret; + atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST); host->bus_hz = clk_get_rate(host->mck); clk_disable_unprepare(host->mck); @@ -2427,7 +2425,7 @@ static int __init atmci_probe(struct platform_device *pdev) ret = request_irq(irq, atmci_interrupt, 0, dev_name(&pdev->dev), host); if (ret) - goto err_request_irq; + return ret; /* Get MCI capabilities and set operations according to it */ atmci_get_cap(host); @@ -2485,7 +2483,7 @@ static int __init atmci_probe(struct platform_device *pdev) if (!host->buffer) { ret = -ENOMEM; dev_err(&pdev->dev, "buffer allocation failed\n"); - goto err_init_slot; + goto err_dma_alloc; } } @@ -2495,16 +2493,16 @@ static int __init atmci_probe(struct platform_device *pdev) return 0; +err_dma_alloc: + for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) { + if (host->slot[i]) + atmci_cleanup_slot(host->slot[i], i); + } err_init_slot: + del_timer_sync(&host->timer); if (host->dma.chan) dma_release_channel(host->dma.chan); free_irq(irq, host); -err_request_irq: - iounmap(host->regs); -err_ioremap: - clk_put(host->mck); -err_clk_get: - kfree(host); return ret; } @@ -2528,14 +2526,11 @@ static int __exit atmci_remove(struct platform_device *pdev) atmci_readl(host, ATMCI_SR); clk_disable_unprepare(host->mck); + del_timer_sync(&host->timer); if (host->dma.chan) dma_release_channel(host->dma.chan); free_irq(platform_get_irq(pdev, 0), host); - iounmap(host->regs); - - clk_put(host->mck); - kfree(host); return 0; } diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c index 9c9f6af29251..725f6a6fd89b 100644 --- a/drivers/mmc/host/au1xmmc.c +++ b/drivers/mmc/host/au1xmmc.c @@ -1028,9 +1028,12 @@ static int au1xmmc_probe(struct platform_device *pdev) host->clk = clk_get(&pdev->dev, ALCHEMY_PERIPH_CLK); if (IS_ERR(host->clk)) { dev_err(&pdev->dev, "cannot find clock\n"); + ret = PTR_ERR(host->clk); goto out_irq; } - if (clk_prepare_enable(host->clk)) { + + ret = clk_prepare_enable(host->clk); + if (ret) { dev_err(&pdev->dev, "cannot enable clock\n"); goto out_clk; } diff --git a/drivers/mmc/host/dw_mmc-pci.c b/drivers/mmc/host/dw_mmc-pci.c index 6ada1b36685b..4c69fbd29811 100644 --- a/drivers/mmc/host/dw_mmc-pci.c +++ b/drivers/mmc/host/dw_mmc-pci.c @@ -95,9 +95,6 @@ static int dw_mci_pci_resume(struct device *dev) return dw_mci_resume(host); } -#else -#define dw_mci_pci_suspend NULL -#define dw_mci_pci_resume NULL #endif /* CONFIG_PM_SLEEP */ static SIMPLE_DEV_PM_OPS(dw_mci_pci_pmops, dw_mci_pci_suspend, dw_mci_pci_resume); diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c index d4a47a9f5584..8b6572162ed9 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.c +++ b/drivers/mmc/host/dw_mmc-pltfm.c @@ -21,6 +21,7 @@ #include <linux/mmc/mmc.h> #include <linux/mmc/dw_mmc.h> #include <linux/of.h> +#include <linux/clk.h> #include "dw_mmc.h" #include "dw_mmc-pltfm.h" @@ -30,10 +31,6 @@ static void dw_mci_pltfm_prepare_command(struct dw_mci *host, u32 *cmdr) *cmdr |= SDMMC_CMD_USE_HOLD_REG; } -static const struct dw_mci_drv_data rockchip_drv_data = { - .prepare_command = dw_mci_pltfm_prepare_command, -}; - static const struct dw_mci_drv_data socfpga_drv_data = { .prepare_command = dw_mci_pltfm_prepare_command, }; @@ -84,9 +81,6 @@ static int dw_mci_pltfm_resume(struct device *dev) return dw_mci_resume(host); } -#else -#define dw_mci_pltfm_suspend NULL -#define dw_mci_pltfm_resume NULL #endif /* CONFIG_PM_SLEEP */ SIMPLE_DEV_PM_OPS(dw_mci_pltfm_pmops, dw_mci_pltfm_suspend, dw_mci_pltfm_resume); @@ -94,8 +88,6 @@ EXPORT_SYMBOL_GPL(dw_mci_pltfm_pmops); static const struct of_device_id dw_mci_pltfm_match[] = { { .compatible = "snps,dw-mshc", }, - { .compatible = "rockchip,rk2928-dw-mshc", - .data = &rockchip_drv_data }, { .compatible = "altr,socfpga-dw-mshc", .data = &socfpga_drv_data }, {}, diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c new file mode 100644 index 000000000000..f0c2cb1a210d --- /dev/null +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2014, Fuzhou Rockchip 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. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/mmc/host.h> +#include <linux/mmc/dw_mmc.h> +#include <linux/of_address.h> + +#include "dw_mmc.h" +#include "dw_mmc-pltfm.h" + +#define RK3288_CLKGEN_DIV 2 + +static void dw_mci_rockchip_prepare_command(struct dw_mci *host, u32 *cmdr) +{ + *cmdr |= SDMMC_CMD_USE_HOLD_REG; +} + +static int dw_mci_rk3288_setup_clock(struct dw_mci *host) +{ + host->bus_hz /= RK3288_CLKGEN_DIV; + + return 0; +} + +static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) +{ + int ret; + unsigned int cclkin; + u32 bus_hz; + + /* + * cclkin: source clock of mmc controller + * bus_hz: card interface clock generated by CLKGEN + * bus_hz = cclkin / RK3288_CLKGEN_DIV + * ios->clock = (div == 0) ? bus_hz : (bus_hz / (2 * div)) + * + * Note: div can only be 0 or 1 + * if DDR50 8bit mode(only emmc work in 8bit mode), + * div must be set 1 + */ + if (ios->bus_width == MMC_BUS_WIDTH_8 && + ios->timing == MMC_TIMING_MMC_DDR52) + cclkin = 2 * ios->clock * RK3288_CLKGEN_DIV; + else + cclkin = ios->clock * RK3288_CLKGEN_DIV; + + ret = clk_set_rate(host->ciu_clk, cclkin); + if (ret) + dev_warn(host->dev, "failed to set rate %uHz\n", ios->clock); + + bus_hz = clk_get_rate(host->ciu_clk) / RK3288_CLKGEN_DIV; + if (bus_hz != host->bus_hz) { + host->bus_hz = bus_hz; + /* force dw_mci_setup_bus() */ + host->current_speed = 0; + } +} + +static const struct dw_mci_drv_data rk2928_drv_data = { + .prepare_command = dw_mci_rockchip_prepare_command, +}; + +static const struct dw_mci_drv_data rk3288_drv_data = { + .prepare_command = dw_mci_rockchip_prepare_command, + .set_ios = dw_mci_rk3288_set_ios, + .setup_clock = dw_mci_rk3288_setup_clock, +}; + +static const struct of_device_id dw_mci_rockchip_match[] = { + { .compatible = "rockchip,rk2928-dw-mshc", + .data = &rk2928_drv_data }, + { .compatible = "rockchip,rk3288-dw-mshc", + .data = &rk3288_drv_data }, + {}, +}; +MODULE_DEVICE_TABLE(of, dw_mci_rockchip_match); + +static int dw_mci_rockchip_probe(struct platform_device *pdev) +{ + const struct dw_mci_drv_data *drv_data; + const struct of_device_id *match; + + if (!pdev->dev.of_node) + return -ENODEV; + + match = of_match_node(dw_mci_rockchip_match, pdev->dev.of_node); + drv_data = match->data; + + return dw_mci_pltfm_register(pdev, drv_data); +} + +#ifdef CONFIG_PM_SLEEP +static int dw_mci_rockchip_suspend(struct device *dev) +{ + struct dw_mci *host = dev_get_drvdata(dev); + + return dw_mci_suspend(host); +} + +static int dw_mci_rockchip_resume(struct device *dev) +{ + struct dw_mci *host = dev_get_drvdata(dev); + + return dw_mci_resume(host); +} +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(dw_mci_rockchip_pmops, + dw_mci_rockchip_suspend, + dw_mci_rockchip_resume); + +static struct platform_driver dw_mci_rockchip_pltfm_driver = { + .probe = dw_mci_rockchip_probe, + .remove = __exit_p(dw_mci_pltfm_remove), + .driver = { + .name = "dwmmc_rockchip", + .of_match_table = dw_mci_rockchip_match, + .pm = &dw_mci_rockchip_pmops, + }, +}; + +module_platform_driver(dw_mci_rockchip_pltfm_driver); + +MODULE_AUTHOR("Addy Ke <addy.ke@rock-chips.com>"); +MODULE_DESCRIPTION("Rockchip Specific DW-MSHC Driver Extension"); +MODULE_ALIAS("platform:dwmmc-rockchip"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 8f216edbdf08..69f0cc68d5b2 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -29,6 +29,7 @@ #include <linux/irq.h> #include <linux/mmc/host.h> #include <linux/mmc/mmc.h> +#include <linux/mmc/sd.h> #include <linux/mmc/sdio.h> #include <linux/mmc/dw_mmc.h> #include <linux/bitops.h> @@ -81,36 +82,6 @@ struct idmac_desc { }; #endif /* CONFIG_MMC_DW_IDMAC */ -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 bool dw_mci_reset(struct dw_mci *host); #if defined(CONFIG_DEBUG_FS) @@ -234,10 +205,13 @@ err: } #endif /* defined(CONFIG_DEBUG_FS) */ +static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg); + static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) { struct mmc_data *data; struct dw_mci_slot *slot = mmc_priv(mmc); + struct dw_mci *host = slot->host; const struct dw_mci_drv_data *drv_data = slot->host->drv_data; u32 cmdr; cmd->error = -EINPROGRESS; @@ -253,6 +227,34 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) else if (cmd->opcode != MMC_SEND_STATUS && cmd->data) cmdr |= SDMMC_CMD_PRV_DAT_WAIT; + if (cmd->opcode == SD_SWITCH_VOLTAGE) { + u32 clk_en_a; + + /* Special bit makes CMD11 not die */ + cmdr |= SDMMC_CMD_VOLT_SWITCH; + + /* Change state to continue to handle CMD11 weirdness */ + WARN_ON(slot->host->state != STATE_SENDING_CMD); + slot->host->state = STATE_SENDING_CMD11; + + /* + * We need to disable low power mode (automatic clock stop) + * while doing voltage switch so we don't confuse the card, + * since stopping the clock is a specific part of the UHS + * voltage change dance. + * + * Note that low power mode (SDMMC_CLKEN_LOW_PWR) will be + * unconditionally turned back on in dw_mci_setup_bus() if it's + * ever called with a non-zero clock. That shouldn't happen + * until the voltage change is all done. + */ + clk_en_a = mci_readl(host, CLKENA); + clk_en_a &= ~(SDMMC_CLKEN_LOW_PWR << slot->id); + mci_writel(host, CLKENA, clk_en_a); + mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | + SDMMC_CMD_PRV_DAT_WAIT, 0); + } + if (cmd->flags & MMC_RSP_PRESENT) { /* We expect a response, so set this bit */ cmdr |= SDMMC_CMD_RESP_EXP; @@ -775,11 +777,15 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) unsigned int clock = slot->clock; u32 div; u32 clk_en_a; + u32 sdmmc_cmd_bits = SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT; + + /* We must continue to set bit 28 in CMD until the change is complete */ + if (host->state == STATE_WAITING_CMD11_DONE) + sdmmc_cmd_bits |= SDMMC_CMD_VOLT_SWITCH; if (!clock) { mci_writel(host, CLKENA, 0); - mci_send_cmd(slot, - SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); + mci_send_cmd(slot, sdmmc_cmd_bits, 0); } else if (clock != host->current_speed || force_clkinit) { div = host->bus_hz / clock; if (host->bus_hz % clock && host->bus_hz > clock) @@ -803,15 +809,13 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) mci_writel(host, CLKSRC, 0); /* inform CIU */ - mci_send_cmd(slot, - SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); + mci_send_cmd(slot, sdmmc_cmd_bits, 0); /* set clock to desired speed */ mci_writel(host, CLKDIV, div); /* inform CIU */ - mci_send_cmd(slot, - SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); + mci_send_cmd(slot, sdmmc_cmd_bits, 0); /* enable clock; only low power if no SDIO */ clk_en_a = SDMMC_CLKEN_ENABLE << slot->id; @@ -820,8 +824,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) mci_writel(host, CLKENA, clk_en_a); /* inform CIU */ - mci_send_cmd(slot, - SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); + mci_send_cmd(slot, sdmmc_cmd_bits, 0); /* keep the clock with reflecting clock dividor */ slot->__clk_old = clock << div; @@ -897,6 +900,17 @@ static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot, slot->mrq = mrq; + if (host->state == STATE_WAITING_CMD11_DONE) { + dev_warn(&slot->mmc->class_dev, + "Voltage change didn't complete\n"); + /* + * this case isn't expected to happen, so we can + * either crash here or just try to continue on + * in the closest possible state + */ + host->state = STATE_IDLE; + } + if (host->state == STATE_IDLE) { host->state = STATE_SENDING_CMD; dw_mci_start_request(host, slot); @@ -936,6 +950,7 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) struct dw_mci_slot *slot = mmc_priv(mmc); const struct dw_mci_drv_data *drv_data = slot->host->drv_data; u32 regs; + int ret; switch (ios->bus_width) { case MMC_BUS_WIDTH_4: @@ -972,14 +987,43 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) /* Slot specific timing and width adjustment */ dw_mci_setup_bus(slot, false); + if (slot->host->state == STATE_WAITING_CMD11_DONE && ios->clock != 0) + slot->host->state = STATE_IDLE; + switch (ios->power_mode) { case MMC_POWER_UP: + if (!IS_ERR(mmc->supply.vmmc)) { + ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, + ios->vdd); + if (ret) { + dev_err(slot->host->dev, + "failed to enable vmmc regulator\n"); + /*return, if failed turn on vmmc*/ + return; + } + } + if (!IS_ERR(mmc->supply.vqmmc) && !slot->host->vqmmc_enabled) { + ret = regulator_enable(mmc->supply.vqmmc); + if (ret < 0) + dev_err(slot->host->dev, + "failed to enable vqmmc regulator\n"); + 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)) + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); + + if (!IS_ERR(mmc->supply.vqmmc) && slot->host->vqmmc_enabled) { + regulator_disable(mmc->supply.vqmmc); + slot->host->vqmmc_enabled = false; + } + regs = mci_readl(slot->host, PWREN); regs &= ~(1 << slot->id); mci_writel(slot->host, PWREN, regs); @@ -989,6 +1033,59 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) } } +static int dw_mci_card_busy(struct mmc_host *mmc) +{ + struct dw_mci_slot *slot = mmc_priv(mmc); + u32 status; + + /* + * Check the busy bit which is low when DAT[3:0] + * (the data lines) are 0000 + */ + status = mci_readl(slot->host, STATUS); + + return !!(status & SDMMC_STATUS_BUSY); +} + +static int dw_mci |