|
@@ -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>
|
|
@@ -138,6 +139,16 @@
|
|
|
#define XUARTPS_SR_TXFULL 0x00000010 /* TX FIFO full */
|
|
|
#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
|
|
@@ -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,18 +1004,31 @@ 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;
|
|
|
dev_set_drvdata(&pdev->dev, 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);
|
|
|
dev_set_drvdata(&pdev->dev, NULL);
|
|
|
- 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;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -995,14 +1040,18 @@ static int xuartps_probe(struct platform_device *pdev)
|
|
|
static int xuartps_remove(struct platform_device *pdev)
|
|
|
{
|
|
|
struct uart_port *port = dev_get_drvdata(&pdev->dev);
|
|
|
- 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);
|
|
|
dev_set_drvdata(&pdev->dev, NULL);
|
|
|
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;
|
|
|
}
|
|
|
|