summaryrefslogtreecommitdiffstats
path: root/drivers/mmc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/core/block.c34
-rw-r--r--drivers/mmc/core/card.h1
-rw-r--r--drivers/mmc/core/core.c5
-rw-r--r--drivers/mmc/core/debugfs.c19
-rw-r--r--drivers/mmc/core/host.h3
-rw-r--r--drivers/mmc/core/mmc.c2
-rw-r--r--drivers/mmc/core/mmc_ops.c4
-rw-r--r--drivers/mmc/core/quirks.h6
-rw-r--r--drivers/mmc/core/sd.c20
-rw-r--r--drivers/mmc/core/sdio.c9
-rw-r--r--drivers/mmc/core/sdio_irq.c4
-rw-r--r--drivers/mmc/core/slot-gpio.c23
-rw-r--r--drivers/mmc/host/Kconfig28
-rw-r--r--drivers/mmc/host/Makefile2
-rw-r--r--drivers/mmc/host/bfin_sdh.c679
-rw-r--r--drivers/mmc/host/dw_mmc-exynos.c9
-rw-r--r--drivers/mmc/host/dw_mmc-hi3798cv200.c202
-rw-r--r--drivers/mmc/host/dw_mmc-k3.c4
-rw-r--r--drivers/mmc/host/dw_mmc-pci.c1
-rw-r--r--drivers/mmc/host/dw_mmc-rockchip.c5
-rw-r--r--drivers/mmc/host/dw_mmc-zx.c1
-rw-r--r--drivers/mmc/host/dw_mmc.c145
-rw-r--r--drivers/mmc/host/dw_mmc.h29
-rw-r--r--drivers/mmc/host/mtk-sd.c12
-rw-r--r--drivers/mmc/host/renesas_sdhi_core.c6
-rw-r--r--drivers/mmc/host/renesas_sdhi_internal_dmac.c11
-rw-r--r--drivers/mmc/host/renesas_sdhi_sys_dmac.c17
-rw-r--r--drivers/mmc/host/sdhci-acpi.c2
-rw-r--r--drivers/mmc/host/sdhci-iproc.c1
-rw-r--r--drivers/mmc/host/sdhci-omap.c378
-rw-r--r--drivers/mmc/host/sdhci-pci-core.c78
-rw-r--r--drivers/mmc/host/sdhci.c19
-rw-r--r--drivers/mmc/host/sh_mmcif.c8
-rw-r--r--drivers/mmc/host/sunxi-mmc.c144
-rw-r--r--drivers/mmc/host/tmio_mmc_core.c66
-rw-r--r--drivers/mmc/host/ushc.c2
36 files changed, 979 insertions, 1000 deletions
diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c
index c895bb0d5569..02485e310c81 100644
--- a/drivers/mmc/core/block.c
+++ b/drivers/mmc/core/block.c
@@ -72,6 +72,7 @@ MODULE_ALIAS("mmc:block");
#define MMC_BLK_TIMEOUT_MS (10 * 1000)
#define MMC_SANITIZE_REQ_TIMEOUT 240000
#define MMC_EXTRACT_INDEX_FROM_ARG(x) ((x & 0x00FF0000) >> 16)
+#define MMC_EXTRACT_VALUE_FROM_ARG(x) ((x & 0x0000FF00) >> 8)
#define mmc_req_rel_wr(req) ((req->cmd_flags & REQ_FUA) && \
(rq_data_dir(req) == WRITE))
@@ -375,22 +376,15 @@ static struct mmc_blk_ioc_data *mmc_blk_ioctl_copy_from_user(
return idata;
}
- idata->buf = kmalloc(idata->buf_bytes, GFP_KERNEL);
- if (!idata->buf) {
- err = -ENOMEM;
+ idata->buf = memdup_user((void __user *)(unsigned long)
+ idata->ic.data_ptr, idata->buf_bytes);
+ if (IS_ERR(idata->buf)) {
+ err = PTR_ERR(idata->buf);
goto idata_err;
}
- if (copy_from_user(idata->buf, (void __user *)(unsigned long)
- idata->ic.data_ptr, idata->buf_bytes)) {
- err = -EFAULT;
- goto copy_err;
- }
-
return idata;
-copy_err:
- kfree(idata->buf);
idata_err:
kfree(idata);
out:
@@ -587,6 +581,24 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
}
/*
+ * Make sure the cache of the PARTITION_CONFIG register and
+ * PARTITION_ACCESS bits is updated in case the ioctl ext_csd write
+ * changed it successfully.
+ */
+ if ((MMC_EXTRACT_INDEX_FROM_ARG(cmd.arg) == EXT_CSD_PART_CONFIG) &&
+ (cmd.opcode == MMC_SWITCH)) {
+ struct mmc_blk_data *main_md = dev_get_drvdata(&card->dev);
+ u8 value = MMC_EXTRACT_VALUE_FROM_ARG(cmd.arg);
+
+ /*
+ * Update cache so the next mmc_blk_part_switch call operates
+ * on up-to-date data.
+ */
+ card->ext_csd.part_config = value;
+ main_md->part_curr = value & EXT_CSD_PART_CONFIG_ACC_MASK;
+ }
+
+ /*
* According to the SD specs, some commands require a delay after
* issuing the command.
*/
diff --git a/drivers/mmc/core/card.h b/drivers/mmc/core/card.h
index 79a5b985ccf5..9c821eedd156 100644
--- a/drivers/mmc/core/card.h
+++ b/drivers/mmc/core/card.h
@@ -82,6 +82,7 @@ struct mmc_fixup {
#define CID_MANFID_APACER 0x27
#define CID_MANFID_KINGSTON 0x70
#define CID_MANFID_HYNIX 0x90
+#define CID_MANFID_NUMONYX 0xFE
#define END_FIXUP { NULL }
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index c0ba6d8823b7..121ce50b6d5e 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -2369,7 +2369,7 @@ unsigned int mmc_calc_max_discard(struct mmc_card *card)
return card->pref_erase;
max_discard = mmc_do_calc_max_discard(card, MMC_ERASE_ARG);
- if (mmc_can_trim(card)) {
+ if (max_discard && mmc_can_trim(card)) {
max_trim = mmc_do_calc_max_discard(card, MMC_TRIM_ARG);
if (max_trim < max_discard)
max_discard = max_trim;
@@ -2655,8 +2655,7 @@ void mmc_start_host(struct mmc_host *host)
void mmc_stop_host(struct mmc_host *host)
{
if (host->slot.cd_irq >= 0) {
- if (host->slot.cd_wake_enabled)
- disable_irq_wake(host->slot.cd_irq);
+ mmc_gpio_set_cd_wake(host, false);
disable_irq(host->slot.cd_irq);
}
diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c
index 0f4a7d7b2626..d2275c5a2311 100644
--- a/drivers/mmc/core/debugfs.c
+++ b/drivers/mmc/core/debugfs.c
@@ -196,18 +196,7 @@ static int mmc_ios_show(struct seq_file *s, void *data)
return 0;
}
-
-static int mmc_ios_open(struct inode *inode, struct file *file)
-{
- return single_open(file, mmc_ios_show, inode->i_private);
-}
-
-static const struct file_operations mmc_ios_fops = {
- .open = mmc_ios_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(mmc_ios);
static int mmc_clock_opt_get(void *data, u64 *val)
{
@@ -254,6 +243,12 @@ void mmc_add_host_debugfs(struct mmc_host *host)
if (!debugfs_create_file("ios", S_IRUSR, root, host, &mmc_ios_fops))
goto err_node;
+ if (!debugfs_create_x32("caps", S_IRUSR, root, &host->caps))
+ goto err_node;
+
+ if (!debugfs_create_x32("caps2", S_IRUSR, root, &host->caps2))
+ goto err_node;
+
if (!debugfs_create_file("clock", S_IRUSR | S_IWUSR, root, host,
&mmc_clock_fops))
goto err_node;
diff --git a/drivers/mmc/core/host.h b/drivers/mmc/core/host.h
index 06ec19b5bf9f..4805438c02ff 100644
--- a/drivers/mmc/core/host.h
+++ b/drivers/mmc/core/host.h
@@ -56,7 +56,8 @@ static inline int mmc_host_uhs(struct mmc_host *host)
return host->caps &
(MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 |
- MMC_CAP_UHS_DDR50);
+ MMC_CAP_UHS_DDR50) &&
+ host->caps & MMC_CAP_4_BIT_DATA;
}
static inline bool mmc_card_hs200(struct mmc_card *card)
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 208a762b87ef..6f8ebd6caa4c 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -792,6 +792,7 @@ MMC_DEV_ATTR(enhanced_area_size, "%u\n", card->ext_csd.enhanced_area_size);
MMC_DEV_ATTR(raw_rpmb_size_mult, "%#x\n", card->ext_csd.raw_rpmb_size_mult);
MMC_DEV_ATTR(rel_sectors, "%#x\n", card->ext_csd.rel_sectors);
MMC_DEV_ATTR(ocr, "0x%08x\n", card->ocr);
+MMC_DEV_ATTR(rca, "0x%04x\n", card->rca);
MMC_DEV_ATTR(cmdq_en, "%d\n", card->ext_csd.cmdq_en);
static ssize_t mmc_fwrev_show(struct device *dev,
@@ -848,6 +849,7 @@ static struct attribute *mmc_std_attrs[] = {
&dev_attr_raw_rpmb_size_mult.attr,
&dev_attr_rel_sectors.attr,
&dev_attr_ocr.attr,
+ &dev_attr_rca.attr,
&dev_attr_dsr.attr,
&dev_attr_cmdq_en.attr,
NULL,
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index 908e4db03535..42d6aa89a48a 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -848,7 +848,6 @@ int mmc_interrupt_hpi(struct mmc_card *card)
return 1;
}
- mmc_claim_host(card->host);
err = mmc_send_status(card, &status);
if (err) {
pr_err("%s: Get card status fail\n", mmc_hostname(card->host));
@@ -890,7 +889,6 @@ int mmc_interrupt_hpi(struct mmc_card *card)
} while (!err);
out:
- mmc_release_host(card->host);
return err;
}
@@ -932,9 +930,7 @@ static int mmc_read_bkops_status(struct mmc_card *card)
int err;
u8 *ext_csd;
- mmc_claim_host(card->host);
err = mmc_get_ext_csd(card, &ext_csd);
- mmc_release_host(card->host);
if (err)
return err;
diff --git a/drivers/mmc/core/quirks.h b/drivers/mmc/core/quirks.h
index 75d317623852..5153577754f0 100644
--- a/drivers/mmc/core/quirks.h
+++ b/drivers/mmc/core/quirks.h
@@ -109,6 +109,12 @@ static const struct mmc_fixup mmc_ext_csd_fixups[] = {
*/
MMC_FIXUP_EXT_CSD_REV(CID_NAME_ANY, CID_MANFID_HYNIX,
0x014a, add_quirk, MMC_QUIRK_BROKEN_HPI, 5),
+ /*
+ * Certain Micron (Numonyx) eMMC 4.5 cards might get broken when HPI
+ * feature is used so disable the HPI feature for such buggy cards.
+ */
+ MMC_FIXUP_EXT_CSD_REV(CID_NAME_ANY, CID_MANFID_NUMONYX,
+ 0x014e, add_quirk, MMC_QUIRK_BROKEN_HPI, 6),
END_FIXUP
};
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 62b84dd8f9fe..baf3d5da4ccb 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -291,8 +291,6 @@ static int mmc_read_switch(struct mmc_card *card)
return 0;
}
- err = -EIO;
-
status = kmalloc(64, GFP_KERNEL);
if (!status)
return -ENOMEM;
@@ -582,9 +580,6 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
int err;
u8 *status;
- if (!card->scr.sda_spec3)
- return 0;
-
if (!(card->csd.cmdclass & CCC_SWITCH))
return 0;
@@ -593,14 +588,11 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
return -ENOMEM;
/* Set 4-bit bus width */
- if ((card->host->caps & MMC_CAP_4_BIT_DATA) &&
- (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
- err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);
- if (err)
- goto out;
+ err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);
+ if (err)
+ goto out;
- mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
- }
+ mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
/*
* Select the bus speed mode depending on host
@@ -676,6 +668,7 @@ MMC_DEV_ATTR(name, "%s\n", card->cid.prod_name);
MMC_DEV_ATTR(oemid, "0x%04x\n", card->cid.oemid);
MMC_DEV_ATTR(serial, "0x%08x\n", card->cid.serial);
MMC_DEV_ATTR(ocr, "0x%08x\n", card->ocr);
+MMC_DEV_ATTR(rca, "0x%04x\n", card->rca);
static ssize_t mmc_dsr_show(struct device *dev,
@@ -709,6 +702,7 @@ static struct attribute *sd_std_attrs[] = {
&dev_attr_oemid.attr,
&dev_attr_serial.attr,
&dev_attr_ocr.attr,
+ &dev_attr_rca.attr,
&dev_attr_dsr.attr,
NULL,
};
@@ -1033,7 +1027,7 @@ retry:
}
/* Initialization sequence for UHS-I cards */
- if (rocr & SD_ROCR_S18A) {
+ if (rocr & SD_ROCR_S18A && mmc_host_uhs(host)) {
err = mmc_sd_init_uhs_card(card);
if (err)
goto free_card;
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index cc43687ca241..c599a628a387 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -518,11 +518,10 @@ static int mmc_sdio_init_uhs_card(struct mmc_card *card)
if (!card->scr.sda_spec3)
return 0;
- /*
- * Switch to wider bus (if supported).
- */
- if (card->host->caps & MMC_CAP_4_BIT_DATA)
- err = sdio_enable_4bit_bus(card);
+ /* Switch to wider bus */
+ err = sdio_enable_4bit_bus(card);
+ if (err)
+ goto out;
/* Set the driver strength for the card */
sdio_select_driver_type(card);
diff --git a/drivers/mmc/core/sdio_irq.c b/drivers/mmc/core/sdio_irq.c
index 7a2eaf8410a3..7ca7b99413f0 100644
--- a/drivers/mmc/core/sdio_irq.c
+++ b/drivers/mmc/core/sdio_irq.c
@@ -277,8 +277,8 @@ static void sdio_single_irq_set(struct mmc_card *card)
*
* Claim and activate the IRQ for the given SDIO function. The provided
* handler will be called when that IRQ is asserted. The host is always
- * claimed already when the handler is called so the handler must not
- * call sdio_claim_host() nor sdio_release_host().
+ * claimed already when the handler is called so the handler should not
+ * call sdio_claim_host() or sdio_release_host().
*/
int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler)
{
diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c
index 3698b0576009..31f7dbb15668 100644
--- a/drivers/mmc/core/slot-gpio.c
+++ b/drivers/mmc/core/slot-gpio.c
@@ -149,11 +149,30 @@ void mmc_gpiod_request_cd_irq(struct mmc_host *host)
if (irq < 0)
host->caps |= MMC_CAP_NEEDS_POLL;
- else if ((host->caps & MMC_CAP_CD_WAKE) && !enable_irq_wake(irq))
- host->slot.cd_wake_enabled = true;
}
EXPORT_SYMBOL(mmc_gpiod_request_cd_irq);
+int mmc_gpio_set_cd_wake(struct mmc_host *host, bool on)
+{
+ int ret = 0;
+
+ if (!(host->caps & MMC_CAP_CD_WAKE) ||
+ host->slot.cd_irq < 0 ||
+ on == host->slot.cd_wake_enabled)
+ return 0;
+
+ if (on) {
+ ret = enable_irq_wake(host->slot.cd_irq);
+ host->slot.cd_wake_enabled = !ret;
+ } else {
+ disable_irq_wake(host->slot.cd_irq);
+ host->slot.cd_wake_enabled = false;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(mmc_gpio_set_cd_wake);
+
/* Register an alternate interrupt service routine for
* the card-detect GPIO.
*/
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 620c2d90a646..9589f9c9046f 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -646,25 +646,6 @@ config MMC_VIA_SDMMC
If unsure, say N.
-config SDH_BFIN
- tristate "Blackfin Secure Digital Host support"
- depends on (BF54x && !BF544) || (BF51x && !BF512)
- help
- If you say yes here you will get support for the Blackfin on-chip
- Secure Digital Host interface. This includes support for MMC and
- SD cards.
-
- To compile this driver as a module, choose M here: the
- module will be called bfin_sdh.
-
- If unsure, say N.
-
-config SDH_BFIN_MISSING_CMD_PULLUP_WORKAROUND
- bool "Blackfin EZkit Missing SDH_CMD Pull Up Resistor Workaround"
- depends on SDH_BFIN
- help
- If you say yes here SD-Cards may work on the EZkit.
-
config MMC_CAVIUM_OCTEON
tristate "Cavium OCTEON SD/MMC Card Interface support"
depends on CAVIUM_OCTEON_SOC
@@ -718,6 +699,15 @@ config MMC_DW_EXYNOS
Synopsys DesignWare Memory Card Interface driver. Select this option
for platforms based on Exynos4 and Exynos5 SoC's.
+config MMC_DW_HI3798CV200
+ tristate "Hi3798CV200 specific extensions for Synopsys DW Memory Card Interface"
+ depends on MMC_DW
+ select MMC_DW_PLTFM
+ help
+ This selects support for HiSilicon Hi3798CV200 SoC specific extensions to the
+ Synopsys DesignWare Memory Card Interface driver. Select this option
+ for platforms based on HiSilicon Hi3798CV200 SoC.
+
config MMC_DW_K3
tristate "K3 specific extensions for Synopsys DW Memory Card Interface"
depends on MMC_DW
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 84cd1388abc3..6aead24879b4 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -43,7 +43,6 @@ obj-$(CONFIG_MMC_SDHI_SYS_DMAC) += renesas_sdhi_sys_dmac.o
obj-$(CONFIG_MMC_SDHI_INTERNAL_DMAC) += renesas_sdhi_internal_dmac.o
obj-$(CONFIG_MMC_CB710) += cb710-mmc.o
obj-$(CONFIG_MMC_VIA_SDMMC) += via-sdmmc.o
-obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o
octeon-mmc-objs := cavium.o cavium-octeon.o
obj-$(CONFIG_MMC_CAVIUM_OCTEON) += octeon-mmc.o
thunderx-mmc-objs := cavium.o cavium-thunderx.o
@@ -51,6 +50,7 @@ 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_EXYNOS) += dw_mmc-exynos.o
+obj-$(CONFIG_MMC_DW_HI3798CV200) += dw_mmc-hi3798cv200.o
obj-$(CONFIG_MMC_DW_K3) += dw_mmc-k3.o
obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o
obj-$(CONFIG_MMC_DW_ROCKCHIP) += dw_mmc-rockchip.o
diff --git a/drivers/mmc/host/bfin_sdh.c b/drivers/mmc/host/bfin_sdh.c
deleted file mode 100644
index 526231e38583..000000000000
--- a/drivers/mmc/host/bfin_sdh.c
+++ /dev/null
@@ -1,679 +0,0 @@
-/*
- * bfin_sdh.c - Analog Devices Blackfin SDH Controller
- *
- * Copyright (C) 2007-2009 Analog Device Inc.
- *
- * Licensed under the GPL-2 or later.
- */
-
-#define DRIVER_NAME "bfin-sdh"
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/ioport.h>
-#include <linux/platform_device.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/dma-mapping.h>
-#include <linux/mmc/host.h>
-#include <linux/proc_fs.h>
-#include <linux/gfp.h>
-
-#include <asm/cacheflush.h>
-#include <asm/dma.h>
-#include <asm/portmux.h>
-#include <asm/bfin_sdh.h>
-
-#if defined(CONFIG_BF51x) || defined(__ADSPBF60x__)
-#define bfin_read_SDH_CLK_CTL bfin_read_RSI_CLK_CTL
-#define bfin_write_SDH_CLK_CTL bfin_write_RSI_CLK_CTL
-#define bfin_write_SDH_ARGUMENT bfin_write_RSI_ARGUMENT
-#define bfin_write_SDH_COMMAND bfin_write_RSI_COMMAND
-#define bfin_write_SDH_DATA_TIMER bfin_write_RSI_DATA_TIMER
-#define bfin_read_SDH_RESPONSE0 bfin_read_RSI_RESPONSE0
-#define bfin_read_SDH_RESPONSE1 bfin_read_RSI_RESPONSE1
-#define bfin_read_SDH_RESPONSE2 bfin_read_RSI_RESPONSE2
-#define bfin_read_SDH_RESPONSE3 bfin_read_RSI_RESPONSE3
-#define bfin_write_SDH_DATA_LGTH bfin_write_RSI_DATA_LGTH
-#define bfin_read_SDH_DATA_CTL bfin_read_RSI_DATA_CTL
-#define bfin_write_SDH_DATA_CTL bfin_write_RSI_DATA_CTL
-#define bfin_read_SDH_DATA_CNT bfin_read_RSI_DATA_CNT
-#define bfin_write_SDH_STATUS_CLR bfin_write_RSI_STATUS_CLR
-#define bfin_read_SDH_E_STATUS bfin_read_RSI_E_STATUS
-#define bfin_write_SDH_E_STATUS bfin_write_RSI_E_STATUS
-#define bfin_read_SDH_STATUS bfin_read_RSI_STATUS
-#define bfin_write_SDH_MASK0 bfin_write_RSI_MASK0
-#define bfin_write_SDH_E_MASK bfin_write_RSI_E_MASK
-#define bfin_read_SDH_CFG bfin_read_RSI_CFG
-#define bfin_write_SDH_CFG bfin_write_RSI_CFG
-# if defined(__ADSPBF60x__)
-# define bfin_read_SDH_BLK_SIZE bfin_read_RSI_BLKSZ
-# define bfin_write_SDH_BLK_SIZE bfin_write_RSI_BLKSZ
-# else
-# define bfin_read_SDH_PWR_CTL bfin_read_RSI_PWR_CTL
-# define bfin_write_SDH_PWR_CTL bfin_write_RSI_PWR_CTL
-# endif
-#endif
-
-struct sdh_host {
- struct mmc_host *mmc;
- spinlock_t lock;
- struct resource *res;
- void __iomem *base;
- int irq;
- int stat_irq;
- int dma_ch;
- int dma_dir;
- struct dma_desc_array *sg_cpu;
- dma_addr_t sg_dma;
- int dma_len;
-
- unsigned long sclk;
- unsigned int imask;
- unsigned int power_mode;
- unsigned int clk_div;
-
- struct mmc_request *mrq;
- struct mmc_command *cmd;
- struct mmc_data *data;
-};
-
-static struct bfin_sd_host *get_sdh_data(struct platform_device *pdev)
-{
- return pdev->dev.platform_data;
-}
-
-static void sdh_stop_clock(struct sdh_host *host)
-{
- bfin_write_SDH_CLK_CTL(bfin_read_SDH_CLK_CTL() & ~CLK_E);
- SSYNC();
-}
-
-static void sdh_enable_stat_irq(struct sdh_host *host, unsigned int mask)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&host->lock, flags);
- host->imask |= mask;
- bfin_write_SDH_MASK0(mask);
- SSYNC();
- spin_unlock_irqrestore(&host->lock, flags);
-}
-
-static void sdh_disable_stat_irq(struct sdh_host *host, unsigned int mask)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&host->lock, flags);
- host->imask &= ~mask;
- bfin_write_SDH_MASK0(host->imask);
- SSYNC();
- spin_unlock_irqrestore(&host->lock, flags);
-}
-
-static int sdh_setup_data(struct sdh_host *host, struct mmc_data *data)
-{
- unsigned int length;
- unsigned int data_ctl;
- unsigned int dma_cfg;
- unsigned int cycle_ns, timeout;
-
- dev_dbg(mmc_dev(host->mmc), "%s enter flags: 0x%x\n", __func__, data->flags);
- host->data = data;
- data_ctl = 0;
- dma_cfg = 0;
-
- length = data->blksz * data->blocks;
- bfin_write_SDH_DATA_LGTH(length);
-
- if (data->flags & MMC_DATA_READ)
- data_ctl |= DTX_DIR;
- /* Only supports power-of-2 block size */
- if (data->blksz & (data->blksz - 1))
- return -EINVAL;
-#ifndef RSI_BLKSZ
- data_ctl |= ((ffs(data->blksz) - 1) << 4);
-#else
- bfin_write_SDH_BLK_SIZE(data->blksz);
-#endif
-
- bfin_write_SDH_DATA_CTL(data_ctl);
- /* the time of a host clock period in ns */
- cycle_ns = 1000000000 / (host->sclk / (2 * (host->clk_div + 1)));
- timeout = data->timeout_ns / cycle_ns;
- timeout += data->timeout_clks;
- bfin_write_SDH_DATA_TIMER(timeout);
- SSYNC();
-
- if (data->flags & MMC_DATA_READ) {
- host->dma_dir = DMA_FROM_DEVICE;
- dma_cfg |= WNR;
- } else
- host->dma_dir = DMA_TO_DEVICE;
-
- sdh_enable_stat_irq(host, (DAT_CRC_FAIL | DAT_TIME_OUT | DAT_END));
- host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, host->dma_dir);
-#if defined(CONFIG_BF54x) || defined(CONFIG_BF60x)
- dma_cfg |= DMAFLOW_ARRAY | RESTART | WDSIZE_32 | DMAEN;
-# ifdef RSI_BLKSZ
- dma_cfg |= PSIZE_32 | NDSIZE_3;
-# else
- dma_cfg |= NDSIZE_5;
-# endif
- {
- struct scatterlist *sg;
- int i;
- for_each_sg(data->sg, sg, host->dma_len, i) {
- host->sg_cpu[i].start_addr = sg_dma_address(sg);
- host->sg_cpu[i].cfg = dma_cfg;
- host->sg_cpu[i].x_count = sg_dma_len(sg) / 4;
- host->sg_cpu[i].x_modify = 4;
- dev_dbg(mmc_dev(host->mmc), "%d: start_addr:0x%lx, "
- "cfg:0x%lx, x_count:0x%lx, x_modify:0x%lx\n",
- i, host->sg_cpu[i].start_addr,
- host->sg_cpu[i].cfg, host->sg_cpu[i].x_count,
- host->sg_cpu[i].x_modify);
- }
- }
- flush_dcache_range((unsigned int)host->sg_cpu,
- (unsigned int)host->sg_cpu +
- host->dma_len * sizeof(struct dma_desc_array));
- /* Set the last descriptor to stop mode */
- host->sg_cpu[host->dma_len - 1].cfg &= ~(DMAFLOW | NDSIZE);
- host->sg_cpu[host->dma_len - 1].cfg |= DI_EN;
-
- set_dma_curr_desc_addr(host->dma_ch, (unsigned long *)host->sg_dma);
- set_dma_x_count(host->dma_ch, 0);
- set_dma_x_modify(host->dma_ch, 0);
- SSYNC();
- set_dma_config(host->dma_ch, dma_cfg);
-#elif defined(CONFIG_BF51x)
- /* RSI DMA doesn't work in array mode */
- dma_cfg |= WDSIZE_32 | DMAEN;
- set_dma_start_addr(host->dma_ch, sg_dma_address(&data->sg[0]));
- set_dma_x_count(host->dma_ch, length / 4);
- set_dma_x_modify(host->dma_ch, 4);
- SSYNC();
- set_dma_config(host->dma_ch, dma_cfg);
-#endif
- bfin_write_SDH_DATA_CTL(bfin_read_SDH_DATA_CTL() | DTX_DMA_E | DTX_E);
-
- SSYNC();
-
- dev_dbg(mmc_dev(host->mmc), "%s exit\n", __func__);
- return 0;
-}
-
-static void sdh_start_cmd(struct sdh_host *host, struct mmc_command *cmd)
-{
- unsigned int sdh_cmd;
- unsigned int stat_mask;
-
- dev_dbg(mmc_dev(host->mmc), "%s enter cmd: 0x%p\n", __func__, cmd);
- WARN_ON(host->cmd != NULL);
- host->cmd = cmd;
-
- sdh_cmd = 0;
- stat_mask = 0;
-
- sdh_cmd |= cmd->opcode;
-
- if (cmd->flags & MMC_RSP_PRESENT) {
- sdh_cmd |= CMD_RSP;
- stat_mask |= CMD_RESP_END;
- } else {
- stat_mask |= CMD_SENT;
- }
-
- if (cmd->flags & MMC_RSP_136)
- sdh_cmd |= CMD_L_RSP;
-
- stat_mask |= CMD_CRC_FAIL | CMD_TIME_OUT;
-
- sdh_enable_stat_irq(host, stat_mask);
-
- bfin_write_SDH_ARGUMENT(cmd->arg);
- bfin_write_SDH_COMMAND(sdh_cmd | CMD_E);
- bfin_write_SDH_CLK_CTL(bfin_read_SDH_CLK_CTL() | CLK_E);
- SSYNC();
-}
-
-static void sdh_finish_request(struct sdh_host *host, struct mmc_request *mrq)
-{
- dev_dbg(mmc_dev(host->mmc), "%s enter\n", __func__);
- host->mrq = NULL;
- host->cmd = NULL;
- host->data = NULL;
- mmc_request_done(host->mmc, mrq);
-}
-
-static int sdh_cmd_done(struct sdh_host *host, unsigned int stat)
-{
- struct mmc_command *cmd = host->cmd;
- int ret = 0;
-
- dev_dbg(mmc_dev(host->mmc), "%s enter cmd: %p\n", __func__, cmd);
- if (!cmd)
- return 0;
-
- host->cmd = NULL;
-
- if (cmd->flags & MMC_RSP_PRESENT) {
- cmd->resp[0] = bfin_read_SDH_RESPONSE0();
- if (cmd->flags & MMC_RSP_136) {
- cmd->resp[1] = bfin_read_SDH_RESPONSE1();
- cmd->resp[2] = bfin_read_SDH_RESPONSE2();
- cmd->resp[3] = bfin_read_SDH_RESPONSE3();
- }
- }
- if (stat & CMD_TIME_OUT)
- cmd->error = -ETIMEDOUT;
- else if (stat & CMD_CRC_FAIL && cmd->flags & MMC_RSP_CRC)
- cmd->error = -EILSEQ;
-
- sdh_disable_stat_irq(host, (CMD_SENT | CMD_RESP_END | CMD_TIME_OUT | CMD_CRC_FAIL));
-
- if (host->data && !cmd->error) {
- if (host->data->flags & MMC_DATA_WRITE) {
- ret = sdh_setup_data(host, host->data);
- if (ret)
- return 0;
- }
-
- sdh_enable_stat_irq(host, DAT_END | RX_OVERRUN | TX_UNDERRUN | DAT_TIME_OUT);
- } else
- sdh_finish_request(host, host->mrq);
-
- return 1;
-}
-
-static int sdh_data_done(struct sdh_host *host, unsigned int stat)
-{
- struct mmc_data *data = host->data;
-
- dev_dbg(mmc_dev(host->mmc), "%s enter stat: 0x%x\n", __func__, stat);
- if (!data)
- return 0;
-
- disable_dma(host->dma_ch);
- dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
- host->dma_dir);
-
- if (stat & DAT_TIME_OUT)
- data->error = -ETIMEDOUT;
- else if (stat & DAT_CRC_FAIL)
- data->error = -EILSEQ;
- else if (stat & (RX_OVERRUN | TX_UNDERRUN))
- data->error = -EIO;
-
- if (!data->error)
- data->bytes_xfered = data->blocks * data->blksz;
- else
- data->bytes_xfered = 0;
-
- bfin_write_SDH_STATUS_CLR(DAT_END_STAT | DAT_TIMEOUT_STAT | \
- DAT_CRC_FAIL_STAT | DAT_BLK_END_STAT | RX_OVERRUN | TX_UNDERRUN);
- bfin_write_SDH_DATA_CTL(0);
- SSYNC();
-
- host->data = NULL;
- if (host->mrq->stop) {
- sdh_stop_clock(host);
- sdh_start_cmd(host, host->mrq->stop);
- } else {
- sdh_finish_request(host, host->mrq);
- }
-
- return 1;
-}
-
-static void sdh_request(struct mmc_host *mmc, struct mmc_request *mrq)
-{
- struct sdh_host *host = mmc_priv(mmc);
- int ret = 0;
-
- dev_dbg(mmc_dev(host->mmc), "%s enter, mrp:%p, cmd:%p\n", __func__, mrq, mrq->cmd);
- WARN_ON(host->mrq != NULL);
-
- spin_lock(&host->lock);
- host->mrq = mrq;
- host->data = mrq->data;
-
- if (mrq->data && mrq->data->flags & MMC_DATA_READ) {
- ret = sdh_setup_data(host, mrq->data);
- if (ret)
- goto data_err;
- }
-
- sdh_start_cmd(host, mrq->cmd);
-data_err:
- spin_unlock(&host->lock);
-}
-
-static void sdh_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
-{
- struct sdh_host *host;
- u16 clk_ctl = 0;
-#ifndef RSI_BLKSZ
- u16 pwr_ctl = 0;
-#endif
- u16 cfg;
- host = mmc_priv(mmc);
-
- spin_lock(&host->lock);
-
- cfg = bfin_read_SDH_CFG();
- cfg |= MWE;
- switch (ios->bus_width) {
- case MMC_BUS_WIDTH_4:
-#ifndef RSI_BLKSZ
- cfg &= ~PD_SDDAT3;
-#endif
- cfg |= PUP_SDDAT3;
- /* Enable 4 bit SDIO */
- cfg |= SD4E;
- clk_ctl |= WIDE_BUS_4;
- break;
- case MMC_BUS_WIDTH_8:
-#ifndef RSI_BLKSZ
- cfg &= ~PD_SDDAT3;
-#endif
- cfg |= PUP_SDDAT3;
- /* Disable 4 bit SDIO */
- cfg &= ~SD4E;
- clk_ctl |= BYTE_BUS_8;
- break;
- default:
- cfg &= ~PUP_SDDAT3;
- /* Disable 4 bit SDIO */
- cfg &= ~SD4E;
- }
- bfin_write_SDH_CFG(cfg);
-
- host->power_mode = ios->power_mode;
-#ifndef RSI_BLKSZ
- if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) {
- pwr_ctl |= ROD_CTL;
-# ifndef CONFIG_SDH_BFIN_MISSING_CMD_PULLUP_WORKAROUND
- pwr_ctl |= SD_CMD_OD;
-# endif
- }
-
- if (ios->power_mode != MMC_POWER_OFF)
- pwr_ctl |= PWR_ON;
- else
- pwr_ctl &= ~PWR_ON;
-
- bfin_write_SDH_PWR_CTL(pwr_ctl);
-#else
-# ifndef CONFIG_SDH_BFIN_MISSING_CMD_PULLUP_WORKAROUND
- if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
- cfg |= SD_CMD_OD;
- else
- cfg &= ~SD_CMD_OD;
-# endif
-
- if (ios->power_mode != MMC_POWER_OFF)
- cfg |= PWR_ON;
- else
- cfg &= ~PWR_ON;
-
- bfin_write_SDH_CFG(cfg);
-#endif
- SSYNC();
-
- if (ios->power_mode == MMC_POWER_ON && ios->clock) {
- unsigned char clk_div;
- clk_div = (get_sclk() / ios->clock - 1) / 2;
- clk_div = min_t(unsigned char, clk_div, 0xFF);
- clk_ctl |= clk_div;
- clk_ctl |= CLK_E;
- host->clk_div = clk_div;
- bfin_write_SDH_CLK_CTL(clk_ctl);
- } else
- sdh_stop_clock(host);
-
- /* set up sdh interrupt mask*/
- if (ios->power_mode == MMC_POWER_ON)
- bfin_write_SDH_MASK0(DAT_END | DAT_TIME_OUT | DAT_CRC_FAIL |
- RX_OVERRUN | TX_UNDERRUN | CMD_SENT | CMD_RESP_END |
- CMD_TIME_OUT | CMD_CRC_FAIL);
- else
- bfin_write_SDH_MASK0(0);
- SSYNC();
-
- spin_unlock(&host->lock);
-
- dev_dbg(mmc_dev(host->mmc), "SDH: clk_div = 0x%x actual clock:%ld expected clock:%d\n",
- host->clk_div,
- host->clk_div ? get_sclk() / (2 * (host->clk_div + 1)) : 0,
- ios->clock);
-}
-
-static const struct mmc_host_ops sdh_ops = {
- .request = sdh_request,
- .set_ios = sdh_set_ios,
-};
-
-static irqreturn_t sdh_dma_irq(int irq, void *devid)
-{
- struct sdh_host *host = devid;
-
- dev_dbg(mmc_dev(host->mmc), "%s enter, irq_stat: 0x%04lx\n", __func__,
- get_dma_curr_irqstat(host->dma_ch));
- clear_dma_irqstat(host->dma_ch);
- SSYNC();
-
- return IRQ_HANDLED;
-}
-
-static irqreturn_t sdh_stat_irq(int irq, void *devid)
-{
- struct sdh_host *host = devid;
- unsigned int status;
- int handled = 0;
-
- dev_dbg(mmc_dev(host->mmc), "%s enter\n", __func__);
-
- spin_lock(&host->lock);
-
- status = bfin_read_SDH_E_STATUS();
- if (status & SD_CARD_DET) {
- mmc_detect_change(host->mmc, 0);
- bfin_write_SDH_E_STATUS(SD_CARD_DET);
- }
- status = bfin_read_SDH_STATUS();
- if (status & (CMD_SENT | CMD_RESP_END | CMD_TIME_OUT | CMD_CRC_FAIL)) {
- handled |= sdh_cmd_done(host, status);
- bfin_write_SDH_STATUS_CLR(CMD_SENT_STAT | CMD_RESP_END_STAT | \
- CMD_TIMEOUT_STAT | CMD_CRC_FAIL_STAT);
- SSYNC();
- }
-
- status = bfin_read_SDH_STATUS();
- if (status & (DAT_END | DAT_TIME_OUT | DAT_CRC_FAIL | RX_OVERRUN | TX_UNDERRUN))
- handled |= sdh_data_done(host, status);
-
- spin_unlock(&host->lock);
-
- dev_dbg(mmc_dev(host->mmc), "%s exit\n\n", __func__);
-
- return IRQ_RETVAL(handled);
-}
-
-static void sdh_reset(void)
-{
-#if defined(CONFIG_BF54x)
- /* Secure Digital Host shares DMA with Nand controller */
- bfin_write_DMAC1_PERIMUX(bfin_read_DMAC1_PERIMUX() | 0x1);
-#endif
-
- bfin_write_SDH_CFG(bfin_read_SDH_CFG() | CLKS_EN);
- SSYNC();
-
- /* Disable card inserting detection pin. set MMC_CAP_NEEDS_POLL, and
- * mmc stack will do the detection.
- */
- bfin_write_SDH_CFG((bfin_read_SDH_CFG() & 0x1F) | (PUP_SDDAT | PUP_SDDAT3));
- SSYNC();
-}
-
-static int sdh_probe(struct platform_device *pdev)
-{
- struct mmc_host *mmc;
- struct sdh_host *host;
- struct bfin_sd_host *drv_data = get_sdh_data(pdev);
- int ret;
-
- if (!drv_data) {
- dev_err(&pdev->dev, "missing platform driver data\n");
- ret = -EINVAL;
- goto out;
- }
-
- mmc = mmc_alloc_host(sizeof(struct sdh_host), &pdev->dev);
- if (!mmc) {
- ret = -ENOMEM;
- goto out;
- }
-
- mmc->ops = &sdh_ops;
-#if defined(CONFIG_BF51x)
- mmc->max_segs = 1;
-#else
- mmc->max_segs = PAGE_SIZE / sizeof(struct dma_desc_array);
-#endif
-#ifdef RSI_BLKSZ
- mmc->max_seg_size = -1;
-#else
- mmc->max_seg_size = 1 << 16;
-#endif
- mmc->max_blk_size = 1 << 11;
- mmc->max_blk_count = 1 << 11;
- mmc->max_req_size = PAGE_SIZE;
- mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
- mmc->f_max = get_sclk();
- mmc->f_min = mmc->f_max >> 9;
- mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_NEEDS_POLL;
- host = mmc_priv(mmc);
- host->mmc = mmc;
- host->sclk = get_sclk();
-
- spin_lock_init(&host->lock);
- host->irq = drv_data->irq_int0;
- host->dma_ch = drv_data->dma_chan;
-
- ret = request_dma(host->dma_ch, DRIVER_NAME "DMA");
- if (ret) {
- dev_err(&pdev->dev, "unable to request DMA channel\n");
- goto out1;
- }
-
- ret = set_dma_callback(host->dma_ch, sdh_dma_irq, host);
- if (ret) {
- dev_err(&pdev->dev, "unable to request DMA irq\n");
- goto out2;
- }
-
- host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL);
- if (host->sg_cpu == NULL) {
- ret = -ENOMEM;
- goto out2;
- }
-
- platform_set_drvdata(pdev, mmc);
-
- ret = request_irq(host->irq, sdh_stat_irq, 0, "SDH Status IRQ", host);
- if (ret) {
- dev_err(&pdev->dev, "unable to request status irq\n");
- goto out3;
- }
-
- ret = peripheral_request_list(drv_data->pin_req, DRIVER_NAME);
- if (ret) {
- dev_err(&pdev->dev, "unable to request peripheral pins\n");
- goto out4;
- }
-
- sdh_reset();
-
- mmc_add_host(mmc);
- return 0;
-
-out4:
- free_irq(host->irq, host);
-out3:
- mmc_remove_host(mmc);
- dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
-out2:
- free_dma(host->dma_ch);
-out1:
- mmc_free_host(mmc);
- out:
- return ret;
-}
-
-static int sdh_remove(struct platform_device *pdev)
-{
- struct mmc_host *mmc = platform_get_drvdata(pdev);
-
- if (mmc) {
- struct sdh_host *host = mmc_priv(mmc);
-
- mmc_remove_host(mmc);
-
- sdh_stop_clock(host);
- free_irq(host->irq, host);
- free_dma(host->dma_ch);
- dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
-
- mmc_free_host(mmc);
- }
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int sdh_suspend(struct platform_device *dev, pm_message_t state)
-{
- struct bfin_sd_host *drv_data = get_sdh_data(dev);
-
- peripheral_free_list(drv_data->pin_req);
-
- return 0;
-}
-
-static int sdh_resume(struct platform_device *dev)
-{
- struct bfin_sd_host *drv_data = get_sdh_data(dev);
- int ret = 0;
-
- ret = peripheral_request_list(drv_data->pin_req, DRIVER_NAME);
- if (ret) {
- dev_err(&dev->dev, "unable to request peripheral pins\n");
- return ret;
- }
-
- sdh_reset();
- return ret;
-}
-#else
-# define sdh_suspend NULL
-# define sdh_resume NULL
-#endif
-
-static struct platform_driver sdh_driver = {
- .probe = sdh_probe,
- .remove = sdh_remove,
- .suspend = sdh_suspend,
- .resume = sdh_resume,
- .driver = {
- .name = DRIVER_NAME,
- },
-};
-
-module_platform_driver(sdh_driver);
-
-MODULE_DESCRIPTION("Blackfin Secure Digital Host Driver");
-MODULE_AUTHOR("Cliff Cai, Roy Huang");
-MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c
index 35026795be28..a84aa3f1ae85 100644
--- a/drivers/mmc/host/dw_mmc-exynos.c
+++ b/drivers/mmc/host/dw_mmc-exynos.c
@@ -165,9 +165,15 @@ static void dw_mci_exynos_set_clksel_timing(struct dw_mci *host, u32 timing)
static int dw_mci_exynos_runtime_resume(struct device *dev)
{
struct dw_mci *host = dev_get_drvdata(dev);
+ int ret;
+
+ ret = dw_mci_runtime_resume(dev);
+ if (ret)
+ return ret;
dw_mci_exynos_config_smu(host);
- return dw_mci_runtime_resume(dev);
+
+ return ret;
}
/**
@@ -487,6 +493,7 @@ static unsigned long exynos_dwmmc_caps[4] = {
static const struct dw_mci_drv_data exynos_drv_data = {
.caps = exynos_dwmmc_caps,
+ .num_caps = ARRAY_SIZE(exynos_dwmmc_caps),
.init = dw_mci_exynos_priv_init,
.set_ios = dw_mci_exynos_set_ios,
.parse_dt = dw_mci_exynos_parse_dt,
diff --git a/drivers/mmc/host/dw_mmc-hi3798cv200.c b/drivers/mmc/host/dw_mmc-hi3798cv200.c
new file mode 100644
index 000000000000..f9b333ff259e
--- /dev/null
+++ b/drivers/mmc/host/dw_mmc-hi3798cv200.c
@@ -0,0 +1,202 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 HiSilicon Technologies Co., Ltd.
+ */
+
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mmc/host.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+#include "dw_mmc.h"
+#include "dw_mmc-pltfm.h"
+
+#define ALL_INT_CLR 0x1ffff
+
+struct hi3798cv200_priv {
+ struct clk *sample_clk;
+ struct clk *drive_clk;
+};
+
+static void dw_mci_hi3798cv200_set_ios(struct dw_mci *host, struct mmc_ios *ios)
+{
+ struct hi3798cv200_priv *priv = host->priv;
+ u32 val;
+
+ val = mci_readl(host, UHS_REG);
+ if (ios->timing == MMC_TIMING_MMC_DDR52 ||
+ ios->timing == MMC_TIMING_UHS_DDR50)
+ val |= SDMMC_UHS_DDR;
+ else
+ val &= ~SDMMC_UHS_DDR;
+ mci_writel(host, UHS_REG, val);
+
+ val = mci_readl(host, ENABLE_SHIFT);
+ if (ios->timing == MMC_TIMING_MMC_DDR52)
+ val |= SDMMC_ENABLE_PHASE;
+ else
+ val &= ~SDMMC_ENABLE_PHASE;
+ mci_writel(host, ENABLE_SHIFT, val);
+
+ val = mci_readl(host, DDR_REG);
+ if (ios->timing == MMC_TIMING_MMC_HS400)
+ val |= SDMMC_DDR_HS400;
+ else
+ val &= ~SDMMC_DDR_HS400;
+ mci_writel(host, DDR_REG, val);
+
+ if (ios->timing == MMC_TIMING_MMC_HS ||
+ ios->timing == MMC_TIMING_LEGACY)
+ clk_set_phase(priv->drive_clk, 180);
+ else if (ios->timing == MMC_TIMING_MMC_HS200)
+ clk_set_phase(priv->drive_clk, 135);
+}
+
+static int dw_mci_hi3798cv200_execute_tuning(struct dw_mci_slot *slot,
+ u32 opcode)
+{
+ int degrees[] = { 0, 45, 90, 135, 180, 225, 270, 315 };
+ struct dw_mci *host = slot->host;
+ struct hi3798cv200_priv *priv = host->priv;
+ int raise_point = -1, fall_point = -1;
+ int err, prev_err = -1;
+ int found = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(degrees); i++) {
+ clk_set_phase(priv->sample_clk, degrees[i]);
+ mci_writel(host, RINTSTS, ALL_INT_CLR);
+
+ err = mmc_send_tuning(slot->mmc, opcode, NULL);
+ if (!err)
+ found = 1;
+
+ if (i > 0) {
+ if (err && !prev_err)
+ fall_point = i - 1;
+ if (!err && prev_err)
+ raise_point = i;
+ }
+
+ if (raise_point != -1 && fall_point != -1)
+ goto tuning_out;
+
+ prev_err = err;
+ err = 0;
+ }
+
+tuning_out:
+ if (found) {
+ if (raise_point == -1)
+ raise_point = 0;
+ if (fall_point == -1)
+ fall_point = ARRAY_SIZE(degrees) - 1;
+ if (fall_point < raise_point) {
+ if ((raise_point + fall_point) >
+ (ARRAY_SIZE(degrees) - 1))
+ i = fall_point / 2;
+ else
+ i = (raise_point + ARRAY_SIZE(degrees) - 1) / 2;
+ } else {
+ i = (raise_point + fall_point) / 2;
+ }
+
+ clk_set_phase(priv->sample_clk, degrees[i]);
+ dev_dbg(host->dev, "Tuning clk_sample[%d, %d], set[%d]\n",
+ raise_point, fall_point, degrees[i]);
+ } else {
+ dev_err(host->dev, "No valid clk_sample shift! use default\n");
+ err = -EINVAL;
+ }
+
+ mci_writel(host, RINTSTS, ALL_INT_CLR);
+ return err;
+}
+
+static int dw_mci_hi3798cv200_init(struct dw_mci *host)
+{
+ struct hi3798cv200_priv *priv;
+ int ret;
+
+ priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->sample_clk = devm_clk_get(host->dev, "ciu-sample");
+ if (IS_ERR(priv->sample_clk)) {
+ dev_err(host->dev, "failed to get ciu-sample clock\n");
+ return PTR_ERR(priv->sample_clk);
+ }
+
+ priv->drive_clk = devm_clk_get(host->dev, "ciu-drive");
+ if (IS_ERR(priv->drive_clk)) {
+ dev_err(host->dev, "failed to get ciu-drive clock\n");
+ return PTR_ERR(priv->drive_clk);
+ }
+
+ ret = clk_prepare_enable(priv->sample_clk);
+ if (ret) {
+ dev_err(host->dev, "failed to enable ciu-sample clock\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(priv->drive_clk);
+ if (ret) {
+ dev_err(host->dev, "failed to enable ciu-drive clock\n");
+ goto disable_sample_clk;
+ }
+
+ host->priv = priv;
+ return 0;
+
+disable_sample_clk:
+ clk_disable_unprepare(priv->sample_clk);
+ return ret;
+}
+
+static const struct dw_mci_drv_data hi3798cv200_data = {
+ .init = dw_mci_hi3798cv200_init,
+ .set_ios = dw_mci_hi3798cv200_set_ios,
+ .execute_tuning = dw_mci_hi3798cv200_execute_tuning,
+};
+
+static int dw_mci_hi3798cv200_probe(struct platform_device *pdev)
+{
+ return dw_mci_pltfm_register(pdev, &hi3798cv200_data);
+}
+
+static int dw_mci_hi3798cv200_remove(struct platform_device *pdev)
+{
+ struct dw_mci *host = platform_get_drvdata(pdev);
+ struct hi3798cv200_priv *priv = host->priv;
+
+ clk_disable_unprepare(priv->drive_clk);
+ clk_disable_unprepare(priv->sample_clk);
+
+ return dw_mci_pltfm_remove(pdev);
+}
+
+static const struct of_device_id dw_mci_hi3798cv200_match[] = {
+ { .compatible = "hisilicon,hi3798cv200-dw-mshc", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, dw_mci_hi3798cv200_match);
+static struct platform_driver dw_mci_hi3798cv200_driver = {
+ .probe = dw_mci_hi3798cv200_probe,
+ .remove = dw_mci_hi3798cv200_remove,
+ .driver = {
+ .name = "dwmmc_hi3798cv200",
+ .of_match_table = dw_mci_hi3798cv200_match,
+ },
+};
+module_platform_driver(dw_mci_hi3798cv200_driver);
+
+MODULE_DESCRIPTION("HiSilicon Hi3798CV200 Specific DW-MSHC Driver Extension");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:dwmmc_hi3798cv200");
diff --git a/drivers/mmc/host/dw_mmc-k3.c b/drivers/mmc/host/dw_mmc-k3.c
index 73fd75c3c824..89cdb3d533bb 100644
--- a/drivers/mmc/host/dw_mmc-k3.c
+++ b/drivers/mmc/host/dw_mmc-k3.c
@@ -135,6 +135,9 @@ static int dw_mci_hi6220_parse_dt(struct dw_mci *host)
if (priv->ctrl_id < 0)
priv->ctrl_id = 0;
+ if (priv->ctrl_id >= TIMING_MODE)
+ return -EINVAL;
+
host->priv = priv;
return 0;
}
@@ -207,6 +210,7 @@ static int dw_mci_hi6220_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
static const struct dw_mci_drv_data hi6220_data = {
.caps = dw_mci_hi6220_caps,
+ .num_caps = ARRAY_SIZE(dw_mci_hi6220_caps),
.switch_voltage = dw_mci_hi6220_switch_voltage,
.set_ios = dw_mci_hi6220_set_ios,
.parse_dt = dw_mci_hi6220_parse_dt,
diff --git a/drivers/mmc/host/dw_mmc-pci.c b/drivers/mmc/host/dw_mmc-pci.c
index ab8713297edb..3ad07d7b2c97 100644
--- a/drivers/mmc/host/dw_mmc-pci.c
+++ b/drivers/mmc/host/dw_mmc-pci.c
@@ -29,7 +29,6 @@
MMC_CAP_SDIO_IRQ)
static struct dw_mci_board pci_board_data = {
- .num_slots = 1,
.caps = DW_MCI_CAPABILITIES,
.bus_hz = 33 * 1000 * 1000,
.detect_delay_ms = 200,
diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c
index a3f1c2b30145..40d7de2eea12 100644
--- a/drivers/mmc/host/dw_mmc-rockchip.c
+++ b/drivers/mmc/host/dw_mmc-rockchip.c
@@ -282,11 +282,11 @@ static int dw_mci_rk3288_parse_dt(struct dw_mci *host)
priv->drv_clk = devm_clk_get(host->dev, "ciu-drive");
if (IS_ERR(priv->drv_clk))
- dev_dbg(host->dev, "ciu_drv not available\n");
+ dev_dbg(host->dev, "ciu-drive not available\n");
priv->sample_clk = devm_clk_get(host->dev, "ciu-sample");
if (IS_ERR(priv->sample_clk))
- dev_dbg(host->dev, "ciu_sample not available\n");
+ dev_dbg(host->dev, "ciu-sample not available\n");
host->priv = priv;
@@ -319,6 +319,7 @@ static const struct dw_mci_drv_data rk2928_drv_data = {
static const struct dw_mci_drv_data rk3288_drv_data = {
.caps = dw_mci_rk3288_dwmmc_caps,
+ .num_caps = ARRAY_SIZE(dw_mci_rk3288_dwmmc_caps),
.set_ios = dw_mci_rk3288_set_ios,
.execute_tuning = dw_mci_rk3288_execute_tuning,
.parse_dt = dw_mci_rk3288_parse_dt,
diff --git a/drivers/mmc/host/dw_mmc-zx.c b/drivers/mmc/host/dw_mmc-zx.c
index d38e94ae2b85..c06b5393312f 100644
--- a/drivers/mmc/host/dw_mmc-zx.c
+++ b/drivers/mmc/host/dw_mmc-zx.c
@@ -195,6 +195,7 @@ static unsigned long zx_dwmmc_caps[3] = {
static const struct dw_mci_drv_data zx_drv_data = {
.caps = zx_dwmmc_caps,
+ .num_caps = ARRAY_SIZE(zx_dwmmc_caps),
.execute_tuning = dw_mci_zx_execute_tuning,
.prepare_hs400_tuning = dw_mci_zx_prepare_hs400_tuning,
.parse_dt = dw_mci_zx_parse_dt,
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 0aa39975f33b..29a1afa81f66 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -147,24 +147,14 @@ static int dw_mci_req_show(struct seq_file *s, void *v)
return 0;
}
-
-static int dw_mci_req_open(struct inode *inode, struct file *file)
-{
- return single_open(file, dw_mci_req_show, inode->i_private);
-}
-
-static const struct file_operations dw_mci_req_fops = {
- .owner = THIS_MODULE,
- .open = dw_mci_req_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(dw_mci_req);
static int dw_mci_regs_show(struct seq_file *s, void *v)
{
struct dw_mci *host = s->private;
+ pm_runtime_get_sync(host->dev);
+
seq_printf(s, "STATUS:\t0x%08x\n", mci_readl(host, STATUS));
seq_printf(s, "RINTSTS:\t0x%08x\n", mci_readl(host, RINTSTS));
seq_printf(s, "CMD:\t0x%08x\n", mci_readl(host, CMD));
@@ -172,21 +162,11 @@ static int dw_mci_regs_show(struct seq_file *s, void *v)
seq_printf(s, "INTMASK:\t0x%08x\n", mci_readl(host, INTMASK));
seq_printf(s, "CLKENA:\t0x%08x\n", mci_readl(host, CLKENA));
- return 0;
-}
+ pm_runtime_put_autosuspend(host->dev);
-static int dw_mci_regs_open(struct inode *inode, struct file *file)
-{
- return single_open(file, dw_mci_regs_show, inode->i_private);
+ return 0;
}
-
-static const struct file_operations dw_mci_regs_fops = {
- .owner = THIS_MODULE,
- .open = dw_mci_regs_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(dw_mci_regs);
static void dw_mci_init_debugfs(struct dw_mci_slot *slot)
{
@@ -409,7 +389,9 @@ static inline void dw_mci_set_cto(struct dw_mci *host)
cto_div = (mci_readl(host, CLKDIV) & 0xff) * 2;
if (cto_div == 0)
cto_div = 1;
- cto_ms = DIV_ROUND_UP(MSEC_PER_SEC * cto_clks * cto_div, host->bus_hz);
+
+ cto_ms = DIV_ROUND_UP_ULL((u64)MSEC_PER_SEC * cto_clks * cto_div,
+ host->bus_hz);
/* add a bit spare time */
cto_ms += 10;
@@ -558,6 +540,7 @@ static int dw_mci_idmac_init(struct dw_mci *host)
(sizeof(struct idmac_desc_64addr) *
(i + 1))) >> 32;
/* Initialize reserved and buffer size fields to "0" */
+ p->des0 = 0;
p->des1 = 0;
p->des2 = 0;
p->des3 = 0;
@@ -580,6 +563,7 @@ static int dw_mci_idmac_init(struct dw_mci *host)
i++, p++) {
p->des3 = cpu_to_le32(host->sg_dma +
(sizeof(struct idmac_desc) * (i + 1)));
+ p->des0 = 0;
p->des1 = 0;
}
@@ -1795,8 +1779,8 @@ static bool dw_mci_reset(struct dw_mci *host)
}
if (host->use_dma == TRANS_MODE_IDMAC)
- /* It is also recommended that we reset and reprogram idmac */
- dw_mci_idmac_reset(host);
+ /* It is also required that we reinit idmac */
+ dw_mci_idmac_init(host);
ret = true;
@@ -1944,8 +1928,9 @@ static void dw_mci_set_drto(struct dw_mci *host)
drto_div = (mci_readl(host, CLKDIV) & 0xff) * 2;
if (drto_div == 0)
drto_div = 1;
- drto_ms = DIV_ROUND_UP(MSEC_PER_SEC * drto_clks * drto_div,
- host->bus_hz);
+
+ drto_ms = DIV_ROUND_UP_ULL((u64)MSEC_PER_SEC * drto_clks * drto_div,
+ host->bus_hz);
/* add a bit spare time */
drto_ms += 10;
@@ -2021,7 +2006,6 @@ static void dw_mci_tasklet_func(unsigned long priv)
set_bit(EVENT_CMD_COMPLETE, &host->completed_events);
err = dw_mci_command_complete(host, cmd);
if (cmd == mrq->sbc && !err) {
- prev_state = state = STATE_SENDING_CMD;
__dw_mci_start_request(host, host->slot,
mrq->cmd);
goto unlock;
@@ -2778,44 +2762,12 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static int dw_mci_init_slot(struct dw_mci *host)
+static int dw_mci_init_slot_caps(struct dw_mci_slot *slot)
{
- struct mmc_host *mmc;
- struct dw_mci_slot *slot;
+ struct dw_mci *host = slot->host;
const struct dw_mci_drv_data *drv_data = host->drv_data;
- int ctrl_id, ret;
- u32 freq[2];
-
- mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev);
- if (!mmc)
- return -ENOMEM;
-
- slot = mmc_priv(mmc);
- slot->id = 0;
- slot->sdio_id = host->sdio_id0 + slot->id;
- slot->mmc = mmc;
- slot->host = host;
- host->slot = slot;
-
- mmc->ops = &dw_mci_ops;
- if (device_property_read_u32_array(host->dev, "clock-freq-min-max",
- freq, 2)) {
- mmc->f_min = DW_MCI_FREQ_MIN;
- mmc->f_max = DW_MCI_FREQ_MAX;
- } else {
- dev_info(host->dev,
- "'clock-freq-min-max' property was deprecated.\n");
- mmc->f_min = freq[0];
- mmc->f_max = freq[1];
- }
-
- /*if there are external regulators, get them*/
- ret = mmc_regulator_get_supply(mmc);
- if (ret)
- goto err_host_allocated;
-
- if (!mmc->ocr_avail)
- mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+ struct mmc_host *mmc = slot->mmc;
+ int ctrl_id;
if (host->pdata->caps)
mmc->caps = host->pdata->caps;
@@ -2836,20 +2788,65 @@ static int dw_mci_init_slot(struct dw_mci *host)
} else {
ctrl_id = to_platform_device(host->dev)->id;
}
- if (drv_data && drv_data->caps)
+
+ if (drv_data && drv_data->caps) {
+ if (ctrl_id >= drv_data->num_caps) {
+ dev_err(host->dev, "invalid controller id %d\n",
+ ctrl_id);
+ return -EINVAL;
+ }
mmc->caps |= drv_data->caps[ctrl_id];
+ }
if (host->pdata->caps2)
mmc->caps2 = host->pdata->caps2;
- ret = mmc_of_parse(mmc);
- if (ret)
- goto err_host_allocated;
+ mmc->f_min = DW_MCI_FREQ_MIN;
+ if (!mmc->f_max)
+ mmc->f_max = DW_MCI_FREQ_MAX;
/* Process SDIO IRQs through the sdio_irq_work. */
if (mmc->caps & MMC_CAP_SDIO_IRQ)
mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
+ return 0;
+}
+
+static int dw_mci_init_slot(struct dw_mci *host)
+{
+ struct mmc_host *mmc;
+ struct dw_mci_slot *slot;
+ int ret;
+
+ mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev);
+ if (!mmc)
+ return -ENOMEM;
+
+ slot = mmc_priv(mmc);
+ slot->id = 0;
+ slot->sdio_id = host->sdio_id0 + slot->id;
+ slot->mmc = mmc;
+ slot->host = host;
+ host->slot = slot;
+
+ mmc->ops = &dw_mci_ops;
+
+ /*if there are external regulators, get them*/
+ ret = mmc_regulator_get_supply(mmc);
+ if (ret)
+ goto err_host_allocated;
+
+ if (!mmc->ocr_avail)
+ mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+
+ ret = mmc_of_parse(mmc);
+ if (ret)
+ goto err_host_allocated;
+
+ ret = dw_mci_init_slot_caps(slot);
+ if (ret)
+ goto err_host_allocated;
+
/* Useful defaults if platform data is unset. */
if (host->use_dma == TRANS_MODE_IDMAC) {
mmc->max_segs = host->ring_size;
@@ -3131,10 +3128,6 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
return ERR_PTR(-EPROBE_DEFER);
}
- /* find out number of slots supported */
- if (!device_property_read_u32(dev, "num-slots", &pdata->num_slots))
- dev_info(dev, "'num-slots' was deprecated.\n");
-
if (device_property_read_u32(dev, "fifo-depth", &pdata->fifo_depth))
dev_info(dev,
"fifo-depth property not found, using value of FIFOTH register as default\n");
diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
index e3124f06a47e..46e9f8ec5398 100644
--- a/drivers/mmc/host/dw_mmc.h
+++ b/drivers/mmc/host/dw_mmc.h
@@ -65,8 +65,7 @@ struct dw_mci_dma_slave {
* @fifo_reg: Pointer to MMIO registers for data FIFO
* @sg: Scatterlist entry currently being processed by PIO code, if any.
* @sg_miter: PIO mapping scatterlist iterator.
- * @cur_slot: The slot which is currently using the controller.
- * @mrq: The request currently being processed on @cur_slot,
+ * @mrq: The request currently being processed on @slot,
* or NULL if the controller is idle.
* @cmd: The command currently being sent to the card, or NULL.
* @data: The data currently being transferred, or NULL if no data
@@ -102,7 +101,6 @@ struct dw_mci_dma_slave {
* @bus_hz: The rate of @mck in Hz. This forms the basis for MMC bus
* rate and timeout calculations.
* @current_speed: Configured rate of the controller.
- * @num_slots: Number of slots available.
* @fifoth_val: The value of FIFOTH register.
* @verid: Denote Version ID.
* @dev: Device associated with the MMC controller.
@@ -134,17 +132,17 @@ struct dw_mci_dma_slave {
* =======
*
* @lock is a softirq-safe spinlock protecting @queue as well as
+ * @slot, @mrq and @state. These must always be updated
* at the same time while holding @lock.
+ * The @mrq field of struct dw_mci_slot is also protected by @lock,
+ * and must always be written at the same time as the slot is added to
+ * @queue.
*
* @irq_lock is an irq-safe spinlock protecting the INTMASK register
* to allow the interrupt handler to modify it directly. Held for only long
* enough to read-modify-write INTMASK and no other locks are grabbed when
* holding this one.
*
- * The @mrq field of struct dw_mci_slot is also protected by @lock,
- * and must always be written at the same time as the slot is added to
- * @queue.
- *
* @pending_events and @completed_events are accessed using atomic bit
* operations, so they don't need any locking.
*
@@ -253,8 +251,6 @@ struct dma_pdata;
/* Board platform data */
struct dw_mci_board {
- u32 num_slots;
-
unsigned int bus_hz; /* Clock speed at the cclk_in pad */
u32 caps; /* Capabilities */
@@ -318,11 +314,12 @@ struct dw_mci_board {
#define SDMMC_BUFADDR 0x098
#define SDMMC_CDTHRCTL 0x100
#define SDMMC_UHS_REG_EXT 0x108
+#define SDMMC_DDR_REG 0x10c
#define SDMMC_ENABLE_SHIFT 0x110
#define SDMMC_DATA(x) (x)
/*
-* Registers to support idmac 64-bit address mode
-*/
+ * Registers to support idmac 64-bit address mode
+ */
#define SDMMC_DBADDRL 0x088
#define SDMMC_DBADDRU 0x08c
#define SDMMC_IDSTS64 0x090
@@ -443,13 +440,19 @@ struct dw_mci_board {
#define SDMMC_CARD_WR_THR_EN BIT(2)
#define SDMMC_CARD_RD_THR_EN BIT(0)
/* UHS-1 register defines */
+#define SDMMC_UHS_DDR BIT(16)
#define SDMMC_UHS_18V BIT(0)
+/* DDR register defines */
+#define SDMMC_DDR_HS400 BIT(31)
+/* Enable shift register defines */
+#define SDMMC_ENABLE_PHASE BIT(0)
/* All ctrl reset bits */
#define SDMMC_CTRL_ALL_RESET_FLAGS \
(SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET)
/* FIFO register access macros. These should not change the data endian-ness
- * as they are written to memory to be dealt with by the upper layers */
+ * as they are written to memory to be dealt with by the upper layers
+ */
#define mci_fifo_readw(__reg) __raw_readw(__reg)
#define mci_fifo_readl(__reg) __raw_readl(__reg)
#define mci_fifo_readq(__reg) __raw_readq(__reg)
@@ -543,6 +546,7 @@ struct dw_mci_slot {
/**
* dw_mci driver data - dw-mshc implementation specific driver data.
* @caps: mmc subsystem specified capabilities of the controller(s).
+ * @num_caps: number of capabilities specified by @caps.
* @init: early implementation specific initialization.
* @set_ios: handle bus specific extensions.
* @parse_dt: parse implementation specific device tree properties.
@@ -554,6 +558,7 @@ struct dw_mci_slot {
*/
struct dw_mci_drv_data {
unsigned long *caps;
+ u32 num_caps;
int (*init)(struct dw_mci *host);
void (*set_ios)(struct dw_mci *host, struct mmc_ios *ios);
int (*parse_dt)(struct dw_mci *host);
diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
index 6457a7d8880f..cb274e822293 100644
--- a/drivers/mmc/host/mtk-sd.c
+++ b/drivers/mmc/host/mtk-sd.c
@@ -438,11 +438,23 @@ static const struct mtk_mmc_compatible mt2712_compat = {
.enhance_rx = true,
};
+static const struct mtk_mmc_compatible mt7622_compat = {
+ .clk_div_bits = 12,
+ .hs400_tune = false,
+ .pad_tune_reg = MSDC_PAD_TUNE0,
+ .async_fifo = true,
+ .data_tune = true,
+ .busy_check = true,
+ .stop_clk_fix = true,
+ .enhance_rx = true,
+};
+
static const struct of_device_id msdc_of_ids[] = {
{ .compatible = "mediatek,mt8135-mmc", .data = &mt8135_compat},
{ .compatible = "mediatek,mt8173-mmc", .data = &mt8173_compat},
{ .compatible = "mediatek,mt2701-mmc", .data = &mt2701_compat},
{ .compatible = "mediatek,mt2712-mmc", .data = &mt2712_compat},
+ { .compatible = "mediatek,mt7622-mmc", .data = &mt7622_compat},
{}
};
MODULE_DEVICE_TABLE(of, msdc_of_ids);
diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c
index 80943fa07db6..51e01f03fb99 100644
--- a/drivers/mmc/host/renesas_sdhi_core.c
+++ b/drivers/mmc/host/renesas_sdhi_core.c
@@ -38,7 +38,7 @@
#include "renesas_sdhi.h"
#include "tmio_mmc.h"
-#define EXT_ACC 0xe4
+#define HOST_MODE 0xe4
#define SDHI_VER_GEN2_SDR50 0x490c
#define SDHI_VER_RZ_A1 0x820b
@@ -76,7 +76,7 @@ static void renesas_sdhi_sdbuf_width(struct tmio_mmc_host *host, int width)
return;
}
- sd_ctrl_write16(host, EXT_ACC, val);
+ sd_ctrl_write16(host, HOST_MODE, val);
}
static int renesas_sdhi_clk_enable(struct tmio_mmc_host *host)
@@ -417,7 +417,7 @@ static int renesas_sdhi_write16_hook(struct tmio_mmc_host *host, int addr)
case CTL_SD_MEM_CARD_OPT:
case CTL_TRANSACTION_CTL:
case CTL_DMA_ENABLE:
- case EXT_ACC:
+ case HOST_MODE:
if (host->pdata->flags & TMIO_MMC_HAVE_CBSY)
bit = TMIO_STAT_CMD_BUSY;
/* fallthrough */
diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
index 7c03cfead6f9..8e0acd197c43 100644
--- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c
+++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
@@ -71,9 +71,8 @@ static struct renesas_sdhi_scc rcar_gen3_scc_taps[] = {
};
static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = {
- .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE |
- TMIO_MMC_CLK_ACTUAL | TMIO_MMC_HAVE_CBSY |
- TMIO_MMC_MIN_RCAR2,
+ .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL |
+ TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2,
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
MMC_CAP_CMD23,
.bus_shift = 2,
@@ -145,7 +144,6 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host,
u32 dtran_mode = DTRAN_MODE_BUS_WID_TH | DTRAN_MODE_ADDR_MODE;
enum dma_data_direction dir;
int ret;
- u32 irq_mask;
/* This DMAC cannot handle if sg_len is not 1 */
WARN_ON(host->sg_len > 1);
@@ -157,11 +155,9 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host,
if (data->flags & MMC_DATA_READ) {
dtran_mode |= DTRAN_MODE_CH_NUM_CH1;
dir = DMA_FROM_DEVICE;
- irq_mask = TMIO_STAT_RXRDY;
} else {
dtran_mode |= DTRAN_MODE_CH_NUM_CH0;
dir = DMA_TO_DEVICE;
- irq_mask = TMIO_STAT_TXRQ;
}
ret = dma_map_sg(&host->pdev->dev, sg, host->sg_len, dir);
@@ -170,9 +166,6 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host,
renesas_sdhi_internal_dmac_enable_dma(host, true);
- /* disable PIO irqs to avoid "PIO IRQ in DMA mode!" */
- tmio_mmc_disable_mmc_irqs(host, irq_mask);
-
/* set dma parameters */
renesas_sdhi_internal_dmac_dm_write(host, DM_CM_DTRAN_MODE,
dtran_mode);
diff --git a/drivers/mmc/host/renesas_sdhi_sys_dmac.c b/drivers/mmc/host/renesas_sdhi_sys_dmac.c
index 82d757c480b2..848e50c1638a 100644
--- a/drivers/mmc/host/renesas_sdhi_sys_dmac.c
+++ b/drivers/mmc/host/renesas_sdhi_sys_dmac.c
@@ -40,8 +40,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_WRPROTECT_DISABLE |
- TMIO_MMC_CLK_ACTUAL,
+ .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL,
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ,
};
@@ -58,9 +57,8 @@ static struct renesas_sdhi_scc rcar_gen2_scc_taps[] = {
};
static const struct renesas_sdhi_of_data of_rcar_gen2_compatible = {
- .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE |
- TMIO_MMC_CLK_ACTUAL | TMIO_MMC_HAVE_CBSY |
- TMIO_MMC_MIN_RCAR2,
+ .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL |
+ TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2,
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
MMC_CAP_CMD23,
.dma_buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES,
@@ -79,9 +77,8 @@ static struct renesas_sdhi_scc rcar_gen3_scc_taps[] = {
};
static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = {
- .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE |
- TMIO_MMC_CLK_ACTUAL | TMIO_MMC_HAVE_CBSY |
- TMIO_MMC_MIN_RCAR2,
+ .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL |
+ TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2,
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
MMC_CAP_CMD23,
.bus_shift = 2,
@@ -205,8 +202,6 @@ static void renesas_sdhi_sys_dmac_start_dma_rx(struct tmio_mmc_host *host)
return;
}
- tmio_mmc_disable_mmc_irqs(host, TMIO_STAT_RXRDY);
-
/* The only sg element can be unaligned, use our bounce buffer then */
if (!aligned) {
sg_init_one(&host->bounce_sg, host->bounce_buf, sg->length);
@@ -280,8 +275,6 @@ static void renesas_sdhi_sys_dmac_start_dma_tx(struct tmio_mmc_host *host)
return;
}
- tmio_mmc_disable_mmc_irqs(host, TMIO_STAT_TXRQ);
-
/* The only sg element can be unaligned, use our bounce buffer then */
if (!aligned) {
unsigned long flags;
diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c
index 4065da58789d..32321bd596d8 100644
--- a/drivers/mmc/host/sdhci-acpi.c
+++ b/drivers/mmc/host/sdhci-acpi.c
@@ -680,7 +680,7 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
host->hw_name = "ACPI";
host->ops = &sdhci_acpi_ops_dflt;
host->irq = platform_get_irq(pdev, 0);
- if (host->irq <= 0) {
+ if (host->irq < 0) {
err = -EINVAL;
goto err_free;
}
diff --git a/drivers/mmc/host/sdhci-iproc.c b/drivers/mmc/host/sdhci-iproc.c
index 61666d269771..0ef741bc515d 100644
--- a/drivers/mmc/host/sdhci-iproc.c
+++ b/drivers/mmc/host/sdhci-iproc.c
@@ -214,6 +214,7 @@ static const struct sdhci_pltfm_data sdhci_bcm2835_pltfm_data = {
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
SDHCI_QUIRK_MISSING_CAPS |
SDHCI_QUIRK_NO_HISPD_BIT,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
.ops = &sdhci_iproc_32only_ops,
};
diff --git a/drivers/mmc/host/sdhci-omap.c b/drivers/mmc/host/sdhci-omap.c
index 628bfe9a3d17..1456abd5eeb9 100644
--- a/drivers/mmc/host/sdhci-omap.c
+++ b/drivers/mmc/host/sdhci-omap.c
@@ -25,17 +25,32 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
+#include <linux/pinctrl/consumer.h>
#include "sdhci-pltfm.h"
#define SDHCI_OMAP_CON 0x12c
#define CON_DW8 BIT(5)
#define CON_DMA_MASTER BIT(20)
+#define CON_DDR BIT(19)
+#define CON_CLKEXTFREE BIT(16)
+#define CON_PADEN BIT(15)
#define CON_INIT BIT(1)
#define CON_OD BIT(0)
+#define SDHCI_OMAP_DLL 0x0134
+#define DLL_SWT BIT(20)
+#define DLL_FORCE_SR_C_SHIFT 13
+#define DLL_FORCE_SR_C_MASK (0x7f << DLL_FORCE_SR_C_SHIFT)
+#define DLL_FORCE_VALUE BIT(12)
+#define DLL_CALIB BIT(1)
+
#define SDHCI_OMAP_CMD 0x20c
+#define SDHCI_OMAP_PSTATE 0x0224
+#define PSTATE_DLEV_DAT0 BIT(20)
+#define PSTATE_DATI BIT(1)
+
#define SDHCI_OMAP_HCTL 0x228
#define HCTL_SDBP BIT(8)
#define HCTL_SDVS_SHIFT 9
@@ -56,12 +71,16 @@
#define SDHCI_OMAP_AC12 0x23c
#define AC12_V1V8_SIGEN BIT(19)
+#define AC12_SCLK_SEL BIT(23)
#define SDHCI_OMAP_CAPA 0x240
#define CAPA_VS33 BIT(24)
#define CAPA_VS30 BIT(25)
#define CAPA_VS18 BIT(26)
+#define SDHCI_OMAP_CAPA2 0x0244
+#define CAPA2_TSDR50 BIT(13)
+
#define SDHCI_OMAP_TIMEOUT 1 /* 1 msec */
#define SYSCTL_CLKD_MAX 0x3FF
@@ -70,8 +89,14 @@
#define IOV_3V0 3000000 /* 300000 uV */
#define IOV_3V3 3300000 /* 330000 uV */
+#define MAX_PHASE_DELAY 0x7C
+
+/* sdhci-omap controller flags */
+#define SDHCI_OMAP_REQUIRE_IODELAY BIT(0)
+
struct sdhci_omap_data {
u32 offset;
+ u8 flags;
};
struct sdhci_omap_host {
@@ -82,8 +107,16 @@ struct sdhci_omap_host {
struct sdhci_host *host;
u8 bus_mode;
u8 power_mode;
+ u8 timing;
+ u8 flags;
+
+ struct pinctrl *pinctrl;
+ struct pinctrl_state **pinctrl_state;
};
+static void sdhci_omap_start_clock(struct sdhci_omap_host *omap_host);
+static void sdhci_omap_stop_clock(struct sdhci_omap_host *omap_host);
+
static inline u32 sdhci_omap_readl(struct sdhci_omap_host *host,
unsigned int offset)
{
@@ -191,6 +224,178 @@ static void sdhci_omap_conf_bus_power(struct sdhci_omap_host *omap_host,
}
}
+static inline void sdhci_omap_set_dll(struct sdhci_omap_host *omap_host,
+ int count)
+{
+ int i;
+ u32 reg;
+
+ reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_DLL);
+ reg |= DLL_FORCE_VALUE;
+ reg &= ~DLL_FORCE_SR_C_MASK;
+ reg |= (count << DLL_FORCE_SR_C_SHIFT);
+ sdhci_omap_writel(omap_host, SDHCI_OMAP_DLL, reg);
+
+ reg |= DLL_CALIB;
+ sdhci_omap_writel(omap_host, SDHCI_OMAP_DLL, reg);
+ for (i = 0; i < 1000; i++) {
+ reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_DLL);
+ if (reg & DLL_CALIB)
+ break;
+ }
+ reg &= ~DLL_CALIB;
+ sdhci_omap_writel(omap_host, SDHCI_OMAP_DLL, reg);
+}
+
+static void sdhci_omap_disable_tuning(struct sdhci_omap_host *omap_host)
+{
+ u32 reg;
+
+ reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12);
+ reg &= ~AC12_SCLK_SEL;
+ sdhci_omap_writel(omap_host, SDHCI_OMAP_AC12, reg);
+
+ reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_DLL);
+ reg &= ~(DLL_FORCE_VALUE | DLL_SWT);
+ sdhci_omap_writel(omap_host, SDHCI_OMAP_DLL, reg);
+}
+
+static int sdhci_omap_execute_tuning(struct mmc_host *mmc, u32 opcode)
+{
+ 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);
+ struct device *dev = omap_host->dev;
+ struct mmc_ios *ios = &mmc->ios;
+ u32 start_window = 0, max_window = 0;
+ u8 cur_match, prev_match = 0;
+ u32 length = 0, max_len = 0;
+ u32 ier = host->ier;
+ u32 phase_delay = 0;
+ int ret = 0;
+ u32 reg;
+
+ pltfm_host = sdhci_priv(host);
+ omap_host = sdhci_pltfm_priv(pltfm_host);
+ dev = omap_host->dev;
+
+ /* clock tuning is not needed for upto 52MHz */
+ if (ios->clock <= 52000000)
+ return 0;
+
+ reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA2);
+ if (ios->timing == MMC_TIMING_UHS_SDR50 && !(reg & CAPA2_TSDR50))
+ return 0;
+
+ reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_DLL);
+ reg |= DLL_SWT;
+ sdhci_omap_writel(omap_host, SDHCI_OMAP_DLL, reg);
+
+ /*
+ * OMAP5/DRA74X/DRA72x Errata i802:
+ * DCRC error interrupts (MMCHS_STAT[21] DCRC=0x1) can occur
+ * during the tuning procedure. So disable it during the
+ * tuning procedure.
+ */
+ ier &= ~SDHCI_INT_DATA_CRC;
+ sdhci_writel(host, ier, SDHCI_INT_ENABLE);
+ sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE);
+
+ while (phase_delay <= MAX_PHASE_DELAY) {
+ sdhci_omap_set_dll(omap_host, phase_delay);
+
+ cur_match = !mmc_send_tuning(mmc, opcode, NULL);
+ if (cur_match) {
+ if (prev_match) {
+ length++;
+ } else {
+ start_window = phase_delay;
+ length = 1;
+ }
+ }
+
+ if (length > max_len) {
+ max_window = start_window;
+ max_len = length;
+ }
+
+ prev_match = cur_match;
+ phase_delay += 4;
+ }
+
+ if (!max_len) {
+ dev_err(dev, "Unable to find match\n");
+ ret = -EIO;
+ goto tuning_error;
+ }
+
+ reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12);
+ if (!(reg & AC12_SCLK_SEL)) {
+ ret = -EIO;
+ goto tuning_error;
+ }
+
+ phase_delay = max_window + 4 * (max_len >> 1);
+ sdhci_omap_set_dll(omap_host, phase_delay);
+
+ goto ret;
+
+tuning_error:
+ dev_err(dev, "Tuning failed\n");
+ sdhci_omap_disable_tuning(omap_host);
+
+ret:
+ sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
+ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
+ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
+ return ret;
+}
+
+static int sdhci_omap_card_busy(struct mmc_host *mmc)
+{
+ u32 reg, ac12;
+ int ret = false;
+ struct sdhci_host *host = mmc_priv(mmc);
+ struct sdhci_pltfm_host *pltfm_host;
+ struct sdhci_omap_host *omap_host;
+ u32 ier = host->ier;
+
+ pltfm_host = sdhci_priv(host);
+ omap_host = sdhci_pltfm_priv(pltfm_host);
+
+ reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
+ ac12 = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12);
+ reg &= ~CON_CLKEXTFREE;
+ if (ac12 & AC12_V1V8_SIGEN)
+ reg |= CON_CLKEXTFREE;
+ reg |= CON_PADEN;
+ sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg);
+
+ disable_irq(host->irq);
+ ier |= SDHCI_INT_CARD_INT;
+ sdhci_writel(host, ier, SDHCI_INT_ENABLE);
+ sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE);
+
+ /*
+ * Delay is required for PSTATE to correctly reflect
+ * DLEV/CLEV values after PADEN is set.
+ */
+ usleep_range(50, 100);
+ reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_PSTATE);
+ if ((reg & PSTATE_DATI) || !(reg & PSTATE_DLEV_DAT0))
+ ret = true;
+
+ reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
+ reg &= ~(CON_CLKEXTFREE | CON_PADEN);
+ sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg);
+
+ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
+ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
+ enable_irq(host->irq);
+
+ return ret;
+}
+
static int sdhci_omap_start_signal_voltage_switch(struct mmc_host *mmc,
struct mmc_ios *ios)
{
@@ -244,6 +449,39 @@ static int sdhci_omap_start_signal_voltage_switch(struct mmc_host *mmc,
return 0;
}
+static void sdhci_omap_set_timing(struct sdhci_omap_host *omap_host, u8 timing)
+{
+ int ret;
+ struct pinctrl_state *pinctrl_state;
+ struct device *dev = omap_host->dev;
+
+ if (!(omap_host->flags & SDHCI_OMAP_REQUIRE_IODELAY))
+ return;
+
+ if (omap_host->timing == timing)
+ return;
+
+ sdhci_omap_stop_clock(omap_host);
+
+ pinctrl_state = omap_host->pinctrl_state[timing];
+ ret = pinctrl_select_state(omap_host->pinctrl, pinctrl_state);
+ if (ret) {
+ dev_err(dev, "failed to select pinctrl state\n");
+ return;
+ }
+
+ sdhci_omap_start_clock(omap_host);
+ omap_host->timing = timing;
+}
+
+static void sdhci_omap_set_power_mode(struct sdhci_omap_host *omap_host,
+ u8 power_mode)
+{
+ if (omap_host->bus_mode == MMC_POWER_OFF)
+ sdhci_omap_disable_tuning(omap_host);
+ omap_host->power_mode = power_mode;
+}
+
static void sdhci_omap_set_bus_mode(struct sdhci_omap_host *omap_host,
unsigned int mode)
{
@@ -272,7 +510,9 @@ static void sdhci_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
omap_host = sdhci_pltfm_priv(pltfm_host);
sdhci_omap_set_bus_mode(omap_host, ios->bus_mode);
+ sdhci_omap_set_timing(omap_host, ios->timing);
sdhci_set_ios(mmc, ios);
+ sdhci_omap_set_power_mode(omap_host, ios->power_mode);
}
static u16 sdhci_omap_calc_divisor(struct sdhci_pltfm_host *host,
@@ -401,8 +641,26 @@ static void sdhci_omap_init_74_clocks(struct sdhci_host *host, u8 power_mode)
sdhci_omap_writel(omap_host, SDHCI_OMAP_STAT, INT_CC_EN);
enable_irq(host->irq);
+}
- omap_host->power_mode = power_mode;
+static void sdhci_omap_set_uhs_signaling(struct sdhci_host *host,
+ unsigned int timing)
+{
+ u32 reg;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
+
+ sdhci_omap_stop_clock(omap_host);
+
+ reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
+ if (timing == MMC_TIMING_UHS_DDR50 || timing == MMC_TIMING_MMC_DDR52)
+ reg |= CON_DDR;
+ else
+ reg &= ~CON_DDR;
+ sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg);
+
+ sdhci_set_uhs_signaling(host, timing);
+ sdhci_omap_start_clock(omap_host);
}
static struct sdhci_ops sdhci_omap_ops = {
@@ -414,7 +672,7 @@ static struct sdhci_ops sdhci_omap_ops = {
.set_bus_width = sdhci_omap_set_bus_width,
.platform_send_init_74_clocks = sdhci_omap_init_74_clocks,
.reset = sdhci_reset,
- .set_uhs_signaling = sdhci_set_uhs_signaling,
+ .set_uhs_signaling = sdhci_omap_set_uhs_signaling,
};
static int sdhci_omap_set_capabilities(struct sdhci_omap_host *omap_host)
@@ -453,14 +711,15 @@ static const struct sdhci_pltfm_data sdhci_omap_pdata = {
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
SDHCI_QUIRK_NO_HISPD_BIT |
SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC,
- .quirks2 = SDHCI_QUIRK2_NO_1_8_V |
- SDHCI_QUIRK2_ACMD23_BROKEN |
+ .quirks2 = SDHCI_QUIRK2_ACMD23_BROKEN |
+ SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
SDHCI_QUIRK2_RSP_136_HAS_CRC,
.ops = &sdhci_omap_ops,
};
static const struct sdhci_omap_data dra7_data = {
.offset = 0x200,
+ .flags = SDHCI_OMAP_REQUIRE_IODELAY,
};
static const struct of_device_id omap_sdhci_match[] = {
@@ -469,6 +728,108 @@ static const struct of_device_id omap_sdhci_match[] = {
};
MODULE_DEVICE_TABLE(of, omap_sdhci_match);
+static struct pinctrl_state
+*sdhci_omap_iodelay_pinctrl_state(struct sdhci_omap_host *omap_host, char *mode,
+ u32 *caps, u32 capmask)
+{
+ struct device *dev = omap_host->dev;
+ struct pinctrl_state *pinctrl_state = ERR_PTR(-ENODEV);
+
+ if (!(*caps & capmask))
+ goto ret;
+
+ 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;
+ }
+
+ret:
+ return pinctrl_state;
+}
+
+static int sdhci_omap_config_iodelay_pinctrl_state(struct sdhci_omap_host
+ *omap_host)
+{
+ struct device *dev = omap_host->dev;
+ struct sdhci_host *host = omap_host->host;
+ struct mmc_host *mmc = host->mmc;
+ u32 *caps = &mmc->caps;
+ u32 *caps2 = &mmc->caps2;
+ struct pinctrl_state *state;
+ struct pinctrl_state **pinctrl_state;
+
+ if (!(omap_host->flags & SDHCI_OMAP_REQUIRE_IODELAY))
+ return 0;
+
+ pinctrl_state = devm_kzalloc(dev, sizeof(*pinctrl_state) *
+ (MMC_TIMING_MMC_HS200 + 1), GFP_KERNEL);
+ if (!pinctrl_state)
+ return -ENOMEM;
+
+ omap_host->pinctrl = devm_pinctrl_get(omap_host->dev);
+ if (IS_ERR(omap_host->pinctrl)) {
+ dev_err(dev, "Cannot get pinctrl\n");
+ return PTR_ERR(omap_host->pinctrl);
+ }
+
+ state = pinctrl_lookup_state(omap_host->pinctrl, "default");
+ if (IS_ERR(state)) {
+ dev_err(dev, "no pinctrl state for default mode\n");
+ return PTR_ERR(state);
+ }
+ pinctrl_state[MMC_TIMING_LEGACY] = state;
+
+ state = sdhci_omap_iodelay_pinctrl_state(omap_host, "sdr104", caps,
+ MMC_CAP_UHS_SDR104);
+ if (!IS_ERR(state))
+ pinctrl_state[MMC_TIMING_UHS_SDR104] = state;
+
+ state = sdhci_omap_iodelay_pinctrl_state(omap_host, "ddr50", caps,
+ MMC_CAP_UHS_DDR50);
+ if (!IS_ERR(state))
+ pinctrl_state[MMC_TIMING_UHS_DDR50] = state;
+
+ state = sdhci_omap_iodelay_pinctrl_state(omap_host, "sdr50", caps,
+ MMC_CAP_UHS_SDR50);
+ if (!IS_ERR(state))
+ pinctrl_state[MMC_TIMING_UHS_SDR50] = state;
+
+ state = sdhci_omap_iodelay_pinctrl_state(omap_host, "sdr25", caps,
+ MMC_CAP_UHS_SDR25);
+ if (!IS_ERR(state))
+ pinctrl_state[MMC_TIMING_UHS_SDR25] = state;
+
+ state = sdhci_omap_iodelay_pinctrl_state(omap_host, "sdr12", caps,
+ MMC_CAP_UHS_SDR12);
+ if (!IS_ERR(state))
+ pinctrl_state[MMC_TIMING_UHS_SDR12] = state;
+
+ state = sdhci_omap_iodelay_pinctrl_state(omap_host, "ddr_1_8v", caps,
+ MMC_CAP_1_8V_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);
+ if (!IS_ERR(state))
+ pinctrl_state[MMC_TIMING_SD_HS] = state;
+
+ state = sdhci_omap_iodelay_pinctrl_state(omap_host, "hs", caps,
+ MMC_CAP_MMC_HIGHSPEED);
+ if (!IS_ERR(state))
+ pinctrl_state[MMC_TIMING_MMC_HS] = state;
+
+ state = sdhci_omap_iodelay_pinctrl_state(omap_host, "hs200_1_8v", caps2,
+ MMC_CAP2_HS200_1_8V_SDR);
+ if (!IS_ERR(state))
+ pinctrl_state[MMC_TIMING_MMC_HS200] = state;
+
+ omap_host->pinctrl_state = pinctrl_state;
+
+ return 0;
+}
+
static int sdhci_omap_probe(struct platform_device *pdev)
{
int ret;
@@ -504,6 +865,9 @@ static int sdhci_omap_probe(struct platform_device *pdev)
omap_host->host = host;
omap_host->base = host->ioaddr;
omap_host->dev = dev;
+ omap_host->power_mode = MMC_POWER_UNDEFINED;
+ omap_host->timing = MMC_TIMING_LEGACY;
+ omap_host->flags = data->flags;
host->ioaddr += offset;
mmc = host->mmc;
@@ -552,10 +916,16 @@ 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;
sdhci_read_caps(host);
host->caps |= SDHCI_CAN_DO_ADMA2;
diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
index 6d1a983e6227..787434e5589d 100644
--- a/drivers/mmc/host/sdhci-pci-core.c
+++ b/drivers/mmc/host/sdhci-pci-core.c
@@ -41,18 +41,25 @@ static void sdhci_pci_hw_reset(struct sdhci_host *host);
static int sdhci_pci_init_wakeup(struct sdhci_pci_chip *chip)
{
mmc_pm_flag_t pm_flags = 0;
+ bool cap_cd_wake = false;
int i;
for (i = 0; i < chip->num_slots; i++) {
struct sdhci_pci_slot *slot = chip->slots[i];
- if (slot)
+ if (slot) {
pm_flags |= slot->host->mmc->pm_flags;
+ if (slot->host->mmc->caps & MMC_CAP_CD_WAKE)
+ cap_cd_wake = true;
+ }
}
- return device_set_wakeup_enable(&chip->pdev->dev,
- (pm_flags & MMC_PM_KEEP_POWER) &&
- (pm_flags & MMC_PM_WAKE_SDIO_IRQ));
+ if ((pm_flags & MMC_PM_KEEP_POWER) && (pm_flags & MMC_PM_WAKE_SDIO_IRQ))
+ return device_wakeup_enable(&chip->pdev->dev);
+ else if (!cap_cd_wake)
+ return device_wakeup_disable(&chip->pdev->dev);
+
+ return 0;
}
static int sdhci_pci_suspend_host(struct sdhci_pci_chip *chip)
@@ -76,6 +83,9 @@ static int sdhci_pci_suspend_host(struct sdhci_pci_chip *chip)
ret = sdhci_suspend_host(host);
if (ret)
goto err_pci_suspend;
+
+ if (device_may_wakeup(&chip->pdev->dev))
+ mmc_gpio_set_cd_wake(host->mmc, true);
}
return 0;
@@ -99,6 +109,8 @@ int sdhci_pci_resume_host(struct sdhci_pci_chip *chip)
ret = sdhci_resume_host(slot->host);
if (ret)
return ret;
+
+ mmc_gpio_set_cd_wake(slot->host->mmc, false);
}
return 0;
@@ -654,9 +666,36 @@ static void byt_read_dsm(struct sdhci_pci_slot *slot)
slot->chip->rpm_retune = intel_host->d3_retune;
}
-static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot)
+static int intel_execute_tuning(struct mmc_host *mmc, u32 opcode)
{
+ int err = sdhci_execute_tuning(mmc, opcode);
+ struct sdhci_host *host = mmc_priv(mmc);
+
+ if (err)
+ return err;
+
+ /*
+ * Tuning can leave the IP in an active state (Buffer Read Enable bit
+ * set) which prevents the entry to low power states (i.e. S0i3). Data
+ * reset will clear it.
+ */
+ sdhci_reset(host, SDHCI_RESET_DATA);
+
+ return 0;
+}
+
+static void byt_probe_slot(struct sdhci_pci_slot *slot)
+{
+ struct mmc_host_ops *ops = &slot->host->mmc_host_ops;
+
byt_read_dsm(slot);
+
+ ops->execute_tuning = intel_execute_tuning;
+}
+
+static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot)
+{
+ byt_probe_slot(slot);
slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE |
MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR |
MMC_CAP_CMD_DURING_TFR |
@@ -685,26 +724,8 @@ static int glk_emmc_probe_slot(struct sdhci_pci_slot *slot)
return ret;
}
-static void glk_cqe_enable(struct mmc_host *mmc)
-{
- struct sdhci_host *host = mmc_priv(mmc);
- u32 reg;
-
- /*
- * CQE gets stuck if it sees Buffer Read Enable bit set, which can be
- * the case after tuning, so ensure the buffer is drained.
- */
- reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
- while (reg & SDHCI_DATA_AVAILABLE) {
- sdhci_readl(host, SDHCI_BUFFER);
- reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
- }
-
- sdhci_cqe_enable(mmc);
-}
-
static const struct cqhci_host_ops glk_cqhci_ops = {
- .enable = glk_cqe_enable,
+ .enable = sdhci_cqe_enable,
.disable = sdhci_cqe_disable,
.dumpregs = sdhci_pci_dumpregs,
};
@@ -779,7 +800,7 @@ static int ni_byt_sdio_probe_slot(struct sdhci_pci_slot *slot)
{
int err;
- byt_read_dsm(slot);
+ byt_probe_slot(slot);
err = ni_set_max_freq(slot);
if (err)
@@ -792,7 +813,7 @@ static int ni_byt_sdio_probe_slot(struct sdhci_pci_slot *slot)
static int byt_sdio_probe_slot(struct sdhci_pci_slot *slot)
{
- byt_read_dsm(slot);
+ byt_probe_slot(slot);
slot->host->mmc->caps |= MMC_CAP_POWER_OFF_CARD | MMC_CAP_NONREMOVABLE |
MMC_CAP_WAIT_WHILE_BUSY;
return 0;
@@ -800,7 +821,7 @@ static int byt_sdio_probe_slot(struct sdhci_pci_slot *slot)
static int byt_sd_probe_slot(struct sdhci_pci_slot *slot)
{
- byt_read_dsm(slot);
+ byt_probe_slot(slot);
slot->host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY |
MMC_CAP_AGGRESSIVE_PM | MMC_CAP_CD_WAKE;
slot->cd_idx = 0;
@@ -1689,6 +1710,9 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
if (device_can_wakeup(&pdev->dev))
host->mmc->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
+ if (host->mmc->caps & MMC_CAP_CD_WAKE)
+ device_init_wakeup(&pdev->dev, true);
+
if (slot->cd_idx >= 0) {
ret = mmc_gpiod_request_cd(host->mmc, NULL, slot->cd_idx,
slot->cd_override_level, 0, NULL);
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 2020e57ffa7e..2ededa7f43df 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -2899,6 +2899,14 @@ static irqreturn_t sdhci_thread_irq(int irq, void *dev_id)
\*****************************************************************************/
#ifdef CONFIG_PM
+
+static bool sdhci_cd_irq_can_wakeup(struct sdhci_host *host)
+{
+ return mmc_card_is_removable(host->mmc) &&
+ !(host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) &&
+ !mmc_can_gpio_cd(host->mmc);
+}
+
/*
* To enable wakeup events, the corresponding events have to be enabled in
* the Interrupt Status Enable register too. See 'Table 1-6: Wakeup Signal
@@ -2915,13 +2923,18 @@ static bool sdhci_enable_irq_wakeups(struct sdhci_host *host)
u8 wake_val = 0;
u8 val;
- if (!(host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)) {
+ if (sdhci_cd_irq_can_wakeup(host)) {
wake_val |= SDHCI_WAKE_ON_INSERT | SDHCI_WAKE_ON_REMOVE;
irq_val |= SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE;
}
- wake_val |= SDHCI_WAKE_ON_INT;
- irq_val |= SDHCI_INT_CARD_INT;
+ if (mmc_card_wake_sdio_irq(host->mmc)) {
+ wake_val |= SDHCI_WAKE_ON_INT;
+ irq_val |= SDHCI_INT_CARD_INT;
+ }
+
+ if (!irq_val)
+ return false;
val = sdhci_readb(host, SDHCI_WAKE_UP_CONTROL);
val &= ~mask;
diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c
index 7bb00c68a756..4c2a1f8ddbf3 100644
--- a/drivers/mmc/host/sh_mmcif.c
+++ b/drivers/mmc/host/sh_mmcif.c
@@ -7,13 +7,6 @@
* 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.
- *
- *
- * TODO
- * 1. DMA
- * 2. Power management
- * 3. Handle MMC errors better
- *
*/
/*
@@ -67,7 +60,6 @@
#include <linux/module.h>
#define DRIVER_NAME "sh_mmcif"
-#define DRIVER_VERSION "2010-04-28"
/* CE_CMD_SET */
#define CMD_MASK 0x3f000000
diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c
index bad612d6f879..20cfb20418f3 100644
--- a/drivers/mmc/host/sunxi-mmc.c
+++ b/drivers/mmc/host/sunxi-mmc.c
@@ -268,6 +268,7 @@ struct sunxi_mmc_cfg {
};
struct sunxi_mmc_host {
+ struct device *dev;
struct mmc_host *mmc;
struct reset_control *reset;
const struct sunxi_mmc_cfg *cfg;
@@ -1165,6 +1166,80 @@ static const struct of_device_id sunxi_mmc_of_match[] = {
};
MODULE_DEVICE_TABLE(of, sunxi_mmc_of_match);
+static int sunxi_mmc_enable(struct sunxi_mmc_host *host)
+{
+ int ret;
+
+ if (!IS_ERR(host->reset)) {
+ ret = reset_control_reset(host->reset);
+ if (ret) {
+ dev_err(host->dev, "Couldn't reset the MMC controller (%d)\n",
+ ret);
+ return ret;
+ }
+ }
+
+ ret = clk_prepare_enable(host->clk_ahb);
+ if (ret) {
+ dev_err(host->dev, "Couldn't enable the bus clocks (%d)\n", ret);
+ goto error_assert_reset;
+ }
+
+ ret = clk_prepare_enable(host->clk_mmc);
+ if (ret) {
+ dev_err(host->dev, "Enable mmc clk err %d\n", ret);
+ goto error_disable_clk_ahb;
+ }
+
+ ret = clk_prepare_enable(host->clk_output);
+ if (ret) {
+ dev_err(host->dev, "Enable output clk err %d\n", ret);
+ goto error_disable_clk_mmc;
+ }
+
+ ret = clk_prepare_enable(host->clk_sample);
+ if (ret) {
+ dev_err(host->dev, "Enable sample clk err %d\n", ret);
+ goto error_disable_clk_output;
+ }
+
+ /*
+ * Sometimes the controller asserts the irq on boot for some reason,
+ * make sure the controller is in a sane state before enabling irqs.
+ */
+ ret = sunxi_mmc_reset_host(host);
+ if (ret)
+ goto error_disable_clk_sample;
+
+ return 0;
+
+error_disable_clk_sample:
+ clk_disable_unprepare(host->clk_sample);
+error_disable_clk_output:
+ clk_disable_unprepare(host->clk_output);
+error_disable_clk_mmc:
+ clk_disable_unprepare(host->clk_mmc);
+error_disable_clk_ahb:
+ clk_disable_unprepare(host->clk_ahb);
+error_assert_reset:
+ if (!IS_ERR(host->reset))
+ reset_control_assert(host->reset);
+ return ret;
+}
+
+static void sunxi_mmc_disable(struct sunxi_mmc_host *host)
+{
+ sunxi_mmc_reset_host(host);
+
+ clk_disable_unprepare(host->clk_sample);
+ clk_disable_unprepare(host->clk_output);
+ clk_disable_unprepare(host->clk_mmc);
+ clk_disable_unprepare(host->clk_ahb);
+
+ if (!IS_ERR(host->reset))
+ reset_control_assert(host->reset);
+}
+
static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host,
struct platform_device *pdev)
{
@@ -1214,66 +1289,21 @@ static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host,
if (PTR_ERR(host->reset) == -EPROBE_DEFER)
return PTR_ERR(host->reset);
- ret = clk_prepare_enable(host->clk_ahb);
- if (ret) {
- dev_err(&pdev->dev, "Enable ahb clk err %d\n", ret);
- return ret;
- }
-
- ret = clk_prepare_enable(host->clk_mmc);
- if (ret) {
- dev_err(&pdev->dev, "Enable mmc clk err %d\n", ret);
- goto error_disable_clk_ahb;
- }
-
- ret = clk_prepare_enable(host->clk_output);
- if (ret) {
- dev_err(&pdev->dev, "Enable output clk err %d\n", ret);
- goto error_disable_clk_mmc;
- }
-
- ret = clk_prepare_enable(host->clk_sample);
- if (ret) {
- dev_err(&pdev->dev, "Enable sample clk err %d\n", ret);
- goto error_disable_clk_output;
- }
-
- if (!IS_ERR(host->reset)) {
- ret = reset_control_reset(host->reset);
- if (ret) {
- dev_err(&pdev->dev, "reset err %d\n", ret);
- goto error_disable_clk_sample;
- }
- }
-
- /*
- * Sometimes the controller asserts the irq on boot for some reason,
- * make sure the controller is in a sane state before enabling irqs.
- */
- ret = sunxi_mmc_reset_host(host);
+ ret = sunxi_mmc_enable(host);
if (ret)
- goto error_assert_reset;
+ return ret;
host->irq = platform_get_irq(pdev, 0);
if (host->irq <= 0) {
ret = -EINVAL;
- goto error_assert_reset;
+ goto error_disable_mmc;
}
return devm_request_threaded_irq(&pdev->dev, host->irq, sunxi_mmc_irq,
sunxi_mmc_handle_manual_stop, 0, "sunxi-mmc", host);
-error_assert_reset:
- if (!IS_ERR(host->reset))
- reset_control_assert(host->reset);
-error_disable_clk_sample:
- clk_disable_unprepare(host->clk_sample);
-error_disable_clk_output:
- clk_disable_unprepare(host->clk_output);
-error_disable_clk_mmc:
- clk_disable_unprepare(host->clk_mmc);
-error_disable_clk_ahb:
- clk_disable_unprepare(host->clk_ahb);
+error_disable_mmc:
+ sunxi_mmc_disable(host);
return ret;
}
@@ -1288,8 +1318,10 @@ static int sunxi_mmc_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "mmc alloc host failed\n");
return -ENOMEM;
}
+ platform_set_drvdata(pdev, mmc);
host = mmc_priv(mmc);
+ host->dev = &pdev->dev;
host->mmc = mmc;
spin_lock_init(&host->lock);
@@ -1353,7 +1385,6 @@ static int sunxi_mmc_probe(struct platform_device *pdev)
goto error_free_dma;
dev_info(&pdev->dev, "base:0x%p irq:%u\n", host->reg_base, host->irq);
- platform_set_drvdata(pdev, mmc);
return 0;
error_free_dma:
@@ -1370,16 +1401,7 @@ static int sunxi_mmc_remove(struct platform_device *pdev)
mmc_remove_host(mmc);
disable_irq(host->irq);
- sunxi_mmc_reset_host(host);
-
- if (!IS_ERR(host->reset))
- reset_control_assert(host->reset);
-
- clk_disable_unprepare(host->clk_sample);
- clk_disable_unprepare(host->clk_output);
- clk_disable_unprepare(host->clk_mmc);
- clk_disable_unprepare(host->clk_ahb);
-
+ sunxi_mmc_disable(host);
dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
mmc_free_host(mmc);
diff --git a/drivers/mmc/host/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c
index 33494241245a..e30df9ad8197 100644
--- a/drivers/mmc/host/tmio_mmc_core.c
+++ b/drivers/mmc/host/tmio_mmc_core.c
@@ -278,7 +278,6 @@ static void tmio_mmc_reset_work(struct work_struct *work)
host->cmd = NULL;
host->data = NULL;
- host->force_pio = false;
spin_unlock_irqrestore(&host->lock, flags);
@@ -350,8 +349,6 @@ static int tmio_mmc_start_command(struct tmio_mmc_host *host,
c |= TRANSFER_READ;
}
- if (!host->native_hotplug)
- irq_mask &= ~(TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT);
tmio_mmc_enable_mmc_irqs(host, irq_mask);
/* Fire off the command */
@@ -623,15 +620,21 @@ static void tmio_mmc_cmd_irq(struct tmio_mmc_host *host, unsigned int stat)
*/
if (host->data && (!cmd->error || cmd->error == -EILSEQ)) {
if (host->data->flags & MMC_DATA_READ) {
- if (host->force_pio || !host->chan_rx)
+ if (host->force_pio || !host->chan_rx) {
tmio_mmc_enable_mmc_irqs(host, TMIO_MASK_READOP);
- else
+ } else {
+ tmio_mmc_disable_mmc_irqs(host,
+ TMIO_MASK_READOP);
tasklet_schedule(&host->dma_issue);
+ }
} else {
- if (host->force_pio || !host->chan_tx)
+ if (host->force_pio || !host->chan_tx) {
tmio_mmc_enable_mmc_irqs(host, TMIO_MASK_WRITEOP);
- else
+ } else {
+ tmio_mmc_disable_mmc_irqs(host,
+ TMIO_MASK_WRITEOP);
tasklet_schedule(&host->dma_issue);
+ }
}
} else {
schedule_work(&host->done);
@@ -755,6 +758,7 @@ static int tmio_mmc_start_data(struct tmio_mmc_host *host,
tmio_mmc_init_sg(host, data);
host->data = data;
+ host->force_pio = false;
/* Set transfer length / blocksize */
sd_ctrl_write16(host, CTL_SD_XFER_LEN, data->blksz);
@@ -846,7 +850,6 @@ static void tmio_process_mrq(struct tmio_mmc_host *host,
return;
fail:
- host->force_pio = false;
host->mrq = NULL;
mrq->cmd->error = ret;
mmc_request_done(host->mmc, mrq);
@@ -896,7 +899,6 @@ static void tmio_mmc_finish_request(struct tmio_mmc_host *host)
if (host->cmd != mrq->sbc) {
host->cmd = NULL;
host->data = NULL;
- host->force_pio = false;
host->mrq = NULL;
}
@@ -1061,10 +1063,17 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
static int tmio_mmc_get_ro(struct mmc_host *mmc)
{
struct tmio_mmc_host *host = mmc_priv(mmc);
- struct tmio_mmc_data *pdata = host->pdata;
- return !((pdata->flags & TMIO_MMC_WRPROTECT_DISABLE) ||
- (sd_ctrl_read16_and_16_as_32(host, CTL_STATUS) & TMIO_STAT_WRPROTECT));
+ return !(sd_ctrl_read16_and_16_as_32(host, CTL_STATUS) &
+ TMIO_STAT_WRPROTECT);
+}
+
+static int tmio_mmc_get_cd(struct mmc_host *mmc)
+{
+ struct tmio_mmc_host *host = mmc_priv(mmc);
+
+ return !!(sd_ctrl_read16_and_16_as_32(host, CTL_STATUS) &
+ TMIO_STAT_SIGSTATE);
}
static int tmio_multi_io_quirk(struct mmc_card *card,
@@ -1082,7 +1091,7 @@ static const struct mmc_host_ops tmio_mmc_ops = {
.request = tmio_mmc_request,
.set_ios = tmio_mmc_set_ios,
.get_ro = tmio_mmc_get_ro,
- .get_cd = mmc_gpio_get_cd,
+ .get_cd = tmio_mmc_get_cd,
.enable_sdio_irq = tmio_mmc_enable_sdio_irq,
.multi_io_quirk = tmio_multi_io_quirk,
.hw_reset = tmio_mmc_hw_reset,
@@ -1114,15 +1123,20 @@ static int tmio_mmc_init_ocr(struct tmio_mmc_host *host)
}
static void tmio_mmc_of_parse(struct platform_device *pdev,
- struct tmio_mmc_data *pdata)
+ struct mmc_host *mmc)
{
const struct device_node *np = pdev->dev.of_node;
if (!np)
return;
+ /*
+ * DEPRECATED:
+ * For new platforms, please use "disable-wp" instead of
+ * "toshiba,mmc-wrprotect-disable"
+ */
if (of_get_property(np, "toshiba,mmc-wrprotect-disable", NULL))
- pdata->flags |= TMIO_MMC_WRPROTECT_DISABLE;
+ mmc->caps2 |= MMC_CAP2_NO_WRITE_PROTECT;
}
struct tmio_mmc_host *tmio_mmc_host_alloc(struct platform_device *pdev,
@@ -1157,7 +1171,7 @@ struct tmio_mmc_host *tmio_mmc_host_alloc(struct platform_device *pdev,
goto free;
}
- tmio_mmc_of_parse(pdev, pdata);
+ tmio_mmc_of_parse(pdev, mmc);
platform_set_drvdata(pdev, host);
@@ -1181,7 +1195,6 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host)
struct tmio_mmc_data *pdata = _host->pdata;
struct mmc_host *mmc = _host->mmc;
int ret;
- u32 irq_mask = TMIO_MASK_CMD;
/*
* Check the sanity of mmc->f_min to prevent tmio_mmc_set_clock() from
@@ -1230,6 +1243,9 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host)
if (mmc_can_gpio_ro(mmc))
_host->ops.get_ro = mmc_gpio_get_ro;
+ if (mmc_can_gpio_cd(mmc))
+ _host->ops.get_cd = mmc_gpio_get_cd;
+
_host->native_hotplug = !(mmc_can_gpio_cd(mmc) ||
mmc->caps & MMC_CAP_NEEDS_POLL ||
!mmc_card_is_removable(mmc));
@@ -1260,15 +1276,9 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host)
_host->sdcard_irq_mask = sd_ctrl_read16_and_16_as_32(_host, CTL_IRQ_MASK);
tmio_mmc_disable_mmc_irqs(_host, TMIO_MASK_ALL);
- /* Unmask the IRQs we want to know about */
- if (!_host->chan_rx)
- irq_mask |= TMIO_MASK_READOP;
- if (!_host->chan_tx)
- irq_mask |= TMIO_MASK_WRITEOP;
- if (!_host->native_hotplug)
- irq_mask &= ~(TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT);
-
- _host->sdcard_irq_mask &= ~irq_mask;
+ if (_host->native_hotplug)
+ tmio_mmc_enable_mmc_irqs(_host,
+ TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT);
spin_lock_init(&_host->lock);
mutex_init(&_host->ios_lock);
@@ -1367,6 +1377,10 @@ int tmio_mmc_host_runtime_resume(struct device *dev)
if (host->clk_cache)
tmio_mmc_set_clock(host, host->clk_cache);
+ if (host->native_hotplug)
+ tmio_mmc_enable_mmc_irqs(host,
+ TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT);
+
tmio_mmc_enable_dma(host, true);
if (tmio_mmc_can_retune(host) && host->select_tuning(host))
diff --git a/drivers/mmc/host/ushc.c b/drivers/mmc/host/ushc.c
index 1d843357422e..81dac17064d7 100644
--- a/drivers/mmc/host/ushc.c
+++ b/drivers/mmc/host/ushc.c
@@ -309,8 +309,6 @@ static void ushc_request(struct mmc_host *mmc, struct mmc_request *req)
/* Submit CSW. */
ret = usb_submit_urb(ushc->csw_urb, GFP_ATOMIC);
- if (ret < 0)
- goto out;
out:
spin_unlock_irqrestore(&ushc->lock, flags);