18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Copyright (C) 2014 Linaro Ltd
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Author: Ulf Hansson <ulf.hansson@linaro.org>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *  MMC power sequence management
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci#include <linux/kernel.h>
108c2ecf20Sopenharmony_ci#include <linux/err.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/of.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/mmc/host.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include "pwrseq.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(pwrseq_list_mutex);
198c2ecf20Sopenharmony_cistatic LIST_HEAD(pwrseq_list);
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ciint mmc_pwrseq_alloc(struct mmc_host *host)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	struct device_node *np;
248c2ecf20Sopenharmony_ci	struct mmc_pwrseq *p;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	np = of_parse_phandle(host->parent->of_node, "mmc-pwrseq", 0);
278c2ecf20Sopenharmony_ci	if (!np)
288c2ecf20Sopenharmony_ci		return 0;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	mutex_lock(&pwrseq_list_mutex);
318c2ecf20Sopenharmony_ci	list_for_each_entry(p, &pwrseq_list, pwrseq_node) {
328c2ecf20Sopenharmony_ci		if (p->dev->of_node == np) {
338c2ecf20Sopenharmony_ci			if (!try_module_get(p->owner))
348c2ecf20Sopenharmony_ci				dev_err(host->parent,
358c2ecf20Sopenharmony_ci					"increasing module refcount failed\n");
368c2ecf20Sopenharmony_ci			else
378c2ecf20Sopenharmony_ci				host->pwrseq = p;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci			break;
408c2ecf20Sopenharmony_ci		}
418c2ecf20Sopenharmony_ci	}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	of_node_put(np);
448c2ecf20Sopenharmony_ci	mutex_unlock(&pwrseq_list_mutex);
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	if (!host->pwrseq)
478c2ecf20Sopenharmony_ci		return -EPROBE_DEFER;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	dev_info(host->parent, "allocated mmc-pwrseq\n");
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	return 0;
528c2ecf20Sopenharmony_ci}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_civoid mmc_pwrseq_pre_power_on(struct mmc_host *host)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	struct mmc_pwrseq *pwrseq = host->pwrseq;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	if (pwrseq && pwrseq->ops->pre_power_on)
598c2ecf20Sopenharmony_ci		pwrseq->ops->pre_power_on(host);
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_civoid mmc_pwrseq_post_power_on(struct mmc_host *host)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	struct mmc_pwrseq *pwrseq = host->pwrseq;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	if (pwrseq && pwrseq->ops->post_power_on)
678c2ecf20Sopenharmony_ci		pwrseq->ops->post_power_on(host);
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_civoid mmc_pwrseq_power_off(struct mmc_host *host)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	struct mmc_pwrseq *pwrseq = host->pwrseq;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	if (pwrseq && pwrseq->ops->power_off)
758c2ecf20Sopenharmony_ci		pwrseq->ops->power_off(host);
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_civoid mmc_pwrseq_reset(struct mmc_host *host)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	struct mmc_pwrseq *pwrseq = host->pwrseq;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	if (pwrseq && pwrseq->ops->reset)
838c2ecf20Sopenharmony_ci		pwrseq->ops->reset(host);
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_civoid mmc_pwrseq_free(struct mmc_host *host)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	struct mmc_pwrseq *pwrseq = host->pwrseq;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	if (pwrseq) {
918c2ecf20Sopenharmony_ci		module_put(pwrseq->owner);
928c2ecf20Sopenharmony_ci		host->pwrseq = NULL;
938c2ecf20Sopenharmony_ci	}
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ciint mmc_pwrseq_register(struct mmc_pwrseq *pwrseq)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	if (!pwrseq || !pwrseq->ops || !pwrseq->dev)
998c2ecf20Sopenharmony_ci		return -EINVAL;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	mutex_lock(&pwrseq_list_mutex);
1028c2ecf20Sopenharmony_ci	list_add(&pwrseq->pwrseq_node, &pwrseq_list);
1038c2ecf20Sopenharmony_ci	mutex_unlock(&pwrseq_list_mutex);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	return 0;
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mmc_pwrseq_register);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_civoid mmc_pwrseq_unregister(struct mmc_pwrseq *pwrseq)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	if (pwrseq) {
1128c2ecf20Sopenharmony_ci		mutex_lock(&pwrseq_list_mutex);
1138c2ecf20Sopenharmony_ci		list_del(&pwrseq->pwrseq_node);
1148c2ecf20Sopenharmony_ci		mutex_unlock(&pwrseq_list_mutex);
1158c2ecf20Sopenharmony_ci	}
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mmc_pwrseq_unregister);
118