Merge branch 'tty-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
[pandora-kernel.git] / drivers / tty / serial / atmel_serial.c
index b922f5d..9988c0c 100644 (file)
@@ -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),
        },
 };