2014-11-28 13:38:36 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2014 Linaro Ltd
|
|
|
|
*
|
|
|
|
* Author: Ulf Hansson <ulf.hansson@linaro.org>
|
|
|
|
*
|
|
|
|
* License terms: GNU General Public License (GPL) version 2
|
|
|
|
*
|
|
|
|
* MMC power sequence management
|
|
|
|
*/
|
2014-12-05 13:36:58 +00:00
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/err.h>
|
2016-04-14 13:02:16 +00:00
|
|
|
#include <linux/module.h>
|
2014-12-05 13:36:58 +00:00
|
|
|
#include <linux/of.h>
|
|
|
|
|
2014-11-28 13:38:36 +00:00
|
|
|
#include <linux/mmc/host.h>
|
|
|
|
|
|
|
|
#include "pwrseq.h"
|
|
|
|
|
2016-04-14 13:02:16 +00:00
|
|
|
static DEFINE_MUTEX(pwrseq_list_mutex);
|
|
|
|
static LIST_HEAD(pwrseq_list);
|
2014-11-28 13:38:36 +00:00
|
|
|
|
|
|
|
int mmc_pwrseq_alloc(struct mmc_host *host)
|
|
|
|
{
|
2014-12-05 13:36:58 +00:00
|
|
|
struct device_node *np;
|
2016-04-14 13:02:16 +00:00
|
|
|
struct mmc_pwrseq *p;
|
2014-12-05 13:36:58 +00:00
|
|
|
|
|
|
|
np = of_parse_phandle(host->parent->of_node, "mmc-pwrseq", 0);
|
|
|
|
if (!np)
|
|
|
|
return 0;
|
|
|
|
|
2016-04-14 13:02:16 +00:00
|
|
|
mutex_lock(&pwrseq_list_mutex);
|
|
|
|
list_for_each_entry(p, &pwrseq_list, pwrseq_node) {
|
|
|
|
if (p->dev->of_node == np) {
|
|
|
|
if (!try_module_get(p->owner))
|
|
|
|
dev_err(host->parent,
|
|
|
|
"increasing module refcount failed\n");
|
|
|
|
else
|
|
|
|
host->pwrseq = p;
|
2014-12-05 13:36:58 +00:00
|
|
|
|
2016-04-14 13:02:16 +00:00
|
|
|
break;
|
|
|
|
}
|
2014-12-05 13:36:58 +00:00
|
|
|
}
|
|
|
|
|
2016-04-14 13:02:16 +00:00
|
|
|
of_node_put(np);
|
|
|
|
mutex_unlock(&pwrseq_list_mutex);
|
|
|
|
|
|
|
|
if (!host->pwrseq)
|
|
|
|
return -EPROBE_DEFER;
|
2015-02-12 04:36:11 +00:00
|
|
|
|
|
|
|
dev_info(host->parent, "allocated mmc-pwrseq\n");
|
2014-12-05 13:36:58 +00:00
|
|
|
|
2016-04-14 13:02:16 +00:00
|
|
|
return 0;
|
2014-11-28 13:38:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void mmc_pwrseq_pre_power_on(struct mmc_host *host)
|
|
|
|
{
|
|
|
|
struct mmc_pwrseq *pwrseq = host->pwrseq;
|
|
|
|
|
2016-04-14 13:02:16 +00:00
|
|
|
if (pwrseq && pwrseq->ops->pre_power_on)
|
2014-11-28 13:38:36 +00:00
|
|
|
pwrseq->ops->pre_power_on(host);
|
|
|
|
}
|
|
|
|
|
|
|
|
void mmc_pwrseq_post_power_on(struct mmc_host *host)
|
|
|
|
{
|
|
|
|
struct mmc_pwrseq *pwrseq = host->pwrseq;
|
|
|
|
|
2016-04-14 13:02:16 +00:00
|
|
|
if (pwrseq && pwrseq->ops->post_power_on)
|
2014-11-28 13:38:36 +00:00
|
|
|
pwrseq->ops->post_power_on(host);
|
|
|
|
}
|
|
|
|
|
|
|
|
void mmc_pwrseq_power_off(struct mmc_host *host)
|
|
|
|
{
|
|
|
|
struct mmc_pwrseq *pwrseq = host->pwrseq;
|
|
|
|
|
2016-04-14 13:02:16 +00:00
|
|
|
if (pwrseq && pwrseq->ops->power_off)
|
2014-11-28 13:38:36 +00:00
|
|
|
pwrseq->ops->power_off(host);
|
|
|
|
}
|
|
|
|
|
2017-05-08 21:49:12 +00:00
|
|
|
void mmc_pwrseq_reset(struct mmc_host *host)
|
|
|
|
{
|
|
|
|
struct mmc_pwrseq *pwrseq = host->pwrseq;
|
|
|
|
|
|
|
|
if (pwrseq && pwrseq->ops->reset)
|
|
|
|
pwrseq->ops->reset(host);
|
|
|
|
}
|
|
|
|
|
2014-11-28 13:38:36 +00:00
|
|
|
void mmc_pwrseq_free(struct mmc_host *host)
|
|
|
|
{
|
|
|
|
struct mmc_pwrseq *pwrseq = host->pwrseq;
|
|
|
|
|
2016-04-14 13:02:16 +00:00
|
|
|
if (pwrseq) {
|
|
|
|
module_put(pwrseq->owner);
|
|
|
|
host->pwrseq = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int mmc_pwrseq_register(struct mmc_pwrseq *pwrseq)
|
|
|
|
{
|
|
|
|
if (!pwrseq || !pwrseq->ops || !pwrseq->dev)
|
|
|
|
return -EINVAL;
|
2015-02-12 04:36:11 +00:00
|
|
|
|
2016-04-14 13:02:16 +00:00
|
|
|
mutex_lock(&pwrseq_list_mutex);
|
|
|
|
list_add(&pwrseq->pwrseq_node, &pwrseq_list);
|
|
|
|
mutex_unlock(&pwrseq_list_mutex);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(mmc_pwrseq_register);
|
|
|
|
|
|
|
|
void mmc_pwrseq_unregister(struct mmc_pwrseq *pwrseq)
|
|
|
|
{
|
|
|
|
if (pwrseq) {
|
|
|
|
mutex_lock(&pwrseq_list_mutex);
|
|
|
|
list_del(&pwrseq->pwrseq_node);
|
|
|
|
mutex_unlock(&pwrseq_list_mutex);
|
|
|
|
}
|
2014-11-28 13:38:36 +00:00
|
|
|
}
|
2016-04-14 13:02:16 +00:00
|
|
|
EXPORT_SYMBOL_GPL(mmc_pwrseq_unregister);
|