]>
Commit | Line | Data |
---|---|---|
ae6b4d85 LW |
1 | /* |
2 | * Core driver for the pin config portions of the pin control subsystem | |
3 | * | |
4 | * Copyright (C) 2011 ST-Ericsson SA | |
5 | * Written on behalf of Linaro for ST-Ericsson | |
6 | * | |
7 | * Author: Linus Walleij <linus.walleij@linaro.org> | |
8 | * | |
9 | * License terms: GNU General Public License (GPL) version 2 | |
10 | */ | |
11 | #define pr_fmt(fmt) "pinconfig core: " fmt | |
12 | ||
13 | #include <linux/kernel.h> | |
14 | #include <linux/module.h> | |
15 | #include <linux/init.h> | |
16 | #include <linux/device.h> | |
17 | #include <linux/slab.h> | |
18 | #include <linux/debugfs.h> | |
19 | #include <linux/seq_file.h> | |
20 | #include <linux/pinctrl/machine.h> | |
21 | #include <linux/pinctrl/pinctrl.h> | |
22 | #include <linux/pinctrl/pinconf.h> | |
23 | #include "core.h" | |
24 | #include "pinconf.h" | |
25 | ||
2b694250 SW |
26 | int pinconf_check_ops(struct pinctrl_dev *pctldev) |
27 | { | |
28 | const struct pinconf_ops *ops = pctldev->desc->confops; | |
29 | ||
30 | /* We must be able to read out pin status */ | |
31 | if (!ops->pin_config_get && !ops->pin_config_group_get) | |
32 | return -EINVAL; | |
33 | /* We have to be able to config the pins in SOME way */ | |
34 | if (!ops->pin_config_set && !ops->pin_config_group_set) | |
35 | return -EINVAL; | |
36 | return 0; | |
37 | } | |
38 | ||
1e2082b5 SW |
39 | int pinconf_validate_map(struct pinctrl_map const *map, int i) |
40 | { | |
41 | if (!map->data.configs.group_or_pin) { | |
42 | pr_err("failed to register map %s (%d): no group/pin given\n", | |
43 | map->name, i); | |
44 | return -EINVAL; | |
45 | } | |
46 | ||
47 | if (map->data.configs.num_configs && | |
48 | !map->data.configs.configs) { | |
49 | pr_err("failed to register map %s (%d): no configs ptr given\n", | |
50 | map->name, i); | |
51 | return -EINVAL; | |
52 | } | |
53 | ||
54 | return 0; | |
55 | } | |
56 | ||
394349f7 | 57 | int pin_config_get_for_pin(struct pinctrl_dev *pctldev, unsigned pin, |
ae6b4d85 LW |
58 | unsigned long *config) |
59 | { | |
60 | const struct pinconf_ops *ops = pctldev->desc->confops; | |
61 | ||
62 | if (!ops || !ops->pin_config_get) { | |
51cd24ee | 63 | dev_err(pctldev->dev, "cannot get pin configuration, missing " |
ae6b4d85 LW |
64 | "pin_config_get() function in driver\n"); |
65 | return -EINVAL; | |
66 | } | |
67 | ||
68 | return ops->pin_config_get(pctldev, pin, config); | |
69 | } | |
70 | ||
71 | /** | |
72 | * pin_config_get() - get the configuration of a single pin parameter | |
43699dea | 73 | * @dev_name: name of the pin controller device for this pin |
ae6b4d85 LW |
74 | * @name: name of the pin to get the config for |
75 | * @config: the config pointed to by this argument will be filled in with the | |
76 | * current pin state, it can be used directly by drivers as a numeral, or | |
77 | * it can be dereferenced to any struct. | |
78 | */ | |
43699dea | 79 | int pin_config_get(const char *dev_name, const char *name, |
ae6b4d85 LW |
80 | unsigned long *config) |
81 | { | |
43699dea | 82 | struct pinctrl_dev *pctldev; |
ae6b4d85 LW |
83 | int pin; |
84 | ||
57b676f9 SW |
85 | mutex_lock(&pinctrl_mutex); |
86 | ||
9dfac4fd | 87 | pctldev = get_pinctrl_dev_from_devname(dev_name); |
57b676f9 SW |
88 | if (!pctldev) { |
89 | pin = -EINVAL; | |
90 | goto unlock; | |
91 | } | |
43699dea | 92 | |
ae6b4d85 LW |
93 | pin = pin_get_from_name(pctldev, name); |
94 | if (pin < 0) | |
57b676f9 | 95 | goto unlock; |
ae6b4d85 | 96 | |
57b676f9 SW |
97 | pin = pin_config_get_for_pin(pctldev, pin, config); |
98 | ||
99 | unlock: | |
100 | mutex_unlock(&pinctrl_mutex); | |
101 | return pin; | |
ae6b4d85 LW |
102 | } |
103 | EXPORT_SYMBOL(pin_config_get); | |
104 | ||
2b694250 | 105 | static int pin_config_set_for_pin(struct pinctrl_dev *pctldev, unsigned pin, |
ae6b4d85 LW |
106 | unsigned long config) |
107 | { | |
108 | const struct pinconf_ops *ops = pctldev->desc->confops; | |
109 | int ret; | |
110 | ||
111 | if (!ops || !ops->pin_config_set) { | |
51cd24ee | 112 | dev_err(pctldev->dev, "cannot configure pin, missing " |
ae6b4d85 LW |
113 | "config function in driver\n"); |
114 | return -EINVAL; | |
115 | } | |
116 | ||
117 | ret = ops->pin_config_set(pctldev, pin, config); | |
118 | if (ret) { | |
51cd24ee | 119 | dev_err(pctldev->dev, |
ae6b4d85 LW |
120 | "unable to set pin configuration on pin %d\n", pin); |
121 | return ret; | |
122 | } | |
123 | ||
124 | return 0; | |
125 | } | |
126 | ||
127 | /** | |
128 | * pin_config_set() - set the configuration of a single pin parameter | |
43699dea | 129 | * @dev_name: name of pin controller device for this pin |
ae6b4d85 LW |
130 | * @name: name of the pin to set the config for |
131 | * @config: the config in this argument will contain the desired pin state, it | |
132 | * can be used directly by drivers as a numeral, or it can be dereferenced | |
133 | * to any struct. | |
134 | */ | |
43699dea | 135 | int pin_config_set(const char *dev_name, const char *name, |
ae6b4d85 LW |
136 | unsigned long config) |
137 | { | |
43699dea | 138 | struct pinctrl_dev *pctldev; |
57b676f9 SW |
139 | int pin, ret; |
140 | ||
141 | mutex_lock(&pinctrl_mutex); | |
ae6b4d85 | 142 | |
9dfac4fd | 143 | pctldev = get_pinctrl_dev_from_devname(dev_name); |
57b676f9 SW |
144 | if (!pctldev) { |
145 | ret = -EINVAL; | |
146 | goto unlock; | |
147 | } | |
43699dea | 148 | |
ae6b4d85 | 149 | pin = pin_get_from_name(pctldev, name); |
57b676f9 SW |
150 | if (pin < 0) { |
151 | ret = pin; | |
152 | goto unlock; | |
153 | } | |
154 | ||
155 | ret = pin_config_set_for_pin(pctldev, pin, config); | |
ae6b4d85 | 156 | |
57b676f9 SW |
157 | unlock: |
158 | mutex_unlock(&pinctrl_mutex); | |
159 | return ret; | |
ae6b4d85 LW |
160 | } |
161 | EXPORT_SYMBOL(pin_config_set); | |
162 | ||
43699dea | 163 | int pin_config_group_get(const char *dev_name, const char *pin_group, |
ae6b4d85 LW |
164 | unsigned long *config) |
165 | { | |
43699dea SW |
166 | struct pinctrl_dev *pctldev; |
167 | const struct pinconf_ops *ops; | |
57b676f9 SW |
168 | int selector, ret; |
169 | ||
170 | mutex_lock(&pinctrl_mutex); | |
ae6b4d85 | 171 | |
9dfac4fd | 172 | pctldev = get_pinctrl_dev_from_devname(dev_name); |
57b676f9 SW |
173 | if (!pctldev) { |
174 | ret = -EINVAL; | |
175 | goto unlock; | |
176 | } | |
43699dea SW |
177 | ops = pctldev->desc->confops; |
178 | ||
ae6b4d85 | 179 | if (!ops || !ops->pin_config_group_get) { |
51cd24ee | 180 | dev_err(pctldev->dev, "cannot get configuration for pin " |
ae6b4d85 LW |
181 | "group, missing group config get function in " |
182 | "driver\n"); | |
57b676f9 SW |
183 | ret = -EINVAL; |
184 | goto unlock; | |
ae6b4d85 LW |
185 | } |
186 | ||
187 | selector = pinctrl_get_group_selector(pctldev, pin_group); | |
57b676f9 SW |
188 | if (selector < 0) { |
189 | ret = selector; | |
190 | goto unlock; | |
191 | } | |
ae6b4d85 | 192 | |
57b676f9 SW |
193 | ret = ops->pin_config_group_get(pctldev, selector, config); |
194 | ||
195 | unlock: | |
196 | mutex_unlock(&pinctrl_mutex); | |
197 | return ret; | |
ae6b4d85 LW |
198 | } |
199 | EXPORT_SYMBOL(pin_config_group_get); | |
200 | ||
43699dea | 201 | int pin_config_group_set(const char *dev_name, const char *pin_group, |
ae6b4d85 LW |
202 | unsigned long config) |
203 | { | |
43699dea SW |
204 | struct pinctrl_dev *pctldev; |
205 | const struct pinconf_ops *ops; | |
206 | const struct pinctrl_ops *pctlops; | |
ae6b4d85 LW |
207 | int selector; |
208 | const unsigned *pins; | |
209 | unsigned num_pins; | |
210 | int ret; | |
211 | int i; | |
212 | ||
57b676f9 SW |
213 | mutex_lock(&pinctrl_mutex); |
214 | ||
9dfac4fd | 215 | pctldev = get_pinctrl_dev_from_devname(dev_name); |
57b676f9 SW |
216 | if (!pctldev) { |
217 | ret = -EINVAL; | |
218 | goto unlock; | |
219 | } | |
43699dea SW |
220 | ops = pctldev->desc->confops; |
221 | pctlops = pctldev->desc->pctlops; | |
222 | ||
ae6b4d85 | 223 | if (!ops || (!ops->pin_config_group_set && !ops->pin_config_set)) { |
51cd24ee | 224 | dev_err(pctldev->dev, "cannot configure pin group, missing " |
ae6b4d85 | 225 | "config function in driver\n"); |
57b676f9 SW |
226 | ret = -EINVAL; |
227 | goto unlock; | |
ae6b4d85 LW |
228 | } |
229 | ||
230 | selector = pinctrl_get_group_selector(pctldev, pin_group); | |
57b676f9 SW |
231 | if (selector < 0) { |
232 | ret = selector; | |
233 | goto unlock; | |
234 | } | |
ae6b4d85 LW |
235 | |
236 | ret = pctlops->get_group_pins(pctldev, selector, &pins, &num_pins); | |
237 | if (ret) { | |
51cd24ee | 238 | dev_err(pctldev->dev, "cannot configure pin group, error " |
ae6b4d85 | 239 | "getting pins\n"); |
57b676f9 | 240 | goto unlock; |
ae6b4d85 LW |
241 | } |
242 | ||
243 | /* | |
244 | * If the pin controller supports handling entire groups we use that | |
245 | * capability. | |
246 | */ | |
247 | if (ops->pin_config_group_set) { | |
248 | ret = ops->pin_config_group_set(pctldev, selector, config); | |
249 | /* | |
250 | * If the pin controller prefer that a certain group be handled | |
251 | * pin-by-pin as well, it returns -EAGAIN. | |
252 | */ | |
253 | if (ret != -EAGAIN) | |
57b676f9 | 254 | goto unlock; |
ae6b4d85 LW |
255 | } |
256 | ||
257 | /* | |
258 | * If the controller cannot handle entire groups, we configure each pin | |
259 | * individually. | |
260 | */ | |
57b676f9 SW |
261 | if (!ops->pin_config_set) { |
262 | ret = 0; | |
263 | goto unlock; | |
264 | } | |
ae6b4d85 LW |
265 | |
266 | for (i = 0; i < num_pins; i++) { | |
267 | ret = ops->pin_config_set(pctldev, pins[i], config); | |
268 | if (ret < 0) | |
57b676f9 | 269 | goto unlock; |
ae6b4d85 LW |
270 | } |
271 | ||
57b676f9 SW |
272 | ret = 0; |
273 | ||
274 | unlock: | |
275 | mutex_unlock(&pinctrl_mutex); | |
276 | ||
277 | return ret; | |
ae6b4d85 LW |
278 | } |
279 | EXPORT_SYMBOL(pin_config_group_set); | |
280 | ||
1e2082b5 SW |
281 | int pinconf_map_to_setting(struct pinctrl_map const *map, |
282 | struct pinctrl_setting *setting) | |
283 | { | |
284 | struct pinctrl_dev *pctldev = setting->pctldev; | |
70b36378 | 285 | int pin; |
1e2082b5 SW |
286 | |
287 | switch (setting->type) { | |
288 | case PIN_MAP_TYPE_CONFIGS_PIN: | |
70b36378 LW |
289 | pin = pin_get_from_name(pctldev, |
290 | map->data.configs.group_or_pin); | |
291 | if (pin < 0) { | |
292 | dev_err(pctldev->dev, "could not map pin config for \"%s\"", | |
293 | map->data.configs.group_or_pin); | |
294 | return pin; | |
295 | } | |
296 | setting->data.configs.group_or_pin = pin; | |
1e2082b5 SW |
297 | break; |
298 | case PIN_MAP_TYPE_CONFIGS_GROUP: | |
70b36378 LW |
299 | pin = pinctrl_get_group_selector(pctldev, |
300 | map->data.configs.group_or_pin); | |
301 | if (pin < 0) { | |
302 | dev_err(pctldev->dev, "could not map group config for \"%s\"", | |
303 | map->data.configs.group_or_pin); | |
304 | return pin; | |
305 | } | |
306 | setting->data.configs.group_or_pin = pin; | |
1e2082b5 SW |
307 | break; |
308 | default: | |
309 | return -EINVAL; | |
310 | } | |
311 | ||
312 | setting->data.configs.num_configs = map->data.configs.num_configs; | |
313 | setting->data.configs.configs = map->data.configs.configs; | |
314 | ||
315 | return 0; | |
316 | } | |
317 | ||
318 | void pinconf_free_setting(struct pinctrl_setting const *setting) | |
319 | { | |
320 | } | |
321 | ||
322 | int pinconf_apply_setting(struct pinctrl_setting const *setting) | |
323 | { | |
324 | struct pinctrl_dev *pctldev = setting->pctldev; | |
325 | const struct pinconf_ops *ops = pctldev->desc->confops; | |
326 | int i, ret; | |
327 | ||
328 | if (!ops) { | |
329 | dev_err(pctldev->dev, "missing confops\n"); | |
330 | return -EINVAL; | |
331 | } | |
332 | ||
333 | switch (setting->type) { | |
334 | case PIN_MAP_TYPE_CONFIGS_PIN: | |
335 | if (!ops->pin_config_set) { | |
336 | dev_err(pctldev->dev, "missing pin_config_set op\n"); | |
337 | return -EINVAL; | |
338 | } | |
339 | for (i = 0; i < setting->data.configs.num_configs; i++) { | |
340 | ret = ops->pin_config_set(pctldev, | |
341 | setting->data.configs.group_or_pin, | |
342 | setting->data.configs.configs[i]); | |
343 | if (ret < 0) { | |
344 | dev_err(pctldev->dev, | |
345 | "pin_config_set op failed for pin %d config %08lx\n", | |
346 | setting->data.configs.group_or_pin, | |
347 | setting->data.configs.configs[i]); | |
348 | return ret; | |
349 | } | |
350 | } | |
351 | break; | |
352 | case PIN_MAP_TYPE_CONFIGS_GROUP: | |
353 | if (!ops->pin_config_group_set) { | |
354 | dev_err(pctldev->dev, | |
355 | "missing pin_config_group_set op\n"); | |
356 | return -EINVAL; | |
357 | } | |
358 | for (i = 0; i < setting->data.configs.num_configs; i++) { | |
359 | ret = ops->pin_config_group_set(pctldev, | |
360 | setting->data.configs.group_or_pin, | |
361 | setting->data.configs.configs[i]); | |
362 | if (ret < 0) { | |
363 | dev_err(pctldev->dev, | |
364 | "pin_config_group_set op failed for group %d config %08lx\n", | |
365 | setting->data.configs.group_or_pin, | |
366 | setting->data.configs.configs[i]); | |
367 | return ret; | |
368 | } | |
369 | } | |
370 | break; | |
371 | default: | |
372 | return -EINVAL; | |
373 | } | |
374 | ||
375 | return 0; | |
376 | } | |
377 | ||
ae6b4d85 LW |
378 | #ifdef CONFIG_DEBUG_FS |
379 | ||
1e2082b5 SW |
380 | void pinconf_show_map(struct seq_file *s, struct pinctrl_map const *map) |
381 | { | |
6cb41587 SW |
382 | struct pinctrl_dev *pctldev; |
383 | const struct pinconf_ops *confops; | |
1e2082b5 SW |
384 | int i; |
385 | ||
6cb41587 SW |
386 | pctldev = get_pinctrl_dev_from_devname(map->ctrl_dev_name); |
387 | if (pctldev) | |
388 | confops = pctldev->desc->confops; | |
389 | else | |
390 | confops = NULL; | |
391 | ||
1e2082b5 SW |
392 | switch (map->type) { |
393 | case PIN_MAP_TYPE_CONFIGS_PIN: | |
394 | seq_printf(s, "pin "); | |
395 | break; | |
396 | case PIN_MAP_TYPE_CONFIGS_GROUP: | |
397 | seq_printf(s, "group "); | |
398 | break; | |
399 | default: | |
400 | break; | |
401 | } | |
402 | ||
403 | seq_printf(s, "%s\n", map->data.configs.group_or_pin); | |
404 | ||
6cb41587 SW |
405 | for (i = 0; i < map->data.configs.num_configs; i++) { |
406 | seq_printf(s, "config "); | |
407 | if (confops && confops->pin_config_config_dbg_show) | |
408 | confops->pin_config_config_dbg_show(pctldev, s, | |
409 | map->data.configs.configs[i]); | |
410 | else | |
411 | seq_printf(s, "%08lx", map->data.configs.configs[i]); | |
412 | seq_printf(s, "\n"); | |
413 | } | |
1e2082b5 SW |
414 | } |
415 | ||
416 | void pinconf_show_setting(struct seq_file *s, | |
417 | struct pinctrl_setting const *setting) | |
418 | { | |
419 | struct pinctrl_dev *pctldev = setting->pctldev; | |
420 | const struct pinctrl_ops *pctlops = pctldev->desc->pctlops; | |
6cb41587 | 421 | const struct pinconf_ops *confops = pctldev->desc->confops; |
1e2082b5 SW |
422 | struct pin_desc *desc; |
423 | int i; | |
424 | ||
425 | switch (setting->type) { | |
426 | case PIN_MAP_TYPE_CONFIGS_PIN: | |
427 | desc = pin_desc_get(setting->pctldev, | |
428 | setting->data.configs.group_or_pin); | |
429 | seq_printf(s, "pin %s (%d)", | |
430 | desc->name ? desc->name : "unnamed", | |
431 | setting->data.configs.group_or_pin); | |
432 | break; | |
433 | case PIN_MAP_TYPE_CONFIGS_GROUP: | |
434 | seq_printf(s, "group %s (%d)", | |
435 | pctlops->get_group_name(pctldev, | |
436 | setting->data.configs.group_or_pin), | |
437 | setting->data.configs.group_or_pin); | |
438 | break; | |
439 | default: | |
440 | break; | |
441 | } | |
442 | ||
443 | /* | |
444 | * FIXME: We should really get the pin controler to dump the config | |
445 | * values, so they can be decoded to something meaningful. | |
446 | */ | |
6cb41587 SW |
447 | for (i = 0; i < setting->data.configs.num_configs; i++) { |
448 | seq_printf(s, " "); | |
449 | if (confops && confops->pin_config_config_dbg_show) | |
450 | confops->pin_config_config_dbg_show(pctldev, s, | |
451 | setting->data.configs.configs[i]); | |
452 | else | |
453 | seq_printf(s, "%08lx", | |
454 | setting->data.configs.configs[i]); | |
455 | } | |
1e2082b5 SW |
456 | |
457 | seq_printf(s, "\n"); | |
458 | } | |
459 | ||
ae6b4d85 LW |
460 | static void pinconf_dump_pin(struct pinctrl_dev *pctldev, |
461 | struct seq_file *s, int pin) | |
462 | { | |
463 | const struct pinconf_ops *ops = pctldev->desc->confops; | |
464 | ||
394349f7 LW |
465 | /* no-op when not using generic pin config */ |
466 | pinconf_generic_dump_pin(pctldev, s, pin); | |
ae6b4d85 LW |
467 | if (ops && ops->pin_config_dbg_show) |
468 | ops->pin_config_dbg_show(pctldev, s, pin); | |
469 | } | |
470 | ||
471 | static int pinconf_pins_show(struct seq_file *s, void *what) | |
472 | { | |
473 | struct pinctrl_dev *pctldev = s->private; | |
ad8bb720 | 474 | const struct pinconf_ops *ops = pctldev->desc->confops; |
706e8520 | 475 | unsigned i, pin; |
ae6b4d85 | 476 | |
ad8bb720 DA |
477 | if (!ops || !ops->pin_config_get) |
478 | return 0; | |
479 | ||
ae6b4d85 | 480 | seq_puts(s, "Pin config settings per pin\n"); |
2aeefe02 | 481 | seq_puts(s, "Format: pin (name): configs\n"); |
ae6b4d85 | 482 | |
57b676f9 SW |
483 | mutex_lock(&pinctrl_mutex); |
484 | ||
706e8520 | 485 | /* The pin number can be retrived from the pin controller descriptor */ |
546edd83 | 486 | for (i = 0; i < pctldev->desc->npins; i++) { |
ae6b4d85 LW |
487 | struct pin_desc *desc; |
488 | ||
706e8520 | 489 | pin = pctldev->desc->pins[i].number; |
ae6b4d85 | 490 | desc = pin_desc_get(pctldev, pin); |
706e8520 | 491 | /* Skip if we cannot search the pin */ |
ae6b4d85 LW |
492 | if (desc == NULL) |
493 | continue; | |
494 | ||
495 | seq_printf(s, "pin %d (%s):", pin, | |
496 | desc->name ? desc->name : "unnamed"); | |
497 | ||
498 | pinconf_dump_pin(pctldev, s, pin); | |
499 | ||
500 | seq_printf(s, "\n"); | |
501 | } | |
502 | ||
57b676f9 SW |
503 | mutex_unlock(&pinctrl_mutex); |
504 | ||
ae6b4d85 LW |
505 | return 0; |
506 | } | |
507 | ||
508 | static void pinconf_dump_group(struct pinctrl_dev *pctldev, | |
509 | struct seq_file *s, unsigned selector, | |
510 | const char *gname) | |
511 | { | |
512 | const struct pinconf_ops *ops = pctldev->desc->confops; | |
513 | ||
394349f7 LW |
514 | /* no-op when not using generic pin config */ |
515 | pinconf_generic_dump_group(pctldev, s, gname); | |
ae6b4d85 LW |
516 | if (ops && ops->pin_config_group_dbg_show) |
517 | ops->pin_config_group_dbg_show(pctldev, s, selector); | |
518 | } | |
519 | ||
520 | static int pinconf_groups_show(struct seq_file *s, void *what) | |
521 | { | |
522 | struct pinctrl_dev *pctldev = s->private; | |
523 | const struct pinctrl_ops *pctlops = pctldev->desc->pctlops; | |
524 | const struct pinconf_ops *ops = pctldev->desc->confops; | |
d1e90e9e | 525 | unsigned ngroups = pctlops->get_groups_count(pctldev); |
ae6b4d85 LW |
526 | unsigned selector = 0; |
527 | ||
528 | if (!ops || !ops->pin_config_group_get) | |
529 | return 0; | |
530 | ||
531 | seq_puts(s, "Pin config settings per pin group\n"); | |
2aeefe02 | 532 | seq_puts(s, "Format: group (name): configs\n"); |
ae6b4d85 | 533 | |
57b676f9 SW |
534 | mutex_lock(&pinctrl_mutex); |
535 | ||
d1e90e9e | 536 | while (selector < ngroups) { |
ae6b4d85 LW |
537 | const char *gname = pctlops->get_group_name(pctldev, selector); |
538 | ||
539 | seq_printf(s, "%u (%s):", selector, gname); | |
540 | pinconf_dump_group(pctldev, s, selector, gname); | |
f7b9006f SW |
541 | seq_printf(s, "\n"); |
542 | ||
ae6b4d85 LW |
543 | selector++; |
544 | } | |
545 | ||
57b676f9 SW |
546 | mutex_unlock(&pinctrl_mutex); |
547 | ||
ae6b4d85 LW |
548 | return 0; |
549 | } | |
550 | ||
551 | static int pinconf_pins_open(struct inode *inode, struct file *file) | |
552 | { | |
553 | return single_open(file, pinconf_pins_show, inode->i_private); | |
554 | } | |
555 | ||
556 | static int pinconf_groups_open(struct inode *inode, struct file *file) | |
557 | { | |
558 | return single_open(file, pinconf_groups_show, inode->i_private); | |
559 | } | |
560 | ||
561 | static const struct file_operations pinconf_pins_ops = { | |
562 | .open = pinconf_pins_open, | |
563 | .read = seq_read, | |
564 | .llseek = seq_lseek, | |
565 | .release = single_release, | |
566 | }; | |
567 | ||
568 | static const struct file_operations pinconf_groups_ops = { | |
569 | .open = pinconf_groups_open, | |
570 | .read = seq_read, | |
571 | .llseek = seq_lseek, | |
572 | .release = single_release, | |
573 | }; | |
574 | ||
575 | void pinconf_init_device_debugfs(struct dentry *devroot, | |
576 | struct pinctrl_dev *pctldev) | |
577 | { | |
578 | debugfs_create_file("pinconf-pins", S_IFREG | S_IRUGO, | |
579 | devroot, pctldev, &pinconf_pins_ops); | |
580 | debugfs_create_file("pinconf-groups", S_IFREG | S_IRUGO, | |
581 | devroot, pctldev, &pinconf_groups_ops); | |
582 | } | |
583 | ||
584 | #endif |