tty: ar933x_uart: add device tree support and binding documentation
[deliverable/linux.git] / drivers / tty / serial / ar933x_uart.c
index 27f20c57abede4ec6874ec4f1de5654b6d618da9..acd03af7cd52287759a5b06275119f0856325fc0 100644 (file)
@@ -17,6 +17,8 @@
 #include <linux/sysrq.h>
 #include <linux/delay.h>
 #include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
 #include <linux/tty.h>
 #include <linux/tty_flip.h>
 #include <linux/serial_core.h>
 #include <linux/slab.h>
 #include <linux/io.h>
 #include <linux/irq.h>
+#include <linux/clk.h>
 
 #include <asm/div64.h>
 
 #include <asm/mach-ath79/ar933x_uart.h>
-#include <asm/mach-ath79/ar933x_uart_platform.h>
 
 #define DRIVER_NAME "ar933x-uart"
 
@@ -47,8 +49,14 @@ struct ar933x_uart_port {
        unsigned int            ier;    /* shadow Interrupt Enable Register */
        unsigned int            min_baud;
        unsigned int            max_baud;
+       struct clk              *clk;
 };
 
+static inline bool ar933x_uart_console_enabled(void)
+{
+       return config_enabled(CONFIG_SERIAL_AR933X_CONSOLE);
+}
+
 static inline unsigned int ar933x_uart_read(struct ar933x_uart_port *up,
                                            int offset)
 {
@@ -322,7 +330,9 @@ static void ar933x_uart_rx_chars(struct ar933x_uart_port *up)
                        tty_insert_flip_char(port, ch, TTY_NORMAL);
        } while (max_count-- > 0);
 
+       spin_unlock(&up->port.lock);
        tty_flip_buffer_push(port);
+       spin_lock(&up->port.lock);
 }
 
 static void ar933x_uart_tx_chars(struct ar933x_uart_port *up)
@@ -497,8 +507,6 @@ static struct uart_ops ar933x_uart_ops = {
        .verify_port    = ar933x_uart_verify_port,
 };
 
-#ifdef CONFIG_SERIAL_AR933X_CONSOLE
-
 static struct ar933x_uart_port *
 ar933x_console_ports[CONFIG_SERIAL_AR933X_NR_UARTS];
 
