msm: sdio: Add a new sdio tty csvt device.

This tty device exposes the SDIO_CSVT channel to user space.
It serves as a data channel for video telephony.

Change-Id: Idc3dd892a0fcb450084a2af9da8e95c2d842c5ab
Signed-off-by: Venkat Gopalakrishnan <venkatg@codeaurora.org>
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 7e29383..05f4ab4 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -103,7 +103,7 @@
 endif
 endif
 endif
-obj-$(CONFIG_MSM_SDIO_TTY) += sdio_tty.o sdio_tty_ciq.o
+obj-$(CONFIG_MSM_SDIO_TTY) += sdio_tty.o
 obj-$(CONFIG_MSM_SMD_TTY) += smd_tty.o
 obj-$(CONFIG_MSM_SMD_QMI) += smd_qmi.o
 obj-$(CONFIG_MSM_SMD_PKT) += smd_pkt.o
diff --git a/arch/arm/mach-msm/include/mach/sdio_tty.h b/arch/arm/mach-msm/include/mach/sdio_tty.h
deleted file mode 100644
index 86746b3..0000000
--- a/arch/arm/mach-msm/include/mach/sdio_tty.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-/*
- * SDIO TTY interface.
- */
-
-#ifndef __SDIO_TTY__
-#define __SDIO_TTY__
-
-/**
- * sdio_tty_init_tty - Initialize the SDIO TTY driver.
- *
- * @tty_name: tty name - identify the tty device.
- * @sdio_ch_name: channel name - identify the channel.
- * @return sdio_tty handle on success, NULL on error.
- *
- */
-void *sdio_tty_init_tty(char *tty_name, char* sdio_ch_name);
-
-/**
- * sdio_tty_uninit_tty - Uninitialize the SDIO TTY driver.
- *
- * @sdio_tty_handle: sdio_tty handle.
- * @return 0 on success, negative value on error.
- */
-int sdio_tty_uninit_tty(void *sdio_tty_handle);
-
-/**
- * sdio_tty_enable_debug_msg - Enable/Disable sdio_tty debug
- * messages.
- *
- * @enable: A flag to indicate if to enable or disable the debug
- *        messages.
- * @return 0 on success, negative value on error.
- */
-void sdio_tty_enable_debug_msg(void *sdio_tty_handle, int enable);
-
-#endif /* __SDIO_TTY__ */
diff --git a/arch/arm/mach-msm/sdio_al_test.c b/arch/arm/mach-msm/sdio_al_test.c
index 8b9abcf..a71aae3 100644
--- a/arch/arm/mach-msm/sdio_al_test.c
+++ b/arch/arm/mach-msm/sdio_al_test.c
@@ -298,6 +298,7 @@
 	uint32_t smem_counter;
 
 	struct platform_device *ciq_app_pdev;
+	struct platform_device *csvt_app_pdev;
 
 	wait_queue_head_t   wait_q;
 	int test_completed;
@@ -6162,7 +6163,7 @@
 	int ret;
 
 	pr_debug(TEST_MODULE_NAME ":%s.\n", __func__);
-	pr_info(TEST_MODULE_NAME ": init test cahnnel %s.\n", name);
+	pr_info(TEST_MODULE_NAME ": init test channel %s.\n", name);
 
 	ch_id = channel_name_to_id(name);
 	pr_debug(TEST_MODULE_NAME ":id = %d.\n", ch_id);
@@ -6324,6 +6325,35 @@
 	return sdio_test_channel_remove(pdev);
 }
 
