soc: qcom: Add snapshot of SMP2P Driver

This snapshot is taken as of msm-4.4 'commit <d2afad6a903b>
("Merge "ext4 crypto: enable HW based encryption with ICE"")'.

In addition fix the coding style issues.

CRs-Fixed: 1079350
Change-Id: I09b4abe8d81d201b2ae023da2561c9711edef1aa
Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index ab28a2d..3dd1965 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -141,3 +141,5 @@
 obj-$(CONFIG_GPIO_ZYNQ)		+= gpio-zynq.o
 obj-$(CONFIG_GPIO_ZX)		+= gpio-zx.o
 obj-$(CONFIG_GPIO_LOONGSON1)	+= gpio-loongson1.o
+obj-$(CONFIG_MSM_SMP2P)		+= gpio-msm-smp2p.o
+obj-$(CONFIG_MSM_SMP2P_TEST)	+= gpio-msm-smp2p-test.o
diff --git a/drivers/gpio/gpio-msm-smp2p-test.c b/drivers/gpio/gpio-msm-smp2p-test.c
new file mode 100644
index 0000000..1067c4a
--- /dev/null
+++ b/drivers/gpio/gpio-msm-smp2p-test.c
@@ -0,0 +1,763 @@
+/* drivers/gpio/gpio-msm-smp2p-test.c
+ *
+ * Copyright (c) 2013-2016, 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/module.h>
+#include <linux/platform_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/gpio.h>
+#include <linux/debugfs.h>
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <linux/bitmap.h>
+#include "../soc/qcom/smp2p_private.h"
+#include "../soc/qcom/smp2p_test_common.h"
+
+/* Interrupt callback data */
+struct gpio_info {
+	int gpio_base_id;
+	int irq_base_id;
+
+	bool initialized;
+	struct completion cb_completion;
+	int cb_count;
+	DECLARE_BITMAP(triggered_irqs, SMP2P_BITS_PER_ENTRY);
+};
+
+/* GPIO Inbound/Outbound callback info */
+struct gpio_inout {
+	struct gpio_info in;
+	struct gpio_info out;
+};
+
+static struct gpio_inout gpio_info[SMP2P_NUM_PROCS];
+
+/**
+ * Init/reset the callback data.
+ *
+ * @info: Pointer to callback data
+ */
+static void cb_data_reset(struct gpio_info *info)
+{
+	int n;
+
+	if (!info)
+		return;
+
+	if (!info->initialized) {
+		init_completion(&info->cb_completion);
+		info->initialized = true;
+	}
+	info->cb_count = 0;
+
+	for (n = 0; n < SMP2P_BITS_PER_ENTRY; ++n)
+		clear_bit(n,  info->triggered_irqs);
+
+	reinit_completion(&info->cb_completion);
+}
+
+static int smp2p_gpio_test_probe(struct platform_device *pdev)
+{
+	int id;
+	int cnt;
+	struct device_node *node = pdev->dev.of_node;
+	struct gpio_info *gpio_info_ptr = NULL;
+
+	/*
+	 * NOTE:  This does a string-lookup of the GPIO pin name and doesn't
+	 * actually directly link to the SMP2P GPIO driver since all
+	 * GPIO/Interrupt access must be through standard
+	 * Linux GPIO / Interrupt APIs.
+	 */
+	if (strcmp("qcom,smp2pgpio_test_smp2p_1_in", node->name) == 0) {
+		gpio_info_ptr = &gpio_info[SMP2P_MODEM_PROC].in;
+	} else if (strcmp("qcom,smp2pgpio_test_smp2p_1_out", node->name) == 0) {
+		gpio_info_ptr = &gpio_info[SMP2P_MODEM_PROC].out;
+	} else if (strcmp("qcom,smp2pgpio_test_smp2p_2_in", node->name) == 0) {
+		gpio_info_ptr = &gpio_info[SMP2P_AUDIO_PROC].in;
+	} else if (strcmp("qcom,smp2pgpio_test_smp2p_2_out", node->name) == 0) {
+		gpio_info_ptr = &gpio_info[SMP2P_AUDIO_PROC].out;
+	} else if (strcmp("qcom,smp2pgpio_test_smp2p_3_in", node->name) == 0) {
+		gpio_info_ptr = &gpio_info[SMP2P_SENSOR_PROC].in;
+	} else if (strcmp("qcom,smp2pgpio_test_smp2p_3_out", node->name) == 0) {
+		gpio_info_ptr = &gpio_info[SMP2P_SENSOR_PROC].out;
+	} else if (strcmp("qcom,smp2pgpio_test_smp2p_4_in", node->name) == 0) {
+		gpio_info_ptr = &gpio_info[SMP2P_WIRELESS_PROC].in;
+	} else if (strcmp("qcom,smp2pgpio_test_smp2p_4_out", node->name) == 0) {
+		gpio_info_ptr = &gpio_info[SMP2P_WIRELESS_PROC].out;
+	} else if (strcmp("qcom,smp2pgpio_test_smp2p_5_in", node->name) == 0) {
+		gpio_info_ptr = &gpio_info[SMP2P_CDSP_PROC].in;
+	} else if (strcmp("qcom,smp2pgpio_test_smp2p_5_out", node->name) == 0) {
+		gpio_info_ptr = &gpio_info[SMP2P_CDSP_PROC].out;
+	} else if (strcmp("qcom,smp2pgpio_test_smp2p_7_in", node->name) == 0) {
+		gpio_info_ptr = &gpio_info[SMP2P_TZ_PROC].in;
+	} else if (strcmp("qcom,smp2pgpio_test_smp2p_7_out", node->name) == 0) {
+		gpio_info_ptr = &gpio_info[SMP2P_TZ_PROC].out;
+	} else if (strcmp("qcom,smp2pgpio_test_smp2p_15_in", node->name) == 0) {
+		gpio_info_ptr = &gpio_info[SMP2P_REMOTE_MOCK_PROC].in;
+	} else if (
+		strcmp("qcom,smp2pgpio_test_smp2p_15_out", node->name) == 0) {
+		gpio_info_ptr = &gpio_info[SMP2P_REMOTE_MOCK_PROC].out;
+	} else {
+		pr_err("%s: unable to match device type '%s'\n",
+				__func__, node->name);
+		return -ENODEV;
+	}
+
+	/* retrieve the GPIO and interrupt ID's */
+	cnt = of_gpio_count(node);
+	if (cnt && gpio_info_ptr) {
+		/*
+		 * Instead of looping through all 32-bits, we can just get the
+		 * first pin to get the base IDs.  This saves on the verbosity
+		 * of the device tree nodes as well.
+		 */
+		id = of_get_gpio(node, 0);
+		if (id == -EPROBE_DEFER)
+			return id;
+		gpio_info_ptr->gpio_base_id = id;
+		gpio_info_ptr->irq_base_id = gpio_to_irq(id);
+	}
+	return 0;
+}
+
+/*
+ * NOTE:  Instead of match table and device driver, you may be able to just
+ * call of_find_compatible_node() in your init function.
+ */
+static const struct of_device_id msm_smp2p_match_table[] = {
+	/* modem */
+	{.compatible = "qcom,smp2pgpio_test_smp2p_1_out", },
+	{.compatible = "qcom,smp2pgpio_test_smp2p_1_in", },
+
+	/* audio (adsp) */
+	{.compatible = "qcom,smp2pgpio_test_smp2p_2_out", },
+	{.compatible = "qcom,smp2pgpio_test_smp2p_2_in", },
+
+	/* sensor */
+	{.compatible = "qcom,smp2pgpio_test_smp2p_3_out", },
+	{.compatible = "qcom,smp2pgpio_test_smp2p_3_in", },
+
+	/* wcnss */
+	{.compatible = "qcom,smp2pgpio_test_smp2p_4_out", },
+	{.compatible = "qcom,smp2pgpio_test_smp2p_4_in", },
+
+	/* CDSP */
+	{.compatible = "qcom,smp2pgpio_test_smp2p_5_out", },
+	{.compatible = "qcom,smp2pgpio_test_smp2p_5_in", },
+
+	/* TZ */
+	{.compatible = "qcom,smp2pgpio_test_smp2p_7_out", },
+	{.compatible = "qcom,smp2pgpio_test_smp2p_7_in", },
+
+	/* mock loopback */
+	{.compatible = "qcom,smp2pgpio_test_smp2p_15_out", },
+	{.compatible = "qcom,smp2pgpio_test_smp2p_15_in", },
+	{},
+};
+
+static struct platform_driver smp2p_gpio_driver = {
+	.probe = smp2p_gpio_test_probe,
+	.driver = {
+		.name = "smp2pgpio_test",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_smp2p_match_table,
+	},
+};
+
+/**
+ * smp2p_ut_local_gpio_out - Verify outbound functionality.
+ *
+ * @s:   pointer to output file
+ */
+static void smp2p_ut_local_gpio_out(struct seq_file *s)
+{
+	int failed = 0;
+	struct gpio_info *cb_info = &gpio_info[SMP2P_REMOTE_MOCK_PROC].out;
+	int ret;
+	int id;
+	struct msm_smp2p_remote_mock *mock;
+
+	seq_printf(s, "Running %s\n", __func__);
+	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);
+		msm_smp2p_set_remote_mock_exists(true);
+		mock->tx_interrupt();
+
+		/* open GPIO entry */
+		smp2p_gpio_open_test_entry("smp2p",
+				SMP2P_REMOTE_MOCK_PROC, true);
+
+		/* verify set/get functions */
+		UT_ASSERT_INT(0, <, cb_info->gpio_base_id);
+		for (id = 0; id < SMP2P_BITS_PER_ENTRY && !failed; ++id) {
+			int pin = cb_info->gpio_base_id + id;
+
+			mock->rx_interrupt_count = 0;
+			gpio_set_value(pin, 1);
+			UT_ASSERT_INT(1, ==, mock->rx_interrupt_count);
+			UT_ASSERT_INT(1, ==, gpio_get_value(pin));
+
+			gpio_set_value(pin, 0);
+			UT_ASSERT_INT(2, ==, mock->rx_interrupt_count);
+			UT_ASSERT_INT(0, ==, gpio_get_value(pin));
+		}
+		if (failed)
+			break;
+
+		seq_puts(s, "\tOK\n");
+	} while (0);
+
+	if (failed) {
+		pr_err("%s: Failed\n", __func__);
+		seq_puts(s, "\tFailed\n");
+	}
+
+	smp2p_gpio_open_test_entry("smp2p",
+			SMP2P_REMOTE_MOCK_PROC, false);
+}
+
+/**
+ * smp2p_gpio_irq - Interrupt handler for inbound entries.
+ *
+ * @irq:         Virtual IRQ being triggered
+ * @data:        Cookie data (struct gpio_info * in this case)
+ * @returns:     Number of bytes written
+ */
+static irqreturn_t smp2p_gpio_irq(int irq, void *data)
+{
+	struct gpio_info *gpio_ptr = (struct gpio_info *)data;
+	int offset;
+
+	if (!gpio_ptr) {
+		pr_err("%s: gpio_ptr is NULL for irq %d\n", __func__, irq);
+		return IRQ_HANDLED;
+	}
+
+	offset = irq - gpio_ptr->irq_base_id;
+	if (offset >= 0 &&  offset < SMP2P_BITS_PER_ENTRY)
+		set_bit(offset, gpio_ptr->triggered_irqs);
+	else
+		pr_err("%s: invalid irq offset base %d; irq %d\n",
+			__func__, gpio_ptr->irq_base_id, irq);
+
+	++gpio_ptr->cb_count;
+	complete(&gpio_ptr->cb_completion);
+	return IRQ_HANDLED;
+}
+
+/**
+ * smp2p_ut_local_gpio_in - Verify inbound functionality.
+ *
+ * @s:   pointer to output file
+ */
+static void smp2p_ut_local_gpio_in(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);
+		msm_smp2p_set_remote_mock_exists(true);
+		mock->tx_interrupt();
+
+		smp2p_gpio_open_test_entry("smp2p",
+				SMP2P_REMOTE_MOCK_PROC, true);
+
+		/* verify set/get functions locally */
+		UT_ASSERT_INT(0, <, cb_info->gpio_base_id);
+		for (id = 0; id < SMP2P_BITS_PER_ENTRY && !failed; ++id) {
+			int pin;
+			int current_value;
+
+			/* verify pin value cannot be set */
+			pin = cb_info->gpio_base_id + id;
+			current_value = gpio_get_value(pin);
+
+			gpio_set_value(pin, 0);
+			UT_ASSERT_INT(current_value, ==, gpio_get_value(pin));
+			gpio_set_value(pin, 1);
+			UT_ASSERT_INT(current_value, ==, gpio_get_value(pin));
+
+			/* verify no interrupts */
+			UT_ASSERT_INT(0, ==, cb_info->cb_count);
+		}
+		if (failed)
+			break;
+
+		/* register for interrupts */
+		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_PTR(NULL, !=, irq_to_desc(virq));
+			ret = request_irq(virq,
+					smp2p_gpio_irq,	IRQF_TRIGGER_RISING,
+					"smp2p_test", cb_info);
+			UT_ASSERT_INT(0, ==, ret);
+		}
+		if (failed)
+			break;
+
+		/* verify both rising and falling edge interrupts */
+		for (id = 0; id < SMP2P_BITS_PER_ENTRY && !failed; ++id) {
+			virq = cb_info->irq_base_id + id;
+			irq_set_irq_type(virq, IRQ_TYPE_EDGE_BOTH);
+			cb_data_reset(cb_info);
+
+			/* verify rising-edge interrupt */
+			mock->remote_item.entries[0].entry = 1 << id;
+			mock->tx_interrupt();
+			UT_ASSERT_INT(cb_info->cb_count, ==, 1);
+			UT_ASSERT_INT(0, <,
+				test_bit(id, cb_info->triggered_irqs));
+			test_bit(id, cb_info->triggered_irqs);
+
+			/* verify falling-edge interrupt */
+			mock->remote_item.entries[0].entry = 0;
+			mock->tx_interrupt();
+			UT_ASSERT_INT(cb_info->cb_count, ==, 2);
+			UT_ASSERT_INT(0, <,
+					test_bit(id, cb_info->triggered_irqs));
+		}
+		if (failed)
+			break;
+
+		/* verify rising-edge interrupts */
+		for (id = 0; id < SMP2P_BITS_PER_ENTRY && !failed; ++id) {
+			virq = cb_info->irq_base_id + id;
+			irq_set_irq_type(virq, IRQ_TYPE_EDGE_RISING);
+			cb_data_reset(cb_info);
+
+			/* verify only rising-edge interrupt is triggered */
+			mock->remote_item.entries[0].entry = 1 << id;
+			mock->tx_interrupt();
+			UT_ASSERT_INT(cb_info->cb_count, ==, 1);
+			UT_ASSERT_INT(0, <,
+				test_bit(id, cb_info->triggered_irqs));
+			test_bit(id, cb_info->triggered_irqs);
+
+			mock->remote_item.entries[0].entry = 0;
+			mock->tx_interrupt();
+			UT_ASSERT_INT(cb_info->cb_count, ==, 1);
+			UT_ASSERT_INT(0, <,
+				test_bit(id, cb_info->triggered_irqs));
+		}
+		if (failed)
+			break;
+
+		/* verify falling-edge interrupts */
+		for (id = 0; id < SMP2P_BITS_PER_ENTRY && !failed; ++id) {
+			virq = cb_info->irq_base_id + id;
+			irq_set_irq_type(virq, IRQ_TYPE_EDGE_FALLING);
+			cb_data_reset(cb_info);
+
+			/* verify only rising-edge interrupt is triggered */
+			mock->remote_item.entries[0].entry = 1 << id;
+			mock->tx_interrupt();
+			UT_ASSERT_INT(cb_info->cb_count, ==, 0);
+			UT_ASSERT_INT(0, ==,
+				test_bit(id, cb_info->triggered_irqs));
+
+			mock->remote_item.entries[0].entry = 0;
+			mock->tx_interrupt();
+			UT_ASSERT_INT(cb_info->cb_count, ==, 1);
+			UT_ASSERT_INT(0, <,
+				test_bit(id, cb_info->triggered_irqs));
+		}
+		if (failed)
+			break;
+
+		seq_puts(s, "\tOK\n");
+	} while (0);
+
+	if (failed) {
+		pr_err("%s: Failed\n", __func__);
+		seq_puts(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_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_PTR(NULL, !=, 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, ",
+						__func__, __LINE__, id);
+					seq_puts(s, "expected set\n");
+					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, ",
+						__func__, __LINE__, id);
+					seq_puts(s, "expected clear\n");
+					failed = 1;
+					break;
+				}
+			}
+		}
+		if (failed)
+			break;
+
+		seq_puts(s, "\tOK\n");
+	} while (0);
+
+	if (failed) {
+		pr_err("%s: Failed\n", __func__);
+		seq_puts(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
+ * @mask: 1 = write gpio_value to this GPIO pin
+ * @gpio_value: value to write to GPIO pin
+ */
+static void smp2p_gpio_write_bits(struct gpio_info *gpio, uint32_t mask,
+	int gpio_value)
+{
+	int n;
+
+	for (n = 0; n < SMP2P_BITS_PER_ENTRY; ++n) {
+		if (mask & 0x1)
+			gpio_set_value(gpio->gpio_base_id + n, gpio_value);
+		mask >>= 1;
+	}
+}
+
+static void smp2p_gpio_set_bits(struct gpio_info *gpio, uint32_t mask)
+{
+	smp2p_gpio_write_bits(gpio, mask, 1);
+}
+
+static void smp2p_gpio_clr_bits(struct gpio_info *gpio, uint32_t mask)
+{
+	smp2p_gpio_write_bits(gpio, mask, 0);
+}
+
+/**
+ * smp2p_gpio_get_value - reads entire 32-bits of GPIO
+ *
+ * @gpio: gpio structure
+ * @returns: 32 bit value of GPIO pins
+ */
+static uint32_t smp2p_gpio_get_value(struct gpio_info *gpio)
+{
+	int n;
+	uint32_t value = 0;
+
+	for (n = 0; n < SMP2P_BITS_PER_ENTRY; ++n) {
+		if (gpio_get_value(gpio->gpio_base_id + n))
+			value |= 1 << n;
+	}
+	return value;
+}
+
+/**
+ * smp2p_ut_remote_inout_core - Verify inbound/outbound functionality.
+ *
+ * @s:   pointer to output file
+ * @remote_pid:  Remote processor to test
+ * @name:        Name of the test for reporting
+ *
+ * This test verifies inbound/outbound functionality for the remote processor.
+ */
+static void smp2p_ut_remote_inout_core(struct seq_file *s, int remote_pid,
+		const char *name)
+{
+	int failed = 0;
+	uint32_t request;
+	uint32_t response;
+	struct gpio_info *cb_in;
+	struct gpio_info *cb_out;
+	int id;
+	int ret;
+
+	seq_printf(s, "Running %s for '%s' remote pid %d\n",
+		   __func__, smp2p_pid_to_name(remote_pid), remote_pid);
+
+	cb_in = &gpio_info[remote_pid].in;
+	cb_out = &gpio_info[remote_pid].out;
+	cb_data_reset(cb_in);
+	cb_data_reset(cb_out);
+	do {
+		/* open test entries */
+		msm_smp2p_deinit_rmt_lpb_proc(remote_pid);
+		smp2p_gpio_open_test_entry("smp2p", remote_pid, true);
+
+		/* register for interrupts */
+		UT_ASSERT_INT(0, <, cb_in->gpio_base_id);
+		UT_ASSERT_INT(0, <, cb_in->irq_base_id);
+		for (id = 0; id < SMP2P_BITS_PER_ENTRY && !failed; ++id) {
+			int virq = cb_in->irq_base_id + id;
+
+			UT_ASSERT_PTR(NULL, !=, irq_to_desc(virq));
+			ret = request_irq(virq,
+				smp2p_gpio_irq,
+				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+				"smp2p_test", cb_in);
+			UT_ASSERT_INT(0, ==, ret);
+		}
+		if (failed)
+			break;
+
+		/* write echo of data value 0 */
+		UT_ASSERT_INT(0, <, cb_out->gpio_base_id);
+		request = 0x0;
+		SMP2P_SET_RMT_CMD_TYPE(request, 1);
+		SMP2P_SET_RMT_CMD(request, SMP2P_LB_CMD_ECHO);
+		SMP2P_SET_RMT_DATA(request, 0x0);
+
+		smp2p_gpio_set_bits(cb_out, SMP2P_RMT_IGNORE_MASK);
+		smp2p_gpio_clr_bits(cb_out, ~SMP2P_RMT_IGNORE_MASK);
+		smp2p_gpio_set_bits(cb_out, request);
+
+		UT_ASSERT_INT(cb_in->cb_count, ==, 0);
+		smp2p_gpio_clr_bits(cb_out, SMP2P_RMT_IGNORE_MASK);
+
+		/* verify response */
+		do {
+			/* wait for up to 32 changes */
+			if (wait_for_completion_timeout(
+					&cb_in->cb_completion, HZ / 2) == 0)
+				break;
+			reinit_completion(&cb_in->cb_completion);
+		} while (cb_in->cb_count < 32);
+		UT_ASSERT_INT(cb_in->cb_count, >, 0);
+		response = smp2p_gpio_get_value(cb_in);
+		SMP2P_SET_RMT_CMD_TYPE(request, 0);
+		UT_ASSERT_HEX(request, ==, response);
+
+		/* write echo of data value of all 1's */
+		request = 0x0;
+		SMP2P_SET_RMT_CMD_TYPE(request, 1);
+		SMP2P_SET_RMT_CMD(request, SMP2P_LB_CMD_ECHO);
+		SMP2P_SET_RMT_DATA(request, ~0);
+
+		smp2p_gpio_set_bits(cb_out, SMP2P_RMT_IGNORE_MASK);
+		cb_data_reset(cb_in);
+		smp2p_gpio_clr_bits(cb_out, ~SMP2P_RMT_IGNORE_MASK);
+		smp2p_gpio_set_bits(cb_out, request);
+
+		UT_ASSERT_INT(cb_in->cb_count, ==, 0);
+		smp2p_gpio_clr_bits(cb_out, SMP2P_RMT_IGNORE_MASK);
+
+		/* verify response including 24 interrupts */
+		do {
+			UT_ASSERT_INT(
+				(int)wait_for_completion_timeout(
+					&cb_in->cb_completion, HZ / 2),
+			   >, 0);
+			reinit_completion(&cb_in->cb_completion);
+		} while (cb_in->cb_count < 24);
+		response = smp2p_gpio_get_value(cb_in);
+		SMP2P_SET_RMT_CMD_TYPE(request, 0);
+		UT_ASSERT_HEX(request, ==, response);
+		UT_ASSERT_INT(24, ==, cb_in->cb_count);
+
+		seq_puts(s, "\tOK\n");
+	} while (0);
+
+	if (failed) {
+		pr_err("%s: Failed\n", name);
+		seq_puts(s, "\tFailed\n");
+	}
+
+	/* unregister for interrupts */
+	if (cb_in->irq_base_id) {
+		for (id = 0; id < SMP2P_BITS_PER_ENTRY; ++id)
+			free_irq(cb_in->irq_base_id + id, cb_in);
+	}
+
+	smp2p_gpio_open_test_entry("smp2p",	remote_pid, false);
+	msm_smp2p_init_rmt_lpb_proc(remote_pid);
+}
+
+/**
+ * smp2p_ut_remote_inout - Verify inbound/outbound functionality for all.
+ *
+ * @s:   pointer to output file
+ *
+ * This test verifies inbound and outbound functionality for all
+ * configured remote processor.
+ */
+static void smp2p_ut_remote_inout(struct seq_file *s)
+{
+	struct smp2p_interrupt_config *int_cfg;
+	int pid;
+
+	int_cfg = smp2p_get_interrupt_config();
+	if (!int_cfg) {
+		seq_puts(s, "Remote processor config unavailable\n");
+		return;
+	}
+
+	for (pid = 0; pid < SMP2P_NUM_PROCS; ++pid) {
+		if (!int_cfg[pid].is_configured)
+			continue;
+
+		smp2p_ut_remote_inout_core(s, pid, __func__);
+	}
+}
+
+static int __init smp2p_debugfs_init(void)
+{
+	/* register GPIO pins */
+	(void)platform_driver_register(&smp2p_gpio_driver);
+
+	/*
+	 * Add Unit Test entries.
+	 *
+	 * The idea with unit tests is that you can run all of them
+	 * from ADB shell by doing:
+	 *  adb shell
+	 *  cat ut*
+	 *
+	 * And if particular tests fail, you can then repeatedly run the
+	 * failing tests as you debug and resolve the failing test.
+	 */
+	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;
+}
+late_initcall(smp2p_debugfs_init);
diff --git a/drivers/gpio/gpio-msm-smp2p.c b/drivers/gpio/gpio-msm-smp2p.c
new file mode 100644
index 0000000..8f132fd
--- /dev/null
+++ b/drivers/gpio/gpio-msm-smp2p.c
@@ -0,0 +1,836 @@
+/* drivers/gpio/gpio-msm-smp2p.c
+ *
+ * Copyright (c) 2013-2014, 2016 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/module.h>
+#include <linux/platform_device.h>
+#include <linux/bitmap.h>
+#include <linux/of.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/ipc_logging.h>
+#include "../soc/qcom/smp2p_private_api.h"
+#include "../soc/qcom/smp2p_private.h"
+
+/* GPIO device - one per SMP2P entry. */
+struct smp2p_chip_dev {
+	struct list_head entry_list;
+	char name[SMP2P_MAX_ENTRY_NAME];
+	int remote_pid;
+	bool is_inbound;
+	bool is_open;
+	bool in_shadow;
+	uint32_t shadow_value;
+	struct work_struct shadow_work;
+	spinlock_t shadow_lock;
+	struct notifier_block out_notifier;
+	struct notifier_block in_notifier;
+	struct msm_smp2p_out *out_handle;
+
+	struct gpio_chip gpio;
+	struct irq_domain *irq_domain;
+	int irq_base;
+
+	spinlock_t irq_lock;
+	DECLARE_BITMAP(irq_enabled, SMP2P_BITS_PER_ENTRY);
+	DECLARE_BITMAP(irq_rising_edge, SMP2P_BITS_PER_ENTRY);
+	DECLARE_BITMAP(irq_falling_edge, SMP2P_BITS_PER_ENTRY);
+};
+
+static struct platform_driver smp2p_gpio_driver;
+static struct lock_class_key smp2p_gpio_lock_class;
+static struct irq_chip smp2p_gpio_irq_chip;
+static DEFINE_SPINLOCK(smp2p_entry_lock_lha1);
+static LIST_HEAD(smp2p_entry_list);
+
+/* Used for mapping edge to name for logging. */
+static const char * const edge_names[] = {
+	"-",
+	"0->1",
+	"1->0",
+	"-",
+};
+
+/* Used for mapping edge to value for logging. */
+static const char * const edge_name_rising[] = {
+	"-",
+	"0->1",
+};
+
+/* Used for mapping edge to value for logging. */
+static const char * const edge_name_falling[] = {
+	"-",
+	"1->0",
+};
+
+static int smp2p_gpio_to_irq(struct gpio_chip *cp,
+	unsigned int offset);
+
+/**
+ * smp2p_get_value - Retrieves GPIO value.
+ *
+ * @cp:      GPIO chip pointer
+ * @offset:  Pin offset
+ * @returns: >=0: value of GPIO Pin; < 0 for error
+ *
+ * Error codes:
+ *   -ENODEV - chip/entry invalid
+ *   -ENETDOWN - valid entry, but entry not yet created
+ */
+static int smp2p_get_value(struct gpio_chip *cp,
+	unsigned int offset)
+{
+	struct smp2p_chip_dev *chip;
+	int ret = 0;
+	uint32_t data;
+
+	if (!cp)
+		return -ENODEV;
+
+	chip = container_of(cp, struct smp2p_chip_dev, gpio);
+	if (!chip->is_open)
+		return -ENETDOWN;
+
+	if (chip->is_inbound)
+		ret = msm_smp2p_in_read(chip->remote_pid, chip->name, &data);
+	else
+		ret = msm_smp2p_out_read(chip->out_handle, &data);
+
+	if (!ret)
+		ret = (data & (1 << offset)) ? 1 : 0;
+
+	return ret;
+}
+
+/**
+ * smp2p_set_value - Sets GPIO value.
+ *
+ * @cp:     GPIO chip pointer
+ * @offset: Pin offset
+ * @value:  New value
+ */
+static void smp2p_set_value(struct gpio_chip *cp, unsigned int offset,
+			    int value)
+{
+	struct smp2p_chip_dev *chip;
+	uint32_t data_set;
+	uint32_t data_clear;
+	bool send_irq;
+	int ret;
+	unsigned long flags;
+
+	if (!cp)
+		return;
+
+	chip = container_of(cp, struct smp2p_chip_dev, gpio);
+
+	if (chip->is_inbound) {
+		SMP2P_INFO("%s: '%s':%d virq %d invalid operation\n",
+			__func__, chip->name, chip->remote_pid,
+			chip->irq_base + offset);
+		return;
+	}
+
+	if (value & SMP2P_GPIO_NO_INT) {
+		value &= ~SMP2P_GPIO_NO_INT;
+		send_irq = false;
+	} else {
+		send_irq = true;
+	}
+
+	if (value) {
+		data_set = 1 << offset;
+		data_clear = 0;
+	} else {
+		data_set = 0;
+		data_clear = 1 << offset;
+	}
+
+	spin_lock_irqsave(&chip->shadow_lock, flags);
+	if (!chip->is_open) {
+		chip->in_shadow = true;
+		chip->shadow_value &= ~data_clear;
+		chip->shadow_value |= data_set;
+		spin_unlock_irqrestore(&chip->shadow_lock, flags);
+		return;
+	}
+
+	if (chip->in_shadow) {
+		chip->in_shadow = false;
+		chip->shadow_value &= ~data_clear;
+		chip->shadow_value |= data_set;
+		ret = msm_smp2p_out_modify(chip->out_handle,
+				chip->shadow_value, 0x0, send_irq);
+		chip->shadow_value = 0x0;
+	} else {
+		ret = msm_smp2p_out_modify(chip->out_handle,
+				data_set, data_clear, send_irq);
+	}
+	spin_unlock_irqrestore(&chip->shadow_lock, flags);
+
+	if (ret)
+		SMP2P_GPIO("'%s':%d gpio %d set to %d failed (%d)\n",
+			chip->name, chip->remote_pid,
+			chip->gpio.base + offset, value, ret);
+	else
+		SMP2P_GPIO("'%s':%d gpio %d set to %d\n",
+			chip->name, chip->remote_pid,
+			chip->gpio.base + offset, value);
+}
+
+/**
+ * smp2p_direction_input - Sets GPIO direction to input.
+ *
+ * @cp:      GPIO chip pointer
+ * @offset:  Pin offset
+ * @returns: 0 for success; < 0 for failure
+ */
+static int smp2p_direction_input(struct gpio_chip *cp, unsigned int offset)
+{
+	struct smp2p_chip_dev *chip;
+
+	if (!cp)
+		return -ENODEV;
+
+	chip = container_of(cp, struct smp2p_chip_dev, gpio);
+	if (!chip->is_inbound)
+		return -EPERM;
+
+	return 0;
+}
+
+/**
+ * smp2p_direction_output - Sets GPIO direction to output.
+ *
+ * @cp:      GPIO chip pointer
+ * @offset:  Pin offset
+ * @value:   Direction
+ * @returns: 0 for success; < 0 for failure
+ */
+static int smp2p_direction_output(struct gpio_chip *cp,
+	unsigned int offset, int value)
+{
+	struct smp2p_chip_dev *chip;
+
+	if (!cp)
+		return -ENODEV;
+
+	chip = container_of(cp, struct smp2p_chip_dev, gpio);
+	if (chip->is_inbound)
+		return -EPERM;
+
+	return 0;
+}
+
+/**
+ * smp2p_gpio_to_irq - Convert GPIO pin to virtual IRQ pin.
+ *
+ * @cp:      GPIO chip pointer
+ * @offset:  Pin offset
+ * @returns: >0 for virtual irq value; < 0 for failure
+ */
+static int smp2p_gpio_to_irq(struct gpio_chip *cp, unsigned int offset)
+{
+	struct smp2p_chip_dev *chip;
+
+	chip = container_of(cp, struct smp2p_chip_dev, gpio);
+	if (!cp || chip->irq_base <= 0)
+		return -ENODEV;
+
+	return chip->irq_base + offset;
+}
+
+/**
+ * smp2p_gpio_irq_mask_helper - Mask/Unmask interrupt.
+ *
+ * @d:    IRQ data
+ * @mask: true to mask (disable), false to unmask (enable)
+ */
+static void smp2p_gpio_irq_mask_helper(struct irq_data *d, bool mask)
+{
+	struct smp2p_chip_dev *chip;
+	int offset;
+	unsigned long flags;
+
+	chip = (struct smp2p_chip_dev *)irq_get_chip_data(d->irq);
+	if (!chip || chip->irq_base <= 0)
+		return;
+
+	offset = d->irq - chip->irq_base;
+	spin_lock_irqsave(&chip->irq_lock, flags);
+	if (mask)
+		clear_bit(offset, chip->irq_enabled);
+	else
+		set_bit(offset, chip->irq_enabled);
+	spin_unlock_irqrestore(&chip->irq_lock, flags);
+}
+
+/**
+ * smp2p_gpio_irq_mask - Mask interrupt.
+ *
+ * @d: IRQ data
+ */
+static void smp2p_gpio_irq_mask(struct irq_data *d)
+{
+	smp2p_gpio_irq_mask_helper(d, true);
+}
+
+/**
+ * smp2p_gpio_irq_unmask - Unmask interrupt.
+ *
+ * @d: IRQ data
+ */
+static void smp2p_gpio_irq_unmask(struct irq_data *d)
+{
+	smp2p_gpio_irq_mask_helper(d, false);
+}
+
+/**
+ * smp2p_gpio_irq_set_type - Set interrupt edge type.
+ *
+ * @d:      IRQ data
+ * @type:   Edge type for interrupt
+ * @returns 0 for success; < 0 for failure
+ */
+static int smp2p_gpio_irq_set_type(struct irq_data *d, unsigned int type)
+{
+	struct smp2p_chip_dev *chip;
+	int offset;
+	unsigned long flags;
+	int ret = 0;
+
+	chip = (struct smp2p_chip_dev *)irq_get_chip_data(d->irq);
+	if (!chip)
+		return -ENODEV;
+
+	if (chip->irq_base <= 0) {
+		SMP2P_ERR("%s: '%s':%d virqbase %d invalid\n",
+			__func__, chip->name, chip->remote_pid,
+			chip->irq_base);
+		return -ENODEV;
+	}
+
+	offset = d->irq - chip->irq_base;
+
+	spin_lock_irqsave(&chip->irq_lock, flags);
+	clear_bit(offset, chip->irq_rising_edge);
+	clear_bit(offset, chip->irq_falling_edge);
+	switch (type) {
+	case IRQ_TYPE_EDGE_RISING:
+		set_bit(offset, chip->irq_rising_edge);
+		break;
+
+	case IRQ_TYPE_EDGE_FALLING:
+		set_bit(offset, chip->irq_falling_edge);
+		break;
+
+	case IRQ_TYPE_NONE:
+	case IRQ_TYPE_DEFAULT:
+	case IRQ_TYPE_EDGE_BOTH:
+		set_bit(offset, chip->irq_rising_edge);
+		set_bit(offset, chip->irq_falling_edge);
+		break;
+
+	default:
+		SMP2P_ERR("%s: unsupported interrupt type 0x%x\n",
+				__func__, type);
+		ret = -EINVAL;
+		break;
+	}
+	spin_unlock_irqrestore(&chip->irq_lock, flags);
+	return ret;
+}
+
+/**
+ * smp2p_irq_map - Creates or updates binding of virtual IRQ
+ *
+ * @domain_ptr: Interrupt domain pointer
+ * @virq:       Virtual IRQ
+ * @hw:         Hardware IRQ (same as virq for nomap)
+ * @returns:    0 for success
+ */
+static int smp2p_irq_map(struct irq_domain *domain_ptr, unsigned int virq,
+	irq_hw_number_t hw)
+{
+	struct smp2p_chip_dev *chip;
+
+	chip = domain_ptr->host_data;
+	if (!chip) {
+		SMP2P_ERR("%s: invalid domain ptr\n", __func__);
+		return -ENODEV;
+	}
+
+	/* map chip structures to device */
+	irq_set_lockdep_class(virq, &smp2p_gpio_lock_class);
+	irq_set_chip_and_handler(virq, &smp2p_gpio_irq_chip,
+				 handle_level_irq);
+	irq_set_chip_data(virq, chip);
+
+	return 0;
+}
+
+static struct irq_chip smp2p_gpio_irq_chip = {
+	.name = "smp2p_gpio",
+	.irq_mask = smp2p_gpio_irq_mask,
+	.irq_unmask = smp2p_gpio_irq_unmask,
+	.irq_set_type = smp2p_gpio_irq_set_type,
+};
+
+/* No-map interrupt Domain */
+static const struct irq_domain_ops smp2p_irq_domain_ops = {
+	.map = smp2p_irq_map,
+};
+
+/**
+ * msm_summary_irq_handler - Handles inbound entry change notification.
+ *
+ * @chip:  GPIO chip pointer
+ * @entry: Change notification data
+ *
+ * Whenever an entry changes, this callback is triggered to determine
+ * which bits changed and if the corresponding interrupts need to be
+ * triggered.
+ */
+static void msm_summary_irq_handler(struct smp2p_chip_dev *chip,
+	struct msm_smp2p_update_notif *entry)
+{
+	int i;
+	uint32_t cur_val;
+	uint32_t prev_val;
+	uint32_t edge;
+	unsigned long flags;
+	bool trigger_interrupt;
+	bool irq_rising;
+	bool irq_falling;
+
+	cur_val = entry->current_value;
+	prev_val = entry->previous_value;
+
+	if (chip->irq_base <= 0)
+		return;
+
+	SMP2P_GPIO("'%s':%d GPIO Summary IRQ Change %08x->%08x\n",
+			chip->name, chip->remote_pid, prev_val, cur_val);
+
+	for (i = 0; i < SMP2P_BITS_PER_ENTRY; ++i) {
+		spin_lock_irqsave(&chip->irq_lock, flags);
+		trigger_interrupt = false;
+		edge = (prev_val & 0x1) << 1 | (cur_val & 0x1);
+		irq_rising = test_bit(i, chip->irq_rising_edge);
+		irq_falling = test_bit(i, chip->irq_falling_edge);
+
+		if (test_bit(i, chip->irq_enabled)) {
+			if (edge == 0x1 && irq_rising)
+				/* 0->1 transition */
+				trigger_interrupt = true;
+			else if (edge == 0x2 && irq_falling)
+				/* 1->0 transition */
+				trigger_interrupt = true;
+		} else {
+			SMP2P_GPIO(
+				"'%s':%d GPIO bit %d virq %d (%s,%s) - edge %s disabled\n",
+				chip->name, chip->remote_pid, i,
+				chip->irq_base + i,
+				edge_name_rising[irq_rising],
+				edge_name_falling[irq_falling],
+				edge_names[edge]);
+		}
+		spin_unlock_irqrestore(&chip->irq_lock, flags);
+
+		if (trigger_interrupt) {
+			SMP2P_INFO(
+				"'%s':%d GPIO bit %d virq %d (%s,%s) - edge %s triggering\n",
+				chip->name, chip->remote_pid, i,
+				chip->irq_base + i,
+				edge_name_rising[irq_rising],
+				edge_name_falling[irq_falling],
+				edge_names[edge]);
+			(void)generic_handle_irq(chip->irq_base + i);
+		}
+
+		cur_val >>= 1;
+		prev_val >>= 1;
+	}
+}
+
+/**
+ * Adds an interrupt domain based upon the DT node.
+ *
+ * @chip: pointer to GPIO chip
+ * @node: pointer to Device Tree node
+ */
+static void smp2p_add_irq_domain(struct smp2p_chip_dev *chip,
+	struct device_node *node)
+{
+	int irq_base;
+
+	/* map GPIO pins to interrupts */
+	chip->irq_domain = irq_domain_add_linear(node, SMP2P_BITS_PER_ENTRY,
+			&smp2p_irq_domain_ops, chip);
+	if (!chip->irq_domain) {
+		SMP2P_ERR("%s: unable to create interrupt domain '%s':%d\n",
+				__func__, chip->name, chip->remote_pid);
+		goto domain_fail;
+	}
+
+	/* alloc a contiguous set of virt irqs from anywhere in the irq space */
+	irq_base = irq_alloc_descs_from(0, SMP2P_BITS_PER_ENTRY, of_node_to_nid(
+				irq_domain_get_of_node(chip->irq_domain)));
+	if (irq_base < 0) {
+		SMP2P_ERR("alloc virt irqs failed:%d name:%s pid%d\n", irq_base,
+						chip->name, chip->remote_pid);
+		goto irq_alloc_fail;
+	}
+
+	/* map the allocated irqs to gpios */
+	irq_domain_associate_many(chip->irq_domain, irq_base, 0,
+				  SMP2P_BITS_PER_ENTRY);
+
+	chip->irq_base = irq_base;
+	SMP2P_DBG("create mapping:%d naem:%s pid:%d\n", chip->irq_base,
+						chip->name, chip->remote_pid);
+	return;
+
+irq_alloc_fail:
+	irq_domain_remove(chip->irq_domain);
+domain_fail:
+	return;
+}
+
+/**
+ * Notifier function passed into smp2p API for out bound entries.
+ *
+ * @self:       Pointer to calling notifier block
+ * @event:	    Event
+ * @data:       Event-specific data
+ * @returns:    0
+ */
+static int smp2p_gpio_out_notify(struct notifier_block *self,
+		unsigned long event, void *data)
+{
+	struct smp2p_chip_dev *chip;
+
+	chip = container_of(self, struct smp2p_chip_dev, out_notifier);
+
+	switch (event) {
+	case SMP2P_OPEN:
+		chip->is_open = 1;
+		SMP2P_GPIO("%s: Opened out '%s':%d in_shadow[%d]\n", __func__,
+				chip->name, chip->remote_pid, chip->in_shadow);
+		if (chip->in_shadow)
+			schedule_work(&chip->shadow_work);
+		break;
+	case SMP2P_ENTRY_UPDATE:
+		break;
+	default:
+		SMP2P_ERR("%s: Unknown event\n", __func__);
+		break;
+	}
+	return 0;
+}
+
+/**
+ * Notifier function passed into smp2p API for in bound entries.
+ *
+ * @self:       Pointer to calling notifier block
+ * @event:	    Event
+ * @data:       Event-specific data
+ * @returns:    0
+ */
+static int smp2p_gpio_in_notify(struct notifier_block *self,
+		unsigned long event, void *data)
+{
+	struct smp2p_chip_dev *chip;
+
+	chip = container_of(self, struct smp2p_chip_dev, in_notifier);
+
+	switch (event) {
+	case SMP2P_OPEN:
+		chip->is_open = 1;
+		SMP2P_GPIO("%s: Opened in '%s':%d\n", __func__,
+				chip->name, chip->remote_pid);
+		break;
+	case SMP2P_ENTRY_UPDATE:
+		msm_summary_irq_handler(chip, data);
+		break;
+	default:
+		SMP2P_ERR("%s: Unknown event\n", __func__);
+		break;
+	}
+	return 0;
+}
+
+/**
+ * smp2p_gpio_shadow_worker - Handles shadow updates of an entry.
+ *
+ * @work: Work Item scheduled to handle the shadow updates.
+ */
+static void smp2p_gpio_shadow_worker(struct work_struct *work)
+{
+	struct smp2p_chip_dev *chip;
+	int ret;
+	unsigned long flags;
+
+	chip = container_of(work, struct smp2p_chip_dev, shadow_work);
+	spin_lock_irqsave(&chip->shadow_lock, flags);
+	if (chip->in_shadow) {
+		ret = msm_smp2p_out_modify(chip->out_handle,
+					chip->shadow_value, 0x0, true);
+
+		if (ret)
+			SMP2P_GPIO("'%s':%d shadow val[0x%x] failed(%d)\n",
+					chip->name, chip->remote_pid,
+					chip->shadow_value, ret);
+		else
+			SMP2P_GPIO("'%s':%d shadow val[0x%x]\n",
+					chip->name, chip->remote_pid,
+					chip->shadow_value);
+		chip->shadow_value = 0;
+		chip->in_shadow = false;
+	}
+	spin_unlock_irqrestore(&chip->shadow_lock, flags);
+}
+
+/**
+ * Device tree probe function.
+ *
+ * @pdev:	 Pointer to device tree data.
+ * @returns: 0 on success; -ENODEV otherwise
+ *
+ * Called for each smp2pgpio entry in the device tree.
+ */
+static int smp2p_gpio_probe(struct platform_device *pdev)
+{
+	struct device_node *node;
+	char *key;
+	struct smp2p_chip_dev *chip;
+	const char *name_tmp;
+	unsigned long flags;
+	bool is_test_entry = false;
+	int ret;
+
+	chip = kzalloc(sizeof(struct smp2p_chip_dev), GFP_KERNEL);
+	if (!chip) {
+		SMP2P_ERR("%s: out of memory\n", __func__);
+		ret = -ENOMEM;
+		goto fail;
+	}
+	spin_lock_init(&chip->irq_lock);
+	spin_lock_init(&chip->shadow_lock);
+	INIT_WORK(&chip->shadow_work, smp2p_gpio_shadow_worker);
+
+	/* parse device tree */
+	node = pdev->dev.of_node;
+	key = "qcom,entry-name";
+	ret = of_property_read_string(node, key, &name_tmp);
+	if (ret) {
+		SMP2P_ERR("%s: missing DT key '%s'\n", __func__, key);
+		goto fail;
+	}
+	strlcpy(chip->name, name_tmp, sizeof(chip->name));
+
+	key = "qcom,remote-pid";
+	ret = of_property_read_u32(node, key, &chip->remote_pid);
+	if (ret) {
+		SMP2P_ERR("%s: missing DT key '%s'\n", __func__, key);
+		goto fail;
+	}
+
+	key = "qcom,is-inbound";
+	chip->is_inbound = of_property_read_bool(node, key);
+
+	/* create virtual GPIO controller */
+	chip->gpio.label = chip->name;
+	chip->gpio.parent = &pdev->dev;
+	chip->gpio.owner = THIS_MODULE;
+	chip->gpio.direction_input	= smp2p_direction_input,
+	chip->gpio.get = smp2p_get_value;
+	chip->gpio.direction_output = smp2p_direction_output,
+	chip->gpio.set = smp2p_set_value;
+	chip->gpio.to_irq = smp2p_gpio_to_irq,
+	chip->gpio.base = -1;	/* use dynamic GPIO pin allocation */
+	chip->gpio.ngpio = SMP2P_BITS_PER_ENTRY;
+	ret = gpiochip_add(&chip->gpio);
+	if (ret) {
+		SMP2P_ERR("%s: unable to register GPIO '%s' ret %d\n",
+				__func__, chip->name, ret);
+		goto fail;
+	}
+
+	/*
+	 * Test entries opened by GPIO Test conflict with loopback
+	 * support, so the test entries must be explicitly opened
+	 * in the unit test framework.
+	 */
+	if (strcmp("smp2p", chip->name) == 0)
+		is_test_entry = true;
+
+	if (!chip->is_inbound)	{
+		chip->out_notifier.notifier_call = smp2p_gpio_out_notify;
+		if (!is_test_entry) {
+			ret = msm_smp2p_out_open(chip->remote_pid, chip->name,
+					   &chip->out_notifier,
+					   &chip->out_handle);
+			if (ret < 0)
+				goto error;
+		}
+	} else {
+		chip->in_notifier.notifier_call = smp2p_gpio_in_notify;
+		if (!is_test_entry) {
+			ret = msm_smp2p_in_register(chip->remote_pid,
+					chip->name,
+					&chip->in_notifier);
+			if (ret < 0)
+				goto error;
+		}
+	}
+
+	spin_lock_irqsave(&smp2p_entry_lock_lha1, flags);
+	list_add(&chip->entry_list, &smp2p_entry_list);
+	spin_unlock_irqrestore(&smp2p_entry_lock_lha1, flags);
+
+	/*
+	 * Create interrupt domain - note that chip can't be removed from the
+	 * interrupt domain, so chip cannot be deleted after this point.
+	 */
+	if (chip->is_inbound)
+		smp2p_add_irq_domain(chip, node);
+	else
+		chip->irq_base = -1;
+
+	SMP2P_GPIO("%s: added %s%s entry '%s':%d gpio %d irq %d",
+			__func__,
+			is_test_entry ? "test " : "",
+			chip->is_inbound ? "in" : "out",
+			chip->name, chip->remote_pid,
+			chip->gpio.base, chip->irq_base);
+
+	return 0;
+error:
+	gpiochip_remove(&chip->gpio);
+
+fail:
+	kfree(chip);
+	return ret;
+}
+
+/**
+ * smp2p_gpio_open_close - Opens or closes entry.
+ *
+ * @entry:   Entry to open or close
+ * @do_open: true = open port; false = close
+ */
+static void smp2p_gpio_open_close(struct smp2p_chip_dev *entry,
+	bool do_open)
+{
+	int ret;
+
+	if (do_open) {
+		/* open entry */
+		if (entry->is_inbound)
+			ret = msm_smp2p_in_register(entry->remote_pid,
+					entry->name, &entry->in_notifier);
+		else
+			ret = msm_smp2p_out_open(entry->remote_pid,
+					entry->name, &entry->out_notifier,
+					&entry->out_handle);
+		SMP2P_GPIO("%s: opened %s '%s':%d ret %d\n",
+				__func__,
+				entry->is_inbound ? "in" : "out",
+				entry->name, entry->remote_pid,
+				ret);
+	} else {
+		/* close entry */
+		if (entry->is_inbound)
+			ret = msm_smp2p_in_unregister(entry->remote_pid,
+					entry->name, &entry->in_notifier);
+		else
+			ret = msm_smp2p_out_close(&entry->out_handle);
+		entry->is_open = false;
+		SMP2P_GPIO("%s: closed %s '%s':%d ret %d\n",
+				__func__,
+				entry->is_inbound ? "in" : "out",
+				entry->name, entry->remote_pid, ret);
+	}
+}
+
+/**
+ * smp2p_gpio_open_test_entry - Opens or closes test entries for unit testing.
+ *
+ * @name:       Name of the entry
+ * @remote_pid: Remote processor ID
+ * @do_open:    true = open port; false = close
+ */
+void smp2p_gpio_open_test_entry(const char *name, int remote_pid, bool do_open)
+{
+	struct smp2p_chip_dev *entry;
+	struct smp2p_chip_dev *start_entry;
+	unsigned long flags;
+
+	spin_lock_irqsave(&smp2p_entry_lock_lha1, flags);
+	if (list_empty(&smp2p_entry_list)) {
+		spin_unlock_irqrestore(&smp2p_entry_lock_lha1, flags);
+		return;
+	}
+	start_entry = list_first_entry(&smp2p_entry_list,
+					struct smp2p_chip_dev,
+					entry_list);
+	entry = start_entry;
+	do {
+		if (!strcmp(entry->name, name)
+				&& entry->remote_pid == remote_pid) {
+			/* found entry to change */
+			spin_unlock_irqrestore(&smp2p_entry_lock_lha1, flags);
+			smp2p_gpio_open_close(entry, do_open);
+			spin_lock_irqsave(&smp2p_entry_lock_lha1, flags);
+		}
+		list_rotate_left(&smp2p_entry_list);
+		entry = list_first_entry(&smp2p_entry_list,
+						struct smp2p_chip_dev,
+						entry_list);
+	} while (entry != start_entry);
+	spin_unlock_irqrestore(&smp2p_entry_lock_lha1, flags);
+}
+
+static const struct of_device_id msm_smp2p_match_table[] = {
+	{.compatible = "qcom,smp2pgpio", },
+	{},
+};
+
+static struct platform_driver smp2p_gpio_driver = {
+	.probe = smp2p_gpio_probe,
+	.driver = {
+		.name = "smp2pgpio",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_smp2p_match_table,
+	},
+};
+
+static int smp2p_init(void)
+{
+	INIT_LIST_HEAD(&smp2p_entry_list);
+	return platform_driver_register(&smp2p_gpio_driver);
+}
+module_init(smp2p_init);
+
+static void __exit smp2p_exit(void)
+{
+	platform_driver_unregister(&smp2p_gpio_driver);
+}
+module_exit(smp2p_exit);
+
+MODULE_DESCRIPTION("SMP2P GPIO");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 9f5eab9..8e49a3d 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -293,3 +293,24 @@
 
 config QTI_SYSTEM_PM
 	bool
+
+config MSM_SMP2P
+	bool "SMSM Point-to-Point (SMP2P)"
+	depends on MSM_SMEM
+	help
+	  Provide point-to-point remote signaling support.
+	  SMP2P enables transferring 32-bit values between
+	  the local and a remote system using shared
+	  memory and interrupts. A client can open multiple
+	  32-bit values by specifying a unique string and
+	  remote processor ID.
+
+config MSM_SMP2P_TEST
+	bool "SMSM Point-to-Point Test"
+	depends on MSM_SMP2P
+	help
+	  Enables loopback and unit testing support for
+	  SMP2P. Loopback support is used by other
+	  processors to do unit testing. Unit tests
+	  are used to verify the local and remote
+	  implementations.
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index 1e3bf00..52f1d8c 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -23,7 +23,7 @@
 obj-$(CONFIG_QCOM_MEMORY_DUMP_V2) += memory_dump_v2.o
 obj-$(CONFIG_QCOM_RUN_QUEUE_STATS) += rq_stats.o
 obj-$(CONFIG_QCOM_SECURE_BUFFER) += secure_buffer.o
-obj-$(CONFIG_MSM_SMEM)	+=	msm_smem.o smem_debug.o
+obj-$(CONFIG_MSM_SMEM) += msm_smem.o smem_debug.o
 obj-$(CONFIG_MSM_GLINK) += glink.o glink_debugfs.o glink_ssr.o
 obj-$(CONFIG_MSM_GLINK_LOOPBACK_SERVER) += glink_loopback_server.o
 obj-$(CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT) += glink_smem_native_xprt.o
@@ -32,3 +32,5 @@
 obj-$(CONFIG_QCOM_BUS_SCALING) += msm_bus/
 obj-$(CONFIG_QTI_RPMH_API) += rpmh.o
 obj-$(CONFIG_QTI_SYSTEM_PM) += system_pm.o
+obj-$(CONFIG_MSM_SMP2P) += msm_smp2p.o smp2p_debug.o smp2p_sleepstate.o
+obj-$(CONFIG_MSM_SMP2P_TEST) += smp2p_loopback.o smp2p_test.o smp2p_spinlock_test.o
diff --git a/drivers/soc/qcom/msm_smp2p.c b/drivers/soc/qcom/msm_smp2p.c
new file mode 100644
index 0000000..825dbf9
--- /dev/null
+++ b/drivers/soc/qcom/msm_smp2p.c
@@ -0,0 +1,1954 @@
+/* drivers/soc/qcom/smp2p.c
+ *
+ * Copyright (c) 2013-2016, 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/list.h>
+#include <linux/ctype.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/ipc_logging.h>
+#include <linux/err.h>
+#include <soc/qcom/smem.h>
+#include "smp2p_private_api.h"
+#include "smp2p_private.h"
+
+#define NUM_LOG_PAGES 3
+
+/**
+ * struct msm_smp2p_out - This structure represents the outbound SMP2P entry.
+ *
+ * @remote_pid: Outbound processor ID.
+ * @name: Entry name.
+ * @out_edge_list: Adds this structure into smp2p_out_list_item::list.
+ * @msm_smp2p_notifier_list: Notifier block head used to notify for open event.
+ * @open_nb: Notifier block used to notify for open event.
+ * @l_smp2p_entry: Pointer to the actual entry in the SMEM item.
+ */
+struct msm_smp2p_out {
+	int remote_pid;
+	char name[SMP2P_MAX_ENTRY_NAME];
+	struct list_head out_edge_list;
+	struct raw_notifier_head msm_smp2p_notifier_list;
+	struct notifier_block *open_nb;
+	uint32_t __iomem *l_smp2p_entry;
+};
+
+/**
+ * struct smp2p_out_list_item - Maintains the state of outbound edge.
+ *
+ * @out_item_lock_lha1: Lock protecting all elements of the structure.
+ * @list: list of outbound entries (struct msm_smp2p_out).
+ * @smem_edge_out: Pointer to outbound smem item.
+ * @smem_edge_state: State of the outbound edge.
+ * @ops_ptr: Pointer to internal version-specific SMEM item access functions.
+ *
+ * @feature_ssr_ack_enabled: SSR ACK Support Enabled
+ * @restart_ack: Current cached state of the local ack bit
+ */
+struct smp2p_out_list_item {
+	spinlock_t out_item_lock_lha1;
+
+	struct list_head list;
+	struct smp2p_smem __iomem *smem_edge_out;
+	enum msm_smp2p_edge_state smem_edge_state;
+	struct smp2p_version_if *ops_ptr;
+
+	bool feature_ssr_ack_enabled;
+	bool restart_ack;
+};
+static struct smp2p_out_list_item out_list[SMP2P_NUM_PROCS];
+
+static void *log_ctx;
+static int smp2p_debug_mask = MSM_SMP2P_INFO | MSM_SMP2P_DEBUG;
+module_param_named(debug_mask, smp2p_debug_mask,
+		   int, 0664);
+
+/**
+ * struct smp2p_in - Represents the entry on remote processor.
+ *
+ * @name: Name of the entry.
+ * @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.
+ * @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.
+ */
+struct smp2p_in {
+	int remote_pid;
+	char name[SMP2P_MAX_ENTRY_NAME];
+	struct list_head in_edge_list;
+	struct raw_notifier_head in_notifier_list;
+	uint32_t prev_entry_val;
+	uint32_t __iomem *entry_ptr;
+	uint32_t notifier_count;
+};
+
+/**
+ * struct smp2p_in_list_item - Maintains the inbound edge state.
+ *
+ * @in_item_lock_lhb1: Lock protecting all elements of the structure.
+ * @list: List head for the entries on remote processor.
+ * @smem_edge_in: Pointer to the remote smem item.
+ */
+struct smp2p_in_list_item {
+	spinlock_t in_item_lock_lhb1;
+	struct list_head list;
+	struct smp2p_smem __iomem *smem_edge_in;
+	uint32_t item_size;
+	uint32_t safe_total_entries;
+};
+static struct smp2p_in_list_item in_list[SMP2P_NUM_PROCS];
+
+/**
+ * SMEM Item access function interface.
+ *
+ * This interface is used to help isolate the implementation of
+ * the functionality from any changes in the shared data structures
+ * that may happen as versions are changed.
+ *
+ * @is_supported: True if this version is supported by SMP2P
+ * @negotiate_features: Returns (sub)set of supported features
+ * @negotiation_complete:  Called when negotiation has been completed
+ * @find_entry: Finds existing / next empty entry
+ * @create_entry: Creates a new entry
+ * @read_entry: Reads the value of an entry
+ * @write_entry: Writes a new value to an entry
+ * @modify_entry: Does a read/modify/write of an entry
+ * validate_size: Verifies the size of the remote SMEM item to ensure that
+ *                an invalid item size doesn't result in an out-of-bounds
+ *                memory access.
+ */
+struct smp2p_version_if {
+	/* common functions */
+	bool is_supported;
+	uint32_t (*negotiate_features)(uint32_t features);
+	void (*negotiation_complete)(struct smp2p_out_list_item *);
+	void (*find_entry)(struct smp2p_smem __iomem *item,
+			uint32_t entries_total,	char *name,
+			uint32_t **entry_ptr, int *empty_spot);
+
+	/* outbound entry functions */
+	int (*create_entry)(struct msm_smp2p_out *);
+	int (*read_entry)(struct msm_smp2p_out *, uint32_t *);
+	int (*write_entry)(struct msm_smp2p_out *, uint32_t);
+	int (*modify_entry)(struct msm_smp2p_out *, uint32_t, uint32_t, bool);
+
+	/* inbound entry functions */
+	struct smp2p_smem __iomem *(*validate_size)(int remote_pid,
+			struct smp2p_smem __iomem *, uint32_t);
+};
+
+static int smp2p_do_negotiation(int remote_pid, struct smp2p_out_list_item *p);
+static void smp2p_send_interrupt(int remote_pid);
+
+/* v0 (uninitialized SMEM item) interface functions */
+static uint32_t smp2p_negotiate_features_v0(uint32_t features);
+static void smp2p_negotiation_complete_v0(struct smp2p_out_list_item *out_item);
+static void smp2p_find_entry_v0(struct smp2p_smem __iomem *item,
+		uint32_t entries_total, char *name, uint32_t **entry_ptr,
+		int *empty_spot);
+static int smp2p_out_create_v0(struct msm_smp2p_out *);
+static int smp2p_out_read_v0(struct msm_smp2p_out *, uint32_t *);
+static int smp2p_out_write_v0(struct msm_smp2p_out *, uint32_t);
+static int smp2p_out_modify_v0(struct msm_smp2p_out *,
+					uint32_t, uint32_t, bool);
+static struct smp2p_smem __iomem *smp2p_in_validate_size_v0(int remote_pid,
+		struct smp2p_smem __iomem *smem_item, uint32_t size);
+
+/* v1 interface functions */
+static uint32_t smp2p_negotiate_features_v1(uint32_t features);
+static void smp2p_negotiation_complete_v1(struct smp2p_out_list_item *out_item);
+static void smp2p_find_entry_v1(struct smp2p_smem __iomem *item,
+		uint32_t entries_total, char *name, uint32_t **entry_ptr,
+		int *empty_spot);
+static int smp2p_out_create_v1(struct msm_smp2p_out *);
+static int smp2p_out_read_v1(struct msm_smp2p_out *, uint32_t *);
+static int smp2p_out_write_v1(struct msm_smp2p_out *, uint32_t);
+static int smp2p_out_modify_v1(struct msm_smp2p_out *,
+					uint32_t, uint32_t, bool);
+static struct smp2p_smem __iomem *smp2p_in_validate_size_v1(int remote_pid,
+		struct smp2p_smem __iomem *smem_item, uint32_t size);
+
+/* Version interface functions */
+static struct smp2p_version_if version_if[] = {
+	[0] = {
+		.negotiate_features = smp2p_negotiate_features_v0,
+		.negotiation_complete = smp2p_negotiation_complete_v0,
+		.find_entry = smp2p_find_entry_v0,
+		.create_entry = smp2p_out_create_v0,
+		.read_entry = smp2p_out_read_v0,
+		.write_entry = smp2p_out_write_v0,
+		.modify_entry = smp2p_out_modify_v0,
+		.validate_size = smp2p_in_validate_size_v0,
+	},
+	[1] = {
+		.is_supported = true,
+		.negotiate_features = smp2p_negotiate_features_v1,
+		.negotiation_complete = smp2p_negotiation_complete_v1,
+		.find_entry = smp2p_find_entry_v1,
+		.create_entry = smp2p_out_create_v1,
+		.read_entry = smp2p_out_read_v1,
+		.write_entry = smp2p_out_write_v1,
+		.modify_entry = smp2p_out_modify_v1,
+		.validate_size = smp2p_in_validate_size_v1,
+	},
+};
+
+/* interrupt configuration (filled by device tree) */
+static struct smp2p_interrupt_config smp2p_int_cfgs[SMP2P_NUM_PROCS] = {
+	[SMP2P_MODEM_PROC].name = "modem",
+	[SMP2P_AUDIO_PROC].name = "lpass",
+	[SMP2P_SENSOR_PROC].name = "dsps",
+	[SMP2P_WIRELESS_PROC].name = "wcnss",
+	[SMP2P_CDSP_PROC].name = "cdsp",
+	[SMP2P_TZ_PROC].name = "tz",
+	[SMP2P_REMOTE_MOCK_PROC].name = "mock",
+};
+
+/**
+ * smp2p_get_log_ctx - Return log context for other SMP2P modules.
+ *
+ * @returns: Log context or NULL if none.
+ */
+void *smp2p_get_log_ctx(void)
+{
+	return log_ctx;
+}
+
+/**
+ * smp2p_get_debug_mask - Return debug mask.
+ *
+ * @returns: Current debug mask.
+ */
+int smp2p_get_debug_mask(void)
+{
+	return smp2p_debug_mask;
+}
+
+/**
+ * smp2p_interrupt_config -  Return interrupt configuration.
+ *
+ * @returns interrupt configuration array for usage by debugfs.
+ */
+struct smp2p_interrupt_config *smp2p_get_interrupt_config(void)
+{
+	return smp2p_int_cfgs;
+}
+
+/**
+ * smp2p_pid_to_name -  Lookup name for remote pid.
+ *
+ * @returns: name (may be NULL).
+ */
+const char *smp2p_pid_to_name(int remote_pid)
+{
+	if (remote_pid >= SMP2P_NUM_PROCS)
+		return NULL;
+
+	return smp2p_int_cfgs[remote_pid].name;
+}
+
+/**
+ * smp2p_get_in_item - Return pointer to remote smem item.
+ *
+ * @remote_pid: Processor ID of the remote system.
+ * @returns:    Pointer to inbound SMEM item
+ *
+ * This is used by debugfs to print the smem items.
+ */
+struct smp2p_smem __iomem *smp2p_get_in_item(int remote_pid)
+{
+	void *ret = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&in_list[remote_pid].in_item_lock_lhb1, flags);
+	if (remote_pid < SMP2P_NUM_PROCS)
+		ret = in_list[remote_pid].smem_edge_in;
+	spin_unlock_irqrestore(&in_list[remote_pid].in_item_lock_lhb1,
+								flags);
+
+	return ret;
+}
+
+/**
+ * smp2p_get_out_item - Return pointer to outbound SMEM item.
+ *
+ * @remote_pid: Processor ID of remote system.
+ * @state:      Edge state of the outbound SMEM item.
+ * @returns:    Pointer to outbound (remote) SMEM item.
+ */
+struct smp2p_smem __iomem *smp2p_get_out_item(int remote_pid, int *state)
+{
+	void *ret = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&out_list[remote_pid].out_item_lock_lha1, flags);
+	if (remote_pid < SMP2P_NUM_PROCS) {
+		ret = out_list[remote_pid].smem_edge_out;
+		if (state)
+			*state = out_list[remote_pid].smem_edge_state;
+	}
+	spin_unlock_irqrestore(&out_list[remote_pid].out_item_lock_lha1, flags);
+
+	return ret;
+}
+
+/**
+ * smp2p_get_smem_item_id - Return the proper SMEM item ID.
+ *
+ * @write_id:	Processor that will write to the item.
+ * @read_id:    Processor that will read from the item.
+ * @returns:    SMEM ID
+ */
+static int smp2p_get_smem_item_id(int write_pid, int read_pid)
+{
+	int ret = -EINVAL;
+
+	switch (write_pid) {
+	case SMP2P_APPS_PROC:
+		ret = SMEM_SMP2P_APPS_BASE + read_pid;
+		break;
+	case SMP2P_MODEM_PROC:
+		ret = SMEM_SMP2P_MODEM_BASE + read_pid;
+		break;
+	case SMP2P_AUDIO_PROC:
+		ret = SMEM_SMP2P_AUDIO_BASE + read_pid;
+		break;
+	case SMP2P_SENSOR_PROC:
+		ret = SMEM_SMP2P_SENSOR_BASE + read_pid;
+		break;
+	case SMP2P_WIRELESS_PROC:
+		ret = SMEM_SMP2P_WIRLESS_BASE + read_pid;
+		break;
+	case SMP2P_CDSP_PROC:
+		ret = SMEM_SMP2P_CDSP_BASE + read_pid;
+		break;
+	case SMP2P_POWER_PROC:
+		ret = SMEM_SMP2P_POWER_BASE + read_pid;
+		break;
+	case SMP2P_TZ_PROC:
+		ret = SMEM_SMP2P_TZ_BASE + read_pid;
+		break;
+	}
+
+	return ret;
+}
+
+/**
+ * Return pointer to SMEM item owned by the local processor.
+ *
+ * @remote_pid: Remote processor ID
+ * @returns:    NULL for failure; otherwise pointer to SMEM item
+ *
+ * Must be called with out_item_lock_lha1 locked for mock proc.
+ */
+static void *smp2p_get_local_smem_item(int remote_pid)
+{
+	struct smp2p_smem __iomem *item_ptr = NULL;
+
+	if (remote_pid < SMP2P_REMOTE_MOCK_PROC) {
+		unsigned int size;
+		int smem_id;
+
+		/* lookup or allocate SMEM item */
+		smem_id = smp2p_get_smem_item_id(SMP2P_APPS_PROC, remote_pid);
+		if (smem_id >= 0) {
+			item_ptr = smem_get_entry(smem_id, &size,
+								remote_pid, 0);
+
+			if (!item_ptr) {
+				size = sizeof(struct smp2p_smem_item);
+				item_ptr = smem_alloc(smem_id, size,
+								remote_pid, 0);
+			}
+		}
+	} else if (remote_pid == SMP2P_REMOTE_MOCK_PROC) {
+		/*
+		 * This path is only used during unit testing so
+		 * the GFP_ATOMIC allocation should not be a
+		 * concern.
+		 */
+		if (!out_list[SMP2P_REMOTE_MOCK_PROC].smem_edge_out)
+			item_ptr = kzalloc(
+					sizeof(struct smp2p_smem_item),
+					GFP_ATOMIC);
+	}
+	return item_ptr;
+}
+
+/**
+ * smp2p_get_remote_smem_item - Return remote SMEM item.
+ *
+ * @remote_pid: Remote processor ID
+ * @out_item:   Pointer to the output item structure
+ * @returns:    NULL for failure; otherwise pointer to SMEM item
+ *
+ * Return pointer to SMEM item owned by the remote processor.
+ *
+ * Note that this function does an SMEM lookup which uses a remote spinlock,
+ * so this function should not be called more than necessary.
+ *
+ * Must be called with out_item_lock_lha1 and in_item_lock_lhb1 locked.
+ */
+static void *smp2p_get_remote_smem_item(int remote_pid,
+	struct smp2p_out_list_item *out_item)
+{
+	void *item_ptr = NULL;
+	unsigned int size = 0;
+
+	if (!out_item)
+		return item_ptr;
+
+	if (remote_pid < SMP2P_REMOTE_MOCK_PROC) {
+		int smem_id;
+
+		smem_id = smp2p_get_smem_item_id(remote_pid, SMP2P_APPS_PROC);
+		if (smem_id >= 0)
+			item_ptr = smem_get_entry(smem_id, &size,
+								remote_pid, 0);
+	} else if (remote_pid == SMP2P_REMOTE_MOCK_PROC) {
+		item_ptr = msm_smp2p_get_remote_mock_smem_item(&size);
+	}
+	item_ptr = out_item->ops_ptr->validate_size(remote_pid, item_ptr, size);
+
+	return item_ptr;
+}
+
+/**
+ * smp2p_ssr_ack_needed - Returns true if SSR ACK required
+ *
+ * @rpid: Remote processor ID
+ *
+ * Must be called with out_item_lock_lha1 and in_item_lock_lhb1 locked.
+ */
+static bool smp2p_ssr_ack_needed(uint32_t rpid)
+{
+	bool ssr_done;
+
+	if (!out_list[rpid].feature_ssr_ack_enabled)
+		return false;
+
+	ssr_done = SMP2P_GET_RESTART_DONE(in_list[rpid].smem_edge_in->flags);
+	if (ssr_done != out_list[rpid].restart_ack)
+		return true;
+
+	return false;
+}
+
+/**
+ * smp2p_do_ssr_ack - Handles SSR ACK
+ *
+ * @rpid: Remote processor ID
+ *
+ * Must be called with out_item_lock_lha1 and in_item_lock_lhb1 locked.
+ */
+static void smp2p_do_ssr_ack(uint32_t rpid)
+{
+	bool ack;
+
+	if (!smp2p_ssr_ack_needed(rpid))
+		return;
+
+	ack = !out_list[rpid].restart_ack;
+	SMP2P_INFO("%s: ssr ack pid %d: %d -> %d\n", __func__, rpid,
+			out_list[rpid].restart_ack, ack);
+	out_list[rpid].restart_ack = ack;
+	SMP2P_SET_RESTART_ACK(out_list[rpid].smem_edge_out->flags, ack);
+	smp2p_send_interrupt(rpid);
+}
+
+/**
+ * smp2p_negotiate_features_v1 - Initial feature negotiation.
+ *
+ * @features: Inbound feature set.
+ * @returns: Supported features (will be a same/subset of @features).
+ */
+static uint32_t smp2p_negotiate_features_v1(uint32_t features)
+{
+	return SMP2P_FEATURE_SSR_ACK;
+}
+
+/**
+ * smp2p_negotiation_complete_v1 - Negotiation completed
+ *
+ * @out_item:   Pointer to the output item structure
+ *
+ * Can be used to do final configuration based upon the negotiated feature set.
+ *
+ * Must be called with out_item_lock_lha1 locked.
+ */
+static void smp2p_negotiation_complete_v1(struct smp2p_out_list_item *out_item)
+{
+	uint32_t features;
+
+	features = SMP2P_GET_FEATURES(out_item->smem_edge_out->feature_version);
+
+	if (features & SMP2P_FEATURE_SSR_ACK)
+		out_item->feature_ssr_ack_enabled = true;
+}
+
+/**
+ * smp2p_find_entry_v1 - Search for an entry in SMEM item.
+ *
+ * @item: Pointer to the smem item.
+ * @entries_total: Total number of entries in @item.
+ * @name: Name of the entry.
+ * @entry_ptr: Set to pointer of entry if found, NULL otherwise.
+ * @empty_spot: If non-null, set to the value of the next empty entry.
+ *
+ * Searches for entry @name in the SMEM item.  If found, a pointer
+ * to the item is returned.  If it isn't found, the first empty
+ * index is returned in @empty_spot.
+ */
+static void smp2p_find_entry_v1(struct smp2p_smem __iomem *item,
+		uint32_t entries_total, char *name, uint32_t **entry_ptr,
+		int *empty_spot)
+{
+	int i;
+	struct smp2p_entry_v1 *pos;
+	char entry_name[SMP2P_MAX_ENTRY_NAME];
+
+	if (!item || !name || !entry_ptr) {
+		SMP2P_ERR("%s: invalid arguments %d %d %d\n",
+				__func__, !item, !name, !entry_ptr);
+		return;
+	}
+
+	*entry_ptr = NULL;
+	if (empty_spot)
+		*empty_spot = -1;
+
+	pos = (struct smp2p_entry_v1 *)(char *)(item + 1);
+	for (i = 0; i < entries_total; i++, ++pos) {
+		memcpy_fromio(entry_name, pos->name, SMP2P_MAX_ENTRY_NAME);
+		if (entry_name[0]) {
+			if (!strcmp(entry_name, name)) {
+				*entry_ptr = &pos->entry;
+				break;
+			}
+		} else if (empty_spot && *empty_spot < 0) {
+			*empty_spot = i;
+		}
+	}
+}
+
+/**
+ * smp2p_out_create_v1 - Creates a outbound SMP2P entry.
+ *
+ * @out_entry: Pointer to the SMP2P entry structure.
+ * @returns: 0 on success, standard Linux error code otherwise.
+ *
+ * Must be called with out_item_lock_lha1 locked.
+ */
+static int smp2p_out_create_v1(struct msm_smp2p_out *out_entry)
+{
+	struct smp2p_smem __iomem *smp2p_h_ptr;
+	struct smp2p_out_list_item *p_list;
+	uint32_t *state_entry_ptr;
+	uint32_t empty_spot;
+	uint32_t entries_total;
+	uint32_t entries_valid;
+
+	if (!out_entry)
+		return -EINVAL;
+
+	p_list = &out_list[out_entry->remote_pid];
+	if (p_list->smem_edge_state != SMP2P_EDGE_STATE_OPENED) {
+		SMP2P_ERR("%s: item '%s':%d opened - wrong create called\n",
+			__func__, out_entry->name, out_entry->remote_pid);
+		return -ENODEV;
+	}
+
+	smp2p_h_ptr = p_list->smem_edge_out;
+	entries_total = SMP2P_GET_ENT_TOTAL(smp2p_h_ptr->valid_total_ent);
+	entries_valid = SMP2P_GET_ENT_VALID(smp2p_h_ptr->valid_total_ent);
+
+	p_list->ops_ptr->find_entry(smp2p_h_ptr, entries_total,
+			out_entry->name, &state_entry_ptr, &empty_spot);
+	if (state_entry_ptr) {
+		/* re-use existing entry */
+		out_entry->l_smp2p_entry = state_entry_ptr;
+
+		SMP2P_DBG("%s: item '%s':%d reused\n", __func__,
+				out_entry->name, out_entry->remote_pid);
+	} else if (entries_valid >= entries_total) {
+		/* need to allocate entry, but not more space */
+		SMP2P_ERR("%s: no space for item '%s':%d\n",
+			__func__, out_entry->name, out_entry->remote_pid);
+		return -ENOMEM;
+	} else {
+		/* allocate a new entry */
+		struct smp2p_entry_v1 *entry_ptr;
+
+		entry_ptr = (struct smp2p_entry_v1 *)((char *)(smp2p_h_ptr + 1)
+			+ empty_spot * sizeof(struct smp2p_entry_v1));
+		memcpy_toio(entry_ptr->name, out_entry->name,
+						sizeof(entry_ptr->name));
+		out_entry->l_smp2p_entry = &entry_ptr->entry;
+		++entries_valid;
+		SMP2P_DBG("%s: item '%s':%d fully created as entry %d of %d\n",
+				__func__, out_entry->name,
+				out_entry->remote_pid,
+				entries_valid, entries_total);
+		SMP2P_SET_ENT_VALID(smp2p_h_ptr->valid_total_ent,
+				entries_valid);
+		smp2p_send_interrupt(out_entry->remote_pid);
+	}
+	raw_notifier_call_chain(&out_entry->msm_smp2p_notifier_list,
+		  SMP2P_OPEN, 0);
+
+	return 0;
+}
+
+/**
+ * smp2p_out_read_v1 -  Read the data from an outbound entry.
+ *
+ * @out_entry: Pointer to the SMP2P entry structure.
+ * @data: Out pointer, the data is available in this argument on success.
+ * @returns: 0 on success, standard Linux error code otherwise.
+ *
+ * Must be called with out_item_lock_lha1 locked.
+ */
+static int smp2p_out_read_v1(struct msm_smp2p_out *out_entry, uint32_t *data)
+{
+	struct smp2p_smem __iomem  *smp2p_h_ptr;
+	uint32_t remote_pid;
+
+	if (!out_entry)
+		return -EINVAL;
+
+	smp2p_h_ptr = out_list[out_entry->remote_pid].smem_edge_out;
+	remote_pid = SMP2P_GET_REMOTE_PID(smp2p_h_ptr->rem_loc_proc_id);
+
+	if (remote_pid != out_entry->remote_pid)
+		return -EINVAL;
+
+	if (out_entry->l_smp2p_entry) {
+		*data = readl_relaxed(out_entry->l_smp2p_entry);
+	} else {
+		SMP2P_ERR("%s: '%s':%d not yet OPEN\n", __func__,
+				out_entry->name, remote_pid);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+/**
+ * smp2p_out_write_v1 - Writes an outbound entry value.
+ *
+ * @out_entry: Pointer to the SMP2P entry structure.
+ * @data: The data to be written.
+ * @returns: 0 on success, standard Linux error code otherwise.
+ *
+ * Must be called with out_item_lock_lha1 locked.
+ */
+static int smp2p_out_write_v1(struct msm_smp2p_out *out_entry, uint32_t data)
+{
+	struct smp2p_smem __iomem  *smp2p_h_ptr;
+	uint32_t remote_pid;
+
+	if (!out_entry)
+		return -EINVAL;
+
+	smp2p_h_ptr = out_list[out_entry->remote_pid].smem_edge_out;
+	remote_pid = SMP2P_GET_REMOTE_PID(smp2p_h_ptr->rem_loc_proc_id);
+
+	if (remote_pid != out_entry->remote_pid)
+		return -EINVAL;
+
+	if (out_entry->l_smp2p_entry) {
+		writel_relaxed(data, out_entry->l_smp2p_entry);
+		smp2p_send_interrupt(remote_pid);
+	} else {
+		SMP2P_ERR("%s: '%s':%d not yet OPEN\n", __func__,
+				out_entry->name, remote_pid);
+		return -ENODEV;
+	}
+	return 0;
+}
+
+/**
+ * smp2p_out_modify_v1 - Modifies and outbound value.
+ *
+ * @set_mask:  Mask containing the bits that needs to be set.
+ * @clear_mask: Mask containing the bits that needs to be cleared.
+ * @send_irq: Flag to send interrupt to remote processor.
+ * @returns: 0 on success, standard Linux error code otherwise.
+ *
+ * The clear mask is applied first, so  if a bit is set in both clear and
+ * set mask, the result will be that the bit is set.
+ *
+ * Must be called with out_item_lock_lha1 locked.
+ */
+static int smp2p_out_modify_v1(struct msm_smp2p_out *out_entry,
+		uint32_t set_mask, uint32_t clear_mask, bool send_irq)
+{
+	struct smp2p_smem __iomem  *smp2p_h_ptr;
+	uint32_t remote_pid;
+
+	if (!out_entry)
+		return -EINVAL;
+
+	smp2p_h_ptr = out_list[out_entry->remote_pid].smem_edge_out;
+	remote_pid = SMP2P_GET_REMOTE_PID(smp2p_h_ptr->rem_loc_proc_id);
+
+	if (remote_pid != out_entry->remote_pid)
+		return -EINVAL;
+
+	if (out_entry->l_smp2p_entry) {
+		uint32_t curr_value;
+
+		curr_value = readl_relaxed(out_entry->l_smp2p_entry);
+		writel_relaxed((curr_value & ~clear_mask) | set_mask,
+			out_entry->l_smp2p_entry);
+	} else {
+		SMP2P_ERR("%s: '%s':%d not yet OPEN\n", __func__,
+				out_entry->name, remote_pid);
+		return -ENODEV;
+	}
+
+	if (send_irq)
+		smp2p_send_interrupt(remote_pid);
+	return 0;
+}
+
+/**
+ * smp2p_in_validate_size_v1 - Size validation for version 1.
+ *
+ * @remote_pid: Remote processor ID.
+ * @smem_item:  Pointer to the inbound SMEM item.
+ * @size:       Size of the SMEM item.
+ * @returns:    Validated smem_item pointer (or NULL if size is too small).
+ *
+ * Validates we don't end up with out-of-bounds array access due to invalid
+ * smem item size.  If out-of-bound array access can't be avoided, then an
+ * error message is printed and NULL is returned to prevent usage of the
+ * item.
+ *
+ * Must be called with in_item_lock_lhb1 locked.
+ */
+static struct smp2p_smem __iomem *smp2p_in_validate_size_v1(int remote_pid,
+		struct smp2p_smem __iomem *smem_item, uint32_t size)
+{
+	uint32_t total_entries;
+	unsigned int expected_size;
+	struct smp2p_smem __iomem *item_ptr;
+	struct smp2p_in_list_item *in_item;
+
+	if (remote_pid >= SMP2P_NUM_PROCS || !smem_item)
+		return NULL;
+
+	in_item = &in_list[remote_pid];
+	item_ptr = (struct smp2p_smem __iomem *)smem_item;
+
+	total_entries = SMP2P_GET_ENT_TOTAL(item_ptr->valid_total_ent);
+	if (total_entries > 0) {
+		in_item->safe_total_entries = total_entries;
+		in_item->item_size = size;
+
+		expected_size =	sizeof(struct smp2p_smem) +
+			(total_entries * sizeof(struct smp2p_entry_v1));
+
+		if (size < expected_size) {
+			unsigned int new_size;
+
+			new_size = size;
+			new_size -= sizeof(struct smp2p_smem);
+			new_size /= sizeof(struct smp2p_entry_v1);
+			in_item->safe_total_entries = new_size;
+
+			SMP2P_ERR(
+				"%s pid %d item too small for %d entries; expected: %d actual: %d; reduced to %d entries\n",
+				__func__, remote_pid, total_entries,
+				expected_size, size, new_size);
+		}
+	} else {
+		/*
+		 * Total entries is 0, so the entry is still being initialized
+		 * or is invalid.  Either way, treat it as if the item does
+		 * not exist yet.
+		 */
+		in_item->safe_total_entries = 0;
+		in_item->item_size = 0;
+	}
+	return item_ptr;
+}
+
+/**
+ * smp2p_negotiate_features_v0 - Initial feature negotiation.
+ *
+ * @features: Inbound feature set.
+ * @returns: 0 (no features supported for v0).
+ */
+static uint32_t smp2p_negotiate_features_v0(uint32_t features)
+{
+	/* no supported features */
+	return 0;
+}
+
+/**
+ * smp2p_negotiation_complete_v0 - Negotiation completed
+ *
+ * @out_item:   Pointer to the output item structure
+ *
+ * Can be used to do final configuration based upon the negotiated feature set.
+ */
+static void smp2p_negotiation_complete_v0(struct smp2p_out_list_item *out_item)
+{
+	SMP2P_ERR("%s: invalid negotiation complete for v0 pid %d\n",
+		__func__,
+		SMP2P_GET_REMOTE_PID(out_item->smem_edge_out->rem_loc_proc_id));
+}
+
+/**
+ * smp2p_find_entry_v0 - Stub function.
+ *
+ * @item: Pointer to the smem item.
+ * @entries_total: Total number of entries in @item.
+ * @name: Name of the entry.
+ * @entry_ptr: Set to pointer of entry if found, NULL otherwise.
+ * @empty_spot: If non-null, set to the value of the next empty entry.
+ *
+ * Entries cannot be searched for until item negotiation has been completed.
+ */
+static void smp2p_find_entry_v0(struct smp2p_smem __iomem *item,
+		uint32_t entries_total, char *name, uint32_t **entry_ptr,
+		int *empty_spot)
+{
+	if (entry_ptr)
+		*entry_ptr = NULL;
+
+	if (empty_spot)
+		*empty_spot = -1;
+
+	SMP2P_ERR("%s: invalid - item negotiation incomplete\n", __func__);
+}
+
+/**
+ * smp2p_out_create_v0 - Initial creation function.
+ *
+ * @out_entry: Pointer to the SMP2P entry structure.
+ * @returns: 0 on success, standard Linux error code otherwise.
+ *
+ * If the outbound SMEM item negotiation is not complete, then
+ * this function is called to start the negotiation process.
+ * Eventually when the negotiation process is complete, this
+ * function pointer is switched with the appropriate function
+ * for the version of SMP2P being created.
+ *
+ * Must be called with out_item_lock_lha1 locked.
+ */
+static int smp2p_out_create_v0(struct msm_smp2p_out *out_entry)
+{
+	int edge_state;
+	struct smp2p_out_list_item *item_ptr;
+
+	if (!out_entry)
+		return -EINVAL;
+
+	edge_state = out_list[out_entry->remote_pid].smem_edge_state;
+
+	switch (edge_state) {
+	case SMP2P_EDGE_STATE_CLOSED:
+		/* start negotiation */
+		item_ptr = &out_list[out_entry->remote_pid];
+		edge_state = smp2p_do_negotiation(out_entry->remote_pid,
+				item_ptr);
+		break;
+
+	case SMP2P_EDGE_STATE_OPENING:
+		/* still negotiating */
+		break;
+
+	case SMP2P_EDGE_STATE_OPENED:
+		SMP2P_ERR("%s: item '%s':%d opened - wrong create called\n",
+			__func__, out_entry->name, out_entry->remote_pid);
+		break;
+
+	default:
+		SMP2P_ERR("%s: item '%s':%d invalid SMEM item state %d\n",
+			__func__, out_entry->name, out_entry->remote_pid,
+			edge_state);
+		break;
+	}
+	return 0;
+}
+
+/**
+ * smp2p_out_read_v0 - Stub function.
+ *
+ * @out_entry: Pointer to the SMP2P entry structure.
+ * @data: Out pointer, the data is available in this argument on success.
+ * @returns: -ENODEV
+ */
+static int smp2p_out_read_v0(struct msm_smp2p_out *out_entry, uint32_t *data)
+{
+	SMP2P_ERR("%s: item '%s':%d not OPEN\n",
+		__func__, out_entry->name, out_entry->remote_pid);
+
+	return -ENODEV;
+}
+
+/**
+ * smp2p_out_write_v0 - Stub function.
+ *
+ * @out_entry: Pointer to the SMP2P entry structure.
+ * @data: The data to be written.
+ * @returns: -ENODEV
+ */
+static int smp2p_out_write_v0(struct msm_smp2p_out *out_entry, uint32_t data)
+{
+	SMP2P_ERR("%s: item '%s':%d not yet OPEN\n",
+		__func__, out_entry->name, out_entry->remote_pid);
+
+	return -ENODEV;
+}
+
+/**
+ * smp2p_out_modify_v0 - Stub function.
+ *
+ * @set_mask:  Mask containing the bits that needs to be set.
+ * @clear_mask: Mask containing the bits that needs to be cleared.
+ * @send_irq: Flag to send interrupt to remote processor.
+ * @returns: -ENODEV
+ */
+static int smp2p_out_modify_v0(struct msm_smp2p_out *out_entry,
+		uint32_t set_mask, uint32_t clear_mask, bool send_irq)
+{
+	SMP2P_ERR("%s: item '%s':%d not yet OPEN\n",
+		__func__, out_entry->name, out_entry->remote_pid);
+
+	return -ENODEV;
+}
+
+/**
+ * smp2p_in_validate_size_v0 - Stub function.
+ *
+ * @remote_pid: Remote processor ID.
+ * @smem_item:  Pointer to the inbound SMEM item.
+ * @size:       Size of the SMEM item.
+ * @returns:    Validated smem_item pointer (or NULL if size is too small).
+ *
+ * Validates we don't end up with out-of-bounds array access due to invalid
+ * smem item size.  If out-of-bound array access can't be avoided, then an
+ * error message is printed and NULL is returned to prevent usage of the
+ * item.
+ *
+ * Must be called with in_item_lock_lhb1 locked.
+ */
+static struct smp2p_smem __iomem *smp2p_in_validate_size_v0(int remote_pid,
+		struct smp2p_smem __iomem *smem_item, uint32_t size)
+{
+	struct smp2p_in_list_item *in_item;
+
+	if (remote_pid >= SMP2P_NUM_PROCS || !smem_item)
+		return NULL;
+
+	in_item = &in_list[remote_pid];
+
+	if (size < sizeof(struct smp2p_smem)) {
+		SMP2P_ERR(
+			"%s pid %d item size too small; expected: %zu actual: %d\n",
+			__func__, remote_pid,
+			sizeof(struct smp2p_smem), size);
+		smem_item = NULL;
+		in_item->item_size = 0;
+	} else {
+		in_item->item_size = size;
+	}
+	return smem_item;
+}
+
+/**
+ * smp2p_init_header - Initializes the header of the smem item.
+ *
+ * @header_ptr: Pointer to the smp2p header.
+ * @local_pid: Local processor ID.
+ * @remote_pid: Remote processor ID.
+ * @feature: Features of smp2p implementation.
+ * @version: Version of smp2p implementation.
+ *
+ * Initializes the header as defined in the protocol specification.
+ */
+void smp2p_init_header(struct smp2p_smem __iomem *header_ptr,
+		int local_pid, int remote_pid,
+		uint32_t features, uint32_t version)
+{
+	header_ptr->magic = SMP2P_MAGIC;
+	SMP2P_SET_LOCAL_PID(header_ptr->rem_loc_proc_id, local_pid);
+	SMP2P_SET_REMOTE_PID(header_ptr->rem_loc_proc_id, remote_pid);
+	SMP2P_SET_FEATURES(header_ptr->feature_version, features);
+	SMP2P_SET_ENT_TOTAL(header_ptr->valid_total_ent, SMP2P_MAX_ENTRY);
+	SMP2P_SET_ENT_VALID(header_ptr->valid_total_ent, 0);
+	header_ptr->flags = 0;
+
+	/* ensure that all fields are valid before version is written */
+	wmb();
+	SMP2P_SET_VERSION(header_ptr->feature_version, version);
+}
+
+/**
+ * smp2p_do_negotiation - Implements negotiation algorithm.
+ *
+ * @remote_pid: Remote processor ID.
+ * @out_item: Pointer to the outbound list item.
+ * @returns: 0 on success, standard Linux error code otherwise.
+ *
+ * Must be called with out_item_lock_lha1 locked.  Will internally lock
+ * in_item_lock_lhb1.
+ */
+static int smp2p_do_negotiation(int remote_pid,
+		struct smp2p_out_list_item *out_item)
+{
+	struct smp2p_smem __iomem *r_smem_ptr;
+	struct smp2p_smem __iomem *l_smem_ptr;
+	uint32_t r_version;
+	uint32_t r_feature;
+	uint32_t l_version, l_feature;
+	int prev_state;
+
+	if (remote_pid >= SMP2P_NUM_PROCS || !out_item)
+		return -EINVAL;
+	if (out_item->smem_edge_state == SMP2P_EDGE_STATE_FAILED)
+		return -EPERM;
+
+	prev_state = out_item->smem_edge_state;
+
+	/* create local item */
+	if (!out_item->smem_edge_out) {
+		out_item->smem_edge_out = smp2p_get_local_smem_item(remote_pid);
+		if (!out_item->smem_edge_out) {
+			SMP2P_ERR(
+				"%s unable to allocate SMEM item for pid %d\n",
+				__func__, remote_pid);
+			return -ENODEV;
+		}
+		out_item->smem_edge_state = SMP2P_EDGE_STATE_OPENING;
+	}
+	l_smem_ptr = out_item->smem_edge_out;
+
+	/* retrieve remote side and version */
+	spin_lock(&in_list[remote_pid].in_item_lock_lhb1);
+	r_smem_ptr = smp2p_get_remote_smem_item(remote_pid, out_item);
+	spin_unlock(&in_list[remote_pid].in_item_lock_lhb1);
+
+	r_version = 0;
+	if (r_smem_ptr) {
+		r_version = SMP2P_GET_VERSION(r_smem_ptr->feature_version);
+		r_feature = SMP2P_GET_FEATURES(r_smem_ptr->feature_version);
+	}
+
+	if (r_version == 0) {
+		/*
+		 * Either remote side doesn't exist, or is in the
+		 * process of being initialized (the version is set last).
+		 *
+		 * In either case, treat as if the other side doesn't exist
+		 * and write out our maximum supported version.
+		 */
+		r_smem_ptr = NULL;
+		r_version = ARRAY_SIZE(version_if) - 1;
+		r_feature = ~0U;
+	}
+
+	/* find maximum supported version and feature set */
+	l_version = min(r_version, (uint32_t)ARRAY_SIZE(version_if) - 1);
+	for (; l_version > 0; --l_version) {
+		if (!version_if[l_version].is_supported)
+			continue;
+
+		/* found valid version */
+		l_feature = version_if[l_version].negotiate_features(~0U);
+		if (l_version == r_version)
+			l_feature &= r_feature;
+		break;
+	}
+
+	if (l_version == 0) {
+		SMP2P_ERR(
+			"%s: negotiation failure pid %d: RV %d RF %x\n",
+			__func__, remote_pid, r_version, r_feature
+			);
+		SMP2P_SET_VERSION(l_smem_ptr->feature_version,
+			SMP2P_EDGE_STATE_FAILED);
+		smp2p_send_interrupt(remote_pid);
+		out_item->smem_edge_state = SMP2P_EDGE_STATE_FAILED;
+		return -EPERM;
+	}
+
+	/* update header and notify remote side */
+	smp2p_init_header(l_smem_ptr, SMP2P_APPS_PROC, remote_pid,
+		l_feature, l_version);
+	smp2p_send_interrupt(remote_pid);
+
+	/* handle internal state changes */
+	if (r_smem_ptr && l_version == r_version &&
+			l_feature == r_feature) {
+		struct msm_smp2p_out *pos;
+
+		/* negotiation complete */
+		out_item->ops_ptr = &version_if[l_version];
+		out_item->ops_ptr->negotiation_complete(out_item);
+		out_item->smem_edge_state = SMP2P_EDGE_STATE_OPENED;
+		SMP2P_INFO(
+			"%s: negotiation complete pid %d: State %d->%d F0x%08x\n",
+			__func__, remote_pid, prev_state,
+			out_item->smem_edge_state, l_feature);
+
+		/* create any pending outbound entries */
+		list_for_each_entry(pos, &out_item->list, out_edge_list) {
+			out_item->ops_ptr->create_entry(pos);
+		}
+
+		/* update inbound edge */
+		spin_lock(&in_list[remote_pid].in_item_lock_lhb1);
+		(void)out_item->ops_ptr->validate_size(remote_pid, r_smem_ptr,
+				in_list[remote_pid].item_size);
+		in_list[remote_pid].smem_edge_in = r_smem_ptr;
+		spin_unlock(&in_list[remote_pid].in_item_lock_lhb1);
+	} else {
+		SMP2P_INFO("%s: negotiation pid %d: State %d->%d F0x%08x\n",
+			__func__, remote_pid, prev_state,
+			out_item->smem_edge_state, l_feature);
+	}
+	return 0;
+}
+
+/**
+ * msm_smp2p_out_open - Opens an outbound entry.
+ *
+ * @remote_pid: Outbound processor ID.
+ * @name: Name of the entry.
+ * @open_notifier: Notifier block for the open notification.
+ * @handle: Handle to the smem entry structure.
+ * @returns: 0 on success, standard Linux error code otherwise.
+ *
+ * Opens an outbound entry with the name specified by entry, from the
+ * local processor to the remote processor(remote_pid). If the entry, remote_pid
+ * and open_notifier are valid, then handle will be set and zero will be
+ * returned. The smem item that holds this entry will be created if it has
+ * not been created according to the version negotiation algorithm.
+ * The open_notifier will be used to notify the clients about the
+ * availability of the entry.
+ */
+int msm_smp2p_out_open(int remote_pid, const char *name,
+				   struct notifier_block *open_notifier,
+				   struct msm_smp2p_out **handle)
+{
+	struct msm_smp2p_out *out_entry;
+	struct msm_smp2p_out *pos;
+	int ret = 0;
+	unsigned long flags;
+
+	if (handle)
+		*handle = NULL;
+
+	if (remote_pid >= SMP2P_NUM_PROCS || !name || !open_notifier || !handle)
+		return -EINVAL;
+
+	if ((remote_pid != SMP2P_REMOTE_MOCK_PROC) &&
+			!smp2p_int_cfgs[remote_pid].is_configured) {
+		SMP2P_INFO("%s before msm_smp2p_init(): pid[%d] name[%s]\n",
+						__func__, remote_pid, name);
+		return -EPROBE_DEFER;
+	}
+
+	/* Allocate the smp2p object and node */
+	out_entry = kzalloc(sizeof(*out_entry), GFP_KERNEL);
+	if (!out_entry)
+		return -ENOMEM;
+
+	/* Handle duplicate registration */
+	spin_lock_irqsave(&out_list[remote_pid].out_item_lock_lha1, flags);
+	list_for_each_entry(pos, &out_list[remote_pid].list,
+			out_edge_list) {
+		if (!strcmp(pos->name, name)) {
+			spin_unlock_irqrestore(
+				&out_list[remote_pid].out_item_lock_lha1,
+				flags);
+			kfree(out_entry);
+			SMP2P_ERR("%s: duplicate registration '%s':%d\n",
+				__func__, name, remote_pid);
+			return -EBUSY;
+		}
+	}
+
+	out_entry->remote_pid = remote_pid;
+	RAW_INIT_NOTIFIER_HEAD(&out_entry->msm_smp2p_notifier_list);
+	strlcpy(out_entry->name, name, SMP2P_MAX_ENTRY_NAME);
+	out_entry->open_nb = open_notifier;
+	raw_notifier_chain_register(&out_entry->msm_smp2p_notifier_list,
+		  out_entry->open_nb);
+	list_add(&out_entry->out_edge_list, &out_list[remote_pid].list);
+
+	ret = out_list[remote_pid].ops_ptr->create_entry(out_entry);
+	if (ret) {
+		list_del(&out_entry->out_edge_list);
+		raw_notifier_chain_unregister(
+			&out_entry->msm_smp2p_notifier_list,
+			out_entry->open_nb);
+		spin_unlock_irqrestore(
+			&out_list[remote_pid].out_item_lock_lha1, flags);
+		kfree(out_entry);
+		SMP2P_ERR("%s: unable to open '%s':%d error %d\n",
+				__func__, name, remote_pid, ret);
+		return ret;
+	}
+	spin_unlock_irqrestore(&out_list[remote_pid].out_item_lock_lha1,
+			flags);
+	*handle = out_entry;
+
+	return 0;
+}
+EXPORT_SYMBOL(msm_smp2p_out_open);
+
+/**
+ * msm_smp2p_out_close - Closes the handle to an outbound entry.
+ *
+ * @handle: Pointer to smp2p out entry handle.
+ * @returns: 0 on success, standard Linux error code otherwise.
+ *
+ * The actual entry will not be deleted and can be re-opened at a later
+ * time.  The handle will be set to NULL.
+ */
+int msm_smp2p_out_close(struct msm_smp2p_out **handle)
+{
+	unsigned long flags;
+	struct msm_smp2p_out *out_entry;
+	struct smp2p_out_list_item *out_item;
+
+	if (!handle || !*handle)
+		return -EINVAL;
+
+	out_entry = *handle;
+	*handle = NULL;
+
+	if ((out_entry->remote_pid != SMP2P_REMOTE_MOCK_PROC) &&
+			!smp2p_int_cfgs[out_entry->remote_pid].is_configured) {
+		SMP2P_INFO("%s before msm_smp2p_init(): pid[%d] name[%s]\n",
+			__func__, out_entry->remote_pid, out_entry->name);
+		return -EPROBE_DEFER;
+	}
+
+	out_item = &out_list[out_entry->remote_pid];
+	spin_lock_irqsave(&out_item->out_item_lock_lha1, flags);
+	list_del(&out_entry->out_edge_list);
+	raw_notifier_chain_unregister(&out_entry->msm_smp2p_notifier_list,
+		out_entry->open_nb);
+	spin_unlock_irqrestore(&out_item->out_item_lock_lha1, flags);
+
+	kfree(out_entry);
+
+	return 0;
+}
+EXPORT_SYMBOL(msm_smp2p_out_close);
+
+/**
+ * msm_smp2p_out_read - Allows reading the entry.
+ *
+ * @handle: Handle to the smem entry structure.
+ * @data: Out pointer that holds the read data.
+ * @returns: 0 on success, standard Linux error code otherwise.
+ *
+ * Allows reading of the outbound entry for read-modify-write
+ * operation.
+ */
+int msm_smp2p_out_read(struct msm_smp2p_out *handle, uint32_t *data)
+{
+	int ret = -EINVAL;
+	unsigned long flags;
+	struct smp2p_out_list_item *out_item;
+
+	if (!handle || !data)
+		return ret;
+
+	if ((handle->remote_pid != SMP2P_REMOTE_MOCK_PROC) &&
+			!smp2p_int_cfgs[handle->remote_pid].is_configured) {
+		SMP2P_INFO("%s before msm_smp2p_init(): pid[%d] name[%s]\n",
+			__func__, handle->remote_pid, handle->name);
+		return -EPROBE_DEFER;
+	}
+
+	out_item = &out_list[handle->remote_pid];
+	spin_lock_irqsave(&out_item->out_item_lock_lha1, flags);
+	ret = out_item->ops_ptr->read_entry(handle, data);
+	spin_unlock_irqrestore(&out_item->out_item_lock_lha1, flags);
+
+	return ret;
+}
+EXPORT_SYMBOL(msm_smp2p_out_read);
+
+/**
+ * msm_smp2p_out_write - Allows writing to the entry.
+ *
+ * @handle: Handle to smem entry structure.
+ * @data: Data that has to be written.
+ * @returns: 0 on success, standard Linux error code otherwise.
+ *
+ * Writes a new value to the output entry. Multiple back-to-back writes
+ * may overwrite previous writes before the remote processor get a chance
+ * to see them leading to ABA race condition. The client must implement
+ * their own synchronization mechanism (such as echo mechanism) if this is
+ * not acceptable.
+ */
+int msm_smp2p_out_write(struct msm_smp2p_out *handle, uint32_t data)
+{
+	int ret = -EINVAL;
+	unsigned long flags;
+	struct smp2p_out_list_item *out_item;
+
+	if (!handle)
+		return ret;
+
+	if ((handle->remote_pid != SMP2P_REMOTE_MOCK_PROC) &&
+			!smp2p_int_cfgs[handle->remote_pid].is_configured) {
+		SMP2P_INFO("%s before msm_smp2p_init(): pid[%d] name[%s]\n",
+			__func__, handle->remote_pid, handle->name);
+		return -EPROBE_DEFER;
+	}
+
+	out_item = &out_list[handle->remote_pid];
+	spin_lock_irqsave(&out_item->out_item_lock_lha1, flags);
+	ret = out_item->ops_ptr->write_entry(handle, data);
+	spin_unlock_irqrestore(&out_item->out_item_lock_lha1, flags);
+
+	return ret;
+
+}
+EXPORT_SYMBOL(msm_smp2p_out_write);
+
+/**
+ * msm_smp2p_out_modify - Modifies the entry.
+ *
+ * @handle: Handle to the smem entry structure.
+ * @set_mask: Specifies the bits that needs to be set.
+ * @clear_mask: Specifies the bits that needs to be cleared.
+ * @send_irq: Flag to send interrupt to remote processor.
+ * @returns: 0 on success, standard Linux error code otherwise.
+ *
+ * The modification is done by doing a bitwise AND of clear mask followed by
+ * the bit wise OR of set mask. The clear bit mask is applied first to the
+ * data, so if a bit is set in both the clear mask and the set mask, then in
+ * the result is a set bit.  Multiple back-to-back modifications may overwrite
+ * previous values before the remote processor gets a chance to see them
+ * leading to ABA race condition. The client must implement their own
+ * synchronization mechanism (such as echo mechanism) if this is not
+ * acceptable.
+ */
+int msm_smp2p_out_modify(struct msm_smp2p_out *handle, uint32_t set_mask,
+					uint32_t clear_mask, bool send_irq)
+{
+	int ret = -EINVAL;
+	unsigned long flags;
+	struct smp2p_out_list_item *out_item;
+
+	if (!handle)
+		return ret;
+
+	if ((handle->remote_pid != SMP2P_REMOTE_MOCK_PROC) &&
+			!smp2p_int_cfgs[handle->remote_pid].is_configured) {
+		SMP2P_INFO("%s before msm_smp2p_init(): pid[%d] name[%s]\n",
+			__func__, handle->remote_pid, handle->name);
+		return -EPROBE_DEFER;
+	}
+
+	out_item = &out_list[handle->remote_pid];
+	spin_lock_irqsave(&out_item->out_item_lock_lha1, flags);
+	ret = out_item->ops_ptr->modify_entry(handle, set_mask,
+						clear_mask, send_irq);
+	spin_unlock_irqrestore(&out_item->out_item_lock_lha1, flags);
+
+	return ret;
+}
+EXPORT_SYMBOL(msm_smp2p_out_modify);
+
+/**
+ * msm_smp2p_in_read - Read an entry on a remote processor.
+ *
+ * @remote_pid: Processor ID of the remote processor.
+ * @name: Name of the entry that is to be read.
+ * @data: Output pointer, the value will be placed here if successful.
+ * @returns: 0 on success, standard Linux error code otherwise.
+ */
+int msm_smp2p_in_read(int remote_pid, const char *name, uint32_t *data)
+{
+	unsigned long flags;
+	struct smp2p_out_list_item *out_item;
+	uint32_t *entry_ptr = NULL;
+
+	if (remote_pid >= SMP2P_NUM_PROCS)
+		return -EINVAL;
+
+	if ((remote_pid != SMP2P_REMOTE_MOCK_PROC) &&
+			!smp2p_int_cfgs[remote_pid].is_configured) {
+		SMP2P_INFO("%s before msm_smp2p_init(): pid[%d] name[%s]\n",
+						__func__, remote_pid, name);
+		return -EPROBE_DEFER;
+	}
+
+	out_item = &out_list[remote_pid];
+	spin_lock_irqsave(&out_item->out_item_lock_lha1, flags);
+	spin_lock(&in_list[remote_pid].in_item_lock_lhb1);
+
+	if (in_list[remote_pid].smem_edge_in)
+		out_item->ops_ptr->find_entry(
+			in_list[remote_pid].smem_edge_in,
+			in_list[remote_pid].safe_total_entries,
+			(char *)name, &entry_ptr, NULL);
+
+	spin_unlock(&in_list[remote_pid].in_item_lock_lhb1);
+	spin_unlock_irqrestore(&out_item->out_item_lock_lha1, flags);
+
+	if (!entry_ptr)
+		return -ENODEV;
+
+	*data = readl_relaxed(entry_ptr);
+	return 0;
+}
+EXPORT_SYMBOL(msm_smp2p_in_read);
+
+/**
+ * msm_smp2p_in_register -  Notifies the change in value of the entry.
+ *
+ * @pid: Remote processor ID.
+ * @name: Name of the entry.
+ * @in_notifier: Notifier block used to notify about the event.
+ * @returns: 0 on success, standard Linux error code otherwise.
+ *
+ * Register for change notifications for a remote entry. If the remote entry
+ * does not exist yet, then the registration request will be held until the
+ * remote side opens. Once the entry is open, then the SMP2P_OPEN notification
+ * will be sent. Any changes to the entry will trigger a call to the notifier
+ * block with an SMP2P_ENTRY_UPDATE event and the data field will point to an
+ * msm_smp2p_update_notif structure containing the current and previous value.
+ */
+int msm_smp2p_in_register(int pid, const char *name,
+	struct notifier_block *in_notifier)
+{
+	struct smp2p_in *pos;
+	struct smp2p_in *in = NULL;
+	int ret;
+	unsigned long flags;
+	struct msm_smp2p_update_notif data;
+	uint32_t *entry_ptr;
+
+	if (pid >= SMP2P_NUM_PROCS || !name || !in_notifier)
+		return -EINVAL;
+
+	if ((pid != SMP2P_REMOTE_MOCK_PROC) &&
+			!smp2p_int_cfgs[pid].is_configured) {
+		SMP2P_INFO("%s before msm_smp2p_init(): pid[%d] name[%s]\n",
+						__func__, pid, name);
+		return -EPROBE_DEFER;
+	}
+
+	/* Pre-allocate before spinlock since we will likely needed it */
+	in = kzalloc(sizeof(*in), GFP_KERNEL);
+	if (!in)
+		return -ENOMEM;
+
+	/* Search for existing entry */
+	spin_lock_irqsave(&out_list[pid].out_item_lock_lha1, flags);
+	spin_lock(&in_list[pid].in_item_lock_lhb1);
+
+	list_for_each_entry(pos, &in_list[pid].list, in_edge_list) {
+		if (!strcmp(pos->name, name)) {
+			kfree(in);
+			in = pos;
+			break;
+		}
+	}
+
+	/* Create and add it to the list */
+	if (!in->notifier_count) {
+		in->remote_pid = pid;
+		strlcpy(in->name, name, SMP2P_MAX_ENTRY_NAME);
+		RAW_INIT_NOTIFIER_HEAD(&in->in_notifier_list);
+		list_add(&in->in_edge_list, &in_list[pid].list);
+	}
+
+	ret = raw_notifier_chain_register(&in->in_notifier_list,
+			in_notifier);
+	if (ret) {
+		if (!in->notifier_count) {
+			list_del(&in->in_edge_list);
+			kfree(in);
+		}
+		SMP2P_DBG("%s: '%s':%d failed %d\n", __func__, name, pid, ret);
+		goto bail;
+	}
+	in->notifier_count++;
+
+	if (out_list[pid].smem_edge_state == SMP2P_EDGE_STATE_OPENED) {
+		out_list[pid].ops_ptr->find_entry(
+				in_list[pid].smem_edge_in,
+				in_list[pid].safe_total_entries, (char *)name,
+				&entry_ptr, NULL);
+		if (entry_ptr) {
+			in->entry_ptr = entry_ptr;
+			in->prev_entry_val = readl_relaxed(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);
+		}
+	}
+	SMP2P_DBG("%s: '%s':%d registered\n", __func__, name, pid);
+
+bail:
+	spin_unlock(&in_list[pid].in_item_lock_lhb1);
+	spin_unlock_irqrestore(&out_list[pid].out_item_lock_lha1, flags);
+	return ret;
+
+}
+EXPORT_SYMBOL(msm_smp2p_in_register);
+
+/**
+ * msm_smp2p_in_unregister - Unregister the notifier for remote entry.
+ *
+ * @remote_pid: Processor Id of the remote processor.
+ * @name: The name of the entry.
+ * @in_notifier: Notifier block passed during registration.
+ * @returns: 0 on success, standard Linux error code otherwise.
+ */
+int msm_smp2p_in_unregister(int remote_pid, const char *name,
+				struct notifier_block *in_notifier)
+{
+	struct smp2p_in *pos;
+	struct smp2p_in *in = NULL;
+	int ret = -ENODEV;
+	unsigned long flags;
+
+	if (remote_pid >= SMP2P_NUM_PROCS || !name || !in_notifier)
+		return -EINVAL;
+
+	if ((remote_pid != SMP2P_REMOTE_MOCK_PROC) &&
+			!smp2p_int_cfgs[remote_pid].is_configured) {
+		SMP2P_INFO("%s before msm_smp2p_init(): pid[%d] name[%s]\n",
+						__func__, remote_pid, name);
+		return -EPROBE_DEFER;
+	}
+
+	spin_lock_irqsave(&in_list[remote_pid].in_item_lock_lhb1, flags);
+	list_for_each_entry(pos, &in_list[remote_pid].list,
+			in_edge_list) {
+		if (!strcmp(pos->name, name)) {
+			in = pos;
+			break;
+		}
+	}
+	if (!in)
+		goto fail;
+
+	ret = raw_notifier_chain_unregister(&pos->in_notifier_list,
+			in_notifier);
+	if (ret == 0) {
+		pos->notifier_count--;
+		if (!pos->notifier_count) {
+			list_del(&pos->in_edge_list);
+			kfree(pos);
+			ret = 0;
+		}
+	} else {
+		SMP2P_ERR("%s: unregister failure '%s':%d\n", __func__,
+			name, remote_pid);
+		ret = -ENODEV;
+	}
+
+fail:
+	spin_unlock_irqrestore(&in_list[remote_pid].in_item_lock_lhb1, flags);
+
+	return ret;
+}
+EXPORT_SYMBOL(msm_smp2p_in_unregister);
+
+/**
+ * smp2p_send_interrupt - Send interrupt to remote system.
+ *
+ * @remote_pid:  Processor ID of the remote system
+ *
+ * Must be called with out_item_lock_lha1 locked.
+ */
+static void smp2p_send_interrupt(int remote_pid)
+{
+	if (smp2p_int_cfgs[remote_pid].name)
+		SMP2P_DBG("SMP2P Int Apps->%s(%d)\n",
+			smp2p_int_cfgs[remote_pid].name, remote_pid);
+
+	++smp2p_int_cfgs[remote_pid].out_interrupt_count;
+	if (remote_pid != SMP2P_REMOTE_MOCK_PROC &&
+			smp2p_int_cfgs[remote_pid].out_int_mask) {
+		/* flush any pending writes before triggering interrupt */
+		wmb();
+		writel_relaxed(smp2p_int_cfgs[remote_pid].out_int_mask,
+			smp2p_int_cfgs[remote_pid].out_int_ptr);
+	} else {
+		smp2p_remote_mock_rx_interrupt();
+	}
+}
+
+/**
+ * smp2p_in_edge_notify - Notifies the entry changed on remote processor.
+ *
+ * @pid: Processor ID of the remote processor.
+ *
+ * This function is invoked on an incoming interrupt, it scans
+ * the list of the clients registered for the entries on the remote
+ * processor and notifies them if  the data changes.
+ *
+ * Note:  Edge state must be OPENED to avoid a race condition with
+ *        out_list[pid].ops_ptr->find_entry.
+ */
+static void smp2p_in_edge_notify(int pid)
+{
+	struct smp2p_in *pos;
+	uint32_t *entry_ptr;
+	unsigned long flags;
+	struct smp2p_smem __iomem *smem_h_ptr;
+	uint32_t curr_data;
+	struct  msm_smp2p_update_notif data;
+
+	spin_lock_irqsave(&in_list[pid].in_item_lock_lhb1, flags);
+	smem_h_ptr = in_list[pid].smem_edge_in;
+	if (!smem_h_ptr) {
+		SMP2P_DBG("%s: No remote SMEM item for pid %d\n",
+			__func__, pid);
+		spin_unlock_irqrestore(&in_list[pid].in_item_lock_lhb1, flags);
+		return;
+	}
+
+	list_for_each_entry(pos, &in_list[pid].list, in_edge_list) {
+		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,
+				&entry_ptr, NULL);
+
+			if (entry_ptr) {
+				pos->entry_ptr = entry_ptr;
+				pos->prev_entry_val = 0;
+				data.previous_value = 0;
+				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);
+}
+
+/**
+ * smp2p_interrupt_handler - Incoming interrupt handler.
+ *
+ * @irq: Interrupt ID
+ * @data: Edge
+ * @returns: IRQ_HANDLED or IRQ_NONE for invalid interrupt
+ */
+static irqreturn_t smp2p_interrupt_handler(int irq, void *data)
+{
+	unsigned long flags;
+	uint32_t remote_pid = (uint32_t)(uintptr_t)data;
+
+	if (remote_pid >= SMP2P_NUM_PROCS) {
+		SMP2P_ERR("%s: invalid interrupt pid %d\n",
+			__func__, remote_pid);
+		return IRQ_NONE;
+	}
+
+	if (smp2p_int_cfgs[remote_pid].name)
+		SMP2P_DBG("SMP2P Int %s(%d)->Apps\n",
+			smp2p_int_cfgs[remote_pid].name, remote_pid);
+
+	spin_lock_irqsave(&out_list[remote_pid].out_item_lock_lha1, flags);
+	++smp2p_int_cfgs[remote_pid].in_interrupt_count;
+
+	if (out_list[remote_pid].smem_edge_state != SMP2P_EDGE_STATE_OPENED)
+		smp2p_do_negotiation(remote_pid, &out_list[remote_pid]);
+
+	if (out_list[remote_pid].smem_edge_state == SMP2P_EDGE_STATE_OPENED) {
+		bool do_restart_ack;
+
+		/*
+		 * Follow double-check pattern for restart ack since:
+		 * 1) we must notify clients of the X->0 transition
+		 *    that is part of the restart
+		 * 2) lock cannot be held during the
+		 *    smp2p_in_edge_notify() call because clients may do
+		 *    re-entrant calls into our APIs.
+		 *
+		 * smp2p_do_ssr_ack() will only do the ack if it is
+		 * necessary to handle the race condition exposed by
+		 * unlocking the spinlocks.
+		 */
+		spin_lock(&in_list[remote_pid].in_item_lock_lhb1);
+		do_restart_ack = smp2p_ssr_ack_needed(remote_pid);
+		spin_unlock(&in_list[remote_pid].in_item_lock_lhb1);
+		spin_unlock_irqrestore(&out_list[remote_pid].out_item_lock_lha1,
+			flags);
+
+		smp2p_in_edge_notify(remote_pid);
+
+		if (do_restart_ack) {
+			spin_lock_irqsave(
+				&out_list[remote_pid].out_item_lock_lha1,
+				flags);
+			spin_lock(&in_list[remote_pid].in_item_lock_lhb1);
+
+			smp2p_do_ssr_ack(remote_pid);
+
+			spin_unlock(&in_list[remote_pid].in_item_lock_lhb1);
+			spin_unlock_irqrestore(
+				&out_list[remote_pid].out_item_lock_lha1,
+				flags);
+		}
+	} else {
+		spin_unlock_irqrestore(&out_list[remote_pid].out_item_lock_lha1,
+			flags);
+	}
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * smp2p_reset_mock_edge - Reinitializes the mock edge.
+ *
+ * @returns: 0 on success, -EAGAIN to retry later.
+ *
+ * Reinitializes the mock edge to initial power-up state values.
+ */
+int smp2p_reset_mock_edge(void)
+{
+	const int rpid = SMP2P_REMOTE_MOCK_PROC;
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&out_list[rpid].out_item_lock_lha1, flags);
+	spin_lock(&in_list[rpid].in_item_lock_lhb1);
+
+	if (!list_empty(&out_list[rpid].list) ||
+			!list_empty(&in_list[rpid].list)) {
+		ret = -EAGAIN;
+		goto fail;
+	}
+
+	kfree(out_list[rpid].smem_edge_out);
+	out_list[rpid].smem_edge_out = NULL;
+	out_list[rpid].ops_ptr = &version_if[0];
+	out_list[rpid].smem_edge_state = SMP2P_EDGE_STATE_CLOSED;
+	out_list[rpid].feature_ssr_ack_enabled = false;
+	out_list[rpid].restart_ack = false;
+
+	in_list[rpid].smem_edge_in = NULL;
+	in_list[rpid].item_size = 0;
+	in_list[rpid].safe_total_entries = 0;
+
+fail:
+	spin_unlock(&in_list[rpid].in_item_lock_lhb1);
+	spin_unlock_irqrestore(&out_list[rpid].out_item_lock_lha1, flags);
+
+	return ret;
+}
+
+/**
+ * msm_smp2p_interrupt_handler - Triggers incoming interrupt.
+ *
+ * @remote_pid: Remote processor ID
+ *
+ * This function is used with the remote mock infrastructure
+ * used for testing. It simulates triggering of interrupt in
+ * a testing environment.
+ */
+void msm_smp2p_interrupt_handler(int remote_pid)
+{
+	smp2p_interrupt_handler(0, (void *)(uintptr_t)remote_pid);
+}
+
+/**
+ * msm_smp2p_probe - Device tree probe function.
+ *
+ * @pdev: Pointer to device tree data.
+ * @returns: 0 on success; -ENODEV otherwise
+ */
+static int msm_smp2p_probe(struct platform_device *pdev)
+{
+	struct resource *r;
+	void *irq_out_ptr = NULL;
+	char *key;
+	uint32_t edge;
+	int ret;
+	struct device_node *node;
+	uint32_t irq_bitmask;
+	uint32_t irq_line;
+	void *temp_p;
+	unsigned int temp_sz;
+
+	node = pdev->dev.of_node;
+
+	key = "qcom,remote-pid";
+	ret = of_property_read_u32(node, key, &edge);
+	if (ret) {
+		SMP2P_ERR("%s: missing edge '%s'\n", __func__, key);
+		ret = -ENODEV;
+		goto fail;
+	}
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!r) {
+		SMP2P_ERR("%s: failed gathering irq-reg resource for edge %d\n"
+				, __func__, edge);
+		ret = -ENODEV;
+		goto fail;
+	}
+	irq_out_ptr = ioremap_nocache(r->start, resource_size(r));
+	if (!irq_out_ptr) {
+		SMP2P_ERR("%s: failed remap from phys to virt for edge %d\n",
+				__func__, edge);
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	key = "qcom,irq-bitmask";
+	ret = of_property_read_u32(node, key, &irq_bitmask);
+	if (ret)
+		goto missing_key;
+
+	key = "interrupts";
+	irq_line = platform_get_irq(pdev, 0);
+	if (irq_line == -ENXIO)
+		goto missing_key;
+
+	/*
+	 * We depend on the SMEM driver, so do a test access to see if SMEM is
+	 * ready.  We don't want any side effects at this time (so no alloc)
+	 * and the return doesn't matter, so long as it is not -EPROBE_DEFER.
+	 */
+	temp_p = smem_get_entry(
+		smp2p_get_smem_item_id(SMP2P_APPS_PROC, SMP2P_MODEM_PROC),
+		&temp_sz,
+		0,
+		SMEM_ANY_HOST_FLAG);
+	if (PTR_ERR(temp_p) == -EPROBE_DEFER) {
+		SMP2P_INFO("%s: edge:%d probe before smem ready\n", __func__,
+									edge);
+		ret = -EPROBE_DEFER;
+		goto fail;
+	}
+
+	ret = request_irq(irq_line, smp2p_interrupt_handler,
+			IRQF_TRIGGER_RISING, "smp2p", (void *)(uintptr_t)edge);
+	if (ret < 0) {
+		SMP2P_ERR("%s: request_irq() failed on %d (edge %d)\n",
+				__func__, irq_line, edge);
+		ret = -ENODEV;
+		goto fail;
+	}
+
+	ret = enable_irq_wake(irq_line);
+	if (ret < 0)
+		SMP2P_ERR("%s: enable_irq_wake() failed on %d (edge %d)\n",
+				__func__, irq_line, edge);
+
+	/*
+	 * Set entry (keep is_configured last to prevent usage before
+	 * initialization).
+	 */
+	smp2p_int_cfgs[edge].in_int_id = irq_line;
+	smp2p_int_cfgs[edge].out_int_mask = irq_bitmask;
+	smp2p_int_cfgs[edge].out_int_ptr = irq_out_ptr;
+	smp2p_int_cfgs[edge].is_configured = true;
+	return 0;
+
+missing_key:
+	SMP2P_ERR("%s: missing '%s' for edge %d\n", __func__, key, edge);
+	ret = -ENODEV;
+fail:
+	if (irq_out_ptr)
+		iounmap(irq_out_ptr);
+	return ret;
+}
+
+static const struct of_device_id msm_smp2p_match_table[] = {
+	{ .compatible = "qcom,smp2p" },
+	{},
+};
+
+static struct platform_driver msm_smp2p_driver = {
+	.probe = msm_smp2p_probe,
+	.driver = {
+		.name = "msm_smp2p",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_smp2p_match_table,
+	},
+};
+
+/**
+ * msm_smp2p_init -  Initialization function for the module.
+ *
+ * @returns: 0 on success, standard Linux error code otherwise.
+ */
+static int __init msm_smp2p_init(void)
+{
+	int i;
+	int rc;
+
+	for (i = 0; i < SMP2P_NUM_PROCS; i++) {
+		spin_lock_init(&out_list[i].out_item_lock_lha1);
+		INIT_LIST_HEAD(&out_list[i].list);
+		out_list[i].smem_edge_out = NULL;
+		out_list[i].smem_edge_state = SMP2P_EDGE_STATE_CLOSED;
+		out_list[i].ops_ptr = &version_if[0];
+		out_list[i].feature_ssr_ack_enabled = false;
+		out_list[i].restart_ack = false;
+
+		spin_lock_init(&in_list[i].in_item_lock_lhb1);
+		INIT_LIST_HEAD(&in_list[i].list);
+		in_list[i].smem_edge_in = NULL;
+	}
+
+	log_ctx = ipc_log_context_create(NUM_LOG_PAGES, "smp2p", 0);
+	if (!log_ctx)
+		SMP2P_ERR("%s: unable to create log context\n", __func__);
+
+	rc = platform_driver_register(&msm_smp2p_driver);
+	if (rc) {
+		SMP2P_ERR("%s: msm_smp2p_driver register failed %d\n",
+			__func__, rc);
+		return rc;
+	}
+
+	return 0;
+}
+module_init(msm_smp2p_init);
+
+MODULE_DESCRIPTION("MSM Shared Memory Point to Point");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/qcom/smp2p_debug.c b/drivers/soc/qcom/smp2p_debug.c
new file mode 100644
index 0000000..8d98d07
--- /dev/null
+++ b/drivers/soc/qcom/smp2p_debug.c
@@ -0,0 +1,335 @@
+/* drivers/soc/qcom/smp2p_debug.c
+ *
+ * Copyright (c) 2013-2014,2016 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 <linux/io.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_puts(s, "| Processor | Incoming Id | Incoming # |");
+	seq_puts(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 | %pK | %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 #/Max: %d/%d Flags: %c%c",
+		SMP2P_GET_ENT_VALID(item_ptr->valid_total_ent),
+		SMP2P_GET_ENT_TOTAL(item_ptr->valid_total_ent),
+		item_ptr->flags & SMP2P_FLAGS_RESTART_ACK_MASK ? 'A' : 'a',
+		item_ptr->flags & SMP2P_FLAGS_RESTART_DONE_MASK ? 'D' : 'd'
+		);
+
+	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;
+	char entry_name[SMP2P_MAX_ENTRY_NAME];
+
+	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(in_ptr->valid_total_ent);
+	}
+
+	for (entry = 0; out_entries || in_entries; ++entry) {
+		if (out_entries && entry < out_valid) {
+			memcpy_fromio(entry_name, out_entries->name,
+							SMP2P_MAX_ENTRY_NAME);
+			scnprintf(tmp_buff, sizeof(tmp_buff),
+					"%-16s 0x%08x",
+					entry_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) {
+			memcpy_fromio(entry_name, in_entries->name,
+							SMP2P_MAX_ENTRY_NAME);
+			scnprintf(tmp_buff, sizeof(tmp_buff),
+					"%-16s 0x%08x",
+					entry_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 */
diff --git a/drivers/soc/qcom/smp2p_loopback.c b/drivers/soc/qcom/smp2p_loopback.c
new file mode 100644
index 0000000..0086381
--- /dev/null
+++ b/drivers/soc/qcom/smp2p_loopback.c
@@ -0,0 +1,449 @@
+/* drivers/soc/qcom/smp2p_loopback.c
+ *
+ * Copyright (c) 2013-2014,2016 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 "smem_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 int 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;
+	struct smp2p_interrupt_config *int_cfg;
+
+	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;
+
+		int_cfg = smp2p_get_interrupt_config();
+		if (!int_cfg) {
+			SMP2P_ERR("Remote processor config unavailable\n");
+			return 0;
+		}
+		if (!int_cfg[i].is_configured)
+			continue;
+
+		msm_smp2p_init_rmt_lpb(&remote_loopback[i],
+			i, SMP2P_RLPB_ENTRY_NAME);
+	}
+	return 0;
+}
+module_init(smp2p_remote_mock_init);
diff --git a/drivers/soc/qcom/smp2p_private.h b/drivers/soc/qcom/smp2p_private.h
new file mode 100644
index 0000000..b332f7b
--- /dev/null
+++ b/drivers/soc/qcom/smp2p_private.h
@@ -0,0 +1,253 @@
+/* drivers/soc/qcom/smp2p_private.h
+ *
+ * Copyright (c) 2013-2014,2016 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.
+ */
+#ifndef _ARCH_ARM_MACH_MSM_MSM_SMP2P_PRIVATE_H_
+#define _ARCH_ARM_MACH_MSM_MSM_SMP2P_PRIVATE_H_
+
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/ipc_logging.h>
+#include "smp2p_private_api.h"
+
+#define SMP2P_MAX_ENTRY 16
+#define SMP2P_FEATURE_SSR_ACK 0x1
+
+/* SMEM Item Header Macros */
+#define SMP2P_MAGIC 0x504D5324
+#define SMP2P_LOCAL_PID_MASK 0x0000ffff
+#define SMP2P_LOCAL_PID_BIT 0
+#define SMP2P_REMOTE_PID_MASK 0xffff0000
+#define SMP2P_REMOTE_PID_BIT 16
+#define SMP2P_VERSION_MASK 0x000000ff
+#define SMP2P_VERSION_BIT 0
+#define SMP2P_FEATURE_MASK 0xffffff00
+#define SMP2P_FEATURE_BIT 8
+#define SMP2P_ENT_TOTAL_MASK 0x0000ffff
+#define SMP2P_ENT_TOTAL_BIT 0
+#define SMP2P_ENT_VALID_MASK 0xffff0000
+#define SMP2P_ENT_VALID_BIT 16
+#define SMP2P_FLAGS_RESTART_DONE_BIT 0
+#define SMP2P_FLAGS_RESTART_DONE_MASK 0x1
+#define SMP2P_FLAGS_RESTART_ACK_BIT 1
+#define SMP2P_FLAGS_RESTART_ACK_MASK 0x2
+#define SMP2P_GPIO_NO_INT BIT(1)
+
+#define SMP2P_GET_BITS(hdr_val, mask, bit) \
+	(((hdr_val) & (mask)) >> (bit))
+#define SMP2P_SET_BITS(hdr_val, mask, bit, new_value) \
+	{\
+		hdr_val = (hdr_val & ~(mask)) \
+		| (((new_value) << (bit)) & (mask)); \
+	}
+
+#define SMP2P_GET_LOCAL_PID(hdr) \
+	SMP2P_GET_BITS(hdr, SMP2P_LOCAL_PID_MASK, SMP2P_LOCAL_PID_BIT)
+#define SMP2P_SET_LOCAL_PID(hdr, pid) \
+	SMP2P_SET_BITS(hdr, SMP2P_LOCAL_PID_MASK, SMP2P_LOCAL_PID_BIT, pid)
+
+#define SMP2P_GET_REMOTE_PID(hdr) \
+	SMP2P_GET_BITS(hdr, SMP2P_REMOTE_PID_MASK, SMP2P_REMOTE_PID_BIT)
+#define SMP2P_SET_REMOTE_PID(hdr, pid) \
+	SMP2P_SET_BITS(hdr, SMP2P_REMOTE_PID_MASK, SMP2P_REMOTE_PID_BIT, pid)
+
+#define SMP2P_GET_VERSION(hdr) \
+	SMP2P_GET_BITS(hdr, SMP2P_VERSION_MASK, SMP2P_VERSION_BIT)
+#define SMP2P_SET_VERSION(hdr, version) \
+	SMP2P_SET_BITS(hdr, SMP2P_VERSION_MASK, SMP2P_VERSION_BIT, version)
+
+#define SMP2P_GET_FEATURES(hdr) \
+	SMP2P_GET_BITS(hdr, SMP2P_FEATURE_MASK, SMP2P_FEATURE_BIT)
+#define SMP2P_SET_FEATURES(hdr, features) \
+	SMP2P_SET_BITS(hdr, SMP2P_FEATURE_MASK, SMP2P_FEATURE_BIT, features)
+
+#define SMP2P_GET_ENT_TOTAL(hdr) \
+	SMP2P_GET_BITS(hdr, SMP2P_ENT_TOTAL_MASK, SMP2P_ENT_TOTAL_BIT)
+#define SMP2P_SET_ENT_TOTAL(hdr, entries) \
+	SMP2P_SET_BITS(hdr, SMP2P_ENT_TOTAL_MASK, SMP2P_ENT_TOTAL_BIT, entries)
+
+#define SMP2P_GET_ENT_VALID(hdr) \
+	SMP2P_GET_BITS(hdr, SMP2P_ENT_VALID_MASK, SMP2P_ENT_VALID_BIT)
+#define SMP2P_SET_ENT_VALID(hdr, entries) \
+	SMP2P_SET_BITS(hdr,  SMP2P_ENT_VALID_MASK, SMP2P_ENT_VALID_BIT,\
+		entries)
+
+#define SMP2P_GET_RESTART_DONE(hdr) \
+	SMP2P_GET_BITS(hdr, SMP2P_FLAGS_RESTART_DONE_MASK, \
+			SMP2P_FLAGS_RESTART_DONE_BIT)
+#define SMP2P_SET_RESTART_DONE(hdr, value) \
+	SMP2P_SET_BITS(hdr, SMP2P_FLAGS_RESTART_DONE_MASK, \
+			SMP2P_FLAGS_RESTART_DONE_BIT, value)
+
+#define SMP2P_GET_RESTART_ACK(hdr) \
+	SMP2P_GET_BITS(hdr, SMP2P_FLAGS_RESTART_ACK_MASK, \
+			SMP2P_FLAGS_RESTART_ACK_BIT)
+#define SMP2P_SET_RESTART_ACK(hdr, value) \
+	SMP2P_SET_BITS(hdr, SMP2P_FLAGS_RESTART_ACK_MASK, \
+			SMP2P_FLAGS_RESTART_ACK_BIT, value)
+
+/* Loopback Command Macros */
+#define SMP2P_RMT_CMD_TYPE_MASK 0x80000000
+#define SMP2P_RMT_CMD_TYPE_BIT 31
+#define SMP2P_RMT_IGNORE_MASK 0x40000000
+#define SMP2P_RMT_IGNORE_BIT 30
+#define SMP2P_RMT_CMD_MASK 0x3f000000
+#define SMP2P_RMT_CMD_BIT 24
+#define SMP2P_RMT_DATA_MASK 0x00ffffff
+#define SMP2P_RMT_DATA_BIT 0
+
+#define SMP2P_GET_RMT_CMD_TYPE(val) \
+	SMP2P_GET_BITS(val, SMP2P_RMT_CMD_TYPE_MASK, SMP2P_RMT_CMD_TYPE_BIT)
+#define SMP2P_GET_RMT_CMD(val) \
+	SMP2P_GET_BITS(val, SMP2P_RMT_CMD_MASK, SMP2P_RMT_CMD_BIT)
+
+#define SMP2P_GET_RMT_DATA(val) \
+	SMP2P_GET_BITS(val, SMP2P_RMT_DATA_MASK, SMP2P_RMT_DATA_BIT)
+
+#define SMP2P_SET_RMT_CMD_TYPE(val, cmd_type) \
+	SMP2P_SET_BITS(val, SMP2P_RMT_CMD_TYPE_MASK, SMP2P_RMT_CMD_TYPE_BIT, \
+		cmd_type)
+#define SMP2P_SET_RMT_CMD_TYPE_REQ(val) \
+	SMP2P_SET_RMT_CMD_TYPE(val, 1)
+#define SMP2P_SET_RMT_CMD_TYPE_RESP(val) \
+	SMP2P_SET_RMT_CMD_TYPE(val, 0)
+
+#define SMP2P_SET_RMT_CMD(val, cmd) \
+	SMP2P_SET_BITS(val, SMP2P_RMT_CMD_MASK, SMP2P_RMT_CMD_BIT, \
+		cmd)
+#define SMP2P_SET_RMT_DATA(val, data) \
+	SMP2P_SET_BITS(val, SMP2P_RMT_DATA_MASK, SMP2P_RMT_DATA_BIT, data)
+
+enum {
+	SMP2P_LB_CMD_NOOP = 0x0,
+	SMP2P_LB_CMD_ECHO,
+	SMP2P_LB_CMD_CLEARALL,
+	SMP2P_LB_CMD_PINGPONG,
+	SMP2P_LB_CMD_RSPIN_START,
+	SMP2P_LB_CMD_RSPIN_LOCKED,
+	SMP2P_LB_CMD_RSPIN_UNLOCKED,
+	SMP2P_LB_CMD_RSPIN_END,
+};
+#define SMP2P_RLPB_IGNORE 0x40
+#define SMP2P_RLPB_ENTRY_NAME "smp2p"
+
+/* Debug Logging Macros */
+enum {
+	MSM_SMP2P_INFO = 1U << 0,
+	MSM_SMP2P_DEBUG = 1U << 1,
+	MSM_SMP2P_GPIO = 1U << 2,
+};
+
+#define SMP2P_IPC_LOG_STR(x...) do { \
+	if (smp2p_get_log_ctx()) \
+		ipc_log_string(smp2p_get_log_ctx(), x); \
+} while (0)
+
+#define SMP2P_DBG(x...) do {                              \
+	if (smp2p_get_debug_mask() & MSM_SMP2P_DEBUG) \
+		SMP2P_IPC_LOG_STR(x);  \
+} while (0)
+
+#define SMP2P_INFO(x...) do {                              \
+	if (smp2p_get_debug_mask() & MSM_SMP2P_INFO) \
+		SMP2P_IPC_LOG_STR(x);  \
+} while (0)
+
+#define SMP2P_ERR(x...) do {                              \
+	pr_err(x); \
+	SMP2P_IPC_LOG_STR(x);  \
+} while (0)
+
+#define SMP2P_GPIO(x...) do {                              \
+	if (smp2p_get_debug_mask() & MSM_SMP2P_GPIO) \
+		SMP2P_IPC_LOG_STR(x);  \
+} while (0)
+
+
+enum msm_smp2p_edge_state {
+	SMP2P_EDGE_STATE_CLOSED,
+	SMP2P_EDGE_STATE_OPENING,
+	SMP2P_EDGE_STATE_OPENED,
+	SMP2P_EDGE_STATE_FAILED = 0xff,
+};
+
+/**
+ * struct smp2p_smem - SMP2P SMEM Item Header
+ *
+ * @magic:  Set to "$SMP" -- used for identification / debug purposes
+ * @feature_version:  Feature and version fields
+ * @rem_loc_proc_id:  Remote (31:16) and Local (15:0) processor IDs
+ * @valid_total_ent:  Valid (31:16) and total (15:0) entries
+ * @flags:  Flags (bits 31:2 reserved)
+ */
+struct smp2p_smem {
+	uint32_t magic;
+	uint32_t feature_version;
+	uint32_t rem_loc_proc_id;
+	uint32_t valid_total_ent;
+	uint32_t flags;
+};
+
+struct smp2p_entry_v1 {
+	char name[SMP2P_MAX_ENTRY_NAME];
+	uint32_t entry;
+};
+
+struct smp2p_smem_item {
+	struct smp2p_smem header;
+	struct smp2p_entry_v1 entries[SMP2P_MAX_ENTRY];
+};
+
+/* Mock object for internal loopback testing. */
+struct msm_smp2p_remote_mock {
+	struct smp2p_smem_item remote_item;
+	int rx_interrupt_count;
+	int (*rx_interrupt)(void);
+	void (*tx_interrupt)(void);
+
+	bool item_exists;
+	bool initialized;
+	struct completion cb_completion;
+};
+
+void smp2p_init_header(struct smp2p_smem *header_ptr, int local_pid,
+		int remote_pid, uint32_t features, uint32_t version);
+void *msm_smp2p_get_remote_mock(void);
+int smp2p_remote_mock_rx_interrupt(void);
+int smp2p_reset_mock_edge(void);
+void msm_smp2p_interrupt_handler(int remote_pid);
+void msm_smp2p_set_remote_mock_exists(bool item_exists);
+void *msm_smp2p_get_remote_mock_smem_item(uint32_t *size);
+void *msm_smp2p_init_rmt_lpb_proc(int remote_pid);
+int msm_smp2p_deinit_rmt_lpb_proc(int remote_pid);
+void *smp2p_get_log_ctx(void);
+int smp2p_get_debug_mask(void);
+
+/* Inbound / outbound Interrupt configuration. */
+struct smp2p_interrupt_config {
+	bool is_configured;
+	uint32_t *out_int_ptr;
+	uint32_t out_int_mask;
+	int in_int_id;
+	const char *name;
+
+	/* interrupt stats */
+	unsigned int in_interrupt_count;
+	unsigned int out_interrupt_count;
+};
+
+struct smp2p_interrupt_config *smp2p_get_interrupt_config(void);
+const char *smp2p_pid_to_name(int remote_pid);
+struct smp2p_smem *smp2p_get_in_item(int remote_pid);
+struct smp2p_smem *smp2p_get_out_item(int remote_pid, int *state);
+void smp2p_gpio_open_test_entry(const char *name, int remote_pid, bool do_open);
+#endif
diff --git a/drivers/soc/qcom/smp2p_private_api.h b/drivers/soc/qcom/smp2p_private_api.h
new file mode 100644
index 0000000..5bff32f
--- /dev/null
+++ b/drivers/soc/qcom/smp2p_private_api.h
@@ -0,0 +1,80 @@
+/* drivers/soc/qcom/smp2p_private_api.h
+ *
+ * Copyright (c) 2013-2016, 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.
+ */
+#ifndef _ARCH_ARM_MACH_MSM_SMP2P_PRIVATE_API_H_
+#define _ARCH_ARM_MACH_MSM_SMP2P_PRIVATE_API_H_
+
+#include <linux/notifier.h>
+
+struct msm_smp2p_out;
+
+/* Maximum size of the entry name and trailing null. */
+#define SMP2P_MAX_ENTRY_NAME 16
+
+/* Bits per entry */
+#define SMP2P_BITS_PER_ENTRY 32
+
+/* Processor ID's */
+enum {
+	SMP2P_APPS_PROC       = 0,
+	SMP2P_MODEM_PROC      = 1,
+	SMP2P_AUDIO_PROC      = 2,
+	SMP2P_SENSOR_PROC     = 3,
+	SMP2P_WIRELESS_PROC   = 4,
+	SMP2P_CDSP_PROC       = 5,
+	SMP2P_POWER_PROC      = 6,
+	SMP2P_TZ_PROC         = 7,
+	/* add new processors here */
+
+	SMP2P_REMOTE_MOCK_PROC = 15,
+	SMP2P_NUM_PROCS,
+};
+
+/**
+ * Notification events that are passed to notifier for incoming and outgoing
+ * entries.
+ *
+ * If the @metadata argument in the notifier is non-null, then it will
+ * point to the associated struct smux_meta_* structure.
+ */
+enum msm_smp2p_events {
+	SMP2P_OPEN,         /* data is NULL */
+	SMP2P_ENTRY_UPDATE, /* data => struct msm_smp2p_update_notif */
+};
+
+/**
+ * Passed in response to a SMP2P_ENTRY_UPDATE event.
+ *
+ * @prev_value:     previous value of entry
+ * @current_value:  latest value of entry
+ */
+struct msm_smp2p_update_notif {
+	uint32_t previous_value;
+	uint32_t current_value;
+};
+
+int msm_smp2p_out_open(int remote_pid, const char *entry,
+	struct notifier_block *open_notifier,
+	struct msm_smp2p_out **handle);
+int msm_smp2p_out_close(struct msm_smp2p_out **handle);
+int msm_smp2p_out_read(struct msm_smp2p_out *handle, uint32_t *data);
+int msm_smp2p_out_write(struct msm_smp2p_out *handle, uint32_t data);
+int msm_smp2p_out_modify(struct msm_smp2p_out *handle, uint32_t set_mask,
+	uint32_t clear_mask, bool send_irq);
+int msm_smp2p_in_read(int remote_pid, const char *entry, uint32_t *data);
+int msm_smp2p_in_register(int remote_pid, const char *entry,
+	struct notifier_block *in_notifier);
+int msm_smp2p_in_unregister(int remote_pid, const char *entry,
+	struct notifier_block *in_notifier);
+
+#endif /* _ARCH_ARM_MACH_MSM_SMP2P_PRIVATE_API_H_ */
diff --git a/drivers/soc/qcom/smp2p_sleepstate.c b/drivers/soc/qcom/smp2p_sleepstate.c
new file mode 100644
index 0000000..44192ff
--- /dev/null
+++ b/drivers/soc/qcom/smp2p_sleepstate.c
@@ -0,0 +1,106 @@
+/* Copyright (c) 2014-2016, 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/gpio.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/suspend.h>
+#include "smp2p_private.h"
+
+#define SET_DELAY (2 * HZ)
+#define PROC_AWAKE_ID 12 /* 12th bit */
+static int slst_gpio_base_id;
+
+/**
+ * sleepstate_pm_notifier() - PM notifier callback function.
+ * @nb:		Pointer to the notifier block.
+ * @event:	Suspend state event from PM module.
+ * @unused:	Null pointer from PM module.
+ *
+ * This function is register as callback function to get notifications
+ * from the PM module on the system suspend state.
+ */
+static int sleepstate_pm_notifier(struct notifier_block *nb,
+				unsigned long event, void *unused)
+{
+	switch (event) {
+	case PM_SUSPEND_PREPARE:
+		gpio_set_value(slst_gpio_base_id + PROC_AWAKE_ID, 0);
+		break;
+
+	case PM_POST_SUSPEND:
+		gpio_set_value(slst_gpio_base_id + PROC_AWAKE_ID, 1);
+		break;
+	}
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block sleepstate_pm_nb = {
+	.notifier_call = sleepstate_pm_notifier,
+};
+
+static int smp2p_sleepstate_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct device_node *node = pdev->dev.of_node;
+
+	slst_gpio_base_id = of_get_gpio(node, 0);
+	if (slst_gpio_base_id == -EPROBE_DEFER) {
+		return slst_gpio_base_id;
+	} else if (slst_gpio_base_id < 0) {
+		SMP2P_ERR("%s: Error to get gpio %d\n",
+				__func__, slst_gpio_base_id);
+		return slst_gpio_base_id;
+	}
+
+
+	gpio_set_value(slst_gpio_base_id + PROC_AWAKE_ID, 1);
+
+	ret = register_pm_notifier(&sleepstate_pm_nb);
+	if (ret)
+		SMP2P_ERR("%s: power state notif error %d\n", __func__, ret);
+
+	return 0;
+}
+
+static const struct of_device_id msm_smp2p_slst_match_table[] = {
+	{.compatible = "qcom,smp2pgpio_sleepstate_3_out"},
+	{.compatible = "qcom,smp2pgpio-sleepstate-out"},
+	{},
+};
+
+static struct platform_driver smp2p_sleepstate_driver = {
+	.probe = smp2p_sleepstate_probe,
+	.driver = {
+		.name = "smp2p_sleepstate",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_smp2p_slst_match_table,
+	},
+};
+
+static int __init smp2p_sleepstate_init(void)
+{
+	int ret;
+
+	ret = platform_driver_register(&smp2p_sleepstate_driver);
+	if (ret) {
+		SMP2P_ERR("%s: smp2p_sleepstate_driver register failed %d\n",
+			 __func__, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+module_init(smp2p_sleepstate_init);
+MODULE_DESCRIPTION("SMP2P SLEEP STATE");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/qcom/smp2p_spinlock_test.c b/drivers/soc/qcom/smp2p_spinlock_test.c
new file mode 100644
index 0000000..56a0af6
--- /dev/null
+++ b/drivers/soc/qcom/smp2p_spinlock_test.c
@@ -0,0 +1,820 @@
+/* drivers/soc/qcom/smp2p_spinlock_test.c
+ *
+ * Copyright (c) 2013-2016, 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/ctype.h>
+#include <linux/jiffies.h>
+#include <linux/delay.h>
+#include <linux/completion.h>
+#include <linux/module.h>
+#include <linux/remote_spinlock.h>
+#include <soc/qcom/smem.h>
+#include "smem_private.h"
+#include "smp2p_private.h"
+#include "smp2p_test_common.h"
+
+#define RS_END_THIEF_PID_BIT 20
+#define RS_END_THIEF_MASK 0x00f00000
+
+/* Spinlock commands used for testing Apps<->RPM spinlocks. */
+enum RPM_SPINLOCK_CMDS {
+	RPM_CMD_INVALID,
+	RPM_CMD_START,
+	RPM_CMD_LOCKED,
+	RPM_CMD_UNLOCKED,
+	RPM_CMD_END,
+};
+
+/* Shared structure for testing Apps<->RPM spinlocks. */
+struct rpm_spinlock_test {
+	uint32_t apps_cmd;
+	uint32_t apps_lock_count;
+	uint32_t rpm_cmd;
+	uint32_t rpm_lock_count;
+};
+
+static uint32_t ut_remote_spinlock_run_time = 1;
+
+/**
+ * smp2p_ut_remote_spinlock_core - Verify remote spinlock.
+ *
+ * @s:           Pointer to output file
+ * @remote_pid:  Remote processor to test
+ * @use_trylock: Use trylock to prevent an Apps deadlock if the
+ *               remote spinlock fails.
+ */
+static void smp2p_ut_remote_spinlock_core(struct seq_file *s, int remote_pid,
+		bool use_trylock)
+{
+	int failed = 0;
+	unsigned int lock_count = 0;
+	struct msm_smp2p_out *handle = NULL;
+	int ret;
+	uint32_t test_request;
+	uint32_t test_response;
+	struct mock_cb_data cb_out;
+	struct mock_cb_data cb_in;
+	unsigned long flags;
+	unsigned int n;
+	bool have_lock;
+	bool timeout;
+	int failed_tmp;
+	int spinlock_owner;
+	remote_spinlock_t *smem_spinlock;
+	unsigned long end;
+
+	seq_printf(s, "Running %s for '%s' remote pid %d\n",
+		   __func__, smp2p_pid_to_name(remote_pid), remote_pid);
+
+	cb_out.initialized = false;
+	cb_in.initialized = false;
+	mock_cb_data_init(&cb_out);
+	mock_cb_data_init(&cb_in);
+	do {
+		smem_spinlock = smem_get_remote_spinlock();
+		UT_ASSERT_PTR(smem_spinlock, !=, NULL);
+
+		/* Open output entry */
+		ret = msm_smp2p_out_open(remote_pid, SMP2P_RLPB_ENTRY_NAME,
+			&cb_out.nb, &handle);
+		UT_ASSERT_INT(ret, ==, 0);
+		UT_ASSERT_INT(
+			(int)wait_for_completion_timeout(
+					&cb_out.cb_completion, HZ * 2),
+			>, 0);
+		UT_ASSERT_INT(cb_out.cb_count, ==, 1);
+		UT_ASSERT_INT(cb_out.event_open, ==, 1);
+
+		/* Open inbound entry */
+		ret = msm_smp2p_in_register(remote_pid, SMP2P_RLPB_ENTRY_NAME,
+				&cb_in.nb);
+		UT_ASSERT_INT(ret, ==, 0);
+		UT_ASSERT_INT(
+			(int)wait_for_completion_timeout(
+					&cb_in.cb_completion, HZ * 2),
+			>, 0);
+		UT_ASSERT_INT(cb_in.cb_count, ==, 1);
+		UT_ASSERT_INT(cb_in.event_open, ==, 1);
+
+		/* Send start */
+		mock_cb_data_reset(&cb_in);
+		mock_cb_data_reset(&cb_out);
+		test_request = 0x0;
+		SMP2P_SET_RMT_CMD_TYPE_REQ(test_request);
+		SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_START);
+		SMP2P_SET_RMT_DATA(test_request, 0x0);
+		ret = msm_smp2p_out_write(handle, test_request);
+		UT_ASSERT_INT(ret, ==, 0);
+
+		UT_ASSERT_INT(
+			(int)wait_for_completion_timeout(
+					&cb_in.cb_completion, HZ * 2),
+			>, 0);
+		UT_ASSERT_INT(cb_in.cb_count, ==, 1);
+		UT_ASSERT_INT(cb_in.event_entry_update, ==, 1);
+		ret = msm_smp2p_in_read(remote_pid, SMP2P_RLPB_ENTRY_NAME,
+				&test_response);
+		UT_ASSERT_INT(ret, ==, 0);
+
+		test_response = SMP2P_GET_RMT_CMD(test_response);
+		if (test_response != SMP2P_LB_CMD_RSPIN_LOCKED &&
+				test_response != SMP2P_LB_CMD_RSPIN_UNLOCKED) {
+			/* invalid response from remote - abort test */
+			test_request = 0x0;
+			SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
+			SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_END);
+			SMP2P_SET_RMT_DATA(test_request, 0x0);
+			ret = msm_smp2p_out_write(handle, test_request);
+			UT_ASSERT_HEX(SMP2P_LB_CMD_RSPIN_LOCKED, ==,
+					test_response);
+		}
+
+		/* Run spinlock test */
+		if (use_trylock)
+			seq_puts(s, "\tUsing remote_spin_trylock\n");
+		else
+			seq_puts(s, "\tUsing remote_spin_lock\n");
+
+		flags = 0;
+		have_lock = false;
+		timeout = false;
+		spinlock_owner = 0;
+		test_request = 0x0;
+		SMP2P_SET_RMT_CMD_TYPE_REQ(test_request);
+		end = jiffies + (ut_remote_spinlock_run_time * HZ);
+		if (ut_remote_spinlock_run_time < 300) {
+			seq_printf(s, "\tRunning test for %u seconds; ",
+				ut_remote_spinlock_run_time);
+			seq_puts(s,
+				"on physical hardware please run >= 300 seconds by doing 'echo 300 >  ut_remote_spinlock_time'\n");
+		}
+		while (time_is_after_jiffies(end)) {
+			/* try to acquire spinlock */
+			if (use_trylock) {
+				unsigned long j_start = jiffies;
+
+				while (!remote_spin_trylock_irqsave(
+						smem_spinlock, flags)) {
+					if (jiffies_to_msecs(jiffies - j_start)
+							> 1000) {
+						seq_puts(s,
+							"\tFail: Timeout trying to get the lock\n");
+						timeout = true;
+						break;
+					}
+				}
+				if (timeout)
+					break;
+			} else {
+				remote_spin_lock_irqsave(smem_spinlock, flags);
+			}
+			have_lock = true;
+			++lock_count;
+
+			/* tell the remote side that we have the lock */
+			SMP2P_SET_RMT_DATA(test_request, lock_count);
+			SMP2P_SET_RMT_CMD(test_request,
+					SMP2P_LB_CMD_RSPIN_LOCKED);
+			ret = msm_smp2p_out_write(handle, test_request);
+			UT_ASSERT_INT(ret, ==, 0);
+
+			/* verify the other side doesn't say it has the lock */
+			for (n = 0; n < 1000; ++n) {
+				spinlock_owner =
+					remote_spin_owner(smem_spinlock);
+				if (spinlock_owner != SMEM_APPS) {
+					/* lock stolen by remote side */
+					seq_puts(s, "\tFail: Remote side: ");
+					seq_printf(s, "%d stole lock pid: %d\n",
+						remote_pid, spinlock_owner);
+					failed = true;
+					break;
+				}
+				spinlock_owner = 0;
+
+				ret = msm_smp2p_in_read(remote_pid,
+					SMP2P_RLPB_ENTRY_NAME, &test_response);
+				UT_ASSERT_INT(ret, ==, 0);
+				test_response =
+					SMP2P_GET_RMT_CMD(test_response);
+				UT_ASSERT_HEX(SMP2P_LB_CMD_RSPIN_UNLOCKED, ==,
+					test_response);
+			}
+			if (failed)
+				break;
+
+			/* tell remote side we are unlocked and release lock */
+			SMP2P_SET_RMT_CMD(test_request,
+					SMP2P_LB_CMD_RSPIN_UNLOCKED);
+			(void)msm_smp2p_out_write(handle, test_request);
+			have_lock = false;
+			remote_spin_unlock_irqrestore(smem_spinlock, flags);
+		}
+		if (have_lock)
+			remote_spin_unlock_irqrestore(smem_spinlock, flags);
+
+		/* End test */
+		mock_cb_data_reset(&cb_in);
+		SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_END);
+		SMP2P_SET_RMT_DATA(test_request, lock_count |
+				(spinlock_owner << RS_END_THIEF_PID_BIT));
+		(void)msm_smp2p_out_write(handle, test_request);
+
+		failed_tmp = failed;
+		failed = false;
+		do {
+			UT_ASSERT_INT(
+				(int)wait_for_completion_timeout(
+					&cb_in.cb_completion, HZ * 2),
+				>, 0);
+			reinit_completion(&cb_in.cb_completion);
+			ret = msm_smp2p_in_read(remote_pid,
+					SMP2P_RLPB_ENTRY_NAME, &test_response);
+			UT_ASSERT_INT(ret, ==, 0);
+		} while (!failed &&
+			SMP2P_GET_RMT_CMD(test_response) !=
+			SMP2P_LB_CMD_RSPIN_END);
+		if (failed)
+			break;
+		failed = failed_tmp;
+
+		test_response = SMP2P_GET_RMT_DATA(test_response);
+		seq_puts(s, "\tLocked spinlock ");
+		seq_printf(s, "local %u times; remote %u times",
+			lock_count,
+			test_response & ((1 << RS_END_THIEF_PID_BIT) - 1)
+			);
+		if (test_response & RS_END_THIEF_MASK) {
+			seq_puts(s, "Remote side reporting lock stolen by ");
+			seq_printf(s, "pid %d.\n",
+				SMP2P_GET_BITS(test_response,
+					RS_END_THIEF_MASK,
+					RS_END_THIEF_PID_BIT));
+			failed = 1;
+		}
+		seq_puts(s, "\n");
+
+		/* Cleanup */
+		ret = msm_smp2p_out_close(&handle);
+		UT_ASSERT_INT(ret, ==, 0);
+		UT_ASSERT_PTR(handle, ==, NULL);
+		ret = msm_smp2p_in_unregister(remote_pid,
+				SMP2P_RLPB_ENTRY_NAME, &cb_in.nb);
+		UT_ASSERT_INT(ret, ==, 0);
+
+		if (!failed && !timeout)
+			seq_puts(s, "\tOK\n");
+	} while (0);
+
+	if (failed) {
+		if (handle) {
+			/* send end command */
+			test_request = 0;
+			SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_END);
+			SMP2P_SET_RMT_DATA(test_request, lock_count);
+			(void)msm_smp2p_out_write(handle, test_request);
+			(void)msm_smp2p_out_close(&handle);
+		}
+		(void)msm_smp2p_in_unregister(remote_pid,
+				SMP2P_RLPB_ENTRY_NAME, &cb_in.nb);
+
+		pr_err("%s: Failed\n", __func__);
+		seq_puts(s, "\tFailed\n");
+	}
+}
+
+/**
+ * smp2p_ut_remote_spinlock_pid - Verify remote spinlock for a processor.
+ *
+ * @s:           Pointer to output file
+ * @pid:         Processor to test
+ * @use_trylock: Use trylock to prevent an Apps deadlock if the
+ *               remote spinlock fails.
+ */
+static void smp2p_ut_remote_spinlock_pid(struct seq_file *s, int pid,
+		bool use_trylock)
+{
+	struct smp2p_interrupt_config *int_cfg;
+
+	int_cfg = smp2p_get_interrupt_config();
+	if (!int_cfg) {
+		seq_puts(s, "Remote processor config unavailable\n");
+		return;
+	}
+
+	if (pid >= SMP2P_NUM_PROCS || !int_cfg[pid].is_configured)
+		return;
+
+	msm_smp2p_deinit_rmt_lpb_proc(pid);
+	smp2p_ut_remote_spinlock_core(s, pid, use_trylock);
+	msm_smp2p_init_rmt_lpb_proc(pid);
+}
+
+/**
+ * smp2p_ut_remote_spinlock - Verify remote spinlock for all processors.
+ *
+ * @s:   pointer to output file
+ */
+static void smp2p_ut_remote_spinlock(struct seq_file *s)
+{
+	int pid;
+
+	for (pid = 0; pid < SMP2P_NUM_PROCS; ++pid)
+		smp2p_ut_remote_spinlock_pid(s, pid, false);
+}
+
+/**
+ * smp2p_ut_remote_spin_trylock - Verify remote trylock for all processors.
+ *
+ * @s:   Pointer to output file
+ */
+static void smp2p_ut_remote_spin_trylock(struct seq_file *s)
+{
+	int pid;
+
+	for (pid = 0; pid < SMP2P_NUM_PROCS; ++pid)
+		smp2p_ut_remote_spinlock_pid(s, pid, true);
+}
+
+/**
+ * smp2p_ut_remote_spinlock - Verify remote spinlock for all processors.
+ *
+ * @s:   pointer to output file
+ *
+ * This test verifies inbound and outbound functionality for all
+ * configured remote processor.
+ */
+static void smp2p_ut_remote_spinlock_modem(struct seq_file *s)
+{
+	smp2p_ut_remote_spinlock_pid(s, SMP2P_MODEM_PROC, false);
+}
+
+static void smp2p_ut_remote_spinlock_adsp(struct seq_file *s)
+{
+	smp2p_ut_remote_spinlock_pid(s, SMP2P_AUDIO_PROC, false);
+}
+
+static void smp2p_ut_remote_spinlock_dsps(struct seq_file *s)
+{
+	smp2p_ut_remote_spinlock_pid(s, SMP2P_SENSOR_PROC, false);
+}
+
+static void smp2p_ut_remote_spinlock_wcnss(struct seq_file *s)
+{
+	smp2p_ut_remote_spinlock_pid(s, SMP2P_WIRELESS_PROC, false);
+}
+
+static void smp2p_ut_remote_spinlock_cdsp(struct seq_file *s)
+{
+	smp2p_ut_remote_spinlock_pid(s, SMP2P_CDSP_PROC, false);
+}
+
+static void smp2p_ut_remote_spinlock_tz(struct seq_file *s)
+{
+	smp2p_ut_remote_spinlock_pid(s, SMP2P_TZ_PROC, false);
+}
+
+/**
+ * smp2p_ut_remote_spinlock_rpm - Verify remote spinlock.
+ *
+ * @s:   pointer to output file
+ * @remote_pid:  Remote processor to test
+ */
+static void smp2p_ut_remote_spinlock_rpm(struct seq_file *s)
+{
+	int failed = 0;
+	unsigned long flags;
+	unsigned int n;
+	unsigned int test_num;
+	struct rpm_spinlock_test *data_ptr;
+	remote_spinlock_t *smem_spinlock;
+	bool have_lock;
+
+	seq_printf(s, "Running %s for Apps<->RPM Test\n",
+		   __func__);
+	do {
+		smem_spinlock = smem_get_remote_spinlock();
+		UT_ASSERT_PTR(smem_spinlock, !=, NULL);
+
+		data_ptr = smem_alloc(SMEM_ID_VENDOR0,
+				sizeof(struct rpm_spinlock_test), 0,
+				SMEM_ANY_HOST_FLAG);
+		UT_ASSERT_PTR(0, !=, data_ptr);
+
+		/* Send start */
+		writel_relaxed(0, &data_ptr->apps_lock_count);
+		writel_relaxed(RPM_CMD_START, &data_ptr->apps_cmd);
+
+		seq_puts(s, "\tWaiting for RPM to start test\n");
+		for (n = 0; n < 1000; ++n) {
+			if (readl_relaxed(&data_ptr->rpm_cmd) !=
+					RPM_CMD_INVALID)
+				break;
+			usleep_range(1000, 1200);
+		}
+		if (readl_relaxed(&data_ptr->rpm_cmd) == RPM_CMD_INVALID) {
+			/* timeout waiting for RPM */
+			writel_relaxed(RPM_CMD_INVALID, &data_ptr->apps_cmd);
+			UT_ASSERT_INT(RPM_CMD_LOCKED, !=, RPM_CMD_INVALID);
+		}
+
+		/* Run spinlock test */
+		flags = 0;
+		have_lock = false;
+		for (test_num = 0; !failed && test_num < 10000; ++test_num) {
+			/* acquire spinlock */
+			remote_spin_lock_irqsave(smem_spinlock, flags);
+			have_lock = true;
+			data_ptr->apps_lock_count++;
+			writel_relaxed(data_ptr->apps_lock_count,
+				&data_ptr->apps_lock_count);
+			writel_relaxed(RPM_CMD_LOCKED, &data_ptr->apps_cmd);
+			/*
+			 * Ensure that the remote side sees our lock has
+			 * been acquired before we start polling their status.
+			 */
+			wmb();
+
+			/* verify the other side doesn't say it has the lock */
+			for (n = 0; n < 1000; ++n) {
+				UT_ASSERT_HEX(RPM_CMD_UNLOCKED, ==,
+					readl_relaxed(&data_ptr->rpm_cmd));
+			}
+			if (failed)
+				break;
+
+			/* release spinlock */
+			have_lock = false;
+			writel_relaxed(RPM_CMD_UNLOCKED, &data_ptr->apps_cmd);
+			/*
+			 * Ensure that our status-update write was committed
+			 * before we unlock the spinlock.
+			 */
+			wmb();
+			remote_spin_unlock_irqrestore(smem_spinlock, flags);
+		}
+		if (have_lock)
+			remote_spin_unlock_irqrestore(smem_spinlock, flags);
+
+		/* End test */
+		writel_relaxed(RPM_CMD_INVALID, &data_ptr->apps_cmd);
+		seq_printf(s, "\tLocked spinlock local %u remote %u\n",
+				readl_relaxed(&data_ptr->apps_lock_count),
+				readl_relaxed(&data_ptr->rpm_lock_count));
+
+		if (!failed)
+			seq_puts(s, "\tOK\n");
+	} while (0);
+
+	if (failed) {
+		pr_err("%s: Failed\n", __func__);
+		seq_puts(s, "\tFailed\n");
+	}
+}
+
+struct rmt_spinlock_work_item {
+	struct work_struct work;
+	struct completion try_lock;
+	struct completion locked;
+	bool has_locked;
+};
+
+static void ut_remote_spinlock_ssr_worker(struct work_struct *work)
+{
+	remote_spinlock_t *smem_spinlock;
+	unsigned long flags;
+	struct rmt_spinlock_work_item *work_item =
+		container_of(work, struct rmt_spinlock_work_item, work);
+
+	work_item->has_locked = false;
+	complete(&work_item->try_lock);
+	smem_spinlock = smem_get_remote_spinlock();
+	if (!smem_spinlock) {
+		pr_err("%s Failed\n", __func__);
+		return;
+	}
+
+	remote_spin_lock_irqsave(smem_spinlock, flags);
+	remote_spin_unlock_irqrestore(smem_spinlock, flags);
+	work_item->has_locked = true;
+	complete(&work_item->locked);
+}
+
+/**
+ * smp2p_ut_remote_spinlock_ssr - Verify remote spinlock.
+ *
+ * @s:   pointer to output file
+ */
+static void smp2p_ut_remote_spinlock_ssr(struct seq_file *s)
+{
+	int failed = 0;
+	unsigned long flags;
+	remote_spinlock_t *smem_spinlock;
+	int spinlock_owner = 0;
+
+	struct workqueue_struct *ws = NULL;
+	struct rmt_spinlock_work_item work_item;
+
+	seq_printf(s, " Running %s Test\n",
+		   __func__);
+	do {
+		smem_spinlock = smem_get_remote_spinlock();
+		UT_ASSERT_PTR(smem_spinlock, !=, NULL);
+
+		ws = create_singlethread_workqueue("ut_remote_spinlock_ssr");
+		UT_ASSERT_PTR(ws, !=, NULL);
+		INIT_WORK(&work_item.work, ut_remote_spinlock_ssr_worker);
+		init_completion(&work_item.try_lock);
+		init_completion(&work_item.locked);
+
+		remote_spin_lock_irqsave(smem_spinlock, flags);
+		/* Unlock local spin lock and hold HW spinlock */
+		spin_unlock_irqrestore(&((smem_spinlock)->local), flags);
+
+		queue_work(ws, &work_item.work);
+		UT_ASSERT_INT(
+			(int)wait_for_completion_timeout(
+					&work_item.try_lock, HZ * 2), >, 0);
+		UT_ASSERT_INT((int)work_item.has_locked, ==, 0);
+		spinlock_owner = remote_spin_owner(smem_spinlock);
+		UT_ASSERT_INT(spinlock_owner, ==, SMEM_APPS);
+		remote_spin_release_all(SMEM_APPS);
+
+		UT_ASSERT_INT(
+			(int)wait_for_completion_timeout(
+					&work_item.locked, HZ * 2), >, 0);
+
+		if (!failed)
+			seq_puts(s, "\tOK\n");
+	} while (0);
+
+	if (failed) {
+		pr_err("%s: Failed\n", __func__);
+		seq_puts(s, "\tFailed\n");
+	}
+}
+
+/**
+ * smp2p_ut_remote_spinlock_track_core - Verify remote spinlock.
+ *
+ * @s:           Pointer to output file
+ * @remote_pid:  Remote processor to test
+ *
+ * This test has the remote subsystem grab the lock, and then has the local
+ * subsystem attempt to grab the lock using the trylock() API. It then verifies
+ * that the ID in the hw_spinlocks array matches the owner of the lock.
+ */
+static void smp2p_ut_remote_spinlock_track_core(struct seq_file *s,
+		int remote_pid)
+{
+	int failed = 0;
+	struct msm_smp2p_out *handle = NULL;
+	int ret;
+	uint32_t test_request;
+	uint32_t test_response;
+	struct mock_cb_data cb_out;
+	struct mock_cb_data cb_in;
+	unsigned long flags;
+	int stored_value;
+	remote_spinlock_t *smem_spinlock;
+
+	seq_printf(s, "Running %s for '%s' remote pid %d\n",
+		   __func__, smp2p_pid_to_name(remote_pid), remote_pid);
+
+	cb_out.initialized = false;
+	cb_in.initialized = false;
+	mock_cb_data_init(&cb_out);
+	mock_cb_data_init(&cb_in);
+	do {
+		smem_spinlock = smem_get_remote_spinlock();
+		UT_ASSERT_PTR(smem_spinlock, !=, NULL);
+
+		/* Open output entry */
+		ret = msm_smp2p_out_open(remote_pid, SMP2P_RLPB_ENTRY_NAME,
+			&cb_out.nb, &handle);
+		UT_ASSERT_INT(ret, ==, 0);
+		UT_ASSERT_INT(
+			(int)wait_for_completion_timeout(
+					&cb_out.cb_completion, HZ * 2),
+			>, 0);
+		UT_ASSERT_INT(cb_out.cb_count, ==, 1);
+		UT_ASSERT_INT(cb_out.event_open, ==, 1);
+
+		/* Open inbound entry */
+		ret = msm_smp2p_in_register(remote_pid, SMP2P_RLPB_ENTRY_NAME,
+				&cb_in.nb);
+		UT_ASSERT_INT(ret, ==, 0);
+		UT_ASSERT_INT(
+			(int)wait_for_completion_timeout(
+					&cb_in.cb_completion, HZ * 2),
+			>, 0);
+		UT_ASSERT_INT(cb_in.cb_count, ==, 1);
+		UT_ASSERT_INT(cb_in.event_open, ==, 1);
+
+		/* Send start */
+		mock_cb_data_reset(&cb_in);
+		mock_cb_data_reset(&cb_out);
+		test_request = 0x0;
+		SMP2P_SET_RMT_CMD_TYPE_REQ(test_request);
+		SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_START);
+		SMP2P_SET_RMT_DATA(test_request, 0x0);
+		ret = msm_smp2p_out_write(handle, test_request);
+		UT_ASSERT_INT(ret, ==, 0);
+
+		UT_ASSERT_INT(
+			(int)wait_for_completion_timeout(
+					&cb_in.cb_completion, HZ * 2),
+			>, 0);
+		UT_ASSERT_INT(cb_in.cb_count, ==, 1);
+		UT_ASSERT_INT(cb_in.event_entry_update, ==, 1);
+		ret = msm_smp2p_in_read(remote_pid, SMP2P_RLPB_ENTRY_NAME,
+				&test_response);
+		UT_ASSERT_INT(ret, ==, 0);
+
+		test_response = SMP2P_GET_RMT_CMD(test_response);
+		if (test_response != SMP2P_LB_CMD_RSPIN_LOCKED &&
+				test_response != SMP2P_LB_CMD_RSPIN_UNLOCKED) {
+			/* invalid response from remote - abort test */
+			test_request = 0x0;
+			SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
+			SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_END);
+			SMP2P_SET_RMT_DATA(test_request, 0x0);
+			ret = msm_smp2p_out_write(handle, test_request);
+			UT_ASSERT_HEX(SMP2P_LB_CMD_RSPIN_LOCKED, ==,
+					test_response);
+		}
+
+		/* Run spinlock test */
+		flags = 0;
+		test_request = 0x0;
+		SMP2P_SET_RMT_CMD_TYPE_REQ(test_request);
+
+		/* try to acquire spinlock */
+		remote_spin_trylock_irqsave(smem_spinlock, flags);
+		/*
+		 * Need to check against the locking token (PID + 1)
+		 * because the remote_spin_owner() API only returns the
+		 * PID.
+		 */
+		stored_value = remote_spin_get_hw_spinlocks_element(
+				smem_spinlock);
+		UT_ASSERT_INT(stored_value, ==,
+			remote_spin_owner(smem_spinlock) + 1);
+		UT_ASSERT_INT(stored_value, ==, remote_pid + 1);
+
+		/* End test */
+		test_request = 0x0;
+		SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_END);
+		SMP2P_SET_RMT_DATA(test_request, 0x0);
+		(void)msm_smp2p_out_write(handle, test_request);
+
+		/* Cleanup */
+		ret = msm_smp2p_out_close(&handle);
+		UT_ASSERT_INT(ret, ==, 0);
+		UT_ASSERT_PTR(handle, ==, NULL);
+		ret = msm_smp2p_in_unregister(remote_pid,
+				SMP2P_RLPB_ENTRY_NAME, &cb_in.nb);
+		UT_ASSERT_INT(ret, ==, 0);
+
+		if (!failed)
+			seq_puts(s, "\tOK\n");
+	} while (0);
+
+	if (failed) {
+		if (handle) {
+			/* send end command */
+			test_request = 0x0;
+			SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_END);
+			SMP2P_SET_RMT_DATA(test_request, 0x0);
+			(void)msm_smp2p_out_write(handle, test_request);
+			(void)msm_smp2p_out_close(&handle);
+		}
+		(void)msm_smp2p_in_unregister(remote_pid,
+				SMP2P_RLPB_ENTRY_NAME, &cb_in.nb);
+
+		pr_err("%s: Failed\n", __func__);
+		seq_puts(s, "\tFailed\n");
+	}
+}
+
+/**
+ * smp2p_ut_remote_spinlock_track - Verify PID tracking for modem.
+ *
+ * @s:	Pointer to output file
+ * @pid:		The processor to test
+ */
+static void smp2p_ut_remote_spinlock_track(struct seq_file *s, int pid)
+{
+	struct smp2p_interrupt_config *int_cfg;
+
+	int_cfg = smp2p_get_interrupt_config();
+	if (!int_cfg) {
+		seq_puts(s, "Remote processor config unavailable\n");
+		return;
+	}
+
+	if (pid >= SMP2P_NUM_PROCS || !int_cfg[pid].is_configured)
+		return;
+
+	msm_smp2p_deinit_rmt_lpb_proc(pid);
+	smp2p_ut_remote_spinlock_track_core(s, pid);
+	msm_smp2p_init_rmt_lpb_proc(pid);
+}
+
+/**
+ * smp2p_ut_remote_spinlock_track - Verify PID tracking for all processors.
+ *
+ * @s:	Pointer to output file
+ *
+ * This test verifies PID tracking for all configured remote processors.
+ */
+static void smp2p_ut_remote_spinlock_track_modem(struct seq_file *s)
+{
+	smp2p_ut_remote_spinlock_track(s, SMP2P_MODEM_PROC);
+}
+
+static void smp2p_ut_remote_spinlock_track_adsp(struct seq_file *s)
+{
+	smp2p_ut_remote_spinlock_track(s, SMP2P_AUDIO_PROC);
+}
+
+static void smp2p_ut_remote_spinlock_track_dsps(struct seq_file *s)
+{
+	smp2p_ut_remote_spinlock_track(s, SMP2P_SENSOR_PROC);
+}
+
+static void smp2p_ut_remote_spinlock_track_wcnss(struct seq_file *s)
+{
+	smp2p_ut_remote_spinlock_track(s, SMP2P_WIRELESS_PROC);
+}
+
+static void smp2p_ut_remote_spinlock_track_cdsp(struct seq_file *s)
+{
+	smp2p_ut_remote_spinlock_track(s, SMP2P_CDSP_PROC);
+}
+
+static void smp2p_ut_remote_spinlock_track_tz(struct seq_file *s)
+{
+	smp2p_ut_remote_spinlock_track(s, SMP2P_TZ_PROC);
+}
+
+static int __init smp2p_debugfs_init(void)
+{
+	/*
+	 * Add Unit Test entries.
+	 *
+	 * The idea with unit tests is that you can run all of them
+	 * from ADB shell by doing:
+	 *  adb shell
+	 *  cat ut*
+	 *
+	 * And if particular tests fail, you can then repeatedly run the
+	 * failing tests as you debug and resolve the failing test.
+	 */
+	smp2p_debug_create("ut_remote_spinlock",
+		smp2p_ut_remote_spinlock);
+	smp2p_debug_create("ut_remote_spin_trylock",
+		smp2p_ut_remote_spin_trylock);
+	smp2p_debug_create("ut_remote_spinlock_modem",
+		smp2p_ut_remote_spinlock_modem);
+	smp2p_debug_create("ut_remote_spinlock_adsp",
+		smp2p_ut_remote_spinlock_adsp);
+	smp2p_debug_create("ut_remote_spinlock_dsps",
+		smp2p_ut_remote_spinlock_dsps);
+	smp2p_debug_create("ut_remote_spinlock_wcnss",
+		smp2p_ut_remote_spinlock_wcnss);
+	smp2p_debug_create("ut_remote_spinlock_cdsp",
+		smp2p_ut_remote_spinlock_cdsp);
+	smp2p_debug_create("ut_remote_spinlock_tz",
+		smp2p_ut_remote_spinlock_tz);
+	smp2p_debug_create("ut_remote_spinlock_rpm",
+		smp2p_ut_remote_spinlock_rpm);
+	smp2p_debug_create_u32("ut_remote_spinlock_time",
+		&ut_remote_spinlock_run_time);
+	smp2p_debug_create("ut_remote_spinlock_ssr",
+		&smp2p_ut_remote_spinlock_ssr);
+	smp2p_debug_create("ut_remote_spinlock_track_modem",
+		&smp2p_ut_remote_spinlock_track_modem);
+	smp2p_debug_create("ut_remote_spinlock_track_adsp",
+		&smp2p_ut_remote_spinlock_track_adsp);
+	smp2p_debug_create("ut_remote_spinlock_track_dsps",
+		&smp2p_ut_remote_spinlock_track_dsps);
+	smp2p_debug_create("ut_remote_spinlock_track_wcnss",
+		&smp2p_ut_remote_spinlock_track_wcnss);
+	smp2p_debug_create("ut_remote_spinlock_track_cdsp",
+		&smp2p_ut_remote_spinlock_track_cdsp);
+	smp2p_debug_create("ut_remote_spinlock_track_tz",
+		&smp2p_ut_remote_spinlock_track_tz);
+	return 0;
+}
+module_init(smp2p_debugfs_init);
diff --git a/drivers/soc/qcom/smp2p_test.c b/drivers/soc/qcom/smp2p_test.c
new file mode 100644
index 0000000..aa8d0c8
--- /dev/null
+++ b/drivers/soc/qcom/smp2p_test.c
@@ -0,0 +1,1324 @@
+/* drivers/soc/qcom/smp2p_test.c
+ *
+ * Copyright (c) 2013-2016, 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/ctype.h>
+#include <linux/jiffies.h>
+#include <linux/delay.h>
+#include <linux/completion.h>
+#include <linux/module.h>
+#include <soc/qcom/subsystem_restart.h>
+#include "smp2p_private.h"
+#include "smp2p_test_common.h"
+
+/**
+ * smp2p_ut_local_basic - Basic sanity test using local loopback.
+ *
+ * @s: pointer to output file
+ *
+ * This test simulates a simple write and read
+ * when remote processor does not exist.
+ */
+static void smp2p_ut_local_basic(struct seq_file *s)
+{
+	int failed = 0;
+	struct msm_smp2p_out *smp2p_obj;
+	struct msm_smp2p_remote_mock *rmp = NULL;
+	int ret;
+	uint32_t test_request;
+	uint32_t test_response = 0;
+	static struct mock_cb_data cb_data;
+
+	seq_printf(s, "Running %s\n", __func__);
+	mock_cb_data_init(&cb_data);
+	do {
+		/* initialize mock edge and start opening */
+		ret = smp2p_reset_mock_edge();
+		UT_ASSERT_INT(ret, ==, 0);
+
+		rmp = msm_smp2p_get_remote_mock();
+		UT_ASSERT_PTR(rmp, !=, NULL);
+
+		rmp->rx_interrupt_count = 0;
+		memset(&rmp->remote_item, 0,
+			sizeof(struct smp2p_smem_item));
+
+		msm_smp2p_set_remote_mock_exists(false);
+
+		ret = msm_smp2p_out_open(SMP2P_REMOTE_MOCK_PROC, "smp2p",
+			&cb_data.nb, &smp2p_obj);
+		UT_ASSERT_INT(ret, ==, 0);
+
+		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
+		UT_ASSERT_INT(cb_data.cb_count, ==, 0);
+		rmp->rx_interrupt_count = 0;
+
+		/* simulate response from remote side */
+		rmp->remote_item.header.magic = SMP2P_MAGIC;
+		SMP2P_SET_LOCAL_PID(
+		rmp->remote_item.header.rem_loc_proc_id,
+					SMP2P_REMOTE_MOCK_PROC);
+		SMP2P_SET_REMOTE_PID(
+		rmp->remote_item.header.rem_loc_proc_id,
+					SMP2P_APPS_PROC);
+		SMP2P_SET_VERSION(
+		rmp->remote_item.header.feature_version, 1);
+		SMP2P_SET_FEATURES(
+		rmp->remote_item.header.feature_version, 0);
+		SMP2P_SET_ENT_TOTAL(
+		rmp->remote_item.header.valid_total_ent, SMP2P_MAX_ENTRY);
+		SMP2P_SET_ENT_VALID(
+		rmp->remote_item.header.valid_total_ent, 0);
+		rmp->remote_item.header.flags = 0x0;
+		msm_smp2p_set_remote_mock_exists(true);
+		rmp->tx_interrupt();
+
+		/* verify port was opened */
+		UT_ASSERT_INT(
+			(int)wait_for_completion_timeout(
+					&cb_data.cb_completion, HZ / 2), >, 0);
+		UT_ASSERT_INT(cb_data.cb_count, ==, 1);
+		UT_ASSERT_INT(cb_data.event_open, ==, 1);
+		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 2);
+
+		/* do write (test outbound entries) */
+		rmp->rx_interrupt_count = 0;
+		test_request = 0xC0DE;
+		ret = msm_smp2p_out_write(smp2p_obj, test_request);
+		UT_ASSERT_INT(ret, ==, 0);
+		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
+
+		/* do read (test inbound entries) */
+		ret = msm_smp2p_out_read(smp2p_obj, &test_response);
+		UT_ASSERT_INT(ret, ==, 0);
+		UT_ASSERT_INT(test_request, ==, test_response);
+
+		ret = msm_smp2p_out_close(&smp2p_obj);
+		UT_ASSERT_INT(ret, ==, 0);
+		UT_ASSERT_PTR(smp2p_obj, ==, 0);
+
+		seq_puts(s, "\tOK\n");
+	} while (0);
+
+	if (failed) {
+		pr_err("%s: Failed\n", __func__);
+		seq_puts(s, "\tFailed\n");
+		(void)msm_smp2p_out_close(&smp2p_obj);
+	}
+}
+
+/**
+ * smp2p_ut_local_late_open - Verify post-negotiation opening.
+ *
+ * @s: pointer to output file
+ *
+ * Verify entry creation for opening entries after negotiation is complete.
+ */
+static void smp2p_ut_local_late_open(struct seq_file *s)
+{
+	int failed = 0;
+	struct msm_smp2p_out *smp2p_obj;
+	struct msm_smp2p_remote_mock *rmp = NULL;
+	int ret;
+	uint32_t test_request;
+	uint32_t test_response = 0;
+	static struct mock_cb_data cb_data;
+
+	seq_printf(s, "Running %s\n", __func__);
+	mock_cb_data_init(&cb_data);
+	do {
+		/* initialize mock edge */
+		ret = smp2p_reset_mock_edge();
+		UT_ASSERT_INT(ret, ==, 0);
+
+		rmp = msm_smp2p_get_remote_mock();
+		UT_ASSERT_PTR(rmp, !=, NULL);
+
+		rmp->rx_interrupt_count = 0;
+		memset(&rmp->remote_item, 0,
+			sizeof(struct smp2p_smem_item));
+		rmp->remote_item.header.magic = SMP2P_MAGIC;
+		SMP2P_SET_LOCAL_PID(
+		rmp->remote_item.header.rem_loc_proc_id,
+						SMP2P_REMOTE_MOCK_PROC);
+		SMP2P_SET_REMOTE_PID(
+		rmp->remote_item.header.rem_loc_proc_id,
+						SMP2P_APPS_PROC);
+		SMP2P_SET_VERSION(
+			rmp->remote_item.header.feature_version, 1);
+		SMP2P_SET_FEATURES(
+			rmp->remote_item.header.feature_version, 0);
+		SMP2P_SET_ENT_TOTAL(
+			rmp->remote_item.header.valid_total_ent,
+			SMP2P_MAX_ENTRY);
+		SMP2P_SET_ENT_VALID(
+		rmp->remote_item.header.valid_total_ent, 0);
+		rmp->remote_item.header.flags = 0x0;
+
+		msm_smp2p_set_remote_mock_exists(true);
+
+		ret = msm_smp2p_out_open(SMP2P_REMOTE_MOCK_PROC, "smp2p",
+			&cb_data.nb, &smp2p_obj);
+		UT_ASSERT_INT(ret, ==, 0);
+
+		/* verify port was opened */
+		UT_ASSERT_INT(
+			(int)wait_for_completion_timeout(
+					&cb_data.cb_completion, HZ / 2),
+			>, 0);
+		UT_ASSERT_INT(cb_data.cb_count, ==, 1);
+		UT_ASSERT_INT(cb_data.event_open, ==, 1);
+		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 2);
+
+		/* do write (test outbound entries) */
+		rmp->rx_interrupt_count = 0;
+		test_request = 0xC0DE;
+		ret = msm_smp2p_out_write(smp2p_obj, test_request);
+		UT_ASSERT_INT(ret, ==, 0);
+		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
+
+		/* do read (test inbound entries) */
+		ret = msm_smp2p_out_read(smp2p_obj, &test_response);
+		UT_ASSERT_INT(ret, ==, 0);
+		UT_ASSERT_INT(test_request, ==, test_response);
+
+		ret = msm_smp2p_out_close(&smp2p_obj);
+		UT_ASSERT_INT(ret, ==, 0);
+		UT_ASSERT_PTR(smp2p_obj, ==, 0);
+
+		seq_puts(s, "\tOK\n");
+	} while (0);
+
+	if (failed) {
+		pr_err("%s: Failed\n", __func__);
+		seq_puts(s, "\tFailed\n");
+		(void)msm_smp2p_out_close(&smp2p_obj);
+	}
+}
+
+/**
+ * smp2p_ut_local_early_open - Verify pre-negotiation opening.
+ *
+ * @s: pointer to output file
+ *
+ * Verify entry creation for opening entries before negotiation is complete.
+ */
+static void smp2p_ut_local_early_open(struct seq_file *s)
+{
+	int failed = 0;
+	struct msm_smp2p_out *smp2p_obj;
+	struct msm_smp2p_remote_mock *rmp = NULL;
+	struct smp2p_smem *outbound_item;
+	int negotiation_state;
+	int ret;
+	uint32_t test_request;
+	uint32_t test_response = 0;
+	static struct mock_cb_data cb_data;
+
+	seq_printf(s, "Running %s\n", __func__);
+	mock_cb_data_init(&cb_data);
+	do {
+		/* initialize mock edge, but don't enable, yet */
+		ret = smp2p_reset_mock_edge();
+		UT_ASSERT_INT(ret, ==, 0);
+
+		rmp = msm_smp2p_get_remote_mock();
+		UT_ASSERT_PTR(rmp, !=, NULL);
+
+		rmp->rx_interrupt_count = 0;
+		memset(&rmp->remote_item, 0,
+			sizeof(struct smp2p_smem_item));
+		rmp->remote_item.header.magic = SMP2P_MAGIC;
+		SMP2P_SET_LOCAL_PID(
+		rmp->remote_item.header.rem_loc_proc_id,
+						SMP2P_REMOTE_MOCK_PROC);
+		SMP2P_SET_REMOTE_PID(
+		rmp->remote_item.header.rem_loc_proc_id,
+						SMP2P_APPS_PROC);
+		SMP2P_SET_VERSION(
+		rmp->remote_item.header.feature_version, 1);
+		SMP2P_SET_FEATURES(
+		rmp->remote_item.header.feature_version, 0);
+		SMP2P_SET_ENT_TOTAL(
+		rmp->remote_item.header.valid_total_ent, SMP2P_MAX_ENTRY);
+		SMP2P_SET_ENT_VALID(
+		rmp->remote_item.header.valid_total_ent, 0);
+		rmp->remote_item.header.flags = 0x0;
+
+		msm_smp2p_set_remote_mock_exists(false);
+		UT_ASSERT_PTR(NULL, ==,
+				smp2p_get_in_item(SMP2P_REMOTE_MOCK_PROC));
+
+		/* initiate open, but verify it doesn't complete */
+		ret = msm_smp2p_out_open(SMP2P_REMOTE_MOCK_PROC, "smp2p",
+			&cb_data.nb, &smp2p_obj);
+		UT_ASSERT_INT(ret, ==, 0);
+
+		UT_ASSERT_INT(
+			(int)wait_for_completion_timeout(
+					&cb_data.cb_completion, HZ / 8),
+			==, 0);
+		UT_ASSERT_INT(cb_data.cb_count, ==, 0);
+		UT_ASSERT_INT(cb_data.event_open, ==, 0);
+		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
+
+		outbound_item = smp2p_get_out_item(SMP2P_REMOTE_MOCK_PROC,
+				&negotiation_state);
+		UT_ASSERT_PTR(outbound_item, !=, NULL);
+		UT_ASSERT_INT(negotiation_state, ==, SMP2P_EDGE_STATE_OPENING);
+		UT_ASSERT_INT(0, ==,
+			SMP2P_GET_ENT_VALID(outbound_item->valid_total_ent));
+
+		/* verify that read/write don't work yet */
+		rmp->rx_interrupt_count = 0;
+		test_request = 0x0;
+		ret = msm_smp2p_out_write(smp2p_obj, test_request);
+		UT_ASSERT_INT(ret, ==, -ENODEV);
+		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 0);
+
+		ret = msm_smp2p_out_read(smp2p_obj, &test_response);
+		UT_ASSERT_INT(ret, ==, -ENODEV);
+
+		/* allocate remote entry and verify open */
+		msm_smp2p_set_remote_mock_exists(true);
+		rmp->tx_interrupt();
+
+		UT_ASSERT_INT(
+			(int)wait_for_completion_timeout(
+					&cb_data.cb_completion, HZ / 2),
+			>, 0);
+		UT_ASSERT_INT(cb_data.cb_count, ==, 1);
+		UT_ASSERT_INT(cb_data.event_open, ==, 1);
+		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 2);
+
+		/* do write (test outbound entries) */
+		rmp->rx_interrupt_count = 0;
+		test_request = 0xC0DE;
+		ret = msm_smp2p_out_write(smp2p_obj, test_request);
+		UT_ASSERT_INT(ret, ==, 0);
+		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
+
+		/* do read (test inbound entries) */
+		ret = msm_smp2p_out_read(smp2p_obj, &test_response);
+		UT_ASSERT_INT(ret, ==, 0);
+		UT_ASSERT_INT(test_request, ==, test_response);
+
+		ret = msm_smp2p_out_close(&smp2p_obj);
+		UT_ASSERT_INT(ret, ==, 0);
+		UT_ASSERT_PTR(smp2p_obj, ==, 0);
+
+		seq_puts(s, "\tOK\n");
+	} while (0);
+
+	if (failed) {
+		pr_err("%s: Failed\n", __func__);
+		seq_puts(s, "\tFailed\n");
+		(void)msm_smp2p_out_close(&smp2p_obj);
+	}
+}
+
+/**
+ * smp2p_ut_mock_loopback - Exercise the remote loopback using remote mock.
+ *
+ * @s: pointer to output file
+ *
+ * This test exercises the remote loopback code using
+ * remote mock object. The remote mock object simulates the remote
+ * processor sending remote loopback commands to the local processor.
+ */
+static void smp2p_ut_mock_loopback(struct seq_file *s)
+{
+	int failed = 0;
+	struct msm_smp2p_remote_mock *rmp = NULL;
+	int ret;
+	uint32_t test_request = 0;
+	uint32_t test_response = 0;
+	struct msm_smp2p_out  *local;
+
+	seq_printf(s, "Running %s\n", __func__);
+	do {
+		/* Initialize the mock edge */
+		ret = smp2p_reset_mock_edge();
+		UT_ASSERT_INT(ret, ==, 0);
+
+		rmp = msm_smp2p_get_remote_mock();
+		UT_ASSERT_PTR(rmp, !=, NULL);
+
+		memset(&rmp->remote_item, 0,
+			sizeof(struct smp2p_smem_item));
+		rmp->remote_item.header.magic = SMP2P_MAGIC;
+		SMP2P_SET_LOCAL_PID(
+		rmp->remote_item.header.rem_loc_proc_id,
+						SMP2P_REMOTE_MOCK_PROC);
+		SMP2P_SET_REMOTE_PID(
+		rmp->remote_item.header.rem_loc_proc_id,
+						SMP2P_APPS_PROC);
+		SMP2P_SET_VERSION(
+		rmp->remote_item.header.feature_version, 1);
+		SMP2P_SET_FEATURES(
+		rmp->remote_item.header.feature_version, 0);
+		SMP2P_SET_ENT_TOTAL(
+		rmp->remote_item.header.valid_total_ent, SMP2P_MAX_ENTRY);
+		SMP2P_SET_ENT_VALID(
+		rmp->remote_item.header.valid_total_ent, 1);
+		rmp->remote_item.header.flags = 0x0;
+		msm_smp2p_set_remote_mock_exists(true);
+
+		/* Create test entry and attach loopback server */
+		rmp->rx_interrupt_count = 0;
+		reinit_completion(&rmp->cb_completion);
+		strlcpy(rmp->remote_item.entries[0].name, "smp2p",
+							SMP2P_MAX_ENTRY_NAME);
+		rmp->remote_item.entries[0].entry = 0;
+		rmp->tx_interrupt();
+
+		local = msm_smp2p_init_rmt_lpb_proc(SMP2P_REMOTE_MOCK_PROC);
+		UT_ASSERT_INT(
+			(int)wait_for_completion_timeout(
+					&rmp->cb_completion, HZ / 2),
+			>, 0);
+		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 2);
+
+		/* Send Echo Command */
+		rmp->rx_interrupt_count = 0;
+		reinit_completion(&rmp->cb_completion);
+		SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
+		SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_ECHO);
+		SMP2P_SET_RMT_DATA(test_request, 10);
+		rmp->remote_item.entries[0].entry = test_request;
+		rmp->tx_interrupt();
+		UT_ASSERT_INT(
+			(int)wait_for_completion_timeout(
+					&rmp->cb_completion, HZ / 2),
+			>, 0);
+
+		/* Verify Echo Response */
+		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
+		ret = msm_smp2p_out_read(local,
+							&test_response);
+		UT_ASSERT_INT(ret, ==, 0);
+		test_response = SMP2P_GET_RMT_DATA(test_response);
+		UT_ASSERT_INT(test_response, ==, 10);
+
+		/* Send PINGPONG command */
+		test_request = 0;
+		test_response = 0;
+		rmp->rx_interrupt_count = 0;
+		reinit_completion(&rmp->cb_completion);
+		SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
+		SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_PINGPONG);
+		SMP2P_SET_RMT_DATA(test_request, 10);
+		rmp->remote_item.entries[0].entry = test_request;
+		rmp->tx_interrupt();
+		UT_ASSERT_INT(
+			(int)wait_for_completion_timeout(
+					&rmp->cb_completion, HZ / 2),
+			>, 0);
+
+		/* Verify PINGPONG Response */
+		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
+		ret = msm_smp2p_out_read(local, &test_response);
+		UT_ASSERT_INT(ret, ==, 0);
+		test_response = SMP2P_GET_RMT_DATA(test_response);
+		UT_ASSERT_INT(test_response, ==, 9);
+
+		/* Send CLEARALL command */
+		test_request = 0;
+		test_response = 0;
+		rmp->rx_interrupt_count = 0;
+		reinit_completion(&rmp->cb_completion);
+		SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
+		SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_CLEARALL);
+		SMP2P_SET_RMT_DATA(test_request, 10);
+		rmp->remote_item.entries[0].entry = test_request;
+		rmp->tx_interrupt();
+		UT_ASSERT_INT(
+			(int)wait_for_completion_timeout(
+					&rmp->cb_completion, HZ / 2),
+			>, 0);
+
+		/* Verify CLEARALL response */
+		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
+		ret = msm_smp2p_out_read(local, &test_response);
+		UT_ASSERT_INT(ret, ==, 0);
+		test_response = SMP2P_GET_RMT_DATA(test_response);
+		UT_ASSERT_INT(test_response, ==, 0);
+
+		ret = msm_smp2p_deinit_rmt_lpb_proc(SMP2P_REMOTE_MOCK_PROC);
+		UT_ASSERT_INT(ret, ==, 0);
+		seq_puts(s, "\tOK\n");
+	} while (0);
+
+	if (failed) {
+		pr_err("%s: Failed\n", __func__);
+		seq_puts(s, "\tFailed\n");
+		msm_smp2p_deinit_rmt_lpb_proc(SMP2P_REMOTE_MOCK_PROC);
+	}
+}
+
+/**
+ * smp2p_ut_remote_inout_core - Verify inbound/outbound functionality.
+ *
+ * @s: pointer to output file
+ * @remote_pid:  Remote processor to test
+ *
+ * This test verifies inbound/outbound functionality for the remote processor.
+ */
+static void smp2p_ut_remote_inout_core(struct seq_file *s, int remote_pid)
+{
+	int failed = 0;
+	struct msm_smp2p_out *handle;
+	int ret;
+	uint32_t test_request;
+	uint32_t test_response = 0;
+	static struct mock_cb_data cb_out;
+	static struct mock_cb_data cb_in;
+
+	seq_printf(s, "Running %s for '%s' remote pid %d\n",
+		   __func__, smp2p_pid_to_name(remote_pid), remote_pid);
+	mock_cb_data_init(&cb_out);
+	mock_cb_data_init(&cb_in);
+	do {
+		/* Open output entry */
+		ret = msm_smp2p_out_open(remote_pid, "smp2p",
+			&cb_out.nb, &handle);
+		UT_ASSERT_INT(ret, ==, 0);
+		UT_ASSERT_INT(
+			(int)wait_for_completion_timeout(
+					&cb_out.cb_completion, HZ / 2),
+			>, 0);
+		UT_ASSERT_INT(cb_out.cb_count, ==, 1);
+		UT_ASSERT_INT(cb_out.event_open, ==, 1);
+
+		/* Open inbound entry */
+		ret = msm_smp2p_in_register(remote_pid, "smp2p",
+				&cb_in.nb);
+		UT_ASSERT_INT(ret, ==, 0);
+		UT_ASSERT_INT(
+			(int)wait_for_completion_timeout(
+					&cb_in.cb_completion, HZ / 2),
+			>, 0);
+		UT_ASSERT_INT(cb_in.cb_count, ==, 1);
+		UT_ASSERT_INT(cb_in.event_open, ==, 1);
+
+		/* Write an echo request */
+		mock_cb_data_reset(&cb_out);
+		mock_cb_data_reset(&cb_in);
+		test_request = 0x0;
+		SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
+		SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_ECHO);
+		SMP2P_SET_RMT_DATA(test_request, 0xAA55);
+		ret = msm_smp2p_out_write(handle, test_request);
+		UT_ASSERT_INT(ret, ==, 0);
+
+		/* Verify inbound reply */
+		UT_ASSERT_INT(
+			(int)wait_for_completion_timeout(
+					&cb_in.cb_completion, HZ / 2),
+			>, 0);
+		UT_ASSERT_INT(cb_in.cb_count, ==, 1);
+		UT_ASSERT_INT(cb_in.event_entry_update, ==, 1);
+		UT_ASSERT_INT(SMP2P_GET_RMT_DATA(
+			    cb_in.entry_data.current_value), ==, 0xAA55);
+
+		ret = msm_smp2p_in_read(remote_pid, "smp2p", &test_response);
+		UT_ASSERT_INT(ret, ==, 0);
+		UT_ASSERT_INT(0, ==, SMP2P_GET_RMT_CMD_TYPE(test_response));
+		UT_ASSERT_INT(SMP2P_LB_CMD_ECHO, ==,
+				SMP2P_GET_RMT_CMD(test_response));
+		UT_ASSERT_INT(0xAA55, ==, SMP2P_GET_RMT_DATA(test_response));
+
+		/* Write a clear all request */
+		mock_cb_data_reset(&cb_in);
+		test_request = 0x0;
+		SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
+		SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_CLEARALL);
+		SMP2P_SET_RMT_DATA(test_request, 0xAA55);
+		ret = msm_smp2p_out_write(handle, test_request);
+		UT_ASSERT_INT(ret, ==, 0);
+
+		/* Verify inbound reply */
+		UT_ASSERT_INT(
+			(int)wait_for_completion_timeout(
+					&cb_in.cb_completion, HZ / 2),
+			>, 0);
+		UT_ASSERT_INT(cb_in.cb_count, ==, 1);
+		UT_ASSERT_INT(cb_in.event_entry_update, ==, 1);
+		UT_ASSERT_INT(SMP2P_GET_RMT_DATA(
+			    cb_in.entry_data.current_value), ==, 0x0000);
+
+		ret = msm_smp2p_in_read(remote_pid, "smp2p", &test_response);
+		UT_ASSERT_INT(ret, ==, 0);
+		UT_ASSERT_INT(0, ==, SMP2P_GET_RMT_CMD_TYPE(test_response));
+		UT_ASSERT_INT(0x0000, ==, SMP2P_GET_RMT_DATA(test_response));
+
+		/* Write a decrement request */
+		mock_cb_data_reset(&cb_in);
+		test_request = 0x0;
+		SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
+		SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_PINGPONG);
+		SMP2P_SET_RMT_DATA(test_request, 0xAA55);
+		ret = msm_smp2p_out_write(handle, test_request);
+		UT_ASSERT_INT(ret, ==, 0);
+
+		/* Verify inbound reply */
+		UT_ASSERT_INT(
+			(int)wait_for_completion_timeout(
+					&cb_in.cb_completion, HZ / 2),
+			>, 0);
+		UT_ASSERT_INT(cb_in.cb_count, ==, 1);
+		UT_ASSERT_INT(cb_in.event_entry_update, ==, 1);
+		UT_ASSERT_INT(SMP2P_GET_RMT_DATA(
+			    cb_in.entry_data.current_value), ==, 0xAA54);
+
+		ret = msm_smp2p_in_read(remote_pid, "smp2p", &test_response);
+		UT_ASSERT_INT(ret, ==, 0);
+		UT_ASSERT_INT(0, ==, SMP2P_GET_RMT_CMD_TYPE(test_response));
+		UT_ASSERT_INT(SMP2P_LB_CMD_PINGPONG, ==,
+				SMP2P_GET_RMT_CMD(test_response));
+		UT_ASSERT_INT(0xAA54, ==, SMP2P_GET_RMT_DATA(test_response));
+
+		/* Test the ignore flag */
+		mock_cb_data_reset(&cb_in);
+		test_request = 0x0;
+		SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
+		SMP2P_SET_RMT_CMD(test_request, SMP2P_RLPB_IGNORE);
+		SMP2P_SET_RMT_DATA(test_request, 0xAA55);
+		ret = msm_smp2p_out_write(handle, test_request);
+		UT_ASSERT_INT(ret, ==, 0);
+
+		UT_ASSERT_INT(
+			(int)wait_for_completion_timeout(
+					&cb_in.cb_completion, HZ / 2),
+			==, 0);
+		UT_ASSERT_INT(cb_in.cb_count, ==, 0);
+		UT_ASSERT_INT(cb_in.event_entry_update, ==, 0);
+		ret = msm_smp2p_in_read(remote_pid, "smp2p", &test_response);
+		UT_ASSERT_INT(ret, ==, 0);
+		UT_ASSERT_INT(0xAA54, ==, SMP2P_GET_RMT_DATA(test_response));
+
+		/* Cleanup */
+		ret = msm_smp2p_out_close(&handle);
+		UT_ASSERT_INT(ret, ==, 0);
+		UT_ASSERT_PTR(handle, ==, 0);
+		ret = msm_smp2p_in_unregister(remote_pid, "smp2p", &cb_in.nb);
+		UT_ASSERT_INT(ret, ==, 0);
+
+		seq_puts(s, "\tOK\n");
+	} while (0);
+
+	if (failed) {
+		if (handle)
+			(void)msm_smp2p_out_close(&handle);
+		(void)msm_smp2p_in_unregister(remote_pid, "smp2p", &cb_in.nb);
+
+		pr_err("%s: Failed\n", __func__);
+		seq_puts(s, "\tFailed\n");
+	}
+}
+
+/**
+ * smp2p_ut_remote_inout - Verify inbound/outbound functionality for all.
+ *
+ * @s: pointer to output file
+ *
+ * This test verifies inbound and outbound functionality for all
+ * configured remote processor.
+ */
+static void smp2p_ut_remote_inout(struct seq_file *s)
+{
+	struct smp2p_interrupt_config *int_cfg;
+	int pid;
+
+	int_cfg = smp2p_get_interrupt_config();
+	if (!int_cfg) {
+		seq_puts(s, "Remote processor config unavailable\n");
+		return;
+	}
+
+	for (pid = 0; pid < SMP2P_NUM_PROCS; ++pid) {
+		if (!int_cfg[pid].is_configured)
+			continue;
+
+		msm_smp2p_deinit_rmt_lpb_proc(pid);
+		smp2p_ut_remote_inout_core(s, pid);
+		msm_smp2p_init_rmt_lpb_proc(pid);
+	}
+}
+
+/**
+ * smp2p_ut_remote_out_max_entries_core - Verify open functionality.
+ *
+ * @s: pointer to output file
+ * @remote_pid:  Remote processor for which the test is executed.
+ *
+ * This test verifies open functionality by creating maximum outbound entries.
+ */
+static void smp2p_ut_remote_out_max_entries_core(struct seq_file *s,
+	int remote_pid)
+{
+	int j = 0;
+	int failed = 0;
+	struct msm_smp2p_out *handle[SMP2P_MAX_ENTRY];
+	int ret;
+	static struct mock_cb_data cb_out[SMP2P_MAX_ENTRY];
+	char entry_name[SMP2P_MAX_ENTRY_NAME];
+	int num_created;
+
+	seq_printf(s, "Running %s for '%s' remote pid %d\n",
+		   __func__, smp2p_pid_to_name(remote_pid), remote_pid);
+
+	for (j = 0; j < SMP2P_MAX_ENTRY; j++) {
+		handle[j] = NULL;
+		mock_cb_data_init(&cb_out[j]);
+	}
+
+	do {
+		num_created = 0;
+		for (j = 0; j < SMP2P_MAX_ENTRY; j++) {
+			/* Open as many output entries as possible */
+			scnprintf((char *)entry_name, SMP2P_MAX_ENTRY_NAME,
+				"smp2p%d", j);
+			ret = msm_smp2p_out_open(remote_pid, entry_name,
+				&cb_out[j].nb, &handle[j]);
+			if (ret == -ENOMEM)
+				/* hit max number */
+				break;
+			UT_ASSERT_INT(ret, ==, 0);
+			++num_created;
+		}
+		if (failed)
+			break;
+
+		/* verify we created more than 1 entry */
+		UT_ASSERT_INT(num_created, <=, SMP2P_MAX_ENTRY);
+		UT_ASSERT_INT(num_created, >, 0);
+
+		seq_puts(s, "\tOK\n");
+	} while (0);
+
+	if (failed) {
+		pr_err("%s: Failed\n", __func__);
+		seq_puts(s, "\tFailed\n");
+	}
+
+	/* cleanup */
+	for (j = 0; j < SMP2P_MAX_ENTRY; j++)
+		ret = msm_smp2p_out_close(&handle[j]);
+}
+
+/**
+ * smp2p_ut_remote_out_max_entries - Verify open for all configured processors.
+ *
+ * @s: pointer to output file
+ *
+ * This test verifies creating max number of entries for
+ * all configured remote processor.
+ */
+static void smp2p_ut_remote_out_max_entries(struct seq_file *s)
+{
+	struct smp2p_interrupt_config *int_cfg;
+	int pid;
+
+	int_cfg = smp2p_get_interrupt_config();
+	if (!int_cfg) {
+		seq_puts(s, "Remote processor config unavailable\n");
+		return;
+	}
+
+	for (pid = 0; pid < SMP2P_NUM_PROCS; ++pid) {
+		if (!int_cfg[pid].is_configured)
+			continue;
+
+		smp2p_ut_remote_out_max_entries_core(s, pid);
+	}
+}
+
+/**
+ * smp2p_ut_local_in_max_entries - Verify registering and unregistering.
+ *
+ * @s: pointer to output file
+ *
+ * This test verifies registering and unregistering for inbound entries using
+ * the remote mock processor.
+ */
+static void smp2p_ut_local_in_max_entries(struct seq_file *s)
+{
+	int j = 0;
+	int failed = 0;
+	struct msm_smp2p_remote_mock *rmp = NULL;
+	int ret;
+	static struct mock_cb_data cb_in[SMP2P_MAX_ENTRY];
+	static struct mock_cb_data cb_out;
+
+	seq_printf(s, "Running %s\n", __func__);
+
+	for (j = 0; j < SMP2P_MAX_ENTRY; j++)
+		mock_cb_data_init(&cb_in[j]);
+
+	mock_cb_data_init(&cb_out);
+
+	do {
+		/* Initialize mock edge */
+		ret = smp2p_reset_mock_edge();
+		UT_ASSERT_INT(ret, ==, 0);
+
+		rmp = msm_smp2p_get_remote_mock();
+		UT_ASSERT_PTR(rmp, !=, NULL);
+
+		rmp->rx_interrupt_count = 0;
+		memset(&rmp->remote_item, 0,
+			sizeof(struct smp2p_smem_item));
+		rmp->remote_item.header.magic = SMP2P_MAGIC;
+		SMP2P_SET_LOCAL_PID(
+		rmp->remote_item.header.rem_loc_proc_id,
+						SMP2P_REMOTE_MOCK_PROC);
+		SMP2P_SET_REMOTE_PID(
+		rmp->remote_item.header.rem_loc_proc_id,
+						SMP2P_APPS_PROC);
+		SMP2P_SET_VERSION(
+		rmp->remote_item.header.feature_version, 1);
+		SMP2P_SET_FEATURES(
+		rmp->remote_item.header.feature_version, 0);
+		SMP2P_SET_ENT_TOTAL(
+		rmp->remote_item.header.valid_total_ent, SMP2P_MAX_ENTRY);
+		SMP2P_SET_ENT_VALID(
+		rmp->remote_item.header.valid_total_ent, 0);
+		rmp->remote_item.header.flags = 0x0;
+		msm_smp2p_set_remote_mock_exists(true);
+
+		/* Create Max Entries in the remote mock object */
+		for (j = 0; j < SMP2P_MAX_ENTRY; j++) {
+			scnprintf(rmp->remote_item.entries[j].name,
+				SMP2P_MAX_ENTRY_NAME, "smp2p%d", j);
+			rmp->remote_item.entries[j].entry = 0;
+			rmp->tx_interrupt();
+		}
+
+		/* Register for in entries */
+		for (j = 0; j < SMP2P_MAX_ENTRY; j++) {
+			ret = msm_smp2p_in_register(SMP2P_REMOTE_MOCK_PROC,
+				rmp->remote_item.entries[j].name,
+				&(cb_in[j].nb));
+			UT_ASSERT_INT(ret, ==, 0);
+			UT_ASSERT_INT(
+				(int)wait_for_completion_timeout(
+					&(cb_in[j].cb_completion), HZ / 2),
+				>, 0);
+			UT_ASSERT_INT(cb_in[j].cb_count, ==, 1);
+			UT_ASSERT_INT(cb_in[j].event_entry_update, ==, 0);
+		}
+		UT_ASSERT_INT(j, ==, SMP2P_MAX_ENTRY);
+
+		/* Unregister */
+		for (j = 0; j < SMP2P_MAX_ENTRY; j++) {
+			ret = msm_smp2p_in_unregister(SMP2P_REMOTE_MOCK_PROC,
+				rmp->remote_item.entries[j].name,
+				&(cb_in[j].nb));
+		    UT_ASSERT_INT(ret, ==, 0);
+		}
+		UT_ASSERT_INT(j, ==, SMP2P_MAX_ENTRY);
+
+		seq_puts(s, "\tOK\n");
+	} while (0);
+
+	if (failed) {
+		pr_err("%s: Failed\n", __func__);
+		seq_puts(s, "\tFailed\n");
+
+		for (j = 0; j < SMP2P_MAX_ENTRY; j++)
+			ret = msm_smp2p_in_unregister(SMP2P_REMOTE_MOCK_PROC,
+				rmp->remote_item.entries[j].name,
+				&(cb_in[j].nb));
+	}
+}
+
+/**
+ * smp2p_ut_local_in_multiple - Verify Multiple Inbound Registration.
+ *
+ * @s: pointer to output file
+ *
+ * This test verifies multiple clients registering for same inbound entries
+ * using the remote mock processor.
+ */
+static void smp2p_ut_local_in_multiple(struct seq_file *s)
+{
+	int failed = 0;
+	struct msm_smp2p_remote_mock *rmp = NULL;
+	int ret;
+	static struct mock_cb_data cb_in_1;
+	static struct mock_cb_data cb_in_2;
+	static struct mock_cb_data cb_out;
+
+	seq_printf(s, "Running %s\n", __func__);
+
+	mock_cb_data_init(&cb_in_1);
+	mock_cb_data_init(&cb_in_2);
+	mock_cb_data_init(&cb_out);
+
+	do {
+		/* Initialize mock edge */
+		ret = smp2p_reset_mock_edge();
+		UT_ASSERT_INT(ret, ==, 0);
+
+		rmp = msm_smp2p_get_remote_mock();
+		UT_ASSERT_PTR(rmp, !=, NULL);
+
+		rmp->rx_interrupt_count = 0;
+		memset(&rmp->remote_item, 0,
+			sizeof(struct smp2p_smem_item));
+		rmp->remote_item.header.magic = SMP2P_MAGIC;
+		SMP2P_SET_LOCAL_PID(
+		rmp->remote_item.header.rem_loc_proc_id,
+						SMP2P_REMOTE_MOCK_PROC);
+		SMP2P_SET_REMOTE_PID(
+		rmp->remote_item.header.rem_loc_proc_id,
+						SMP2P_APPS_PROC);
+		SMP2P_SET_VERSION(
+		rmp->remote_item.header.feature_version, 1);
+		SMP2P_SET_FEATURES(
+		rmp->remote_item.header.feature_version, 0);
+		SMP2P_SET_ENT_TOTAL(
+		rmp->remote_item.header.valid_total_ent, 1);
+		SMP2P_SET_ENT_VALID(
+		rmp->remote_item.header.valid_total_ent, 0);
+		rmp->remote_item.header.flags = 0x0;
+		msm_smp2p_set_remote_mock_exists(true);
+
+		/* Create an Entry in the remote mock object */
+		scnprintf(rmp->remote_item.entries[0].name,
+				SMP2P_MAX_ENTRY_NAME, "smp2p%d", 1);
+		rmp->remote_item.entries[0].entry = 0;
+		rmp->tx_interrupt();
+
+		/* Register multiple clients for the inbound entry */
+		ret = msm_smp2p_in_register(SMP2P_REMOTE_MOCK_PROC,
+				rmp->remote_item.entries[0].name,
+				&cb_in_1.nb);
+		UT_ASSERT_INT(ret, ==, 0);
+		UT_ASSERT_INT(
+				(int)wait_for_completion_timeout(
+				&(cb_in_1.cb_completion), HZ / 2),
+				>, 0);
+		UT_ASSERT_INT(cb_in_1.cb_count, ==, 1);
+		UT_ASSERT_INT(cb_in_1.event_entry_update, ==, 0);
+
+		ret = msm_smp2p_in_register(SMP2P_REMOTE_MOCK_PROC,
+				rmp->remote_item.entries[0].name,
+				&cb_in_2.nb);
+		UT_ASSERT_INT(ret, ==, 0);
+		UT_ASSERT_INT(
+				(int)wait_for_completion_timeout(
+				&(cb_in_2.cb_completion), HZ / 2),
+				>, 0);
+		UT_ASSERT_INT(cb_in_2.cb_count, ==, 1);
+		UT_ASSERT_INT(cb_in_2.event_entry_update, ==, 0);
+
+
+		/* Unregister the clients */
+		ret = msm_smp2p_in_unregister(SMP2P_REMOTE_MOCK_PROC,
+				rmp->remote_item.entries[0].name,
+				&(cb_in_1.nb));
+		UT_ASSERT_INT(ret, ==, 0);
+
+		ret = msm_smp2p_in_unregister(SMP2P_REMOTE_MOCK_PROC,
+				rmp->remote_item.entries[0].name,
+				&(cb_in_2.nb));
+		UT_ASSERT_INT(ret, ==, 0);
+
+		seq_puts(s, "\tOK\n");
+	} while (0);
+
+	if (failed) {
+		pr_err("%s: Failed\n", __func__);
+		seq_puts(s, "\tFailed\n");
+
+		ret = msm_smp2p_in_unregister(SMP2P_REMOTE_MOCK_PROC,
+				rmp->remote_item.entries[0].name,
+				&(cb_in_1.nb));
+
+		ret = msm_smp2p_in_unregister(SMP2P_REMOTE_MOCK_PROC,
+				rmp->remote_item.entries[0].name,
+				&(cb_in_2.nb));
+	}
+}
+
+/**
+ * smp2p_ut_local_ssr_ack - Verify SSR Done/ACK Feature
+ *
+ * @s: pointer to output file
+ */
+static void smp2p_ut_local_ssr_ack(struct seq_file *s)
+{
+	int failed = 0;
+	struct msm_smp2p_remote_mock *rmp = NULL;
+	int ret;
+
+	seq_printf(s, "Running %s\n", __func__);
+	do {
+		struct smp2p_smem *rhdr;
+		struct smp2p_smem *lhdr;
+		int negotiation_state;
+
+		/* initialize v1 without SMP2P_FEATURE_SSR_ACK enabled */
+		ret = smp2p_reset_mock_edge();
+		UT_ASSERT_INT(ret, ==, 0);
+		rmp = msm_smp2p_get_remote_mock();
+		UT_ASSERT_PTR(rmp, !=, NULL);
+		rhdr = &rmp->remote_item.header;
+
+		rmp->rx_interrupt_count = 0;
+		memset(&rmp->remote_item, 0, sizeof(struct smp2p_smem_item));
+		rhdr->magic = SMP2P_MAGIC;
+		SMP2P_SET_LOCAL_PID(rhdr->rem_loc_proc_id,
+				SMP2P_REMOTE_MOCK_PROC);
+		SMP2P_SET_REMOTE_PID(rhdr->rem_loc_proc_id, SMP2P_APPS_PROC);
+		SMP2P_SET_VERSION(rhdr->feature_version, 1);
+		SMP2P_SET_FEATURES(rhdr->feature_version, 0);
+		SMP2P_SET_ENT_TOTAL(rhdr->valid_total_ent, SMP2P_MAX_ENTRY);
+		SMP2P_SET_ENT_VALID(rhdr->valid_total_ent, 0);
+		rhdr->flags = 0x0;
+		msm_smp2p_set_remote_mock_exists(true);
+		rmp->tx_interrupt();
+
+		/* verify edge is open */
+		lhdr = smp2p_get_out_item(SMP2P_REMOTE_MOCK_PROC,
+					&negotiation_state);
+		UT_ASSERT_PTR(NULL, !=, lhdr);
+		UT_ASSERT_INT(negotiation_state, ==, SMP2P_EDGE_STATE_OPENED);
+		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
+
+		/* verify no response to ack feature */
+		rmp->rx_interrupt_count = 0;
+		SMP2P_SET_RESTART_DONE(rhdr->flags, 1);
+		rmp->tx_interrupt();
+		UT_ASSERT_INT(0, ==, SMP2P_GET_RESTART_DONE(lhdr->flags));
+		UT_ASSERT_INT(0, ==, SMP2P_GET_RESTART_ACK(lhdr->flags));
+		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 0);
+
+		/* initialize v1 with SMP2P_FEATURE_SSR_ACK enabled */
+		ret = smp2p_reset_mock_edge();
+		UT_ASSERT_INT(ret, ==, 0);
+		rmp = msm_smp2p_get_remote_mock();
+		UT_ASSERT_PTR(rmp, !=, NULL);
+		rhdr = &rmp->remote_item.header;
+
+		rmp->rx_interrupt_count = 0;
+		memset(&rmp->remote_item, 0, sizeof(struct smp2p_smem_item));
+		rhdr->magic = SMP2P_MAGIC;
+		SMP2P_SET_LOCAL_PID(rhdr->rem_loc_proc_id,
+				SMP2P_REMOTE_MOCK_PROC);
+		SMP2P_SET_REMOTE_PID(rhdr->rem_loc_proc_id, SMP2P_APPS_PROC);
+		SMP2P_SET_VERSION(rhdr->feature_version, 1);
+		SMP2P_SET_FEATURES(rhdr->feature_version,
+				SMP2P_FEATURE_SSR_ACK);
+		SMP2P_SET_ENT_TOTAL(rhdr->valid_total_ent, SMP2P_MAX_ENTRY);
+		SMP2P_SET_ENT_VALID(rhdr->valid_total_ent, 0);
+		rmp->rx_interrupt_count = 0;
+		rhdr->flags = 0x0;
+		msm_smp2p_set_remote_mock_exists(true);
+		rmp->tx_interrupt();
+
+		/* verify edge is open */
+		lhdr = smp2p_get_out_item(SMP2P_REMOTE_MOCK_PROC,
+					&negotiation_state);
+		UT_ASSERT_PTR(NULL, !=, lhdr);
+		UT_ASSERT_INT(negotiation_state, ==, SMP2P_EDGE_STATE_OPENED);
+		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
+
+		/* verify response to ack feature */
+		rmp->rx_interrupt_count = 0;
+		SMP2P_SET_RESTART_DONE(rhdr->flags, 1);
+		rmp->tx_interrupt();
+		UT_ASSERT_INT(0, ==, SMP2P_GET_RESTART_DONE(lhdr->flags));
+		UT_ASSERT_INT(1, ==, SMP2P_GET_RESTART_ACK(lhdr->flags));
+		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
+
+		rmp->rx_interrupt_count = 0;
+		SMP2P_SET_RESTART_DONE(rhdr->flags, 0);
+		rmp->tx_interrupt();
+		UT_ASSERT_INT(0, ==, SMP2P_GET_RESTART_DONE(lhdr->flags));
+		UT_ASSERT_INT(0, ==, SMP2P_GET_RESTART_ACK(lhdr->flags));
+		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
+
+		seq_puts(s, "\tOK\n");
+	} while (0);
+
+	if (failed) {
+		pr_err("%s: Failed\n", __func__);
+		seq_puts(s, "\tFailed\n");
+	}
+}
+
+/**
+ * get_ssr_name_for_proc - Retrieve an SSR name from the provided list
+ *
+ * @names:	List of possible processor names
+ * @name_len:	The length of @names
+ * @index:	Index into @names
+ *
+ * Return: Pointer to the next processor name, NULL in error conditions
+ */
+static char *get_ssr_name_for_proc(char * const names[], size_t name_len,
+				   int index)
+{
+	if (index >= name_len) {
+		pr_err("%s: SSR failed; check subsys name table\n",
+				__func__);
+		return NULL;
+	}
+
+	return names[index];
+}
+
+/**
+ * smp2p_ut_local_ssr_ack - Verify SSR Done/ACK Feature
+ *
+ * @s: pointer to output file
+ * @rpid: Remote processor ID
+ * @int_cfg: Interrupt config
+ */
+static void smp2p_ut_remotesubsys_ssr_ack(struct seq_file *s, uint32_t rpid,
+		struct smp2p_interrupt_config *int_cfg)
+{
+	int failed = 0;
+
+	seq_printf(s, "Running %s\n", __func__);
+	do {
+		struct smp2p_smem *rhdr;
+		struct smp2p_smem *lhdr;
+		int negotiation_state;
+		int name_index;
+		int ret;
+		uint32_t ssr_done_start;
+		bool ssr_ack_enabled = false;
+		bool ssr_success = false;
+		char *name = NULL;
+
+		static char * const mpss_names[] = {"modem", "mpss"};
+		static char * const lpass_names[] = {"adsp", "lpass"};
+		static char * const sensor_names[] = {"slpi", "dsps"};
+		static char * const wcnss_names[] = {"wcnss"};
+
+		lhdr = smp2p_get_out_item(rpid, &negotiation_state);
+		UT_ASSERT_PTR(NULL, !=, lhdr);
+		UT_ASSERT_INT(SMP2P_EDGE_STATE_OPENED, ==, negotiation_state);
+
+		rhdr = smp2p_get_in_item(rpid);
+		UT_ASSERT_PTR(NULL, !=, rhdr);
+
+		/* get initial state of SSR flags */
+		if (SMP2P_GET_FEATURES(rhdr->feature_version)
+				& SMP2P_FEATURE_SSR_ACK)
+			ssr_ack_enabled = true;
+		else
+			ssr_ack_enabled = false;
+
+		ssr_done_start = SMP2P_GET_RESTART_DONE(rhdr->flags);
+		UT_ASSERT_INT(ssr_done_start, ==,
+				SMP2P_GET_RESTART_ACK(lhdr->flags));
+
+		/* trigger restart */
+		name_index = 0;
+		while (!ssr_success) {
+
+			switch (rpid) {
+			case SMP2P_MODEM_PROC:
+				name = get_ssr_name_for_proc(mpss_names,
+						ARRAY_SIZE(mpss_names),
+						name_index);
+				break;
+			case SMP2P_AUDIO_PROC:
+				name = get_ssr_name_for_proc(lpass_names,
+						ARRAY_SIZE(lpass_names),
+						name_index);
+				break;
+			case SMP2P_SENSOR_PROC:
+				name = get_ssr_name_for_proc(sensor_names,
+						ARRAY_SIZE(sensor_names),
+						name_index);
+				break;
+			case SMP2P_WIRELESS_PROC:
+				name = get_ssr_name_for_proc(wcnss_names,
+						ARRAY_SIZE(wcnss_names),
+						name_index);
+				break;
+			default:
+				pr_err("%s: Invalid proc ID %d given for ssr\n",
+						__func__, rpid);
+			}
+
+			if (!name) {
+				seq_puts(s, "\tSSR failed; check subsys name table\n");
+				failed = true;
+				break;
+			}
+
+			seq_printf(s, "Restarting '%s'\n", name);
+			ret = subsystem_restart(name);
+			if (ret == -ENODEV) {
+				seq_puts(s, "\tSSR call failed\n");
+				++name_index;
+				continue;
+			}
+			ssr_success = true;
+		}
+		if (failed)
+			break;
+
+		msleep(10*1000);
+
+		/* verify ack signaling */
+		if (ssr_ack_enabled) {
+			ssr_done_start ^= 1;
+			UT_ASSERT_INT(ssr_done_start, ==,
+					SMP2P_GET_RESTART_ACK(lhdr->flags));
+			UT_ASSERT_INT(ssr_done_start, ==,
+					SMP2P_GET_RESTART_DONE(rhdr->flags));
+			UT_ASSERT_INT(0, ==,
+					SMP2P_GET_RESTART_DONE(lhdr->flags));
+			seq_puts(s, "\tSSR ACK Enabled and Toggled\n");
+		} else {
+			UT_ASSERT_INT(0, ==,
+					SMP2P_GET_RESTART_DONE(lhdr->flags));
+			UT_ASSERT_INT(0, ==,
+					SMP2P_GET_RESTART_ACK(lhdr->flags));
+
+			UT_ASSERT_INT(0, ==,
+					SMP2P_GET_RESTART_DONE(rhdr->flags));
+			UT_ASSERT_INT(0, ==,
+					SMP2P_GET_RESTART_ACK(rhdr->flags));
+			seq_puts(s, "\tSSR ACK Disabled\n");
+		}
+
+		seq_puts(s, "\tOK\n");
+	} while (0);
+
+	if (failed) {
+		pr_err("%s: Failed\n", __func__);
+		seq_puts(s, "\tFailed\n");
+	}
+}
+
+/**
+ * smp2p_ut_remote_ssr_ack - Verify SSR Done/ACK Feature
+ *
+ * @s: pointer to output file
+ *
+ * Triggers SSR for each subsystem.
+ */
+static void smp2p_ut_remote_ssr_ack(struct seq_file *s)
+{
+	struct smp2p_interrupt_config *int_cfg;
+	int pid;
+
+	int_cfg = smp2p_get_interrupt_config();
+	if (!int_cfg) {
+		seq_puts(s,
+			"Remote processor config unavailable\n");
+		return;
+	}
+
+	for (pid = 0; pid < SMP2P_NUM_PROCS; ++pid) {
+		if (!int_cfg[pid].is_configured)
+			continue;
+
+		msm_smp2p_deinit_rmt_lpb_proc(pid);
+		smp2p_ut_remotesubsys_ssr_ack(s, pid, &int_cfg[pid]);
+		msm_smp2p_init_rmt_lpb_proc(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 smp2p_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);
+}
+
+void smp2p_debug_create_u32(const char *name, uint32_t *value)
+{
+	struct dentry *file;
+
+	file = debugfs_create_u32(name, 0644, dent, value);
+	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_test", 0);
+	if (IS_ERR(dent))
+		return PTR_ERR(dent);
+
+	/*
+	 * Add Unit Test entries.
+	 *
+	 * The idea with unit tests is that you can run all of them
+	 * from ADB shell by doing:
+	 *  adb shell
+	 *  cat ut*
+	 *
+	 * And if particular tests fail, you can then repeatedly run the
+	 * failing tests as you debug and resolve the failing test.
+	 */
+	smp2p_debug_create("ut_local_basic",
+			smp2p_ut_local_basic);
+	smp2p_debug_create("ut_local_late_open",
+			smp2p_ut_local_late_open);
+	smp2p_debug_create("ut_local_early_open",
+			smp2p_ut_local_early_open);
+	smp2p_debug_create("ut_mock_loopback",
+			smp2p_ut_mock_loopback);
+	smp2p_debug_create("ut_remote_inout",
+			smp2p_ut_remote_inout);
+	smp2p_debug_create("ut_local_in_max_entries",
+		smp2p_ut_local_in_max_entries);
+	smp2p_debug_create("ut_remote_out_max_entries",
+			smp2p_ut_remote_out_max_entries);
+	smp2p_debug_create("ut_local_in_multiple",
+			smp2p_ut_local_in_multiple);
+	smp2p_debug_create("ut_local_ssr_ack",
+			smp2p_ut_local_ssr_ack);
+	smp2p_debug_create("ut_remote_ssr_ack",
+			smp2p_ut_remote_ssr_ack);
+
+	return 0;
+}
+module_init(smp2p_debugfs_init);
diff --git a/drivers/soc/qcom/smp2p_test_common.h b/drivers/soc/qcom/smp2p_test_common.h
new file mode 100644
index 0000000..0d22fec
--- /dev/null
+++ b/drivers/soc/qcom/smp2p_test_common.h
@@ -0,0 +1,214 @@
+/* drivers/soc/qcom/smp2p_test_common.h
+ *
+ * Copyright (c) 2013-2014,2016 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.
+ */
+#ifndef _SMP2P_TEST_COMMON_H_
+#define _SMP2P_TEST_COMMON_H_
+
+#include <linux/debugfs.h>
+
+/**
+ * Unit test assertion for logging test cases.
+ *
+ * @a lval
+ * @b rval
+ * @cmp comparison operator
+ *
+ * Assertion fails if (@a cmp @b) is not true which then
+ * logs the function and line number where the error occurred
+ * along with the values of @a and @b.
+ *
+ * Assumes that the following local variables exist:
+ * @s - sequential output file pointer
+ * @failed - set to true if test fails
+ */
+#define UT_ASSERT_INT(a, cmp, b) \
+	{ \
+	int a_tmp = (a); \
+	int b_tmp = (b); \
+	if (!((a_tmp)cmp(b_tmp))) { \
+		seq_printf(s, "%s:%d Fail: " #a "(%d) " #cmp " " #b "(%d)\n", \
+				__func__, __LINE__, \
+				a_tmp, b_tmp); \
+		failed = 1; \
+		break; \
+	} \
+	}
+
+#define UT_ASSERT_PTR(a, cmp, b) \
+	{ \
+	void *a_tmp = (a); \
+	void *b_tmp = (b); \
+	if (!((a_tmp)cmp(b_tmp))) { \
+		seq_printf(s, "%s:%d Fail: " #a "(%pK) " #cmp \
+				" " #b "(%pK)\n", \
+				__func__, __LINE__, \
+				a_tmp, b_tmp); \
+		failed = 1; \
+		break; \
+	} \
+	}
+
+#define UT_ASSERT_UINT(a, cmp, b) \
+	{ \
+	unsigned int a_tmp = (a); \
+	unsigned int b_tmp = (b); \
+	if (!((a_tmp)cmp(b_tmp))) { \
+		seq_printf(s, "%s:%d Fail: " #a "(%u) " #cmp " " #b "(%u)\n", \
+				__func__, __LINE__, \
+				a_tmp, b_tmp); \
+		failed = 1; \
+		break; \
+	} \
+	}
+
+#define UT_ASSERT_HEX(a, cmp, b) \
+	{ \
+	unsigned int a_tmp = (a); \
+	unsigned int b_tmp = (b); \
+	if (!((a_tmp)cmp(b_tmp))) { \
+		seq_printf(s, "%s:%d Fail: " #a "(%x) " #cmp " " #b "(%x)\n", \
+				__func__, __LINE__, \
+				a_tmp, b_tmp); \
+		failed = 1; \
+		break; \
+	} \
+	}
+
+/**
+ * In-range unit test assertion for test cases.
+ *
+ * @a lval
+ * @minv Minimum value
+ * @maxv Maximum value
+ *
+ * Assertion fails if @a is not on the exclusive range minv, maxv
+ * ((@a < @minv) or (@a > @maxv)).  In the failure case, the macro
+ * logs the function and line number where the error occurred along
+ * with the values of @a and @minv, @maxv.
+ *
+ * Assumes that the following local variables exist:
+ * @s - sequential output file pointer
+ * @failed - set to true if test fails
+ */
+#define UT_ASSERT_INT_IN_RANGE(a, minv, maxv) \
+	{ \
+	int a_tmp = (a); \
+	int minv_tmp = (minv); \
+	int maxv_tmp = (maxv); \
+	if (((a_tmp) < (minv_tmp)) || ((a_tmp) > (maxv_tmp))) { \
+		seq_printf(s, "%s:%d Fail: " #a "(%d) < " #minv "(%d) or " \
+				 #a "(%d) > " #maxv "(%d)\n", \
+				__func__, __LINE__, \
+				a_tmp, minv_tmp, a_tmp, maxv_tmp); \
+		failed = 1; \
+		break; \
+	} \
+	}
+
+/* Structure to track state changes for the notifier callback. */
+struct mock_cb_data {
+	bool initialized;
+	spinlock_t lock;
+	struct notifier_block nb;
+
+	/* events */
+	struct completion cb_completion;
+	int cb_count;
+	int event_open;
+	int event_entry_update;
+	struct msm_smp2p_update_notif entry_data;
+};
+
+void smp2p_debug_create(const char *name, void (*show)(struct seq_file *));
+void smp2p_debug_create_u32(const char *name, uint32_t *value);
+static inline int smp2p_test_notify(struct notifier_block *self,
+	unsigned long event, void *data);
+
+/**
+ * Reset mock callback data to default values.
+ *
+ * @cb:  Mock callback data
+ */
+static inline void mock_cb_data_reset(struct mock_cb_data *cb)
+{
+	reinit_completion(&cb->cb_completion);
+	cb->cb_count = 0;
+	cb->event_open = 0;
+	cb->event_entry_update = 0;
+	memset(&cb->entry_data, 0,
+		sizeof(struct msm_smp2p_update_notif));
+}
+
+
+/**
+ * Initialize mock callback data.
+ *
+ * @cb:  Mock callback data
+ */
+static inline void mock_cb_data_init(struct mock_cb_data *cb)
+{
+	if (!cb->initialized) {
+		init_completion(&cb->cb_completion);
+		spin_lock_init(&cb->lock);
+		cb->initialized = true;
+		cb->nb.notifier_call = smp2p_test_notify;
+		memset(&cb->entry_data, 0,
+			sizeof(struct msm_smp2p_update_notif));
+	}
+	mock_cb_data_reset(cb);
+}
+
+/**
+ * Notifier function passed into SMP2P for testing.
+ *
+ * @self:       Pointer to calling notifier block
+ * @event:	    Event
+ * @data:       Event-specific data
+ * @returns:    0
+ */
+static inline int smp2p_test_notify(struct notifier_block *self,
+		unsigned long event, void *data)
+{
+	struct mock_cb_data *cb_data_ptr;
+	unsigned long flags;
+
+	cb_data_ptr = container_of(self, struct mock_cb_data, nb);
+
+	spin_lock_irqsave(&cb_data_ptr->lock, flags);
+
+	switch (event) {
+	case SMP2P_OPEN:
+		++cb_data_ptr->event_open;
+		if (data) {
+			cb_data_ptr->entry_data =
+			*(struct msm_smp2p_update_notif *)(data);
+		}
+		break;
+	case SMP2P_ENTRY_UPDATE:
+		++cb_data_ptr->event_entry_update;
+		if (data) {
+			cb_data_ptr->entry_data =
+			*(struct msm_smp2p_update_notif *)(data);
+		}
+		break;
+	default:
+		pr_err("%s Unknown event\n", __func__);
+		break;
+	}
+
+	++cb_data_ptr->cb_count;
+	complete(&cb_data_ptr->cb_completion);
+	spin_unlock_irqrestore(&cb_data_ptr->lock, flags);
+	return 0;
+}
+#endif /* _SMP2P_TEST_COMMON_H_ */