summaryrefslogtreecommitdiff
path: root/drivers/mmc/host
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2017-11-13 17:45:57 +0300
committerTakashi Iwai <tiwai@suse.de>2017-11-13 17:45:57 +0300
commit76727c2c3bf4a5e58dff8cca23d0147ba08fb2c8 (patch)
treec84c07b9deac4425190777a962f6788d355a0dd1 /drivers/mmc/host
parentc429bda21ffafb28f02fb2eb4055b4ab6879ed58 (diff)
parentdf6a3e245541ac61cc99f2887437e0a43dd08f2e (diff)
downloadlinux-76727c2c3bf4a5e58dff8cca23d0147ba08fb2c8.tar.xz
Merge tag 'asoc-v4.15' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-linus
ASoC: Updates for v4.15 The biggest thing this release has been the conversion of the AC98 bus to the driver model, that's been a long time coming so thanks to Robert Jarzmik for his dedication there. Due to there being some AC97 MFD there's a few fairly large changes in input and the MFD layer, mainly to the wm97xx driver. There's also some drivers/drm changes to support the new AMD Stoney platform, these are shared with the DRM subsystem and should be being merged via both. Within the subsystem the overwhelming bulk of the changes is in the Intel drivers which continue to need lots of cleanups and fixes, this release they've also gained support for their open source firmware. There's also some large changs in the core as Morimoto-san continues to mirror operations into the component level in preparation for conversion of drivers to that. - The AC97 bus has finally caught up with the driver model thanks to some dedicated and persistent work from Robert Jarzmik. - Continued work from Morimoto-san on moving us towards being able to use components for everything. - Lots of cleanups for the Intel platform code, including support for their open source audio firmware. - Support for scaling MCLK with sample rate in simple-card. - Support for AMD Stoney platform.
Diffstat (limited to 'drivers/mmc/host')
-rw-r--r--drivers/mmc/host/Makefile1
-rw-r--r--drivers/mmc/host/dw_mmc-zx.h1
-rw-r--r--drivers/mmc/host/dw_mmc.c107
-rw-r--r--drivers/mmc/host/pxamci.h1
-rw-r--r--drivers/mmc/host/renesas_sdhi_internal_dmac.c17
-rw-r--r--drivers/mmc/host/sdhci-pci.h1
-rw-r--r--drivers/mmc/host/tmio_mmc_core.c13
7 files changed, 121 insertions, 20 deletions
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 303f5cd46cd9..7c7b29ff591a 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for MMC/SD host controller drivers
#
diff --git a/drivers/mmc/host/dw_mmc-zx.h b/drivers/mmc/host/dw_mmc-zx.h
index f369997a39ec..09ac52766f14 100644
--- a/drivers/mmc/host/dw_mmc-zx.h
+++ b/drivers/mmc/host/dw_mmc-zx.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _DW_MMC_ZX_H_
#define _DW_MMC_ZX_H_
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 860313bd952a..4f2806720c5c 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -401,16 +401,37 @@ static u32 dw_mci_prep_stop_abort(struct dw_mci *host, struct mmc_command *cmd)
static inline void dw_mci_set_cto(struct dw_mci *host)
{
unsigned int cto_clks;
+ unsigned int cto_div;
unsigned int cto_ms;
+ unsigned long irqflags;
cto_clks = mci_readl(host, TMOUT) & 0xff;
- cto_ms = DIV_ROUND_UP(cto_clks, host->bus_hz / 1000);
+ 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);
/* add a bit spare time */
cto_ms += 10;
- mod_timer(&host->cto_timer,
- jiffies + msecs_to_jiffies(cto_ms) + 1);
+ /*
+ * The durations we're working with are fairly short so we have to be
+ * extra careful about synchronization here. Specifically in hardware a
+ * command timeout is _at most_ 5.1 ms, so that means we expect an
+ * interrupt (either command done or timeout) to come rather quickly
+ * after the mci_writel. ...but just in case we have a long interrupt
+ * latency let's add a bit of paranoia.
+ *
+ * In general we'll assume that at least an interrupt will be asserted
+ * in hardware by the time the cto_timer runs. ...and if it hasn't
+ * been asserted in hardware by that time then we'll assume it'll never
+ * come.
+ */
+ spin_lock_irqsave(&host->irq_lock, irqflags);
+ if (!test_bit(EVENT_CMD_COMPLETE, &host->pending_events))
+ mod_timer(&host->cto_timer,
+ jiffies + msecs_to_jiffies(cto_ms) + 1);
+ spin_unlock_irqrestore(&host->irq_lock, irqflags);
}
static void dw_mci_start_command(struct dw_mci *host,
@@ -425,11 +446,11 @@ static void dw_mci_start_command(struct dw_mci *host,
wmb(); /* drain writebuffer */
dw_mci_wait_while_busy(host, cmd_flags);
+ mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START);
+
/* response expected command only */
if (cmd_flags & SDMMC_CMD_RESP_EXP)
dw_mci_set_cto(host);
-
- mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START);
}
static inline void send_stop_abort(struct dw_mci *host, struct mmc_data *data)
@@ -1915,10 +1936,15 @@ static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data)
static void dw_mci_set_drto(struct dw_mci *host)
{
unsigned int drto_clks;
+ unsigned int drto_div;
unsigned int drto_ms;
drto_clks = mci_readl(host, TMOUT) >> 8;
- drto_ms = DIV_ROUND_UP(drto_clks, host->bus_hz / 1000);
+ 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);
/* add a bit spare time */
drto_ms += 10;
@@ -1926,6 +1952,24 @@ static void dw_mci_set_drto(struct dw_mci *host)
mod_timer(&host->dto_timer, jiffies + msecs_to_jiffies(drto_ms));
}
+static bool dw_mci_clear_pending_cmd_complete(struct dw_mci *host)
+{
+ if (!test_bit(EVENT_CMD_COMPLETE, &host->pending_events))
+ return false;
+
+ /*
+ * Really be certain that the timer has stopped. This is a bit of
+ * paranoia and could only really happen if we had really bad
+ * interrupt latency and the interrupt routine and timeout were
+ * running concurrently so that the del_timer() in the interrupt
+ * handler couldn't run.
+ */
+ WARN_ON(del_timer_sync(&host->cto_timer));
+ clear_bit(EVENT_CMD_COMPLETE, &host->pending_events);
+
+ return true;
+}
+
static void dw_mci_tasklet_func(unsigned long priv)
{
struct dw_mci *host = (struct dw_mci *)priv;
@@ -1952,8 +1996,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
case STATE_SENDING_CMD11:
case STATE_SENDING_CMD:
- if (!test_and_clear_bit(EVENT_CMD_COMPLETE,
- &host->pending_events))
+ if (!dw_mci_clear_pending_cmd_complete(host))
break;
cmd = host->cmd;
@@ -2122,8 +2165,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
/* fall through */
case STATE_SENDING_STOP:
- if (!test_and_clear_bit(EVENT_CMD_COMPLETE,
- &host->pending_events))
+ if (!dw_mci_clear_pending_cmd_complete(host))
break;
/* CMD error in data command */
@@ -2570,6 +2612,8 @@ done:
static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status)
{
+ del_timer(&host->cto_timer);
+
if (!host->cmd_status)
host->cmd_status = status;
@@ -2594,6 +2638,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
struct dw_mci *host = dev_id;
u32 pending;
struct dw_mci_slot *slot = host->slot;
+ unsigned long irqflags;
pending = mci_readl(host, MINTSTS); /* read-only mask reg */
@@ -2601,8 +2646,6 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
/* Check volt switch first, since it can look like an error */
if ((host->state == STATE_SENDING_CMD11) &&
(pending & SDMMC_INT_VOLT_SWITCH)) {
- unsigned long irqflags;
-
mci_writel(host, RINTSTS, SDMMC_INT_VOLT_SWITCH);
pending &= ~SDMMC_INT_VOLT_SWITCH;
@@ -2618,11 +2661,15 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
}
if (pending & DW_MCI_CMD_ERROR_FLAGS) {
+ spin_lock_irqsave(&host->irq_lock, irqflags);
+
del_timer(&host->cto_timer);
mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS);
host->cmd_status = pending;
smp_wmb(); /* drain writebuffer */
set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
+
+ spin_unlock_irqrestore(&host->irq_lock, irqflags);
}
if (pending & DW_MCI_DATA_ERROR_FLAGS) {
@@ -2662,9 +2709,12 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
}
if (pending & SDMMC_INT_CMD_DONE) {
- del_timer(&host->cto_timer);
+ spin_lock_irqsave(&host->irq_lock, irqflags);
+
mci_writel(host, RINTSTS, SDMMC_INT_CMD_DONE);
dw_mci_cmd_interrupt(host, pending);
+
+ spin_unlock_irqrestore(&host->irq_lock, irqflags);
}
if (pending & SDMMC_INT_CD) {
@@ -2938,7 +2988,35 @@ static void dw_mci_cmd11_timer(unsigned long arg)
static void dw_mci_cto_timer(unsigned long arg)
{
struct dw_mci *host = (struct dw_mci *)arg;
+ unsigned long irqflags;
+ u32 pending;
+ spin_lock_irqsave(&host->irq_lock, irqflags);
+
+ /*
+ * If somehow we have very bad interrupt latency it's remotely possible
+ * that the timer could fire while the interrupt is still pending or
+ * while the interrupt is midway through running. Let's be paranoid
+ * and detect those two cases. Note that this is paranoia is somewhat
+ * justified because in this function we don't actually cancel the
+ * pending command in the controller--we just assume it will never come.
+ */
+ pending = mci_readl(host, MINTSTS); /* read-only mask reg */
+ if (pending & (DW_MCI_CMD_ERROR_FLAGS | SDMMC_INT_CMD_DONE)) {
+ /* The interrupt should fire; no need to act but we can warn */
+ dev_warn(host->dev, "Unexpected interrupt latency\n");
+ goto exit;
+ }
+ if (test_bit(EVENT_CMD_COMPLETE, &host->pending_events)) {
+ /* Presumably interrupt handler couldn't delete the timer */
+ dev_warn(host->dev, "CTO timeout when already completed\n");
+ goto exit;
+ }
+
+ /*
+ * Continued paranoia to make sure we're in the state we expect.
+ * This paranoia isn't really justified but it seems good to be safe.
+ */
switch (host->state) {
case STATE_SENDING_CMD11:
case STATE_SENDING_CMD:
@@ -2957,6 +3035,9 @@ static void dw_mci_cto_timer(unsigned long arg)
host->state);
break;
}
+
+exit:
+ spin_unlock_irqrestore(&host->irq_lock, irqflags);
}
static void dw_mci_dto_timer(unsigned long arg)
diff --git a/drivers/mmc/host/pxamci.h b/drivers/mmc/host/pxamci.h
index f6c2e2fcce37..d301ca18c5d4 100644
--- a/drivers/mmc/host/pxamci.h
+++ b/drivers/mmc/host/pxamci.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#define MMC_STRPCL 0x0000
#define STOP_CLOCK (1 << 0)
#define START_CLOCK (2 << 0)
diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
index f905f2361d12..8bae88a150fd 100644
--- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c
+++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
@@ -146,11 +146,8 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host,
WARN_ON(host->sg_len > 1);
/* This DMAC cannot handle if buffer is not 8-bytes alignment */
- if (!IS_ALIGNED(sg->offset, 8)) {
- host->force_pio = true;
- renesas_sdhi_internal_dmac_enable_dma(host, false);
- return;
- }
+ if (!IS_ALIGNED(sg->offset, 8))
+ goto force_pio;
if (data->flags & MMC_DATA_READ) {
dtran_mode |= DTRAN_MODE_CH_NUM_CH1;
@@ -163,8 +160,8 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host,
}
ret = dma_map_sg(&host->pdev->dev, sg, host->sg_len, dir);
- if (ret < 0)
- return;
+ if (ret == 0)
+ goto force_pio;
renesas_sdhi_internal_dmac_enable_dma(host, true);
@@ -176,6 +173,12 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host,
dtran_mode);
renesas_sdhi_internal_dmac_dm_write(host, DM_DTRAN_ADDR,
sg->dma_address);
+
+ return;
+
+force_pio:
+ host->force_pio = true;
+ renesas_sdhi_internal_dmac_enable_dma(host, false);
}
static void renesas_sdhi_internal_dmac_issue_tasklet_fn(unsigned long arg)
diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h
index 75196a2b5289..3e8ea3e566f6 100644
--- a/drivers/mmc/host/sdhci-pci.h
+++ b/drivers/mmc/host/sdhci-pci.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __SDHCI_PCI_H
#define __SDHCI_PCI_H
diff --git a/drivers/mmc/host/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c
index a7293e186e03..9c4e6199b854 100644
--- a/drivers/mmc/host/tmio_mmc_core.c
+++ b/drivers/mmc/host/tmio_mmc_core.c
@@ -47,6 +47,7 @@
#include <linux/mmc/sdio.h>
#include <linux/scatterlist.h>
#include <linux/spinlock.h>
+#include <linux/swiotlb.h>
#include <linux/workqueue.h>
#include "tmio_mmc.h"
@@ -1215,6 +1216,18 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host,
mmc->max_blk_count = pdata->max_blk_count ? :
(PAGE_SIZE / mmc->max_blk_size) * mmc->max_segs;
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
+ /*
+ * Since swiotlb has memory size limitation, this will calculate
+ * the maximum size locally (because we don't have any APIs for it now)
+ * and check the current max_req_size. And then, this will update
+ * the max_req_size if needed as a workaround.
+ */
+ if (swiotlb_max_segment()) {
+ unsigned int max_size = (1 << IO_TLB_SHIFT) * IO_TLB_SEGSIZE;
+
+ if (mmc->max_req_size > max_size)
+ mmc->max_req_size = max_size;
+ }
mmc->max_seg_size = mmc->max_req_size;
_host->native_hotplug = !(pdata->flags & TMIO_MMC_USE_GPIO_CD ||