+static int sdio_test_channel_csvt_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+
+	if (!pdev)
+		return -ENODEV;
+
+	test_ctx->csvt_app_pdev = platform_device_alloc("SDIO_CSVT_TEST_APP",
+							-1);
+	ret = platform_device_add(test_ctx->csvt_app_pdev);
+		if (ret) {
+			pr_err(MODULE_NAME ":platform_device_add failed, "
+					   "ret=%d\n", ret);
+			return ret;
+		}
+
+	return sdio_test_channel_probe(pdev);
+}
+
+static int sdio_test_channel_csvt_remove(struct platform_device *pdev)
+{
+	if (!pdev)
+		return -ENODEV;
+
+	platform_device_unregister(test_ctx->csvt_app_pdev);
+
+	return sdio_test_channel_remove(pdev);
+}
+
 static struct platform_driver sdio_rpc_drv = {
 	.probe		= sdio_test_channel_probe,
 	.remove		= sdio_test_channel_remove,
@@ -6388,8 +6418,8 @@
 };
 
 static struct platform_driver sdio_csvt_drv = {
-	.probe		= sdio_test_channel_probe,
-	.remove		= sdio_test_channel_remove,
+	.probe		= sdio_test_channel_csvt_probe,
+	.remove		= sdio_test_channel_csvt_remove,
 	.driver		= {
 		.name	= "SDIO_CSVT_TEST",
 		.owner	= THIS_MODULE,
diff --git a/arch/arm/mach-msm/sdio_tty.c b/arch/arm/mach-msm/sdio_tty.c
index fca88d8..e249d06 100644
--- a/arch/arm/mach-msm/sdio_tty.c
+++ b/arch/arm/mach-msm/sdio_tty.c
@@ -17,6 +17,7 @@
 #include <linux/platform_device.h>
 #include <linux/tty.h>
 #include <linux/tty_flip.h>
+#include <linux/module.h>
 #include <mach/sdio_al.h>
 
 #define INPUT_SPEED			4800
@@ -24,6 +25,19 @@
 #define SDIO_TTY_MODULE_NAME		"sdio_tty"
 #define SDIO_TTY_MAX_PACKET_SIZE	4096
 #define MAX_SDIO_TTY_DRV		1
+#define MAX_SDIO_TTY_DEVS		2
+#define MAX_SDIO_TTY_DEV_NAME_SIZE	25
+
+/* Configurations per channel device */
+/* CIQ */
+#define SDIO_TTY_CIQ_DEV		"sdio_tty_ciq_0"
+#define SDIO_TTY_CIQ_TEST_DEV		"sdio_tty_ciq_test_0"
+#define SDIO_TTY_CH_CIQ			"SDIO_CIQ"
+
+/* CSVT */
+#define SDIO_TTY_CSVT_DEV		"sdio_tty_csvt_0"
+#define SDIO_TTY_CSVT_TEST_DEV		"sdio_tty_csvt_test_0"
+#define SDIO_TTY_CH_CSVT		"SDIO_CSVT"
 
 enum sdio_tty_state {
 	TTY_INITIAL = 0,
@@ -32,9 +46,27 @@
 	TTY_CLOSED = 3,
 };
 
+enum sdio_tty_devices {
+	SDIO_CIQ,
+	SDIO_CIQ_TEST_APP,
+	SDIO_CSVT,
+	SDIO_CSVT_TEST_APP,
+};
+
+static const struct platform_device_id sdio_tty_id_table[] = {
+	{ "SDIO_CIQ",		SDIO_CIQ },
+	{ "SDIO_CIQ_TEST_APP",	SDIO_CIQ_TEST_APP },
+	{ "SDIO_CSVT",		SDIO_CSVT },
+	{ "SDIO_CSVT_TEST_APP",	SDIO_CSVT_TEST_APP },
+	{ },
+};
+MODULE_DEVICE_TABLE(platform, sdio_tty_id_table);
+
 struct sdio_tty {
 	struct sdio_channel *ch;
 	char *sdio_ch_name;
+	char tty_dev_name[MAX_SDIO_TTY_DEV_NAME_SIZE];
+	int device_id;
 	struct workqueue_struct *workq;
 	struct work_struct work_read;
 	wait_queue_head_t   waitq;
@@ -44,11 +76,21 @@
 	char *read_buf;
 	enum sdio_tty_state sdio_tty_state;
 	int is_sdio_open;
+	int tty_open_count;
 };
 
-static struct sdio_tty *sdio_tty;
+static struct sdio_tty *sdio_tty[MAX_SDIO_TTY_DEVS];
 
-#define DEBUG_MSG(sdio_tty, x...) if (sdio_tty->debug_msg_on) pr_info(x)
+#define DEBUG_MSG(sdio_tty_drv, x...) if (sdio_tty_drv->debug_msg_on) pr_info(x)
+
+/*
+ * Enable sdio_tty debug messages
+ * By default the sdio_tty debug messages are turned off
+ */
+static int ciq_debug_msg_on;
+module_param(ciq_debug_msg_on, int, 0);
+static int csvt_debug_msg_on;
+module_param(csvt_debug_msg_on, int, 0);
 
 static void sdio_tty_read(struct work_struct *work)
 {
@@ -62,30 +104,30 @@
 	sdio_tty_drv = container_of(work, struct sdio_tty, work_read);
 
 	if (!sdio_tty_drv) {
-		pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty",
-		       __func__);
+		pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty", __func__);
 		return ;
 	}
 
 	if (sdio_tty_drv->sdio_tty_state != TTY_OPENED) {
 		pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_tty_state = %d",
-		       __func__, sdio_tty_drv->sdio_tty_state);
+			__func__, sdio_tty_drv->sdio_tty_state);
 		return;
 	}
 
 	if (!sdio_tty_drv->read_buf) {
-		pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL read_buf", __func__);
+		pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL read_buf for dev %s",
+			__func__, sdio_tty_drv->tty_dev_name);
 		return;
 	}
 
-	/* Read the data from teh SDIO channel as long as there is available
+	/* Read the data from the SDIO channel as long as there is available
 	   data */
 	while (1) {
 		if (test_bit(TTY_THROTTLED, &sdio_tty_drv->tty_str->flags)) {
-			DEBUG_MSG(sdio_tty_drv,
-				  SDIO_TTY_MODULE_NAME ": %s: TTY_THROTTLED bit"
-						       " is set, exit",
-				  __func__);
+			DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME
+					": %s: TTY_THROTTLED bit is set for "
+					"dev %s, exit", __func__,
+					sdio_tty_drv->tty_dev_name);
 			return;
 		}
 
