| /******************************************************************************* | |
| * Copyright (C) 2009, mbtk Corporation. | |
| * | |
| * File Name: mbtk_GpioWakeUp.c | |
| * File Mark: | |
| * Description: | |
| * Others: | |
| * Version: V1.0 | |
| * Author: xxxx | |
| * Date: 2023-07-04 | |
| * History 1: | |
| * Date: | |
| * Version: | |
| * Author: | |
| * Modification: | |
| * History 2: | |
| ********************************************************************************/ | |
| /**************************************************************************** | |
| * Include files | |
| ****************************************************************************/ | |
| #include <linux/module.h> | |
| #include <linux/kernel.h> | |
| #include <linux/kthread.h> | |
| #include <linux/init.h> | |
| #include <linux/spinlock.h> | |
| #include <linux/interrupt.h> | |
| #include <linux/types.h> | |
| #include <linux/suspend.h> | |
| #include <linux/tick.h> | |
| #include <linux/slab.h> | |
| #include <linux/err.h> | |
| #include <linux/of_irq.h> | |
| #include <linux/of_address.h> | |
| #include <linux/of_platform.h> | |
| #include <linux/of_gpio.h> | |
| #include <linux/edge_wakeup_mmp.h> | |
| //#include <mach/irqs.h> | |
| //#include <uapi/linux/gpio.h> | |
| //#include <linux/soc/sc/pcu.h> | |
| #include <linux/irq.h> | |
| #include <linux/delay.h> | |
| #include <linux/gpio.h> | |
| #include <linux/pm_wakeup.h> | |
| //#include <linux/soc/sc/drv_idle.h> | |
| #include <linux/sysfs.h> | |
| #include <linux/kobject.h> | |
| #include <linux/input.h> | |
| /**************************************************************************** | |
| * Local Macros | |
| ****************************************************************************/ | |
| #define HAS_WAKE_OUT_PIN 1 // 1: for output pin level when wakeup or sleeping; 0 is disable | |
| #define HAS_WAKE_SOURCE_LOCK 1 //1: for wakelock when wakeup and release when sleeping with the wake gpio level | |
| #define HAL_GPIO_WAKUP_PM 1 // release or lock wakelock when sleep or wakeup | |
| #define WAKEUP_STATE_VALUE 0 | |
| #define WAKEUP_KEY KEY_1 //KEY_POWER ,report key code to application | |
| #define CP_WAKE_STATUS 1 | |
| #define CP_SLEEP_STATUS 0 | |
| #ifndef TRUE | |
| #define TRUE 1 | |
| #endif | |
| #ifndef FALSE | |
| #define FALSE 0 | |
| #endif | |
| #define GPIO_WAKUP_DEBUG | |
| #ifdef GPIO_WAKUP_DEBUG | |
| #define GPIO_WAKUP_debug(fmt, args...) printk(KERN_INFO "[SLP] " fmt, ##args) | |
| #else | |
| #define GPIO_WAKUP_debug(fmt, args...) | |
| #endif | |
| #define GPIO_WAKUP_STATE "GpioWakeUp_state" | |
| /**************************************************************************** | |
| * Local Types | |
| ****************************************************************************/ | |
| struct mbtkGpioWakeUp_dev | |
| { | |
| struct device *dev; | |
| struct pinctrl *pctrl_wk_int; | |
| struct pinctrl_state *st_gpio; | |
| struct pinctrl_state *st_int; | |
| int wake_state; | |
| int curr_gpio_value; | |
| struct task_struct *wake_int_thread; | |
| spinlock_t wk_lock; | |
| struct semaphore wk_sem; | |
| int wake_ap_gpio; | |
| int wake_me_gpio; | |
| struct gpio_desc *gd; | |
| struct input_dev *input; | |
| int eint_irq; | |
| int GpioWakeUp_irq_state; | |
| int wake_cnt; | |
| int sleep_cnt; | |
| struct wakeup_source *GpioWakeUp_ws; | |
| }; | |
| /**************************************************************************** | |
| * Global Variables | |
| ****************************************************************************/ | |
| //static int wakeup_irq_occurs = 0; | |
| struct wakeup_source *GpioWakeUp_wake_lock; | |
| static int GpioWakeUp_init_flag = 0; | |
| static int currState = 1; | |
| /**************************************************************************** | |
| * Local Constants | |
| ****************************************************************************/ | |
| struct mbtkGpioWakeUp_dev g_GpioWakeUp = {0}; | |
| /******************************************************************************* | |
| * Function: ApWakeCp int | |
| * Description: ap2cp wake up int thread | |
| * Parameters: | |
| * Input: | |
| * | |
| * Output: | |
| * | |
| * Returns: | |
| * | |
| * Others: | |
| ********************************************************************************/ | |
| irqreturn_t GpioWakeUp_wkcp_thread_fn(int irq, void *priv) | |
| { | |
| unsigned long flags; | |
| int gpio_value; | |
| struct mbtkGpioWakeUp_dev *GpioWakeUp_dev = (struct mbtkGpioWakeUp_dev *)priv; | |
| disable_irq_nosync(irq);/*jb.qi add for dtr on 20240202*/ | |
| gpio_value = 0 ; | |
| //pr_err("%s/L%d\n", __func__, __LINE__); | |
| if(GpioWakeUp_dev->GpioWakeUp_ws){ | |
| //gpio_value = gpio_get_value_cansleep(GpioWakeUp_dev->wake_me_gpio) ; | |
| //if(gpio_value == 0) | |
| __pm_wakeup_event(GpioWakeUp_dev->GpioWakeUp_ws, 2000); | |
| } | |
| /*if (pinctrl_select_state(GpioWakeUp_dev->pctrl_wk_int, GpioWakeUp_dev->st_gpio) < 0) { | |
| printk("setting card detect gpio failed\n"); | |
| }*/ | |
| spin_lock_irqsave(&GpioWakeUp_dev->wk_lock, flags); | |
| gpio_value = gpio_get_value_cansleep(GpioWakeUp_dev->wake_me_gpio) ; | |
| spin_unlock_irqrestore(&GpioWakeUp_dev->wk_lock, flags); | |
| if(gpio_value < 0) | |
| { | |
| printk( "[%s-%d]: gpio get state failed\n",__func__,__LINE__); | |
| } | |
| if(gpio_value != currState) | |
| { | |
| GPIO_WAKUP_debug("interrup handler: gpio%d_value is %d \n",GpioWakeUp_dev->wake_me_gpio,gpio_value); | |
| GpioWakeUp_dev->wake_state = gpio_value; | |
| //input_report_key(GpioWakeUp_dev->input, WAKEUP_KEY, !!gpio_value); | |
| input_event(GpioWakeUp_dev->input, EV_MSC, MSC_RAW, !!gpio_value); | |
| input_sync(GpioWakeUp_dev->input); | |
| #if HAS_WAKE_SOURCE_LOCK | |
| if(WAKEUP_STATE_VALUE == gpio_value){ //wake up | |
| pm_stay_awake(g_GpioWakeUp.dev); | |
| } | |
| else{ //into sleep | |
| pm_relax(g_GpioWakeUp.dev); | |
| } | |
| #endif | |
| } | |
| currState = gpio_value; | |
| enable_irq(GpioWakeUp_dev->eint_irq); | |
| return IRQ_HANDLED; | |
| } | |
| #if HAL_GPIO_WAKUP_PM | |
| static int mbtkGpioWakeUp_pm_resume(struct device *dev) | |
| { | |
| //pm_stay_awake(g_GpioWakeUp.dev); | |
| //gpio_direction_output(g_GpioWakeUp.wake_ap_gpio, CP_WAKE_STATUS); | |
| pr_info("mbtkGpioWakeUp_pm_resume\n"); | |
| return 0; | |
| } | |
| static int mbtkGpioWakeUp_pm_suspend(struct device *dev) | |
| { | |
| //pm_relax(g_GpioWakeUp.dev); | |
| gpio_direction_output(g_GpioWakeUp.wake_ap_gpio, CP_SLEEP_STATUS); | |
| pr_info("mbtkGpioWakeUp_pm_suspend\n"); | |
| return 0; | |
| } | |
| static int mbtkGpioWakeUp_pm_runtime_resume(struct device *dev) | |
| { | |
| /* enable clk and restore regs */ | |
| pr_info("mbtkGpioWakeUp_pm_runtime_resume\n"); | |
| return 0; | |
| } | |
| static int mbtkGpioWakeUp_pm_runtime_suspend(struct device *dev) | |
| { | |
| /* backup regs and disable clk */ | |
| pr_info("mbtkGpioWakeUp_pm_runtime_suspend\n"); | |
| return 0; | |
| } | |
| static int mbtkGpioWakeUp_pm_runtime_idle(struct device *dev) | |
| { | |
| pr_info("mbtkGpioWakeUp_pm_runtime_idle\n"); | |
| return 0; | |
| } | |
| static const struct dev_pm_ops mbtkGpioWakeUp_pm = { | |
| .resume = mbtkGpioWakeUp_pm_resume, | |
| .suspend = mbtkGpioWakeUp_pm_suspend, | |
| .runtime_resume = mbtkGpioWakeUp_pm_runtime_resume, | |
| .runtime_suspend = mbtkGpioWakeUp_pm_runtime_suspend, | |
| .runtime_idle = mbtkGpioWakeUp_pm_runtime_idle | |
| }; | |
| #endif | |
| static const struct of_device_id mbtkGpioWakeUp_match[] = { | |
| { .compatible = "mbtk,GpioWakeUp", }, | |
| { } | |
| }; | |
| static ssize_t mbtkGpioWakeUp_value_show(struct device *dev, | |
| struct device_attribute *attr, char *buf) | |
| { | |
| int gpio_value; | |
| unsigned long flags; | |
| struct mbtkGpioWakeUp_dev *GpioWakeUp_dev = &g_GpioWakeUp; | |
| spin_lock_irqsave(&GpioWakeUp_dev->wk_lock, flags); | |
| gpio_value = gpio_get_value_cansleep(GpioWakeUp_dev->wake_me_gpio) ; | |
| spin_unlock_irqrestore(&GpioWakeUp_dev->wk_lock, flags); | |
| currState = gpio_value; | |
| input_event(GpioWakeUp_dev->input, EV_MSC, MSC_RAW, gpio_value); | |
| input_sync(GpioWakeUp_dev->input); | |
| sprintf(buf, "%d\n", gpio_value); | |
| printk("gpio%d = %d \n",GpioWakeUp_dev->wake_me_gpio,gpio_value); | |
| return strlen(buf) + 1; | |
| } | |
| static ssize_t mbtkGpioWakeUp_value_store(struct device *dev, | |
| struct device_attribute *attr, const char *buf, | |
| size_t size) | |
| { | |
| int gpio_value=0; | |
| struct mbtkGpioWakeUp_dev *GpioWakeUp_dev = &g_GpioWakeUp; | |
| sscanf(buf, "%d", &gpio_value); | |
| if (gpio_value != 0 && gpio_value != 1) { | |
| pr_err("%s: invalid value %d, should be 0 or 1\n", | |
| __func__, gpio_value); | |
| return -EINVAL; | |
| } | |
| input_event(GpioWakeUp_dev->input, EV_MSC, MSC_RAW, gpio_value); | |
| input_sync(GpioWakeUp_dev->input); | |
| return size; | |
| } | |
| static DEVICE_ATTR(WakeUpValue, 0664, mbtkGpioWakeUp_value_show, mbtkGpioWakeUp_value_store); | |
| int mbtkGpioWakeUp_probe(struct platform_device *pdev) | |
| { | |
| int gpio; | |
| int hsm_en_gpio; | |
| int gpio_value; | |
| unsigned long flags; | |
| int ret = 0, error; | |
| struct device_node *np = NULL; | |
| struct input_dev *input; | |
| g_GpioWakeUp.dev = &pdev->dev; | |
| np = pdev->dev.of_node; | |
| sema_init(&g_GpioWakeUp.wk_sem,0); | |
| spin_lock_init(&g_GpioWakeUp.wk_lock); | |
| device_init_wakeup(&pdev->dev, true); | |
| g_GpioWakeUp.GpioWakeUp_ws = wakeup_source_register(g_GpioWakeUp.dev ,"GpioWakeUp"); | |
| if(g_GpioWakeUp.GpioWakeUp_ws == NULL){ | |
| printk("adb_setup wakeup_source_create fail\n"); | |
| goto wake_source_init_fail; | |
| } | |
| hsm_en_gpio = of_get_named_gpio(np, "hsm-en-out-gpio", 0); | |
| if (unlikely(hsm_en_gpio < 0)) { | |
| pr_err("%s. parse hsm-en-out-gpio failed\n", __func__); | |
| ret = -EINVAL; | |
| } else { | |
| pr_err("%s. hsm-en-out-gpio=%d\n", __func__, hsm_en_gpio); | |
| if (gpio_request(hsm_en_gpio, "hsm-en-out-gpio")) { | |
| pr_err("Failed to request GPIO %d\n", hsm_en_gpio); | |
| ret = -EBUSY; | |
| } | |
| if (gpio_direction_output(hsm_en_gpio, 1)) { | |
| pr_err("Failed to set GPIO %d as output\n", hsm_en_gpio); | |
| ret = -EIO; | |
| } | |
| pr_err("hsm-gpio config sucess\n"); | |
| gpio_free(hsm_en_gpio); | |
| } | |
| gpio = of_get_named_gpio(np, "wakeup-in-gpio", 0); | |
| if (unlikely(gpio < 0)) { | |
| pr_err("%s. parse wakeup-in-gpio failed\n", __func__); | |
| ret = -EINVAL; | |
| } else { | |
| pr_err("%s. wakeup-in-gpio=%d\n", __func__, gpio); | |
| } | |
| if(gpio >= 0) | |
| { | |
| request_mfp_edge_wakeup(gpio, NULL, NULL, g_GpioWakeUp.dev); | |
| /*LYNQ_MODFIY_START | |
| ret = gpio_request(gpio, "wakeup-in"); | |
| if(ret < 0) | |
| { | |
| pr_err("%s. gpio_request for wakeup-in-gpio=%d failed\n", __func__,gpio); | |
| return ret; | |
| } | |
| LYNQ_MODFIY_END*/ | |
| g_GpioWakeUp.wake_me_gpio = gpio; | |
| gpio_direction_input(g_GpioWakeUp.wake_me_gpio); | |
| g_GpioWakeUp.eint_irq = gpio_to_irq(g_GpioWakeUp.wake_me_gpio); | |
| if (g_GpioWakeUp.eint_irq < 0) | |
| { | |
| error = g_GpioWakeUp.eint_irq ; | |
| printk("Unable to get irq number for GPIO %d, error %d\n",g_GpioWakeUp.wake_me_gpio, error); | |
| goto wake_in_pin_init_fail; | |
| } | |
| else | |
| { | |
| printk("wake in gpio request irq %d sucess\n", g_GpioWakeUp.eint_irq); | |
| } | |
| } | |
| input = input_allocate_device();; | |
| if (!input) { | |
| printk("failed to allocate state\n"); | |
| error = -ENOMEM; | |
| goto input_fail; | |
| } | |
| g_GpioWakeUp.input = input; | |
| input->name = "WAKE-in-GPIO"; | |
| /* 2. ?? */ | |
| /* 2.1 ??????? */ | |
| set_bit(EV_MSC, g_GpioWakeUp.input->evbit); | |
| set_bit(EV_SYN, g_GpioWakeUp.input->evbit); | |
| /* 2.2 ?????????????: L,S,ENTER,LEFTSHIT */ | |
| set_bit(MSC_RAW, g_GpioWakeUp.input->mscbit); | |
| //set_bit(KEY_LEFTSHIFT, buttons_dev->keybit); | |
| /* 3. ?? */ | |
| input_set_capability(g_GpioWakeUp.input, EV_MSC, MSC_RAW); | |
| error = input_register_device(input); | |
| if (error) | |
| { | |
| pr_err("%s. input_register_device=%d failed\n", __func__,error); | |
| goto input_fail; | |
| } | |
| spin_lock_irqsave(&g_GpioWakeUp.wk_lock, flags); | |
| gpio_value = gpio_get_value(g_GpioWakeUp.wake_me_gpio) ; | |
| spin_unlock_irqrestore(&g_GpioWakeUp.wk_lock, flags); | |
| if(gpio_value < 0) | |
| { | |
| printk( "[%s-%d]: gpio get state failed\n",__func__,__LINE__); | |
| } | |
| //input_report_key(g_GpioWakeUp.input, KEY_1, !!gpio_value); | |
| input_event(input, EV_MSC, MSC_RAW, !!gpio_value); | |
| input_sync(input); | |
| #if HAS_WAKE_SOURCE_LOCK | |
| if(WAKEUP_STATE_VALUE == gpio_value) | |
| { | |
| pm_stay_awake(g_GpioWakeUp.dev); | |
| } | |
| else | |
| { | |
| pm_relax(g_GpioWakeUp.dev); | |
| } | |
| #endif | |
| currState = gpio_value; | |
| GPIO_WAKUP_debug("%s. devm_request_threaded_irqn", __func__); | |
| ret = devm_request_threaded_irq(g_GpioWakeUp.dev, g_GpioWakeUp.eint_irq,NULL,GpioWakeUp_wkcp_thread_fn, (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT),"GpioWakeUp_wake int", &g_GpioWakeUp); | |
| if(ret<0){ | |
| panic("mbtkGpioWakeUp_probe request_irq fail, %d", ret); | |
| } | |
| enable_irq_wake(g_GpioWakeUp.eint_irq); | |
| ret = device_create_file(g_GpioWakeUp.dev, &dev_attr_WakeUpValue); | |
| if(ret) | |
| { | |
| printk("ERROR: mbtkGpioWakeUp device_create_file failed\n"); | |
| } | |
| #if HAS_WAKE_OUT_PIN | |
| gpio = of_get_named_gpio(np, "wakeup-out-gpio", 0); | |
| if (unlikely(gpio < 0)) { | |
| pr_err("%s. parse wakeup-out-gpio failed\n", __func__); | |
| } else | |
| { | |
| pr_err("%s. wakeup-out-gpio=%d\n", __func__, gpio); | |
| } | |
| if(gpio >= 0) | |
| { | |
| /*LYNQ_MODFIY_START | |
| ret = gpio_request(gpio, "wakeup-out"); | |
| if(ret < 0) | |
| { | |
| pr_err("%s. gpio_request for wakeup-out-gpio=%d failed\n", __func__,gpio); | |
| goto wake_out_pin_init_fail; | |
| } | |
| LYNQ_MODFIY_END*/ | |
| g_GpioWakeUp.wake_ap_gpio = gpio; | |
| GPIO_WAKUP_debug("%s. gpio_request for wakeup-out-gpio=%d sucess\n", __func__,g_GpioWakeUp.wake_ap_gpio); | |
| } | |
| gpio_direction_output(g_GpioWakeUp.wake_ap_gpio, CP_WAKE_STATUS); | |
| #endif | |
| g_GpioWakeUp.wake_cnt = 0; | |
| g_GpioWakeUp.sleep_cnt = 0; | |
| #if HAS_WAKE_OUT_PIN | |
| wake_out_pin_init_fail: | |
| if(ret){ | |
| pr_err("wakeup-out gpio%d init failed\n",gpio); | |
| } | |
| #endif | |
| input_fail: | |
| input_free_device(g_GpioWakeUp.input); | |
| wake_in_pin_init_fail: | |
| if(ret){ | |
| pr_err("wakeup-in gpio%d init failed\n",gpio); | |
| } | |
| //pinctrl_init_fail: | |
| // if(ret){ | |
| // printk("pinctrl_init_fail\n"); | |
| // } | |
| wake_source_init_fail: | |
| //pm_relax(g_GpioWakeUp.dev); | |
| return ret; | |
| } | |
| static struct platform_driver mbtkGpioWakeUp_driver = { | |
| .probe = mbtkGpioWakeUp_probe, | |
| .driver = { | |
| .name = "mbtk_GpioWakeUp", | |
| #if HAL_GPIO_WAKUP_PM | |
| .pm = &mbtkGpioWakeUp_pm, | |
| #endif | |
| .of_match_table = mbtkGpioWakeUp_match, | |
| }, | |
| }; | |
| /******************************************************************************* | |
| * Function: zDrvAp2cp_Initiate | |
| * Description: | |
| * Parameters: | |
| * Input: | |
| * | |
| * Output: | |
| * | |
| * Returns: | |
| * | |
| * Others: | |
| ********************************************************************************/ | |
| static int __init zDrvGpioWakeUp_Initiate(void) | |
| { | |
| int ret=0; | |
| //int errCode = -1; | |
| GpioWakeUp_init_flag = 1; | |
| ret = platform_driver_register(&mbtkGpioWakeUp_driver); | |
| if (ret != 0){ | |
| printk("GPIO_WAKUP: platform_driver_register fail!\n"); | |
| return ret; | |
| } | |
| return 0; | |
| } | |
| int zDrvGpioWakeUp_Release(void) | |
| { | |
| gpio_free(g_GpioWakeUp.wake_me_gpio); | |
| #if HAS_WAKE_OUT_PIN | |
| gpio_free(g_GpioWakeUp.wake_ap_gpio); | |
| #endif | |
| return 0; | |
| } | |
| late_initcall(zDrvGpioWakeUp_Initiate); | |
| //module_exit(zDrvGpioWakeUp_Release); | |