msm: smp2p: Handle simultaneous open and update events

If an SMP2P entry is changed while version negotiation is ongoing, then
only an OPEN event will be sent instead of the expected OPEN event
followed by an UPDATE event.

Modify the code to always send an OPEN event followed by an update event
for this case.

Change-Id: Ideba72346d40679eab3c21da4ae24d26f0dfaace
Signed-off-by: Eric Holmberg <eholmber@codeaurora.org>
diff --git a/arch/arm/mach-msm/smp2p.c b/arch/arm/mach-msm/smp2p.c
index 193e468..9dc17d9 100644
--- a/arch/arm/mach-msm/smp2p.c
+++ b/arch/arm/mach-msm/smp2p.c
@@ -76,7 +76,7 @@
  * @remote_pid: Outbound processor ID.
  * @in_edge_list: Adds this structure into smp2p_in_list_item::list.
  * @in_notifier_list: List for notifier block for entry opening/updates.
- * @entry_val: Previous value of the entry.
+ * @prev_entry_val: Previous value of the entry.
  * @entry_ptr: Points to the current value in smem item.
  * @notifier_count: Counts the number of notifier registered per pid,entry.
  */
@@ -85,7 +85,7 @@
 	char name[SMP2P_MAX_ENTRY_NAME];
 	struct list_head in_edge_list;
 	struct raw_notifier_head in_notifier_list;
-	uint32_t entry_val;
+	uint32_t prev_entry_val;
 	uint32_t __iomem *entry_ptr;
 	uint32_t notifier_count;
 };
@@ -1333,10 +1333,10 @@
 				&entry_ptr, NULL);
 		if (entry_ptr) {
 			in->entry_ptr = entry_ptr;
-			in->entry_val = *(entry_ptr);
+			in->prev_entry_val = readl_relaxed(entry_ptr);
 
-			data.previous_value = in->entry_val;
-			data.current_value = *(in->entry_ptr);
+			data.previous_value = in->prev_entry_val;
+			data.current_value = in->prev_entry_val;
 			in_notifier->notifier_call(in_notifier, SMP2P_OPEN,
 					(void *)&data);
 		}
