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