summaryrefslogtreecommitdiff
path: root/drivers/mmc/host
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/host')
-rw-r--r--drivers/mmc/host/Kconfig32
-rw-r--r--drivers/mmc/host/Makefile1
-rw-r--r--drivers/mmc/host/android-goldfish.c8
-rw-r--r--drivers/mmc/host/atmel-mci.c21
-rw-r--r--drivers/mmc/host/au1xmmc.c12
-rw-r--r--drivers/mmc/host/davinci_mmc.c6
-rw-r--r--drivers/mmc/host/dw_mmc-bluefield.c81
-rw-r--r--drivers/mmc/host/dw_mmc-rockchip.c5
-rw-r--r--drivers/mmc/host/dw_mmc.c4
-rw-r--r--drivers/mmc/host/jz4740_mmc.c203
-rw-r--r--drivers/mmc/host/meson-gx-mmc.c72
-rw-r--r--drivers/mmc/host/mmci.c5
-rw-r--r--drivers/mmc/host/mtk-sd.c63
-rw-r--r--drivers/mmc/host/mvsdio.c6
-rw-r--r--drivers/mmc/host/mxcmmc.c22
-rw-r--r--drivers/mmc/host/renesas_sdhi_core.c5
-rw-r--r--drivers/mmc/host/renesas_sdhi_internal_dmac.c32
-rw-r--r--drivers/mmc/host/renesas_sdhi_sys_dmac.c3
-rw-r--r--drivers/mmc/host/rtsx_usb_sdmmc.c19
-rw-r--r--drivers/mmc/host/sdhci-bcm-kona.c4
-rw-r--r--drivers/mmc/host/sdhci-cadence.c22
-rw-r--r--drivers/mmc/host/sdhci-esdhc-imx.c22
-rw-r--r--drivers/mmc/host/sdhci-iproc.c33
-rw-r--r--drivers/mmc/host/sdhci-msm.c85
-rw-r--r--drivers/mmc/host/sdhci-of-arasan.c9
-rw-r--r--drivers/mmc/host/sdhci-omap.c92
-rw-r--r--drivers/mmc/host/sdhci-pci-core.c38
-rw-r--r--drivers/mmc/host/sdhci-pci.h1
-rw-r--r--drivers/mmc/host/sdhci-pic32.c4
-rw-r--r--drivers/mmc/host/sdhci-pxav2.c4
-rw-r--r--drivers/mmc/host/sdhci-pxav3.c4
-rw-r--r--drivers/mmc/host/sdhci-s3c.c4
-rw-r--r--drivers/mmc/host/sdhci-spear.c4
-rw-r--r--drivers/mmc/host/sdhci-st.c4
-rw-r--r--drivers/mmc/host/sdhci-tegra.c2
-rw-r--r--drivers/mmc/host/sdhci-xenon-phy.c11
-rw-r--r--drivers/mmc/host/sdhci.c148
-rw-r--r--drivers/mmc/host/sdhci.h15
-rw-r--r--drivers/mmc/host/sunxi-mmc.c187
-rw-r--r--drivers/mmc/host/ushc.c4
-rw-r--r--drivers/mmc/host/wbsd.c68
-rw-r--r--drivers/mmc/host/wmt-sdmmc.c6
42 files changed, 998 insertions, 373 deletions
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 9589f9c9046f..0581c199c996 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -345,11 +345,11 @@ config MMC_SDHCI_IPROC
If unsure, say N.
config MMC_MESON_GX
- tristate "Amlogic S905/GX* SD/MMC Host Controller support"
+ tristate "Amlogic S905/GX*/AXG SD/MMC Host Controller support"
depends on ARCH_MESON && MMC
help
This selects support for the Amlogic SD/MMC Host Controller
- found on the S905/GX* family of SoCs. This controller is
+ found on the S905/GX*/AXG family of SoCs. This controller is
MMC 5.1 compliant and supports SD, eMMC and SDIO interfaces.
If you have a controller with this interface, say Y here.
@@ -358,7 +358,6 @@ config MMC_MESON_MX_SDIO
tristate "Amlogic Meson6/Meson8/Meson8b SD/MMC Host Controller support"
depends on ARCH_MESON || COMPILE_TEST
depends on COMMON_CLK
- depends on HAS_DMA
depends on OF
help
This selects support for the SD/MMC Host Controller on
@@ -401,7 +400,6 @@ config MMC_OMAP
config MMC_OMAP_HS
tristate "TI OMAP High Speed Multimedia Card Interface support"
- depends on HAS_DMA
depends on ARCH_OMAP2PLUS || ARCH_KEYSTONE || COMPILE_TEST
help
This selects the TI OMAP High Speed Multimedia card Interface.
@@ -511,7 +509,6 @@ config MMC_DAVINCI
config MMC_GOLDFISH
tristate "goldfish qemu Multimedia Card Interface support"
- depends on HAS_DMA
depends on GOLDFISH || COMPILE_TEST
help
This selects the Goldfish Multimedia card Interface emulation
@@ -605,7 +602,7 @@ config MMC_SDHI
config MMC_SDHI_SYS_DMAC
tristate "DMA for SDHI SD/SDIO controllers using SYS-DMAC"
- depends on MMC_SDHI && HAS_DMA
+ depends on MMC_SDHI
default MMC_SDHI if (SUPERH || ARM)
help
This provides DMA support for SDHI SD/SDIO controllers
@@ -615,7 +612,7 @@ config MMC_SDHI_SYS_DMAC
config MMC_SDHI_INTERNAL_DMAC
tristate "DMA for SDHI SD/SDIO controllers using on-chip bus mastering"
depends on ARM64 || COMPILE_TEST
- depends on MMC_SDHI && HAS_DMA
+ depends on MMC_SDHI
default MMC_SDHI if ARM64
help
This provides DMA support for SDHI SD/SDIO controllers
@@ -669,7 +666,6 @@ config MMC_CAVIUM_THUNDERX
config MMC_DW
tristate "Synopsys DesignWare Memory Card Interface"
- depends on HAS_DMA
depends on ARC || ARM || ARM64 || MIPS || COMPILE_TEST
help
This selects support for the Synopsys DesignWare Mobile Storage IP
@@ -690,6 +686,15 @@ config MMC_DW_PLTFM
If unsure, say Y.
+config MMC_DW_BLUEFIELD
+ tristate "BlueField specific extensions for Synopsys DW Memory Card Interface"
+ depends on MMC_DW
+ select MMC_DW_PLTFM
+ help
+ This selects support for Mellanox BlueField SoC specific extensions to
+ the Synopsys DesignWare Memory Card Interface driver. Select this
+ option for platforms based on Mellanox BlueField SoC's.
+
config MMC_DW_EXYNOS
tristate "Exynos specific extensions for Synopsys DW Memory Card Interface"
depends on MMC_DW
@@ -748,7 +753,6 @@ config MMC_DW_ZX
config MMC_SH_MMCIF
tristate "SuperH Internal MMCIF support"
- depends on HAS_DMA
depends on SUPERH || ARCH_RENESAS || COMPILE_TEST
help
This selects the MMC Host Interface controller (MMCIF) found in various
@@ -756,11 +760,12 @@ config MMC_SH_MMCIF
config MMC_JZ4740
- tristate "JZ4740 SD/Multimedia Card Interface support"
- depends on MACH_JZ4740
+ tristate "Ingenic JZ47xx SD/Multimedia Card Interface support"
+ depends on MACH_JZ4740 || MACH_JZ4780
help
- This selects support for the SD/MMC controller on Ingenic JZ4740
- SoCs.
+ This selects support for the SD/MMC controller on Ingenic
+ JZ4740, JZ4750, JZ4770 and JZ4780 SoCs.
+
If you have a board based on such a SoC and with a SD/MMC slot,
say Y or M here.
@@ -868,7 +873,6 @@ config MMC_TOSHIBA_PCI
config MMC_BCM2835
tristate "Broadcom BCM2835 SDHOST MMC Controller support"
depends on ARCH_BCM2835 || COMPILE_TEST
- depends on HAS_DMA
help
This selects the BCM2835 SDHOST MMC controller. If you have
a BCM2835 platform with SD or MMC devices, say Y or M here.
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 6aead24879b4..85dc1322c3de 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -49,6 +49,7 @@ thunderx-mmc-objs := cavium.o cavium-thunderx.o
obj-$(CONFIG_MMC_CAVIUM_THUNDERX) += thunderx-mmc.o
obj-$(CONFIG_MMC_DW) += dw_mmc.o
obj-$(CONFIG_MMC_DW_PLTFM) += dw_mmc-pltfm.o
+obj-$(CONFIG_MMC_DW_BLUEFIELD) += dw_mmc-bluefield.o
obj-$(CONFIG_MMC_DW_EXYNOS) += dw_mmc-exynos.o
obj-$(CONFIG_MMC_DW_HI3798CV200) += dw_mmc-hi3798cv200.o
obj-$(CONFIG_MMC_DW_K3) += dw_mmc-k3.o
diff --git a/drivers/mmc/host/android-goldfish.c b/drivers/mmc/host/android-goldfish.c
index 63d27589cd89..294de177632c 100644
--- a/drivers/mmc/host/android-goldfish.c
+++ b/drivers/mmc/host/android-goldfish.c
@@ -217,8 +217,8 @@ static void goldfish_mmc_xfer_done(struct goldfish_mmc_host *host,
* We don't really have DMA, so we need
* to copy from our platform driver buffer
*/
- uint8_t *dest = (uint8_t *)sg_virt(data->sg);
- memcpy(dest, host->virt_base, data->sg->length);
+ sg_copy_to_buffer(data->sg, 1, host->virt_base,
+ data->sg->length);
}
host->data->bytes_xfered += data->sg->length;
dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_len,
@@ -393,8 +393,8 @@ static void goldfish_mmc_prepare_data(struct goldfish_mmc_host *host,
* We don't really have DMA, so we need to copy to our
* platform driver buffer
*/
- const uint8_t *src = (uint8_t *)sg_virt(data->sg);
- memcpy(host->virt_base, src, data->sg->length);
+ sg_copy_from_buffer(data->sg, 1, host->virt_base,
+ data->sg->length);
}
}
diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c
index e55f3932d580..5aa2c9404e92 100644
--- a/drivers/mmc/host/atmel-mci.c
+++ b/drivers/mmc/host/atmel-mci.c
@@ -1967,7 +1967,6 @@ static void atmci_tasklet_func(unsigned long priv)
static void atmci_read_data_pio(struct atmel_mci *host)
{
struct scatterlist *sg = host->sg;
- void *buf = sg_virt(sg);
unsigned int offset = host->pio_offset;
struct mmc_data *data = host->data;
u32 value;
@@ -1977,7 +1976,7 @@ static void atmci_read_data_pio(struct atmel_mci *host)
do {
value = atmci_readl(host, ATMCI_RDR);
if (likely(offset + 4 <= sg->length)) {
- put_unaligned(value, (u32 *)(buf + offset));
+ sg_pcopy_to_buffer(sg, 1, &value, sizeof(u32), offset);
offset += 4;
nbytes += 4;
@@ -1990,11 +1989,11 @@ static void atmci_read_data_pio(struct atmel_mci *host)
goto done;
offset = 0;
- buf = sg_virt(sg);
}
} else {
unsigned int remaining = sg->length - offset;
- memcpy(buf + offset, &value, remaining);
+
+ sg_pcopy_to_buffer(sg, 1, &value, remaining, offset);
nbytes += remaining;
flush_dcache_page(sg_page(sg));
@@ -2004,8 +2003,8 @@ static void atmci_read_data_pio(struct atmel_mci *host)
goto done;
offset = 4 - remaining;
- buf = sg_virt(sg);
- memcpy(buf, (u8 *)&value + remaining, offset);
+ sg_pcopy_to_buffer(sg, 1, (u8 *)&value + remaining,
+ offset, 0);
nbytes += offset;
}
@@ -2035,7 +2034,6 @@ done:
static void atmci_write_data_pio(struct atmel_mci *host)
{
struct scatterlist *sg = host->sg;
- void *buf = sg_virt(sg);
unsigned int offset = host->pio_offset;
struct mmc_data *data = host->data;
u32 value;
@@ -2044,7 +2042,7 @@ static void atmci_write_data_pio(struct atmel_mci *host)
do {
if (likely(offset + 4 <= sg->length)) {
- value = get_unaligned((u32 *)(buf + offset));
+ sg_pcopy_from_buffer(sg, 1, &value, sizeof(u32), offset);
atmci_writel(host, ATMCI_TDR, value);
offset += 4;
@@ -2056,13 +2054,12 @@ static void atmci_write_data_pio(struct atmel_mci *host)
goto done;
offset = 0;
- buf = sg_virt(sg);
}
} else {
unsigned int remaining = sg->length - offset;
value = 0;
- memcpy(&value, buf + offset, remaining);
+ sg_pcopy_from_buffer(sg, 1, &value, remaining, offset);
nbytes += remaining;
host->sg = sg = sg_next(sg);
@@ -2073,8 +2070,8 @@ static void atmci_write_data_pio(struct atmel_mci *host)
}
offset = 4 - remaining;
- buf = sg_virt(sg);
- memcpy((u8 *)&value + remaining, buf, offset);
+ sg_pcopy_from_buffer(sg, 1, (u8 *)&value + remaining,
+ offset, 0);
atmci_writel(host, ATMCI_TDR, value);
nbytes += offset;
}
diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c
index ed77fbfa4774..9b4be67330dd 100644
--- a/drivers/mmc/host/au1xmmc.c
+++ b/drivers/mmc/host/au1xmmc.c
@@ -40,6 +40,7 @@
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <linux/scatterlist.h>
+#include <linux/highmem.h>
#include <linux/leds.h>
#include <linux/mmc/host.h>
#include <linux/slab.h>
@@ -405,7 +406,7 @@ static void au1xmmc_send_pio(struct au1xmmc_host *host)
/* This is the pointer to the data buffer */
sg = &data->sg[host->pio.index];
- sg_ptr = sg_virt(sg) + host->pio.offset;
+ sg_ptr = kmap_atomic(sg_page(sg)) + sg->offset + host->pio.offset;
/* This is the space left inside the buffer */
sg_len = data->sg[host->pio.index].length - host->pio.offset;
@@ -421,11 +422,12 @@ static void au1xmmc_send_pio(struct au1xmmc_host *host)
if (!(status & SD_STATUS_TH))
break;
- val = *sg_ptr++;
+ val = sg_ptr[count];
__raw_writel((unsigned long)val, HOST_TXPORT(host));
wmb(); /* drain writebuffer */
}
+ kunmap_atomic(sg_ptr);
host->pio.len -= count;
host->pio.offset += count;
@@ -462,7 +464,7 @@ static void au1xmmc_receive_pio(struct au1xmmc_host *host)
if (host->pio.index < host->dma.len) {
sg = &data->sg[host->pio.index];
- sg_ptr = sg_virt(sg) + host->pio.offset;
+ sg_ptr = kmap_atomic(sg_page(sg)) + sg->offset + host->pio.offset;
/* This is the space left inside the buffer */
sg_len = sg_dma_len(&data->sg[host->pio.index]) - host->pio.offset;
@@ -501,8 +503,10 @@ static void au1xmmc_receive_pio(struct au1xmmc_host *host)
val = __raw_readl(HOST_RXPORT(host));
if (sg_ptr)
- *sg_ptr++ = (unsigned char)(val & 0xFF);
+ sg_ptr[count] = (unsigned char)(val & 0xFF);
}
+ if (sg_ptr)
+ kunmap_atomic(sg_ptr);
host->pio.len -= count;
host->pio.offset += count;
diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c
index 8e363174f9d6..9e68c3645e22 100644
--- a/drivers/mmc/host/davinci_mmc.c
+++ b/drivers/mmc/host/davinci_mmc.c
@@ -1377,8 +1377,7 @@ static int __exit davinci_mmcsd_remove(struct platform_device *pdev)
#ifdef CONFIG_PM
static int davinci_mmcsd_suspend(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct mmc_davinci_host *host = platform_get_drvdata(pdev);
+ struct mmc_davinci_host *host = dev_get_drvdata(dev);
writel(0, host->base + DAVINCI_MMCIM);
mmc_davinci_reset_ctrl(host, 1);
@@ -1389,8 +1388,7 @@ static int davinci_mmcsd_suspend(struct device *dev)
static int davinci_mmcsd_resume(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct mmc_davinci_host *host = platform_get_drvdata(pdev);
+ struct mmc_davinci_host *host = dev_get_drvdata(dev);
clk_enable(host->clk);
mmc_davinci_reset_ctrl(host, 0);
diff --git a/drivers/mmc/host/dw_mmc-bluefield.c b/drivers/mmc/host/dw_mmc-bluefield.c
new file mode 100644
index 000000000000..54c3fbb4a391
--- /dev/null
+++ b/drivers/mmc/host/dw_mmc-bluefield.c
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Mellanox Technologies.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include "dw_mmc.h"
+#include "dw_mmc-pltfm.h"
+
+#define UHS_REG_EXT_SAMPLE_MASK GENMASK(22, 16)
+#define UHS_REG_EXT_DRIVE_MASK GENMASK(29, 23)
+#define BLUEFIELD_UHS_REG_EXT_SAMPLE 2
+#define BLUEFIELD_UHS_REG_EXT_DRIVE 4
+
+static void dw_mci_bluefield_set_ios(struct dw_mci *host, struct mmc_ios *ios)
+{
+ u32 reg;
+
+ /* Update the Drive and Sample fields in register UHS_REG_EXT. */
+ reg = mci_readl(host, UHS_REG_EXT);
+ reg &= ~UHS_REG_EXT_SAMPLE_MASK;
+ reg |= FIELD_PREP(UHS_REG_EXT_SAMPLE_MASK,
+ BLUEFIELD_UHS_REG_EXT_SAMPLE);
+ reg &= ~UHS_REG_EXT_DRIVE_MASK;
+ reg |= FIELD_PREP(UHS_REG_EXT_DRIVE_MASK, BLUEFIELD_UHS_REG_EXT_DRIVE);
+ mci_writel(host, UHS_REG_EXT, reg);
+}
+
+static const struct dw_mci_drv_data bluefield_drv_data = {
+ .set_ios = dw_mci_bluefield_set_ios
+};
+
+static const struct of_device_id dw_mci_bluefield_match[] = {
+ { .compatible = "mellanox,bluefield-dw-mshc",
+ .data = &bluefield_drv_data },
+ {},
+};
+MODULE_DEVICE_TABLE(of, dw_mci_bluefield_match);
+
+static int dw_mci_bluefield_probe(struct platform_device *pdev)
+{
+ const struct dw_mci_drv_data *drv_data = NULL;
+ const struct of_device_id *match;
+
+ if (pdev->dev.of_node) {
+ match = of_match_node(dw_mci_bluefield_match,
+ pdev->dev.of_node);
+ drv_data = match->data;
+ }
+
+ return dw_mci_pltfm_register(pdev, drv_data);
+}
+
+static struct platform_driver dw_mci_bluefield_pltfm_driver = {
+ .probe = dw_mci_bluefield_probe,
+ .remove = dw_mci_pltfm_remove,
+ .driver = {
+ .name = "dwmmc_bluefield",
+ .of_match_table = dw_mci_bluefield_match,
+ .pm = &dw_mci_pltfm_pmops,
+ },
+};
+
+module_platform_driver(dw_mci_bluefield_pltfm_driver);
+
+MODULE_DESCRIPTION("BlueField DW Multimedia Card driver");
+MODULE_AUTHOR("Mellanox Technologies");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c
index 40d7de2eea12..8c86a800a8fd 100644
--- a/drivers/mmc/host/dw_mmc-rockchip.c
+++ b/drivers/mmc/host/dw_mmc-rockchip.c
@@ -44,9 +44,8 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios)
* bus_hz = cclkin / RK3288_CLKGEN_DIV
* ios->clock = (div == 0) ? bus_hz : (bus_hz / (2 * div))
*
- * Note: div can only be 0 or 1
- * if DDR50 8bit mode(only emmc work in 8bit mode),
- * div must be set 1
+ * Note: div can only be 0 or 1, but div must be set to 1 for eMMC
+ * DDR52 8-bit mode.
*/
if (ios->bus_width == MMC_BUS_WIDTH_8 &&
ios->timing == MMC_TIMING_MMC_DDR52)
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 29a1afa81f66..623f4d27fa01 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -1230,6 +1230,8 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
if (host->state == STATE_WAITING_CMD11_DONE)
sdmmc_cmd_bits |= SDMMC_CMD_VOLT_SWITCH;
+ slot->mmc->actual_clock = 0;
+
if (!clock) {
mci_writel(host, CLKENA, 0);
mci_send_cmd(slot, sdmmc_cmd_bits, 0);
@@ -1288,6 +1290,8 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
/* keep the last clock value that was requested from core */
slot->__clk_old = clock;
+ slot->mmc->actual_clock = div ? ((host->bus_hz / div) >> 1) :
+ host->bus_hz;
}
host->current_speed = clock;
diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c
index a0168e9e4fce..993386c9ea50 100644
--- a/drivers/mmc/host/jz4740_mmc.c
+++ b/drivers/mmc/host/jz4740_mmc.c
@@ -1,5 +1,7 @@
/*
* Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
+ * Copyright (C) 2013, Imagination Technologies
+ *
* JZ4740 SD/MMC controller driver
*
* This program is free software; you can redistribute it and/or modify it
@@ -13,24 +15,25 @@
*
*/
-#include <linux/mmc/host.h>
-#include <linux/mmc/slot-gpio.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
-#include <linux/interrupt.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/slot-gpio.h>
#include <linux/module.h>
+#include <linux/of_device.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
-#include <linux/delay.h>
#include <linux/scatterlist.h>
-#include <linux/clk.h>
-#include <linux/bitops.h>
-#include <linux/gpio.h>
#include <asm/cacheflush.h>
-#include <linux/dma-mapping.h>
-#include <linux/dmaengine.h>
#include <asm/mach-jz4740/dma.h>
#include <asm/mach-jz4740/jz4740_mmc.h>
@@ -51,6 +54,7 @@
#define JZ_REG_MMC_RESP_FIFO 0x34
#define JZ_REG_MMC_RXFIFO 0x38
#define JZ_REG_MMC_TXFIFO 0x3C
+#define JZ_REG_MMC_DMAC 0x44
#define JZ_MMC_STRPCL_EXIT_MULTIPLE BIT(7)
#define JZ_MMC_STRPCL_EXIT_TRANSFER BIT(6)
@@ -104,9 +108,17 @@
#define JZ_MMC_IRQ_PRG_DONE BIT(1)
#define JZ_MMC_IRQ_DATA_TRAN_DONE BIT(0)
+#define JZ_MMC_DMAC_DMA_SEL BIT(1)
+#define JZ_MMC_DMAC_DMA_EN BIT(0)
#define JZ_MMC_CLK_RATE 24000000
+enum jz4740_mmc_version {
+ JZ_MMC_JZ4740,
+ JZ_MMC_JZ4750,
+ JZ_MMC_JZ4780,
+};
+
enum jz4740_mmc_state {
JZ4740_MMC_STATE_READ_RESPONSE,
JZ4740_MMC_STATE_TRANSFER_DATA,
@@ -125,6 +137,8 @@ struct jz4740_mmc_host {
struct jz4740_mmc_platform_data *pdata;
struct clk *clk;
+ enum jz4740_mmc_version version;
+
int irq;
int card_detect_irq;
@@ -137,7 +151,7 @@ struct jz4740_mmc_host {
uint32_t cmdat;
- uint16_t irq_mask;
+ uint32_t irq_mask;
spinlock_t lock;
@@ -159,6 +173,32 @@ struct jz4740_mmc_host {
#define JZ4740_MMC_FIFO_HALF_SIZE 8
};
+static void jz4740_mmc_write_irq_mask(struct jz4740_mmc_host *host,
+ uint32_t val)
+{
+ if (host->version >= JZ_MMC_JZ4750)
+ return writel(val, host->base + JZ_REG_MMC_IMASK);
+ else
+ return writew(val, host->base + JZ_REG_MMC_IMASK);
+}
+
+static void jz4740_mmc_write_irq_reg(struct jz4740_mmc_host *host,
+ uint32_t val)
+{
+ if (host->version >= JZ_MMC_JZ4780)
+ return writel(val, host->base + JZ_REG_MMC_IREG);
+ else
+ return writew(val, host->base + JZ_REG_MMC_IREG);
+}
+
+static uint32_t jz4740_mmc_read_irq_reg(struct jz4740_mmc_host *host)
+{
+ if (host->version >= JZ_MMC_JZ4780)
+ return readl(host->base + JZ_REG_MMC_IREG);
+ else
+ return readw(host->base + JZ_REG_MMC_IREG);
+}
+
/*----------------------------------------------------------------------------*/
/* DMA infrastructure */
@@ -173,31 +213,23 @@ static void jz4740_mmc_release_dma_channels(struct jz4740_mmc_host *host)
static int jz4740_mmc_acquire_dma_channels(struct jz4740_mmc_host *host)
{
- dma_cap_mask_t mask;
-
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
-
- host->dma_tx = dma_request_channel(mask, NULL, host);
- if (!host->dma_tx) {
+ host->dma_tx = dma_request_chan(mmc_dev(host->mmc), "tx");
+ if (IS_ERR(host->dma_tx)) {
dev_err(mmc_dev(host->mmc), "Failed to get dma_tx channel\n");
- return -ENODEV;
+ return PTR_ERR(host->dma_tx);
}
- host->dma_rx = dma_request_channel(mask, NULL, host);
- if (!host->dma_rx) {
+ host->dma_rx = dma_request_chan(mmc_dev(host->mmc), "rx");
+ if (IS_ERR(host->dma_rx)) {
dev_err(mmc_dev(host->mmc), "Failed to get dma_rx channel\n");
- goto free_master_write;
+ dma_release_channel(host->dma_tx);
+ return PTR_ERR(host->dma_rx);
}
/* Initialize DMA pre request cookie */
host->next_data.cookie = 1;
return 0;
-
-free_master_write:
- dma_release_channel(host->dma_tx);
- return -ENODEV;
}
static inline struct dma_chan *jz4740_mmc_get_dma_chan(struct jz4740_mmc_host *host,
@@ -363,7 +395,7 @@ static void jz4740_mmc_set_irq_enabled(struct jz4740_mmc_host *host,
else
host->irq_mask |= irq;
- writew(host->irq_mask, host->base + JZ_REG_MMC_IMASK);
+ jz4740_mmc_write_irq_mask(host, host->irq_mask);
spin_unlock_irqrestore(&host->lock, flags);
}
@@ -415,10 +447,10 @@ static unsigned int jz4740_mmc_poll_irq(struct jz4740_mmc_host *host,
unsigned int irq)
{
unsigned int timeout = 0x800;
- uint16_t status;
+ uint32_t status;
do {
- status = readw(host->base + JZ_REG_MMC_IREG);
+ status = jz4740_mmc_read_irq_reg(host);
} while (!(status & irq) && --timeout);
if (timeout == 0) {
@@ -518,7 +550,7 @@ static bool jz4740_mmc_read_data(struct jz4740_mmc_host *host,
void __iomem *fifo_addr = host->base + JZ_REG_MMC_RXFIFO;
uint32_t *buf;
uint32_t d;
- uint16_t status;
+ uint32_t status;
size_t i, j;
unsigned int timeout;
@@ -654,8 +686,25 @@ static void jz4740_mmc_send_command(struct jz4740_mmc_host *host,
cmdat |= JZ_MMC_CMDAT_DATA_EN;
if (cmd->data->flags & MMC_DATA_WRITE)
cmdat |= JZ_MMC_CMDAT_WRITE;
- if (host->use_dma)
- cmdat |= JZ_MMC_CMDAT_DMA_EN;
+ if (host->use_dma) {
+ /*
+ * The 4780's MMC controller has integrated DMA ability
+ * in addition to being able to use the external DMA
+ * controller. It moves DMA control bits to a separate
+ * register. The DMA_SEL bit chooses the external
+ * controller over the integrated one. Earlier SoCs
+ * can only use the external controller, and have a
+ * single DMA enable bit in CMDAT.
+ */
+ if (host->version >= JZ_MMC_JZ4780) {
+ writel(JZ_MMC_DMAC_DMA_EN | JZ_MMC_DMAC_DMA_SEL,
+ host->base + JZ_REG_MMC_DMAC);
+ } else {
+ cmdat |= JZ_MMC_CMDAT_DMA_EN;
+ }
+ } else if (host->version >= JZ_MMC_JZ4780) {
+ writel(0, host->base + JZ_REG_MMC_DMAC);
+ }
writew(cmd->data->blksz, host->base + JZ_REG_MMC_BLKLEN);
writew(cmd->data->blocks, host->base + JZ_REG_MMC_NOB);
@@ -736,7 +785,7 @@ static irqreturn_t jz_mmc_irq_worker(int irq, void *devid)
host->state = JZ4740_MMC_STATE_SEND_STOP;
break;
}
- writew(JZ_MMC_IRQ_DATA_TRAN_DONE, host->base + JZ_REG_MMC_IREG);
+ jz4740_mmc_write_irq_reg(host, JZ_MMC_IRQ_DATA_TRAN_DONE);
case JZ4740_MMC_STATE_SEND_STOP:
if (!req->stop)
@@ -766,9 +815,10 @@ static irqreturn_t jz_mmc_irq(int irq, void *devid)
{
struct jz4740_mmc_host *host = devid;
struct mmc_command *cmd = host->cmd;
- uint16_t irq_reg, status, tmp;
+ uint32_t irq_reg, status, tmp;
- irq_reg = readw(host->base + JZ_REG_MMC_IREG);
+ status = readl(host->base + JZ_REG_MMC_STATUS);
+ irq_reg = jz4740_mmc_read_irq_reg(host);
tmp = irq_reg;
irq_reg &= ~host->irq_mask;
@@ -777,10 +827,10 @@ static irqreturn_t jz_mmc_irq(int irq, void *devid)
JZ_MMC_IRQ_PRG_DONE | JZ_MMC_IRQ_DATA_TRAN_DONE);
if (tmp != irq_reg)
- writew(tmp & ~irq_reg, host->base + JZ_REG_MMC_IREG);
+ jz4740_mmc_write_irq_reg(host, tmp & ~irq_reg);
if (irq_reg & JZ_MMC_IRQ_SDIO) {
- writew(JZ_MMC_IRQ_SDIO, host->base + JZ_REG_MMC_IREG);
+ jz4740_mmc_write_irq_reg(host, JZ_MMC_IRQ_SDIO);
mmc_signal_sdio_irq(host->mmc);
irq_reg &= ~JZ_MMC_IRQ_SDIO;
}
@@ -789,8 +839,6 @@ static irqreturn_t jz_mmc_irq(int irq, void *devid)
if (test_and_clear_bit(0, &host->waiting)) {
del_timer(&host->timeout_timer);
- status = readl(host->base + JZ_REG_MMC_STATUS);
-
if (status & JZ_MMC_STATUS_TIMEOUT_RES) {
cmd->error = -ETIMEDOUT;
} else if (status & JZ_MMC_STATUS_CRC_RES_ERR) {
@@ -803,7 +851,7 @@ static irqreturn_t jz_mmc_irq(int irq, void *devid)
}
jz4740_mmc_set_irq_enabled(host, irq_reg, false);
- writew(irq_reg, host->base + JZ_REG_MMC_IREG);
+ jz4740_mmc_write_irq_reg(host, irq_reg);
return IRQ_WAKE_THREAD;
}
@@ -818,7 +866,7 @@ static int jz4740_mmc_set_clock_rate(struct jz4740_mmc_host *host, int rate)
int real_rate;
jz4740_mmc_clock_disable(host);
- clk_set_rate(host->clk, JZ_MMC_CLK_RATE);
+ clk_set_rate(host->clk, host->mmc->f_max);
real_rate = clk_get_rate(host->clk);
@@ -837,9 +885,7 @@ static void jz4740_mmc_request(struct mmc_host *mmc, struct mmc_request *req)
host->req = req;
- writew(0xffff, host->base + JZ_REG_MMC_IREG);
-
- writew(JZ_MMC_IRQ_END_CMD_RES, host->base + JZ_REG_MMC_IREG);
+ jz4740_mmc_write_irq_reg(host, ~0);
jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_END_CMD_RES, true);
host->state = JZ4740_MMC_STATE_READ_RESPONSE;
@@ -857,7 +903,7 @@ static void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
switch (ios->power_mode) {
case MMC_POWER_UP:
jz4740_mmc_reset(host);
- if (gpio_is_valid(host->pdata->gpio_power))
+ if (host->pdata && gpio_is_valid(host->pdata->gpio_power))
gpio_set_value(host->pdata->gpio_power,
!host->pdata->power_active_low);
host->cmdat |= JZ_MMC_CMDAT_INIT;
@@ -866,7 +912,7 @@ static void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
case MMC_POWER_ON:
break;
default:
- if (gpio_is_valid(host->pdata->gpio_power))
+ if (host->pdata && gpio_is_valid(host->pdata->gpio_power))
gpio_set_value(host->pdata->gpio_power,
host->pdata->power_active_low);
clk_disable_unprepare(host->clk);
@@ -926,7 +972,7 @@ static int jz4740_mmc_request_gpio(struct device *dev, int gpio,
static int jz4740_mmc_request_gpios(struct mmc_host *mmc,
struct platform_device *pdev)
{
- struct jz4740_mmc_platform_data *pdata = pdev->dev.platform_data;
+ struct jz4740_mmc_platform_data *pdata = dev_get_platdata(&pdev->dev);
int ret = 0;
if (!pdata)
@@ -955,7 +1001,7 @@ static int jz4740_mmc_request_gpios(struct mmc_host *mmc,
static void jz4740_mmc_free_gpios(struct platform_device *pdev)
{
- struct jz4740_mmc_platform_data *pdata = pdev->dev.platform_data;
+ struct jz4740_mmc_platform_data *pdata = dev_get_platdata(&pdev->dev);
if (!pdata)
return;
@@ -964,14 +1010,22 @@ static void jz4740_mmc_free_gpios(struct platform_device *pdev)
gpio_free(pdata->gpio_power);
}
+static const struct of_device_id jz4740_mmc_of_match[] = {
+ { .compatible = "ingenic,jz4740-mmc", .data = (void *) JZ_MMC_JZ4740 },
+ { .compatible = "ingenic,jz4780-mmc", .data = (void *) JZ_MMC_JZ4780 },
+ {},
+};
+MODULE_DEVICE_TABLE(of, jz4740_mmc_of_match);
+
static int jz4740_mmc_probe(struct platform_device* pdev)
{
int ret;
struct mmc_host *mmc;
struct jz4740_mmc_host *host;
+ const struct of_device_id *match;
struct jz4740_mmc_platform_data *pdata;
- pdata = pdev->dev.platform_data;
+ pdata = dev_get_platdata(&pdev->dev);
mmc = mmc_alloc_host(sizeof(struct jz4740_mmc_host), &pdev->dev);
if (!mmc) {
@@ -982,6 +1036,27 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
host = mmc_priv(mmc);
host->pdata = pdata;
+ match = of_match_device(jz4740_mmc_of_match, &pdev->dev);
+ if (match) {
+ host->version = (enum jz4740_mmc_version)match->data;
+ ret = mmc_of_parse(mmc);
+ if (ret) {
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev,
+ "could not parse of data: %d\n", ret);
+ goto err_free_host;
+ }
+ } else {
+ /* JZ4740 should be the only one using legacy probe */
+ host->version = JZ_MMC_JZ4740;
+ mmc->caps |= MMC_CAP_SDIO_IRQ;
+ if (!(pdata && pdata->data_1bit))
+ mmc->caps |= MMC_CAP_4_BIT_DATA;
+ ret = jz4740_mmc_request_gpios(mmc, pdev);
+ if (ret)
+ goto err_free_host;
+ }
+
host->irq = platform_get_irq(pdev, 0);
if (host->irq < 0) {
ret = host->irq;
@@ -1004,16 +1079,11 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
goto err_free_host;
}
- ret = jz4740_mmc_request_gpios(mmc, pdev);
- if (ret)
- goto err_release_dma;
-
mmc->ops = &jz4740_mmc_ops;
- mmc->f_min = JZ_MMC_CLK_RATE / 128;
- mmc->f_max = JZ_MMC_CLK_RATE;
+ if (!mmc->f_max)
+ mmc->f_max = JZ_MMC_CLK_RATE;
+ mmc->f_min = mmc->f_max / 128;
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
- mmc->caps = (pdata && pdata->data_1bit) ? 0 : MMC_CAP_4_BIT_DATA;
- mmc->caps |= MMC_CAP_SDIO_IRQ;
mmc->max_blk_size = (1 << 10) - 1;
mmc->max_blk_count = (1 << 15) - 1;
@@ -1025,7 +1095,9 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
host->mmc = mmc;
host->pdev = pdev;
spin_lock_init(&host->lock);
- host->irq_mask = 0xffff;
+ host->irq_mask = ~0;
+
+ jz4740_mmc_reset(host);
ret = request_threaded_irq(host->irq, jz_mmc_irq, jz_mmc_irq_worker, 0,
dev_name(&pdev->dev), host);
@@ -1034,20 +1106,20 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
goto err_free_gpios;
}
- jz4740_mmc_reset(host);
jz4740_mmc_clock_disable(host);
timer_setup(&host->timeout_timer, jz4740_mmc_timeout, 0);
- host->use_dma = true;
- if (host->use_dma && jz4740_mmc_acquire_dma_channels(host) != 0)
- host->use_dma = false;
+ ret = jz4740_mmc_acquire_dma_channels(host);
+ if (ret == -EPROBE_DEFER)
+ goto err_free_irq;
+ host->use_dma = !ret;
platform_set_drvdata(pdev, host);
ret = mmc_add_host(mmc);
if (ret) {
dev_err(&pdev->dev, "Failed to add mmc host: %d\n", ret);
- goto err_free_irq;
+ goto err_release_dma;
}
dev_info(&pdev->dev, "JZ SD/MMC card driver registered\n");
@@ -1057,13 +1129,13 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
return 0;
+err_release_dma:
+ if (host->use_dma)
+ jz4740_mmc_release_dma_channels(host);
err_free_irq:
free_irq(host->irq, host);
err_free_gpios:
jz4740_mmc_free_gpios(pdev);
-err_release_dma:
- if (host->use_dma)
- jz4740_mmc_release_dma_channels(host);
err_free_host:
mmc_free_host(mmc);
@@ -1116,6 +1188,7 @@ static struct platform_driver jz4740_mmc_driver = {
.remove = jz4740_mmc_remove,
.driver = {
.name = "jz4740-mmc",
+ .of_match_table = of_match_ptr(jz4740_mmc_of_match),
.pm = JZ4740_MMC_PM_OPS,
},
};
diff --git a/drivers/mmc/host/meson-gx-mmc.c b/drivers/mmc/host/meson-gx-mmc.c
index 4f972b879fe6..c201c378537e 100644
--- a/drivers/mmc/host/meson-gx-mmc.c
+++ b/drivers/mmc/host/meson-gx-mmc.c
@@ -35,6 +35,7 @@
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
#include <linux/interrupt.h>
#include <linux/bitfield.h>
#include <linux/pinctrl/consumer.h>
@@ -47,15 +48,29 @@
#define CLK_CORE_PHASE_MASK GENMASK(9, 8)
#define CLK_TX_PHASE_MASK GENMASK(11, 10)
#define CLK_RX_PHASE_MASK GENMASK(13, 12)
-#define CLK_TX_DELAY_MASK GENMASK(19, 16)
-#define CLK_RX_DELAY_MASK GENMASK(23, 20)
+#define CLK_V2_TX_DELAY_MASK GENMASK(19, 16)
+#define CLK_V2_RX_DELAY_MASK GENMASK(23, 20)
+#define CLK_V2_ALWAYS_ON BIT(24)
+
+#define CLK_V3_TX_DELAY_MASK GENMASK(21, 16)
+#define CLK_V3_RX_DELAY_MASK GENMASK(27, 22)
+#define CLK_V3_ALWAYS_ON BIT(28)
+
#define CLK_DELAY_STEP_PS 200
#define CLK_PHASE_STEP 30
#define CLK_PHASE_POINT_NUM (360 / CLK_PHASE_STEP)
-#define CLK_ALWAYS_ON BIT(24)
+
+#define CLK_TX_DELAY_MASK(h) (h->data->tx_delay_mask)
+#define CLK_RX_DELAY_MASK(h) (h->data->rx_delay_mask)
+#define CLK_ALWAYS_ON(h) (h->data->always_on)
#define SD_EMMC_DELAY 0x4
#define SD_EMMC_ADJUST 0x8
+
+#define SD_EMMC_DELAY1 0x4
+#define SD_EMMC_DELAY2 0x8
+#define SD_EMMC_V3_ADJUST 0xc
+
#define SD_EMMC_CALOUT 0x10
#define SD_EMMC_START 0x40
#define START_DESC_INIT BIT(0)
@@ -122,6 +137,12 @@
#define MUX_CLK_NUM_PARENTS 2
+struct meson_mmc_data {
+ unsigned int tx_delay_mask;
+ unsigned int rx_delay_mask;
+ unsigned int always_on;
+};
+
struct sd_emmc_desc {
u32 cmd_cfg;
u32 cmd_arg;
@@ -131,6 +152,7 @@ struct sd_emmc_desc {
struct meson_host {
struct device *dev;
+ struct meson_mmc_data *data;
struct mmc_host *mmc;
struct mmc_command *cmd;
@@ -474,7 +496,7 @@ static int meson_mmc_clk_init(struct meson_host *host)
/* init SD_EMMC_CLOCK to sane defaults w/min clock rate */
clk_reg = 0;
- clk_reg |= CLK_ALWAYS_ON;
+ clk_reg |= CLK_ALWAYS_ON(host);
clk_reg |= CLK_DIV_MASK;
writel(clk_reg, host->regs + SD_EMMC_CLOCK);
@@ -574,7 +596,7 @@ static int meson_mmc_clk_init(struct meson_host *host)
tx->reg = host->regs + SD_EMMC_CLOCK;
tx->phase_mask = CLK_TX_PHASE_MASK;
- tx->delay_mask = CLK_TX_DELAY_MASK;
+ tx->delay_mask = CLK_TX_DELAY_MASK(host);
tx->delay_step_ps = CLK_DELAY_STEP_PS;
tx->hw.init = &init;
@@ -597,7 +619,7 @@ static int meson_mmc_clk_init(struct meson_host *host)
rx->reg = host->regs + SD_EMMC_CLOCK;
rx->phase_mask = CLK_RX_PHASE_MASK;
- rx->delay_mask = CLK_RX_DELAY_MASK;
+ rx->delay_mask = CLK_RX_DELAY_MASK(host);
rx->delay_step_ps = CLK_DELAY_STEP_PS;
rx->hw.init = &init;
@@ -1184,6 +1206,21 @@ static int meson_mmc_probe(struct platform_device *pdev)
goto free_host;
}
+ host->data = (struct meson_mmc_data *)
+ of_device_get_match_data(&pdev->dev);
+ if (!host->data) {
+ ret = -EINVAL;
+ goto free_host;
+ }
+
+ ret = device_reset_optional(&pdev->dev);
+ if (ret) {
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "device reset failed: %d\n", ret);
+
+ return ret;
+ }
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
host->regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(host->regs)) {
@@ -1315,11 +1352,24 @@ static int meson_mmc_remove(struct platform_device *pdev)
return 0;
}
+static const struct meson_mmc_data meson_gx_data = {
+ .tx_delay_mask = CLK_V2_TX_DELAY_MASK,
+ .rx_delay_mask = CLK_V2_RX_DELAY_MASK,
+ .always_on = CLK_V2_ALWAYS_ON,
+};
+
+static const struct meson_mmc_data meson_axg_data = {
+ .tx_delay_mask = CLK_V3_TX_DELAY_MASK,
+ .rx_delay_mask = CLK_V3_RX_DELAY_MASK,
+ .always_on = CLK_V3_ALWAYS_ON,
+};
+
static const struct of_device_id meson_mmc_of_match[] = {
- { .compatible = "amlogic,meson-gx-mmc", },
- { .compatible = "amlogic,meson-gxbb-mmc", },
- { .compatible = "amlogic,meson-gxl-mmc", },
- { .compatible = "amlogic,meson-gxm-mmc", },
+ { .compatible = "amlogic,meson-gx-mmc", .data = &meson_gx_data },
+ { .compatible = "amlogic,meson-gxbb-mmc", .data = &meson_gx_data },
+ { .compatible = "amlogic,meson-gxl-mmc", .data = &meson_gx_data },
+ { .compatible = "amlogic,meson-gxm-mmc", .data = &meson_gx_data },
+ { .compatible = "amlogic,meson-axg-mmc", .data = &meson_axg_data },
{}
};
MODULE_DEVICE_TABLE(of, meson_mmc_of_match);
@@ -1335,6 +1385,6 @@ static struct platform_driver meson_mmc_driver = {
module_platform_driver(meson_mmc_driver);
-MODULE_DESCRIPTION("Amlogic S905*/GX* SD/eMMC driver");
+MODULE_DESCRIPTION("Amlogic S905*/GX*/AXG SD/eMMC driver");
MODULE_AUTHOR("Kevin Hilman <khilman@baylibre.com>");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 70b0df8b9c78..f1849775e47e 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -1253,15 +1253,12 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id)
struct sg_mapping_iter *sg_miter = &host->sg_miter;
struct variant_data *variant = host->variant;
void __iomem *base = host->base;
- unsigned long flags;
u32 status;
status = readl(base + MMCISTATUS);
dev_dbg(mmc_dev(host->mmc), "irq1 (pio) %08x\n", status);
- local_irq_save(flags);
-
do {
unsigned int remain, len;
char *buffer;
@@ -1301,8 +1298,6 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id)
sg_miter_stop(sg_miter);
- local_irq_restore(flags);
-
/*
* If we have less than the fifo 'half-full' threshold to transfer,
* trigger a PIO interrupt as soon as any data is available.
diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
index cb274e822293..04841386b65d 100644
--- a/drivers/mmc/host/mtk-sd.c
+++ b/drivers/mmc/host/mtk-sd.c
@@ -19,6 +19,7 @@
#include <linux/ioport.h>
#include <linux/irq.h>
#include <linux/of_address.h>
+#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/pinctrl/consumer.h>
@@ -70,6 +71,7 @@
#define SDC_ADV_CFG0 0x64
#define EMMC_IOCON 0x7c
#define SDC_ACMD_RESP 0x80
+#define DMA_SA_H4BIT 0x8c
#define MSDC_DMA_SA 0x90
#define MSDC_DMA_CTRL 0x98
#define MSDC_DMA_CFG 0x9c
@@ -194,6 +196,9 @@
/* SDC_ADV_CFG0 mask */
#define SDC_RX_ENHANCE_EN (0x1 << 20) /* RW */
+/* DMA_SA_H4BIT mask */
+#define DMA_ADDR_HIGH_4BIT (0xf << 0) /* RW */
+
/* MSDC_DMA_CTRL mask */
#define MSDC_DMA_CTRL_START (0x1 << 0) /* W */
#define MSDC_DMA_CTRL_STOP (0x1 << 1) /* W */
@@ -227,6 +232,7 @@
#define MSDC_PATCH_BIT2_CFGRESP (0x1 << 15) /* RW */
#define MSDC_PATCH_BIT2_CFGCRCSTS (0x1 << 28) /* RW */
+#define MSDC_PB2_SUPPORT_64G (0x1 << 1) /* RW */
#define MSDC_PB2_RESPWAIT (0x3 << 2) /* RW */
#define MSDC_PB2_RESPSTSENSEL (0x7 << 16) /* RW */
#define MSDC_PB2_CRCSTSENSEL (0x7 << 29) /* RW */
@@ -280,6 +286,8 @@ struct mt_gpdma_desc {
#define GPDMA_DESC_BDP (0x1 << 1)
#define GPDMA_DESC_CHECKSUM (0xff << 8) /* bit8 ~ bit15 */
#define GPDMA_DESC_INT (0x1 << 16)
+#define GPDMA_DESC_NEXT_H4 (0xf << 24)
+#define GPDMA_DESC_PTR_H4 (0xf << 28)
u32 next;
u32 ptr;
u32 gpd_data_len;
@@ -296,6 +304,8 @@ struct mt_bdma_desc {
#define BDMA_DESC_CHECKSUM (0xff << 8) /* bit8 ~ bit15 */
#define BDMA_DESC_BLKPAD (0x1 << 17)
#define BDMA_DESC_DWPAD (0x1 << 18)
+#define BDMA_DESC_NEXT_H4 (0xf << 24)
+#define BDMA_DESC_PTR_H4 (0xf << 28)
u32 next;
u32 ptr;
u32 bd_data_len;
@@ -334,6 +344,7 @@ struct mtk_mmc_compatible {
bool busy_check;
bool stop_clk_fix;
bool enhance_rx;
+ bool support_64g;
};
struct msdc_tune_para {
@@ -403,6 +414,7 @@ static const struct mtk_mmc_compatible mt8135_compat = {
.busy_check = false,
.stop_clk_fix = false,
.enhance_rx = false,
+ .support_64g = false,
};
static const struct mtk_mmc_compatible mt8173_compat = {
@@ -414,6 +426,7 @@ static const struct mtk_mmc_compatible mt8173_compat = {
.busy_check = false,
.stop_clk_fix = false,
.enhance_rx = false,
+ .support_64g = false,
};
static const struct mtk_mmc_compatible mt2701_compat = {
@@ -425,6 +438,7 @@ static const struct mtk_mmc_compatible mt2701_compat = {
.busy_check = false,
.stop_clk_fix = false,
.enhance_rx = false,
+ .support_64g = false,
};
static const struct mtk_mmc_compatible mt2712_compat = {
@@ -436,6 +450,7 @@ static const struct mtk_mmc_compatible mt2712_compat = {
.busy_check = true,
.stop_clk_fix = true,
.enhance_rx = true,
+ .support_64g = true,
};
static const struct mtk_mmc_compatible mt7622_compat = {
@@ -447,6 +462,7 @@ static const struct mtk_mmc_compatible mt7622_compat = {
.busy_check = true,
.stop_clk_fix = true,
.enhance_rx = true,
+ .support_64g = false,
};
static const struct of_device_id msdc_of_ids[] = {
@@ -556,7 +572,12 @@ static inline void msdc_dma_setup(struct msdc_host *host, struct msdc_dma *dma,
/* init bd */
bd[j].bd_info &= ~BDMA_DESC_BLKPAD;
bd[j].bd_info &= ~BDMA_DESC_DWPAD;
- bd[j].ptr = (u32)dma_address;
+ bd[j].ptr = lower_32_bits(dma_address);
+ if (host->dev_comp->support_64g) {
+ bd[j].bd_info &= ~BDMA_DESC_PTR_H4;
+ bd[j].bd_info |= (upper_32_bits(dma_address) & 0xf)
+ << 28;
+ }
bd[j].bd_data_len &= ~BDMA_DESC_BUFLEN;
bd[j].bd_data_len |= (dma_len & BDMA_DESC_BUFLEN);
@@ -575,7 +596,10 @@ static inline void msdc_dma_setup(struct msdc_host *host, struct msdc_dma *dma,
dma_ctrl &= ~(MSDC_DMA_CTRL_BRUSTSZ | MSDC_DMA_CTRL_MODE);
dma_ctrl |= (MSDC_BURST_64B << 12 | 1 << 8);
writel_relaxed(dma_ctrl, host->base + MSDC_DMA_CTRL);
- writel((u32)dma->gpd_addr, host->base + MSDC_DMA_SA);
+ if (host->dev_comp->support_64g)
+ sdr_set_field(host->base + DMA_SA_H4BIT, DMA_ADDR_HIGH_4BIT,
+ upper_32_bits(dma->gpd_addr) & 0xf);
+ writel(lower_32_bits(dma->gpd_addr), host->base + MSDC_DMA_SA);
}
static void msdc_prepare_data(struct msdc_host *host, struct mmc_request *mrq)
@@ -1366,6 +1390,9 @@ static void msdc_init_hw(struct msdc_host *host)
MSDC_PATCH_BIT2_CFGCRCSTS);
}
+ if (host->dev_comp->support_64g)
+ sdr_set_bits(host->base + MSDC_PATCH_BIT2,
+ MSDC_PB2_SUPPORT_64G);
if (host->dev_comp->data_tune) {
sdr_set_bits(host->base + tune_reg,
MSDC_PAD_TUNE_RD_SEL | MSDC_PAD_TUNE_CMD_SEL);
@@ -1407,19 +1434,32 @@ static void msdc_init_gpd_bd(struct msdc_host *host, struct msdc_dma *dma)
{
struct mt_gpdma_desc *gpd = dma->gpd;
struct mt_bdma_desc *bd = dma->bd;
+ dma_addr_t dma_addr;
int i;
memset(gpd, 0, sizeof(struct mt_gpdma_desc) * 2);
+ dma_addr = dma->gpd_addr + sizeof(struct mt_gpdma_desc);
gpd->gpd_info = GPDMA_DESC_BDP; /* hwo, cs, bd pointer */
- gpd->ptr = (u32)dma->bd_addr; /* physical address */
/* gpd->next is must set for desc DMA
* That's why must alloc 2 gpd structure.
*/
- gpd->next = (u32)dma->gpd_addr + sizeof(struct mt_gpdma_desc);
+ gpd->next = lower_32_bits(dma_addr);
+ if (host->dev_comp->support_64g)
+ gpd->gpd_info |= (upper_32_bits(dma_addr) & 0xf) << 24;
+
+ dma_addr = dma->bd_addr;
+ gpd->ptr = lower_32_bits(dma->bd_addr); /* physical address */
+ if (host->dev_comp->support_64g)
+ gpd->gpd_info |= (upper_32_bits(dma_addr) & 0xf) << 28;
+
memset(bd, 0, sizeof(struct mt_bdma_desc) * MAX_BD_NUM);
- for (i = 0; i < (MAX_BD_NUM - 1); i++)
- bd[i].next = (u32)dma->bd_addr + sizeof(*bd) * (i + 1);
+ for (i = 0; i < (MAX_BD_NUM - 1); i++) {
+ dma_addr = dma->bd_addr + sizeof(*bd) * (i + 1);
+ bd[i].next = lower_32_bits(dma_addr);
+ if (host->dev_comp->support_64g)
+ bd[i].bd_info |= (upper_32_bits(dma_addr) & 0xf) << 24;
+ }
}
static void msdc_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
@@ -1820,7 +1860,6 @@ static int msdc_drv_probe(struct platform_device *pdev)
struct mmc_host *mmc;
struct msdc_host *host;
struct resource *res;
- const struct of_device_id *of_id;
int ret;
if (!pdev->dev.of_node) {
@@ -1828,9 +1867,6 @@ static int msdc_drv_probe(struct platform_device *pdev)
return -EINVAL;
}
- of_id = of_match_node(msdc_of_ids, pdev->dev.of_node);
- if (!of_id)
- return -EINVAL;
/* Allocate MMC host for this device */
mmc = mmc_alloc_host(sizeof(struct msdc_host), &pdev->dev);
if (!mmc)
@@ -1899,7 +1935,7 @@ static int msdc_drv_probe(struct platform_device *pdev)
msdc_of_property_parse(pdev, host);
host->dev = &pdev->dev;
- host->dev_comp = of_id->data;
+ host->dev_comp = of_device_get_match_data(&pdev->dev);
host->mmc = mmc;
host->src_clk_freq = clk_get_rate(host->src_clk);
/* Set host parameters to mmc */
@@ -1916,7 +1952,10 @@ static int msdc_drv_probe(struct platform_device *pdev)
mmc->max_blk_size = 2048;
mmc->max_req_size = 512 * 1024;
mmc->max_blk_count = mmc->max_req_size / 512;
- host->dma_mask = DMA_BIT_MASK(32);
+ if (host->dev_comp->support_64g)
+ host->dma_mask = DMA_BIT_MASK(36);
+ else
+ host->dma_mask = DMA_BIT_MASK(32);
mmc_dev(mmc)->dma_mask = &host->dma_mask;
host->timeout_clks = 3 * 1048576;
diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c
index 210247b3d11a..e22bbff89c8d 100644
--- a/drivers/mmc/host/mvsdio.c
+++ b/drivers/mmc/host/mvsdio.c
@@ -143,6 +143,7 @@ static void mvsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
struct mmc_command *cmd = mrq->cmd;
u32 cmdreg = 0, xfer = 0, intr = 0;
unsigned long flags;
+ unsigned int timeout;
BUG_ON(host->mrq != NULL);
host->mrq = mrq;
@@ -234,7 +235,8 @@ static void mvsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
mvsd_write(MVSD_NOR_INTR_EN, host->intr_en);
mvsd_write(MVSD_ERR_INTR_EN, 0xffff);
- mod_timer(&host->timer, jiffies + 5 * HZ);
+ timeout = cmd->busy_timeout ? cmd->busy_timeout : 5000;
+ mod_timer(&host->timer, jiffies + msecs_to_jiffies(timeout));
spin_unlock_irqrestore(&host->lock, flags);
}
@@ -755,6 +757,8 @@ 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_ioremap_resource(&pdev->dev, r);
diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c
index 5ff8ef7223cc..75f781c11e89 100644
--- a/drivers/mmc/host/mxcmmc.c
+++ b/drivers/mmc/host/mxcmmc.c
@@ -21,6 +21,7 @@
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
+#include <linux/highmem.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/blkdev.h>
@@ -291,8 +292,10 @@ static void mxcmci_swap_buffers(struct mmc_data *data)
struct scatterlist *sg;
int i;
- for_each_sg(data->sg, sg, data->sg_len, i)
- buffer_swap32(sg_virt(sg), sg->length);
+ for_each_sg(data->sg, sg, data->sg_len, i) {
+ void *buf = kmap_atomic(sg_page(sg) + sg->offset;
+ buffer_swap32(buf, sg->length);
+ kunmap_atomic(buf);
}
#else
static inline void mxcmci_swap_buffers(struct mmc_data *data) {}
@@ -609,6 +612,7 @@ static int mxcmci_transfer_data(struct mxcmci_host *host)
{
struct mmc_data *data = host->req->data;
struct scatterlist *sg;
+ void *buf;
int stat, i;
host->data = data;
@@ -616,14 +620,18 @@ static int mxcmci_transfer_data(struct mxcmci_host *host)
if (data->flags & MMC_DATA_READ) {
for_each_sg(data->sg, sg, data->sg_len, i) {
- stat = mxcmci_pull(host, sg_virt(sg), sg->length);
+ buf = kmap_atomic(sg_page(sg) + sg->offset);
+ stat = mxcmci_pull(host, buf, sg->length);
+ kunmap(buf);
if (stat)
return stat;
host->datasize += sg->length;
}
} else {
for_each_sg(data->sg, sg, data->sg_len, i) {
- stat = mxcmci_push(host, sg_virt(sg), sg->length);
+ buf = kmap_atomic(sg_page(sg) + sg->offset);
+ stat = mxcmci_push(host, buf, sg->length);
+ kunmap(buf);
if (stat)
return stat;
host->datasize += sg->length;
@@ -1206,7 +1214,8 @@ static int mxcmci_remove(struct platform_device *pdev)
return 0;
}
-static int __maybe_unused mxcmci_suspend(struct device *dev)
+#ifdef CONFIG_PM_SLEEP
+static int mxcmci_suspend(struct device *dev)
{
struct mmc_host *mmc = dev_get_drvdata(dev);
struct mxcmci_host *host = mmc_priv(mmc);
@@ -1216,7 +1225,7 @@ static int __maybe_unused mxcmci_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused mxcmci_resume(struct device *dev)
+static int mxcmci_resume(struct device *dev)
{
struct mmc_host *mmc = dev_get_drvdata(dev);
struct mxcmci_host *host = mmc_priv(mmc);
@@ -1232,6 +1241,7 @@ static int __maybe_unused mxcmci_resume(struct device *dev)
return ret;
}
+#endif
static SIMPLE_DEV_PM_OPS(mxcmci_pm_ops, mxcmci_suspend, mxcmci_resume);
diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c
index 51e01f03fb99..45c015da2e75 100644
--- a/drivers/mmc/host/renesas_sdhi_core.c
+++ b/drivers/mmc/host/renesas_sdhi_core.c
@@ -28,6 +28,7 @@
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/mmc/host.h>
+#include <linux/mmc/slot-gpio.h>
#include <linux/mfd/tmio.h>
#include <linux/sh_dma.h>
#include <linux/delay.h>
@@ -534,6 +535,10 @@ int renesas_sdhi_probe(struct platform_device *pdev,
host->multi_io_quirk = renesas_sdhi_multi_io_quirk;
host->dma_ops = dma_ops;
+ /* For some SoC, we disable internal WP. GPIO may override this */
+ if (mmc_can_gpio_ro(host->mmc))
+ mmc_data->capabilities2 &= ~MMC_CAP2_NO_WRITE_PROTECT;
+
/* SDR speeds are only available on Gen2+ */
if (mmc_data->flags & TMIO_MMC_MIN_RCAR2) {
/* card_busy caused issues on r8a73a4 (pre-Gen2) CD-less SDHI */
diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
index 6af946d16d24..f7f9773d161f 100644
--- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c
+++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
@@ -87,11 +87,12 @@ static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = {
TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2,
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
MMC_CAP_CMD23,
+ .capabilities2 = MMC_CAP2_NO_WRITE_PROTECT,
.bus_shift = 2,
.scc_offset = 0x1000,
.taps = rcar_gen3_scc_taps,
.taps_num = ARRAY_SIZE(rcar_gen3_scc_taps),
- /* Gen3 SDHI DMAC can handle 0xffffffff blk count, but seg = 1 */
+ /* DMAC can handle 0xffffffff blk count but only 1 segment */
.max_blk_count = 0xffffffff,
.max_segs = 1,
};
@@ -157,38 +158,34 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host,
{
struct scatterlist *sg = host->sg_ptr;
u32 dtran_mode = DTRAN_MODE_BUS_WID_TH | DTRAN_MODE_ADDR_MODE;
- enum dma_data_direction dir;
- int ret;
- /* This DMAC cannot handle if sg_len is not 1 */
- WARN_ON(host->sg_len > 1);
+ if (!dma_map_sg(&host->pdev->dev, sg, host->sg_len,
+ mmc_get_dma_dir(data)))
+ goto force_pio;
/* This DMAC cannot handle if buffer is not 8-bytes alignment */
- if (!IS_ALIGNED(sg->offset, 8))
+ if (!IS_ALIGNED(sg_dma_address(sg), 8)) {
+ dma_unmap_sg(&host->pdev->dev, sg, host->sg_len,
+ mmc_get_dma_dir(data));
goto force_pio;
+ }
if (data->flags & MMC_DATA_READ) {
dtran_mode |= DTRAN_MODE_CH_NUM_CH1;
- dir = DMA_FROM_DEVICE;
if (test_bit(SDHI_INTERNAL_DMAC_ONE_RX_ONLY, &global_flags) &&
test_and_set_bit(SDHI_INTERNAL_DMAC_RX_IN_USE, &global_flags))
goto force_pio;
} else {
dtran_mode |= DTRAN_MODE_CH_NUM_CH0;
- dir = DMA_TO_DEVICE;
}
- ret = dma_map_sg(&host->pdev->dev, sg, host->sg_len, dir);
- if (ret == 0)
- goto force_pio;
-
renesas_sdhi_internal_dmac_enable_dma(host, true);
/* set dma parameters */
renesas_sdhi_internal_dmac_dm_write(host, DM_CM_DTRAN_MODE,
dtran_mode);
renesas_sdhi_internal_dmac_dm_write(host, DM_DTRAN_ADDR,
- sg->dma_address);
+ sg_dma_address(sg));
return;
@@ -272,12 +269,17 @@ static const struct tmio_mmc_dma_ops renesas_sdhi_internal_dmac_dma_ops = {
* implementation as others may use a different implementation.
*/
static const struct soc_device_attribute gen3_soc_whitelist[] = {
+ /* specific ones */
{ .soc_id = "r8a7795", .revision = "ES1.*",
.data = (void *)BIT(SDHI_INTERNAL_DMAC_ONE_RX_ONLY) },
- { .soc_id = "r8a7795", .revision = "ES2.0" },
{ .soc_id = "r8a7796", .revision = "ES1.0",
.data = (void *)BIT(SDHI_INTERNAL_DMAC_ONE_RX_ONLY) },
- { .soc_id = "r8a77995", .revision = "ES1.0" },
+ /* generic ones */
+ { .soc_id = "r8a7795" },
+ { .soc_id = "r8a7796" },
+ { .soc_id = "r8a77965" },
+ { .soc_id = "r8a77980" },
+ { .soc_id = "r8a77995" },
{ /* sentinel */ }
};
diff --git a/drivers/mmc/host/renesas_sdhi_sys_dmac.c b/drivers/mmc/host/renesas_sdhi_sys_dmac.c
index 848e50c1638a..4bb46c489d71 100644
--- a/drivers/mmc/host/renesas_sdhi_sys_dmac.c
+++ b/drivers/mmc/host/renesas_sdhi_sys_dmac.c
@@ -42,6 +42,7 @@ static const struct renesas_sdhi_of_data of_rz_compatible = {
static const struct renesas_sdhi_of_data of_rcar_gen1_compatible = {
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL,
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ,
+ .capabilities2 = MMC_CAP2_NO_WRITE_PROTECT,
};
/* Definitions for sampling clocks */
@@ -61,6 +62,7 @@ static const struct renesas_sdhi_of_data of_rcar_gen2_compatible = {
TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2,
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
MMC_CAP_CMD23,
+ .capabilities2 = MMC_CAP2_NO_WRITE_PROTECT,
.dma_buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES,
.dma_rx_offset = 0x2000,
.scc_offset = 0x0300,
@@ -81,6 +83,7 @@ static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = {
TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2,
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
MMC_CAP_CMD23,
+ .capabilities2 = MMC_CAP2_NO_WRITE_PROTECT,
.bus_shift = 2,
.scc_offset = 0x1000,
.taps = rcar_gen3_scc_taps,
diff --git a/drivers/mmc/host/rtsx_usb_sdmmc.c b/drivers/mmc/host/rtsx_usb_sdmmc.c
index 78422079ecfa..9a3ff22dd0fe 100644
--- a/drivers/mmc/host/rtsx_usb_sdmmc.c
+++ b/drivers/mmc/host/rtsx_usb_sdmmc.c
@@ -26,7 +26,6 @@
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/sd.h>
-#include <linux/mmc/sdio.h>
#include <linux/mmc/card.h>
#include <linux/scatterlist.h>
#include <linux/pm_runtime.h>
@@ -343,7 +342,7 @@ static void sd_send_cmd_get_rsp(struct rtsx_usb_sdmmc *host,
}
if (rsp_type == SD_RSP_TYPE_R1b)
- timeout = 3000;
+ timeout = cmd->busy_timeout ? cmd->busy_timeout : 3000;
if (cmd->opcode == SD_SWITCH_VOLTAGE) {
err = rtsx_usb_write_register(ucr, SD_BUS_STAT,
@@ -839,17 +838,6 @@ static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
goto finish_detect_card;
}
- /*
- * Reject SDIO CMDs to speed up card identification
- * since unsupported
- */
- if (cmd->opcode == SD_IO_SEND_OP_COND ||
- cmd->opcode == SD_IO_RW_DIRECT ||
- cmd->opcode == SD_IO_RW_EXTENDED) {
- cmd->error = -EINVAL;
- goto finish;
- }
-
mutex_lock(&ucr->dev_mutex);
mutex_lock(&host->host_mutex);
@@ -1332,8 +1320,9 @@ 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_NEEDS_POLL;
- mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE;
+ MMC_CAP_NEEDS_POLL | MMC_CAP_ERASE;
+ mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE |
+ MMC_CAP2_NO_SDIO;
mmc->max_current_330 = 400;
mmc->max_current_180 = 800;
diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c
index 11ca95c60bcf..bdbd4897c0f7 100644
--- a/drivers/mmc/host/sdhci-bcm-kona.c
+++ b/drivers/mmc/host/sdhci-bcm-kona.c
@@ -284,10 +284,8 @@ static int sdhci_bcm_kona_probe(struct platform_device *pdev)
sdhci_bcm_kona_sd_init(host);
ret = sdhci_add_host(host);
- if (ret) {
- dev_err(dev, "Failed sdhci_add_host\n");
+ if (ret)
goto err_reset;
- }
/* if device is eMMC, emulate card insert right here */
if (!mmc_card_is_removable(host->mmc)) {
diff --git a/drivers/mmc/host/sdhci-cadence.c b/drivers/mmc/host/sdhci-cadence.c
index 0f589e26ee63..7a343b87b5e5 100644
--- a/drivers/mmc/host/sdhci-cadence.c
+++ b/drivers/mmc/host/sdhci-cadence.c
@@ -253,6 +253,7 @@ static int sdhci_cdns_set_tune_val(struct sdhci_host *host, unsigned int val)
struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
void __iomem *reg = priv->hrs_addr + SDHCI_CDNS_HRS06;
u32 tmp;
+ int i, ret;
if (WARN_ON(!FIELD_FIT(SDHCI_CDNS_HRS06_TUNE, val)))
return -EINVAL;
@@ -260,11 +261,24 @@ static int sdhci_cdns_set_tune_val(struct sdhci_host *host, unsigned int val)
tmp = readl(reg);
tmp &= ~SDHCI_CDNS_HRS06_TUNE;
tmp |= FIELD_PREP(SDHCI_CDNS_HRS06_TUNE, val);
- tmp |= SDHCI_CDNS_HRS06_TUNE_UP;
- writel(tmp, reg);
- return readl_poll_timeout(reg, tmp, !(tmp & SDHCI_CDNS_HRS06_TUNE_UP),
- 0, 1);
+ /*
+ * Workaround for IP errata:
+ * The IP6116 SD/eMMC PHY design has a timing issue on receive data
+ * path. Send tune request twice.
+ */
+ for (i = 0; i < 2; i++) {
+ tmp |= SDHCI_CDNS_HRS06_TUNE_UP;
+ writel(tmp, reg);
+
+ ret = readl_poll_timeout(reg, tmp,
+ !(tmp & SDHCI_CDNS_HRS06_TUNE_UP),
+ 0, 1);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
}
static int sdhci_cdns_execute_tuning(struct mmc_host *mmc, u32 opcode)
diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index cd2b5f643a15..d6aef70d34fa 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -41,6 +41,12 @@
#define ESDHC_VENDOR_SPEC_FRC_SDCLK_ON (1 << 8)
#define ESDHC_WTMK_LVL 0x44
#define ESDHC_WTMK_DEFAULT_VAL 0x10401040
+#define ESDHC_WTMK_LVL_RD_WML_MASK 0x000000FF
+#define ESDHC_WTMK_LVL_RD_WML_SHIFT 0
+#define ESDHC_WTMK_LVL_WR_WML_MASK 0x00FF0000
+#define ESDHC_WTMK_LVL_WR_WML_SHIFT 16
+#define ESDHC_WTMK_LVL_WML_VAL_DEF 64
+#define ESDHC_WTMK_LVL_WML_VAL_MAX 128
#define ESDHC_MIX_CTRL 0x48
#define ESDHC_MIX_CTRL_DDREN (1 << 3)
#define ESDHC_MIX_CTRL_AC23EN (1 << 7)
@@ -516,6 +522,7 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
}
if (esdhc_is_usdhc(imx_data)) {
+ u32 wml;
u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
/* Swap AC23 bit */
if (val & SDHCI_TRNS_AUTO_CMD23) {
@@ -524,6 +531,21 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
}
m = val | (m & ~ESDHC_MIX_CTRL_SDHCI_MASK);
writel(m, host->ioaddr + ESDHC_MIX_CTRL);
+
+ /* Set watermark levels for PIO access to maximum value
+ * (128 words) to accommodate full 512 bytes buffer.
+ * For DMA access restore the levels to default value.
+ */
+ m = readl(host->ioaddr + ESDHC_WTMK_LVL);
+ if (val & SDHCI_TRNS_DMA)
+ wml = ESDHC_WTMK_LVL_WML_VAL_DEF;
+ else
+ wml = ESDHC_WTMK_LVL_WML_VAL_MAX;
+ m &= ~(ESDHC_WTMK_LVL_RD_WML_MASK |
+ ESDHC_WTMK_LVL_WR_WML_MASK);
+ m |= (wml << ESDHC_WTMK_LVL_RD_WML_SHIFT) |
+ (wml << ESDHC_WTMK_LVL_WR_WML_SHIFT);
+ writel(m, host->ioaddr + ESDHC_WTMK_LVL);
} else {
/*
* Postpone this write, we must do it together with a
diff --git a/drivers/mmc/host/sdhci-iproc.c b/drivers/mmc/host/sdhci-iproc.c
index 0ef741bc515d..d0e83db42ae5 100644
--- a/drivers/mmc/host/sdhci-iproc.c
+++ b/drivers/mmc/host/sdhci-iproc.c
@@ -33,6 +33,8 @@ struct sdhci_iproc_host {
const struct sdhci_iproc_data *data;
u32 shadow_cmd;
u32 shadow_blk;
+ bool is_cmd_shadowed;
+ bool is_blk_shadowed;
};
#define REG_OFFSET_IN_BITS(reg) ((reg) << 3 & 0x18)
@@ -48,8 +50,22 @@ static inline u32 sdhci_iproc_readl(struct sdhci_host *host, int reg)
static u16 sdhci_iproc_readw(struct sdhci_host *host, int reg)
{
- u32 val = sdhci_iproc_readl(host, (reg & ~3));
- u16 word = val >> REG_OFFSET_IN_BITS(reg) & 0xffff;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_iproc_host *iproc_host = sdhci_pltfm_priv(pltfm_host);
+ u32 val;
+ u16 word;
+
+ if ((reg == SDHCI_TRANSFER_MODE) && iproc_host->is_cmd_shadowed) {
+ /* Get the saved transfer mode */
+ val = iproc_host->shadow_cmd;
+ } else if ((reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) &&
+ iproc_host->is_blk_shadowed) {
+ /* Get the saved block info */
+ val = iproc_host->shadow_blk;
+ } else {
+ val = sdhci_iproc_readl(host, (reg & ~3));
+ }
+ word = val >> REG_OFFSET_IN_BITS(reg) & 0xffff;
return word;
}
@@ -105,13 +121,15 @@ static void sdhci_iproc_writew(struct sdhci_host *host, u16 val, int reg)
if (reg == SDHCI_COMMAND) {
/* Write the block now as we are issuing a command */
- if (iproc_host->shadow_blk != 0) {
+ if (iproc_host->is_blk_shadowed) {
sdhci_iproc_writel(host, iproc_host->shadow_blk,
SDHCI_BLOCK_SIZE);
- iproc_host->shadow_blk = 0;
+ iproc_host->is_blk_shadowed = false;
}
oldval = iproc_host->shadow_cmd;
- } else if (reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) {
+ iproc_host->is_cmd_shadowed = false;
+ } else if ((reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) &&
+ iproc_host->is_blk_shadowed) {
/* Block size and count are stored in shadow reg */
oldval = iproc_host->shadow_blk;
} else {
@@ -123,9 +141,11 @@ static void sdhci_iproc_writew(struct sdhci_host *host, u16 val, int reg)
if (reg == SDHCI_TRANSFER_MODE) {
/* Save the transfer mode until the command is issued */
iproc_host->shadow_cmd = newval;
+ iproc_host->is_cmd_shadowed = true;
} else if (reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) {
/* Save the block info until the command is issued */
iproc_host->shadow_blk = newval;
+ iproc_host->is_blk_shadowed = true;
} else {
/* Command or other regular 32-bit write */
sdhci_iproc_writel(host, newval, reg & ~3);
@@ -166,7 +186,7 @@ static const struct sdhci_ops sdhci_iproc_32only_ops = {
static const struct sdhci_pltfm_data sdhci_iproc_cygnus_pltfm_data = {
.quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK,
- .quirks2 = SDHCI_QUIRK2_ACMD23_BROKEN,
+ .quirks2 = SDHCI_QUIRK2_ACMD23_BROKEN | SDHCI_QUIRK2_HOST_OFF_CARD_ON,
.ops = &sdhci_iproc_32only_ops,
};
@@ -206,7 +226,6 @@ static const struct sdhci_iproc_data iproc_data = {
.caps1 = SDHCI_DRIVER_TYPE_C |
SDHCI_DRIVER_TYPE_D |
SDHCI_SUPPORT_DDR50,
- .mmc_caps = MMC_CAP_1_8V_DDR,
};
static const struct sdhci_pltfm_data sdhci_bcm2835_pltfm_data = {
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index c283291db705..646bf377ba77 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -21,6 +21,7 @@
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/iopoll.h>
+#include <linux/regulator/consumer.h>
#include "sdhci-pltfm.h"
@@ -77,10 +78,16 @@
#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_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_VOLT_SUPPORT (CORE_3_0V_SUPPORT | CORE_1_8V_SUPPORT)
+
#define CORE_CSR_CDC_CTLR_CFG0 0x130
#define CORE_SW_TRIG_FULL_CALIB BIT(16)
#define CORE_HW_AUTOCAL_ENA BIT(17)
@@ -148,6 +155,7 @@ struct sdhci_msm_host {
u32 curr_io_level;
wait_queue_head_t pwr_irq_wait;
bool pwr_irq_flag;
+ u32 caps_0;
};
static unsigned int msm_get_clock_rate_for_bus_mode(struct sdhci_host *host,
@@ -1103,8 +1111,8 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq)
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
u32 irq_status, irq_ack = 0;
int retry = 10;
- int pwr_state = 0, io_level = 0;
-
+ u32 pwr_state = 0, io_level = 0;
+ u32 config;
irq_status = readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS);
irq_status &= INT_MASK;
@@ -1161,6 +1169,38 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq)
*/
writel_relaxed(irq_ack, msm_host->core_mem + CORE_PWRCTL_CTL);
+ /*
+ * If we don't have info regarding the voltage levels supported by
+ * regulators, don't change the IO PAD PWR SWITCH.
+ */
+ if (msm_host->caps_0 & CORE_VOLT_SUPPORT) {
+ u32 new_config;
+ /*
+ * We should unset IO PAD PWR switch only if the register write
+ * can set IO lines high and the regulator also switches to 3 V.
+ * Else, we should keep the IO PAD PWR switch set.
+ * This is applicable to certain targets where eMMC vccq supply
+ * is only 1.8V. In such targets, even during REQ_IO_HIGH, the
+ * IO PAD PWR switch must be kept set to reflect actual
+ * regulator voltage. This way, during initialization of
+ * controllers with only 1.8V, we will set the IO PAD bit
+ * without waiting for a REQ_IO_LOW.
+ */
+ config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
+ new_config = config;
+
+ if ((io_level & REQ_IO_HIGH) &&
+ (msm_host->caps_0 & CORE_3_0V_SUPPORT))
+ new_config &= ~CORE_IO_PAD_PWR_SWITCH;
+ else if ((io_level & REQ_IO_LOW) ||
+ (msm_host->caps_0 & CORE_1_8V_SUPPORT))
+ new_config |= CORE_IO_PAD_PWR_SWITCH;
+
+ if (config ^ new_config)
+ writel_relaxed(new_config,
+ host->ioaddr + CORE_VENDOR_SPEC);
+ }
+
if (pwr_state)
msm_host->curr_pwr_state = pwr_state;
if (io_level)
@@ -1313,6 +1353,45 @@ static void sdhci_msm_writeb(struct sdhci_host *host, u8 val, int reg)
sdhci_msm_check_power_status(host, req_type);
}
+static void sdhci_msm_set_regulator_caps(struct sdhci_msm_host *msm_host)
+{
+ struct mmc_host *mmc = msm_host->mmc;
+ struct regulator *supply = mmc->supply.vqmmc;
+ u32 caps = 0, config;
+ struct sdhci_host *host = mmc_priv(mmc);
+
+ if (!IS_ERR(mmc->supply.vqmmc)) {
+ if (regulator_is_supported_voltage(supply, 1700000, 1950000))
+ caps |= CORE_1_8V_SUPPORT;
+ if (regulator_is_supported_voltage(supply, 2700000, 3600000))
+ caps |= CORE_3_0V_SUPPORT;
+
+ if (!caps)
+ pr_warn("%s: 1.8/3V not supported for vqmmc\n",
+ mmc_hostname(mmc));
+ }
+
+ if (caps) {
+ /*
+ * Set the PAD_PWR_SWITCH_EN bit so that the PAD_PWR_SWITCH
+ * bit can be used as required later on.
+ */
+ u32 io_level = msm_host->curr_io_level;
+
+ config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
+ config |= CORE_IO_PAD_PWR_SWITCH_EN;
+
+ if ((io_level & REQ_IO_HIGH) && (caps & CORE_3_0V_SUPPORT))
+ config &= ~CORE_IO_PAD_PWR_SWITCH;
+ else if ((io_level & REQ_IO_LOW) || (caps & CORE_1_8V_SUPPORT))
+ config |= CORE_IO_PAD_PWR_SWITCH;
+
+ writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
+ }
+ msm_host->caps_0 |= caps;
+ pr_debug("%s: supported caps: 0x%08x\n", mmc_hostname(mmc), caps);
+}
+
static const struct of_device_id sdhci_msm_dt_match[] = {
{ .compatible = "qcom,sdhci-msm-v4" },
{},
@@ -1333,7 +1412,6 @@ static const struct sdhci_ops sdhci_msm_ops = {
static const struct sdhci_pltfm_data sdhci_msm_pdata = {
.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
- SDHCI_QUIRK_NO_CARD_NO_RESET |
SDHCI_QUIRK_SINGLE_POWER_WRITE |
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
@@ -1530,6 +1608,7 @@ static int sdhci_msm_probe(struct platform_device *pdev)
ret = sdhci_add_host(host);
if (ret)
goto pm_runtime_disable;
+ sdhci_msm_set_regulator_caps(msm_host);
pm_runtime_mark_last_busy(&pdev->dev);
pm_runtime_put_autosuspend(&pdev->dev);
diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c
index c33a5f7393bd..e3332a522a5d 100644
--- a/drivers/mmc/host/sdhci-of-arasan.c
+++ b/drivers/mmc/host/sdhci-of-arasan.c
@@ -290,7 +290,8 @@ 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_CLOCK_DIV_ZERO_BROKEN |
+ SDHCI_QUIRK2_STOP_WITH_TC,
};
static u32 sdhci_arasan_cqhci_irq(struct sdhci_host *host, u32 intmask)
@@ -359,8 +360,7 @@ static const struct sdhci_pltfm_data sdhci_arasan_cqe_pdata = {
*/
static int sdhci_arasan_suspend(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct sdhci_host *host = platform_get_drvdata(pdev);
+ struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
int ret;
@@ -403,8 +403,7 @@ static int sdhci_arasan_suspend(struct device *dev)
*/
static int sdhci_arasan_resume(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct sdhci_host *host = platform_get_drvdata(pdev);
+ struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
int ret;
diff --git a/drivers/mmc/host/sdhci-omap.c b/drivers/mmc/host/sdhci-omap.c
index 1456abd5eeb9..f3a7c8ece4be 100644
--- a/drivers/mmc/host/sdhci-omap.c
+++ b/drivers/mmc/host/sdhci-omap.c
@@ -26,6 +26,7 @@
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/pinctrl/consumer.h>
+#include <linux/sys_soc.h>
#include "sdhci-pltfm.h"
@@ -35,6 +36,7 @@
#define CON_DDR BIT(19)
#define CON_CLKEXTFREE BIT(16)
#define CON_PADEN BIT(15)
+#define CON_CTPL BIT(11)
#define CON_INIT BIT(1)
#define CON_OD BIT(0)
@@ -100,6 +102,7 @@ struct sdhci_omap_data {
};
struct sdhci_omap_host {
+ char *version;
void __iomem *base;
struct device *dev;
struct regulator *pbias;
@@ -224,6 +227,23 @@ static void sdhci_omap_conf_bus_power(struct sdhci_omap_host *omap_host,
}
}
+static void sdhci_omap_enable_sdio_irq(struct mmc_host *mmc, int enable)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
+ u32 reg;
+
+ reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
+ if (enable)
+ reg |= (CON_CTPL | CON_CLKEXTFREE);
+ else
+ reg &= ~(CON_CTPL | CON_CLKEXTFREE);
+ sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg);
+
+ sdhci_enable_sdio_irq(mmc, enable);
+}
+
static inline void sdhci_omap_set_dll(struct sdhci_omap_host *omap_host,
int count)
{
@@ -713,10 +733,15 @@ static const struct sdhci_pltfm_data sdhci_omap_pdata = {
SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC,
.quirks2 = SDHCI_QUIRK2_ACMD23_BROKEN |
SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
- SDHCI_QUIRK2_RSP_136_HAS_CRC,
+ SDHCI_QUIRK2_RSP_136_HAS_CRC |
+ SDHCI_QUIRK2_DISABLE_HW_TIMEOUT,
.ops = &sdhci_omap_ops,
};
+static const struct sdhci_omap_data k2g_data = {
+ .offset = 0x200,
+};
+
static const struct sdhci_omap_data dra7_data = {
.offset = 0x200,
.flags = SDHCI_OMAP_REQUIRE_IODELAY,
@@ -724,6 +749,7 @@ static const struct sdhci_omap_data dra7_data = {
static const struct of_device_id omap_sdhci_match[] = {
{ .compatible = "ti,dra7-sdhci", .data = &dra7_data },
+ { .compatible = "ti,k2g-sdhci", .data = &k2g_data },
{},
};
MODULE_DEVICE_TABLE(of, omap_sdhci_match);
@@ -733,12 +759,21 @@ static struct pinctrl_state
u32 *caps, u32 capmask)
{
struct device *dev = omap_host->dev;
+ char *version = omap_host->version;
struct pinctrl_state *pinctrl_state = ERR_PTR(-ENODEV);
+ char str[20];
if (!(*caps & capmask))
goto ret;
- pinctrl_state = pinctrl_lookup_state(omap_host->pinctrl, mode);
+ if (version) {
+ snprintf(str, 20, "%s-%s", mode, version);
+ pinctrl_state = pinctrl_lookup_state(omap_host->pinctrl, str);
+ }
+
+ if (IS_ERR(pinctrl_state))
+ pinctrl_state = pinctrl_lookup_state(omap_host->pinctrl, mode);
+
if (IS_ERR(pinctrl_state)) {
dev_err(dev, "no pinctrl state for %s mode", mode);
*caps &= ~capmask;
@@ -807,8 +842,15 @@ static int sdhci_omap_config_iodelay_pinctrl_state(struct sdhci_omap_host
state = sdhci_omap_iodelay_pinctrl_state(omap_host, "ddr_1_8v", caps,
MMC_CAP_1_8V_DDR);
- if (!IS_ERR(state))
+ if (!IS_ERR(state)) {
pinctrl_state[MMC_TIMING_MMC_DDR52] = state;
+ } else {
+ state = sdhci_omap_iodelay_pinctrl_state(omap_host, "ddr_3_3v",
+ caps,
+ MMC_CAP_3_3V_DDR);
+ if (!IS_ERR(state))
+ pinctrl_state[MMC_TIMING_MMC_DDR52] = state;
+ }
state = sdhci_omap_iodelay_pinctrl_state(omap_host, "hs", caps,
MMC_CAP_SD_HIGHSPEED);
@@ -830,6 +872,16 @@ static int sdhci_omap_config_iodelay_pinctrl_state(struct sdhci_omap_host
return 0;
}
+static const struct soc_device_attribute sdhci_omap_soc_devices[] = {
+ {
+ .machine = "DRA7[45]*",
+ .revision = "ES1.[01]",
+ },
+ {
+ /* sentinel */
+ }
+};
+
static int sdhci_omap_probe(struct platform_device *pdev)
{
int ret;
@@ -841,6 +893,7 @@ static int sdhci_omap_probe(struct platform_device *pdev)
struct mmc_host *mmc;
const struct of_device_id *match;
struct sdhci_omap_data *data;
+ const struct soc_device_attribute *soc;
match = of_match_device(omap_sdhci_match, dev);
if (!match)
@@ -871,10 +924,22 @@ static int sdhci_omap_probe(struct platform_device *pdev)
host->ioaddr += offset;
mmc = host->mmc;
+ sdhci_get_of_property(pdev);
ret = mmc_of_parse(mmc);
if (ret)
goto err_pltfm_free;
+ soc = soc_device_match(sdhci_omap_soc_devices);
+ if (soc) {
+ omap_host->version = "rev11";
+ if (!strcmp(dev_name(dev), "4809c000.mmc"))
+ mmc->f_max = 96000000;
+ if (!strcmp(dev_name(dev), "480b4000.mmc"))
+ mmc->f_max = 48000000;
+ if (!strcmp(dev_name(dev), "480ad000.mmc"))
+ mmc->f_max = 48000000;
+ }
+
pltfm_host->clk = devm_clk_get(dev, "fck");
if (IS_ERR(pltfm_host->clk)) {
ret = PTR_ERR(pltfm_host->clk);
@@ -916,26 +981,31 @@ static int sdhci_omap_probe(struct platform_device *pdev)
goto err_put_sync;
}
- ret = sdhci_omap_config_iodelay_pinctrl_state(omap_host);
- if (ret)
- goto err_put_sync;
-
host->mmc_host_ops.get_ro = mmc_gpio_get_ro;
host->mmc_host_ops.start_signal_voltage_switch =
sdhci_omap_start_signal_voltage_switch;
host->mmc_host_ops.set_ios = sdhci_omap_set_ios;
host->mmc_host_ops.card_busy = sdhci_omap_card_busy;
host->mmc_host_ops.execute_tuning = sdhci_omap_execute_tuning;
+ host->mmc_host_ops.enable_sdio_irq = sdhci_omap_enable_sdio_irq;
- sdhci_read_caps(host);
- host->caps |= SDHCI_CAN_DO_ADMA2;
-
- ret = sdhci_add_host(host);
+ ret = sdhci_setup_host(host);
if (ret)
goto err_put_sync;
+ ret = sdhci_omap_config_iodelay_pinctrl_state(omap_host);
+ if (ret)
+ goto err_cleanup_host;
+
+ ret = __sdhci_add_host(host);
+ if (ret)
+ goto err_cleanup_host;
+
return 0;
+err_cleanup_host:
+ sdhci_cleanup_host(host);
+
err_put_sync:
pm_runtime_put_sync(dev);
diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
index 78c25ad35fd2..77dd3521daae 100644
--- a/drivers/mmc/host/sdhci-pci-core.c
+++ b/drivers/mmc/host/sdhci-pci-core.c
@@ -453,6 +453,7 @@ static const struct sdhci_pci_fixes sdhci_intel_pch_sdio = {
enum {
INTEL_DSM_FNS = 0,
INTEL_DSM_V18_SWITCH = 3,
+ INTEL_DSM_V33_SWITCH = 4,
INTEL_DSM_DRV_STRENGTH = 9,
INTEL_DSM_D3_RETUNE = 10,
};
@@ -620,17 +621,37 @@ static void intel_hs400_enhanced_strobe(struct mmc_host *mmc,
sdhci_writel(host, val, INTEL_HS400_ES_REG);
}
-static void sdhci_intel_voltage_switch(struct sdhci_host *host)
+static int intel_start_signal_voltage_switch(struct mmc_host *mmc,
+ struct mmc_ios *ios)
{
+ struct device *dev = mmc_dev(mmc);
+ struct sdhci_host *host = mmc_priv(mmc);
struct sdhci_pci_slot *slot = sdhci_priv(host);
struct intel_host *intel_host = sdhci_pci_priv(slot);
- struct device *dev = &slot->chip->pdev->dev;
+ unsigned int fn;
u32 result = 0;
int err;
- err = intel_dsm(intel_host, dev, INTEL_DSM_V18_SWITCH, &result);
- pr_debug("%s: %s DSM error %d result %u\n",
- mmc_hostname(host->mmc), __func__, err, result);
+ err = sdhci_start_signal_voltage_switch(mmc, ios);
+ if (err)
+ return err;
+
+ switch (ios->signal_voltage) {
+ case MMC_SIGNAL_VOLTAGE_330:
+ fn = INTEL_DSM_V33_SWITCH;
+ break;
+ case MMC_SIGNAL_VOLTAGE_180:
+ fn = INTEL_DSM_V18_SWITCH;
+ break;
+ default:
+ return 0;
+ }
+
+ err = intel_dsm(intel_host, dev, fn, &result);
+ pr_debug("%s: %s DSM fn %u error %d result %u\n",
+ mmc_hostname(mmc), __func__, fn, err, result);
+
+ return 0;
}
static const struct sdhci_ops sdhci_intel_byt_ops = {
@@ -641,7 +662,6 @@ static const struct sdhci_ops sdhci_intel_byt_ops = {
.reset = sdhci_reset,
.set_uhs_signaling = sdhci_set_uhs_signaling,
.hw_reset = sdhci_pci_hw_reset,
- .voltage_switch = sdhci_intel_voltage_switch,
};
static const struct sdhci_ops sdhci_intel_glk_ops = {
@@ -652,7 +672,6 @@ static const struct sdhci_ops sdhci_intel_glk_ops = {
.reset = sdhci_reset,
.set_uhs_signaling = sdhci_set_uhs_signaling,
.hw_reset = sdhci_pci_hw_reset,
- .voltage_switch = sdhci_intel_voltage_switch,
.irq = sdhci_cqhci_irq,
};
@@ -691,6 +710,7 @@ static void byt_probe_slot(struct sdhci_pci_slot *slot)
byt_read_dsm(slot);
ops->execute_tuning = intel_execute_tuning;
+ ops->start_signal_voltage_switch = intel_start_signal_voltage_switch;
}
static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot)
@@ -832,6 +852,10 @@ static int byt_sd_probe_slot(struct sdhci_pci_slot *slot)
slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_GLK_SD)
slot->host->mmc_host_ops.get_cd = bxt_get_cd;
+ if (slot->chip->pdev->subsystem_vendor == PCI_VENDOR_ID_NI &&
+ slot->chip->pdev->subsystem_device == PCI_SUBDEVICE_ID_NI_78E3)
+ slot->host->mmc->caps2 |= MMC_CAP2_AVOID_3_3V;
+
return 0;
}
diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h
index 5cbcdc448f98..db9cb54ef700 100644
--- a/drivers/mmc/host/sdhci-pci.h
+++ b/drivers/mmc/host/sdhci-pci.h
@@ -54,6 +54,7 @@
#define PCI_DEVICE_ID_REALTEK_5250 0x5250
#define PCI_SUBDEVICE_ID_NI_7884 0x7884
+#define PCI_SUBDEVICE_ID_NI_78E3 0x78e3
#define PCI_VENDOR_ID_ARASAN 0x16e6
#define PCI_DEVICE_ID_ARASAN_PHY_EMMC 0x0670
diff --git a/drivers/mmc/host/sdhci-pic32.c b/drivers/mmc/host/sdhci-pic32.c
index a6caa49ca25a..a11e6397d4ff 100644
--- a/drivers/mmc/host/sdhci-pic32.c
+++ b/drivers/mmc/host/sdhci-pic32.c
@@ -200,10 +200,8 @@ static int pic32_sdhci_probe(struct platform_device *pdev)
}
ret = sdhci_add_host(host);
- if (ret) {
- dev_err(&pdev->dev, "error adding host\n");
+ if (ret)
goto err_base_clk;
- }
dev_info(&pdev->dev, "Successfully added sdhci host\n");
return 0;
diff --git a/drivers/mmc/host/sdhci-pxav2.c b/drivers/mmc/host/sdhci-pxav2.c
index 8986f9d9cf98..2c3827f54927 100644
--- a/drivers/mmc/host/sdhci-pxav2.c
+++ b/drivers/mmc/host/sdhci-pxav2.c
@@ -221,10 +221,8 @@ static int sdhci_pxav2_probe(struct platform_device *pdev)
host->ops = &pxav2_sdhci_ops;
ret = sdhci_add_host(host);
- if (ret) {
- dev_err(&pdev->dev, "failed to add host\n");
+ if (ret)
goto disable_clk;
- }
return 0;
diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c
index a34434166ca7..b8e96f392428 100644
--- a/drivers/mmc/host/sdhci-pxav3.c
+++ b/drivers/mmc/host/sdhci-pxav3.c
@@ -472,10 +472,8 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
pm_suspend_ignore_children(&pdev->dev, 1);
ret = sdhci_add_host(host);
- if (ret) {
- dev_err(&pdev->dev, "failed to add host\n");
+ if (ret)
goto err_add_host;
- }
if (host->mmc->pm_caps & MMC_PM_WAKE_SDIO_IRQ)
device_init_wakeup(&pdev->dev, 1);
diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c
index cda83ccb2702..9ef89d00970e 100644
--- a/drivers/mmc/host/sdhci-s3c.c
+++ b/drivers/mmc/host/sdhci-s3c.c
@@ -655,10 +655,8 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
goto err_req_regs;
ret = sdhci_add_host(host);
- if (ret) {
- dev_err(dev, "sdhci_add_host() failed\n");
+ if (ret)
goto err_req_regs;
- }
#ifdef CONFIG_PM
if (pdata->cd_type != S3C_SDHCI_CD_INTERNAL)
diff --git a/drivers/mmc/host/sdhci-spear.c b/drivers/mmc/host/sdhci-spear.c
index 14511526a3a8..9247d51f2eed 100644
--- a/drivers/mmc/host/sdhci-spear.c
+++ b/drivers/mmc/host/sdhci-spear.c
@@ -126,10 +126,8 @@ static int sdhci_probe(struct platform_device *pdev)
}
ret = sdhci_add_host(host);
- if (ret) {
- dev_dbg(&pdev->dev, "error adding host\n");
+ if (ret)
goto disable_clk;
- }
platform_set_drvdata(pdev, host);
diff --git a/drivers/mmc/host/sdhci-st.c b/drivers/mmc/host/sdhci-st.c
index c32daed0d418..8f95647195d9 100644
--- a/drivers/mmc/host/sdhci-st.c
+++ b/drivers/mmc/host/sdhci-st.c
@@ -422,10 +422,8 @@ static int sdhci_st_probe(struct platform_device *pdev)
st_mmcss_cconfig(np, host);
ret = sdhci_add_host(host);
- if (ret) {
- dev_err(&pdev->dev, "Failed sdhci_add_host\n");
+ if (ret)
goto err_out;
- }
host_version = readw_relaxed((host->ioaddr + SDHCI_HOST_VERSION));
diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
index b877c13184c2..970d38f68939 100644
--- a/drivers/mmc/host/sdhci-tegra.c
+++ b/drivers/mmc/host/sdhci-tegra.c
@@ -231,7 +231,7 @@ static void tegra_sdhci_set_uhs_signaling(struct sdhci_host *host,
if (timing == MMC_TIMING_UHS_DDR50)
tegra_host->ddr_signaling = true;
- return sdhci_set_uhs_signaling(host, timing);
+ sdhci_set_uhs_signaling(host, timing);
}
static unsigned int tegra_sdhci_get_max_clock(struct sdhci_host *host)
diff --git a/drivers/mmc/host/sdhci-xenon-phy.c b/drivers/mmc/host/sdhci-xenon-phy.c
index ec8794335241..a35804b203a7 100644
--- a/drivers/mmc/host/sdhci-xenon-phy.c
+++ b/drivers/mmc/host/sdhci-xenon-phy.c
@@ -814,15 +814,10 @@ static int xenon_add_phy(struct device_node *np, struct sdhci_host *host,
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
- int i, ret;
+ int ret;
- for (i = 0; i < NR_PHY_TYPES; i++) {
- if (!strcmp(phy_name, phy_types[i])) {
- priv->phy_type = i;
- break;
- }
- }
- if (i == NR_PHY_TYPES) {
+ priv->phy_type = match_string(phy_types, NR_PHY_TYPES, phy_name);
+ if (priv->phy_type < 0) {
dev_err(mmc_dev(host->mmc),
"Unable to determine PHY name %s. Use default eMMC 5.1 PHY\n",
phy_name);
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 2ededa7f43df..1c828e0e9905 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -709,29 +709,16 @@ static u32 sdhci_sdma_address(struct sdhci_host *host)
return sg_dma_address(host->data->sg);
}
-static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd)
+static unsigned int sdhci_target_timeout(struct sdhci_host *host,
+ struct mmc_command *cmd,
+ struct mmc_data *data)
{
- u8 count;
- struct mmc_data *data = cmd->data;
- unsigned target_timeout, current_timeout;
-
- /*
- * If the host controller provides us with an incorrect timeout
- * value, just skip the check and use 0xE. The hardware may take
- * longer to time out, but that's much better than having a too-short
- * timeout value.
- */
- if (host->quirks & SDHCI_QUIRK_BROKEN_TIMEOUT_VAL)
- return 0xE;
-
- /* Unspecified timeout, assume max */
- if (!data && !cmd->busy_timeout)
- return 0xE;
+ unsigned int target_timeout;
/* timeout in us */
- if (!data)
+ if (!data) {
target_timeout = cmd->busy_timeout * 1000;
- else {
+ } else {
target_timeout = DIV_ROUND_UP(data->timeout_ns, 1000);
if (host->clock && data->timeout_clks) {
unsigned long long val;
@@ -748,6 +735,67 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd)
}
}
+ return target_timeout;
+}
+
+static void sdhci_calc_sw_timeout(struct sdhci_host *host,
+ struct mmc_command *cmd)
+{
+ struct mmc_data *data = cmd->data;
+ struct mmc_host *mmc = host->mmc;
+ struct mmc_ios *ios = &mmc->ios;
+ unsigned char bus_width = 1 << ios->bus_width;
+ unsigned int blksz;
+ unsigned int freq;
+ u64 target_timeout;
+ u64 transfer_time;
+
+ target_timeout = sdhci_target_timeout(host, cmd, data);
+ target_timeout *= NSEC_PER_USEC;
+
+ if (data) {
+ blksz = data->blksz;
+ freq = host->mmc->actual_clock ? : host->clock;
+ transfer_time = (u64)blksz * NSEC_PER_SEC * (8 / bus_width);
+ do_div(transfer_time, freq);
+ /* multiply by '2' to account for any unknowns */
+ transfer_time = transfer_time * 2;
+ /* calculate timeout for the entire data */
+ host->data_timeout = data->blocks * target_timeout +
+ transfer_time;
+ } else {
+ host->data_timeout = target_timeout;
+ }
+
+ if (host->data_timeout)
+ host->data_timeout += MMC_CMD_TRANSFER_TIME;
+}
+
+static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd,
+ bool *too_big)
+{
+ u8 count;
+ struct mmc_data *data = cmd->data;
+ unsigned target_timeout, current_timeout;
+
+ *too_big = true;
+
+ /*
+ * If the host controller provides us with an incorrect timeout
+ * value, just skip the check and use 0xE. The hardware may take
+ * longer to time out, but that's much better than having a too-short
+ * timeout value.
+ */
+ if (host->quirks & SDHCI_QUIRK_BROKEN_TIMEOUT_VAL)
+ return 0xE;
+
+ /* Unspecified timeout, assume max */
+ if (!data && !cmd->busy_timeout)
+ return 0xE;
+
+ /* timeout in us */
+ target_timeout = sdhci_target_timeout(host, cmd, data);
+
/*
* Figure out needed cycles.
* We do this in steps in order to fit inside a 32 bit int.
@@ -768,9 +816,12 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd)
}
if (count >= 0xF) {
- DBG("Too large timeout 0x%x requested for CMD%d!\n",
- count, cmd->opcode);
+ if (!(host->quirks2 & SDHCI_QUIRK2_DISABLE_HW_TIMEOUT))
+ DBG("Too large timeout 0x%x requested for CMD%d!\n",
+ count, cmd->opcode);
count = 0xE;
+ } else {
+ *too_big = false;
}
return count;
@@ -790,6 +841,16 @@ static void sdhci_set_transfer_irqs(struct sdhci_host *host)
sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
}
+static void sdhci_set_data_timeout_irq(struct sdhci_host *host, bool enable)
+{
+ if (enable)
+ host->ier |= SDHCI_INT_DATA_TIMEOUT;
+ else
+ host->ier &= ~SDHCI_INT_DATA_TIMEOUT;
+ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
+ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
+}
+
static void sdhci_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
{
u8 count;
@@ -797,7 +858,18 @@ static void sdhci_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
if (host->ops->set_timeout) {
host->ops->set_timeout(host, cmd);
} else {
- count = sdhci_calc_timeout(host, cmd);
+ bool too_big = false;
+
+ count = sdhci_calc_timeout(host, cmd, &too_big);
+
+ if (too_big &&
+ host->quirks2 & SDHCI_QUIRK2_DISABLE_HW_TIMEOUT) {
+ sdhci_calc_sw_timeout(host, cmd);
+ sdhci_set_data_timeout_irq(host, false);
+ } else if (!(host->ier & SDHCI_INT_DATA_TIMEOUT)) {
+ sdhci_set_data_timeout_irq(host, true);
+ }
+
sdhci_writeb(host, count, SDHCI_TIMEOUT_CONTROL);
}
}
@@ -807,6 +879,8 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
u8 ctrl;
struct mmc_data *data = cmd->data;
+ host->data_timeout = 0;
+
if (sdhci_data_line_cmd(cmd))
sdhci_set_timeout(host, cmd);
@@ -1160,13 +1234,6 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
mdelay(1);
}
- timeout = jiffies;
- if (!cmd->data && cmd->busy_timeout > 9000)
- timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * HZ + HZ;
- else
- timeout += 10 * HZ;
- sdhci_mod_timer(host, cmd->mrq, timeout);
-
host->cmd = cmd;
if (sdhci_data_line_cmd(cmd)) {
WARN_ON(host->data_cmd);
@@ -1206,6 +1273,15 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200)
flags |= SDHCI_CMD_DATA;
+ timeout = jiffies;
+ if (host->data_timeout)
+ timeout += nsecs_to_jiffies(host->data_timeout);
+ else if (!cmd->data && cmd->busy_timeout > 9000)
+ timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * HZ + HZ;
+ else
+ timeout += 10 * HZ;
+ sdhci_mod_timer(host, cmd->mrq, timeout);
+
sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
}
EXPORT_SYMBOL_GPL(sdhci_send_command);
@@ -3616,6 +3692,10 @@ int sdhci_setup_host(struct sdhci_host *host)
mmc->max_busy_timeout /= host->timeout_clk;
}
+ if (host->quirks2 & SDHCI_QUIRK2_DISABLE_HW_TIMEOUT &&
+ !host->ops->get_max_timeout_count)
+ mmc->max_busy_timeout = 0;
+
mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23;
mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
@@ -3672,6 +3752,16 @@ int sdhci_setup_host(struct sdhci_host *host)
if (host->quirks2 & SDHCI_QUIRK2_NO_1_8_V) {
host->caps1 &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 |
SDHCI_SUPPORT_DDR50);
+ /*
+ * The SDHCI controller in a SoC might support HS200/HS400
+ * (indicated using mmc-hs200-1_8v/mmc-hs400-1_8v dt property),
+ * but if the board is modeled such that the IO lines are not
+ * connected to 1.8v then HS200/HS400 cannot be supported.
+ * Disable HS200/HS400 if the board does not have 1.8v connected
+ * to the IO lines. (Applicable for other modes in 1.8v)
+ */
+ mmc->caps2 &= ~(MMC_CAP2_HSX00_1_8V | MMC_CAP2_HS400_ES);
+ mmc->caps &= ~(MMC_CAP_1_8V_DDR | MMC_CAP_UHS);
}
/* Any UHS-I mode in caps implies SDR12 and SDR25 support. */
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index c95b0a4a7594..23966f887da6 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -332,6 +332,14 @@ struct sdhci_adma2_64_desc {
/* Allow for a a command request and a data request at the same time */
#define SDHCI_MAX_MRQS 2
+/*
+ * 48bit command and 136 bit response in 100KHz clock could take upto 2.48ms.
+ * However since the start time of the command, the time between
+ * command and response, and the time between response and start of data is
+ * not known, set the command transfer time to 10ms.
+ */
+#define MMC_CMD_TRANSFER_TIME (10 * NSEC_PER_MSEC) /* max 10 ms */
+
enum sdhci_cookie {
COOKIE_UNMAPPED,
COOKIE_PRE_MAPPED, /* mapped by sdhci_pre_req() */
@@ -437,6 +445,11 @@ struct sdhci_host {
#define SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN (1<<15)
/* Controller has CRC in 136 bit Command Response */
#define SDHCI_QUIRK2_RSP_136_HAS_CRC (1<<16)
+/*
+ * Disable HW timeout if the requested timeout is more than the maximum
+ * obtainable timeout.
+ */
+#define SDHCI_QUIRK2_DISABLE_HW_TIMEOUT (1<<17)
int irq; /* Device IRQ */
void __iomem *ioaddr; /* Mapped address */
@@ -550,6 +563,8 @@ struct sdhci_host {
/* Host SDMA buffer boundary. */
u32 sdma_boundary;
+ u64 data_timeout;
+
unsigned long private[0] ____cacheline_aligned;
};
diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c
index 20cfb20418f3..e7472590f2ed 100644
--- a/drivers/mmc/host/sunxi-mmc.c
+++ b/drivers/mmc/host/sunxi-mmc.c
@@ -13,36 +13,34 @@
* the License, or (at your option) any later version.
*/
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/io.h>
-#include <linux/device.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/err.h>
-
#include <linux/clk.h>
#include <linux/clk/sunxi-ng.h>
-#include <linux/gpio.h>
-#include <linux/platform_device.h>
-#include <linux/spinlock.h>
-#include <linux/scatterlist.h>
+#include <linux/delay.h>
+#include <linux/device.h>
#include <linux/dma-mapping.h>
-#include <linux/slab.h>
-#include <linux/reset.h>
-#include <linux/regulator/consumer.h>
-
-#include <linux/of_address.h>
-#include <linux/of_gpio.h>
-#include <linux/of_platform.h>
-
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/core.h>
#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
#include <linux/mmc/sd.h>
#include <linux/mmc/sdio.h>
-#include <linux/mmc/mmc.h>
-#include <linux/mmc/core.h>
-#include <linux/mmc/card.h>
#include <linux/mmc/slot-gpio.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
/* register offset definitions */
#define SDXC_REG_GCTRL (0x00) /* SMC Global Control Register */
@@ -322,10 +320,9 @@ static int sunxi_mmc_reset_host(struct sunxi_mmc_host *host)
return 0;
}
-static int sunxi_mmc_init_host(struct mmc_host *mmc)
+static int sunxi_mmc_init_host(struct sunxi_mmc_host *host)
{
u32 rval;
- struct sunxi_mmc_host *host = mmc_priv(mmc);
if (sunxi_mmc_reset_host(host))
return -EIO;
@@ -859,17 +856,48 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host,
return 0;
}
-static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+static void sunxi_mmc_set_bus_width(struct sunxi_mmc_host *host,
+ unsigned char width)
+{
+ switch (width) {
+ case MMC_BUS_WIDTH_1:
+ mmc_writel(host, REG_WIDTH, SDXC_WIDTH1);
+ break;
+ case MMC_BUS_WIDTH_4:
+ mmc_writel(host, REG_WIDTH, SDXC_WIDTH4);
+ break;
+ case MMC_BUS_WIDTH_8:
+ mmc_writel(host, REG_WIDTH, SDXC_WIDTH8);
+ break;
+ }
+}
+
+static void sunxi_mmc_set_clk(struct sunxi_mmc_host *host, struct mmc_ios *ios)
{
- struct sunxi_mmc_host *host = mmc_priv(mmc);
u32 rval;
- /* Set the power state */
- switch (ios->power_mode) {
- case MMC_POWER_ON:
- break;
+ /* set ddr mode */
+ rval = mmc_readl(host, REG_GCTRL);
+ if (ios->timing == MMC_TIMING_UHS_DDR50 ||
+ ios->timing == MMC_TIMING_MMC_DDR52)
+ rval |= SDXC_DDR_MODE;
+ else
+ rval &= ~SDXC_DDR_MODE;
+ mmc_writel(host, REG_GCTRL, rval);
+
+ host->ferror = sunxi_mmc_clk_set_rate(host, ios);
+ /* Android code had a usleep_range(50000, 55000); here */
+}
+static void sunxi_mmc_card_power(struct sunxi_mmc_host *host,
+ struct mmc_ios *ios)
+{
+ struct mmc_host *mmc = host->mmc;
+
+ switch (ios->power_mode) {
case MMC_POWER_UP:
+ dev_dbg(mmc_dev(mmc), "Powering card up\n");
+
if (!IS_ERR(mmc->supply.vmmc)) {
host->ferror = mmc_regulator_set_ocr(mmc,
mmc->supply.vmmc,
@@ -887,53 +915,33 @@ static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
}
host->vqmmc_enabled = true;
}
-
- host->ferror = sunxi_mmc_init_host(mmc);
- if (host->ferror)
- return;
-
- dev_dbg(mmc_dev(mmc), "power on!\n");
break;
case MMC_POWER_OFF:
- dev_dbg(mmc_dev(mmc), "power off!\n");
- sunxi_mmc_reset_host(host);
+ dev_dbg(mmc_dev(mmc), "Powering card off\n");
+
if (!IS_ERR(mmc->supply.vmmc))
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
if (!IS_ERR(mmc->supply.vqmmc) && host->vqmmc_enabled)
regulator_disable(mmc->supply.vqmmc);
+
host->vqmmc_enabled = false;
break;
- }
- /* set bus width */
- switch (ios->bus_width) {
- case MMC_BUS_WIDTH_1:
- mmc_writel(host, REG_WIDTH, SDXC_WIDTH1);
- break;
- case MMC_BUS_WIDTH_4:
- mmc_writel(host, REG_WIDTH, SDXC_WIDTH4);
- break;
- case MMC_BUS_WIDTH_8:
- mmc_writel(host, REG_WIDTH, SDXC_WIDTH8);
+ default:
+ dev_dbg(mmc_dev(mmc), "Ignoring unknown card power state\n");
break;
}
+}
- /* set ddr mode */
- rval = mmc_readl(host, REG_GCTRL);
- if (ios->timing == MMC_TIMING_UHS_DDR50 ||
- ios->timing == MMC_TIMING_MMC_DDR52)
- rval |= SDXC_DDR_MODE;
- else
- rval &= ~SDXC_DDR_MODE;
- mmc_writel(host, REG_GCTRL, rval);
+static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct sunxi_mmc_host *host = mmc_priv(mmc);
- /* set up clock */
- if (ios->power_mode) {
- host->ferror = sunxi_mmc_clk_set_rate(host, ios);
- /* Android code had a usleep_range(50000, 55000); here */
- }
+ sunxi_mmc_card_power(host, ios);
+ sunxi_mmc_set_bus_width(host, ios->bus_width);
+ sunxi_mmc_set_clk(host, ios);
}
static int sunxi_mmc_volt_switch(struct mmc_host *mmc, struct mmc_ios *ios)
@@ -955,6 +963,9 @@ static void sunxi_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
unsigned long flags;
u32 imask;
+ if (enable)
+ pm_runtime_get_noresume(host->dev);
+
spin_lock_irqsave(&host->lock, flags);
imask = mmc_readl(host, REG_IMASK);
@@ -967,6 +978,9 @@ static void sunxi_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
}
mmc_writel(host, REG_IMASK, imask);
spin_unlock_irqrestore(&host->lock, flags);
+
+ if (!enable)
+ pm_runtime_put_noidle(host->mmc->parent);
}
static void sunxi_mmc_hw_reset(struct mmc_host *mmc)
@@ -1380,6 +1394,15 @@ static int sunxi_mmc_probe(struct platform_device *pdev)
if (ret)
goto error_free_dma;
+ ret = sunxi_mmc_init_host(host);
+ if (ret)
+ goto error_free_dma;
+
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
ret = mmc_add_host(mmc);
if (ret)
goto error_free_dma;
@@ -1400,6 +1423,7 @@ static int sunxi_mmc_remove(struct platform_device *pdev)
struct sunxi_mmc_host *host = mmc_priv(mmc);
mmc_remove_host(mmc);
+ pm_runtime_force_suspend(&pdev->dev);
disable_irq(host->irq);
sunxi_mmc_disable(host);
dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
@@ -1408,10 +1432,47 @@ static int sunxi_mmc_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM
+static int sunxi_mmc_runtime_resume(struct device *dev)
+{
+ struct mmc_host *mmc = dev_get_drvdata(dev);
+ struct sunxi_mmc_host *host = mmc_priv(mmc);
+ int ret;
+
+ ret = sunxi_mmc_enable(host);
+ if (ret)
+ return ret;
+
+ sunxi_mmc_init_host(host);
+ sunxi_mmc_set_bus_width(host, mmc->ios.bus_width);
+ sunxi_mmc_set_clk(host, &mmc->ios);
+
+ return 0;
+}
+
+static int sunxi_mmc_runtime_suspend(struct device *dev)
+{
+ struct mmc_host *mmc = dev_get_drvdata(dev);
+ struct sunxi_mmc_host *host = mmc_priv(mmc);
+
+ sunxi_mmc_reset_host(host);
+ sunxi_mmc_disable(host);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops sunxi_mmc_pm_ops = {
+ SET_RUNTIME_PM_OPS(sunxi_mmc_runtime_suspend,
+ sunxi_mmc_runtime_resume,
+ NULL)
+};
+
static struct platform_driver sunxi_mmc_driver = {
.driver = {
.name = "sunxi-mmc",
.of_match_table = of_match_ptr(sunxi_mmc_of_match),
+ .pm = &sunxi_mmc_pm_ops,
},
.probe = sunxi_mmc_probe,
.remove = sunxi_mmc_remove,
diff --git a/drivers/mmc/host/ushc.c b/drivers/mmc/host/ushc.c
index 81dac17064d7..b2b379b10dfa 100644
--- a/drivers/mmc/host/ushc.c
+++ b/drivers/mmc/host/ushc.c
@@ -300,8 +300,10 @@ static void ushc_request(struct mmc_host *mmc, struct mmc_request *req)
pipe = usb_sndbulkpipe(ushc->usb_dev, 2);
usb_fill_bulk_urb(ushc->data_urb, ushc->usb_dev, pipe,
- sg_virt(data->sg), data->sg->length,
+ NULL, data->sg->length,
data_callback, ushc);
+ ushc->data_urb->num_sgs = 1;
+ ushc->data_urb->sg = data->sg;
ret = usb_submit_urb(ushc->data_urb, GFP_ATOMIC);
if (ret < 0)
goto out;
diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c
index f4233576153b..1e54bbf13d75 100644
--- a/drivers/mmc/host/wbsd.c
+++ b/drivers/mmc/host/wbsd.c
@@ -268,43 +268,29 @@ static inline int wbsd_next_sg(struct wbsd_host *host)
return host->num_sg;
}
-static inline char *wbsd_sg_to_buffer(struct wbsd_host *host)
+static inline char *wbsd_map_sg(struct wbsd_host *host)
{
- return sg_virt(host->cur_sg);
+ return kmap_atomic(sg_page(host->cur_sg)) + host->cur_sg->offset;
}
static inline void wbsd_sg_to_dma(struct wbsd_host *host, struct mmc_data *data)
{
- unsigned int len, i;
- struct scatterlist *sg;
- char *dmabuf = host->dma_buffer;
- char *sgbuf;
-
- sg = data->sg;
- len = data->sg_len;
-
- for (i = 0; i < len; i++) {
- sgbuf = sg_virt(&sg[i]);
- memcpy(dmabuf, sgbuf, sg[i].length);
- dmabuf += sg[i].length;
- }
+ size_t len = 0;
+ int i;
+
+ for (i = 0; i < data->sg_len; i++)
+ len += data->sg[i].length;
+ sg_copy_to_buffer(data->sg, data->sg_len, host->dma_buffer, len);
}
static inline void wbsd_dma_to_sg(struct wbsd_host *host, struct mmc_data *data)
{
- unsigned int len, i;
- struct scatterlist *sg;
- char *dmabuf = host->dma_buffer;
- char *sgbuf;
-
- sg = data->sg;
- len = data->sg_len;
-
- for (i = 0; i < len; i++) {
- sgbuf = sg_virt(&sg[i]);
- memcpy(sgbuf, dmabuf, sg[i].length);
- dmabuf += sg[i].length;
- }
+ size_t len = 0;
+ int i;
+
+ for (i = 0; i < data->sg_len; i++)
+ len += data->sg[i].length;
+ sg_copy_from_buffer(data->sg, data->sg_len, host->dma_buffer, len);
}
/*
@@ -418,7 +404,7 @@ static void wbsd_empty_fifo(struct wbsd_host *host)
{
struct mmc_data *data = host->mrq->cmd->data;
char *buffer;
- int i, fsr, fifo;
+ int i, idx, fsr, fifo;
/*
* Handle excessive data.
@@ -426,7 +412,8 @@ static void wbsd_empty_fifo(struct wbsd_host *host)
if (host->num_sg == 0)
return;
- buffer = wbsd_sg_to_buffer(host) + host->offset;
+ buffer = wbsd_map_sg(host) + host->offset;
+ idx = 0;
/*
* Drain the fifo. This has a tendency to loop longer
@@ -445,8 +432,7 @@ static void wbsd_empty_fifo(struct wbsd_host *host)
fifo = 1;
for (i = 0; i < fifo; i++) {
- *buffer = inb(host->base + WBSD_DFR);
- buffer++;
+ buffer[idx++] = inb(host->base + WBSD_DFR);
host->offset++;
host->remain--;
@@ -456,16 +442,19 @@ static void wbsd_empty_fifo(struct wbsd_host *host)
* End of scatter list entry?
*/
if (host->remain == 0) {
+ kunmap_atomic(buffer);
/*
* Get next entry. Check if last.
*/
if (!wbsd_next_sg(host))
return;
- buffer = wbsd_sg_to_buffer(host);
+ buffer = wbsd_map_sg(host);
+ idx = 0;
}
}
}
+ kunmap_atomic(buffer);
/*
* This is a very dirty hack to solve a
@@ -480,7 +469,7 @@ static void wbsd_fill_fifo(struct wbsd_host *host)
{
struct mmc_data *data = host->mrq->cmd->data;
char *buffer;
- int i, fsr, fifo;
+ int i, idx, fsr, fifo;
/*
* Check that we aren't being called after the
@@ -489,7 +478,8 @@ static void wbsd_fill_fifo(struct wbsd_host *host)
if (host->num_sg == 0)
return;
- buffer = wbsd_sg_to_buffer(host) + host->offset;
+ buffer = wbsd_map_sg(host) + host->offset;
+ idx = 0;
/*
* Fill the fifo. This has a tendency to loop longer
@@ -508,8 +498,7 @@ static void wbsd_fill_fifo(struct wbsd_host *host)
fifo = 15;
for (i = 16; i > fifo; i--) {
- outb(*buffer, host->base + WBSD_DFR);
- buffer++;
+ outb(buffer[idx], host->base + WBSD_DFR);
host->offset++;
host->remain--;
@@ -519,16 +508,19 @@ static void wbsd_fill_fifo(struct wbsd_host *host)
* End of scatter list entry?
*/
if (host->remain == 0) {
+ kunmap_atomic(buffer);
/*
* Get next entry. Check if last.
*/
if (!wbsd_next_sg(host))
return;
- buffer = wbsd_sg_to_buffer(host);
+ buffer = wbsd_map_sg(host);
+ idx = 0;
}
}
}
+ kunmap_atomic(buffer);
/*
* The controller stops sending interrupts for
diff --git a/drivers/mmc/host/wmt-sdmmc.c b/drivers/mmc/host/wmt-sdmmc.c
index fd30ac7da5e5..3ba42f508014 100644
--- a/drivers/mmc/host/wmt-sdmmc.c
+++ b/drivers/mmc/host/wmt-sdmmc.c
@@ -928,8 +928,7 @@ static int wmt_mci_remove(struct platform_device *pdev)
static int wmt_mci_suspend(struct device *dev)
{
u32 reg_tmp;
- struct platform_device *pdev = to_platform_device(dev);
- struct mmc_host *mmc = platform_get_drvdata(pdev);
+ struct mmc_host *mmc = dev_get_drvdata(dev);
struct wmt_mci_priv *priv;
if (!mmc)
@@ -953,8 +952,7 @@ static int wmt_mci_suspend(struct device *dev)
static int wmt_mci_resume(struct device *dev)
{
u32 reg_tmp;
- struct platform_device *pdev = to_platform_device(dev);
- struct mmc_host *mmc = platform_get_drvdata(pdev);
+ struct mmc_host *mmc = dev_get_drvdata(dev);
struct wmt_mci_priv *priv;
if (mmc) {