@@ -93,20 +135,22 @@
 		read_avail = sdio_read_avail(sdio_tty_drv->ch);
 
 		DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME
-					     ": %s: read_avail is %d", __func__,
-					     read_avail);
+				": %s: read_avail is %d for dev %s", __func__,
+				read_avail, sdio_tty_drv->tty_dev_name);
 
 		if (read_avail == 0) {
-			DEBUG_MSG(sdio_tty_drv,
-				  SDIO_TTY_MODULE_NAME ": %s: read_avail is 0",
-				  __func__);
+			DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME
+					": %s: read_avail is 0 for dev %s",
+					__func__, sdio_tty_drv->tty_dev_name);
 			return;
 		}
 
 		if (read_avail > SDIO_TTY_MAX_PACKET_SIZE) {
 			pr_err(SDIO_TTY_MODULE_NAME ": %s: read_avail(%d) is "
-				"bigger than SDIO_TTY_MAX_PACKET_SIZE(%d)",
-			       __func__, read_avail, SDIO_TTY_MAX_PACKET_SIZE);
+				"bigger than SDIO_TTY_MAX_PACKET_SIZE(%d) "
+				"for dev %s", __func__, read_avail,
+				SDIO_TTY_MAX_PACKET_SIZE,
+				sdio_tty_drv->tty_dev_name);
 			return;
 		}
 
@@ -114,8 +158,9 @@
 				sdio_tty_drv->read_buf,
 				read_avail);
 		if (ret < 0) {
-			pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_read error(%d)",
-			       __func__, ret);
+			pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_read error(%d) "
+				"for dev %s", __func__, ret,
+				sdio_tty_drv->tty_dev_name);
 			return;
 		}
 
@@ -132,15 +177,17 @@
 
 		if (total_push != read_avail) {
 			pr_err(SDIO_TTY_MODULE_NAME ": %s: failed, total_push"
-						    "(%d) != read_avail(%d)\n",
-			       __func__, total_push, read_avail);
+				"(%d) != read_avail(%d) for dev %s\n",
+				__func__, total_push, read_avail,
+				sdio_tty_drv->tty_dev_name);
 		}
 
 		tty_flip_buffer_push(sdio_tty_drv->tty_str);
 
-		DEBUG_MSG(sdio_tty_drv,
-			  SDIO_TTY_MODULE_NAME ": %s: End of read %d bytes",
-				__func__, read_avail);
+		DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME
+				": %s: End of read %d bytes for dev %s",
+				__func__, read_avail,
+				sdio_tty_drv->tty_dev_name);
 	}
 }
 
@@ -159,27 +206,26 @@
 	struct sdio_tty *sdio_tty_drv = NULL;
 
 	if (!tty) {
-		pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL tty",
-		       __func__);
+		pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL tty", __func__);
 		return -ENODEV;
 	}
 	sdio_tty_drv = tty->driver_data;
 	if (!sdio_tty_drv) {
 		pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty_drv",
-		       __func__);
+			__func__);
 		return -ENODEV;
 	}
 
 	if (sdio_tty_drv->sdio_tty_state != TTY_OPENED) {
 		pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_tty_state = %d",
-		       __func__, sdio_tty_drv->sdio_tty_state);
+			__func__, sdio_tty_drv->sdio_tty_state);
 		return -EPERM;
 	}
 
 	write_avail = sdio_write_avail(sdio_tty_drv->ch);
-	DEBUG_MSG(sdio_tty_drv,
-		  SDIO_TTY_MODULE_NAME ": %s: write_avail=%d",
-		 __func__, write_avail);
+	DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: write_avail=%d "
+			"for dev %s", __func__, write_avail,
+			sdio_tty_drv->tty_dev_name);
 
 	return write_avail;
 }
@@ -205,60 +251,60 @@
 	struct sdio_tty *sdio_tty_drv = NULL;
 
 	if (!tty) {
-		pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL tty",
-		       __func__);
+		pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL tty", __func__);
 		return -ENODEV;
 	}
 	sdio_tty_drv = tty->driver_data;
 	if (!sdio_tty_drv) {
 		pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty_drv",
-		       __func__);
+			__func__);
 		return -ENODEV;
 	}
 
 	if (sdio_tty_drv->sdio_tty_state != TTY_OPENED) {
 		pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_tty_state = %d",
-		       __func__, sdio_tty_drv->sdio_tty_state);
+			__func__, sdio_tty_drv->sdio_tty_state);
 		return -EPERM;
 	}
 
