All of lore.kernel.org
 help / color / mirror / Atom feed
From: ryan@bluewatersys.com (Ryan Mallon)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH] atmel_serial: Atmel RS485 support
Date: Mon, 22 Mar 2010 09:15:31 +1300	[thread overview]
Message-ID: <4BA67E63.6020909@bluewatersys.com> (raw)
In-Reply-To: <4BA335A8.1000205@evidence.eu.com>

Claudio Scordino wrote:
> Hi all,
>
>   this is a patch to add RS485 support to the atmel_serial driver.
>
> The patch has been successfully tested by Sebastian Heutling (CC:-ed).
>
> Feedback (comments, suggestions, further testing) is welcome.
>
> Many thanks,
>
>           Claudio
>
Please post patches inline in the message body as it makes them easier
to reply to. Some comments below:

> atmel_serial: 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 | 4 +
> drivers/serial/atmel_serial.c | 247 ++++++++++++++++++++++++++----- 3
> files changed, 218 insertions(+), 35 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..21f1308
> 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,9 @@ 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; /* this flag specifies + if the uart must
> be used + as RS485 */ 
This comment is misleading, it is a struct not a flag. The comment should 
say that it is the rs485 settings for the uart.

> }; extern void __init at91_add_device_serial(void); diff --git
> a/drivers/serial/atmel_serial.c b/drivers/serial/atmel_serial.c index
> 2c9bf9b..21bf3a8 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); 
Can you move these functions, so that these declarations are not needed?

> #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,16 @@ struct atmel_uart_port { unsigned
> int irq_status_prev; struct circ_buf rx_ring; + + struct serial_rs485
> rs485; /* this flag specifies + if the uart must be used + as RS485 */ 
Same here.

> + /* Fields related to interrupts: + if (rs485) = ATMEL_US_TXEMPTY +
> else if (use_dma_tx) = ATMEL_US_ENDTX | ATMEL_US_TXBUFE + else =
> ATMEL_US_TXRDY; + */ 
This comment doesn't really belong here. Also, please use *s as the beginning
of each line in a multi-line comment.

> + unsigned int tx_done_mask; }; static struct atmel_uart_port
> atmel_ports[ATMEL_MAX_UART]; @@ -187,6 +202,85 @@ static bool
> atmel_use_dma_tx(struct uart_port *port) } #endif +/* Enable or
> disable the rs485 support */ +void atmel_enable_disable_rs485(struct
> uart_port *port, struct serial_rs485 *rs485conf) 
Please change the name of this function, something like atmel_config_rs485
would be better.

> +{ + 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)) { 
Reversing the logic of this if statement will make it slightly easier to read.

> + 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; +
> } else { + 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; +
> } + 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); + current_mode &= ATMEL_US_USMODE; 
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 == NULL) + goto err; 
if (!buf)
	goto -EINVAL;

> + + value = simple_strtoul(buf, NULL, 10); + + if ((value != 0) &&
> (value != 1)) + goto err; 
Is there any reason to be so strict on the values, you could just do this:

value = !!(simple_strtoul(buf, NULL, 0));

which will allow the user to use values in any base, and will convert any 
non-zero values to 1.

> + if (!value) { + rs485conf.flags &= ~(SER_RS485_ENABLED); 
Again, I would revert the logic of this if statement.

> + } else { + rs485conf.flags |= SER_RS485_ENABLED; + /* 0x4 is the
> normal reset value. */ + rs485conf.delay_rts_before_send = 0x00000004;
> + } + + atmel_enable_disable_rs485(port, &rs485conf); + +err: + 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 +296,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 +331,16 @@ 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 +373,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 +391,22 @@ 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); } /* @@ -302,12
> +417,28 @@ 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); + UART_PUT_IDR(port,
> ATMEL_US_ENDRX | ATMEL_US_TIMEOUT | port->read_status_mask); 
This line, and a couple of others extend over 80 characters, can you please
reformat, ie:

UART_PUT_IDR(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT | 
	     port->read_status_mask);

> } else UART_PUT_IDR(port, ATMEL_US_RXRDY); 

Please use braces even on single line else statements in the case where the
if statement has braces.

> } /* + * 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); +} + + +/* * Enable modem
> status interrupts */ static void atmel_enable_ms(struct uart_port
> *port) @@ -428,8 +559,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 +569,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 +581,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 +634,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 +715,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 +1148,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 +1247,16 @@ 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 +1373,31 @@ 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) +{ + + switch (cmd) { + case TIOCSRS485: + { +
> struct serial_rs485 rs485conf; 
Moving this declaration to the top of the function will remove two 
indentation levels.

Is it necessary to have both an ioctl and a sysfs control for putting the
uart into rs485 mode? The ioctl is more standardised, so perhaps drop the
sysfs control?

> + if (copy_from_user(&rs485conf, (struct serial_rs485 *) arg, +
> sizeof(rs485conf))) + return -EFAULT; + + dev_dbg(port->dev, "enable
> e/o disable rs485\n"); + + atmel_enable_disable_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 +1417,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 +1433,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 +1466,15 @@ 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 +1508,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 +1516,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 +1530,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 +1762,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 +1794,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 
~Ryan

WARNING: multiple messages have this Message-ID (diff)
From: Ryan Mallon <ryan@bluewatersys.com>
To: Claudio Scordino <claudio@evidence.eu.com>
Cc: Linux Kernel <linux-kernel@vger.kernel.org>,
	kernel@arm.linux.org.uk, Rick Bronson <rick@efn.org>,
	John Nicholls <john@thinlinx.com>,
	Sebastian Heutling <Sebastian.Heutling@who-ing.de>,
	linux-arm-kernel <linux-arm-kernel@lists.infradead.org>,
	michael trimarchi <michael@evidence.eu.com>,
	alan@lxorguk.ukuu.org.uk
Subject: Re: [PATCH] atmel_serial: Atmel RS485 support
Date: Mon, 22 Mar 2010 09:15:31 +1300	[thread overview]
Message-ID: <4BA67E63.6020909@bluewatersys.com> (raw)
In-Reply-To: <4BA335A8.1000205@evidence.eu.com>

Claudio Scordino wrote:
> Hi all,
>
>   this is a patch to add RS485 support to the atmel_serial driver.
>
> The patch has been successfully tested by Sebastian Heutling (CC:-ed).
>
> Feedback (comments, suggestions, further testing) is welcome.
>
> Many thanks,
>
>           Claudio
>
Please post patches inline in the message body as it makes them easier
to reply to. Some comments below:

> atmel_serial: 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 | 4 +
> drivers/serial/atmel_serial.c | 247 ++++++++++++++++++++++++++----- 3
> files changed, 218 insertions(+), 35 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..21f1308
> 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,9 @@ 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; /* this flag specifies + if the uart must
> be used + as RS485 */ 
This comment is misleading, it is a struct not a flag. The comment should 
say that it is the rs485 settings for the uart.

> }; extern void __init at91_add_device_serial(void); diff --git
> a/drivers/serial/atmel_serial.c b/drivers/serial/atmel_serial.c index
> 2c9bf9b..21bf3a8 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); 
Can you move these functions, so that these declarations are not needed?

> #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,16 @@ struct atmel_uart_port { unsigned
> int irq_status_prev; struct circ_buf rx_ring; + + struct serial_rs485
> rs485; /* this flag specifies + if the uart must be used + as RS485 */ 
Same here.

> + /* Fields related to interrupts: + if (rs485) = ATMEL_US_TXEMPTY +
> else if (use_dma_tx) = ATMEL_US_ENDTX | ATMEL_US_TXBUFE + else =
> ATMEL_US_TXRDY; + */ 
This comment doesn't really belong here. Also, please use *s as the beginning
of each line in a multi-line comment.

> + unsigned int tx_done_mask; }; static struct atmel_uart_port
> atmel_ports[ATMEL_MAX_UART]; @@ -187,6 +202,85 @@ static bool
> atmel_use_dma_tx(struct uart_port *port) } #endif +/* Enable or
> disable the rs485 support */ +void atmel_enable_disable_rs485(struct
> uart_port *port, struct serial_rs485 *rs485conf) 
Please change the name of this function, something like atmel_config_rs485
would be better.

