blob: 2b400189be911cbe0d9ef8fa5893166aab9181e1 [file] [log] [blame]
Greg Kroah-Hartmane3b3d0f2017-11-06 18:11:51 +01001// SPDX-License-Identifier: GPL-2.0+
Richard Genoud84130aa2014-05-13 20:20:43 +02002/*
3 * Helpers for controlling modem lines via GPIO
4 *
5 * Copyright (C) 2014 Paratronic S.A.
Richard Genoud84130aa2014-05-13 20:20:43 +02006 */
7
8#include <linux/err.h>
9#include <linux/device.h>
Uwe Kleine-Königce59e482015-09-30 10:19:41 +020010#include <linux/irq.h>
Richard Genoud84130aa2014-05-13 20:20:43 +020011#include <linux/gpio/consumer.h>
Alexander Shiyan93b88772014-09-20 09:34:45 +040012#include <linux/termios.h>
Uwe Kleine-Königce59e482015-09-30 10:19:41 +020013#include <linux/serial_core.h>
Romain Izard82a3f872016-02-23 15:54:54 +010014#include <linux/module.h>
Stefan Roesed9948262019-06-20 08:24:19 +020015#include <linux/property.h>
Richard Genoud84130aa2014-05-13 20:20:43 +020016
17#include "serial_mctrl_gpio.h"
18
19struct mctrl_gpios {
Uwe Kleine-Königce59e482015-09-30 10:19:41 +020020 struct uart_port *port;
Richard Genoud84130aa2014-05-13 20:20:43 +020021 struct gpio_desc *gpio[UART_GPIO_MAX];
Uwe Kleine-Königce59e482015-09-30 10:19:41 +020022 int irq[UART_GPIO_MAX];
23 unsigned int mctrl_prev;
24 bool mctrl_on;
Richard Genoud84130aa2014-05-13 20:20:43 +020025};
26
27static const struct {
28 const char *name;
29 unsigned int mctrl;
30 bool dir_out;
31} mctrl_gpios_desc[UART_GPIO_MAX] = {
32 { "cts", TIOCM_CTS, false, },
33 { "dsr", TIOCM_DSR, false, },
34 { "dcd", TIOCM_CD, false, },
35 { "rng", TIOCM_RNG, false, },
36 { "rts", TIOCM_RTS, true, },
37 { "dtr", TIOCM_DTR, true, },
Richard Genoud84130aa2014-05-13 20:20:43 +020038};
39
40void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl)
41{
42 enum mctrl_gpio_idx i;
Rojhalat Ibrahim834296a2014-11-17 18:31:30 +010043 struct gpio_desc *desc_array[UART_GPIO_MAX];
Janusz Krzysztofikb9762be2018-09-05 23:50:05 +020044 DECLARE_BITMAP(values, UART_GPIO_MAX);
Rojhalat Ibrahim834296a2014-11-17 18:31:30 +010045 unsigned int count = 0;
Richard Genoud84130aa2014-05-13 20:20:43 +020046
Yegor Yefremov434be0a2016-05-31 10:59:17 +020047 if (gpios == NULL)
48 return;
49
Richard Genoud84130aa2014-05-13 20:20:43 +020050 for (i = 0; i < UART_GPIO_MAX; i++)
Uwe Kleine-König445df7f2015-05-19 21:56:29 +020051 if (gpios->gpio[i] && mctrl_gpios_desc[i].dir_out) {
Rojhalat Ibrahim834296a2014-11-17 18:31:30 +010052 desc_array[count] = gpios->gpio[i];
Janusz Krzysztofikb9762be2018-09-05 23:50:05 +020053 __assign_bit(count, values,
54 mctrl & mctrl_gpios_desc[i].mctrl);
Rojhalat Ibrahim834296a2014-11-17 18:31:30 +010055 count++;
56 }
Janusz Krzysztofik77588c12018-09-05 23:50:07 +020057 gpiod_set_array_value(count, desc_array, NULL, values);
Richard Genoud84130aa2014-05-13 20:20:43 +020058}
59EXPORT_SYMBOL_GPL(mctrl_gpio_set);
60
61struct gpio_desc *mctrl_gpio_to_gpiod(struct mctrl_gpios *gpios,
62 enum mctrl_gpio_idx gidx)
63{
Uwe Kleine-König9e9f0792015-02-12 15:24:41 +010064 return gpios->gpio[gidx];
Richard Genoud84130aa2014-05-13 20:20:43 +020065}
66EXPORT_SYMBOL_GPL(mctrl_gpio_to_gpiod);
67
68unsigned int mctrl_gpio_get(struct mctrl_gpios *gpios, unsigned int *mctrl)
69{
70 enum mctrl_gpio_idx i;
71
Yegor Yefremov434be0a2016-05-31 10:59:17 +020072 if (gpios == NULL)
73 return *mctrl;
74
Richard Genoud84130aa2014-05-13 20:20:43 +020075 for (i = 0; i < UART_GPIO_MAX; i++) {
Uwe Kleine-König9e9f0792015-02-12 15:24:41 +010076 if (gpios->gpio[i] && !mctrl_gpios_desc[i].dir_out) {
Richard Genoud84130aa2014-05-13 20:20:43 +020077 if (gpiod_get_value(gpios->gpio[i]))
78 *mctrl |= mctrl_gpios_desc[i].mctrl;
79 else
80 *mctrl &= ~mctrl_gpios_desc[i].mctrl;
81 }
82 }
83
84 return *mctrl;
85}
86EXPORT_SYMBOL_GPL(mctrl_gpio_get);
87
Yegor Yefremovbf5cee62016-05-31 10:59:16 +020088unsigned int
89mctrl_gpio_get_outputs(struct mctrl_gpios *gpios, unsigned int *mctrl)
90{
91 enum mctrl_gpio_idx i;
92
Yegor Yefremov434be0a2016-05-31 10:59:17 +020093 if (gpios == NULL)
94 return *mctrl;
95
Yegor Yefremovbf5cee62016-05-31 10:59:16 +020096 for (i = 0; i < UART_GPIO_MAX; i++) {
97 if (gpios->gpio[i] && mctrl_gpios_desc[i].dir_out) {
98 if (gpiod_get_value(gpios->gpio[i]))
99 *mctrl |= mctrl_gpios_desc[i].mctrl;
100 else
101 *mctrl &= ~mctrl_gpios_desc[i].mctrl;
102 }
103 }
104
105 return *mctrl;
106}
107EXPORT_SYMBOL_GPL(mctrl_gpio_get_outputs);
108
Uwe Kleine-König7d8c70d2015-09-30 10:19:40 +0200109struct mctrl_gpios *mctrl_gpio_init_noauto(struct device *dev, unsigned int idx)
Richard Genoud84130aa2014-05-13 20:20:43 +0200110{
111 struct mctrl_gpios *gpios;
112 enum mctrl_gpio_idx i;
Richard Genoud84130aa2014-05-13 20:20:43 +0200113
114 gpios = devm_kzalloc(dev, sizeof(*gpios), GFP_KERNEL);
115 if (!gpios)
116 return ERR_PTR(-ENOMEM);
117
118 for (i = 0; i < UART_GPIO_MAX; i++) {
Uwe Kleine-König1d267ea2015-02-12 15:24:42 +0100119 enum gpiod_flags flags;
Stefan Roesed9948262019-06-20 08:24:19 +0200120 char *gpio_str;
121 bool present;
122
123 /* Check if GPIO property exists and continue if not */
124 gpio_str = kasprintf(GFP_KERNEL, "%s-gpios",
125 mctrl_gpios_desc[i].name);
126 if (!gpio_str)
127 continue;
128
129 present = device_property_present(dev, gpio_str);
130 kfree(gpio_str);
131 if (!present)
132 continue;
Richard Genoud84130aa2014-05-13 20:20:43 +0200133
134 if (mctrl_gpios_desc[i].dir_out)
Uwe Kleine-König1d267ea2015-02-12 15:24:42 +0100135 flags = GPIOD_OUT_LOW;
Richard Genoud84130aa2014-05-13 20:20:43 +0200136 else
Uwe Kleine-König1d267ea2015-02-12 15:24:42 +0100137 flags = GPIOD_IN;
138
139 gpios->gpio[i] =
140 devm_gpiod_get_index_optional(dev,
141 mctrl_gpios_desc[i].name,
142 idx, flags);
143
144 if (IS_ERR(gpios->gpio[i]))
Fabio Estevam13bc2bb2015-03-10 12:23:18 -0300145 return ERR_CAST(gpios->gpio[i]);
Richard Genoud84130aa2014-05-13 20:20:43 +0200146 }
147
148 return gpios;
149}
Uwe Kleine-König7d8c70d2015-09-30 10:19:40 +0200150EXPORT_SYMBOL_GPL(mctrl_gpio_init_noauto);
Richard Genoud84130aa2014-05-13 20:20:43 +0200151
Uwe Kleine-Königce59e482015-09-30 10:19:41 +0200152#define MCTRL_ANY_DELTA (TIOCM_RI | TIOCM_DSR | TIOCM_CD | TIOCM_CTS)
153static irqreturn_t mctrl_gpio_irq_handle(int irq, void *context)
154{
155 struct mctrl_gpios *gpios = context;
156 struct uart_port *port = gpios->port;
157 u32 mctrl = gpios->mctrl_prev;
158 u32 mctrl_diff;
Yegor Yefremovd11df6182016-04-29 10:45:07 +0200159 unsigned long flags;
Uwe Kleine-Königce59e482015-09-30 10:19:41 +0200160
161 mctrl_gpio_get(gpios, &mctrl);
162
Yegor Yefremovd11df6182016-04-29 10:45:07 +0200163 spin_lock_irqsave(&port->lock, flags);
164
Uwe Kleine-Königce59e482015-09-30 10:19:41 +0200165 mctrl_diff = mctrl ^ gpios->mctrl_prev;
166 gpios->mctrl_prev = mctrl;
167
168 if (mctrl_diff & MCTRL_ANY_DELTA && port->state != NULL) {
169 if ((mctrl_diff & mctrl) & TIOCM_RI)
170 port->icount.rng++;
171
172 if ((mctrl_diff & mctrl) & TIOCM_DSR)
173 port->icount.dsr++;
174
175 if (mctrl_diff & TIOCM_CD)
176 uart_handle_dcd_change(port, mctrl & TIOCM_CD);
177
178 if (mctrl_diff & TIOCM_CTS)
179 uart_handle_cts_change(port, mctrl & TIOCM_CTS);
180
181 wake_up_interruptible(&port->state->port.delta_msr_wait);
182 }
183
Yegor Yefremovd11df6182016-04-29 10:45:07 +0200184 spin_unlock_irqrestore(&port->lock, flags);
185
Uwe Kleine-Königce59e482015-09-30 10:19:41 +0200186 return IRQ_HANDLED;
187}
188
189struct mctrl_gpios *mctrl_gpio_init(struct uart_port *port, unsigned int idx)
190{
191 struct mctrl_gpios *gpios;
192 enum mctrl_gpio_idx i;
193
194 gpios = mctrl_gpio_init_noauto(port->dev, idx);
195 if (IS_ERR(gpios))
196 return gpios;
197
198 gpios->port = port;
199
200 for (i = 0; i < UART_GPIO_MAX; ++i) {
201 int ret;
202
203 if (!gpios->gpio[i] || mctrl_gpios_desc[i].dir_out)
204 continue;
205
206 ret = gpiod_to_irq(gpios->gpio[i]);
207 if (ret <= 0) {
208 dev_err(port->dev,
209 "failed to find corresponding irq for %s (idx=%d, err=%d)\n",
210 mctrl_gpios_desc[i].name, idx, ret);
211 return ERR_PTR(ret);
212 }
213 gpios->irq[i] = ret;
214
215 /* irqs should only be enabled in .enable_ms */
216 irq_set_status_flags(gpios->irq[i], IRQ_NOAUTOEN);
217
218 ret = devm_request_irq(port->dev, gpios->irq[i],
219 mctrl_gpio_irq_handle,
220 IRQ_TYPE_EDGE_BOTH, dev_name(port->dev),
221 gpios);
222 if (ret) {
223 /* alternatively implement polling */
224 dev_err(port->dev,
225 "failed to request irq for %s (idx=%d, err=%d)\n",
226 mctrl_gpios_desc[i].name, idx, ret);
227 return ERR_PTR(ret);
228 }
229 }
230
231 return gpios;
232}
Uwe Kleine-König4f71a2e2015-12-13 11:30:02 +0100233EXPORT_SYMBOL_GPL(mctrl_gpio_init);
Uwe Kleine-Königce59e482015-09-30 10:19:41 +0200234
Richard Genoud84130aa2014-05-13 20:20:43 +0200235void mctrl_gpio_free(struct device *dev, struct mctrl_gpios *gpios)
236{
237 enum mctrl_gpio_idx i;
238
Yegor Yefremov434be0a2016-05-31 10:59:17 +0200239 if (gpios == NULL)
240 return;
241
Uwe Kleine-Königce59e482015-09-30 10:19:41 +0200242 for (i = 0; i < UART_GPIO_MAX; i++) {
243 if (gpios->irq[i])
244 devm_free_irq(gpios->port->dev, gpios->irq[i], gpios);
245
Uwe Kleine-König445df7f2015-05-19 21:56:29 +0200246 if (gpios->gpio[i])
Richard Genoud84130aa2014-05-13 20:20:43 +0200247 devm_gpiod_put(dev, gpios->gpio[i]);
Uwe Kleine-Königce59e482015-09-30 10:19:41 +0200248 }
Richard Genoud84130aa2014-05-13 20:20:43 +0200249 devm_kfree(dev, gpios);
250}
251EXPORT_SYMBOL_GPL(mctrl_gpio_free);
Uwe Kleine-Königce59e482015-09-30 10:19:41 +0200252
253void mctrl_gpio_enable_ms(struct mctrl_gpios *gpios)
254{
255 enum mctrl_gpio_idx i;
256
Yegor Yefremov434be0a2016-05-31 10:59:17 +0200257 if (gpios == NULL)
258 return;
259
Uwe Kleine-Königce59e482015-09-30 10:19:41 +0200260 /* .enable_ms may be called multiple times */
261 if (gpios->mctrl_on)
262 return;
263
264 gpios->mctrl_on = true;
265
266 /* get initial status of modem lines GPIOs */
267 mctrl_gpio_get(gpios, &gpios->mctrl_prev);
268
269 for (i = 0; i < UART_GPIO_MAX; ++i) {
270 if (!gpios->irq[i])
271 continue;
272
273 enable_irq(gpios->irq[i]);
274 }
275}
276EXPORT_SYMBOL_GPL(mctrl_gpio_enable_ms);
277
278void mctrl_gpio_disable_ms(struct mctrl_gpios *gpios)
279{
280 enum mctrl_gpio_idx i;
281
Yegor Yefremov434be0a2016-05-31 10:59:17 +0200282 if (gpios == NULL)
283 return;
284
Uwe Kleine-Königce59e482015-09-30 10:19:41 +0200285 if (!gpios->mctrl_on)
286 return;
287
288 gpios->mctrl_on = false;
289
290 for (i = 0; i < UART_GPIO_MAX; ++i) {
291 if (!gpios->irq[i])
292 continue;
293
294 disable_irq(gpios->irq[i]);
295 }
296}
Uwe Kleine-König4f71a2e2015-12-13 11:30:02 +0100297EXPORT_SYMBOL_GPL(mctrl_gpio_disable_ms);
Romain Izard82a3f872016-02-23 15:54:54 +0100298
299MODULE_LICENSE("GPL");