diff options
-rw-r--r-- | Documentation/serial/driver | 25 | ||||
-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 |
5 files changed, 284 insertions, 0 deletions
diff --git a/Documentation/serial/driver b/Documentation/serial/driver index c3a7689a90e6..3bba1aeb799c 100644 --- a/Documentation/serial/driver +++ b/Documentation/serial/driver | |||
@@ -429,3 +429,28 @@ thus: | |||
429 | struct uart_port port; | 429 | struct uart_port port; |
430 | int my_stuff; | 430 | int my_stuff; |
431 | }; | 431 | }; |
432 | |||
433 | Modem control lines via GPIO | ||
434 | ---------------------------- | ||
435 | |||
436 | Some helpers are provided in order to set/get modem control lines via GPIO. | ||
437 | |||
438 | mctrl_gpio_init(dev, idx): | ||
439 | This will get the {cts,rts,...}-gpios from device tree if they are | ||
440 | present and request them, set direction etc, and return an | ||
441 | allocated structure. devm_* functions are used, so there's no need | ||
442 | to call mctrl_gpio_free(). | ||
443 | |||
444 | mctrl_gpio_free(dev, gpios): | ||
445 | This will free the requested gpios in mctrl_gpio_init(). | ||
446 | As devm_* function are used, there's generally no need to call | ||
447 | this function. | ||
448 | |||
449 | mctrl_gpio_to_gpiod(gpios, gidx) | ||
450 | This returns the gpio structure associated to the modem line index. | ||
451 | |||
452 | mctrl_gpio_set(gpios, mctrl): | ||
453 | This will sets the gpios according to the mctrl state. | ||
454 | |||
455 | mctrl_gpio_get(gpios, mctrl): | ||
456 | This will update mctrl with the gpios values. | ||
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 | ||