Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-only
2 : : /*
3 : : * drivers/clk/clkdev.c
4 : : *
5 : : * Copyright (C) 2008 Russell King.
6 : : *
7 : : * Helper for the clk API to assist looking up a struct clk.
8 : : */
9 : : #include <linux/module.h>
10 : : #include <linux/kernel.h>
11 : : #include <linux/device.h>
12 : : #include <linux/list.h>
13 : : #include <linux/errno.h>
14 : : #include <linux/err.h>
15 : : #include <linux/string.h>
16 : : #include <linux/mutex.h>
17 : : #include <linux/clk.h>
18 : : #include <linux/clkdev.h>
19 : : #include <linux/clk-provider.h>
20 : : #include <linux/of.h>
21 : :
22 : : #include "clk.h"
23 : :
24 : : static LIST_HEAD(clocks);
25 : : static DEFINE_MUTEX(clocks_mutex);
26 : :
27 : : /*
28 : : * Find the correct struct clk for the device and connection ID.
29 : : * We do slightly fuzzy matching here:
30 : : * An entry with a NULL ID is assumed to be a wildcard.
31 : : * If an entry has a device ID, it must match
32 : : * If an entry has a connection ID, it must match
33 : : * Then we take the most specific entry - with the following
34 : : * order of precedence: dev+con > dev only > con only.
35 : : */
36 : 0 : static struct clk_lookup *clk_find(const char *dev_id, const char *con_id)
37 : : {
38 : 0 : struct clk_lookup *p, *cl = NULL;
39 : 0 : int match, best_found = 0, best_possible = 0;
40 : :
41 [ # # ]: 0 : if (dev_id)
42 : 0 : best_possible += 2;
43 [ # # ]: 0 : if (con_id)
44 : 0 : best_possible += 1;
45 : :
46 : 0 : lockdep_assert_held(&clocks_mutex);
47 : :
48 [ # # ]: 0 : list_for_each_entry(p, &clocks, node) {
49 : 0 : match = 0;
50 [ # # ]: 0 : if (p->dev_id) {
51 [ # # # # ]: 0 : if (!dev_id || strcmp(p->dev_id, dev_id))
52 : 0 : continue;
53 : : match += 2;
54 : : }
55 [ # # ]: 0 : if (p->con_id) {
56 [ # # # # ]: 0 : if (!con_id || strcmp(p->con_id, con_id))
57 : 0 : continue;
58 : 0 : match += 1;
59 : : }
60 : :
61 [ # # ]: 0 : if (match > best_found) {
62 : 0 : cl = p;
63 [ # # ]: 0 : if (match != best_possible)
64 : : best_found = match;
65 : : else
66 : : break;
67 : : }
68 : : }
69 : 0 : return cl;
70 : : }
71 : :
72 : 0 : struct clk_hw *clk_find_hw(const char *dev_id, const char *con_id)
73 : : {
74 : 0 : struct clk_lookup *cl;
75 : 0 : struct clk_hw *hw = ERR_PTR(-ENOENT);
76 : :
77 : 0 : mutex_lock(&clocks_mutex);
78 : 0 : cl = clk_find(dev_id, con_id);
79 [ # # ]: 0 : if (cl)
80 : 0 : hw = cl->clk_hw;
81 : 0 : mutex_unlock(&clocks_mutex);
82 : :
83 : 0 : return hw;
84 : : }
85 : :
86 : 0 : static struct clk *__clk_get_sys(struct device *dev, const char *dev_id,
87 : : const char *con_id)
88 : : {
89 : 0 : struct clk_hw *hw = clk_find_hw(dev_id, con_id);
90 : :
91 : 0 : return clk_hw_create_clk(dev, hw, dev_id, con_id);
92 : : }
93 : :
94 : 0 : struct clk *clk_get_sys(const char *dev_id, const char *con_id)
95 : : {
96 : 0 : return __clk_get_sys(NULL, dev_id, con_id);
97 : : }
98 : : EXPORT_SYMBOL(clk_get_sys);
99 : :
100 : 0 : struct clk *clk_get(struct device *dev, const char *con_id)
101 : : {
102 [ # # ]: 0 : const char *dev_id = dev ? dev_name(dev) : NULL;
103 : 0 : struct clk_hw *hw;
104 : :
105 : 0 : if (dev && dev->of_node) {
106 : : hw = of_clk_get_hw(dev->of_node, 0, con_id);
107 : : if (!IS_ERR(hw) || PTR_ERR(hw) == -EPROBE_DEFER)
108 : : return clk_hw_create_clk(dev, hw, dev_id, con_id);
109 : : }
110 : :
111 : 0 : return __clk_get_sys(dev, dev_id, con_id);
112 : : }
113 : : EXPORT_SYMBOL(clk_get);
114 : :
115 : 0 : void clk_put(struct clk *clk)
116 : : {
117 : 0 : __clk_put(clk);
118 : 0 : }
119 : : EXPORT_SYMBOL(clk_put);
120 : :
121 : 0 : static void __clkdev_add(struct clk_lookup *cl)
122 : : {
123 : 0 : mutex_lock(&clocks_mutex);
124 : 0 : list_add_tail(&cl->node, &clocks);
125 : 0 : mutex_unlock(&clocks_mutex);
126 : 0 : }
127 : :
128 : 0 : void clkdev_add(struct clk_lookup *cl)
129 : : {
130 [ # # ]: 0 : if (!cl->clk_hw)
131 : 0 : cl->clk_hw = __clk_get_hw(cl->clk);
132 : 0 : __clkdev_add(cl);
133 : 0 : }
134 : : EXPORT_SYMBOL(clkdev_add);
135 : :
136 : 0 : void clkdev_add_table(struct clk_lookup *cl, size_t num)
137 : : {
138 : 0 : mutex_lock(&clocks_mutex);
139 [ # # ]: 0 : while (num--) {
140 : 0 : cl->clk_hw = __clk_get_hw(cl->clk);
141 : 0 : list_add_tail(&cl->node, &clocks);
142 : 0 : cl++;
143 : : }
144 : 0 : mutex_unlock(&clocks_mutex);
145 : 0 : }
146 : :
147 : : #define MAX_DEV_ID 20
148 : : #define MAX_CON_ID 16
149 : :
150 : : struct clk_lookup_alloc {
151 : : struct clk_lookup cl;
152 : : char dev_id[MAX_DEV_ID];
153 : : char con_id[MAX_CON_ID];
154 : : };
155 : :
156 : : static struct clk_lookup * __ref
157 : 0 : vclkdev_alloc(struct clk_hw *hw, const char *con_id, const char *dev_fmt,
158 : : va_list ap)
159 : : {
160 : 0 : struct clk_lookup_alloc *cla;
161 : :
162 : 0 : cla = kzalloc(sizeof(*cla), GFP_KERNEL);
163 [ # # ]: 0 : if (!cla)
164 : : return NULL;
165 : :
166 : 0 : cla->cl.clk_hw = hw;
167 [ # # ]: 0 : if (con_id) {
168 : 0 : strlcpy(cla->con_id, con_id, sizeof(cla->con_id));
169 : 0 : cla->cl.con_id = cla->con_id;
170 : : }
171 : :
172 [ # # ]: 0 : if (dev_fmt) {
173 : 0 : vscnprintf(cla->dev_id, sizeof(cla->dev_id), dev_fmt, ap);
174 : 0 : cla->cl.dev_id = cla->dev_id;
175 : : }
176 : :
177 : 0 : return &cla->cl;
178 : : }
179 : :
180 : : static struct clk_lookup *
181 : 0 : vclkdev_create(struct clk_hw *hw, const char *con_id, const char *dev_fmt,
182 : : va_list ap)
183 : : {
184 : 0 : struct clk_lookup *cl;
185 : :
186 : 0 : cl = vclkdev_alloc(hw, con_id, dev_fmt, ap);
187 [ # # # # : 0 : if (cl)
# # ]
188 : 0 : __clkdev_add(cl);
189 : :
190 : 0 : return cl;
191 : : }
192 : :
193 : : struct clk_lookup * __ref
194 : 0 : clkdev_alloc(struct clk *clk, const char *con_id, const char *dev_fmt, ...)
195 : : {
196 : 0 : struct clk_lookup *cl;
197 : 0 : va_list ap;
198 : :
199 : 0 : va_start(ap, dev_fmt);
200 : 0 : cl = vclkdev_alloc(__clk_get_hw(clk), con_id, dev_fmt, ap);
201 : 0 : va_end(ap);
202 : :
203 : 0 : return cl;
204 : : }
205 : : EXPORT_SYMBOL(clkdev_alloc);
206 : :
207 : : struct clk_lookup *
208 : 0 : clkdev_hw_alloc(struct clk_hw *hw, const char *con_id, const char *dev_fmt, ...)
209 : : {
210 : 0 : struct clk_lookup *cl;
211 : 0 : va_list ap;
212 : :
213 : 0 : va_start(ap, dev_fmt);
214 : 0 : cl = vclkdev_alloc(hw, con_id, dev_fmt, ap);
215 : 0 : va_end(ap);
216 : :
217 : 0 : return cl;
218 : : }
219 : : EXPORT_SYMBOL(clkdev_hw_alloc);
220 : :
221 : : /**
222 : : * clkdev_create - allocate and add a clkdev lookup structure
223 : : * @clk: struct clk to associate with all clk_lookups
224 : : * @con_id: connection ID string on device
225 : : * @dev_fmt: format string describing device name
226 : : *
227 : : * Returns a clk_lookup structure, which can be later unregistered and
228 : : * freed.
229 : : */
230 : 0 : struct clk_lookup *clkdev_create(struct clk *clk, const char *con_id,
231 : : const char *dev_fmt, ...)
232 : : {
233 : 0 : struct clk_lookup *cl;
234 : 0 : va_list ap;
235 : :
236 : 0 : va_start(ap, dev_fmt);
237 : 0 : cl = vclkdev_create(__clk_get_hw(clk), con_id, dev_fmt, ap);
238 : 0 : va_end(ap);
239 : :
240 : 0 : return cl;
241 : : }
242 : : EXPORT_SYMBOL_GPL(clkdev_create);
243 : :
244 : : /**
245 : : * clkdev_hw_create - allocate and add a clkdev lookup structure
246 : : * @hw: struct clk_hw to associate with all clk_lookups
247 : : * @con_id: connection ID string on device
248 : : * @dev_fmt: format string describing device name
249 : : *
250 : : * Returns a clk_lookup structure, which can be later unregistered and
251 : : * freed.
252 : : */
253 : 0 : struct clk_lookup *clkdev_hw_create(struct clk_hw *hw, const char *con_id,
254 : : const char *dev_fmt, ...)
255 : : {
256 : 0 : struct clk_lookup *cl;
257 : 0 : va_list ap;
258 : :
259 : 0 : va_start(ap, dev_fmt);
260 : 0 : cl = vclkdev_create(hw, con_id, dev_fmt, ap);
261 : 0 : va_end(ap);
262 : :
263 : 0 : return cl;
264 : : }
265 : : EXPORT_SYMBOL_GPL(clkdev_hw_create);
266 : :
267 : 0 : int clk_add_alias(const char *alias, const char *alias_dev_name,
268 : : const char *con_id, struct device *dev)
269 : : {
270 [ # # ]: 0 : struct clk *r = clk_get(dev, con_id);
271 : 0 : struct clk_lookup *l;
272 : :
273 [ # # ]: 0 : if (IS_ERR(r))
274 : 0 : return PTR_ERR(r);
275 : :
276 [ # # ]: 0 : l = clkdev_create(r, alias, alias_dev_name ? "%s" : NULL,
277 : : alias_dev_name);
278 : 0 : clk_put(r);
279 : :
280 [ # # ]: 0 : return l ? 0 : -ENODEV;
281 : : }
282 : : EXPORT_SYMBOL(clk_add_alias);
283 : :
284 : : /*
285 : : * clkdev_drop - remove a clock dynamically allocated
286 : : */
287 : 0 : void clkdev_drop(struct clk_lookup *cl)
288 : : {
289 : 0 : mutex_lock(&clocks_mutex);
290 : 0 : list_del(&cl->node);
291 : 0 : mutex_unlock(&clocks_mutex);
292 : 0 : kfree(cl);
293 : 0 : }
294 : : EXPORT_SYMBOL(clkdev_drop);
295 : :
296 : 0 : static struct clk_lookup *__clk_register_clkdev(struct clk_hw *hw,
297 : : const char *con_id,
298 : : const char *dev_id, ...)
299 : : {
300 : 0 : struct clk_lookup *cl;
301 : 0 : va_list ap;
302 : :
303 : 0 : va_start(ap, dev_id);
304 : 0 : cl = vclkdev_create(hw, con_id, dev_id, ap);
305 : 0 : va_end(ap);
306 : :
307 : 0 : return cl;
308 : : }
309 : :
310 : 0 : static int do_clk_register_clkdev(struct clk_hw *hw,
311 : : struct clk_lookup **cl, const char *con_id, const char *dev_id)
312 : : {
313 [ # # ]: 0 : if (IS_ERR(hw))
314 : 0 : return PTR_ERR(hw);
315 : : /*
316 : : * Since dev_id can be NULL, and NULL is handled specially, we must
317 : : * pass it as either a NULL format string, or with "%s".
318 : : */
319 [ # # ]: 0 : if (dev_id)
320 : 0 : *cl = __clk_register_clkdev(hw, con_id, "%s", dev_id);
321 : : else
322 : 0 : *cl = __clk_register_clkdev(hw, con_id, NULL);
323 : :
324 [ # # ]: 0 : return *cl ? 0 : -ENOMEM;
325 : : }
326 : :
327 : : /**
328 : : * clk_register_clkdev - register one clock lookup for a struct clk
329 : : * @clk: struct clk to associate with all clk_lookups
330 : : * @con_id: connection ID string on device
331 : : * @dev_id: string describing device name
332 : : *
333 : : * con_id or dev_id may be NULL as a wildcard, just as in the rest of
334 : : * clkdev.
335 : : *
336 : : * To make things easier for mass registration, we detect error clks
337 : : * from a previous clk_register() call, and return the error code for
338 : : * those. This is to permit this function to be called immediately
339 : : * after clk_register().
340 : : */
341 : 0 : int clk_register_clkdev(struct clk *clk, const char *con_id,
342 : : const char *dev_id)
343 : : {
344 : 0 : struct clk_lookup *cl;
345 : :
346 [ # # ]: 0 : if (IS_ERR(clk))
347 : 0 : return PTR_ERR(clk);
348 : :
349 : 0 : return do_clk_register_clkdev(__clk_get_hw(clk), &cl, con_id,
350 : : dev_id);
351 : : }
352 : : EXPORT_SYMBOL(clk_register_clkdev);
353 : :
354 : : /**
355 : : * clk_hw_register_clkdev - register one clock lookup for a struct clk_hw
356 : : * @hw: struct clk_hw to associate with all clk_lookups
357 : : * @con_id: connection ID string on device
358 : : * @dev_id: format string describing device name
359 : : *
360 : : * con_id or dev_id may be NULL as a wildcard, just as in the rest of
361 : : * clkdev.
362 : : *
363 : : * To make things easier for mass registration, we detect error clk_hws
364 : : * from a previous clk_hw_register_*() call, and return the error code for
365 : : * those. This is to permit this function to be called immediately
366 : : * after clk_hw_register_*().
367 : : */
368 : 0 : int clk_hw_register_clkdev(struct clk_hw *hw, const char *con_id,
369 : : const char *dev_id)
370 : : {
371 : 0 : struct clk_lookup *cl;
372 : :
373 : 0 : return do_clk_register_clkdev(hw, &cl, con_id, dev_id);
374 : : }
375 : : EXPORT_SYMBOL(clk_hw_register_clkdev);
376 : :
377 : 0 : static void devm_clkdev_release(struct device *dev, void *res)
378 : : {
379 : 0 : clkdev_drop(*(struct clk_lookup **)res);
380 : 0 : }
381 : :
382 : 0 : static int devm_clk_match_clkdev(struct device *dev, void *res, void *data)
383 : : {
384 : 0 : struct clk_lookup **l = res;
385 : :
386 : 0 : return *l == data;
387 : : }
388 : :
389 : : /**
390 : : * devm_clk_release_clkdev - Resource managed clkdev lookup release
391 : : * @dev: device this lookup is bound
392 : : * @con_id: connection ID string on device
393 : : * @dev_id: format string describing device name
394 : : *
395 : : * Drop the clkdev lookup created with devm_clk_hw_register_clkdev.
396 : : * Normally this function will not need to be called and the resource
397 : : * management code will ensure that the resource is freed.
398 : : */
399 : 0 : void devm_clk_release_clkdev(struct device *dev, const char *con_id,
400 : : const char *dev_id)
401 : : {
402 : 0 : struct clk_lookup *cl;
403 : 0 : int rval;
404 : :
405 : 0 : mutex_lock(&clocks_mutex);
406 : 0 : cl = clk_find(dev_id, con_id);
407 : 0 : mutex_unlock(&clocks_mutex);
408 : :
409 [ # # ]: 0 : WARN_ON(!cl);
410 : 0 : rval = devres_release(dev, devm_clkdev_release,
411 : : devm_clk_match_clkdev, cl);
412 [ # # ]: 0 : WARN_ON(rval);
413 : 0 : }
414 : : EXPORT_SYMBOL(devm_clk_release_clkdev);
415 : :
416 : : /**
417 : : * devm_clk_hw_register_clkdev - managed clk lookup registration for clk_hw
418 : : * @dev: device this lookup is bound
419 : : * @hw: struct clk_hw to associate with all clk_lookups
420 : : * @con_id: connection ID string on device
421 : : * @dev_id: format string describing device name
422 : : *
423 : : * con_id or dev_id may be NULL as a wildcard, just as in the rest of
424 : : * clkdev.
425 : : *
426 : : * To make things easier for mass registration, we detect error clk_hws
427 : : * from a previous clk_hw_register_*() call, and return the error code for
428 : : * those. This is to permit this function to be called immediately
429 : : * after clk_hw_register_*().
430 : : */
431 : 0 : int devm_clk_hw_register_clkdev(struct device *dev, struct clk_hw *hw,
432 : : const char *con_id, const char *dev_id)
433 : : {
434 : 0 : int rval = -ENOMEM;
435 : 0 : struct clk_lookup **cl;
436 : :
437 : 0 : cl = devres_alloc(devm_clkdev_release, sizeof(*cl), GFP_KERNEL);
438 [ # # ]: 0 : if (cl) {
439 : 0 : rval = do_clk_register_clkdev(hw, cl, con_id, dev_id);
440 [ # # ]: 0 : if (!rval)
441 : 0 : devres_add(dev, cl);
442 : : else
443 : 0 : devres_free(cl);
444 : : }
445 : 0 : return rval;
446 : : }
447 : : EXPORT_SYMBOL(devm_clk_hw_register_clkdev);
|