msm: sysmon: Add USB HSIC transport support for external modems

The sysmon driver only supports SMD as a transport for exchanging
notification messages with other subsystems. Add support for an
additional USB HSIC transport that may be used to communicate with
peripherals such as external modems. The hsic_sysmon driver is
leveraged to accomplish this.

Change-Id: Ie606a4dca0abdb70d201cd2a26b919fbfd4473d0
Signed-off-by: Matt Wagantall <mattw@codeaurora.org>
diff --git a/arch/arm/mach-msm/sysmon.c b/arch/arm/mach-msm/sysmon.c
index 3d3824a..ddb8502 100644
--- a/arch/arm/mach-msm/sysmon.c
+++ b/arch/arm/mach-msm/sysmon.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2011-2012, 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
@@ -24,20 +24,34 @@
 #include <mach/msm_smd.h>
 #include <mach/subsystem_notif.h>
 
+#include "hsic_sysmon.h"
 #include "sysmon.h"
 
 #define MAX_MSG_LENGTH	50
 #define TIMEOUT_MS	5000
 
+enum transports {
+	TRANSPORT_SMD,
+	TRANSPORT_HSIC,
+};
+
 struct sysmon_subsys {
 	struct mutex		lock;
 	struct smd_channel	*chan;
 	bool			chan_open;
 	struct completion	resp_ready;
 	char			rx_buf[MAX_MSG_LENGTH];
+	enum transports		transport;
 };
 
-static struct sysmon_subsys subsys[SYSMON_NUM_SS];
+static struct sysmon_subsys subsys[SYSMON_NUM_SS] = {
+	[SYSMON_SS_MODEM].transport     = TRANSPORT_SMD,
+	[SYSMON_SS_LPASS].transport     = TRANSPORT_SMD,
+	[SYSMON_SS_WCNSS].transport     = TRANSPORT_SMD,
+	[SYSMON_SS_DSPS].transport      = TRANSPORT_SMD,
+	[SYSMON_SS_Q6FW].transport      = TRANSPORT_SMD,
+	[SYSMON_SS_EXT_MODEM].transport = TRANSPORT_HSIC,
+};
 
 static const char *notif_name[SUBSYS_NOTIF_TYPE_COUNT] = {
 	[SUBSYS_BEFORE_SHUTDOWN] = "before_shutdown",
@@ -46,6 +60,39 @@
 	[SUBSYS_AFTER_POWERUP]   = "after_powerup",
 };
 
+static int sysmon_send_smd(struct sysmon_subsys *ss, char *tx_buf, size_t len)
+{
+	int ret;
+
+	if (!ss->chan_open)
+		return -ENODEV;
+
+	init_completion(&ss->resp_ready);
+	pr_debug("Sending SMD message: %s\n", tx_buf);
+	smd_write(ss->chan, tx_buf, len);
+	ret = wait_for_completion_timeout(&ss->resp_ready,
+				  msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret)
+		return -ETIMEDOUT;
+
+	return 0;
+}
+
+static int sysmon_send_hsic(struct sysmon_subsys *ss, char *tx_buf, size_t len)
+{
+	int ret;
+	size_t actual_len;
+
+	pr_debug("Sending HSIC message: %s\n", tx_buf);
+	ret = hsic_sysmon_write(HSIC_SYSMON_DEV_EXT_MODEM,
+				tx_buf, len, TIMEOUT_MS);
+	if (ret)
+		return ret;
+	ret = hsic_sysmon_read(HSIC_SYSMON_DEV_EXT_MODEM, ss->rx_buf,
+			       ARRAY_SIZE(ss->rx_buf), &actual_len, TIMEOUT_MS);
+	return ret;
+}
+
 int sysmon_send_event(enum subsys_id dest_ss, const char *event_ss,
 		      enum subsys_notif_type notif)
 {
@@ -58,31 +105,34 @@
 	    event_ss == NULL)
 		return -EINVAL;
 
-	if (!ss->chan_open)
-		return -ENODEV;
-
-	mutex_lock(&ss->lock);
-	init_completion(&ss->resp_ready);
 	snprintf(tx_buf, ARRAY_SIZE(tx_buf), "ssr:%s:%s", event_ss,
 		 notif_name[notif]);
-	pr_debug("Sending message: %s\n", tx_buf);
-	smd_write(ss->chan, tx_buf, ARRAY_SIZE(tx_buf));
-	ret = wait_for_completion_timeout(&ss->resp_ready,
-					  msecs_to_jiffies(TIMEOUT_MS));
-	if (!ret) {
-		ret = -ETIMEDOUT;
-	} else if (strncmp(ss->rx_buf, "ssr:ack", ARRAY_SIZE(ss->rx_buf))) {
-		pr_debug("Received response: %s\n", ss->rx_buf);
-		ret = -ENOSYS;
-	} else {
-		ret = 0;
-	}
-	mutex_unlock(&ss->lock);
 