-	DEBUG_MSG(sdio_tty_drv,
-		  SDIO_TTY_MODULE_NAME ": %s: WRITING CALLBACK CALLED WITH "
-				       "%d bytes\n",
-		 __func__, count);
+	DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: Write Callback "
+			"called with %d bytes for dev %s\n", __func__, count,
+			sdio_tty_drv->tty_dev_name);
 	write_avail = sdio_write_avail(sdio_tty_drv->ch);
 	if (write_avail == 0) {
-		DEBUG_MSG(sdio_tty_drv,
-			  SDIO_TTY_MODULE_NAME ": %s: write_avail is 0\n",
-			 __func__);
+		DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: "
+				"write_avail is 0 for dev %s\n",
+				__func__, sdio_tty_drv->tty_dev_name);
 		return 0;
 	}
 	if (write_avail > SDIO_TTY_MAX_PACKET_SIZE) {
-		DEBUG_MSG(sdio_tty_drv,
-			  SDIO_TTY_MODULE_NAME ": %s: write_avail(%d) is "
-			  "bigger than max packet size,(%d), setting to "
-			  "max_packet_size\n",
-			  __func__, write_avail, SDIO_TTY_MAX_PACKET_SIZE);
+		DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: "
+				"write_avail(%d) is bigger than max packet "
+				"size(%d) for dev %s, setting to "
+				"max_packet_size\n", __func__, write_avail,
+				SDIO_TTY_MAX_PACKET_SIZE,
+				sdio_tty_drv->tty_dev_name);
 		write_avail = SDIO_TTY_MAX_PACKET_SIZE;
 	}
 	if (write_avail < count) {
-		DEBUG_MSG(sdio_tty_drv,
-			  SDIO_TTY_MODULE_NAME ": %s: write_avail(%d) is "
-					       "smaller than "
-				    "required(%d), writing only %d bytes\n",
-			 __func__, write_avail, count, write_avail);
+		DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: "
+				"write_avail(%d) is smaller than required(%d) "
+				"for dev %s, writing only %d bytes\n",
+				__func__, write_avail, count,
+				sdio_tty_drv->tty_dev_name, write_avail);
 		len = write_avail;
 	}
 	ret = sdio_write(sdio_tty_drv->ch, buf, len);
 	if (ret) {
-		pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_write failed, ret=%d\n",
-			 __func__, ret);
+		pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_write failed for "
+			"dev %s, ret=%d\n", __func__,
+			sdio_tty_drv->tty_dev_name, ret);
 		return 0;
 	}
 
-	DEBUG_MSG(sdio_tty_drv,
-		  SDIO_TTY_MODULE_NAME ": %s: End of function, len=%d bytes\n",
-		 __func__, len);
+	DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: End of function, "
+			"dev=%s, len=%d bytes\n", __func__,
+			sdio_tty_drv->tty_dev_name, len);
 
 	return len;
 }
@@ -269,18 +315,18 @@
 
 	if (!sdio_tty_drv) {
 		pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty_drv",
-		       __func__);
+			__func__);
 	}
 
 	if (sdio_tty_drv->sdio_tty_state != TTY_OPENED) {
 		pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_tty_state = %d",
-		       __func__, sdio_tty_drv->sdio_tty_state);
+			__func__, sdio_tty_drv->sdio_tty_state);
 		return;
 	}
 
-	DEBUG_MSG(sdio_tty_drv,
-		  SDIO_TTY_MODULE_NAME ": %s: event %d received\n", __func__,
-		  event);
+	DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: event %d "
+			"received for dev %s\n", __func__, event,
+			sdio_tty_drv->tty_dev_name);
 
 	if (event == SDIO_EVENT_DATA_READ_AVAIL)
 		queue_work(sdio_tty_drv->workq, &sdio_tty_drv->work_read);
@@ -298,20 +344,37 @@
 static int sdio_tty_open(struct tty_struct *tty, struct file *file)
 {
 	int ret = 0;
+	int i = 0;
 	struct sdio_tty *sdio_tty_drv = NULL;
 
 	if (!tty) {
-		pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL tty",
-		       __func__);
+		pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL tty", __func__);
 		return -ENODEV;
 	}
-	sdio_tty_drv = sdio_tty;
+
+	for (i = 0; i < MAX_SDIO_TTY_DEVS; i++) {
+		if (sdio_tty[i] == NULL)
+			continue;
+		if (!strncmp(sdio_tty[i]->tty_dev_name, tty->name,
+				MAX_SDIO_TTY_DEV_NAME_SIZE)) {
+			sdio_tty_drv = sdio_tty[i];
+			break;
+		}
+	}
+
 	if (!sdio_tty_drv) {
 		pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty_drv",
 		       __func__);
 		return -ENODEV;
 	}
 
+	sdio_tty_drv->tty_open_count++;
+	if (sdio_tty_drv->sdio_tty_state == TTY_OPENED) {
+		pr_err(SDIO_TTY_MODULE_NAME ": %s: tty dev(%s) is already open",
+			__func__, sdio_tty_drv->tty_dev_name);
+		return -EBUSY;
+	}
+
 	tty->driver_data = sdio_tty_drv;
 
 	sdio_tty_drv->tty_str = tty;
