diff options
| author | Richard Genoud <richard.genoud@gmail.com> | 2014-05-13 14:20:43 -0400 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-05-28 15:49:14 -0400 |
| commit | 84130aace83989c1dba073ed98dad721d2060258 (patch) | |
| tree | e313e54a9276328a551825f58fc867aa02f8acf1 /drivers/tty | |
| parent | fa3909320c00286c6b9d6ade16bc6d44f940379c (diff) | |
tty/serial: Add GPIOLIB helpers for controlling modem lines
This patch add some helpers to control modem lines (CTS/RTS/DSR...) via
GPIO.
This will be useful for many boards which have a serial controller that
only handle CTS/RTS pins (or even just RX/TX).
Signed-off-by: Richard Genoud <richard.genoud@gmail.com>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Tested-by: Yegor Yefremov <yegorslists@googlemail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty')
| -rw-r--r-- | drivers/tty/serial/Kconfig | 3 | ||||
| -rw-r--r-- | drivers/tty/serial/Makefile | 3 | ||||
| -rw-r--r-- | drivers/tty/serial/serial_mctrl_gpio.c | 143 | ||||
| -rw-r--r-- | drivers/tty/serial/serial_mctrl_gpio.h | 110 |
4 files changed, 259 insertions, 0 deletions
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index d88c058634ff..4bf6d220357b 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig | |||
| @@ -1553,4 +1553,7 @@ config SERIAL_MEN_Z135 | |||
| 1553 | 1553 | ||
| 1554 | endmenu | 1554 | endmenu |
| 1555 | 1555 | ||
| 1556 | config SERIAL_MCTRL_GPIO | ||
| 1557 | tristate | ||
| 1558 | |||
| 1556 | endif # TTY | 1559 | endif # TTY |
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 712732b43917..0080cc362e09 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile | |||
| @@ -92,3 +92,6 @@ obj-$(CONFIG_SERIAL_ARC) += arc_uart.o | |||
| 92 | obj-$(CONFIG_SERIAL_RP2) += rp2.o | 92 | obj-$(CONFIG_SERIAL_RP2) += rp2.o |
| 93 | obj-$(CONFIG_SERIAL_FSL_LPUART) += fsl_lpuart.o | 93 | obj-$(CONFIG_SERIAL_FSL_LPUART) += fsl_lpuart.o |
| 94 | obj-$(CONFIG_SERIAL_MEN_Z135) += men_z135_uart.o | 94 | obj-$(CONFIG_SERIAL_MEN_Z135) += men_z135_uart.o |
| 95 | |||
| 96 | # GPIOLIB helpers for modem control lines | ||
| 97 | obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o | ||
diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c new file mode 100644 index 000000000000..bf9560ffe3f4 --- /dev/null +++ b/drivers/tty/serial/serial_mctrl_gpio.c | |||
| @@ -0,0 +1,143 @@ | |||
| 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 | |||
| 18 | #include <linux/err.h> | ||
| 19 | #include <linux/device.h> | ||
| 20 | #include <linux/gpio/consumer.h> | ||
| 21 | #include <uapi/asm-generic/termios.h> | ||
| 22 | |||
| 23 | #include "serial_mctrl_gpio.h" | ||
| 24 | |||
| 25 | struct mctrl_gpios { | ||
| 26 | struct gpio_desc *gpio[UART_GPIO_MAX]; | ||
| 27 | }; | ||
| 28 | |||
| 29 | static const struct { | ||
| 30 | const char *name; | ||
| 31 | unsigned int mctrl; | ||
| 32 | bool dir_out; | ||
| 33 | } mctrl_gpios_desc[UART_GPIO_MAX] = { | ||
| 34 | { "cts", TIOCM_CTS, false, }, | ||
| 35 | { "dsr", TIOCM_DSR, false, }, | ||
| 36 | { "dcd", TIOCM_CD, false, }, | ||
| 37 | { "rng", TIOCM_RNG, false, }, | ||
| 38 | { "rts", TIOCM_RTS, true, }, | ||
| 39 | { "dtr", TIOCM_DTR, true, }, | ||
| 40 | { "out1", TIOCM_OUT1, true, }, | ||
| 41 | { "out2", TIOCM_OUT2, true, }, | ||
| 42 | }; | ||
| 43 | |||
| 44 | void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl) | ||
| 45 | { | ||
| 46 | enum mctrl_gpio_idx i; | ||
| 47 | |||
| 48 | if (IS_ERR_OR_NULL(gpios)) | ||
| 49 | return; | ||
| 50 | |||
| 51 | for (i = 0; i < UART_GPIO_MAX; i++) | ||
| 52 | if (!IS_ERR_OR_NULL(gpios->gpio[i]) && | ||
| 53 | mctrl_gpios_desc[i].dir_out) | ||
| 54 | gpiod_set_value(gpios->gpio[i], | ||
| 55 | !!(mctrl & mctrl_gpios_desc[i].mctrl)); | ||
| 56 | } | ||
| 57 | EXPORT_SYMBOL_GPL(mctrl_gpio_set); | ||
| 58 | |||
| 59 | struct gpio_desc *mctrl_gpio_to_gpiod(struct mctrl_gpios *gpios, | ||
| 60 | enum mctrl_gpio_idx gidx) | ||
| 61 | { | ||
| 62 | if (!IS_ERR_OR_NULL(gpios) && !IS_ERR_OR_NULL(gpios->gpio[gidx])) | ||
| 63 | return gpios->gpio[gidx]; | ||
| 64 | else | ||
| 65 | return NULL; | ||
| 66 | } | ||
| 67 | EXPORT_SYMBOL_GPL(mctrl_gpio_to_gpiod); | ||
| 68 | |||
| 69 | unsigned int mctrl_gpio_get(struct mctrl_gpios *gpios, unsigned int *mctrl) | ||
| 70 | { | ||
| 71 | enum mctrl_gpio_idx i; | ||
| 72 | |||
| 73 | /* | ||
| 74 | * return it unchanged if the structure is not allocated | ||
| 75 | */ | ||
| 76 | if (IS_ERR_OR_NULL(gpios)) | ||
| 77 | return *mctrl; | ||
| 78 | |||
| 79 | for (i = 0; i < UART_GPIO_MAX; i++) { | ||
| 80 | if (!IS_ERR_OR_NULL(gpios->gpio[i]) && | ||
| 81 | !mctrl_gpios_desc[i].dir_out) { | ||
| 82 | if (gpiod_get_value(gpios->gpio[i])) | ||
| 83 | *mctrl |= mctrl_gpios_desc[i].mctrl; | ||
| 84 | else | ||
| 85 | *mctrl &= ~mctrl_gpios_desc[i].mctrl; | ||
| 86 | } | ||
| 87 | } | ||
| 88 | |||
| 89 | return *mctrl; | ||
| 90 | } | ||
| 91 | EXPORT_SYMBOL_GPL(mctrl_gpio_get); | ||
| 92 | |||
| 93 | struct mctrl_gpios *mctrl_gpio_init(struct device *dev, unsigned int idx) | ||
| 94 | { | ||
| 95 | struct mctrl_gpios *gpios; | ||
| 96 | enum mctrl_gpio_idx i; | ||
| 97 | int err; | ||
| 98 | |||
| 99 | gpios = devm_kzalloc(dev, sizeof(*gpios), GFP_KERNEL); | ||
| 100 | if (!gpios) | ||
| 101 | return ERR_PTR(-ENOMEM); | ||
| 102 | |||
| 103 | for (i = 0; i < UART_GPIO_MAX; i++) { | ||
| 104 | gpios->gpio[i] = devm_gpiod_get_index(dev, | ||
| 105 | mctrl_gpios_desc[i].name, | ||
| 106 | idx); | ||
| 107 | |||
| 108 | /* | ||
| 109 | * The GPIOs are maybe not all filled, | ||
| 110 | * this is not an error. | ||
| 111 | */ | ||
| 112 | if (IS_ERR_OR_NULL(gpios->gpio[i])) | ||
| 113 | continue; | ||
| 114 | |||
| 115 | if (mctrl_gpios_desc[i].dir_out) | ||
| 116 | err = gpiod_direction_output(gpios->gpio[i], 0); | ||
| 117 | else | ||
| 118 | err = gpiod_direction_input(gpios->gpio[i]); | ||
| 119 | if (err) { | ||
| 120 | dev_dbg(dev, "Unable to set direction for %s GPIO", | ||
| 121 | mctrl_gpios_desc[i].name); | ||
| 122 | devm_gpiod_put(dev, gpios->gpio[i]); | ||
| 123 | gpios->gpio[i] = NULL; | ||
| 124 | } | ||
| 125 | } | ||
| 126 | |||
| 127 | return gpios; | ||
| 128 | } | ||
| 129 | EXPORT_SYMBOL_GPL(mctrl_gpio_init); | ||
| 130 | |||
| 131 | void mctrl_gpio_free(struct device *dev, struct mctrl_gpios *gpios) | ||
| 132 | { | ||
| 133 | enum mctrl_gpio_idx i; | ||
| 134 | |||
| 135 | if (IS_ERR_OR_NULL(gpios)) | ||
| 136 | return; | ||
| 137 | |||
| 138 | for (i = 0; i < UART_GPIO_MAX; i++) | ||
| 139 | if (!IS_ERR_OR_NULL(gpios->gpio[i])) | ||
| 140 | devm_gpiod_put(dev, gpios->gpio[i]); | ||
| 141 | devm_kfree(dev, gpios); | ||
| 142 | } | ||
| 143 | EXPORT_SYMBOL_GPL(mctrl_gpio_free); | ||
diff --git a/drivers/tty/serial/serial_mctrl_gpio.h b/drivers/tty/serial/serial_mctrl_gpio.h new file mode 100644 index 000000000000..400ba0494a17 --- /dev/null +++ b/drivers/tty/serial/serial_mctrl_gpio.h | |||
| @@ -0,0 +1,110 @@ | |||
| 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 | |||
| 18 | #ifndef __SERIAL_MCTRL_GPIO__ | ||
| 19 | #define __SERIAL_MCTRL_GPIO__ | ||
| 20 | |||
| 21 | #include <linux/err.h> | ||
| 22 | #include <linux/device.h> | ||
| 23 | #include <linux/gpio/consumer.h> | ||
| 24 | |||
| 25 | enum mctrl_gpio_idx { | ||
| 26 | UART_GPIO_CTS, | ||
| 27 | UART_GPIO_DSR, | ||
| 28 | UART_GPIO_DCD, | ||
| 29 | UART_GPIO_RNG, | ||
| 30 | UART_GPIO_RI = UART_GPIO_RNG, | ||
| 31 | UART_GPIO_RTS, | ||
| 32 | UART_GPIO_DTR, | ||
| 33 | UART_GPIO_OUT1, | ||
| 34 | UART_GPIO_OUT2, | ||
| 35 | UART_GPIO_MAX, | ||
| 36 | }; | ||
| 37 | |||
| 38 | /* | ||
| 39 | * Opaque descriptor for modem lines controlled by GPIOs | ||
| 40 | */ | ||
| 41 | struct mctrl_gpios; | ||
| 42 | |||
| 43 | #ifdef CONFIG_GPIOLIB | ||
| 44 | |||
| 45 | /* | ||
| 46 | * Set state of the modem control output lines via GPIOs. | ||
| 47 | */ | ||
| 48 | void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl); | ||
| 49 | |||
| 50 | /* | ||
| 51 | * Get state of the modem control output lines from GPIOs. | ||
| 52 | * The mctrl flags are updated and returned. | ||
| 53 | */ | ||
| 54 | unsigned int mctrl_gpio_get(struct mctrl_gpios *gpios, unsigned int *mctrl); | ||
| 55 | |||
| 56 | /* | ||
| 57 | * Returns the associated struct gpio_desc to the modem line gidx | ||
| 58 | */ | ||
| 59 | struct gpio_desc *mctrl_gpio_to_gpiod(struct mctrl_gpios *gpios, | ||
| 60 | enum mctrl_gpio_idx gidx); | ||
| 61 | |||
| 62 | /* | ||
| 63 | * Request and set direction of modem control lines GPIOs. | ||
| 64 | * devm_* functions are used, so there's no need to call mctrl_gpio_free(). | ||
| 65 | * Returns a pointer to the allocated mctrl structure if ok, -ENOMEM on | ||
| 66 | * allocation error. | ||
| 67 | */ | ||
| 68 | struct mctrl_gpios *mctrl_gpio_init(struct device *dev, unsigned int idx); | ||
| 69 | |||
| 70 | /* | ||
| 71 | * Free the mctrl_gpios structure. | ||
| 72 | * Normally, this function will not be called, as the GPIOs will | ||
| 73 | * be disposed of by the resource management code. | ||
| 74 | */ | ||
| 75 | void mctrl_gpio_free(struct device *dev, struct mctrl_gpios *gpios); | ||
| 76 | |||
| 77 | #else /* GPIOLIB */ | ||
| 78 | |||
| 79 | static inline | ||
| 80 | void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl) | ||
| 81 | { | ||
| 82 | } | ||
| 83 | |||
| 84 | static inline | ||
| 85 | unsigned int mctrl_gpio_get(struct mctrl_gpios *gpios, unsigned int *mctrl) | ||
| 86 | { | ||
| 87 | return *mctrl; | ||
| 88 | } | ||
| 89 | |||
| 90 | static inline | ||
| 91 | struct gpio_desc *mctrl_gpio_to_gpiod(struct mctrl_gpios *gpios, | ||
| 92 | enum mctrl_gpio_idx gidx) | ||
| 93 | { | ||
| 94 | return ERR_PTR(-ENOSYS); | ||
| 95 | } | ||
| 96 | |||
| 97 | static inline | ||
| 98 | struct mctrl_gpios *mctrl_gpio_init(struct device *dev, unsigned int idx) | ||
| 99 | { | ||
| 100 | return ERR_PTR(-ENOSYS); | ||
| 101 | } | ||
| 102 | |||
| 103 | static inline | ||
| 104 | void mctrl_gpio_free(struct device *dev, struct mctrl_gpios *gpios) | ||
| 105 | { | ||
| 106 | } | ||
| 107 | |||
| 108 | #endif /* GPIOLIB */ | ||
| 109 | |||
| 110 | #endif | ||
