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 | |
21 | int mmc_pwrseq_alloc(struct mmc_host *host) |
22 | { |
23 | struct device_node *np; |
24 | struct mmc_pwrseq *p; |
25 | |
26 | np = of_parse_phandle(np: host->parent->of_node, phandle_name: "mmc-pwrseq", index: 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(dev: p->dev, np)) { |
33 | if (!try_module_get(module: 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(node: np); |
44 | mutex_unlock(lock: &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 | |
54 | void 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 | |
62 | void 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 | |
70 | void 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 | |
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 | |
86 | void mmc_pwrseq_free(struct mmc_host *host) |
87 | { |
88 | struct mmc_pwrseq *pwrseq = host->pwrseq; |
89 | |
90 | if (pwrseq) { |
91 | module_put(module: 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; |
100 | |
101 | mutex_lock(&pwrseq_list_mutex); |
102 | list_add(new: &pwrseq->pwrseq_node, head: &pwrseq_list); |
103 | mutex_unlock(lock: &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(entry: &pwrseq->pwrseq_node); |
114 | mutex_unlock(lock: &pwrseq_list_mutex); |
115 | } |
116 | } |
117 | EXPORT_SYMBOL_GPL(mmc_pwrseq_unregister); |
118 |