msm: smd: Refactor init code for platform data

Refactor init code to use configuration data from
platform devices. Platform device will contain the
interrupt numbers and other configurations
that is valid for a particular target. SMD init code
will use the platform data to register for and trigger
the interrupts which are relevant to a target.

Change-Id: If5fae72f8dc41e0d8397d0d9536b0013fa84243c
Signed-off-by: Angshuman Sarkar <angshuman@codeaurora.org>
diff --git a/arch/arm/mach-msm/devices-msm7x30.c b/arch/arm/mach-msm/devices-msm7x30.c
index db656f3..fc732da 100644
--- a/arch/arm/mach-msm/devices-msm7x30.c
+++ b/arch/arm/mach-msm/devices-msm7x30.c
@@ -581,9 +581,67 @@
 	},
 };
 
+static struct resource smd_resource[] = {
+	{
+		.name   = "a9_m2a_0",
+		.start  = INT_A9_M2A_0,
+		.flags  = IORESOURCE_IRQ,
+	},
+	{
+		.name   = "a9_m2a_5",
+		.start  = INT_A9_M2A_5,
+		.flags  = IORESOURCE_IRQ,
+	},
+	{
+		.name   = "adsp_a11_smsm",
+		.start  = INT_ADSP_A11,
+		.flags  = IORESOURCE_IRQ,
+	},
+};
+
+static struct smd_subsystem_config smd_config_list[] = {
+	{
+		.irq_config_id = SMD_MODEM,
+		.subsys_name = "modem",
+		.edge = SMD_APPS_MODEM,
+
+		.smd_int.irq_name = "a9_m2a_0",
+		.smd_int.flags = IRQF_TRIGGER_RISING,
+		.smd_int.irq_id = -1,
+		.smd_int.device_name = "smd_dev",
+		.smd_int.dev_id = 0,
+
+		.smd_int.out_bit_pos =  1 << 0,
+		.smd_int.out_base = (void __iomem *)MSM_GCC_BASE,
+		.smd_int.out_offset = 0x8,
+
+		.smsm_int.irq_name = "a9_m2a_5",
+		.smsm_int.flags = IRQF_TRIGGER_RISING,
+		.smsm_int.irq_id = -1,
+		.smsm_int.device_name = "smd_dev",
+		.smsm_int.dev_id = 0,
+
+		.smsm_int.out_bit_pos =  1 << 5,
+		.smsm_int.out_base = (void __iomem *)MSM_GCC_BASE,
+		.smsm_int.out_offset = 0x8,
+
+	}
+};
+
+static struct smd_platform smd_platform_data = {
+	.num_ss_configs = ARRAY_SIZE(smd_config_list),
+	.smd_ss_configs = smd_config_list,
+};
+
 struct platform_device msm_device_smd = {
 	.name	= "msm_smd",
 	.id	= -1,
+	.resource = smd_resource,
+	.num_resources = ARRAY_SIZE(smd_resource),
+	.dev = {
+		.platform_data = &smd_platform_data,
+	}
+
 };
 
 static struct resource msm_dmov_resource[] = {
diff --git a/arch/arm/mach-msm/include/mach/msm_smd.h b/arch/arm/mach-msm/include/mach/msm_smd.h
index af47425..4be6d9ea 100644
--- a/arch/arm/mach-msm/include/mach/msm_smd.h
+++ b/arch/arm/mach-msm/include/mach/msm_smd.h
@@ -1,7 +1,7 @@
 /* linux/include/asm-arm/arch-msm/msm_smd.h
  *
  * Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
  * Author: Brian Swetland <swetland@google.com>
  *
  * This software is licensed under the terms of the GNU General Public
@@ -18,6 +18,9 @@
 #ifndef __ASM_ARCH_MSM_SMD_H
 #define __ASM_ARCH_MSM_SMD_H
 
+#include <linux/io.h>
+#include <mach/msm_smsm.h>
+
 typedef struct smd_channel smd_channel_t;
 
 #define SMD_MAX_CH_NAME_LEN 20 /* includes null char at end */
@@ -28,6 +31,24 @@
 #define SMD_EVENT_STATUS 4
 #define SMD_EVENT_REOPEN_READY 5
 
+/*
+ * SMD Processor ID's.
+ *
+ * For all processors that have both SMSM and SMD clients,
+ * the SMSM Processor ID and the SMD Processor ID will
+ * be the same.  In cases where a processor only supports
+ * SMD, the entry will only exist in this enum.
+ */
+enum {
+	SMD_APPS = SMSM_APPS,
+	SMD_MODEM = SMSM_MODEM,
+	SMD_Q6 = SMSM_Q6,
+	SMD_WCNSS = SMSM_WCNSS,
+	SMD_DSPS = SMSM_DSPS,
+	SMD_MODEM_Q6_FW,
+	NUM_SMD_SUBSYSTEMS,
+};
+
 enum {
 	SMD_APPS_MODEM = 0,
 	SMD_APPS_QDSP,
@@ -49,6 +70,59 @@
 
 };
 
+/*
+ * SMD IRQ Configuration
+ *
+ * Used to initialize IRQ configurations from platform data
+ *
+ * @irq_name: irq_name to query platform data
+ * @irq_id: initialized to -1 in platform data, stores actual irq id on
+ *		successful registration
+ * @out_base: if not null then settings used for outgoing interrupt
+ *		initialied from platform data
+ */
+
+struct smd_irq_config {
+	/* incoming interrupt config */
+	const char *irq_name;
+	unsigned long flags;
+	int irq_id;
+	const char *device_name;
+	const void *dev_id;
+
+	/* outgoing interrupt config */
+	uint32_t out_bit_pos;
+	void __iomem *out_base;
+	uint32_t out_offset;
+};
+
+/*
+ * SMD subsystem configurations
+ *
+ * SMD subsystems configurations for platform data. This contains the
+ * M2A and A2M interrupt configurations for both SMD and SMSM per
+ * subsystem.
+ *
+ * @subsys_name: name of subsystem passed to PIL
+ * @irq_config_id: unique id for each subsystem
+ * @edge: maps to actual remote subsystem edge
+ *
+ */
+struct smd_subsystem_config {
+	unsigned irq_config_id;
+	const char *subsys_name;
+	int edge;
+
+	struct smd_irq_config smd_int;
+	struct smd_irq_config smsm_int;
+
+};
+
+struct smd_platform {
+	uint32_t num_ss_configs;
+	struct smd_subsystem_config *smd_ss_configs;
+};
+
 #ifdef CONFIG_MSM_SMD
 /* warning: notify() may be called before open returns */
 int smd_open(const char *name, smd_channel_t **ch, void *priv,
diff --git a/arch/arm/mach-msm/smd.c b/arch/arm/mach-msm/smd.c
index 10593c6..c9dacb7 100644
--- a/arch/arm/mach-msm/smd.c
+++ b/arch/arm/mach-msm/smd.c
@@ -102,6 +102,45 @@
 	uint32_t last_value;
 };
 
+struct interrupt_config_item {
+	/* must be initialized */
+	irqreturn_t (*irq_handler)(int req, void *data);
+	/* outgoing interrupt config (set from platform data) */
+	uint32_t out_bit_pos;
+	void __iomem *out_base;
+	uint32_t out_offset;
+};
+
+struct interrupt_config {
+	struct interrupt_config_item smd;
+	struct interrupt_config_item smsm;
+};
+
+static irqreturn_t smd_modem_irq_handler(int irq, void *data);
+static irqreturn_t smd_dsp_irq_handler(int irq, void *data);
+static irqreturn_t smd_dsps_irq_handler(int irq, void *data);
+static irqreturn_t smd_wcnss_irq_handler(int irq, void *data);
+static irqreturn_t smsm_irq_handler(int irq, void *data);
+
+static struct interrupt_config private_intr_config[NUM_SMD_SUBSYSTEMS] = {
+	[SMD_MODEM] = {
+		.smd.irq_handler = smd_modem_irq_handler,
+		.smsm.irq_handler = smsm_irq_handler,
+	},
+	[SMD_Q6] = {
+		.smd.irq_handler = smd_dsp_irq_handler,
+		.smsm.irq_handler = smsm_irq_handler,
+	},
+	[SMD_DSPS] = {
+		.smd.irq_handler = smd_dsps_irq_handler,
+		.smsm.irq_handler = smsm_irq_handler,
+	},
+	[SMD_WCNSS] = {
+		.smd.irq_handler = smd_wcnss_irq_handler,
+		.smsm.irq_handler = smsm_irq_handler,
+	},
+};
+
 #define SMSM_STATE_ADDR(entry)           (smsm_info.state + entry)
 #define SMSM_INTR_MASK_ADDR(entry, host) (smsm_info.intr_mask + \
 					  entry * SMSM_NUM_HOSTS + host)
