summaryrefslogtreecommitdiffstats
path: root/drivers/mmc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/core/block.c7
-rw-r--r--drivers/mmc/core/mmc.c8
-rw-r--r--drivers/mmc/core/mmc_ops.h1
-rw-r--r--drivers/mmc/core/slot-gpio.c42
-rw-r--r--drivers/mmc/host/Kconfig8
-rw-r--r--drivers/mmc/host/cqhci-core.c4
-rw-r--r--drivers/mmc/host/dw_mmc.c3
-rw-r--r--drivers/mmc/host/mmci.c4
-rw-r--r--drivers/mmc/host/moxart-mmc.c16
-rw-r--r--drivers/mmc/host/mtk-sd.c99
-rw-r--r--drivers/mmc/host/omap_hsmmc.c12
-rw-r--r--drivers/mmc/host/sdhci-of-arasan.c29
-rw-r--r--drivers/mmc/host/sdhci-omap.c61
-rw-r--r--drivers/mmc/host/sdhci-pci-o2micro.c2
-rw-r--r--drivers/mmc/host/sdhci-s3c.c1
-rw-r--r--drivers/mmc/host/sdhci-sprd.c13
-rw-r--r--drivers/mmc/host/sdhci.c23
17 files changed, 267 insertions, 66 deletions
diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c
index 431af5e8be2f..61590cf7a7b1 100644
--- a/drivers/mmc/core/block.c
+++ b/drivers/mmc/core/block.c
@@ -2442,9 +2442,14 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
/* used in ->open, must be set before add_disk: */
if (area_type == MMC_BLK_DATA_AREA_MAIN)
dev_set_drvdata(&card->dev, md);
- device_add_disk(md->parent, md->disk, mmc_disk_attr_groups);
+ ret = device_add_disk(md->parent, md->disk, mmc_disk_attr_groups);
+ if (ret)
+ goto err_cleanup_queue;
return md;
+ err_cleanup_queue:
+ blk_cleanup_queue(md->disk->queue);
+ blk_mq_free_tag_set(&md->queue.tag_set);
err_kfree:
kfree(md);
out:
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 29e58ffae379..b1c1716dacf0 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -1224,6 +1224,14 @@ static int mmc_select_hs400(struct mmc_card *card)
mmc_set_timing(host, MMC_TIMING_MMC_HS400);
mmc_set_bus_speed(card);
+ if (host->ops->execute_hs400_tuning) {
+ mmc_retune_disable(host);
+ err = host->ops->execute_hs400_tuning(host, card);
+ mmc_retune_enable(host);
+ if (err)
+ goto out_err;
+ }
+
if (host->ops->hs400_complete)
host->ops->hs400_complete(host);
diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h
index ae25ffc2e870..e5e94567a9a9 100644
--- a/drivers/mmc/core/mmc_ops.h
+++ b/drivers/mmc/core/mmc_ops.h
@@ -38,7 +38,6 @@ int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp);
int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
int mmc_bus_test(struct mmc_card *card, u8 bus_width);
int mmc_can_ext_csd(struct mmc_card *card);
-int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd);
int mmc_switch_status(struct mmc_card *card, bool crc_err_fatal);
bool mmc_prepare_busy_cmd(struct mmc_host *host, struct mmc_command *cmd,
unsigned int timeout_ms);
diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c
index 05e907451df9..dd2a4b6ab6ad 100644
--- a/drivers/mmc/core/slot-gpio.c
+++ b/drivers/mmc/core/slot-gpio.c
@@ -39,24 +39,24 @@ static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id)
int mmc_gpio_alloc(struct mmc_host *host)
{
- struct mmc_gpio *ctx = devm_kzalloc(host->parent,
- sizeof(*ctx), GFP_KERNEL);
-
- if (ctx) {
- ctx->cd_debounce_delay_ms = 200;
- ctx->cd_label = devm_kasprintf(host->parent, GFP_KERNEL,
- "%s cd", dev_name(host->parent));
- if (!ctx->cd_label)
- return -ENOMEM;
- ctx->ro_label = devm_kasprintf(host->parent, GFP_KERNEL,
- "%s ro", dev_name(host->parent));
- if (!ctx->ro_label)
- return -ENOMEM;
- host->slot.handler_priv = ctx;
- host->slot.cd_irq = -EINVAL;
- }
+ const char *devname = dev_name(host->parent);
+ struct mmc_gpio *ctx;
+
+ ctx = devm_kzalloc(host->parent, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->cd_debounce_delay_ms = 200;
+ ctx->cd_label = devm_kasprintf(host->parent, GFP_KERNEL, "%s cd", devname);
+ if (!ctx->cd_label)
+ return -ENOMEM;
+ ctx->ro_label = devm_kasprintf(host->parent, GFP_KERNEL, "%s ro", devname);
+ if (!ctx->ro_label)
+ return -ENOMEM;
+ host->slot.handler_priv = ctx;
+ host->slot.cd_irq = -EINVAL;
- return ctx ? 0 : -ENOMEM;
+ return 0;
}
int mmc_gpio_get_ro(struct mmc_host *host)
@@ -178,6 +178,10 @@ int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id,
if (IS_ERR(desc))
return PTR_ERR(desc);
+ /* Update default label if no con_id provided */
+ if (!con_id)
+ gpiod_set_consumer_name(desc, ctx->cd_label);
+
if (debounce) {
ret = gpiod_set_debounce(desc, debounce);
if (ret < 0)
@@ -226,6 +230,10 @@ int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id,
if (IS_ERR(desc))
return PTR_ERR(desc);
+ /* Update default label if no con_id provided */
+ if (!con_id)
+ gpiod_set_consumer_name(desc, ctx->ro_label);
+
if (debounce) {
ret = gpiod_set_debounce(desc, debounce);
if (ret < 0)
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 95b3511b0560..ed1bab8f3a8f 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -315,15 +315,17 @@ config MMC_SDHCI_TEGRA
If unsure, say N.
config MMC_SDHCI_S3C
- tristate "SDHCI support on Samsung S3C SoC"
+ tristate "SDHCI support on Samsung S3C/S5P/Exynos SoC"
depends on MMC_SDHCI
depends on PLAT_SAMSUNG || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
help
This selects the Secure Digital Host Controller Interface (SDHCI)
often referrered to as the HSMMC block in some of the Samsung S3C
- range of SoC.
+ (S3C2416, S3C2443, S3C6410), S5Pv210 and Exynos (Exynso4210,
+ Exynos4412) SoCs.
- If you have a controller with this interface, say Y or M here.
+ If you have a controller with this interface (thereforeyou build for
+ such Samsung SoC), say Y or M here.
If unsure, say N.
diff --git a/drivers/mmc/host/cqhci-core.c b/drivers/mmc/host/cqhci-core.c
index 38559a956330..ca8329d55f43 100644
--- a/drivers/mmc/host/cqhci-core.c
+++ b/drivers/mmc/host/cqhci-core.c
@@ -899,8 +899,8 @@ static bool cqhci_timeout(struct mmc_host *mmc, struct mmc_request *mrq,
spin_unlock_irqrestore(&cq_host->lock, flags);
if (timed_out) {
- pr_err("%s: cqhci: timeout for tag %d\n",
- mmc_hostname(mmc), tag);
+ pr_err("%s: cqhci: timeout for tag %d, qcnt %d\n",
+ mmc_hostname(mmc), tag, cq_host->qcnt);
cqhci_dumpregs(cq_host);
}
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 380f9aa56eb2..1e8f1bb3cad7 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -2086,7 +2086,8 @@ static void dw_mci_tasklet_func(struct tasklet_struct *t)
* delayed. Allowing the transfer to take place
* avoids races and keeps things simple.
*/
- if (err != -ETIMEDOUT) {
+ if (err != -ETIMEDOUT &&
+ host->dir_status == DW_MCI_RECV_STATUS) {
state = STATE_SENDING_DATA;
continue;
}
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 3765e2f4ad98..c9cacd4d5b22 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -1394,6 +1394,10 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
} else if (host->variant->busy_timeout && busy_resp &&
status & MCI_DATATIMEOUT) {
cmd->error = -ETIMEDOUT;
+ /*
+ * This will wake up mmci_irq_thread() which will issue
+ * a hardware reset of the MMCI block.
+ */
host->irq_action = IRQ_WAKE_THREAD;
} else {
cmd->resp[0] = readl(base + MMCIRESPONSE0);
diff --git a/drivers/mmc/host/moxart-mmc.c b/drivers/mmc/host/moxart-mmc.c
index 6c9d38132f74..7b9fcef490de 100644
--- a/drivers/mmc/host/moxart-mmc.c
+++ b/drivers/mmc/host/moxart-mmc.c
@@ -621,6 +621,14 @@ static int moxart_probe(struct platform_device *pdev)
ret = -EPROBE_DEFER;
goto out;
}
+ if (!IS_ERR(host->dma_chan_tx)) {
+ dma_release_channel(host->dma_chan_tx);
+ host->dma_chan_tx = NULL;
+ }
+ if (!IS_ERR(host->dma_chan_rx)) {
+ dma_release_channel(host->dma_chan_rx);
+ host->dma_chan_rx = NULL;
+ }
dev_dbg(dev, "PIO mode transfer enabled\n");
host->have_dma = false;
} else {
@@ -675,6 +683,10 @@ static int moxart_probe(struct platform_device *pdev)
return 0;
out:
+ if (!IS_ERR_OR_NULL(host->dma_chan_tx))
+ dma_release_channel(host->dma_chan_tx);
+ if (!IS_ERR_OR_NULL(host->dma_chan_rx))
+ dma_release_channel(host->dma_chan_rx);
if (mmc)
mmc_free_host(mmc);
return ret;
@@ -687,9 +699,9 @@ static int moxart_remove(struct platform_device *pdev)
dev_set_drvdata(&pdev->dev, NULL);
- if (!IS_ERR(host->dma_chan_tx))
+ if (!IS_ERR_OR_NULL(host->dma_chan_tx))
dma_release_channel(host->dma_chan_tx);
- if (!IS_ERR(host->dma_chan_rx))
+ if (!IS_ERR_OR_NULL(host->dma_chan_rx))
dma_release_channel(host->dma_chan_rx);
mmc_remove_host(mmc);
mmc_free_host(mmc);
diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
index 4dfc246c5f95..b124cfee05a1 100644
--- a/drivers/mmc/host/mtk-sd.c
+++ b/drivers/mmc/host/mtk-sd.c
@@ -8,6 +8,7 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
+#include <linux/iopoll.h>
#include <linux/ioport.h>
#include <linux/irq.h>
#include <linux/of_address.h>
@@ -258,6 +259,7 @@
#define MSDC_PAD_TUNE_RD_SEL (0x1 << 13) /* RW */
#define MSDC_PAD_TUNE_CMD_SEL (0x1 << 21) /* RW */
+#define PAD_DS_TUNE_DLY_SEL (0x1 << 0) /* RW */
#define PAD_DS_TUNE_DLY1 (0x1f << 2) /* RW */
#define PAD_DS_TUNE_DLY2 (0x1f << 7) /* RW */
#define PAD_DS_TUNE_DLY3 (0x1f << 12) /* RW */
@@ -301,6 +303,11 @@
#define PAD_CMD_RD_RXDLY_SEL (0x1 << 11) /* RW */
#define PAD_CMD_TX_DLY (0x1f << 12) /* RW */
+/* EMMC50_PAD_DS_TUNE mask */
+#define PAD_DS_DLY_SEL (0x1 << 16) /* RW */
+#define PAD_DS_DLY1 (0x1f << 10) /* RW */
+#define PAD_DS_DLY3 (0x1f << 0) /* RW */
+
#define REQ_CMD_EIO (0x1 << 0)
#define REQ_CMD_TMO (0x1 << 1)
#define REQ_DAT_ERR (0x1 << 2)
@@ -448,11 +455,13 @@ struct msdc_host {
bool vqmmc_enabled;
u32 latch_ck;
u32 hs400_ds_delay;
+ u32 hs400_ds_dly3;
u32 hs200_cmd_int_delay; /* cmd internal delay for HS200/SDR104 */
u32 hs400_cmd_int_delay; /* cmd internal delay for HS400 */
bool hs400_cmd_resp_sel_rising;
/* cmd response sample selection for HS400 */
bool hs400_mode; /* current eMMC will run at hs400 mode */
+ bool hs400_tuning; /* hs400 mode online tuning */
bool internal_cd; /* Use internal card-detect logic */
bool cqhci; /* support eMMC hw cmdq */
struct msdc_save_para save_para; /* used when gate HCLK */
@@ -961,7 +970,7 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
}
static inline u32 msdc_cmd_find_resp(struct msdc_host *host,
- struct mmc_request *mrq, struct mmc_command *cmd)
+ struct mmc_command *cmd)
{
u32 resp;
@@ -997,7 +1006,7 @@ static inline u32 msdc_cmd_prepare_raw_cmd(struct msdc_host *host,
* stop << 14 | rw << 13 | dtype << 11 | rsptyp << 7 | brk << 6 | opcode
*/
u32 opcode = cmd->opcode;
- u32 resp = msdc_cmd_find_resp(host, mrq, cmd);
+ u32 resp = msdc_cmd_find_resp(host, cmd);
u32 rawcmd = (opcode & 0x3f) | ((resp & 0x7) << 7);
host->cmd_rsp = resp;
@@ -1043,8 +1052,8 @@ static inline u32 msdc_cmd_prepare_raw_cmd(struct msdc_host *host,
return rawcmd;
}
-static void msdc_start_data(struct msdc_host *host, struct mmc_request *mrq,
- struct mmc_command *cmd, struct mmc_data *data)
+static void msdc_start_data(struct msdc_host *host, struct mmc_command *cmd,
+ struct mmc_data *data)
{
bool read;
@@ -1112,8 +1121,7 @@ static void msdc_recheck_sdio_irq(struct msdc_host *host)
}
}
-static void msdc_track_cmd_data(struct msdc_host *host,
- struct mmc_command *cmd, struct mmc_data *data)
+static void msdc_track_cmd_data(struct msdc_host *host, struct mmc_command *cmd)
{
if (host->error)
dev_dbg(host->dev, "%s: cmd=%d arg=%08X; host->error=0x%08X\n",
@@ -1134,7 +1142,7 @@ static void msdc_request_done(struct msdc_host *host, struct mmc_request *mrq)
host->mrq = NULL;
spin_unlock_irqrestore(&host->lock, flags);
- msdc_track_cmd_data(host, mrq->cmd, mrq->data);
+ msdc_track_cmd_data(host, mrq->cmd);
if (mrq->data)
msdc_unprepare_data(host, mrq->data);
if (host->error)
@@ -1190,7 +1198,8 @@ static bool msdc_cmd_done(struct msdc_host *host, int events,
if (!sbc_error && !(events & MSDC_INT_CMDRDY)) {
if (events & MSDC_INT_CMDTMO ||
(cmd->opcode != MMC_SEND_TUNING_BLOCK &&
- cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200))
+ cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200 &&
+ !host->hs400_tuning))
/*
* should not clear fifo/interrupt as the tune data
* may have alreay come when cmd19/cmd21 gets response
@@ -1287,7 +1296,8 @@ static void msdc_cmd_next(struct msdc_host *host,
if ((cmd->error &&
!(cmd->error == -EILSEQ &&
(cmd->opcode == MMC_SEND_TUNING_BLOCK ||
- cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200))) ||
+ cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200 ||
+ host->hs400_tuning))) ||
(mrq->sbc && mrq->sbc->error))
msdc_request_done(host, mrq);
else if (cmd == mrq->sbc)
@@ -1295,7 +1305,7 @@ static void msdc_cmd_next(struct msdc_host *host,
else if (!cmd->data)
msdc_request_done(host, mrq);
else
- msdc_start_data(host, mrq, cmd, cmd->data);
+ msdc_start_data(host, cmd, cmd->data);
}
static void msdc_ops_request(struct mmc_host *mmc, struct mmc_request *mrq)
@@ -2251,6 +2261,67 @@ static int msdc_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios)
return 0;
}
+static int msdc_execute_hs400_tuning(struct mmc_host *mmc, struct mmc_card *card)
+{
+ struct msdc_host *host = mmc_priv(mmc);
+ struct msdc_delay_phase dly1_delay;
+ u32 val, result_dly1 = 0;
+ u8 *ext_csd;
+ int i, ret;
+
+ if (host->top_base) {
+ sdr_set_bits(host->top_base + EMMC50_PAD_DS_TUNE,
+ PAD_DS_DLY_SEL);
+ if (host->hs400_ds_dly3)
+ sdr_set_field(host->top_base + EMMC50_PAD_DS_TUNE,
+ PAD_DS_DLY3, host->hs400_ds_dly3);
+ } else {
+ sdr_set_bits(host->base + PAD_DS_TUNE, PAD_DS_TUNE_DLY_SEL);
+ if (host->hs400_ds_dly3)
+ sdr_set_field(host->base + PAD_DS_TUNE,
+ PAD_DS_TUNE_DLY3, host->hs400_ds_dly3);
+ }
+
+ host->hs400_tuning = true;
+ for (i = 0; i < PAD_DELAY_MAX; i++) {
+ if (host->top_base)
+ sdr_set_field(host->top_base + EMMC50_PAD_DS_TUNE,
+ PAD_DS_DLY1, i);
+ else
+ sdr_set_field(host->base + PAD_DS_TUNE,
+ PAD_DS_TUNE_DLY1, i);
+ ret = mmc_get_ext_csd(card, &ext_csd);
+ if (!ret)
+ result_dly1 |= (1 << i);
+ }
+ host->hs400_tuning = false;
+
+ dly1_delay = get_best_delay(host, result_dly1);
+ if (dly1_delay.maxlen == 0) {
+ dev_err(host->dev, "Failed to get DLY1 delay!\n");
+ goto fail;
+ }
+ if (host->top_base)
+ sdr_set_field(host->top_base + EMMC50_PAD_DS_TUNE,
+ PAD_DS_DLY1, dly1_delay.final_phase);
+ else
+ sdr_set_field(host->base + PAD_DS_TUNE,
+ PAD_DS_TUNE_DLY1, dly1_delay.final_phase);
+
+ if (host->top_base)
+ val = readl(host->top_base + EMMC50_PAD_DS_TUNE);
+ else
+ val = readl(host->base + PAD_DS_TUNE);
+
+ dev_info(host->dev, "Fianl PAD_DS_TUNE: 0x%x\n", val);
+
+ return 0;
+
+fail:
+ dev_err(host->dev, "Failed to tuning DS pin delay!\n");
+ return -EIO;
+}
+
static void msdc_hw_reset(struct mmc_host *mmc)
{
struct msdc_host *host = mmc_priv(mmc);
@@ -2330,6 +2401,7 @@ static void msdc_cqe_enable(struct mmc_host *mmc)
static void msdc_cqe_disable(struct mmc_host *mmc, bool recovery)
{
struct msdc_host *host = mmc_priv(mmc);
+ unsigned int val = 0;
/* disable cmdq irq */
sdr_clr_bits(host->base + MSDC_INTEN, MSDC_INT_CMDQ);
@@ -2339,6 +2411,9 @@ static void msdc_cqe_disable(struct mmc_host *mmc, bool recovery)
if (recovery) {
sdr_set_field(host->base + MSDC_DMA_CTRL,
MSDC_DMA_CTRL_STOP, 1);
+ if (WARN_ON(readl_poll_timeout(host->base + MSDC_DMA_CFG, val,
+ !(val & MSDC_DMA_CFG_STS), 1, 3000)))
+ return;
msdc_reset_hw(host);
}
}
@@ -2377,6 +2452,7 @@ static const struct mmc_host_ops mt_msdc_ops = {
.card_busy = msdc_card_busy,
.execute_tuning = msdc_execute_tuning,
.prepare_hs400_tuning = msdc_prepare_hs400_tuning,
+ .execute_hs400_tuning = msdc_execute_hs400_tuning,
.hw_reset = msdc_hw_reset,
};
@@ -2396,6 +2472,9 @@ static void msdc_of_property_parse(struct platform_device *pdev,
of_property_read_u32(pdev->dev.of_node, "hs400-ds-delay",
&host->hs400_ds_delay);
+ of_property_read_u32(pdev->dev.of_node, "mediatek,hs400-ds-dly3",
+ &host->hs400_ds_dly3);
+
of_property_read_u32(pdev->dev.of_node, "mediatek,hs200-cmd-int-delay",
&host->hs200_cmd_int_delay);
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 2f8038d69f67..9dafcbf969d9 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -702,11 +702,6 @@ static void omap_hsmmc_context_save(struct omap_hsmmc_host *host)
#else
-static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
-{
- return 0;
-}
-
static void omap_hsmmc_context_save(struct omap_hsmmc_host *host)
{
}
@@ -1515,7 +1510,7 @@ static void omap_hsmmc_init_card(struct mmc_host *mmc, struct mmc_card *card)
* REVISIT: should be moved to sdio core and made more
* general e.g. by expanding the DT bindings of child nodes
* to provide a mechanism to provide this information:
- * Documentation/devicetree/bindings/mmc/mmc-card.txt
+ * Documentation/devicetree/bindings/mmc/mmc-card.yaml
*/
np = of_get_compatible_child(np, "ti,wl1251");
@@ -2086,6 +2081,7 @@ static int omap_hsmmc_resume(struct device *dev)
}
#endif
+#ifdef CONFIG_PM
static int omap_hsmmc_runtime_suspend(struct device *dev)
{
struct omap_hsmmc_host *host;
@@ -2153,11 +2149,11 @@ static int omap_hsmmc_runtime_resume(struct device *dev)
spin_unlock_irqrestore(&host->irq_lock, flags);
return 0;
}
+#endif
static const struct dev_pm_ops omap_hsmmc_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(omap_hsmmc_suspend, omap_hsmmc_resume)
- .runtime_suspend = omap_hsmmc_runtime_suspend,
- .runtime_resume = omap_hsmmc_runtime_resume,
+ SET_RUNTIME_PM_OPS(omap_hsmmc_runtime_suspend, omap_hsmmc_runtime_resume, NULL)
};
static struct platform_driver omap_hsmmc_driver = {
diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c
index 737e2bfdedc2..6a2e5a468424 100644
--- a/drivers/mmc/host/sdhci-of-arasan.c
+++ b/drivers/mmc/host/sdhci-of-arasan.c
@@ -191,6 +191,13 @@ static const struct sdhci_arasan_soc_ctl_map intel_lgm_sdxc_soc_ctl_map = {
.hiword_update = false,
};
+static const struct sdhci_arasan_soc_ctl_map thunderbay_soc_ctl_map = {
+ .baseclkfreq = { .reg = 0x0, .width = 8, .shift = 14 },
+ .clockmultiplier = { .reg = 0x4, .width = 8, .shift = 14 },
+ .support64b = { .reg = 0x4, .width = 1, .shift = 24 },
+ .hiword_update = false,
+};
+
static const struct sdhci_arasan_soc_ctl_map intel_keembay_soc_ctl_map = {
.baseclkfreq = { .reg = 0x0, .width = 8, .shift = 14 },
.clockmultiplier = { .reg = 0x4, .width = 8, .shift = 14 },
@@ -456,6 +463,15 @@ static const struct sdhci_pltfm_data sdhci_arasan_cqe_pdata = {
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
};
+static const struct sdhci_pltfm_data sdhci_arasan_thunderbay_pdata = {
+ .ops = &sdhci_arasan_cqe_ops,
+ .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+ SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN |
+ SDHCI_QUIRK2_STOP_WITH_TC |
+ SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400,
+};
+
#ifdef CONFIG_PM_SLEEP
/**
* sdhci_arasan_suspend - Suspend method for the driver
@@ -1132,6 +1148,12 @@ static struct sdhci_arasan_of_data sdhci_arasan_generic_data = {
.clk_ops = &arasan_clk_ops,
};
+static const struct sdhci_arasan_of_data sdhci_arasan_thunderbay_data = {
+ .soc_ctl_map = &thunderbay_soc_ctl_map,
+ .pdata = &sdhci_arasan_thunderbay_pdata,
+ .clk_ops = &arasan_clk_ops,
+};
+
static const struct sdhci_pltfm_data sdhci_keembay_emmc_pdata = {
.ops = &sdhci_arasan_cqe_ops,
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
@@ -1265,6 +1287,10 @@ static const struct of_device_id sdhci_arasan_of_match[] = {
.compatible = "intel,keembay-sdhci-5.1-sdio",
.data = &intel_keembay_sdio_data,
},
+ {
+ .compatible = "intel,thunderbay-sdhci-5.1",
+ .data = &sdhci_arasan_thunderbay_data,
+ },
/* Generic compatible below here */
{
.compatible = "arasan,sdhci-8.9a",
@@ -1626,7 +1652,8 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
if (of_device_is_compatible(np, "intel,keembay-sdhci-5.1-emmc") ||
of_device_is_compatible(np, "intel,keembay-sdhci-5.1-sd") ||
- of_device_is_compatible(np, "intel,keembay-sdhci-5.1-sdio")) {
+ of_device_is_compatible(np, "intel,keembay-sdhci-5.1-sdio") ||
+ of_device_is_compatible(np, "intel,thunderbay-sdhci-5.1")) {
sdhci_arasan_update_clockmultiplier(host, 0x0);
sdhci_arasan_update_support64b(host, 0x0);
diff --git a/drivers/mmc/host/sdhci-omap.c b/drivers/mmc/host/sdhci-omap.c
index 8f4d1f003f65..9422c9819b77 100644
--- a/drivers/mmc/host/sdhci-omap.c
+++ b/drivers/mmc/host/sdhci-omap.c
@@ -21,6 +21,8 @@
#include "sdhci-pltfm.h"
+#define SDHCI_OMAP_SYSCONFIG 0x110
+
#define SDHCI_OMAP_CON 0x12c
#define CON_DW8 BIT(5)
#define CON_DMA_MASTER BIT(20)
@@ -62,6 +64,8 @@
#define SDHCI_OMAP_IE 0x234
#define INT_CC_EN BIT(0)
+#define SDHCI_OMAP_ISE 0x238
+
#define SDHCI_OMAP_AC12 0x23c
#define AC12_V1V8_SIGEN BIT(19)
#define AC12_SCLK_SEL BIT(23)
@@ -113,6 +117,8 @@ struct sdhci_omap_host {
u32 hctl;
u32 sysctl;
u32 capa;
+ u32 ie;
+ u32 ise;
};
static void sdhci_omap_start_clock(struct sdhci_omap_host *omap_host);
@@ -682,7 +688,24 @@ static void sdhci_omap_set_power(struct sdhci_host *host, unsigned char mode,
{
struct mmc_host *mmc = host->mmc;
- mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd);
+ if (!IS_ERR(mmc->supply.vmmc))
+ mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd);
+}
+
+/*
+ * MMCHS_HL_HWINFO has the MADMA_EN bit set if the controller instance
+ * is connected to L3 interconnect and is bus master capable. Note that
+ * the MMCHS_HL_HWINFO register is in the module registers before the
+ * omap registers and sdhci registers. The offset can vary for omap
+ * registers depending on the SoC. Do not use sdhci_omap_readl() here.
+ */
+static bool sdhci_omap_has_adma(struct sdhci_omap_host *omap_host, int offset)
+{
+ /* MMCHS_HL_HWINFO register is only available on omap4 and later */
+ if (offset < 0x200)
+ return false;
+
+ return readl(omap_host->base + 4) & 1;
}
static int sdhci_omap_enable_dma(struct sdhci_host *host)
@@ -792,6 +815,11 @@ static void sdhci_omap_reset(struct sdhci_host *host, u8 mask)
struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
unsigned long limit = MMC_TIMEOUT_US;
unsigned long i = 0;
+ u32 sysc;
+
+ /* Save target module sysconfig configured by SoC PM layer */
+ if (mask & SDHCI_RESET_ALL)
+ sysc = sdhci_omap_readl(omap_host, SDHCI_OMAP_SYSCONFIG);
/* Don't reset data lines during tuning operation */
if (omap_host->is_tuning)
@@ -811,10 +839,15 @@ static void sdhci_omap_reset(struct sdhci_host *host, u8 mask)
dev_err(mmc_dev(host->mmc),
"Timeout waiting on controller reset in %s\n",
__func__);
- return;
+
+ goto restore_sysc;
}
sdhci_reset(host, mask);
+
+restore_sysc:
+ if (mask & SDHCI_RESET_ALL)
+ sdhci_omap_writel(omap_host, SDHCI_OMAP_SYSCONFIG, sysc);
}
#define CMD_ERR_MASK (SDHCI_INT_CRC | SDHCI_INT_END_BIT | SDHCI_INT_INDEX |\
@@ -1192,10 +1225,19 @@ static int sdhci_omap_probe(struct platform_device *pdev)
host->mmc_host_ops.execute_tuning = sdhci_omap_execute_tuning;
host->mmc_host_ops.enable_sdio_irq = sdhci_omap_enable_sdio_irq;
- /* Switch to external DMA only if there is the "dmas" property */
- if (of_find_property(dev->of_node, "dmas", NULL))
+ /*
+ * Switch to external DMA only if there is the "dmas" property and
+ * ADMA is not available on the controller instance.
+ */
+ if (device_property_present(dev, "dmas") &&
+ !sdhci_omap_has_adma(omap_host, offset))
sdhci_switch_external_dma(host, true);
+ if (device_property_read_bool(dev, "ti,non-removable")) {
+ dev_warn_once(dev, "using old ti,non-removable property\n");
+ mmc->caps |= MMC_CAP_NONREMOVABLE;
+ }
+
/* R1B responses is required to properly manage HW busy detection. */
mmc->caps |= MMC_CAP_NEED_RSP_BUSY;
@@ -1244,14 +1286,23 @@ static void sdhci_omap_context_save(struct sdhci_omap_host *omap_host)
{
omap_host->con = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
omap_host->hctl = sdhci_omap_readl(omap_host, SDHCI_OMAP_HCTL);
+ omap_host->sysctl = sdhci_omap_readl(omap_host, SDHCI_OMAP_SYSCTL);
omap_host->capa = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA);
+ omap_host->ie = sdhci_omap_readl(omap_host, SDHCI_OMAP_IE);
+ omap_host->ise = sdhci_omap_readl(omap_host, SDHCI_OMAP_ISE);
}
+/* Order matters here, HCTL must be restored in two phases */
static void sdhci_omap_context_restore(struct sdhci_omap_host *omap_host)
{
- sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, omap_host->con);
sdhci_omap_writel(omap_host, SDHCI_OMAP_HCTL, omap_host->hctl);
sdhci_omap_writel(omap_host, SDHCI_OMAP_CAPA, omap_host->capa);
+ sdhci_omap_writel(omap_host, SDHCI_OMAP_HCTL, omap_host->hctl);
+
+ sdhci_omap_writel(omap_host, SDHCI_OMAP_SYSCTL, omap_host->sysctl);
+ sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, omap_host->con);
+ sdhci_omap_writel(omap_host, SDHCI_OMAP_IE, omap_host->ie);
+ sdhci_omap_writel(omap_host, SDHCI_OMAP_ISE, omap_host->ise);
}
static int __maybe_unused sdhci_omap_suspend(struct device *dev)
diff --git a/drivers/mmc/host/sdhci-pci-o2micro.c b/drivers/mmc/host/sdhci-pci-o2micro.c
index 51d55a87aebe..f045c1ee4667 100644
--- a/drivers/mmc/host/sdhci-pci-o2micro.c
+++ b/drivers/mmc/host/sdhci-pci-o2micro.c
@@ -489,7 +489,7 @@ static void sdhci_pci_o2_enable_msi(struct sdhci_pci_chip *chip,
ret = pci_find_capability(chip->pdev, PCI_CAP_ID_MSI);
if (!ret) {
- pr_info("%s: unsupport msi, use INTx irq\n",
+ pr_info("%s: unsupported MSI, use INTx irq\n",
mmc_hostname(host->mmc));
return;
}
diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c
index 862f033d235d..9085f3932443 100644
--- a/drivers/mmc/host/sdhci-s3c.c
+++ b/drivers/mmc/host/sdhci-s3c.c
@@ -791,4 +791,3 @@ module_platform_driver(sdhci_s3c_driver);
MODULE_DESCRIPTION("Samsung SDHCI (HSMMC) glue");
MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:s3c-sdhci");
diff --git a/drivers/mmc/host/sdhci-sprd.c b/drivers/mmc/host/sdhci-sprd.c
index 11e375579cfb..f33e9349e4e6 100644
--- a/drivers/mmc/host/sdhci-sprd.c
+++ b/drivers/mmc/host/sdhci-sprd.c
@@ -8,6 +8,7 @@
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/highmem.h>
+#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
@@ -39,6 +40,9 @@
#define SDHCI_SPRD_BIT_POSRD_DLY_INV BIT(21)
#define SDHCI_SPRD_BIT_NEGRD_DLY_INV BIT(29)
+#define SDHCI_SPRD_REG_32_DLL_STS0 0x210
+#define SDHCI_SPRD_DLL_LOCKED BIT(18)
+
#define SDHCI_SPRD_REG_32_BUSY_POSI 0x250
#define SDHCI_SPRD_BIT_OUTR_CLK_AUTO_EN BIT(25)
#define SDHCI_SPRD_BIT_INNR_CLK_AUTO_EN BIT(24)
@@ -256,6 +260,15 @@ static void sdhci_sprd_enable_phy_dll(struct sdhci_host *host)
sdhci_writel(host, tmp, SDHCI_SPRD_REG_32_DLL_CFG);
/* wait 1ms */
usleep_range(1000, 1250);
+
+ if (read_poll_timeout(sdhci_readl, tmp, (tmp & SDHCI_SPRD_DLL_LOCKED),
+ 2000, USEC_PER_SEC, false, host, SDHCI_SPRD_REG_32_DLL_STS0)) {
+ pr_err("%s: DLL locked fail!\n", mmc_hostname(host->mmc));
+ pr_info("%s: DLL_STS0 : 0x%x, DLL_CFG : 0x%x\n",
+ mmc_hostname(host->mmc),
+ sdhci_readl(host, SDHCI_SPRD_REG_32_DLL_STS0),
+ sdhci_readl(host, SDHCI_SPRD_REG_32_DLL_CFG));
+ }
}
static void sdhci_sprd_set_clock(struct sdhci_host *host, unsigned int clock)
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 2d80a04e11d8..dbe87995596c 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -930,7 +930,7 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd,
struct mmc_data *data;
unsigned target_timeout, current_timeout;
- *too_big = true;
+ *too_big = false;
/*
* If the host controller provides us with an incorrect timeout
@@ -941,7 +941,7 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd,
if (host->quirks & SDHCI_QUIRK_BROKEN_TIMEOUT_VAL)
return host->max_timeout_count;
- /* Unspecified command, asume max */
+ /* Unspecified command, assume max */
if (cmd == NULL)
return host->max_timeout_count;
@@ -968,17 +968,14 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd,
while (current_timeout < target_timeout) {
count++;
current_timeout <<= 1;
- if (count > host->max_timeout_count)
+ if (count > host->max_timeout_count) {
+ if (!(host->quirks2 & SDHCI_QUIRK2_DISABLE_HW_TIMEOUT))
+ DBG("Too large timeout 0x%x requested for CMD%d!\n",
+ count, cmd->opcode);
+ count = host->max_timeout_count;
+ *too_big = true;
break;
- }
-
- if (count > host->max_timeout_count) {
- if (!(host->quirks2 & SDHCI_QUIRK2_DISABLE_HW_TIMEOUT))
- DBG("Too large timeout 0x%x requested for CMD%d!\n",
- count, cmd->opcode);
- count = host->max_timeout_count;
- } else {
- *too_big = false;
+ }
}
return count;
@@ -3238,7 +3235,7 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *intmask_p)
-ETIMEDOUT :
-EILSEQ;
- if (mrq->sbc && (host->flags & SDHCI_AUTO_CMD23)) {
+ if (sdhci_auto_cmd23(host, mrq)) {
mrq->sbc->error = err;
__sdhci_finish_mrq(host, mrq);
return;