msm_serial_hs: Add support for device tree for BLSP based UART
This change adds support for device tree for BLSP based UART
Core.
Also document pdata and their usage with device tree in :
Documentation/devicetree/bindings/tty/serial/msm_serial_hs.txt
Change-Id: I72c6bdcf1b22320ef4674b17fab226ae0cac72ac
Signed-off-by: Mayank Rana <mrana@codeaurora.org>
Signed-off-by: Saket Saurabh <ssaurabh@codeaurora.org>
diff --git a/Documentation/devicetree/bindings/tty/serial/msm_serial_hs.txt b/Documentation/devicetree/bindings/tty/serial/msm_serial_hs.txt
new file mode 100644
index 0000000..3534823
--- /dev/null
+++ b/Documentation/devicetree/bindings/tty/serial/msm_serial_hs.txt
@@ -0,0 +1,70 @@
+* Qualcomm MSM HSUART
+
+Required properties:
+- compatible :
+ - "qcom,msm-hsuart-v14" to be used for UARTDM Core v1.4
+- reg : offset and length of the register set for both the device,
+ uart core and bam core
+- reg-names : names of the uart core and bam core.
+- interrupts : should contain the uart interrupt.
+- interrupt-names : names of interrupts to be used.
+- bam-tx-ep-pipe-index : BAM TX Endpoint Pipe Index for HSUART
+- bam-rx-ep-pipe-index : BAM RX Endpoint Pipe Index for HSUART
+
+BLSP has a static pipe allocation and assumes a pair-pipe for each uart core.
+Pipes [2*i : 2*i+1] are allocated for UART cores where i = [0 : 5].
+Hence, Minimum and Maximum permitted value of endpoint pipe index to be used
+with uart core is 0 and 11 respectively.
+
+There is one HSUART block used in MSM devices,
+"qcom,msm-hsuart-v14". The msm-serial-hs driver is
+able to handle this, and matches against the "qcom,msm-hsuart-v14"
+as the compatibility.
+
+The registers for the "qcom,msm-hsuart-v14" device need to specify both
+register blocks - uart core and bam core.
+
+Example:
+
+ uart7@f995d000 {
+ compatible = "qcom,msm-hsuart-v14";
+ reg = <0xf995d000 0x1000>,
+ <0xf9944000 0x5000>;
+ reg-names = "core_mem", "bam_mem";
+ interrupts = <0 113 0>, <0 239 0>;
+ interrupt-names = "core_irq", "bam_irq";
+ };
+
+Optional properties:
+- qcom,<gpio-name>-gpio : handle to the GPIO node, see "gpios property" in
+Documentation/devicetree/bindings/gpio/gpio.txt.
+"gpio-name" can be "tx", "rx", "cts" and "rfr" based on number of UART GPIOs
+need to configured.
+Gpio's are optional if it is required to be not configured by UART driver or
+case where there is nothing connected and we want to use internal loopback mode
+for uart.
+- qcom, wakeup_irq : UART RX GPIO IRQ line to be configured as wakeup source.
+- qcom,inject_rx_on_wakeup : inject_rx_on_wakeup enables feature where on
+receiving interrupt with UART RX GPIO IRQ line (i.e. above wakeup_irq property),
+HSUART driver injects provided character with property rx_to_inject.
+- qcom, rx_to_inject : The character to be inserted on wakeup.
+
+
+Example:
+
+ uart7: uart@f995d000 {
+ compatible = "qcom,msm-hsuart-v14"
+ reg = <0x19c40000 0x1000">,
+ <0xf9944000 0x5000>;
+ reg-names = "core_mem", "bam_mem";
+ interrupts = <0 113 0>, <0 239 0>;
+ interrupt-names = "core_irq", "bam_irq";
+
+ qcom,tx-gpio = <&msmgpio 41 0x00>;
+ qcom,rx-gpio = <&msmgpio 42 0x00>;
+ qcom,cts-gpio = <&msmgpio 43 0x00>;
+ qcom,rfr-gpio = <&msmgpio 44 0x00>;
+
+ qcom,bam-tx-ep-pipe-index = <0>;
+ qcom,bam-rx-ep-pipe-index = <1>;
+ };
diff --git a/arch/arm/mach-msm/include/mach/msm_serial_hs.h b/arch/arm/mach-msm/include/mach/msm_serial_hs.h
index d2905d4..cc50955 100644
--- a/arch/arm/mach-msm/include/mach/msm_serial_hs.h
+++ b/arch/arm/mach-msm/include/mach/msm_serial_hs.h
@@ -17,15 +17,34 @@
#include<linux/serial_core.h>
-/* Optional platform device data for msm_serial_hs driver.
- * Used to configure low power wakeup */
+/**
+ * struct msm_serial_hs_platform_data - platform device data
+ * for msm hsuart device
+ * @wakeup_irq : IRQ line to be configured as Wakeup source.
+ * @inject_rx_on_wakeup : Set 1 if specific character to be inserted on wakeup
+ * @rx_to_inject : Character to be inserted on wakeup
+ * @gpio_config : Configure gpios that are used for uart communication
+ * @userid : User-defined number to be used to enumerate device as tty<userid>
+ * @uart_tx_gpio: GPIO number for UART Tx Line.
+ * @uart_rx_gpio: GPIO number for UART Rx Line.
+ * @uart_cts_gpio: GPIO number for UART CTS Line.
+ * @uart_rfr_gpio: GPIO number for UART RFR Line.
+ * @bam_tx_ep_pipe_index : BAM TX Endpoint Pipe Index for HSUART
+ * @bam_tx_ep_pipe_index : BAM RX Endpoint Pipe Index for HSUART
+ */
struct msm_serial_hs_platform_data {
int wakeup_irq; /* wakeup irq */
- /* bool: inject char into rx tty on wakeup */
unsigned char inject_rx_on_wakeup;
char rx_to_inject;
int (*gpio_config)(int);
int userid;
+
+ unsigned uart_tx_gpio;
+ unsigned uart_rx_gpio;
+ unsigned uart_cts_gpio;
+ unsigned uart_rfr_gpio;
+ unsigned bam_tx_ep_pipe_index;
+ unsigned bam_rx_ep_pipe_index;
};
unsigned int msm_hs_tx_empty(struct uart_port *uport);
diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c
index 2d73af6..6fe0531 100644
--- a/drivers/tty/serial/msm_serial_hs.c
+++ b/drivers/tty/serial/msm_serial_hs.c
@@ -54,6 +54,9 @@
#include <linux/device.h>
#include <linux/wakelock.h>
#include <linux/debugfs.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
#include <asm/atomic.h>
#include <asm/irq.h>
@@ -66,6 +69,16 @@
static int hs_serial_debug_mask = 1;
module_param_named(debug_mask, hs_serial_debug_mask,
int, S_IRUGO | S_IWUSR | S_IWGRP);
+/*
+ * There are 3 different kind of UART Core available on MSM.
+ * High Speed UART (i.e. Legacy HSUART), GSBI based HSUART
+ * and BSLP based HSUART.
+ */
+enum uart_core_type {
+ LEGACY_HSUART,
+ GSBI_HSUART,
+ BLSP_HSUART,
+};
enum flush_reason {
FLUSH_NONE,
@@ -169,6 +182,13 @@
struct workqueue_struct *hsuart_wq; /* hsuart workqueue */
struct mutex clk_mutex; /* mutex to guard against clock off/clock on */
bool tty_flush_receive;
+ enum uart_core_type uart_type;
+ u32 bam_handle;
+ resource_size_t bam_mem;
+ int bam_irq;
+ unsigned char __iomem *bam_base;
+ unsigned int bam_tx_ep_pipe_index;
+ unsigned int bam_rx_ep_pipe_index;
};
#define MSM_UARTDM_BURST_SIZE 16 /* DM burst size (in bytes) */
@@ -176,6 +196,8 @@
#define UARTDM_RX_BUF_SIZE 512
#define RETRY_TIMEOUT 5
#define UARTDM_NR 256
+#define BAM_PIPE_MIN 0
+#define BAM_PIPE_MAX 11
static struct dentry *debug_base;
static struct msm_hs_port q_uart_port[UARTDM_NR];
@@ -244,7 +266,10 @@
/* assume gsbi uart if gsbi resource found in pdata */
return ((msm_uport->mapped_gsbi != NULL));
}
-
+static unsigned int is_blsp_uart(struct msm_hs_port *msm_uport)
+{
+ return (msm_uport->uart_type == BLSP_HSUART);
+}
static inline unsigned int msm_hs_read(struct uart_port *uport,
unsigned int offset)
{
@@ -1804,24 +1829,6 @@
struct msm_hs_tx *tx = &msm_uport->tx;
struct msm_hs_rx *rx = &msm_uport->rx;
- /* Allocate the command pointer. Needs to be 64 bit aligned */
- tx->command_ptr = kmalloc(sizeof(dmov_box), GFP_KERNEL | __GFP_DMA);
- if (!tx->command_ptr)
- return -ENOMEM;
-
- tx->command_ptr_ptr = kmalloc(sizeof(u32), GFP_KERNEL | __GFP_DMA);
- if (!tx->command_ptr_ptr) {
- ret = -ENOMEM;
- goto free_tx_command_ptr;
- }
-
- tx->mapped_cmd_ptr = dma_map_single(uport->dev, tx->command_ptr,
- sizeof(dmov_box), DMA_TO_DEVICE);
- tx->mapped_cmd_ptr_ptr = dma_map_single(uport->dev,
- tx->command_ptr_ptr,
- sizeof(u32), DMA_TO_DEVICE);
- tx->xfer.cmdptr = DMOV_CMD_ADDR(tx->mapped_cmd_ptr_ptr);
-
init_waitqueue_head(&rx->wait);
init_waitqueue_head(&tx->wait);
wake_lock_init(&rx->wake_lock, WAKE_LOCK_SUSPEND, "msm_serial_hs_rx");
@@ -1848,12 +1855,40 @@
goto free_pool;
}
+ /* Set up Uart Receive */
+ msm_hs_write(uport, UARTDM_RFWR_ADDR, 0);
+
+ INIT_DELAYED_WORK(&rx->flip_insert_work, flip_insert_work);
+
+ if (is_blsp_uart(msm_uport))
+ return ret;
+
+ /* Allocate the command pointer. Needs to be 64 bit aligned */
+ tx->command_ptr = kmalloc(sizeof(dmov_box), GFP_KERNEL | __GFP_DMA);
+ if (!tx->command_ptr) {
+ return -ENOMEM;
+ goto free_rx_buffer;
+ }
+
+ tx->command_ptr_ptr = kmalloc(sizeof(u32), GFP_KERNEL | __GFP_DMA);
+ if (!tx->command_ptr_ptr) {
+ ret = -ENOMEM;
+ goto free_tx_command_ptr;
+ }
+
+ tx->mapped_cmd_ptr = dma_map_single(uport->dev, tx->command_ptr,
+ sizeof(dmov_box), DMA_TO_DEVICE);
+ tx->mapped_cmd_ptr_ptr = dma_map_single(uport->dev,
+ tx->command_ptr_ptr,
+ sizeof(u32), DMA_TO_DEVICE);
+ tx->xfer.cmdptr = DMOV_CMD_ADDR(tx->mapped_cmd_ptr_ptr);
+
/* Allocate the command pointer. Needs to be 64 bit aligned */
rx->command_ptr = kmalloc(sizeof(dmov_box), GFP_KERNEL | __GFP_DMA);
if (!rx->command_ptr) {
pr_err("%s(): cannot allocate rx->command_ptr", __func__);
ret = -ENOMEM;
- goto free_rx_buffer;
+ goto free_tx_command_ptr_ptr;
}
rx->command_ptr_ptr = kmalloc(sizeof(u32), GFP_KERNEL | __GFP_DMA);
@@ -1868,9 +1903,6 @@
rx->command_ptr->dst_row_addr = rx->rbuffer;
- /* Set up Uart Receive */
- msm_hs_write(uport, UARTDM_RFWR_ADDR, 0);
-
rx->xfer.complete_func = msm_hs_dmov_rx_callback;
rx->command_ptr->cmd = CMD_LC |
@@ -1890,13 +1922,21 @@
sizeof(u32), DMA_TO_DEVICE);
rx->xfer.cmdptr = DMOV_CMD_ADDR(rx->cmdptr_dmaaddr);
- INIT_DELAYED_WORK(&rx->flip_insert_work, flip_insert_work);
-
return ret;
free_rx_command_ptr:
kfree(rx->command_ptr);
+free_tx_command_ptr_ptr:
+ kfree(msm_uport->tx.command_ptr_ptr);
+ dma_unmap_single(uport->dev, msm_uport->tx.mapped_cmd_ptr_ptr,
+ sizeof(u32), DMA_TO_DEVICE);
+ dma_unmap_single(uport->dev, msm_uport->tx.mapped_cmd_ptr,
+ sizeof(dmov_box), DMA_TO_DEVICE);
+
+free_tx_command_ptr:
+ kfree(msm_uport->tx.command_ptr);
+
free_rx_buffer:
dma_pool_free(msm_uport->rx.pool, msm_uport->rx.buffer,
msm_uport->rx.rbuffer);
@@ -1909,47 +1949,196 @@
wake_lock_destroy(&msm_uport->dma_wake_lock);
tasklet_kill(&msm_uport->tx.tlet);
tasklet_kill(&msm_uport->rx.tlet);
- dma_unmap_single(uport->dev, msm_uport->tx.mapped_cmd_ptr_ptr,
- sizeof(u32), DMA_TO_DEVICE);
- dma_unmap_single(uport->dev, msm_uport->tx.mapped_cmd_ptr,
- sizeof(dmov_box), DMA_TO_DEVICE);
- kfree(msm_uport->tx.command_ptr_ptr);
-
-free_tx_command_ptr:
- kfree(msm_uport->tx.command_ptr);
return ret;
}
+struct msm_serial_hs_platform_data
+ *msm_hs_dt_to_pdata(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct msm_serial_hs_platform_data *pdata;
+ int rx_to_inject, ret;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ pr_err("unable to allocate memory for platform data\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* UART TX GPIO */
+ pdata->uart_tx_gpio = of_get_named_gpio(node,
+ "qcom,tx-gpio", 0);
+ if (pdata->uart_tx_gpio < 0)
+ pr_debug("uart_tx_gpio is not available\n");
+
+ /* UART RX GPIO */
+ pdata->uart_rx_gpio = of_get_named_gpio(node,
+ "qcom,rx-gpio", 0);
+ if (pdata->uart_rx_gpio < 0)
+ pr_debug("uart_rx_gpio is not available\n");
+
+ /* UART CTS GPIO */
+ pdata->uart_cts_gpio = of_get_named_gpio(node,
+ "qcom,cts-gpio", 0);
+ if (pdata->uart_cts_gpio < 0)
+ pr_debug("uart_cts_gpio is not available\n");
+
+ /* UART RFR GPIO */
+ pdata->uart_rfr_gpio = of_get_named_gpio(node,
+ "qcom,rfr-gpio", 0);
+ if (pdata->uart_rfr_gpio < 0)
+ pr_debug("uart_rfr_gpio is not available\n");
+
+ pdata->inject_rx_on_wakeup = of_property_read_bool(node,
+ "qcom,inject-rx-on-wakeup");
+
+ if (pdata->inject_rx_on_wakeup) {
+ ret = of_property_read_u32(node, "qcom,rx-char-to-inject",
+ &rx_to_inject);
+ if (ret < 0) {
+ pr_err("Error: Rx_char_to_inject not specified.\n");
+ return ERR_PTR(ret);
+ }
+ pdata->rx_to_inject = (char)rx_to_inject;
+ }
+
+ ret = of_property_read_u32(node, "qcom,bam-tx-ep-pipe-index",
+ &pdata->bam_tx_ep_pipe_index);
+ if (ret < 0) {
+ pr_err("Error: Getting UART BAM TX EP Pipe Index.\n");
+ return ERR_PTR(ret);
+ }
+
+ if (!(pdata->bam_tx_ep_pipe_index >= BAM_PIPE_MIN &&
+ pdata->bam_tx_ep_pipe_index <= BAM_PIPE_MAX)) {
+ pr_err("Error: Invalid UART BAM TX EP Pipe Index.\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ ret = of_property_read_u32(node, "qcom,bam-rx-ep-pipe-index",
+ &pdata->bam_rx_ep_pipe_index);
+ if (ret < 0) {
+ pr_err("Error: Getting UART BAM RX EP Pipe Index.\n");
+ return ERR_PTR(ret);
+ }
+
+ if (!(pdata->bam_rx_ep_pipe_index >= BAM_PIPE_MIN &&
+ pdata->bam_rx_ep_pipe_index <= BAM_PIPE_MAX)) {
+ pr_err("Error: Invalid UART BAM RX EP Pipe Index.\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ pr_debug("tx_ep_pipe_index:%d rx_ep_pipe_index:%d\n"
+ "tx_gpio:%d rx_gpio:%d rfr_gpio:%d cts_gpio:%d",
+ pdata->bam_tx_ep_pipe_index, pdata->bam_rx_ep_pipe_index,
+ pdata->uart_tx_gpio, pdata->uart_rx_gpio, pdata->uart_cts_gpio,
+ pdata->uart_rfr_gpio);
+
+ return pdata;
+}
+
static int __devinit msm_hs_probe(struct platform_device *pdev)
{
int ret;
struct uart_port *uport;
struct msm_hs_port *msm_uport;
+ struct resource *core_resource;
+ struct resource *bam_resource;
struct resource *resource;
+ int core_irqres, bam_irqres;
struct msm_serial_hs_platform_data *pdata = pdev->dev.platform_data;
+ struct device_node *node = pdev->dev.of_node;
+
+ if (pdev->dev.of_node) {
+ dev_dbg(&pdev->dev, "device tree enabled\n");
+ pdata = msm_hs_dt_to_pdata(pdev);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+
+ of_property_read_u32(node, "cell-index",
+ &pdev->id);
+
+ pdev->dev.platform_data = pdata;
+ }
if (pdev->id < 0 || pdev->id >= UARTDM_NR) {
- printk(KERN_ERR "Invalid plaform device ID = %d\n", pdev->id);
+ pr_err("Invalid plaform device ID = %d\n", pdev->id);
return -EINVAL;
}
msm_uport = &q_uart_port[pdev->id];
uport = &msm_uport->uport;
-
uport->dev = &pdev->dev;
- resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (unlikely(!resource))
- return -ENXIO;
- uport->mapbase = resource->start; /* virtual address */
+ if (pdev->dev.of_node)
+ msm_uport->uart_type = BLSP_HSUART;
- uport->membase = ioremap(uport->mapbase, PAGE_SIZE);
- if (unlikely(!uport->membase))
- return -ENOMEM;
+ /* Get required resources for BAM HSUART */
+ if (is_blsp_uart(msm_uport)) {
+ core_resource = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "core_mem");
+ bam_resource = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "bam_mem");
+ core_irqres = platform_get_irq_byname(pdev, "core_irq");
+ bam_irqres = platform_get_irq_byname(pdev, "bam_irq");
- uport->irq = platform_get_irq(pdev, 0);
- if (unlikely((int)uport->irq < 0))
- return -ENXIO;
+ if (!core_resource) {
+ pr_err("Invalid core HSUART Resources.\n");
+ return -ENXIO;
+ }
+
+ if (!bam_resource) {
+ pr_err("Invalid BAM HSUART Resources.\n");
+ return -ENXIO;
+ }
+
+ if (!core_irqres) {
+ pr_err("Invalid core irqres Resources.\n");
+ return -ENXIO;
+ }
+ if (!bam_irqres) {
+ pr_err("Invalid bam irqres Resources.\n");
+ return -ENXIO;
+ }
+
+ uport->mapbase = core_resource->start;
+
+ uport->membase = ioremap(uport->mapbase,
+ resource_size(core_resource));
+ if (unlikely(!uport->membase)) {
+ pr_err("UART Resource ioremap Failed.\n");
+ return -ENOMEM;
+ }
+ msm_uport->bam_mem = bam_resource->start;
+ msm_uport->bam_base = ioremap(msm_uport->bam_mem,
+ resource_size(bam_resource));
+ if (unlikely(!msm_uport->bam_base)) {
+ pr_err("UART BAM Resource ioremap Failed.\n");
+ iounmap(uport->membase);
+ return -ENOMEM;
+ }
+
+ uport->irq = core_irqres;
+ msm_uport->bam_irq = bam_irqres;
+
+ } else {
+
+ resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (unlikely(!resource))
+ return -ENXIO;
+ uport->mapbase = resource->start;
+ uport->membase = ioremap(uport->mapbase,
+ resource_size(resource));
+ if (unlikely(!uport->membase))
+ return -ENOMEM;
+
+ uport->irq = platform_get_irq(pdev, 0);
+ if (unlikely((int)uport->irq < 0)) {
+ pr_err("UART IRQ Failed.\n");
+ iounmap(uport->membase);
+ return -ENXIO;
+ }
+ }
if (pdata == NULL)
msm_uport->wakeup.irq = -1;
@@ -1959,24 +2148,41 @@
msm_uport->wakeup.inject_rx = pdata->inject_rx_on_wakeup;
msm_uport->wakeup.rx_to_inject = pdata->rx_to_inject;
- if (unlikely(msm_uport->wakeup.irq < 0))
- return -ENXIO;
+ if (unlikely(msm_uport->wakeup.irq < 0)) {
+ ret = -ENXIO;
+ goto unmap_memory;
+ }
+ if (is_blsp_uart(msm_uport)) {
+ msm_uport->bam_tx_ep_pipe_index =
+ pdata->bam_tx_ep_pipe_index;
+ msm_uport->bam_rx_ep_pipe_index =
+ pdata->bam_rx_ep_pipe_index;
+ }
}
- resource = platform_get_resource_byname(pdev, IORESOURCE_DMA,
- "uartdm_channels");
- if (unlikely(!resource))
- return -ENXIO;
- msm_uport->dma_tx_channel = resource->start;
- msm_uport->dma_rx_channel = resource->end;
+ if (!is_blsp_uart(msm_uport)) {
- resource = platform_get_resource_byname(pdev, IORESOURCE_DMA,
- "uartdm_crci");
- if (unlikely(!resource))
- return -ENXIO;
- msm_uport->dma_tx_crci = resource->start;
- msm_uport->dma_rx_crci = resource->end;
+ resource = platform_get_resource_byname(pdev,
+ IORESOURCE_DMA, "uartdm_channels");
+ if (unlikely(!resource)) {
+ ret = -ENXIO;
+ goto unmap_memory;
+ }
+
+ msm_uport->dma_tx_channel = resource->start;
+ msm_uport->dma_rx_channel = resource->end;
+
+ resource = platform_get_resource_byname(pdev,
+ IORESOURCE_DMA, "uartdm_crci");
+ if (unlikely(!resource)) {
+ ret = -ENXIO;
+ goto unmap_memory;
+ }
+
+ msm_uport->dma_tx_crci = resource->start;
+ msm_uport->dma_rx_crci = resource->end;
+ }
uport->iotype = UPIO_MEM;
uport->fifosize = 64;
@@ -1986,8 +2192,10 @@
msm_uport->imr_reg = 0x0;
msm_uport->clk = clk_get(&pdev->dev, "core_clk");
- if (IS_ERR(msm_uport->clk))
- return PTR_ERR(msm_uport->clk);
+ if (IS_ERR(msm_uport->clk)) {
+ ret = PTR_ERR(msm_uport->clk);
+ goto unmap_memory;
+ }
msm_uport->pclk = clk_get(&pdev->dev, "iface_clk");
/*
@@ -2000,7 +2208,7 @@
ret = clk_set_rate(msm_uport->clk, uport->uartclk);
if (ret) {
printk(KERN_WARNING "Error setting clock rate on UART\n");
- return ret;
+ goto unmap_memory;
}
msm_uport->hsuart_wq = alloc_workqueue("k_hsuart",
@@ -2008,7 +2216,8 @@
if (!msm_uport->hsuart_wq) {
pr_err("%s(): Unable to create workqueue hsuart_wq\n",
__func__);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto unmap_memory;
}
INIT_WORK(&msm_uport->clock_off_w, hsuart_clock_off_work);
@@ -2023,7 +2232,7 @@
clk_disable_unprepare(msm_uport->clk);
if (msm_uport->pclk)
clk_disable_unprepare(msm_uport->pclk);
- return ret;
+ goto unmap_memory;
}
/* configure the CR Protection to Enable */
@@ -2048,14 +2257,23 @@
ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_clock.attr);
if (unlikely(ret))
- return ret;
+ goto unmap_memory;
msm_serial_debugfs_init(msm_uport, pdev->id);
uport->line = pdev->id;
if (pdata != NULL && pdata->userid && pdata->userid <= UARTDM_NR)
uport->line = pdata->userid;
- return uart_add_one_port(&msm_hs_driver, uport);
+ ret = uart_add_one_port(&msm_hs_driver, uport);
+ if (!ret)
+ return ret;
+
+unmap_memory:
+ iounmap(uport->membase);
+ if (is_blsp_uart(msm_uport))
+ iounmap(msm_uport->bam_base);
+
+ return ret;
}
static int __init msm_serial_hs_init(void)
@@ -2211,12 +2429,18 @@
.runtime_idle = msm_hs_runtime_idle,
};
+static struct of_device_id msm_hs_match_table[] = {
+ { .compatible = "qcom,msm-hsuart-v14" },
+ {}
+};
+
static struct platform_driver msm_serial_hs_platform_driver = {
.probe = msm_hs_probe,
.remove = __devexit_p(msm_hs_remove),
.driver = {
.name = "msm_serial_hs",
.pm = &msm_hs_dev_pm_ops,
+ .of_match_table = msm_hs_match_table,
},
};