Merge tag 'mmc-v5.2' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc

Pull MMC updates from Ulf Hansson:
 "MMC core:
   - Fix a few memoryleaks
   - Minor improvements to the card initialization sequence
   - Partially support sleepy GPIO controllers for pwrseq eMMC

  MMC host:
   - alcor: Work with multiple-entry sglists
   - alcor: Enable DMA for writes
   - meson-gx: Improve tuning support
   - meson-gx: Avoid clock glitch when switching to DDR modes
   - meson-gx: Disable unreliable HS400 mode
   - mmci: Minor updates for support of HW busy detection
   - mmci: Support data transfers for the stm32_sdmmc variant
   - mmci: Restructure code to better support different variants
   - mtk-sd: Add support for version found on MT7620 family SOCs
   - mtk-sd: Add support for the MT8516 version
   - mtk-sd: Add Chaotian Jing as the maintainer
   - sdhci: Reorganize request-code to convert from tasklet to workqueue
   - sdhci_am654: Stabilize support for lower speed modes
   - sdhci-esdhc-imx: Add HS400 support for iMX7ULP
   - sdhci-esdhc-imx: Add support for iMX7ULP version
   - sdhci-of-arasan: Allow to disable DCMDs via DT for CQE
   - sdhci-of-esdhc: Add support for the ls1028a version
   - sdhci-of-esdhc: Several fixups for errata
   - sdhci-pci: Fix BYT OCP setting
   - sdhci-pci: Add support for Intel CML
   - sdhci-tegra: Add support for system suspend/resume
   - sdhci-tegra: Add CQE support for Tegra186 WAR
   - sdhci-tegra: Add support for Tegra194
   - sdhci-tegra: Update HW tuning process

  MEMSTICK:
   - I volunteered to help as a maintainer for the memstick subsystem,
     which is reflected by an update to the MAINTAINERS file. Changes
     are funneled through my MMC git and we will use the linux-mmc
     mailing list.

  MEMSTICK host:
   - A few minor cleanups"

* tag 'mmc-v5.2' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (87 commits)
  mmc: sdhci-pci: Fix BYT OCP setting
  dt-bindings: mmc: add DT bindings for ls1028a eSDHC host controller
  mmc: alcor: Drop pointer to mmc_host from alcor_sdmmc_host
  mmc: mtk-sd: select REGULATOR
  mmc: mtk-sd: enable internal card-detect logic.
  mmc: mtk-sd: add support for config found in mt7620 family SOCs.
  mmc: mtk-sd: don't hard-code interrupt trigger type
  mmc: core: Fix tag set memory leak
  dt-bindings: mmc: Add support for MT8516 to mtk-sd
  mmc: mmci: Prevent polling for busy detection in IRQ context
  mmc: mmci: Cleanup mmci_cmd_irq() for busy detect
  mmc: usdhi6rol0: mark expected switch fall-throughs
  mmc: core: Verify SD bus width
  mmc: sdhci-esdhc-imx: Add HS400 support for iMX7ULP
  mmc: sdhci-esdhc-imx: add pm_qos to interact with cpuidle
  dt-bindings: mmc: fsl-imx-esdhc: add imx7ulp compatible string
  mmc: meson-gx: add signal resampling tuning
  mmc: meson-gx: remove Rx phase tuning
  mmc: meson-gx: avoid clock glitch when switching to DDR modes
  mmc: meson-gx: disable HS400
  ...
This commit is contained in:
Linus Torvalds
2019-05-07 12:56:19 -07:00
46 changed files with 1185 additions and 681 deletions

View File