@@ -597,80 +605,88 @@ static struct console ar933x_uart_console = {
 
 static void ar933x_uart_add_console_port(struct ar933x_uart_port *up)
 {
+       if (!ar933x_uart_console_enabled())
+               return;
+
        ar933x_console_ports[up->port.line] = up;
 }
 
-#define AR933X_SERIAL_CONSOLE  (&ar933x_uart_console)
-
-#else
-
-static inline void ar933x_uart_add_console_port(struct ar933x_uart_port *up) {}
-
-#define AR933X_SERIAL_CONSOLE  NULL
-
-#endif /* CONFIG_SERIAL_AR933X_CONSOLE */
-
 static struct uart_driver ar933x_uart_driver = {
        .owner          = THIS_MODULE,
        .driver_name    = DRIVER_NAME,
        .dev_name       = "ttyATH",
        .nr             = CONFIG_SERIAL_AR933X_NR_UARTS,
-       .cons           = AR933X_SERIAL_CONSOLE,
+       .cons           = NULL, /* filled in runtime */
 };
 
 static int ar933x_uart_probe(struct platform_device *pdev)
 {
-       struct ar933x_uart_platform_data *pdata;
        struct ar933x_uart_port *up;
        struct uart_port *port;
        struct resource *mem_res;
        struct resource *irq_res;
+       struct device_node *np;
        unsigned int baud;
        int id;
        int ret;
 
-       pdata = pdev->dev.platform_data;
-       if (!pdata)
-               return -EINVAL;
-
-       id = pdev->id;
-       if (id == -1)
-               id = 0;
+       np = pdev->dev.of_node;
+       if (config_enabled(CONFIG_OF) && np) {
+               id = of_alias_get_id(np, "serial");
+               if (id < 0) {
+                       dev_err(&pdev->dev, "unable to get alias id, err=%d\n",
+                               id);
+                       return id;
+               }
+       } else {
+               id = pdev->id;
+               if (id == -1)
+                       id = 0;
+       }
 
        if (id > CONFIG_SERIAL_AR933X_NR_UARTS)
                return -EINVAL;
 
-       mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!mem_res) {
-               dev_err(&pdev->dev, "no MEM resource\n");
-               return -EINVAL;
-       }
-
        irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
        if (!irq_res) {
                dev_err(&pdev->dev, "no IRQ resource\n");
                return -EINVAL;
        }
 
-       up = kzalloc(sizeof(struct ar933x_uart_port), GFP_KERNEL);
+       up = devm_kzalloc(&pdev->dev, sizeof(struct ar933x_uart_port),
+                         GFP_KERNEL);
        if (!up)
                return -ENOMEM;
 
+       up->clk = devm_clk_get(&pdev->dev, "uart");
+       if (IS_ERR(up->clk)) {
+               dev_err(&pdev->dev, "unable to get UART clock\n");
+               return PTR_ERR(up->clk);
+       }
+
        port = &up->port;
-       port->mapbase = mem_res->start;
 
-       port->membase = ioremap(mem_res->start, AR933X_UART_REGS_SIZE);
-       if (!port->membase) {
-               ret = -ENOMEM;
-               goto err_free_up;
+       mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       port->membase = devm_ioremap_resource(&pdev->dev, mem_res);
+       if (IS_ERR(port->membase))
+               return PTR_ERR(port->membase);
+
+       ret = clk_prepare_enable(up->clk);
+       if (ret)
+               return ret;
+
+       port->uartclk = clk_get_rate(up->clk);
+       if (!port->uartclk) {
+               ret = -EINVAL;
+               goto err_disable_clk;
        }
 
+       port->mapbase = mem_res->start;
        port->line = id;
        port->irq = irq_res->start;
        port->dev = &pdev->dev;
        port->type = PORT_AR933X;
        port->iotype = UPIO_MEM32;
-       port->uartclk = pdata->uartclk;
 
        port->regshift = 2;
        port->fifosize = AR933X_UART_FIFO_SIZE;
@@ -686,15 +702,13 @@ static int ar933x_uart_probe(struct platform_device *pdev)
 
        ret = uart_add_one_port(&ar933x_uart_driver, &up->port);
        if (ret)
-               goto err_unmap;
+               goto err_disable_clk;
 
        platform_set_drvdata(pdev, up);
        return 0;
 
-err_unmap:
-       iounmap(up->port.membase);
-err_free_up:
-       kfree(up);
+err_disable_clk:
+       clk_disable_unprepare(up->clk);
        return ret;
 }
 
@@ -703,23 +717,30 @@ static int ar933x_uart_remove(struct platform_device *pdev)
        struct ar933x_uart_port *up;
 
        up = platform_get_drvdata(pdev);
-       platform_set_drvdata(pdev, NULL);
 
        if (up) {
                uart_remove_one_port(&ar933x_uart_driver, &up->port);
-               iounmap(up->port.membase);
-               kfree(up);
+               clk_disable_unprepare(up->clk);
        }
 
        return 0;
 }
 
+#ifdef CONFIG_OF
+static const struct of_device_id ar933x_uart_of_ids[] = {
+       { .compatible = "qca,ar9330-uart" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, ar933x_uart_of_ids);
+#endif
+
 static struct platform_driver ar933x_uart_platform_driver = {
        .probe          = ar933x_uart_probe,
        .remove         = ar933x_uart_remove,
        .driver         = {
                .name           = DRIVER_NAME,
                .owner          = THIS_MODULE,
+               .of_match_table = of_match_ptr(ar933x_uart_of_ids),
        },
 };
 
@@ -727,7 +748,9 @@ static int __init ar933x_uart_init(void)
 {
        int ret;
 
-       ar933x_uart_driver.nr = CONFIG_SERIAL_AR933X_NR_UARTS;
+       if (ar933x_uart_console_enabled())
+               ar933x_uart_driver.cons = &ar933x_uart_console;
+
        ret = uart_register_driver(&ar933x_uart_driver);
        if (ret)
                goto err_out;
This page took 0.029523 seconds and 5 git commands to generate.