[Feature][API-732][lpm] add common framework for suspend/resume actions in lpm

Change-Id: Id7c1fe9d5f1d58aed459f7271d974edf4f340531
diff --git a/src/kernel/linux/v4.19/arch/arm64/boot/dts/mediatek/auto2735evb.dts b/src/kernel/linux/v4.19/arch/arm64/boot/dts/mediatek/auto2735evb.dts
index 973fcfd..33798e2 100755
--- a/src/kernel/linux/v4.19/arch/arm64/boot/dts/mediatek/auto2735evb.dts
+++ b/src/kernel/linux/v4.19/arch/arm64/boot/dts/mediatek/auto2735evb.dts
@@ -1574,5 +1574,31 @@
 
 };
 /*Lxf add in 2022/12/2 for gpio init end*/
+/*youchen@2023-01-05 add for lpm suspend actions begin*/
+&lynq_lpm_gpio_actions {
+	wifi_power_202: gpio@202 {
+        valid_absence_of_module = "bcmdhd";
+        gpio_num = <202>;
+        actions_suspend_202: suspend@202{
+            gpio_value = <0>;
+        };
+        actions_resume_202: resume@202 {
+            gpio_value = <1>;
+            post_mdelay_time = <10>;
+        };
+    };
+    wifi_power_182: gpio@182 {
+        valid_absence_of_module = "bcmdhd";
+        gpio_num = <182>;
+        actions_suspend_182: suspend@182{
+            gpio_value = <0>;
+        };
+        actions_resume_182: resume@182 {
+            gpio_value = <1>;
+            post_mdelay_time = <1>;
+        };
+    };
+};
+/*youchen@2023-01-05 add for lpm suspend actions end*/
 #include <mediatek/evb6890v1_64_cpe/cust.dtsi>
 /*End of this file, DO NOT ADD ANYTHING HERE*/
diff --git a/src/kernel/linux/v4.19/arch/arm64/boot/dts/mediatek/mt2735.dtsi b/src/kernel/linux/v4.19/arch/arm64/boot/dts/mediatek/mt2735.dtsi
index 3c8adf4..6b11ec6 100644
--- a/src/kernel/linux/v4.19/arch/arm64/boot/dts/mediatek/mt2735.dtsi
+++ b/src/kernel/linux/v4.19/arch/arm64/boot/dts/mediatek/mt2735.dtsi
@@ -4505,6 +4505,11 @@
 		compatible = "linux,gpio_init";
 	};
 /*Lxf add in 2022/12/2 for gpio init end*/
