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_loopback.c b/arch/arm/mach-msm/smp2p_loopback.c
new file mode 100644
index 0000000..d95c93f
--- /dev/null
+++ b/arch/arm/mach-msm/smp2p_loopback.c
@@ -0,0 +1,440 @@
+/* arch/arm/mach-msm/smp2p_loopback.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/debugfs.h>
+#include <linux/list.h>
+#include <linux/ctype.h>
+#include <linux/jiffies.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/completion.h>
+#include <linux/termios.h>
+#include <linux/module.h>
+#include <linux/remote_spinlock.h>
+#include "smd_private.h"
+#include "smp2p_private.h"
+
+/**
+ * struct smp2p_loopback_ctx - Representation of remote loopback object.
+ *
+ * @proc_id: Processor id of the processor that sends the loopback commands.
+ * @out: Handle to the  smem entry structure for providing the response.
+ * @out_nb: Notifies the opening of local entry.
+ * @out_is_active: Outbound entry events should be processed.
+ * @in_nb: Notifies changes in the remote entry.
+ * @in_is_active: Inbound entry events should be processed.
+ * @rmt_lpb_work: Work item that handles the incoming loopback commands.
+ * @rmt_cmd: Structure that holds the current and previous value of the entry.
+ */
+struct smp2p_loopback_ctx {
+	int proc_id;
+	struct msm_smp2p_out *out;
+	struct notifier_block out_nb;
+	bool out_is_active;
+	struct notifier_block in_nb;
+	bool in_is_active;
+	struct work_struct  rmt_lpb_work;
+	struct msm_smp2p_update_notif rmt_cmd;
+};
+
+static struct smp2p_loopback_ctx  remote_loopback[SMP2P_NUM_PROCS];
+static struct msm_smp2p_remote_mock remote_mock;
+
+/**
+ * remote_spinlock_test - Handles remote spinlock test.
+ *
+ * @ctx: Loopback context
+ */
+static void remote_spinlock_test(struct smp2p_loopback_ctx *ctx)
+{
+	uint32_t test_request;
+	uint32_t test_response;
+	unsigned long flags;
+	int n;
+	unsigned lock_count = 0;
+	remote_spinlock_t *smem_spinlock;
+
+	test_request = 0x0;
+	SMP2P_SET_RMT_CMD_TYPE_REQ(test_request);
+	smem_spinlock = smem_get_remote_spinlock();
+	if (!smem_spinlock) {
+		pr_err("%s: unable to get remote spinlock\n", __func__);
+		return;
+	}
+
+	for (;;) {
+		remote_spin_lock_irqsave(smem_spinlock, flags);
+		++lock_count;
+		SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_LOCKED);
+		(void)msm_smp2p_out_write(ctx->out, test_request);
+
+		for (n = 0; n < 10000; ++n) {
+			(void)msm_smp2p_in_read(ctx->proc_id,
+					"smp2p", &test_response);
+			test_response = SMP2P_GET_RMT_CMD(test_response);
+
+			if (test_response == SMP2P_LB_CMD_RSPIN_END)
+				break;
+
+			if (test_response != SMP2P_LB_CMD_RSPIN_UNLOCKED)
+				SMP2P_ERR("%s: invalid spinlock command %x\n",
+					__func__, test_response);
+		}
+
+		if (test_response == SMP2P_LB_CMD_RSPIN_END) {
+			SMP2P_SET_RMT_CMD_TYPE_RESP(test_request);
+			SMP2P_SET_RMT_CMD(test_request,
+					SMP2P_LB_CMD_RSPIN_END);
+			SMP2P_SET_RMT_DATA(test_request, lock_count);
+			(void)msm_smp2p_out_write(ctx->out, test_request);
+			break;
+		}
+
+		SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_UNLOCKED);
+		(void)msm_smp2p_out_write(ctx->out, test_request);
+		remote_spin_unlock_irqrestore(smem_spinlock, flags);
+	}
+	remote_spin_unlock_irqrestore(smem_spinlock, flags);
+}
+
+/**
+ * smp2p_rmt_lpb_worker - Handles incoming remote loopback commands.
+ *
+ * @work: Work Item scheduled to handle the incoming commands.
+ */
+static void smp2p_rmt_lpb_worker(struct work_struct *work)
+{
+	struct smp2p_loopback_ctx *ctx;
+	int lpb_cmd;
+	int lpb_cmd_type;
+	int lpb_data;
+
+	ctx = container_of(work, struct smp2p_loopback_ctx, rmt_lpb_work);
+
+	if (!ctx->in_is_active || !ctx->out_is_active)
+		return;
+
+	if (ctx->rmt_cmd.previous_value == ctx->rmt_cmd.current_value)
+		return;
+
+	lpb_cmd_type =  SMP2P_GET_RMT_CMD_TYPE(ctx->rmt_cmd.current_value);
+	lpb_cmd = SMP2P_GET_RMT_CMD(ctx->rmt_cmd.current_value);
+	lpb_data = SMP2P_GET_RMT_DATA(ctx->rmt_cmd.current_value);
+
+	if (lpb_cmd & SMP2P_RLPB_IGNORE)
+		return;
+
+	switch (lpb_cmd) {
+	case SMP2P_LB_CMD_NOOP:
+	    /* Do nothing */
+	    break;
+
+	case SMP2P_LB_CMD_ECHO:
+		SMP2P_SET_RMT_CMD_TYPE(ctx->rmt_cmd.current_value, 0);
+		SMP2P_SET_RMT_DATA(ctx->rmt_cmd.current_value,
+							lpb_data);
+		(void)msm_smp2p_out_write(ctx->out,
+					ctx->rmt_cmd.current_value);
+	    break;
+
+	case SMP2P_LB_CMD_CLEARALL:
+		ctx->rmt_cmd.current_value = 0;
+		(void)msm_smp2p_out_write(ctx->out,
+					ctx->rmt_cmd.current_value);
+	    break;
+
+	case SMP2P_LB_CMD_PINGPONG:
+		SMP2P_SET_RMT_CMD_TYPE(ctx->rmt_cmd.current_value, 0);
+		if (lpb_data) {
+			lpb_data--;
+			SMP2P_SET_RMT_DATA(ctx->rmt_cmd.current_value,
+					lpb_data);
+			(void)msm_smp2p_out_write(ctx->out,
+					ctx->rmt_cmd.current_value);
+		}
+	    break;
+
+	case SMP2P_LB_CMD_RSPIN_START:
+		remote_spinlock_test(ctx);
+		break;
+
+	case SMP2P_LB_CMD_RSPIN_LOCKED:
+	case SMP2P_LB_CMD_RSPIN_UNLOCKED:
+	case SMP2P_LB_CMD_RSPIN_END:
+		/* not used for remote spinlock test */
+		break;
+
+	default:
+		SMP2P_DBG("%s: Unknown loopback command %x\n",
+				__func__, lpb_cmd);
+		break;
+	}
+}
+
+/**
+ * smp2p_rmt_in_edge_notify -  Schedules a work item to handle the commands.
+ *
+ * @nb: Notifier block, this is called when the value in remote entry changes.
+ * @event: Takes value SMP2P_ENTRY_UPDATE or SMP2P_OPEN based on the event.
+ * @data: Consists of previous and current value in case of entry update.
+ * @returns: 0 for success (return value required for notifier chains).
+ */
+static int smp2p_rmt_in_edge_notify(struct notifier_block *nb,
+				unsigned long event, void *data)
+{
+	struct smp2p_loopback_ctx *ctx;
+
+	if (!(event == SMP2P_ENTRY_UPDATE || event == SMP2P_OPEN))
+		return 0;
+
+	ctx = container_of(nb, struct smp2p_loopback_ctx, in_nb);
+	if (data && ctx->in_is_active) {
+			ctx->rmt_cmd =
+			    *(struct msm_smp2p_update_notif *)data;
+			schedule_work(&ctx->rmt_lpb_work);
+	}
+
+	return 0;
+}
+
+/**
+ * smp2p_rmt_out_edge_notify - Notifies on the opening of the outbound entry.
+ *
+ * @nb: Notifier block, this is called when the local entry is open.
+ * @event: Takes on value SMP2P_OPEN when the local entry is open.
+ * @data: Consist of current value of the remote entry, if entry is open.
+ * @returns: 0 for success (return value required for notifier chains).
+ */
+static int smp2p_rmt_out_edge_notify(struct notifier_block  *nb,
+				unsigned long event, void *data)
+{
+	struct smp2p_loopback_ctx *ctx;
+
+	ctx = container_of(nb, struct smp2p_loopback_ctx, out_nb);
+	if (event == SMP2P_OPEN)
+		SMP2P_DBG("%s: 'smp2p':%d opened\n", __func__,
+				ctx->proc_id);
+
+	return 0;
+}
+
+/**
+ * msm_smp2p_init_rmt_lpb -  Initializes the remote loopback object.
+ *
+ * @ctx: Pointer to remote loopback object that needs to be initialized.
+ * @pid: Processor id  of the processor that is sending the commands.
+ * @entry: Name of the entry that needs to be opened locally.
+ * @returns: 0 on success, standard Linux error code otherwise.
+ */
+static int msm_smp2p_init_rmt_lpb(struct  smp2p_loopback_ctx *ctx,
+			int pid, const char *entry)
+{
+	int ret = 0;
+	int tmp;
+
+	if (!ctx || !entry || pid > SMP2P_NUM_PROCS)
+		return -EINVAL;
+
+	ctx->in_nb.notifier_call = smp2p_rmt_in_edge_notify;
+	ctx->out_nb.notifier_call = smp2p_rmt_out_edge_notify;
+	ctx->proc_id = pid;
+	ctx->in_is_active = true;
+	ctx->out_is_active = true;
+	tmp = msm_smp2p_out_open(pid, entry, &ctx->out_nb,
+						&ctx->out);
+	if (tmp) {
+		SMP2P_ERR("%s: open failed outbound entry '%s':%d - ret %d\n",
+				__func__, entry, pid, tmp);
+		ret = tmp;
+	}
+
+	tmp = msm_smp2p_in_register(ctx->proc_id,
+				SMP2P_RLPB_ENTRY_NAME,
+				&ctx->in_nb);
+	if (tmp) {
+		SMP2P_ERR("%s: unable to open inbound entry '%s':%d - ret %d\n",
+				__func__, entry, pid, tmp);
+		ret = tmp;
+	}
+
+	return ret;
+}
+
+/**
+ * msm_smp2p_init_rmt_lpb_proc - Wrapper over msm_smp2p_init_rmt_lpb
+ *
+ * @remote_pid: Processor ID of the processor that sends loopback command.
+ * @returns: Pointer to outbound entry handle.
+ */
+void *msm_smp2p_init_rmt_lpb_proc(int remote_pid)
+{
+	int tmp;
+	void *ret = NULL;
+
+	tmp = msm_smp2p_init_rmt_lpb(&remote_loopback[remote_pid],
+			remote_pid, SMP2P_RLPB_ENTRY_NAME);
+	if (!tmp)
+		ret = remote_loopback[remote_pid].out;
+
+	return ret;
+}
+EXPORT_SYMBOL(msm_smp2p_init_rmt_lpb_proc);
+
+/**
+ * msm_smp2p_deinit_rmt_lpb_proc - Unregister support for remote processor.
+ *
+ * @remote_pid:  Processor ID of the remote system.
+ * @returns: 0 on success, standard Linux error code otherwise.
+ *
+ * Unregister loopback support for remote processor.
+ */
+int msm_smp2p_deinit_rmt_lpb_proc(int remote_pid)
+{
+	int ret = 0;
+	int tmp;
+	struct smp2p_loopback_ctx *ctx;
+
+	if (remote_pid >= SMP2P_NUM_PROCS)
+		return -EINVAL;
+
+	ctx = &remote_loopback[remote_pid];
+
+	/* abort any pending notifications */
+	remote_loopback[remote_pid].out_is_active = false;
+	remote_loopback[remote_pid].in_is_active = false;
+	flush_work(&ctx->rmt_lpb_work);
+
+	/* unregister entries */
+	tmp = msm_smp2p_out_close(&remote_loopback[remote_pid].out);
+	remote_loopback[remote_pid].out = NULL;
+	if (tmp) {
+		SMP2P_ERR("%s: outbound 'smp2p':%d close failed %d\n",
+				__func__, remote_pid, tmp);
+		ret = tmp;
+	}
+
+	tmp = msm_smp2p_in_unregister(remote_pid,
+		SMP2P_RLPB_ENTRY_NAME, &remote_loopback[remote_pid].in_nb);
+	if (tmp) {
+		SMP2P_ERR("%s: inbound 'smp2p':%d close failed %d\n",
+				__func__, remote_pid, tmp);
+		ret = tmp;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(msm_smp2p_deinit_rmt_lpb_proc);
+
+/**
+ * msm_smp2p_set_remote_mock_exists - Sets the remote mock configuration.
+ *
+ * @item_exists: true = Remote mock SMEM item exists
+ *
+ * This is used in the testing environment to simulate the existence of the
+ * remote smem item in order to test the negotiation algorithm.
+ */
+void msm_smp2p_set_remote_mock_exists(bool item_exists)
+{
+	remote_mock.item_exists = item_exists;
+}
+EXPORT_SYMBOL(msm_smp2p_set_remote_mock_exists);
+
+/**
+ * msm_smp2p_get_remote_mock - Get remote mock object.
+ *
+ * @returns: Point to the remote mock object.
+ */
+void *msm_smp2p_get_remote_mock(void)
+{
+	return &remote_mock;
+}
+EXPORT_SYMBOL(msm_smp2p_get_remote_mock);
+
+/**
+ * msm_smp2p_get_remote_mock_smem_item - Returns a pointer to remote item.
+ *
+ * @size:    Size of item.
+ * @returns: Pointer to mock remote smem item.
+ */
+void *msm_smp2p_get_remote_mock_smem_item(uint32_t *size)
+{
+	void *ptr = NULL;
+	if (remote_mock.item_exists) {
+		*size = sizeof(remote_mock.remote_item);
+		ptr = &(remote_mock.remote_item);
+	}
+
+	return ptr;
+}
+EXPORT_SYMBOL(msm_smp2p_get_remote_mock_smem_item);
+
+/**
+ * smp2p_remote_mock_rx_interrupt - Triggers receive interrupt for mock proc.
+ *
+ * @returns: 0 for success
+ *
+ * This function simulates the receiving of interrupt by the mock remote
+ * processor in a testing environment.
+ */
+int smp2p_remote_mock_rx_interrupt(void)
+{
+	remote_mock.rx_interrupt_count++;
+	if (remote_mock.initialized)
+		complete(&remote_mock.cb_completion);
+	return 0;
+}
+EXPORT_SYMBOL(smp2p_remote_mock_rx_interrupt);
+
+/**
+ * smp2p_remote_mock_tx_interrupt - Calls the SMP2P interrupt handler.
+ *
+ * This function calls the interrupt handler of the Apps processor to simulate
+ * receiving interrupts from a remote processor.
+ */
+static void smp2p_remote_mock_tx_interrupt(void)
+{
+	msm_smp2p_interrupt_handler(SMP2P_REMOTE_MOCK_PROC);
+}
+
+/**
+ * smp2p_remote_mock_init - Initialize the remote mock and loopback objects.
+ *
+ * @returns: 0 for success
+ */
+static int __init smp2p_remote_mock_init(void)
+{
+	int i;
+
+	smp2p_init_header(&remote_mock.remote_item.header,
+			SMP2P_REMOTE_MOCK_PROC, SMP2P_APPS_PROC,
+			0, 0);
+	remote_mock.rx_interrupt_count = 0;
+	remote_mock.rx_interrupt = smp2p_remote_mock_rx_interrupt;
+	remote_mock.tx_interrupt = smp2p_remote_mock_tx_interrupt;
+	remote_mock.item_exists = false;
+	init_completion(&remote_mock.cb_completion);
+	remote_mock.initialized = true;
+
+	for (i = 0; i < SMP2P_NUM_PROCS; i++) {
+		INIT_WORK(&(remote_loopback[i].rmt_lpb_work),
+				smp2p_rmt_lpb_worker);
+		if (i == SMP2P_REMOTE_MOCK_PROC)
+			/* do not register loopback for remote mock proc */
+			continue;
+
+		msm_smp2p_init_rmt_lpb(&remote_loopback[i],
+			i, SMP2P_RLPB_ENTRY_NAME);
+	}
+	return 0;
+}
+module_init(smp2p_remote_mock_init);