@@ -321,36 +384,31 @@
 
 	sdio_tty_drv->read_buf = kzalloc(SDIO_TTY_MAX_PACKET_SIZE, GFP_KERNEL);
 	if (sdio_tty_drv->read_buf == NULL) {
-		pr_err(SDIO_TTY_MODULE_NAME ": %s: failed to allocate read_buf",
-		       __func__);
+		pr_err(SDIO_TTY_MODULE_NAME ": %s: failed to allocate read_buf "
+			"for dev %s", __func__, sdio_tty_drv->tty_dev_name);
 		return -ENOMEM;
 	}
 
 	sdio_tty_drv->workq = create_singlethread_workqueue("sdio_tty_read");
 	if (!sdio_tty_drv->workq) {
-		pr_err(SDIO_TTY_MODULE_NAME ": %s: failed to create workq",
-		       __func__);
+		pr_err(SDIO_TTY_MODULE_NAME ": %s: failed to create workq "
+			"for dev %s", __func__, sdio_tty_drv->tty_dev_name);
 		return -ENOMEM;
 	}
 
-	if (sdio_tty_drv->sdio_tty_state == TTY_OPENED) {
-		pr_err(SDIO_TTY_MODULE_NAME ": %s: tty is already open",
-		       __func__);
-		return -EBUSY;
-	}
-
 	if (!sdio_tty_drv->is_sdio_open) {
 		ret = sdio_open(sdio_tty_drv->sdio_ch_name, &sdio_tty_drv->ch,
 				sdio_tty_drv, sdio_tty_notify);
 		if (ret < 0) {
-			pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_open err=%d\n",
-			       __func__, ret);
+			pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_open err=%d "
+				"for dev %s\n", __func__, ret,
+				sdio_tty_drv->tty_dev_name);
 			destroy_workqueue(sdio_tty_drv->workq);
 			return ret;
 		}
 
-		pr_info(SDIO_TTY_MODULE_NAME ": %s: SDIO_TTY channel opened\n",
-			__func__);
+		pr_info(SDIO_TTY_MODULE_NAME ": %s: SDIO_TTY channel(%s) opened "
+			"\n", __func__, sdio_tty_drv->sdio_ch_name);
 
 		sdio_tty_drv->is_sdio_open = 1;
 	} else {
@@ -363,8 +421,8 @@
 
 	sdio_tty_drv->sdio_tty_state = TTY_OPENED;
 
-	pr_info(SDIO_TTY_MODULE_NAME ": %s: TTY device opened\n",
-		__func__);
+	pr_info(SDIO_TTY_MODULE_NAME ": %s: TTY device(%s) opened\n",
+		__func__, sdio_tty_drv->tty_dev_name);
 
 	return ret;
 }
@@ -385,8 +443,7 @@
 	struct sdio_tty *sdio_tty_drv = NULL;
 
 	if (!tty) {
-		pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL tty",
-		       __func__);
+		pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL tty", __func__);
 		return;
 	}
 	sdio_tty_drv = tty->driver_data;
@@ -397,10 +454,11 @@
 	}
 	if (sdio_tty_drv->sdio_tty_state != TTY_OPENED) {
 		pr_err(SDIO_TTY_MODULE_NAME ": %s: trying to close a "
-					    "TTY device that was not opened\n",
-		       __func__);
+			"TTY device that was not opened\n", __func__);
 		return;
 	}
+	if (--sdio_tty_drv->tty_open_count != 0)
+		return;
 
 	flush_workqueue(sdio_tty_drv->workq);
 	destroy_workqueue(sdio_tty_drv->workq);
@@ -410,8 +468,8 @@
 
 	sdio_tty_drv->sdio_tty_state = TTY_CLOSED;
 
-	pr_info(SDIO_TTY_MODULE_NAME ": %s: SDIO_TTY channel closed\n",
-		__func__);
+	pr_info(SDIO_TTY_MODULE_NAME ": %s: SDIO_TTY device(%s) closed\n",
+		__func__, sdio_tty_drv->tty_dev_name);
 }
 
 static void sdio_tty_unthrottle(struct tty_struct *tty)
@@ -419,8 +477,7 @@
 	struct sdio_tty *sdio_tty_drv = NULL;
 
 	if (!tty) {
-		pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL tty",
-		       __func__);
+		pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL tty", __func__);
 		return;
 	}
 	sdio_tty_drv = tty->driver_data;
@@ -448,33 +505,53 @@
 	.unthrottle = sdio_tty_unthrottle,
 };
 
