1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Copyright (C) 2014 Linaro Ltd 4 * 5 * Author: Ulf Hansson <ulf.hansson@linaro.org> 6 * 7 * MMC power sequence management 8 */ 9#include <linux/kernel.h> 10#include <linux/err.h> 11#include <linux/module.h> 12#include <linux/of.h> 13 14#include <linux/mmc/host.h> 15 16#include "pwrseq.h" 17 18static DEFINE_MUTEX(pwrseq_list_mutex); 19static LIST_HEAD(pwrseq_list); 20 21int mmc_pwrseq_alloc(struct mmc_host *host) 22{ 23 struct device_node *np; 24 struct mmc_pwrseq *p; 25 26 np = of_parse_phandle(host->parent->of_node, "mmc-pwrseq", 0); 27 if (!np) 28 return 0; 29 30 mutex_lock(&pwrseq_list_mutex); 31 list_for_each_entry(p, &pwrseq_list, pwrseq_node) { 32 if (p->dev->of_node == np) { 33 if (!try_module_get(p->owner)) 34 dev_err(host->parent, 35 "increasing module refcount failed\n"); 36 else 37 host->pwrseq = p; 38 39 break; 40 } 41 } 42 43 of_node_put(np); 44 mutex_unlock(&pwrseq_list_mutex); 45 46 if (!host->pwrseq) 47 return -EPROBE_DEFER; 48 49 dev_info(host->parent, "allocated mmc-pwrseq\n"); 50 51 return 0; 52} 53 54void mmc_pwrseq_pre_power_on(struct mmc_host *host) 55{ 56 struct mmc_pwrseq *pwrseq = host->pwrseq; 57 58 if (pwrseq && pwrseq->ops->pre_power_on) 59 pwrseq->ops->pre_power_on(host); 60} 61 62void mmc_pwrseq_post_power_on(struct mmc_host *host) 63{ 64 struct mmc_pwrseq *pwrseq = host->pwrseq; 65 66 if (pwrseq && pwrseq->ops->post_power_on) 67 pwrseq->ops->post_power_on(host); 68} 69 70void mmc_pwrseq_power_off(struct mmc_host *host) 71{ 72 struct mmc_pwrseq *pwrseq = host->pwrseq; 73 74 if (pwrseq && pwrseq->ops->power_off) 75 pwrseq->ops->power_off(host); 76} 77 78void mmc_pwrseq_reset(struct mmc_host *host) 79{ 80 struct mmc_pwrseq *pwrseq = host->pwrseq; 81 82 if (pwrseq && pwrseq->ops->reset) 83 pwrseq->ops->reset(host); 84} 85 86void mmc_pwrseq_free(struct mmc_host *host) 87{ 88 struct mmc_pwrseq *pwrseq = host->pwrseq; 89 90 if (pwrseq) { 91 module_put(pwrseq->owner); 92 host->pwrseq = NULL; 93 } 94} 95 96int mmc_pwrseq_register(struct mmc_pwrseq *pwrseq) 97{ 98 if (!pwrseq || !pwrseq->ops || !pwrseq->dev) 99 return -EINVAL; 100 101 mutex_lock(&pwrseq_list_mutex); 102 list_add(&pwrseq->pwrseq_node, &pwrseq_list); 103 mutex_unlock(&pwrseq_list_mutex); 104 105 return 0; 106} 107EXPORT_SYMBOL_GPL(mmc_pwrseq_register); 108 109void mmc_pwrseq_unregister(struct mmc_pwrseq *pwrseq) 110{ 111 if (pwrseq) { 112 mutex_lock(&pwrseq_list_mutex); 113 list_del(&pwrseq->pwrseq_node); 114 mutex_unlock(&pwrseq_list_mutex); 115 } 116} 117EXPORT_SYMBOL_GPL(mmc_pwrseq_unregister); 118