msm: smp2p: Add new SMSM Point-to-Point Driver

The Shared Memory Point to Point (SMP2P) driver facilitates
communication of multiple 32-bit values between two processors.  Each
value (called a state entry) has a single writer (the local side) and a
single reader (the remote side).  Entries are uniquely identified in the
system by the directed edge (local processor ID to remote processor ID)
and a string identifier.

This driver, which is layered on top of shared memory (SMEM), will
eventually replace the existing broadcast implementation of SMSM.  This
driver has several advantages over SMSM including:
 * Point-to-point communication which allows for XPU protection of SMEM
 * Multiple 32-bit state values for future expansion
 * GPIO and Virtual interrupt support for client-facing API
 * Version and feature negotiation to allow for phased upgrades of
   a system without requiring cross-processor dependencies.

Change-Id: Iefe2169ae686a3e5f400b3a7039f6b114a8b7e5a
Signed-off-by: Eric Holmberg <eholmber@codeaurora.org>
diff --git a/arch/arm/mach-msm/smp2p_debug.c b/arch/arm/mach-msm/smp2p_debug.c
new file mode 100644
index 0000000..1a5c96e
--- /dev/null
+++ b/arch/arm/mach-msm/smp2p_debug.c
@@ -0,0 +1,328 @@
+/* arch/arm/mach-msm/smp2p_debug.c
+ *
+ * Copyright (c) 2013, The Linux Foundation. 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/ctype.h>
+#include <linux/list.h>
+#include <linux/debugfs.h>
+#include "smp2p_private.h"
+
+#if defined(CONFIG_DEBUG_FS)
+
+/**
+ * Dump interrupt statistics.
+ *
+ * @s:   pointer to output file
+ */
+static void smp2p_int_stats(struct seq_file *s)
+{
+	struct smp2p_interrupt_config *int_cfg;
+	int pid;
+
+	int_cfg = smp2p_get_interrupt_config();
+	if (!int_cfg)
+		return;
+
+	seq_printf(s, "| Processor | Incoming Id | Incoming # |");
+	seq_printf(s, " Outgoing # | Base Ptr |   Mask   |\n");
+
+	for (pid = 0; pid < SMP2P_NUM_PROCS; ++pid) {
+		if (!int_cfg[pid].is_configured &&
+				pid != SMP2P_REMOTE_MOCK_PROC)
+			continue;
+
+		seq_printf(s,
+			"| %5s (%d) | %11u | %10u | %10u | %p | %08x |\n",
+			int_cfg[pid].name,
+			pid, int_cfg[pid].in_int_id,
+			int_cfg[pid].in_interrupt_count,
+			int_cfg[pid].out_interrupt_count,
+			int_cfg[pid].out_int_ptr,
+			int_cfg[pid].out_int_mask);
+	}
+}
+
+/**
+ * Dump item header line 1.
+ *
+ * @buf:      output buffer
+ * @max:      length of output buffer
+ * @item_ptr: SMEM item pointer
+ * @state:    item state
+ * @returns: Number of bytes written to output buffer
+ */
+static int smp2p_item_header1(char *buf, int max, struct smp2p_smem *item_ptr,
+	enum msm_smp2p_edge_state state)
+{
+	int i = 0;
+	const char *state_text;
+
+	if (!item_ptr) {
+		i += scnprintf(buf + i, max - i, "None");
+		return i;
+	}
+
+	switch (state) {
+	case SMP2P_EDGE_STATE_CLOSED:
+		state_text = "State: Closed";
+		break;
+	case SMP2P_EDGE_STATE_OPENING:
+		state_text = "State: Opening";
+		break;
+	case SMP2P_EDGE_STATE_OPENED:
+		state_text = "State: Opened";
+		break;
+	default:
+		state_text = "";
+		break;
+	}
+
+	i += scnprintf(buf + i, max - i,
+		"%-14s LPID %d RPID %d",
+		state_text,
+		SMP2P_GET_LOCAL_PID(item_ptr->rem_loc_proc_id),
+		SMP2P_GET_REMOTE_PID(item_ptr->rem_loc_proc_id)
+		);
+
+	return i;
+}
+
+/**
+ * Dump item header line 2.
+ *
+ * @buf:      output buffer
+ * @max:      length of output buffer
+ * @item_ptr: SMEM item pointer
+ * @returns: Number of bytes written to output buffer
+ */
+static int smp2p_item_header2(char *buf, int max, struct smp2p_smem *item_ptr)
+{
+	int i = 0;
+
+	if (!item_ptr) {
+		i += scnprintf(buf + i, max - i, "None");
+		return i;
+	}
+
+	i += scnprintf(buf + i, max - i,
+		"Version: %08x Features: %08x",
+		SMP2P_GET_VERSION(item_ptr->feature_version),
+		SMP2P_GET_FEATURES(item_ptr->feature_version)
+		);
+
+	return i;
+}
+
+/**
+ * Dump item header line 3.
+ *
+ * @buf:      output buffer
+ * @max:      length of output buffer
+ * @item_ptr: SMEM item pointer
+ * @state:    item state
+ * @returns: Number of bytes written to output buffer
+ */
+static int smp2p_item_header3(char *buf, int max, struct smp2p_smem *item_ptr)
+{
+	int i = 0;
+
+	if (!item_ptr) {
+		i += scnprintf(buf + i, max - i, "None");
+		return i;
+	}
+
+	i += scnprintf(buf + i, max - i,
+		"Entries Valid/Max: %d/%d",
+		SMP2P_GET_ENT_VALID(item_ptr->valid_total_ent),
+		SMP2P_GET_ENT_TOTAL(item_ptr->valid_total_ent)
+		);
+
+	return i;
+}
+
+/**
+ * Dump individual input/output item pair.
+ *
+ * @s:   pointer to output file
+ */
+static void smp2p_item(struct seq_file *s, int remote_pid)
+{
+	struct smp2p_smem *out_ptr;
+	struct smp2p_smem *in_ptr;
+	struct smp2p_interrupt_config *int_cfg;
+	char tmp_buff[64];
+	int state;
+	int entry;
+	struct smp2p_entry_v1 *out_entries = NULL;
+	struct smp2p_entry_v1 *in_entries = NULL;
+	int out_valid = 0;
+	int in_valid = 0;
+
+	int_cfg = smp2p_get_interrupt_config();
+	if (!int_cfg)
+		return;
+	if (!int_cfg[remote_pid].is_configured &&
+			remote_pid != SMP2P_REMOTE_MOCK_PROC)
+		return;
+
+	out_ptr = smp2p_get_out_item(remote_pid, &state);
+	in_ptr = smp2p_get_in_item(remote_pid);
+
+	if (!out_ptr && !in_ptr)
+		return;
+
+	/* print item headers */
+	seq_printf(s, "%s%s\n",
+		" ====================================== ",
+		"======================================");
+	scnprintf(tmp_buff, sizeof(tmp_buff),
+		"Apps(%d)->%s(%d)",
+		SMP2P_APPS_PROC, int_cfg[remote_pid].name, remote_pid);
+	seq_printf(s, "| %-37s", tmp_buff);
+
+	scnprintf(tmp_buff, sizeof(tmp_buff),
+		"%s(%d)->Apps(%d)",
+		int_cfg[remote_pid].name, remote_pid, SMP2P_APPS_PROC);
+	seq_printf(s, "| %-37s|\n", tmp_buff);
+	seq_printf(s, "%s%s\n",
+		" ====================================== ",
+		"======================================");
+
+	smp2p_item_header1(tmp_buff, sizeof(tmp_buff), out_ptr, state);
+	seq_printf(s, "| %-37s", tmp_buff);
+	smp2p_item_header1(tmp_buff, sizeof(tmp_buff), in_ptr, -1);
+	seq_printf(s, "| %-37s|\n", tmp_buff);
+
+	smp2p_item_header2(tmp_buff, sizeof(tmp_buff), out_ptr);
+	seq_printf(s, "| %-37s", tmp_buff);
+	smp2p_item_header2(tmp_buff, sizeof(tmp_buff), in_ptr);
+	seq_printf(s, "| %-37s|\n", tmp_buff);
+
+	smp2p_item_header3(tmp_buff, sizeof(tmp_buff), out_ptr);
+	seq_printf(s, "| %-37s", tmp_buff);
+	smp2p_item_header3(tmp_buff, sizeof(tmp_buff), in_ptr);
+	seq_printf(s, "| %-37s|\n", tmp_buff);
+
+	seq_printf(s, " %s%s\n",
+		"-------------------------------------- ",
+		"--------------------------------------");
+	seq_printf(s, "| %-37s",
+		"Entry Name       Value");
+	seq_printf(s, "| %-37s|\n",
+		"Entry Name       Value");
+	seq_printf(s, " %s%s\n",
+		"-------------------------------------- ",
+		"--------------------------------------");
+
+	/* print entries */
+	if (out_ptr) {
+		out_entries = (struct smp2p_entry_v1 *)((void *)out_ptr +
+				sizeof(struct smp2p_smem));
+		out_valid = SMP2P_GET_ENT_VALID(out_ptr->valid_total_ent);
+	}
+
+	if (in_ptr) {
+		in_entries = (struct smp2p_entry_v1 *)((void *)in_ptr +
+				sizeof(struct smp2p_smem));
+		in_valid = SMP2P_GET_ENT_VALID(out_ptr->valid_total_ent);
+	}
+
+	for (entry = 0; out_entries || in_entries; ++entry) {
+		if (out_entries && entry < out_valid) {
+			scnprintf(tmp_buff, sizeof(tmp_buff),
+					"%-16s 0x%08x",
+					out_entries->name,
+					out_entries->entry);
+			++out_entries;
+		} else {
+			out_entries = NULL;
+			scnprintf(tmp_buff, sizeof(tmp_buff), "None");
+		}
+		seq_printf(s, "| %-37s", tmp_buff);
+
+		if (in_entries && entry < in_valid) {
+			scnprintf(tmp_buff, sizeof(tmp_buff),
+					"%-16s 0x%08x",
+					in_entries->name,
+					in_entries->entry);
+			++in_entries;
+		} else {
+			in_entries = NULL;
+			scnprintf(tmp_buff, sizeof(tmp_buff), "None");
+		}
+		seq_printf(s, "| %-37s|\n", tmp_buff);
+	}
+	seq_printf(s, " %s%s\n\n",
+		"-------------------------------------- ",
+		"--------------------------------------");
+}
+
+/**
+ * Dump item state.
+ *
+ * @s:   pointer to output file
+ */
+static void smp2p_items(struct seq_file *s)
+{
+	int pid;
+
+	for (pid = 0; pid < SMP2P_NUM_PROCS; ++pid)
+		smp2p_item(s, pid);
+}
+
+static struct dentry *dent;
+
+static int debugfs_show(struct seq_file *s, void *data)
+{
+	void (*show)(struct seq_file *) = s->private;
+
+	show(s);
+
+	return 0;
+}
+
+static int debug_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, debugfs_show, inode->i_private);
+}
+
+static const struct file_operations debug_ops = {
+	.open = debug_open,
+	.release = single_release,
+	.read = seq_read,
+	.llseek = seq_lseek,
+};
+
+void debug_create(const char *name,
+			 void (*show)(struct seq_file *))
+{
+	struct dentry *file;
+
+	file = debugfs_create_file(name, 0444, dent, show, &debug_ops);
+	if (!file)
+		pr_err("%s: unable to create file '%s'\n", __func__, name);
+}
+
+static int __init smp2p_debugfs_init(void)
+{
+	dent = debugfs_create_dir("smp2p", 0);
+	if (IS_ERR(dent))
+		return PTR_ERR(dent);
+
+	debug_create("int_stats", smp2p_int_stats);
+	debug_create("items", smp2p_items);
+
+	return 0;
+}
+
+late_initcall(smp2p_debugfs_init);
+#endif /* CONFIG_DEBUG_FS */