-void *sdio_tty_init_tty(char *tty_name, char *sdio_ch_name)
+int sdio_tty_init_tty(char *tty_name, char *sdio_ch_name,
+			enum sdio_tty_devices device_id, int debug_msg_on)
 {
 	int ret = 0;
-	struct device *tty_dev;
-	struct sdio_tty *sdio_tty_drv;
-
-	pr_info(SDIO_TTY_MODULE_NAME ": %s\n", __func__);
+	int i = 0;
+	struct device *tty_dev = NULL;
+	struct sdio_tty *sdio_tty_drv = NULL;
 
 	sdio_tty_drv = kzalloc(sizeof(struct sdio_tty), GFP_KERNEL);
 	if (sdio_tty_drv == NULL) {
-		pr_err(SDIO_TTY_MODULE_NAME "%s: failed to allocate sdio_tty",
-		       __func__);
-		return NULL;
+		pr_err(SDIO_TTY_MODULE_NAME ": %s: failed to allocate sdio_tty "
+			"for dev %s", __func__, tty_name);
+		return -ENOMEM;
 	}
 
-	sdio_tty = sdio_tty_drv;
+	for (i = 0; i < MAX_SDIO_TTY_DEVS; i++) {
+		if (sdio_tty[i] == NULL) {
+			sdio_tty[i] = sdio_tty_drv;
+			break;
+		}
+	}
+
+	if (i == MAX_SDIO_TTY_DEVS) {
+		pr_err(SDIO_TTY_MODULE_NAME ": %s: tty dev(%s) creation failed,"
+			" max limit(%d) reached.", __func__, tty_name,
+			MAX_SDIO_TTY_DEVS);
+		kfree(sdio_tty_drv);
+		return -ENODEV;
+	}
+
+	snprintf(sdio_tty_drv->tty_dev_name, MAX_SDIO_TTY_DEV_NAME_SIZE,
+			"%s%d", tty_name, 0);
 	sdio_tty_drv->sdio_ch_name = sdio_ch_name;
+	sdio_tty_drv->device_id = device_id;
+	pr_info(SDIO_TTY_MODULE_NAME ": %s: dev=%s, id=%d, channel=%s\n",
+		__func__, sdio_tty_drv->tty_dev_name, sdio_tty_drv->device_id,
+		sdio_tty_drv->sdio_ch_name);
 
 	INIT_WORK(&sdio_tty_drv->work_read, sdio_tty_read);
 
 	sdio_tty_drv->tty_drv = alloc_tty_driver(MAX_SDIO_TTY_DRV);
 
 	if (!sdio_tty_drv->tty_drv) {
-		pr_err(SDIO_TTY_MODULE_NAME ": %s - tty_drv is NULL",
-				   __func__);
+		pr_err(SDIO_TTY_MODULE_NAME ": %s - tty_drv is NULL for dev %s",
+			__func__, sdio_tty_drv->tty_dev_name);
 		kfree(sdio_tty_drv);
-		return NULL;
+		return -ENODEV;
 	}
 
 	sdio_tty_drv->tty_drv->name = tty_name;
@@ -500,31 +577,38 @@
 	if (ret) {
 		put_tty_driver(sdio_tty_drv->tty_drv);
 		pr_err(SDIO_TTY_MODULE_NAME ": %s: tty_register_driver() "
-					    "failed\n", __func__);
+			"failed for dev %s\n", __func__,
+			sdio_tty_drv->tty_dev_name);
 
 		sdio_tty_drv->tty_drv = NULL;
 		kfree(sdio_tty_drv);
-		return NULL;
+		return -ENODEV;
 	}
 
 	tty_dev = tty_register_device(sdio_tty_drv->tty_drv, 0, NULL);
 	if (IS_ERR(tty_dev)) {
 		pr_err(SDIO_TTY_MODULE_NAME ": %s: tty_register_device() "
-			"failed\n", __func__);
+			"failed for dev %s\n", __func__,
+			sdio_tty_drv->tty_dev_name);
 		tty_unregister_driver(sdio_tty_drv->tty_drv);
 		put_tty_driver(sdio_tty_drv->tty_drv);
 		kfree(sdio_tty_drv);
-		return NULL;
+		return -ENODEV;
 	}
 
 	sdio_tty_drv->sdio_tty_state = TTY_REGISTERED;
-	return sdio_tty_drv;
+	if (debug_msg_on) {
+		pr_info(SDIO_TTY_MODULE_NAME ": %s: turn on debug msg for %s",
+			__func__, sdio_tty_drv->tty_dev_name);
+		sdio_tty_drv->debug_msg_on = debug_msg_on;
+	}
+	return 0;
 }
-EXPORT_SYMBOL(sdio_tty_init_tty);
 
 int sdio_tty_uninit_tty(void *sdio_tty_handle)
 {
 	int ret = 0;
+	int i = 0;
 	struct sdio_tty *sdio_tty_drv = sdio_tty_handle;
 
 	if (!sdio_tty_drv) {
@@ -546,51 +630,151 @@
 		ret = tty_unregister_driver(sdio_tty_drv->tty_drv);
 		if (ret) {
 			pr_err(SDIO_TTY_MODULE_NAME ": %s: "
-			    "tty_unregister_driver() failed\n", __func__);
+				"tty_unregister_driver() failed for dev %s\n",
+				__func__, sdio_tty_drv->tty_dev_name);
 		}
 		put_tty_driver(sdio_tty_drv->tty_drv);
 		sdio_tty_drv->sdio_tty_state = TTY_INITIAL;
 		sdio_tty_drv->tty_drv = NULL;
 	}
 