@@ -446,6 +446,28 @@ static inline void sdhci_led_deactivate(struct sdhci_host *host)
#endif
static void sdhci_mod_timer(struct sdhci_host *host, struct mmc_request *mrq,
unsigned long timeout)
{
if (sdhci_data_line_cmd(mrq->cmd))
mod_timer(&host->data_timer, timeout);
else
mod_timer(&host->timer, timeout);
}
static void sdhci_del_timer(struct sdhci_host *host, struct mmc_request *mrq)
{
if (sdhci_data_line_cmd(mrq->cmd))
del_timer(&host->data_timer);
else
del_timer(&host->timer);
}
static inline bool sdhci_has_requests(struct sdhci_host *host)
{
return host->cmd || host->data_cmd;
}
/*****************************************************************************\
* *
* Core functions *
@@ -1221,6 +1243,18 @@ static void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
{
int i;
if (host->cmd && host->cmd->mrq == mrq)
host->cmd = NULL;
if (host->data_cmd && host->data_cmd->mrq == mrq)
host->data_cmd = NULL;
if (host->data && host->data->mrq == mrq)
host->data = NULL;
if (sdhci_needs_reset(host, mrq))
host->pending_reset = true;
for (i = 0; i < SDHCI_MAX_MRQS; i++) {
if (host->mrqs_done[i] == mrq) {
WARN_ON(1);
@@ -1237,24 +1271,17 @@ static void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
WARN_ON(i >= SDHCI_MAX_MRQS);
tasklet_schedule(&host->finish_tasklet);
sdhci_del_timer(host, mrq);
if (!sdhci_has_requests(host))
sdhci_led_deactivate(host);
}
static void sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
{
if (host->cmd && host->cmd->mrq == mrq)
host->cmd = NULL;
if (host->data_cmd && host->data_cmd->mrq == mrq)
host->data_cmd = NULL;
if (host->data && host->data->mrq == mrq)
host->data = NULL;
if (sdhci_needs_reset(host, mrq))
host->pending_reset = true;
__sdhci_finish_mrq(host, mrq);
queue_work(host->complete_wq, &host->complete_work);
}
static void sdhci_finish_data(struct sdhci_host *host)
@@ -1305,34 +1332,17 @@ static void sdhci_finish_data(struct sdhci_host *host)
* responsibility to send the stop command if required.
*/
if (data->mrq->cap_cmd_during_tfr) {
sdhci_finish_mrq(host, data->mrq);
__sdhci_finish_mrq(host, data->mrq);
} else {
/* Avoid triggering warning in sdhci_send_command() */
host->cmd = NULL;
sdhci_send_command(host, data->stop);
}
} else {
sdhci_finish_mrq(host, data->mrq);
__sdhci_finish_mrq(host, data->mrq);
}
}
static void sdhci_mod_timer(struct sdhci_host *host, struct mmc_request *mrq,
unsigned long timeout)
{
if (sdhci_data_line_cmd(mrq->cmd))
mod_timer(&host->data_timer, timeout);
else
mod_timer(&host->timer, timeout);
}
static void sdhci_del_timer(struct sdhci_host *host, struct mmc_request *mrq)
{
if (sdhci_data_line_cmd(mrq->cmd))
del_timer(&host->data_timer);
else
del_timer(&host->timer);
}
void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
{
int flags;
@@ -1492,7 +1502,7 @@ static void sdhci_finish_command(struct sdhci_host *host)
sdhci_finish_data(host);
if (!cmd->data)
sdhci_finish_mrq(host, cmd->mrq);
__sdhci_finish_mrq(host, cmd->mrq);
}
}
@@ -2364,9 +2374,9 @@ static int __sdhci_execute_tuning(struct sdhci_host *host, u32 opcode)
/*
* Issue opcode repeatedly till Execute Tuning is set to 0 or the number
* of loops reaches 40 times.
* of loops reaches tuning loop count.
*/
for (i = 0; i < MAX_TUNING_LOOP; i++) {
for (i = 0; i < host->tuning_loop_count; i++) {
u16 ctrl;
sdhci_send_tuning(host, opcode);
@@ -2523,11 +2533,6 @@ static void sdhci_pre_req(struct mmc_host *mmc, struct mmc_request *mrq)
sdhci_pre_dma_transfer(host, mrq->data, COOKIE_PRE_MAPPED);
}
static inline bool sdhci_has_requests(struct sdhci_host *host)
{
return host->cmd || host->data_cmd;
}
static void sdhci_error_out_mrqs(struct sdhci_host *host, int err)
{
if (host->data_cmd) {
@@ -2589,7 +2594,7 @@ static const struct mmc_host_ops sdhci_ops = {
/*****************************************************************************\
* *
* Tasklets *
* Request done *
* *
\*****************************************************************************/
@@ -2612,8 +2617,6 @@ static bool sdhci_request_done(struct sdhci_host *host)
return true;
}
sdhci_del_timer(host, mrq);
/*
* Always unmap the data buffers if they were mapped by
* sdhci_prepare_data() whenever we finish with a request.
@@ -2695,9 +2698,6 @@ static bool sdhci_request_done(struct sdhci_host *host)
host->pending_reset = false;
}
if (!sdhci_has_requests(host))
sdhci_led_deactivate(host);
host->mrqs_done[i] = NULL;
spin_unlock_irqrestore(&host->lock, flags);
@@ -2707,9 +2707,10 @@ static bool sdhci_request_done(struct sdhci_host *host)
return false;
}
static void sdhci_tasklet_finish(unsigned long param)
static void sdhci_complete_work(struct work_struct *work)
{
struct sdhci_host *host = (struct sdhci_host *)param;
struct sdhci_host *host = container_of(work, struct sdhci_host,
complete_work);
while (!sdhci_request_done(host))
;
@@ -2754,6 +2755,7 @@ static void sdhci_timeout_data_timer(struct timer_list *t)
if (host->data) {
host->data->error = -ETIMEDOUT;
sdhci_finish_data(host);
queue_work(host->complete_wq, &host->complete_work);
} else if (host->data_cmd) {
host->data_cmd->error = -ETIMEDOUT;
sdhci_finish_mrq(host, host->data_cmd->mrq);
@@ -2819,7 +2821,7 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *intmask_p)
return;
}
sdhci_finish_mrq(host, host->cmd->mrq);
__sdhci_finish_mrq(host, host->cmd->mrq);
return;
}
@@ -2833,7 +2835,7 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *intmask_p)
if (mrq->sbc && (host->flags & SDHCI_AUTO_CMD23)) {
mrq->sbc->error = err;
sdhci_finish_mrq(host, mrq);
__sdhci_finish_mrq(host, mrq);
return;
}
}
@@ -2897,7 +2899,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
if (intmask & SDHCI_INT_DATA_TIMEOUT) {
host->data_cmd = NULL;
data_cmd->error = -ETIMEDOUT;
sdhci_finish_mrq(host, data_cmd->mrq);
__sdhci_finish_mrq(host, data_cmd->mrq);
return;
}
if (intmask & SDHCI_INT_DATA_END) {
@@ -2910,7 +2912,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
if (host->cmd == data_cmd)
return;
sdhci_finish_mrq(host, data_cmd->mrq);
__sdhci_finish_mrq(host, data_cmd->mrq);
return;
}
}
@@ -2993,12 +2995,24 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
}
}
static inline bool sdhci_defer_done(struct sdhci_host *host,
struct mmc_request *mrq)
{
struct mmc_data *data = mrq->data;
return host->pending_reset ||
((host->flags & SDHCI_REQ_USE_DMA) && data &&
data->host_cookie == COOKIE_MAPPED);
}
static irqreturn_t sdhci_irq(int irq, void *dev_id)
{
struct mmc_request *mrqs_done[SDHCI_MAX_MRQS] = {0};
irqreturn_t result = IRQ_NONE;
struct sdhci_host *host = dev_id;
u32 intmask, mask, unexpected = 0;
int max_loops = 16;
int i;
spin_lock(&host->lock);
@@ -3092,9 +3106,30 @@ cont:
intmask = sdhci_readl(host, SDHCI_INT_STATUS);
} while (intmask && --max_loops);
/* Determine if mrqs can be completed immediately */
for (i = 0; i < SDHCI_MAX_MRQS; i++) {
struct mmc_request *mrq = host->mrqs_done[i];
if (!mrq)
continue;
if (sdhci_defer_done(host, mrq)) {
result = IRQ_WAKE_THREAD;
} else {
mrqs_done[i] = mrq;
host->mrqs_done[i] = NULL;
}
}
out:
spin_unlock(&host->lock);
/* Process mrqs ready for immediate completion */
for (i = 0; i < SDHCI_MAX_MRQS; i++) {
if (mrqs_done[i])
mmc_request_done(host->mmc, mrqs_done[i]);
}
if (unexpected) {
pr_err("%s: Unexpected interrupt 0x%08x.\n",
mmc_hostname(host->mmc), unexpected);
@@ -3110,6 +3145,9 @@ static irqreturn_t sdhci_thread_irq(int irq, void *dev_id)
unsigned long flags;
u32 isr;
while (!sdhci_request_done(host))
;
spin_lock_irqsave(&host->lock, flags);
isr = host->thread_isr;
host->thread_isr = 0;
@@ -3131,7 +3169,7 @@ static irqreturn_t sdhci_thread_irq(int irq, void *dev_id)
spin_unlock_irqrestore(&host->lock, flags);
}
return isr ? IRQ_HANDLED : IRQ_NONE;
return IRQ_HANDLED;
}
/*****************************************************************************\
@@ -3483,6 +3521,7 @@ struct sdhci_host *sdhci_alloc_host(struct device *dev,
host->cqe_err_ier = SDHCI_CQE_INT_ERR_MASK;
host->tuning_delay = -1;
host->tuning_loop_count = MAX_TUNING_LOOP;
host->sdma_boundary = SDHCI_DEFAULT_BOUNDARY_ARG;
@@ -4213,14 +4252,15 @@ EXPORT_SYMBOL_GPL(sdhci_cleanup_host);
int __sdhci_add_host(struct sdhci_host *host)
{
unsigned int flags = WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_HIGHPRI;
struct mmc_host *mmc = host->mmc;
int ret;
/*
* Init tasklets.
*/
tasklet_init(&host->finish_tasklet,
sdhci_tasklet_finish, (unsigned long)host);
host->complete_wq = alloc_workqueue("sdhci", flags, 0);
if (!host->complete_wq)
return -ENOMEM;
INIT_WORK(&host->complete_work, sdhci_complete_work);
timer_setup(&host->timer, sdhci_timeout_timer, 0);
timer_setup(&host->data_timer, sdhci_timeout_data_timer, 0);
@@ -4234,7 +4274,7 @@ int __sdhci_add_host(struct sdhci_host *host)
if (ret) {
pr_err("%s: Failed to request IRQ %d: %d\n",
mmc_hostname(mmc), host->irq, ret);
goto untasklet;
goto unwq;
}
ret = sdhci_led_register(host);
@@ -4265,8 +4305,8 @@ unirq:
sdhci_writel(host, 0, SDHCI_INT_ENABLE);
sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);
free_irq(host->irq, host);
untasklet:
tasklet_kill(&host->finish_tasklet);
unwq:
destroy_workqueue(host->complete_wq);
return ret;
}
@@ -4328,7 +4368,7 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
del_timer_sync(&host->timer);
del_timer_sync(&host->data_timer);
tasklet_kill(&host->finish_tasklet);
destroy_workqueue(host->complete_wq);
if (!IS_ERR(mmc->supply.vqmmc))
regulator_disable(mmc->supply.vqmmc);