xref: /linux/drivers/tty/serial/serial_mctrl_gpio.c (revision 13bc2bb9a0075342707e853e6dac298f0bbca111)
184130aacSRichard Genoud /*
284130aacSRichard Genoud  * Helpers for controlling modem lines via GPIO
384130aacSRichard Genoud  *
484130aacSRichard Genoud  * Copyright (C) 2014 Paratronic S.A.
584130aacSRichard Genoud  *
684130aacSRichard Genoud  * This program is free software; you can redistribute it and/or modify
784130aacSRichard Genoud  * it under the terms of the GNU General Public License as published by
884130aacSRichard Genoud  * the Free Software Foundation; either version 2 of the License, or
984130aacSRichard Genoud  * (at your option) any later version.
1084130aacSRichard Genoud  *
1184130aacSRichard Genoud  * This program is distributed in the hope that it will be useful,
1284130aacSRichard Genoud  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1384130aacSRichard Genoud  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1484130aacSRichard Genoud  * GNU General Public License for more details.
1584130aacSRichard Genoud  *
1684130aacSRichard Genoud  */
1784130aacSRichard Genoud 
1884130aacSRichard Genoud #include <linux/err.h>
1984130aacSRichard Genoud #include <linux/device.h>
2084130aacSRichard Genoud #include <linux/gpio/consumer.h>
2193b88774SAlexander Shiyan #include <linux/termios.h>
2284130aacSRichard Genoud 
2384130aacSRichard Genoud #include "serial_mctrl_gpio.h"
2484130aacSRichard Genoud 
2584130aacSRichard Genoud struct mctrl_gpios {
2684130aacSRichard Genoud 	struct gpio_desc *gpio[UART_GPIO_MAX];
2784130aacSRichard Genoud };
2884130aacSRichard Genoud 
2984130aacSRichard Genoud static const struct {
3084130aacSRichard Genoud 	const char *name;
3184130aacSRichard Genoud 	unsigned int mctrl;
3284130aacSRichard Genoud 	bool dir_out;
3384130aacSRichard Genoud } mctrl_gpios_desc[UART_GPIO_MAX] = {
3484130aacSRichard Genoud 	{ "cts", TIOCM_CTS, false, },
3584130aacSRichard Genoud 	{ "dsr", TIOCM_DSR, false, },
3684130aacSRichard Genoud 	{ "dcd", TIOCM_CD, false, },
3784130aacSRichard Genoud 	{ "rng", TIOCM_RNG, false, },
3884130aacSRichard Genoud 	{ "rts", TIOCM_RTS, true, },
3984130aacSRichard Genoud 	{ "dtr", TIOCM_DTR, true, },
4084130aacSRichard Genoud 	{ "out1", TIOCM_OUT1, true, },
4184130aacSRichard Genoud 	{ "out2", TIOCM_OUT2, true, },
4284130aacSRichard Genoud };
4384130aacSRichard Genoud 
4484130aacSRichard Genoud void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl)
4584130aacSRichard Genoud {
4684130aacSRichard Genoud 	enum mctrl_gpio_idx i;
47834296a3SRojhalat Ibrahim 	struct gpio_desc *desc_array[UART_GPIO_MAX];
48834296a3SRojhalat Ibrahim 	int value_array[UART_GPIO_MAX];
49834296a3SRojhalat Ibrahim 	unsigned int count = 0;
5084130aacSRichard Genoud 
5184130aacSRichard Genoud 	for (i = 0; i < UART_GPIO_MAX; i++)
5284130aacSRichard Genoud 		if (!IS_ERR_OR_NULL(gpios->gpio[i]) &&
53834296a3SRojhalat Ibrahim 		    mctrl_gpios_desc[i].dir_out) {
54834296a3SRojhalat Ibrahim 			desc_array[count] = gpios->gpio[i];
55834296a3SRojhalat Ibrahim 			value_array[count] = !!(mctrl & mctrl_gpios_desc[i].mctrl);
56834296a3SRojhalat Ibrahim 			count++;
57834296a3SRojhalat Ibrahim 		}
58834296a3SRojhalat Ibrahim 	gpiod_set_array(count, desc_array, value_array);
5984130aacSRichard Genoud }
6084130aacSRichard Genoud EXPORT_SYMBOL_GPL(mctrl_gpio_set);
6184130aacSRichard Genoud 
6284130aacSRichard Genoud struct gpio_desc *mctrl_gpio_to_gpiod(struct mctrl_gpios *gpios,
6384130aacSRichard Genoud 				      enum mctrl_gpio_idx gidx)
6484130aacSRichard Genoud {
6584130aacSRichard Genoud 	return gpios->gpio[gidx];
6684130aacSRichard Genoud }
6784130aacSRichard Genoud EXPORT_SYMBOL_GPL(mctrl_gpio_to_gpiod);
6884130aacSRichard Genoud 
6984130aacSRichard Genoud unsigned int mctrl_gpio_get(struct mctrl_gpios *gpios, unsigned int *mctrl)
7084130aacSRichard Genoud {
7184130aacSRichard Genoud 	enum mctrl_gpio_idx i;
7284130aacSRichard Genoud 
7384130aacSRichard Genoud 	for (i = 0; i < UART_GPIO_MAX; i++) {
749e9f079cSUwe Kleine-König 		if (gpios->gpio[i] && !mctrl_gpios_desc[i].dir_out) {
7584130aacSRichard Genoud 			if (gpiod_get_value(gpios->gpio[i]))
7684130aacSRichard Genoud 				*mctrl |= mctrl_gpios_desc[i].mctrl;
7784130aacSRichard Genoud 			else
7884130aacSRichard Genoud 				*mctrl &= ~mctrl_gpios_desc[i].mctrl;
7984130aacSRichard Genoud 		}
8084130aacSRichard Genoud 	}
8184130aacSRichard Genoud 
8284130aacSRichard Genoud 	return *mctrl;
8384130aacSRichard Genoud }
8484130aacSRichard Genoud EXPORT_SYMBOL_GPL(mctrl_gpio_get);
8584130aacSRichard Genoud 
8684130aacSRichard Genoud struct mctrl_gpios *mctrl_gpio_init(struct device *dev, unsigned int idx)
8784130aacSRichard Genoud {
8884130aacSRichard Genoud 	struct mctrl_gpios *gpios;
8984130aacSRichard Genoud 	enum mctrl_gpio_idx i;
9084130aacSRichard Genoud 
9184130aacSRichard Genoud 	gpios = devm_kzalloc(dev, sizeof(*gpios), GFP_KERNEL);
9284130aacSRichard Genoud 	if (!gpios)
9384130aacSRichard Genoud 		return ERR_PTR(-ENOMEM);
9484130aacSRichard Genoud 
9584130aacSRichard Genoud 	for (i = 0; i < UART_GPIO_MAX; i++) {
961d267ea6SUwe Kleine-König 		enum gpiod_flags flags;
9784130aacSRichard Genoud 
9884130aacSRichard Genoud 		if (mctrl_gpios_desc[i].dir_out)
991d267ea6SUwe Kleine-König 			flags = GPIOD_OUT_LOW;
10084130aacSRichard Genoud 		else
1011d267ea6SUwe Kleine-König 			flags = GPIOD_IN;
1021d267ea6SUwe Kleine-König 
1031d267ea6SUwe Kleine-König 		gpios->gpio[i] =
1041d267ea6SUwe Kleine-König 			devm_gpiod_get_index_optional(dev,
1051d267ea6SUwe Kleine-König 						      mctrl_gpios_desc[i].name,
1061d267ea6SUwe Kleine-König 						      idx, flags);
1071d267ea6SUwe Kleine-König 
1081d267ea6SUwe Kleine-König 		if (IS_ERR(gpios->gpio[i]))
109*13bc2bb9SFabio Estevam 			return ERR_CAST(gpios->gpio[i]);
11084130aacSRichard Genoud 	}
11184130aacSRichard Genoud 
11284130aacSRichard Genoud 	return gpios;
11384130aacSRichard Genoud }
11484130aacSRichard Genoud EXPORT_SYMBOL_GPL(mctrl_gpio_init);
11584130aacSRichard Genoud 
11684130aacSRichard Genoud void mctrl_gpio_free(struct device *dev, struct mctrl_gpios *gpios)
11784130aacSRichard Genoud {
11884130aacSRichard Genoud 	enum mctrl_gpio_idx i;
11984130aacSRichard Genoud 
12084130aacSRichard Genoud 	for (i = 0; i < UART_GPIO_MAX; i++)
12184130aacSRichard Genoud 		if (!IS_ERR_OR_NULL(gpios->gpio[i]))
12284130aacSRichard Genoud 			devm_gpiod_put(dev, gpios->gpio[i]);
12384130aacSRichard Genoud 	devm_kfree(dev, gpios);
12484130aacSRichard Genoud }
12584130aacSRichard Genoud EXPORT_SYMBOL_GPL(mctrl_gpio_free);
126