* [PATCH v2 0/4] tty/serial: Add helpers to use GPIOs to control modem lines
@ 2014-02-11 17:45 Richard Genoud
2014-02-11 17:45 ` [PATCH v2 1/4] tty/serial: Add GPIOLIB helpers for controlling " Richard Genoud
` (4 more replies)
0 siblings, 5 replies; 8+ messages in thread
From: Richard Genoud @ 2014-02-11 17:45 UTC (permalink / raw)
To: linux-arm-kernel
The USART controller on sam9x5 chips (and also all AT91/SAMA5 chips
but at91rm9200) are not capable of handling DTR/DSR/DCD/RI signal.
Moreover, even if the controller can handle CTS/RTS, the dedicated
CTS/RTS pins are already muxed for other peripherals (LCDC/EMAC/MMC).
So this patchset adds the possibility to control those lines via GPIO,
as it is done for RTS in the patch "switch atmel serial to use gpiolib"
As it was suggested by Alexander Shiyan, I made that available for
every board.
This is based on 3.14-rc2 + Linus Walleij/Nicolas Ferre's patch:
354e57f3a0a2 ARM/serial: at91: switch atmel serial to use gpiolib
(in Uwe's tree git://git.pengutronix.de/git/ukl/linux.git dropmachtimexh )
and Philipp Zabel's patch:
gpiolib: make gpiod_direction_output take a logical value, add gpiod_direction_output_raw
(there won't be a merge conflict if this last patch is not present, the
gpios will "just" be on the wrong direction.)
Tested on at91sam9g35, with a null modem cable between 2 serial ports,
one with CTS/RTS controlled by the USART controller, the other via GPIO,
full duplex transfers.
Did some tests also with null modem cables on a PC.
Updates from v2:
- Instead of controlling modem signal only on atmel board, the
code is now available for every board.
- The active low flag from device tree is now used.
Richard Genoud (4):
tty/serial: Add GPIOLIB helpers for controlling modem lines
tty/serial: at91: use dev_err instead of printk
tty/serial: at91: remove unused open/close hooks
tty/serial: at91: use mctrl_gpio helpers
.../devicetree/bindings/serial/atmel-usart.txt | 12 +-
Documentation/serial/driver | 21 ++
arch/arm/mach-at91/at91rm9200_devices.c | 25 ++
arch/arm/mach-at91/at91sam9260_devices.c | 35 +++
arch/arm/mach-at91/at91sam9261_devices.c | 20 ++
arch/arm/mach-at91/at91sam9263_devices.c | 20 ++
arch/arm/mach-at91/at91sam9g45_devices.c | 25 ++
arch/arm/mach-at91/at91sam9rl_devices.c | 25 ++
drivers/tty/serial/Kconfig | 4 +
drivers/tty/serial/Makefile | 3 +
drivers/tty/serial/atmel_serial.c | 313 +++++++++++++++++----
drivers/tty/serial/serial_mctrl_gpio.c | 113 ++++++++
drivers/tty/serial/serial_mctrl_gpio.h | 56 ++++
include/linux/platform_data/atmel.h | 5 +
14 files changed, 615 insertions(+), 62 deletions(-)
create mode 100644 drivers/tty/serial/serial_mctrl_gpio.c
create mode 100644 drivers/tty/serial/serial_mctrl_gpio.h
--
1.8.5
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v2 1/4] tty/serial: Add GPIOLIB helpers for controlling modem lines
2014-02-11 17:45 [PATCH v2 0/4] tty/serial: Add helpers to use GPIOs to control modem lines Richard Genoud
@ 2014-02-11 17:45 ` Richard Genoud
2014-02-11 18:23 ` Alexander Shiyan
2014-02-11 17:45 ` [PATCH v2 2/4] tty/serial: at91: use dev_err instead of printk Richard Genoud
` (3 subsequent siblings)
4 siblings, 1 reply; 8+ messages in thread
From: Richard Genoud @ 2014-02-11 17:45 UTC (permalink / raw)
To: linux-arm-kernel
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>
---
Documentation/serial/driver | 21 ++++++
drivers/tty/serial/Kconfig | 4 ++
drivers/tty/serial/Makefile | 3 +
drivers/tty/serial/serial_mctrl_gpio.c | 113 +++++++++++++++++++++++++++++++++
drivers/tty/serial/serial_mctrl_gpio.h | 56 ++++++++++++++++
5 files changed, 197 insertions(+)
create mode 100644 drivers/tty/serial/serial_mctrl_gpio.c
create mode 100644 drivers/tty/serial/serial_mctrl_gpio.h
diff --git a/Documentation/serial/driver b/Documentation/serial/driver
index c3a7689a90e6..b33f0f9e1b8e 100644
--- a/Documentation/serial/driver
+++ b/Documentation/serial/driver
@@ -429,3 +429,24 @@ thus:
struct uart_port port;
int my_stuff;
};
+
+Modem control lines via GPIO
+----------------------------
+
+Some helpers are provided in order to set/get modem control lines via GPIO.
+
+mctrl_gpio_init(dev, gpios):
+ This will get the {cts,rts,...}-gpios from device tree if they are
+ present and request them, set direction etc.
+
+mctrl_gpio_free(gpios):
+ This will free the requested gpios in mctrl_gpio_init().
+
+mctrl_gpio_set(gpios, mctrl):
+ This will sets the gpios according to the mctrl state.
+
+mctrl_gpio_get(gpios, mctrl):
+ This will update mctrl with the gpios values.
+
+get_mctrl_gpio_name(mctrl_gpio_idx):
+ Returns a name associated to the GPIO modem line.
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index a3815eaed421..58c54d7eae53 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1509,4 +1509,8 @@ config SERIAL_ST_ASC_CONSOLE
endmenu
+config SERIAL_MCTRL_GPIO
+ def_bool y
+ depends on GPIOLIB
+
endif # TTY
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index 3680854fef41..bcf31da267dd 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -87,3 +87,6 @@ obj-$(CONFIG_SERIAL_EFM32_UART) += efm32-uart.o
obj-$(CONFIG_SERIAL_ARC) += arc_uart.o
obj-$(CONFIG_SERIAL_RP2) += rp2.o
obj-$(CONFIG_SERIAL_FSL_LPUART) += fsl_lpuart.o
+
+# GPIOLIB helpers for modem control lines
+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..ddcc7538f487
--- /dev/null
+++ b/drivers/tty/serial/serial_mctrl_gpio.c
@@ -0,0 +1,113 @@
+/*
+ * Helpers for controlling modem lines via GPIO
+ *
+ * Copyright (C) 2014 Paratronic S.A.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <uapi/asm-generic/termios.h>
+
+#include "serial_mctrl_gpio.h"
+
+static const char *mctrl_gpio_of_names[UART_GPIO_MAX] = {
+ "cts", "dsr", "dcd", "ri", "rts", "dtr"
+};
+
+const char *get_mctrl_gpio_name(enum mctrl_gpio_idx idx)
+{
+ return mctrl_gpio_of_names[idx];
+}
+EXPORT_SYMBOL_GPL(get_mctrl_gpio_name);
+
+void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl)
+{
+ if (!IS_ERR_OR_NULL(gpios->gpio[UART_GPIO_RTS]))
+ gpiod_set_value(gpios->gpio[UART_GPIO_RTS],
+ !!(mctrl & TIOCM_RTS));
+
+ if (!IS_ERR_OR_NULL(gpios->gpio[UART_GPIO_DTR]))
+ gpiod_set_value(gpios->gpio[UART_GPIO_DTR],
+ !!(mctrl & TIOCM_DTR));
+}
+EXPORT_SYMBOL_GPL(mctrl_gpio_set);
+
+unsigned int mctrl_gpio_get(struct mctrl_gpios *gpios, unsigned int *mctrl)
+{
+ if (!IS_ERR_OR_NULL(gpios->gpio[UART_GPIO_CTS])) {
+ if (gpiod_get_value(gpios->gpio[UART_GPIO_CTS]))
+ *mctrl |= TIOCM_CTS;
+ else
+ *mctrl &= ~TIOCM_CTS;
+ }
+
+ if (!IS_ERR_OR_NULL(gpios->gpio[UART_GPIO_DSR])) {
+ if (gpiod_get_value(gpios->gpio[UART_GPIO_DSR]))
+ *mctrl |= TIOCM_DSR;
+ else
+ *mctrl &= ~TIOCM_DSR;
+ }
+
+ if (!IS_ERR_OR_NULL(gpios->gpio[UART_GPIO_RI])) {
+ if (gpiod_get_value(gpios->gpio[UART_GPIO_RI]))
+ *mctrl |= TIOCM_RI;
+ else
+ *mctrl &= ~TIOCM_RI;
+ }
+
+ if (!IS_ERR_OR_NULL(gpios->gpio[UART_GPIO_DCD])) {
+ if (gpiod_get_value(gpios->gpio[UART_GPIO_DCD]))
+ *mctrl |= TIOCM_CD;
+ else
+ *mctrl &= ~TIOCM_CD;
+ }
+
+ return *mctrl;
+}
+EXPORT_SYMBOL_GPL(mctrl_gpio_get);
+
+int mctrl_gpio_init(struct device *dev, struct mctrl_gpios *gpios)
+{
+ enum mctrl_gpio_idx i;
+ int err = 0;
+ int ret = 0;
+
+ for (i = UART_GPIO_MIN; i < UART_GPIO_MAX; i++) {
+ gpios->gpio[i] = gpiod_get(dev, mctrl_gpio_of_names[i]);
+
+ /*
+ * The GPIOs are maybe not all filled,
+ * this is not an error.
+ */
+ if (IS_ERR_OR_NULL(gpios->gpio[i]))
+ continue;
+
+ if (i < UART_GPIO_MAX_INPUT)
+ err = gpiod_direction_input(gpios->gpio[i]);
+ else
+ err = gpiod_direction_output(gpios->gpio[i], 0);
+ if (err) {
+ dev_err(dev, "Unable to set direction for %s GPIO",
+ mctrl_gpio_of_names[i]);
+ gpiod_put(gpios->gpio[i]);
+ gpios->gpio[i] = NULL;
+ ret--;
+ }
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(mctrl_gpio_init);
+
+void mctrl_gpio_free(struct mctrl_gpios *gpios)
+{
+ enum mctrl_gpio_idx i;
+
+ for (i = UART_GPIO_MIN; i < UART_GPIO_MAX; i++)
+ if (!IS_ERR_OR_NULL(gpios->gpio[i])) {
+ gpiod_put(gpios->gpio[i]);
+ gpios->gpio[i] = NULL;
+ }
+}
+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..5b8be48f6442
--- /dev/null
+++ b/drivers/tty/serial/serial_mctrl_gpio.h
@@ -0,0 +1,56 @@
+/*
+ * Helpers for controlling modem lines via GPIO
+ *
+ * Copyright (C) 2014 Paratronic S.A.
+ *
+ */
+
+#ifndef __SERIAL_MCTRL_GPIO__
+#define __SERIAL_MCTRL_GPIO__
+
+#include <linux/gpio/consumer.h>
+
+enum mctrl_gpio_idx {
+ UART_GPIO_MIN = 0,
+ UART_GPIO_CTS = 0,
+ UART_GPIO_DSR = 1,
+ UART_GPIO_DCD = 2,
+ UART_GPIO_RI = 3,
+ UART_GPIO_MAX_INPUT = 4,
+ UART_GPIO_RTS = 4,
+ UART_GPIO_DTR = 5,
+ UART_GPIO_MAX,
+};
+
+struct mctrl_gpios {
+ struct gpio_desc *gpio[UART_GPIO_MAX];
+};
+
+/*
+ * Names of the modem lines (cts, dsr, dcd...)
+ */
+const char *get_mctrl_gpio_name(enum mctrl_gpio_idx idx);
+
+/*
+ * Set state of the modem control output lines via GPIOs.
+ */
+void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl);
+
+/*
+ * Get state of the modem control output lines from GPIOs.
+ * The mctrl flags are updated and returned.
+ */
+unsigned int mctrl_gpio_get(struct mctrl_gpios *gpios, unsigned int *mctrl);
+
+/*
+ * Request and set direction of modem control lines GPIOs.
+ * Returns 0 if ok, -nb_err if setting direction failed.
+ */
+int mctrl_gpio_init(struct device *dev, struct mctrl_gpios *gpios);
+
+/*
+ * Free all modem control lines GPIOs
+ */
+void mctrl_gpio_free(struct mctrl_gpios *gpios);
+
+#endif
--
1.8.5
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v2 2/4] tty/serial: at91: use dev_err instead of printk
2014-02-11 17:45 [PATCH v2 0/4] tty/serial: Add helpers to use GPIOs to control modem lines Richard Genoud
2014-02-11 17:45 ` [PATCH v2 1/4] tty/serial: Add GPIOLIB helpers for controlling " Richard Genoud
@ 2014-02-11 17:45 ` Richard Genoud
2014-02-11 17:45 ` [PATCH v2 3/4] tty/serial: at91: remove unused open/close hooks Richard Genoud
` (2 subsequent siblings)
4 siblings, 0 replies; 8+ messages in thread
From: Richard Genoud @ 2014-02-11 17:45 UTC (permalink / raw)
To: linux-arm-kernel
For better consistency.
Signed-off-by: Richard Genoud <richard.genoud@gmail.com>
---
drivers/tty/serial/atmel_serial.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 91c0d8839570..ed9621a21d67 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -1549,7 +1549,7 @@ static int atmel_startup(struct uart_port *port)
retval = request_irq(port->irq, atmel_interrupt, IRQF_SHARED,
tty ? tty->name : "atmel_serial", port);
if (retval) {
- printk("atmel_serial: atmel_startup - Can't get irq\n");
+ dev_err(port->dev, "atmel_startup - Can't get irq\n");
return retval;
}
@@ -1732,7 +1732,7 @@ static void atmel_serial_pm(struct uart_port *port, unsigned int state,
clk_disable_unprepare(atmel_port->clk);
break;
default:
- printk(KERN_ERR "atmel_serial: unknown pm %d\n", state);
+ dev_err(port->dev, "atmel_serial: unknown pm %d\n", state);
}
}
--
1.8.5
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v2 3/4] tty/serial: at91: remove unused open/close hooks
2014-02-11 17:45 [PATCH v2 0/4] tty/serial: Add helpers to use GPIOs to control modem lines Richard Genoud
2014-02-11 17:45 ` [PATCH v2 1/4] tty/serial: Add GPIOLIB helpers for controlling " Richard Genoud
2014-02-11 17:45 ` [PATCH v2 2/4] tty/serial: at91: use dev_err instead of printk Richard Genoud
@ 2014-02-11 17:45 ` Richard Genoud
2014-02-11 17:45 ` [PATCH v2 4/4] tty/serial: at91: use mctrl_gpio helpers Richard Genoud
2014-02-13 17:37 ` [PATCH v2 0/4] tty/serial: Add helpers to use GPIOs to control modem lines Greg Kroah-Hartman
4 siblings, 0 replies; 8+ messages in thread
From: Richard Genoud @ 2014-02-11 17:45 UTC (permalink / raw)
To: linux-arm-kernel
commit 95e629b761ce36996d1befe2824d5346b5a220b9 removed the use of board
specific hooks in serial_at91.h, so now, the open/close hook are just
dead code.
Signed-off-by: Richard Genoud <richard.genoud@gmail.com>
---
drivers/tty/serial/atmel_serial.c | 21 ---------------------
1 file changed, 21 deletions(-)
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index ed9621a21d67..a51b3a762948 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -112,9 +112,6 @@ static void atmel_stop_rx(struct uart_port *port);
#define UART_PUT_TCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_TCR)
#define UART_GET_TCR(port) __raw_readl((port)->membase + ATMEL_PDC_TCR)
-static int (*atmel_open_hook)(struct uart_port *);
-static void (*atmel_close_hook)(struct uart_port *);
-
struct atmel_dma_buffer {
unsigned char *buf;
dma_addr_t dma_addr;
@@ -1569,17 +1566,6 @@ static int atmel_startup(struct uart_port *port)
if (retval < 0)
atmel_set_ops(port);
}
- /*
- * If there is a specific "open" function (to register
- * control line interrupts)
- */
- if (atmel_open_hook) {
- retval = atmel_open_hook(port);
- if (retval) {
- free_irq(port->irq, port);
- return retval;
- }
- }
/* Save current CSR for comparison in atmel_tasklet_func() */
atmel_port->irq_status_prev = UART_GET_CSR(port);
@@ -1678,13 +1664,6 @@ static void atmel_shutdown(struct uart_port *port)
* Free the interrupt
*/
free_irq(port->irq, port);
-
- /*
- * If there is a specific "close" function (to unregister
- * control line interrupts)
- */
- if (atmel_close_hook)
- atmel_close_hook(port);
}
/*
--
1.8.5
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v2 4/4] tty/serial: at91: use mctrl_gpio helpers
2014-02-11 17:45 [PATCH v2 0/4] tty/serial: Add helpers to use GPIOs to control modem lines Richard Genoud
` (2 preceding siblings ...)
2014-02-11 17:45 ` [PATCH v2 3/4] tty/serial: at91: remove unused open/close hooks Richard Genoud
@ 2014-02-11 17:45 ` Richard Genoud
2014-02-13 17:37 ` [PATCH v2 0/4] tty/serial: Add helpers to use GPIOs to control modem lines Greg Kroah-Hartman
4 siblings, 0 replies; 8+ messages in thread
From: Richard Genoud @ 2014-02-11 17:45 UTC (permalink / raw)
To: linux-arm-kernel
On sam9x5, dedicated CTS (and RTS) pins are unusable together with the
LCDC, the EMAC, or the MMC because they share the same line.
Moreover, the USART controller doesn't handle DTR/DSR/DCD/RI signals,
so we have to control them via GPIO.
This patch permits to use GPIOs to control the CTS/RTS/DTR/DSR/DCD/RI
signals.
Signed-off-by: Richard Genoud <richard.genoud@gmail.com>
---
.../devicetree/bindings/serial/atmel-usart.txt | 12 +-
arch/arm/mach-at91/at91rm9200_devices.c | 25 ++
arch/arm/mach-at91/at91sam9260_devices.c | 35 +++
arch/arm/mach-at91/at91sam9261_devices.c | 20 ++
arch/arm/mach-at91/at91sam9263_devices.c | 20 ++
arch/arm/mach-at91/at91sam9g45_devices.c | 25 ++
arch/arm/mach-at91/at91sam9rl_devices.c | 25 ++
drivers/tty/serial/atmel_serial.c | 290 ++++++++++++++++++---
include/linux/platform_data/atmel.h | 5 +
9 files changed, 417 insertions(+), 40 deletions(-)
diff --git a/Documentation/devicetree/bindings/serial/atmel-usart.txt b/Documentation/devicetree/bindings/serial/atmel-usart.txt
index 17c1042b2df8..77edc6504a29 100644
--- a/Documentation/devicetree/bindings/serial/atmel-usart.txt
+++ b/Documentation/devicetree/bindings/serial/atmel-usart.txt
@@ -13,8 +13,9 @@ Required properties:
Optional properties:
- atmel,use-dma-rx: use of PDC or DMA for receiving data
- atmel,use-dma-tx: use of PDC or DMA for transmitting data
-- rts-gpios: specify a GPIO for RTS line. It will use specified PIO instead of the peripheral
- function pin for the USART RTS feature. If unsure, don't specify this property.
+- {rts,cts,dtr,dsr,ri,dcd}-gpios: specify a GPIO for RTS/CTS/DTR/DSR/RI/DCD line respectively.
+ It will use specified PIO instead of the peripheral function pin for the USART feature.
+ If unsure, don't specify this property.
- add dma bindings for dma transfer:
- dmas: DMA specifier, consisting of a phandle to DMA controller node,
memory peripheral interface and USART DMA channel ID, FIFO configuration.
@@ -35,7 +36,12 @@ Example:
clock-names = "usart";
atmel,use-dma-rx;
atmel,use-dma-tx;
- rts-gpios = <&pioD 15 0>;
+ rts-gpios = <&pioD 15 GPIO_ACTIVE_LOW>;
+ cts-gpios = <&pioD 16 GPIO_ACTIVE_LOW>;
+ dtr-gpios = <&pioD 17 GPIO_ACTIVE_LOW>;
+ dsr-gpios = <&pioD 18 GPIO_ACTIVE_LOW>;
+ dcd-gpios = <&pioD 20 GPIO_ACTIVE_LOW>;
+ ri-gpios = <&pioD 19 GPIO_ACTIVE_LOW>;
};
- use DMA:
diff --git a/arch/arm/mach-at91/at91rm9200_devices.c b/arch/arm/mach-at91/at91rm9200_devices.c
index 605add05af7e..288421f38b42 100644
--- a/arch/arm/mach-at91/at91rm9200_devices.c
+++ b/arch/arm/mach-at91/at91rm9200_devices.c
@@ -923,6 +923,11 @@ static struct atmel_uart_data dbgu_data = {
.use_dma_tx = 0,
.use_dma_rx = 0, /* DBGU not capable of receive DMA */
.rts_gpio = -EINVAL,
+ .cts_gpio = -EINVAL,
+ .dtr_gpio = -EINVAL,
+ .dsr_gpio = -EINVAL,
+ .ri_gpio = -EINVAL,
+ .dcd_gpio = -EINVAL,
};
static u64 dbgu_dmamask = DMA_BIT_MASK(32);
@@ -962,6 +967,11 @@ static struct atmel_uart_data uart0_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
.rts_gpio = -EINVAL,
+ .cts_gpio = -EINVAL,
+ .dtr_gpio = -EINVAL,
+ .dsr_gpio = -EINVAL,
+ .ri_gpio = -EINVAL,
+ .dcd_gpio = -EINVAL,
};
static u64 uart0_dmamask = DMA_BIT_MASK(32);
@@ -1013,6 +1023,11 @@ static struct atmel_uart_data uart1_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
.rts_gpio = -EINVAL,
+ .cts_gpio = -EINVAL,
+ .dtr_gpio = -EINVAL,
+ .dsr_gpio = -EINVAL,
+ .ri_gpio = -EINVAL,
+ .dcd_gpio = -EINVAL,
};
static u64 uart1_dmamask = DMA_BIT_MASK(32);
@@ -1065,6 +1080,11 @@ static struct atmel_uart_data uart2_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
.rts_gpio = -EINVAL,
+ .cts_gpio = -EINVAL,
+ .dtr_gpio = -EINVAL,
+ .dsr_gpio = -EINVAL,
+ .ri_gpio = -EINVAL,
+ .dcd_gpio = -EINVAL,
};
static u64 uart2_dmamask = DMA_BIT_MASK(32);
@@ -1109,6 +1129,11 @@ static struct atmel_uart_data uart3_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
.rts_gpio = -EINVAL,
+ .cts_gpio = -EINVAL,
+ .dtr_gpio = -EINVAL,
+ .dsr_gpio = -EINVAL,
+ .ri_gpio = -EINVAL,
+ .dcd_gpio = -EINVAL,
};
static u64 uart3_dmamask = DMA_BIT_MASK(32);
diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c
index b52527c78b12..0e20ba04d43f 100644
--- a/arch/arm/mach-at91/at91sam9260_devices.c
+++ b/arch/arm/mach-at91/at91sam9260_devices.c
@@ -820,6 +820,11 @@ static struct atmel_uart_data dbgu_data = {
.use_dma_tx = 0,
.use_dma_rx = 0, /* DBGU not capable of receive DMA */
.rts_gpio = -EINVAL,
+ .cts_gpio = -EINVAL,
+ .dtr_gpio = -EINVAL,
+ .dsr_gpio = -EINVAL,
+ .ri_gpio = -EINVAL,
+ .dcd_gpio = -EINVAL,
};
static u64 dbgu_dmamask = DMA_BIT_MASK(32);
@@ -859,6 +864,11 @@ static struct atmel_uart_data uart0_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
.rts_gpio = -EINVAL,
+ .cts_gpio = -EINVAL,
+ .dtr_gpio = -EINVAL,
+ .dsr_gpio = -EINVAL,
+ .ri_gpio = -EINVAL,
+ .dcd_gpio = -EINVAL,
};
static u64 uart0_dmamask = DMA_BIT_MASK(32);
@@ -911,6 +921,11 @@ static struct atmel_uart_data uart1_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
.rts_gpio = -EINVAL,
+ .cts_gpio = -EINVAL,
+ .dtr_gpio = -EINVAL,
+ .dsr_gpio = -EINVAL,
+ .ri_gpio = -EINVAL,
+ .dcd_gpio = -EINVAL,
};
static u64 uart1_dmamask = DMA_BIT_MASK(32);
@@ -955,6 +970,11 @@ static struct atmel_uart_data uart2_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
.rts_gpio = -EINVAL,
+ .cts_gpio = -EINVAL,
+ .dtr_gpio = -EINVAL,
+ .dsr_gpio = -EINVAL,
+ .ri_gpio = -EINVAL,
+ .dcd_gpio = -EINVAL,
};
static u64 uart2_dmamask = DMA_BIT_MASK(32);
@@ -999,6 +1019,11 @@ static struct atmel_uart_data uart3_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
.rts_gpio = -EINVAL,
+ .cts_gpio = -EINVAL,
+ .dtr_gpio = -EINVAL,
+ .dsr_gpio = -EINVAL,
+ .ri_gpio = -EINVAL,
+ .dcd_gpio = -EINVAL,
};
static u64 uart3_dmamask = DMA_BIT_MASK(32);
@@ -1043,6 +1068,11 @@ static struct atmel_uart_data uart4_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
.rts_gpio = -EINVAL,
+ .cts_gpio = -EINVAL,
+ .dtr_gpio = -EINVAL,
+ .dsr_gpio = -EINVAL,
+ .ri_gpio = -EINVAL,
+ .dcd_gpio = -EINVAL,
};
static u64 uart4_dmamask = DMA_BIT_MASK(32);
@@ -1082,6 +1112,11 @@ static struct atmel_uart_data uart5_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
.rts_gpio = -EINVAL,
+ .cts_gpio = -EINVAL,
+ .dtr_gpio = -EINVAL,
+ .dsr_gpio = -EINVAL,
+ .ri_gpio = -EINVAL,
+ .dcd_gpio = -EINVAL,
};
static u64 uart5_dmamask = DMA_BIT_MASK(32);
diff --git a/arch/arm/mach-at91/at91sam9261_devices.c b/arch/arm/mach-at91/at91sam9261_devices.c
index 6c1a2ecc306f..d3d7a546db9b 100644
--- a/arch/arm/mach-at91/at91sam9261_devices.c
+++ b/arch/arm/mach-at91/at91sam9261_devices.c
@@ -881,6 +881,11 @@ static struct atmel_uart_data dbgu_data = {
.use_dma_tx = 0,
.use_dma_rx = 0, /* DBGU not capable of receive DMA */
.rts_gpio = -EINVAL,
+ .cts_gpio = -EINVAL,
+ .dtr_gpio = -EINVAL,
+ .dsr_gpio = -EINVAL,
+ .ri_gpio = -EINVAL,
+ .dcd_gpio = -EINVAL,
};
static u64 dbgu_dmamask = DMA_BIT_MASK(32);
@@ -920,6 +925,11 @@ static struct atmel_uart_data uart0_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
.rts_gpio = -EINVAL,
+ .cts_gpio = -EINVAL,
+ .dtr_gpio = -EINVAL,
+ .dsr_gpio = -EINVAL,
+ .ri_gpio = -EINVAL,
+ .dcd_gpio = -EINVAL,
};
static u64 uart0_dmamask = DMA_BIT_MASK(32);
@@ -964,6 +974,11 @@ static struct atmel_uart_data uart1_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
.rts_gpio = -EINVAL,
+ .cts_gpio = -EINVAL,
+ .dtr_gpio = -EINVAL,
+ .dsr_gpio = -EINVAL,
+ .ri_gpio = -EINVAL,
+ .dcd_gpio = -EINVAL,
};
static u64 uart1_dmamask = DMA_BIT_MASK(32);
@@ -1008,6 +1023,11 @@ static struct atmel_uart_data uart2_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
.rts_gpio = -EINVAL,
+ .cts_gpio = -EINVAL,
+ .dtr_gpio = -EINVAL,
+ .dsr_gpio = -EINVAL,
+ .ri_gpio = -EINVAL,
+ .dcd_gpio = -EINVAL,
};
static u64 uart2_dmamask = DMA_BIT_MASK(32);
diff --git a/arch/arm/mach-at91/at91sam9263_devices.c b/arch/arm/mach-at91/at91sam9263_devices.c
index 97cc2a0d6f90..5fcb2a0383d1 100644
--- a/arch/arm/mach-at91/at91sam9263_devices.c
+++ b/arch/arm/mach-at91/at91sam9263_devices.c
@@ -1325,6 +1325,11 @@ static struct atmel_uart_data dbgu_data = {
.use_dma_tx = 0,
.use_dma_rx = 0, /* DBGU not capable of receive DMA */
.rts_gpio = -EINVAL,
+ .cts_gpio = -EINVAL,
+ .dtr_gpio = -EINVAL,
+ .dsr_gpio = -EINVAL,
+ .ri_gpio = -EINVAL,
+ .dcd_gpio = -EINVAL,
};
static u64 dbgu_dmamask = DMA_BIT_MASK(32);
@@ -1364,6 +1369,11 @@ static struct atmel_uart_data uart0_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
.rts_gpio = -EINVAL,
+ .cts_gpio = -EINVAL,
+ .dtr_gpio = -EINVAL,
+ .dsr_gpio = -EINVAL,
+ .ri_gpio = -EINVAL,
+ .dcd_gpio = -EINVAL,
};
static u64 uart0_dmamask = DMA_BIT_MASK(32);
@@ -1408,6 +1418,11 @@ static struct atmel_uart_data uart1_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
.rts_gpio = -EINVAL,
+ .cts_gpio = -EINVAL,
+ .dtr_gpio = -EINVAL,
+ .dsr_gpio = -EINVAL,
+ .ri_gpio = -EINVAL,
+ .dcd_gpio = -EINVAL,
};
static u64 uart1_dmamask = DMA_BIT_MASK(32);
@@ -1452,6 +1467,11 @@ static struct atmel_uart_data uart2_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
.rts_gpio = -EINVAL,
+ .cts_gpio = -EINVAL,
+ .dtr_gpio = -EINVAL,
+ .dsr_gpio = -EINVAL,
+ .ri_gpio = -EINVAL,
+ .dcd_gpio = -EINVAL,
};
static u64 uart2_dmamask = DMA_BIT_MASK(32);
diff --git a/arch/arm/mach-at91/at91sam9g45_devices.c b/arch/arm/mach-at91/at91sam9g45_devices.c
index c10149588e21..bd44970403e5 100644
--- a/arch/arm/mach-at91/at91sam9g45_devices.c
+++ b/arch/arm/mach-at91/at91sam9g45_devices.c
@@ -1588,6 +1588,11 @@ static struct atmel_uart_data dbgu_data = {
.use_dma_tx = 0,
.use_dma_rx = 0,
.rts_gpio = -EINVAL,
+ .cts_gpio = -EINVAL,
+ .dtr_gpio = -EINVAL,
+ .dsr_gpio = -EINVAL,
+ .ri_gpio = -EINVAL,
+ .dcd_gpio = -EINVAL,
};
static u64 dbgu_dmamask = DMA_BIT_MASK(32);
@@ -1627,6 +1632,11 @@ static struct atmel_uart_data uart0_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
.rts_gpio = -EINVAL,
+ .cts_gpio = -EINVAL,
+ .dtr_gpio = -EINVAL,
+ .dsr_gpio = -EINVAL,
+ .ri_gpio = -EINVAL,
+ .dcd_gpio = -EINVAL,
};
static u64 uart0_dmamask = DMA_BIT_MASK(32);
@@ -1671,6 +1681,11 @@ static struct atmel_uart_data uart1_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
.rts_gpio = -EINVAL,
+ .cts_gpio = -EINVAL,
+ .dtr_gpio = -EINVAL,
+ .dsr_gpio = -EINVAL,
+ .ri_gpio = -EINVAL,
+ .dcd_gpio = -EINVAL,
};
static u64 uart1_dmamask = DMA_BIT_MASK(32);
@@ -1715,6 +1730,11 @@ static struct atmel_uart_data uart2_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
.rts_gpio = -EINVAL,
+ .cts_gpio = -EINVAL,
+ .dtr_gpio = -EINVAL,
+ .dsr_gpio = -EINVAL,
+ .ri_gpio = -EINVAL,
+ .dcd_gpio = -EINVAL,
};
static u64 uart2_dmamask = DMA_BIT_MASK(32);
@@ -1759,6 +1779,11 @@ static struct atmel_uart_data uart3_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
.rts_gpio = -EINVAL,
+ .cts_gpio = -EINVAL,
+ .dtr_gpio = -EINVAL,
+ .dsr_gpio = -EINVAL,
+ .ri_gpio = -EINVAL,
+ .dcd_gpio = -EINVAL,
};
static u64 uart3_dmamask = DMA_BIT_MASK(32);
diff --git a/arch/arm/mach-at91/at91sam9rl_devices.c b/arch/arm/mach-at91/at91sam9rl_devices.c
index 4120af972b61..e43623dc1c9e 100644
--- a/arch/arm/mach-at91/at91sam9rl_devices.c
+++ b/arch/arm/mach-at91/at91sam9rl_devices.c
@@ -957,6 +957,11 @@ static struct atmel_uart_data dbgu_data = {
.use_dma_tx = 0,
.use_dma_rx = 0, /* DBGU not capable of receive DMA */
.rts_gpio = -EINVAL,
+ .cts_gpio = -EINVAL,
+ .dtr_gpio = -EINVAL,
+ .dsr_gpio = -EINVAL,
+ .ri_gpio = -EINVAL,
+ .dcd_gpio = -EINVAL,
};
static u64 dbgu_dmamask = DMA_BIT_MASK(32);
@@ -996,6 +1001,11 @@ static struct atmel_uart_data uart0_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
.rts_gpio = -EINVAL,
+ .cts_gpio = -EINVAL,
+ .dtr_gpio = -EINVAL,
+ .dsr_gpio = -EINVAL,
+ .ri_gpio = -EINVAL,
+ .dcd_gpio = -EINVAL,
};
static u64 uart0_dmamask = DMA_BIT_MASK(32);
@@ -1048,6 +1058,11 @@ static struct atmel_uart_data uart1_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
.rts_gpio = -EINVAL,
+ .cts_gpio = -EINVAL,
+ .dtr_gpio = -EINVAL,
+ .dsr_gpio = -EINVAL,
+ .ri_gpio = -EINVAL,
+ .dcd_gpio = -EINVAL,
};
static u64 uart1_dmamask = DMA_BIT_MASK(32);
@@ -1092,6 +1107,11 @@ static struct atmel_uart_data uart2_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
.rts_gpio = -EINVAL,
+ .cts_gpio = -EINVAL,
+ .dtr_gpio = -EINVAL,
+ .dsr_gpio = -EINVAL,
+ .ri_gpio = -EINVAL,
+ .dcd_gpio = -EINVAL,
};
static u64 uart2_dmamask = DMA_BIT_MASK(32);
@@ -1136,6 +1156,11 @@ static struct atmel_uart_data uart3_data = {
.use_dma_tx = 1,
.use_dma_rx = 1,
.rts_gpio = -EINVAL,
+ .cts_gpio = -EINVAL,
+ .dtr_gpio = -EINVAL,
+ .dsr_gpio = -EINVAL,
+ .ri_gpio = -EINVAL,
+ .dcd_gpio = -EINVAL,
};
static u64 uart3_dmamask = DMA_BIT_MASK(32);
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index a51b3a762948..7cb6d1954062 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -43,6 +43,8 @@
#include <linux/platform_data/atmel.h>
#include <linux/timer.h>
#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/irq.h>
#include <asm/io.h>
#include <asm/ioctls.h>
@@ -57,6 +59,8 @@
#include <linux/serial_core.h>
+#include "serial_mctrl_gpio.h"
+
static void atmel_start_rx(struct uart_port *port);
static void atmel_stop_rx(struct uart_port *port);
@@ -162,8 +166,10 @@ struct atmel_uart_port {
struct circ_buf rx_ring;
struct serial_rs485 rs485; /* rs485 settings */
- int rts_gpio; /* optional RTS GPIO */
+ struct mctrl_gpios gpios;
+ int gpio_irq[UART_GPIO_MAX_INPUT];
unsigned int tx_done_mask;
+ bool ms_irq_enabled;
bool is_usart; /* usart or uart */
struct timer_list uart_timer; /* uart timer */
int (*prepare_rx)(struct uart_port *port);
@@ -237,6 +243,46 @@ static bool atmel_use_dma_rx(struct uart_port *port)
return atmel_port->use_dma_rx;
}
+static unsigned int atmel_get_lines_status(struct uart_port *port)
+{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ unsigned int status, ret = 0;
+
+ status = UART_GET_CSR(port);
+
+ mctrl_gpio_get(&atmel_port->gpios, &ret);
+
+ if (!IS_ERR_OR_NULL(atmel_port->gpios.gpio[UART_GPIO_CTS])) {
+ if (ret & TIOCM_CTS)
+ status &= ~ATMEL_US_CTS;
+ else
+ status |= ATMEL_US_CTS;
+ }
+
+ if (!IS_ERR_OR_NULL(atmel_port->gpios.gpio[UART_GPIO_DSR])) {
+ if (ret & TIOCM_DSR)
+ status &= ~ATMEL_US_DSR;
+ else
+ status |= ATMEL_US_DSR;
+ }
+
+ if (!IS_ERR_OR_NULL(atmel_port->gpios.gpio[UART_GPIO_RI])) {
+ if (ret & TIOCM_RI)
+ status &= ~ATMEL_US_RI;
+ else
+ status |= ATMEL_US_RI;
+ }
+
+ if (!IS_ERR_OR_NULL(atmel_port->gpios.gpio[UART_GPIO_DCD])) {
+ if (ret & TIOCM_CD)
+ status &= ~ATMEL_US_DCD;
+ else
+ status |= ATMEL_US_DCD;
+ }
+
+ return status;
+}
+
/* Enable or disable the rs485 support */
void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf)
{
@@ -296,17 +342,6 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
unsigned int mode;
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
- /*
- * AT91RM9200 Errata #39: RTS0 is not internally connected
- * to PA21. We need to drive the pin as a GPIO.
- */
- if (gpio_is_valid(atmel_port->rts_gpio)) {
- if (mctrl & TIOCM_RTS)
- gpio_set_value(atmel_port->rts_gpio, 0);
- else
- gpio_set_value(atmel_port->rts_gpio, 1);
- }
-
if (mctrl & TIOCM_RTS)
control |= ATMEL_US_RTSEN;
else
@@ -319,6 +354,8 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
UART_PUT_CR(port, control);
+ mctrl_gpio_set(&atmel_port->gpios, mctrl);
+
/* Local loopback mode? */
mode = UART_GET_MR(port) & ~ATMEL_US_CHMODE;
if (mctrl & TIOCM_LOOP)
@@ -346,7 +383,8 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
*/
static u_int atmel_get_mctrl(struct uart_port *port)
{
- unsigned int status, ret = 0;
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ unsigned int ret = 0, status;
status = UART_GET_CSR(port);
@@ -362,7 +400,7 @@ static u_int atmel_get_mctrl(struct uart_port *port)
if (!(status & ATMEL_US_RI))
ret |= TIOCM_RI;
- return ret;
+ return mctrl_gpio_get(&atmel_port->gpios, &ret);
}
/*
@@ -449,8 +487,38 @@ static void atmel_stop_rx(struct uart_port *port)
*/
static void atmel_enable_ms(struct uart_port *port)
{
- UART_PUT_IER(port, ATMEL_US_RIIC | ATMEL_US_DSRIC
- | ATMEL_US_DCDIC | ATMEL_US_CTSIC);
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ uint32_t ier = 0;
+
+ /*
+ * Interrupt should not be enabled twice
+ */
+ if (atmel_port->ms_irq_enabled)
+ return;
+
+ atmel_port->ms_irq_enabled = true;
+
+ if (atmel_port->gpio_irq[UART_GPIO_CTS] >= 0)
+ enable_irq(atmel_port->gpio_irq[UART_GPIO_CTS]);
+ else
+ ier |= ATMEL_US_CTSIC;
+
+ if (atmel_port->gpio_irq[UART_GPIO_DSR] >= 0)
+ enable_irq(atmel_port->gpio_irq[UART_GPIO_DSR]);
+ else
+ ier |= ATMEL_US_DSRIC;
+
+ if (atmel_port->gpio_irq[UART_GPIO_RI] >= 0)
+ enable_irq(atmel_port->gpio_irq[UART_GPIO_RI]);
+ else
+ ier |= ATMEL_US_RIIC;
+
+ if (atmel_port->gpio_irq[UART_GPIO_DCD] >= 0)
+ enable_irq(atmel_port->gpio_irq[UART_GPIO_DCD]);
+ else
+ ier |= ATMEL_US_DCDIC;
+
+ UART_PUT_IER(port, ier);
}
/*
@@ -1039,11 +1107,35 @@ atmel_handle_status(struct uart_port *port, unsigned int pending,
static irqreturn_t atmel_interrupt(int irq, void *dev_id)
{
struct uart_port *port = dev_id;
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
unsigned int status, pending, pass_counter = 0;
+ bool gpio_handled = false;
do {
- status = UART_GET_CSR(port);
+ status = atmel_get_lines_status(port);
pending = status & UART_GET_IMR(port);
+ if (!gpio_handled) {
+ /*
+ * Dealing with GPIO interrupt
+ */
+ if ((irq >= 0) &&
+ (irq == atmel_port->gpio_irq[UART_GPIO_CTS]))
+ pending |= ATMEL_US_CTSIC;
+
+ if ((irq >= 0) &&
+ (irq == atmel_port->gpio_irq[UART_GPIO_DSR]))
+ pending |= ATMEL_US_DSRIC;
+
+ if ((irq >= 0) &&
+ (irq == atmel_port->gpio_irq[UART_GPIO_RI]))
+ pending |= ATMEL_US_RIIC;
+
+ if ((irq >= 0) &&
+ (irq == atmel_port->gpio_irq[UART_GPIO_DCD]))
+ pending |= ATMEL_US_DCDIC;
+
+ gpio_handled = true;
+ }
if (!pending)
break;
@@ -1523,6 +1615,45 @@ static void atmel_get_ip_name(struct uart_port *port)
}
}
+static void atmel_free_gpio_irq(struct uart_port *port)
+{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ enum mctrl_gpio_idx i;
+
+ for (i = UART_GPIO_MIN; i < UART_GPIO_MAX_INPUT; i++)
+ if (atmel_port->gpio_irq[i] >= 0)
+ free_irq(atmel_port->gpio_irq[i], port);
+}
+
+static int atmel_request_gpio_irq(struct uart_port *port)
+{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ int *irq = atmel_port->gpio_irq;
+ enum mctrl_gpio_idx i;
+ int err = 0;
+
+ for (i = UART_GPIO_MIN; (i < UART_GPIO_MAX_INPUT) && !err; i++) {
+ if (irq[i] < 0)
+ continue;
+
+ irq_set_status_flags(irq[i], IRQ_NOAUTOEN);
+ err = request_irq(irq[i], atmel_interrupt, IRQ_TYPE_EDGE_BOTH,
+ get_mctrl_gpio_name(i), port);
+ if (err)
+ dev_err(port->dev, "atmel_startup - Can't get %s irq\n",
+ get_mctrl_gpio_name(i));
+ }
+
+ /*
+ * If something went wrong, rollback.
+ */
+ while (err && --i)
+ if (irq[i] >= 0)
+ free_irq(irq[i], port);
+
+ return err;
+}
+
/*
* Perform initialization and enable port for reception
*/
@@ -1539,6 +1670,7 @@ static int atmel_startup(struct uart_port *port)
* handle an unexpected interrupt
*/
UART_PUT_IDR(port, -1);
+ atmel_port->ms_irq_enabled = false;
/*
* Allocate the IRQ
@@ -1551,6 +1683,13 @@ static int atmel_startup(struct uart_port *port)
}
/*
+ * Get the GPIO lines IRQ
+ */
+ retval = atmel_request_gpio_irq(port);
+ if (retval)
+ goto free_irq;
+
+ /*
* Initialize DMA (if necessary)
*/
atmel_init_property(atmel_port, pdev);
@@ -1568,7 +1707,7 @@ static int atmel_startup(struct uart_port *port)
}
/* Save current CSR for comparison in atmel_tasklet_func() */
- atmel_port->irq_status_prev = UART_GET_CSR(port);
+ atmel_port->irq_status_prev = atmel_get_lines_status(port);
atmel_port->irq_status = atmel_port->irq_status_prev;
/*
@@ -1614,6 +1753,11 @@ static int atmel_startup(struct uart_port *port)
}
return 0;
+
+free_irq:
+ free_irq(port->irq, port);
+
+ return retval;
}
/*
@@ -1661,9 +1805,12 @@ static void atmel_shutdown(struct uart_port *port)
atmel_port->rx_ring.tail = 0;
/*
- * Free the interrupt
+ * Free the interrupts
*/
free_irq(port->irq, port);
+ atmel_free_gpio_irq(port);
+
+ atmel_port->ms_irq_enabled = false;
}
/*
@@ -2327,6 +2474,86 @@ static int atmel_serial_resume(struct platform_device *pdev)
#define atmel_serial_resume NULL
#endif
+/*
+ * TODO:
+ * Suppress that stuff when the platform data is eradicated
+ */
+static int atmel_get_pdata_gpio(struct atmel_uart_port *p, struct device *dev)
+{
+ struct atmel_uart_data *pdata = dev_get_platdata(dev);
+ enum mctrl_gpio_idx i;
+ int err;
+ int ret = 0;
+
+ if (gpio_is_valid(pdata->cts_gpio))
+ p->gpios.gpio[UART_GPIO_CTS] = gpio_to_desc(pdata->cts_gpio);
+ if (gpio_is_valid(pdata->rts_gpio))
+ p->gpios.gpio[UART_GPIO_RTS] = gpio_to_desc(pdata->rts_gpio);
+ if (gpio_is_valid(pdata->dtr_gpio))
+ p->gpios.gpio[UART_GPIO_DTR] = gpio_to_desc(pdata->dtr_gpio);
+ if (gpio_is_valid(pdata->dsr_gpio))
+ p->gpios.gpio[UART_GPIO_DSR] = gpio_to_desc(pdata->dsr_gpio);
+ if (gpio_is_valid(pdata->dcd_gpio))
+ p->gpios.gpio[UART_GPIO_DCD] = gpio_to_desc(pdata->dcd_gpio);
+ if (gpio_is_valid(pdata->ri_gpio))
+ p->gpios.gpio[UART_GPIO_RI] = gpio_to_desc(pdata->ri_gpio);
+
+ for (i = UART_GPIO_MIN; i < UART_GPIO_MAX; i++) {
+ /*
+ * The GPIOs are maybe not all filled,
+ * this is not an error.
+ */
+ if (IS_ERR_OR_NULL(p->gpios.gpio[i]))
+ continue;
+
+ err = devm_gpio_request(dev, desc_to_gpio(p->gpios.gpio[i]),
+ get_mctrl_gpio_name(i));
+ if (err) {
+ dev_err(dev, "error requesting %s GPIO\n",
+ get_mctrl_gpio_name(i));
+ ret--;
+ continue;
+ }
+
+ /* Default to 1 as all signals are active low */
+ (void) gpiod_sysfs_set_active_low(p->gpios.gpio[i], 1);
+
+ if (i < UART_GPIO_MAX_INPUT)
+ err = gpiod_direction_input(p->gpios.gpio[i]);
+ else
+ err = gpiod_direction_output(p->gpios.gpio[i], 0);
+ if (err) {
+ dev_err(dev, "Unable to set direction for %s GPIO",
+ get_mctrl_gpio_name(i));
+ devm_gpio_free(dev, desc_to_gpio(p->gpios.gpio[i]));
+ p->gpios.gpio[i] = NULL;
+ ret--;
+ }
+ }
+
+ return ret;
+}
+
+static int atmel_init_gpios(struct atmel_uart_port *p, struct device *dev)
+{
+ struct atmel_uart_data *pdata = dev_get_platdata(dev);
+ enum mctrl_gpio_idx i;
+ int err;
+
+ if (pdata)
+ err = atmel_get_pdata_gpio(p, dev);
+ else
+ err = mctrl_gpio_init(dev, &p->gpios);
+
+ for (i = UART_GPIO_MIN; i < UART_GPIO_MAX_INPUT; i++)
+ if (IS_ERR_OR_NULL(p->gpios.gpio[i]))
+ p->gpio_irq[i] = -EINVAL;
+ else
+ p->gpio_irq[i] = gpiod_to_irq(p->gpios.gpio[i]);
+
+ return err;
+}
+
static int atmel_serial_probe(struct platform_device *pdev)
{
struct atmel_uart_port *port;
@@ -2362,25 +2589,11 @@ static int atmel_serial_probe(struct platform_device *pdev)
port = &atmel_ports[ret];
port->backup_imr = 0;
port->uart.line = ret;
- port->rts_gpio = -EINVAL; /* Invalid, zero could be valid */
- if (pdata)
- port->rts_gpio = pdata->rts_gpio;
- else if (np)
- port->rts_gpio = of_get_named_gpio(np, "rts-gpios", 0);
- if (gpio_is_valid(port->rts_gpio)) {
- ret = devm_gpio_request(&pdev->dev, port->rts_gpio, "RTS");
- if (ret) {
- dev_err(&pdev->dev, "error requesting RTS GPIO\n");
- goto err;
- }
- /* Default to 1 as RTS is active low */
- ret = gpio_direction_output(port->rts_gpio, 1);
- if (ret) {
- dev_err(&pdev->dev, "error setting up RTS GPIO\n");
- goto err;
- }
- }
+ ret = atmel_init_gpios(port, &pdev->dev);
+ if (ret < 0)
+ dev_err(&pdev->dev, "Failed to initialize %d GPIOs. The serial port may not work as expected",
+ ret * -1);
ret = atmel_init_port(port, pdev);
if (ret)
@@ -2434,6 +2647,7 @@ err_alloc_ring:
port->clk = NULL;
}
err:
+ mctrl_gpio_free(&port->gpios);
return ret;
}
@@ -2457,6 +2671,8 @@ static int atmel_serial_remove(struct platform_device *pdev)
clk_put(atmel_port->clk);
+ mctrl_gpio_free(&atmel_port->gpios);
+
return ret;
}
diff --git a/include/linux/platform_data/atmel.h b/include/linux/platform_data/atmel.h
index e26b0c14edea..565c5c693c7f 100644
--- a/include/linux/platform_data/atmel.h
+++ b/include/linux/platform_data/atmel.h
@@ -85,6 +85,11 @@ struct atmel_uart_data {
void __iomem *regs; /* virt. base address, if any */
struct serial_rs485 rs485; /* rs485 settings */
int rts_gpio; /* optional RTS GPIO */
+ int cts_gpio; /* optional CTS GPIO */
+ int dtr_gpio; /* optional DTR GPIO */
+ int dsr_gpio; /* optional DSR GPIO */
+ int dcd_gpio; /* optional DCD GPIO */
+ int ri_gpio; /* optional Ring GPIO */
};
/* Touchscreen Controller */
--
1.8.5
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v2 1/4] tty/serial: Add GPIOLIB helpers for controlling modem lines
2014-02-11 17:45 ` [PATCH v2 1/4] tty/serial: Add GPIOLIB helpers for controlling " Richard Genoud
@ 2014-02-11 18:23 ` Alexander Shiyan
2014-02-12 8:33 ` Richard Genoud
0 siblings, 1 reply; 8+ messages in thread
From: Alexander Shiyan @ 2014-02-11 18:23 UTC (permalink / raw)
To: linux-arm-kernel
Hello.
???????, 11 ??????? 2014, 18:45 +01:00 ?? Richard Genoud <richard.genoud@gmail.com>:
> 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>
> ---
OK, a few comments below.
...
> +config SERIAL_MCTRL_GPIO
> + def_bool y
> + depends on GPIOLIB
I suggest to move GPIOLIB dependency to serial_mctrl_gpio.h header, so
unit can be used with or without GPIOLIB, so Kconfig will look something like this:
config SERIAL_MCTRL_GPIO
tristate
Then, you can select this option for particular UART:
config SERIAL_ATMEL
...
select SERIAL_MCTRL_GPIO
...
> +++ b/drivers/tty/serial/serial_mctrl_gpio.c
...
> +static const char *mctrl_gpio_of_names[UART_GPIO_MAX] = {
> + "cts", "dsr", "dcd", "ri", "rts", "dtr"
> +};
Make a combined array. This will use cycles for set/get operations.
static struct {
const char *name;
unsigned int mctrl;
} mctrl_gpios[] = {
{ "cts", TIOCM_CTS, },
...
};
...
> +int mctrl_gpio_init(struct device *dev, struct mctrl_gpios *gpios)
> +{
I'm not sure whether to make a non-DT support at all ...
> + enum mctrl_gpio_idx i;
> + int err = 0;
> + int ret = 0;
> +
> + for (i = UART_GPIO_MIN; i < UART_GPIO_MAX; i++) {
> + gpios->gpio[i] = gpiod_get(dev, mctrl_gpio_of_names[i]);
What a reason to using gpiod_xxx() ?
Why we cannot use standart devm_gpio_request/get/set etc. ?
In addition, I recommend create this patch as separate,
because it will most likely still be comments later.
Thanks.
---
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v2 1/4] tty/serial: Add GPIOLIB helpers for controlling modem lines
2014-02-11 18:23 ` Alexander Shiyan
@ 2014-02-12 8:33 ` Richard Genoud
0 siblings, 0 replies; 8+ messages in thread
From: Richard Genoud @ 2014-02-12 8:33 UTC (permalink / raw)
To: linux-arm-kernel
2014-02-11 19:23 GMT+01:00 Alexander Shiyan <shc_work@mail.ru>:
> Hello.
Hi !
>
> ???????, 11 ??????? 2014, 18:45 +01:00 ?? Richard Genoud <richard.genoud@gmail.com>:
>> 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>
>> ---
>
> OK, a few comments below.
>
> ...
>> +config SERIAL_MCTRL_GPIO
>> + def_bool y
>> + depends on GPIOLIB
>
> I suggest to move GPIOLIB dependency to serial_mctrl_gpio.h header, so
> unit can be used with or without GPIOLIB, so Kconfig will look something like this:
>
> config SERIAL_MCTRL_GPIO
> tristate
>
> Then, you can select this option for particular UART:
> config SERIAL_ATMEL
> ...
> select SERIAL_MCTRL_GPIO
Yes, you're right, it seems better like that.
> ...
>> +++ b/drivers/tty/serial/serial_mctrl_gpio.c
> ...
>> +static const char *mctrl_gpio_of_names[UART_GPIO_MAX] = {
>> + "cts", "dsr", "dcd", "ri", "rts", "dtr"
>> +};
>
> Make a combined array. This will use cycles for set/get operations.
>
> static struct {
> const char *name;
> unsigned int mctrl;
> } mctrl_gpios[] = {
> { "cts", TIOCM_CTS, },
> ...
> };
yes, good idea !
> ...
>> +int mctrl_gpio_init(struct device *dev, struct mctrl_gpios *gpios)
>> +{
>
> I'm not sure whether to make a non-DT support at all ...
I don't understand what you mean.
You would like another _init() function for non-DT board ?
Well, I thought that non DT board could handle their GPIO init (like I
did in atmel_serial.c for platform_data based boards), but still use
the mctrl_gpio_{get,set} functions.
>> + enum mctrl_gpio_idx i;
>> + int err = 0;
>> + int ret = 0;
>> +
>> + for (i = UART_GPIO_MIN; i < UART_GPIO_MAX; i++) {
>> + gpios->gpio[i] = gpiod_get(dev, mctrl_gpio_of_names[i]);
>
> What a reason to using gpiod_xxx() ?
> Why we cannot use standart devm_gpio_request/get/set etc. ?
Ah ! I missed devm_gpiod_get()
I'll use it.
>
> In addition, I recommend create this patch as separate,
> because it will most likely still be comments later.
Well, I'd rather not separate this patch from at least "patch 4/4
tty/serial: at91: use mctrl_gpio helpers"
because if the patch 4/4 is merged before this one, the compilation
will fail, and git bisecting will be a pain.
Or there's another solution ?
> Thanks.
Thanks !
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v2 0/4] tty/serial: Add helpers to use GPIOs to control modem lines
2014-02-11 17:45 [PATCH v2 0/4] tty/serial: Add helpers to use GPIOs to control modem lines Richard Genoud
` (3 preceding siblings ...)
2014-02-11 17:45 ` [PATCH v2 4/4] tty/serial: at91: use mctrl_gpio helpers Richard Genoud
@ 2014-02-13 17:37 ` Greg Kroah-Hartman
4 siblings, 0 replies; 8+ messages in thread
From: Greg Kroah-Hartman @ 2014-02-13 17:37 UTC (permalink / raw)
To: linux-arm-kernel
On Tue, Feb 11, 2014 at 06:45:11PM +0100, Richard Genoud wrote:
> The USART controller on sam9x5 chips (and also all AT91/SAMA5 chips
> but at91rm9200) are not capable of handling DTR/DSR/DCD/RI signal.
> Moreover, even if the controller can handle CTS/RTS, the dedicated
> CTS/RTS pins are already muxed for other peripherals (LCDC/EMAC/MMC).
>
> So this patchset adds the possibility to control those lines via GPIO,
> as it is done for RTS in the patch "switch atmel serial to use gpiolib"
>
> As it was suggested by Alexander Shiyan, I made that available for
> every board.
>
> This is based on 3.14-rc2 + Linus Walleij/Nicolas Ferre's patch:
> 354e57f3a0a2 ARM/serial: at91: switch atmel serial to use gpiolib
> (in Uwe's tree git://git.pengutronix.de/git/ukl/linux.git dropmachtimexh )
> and Philipp Zabel's patch:
> gpiolib: make gpiod_direction_output take a logical value, add gpiod_direction_output_raw
> (there won't be a merge conflict if this last patch is not present, the
> gpios will "just" be on the wrong direction.)
>
> Tested on at91sam9g35, with a null modem cable between 2 serial ports,
> one with CTS/RTS controlled by the USART controller, the other via GPIO,
> full duplex transfers.
> Did some tests also with null modem cables on a PC.
>
> Updates from v2:
> - Instead of controlling modem signal only on atmel board, the
> code is now available for every board.
> - The active low flag from device tree is now used.
I'm confused, how does this series relate to the previously sent 8 patch
series?
As I don't know what is going on, I'm going to drop both from my
to-apply queue, and await a new set of patches.
thanks,
greg k-h
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2014-02-13 17:37 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-02-11 17:45 [PATCH v2 0/4] tty/serial: Add helpers to use GPIOs to control modem lines Richard Genoud
2014-02-11 17:45 ` [PATCH v2 1/4] tty/serial: Add GPIOLIB helpers for controlling " Richard Genoud
2014-02-11 18:23 ` Alexander Shiyan
2014-02-12 8:33 ` Richard Genoud
2014-02-11 17:45 ` [PATCH v2 2/4] tty/serial: at91: use dev_err instead of printk Richard Genoud
2014-02-11 17:45 ` [PATCH v2 3/4] tty/serial: at91: remove unused open/close hooks Richard Genoud
2014-02-11 17:45 ` [PATCH v2 4/4] tty/serial: at91: use mctrl_gpio helpers Richard Genoud
2014-02-13 17:37 ` [PATCH v2 0/4] tty/serial: Add helpers to use GPIOs to control modem lines Greg Kroah-Hartman
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).