xref: /openbmc/linux/drivers/clk/clk-devres.c (revision 7effbd18)
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(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(-ENOMEM);
34 
35 	clk = get(dev, id);
36 	if (IS_ERR(clk)) {
37 		ret = PTR_ERR(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, state);
51 
52 	return clk;
53 
54 err_clk_init:
55 
56 	clk_put(clk);
57 err_clk_get:
58 
59 	devres_free(state);
60 	return ERR_PTR(ret);
61 }
62 
63 struct clk *devm_clk_get(struct device *dev, const char *id)
64 {
65 	return __devm_clk_get(dev, id, 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, clk_get, clk_prepare, 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, clk_get,
78 			      clk_prepare_enable, 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, 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, clk_get_optional,
91 			      clk_prepare, 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, clk_get_optional,
98 			      clk_prepare_enable, 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(devres->num_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, devres);
133 	} else {
134 		devres_free(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, 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, 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(devres->num_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, &devres->clks);
173 	if (ret > 0) {
174 		*clks = devres->clks;
175 		devres->num_clks = ret;
176 		devres_add(dev, devres);
177 	} else {
178 		devres_free(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, devm_clk_release, devm_clk_match, 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 clk **ptr, *clk;
209 
210 	ptr = devres_alloc(devm_clk_release, sizeof(*ptr), GFP_KERNEL);
211 	if (!ptr)
212 		return ERR_PTR(-ENOMEM);
213 
214 	clk = of_clk_get_by_name(np, con_id);
215 	if (!IS_ERR(clk)) {
216 		*ptr = clk;
217 		devres_add(dev, ptr);
218 	} else {
219 		devres_free(ptr);
220 	}
221 
222 	return clk;
223 }
224 EXPORT_SYMBOL(devm_get_clk_from_child);
225