@@ -240,7 +279,6 @@
 static remote_spinlock_t remote_spinlock;
 
 static LIST_HEAD(smd_ch_list_loopback);
-static irqreturn_t smsm_irq_handler(int irq, void *data);
 static void smd_fake_irq_handler(unsigned long arg);
 static void smsm_cb_snapshot(void);
 
@@ -275,6 +313,98 @@
 static inline void wakeup_v1_riva(void) {}
 #endif
 
+static inline void notify_modem_smd(void)
+{
+	static const struct interrupt_config_item *intr
+	   = &private_intr_config[SMD_MODEM].smd;
+	if (intr->out_base)
+		smd_write_intr(intr->out_bit_pos,
+		intr->out_base + intr->out_offset);
+	else
+		MSM_TRIG_A2M_SMD_INT;
+}
+
+static inline void notify_dsp_smd(void)
+{
+	static const struct interrupt_config_item *intr
+		= &private_intr_config[SMD_Q6].smd;
+	if (intr->out_base)
+		smd_write_intr(intr->out_bit_pos,
+		intr->out_base + intr->out_offset);
+	else
+		MSM_TRIG_A2Q6_SMD_INT;
+}
+
+static inline void notify_dsps_smd(void)
+{
+	static const struct interrupt_config_item *intr
+		= &private_intr_config[SMD_DSPS].smd;
+	if (intr->out_base)
+		smd_write_intr(intr->out_bit_pos,
+		intr->out_base + intr->out_offset);
+	else
+		MSM_TRIG_A2DSPS_SMD_INT;
+}
+
+static inline void notify_wcnss_smd(void)
+{
+	static const struct interrupt_config_item *intr
+		= &private_intr_config[SMD_WCNSS].smd;
+	wakeup_v1_riva();
+
+	if (intr->out_base)
+		smd_write_intr(intr->out_bit_pos,
+		intr->out_base + intr->out_offset);
+	else
+		MSM_TRIG_A2WCNSS_SMD_INT;
+}
+
+static inline void notify_modem_smsm(void)
+{
+	static const struct interrupt_config_item *intr
+		= &private_intr_config[SMD_MODEM].smsm;
+	if (intr->out_base)
+		smd_write_intr(intr->out_bit_pos,
+		intr->out_base + intr->out_offset);
+	else
+		MSM_TRIG_A2M_SMSM_INT;
+}
+
+static inline void notify_dsp_smsm(void)
+{
+	static const struct interrupt_config_item *intr
+		= &private_intr_config[SMD_Q6].smsm;
+	if (intr->out_base)
+		smd_write_intr(intr->out_bit_pos,
+		intr->out_base + intr->out_offset);
+	else
+		MSM_TRIG_A2Q6_SMSM_INT;
+}
+
+static inline void notify_dsps_smsm(void)
+{
+	static const struct interrupt_config_item *intr
+		= &private_intr_config[SMD_DSPS].smsm;
+	if (intr->out_base)
+		smd_write_intr(intr->out_bit_pos,
+		intr->out_base + intr->out_offset);
+	else
+		MSM_TRIG_A2DSPS_SMSM_INT;
+}
+
+static inline void notify_wcnss_smsm(void)
+{
+	static const struct interrupt_config_item *intr
+		= &private_intr_config[SMD_WCNSS].smsm;
+	wakeup_v1_riva();
+
+	if (intr->out_base)
+		smd_write_intr(intr->out_bit_pos,
+		intr->out_base + intr->out_offset);
+	else
+		MSM_TRIG_A2WCNSS_SMSM_INT;
+}
+
 static void notify_other_smsm(uint32_t smsm_entry, uint32_t notify_mask)
 {
 	/* older protocol don't use smsm_intr_mask,
@@ -282,7 +412,7 @@
 	if (!smsm_info.intr_mask ||
 	    (__raw_readl(SMSM_INTR_MASK_ADDR(smsm_entry, SMSM_MODEM))
 				& notify_mask))
-		MSM_TRIG_A2M_SMSM_INT;
+		notify_modem_smsm();
 
 	if (smsm_info.intr_mask &&
 	    (__raw_readl(SMSM_INTR_MASK_ADDR(smsm_entry, SMSM_Q6))
@@ -296,46 +426,24 @@
 			__raw_writel(mux_val,
 					SMSM_INTR_MUX_ADDR(SMEM_APPS_Q6_SMSM));
 		}
-		MSM_TRIG_A2Q6_SMSM_INT;
+		notify_dsp_smsm();
 	}
 
 	if (smsm_info.intr_mask &&
 	    (__raw_readl(SMSM_INTR_MASK_ADDR(smsm_entry, SMSM_WCNSS))
 				& notify_mask)) {
-		wakeup_v1_riva();
-		MSM_TRIG_A2WCNSS_SMSM_INT;
+		notify_wcnss_smsm();
 	}
 
 	if (smsm_info.intr_mask &&
 	    (__raw_readl(SMSM_INTR_MASK_ADDR(smsm_entry, SMSM_DSPS))
 				& notify_mask)) {
-		MSM_TRIG_A2DSPS_SMSM_INT;
+		notify_dsps_smsm();
 	}
 
 	smsm_cb_snapshot();
 }
 
-static inline void notify_modem_smd(void)
-{
-	MSM_TRIG_A2M_SMD_INT;
-}
-
-static inline void notify_dsp_smd(void)
-{
-	MSM_TRIG_A2Q6_SMD_INT;
-}
-
-static inline void notify_dsps_smd(void)
-{
-	MSM_TRIG_A2DSPS_SMD_INT;
-}
-
-static inline void notify_wcnss_smd(void)
-{
-	wakeup_v1_riva();
-	MSM_TRIG_A2WCNSS_SMD_INT;
-}
-
 void smd_diag(void)
 {
 	char *x;
@@ -451,23 +559,6 @@
 	uint32_t	remote_pid;
 };
 
-/*
- * SMD Processor ID's.
- *
- * For all processors that have both SMSM and SMD clients,
- * the SMSM Processor ID and the SMD Processor ID will
- * be the same.  In cases where a processor only supports
- * SMD, the entry will only exist in this enum.
- */
-enum {
-	SMD_APPS = SMSM_APPS,
-	SMD_MODEM = SMSM_MODEM,
-	SMD_Q6 = SMSM_Q6,
-	SMD_WCNSS = SMSM_WCNSS,
-	SMD_DSPS = SMSM_DSPS,
-	SMD_MODEM_Q6_FW,
-};
-
 /**
  * Maps edge type to local and remote processor ID's.
  */
@@ -1008,32 +1099,26 @@
 	return IRQ_HANDLED;
 }
 
