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