msm: smd: Add notifier chain to generate SMSM driver state notifications

This enables drivers to register for and receive any SMSM driver state
change notifications, including the initialization of SMSM driver.

CRs-Fixed: 338264
Change-Id: I090d19140f3bc46205566cd0431ed90edf440c02
Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
diff --git a/arch/arm/mach-msm/include/mach/msm_smsm.h b/arch/arm/mach-msm/include/mach/msm_smsm.h
index 6d45b7d..772c9f6 100644
--- a/arch/arm/mach-msm/include/mach/msm_smsm.h
+++ b/arch/arm/mach-msm/include/mach/msm_smsm.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -13,6 +13,7 @@
 #ifndef _ARCH_ARM_MACH_MSM_SMSM_H_
 #define _ARCH_ARM_MACH_MSM_SMSM_H_
 
+#include <linux/notifier.h>
 #if defined(CONFIG_MSM_N_WAY_SMSM)
 enum {
 	SMSM_APPS_STATE,
@@ -108,6 +109,8 @@
 	void *data);
 int smsm_state_cb_deregister(uint32_t smsm_entry, uint32_t mask,
 	void (*notify)(void *, uint32_t, uint32_t), void *data);
+int smsm_driver_state_notifier_register(struct notifier_block *nb);
+int smsm_driver_state_notifier_unregister(struct notifier_block *nb);
 void smsm_print_sleep_info(uint32_t sleep_delay, uint32_t sleep_limit,
 	uint32_t irq_mask, uint32_t wakeup_reason, uint32_t pending_irqs);
 void smsm_reset_modem(unsigned mode);
diff --git a/arch/arm/mach-msm/smd.c b/arch/arm/mach-msm/smd.c
index a1c044e..c502bce 100644
--- a/arch/arm/mach-msm/smd.c
+++ b/arch/arm/mach-msm/smd.c
@@ -33,6 +33,7 @@
 #include <linux/uaccess.h>
 #include <linux/kfifo.h>
 #include <linux/wakelock.h>
+#include <linux/notifier.h>
 #include <mach/msm_smd.h>
 #include <mach/msm_iomap.h>
 #include <mach/system.h>
@@ -334,6 +335,9 @@
 static DEFINE_MUTEX(smsm_lock);
 static struct smsm_state_info *smsm_states;
 static int spinlocks_initialized;
+static RAW_NOTIFIER_HEAD(smsm_driver_state_notifier_list);
+static DEFINE_MUTEX(smsm_driver_state_notifier_lock);
+static void smsm_driver_state_notify(uint32_t state, void *data);
 
 static inline void smd_write_intr(unsigned int val,
 				const void __iomem *addr)
@@ -2306,6 +2310,7 @@
 		return i;
 
 	wmb();
+	smsm_driver_state_notify(SMSM_INIT, NULL);
 	return 0;
 }
 
@@ -2764,6 +2769,38 @@
 }
 EXPORT_SYMBOL(smsm_state_cb_deregister);
 
+int smsm_driver_state_notifier_register(struct notifier_block *nb)
+{
+	int ret;
+	if (!nb)
+		return -EINVAL;
+	mutex_lock(&smsm_driver_state_notifier_lock);
+	ret = raw_notifier_chain_register(&smsm_driver_state_notifier_list, nb);
+	mutex_unlock(&smsm_driver_state_notifier_lock);
+	return ret;
+}
+EXPORT_SYMBOL(smsm_driver_state_notifier_register);
+
+int smsm_driver_state_notifier_unregister(struct notifier_block *nb)
+{
+	int ret;
+	if (!nb)
+		return -EINVAL;
+	mutex_lock(&smsm_driver_state_notifier_lock);
+	ret = raw_notifier_chain_unregister(&smsm_driver_state_notifier_list,
+					    nb);
+	mutex_unlock(&smsm_driver_state_notifier_lock);
+	return ret;
+}
+EXPORT_SYMBOL(smsm_driver_state_notifier_unregister);
+
+static void smsm_driver_state_notify(uint32_t state, void *data)
+{
+	mutex_lock(&smsm_driver_state_notifier_lock);
+	raw_notifier_call_chain(&smsm_driver_state_notifier_list,
+				state, data);
+	mutex_unlock(&smsm_driver_state_notifier_lock);
+}
 
 int smd_core_init(void)
 {