-	pr_info(SDIO_TTY_MODULE_NAME ": %s: Freeing sdio_tty structure",
-		__func__);
+	for (i = 0; i < MAX_SDIO_TTY_DEVS; i++) {
+		if (sdio_tty[i] == NULL)
+			continue;
+		if (sdio_tty[i]->device_id == sdio_tty_drv->device_id) {
+			sdio_tty[i] = NULL;
+			break;
+		}
+	}
+
+	DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: Freeing sdio_tty "
+			"structure, dev=%s", __func__,
+			sdio_tty_drv->tty_dev_name);
 	kfree(sdio_tty_drv);
 
 	return 0;
 }
-EXPORT_SYMBOL(sdio_tty_uninit_tty);
 
-
-void sdio_tty_enable_debug_msg(void *sdio_tty_handle, int enable)
+static int sdio_tty_probe(struct platform_device *pdev)
 {
-	struct sdio_tty *sdio_tty_drv = sdio_tty_handle;
+	const struct platform_device_id *id = platform_get_device_id(pdev);
+	enum sdio_tty_devices device_id = id->driver_data;
+	char *device_name = NULL;
+	char *channel_name = NULL;
+	int debug_msg_on = 0;
+	int ret = 0;
+
+	pr_debug(SDIO_TTY_MODULE_NAME ": %s for %s", __func__, pdev->name);
+
+	switch (device_id) {
+	case SDIO_CIQ:
+		device_name = SDIO_TTY_CIQ_DEV;
+		channel_name = SDIO_TTY_CH_CIQ;
+		debug_msg_on = ciq_debug_msg_on;
+		break;
+	case SDIO_CIQ_TEST_APP:
+		device_name = SDIO_TTY_CIQ_TEST_DEV;
+		channel_name = SDIO_TTY_CH_CIQ;
+		debug_msg_on = ciq_debug_msg_on;
+		break;
+	case SDIO_CSVT:
+		device_name = SDIO_TTY_CSVT_DEV;
+		channel_name = SDIO_TTY_CH_CSVT;
+		debug_msg_on = csvt_debug_msg_on;
+		break;
+	case SDIO_CSVT_TEST_APP:
+		device_name = SDIO_TTY_CSVT_TEST_DEV;
+		channel_name = SDIO_TTY_CH_CSVT;
+		debug_msg_on = csvt_debug_msg_on;
+		break;
+	default:
+		pr_err(SDIO_TTY_MODULE_NAME ": %s Invalid device:%s, id:%d",
+			__func__, pdev->name, device_id);
+		ret = -ENODEV;
+		break;
+	}
+
+	if (device_name) {
+		ret = sdio_tty_init_tty(device_name, channel_name,
+					device_id, debug_msg_on);
+		if (ret) {
+			pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_tty_init_tty "
+				"failed for dev:%s", __func__, device_name);
+		}
+	}
+	return ret;
+}
+
+static int sdio_tty_remove(struct platform_device *pdev)
+{
+	const struct platform_device_id *id = platform_get_device_id(pdev);
+	enum sdio_tty_devices device_id = id->driver_data;
+	struct sdio_tty *sdio_tty_drv = NULL;
+	int i = 0;
+	int ret = 0;
+
+	pr_debug(SDIO_TTY_MODULE_NAME ": %s for %s", __func__, pdev->name);
+
+	for (i = 0; i < MAX_SDIO_TTY_DEVS; i++) {
+		if (sdio_tty[i] == NULL)
+			continue;
+		if (sdio_tty[i]->device_id == device_id) {
+			sdio_tty_drv = sdio_tty[i];
+			break;
+		}
+	}
 
 	if (!sdio_tty_drv) {
 		pr_err(SDIO_TTY_MODULE_NAME ": %s: NULL sdio_tty_drv",
 		       __func__);
-		return;
+		return -ENODEV;
 	}
-	pr_info(SDIO_TTY_MODULE_NAME ": %s: setting debug_msg_on to %d",
-		       __func__, enable);
-	sdio_tty_drv->debug_msg_on = enable;
+
+	ret = sdio_tty_uninit_tty(sdio_tty_drv);
+	if (ret) {
+		pr_err(SDIO_TTY_MODULE_NAME ": %s: sdio_tty_uninit_tty "
+			"failed for %s", __func__, pdev->name);
+	}
+	return ret;
 }
-EXPORT_SYMBOL(sdio_tty_enable_debug_msg);
 
+static struct platform_driver sdio_tty_pdrv = {
+	.probe		= sdio_tty_probe,
+	.remove		= sdio_tty_remove,
+	.id_table	= sdio_tty_id_table,
+	.driver		= {
+		.name	= "SDIO_TTY",
+		.owner	= THIS_MODULE,
+	},
+};
 
+/*
+ *  Module Init.
+ *
+ *  Register SDIO TTY driver.
+ *
+ */
 static int __init sdio_tty_init(void)
 {
-	return 0;
+	int ret = 0;
+
+	ret = platform_driver_register(&sdio_tty_pdrv);
+	if (ret) {
+		pr_err(SDIO_TTY_MODULE_NAME ": %s: platform_driver_register "
+					    "failed", __func__);
+	}
+	return ret;
 };
 
 /*
  *  Module Exit.
  *
- *  Unregister SDIO driver.
+ *  Unregister SDIO TTY driver.
  *
  */
 static void __exit sdio_tty_exit(void)
 {
+	platform_driver_unregister(&sdio_tty_pdrv);
 }
 
 module_init(sdio_tty_init);
