From 1d8cd065f7ab2992903f4611d8400ee794de0699 Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Sat, 23 Mar 2019 21:45:19 -0700 Subject: mmc: sdhci: allow host to specify maximum tuning loops As per the Host Controller Standard Specification Version 4.20, limitation of tuning iteration count is removed as PLL locking time can be longer than UHS-1 tuning due to larger PVT fluctuation and it will result in increase of tuning iteration to complete the tuning. This patch creates sdhci_host member tuning_loop_count to allow hosts to specify maximum tuning iterations and also updates execute_tuning to use this specified maximum tuning iteration count. Default tuning_loop_count is set to same as existing loop count of MAX_TUNING_LOOP which is 40 iterations. Tested-by: Jon Hunter Signed-off-by: Sowjanya Komatineni Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/mmc/host/sdhci.c') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index a8141ff9be03..bbc0e0bb7128 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2369,9 +2369,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); @@ -3494,6 +3494,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; -- cgit From 2e72ab9b2f565b6f3e1ff4a64a3736bb256c605c Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 5 Apr 2019 15:40:16 +0300 Subject: mmc: sdhci: Reorganize sdhci_finish_mrq() and __sdhci_finish_mrq() In preparation for removing finish_tasklet, reorganize sdhci_finish_mrq() and __sdhci_finish_mrq() to separate the tasklet scheduling from other processing. Signed-off-by: Adrian Hunter Reviewed-by: Faiz Abbas Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'drivers/mmc/host/sdhci.c') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index bbc0e0bb7128..76e546ab7f33 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1221,6 +1221,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); @@ -1236,25 +1248,13 @@ static void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq) } WARN_ON(i >= SDHCI_MAX_MRQS); - - tasklet_schedule(&host->finish_tasklet); } 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); + + tasklet_schedule(&host->finish_tasklet); } static void sdhci_finish_data(struct sdhci_host *host) -- cgit From 97a1abae46a690a0d1d4c676e00a261b2c12ac1b Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 5 Apr 2019 15:40:17 +0300 Subject: mmc: sdhci: Move timer and has_requests functions In preparation for removing finish_tasklet, move some functions. Signed-off-by: Adrian Hunter Reviewed-by: Faiz Abbas Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) (limited to 'drivers/mmc/host/sdhci.c') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 76e546ab7f33..7d16c88f7eaa 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -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 * @@ -1316,23 +1338,6 @@ static void sdhci_finish_data(struct sdhci_host *host) } } -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; @@ -2528,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) { -- cgit From e9a072993d69ae2241def3e9a8fe3e974c4d591f Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 5 Apr 2019 15:40:18 +0300 Subject: mmc: sdhci: Move some processing to __sdhci_finish_mrq() In preparation for removing finish_tasklet, move some processing from sdhci_request_done() to __sdhci_finish_mrq(). Signed-off-by: Adrian Hunter Reviewed-by: Faiz Abbas Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/mmc/host/sdhci.c') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 7d16c88f7eaa..cb3e75bd5b37 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1270,6 +1270,11 @@ static void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq) } WARN_ON(i >= SDHCI_MAX_MRQS); + + 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) @@ -2617,8 +2622,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. @@ -2700,9 +2703,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; mmiowb(); -- cgit From 19d2f695f4e82794df7465b029c02b104d1b9903 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 5 Apr 2019 15:40:19 +0300 Subject: mmc: sdhci: Call mmc_request_done() from IRQ handler if possible In preparation for removing finish_tasklet, call mmc_request_done() from the IRQ handler if possible. That will alleviate the potential loss of performance from shifting away from finish_tasklet. Signed-off-by: Adrian Hunter Reviewed-by: Faiz Abbas Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 48 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 7 deletions(-) (limited to 'drivers/mmc/host/sdhci.c') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index cb3e75bd5b37..3317c6a2f0f9 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1332,14 +1332,14 @@ 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); } } @@ -1502,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); } } @@ -2761,6 +2761,7 @@ static void sdhci_timeout_data_timer(struct timer_list *t) if (host->data) { host->data->error = -ETIMEDOUT; sdhci_finish_data(host); + tasklet_schedule(&host->finish_tasklet); } else if (host->data_cmd) { host->data_cmd->error = -ETIMEDOUT; sdhci_finish_mrq(host, host->data_cmd->mrq); @@ -2827,7 +2828,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; } @@ -2841,7 +2842,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; } } @@ -2905,7 +2906,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) { @@ -2918,7 +2919,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; } } @@ -3001,12 +3002,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); @@ -3100,9 +3113,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)) { + tasklet_schedule(&host->finish_tasklet); + } 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); -- cgit From c07a48c2651965e84d35cf193dfc0e5f7892d612 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 5 Apr 2019 15:40:20 +0300 Subject: mmc: sdhci: Remove finish_tasklet Remove finish_tasklet. Requests that require DMA-unmapping or sdhci_reset are completed either in the IRQ thread or a workqueue if the completion is not initiated by the IRQ. Signed-off-by: Adrian Hunter Reviewed-by: Faiz Abbas Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) (limited to 'drivers/mmc/host/sdhci.c') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 3317c6a2f0f9..5c82387d53e2 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1281,7 +1281,7 @@ static void sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq) { __sdhci_finish_mrq(host, mrq); - tasklet_schedule(&host->finish_tasklet); + queue_work(host->complete_wq, &host->complete_work); } static void sdhci_finish_data(struct sdhci_host *host) @@ -2599,7 +2599,7 @@ static const struct mmc_host_ops sdhci_ops = { /*****************************************************************************\ * * - * Tasklets * + * Request done * * * \*****************************************************************************/ @@ -2713,9 +2713,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)) ; @@ -2761,7 +2762,7 @@ static void sdhci_timeout_data_timer(struct timer_list *t) if (host->data) { host->data->error = -ETIMEDOUT; sdhci_finish_data(host); - tasklet_schedule(&host->finish_tasklet); + 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); @@ -3122,7 +3123,7 @@ cont: continue; if (sdhci_defer_done(host, mrq)) { - tasklet_schedule(&host->finish_tasklet); + result = IRQ_WAKE_THREAD; } else { mrqs_done[i] = mrq; host->mrqs_done[i] = NULL; @@ -3152,6 +3153,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; @@ -3173,7 +3177,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; } /*****************************************************************************\ @@ -4259,14 +4263,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); @@ -4280,7 +4285,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); @@ -4313,8 +4318,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; } @@ -4376,7 +4381,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); -- cgit