tty/serial: Add helpers to use GPIOs to control modem lines and implement atmel_serial.c
Subsystems (7)
cover tty/serial: Add helpers to use GPIOs to control modem lines and implement atmel_serial.c
This patch series is a leftover of http://www.spinics.net/lists/arm-kernel/msg314242.html
that missed 3.15 because it caused merge conflicts.
The USART controllers 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 may be 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"
NB: The 1st patch should make it for 3.15-final since it corrects a device tree binding documentation example that has been introduced in 3.15-rc1.
Patch 1: For 3.15-final: fix GPIO_ACTIVE_LOW usage in device-tree documentation. Patch 2: For 3.16: implements at91_gpiolib_get_direction used in patch 4 Patch 3: For 3.16: implements the generic helpers to control modem lines via GPIO. Patch 4: For 3.16: implements modem control lines in atmel_serial atmel_serial. Patch 5: For 3.16: implements the interrupts of CTS/DSR/DCD/RI.
This is based on v3.15-rc5.
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, and with real modems.
Richard Genoud (5):
tty/serial: atmel_serial: Fix device tree documentation
ARM: at91: gpio: implement get_direction tty/serial: Add GPIOLIB helpers for controlling modem lines tty/serial: at91: use mctrl_gpio helpers tty/serial: at91: add interrupts for modem control lines
.../devicetree/bindings/serial/atmel-usart.txt | 12 +- Documentation/serial/driver | 25 +++ arch/arm/mach-at91/at91rm9200_devices.c | 16 +- arch/arm/mach-at91/at91sam9260_devices.c | 7 - arch/arm/mach-at91/at91sam9261_devices.c | 4 - arch/arm/mach-at91/at91sam9263_devices.c | 4 - arch/arm/mach-at91/at91sam9g45_devices.c | 5 - arch/arm/mach-at91/at91sam9rl_devices.c | 5 - arch/arm/mach-at91/gpio.c | 13 ++ drivers/tty/serial/Kconfig | 4 + drivers/tty/serial/Makefile | 3 + drivers/tty/serial/atmel_serial.c | 230 ++++++++++++++++---- drivers/tty/serial/serial_mctrl_gpio.c | 143 ++++++++++++ drivers/tty/serial/serial_mctrl_gpio.h | 110 ++++++++++ include/linux/platform_data/atmel.h | 1 - 15 files changed, 509 insertions(+), 73 deletions(-) create mode 100644 drivers/tty/serial/serial_mctrl_gpio.c create mode 100644 drivers/tty/serial/serial_mctrl_gpio.h
Signature · 1 line
1.7.10.4
PATCH RESEND 1/5 tty/serial: atmel_serial: Fix device tree documentation
RTS pin is an active low pin.
For now, this doesn't change anything as the ACTIVE_LOW flag is not handled in atmel_serial, but it will be in 3.16.
--- Documentation/devicetree/bindings/serial/atmel-usart.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
Documentation/devicetree/bindings/serial/atmel-usart.txt +1 -1
@@ -35,7 +35,7 @@ Example: clock-names = "usart"; atmel,use-dma-rx; atmel,use-dma-tx; - rts-gpios = <&pioD 15 0>; + rts-gpios = <&pioD 15 GPIO_ACTIVE_LOW>; }; - use DMA:
Signature · 1 line
1.7.10.4
PATCH RESEND 2/5 ARM: at91: gpio: implement get_direction
In reply to Richard GenoudThis is needed for gpiod_get_direction().
Otherwise, it returns -EINVAL.
--- arch/arm/mach-at91/gpio.c | 13 +++++++++++++ 1 file changed, 13 insertions(+)
arch/arm/mach-at91/gpio.c +13 -0
@@ -49,6 +49,7 @@ static int at91_gpiolib_request(struct gpio_chip *chip, unsigned offset);static void at91_gpiolib_dbg_show (struct seq_file *s,struct gpio_chip *chip);static void at91_gpiolib_set (struct gpio_chip *chip,unsigned offset,int val);static int at91_gpiolib_get (struct gpio_chip *chip,unsigned offset); +static int at91_gpiolib_get_direction (struct gpio_chip *chip,unsigned offset);static int at91_gpiolib_direction_output (struct gpio_chip *chip,unsigned offset,int val); static int at91_gpiolib_direction_input(struct gpio_chip *chip,
@@ -60,6 +61,7 @@ static int at91_gpiolib_to_irq(struct gpio_chip *chip, unsigned offset); .chip = { \ .label = name, \ .request = at91_gpiolib_request, \ + .get_direction = at91_gpiolib_get_direction, \ .direction_input = at91_gpiolib_direction_input, \ .direction_output = at91_gpiolib_direction_output, \ .get = at91_gpiolib_get, \
@@ -799,6 +801,17 @@ static int at91_gpiolib_request(struct gpio_chip *chip, unsigned offset)return 0 ; } +static int at91_gpiolib_get_direction (struct gpio_chip *chip,unsigned offset) +{ +struct at91_gpio_chip *at91_gpio =to_at91_gpio_chip (chip); +void __iomem *pio = at91_gpio->regbase; +unsigned mask =1 << offset; +u32 osr; + + osr =__raw_readl (pio +PIO_OSR ); +return !(osr & mask); +} +static int at91_gpiolib_direction_input (struct gpio_chip *chip,unsigned offset) {
Signature · 1 line
1.7.10.4
PATCH RESEND 3/5 tty/serial: Add GPIOLIB helpers for controlling modem lines
In reply to Richard GenoudThis 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).
--- Documentation/serial/driver | 25 ++++++ drivers/tty/serial/Kconfig | 3 + drivers/tty/serial/Makefile | 3 + drivers/tty/serial/serial_mctrl_gpio.c | 143 ++++++++++++++++++++++++++++++++ drivers/tty/serial/serial_mctrl_gpio.h | 110 ++++++++++++++++++++++++ 5 files changed, 284 insertions(+) create mode 100644 drivers/tty/serial/serial_mctrl_gpio.c create mode 100644 drivers/tty/serial/serial_mctrl_gpio.h
Documentation/serial/driver +25 -0
@@ -429,3 +429,28 @@ 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, idx): + This will get the {cts,rts,...}-gpios from device tree if they are + present and request them, set direction etc, and return an + allocated structure. devm_* functions are used, so there's no need + to call mctrl_gpio_free(). + +mctrl_gpio_free(dev, gpios): + This will free the requested gpios in mctrl_gpio_init(). + As devm_* function are used, there's generally no need to call + this function. + +mctrl_gpio_to_gpiod(gpios, gidx) + This returns the gpio structure associated to the modem line index. + +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.
drivers/tty/serial/Kconfig +3 -0
@@ -1510,4 +1510,7 @@ config SERIAL_ST_ASC_CONSOLE endmenu +config SERIAL_MCTRL_GPIO + tristate + endif # TTY
drivers/tty/serial/Makefile +3 -0
@@ -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
drivers/tty/serial/serial_mctrl_gpio.c new file +143 -0
143 lines not on this page · raw message
drivers/tty/serial/serial_mctrl_gpio.h new file +110 -0
110 lines not on this page · raw message
Signature · 1 line
1.7.10.4
PATCH RESEND 3/5 tty/serial: Add GPIOLIB helpers for controlling modem lines
Quoted 7 lines · Richard Genoud
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 <[email protected]>
Quoted 331 lines
--- Documentation/serial/driver | 25 ++++++ drivers/tty/serial/Kconfig | 3 + drivers/tty/serial/Makefile | 3 + drivers/tty/serial/serial_mctrl_gpio.c | 143 ++++++++++++++++++++++++++++++++ drivers/tty/serial/serial_mctrl_gpio.h | 110 ++++++++++++++++++++++++ 5 files changed, 284 insertions(+) create mode 100644 drivers/tty/serial/serial_mctrl_gpio.c create mode 100644 drivers/tty/serial/serial_mctrl_gpio.hDocumentation/serial/driver +25
-0@@ -429,3 +429,28 @@ 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, idx): + This will get the {cts,rts,...}-gpios from device tree if they are + present and request them, set direction etc, and return an + allocated structure. devm_* functions are used, so there's no need + to call mctrl_gpio_free(). + +mctrl_gpio_free(dev, gpios): + This will free the requested gpios in mctrl_gpio_init(). + As devm_* function are used, there's generally no need to call + this function. + +mctrl_gpio_to_gpiod(gpios, gidx) + This returns the gpio structure associated to the modem line index. + +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.drivers/tty/serial/Kconfig +3
-0@@ -1510,4 +1510,7 @@ config SERIAL_ST_ASC_CONSOLE endmenu +config SERIAL_MCTRL_GPIO + tristate + endif # TTYdrivers/tty/serial/Makefile +3
-0@@ -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.odrivers/tty/serial/serial_mctrl_gpio.c new file +143
-0@@ -0,0 +1,143 @@ +/* +* Helpers for controlling modem lines via GPIO +* +* Copyright (C) 2014 Paratronic S.A. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +*/ + +#include <linux/err.h> +#include <linux/device.h> +#include <linux/gpio/consumer.h> +#include <uapi/asm-generic/termios.h> + +#include "serial_mctrl_gpio.h" + +struct mctrl_gpios { +struct gpio_desc *gpio[UART_GPIO_MAX ]; +}; + +static const struct { +const char *name; +unsigned int mctrl; +bool dir_out; +} mctrl_gpios_desc[UART_GPIO_MAX ] = { + {"cts" ,TIOCM_CTS , false, }, + {"dsr" ,TIOCM_DSR , false, }, + {"dcd" ,TIOCM_CD , false, }, + {"rng" ,TIOCM_RNG , false, }, + {"rts" ,TIOCM_RTS , true, }, + {"dtr" ,TIOCM_DTR , true, }, + {"out1" ,TIOCM_OUT1 , true, }, + {"out2" ,TIOCM_OUT2 , true, }, +}; + +void mctrl_gpio_set (struct mctrl_gpios *gpios,unsigned int mctrl) +{ +enum mctrl_gpio_idx i; + +if (IS_ERR_OR_NULL (gpios)) +return ; + +for (i =0 ; i <UART_GPIO_MAX ; i++) +if (!IS_ERR_OR_NULL (gpios->gpio[i]) && + mctrl_gpios_desc[i].dir_out) +gpiod_set_value (gpios->gpio[i], + !!(mctrl & mctrl_gpios_desc[i].mctrl)); +} +EXPORT_SYMBOL_GPL (mctrl_gpio_set); + +struct gpio_desc *mctrl_gpio_to_gpiod (struct mctrl_gpios *gpios, +enum mctrl_gpio_idx gidx) +{ +if (!IS_ERR_OR_NULL (gpios) && !IS_ERR_OR_NULL (gpios->gpio[gidx])) +return gpios->gpio[gidx]; +else +return NULL ; +} +EXPORT_SYMBOL_GPL (mctrl_gpio_to_gpiod); + +unsigned int mctrl_gpio_get (struct mctrl_gpios *gpios,unsigned int *mctrl) +{ +enum mctrl_gpio_idx i; + +/* +* return it unchanged if the structure is not allocated +*/ +if (IS_ERR_OR_NULL (gpios)) +return *mctrl; + +for (i =0 ; i <UART_GPIO_MAX ; i++) { +if (!IS_ERR_OR_NULL (gpios->gpio[i]) && + !mctrl_gpios_desc[i].dir_out) { +if (gpiod_get_value (gpios->gpio[i])) + *mctrl |= mctrl_gpios_desc[i].mctrl; +else + *mctrl &= ~mctrl_gpios_desc[i].mctrl; + } + } + +return *mctrl; +} +EXPORT_SYMBOL_GPL (mctrl_gpio_get); + +struct mctrl_gpios *mctrl_gpio_init (struct device *dev,unsigned int idx) +{ +struct mctrl_gpios *gpios; +enum mctrl_gpio_idx i; +int err; + + gpios =devm_kzalloc (dev,sizeof (*gpios),GFP_KERNEL ); +if (!gpios) +return ERR_PTR (-ENOMEM ); + +for (i =0 ; i <UART_GPIO_MAX ; i++) { + gpios->gpio[i] =devm_gpiod_get_index (dev, + mctrl_gpios_desc[i].name, + idx); + +/* +* The GPIOs are maybe not all filled, +* this is not an error. +*/ +if (IS_ERR_OR_NULL (gpios->gpio[i])) +continue ; + +if (mctrl_gpios_desc[i].dir_out) + err =gpiod_direction_output (gpios->gpio[i],0 ); +else + err =gpiod_direction_input (gpios->gpio[i]); +if (err) { +dev_dbg (dev,"Unable to set direction for %s GPIO" , + mctrl_gpios_desc[i].name); +devm_gpiod_put (dev, gpios->gpio[i]); + gpios->gpio[i] =NULL ; + } + } + +return gpios; +} +EXPORT_SYMBOL_GPL (mctrl_gpio_init); + +void mctrl_gpio_free (struct device *dev,struct mctrl_gpios *gpios) +{ +enum mctrl_gpio_idx i; + +if (IS_ERR_OR_NULL (gpios)) +return ; + +for (i =0 ; i <UART_GPIO_MAX ; i++) +if (!IS_ERR_OR_NULL (gpios->gpio[i])) +devm_gpiod_put (dev, gpios->gpio[i]); +devm_kfree (dev, gpios); +} +EXPORT_SYMBOL_GPL (mctrl_gpio_free);drivers/tty/serial/serial_mctrl_gpio.h new file +110
-0@@ -0,0 +1,110 @@ +/* +* Helpers for controlling modem lines via GPIO +* +* Copyright (C) 2014 Paratronic S.A. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +*/ + +#ifndef __SERIAL_MCTRL_GPIO__ +#define __SERIAL_MCTRL_GPIO__ + +#include <linux/err.h> +#include <linux/device.h> +#include <linux/gpio/consumer.h> + +enum mctrl_gpio_idx { +UART_GPIO_CTS , +UART_GPIO_DSR , +UART_GPIO_DCD , +UART_GPIO_RNG , +UART_GPIO_RI =UART_GPIO_RNG , +UART_GPIO_RTS , +UART_GPIO_DTR , +UART_GPIO_OUT1 , +UART_GPIO_OUT2 , +UART_GPIO_MAX , +}; + +/* +* Opaque descriptor for modem lines controlled by GPIOs +*/ +struct mctrl_gpios ; + +#ifdef CONFIG_GPIOLIB + +/* +* 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); + +/* +* Returns the associated struct gpio_desc to the modem line gidx +*/ +struct gpio_desc *mctrl_gpio_to_gpiod (struct mctrl_gpios *gpios, +enum mctrl_gpio_idx gidx); + +/* +* Request and set direction of modem control lines GPIOs. +* devm_* functions are used, so there's no need to call mctrl_gpio_free(). +* Returns a pointer to the allocated mctrl structure if ok, -ENOMEM on +* allocation error. +*/ +struct mctrl_gpios *mctrl_gpio_init (struct device *dev,unsigned int idx); + +/* +* Free the mctrl_gpios structure. +* Normally, this function will not be called, as the GPIOs will +* be disposed of by the resource management code. +*/ +void mctrl_gpio_free (struct device *dev,struct mctrl_gpios *gpios); + +#else /* GPIOLIB */ + +static inline +void mctrl_gpio_set (struct mctrl_gpios *gpios,unsigned int mctrl) +{ +} + +static inline +unsigned int mctrl_gpio_get (struct mctrl_gpios *gpios,unsigned int *mctrl) +{ +return *mctrl; +} + +static inline +struct gpio_desc *mctrl_gpio_to_gpiod (struct mctrl_gpios *gpios, +enum mctrl_gpio_idx gidx) +{ +return ERR_PTR (-ENOSYS ); +} + +static inline +struct mctrl_gpios *mctrl_gpio_init (struct device *dev,unsigned int idx) +{ +return ERR_PTR (-ENOSYS ); +} + +static inline +void mctrl_gpio_free (struct device *dev,struct mctrl_gpios *gpios) +{ +} + +#endif /* GPIOLIB */ + +#endif
Greg Kroah-Hartman
maintainerPATCH RESEND 3/5 tty/serial: Add GPIOLIB helpers for controlling modem lines
In reply to Richard GenoudQuoted 55 lines · Richard Genoud
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 <[email protected]>Tested-by Yegor Yefremov <[email protected]>--- Documentation/serial/driver | 25 ++++++ drivers/tty/serial/Kconfig | 3 + drivers/tty/serial/Makefile | 3 + drivers/tty/serial/serial_mctrl_gpio.c | 143 ++++++++++++++++++++++++++++++++ drivers/tty/serial/serial_mctrl_gpio.h | 110 ++++++++++++++++++++++++ 5 files changed, 284 insertions(+) create mode 100644 drivers/tty/serial/serial_mctrl_gpio.c create mode 100644 drivers/tty/serial/serial_mctrl_gpio.hDocumentation/serial/driver +25
-0@@ -429,3 +429,28 @@ 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, idx): + This will get the {cts,rts,...}-gpios from device tree if they are + present and request them, set direction etc, and return an + allocated structure. devm_* functions are used, so there's no need + to call mctrl_gpio_free(). + +mctrl_gpio_free(dev, gpios): + This will free the requested gpios in mctrl_gpio_init(). + As devm_* function are used, there's generally no need to call + this function. + +mctrl_gpio_to_gpiod(gpios, gidx) + This returns the gpio structure associated to the modem line index. + +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.drivers/tty/serial/Kconfig +0
-0
drivers/tty/serial/Kconfig config SERIAL_ST_ASC_CONSOLE · line 1514
Quoted 5 lines
drivers/tty/serial/Kconfig +2
-0@@ -1510,4 +1510,7 @@ config SERIAL_ST_ASC_CONSOLE endmenu +config SERIAL_MCTRL_GPIO + tristate
Why is this added here? Who is going to use it?
drivers/tty/serial/Makefile obj-$(CONFIG_SERIAL_EFM32_UART) += efm32-uart.o · line 92
Quoted 13 lines
drivers/tty/serial/Kconfig +1
-0+ endif # TTYdrivers/tty/serial/Makefile +3
-0@@ -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
This chunk doesn't apply anymore :(
Greg Kroah-Hartman
maintainerPATCH RESEND 3/5 tty/serial: Add GPIOLIB helpers for controlling modem lines
Quoted 57 lines · Greg Kroah-Hartman
Quoted 56 lines · Richard Genoud
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 <[email protected]>Tested-by Yegor Yefremov <[email protected]>--- Documentation/serial/driver | 25 ++++++ drivers/tty/serial/Kconfig | 3 + drivers/tty/serial/Makefile | 3 + drivers/tty/serial/serial_mctrl_gpio.c | 143 ++++++++++++++++++++++++++++++++ drivers/tty/serial/serial_mctrl_gpio.h | 110 ++++++++++++++++++++++++ 5 files changed, 284 insertions(+) create mode 100644 drivers/tty/serial/serial_mctrl_gpio.c create mode 100644 drivers/tty/serial/serial_mctrl_gpio.hDocumentation/serial/driver +25
-0@@ -429,3 +429,28 @@ 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, idx): + This will get the {cts,rts,...}-gpios from device tree if they are + present and request them, set direction etc, and return an + allocated structure. devm_* functions are used, so there's no need + to call mctrl_gpio_free(). + +mctrl_gpio_free(dev, gpios): + This will free the requested gpios in mctrl_gpio_init(). + As devm_* function are used, there's generally no need to call + this function. + +mctrl_gpio_to_gpiod(gpios, gidx) + This returns the gpio structure associated to the modem line index. + +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.drivers/tty/serial/Kconfig +0
-0@@ -1510,4 +1510,7 @@ config SERIAL_ST_ASC_CONSOLE
Quoted 6 lines
drivers/tty/serial/Kconfig config SERIAL_ST_ASC_CONSOLE · line 1514
Quoted 4 lines
drivers/tty/serial/Kconfig +2
-0endmenu +config SERIAL_MCTRL_GPIO + tristateWhy is this added here? Who is going to use it?
Ah, the next patch does, nevermind...
PATCH RESEND 4/5 tty/serial: at91: use mctrl_gpio helpers
In reply to Richard GenoudOn 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.
--- .../devicetree/bindings/serial/atmel-usart.txt | 10 +- arch/arm/mach-at91/at91rm9200_devices.c | 16 +-- arch/arm/mach-at91/at91sam9260_devices.c | 7 -- arch/arm/mach-at91/at91sam9261_devices.c | 4 - arch/arm/mach-at91/at91sam9263_devices.c | 4 - arch/arm/mach-at91/at91sam9g45_devices.c | 5 - arch/arm/mach-at91/at91sam9rl_devices.c | 5 - drivers/tty/serial/Kconfig | 1 + drivers/tty/serial/atmel_serial.c | 105 +++++++++++++------- include/linux/platform_data/atmel.h | 1 - 10 files changed, 89 insertions(+), 69 deletions(-)
Documentation/devicetree/bindings/serial/atmel-usart.txt +8 -2
@@ -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,rng,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.
@@ -36,6 +37,11 @@ Example: atmel,use-dma-rx; atmel,use-dma-tx; 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>; + rng-gpios = <&pioD 19 GPIO_ACTIVE_LOW>; }; - use DMA:
arch/arm/mach-at91/at91rm9200_devices.c +10 -6
@@ -15,6 +15,7 @@#include <linux/dma-mapping.h> #include <linux/gpio.h> +#include <linux/gpio/driver.h> #include <linux/platform_device.h> #include <linux/i2c-gpio.h>
@@ -923,7 +924,6 @@ static struct resource dbgu_resources[] = {static struct atmel_uart_data dbgu_data = { .use_dma_tx =0 , .use_dma_rx =0 ,/* DBGU not capable of receive DMA */ - .rts_gpio = -EINVAL , };static u64 dbgu_dmamask =DMA_BIT_MASK (32 );
@@ -962,7 +962,14 @@ static struct resource uart0_resources[] = {static struct atmel_uart_data uart0_data = { .use_dma_tx =1 , .use_dma_rx =1 , - .rts_gpio = -EINVAL , +}; + +static struct gpiod_lookup_table uart0_gpios_table = { + .dev_id ="atmel_usart" , + .table = { +GPIO_LOOKUP ("pioA" ,21 ,"rts" ,GPIO_ACTIVE_LOW ), + { }, + }, };static u64 uart0_dmamask =DMA_BIT_MASK (32 );
@@ -993,7 +1000,7 @@ static inline void configure_usart0_pins(unsigned pins)* We need to drive the pin manually. The serial driver will driver * this to high when initializing. */ - uart0_data.rts_gpio =AT91_PIN_PA21 ; +gpiod_add_lookup_table (&uart0_gpios_table); } }
@@ -1013,7 +1020,6 @@ static struct resource uart1_resources[] = {static struct atmel_uart_data uart1_data = { .use_dma_tx =1 , .use_dma_rx =1 , - .rts_gpio = -EINVAL , };static u64 uart1_dmamask =DMA_BIT_MASK (32 );
@@ -1065,7 +1071,6 @@ static struct resource uart2_resources[] = {static struct atmel_uart_data uart2_data = { .use_dma_tx =1 , .use_dma_rx =1 , - .rts_gpio = -EINVAL , };static u64 uart2_dmamask =DMA_BIT_MASK (32 );
@@ -1109,7 +1114,6 @@ static struct resource uart3_resources[] = {static struct atmel_uart_data uart3_data = { .use_dma_tx =1 , .use_dma_rx =1 , - .rts_gpio = -EINVAL , };static u64 uart3_dmamask =DMA_BIT_MASK (32 );
arch/arm/mach-at91/at91sam9260_devices.c +0 -7
@@ -820,7 +820,6 @@ static struct resource dbgu_resources[] = {static struct atmel_uart_data dbgu_data = { .use_dma_tx =0 , .use_dma_rx =0 ,/* DBGU not capable of receive DMA */ - .rts_gpio = -EINVAL , };static u64 dbgu_dmamask =DMA_BIT_MASK (32 );
@@ -859,7 +858,6 @@ static struct resource uart0_resources[] = {static struct atmel_uart_data uart0_data = { .use_dma_tx =1 , .use_dma_rx =1 , - .rts_gpio = -EINVAL , };static u64 uart0_dmamask =DMA_BIT_MASK (32 );
@@ -911,7 +909,6 @@ static struct resource uart1_resources[] = {static struct atmel_uart_data uart1_data = { .use_dma_tx =1 , .use_dma_rx =1 , - .rts_gpio = -EINVAL , };static u64 uart1_dmamask =DMA_BIT_MASK (32 );
@@ -955,7 +952,6 @@ static struct resource uart2_resources[] = {static struct atmel_uart_data uart2_data = { .use_dma_tx =1 , .use_dma_rx =1 , - .rts_gpio = -EINVAL , };static u64 uart2_dmamask =DMA_BIT_MASK (32 );
@@ -999,7 +995,6 @@ static struct resource uart3_resources[] = {static struct atmel_uart_data uart3_data = { .use_dma_tx =1 , .use_dma_rx =1 , - .rts_gpio = -EINVAL , };static u64 uart3_dmamask =DMA_BIT_MASK (32 );
@@ -1043,7 +1038,6 @@ static struct resource uart4_resources[] = {static struct atmel_uart_data uart4_data = { .use_dma_tx =1 , .use_dma_rx =1 , - .rts_gpio = -EINVAL , };static u64 uart4_dmamask =DMA_BIT_MASK (32 );
@@ -1082,7 +1076,6 @@ static struct resource uart5_resources[] = {static struct atmel_uart_data uart5_data = { .use_dma_tx =1 , .use_dma_rx =1 , - .rts_gpio = -EINVAL , };static u64 uart5_dmamask =DMA_BIT_MASK (32 );
arch/arm/mach-at91/at91sam9261_devices.c +0 -4
@@ -881,7 +881,6 @@ static struct resource dbgu_resources[] = {static struct atmel_uart_data dbgu_data = { .use_dma_tx =0 , .use_dma_rx =0 ,/* DBGU not capable of receive DMA */ - .rts_gpio = -EINVAL , };static u64 dbgu_dmamask =DMA_BIT_MASK (32 );
@@ -920,7 +919,6 @@ static struct resource uart0_resources[] = {static struct atmel_uart_data uart0_data = { .use_dma_tx =1 , .use_dma_rx =1 , - .rts_gpio = -EINVAL , };static u64 uart0_dmamask =DMA_BIT_MASK (32 );
@@ -964,7 +962,6 @@ static struct resource uart1_resources[] = {static struct atmel_uart_data uart1_data = { .use_dma_tx =1 , .use_dma_rx =1 , - .rts_gpio = -EINVAL , };static u64 uart1_dmamask =DMA_BIT_MASK (32 );
@@ -1008,7 +1005,6 @@ static struct resource uart2_resources[] = {static struct atmel_uart_data uart2_data = { .use_dma_tx =1 , .use_dma_rx =1 , - .rts_gpio = -EINVAL , };static u64 uart2_dmamask =DMA_BIT_MASK (32 );
arch/arm/mach-at91/at91sam9263_devices.c +0 -4
@@ -1325,7 +1325,6 @@ static struct resource dbgu_resources[] = {static struct atmel_uart_data dbgu_data = { .use_dma_tx =0 , .use_dma_rx =0 ,/* DBGU not capable of receive DMA */ - .rts_gpio = -EINVAL , };static u64 dbgu_dmamask =DMA_BIT_MASK (32 );
@@ -1364,7 +1363,6 @@ static struct resource uart0_resources[] = {static struct atmel_uart_data uart0_data = { .use_dma_tx =1 , .use_dma_rx =1 , - .rts_gpio = -EINVAL , };static u64 uart0_dmamask =DMA_BIT_MASK (32 );
@@ -1408,7 +1406,6 @@ static struct resource uart1_resources[] = {static struct atmel_uart_data uart1_data = { .use_dma_tx =1 , .use_dma_rx =1 , - .rts_gpio = -EINVAL , };static u64 uart1_dmamask =DMA_BIT_MASK (32 );
@@ -1452,7 +1449,6 @@ static struct resource uart2_resources[] = {static struct atmel_uart_data uart2_data = { .use_dma_tx =1 , .use_dma_rx =1 , - .rts_gpio = -EINVAL , };static u64 uart2_dmamask =DMA_BIT_MASK (32 );
arch/arm/mach-at91/at91sam9g45_devices.c +0 -5
@@ -1588,7 +1588,6 @@ static struct resource dbgu_resources[] = {static struct atmel_uart_data dbgu_data = { .use_dma_tx =0 , .use_dma_rx =0 , - .rts_gpio = -EINVAL , };static u64 dbgu_dmamask =DMA_BIT_MASK (32 );
@@ -1627,7 +1626,6 @@ static struct resource uart0_resources[] = {static struct atmel_uart_data uart0_data = { .use_dma_tx =1 , .use_dma_rx =1 , - .rts_gpio = -EINVAL , };static u64 uart0_dmamask =DMA_BIT_MASK (32 );
@@ -1671,7 +1669,6 @@ static struct resource uart1_resources[] = {static struct atmel_uart_data uart1_data = { .use_dma_tx =1 , .use_dma_rx =1 , - .rts_gpio = -EINVAL , };static u64 uart1_dmamask =DMA_BIT_MASK (32 );
@@ -1715,7 +1712,6 @@ static struct resource uart2_resources[] = {static struct atmel_uart_data uart2_data = { .use_dma_tx =1 , .use_dma_rx =1 , - .rts_gpio = -EINVAL , };static u64 uart2_dmamask =DMA_BIT_MASK (32 );
@@ -1759,7 +1755,6 @@ static struct resource uart3_resources[] = {static struct atmel_uart_data uart3_data = { .use_dma_tx =1 , .use_dma_rx =1 , - .rts_gpio = -EINVAL , };static u64 uart3_dmamask =DMA_BIT_MASK (32 );
arch/arm/mach-at91/at91sam9rl_devices.c +0 -5
@@ -957,7 +957,6 @@ static struct resource dbgu_resources[] = {static struct atmel_uart_data dbgu_data = { .use_dma_tx =0 , .use_dma_rx =0 ,/* DBGU not capable of receive DMA */ - .rts_gpio = -EINVAL , };static u64 dbgu_dmamask =DMA_BIT_MASK (32 );
@@ -996,7 +995,6 @@ static struct resource uart0_resources[] = {static struct atmel_uart_data uart0_data = { .use_dma_tx =1 , .use_dma_rx =1 , - .rts_gpio = -EINVAL , };static u64 uart0_dmamask =DMA_BIT_MASK (32 );
@@ -1048,7 +1046,6 @@ static struct resource uart1_resources[] = {static struct atmel_uart_data uart1_data = { .use_dma_tx =1 , .use_dma_rx =1 , - .rts_gpio = -EINVAL , };static u64 uart1_dmamask =DMA_BIT_MASK (32 );
@@ -1092,7 +1089,6 @@ static struct resource uart2_resources[] = {static struct atmel_uart_data uart2_data = { .use_dma_tx =1 , .use_dma_rx =1 , - .rts_gpio = -EINVAL , };static u64 uart2_dmamask =DMA_BIT_MASK (32 );
@@ -1136,7 +1132,6 @@ static struct resource uart3_resources[] = {static struct atmel_uart_data uart3_data = { .use_dma_tx =1 , .use_dma_rx =1 , - .rts_gpio = -EINVAL , };static u64 uart3_dmamask =DMA_BIT_MASK (32 );
drivers/tty/serial/Kconfig +1 -0
@@ -97,6 +97,7 @@ config SERIAL_ATMEL bool "AT91 / AT32 on-chip serial port support" depends on ARCH_AT91 || AVR32 select SERIAL_CORE + select SERIAL_MCTRL_GPIO help This enables the driver for the on-chip UARTs of the Atmel AT91 and AT32 processors.
drivers/tty/serial/atmel_serial.c +70 -35
177 lines not on this page · raw message
include/linux/platform_data/atmel.h +0 -1
@@ -84,7 +84,6 @@ struct atmel_uart_data {short use_dma_rx;/* use receive DMA? */ void __iomem *regs;/* virt. base address, if any */ struct serial_rs485 rs485;/* rs485 settings */ -int rts_gpio;/* optional RTS GPIO */ };/* Touchscreen Controller */
Signature · 1 line
1.7.10.4
PATCH RESEND 5/5 tty/serial: at91: add interrupts for modem control lines
In reply to Richard GenoudHandle CTS/DSR/RI/DCD GPIO interrupts in atmel_serial.
--- drivers/tty/serial/atmel_serial.c | 125 ++++++++++++++++++++++++++++++++++++- 1 file changed, 122 insertions(+), 3 deletions(-)
drivers/tty/serial/atmel_serial.c +122 -3
@@ -45,6 +45,7 @@#include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/err.h> +#include <linux/irq.h> #include <asm/io.h> #include <asm/ioctls.h>
@@ -167,7 +168,9 @@ struct atmel_uart_port {struct serial_rs485 rs485;/* rs485 settings */ struct mctrl_gpios *gpios; +int gpio_irq[UART_GPIO_MAX ];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);
@@ -489,8 +492,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); }/*
@@ -1079,11 +1112,31 @@ 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 = atmel_get_lines_status(port); pending = status & UART_GET_IMR(port); + if (!gpio_handled) { + /* + * Dealing with GPIO interrupt + */ + if (irq == atmel_port->gpio_irq[UART_GPIO_CTS]) + pending |= ATMEL_US_CTSIC; + + if (irq == atmel_port->gpio_irq[UART_GPIO_DSR]) + pending |= ATMEL_US_DSRIC; + + if (irq == atmel_port->gpio_irq[UART_GPIO_RI]) + pending |= ATMEL_US_RIIC; + + if (irq == atmel_port->gpio_irq[UART_GPIO_DCD]) + pending |= ATMEL_US_DCDIC; + + gpio_handled = true; + } if (!pending) break;
@@ -1563,6 +1616,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 =0 ; i <UART_GPIO_MAX ; 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 =0 ; (i <UART_GPIO_MAX ) && !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 , +"atmel_serial" , port); +if (err) +dev_err (port->dev,"atmel_startup - Can't get %d irq\n" , + irq[i]); + } + +/* +* If something went wrong, rollback. +*/ +while (err && (--i >=0 )) +if (irq[i] >=0 ) +free_irq (irq[i], port); + +return err; +} +/* * Perform initialization and enable port for reception */
@@ -1579,6 +1671,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
@@ -1591,6 +1684,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);
@@ -1654,6 +1754,11 @@ static int atmel_startup(struct uart_port *port) }return 0 ; + +free_irq: +free_irq (port->irq, port); + +return retval; }/*
@@ -1701,9 +1806,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; }/*
@@ -2366,10 +2474,21 @@ static int atmel_serial_resume(struct platform_device *pdev)static int atmel_init_gpios (struct atmel_uart_port *p,struct device *dev) { +enum mctrl_gpio_idx i; +struct gpio_desc *gpiod; + p->gpios =mctrl_gpio_init (dev,0 );if (IS_ERR_OR_NULL (p->gpios))return -1 ; +for (i =0 ; i <UART_GPIO_MAX ; i++) { + gpiod =mctrl_gpio_to_gpiod (p->gpios, i); +if (gpiod && (gpiod_get_direction (gpiod) ==GPIOF_DIR_IN )) + p->gpio_irq[i] =gpiod_to_irq (gpiod); +else + p->gpio_irq[i] = -EINVAL ; + } +return 0 ; }
Signature · 1 line
1.7.10.4
PATCH RESEND 5/5 tty/serial: at91: add interrupts for modem control lines
Hi Richard,
Quoted 57 lines · Richard Genoud
Handle CTS/DSR/RI/DCD GPIO interrupts in
atmel_serial.Signed-off-by Richard Genoud <[email protected]>Acked-by Nicolas Ferre <[email protected]>--- drivers/tty/serial/atmel_serial.c | 125 ++++++++++++++++++++++++++++++++++++- 1 file changed, 122 insertions(+), 3 deletions(-)drivers/tty/serial/atmel_serial.c +23
-2@@ -45,6 +45,7 @@#include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/err.h> +#include <linux/irq.h> #include <asm/io.h> #include <asm/ioctls.h> @@ -167,7 +168,9 @@ struct atmel_uart_port {struct serial_rs485 rs485;/* rs485 settings */ struct mctrl_gpios *gpios; +int gpio_irq[UART_GPIO_MAX ];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);@@ -489,8 +492,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 ;
drivers/tty/serial/atmel_serial.c static void atmel_stop_rx(struct uart_port *port) · line 524
Quoted 10 lines
drivers/tty/serial/atmel_serial.c +10
-0+ +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 ;
Where are these gpio irqs disabled if !UART_ENABLE_MS()?
Quoted 154 lines
drivers/tty/serial/atmel_serial.c +89
-1+ +UART_PUT_IER (port, ier); }/* @@ -1079,11 +1112,31 @@ 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 = atmel_get_lines_status(port); pending = status & UART_GET_IMR(port); + if (!gpio_handled) { + /* + * Dealing with GPIO interrupt + */ + if (irq == atmel_port->gpio_irq[UART_GPIO_CTS]) + pending |= ATMEL_US_CTSIC; + + if (irq == atmel_port->gpio_irq[UART_GPIO_DSR]) + pending |= ATMEL_US_DSRIC; + + if (irq == atmel_port->gpio_irq[UART_GPIO_RI]) + pending |= ATMEL_US_RIIC; + + if (irq == atmel_port->gpio_irq[UART_GPIO_DCD]) + pending |= ATMEL_US_DCDIC; + + gpio_handled = true; + } if (!pending) break;@@ -1563,6 +1616,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 =0 ; i <UART_GPIO_MAX ; 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 =0 ; (i <UART_GPIO_MAX ) && !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 , +"atmel_serial" , port); +if (err) +dev_err (port->dev,"atmel_startup - Can't get %d irq\n" , + irq[i]); + } + +/* +* If something went wrong, rollback. +*/ +while (err && (--i >=0 )) +if (irq[i] >=0 ) +free_irq (irq[i], port); + +return err; +} +/* * Perform initialization and enable port for reception */ @@ -1579,6 +1671,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 @@ -1591,6 +1684,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);@@ -1654,6 +1754,11 @@ static int atmel_startup(struct uart_port *port) }return 0 ; + +free_irq: +free_irq (port->irq, port); + +return retval; }/* @@ -1701,9 +1806,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; }/* @@ -2366,10 +2474,21 @@ static int atmel_serial_resume(struct platform_device *pdev)static int atmel_init_gpios (struct atmel_uart_port *p,struct device *dev) { +enum mctrl_gpio_idx i; +struct gpio_desc *gpiod; + p->gpios =mctrl_gpio_init (dev,0 );if (IS_ERR_OR_NULL (p->gpios))return -1 ; +for (i =0 ; i <UART_GPIO_MAX ; i++) { + gpiod =mctrl_gpio_to_gpiod (p->gpios, i); +if (gpiod && (gpiod_get_direction (gpiod) ==GPIOF_DIR_IN )) + p->gpio_irq[i] =gpiod_to_irq (gpiod); +else + p->gpio_irq[i] = -EINVAL ; + } +return 0 ; }
PATCH RESEND 5/5 tty/serial: at91: add interrupts for modem control lines
2014-08-10 21:33 GMT+02:00 Peter Hurley <[email protected]>:
Quoted 1 line
Hi Richard,
Hi !
Sorry for the delay !
Quoted 62 lines · Richard Genoud
Quoted 61 lines
Handle CTS/DSR/RI/DCD GPIO interrupts in
atmel_serial.Signed-off-by Richard Genoud <[email protected]>Acked-by Nicolas Ferre <[email protected]>--- drivers/tty/serial/atmel_serial.c | 125 ++++++++++++++++++++++++++++++++++++- 1 file changed, 122 insertions(+), 3 deletions(-)drivers/tty/serial/atmel_serial.c +27
-2@@ -45,6 +45,7 @@#include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/err.h> +#include <linux/irq.h> #include <asm/io.h> #include <asm/ioctls.h> @@ -167,7 +168,9 @@ struct atmel_uart_port {struct serial_rs485 rs485;/* rs485 settings */ struct mctrl_gpios *gpios; +int gpio_irq[UART_GPIO_MAX ];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);@@ -489,8 +492,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
Quoted 8 lines
drivers/tty/serial/atmel_serial.c static void atmel_stop_rx(struct uart_port *port) · line 524
Quoted 6 lines
drivers/tty/serial/atmel_serial.c +6
-0+ 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 ;Where are these gpio irqs disabled if !UART_ENABLE_MS()?
They are disabled in atmel_shutdown()/atmel_free_gpio_irq(). Like the doc says in Documentation/serial/driver
enable_ms(port)
Enable the modem status interrupts.
This method may be called multiple times. Modem status interrupts should be disabled when the shutdown method is called.
Regards,
Richard.
PATCH RESEND 5/5 tty/serial: at91: add interrupts for modem control lines
Quoted 76 lines · Richard Genoud
2014-08-10 21:33 GMT+02:00 Peter Hurley <[email protected]>:
Quoted 1 line
Hi Richard,
Hi !
Sorry for the delay !Quoted 62 lines · Richard Genoud
Quoted 61 lines
Handle CTS/DSR/RI/DCD GPIO interrupts in
atmel_serial.Signed-off-by Richard Genoud <[email protected]>Acked-by Nicolas Ferre <[email protected]>--- drivers/tty/serial/atmel_serial.c | 125 ++++++++++++++++++++++++++++++++++++- 1 file changed, 122 insertions(+), 3 deletions(-)drivers/tty/serial/atmel_serial.c +27
-2@@ -45,6 +45,7 @@#include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/err.h> +#include <linux/irq.h> #include <asm/io.h> #include <asm/ioctls.h> @@ -167,7 +168,9 @@ struct atmel_uart_port {struct serial_rs485 rs485;/* rs485 settings */ struct mctrl_gpios *gpios; +int gpio_irq[UART_GPIO_MAX ];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);@@ -489,8 +492,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 Quoted 8 lines
drivers/tty/serial/atmel_serial.c static void atmel_stop_rx(struct uart_port *port) · line 524
Quoted 6 lines
drivers/tty/serial/atmel_serial.c +6
-0+ 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 ;Where are these gpio irqs disabled if !UART_ENABLE_MS()?
Quoted 8 lines
They are disabled in atmel_shutdown()/atmel_free_gpio_irq(). Like the doc says in
Documentation/serial/driver
enable_ms(port)
Enable the modem status interrupts.This method may be called multiple times. Modem status interrupts should be disabled when the shutdown method is called.
Thanks for replying.
The point of the UART_ENABLE_MS() macro is to allow the UART driver to determine if modem status interrupts need to be enabled when handling the .set_termios() method.
When handling set_termios(), if UART_ENABLE_MS is false (ie., the serial core does not require modem status interrupts), the UART driver can and should turn modem status interrupts off.
It is _also_ true that "modem status interrupts should be disabled when the shutdown method is called"; in fact, _all_ UART-sourced interrupts should be disabled when the shutdown method is called.
Regards,
Peter Hurley
PATCH RESEND 5/5 tty/serial: at91: add interrupts for modem control lines
2014-09-02 13:40 GMT+02:00 Peter Hurley <[email protected]>:
Quoted 92 lines · Richard Genoud
Quoted 76 lines
2014-08-10 21:33 GMT+02:00 Peter Hurley <[email protected]>:
Quoted 1 line
Hi Richard,
Hi !
Sorry for the delay !Quoted 62 lines · Richard Genoud
Quoted 61 lines
Handle CTS/DSR/RI/DCD GPIO interrupts in
atmel_serial.Signed-off-by Richard Genoud <[email protected]>Acked-by Nicolas Ferre <[email protected]>--- drivers/tty/serial/atmel_serial.c | 125 ++++++++++++++++++++++++++++++++++++- 1 file changed, 122 insertions(+), 3 deletions(-)drivers/tty/serial/atmel_serial.c +27
-2@@ -45,6 +45,7 @@#include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/err.h> +#include <linux/irq.h> #include <asm/io.h> #include <asm/ioctls.h> @@ -167,7 +168,9 @@ struct atmel_uart_port {struct serial_rs485 rs485;/* rs485 settings */ struct mctrl_gpios *gpios; +int gpio_irq[UART_GPIO_MAX ];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);@@ -489,8 +492,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 Quoted 8 lines
drivers/tty/serial/atmel_serial.c static void atmel_stop_rx(struct uart_port *port) · line 524
Quoted 6 lines
drivers/tty/serial/atmel_serial.c +6
-0+ 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 ;Where are these gpio irqs disabled if !UART_ENABLE_MS()?
Quoted 8 lines
They are disabled in atmel_shutdown()/atmel_free_gpio_irq(). Like the doc says in
Documentation/serial/driver
enable_ms(port)
Enable the modem status interrupts.This method may be called multiple times. Modem status interrupts should be disabled when the shutdown method is called.
Thanks for replying.
The point of the
UART_ENABLE_MS()macro is to allow the UART driver to determine if modem status interrupts need to be enabled when handling the .set_termios() method.
Quoted 7 lines
When handling
set_termios(), ifUART_ENABLE_MSis false (ie., the serial core does not require modem status interrupts), the UART driver can and should turn modem status interrupts off.It is _also_ true that "modem status interrupts should be disabled when the shutdown method is called"; in fact, _all_ UART-sourced interrupts should be disabled when the shutdown method is called.
That's right. I totally missed that !
I'll come with a patch.
Thanks !!
Richard.