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