]>
Commit | Line | Data |
---|---|---|
6e908892 MY |
1 | /* |
2 | * Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.com> | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation; either version 2 of the License, or | |
7 | * (at your option) any later version. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | */ | |
14 | ||
15 | #include <linux/export.h> | |
16 | #include <linux/mfd/syscon.h> | |
3e030b0b | 17 | #include <linux/of.h> |
6e908892 MY |
18 | #include <linux/pinctrl/pinconf.h> |
19 | #include <linux/pinctrl/pinconf-generic.h> | |
20 | #include <linux/pinctrl/pinctrl.h> | |
21 | #include <linux/pinctrl/pinmux.h> | |
22 | #include <linux/platform_device.h> | |
23 | #include <linux/regmap.h> | |
24 | ||
25 | #include "../core.h" | |
26 | #include "../pinctrl-utils.h" | |
27 | #include "pinctrl-uniphier.h" | |
28 | ||
29 | struct uniphier_pinctrl_priv { | |
fc78a566 | 30 | struct pinctrl_desc pctldesc; |
6e908892 MY |
31 | struct pinctrl_dev *pctldev; |
32 | struct regmap *regmap; | |
3e030b0b | 33 | unsigned int regbase; |
6e908892 MY |
34 | struct uniphier_pinctrl_socdata *socdata; |
35 | }; | |
36 | ||
37 | static int uniphier_pctl_get_groups_count(struct pinctrl_dev *pctldev) | |
38 | { | |
39 | struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev); | |
40 | ||
41 | return priv->socdata->groups_count; | |
42 | } | |
43 | ||
44 | static const char *uniphier_pctl_get_group_name(struct pinctrl_dev *pctldev, | |
45 | unsigned selector) | |
46 | { | |
47 | struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev); | |
48 | ||
49 | return priv->socdata->groups[selector].name; | |
50 | } | |
51 | ||
52 | static int uniphier_pctl_get_group_pins(struct pinctrl_dev *pctldev, | |
53 | unsigned selector, | |
54 | const unsigned **pins, | |
55 | unsigned *num_pins) | |
56 | { | |
57 | struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev); | |
58 | ||
59 | *pins = priv->socdata->groups[selector].pins; | |
60 | *num_pins = priv->socdata->groups[selector].num_pins; | |
61 | ||
62 | return 0; | |
63 | } | |
64 | ||
65 | #ifdef CONFIG_DEBUG_FS | |
66 | static void uniphier_pctl_pin_dbg_show(struct pinctrl_dev *pctldev, | |
67 | struct seq_file *s, unsigned offset) | |
68 | { | |
94bf176b | 69 | const struct pin_desc *desc = pin_desc_get(pctldev, offset); |
9eaa98a6 | 70 | const char *pull_dir, *drv_type; |
6e908892 | 71 | |
94bf176b | 72 | switch (uniphier_pin_get_pull_dir(desc->drv_data)) { |
6e908892 MY |
73 | case UNIPHIER_PIN_PULL_UP: |
74 | pull_dir = "UP"; | |
75 | break; | |
76 | case UNIPHIER_PIN_PULL_DOWN: | |
77 | pull_dir = "DOWN"; | |
78 | break; | |
10ef8277 MY |
79 | case UNIPHIER_PIN_PULL_UP_FIXED: |
80 | pull_dir = "UP(FIXED)"; | |
81 | break; | |
82 | case UNIPHIER_PIN_PULL_DOWN_FIXED: | |
83 | pull_dir = "DOWN(FIXED)"; | |
84 | break; | |
6e908892 MY |
85 | case UNIPHIER_PIN_PULL_NONE: |
86 | pull_dir = "NONE"; | |
87 | break; | |
88 | default: | |
89 | BUG(); | |
90 | } | |
91 | ||
94bf176b | 92 | switch (uniphier_pin_get_drv_type(desc->drv_data)) { |
9eaa98a6 MY |
93 | case UNIPHIER_PIN_DRV_1BIT: |
94 | drv_type = "4/8(mA)"; | |
6e908892 | 95 | break; |
9eaa98a6 MY |
96 | case UNIPHIER_PIN_DRV_2BIT: |
97 | drv_type = "8/12/16/20(mA)"; | |
6e908892 | 98 | break; |
72e5706a MY |
99 | case UNIPHIER_PIN_DRV_3BIT: |
100 | drv_type = "4/5/7/9/11/12/14/16(mA)"; | |
101 | break; | |
9eaa98a6 MY |
102 | case UNIPHIER_PIN_DRV_FIXED4: |
103 | drv_type = "4(mA)"; | |
6e908892 | 104 | break; |
9eaa98a6 MY |
105 | case UNIPHIER_PIN_DRV_FIXED5: |
106 | drv_type = "5(mA)"; | |
6e908892 | 107 | break; |
9eaa98a6 MY |
108 | case UNIPHIER_PIN_DRV_FIXED8: |
109 | drv_type = "8(mA)"; | |
6e908892 MY |
110 | break; |
111 | case UNIPHIER_PIN_DRV_NONE: | |
9eaa98a6 | 112 | drv_type = "NONE"; |
6e908892 MY |
113 | break; |
114 | default: | |
115 | BUG(); | |
116 | } | |
117 | ||
9eaa98a6 | 118 | seq_printf(s, " PULL_DIR=%s DRV_TYPE=%s", pull_dir, drv_type); |
6e908892 MY |
119 | } |
120 | #endif | |
121 | ||
122 | static const struct pinctrl_ops uniphier_pctlops = { | |
123 | .get_groups_count = uniphier_pctl_get_groups_count, | |
124 | .get_group_name = uniphier_pctl_get_group_name, | |
125 | .get_group_pins = uniphier_pctl_get_group_pins, | |
126 | #ifdef CONFIG_DEBUG_FS | |
127 | .pin_dbg_show = uniphier_pctl_pin_dbg_show, | |
128 | #endif | |
129 | .dt_node_to_map = pinconf_generic_dt_node_to_map_all, | |
d32f7fd3 | 130 | .dt_free_map = pinctrl_utils_free_map, |
6e908892 MY |
131 | }; |
132 | ||
133 | static int uniphier_conf_pin_bias_get(struct pinctrl_dev *pctldev, | |
94bf176b | 134 | const struct pin_desc *desc, |
6e908892 MY |
135 | enum pin_config_param param) |
136 | { | |
137 | struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev); | |
138 | enum uniphier_pin_pull_dir pull_dir = | |
94bf176b | 139 | uniphier_pin_get_pull_dir(desc->drv_data); |
6e908892 MY |
140 | unsigned int pupdctrl, reg, shift, val; |
141 | unsigned int expected = 1; | |
142 | int ret; | |
143 | ||
144 | switch (param) { | |
145 | case PIN_CONFIG_BIAS_DISABLE: | |
146 | if (pull_dir == UNIPHIER_PIN_PULL_NONE) | |
147 | return 0; | |
148 | if (pull_dir == UNIPHIER_PIN_PULL_UP_FIXED || | |
149 | pull_dir == UNIPHIER_PIN_PULL_DOWN_FIXED) | |
150 | return -EINVAL; | |
151 | expected = 0; | |
152 | break; | |
153 | case PIN_CONFIG_BIAS_PULL_UP: | |
154 | if (pull_dir == UNIPHIER_PIN_PULL_UP_FIXED) | |
155 | return 0; | |
156 | if (pull_dir != UNIPHIER_PIN_PULL_UP) | |
157 | return -EINVAL; | |
158 | break; | |
159 | case PIN_CONFIG_BIAS_PULL_DOWN: | |
160 | if (pull_dir == UNIPHIER_PIN_PULL_DOWN_FIXED) | |
161 | return 0; | |
162 | if (pull_dir != UNIPHIER_PIN_PULL_DOWN) | |
163 | return -EINVAL; | |
164 | break; | |
165 | default: | |
166 | BUG(); | |
167 | } | |
168 | ||
94bf176b | 169 | pupdctrl = uniphier_pin_get_pupdctrl(desc->drv_data); |
6e908892 MY |
170 | |
171 | reg = UNIPHIER_PINCTRL_PUPDCTRL_BASE + pupdctrl / 32 * 4; | |
172 | shift = pupdctrl % 32; | |
173 | ||
3e030b0b | 174 | ret = regmap_read(priv->regmap, priv->regbase + reg, &val); |
6e908892 MY |
175 | if (ret) |
176 | return ret; | |
177 | ||
178 | val = (val >> shift) & 1; | |
179 | ||
180 | return (val == expected) ? 0 : -EINVAL; | |
181 | } | |
182 | ||
183 | static int uniphier_conf_pin_drive_get(struct pinctrl_dev *pctldev, | |
94bf176b | 184 | const struct pin_desc *desc, |
6e908892 MY |
185 | u16 *strength) |
186 | { | |
187 | struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev); | |
9eaa98a6 | 188 | enum uniphier_pin_drv_type type = |
94bf176b | 189 | uniphier_pin_get_drv_type(desc->drv_data); |
9eaa98a6 MY |
190 | const unsigned int strength_1bit[] = {4, 8}; |
191 | const unsigned int strength_2bit[] = {8, 12, 16, 20}; | |
72e5706a | 192 | const unsigned int strength_3bit[] = {4, 5, 7, 9, 11, 12, 14, 16}; |
6e908892 MY |
193 | const unsigned int *supported_strength; |
194 | unsigned int drvctrl, reg, shift, mask, width, val; | |
195 | int ret; | |
196 | ||
9eaa98a6 MY |
197 | switch (type) { |
198 | case UNIPHIER_PIN_DRV_1BIT: | |
199 | supported_strength = strength_1bit; | |
72e5706a | 200 | reg = UNIPHIER_PINCTRL_DRVCTRL_BASE; |
6e908892 MY |
201 | width = 1; |
202 | break; | |
9eaa98a6 MY |
203 | case UNIPHIER_PIN_DRV_2BIT: |
204 | supported_strength = strength_2bit; | |
72e5706a | 205 | reg = UNIPHIER_PINCTRL_DRV2CTRL_BASE; |
6e908892 MY |
206 | width = 2; |
207 | break; | |
72e5706a MY |
208 | case UNIPHIER_PIN_DRV_3BIT: |
209 | supported_strength = strength_3bit; | |
210 | reg = UNIPHIER_PINCTRL_DRV3CTRL_BASE; | |
211 | width = 4; | |
212 | break; | |
9eaa98a6 | 213 | case UNIPHIER_PIN_DRV_FIXED4: |
6e908892 MY |
214 | *strength = 4; |
215 | return 0; | |
9eaa98a6 | 216 | case UNIPHIER_PIN_DRV_FIXED5: |
6e908892 MY |
217 | *strength = 5; |
218 | return 0; | |
9eaa98a6 | 219 | case UNIPHIER_PIN_DRV_FIXED8: |
6e908892 MY |
220 | *strength = 8; |
221 | return 0; | |
222 | default: | |
223 | /* drive strength control is not supported for this pin */ | |
224 | return -EINVAL; | |
225 | } | |
226 | ||
94bf176b | 227 | drvctrl = uniphier_pin_get_drvctrl(desc->drv_data); |
6e908892 MY |
228 | drvctrl *= width; |
229 | ||
6e908892 MY |
230 | reg += drvctrl / 32 * 4; |
231 | shift = drvctrl % 32; | |
232 | mask = (1U << width) - 1; | |
233 | ||
3e030b0b | 234 | ret = regmap_read(priv->regmap, priv->regbase + reg, &val); |
6e908892 MY |
235 | if (ret) |
236 | return ret; | |
237 | ||
238 | *strength = supported_strength[(val >> shift) & mask]; | |
239 | ||
240 | return 0; | |
241 | } | |
242 | ||
243 | static int uniphier_conf_pin_input_enable_get(struct pinctrl_dev *pctldev, | |
94bf176b | 244 | const struct pin_desc *desc) |
6e908892 MY |
245 | { |
246 | struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev); | |
94bf176b | 247 | unsigned int iectrl = uniphier_pin_get_iectrl(desc->drv_data); |
6e908892 MY |
248 | unsigned int val; |
249 | int ret; | |
250 | ||
251 | if (iectrl == UNIPHIER_PIN_IECTRL_NONE) | |
252 | /* This pin is always input-enabled. */ | |
253 | return 0; | |
254 | ||
3e030b0b MY |
255 | ret = regmap_read(priv->regmap, |
256 | priv->regbase + UNIPHIER_PINCTRL_IECTRL, &val); | |
6e908892 MY |
257 | if (ret) |
258 | return ret; | |
259 | ||
260 | return val & BIT(iectrl) ? 0 : -EINVAL; | |
261 | } | |
262 | ||
263 | static int uniphier_conf_pin_config_get(struct pinctrl_dev *pctldev, | |
264 | unsigned pin, | |
265 | unsigned long *configs) | |
266 | { | |
94bf176b | 267 | const struct pin_desc *desc = pin_desc_get(pctldev, pin); |
6e908892 MY |
268 | enum pin_config_param param = pinconf_to_config_param(*configs); |
269 | bool has_arg = false; | |
270 | u16 arg; | |
271 | int ret; | |
272 | ||
273 | switch (param) { | |
274 | case PIN_CONFIG_BIAS_DISABLE: | |
275 | case PIN_CONFIG_BIAS_PULL_UP: | |
276 | case PIN_CONFIG_BIAS_PULL_DOWN: | |
94bf176b | 277 | ret = uniphier_conf_pin_bias_get(pctldev, desc, param); |
6e908892 MY |
278 | break; |
279 | case PIN_CONFIG_DRIVE_STRENGTH: | |
94bf176b | 280 | ret = uniphier_conf_pin_drive_get(pctldev, desc, &arg); |
6e908892 MY |
281 | has_arg = true; |
282 | break; | |
283 | case PIN_CONFIG_INPUT_ENABLE: | |
94bf176b | 284 | ret = uniphier_conf_pin_input_enable_get(pctldev, desc); |
6e908892 MY |
285 | break; |
286 | default: | |
287 | /* unsupported parameter */ | |
288 | ret = -EINVAL; | |
289 | break; | |
290 | } | |
291 | ||
292 | if (ret == 0 && has_arg) | |
293 | *configs = pinconf_to_config_packed(param, arg); | |
294 | ||
295 | return ret; | |
296 | } | |
297 | ||
298 | static int uniphier_conf_pin_bias_set(struct pinctrl_dev *pctldev, | |
94bf176b MY |
299 | const struct pin_desc *desc, |
300 | enum pin_config_param param, u16 arg) | |
6e908892 MY |
301 | { |
302 | struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev); | |
303 | enum uniphier_pin_pull_dir pull_dir = | |
94bf176b | 304 | uniphier_pin_get_pull_dir(desc->drv_data); |
6e908892 MY |
305 | unsigned int pupdctrl, reg, shift; |
306 | unsigned int val = 1; | |
307 | ||
308 | switch (param) { | |
309 | case PIN_CONFIG_BIAS_DISABLE: | |
310 | if (pull_dir == UNIPHIER_PIN_PULL_NONE) | |
311 | return 0; | |
312 | if (pull_dir == UNIPHIER_PIN_PULL_UP_FIXED || | |
313 | pull_dir == UNIPHIER_PIN_PULL_DOWN_FIXED) { | |
314 | dev_err(pctldev->dev, | |
94bf176b MY |
315 | "can not disable pull register for pin %s\n", |
316 | desc->name); | |
6e908892 MY |
317 | return -EINVAL; |
318 | } | |
319 | val = 0; | |
320 | break; | |
321 | case PIN_CONFIG_BIAS_PULL_UP: | |
322 | if (pull_dir == UNIPHIER_PIN_PULL_UP_FIXED && arg != 0) | |
323 | return 0; | |
324 | if (pull_dir != UNIPHIER_PIN_PULL_UP) { | |
325 | dev_err(pctldev->dev, | |
94bf176b MY |
326 | "pull-up is unsupported for pin %s\n", |
327 | desc->name); | |
6e908892 MY |
328 | return -EINVAL; |
329 | } | |
330 | if (arg == 0) { | |
331 | dev_err(pctldev->dev, "pull-up can not be total\n"); | |
332 | return -EINVAL; | |
333 | } | |
334 | break; | |
335 | case PIN_CONFIG_BIAS_PULL_DOWN: | |
336 | if (pull_dir == UNIPHIER_PIN_PULL_DOWN_FIXED && arg != 0) | |
337 | return 0; | |
338 | if (pull_dir != UNIPHIER_PIN_PULL_DOWN) { | |
339 | dev_err(pctldev->dev, | |
94bf176b MY |
340 | "pull-down is unsupported for pin %s\n", |
341 | desc->name); | |
6e908892 MY |
342 | return -EINVAL; |
343 | } | |
344 | if (arg == 0) { | |
345 | dev_err(pctldev->dev, "pull-down can not be total\n"); | |
346 | return -EINVAL; | |
347 | } | |
348 | break; | |
349 | case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: | |
350 | if (pull_dir == UNIPHIER_PIN_PULL_NONE) { | |
351 | dev_err(pctldev->dev, | |
94bf176b MY |
352 | "pull-up/down is unsupported for pin %s\n", |
353 | desc->name); | |
6e908892 MY |
354 | return -EINVAL; |
355 | } | |
356 | ||
357 | if (arg == 0) | |
358 | return 0; /* configuration ingored */ | |
359 | break; | |
360 | default: | |
361 | BUG(); | |
362 | } | |
363 | ||
94bf176b | 364 | pupdctrl = uniphier_pin_get_pupdctrl(desc->drv_data); |
6e908892 MY |
365 | |
366 | reg = UNIPHIER_PINCTRL_PUPDCTRL_BASE + pupdctrl / 32 * 4; | |
367 | shift = pupdctrl % 32; | |
368 | ||
3e030b0b MY |
369 | return regmap_update_bits(priv->regmap, priv->regbase + reg, |
370 | 1 << shift, val << shift); | |
6e908892 MY |
371 | } |
372 | ||
373 | static int uniphier_conf_pin_drive_set(struct pinctrl_dev *pctldev, | |
94bf176b | 374 | const struct pin_desc *desc, |
6e908892 MY |
375 | u16 strength) |
376 | { | |
377 | struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev); | |
9eaa98a6 | 378 | enum uniphier_pin_drv_type type = |
94bf176b | 379 | uniphier_pin_get_drv_type(desc->drv_data); |
9eaa98a6 MY |
380 | const unsigned int strength_1bit[] = {4, 8, -1}; |
381 | const unsigned int strength_2bit[] = {8, 12, 16, 20, -1}; | |
72e5706a | 382 | const unsigned int strength_3bit[] = {4, 5, 7, 9, 11, 12, 14, 16, -1}; |
6e908892 MY |
383 | const unsigned int *supported_strength; |
384 | unsigned int drvctrl, reg, shift, mask, width, val; | |
385 | ||
9eaa98a6 MY |
386 | switch (type) { |
387 | case UNIPHIER_PIN_DRV_1BIT: | |
388 | supported_strength = strength_1bit; | |
72e5706a | 389 | reg = UNIPHIER_PINCTRL_DRVCTRL_BASE; |
6e908892 MY |
390 | width = 1; |
391 | break; | |
9eaa98a6 MY |
392 | case UNIPHIER_PIN_DRV_2BIT: |
393 | supported_strength = strength_2bit; | |
72e5706a | 394 | reg = UNIPHIER_PINCTRL_DRV2CTRL_BASE; |
6e908892 MY |
395 | width = 2; |
396 | break; | |
72e5706a MY |
397 | case UNIPHIER_PIN_DRV_3BIT: |
398 | supported_strength = strength_3bit; | |
399 | reg = UNIPHIER_PINCTRL_DRV3CTRL_BASE; | |
400 | width = 4; | |
401 | break; | |
6e908892 MY |
402 | default: |
403 | dev_err(pctldev->dev, | |
94bf176b MY |
404 | "cannot change drive strength for pin %s\n", |
405 | desc->name); | |
6e908892 MY |
406 | return -EINVAL; |
407 | } | |
408 | ||
409 | for (val = 0; supported_strength[val] > 0; val++) { | |
410 | if (supported_strength[val] > strength) | |
411 | break; | |
412 | } | |
413 | ||
414 | if (val == 0) { | |
415 | dev_err(pctldev->dev, | |
94bf176b MY |
416 | "unsupported drive strength %u mA for pin %s\n", |
417 | strength, desc->name); | |
6e908892 MY |
418 | return -EINVAL; |
419 | } | |
420 | ||
421 | val--; | |
422 | ||
94bf176b | 423 | drvctrl = uniphier_pin_get_drvctrl(desc->drv_data); |
6e908892 MY |
424 | drvctrl *= width; |
425 | ||
6e908892 MY |
426 | reg += drvctrl / 32 * 4; |
427 | shift = drvctrl % 32; | |
428 | mask = (1U << width) - 1; | |
429 | ||
3e030b0b | 430 | return regmap_update_bits(priv->regmap, priv->regbase + reg, |
6e908892 MY |
431 | mask << shift, val << shift); |
432 | } | |
433 | ||
434 | static int uniphier_conf_pin_input_enable(struct pinctrl_dev *pctldev, | |
94bf176b | 435 | const struct pin_desc *desc, |
6e908892 MY |
436 | u16 enable) |
437 | { | |
438 | struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev); | |
94bf176b | 439 | unsigned int iectrl = uniphier_pin_get_iectrl(desc->drv_data); |
aa543888 MY |
440 | unsigned int reg, mask; |
441 | ||
442 | /* | |
443 | * Multiple pins share one input enable, per-pin disabling is | |
444 | * impossible. | |
445 | */ | |
446 | if (!(priv->socdata->caps & UNIPHIER_PINCTRL_CAPS_PERPIN_IECTRL) && | |
447 | !enable) | |
6e908892 | 448 | return -EINVAL; |
6e908892 | 449 | |
aa543888 | 450 | /* UNIPHIER_PIN_IECTRL_NONE means the pin is always input-enabled */ |
6e908892 | 451 | if (iectrl == UNIPHIER_PIN_IECTRL_NONE) |
aa543888 MY |
452 | return enable ? 0 : -EINVAL; |
453 | ||
3e030b0b | 454 | reg = priv->regbase + UNIPHIER_PINCTRL_IECTRL + iectrl / 32 * 4; |
aa543888 | 455 | mask = BIT(iectrl % 32); |
6e908892 | 456 | |
aa543888 | 457 | return regmap_update_bits(priv->regmap, reg, mask, enable ? mask : 0); |
6e908892 MY |
458 | } |
459 | ||
460 | static int uniphier_conf_pin_config_set(struct pinctrl_dev *pctldev, | |
461 | unsigned pin, | |
462 | unsigned long *configs, | |
463 | unsigned num_configs) | |
464 | { | |
94bf176b | 465 | const struct pin_desc *desc = pin_desc_get(pctldev, pin); |
6e908892 MY |
466 | int i, ret; |
467 | ||
468 | for (i = 0; i < num_configs; i++) { | |
469 | enum pin_config_param param = | |
470 | pinconf_to_config_param(configs[i]); | |
471 | u16 arg = pinconf_to_config_argument(configs[i]); | |
472 | ||
473 | switch (param) { | |
474 | case PIN_CONFIG_BIAS_DISABLE: | |
475 | case PIN_CONFIG_BIAS_PULL_UP: | |
476 | case PIN_CONFIG_BIAS_PULL_DOWN: | |
477 | case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: | |
94bf176b | 478 | ret = uniphier_conf_pin_bias_set(pctldev, desc, |
6e908892 MY |
479 | param, arg); |
480 | break; | |
481 | case PIN_CONFIG_DRIVE_STRENGTH: | |
94bf176b | 482 | ret = uniphier_conf_pin_drive_set(pctldev, desc, arg); |
6e908892 MY |
483 | break; |
484 | case PIN_CONFIG_INPUT_ENABLE: | |
94bf176b MY |
485 | ret = uniphier_conf_pin_input_enable(pctldev, desc, |
486 | arg); | |
6e908892 MY |
487 | break; |
488 | default: | |
489 | dev_err(pctldev->dev, | |
490 | "unsupported configuration parameter %u\n", | |
491 | param); | |
492 | return -EINVAL; | |
493 | } | |
494 | ||
495 | if (ret) | |
496 | return ret; | |
497 | } | |
498 | ||
499 | return 0; | |
500 | } | |
501 | ||
502 | static int uniphier_conf_pin_config_group_set(struct pinctrl_dev *pctldev, | |
503 | unsigned selector, | |
504 | unsigned long *configs, | |
505 | unsigned num_configs) | |
506 | { | |
507 | struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev); | |
508 | const unsigned *pins = priv->socdata->groups[selector].pins; | |
509 | unsigned num_pins = priv->socdata->groups[selector].num_pins; | |
510 | int i, ret; | |
511 | ||
512 | for (i = 0; i < num_pins; i++) { | |
513 | ret = uniphier_conf_pin_config_set(pctldev, pins[i], | |
514 | configs, num_configs); | |
515 | if (ret) | |
516 | return ret; | |
517 | } | |
518 | ||
519 | return 0; | |
520 | } | |
521 | ||
522 | static const struct pinconf_ops uniphier_confops = { | |
523 | .is_generic = true, | |
524 | .pin_config_get = uniphier_conf_pin_config_get, | |
525 | .pin_config_set = uniphier_conf_pin_config_set, | |
526 | .pin_config_group_set = uniphier_conf_pin_config_group_set, | |
527 | }; | |
528 | ||
529 | static int uniphier_pmx_get_functions_count(struct pinctrl_dev *pctldev) | |
530 | { | |
531 | struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev); | |
532 | ||
533 | return priv->socdata->functions_count; | |
534 | } | |
535 | ||
536 | static const char *uniphier_pmx_get_function_name(struct pinctrl_dev *pctldev, | |
537 | unsigned selector) | |
538 | { | |
539 | struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev); | |
540 | ||
541 | return priv->socdata->functions[selector].name; | |
542 | } | |
543 | ||
544 | static int uniphier_pmx_get_function_groups(struct pinctrl_dev *pctldev, | |
545 | unsigned selector, | |
546 | const char * const **groups, | |
547 | unsigned *num_groups) | |
548 | { | |
549 | struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev); | |
550 | ||
551 | *groups = priv->socdata->functions[selector].groups; | |
552 | *num_groups = priv->socdata->functions[selector].num_groups; | |
553 | ||
554 | return 0; | |
555 | } | |
556 | ||
557 | static int uniphier_pmx_set_one_mux(struct pinctrl_dev *pctldev, unsigned pin, | |
39ec9ace | 558 | int muxval) |
6e908892 MY |
559 | { |
560 | struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev); | |
c2ebf475 MY |
561 | unsigned int mux_bits, reg_stride, reg, reg_end, shift, mask; |
562 | bool load_pinctrl; | |
6e908892 MY |
563 | int ret; |
564 | ||
bac7f4c1 MY |
565 | /* some pins need input-enabling */ |
566 | ret = uniphier_conf_pin_input_enable(pctldev, | |
94bf176b | 567 | pin_desc_get(pctldev, pin), 1); |
bac7f4c1 MY |
568 | if (ret) |
569 | return ret; | |
570 | ||
39ec9ace MY |
571 | if (muxval < 0) |
572 | return 0; /* dedicated pin; nothing to do for pin-mux */ | |
573 | ||
c2ebf475 MY |
574 | if (priv->socdata->caps & UNIPHIER_PINCTRL_CAPS_DBGMUX_SEPARATE) { |
575 | /* | |
576 | * Mode reg_offset bit_position | |
577 | * Normal 4 * n shift+3:shift | |
578 | * Debug 4 * n shift+7:shift+4 | |
579 | */ | |
580 | mux_bits = 4; | |
581 | reg_stride = 8; | |
582 | load_pinctrl = true; | |
583 | } else { | |
584 | /* | |
585 | * Mode reg_offset bit_position | |
586 | * Normal 8 * n shift+3:shift | |
587 | * Debug 8 * n + 4 shift+3:shift | |
588 | */ | |
589 | mux_bits = 8; | |
590 | reg_stride = 4; | |
591 | load_pinctrl = false; | |
592 | } | |
593 | ||
6e908892 MY |
594 | reg = UNIPHIER_PINCTRL_PINMUX_BASE + pin * mux_bits / 32 * reg_stride; |
595 | reg_end = reg + reg_stride; | |
596 | shift = pin * mux_bits % 32; | |
597 | mask = (1U << mux_bits) - 1; | |
598 | ||
599 | /* | |
600 | * If reg_stride is greater than 4, the MSB of each pinsel shall be | |
601 | * stored in the offset+4. | |
602 | */ | |
603 | for (; reg < reg_end; reg += 4) { | |
3e030b0b | 604 | ret = regmap_update_bits(priv->regmap, priv->regbase + reg, |
6e908892 MY |
605 | mask << shift, muxval << shift); |
606 | if (ret) | |
607 | return ret; | |
608 | muxval >>= mux_bits; | |
609 | } | |
610 | ||
c2ebf475 | 611 | if (load_pinctrl) { |
6e908892 | 612 | ret = regmap_write(priv->regmap, |
3e030b0b MY |
613 | priv->regbase + UNIPHIER_PINCTRL_LOAD_PINMUX, |
614 | 1); | |
6e908892 MY |
615 | if (ret) |
616 | return ret; | |
617 | } | |
618 | ||
bac7f4c1 | 619 | return 0; |
6e908892 MY |
620 | } |
621 | ||
622 | static int uniphier_pmx_set_mux(struct pinctrl_dev *pctldev, | |
623 | unsigned func_selector, | |
624 | unsigned group_selector) | |
625 | { | |
626 | struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev); | |
627 | const struct uniphier_pinctrl_group *grp = | |
628 | &priv->socdata->groups[group_selector]; | |
629 | int i; | |
630 | int ret; | |
631 | ||
632 | for (i = 0; i < grp->num_pins; i++) { | |
633 | ret = uniphier_pmx_set_one_mux(pctldev, grp->pins[i], | |
634 | grp->muxvals[i]); | |
635 | if (ret) | |
636 | return ret; | |
637 | } | |
638 | ||
639 | return 0; | |
640 | } | |
641 | ||
642 | static int uniphier_pmx_gpio_request_enable(struct pinctrl_dev *pctldev, | |
643 | struct pinctrl_gpio_range *range, | |
644 | unsigned offset) | |
645 | { | |
646 | struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev); | |
647 | const struct uniphier_pinctrl_group *groups = priv->socdata->groups; | |
648 | int groups_count = priv->socdata->groups_count; | |
649 | enum uniphier_pinmux_gpio_range_type range_type; | |
650 | int i, j; | |
651 | ||
652 | if (strstr(range->name, "irq")) | |
653 | range_type = UNIPHIER_PINMUX_GPIO_RANGE_IRQ; | |
654 | else | |
655 | range_type = UNIPHIER_PINMUX_GPIO_RANGE_PORT; | |
656 | ||
657 | for (i = 0; i < groups_count; i++) { | |
658 | if (groups[i].range_type != range_type) | |
659 | continue; | |
660 | ||
661 | for (j = 0; j < groups[i].num_pins; j++) | |
662 | if (groups[i].pins[j] == offset) | |
663 | goto found; | |
664 | } | |
665 | ||
666 | dev_err(pctldev->dev, "pin %u does not support GPIO\n", offset); | |
667 | return -EINVAL; | |
668 | ||
669 | found: | |
670 | return uniphier_pmx_set_one_mux(pctldev, offset, groups[i].muxvals[j]); | |
671 | } | |
672 | ||
673 | static const struct pinmux_ops uniphier_pmxops = { | |
674 | .get_functions_count = uniphier_pmx_get_functions_count, | |
675 | .get_function_name = uniphier_pmx_get_function_name, | |
676 | .get_function_groups = uniphier_pmx_get_function_groups, | |
677 | .set_mux = uniphier_pmx_set_mux, | |
678 | .gpio_request_enable = uniphier_pmx_gpio_request_enable, | |
679 | .strict = true, | |
680 | }; | |
681 | ||
682 | int uniphier_pinctrl_probe(struct platform_device *pdev, | |
6e908892 MY |
683 | struct uniphier_pinctrl_socdata *socdata) |
684 | { | |
685 | struct device *dev = &pdev->dev; | |
686 | struct uniphier_pinctrl_priv *priv; | |
3e030b0b | 687 | struct device_node *parent; |
6e908892 MY |
688 | |
689 | if (!socdata || | |
fc78a566 | 690 | !socdata->pins || !socdata->npins || |
c2ebf475 MY |
691 | !socdata->groups || !socdata->groups_count || |
692 | !socdata->functions || !socdata->functions_count) { | |
6e908892 MY |
693 | dev_err(dev, "pinctrl socdata lacks necessary members\n"); |
694 | return -EINVAL; | |
695 | } | |
696 | ||
697 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | |
698 | if (!priv) | |
699 | return -ENOMEM; | |
700 | ||
3e030b0b MY |
701 | if (of_device_is_compatible(dev->of_node, "socionext,ph1-ld4-pinctrl") || |
702 | of_device_is_compatible(dev->of_node, "socionext,ph1-pro4-pinctrl") || | |
703 | of_device_is_compatible(dev->of_node, "socionext,ph1-sld8-pinctrl") || | |
704 | of_device_is_compatible(dev->of_node, "socionext,ph1-pro5-pinctrl") || | |
705 | of_device_is_compatible(dev->of_node, "socionext,proxstream2-pinctrl") || | |
706 | of_device_is_compatible(dev->of_node, "socionext,ph1-ld6b-pinctrl")) { | |
707 | /* old binding */ | |
708 | priv->regmap = syscon_node_to_regmap(dev->of_node); | |
709 | } else { | |
710 | priv->regbase = 0x1000; | |
711 | parent = of_get_parent(dev->of_node); | |
712 | priv->regmap = syscon_node_to_regmap(parent); | |
713 | of_node_put(parent); | |
714 | } | |
715 | ||
6e908892 MY |
716 | if (IS_ERR(priv->regmap)) { |
717 | dev_err(dev, "failed to get regmap\n"); | |
718 | return PTR_ERR(priv->regmap); | |
719 | } | |
720 | ||
721 | priv->socdata = socdata; | |
fc78a566 MY |
722 | priv->pctldesc.name = dev->driver->name; |
723 | priv->pctldesc.pins = socdata->pins; | |
724 | priv->pctldesc.npins = socdata->npins; | |
725 | priv->pctldesc.pctlops = &uniphier_pctlops; | |
726 | priv->pctldesc.pmxops = &uniphier_pmxops; | |
727 | priv->pctldesc.confops = &uniphier_confops; | |
728 | priv->pctldesc.owner = dev->driver->owner; | |
729 | ||
730 | priv->pctldev = devm_pinctrl_register(dev, &priv->pctldesc, priv); | |
6e908892 MY |
731 | if (IS_ERR(priv->pctldev)) { |
732 | dev_err(dev, "failed to register UniPhier pinctrl driver\n"); | |
733 | return PTR_ERR(priv->pctldev); | |
734 | } | |
735 | ||
736 | platform_set_drvdata(pdev, priv); | |
737 | ||
738 | return 0; | |
739 | } | |
740 | EXPORT_SYMBOL_GPL(uniphier_pinctrl_probe); |