-#if defined(CONFIG_QDSP6)
 static irqreturn_t smd_dsp_irq_handler(int irq, void *data)
 {
 	handle_smd_irq(&smd_ch_list_dsp, notify_dsp_smd);
 	handle_smd_irq_closing_list();
 	return IRQ_HANDLED;
 }
-#endif
 
-#if defined(CONFIG_DSPS)
 static irqreturn_t smd_dsps_irq_handler(int irq, void *data)
 {
 	handle_smd_irq(&smd_ch_list_dsps, notify_dsps_smd);
 	handle_smd_irq_closing_list();
 	return IRQ_HANDLED;
 }
-#endif
 
-#if defined(CONFIG_WCNSS)
 static irqreturn_t smd_wcnss_irq_handler(int irq, void *data)
 {
 	handle_smd_irq(&smd_ch_list_wcnss, notify_wcnss_smd);
 	handle_smd_irq_closing_list();
 	return IRQ_HANDLED;
 }
-#endif
 
 static void smd_fake_irq_handler(unsigned long arg)
 {
@@ -2567,22 +2652,121 @@
 		pr_err("smd_core_init: "
 		       "enable_irq_wake failed for INT_DSPS_A11_SMSM\n");
 #endif
-
-	/* we may have missed a signal while booting -- fake
-	 * an interrupt to make sure we process any existing
-	 * state
-	 */
-	smsm_irq_handler(0, 0);
-
 	SMD_INFO("smd_core_init() done\n");
 
 	return 0;
 }
 
