diff options
Diffstat (limited to 'drivers/mmc/host')
57 files changed, 3269 insertions, 544 deletions
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 462b5352fea7..0ce332ad986b 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -171,7 +171,7 @@ config MMC_SDHCI_OF_ASPEED config MMC_SDHCI_OF_AT91 tristate "SDHCI OF support for the Atmel SDMMC controller" depends on MMC_SDHCI_PLTFM - depends on OF + depends on OF && HAVE_CLK help This selects the Atmel SDMMC driver @@ -235,6 +235,19 @@ config MMC_SDHCI_CNS3XXX If unsure, say N. +config MMC_SDHCI_ESDHC_MCF + tristate "SDHCI support for the Freescale eSDHC ColdFire controller" + depends on M5441x + depends on MMC_SDHCI_PLTFM + select MMC_SDHCI_IO_ACCESSORS + help + This selects the Freescale eSDHC controller support for + ColdFire mcf5441x devices. + + If you have a controller with this interface, say Y or M here. + + If unsure, say N. + config MMC_SDHCI_ESDHC_IMX tristate "SDHCI support for the Freescale eSDHC/uSDHC i.MX controller" depends on ARCH_MXC @@ -405,6 +418,20 @@ config MMC_MESON_GX If you have a controller with this interface, say Y here. +config MMC_MESON_MX_SDHC + tristate "Amlogic Meson SDHC Host Controller support" + depends on (ARM && ARCH_MESON) || COMPILE_TEST + depends on COMMON_CLK + depends on OF + help + This selects support for the SDHC Host Controller on + Amlogic Meson6, Meson8, Meson8b and Meson8m2 SoCs. + The controller supports the SD/SDIO Spec 3.x and eMMC Spec 4.5x + with 1, 4, and 8 bit bus widths. + + If you have a controller with this interface, say Y or M here. + If unsure, say N. + config MMC_MESON_MX_SDIO tristate "Amlogic Meson6/Meson8/Meson8b SD/MMC Host Controller support" depends on ARCH_MESON || COMPILE_TEST diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index b929ef941208..4d5bcb0144a0 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -68,6 +68,8 @@ obj-$(CONFIG_MMC_VUB300) += vub300.o obj-$(CONFIG_MMC_USHC) += ushc.o obj-$(CONFIG_MMC_WMT) += wmt-sdmmc.o obj-$(CONFIG_MMC_MESON_GX) += meson-gx-mmc.o +meson-mx-sdhc-objs := meson-mx-sdhc-clkc.o meson-mx-sdhc-mmc.o +obj-$(CONFIG_MMC_MESON_MX_SDHC) += meson-mx-sdhc.o obj-$(CONFIG_MMC_MESON_MX_SDIO) += meson-mx-sdio.o obj-$(CONFIG_MMC_MOXART) += moxart-mmc.o obj-$(CONFIG_MMC_SUNXI) += sunxi-mmc.o @@ -82,6 +84,7 @@ obj-$(CONFIG_MMC_REALTEK_USB) += rtsx_usb_sdmmc.o obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-pltfm.o obj-$(CONFIG_MMC_SDHCI_CADENCE) += sdhci-cadence.o obj-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.o +obj-$(CONFIG_MMC_SDHCI_ESDHC_MCF) += sdhci-esdhc-mcf.o obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX) += sdhci-esdhc-imx.o obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o obj-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci-tegra.o diff --git a/drivers/mmc/host/android-goldfish.c b/drivers/mmc/host/android-goldfish.c index 914e17bab3be..ceb4924e02d0 100644 --- a/drivers/mmc/host/android-goldfish.c +++ b/drivers/mmc/host/android-goldfish.c @@ -27,7 +27,6 @@ #include <linux/mutex.h> #include <linux/scatterlist.h> #include <linux/mmc/mmc.h> -#include <linux/mmc/sdio.h> #include <linux/mmc/host.h> #include <linux/mmc/card.h> @@ -404,14 +403,6 @@ static void goldfish_mmc_request(struct mmc_host *mmc, struct mmc_request *req) host->mrq = req; goldfish_mmc_prepare_data(host, req); goldfish_mmc_start_command(host, req->cmd); - - /* - * This is to avoid accidentally being detected as an SDIO card - * in mmc_attach_sdio(). - */ - if (req->cmd->opcode == SD_IO_SEND_OP_COND && - req->cmd->flags == (MMC_RSP_SPI_R4 | MMC_RSP_R4 | MMC_CMD_BCR)) - req->cmd->error = -EINVAL; } static void goldfish_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) @@ -482,6 +473,7 @@ static int goldfish_mmc_probe(struct platform_device *pdev) mmc->f_max = 24000000; mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; mmc->caps = MMC_CAP_4_BIT_DATA; + mmc->caps2 = MMC_CAP2_NO_SDIO; /* Use scatterlist DMA to reduce per-transfer costs. * NOTE max_seg_size assumption that small blocks aren't diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index aeaaa5314924..5cb692687698 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -169,6 +169,7 @@ #define atmci_writel(port, reg, value) \ __raw_writel((value), (port)->regs + reg) +#define ATMCI_CMD_TIMEOUT_MS 2000 #define AUTOSUSPEND_DELAY 50 #define ATMCI_DATA_ERROR_FLAGS (ATMCI_DCRCE | ATMCI_DTOE | ATMCI_OVRE | ATMCI_UNRE) @@ -808,6 +809,9 @@ static u32 atmci_prepare_command(struct mmc_host *mmc, static void atmci_send_command(struct atmel_mci *host, struct mmc_command *cmd, u32 cmd_flags) { + unsigned int timeout_ms = cmd->busy_timeout ? cmd->busy_timeout : + ATMCI_CMD_TIMEOUT_MS; + WARN_ON(host->cmd); host->cmd = cmd; @@ -817,6 +821,8 @@ static void atmci_send_command(struct atmel_mci *host, atmci_writel(host, ATMCI_ARGR, cmd->arg); atmci_writel(host, ATMCI_CMDR, cmd_flags); + + mod_timer(&host->timer, jiffies + msecs_to_jiffies(timeout_ms)); } static void atmci_send_stop_cmd(struct atmel_mci *host, struct mmc_data *data) @@ -1314,8 +1320,6 @@ static void atmci_start_request(struct atmel_mci *host, * prepared yet.) */ atmci_writel(host, ATMCI_IER, iflags); - - mod_timer(&host->timer, jiffies + msecs_to_jiffies(2000)); } static void atmci_queue_request(struct atmel_mci *host, @@ -1557,6 +1561,8 @@ static void atmci_request_end(struct atmel_mci *host, struct mmc_request *mrq) WARN_ON(host->cmd || host->data); + del_timer(&host->timer); + /* * Update the MMC clock rate if necessary. This may be * necessary if set_ios() is called when a different slot is @@ -1583,8 +1589,6 @@ static void atmci_request_end(struct atmel_mci *host, struct mmc_request *mrq) host->state = STATE_IDLE; } - del_timer(&host->timer); - spin_unlock(&host->lock); mmc_request_done(prev_mmc, mrq); spin_lock(&host->lock); diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c index 8823680ca42c..9bb1910268ca 100644 --- a/drivers/mmc/host/au1xmmc.c +++ b/drivers/mmc/host/au1xmmc.c @@ -259,7 +259,7 @@ static void au1xmmc_tasklet_finish(unsigned long param) au1xmmc_finish_request(host); } -static int au1xmmc_send_command(struct au1xmmc_host *host, int wait, +static int au1xmmc_send_command(struct au1xmmc_host *host, struct mmc_command *cmd, struct mmc_data *data) { u32 mmccmd = (cmd->opcode << SD_CMD_CI_SHIFT); @@ -302,9 +302,6 @@ static int au1xmmc_send_command(struct au1xmmc_host *host, int wait, __raw_writel(cmd->arg, HOST_CMDARG(host)); wmb(); /* drain writebuffer */ - if (wait) - IRQ_OFF(host, SD_CONFIG_CR); - __raw_writel((mmccmd | SD_CMD_GO), HOST_CMD(host)); wmb(); /* drain writebuffer */ @@ -312,19 +309,6 @@ static int au1xmmc_send_command(struct au1xmmc_host *host, int wait, while (__raw_readl(HOST_CMD(host)) & SD_CMD_GO) /* nop */; - /* Wait for the command to come back */ - if (wait) { - u32 status = __raw_readl(HOST_STATUS(host)); - - while (!(status & SD_STATUS_CR)) - status = __raw_readl(HOST_STATUS(host)); - - /* Clear the CR status */ - __raw_writel(SD_STATUS_CR, HOST_STATUS(host)); - - IRQ_ON(host, SD_CONFIG_CR); - } - return 0; } @@ -711,7 +695,7 @@ static void au1xmmc_request(struct mmc_host* mmc, struct mmc_request* mrq) } if (!ret) - ret = au1xmmc_send_command(host, 0, mrq->cmd, mrq->data); + ret = au1xmmc_send_command(host, mrq->cmd, mrq->data); if (ret) { mrq->cmd->error = ret; diff --git a/drivers/mmc/host/bcm2835.c b/drivers/mmc/host/bcm2835.c index c3d949847cbd..a0767790a826 100644 --- a/drivers/mmc/host/bcm2835.c +++ b/drivers/mmc/host/bcm2835.c @@ -1280,8 +1280,7 @@ static int bcm2835_add_host(struct bcm2835_host *host) /* host controller capabilities */ mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED | - MMC_CAP_NEEDS_POLL | MMC_CAP_HW_RESET | MMC_CAP_ERASE | - MMC_CAP_CMD23; + MMC_CAP_NEEDS_POLL | MMC_CAP_HW_RESET | MMC_CAP_CMD23; spin_lock_init(&host->lock); mutex_init(&host->mutex); diff --git a/drivers/mmc/host/cavium.c b/drivers/mmc/host/cavium.c index 89deb451e0ac..c5da3aaee334 100644 --- a/drivers/mmc/host/cavium.c +++ b/drivers/mmc/host/cavium.c @@ -1038,8 +1038,7 @@ int cvm_mmc_of_slot_probe(struct device *dev, struct cvm_mmc_host *host) * Disable bounce buffers for max_segs = 1 */ mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED | - MMC_CAP_ERASE | MMC_CAP_CMD23 | MMC_CAP_POWER_OFF_CARD | - MMC_CAP_3_3V_DDR; + MMC_CAP_CMD23 | MMC_CAP_POWER_OFF_CARD | MMC_CAP_3_3V_DDR; if (host->use_sg) mmc->max_segs = 16; diff --git a/drivers/mmc/host/cb710-mmc.c b/drivers/mmc/host/cb710-mmc.c index e33270e40539..e84ed84ea4cc 100644 --- a/drivers/mmc/host/cb710-mmc.c +++ b/drivers/mmc/host/cb710-mmc.c @@ -10,6 +10,8 @@ #include <linux/delay.h> #include "cb710-mmc.h" +#define CB710_MMC_REQ_TIMEOUT_MS 2000 + static const u8 cb710_clock_divider_log2[8] = { /* 1, 2, 4, 8, 16, 32, 128, 512 */ 0, 1, 2, 3, 4, 5, 7, 9 @@ -707,6 +709,12 @@ static int cb710_mmc_init(struct platform_device *pdev) mmc->f_min = val >> cb710_clock_divider_log2[CB710_MAX_DIVIDER_IDX]; mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34; mmc->caps = MMC_CAP_4_BIT_DATA; + /* + * In cb710_wait_for_event() we use a fixed timeout of ~2s, hence let's + * inform the core about it. A future improvement should instead make + * use of the cmd->busy_timeout. + */ + mmc->max_busy_timeout = CB710_MMC_REQ_TIMEOUT_MS; reader = mmc_priv(mmc); diff --git a/drivers/mmc/host/dw_mmc-k3.c b/drivers/mmc/host/dw_mmc-k3.c index 23b6f65b3785..50977ff18074 100644 --- a/drivers/mmc/host/dw_mmc-k3.c +++ b/drivers/mmc/host/dw_mmc-k3.c @@ -424,7 +424,7 @@ static int dw_mci_hi3660_switch_voltage(struct mmc_host *mmc, if (!IS_ERR(mmc->supply.vqmmc)) { ret = mmc_regulator_set_vqmmc(mmc, ios); - if (ret) { + if (ret < 0) { dev_err(host->dev, "Regulator set error %d\n", ret); return ret; } diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index bc5278ab5707..35ae5737c622 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1546,8 +1546,7 @@ static int dw_mci_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) if (!IS_ERR(mmc->supply.vqmmc)) { ret = mmc_regulator_set_vqmmc(mmc, ios); - - if (ret) { + if (ret < 0) { dev_dbg(&mmc->class_dev, "Regulator set error %d - %s V\n", ret, uhs & v18 ? "1.8" : "3.3"); @@ -2752,12 +2751,6 @@ static int dw_mci_init_slot_caps(struct dw_mci_slot *slot) if (host->pdata->caps) mmc->caps = host->pdata->caps; - /* - * Support MMC_CAP_ERASE by default. - * It needs to use trim/discard/erase commands. - */ - mmc->caps |= MMC_CAP_ERASE; - if (host->pdata->pm_caps) mmc->pm_caps = host->pdata->pm_caps; diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c index fbae87d1f017..cba7a6fcd178 100644 --- a/drivers/mmc/host/jz4740_mmc.c +++ b/drivers/mmc/host/jz4740_mmc.c @@ -108,6 +108,7 @@ #define JZ_MMC_LPM_LOW_POWER_MODE_EN BIT(0) #define JZ_MMC_CLK_RATE 24000000 +#define JZ_MMC_REQ_TIMEOUT_MS 5000 enum jz4740_mmc_version { JZ_MMC_JZ4740, @@ -440,7 +441,8 @@ static unsigned int jz4740_mmc_poll_irq(struct jz4740_mmc_host *host, if (timeout == 0) { set_bit(0, &host->waiting); - mod_timer(&host->timeout_timer, jiffies + 5*HZ); + mod_timer(&host->timeout_timer, + jiffies + msecs_to_jiffies(JZ_MMC_REQ_TIMEOUT_MS)); jz4740_mmc_set_irq_enabled(host, irq, true); return true; } @@ -893,7 +895,8 @@ static void jz4740_mmc_request(struct mmc_host *mmc, struct mmc_request *req) host->state = JZ4740_MMC_STATE_READ_RESPONSE; set_bit(0, &host->waiting); - mod_timer(&host->timeout_timer, jiffies + 5*HZ); + mod_timer(&host->timeout_timer, + jiffies + msecs_to_jiffies(JZ_MMC_REQ_TIMEOUT_MS)); jz4740_mmc_send_command(host, req->cmd); } @@ -1023,6 +1026,12 @@ static int jz4740_mmc_probe(struct platform_device* pdev) mmc->f_min = mmc->f_max / 128; mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; + /* + * We use a fixed timeout of 5s, hence inform the core about it. A + * future improvement should instead respect the cmd->busy_timeout. + */ + mmc->max_busy_timeout = JZ_MMC_REQ_TIMEOUT_MS; + mmc->max_blk_size = (1 << 10) - 1; mmc->max_blk_count = (1 << 15) - 1; mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; diff --git a/drivers/mmc/host/meson-gx-mmc.c b/drivers/mmc/host/meson-gx-mmc.c index 35400cf2a2e4..7eb38d7482c6 100644 --- a/drivers/mmc/host/meson-gx-mmc.c +++ b/drivers/mmc/host/meson-gx-mmc.c @@ -1004,6 +1004,8 @@ static int meson_mmc_card_busy(struct mmc_host *mmc) static int meson_mmc_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios) { + int ret; + /* vqmmc regulator is available */ if (!IS_ERR(mmc->supply.vqmmc)) { /* @@ -1013,7 +1015,8 @@ static int meson_mmc_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios) * to 1.8v. Please make sure the regulator framework is aware * of your own regulator constraints */ - return mmc_regulator_set_vqmmc(mmc, ios); + ret = mmc_regulator_set_vqmmc(mmc, ios); + return ret < 0 ? ret : 0; } /* no vqmmc regulator, assume fixed regulator at 3/3.3V */ diff --git a/drivers/mmc/host/meson-mx-sdhc-clkc.c b/drivers/mmc/host/meson-mx-sdhc-clkc.c new file mode 100644 index 000000000000..e1f29b279123 --- /dev/null +++ b/drivers/mmc/host/meson-mx-sdhc-clkc.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Amlogic Meson SDHC clock controller + * + * Copyright (C) 2020 Martin Blumenstingl <martin.blumenstingl@googlemail.com> + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/device.h> +#include <linux/platform_device.h> + +#include "meson-mx-sdhc.h" + +#define MESON_SDHC_NUM_BUILTIN_CLKS 6 + +struct meson_mx_sdhc_clkc { + struct clk_mux src_sel; + struct clk_divider div; + struct clk_gate mod_clk_en; + struct clk_gate tx_clk_en; + struct clk_gate rx_clk_en; + struct clk_gate sd_clk_en; +}; + +static const struct clk_parent_data meson_mx_sdhc_src_sel_parents[4] = { + { .fw_name = "clkin0" }, + { .fw_name = "clkin1" }, + { .fw_name = "clkin2" }, + { .fw_name = "clkin3" }, +}; + +static const struct clk_div_table meson_mx_sdhc_div_table[] = { + { .div = 6, .val = 5, }, + { .div = 8, .val = 7, }, + { .div = 9, .val = 8, }, + { .div = 10, .val = 9, }, + { .div = 12, .val = 11, }, + { .div = 16, .val = 15, }, + { .div = 18, .val = 17, }, + { .div = 34, .val = 33, }, + { .div = 142, .val = 141, }, + { .div = 850, .val = 849, }, + { .div = 2126, .val = 2125, }, + { .div = 4096, .val = 4095, }, + { /* sentinel */ } +}; + +static int meson_mx_sdhc_clk_hw_register(struct device *dev, + const char *name_suffix, + const struct clk_parent_data *parents, + unsigned int num_parents, + const struct clk_ops *ops, + struct clk_hw *hw) +{ + struct clk_init_data init = { }; + char clk_name[32]; + + snprintf(clk_name, sizeof(clk_name), "%s#%s", dev_name(dev), + name_suffix); + + init.name = clk_name; + init.ops = ops; + init.flags = CLK_SET_RATE_PARENT; + init.parent_data = parents; + init.num_parents = num_parents; + + hw->init = &init; + + return devm_clk_hw_register(dev, hw); +} + +static int meson_mx_sdhc_gate_clk_hw_register(struct device *dev, + const char *name_suffix, + struct clk_hw *parent, + struct clk_hw *hw) +{ + struct clk_parent_data parent_data = { .hw = parent }; + + return meson_mx_sdhc_clk_hw_register(dev, name_suffix, &parent_data, 1, + &clk_gate_ops, hw); +} + +int meson_mx_sdhc_register_clkc(struct device *dev, void __iomem *base, + struct clk_bulk_data *clk_bulk_data) +{ + struct clk_parent_data div_parent = { }; + struct meson_mx_sdhc_clkc *clkc_data; + int ret; + + clkc_data = devm_kzalloc(dev, sizeof(*clkc_data), GFP_KERNEL); + if (!clkc_data) + return -ENOMEM; + + clkc_data->src_sel.reg = base + MESON_SDHC_CLKC; + clkc_data->src_sel.mask = 0x3; + clkc_data->src_sel.shift = 16; + ret = meson_mx_sdhc_clk_hw_register(dev, "src_sel", + meson_mx_sdhc_src_sel_parents, 4, + &clk_mux_ops, + &clkc_data->src_sel.hw); + if (ret) + return ret; + + clkc_data->div.reg = base + MESON_SDHC_CLKC; + clkc_data->div.shift = 0; + clkc_data->div.width = 12; + clkc_data->div.table = meson_mx_sdhc_div_table; + div_parent.hw = &clkc_data->src_sel.hw; + ret = meson_mx_sdhc_clk_hw_register(dev, "div", &div_parent, 1, + &clk_divider_ops, + &clkc_data->div.hw); + if (ret) + return ret; + + clkc_data->mod_clk_en.reg = base + MESON_SDHC_CLKC; + clkc_data->mod_clk_en.bit_idx = 15; + ret = meson_mx_sdhc_gate_clk_hw_register(dev, "mod_clk_on", + &clkc_data->div.hw, + &clkc_data->mod_clk_en.hw); + if (ret) + return ret; + + clkc_data->tx_clk_en.reg = base + MESON_SDHC_CLKC; + clkc_data->tx_clk_en.bit_idx = 14; + ret = meson_mx_sdhc_gate_clk_hw_register(dev, "tx_clk_on", + &clkc_data->div.hw, + &clkc_data->tx_clk_en.hw); + if (ret) + return ret; + + clkc_data->rx_clk_en.reg = base + MESON_SDHC_CLKC; + clkc_data->rx_clk_en.bit_idx = 13; + ret = meson_mx_sdhc_gate_clk_hw_register(dev, "rx_clk_on", + &clkc_data->div.hw, + &clkc_data->rx_clk_en.hw); + if (ret) + return ret; + + clkc_data->sd_clk_en.reg = base + MESON_SDHC_CLKC; + clkc_data->sd_clk_en.bit_idx = 12; + ret = meson_mx_sdhc_gate_clk_hw_register(dev, "sd_clk_on", + &clkc_data->div.hw, + &clkc_data->sd_clk_en.hw); + if (ret) + return ret; + + /* + * TODO: Replace clk_hw.clk with devm_clk_hw_get_clk() once that is + * available. + */ + clk_bulk_data[0].clk = clkc_data->mod_clk_en.hw.clk; + clk_bulk_data[1].clk = clkc_data->sd_clk_en.hw.clk; + clk_bulk_data[2].clk = clkc_data->tx_clk_en.hw.clk; + clk_bulk_data[3].clk = clkc_data->rx_clk_en.hw.clk; + + return 0; +} diff --git a/drivers/mmc/host/meson-mx-sdhc-mmc.c b/drivers/mmc/host/meson-mx-sdhc-mmc.c new file mode 100644 index 000000000000..53e3f6a4245a --- /dev/null +++ b/drivers/mmc/host/meson-mx-sdhc-mmc.c @@ -0,0 +1,914 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Amlogic Meson6/Meson8/Meson8b/Meson8m2 SDHC MMC host controller driver. + * + * Copyright (C) 2020 Martin Blumenstingl <martin.blumenstingl@googlemail.com> + */ + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/interrupt.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/types.h> + +#include <linux/mmc/host.h> +#include <linux/mmc/mmc.h> +#include <linux/mmc/sdio.h> +#include <linux/mmc/slot-gpio.h> + +#include "meson-mx-sdhc.h" + +#define MESON_SDHC_NUM_BULK_CLKS 4 +#define MESON_SDHC_MAX_BLK_SIZE 512 +#define MESON_SDHC_NUM_TUNING_TRIES 10 + +#define MESON_SDHC_WAIT_CMD_READY_SLEEP_US 1 +#define MESON_SDHC_WAIT_CMD_READY_TIMEOUT_US 100000 +#define MESON_SDHC_WAIT_BEFORE_SEND_SLEEP_US 1 +#define MESON_SDHC_WAIT_BEFORE_SEND_TIMEOUT_US 200 + +struct meson_mx_sdhc_data { + void (*init_hw)(struct mmc_host *mmc); + void (*set_pdma)(struct mmc_host *mmc); + void (*wait_before_send)(struct mmc_host *mmc); + bool hardware_flush_all_cmds; +}; + +struct meson_mx_sdhc_host { + struct mmc_host *mmc; + + struct mmc_request *mrq; + struct mmc_command *cmd; + int error; + + struct regmap *regmap; + + struct clk *pclk; + struct clk *sd_clk; + struct clk_bulk_data bulk_clks[MESON_SDHC_NUM_BULK_CLKS]; + bool bulk_clks_enabled; + + const struct meson_mx_sdhc_data *platform; +}; + +static const struct regmap_config meson_mx_sdhc_regmap_config = { + .reg_bits = 8, + .val_bits = 32, + .reg_stride = 4, + .max_register = MESON_SDHC_CLK2, +}; + +static void meson_mx_sdhc_hw_reset(struct mmc_host *mmc) +{ + struct meson_mx_sdhc_host *host = mmc_priv(mmc); + + regmap_write(host->regmap, MESON_SDHC_SRST, MESON_SDHC_SRST_MAIN_CTRL | + MESON_SDHC_SRST_RXFIFO | MESON_SDHC_SRST_TXFIFO | + MESON_SDHC_SRST_DPHY_RX | MESON_SDHC_SRST_DPHY_TX | + MESON_SDHC_SRST_DMA_IF); + usleep_range(10, 100); + + regmap_write(host->regmap, MESON_SDHC_SRST, 0); + usleep_range(10, 100); +} + +static void meson_mx_sdhc_clear_fifo(struct mmc_host *mmc) +{ + struct meson_mx_sdhc_host *host = mmc_priv(mmc); + u32 stat; + + regmap_read(host->regmap, MESON_SDHC_STAT, &stat); + if (!FIELD_GET(MESON_SDHC_STAT_RXFIFO_CNT, stat) && + !FIELD_GET(MESON_SDHC_STAT_TXFIFO_CNT, stat)) + return; + + regmap_write(host->regmap, MESON_SDHC_SRST, MESON_SDHC_SRST_RXFIFO | + MESON_SDHC_SRST_TXFIFO | MESON_SDHC_SRST_MAIN_CTRL); + udelay(5); + + regmap_read(host->regmap, MESON_SDHC_STAT, &stat); + if (FIELD_GET(MESON_SDHC_STAT_RXFIFO_CNT, stat) || + FIELD_GET(MESON_SDHC_STAT_TXFIFO_CNT, stat)) + dev_warn(mmc_dev(host->mmc), + "Failed to clear FIFOs, RX: %lu, TX: %lu\n", + FIELD_GET(MESON_SDHC_STAT_RXFIFO_CNT, stat), + FIELD_GET(MESON_SDHC_STAT_TXFIFO_CNT, stat)); +} + +static void meson_mx_sdhc_wait_cmd_ready(struct mmc_host *mmc) +{ + struct meson_mx_sdhc_host *host = mmc_priv(mmc); + u32 stat, esta; + int ret; + + ret = regmap_read_poll_timeout(host->regmap, MESON_SDHC_STAT, stat, + !(stat & MESON_SDHC_STAT_CMD_BUSY), + MESON_SDHC_WAIT_CMD_READY_SLEEP_US, + MESON_SDHC_WAIT_CMD_READY_TIMEOUT_US); + if (ret) { + dev_warn(mmc_dev(mmc), + "Failed to poll for CMD_BUSY while processing CMD%d\n", + host->cmd->opcode); + meson_mx_sdhc_hw_reset(mmc); + } + + ret = regmap_read_poll_timeout(host->regmap, MESON_SDHC_ESTA, esta, + !(esta & MESON_SDHC_ESTA_11_13), + MESON_SDHC_WAIT_CMD_READY_SLEEP_US, + MESON_SDHC_WAIT_CMD_READY_TIMEOUT_US); + if (ret) { + dev_warn(mmc_dev(mmc), + "Failed to poll for ESTA[13:11] while processing CMD%d\n", + host->cmd->opcode); + meson_mx_sdhc_hw_reset(mmc); + } +} + +static void meson_mx_sdhc_start_cmd(struct mmc_host *mmc, + struct mmc_command *cmd) +{ + struct meson_mx_sdhc_host *host = mmc_priv(mmc); + u32 ictl, send; + int pack_len; + + host->cmd = cmd; + + ictl = MESON_SDHC_ICTL_DATA_TIMEOUT | MESON_SDHC_ICTL_DATA_ERR_CRC | + MESON_SDHC_ICTL_RXFIFO_FULL | MESON_SDHC_ICTL_TXFIFO_EMPTY | + MESON_SDHC_ICTL_RESP_TIMEOUT | MESON_SDHC_ICTL_RESP_ERR_CRC; + + send = FIELD_PREP(MESON_SDHC_SEND_CMD_INDEX, cmd->opcode); + + if (cmd->data) { + send |= MESON_SDHC_SEND_CMD_HAS_DATA; + send |= FIELD_PREP(MESON_SDHC_SEND_TOTAL_PACK, + cmd->data->blocks - 1); + + if (cmd->data->blksz < MESON_SDHC_MAX_BLK_SIZE) + pack_len = cmd->data->blksz; + else + pack_len = 0; + + if (cmd->data->flags & MMC_DATA_WRITE) + send |= MESON_SDHC_SEND_DATA_DIR; + + /* + * If command with no data, just wait response done + * interrupt(int[0]), and if command with data transfer, just + * wait dma done interrupt(int[11]), don't need care about + * dat0 busy or not. + */ + if (host->platform->hardware_flush_all_cmds || + cmd->data->flags & MMC_DATA_WRITE) + /* hardware flush: */ + ictl |= MESON_SDHC_ICTL_DMA_DONE; + else + /* software flush: */ + ictl |= MESON_SDHC_ICTL_DATA_XFER_OK; + } else { + pack_len = 0; + + ictl |= MESON_SDHC_ICTL_RESP_OK; + } + + if (cmd->opcode == MMC_STOP_TRANSMISSION) + send |= MESON_SDHC_SEND_DATA_STOP; + + if (cmd->flags & MMC_RSP_PRESENT) + send |= MESON_SDHC_SEND_CMD_HAS_RESP; + + if (cmd->flags & MMC_RSP_136) { + send |= MESON_SDHC_SEND_RESP_LEN; + send |= MESON_SDHC_SEND_RESP_NO_CRC; + } + + if (!(cmd->flags & MMC_RSP_CRC)) + send |= MESON_SDHC_SEND_RESP_NO_CRC; + + if (cmd->flags & MMC_RSP_BUSY) + send |= MESON_SDHC_SEND_R1B; + + /* enable the new IRQs and mask all pending ones */ + regmap_write(host->regmap, MESON_SDHC_ICTL, ictl); + regmap_write(host->regmap, MESON_SDHC_ISTA, MESON_SDHC_ISTA_ALL_IRQS); + + regmap_write(host->regmap, MESON_SDHC_ARGU, cmd->arg); + + regmap_update_bits(host->regmap, MESON_SDHC_CTRL, + MESON_SDHC_CTRL_PACK_LEN, + FIELD_PREP(MESON_SDHC_CTRL_PACK_LEN, pack_len)); + + if (cmd->data) + regmap_write(host->regmap, MESON_SDHC_ADDR, + sg_dma_address(cmd->data->sg)); + + meson_mx_sdhc_wait_cmd_ready(mmc); + + if (cmd->data) + host->platform->set_pdma(mmc); + + if (host->platform->wait_before_send) + host->platform->wait_before_send(mmc); + + regmap_write(host->regmap, MESON_SDHC_SEND, send); +} + +static void meson_mx_sdhc_disable_clks(struct mmc_host *mmc) +{ + struct meson_mx_sdhc_host *host = mmc_priv(mmc); + + if (!host->bulk_clks_enabled) + return; + + clk_bulk_disable_unprepare(MESON_SDHC_NUM_BULK_CLKS, host->bulk_clks); + + host->bulk_clks_enabled = false; +} + +static int meson_mx_sdhc_enable_clks(struct mmc_host *mmc) +{ + struct meson_mx_sdhc_host *host = mmc_priv(mmc); + int ret; + + if (host->bulk_clks_enabled) + return 0; + + ret = clk_bulk_prepare_enable(MESON_SDHC_NUM_BULK_CLKS, + host->bulk_clks); + if (ret) + return ret; + + host->bulk_clks_enabled = true; + + return 0; +} + +static int meson_mx_sdhc_set_clk(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct meson_mx_sdhc_host *host = mmc_priv(mmc); + u32 rx_clk_phase; + int ret; + + meson_mx_sdhc_disable_clks(mmc); + + if (ios->clock) { + ret = clk_set_rate(host->sd_clk, ios->clock); + if (ret) { + dev_warn(mmc_dev(mmc), + "Failed to set MMC clock to %uHz: %d\n", + ios->clock, host->error); + return ret; + } + + ret = meson_mx_sdhc_enable_clks(mmc); + if (ret) + return ret; + + mmc->actual_clock = clk_get_rate(host->sd_clk); + + /* + * according to Amlogic the following latching points are + * selected with empirical values, there is no (known) formula + * to calculate these. + */ + if (mmc->actual_clock > 100000000) { + rx_clk_phase = 1; + } else if (mmc->actual_clock > 45000000) { + if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) + rx_clk_phase = 15; + else + rx_clk_phase = 11; + } else if (mmc->actual_clock >= 25000000) { + rx_clk_phase = 15; + } else if (mmc->actual_clock > 5000000) { + rx_clk_phase = 23; + } else if (mmc->actual_clock > 1000000) { + rx_clk_phase = 55; + } else { + rx_clk_phase = 1061; + } + + regmap_update_bits(host->regmap, MESON_SDHC_CLK2, + MESON_SDHC_CLK2_RX_CLK_PHASE, + FIELD_PREP(MESON_SDHC_CLK2_RX_CLK_PHASE, + rx_clk_phase)); + } else { + mmc->actual_clock = 0; + } + + return 0; +} + +static void meson_mx_sdhc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct meson_mx_sdhc_host *host = mmc_priv(mmc); + unsigned short vdd = ios->vdd; + + switch (ios->power_mode) { + case MMC_POWER_OFF: + vdd = 0; + fallthrough; + + case MMC_POWER_UP: + if (!IS_ERR(mmc->supply.vmmc)) { + host->error = mmc_regulator_set_ocr(mmc, + mmc->supply.vmmc, + vdd); + if (host->error) + return; + } + + break; + + case MMC_POWER_ON: + break; + } + + host->error = meson_mx_sdhc_set_clk(mmc, ios); + if (host->error) + return; + + switch (ios->bus_width) { + case MMC_BUS_WIDTH_1: + regmap_update_bits(host->regmap, MESON_SDHC_CTRL, + MESON_SDHC_CTRL_DAT_TYPE, + FIELD_PREP(MESON_SDHC_CTRL_DAT_TYPE, 0)); + break; + + case MMC_BUS_WIDTH_4: + regmap_update_bits(host->regmap, MESON_SDHC_CTRL, + MESON_SDHC_CTRL_DAT_TYPE, + FIELD_PREP(MESON_SDHC_CTRL_DAT_TYPE, 1)); + break; + + case MMC_BUS_WIDTH_8: + regmap_update_bits(host->regmap, MESON_SDHC_CTRL, + MESON_SDHC_CTRL_DAT_TYPE, + FIELD_PREP(MESON_SDHC_CTRL_DAT_TYPE, 2)); + break; + + default: + dev_err(mmc_dev(mmc), "unsupported bus width: %d\n", + ios->bus_width); + host->error = -EINVAL; + return; + } +} + +static int meson_mx_sdhc_map_dma(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct mmc_data *data = mrq->data; + int dma_len; + + if (!data) + return 0; + + dma_len = dma_map_sg(mmc_dev(mmc), data->sg, data->sg_len, + mmc_get_dma_dir(data)); + if (dma_len <= 0) { + dev_err(mmc_dev(mmc), "dma_map_sg failed\n"); + return -ENOMEM; + } + + return 0; +} + +static void meson_mx_sdhc_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct meson_mx_sdhc_host *host = mmc_priv(mmc); + struct mmc_command *cmd = mrq->cmd; + + if (!host->error) + host->error = meson_mx_sdhc_map_dma(mmc, mrq); + + if (host->error) { + cmd->error = host->error; + mmc_request_done(mmc, mrq); + return; + } + + host->mrq = mrq; + + meson_mx_sdhc_start_cmd(mmc, mrq->cmd); +} + +static int meson_mx_sdhc_card_busy(struct mmc_host *mmc) +{ + struct meson_mx_sdhc_host *host = mmc_priv(mmc); + u32 stat; + + regmap_read(host->regmap, MESON_SDHC_STAT, &stat); + return FIELD_GET(MESON_SDHC_STAT_DAT3_0, stat) == 0; +} + +static bool meson_mx_sdhc_tuning_point_matches(struct mmc_host *mmc, + u32 opcode) +{ + unsigned int i, num_matches = 0; + int ret; + + for (i = 0; i < MESON_SDHC_NUM_TUNING_TRIES; i++) { + ret = mmc_send_tuning(mmc, opcode, NULL); + if (!ret) + num_matches++; + } + + return num_matches == MESON_SDHC_NUM_TUNING_TRIES; +} + +static int meson_mx_sdhc_execute_tuning(struct mmc_host *mmc, u32 opcode) +{ + struct meson_mx_sdhc_host *host = mmc_priv(mmc); + int div, start, len, best_start, best_len; + int curr_phase, old_phase, new_phase; + u32 val; + + len = 0; + start = 0; + best_len = 0; + + regmap_read(host->regmap, MESON_SDHC_CLK2, &val); + old_phase = FIELD_GET(MESON_SDHC_CLK2_RX_CLK_PHASE, val); + + regmap_read(host->regmap, MESON_SDHC_CLKC, &val); + div = FIELD_GET(MESON_SDHC_CLKC_CLK_DIV, val); + + for (curr_phase = 0; curr_phase <= div; curr_phase++) { + regmap_update_bits(host->regmap, MESON_SDHC_CLK2, + MESON_SDHC_CLK2_RX_CLK_PHASE, + FIELD_PREP(MESON_SDHC_CLK2_RX_CLK_PHASE, + curr_phase)); + + if (meson_mx_sdhc_tuning_point_matches(mmc, opcode)) { + if (!len) { + start = curr_phase; + + dev_dbg(mmc_dev(mmc), + "New RX phase window starts at %u\n", + start); + } + + len++; + } else { + if (len > best_len) { + best_start = start; + best_len = len; + + dev_dbg(mmc_dev(mmc), + "New best RX phase window: %u - %u\n", + best_start, best_start + best_len); + } + + /* reset the current window */ + len = 0; + } + } + + if (len > best_len) + /* the last window is the best (or possibly only) window */ + new_phase = start + (len / 2); + else if (best_len) + /* there was a better window than the last */ + new_phase = best_start + (best_len / 2); + else + /* no window was found at all, reset to the original phase */ + new_phase = old_phase; + + regmap_update_bits(host->regmap, MESON_SDHC_CLK2, + MESON_SDHC_CLK2_RX_CLK_PHASE, + FIELD_PREP(MESON_SDHC_CLK2_RX_CLK_PHASE, + new_phase)); + + if (!len && !best_len) + return -EIO; + + dev_dbg(mmc_dev(mmc), "Tuned RX clock phase to %u\n", new_phase); + + return 0; +} + +static const struct mmc_host_ops meson_mx_sdhc_ops = { + .hw_reset = meson_mx_sdhc_hw_reset, + .request = meson_mx_sdhc_request, + .set_ios = meson_mx_sdhc_set_ios, + .card_busy = meson_mx_sdhc_card_busy, + .execute_tuning = meson_mx_sdhc_execute_tuning, + .get_cd = mmc_gpio_get_cd, + .get_ro = mmc_gpio_get_ro, +}; + +static void meson_mx_sdhc_request_done(struct meson_mx_sdhc_host *host) +{ + struct mmc_request *mrq = host->mrq; + struct mmc_host *mmc = host->mmc; + + /* disable interrupts and mask all pending ones */ + regmap_update_bits(host->regmap, MESON_SDHC_ICTL, + MESON_SDHC_ICTL_ALL_IRQS, 0); + regmap_update_bits(host->regmap, MESON_SDHC_ISTA, + MESON_SDHC_ISTA_ALL_IRQS, MESON_SDHC_ISTA_ALL_IRQS); + + host->mrq = NULL; + host->cmd = NULL; + + mmc_request_done(mmc, mrq); +} + +static u32 meson_mx_sdhc_read_response(struct meson_mx_sdhc_host *host, u8 idx) +{ + u32 val; + + regmap_update_bits(host->regmap, MESON_SDHC_PDMA, + MESON_SDHC_PDMA_DMA_MODE, 0); + + regmap_update_bits(host->regmap, MESON_SDHC_PDMA, + MESON_SDHC_PDMA_PIO_RDRESP, + FIELD_PREP(MESON_SDHC_PDMA_PIO_RDRESP, idx)); + + regmap_read(host->regmap, MESON_SDHC_ARGU, &val); + + return val; +} + +static irqreturn_t meson_mx_sdhc_irq(int irq, void *data) +{ + struct meson_mx_sdhc_host *host = data; + struct mmc_command *cmd = host->cmd; + u32 ictl, ista; + + regmap_read(host->regmap, MESON_SDHC_ICTL, &ictl); + regmap_read(host->regmap, MESON_SDHC_ISTA, &ista); + + if (!(ictl & ista)) + return IRQ_NONE; + + if (ista & MESON_SDHC_ISTA_RXFIFO_FULL || + ista & MESON_SDHC_ISTA_TXFIFO_EMPTY) + cmd->error = -EIO; + else if (ista & MESON_SDHC_ISTA_RESP_ERR_CRC) + cmd->error = -EILSEQ; + else if (ista & MESON_SDHC_ISTA_RESP_TIMEOUT) + cmd->error = -ETIMEDOUT; + + if (cmd->data) { + if (ista & MESON_SDHC_ISTA_DATA_ERR_CRC) + cmd->data->error = -EILSEQ; + else if (ista & MESON_SDHC_ISTA_DATA_TIMEOUT) + cmd->data->error = -ETIMEDOUT; + } + + if (cmd->error || (cmd->data && cmd->data->error)) + dev_dbg(mmc_dev(host->mmc), "CMD%d error, ISTA: 0x%08x\n", + cmd->opcode, ista); + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t meson_mx_sdhc_irq_thread(int irq, void *irq_data) +{ + struct meson_mx_sdhc_host *host = irq_data; + struct mmc_command *cmd; + u32 val; + + cmd = host->cmd; + if (WARN_ON(!cmd)) + return IRQ_HANDLED; + + if (cmd->data && !cmd->data->error) { + if (!host->platform->hardware_flush_all_cmds && + cmd->data->flags & MMC_DATA_READ) { + meson_mx_sdhc_wait_cmd_ready(host->mmc); + + /* + * If MESON_SDHC_PDMA_RXFIFO_MANUAL_FLUSH was + * previously 0x1 then it has to be set to 0x3. If it + * was 0x0 before then it has to be set to 0x2. Without + * this reading SD cards sometimes transfers garbage, + * which results in cards not being detected due to: + * unrecognised SCR structure version <random number> + */ + val = FIELD_PREP(MESON_SDHC_PDMA_RXFIFO_MANUAL_FLUSH, + 2); + regmap_update_bits(host->regmap, MESON_SDHC_PDMA, val, + val); + } + + dma_unmap_sg(mmc_dev(host->mmc), cmd->data->sg, + cmd->data->sg_len, mmc_get_dma_dir(cmd->data)); + + cmd->data->bytes_xfered = cmd->data->blksz * cmd->data->blocks; + } + + meson_mx_sdhc_wait_cmd_ready(host->mmc); + + if (cmd->flags & MMC_RSP_136) { + cmd->resp[0] = meson_mx_sdhc_read_response(host, 4); + cmd->resp[1] = meson_mx_sdhc_read_response(host, 3); + cmd->resp[2] = meson_mx_sdhc_read_response(host, 2); + cmd->resp[3] = meson_mx_sdhc_read_response(host, 1); + } else { + cmd->resp[0] = meson_mx_sdhc_read_response(host, 0); + } + + if (cmd->error == -EIO || cmd->error == -ETIMEDOUT) + meson_mx_sdhc_hw_reset(host->mmc); + else if (cmd->data) + /* + * Clear the FIFOs after completing data transfers to prevent + * corrupting data on write access. It's not clear why this is + * needed (for reads and writes), but it mimics what the BSP + * kernel did. + */ + meson_mx_sdhc_clear_fifo(host->mmc); + + meson_mx_sdhc_request_done(host); + + return IRQ_HANDLED; +} + +static void meson_mx_sdhc_init_hw_meson8(struct mmc_host *mmc) +{ + struct meson_mx_sdhc_host *host = mmc_priv(mmc); + + regmap_write(host->regmap, MESON_SDHC_MISC, + FIELD_PREP(MESON_SDHC_MISC_TXSTART_THRES, 7) | + FIELD_PREP(MESON_SDHC_MISC_WCRC_ERR_PATT, 5) | + FIELD_PREP(MESON_SDHC_MISC_WCRC_OK_PATT, 2)); + + regmap_write(host->regmap, MESON_SDHC_ENHC, + FIELD_PREP(MESON_SDHC_ENHC_RXFIFO_TH, 63) | + MESON_SDHC_ENHC_MESON6_DMA_WR_RESP | + FIELD_PREP(MESON_SDHC_ENHC_MESON6_RX_TIMEOUT, 255) | + FIELD_PREP(MESON_SDHC_ENHC_SDIO_IRQ_PERIOD, 12)); +}; + +static void meson_mx_sdhc_set_pdma_meson8(struct mmc_host *mmc) +{ + struct meson_mx_sdhc_host *host = mmc_priv(mmc); + + if (host->cmd->data->flags & MMC_DATA_WRITE) + regmap_update_bits(host->regmap, MESON_SDHC_PDMA, + MESON_SDHC_PDMA_DMA_MODE | + MESON_SDHC_PDMA_RD_BURST | + MESON_SDHC_PDMA_TXFIFO_FILL, + MESON_SDHC_PDMA_DMA_MODE | + FIELD_PREP(MESON_SDHC_PDMA_RD_BURST, 31) | + MESON_SDHC_PDMA_TXFIFO_FILL); + else + regmap_update_bits(host->regmap, MESON_SDHC_PDMA, + MESON_SDHC_PDMA_DMA_MODE | + MESON_SDHC_PDMA_RXFIFO_MANUAL_FLUSH, + MESON_SDHC_PDMA_DMA_MODE | + FIELD_PREP(MESON_SDHC_PDMA_RXFIFO_MANUAL_FLUSH, + 1)); + + if (host->cmd->data->flags & MMC_DATA_WRITE) + regmap_update_bits(host->regmap, MESON_SDHC_PDMA, + MESON_SDHC_PDMA_RD_BURST, + FIELD_PREP(MESON_SDHC_PDMA_RD_BURST, 15)); +} + +static void meson_mx_sdhc_wait_before_send_meson8(struct mmc_host *mmc) +{ + struct meson_mx_sdhc_host *host = mmc_priv(mmc); + u32 val; + int ret; + + ret = regmap_read_poll_timeout(host->regmap, MESON_SDHC_ESTA, val, + val == 0, + MESON_SDHC_WAIT_BEFORE_SEND_SLEEP_US, + MESON_SDHC_WAIT_BEFORE_SEND_TIMEOUT_US); + if (ret) + dev_warn(mmc_dev(mmc), + "Failed to wait for ESTA to clear: 0x%08x\n", val); + + if (host->cmd->data && host->cmd->data->flags & MMC_DATA_WRITE) { + ret = regmap_read_poll_timeout(host->regmap, MESON_SDHC_STAT, + val, val & MESON_SDHC_STAT_TXFIFO_CNT, + MESON_SDHC_WAIT_BEFORE_SEND_SLEEP_US, + MESON_SDHC_WAIT_BEFORE_SEND_TIMEOUT_US); + if (ret) + dev_warn(mmc_dev(mmc), + "Failed to wait for TX FIFO to fill\n"); + } +} + +static void meson_mx_sdhc_init_hw_meson8m2(struct mmc_host *mmc) +{ + struct meson_mx_sdhc_host *host = mmc_priv(mmc); + + regmap_write(host->regmap, MESON_SDHC_MISC, + FIELD_PREP(MESON_SDHC_MISC_TXSTART_THRES, 6) | + FIELD_PREP(MESON_SDHC_MISC_WCRC_ERR_PATT, 5) | + FIELD_PREP(MESON_SDHC_MISC_WCRC_OK_PATT, 2)); + + regmap_write(host->regmap, MESON_SDHC_ENHC, + FIELD_PREP(MESON_SDHC_ENHC_RXFIFO_TH, 64) | + FIELD_PREP(MESON_SDHC_ENHC_MESON8M2_DEBUG, 1) | + MESON_SDHC_ENHC_MESON8M2_WRRSP_MODE | + FIELD_PREP(MESON_SDHC_ENHC_SDIO_IRQ_PERIOD, 12)); +} + +static void meson_mx_sdhc_set_pdma_meson8m2(struct mmc_host *mmc) +{ + struct meson_mx_sdhc_host *host = mmc_priv(mmc); + + regmap_update_bits(host->regmap, MESON_SDHC_PDMA, + MESON_SDHC_PDMA_DMA_MODE, MESON_SDHC_PDMA_DMA_MODE); +} + +static void meson_mx_sdhc_init_hw(struct mmc_host *mmc) +{ + struct meson_mx_sdhc_host *host = mmc_priv(mmc); + + meson_mx_sdhc_hw_reset(mmc); + + regmap_write(host->regmap, MESON_SDHC_CTRL, + FIELD_PREP(MESON_SDHC_CTRL_RX_PERIOD, 0xf) | + FIELD_PREP(MESON_SDHC_CTRL_RX_TIMEOUT, 0x7f) | + FIELD_PREP(MESON_SDHC_CTRL_RX_ENDIAN, 0x7) | + FIELD_PREP(MESON_SDHC_CTRL_TX_ENDIAN, 0x7)); + + /* + * start with a valid divider and enable the memory (un-setting + * MESON_SDHC_CLKC_MEM_PWR_OFF). + */ + regmap_write(host->regmap, MESON_SDHC_CLKC, MESON_SDHC_CLKC_CLK_DIV); + + regmap_write(host->regmap, MESON_SDHC_CLK2, + FIELD_PREP(MESON_SDHC_CLK2_SD_CLK_PHASE, 1)); + + regmap_write(host->regmap, MESON_SDHC_PDMA, + MESON_SDHC_PDMA_DMA_URGENT | + FIELD_PREP(MESON_SDHC_PDMA_WR_BURST, 7) | + FIELD_PREP(MESON_SDHC_PDMA_TXFIFO_TH, 49) | + FIELD_PREP(MESON_SDHC_PDMA_RD_BURST, 15) | + FIELD_PREP(MESON_SDHC_PDMA_RXFIFO_TH, 7)); + + /* some initialization bits depend on the SoC: */ + host->platform->init_hw(mmc); + + /* disable and mask all interrupts: */ + regmap_write(host->regmap, MESON_SDHC_ICTL, 0); + regmap_write(host->regmap, MESON_SDHC_ISTA, MESON_SDHC_ISTA_ALL_IRQS); +} + +static int meson_mx_sdhc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct meson_mx_sdhc_host *host; + struct mmc_host *mmc; + void __iomem *base; + int ret, irq; + + mmc = mmc_alloc_host(sizeof(*host), dev); + if (!mmc) + return -ENOMEM; + + ret = devm_add_action_or_reset(dev, (void(*)(void *))mmc_free_host, + mmc); + if (ret) { + dev_err(dev, "Failed to register mmc_free_host action\n"); + return ret; + } + + host = mmc_priv(mmc); + host->mmc = mmc; + + platform_set_drvdata(pdev, host); + + host->platform = device_get_match_data(dev); + if (!host->platform) + return -EINVAL; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + host->regmap = devm_regmap_init_mmio(dev, base, + &meson_mx_sdhc_regmap_config); + if (IS_ERR(host->regmap)) + return PTR_ERR(host->regmap); + + host->pclk = devm_clk_get(dev, "pclk"); + if (IS_ERR(host->pclk)) + return PTR_ERR(host->pclk); + + /* accessing any register requires the module clock to be enabled: */ + ret = clk_prepare_enable(host->pclk); + if (ret) { + dev_err(dev, "Failed to enable 'pclk' clock\n"); + return ret; + } + + meson_mx_sdhc_init_hw(mmc); + + ret = meson_mx_sdhc_register_clkc(dev, base, host->bulk_clks); + if (ret) + goto err_disable_pclk; + + host->sd_clk = host->bulk_clks[1].clk; + + /* Get regulators and the supported OCR mask */ + ret = mmc_regulator_get_supply(mmc); + if (ret) + goto err_disable_pclk; + + mmc->max_req_size = SZ_128K; + mmc->max_seg_size = mmc->max_req_size; + mmc->max_blk_count = FIELD_GET(MESON_SDHC_SEND_TOTAL_PACK, ~0); + mmc->max_blk_size = MESON_SDHC_MAX_BLK_SIZE; + mmc->max_busy_timeout = 30 * MSEC_PER_SEC; + mmc->f_min = clk_round_rate(host->sd_clk, 1); + mmc->f_max = clk_round_rate(host->sd_clk, ULONG_MAX); + mmc->max_current_180 = 300; + mmc->max_current_330 = 300; + mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_HW_RESET; + mmc->ops = &meson_mx_sdhc_ops; + + ret = mmc_of_parse(mmc); + if (ret) + goto err_disable_pclk; + + irq = platform_get_irq(pdev, 0); + ret = devm_request_threaded_irq(dev, irq, meson_mx_sdhc_irq, + meson_mx_sdhc_irq_thread, IRQF_ONESHOT, + NULL, host); + if (ret) + goto err_disable_pclk; + + ret = mmc_add_host(mmc); + if (ret) + goto err_disable_pclk; + + return 0; + +err_disable_pclk: + clk_disable_unprepare(host->pclk); + return ret; +} + +static int meson_mx_sdhc_remove(struct platform_device *pdev) +{ + struct meson_mx_sdhc_host *host = platform_get_drvdata(pdev); + + mmc_remove_host(host->mmc); + + meson_mx_sdhc_disable_clks(host->mmc); + + clk_disable_unprepare(host->pclk); + + return 0; +} + +static const struct meson_mx_sdhc_data meson_mx_sdhc_data_meson8 = { + .init_hw = meson_mx_sdhc_init_hw_meson8, + .set_pdma = meson_mx_sdhc_set_pdma_meson8, + .wait_before_send = meson_mx_sdhc_wait_before_send_meson8, + .hardware_flush_all_cmds = false, +}; + +static const struct meson_mx_sdhc_data meson_mx_sdhc_data_meson8m2 = { + .init_hw = meson_mx_sdhc_init_hw_meson8m2, + .set_pdma = meson_mx_sdhc_set_pdma_meson8m2, + .hardware_flush_all_cmds = true, +}; + +static const struct of_device_id meson_mx_sdhc_of_match[] = { + { + .compatible = "amlogic,meson8-sdhc", + .data = &meson_mx_sdhc_data_meson8 + }, + { + .compatible = "amlogic,meson8b-sdhc", + .data = &meson_mx_sdhc_data_meson8 + }, + { + .compatible = "amlogic,meson8m2-sdhc", + .data = &meson_mx_sdhc_data_meson8m2 + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, meson_mx_sdhc_of_match); + +static struct platform_driver meson_mx_sdhc_driver = { + .probe = meson_mx_sdhc_probe, + .remove = meson_mx_sdhc_remove, + .driver = { + .name = "meson-mx-sdhc", + .of_match_table = of_match_ptr(meson_mx_sdhc_of_match), + }, +}; + +module_platform_driver(meson_mx_sdhc_driver); + +MODULE_DESCRIPTION("Meson6, Meson8, Meson8b and Meson8m2 SDHC Host Driver"); +MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmc/host/meson-mx-sdhc.h b/drivers/mmc/host/meson-mx-sdhc.h new file mode 100644 index 000000000000..230e8fbe6b3f --- /dev/null +++ b/drivers/mmc/host/meson-mx-sdhc.h @@ -0,0 +1,141 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2020 Martin Blumenstingl <martin.blumenstingl@googlemail.com> + */ + +#ifndef _MESON_MX_SDHC_H_ +#define _MESON_MX_SDHC_H_ + +#include <linux/bitfield.h> + +#define MESON_SDHC_ARGU 0x00 + +#define MESON_SDHC_SEND 0x04 + #define MESON_SDHC_SEND_CMD_INDEX GENMASK(5, 0) + #define MESON_SDHC_SEND_CMD_HAS_RESP BIT(6) + #define MESON_SDHC_SEND_CMD_HAS_DATA BIT(7) + #define MESON_SDHC_SEND_RESP_LEN BIT(8) + #define MESON_SDHC_SEND_RESP_NO_CRC BIT(9) + #define MESON_SDHC_SEND_DATA_DIR BIT(10) + #define MESON_SDHC_SEND_DATA_STOP BIT(11) + #define MESON_SDHC_SEND_R1B BIT(12) + #define MESON_SDHC_SEND_TOTAL_PACK GENMASK(31, 16) + +#define MESON_SDHC_CTRL 0x08 + #define MESON_SDHC_CTRL_DAT_TYPE GENMASK(1, 0) + #define MESON_SDHC_CTRL_DDR_MODE BIT(2) + #define MESON_SDHC_CTRL_TX_CRC_NOCHECK BIT(3) + #define MESON_SDHC_CTRL_PACK_LEN GENMASK(12, 4) + #define MESON_SDHC_CTRL_RX_TIMEOUT GENMASK(19, 13) + #define MESON_SDHC_CTRL_RX_PERIOD GENMASK(23, 20) + #define MESON_SDHC_CTRL_RX_ENDIAN GENMASK(26, 24) + #define MESON_SDHC_CTRL_SDIO_IRQ_MODE BIT(27) + #define MESON_SDHC_CTRL_DAT0_IRQ_SEL BIT(28) + #define MESON_SDHC_CTRL_TX_ENDIAN GENMASK(31, 29) + +#define MESON_SDHC_STAT 0x0c + #define MESON_SDHC_STAT_CMD_BUSY BIT(0) + #define MESON_SDHC_STAT_DAT3_0 GENMASK(4, 1) + #define MESON_SDHC_STAT_CMD BIT(5) + #define MESON_SDHC_STAT_RXFIFO_CNT GENMASK(12, 6) + #define MESON_SDHC_STAT_TXFIFO_CNT GENMASK(19, 13) + #define MESON_SDHC_STAT_DAT7_4 GENMASK(23, 20) + +#define MESON_SDHC_CLKC 0x10 + #define MESON_SDHC_CLKC_CLK_DIV GENMASK(11, 0) + #define MESON_SDHC_CLKC_CLK_JIC BIT(24) + #define MESON_SDHC_CLKC_MEM_PWR_OFF GENMASK(26, 25) + +#define MESON_SDHC_ADDR 0x14 + +#define MESON_SDHC_PDMA 0x18 + #define MESON_SDHC_PDMA_DMA_MODE BIT(0) + #define MESON_SDHC_PDMA_PIO_RDRESP GENMASK(3, 1) + #define MESON_SDHC_PDMA_DMA_URGENT BIT(4) + #define MESON_SDHC_PDMA_WR_BURST GENMASK(9, 5) + #define MESON_SDHC_PDMA_RD_BURST GENMASK(14, 10) + #define MESON_SDHC_PDMA_RXFIFO_TH GENMASK(21, 15) + #define MESON_SDHC_PDMA_TXFIFO_TH GENMASK(28, 22) + #define MESON_SDHC_PDMA_RXFIFO_MANUAL_FLUSH GENMASK(30, 29) + #define MESON_SDHC_PDMA_TXFIFO_FILL BIT(31) + +#define MESON_SDHC_MISC 0x1c + #define MESON_SDHC_MISC_WCRC_ERR_PATT GENMASK(6, 4) + #define MESON_SDHC_MISC_WCRC_OK_PATT GENMASK(9, 7) + #define MESON_SDHC_MISC_BURST_NUM GENMASK(21, 16) + #define MESON_SDHC_MISC_THREAD_ID GENMASK(27, 22) + #define MESON_SDHC_MISC_MANUAL_STOP BIT(28) + #define MESON_SDHC_MISC_TXSTART_THRES GENMASK(31, 29) + +#define MESON_SDHC_DATA 0x20 + +#define MESON_SDHC_ICTL 0x24 + #define MESON_SDHC_ICTL_RESP_OK BIT(0) + #define MESON_SDHC_ICTL_RESP_TIMEOUT BIT(1) + #define MESON_SDHC_ICTL_RESP_ERR_CRC BIT(2) + #define MESON_SDHC_ICTL_RESP_OK_NOCLEAR BIT(3) + #define MESON_SDHC_ICTL_DATA_1PACK_OK BIT(4) + #define MESON_SDHC_ICTL_DATA_TIMEOUT BIT(5) + #define MESON_SDHC_ICTL_DATA_ERR_CRC BIT(6) + #define MESON_SDHC_ICTL_DATA_XFER_OK BIT(7) + #define MESON_SDHC_ICTL_RX_HIGHER BIT(8) + #define MESON_SDHC_ICTL_RX_LOWER BIT(9) + #define MESON_SDHC_ICTL_DAT1_IRQ BIT(10) + #define MESON_SDHC_ICTL_DMA_DONE BIT(11) + #define MESON_SDHC_ICTL_RXFIFO_FULL BIT(12) + #define MESON_SDHC_ICTL_TXFIFO_EMPTY BIT(13) + #define MESON_SDHC_ICTL_ADDI_DAT1_IRQ BIT(14) + #define MESON_SDHC_ICTL_ALL_IRQS GENMASK(14, 0) + #define MESON_SDHC_ICTL_DAT1_IRQ_DELAY GENMASK(17, 16) + +#define MESON_SDHC_ISTA 0x28 + #define MESON_SDHC_ISTA_RESP_OK BIT(0) + #define MESON_SDHC_ISTA_RESP_TIMEOUT BIT(1) + #define MESON_SDHC_ISTA_RESP_ERR_CRC BIT(2) + #define MESON_SDHC_ISTA_RESP_OK_NOCLEAR BIT(3) + #define MESON_SDHC_ISTA_DATA_1PACK_OK BIT(4) + #define MESON_SDHC_ISTA_DATA_TIMEOUT BIT(5) + #define MESON_SDHC_ISTA_DATA_ERR_CRC BIT(6) + #define MESON_SDHC_ISTA_DATA_XFER_OK BIT(7) + #define MESON_SDHC_ISTA_RX_HIGHER BIT(8) + #define MESON_SDHC_ISTA_RX_LOWER BIT(9) + #define MESON_SDHC_ISTA_DAT1_IRQ BIT(10) + #define MESON_SDHC_ISTA_DMA_DONE BIT(11) + #define MESON_SDHC_ISTA_RXFIFO_FULL BIT(12) + #define MESON_SDHC_ISTA_TXFIFO_EMPTY BIT(13) + #define MESON_SDHC_ISTA_ADDI_DAT1_IRQ BIT(14) + #define MESON_SDHC_ISTA_ALL_IRQS GENMASK(14, 0) + +#define MESON_SDHC_SRST 0x2c + #define MESON_SDHC_SRST_MAIN_CTRL BIT(0) + #define MESON_SDHC_SRST_RXFIFO BIT(1) + #define MESON_SDHC_SRST_TXFIFO BIT(2) + #define MESON_SDHC_SRST_DPHY_RX BIT(3) + #define MESON_SDHC_SRST_DPHY_TX BIT(4) + #define MESON_SDHC_SRST_DMA_IF BIT(5) + +#define MESON_SDHC_ESTA 0x30 + #define MESON_SDHC_ESTA_11_13 GENMASK(13, 11) + +#define MESON_SDHC_ENHC 0x34 + #define MESON_SDHC_ENHC_MESON8M2_WRRSP_MODE BIT(0) + #define MESON_SDHC_ENHC_MESON8M2_CHK_WRRSP BIT(1) + #define MESON_SDHC_ENHC_MESON8M2_CHK_DMA BIT(2) + #define MESON_SDHC_ENHC_MESON8M2_DEBUG GENMASK(5, 3) + #define MESON_SDHC_ENHC_MESON6_RX_TIMEOUT GENMASK(7, 0) + #define MESON_SDHC_ENHC_MESON6_DMA_RD_RESP BIT(16) + #define MESON_SDHC_ENHC_MESON6_DMA_WR_RESP BIT(17) + #define MESON_SDHC_ENHC_SDIO_IRQ_PERIOD GENMASK(15, 8) + #define MESON_SDHC_ENHC_RXFIFO_TH GENMASK(24, 18) + #define MESON_SDHC_ENHC_TXFIFO_TH GENMASK(31, 25) + +#define MESON_SDHC_CLK2 0x38 + #define MESON_SDHC_CLK2_RX_CLK_PHASE GENMASK(11, 0) + #define MESON_SDHC_CLK2_SD_CLK_PHASE GENMASK(23, 12) + +struct clk_bulk_data; + +int meson_mx_sdhc_register_clkc(struct device *dev, void __iomem *base, + struct clk_bulk_data *clk_bulk_data); + +#endif /* _MESON_MX_SDHC_H_ */ diff --git a/drivers/mmc/host/meson-mx-sdio.c b/drivers/mmc/host/meson-mx-sdio.c index 2e58743d83bb..9b2cf7afc246 100644 --- a/drivers/mmc/host/meson-mx-sdio.c +++ b/drivers/mmc/host/meson-mx-sdio.c @@ -246,6 +246,9 @@ static void meson_mx_mmc_request_done(struct meson_mx_mmc_host *host) mrq = host->mrq; + if (host->cmd->error) + meson_mx_mmc_soft_reset(host); + host->mrq = NULL; host->cmd = NULL; @@ -561,7 +564,7 @@ static int meson_mx_mmc_add_host(struct meson_mx_mmc_host *host) mmc->f_max = clk_round_rate(host->cfg_div_clk, clk_get_rate(host->parent_clk)); - mmc->caps |= MMC_CAP_ERASE | MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY; + mmc->caps |= MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY; mmc->ops = &meson_mx_mmc_ops; ret = mmc_of_parse(mmc); diff --git a/drivers/mmc/host/mmc_hsq.c b/drivers/mmc/host/mmc_hsq.c index b90b2c97b6cf..a5e05ed0fda3 100644 --- a/drivers/mmc/host/mmc_hsq.c +++ b/drivers/mmc/host/mmc_hsq.c @@ -16,11 +16,20 @@ #define HSQ_NUM_SLOTS 64 #define HSQ_INVALID_TAG HSQ_NUM_SLOTS +static void mmc_hsq_retry_handler(struct work_struct *work) +{ + struct mmc_hsq *hsq = container_of(work, struct mmc_hsq, retry_work); + struct mmc_host *mmc = hsq->mmc; + + mmc->ops->request(mmc, hsq->mrq); +} + static void mmc_hsq_pump_requests(struct mmc_hsq *hsq) { struct mmc_host *mmc = hsq->mmc; struct hsq_slot *slot; unsigned long flags; + int ret = 0; spin_lock_irqsave(&hsq->lock, flags); @@ -42,7 +51,24 @@ static void mmc_hsq_pump_requests(struct mmc_hsq *hsq) spin_unlock_irqrestore(&hsq->lock, flags); - mmc->ops->request(mmc, hsq->mrq); + if (mmc->ops->request_atomic) + ret = mmc->ops->request_atomic(mmc, hsq->mrq); + else + mmc->ops->request(mmc, hsq->mrq); + + /* + * If returning BUSY from request_atomic(), which means the card + * may be busy now, and we should change to non-atomic context to + * try again for this unusual case, to avoid time-consuming operations + * in the atomic context. + * + * Note: we just give a warning for other error cases, since the host + * driver will handle them. + */ + if (ret == -EBUSY) + schedule_work(&hsq->retry_work); + else + WARN_ON_ONCE(ret); } static void mmc_hsq_update_next_tag(struct mmc_hsq *hsq, int remains) @@ -325,6 +351,7 @@ int mmc_hsq_init(struct mmc_hsq *hsq, struct mmc_host *mmc) hsq->mmc->cqe_private = hsq; mmc->cqe_ops = &mmc_hsq_ops; + INIT_WORK(&hsq->retry_work, mmc_hsq_retry_handler); spin_lock_init(&hsq->lock); init_waitqueue_head(&hsq->wait_queue); diff --git a/drivers/mmc/host/mmc_hsq.h b/drivers/mmc/host/mmc_hsq.h index 18b9cf55925f..ffdd9cd172c3 100644 --- a/drivers/mmc/host/mmc_hsq.h +++ b/drivers/mmc/host/mmc_hsq.h @@ -12,6 +12,7 @@ struct mmc_hsq { wait_queue_head_t wait_queue; struct hsq_slot *slot; spinlock_t lock; + struct work_struct retry_work; int next_tag; int num_slots; diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index 951f76dc1ddd..39bb1e30c2d7 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -77,14 +77,8 @@ #define MMC_SPI_BLOCKSIZE 512 - -/* These fixed timeouts come from the latest SD specs, which say to ignore - * the CSD values. The R1B value is for card erase (e.g. the "I forgot the - * card's password" scenario); it's mostly applied to STOP_TRANSMISSION after - * reads which takes nowhere near that long. Older cards may be able to use - * shorter timeouts ... but why bother? - */ -#define r1b_timeout (HZ * 3) +#define MMC_SPI_R1B_TIMEOUT_MS 3000 +#define MMC_SPI_INIT_TIMEOUT_MS 3000 /* One of the critical speed parameters is the amount of data which may * be transferred in one command. If this value is too low, the SD card @@ -248,6 +242,7 @@ static char *maptype(struct mmc_command *cmd) static int mmc_spi_response_get(struct mmc_spi_host *host, struct mmc_command *cmd, int cs_on) { + unsigned long timeout_ms; u8 *cp = host->data->status; u8 *end = cp + host->t.len; int value = 0; @@ -346,8 +341,11 @@ checkstatus: /* maybe we read all the busy tokens already */ while (cp < end && *cp == 0) cp++; - if (cp == end) - mmc_spi_wait_unbusy(host, r1b_timeout); + if (cp == end) { + timeout_ms = cmd->busy_timeout ? cmd->busy_timeout : + MMC_SPI_R1B_TIMEOUT_MS; + mmc_spi_wait_unbusy(host, msecs_to_jiffies(timeout_ms)); + } break; /* SPI R2 == R1 + second status byte; SEND_STATUS @@ -1118,7 +1116,7 @@ static void mmc_spi_initsequence(struct mmc_spi_host *host) /* Try to be very sure any previous command has completed; * wait till not-busy, skip debris from any old commands. */ - mmc_spi_wait_unbusy(host, r1b_timeout); + mmc_spi_wait_unbusy(host, msecs_to_jiffies(MMC_SPI_INIT_TIMEOUT_MS)); mmc_spi_readbytes(host, 10); /* diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 647567def612..a69d6a0c2e15 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1861,31 +1861,17 @@ static int mmci_get_cd(struct mmc_host *mmc) static int mmci_sig_volt_switch(struct mmc_host *mmc, struct mmc_ios *ios) { struct mmci_host *host = mmc_priv(mmc); - int ret = 0; - - if (!IS_ERR(mmc->supply.vqmmc)) { + int ret; - switch (ios->signal_voltage) { - case MMC_SIGNAL_VOLTAGE_330: - ret = regulator_set_voltage(mmc->supply.vqmmc, - 2700000, 3600000); - break; - case MMC_SIGNAL_VOLTAGE_180: - ret = regulator_set_voltage(mmc->supply.vqmmc, - 1700000, 1950000); - break; - case MMC_SIGNAL_VOLTAGE_120: - ret = regulator_set_voltage(mmc->supply.vqmmc, - 1100000, 1300000); - break; - } + ret = mmc_regulator_set_vqmmc(mmc, ios); - if (!ret && host->ops && host->ops->post_sig_volt_switch) - ret = host->ops->post_sig_volt_switch(host, ios); + if (!ret && host->ops && host->ops->post_sig_volt_switch) + ret = host->ops->post_sig_volt_switch(host, ios); + else if (ret) + ret = 0; - if (ret) - dev_warn(mmc_dev(mmc), "Voltage switch failed\n"); - } + if (ret < 0) + dev_warn(mmc_dev(mmc), "Voltage switch failed\n"); return ret; } diff --git a/drivers/mmc/host/mmci_stm32_sdmmc.c b/drivers/mmc/host/mmci_stm32_sdmmc.c index d33e62bd6153..51db30acf4dc 100644 --- a/drivers/mmc/host/mmci_stm32_sdmmc.c +++ b/drivers/mmc/host/mmci_stm32_sdmmc.c @@ -119,20 +119,19 @@ static void sdmmc_idma_unprep_data(struct mmci_host *host, static int sdmmc_idma_setup(struct mmci_host *host) { struct sdmmc_idma *idma; + struct device *dev = mmc_dev(host->mmc); - idma = devm_kzalloc(mmc_dev(host->mmc), sizeof(*idma), GFP_KERNEL); + idma = devm_kzalloc(dev, sizeof(*idma), GFP_KERNEL); if (!idma) return -ENOMEM; host->dma_priv = idma; if (host->variant->dma_lli) { - idma->sg_cpu = dmam_alloc_coherent(mmc_dev(host->mmc), - SDMMC_LLI_BUF_LEN, + idma->sg_cpu = dmam_alloc_coherent(dev, SDMMC_LLI_BUF_LEN, &idma->sg_dma, GFP_KERNEL); if (!idma->sg_cpu) { - dev_err(mmc_dev(host->mmc), - "Failed to alloc IDMA descriptor\n"); + dev_err(dev, "Failed to alloc IDMA descriptor\n"); return -ENOMEM; } host->mmc->max_segs = SDMMC_LLI_BUF_LEN / @@ -143,7 +142,7 @@ static int sdmmc_idma_setup(struct mmci_host *host) host->mmc->max_seg_size = host->mmc->max_req_size; } - return 0; + return dma_set_max_seg_size(dev, host->mmc->max_seg_size); } static int sdmmc_idma_start(struct mmci_host *host, unsigned int *datactrl) @@ -188,6 +187,9 @@ static int sdmmc_idma_start(struct mmci_host *host, unsigned int *datactrl) static void sdmmc_idma_finalize(struct mmci_host *host, struct mmc_data *data) { writel_relaxed(0, host->base + MMCI_STM32_IDMACTRLR); + + if (!data->host_cookie) + sdmmc_idma_unprep_data(host, data, 0); } static void mmci_sdmmc_set_clkreg(struct mmci_host *host, unsigned int desired) @@ -519,6 +521,7 @@ void sdmmc_variant_init(struct mmci_host *host) struct sdmmc_dlyb *dlyb; host->ops = &sdmmc_variant_ops; + host->pwr_reg = readl_relaxed(host->base + MMCIPOWER); base_dlyb = devm_of_iomap(mmc_dev(host->mmc), np, 1, NULL); if (IS_ERR(base_dlyb)) diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c index b221c02cc71f..39e7fc54c438 100644 --- a/drivers/mmc/host/mtk-sd.c +++ b/drivers/mmc/host/mtk-sd.c @@ -1369,7 +1369,7 @@ static void msdc_set_buswidth(struct msdc_host *host, u32 width) static int msdc_ops_switch_volt(struct mmc_host *mmc, struct mmc_ios *ios) { struct msdc_host *host = mmc_priv(mmc); - int ret = 0; + int ret; if (!IS_ERR(mmc->supply.vqmmc)) { if (ios->signal_voltage != MMC_SIGNAL_VOLTAGE_330 && @@ -1379,18 +1379,19 @@ static int msdc_ops_switch_volt(struct mmc_host *mmc, struct mmc_ios *ios) } ret = mmc_regulator_set_vqmmc(mmc, ios); - if (ret) { + if (ret < 0) { dev_dbg(host->dev, "Regulator set error %d (%d)\n", ret, ios->signal_voltage); - } else { - /* Apply different pinctrl settings for different signal voltage */ - if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) - pinctrl_select_state(host->pinctrl, host->pins_uhs); - else - pinctrl_select_state(host->pinctrl, host->pins_default); + return ret; } + + /* Apply different pinctrl settings for different signal voltage */ + if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) + pinctrl_select_state(host->pinctrl, host->pins_uhs); + else + pinctrl_select_state(host->pinctrl, host->pins_default); } - return ret; + return 0; } static int msdc_card_busy(struct mmc_host *mmc) @@ -2325,7 +2326,7 @@ static int msdc_drv_probe(struct platform_device *pdev) if (mmc->caps & MMC_CAP_SDIO_IRQ) mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD; - mmc->caps |= MMC_CAP_ERASE | MMC_CAP_CMD23; + mmc->caps |= MMC_CAP_CMD23; /* MMC core transfer sizes tunable parameters */ mmc->max_segs = MAX_BD_NUM; if (host->dev_comp->support_64g) diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c index 203b61712601..cc0752a9df6d 100644 --- a/drivers/mmc/host/mvsdio.c +++ b/drivers/mmc/host/mvsdio.c @@ -752,8 +752,6 @@ static int mvsd_probe(struct platform_device *pdev) if (maxfreq) mmc->f_max = maxfreq; - mmc->caps |= MMC_CAP_ERASE; - spin_lock_init(&host->lock); host->base = devm_platform_ioremap_resource(pdev, 0); diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index d82674aed447..b1820def36c0 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -634,8 +634,7 @@ static int mxs_mmc_probe(struct platform_device *pdev) /* set mmc core parameters */ mmc->ops = &mxs_mmc_ops; mmc->caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED | - MMC_CAP_SDIO_IRQ | MMC_CAP_NEEDS_POLL | MMC_CAP_CMD23 | - MMC_CAP_ERASE; + MMC_CAP_SDIO_IRQ | MMC_CAP_NEEDS_POLL | MMC_CAP_CMD23; host->broken_cd = of_property_read_bool(np, "broken-cd"); diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index d74e73c95fdf..33d7af7c7762 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -1244,7 +1244,7 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id) mmc->caps = 0; if (host->pdata->slots[id].wires >= 4) - mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_ERASE; + mmc->caps |= MMC_CAP_4_BIT_DATA; mmc->ops = &mmc_omap_ops; mmc->f_min = 400000; diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index a379c45b985c..37b8740513f5 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -1922,7 +1922,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev) mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED | - MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_ERASE | MMC_CAP_CMD23; + MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_CMD23; mmc->caps |= mmc_pdata(host)->caps; if (mmc->caps & MMC_CAP_8_BIT_DATA) diff --git a/drivers/mmc/host/owl-mmc.c b/drivers/mmc/host/owl-mmc.c index 01ffe51f413d..5e20c099fe03 100644 --- a/drivers/mmc/host/owl-mmc.c +++ b/drivers/mmc/host/owl-mmc.c @@ -92,6 +92,8 @@ #define OWL_SD_STATE_RC16ER BIT(1) #define OWL_SD_STATE_CRC7ER BIT(0) +#define OWL_CMD_TIMEOUT_MS 30000 + struct owl_mmc_host { struct device *dev; struct reset_control *reset; @@ -172,6 +174,7 @@ static void owl_mmc_send_cmd(struct owl_mmc_host *owl_host, struct mmc_command *cmd, struct mmc_data *data) { + unsigned long timeout; u32 mode, state, resp[2]; u32 cmd_rsp_mask = 0; @@ -239,7 +242,10 @@ static void owl_mmc_send_cmd(struct owl_mmc_host *owl_host, if (data) return; - if (!wait_for_completion_timeout(&owl_host->sdc_complete, 30 * HZ)) { + timeout = msecs_to_jiffies(cmd->busy_timeout ? cmd->busy_timeout : + OWL_CMD_TIMEOUT_MS); + + if (!wait_for_completion_timeout(&owl_host->sdc_complete, timeout)) { dev_err(owl_host->dev, "CMD interrupt timeout\n"); cmd->error = -ETIMEDOUT; return; diff --git a/drivers/mmc/host/renesas_sdhi.h b/drivers/mmc/host/renesas_sdhi.h index 2a4c83a5f32e..14c64caefc64 100644 --- a/drivers/mmc/host/renesas_sdhi.h +++ b/drivers/mmc/host/renesas_sdhi.h @@ -36,6 +36,7 @@ struct renesas_sdhi_of_data { struct renesas_sdhi_quirks { bool hs400_disabled; bool hs400_4taps; + u32 hs400_bad_taps; }; struct tmio_mmc_dma { @@ -61,8 +62,10 @@ struct renesas_sdhi { /* Tuning values: 1 for success, 0 for failure */ DECLARE_BITMAP(taps, BITS_PER_LONG); + /* Sampling data comparison: 1 for match, 0 for mismatch */ + DECLARE_BITMAP(smpcmp, BITS_PER_LONG); unsigned int tap_num; - unsigned long tap_set; + unsigned int tap_set; }; #define host_to_priv(host) \ diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c index df826661366f..15e21894bd44 100644 --- a/drivers/mmc/host/renesas_sdhi_core.c +++ b/drivers/mmc/host/renesas_sdhi_core.c @@ -24,6 +24,7 @@ #include <linux/module.h> #include <linux/of_device.h> #include <linux/platform_device.h> +#include <linux/pm_domain.h> #include <linux/mmc/host.h> #include <linux/mmc/slot-gpio.h> #include <linux/mfd/tmio.h> @@ -82,16 +83,11 @@ static int renesas_sdhi_clk_enable(struct tmio_mmc_host *host) { struct mmc_host *mmc = host->mmc; struct renesas_sdhi *priv = host_to_priv(host); - int ret = clk_prepare_enable(priv->clk); - - if (ret < 0) - return ret; + int ret; ret = clk_prepare_enable(priv->clk_cd); - if (ret < 0) { - clk_disable_unprepare(priv->clk); + if (ret < 0) return ret; - } /* * The clock driver may not know what maximum frequency @@ -197,7 +193,6 @@ static void renesas_sdhi_clk_disable(struct tmio_mmc_host *host) { struct renesas_sdhi *priv = host_to_priv(host); - clk_disable_unprepare(priv->clk); clk_disable_unprepare(priv->clk_cd); } @@ -237,7 +232,7 @@ static int renesas_sdhi_start_signal_voltage_switch(struct mmc_host *mmc, MMC_SIGNAL_VOLTAGE_330 ? 0 : -EINVAL; ret = mmc_regulator_set_vqmmc(host->mmc, ios); - if (ret) + if (ret < 0) return ret; return pinctrl_select_state(priv->pinctrl, pin_state); @@ -325,6 +320,8 @@ static void renesas_sdhi_hs400_complete(struct mmc_host *mmc) { struct tmio_mmc_host *host = mmc_priv(mmc); struct renesas_sdhi *priv = host_to_priv(host); + u32 bad_taps = priv->quirks ? priv->quirks->hs400_bad_taps : 0; + bool use_4tap = priv->quirks && priv->quirks->hs400_4taps; sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN & sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); @@ -352,10 +349,23 @@ static void renesas_sdhi_hs400_complete(struct mmc_host *mmc) SH_MOBILE_SDHI_SCC_DTCNTL_TAPEN | 0x4 << SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_SHIFT); + /* Avoid bad TAP */ + if (bad_taps & BIT(priv->tap_set)) { + u32 new_tap = (priv->tap_set + 1) % priv->tap_num; + + if (bad_taps & BIT(new_tap)) + new_tap = (priv->tap_set - 1) % priv->tap_num; - if (priv->quirks && priv->quirks->hs400_4taps) - sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET, - priv->tap_set / 2); + if (bad_taps & BIT(new_tap)) { + new_tap = priv->tap_set; + dev_dbg(&host->pdev->dev, "Can't handle three bad tap in a row\n"); + } + + priv->tap_set = new_tap; + } + + sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET, + priv->tap_set / (use_4tap ? 2 : 1)); sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_CKSEL, SH_MOBILE_SDHI_SCC_CKSEL_DTSEL | @@ -422,20 +432,16 @@ static int renesas_sdhi_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_io return 0; } -#define SH_MOBILE_SDHI_MAX_TAP 3 +#define SH_MOBILE_SDHI_MIN_TAP_ROW 3 static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host) { struct renesas_sdhi *priv = host_to_priv(host); - unsigned long tap_cnt; /* counter of tuning success */ - unsigned long tap_start;/* start position of tuning success */ - unsigned long tap_end; /* end position of tuning success */ - unsigned long ntap; /* temporary counter of tuning success */ - unsigned long i; + unsigned int tap_start = 0, tap_end = 0, tap_cnt = 0, rs, re, i; + unsigned int taps_size = priv->tap_num * 2, min_tap_row; + unsigned long *bitmap; priv->doing_tune = false; - - /* Clear SCC_RVSREQ */ sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSREQ, 0); /* @@ -443,42 +449,42 @@ static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host) * result requiring the tap to be good in both runs before * considering it for tuning selection. */ - for (i = 0; i < priv->tap_num * 2; i++) { + for (i = 0; i < taps_size; i++) { int offset = priv->tap_num * (i < priv->tap_num ? 1 : -1); if (!test_bit(i, priv->taps)) clear_bit(i + offset, priv->taps); + + if (!test_bit(i, priv->smpcmp)) + clear_bit(i + offset, priv->smpcmp); } /* - * Find the longest consecutive run of successful probes. If that - * is more than SH_MOBILE_SDHI_MAX_TAP probes long then use the - * center index as the tap. + * If all TAP are OK, the sampling clock position is selected by + * identifying the change point of data. */ - tap_cnt = 0; - ntap = 0; - tap_start = 0; - tap_end = 0; - for (i = 0; i < priv->tap_num * 2; i++) { - if (test_bit(i, priv->taps)) { - ntap++; - } else { - if (ntap > tap_cnt) { - tap_start = i - ntap; - tap_end = i - 1; - tap_cnt = ntap; - } - ntap = 0; - } + if (bitmap_full(priv->taps, taps_size)) { + bitmap = priv->smpcmp; + min_tap_row = 1; + } else { + bitmap = priv->taps; + min_tap_row = SH_MOBILE_SDHI_MIN_TAP_ROW; } - if (ntap > tap_cnt) { - tap_start = i - ntap; - tap_end = i - 1; - tap_cnt = ntap; + /* + * Find the longest consecutive run of successful probes. If that + * is at least SH_MOBILE_SDHI_MIN_TAP_ROW probes long then use the + * center index as the tap, otherwise bail out. + */ + bitmap_for_each_set_region(bitmap, rs, re, 0, taps_size) { + if (re - rs > tap_cnt) { + tap_end = re; + tap_start = rs; + tap_cnt = tap_end - tap_start; + } } - if (tap_cnt >= SH_MOBILE_SDHI_MAX_TAP) + if (tap_cnt >= min_tap_row) priv->tap_set = (tap_start + tap_end) / 2 % priv->tap_num; else return -EIO; @@ -511,6 +517,7 @@ static int renesas_sdhi_execute_tuning(struct tmio_mmc_host *host, u32 opcode) priv->doing_tune = true; bitmap_zero(priv->taps, priv->tap_num * 2); + bitmap_zero(priv->smpcmp, priv->tap_num * 2); /* Issue CMD19 twice for each tap */ for (i = 0; i < 2 * priv->tap_num; i++) { @@ -519,6 +526,9 @@ static int renesas_sdhi_execute_tuning(struct tmio_mmc_host *host, u32 opcode) if (mmc_send_tuning(host->mmc, opcode, NULL) == 0) set_bit(i, priv->taps); + + if (sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_SMPCMP) == 0) + set_bit(i, priv->smpcmp); } return renesas_sdhi_select_tuning(host); @@ -527,7 +537,7 @@ static int renesas_sdhi_execute_tuning(struct tmio_mmc_host *host, u32 opcode) static bool renesas_sdhi_manual_correction(struct tmio_mmc_host *host, bool use_4tap) { struct renesas_sdhi *priv = host_to_priv(host); - unsigned long new_tap = priv->tap_set; + unsigned int new_tap = priv->tap_set, error_tap = priv->tap_set; u32 val; val = sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSREQ); @@ -539,20 +549,32 @@ static bool renesas_sdhi_manual_correction(struct tmio_mmc_host *host, bool use_ /* Change TAP position according to correction status */ if (sd_ctrl_read16(host, CTL_VERSION) == SDHI_VER_GEN3_SDMMC && host->mmc->ios.timing == MMC_TIMING_MMC_HS400) { + u32 bad_taps = priv->quirks ? priv->quirks->hs400_bad_taps : 0; /* * With HS400, the DAT signal is based on DS, not CLK. * Therefore, use only CMD status. */ u32 smpcmp = sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_SMPCMP) & SH_MOBILE_SDHI_SCC_SMPCMP_CMD_ERR; - if (!smpcmp) + if (!smpcmp) { return false; /* no error in CMD signal */ - else if (smpcmp == SH_MOBILE_SDHI_SCC_SMPCMP_CMD_REQUP) + } else if (smpcmp == SH_MOBILE_SDHI_SCC_SMPCMP_CMD_REQUP) { new_tap++; - else if (smpcmp == SH_MOBILE_SDHI_SCC_SMPCMP_CMD_REQDOWN) + error_tap--; + } else if (smpcmp == SH_MOBILE_SDHI_SCC_SMPCMP_CMD_REQDOWN) { new_tap--; - else + error_tap++; + } else { return true; /* need retune */ + } + + /* + * When new_tap is a bad tap, we cannot change. Then, we compare + * with the HS200 tuning result. When smpcmp[error_tap] is OK, + * we can at least retune. + */ + if (bad_taps & BIT(new_tap % priv->tap_num)) + return test_bit(error_tap % priv->tap_num, priv->smpcmp); } else { if (val & SH_MOBILE_SDHI_SCC_RVSREQ_RVSERR) return true; /* need retune */ @@ -705,17 +727,35 @@ static const struct renesas_sdhi_quirks sdhi_quirks_4tap_nohs400 = { static const struct renesas_sdhi_quirks sdhi_quirks_4tap = { .hs400_4taps = true, + .hs400_bad_taps = BIT(2) | BIT(3) | BIT(6) | BIT(7), }; static const struct renesas_sdhi_quirks sdhi_quirks_nohs400 = { .hs400_disabled = true, }; +static const struct renesas_sdhi_quirks sdhi_quirks_bad_taps1357 = { + .hs400_bad_taps = BIT(1) | BIT(3) | BIT(5) | BIT(7), +}; + +static const struct renesas_sdhi_quirks sdhi_quirks_bad_taps2367 = { + .hs400_bad_taps = BIT(2) | BIT(3) | BIT(6) | BIT(7), +}; + +/* + * Note for r8a7796 / r8a774a1: we can't distinguish ES1.1 and 1.2 as of now. + * So, we want to treat them equally and only have a match for ES1.2 to enforce + * this if there ever will be a way to distinguish ES1.2. + */ static const struct soc_device_attribute sdhi_quirks_match[] = { { .soc_id = "r8a774a1", .revision = "ES1.[012]", .data = &sdhi_quirks_4tap_nohs400 }, { .soc_id = "r8a7795", .revision = "ES1.*", .data = &sdhi_quirks_4tap_nohs400 }, { .soc_id = "r8a7795", .revision = "ES2.0", .data = &sdhi_quirks_4tap }, + { .soc_id = "r8a7795", .revision = "ES3.*", .data = &sdhi_quirks_bad_taps2367 }, { .soc_id = "r8a7796", .revision = "ES1.[012]", .data = &sdhi_quirks_4tap_nohs400 }, + { .soc_id = "r8a7796", .revision = "ES1.*", .data = &sdhi_quirks_4tap }, + { .soc_id = "r8a7796", .revision = "ES3.*", .data = &sdhi_quirks_bad_taps1357 }, + { .soc_id = "r8a77965", .data = &sdhi_quirks_bad_taps2367 }, { .soc_id = "r8a77980", .data = &sdhi_quirks_nohs400 }, { /* Sentinel. */ }, }; @@ -860,6 +900,8 @@ int renesas_sdhi_probe(struct platform_device *pdev, /* All SDHI have SDIO status bits which must be 1 */ mmc_data->flags |= TMIO_MMC_SDIO_STATUS_SETBITS; + dev_pm_domain_start(&pdev->dev); + ret = renesas_sdhi_clk_enable(host); if (ret) goto efree; @@ -933,10 +975,8 @@ int renesas_sdhi_probe(struct platform_device *pdev, goto eirq; } - dev_info(&pdev->dev, "%s base at 0x%08lx max clock rate %u MHz\n", - mmc_hostname(host->mmc), (unsigned long) - (platform_get_resource(pdev, IORESOURCE_MEM, 0)->start), - host->mmc->f_max / 1000000); + dev_info(&pdev->dev, "%s base at %pa, max clock rate %u MHz\n", + mmc_hostname(host->mmc), &res->start, host->mmc->f_max / 1000000); return ret; diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index 11087976ab19..5a71f6678fd3 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -1347,7 +1347,7 @@ static void realtek_init_host(struct realtek_pci_sdmmc *host) mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST | - MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_ERASE; + MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25; mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE; mmc->max_current_330 = 400; mmc->max_current_180 = 800; diff --git a/drivers/mmc/host/rtsx_usb_sdmmc.c b/drivers/mmc/host/rtsx_usb_sdmmc.c index 81d0dfe553a8..a7084c50ad65 100644 --- a/drivers/mmc/host/rtsx_usb_sdmmc.c +++ b/drivers/mmc/host/rtsx_usb_sdmmc.c @@ -1314,7 +1314,7 @@ static void rtsx_usb_init_host(struct rtsx_usb_sdmmc *host) mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST | MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50 | - MMC_CAP_ERASE | MMC_CAP_SYNC_RUNTIME_PM; + MMC_CAP_SYNC_RUNTIME_PM; mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE | MMC_CAP2_NO_SDIO; diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c index 1e616ae56b13..444b2769ae2c 100644 --- a/drivers/mmc/host/s3cmci.c +++ b/drivers/mmc/host/s3cmci.c @@ -958,13 +958,6 @@ static int s3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data) { u32 dcon, imsk, stoptries = 3; - /* write DCON register */ - - if (!data) { - writel(0, host->base + S3C2410_SDIDCON); - return 0; - } - if ((data->blksz & 3) != 0) { /* We cannot deal with unaligned blocks with more than * one block being transferred. */ diff --git a/drivers/mmc/host/sdhci-cadence.c b/drivers/mmc/host/sdhci-cadence.c index 6da6d4fb5edd..4a6c9ba82538 100644 --- a/drivers/mmc/host/sdhci-cadence.c +++ b/drivers/mmc/host/sdhci-cadence.c @@ -97,6 +97,11 @@ static int sdhci_cdns_write_phy_reg(struct sdhci_cdns_priv *priv, u32 tmp; int ret; + ret = readl_poll_timeout(reg, tmp, !(tmp & SDHCI_CDNS_HRS04_ACK), + 0, 10); + if (ret) + return ret; + tmp = FIELD_PREP(SDHCI_CDNS_HRS04_WDATA, data) | FIELD_PREP(SDHCI_CDNS_HRS04_ADDR, addr); writel(tmp, reg); @@ -111,7 +116,10 @@ static int sdhci_cdns_write_phy_reg(struct sdhci_cdns_priv *priv, tmp &= ~SDHCI_CDNS_HRS04_WR; writel(tmp, reg); - return 0; + ret = readl_poll_timeout(reg, tmp, !(tmp & SDHCI_CDNS_HRS04_ACK), + 0, 10); + + return ret; } static unsigned int sdhci_cdns_phy_param_count(struct device_node *np) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 5ec8e4bf1ac7..1d7f84b23a22 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -8,6 +8,7 @@ * Author: Wolfram Sang <kernel@pengutronix.de> */ +#include <linux/bitfield.h> #include <linux/io.h> #include <linux/iopoll.h> #include <linux/delay.h> @@ -89,7 +90,8 @@ #define ESDHC_STD_TUNING_EN (1 << 24) /* NOTE: the minimum valid tuning start tap for mx6sl is 1 */ #define ESDHC_TUNING_START_TAP_DEFAULT 0x1 -#define ESDHC_TUNING_START_TAP_MASK 0xff +#define ESDHC_TUNING_START_TAP_MASK 0x7f +#define ESDHC_TUNING_CMD_CRC_CHECK_DISABLE (1 << 7) #define ESDHC_TUNING_STEP_MASK 0x00070000 #define ESDHC_TUNING_STEP_SHIFT 16 @@ -214,6 +216,7 @@ static const struct esdhc_soc_data usdhc_imx6sl_data = { static const struct esdhc_soc_data usdhc_imx6sll_data = { .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200 + | ESDHC_FLAG_HS400 | ESDHC_FLAG_STATE_LOST_IN_LPMODE, }; @@ -399,7 +402,8 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg) val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 | SDHCI_USE_SDR50_TUNING - | (SDHCI_TUNING_MODE_3 << SDHCI_RETUNING_MODE_SHIFT); + | FIELD_PREP(SDHCI_RETUNING_MODE_MASK, + SDHCI_TUNING_MODE_3); if (imx_data->socdata->flags & ESDHC_FLAG_HS400) val |= SDHCI_SUPPORT_HS400; @@ -417,9 +421,9 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg) if (unlikely(reg == SDHCI_MAX_CURRENT) && esdhc_is_usdhc(imx_data)) { val = 0; - val |= 0xFF << SDHCI_MAX_CURRENT_330_SHIFT; - val |= 0xFF << SDHCI_MAX_CURRENT_300_SHIFT; - val |= 0xFF << SDHCI_MAX_CURRENT_180_SHIFT; + val |= FIELD_PREP(SDHCI_MAX_CURRENT_330_MASK, 0xFF); + val |= FIELD_PREP(SDHCI_MAX_CURRENT_300_MASK, 0xFF); + val |= FIELD_PREP(SDHCI_MAX_CURRENT_180_MASK, 0xFF); } if (unlikely(reg == SDHCI_INT_STATUS)) { @@ -1313,6 +1317,18 @@ static void sdhci_esdhc_imx_hwinit(struct sdhci_host *host) tmp |= imx_data->boarddata.tuning_step << ESDHC_TUNING_STEP_SHIFT; } + + /* Disable the CMD CRC check for tuning, if not, need to + * add some delay after every tuning command, because + * hardware standard tuning logic will directly go to next + * step once it detect the CMD CRC error, will not wait for + * the card side to finally send out the tuning data, trigger + * the buffer read ready interrupt immediately. If usdhc send + * the next tuning command some eMMC card will stuck, can't + * response, block the tuning procedure or the first command + * after the whole tuning procedure always can't get any response. + */ + tmp |= ESDHC_TUNING_CMD_CRC_CHECK_DISABLE; writel(tmp, host->ioaddr + ESDHC_TUNING_CTRL); } else if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) { /* @@ -1596,6 +1612,10 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) if (esdhc_is_usdhc(imx_data)) { host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN; host->mmc->caps |= MMC_CAP_1_8V_DDR | MMC_CAP_3_3V_DDR; + + /* GPIO CD can be set as a wakeup source */ + host->mmc->caps |= MMC_CAP_CD_WAKE; + if (!(imx_data->socdata->flags & ESDHC_FLAG_HS200)) host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200; @@ -1653,8 +1673,6 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) if (err) goto disable_ahb_clk; - host->tuning_delay = 1; - sdhci_esdhc_imx_hwinit(host); err = sdhci_add_host(host); @@ -1731,8 +1749,14 @@ static int sdhci_esdhc_suspend(struct device *dev) mmc_retune_needed(host->mmc); ret = sdhci_suspend_host(host); - if (!ret) - return pinctrl_pm_select_sleep_state(dev); + if (ret) + return ret; + + ret = pinctrl_pm_select_sleep_state(dev); + if (ret) + return ret; + + ret = mmc_gpio_set_cd_wake(host->mmc, true); return ret; } @@ -1756,6 +1780,9 @@ static int sdhci_esdhc_resume(struct device *dev) if (host->mmc->caps2 & MMC_CAP2_CQE) ret = cqhci_resume(host->mmc); + if (!ret) + ret = mmc_gpio_set_cd_wake(host->mmc, false); + return ret; } #endif diff --git a/drivers/mmc/host/sdhci-esdhc-mcf.c b/drivers/mmc/host/sdhci-esdhc-mcf.c new file mode 100644 index 000000000000..71bf086a9812 --- /dev/null +++ b/drivers/mmc/host/sdhci-esdhc-mcf.c @@ -0,0 +1,521 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Freescale eSDHC ColdFire family controller driver, platform bus. + * + * Copyright (c) 2020 Timesys Corporation + * Author: Angelo Dureghello <angelo.dureghello@timesys.it> + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/platform_data/mmc-esdhc-mcf.h> +#include <linux/mmc/mmc.h> +#include "sdhci-pltfm.h" +#include "sdhci-esdhc.h" + +#define ESDHC_PROCTL_D3CD 0x08 +#define ESDHC_SYS_CTRL_DTOCV_MASK 0x0f +#define ESDHC_DEFAULT_HOST_CONTROL 0x28 + +/* + * Freescale eSDHC has DMA ERR flag at bit 28, not as std spec says, bit 25. + */ +#define ESDHC_INT_VENDOR_SPEC_DMA_ERR BIT(28) + +struct pltfm_mcf_data { + struct clk *clk_ipg; + struct clk *clk_ahb; + struct clk *clk_per; + int aside; + int current_bus_width; +}; + +static inline void esdhc_mcf_buffer_swap32(u32 *buf, int len) +{ + int i; + u32 temp; + + len = (len + 3) >> 2; + + for (i = 0; i < len; i++) { + temp = swab32(*buf); + *buf++ = temp; + } +} + +static inline void esdhc_clrset_be(struct sdhci_host *host, + u32 mask, u32 val, int reg) +{ + void __iomem *base = host->ioaddr + (reg & ~3); + u8 shift = (reg & 3) << 3; + + mask <<= shift; + val <<= shift; + + if (reg == SDHCI_HOST_CONTROL) + val |= ESDHC_PROCTL_D3CD; + + writel((readl(base) & ~mask) | val, base); +} + +/* + * Note: mcf is big-endian, single bytes need to be accessed at big endian + * offsets. + */ +static void esdhc_mcf_writeb_be(struct sdhci_host *host, u8 val, int reg) +{ + void __iomem *base = host->ioaddr + (reg & ~3); + u8 shift = (reg & 3) << 3; + u32 mask = ~(0xff << shift); + + if (reg == SDHCI_HOST_CONTROL) { + u32 host_ctrl = ESDHC_DEFAULT_HOST_CONTROL; + u8 dma_bits = (val & SDHCI_CTRL_DMA_MASK) >> 3; + u8 tmp = readb(host->ioaddr + SDHCI_HOST_CONTROL + 1); + + tmp &= ~0x03; + tmp |= dma_bits; + + /* + * Recomposition needed, restore always endianness and + * keep D3CD and AI, just setting bus width. + */ + host_ctrl |= val; + host_ctrl |= (dma_bits << 8); + writel(host_ctrl, host->ioaddr + SDHCI_HOST_CONTROL); + + return; + } + + writel((readl(base) & mask) | (val << shift), base); +} + +static void esdhc_mcf_writew_be(struct sdhci_host *host, u16 val, int reg) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct pltfm_mcf_data *mcf_data = sdhci_pltfm_priv(pltfm_host); + void __iomem *base = host->ioaddr + (reg & ~3); + u8 shift = (reg & 3) << 3; + u32 mask = ~(0xffff << shift); + + switch (reg) { + case SDHCI_TRANSFER_MODE: + mcf_data->aside = val; + return; + case SDHCI_COMMAND: + if (host->cmd->opcode == MMC_STOP_TRANSMISSION) + val |= SDHCI_CMD_ABORTCMD; + + /* + * As for the fsl driver, + * we have to set the mode in a single write here. + */ + writel(val << 16 | mcf_data->aside, + host->ioaddr + SDHCI_TRANSFER_MODE); + return; + } + + writel((readl(base) & mask) | (val << shift), base); +} + +static void esdhc_mcf_writel_be(struct sdhci_host *host, u32 val, int reg) +{ + writel(val, host->ioaddr + reg); +} + +static u8 esdhc_mcf_readb_be(struct sdhci_host *host, int reg) +{ + if (reg == SDHCI_HOST_CONTROL) { + u8 __iomem *base = host->ioaddr + (reg & ~3); + u16 val = readw(base + 2); + u8 dma_bits = (val >> 5) & SDHCI_CTRL_DMA_MASK; + u8 host_ctrl = val & 0xff; + + host_ctrl &= ~SDHCI_CTRL_DMA_MASK; + host_ctrl |= dma_bits; + + return host_ctrl; + } + + return readb(host->ioaddr + (reg ^ 0x3)); +} + +static u16 esdhc_mcf_readw_be(struct sdhci_host *host, int reg) +{ + /* + * For SDHCI_HOST_VERSION, sdhci specs defines 0xFE, + * a wrong offset for us, we are at 0xFC. + */ + if (reg == SDHCI_HOST_VERSION) + reg -= 2; + + return readw(host->ioaddr + (reg ^ 0x2)); +} + +static u32 esdhc_mcf_readl_be(struct sdhci_host *host, int reg) +{ + u32 val; + + val = readl(host->ioaddr + reg); + + /* + * RM (25.3.9) sd pin clock must never exceed 25Mhz. + * So forcing legacy mode at 25Mhz. + */ + if (unlikely(reg == SDHCI_CAPABILITIES)) + val &= ~SDHCI_CAN_DO_HISPD; + + if (unlikely(reg == SDHCI_INT_STATUS)) { + if (val & ESDHC_INT_VENDOR_SPEC_DMA_ERR) { + val &= ~ESDHC_INT_VENDOR_SPEC_DMA_ERR; + val |= SDHCI_INT_ADMA_ERROR; + } + } + + return val; +} + +static unsigned int esdhc_mcf_get_max_timeout_count(struct sdhci_host *host) +{ + return 1 << 27; +} + +static void esdhc_mcf_set_timeout(struct sdhci_host *host, + struct mmc_command *cmd) +{ + /* Use maximum timeout counter */ + esdhc_clrset_be(host, ESDHC_SYS_CTRL_DTOCV_MASK, 0xE, + SDHCI_TIMEOUT_CONTROL); +} + +static void esdhc_mcf_reset(struct sdhci_host *host, u8 mask) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct pltfm_mcf_data *mcf_data = sdhci_pltfm_priv(pltfm_host); + + sdhci_reset(host, mask); + + esdhc_clrset_be(host, ESDHC_CTRL_BUSWIDTH_MASK, + mcf_data->current_bus_width, SDHCI_HOST_CONTROL); + + sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); + sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); +} + +static unsigned int esdhc_mcf_pltfm_get_max_clock(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + + return pltfm_host->clock; +} + +static unsigned int esdhc_mcf_pltfm_get_min_clock(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + + return pltfm_host->clock / 256 / 16; +} + +static void esdhc_mcf_pltfm_set_clock(struct sdhci_host *host, + unsigned int clock) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + unsigned long *pll_dr = (unsigned long *)MCF_PLL_DR; + u32 fvco, fsys, fesdhc, temp; + const int sdclkfs[] = {2, 4, 8, 16, 32, 64, 128, 256}; + int delta, old_delta = clock; + int i, q, ri, rq; + + if (clock == 0) { + host->mmc->actual_clock = 0; + return; + } + + /* + * ColdFire eSDHC clock.s + * + * pll -+-> / outdiv1 --> fsys + * +-> / outdiv3 --> eSDHC clock ---> / SDCCLKFS / DVS + * + * mcf5441x datasheet says: + * (8.1.2) eSDHC should be 40 MHz max + * (25.3.9) eSDHC input is, as example, 96 Mhz ... + * (25.3.9) sd pin clock must never exceed 25Mhz + * + * fvco = fsys * outdvi1 + 1 + * fshdc = fvco / outdiv3 + 1 + */ + temp = readl(pll_dr); + fsys = pltfm_host->clock; + fvco = fsys * ((temp & 0x1f) + 1); + fesdhc = fvco / (((temp >> 10) & 0x1f) + 1); + + for (i = 0; i < 8; ++i) { + int result = fesdhc / sdclkfs[i]; + + for (q = 1; q < 17; ++q) { + int finale = result / q; + + delta = abs(clock - finale); + + if (delta < old_delta) { + old_delta = delta; + ri = i; + rq = q; + } + } + } + + /* + * Apply divisors and re-enable all the clocks + */ + temp = ((sdclkfs[ri] >> 1) << 8) | ((rq - 1) << 4) | + (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN); + esdhc_clrset_be(host, 0x0000fff7, temp, SDHCI_CLOCK_CONTROL); + + host->mmc->actual_clock = clock; + + mdelay(1); +} + +static void esdhc_mcf_pltfm_set_bus_width(struct sdhci_host *host, int width) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct pltfm_mcf_data *mcf_data = sdhci_pltfm_priv(pltfm_host); + + switch (width) { + case MMC_BUS_WIDTH_4: + mcf_data->current_bus_width = ESDHC_CTRL_4BITBUS; + break; + default: + mcf_data->current_bus_width = 0; + break; + } + + esdhc_clrset_be(host, ESDHC_CTRL_BUSWIDTH_MASK, + mcf_data->current_bus_width, SDHCI_HOST_CONTROL); +} + +static void esdhc_mcf_request_done(struct sdhci_host *host, + struct mmc_request *mrq) +{ + struct scatterlist *sg; + u32 *buffer; + int i; + + if (!mrq->data || !mrq->data->bytes_xfered) + goto exit_done; + + if (mmc_get_dma_dir(mrq->data) != DMA_FROM_DEVICE) + goto exit_done; + + /* + * On mcf5441x there is no hw sdma option/flag to select the dma + * transfer endiannes. A swap after the transfer is needed. + */ + for_each_sg(mrq->data->sg, sg, mrq->data->sg_len, i) { + buffer = (u32 *)sg_virt(sg); + esdhc_mcf_buffer_swap32(buffer, sg->length); + } + +exit_done: + mmc_request_done(host->mmc, mrq); +} + +static void esdhc_mcf_copy_to_bounce_buffer(struct sdhci_host *host, + struct mmc_data *data, + unsigned int length) +{ + sg_copy_to_buffer(data->sg, data->sg_len, + host->bounce_buffer, length); + + esdhc_mcf_buffer_swap32((u32 *)host->bounce_buffer, + data->blksz * data->blocks); +} + +static struct sdhci_ops sdhci_esdhc_ops = { + .reset = esdhc_mcf_reset, + .set_clock = esdhc_mcf_pltfm_set_clock, + .get_max_clock = esdhc_mcf_pltfm_get_max_clock, + .get_min_clock = esdhc_mcf_pltfm_get_min_clock, + .set_bus_width = esdhc_mcf_pltfm_set_bus_width, + .get_max_timeout_count = esdhc_mcf_get_max_timeout_count, + .set_timeout = esdhc_mcf_set_timeout, + .write_b = esdhc_mcf_writeb_be, + .write_w = esdhc_mcf_writew_be, + .write_l = esdhc_mcf_writel_be, + .read_b = esdhc_mcf_readb_be, + .read_w = esdhc_mcf_readw_be, + .read_l = esdhc_mcf_readl_be, + .copy_to_bounce_buffer = esdhc_mcf_copy_to_bounce_buffer, + .request_done = esdhc_mcf_request_done, +}; + +static const struct sdhci_pltfm_data sdhci_esdhc_mcf_pdata = { + .ops = &sdhci_esdhc_ops, + .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_FORCE_DMA, + /* + * Mandatory quirk, + * controller does not support cmd23, + * without, on > 8G cards cmd23 is used, and + * driver times out. + */ + SDHCI_QUIRK2_HOST_NO_CMD23, +}; + +static int esdhc_mcf_plat_init(struct sdhci_host *host, + struct pltfm_mcf_data *mcf_data) +{ + struct mcf_esdhc_platform_data *plat_data; + + if (!host->mmc->parent->platform_data) { + dev_err(mmc_dev(host->mmc), "no platform data!\n"); + return -EINVAL; + } + + plat_data = (struct mcf_esdhc_platform_data *) + host->mmc->parent->platform_data; + + /* Card_detect */ + switch (plat_data->cd_type) { + default: + case ESDHC_CD_CONTROLLER: + /* We have a working card_detect back */ + host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION; + break; + case ESDHC_CD_PERMANENT: + host->mmc->caps |= MMC_CAP_NONREMOVABLE; + break; + case ESDHC_CD_NONE: + break; + } + + switch (plat_data->max_bus_width) { + case 4: + host->mmc->caps |= MMC_CAP_4_BIT_DATA; + break; + case 1: + default: + host->quirks |= SDHCI_QUIRK_FORCE_1_BIT_DATA; + break; + } + + return 0; +} + +static int sdhci_esdhc_mcf_probe(struct platform_device *pdev) +{ + struct sdhci_host *host; + struct sdhci_pltfm_host *pltfm_host; + struct pltfm_mcf_data *mcf_data; + int err; + + host = sdhci_pltfm_init(pdev, &sdhci_esdhc_mcf_pdata, + sizeof(*mcf_data)); + + if (IS_ERR(host)) + return PTR_ERR(host); + + pltfm_host = sdhci_priv(host); + mcf_data = sdhci_pltfm_priv(pltfm_host); + + host->sdma_boundary = 0; + + host->flags |= SDHCI_AUTO_CMD12; + + mcf_data->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); + if (IS_ERR(mcf_data->clk_ipg)) { + err = PTR_ERR(mcf_data->clk_ipg); + goto err_exit; + } + + mcf_data->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); + if (IS_ERR(mcf_data->clk_ahb)) { + err = PTR_ERR(mcf_data->clk_ahb); + goto err_exit; + } + + mcf_data->clk_per = devm_clk_get(&pdev->dev, "per"); + if (IS_ERR(mcf_data->clk_per)) { + err = PTR_ERR(mcf_data->clk_per); + goto err_exit; + } + + pltfm_host->clk = mcf_data->clk_per; + pltfm_host->clock = clk_get_rate(pltfm_host->clk); + err = clk_prepare_enable(mcf_data->clk_per); + if (err) + goto err_exit; + + err = clk_prepare_enable(mcf_data->clk_ipg); + if (err) + goto unprep_per; + + err = clk_prepare_enable(mcf_data->clk_ahb); + if (err) + goto unprep_ipg; + + err = esdhc_mcf_plat_init(host, mcf_data); + if (err) + goto unprep_ahb; + + err = sdhci_setup_host(host); + if (err) + goto unprep_ahb; + + if (!host->bounce_buffer) { + dev_err(&pdev->dev, "bounce buffer not allocated"); + err = -ENOMEM; + goto cleanup; + } + + err = __sdhci_add_host(host); + if (err) + goto cleanup; + + return 0; + +cleanup: + sdhci_cleanup_host(host); +unprep_ahb: + clk_disable_unprepare(mcf_data->clk_ahb); +unprep_ipg: + clk_disable_unprepare(mcf_data->clk_ipg); +unprep_per: + clk_disable_unprepare(mcf_data->clk_per); +err_exit: + sdhci_pltfm_free(pdev); + + return err; +} + +static int sdhci_esdhc_mcf_remove(struct platform_device *pdev) +{ + struct sdhci_host *host = platform_get_drvdata(pdev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct pltfm_mcf_data *mcf_data = sdhci_pltfm_priv(pltfm_host); + + sdhci_remove_host(host, 0); + + clk_disable_unprepare(mcf_data->clk_ipg); + clk_disable_unprepare(mcf_data->clk_ahb); + clk_disable_unprepare(mcf_data->clk_per); + + sdhci_pltfm_free(pdev); + + return 0; +} + +static struct platform_driver sdhci_esdhc_mcf_driver = { + .driver = { + .name = "sdhci-esdhc-mcf", + }, + .probe = sdhci_esdhc_mcf_probe, + .remove = sdhci_esdhc_mcf_remove, +}; + +module_platform_driver(sdhci_esdhc_mcf_driver); + +MODULE_DESCRIPTION("SDHCI driver for Freescale ColdFire eSDHC"); +MODULE_AUTHOR("Angelo Dureghello <angelo.dureghello@timesys.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h index 947212f16bc6..a30796e79b1c 100644 --- a/drivers/mmc/host/sdhci-esdhc.h +++ b/drivers/mmc/host/sdhci-esdhc.h @@ -5,7 +5,7 @@ * Copyright (c) 2007 Freescale Semiconductor, Inc. * Copyright (c) 2009 MontaVista Software, Inc. * Copyright (c) 2010 Pengutronix e.K. - * Author: Wolfram Sang <w.sang@pengutronix.de> + * Author: Wolfram Sang <kernel@pengutronix.de> */ #ifndef _DRIVERS_MMC_SDHCI_ESDHC_H diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index a8bcb3f16aa4..b277dd7fbdb5 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -10,6 +10,7 @@ #include <linux/delay.h> #include <linux/mmc/mmc.h> #include <linux/pm_runtime.h> +#include <linux/pm_opp.h> #include <linux/slab.h> #include <linux/iopoll.h> #include <linux/regulator/consumer.h> @@ -56,19 +57,27 @@ #define CORE_FLL_CYCLE_CNT BIT(18) #define CORE_DLL_CLOCK_DISABLE BIT(21) -#define CORE_VENDOR_SPEC_POR_VAL 0xa1c +#define DLL_USR_CTL_POR_VAL 0x10800 +#define ENABLE_DLL_LOCK_STATUS BIT(26) +#define FINE_TUNE_MODE_EN BIT(27) +#define BIAS_OK_SIGNAL BIT(29) + +#define DLL_CONFIG_3_LOW_FREQ_VAL 0x08 +#define DLL_CONFIG_3_HIGH_FREQ_VAL 0x10 + +#define CORE_VENDOR_SPEC_POR_VAL 0xa9c #define CORE_CLK_PWRSAVE BIT(1) #define CORE_HC_MCLK_SEL_DFLT (2 << 8) #define CORE_HC_MCLK_SEL_HS400 (3 << 8) #define CORE_HC_MCLK_SEL_MASK (3 << 8) -#define CORE_IO_PAD_PWR_SWITCH_EN (1 << 15) -#define CORE_IO_PAD_PWR_SWITCH (1 << 16) +#define CORE_IO_PAD_PWR_SWITCH_EN BIT(15) +#define CORE_IO_PAD_PWR_SWITCH BIT(16) #define CORE_HC_SELECT_IN_EN BIT(18) #define CORE_HC_SELECT_IN_HS400 (6 << 19) #define CORE_HC_SELECT_IN_MASK (7 << 19) -#define CORE_3_0V_SUPPORT (1 << 25) -#define CORE_1_8V_SUPPORT (1 << 26) +#define CORE_3_0V_SUPPORT BIT(25) +#define CORE_1_8V_SUPPORT BIT(26) #define CORE_VOLT_SUPPORT (CORE_3_0V_SUPPORT | CORE_1_8V_SUPPORT) #define CORE_CSR_CDC_CTLR_CFG0 0x130 @@ -156,6 +165,7 @@ struct sdhci_msm_offset { u32 core_dll_config_3; u32 core_ddr_config_old; /* Applicable to sdcc minor ver < 0x49 */ u32 core_ddr_config; + u32 core_dll_usr_ctl; /* Present on SDCC5.1 onwards */ }; static const struct sdhci_msm_offset sdhci_msm_v5_offset = { @@ -185,6 +195,7 @@ static const struct sdhci_msm_offset sdhci_msm_v5_offset = { .core_dll_config_2 = 0x254, .core_dll_config_3 = 0x258, .core_ddr_config = 0x25c, + .core_dll_usr_ctl = 0x388, }; static const struct sdhci_msm_offset sdhci_msm_mci_offset = { @@ -230,6 +241,7 @@ struct sdhci_msm_variant_ops { struct sdhci_msm_variant_info { bool mci_removed; bool restore_dll_config; + bool uses_tassadar_dll; const struct sdhci_msm_variant_ops *var_ops; const struct sdhci_msm_offset *offset; }; @@ -243,6 +255,8 @@ struct sdhci_msm_host { struct clk_bulk_data bulk_clks[4]; /* core, iface, cal, sleep clocks */ unsigned long clk_rate; struct mmc_host *mmc; + struct opp_table *opp_table; + bool has_opp_table; bool use_14lpp_dll_reset; bool tuning_done; bool calibration_done; @@ -260,6 +274,9 @@ struct sdhci_msm_host { bool use_cdr; u32 transfer_mode; bool updated_ddr_cfg; + bool uses_tassadar_dll; + u32 dll_config; + u32 ddr_config; }; static const struct sdhci_msm_offset *sdhci_priv_msm_offset(struct sdhci_host *host) @@ -332,7 +349,7 @@ static void msm_set_clock_rate_for_bus_mode(struct sdhci_host *host, int rc; clock = msm_get_clock_rate_for_bus_mode(host, clock); - rc = clk_set_rate(core_clk, clock); + rc = dev_pm_opp_set_rate(mmc_dev(host->mmc), clock); if (rc) { pr_err("%s: Failed to set clock at rate %u at timing %d\n", mmc_hostname(host->mmc), clock, @@ -601,6 +618,9 @@ static int msm_init_cm_dll(struct sdhci_host *host) config &= ~CORE_CLK_PWRSAVE; writel_relaxed(config, host->ioaddr + msm_offset->core_vendor_spec); + config = msm_host->dll_config; + writel_relaxed(config, host->ioaddr + msm_offset->core_dll_config); + if (msm_host->use_14lpp_dll_reset) { config = readl_relaxed(host->ioaddr + msm_offset->core_dll_config); @@ -626,7 +646,9 @@ static int msm_init_cm_dll(struct sdhci_host *host) config |= CORE_DLL_PDN; writel_relaxed(config, host->ioaddr + msm_offset->core_dll_config); - msm_cm_dll_set_freq(host); + + if (!msm_host->dll_config) + msm_cm_dll_set_freq(host); if (msm_host->use_14lpp_dll_reset && !IS_ERR_OR_NULL(msm_host->xo_clk)) { @@ -666,7 +688,8 @@ static int msm_init_cm_dll(struct sdhci_host *host) msm_offset->core_dll_config); if (msm_host->use_14lpp_dll_reset) { - msm_cm_dll_set_freq(host); + if (!msm_host->dll_config) + msm_cm_dll_set_freq(host); config = readl_relaxed(host->ioaddr + msm_offset->core_dll_config_2); config &= ~CORE_DLL_CLOCK_DISABLE; @@ -674,6 +697,27 @@ static int msm_init_cm_dll(struct sdhci_host *host) msm_offset->core_dll_config_2); } + /* + * Configure DLL user control register to enable DLL status. + * This setting is applicable to SDCC v5.1 onwards only. + */ + if (msm_host->uses_tassadar_dll) { + config = DLL_USR_CTL_POR_VAL | FINE_TUNE_MODE_EN | + ENABLE_DLL_LOCK_STATUS | BIAS_OK_SIGNAL; + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_usr_ctl); + + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config_3); + config &= ~0xFF; + if (msm_host->clk_rate < 150000000) + config |= DLL_CONFIG_3_LOW_FREQ_VAL; + else + config |= DLL_CONFIG_3_HIGH_FREQ_VAL; + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config_3); + } + config = readl_relaxed(host->ioaddr + msm_offset->core_dll_config); config |= CORE_DLL_EN; @@ -951,7 +995,7 @@ static int sdhci_msm_cm_dll_sdc4_calibration(struct sdhci_host *host) ddr_cfg_offset = msm_offset->core_ddr_config; else ddr_cfg_offset = msm_offset->core_ddr_config_old; - writel_relaxed(DDR_CONFIG_POR_VAL, host->ioaddr + ddr_cfg_offset); + writel_relaxed(msm_host->ddr_config, host->ioaddr + ddr_cfg_offset); if (mmc->ios.enhanced_strobe) { config = readl_relaxed(host->ioaddr + @@ -1130,6 +1174,12 @@ static int sdhci_msm_execute_tuning(struct mmc_host *mmc, u32 opcode) msm_host->use_cdr = true; /* + * Clear tuning_done flag before tuning to ensure proper + * HS400 settings. + */ + msm_host->tuning_done = 0; + + /* * For HS400 tuning in HS200 timing requires: * - select MCLK/2 in VENDOR_SPEC * - program MCLK to 400MHz (or nearest supported) in GCC @@ -1830,6 +1880,36 @@ static void sdhci_msm_reset(struct sdhci_host *host, u8 mask) sdhci_reset(host, mask); } +#define DRIVER_NAME "sdhci_msm" +#define SDHCI_MSM_DUMP(f, x...) \ + pr_err("%s: " DRIVER_NAME ": " f, mmc_hostname(host->mmc), ## x) + +void sdhci_msm_dump_vendor_regs(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + const struct sdhci_msm_offset *msm_offset = msm_host->offset; + + SDHCI_MSM_DUMP("----------- VENDOR REGISTER DUMP -----------\n"); + + SDHCI_MSM_DUMP( + "DLL sts: 0x%08x | DLL cfg: 0x%08x | DLL cfg2: 0x%08x\n", + readl_relaxed(host->ioaddr + msm_offset->core_dll_status), + readl_relaxed(host->ioaddr + msm_offset->core_dll_config), + readl_relaxed(host->ioaddr + msm_offset->core_dll_config_2)); + SDHCI_MSM_DUMP( + "DLL cfg3: 0x%08x | DLL usr ctl: 0x%08x | DDR cfg: 0x%08x\n", + readl_relaxed(host->ioaddr + msm_offset->core_dll_config_3), + readl_relaxed(host->ioaddr + msm_offset->core_dll_usr_ctl), + readl_relaxed(host->ioaddr + msm_offset->core_ddr_config)); + SDHCI_MSM_DUMP( + "Vndr func: 0x%08x | Vndr func2 : 0x%08x Vndr func3: 0x%08x\n", + readl_relaxed(host->ioaddr + msm_offset->core_vendor_spec), + readl_relaxed(host->ioaddr + + msm_offset->core_vendor_spec_func2), + readl_relaxed(host->ioaddr + msm_offset->core_vendor_spec3)); +} + static const struct sdhci_msm_variant_ops mci_var_ops = { .msm_readl_relaxed = sdhci_msm_mci_variant_readl_relaxed, .msm_writel_relaxed = sdhci_msm_mci_variant_writel_relaxed, @@ -1858,10 +1938,18 @@ static const struct sdhci_msm_variant_info sdm845_sdhci_var = { .offset = &sdhci_msm_v5_offset, }; +static const struct sdhci_msm_variant_info sm8250_sdhci_var = { + .mci_removed = true, + .uses_tassadar_dll = true, + .var_ops = &v5_var_ops, + .offset = &sdhci_msm_v5_offset, +}; + static const struct of_device_id sdhci_msm_dt_match[] = { {.compatible = "qcom,sdhci-msm-v4", .data = &sdhci_msm_mci_var}, {.compatible = "qcom,sdhci-msm-v5", .data = &sdhci_msm_v5_var}, {.compatible = "qcom,sdm845-sdhci", .data = &sdm845_sdhci_var}, + {.compatible = "qcom,sm8250-sdhci", .data = &sm8250_sdhci_var}, {}, }; @@ -1877,16 +1965,34 @@ static const struct sdhci_ops sdhci_msm_ops = { .write_w = sdhci_msm_writew, .write_b = sdhci_msm_writeb, .irq = sdhci_msm_cqe_irq, + .dump_vendor_regs = sdhci_msm_dump_vendor_regs, }; static const struct sdhci_pltfm_data sdhci_msm_pdata = { .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION | SDHCI_QUIRK_SINGLE_POWER_WRITE | - SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, + SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | + SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, .ops = &sdhci_msm_ops, }; +static inline void sdhci_msm_get_of_property(struct platform_device *pdev, + struct sdhci_host *host) +{ + struct device_node *node = pdev->dev.of_node; + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + + if (of_property_read_u32(node, "qcom,ddr-config", + &msm_host->ddr_config)) + msm_host->ddr_config = DDR_CONFIG_POR_VAL; + + of_property_read_u32(node, "qcom,dll-config", &msm_host->dll_config); +} + + static int sdhci_msm_probe(struct platform_device *pdev) { struct sdhci_host *host; @@ -1925,10 +2031,12 @@ static int sdhci_msm_probe(struct platform_device *pdev) msm_host->restore_dll_config = var_info->restore_dll_config; msm_host->var_ops = var_info->var_ops; msm_host->offset = var_info->offset; + msm_host->uses_tassadar_dll = var_info->uses_tassadar_dll; msm_offset = msm_host->offset; sdhci_get_of_property(pdev); + sdhci_msm_get_of_property(pdev, host); msm_host->saved_tuning_phase = INVALID_TUNING_PHASE; @@ -1962,8 +2070,23 @@ static int sdhci_msm_probe(struct platform_device *pdev) } msm_host->bulk_clks[0].clk = clk; + msm_host->opp_table = dev_pm_opp_set_clkname(&pdev->dev, "core"); + if (IS_ERR(msm_host->opp_table)) { + ret = PTR_ERR(msm_host->opp_table); + goto bus_clk_disable; + } + + /* OPP table is optional */ + ret = dev_pm_opp_of_add_table(&pdev->dev); + if (!ret) { + msm_host->has_opp_table = true; + } else if (ret != -ENODEV) { + dev_err(&pdev->dev, "Invalid OPP table in Device tree\n"); + goto opp_cleanup; + } + /* Vote for maximum clock rate for maximum performance */ - ret = clk_set_rate(clk, INT_MAX); + ret = dev_pm_opp_set_rate(&pdev->dev, INT_MAX); if (ret) dev_warn(&pdev->dev, "core clock boost failed\n"); @@ -1980,7 +2103,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) ret = clk_bulk_prepare_enable(ARRAY_SIZE(msm_host->bulk_clks), msm_host->bulk_clks); if (ret) - goto bus_clk_disable; + goto opp_cleanup; /* * xo clock is needed for FLL feature of cm_dll. @@ -2117,6 +2240,10 @@ pm_runtime_disable: clk_disable: clk_bulk_disable_unprepare(ARRAY_SIZE(msm_host->bulk_clks), msm_host->bulk_clks); +opp_cleanup: + if (msm_host->has_opp_table) + dev_pm_opp_of_remove_table(&pdev->dev); + dev_pm_opp_put_clkname(msm_host->opp_table); bus_clk_disable: if (!IS_ERR(msm_host->bus_clk)) clk_disable_unprepare(msm_host->bus_clk); @@ -2135,6 +2262,9 @@ static int sdhci_msm_remove(struct platform_device *pdev) sdhci_remove_host(host, dead); + if (msm_host->has_opp_table) + dev_pm_opp_of_remove_table(&pdev->dev); + dev_pm_opp_put_clkname(msm_host->opp_table); pm_runtime_get_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); pm_runtime_put_noidle(&pdev->dev); @@ -2153,6 +2283,8 @@ static __maybe_unused int sdhci_msm_runtime_suspend(struct device *dev) struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + /* Drop the performance vote */ + dev_pm_opp_set_rate(dev, 0); clk_bulk_disable_unprepare(ARRAY_SIZE(msm_host->bulk_clks), msm_host->bulk_clks); @@ -2175,9 +2307,11 @@ static __maybe_unused int sdhci_msm_runtime_resume(struct device *dev) * restore the SDR DLL settings when the clock is ungated. */ if (msm_host->restore_dll_config && msm_host->clk_rate) - return sdhci_msm_restore_sdr_dll_config(host); + ret = sdhci_msm_restore_sdr_dll_config(host); - return 0; + dev_pm_opp_set_rate(dev, msm_host->clk_rate); + + return ret; } static const struct dev_pm_ops sdhci_msm_pm_ops = { diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index d4905c106c06..2a4c8a2f3e64 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -28,15 +28,26 @@ #include "sdhci-pltfm.h" #define SDHCI_ARASAN_VENDOR_REGISTER 0x78 + +#define SDHCI_ARASAN_ITAPDLY_REGISTER 0xF0F8 +#define SDHCI_ARASAN_OTAPDLY_REGISTER 0xF0FC + #define SDHCI_ARASAN_CQE_BASE_ADDR 0x200 #define VENDOR_ENHANCED_STROBE BIT(0) #define PHY_CLK_TOO_SLOW_HZ 400000 +#define SDHCI_ITAPDLY_CHGWIN 0x200 +#define SDHCI_ITAPDLY_ENABLE 0x100 +#define SDHCI_OTAPDLY_ENABLE 0x40 + /* Default settings for ZynqMP Clock Phases */ #define ZYNQMP_ICLK_PHASE {0, 63, 63, 0, 63, 0, 0, 183, 54, 0, 0} #define ZYNQMP_OCLK_PHASE {0, 72, 60, 0, 60, 72, 135, 48, 72, 135, 0} +#define VERSAL_ICLK_PHASE {0, 132, 132, 0, 132, 0, 0, 162, 90, 0, 0} +#define VERSAL_OCLK_PHASE {0, 60, 48, 0, 48, 72, 90, 36, 60, 90, 0} + /* * On some SoCs the syscon area has a feature where the upper 16-bits of * each 32-bit register act as a write mask for the lower 16-bits. This allows @@ -62,22 +73,36 @@ struct sdhci_arasan_soc_ctl_field { /** * struct sdhci_arasan_soc_ctl_map - Map in syscon to corecfg registers * - * It's up to the licensee of the Arsan IP block to make these available - * somewhere if needed. Presumably these will be scattered somewhere that's - * accessible via the syscon API. - * * @baseclkfreq: Where to find corecfg_baseclkfreq * @clockmultiplier: Where to find corecfg_clockmultiplier + * @support64b: Where to find SUPPORT64B bit * @hiword_update: If true, use HIWORD_UPDATE to access the syscon + * + * It's up to the licensee of the Arsan IP block to make these available + * somewhere if needed. Presumably these will be scattered somewhere that's + * accessible via the syscon API. */ struct sdhci_arasan_soc_ctl_map { struct sdhci_arasan_soc_ctl_field baseclkfreq; struct sdhci_arasan_soc_ctl_field clockmultiplier; + struct sdhci_arasan_soc_ctl_field support64b; bool hiword_update; }; /** - * struct sdhci_arasan_clk_data + * struct sdhci_arasan_clk_ops - Clock Operations for Arasan SD controller + * + * @sdcardclk_ops: The output clock related operations + * @sampleclk_ops: The sample clock related operations + */ +struct sdhci_arasan_clk_ops { + const struct clk_ops *sdcardclk_ops; + const struct clk_ops *sampleclk_ops; +}; + +/** + * struct sdhci_arasan_clk_data - Arasan Controller Clock Data. + * * @sdcardclk_hw: Struct for the clock we might provide to a PHY. * @sdcardclk: Pointer to normal 'struct clock' for sdcardclk_hw. * @sampleclk_hw: Struct for the clock we might provide to a PHY. @@ -103,14 +128,18 @@ struct sdhci_arasan_zynqmp_clk_data { }; /** - * struct sdhci_arasan_data + * struct sdhci_arasan_data - Arasan Controller Data + * * @host: Pointer to the main SDHCI host structure. * @clk_ahb: Pointer to the AHB clock * @phy: Pointer to the generic phy * @is_phy_on: True if the PHY is on; false if not. + * @has_cqe: True if controller has command queuing engine. * @clk_data: Struct for the Arasan Controller Clock Data. + * @clk_ops: Struct for the Arasan Controller Clock Operations. * @soc_ctl_base: Pointer to regmap for syscon for soc_ctl registers. * @soc_ctl_map: Map to get offsets into soc_ctl registers. + * @quirks: Arasan deviations from spec. */ struct sdhci_arasan_data { struct sdhci_host *host; @@ -120,10 +149,11 @@ struct sdhci_arasan_data { bool has_cqe; struct sdhci_arasan_clk_data clk_data; + const struct sdhci_arasan_clk_ops *clk_ops; struct regmap *soc_ctl_base; const struct sdhci_arasan_soc_ctl_map *soc_ctl_map; - unsigned int quirks; /* Arasan deviations from spec */ + unsigned int quirks; /* Controller does not have CD wired and will not function normally without */ #define SDHCI_ARASAN_QUIRK_FORCE_CDTEST BIT(0) @@ -135,6 +165,7 @@ struct sdhci_arasan_data { struct sdhci_arasan_of_data { const struct sdhci_arasan_soc_ctl_map *soc_ctl_map; const struct sdhci_pltfm_data *pdata; + const struct sdhci_arasan_clk_ops *clk_ops; }; static const struct sdhci_arasan_soc_ctl_map rk3399_soc_ctl_map = { @@ -155,17 +186,26 @@ static const struct sdhci_arasan_soc_ctl_map intel_lgm_sdxc_soc_ctl_map = { .hiword_update = false, }; +static const struct sdhci_arasan_soc_ctl_map intel_keembay_soc_ctl_map = { + .baseclkfreq = { .reg = 0x0, .width = 8, .shift = 14 }, + .clockmultiplier = { .reg = 0x4, .width = 8, .shift = 14 }, + .support64b = { .reg = 0x4, .width = 1, .shift = 24 }, + .hiword_update = false, +}; + /** * sdhci_arasan_syscon_write - Write to a field in soc_ctl registers * + * @host: The sdhci_host + * @fld: The field to write to + * @val: The value to write + * * This function allows writing to fields in sdhci_arasan_soc_ctl_map. * Note that if a field is specified as not available (shift < 0) then * this function will silently return an error code. It will be noisy * and print errors for any other (unexpected) errors. * - * @host: The sdhci_host - * @fld: The field to write to - * @val: The value to write + * Return: 0 on success and error value on error */ static int sdhci_arasan_syscon_write(struct sdhci_host *host, const struct sdhci_arasan_soc_ctl_field *fld, @@ -335,29 +375,6 @@ static const struct sdhci_ops sdhci_arasan_ops = { .set_power = sdhci_set_power_and_bus_voltage, }; -static const struct sdhci_pltfm_data sdhci_arasan_pdata = { - .ops = &sdhci_arasan_ops, - .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, - .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | - SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN | - SDHCI_QUIRK2_STOP_WITH_TC, -}; - -static struct sdhci_arasan_of_data sdhci_arasan_data = { - .pdata = &sdhci_arasan_pdata, -}; - -static const struct sdhci_pltfm_data sdhci_arasan_zynqmp_pdata = { - .ops = &sdhci_arasan_ops, - .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | - SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN | - SDHCI_QUIRK2_STOP_WITH_TC, -}; - -static struct sdhci_arasan_of_data sdhci_arasan_zynqmp_data = { - .pdata = &sdhci_arasan_zynqmp_pdata, -}; - static u32 sdhci_arasan_cqhci_irq(struct sdhci_host *host, u32 intmask) { int cmd_error = 0; @@ -414,28 +431,14 @@ static const struct sdhci_pltfm_data sdhci_arasan_cqe_pdata = { SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN, }; -static struct sdhci_arasan_of_data sdhci_arasan_rk3399_data = { - .soc_ctl_map = &rk3399_soc_ctl_map, - .pdata = &sdhci_arasan_cqe_pdata, -}; - -static struct sdhci_arasan_of_data intel_lgm_emmc_data = { - .soc_ctl_map = &intel_lgm_emmc_soc_ctl_map, - .pdata = &sdhci_arasan_cqe_pdata, -}; - -static struct sdhci_arasan_of_data intel_lgm_sdxc_data = { - .soc_ctl_map = &intel_lgm_sdxc_soc_ctl_map, - .pdata = &sdhci_arasan_cqe_pdata, -}; - #ifdef CONFIG_PM_SLEEP /** * sdhci_arasan_suspend - Suspend method for the driver * @dev: Address of the device structure - * Returns 0 on success and error value on error * * Put the device in a low power state. + * + * Return: 0 on success and error value on error */ static int sdhci_arasan_suspend(struct device *dev) { @@ -476,9 +479,10 @@ static int sdhci_arasan_suspend(struct device *dev) /** * sdhci_arasan_resume - Resume method for the driver * @dev: Address of the device structure - * Returns 0 on success and error value on error * * Resume operation after suspend + * + * Return: 0 on success and error value on error */ static int sdhci_arasan_resume(struct device *dev) { @@ -524,54 +528,19 @@ static int sdhci_arasan_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(sdhci_arasan_dev_pm_ops, sdhci_arasan_suspend, sdhci_arasan_resume); -static const struct of_device_id sdhci_arasan_of_match[] = { - /* SoC-specific compatible strings w/ soc_ctl_map */ - { - .compatible = "rockchip,rk3399-sdhci-5.1", - .data = &sdhci_arasan_rk3399_data, - }, - { - .compatible = "intel,lgm-sdhci-5.1-emmc", - .data = &intel_lgm_emmc_data, - }, - { - .compatible = "intel,lgm-sdhci-5.1-sdxc", - .data = &intel_lgm_sdxc_data, - }, - /* Generic compatible below here */ - { - .compatible = "arasan,sdhci-8.9a", - .data = &sdhci_arasan_data, - }, - { - .compatible = "arasan,sdhci-5.1", - .data = &sdhci_arasan_data, - }, - { - .compatible = "arasan,sdhci-4.9a", - .data = &sdhci_arasan_data, - }, - { - .compatible = "xlnx,zynqmp-8.9a", - .data = &sdhci_arasan_zynqmp_data, - }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match); - /** * sdhci_arasan_sdcardclk_recalc_rate - Return the card clock rate * + * @hw: Pointer to the hardware clock structure. + * @parent_rate: The parent rate (should be rate of clk_xin). + * * Return the current actual rate of the SD card clock. This can be used * to communicate with out PHY. * - * @hw: Pointer to the hardware clock structure. - * @parent_rate The parent rate (should be rate of clk_xin). - * Returns the card clock rate. + * Return: The card clock rate. */ static unsigned long sdhci_arasan_sdcardclk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) - { struct sdhci_arasan_clk_data *clk_data = container_of(hw, struct sdhci_arasan_clk_data, sdcardclk_hw); @@ -589,16 +558,16 @@ static const struct clk_ops arasan_sdcardclk_ops = { /** * sdhci_arasan_sampleclk_recalc_rate - Return the sampling clock rate * + * @hw: Pointer to the hardware clock structure. + * @parent_rate: The parent rate (should be rate of clk_xin). + * * Return the current actual rate of the sampling clock. This can be used * to communicate with out PHY. * - * @hw: Pointer to the hardware clock structure. - * @parent_rate The parent rate (should be rate of clk_xin). - * Returns the sample clock rate. + * Return: The sample clock rate. */ static unsigned long sdhci_arasan_sampleclk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) - { struct sdhci_arasan_clk_data *clk_data = container_of(hw, struct sdhci_arasan_clk_data, sampleclk_hw); @@ -616,14 +585,14 @@ static const struct clk_ops arasan_sampleclk_ops = { /** * sdhci_zynqmp_sdcardclk_set_phase - Set the SD Output Clock Tap Delays * + * @hw: Pointer to the hardware clock structure. + * @degrees: The clock phase shift between 0 - 359. + * * Set the SD Output Clock Tap Delays for Output path * - * @hw: Pointer to the hardware clock structure. - * @degrees The clock phase shift between 0 - 359. * Return: 0 on success and error value on error */ static int sdhci_zynqmp_sdcardclk_set_phase(struct clk_hw *hw, int degrees) - { struct sdhci_arasan_clk_data *clk_data = container_of(hw, struct sdhci_arasan_clk_data, sdcardclk_hw); @@ -688,14 +657,14 @@ static const struct clk_ops zynqmp_sdcardclk_ops = { /** * sdhci_zynqmp_sampleclk_set_phase - Set the SD Input Clock Tap Delays * + * @hw: Pointer to the hardware clock structure. + * @degrees: The clock phase shift between 0 - 359. + * * Set the SD Input Clock Tap Delays for Input path * - * @hw: Pointer to the hardware clock structure. - * @degrees The clock phase shift between 0 - 359. * Return: 0 on success and error value on error */ static int sdhci_zynqmp_sampleclk_set_phase(struct clk_hw *hw, int degrees) - { struct sdhci_arasan_clk_data *clk_data = container_of(hw, struct sdhci_arasan_clk_data, sampleclk_hw); @@ -757,6 +726,152 @@ static const struct clk_ops zynqmp_sampleclk_ops = { .set_phase = sdhci_zynqmp_sampleclk_set_phase, }; +/** + * sdhci_versal_sdcardclk_set_phase - Set the SD Output Clock Tap Delays + * + * @hw: Pointer to the hardware clock structure. + * @degrees: The clock phase shift between 0 - 359. + * + * Set the SD Output Clock Tap Delays for Output path + * + * Return: 0 on success and error value on error + */ +static int sdhci_versal_sdcardclk_set_phase(struct clk_hw *hw, int degrees) +{ + struct sdhci_arasan_clk_data *clk_data = + container_of(hw, struct sdhci_arasan_clk_data, sdcardclk_hw); + struct sdhci_arasan_data *sdhci_arasan = + container_of(clk_data, struct sdhci_arasan_data, clk_data); + struct sdhci_host *host = sdhci_arasan->host; + u8 tap_delay, tap_max = 0; + + /* + * This is applicable for SDHCI_SPEC_300 and above + * Versal does not set phase for <=25MHz clock. + * If degrees is zero, no need to do anything. + */ + if (host->version < SDHCI_SPEC_300 || + host->timing == MMC_TIMING_LEGACY || + host->timing == MMC_TIMING_UHS_SDR12 || !degrees) + return 0; + + switch (host->timing) { + case MMC_TIMING_MMC_HS: + case MMC_TIMING_SD_HS: + case MMC_TIMING_UHS_SDR25: + case MMC_TIMING_UHS_DDR50: + case MMC_TIMING_MMC_DDR52: + /* For 50MHz clock, 30 Taps are available */ + tap_max = 30; + break; + case MMC_TIMING_UHS_SDR50: + /* For 100MHz clock, 15 Taps are available */ + tap_max = 15; + break; + case MMC_TIMING_UHS_SDR104: + case MMC_TIMING_MMC_HS200: + /* For 200MHz clock, 8 Taps are available */ + tap_max = 8; + default: + break; + } + + tap_delay = (degrees * tap_max) / 360; + + /* Set the Clock Phase */ + if (tap_delay) { + u32 regval; + + regval = sdhci_readl(host, SDHCI_ARASAN_OTAPDLY_REGISTER); + regval |= SDHCI_OTAPDLY_ENABLE; + sdhci_writel(host, regval, SDHCI_ARASAN_OTAPDLY_REGISTER); + regval |= tap_delay; + sdhci_writel(host, regval, SDHCI_ARASAN_OTAPDLY_REGISTER); + } + + return 0; +} + +static const struct clk_ops versal_sdcardclk_ops = { + .recalc_rate = sdhci_arasan_sdcardclk_recalc_rate, + .set_phase = sdhci_versal_sdcardclk_set_phase, +}; + +/** + * sdhci_versal_sampleclk_set_phase - Set the SD Input Clock Tap Delays + * + * @hw: Pointer to the hardware clock structure. + * @degrees: The clock phase shift between 0 - 359. + * + * Set the SD Input Clock Tap Delays for Input path + * + * Return: 0 on success and error value on error + */ +static int sdhci_versal_sampleclk_set_phase(struct clk_hw *hw, int degrees) +{ + struct sdhci_arasan_clk_data *clk_data = + container_of(hw, struct sdhci_arasan_clk_data, sampleclk_hw); + struct sdhci_arasan_data *sdhci_arasan = + container_of(clk_data, struct sdhci_arasan_data, clk_data); + struct sdhci_host *host = sdhci_arasan->host; + u8 tap_delay, tap_max = 0; + + /* + * This is applicable for SDHCI_SPEC_300 and above + * Versal does not set phase for <=25MHz clock. + * If degrees is zero, no need to do anything. + */ + if (host->version < SDHCI_SPEC_300 || + host->timing == MMC_TIMING_LEGACY || + host->timing == MMC_TIMING_UHS_SDR12 || !degrees) + return 0; + + switch (host->timing) { + case MMC_TIMING_MMC_HS: + case MMC_TIMING_SD_HS: + case MMC_TIMING_UHS_SDR25: + case MMC_TIMING_UHS_DDR50: + case MMC_TIMING_MMC_DDR52: + /* For 50MHz clock, 120 Taps are available */ + tap_max = 120; + break; + case MMC_TIMING_UHS_SDR50: + /* For 100MHz clock, 60 Taps are available */ + tap_max = 60; + break; + case MMC_TIMING_UHS_SDR104: + case MMC_TIMING_MMC_HS200: + /* For 200MHz clock, 30 Taps are available */ + tap_max = 30; + default: + break; + } + + tap_delay = (degrees * tap_max) / 360; + + /* Set the Clock Phase */ + if (tap_delay) { + u32 regval; + + regval = sdhci_readl(host, SDHCI_ARASAN_ITAPDLY_REGISTER); + regval |= SDHCI_ITAPDLY_CHGWIN; + sdhci_writel(host, regval, SDHCI_ARASAN_ITAPDLY_REGISTER); + regval |= SDHCI_ITAPDLY_ENABLE; + sdhci_writel(host, regval, SDHCI_ARASAN_ITAPDLY_REGISTER); + regval |= tap_delay; + sdhci_writel(host, regval, SDHCI_ARASAN_ITAPDLY_REGISTER); + regval &= ~SDHCI_ITAPDLY_CHGWIN; + sdhci_writel(host, regval, SDHCI_ARASAN_ITAPDLY_REGISTER); + } + + return 0; +} + +static const struct clk_ops versal_sampleclk_ops = { + .recalc_rate = sdhci_arasan_sampleclk_recalc_rate, + .set_phase = sdhci_versal_sampleclk_set_phase, +}; + static void arasan_zynqmp_dll_reset(struct sdhci_host *host, u32 deviceid) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -804,6 +919,9 @@ static int arasan_zynqmp_execute_tuning(struct mmc_host *mmc, u32 opcode) /** * sdhci_arasan_update_clockmultiplier - Set corecfg_clockmultiplier * + * @host: The sdhci_host + * @value: The value to write + * * The corecfg_clockmultiplier is supposed to contain clock multiplier * value of programmable clock generator. * @@ -815,8 +933,6 @@ static int arasan_zynqmp_execute_tuning(struct mmc_host *mmc, u32 opcode) * - The value of corecfg_clockmultiplier should sync with that of corresponding * value reading from sdhci_capability_register. So this function is called * once at probe time and never called again. - * - * @host: The sdhci_host */ static void sdhci_arasan_update_clockmultiplier(struct sdhci_host *host, u32 value) @@ -843,6 +959,8 @@ static void sdhci_arasan_update_clockmultiplier(struct sdhci_host *host, /** * sdhci_arasan_update_baseclkfreq - Set corecfg_baseclkfreq * + * @host: The sdhci_host + * * The corecfg_baseclkfreq is supposed to contain the MHz of clk_xin. This * function can be used to make that happen. * @@ -854,8 +972,6 @@ static void sdhci_arasan_update_clockmultiplier(struct sdhci_host *host, * - It's assumed that clk_xin is not dynamic and that we use the SDHCI divider * to achieve lower clock rates. That means that this function is called once * at probe time and never called again. - * - * @host: The sdhci_host */ static void sdhci_arasan_update_baseclkfreq(struct sdhci_host *host) { @@ -919,10 +1035,10 @@ static void arasan_dt_read_clk_phase(struct device *dev, /** * arasan_dt_parse_clk_phases - Read Clock Delay values from DT * - * Called at initialization to parse the values of Clock Delays. - * * @dev: Pointer to our struct device. * @clk_data: Pointer to the Clock Data structure + * + * Called at initialization to parse the values of Clock Delays. */ static void arasan_dt_parse_clk_phases(struct device *dev, struct sdhci_arasan_clk_data *clk_data) @@ -954,6 +1070,16 @@ static void arasan_dt_parse_clk_phases(struct device *dev, } } + if (of_device_is_compatible(dev->of_node, "xlnx,versal-8.9a")) { + iclk_phase = (int [MMC_TIMING_MMC_HS400 + 1]) VERSAL_ICLK_PHASE; + oclk_phase = (int [MMC_TIMING_MMC_HS400 + 1]) VERSAL_OCLK_PHASE; + + for (i = 0; i <= MMC_TIMING_MMC_HS400; i++) { + clk_data->clk_phase_in[i] = iclk_phase[i]; + clk_data->clk_phase_out[i] = oclk_phase[i]; + } + } + arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_LEGACY, "clk-phase-legacy"); arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS, @@ -978,17 +1104,191 @@ static void arasan_dt_parse_clk_phases(struct device *dev, "clk-phase-mmc-hs400"); } +static const struct sdhci_pltfm_data sdhci_arasan_pdata = { + .ops = &sdhci_arasan_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | + SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN | + SDHCI_QUIRK2_STOP_WITH_TC, +}; + +static const struct sdhci_arasan_clk_ops arasan_clk_ops = { + .sdcardclk_ops = &arasan_sdcardclk_ops, + .sampleclk_ops = &arasan_sampleclk_ops, +}; + +static struct sdhci_arasan_of_data sdhci_arasan_generic_data = { + .pdata = &sdhci_arasan_pdata, + .clk_ops = &arasan_clk_ops, +}; + +static const struct sdhci_pltfm_data sdhci_keembay_emmc_pdata = { + .ops = &sdhci_arasan_cqe_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | + SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | + SDHCI_QUIRK_NO_LED | + SDHCI_QUIRK_32BIT_DMA_ADDR | + SDHCI_QUIRK_32BIT_DMA_SIZE | + SDHCI_QUIRK_32BIT_ADMA_SIZE, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | + SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN | + SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 | + SDHCI_QUIRK2_STOP_WITH_TC | + SDHCI_QUIRK2_BROKEN_64_BIT_DMA, +}; + +static const struct sdhci_pltfm_data sdhci_keembay_sd_pdata = { + .ops = &sdhci_arasan_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | + SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | + SDHCI_QUIRK_NO_LED | + SDHCI_QUIRK_32BIT_DMA_ADDR | + SDHCI_QUIRK_32BIT_DMA_SIZE | + SDHCI_QUIRK_32BIT_ADMA_SIZE, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | + SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN | + SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON | + SDHCI_QUIRK2_STOP_WITH_TC | + SDHCI_QUIRK2_BROKEN_64_BIT_DMA, +}; + +static const struct sdhci_pltfm_data sdhci_keembay_sdio_pdata = { + .ops = &sdhci_arasan_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | + SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | + SDHCI_QUIRK_NO_LED | + SDHCI_QUIRK_32BIT_DMA_ADDR | + SDHCI_QUIRK_32BIT_DMA_SIZE | + SDHCI_QUIRK_32BIT_ADMA_SIZE, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | + SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN | + SDHCI_QUIRK2_HOST_OFF_CARD_ON | + SDHCI_QUIRK2_BROKEN_64_BIT_DMA, +}; + +static struct sdhci_arasan_of_data sdhci_arasan_rk3399_data = { + .soc_ctl_map = &rk3399_soc_ctl_map, + .pdata = &sdhci_arasan_cqe_pdata, + .clk_ops = &arasan_clk_ops, +}; + +static struct sdhci_arasan_of_data intel_lgm_emmc_data = { + .soc_ctl_map = &intel_lgm_emmc_soc_ctl_map, + .pdata = &sdhci_arasan_cqe_pdata, + .clk_ops = &arasan_clk_ops, +}; + +static struct sdhci_arasan_of_data intel_lgm_sdxc_data = { + .soc_ctl_map = &intel_lgm_sdxc_soc_ctl_map, + .pdata = &sdhci_arasan_cqe_pdata, + .clk_ops = &arasan_clk_ops, +}; + +static const struct sdhci_pltfm_data sdhci_arasan_zynqmp_pdata = { + .ops = &sdhci_arasan_ops, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | + SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN | + SDHCI_QUIRK2_STOP_WITH_TC, +}; + +static const struct sdhci_arasan_clk_ops zynqmp_clk_ops = { + .sdcardclk_ops = &zynqmp_sdcardclk_ops, + .sampleclk_ops = &zynqmp_sampleclk_ops, +}; + +static struct sdhci_arasan_of_data sdhci_arasan_zynqmp_data = { + .pdata = &sdhci_arasan_zynqmp_pdata, + .clk_ops = &zynqmp_clk_ops, +}; + +static const struct sdhci_arasan_clk_ops versal_clk_ops = { + .sdcardclk_ops = &versal_sdcardclk_ops, + .sampleclk_ops = &versal_sampleclk_ops, +}; + +static struct sdhci_arasan_of_data sdhci_arasan_versal_data = { + .pdata = &sdhci_arasan_zynqmp_pdata, + .clk_ops = &versal_clk_ops, +}; + +static struct sdhci_arasan_of_data intel_keembay_emmc_data = { + .soc_ctl_map = &intel_keembay_soc_ctl_map, + .pdata = &sdhci_keembay_emmc_pdata, +}; + +static struct sdhci_arasan_of_data intel_keembay_sd_data = { + .soc_ctl_map = &intel_keembay_soc_ctl_map, + .pdata = &sdhci_keembay_sd_pdata, +}; + +static struct sdhci_arasan_of_data intel_keembay_sdio_data = { + .soc_ctl_map = &intel_keembay_soc_ctl_map, + .pdata = &sdhci_keembay_sdio_pdata, +}; + +static const struct of_device_id sdhci_arasan_of_match[] = { + /* SoC-specific compatible strings w/ soc_ctl_map */ + { + .compatible = "rockchip,rk3399-sdhci-5.1", + .data = &sdhci_arasan_rk3399_data, + }, + { + .compatible = "intel,lgm-sdhci-5.1-emmc", + .data = &intel_lgm_emmc_data, + }, + { + .compatible = "intel,lgm-sdhci-5.1-sdxc", + .data = &intel_lgm_sdxc_data, + }, + { + .compatible = "intel,keembay-sdhci-5.1-emmc", + .data = &intel_keembay_emmc_data, + }, + { + .compatible = "intel,keembay-sdhci-5.1-sd", + .data = &intel_keembay_sd_data, + }, + { + .compatible = "intel,keembay-sdhci-5.1-sdio", + .data = &intel_keembay_sdio_data, + }, + /* Generic compatible below here */ + { + .compatible = "arasan,sdhci-8.9a", + .data = &sdhci_arasan_generic_data, + }, + { + .compatible = "arasan,sdhci-5.1", + .data = &sdhci_arasan_generic_data, + }, + { + .compatible = "arasan,sdhci-4.9a", + .data = &sdhci_arasan_generic_data, + }, + { + .compatible = "xlnx,zynqmp-8.9a", + .data = &sdhci_arasan_zynqmp_data, + }, + { + .compatible = "xlnx,versal-8.9a", + .data = &sdhci_arasan_versal_data, + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match); + /** * sdhci_arasan_register_sdcardclk - Register the sdcardclk for a PHY to use * + * @sdhci_arasan: Our private data structure. + * @clk_xin: Pointer to the functional clock + * @dev: Pointer to our struct device. + * * Some PHY devices need to know what the actual card clock is. In order for * them to find out, we'll provide a clock through the common clock framework * for them to query. * - * @sdhci_arasan: Our private data structure. - * @clk_xin: Pointer to the functional clock - * @dev: Pointer to our struct device. - * Returns 0 on success and error value on error + * Return: 0 on success and error value on error */ static int sdhci_arasan_register_sdcardclk(struct sdhci_arasan_data *sdhci_arasan, @@ -1012,10 +1312,7 @@ sdhci_arasan_register_sdcardclk(struct sdhci_arasan_data *sdhci_arasan, sdcardclk_init.parent_names = &parent_clk_name; sdcardclk_init.num_parents = 1; sdcardclk_init.flags = CLK_GET_RATE_NOCACHE; - if (of_device_is_compatible(np, "xlnx,zynqmp-8.9a")) - sdcardclk_init.ops = &zynqmp_sdcardclk_ops; - else - sdcardclk_init.ops = &arasan_sdcardclk_ops; + sdcardclk_init.ops = sdhci_arasan->clk_ops->sdcardclk_ops; clk_data->sdcardclk_hw.init = &sdcardclk_init; clk_data->sdcardclk = @@ -1033,14 +1330,15 @@ sdhci_arasan_register_sdcardclk(struct sdhci_arasan_data *sdhci_arasan, /** * sdhci_arasan_register_sampleclk - Register the sampleclk for a PHY to use * + * @sdhci_arasan: Our private data structure. + * @clk_xin: Pointer to the functional clock + * @dev: Pointer to our struct device. + * * Some PHY devices need to know what the actual card clock is. In order for * them to find out, we'll provide a clock through the common clock framework * for them to query. * - * @sdhci_arasan: Our private data structure. - * @clk_xin: Pointer to the functional clock - * @dev: Pointer to our struct device. - * Returns 0 on success and error value on error + * Return: 0 on success and error value on error */ static int sdhci_arasan_register_sampleclk(struct sdhci_arasan_data *sdhci_arasan, @@ -1064,10 +1362,7 @@ sdhci_arasan_register_sampleclk(struct sdhci_arasan_data *sdhci_arasan, sampleclk_init.parent_names = &parent_clk_name; sampleclk_init.num_parents = 1; sampleclk_init.flags = CLK_GET_RATE_NOCACHE; - if (of_device_is_compatible(np, "xlnx,zynqmp-8.9a")) - sampleclk_init.ops = &zynqmp_sampleclk_ops; - else - sampleclk_init.ops = &arasan_sampleclk_ops; + sampleclk_init.ops = sdhci_arasan->clk_ops->sampleclk_ops; clk_data->sampleclk_hw.init = &sampleclk_init; clk_data->sampleclk = @@ -1085,10 +1380,10 @@ sdhci_arasan_register_sampleclk(struct sdhci_arasan_data *sdhci_arasan, /** * sdhci_arasan_unregister_sdclk - Undoes sdhci_arasan_register_sdclk() * + * @dev: Pointer to our struct device. + * * Should be called any time we're exiting and sdhci_arasan_register_sdclk() * returned success. - * - * @dev: Pointer to our struct device. */ static void sdhci_arasan_unregister_sdclk(struct device *dev) { @@ -1101,8 +1396,46 @@ static void sdhci_arasan_unregister_sdclk(struct device *dev) } /** + * sdhci_arasan_update_support64b - Set SUPPORT_64B (64-bit System Bus Support) + * + * This should be set based on the System Address Bus. + * 0: the Core supports only 32-bit System Address Bus. + * 1: the Core supports 64-bit System Address Bus. + * + * NOTES: + * - For Keem Bay, it is required to clear this bit. Its default value is 1'b1. + * Keem Bay does not support 64-bit access. + * + * @host The sdhci_host + */ +static void sdhci_arasan_update_support64b(struct sdhci_host *host, u32 value) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); + const struct sdhci_arasan_soc_ctl_map *soc_ctl_map = + sdhci_arasan->soc_ctl_map; + + /* Having a map is optional */ + if (!soc_ctl_map) + return; + + /* If we have a map, we expect to have a syscon */ + if (!sdhci_arasan->soc_ctl_base) { + pr_warn("%s: Have regmap, but no soc-ctl-syscon\n", + mmc_hostname(host->mmc)); + return; + } + + sdhci_arasan_syscon_write(host, &soc_ctl_map->support64b, value); +} + +/** * sdhci_arasan_register_sdclk - Register the sdcardclk for a PHY to use * + * @sdhci_arasan: Our private data structure. + * @clk_xin: Pointer to the functional clock + * @dev: Pointer to our struct device. + * * Some PHY devices need to know what the actual card clock is. In order for * them to find out, we'll provide a clock through the common clock framework * for them to query. @@ -1115,10 +1448,7 @@ static void sdhci_arasan_unregister_sdclk(struct device *dev) * to create nice clean device tree bindings and later (if needed) we can try * re-architecting SDHCI if we see some benefit to it. * - * @sdhci_arasan: Our private data structure. - * @clk_xin: Pointer to the functional clock - * @dev: Pointer to our struct device. - * Returns 0 on success and error value on error + * Return: 0 on success and error value on error */ static int sdhci_arasan_register_sdclk(struct sdhci_arasan_data *sdhci_arasan, struct clk *clk_xin, @@ -1215,6 +1545,7 @@ static int sdhci_arasan_probe(struct platform_device *pdev) sdhci_arasan->host = host; sdhci_arasan->soc_ctl_map = data->soc_ctl_map; + sdhci_arasan->clk_ops = data->clk_ops; node = of_parse_phandle(pdev->dev.of_node, "arasan,soc-ctl-syscon", 0); if (node) { @@ -1270,6 +1601,15 @@ static int sdhci_arasan_probe(struct platform_device *pdev) "rockchip,rk3399-sdhci-5.1")) sdhci_arasan_update_clockmultiplier(host, 0x0); + if (of_device_is_compatible(np, "intel,keembay-sdhci-5.1-emmc") || + of_device_is_compatible(np, "intel,keembay-sdhci-5.1-sd") || + of_device_is_compatible(np, "intel,keembay-sdhci-5.1-sdio")) { + sdhci_arasan_update_clockmultiplier(host, 0x0); + sdhci_arasan_update_support64b(host, 0x0); + + host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY; + } + sdhci_arasan_update_baseclkfreq(host); ret = sdhci_arasan_register_sdclk(sdhci_arasan, clk_xin, &pdev->dev); diff --git a/drivers/mmc/host/sdhci-of-at91.c b/drivers/mmc/host/sdhci-of-at91.c index c79bff5e2280..1ece2c50042c 100644 --- a/drivers/mmc/host/sdhci-of-at91.c +++ b/drivers/mmc/host/sdhci-of-at91.c @@ -6,6 +6,7 @@ * 2015 Ludovic Desroches <ludovic.desroches@atmel.com> */ +#include <linux/bitfield.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/err.h> @@ -120,9 +121,12 @@ static void sdhci_at91_reset(struct sdhci_host *host, u8 mask) || mmc_gpio_get_cd(host->mmc) >= 0) sdhci_at91_set_force_card_detect(host); - if (priv->cal_always_on && (mask & SDHCI_RESET_ALL)) - sdhci_writel(host, SDMMC_CALCR_ALWYSON | SDMMC_CALCR_EN, + if (priv->cal_always_on && (mask & SDHCI_RESET_ALL)) { + u32 calcr = sdhci_readl(host, SDMMC_CALCR); + + sdhci_writel(host, calcr | SDMMC_CALCR_ALWYSON | SDMMC_CALCR_EN, SDMMC_CALCR); + } } static const struct sdhci_ops sdhci_at91_sama5d2_ops = { @@ -179,9 +183,9 @@ static int sdhci_at91_set_clks_presets(struct device *dev) clk_mul = gck_rate / clk_base_rate - 1; caps0 &= ~SDHCI_CLOCK_V3_BASE_MASK; - caps0 |= (clk_base << SDHCI_CLOCK_BASE_SHIFT) & SDHCI_CLOCK_V3_BASE_MASK; + caps0 |= FIELD_PREP(SDHCI_CLOCK_V3_BASE_MASK, clk_base); caps1 &= ~SDHCI_CLOCK_MUL_MASK; - caps1 |= (clk_mul << SDHCI_CLOCK_MUL_SHIFT) & SDHCI_CLOCK_MUL_MASK; + caps1 |= FIELD_PREP(SDHCI_CLOCK_MUL_MASK, clk_mul); /* Set capabilities in r/w mode. */ writel(SDMMC_CACR_KEY | SDMMC_CACR_CAPWREN, host->ioaddr + SDMMC_CACR); writel(caps0, host->ioaddr + SDHCI_CAPABILITIES); diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c index a5137845a1c7..64ac0dbee95c 100644 --- a/drivers/mmc/host/sdhci-of-dwcmshc.c +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -16,6 +16,9 @@ #include "sdhci-pltfm.h" +/* DWCMSHC specific Mode Select value */ +#define DWCMSHC_CTRL_HS400 0x7 + #define BOUNDARY_OK(addr, len) \ ((addr | (SZ_128M - 1)) == ((addr + len - 1) | (SZ_128M - 1))) @@ -46,10 +49,36 @@ static void dwcmshc_adma_write_desc(struct sdhci_host *host, void **desc, sdhci_adma_write_desc(host, desc, addr, len, cmd); } +static void dwcmshc_set_uhs_signaling(struct sdhci_host *host, + unsigned int timing) +{ + u16 ctrl_2; + + ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); + /* Select Bus Speed Mode for host */ + ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; + if ((timing == MMC_TIMING_MMC_HS200) || + (timing == MMC_TIMING_UHS_SDR104)) + ctrl_2 |= SDHCI_CTRL_UHS_SDR104; + else if (timing == MMC_TIMING_UHS_SDR12) + ctrl_2 |= SDHCI_CTRL_UHS_SDR12; + else if ((timing == MMC_TIMING_UHS_SDR25) || + (timing == MMC_TIMING_MMC_HS)) + ctrl_2 |= SDHCI_CTRL_UHS_SDR25; + else if (timing == MMC_TIMING_UHS_SDR50) + ctrl_2 |= SDHCI_CTRL_UHS_SDR50; + else if ((timing == MMC_TIMING_UHS_DDR50) || + (timing == MMC_TIMING_MMC_DDR52)) + ctrl_2 |= SDHCI_CTRL_UHS_DDR50; + else if (timing == MMC_TIMING_MMC_HS400) + ctrl_2 |= DWCMSHC_CTRL_HS400; + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); +} + static const struct sdhci_ops sdhci_dwcmshc_ops = { .set_clock = sdhci_set_clock, .set_bus_width = sdhci_set_bus_width, - .set_uhs_signaling = sdhci_set_uhs_signaling, + .set_uhs_signaling = dwcmshc_set_uhs_signaling, .get_max_clock = sdhci_pltfm_clk_get_max_clock, .reset = sdhci_reset, .adma_write_desc = dwcmshc_adma_write_desc, @@ -134,6 +163,48 @@ static int dwcmshc_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int dwcmshc_suspend(struct device *dev) +{ + struct sdhci_host *host = dev_get_drvdata(dev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); + int ret; + + ret = sdhci_suspend_host(host); + if (ret) + return ret; + + clk_disable_unprepare(pltfm_host->clk); + if (!IS_ERR(priv->bus_clk)) + clk_disable_unprepare(priv->bus_clk); + + return ret; +} + +static int dwcmshc_resume(struct device *dev) +{ + struct sdhci_host *host = dev_get_drvdata(dev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); + int ret; + + ret = clk_prepare_enable(pltfm_host->clk); + if (ret) + return ret; + + if (!IS_ERR(priv->bus_clk)) { + ret = clk_prepare_enable(priv->bus_clk); + if (ret) + return ret; + } + + return sdhci_resume_host(host); +} +#endif + +static SIMPLE_DEV_PM_OPS(dwcmshc_pmops, dwcmshc_suspend, dwcmshc_resume); + static const struct of_device_id sdhci_dwcmshc_dt_ids[] = { { .compatible = "snps,dwcmshc-sdhci" }, {} @@ -144,6 +215,7 @@ static struct platform_driver sdhci_dwcmshc_driver = { .driver = { .name = "sdhci-dwcmshc", .of_match_table = sdhci_dwcmshc_dt_ids, + .pm = &dwcmshc_pmops, }, .probe = dwcmshc_probe, .remove = dwcmshc_remove, diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 5d8dd870bd44..7c73d243dc6c 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -1135,6 +1135,40 @@ static int esdhc_execute_tuning(struct mmc_host *mmc, u32 opcode) static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned int timing) { + u32 val; + + /* + * There are specific registers setting for HS400 mode. + * Clean all of them if controller is in HS400 mode to + * exit HS400 mode before re-setting any speed mode. + */ + val = sdhci_readl(host, ESDHC_TBCTL); + if (val & ESDHC_HS400_MODE) { + val = sdhci_readl(host, ESDHC_SDTIMNGCTL); + val &= ~ESDHC_FLW_CTL_BG; + sdhci_writel(host, val, ESDHC_SDTIMNGCTL); + + val = sdhci_readl(host, ESDHC_SDCLKCTL); + val &= ~ESDHC_CMD_CLK_CTL; + sdhci_writel(host, val, ESDHC_SDCLKCTL); + + esdhc_clock_enable(host, false); + val = sdhci_readl(host, ESDHC_TBCTL); + val &= ~ESDHC_HS400_MODE; + sdhci_writel(host, val, ESDHC_TBCTL); + esdhc_clock_enable(host, true); + + val = sdhci_readl(host, ESDHC_DLLCFG0); + val &= ~(ESDHC_DLL_ENABLE | ESDHC_DLL_FREQ_SEL); + sdhci_writel(host, val, ESDHC_DLLCFG0); + + val = sdhci_readl(host, ESDHC_TBCTL); + val &= ~ESDHC_HS400_WNDW_ADJUST; + sdhci_writel(host, val, ESDHC_TBCTL); + + esdhc_tuning_block_enable(host, false); + } + if (timing == MMC_TIMING_MMC_HS400) esdhc_tuning_block_enable(host, true); else diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index 2527244c2ae1..bb6802448b2f 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -249,12 +249,8 @@ static int ricoh_probe(struct sdhci_pci_chip *chip) static int ricoh_mmc_probe_slot(struct sdhci_pci_slot *slot) { slot->host->caps = - ((0x21 << SDHCI_TIMEOUT_CLK_SHIFT) - & SDHCI_TIMEOUT_CLK_MASK) | - - ((0x21 << SDHCI_CLOCK_BASE_SHIFT) - & SDHCI_CLOCK_BASE_MASK) | - + FIELD_PREP(SDHCI_TIMEOUT_CLK_MASK, 0x21) | + FIELD_PREP(SDHCI_CLOCK_BASE_MASK, 0x21) | SDHCI_TIMEOUT_CLK_UNIT | SDHCI_CAN_VDD_330 | SDHCI_CAN_DO_HISPD | @@ -1749,6 +1745,7 @@ static const struct pci_device_id pci_ids[] = { SDHCI_PCI_DEVICE(SYNOPSYS, DWC_MSHC, snps), SDHCI_PCI_DEVICE(GLI, 9750, gl9750), SDHCI_PCI_DEVICE(GLI, 9755, gl9755), + SDHCI_PCI_DEVICE(GLI, 9763E, gl9763e), SDHCI_PCI_DEVICE_CLASS(AMD, SYSTEM_SDHCI, PCI_CLASS_MASK, amd), /* Generic SD host controller */ {PCI_DEVICE_CLASS(SYSTEM_SDHCI, PCI_CLASS_MASK)}, diff --git a/drivers/mmc/host/sdhci-pci-gli.c b/drivers/mmc/host/sdhci-pci-gli.c index fd76aa672e02..ca0166d9bf82 100644 --- a/drivers/mmc/host/sdhci-pci-gli.c +++ b/drivers/mmc/host/sdhci-pci-gli.c @@ -63,6 +63,19 @@ #define SDHCI_GLI_9750_TUNING_PARAMETERS_RX_DLY GENMASK(2, 0) #define GLI_9750_TUNING_PARAMETERS_RX_DLY_VALUE 0x1 +#define SDHCI_GLI_9763E_CTRL_HS400 0x7 + +#define SDHCI_GLI_9763E_HS400_ES_REG 0x52C +#define SDHCI_GLI_9763E_HS400_ES_BIT BIT(8) + +#define PCIE_GLI_9763E_VHS 0x884 +#define GLI_9763E_VHS_REV GENMASK(19, 16) +#define GLI_9763E_VHS_REV_R 0x0 +#define GLI_9763E_VHS_REV_M 0x1 +#define GLI_9763E_VHS_REV_W 0x2 +#define PCIE_GLI_9763E_SCR 0x8E0 +#define GLI_9763E_SCR_AXI_REQ BIT(9) + #define GLI_MAX_TUNING_LOOP 40 /* Genesys Logic chipset */ @@ -351,6 +364,81 @@ static int sdhci_pci_gli_resume(struct sdhci_pci_chip *chip) } #endif +static void gl9763e_hs400_enhanced_strobe(struct mmc_host *mmc, + struct mmc_ios *ios) +{ + struct sdhci_host *host = mmc_priv(mmc); + u32 val; + + val = sdhci_readl(host, SDHCI_GLI_9763E_HS400_ES_REG); + if (ios->enhanced_strobe) + val |= SDHCI_GLI_9763E_HS400_ES_BIT; + else + val &= ~SDHCI_GLI_9763E_HS400_ES_BIT; + + sdhci_writel(host, val, SDHCI_GLI_9763E_HS400_ES_REG); +} + +static void sdhci_set_gl9763e_signaling(struct sdhci_host *host, + unsigned int timing) +{ + u16 ctrl_2; + + ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); + ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; + if (timing == MMC_TIMING_MMC_HS200) + ctrl_2 |= SDHCI_CTRL_UHS_SDR104; + else if (timing == MMC_TIMING_MMC_HS) + ctrl_2 |= SDHCI_CTRL_UHS_SDR25; + else if (timing == MMC_TIMING_MMC_DDR52) + ctrl_2 |= SDHCI_CTRL_UHS_DDR50; + else if (timing == MMC_TIMING_MMC_HS400) + ctrl_2 |= SDHCI_GLI_9763E_CTRL_HS400; + + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); +} + +static void gli_set_gl9763e(struct sdhci_pci_slot *slot) +{ + struct pci_dev *pdev = slot->chip->pdev; + u32 value; + + pci_read_config_dword(pdev, PCIE_GLI_9763E_VHS, &value); + value &= ~GLI_9763E_VHS_REV; + value |= FIELD_PREP(GLI_9763E_VHS_REV, GLI_9763E_VHS_REV_W); + pci_write_config_dword(pdev, PCIE_GLI_9763E_VHS, value); + + pci_read_config_dword(pdev, PCIE_GLI_9763E_SCR, &value); + value |= GLI_9763E_SCR_AXI_REQ; + pci_write_config_dword(pdev, PCIE_GLI_9763E_SCR, value); + + pci_read_config_dword(pdev, PCIE_GLI_9763E_VHS, &value); + value &= ~GLI_9763E_VHS_REV; + value |= FIELD_PREP(GLI_9763E_VHS_REV, GLI_9763E_VHS_REV_R); + pci_write_config_dword(pdev, PCIE_GLI_9763E_VHS, value); +} + +static int gli_probe_slot_gl9763e(struct sdhci_pci_slot *slot) +{ + struct sdhci_host *host = slot->host; + + host->mmc->caps |= MMC_CAP_8_BIT_DATA | + MMC_CAP_1_8V_DDR | + MMC_CAP_NONREMOVABLE; + host->mmc->caps2 |= MMC_CAP2_HS200_1_8V_SDR | + MMC_CAP2_HS400_1_8V | + MMC_CAP2_HS400_ES | + MMC_CAP2_NO_SDIO | + MMC_CAP2_NO_SD; + gli_pcie_enable_msi(slot); + host->mmc_host_ops.hs400_enhanced_strobe = + gl9763e_hs400_enhanced_strobe; + gli_set_gl9763e(slot); + sdhci_enable_v4_mode(host); + + return 0; +} + static const struct sdhci_ops sdhci_gl9755_ops = { .set_clock = sdhci_set_clock, .enable_dma = sdhci_pci_enable_dma, @@ -390,3 +478,21 @@ const struct sdhci_pci_fixes sdhci_gl9750 = { .resume = sdhci_pci_gli_resume, #endif }; + +static const struct sdhci_ops sdhci_gl9763e_ops = { + .set_clock = sdhci_set_clock, + .enable_dma = sdhci_pci_enable_dma, + .set_bus_width = sdhci_set_bus_width, + .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_gl9763e_signaling, + .voltage_switch = sdhci_gli_voltage_switch, +}; + +const struct sdhci_pci_fixes sdhci_gl9763e = { + .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, + .probe_slot = gli_probe_slot_gl9763e, + .ops = &sdhci_gl9763e_ops, +#ifdef CONFIG_PM_SLEEP + .resume = sdhci_pci_gli_resume, +#endif +}; diff --git a/drivers/mmc/host/sdhci-pci-o2micro.c b/drivers/mmc/host/sdhci-pci-o2micro.c index fa8105087d68..e2a846885902 100644 --- a/drivers/mmc/host/sdhci-pci-o2micro.c +++ b/drivers/mmc/host/sdhci-pci-o2micro.c @@ -494,7 +494,7 @@ static void sdhci_o2_enable_clk(struct sdhci_host *host, u16 clk) } } -void sdhci_pci_o2_set_clock(struct sdhci_host *host, unsigned int clock) +static void sdhci_pci_o2_set_clock(struct sdhci_host *host, unsigned int clock) { u16 clk; @@ -509,7 +509,7 @@ void sdhci_pci_o2_set_clock(struct sdhci_host *host, unsigned int clock) sdhci_o2_enable_clk(host, clk); } -int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot) +static int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot) { struct sdhci_pci_chip *chip; struct sdhci_host *host; @@ -578,7 +578,7 @@ int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot) return 0; } -int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip) +static int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip) { int ret; u8 scratch; @@ -783,7 +783,7 @@ int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip) } #ifdef CONFIG_PM_SLEEP -int sdhci_pci_o2_resume(struct sdhci_pci_chip *chip) +static int sdhci_pci_o2_resume(struct sdhci_pci_chip *chip) { sdhci_pci_o2_probe(chip); return sdhci_pci_resume_host(chip); diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h index 42ccd123b046..d0ed232af0eb 100644 --- a/drivers/mmc/host/sdhci-pci.h +++ b/drivers/mmc/host/sdhci-pci.h @@ -72,6 +72,7 @@ #define PCI_DEVICE_ID_GLI_9755 0x9755 #define PCI_DEVICE_ID_GLI_9750 0x9750 +#define PCI_DEVICE_ID_GLI_9763E 0xe763 /* * PCI device class and mask @@ -195,5 +196,6 @@ extern const struct sdhci_pci_fixes sdhci_snps; extern const struct sdhci_pci_fixes sdhci_o2; extern const struct sdhci_pci_fixes sdhci_gl9750; extern const struct sdhci_pci_fixes sdhci_gl9755; +extern const struct sdhci_pci_fixes sdhci_gl9763e; #endif /* __SDHCI_PCI_H */ diff --git a/drivers/mmc/host/sdhci-sprd.c b/drivers/mmc/host/sdhci-sprd.c index 2ab42c59e4f8..a910cb461ed7 100644 --- a/drivers/mmc/host/sdhci-sprd.c +++ b/drivers/mmc/host/sdhci-sprd.c @@ -406,7 +406,8 @@ static struct sdhci_ops sdhci_sprd_ops = { .request_done = sdhci_sprd_request_done, }; -static void sdhci_sprd_request(struct mmc_host *mmc, struct mmc_request *mrq) +static void sdhci_sprd_check_auto_cmd23(struct mmc_host *mmc, + struct mmc_request *mrq) { struct sdhci_host *host = mmc_priv(mmc); struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host); @@ -422,10 +423,23 @@ static void sdhci_sprd_request(struct mmc_host *mmc, struct mmc_request *mrq) mrq->sbc && (mrq->sbc->arg & SDHCI_SPRD_ARG2_STUFF) && (host->flags & SDHCI_AUTO_CMD23)) host->flags &= ~SDHCI_AUTO_CMD23; +} + +static void sdhci_sprd_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + sdhci_sprd_check_auto_cmd23(mmc, mrq); sdhci_request(mmc, mrq); } +static int sdhci_sprd_request_atomic(struct mmc_host *mmc, + struct mmc_request *mrq) +{ + sdhci_sprd_check_auto_cmd23(mmc, mrq); + + return sdhci_request_atomic(mmc, mrq); +} + static int sdhci_sprd_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios) { struct sdhci_host *host = mmc_priv(mmc); @@ -434,7 +448,7 @@ static int sdhci_sprd_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios) if (!IS_ERR(mmc->supply.vqmmc)) { ret = mmc_regulator_set_vqmmc(mmc, ios); - if (ret) { + if (ret < 0) { pr_err("%s: Switching signalling voltage failed\n", mmc_hostname(mmc)); return ret; @@ -556,11 +570,17 @@ static int sdhci_sprd_probe(struct platform_device *pdev) sdhci_sprd_voltage_switch; host->mmc->caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED | - MMC_CAP_ERASE | MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY; + MMC_CAP_WAIT_WHILE_BUSY; + ret = mmc_of_parse(host->mmc); if (ret) goto pltfm_free; + if (!mmc_card_is_removable(host->mmc)) + host->mmc_host_ops.request_atomic = sdhci_sprd_request_atomic; + else + host->always_defer_done = true; + sprd_host = TO_SPRD_HOST(host); sdhci_sprd_phy_param_parse(sprd_host, pdev->dev.of_node); @@ -654,8 +674,6 @@ static int sdhci_sprd_probe(struct platform_device *pdev) if (ret) goto err_cleanup_host; - host->always_defer_done = true; - ret = __sdhci_add_host(host); if (ret) goto err_cleanup_host; diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index 3e2c5101291d..3a372ab3d12e 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -605,6 +605,39 @@ static void tegra_sdhci_parse_pad_autocal_dt(struct sdhci_host *host) autocal->pull_down_1v8 = 0; err = device_property_read_u32(host->mmc->parent, + "nvidia,pad-autocal-pull-up-offset-sdr104", + &autocal->pull_up_sdr104); + if (err) + autocal->pull_up_sdr104 = autocal->pull_up_1v8; + + err = device_property_read_u32(host->mmc->parent, + "nvidia,pad-autocal-pull-down-offset-sdr104", + &autocal->pull_down_sdr104); + if (err) + autocal->pull_down_sdr104 = autocal->pull_down_1v8; + + err = device_property_read_u32(host->mmc->parent, + "nvidia,pad-autocal-pull-up-offset-hs400", + &autocal->pull_up_hs400); + if (err) + autocal->pull_up_hs400 = autocal->pull_up_1v8; + + err = device_property_read_u32(host->mmc->parent, + "nvidia,pad-autocal-pull-down-offset-hs400", + &autocal->pull_down_hs400); + if (err) + autocal->pull_down_hs400 = autocal->pull_down_1v8; + + /* + * Different fail-safe drive strength values based on the signaling + * voltage are applicable for SoCs supporting 3V3 and 1V8 pad controls. + * So, avoid reading below device tree properties for SoCs that don't + * have NVQUIRK_NEEDS_PAD_CONTROL. + */ + if (!(tegra_host->soc_data->nvquirks & NVQUIRK_NEEDS_PAD_CONTROL)) + return; + + err = device_property_read_u32(host->mmc->parent, "nvidia,pad-autocal-pull-up-offset-3v3-timeout", &autocal->pull_up_3v3_timeout); if (err) { @@ -647,30 +680,6 @@ static void tegra_sdhci_parse_pad_autocal_dt(struct sdhci_host *host) mmc_hostname(host->mmc)); autocal->pull_down_1v8_timeout = 0; } - - err = device_property_read_u32(host->mmc->parent, - "nvidia,pad-autocal-pull-up-offset-sdr104", - &autocal->pull_up_sdr104); - if (err) - autocal->pull_up_sdr104 = autocal->pull_up_1v8; - - err = device_property_read_u32(host->mmc->parent, - "nvidia,pad-autocal-pull-down-offset-sdr104", - &autocal->pull_down_sdr104); - if (err) - autocal->pull_down_sdr104 = autocal->pull_down_1v8; - - err = device_property_read_u32(host->mmc->parent, - "nvidia,pad-autocal-pull-up-offset-hs400", - &autocal->pull_up_hs400); - if (err) - autocal->pull_up_hs400 = autocal->pull_up_1v8; - - err = device_property_read_u32(host->mmc->parent, - "nvidia,pad-autocal-pull-down-offset-hs400", - &autocal->pull_down_hs400); - if (err) - autocal->pull_down_hs400 = autocal->pull_down_1v8; } static void tegra_sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 3f716466fcfd..37b1158c1c0c 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -48,10 +48,10 @@ static unsigned int debug_quirks = 0; static unsigned int debug_quirks2; -static void sdhci_finish_data(struct sdhci_host *); - static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable); +static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd); + void sdhci_dumpregs(struct sdhci_host *host) { SDHCI_DUMP("============ SDHCI REGISTER DUMP ===========\n"); @@ -111,6 +111,9 @@ void sdhci_dumpregs(struct sdhci_host *host) } } + if (host->ops->dump_vendor_regs) + host->ops->dump_vendor_regs(host); + SDHCI_DUMP("============================================\n"); } EXPORT_SYMBOL_GPL(sdhci_dumpregs); @@ -317,6 +320,7 @@ out: static void sdhci_init(struct sdhci_host *host, int soft) { struct mmc_host *mmc = host->mmc; + unsigned long flags; if (soft) sdhci_do_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); @@ -326,7 +330,9 @@ static void sdhci_init(struct sdhci_host *host, int soft) if (host->v4_mode) sdhci_do_enable_v4_mode(host); + spin_lock_irqsave(&host->lock, flags); sdhci_set_default_irqs(host); + spin_unlock_irqrestore(&host->lock, flags); host->cqe_on = false; @@ -634,9 +640,13 @@ static int sdhci_pre_dma_transfer(struct sdhci_host *host, } if (mmc_get_dma_dir(data) == DMA_TO_DEVICE) { /* Copy the data to the bounce buffer */ - sg_copy_to_buffer(data->sg, data->sg_len, - host->bounce_buffer, - length); + if (host->ops->copy_to_bounce_buffer) { + host->ops->copy_to_bounce_buffer(host, + data, length); + } else { + sg_copy_to_buffer(data->sg, data->sg_len, + host->bounce_buffer, length); + } } /* Switch ownership to the DMA */ dma_sync_single_for_device(host->mmc->parent, @@ -1350,13 +1360,25 @@ static inline bool sdhci_auto_cmd12(struct sdhci_host *host, !mrq->cap_cmd_during_tfr; } +static inline bool sdhci_auto_cmd23(struct sdhci_host *host, + struct mmc_request *mrq) +{ + return mrq->sbc && (host->flags & SDHCI_AUTO_CMD23); +} + +static inline bool sdhci_manual_cmd23(struct sdhci_host *host, + struct mmc_request *mrq) +{ + return mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23); +} + static inline void sdhci_auto_cmd_select(struct sdhci_host *host, struct mmc_command *cmd, u16 *mode) { bool use_cmd12 = sdhci_auto_cmd12(host, cmd->mrq) && (cmd->opcode != SD_IO_RW_EXTENDED); - bool use_cmd23 = cmd->mrq->sbc && (host->flags & SDHCI_AUTO_CMD23); + bool use_cmd23 = sdhci_auto_cmd23(host, cmd->mrq); u16 ctrl2; /* @@ -1416,7 +1438,7 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host, if (mmc_op_multi(cmd->opcode) || data->blocks > 1) { mode = SDHCI_TRNS_BLK_CNT_EN | SDHCI_TRNS_MULTI; sdhci_auto_cmd_select(host, cmd, &mode); - if (cmd->mrq->sbc && (host->flags & SDHCI_AUTO_CMD23)) + if (sdhci_auto_cmd23(host, cmd->mrq)) sdhci_writel(host, cmd->mrq->sbc->arg, SDHCI_ARGUMENT2); } @@ -1466,6 +1488,9 @@ static void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq) if (host->data_cmd && host->data_cmd->mrq == mrq) host->data_cmd = NULL; + if (host->deferred_cmd && host->deferred_cmd->mrq == mrq) + host->deferred_cmd = NULL; + if (host->data && host->data->mrq == mrq) host->data = NULL; @@ -1487,7 +1512,7 @@ static void sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq) queue_work(host->complete_wq, &host->complete_work); } -static void sdhci_finish_data(struct sdhci_host *host) +static void __sdhci_finish_data(struct sdhci_host *host, bool sw_data_timeout) { struct mmc_command *data_cmd = host->data_cmd; struct mmc_data *data = host->data; @@ -1539,14 +1564,31 @@ static void sdhci_finish_data(struct sdhci_host *host) } else { /* Avoid triggering warning in sdhci_send_command() */ host->cmd = NULL; - sdhci_send_command(host, data->stop); + if (!sdhci_send_command(host, data->stop)) { + if (sw_data_timeout) { + /* + * This is anyway a sw data timeout, so + * give up now. + */ + data->stop->error = -EIO; + __sdhci_finish_mrq(host, data->mrq); + } else { + WARN_ON(host->deferred_cmd); + host->deferred_cmd = data->stop; + } + } } } else { __sdhci_finish_mrq(host, data->mrq); } } -void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) +static void sdhci_finish_data(struct sdhci_host *host) +{ + __sdhci_finish_data(host, false); +} + +static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) { int flags; u32 mask; @@ -1561,9 +1603,6 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) cmd->opcode == MMC_STOP_TRANSMISSION) cmd->flags |= MMC_RSP_BUSY; - /* Wait max 10 ms */ - timeout = 10; - mask = SDHCI_CMD_INHIBIT; if (sdhci_data_line_cmd(cmd)) mask |= SDHCI_DATA_INHIBIT; @@ -1573,18 +1612,8 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) if (cmd->mrq->data && (cmd == cmd->mrq->data->stop)) mask &= ~SDHCI_DATA_INHIBIT; - while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) { - if (timeout == 0) { - pr_err("%s: Controller never released inhibit bit(s).\n", - mmc_hostname(host->mmc)); - sdhci_dumpregs(host); - cmd->error = -EIO; - sdhci_finish_mrq(host, cmd->mrq); - return; - } - timeout--; - mdelay(1); - } + if (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) + return false; host->cmd = cmd; host->data_timeout = 0; @@ -1606,11 +1635,13 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) sdhci_set_transfer_mode(host, cmd); if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) { - pr_err("%s: Unsupported response type!\n", - mmc_hostname(host->mmc)); - cmd->error = -EINVAL; - sdhci_finish_mrq(host, cmd->mrq); - return; + WARN_ONCE(1, "Unsupported response type!\n"); + /* + * This does not happen in practice because 136-bit response + * commands never have busy waiting, so rather than complicate + * the error path, just remove busy waiting and continue. + */ + cmd->flags &= ~MMC_RSP_BUSY; } if (!(cmd->flags & MMC_RSP_PRESENT)) @@ -1645,8 +1676,61 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) sdhci_external_dma_pre_transfer(host, cmd); sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND); + + return true; +} + +static bool sdhci_present_error(struct sdhci_host *host, + struct mmc_command *cmd, bool present) +{ + if (!present || host->flags & SDHCI_DEVICE_DEAD) { + cmd->error = -ENOMEDIUM; + return true; + } + + return false; +} + +static bool sdhci_send_command_retry(struct sdhci_host *host, + struct mmc_command *cmd, + unsigned long flags) + __releases(host->lock) + __acquires(host->lock) +{ + struct mmc_command *deferred_cmd = host->deferred_cmd; + int timeout = 10; /* Approx. 10 ms */ + bool present; + + while (!sdhci_send_command(host, cmd)) { + if (!timeout--) { + pr_err("%s: Controller never released inhibit bit(s).\n", + mmc_hostname(host->mmc)); + sdhci_dumpregs(host); + cmd->error = -EIO; + return false; + } + + spin_unlock_irqrestore(&host->lock, flags); + + usleep_range(1000, 1250); + + present = host->mmc->ops->get_cd(host->mmc); + + spin_lock_irqsave(&host->lock, flags); + + /* A deferred command might disappear, handle that */ + if (cmd == deferred_cmd && cmd != host->deferred_cmd) + return true; + + if (sdhci_present_error(host, cmd, present)) + return false; + } + + if (cmd == host->deferred_cmd) + host->deferred_cmd = NULL; + + return true; } -EXPORT_SYMBOL_GPL(sdhci_send_command); static void sdhci_read_rsp_136(struct sdhci_host *host, struct mmc_command *cmd) { @@ -1707,7 +1791,10 @@ static void sdhci_finish_command(struct sdhci_host *host) /* Finished CMD23, now send actual command. */ if (cmd == cmd->mrq->sbc) { - sdhci_send_command(host, cmd->mrq->cmd); + if (!sdhci_send_command(host, cmd->mrq->cmd)) { + WARN_ON(host->deferred_cmd); + host->deferred_cmd = cmd->mrq->cmd; + } } else { /* Processed actual command. */ @@ -2037,11 +2124,10 @@ EXPORT_SYMBOL_GPL(sdhci_set_power_and_bus_voltage); void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) { - struct sdhci_host *host; - int present; + struct sdhci_host *host = mmc_priv(mmc); + struct mmc_command *cmd; unsigned long flags; - - host = mmc_priv(mmc); + bool present; /* Firstly check card presence */ present = mmc->ops->get_cd(mmc); @@ -2050,19 +2136,57 @@ void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) sdhci_led_activate(host); - if (!present || host->flags & SDHCI_DEVICE_DEAD) { - mrq->cmd->error = -ENOMEDIUM; + if (sdhci_present_error(host, mrq->cmd, present)) + goto out_finish; + + cmd = sdhci_manual_cmd23(host, mrq) ? mrq->sbc : mrq->cmd; + + if (!sdhci_send_command_retry(host, cmd, flags)) + goto out_finish; + + spin_unlock_irqrestore(&host->lock, flags); + + return; + +out_finish: + sdhci_finish_mrq(host, mrq); + spin_unlock_irqrestore(&host->lock, flags); +} +EXPORT_SYMBOL_GPL(sdhci_request); + +int sdhci_request_atomic(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct sdhci_host *host = mmc_priv(mmc); + struct mmc_command *cmd; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&host->lock, flags); + + if (sdhci_present_error(host, mrq->cmd, true)) { sdhci_finish_mrq(host, mrq); - } else { - if (mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23)) - sdhci_send_command(host, mrq->sbc); - else - sdhci_send_command(host, mrq->cmd); + goto out_finish; } + cmd = sdhci_manual_cmd23(host, mrq) ? mrq->sbc : mrq->cmd; + + /* + * The HSQ may send a command in interrupt context without polling + * the busy signaling, which means we should return BUSY if controller + * has not released inhibit bits to allow HSQ trying to send request + * again in non-atomic context. So we should not finish this request + * here. + */ + if (!sdhci_send_command(host, cmd)) + ret = -EBUSY; + else + sdhci_led_activate(host); + +out_finish: spin_unlock_irqrestore(&host->lock, flags); + return ret; } -EXPORT_SYMBOL_GPL(sdhci_request); +EXPORT_SYMBOL_GPL(sdhci_request_atomic); void sdhci_set_bus_width(struct sdhci_host *host, int width) { @@ -2411,7 +2535,7 @@ int sdhci_start_signal_voltage_switch(struct mmc_host *mmc, if (!IS_ERR(mmc->supply.vqmmc)) { ret = mmc_regulator_set_vqmmc(mmc, ios); - if (ret) { + if (ret < 0) { pr_warn("%s: Switching to 3.3V signalling voltage failed\n", mmc_hostname(mmc)); return -EIO; @@ -2434,7 +2558,7 @@ int sdhci_start_signal_voltage_switch(struct mmc_host *mmc, return -EINVAL; if (!IS_ERR(mmc->supply.vqmmc)) { ret = mmc_regulator_set_vqmmc(mmc, ios); - if (ret) { + if (ret < 0) { pr_warn("%s: Switching to 1.8V signalling voltage failed\n", mmc_hostname(mmc)); return -EIO; @@ -2466,7 +2590,7 @@ int sdhci_start_signal_voltage_switch(struct mmc_host *mmc, return -EINVAL; if (!IS_ERR(mmc->supply.vqmmc)) { ret = mmc_regulator_set_vqmmc(mmc, ios); - if (ret) { + if (ret < 0) { pr_warn("%s: Switching to 1.2V signalling voltage failed\n", mmc_hostname(mmc)); return -EIO; @@ -2600,7 +2724,11 @@ void sdhci_send_tuning(struct sdhci_host *host, u32 opcode) */ sdhci_writew(host, SDHCI_TRNS_READ, SDHCI_TRANSFER_MODE); - sdhci_send_command(host, &cmd); + if (!sdhci_send_command_retry(host, &cmd, flags)) { + spin_unlock_irqrestore(&host->lock, flags); + host->tuning_done = 0; + return; + } host->cmd = NULL; @@ -3018,7 +3146,7 @@ static void sdhci_timeout_data_timer(struct timer_list *t) if (host->data) { host->data->error = -ETIMEDOUT; - sdhci_finish_data(host); + __sdhci_finish_data(host, true); queue_work(host->complete_wq, &host->complete_work); } else if (host->data_cmd) { host->data_cmd->error = -ETIMEDOUT; @@ -3390,6 +3518,9 @@ cont: } } out: + if (host->deferred_cmd) + result = IRQ_WAKE_THREAD; + spin_unlock(&host->lock); /* Process mrqs ready for immediate completion */ @@ -3415,6 +3546,7 @@ out: static irqreturn_t sdhci_thread_irq(int irq, void *dev_id) { struct sdhci_host *host = dev_id; + struct mmc_command *cmd; unsigned long flags; u32 isr; @@ -3422,8 +3554,14 @@ static irqreturn_t sdhci_thread_irq(int irq, void *dev_id) ; spin_lock_irqsave(&host->lock, flags); + isr = host->thread_isr; host->thread_isr = 0; + + cmd = host->deferred_cmd; + if (cmd && !sdhci_send_command_retry(host, cmd, flags)) + sdhci_finish_mrq(host, cmd->mrq); + spin_unlock_irqrestore(&host->lock, flags); if (isr & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) { @@ -4000,9 +4138,6 @@ int sdhci_setup_host(struct sdhci_host *host) mmc_hostname(mmc), host->version); } - if (host->quirks & SDHCI_QUIRK_BROKEN_CQE) - mmc->caps2 &= ~MMC_CAP2_CQE; - if (host->quirks & SDHCI_QUIRK_FORCE_DMA) host->flags |= SDHCI_USE_SDMA; else if (!(host->caps & SDHCI_CAN_DO_SDMA)) @@ -4117,11 +4252,9 @@ int sdhci_setup_host(struct sdhci_host *host) } if (host->version >= SDHCI_SPEC_300) - host->max_clk = (host->caps & SDHCI_CLOCK_V3_BASE_MASK) - >> SDHCI_CLOCK_BASE_SHIFT; + host->max_clk = FIELD_GET(SDHCI_CLOCK_V3_BASE_MASK, host->caps); else - host->max_clk = (host->caps & SDHCI_CLOCK_BASE_MASK) - >> SDHCI_CLOCK_BASE_SHIFT; + host->max_clk = FIELD_GET(SDHCI_CLOCK_BASE_MASK, host->caps); host->max_clk *= 1000000; if (host->max_clk == 0 || host->quirks & @@ -4139,8 +4272,7 @@ int sdhci_setup_host(struct sdhci_host *host) * In case of Host Controller v3.00, find out whether clock * multiplier is supported. */ - host->clk_mul = (host->caps1 & SDHCI_CLOCK_MUL_MASK) >> - SDHCI_CLOCK_MUL_SHIFT; + host->clk_mul = FIELD_GET(SDHCI_CLOCK_MUL_MASK, host->caps1); /* * In case the value in Clock Multiplier is 0, then programmable @@ -4173,8 +4305,7 @@ int sdhci_setup_host(struct sdhci_host *host) mmc->f_max = max_clk; if (!(host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)) { - host->timeout_clk = (host->caps & SDHCI_TIMEOUT_CLK_MASK) >> - SDHCI_TIMEOUT_CLK_SHIFT; + host->timeout_clk = FIELD_GET(SDHCI_TIMEOUT_CLK_MASK, host->caps); if (host->caps & SDHCI_TIMEOUT_CLK_UNIT) host->timeout_clk *= 1000; @@ -4204,7 +4335,7 @@ int sdhci_setup_host(struct sdhci_host *host) !host->ops->get_max_timeout_count) mmc->max_busy_timeout = 0; - mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23; + mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_CMD23; mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD; if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12) @@ -4326,8 +4457,8 @@ int sdhci_setup_host(struct sdhci_host *host) mmc->caps |= MMC_CAP_DRIVER_TYPE_D; /* Initial value for re-tuning timer count */ - host->tuning_count = (host->caps1 & SDHCI_RETUNING_TIMER_COUNT_MASK) >> - SDHCI_RETUNING_TIMER_COUNT_SHIFT; + host->tuning_count = FIELD_GET(SDHCI_RETUNING_TIMER_COUNT_MASK, + host->caps1); /* * In case Re-tuning Timer is not disabled, the actual value of @@ -4337,8 +4468,7 @@ int sdhci_setup_host(struct sdhci_host *host) host->tuning_count = 1 << (host->tuning_count - 1); /* Re-tuning mode supported by the Host Controller */ - host->tuning_mode = (host->caps1 & SDHCI_RETUNING_MODE_MASK) >> - SDHCI_RETUNING_MODE_SHIFT; + host->tuning_mode = FIELD_GET(SDHCI_RETUNING_MODE_MASK, host->caps1); ocr_avail = 0; @@ -4360,35 +4490,32 @@ int sdhci_setup_host(struct sdhci_host *host) curr = min_t(u32, curr, SDHCI_MAX_CURRENT_LIMIT); max_current_caps = - (curr << SDHCI_MAX_CURRENT_330_SHIFT) | - (curr << SDHCI_MAX_CURRENT_300_SHIFT) | - (curr << SDHCI_MAX_CURRENT_180_SHIFT); + FIELD_PREP(SDHCI_MAX_CURRENT_330_MASK, curr) | + FIELD_PREP(SDHCI_MAX_CURRENT_300_MASK, curr) | + FIELD_PREP(SDHCI_MAX_CURRENT_180_MASK, curr); } } if (host->caps & SDHCI_CAN_VDD_330) { ocr_avail |= MMC_VDD_32_33 | MMC_VDD_33_34; - mmc->max_current_330 = ((max_current_caps & - SDHCI_MAX_CURRENT_330_MASK) >> - SDHCI_MAX_CURRENT_330_SHIFT) * - SDHCI_MAX_CURRENT_MULTIPLIER; + mmc->max_current_330 = FIELD_GET(SDHCI_MAX_CURRENT_330_MASK, + max_current_caps) * + SDHCI_MAX_CURRENT_MULTIPLIER; } if (host->caps & SDHCI_CAN_VDD_300) { ocr_avail |= MMC_VDD_29_30 | MMC_VDD_30_31; - mmc->max_current_300 = ((max_current_caps & - SDHCI_MAX_CURRENT_300_MASK) >> - SDHCI_MAX_CURRENT_300_SHIFT) * - SDHCI_MAX_CURRENT_MULTIPLIER; + mmc->max_current_300 = FIELD_GET(SDHCI_MAX_CURRENT_300_MASK, + max_current_caps) * + SDHCI_MAX_CURRENT_MULTIPLIER; } if (host->caps & SDHCI_CAN_VDD_180) { ocr_avail |= MMC_VDD_165_195; - mmc->max_current_180 = ((max_current_caps & - SDHCI_MAX_CURRENT_180_MASK) >> - SDHCI_MAX_CURRENT_180_SHIFT) * - SDHCI_MAX_CURRENT_MULTIPLIER; + mmc->max_current_180 = FIELD_GET(SDHCI_MAX_CURRENT_180_MASK, + max_current_caps) * + SDHCI_MAX_CURRENT_MULTIPLIER; } /* If OCR set by host, use it instead. */ @@ -4539,6 +4666,12 @@ int __sdhci_add_host(struct sdhci_host *host) struct mmc_host *mmc = host->mmc; int ret; + if ((mmc->caps2 & MMC_CAP2_CQE) && + (host->quirks & SDHCI_QUIRK_BROKEN_CQE)) { + mmc->caps2 &= ~MMC_CAP2_CQE; + mmc->cqe_ops = NULL; + } + host->complete_wq = alloc_workqueue("sdhci", flags, 0); if (!host->complete_wq) return -ENOMEM; diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 79dffbb731d3..0008bbd27127 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -200,12 +200,10 @@ #define SDHCI_CTRL_PRESET_VAL_ENABLE 0x8000 #define SDHCI_CAPABILITIES 0x40 -#define SDHCI_TIMEOUT_CLK_MASK 0x0000003F -#define SDHCI_TIMEOUT_CLK_SHIFT 0 +#define SDHCI_TIMEOUT_CLK_MASK GENMASK(5, 0) #define SDHCI_TIMEOUT_CLK_UNIT 0x00000080 -#define SDHCI_CLOCK_BASE_MASK 0x00003F00 -#define SDHCI_CLOCK_V3_BASE_MASK 0x0000FF00 -#define SDHCI_CLOCK_BASE_SHIFT 8 +#define SDHCI_CLOCK_BASE_MASK GENMASK(13, 8) +#define SDHCI_CLOCK_V3_BASE_MASK GENMASK(15, 8) #define SDHCI_MAX_BLOCK_MASK 0x00030000 #define SDHCI_MAX_BLOCK_SHIFT 16 #define SDHCI_CAN_DO_8BIT 0x00040000 @@ -220,32 +218,25 @@ #define SDHCI_CAN_64BIT_V4 0x08000000 #define SDHCI_CAN_64BIT 0x10000000 +#define SDHCI_CAPABILITIES_1 0x44 #define SDHCI_SUPPORT_SDR50 0x00000001 #define SDHCI_SUPPORT_SDR104 0x00000002 #define SDHCI_SUPPORT_DDR50 0x00000004 #define SDHCI_DRIVER_TYPE_A 0x00000010 #define SDHCI_DRIVER_TYPE_C 0x00000020 #define SDHCI_DRIVER_TYPE_D 0x00000040 -#define SDHCI_RETUNING_TIMER_COUNT_MASK 0x00000F00 -#define SDHCI_RETUNING_TIMER_COUNT_SHIFT 8 +#define SDHCI_RETUNING_TIMER_COUNT_MASK GENMASK(11, 8) #define SDHCI_USE_SDR50_TUNING 0x00002000 -#define SDHCI_RETUNING_MODE_MASK 0x0000C000 -#define SDHCI_RETUNING_MODE_SHIFT 14 -#define SDHCI_CLOCK_MUL_MASK 0x00FF0000 -#define SDHCI_CLOCK_MUL_SHIFT 16 +#define SDHCI_RETUNING_MODE_MASK GENMASK(15, 14) +#define SDHCI_CLOCK_MUL_MASK GENMASK(23, 16) #define SDHCI_CAN_DO_ADMA3 0x08000000 #define SDHCI_SUPPORT_HS400 0x80000000 /* Non-standard */ -#define SDHCI_CAPABILITIES_1 0x44 - #define SDHCI_MAX_CURRENT 0x48 -#define SDHCI_MAX_CURRENT_LIMIT 0xFF -#define SDHCI_MAX_CURRENT_330_MASK 0x0000FF -#define SDHCI_MAX_CURRENT_330_SHIFT 0 -#define SDHCI_MAX_CURRENT_300_MASK 0x00FF00 -#define SDHCI_MAX_CURRENT_300_SHIFT 8 -#define SDHCI_MAX_CURRENT_180_MASK 0xFF0000 -#define SDHCI_MAX_CURRENT_180_SHIFT 16 +#define SDHCI_MAX_CURRENT_LIMIT GENMASK(7, 0) +#define SDHCI_MAX_CURRENT_330_MASK GENMASK(7, 0) +#define SDHCI_MAX_CURRENT_300_MASK GENMASK(15, 8) +#define SDHCI_MAX_CURRENT_180_MASK GENMASK(23, 16) #define SDHCI_MAX_CURRENT_MULTIPLIER 4 /* 4C-4F reserved for more max current */ @@ -540,6 +531,7 @@ struct sdhci_host { struct mmc_request *mrqs_done[SDHCI_MAX_MRQS]; /* Requests done */ struct mmc_command *cmd; /* Current command */ struct mmc_command *data_cmd; /* Current data command */ + struct mmc_command *deferred_cmd; /* Deferred command */ struct mmc_data *data; /* Current data request */ unsigned int data_early:1; /* Data finished before cmd */ @@ -653,8 +645,12 @@ struct sdhci_ops { void (*voltage_switch)(struct sdhci_host *host); void (*adma_write_desc)(struct sdhci_host *host, void **desc, dma_addr_t addr, int len, unsigned int cmd); + void (*copy_to_bounce_buffer)(struct sdhci_host *host, + struct mmc_data *data, + unsigned int length); void (*request_done)(struct sdhci_host *host, struct mmc_request *mrq); + void (*dump_vendor_regs)(struct sdhci_host *host); }; #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS @@ -757,7 +753,6 @@ void sdhci_cleanup_host(struct sdhci_host *host); int __sdhci_add_host(struct sdhci_host *host); int sdhci_add_host(struct sdhci_host *host); void sdhci_remove_host(struct sdhci_host *host, int dead); -void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd); static inline void sdhci_read_caps(struct sdhci_host *host) { @@ -776,6 +771,7 @@ void sdhci_set_power_and_bus_voltage(struct sdhci_host *host, void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode, unsigned short vdd); void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq); +int sdhci_request_atomic(struct mmc_host *mmc, struct mmc_request *mrq); void sdhci_set_bus_width(struct sdhci_host *host, int width); void sdhci_reset(struct sdhci_host *host, u8 mask); void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing); diff --git a/drivers/mmc/host/sdricoh_cs.c b/drivers/mmc/host/sdricoh_cs.c index a38b8b2a4e5c..76a8cd3a186f 100644 --- a/drivers/mmc/host/sdricoh_cs.c +++ b/drivers/mmc/host/sdricoh_cs.c @@ -15,6 +15,7 @@ #include <linux/module.h> #include <linux/pci.h> #include <linux/ioport.h> +#include <linux/iopoll.h> #include <linux/scatterlist.h> #include <pcmcia/cistpl.h> @@ -22,6 +23,7 @@ #include <linux/io.h> #include <linux/mmc/host.h> +#include <linux/mmc/mmc.h> #define DRIVER_NAME "sdricoh_cs" @@ -57,10 +59,8 @@ static unsigned int switchlocked; #define STATUS_BUSY 0x40000000 /* timeouts */ -#define INIT_TIMEOUT 100 -#define CMD_TIMEOUT 100000 -#define TRANSFER_TIMEOUT 100000 -#define BUSY_TIMEOUT 32767 +#define SDRICOH_CMD_TIMEOUT_US 1000000 +#define SDRICOH_DATA_TIMEOUT_US 1000000 /* list of supported pcmcia devices */ static const struct pcmcia_device_id pcmcia_ids[] = { @@ -124,19 +124,24 @@ static inline unsigned int sdricoh_readb(struct sdricoh_host *host, return value; } -static int sdricoh_query_status(struct sdricoh_host *host, unsigned int wanted, - unsigned int timeout){ - unsigned int loop; +static bool sdricoh_status_ok(struct sdricoh_host *host, unsigned int status, + unsigned int wanted) +{ + sdricoh_writel(host, R2E4_STATUS_RESP, status); + return status & wanted; +} + +static int sdricoh_query_status(struct sdricoh_host *host, unsigned int wanted) +{ + int ret; unsigned int status = 0; struct device *dev = host->dev; - for (loop = 0; loop < timeout; loop++) { - status = sdricoh_readl(host, R21C_STATUS); - sdricoh_writel(host, R2E4_STATUS_RESP, status); - if (status & wanted) - break; - } - if (loop == timeout) { + ret = read_poll_timeout(sdricoh_readl, status, + sdricoh_status_ok(host, status, wanted), + 32, SDRICOH_DATA_TIMEOUT_US, false, + host, R21C_STATUS); + if (ret) { dev_err(dev, "query_status: timeout waiting for %x\n", wanted); return -ETIMEDOUT; } @@ -150,35 +155,46 @@ static int sdricoh_query_status(struct sdricoh_host *host, unsigned int wanted, } -static int sdricoh_mmc_cmd(struct sdricoh_host *host, unsigned char opcode, - unsigned int arg) +static int sdricoh_mmc_cmd(struct sdricoh_host *host, struct mmc_command *cmd) { - unsigned int status; - int result = 0; - unsigned int loop = 0; + unsigned int status, timeout_us; + int ret; + unsigned char opcode = cmd->opcode; + /* reset status reg? */ sdricoh_writel(host, R21C_STATUS, 0x18); + + /* MMC_APP_CMDs need some special handling */ + if (host->app_cmd) { + opcode |= 64; + host->app_cmd = 0; + } else if (opcode == MMC_APP_CMD) + host->app_cmd = 1; + /* fill parameters */ - sdricoh_writel(host, R204_CMD_ARG, arg); + sdricoh_writel(host, R204_CMD_ARG, cmd->arg); sdricoh_writel(host, R200_CMD, (0x10000 << 8) | opcode); + /* wait for command completion */ - if (opcode) { - for (loop = 0; loop < CMD_TIMEOUT; loop++) { - status = sdricoh_readl(host, R21C_STATUS); - sdricoh_writel(host, R2E4_STATUS_RESP, status); - if (status & STATUS_CMD_FINISHED) - break; - } - /* don't check for timeout in the loop it is not always - reset correctly - */ - if (loop == CMD_TIMEOUT || status & STATUS_CMD_TIMEOUT) - result = -ETIMEDOUT; + if (!opcode) + return 0; - } + timeout_us = cmd->busy_timeout ? cmd->busy_timeout * 1000 : + SDRICOH_CMD_TIMEOUT_US; - return result; + ret = read_poll_timeout(sdricoh_readl, status, + sdricoh_status_ok(host, status, STATUS_CMD_FINISHED), + 32, timeout_us, false, + host, R21C_STATUS); + + /* + * Don't check for timeout status in the loop, as it's not always reset + * correctly. + */ + if (ret || status & STATUS_CMD_TIMEOUT) + return -ETIMEDOUT; + return 0; } static int sdricoh_reset(struct sdricoh_host *host) @@ -207,8 +223,7 @@ static int sdricoh_blockio(struct sdricoh_host *host, int read, u32 data = 0; /* wait until the data is available */ if (read) { - if (sdricoh_query_status(host, STATUS_READY_TO_READ, - TRANSFER_TIMEOUT)) + if (sdricoh_query_status(host, STATUS_READY_TO_READ)) return -ETIMEDOUT; sdricoh_writel(host, R21C_STATUS, 0x18); /* read data */ @@ -224,8 +239,7 @@ static int sdricoh_blockio(struct sdricoh_host *host, int read, } } } else { - if (sdricoh_query_status(host, STATUS_READY_TO_WRITE, - TRANSFER_TIMEOUT)) + if (sdricoh_query_status(host, STATUS_READY_TO_WRITE)) return -ETIMEDOUT; sdricoh_writel(host, R21C_STATUS, 0x18); /* write data */ @@ -251,28 +265,20 @@ static void sdricoh_request(struct mmc_host *mmc, struct mmc_request *mrq) struct mmc_command *cmd = mrq->cmd; struct mmc_data *data = cmd->data; struct device *dev = host->dev; - unsigned char opcode = cmd->opcode; int i; dev_dbg(dev, "=============================\n"); - dev_dbg(dev, "sdricoh_request opcode=%i\n", opcode); + dev_dbg(dev, "sdricoh_request opcode=%i\n", cmd->opcode); sdricoh_writel(host, R21C_STATUS, 0x18); - /* MMC_APP_CMDs need some special handling */ - if (host->app_cmd) { - opcode |= 64; - host->app_cmd = 0; - } else if (opcode == 55) - host->app_cmd = 1; - /* read/write commands seem to require this */ if (data) { sdricoh_writew(host, R226_BLOCKSIZE, data->blksz); sdricoh_writel(host, R208_DATAIO, 0); } - cmd->error = sdricoh_mmc_cmd(host, opcode, cmd->arg); + cmd->error = sdricoh_mmc_cmd(host, cmd); /* read response buffer */ if (cmd->flags & MMC_RSP_PRESENT) { @@ -323,8 +329,7 @@ static void sdricoh_request(struct mmc_host *mmc, struct mmc_request *mrq) sdricoh_writel(host, R208_DATAIO, 1); - if (sdricoh_query_status(host, STATUS_TRANSFER_FINISHED, - TRANSFER_TIMEOUT)) { + if (sdricoh_query_status(host, STATUS_TRANSFER_FINISHED)) { dev_err(dev, "sdricoh_request: transfer end error\n"); cmd->error = -EINVAL; } diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index f87d7967457f..5e95bbc51644 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -951,9 +951,13 @@ static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) static int sunxi_mmc_volt_switch(struct mmc_host *mmc, struct mmc_ios *ios) { + int ret; + /* vqmmc regulator is available */ - if (!IS_ERR(mmc->supply.vqmmc)) - return mmc_regulator_set_vqmmc(mmc, ios); + if (!IS_ERR(mmc->supply.vqmmc)) { + ret = mmc_regulator_set_vqmmc(mmc, ios); + return ret < 0 ? ret : 0; + } /* no vqmmc regulator, assume fixed regulator at 3/3.3V */ if (mmc->ios.signal_voltage == MMC_SIGNAL_VOLTAGE_330) @@ -1390,7 +1394,7 @@ static int sunxi_mmc_probe(struct platform_device *pdev) mmc->f_min = 400000; mmc->f_max = 52000000; mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED | - MMC_CAP_ERASE | MMC_CAP_SDIO_IRQ; + MMC_CAP_SDIO_IRQ; /* * Some H5 devices do not have signal traces precise enough to diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c index 54271b92ee59..5987656e0474 100644 --- a/drivers/mmc/host/tifm_sd.c +++ b/drivers/mmc/host/tifm_sd.c @@ -73,6 +73,8 @@ module_param(fixed_timeout, bool, 0644); #define TIFM_MMCSD_MAX_BLOCK_SIZE 0x0800UL +#define TIFM_MMCSD_REQ_TIMEOUT_MS 1000 + enum { CMD_READY = 0x0001, FIFO_READY = 0x0002, @@ -959,7 +961,12 @@ static int tifm_sd_probe(struct tifm_dev *sock) host = mmc_priv(mmc); tifm_set_drvdata(sock, mmc); host->dev = sock; - host->timeout_jiffies = msecs_to_jiffies(1000); + host->timeout_jiffies = msecs_to_jiffies(TIFM_MMCSD_REQ_TIMEOUT_MS); + /* + * We use a fixed request timeout of 1s, hence inform the core about it. + * A future improvement should instead respect the cmd->busy_timeout. + */ + mmc->max_busy_timeout = TIFM_MMCSD_REQ_TIMEOUT_MS; tasklet_init(&host->finish_tasklet, tifm_sd_end_cmd, (unsigned long)host); diff --git a/drivers/mmc/host/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c index 9520bd94cf43..d7fde57c78c1 100644 --- a/drivers/mmc/host/tmio_mmc_core.c +++ b/drivers/mmc/host/tmio_mmc_core.c @@ -39,7 +39,6 @@ #include <linux/module.h> #include <linux/pagemap.h> #include <linux/platform_device.h> -#include <linux/pm_domain.h> #include <linux/pm_qos.h> #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> @@ -1128,7 +1127,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host) if (ret == -EPROBE_DEFER) return ret; - mmc->caps |= MMC_CAP_ERASE | MMC_CAP_4_BIT_DATA | pdata->capabilities; + mmc->caps |= MMC_CAP_4_BIT_DATA | pdata->capabilities; mmc->caps2 |= pdata->capabilities2; mmc->max_segs = pdata->max_segs ? : 32; mmc->max_blk_size = TMIO_MAX_BLK_SIZE; @@ -1192,7 +1191,6 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host) /* See if we also get DMA */ tmio_mmc_request_dma(_host, pdata); - dev_pm_domain_start(&pdev->dev); pm_runtime_get_noresume(&pdev->dev); pm_runtime_set_active(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, 50); @@ -1231,12 +1229,14 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host) cancel_work_sync(&host->done); cancel_delayed_work_sync(&host->delayed_reset_work); tmio_mmc_release_dma(host); + tmio_mmc_disable_mmc_irqs(host, TMIO_MASK_ALL); - pm_runtime_dont_use_autosuspend(&pdev->dev); if (host->native_hotplug) pm_runtime_put_noidle(&pdev->dev); - pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); } EXPORT_SYMBOL_GPL(tmio_mmc_host_remove); diff --git a/drivers/mmc/host/uniphier-sd.c b/drivers/mmc/host/uniphier-sd.c index a1683c49cb90..f82baf99fd69 100644 --- a/drivers/mmc/host/uniphier-sd.c +++ b/drivers/mmc/host/uniphier-sd.c @@ -610,11 +610,6 @@ static int uniphier_sd_probe(struct platform_device *pdev) } } - ret = devm_request_irq(dev, irq, tmio_mmc_irq, IRQF_SHARED, - dev_name(dev), host); - if (ret) - goto free_host; - if (priv->caps & UNIPHIER_SD_CAP_EXTENDED_IP) host->dma_ops = &uniphier_sd_internal_dma_ops; else @@ -642,8 +637,15 @@ static int uniphier_sd_probe(struct platform_device *pdev) if (ret) goto free_host; + ret = devm_request_irq(dev, irq, tmio_mmc_irq, IRQF_SHARED, + dev_name(dev), host); + if (ret) + goto remove_host; + return 0; +remove_host: + tmio_mmc_host_remove(host); free_host: tmio_mmc_host_free(host); diff --git a/drivers/mmc/host/usdhi6rol0.c b/drivers/mmc/host/usdhi6rol0.c index 9a0b1e4e405d..369b8dee2e3d 100644 --- a/drivers/mmc/host/usdhi6rol0.c +++ b/drivers/mmc/host/usdhi6rol0.c @@ -136,6 +136,8 @@ #define USDHI6_MIN_DMA 64 +#define USDHI6_REQ_TIMEOUT_MS 4000 + enum usdhi6_wait_for { USDHI6_WAIT_FOR_REQUEST, USDHI6_WAIT_FOR_CMD, @@ -1763,7 +1765,12 @@ static int usdhi6_probe(struct platform_device *pdev) host = mmc_priv(mmc); host->mmc = mmc; host->wait = USDHI6_WAIT_FOR_REQUEST; - host->timeout = msecs_to_jiffies(4000); + host->timeout = msecs_to_jiffies(USDHI6_REQ_TIMEOUT_MS); + /* + * We use a fixed timeout of 4s, hence inform the core about it. A + * future improvement should instead respect the cmd->busy_timeout. + */ + mmc->max_busy_timeout = USDHI6_REQ_TIMEOUT_MS; host->pinctrl = devm_pinctrl_get(&pdev->dev); if (IS_ERR(host->pinctrl)) { diff --git a/drivers/mmc/host/via-sdmmc.c b/drivers/mmc/host/via-sdmmc.c index e48bddd95ce6..ef95bce50889 100644 --- a/drivers/mmc/host/via-sdmmc.c +++ b/drivers/mmc/host/via-sdmmc.c @@ -319,6 +319,8 @@ struct via_crdr_mmc_host { /* some devices need a very long delay for power to stabilize */ #define VIA_CRDR_QUIRK_300MS_PWRDELAY 0x0001 +#define VIA_CMD_TIMEOUT_MS 1000 + static const struct pci_device_id via_ids[] = { {PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_9530, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0,}, @@ -551,14 +553,17 @@ static void via_sdc_send_command(struct via_crdr_mmc_host *host, { void __iomem *addrbase; struct mmc_data *data; + unsigned int timeout_ms; u32 cmdctrl = 0; WARN_ON(host->cmd); data = cmd->data; - mod_timer(&host->timer, jiffies + HZ); host->cmd = cmd; + timeout_ms = cmd->busy_timeout ? cmd->busy_timeout : VIA_CMD_TIMEOUT_MS; + mod_timer(&host->timer, jiffies + msecs_to_jiffies(timeout_ms)); + /*Command index*/ cmdctrl = cmd->opcode << 8; diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c index 740179f42cf2..67f917d6ecd3 100644 --- a/drivers/mmc/host/wbsd.c +++ b/drivers/mmc/host/wbsd.c @@ -28,6 +28,8 @@ #include <linux/pnp.h> #include <linux/highmem.h> #include <linux/mmc/host.h> +#include <linux/mmc/mmc.h> +#include <linux/mmc/sd.h> #include <linux/scatterlist.h> #include <linux/slab.h> @@ -770,22 +772,22 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq) * interrupts. */ switch (cmd->opcode) { - case 11: - case 17: - case 18: - case 20: - case 24: - case 25: - case 26: - case 27: - case 30: - case 42: - case 56: + case SD_SWITCH_VOLTAGE: + case MMC_READ_SINGLE_BLOCK: + case MMC_READ_MULTIPLE_BLOCK: + case MMC_WRITE_DAT_UNTIL_STOP: + case MMC_WRITE_BLOCK: + case MMC_WRITE_MULTIPLE_BLOCK: + case MMC_PROGRAM_CID: + case MMC_PROGRAM_CSD: + case MMC_SEND_WRITE_PROT: + case MMC_LOCK_UNLOCK: + case MMC_GEN_CMD: break; /* ACMDs. We don't keep track of state, so we just treat them * like any other command. */ - case 51: + case SD_APP_SEND_SCR: break; default: |