* [PATCH] atmel_serial: Atmel RS485 support v2
@ 2010-03-29 7:16 Claudio Scordino
2010-03-29 19:44 ` Ryan Mallon
0 siblings, 1 reply; 43+ messages in thread
From: Claudio Scordino @ 2010-03-29 7:16 UTC (permalink / raw)
To: Linux Kernel
Cc: linux-arm-kernel, John Nicholls, Rick Bronson,
Sebastian Heutling, michael trimarchi, alan, rmk, Ryan Mallon,
hskinnemoen, linux
Hi all,
this is the new version of the patch to add RS485 support to the
atmel_serial driver. It's been changed according to Ryan Mallon's
comments.
This new patch has been tested again (with and without DMA) by Sebastian
Heutling (CC:-ed).
Many thanks to all,
Claudio
atmel_serial: Atmel RS485 support
Signed-off-by: Claudio Scordino <claudio@evidence.eu.com>
Signed-off-by: Michael Trimarchi <michael@evidence.eu.com>
Signed-off-by: Rick Bronson <rick@efn.org>
Signed-off-by: Sebastian Heutling <Sebastian.Heutling@who-ing.de>
---
arch/arm/include/asm/ioctls.h | 2 +
arch/arm/mach-at91/include/mach/board.h | 2 +
drivers/serial/atmel_serial.c | 244 ++++++++++++++++++++++++++-----
3 files changed, 212 insertions(+), 36 deletions(-)
diff --git a/arch/arm/include/asm/ioctls.h b/arch/arm/include/asm/ioctls.h
index a91d8a1..82f2177 100644
--- a/arch/arm/include/asm/ioctls.h
+++ b/arch/arm/include/asm/ioctls.h
@@ -70,6 +70,8 @@
#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
#define FIOQSIZE 0x545E
+#define TIOCSRS485 0x5461
+
/* Used for packet mode */
#define TIOCPKT_DATA 0
#define TIOCPKT_FLUSHREAD 1
diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
index ceaec6c..fd8a385 100644
--- a/arch/arm/mach-at91/include/mach/board.h
+++ b/arch/arm/mach-at91/include/mach/board.h
@@ -39,6 +39,7 @@
#include <linux/usb/atmel_usba_udc.h>
#include <linux/atmel-mci.h>
#include <sound/atmel-ac97c.h>
+#include <linux/serial.h>
/* USB Device */
struct at91_udc_data {
@@ -146,6 +147,7 @@ struct atmel_uart_data {
short use_dma_tx; /* use transmit DMA? */
short use_dma_rx; /* use receive DMA? */
void __iomem *regs; /* virtual base address, if any */
+ struct serial_rs485 rs485; /* rs485 settings */
};
extern void __init at91_add_device_serial(void);
diff --git a/drivers/serial/atmel_serial.c b/drivers/serial/atmel_serial.c
index 2c9bf9b..826a885 100644
--- a/drivers/serial/atmel_serial.c
+++ b/drivers/serial/atmel_serial.c
@@ -38,6 +38,7 @@
#include <linux/dma-mapping.h>
#include <linux/atmel_pdc.h>
#include <linux/atmel_serial.h>
+#include <linux/uaccess.h>
#include <asm/io.h>
@@ -59,6 +60,9 @@
#include <linux/serial_core.h>
+static void atmel_start_rx(struct uart_port *port);
+static void atmel_stop_rx(struct uart_port *port);
+
#ifdef CONFIG_SERIAL_ATMEL_TTYAT
/* Use device name ttyAT, major 204 and minor 154-169. This is necessary if we
@@ -93,6 +97,7 @@
#define UART_GET_BRGR(port) __raw_readl((port)->membase + ATMEL_US_BRGR)
#define UART_PUT_BRGR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_BRGR)
#define UART_PUT_RTOR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_RTOR)
+#define UART_PUT_TTGR(port, v) __raw_writel(v, (port)->membase + ATMEL_US_TTGR)
/* PDC registers */
#define UART_PUT_PTCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_PTCR)
@@ -147,6 +152,10 @@ struct atmel_uart_port {
unsigned int irq_status_prev;
struct circ_buf rx_ring;
+
+ struct serial_rs485 rs485; /* rs485 settings */
+
+ unsigned int tx_done_mask;
};
static struct atmel_uart_port atmel_ports[ATMEL_MAX_UART];
@@ -187,6 +196,81 @@ static bool atmel_use_dma_tx(struct uart_port *port)
}
#endif
+/* Enable or disable the rs485 support */
+void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf)
+{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ unsigned long flags;
+ unsigned int mode;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ mode = UART_GET_MR(port);
+
+ /* Resetting serial mode to RS232 (0x0) */
+ mode &= ~ATMEL_US_USMODE;
+
+ atmel_port->rs485 = *rs485conf;
+
+ if (rs485conf->flags & SER_RS485_ENABLED) {
+ dev_dbg(port->dev, "Setting UART to RS485\n");
+ atmel_port->tx_done_mask = ATMEL_US_TXEMPTY;
+ UART_PUT_TTGR(port, rs485conf->delay_rts_before_send);
+ mode |= ATMEL_US_USMODE_RS485;
+ } else {
+ dev_dbg(port->dev, "Setting UART to RS232\n");
+ if (atmel_use_dma_tx(port))
+ atmel_port->tx_done_mask = ATMEL_US_ENDTX | \
+ ATMEL_US_TXBUFE;
+ else
+ atmel_port->tx_done_mask = ATMEL_US_TXRDY;
+ }
+ UART_PUT_MR(port, mode);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+
+static ssize_t show_rs485(struct device *dev, struct device_attribute *attr, \
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct uart_port *port = platform_get_drvdata(pdev);
+ unsigned int current_mode;
+
+ current_mode = UART_GET_MR(port) & ATMEL_US_USMODE;
+ return snprintf(buf, PAGE_SIZE, "%u\n", current_mode);
+}
+
+static ssize_t set_rs485(struct device *dev, struct device_attribute *attr, \
+ const char *buf, size_t len)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct uart_port *port = platform_get_drvdata(pdev);
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ struct serial_rs485 rs485conf = atmel_port->rs485;
+ unsigned int value;
+
+ if (!buf)
+ return -EINVAL;
+
+ value = !!(simple_strtoul(buf, NULL, 0));
+
+ if (value) {
+ rs485conf.flags |= SER_RS485_ENABLED;
+ /* 0x4 is the normal reset value. */
+ rs485conf.delay_rts_before_send = 0x00000004;
+ } else {
+ rs485conf.flags &= ~(SER_RS485_ENABLED);
+ }
+
+ atmel_config_rs485(port, &rs485conf);
+
+ return strnlen(buf, PAGE_SIZE);
+}
+
+static DEVICE_ATTR(rs485, 0644, show_rs485, set_rs485);
+
/*
* Return TIOCSER_TEMT when transmitter FIFO and Shift register is empty.
*/
@@ -202,6 +286,7 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
{
unsigned int control = 0;
unsigned int mode;
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
#ifdef CONFIG_ARCH_AT91RM9200
if (cpu_is_at91rm9200()) {
@@ -236,6 +321,17 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
mode |= ATMEL_US_CHMODE_LOC_LOOP;
else
mode |= ATMEL_US_CHMODE_NORMAL;
+
+ /* Resetting serial mode to RS232 (0x0) */
+ mode &= ~ATMEL_US_USMODE;
+
+ if (atmel_port->rs485.flags & SER_RS485_ENABLED) {
+ dev_dbg(port->dev, "Keeping UART to RS485\n");
+ UART_PUT_TTGR(port, atmel_port->rs485.delay_rts_before_send);
+ mode |= ATMEL_US_USMODE_RS485;
+ } else {
+ dev_dbg(port->dev, "Keeping UART to RS232\n");
+ }
UART_PUT_MR(port, mode);
}
@@ -268,12 +364,17 @@ static u_int atmel_get_mctrl(struct uart_port *port)
*/
static void atmel_stop_tx(struct uart_port *port)
{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
if (atmel_use_dma_tx(port)) {
/* disable PDC transmit */
UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS);
- UART_PUT_IDR(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE);
- } else
- UART_PUT_IDR(port, ATMEL_US_TXRDY);
+ }
+ /* Disable interrupts */
+ UART_PUT_IDR(port, atmel_port->tx_done_mask);
+
+ if (atmel_port->rs485.flags & SER_RS485_ENABLED)
+ atmel_start_rx(port);
}
/*
@@ -281,17 +382,39 @@ static void atmel_stop_tx(struct uart_port *port)
*/
static void atmel_start_tx(struct uart_port *port)
{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
if (atmel_use_dma_tx(port)) {
if (UART_GET_PTSR(port) & ATMEL_PDC_TXTEN)
/* The transmitter is already running. Yes, we
really need this.*/
return;
- UART_PUT_IER(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE);
+ if (atmel_port->rs485.flags & SER_RS485_ENABLED)
+ atmel_stop_rx(port);
+
/* re-enable PDC transmit */
UART_PUT_PTCR(port, ATMEL_PDC_TXTEN);
- } else
- UART_PUT_IER(port, ATMEL_US_TXRDY);
+ }
+ /* Enable interrupts */
+ UART_PUT_IER(port, atmel_port->tx_done_mask);
+}
+
+/*
+ * start receiving - port is in process of being opened.
+ */
+static void atmel_start_rx(struct uart_port *port)
+{
+ UART_PUT_CR(port, ATMEL_US_RSTSTA); /* reset status and receiver */
+
+ if (atmel_use_dma_rx(port)) {
+ /* enable PDC controller */
+ UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT | \
+ port->read_status_mask);
+ UART_PUT_PTCR(port, ATMEL_PDC_RXTEN);
+ } else {
+ UART_PUT_IER(port, ATMEL_US_RXRDY);
+ }
}
/*
@@ -302,9 +425,11 @@ static void atmel_stop_rx(struct uart_port *port)
if (atmel_use_dma_rx(port)) {
/* disable PDC receive */
UART_PUT_PTCR(port, ATMEL_PDC_RXTDIS);
- UART_PUT_IDR(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT);
- } else
+ UART_PUT_IDR(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT | \
+ port->read_status_mask);
+ } else {
UART_PUT_IDR(port, ATMEL_US_RXRDY);
+ }
}
/*
@@ -428,8 +553,9 @@ static void atmel_rx_chars(struct uart_port *port)
static void atmel_tx_chars(struct uart_port *port)
{
struct circ_buf *xmit = &port->state->xmit;
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
- if (port->x_char && UART_GET_CSR(port) & ATMEL_US_TXRDY) {
+ if (port->x_char && UART_GET_CSR(port) & atmel_port->tx_done_mask) {
UART_PUT_CHAR(port, port->x_char);
port->icount.tx++;
port->x_char = 0;
@@ -437,7 +563,7 @@ static void atmel_tx_chars(struct uart_port *port)
if (uart_circ_empty(xmit) || uart_tx_stopped(port))
return;
- while (UART_GET_CSR(port) & ATMEL_US_TXRDY) {
+ while (UART_GET_CSR(port) & atmel_port->tx_done_mask) {
UART_PUT_CHAR(port, xmit->buf[xmit->tail]);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
port->icount.tx++;
@@ -449,7 +575,8 @@ static void atmel_tx_chars(struct uart_port *port)
uart_write_wakeup(port);
if (!uart_circ_empty(xmit))
- UART_PUT_IER(port, ATMEL_US_TXRDY);
+ /* Enable interrupts */
+ UART_PUT_IER(port, atmel_port->tx_done_mask);
}
/*
@@ -501,18 +628,10 @@ atmel_handle_transmit(struct uart_port *port, unsigned int pending)
{
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
- if (atmel_use_dma_tx(port)) {
- /* PDC transmit */
- if (pending & (ATMEL_US_ENDTX | ATMEL_US_TXBUFE)) {
- UART_PUT_IDR(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE);
- tasklet_schedule(&atmel_port->tasklet);
- }
- } else {
- /* Interrupt transmit */
- if (pending & ATMEL_US_TXRDY) {
- UART_PUT_IDR(port, ATMEL_US_TXRDY);
- tasklet_schedule(&atmel_port->tasklet);
- }
+ if (pending & atmel_port->tx_done_mask) {
+ /* Either PDC or interrupt transmission */
+ UART_PUT_IDR(port, atmel_port->tx_done_mask);
+ tasklet_schedule(&atmel_port->tasklet);
}
}
@@ -590,9 +709,15 @@ static void atmel_tx_dma(struct uart_port *port)
UART_PUT_TPR(port, pdc->dma_addr + xmit->tail);
UART_PUT_TCR(port, count);
- /* re-enable PDC transmit and interrupts */
+ /* re-enable PDC transmit */
UART_PUT_PTCR(port, ATMEL_PDC_TXTEN);
- UART_PUT_IER(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE);
+ /* Enable interrupts */
+ UART_PUT_IER(port, atmel_port->tx_done_mask);
+ } else {
+ if (atmel_port->rs485.flags & SER_RS485_ENABLED) {
+ /* DMA done, stop TX, start RX for RS485 */
+ atmel_start_rx(port);
+ }
}
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
@@ -1017,6 +1142,7 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
{
unsigned long flags;
unsigned int mode, imr, quot, baud;
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
/* Get current mode register */
mode = UART_GET_MR(port) & ~(ATMEL_US_USCLKS | ATMEL_US_CHRL
@@ -1115,6 +1241,17 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
/* disable receiver and transmitter */
UART_PUT_CR(port, ATMEL_US_TXDIS | ATMEL_US_RXDIS);
+ /* Resetting serial mode to RS232 (0x0) */
+ mode &= ~ATMEL_US_USMODE;
+
+ if (atmel_port->rs485.flags & SER_RS485_ENABLED) {
+ dev_dbg(port->dev, "Keeping UART to RS485\n");
+ UART_PUT_TTGR(port, atmel_port->rs485.delay_rts_before_send);
+ mode |= ATMEL_US_USMODE_RS485;
+ } else {
+ dev_dbg(port->dev, "Keeping UART to RS232\n");
+ }
+
/* set the parity, stop bits and data size */
UART_PUT_MR(port, mode);
@@ -1231,6 +1368,30 @@ static void atmel_poll_put_char(struct uart_port *port, unsigned char ch)
}
#endif
+static int
+atmel_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg)
+{
+ struct serial_rs485 rs485conf;
+
+ switch (cmd) {
+ case TIOCSRS485:
+ if (copy_from_user(&rs485conf, (struct serial_rs485 *) arg, \
+ sizeof(rs485conf)))
+ return -EFAULT;
+
+ dev_dbg(port->dev, "enable e/o disable rs485\n");
+
+ atmel_config_rs485(port, &rs485conf);
+ break;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+
+
static struct uart_ops atmel_pops = {
.tx_empty = atmel_tx_empty,
.set_mctrl = atmel_set_mctrl,
@@ -1250,6 +1411,7 @@ static struct uart_ops atmel_pops = {
.config_port = atmel_config_port,
.verify_port = atmel_verify_port,
.pm = atmel_serial_pm,
+ .ioctl = atmel_ioctl,
#ifdef CONFIG_CONSOLE_POLL
.poll_get_char = atmel_poll_get_char,
.poll_put_char = atmel_poll_put_char,
@@ -1265,13 +1427,12 @@ static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port,
struct uart_port *port = &atmel_port->uart;
struct atmel_uart_data *data = pdev->dev.platform_data;
- port->iotype = UPIO_MEM;
- port->flags = UPF_BOOT_AUTOCONF;
- port->ops = &atmel_pops;
- port->fifosize = 1;
- port->line = pdev->id;
- port->dev = &pdev->dev;
-
+ port->iotype = UPIO_MEM;
+ port->flags = UPF_BOOT_AUTOCONF;
+ port->ops = &atmel_pops;
+ port->fifosize = 1;
+ port->line = pdev->id;
+ port->dev = &pdev->dev;
port->mapbase = pdev->resource[0].start;
port->irq = pdev->resource[1].start;
@@ -1299,8 +1460,16 @@ static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port,
atmel_port->use_dma_rx = data->use_dma_rx;
atmel_port->use_dma_tx = data->use_dma_tx;
- if (atmel_use_dma_tx(port))
+ atmel_port->rs485 = data->rs485;
+ /* Use TXEMPTY for interrupt when rs485 else TXRDY or ENDTX|TXBUFE */
+ if (atmel_port->rs485.flags & SER_RS485_ENABLED)
+ atmel_port->tx_done_mask = ATMEL_US_TXEMPTY;
+ else if (atmel_use_dma_tx(port)) {
port->fifosize = PDC_BUFFER_SIZE;
+ atmel_port->tx_done_mask = ATMEL_US_ENDTX | ATMEL_US_TXBUFE;
+ } else {
+ atmel_port->tx_done_mask = ATMEL_US_TXRDY;
+ }
}
/*
@@ -1334,6 +1503,7 @@ static void atmel_console_putchar(struct uart_port *port, int ch)
static void atmel_console_write(struct console *co, const char *s, u_int count)
{
struct uart_port *port = &atmel_ports[co->index].uart;
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
unsigned int status, imr;
unsigned int pdc_tx;
@@ -1341,7 +1511,7 @@ static void atmel_console_write(struct console *co, const char *s, u_int count)
* First, save IMR and then disable interrupts
*/
imr = UART_GET_IMR(port);
- UART_PUT_IDR(port, ATMEL_US_RXRDY | ATMEL_US_TXRDY);
+ UART_PUT_IDR(port, ATMEL_US_RXRDY | atmel_port->tx_done_mask);
/* Store PDC transmit status and disable it */
pdc_tx = UART_GET_PTSR(port) & ATMEL_PDC_TXTEN;
@@ -1355,7 +1525,7 @@ static void atmel_console_write(struct console *co, const char *s, u_int count)
*/
do {
status = UART_GET_CSR(port);
- } while (!(status & ATMEL_US_TXRDY));
+ } while (!(status & atmel_port->tx_done_mask));
/* Restore PDC transmit status */
if (pdc_tx)
@@ -1587,7 +1757,7 @@ static int __devinit atmel_serial_probe(struct platform_device *pdev)
device_init_wakeup(&pdev->dev, 1);
platform_set_drvdata(pdev, port);
- return 0;
+ return device_create_file(&(pdev->dev), &dev_attr_rs485);
err_add_port:
kfree(port->rx_ring.buf);
@@ -1619,6 +1789,8 @@ static int __devexit atmel_serial_remove(struct platform_device *pdev)
clk_put(atmel_port->clk);
+ device_remove_file(&(pdev->dev), &dev_attr_rs485);
+
return ret;
}
--
1.6.0.4
^ permalink raw reply related [flat|nested] 43+ messages in thread
* Re: [PATCH] atmel_serial: Atmel RS485 support v2
2010-03-29 7:16 [PATCH] atmel_serial: Atmel RS485 support v2 Claudio Scordino
@ 2010-03-29 19:44 ` Ryan Mallon
2010-03-30 9:07 ` Claudio Scordino
0 siblings, 1 reply; 43+ messages in thread
From: Ryan Mallon @ 2010-03-29 19:44 UTC (permalink / raw)
To: Claudio Scordino
Cc: Linux Kernel, linux-arm-kernel, John Nicholls, Rick Bronson,
Sebastian Heutling, michael trimarchi, alan, rmk, hskinnemoen,
linux
Claudio Scordino wrote:
> Hi all,
>
> this is the new version of the patch to add RS485 support to the
> atmel_serial driver. It's been changed according to Ryan Mallon's
> comments.
>
> This new patch has been tested again (with and without DMA) by Sebastian
> Heutling (CC:-ed).
A few more, mostly nitpicky, comments below.
> Many thanks to all,
>
> Claudio
>
>
> atmel_serial: Atmel RS485 support
>
> Signed-off-by: Claudio Scordino <claudio@evidence.eu.com>
> Signed-off-by: Michael Trimarchi <michael@evidence.eu.com>
> Signed-off-by: Rick Bronson <rick@efn.org>
> Signed-off-by: Sebastian Heutling <Sebastian.Heutling@who-ing.de>
> ---
> arch/arm/include/asm/ioctls.h | 2 +
> arch/arm/mach-at91/include/mach/board.h | 2 +
> drivers/serial/atmel_serial.c | 244 ++++++++++++++++++++++++++-----
> 3 files changed, 212 insertions(+), 36 deletions(-)
>
> diff --git a/arch/arm/include/asm/ioctls.h b/arch/arm/include/asm/ioctls.h
> index a91d8a1..82f2177 100644
> --- a/arch/arm/include/asm/ioctls.h
> +++ b/arch/arm/include/asm/ioctls.h
> @@ -70,6 +70,8 @@
> #define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
> #define FIOQSIZE 0x545E
>
> +#define TIOCSRS485 0x5461
> +
> /* Used for packet mode */
> #define TIOCPKT_DATA 0
> #define TIOCPKT_FLUSHREAD 1
> diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
> index ceaec6c..fd8a385 100644
> --- a/arch/arm/mach-at91/include/mach/board.h
> +++ b/arch/arm/mach-at91/include/mach/board.h
> @@ -39,6 +39,7 @@
> #include <linux/usb/atmel_usba_udc.h>
> #include <linux/atmel-mci.h>
> #include <sound/atmel-ac97c.h>
> +#include <linux/serial.h>
>
> /* USB Device */
> struct at91_udc_data {
> @@ -146,6 +147,7 @@ struct atmel_uart_data {
> short use_dma_tx; /* use transmit DMA? */
> short use_dma_rx; /* use receive DMA? */
> void __iomem *regs; /* virtual base address, if any */
> + struct serial_rs485 rs485; /* rs485 settings */
> };
Can you re-indent this structure so the rest of the members line up with
the new rs485 one.
> extern void __init at91_add_device_serial(void);
>
> diff --git a/drivers/serial/atmel_serial.c b/drivers/serial/atmel_serial.c
> index 2c9bf9b..826a885 100644
> --- a/drivers/serial/atmel_serial.c
> +++ b/drivers/serial/atmel_serial.c
> @@ -38,6 +38,7 @@
> #include <linux/dma-mapping.h>
> #include <linux/atmel_pdc.h>
> #include <linux/atmel_serial.h>
> +#include <linux/uaccess.h>
>
> #include <asm/io.h>
>
> @@ -59,6 +60,9 @@
>
> #include <linux/serial_core.h>
>
> +static void atmel_start_rx(struct uart_port *port);
> +static void atmel_stop_rx(struct uart_port *port);
> +
> #ifdef CONFIG_SERIAL_ATMEL_TTYAT
>
> /* Use device name ttyAT, major 204 and minor 154-169. This is necessary if we
> @@ -93,6 +97,7 @@
> #define UART_GET_BRGR(port) __raw_readl((port)->membase + ATMEL_US_BRGR)
> #define UART_PUT_BRGR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_BRGR)
> #define UART_PUT_RTOR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_RTOR)
> +#define UART_PUT_TTGR(port, v) __raw_writel(v, (port)->membase + ATMEL_US_TTGR)
>
> /* PDC registers */
> #define UART_PUT_PTCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_PTCR)
> @@ -147,6 +152,10 @@ struct atmel_uart_port {
> unsigned int irq_status_prev;
>
> struct circ_buf rx_ring;
> +
> + struct serial_rs485 rs485; /* rs485 settings */
> +
> + unsigned int tx_done_mask;
> };
Do we need all the whitespace here?
> static struct atmel_uart_port atmel_ports[ATMEL_MAX_UART];
> @@ -187,6 +196,81 @@ static bool atmel_use_dma_tx(struct uart_port *port)
> }
> #endif
>
> +/* Enable or disable the rs485 support */
> +void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf)
> +{
> + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
> + unsigned long flags;
> + unsigned int mode;
> +
> + spin_lock_irqsave(&port->lock, flags);
> +
> + mode = UART_GET_MR(port);
> +
> + /* Resetting serial mode to RS232 (0x0) */
> + mode &= ~ATMEL_US_USMODE;
> +
> + atmel_port->rs485 = *rs485conf;
> +
> + if (rs485conf->flags & SER_RS485_ENABLED) {
> + dev_dbg(port->dev, "Setting UART to RS485\n");
> + atmel_port->tx_done_mask = ATMEL_US_TXEMPTY;
> + UART_PUT_TTGR(port, rs485conf->delay_rts_before_send);
> + mode |= ATMEL_US_USMODE_RS485;
> + } else {
> + dev_dbg(port->dev, "Setting UART to RS232\n");
> + if (atmel_use_dma_tx(port))
> + atmel_port->tx_done_mask = ATMEL_US_ENDTX | \
> + ATMEL_US_TXBUFE;
You only need to use backslashes on split lines in macro definitions.
> + else
> + atmel_port->tx_done_mask = ATMEL_US_TXRDY;
> + }
> + UART_PUT_MR(port, mode);
> +
> + spin_unlock_irqrestore(&port->lock, flags);
> +}
> +
> +
> +static ssize_t show_rs485(struct device *dev, struct device_attribute *attr, \
> + char *buf)
> +{
Same here, and a couple more places.
> + struct platform_device *pdev = to_platform_device(dev);
> + struct uart_port *port = platform_get_drvdata(pdev);
> + unsigned int current_mode;
> +
> + current_mode = UART_GET_MR(port) & ATMEL_US_USMODE;
> + return snprintf(buf, PAGE_SIZE, "%u\n", current_mode);
> +}
> +
> +static ssize_t set_rs485(struct device *dev, struct device_attribute *attr, \
> + const char *buf, size_t len)
> +{
> + struct platform_device *pdev = to_platform_device(dev);
> + struct uart_port *port = platform_get_drvdata(pdev);
> + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
> + struct serial_rs485 rs485conf = atmel_port->rs485;
> + unsigned int value;
> +
> + if (!buf)
> + return -EINVAL;
> +
> + value = !!(simple_strtoul(buf, NULL, 0));
> +
> + if (value) {
> + rs485conf.flags |= SER_RS485_ENABLED;
> + /* 0x4 is the normal reset value. */
> + rs485conf.delay_rts_before_send = 0x00000004;
> + } else {
> + rs485conf.flags &= ~(SER_RS485_ENABLED);
Don't need the parenthesis here.
> + }
> +
> + atmel_config_rs485(port, &rs485conf);
> +
> + return strnlen(buf, PAGE_SIZE);
> +}
> +
> +static DEVICE_ATTR(rs485, 0644, show_rs485, set_rs485);
> +
> /*
> * Return TIOCSER_TEMT when transmitter FIFO and Shift register is empty.
> */
> @@ -202,6 +286,7 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
> {
> unsigned int control = 0;
> unsigned int mode;
> + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
>
> #ifdef CONFIG_ARCH_AT91RM9200
> if (cpu_is_at91rm9200()) {
> @@ -236,6 +321,17 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
> mode |= ATMEL_US_CHMODE_LOC_LOOP;
> else
> mode |= ATMEL_US_CHMODE_NORMAL;
> +
> + /* Resetting serial mode to RS232 (0x0) */
> + mode &= ~ATMEL_US_USMODE;
> +
> + if (atmel_port->rs485.flags & SER_RS485_ENABLED) {
> + dev_dbg(port->dev, "Keeping UART to RS485\n");
"Setting" UART to RS485 probably makes more sense here (and couple of
other places).
> + UART_PUT_TTGR(port, atmel_port->rs485.delay_rts_before_send);
> + mode |= ATMEL_US_USMODE_RS485;
> + } else {
> + dev_dbg(port->dev, "Keeping UART to RS232\n");
> + }
> UART_PUT_MR(port, mode);
> }
>
> @@ -268,12 +364,17 @@ static u_int atmel_get_mctrl(struct uart_port *port)
> */
> static void atmel_stop_tx(struct uart_port *port)
> {
> + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
> +
> if (atmel_use_dma_tx(port)) {
> /* disable PDC transmit */
> UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS);
> - UART_PUT_IDR(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE);
> - } else
> - UART_PUT_IDR(port, ATMEL_US_TXRDY);
> + }
> + /* Disable interrupts */
> + UART_PUT_IDR(port, atmel_port->tx_done_mask);
> +
> + if (atmel_port->rs485.flags & SER_RS485_ENABLED)
> + atmel_start_rx(port);
> }
>
> /*
> @@ -281,17 +382,39 @@ static void atmel_stop_tx(struct uart_port *port)
> */
> static void atmel_start_tx(struct uart_port *port)
> {
> + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
> +
> if (atmel_use_dma_tx(port)) {
> if (UART_GET_PTSR(port) & ATMEL_PDC_TXTEN)
> /* The transmitter is already running. Yes, we
> really need this.*/
> return;
>
> - UART_PUT_IER(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE);
> + if (atmel_port->rs485.flags & SER_RS485_ENABLED)
> + atmel_stop_rx(port);
> +
> /* re-enable PDC transmit */
> UART_PUT_PTCR(port, ATMEL_PDC_TXTEN);
> - } else
> - UART_PUT_IER(port, ATMEL_US_TXRDY);
> + }
> + /* Enable interrupts */
> + UART_PUT_IER(port, atmel_port->tx_done_mask);
> +}
> +
> +/*
> + * start receiving - port is in process of being opened.
> + */
> +static void atmel_start_rx(struct uart_port *port)
> +{
> + UART_PUT_CR(port, ATMEL_US_RSTSTA); /* reset status and receiver */
> +
> + if (atmel_use_dma_rx(port)) {
> + /* enable PDC controller */
> + UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT | \
> + port->read_status_mask);
> + UART_PUT_PTCR(port, ATMEL_PDC_RXTEN);
> + } else {
> + UART_PUT_IER(port, ATMEL_US_RXRDY);
> + }
> }
>
> /*
> @@ -302,9 +425,11 @@ static void atmel_stop_rx(struct uart_port *port)
> if (atmel_use_dma_rx(port)) {
> /* disable PDC receive */
> UART_PUT_PTCR(port, ATMEL_PDC_RXTDIS);
> - UART_PUT_IDR(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT);
> - } else
> + UART_PUT_IDR(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT | \
> + port->read_status_mask);
> + } else {
> UART_PUT_IDR(port, ATMEL_US_RXRDY);
> + }
> }
>
> /*
> @@ -428,8 +553,9 @@ static void atmel_rx_chars(struct uart_port *port)
> static void atmel_tx_chars(struct uart_port *port)
> {
> struct circ_buf *xmit = &port->state->xmit;
> + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
>
> - if (port->x_char && UART_GET_CSR(port) & ATMEL_US_TXRDY) {
> + if (port->x_char && UART_GET_CSR(port) & atmel_port->tx_done_mask) {
> UART_PUT_CHAR(port, port->x_char);
> port->icount.tx++;
> port->x_char = 0;
> @@ -437,7 +563,7 @@ static void atmel_tx_chars(struct uart_port *port)
> if (uart_circ_empty(xmit) || uart_tx_stopped(port))
> return;
>
> - while (UART_GET_CSR(port) & ATMEL_US_TXRDY) {
> + while (UART_GET_CSR(port) & atmel_port->tx_done_mask) {
> UART_PUT_CHAR(port, xmit->buf[xmit->tail]);
> xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
> port->icount.tx++;
> @@ -449,7 +575,8 @@ static void atmel_tx_chars(struct uart_port *port)
> uart_write_wakeup(port);
>
> if (!uart_circ_empty(xmit))
> - UART_PUT_IER(port, ATMEL_US_TXRDY);
> + /* Enable interrupts */
> + UART_PUT_IER(port, atmel_port->tx_done_mask);
> }
>
> /*
> @@ -501,18 +628,10 @@ atmel_handle_transmit(struct uart_port *port, unsigned int pending)
> {
> struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
>
> - if (atmel_use_dma_tx(port)) {
> - /* PDC transmit */
> - if (pending & (ATMEL_US_ENDTX | ATMEL_US_TXBUFE)) {
> - UART_PUT_IDR(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE);
> - tasklet_schedule(&atmel_port->tasklet);
> - }
> - } else {
> - /* Interrupt transmit */
> - if (pending & ATMEL_US_TXRDY) {
> - UART_PUT_IDR(port, ATMEL_US_TXRDY);
> - tasklet_schedule(&atmel_port->tasklet);
> - }
> + if (pending & atmel_port->tx_done_mask) {
> + /* Either PDC or interrupt transmission */
> + UART_PUT_IDR(port, atmel_port->tx_done_mask);
> + tasklet_schedule(&atmel_port->tasklet);
> }
> }
>
> @@ -590,9 +709,15 @@ static void atmel_tx_dma(struct uart_port *port)
>
> UART_PUT_TPR(port, pdc->dma_addr + xmit->tail);
> UART_PUT_TCR(port, count);
> - /* re-enable PDC transmit and interrupts */
> + /* re-enable PDC transmit */
> UART_PUT_PTCR(port, ATMEL_PDC_TXTEN);
> - UART_PUT_IER(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE);
> + /* Enable interrupts */
> + UART_PUT_IER(port, atmel_port->tx_done_mask);
> + } else {
> + if (atmel_port->rs485.flags & SER_RS485_ENABLED) {
> + /* DMA done, stop TX, start RX for RS485 */
> + atmel_start_rx(port);
> + }
> }
>
> if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> @@ -1017,6 +1142,7 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
> {
> unsigned long flags;
> unsigned int mode, imr, quot, baud;
> + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
>
> /* Get current mode register */
> mode = UART_GET_MR(port) & ~(ATMEL_US_USCLKS | ATMEL_US_CHRL
> @@ -1115,6 +1241,17 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
> /* disable receiver and transmitter */
> UART_PUT_CR(port, ATMEL_US_TXDIS | ATMEL_US_RXDIS);
>
> + /* Resetting serial mode to RS232 (0x0) */
> + mode &= ~ATMEL_US_USMODE;
> +
> + if (atmel_port->rs485.flags & SER_RS485_ENABLED) {
> + dev_dbg(port->dev, "Keeping UART to RS485\n");
> + UART_PUT_TTGR(port, atmel_port->rs485.delay_rts_before_send);
> + mode |= ATMEL_US_USMODE_RS485;
> + } else {
> + dev_dbg(port->dev, "Keeping UART to RS232\n");
> + }
> +
> /* set the parity, stop bits and data size */
> UART_PUT_MR(port, mode);
>
> @@ -1231,6 +1368,30 @@ static void atmel_poll_put_char(struct uart_port *port, unsigned char ch)
> }
> #endif
>
> +static int
> +atmel_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg)
> +{
> + struct serial_rs485 rs485conf;
> +
> + switch (cmd) {
> + case TIOCSRS485:
> + if (copy_from_user(&rs485conf, (struct serial_rs485 *) arg, \
> + sizeof(rs485conf)))
> + return -EFAULT;
> +
> + dev_dbg(port->dev, "enable e/o disable rs485\n");
You could remove this dev_dbg since amtel_config_rs485 already prints
debug information.
> +
> + atmel_config_rs485(port, &rs485conf);
> + break;
> +
> + default:
> + return -ENOIOCTLCMD;
> + }
> + return 0;
> +}
> +
> +
> +
> static struct uart_ops atmel_pops = {
> .tx_empty = atmel_tx_empty,
> .set_mctrl = atmel_set_mctrl,
> @@ -1250,6 +1411,7 @@ static struct uart_ops atmel_pops = {
> .config_port = atmel_config_port,
> .verify_port = atmel_verify_port,
> .pm = atmel_serial_pm,
> + .ioctl = atmel_ioctl,
> #ifdef CONFIG_CONSOLE_POLL
> .poll_get_char = atmel_poll_get_char,
> .poll_put_char = atmel_poll_put_char,
> @@ -1265,13 +1427,12 @@ static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port,
> struct uart_port *port = &atmel_port->uart;
> struct atmel_uart_data *data = pdev->dev.platform_data;
>
> - port->iotype = UPIO_MEM;
> - port->flags = UPF_BOOT_AUTOCONF;
> - port->ops = &atmel_pops;
> - port->fifosize = 1;
> - port->line = pdev->id;
> - port->dev = &pdev->dev;
> -
> + port->iotype = UPIO_MEM;
> + port->flags = UPF_BOOT_AUTOCONF;
> + port->ops = &atmel_pops;
> + port->fifosize = 1;
> + port->line = pdev->id;
> + port->dev = &pdev->dev;
> port->mapbase = pdev->resource[0].start;
> port->irq = pdev->resource[1].start;
>
> @@ -1299,8 +1460,16 @@ static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port,
>
> atmel_port->use_dma_rx = data->use_dma_rx;
> atmel_port->use_dma_tx = data->use_dma_tx;
> - if (atmel_use_dma_tx(port))
> + atmel_port->rs485 = data->rs485;
> + /* Use TXEMPTY for interrupt when rs485 else TXRDY or ENDTX|TXBUFE */
> + if (atmel_port->rs485.flags & SER_RS485_ENABLED)
> + atmel_port->tx_done_mask = ATMEL_US_TXEMPTY;
> + else if (atmel_use_dma_tx(port)) {
> port->fifosize = PDC_BUFFER_SIZE;
> + atmel_port->tx_done_mask = ATMEL_US_ENDTX | ATMEL_US_TXBUFE;
> + } else {
> + atmel_port->tx_done_mask = ATMEL_US_TXRDY;
> + }
> }
>
> /*
> @@ -1334,6 +1503,7 @@ static void atmel_console_putchar(struct uart_port *port, int ch)
> static void atmel_console_write(struct console *co, const char *s, u_int count)
> {
> struct uart_port *port = &atmel_ports[co->index].uart;
> + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
> unsigned int status, imr;
> unsigned int pdc_tx;
>
> @@ -1341,7 +1511,7 @@ static void atmel_console_write(struct console *co, const char *s, u_int count)
> * First, save IMR and then disable interrupts
> */
> imr = UART_GET_IMR(port);
> - UART_PUT_IDR(port, ATMEL_US_RXRDY | ATMEL_US_TXRDY);
> + UART_PUT_IDR(port, ATMEL_US_RXRDY | atmel_port->tx_done_mask);
>
> /* Store PDC transmit status and disable it */
> pdc_tx = UART_GET_PTSR(port) & ATMEL_PDC_TXTEN;
> @@ -1355,7 +1525,7 @@ static void atmel_console_write(struct console *co, const char *s, u_int count)
> */
> do {
> status = UART_GET_CSR(port);
> - } while (!(status & ATMEL_US_TXRDY));
> + } while (!(status & atmel_port->tx_done_mask));
>
> /* Restore PDC transmit status */
> if (pdc_tx)
> @@ -1587,7 +1757,7 @@ static int __devinit atmel_serial_probe(struct platform_device *pdev)
> device_init_wakeup(&pdev->dev, 1);
> platform_set_drvdata(pdev, port);
>
> - return 0;
> + return device_create_file(&(pdev->dev), &dev_attr_rs485);
>
> err_add_port:
> kfree(port->rx_ring.buf);
> @@ -1619,6 +1789,8 @@ static int __devexit atmel_serial_remove(struct platform_device *pdev)
>
> clk_put(atmel_port->clk);
>
> + device_remove_file(&(pdev->dev), &dev_attr_rs485);
> +
> return ret;
> }
>
~Ryan
--
Bluewater Systems Ltd - ARM Technology Solution Centre
Ryan Mallon 5 Amuri Park, 404 Barbadoes St
ryan@bluewatersys.com PO Box 13 889, Christchurch 8013
http://www.bluewatersys.com New Zealand
Phone: +64 3 3779127 Freecall: Australia 1800 148 751
Fax: +64 3 3779135 USA 1800 261 2934
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH] atmel_serial: Atmel RS485 support v2
2010-03-29 19:44 ` Ryan Mallon
@ 2010-03-30 9:07 ` Claudio Scordino
2010-03-30 19:37 ` Ryan Mallon
0 siblings, 1 reply; 43+ messages in thread
From: Claudio Scordino @ 2010-03-30 9:07 UTC (permalink / raw)
To: Ryan Mallon
Cc: Linux Kernel, linux-arm-kernel, John Nicholls, Rick Bronson,
Sebastian Heutling, michael trimarchi, alan, rmk, hskinnemoen,
linux
Ryan Mallon ha scritto:
> Claudio Scordino wrote:
>> Hi all,
>>
>> this is the new version of the patch to add RS485 support to the
>> atmel_serial driver. It's been changed according to Ryan Mallon's
>> comments.
>>
>> This new patch has been tested again (with and without DMA) by Sebastian
>> Heutling (CC:-ed).
>
> A few more, mostly nitpicky, comments below.
Hi Ryan,
many thanks for your feedback.
Please find attached a new version of the patch, modified according to
your comments.
Regards,
Claudio
atmel_serial: Atmel RS485 support
Signed-off-by: Claudio Scordino <claudio@evidence.eu.com>
Signed-off-by: Michael Trimarchi <michael@evidence.eu.com>
Signed-off-by: Rick Bronson <rick@efn.org>
Signed-off-by: Sebastian Heutling <Sebastian.Heutling@who-ing.de>
---
arch/arm/include/asm/ioctls.h | 2 +
arch/arm/mach-at91/include/mach/board.h | 8 +-
drivers/serial/atmel_serial.c | 241 ++++++++++++++++++++++++++-----
3 files changed, 212 insertions(+), 39 deletions(-)
diff --git a/arch/arm/include/asm/ioctls.h b/arch/arm/include/asm/ioctls.h
index a91d8a1..82f2177 100644
--- a/arch/arm/include/asm/ioctls.h
+++ b/arch/arm/include/asm/ioctls.h
@@ -70,6 +70,8 @@
#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
#define FIOQSIZE 0x545E
+#define TIOCSRS485 0x5461
+
/* Used for packet mode */
#define TIOCPKT_DATA 0
#define TIOCPKT_FLUSHREAD 1
diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
index ceaec6c..df2ed84 100644
--- a/arch/arm/mach-at91/include/mach/board.h
+++ b/arch/arm/mach-at91/include/mach/board.h
@@ -39,6 +39,7 @@
#include <linux/usb/atmel_usba_udc.h>
#include <linux/atmel-mci.h>
#include <sound/atmel-ac97c.h>
+#include <linux/serial.h>
/* USB Device */
struct at91_udc_data {
@@ -143,9 +144,10 @@ extern struct platform_device *atmel_default_console_device;
extern void __init __deprecated at91_init_serial(struct at91_uart_config *config);
struct atmel_uart_data {
- short use_dma_tx; /* use transmit DMA? */
- short use_dma_rx; /* use receive DMA? */
- void __iomem *regs; /* virtual base address, if any */
+ short use_dma_tx; /* use transmit DMA? */
+ short use_dma_rx; /* use receive DMA? */
+ void __iomem *regs; /* virt. base address, if any */
+ struct serial_rs485 rs485; /* rs485 settings */
};
extern void __init at91_add_device_serial(void);
diff --git a/drivers/serial/atmel_serial.c b/drivers/serial/atmel_serial.c
index 2c9bf9b..039cd65 100644
--- a/drivers/serial/atmel_serial.c
+++ b/drivers/serial/atmel_serial.c
@@ -38,6 +38,7 @@
#include <linux/dma-mapping.h>
#include <linux/atmel_pdc.h>
#include <linux/atmel_serial.h>
+#include <linux/uaccess.h>
#include <asm/io.h>
@@ -59,6 +60,9 @@
#include <linux/serial_core.h>
+static void atmel_start_rx(struct uart_port *port);
+static void atmel_stop_rx(struct uart_port *port);
+
#ifdef CONFIG_SERIAL_ATMEL_TTYAT
/* Use device name ttyAT, major 204 and minor 154-169. This is necessary if we
@@ -93,6 +97,7 @@
#define UART_GET_BRGR(port) __raw_readl((port)->membase + ATMEL_US_BRGR)
#define UART_PUT_BRGR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_BRGR)
#define UART_PUT_RTOR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_RTOR)
+#define UART_PUT_TTGR(port, v) __raw_writel(v, (port)->membase + ATMEL_US_TTGR)
/* PDC registers */
#define UART_PUT_PTCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_PTCR)
@@ -147,6 +152,9 @@ struct atmel_uart_port {
unsigned int irq_status_prev;
struct circ_buf rx_ring;
+
+ struct serial_rs485 rs485; /* rs485 settings */
+ unsigned int tx_done_mask;
};
static struct atmel_uart_port atmel_ports[ATMEL_MAX_UART];
@@ -187,6 +195,81 @@ static bool atmel_use_dma_tx(struct uart_port *port)
}
#endif
+/* Enable or disable the rs485 support */
+void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf)
+{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ unsigned long flags;
+ unsigned int mode;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ mode = UART_GET_MR(port);
+
+ /* Resetting serial mode to RS232 (0x0) */
+ mode &= ~ATMEL_US_USMODE;
+
+ atmel_port->rs485 = *rs485conf;
+
+ if (rs485conf->flags & SER_RS485_ENABLED) {
+ dev_dbg(port->dev, "Setting UART to RS485\n");
+ atmel_port->tx_done_mask = ATMEL_US_TXEMPTY;
+ UART_PUT_TTGR(port, rs485conf->delay_rts_before_send);
+ mode |= ATMEL_US_USMODE_RS485;
+ } else {
+ dev_dbg(port->dev, "Setting UART to RS232\n");
+ if (atmel_use_dma_tx(port))
+ atmel_port->tx_done_mask = ATMEL_US_ENDTX |
+ ATMEL_US_TXBUFE;
+ else
+ atmel_port->tx_done_mask = ATMEL_US_TXRDY;
+ }
+ UART_PUT_MR(port, mode);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+
+static ssize_t show_rs485(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct uart_port *port = platform_get_drvdata(pdev);
+ unsigned int current_mode;
+
+ current_mode = UART_GET_MR(port) & ATMEL_US_USMODE;
+ return snprintf(buf, PAGE_SIZE, "%u\n", current_mode);
+}
+
+static ssize_t set_rs485(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct uart_port *port = platform_get_drvdata(pdev);
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ struct serial_rs485 rs485conf = atmel_port->rs485;
+ unsigned int value;
+
+ if (!buf)
+ return -EINVAL;
+
+ value = !!(simple_strtoul(buf, NULL, 0));
+
+ if (value) {
+ rs485conf.flags |= SER_RS485_ENABLED;
+ /* 0x4 is the normal reset value. */
+ rs485conf.delay_rts_before_send = 0x00000004;
+ } else {
+ rs485conf.flags &= ~SER_RS485_ENABLED;
+ }
+
+ atmel_config_rs485(port, &rs485conf);
+
+ return strnlen(buf, PAGE_SIZE);
+}
+
+static DEVICE_ATTR(rs485, 0644, show_rs485, set_rs485);
+
/*
* Return TIOCSER_TEMT when transmitter FIFO and Shift register is empty.
*/
@@ -202,6 +285,7 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
{
unsigned int control = 0;
unsigned int mode;
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
#ifdef CONFIG_ARCH_AT91RM9200
if (cpu_is_at91rm9200()) {
@@ -236,6 +320,17 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
mode |= ATMEL_US_CHMODE_LOC_LOOP;
else
mode |= ATMEL_US_CHMODE_NORMAL;
+
+ /* Resetting serial mode to RS232 (0x0) */
+ mode &= ~ATMEL_US_USMODE;
+
+ if (atmel_port->rs485.flags & SER_RS485_ENABLED) {
+ dev_dbg(port->dev, "Setting UART to RS485\n");
+ UART_PUT_TTGR(port, atmel_port->rs485.delay_rts_before_send);
+ mode |= ATMEL_US_USMODE_RS485;
+ } else {
+ dev_dbg(port->dev, "Setting UART to RS232\n");
+ }
UART_PUT_MR(port, mode);
}
@@ -268,12 +363,17 @@ static u_int atmel_get_mctrl(struct uart_port *port)
*/
static void atmel_stop_tx(struct uart_port *port)
{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
if (atmel_use_dma_tx(port)) {
/* disable PDC transmit */
UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS);
- UART_PUT_IDR(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE);
- } else
- UART_PUT_IDR(port, ATMEL_US_TXRDY);
+ }
+ /* Disable interrupts */
+ UART_PUT_IDR(port, atmel_port->tx_done_mask);
+
+ if (atmel_port->rs485.flags & SER_RS485_ENABLED)
+ atmel_start_rx(port);
}
/*
@@ -281,17 +381,39 @@ static void atmel_stop_tx(struct uart_port *port)
*/
static void atmel_start_tx(struct uart_port *port)
{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
if (atmel_use_dma_tx(port)) {
if (UART_GET_PTSR(port) & ATMEL_PDC_TXTEN)
/* The transmitter is already running. Yes, we
really need this.*/
return;
- UART_PUT_IER(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE);
+ if (atmel_port->rs485.flags & SER_RS485_ENABLED)
+ atmel_stop_rx(port);
+
/* re-enable PDC transmit */
UART_PUT_PTCR(port, ATMEL_PDC_TXTEN);
- } else
- UART_PUT_IER(port, ATMEL_US_TXRDY);
+ }
+ /* Enable interrupts */
+ UART_PUT_IER(port, atmel_port->tx_done_mask);
+}
+
+/*
+ * start receiving - port is in process of being opened.
+ */
+static void atmel_start_rx(struct uart_port *port)
+{
+ UART_PUT_CR(port, ATMEL_US_RSTSTA); /* reset status and receiver */
+
+ if (atmel_use_dma_rx(port)) {
+ /* enable PDC controller */
+ UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT |
+ port->read_status_mask);
+ UART_PUT_PTCR(port, ATMEL_PDC_RXTEN);
+ } else {
+ UART_PUT_IER(port, ATMEL_US_RXRDY);
+ }
}
/*
@@ -302,9 +424,11 @@ static void atmel_stop_rx(struct uart_port *port)
if (atmel_use_dma_rx(port)) {
/* disable PDC receive */
UART_PUT_PTCR(port, ATMEL_PDC_RXTDIS);
- UART_PUT_IDR(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT);
- } else
+ UART_PUT_IDR(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT |
+ port->read_status_mask);
+ } else {
UART_PUT_IDR(port, ATMEL_US_RXRDY);
+ }
}
/*
@@ -428,8 +552,9 @@ static void atmel_rx_chars(struct uart_port *port)
static void atmel_tx_chars(struct uart_port *port)
{
struct circ_buf *xmit = &port->state->xmit;
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
- if (port->x_char && UART_GET_CSR(port) & ATMEL_US_TXRDY) {
+ if (port->x_char && UART_GET_CSR(port) & atmel_port->tx_done_mask) {
UART_PUT_CHAR(port, port->x_char);
port->icount.tx++;
port->x_char = 0;
@@ -437,7 +562,7 @@ static void atmel_tx_chars(struct uart_port *port)
if (uart_circ_empty(xmit) || uart_tx_stopped(port))
return;
- while (UART_GET_CSR(port) & ATMEL_US_TXRDY) {
+ while (UART_GET_CSR(port) & atmel_port->tx_done_mask) {
UART_PUT_CHAR(port, xmit->buf[xmit->tail]);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
port->icount.tx++;
@@ -449,7 +574,8 @@ static void atmel_tx_chars(struct uart_port *port)
uart_write_wakeup(port);
if (!uart_circ_empty(xmit))
- UART_PUT_IER(port, ATMEL_US_TXRDY);
+ /* Enable interrupts */
+ UART_PUT_IER(port, atmel_port->tx_done_mask);
}
/*
@@ -501,18 +627,10 @@ atmel_handle_transmit(struct uart_port *port, unsigned int pending)
{
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
- if (atmel_use_dma_tx(port)) {
- /* PDC transmit */
- if (pending & (ATMEL_US_ENDTX | ATMEL_US_TXBUFE)) {
- UART_PUT_IDR(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE);
- tasklet_schedule(&atmel_port->tasklet);
- }
- } else {
- /* Interrupt transmit */
- if (pending & ATMEL_US_TXRDY) {
- UART_PUT_IDR(port, ATMEL_US_TXRDY);
- tasklet_schedule(&atmel_port->tasklet);
- }
+ if (pending & atmel_port->tx_done_mask) {
+ /* Either PDC or interrupt transmission */
+ UART_PUT_IDR(port, atmel_port->tx_done_mask);
+ tasklet_schedule(&atmel_port->tasklet);
}
}
@@ -590,9 +708,15 @@ static void atmel_tx_dma(struct uart_port *port)
UART_PUT_TPR(port, pdc->dma_addr + xmit->tail);
UART_PUT_TCR(port, count);
- /* re-enable PDC transmit and interrupts */
+ /* re-enable PDC transmit */
UART_PUT_PTCR(port, ATMEL_PDC_TXTEN);
- UART_PUT_IER(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE);
+ /* Enable interrupts */
+ UART_PUT_IER(port, atmel_port->tx_done_mask);
+ } else {
+ if (atmel_port->rs485.flags & SER_RS485_ENABLED) {
+ /* DMA done, stop TX, start RX for RS485 */
+ atmel_start_rx(port);
+ }
}
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
@@ -1017,6 +1141,7 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
{
unsigned long flags;
unsigned int mode, imr, quot, baud;
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
/* Get current mode register */
mode = UART_GET_MR(port) & ~(ATMEL_US_USCLKS | ATMEL_US_CHRL
@@ -1115,6 +1240,17 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
/* disable receiver and transmitter */
UART_PUT_CR(port, ATMEL_US_TXDIS | ATMEL_US_RXDIS);
+ /* Resetting serial mode to RS232 (0x0) */
+ mode &= ~ATMEL_US_USMODE;
+
+ if (atmel_port->rs485.flags & SER_RS485_ENABLED) {
+ dev_dbg(port->dev, "Setting UART to RS485\n");
+ UART_PUT_TTGR(port, atmel_port->rs485.delay_rts_before_send);
+ mode |= ATMEL_US_USMODE_RS485;
+ } else {
+ dev_dbg(port->dev, "Setting UART to RS232\n");
+ }
+
/* set the parity, stop bits and data size */
UART_PUT_MR(port, mode);
@@ -1231,6 +1367,28 @@ static void atmel_poll_put_char(struct uart_port *port, unsigned char ch)
}
#endif
+static int
+atmel_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg)
+{
+ struct serial_rs485 rs485conf;
+
+ switch (cmd) {
+ case TIOCSRS485:
+ if (copy_from_user(&rs485conf, (struct serial_rs485 *) arg,
+ sizeof(rs485conf)))
+ return -EFAULT;
+
+ atmel_config_rs485(port, &rs485conf);
+ break;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+
+
static struct uart_ops atmel_pops = {
.tx_empty = atmel_tx_empty,
.set_mctrl = atmel_set_mctrl,
@@ -1250,6 +1408,7 @@ static struct uart_ops atmel_pops = {
.config_port = atmel_config_port,
.verify_port = atmel_verify_port,
.pm = atmel_serial_pm,
+ .ioctl = atmel_ioctl,
#ifdef CONFIG_CONSOLE_POLL
.poll_get_char = atmel_poll_get_char,
.poll_put_char = atmel_poll_put_char,
@@ -1265,13 +1424,12 @@ static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port,
struct uart_port *port = &atmel_port->uart;
struct atmel_uart_data *data = pdev->dev.platform_data;
- port->iotype = UPIO_MEM;
- port->flags = UPF_BOOT_AUTOCONF;
- port->ops = &atmel_pops;
- port->fifosize = 1;
- port->line = pdev->id;
- port->dev = &pdev->dev;
-
+ port->iotype = UPIO_MEM;
+ port->flags = UPF_BOOT_AUTOCONF;
+ port->ops = &atmel_pops;
+ port->fifosize = 1;
+ port->line = pdev->id;
+ port->dev = &pdev->dev;
port->mapbase = pdev->resource[0].start;
port->irq = pdev->resource[1].start;
@@ -1299,8 +1457,16 @@ static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port,
atmel_port->use_dma_rx = data->use_dma_rx;
atmel_port->use_dma_tx = data->use_dma_tx;
- if (atmel_use_dma_tx(port))
+ atmel_port->rs485 = data->rs485;
+ /* Use TXEMPTY for interrupt when rs485 else TXRDY or ENDTX|TXBUFE */
+ if (atmel_port->rs485.flags & SER_RS485_ENABLED)
+ atmel_port->tx_done_mask = ATMEL_US_TXEMPTY;
+ else if (atmel_use_dma_tx(port)) {
port->fifosize = PDC_BUFFER_SIZE;
+ atmel_port->tx_done_mask = ATMEL_US_ENDTX | ATMEL_US_TXBUFE;
+ } else {
+ atmel_port->tx_done_mask = ATMEL_US_TXRDY;
+ }
}
/*
@@ -1334,6 +1500,7 @@ static void atmel_console_putchar(struct uart_port *port, int ch)
static void atmel_console_write(struct console *co, const char *s, u_int count)
{
struct uart_port *port = &atmel_ports[co->index].uart;
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
unsigned int status, imr;
unsigned int pdc_tx;
@@ -1341,7 +1508,7 @@ static void atmel_console_write(struct console *co, const char *s, u_int count)
* First, save IMR and then disable interrupts
*/
imr = UART_GET_IMR(port);
- UART_PUT_IDR(port, ATMEL_US_RXRDY | ATMEL_US_TXRDY);
+ UART_PUT_IDR(port, ATMEL_US_RXRDY | atmel_port->tx_done_mask);
/* Store PDC transmit status and disable it */
pdc_tx = UART_GET_PTSR(port) & ATMEL_PDC_TXTEN;
@@ -1355,7 +1522,7 @@ static void atmel_console_write(struct console *co, const char *s, u_int count)
*/
do {
status = UART_GET_CSR(port);
- } while (!(status & ATMEL_US_TXRDY));
+ } while (!(status & atmel_port->tx_done_mask));
/* Restore PDC transmit status */
if (pdc_tx)
@@ -1587,7 +1754,7 @@ static int __devinit atmel_serial_probe(struct platform_device *pdev)
device_init_wakeup(&pdev->dev, 1);
platform_set_drvdata(pdev, port);
- return 0;
+ return device_create_file(&(pdev->dev), &dev_attr_rs485);
err_add_port:
kfree(port->rx_ring.buf);
@@ -1619,6 +1786,8 @@ static int __devexit atmel_serial_remove(struct platform_device *pdev)
clk_put(atmel_port->clk);
+ device_remove_file(&(pdev->dev), &dev_attr_rs485);
+
return ret;
}
--
1.6.0.4
^ permalink raw reply related [flat|nested] 43+ messages in thread
* Re: [PATCH] atmel_serial: Atmel RS485 support v2
2010-03-30 9:07 ` Claudio Scordino
@ 2010-03-30 19:37 ` Ryan Mallon
2010-04-08 7:58 ` Claudio Scordino
0 siblings, 1 reply; 43+ messages in thread
From: Ryan Mallon @ 2010-03-30 19:37 UTC (permalink / raw)
To: Claudio Scordino
Cc: Linux Kernel, linux-arm-kernel, John Nicholls, Rick Bronson,
Sebastian Heutling, michael trimarchi, alan, rmk, hskinnemoen,
linux
Claudio Scordino wrote:
> Ryan Mallon ha scritto:
>> Claudio Scordino wrote:
>>> Hi all,
>>>
>>> this is the new version of the patch to add RS485 support to the
>>> atmel_serial driver. It's been changed according to Ryan Mallon's
>>> comments.
>>>
>>> This new patch has been tested again (with and without DMA) by Sebastian
>>> Heutling (CC:-ed).
>> A few more, mostly nitpicky, comments below.
>
> Hi Ryan,
>
> many thanks for your feedback.
>
> Please find attached a new version of the patch, modified according to
> your comments.
Looks good. You should add a Tested-by for Sebastian, and you can add:
Reviewed-by: Ryan Mallon <ryan@bluewatersys.com>
~Ryan
> Regards,
>
> Claudio
>
>
> atmel_serial: Atmel RS485 support
>
> Signed-off-by: Claudio Scordino <claudio@evidence.eu.com>
> Signed-off-by: Michael Trimarchi <michael@evidence.eu.com>
> Signed-off-by: Rick Bronson <rick@efn.org>
> Signed-off-by: Sebastian Heutling <Sebastian.Heutling@who-ing.de>
> ---
> arch/arm/include/asm/ioctls.h | 2 +
> arch/arm/mach-at91/include/mach/board.h | 8 +-
> drivers/serial/atmel_serial.c | 241 ++++++++++++++++++++++++++-----
> 3 files changed, 212 insertions(+), 39 deletions(-)
>
> diff --git a/arch/arm/include/asm/ioctls.h b/arch/arm/include/asm/ioctls.h
> index a91d8a1..82f2177 100644
> --- a/arch/arm/include/asm/ioctls.h
> +++ b/arch/arm/include/asm/ioctls.h
> @@ -70,6 +70,8 @@
> #define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
> #define FIOQSIZE 0x545E
>
> +#define TIOCSRS485 0x5461
> +
> /* Used for packet mode */
> #define TIOCPKT_DATA 0
> #define TIOCPKT_FLUSHREAD 1
> diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
> index ceaec6c..df2ed84 100644
> --- a/arch/arm/mach-at91/include/mach/board.h
> +++ b/arch/arm/mach-at91/include/mach/board.h
> @@ -39,6 +39,7 @@
> #include <linux/usb/atmel_usba_udc.h>
> #include <linux/atmel-mci.h>
> #include <sound/atmel-ac97c.h>
> +#include <linux/serial.h>
>
> /* USB Device */
> struct at91_udc_data {
> @@ -143,9 +144,10 @@ extern struct platform_device *atmel_default_console_device;
> extern void __init __deprecated at91_init_serial(struct at91_uart_config *config);
>
> struct atmel_uart_data {
> - short use_dma_tx; /* use transmit DMA? */
> - short use_dma_rx; /* use receive DMA? */
> - void __iomem *regs; /* virtual base address, if any */
> + short use_dma_tx; /* use transmit DMA? */
> + short use_dma_rx; /* use receive DMA? */
> + void __iomem *regs; /* virt. base address, if any */
> + struct serial_rs485 rs485; /* rs485 settings */
> };
> extern void __init at91_add_device_serial(void);
>
> diff --git a/drivers/serial/atmel_serial.c b/drivers/serial/atmel_serial.c
> index 2c9bf9b..039cd65 100644
> --- a/drivers/serial/atmel_serial.c
> +++ b/drivers/serial/atmel_serial.c
> @@ -38,6 +38,7 @@
> #include <linux/dma-mapping.h>
> #include <linux/atmel_pdc.h>
> #include <linux/atmel_serial.h>
> +#include <linux/uaccess.h>
>
> #include <asm/io.h>
>
> @@ -59,6 +60,9 @@
>
> #include <linux/serial_core.h>
>
> +static void atmel_start_rx(struct uart_port *port);
> +static void atmel_stop_rx(struct uart_port *port);
> +
> #ifdef CONFIG_SERIAL_ATMEL_TTYAT
>
> /* Use device name ttyAT, major 204 and minor 154-169. This is necessary if we
> @@ -93,6 +97,7 @@
> #define UART_GET_BRGR(port) __raw_readl((port)->membase + ATMEL_US_BRGR)
> #define UART_PUT_BRGR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_BRGR)
> #define UART_PUT_RTOR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_RTOR)
> +#define UART_PUT_TTGR(port, v) __raw_writel(v, (port)->membase + ATMEL_US_TTGR)
>
> /* PDC registers */
> #define UART_PUT_PTCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_PTCR)
> @@ -147,6 +152,9 @@ struct atmel_uart_port {
> unsigned int irq_status_prev;
>
> struct circ_buf rx_ring;
> +
> + struct serial_rs485 rs485; /* rs485 settings */
> + unsigned int tx_done_mask;
> };
>
> static struct atmel_uart_port atmel_ports[ATMEL_MAX_UART];
> @@ -187,6 +195,81 @@ static bool atmel_use_dma_tx(struct uart_port *port)
> }
> #endif
>
> +/* Enable or disable the rs485 support */
> +void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf)
> +{
> + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
> + unsigned long flags;
> + unsigned int mode;
> +
> + spin_lock_irqsave(&port->lock, flags);
> +
> + mode = UART_GET_MR(port);
> +
> + /* Resetting serial mode to RS232 (0x0) */
> + mode &= ~ATMEL_US_USMODE;
> +
> + atmel_port->rs485 = *rs485conf;
> +
> + if (rs485conf->flags & SER_RS485_ENABLED) {
> + dev_dbg(port->dev, "Setting UART to RS485\n");
> + atmel_port->tx_done_mask = ATMEL_US_TXEMPTY;
> + UART_PUT_TTGR(port, rs485conf->delay_rts_before_send);
> + mode |= ATMEL_US_USMODE_RS485;
> + } else {
> + dev_dbg(port->dev, "Setting UART to RS232\n");
> + if (atmel_use_dma_tx(port))
> + atmel_port->tx_done_mask = ATMEL_US_ENDTX |
> + ATMEL_US_TXBUFE;
> + else
> + atmel_port->tx_done_mask = ATMEL_US_TXRDY;
> + }
> + UART_PUT_MR(port, mode);
> +
> + spin_unlock_irqrestore(&port->lock, flags);
> +}
> +
> +
> +static ssize_t show_rs485(struct device *dev, struct device_attribute *attr,
> + char *buf)
> +{
> + struct platform_device *pdev = to_platform_device(dev);
> + struct uart_port *port = platform_get_drvdata(pdev);
> + unsigned int current_mode;
> +
> + current_mode = UART_GET_MR(port) & ATMEL_US_USMODE;
> + return snprintf(buf, PAGE_SIZE, "%u\n", current_mode);
> +}
> +
> +static ssize_t set_rs485(struct device *dev, struct device_attribute *attr,
> + const char *buf, size_t len)
> +{
> + struct platform_device *pdev = to_platform_device(dev);
> + struct uart_port *port = platform_get_drvdata(pdev);
> + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
> + struct serial_rs485 rs485conf = atmel_port->rs485;
> + unsigned int value;
> +
> + if (!buf)
> + return -EINVAL;
> +
> + value = !!(simple_strtoul(buf, NULL, 0));
> +
> + if (value) {
> + rs485conf.flags |= SER_RS485_ENABLED;
> + /* 0x4 is the normal reset value. */
> + rs485conf.delay_rts_before_send = 0x00000004;
> + } else {
> + rs485conf.flags &= ~SER_RS485_ENABLED;
> + }
> +
> + atmel_config_rs485(port, &rs485conf);
> +
> + return strnlen(buf, PAGE_SIZE);
> +}
> +
> +static DEVICE_ATTR(rs485, 0644, show_rs485, set_rs485);
> +
> /*
> * Return TIOCSER_TEMT when transmitter FIFO and Shift register is empty.
> */
> @@ -202,6 +285,7 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
> {
> unsigned int control = 0;
> unsigned int mode;
> + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
>
> #ifdef CONFIG_ARCH_AT91RM9200
> if (cpu_is_at91rm9200()) {
> @@ -236,6 +320,17 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
> mode |= ATMEL_US_CHMODE_LOC_LOOP;
> else
> mode |= ATMEL_US_CHMODE_NORMAL;
> +
> + /* Resetting serial mode to RS232 (0x0) */
> + mode &= ~ATMEL_US_USMODE;
> +
> + if (atmel_port->rs485.flags & SER_RS485_ENABLED) {
> + dev_dbg(port->dev, "Setting UART to RS485\n");
> + UART_PUT_TTGR(port, atmel_port->rs485.delay_rts_before_send);
> + mode |= ATMEL_US_USMODE_RS485;
> + } else {
> + dev_dbg(port->dev, "Setting UART to RS232\n");
> + }
> UART_PUT_MR(port, mode);
> }
>
> @@ -268,12 +363,17 @@ static u_int atmel_get_mctrl(struct uart_port *port)
> */
> static void atmel_stop_tx(struct uart_port *port)
> {
> + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
> +
> if (atmel_use_dma_tx(port)) {
> /* disable PDC transmit */
> UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS);
> - UART_PUT_IDR(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE);
> - } else
> - UART_PUT_IDR(port, ATMEL_US_TXRDY);
> + }
> + /* Disable interrupts */
> + UART_PUT_IDR(port, atmel_port->tx_done_mask);
> +
> + if (atmel_port->rs485.flags & SER_RS485_ENABLED)
> + atmel_start_rx(port);
> }
>
> /*
> @@ -281,17 +381,39 @@ static void atmel_stop_tx(struct uart_port *port)
> */
> static void atmel_start_tx(struct uart_port *port)
> {
> + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
> +
> if (atmel_use_dma_tx(port)) {
> if (UART_GET_PTSR(port) & ATMEL_PDC_TXTEN)
> /* The transmitter is already running. Yes, we
> really need this.*/
> return;
>
> - UART_PUT_IER(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE);
> + if (atmel_port->rs485.flags & SER_RS485_ENABLED)
> + atmel_stop_rx(port);
> +
> /* re-enable PDC transmit */
> UART_PUT_PTCR(port, ATMEL_PDC_TXTEN);
> - } else
> - UART_PUT_IER(port, ATMEL_US_TXRDY);
> + }
> + /* Enable interrupts */
> + UART_PUT_IER(port, atmel_port->tx_done_mask);
> +}
> +
> +/*
> + * start receiving - port is in process of being opened.
> + */
> +static void atmel_start_rx(struct uart_port *port)
> +{
> + UART_PUT_CR(port, ATMEL_US_RSTSTA); /* reset status and receiver */
> +
> + if (atmel_use_dma_rx(port)) {
> + /* enable PDC controller */
> + UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT |
> + port->read_status_mask);
> + UART_PUT_PTCR(port, ATMEL_PDC_RXTEN);
> + } else {
> + UART_PUT_IER(port, ATMEL_US_RXRDY);
> + }
> }
>
> /*
> @@ -302,9 +424,11 @@ static void atmel_stop_rx(struct uart_port *port)
> if (atmel_use_dma_rx(port)) {
> /* disable PDC receive */
> UART_PUT_PTCR(port, ATMEL_PDC_RXTDIS);
> - UART_PUT_IDR(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT);
> - } else
> + UART_PUT_IDR(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT |
> + port->read_status_mask);
> + } else {
> UART_PUT_IDR(port, ATMEL_US_RXRDY);
> + }
> }
>
> /*
> @@ -428,8 +552,9 @@ static void atmel_rx_chars(struct uart_port *port)
> static void atmel_tx_chars(struct uart_port *port)
> {
> struct circ_buf *xmit = &port->state->xmit;
> + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
>
> - if (port->x_char && UART_GET_CSR(port) & ATMEL_US_TXRDY) {
> + if (port->x_char && UART_GET_CSR(port) & atmel_port->tx_done_mask) {
> UART_PUT_CHAR(port, port->x_char);
> port->icount.tx++;
> port->x_char = 0;
> @@ -437,7 +562,7 @@ static void atmel_tx_chars(struct uart_port *port)
> if (uart_circ_empty(xmit) || uart_tx_stopped(port))
> return;
>
> - while (UART_GET_CSR(port) & ATMEL_US_TXRDY) {
> + while (UART_GET_CSR(port) & atmel_port->tx_done_mask) {
> UART_PUT_CHAR(port, xmit->buf[xmit->tail]);
> xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
> port->icount.tx++;
> @@ -449,7 +574,8 @@ static void atmel_tx_chars(struct uart_port *port)
> uart_write_wakeup(port);
>
> if (!uart_circ_empty(xmit))
> - UART_PUT_IER(port, ATMEL_US_TXRDY);
> + /* Enable interrupts */
> + UART_PUT_IER(port, atmel_port->tx_done_mask);
> }
>
> /*
> @@ -501,18 +627,10 @@ atmel_handle_transmit(struct uart_port *port, unsigned int pending)
> {
> struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
>
> - if (atmel_use_dma_tx(port)) {
> - /* PDC transmit */
> - if (pending & (ATMEL_US_ENDTX | ATMEL_US_TXBUFE)) {
> - UART_PUT_IDR(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE);
> - tasklet_schedule(&atmel_port->tasklet);
> - }
> - } else {
> - /* Interrupt transmit */
> - if (pending & ATMEL_US_TXRDY) {
> - UART_PUT_IDR(port, ATMEL_US_TXRDY);
> - tasklet_schedule(&atmel_port->tasklet);
> - }
> + if (pending & atmel_port->tx_done_mask) {
> + /* Either PDC or interrupt transmission */
> + UART_PUT_IDR(port, atmel_port->tx_done_mask);
> + tasklet_schedule(&atmel_port->tasklet);
> }
> }
>
> @@ -590,9 +708,15 @@ static void atmel_tx_dma(struct uart_port *port)
>
> UART_PUT_TPR(port, pdc->dma_addr + xmit->tail);
> UART_PUT_TCR(port, count);
> - /* re-enable PDC transmit and interrupts */
> + /* re-enable PDC transmit */
> UART_PUT_PTCR(port, ATMEL_PDC_TXTEN);
> - UART_PUT_IER(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE);
> + /* Enable interrupts */
> + UART_PUT_IER(port, atmel_port->tx_done_mask);
> + } else {
> + if (atmel_port->rs485.flags & SER_RS485_ENABLED) {
> + /* DMA done, stop TX, start RX for RS485 */
> + atmel_start_rx(port);
> + }
> }
>
> if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> @@ -1017,6 +1141,7 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
> {
> unsigned long flags;
> unsigned int mode, imr, quot, baud;
> + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
>
> /* Get current mode register */
> mode = UART_GET_MR(port) & ~(ATMEL_US_USCLKS | ATMEL_US_CHRL
> @@ -1115,6 +1240,17 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
> /* disable receiver and transmitter */
> UART_PUT_CR(port, ATMEL_US_TXDIS | ATMEL_US_RXDIS);
>
> + /* Resetting serial mode to RS232 (0x0) */
> + mode &= ~ATMEL_US_USMODE;
> +
> + if (atmel_port->rs485.flags & SER_RS485_ENABLED) {
> + dev_dbg(port->dev, "Setting UART to RS485\n");
> + UART_PUT_TTGR(port, atmel_port->rs485.delay_rts_before_send);
> + mode |= ATMEL_US_USMODE_RS485;
> + } else {
> + dev_dbg(port->dev, "Setting UART to RS232\n");
> + }
> +
> /* set the parity, stop bits and data size */
> UART_PUT_MR(port, mode);
>
> @@ -1231,6 +1367,28 @@ static void atmel_poll_put_char(struct uart_port *port, unsigned char ch)
> }
> #endif
>
> +static int
> +atmel_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg)
> +{
> + struct serial_rs485 rs485conf;
> +
> + switch (cmd) {
> + case TIOCSRS485:
> + if (copy_from_user(&rs485conf, (struct serial_rs485 *) arg,
> + sizeof(rs485conf)))
> + return -EFAULT;
> +
> + atmel_config_rs485(port, &rs485conf);
> + break;
> +
> + default:
> + return -ENOIOCTLCMD;
> + }
> + return 0;
> +}
> +
> +
> +
> static struct uart_ops atmel_pops = {
> .tx_empty = atmel_tx_empty,
> .set_mctrl = atmel_set_mctrl,
> @@ -1250,6 +1408,7 @@ static struct uart_ops atmel_pops = {
> .config_port = atmel_config_port,
> .verify_port = atmel_verify_port,
> .pm = atmel_serial_pm,
> + .ioctl = atmel_ioctl,
> #ifdef CONFIG_CONSOLE_POLL
> .poll_get_char = atmel_poll_get_char,
> .poll_put_char = atmel_poll_put_char,
> @@ -1265,13 +1424,12 @@ static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port,
> struct uart_port *port = &atmel_port->uart;
> struct atmel_uart_data *data = pdev->dev.platform_data;
>
> - port->iotype = UPIO_MEM;
> - port->flags = UPF_BOOT_AUTOCONF;
> - port->ops = &atmel_pops;
> - port->fifosize = 1;
> - port->line = pdev->id;
> - port->dev = &pdev->dev;
> -
> + port->iotype = UPIO_MEM;
> + port->flags = UPF_BOOT_AUTOCONF;
> + port->ops = &atmel_pops;
> + port->fifosize = 1;
> + port->line = pdev->id;
> + port->dev = &pdev->dev;
> port->mapbase = pdev->resource[0].start;
> port->irq = pdev->resource[1].start;
>
> @@ -1299,8 +1457,16 @@ static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port,
>
> atmel_port->use_dma_rx = data->use_dma_rx;
> atmel_port->use_dma_tx = data->use_dma_tx;
> - if (atmel_use_dma_tx(port))
> + atmel_port->rs485 = data->rs485;
> + /* Use TXEMPTY for interrupt when rs485 else TXRDY or ENDTX|TXBUFE */
> + if (atmel_port->rs485.flags & SER_RS485_ENABLED)
> + atmel_port->tx_done_mask = ATMEL_US_TXEMPTY;
> + else if (atmel_use_dma_tx(port)) {
> port->fifosize = PDC_BUFFER_SIZE;
> + atmel_port->tx_done_mask = ATMEL_US_ENDTX | ATMEL_US_TXBUFE;
> + } else {
> + atmel_port->tx_done_mask = ATMEL_US_TXRDY;
> + }
> }
>
> /*
> @@ -1334,6 +1500,7 @@ static void atmel_console_putchar(struct uart_port *port, int ch)
> static void atmel_console_write(struct console *co, const char *s, u_int count)
> {
> struct uart_port *port = &atmel_ports[co->index].uart;
> + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
> unsigned int status, imr;
> unsigned int pdc_tx;
>
> @@ -1341,7 +1508,7 @@ static void atmel_console_write(struct console *co, const char *s, u_int count)
> * First, save IMR and then disable interrupts
> */
> imr = UART_GET_IMR(port);
> - UART_PUT_IDR(port, ATMEL_US_RXRDY | ATMEL_US_TXRDY);
> + UART_PUT_IDR(port, ATMEL_US_RXRDY | atmel_port->tx_done_mask);
>
> /* Store PDC transmit status and disable it */
> pdc_tx = UART_GET_PTSR(port) & ATMEL_PDC_TXTEN;
> @@ -1355,7 +1522,7 @@ static void atmel_console_write(struct console *co, const char *s, u_int count)
> */
> do {
> status = UART_GET_CSR(port);
> - } while (!(status & ATMEL_US_TXRDY));
> + } while (!(status & atmel_port->tx_done_mask));
>
> /* Restore PDC transmit status */
> if (pdc_tx)
> @@ -1587,7 +1754,7 @@ static int __devinit atmel_serial_probe(struct platform_device *pdev)
> device_init_wakeup(&pdev->dev, 1);
> platform_set_drvdata(pdev, port);
>
> - return 0;
> + return device_create_file(&(pdev->dev), &dev_attr_rs485);
>
> err_add_port:
> kfree(port->rx_ring.buf);
> @@ -1619,6 +1786,8 @@ static int __devexit atmel_serial_remove(struct platform_device *pdev)
>
> clk_put(atmel_port->clk);
>
> + device_remove_file(&(pdev->dev), &dev_attr_rs485);
> +
> return ret;
> }
>
--
Bluewater Systems Ltd - ARM Technology Solution Centre
Ryan Mallon 5 Amuri Park, 404 Barbadoes St
ryan@bluewatersys.com PO Box 13 889, Christchurch 8013
http://www.bluewatersys.com New Zealand
Phone: +64 3 3779127 Freecall: Australia 1800 148 751
Fax: +64 3 3779135 USA 1800 261 2934
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH] atmel_serial: Atmel RS485 support v2
2010-03-30 19:37 ` Ryan Mallon
@ 2010-04-08 7:58 ` Claudio Scordino
2010-04-08 9:01 ` Russell King - ARM Linux
2010-04-08 10:13 ` Alan Cox
0 siblings, 2 replies; 43+ messages in thread
From: Claudio Scordino @ 2010-04-08 7:58 UTC (permalink / raw)
To: Ryan Mallon
Cc: Linux Kernel, linux-arm-kernel, John Nicholls, Rick Bronson,
Sebastian Heutling, michael trimarchi, alan, rmk, hskinnemoen,
linux
Ryan Mallon ha scritto:
> Claudio Scordino wrote:
>> Ryan Mallon ha scritto:
>>> Claudio Scordino wrote:
>>>> Hi all,
>>>>
>>>> this is the new version of the patch to add RS485 support to the
>>>> atmel_serial driver. It's been changed according to Ryan Mallon's
>>>> comments.
>>>>
>>>> This new patch has been tested again (with and without DMA) by Sebastian
>>>> Heutling (CC:-ed).
>>> A few more, mostly nitpicky, comments below.
>> Hi Ryan,
>>
>> many thanks for your feedback.
>>
>> Please find attached a new version of the patch, modified according to
>> your comments.
>
> Looks good. You should add a Tested-by for Sebastian, and you can add:
>
> Reviewed-by: Ryan Mallon <ryan@bluewatersys.com>
>
Here it is (I just added the Tested-by and Reviewed-by fields).
Best regards,
Claudio
atmel_serial: Atmel RS485 support
Signed-off-by: Claudio Scordino <claudio@evidence.eu.com>
Signed-off-by: Michael Trimarchi <michael@evidence.eu.com>
Signed-off-by: Rick Bronson <rick@efn.org>
Signed-off-by: Sebastian Heutling <Sebastian.Heutling@who-ing.de>
Tested-by: Sebastian Heutling <Sebastian.Heutling@who-ing.de>
Reviewed-by: Ryan Mallon <ryan@bluewatersys.com>
---
arch/arm/include/asm/ioctls.h | 2 +
arch/arm/mach-at91/include/mach/board.h | 8 +-
drivers/serial/atmel_serial.c | 241 ++++++++++++++++++++++++++-----
3 files changed, 212 insertions(+), 39 deletions(-)
diff --git a/arch/arm/include/asm/ioctls.h b/arch/arm/include/asm/ioctls.h
index a91d8a1..82f2177 100644
--- a/arch/arm/include/asm/ioctls.h
+++ b/arch/arm/include/asm/ioctls.h
@@ -70,6 +70,8 @@
#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
#define FIOQSIZE 0x545E
+#define TIOCSRS485 0x5461
+
/* Used for packet mode */
#define TIOCPKT_DATA 0
#define TIOCPKT_FLUSHREAD 1
diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
index ceaec6c..df2ed84 100644
--- a/arch/arm/mach-at91/include/mach/board.h
+++ b/arch/arm/mach-at91/include/mach/board.h
@@ -39,6 +39,7 @@
#include <linux/usb/atmel_usba_udc.h>
#include <linux/atmel-mci.h>
#include <sound/atmel-ac97c.h>
+#include <linux/serial.h>
/* USB Device */
struct at91_udc_data {
@@ -143,9 +144,10 @@ extern struct platform_device *atmel_default_console_device;
extern void __init __deprecated at91_init_serial(struct at91_uart_config *config);
struct atmel_uart_data {
- short use_dma_tx; /* use transmit DMA? */
- short use_dma_rx; /* use receive DMA? */
- void __iomem *regs; /* virtual base address, if any */
+ short use_dma_tx; /* use transmit DMA? */
+ short use_dma_rx; /* use receive DMA? */
+ void __iomem *regs; /* virt. base address, if any */
+ struct serial_rs485 rs485; /* rs485 settings */
};
extern void __init at91_add_device_serial(void);
diff --git a/drivers/serial/atmel_serial.c b/drivers/serial/atmel_serial.c
index 2c9bf9b..039cd65 100644
--- a/drivers/serial/atmel_serial.c
+++ b/drivers/serial/atmel_serial.c
@@ -38,6 +38,7 @@
#include <linux/dma-mapping.h>
#include <linux/atmel_pdc.h>
#include <linux/atmel_serial.h>
+#include <linux/uaccess.h>
#include <asm/io.h>
@@ -59,6 +60,9 @@
#include <linux/serial_core.h>
+static void atmel_start_rx(struct uart_port *port);
+static void atmel_stop_rx(struct uart_port *port);
+
#ifdef CONFIG_SERIAL_ATMEL_TTYAT
/* Use device name ttyAT, major 204 and minor 154-169. This is necessary if we
@@ -93,6 +97,7 @@
#define UART_GET_BRGR(port) __raw_readl((port)->membase + ATMEL_US_BRGR)
#define UART_PUT_BRGR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_BRGR)
#define UART_PUT_RTOR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_RTOR)
+#define UART_PUT_TTGR(port, v) __raw_writel(v, (port)->membase + ATMEL_US_TTGR)
/* PDC registers */
#define UART_PUT_PTCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_PTCR)
@@ -147,6 +152,9 @@ struct atmel_uart_port {
unsigned int irq_status_prev;
struct circ_buf rx_ring;
+
+ struct serial_rs485 rs485; /* rs485 settings */
+ unsigned int tx_done_mask;
};
static struct atmel_uart_port atmel_ports[ATMEL_MAX_UART];
@@ -187,6 +195,81 @@ static bool atmel_use_dma_tx(struct uart_port *port)
}
#endif
+/* Enable or disable the rs485 support */
+void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf)
+{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ unsigned long flags;
+ unsigned int mode;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ mode = UART_GET_MR(port);
+
+ /* Resetting serial mode to RS232 (0x0) */
+ mode &= ~ATMEL_US_USMODE;
+
+ atmel_port->rs485 = *rs485conf;
+
+ if (rs485conf->flags & SER_RS485_ENABLED) {
+ dev_dbg(port->dev, "Setting UART to RS485\n");
+ atmel_port->tx_done_mask = ATMEL_US_TXEMPTY;
+ UART_PUT_TTGR(port, rs485conf->delay_rts_before_send);
+ mode |= ATMEL_US_USMODE_RS485;
+ } else {
+ dev_dbg(port->dev, "Setting UART to RS232\n");
+ if (atmel_use_dma_tx(port))
+ atmel_port->tx_done_mask = ATMEL_US_ENDTX |
+ ATMEL_US_TXBUFE;
+ else
+ atmel_port->tx_done_mask = ATMEL_US_TXRDY;
+ }
+ UART_PUT_MR(port, mode);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+
+static ssize_t show_rs485(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct uart_port *port = platform_get_drvdata(pdev);
+ unsigned int current_mode;
+
+ current_mode = UART_GET_MR(port) & ATMEL_US_USMODE;
+ return snprintf(buf, PAGE_SIZE, "%u\n", current_mode);
+}
+
+static ssize_t set_rs485(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct uart_port *port = platform_get_drvdata(pdev);
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ struct serial_rs485 rs485conf = atmel_port->rs485;
+ unsigned int value;
+
+ if (!buf)
+ return -EINVAL;
+
+ value = !!(simple_strtoul(buf, NULL, 0));
+
+ if (value) {
+ rs485conf.flags |= SER_RS485_ENABLED;
+ /* 0x4 is the normal reset value. */
+ rs485conf.delay_rts_before_send = 0x00000004;
+ } else {
+ rs485conf.flags &= ~SER_RS485_ENABLED;
+ }
+
+ atmel_config_rs485(port, &rs485conf);
+
+ return strnlen(buf, PAGE_SIZE);
+}
+
+static DEVICE_ATTR(rs485, 0644, show_rs485, set_rs485);
+
/*
* Return TIOCSER_TEMT when transmitter FIFO and Shift register is empty.
*/
@@ -202,6 +285,7 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
{
unsigned int control = 0;
unsigned int mode;
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
#ifdef CONFIG_ARCH_AT91RM9200
if (cpu_is_at91rm9200()) {
@@ -236,6 +320,17 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
mode |= ATMEL_US_CHMODE_LOC_LOOP;
else
mode |= ATMEL_US_CHMODE_NORMAL;
+
+ /* Resetting serial mode to RS232 (0x0) */
+ mode &= ~ATMEL_US_USMODE;
+
+ if (atmel_port->rs485.flags & SER_RS485_ENABLED) {
+ dev_dbg(port->dev, "Setting UART to RS485\n");
+ UART_PUT_TTGR(port, atmel_port->rs485.delay_rts_before_send);
+ mode |= ATMEL_US_USMODE_RS485;
+ } else {
+ dev_dbg(port->dev, "Setting UART to RS232\n");
+ }
UART_PUT_MR(port, mode);
}
@@ -268,12 +363,17 @@ static u_int atmel_get_mctrl(struct uart_port *port)
*/
static void atmel_stop_tx(struct uart_port *port)
{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
if (atmel_use_dma_tx(port)) {
/* disable PDC transmit */
UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS);
- UART_PUT_IDR(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE);
- } else
- UART_PUT_IDR(port, ATMEL_US_TXRDY);
+ }
+ /* Disable interrupts */
+ UART_PUT_IDR(port, atmel_port->tx_done_mask);
+
+ if (atmel_port->rs485.flags & SER_RS485_ENABLED)
+ atmel_start_rx(port);
}
/*
@@ -281,17 +381,39 @@ static void atmel_stop_tx(struct uart_port *port)
*/
static void atmel_start_tx(struct uart_port *port)
{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
if (atmel_use_dma_tx(port)) {
if (UART_GET_PTSR(port) & ATMEL_PDC_TXTEN)
/* The transmitter is already running. Yes, we
really need this.*/
return;
- UART_PUT_IER(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE);
+ if (atmel_port->rs485.flags & SER_RS485_ENABLED)
+ atmel_stop_rx(port);
+
/* re-enable PDC transmit */
UART_PUT_PTCR(port, ATMEL_PDC_TXTEN);
- } else
- UART_PUT_IER(port, ATMEL_US_TXRDY);
+ }
+ /* Enable interrupts */
+ UART_PUT_IER(port, atmel_port->tx_done_mask);
+}
+
+/*
+ * start receiving - port is in process of being opened.
+ */
+static void atmel_start_rx(struct uart_port *port)
+{
+ UART_PUT_CR(port, ATMEL_US_RSTSTA); /* reset status and receiver */
+
+ if (atmel_use_dma_rx(port)) {
+ /* enable PDC controller */
+ UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT |
+ port->read_status_mask);
+ UART_PUT_PTCR(port, ATMEL_PDC_RXTEN);
+ } else {
+ UART_PUT_IER(port, ATMEL_US_RXRDY);
+ }
}
/*
@@ -302,9 +424,11 @@ static void atmel_stop_rx(struct uart_port *port)
if (atmel_use_dma_rx(port)) {
/* disable PDC receive */
UART_PUT_PTCR(port, ATMEL_PDC_RXTDIS);
- UART_PUT_IDR(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT);
- } else
+ UART_PUT_IDR(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT |
+ port->read_status_mask);
+ } else {
UART_PUT_IDR(port, ATMEL_US_RXRDY);
+ }
}
/*
@@ -428,8 +552,9 @@ static void atmel_rx_chars(struct uart_port *port)
static void atmel_tx_chars(struct uart_port *port)
{
struct circ_buf *xmit = &port->state->xmit;
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
- if (port->x_char && UART_GET_CSR(port) & ATMEL_US_TXRDY) {
+ if (port->x_char && UART_GET_CSR(port) & atmel_port->tx_done_mask) {
UART_PUT_CHAR(port, port->x_char);
port->icount.tx++;
port->x_char = 0;
@@ -437,7 +562,7 @@ static void atmel_tx_chars(struct uart_port *port)
if (uart_circ_empty(xmit) || uart_tx_stopped(port))
return;
- while (UART_GET_CSR(port) & ATMEL_US_TXRDY) {
+ while (UART_GET_CSR(port) & atmel_port->tx_done_mask) {
UART_PUT_CHAR(port, xmit->buf[xmit->tail]);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
port->icount.tx++;
@@ -449,7 +574,8 @@ static void atmel_tx_chars(struct uart_port *port)
uart_write_wakeup(port);
if (!uart_circ_empty(xmit))
- UART_PUT_IER(port, ATMEL_US_TXRDY);
+ /* Enable interrupts */
+ UART_PUT_IER(port, atmel_port->tx_done_mask);
}
/*
@@ -501,18 +627,10 @@ atmel_handle_transmit(struct uart_port *port, unsigned int pending)
{
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
- if (atmel_use_dma_tx(port)) {
- /* PDC transmit */
- if (pending & (ATMEL_US_ENDTX | ATMEL_US_TXBUFE)) {
- UART_PUT_IDR(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE);
- tasklet_schedule(&atmel_port->tasklet);
- }
- } else {
- /* Interrupt transmit */
- if (pending & ATMEL_US_TXRDY) {
- UART_PUT_IDR(port, ATMEL_US_TXRDY);
- tasklet_schedule(&atmel_port->tasklet);
- }
+ if (pending & atmel_port->tx_done_mask) {
+ /* Either PDC or interrupt transmission */
+ UART_PUT_IDR(port, atmel_port->tx_done_mask);
+ tasklet_schedule(&atmel_port->tasklet);
}
}
@@ -590,9 +708,15 @@ static void atmel_tx_dma(struct uart_port *port)
UART_PUT_TPR(port, pdc->dma_addr + xmit->tail);
UART_PUT_TCR(port, count);
- /* re-enable PDC transmit and interrupts */
+ /* re-enable PDC transmit */
UART_PUT_PTCR(port, ATMEL_PDC_TXTEN);
- UART_PUT_IER(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE);
+ /* Enable interrupts */
+ UART_PUT_IER(port, atmel_port->tx_done_mask);
+ } else {
+ if (atmel_port->rs485.flags & SER_RS485_ENABLED) {
+ /* DMA done, stop TX, start RX for RS485 */
+ atmel_start_rx(port);
+ }
}
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
@@ -1017,6 +1141,7 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
{
unsigned long flags;
unsigned int mode, imr, quot, baud;
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
/* Get current mode register */
mode = UART_GET_MR(port) & ~(ATMEL_US_USCLKS | ATMEL_US_CHRL
@@ -1115,6 +1240,17 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
/* disable receiver and transmitter */
UART_PUT_CR(port, ATMEL_US_TXDIS | ATMEL_US_RXDIS);
+ /* Resetting serial mode to RS232 (0x0) */
+ mode &= ~ATMEL_US_USMODE;
+
+ if (atmel_port->rs485.flags & SER_RS485_ENABLED) {
+ dev_dbg(port->dev, "Setting UART to RS485\n");
+ UART_PUT_TTGR(port, atmel_port->rs485.delay_rts_before_send);
+ mode |= ATMEL_US_USMODE_RS485;
+ } else {
+ dev_dbg(port->dev, "Setting UART to RS232\n");
+ }
+
/* set the parity, stop bits and data size */
UART_PUT_MR(port, mode);
@@ -1231,6 +1367,28 @@ static void atmel_poll_put_char(struct uart_port *port, unsigned char ch)
}
#endif
+static int
+atmel_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg)
+{
+ struct serial_rs485 rs485conf;
+
+ switch (cmd) {
+ case TIOCSRS485:
+ if (copy_from_user(&rs485conf, (struct serial_rs485 *) arg,
+ sizeof(rs485conf)))
+ return -EFAULT;
+
+ atmel_config_rs485(port, &rs485conf);
+ break;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+
+
static struct uart_ops atmel_pops = {
.tx_empty = atmel_tx_empty,
.set_mctrl = atmel_set_mctrl,
@@ -1250,6 +1408,7 @@ static struct uart_ops atmel_pops = {
.config_port = atmel_config_port,
.verify_port = atmel_verify_port,
.pm = atmel_serial_pm,
+ .ioctl = atmel_ioctl,
#ifdef CONFIG_CONSOLE_POLL
.poll_get_char = atmel_poll_get_char,
.poll_put_char = atmel_poll_put_char,
@@ -1265,13 +1424,12 @@ static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port,
struct uart_port *port = &atmel_port->uart;
struct atmel_uart_data *data = pdev->dev.platform_data;
- port->iotype = UPIO_MEM;
- port->flags = UPF_BOOT_AUTOCONF;
- port->ops = &atmel_pops;
- port->fifosize = 1;
- port->line = pdev->id;
- port->dev = &pdev->dev;
-
+ port->iotype = UPIO_MEM;
+ port->flags = UPF_BOOT_AUTOCONF;
+ port->ops = &atmel_pops;
+ port->fifosize = 1;
+ port->line = pdev->id;
+ port->dev = &pdev->dev;
port->mapbase = pdev->resource[0].start;
port->irq = pdev->resource[1].start;
@@ -1299,8 +1457,16 @@ static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port,
atmel_port->use_dma_rx = data->use_dma_rx;
atmel_port->use_dma_tx = data->use_dma_tx;
- if (atmel_use_dma_tx(port))
+ atmel_port->rs485 = data->rs485;
+ /* Use TXEMPTY for interrupt when rs485 else TXRDY or ENDTX|TXBUFE */
+ if (atmel_port->rs485.flags & SER_RS485_ENABLED)
+ atmel_port->tx_done_mask = ATMEL_US_TXEMPTY;
+ else if (atmel_use_dma_tx(port)) {
port->fifosize = PDC_BUFFER_SIZE;
+ atmel_port->tx_done_mask = ATMEL_US_ENDTX | ATMEL_US_TXBUFE;
+ } else {
+ atmel_port->tx_done_mask = ATMEL_US_TXRDY;
+ }
}
/*
@@ -1334,6 +1500,7 @@ static void atmel_console_putchar(struct uart_port *port, int ch)
static void atmel_console_write(struct console *co, const char *s, u_int count)
{
struct uart_port *port = &atmel_ports[co->index].uart;
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
unsigned int status, imr;
unsigned int pdc_tx;
@@ -1341,7 +1508,7 @@ static void atmel_console_write(struct console *co, const char *s, u_int count)
* First, save IMR and then disable interrupts
*/
imr = UART_GET_IMR(port);
- UART_PUT_IDR(port, ATMEL_US_RXRDY | ATMEL_US_TXRDY);
+ UART_PUT_IDR(port, ATMEL_US_RXRDY | atmel_port->tx_done_mask);
/* Store PDC transmit status and disable it */
pdc_tx = UART_GET_PTSR(port) & ATMEL_PDC_TXTEN;
@@ -1355,7 +1522,7 @@ static void atmel_console_write(struct console *co, const char *s, u_int count)
*/
do {
status = UART_GET_CSR(port);
- } while (!(status & ATMEL_US_TXRDY));
+ } while (!(status & atmel_port->tx_done_mask));
/* Restore PDC transmit status */
if (pdc_tx)
@@ -1587,7 +1754,7 @@ static int __devinit atmel_serial_probe(struct platform_device *pdev)
device_init_wakeup(&pdev->dev, 1);
platform_set_drvdata(pdev, port);
- return 0;
+ return device_create_file(&(pdev->dev), &dev_attr_rs485);
err_add_port:
kfree(port->rx_ring.buf);
@@ -1619,6 +1786,8 @@ static int __devexit atmel_serial_remove(struct platform_device *pdev)
clk_put(atmel_port->clk);
+ device_remove_file(&(pdev->dev), &dev_attr_rs485);
+
return ret;
}
--
1.6.0.4
^ permalink raw reply related [flat|nested] 43+ messages in thread
* Re: [PATCH] atmel_serial: Atmel RS485 support v2
2010-04-08 7:58 ` Claudio Scordino
@ 2010-04-08 9:01 ` Russell King - ARM Linux
2010-04-08 10:13 ` Alan Cox
1 sibling, 0 replies; 43+ messages in thread
From: Russell King - ARM Linux @ 2010-04-08 9:01 UTC (permalink / raw)
To: Claudio Scordino
Cc: Ryan Mallon, Linux Kernel, linux-arm-kernel, John Nicholls,
Rick Bronson, Sebastian Heutling, michael trimarchi, alan,
hskinnemoen, linux
On Thu, Apr 08, 2010 at 09:58:21AM +0200, Claudio Scordino wrote:
> diff --git a/arch/arm/include/asm/ioctls.h b/arch/arm/include/asm/ioctls.h
> index a91d8a1..82f2177 100644
> --- a/arch/arm/include/asm/ioctls.h
> +++ b/arch/arm/include/asm/ioctls.h
> @@ -70,6 +70,8 @@
> #define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
> #define FIOQSIZE 0x545E
>
> +#define TIOCSRS485 0x5461
Why are you only supporting half the interface?
#define TIOCGRS485 0x542E
#define TIOCSRS485 0x542F
is what is in include/asm-generic/ioctls.h - and it would be a good idea
to use the same numbering as the generic file rather than creating yet
more divergence.
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH] atmel_serial: Atmel RS485 support v2
2010-04-08 7:58 ` Claudio Scordino
2010-04-08 9:01 ` Russell King - ARM Linux
@ 2010-04-08 10:13 ` Alan Cox
2010-04-08 13:16 ` Claudio Scordino
1 sibling, 1 reply; 43+ messages in thread
From: Alan Cox @ 2010-04-08 10:13 UTC (permalink / raw)
To: Claudio Scordino
Cc: Ryan Mallon, Linux Kernel, linux-arm-kernel, John Nicholls,
Rick Bronson, Sebastian Heutling, michael trimarchi, rmk,
hskinnemoen, linux
NAK - assorted problems, notably locking ones caused by the sysfs stuff
which should probably be dropped.
> +#define TIOCSRS485 0x5461
Please provide TIOCGRS485 as well
> +static ssize_t show_rs485(struct device *dev, struct device_attribute *attr,
> + char *buf)
> +{
> + struct platform_device *pdev = to_platform_device(dev);
> + struct uart_port *port = platform_get_drvdata(pdev);
> + unsigned int current_mode;
> +
> + current_mode = UART_GET_MR(port) & ATMEL_US_USMODE;
> + return snprintf(buf, PAGE_SIZE, "%u\n", current_mode);
> +}
You should have TIOCGRS485 for providing the info back as part of the API
> +
> +static ssize_t set_rs485(struct device *dev, struct device_attribute *attr,
> + const char *buf, size_t len)
Expain the locking on this could you - I don't see what protects against
parallel ioctl and sysfs stuff ?
> +static DEVICE_ATTR(rs485, 0644, show_rs485, set_rs485);
Why should this be public read ?
> + /* Resetting serial mode to RS232 (0x0) */
> + mode &= ~ATMEL_US_USMODE;
> +
> + if (atmel_port->rs485.flags & SER_RS485_ENABLED) {
> + dev_dbg(port->dev, "Setting UART to RS485\n");
> + UART_PUT_TTGR(port, atmel_port->rs485.delay_rts_before_send);
> + mode |= ATMEL_US_USMODE_RS485;
> + } else {
> + dev_dbg(port->dev, "Setting UART to RS232\n");
Locking versus sysfs ?
> + UART_PUT_IDR(port, atmel_port->tx_done_mask);
> +
> + if (atmel_port->rs485.flags & SER_RS485_ENABLED)
> + atmel_start_rx(port);
> }
Ditto
> - UART_PUT_IER(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE);
> + if (atmel_port->rs485.flags & SER_RS485_ENABLED)
> + atmel_stop_rx(port);
> +
Ditto
Given the locking mess you are going to create I would suggest dropping
the sysfs stuff.
Alan
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH] atmel_serial: Atmel RS485 support v2
2010-04-08 10:13 ` Alan Cox
@ 2010-04-08 13:16 ` Claudio Scordino
2010-04-08 13:42 ` Alan Cox
2010-05-26 13:18 ` Nicolas Ferre
0 siblings, 2 replies; 43+ messages in thread
From: Claudio Scordino @ 2010-04-08 13:16 UTC (permalink / raw)
To: Alan Cox
Cc: Ryan Mallon, Linux Kernel, linux-arm-kernel, John Nicholls,
Rick Bronson, Sebastian Heutling, michael trimarchi, rmk,
hskinnemoen, linux
Alan Cox ha scritto:
> NAK - assorted problems, notably locking ones caused by the sysfs stuff
> which should probably be dropped.
>
>> +#define TIOCSRS485 0x5461
>
> Please provide TIOCGRS485 as well
>
>
>
>> +static ssize_t show_rs485(struct device *dev, struct device_attribute *attr,
>> + char *buf)
>> +{
>> + struct platform_device *pdev = to_platform_device(dev);
>> + struct uart_port *port = platform_get_drvdata(pdev);
>> + unsigned int current_mode;
>> +
>> + current_mode = UART_GET_MR(port) & ATMEL_US_USMODE;
>> + return snprintf(buf, PAGE_SIZE, "%u\n", current_mode);
>> +}
>
> You should have TIOCGRS485 for providing the info back as part of the API
>
>> +
>> +static ssize_t set_rs485(struct device *dev, struct device_attribute *attr,
>> + const char *buf, size_t len)
>
> Expain the locking on this could you - I don't see what protects against
> parallel ioctl and sysfs stuff ?
>
>> +static DEVICE_ATTR(rs485, 0644, show_rs485, set_rs485);
>
> Why should this be public read ?
>
>> + /* Resetting serial mode to RS232 (0x0) */
>> + mode &= ~ATMEL_US_USMODE;
>> +
>> + if (atmel_port->rs485.flags & SER_RS485_ENABLED) {
>> + dev_dbg(port->dev, "Setting UART to RS485\n");
>> + UART_PUT_TTGR(port, atmel_port->rs485.delay_rts_before_send);
>> + mode |= ATMEL_US_USMODE_RS485;
>> + } else {
>> + dev_dbg(port->dev, "Setting UART to RS232\n");
>
> Locking versus sysfs ?
>
>> + UART_PUT_IDR(port, atmel_port->tx_done_mask);
>> +
>> + if (atmel_port->rs485.flags & SER_RS485_ENABLED)
>> + atmel_start_rx(port);
>> }
>
> Ditto
>
>> - UART_PUT_IER(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE);
>> + if (atmel_port->rs485.flags & SER_RS485_ENABLED)
>> + atmel_stop_rx(port);
>> +
>
> Ditto
>
>
> Given the locking mess you are going to create I would suggest dropping
> the sysfs stuff.
>
> Alan
Thank you for the feedback.
Let me know if this new version solves the above problems.
Claudio
atmel_serial: Atmel RS485 support
Signed-off-by: Claudio Scordino <claudio@evidence.eu.com>
Signed-off-by: Michael Trimarchi <michael@evidence.eu.com>
Signed-off-by: Rick Bronson <rick@efn.org>
Signed-off-by: Sebastian Heutling <Sebastian.Heutling@who-ing.de>
Tested-by: Sebastian Heutling <Sebastian.Heutling@who-ing.de>
Reviewed-by: Ryan Mallon <ryan@bluewatersys.com>
---
arch/arm/include/asm/ioctls.h | 3 +
arch/arm/mach-at91/include/mach/board.h | 8 +-
drivers/serial/atmel_serial.c | 203 +++++++++++++++++++++++++------
3 files changed, 176 insertions(+), 38 deletions(-)
diff --git a/arch/arm/include/asm/ioctls.h b/arch/arm/include/asm/ioctls.h
index a91d8a1..7f0b6d1 100644
--- a/arch/arm/include/asm/ioctls.h
+++ b/arch/arm/include/asm/ioctls.h
@@ -53,6 +53,9 @@
#define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
#define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */
+#define TIOCGRS485 0x542E
+#define TIOCSRS485 0x542F
+
#define FIONCLEX 0x5450 /* these numbers need to be adjusted. */
#define FIOCLEX 0x5451
#define FIOASYNC 0x5452
diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
index ceaec6c..df2ed84 100644
--- a/arch/arm/mach-at91/include/mach/board.h
+++ b/arch/arm/mach-at91/include/mach/board.h
@@ -39,6 +39,7 @@
#include <linux/usb/atmel_usba_udc.h>
#include <linux/atmel-mci.h>
#include <sound/atmel-ac97c.h>
+#include <linux/serial.h>
/* USB Device */
struct at91_udc_data {
@@ -143,9 +144,10 @@ extern struct platform_device *atmel_default_console_device;
extern void __init __deprecated at91_init_serial(struct at91_uart_config *config);
struct atmel_uart_data {
- short use_dma_tx; /* use transmit DMA? */
- short use_dma_rx; /* use receive DMA? */
- void __iomem *regs; /* virtual base address, if any */
+ short use_dma_tx; /* use transmit DMA? */
+ short use_dma_rx; /* use receive DMA? */
+ void __iomem *regs; /* virt. base address, if any */
+ struct serial_rs485 rs485; /* rs485 settings */
};
extern void __init at91_add_device_serial(void);
diff --git a/drivers/serial/atmel_serial.c b/drivers/serial/atmel_serial.c
index 2c9bf9b..4f4b0b9 100644
--- a/drivers/serial/atmel_serial.c
+++ b/drivers/serial/atmel_serial.c
@@ -38,6 +38,7 @@
#include <linux/dma-mapping.h>
#include <linux/atmel_pdc.h>
#include <linux/atmel_serial.h>
+#include <linux/uaccess.h>
#include <asm/io.h>
@@ -59,6 +60,9 @@
#include <linux/serial_core.h>
+static void atmel_start_rx(struct uart_port *port);
+static void atmel_stop_rx(struct uart_port *port);
+
#ifdef CONFIG_SERIAL_ATMEL_TTYAT
/* Use device name ttyAT, major 204 and minor 154-169. This is necessary if we
@@ -93,6 +97,7 @@
#define UART_GET_BRGR(port) __raw_readl((port)->membase + ATMEL_US_BRGR)
#define UART_PUT_BRGR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_BRGR)
#define UART_PUT_RTOR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_RTOR)
+#define UART_PUT_TTGR(port, v) __raw_writel(v, (port)->membase + ATMEL_US_TTGR)
/* PDC registers */
#define UART_PUT_PTCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_PTCR)
@@ -147,6 +152,9 @@ struct atmel_uart_port {
unsigned int irq_status_prev;
struct circ_buf rx_ring;
+
+ struct serial_rs485 rs485; /* rs485 settings */
+ unsigned int tx_done_mask;
};
static struct atmel_uart_port atmel_ports[ATMEL_MAX_UART];
@@ -187,6 +195,40 @@ static bool atmel_use_dma_tx(struct uart_port *port)
}
#endif
+/* Enable or disable the rs485 support */
+void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf)
+{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ unsigned long flags;
+ unsigned int mode;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ mode = UART_GET_MR(port);
+
+ /* Resetting serial mode to RS232 (0x0) */
+ mode &= ~ATMEL_US_USMODE;
+
+ atmel_port->rs485 = *rs485conf;
+
+ if (rs485conf->flags & SER_RS485_ENABLED) {
+ dev_dbg(port->dev, "Setting UART to RS485\n");
+ atmel_port->tx_done_mask = ATMEL_US_TXEMPTY;
+ UART_PUT_TTGR(port, rs485conf->delay_rts_before_send);
+ mode |= ATMEL_US_USMODE_RS485;
+ } else {
+ dev_dbg(port->dev, "Setting UART to RS232\n");
+ if (atmel_use_dma_tx(port))
+ atmel_port->tx_done_mask = ATMEL_US_ENDTX |
+ ATMEL_US_TXBUFE;
+ else
+ atmel_port->tx_done_mask = ATMEL_US_TXRDY;
+ }
+ UART_PUT_MR(port, mode);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
/*
* Return TIOCSER_TEMT when transmitter FIFO and Shift register is empty.
*/
@@ -202,6 +244,7 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
{
unsigned int control = 0;
unsigned int mode;
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
#ifdef CONFIG_ARCH_AT91RM9200
if (cpu_is_at91rm9200()) {
@@ -236,6 +279,17 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
mode |= ATMEL_US_CHMODE_LOC_LOOP;
else
mode |= ATMEL_US_CHMODE_NORMAL;
+
+ /* Resetting serial mode to RS232 (0x0) */
+ mode &= ~ATMEL_US_USMODE;
+
+ if (atmel_port->rs485.flags & SER_RS485_ENABLED) {
+ dev_dbg(port->dev, "Setting UART to RS485\n");
+ UART_PUT_TTGR(port, atmel_port->rs485.delay_rts_before_send);
+ mode |= ATMEL_US_USMODE_RS485;
+ } else {
+ dev_dbg(port->dev, "Setting UART to RS232\n");
+ }
UART_PUT_MR(port, mode);
}
@@ -268,12 +322,17 @@ static u_int atmel_get_mctrl(struct uart_port *port)
*/
static void atmel_stop_tx(struct uart_port *port)
{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
if (atmel_use_dma_tx(port)) {
/* disable PDC transmit */
UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS);
- UART_PUT_IDR(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE);
- } else
- UART_PUT_IDR(port, ATMEL_US_TXRDY);
+ }
+ /* Disable interrupts */
+ UART_PUT_IDR(port, atmel_port->tx_done_mask);
+
+ if (atmel_port->rs485.flags & SER_RS485_ENABLED)
+ atmel_start_rx(port);
}
/*
@@ -281,17 +340,39 @@ static void atmel_stop_tx(struct uart_port *port)
*/
static void atmel_start_tx(struct uart_port *port)
{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
if (atmel_use_dma_tx(port)) {
if (UART_GET_PTSR(port) & ATMEL_PDC_TXTEN)
/* The transmitter is already running. Yes, we
really need this.*/
return;
- UART_PUT_IER(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE);
+ if (atmel_port->rs485.flags & SER_RS485_ENABLED)
+ atmel_stop_rx(port);
+
/* re-enable PDC transmit */
UART_PUT_PTCR(port, ATMEL_PDC_TXTEN);
- } else
- UART_PUT_IER(port, ATMEL_US_TXRDY);
+ }
+ /* Enable interrupts */
+ UART_PUT_IER(port, atmel_port->tx_done_mask);
+}
+
+/*
+ * start receiving - port is in process of being opened.
+ */
+static void atmel_start_rx(struct uart_port *port)
+{
+ UART_PUT_CR(port, ATMEL_US_RSTSTA); /* reset status and receiver */
+
+ if (atmel_use_dma_rx(port)) {
+ /* enable PDC controller */
+ UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT |
+ port->read_status_mask);
+ UART_PUT_PTCR(port, ATMEL_PDC_RXTEN);
+ } else {
+ UART_PUT_IER(port, ATMEL_US_RXRDY);
+ }
}
/*
@@ -302,9 +383,11 @@ static void atmel_stop_rx(struct uart_port *port)
if (atmel_use_dma_rx(port)) {
/* disable PDC receive */
UART_PUT_PTCR(port, ATMEL_PDC_RXTDIS);
- UART_PUT_IDR(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT);
- } else
+ UART_PUT_IDR(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT |
+ port->read_status_mask);
+ } else {
UART_PUT_IDR(port, ATMEL_US_RXRDY);
+ }
}
/*
@@ -428,8 +511,9 @@ static void atmel_rx_chars(struct uart_port *port)
static void atmel_tx_chars(struct uart_port *port)
{
struct circ_buf *xmit = &port->state->xmit;
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
- if (port->x_char && UART_GET_CSR(port) & ATMEL_US_TXRDY) {
+ if (port->x_char && UART_GET_CSR(port) & atmel_port->tx_done_mask) {
UART_PUT_CHAR(port, port->x_char);
port->icount.tx++;
port->x_char = 0;
@@ -437,7 +521,7 @@ static void atmel_tx_chars(struct uart_port *port)
if (uart_circ_empty(xmit) || uart_tx_stopped(port))
return;
- while (UART_GET_CSR(port) & ATMEL_US_TXRDY) {
+ while (UART_GET_CSR(port) & atmel_port->tx_done_mask) {
UART_PUT_CHAR(port, xmit->buf[xmit->tail]);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
port->icount.tx++;
@@ -449,7 +533,8 @@ static void atmel_tx_chars(struct uart_port *port)
uart_write_wakeup(port);
if (!uart_circ_empty(xmit))
- UART_PUT_IER(port, ATMEL_US_TXRDY);
+ /* Enable interrupts */
+ UART_PUT_IER(port, atmel_port->tx_done_mask);
}
/*
@@ -501,18 +586,10 @@ atmel_handle_transmit(struct uart_port *port, unsigned int pending)
{
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
- if (atmel_use_dma_tx(port)) {
- /* PDC transmit */
- if (pending & (ATMEL_US_ENDTX | ATMEL_US_TXBUFE)) {
- UART_PUT_IDR(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE);
- tasklet_schedule(&atmel_port->tasklet);
- }
- } else {
- /* Interrupt transmit */
- if (pending & ATMEL_US_TXRDY) {
- UART_PUT_IDR(port, ATMEL_US_TXRDY);
- tasklet_schedule(&atmel_port->tasklet);
- }
+ if (pending & atmel_port->tx_done_mask) {
+ /* Either PDC or interrupt transmission */
+ UART_PUT_IDR(port, atmel_port->tx_done_mask);
+ tasklet_schedule(&atmel_port->tasklet);
}
}
@@ -590,9 +667,15 @@ static void atmel_tx_dma(struct uart_port *port)
UART_PUT_TPR(port, pdc->dma_addr + xmit->tail);
UART_PUT_TCR(port, count);
- /* re-enable PDC transmit and interrupts */
+ /* re-enable PDC transmit */
UART_PUT_PTCR(port, ATMEL_PDC_TXTEN);
- UART_PUT_IER(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE);
+ /* Enable interrupts */
+ UART_PUT_IER(port, atmel_port->tx_done_mask);
+ } else {
+ if (atmel_port->rs485.flags & SER_RS485_ENABLED) {
+ /* DMA done, stop TX, start RX for RS485 */
+ atmel_start_rx(port);
+ }
}
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
@@ -1017,6 +1100,7 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
{
unsigned long flags;
unsigned int mode, imr, quot, baud;
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
/* Get current mode register */
mode = UART_GET_MR(port) & ~(ATMEL_US_USCLKS | ATMEL_US_CHRL
@@ -1115,6 +1199,17 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
/* disable receiver and transmitter */
UART_PUT_CR(port, ATMEL_US_TXDIS | ATMEL_US_RXDIS);
+ /* Resetting serial mode to RS232 (0x0) */
+ mode &= ~ATMEL_US_USMODE;
+
+ if (atmel_port->rs485.flags & SER_RS485_ENABLED) {
+ dev_dbg(port->dev, "Setting UART to RS485\n");
+ UART_PUT_TTGR(port, atmel_port->rs485.delay_rts_before_send);
+ mode |= ATMEL_US_USMODE_RS485;
+ } else {
+ dev_dbg(port->dev, "Setting UART to RS232\n");
+ }
+
/* set the parity, stop bits and data size */
UART_PUT_MR(port, mode);
@@ -1231,6 +1326,35 @@ static void atmel_poll_put_char(struct uart_port *port, unsigned char ch)
}
#endif
+static int
+atmel_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg)
+{
+ struct serial_rs485 rs485conf;
+
+ switch (cmd) {
+ case TIOCSRS485:
+ if (copy_from_user(&rs485conf, (struct serial_rs485 *) arg,
+ sizeof(rs485conf)))
+ return -EFAULT;
+
+ atmel_config_rs485(port, &rs485conf);
+ break;
+
+ case TIOCGRS485:
+ if (copy_to_user((struct serial_rs485 *) arg,
+ &(to_atmel_uart_port(port)->rs485),
+ sizeof(rs485conf)))
+ return -EFAULT;
+ break;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+
+
static struct uart_ops atmel_pops = {
.tx_empty = atmel_tx_empty,
.set_mctrl = atmel_set_mctrl,
@@ -1250,6 +1374,7 @@ static struct uart_ops atmel_pops = {
.config_port = atmel_config_port,
.verify_port = atmel_verify_port,
.pm = atmel_serial_pm,
+ .ioctl = atmel_ioctl,
#ifdef CONFIG_CONSOLE_POLL
.poll_get_char = atmel_poll_get_char,
.poll_put_char = atmel_poll_put_char,
@@ -1265,13 +1390,12 @@ static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port,
struct uart_port *port = &atmel_port->uart;
struct atmel_uart_data *data = pdev->dev.platform_data;
- port->iotype = UPIO_MEM;
- port->flags = UPF_BOOT_AUTOCONF;
- port->ops = &atmel_pops;
- port->fifosize = 1;
- port->line = pdev->id;
- port->dev = &pdev->dev;
-
+ port->iotype = UPIO_MEM;
+ port->flags = UPF_BOOT_AUTOCONF;
+ port->ops = &atmel_pops;
+ port->fifosize = 1;
+ port->line = pdev->id;
+ port->dev = &pdev->dev;
port->mapbase = pdev->resource[0].start;
port->irq = pdev->resource[1].start;
@@ -1299,8 +1423,16 @@ static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port,
atmel_port->use_dma_rx = data->use_dma_rx;
atmel_port->use_dma_tx = data->use_dma_tx;
- if (atmel_use_dma_tx(port))
+ atmel_port->rs485 = data->rs485;
+ /* Use TXEMPTY for interrupt when rs485 else TXRDY or ENDTX|TXBUFE */
+ if (atmel_port->rs485.flags & SER_RS485_ENABLED)
+ atmel_port->tx_done_mask = ATMEL_US_TXEMPTY;
+ else if (atmel_use_dma_tx(port)) {
port->fifosize = PDC_BUFFER_SIZE;
+ atmel_port->tx_done_mask = ATMEL_US_ENDTX | ATMEL_US_TXBUFE;
+ } else {
+ atmel_port->tx_done_mask = ATMEL_US_TXRDY;
+ }
}
/*
@@ -1334,6 +1466,7 @@ static void atmel_console_putchar(struct uart_port *port, int ch)
static void atmel_console_write(struct console *co, const char *s, u_int count)
{
struct uart_port *port = &atmel_ports[co->index].uart;
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
unsigned int status, imr;
unsigned int pdc_tx;
@@ -1341,7 +1474,7 @@ static void atmel_console_write(struct console *co, const char *s, u_int count)
* First, save IMR and then disable interrupts
*/
imr = UART_GET_IMR(port);
- UART_PUT_IDR(port, ATMEL_US_RXRDY | ATMEL_US_TXRDY);
+ UART_PUT_IDR(port, ATMEL_US_RXRDY | atmel_port->tx_done_mask);
/* Store PDC transmit status and disable it */
pdc_tx = UART_GET_PTSR(port) & ATMEL_PDC_TXTEN;
@@ -1355,7 +1488,7 @@ static void atmel_console_write(struct console *co, const char *s, u_int count)
*/
do {
status = UART_GET_CSR(port);
- } while (!(status & ATMEL_US_TXRDY));
+ } while (!(status & atmel_port->tx_done_mask));
/* Restore PDC transmit status */
if (pdc_tx)
--
1.6.0.4
^ permalink raw reply related [flat|nested] 43+ messages in thread
* Re: [PATCH] atmel_serial: Atmel RS485 support v2
2010-04-08 13:16 ` Claudio Scordino
@ 2010-04-08 13:42 ` Alan Cox
2010-05-26 13:18 ` Nicolas Ferre
1 sibling, 0 replies; 43+ messages in thread
From: Alan Cox @ 2010-04-08 13:42 UTC (permalink / raw)
To: Claudio Scordino
Cc: Ryan Mallon, Linux Kernel, linux-arm-kernel, John Nicholls,
Rick Bronson, Sebastian Heutling, michael trimarchi, rmk,
hskinnemoen, linux
> Thank you for the feedback.
>
> Let me know if this new version solves the above problems.
Looks good to me. Thanks for the quick clean up.
Alan
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH] atmel_serial: Atmel RS485 support v2
2010-04-08 13:16 ` Claudio Scordino
2010-04-08 13:42 ` Alan Cox
@ 2010-05-26 13:18 ` Nicolas Ferre
2010-05-27 8:37 ` Claudio Scordino
1 sibling, 1 reply; 43+ messages in thread
From: Nicolas Ferre @ 2010-05-26 13:18 UTC (permalink / raw)
To: Claudio Scordino, Alan Cox, hskinnemoen, linux, linux-arm-kernel
Cc: Rick Bronson, John Nicholls, Linux Kernel, Sebastian Heutling,
Ryan Mallon, rmk, michael trimarchi
Le 08/04/2010 15:16, Claudio Scordino :
[..]
> atmel_serial: Atmel RS485 support
>
> Signed-off-by: Claudio Scordino <claudio@evidence.eu.com>
> Signed-off-by: Michael Trimarchi <michael@evidence.eu.com>
> Signed-off-by: Rick Bronson <rick@efn.org>
> Signed-off-by: Sebastian Heutling <Sebastian.Heutling@who-ing.de>
> Tested-by: Sebastian Heutling <Sebastian.Heutling@who-ing.de>
> Reviewed-by: Ryan Mallon <ryan@bluewatersys.com>
Even though I personally did not test this patch I think it is time to
go forward with this very good work.
Considering the number of people involved in this testing and review I
think that we can submit this patch for mainline. Who will handle it?
- do we submit to ARM or serial subsystem ?
Best regards,
--
Nicolas Ferre
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH] atmel_serial: Atmel RS485 support v2
2010-05-26 13:18 ` Nicolas Ferre
@ 2010-05-27 8:37 ` Claudio Scordino
2010-05-27 9:57 ` Nicolas Ferre
0 siblings, 1 reply; 43+ messages in thread
From: Claudio Scordino @ 2010-05-27 8:37 UTC (permalink / raw)
To: Nicolas Ferre
Cc: Alan Cox, hskinnemoen, linux, linux-arm-kernel, Rick Bronson,
John Nicholls, Linux Kernel, Sebastian Heutling, Ryan Mallon,
michael trimarchi, rmk
Nicolas Ferre ha scritto:
> Le 08/04/2010 15:16, Claudio Scordino :
>
> [..]
>
>
>> atmel_serial: Atmel RS485 support
>>
>> Signed-off-by: Claudio Scordino <claudio@evidence.eu.com>
>> Signed-off-by: Michael Trimarchi <michael@evidence.eu.com>
>> Signed-off-by: Rick Bronson <rick@efn.org>
>> Signed-off-by: Sebastian Heutling <Sebastian.Heutling@who-ing.de>
>> Tested-by: Sebastian Heutling <Sebastian.Heutling@who-ing.de>
>> Reviewed-by: Ryan Mallon <ryan@bluewatersys.com>
>>
>
> Even though I personally did not test this patch I think it is time to
> go forward with this very good work.
>
> Considering the number of people involved in this testing and review I
> think that we can submit this patch for mainline. Who will handle it?
> - do we submit to ARM or serial subsystem ?
>
Hi Nicolas,
the latest version of the patch has been already merged in the
latest Linus' git tree through Russell King's patchsystem (the maintainer of the
driver never answered to emails...).
Many thanks,
Claudio
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH] atmel_serial: Atmel RS485 support v2
2010-05-27 8:37 ` Claudio Scordino
@ 2010-05-27 9:57 ` Nicolas Ferre
2010-05-27 10:29 ` Wolfram Sang
2010-05-28 9:42 ` [PATCH] atmel_serial: Atmel RS485 support v2 Haavard Skinnemoen
0 siblings, 2 replies; 43+ messages in thread
From: Nicolas Ferre @ 2010-05-27 9:57 UTC (permalink / raw)
To: Claudio Scordino
Cc: Alan Cox, hskinnemoen, linux, linux-arm-kernel, Rick Bronson,
John Nicholls, Linux Kernel, Sebastian Heutling, Ryan Mallon,
michael trimarchi, rmk
Le 27/05/2010 10:37, Claudio Scordino :
> Nicolas Ferre ha scritto:
>> Le 08/04/2010 15:16, Claudio Scordino :
>>
>> [..]
>>
>>
>>> atmel_serial: Atmel RS485 support
>>>
>>> Signed-off-by: Claudio Scordino <claudio@evidence.eu.com>
>>> Signed-off-by: Michael Trimarchi <michael@evidence.eu.com>
>>> Signed-off-by: Rick Bronson <rick@efn.org>
>>> Signed-off-by: Sebastian Heutling <Sebastian.Heutling@who-ing.de>
>>> Tested-by: Sebastian Heutling <Sebastian.Heutling@who-ing.de>
>>> Reviewed-by: Ryan Mallon <ryan@bluewatersys.com>
>>>
>>
>> Even though I personally did not test this patch I think it is time to
>> go forward with this very good work.
>>
>> Considering the number of people involved in this testing and review I
>> think that we can submit this patch for mainline. Who will handle it?
>> - do we submit to ARM or serial subsystem ?
>>
>
> Hi Nicolas,
>
> the latest version of the patch has been already merged in the
> latest Linus' git tree through Russell King's patchsystem
My fault: I use to check Russell patchsystem with "AT91" searching
string and not "atmel" ;-)
> (the maintainer of the driver never answered to emails...).
I was supposed to take over its maintainance one year ago but did not
find time to do it. My fault here also...
Anyway, it was the occasion to tell all of you that this RS485 support
is a nice job and that even if I do not answer, I try to track all
interesting activity in the background.
Best regards,
--
Nicolas Ferre
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH] atmel_serial: Atmel RS485 support v2
2010-05-27 9:57 ` Nicolas Ferre
@ 2010-05-27 10:29 ` Wolfram Sang
2010-05-28 15:05 ` Claudio Scordino
2010-08-11 9:26 ` [PATCH] Documentation about RS485 serial communications Claudio Scordino
2010-05-28 9:42 ` [PATCH] atmel_serial: Atmel RS485 support v2 Haavard Skinnemoen
1 sibling, 2 replies; 43+ messages in thread
From: Wolfram Sang @ 2010-05-27 10:29 UTC (permalink / raw)
To: Nicolas Ferre
Cc: Claudio Scordino, Alan Cox, hskinnemoen, linux, linux-arm-kernel,
Rick Bronson, John Nicholls, Linux Kernel, Sebastian Heutling,
Ryan Mallon, michael trimarchi, rmk
[-- Attachment #1: Type: text/plain, Size: 488 bytes --]
Hi Claudio,
kudos for getting a RS485-implementation into the kernel! Very useful.
> > the latest version of the patch has been already merged in the
> > latest Linus' git tree through Russell King's patchsystem
Nitpick: Wasn't there some documentation promised after the final patch ;)
Regards,
Wolfram
--
Pengutronix e.K. | Wolfram Sang |
Industrial Linux Solutions | http://www.pengutronix.de/ |
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 197 bytes --]
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH] atmel_serial: Atmel RS485 support v2
2010-05-27 10:29 ` Wolfram Sang
@ 2010-05-28 15:05 ` Claudio Scordino
2010-08-11 9:26 ` [PATCH] Documentation about RS485 serial communications Claudio Scordino
1 sibling, 0 replies; 43+ messages in thread
From: Claudio Scordino @ 2010-05-28 15:05 UTC (permalink / raw)
To: Wolfram Sang, Philippe De Muyter
Cc: Nicolas Ferre, Alan Cox, hskinnemoen, linux, linux-arm-kernel,
Rick Bronson, John Nicholls, Linux Kernel, Sebastian Heutling,
Ryan Mallon, michael trimarchi, rmk, Bernhard Roth,
Konrad Mattheis, Elektrolot, Prchal Jiří
Wolfram Sang ha scritto:
> Hi Claudio,
>
> kudos for getting a RS485-implementation into the kernel! Very useful.
Many thanks!
I hope other people will find it useful too.
>
>>> the latest version of the patch has been already merged in the
>>> latest Linus' git tree through Russell King's patchsystem
>
> Nitpick: Wasn't there some documentation promised after the final patch ;)
Here is the first attempt.
Best regards,
Claudio
Documentation about RS485
Signed-off-by: Claudio Scordino <claudio@evidence.eu.com>
---
Documentation/serial/00-INDEX | 2 +
Documentation/serial/serial-rs485.txt | 118 +++++++++++++++++++++++++++++++++
2 files changed, 120 insertions(+), 0 deletions(-)
create mode 100644 Documentation/serial/serial-rs485.txt
diff --git a/Documentation/serial/00-INDEX b/Documentation/serial/00-INDEX
index 07dcdb0..e09468a 100644
--- a/Documentation/serial/00-INDEX
+++ b/Documentation/serial/00-INDEX
@@ -14,6 +14,8 @@ riscom8.txt
- notes on using the RISCom/8 multi-port serial driver.
rocket.txt
- info on the Comtrol RocketPort multiport serial driver.
+serial-rs485.txt
+ - info about RS485 structures and support in the kernel.
specialix.txt
- info on hardware/driver for specialix IO8+ multiport serial card.
stallion.txt
diff --git a/Documentation/serial/serial-rs485.txt b/Documentation/serial/serial-rs485.txt
new file mode 100644
index 0000000..dac98c3
--- /dev/null
+++ b/Documentation/serial/serial-rs485.txt
@@ -0,0 +1,118 @@
+ RS485 SERIAL COMMUNICATIONS
+
+1. INTRODUCTION
+
+ EIA-485, also known as TIA/EIA-485 or RS-485, is a standard defining the
+ electrical characteristics of drivers and receivers for use in balanced
+ digital multipoint systems.
+ This standard is widely used for communications in industrial automation
+ because it can be used effectively over long distances and in electrically
+ noisy environments.
+ Even though the data is transmitted over a 2-wire twisted pair bus, all
+ EIA-485 transceivers interpret the voltage levels of the differential
+ signals with respect to a third common voltage. Without this common
+ reference, a set of transceivers may interpret the differential signals
+ incorrectly.
+ See [1] for more information.
+
+
+2. HARDWARE-RELATED CONSIDERATIONS
+
+ Some CPUs (e.g., Atmel AT91) contain a transceiver capable of working both
+ as RS232 and RS485. For these microcontrollers, the Linux driver should be
+ able of working in both modes, and proper ioctls (see later) should be made
+ available at user-level to allow switching from one mode to the other, and
+ viceversa.
+
+ On some other CPUs (e.g., Freescale imx25) the RS485 transceiver is not
+ integrated inside the microcontroller itself. Therefore, manufacturers who
+ use these microcontrollers to produce embedded boards need to connect an
+ external transceiver to some pin of the CPU.
+ On these architectures, therefore, no assumptions can be done at the
+ CPU-level about the presence of a RS485 transceiver, because the connection
+ (if any) is done outside the microcontroller. Moreover, even in case of
+ RS485 transceiver, the manufacturer is free to choose the CPU pin used for
+ the connection.
+
+
+3. DATA STRUCTURES ALREADY AVAILABLE IN THE KERNEL
+
+ The Linux kernel provides the serial_rs485 structure (inside
+ include/linux/serial.h) to handle RS485 communications. This data structure
+ is used to set and configure RS485 parameters in the platform data and in
+ ioctls.
+
+ Any driver for interfaces capable of working both as RS232 and RS485 should
+ provide at least the following ioctls:
+
+ - TIOCSRS485 (typically associated with number 0x542F). This ioctl is used
+ to enable/disable RS485 mode from user-space
+
+ - TIOCGRS485 (typically associated with number 0x542E). This ioctl is used
+ to get RS485 mode from kernel-space (i.e., driver) to user-space.
+
+ In other words, the serial driver should contain a code similar to the next
+ one:
+
+ static struct uart_ops atmel_pops = {
+ /* ... */
+ .ioctl = handle_ioctl,
+ };
+
+ static int
+ handle_ioctl(struct uart_port *port,
+ unsigned int cmd,
+ unsigned long arg)
+ {
+ struct serial_rs485 rs485conf;
+
+ switch (cmd) {
+ case TIOCSRS485:
+ if (copy_from_user(&rs485conf,
+ (struct serial_rs485 *) arg,
+ sizeof(rs485conf)))
+ return -EFAULT;
+
+ /* ... */
+ break;
+
+ case TIOCGRS485:
+ if (copy_to_user((struct serial_rs485 *) arg,
+ ...,
+ sizeof(rs485conf)))
+ return -EFAULT;
+ /* ... */
+ break;
+ /* ... */
+ }
+
+
+4. USAGE FROM USER-LEVEL
+
+ From user-level, RS485 configuration can be get/set using the previous
+ ioctls. For instance, to set RS485 you can use the following code:
+
+ #include <linux/serial.h>
+
+ /* Driver-specific ioctls: */
+ #define TIOCGRS485 0x542E
+ #define TIOCSRS485 0x542F
+
+ /* Open specific device: */
+ int fd = open ("/dev/mydevice", O_RDWR);
+ struct serial_rs485 rs485conf;
+
+ /* Set RS485 mode: */
+ rs485conf.flags |= SER_RS485_ENABLED;
+
+ /* Set delay: */
+ rs485conf.delay_rts_before_send = 0x00000004;
+ ioctl (fd, TIOCSRS485, &rs485conf);
+
+ close (fd);
+
+5. REFERENCES
+
+ [1] http://en.wikipedia.org/wiki/Rs485
+
+
--
1.6.0.4
^ permalink raw reply related [flat|nested] 43+ messages in thread
* [PATCH] Documentation about RS485 serial communications
2010-05-27 10:29 ` Wolfram Sang
2010-05-28 15:05 ` Claudio Scordino
@ 2010-08-11 9:26 ` Claudio Scordino
2010-08-11 10:02 ` Philippe De Muyter
2010-08-11 15:32 ` Randy Dunlap
1 sibling, 2 replies; 43+ messages in thread
From: Claudio Scordino @ 2010-08-11 9:26 UTC (permalink / raw)
To: Wolfram Sang, Philippe De Muyter, Linux Kernel
Cc: Nicolas Ferre, Alan Cox, hskinnemoen, linux-arm-kernel,
Rick Bronson, John Nicholls, Sebastian Heutling, Ryan Mallon,
rmk, Bernhard Roth, Konrad Mattheis, Elektrolot,
Prchal Jiří,
gregkh, Andrew Morton
Hi all,
some time ago I've been asked (by both Wolfram and Philippe) to
provide some minimal documentation about the usage of the RS485
interface.
Here is the document (updated with the very last changes in the
interface).
Best regards,
Claudio
Documentation about RS485 serial communications.
Signed-off-by: Claudio Scordino <claudio@evidence.eu.com>
---
Documentation/serial/00-INDEX | 2 +
Documentation/serial/serial-rs485.txt | 123 +++++++++++++++++++++++++++++++++
2 files changed, 125 insertions(+), 0 deletions(-)
create mode 100644 Documentation/serial/serial-rs485.txt
diff --git a/Documentation/serial/00-INDEX b/Documentation/serial/00-INDEX
index 07dcdb0..e09468a 100644
--- a/Documentation/serial/00-INDEX
+++ b/Documentation/serial/00-INDEX
@@ -14,6 +14,8 @@ riscom8.txt
- notes on using the RISCom/8 multi-port serial driver.
rocket.txt
- info on the Comtrol RocketPort multiport serial driver.
+serial-rs485.txt
+ - info about RS485 structures and support in the kernel.
specialix.txt
- info on hardware/driver for specialix IO8+ multiport serial card.
stallion.txt
diff --git a/Documentation/serial/serial-rs485.txt b/Documentation/serial/serial-rs485.txt
new file mode 100644
index 0000000..f594831
--- /dev/null
+++ b/Documentation/serial/serial-rs485.txt
@@ -0,0 +1,123 @@
+ RS485 SERIAL COMMUNICATIONS
+
+1. INTRODUCTION
+
+ EIA-485, also known as TIA/EIA-485 or RS-485, is a standard defining the
+ electrical characteristics of drivers and receivers for use in balanced
+ digital multipoint systems.
+ This standard is widely used for communications in industrial automation
+ because it can be used effectively over long distances and in electrically
+ noisy environments.
+ Even though the data is transmitted over a 2-wire twisted pair bus, all
+ EIA-485 transceivers interpret the voltage levels of the differential
+ signals with respect to a third common voltage. Without this common
+ reference, a set of transceivers may interpret the differential signals
+ incorrectly.
+ See [1] for more information.
+
+
+2. HARDWARE-RELATED CONSIDERATIONS
+
+ Some CPUs (e.g., Atmel AT91) contain a transceiver capable of working both
+ as RS232 and RS485. For these microcontrollers, the Linux driver should be
+ able of working in both modes, and proper ioctls (see later) should be made
+ available at user-level to allow switching from one mode to the other, and
+ viceversa.
+
+ On some other CPUs (e.g., Freescale imx25) the RS485 transceiver is not
+ integrated inside the microcontroller itself. Therefore, manufacturers who
+ use these microcontrollers to produce embedded boards need to connect an
+ external transceiver to some pin of the CPU.
+ On these architectures, therefore, no assumptions can be done at the
+ CPU-level about the presence of a RS485 transceiver, because the connection
+ (if any) is done outside the microcontroller. Moreover, even in case of
+ RS485 transceiver, the manufacturer is free to choose the CPU pin used for
+ the connection.
+
+
+3. DATA STRUCTURES ALREADY AVAILABLE IN THE KERNEL
+
+ The Linux kernel provides the serial_rs485 structure (see [2]) to handle
+ RS485 communications. This data structure is used to set and configure RS485
+ parameters in the platform data and in ioctls.
+
+ Any driver for devices capable of working both as RS232 and RS485 should
+ provide at least the following ioctls:
+
+ - TIOCSRS485 (typically associated with number 0x542F). This ioctl is used
+ to enable/disable RS485 mode from user-space
+
+ - TIOCGRS485 (typically associated with number 0x542E). This ioctl is used
+ to get RS485 mode from kernel-space (i.e., driver) to user-space.
+
+ In other words, the serial driver should contain a code similar to the next
+ one:
+
+ static struct uart_ops atmel_pops = {
+ /* ... */
+ .ioctl = handle_ioctl,
+ };
+
+ static int handle_ioctl(struct uart_port *port,
+ unsigned int cmd,
+ unsigned long arg)
+ {
+ struct serial_rs485 rs485conf;
+
+ switch (cmd) {
+ case TIOCSRS485:
+ if (copy_from_user(&rs485conf,
+ (struct serial_rs485 *) arg,
+ sizeof(rs485conf)))
+ return -EFAULT;
+
+ /* ... */
+ break;
+
+ case TIOCGRS485:
+ if (copy_to_user((struct serial_rs485 *) arg,
+ ...,
+ sizeof(rs485conf)))
+ return -EFAULT;
+ /* ... */
+ break;
+
+ /* ... */
+ }
+ }
+
+
+4. USAGE FROM USER-LEVEL
+
+ From user-level, RS485 configuration can be get/set using the previous
+ ioctls. For instance, to set RS485 you can use the following code:
+
+ #include <linux/serial.h>
+
+ /* Driver-specific ioctls: */
+ #define TIOCGRS485 0x542E
+ #define TIOCSRS485 0x542F
+
+ /* Open specific device: */
+ int fd = open ("/dev/mydevice", O_RDWR);
+ struct serial_rs485 rs485conf;
+
+ /* Set RS485 mode: */
+ rs485conf.flags |= SER_RS485_ENABLED;
+
+ /* Set rts delay before send, if needed: */
+ rs485conf.flags |= SER_RS485_RTS_BEFORE_SEND;
+ rs485conf.delay_rts_before_send = ...;
+
+ /* Set rts delay after send, if needed: */
+ rs485conf.flags |= SER_RS485_RTS_AFTER_SEND;
+ rs485conf.delay_rts_after_send = ...;
+
+ ioctl (fd, TIOCSRS485, &rs485conf);
+
+ close (fd);
+
+5. REFERENCES
+
+ [1] http://en.wikipedia.org/wiki/Rs485
+ [2] include/linux/serial.h
--
1.6.0.4
^ permalink raw reply related [flat|nested] 43+ messages in thread
* Re: [PATCH] Documentation about RS485 serial communications
2010-08-11 9:26 ` [PATCH] Documentation about RS485 serial communications Claudio Scordino
@ 2010-08-11 10:02 ` Philippe De Muyter
2010-08-11 15:32 ` Randy Dunlap
1 sibling, 0 replies; 43+ messages in thread
From: Philippe De Muyter @ 2010-08-11 10:02 UTC (permalink / raw)
To: Claudio Scordino
Cc: Wolfram Sang, Linux Kernel, Nicolas Ferre, Alan Cox, hskinnemoen,
linux-arm-kernel, Rick Bronson, John Nicholls,
Sebastian Heutling, Ryan Mallon, rmk, Bernhard Roth,
Konrad Mattheis, Elektrolot, Prchal Jiří,
gregkh, Andrew Morton
On Wed, Aug 11, 2010 at 11:26:23AM +0200, Claudio Scordino wrote:
> Hi all,
>
> some time ago I've been asked (by both Wolfram and Philippe) to
> provide some minimal documentation about the usage of the RS485
> interface.
>
> Here is the document (updated with the very last changes in the
> interface).
Thanks
>
> Best regards,
>
> Claudio
>
>
> Documentation about RS485 serial communications.
>
> Signed-off-by: Claudio Scordino <claudio@evidence.eu.com>
> ---
> Documentation/serial/00-INDEX | 2 +
> Documentation/serial/serial-rs485.txt | 123 +++++++++++++++++++++++++++++++++
> 2 files changed, 125 insertions(+), 0 deletions(-)
> create mode 100644 Documentation/serial/serial-rs485.txt
>
> diff --git a/Documentation/serial/00-INDEX b/Documentation/serial/00-INDEX
> index 07dcdb0..e09468a 100644
> --- a/Documentation/serial/00-INDEX
> +++ b/Documentation/serial/00-INDEX
> @@ -14,6 +14,8 @@ riscom8.txt
> - notes on using the RISCom/8 multi-port serial driver.
> rocket.txt
> - info on the Comtrol RocketPort multiport serial driver.
> +serial-rs485.txt
> + - info about RS485 structures and support in the kernel.
> specialix.txt
> - info on hardware/driver for specialix IO8+ multiport serial card.
> stallion.txt
> diff --git a/Documentation/serial/serial-rs485.txt b/Documentation/serial/serial-rs485.txt
> new file mode 100644
> index 0000000..f594831
> --- /dev/null
> +++ b/Documentation/serial/serial-rs485.txt
> @@ -0,0 +1,123 @@
> + RS485 SERIAL COMMUNICATIONS
> +
> +1. INTRODUCTION
> +
> + EIA-485, also known as TIA/EIA-485 or RS-485, is a standard defining the
> + electrical characteristics of drivers and receivers for use in balanced
> + digital multipoint systems.
> + This standard is widely used for communications in industrial automation
> + because it can be used effectively over long distances and in electrically
> + noisy environments.
> + Even though the data is transmitted over a 2-wire twisted pair bus, all
> + EIA-485 transceivers interpret the voltage levels of the differential
> + signals with respect to a third common voltage. Without this common
> + reference, a set of transceivers may interpret the differential signals
> + incorrectly.
> + See [1] for more information.
> +
> +
> +2. HARDWARE-RELATED CONSIDERATIONS
> +
> + Some CPUs (e.g., Atmel AT91) contain a transceiver capable of working both
> + as RS232 and RS485. For these microcontrollers, the Linux driver should be
> + able of working in both modes, and proper ioctls (see later) should be made
> + available at user-level to allow switching from one mode to the other, and
> + viceversa.
> +
> + On some other CPUs (e.g., Freescale imx25) the RS485 transceiver is not
> + integrated inside the microcontroller itself. Therefore, manufacturers who
> + use these microcontrollers to produce embedded boards need to connect an
> + external transceiver to some pin of the CPU.
> + On these architectures, therefore, no assumptions can be done at the
> + CPU-level about the presence of a RS485 transceiver, because the connection
> + (if any) is done outside the microcontroller. Moreover, even in case of
> + RS485 transceiver, the manufacturer is free to choose the CPU pin used for
> + the connection.
> +
> +
> +3. DATA STRUCTURES ALREADY AVAILABLE IN THE KERNEL
> +
> + The Linux kernel provides the serial_rs485 structure (see [2]) to handle
> + RS485 communications. This data structure is used to set and configure RS485
> + parameters in the platform data and in ioctls.
> +
> + Any driver for devices capable of working both as RS232 and RS485 should
> + provide at least the following ioctls:
> +
> + - TIOCSRS485 (typically associated with number 0x542F). This ioctl is used
> + to enable/disable RS485 mode from user-space
> +
> + - TIOCGRS485 (typically associated with number 0x542E). This ioctl is used
> + to get RS485 mode from kernel-space (i.e., driver) to user-space.
> +
> + In other words, the serial driver should contain a code similar to the next
> + one:
> +
> + static struct uart_ops atmel_pops = {
> + /* ... */
> + .ioctl = handle_ioctl,
> + };
> +
> + static int handle_ioctl(struct uart_port *port,
> + unsigned int cmd,
> + unsigned long arg)
> + {
> + struct serial_rs485 rs485conf;
> +
> + switch (cmd) {
> + case TIOCSRS485:
> + if (copy_from_user(&rs485conf,
> + (struct serial_rs485 *) arg,
> + sizeof(rs485conf)))
> + return -EFAULT;
> +
> + /* ... */
> + break;
> +
> + case TIOCGRS485:
> + if (copy_to_user((struct serial_rs485 *) arg,
> + ...,
> + sizeof(rs485conf)))
> + return -EFAULT;
> + /* ... */
> + break;
> +
> + /* ... */
> + }
> + }
> +
> +
> +4. USAGE FROM USER-LEVEL
> +
> + From user-level, RS485 configuration can be get/set using the previous
> + ioctls. For instance, to set RS485 you can use the following code:
> +
> + #include <linux/serial.h>
> +
> + /* Driver-specific ioctls: */
> + #define TIOCGRS485 0x542E
> + #define TIOCSRS485 0x542F
Should those defines not come from a linux header file ?
> +
> + /* Open specific device: */
> + int fd = open ("/dev/mydevice", O_RDWR);
> + struct serial_rs485 rs485conf;
> +
> + /* Set RS485 mode: */
> + rs485conf.flags |= SER_RS485_ENABLED;
> +
> + /* Set rts delay before send, if needed: */
> + rs485conf.flags |= SER_RS485_RTS_BEFORE_SEND;
> + rs485conf.delay_rts_before_send = ...;
> +
> + /* Set rts delay after send, if needed: */
> + rs485conf.flags |= SER_RS485_RTS_AFTER_SEND;
> + rs485conf.delay_rts_after_send = ...;
> +
> + ioctl (fd, TIOCSRS485, &rs485conf);
I surmise that all the real work, the read() and write() system calls,
must come here, before the close() system call.
> +
> + close (fd);
> +
> +5. REFERENCES
> +
> + [1] http://en.wikipedia.org/wiki/Rs485
> + [2] include/linux/serial.h
> --
> 1.6.0.4
>
--
Philippe De Muyter phdm at macqel dot be Tel +32 27029044
Macq Electronique SA rue de l'Aeronef 2 B-1140 Bruxelles Fax +32 27029077
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH] Documentation about RS485 serial communications
2010-08-11 9:26 ` [PATCH] Documentation about RS485 serial communications Claudio Scordino
2010-08-11 10:02 ` Philippe De Muyter
@ 2010-08-11 15:32 ` Randy Dunlap
2010-08-11 19:58 ` Claudio Scordino
2010-08-14 12:50 ` Claudio Scordino
1 sibling, 2 replies; 43+ messages in thread
From: Randy Dunlap @ 2010-08-11 15:32 UTC (permalink / raw)
To: Claudio Scordino
Cc: Wolfram Sang, Philippe De Muyter, Linux Kernel, Nicolas Ferre,
Alan Cox, hskinnemoen, linux-arm-kernel, Rick Bronson,
John Nicholls, Sebastian Heutling, Ryan Mallon, rmk,
Bernhard Roth, Konrad Mattheis, Elektrolot, Prchal Jiří,
gregkh, Andrew Morton
On Wed, 11 Aug 2010 11:26:23 +0200 Claudio Scordino wrote:
> Hi all,
>
> some time ago I've been asked (by both Wolfram and Philippe) to
> provide some minimal documentation about the usage of the RS485
> interface.
>
> Here is the document (updated with the very last changes in the
> interface).
>
> Best regards,
>
> Claudio
>
>
> Documentation about RS485 serial communications.
>
> Signed-off-by: Claudio Scordino <claudio@evidence.eu.com>
> ---
> Documentation/serial/00-INDEX | 2 +
> Documentation/serial/serial-rs485.txt | 123 +++++++++++++++++++++++++++++++++
> 2 files changed, 125 insertions(+), 0 deletions(-)
> create mode 100644 Documentation/serial/serial-rs485.txt
> diff --git a/Documentation/serial/serial-rs485.txt b/Documentation/serial/serial-rs485.txt
> new file mode 100644
> index 0000000..f594831
> --- /dev/null
> +++ b/Documentation/serial/serial-rs485.txt
> @@ -0,0 +1,123 @@
> + RS485 SERIAL COMMUNICATIONS
> +
...
> +
> +2. HARDWARE-RELATED CONSIDERATIONS
> +
> + Some CPUs (e.g., Atmel AT91) contain a transceiver capable of working both
> + as RS232 and RS485. For these microcontrollers, the Linux driver should be
> + able of working in both modes, and proper ioctls (see later) should be made
should be able to work in both modes
or
should be made capable of working in both modes
> + available at user-level to allow switching from one mode to the other, and
> + viceversa.
vice versa.
> +
> + On some other CPUs (e.g., Freescale imx25) the RS485 transceiver is not
> + integrated inside the microcontroller itself. Therefore, manufacturers who
> + use these microcontrollers to produce embedded boards need to connect an
> + external transceiver to some pin of the CPU.
> + On these architectures, therefore, no assumptions can be done at the
> + CPU-level about the presence of a RS485 transceiver, because the connection
> + (if any) is done outside the microcontroller. Moreover, even in case of
> + RS485 transceiver, the manufacturer is free to choose the CPU pin used for
> + the connection.
> +
> +
> +3. DATA STRUCTURES ALREADY AVAILABLE IN THE KERNEL
> +
> + The Linux kernel provides the serial_rs485 structure (see [2]) to handle
> + RS485 communications. This data structure is used to set and configure RS485
> + parameters in the platform data and in ioctls.
> +
> + Any driver for devices capable of working both as RS232 and RS485 should
> + provide at least the following ioctls:
> +
> + - TIOCSRS485 (typically associated with number 0x542F). This ioctl is used
> + to enable/disable RS485 mode from user-space
> +
> + - TIOCGRS485 (typically associated with number 0x542E). This ioctl is used
> + to get RS485 mode from kernel-space (i.e., driver) to user-space.
TIOC[SG]RS485 are #defined in <asm-generic/ioctls.h>, so
#include <asm-generic/ioctls.h>
here and/or below (in userspace program).
> + In other words, the serial driver should contain a code similar to the next
contain code similar to this:
> + one:
> +
> + static struct uart_ops atmel_pops = {
> + /* ... */
> + .ioctl = handle_ioctl,
> + };
> +
> + static int handle_ioctl(struct uart_port *port,
> + unsigned int cmd,
> + unsigned long arg)
> + {
> + struct serial_rs485 rs485conf;
> +
> + switch (cmd) {
> + case TIOCSRS485:
Coding style: we put switch and case at the same indent level.
> + if (copy_from_user(&rs485conf,
> + (struct serial_rs485 *) arg,
> + sizeof(rs485conf)))
> + return -EFAULT;
> +
> + /* ... */
> + break;
> +
> + case TIOCGRS485:
> + if (copy_to_user((struct serial_rs485 *) arg,
> + ...,
> + sizeof(rs485conf)))
> + return -EFAULT;
> + /* ... */
> + break;
> +
> + /* ... */
> + }
> + }
> +
> +
> +4. USAGE FROM USER-LEVEL
> +
> + From user-level, RS485 configuration can be get/set using the previous
> + ioctls. For instance, to set RS485 you can use the following code:
> +
> + #include <linux/serial.h>
> +
> + /* Driver-specific ioctls: */
> + #define TIOCGRS485 0x542E
> + #define TIOCSRS485 0x542F
> +
...
> +
> +5. REFERENCES
> +
> + [1] http://en.wikipedia.org/wiki/Rs485
> + [2] include/linux/serial.h
Thanks for the addition.
---
~Randy
*** Remember to use Documentation/SubmitChecklist when testing your code ***
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH] Documentation about RS485 serial communications
2010-08-11 15:32 ` Randy Dunlap
@ 2010-08-11 19:58 ` Claudio Scordino
2010-08-14 12:50 ` Claudio Scordino
1 sibling, 0 replies; 43+ messages in thread
From: Claudio Scordino @ 2010-08-11 19:58 UTC (permalink / raw)
To: Randy Dunlap
Cc: Wolfram Sang, Philippe De Muyter, Linux Kernel, Nicolas Ferre,
Alan Cox, hskinnemoen, linux-arm-kernel, Rick Bronson,
John Nicholls, Sebastian Heutling, Ryan Mallon, rmk,
Bernhard Roth, Konrad Mattheis, Elektrolot, Prchal Jiří,
gregkh, Andrew Morton
Hi Randy,
thank you for the feedback.
[...]
>
> TIOC[SG]RS485 are #defined in <asm-generic/ioctls.h>, so
> #include <asm-generic/ioctls.h>
> here and/or below (in userspace program).
>
I noticed that for some architectures (e.g., cris) these ioctls are defined also in arch/cris/include/asm/ioctls.h, and with different values with respect to the values defined on asm-generic/ioctls.h.
Therefore, I wasn't completely sure that the values defined in asm-generic are being used in every driver...
Best regards,
Claudio
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH] Documentation about RS485 serial communications
2010-08-11 15:32 ` Randy Dunlap
2010-08-11 19:58 ` Claudio Scordino
@ 2010-08-14 12:50 ` Claudio Scordino
2010-08-15 22:02 ` Randy Dunlap
1 sibling, 1 reply; 43+ messages in thread
From: Claudio Scordino @ 2010-08-14 12:50 UTC (permalink / raw)
To: Randy Dunlap, Philippe De Muyter
Cc: Wolfram Sang, Linux Kernel, Nicolas Ferre, Alan Cox, hskinnemoen,
linux-arm-kernel, Rick Bronson, John Nicholls,
Sebastian Heutling, Ryan Mallon, rmk, Bernhard Roth,
Konrad Mattheis, Elektrolot, Prchal Jiří,
gregkh, Andrew Morton
Randy Dunlap ha scritto:
> On Wed, 11 Aug 2010 11:26:23 +0200 Claudio Scordino wrote:
>
>> Hi all,
>>
>> some time ago I've been asked (by both Wolfram and Philippe) to
>> provide some minimal documentation about the usage of the RS485
>> interface.
>>
[...]
>
> Thanks for the addition.
>
Hi all,
here is the document about RS485 with the requested additions.
If OK, somebody please provide for merging.
Best regards,
Claudio
Documentation about RS485 serial communications.
Signed-off-by: Claudio Scordino <claudio@evidence.eu.com>
---
Documentation/serial/00-INDEX | 2 +
Documentation/serial/serial-rs485.txt | 126 +++++++++++++++++++++++++++++++++
2 files changed, 128 insertions(+), 0 deletions(-)
create mode 100644 Documentation/serial/serial-rs485.txt
diff --git a/Documentation/serial/00-INDEX b/Documentation/serial/00-INDEX
index 07dcdb0..e09468a 100644
--- a/Documentation/serial/00-INDEX
+++ b/Documentation/serial/00-INDEX
@@ -14,6 +14,8 @@ riscom8.txt
- notes on using the RISCom/8 multi-port serial driver.
rocket.txt
- info on the Comtrol RocketPort multiport serial driver.
+serial-rs485.txt
+ - info about RS485 structures and support in the kernel.
specialix.txt
- info on hardware/driver for specialix IO8+ multiport serial card.
stallion.txt
diff --git a/Documentation/serial/serial-rs485.txt b/Documentation/serial/serial-rs485.txt
new file mode 100644
index 0000000..93b029e
--- /dev/null
+++ b/Documentation/serial/serial-rs485.txt
@@ -0,0 +1,126 @@
+ RS485 SERIAL COMMUNICATIONS
+
+1. INTRODUCTION
+
+ EIA-485, also known as TIA/EIA-485 or RS-485, is a standard defining the
+ electrical characteristics of drivers and receivers for use in balanced
+ digital multipoint systems.
+ This standard is widely used for communications in industrial automation
+ because it can be used effectively over long distances and in electrically
+ noisy environments.
+ Even though the data is transmitted over a 2-wire twisted pair bus, all
+ EIA-485 transceivers interpret the voltage levels of the differential
+ signals with respect to a third common voltage. Without this common
+ reference, a set of transceivers may interpret the differential signals
+ incorrectly.
+ See [1] for more information.
+
+
+2. HARDWARE-RELATED CONSIDERATIONS
+
+ Some CPUs (e.g., Atmel AT91) contain a transceiver capable of working both
+ as RS232 and RS485. For these microcontrollers, the Linux driver should be
+ made capable of working in both modes, and proper ioctls (see later) should
+ be made available at user-level to allow switching from one mode to the
+ other, and vice versa.
+
+ On some other CPUs (e.g., Freescale imx25) the RS485 transceiver is not
+ integrated inside the microcontroller itself. Therefore, manufacturers who
+ use these microcontrollers to produce embedded boards need to connect an
+ external transceiver to some pin of the CPU.
+ On these architectures, therefore, no assumptions can be done at the
+ CPU-level about the presence of a RS485 transceiver, because the connection
+ (if any) is done outside the microcontroller. Moreover, even in case of
+ RS485 transceiver, the manufacturer is free to choose the CPU pin used for
+ the connection.
+
+
+3. DATA STRUCTURES ALREADY AVAILABLE IN THE KERNEL
+
+ The Linux kernel provides the serial_rs485 structure (see [2]) to handle
+ RS485 communications. This data structure is used to set and configure RS485
+ parameters in the platform data and in ioctls.
+
+ Any driver for devices capable of working both as RS232 and RS485 should
+ provide at least the following ioctls:
+
+ - TIOCSRS485 (typically associated with number 0x542F). This ioctl is used
+ to enable/disable RS485 mode from user-space
+
+ - TIOCGRS485 (typically associated with number 0x542E). This ioctl is used
+ to get RS485 mode from kernel-space (i.e., driver) to user-space.
+
+ In other words, the serial driver should contain a code similar to the next
+ one:
+
+ static struct uart_ops atmel_pops = {
+ /* ... */
+ .ioctl = handle_ioctl,
+ };
+
+ static int handle_ioctl(struct uart_port *port,
+ unsigned int cmd,
+ unsigned long arg)
+ {
+ struct serial_rs485 rs485conf;
+
+ switch (cmd) {
+ case TIOCSRS485:
+ if (copy_from_user(&rs485conf,
+ (struct serial_rs485 *) arg,
+ sizeof(rs485conf)))
+ return -EFAULT;
+
+ /* ... */
+ break;
+
+ case TIOCGRS485:
+ if (copy_to_user((struct serial_rs485 *) arg,
+ ...,
+ sizeof(rs485conf)))
+ return -EFAULT;
+ /* ... */
+ break;
+
+ /* ... */
+ }
+ }
+
+
+4. USAGE FROM USER-LEVEL
+
+ From user-level, RS485 configuration can be get/set using the previous
+ ioctls. For instance, to set RS485 you can use the following code:
+
+ #include <linux/serial.h>
+
+ /* Driver-specific ioctls: */
+ #define TIOCGRS485 0x542E
+ #define TIOCSRS485 0x542F
+
+ /* Open your specific device (e.g., /dev/mydevice): */
+ int fd = open ("/dev/mydevice", O_RDWR);
+ struct serial_rs485 rs485conf;
+
+ /* Set RS485 mode: */
+ rs485conf.flags |= SER_RS485_ENABLED;
+
+ /* Set rts delay before send, if needed: */
+ rs485conf.flags |= SER_RS485_RTS_BEFORE_SEND;
+ rs485conf.delay_rts_before_send = ...;
+
+ /* Set rts delay after send, if needed: */
+ rs485conf.flags |= SER_RS485_RTS_AFTER_SEND;
+ rs485conf.delay_rts_after_send = ...;
+
+ ioctl (fd, TIOCSRS485, &rs485conf);
+
+ /* Use read() and write() syscalls here... */
+
+ /* Close the device when finished: */
+ close (fd);
+
+5. REFERENCES
+
+ [1] http://en.wikipedia.org/wiki/Rs485
+ [2] include/linux/serial.h
--
1.6.0.4
^ permalink raw reply related [flat|nested] 43+ messages in thread
* Re: [PATCH] Documentation about RS485 serial communications
2010-08-14 12:50 ` Claudio Scordino
@ 2010-08-15 22:02 ` Randy Dunlap
2010-08-15 22:19 ` Russell King - ARM Linux
0 siblings, 1 reply; 43+ messages in thread
From: Randy Dunlap @ 2010-08-15 22:02 UTC (permalink / raw)
To: Claudio Scordino
Cc: Philippe De Muyter, Wolfram Sang, Linux Kernel, Nicolas Ferre,
Alan Cox, hskinnemoen, linux-arm-kernel, Rick Bronson,
John Nicholls, Sebastian Heutling, Ryan Mallon, rmk,
Bernhard Roth, Konrad Mattheis, Elektrolot, Prchal Jiří,
gregkh, Andrew Morton
On 08/14/10 05:50, Claudio Scordino wrote:
> Randy Dunlap ha scritto:
>> On Wed, 11 Aug 2010 11:26:23 +0200 Claudio Scordino wrote:
>>
>>> Hi all,
>>>
>>> some time ago I've been asked (by both Wolfram and Philippe) to
>>> provide some minimal documentation about the usage of the RS485
>>> interface.
>>>
>
> [...]
>
>>
>> Thanks for the addition.
>>
>
> Hi all,
>
> here is the document about RS485 with the requested additions.
>
> If OK, somebody please provide for merging.
I would expect GregKH to merge it.
Acked-by: Randy Dunlap <randy.dunlap@oracle.com>
Thanks.
> Best regards,
>
> Claudio
>
>
> Documentation about RS485 serial communications.
>
> Signed-off-by: Claudio Scordino <claudio@evidence.eu.com>
> ---
> Documentation/serial/00-INDEX | 2 +
> Documentation/serial/serial-rs485.txt | 126 +++++++++++++++++++++++++++++++++
> 2 files changed, 128 insertions(+), 0 deletions(-)
> create mode 100644 Documentation/serial/serial-rs485.txt
>
> diff --git a/Documentation/serial/00-INDEX b/Documentation/serial/00-INDEX
> index 07dcdb0..e09468a 100644
> --- a/Documentation/serial/00-INDEX
> +++ b/Documentation/serial/00-INDEX
> @@ -14,6 +14,8 @@ riscom8.txt
> - notes on using the RISCom/8 multi-port serial driver.
> rocket.txt
> - info on the Comtrol RocketPort multiport serial driver.
> +serial-rs485.txt
> + - info about RS485 structures and support in the kernel.
> specialix.txt
> - info on hardware/driver for specialix IO8+ multiport serial card.
> stallion.txt
> diff --git a/Documentation/serial/serial-rs485.txt b/Documentation/serial/serial-rs485.txt
> new file mode 100644
> index 0000000..93b029e
> --- /dev/null
> +++ b/Documentation/serial/serial-rs485.txt
> @@ -0,0 +1,126 @@
> + RS485 SERIAL COMMUNICATIONS
> +
> +1. INTRODUCTION
> +
> + EIA-485, also known as TIA/EIA-485 or RS-485, is a standard defining the
> + electrical characteristics of drivers and receivers for use in balanced
> + digital multipoint systems.
> + This standard is widely used for communications in industrial automation
> + because it can be used effectively over long distances and in electrically
> + noisy environments.
> + Even though the data is transmitted over a 2-wire twisted pair bus, all
> + EIA-485 transceivers interpret the voltage levels of the differential
> + signals with respect to a third common voltage. Without this common
> + reference, a set of transceivers may interpret the differential signals
> + incorrectly.
> + See [1] for more information.
> +
> +
> +2. HARDWARE-RELATED CONSIDERATIONS
> +
> + Some CPUs (e.g., Atmel AT91) contain a transceiver capable of working both
> + as RS232 and RS485. For these microcontrollers, the Linux driver should be
> + made capable of working in both modes, and proper ioctls (see later) should
> + be made available at user-level to allow switching from one mode to the
> + other, and vice versa.
> +
> + On some other CPUs (e.g., Freescale imx25) the RS485 transceiver is not
> + integrated inside the microcontroller itself. Therefore, manufacturers who
> + use these microcontrollers to produce embedded boards need to connect an
> + external transceiver to some pin of the CPU.
> + On these architectures, therefore, no assumptions can be done at the
> + CPU-level about the presence of a RS485 transceiver, because the connection
> + (if any) is done outside the microcontroller. Moreover, even in case of
> + RS485 transceiver, the manufacturer is free to choose the CPU pin used for
> + the connection.
> +
> +
> +3. DATA STRUCTURES ALREADY AVAILABLE IN THE KERNEL
> +
> + The Linux kernel provides the serial_rs485 structure (see [2]) to handle
> + RS485 communications. This data structure is used to set and configure RS485
> + parameters in the platform data and in ioctls.
> +
> + Any driver for devices capable of working both as RS232 and RS485 should
> + provide at least the following ioctls:
> +
> + - TIOCSRS485 (typically associated with number 0x542F). This ioctl is used
> + to enable/disable RS485 mode from user-space
> +
> + - TIOCGRS485 (typically associated with number 0x542E). This ioctl is used
> + to get RS485 mode from kernel-space (i.e., driver) to user-space.
> +
> + In other words, the serial driver should contain a code similar to the next
> + one:
> +
> + static struct uart_ops atmel_pops = {
> + /* ... */
> + .ioctl = handle_ioctl,
> + };
> +
> + static int handle_ioctl(struct uart_port *port,
> + unsigned int cmd,
> + unsigned long arg)
> + {
> + struct serial_rs485 rs485conf;
> +
> + switch (cmd) {
> + case TIOCSRS485:
> + if (copy_from_user(&rs485conf,
> + (struct serial_rs485 *) arg,
> + sizeof(rs485conf)))
> + return -EFAULT;
> +
> + /* ... */
> + break;
> +
> + case TIOCGRS485:
> + if (copy_to_user((struct serial_rs485 *) arg,
> + ...,
> + sizeof(rs485conf)))
> + return -EFAULT;
> + /* ... */
> + break;
> +
> + /* ... */
> + }
> + }
> +
> +
> +4. USAGE FROM USER-LEVEL
> +
> + From user-level, RS485 configuration can be get/set using the previous
> + ioctls. For instance, to set RS485 you can use the following code:
> +
> + #include <linux/serial.h>
> +
> + /* Driver-specific ioctls: */
> + #define TIOCGRS485 0x542E
> + #define TIOCSRS485 0x542F
> +
> + /* Open your specific device (e.g., /dev/mydevice): */
> + int fd = open ("/dev/mydevice", O_RDWR);
> + struct serial_rs485 rs485conf;
> +
> + /* Set RS485 mode: */
> + rs485conf.flags |= SER_RS485_ENABLED;
> +
> + /* Set rts delay before send, if needed: */
> + rs485conf.flags |= SER_RS485_RTS_BEFORE_SEND;
> + rs485conf.delay_rts_before_send = ...;
> +
> + /* Set rts delay after send, if needed: */
> + rs485conf.flags |= SER_RS485_RTS_AFTER_SEND;
> + rs485conf.delay_rts_after_send = ...;
> +
> + ioctl (fd, TIOCSRS485, &rs485conf);
> +
> + /* Use read() and write() syscalls here... */
> +
> + /* Close the device when finished: */
> + close (fd);
> +
> +5. REFERENCES
> +
> + [1] http://en.wikipedia.org/wiki/Rs485
> + [2] include/linux/serial.h
--
~Randy
*** Remember to use Documentation/SubmitChecklist when testing your code ***
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH] Documentation about RS485 serial communications
2010-08-15 22:02 ` Randy Dunlap
@ 2010-08-15 22:19 ` Russell King - ARM Linux
2010-10-19 12:28 ` Claudio Scordino
0 siblings, 1 reply; 43+ messages in thread
From: Russell King - ARM Linux @ 2010-08-15 22:19 UTC (permalink / raw)
To: Randy Dunlap
Cc: Claudio Scordino, Philippe De Muyter, Wolfram Sang, Linux Kernel,
Nicolas Ferre, Alan Cox, hskinnemoen, linux-arm-kernel,
Rick Bronson, John Nicholls, Sebastian Heutling, Ryan Mallon,
Bernhard Roth, Konrad Mattheis, Elektrolot, Prchal Jiří,
gregkh, Andrew Morton
On Sun, Aug 15, 2010 at 03:02:57PM -0700, Randy Dunlap wrote:
> On 08/14/10 05:50, Claudio Scordino wrote:
> > diff --git a/Documentation/serial/serial-rs485.txt b/Documentation/serial/serial-rs485.txt
> > new file mode 100644
> > index 0000000..93b029e
> > --- /dev/null
> > +++ b/Documentation/serial/serial-rs485.txt
> > @@ -0,0 +1,126 @@
> > + RS485 SERIAL COMMUNICATIONS
> > +
> > +1. INTRODUCTION
> > +
> > + EIA-485, also known as TIA/EIA-485 or RS-485, is a standard defining the
> > + electrical characteristics of drivers and receivers for use in balanced
> > + digital multipoint systems.
> > + This standard is widely used for communications in industrial automation
> > + because it can be used effectively over long distances and in electrically
> > + noisy environments.
> > + Even though the data is transmitted over a 2-wire twisted pair bus, all
> > + EIA-485 transceivers interpret the voltage levels of the differential
> > + signals with respect to a third common voltage. Without this common
> > + reference, a set of transceivers may interpret the differential signals
> > + incorrectly.
> > + See [1] for more information.
There are devices on the market which are fully isolating RS485
transceivers which just take the A/B connections, measuring the
differential voltage, and provide you with the TXD/RXD TTL signals
for your UART.
Also note that [1] appears a little confused about the number of pins
required for RS485 - it says two, and then lists three pins. I don't
think you can use this as being supportive of the requirement for three
connections - and as there are these fully isolating transceivers...
> > +4. USAGE FROM USER-LEVEL
> > +
> > + From user-level, RS485 configuration can be get/set using the previous
> > + ioctls. For instance, to set RS485 you can use the following code:
> > +
> > + #include <linux/serial.h>
> > +
> > + /* Driver-specific ioctls: */
> > + #define TIOCGRS485 0x542E
> > + #define TIOCSRS485 0x542F
> > +
> > + /* Open your specific device (e.g., /dev/mydevice): */
> > + int fd = open ("/dev/mydevice", O_RDWR);
> > + struct serial_rs485 rs485conf;
> > +
> > + /* Set RS485 mode: */
> > + rs485conf.flags |= SER_RS485_ENABLED;
> > +
> > + /* Set rts delay before send, if needed: */
> > + rs485conf.flags |= SER_RS485_RTS_BEFORE_SEND;
> > + rs485conf.delay_rts_before_send = ...;
> > +
> > + /* Set rts delay after send, if needed: */
> > + rs485conf.flags |= SER_RS485_RTS_AFTER_SEND;
> > + rs485conf.delay_rts_after_send = ...;
> > +
> > + ioctl (fd, TIOCSRS485, &rs485conf);
Example code really should do things right, such as check for error
conditions - otherwise people will copy'n'paste this and assume that
the ioctl never fails.
> > +
> > + /* Use read() and write() syscalls here... */
> > +
> > + /* Close the device when finished: */
> > + close (fd);
> > +
> > +5. REFERENCES
> > +
> > + [1] http://en.wikipedia.org/wiki/Rs485
> > + [2] include/linux/serial.h
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH] Documentation about RS485 serial communications
2010-08-15 22:19 ` Russell King - ARM Linux
@ 2010-10-19 12:28 ` Claudio Scordino
2010-10-19 14:29 ` Grant Edwards
0 siblings, 1 reply; 43+ messages in thread
From: Claudio Scordino @ 2010-10-19 12:28 UTC (permalink / raw)
To: Russell King - ARM Linux
Cc: Randy Dunlap, linux-arm-kernel, gregkh, Rick Bronson,
Konrad Mattheis, hskinnemoen, John Nicholls, Nicolas Ferre,
Linux Kernel, Wolfram Sang, Sebastian Heutling, Ryan Mallon,
Elektrolot, Philippe De Muyter, Prchal Jiří,
Andrew Morton, Bernhard Roth, Alan Cox, linux-serial
Russell King - ARM Linux ha scritto:
> On Sun, Aug 15, 2010 at 03:02:57PM -0700, Randy Dunlap wrote:
>> On 08/14/10 05:50, Claudio Scordino wrote:
>>> diff --git a/Documentation/serial/serial-rs485.txt b/Documentation/serial/serial-rs485.txt
>>> new file mode 100644
>>> index 0000000..93b029e
>>> --- /dev/null
>>> +++ b/Documentation/serial/serial-rs485.txt
>>> @@ -0,0 +1,126 @@
>>> + RS485 SERIAL COMMUNICATIONS
>>> +
>>> +1. INTRODUCTION
>>> +
>>> + EIA-485, also known as TIA/EIA-485 or RS-485, is a standard defining the
>>> + electrical characteristics of drivers and receivers for use in balanced
>>> + digital multipoint systems.
>>> + This standard is widely used for communications in industrial automation
>>> + because it can be used effectively over long distances and in electrically
>>> + noisy environments.
>>> + Even though the data is transmitted over a 2-wire twisted pair bus, all
>>> + EIA-485 transceivers interpret the voltage levels of the differential
>>> + signals with respect to a third common voltage. Without this common
>>> + reference, a set of transceivers may interpret the differential signals
>>> + incorrectly.
>>> + See [1] for more information.
>
> There are devices on the market which are fully isolating RS485
> transceivers which just take the A/B connections, measuring the
> differential voltage, and provide you with the TXD/RXD TTL signals
> for your UART.
>
> Also note that [1] appears a little confused about the number of pins
> required for RS485 - it says two, and then lists three pins. I don't
> think you can use this as being supportive of the requirement for three
> connections - and as there are these fully isolating transceivers...
>
>>> +4. USAGE FROM USER-LEVEL
>>> +
>>> + From user-level, RS485 configuration can be get/set using the previous
>>> + ioctls. For instance, to set RS485 you can use the following code:
>>> +
>>> + #include <linux/serial.h>
>>> +
>>> + /* Driver-specific ioctls: */
>>> + #define TIOCGRS485 0x542E
>>> + #define TIOCSRS485 0x542F
>>> +
>>> + /* Open your specific device (e.g., /dev/mydevice): */
>>> + int fd = open ("/dev/mydevice", O_RDWR);
>>> + struct serial_rs485 rs485conf;
>>> +
>>> + /* Set RS485 mode: */
>>> + rs485conf.flags |= SER_RS485_ENABLED;
>>> +
>>> + /* Set rts delay before send, if needed: */
>>> + rs485conf.flags |= SER_RS485_RTS_BEFORE_SEND;
>>> + rs485conf.delay_rts_before_send = ...;
>>> +
>>> + /* Set rts delay after send, if needed: */
>>> + rs485conf.flags |= SER_RS485_RTS_AFTER_SEND;
>>> + rs485conf.delay_rts_after_send = ...;
>>> +
>>> + ioctl (fd, TIOCSRS485, &rs485conf);
>
> Example code really should do things right, such as check for error
> conditions - otherwise people will copy'n'paste this and assume that
> the ioctl never fails.
Here is the new document containing the changed you've proposed.
If OK, somebody please provide for commit (the merge window is opening...).
Cheers,
Claudio
Documentation about RS485 serial communications
Signed-off-by: Claudio Scordino <claudio@evidence.eu.com>
---
Documentation/serial/00-INDEX | 2 +
Documentation/serial/serial-rs485.txt | 126 +++++++++++++++++++++++++++++++++
2 files changed, 128 insertions(+), 0 deletions(-)
create mode 100644 Documentation/serial/serial-rs485.txt
diff --git a/Documentation/serial/00-INDEX b/Documentation/serial/00-INDEX
index 07dcdb0..e09468a 100644
--- a/Documentation/serial/00-INDEX
+++ b/Documentation/serial/00-INDEX
@@ -14,6 +14,8 @@ riscom8.txt
- notes on using the RISCom/8 multi-port serial driver.
rocket.txt
- info on the Comtrol RocketPort multiport serial driver.
+serial-rs485.txt
+ - info about RS485 structures and support in the kernel.
specialix.txt
- info on hardware/driver for specialix IO8+ multiport serial card.
stallion.txt
diff --git a/Documentation/serial/serial-rs485.txt b/Documentation/serial/serial-rs485.txt
new file mode 100644
index 0000000..82ebc86
--- /dev/null
+++ b/Documentation/serial/serial-rs485.txt
@@ -0,0 +1,126 @@
+ RS485 SERIAL COMMUNICATIONS
+
+1. INTRODUCTION
+
+ EIA-485, also known as TIA/EIA-485 or RS-485, is a standard defining the
+ electrical characteristics of drivers and receivers for use in balanced
+ digital multipoint systems.
+ This standard is widely used for communications in industrial automation
+ because it can be used effectively over long distances and in electrically
+ noisy environments.
+
+2. HARDWARE-RELATED CONSIDERATIONS
+
+ Some CPUs (e.g., Atmel AT91) contain a transceiver capable of working both
+ as RS232 and RS485. For these microcontrollers, the Linux driver should be
+ made capable of working in both modes, and proper ioctls (see later) should
+ be made available at user-level to allow switching from one mode to the
+ other, and vice versa.
+
+ On some other CPUs (e.g., Freescale imx25) the RS485 transceiver is not
+ integrated inside the microcontroller itself. Therefore, manufacturers who
+ use these microcontrollers to produce embedded boards need to connect an
+ external transceiver to some pin of the CPU.
+ On these architectures, therefore, no assumptions can be done at the
+ CPU-level about the presence of a RS485 transceiver, because the connection
+ (if any) is done outside the microcontroller. Moreover, even in case of
+ RS485 transceiver, the manufacturer is free to choose the CPU pin used for
+ the connection.
+
+
+3. DATA STRUCTURES ALREADY AVAILABLE IN THE KERNEL
+
+ The Linux kernel provides the serial_rs485 structure (see [1]) to handle
+ RS485 communications. This data structure is used to set and configure RS485
+ parameters in the platform data and in ioctls.
+
+ Any driver for devices capable of working both as RS232 and RS485 should
+ provide at least the following ioctls:
+
+ - TIOCSRS485 (typically associated with number 0x542F). This ioctl is used
+ to enable/disable RS485 mode from user-space
+
+ - TIOCGRS485 (typically associated with number 0x542E). This ioctl is used
+ to get RS485 mode from kernel-space (i.e., driver) to user-space.
+
+ In other words, the serial driver should contain a code similar to the next
+ one:
+
+ static struct uart_ops atmel_pops = {
+ /* ... */
+ .ioctl = handle_ioctl,
+ };
+
+ static int handle_ioctl(struct uart_port *port,
+ unsigned int cmd,
+ unsigned long arg)
+ {
+ struct serial_rs485 rs485conf;
+
+ switch (cmd) {
+ case TIOCSRS485:
+ if (copy_from_user(&rs485conf,
+ (struct serial_rs485 *) arg,
+ sizeof(rs485conf)))
+ return -EFAULT;
+
+ /* ... */
+ break;
+
+ case TIOCGRS485:
+ if (copy_to_user((struct serial_rs485 *) arg,
+ ...,
+ sizeof(rs485conf)))
+ return -EFAULT;
+ /* ... */
+ break;
+
+ /* ... */
+ }
+ }
+
+
+4. USAGE FROM USER-LEVEL
+
+ From user-level, RS485 configuration can be get/set using the previous
+ ioctls. For instance, to set RS485 you can use the following code:
+
+ #include <linux/serial.h>
+
+ /* Driver-specific ioctls: */
+ #define TIOCGRS485 0x542E
+ #define TIOCSRS485 0x542F
+
+ /* Open your specific device (e.g., /dev/mydevice): */
+ int fd = open ("/dev/mydevice", O_RDWR);
+ if (fd < 0) {
+ /* Error handling. See errno. */
+ }
+
+ struct serial_rs485 rs485conf;
+
+ /* Set RS485 mode: */
+ rs485conf.flags |= SER_RS485_ENABLED;
+
+ /* Set rts delay before send, if needed: */
+ rs485conf.flags |= SER_RS485_RTS_BEFORE_SEND;
+ rs485conf.delay_rts_before_send = ...;
+
+ /* Set rts delay after send, if needed: */
+ rs485conf.flags |= SER_RS485_RTS_AFTER_SEND;
+ rs485conf.delay_rts_after_send = ...;
+
+ if (ioctl (fd, TIOCSRS485, &rs485conf) < 0) {
+ /* Error handling. See errno. */
+ }
+
+ /* Use read() and write() syscalls here... */
+
+ /* Close the device when finished: */
+ if (close (fd) < 0) {
+ /* Error handling. See errno. */
+ }
+
+5. REFERENCES
+
+ [1] include/linux/serial.h
^ permalink raw reply related [flat|nested] 43+ messages in thread
* Re: [PATCH] Documentation about RS485 serial communications
2010-10-19 12:28 ` Claudio Scordino
@ 2010-10-19 14:29 ` Grant Edwards
2010-10-19 15:27 ` Alexander Stein
0 siblings, 1 reply; 43+ messages in thread
From: Grant Edwards @ 2010-10-19 14:29 UTC (permalink / raw)
To: linux-kernel; +Cc: linux-serial, linux-arm-kernel, linux-serial, linux-kernel
On 2010-10-19, Claudio Scordino <claudio@evidence.eu.com> wrote:
> +2. HARDWARE-RELATED CONSIDERATIONS
> +
> + Some CPUs (e.g., Atmel AT91) contain a transceiver capable of working both
> + as RS232 and RS485.
Pardon my curiosity, but I've looked at a few AT91 parts, and I've
never seen on with an internal transceiver -- nor does Google seem
able to find any.
Frankly, I'd be pretty surprised, since it would be very difficult
(fab-wise), to build a mircoprocessor with I/O cells capable of
withstanding the voltage levels specified in RS232.
Can you specify to which parts you're referring?
--
Grant Edwards grant.b.edwards Yow! I have the power to
at HALT PRODUCTION on all
gmail.com TEENAGE SEX COMEDIES!!
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH] Documentation about RS485 serial communications
2010-10-19 14:29 ` Grant Edwards
@ 2010-10-19 15:27 ` Alexander Stein
[not found] ` <i9kk4o$svc$1@dough.gmane.org>
0 siblings, 1 reply; 43+ messages in thread
From: Alexander Stein @ 2010-10-19 15:27 UTC (permalink / raw)
To: linux-arm-kernel; +Cc: Grant Edwards, linux-kernel, linux-serial
On Tuesday 19 October 2010, 16:29:48 Grant Edwards wrote:
> On 2010-10-19, Claudio Scordino <claudio@evidence.eu.com> wrote:
> > +2. HARDWARE-RELATED CONSIDERATIONS
> > +
> > + Some CPUs (e.g., Atmel AT91) contain a transceiver capable of working
> > both + as RS232 and RS485.
>
> Pardon my curiosity, but I've looked at a few AT91 parts, and I've
> never seen on with an internal transceiver -- nor does Google seem
> able to find any.
>
> Frankly, I'd be pretty surprised, since it would be very difficult
> (fab-wise), to build a mircoprocessor with I/O cells capable of
> withstanding the voltage levels specified in RS232.
>
> Can you specify to which parts you're referring?
I doubt the AT91 do have an internal transceiver but it has a dedicated pin
feature (RTS or CTS or something) which can be used to switch between RS232
and RS485 by setting a bit in the USART provided the pin is connected
properly.
Regards,
Alexander
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH] atmel_serial: Atmel RS485 support v2
2010-05-27 9:57 ` Nicolas Ferre
2010-05-27 10:29 ` Wolfram Sang
@ 2010-05-28 9:42 ` Haavard Skinnemoen
2010-05-28 13:54 ` [PATCH] MAINTAINERS: some Atmel drivers change maintainer Nicolas Ferre
1 sibling, 1 reply; 43+ messages in thread
From: Haavard Skinnemoen @ 2010-05-28 9:42 UTC (permalink / raw)
To: Nicolas Ferre
Cc: Claudio Scordino, Alan Cox, linux, linux-arm-kernel,
Rick Bronson, John Nicholls, Linux Kernel, Sebastian Heutling,
Ryan Mallon, michael trimarchi, rmk
Nicolas Ferre <nicolas.ferre@atmel.com> wrote:
> > (the maintainer of the driver never answered to emails...).
>
> I was supposed to take over its maintainance one year ago but did not
> find time to do it. My fault here also...
My fault too...I should really try harder to respond to e-mails. I was
planning to give it a spin and send it upstream, but I never found the
time.
Nicolas, is it ok if I send a patch to update MAINTAINERS? That way,
you'll receive these patches and questions about them by e-mail rather
than me.
Haavard
^ permalink raw reply [flat|nested] 43+ messages in thread
* [PATCH] MAINTAINERS: some Atmel drivers change maintainer
2010-05-28 9:42 ` [PATCH] atmel_serial: Atmel RS485 support v2 Haavard Skinnemoen
@ 2010-05-28 13:54 ` Nicolas Ferre
2010-05-28 13:31 ` Haavard Skinnemoen
2010-06-11 7:23 ` Nicolas Ferre
0 siblings, 2 replies; 43+ messages in thread
From: Nicolas Ferre @ 2010-05-28 13:54 UTC (permalink / raw)
To: linux-arm-kernel, haavard.skinnemoen
Cc: linux-kernel, avictor.za, Nicolas Ferre
I take over the maintenance of SPI, USART, Ethernet and USB gadget drivers.
Those drivers are found in Atmel microcontrollers, both AT32/AVR32 and
AT91/ARM.
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
---
Haavard,
I finally change the MAINTAINERS entries for all drivers that you were
taking care of. I must admit that I was a bit frighten at the first place but
anyway, since one year, I am getting used to it ;-)
MAINTAINERS | 10 +++++-----
1 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/MAINTAINERS b/MAINTAINERS
index 33047a6..d9ce9c6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1148,7 +1148,7 @@ F: drivers/mmc/host/atmel-mci.c
F: drivers/mmc/host/atmel-mci-regs.h
ATMEL AT91 / AT32 SERIAL DRIVER
-M: Haavard Skinnemoen <hskinnemoen@atmel.com>
+M: Nicolas Ferre <nicolas.ferre@atmel.com>
S: Supported
F: drivers/serial/atmel_serial.c
@@ -1160,18 +1160,18 @@ F: drivers/video/atmel_lcdfb.c
F: include/video/atmel_lcdc.h
ATMEL MACB ETHERNET DRIVER
-M: Haavard Skinnemoen <hskinnemoen@atmel.com>
+M: Nicolas Ferre <nicolas.ferre@atmel.com>
S: Supported
F: drivers/net/macb.*
ATMEL SPI DRIVER
-M: Haavard Skinnemoen <hskinnemoen@atmel.com>
+M: Nicolas Ferre <nicolas.ferre@atmel.com>
S: Supported
F: drivers/spi/atmel_spi.*
ATMEL USBA UDC DRIVER
-M: Haavard Skinnemoen <hskinnemoen@atmel.com>
-L: kernel@avr32linux.org
+M: Nicolas Ferre <nicolas.ferre@atmel.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
W: http://avr32linux.org/twiki/bin/view/Main/AtmelUsbDeviceDriver
S: Supported
F: drivers/usb/gadget/atmel_usba_udc.*
--
1.5.6.5
^ permalink raw reply related [flat|nested] 43+ messages in thread
* Re: [PATCH] MAINTAINERS: some Atmel drivers change maintainer
2010-05-28 13:54 ` [PATCH] MAINTAINERS: some Atmel drivers change maintainer Nicolas Ferre
@ 2010-05-28 13:31 ` Haavard Skinnemoen
2010-06-11 7:23 ` Nicolas Ferre
1 sibling, 0 replies; 43+ messages in thread
From: Haavard Skinnemoen @ 2010-05-28 13:31 UTC (permalink / raw)
To: Nicolas Ferre; +Cc: linux-arm-kernel, linux-kernel, avictor.za
Nicolas Ferre <nicolas.ferre@atmel.com> wrote:
> I take over the maintenance of SPI, USART, Ethernet and USB gadget drivers.
> Those drivers are found in Atmel microcontrollers, both AT32/AVR32 and
> AT91/ARM.
>
> Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Acked-by: Haavard Skinnemoen <haavard.skinnemoen@atmel.com>
> ---
> Haavard,
> I finally change the MAINTAINERS entries for all drivers that you were
> taking care of. I must admit that I was a bit frighten at the first place but
> anyway, since one year, I am getting used to it ;-)
Good to hear that you're getting the hang of it :-)
Haavard
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH] MAINTAINERS: some Atmel drivers change maintainer
2010-05-28 13:54 ` [PATCH] MAINTAINERS: some Atmel drivers change maintainer Nicolas Ferre
2010-05-28 13:31 ` Haavard Skinnemoen
@ 2010-06-11 7:23 ` Nicolas Ferre
1 sibling, 0 replies; 43+ messages in thread
From: Nicolas Ferre @ 2010-06-11 7:23 UTC (permalink / raw)
To: linux-arm-kernel, linux-kernel
Cc: Nicolas Ferre, haavard.skinnemoen, avictor.za
Le 28/05/2010 15:54, Nicolas Ferre :
> I take over the maintenance of SPI, USART, Ethernet and USB gadget drivers.
> Those drivers are found in Atmel microcontrollers, both AT32/AVR32 and
> AT91/ARM.
>
> Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Ping,
With added acked-by:
Acked-by: Haavard Skinnemoen <haavard.skinnemoen@atmel.com>
> ---
> Haavard,
> I finally change the MAINTAINERS entries for all drivers that you were
> taking care of. I must admit that I was a bit frighten at the first place but
> anyway, since one year, I am getting used to it ;-)
>
>
> MAINTAINERS | 10 +++++-----
> 1 files changed, 5 insertions(+), 5 deletions(-)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 33047a6..d9ce9c6 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1148,7 +1148,7 @@ F: drivers/mmc/host/atmel-mci.c
> F: drivers/mmc/host/atmel-mci-regs.h
>
> ATMEL AT91 / AT32 SERIAL DRIVER
> -M: Haavard Skinnemoen <hskinnemoen@atmel.com>
> +M: Nicolas Ferre <nicolas.ferre@atmel.com>
> S: Supported
> F: drivers/serial/atmel_serial.c
>
> @@ -1160,18 +1160,18 @@ F: drivers/video/atmel_lcdfb.c
> F: include/video/atmel_lcdc.h
>
> ATMEL MACB ETHERNET DRIVER
> -M: Haavard Skinnemoen <hskinnemoen@atmel.com>
> +M: Nicolas Ferre <nicolas.ferre@atmel.com>
> S: Supported
> F: drivers/net/macb.*
>
> ATMEL SPI DRIVER
> -M: Haavard Skinnemoen <hskinnemoen@atmel.com>
> +M: Nicolas Ferre <nicolas.ferre@atmel.com>
> S: Supported
> F: drivers/spi/atmel_spi.*
>
> ATMEL USBA UDC DRIVER
> -M: Haavard Skinnemoen <hskinnemoen@atmel.com>
> -L: kernel@avr32linux.org
> +M: Nicolas Ferre <nicolas.ferre@atmel.com>
> +L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
> W: http://avr32linux.org/twiki/bin/view/Main/AtmelUsbDeviceDriver
> S: Supported
> F: drivers/usb/gadget/atmel_usba_udc.*
--
Nicolas Ferre
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH] Documentation about RS485 serial communications
@ 2010-08-11 20:20 Randy Dunlap
0 siblings, 0 replies; 43+ messages in thread
From: Randy Dunlap @ 2010-08-11 20:20 UTC (permalink / raw)
To: claudio
Cc: alan, jiri.prchal, rick, akpm, ryan, w.sang, rmk, nicolas.ferre,
br, gregkh, Sebastian.Heutling, hskinnemoen, linux-arm-kernel,
elektrolot, mattheis, linux-kernel, john, phdm
----- Original Message -----
From: claudio@evidence.eu.com
To: randy.dunlap@oracle.com
Sent: Wednesday, August 11, 2010 12:59:08 PM GMT -08:00 US/Canada Pacific
Subject: Re: [PATCH] Documentation about RS485 serial communications
Hi Randy,
thank you for the feedback.
[...]
>
> TIOC[SG]RS485 are #defined in <asm-generic/ioctls.h>, so
> #include <asm-generic/ioctls.h>
> here and/or below (in userspace program).
>
I noticed that for some architectures (e.g., cris) these ioctls are defined also in arch/cris/include/asm/ioctls.h, and with different values with respect to the values defined on asm-generic/ioctls.h.
Therefore, I wasn't completely sure that the values defined in asm-generic are being used in every driver...
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Oh, OK, I see. Thanks for the info.
~Randy
^ permalink raw reply [flat|nested] 43+ messages in thread
end of thread, other threads:[~2010-11-30 19:19 UTC | newest]
Thread overview: 43+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-03-29 7:16 [PATCH] atmel_serial: Atmel RS485 support v2 Claudio Scordino
2010-03-29 19:44 ` Ryan Mallon
2010-03-30 9:07 ` Claudio Scordino
2010-03-30 19:37 ` Ryan Mallon
2010-04-08 7:58 ` Claudio Scordino
2010-04-08 9:01 ` Russell King - ARM Linux
2010-04-08 10:13 ` Alan Cox
2010-04-08 13:16 ` Claudio Scordino
2010-04-08 13:42 ` Alan Cox
2010-05-26 13:18 ` Nicolas Ferre
2010-05-27 8:37 ` Claudio Scordino
2010-05-27 9:57 ` Nicolas Ferre
2010-05-27 10:29 ` Wolfram Sang
2010-05-28 15:05 ` Claudio Scordino
2010-08-11 9:26 ` [PATCH] Documentation about RS485 serial communications Claudio Scordino
2010-08-11 10:02 ` Philippe De Muyter
2010-08-11 15:32 ` Randy Dunlap
2010-08-11 19:58 ` Claudio Scordino
2010-08-14 12:50 ` Claudio Scordino
2010-08-15 22:02 ` Randy Dunlap
2010-08-15 22:19 ` Russell King - ARM Linux
2010-10-19 12:28 ` Claudio Scordino
2010-10-19 14:29 ` Grant Edwards
2010-10-19 15:27 ` Alexander Stein
[not found] ` <i9kk4o$svc$1@dough.gmane.org>
2010-10-20 8:00 ` Alexander Stein
2010-10-20 18:56 ` Alan Cox
2010-10-24 11:29 ` Claudio Scordino
2010-11-10 9:17 ` Nicolas Ferre
2010-11-10 17:28 ` Greg KH
2010-11-11 10:22 ` Claudio Scordino
2010-11-16 14:30 ` Pavel Machek
2010-11-16 15:28 ` Alexander Stein
2010-11-16 16:13 ` Matt Schulte
2010-11-16 17:20 ` Alan Cox
2010-11-16 18:03 ` Grant Edwards
2010-11-16 20:04 ` Alan Cox
2010-11-30 19:19 ` Pavel Machek
[not found] ` <ibubf5$ev$1@dough.gmane.org>
2010-11-16 18:41 ` Matt Schulte
2010-05-28 9:42 ` [PATCH] atmel_serial: Atmel RS485 support v2 Haavard Skinnemoen
2010-05-28 13:54 ` [PATCH] MAINTAINERS: some Atmel drivers change maintainer Nicolas Ferre
2010-05-28 13:31 ` Haavard Skinnemoen
2010-06-11 7:23 ` Nicolas Ferre
2010-08-11 20:20 [PATCH] Documentation about RS485 serial communications Randy Dunlap
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).