/******************************************************************************* | |
* 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 <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 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; | |
} | |
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) | |
{ | |
/*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); | |