Commit | Line | Data |
---|---|---|
84130aac RG |
1 | /* |
2 | * Helpers for controlling modem lines via GPIO | |
3 | * | |
4 | * Copyright (C) 2014 Paratronic S.A. | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | */ | |
17 | ||
18 | #include <linux/err.h> | |
19 | #include <linux/device.h> | |
20 | #include <linux/gpio/consumer.h> | |
93b88774 | 21 | #include <linux/termios.h> |
84130aac RG |
22 | |
23 | #include "serial_mctrl_gpio.h" | |
24 | ||
25 | struct mctrl_gpios { | |
26 | struct gpio_desc *gpio[UART_GPIO_MAX]; | |
27 | }; | |
28 | ||
29 | static const struct { | |
30 | const char *name; | |
31 | unsigned int mctrl; | |
32 | bool dir_out; | |
33 | } mctrl_gpios_desc[UART_GPIO_MAX] = { | |
34 | { "cts", TIOCM_CTS, false, }, | |
35 | { "dsr", TIOCM_DSR, false, }, | |
36 | { "dcd", TIOCM_CD, false, }, | |
37 | { "rng", TIOCM_RNG, false, }, | |
38 | { "rts", TIOCM_RTS, true, }, | |
39 | { "dtr", TIOCM_DTR, true, }, | |
40 | { "out1", TIOCM_OUT1, true, }, | |
41 | { "out2", TIOCM_OUT2, true, }, | |
42 | }; | |
43 | ||
44 | void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl) | |
45 | { | |
46 | enum mctrl_gpio_idx i; | |
834296a3 RI |
47 | struct gpio_desc *desc_array[UART_GPIO_MAX]; |
48 | int value_array[UART_GPIO_MAX]; | |
49 | unsigned int count = 0; | |
84130aac RG |
50 | |
51 | if (IS_ERR_OR_NULL(gpios)) | |
52 | return; | |
53 | ||
54 | for (i = 0; i < UART_GPIO_MAX; i++) | |
55 | if (!IS_ERR_OR_NULL(gpios->gpio[i]) && | |
834296a3 RI |
56 | mctrl_gpios_desc[i].dir_out) { |
57 | desc_array[count] = gpios->gpio[i]; | |
58 | value_array[count] = !!(mctrl & mctrl_gpios_desc[i].mctrl); | |
59 | count++; | |
60 | } | |
61 | gpiod_set_array(count, desc_array, value_array); | |
84130aac RG |
62 | } |
63 | EXPORT_SYMBOL_GPL(mctrl_gpio_set); | |
64 | ||
65 | struct gpio_desc *mctrl_gpio_to_gpiod(struct mctrl_gpios *gpios, | |
66 | enum mctrl_gpio_idx gidx) | |
67 | { | |
68 | if (!IS_ERR_OR_NULL(gpios) && !IS_ERR_OR_NULL(gpios->gpio[gidx])) | |
69 | return gpios->gpio[gidx]; | |
70 | else | |
71 | return NULL; | |
72 | } | |
73 | EXPORT_SYMBOL_GPL(mctrl_gpio_to_gpiod); | |
74 | ||
75 | unsigned int mctrl_gpio_get(struct mctrl_gpios *gpios, unsigned int *mctrl) | |
76 | { | |
77 | enum mctrl_gpio_idx i; | |
78 | ||
79 | /* | |
80 | * return it unchanged if the structure is not allocated | |
81 | */ | |
82 | if (IS_ERR_OR_NULL(gpios)) | |
83 | return *mctrl; | |
84 | ||
85 | for (i = 0; i < UART_GPIO_MAX; i++) { | |
86 | if (!IS_ERR_OR_NULL(gpios->gpio[i]) && | |
87 | !mctrl_gpios_desc[i].dir_out) { | |
88 | if (gpiod_get_value(gpios->gpio[i])) | |
89 | *mctrl |= mctrl_gpios_desc[i].mctrl; | |
90 | else | |
91 | *mctrl &= ~mctrl_gpios_desc[i].mctrl; | |
92 | } | |
93 | } | |
94 | ||
95 | return *mctrl; | |
96 | } | |
97 | EXPORT_SYMBOL_GPL(mctrl_gpio_get); | |
98 | ||
99 | struct mctrl_gpios *mctrl_gpio_init(struct device *dev, unsigned int idx) | |
100 | { | |
101 | struct mctrl_gpios *gpios; | |
102 | enum mctrl_gpio_idx i; | |
103 | int err; | |
104 | ||
105 | gpios = devm_kzalloc(dev, sizeof(*gpios), GFP_KERNEL); | |
106 | if (!gpios) | |
107 | return ERR_PTR(-ENOMEM); | |
108 | ||
109 | for (i = 0; i < UART_GPIO_MAX; i++) { | |
110 | gpios->gpio[i] = devm_gpiod_get_index(dev, | |
111 | mctrl_gpios_desc[i].name, | |
112 | idx); | |
113 | ||
114 | /* | |
115 | * The GPIOs are maybe not all filled, | |
116 | * this is not an error. | |
117 | */ | |
118 | if (IS_ERR_OR_NULL(gpios->gpio[i])) | |
119 | continue; | |
120 | ||
121 | if (mctrl_gpios_desc[i].dir_out) | |
122 | err = gpiod_direction_output(gpios->gpio[i], 0); | |
123 | else | |
124 | err = gpiod_direction_input(gpios->gpio[i]); | |
125 | if (err) { | |
126 | dev_dbg(dev, "Unable to set direction for %s GPIO", | |
127 | mctrl_gpios_desc[i].name); | |
128 | devm_gpiod_put(dev, gpios->gpio[i]); | |
129 | gpios->gpio[i] = NULL; | |
130 | } | |
131 | } | |
132 | ||
133 | return gpios; | |
134 | } | |
135 | EXPORT_SYMBOL_GPL(mctrl_gpio_init); | |
136 | ||
137 | void mctrl_gpio_free(struct device *dev, struct mctrl_gpios *gpios) | |
138 | { | |
139 | enum mctrl_gpio_idx i; | |
140 | ||
141 | if (IS_ERR_OR_NULL(gpios)) | |
142 | return; | |
143 | ||
144 | for (i = 0; i < UART_GPIO_MAX; i++) | |
145 | if (!IS_ERR_OR_NULL(gpios->gpio[i])) | |
146 | devm_gpiod_put(dev, gpios->gpio[i]); | |
147 | devm_kfree(dev, gpios); | |
148 | } | |
149 | EXPORT_SYMBOL_GPL(mctrl_gpio_free); |