2019-01-14 10:05:17 +00:00
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) STMicroelectronics 2018
// Author: Pascal Paillet <p.paillet@st.com> for STMicroelectronics.
# include <linux/input.h>
# include <linux/interrupt.h>
# include <linux/mfd/stpmic1.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/platform_device.h>
# include <linux/property.h>
# include <linux/regmap.h>
/**
* struct stpmic1_onkey - OnKey data
* @ input_dev : pointer to input device
* @ irq_falling : irq that we are hooked on to
* @ irq_rising : irq that we are hooked on to
*/
struct stpmic1_onkey {
struct input_dev * input_dev ;
int irq_falling ;
int irq_rising ;
} ;
static irqreturn_t onkey_falling_irq ( int irq , void * ponkey )
{
struct stpmic1_onkey * onkey = ponkey ;
struct input_dev * input_dev = onkey - > input_dev ;
input_report_key ( input_dev , KEY_POWER , 1 ) ;
pm_wakeup_event ( input_dev - > dev . parent , 0 ) ;
input_sync ( input_dev ) ;
return IRQ_HANDLED ;
}
static irqreturn_t onkey_rising_irq ( int irq , void * ponkey )
{
struct stpmic1_onkey * onkey = ponkey ;
struct input_dev * input_dev = onkey - > input_dev ;
input_report_key ( input_dev , KEY_POWER , 0 ) ;
pm_wakeup_event ( input_dev - > dev . parent , 0 ) ;
input_sync ( input_dev ) ;
return IRQ_HANDLED ;
}
static int stpmic1_onkey_probe ( struct platform_device * pdev )
{
struct stpmic1 * pmic = dev_get_drvdata ( pdev - > dev . parent ) ;
struct device * dev = & pdev - > dev ;
struct input_dev * input_dev ;
struct stpmic1_onkey * onkey ;
unsigned int val , reg = 0 ;
int error ;
onkey = devm_kzalloc ( dev , sizeof ( * onkey ) , GFP_KERNEL ) ;
if ( ! onkey )
return - ENOMEM ;
onkey - > irq_falling = platform_get_irq_byname ( pdev , " onkey-falling " ) ;
2019-08-14 10:46:38 -07:00
if ( onkey - > irq_falling < 0 )
2019-01-14 10:05:17 +00:00
return onkey - > irq_falling ;
onkey - > irq_rising = platform_get_irq_byname ( pdev , " onkey-rising " ) ;
2019-08-14 10:46:38 -07:00
if ( onkey - > irq_rising < 0 )
2019-01-14 10:05:17 +00:00
return onkey - > irq_rising ;
if ( ! device_property_read_u32 ( dev , " power-off-time-sec " , & val ) ) {
if ( val > 0 & & val < = 16 ) {
dev_dbg ( dev , " power-off-time=%d seconds \n " , val ) ;
reg | = PONKEY_PWR_OFF ;
reg | = ( ( 16 - val ) & PONKEY_TURNOFF_TIMER_MASK ) ;
} else {
dev_err ( dev , " power-off-time-sec out of range \n " ) ;
return - EINVAL ;
}
}
if ( device_property_present ( dev , " st,onkey-clear-cc-flag " ) )
reg | = PONKEY_CC_FLAG_CLEAR ;
error = regmap_update_bits ( pmic - > regmap , PKEY_TURNOFF_CR ,
PONKEY_TURNOFF_MASK , reg ) ;
if ( error ) {
dev_err ( dev , " PKEY_TURNOFF_CR write failed: %d \n " , error ) ;
return error ;
}
if ( device_property_present ( dev , " st,onkey-pu-inactive " ) ) {
error = regmap_update_bits ( pmic - > regmap , PADS_PULL_CR ,
PONKEY_PU_INACTIVE ,
PONKEY_PU_INACTIVE ) ;
if ( error ) {
dev_err ( dev , " ONKEY Pads configuration failed: %d \n " ,
error ) ;
return error ;
}
}
input_dev = devm_input_allocate_device ( dev ) ;
if ( ! input_dev ) {
dev_err ( dev , " Can't allocate Pwr Onkey Input Device \n " ) ;
return - ENOMEM ;
}
input_dev - > name = " pmic_onkey " ;
input_dev - > phys = " pmic_onkey/input0 " ;
input_set_capability ( input_dev , EV_KEY , KEY_POWER ) ;
onkey - > input_dev = input_dev ;
/* interrupt is nested in a thread */
error = devm_request_threaded_irq ( dev , onkey - > irq_falling , NULL ,
onkey_falling_irq , IRQF_ONESHOT ,
dev_name ( dev ) , onkey ) ;
if ( error ) {
dev_err ( dev , " Can't get IRQ Onkey Falling: %d \n " , error ) ;
return error ;
}
error = devm_request_threaded_irq ( dev , onkey - > irq_rising , NULL ,
onkey_rising_irq , IRQF_ONESHOT ,
dev_name ( dev ) , onkey ) ;
if ( error ) {
dev_err ( dev , " Can't get IRQ Onkey Rising: %d \n " , error ) ;
return error ;
}
error = input_register_device ( input_dev ) ;
if ( error ) {
dev_err ( dev , " Can't register power button: %d \n " , error ) ;
return error ;
}
platform_set_drvdata ( pdev , onkey ) ;
device_init_wakeup ( dev , true ) ;
return 0 ;
}
static int __maybe_unused stpmic1_onkey_suspend ( struct device * dev )
{
struct platform_device * pdev = to_platform_device ( dev ) ;
struct stpmic1_onkey * onkey = platform_get_drvdata ( pdev ) ;
if ( device_may_wakeup ( dev ) ) {
enable_irq_wake ( onkey - > irq_falling ) ;
enable_irq_wake ( onkey - > irq_rising ) ;
}
return 0 ;
}
static int __maybe_unused stpmic1_onkey_resume ( struct device * dev )
{
struct platform_device * pdev = to_platform_device ( dev ) ;
struct stpmic1_onkey * onkey = platform_get_drvdata ( pdev ) ;
if ( device_may_wakeup ( dev ) ) {
disable_irq_wake ( onkey - > irq_falling ) ;
disable_irq_wake ( onkey - > irq_rising ) ;
}
return 0 ;
}
static SIMPLE_DEV_PM_OPS ( stpmic1_onkey_pm ,
stpmic1_onkey_suspend ,
stpmic1_onkey_resume ) ;
static const struct of_device_id of_stpmic1_onkey_match [ ] = {
{ . compatible = " st,stpmic1-onkey " } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , of_stpmic1_onkey_match ) ;
static struct platform_driver stpmic1_onkey_driver = {
. probe = stpmic1_onkey_probe ,
. driver = {
. name = " stpmic1_onkey " ,
. of_match_table = of_match_ptr ( of_stpmic1_onkey_match ) ,
. pm = & stpmic1_onkey_pm ,
} ,
} ;
module_platform_driver ( stpmic1_onkey_driver ) ;
MODULE_DESCRIPTION ( " Onkey driver for STPMIC1 " ) ;
MODULE_AUTHOR ( " Pascal Paillet <p.paillet@st.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;