@@ -1458,18 +1458,7 @@
 	}
 
 	list_for_each_entry(pos, &in_list[pid].list, in_edge_list) {
-		if (pos->entry_ptr != NULL) {
-			/* entry already open */
-			curr_data = *(pos->entry_ptr);
-			if (curr_data != pos->entry_val) {
-				data.previous_value = pos->entry_val;
-				data.current_value = curr_data;
-				pos->entry_val = curr_data;
-				raw_notifier_call_chain(
-					&pos->in_notifier_list,
-					SMP2P_ENTRY_UPDATE, (void *)&data);
-			}
-		} else {
+		if (pos->entry_ptr == NULL) {
 			/* entry not open - try to open it */
 			out_list[pid].ops_ptr->find_entry(smem_h_ptr,
 				in_list[pid].safe_total_entries, pos->name,
@@ -1477,14 +1466,27 @@
 
 			if (entry_ptr) {
 				pos->entry_ptr = entry_ptr;
+				pos->prev_entry_val = 0;
 				data.previous_value = 0;
-				data.current_value =
-					*(entry_ptr);
+				data.current_value = readl_relaxed(entry_ptr);
 				raw_notifier_call_chain(
 					    &pos->in_notifier_list,
 					    SMP2P_OPEN, (void *)&data);
 			}
 		}
+
+		if (pos->entry_ptr != NULL) {
+			/* send update notification */
+			curr_data = readl_relaxed(pos->entry_ptr);
+			if (curr_data != pos->prev_entry_val) {
+				data.previous_value = pos->prev_entry_val;
+				data.current_value = curr_data;
+				pos->prev_entry_val = curr_data;
+				raw_notifier_call_chain(
+					&pos->in_notifier_list,
+					SMP2P_ENTRY_UPDATE, (void *)&data);
+			}
+		}
 	}
 	spin_unlock_irqrestore(&in_list[pid].in_item_lock_lhb1, flags);
 }
diff --git a/arch/arm/mach-msm/smp2p_gpio_test.c b/arch/arm/mach-msm/smp2p_gpio_test.c
index 70de20a..1f6f479 100644
--- a/arch/arm/mach-msm/smp2p_gpio_test.c
+++ b/arch/arm/mach-msm/smp2p_gpio_test.c
@@ -408,6 +408,115 @@
 }
 
 /**
+ * smp2p_ut_local_gpio_in_update_open - Verify combined open/update.
+ *
+ * @s:   pointer to output file
+ *
+ * If the remote side updates the SMP2P bits and sends before negotiation is
+ * complete, then the UPDATE event will have to be delayed until negotiation is
+ * complete.  This should result in both the OPEN and UPDATE events coming in
+ * right after each other and the behavior should be transparent to the clients
+ * of SMP2P GPIO.
+ */
+static void smp2p_ut_local_gpio_in_update_open(struct seq_file *s)
+{
+	int failed = 0;
+	struct gpio_info *cb_info = &gpio_info[SMP2P_REMOTE_MOCK_PROC].in;
+	int id;
+	int ret;
+	int virq;
+	struct msm_smp2p_remote_mock *mock;
+
+	seq_printf(s, "Running %s\n", __func__);
+
+	cb_data_reset(cb_info);
+	do {
+		/* initialize mock edge */
+		ret = smp2p_reset_mock_edge();
+		UT_ASSERT_INT(ret, ==, 0);
+
+		mock = msm_smp2p_get_remote_mock();
+		UT_ASSERT_PTR(mock, !=, NULL);
+
+		mock->rx_interrupt_count = 0;
+		memset(&mock->remote_item, 0,
+			sizeof(struct smp2p_smem_item));
+		smp2p_init_header((struct smp2p_smem *)&mock->remote_item,
+			SMP2P_REMOTE_MOCK_PROC, SMP2P_APPS_PROC,
+			0, 1);
+		strlcpy(mock->remote_item.entries[0].name, "smp2p",
+			SMP2P_MAX_ENTRY_NAME);
+		SMP2P_SET_ENT_VALID(
+			mock->remote_item.header.valid_total_ent, 1);
+
+		/* register for interrupts */
+		smp2p_gpio_open_test_entry("smp2p",
+				SMP2P_REMOTE_MOCK_PROC, true);
+
+		UT_ASSERT_INT(0, <, cb_info->irq_base_id);
+		for (id = 0; id < SMP2P_BITS_PER_ENTRY && !failed; ++id) {
+			virq = cb_info->irq_base_id + id;
+			UT_ASSERT_INT(0, >, (unsigned int)irq_to_desc(virq));
+			ret = request_irq(virq,
+					smp2p_gpio_irq,	IRQ_TYPE_EDGE_BOTH,
+					"smp2p_test", cb_info);
+			UT_ASSERT_INT(0, ==, ret);
+		}
+		if (failed)
+			break;
+
+		/* update the state value and complete negotiation */
+		mock->remote_item.entries[0].entry = 0xDEADDEAD;
+		msm_smp2p_set_remote_mock_exists(true);
+		mock->tx_interrupt();
+
+		/* verify delayed state updates were processed */
+		for (id = 0; id < SMP2P_BITS_PER_ENTRY && !failed; ++id) {
+			virq = cb_info->irq_base_id + id;
+
+			UT_ASSERT_INT(cb_info->cb_count, >, 0);
+			if (0x1 & (0xDEADDEAD >> id)) {
+				/* rising edge should have been triggered */
+				if (!test_bit(id, cb_info->triggered_irqs)) {
+					seq_printf(s,
+						"%s:%d bit %d clear, expected set\n",
+						__func__, __LINE__, id);
+					failed = 1;
+					break;
+				}
+			} else {
+				/* edge should not have been triggered */
+				if (test_bit(id, cb_info->triggered_irqs)) {
+					seq_printf(s,
+						"%s:%d bit %d set, expected clear\n",
+						__func__, __LINE__, id);
+					failed = 1;
+					break;
+				}
+			}
+		}
+		if (failed)
+			break;
+
+		seq_printf(s, "\tOK\n");
+	} while (0);
+
+	if (failed) {
+		pr_err("%s: Failed\n", __func__);
+		seq_printf(s, "\tFailed\n");
+	}
+
+	/* unregister for interrupts */
+	if (cb_info->irq_base_id) {
+		for (id = 0; id < SMP2P_BITS_PER_ENTRY; ++id)
+			free_irq(cb_info->irq_base_id + id, cb_info);
+	}
+
+	smp2p_gpio_open_test_entry("smp2p",
+			SMP2P_REMOTE_MOCK_PROC, false);
+}
+
+/**
  * smp2p_gpio_write_bits - writes value to each GPIO pin specified in mask.
  *
  * @gpio: gpio test structure
@@ -618,6 +727,8 @@
 	 */
 	smp2p_debug_create("ut_local_gpio_out", smp2p_ut_local_gpio_out);
 	smp2p_debug_create("ut_local_gpio_in", smp2p_ut_local_gpio_in);
+	smp2p_debug_create("ut_local_gpio_in_update_open",
+		smp2p_ut_local_gpio_in_update_open);
 	smp2p_debug_create("ut_remote_gpio_inout", smp2p_ut_remote_inout);
 	return 0;
 }