Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0+
2 : : /*
3 : : * Helpers for controlling modem lines via GPIO
4 : : *
5 : : * Copyright (C) 2014 Paratronic S.A.
6 : : */
7 : :
8 : : #include <linux/err.h>
9 : : #include <linux/device.h>
10 : : #include <linux/irq.h>
11 : : #include <linux/gpio/consumer.h>
12 : : #include <linux/termios.h>
13 : : #include <linux/serial_core.h>
14 : : #include <linux/module.h>
15 : : #include <linux/property.h>
16 : :
17 : : #include "serial_mctrl_gpio.h"
18 : :
19 : : struct mctrl_gpios {
20 : : struct uart_port *port;
21 : : struct gpio_desc *gpio[UART_GPIO_MAX];
22 : : int irq[UART_GPIO_MAX];
23 : : unsigned int mctrl_prev;
24 : : bool mctrl_on;
25 : : };
26 : :
27 : : static const struct {
28 : : const char *name;
29 : : unsigned int mctrl;
30 : : enum gpiod_flags flags;
31 : : } mctrl_gpios_desc[UART_GPIO_MAX] = {
32 : : { "cts", TIOCM_CTS, GPIOD_IN, },
33 : : { "dsr", TIOCM_DSR, GPIOD_IN, },
34 : : { "dcd", TIOCM_CD, GPIOD_IN, },
35 : : { "rng", TIOCM_RNG, GPIOD_IN, },
36 : : { "rts", TIOCM_RTS, GPIOD_OUT_LOW, },
37 : : { "dtr", TIOCM_DTR, GPIOD_OUT_LOW, },
38 : : };
39 : :
40 : : static bool mctrl_gpio_flags_is_dir_out(unsigned int idx)
41 : : {
42 : 0 : return mctrl_gpios_desc[idx].flags & GPIOD_FLAGS_BIT_DIR_OUT;
43 : : }
44 : :
45 : 0 : void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl)
46 : : {
47 : : enum mctrl_gpio_idx i;
48 : : struct gpio_desc *desc_array[UART_GPIO_MAX];
49 : : DECLARE_BITMAP(values, UART_GPIO_MAX);
50 : : unsigned int count = 0;
51 : :
52 [ # # ]: 0 : if (gpios == NULL)
53 : 0 : return;
54 : :
55 [ # # ]: 0 : for (i = 0; i < UART_GPIO_MAX; i++)
56 [ # # # # ]: 0 : if (gpios->gpio[i] && mctrl_gpio_flags_is_dir_out(i)) {
57 : 0 : desc_array[count] = gpios->gpio[i];
58 : 0 : __assign_bit(count, values,
59 : 0 : mctrl & mctrl_gpios_desc[i].mctrl);
60 : 0 : count++;
61 : : }
62 : 0 : gpiod_set_array_value(count, desc_array, NULL, values);
63 : : }
64 : : EXPORT_SYMBOL_GPL(mctrl_gpio_set);
65 : :
66 : 0 : struct gpio_desc *mctrl_gpio_to_gpiod(struct mctrl_gpios *gpios,
67 : : enum mctrl_gpio_idx gidx)
68 : : {
69 [ # # ]: 0 : if (gpios == NULL)
70 : : return NULL;
71 : :
72 : 0 : return gpios->gpio[gidx];
73 : : }
74 : : EXPORT_SYMBOL_GPL(mctrl_gpio_to_gpiod);
75 : :
76 : 0 : unsigned int mctrl_gpio_get(struct mctrl_gpios *gpios, unsigned int *mctrl)
77 : : {
78 : : enum mctrl_gpio_idx i;
79 : :
80 [ # # ]: 0 : if (gpios == NULL)
81 : 0 : return *mctrl;
82 : :
83 [ # # ]: 0 : for (i = 0; i < UART_GPIO_MAX; i++) {
84 [ # # # # ]: 0 : if (gpios->gpio[i] && !mctrl_gpio_flags_is_dir_out(i)) {
85 [ # # ]: 0 : if (gpiod_get_value(gpios->gpio[i]))
86 : 0 : *mctrl |= mctrl_gpios_desc[i].mctrl;
87 : : else
88 : 0 : *mctrl &= ~mctrl_gpios_desc[i].mctrl;
89 : : }
90 : : }
91 : :
92 : 0 : return *mctrl;
93 : : }
94 : : EXPORT_SYMBOL_GPL(mctrl_gpio_get);
95 : :
96 : : unsigned int
97 : 0 : mctrl_gpio_get_outputs(struct mctrl_gpios *gpios, unsigned int *mctrl)
98 : : {
99 : : enum mctrl_gpio_idx i;
100 : :
101 [ # # ]: 0 : if (gpios == NULL)
102 : 0 : return *mctrl;
103 : :
104 [ # # ]: 0 : for (i = 0; i < UART_GPIO_MAX; i++) {
105 [ # # # # ]: 0 : if (gpios->gpio[i] && mctrl_gpio_flags_is_dir_out(i)) {
106 [ # # ]: 0 : if (gpiod_get_value(gpios->gpio[i]))
107 : 0 : *mctrl |= mctrl_gpios_desc[i].mctrl;
108 : : else
109 : 0 : *mctrl &= ~mctrl_gpios_desc[i].mctrl;
110 : : }
111 : : }
112 : :
113 : 0 : return *mctrl;
114 : : }
115 : : EXPORT_SYMBOL_GPL(mctrl_gpio_get_outputs);
116 : :
117 : 0 : struct mctrl_gpios *mctrl_gpio_init_noauto(struct device *dev, unsigned int idx)
118 : : {
119 : : struct mctrl_gpios *gpios;
120 : : enum mctrl_gpio_idx i;
121 : :
122 : : gpios = devm_kzalloc(dev, sizeof(*gpios), GFP_KERNEL);
123 [ # # ]: 0 : if (!gpios)
124 : : return ERR_PTR(-ENOMEM);
125 : :
126 [ # # ]: 0 : for (i = 0; i < UART_GPIO_MAX; i++) {
127 : : char *gpio_str;
128 : : bool present;
129 : :
130 : : /* Check if GPIO property exists and continue if not */
131 : 0 : gpio_str = kasprintf(GFP_KERNEL, "%s-gpios",
132 : : mctrl_gpios_desc[i].name);
133 [ # # ]: 0 : if (!gpio_str)
134 : 0 : continue;
135 : :
136 : 0 : present = device_property_present(dev, gpio_str);
137 : 0 : kfree(gpio_str);
138 [ # # ]: 0 : if (!present)
139 : 0 : continue;
140 : :
141 : 0 : gpios->gpio[i] =
142 : 0 : devm_gpiod_get_index_optional(dev,
143 : : mctrl_gpios_desc[i].name,
144 : : idx,
145 : : mctrl_gpios_desc[i].flags);
146 : :
147 [ # # ]: 0 : if (IS_ERR(gpios->gpio[i]))
148 : 0 : return ERR_CAST(gpios->gpio[i]);
149 : : }
150 : :
151 : : return gpios;
152 : : }
153 : : EXPORT_SYMBOL_GPL(mctrl_gpio_init_noauto);
154 : :
155 : : #define MCTRL_ANY_DELTA (TIOCM_RI | TIOCM_DSR | TIOCM_CD | TIOCM_CTS)
156 : 0 : static irqreturn_t mctrl_gpio_irq_handle(int irq, void *context)
157 : : {
158 : : struct mctrl_gpios *gpios = context;
159 : 0 : struct uart_port *port = gpios->port;
160 : 0 : u32 mctrl = gpios->mctrl_prev;
161 : : u32 mctrl_diff;
162 : : unsigned long flags;
163 : :
164 : 0 : mctrl_gpio_get(gpios, &mctrl);
165 : :
166 : 0 : spin_lock_irqsave(&port->lock, flags);
167 : :
168 : 0 : mctrl_diff = mctrl ^ gpios->mctrl_prev;
169 : 0 : gpios->mctrl_prev = mctrl;
170 : :
171 [ # # # # ]: 0 : if (mctrl_diff & MCTRL_ANY_DELTA && port->state != NULL) {
172 [ # # ]: 0 : if ((mctrl_diff & mctrl) & TIOCM_RI)
173 : 0 : port->icount.rng++;
174 : :
175 [ # # ]: 0 : if ((mctrl_diff & mctrl) & TIOCM_DSR)
176 : 0 : port->icount.dsr++;
177 : :
178 [ # # ]: 0 : if (mctrl_diff & TIOCM_CD)
179 : 0 : uart_handle_dcd_change(port, mctrl & TIOCM_CD);
180 : :
181 [ # # ]: 0 : if (mctrl_diff & TIOCM_CTS)
182 : 0 : uart_handle_cts_change(port, mctrl & TIOCM_CTS);
183 : :
184 : 0 : wake_up_interruptible(&port->state->port.delta_msr_wait);
185 : : }
186 : :
187 : : spin_unlock_irqrestore(&port->lock, flags);
188 : :
189 : 0 : return IRQ_HANDLED;
190 : : }
191 : :
192 : 0 : struct mctrl_gpios *mctrl_gpio_init(struct uart_port *port, unsigned int idx)
193 : : {
194 : : struct mctrl_gpios *gpios;
195 : : enum mctrl_gpio_idx i;
196 : :
197 : 0 : gpios = mctrl_gpio_init_noauto(port->dev, idx);
198 [ # # ]: 0 : if (IS_ERR(gpios))
199 : : return gpios;
200 : :
201 : 0 : gpios->port = port;
202 : :
203 [ # # ]: 0 : for (i = 0; i < UART_GPIO_MAX; ++i) {
204 : : int ret;
205 : :
206 [ # # # # ]: 0 : if (!gpios->gpio[i] || mctrl_gpio_flags_is_dir_out(i))
207 : 0 : continue;
208 : :
209 : 0 : ret = gpiod_to_irq(gpios->gpio[i]);
210 [ # # ]: 0 : if (ret <= 0) {
211 : 0 : dev_err(port->dev,
212 : : "failed to find corresponding irq for %s (idx=%d, err=%d)\n",
213 : : mctrl_gpios_desc[i].name, idx, ret);
214 : 0 : return ERR_PTR(ret);
215 : : }
216 : 0 : gpios->irq[i] = ret;
217 : :
218 : : /* irqs should only be enabled in .enable_ms */
219 : 0 : irq_set_status_flags(gpios->irq[i], IRQ_NOAUTOEN);
220 : :
221 : 0 : ret = devm_request_irq(port->dev, gpios->irq[i],
222 : : mctrl_gpio_irq_handle,
223 : 0 : IRQ_TYPE_EDGE_BOTH, dev_name(port->dev),
224 : : gpios);
225 [ # # ]: 0 : if (ret) {
226 : : /* alternatively implement polling */
227 : 0 : dev_err(port->dev,
228 : : "failed to request irq for %s (idx=%d, err=%d)\n",
229 : : mctrl_gpios_desc[i].name, idx, ret);
230 : 0 : return ERR_PTR(ret);
231 : : }
232 : : }
233 : :
234 : : return gpios;
235 : : }
236 : : EXPORT_SYMBOL_GPL(mctrl_gpio_init);
237 : :
238 : 0 : void mctrl_gpio_free(struct device *dev, struct mctrl_gpios *gpios)
239 : : {
240 : : enum mctrl_gpio_idx i;
241 : :
242 [ # # ]: 0 : if (gpios == NULL)
243 : 0 : return;
244 : :
245 [ # # ]: 0 : for (i = 0; i < UART_GPIO_MAX; i++) {
246 [ # # ]: 0 : if (gpios->irq[i])
247 : 0 : devm_free_irq(gpios->port->dev, gpios->irq[i], gpios);
248 : :
249 [ # # ]: 0 : if (gpios->gpio[i])
250 : 0 : devm_gpiod_put(dev, gpios->gpio[i]);
251 : : }
252 : 0 : devm_kfree(dev, gpios);
253 : : }
254 : : EXPORT_SYMBOL_GPL(mctrl_gpio_free);
255 : :
256 : 0 : void mctrl_gpio_enable_ms(struct mctrl_gpios *gpios)
257 : : {
258 : : enum mctrl_gpio_idx i;
259 : :
260 [ # # ]: 0 : if (gpios == NULL)
261 : : return;
262 : :
263 : : /* .enable_ms may be called multiple times */
264 [ # # ]: 0 : if (gpios->mctrl_on)
265 : : return;
266 : :
267 : 0 : gpios->mctrl_on = true;
268 : :
269 : : /* get initial status of modem lines GPIOs */
270 : 0 : mctrl_gpio_get(gpios, &gpios->mctrl_prev);
271 : :
272 [ # # ]: 0 : for (i = 0; i < UART_GPIO_MAX; ++i) {
273 [ # # ]: 0 : if (!gpios->irq[i])
274 : 0 : continue;
275 : :
276 : 0 : enable_irq(gpios->irq[i]);
277 : : }
278 : : }
279 : : EXPORT_SYMBOL_GPL(mctrl_gpio_enable_ms);
280 : :
281 : 0 : void mctrl_gpio_disable_ms(struct mctrl_gpios *gpios)
282 : : {
283 : : enum mctrl_gpio_idx i;
284 : :
285 [ # # ]: 0 : if (gpios == NULL)
286 : : return;
287 : :
288 [ # # ]: 0 : if (!gpios->mctrl_on)
289 : : return;
290 : :
291 : 0 : gpios->mctrl_on = false;
292 : :
293 [ # # ]: 0 : for (i = 0; i < UART_GPIO_MAX; ++i) {
294 [ # # ]: 0 : if (!gpios->irq[i])
295 : 0 : continue;
296 : :
297 : 0 : disable_irq(gpios->irq[i]);
298 : : }
299 : : }
300 : : EXPORT_SYMBOL_GPL(mctrl_gpio_disable_ms);
301 : :
302 : : MODULE_LICENSE("GPL");
|