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 18 static DEFINE_MUTEX(pwrseq_list_mutex); 19 static LIST_HEAD(pwrseq_list); 20 mmc_pwrseq_alloc(struct mmc_host * host)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 (device_match_of_node(p->dev, 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 mmc_pwrseq_pre_power_on(struct mmc_host * host)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 mmc_pwrseq_post_power_on(struct mmc_host * host)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 mmc_pwrseq_power_off(struct mmc_host * host)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 mmc_pwrseq_reset(struct mmc_host * host)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 mmc_pwrseq_free(struct mmc_host * host)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 mmc_pwrseq_register(struct mmc_pwrseq * pwrseq)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 } 107 EXPORT_SYMBOL_GPL(mmc_pwrseq_register); 108 mmc_pwrseq_unregister(struct mmc_pwrseq * pwrseq)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 } 117 EXPORT_SYMBOL_GPL(mmc_pwrseq_unregister); 118