Merge tag 'soc-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
[platform/kernel/linux-stable.git] / drivers / tty / serial / xilinx_uartps.c
index 6c91745..7e4150a 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/platform_device.h>
 #include <linux/serial.h>
 #include <linux/serial_core.h>
+#include <linux/slab.h>
 #include <linux/tty.h>
 #include <linux/tty_flip.h>
 #include <linux/console.h>
 #define XUARTPS_SR_RXTRIG      0x00000001 /* Rx Trigger */
 
 /**
+ * struct xuartps - device data
+ * @refclk     Reference clock
+ * @aperclk    APB clock
+ */
+struct xuartps {
+       struct clk              *refclk;
+       struct clk              *aperclk;
+};
+
+/**
  * xuartps_isr - Interrupt handler
  * @irq: Irq number
  * @dev_id: Id of the port
@@ -936,34 +947,55 @@ static int xuartps_probe(struct platform_device *pdev)
        int rc;
        struct uart_port *port;
        struct resource *res, *res2;
-       struct clk *clk;
+       struct xuartps *xuartps_data;
 
-       clk = of_clk_get(pdev->dev.of_node, 0);
-       if (IS_ERR(clk)) {
-               dev_err(&pdev->dev, "no clock specified\n");
-               return PTR_ERR(clk);
+       xuartps_data = kzalloc(sizeof(*xuartps_data), GFP_KERNEL);
+       if (!xuartps_data)
+               return -ENOMEM;
+
+       xuartps_data->aperclk = clk_get(&pdev->dev, "aper_clk");
+       if (IS_ERR(xuartps_data->aperclk)) {
+               dev_err(&pdev->dev, "aper_clk clock not found.\n");
+               rc = PTR_ERR(xuartps_data->aperclk);
+               goto err_out_free;
+       }
+       xuartps_data->refclk = clk_get(&pdev->dev, "ref_clk");
+       if (IS_ERR(xuartps_data->refclk)) {
+               dev_err(&pdev->dev, "ref_clk clock not found.\n");
+               rc = PTR_ERR(xuartps_data->refclk);
+               goto err_out_clk_put_aper;
        }
 
-       rc = clk_prepare_enable(clk);
+       rc = clk_prepare_enable(xuartps_data->aperclk);
+       if (rc) {
+               dev_err(&pdev->dev, "Unable to enable APER clock.\n");
+               goto err_out_clk_put;
+       }
+       rc = clk_prepare_enable(xuartps_data->refclk);
        if (rc) {
-               dev_err(&pdev->dev, "could not enable clock\n");
-               return -EBUSY;
+               dev_err(&pdev->dev, "Unable to enable device clock.\n");
+               goto err_out_clk_dis_aper;
        }
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res)
-               return -ENODEV;
+       if (!res) {
+               rc = -ENODEV;
+               goto err_out_clk_disable;
+       }
 
        res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
-       if (!res2)
-               return -ENODEV;
+       if (!res2) {
+               rc = -ENODEV;
+               goto err_out_clk_disable;
+       }
 
        /* Initialize the port structure */
        port = xuartps_get_port();
 
        if (!port) {
                dev_err(&pdev->dev, "Cannot get uart_port structure\n");
-               return -ENODEV;
+               rc = -ENODEV;
+               goto err_out_clk_disable;
        } else {
                /* Register the port.
                 * This function also registers this device with the tty layer
@@ -972,17 +1004,30 @@ static int xuartps_probe(struct platform_device *pdev)
                port->mapbase = res->start;
                port->irq = res2->start;
                port->dev = &pdev->dev;
-               port->uartclk = clk_get_rate(clk);
-               port->private_data = clk;
+               port->uartclk = clk_get_rate(xuartps_data->refclk);
+               port->private_data = xuartps_data;
                platform_set_drvdata(pdev, port);
                rc = uart_add_one_port(&xuartps_uart_driver, port);
                if (rc) {
                        dev_err(&pdev->dev,
                                "uart_add_one_port() failed; err=%i\n", rc);
-                       return rc;
+                       goto err_out_clk_disable;
                }
                return 0;
        }
+
+err_out_clk_disable:
+       clk_disable_unprepare(xuartps_data->refclk);
+err_out_clk_dis_aper:
+       clk_disable_unprepare(xuartps_data->aperclk);
+err_out_clk_put:
+       clk_put(xuartps_data->refclk);
+err_out_clk_put_aper:
+       clk_put(xuartps_data->aperclk);
+err_out_free:
+       kfree(xuartps_data);
+
+       return rc;
 }
 
 /**
@@ -994,13 +1039,17 @@ static int xuartps_probe(struct platform_device *pdev)
 static int xuartps_remove(struct platform_device *pdev)
 {
        struct uart_port *port = platform_get_drvdata(pdev);
-       struct clk *clk = port->private_data;
+       struct xuartps *xuartps_data = port->private_data;
        int rc;
 
        /* Remove the xuartps port from the serial core */
        rc = uart_remove_one_port(&xuartps_uart_driver, port);
        port->mapbase = 0;
-       clk_disable_unprepare(clk);
+       clk_disable_unprepare(xuartps_data->refclk);
+       clk_disable_unprepare(xuartps_data->aperclk);
+       clk_put(xuartps_data->refclk);
+       clk_put(xuartps_data->aperclk);
+       kfree(xuartps_data);
        return rc;
 }