+/*youchen@2023-01-05 add for lpm suspend actions begin*/
+	lynq_lpm_gpio_actions: lynq_lpm_gpio_actions {
+		compatible = "linux,lynq_lpm_gpio_actions";
+	};
+/*youchen@2023-01-05 add for lpm suspend actions end*/
 	firmware {
 		optee {
 			compatible = "linaro,optee-tz";
diff --git a/src/kernel/linux/v4.19/arch/arm64/configs/auto2735evb_defconfig b/src/kernel/linux/v4.19/arch/arm64/configs/auto2735evb_defconfig
index fe4ef53..b6185c1 100644
--- a/src/kernel/linux/v4.19/arch/arm64/configs/auto2735evb_defconfig
+++ b/src/kernel/linux/v4.19/arch/arm64/configs/auto2735evb_defconfig
@@ -825,3 +825,5 @@
 CONFIG_SMI230_GYRO=y
 CONFIG_SMI230_GYRO_I2C=y
 #dongyu@2022.11.03 Add imu/smi230 sensor end
+#you.chen@2023-01-05 add for lpm suspend actions
+CONFIG_LYNQ_LPM_SUSPEND_SUPPORT=y
diff --git a/src/kernel/linux/v4.19/drivers/misc/mediatek/lpm/Kconfig b/src/kernel/linux/v4.19/drivers/misc/mediatek/lpm/Kconfig
index 65e6234..1842bb0 100644
--- a/src/kernel/linux/v4.19/drivers/misc/mediatek/lpm/Kconfig
+++ b/src/kernel/linux/v4.19/drivers/misc/mediatek/lpm/Kconfig
@@ -32,3 +32,10 @@
 	  Set Y to select this feature for specific platform-mt6779.
 	  If unsure, set N to disable.
 
+config LYNQ_LPM_SUSPEND_SUPPORT
+	bool "Mobiletek Low Power Module Suspend support"
+	depends on MTK_LOW_POWER_MODULE
+	help
+	   Select LYNQ_LPM_SUSPEND_SUPPORT then low module
+	   feature of Mobiletck suspend will be enabled. 
+
diff --git a/src/kernel/linux/v4.19/drivers/misc/mediatek/lpm/modules/platform/mt6880/Makefile b/src/kernel/linux/v4.19/drivers/misc/mediatek/lpm/modules/platform/mt6880/Makefile
index ca3cdd3..74ca59b 100755
--- a/src/kernel/linux/v4.19/drivers/misc/mediatek/lpm/modules/platform/mt6880/Makefile
+++ b/src/kernel/linux/v4.19/drivers/misc/mediatek/lpm/modules/platform/mt6880/Makefile
@@ -23,3 +23,9 @@
 obj-y += mtk_lp_plat_pll.o
 
 obj-y += suspend/mtk_suspend.o
+#you.chen@2023-01-05 add for lpm suspend actions begin
+ifeq ($(CONFIG_LYNQ_LPM_SUSPEND_SUPPORT), y)
+ccflags-y += -I$(srctree)/drivers/pinctrl/mediatek/
+obj-y += suspend/lynq_suspend.o
+endif
+#you.chen@2023-01-05 add for lpm suspend actions end
diff --git a/src/kernel/linux/v4.19/drivers/misc/mediatek/lpm/modules/platform/mt6880/suspend/lynq_suspend.c b/src/kernel/linux/v4.19/drivers/misc/mediatek/lpm/modules/platform/mt6880/suspend/lynq_suspend.c
new file mode 100644
index 0000000..0aa4b7c
--- /dev/null
+++ b/src/kernel/linux/v4.19/drivers/misc/mediatek/lpm/modules/platform/mt6880/suspend/lynq_suspend.c
@@ -0,0 +1,371 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2023 Mobiletek Inc.
+ */
+
+#include <linux/cpuidle.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/slab.h>
+#include <linux/cpu_pm.h>
+#include <linux/syscore_ops.h>
+#include <linux/suspend.h>
+#include <linux/rtc.h>
+#include <asm/cpuidle.h>
+#include <asm/suspend.h>
+#include <linux/delay.h>
+
+#include <linux/platform_device.h>
+#include <pinctrl-mtk-common-v2.h>
+#include <lynq_suspend.h>
+extern struct mtk_pinctrl* mtk_gpio_find_mtk_pinctrl_dev_hw(void);
+
+#define SUSPEND_GPIO_MAX 16
+
+#undef printk_deferred
+#define printk_deferred printk
+#define DEBUG printk
+
+enum e_gpio_suspend_resume_flag{
+	ACT_SUSPEND=0,
+	ACT_RESUME,
+	ACT_MAX
+};
+
+struct gpio_action{
+	int gpio_mode;
+	int gpio_dir;
+	int gpio_value;
+	int pre_mdelay_time;
+	int post_mdelay_time;
+};
+
+struct gpio_action_node{
+	char valid_absence_of_module[16];
+    int gpio_num;
+	int need_action;
+	struct gpio_action actions[ACT_MAX];
+};
+
+struct gpio_action_node * lpm_gpio_list[SUSPEND_GPIO_MAX] = {0};
+
+static inline void __inner_init_action(struct gpio_action *act)
+{
+	if (act == NULL)
+		return;
+	act->gpio_mode = -1;
+	act->gpio_dir = -1;
+	act->gpio_value = -1;
+	act->pre_mdelay_time = -1;
+	act->post_mdelay_time = -1;
+}
+
+static inline struct gpio_action_node * __inner_alloc_gpio_action_node(void)
+{
+	int i;
+	struct gpio_action_node * node;
+	node = kmalloc(sizeof(struct gpio_action_node), GFP_KERNEL);
+	if (node == NULL)
+		return NULL;
+	memset(node->valid_absence_of_module, 0, sizeof(node->valid_absence_of_module));
+	node->gpio_num = -1;
+	node->need_action = 0;
+	for(i=0; i<ACT_MAX; i++)
+	{
+		__inner_init_action(&node->actions[i]);
+	}
+	return node;
+}
+
+static inline void prepare_action_with_condition(void)
+{
+	int i;
+
+	for(i=0; i< SUSPEND_GPIO_MAX; i++)
+	{
+		if (lpm_gpio_list[i] == NULL)
+			break;
+		if (lpm_gpio_list[i]->gpio_num <= 0 || lpm_gpio_list[i]->valid_absence_of_module[0] == '\0')
+			continue;
+		if (find_module(lpm_gpio_list[i]->valid_absence_of_module) == NULL)
+		{
+			lpm_gpio_list[i]->need_action = 1;
+		}
+		else
+		{
+			lpm_gpio_list[i]->need_action = 0;
+		}
+	}
+}
+
+static inline void process_one_action(struct gpio_action * act, struct mtk_pinctrl *hw,  const struct mtk_pin_desc *desc)
+{
+	int ret;
+	unsigned int gpio_dir;
+
+	if (act == NULL || hw == NULL || hw->soc == NULL)
+		return;
+
+	if (act->pre_mdelay_time > 0)
+	{
+		DEBUG("to delay %d\n", act->pre_mdelay_time);
+		mdelay(act->pre_mdelay_time);
+	}
+
+	if (act->gpio_mode >= 0)
+	{
+		DEBUG("to set gpio mode %d\n", act->gpio_mode);
+		ret = mtk_hw_set_value(hw, desc, PINCTRL_PIN_REG_MODE, act->gpio_mode);
+		if (ret != 0)
+			return;
+	}
+
+	if (act->gpio_dir == 0 || act->gpio_dir == 1)
+	{
+		DEBUG("to set gpio dir %d\n", act->gpio_dir);
+		ret =  mtk_hw_set_value(hw, desc, PINCTRL_PIN_REG_DIR, act->gpio_dir);	
+		if (ret != 0)
+			return;
+	}
+
+	if (act->gpio_value == 0 || act->gpio_value == 1)
+	{
+		gpio_dir = -1;
+		if (act->gpio_dir == 0 || act->gpio_dir == 1)
+			gpio_dir = act->gpio_dir;
+		else
+			ret = mtk_hw_get_value(hw, desc, PINCTRL_PIN_REG_DIR, &gpio_dir);
+
+		if (gpio_dir == 1)
+		{
+			DEBUG("to set gpio dir %d value %d\n", gpio_dir, act->gpio_value);
+			ret = mtk_hw_set_value(hw, desc, PINCTRL_PIN_REG_DO, act->gpio_value);
+		}
+		else if (gpio_dir == 0)
+		{
+			DEBUG("to set gpio dir2 %d value %d\n", gpio_dir, act->gpio_value);
+			ret = mtk_hw_set_value(hw, desc, PINCTRL_PIN_REG_DI, act->gpio_value);
+		}
+		else
+		{
+			printk("unkown gpio dir %d\n", gpio_dir);
+		}
+	}
+
+	if (act->post_mdelay_time > 0)
+	{
+		DEBUG("post delay %d\n", act->post_mdelay_time);
+		mdelay(act->post_mdelay_time);
+	}
+}
+
+int lynq_suspend_common_enter(unsigned int *susp_status)
+{
+	int i;
+	struct mtk_pinctrl *hw;
+	const struct mtk_pin_desc *desc;
+
+	printk_deferred("lynq_suspend_common_enter\n");
+
+	prepare_action_with_condition();
+
+	hw = mtk_gpio_find_mtk_pinctrl_dev_hw();
+
+	for(i=0; i< SUSPEND_GPIO_MAX; i++)
+	{
+		if (lpm_gpio_list[i] == NULL)
+			break;
+		if (lpm_gpio_list[i]->gpio_num <= 0 || lpm_gpio_list[i]->need_action != 1)
+			continue;
+
+		desc = (const struct mtk_pin_desc *)&hw->soc->pins[lpm_gpio_list[i]->gpio_num];
+
+		printk("to do suspend of gpio %d\n", lpm_gpio_list[i]->gpio_num);
+		process_one_action(&lpm_gpio_list[i]->actions[ACT_SUSPEND], hw, desc);
+	}
+
+	return 0;
+}
+
+
+int lynq_suspend_common_resume(unsigned int susp_status)
+{
+	int i;
+	struct mtk_pinctrl *hw;
+	const struct mtk_pin_desc *desc;
+
+	printk_deferred("lynq_suspend_common_resume\n");
+	/* Implement suspend common flow here */
+
+	hw = mtk_gpio_find_mtk_pinctrl_dev_hw();
+
+	for(i=SUSPEND_GPIO_MAX-1; i>= 0; i--)
+	{
+		if (lpm_gpio_list[i] == NULL || lpm_gpio_list[i]->gpio_num <= 0 || lpm_gpio_list[i]->need_action != 1)
+			continue;
+
+		desc = (const struct mtk_pin_desc *)&hw->soc->pins[lpm_gpio_list[i]->gpio_num];
+
+		printk("to do resume of gpio %d\n", lpm_gpio_list[i]->gpio_num);
+		process_one_action(&lpm_gpio_list[i]->actions[ACT_RESUME], hw, desc);
+	}
+
+	return 0;
+}
+
+static inline int read_one_action(struct device_node *gpio_node, struct gpio_action *action_node)
+{
+	int ret;
+	unsigned int gpio_mode, gpio_dir, gpio_value, pre_mdelay_time, post_mdelay_time;
+
+	ret = of_property_read_u32(gpio_node, "gpio_mode", &gpio_mode);
+    if(ret == 0)
+    {
+        printk("read_one_node: the gpio_mode is %u", gpio_mode);
+        if(gpio_mode >= 0 && gpio_mode <= 7)
+        {
+            action_node->gpio_mode = gpio_mode;
+        }
+    }
+
+	ret = of_property_read_u32(gpio_node, "gpio_dir", &gpio_dir);
+    if(ret == 0)
+    {
+        printk("read_one_node: the gpio_dir is %u", gpio_dir);
+        if(gpio_dir == 0 || gpio_dir == 1)
+        {
+            action_node->gpio_dir = gpio_dir;
+        }
+    }
+
+	ret = of_property_read_u32(gpio_node, "gpio_value", &gpio_value);
+    if(ret == 0)
+    {
+        printk("read_one_node: the gpio_value is %u", gpio_value);
+        if(gpio_value == 0 || gpio_value == 1)
+        {
+            action_node->gpio_value = gpio_value;
+        }
+    }
+
+	ret = of_property_read_u32(gpio_node, "pre_mdelay_time", &pre_mdelay_time);
+    if(ret == 0)
+    {
+        printk("read_one_node: the pre_mdelay is %u", pre_mdelay_time);
+        if(pre_mdelay_time < 1000*100)
+        {
+            action_node->pre_mdelay_time = pre_mdelay_time;
+        }
+    }
+
+	ret = of_property_read_u32(gpio_node, "post_mdelay_time", &post_mdelay_time);
+    if(ret == 0)
+    {
+        printk("read_one_node: the post_mdelay is %u", post_mdelay_time);
+        if(post_mdelay_time < 1000*100)
+        {
+            action_node->post_mdelay_time = post_mdelay_time;
+        }
+    }
+
+	return 0;
+}
+
+static inline int read_one_node(struct device_node *gpio_node, struct gpio_action_node * action_node)
+{
+	int ret;
+	unsigned int gpio_num;
+	const char * valid_absence_of_module;
+	struct device_node *gpio_next_node;
+
+	printk("read_one_node\n");
+	ret = of_property_read_u32(gpio_node, "gpio_num", &gpio_num);
+
+    if(ret == -EINVAL || ret == -ENODATA)
+    {
+        printk("no value or no node");
+        return -1;
+    }
+    else if(ret < 0)
+    {
+        printk("read_one_node:READ ERROR: %d", ret);
+        return ret;
+    }
+    action_node->gpio_num = gpio_num;
+
+	valid_absence_of_module = NULL;
+	ret = of_property_read_string(gpio_node, "valid_absence_of_module", &valid_absence_of_module);
+
+	if (ret == 0 && valid_absence_of_module != NULL)
+	{
+		strncpy(action_node->valid_absence_of_module,  valid_absence_of_module, sizeof(action_node->valid_absence_of_module));
+		printk("read module %s\n", action_node->valid_absence_of_module);
+	}
+
+	printk("to find act_suspend\n");
+	gpio_next_node = of_find_node_by_name(gpio_node, "suspend");
+	if (gpio_next_node == NULL)
+		printk("no act_suspend node found\n");
+	else
+		printk("find act_suspend\n");
+	read_one_action(gpio_next_node, &action_node->actions[ACT_SUSPEND]);
+	gpio_next_node = of_find_node_by_name(gpio_node, "resume");
+	if (gpio_next_node == NULL)
+		printk("no act_resume node found\n");
+	else
+		printk("find act_resume\n");
+	read_one_action(gpio_next_node, &action_node->actions[ACT_RESUME]);
+
+	return 0;
+}
+
+static int __init lynq_suspend_init(void)
+{
+	int node_num, i;
+	struct device_node *gpio_device_node, *gpio_next_node;
+
+	gpio_device_node = of_find_node_by_path("/lynq_lpm_gpio_actions");
+	//gpio_device_node = of_find_node_by_path("/gpio_init");
+    if(gpio_device_node == NULL)
+    {
+        printk_deferred("lynq_suspend_init: get DTS property failed!\n");
+        return 0;
+    }
+
+	node_num = of_get_child_count(gpio_device_node);
+    if(node_num > SUSPEND_GPIO_MAX)
+    {
+        node_num = SUSPEND_GPIO_MAX;
+        printk("lynq_suspend_init: over the max number");
+    }
+    else
+    {
+        printk("lynq_suspend_init: node number: %d", node_num);
+    }
+
+	gpio_next_node = NULL;
+
+	for(i = 0; i < node_num; i++)
+    {
+        gpio_next_node = of_get_next_child(gpio_device_node, gpio_next_node);
+        if(!gpio_next_node)
+        {
+            printk("no more node");
+            break;
+        }
+		lpm_gpio_list[i] = __inner_alloc_gpio_action_node();
+		read_one_node(gpio_next_node, lpm_gpio_list[i]);
+		printk("read one node finish\n");
+    }
+
+	printk_deferred("[name:lynq_spm&][%s:%d] - suspend init\n",
+			__func__, __LINE__);
+
+	return 0;
+}
+
+late_initcall(lynq_suspend_init);
+
+MODULE_LICENSE("GPL");
diff --git a/src/kernel/linux/v4.19/drivers/misc/mediatek/lpm/modules/platform/mt6880/suspend/lynq_suspend.h b/src/kernel/linux/v4.19/drivers/misc/mediatek/lpm/modules/platform/mt6880/suspend/lynq_suspend.h
new file mode 100644
index 0000000..82d862e
--- /dev/null
+++ b/src/kernel/linux/v4.19/drivers/misc/mediatek/lpm/modules/platform/mt6880/suspend/lynq_suspend.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2023 Mobiletek Inc.
+ */
+
+#ifndef __LYNQ_SUSPEND_H__
+#define __LYNQ_SUSPEND_H__
+
+int lynq_suspend_common_enter(unsigned int *susp_status);
+
+int lynq_suspend_common_resume(unsigned int susp_status);
+
+#endif
diff --git a/src/kernel/linux/v4.19/drivers/misc/mediatek/lpm/modules/platform/mt6880/suspend/mtk_suspend.c b/src/kernel/linux/v4.19/drivers/misc/mediatek/lpm/modules/platform/mt6880/suspend/mtk_suspend.c
index 0fdc558..a155a10 100644
--- a/src/kernel/linux/v4.19/drivers/misc/mediatek/lpm/modules/platform/mt6880/suspend/mtk_suspend.c
+++ b/src/kernel/linux/v4.19/drivers/misc/mediatek/lpm/modules/platform/mt6880/suspend/mtk_suspend.c
@@ -30,9 +30,11 @@
 #ifdef CONFIG_MTK_CCCI_DEVICES
 #include <mt-plat/mtk_ccci_common.h>
 #endif
-#include <linux/gpio.h>
-
-#define MTK_GSW_WG870_GPIO_PWR_RST
+//you.chen@2023-01-05 add for lpm suspend actions begin
+#ifdef CONFIG_LYNQ_LPM_SUSPEND_SUPPORT
+#include <lynq_suspend.h>
+#endif
+//you.chen@2023-01-05 add for lpm suspend actions end
 
 unsigned int mtk_suspend_status;
 u64 before_md_sleep_time;
@@ -95,19 +97,16 @@
 				| PLAT_PMIC_VCORE_SRCLKEN0
 				| PLAT_SUSPEND;
 
+//you.chen@2023-01-05 add for lpm suspend actions begin
+#ifdef CONFIG_LYNQ_LPM_SUSPEND_SUPPORT
+	lynq_suspend_common_enter(susp_status);
+#endif
+//you.chen@2023-01-05 add for lpm suspend actions end
+
 	/* maybe need to stop sspm/mcupm mcdi task here */
 	if (susp_status)
 		*susp_status = status;
 
-	//you.chen@2022-07-15 add for shutdown wifi power when suspend begin(for temp)
-#if defined(MTK_GSW_WG870_GPIO_PWR_RST)
-    /* Control GPIO182 for turn off WIFI chip */
-    printk_deferred("mt_spm_suspend_enter\n");
-    gpio_set_value(202+268, 0);
-    gpio_set_value(182+268, 0);
-#endif
-	//you.chen@2022-07-15 add for shutdown wifi power when suspend end(for temp)
-
 	return 0;
 }
 
@@ -115,19 +114,11 @@
 static inline int mtk_suspend_common_resume(unsigned int susp_status)
 {
 	/* Implement suspend common flow here */
-	//you.chen@2022-07-15 add for shutdown wifi power when suspend begin(for temp)
-#if defined(MTK_GSW_WG870_GPIO_PWR_RST)
-    /* Control GPIO182 for turn on WIFI chip */
-    gpio_set_value(182+268, 1);
-    mdelay(1);
-    gpio_set_value(202+268, 1);
-
-    /* Waiting for WIFI chip done*/
-    mdelay(10);
-    printk_deferred("mt_spm_suspend_resume\n");
+//you.chen@2023-01-05 add for lpm suspend actions begin
+#ifdef CONFIG_LYNQ_LPM_SUSPEND_SUPPORT
+	return lynq_suspend_common_resume(susp_status);
 #endif
-	//you.chen@2022-07-15 add for shutdown wifi power when suspend end(for temp)
-
+//you.chen@2023-01-05 add for lpm suspend actions end
 	return 0;
 }