mmc: host: Add facility to support re-tuning
Currently, there is core support for tuning during initialization. There can also be a need to re-tune periodically (e.g. sdhci) or to re-tune after the host controller is powered off (e.g. after PM runtime suspend / resume) or to re-tune in response to CRC errors. The main requirements for re-tuning are: - ability to enable / disable re-tuning - ability to flag that re-tuning is needed - ability to re-tune before any request - ability to hold off re-tuning if the card is busy - ability to hold off re-tuning if re-tuning is in progress - ability to run a re-tuning timer To support those requirements 7 members are added to struct mmc_host: unsigned int can_retune:1; /* re-tuning can be used */ unsigned int doing_retune:1; /* re-tuning in progress */ unsigned int retune_now:1; /* do re-tuning at next req */ int need_retune; /* re-tuning is needed */ int hold_retune; /* hold off re-tuning */ unsigned int retune_period; /* re-tuning period in secs */ struct timer_list retune_timer; /* for periodic re-tuning */ need_retune is an integer so it can be set without needing synchronization. hold_retune is a integer to allow nesting. Various simple functions are provided to set / clear those variables. Subsequent patches take those functions into use. Signed-off-by: Adrian Hunter <adrian.hunter@intel.com> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
This commit is contained in:
parent
59d22309c4
commit
dfa13ebbe3
3 changed files with 97 additions and 0 deletions
|
@ -301,6 +301,73 @@ static inline void mmc_host_clk_sysfs_init(struct mmc_host *host)
|
|||
|
||||
#endif
|
||||
|
||||
void mmc_retune_enable(struct mmc_host *host)
|
||||
{
|
||||
host->can_retune = 1;
|
||||
if (host->retune_period)
|
||||
mod_timer(&host->retune_timer,
|
||||
jiffies + host->retune_period * HZ);
|
||||
}
|
||||
|
||||
void mmc_retune_disable(struct mmc_host *host)
|
||||
{
|
||||
host->can_retune = 0;
|
||||
del_timer_sync(&host->retune_timer);
|
||||
host->retune_now = 0;
|
||||
host->need_retune = 0;
|
||||
}
|
||||
|
||||
void mmc_retune_timer_stop(struct mmc_host *host)
|
||||
{
|
||||
del_timer_sync(&host->retune_timer);
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_retune_timer_stop);
|
||||
|
||||
void mmc_retune_hold(struct mmc_host *host)
|
||||
{
|
||||
if (!host->hold_retune)
|
||||
host->retune_now = 1;
|
||||
host->hold_retune += 1;
|
||||
}
|
||||
|
||||
void mmc_retune_release(struct mmc_host *host)
|
||||
{
|
||||
if (host->hold_retune)
|
||||
host->hold_retune -= 1;
|
||||
else
|
||||
WARN_ON(1);
|
||||
}
|
||||
|
||||
int mmc_retune(struct mmc_host *host)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (host->retune_now)
|
||||
host->retune_now = 0;
|
||||
else
|
||||
return 0;
|
||||
|
||||
if (!host->need_retune || host->doing_retune || !host->card)
|
||||
return 0;
|
||||
|
||||
host->need_retune = 0;
|
||||
|
||||
host->doing_retune = 1;
|
||||
|
||||
err = mmc_execute_tuning(host->card);
|
||||
|
||||
host->doing_retune = 0;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void mmc_retune_timer(unsigned long data)
|
||||
{
|
||||
struct mmc_host *host = (struct mmc_host *)data;
|
||||
|
||||
mmc_retune_needed(host);
|
||||
}
|
||||
|
||||
/**
|
||||
* mmc_of_parse() - parse host's device-tree node
|
||||
* @host: host whose node should be parsed.
|
||||
|
@ -504,6 +571,7 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
|
|||
#ifdef CONFIG_PM
|
||||
host->pm_notify.notifier_call = mmc_pm_notify;
|
||||
#endif
|
||||
setup_timer(&host->retune_timer, mmc_retune_timer, (unsigned long)host);
|
||||
|
||||
/*
|
||||
* By default, hosts do not support SGIO or large requests.
|
||||
|
|
|
@ -15,5 +15,11 @@
|
|||
int mmc_register_host_class(void);
|
||||
void mmc_unregister_host_class(void);
|
||||
|
||||
void mmc_retune_enable(struct mmc_host *host);
|
||||
void mmc_retune_disable(struct mmc_host *host);
|
||||
void mmc_retune_hold(struct mmc_host *host);
|
||||
void mmc_retune_release(struct mmc_host *host);
|
||||
int mmc_retune(struct mmc_host *host);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include <linux/leds.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/fault-inject.h>
|
||||
|
@ -321,10 +322,18 @@ struct mmc_host {
|
|||
#ifdef CONFIG_MMC_DEBUG
|
||||
unsigned int removed:1; /* host is being removed */
|
||||
#endif
|
||||
unsigned int can_retune:1; /* re-tuning can be used */
|
||||
unsigned int doing_retune:1; /* re-tuning in progress */
|
||||
unsigned int retune_now:1; /* do re-tuning at next req */
|
||||
|
||||
int rescan_disable; /* disable card detection */
|
||||
int rescan_entered; /* used with nonremovable devices */
|
||||
|
||||
int need_retune; /* re-tuning is needed */
|
||||
int hold_retune; /* hold off re-tuning */
|
||||
unsigned int retune_period; /* re-tuning period in secs */
|
||||
struct timer_list retune_timer; /* for periodic re-tuning */
|
||||
|
||||
bool trigger_card_event; /* card_event necessary */
|
||||
|
||||
struct mmc_card *card; /* device attached to this host */
|
||||
|
@ -513,4 +522,18 @@ static inline bool mmc_card_hs400(struct mmc_card *card)
|
|||
return card->host->ios.timing == MMC_TIMING_MMC_HS400;
|
||||
}
|
||||
|
||||
void mmc_retune_timer_stop(struct mmc_host *host);
|
||||
|
||||
static inline void mmc_retune_needed(struct mmc_host *host)
|
||||
{
|
||||
if (host->can_retune)
|
||||
host->need_retune = 1;
|
||||
}
|
||||
|
||||
static inline void mmc_retune_recheck(struct mmc_host *host)
|
||||
{
|
||||
if (host->hold_retune <= 1)
|
||||
host->retune_now = 1;
|
||||
}
|
||||
|
||||
#endif /* LINUX_MMC_HOST_H */
|
||||
|
|
Loading…
Reference in a new issue