1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <linux/clk.h> |
3 | #include <linux/device.h> |
4 | #include <linux/export.h> |
5 | #include <linux/gfp.h> |
6 | |
7 | struct devm_clk_state { |
8 | struct clk *clk; |
9 | void (*exit)(struct clk *clk); |
10 | }; |
11 | |
12 | static void devm_clk_release(struct device *dev, void *res) |
13 | { |
14 | struct devm_clk_state *state = res; |
15 | |
16 | if (state->exit) |
17 | state->exit(state->clk); |
18 | |
19 | clk_put(clk: state->clk); |
20 | } |
21 | |
22 | static struct clk *__devm_clk_get(struct device *dev, const char *id, |
23 | struct clk *(*get)(struct device *dev, const char *id), |
24 | int (*init)(struct clk *clk), |
25 | void (*exit)(struct clk *clk)) |
26 | { |
27 | struct devm_clk_state *state; |
28 | struct clk *clk; |
29 | int ret; |
30 | |
31 | state = devres_alloc(devm_clk_release, sizeof(*state), GFP_KERNEL); |
32 | if (!state) |
33 | return ERR_PTR(error: -ENOMEM); |
34 | |
35 | clk = get(dev, id); |
36 | if (IS_ERR(ptr: clk)) { |
37 | ret = PTR_ERR(ptr: clk); |
38 | goto err_clk_get; |
39 | } |
40 | |
41 | if (init) { |
42 | ret = init(clk); |
43 | if (ret) |
44 | goto err_clk_init; |
45 | } |
46 | |
47 | state->clk = clk; |
48 | state->exit = exit; |
49 | |
50 | devres_add(dev, res: state); |
51 | |
52 | return clk; |
53 | |
54 | err_clk_init: |
55 | |
56 | clk_put(clk); |
57 | err_clk_get: |
58 | |
59 | devres_free(res: state); |
60 | return ERR_PTR(error: ret); |
61 | } |
62 | |
63 | struct clk *devm_clk_get(struct device *dev, const char *id) |
64 | { |
65 | return __devm_clk_get(dev, id, get: clk_get, NULL, NULL); |
66 | } |
67 | EXPORT_SYMBOL(devm_clk_get); |
68 | |
69 | struct clk *devm_clk_get_prepared(struct device *dev, const char *id) |
70 | { |
71 | return __devm_clk_get(dev, id, get: clk_get, init: clk_prepare, exit: clk_unprepare); |
72 | } |
73 | EXPORT_SYMBOL_GPL(devm_clk_get_prepared); |
74 | |
75 | struct clk *devm_clk_get_enabled(struct device *dev, const char *id) |
76 | { |
77 | return __devm_clk_get(dev, id, get: clk_get, |
78 | init: clk_prepare_enable, exit: clk_disable_unprepare); |
79 | } |
80 | EXPORT_SYMBOL_GPL(devm_clk_get_enabled); |
81 | |
82 | struct clk *devm_clk_get_optional(struct device *dev, const char *id) |
83 | { |
84 | return __devm_clk_get(dev, id, get: clk_get_optional, NULL, NULL); |
85 | } |
86 | EXPORT_SYMBOL(devm_clk_get_optional); |
87 | |
88 | struct clk *devm_clk_get_optional_prepared(struct device *dev, const char *id) |
89 | { |
90 | return __devm_clk_get(dev, id, get: clk_get_optional, |
91 | init: clk_prepare, exit: clk_unprepare); |
92 | } |
93 | EXPORT_SYMBOL_GPL(devm_clk_get_optional_prepared); |
94 | |
95 | struct clk *devm_clk_get_optional_enabled(struct device *dev, const char *id) |
96 | { |
97 | return __devm_clk_get(dev, id, get: clk_get_optional, |
98 | init: clk_prepare_enable, exit: clk_disable_unprepare); |
99 | } |
100 | EXPORT_SYMBOL_GPL(devm_clk_get_optional_enabled); |
101 | |
102 | struct clk_bulk_devres { |
103 | struct clk_bulk_data *clks; |
104 | int num_clks; |
105 | }; |
106 | |
107 | static void devm_clk_bulk_release(struct device *dev, void *res) |
108 | { |
109 | struct clk_bulk_devres *devres = res; |
110 | |
111 | clk_bulk_put(num_clks: devres->num_clks, clks: devres->clks); |
112 | } |
113 | |
114 | static int __devm_clk_bulk_get(struct device *dev, int num_clks, |
115 | struct clk_bulk_data *clks, bool optional) |
116 | { |
117 | struct clk_bulk_devres *devres; |
118 | int ret; |
119 | |
120 | devres = devres_alloc(devm_clk_bulk_release, |
121 | sizeof(*devres), GFP_KERNEL); |
122 | if (!devres) |
123 | return -ENOMEM; |
124 | |
125 | if (optional) |
126 | ret = clk_bulk_get_optional(dev, num_clks, clks); |
127 | else |
128 | ret = clk_bulk_get(dev, num_clks, clks); |
129 | if (!ret) { |
130 | devres->clks = clks; |
131 | devres->num_clks = num_clks; |
132 | devres_add(dev, res: devres); |
133 | } else { |
134 | devres_free(res: devres); |
135 | } |
136 | |
137 | return ret; |
138 | } |
139 | |
140 | int __must_check devm_clk_bulk_get(struct device *dev, int num_clks, |
141 | struct clk_bulk_data *clks) |
142 | { |
143 | return __devm_clk_bulk_get(dev, num_clks, clks, optional: false); |
144 | } |
145 | EXPORT_SYMBOL_GPL(devm_clk_bulk_get); |
146 | |
147 | int __must_check devm_clk_bulk_get_optional(struct device *dev, int num_clks, |
148 | struct clk_bulk_data *clks) |
149 | { |
150 | return __devm_clk_bulk_get(dev, num_clks, clks, optional: true); |
151 | } |
152 | EXPORT_SYMBOL_GPL(devm_clk_bulk_get_optional); |
153 | |
154 | static void devm_clk_bulk_release_all(struct device *dev, void *res) |
155 | { |
156 | struct clk_bulk_devres *devres = res; |
157 | |
158 | clk_bulk_put_all(num_clks: devres->num_clks, clks: devres->clks); |
159 | } |
160 | |
161 | int __must_check devm_clk_bulk_get_all(struct device *dev, |
162 | struct clk_bulk_data **clks) |
163 | { |
164 | struct clk_bulk_devres *devres; |
165 | int ret; |
166 | |
167 | devres = devres_alloc(devm_clk_bulk_release_all, |
168 | sizeof(*devres), GFP_KERNEL); |
169 | if (!devres) |
170 | return -ENOMEM; |
171 | |
172 | ret = clk_bulk_get_all(dev, clks: &devres->clks); |
173 | if (ret > 0) { |
174 | *clks = devres->clks; |
175 | devres->num_clks = ret; |
176 | devres_add(dev, res: devres); |
177 | } else { |
178 | devres_free(res: devres); |
179 | } |
180 | |
181 | return ret; |
182 | } |
183 | EXPORT_SYMBOL_GPL(devm_clk_bulk_get_all); |
184 | |
185 | static int devm_clk_match(struct device *dev, void *res, void *data) |
186 | { |
187 | struct clk **c = res; |
188 | if (!c || !*c) { |
189 | WARN_ON(!c || !*c); |
190 | return 0; |
191 | } |
192 | return *c == data; |
193 | } |
194 | |
195 | void devm_clk_put(struct device *dev, struct clk *clk) |
196 | { |
197 | int ret; |
198 | |
199 | ret = devres_release(dev, release: devm_clk_release, match: devm_clk_match, match_data: clk); |
200 | |
201 | WARN_ON(ret); |
202 | } |
203 | EXPORT_SYMBOL(devm_clk_put); |
204 | |
205 | struct clk *devm_get_clk_from_child(struct device *dev, |
206 | struct device_node *np, const char *con_id) |
207 | { |
208 | struct devm_clk_state *state; |
209 | struct clk *clk; |
210 | |
211 | state = devres_alloc(devm_clk_release, sizeof(*state), GFP_KERNEL); |
212 | if (!state) |
213 | return ERR_PTR(error: -ENOMEM); |
214 | |
215 | clk = of_clk_get_by_name(np, name: con_id); |
216 | if (!IS_ERR(ptr: clk)) { |
217 | state->clk = clk; |
218 | devres_add(dev, res: state); |
219 | } else { |
220 | devres_free(res: state); |
221 | } |
222 | |
223 | return clk; |
224 | } |
225 | EXPORT_SYMBOL(devm_get_clk_from_child); |
226 | |