> +{ + 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)) { 
Reversing the logic of this if statement will make it slightly easier to read.

> + 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; +
> } else { + 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; +
> } + 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); + current_mode &= ATMEL_US_USMODE; 
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 == NULL) + goto err; 
if (!buf)
	goto -EINVAL;

> + + value = simple_strtoul(buf, NULL, 10); + + if ((value != 0) &&
> (value != 1)) + goto err; 
Is there any reason to be so strict on the values, you could just do this:

value = !!(simple_strtoul(buf, NULL, 0));

which will allow the user to use values in any base, and will convert any 
non-zero values to 1.

> + if (!value) { + rs485conf.flags &= ~(SER_RS485_ENABLED); 
Again, I would revert the logic of this if statement.

> + } else { + rs485conf.flags |= SER_RS485_ENABLED; + /* 0x4 is the
> normal reset value. */ + rs485conf.delay_rts_before_send = 0x00000004;
> + } + + atmel_enable_disable_rs485(port, &rs485conf); + +err: + 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 +296,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 +331,16 @@ 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 +373,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 +391,22 @@ 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); } /* @@ -302,12
> +417,28 @@ 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); + UART_PUT_IDR(port,
> ATMEL_US_ENDRX | ATMEL_US_TIMEOUT | port->read_status_mask); 
This line, and a couple of others extend over 80 characters, can you please
reformat, ie:

UART_PUT_IDR(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT | 
	     port->read_status_mask);

> } else UART_PUT_IDR(port, ATMEL_US_RXRDY); 

Please use braces even on single line else statements in the case where the
if statement has braces.

> } /* + * 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); +} + + +/* * Enable modem
> status interrupts */ static void atmel_enable_ms(struct uart_port
> *port) @@ -428,8 +559,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 +569,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 +581,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 +634,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 +715,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 +1148,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 +1247,16 @@ 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 +1373,31 @@ 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) +{ + + switch (cmd) { + case TIOCSRS485: + { +
> struct serial_rs485 rs485conf; 
Moving this declaration to the top of the function will remove two 
indentation levels.

Is it necessary to have both an ioctl and a sysfs control for putting the
uart into rs485 mode? The ioctl is more standardised, so perhaps drop the
sysfs control?

> + if (copy_from_user(&rs485conf, (struct serial_rs485 *) arg, +
> sizeof(rs485conf))) + return -EFAULT; + + dev_dbg(port->dev, "enable
> e/o disable rs485\n"); + + atmel_enable_disable_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 +1417,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 +1433,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 +1466,15 @@ 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 +1508,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 +1516,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 +1530,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 +1762,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 +1794,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 
~Ryan



  reply	other threads:[~2010-03-21 20:15 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-03-19  8:28 [PATCH] atmel_serial: Atmel RS485 support Claudio Scordino
2010-03-19  8:28 ` Claudio Scordino
2010-03-21 20:15 ` Ryan Mallon [this message]
2010-03-21 20:15   ` Ryan Mallon
2010-03-21 21:55 ` Ryan Mallon
2010-03-21 21:55   ` Ryan Mallon
2010-03-22 11:15   ` Claudio Scordino
2010-03-22 11:15     ` Claudio Scordino

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4BA67E63.6020909@bluewatersys.com \
    --to=ryan@bluewatersys.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.