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.