diff --git a/arch/arm/mach-msm/sdio_tty_ciq.c b/arch/arm/mach-msm/sdio_tty_ciq.c
deleted file mode 100644
index 64b772d..0000000
--- a/arch/arm/mach-msm/sdio_tty_ciq.c
+++ /dev/null
@@ -1,134 +0,0 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#include <linux/slab.h>
-#include <linux/platform_device.h>
-#include <linux/debugfs.h>
-#include <mach/sdio_al.h>
-#include <mach/sdio_tty.h>
-
-#define SDIO_TTY_DEV		"sdio_tty_ciq_0"
-#define SDIO_CIQ		"sdio_ciq"
-#define SDIO_TTY_DEV_TEST	"sdio_tty_ciq_test_0"
-#define TTY_CIQ_MODULE_NAME	"sdio_tty_ciq"
-
-struct sdio_tty_ciq {
-	void *sdio_tty_handle;
-};
-
-static struct sdio_tty_ciq sdio_tty_ciq;
-
-/*
- * Enable sdio_tty debug messages
- * By default the sdio_tty debug messages are turned off
- */
-static int debug_msg_on;
-module_param(debug_msg_on, int, 0);
-
-static int sdio_tty_probe(struct platform_device *pdev)
-{
-	sdio_tty_ciq.sdio_tty_handle = sdio_tty_init_tty(SDIO_TTY_DEV,
-							 "SDIO_CIQ");
-	if (!sdio_tty_ciq.sdio_tty_handle) {
-		pr_err(TTY_CIQ_MODULE_NAME ": %s: NULL sdio_tty_handle",
-		       __func__);
-		return -ENODEV;
-	}
-
-	if (debug_msg_on)
-		sdio_tty_enable_debug_msg(sdio_tty_ciq.sdio_tty_handle,
-					  debug_msg_on);
-
-	return 0;
-}
-
-static int sdio_tty_test_probe(struct platform_device *pdev)
-{
-	sdio_tty_ciq.sdio_tty_handle = sdio_tty_init_tty(SDIO_TTY_DEV_TEST,
-							 "SDIO_CIQ");
-	if (!sdio_tty_ciq.sdio_tty_handle) {
-		pr_err(TTY_CIQ_MODULE_NAME ": %s: NULL sdio_tty_handle",
-		       __func__);
-		return -ENODEV;
-	}
-
-	if (debug_msg_on)
-		sdio_tty_enable_debug_msg(sdio_tty_ciq.sdio_tty_handle,
-					  debug_msg_on);
-
-	return 0;
-}
-
-static int sdio_tty_remove(struct platform_device *pdev)
-{
-	int ret = 0;
-
-	pr_info(TTY_CIQ_MODULE_NAME ": %s", __func__);
-	ret = sdio_tty_uninit_tty(sdio_tty_ciq.sdio_tty_handle);
-	if (ret) {
-		pr_err(TTY_CIQ_MODULE_NAME ": %s: sdio_tty_uninit_tty failed",
-		       __func__);
-		return ret;
-	}
-
-	return 0;
-}
-
-static struct platform_driver sdio_tty_pdrv = {
-	.probe		= sdio_tty_probe,
-	.remove		= sdio_tty_remove,
-	.driver		= {
-		.name	= "SDIO_CIQ",
-		.owner	= THIS_MODULE,
-	},
-};
-
-static struct platform_driver sdio_tty_test_pdrv = {
-	.probe		= sdio_tty_test_probe,
-	.remove		= sdio_tty_remove,
-	.driver		= {
-		.name	= "SDIO_CIQ_TEST_APP",
-		.owner	= THIS_MODULE,
-	},
-};
-
-static int __init sdio_tty_ciq_init(void)
-{
-	int ret = 0;
-
-	ret = platform_driver_register(&sdio_tty_pdrv);
-	if (ret) {
-		pr_err(TTY_CIQ_MODULE_NAME ": %s: platform_driver_register "
-					    "failed", __func__);
-		return ret;
-	}
-	return platform_driver_register(&sdio_tty_test_pdrv);
-};
-
-/*
- *  Module Exit.
- *
- *  Unregister SDIO driver.
- *
- */
-static void __exit sdio_tty_ciq_exit(void)
-{
-	platform_driver_unregister(&sdio_tty_pdrv);
-	platform_driver_unregister(&sdio_tty_test_pdrv);
-}
-
-module_init(sdio_tty_ciq_init);
-module_exit(sdio_tty_ciq_exit);
-
-MODULE_DESCRIPTION("SDIO TTY CIQ");
-MODULE_LICENSE("GPL v2");
-MODULE_AUTHOR("Maya Erez <merez@codeaurora.org>");