diff options
author | Quentin Schulz <quentin.schulz@free-electrons.com> | 2017-12-05 09:46:41 -0500 |
---|---|---|
committer | Linus Walleij <linus.walleij@linaro.org> | 2017-12-07 04:05:58 -0500 |
commit | 23f75d7dfa92132e08e31640e0f641dcdea781b9 (patch) | |
tree | 174e4f50e93914cc5500990eb1e5054f3f830908 /drivers/pinctrl/pinctrl-axp209.c | |
parent | 449317a8b4c4ed47c2a51f864c4697ae57195b96 (diff) |
pinctrl: axp209: add pinctrl features
The X-Powers AXP209 has 3 GPIOs. GPIO0/1 can each act either as a GPIO,
an ADC or a LDO regulator. GPIO2 can only act as a GPIO.
This adds the pinctrl features to the driver so GPIO0/1 can be used as
ADC or LDO regulator.
Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Diffstat (limited to 'drivers/pinctrl/pinctrl-axp209.c')
-rw-r--r-- | drivers/pinctrl/pinctrl-axp209.c | 304 |
1 files changed, 284 insertions, 20 deletions
diff --git a/drivers/pinctrl/pinctrl-axp209.c b/drivers/pinctrl/pinctrl-axp209.c index 6ee7dc1418fa..48cb57dcc575 100644 --- a/drivers/pinctrl/pinctrl-axp209.c +++ b/drivers/pinctrl/pinctrl-axp209.c | |||
@@ -1,7 +1,8 @@ | |||
1 | /* | 1 | /* |
2 | * AXP20x GPIO driver | 2 | * AXP20x pinctrl and GPIO driver |
3 | * | 3 | * |
4 | * Copyright (C) 2016 Maxime Ripard <maxime.ripard@free-electrons.com> | 4 | * Copyright (C) 2016 Maxime Ripard <maxime.ripard@free-electrons.com> |
5 | * Copyright (C) 2017 Quentin Schulz <quentin.schulz@free-electrons.com> | ||
5 | * | 6 | * |
6 | * This program is free software; you can redistribute it and/or modify it | 7 | * This program is free software; you can redistribute it and/or modify it |
7 | * under the terms of the GNU General Public License as published by the | 8 | * under the terms of the GNU General Public License as published by the |
@@ -18,6 +19,9 @@ | |||
18 | #include <linux/mfd/axp20x.h> | 19 | #include <linux/mfd/axp20x.h> |
19 | #include <linux/module.h> | 20 | #include <linux/module.h> |
20 | #include <linux/of.h> | 21 | #include <linux/of.h> |
22 | #include <linux/pinctrl/pinconf-generic.h> | ||
23 | #include <linux/pinctrl/pinctrl.h> | ||
24 | #include <linux/pinctrl/pinmux.h> | ||
21 | #include <linux/platform_device.h> | 25 | #include <linux/platform_device.h> |
22 | #include <linux/regmap.h> | 26 | #include <linux/regmap.h> |
23 | #include <linux/slab.h> | 27 | #include <linux/slab.h> |
@@ -27,9 +31,52 @@ | |||
27 | #define AXP20X_GPIO_FUNCTION_OUT_HIGH 1 | 31 | #define AXP20X_GPIO_FUNCTION_OUT_HIGH 1 |
28 | #define AXP20X_GPIO_FUNCTION_INPUT 2 | 32 | #define AXP20X_GPIO_FUNCTION_INPUT 2 |
29 | 33 | ||
34 | #define AXP20X_FUNC_GPIO_OUT 0 | ||
35 | #define AXP20X_FUNC_GPIO_IN 1 | ||
36 | #define AXP20X_FUNC_LDO 2 | ||
37 | #define AXP20X_FUNC_ADC 3 | ||
38 | #define AXP20X_FUNCS_NB 4 | ||
39 | |||
40 | #define AXP20X_MUX_GPIO_OUT 0 | ||
41 | #define AXP20X_MUX_GPIO_IN BIT(1) | ||
42 | #define AXP20X_MUX_ADC BIT(2) | ||
43 | |||
44 | struct axp20x_pctrl_desc { | ||
45 | const struct pinctrl_pin_desc *pins; | ||
46 | unsigned int npins; | ||
47 | /* Stores the pins supporting LDO function. Bit offset is pin number. */ | ||
48 | u8 ldo_mask; | ||
49 | /* Stores the pins supporting ADC function. Bit offset is pin number. */ | ||
50 | u8 adc_mask; | ||
51 | }; | ||
52 | |||
53 | struct axp20x_pinctrl_function { | ||
54 | const char *name; | ||
55 | unsigned int muxval; | ||
56 | const char **groups; | ||
57 | unsigned int ngroups; | ||
58 | }; | ||
59 | |||
30 | struct axp20x_gpio { | 60 | struct axp20x_gpio { |
31 | struct gpio_chip chip; | 61 | struct gpio_chip chip; |
32 | struct regmap *regmap; | 62 | struct regmap *regmap; |
63 | struct pinctrl_dev *pctl_dev; | ||
64 | struct device *dev; | ||
65 | const struct axp20x_pctrl_desc *desc; | ||
66 | struct axp20x_pinctrl_function funcs[AXP20X_FUNCS_NB]; | ||
67 | }; | ||
68 | |||
69 | static const struct pinctrl_pin_desc axp209_pins[] = { | ||
70 | PINCTRL_PIN(0, "GPIO0"), | ||
71 | PINCTRL_PIN(1, "GPIO1"), | ||
72 | PINCTRL_PIN(2, "GPIO2"), | ||
73 | }; | ||
74 | |||
75 | static const struct axp20x_pctrl_desc axp20x_data = { | ||
76 | .pins = axp209_pins, | ||
77 | .npins = ARRAY_SIZE(axp209_pins), | ||
78 | .ldo_mask = BIT(0) | BIT(1), | ||
79 | .adc_mask = BIT(0) | BIT(1), | ||
33 | }; | 80 | }; |
34 | 81 | ||
35 | static int axp20x_gpio_get_reg(unsigned int offset) | 82 | static int axp20x_gpio_get_reg(unsigned int offset) |
@@ -48,16 +95,7 @@ static int axp20x_gpio_get_reg(unsigned int offset) | |||
48 | 95 | ||
49 | static int axp20x_gpio_input(struct gpio_chip *chip, unsigned int offset) | 96 | static int axp20x_gpio_input(struct gpio_chip *chip, unsigned int offset) |
50 | { | 97 | { |
51 | struct axp20x_gpio *gpio = gpiochip_get_data(chip); | 98 | return pinctrl_gpio_direction_input(chip->base + offset); |
52 | int reg; | ||
53 | |||
54 | reg = axp20x_gpio_get_reg(offset); | ||
55 | if (reg < 0) | ||
56 | return reg; | ||
57 | |||
58 | return regmap_update_bits(gpio->regmap, reg, | ||
59 | AXP20X_GPIO_FUNCTIONS, | ||
60 | AXP20X_GPIO_FUNCTION_INPUT); | ||
61 | } | 99 | } |
62 | 100 | ||
63 | static int axp20x_gpio_get(struct gpio_chip *chip, unsigned int offset) | 101 | static int axp20x_gpio_get(struct gpio_chip *chip, unsigned int offset) |
@@ -106,29 +144,220 @@ static int axp20x_gpio_get_direction(struct gpio_chip *chip, | |||
106 | static int axp20x_gpio_output(struct gpio_chip *chip, unsigned int offset, | 144 | static int axp20x_gpio_output(struct gpio_chip *chip, unsigned int offset, |
107 | int value) | 145 | int value) |
108 | { | 146 | { |
147 | chip->set(chip, offset, value); | ||
148 | |||
149 | return 0; | ||
150 | } | ||
151 | |||
152 | static void axp20x_gpio_set(struct gpio_chip *chip, unsigned int offset, | ||
153 | int value) | ||
154 | { | ||
109 | struct axp20x_gpio *gpio = gpiochip_get_data(chip); | 155 | struct axp20x_gpio *gpio = gpiochip_get_data(chip); |
110 | int reg; | 156 | int reg; |
111 | 157 | ||
112 | reg = axp20x_gpio_get_reg(offset); | 158 | reg = axp20x_gpio_get_reg(offset); |
113 | if (reg < 0) | 159 | if (reg < 0) |
160 | return; | ||
161 | |||
162 | regmap_update_bits(gpio->regmap, reg, | ||
163 | AXP20X_GPIO_FUNCTIONS, | ||
164 | value ? AXP20X_GPIO_FUNCTION_OUT_HIGH : | ||
165 | AXP20X_GPIO_FUNCTION_OUT_LOW); | ||
166 | } | ||
167 | |||
168 | static int axp20x_pmx_set(struct pinctrl_dev *pctldev, unsigned int offset, | ||
169 | u8 config) | ||
170 | { | ||
171 | struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev); | ||
172 | int reg; | ||
173 | |||
174 | reg = axp20x_gpio_get_reg(offset); | ||
175 | if (reg < 0) | ||
114 | return reg; | 176 | return reg; |
115 | 177 | ||
116 | return regmap_update_bits(gpio->regmap, reg, | 178 | return regmap_update_bits(gpio->regmap, reg, AXP20X_GPIO_FUNCTIONS, |
117 | AXP20X_GPIO_FUNCTIONS, | 179 | config); |
118 | value ? AXP20X_GPIO_FUNCTION_OUT_HIGH | ||
119 | : AXP20X_GPIO_FUNCTION_OUT_LOW); | ||
120 | } | 180 | } |
121 | 181 | ||
122 | static void axp20x_gpio_set(struct gpio_chip *chip, unsigned int offset, | 182 | static int axp20x_pmx_func_cnt(struct pinctrl_dev *pctldev) |
123 | int value) | 183 | { |
184 | struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev); | ||
185 | |||
186 | return ARRAY_SIZE(gpio->funcs); | ||
187 | } | ||
188 | |||
189 | static const char *axp20x_pmx_func_name(struct pinctrl_dev *pctldev, | ||
190 | unsigned int selector) | ||
191 | { | ||
192 | struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev); | ||
193 | |||
194 | return gpio->funcs[selector].name; | ||
195 | } | ||
196 | |||
197 | static int axp20x_pmx_func_groups(struct pinctrl_dev *pctldev, | ||
198 | unsigned int selector, | ||
199 | const char * const **groups, | ||
200 | unsigned int *num_groups) | ||
201 | { | ||
202 | struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev); | ||
203 | |||
204 | *groups = gpio->funcs[selector].groups; | ||
205 | *num_groups = gpio->funcs[selector].ngroups; | ||
206 | |||
207 | return 0; | ||
208 | } | ||
209 | |||
210 | static int axp20x_pmx_set_mux(struct pinctrl_dev *pctldev, | ||
211 | unsigned int function, unsigned int group) | ||
212 | { | ||
213 | struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev); | ||
214 | unsigned int mask; | ||
215 | |||
216 | /* Every pin supports GPIO_OUT and GPIO_IN functions */ | ||
217 | if (function <= AXP20X_FUNC_GPIO_IN) | ||
218 | return axp20x_pmx_set(pctldev, group, | ||
219 | gpio->funcs[function].muxval); | ||
220 | |||
221 | if (function == AXP20X_FUNC_LDO) | ||
222 | mask = gpio->desc->ldo_mask; | ||
223 | else | ||
224 | mask = gpio->desc->adc_mask; | ||
225 | |||
226 | if (!(BIT(group) & mask)) | ||
227 | return -EINVAL; | ||
228 | |||
229 | /* | ||
230 | * We let the regulator framework handle the LDO muxing as muxing bits | ||
231 | * are basically also regulators on/off bits. It's better not to enforce | ||
232 | * any state of the regulator when selecting LDO mux so that we don't | ||
233 | * interfere with the regulator driver. | ||
234 | */ | ||
235 | if (function == AXP20X_FUNC_LDO) | ||
236 | return 0; | ||
237 | |||
238 | return axp20x_pmx_set(pctldev, group, gpio->funcs[function].muxval); | ||
239 | } | ||
240 | |||
241 | static int axp20x_pmx_gpio_set_direction(struct pinctrl_dev *pctldev, | ||
242 | struct pinctrl_gpio_range *range, | ||
243 | unsigned int offset, bool input) | ||
244 | { | ||
245 | struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev); | ||
246 | |||
247 | if (input) | ||
248 | return axp20x_pmx_set(pctldev, offset, | ||
249 | gpio->funcs[AXP20X_FUNC_GPIO_IN].muxval); | ||
250 | |||
251 | return axp20x_pmx_set(pctldev, offset, | ||
252 | gpio->funcs[AXP20X_FUNC_GPIO_OUT].muxval); | ||
253 | } | ||
254 | |||
255 | static const struct pinmux_ops axp20x_pmx_ops = { | ||
256 | .get_functions_count = axp20x_pmx_func_cnt, | ||
257 | .get_function_name = axp20x_pmx_func_name, | ||
258 | .get_function_groups = axp20x_pmx_func_groups, | ||
259 | .set_mux = axp20x_pmx_set_mux, | ||
260 | .gpio_set_direction = axp20x_pmx_gpio_set_direction, | ||
261 | .strict = true, | ||
262 | }; | ||
263 | |||
264 | static int axp20x_groups_cnt(struct pinctrl_dev *pctldev) | ||
265 | { | ||
266 | struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev); | ||
267 | |||
268 | return gpio->desc->npins; | ||
269 | } | ||
270 | |||
271 | static int axp20x_group_pins(struct pinctrl_dev *pctldev, unsigned int selector, | ||
272 | const unsigned int **pins, unsigned int *num_pins) | ||
273 | { | ||
274 | struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev); | ||
275 | |||
276 | *pins = (unsigned int *)&gpio->desc->pins[selector]; | ||
277 | *num_pins = 1; | ||
278 | |||
279 | return 0; | ||
280 | } | ||
281 | |||
282 | static const char *axp20x_group_name(struct pinctrl_dev *pctldev, | ||
283 | unsigned int selector) | ||
124 | { | 284 | { |
125 | axp20x_gpio_output(chip, offset, value); | 285 | struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev); |
286 | |||
287 | return gpio->desc->pins[selector].name; | ||
288 | } | ||
289 | |||
290 | static const struct pinctrl_ops axp20x_pctrl_ops = { | ||
291 | .dt_node_to_map = pinconf_generic_dt_node_to_map_group, | ||
292 | .dt_free_map = pinconf_generic_dt_free_map, | ||
293 | .get_groups_count = axp20x_groups_cnt, | ||
294 | .get_group_name = axp20x_group_name, | ||
295 | .get_group_pins = axp20x_group_pins, | ||
296 | }; | ||
297 | |||
298 | static void axp20x_funcs_groups_from_mask(struct device *dev, unsigned int mask, | ||
299 | unsigned int mask_len, | ||
300 | struct axp20x_pinctrl_function *func, | ||
301 | const struct pinctrl_pin_desc *pins) | ||
302 | { | ||
303 | unsigned long int mask_cpy = mask; | ||
304 | const char **group; | ||
305 | unsigned int ngroups = hweight8(mask); | ||
306 | int bit; | ||
307 | |||
308 | func->ngroups = ngroups; | ||
309 | if (func->ngroups > 0) { | ||
310 | func->groups = devm_kzalloc(dev, ngroups * sizeof(const char *), | ||
311 | GFP_KERNEL); | ||
312 | group = func->groups; | ||
313 | for_each_set_bit(bit, &mask_cpy, mask_len) { | ||
314 | *group = pins[bit].name; | ||
315 | group++; | ||
316 | } | ||
317 | } | ||
318 | } | ||
319 | |||
320 | static void axp20x_build_funcs_groups(struct platform_device *pdev) | ||
321 | { | ||
322 | struct axp20x_gpio *gpio = platform_get_drvdata(pdev); | ||
323 | int i, pin, npins = gpio->desc->npins; | ||
324 | |||
325 | gpio->funcs[AXP20X_FUNC_GPIO_OUT].name = "gpio_out"; | ||
326 | gpio->funcs[AXP20X_FUNC_GPIO_OUT].muxval = AXP20X_MUX_GPIO_OUT; | ||
327 | gpio->funcs[AXP20X_FUNC_GPIO_IN].name = "gpio_in"; | ||
328 | gpio->funcs[AXP20X_FUNC_GPIO_IN].muxval = AXP20X_MUX_GPIO_IN; | ||
329 | gpio->funcs[AXP20X_FUNC_LDO].name = "ldo"; | ||
330 | /* | ||
331 | * Muxval for LDO is useless as we won't use it. | ||
332 | * See comment in axp20x_pmx_set_mux. | ||
333 | */ | ||
334 | gpio->funcs[AXP20X_FUNC_ADC].name = "adc"; | ||
335 | gpio->funcs[AXP20X_FUNC_ADC].muxval = AXP20X_MUX_ADC; | ||
336 | |||
337 | /* Every pin supports GPIO_OUT and GPIO_IN functions */ | ||
338 | for (i = 0; i <= AXP20X_FUNC_GPIO_IN; i++) { | ||
339 | gpio->funcs[i].ngroups = npins; | ||
340 | gpio->funcs[i].groups = devm_kzalloc(&pdev->dev, | ||
341 | npins * sizeof(char *), | ||
342 | GFP_KERNEL); | ||
343 | for (pin = 0; pin < npins; pin++) | ||
344 | gpio->funcs[i].groups[pin] = gpio->desc->pins[pin].name; | ||
345 | } | ||
346 | |||
347 | axp20x_funcs_groups_from_mask(&pdev->dev, gpio->desc->ldo_mask, | ||
348 | npins, &gpio->funcs[AXP20X_FUNC_LDO], | ||
349 | gpio->desc->pins); | ||
350 | |||
351 | axp20x_funcs_groups_from_mask(&pdev->dev, gpio->desc->adc_mask, | ||
352 | npins, &gpio->funcs[AXP20X_FUNC_ADC], | ||
353 | gpio->desc->pins); | ||
126 | } | 354 | } |
127 | 355 | ||
128 | static int axp20x_gpio_probe(struct platform_device *pdev) | 356 | static int axp20x_gpio_probe(struct platform_device *pdev) |
129 | { | 357 | { |
130 | struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); | 358 | struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); |
131 | struct axp20x_gpio *gpio; | 359 | struct axp20x_gpio *gpio; |
360 | struct pinctrl_desc *pctrl_desc; | ||
132 | int ret; | 361 | int ret; |
133 | 362 | ||
134 | if (!of_device_is_available(pdev->dev.of_node)) | 363 | if (!of_device_is_available(pdev->dev.of_node)) |
@@ -145,6 +374,8 @@ static int axp20x_gpio_probe(struct platform_device *pdev) | |||
145 | 374 | ||
146 | gpio->chip.base = -1; | 375 | gpio->chip.base = -1; |
147 | gpio->chip.can_sleep = true; | 376 | gpio->chip.can_sleep = true; |
377 | gpio->chip.request = gpiochip_generic_request; | ||
378 | gpio->chip.free = gpiochip_generic_free; | ||
148 | gpio->chip.parent = &pdev->dev; | 379 | gpio->chip.parent = &pdev->dev; |
149 | gpio->chip.label = dev_name(&pdev->dev); | 380 | gpio->chip.label = dev_name(&pdev->dev); |
150 | gpio->chip.owner = THIS_MODULE; | 381 | gpio->chip.owner = THIS_MODULE; |
@@ -155,7 +386,30 @@ static int axp20x_gpio_probe(struct platform_device *pdev) | |||
155 | gpio->chip.direction_output = axp20x_gpio_output; | 386 | gpio->chip.direction_output = axp20x_gpio_output; |
156 | gpio->chip.ngpio = 3; | 387 | gpio->chip.ngpio = 3; |
157 | 388 | ||
389 | gpio->desc = &axp20x_data; | ||
158 | gpio->regmap = axp20x->regmap; | 390 | gpio->regmap = axp20x->regmap; |
391 | gpio->dev = &pdev->dev; | ||
392 | |||
393 | platform_set_drvdata(pdev, gpio); | ||
394 | |||
395 | axp20x_build_funcs_groups(pdev); | ||
396 | |||
397 | pctrl_desc = devm_kzalloc(&pdev->dev, sizeof(*pctrl_desc), GFP_KERNEL); | ||
398 | if (!pctrl_desc) | ||
399 | return -ENOMEM; | ||
400 | |||
401 | pctrl_desc->name = dev_name(&pdev->dev); | ||
402 | pctrl_desc->owner = THIS_MODULE; | ||
403 | pctrl_desc->pins = gpio->desc->pins; | ||
404 | pctrl_desc->npins = gpio->desc->npins; | ||
405 | pctrl_desc->pctlops = &axp20x_pctrl_ops; | ||
406 | pctrl_desc->pmxops = &axp20x_pmx_ops; | ||
407 | |||
408 | gpio->pctl_dev = devm_pinctrl_register(&pdev->dev, pctrl_desc, gpio); | ||
409 | if (IS_ERR(gpio->pctl_dev)) { | ||
410 | dev_err(&pdev->dev, "couldn't register pinctrl driver\n"); | ||
411 | return PTR_ERR(gpio->pctl_dev); | ||
412 | } | ||
159 | 413 | ||
160 | ret = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio); | 414 | ret = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio); |
161 | if (ret) { | 415 | if (ret) { |
@@ -163,7 +417,16 @@ static int axp20x_gpio_probe(struct platform_device *pdev) | |||
163 | return ret; | 417 | return ret; |
164 | } | 418 | } |
165 | 419 | ||
166 | dev_info(&pdev->dev, "AXP209 GPIO driver loaded\n"); | 420 | ret = gpiochip_add_pin_range(&gpio->chip, dev_name(&pdev->dev), |
421 | gpio->desc->pins->number, | ||
422 | gpio->desc->pins->number, | ||
423 | gpio->desc->npins); | ||
424 | if (ret) { | ||
425 | dev_err(&pdev->dev, "failed to add pin range\n"); | ||
426 | return ret; | ||
427 | } | ||
428 | |||
429 | dev_info(&pdev->dev, "AXP209 pinctrl and GPIO driver loaded\n"); | ||
167 | 430 | ||
168 | return 0; | 431 | return 0; |
169 | } | 432 | } |
@@ -185,5 +448,6 @@ static struct platform_driver axp20x_gpio_driver = { | |||
185 | module_platform_driver(axp20x_gpio_driver); | 448 | module_platform_driver(axp20x_gpio_driver); |
186 | 449 | ||
187 | MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); | 450 | MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); |
188 | MODULE_DESCRIPTION("AXP20x PMIC GPIO driver"); | 451 | MODULE_AUTHOR("Quentin Schulz <quentin.schulz@free-electrons.com>"); |
452 | MODULE_DESCRIPTION("AXP20x PMIC pinctrl and GPIO driver"); | ||
189 | MODULE_LICENSE("GPL"); | 453 | MODULE_LICENSE("GPL"); |