msm: smd: Add SMSM state queue
To reduce the chance of missing SMSM state transitions,
push the SMSM state into a queue in the interrupt handler
and then pull the state for processing the in callback
workqueue.
Change-Id: Ib66bc15f7445369ea7ad2ca271a79ac10b8105a0
CRs-Fixed: 323725
Signed-off-by: Eric Holmberg <eholmber@codeaurora.org>
diff --git a/arch/arm/mach-msm/smd.c b/arch/arm/mach-msm/smd.c
index 691dad2..b31550a 100644
--- a/arch/arm/mach-msm/smd.c
+++ b/arch/arm/mach-msm/smd.c
@@ -1,7 +1,7 @@
/* arch/arm/mach-msm/smd.c
*
* Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
* Author: Brian Swetland <swetland@google.com>
*
* This software is licensed under the terms of the GNU General Public
@@ -31,6 +31,7 @@
#include <linux/ctype.h>
#include <linux/remote_spinlock.h>
#include <linux/uaccess.h>
+#include <linux/kfifo.h>
#include <mach/msm_smd.h>
#include <mach/msm_iomap.h>
#include <mach/system.h>
@@ -61,6 +62,7 @@
#define MODULE_NAME "msm_smd"
#define SMEM_VERSION 0x000B
#define SMD_VERSION 0x00020000
+#define SMSM_SNAPSHOT_CNT 64
uint32_t SMSM_NUM_ENTRIES = 8;
uint32_t SMSM_NUM_HOSTS = 3;
@@ -79,6 +81,7 @@
};
static struct smsm_shared_info smsm_info;
+struct kfifo smsm_snapshot_fifo;
struct smsm_size_info_type {
uint32_t num_hosts;
@@ -239,6 +242,7 @@
static LIST_HEAD(smd_ch_list_loopback);
static irqreturn_t smsm_irq_handler(int irq, void *data);
static void smd_fake_irq_handler(unsigned long arg);
+static void smsm_cb_snapshot(void);
static void notify_smsm_cb_clients_worker(struct work_struct *work);
static DECLARE_WORK(smsm_cb_work, notify_smsm_cb_clients_worker);
@@ -308,7 +312,7 @@
MSM_TRIG_A2DSPS_SMSM_INT;
}
- schedule_work(&smsm_cb_work);
+ smsm_cb_snapshot();
}
static inline void notify_modem_smd(void)
@@ -1955,6 +1959,14 @@
SMSM_NUM_HOSTS = smsm_size_info->num_hosts;
}
+ i = kfifo_alloc(&smsm_snapshot_fifo,
+ sizeof(uint32_t) * SMSM_NUM_ENTRIES * SMSM_SNAPSHOT_CNT,
+ GFP_KERNEL);
+ if (i) {
+ pr_err("%s: SMSM state fifo alloc failed %d\n", __func__, i);
+ return i;
+ }
+
if (!smsm_info.state) {
smsm_info.state = smem_alloc2(ID_SHARED_STATE,
SMSM_NUM_ENTRIES *
@@ -2024,6 +2036,31 @@
}
EXPORT_SYMBOL(smsm_reset_modem_cont);
+static void smsm_cb_snapshot(void)
+{
+ int n;
+ uint32_t new_state;
+ int ret;
+
+ ret = kfifo_avail(&smsm_snapshot_fifo);
+ if (ret < (SMSM_NUM_ENTRIES * 4)) {
+ pr_err("%s: SMSM snapshot full %d\n", __func__, ret);
+ return;
+ }
+
+ for (n = 0; n < SMSM_NUM_ENTRIES; n++) {
+ new_state = __raw_readl(SMSM_STATE_ADDR(n));
+
+ ret = kfifo_in(&smsm_snapshot_fifo,
+ &new_state, sizeof(new_state));
+ if (ret != sizeof(new_state)) {
+ pr_err("%s: SMSM snapshot failure %d\n", __func__, ret);
+ return;
+ }
+ }
+ schedule_work(&smsm_cb_work);
+}
+
static irqreturn_t smsm_irq_handler(int irq, void *data)
{
unsigned long flags;
@@ -2039,7 +2076,9 @@
prev_smem_q6_apps_smsm = mux_val;
}
- schedule_work(&smsm_cb_work);
+ spin_lock_irqsave(&smem_lock, flags);
+ smsm_cb_snapshot();
+ spin_unlock_irqrestore(&smem_lock, flags);
return IRQ_HANDLED;
}
@@ -2097,7 +2136,7 @@
notify_other_smsm(SMSM_APPS_STATE, (old_apps ^ apps));
}
- schedule_work(&smsm_cb_work);
+ smsm_cb_snapshot();
}
spin_unlock_irqrestore(&smem_lock, flags);
return IRQ_HANDLED;
@@ -2212,35 +2251,41 @@
int n;
uint32_t new_state;
uint32_t state_changes;
+ int ret;
+ int snapshot_size = SMSM_NUM_ENTRIES * sizeof(uint32_t);
- mutex_lock(&smsm_lock);
-
- if (!smsm_states) {
- /* smsm not yet initialized */
- mutex_unlock(&smsm_lock);
+ if (!smd_initialized)
return;
- }
- for (n = 0; n < SMSM_NUM_ENTRIES; n++) {
- state_info = &smsm_states[n];
- new_state = __raw_readl(SMSM_STATE_ADDR(n));
+ while (kfifo_len(&smsm_snapshot_fifo) >= snapshot_size) {
+ mutex_lock(&smsm_lock);
+ for (n = 0; n < SMSM_NUM_ENTRIES; n++) {
+ state_info = &smsm_states[n];
- if (new_state != state_info->last_value) {
- state_changes = state_info->last_value ^ new_state;
-
- list_for_each_entry(cb_info,
- &state_info->callbacks, cb_list) {
-
- if (cb_info->mask & state_changes)
- cb_info->notify(cb_info->data,
- state_info->last_value,
- new_state);
+ ret = kfifo_out(&smsm_snapshot_fifo, &new_state,
+ sizeof(new_state));
+ if (ret != sizeof(new_state)) {
+ pr_err("%s: snapshot underflow %d\n",
+ __func__, ret);
+ mutex_unlock(&smsm_lock);
+ return;
}
- state_info->last_value = new_state;
- }
- }
- mutex_unlock(&smsm_lock);
+ state_changes = state_info->last_value ^ new_state;
+ if (state_changes) {
+ list_for_each_entry(cb_info,
+ &state_info->callbacks, cb_list) {
+
+ if (cb_info->mask & state_changes)
+ cb_info->notify(cb_info->data,
+ state_info->last_value,
+ new_state);
+ }
+ state_info->last_value = new_state;
+ }
+ }
+ mutex_unlock(&smsm_lock);
+ }
}
diff --git a/arch/arm/mach-msm/smd_debug.c b/arch/arm/mach-msm/smd_debug.c
index d7602f2..6171c92 100644
--- a/arch/arm/mach-msm/smd_debug.c
+++ b/arch/arm/mach-msm/smd_debug.c
@@ -1,7 +1,7 @@
/* arch/arm/mach-msm/smd_debug.c
*
* Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
* Author: Brian Swetland <swetland@google.com>
*
* This software is licensed under the terms of the GNU General Public
@@ -18,6 +18,7 @@
#include <linux/debugfs.h>
#include <linux/list.h>
#include <linux/ctype.h>
+#include <linux/jiffies.h>
#include <mach/msm_iomap.h>
@@ -246,6 +247,7 @@
uint32_t new_state;
};
static struct SMSM_CB_DATA smsm_cb_data;
+static struct completion smsm_cb_completion;
static void smsm_state_cb(void *data, uint32_t old_state, uint32_t new_state)
{
@@ -253,6 +255,7 @@
smsm_cb_data.old_state = old_state;
smsm_cb_data.new_state = new_state;
smsm_cb_data.data = data;
+ complete_all(&smsm_cb_completion);
}
#define UT_EQ_INT(a, b) \
@@ -265,6 +268,16 @@
} \
do {} while (0)
+#define UT_GT_INT(a, b) \
+ if ((a) <= (b)) { \
+ i += scnprintf(buf + i, max - i, \
+ "%s:%d " #a "(%d) > " #b "(%d)\n", \
+ __func__, __LINE__, \
+ a, b); \
+ break; \
+ } \
+ do {} while (0)
+
#define SMSM_CB_TEST_INIT() \
do { \
smsm_cb_data.cb_count = 0; \
@@ -290,16 +303,21 @@
/* de-assert SMSM_SMD_INIT to trigger state update */
UT_EQ_INT(smsm_cb_data.cb_count, 0);
+ INIT_COMPLETION(smsm_cb_completion);
smsm_change_state(SMSM_APPS_STATE, SMSM_SMDINIT, 0x0);
+ UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion,
+ msecs_to_jiffies(20)), 0);
UT_EQ_INT(smsm_cb_data.cb_count, 1);
- UT_EQ_INT(smsm_cb_data.cb_count, 1);
UT_EQ_INT(smsm_cb_data.old_state & SMSM_SMDINIT, SMSM_SMDINIT);
UT_EQ_INT(smsm_cb_data.new_state & SMSM_SMDINIT, 0x0);
UT_EQ_INT((int)smsm_cb_data.data, 0x1234);
/* re-assert SMSM_SMD_INIT to trigger state update */
+ INIT_COMPLETION(smsm_cb_completion);
smsm_change_state(SMSM_APPS_STATE, 0x0, SMSM_SMDINIT);
+ UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion,
+ msecs_to_jiffies(20)), 0);
UT_EQ_INT(smsm_cb_data.cb_count, 2);
UT_EQ_INT(smsm_cb_data.old_state & SMSM_SMDINIT, 0x0);
UT_EQ_INT(smsm_cb_data.new_state & SMSM_SMDINIT, SMSM_SMDINIT);
@@ -310,8 +328,11 @@
UT_EQ_INT(ret, 2);
/* make sure state change doesn't cause any more callbacks */
+ INIT_COMPLETION(smsm_cb_completion);
smsm_change_state(SMSM_APPS_STATE, SMSM_SMDINIT, 0x0);
smsm_change_state(SMSM_APPS_STATE, 0x0, SMSM_SMDINIT);
+ UT_EQ_INT((int)wait_for_completion_timeout(&smsm_cb_completion,
+ msecs_to_jiffies(20)), 0);
UT_EQ_INT(smsm_cb_data.cb_count, 2);
i += scnprintf(buf + i, max - i, "Test %d - PASS\n", test_num);
@@ -329,28 +350,49 @@
UT_EQ_INT(ret, 1);
/* verify both callback bits work */
+ INIT_COMPLETION(smsm_cb_completion);
UT_EQ_INT(smsm_cb_data.cb_count, 0);
smsm_change_state(SMSM_APPS_STATE, SMSM_SMDINIT, 0x0);
+ UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion,
+ msecs_to_jiffies(20)), 0);
UT_EQ_INT(smsm_cb_data.cb_count, 1);
+ INIT_COMPLETION(smsm_cb_completion);
smsm_change_state(SMSM_APPS_STATE, 0x0, SMSM_SMDINIT);
+ UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion,
+ msecs_to_jiffies(20)), 0);
UT_EQ_INT(smsm_cb_data.cb_count, 2);
+ INIT_COMPLETION(smsm_cb_completion);
smsm_change_state(SMSM_APPS_STATE, SMSM_INIT, 0x0);
+ UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion,
+ msecs_to_jiffies(20)), 0);
UT_EQ_INT(smsm_cb_data.cb_count, 3);
+ INIT_COMPLETION(smsm_cb_completion);
smsm_change_state(SMSM_APPS_STATE, 0x0, SMSM_INIT);
+ UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion,
+ msecs_to_jiffies(20)), 0);
UT_EQ_INT(smsm_cb_data.cb_count, 4);
/* deregister 1st callback */
ret = smsm_state_cb_deregister(SMSM_APPS_STATE, SMSM_SMDINIT,
smsm_state_cb, (void *)0x1234);
UT_EQ_INT(ret, 1);
+ INIT_COMPLETION(smsm_cb_completion);
smsm_change_state(SMSM_APPS_STATE, SMSM_SMDINIT, 0x0);
smsm_change_state(SMSM_APPS_STATE, 0x0, SMSM_SMDINIT);
+ UT_EQ_INT((int)wait_for_completion_timeout(&smsm_cb_completion,
+ msecs_to_jiffies(20)), 0);
UT_EQ_INT(smsm_cb_data.cb_count, 4);
+ INIT_COMPLETION(smsm_cb_completion);
smsm_change_state(SMSM_APPS_STATE, SMSM_INIT, 0x0);
+ UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion,
+ msecs_to_jiffies(20)), 0);
UT_EQ_INT(smsm_cb_data.cb_count, 5);
+ INIT_COMPLETION(smsm_cb_completion);
smsm_change_state(SMSM_APPS_STATE, 0x0, SMSM_INIT);
+ UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion,
+ msecs_to_jiffies(20)), 0);
UT_EQ_INT(smsm_cb_data.cb_count, 6);
/* deregister 2nd callback */
@@ -359,8 +401,11 @@
UT_EQ_INT(ret, 2);
/* make sure state change doesn't cause any more callbacks */
+ INIT_COMPLETION(smsm_cb_completion);
smsm_change_state(SMSM_APPS_STATE, SMSM_INIT, 0x0);
smsm_change_state(SMSM_APPS_STATE, 0x0, SMSM_INIT);
+ UT_EQ_INT((int)wait_for_completion_timeout(&smsm_cb_completion,
+ msecs_to_jiffies(20)), 0);
UT_EQ_INT(smsm_cb_data.cb_count, 6);
i += scnprintf(buf + i, max - i, "Test %d - PASS\n", test_num);
@@ -378,12 +423,18 @@
UT_EQ_INT(ret, 0);
/* verify both callbacks work */
+ INIT_COMPLETION(smsm_cb_completion);
UT_EQ_INT(smsm_cb_data.cb_count, 0);
smsm_change_state(SMSM_APPS_STATE, SMSM_SMDINIT, 0x0);
+ UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion,
+ msecs_to_jiffies(20)), 0);
UT_EQ_INT(smsm_cb_data.cb_count, 1);
UT_EQ_INT((int)smsm_cb_data.data, 0x1234);
+ INIT_COMPLETION(smsm_cb_completion);
smsm_change_state(SMSM_APPS_STATE, SMSM_INIT, 0x0);
+ UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion,
+ msecs_to_jiffies(20)), 0);
UT_EQ_INT(smsm_cb_data.cb_count, 2);
UT_EQ_INT((int)smsm_cb_data.data, 0x3456);
@@ -659,6 +710,8 @@
debug_create("version", 0444, dent, debug_read_smem_version);
debug_create("smsm_test", 0444, dent, debug_test_smsm);
+ init_completion(&smsm_cb_completion);
+
return 0;
}