diff options
author | Laxman Dewangan <ldewangan@nvidia.com> | 2013-10-02 11:50:29 -0400 |
---|---|---|
committer | Linus Walleij <linus.walleij@linaro.org> | 2013-10-10 11:38:29 -0400 |
commit | c8ce878206076b159ee9488133aa51314570da38 (patch) | |
tree | 42f9f1e3ea4c60c6b8f525ff90cea27ff31077c0 /drivers/pinctrl/pinctrl-as3722.c | |
parent | d1c30115d06e255a7ba69c7b63129a92c2f34de6 (diff) |
pincntrl: add support for ams AS3722 pin control driver
The AS3722 is a compact system PMU suitable for mobile phones, tablets etc.
Add a driver to support accessing the GPIO, pinmux and pin configuration
of 8 GPIO pins found on the ams AS3722 through pin control driver and
gpiolib.
The driver will register itself as the pincontrol driver and gpio driver.
Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Diffstat (limited to 'drivers/pinctrl/pinctrl-as3722.c')
-rw-r--r-- | drivers/pinctrl/pinctrl-as3722.c | 630 |
1 files changed, 630 insertions, 0 deletions
diff --git a/drivers/pinctrl/pinctrl-as3722.c b/drivers/pinctrl/pinctrl-as3722.c new file mode 100644 index 000000000000..01bffc1d52fd --- /dev/null +++ b/drivers/pinctrl/pinctrl-as3722.c | |||
@@ -0,0 +1,630 @@ | |||
1 | /* | ||
2 | * ams AS3722 pin control and GPIO driver. | ||
3 | * | ||
4 | * Copyright (c) 2013, NVIDIA Corporation. | ||
5 | * | ||
6 | * Author: Laxman Dewangan <ldewangan@nvidia.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License as | ||
10 | * published by the Free Software Foundation version 2. | ||
11 | * | ||
12 | * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, | ||
13 | * whether express or implied; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | ||
20 | * 02111-1307, USA | ||
21 | */ | ||
22 | |||
23 | #include <linux/delay.h> | ||
24 | #include <linux/gpio.h> | ||
25 | #include <linux/kernel.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/mfd/as3722.h> | ||
28 | #include <linux/of.h> | ||
29 | #include <linux/of_device.h> | ||
30 | #include <linux/platform_device.h> | ||
31 | #include <linux/pinctrl/consumer.h> | ||
32 | #include <linux/pinctrl/machine.h> | ||
33 | #include <linux/pinctrl/pinctrl.h> | ||
34 | #include <linux/pinctrl/pinconf-generic.h> | ||
35 | #include <linux/pinctrl/pinconf.h> | ||
36 | #include <linux/pinctrl/pinmux.h> | ||
37 | #include <linux/pm.h> | ||
38 | #include <linux/slab.h> | ||
39 | |||
40 | #include "core.h" | ||
41 | #include "pinconf.h" | ||
42 | #include "pinctrl-utils.h" | ||
43 | |||
44 | #define AS3722_PIN_GPIO0 0 | ||
45 | #define AS3722_PIN_GPIO1 1 | ||
46 | #define AS3722_PIN_GPIO2 2 | ||
47 | #define AS3722_PIN_GPIO3 3 | ||
48 | #define AS3722_PIN_GPIO4 4 | ||
49 | #define AS3722_PIN_GPIO5 5 | ||
50 | #define AS3722_PIN_GPIO6 6 | ||
51 | #define AS3722_PIN_GPIO7 7 | ||
52 | #define AS3722_PIN_NUM (AS3722_PIN_GPIO7 + 1) | ||
53 | |||
54 | #define AS3722_GPIO_MODE_PULL_UP BIT(PIN_CONFIG_BIAS_PULL_UP) | ||
55 | #define AS3722_GPIO_MODE_PULL_DOWN BIT(PIN_CONFIG_BIAS_PULL_DOWN) | ||
56 | #define AS3722_GPIO_MODE_HIGH_IMPED BIT(PIN_CONFIG_BIAS_HIGH_IMPEDANCE) | ||
57 | #define AS3722_GPIO_MODE_OPEN_DRAIN BIT(PIN_CONFIG_DRIVE_OPEN_DRAIN) | ||
58 | |||
59 | struct as3722_pin_function { | ||
60 | const char *name; | ||
61 | const char * const *groups; | ||
62 | unsigned ngroups; | ||
63 | int mux_option; | ||
64 | }; | ||
65 | |||
66 | struct as3722_gpio_pin_control { | ||
67 | bool enable_gpio_invert; | ||
68 | unsigned mode_prop; | ||
69 | int io_function; | ||
70 | }; | ||
71 | |||
72 | struct as3722_pingroup { | ||
73 | const char *name; | ||
74 | const unsigned pins[1]; | ||
75 | unsigned npins; | ||
76 | }; | ||
77 | |||
78 | struct as3722_pctrl_info { | ||
79 | struct device *dev; | ||
80 | struct pinctrl_dev *pctl; | ||
81 | struct as3722 *as3722; | ||
82 | struct gpio_chip gpio_chip; | ||
83 | int pins_current_opt[AS3722_PIN_NUM]; | ||
84 | const struct as3722_pin_function *functions; | ||
85 | unsigned num_functions; | ||
86 | const struct as3722_pingroup *pin_groups; | ||
87 | int num_pin_groups; | ||
88 | const struct pinctrl_pin_desc *pins; | ||
89 | unsigned num_pins; | ||
90 | struct as3722_gpio_pin_control gpio_control[AS3722_PIN_NUM]; | ||
91 | }; | ||
92 | |||
93 | static const struct pinctrl_pin_desc as3722_pins_desc[] = { | ||
94 | PINCTRL_PIN(AS3722_PIN_GPIO0, "gpio0"), | ||
95 | PINCTRL_PIN(AS3722_PIN_GPIO1, "gpio1"), | ||
96 | PINCTRL_PIN(AS3722_PIN_GPIO2, "gpio2"), | ||
97 | PINCTRL_PIN(AS3722_PIN_GPIO3, "gpio3"), | ||
98 | PINCTRL_PIN(AS3722_PIN_GPIO4, "gpio4"), | ||
99 | PINCTRL_PIN(AS3722_PIN_GPIO5, "gpio5"), | ||
100 | PINCTRL_PIN(AS3722_PIN_GPIO6, "gpio6"), | ||
101 | PINCTRL_PIN(AS3722_PIN_GPIO7, "gpio7"), | ||
102 | }; | ||
103 | |||
104 | static const char * const gpio_groups[] = { | ||
105 | "gpio0", | ||
106 | "gpio1", | ||
107 | "gpio2", | ||
108 | "gpio3", | ||
109 | "gpio4", | ||
110 | "gpio5", | ||
111 | "gpio6", | ||
112 | "gpio7", | ||
113 | }; | ||
114 | |||
115 | enum as3722_pinmux_option { | ||
116 | AS3722_PINMUX_GPIO = 0, | ||
117 | AS3722_PINMUX_INTERRUPT_OUT = 1, | ||
118 | AS3722_PINMUX_VSUB_VBAT_UNDEB_LOW_OUT = 2, | ||
119 | AS3722_PINMUX_GPIO_INTERRUPT = 3, | ||
120 | AS3722_PINMUX_PWM_INPUT = 4, | ||
121 | AS3722_PINMUX_VOLTAGE_IN_STBY = 5, | ||
122 | AS3722_PINMUX_OC_PG_SD0 = 6, | ||
123 | AS3722_PINMUX_PG_OUT = 7, | ||
124 | AS3722_PINMUX_CLK32K_OUT = 8, | ||
125 | AS3722_PINMUX_WATCHDOG_INPUT = 9, | ||
126 | AS3722_PINMUX_SOFT_RESET_IN = 11, | ||
127 | AS3722_PINMUX_PWM_OUTPUT = 12, | ||
128 | AS3722_PINMUX_VSUB_VBAT_LOW_DEB_OUT = 13, | ||
129 | AS3722_PINMUX_OC_PG_SD6 = 14, | ||
130 | }; | ||
131 | |||
132 | #define FUNCTION_GROUP(fname, mux) \ | ||
133 | { \ | ||
134 | .name = #fname, \ | ||
135 | .groups = gpio_groups, \ | ||
136 | .ngroups = ARRAY_SIZE(gpio_groups), \ | ||
137 | .mux_option = AS3722_PINMUX_##mux, \ | ||
138 | } | ||
139 | |||
140 | static const struct as3722_pin_function as3722_pin_function[] = { | ||
141 | FUNCTION_GROUP(gpio, GPIO), | ||
142 | FUNCTION_GROUP(interrupt-out, INTERRUPT_OUT), | ||
143 | FUNCTION_GROUP(gpio-in-interrupt, GPIO_INTERRUPT), | ||
144 | FUNCTION_GROUP(vsup-vbat-low-undebounce-out, VSUB_VBAT_UNDEB_LOW_OUT), | ||
145 | FUNCTION_GROUP(vsup-vbat-low-debounce-out, VSUB_VBAT_LOW_DEB_OUT), | ||
146 | FUNCTION_GROUP(voltage-in-standby, VOLTAGE_IN_STBY), | ||
147 | FUNCTION_GROUP(oc-pg-sd0, OC_PG_SD0), | ||
148 | FUNCTION_GROUP(oc-pg-sd6, OC_PG_SD6), | ||
149 | FUNCTION_GROUP(powergood-out, PG_OUT), | ||
150 | FUNCTION_GROUP(pwm-in, PWM_INPUT), | ||
151 | FUNCTION_GROUP(pwm-out, PWM_OUTPUT), | ||
152 | FUNCTION_GROUP(clk32k-out, CLK32K_OUT), | ||
153 | FUNCTION_GROUP(watchdog-in, WATCHDOG_INPUT), | ||
154 | FUNCTION_GROUP(soft-reset-in, SOFT_RESET_IN), | ||
155 | }; | ||
156 | |||
157 | #define AS3722_PINGROUP(pg_name, pin_id) \ | ||
158 | { \ | ||
159 | .name = #pg_name, \ | ||
160 | .pins = {AS3722_PIN_##pin_id}, \ | ||
161 | .npins = 1, \ | ||
162 | } | ||
163 | |||
164 | static const struct as3722_pingroup as3722_pingroups[] = { | ||
165 | AS3722_PINGROUP(gpio0, GPIO0), | ||
166 | AS3722_PINGROUP(gpio1, GPIO1), | ||
167 | AS3722_PINGROUP(gpio2, GPIO2), | ||
168 | AS3722_PINGROUP(gpio3, GPIO3), | ||
169 | AS3722_PINGROUP(gpio4, GPIO4), | ||
170 | AS3722_PINGROUP(gpio5, GPIO5), | ||
171 | AS3722_PINGROUP(gpio6, GPIO6), | ||
172 | AS3722_PINGROUP(gpio7, GPIO7), | ||
173 | }; | ||
174 | |||
175 | static int as3722_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) | ||
176 | { | ||
177 | struct as3722_pctrl_info *as_pci = pinctrl_dev_get_drvdata(pctldev); | ||
178 | |||
179 | return as_pci->num_pin_groups; | ||
180 | } | ||
181 | |||
182 | static const char *as3722_pinctrl_get_group_name(struct pinctrl_dev *pctldev, | ||
183 | unsigned group) | ||
184 | { | ||
185 | struct as3722_pctrl_info *as_pci = pinctrl_dev_get_drvdata(pctldev); | ||
186 | |||
187 | return as_pci->pin_groups[group].name; | ||
188 | } | ||
189 | |||
190 | static int as3722_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, | ||
191 | unsigned group, const unsigned **pins, unsigned *num_pins) | ||
192 | { | ||
193 | struct as3722_pctrl_info *as_pci = pinctrl_dev_get_drvdata(pctldev); | ||
194 | |||
195 | *pins = as_pci->pin_groups[group].pins; | ||
196 | *num_pins = as_pci->pin_groups[group].npins; | ||
197 | return 0; | ||
198 | } | ||
199 | |||
200 | static const struct pinctrl_ops as3722_pinctrl_ops = { | ||
201 | .get_groups_count = as3722_pinctrl_get_groups_count, | ||
202 | .get_group_name = as3722_pinctrl_get_group_name, | ||
203 | .get_group_pins = as3722_pinctrl_get_group_pins, | ||
204 | .dt_node_to_map = pinconf_generic_dt_node_to_map_pin, | ||
205 | .dt_free_map = pinctrl_utils_dt_free_map, | ||
206 | }; | ||
207 | |||
208 | static int as3722_pinctrl_get_funcs_count(struct pinctrl_dev *pctldev) | ||
209 | { | ||
210 | struct as3722_pctrl_info *as_pci = pinctrl_dev_get_drvdata(pctldev); | ||
211 | |||
212 | return as_pci->num_functions; | ||
213 | } | ||
214 | |||
215 | static const char *as3722_pinctrl_get_func_name(struct pinctrl_dev *pctldev, | ||
216 | unsigned function) | ||
217 | { | ||
218 | struct as3722_pctrl_info *as_pci = pinctrl_dev_get_drvdata(pctldev); | ||
219 | |||
220 | return as_pci->functions[function].name; | ||
221 | } | ||
222 | |||
223 | static int as3722_pinctrl_get_func_groups(struct pinctrl_dev *pctldev, | ||
224 | unsigned function, const char * const **groups, | ||
225 | unsigned * const num_groups) | ||
226 | { | ||
227 | struct as3722_pctrl_info *as_pci = pinctrl_dev_get_drvdata(pctldev); | ||
228 | |||
229 | *groups = as_pci->functions[function].groups; | ||
230 | *num_groups = as_pci->functions[function].ngroups; | ||
231 | return 0; | ||
232 | } | ||
233 | |||
234 | static int as3722_pinctrl_enable(struct pinctrl_dev *pctldev, unsigned function, | ||
235 | unsigned group) | ||
236 | { | ||
237 | struct as3722_pctrl_info *as_pci = pinctrl_dev_get_drvdata(pctldev); | ||
238 | int gpio_cntr_reg = AS3722_GPIOn_CONTROL_REG(group); | ||
239 | u8 val = AS3722_GPIO_IOSF_VAL(as_pci->functions[function].mux_option); | ||
240 | int ret; | ||
241 | |||
242 | dev_dbg(as_pci->dev, "%s(): GPIO %u pin to function %u and val %u\n", | ||
243 | __func__, group, function, val); | ||
244 | |||
245 | ret = as3722_update_bits(as_pci->as3722, gpio_cntr_reg, | ||
246 | AS3722_GPIO_IOSF_MASK, val); | ||
247 | if (ret < 0) { | ||
248 | dev_err(as_pci->dev, "GPIO%d_CTRL_REG update failed %d\n", | ||
249 | group, ret); | ||
250 | return ret; | ||
251 | } | ||
252 | as_pci->gpio_control[group].io_function = function; | ||
253 | return ret; | ||
254 | } | ||
255 | |||
256 | static int as3722_pinctrl_gpio_get_mode(unsigned gpio_mode_prop, bool input) | ||
257 | { | ||
258 | if (gpio_mode_prop & AS3722_GPIO_MODE_HIGH_IMPED) | ||
259 | return -EINVAL; | ||
260 | |||
261 | if (gpio_mode_prop & AS3722_GPIO_MODE_OPEN_DRAIN) { | ||
262 | if (gpio_mode_prop & AS3722_GPIO_MODE_PULL_UP) | ||
263 | return AS3722_GPIO_MODE_IO_OPEN_DRAIN_PULL_UP; | ||
264 | return AS3722_GPIO_MODE_IO_OPEN_DRAIN; | ||
265 | } | ||
266 | if (input) { | ||
267 | if (gpio_mode_prop & AS3722_GPIO_MODE_PULL_UP) | ||
268 | return AS3722_GPIO_MODE_INPUT_PULL_UP; | ||
269 | else if (gpio_mode_prop & AS3722_GPIO_MODE_PULL_DOWN) | ||
270 | return AS3722_GPIO_MODE_INPUT_PULL_DOWN; | ||
271 | return AS3722_GPIO_MODE_INPUT; | ||
272 | } | ||
273 | if (gpio_mode_prop & AS3722_GPIO_MODE_PULL_DOWN) | ||
274 | return AS3722_GPIO_MODE_OUTPUT_VDDL; | ||
275 | return AS3722_GPIO_MODE_OUTPUT_VDDH; | ||
276 | } | ||
277 | |||
278 | static int as3722_pinctrl_gpio_request_enable(struct pinctrl_dev *pctldev, | ||
279 | struct pinctrl_gpio_range *range, unsigned offset) | ||
280 | { | ||
281 | struct as3722_pctrl_info *as_pci = pinctrl_dev_get_drvdata(pctldev); | ||
282 | |||
283 | if (as_pci->gpio_control[offset].io_function) | ||
284 | return -EBUSY; | ||
285 | return 0; | ||
286 | } | ||
287 | |||
288 | static int as3722_pinctrl_gpio_set_direction(struct pinctrl_dev *pctldev, | ||
289 | struct pinctrl_gpio_range *range, unsigned offset, bool input) | ||
290 | { | ||
291 | struct as3722_pctrl_info *as_pci = pinctrl_dev_get_drvdata(pctldev); | ||
292 | struct as3722 *as3722 = as_pci->as3722; | ||
293 | int mode; | ||
294 | |||
295 | mode = as3722_pinctrl_gpio_get_mode( | ||
296 | as_pci->gpio_control[offset].mode_prop, input); | ||
297 | if (mode < 0) { | ||
298 | dev_err(as_pci->dev, "%s direction for GPIO %d not supported\n", | ||
299 | (input) ? "Input" : "Output", offset); | ||
300 | return mode; | ||
301 | } | ||
302 | |||
303 | if (as_pci->gpio_control[offset].enable_gpio_invert) | ||
304 | mode |= AS3722_GPIO_INV; | ||
305 | |||
306 | return as3722_write(as3722, AS3722_GPIOn_CONTROL_REG(offset), mode); | ||
307 | } | ||
308 | |||
309 | static const struct pinmux_ops as3722_pinmux_ops = { | ||
310 | .get_functions_count = as3722_pinctrl_get_funcs_count, | ||
311 | .get_function_name = as3722_pinctrl_get_func_name, | ||
312 | .get_function_groups = as3722_pinctrl_get_func_groups, | ||
313 | .enable = as3722_pinctrl_enable, | ||
314 | .gpio_request_enable = as3722_pinctrl_gpio_request_enable, | ||
315 | .gpio_set_direction = as3722_pinctrl_gpio_set_direction, | ||
316 | }; | ||
317 | |||
318 | static int as3722_pinconf_get(struct pinctrl_dev *pctldev, | ||
319 | unsigned pin, unsigned long *config) | ||
320 | { | ||
321 | struct as3722_pctrl_info *as_pci = pinctrl_dev_get_drvdata(pctldev); | ||
322 | enum pin_config_param param = pinconf_to_config_param(*config); | ||
323 | int arg = 0; | ||
324 | u16 prop; | ||
325 | |||
326 | switch (param) { | ||
327 | case PIN_CONFIG_BIAS_DISABLE: | ||
328 | prop = AS3722_GPIO_MODE_PULL_UP | | ||
329 | AS3722_GPIO_MODE_PULL_DOWN; | ||
330 | if (!(as_pci->gpio_control[pin].mode_prop & prop)) | ||
331 | arg = 1; | ||
332 | prop = 0; | ||
333 | break; | ||
334 | |||
335 | case PIN_CONFIG_BIAS_PULL_UP: | ||
336 | prop = AS3722_GPIO_MODE_PULL_UP; | ||
337 | break; | ||
338 | |||
339 | case PIN_CONFIG_BIAS_PULL_DOWN: | ||
340 | prop = AS3722_GPIO_MODE_PULL_DOWN; | ||
341 | break; | ||
342 | |||
343 | case PIN_CONFIG_DRIVE_OPEN_DRAIN: | ||
344 | prop = AS3722_GPIO_MODE_OPEN_DRAIN; | ||
345 | break; | ||
346 | |||
347 | case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: | ||
348 | prop = AS3722_GPIO_MODE_HIGH_IMPED; | ||
349 | break; | ||
350 | |||
351 | default: | ||
352 | dev_err(as_pci->dev, "Properties not supported\n"); | ||
353 | return -ENOTSUPP; | ||
354 | } | ||
355 | |||
356 | if (as_pci->gpio_control[pin].mode_prop & prop) | ||
357 | arg = 1; | ||
358 | |||
359 | *config = pinconf_to_config_packed(param, (u16)arg); | ||
360 | return 0; | ||
361 | } | ||
362 | |||
363 | static int as3722_pinconf_set(struct pinctrl_dev *pctldev, | ||
364 | unsigned pin, unsigned long *configs, | ||
365 | unsigned num_configs) | ||
366 | { | ||
367 | struct as3722_pctrl_info *as_pci = pinctrl_dev_get_drvdata(pctldev); | ||
368 | enum pin_config_param param; | ||
369 | int mode_prop; | ||
370 | int i; | ||
371 | |||
372 | for (i = 0; i < num_configs; i++) { | ||
373 | param = pinconf_to_config_param(configs[i]); | ||
374 | mode_prop = as_pci->gpio_control[pin].mode_prop; | ||
375 | |||
376 | switch (param) { | ||
377 | case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: | ||
378 | break; | ||
379 | |||
380 | case PIN_CONFIG_BIAS_DISABLE: | ||
381 | mode_prop &= ~(AS3722_GPIO_MODE_PULL_UP | | ||
382 | AS3722_GPIO_MODE_PULL_DOWN); | ||
383 | break; | ||
384 | case PIN_CONFIG_BIAS_PULL_UP: | ||
385 | mode_prop |= AS3722_GPIO_MODE_PULL_UP; | ||
386 | break; | ||
387 | |||
388 | case PIN_CONFIG_BIAS_PULL_DOWN: | ||
389 | mode_prop |= AS3722_GPIO_MODE_PULL_DOWN; | ||
390 | break; | ||
391 | |||
392 | case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: | ||
393 | mode_prop |= AS3722_GPIO_MODE_HIGH_IMPED; | ||
394 | break; | ||
395 | |||
396 | case PIN_CONFIG_DRIVE_OPEN_DRAIN: | ||
397 | mode_prop |= AS3722_GPIO_MODE_OPEN_DRAIN; | ||
398 | break; | ||
399 | |||
400 | default: | ||
401 | dev_err(as_pci->dev, "Properties not supported\n"); | ||
402 | return -ENOTSUPP; | ||
403 | } | ||
404 | |||
405 | as_pci->gpio_control[pin].mode_prop = mode_prop; | ||
406 | } | ||
407 | return 0; | ||
408 | } | ||
409 | |||
410 | static const struct pinconf_ops as3722_pinconf_ops = { | ||
411 | .pin_config_get = as3722_pinconf_get, | ||
412 | .pin_config_set = as3722_pinconf_set, | ||
413 | }; | ||
414 | |||
415 | static struct pinctrl_desc as3722_pinctrl_desc = { | ||
416 | .pctlops = &as3722_pinctrl_ops, | ||
417 | .pmxops = &as3722_pinmux_ops, | ||
418 | .confops = &as3722_pinconf_ops, | ||
419 | .owner = THIS_MODULE, | ||
420 | }; | ||
421 | |||
422 | static inline struct as3722_pctrl_info *to_as_pci(struct gpio_chip *chip) | ||
423 | { | ||
424 | return container_of(chip, struct as3722_pctrl_info, gpio_chip); | ||
425 | } | ||
426 | |||
427 | static int as3722_gpio_get(struct gpio_chip *chip, unsigned offset) | ||
428 | { | ||
429 | struct as3722_pctrl_info *as_pci = to_as_pci(chip); | ||
430 | struct as3722 *as3722 = as_pci->as3722; | ||
431 | int ret; | ||
432 | u32 reg; | ||
433 | u32 control; | ||
434 | u32 val; | ||
435 | int mode; | ||
436 | int invert_enable; | ||
437 | |||
438 | ret = as3722_read(as3722, AS3722_GPIOn_CONTROL_REG(offset), &control); | ||
439 | if (ret < 0) { | ||
440 | dev_err(as_pci->dev, | ||
441 | "GPIO_CONTROL%d_REG read failed: %d\n", offset, ret); | ||
442 | return ret; | ||
443 | } | ||
444 | |||
445 | invert_enable = !!(control & AS3722_GPIO_INV); | ||
446 | mode = control & AS3722_GPIO_MODE_MASK; | ||
447 | switch (mode) { | ||
448 | case AS3722_GPIO_MODE_INPUT: | ||
449 | case AS3722_GPIO_MODE_INPUT_PULL_UP: | ||
450 | case AS3722_GPIO_MODE_INPUT_PULL_DOWN: | ||
451 | case AS3722_GPIO_MODE_IO_OPEN_DRAIN: | ||
452 | case AS3722_GPIO_MODE_IO_OPEN_DRAIN_PULL_UP: | ||
453 | reg = AS3722_GPIO_SIGNAL_IN_REG; | ||
454 | break; | ||
455 | case AS3722_GPIO_MODE_OUTPUT_VDDH: | ||
456 | case AS3722_GPIO_MODE_OUTPUT_VDDL: | ||
457 | reg = AS3722_GPIO_SIGNAL_OUT_REG; | ||
458 | break; | ||
459 | default: | ||
460 | return -EINVAL; | ||
461 | } | ||
462 | |||
463 | ret = as3722_read(as3722, reg, &val); | ||
464 | if (ret < 0) { | ||
465 | dev_err(as_pci->dev, | ||
466 | "GPIO_SIGNAL_IN_REG read failed: %d\n", ret); | ||
467 | return ret; | ||
468 | } | ||
469 | |||
470 | val = !!(val & AS3722_GPIOn_SIGNAL(offset)); | ||
471 | return (invert_enable) ? !val : val; | ||
472 | } | ||
473 | |||
474 | static void as3722_gpio_set(struct gpio_chip *chip, unsigned offset, | ||
475 | int value) | ||
476 | { | ||
477 | struct as3722_pctrl_info *as_pci = to_as_pci(chip); | ||
478 | struct as3722 *as3722 = as_pci->as3722; | ||
479 | int en_invert = as_pci->gpio_control[offset].enable_gpio_invert; | ||
480 | u32 val; | ||
481 | int ret; | ||
482 | |||
483 | if (value) | ||
484 | val = (en_invert) ? 0 : AS3722_GPIOn_SIGNAL(offset); | ||
485 | else | ||
486 | val = (en_invert) ? AS3722_GPIOn_SIGNAL(offset) : 0; | ||
487 | |||
488 | ret = as3722_update_bits(as3722, AS3722_GPIO_SIGNAL_OUT_REG, | ||
489 | AS3722_GPIOn_SIGNAL(offset), val); | ||
490 | if (ret < 0) | ||
491 | dev_err(as_pci->dev, | ||
492 | "GPIO_SIGNAL_OUT_REG update failed: %d\n", ret); | ||
493 | } | ||
494 | |||
495 | static int as3722_gpio_direction_input(struct gpio_chip *chip, unsigned offset) | ||
496 | { | ||
497 | return pinctrl_gpio_direction_input(chip->base + offset); | ||
498 | } | ||
499 | |||
500 | static int as3722_gpio_direction_output(struct gpio_chip *chip, | ||
501 | unsigned offset, int value) | ||
502 | { | ||
503 | as3722_gpio_set(chip, offset, value); | ||
504 | return pinctrl_gpio_direction_output(chip->base + offset); | ||
505 | } | ||
506 | |||
507 | static int as3722_gpio_to_irq(struct gpio_chip *chip, unsigned offset) | ||
508 | { | ||
509 | struct as3722_pctrl_info *as_pci = to_as_pci(chip); | ||
510 | |||
511 | return as3722_irq_get_virq(as_pci->as3722, offset); | ||
512 | } | ||
513 | |||
514 | static int as3722_gpio_request(struct gpio_chip *chip, unsigned offset) | ||
515 | { | ||
516 | return pinctrl_request_gpio(chip->base + offset); | ||
517 | } | ||
518 | |||
519 | static void as3722_gpio_free(struct gpio_chip *chip, unsigned offset) | ||
520 | { | ||
521 | pinctrl_free_gpio(chip->base + offset); | ||
522 | } | ||
523 | |||
524 | static const struct gpio_chip as3722_gpio_chip = { | ||
525 | .label = "as3722-gpio", | ||
526 | .owner = THIS_MODULE, | ||
527 | .request = as3722_gpio_request, | ||
528 | .free = as3722_gpio_free, | ||
529 | .get = as3722_gpio_get, | ||
530 | .set = as3722_gpio_set, | ||
531 | .direction_input = as3722_gpio_direction_input, | ||
532 | .direction_output = as3722_gpio_direction_output, | ||
533 | .to_irq = as3722_gpio_to_irq, | ||
534 | .can_sleep = 1, | ||
535 | .ngpio = AS3722_PIN_NUM, | ||
536 | .base = -1, | ||
537 | }; | ||
538 | |||
539 | static int as3722_pinctrl_probe(struct platform_device *pdev) | ||
540 | { | ||
541 | struct as3722_pctrl_info *as_pci; | ||
542 | int ret; | ||
543 | int tret; | ||
544 | |||
545 | as_pci = devm_kzalloc(&pdev->dev, sizeof(*as_pci), GFP_KERNEL); | ||
546 | if (!as_pci) | ||
547 | return -ENOMEM; | ||
548 | |||
549 | as_pci->dev = &pdev->dev; | ||
550 | as_pci->dev->of_node = pdev->dev.parent->of_node; | ||
551 | as_pci->as3722 = dev_get_drvdata(pdev->dev.parent); | ||
552 | platform_set_drvdata(pdev, as_pci); | ||
553 | |||
554 | as_pci->pins = as3722_pins_desc; | ||
555 | as_pci->num_pins = ARRAY_SIZE(as3722_pins_desc); | ||
556 | as_pci->functions = as3722_pin_function; | ||
557 | as_pci->num_functions = ARRAY_SIZE(as3722_pin_function); | ||
558 | as_pci->pin_groups = as3722_pingroups; | ||
559 | as_pci->num_pin_groups = ARRAY_SIZE(as3722_pingroups); | ||
560 | as3722_pinctrl_desc.name = dev_name(&pdev->dev); | ||
561 | as3722_pinctrl_desc.pins = as3722_pins_desc; | ||
562 | as3722_pinctrl_desc.npins = ARRAY_SIZE(as3722_pins_desc); | ||
563 | as_pci->pctl = pinctrl_register(&as3722_pinctrl_desc, | ||
564 | &pdev->dev, as_pci); | ||
565 | if (!as_pci->pctl) { | ||
566 | dev_err(&pdev->dev, "Couldn't register pinctrl driver\n"); | ||
567 | return -EINVAL; | ||
568 | } | ||
569 | |||
570 | as_pci->gpio_chip = as3722_gpio_chip; | ||
571 | as_pci->gpio_chip.dev = &pdev->dev; | ||
572 | as_pci->gpio_chip.of_node = pdev->dev.parent->of_node; | ||
573 | ret = gpiochip_add(&as_pci->gpio_chip); | ||
574 | if (ret < 0) { | ||
575 | dev_err(&pdev->dev, "Couldn't register gpiochip, %d\n", ret); | ||
576 | goto fail_chip_add; | ||
577 | } | ||
578 | |||
579 | ret = gpiochip_add_pin_range(&as_pci->gpio_chip, dev_name(&pdev->dev), | ||
580 | 0, 0, AS3722_PIN_NUM); | ||
581 | if (ret < 0) { | ||
582 | dev_err(&pdev->dev, "Couldn't add pin range, %d\n", ret); | ||
583 | goto fail_range_add; | ||
584 | } | ||
585 | |||
586 | return 0; | ||
587 | |||
588 | fail_range_add: | ||
589 | tret = gpiochip_remove(&as_pci->gpio_chip); | ||
590 | if (tret < 0) | ||
591 | dev_warn(&pdev->dev, "Couldn't remove gpio chip, %d\n", tret); | ||
592 | |||
593 | fail_chip_add: | ||
594 | pinctrl_unregister(as_pci->pctl); | ||
595 | return ret; | ||
596 | } | ||
597 | |||
598 | static int as3722_pinctrl_remove(struct platform_device *pdev) | ||
599 | { | ||
600 | struct as3722_pctrl_info *as_pci = platform_get_drvdata(pdev); | ||
601 | int ret; | ||
602 | |||
603 | ret = gpiochip_remove(&as_pci->gpio_chip); | ||
604 | if (ret < 0) | ||
605 | return ret; | ||
606 | pinctrl_unregister(as_pci->pctl); | ||
607 | return 0; | ||
608 | } | ||
609 | |||
610 | static struct of_device_id as3722_pinctrl_of_match[] = { | ||
611 | { .compatible = "ams,as3722-pinctrl", }, | ||
612 | { }, | ||
613 | }; | ||
614 | MODULE_DEVICE_TABLE(of, as3722_pinctrl_of_match); | ||
615 | |||
616 | static struct platform_driver as3722_pinctrl_driver = { | ||
617 | .driver = { | ||
618 | .name = "as3722-pinctrl", | ||
619 | .owner = THIS_MODULE, | ||
620 | .of_match_table = as3722_pinctrl_of_match, | ||
621 | }, | ||
622 | .probe = as3722_pinctrl_probe, | ||
623 | .remove = as3722_pinctrl_remove, | ||
624 | }; | ||
625 | module_platform_driver(as3722_pinctrl_driver); | ||
626 | |||
627 | MODULE_ALIAS("platform:as3722-pinctrl"); | ||
628 | MODULE_DESCRIPTION("AS3722 pin control and GPIO driver"); | ||
629 | MODULE_AUTHOR("Laxman Dewangan<ldewangan@nvidia.com>"); | ||
630 | MODULE_LICENSE("GPL v2"); | ||