+static int intr_init(struct interrupt_config_item *private_irq,
+			struct smd_irq_config *platform_irq,
+			struct platform_device *pdev
+			)
+{
+	int irq_id;
+	int ret;
+	int ret_wake;
+
+	private_irq->out_bit_pos = platform_irq->out_bit_pos;
+	private_irq->out_offset = platform_irq->out_offset;
+	private_irq->out_base = platform_irq->out_base;
+
+	irq_id = platform_get_irq_byname(
+					pdev,
+					platform_irq->irq_name
+				);
+	SMD_DBG("smd: %s: register irq: %s id: %d\n", __func__,
+				platform_irq->irq_name, irq_id);
+	ret = request_irq(irq_id,
+				private_irq->irq_handler,
+				platform_irq->flags,
+				platform_irq->device_name,
+				(void *)platform_irq->dev_id
+			);
+	if (ret < 0) {
+		platform_irq->irq_id = ret;
+	} else {
+		platform_irq->irq_id = irq_id;
+		ret_wake = enable_irq_wake(irq_id);
+		if (ret_wake < 0) {
+			pr_err("smd: enable_irq_wake failed on %s",
+					platform_irq->irq_name);
+		}
+	}
+
+	return ret;
+}
+
+int smd_core_platform_init(struct platform_device *pdev)
+{
+	int i;
+	int ret;
+	uint32_t num_ss;
+	struct smd_platform *smd_platform_data;
+	struct smd_subsystem_config *smd_ss_config_list;
+	struct smd_subsystem_config *cfg;
+	int err_ret = 0;
+
+	smd_platform_data = pdev->dev.platform_data;
+	num_ss = smd_platform_data->num_ss_configs;
+	smd_ss_config_list = smd_platform_data->smd_ss_configs;
+
+	for (i = 0; i < num_ss; i++) {
+		cfg = &smd_ss_config_list[i];
+
+		ret = intr_init(
+			&private_intr_config[cfg->irq_config_id].smd,
+			&cfg->smd_int,
+			pdev
+			);
+
+		if (ret < 0) {
+			err_ret = ret;
+			pr_err("smd: register irq failed on %s\n",
+				cfg->smd_int.irq_name);
+			break;
+		}
+
+		ret = intr_init(
+			&private_intr_config[cfg->irq_config_id].smsm,
+			&cfg->smsm_int,
+			pdev
+			);
+
+		if (ret < 0) {
+			err_ret = ret;
+			pr_err("smd: register irq failed on %s\n",
+				cfg->smsm_int.irq_name);
+			break;
+		}
+	}
+
+	if (err_ret < 0) {
+		pr_err("smd: deregistering IRQs\n");
+		for (i = 0; i < num_ss; ++i) {
+			cfg = &smd_ss_config_list[i];
+
+			if (cfg->smd_int.irq_id >= 0)
+				free_irq(cfg->smd_int.irq_id,
+					(void *)cfg->smd_int.dev_id
+					);
+			if (cfg->smsm_int.irq_id >= 0)
+				free_irq(cfg->smsm_int.irq_id,
+					(void *)cfg->smsm_int.dev_id
+					);
+		}
+		return err_ret;
+	}
+
+	SMD_INFO("smd_core_platform_init() done\n");
+	return 0;
+
+}
+
 static int __devinit msm_smd_probe(struct platform_device *pdev)
 {
-	SMD_INFO("smd probe\n");
+	int ret;
 
+	SMD_INFO("smd probe\n");
 	INIT_WORK(&probe_work, smd_channel_probe_worker);
 
 	channel_close_wq = create_singlethread_workqueue("smd_channel_close");
@@ -2596,9 +2780,27 @@
 		return -1;
 	}
 
-	if (smd_core_init()) {
-		pr_err("smd_core_init() failed\n");
-		return -1;
+	if (pdev) {
+		if (pdev->dev.of_node) {
+			pr_err("SMD: Device tree not currently supported\n");
+			return -ENODEV;
+		} else if (pdev->dev.platform_data) {
+			ret = smd_core_platform_init(pdev);
+			if (ret) {
+				pr_err(
+				"SMD: smd_core_platform_init() failed\n");
+				return -ENODEV;
+			}
+		} else {
+			ret = smd_core_init();
+			if (ret) {
+				pr_err("smd_core_init() failed\n");
+				return -ENODEV;
+			}
+		}
+	} else {
+		pr_err("SMD: PDEV not found\n");
+		return -ENODEV;
 	}
 
 	smd_initialized = 1;