+	mutex_lock(&ss->lock);
+	switch (ss->transport) {
+	case TRANSPORT_SMD:
+		ret = sysmon_send_smd(ss, tx_buf, ARRAY_SIZE(tx_buf));
+		break;
+	case TRANSPORT_HSIC:
+		ret = sysmon_send_hsic(ss, tx_buf, ARRAY_SIZE(tx_buf));
+		break;
+	default:
+		ret = -EINVAL;
+	}
+	if (ret)
+		goto out;
+
+	pr_debug("Received response: %s\n", ss->rx_buf);
+	if (strncmp(ss->rx_buf, "ssr:ack", ARRAY_SIZE(ss->rx_buf)))
+		ret = -ENOSYS;
+	else
+		ret = 0;
+out:
+	mutex_unlock(&ss->lock);
 	return ret;
 }
 
-static void sysmon_notify(void *priv, unsigned int smd_event)
+static void sysmon_smd_notify(void *priv, unsigned int smd_event)
 {
 	struct sysmon_subsys *ss = priv;
 
@@ -104,44 +154,61 @@
 	}
 }
 
-static const uint32_t ss_map[SMD_NUM_TYPE] = {
-	[SMD_APPS_MODEM]	= SYSMON_SS_MODEM,
-	[SMD_APPS_QDSP]		= SYSMON_SS_LPASS,
-	[SMD_APPS_WCNSS]	= SYSMON_SS_WCNSS,
-	[SMD_APPS_DSPS]		= SYSMON_SS_DSPS,
-	[SMD_APPS_Q6FW]		= SYSMON_SS_Q6FW,
-};
-
 static int sysmon_probe(struct platform_device *pdev)
 {
 	struct sysmon_subsys *ss;
 	int ret;
 
-	if (pdev == NULL)
-		return -EINVAL;
-
-	if (pdev->id < 0 || pdev->id >= SMD_NUM_TYPE ||
-	    ss_map[pdev->id] < 0 || ss_map[pdev->id] >= SYSMON_NUM_SS)
+	if (pdev->id < 0 || pdev->id >= SYSMON_NUM_SS)
 		return -ENODEV;
 
-	ss = &subsys[ss_map[pdev->id]];
+	ss = &subsys[pdev->id];
 	mutex_init(&ss->lock);
 
-	/* Open and configure the SMD channel */
-	ret = smd_named_open_on_edge("sys_mon", pdev->id, &ss->chan,
-				     ss, sysmon_notify);
-	if (ret) {
-		pr_err("SMD open failed\n");
-		return -ENOSYS;
+	switch (ss->transport) {
+	case TRANSPORT_SMD:
+		if (pdev->id >= SMD_NUM_TYPE)
+			return -EINVAL;
+
+		ret = smd_named_open_on_edge("sys_mon", pdev->id, &ss->chan, ss,
+					     sysmon_smd_notify);
+		if (ret) {
+			pr_err("SMD open failed\n");
+			return ret;
+		}
+
+		smd_disable_read_intr(ss->chan);
+		break;
+	case TRANSPORT_HSIC:
+		if (pdev->id < SMD_NUM_TYPE)
+			return -EINVAL;
+
+		ret = hsic_sysmon_open(HSIC_SYSMON_DEV_EXT_MODEM);
+		if (ret) {
+			pr_err("HSIC open failed\n");
+			return ret;
+		}
+		break;
+	default:
+		return -EINVAL;
 	}
-	smd_disable_read_intr(ss->chan);
 
 	return 0;
 }
 
 static int __devexit sysmon_remove(struct platform_device *pdev)
 {
-	smd_close(subsys[ss_map[pdev->id]].chan);
+	struct sysmon_subsys *ss = &subsys[pdev->id];
+
+	switch (ss->transport) {
+	case TRANSPORT_SMD:
+		smd_close(ss->chan);
+		break;
+	case TRANSPORT_HSIC:
+		hsic_sysmon_close(HSIC_SYSMON_DEV_EXT_MODEM);
+		break;
+	}
+
 	return 0;
 }
 
diff --git a/arch/arm/mach-msm/sysmon.h b/arch/arm/mach-msm/sysmon.h
index 496adf9..d014187 100644
--- a/arch/arm/mach-msm/sysmon.h
+++ b/arch/arm/mach-msm/sysmon.h
@@ -15,22 +15,25 @@
 #ifndef __MSM_SYSMON_H
 #define __MSM_SYSMON_H
 
+#include <mach/msm_smd.h>
 #include <mach/subsystem_notif.h>
 
 /**
  * enum subsys_id - Destination subsystems for events.
  */
 enum subsys_id {
-	SYSMON_SS_MODEM,
-	SYSMON_SS_LPASS,
-	SYSMON_SS_WCNSS,
-	SYSMON_SS_DSPS,
-	SYSMON_SS_Q6FW,
-	SYSMON_SS_EXT_MODEM,
+	/* SMD subsystems */
+	SYSMON_SS_MODEM     = SMD_APPS_MODEM,
+	SYSMON_SS_LPASS     = SMD_APPS_QDSP,
+	SYSMON_SS_WCNSS     = SMD_APPS_WCNSS,
+	SYSMON_SS_DSPS      = SMD_APPS_DSPS,
+	SYSMON_SS_Q6FW      = SMD_APPS_Q6FW,
+
+	/* Non-SMD subsystems */
+	SYSMON_SS_EXT_MODEM = SMD_NUM_TYPE,
 	SYSMON_NUM_SS
 };
 
-
 /**
  * sysmon_send_event() - Notify a subsystem of another's state change.
  * @dest_ss:	ID of subsystem the notification should be sent to.