aboutsummaryrefslogtreecommitdiff
path: root/drivers/tty/serial/atmel_serial.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2011-10-26 15:11:09 +0200
committerLinus Torvalds <torvalds@linux-foundation.org>2011-10-26 15:11:09 +0200
commitefb8d21b2c6db3497655cc6a033ae8a9883e4063 (patch)
treea14a0dbb9fec3a6db5e542ba7ed4a49681706420 /drivers/tty/serial/atmel_serial.c
parent3cb603284b3d256ae9ae9e65887cee8416bfef15 (diff)
parentd208a3bf77f902283894f546b6b5383202cf7882 (diff)
Merge branch 'tty-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
* 'tty-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (79 commits) TTY: serial_core: Fix crash if DCD drop during suspend tty/serial: atmel_serial: bootconsole removed from auto-enumerates Revert "TTY: call tty_driver_lookup_tty unconditionally" tty/serial: atmel_serial: add device tree support tty/serial: atmel_serial: auto-enumerate ports tty/serial: atmel_serial: whitespace and braces modifications tty/serial: atmel_serial: change platform_data variable name tty/serial: RS485 bindings for device tree TTY: call tty_driver_lookup_tty unconditionally TTY: pty, release tty in all ptmx_open fail paths TTY: make tty_add_file non-failing TTY: drop driver reference in tty_open fail path 8250_pci: Fix kernel panic when pch_uart is disabled h8300: drivers/serial/Kconfig was moved parport_pc: release IO region properly if unsupported ITE887x card is found tty: Support compat_ioctl get/set termios_locked hvc_console: display printk messages on console. TTY: snyclinkmp: forever loop in tx_load_dma_buffer() tty/n_gsm: avoid fifo overflow in gsm_dlci_data_output tty/n_gsm: fix a bug in gsm_dlci_data_output (adaption = 2 case) ... Fix up Conflicts in: - drivers/tty/serial/8250_pci.c Trivial conflict with removed duplicate device ID - drivers/tty/serial/atmel_serial.c Annoying silly conflict between "specify the port num via platform_data" and other changes to atmel_console_init
Diffstat (limited to 'drivers/tty/serial/atmel_serial.c')
-rw-r--r--drivers/tty/serial/atmel_serial.c128
1 files changed, 111 insertions, 17 deletions
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index b922f5d2e61..9988c0c305c 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -33,6 +33,8 @@
#include <linux/sysrq.h>
#include <linux/tty_flip.h>
#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/dma-mapping.h>
#include <linux/atmel_pdc.h>
#include <linux/atmel_serial.h>
@@ -157,11 +159,22 @@ struct atmel_uart_port {
};
static struct atmel_uart_port atmel_ports[ATMEL_MAX_UART];
+static unsigned long atmel_ports_in_use;
#ifdef SUPPORT_SYSRQ
static struct console atmel_console;
#endif
+#if defined(CONFIG_OF)
+static const struct of_device_id atmel_serial_dt_ids[] = {
+ { .compatible = "atmel,at91rm9200-usart" },
+ { .compatible = "atmel,at91sam9260-usart" },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, atmel_serial_dt_ids);
+#endif
+
static inline struct atmel_uart_port *
to_atmel_uart_port(struct uart_port *uart)
{
@@ -339,7 +352,8 @@ static void atmel_stop_tx(struct uart_port *port)
/* Disable interrupts */
UART_PUT_IDR(port, atmel_port->tx_done_mask);
- if (atmel_port->rs485.flags & SER_RS485_ENABLED)
+ if ((atmel_port->rs485.flags & SER_RS485_ENABLED) &&
+ !(atmel_port->rs485.flags & SER_RS485_RX_DURING_TX))
atmel_start_rx(port);
}
@@ -356,7 +370,8 @@ static void atmel_start_tx(struct uart_port *port)
really need this.*/
return;
- if (atmel_port->rs485.flags & SER_RS485_ENABLED)
+ if ((atmel_port->rs485.flags & SER_RS485_ENABLED) &&
+ !(atmel_port->rs485.flags & SER_RS485_RX_DURING_TX))
atmel_stop_rx(port);
/* re-enable PDC transmit */
@@ -680,7 +695,8 @@ static void atmel_tx_dma(struct uart_port *port)
/* Enable interrupts */
UART_PUT_IER(port, atmel_port->tx_done_mask);
} else {
- if (atmel_port->rs485.flags & SER_RS485_ENABLED) {
+ if ((atmel_port->rs485.flags & SER_RS485_ENABLED) &&
+ !(atmel_port->rs485.flags & SER_RS485_RX_DURING_TX)) {
/* DMA done, stop TX, start RX for RS485 */
atmel_start_rx(port);
}
@@ -1407,6 +1423,48 @@ static struct uart_ops atmel_pops = {
#endif
};
+static void __devinit atmel_of_init_port(struct atmel_uart_port *atmel_port,
+ struct device_node *np)
+{
+ u32 rs485_delay[2];
+
+ /* DMA/PDC usage specification */
+ if (of_get_property(np, "atmel,use-dma-rx", NULL))
+ atmel_port->use_dma_rx = 1;
+ else
+ atmel_port->use_dma_rx = 0;
+ if (of_get_property(np, "atmel,use-dma-tx", NULL))
+ atmel_port->use_dma_tx = 1;
+ else
+ atmel_port->use_dma_tx = 0;
+
+ /* rs485 properties */
+ if (of_property_read_u32_array(np, "rs485-rts-delay",
+ rs485_delay, 2) == 0) {
+ struct serial_rs485 *rs485conf = &atmel_port->rs485;
+
+ rs485conf->delay_rts_before_send = rs485_delay[0];
+ rs485conf->delay_rts_after_send = rs485_delay[1];
+ rs485conf->flags = 0;
+
+ if (rs485conf->delay_rts_before_send == 0 &&
+ rs485conf->delay_rts_after_send == 0) {
+ rs485conf->flags |= SER_RS485_RTS_ON_SEND;
+ } else {
+ if (rs485conf->delay_rts_before_send)
+ rs485conf->flags |= SER_RS485_RTS_BEFORE_SEND;
+ if (rs485conf->delay_rts_after_send)
+ rs485conf->flags |= SER_RS485_RTS_AFTER_SEND;
+ }
+
+ if (of_get_property(np, "rs485-rx-during-tx", NULL))
+ rs485conf->flags |= SER_RS485_RX_DURING_TX;
+
+ if (of_get_property(np, "linux,rs485-enabled-at-boot-time", NULL))
+ rs485conf->flags |= SER_RS485_ENABLED;
+ }
+}
+
/*
* Configure the port from the platform device resource info.
*/
@@ -1414,13 +1472,20 @@ static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port,
struct platform_device *pdev)
{
struct uart_port *port = &atmel_port->uart;
- struct atmel_uart_data *data = pdev->dev.platform_data;
+ struct atmel_uart_data *pdata = pdev->dev.platform_data;
+
+ if (pdev->dev.of_node) {
+ atmel_of_init_port(atmel_port, pdev->dev.of_node);
+ } else {
+ atmel_port->use_dma_rx = pdata->use_dma_rx;
+ atmel_port->use_dma_tx = pdata->use_dma_tx;
+ atmel_port->rs485 = pdata->rs485;
+ }
port->iotype = UPIO_MEM;
port->flags = UPF_BOOT_AUTOCONF;
port->ops = &atmel_pops;
port->fifosize = 1;
- port->line = data->num;
port->dev = &pdev->dev;
port->mapbase = pdev->resource[0].start;
port->irq = pdev->resource[1].start;
@@ -1430,10 +1495,10 @@ static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port,
memset(&atmel_port->rx_ring, 0, sizeof(atmel_port->rx_ring));
- if (data->regs)
+ if (pdata && pdata->regs) {
/* Already mapped by setup code */
- port->membase = data->regs;
- else {
+ port->membase = pdata->regs;
+ } else {
port->flags |= UPF_IOREMAP;
port->membase = NULL;
}
@@ -1447,9 +1512,6 @@ static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port,
/* only enable clock when USART is in use */
}
- atmel_port->use_dma_rx = data->use_dma_rx;
- atmel_port->use_dma_tx = data->use_dma_tx;
- 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;
@@ -1611,10 +1673,14 @@ static int __init atmel_console_init(void)
if (atmel_default_console_device) {
struct atmel_uart_data *pdata =
atmel_default_console_device->dev.platform_data;
+ int id = pdata->num;
+ struct atmel_uart_port *port = &atmel_ports[id];
+
+ port->backup_imr = 0;
+ port->uart.line = id;
- add_preferred_console(ATMEL_DEVICENAME, pdata->num, NULL);
- atmel_init_port(&atmel_ports[pdata->num],
- atmel_default_console_device);
+ add_preferred_console(ATMEL_DEVICENAME, id, NULL);
+ atmel_init_port(port, atmel_default_console_device);
register_console(&atmel_console);
}
@@ -1711,14 +1777,39 @@ static int atmel_serial_resume(struct platform_device *pdev)
static int __devinit atmel_serial_probe(struct platform_device *pdev)
{
struct atmel_uart_port *port;
+ struct device_node *np = pdev->dev.of_node;
struct atmel_uart_data *pdata = pdev->dev.platform_data;
void *data;
- int ret;
+ int ret = -ENODEV;
BUILD_BUG_ON(ATMEL_SERIAL_RINGSIZE & (ATMEL_SERIAL_RINGSIZE - 1));
- port = &atmel_ports[pdata->num];
+ if (np)
+ ret = of_alias_get_id(np, "serial");
+ else
+ if (pdata)
+ ret = pdata->num;
+
+ if (ret < 0)
+ /* port id not found in platform data nor device-tree aliases:
+ * auto-enumerate it */
+ ret = find_first_zero_bit(&atmel_ports_in_use,
+ sizeof(atmel_ports_in_use));
+
+ if (ret > ATMEL_MAX_UART) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ if (test_and_set_bit(ret, &atmel_ports_in_use)) {
+ /* port already in use */
+ ret = -EBUSY;
+ goto err;
+ }
+
+ port = &atmel_ports[ret];
port->backup_imr = 0;
+ port->uart.line = ret;
atmel_init_port(port, pdev);
@@ -1764,7 +1855,7 @@ err_alloc_ring:
clk_put(port->clk);
port->clk = NULL;
}
-
+err:
return ret;
}
@@ -1784,6 +1875,8 @@ static int __devexit atmel_serial_remove(struct platform_device *pdev)
/* "port" is allocated statically, so we shouldn't free it */
+ clear_bit(port->line, &atmel_ports_in_use);
+
clk_put(atmel_port->clk);
return ret;
@@ -1797,6 +1890,7 @@ static struct platform_driver atmel_serial_driver = {
.driver = {
.name = "atmel_usart",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(atmel_serial_dt_ids),
},
};