Commit | Line | Data |
---|---|---|
394349f7 LW |
1 | /* |
2 | * Core driver for the generic 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 | ||
12 | #define pr_fmt(fmt) "generic pinconfig core: " fmt | |
13 | ||
14 | #include <linux/kernel.h> | |
9cfd1724 | 15 | #include <linux/module.h> |
394349f7 LW |
16 | #include <linux/init.h> |
17 | #include <linux/device.h> | |
18 | #include <linux/slab.h> | |
19 | #include <linux/debugfs.h> | |
20 | #include <linux/seq_file.h> | |
21 | #include <linux/pinctrl/pinctrl.h> | |
22 | #include <linux/pinctrl/pinconf.h> | |
23 | #include <linux/pinctrl/pinconf-generic.h> | |
7db9af4b | 24 | #include <linux/of.h> |
394349f7 LW |
25 | #include "core.h" |
26 | #include "pinconf.h" | |
e81c8f18 | 27 | #include "pinctrl-utils.h" |
394349f7 LW |
28 | |
29 | #ifdef CONFIG_DEBUG_FS | |
30 | ||
31 | struct pin_config_item { | |
32 | const enum pin_config_param param; | |
33 | const char * const display; | |
34 | const char * const format; | |
eec45071 | 35 | bool has_arg; |
394349f7 LW |
36 | }; |
37 | ||
eec45071 SB |
38 | #define PCONFDUMP(a, b, c, d) { .param = a, .display = b, .format = c, \ |
39 | .has_arg = d } | |
394349f7 | 40 | |
2500bcc9 | 41 | static const struct pin_config_item conf_items[] = { |
eec45071 SB |
42 | PCONFDUMP(PIN_CONFIG_BIAS_DISABLE, "input bias disabled", NULL, false), |
43 | PCONFDUMP(PIN_CONFIG_BIAS_HIGH_IMPEDANCE, "input bias high impedance", NULL, false), | |
44 | PCONFDUMP(PIN_CONFIG_BIAS_BUS_HOLD, "input bias bus hold", NULL, false), | |
45 | PCONFDUMP(PIN_CONFIG_BIAS_PULL_UP, "input bias pull up", NULL, false), | |
46 | PCONFDUMP(PIN_CONFIG_BIAS_PULL_DOWN, "input bias pull down", NULL, false), | |
7970cb77 | 47 | PCONFDUMP(PIN_CONFIG_BIAS_PULL_PIN_DEFAULT, |
eec45071 SB |
48 | "input bias pull to pin specific state", NULL, false), |
49 | PCONFDUMP(PIN_CONFIG_DRIVE_PUSH_PULL, "output drive push pull", NULL, false), | |
50 | PCONFDUMP(PIN_CONFIG_DRIVE_OPEN_DRAIN, "output drive open drain", NULL, false), | |
51 | PCONFDUMP(PIN_CONFIG_DRIVE_OPEN_SOURCE, "output drive open source", NULL, false), | |
52 | PCONFDUMP(PIN_CONFIG_DRIVE_STRENGTH, "output drive strength", "mA", true), | |
53 | PCONFDUMP(PIN_CONFIG_INPUT_ENABLE, "input enabled", NULL, false), | |
54 | PCONFDUMP(PIN_CONFIG_INPUT_SCHMITT_ENABLE, "input schmitt enabled", NULL, false), | |
55 | PCONFDUMP(PIN_CONFIG_INPUT_SCHMITT, "input schmitt trigger", NULL, false), | |
56 | PCONFDUMP(PIN_CONFIG_INPUT_DEBOUNCE, "input debounce", "usec", true), | |
57 | PCONFDUMP(PIN_CONFIG_POWER_SOURCE, "pin power source", "selector", true), | |
58 | PCONFDUMP(PIN_CONFIG_SLEW_RATE, "slew rate", NULL, true), | |
59 | PCONFDUMP(PIN_CONFIG_LOW_POWER_MODE, "pin low power", "mode", true), | |
60 | PCONFDUMP(PIN_CONFIG_OUTPUT, "pin output", "level", true), | |
394349f7 LW |
61 | }; |
62 | ||
63 | void pinconf_generic_dump_pin(struct pinctrl_dev *pctldev, | |
64 | struct seq_file *s, unsigned pin) | |
65 | { | |
66 | const struct pinconf_ops *ops = pctldev->desc->confops; | |
67 | int i; | |
68 | ||
69 | if (!ops->is_generic) | |
70 | return; | |
71 | ||
b6465424 | 72 | for (i = 0; i < ARRAY_SIZE(conf_items); i++) { |
394349f7 LW |
73 | unsigned long config; |
74 | int ret; | |
75 | ||
76 | /* We want to check out this parameter */ | |
77 | config = pinconf_to_config_packed(conf_items[i].param, 0); | |
78 | ret = pin_config_get_for_pin(pctldev, pin, &config); | |
79 | /* These are legal errors */ | |
80 | if (ret == -EINVAL || ret == -ENOTSUPP) | |
81 | continue; | |
82 | if (ret) { | |
83 | seq_printf(s, "ERROR READING CONFIG SETTING %d ", i); | |
84 | continue; | |
85 | } | |
86 | /* Space between multiple configs */ | |
87 | seq_puts(s, " "); | |
88 | seq_puts(s, conf_items[i].display); | |
89 | /* Print unit if available */ | |
eec45071 SB |
90 | if (conf_items[i].has_arg) { |
91 | seq_printf(s, " (%u", | |
92 | pinconf_to_config_argument(config)); | |
93 | if (conf_items[i].format) | |
94 | seq_printf(s, " %s)", conf_items[i].format); | |
95 | else | |
96 | seq_puts(s, ")"); | |
97 | } | |
394349f7 LW |
98 | } |
99 | } | |
100 | ||
101 | void pinconf_generic_dump_group(struct pinctrl_dev *pctldev, | |
102 | struct seq_file *s, const char *gname) | |
103 | { | |
104 | const struct pinconf_ops *ops = pctldev->desc->confops; | |
105 | int i; | |
106 | ||
107 | if (!ops->is_generic) | |
108 | return; | |
109 | ||
b6465424 | 110 | for (i = 0; i < ARRAY_SIZE(conf_items); i++) { |
394349f7 LW |
111 | unsigned long config; |
112 | int ret; | |
113 | ||
114 | /* We want to check out this parameter */ | |
115 | config = pinconf_to_config_packed(conf_items[i].param, 0); | |
116 | ret = pin_config_group_get(dev_name(pctldev->dev), gname, | |
117 | &config); | |
118 | /* These are legal errors */ | |
119 | if (ret == -EINVAL || ret == -ENOTSUPP) | |
120 | continue; | |
121 | if (ret) { | |
122 | seq_printf(s, "ERROR READING CONFIG SETTING %d ", i); | |
123 | continue; | |
124 | } | |
125 | /* Space between multiple configs */ | |
126 | seq_puts(s, " "); | |
127 | seq_puts(s, conf_items[i].display); | |
128 | /* Print unit if available */ | |
eec45071 SB |
129 | if (conf_items[i].has_arg) { |
130 | seq_printf(s, " (%u", | |
131 | pinconf_to_config_argument(config)); | |
132 | if (conf_items[i].format) | |
133 | seq_printf(s, " %s)", conf_items[i].format); | |
134 | else | |
135 | seq_puts(s, ")"); | |
136 | } | |
394349f7 LW |
137 | } |
138 | } | |
139 | ||
9cfd1724 HZ |
140 | void pinconf_generic_dump_config(struct pinctrl_dev *pctldev, |
141 | struct seq_file *s, unsigned long config) | |
142 | { | |
143 | int i; | |
144 | ||
b6465424 | 145 | for (i = 0; i < ARRAY_SIZE(conf_items); i++) { |
9cfd1724 HZ |
146 | if (pinconf_to_config_param(config) != conf_items[i].param) |
147 | continue; | |
148 | seq_printf(s, "%s: 0x%x", conf_items[i].display, | |
149 | pinconf_to_config_argument(config)); | |
150 | } | |
151 | } | |
152 | EXPORT_SYMBOL_GPL(pinconf_generic_dump_config); | |
394349f7 | 153 | #endif |
7db9af4b HS |
154 | |
155 | #ifdef CONFIG_OF | |
156 | struct pinconf_generic_dt_params { | |
157 | const char * const property; | |
158 | enum pin_config_param param; | |
159 | u32 default_value; | |
160 | }; | |
161 | ||
2500bcc9 | 162 | static const struct pinconf_generic_dt_params dt_params[] = { |
7db9af4b HS |
163 | { "bias-disable", PIN_CONFIG_BIAS_DISABLE, 0 }, |
164 | { "bias-high-impedance", PIN_CONFIG_BIAS_HIGH_IMPEDANCE, 0 }, | |
165 | { "bias-bus-hold", PIN_CONFIG_BIAS_BUS_HOLD, 0 }, | |
9ee1f7d2 HS |
166 | { "bias-pull-up", PIN_CONFIG_BIAS_PULL_UP, 1 }, |
167 | { "bias-pull-down", PIN_CONFIG_BIAS_PULL_DOWN, 1 }, | |
168 | { "bias-pull-pin-default", PIN_CONFIG_BIAS_PULL_PIN_DEFAULT, 1 }, | |
7db9af4b HS |
169 | { "drive-push-pull", PIN_CONFIG_DRIVE_PUSH_PULL, 0 }, |
170 | { "drive-open-drain", PIN_CONFIG_DRIVE_OPEN_DRAIN, 0 }, | |
171 | { "drive-open-source", PIN_CONFIG_DRIVE_OPEN_SOURCE, 0 }, | |
172 | { "drive-strength", PIN_CONFIG_DRIVE_STRENGTH, 0 }, | |
8ba3f4d0 SY |
173 | { "input-enable", PIN_CONFIG_INPUT_ENABLE, 1 }, |
174 | { "input-disable", PIN_CONFIG_INPUT_ENABLE, 0 }, | |
7db9af4b HS |
175 | { "input-schmitt-enable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 1 }, |
176 | { "input-schmitt-disable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 0 }, | |
7db9af4b | 177 | { "input-debounce", PIN_CONFIG_INPUT_DEBOUNCE, 0 }, |
ca6c5518 | 178 | { "power-source", PIN_CONFIG_POWER_SOURCE, 0 }, |
9ee1f7d2 HS |
179 | { "low-power-enable", PIN_CONFIG_LOW_POWER_MODE, 1 }, |
180 | { "low-power-disable", PIN_CONFIG_LOW_POWER_MODE, 0 }, | |
7db9af4b HS |
181 | { "output-low", PIN_CONFIG_OUTPUT, 0, }, |
182 | { "output-high", PIN_CONFIG_OUTPUT, 1, }, | |
8ba3f4d0 | 183 | { "slew-rate", PIN_CONFIG_SLEW_RATE, 0}, |
7db9af4b HS |
184 | }; |
185 | ||
186 | /** | |
187 | * pinconf_generic_parse_dt_config() | |
188 | * parse the config properties into generic pinconfig values. | |
189 | * @np: node containing the pinconfig properties | |
190 | * @configs: array with nconfigs entries containing the generic pinconf values | |
191 | * @nconfigs: umber of configurations | |
192 | */ | |
193 | int pinconf_generic_parse_dt_config(struct device_node *np, | |
194 | unsigned long **configs, | |
195 | unsigned int *nconfigs) | |
196 | { | |
6abab2d4 | 197 | unsigned long *cfg; |
7db9af4b HS |
198 | unsigned int ncfg = 0; |
199 | int ret; | |
200 | int i; | |
201 | u32 val; | |
202 | ||
203 | if (!np) | |
204 | return -EINVAL; | |
205 | ||
6abab2d4 HS |
206 | /* allocate a temporary array big enough to hold one of each option */ |
207 | cfg = kzalloc(sizeof(*cfg) * ARRAY_SIZE(dt_params), GFP_KERNEL); | |
208 | if (!cfg) | |
209 | return -ENOMEM; | |
210 | ||
7db9af4b | 211 | for (i = 0; i < ARRAY_SIZE(dt_params); i++) { |
2500bcc9 | 212 | const struct pinconf_generic_dt_params *par = &dt_params[i]; |
7db9af4b HS |
213 | ret = of_property_read_u32(np, par->property, &val); |
214 | ||
215 | /* property not found */ | |
216 | if (ret == -EINVAL) | |
217 | continue; | |
218 | ||
219 | /* use default value, when no value is specified */ | |
220 | if (ret) | |
221 | val = par->default_value; | |
222 | ||
223 | pr_debug("found %s with value %u\n", par->property, val); | |
224 | cfg[ncfg] = pinconf_to_config_packed(par->param, val); | |
225 | ncfg++; | |
226 | } | |
227 | ||
6abab2d4 HS |
228 | ret = 0; |
229 | ||
e4a8844c HS |
230 | /* no configs found at all */ |
231 | if (ncfg == 0) { | |
232 | *configs = NULL; | |
233 | *nconfigs = 0; | |
6abab2d4 | 234 | goto out; |
e4a8844c HS |
235 | } |
236 | ||
7db9af4b HS |
237 | /* |
238 | * Now limit the number of configs to the real number of | |
239 | * found properties. | |
240 | */ | |
db388dfb | 241 | *configs = kmemdup(cfg, ncfg * sizeof(unsigned long), GFP_KERNEL); |
6abab2d4 HS |
242 | if (!*configs) { |
243 | ret = -ENOMEM; | |
244 | goto out; | |
245 | } | |
7db9af4b | 246 | |
7db9af4b | 247 | *nconfigs = ncfg; |
6abab2d4 HS |
248 | |
249 | out: | |
250 | kfree(cfg); | |
251 | return ret; | |
7db9af4b | 252 | } |
e81c8f18 LD |
253 | |
254 | int pinconf_generic_dt_subnode_to_map(struct pinctrl_dev *pctldev, | |
255 | struct device_node *np, struct pinctrl_map **map, | |
3287c240 LD |
256 | unsigned *reserved_maps, unsigned *num_maps, |
257 | enum pinctrl_map_type type) | |
e81c8f18 LD |
258 | { |
259 | int ret; | |
260 | const char *function; | |
261 | struct device *dev = pctldev->dev; | |
262 | unsigned long *configs = NULL; | |
263 | unsigned num_configs = 0; | |
264 | unsigned reserve; | |
265 | struct property *prop; | |
266 | const char *group; | |
31c89c95 | 267 | const char *subnode_target_type = "pins"; |
e81c8f18 LD |
268 | |
269 | ret = of_property_read_string(np, "function", &function); | |
270 | if (ret < 0) { | |
271 | /* EINVAL=missing, which is fine since it's optional */ | |
272 | if (ret != -EINVAL) | |
acf564a8 | 273 | dev_err(dev, "could not parse property function\n"); |
e81c8f18 LD |
274 | function = NULL; |
275 | } | |
276 | ||
277 | ret = pinconf_generic_parse_dt_config(np, &configs, &num_configs); | |
278 | if (ret < 0) { | |
279 | dev_err(dev, "could not parse node property\n"); | |
280 | return ret; | |
281 | } | |
282 | ||
283 | reserve = 0; | |
284 | if (function != NULL) | |
285 | reserve++; | |
286 | if (num_configs) | |
287 | reserve++; | |
31c89c95 | 288 | |
e81c8f18 LD |
289 | ret = of_property_count_strings(np, "pins"); |
290 | if (ret < 0) { | |
31c89c95 SB |
291 | ret = of_property_count_strings(np, "groups"); |
292 | if (ret < 0) { | |
293 | dev_err(dev, "could not parse property pins/groups\n"); | |
294 | goto exit; | |
295 | } | |
296 | if (type == PIN_MAP_TYPE_INVALID) | |
297 | type = PIN_MAP_TYPE_CONFIGS_GROUP; | |
298 | subnode_target_type = "groups"; | |
299 | } else { | |
300 | if (type == PIN_MAP_TYPE_INVALID) | |
301 | type = PIN_MAP_TYPE_CONFIGS_PIN; | |
e81c8f18 LD |
302 | } |
303 | reserve *= ret; | |
304 | ||
305 | ret = pinctrl_utils_reserve_map(pctldev, map, reserved_maps, | |
306 | num_maps, reserve); | |
307 | if (ret < 0) | |
308 | goto exit; | |
309 | ||
31c89c95 | 310 | of_property_for_each_string(np, subnode_target_type, prop, group) { |
e81c8f18 LD |
311 | if (function) { |
312 | ret = pinctrl_utils_add_map_mux(pctldev, map, | |
313 | reserved_maps, num_maps, group, | |
314 | function); | |
315 | if (ret < 0) | |
316 | goto exit; | |
317 | } | |
318 | ||
319 | if (num_configs) { | |
320 | ret = pinctrl_utils_add_map_configs(pctldev, map, | |
321 | reserved_maps, num_maps, group, configs, | |
3287c240 | 322 | num_configs, type); |
e81c8f18 LD |
323 | if (ret < 0) |
324 | goto exit; | |
325 | } | |
326 | } | |
327 | ret = 0; | |
328 | ||
329 | exit: | |
330 | kfree(configs); | |
331 | return ret; | |
332 | } | |
333 | EXPORT_SYMBOL_GPL(pinconf_generic_dt_subnode_to_map); | |
334 | ||
335 | int pinconf_generic_dt_node_to_map(struct pinctrl_dev *pctldev, | |
336 | struct device_node *np_config, struct pinctrl_map **map, | |
3287c240 | 337 | unsigned *num_maps, enum pinctrl_map_type type) |
e81c8f18 LD |
338 | { |
339 | unsigned reserved_maps; | |
340 | struct device_node *np; | |
341 | int ret; | |
342 | ||
343 | reserved_maps = 0; | |
344 | *map = NULL; | |
345 | *num_maps = 0; | |
346 | ||
347 | for_each_child_of_node(np_config, np) { | |
348 | ret = pinconf_generic_dt_subnode_to_map(pctldev, np, map, | |
3287c240 | 349 | &reserved_maps, num_maps, type); |
e81c8f18 LD |
350 | if (ret < 0) { |
351 | pinctrl_utils_dt_free_map(pctldev, *map, *num_maps); | |
352 | return ret; | |
353 | } | |
354 | } | |
355 | return 0; | |
356 | } | |
357 | EXPORT_SYMBOL_GPL(pinconf_generic_dt_node_to_map); | |
358 | ||
7db9af4b | 359 | #endif |