Commit | Line | Data |
---|---|---|
af873fce | 1 | // SPDX-License-Identifier: GPL-2.0-only |
3aa8793f UH |
2 | /* |
3 | * Copyright (C) 2014 Linaro Ltd | |
4 | * | |
5 | * Author: Ulf Hansson <ulf.hansson@linaro.org> | |
6 | * | |
3aa8793f UH |
7 | * MMC power sequence management |
8 | */ | |
8c96f89c | 9 | #include <linux/kernel.h> |
8c96f89c | 10 | #include <linux/err.h> |
d97a1e5d | 11 | #include <linux/module.h> |
8c96f89c | 12 | #include <linux/of.h> |
8c96f89c | 13 | |
3aa8793f UH |
14 | #include <linux/mmc/host.h> |
15 | ||
16 | #include "pwrseq.h" | |
17 | ||
d97a1e5d SK |
18 | static DEFINE_MUTEX(pwrseq_list_mutex); |
19 | static LIST_HEAD(pwrseq_list); | |
3aa8793f UH |
20 | |
21 | int mmc_pwrseq_alloc(struct mmc_host *host) | |
22 | { | |
8c96f89c | 23 | struct device_node *np; |
d97a1e5d | 24 | struct mmc_pwrseq *p; |
8c96f89c UH |
25 | |
26 | np = of_parse_phandle(host->parent->of_node, "mmc-pwrseq", 0); | |
27 | if (!np) | |
28 | return 0; | |
29 | ||
d97a1e5d SK |
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; | |
8c96f89c | 38 | |
d97a1e5d SK |
39 | break; |
40 | } | |
8c96f89c UH |
41 | } |
42 | ||
d97a1e5d SK |
43 | of_node_put(np); |
44 | mutex_unlock(&pwrseq_list_mutex); | |
45 | ||
46 | if (!host->pwrseq) | |
47 | return -EPROBE_DEFER; | |
0f12a0ce | 48 | |
0f12a0ce | 49 | dev_info(host->parent, "allocated mmc-pwrseq\n"); |
8c96f89c | 50 | |
d97a1e5d | 51 | return 0; |
3aa8793f UH |
52 | } |
53 | ||
54 | void mmc_pwrseq_pre_power_on(struct mmc_host *host) | |
55 | { | |
56 | struct mmc_pwrseq *pwrseq = host->pwrseq; | |
57 | ||
d97a1e5d | 58 | if (pwrseq && pwrseq->ops->pre_power_on) |
3aa8793f UH |
59 | pwrseq->ops->pre_power_on(host); |
60 | } | |
61 | ||
62 | void mmc_pwrseq_post_power_on(struct mmc_host *host) | |
63 | { | |
64 | struct mmc_pwrseq *pwrseq = host->pwrseq; | |
65 | ||
d97a1e5d | 66 | if (pwrseq && pwrseq->ops->post_power_on) |
3aa8793f UH |
67 | pwrseq->ops->post_power_on(host); |
68 | } | |
69 | ||
70 | void mmc_pwrseq_power_off(struct mmc_host *host) | |
71 | { | |
72 | struct mmc_pwrseq *pwrseq = host->pwrseq; | |
73 | ||
d97a1e5d | 74 | if (pwrseq && pwrseq->ops->power_off) |
3aa8793f UH |
75 | pwrseq->ops->power_off(host); |
76 | } | |
77 | ||
773a9ef8 UH |
78 | void 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 | ||
3aa8793f UH |
86 | void mmc_pwrseq_free(struct mmc_host *host) |
87 | { | |
88 | struct mmc_pwrseq *pwrseq = host->pwrseq; | |
89 | ||
d97a1e5d SK |
90 | if (pwrseq) { |
91 | module_put(pwrseq->owner); | |
92 | host->pwrseq = NULL; | |
93 | } | |
94 | } | |
95 | ||
96 | int mmc_pwrseq_register(struct mmc_pwrseq *pwrseq) | |
97 | { | |
98 | if (!pwrseq || !pwrseq->ops || !pwrseq->dev) | |
99 | return -EINVAL; | |
0f12a0ce | 100 | |
d97a1e5d SK |
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 | ||
109 | void 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 | } | |
3aa8793f | 116 | } |
d97a1e5d | 117 | EXPORT_SYMBOL_GPL(mmc_pwrseq_unregister); |