2011-05-13 15:17:51 +05:30
/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/slab.h>
# include <linux/input.h>
# include <linux/interrupt.h>
# include <linux/platform_device.h>
2013-12-15 03:24:45 -08:00
# include <linux/regmap.h>
2011-05-13 15:17:51 +05:30
# include <linux/log2.h>
2014-03-29 12:39:38 -07:00
# include <linux/of.h>
2011-05-13 15:17:51 +05:30
# define PON_CNTL_1 0x1C
# define PON_CNTL_PULL_UP BIT(7)
# define PON_CNTL_TRIG_DELAY_MASK (0x7)
/**
* struct pmic8xxx_pwrkey - pmic8xxx pwrkey information
* @ key_press_irq : key press irq number
*/
struct pmic8xxx_pwrkey {
int key_press_irq ;
} ;
2013-12-15 03:14:47 -08:00
static irqreturn_t pwrkey_press_irq ( int irq , void * _pwr )
2011-05-13 15:17:51 +05:30
{
2013-12-15 03:14:47 -08:00
struct input_dev * pwr = _pwr ;
2011-05-13 15:17:51 +05:30
2013-12-15 03:14:47 -08:00
input_report_key ( pwr , KEY_POWER , 1 ) ;
input_sync ( pwr ) ;
2011-05-13 15:17:51 +05:30
return IRQ_HANDLED ;
}
2013-12-15 03:14:47 -08:00
static irqreturn_t pwrkey_release_irq ( int irq , void * _pwr )
2011-05-13 15:17:51 +05:30
{
2013-12-15 03:14:47 -08:00
struct input_dev * pwr = _pwr ;
2011-05-13 15:17:51 +05:30
2013-12-15 03:14:47 -08:00
input_report_key ( pwr , KEY_POWER , 0 ) ;
input_sync ( pwr ) ;
2011-05-13 15:17:51 +05:30
return IRQ_HANDLED ;
}
# ifdef CONFIG_PM_SLEEP
static int pmic8xxx_pwrkey_suspend ( struct device * dev )
{
struct pmic8xxx_pwrkey * pwrkey = dev_get_drvdata ( dev ) ;
if ( device_may_wakeup ( dev ) )
enable_irq_wake ( pwrkey - > key_press_irq ) ;
return 0 ;
}
static int pmic8xxx_pwrkey_resume ( struct device * dev )
{
struct pmic8xxx_pwrkey * pwrkey = dev_get_drvdata ( dev ) ;
if ( device_may_wakeup ( dev ) )
disable_irq_wake ( pwrkey - > key_press_irq ) ;
return 0 ;
}
# endif
static SIMPLE_DEV_PM_OPS ( pm8xxx_pwr_key_pm_ops ,
pmic8xxx_pwrkey_suspend , pmic8xxx_pwrkey_resume ) ;
2012-11-23 21:38:25 -08:00
static int pmic8xxx_pwrkey_probe ( struct platform_device * pdev )
2011-05-13 15:17:51 +05:30
{
struct input_dev * pwr ;
int key_release_irq = platform_get_irq ( pdev , 0 ) ;
int key_press_irq = platform_get_irq ( pdev , 1 ) ;
int err ;
unsigned int delay ;
2013-12-15 03:24:45 -08:00
unsigned int pon_cntl ;
struct regmap * regmap ;
2011-05-13 15:17:51 +05:30
struct pmic8xxx_pwrkey * pwrkey ;
2014-03-29 12:39:38 -07:00
u32 kpd_delay ;
bool pull_up ;
2011-05-13 15:17:51 +05:30
2014-03-29 12:39:38 -07:00
if ( of_property_read_u32 ( pdev - > dev . of_node , " debounce " , & kpd_delay ) )
2014-05-14 11:58:55 -07:00
kpd_delay = 15625 ;
2011-05-13 15:17:51 +05:30
2014-05-14 11:58:55 -07:00
if ( kpd_delay > 62500 | | kpd_delay = = 0 ) {
2011-05-13 15:17:51 +05:30
dev_err ( & pdev - > dev , " invalid power key trigger delay \n " ) ;
return - EINVAL ;
}
2014-05-14 11:58:55 -07:00
pull_up = of_property_read_bool ( pdev - > dev . of_node , " pull-up " ) ;
2013-12-15 03:24:45 -08:00
regmap = dev_get_regmap ( pdev - > dev . parent , NULL ) ;
if ( ! regmap ) {
dev_err ( & pdev - > dev , " failed to locate regmap for the device \n " ) ;
return - ENODEV ;
}
2013-12-15 03:28:26 -08:00
pwrkey = devm_kzalloc ( & pdev - > dev , sizeof ( * pwrkey ) , GFP_KERNEL ) ;
2011-05-13 15:17:51 +05:30
if ( ! pwrkey )
return - ENOMEM ;
2013-12-15 03:28:26 -08:00
pwrkey - > key_press_irq = key_press_irq ;
pwr = devm_input_allocate_device ( & pdev - > dev ) ;
2011-05-13 15:17:51 +05:30
if ( ! pwr ) {
dev_dbg ( & pdev - > dev , " Can't allocate power button \n " ) ;
2013-12-15 03:28:26 -08:00
return - ENOMEM ;
2011-05-13 15:17:51 +05:30
}
input_set_capability ( pwr , EV_KEY , KEY_POWER ) ;
pwr - > name = " pmic8xxx_pwrkey " ;
pwr - > phys = " pmic8xxx_pwrkey/input0 " ;
2014-03-29 12:39:38 -07:00
delay = ( kpd_delay < < 10 ) / USEC_PER_SEC ;
2011-05-13 15:17:51 +05:30
delay = 1 + ilog2 ( delay ) ;
2013-12-15 03:24:45 -08:00
err = regmap_read ( regmap , PON_CNTL_1 , & pon_cntl ) ;
2011-05-13 15:17:51 +05:30
if ( err < 0 ) {
dev_err ( & pdev - > dev , " failed reading PON_CNTL_1 err=%d \n " , err ) ;
2013-12-15 03:28:26 -08:00
return err ;
2011-05-13 15:17:51 +05:30
}
pon_cntl & = ~ PON_CNTL_TRIG_DELAY_MASK ;
pon_cntl | = ( delay & PON_CNTL_TRIG_DELAY_MASK ) ;
2014-03-29 12:39:38 -07:00
if ( pull_up )
2011-05-13 15:17:51 +05:30
pon_cntl | = PON_CNTL_PULL_UP ;
else
pon_cntl & = ~ PON_CNTL_PULL_UP ;
2013-12-15 03:24:45 -08:00
err = regmap_write ( regmap , PON_CNTL_1 , pon_cntl ) ;
2011-05-13 15:17:51 +05:30
if ( err < 0 ) {
dev_err ( & pdev - > dev , " failed writing PON_CNTL_1 err=%d \n " , err ) ;
2013-12-15 03:28:26 -08:00
return err ;
2011-05-13 15:17:51 +05:30
}
2013-12-15 03:28:26 -08:00
err = devm_request_irq ( & pdev - > dev , key_press_irq , pwrkey_press_irq ,
IRQF_TRIGGER_RISING ,
" pmic8xxx_pwrkey_press " , pwr ) ;
2011-05-13 15:17:51 +05:30
if ( err ) {
2013-12-15 03:28:26 -08:00
dev_err ( & pdev - > dev , " Can't get %d IRQ for pwrkey: %d \n " ,
key_press_irq , err ) ;
return err ;
2011-05-13 15:17:51 +05:30
}
2013-12-15 03:28:26 -08:00
err = devm_request_irq ( & pdev - > dev , key_release_irq , pwrkey_release_irq ,
IRQF_TRIGGER_RISING ,
" pmic8xxx_pwrkey_release " , pwr ) ;
if ( err ) {
dev_err ( & pdev - > dev , " Can't get %d IRQ for pwrkey: %d \n " ,
key_release_irq , err ) ;
return err ;
2011-05-13 15:17:51 +05:30
}
2013-12-15 03:28:26 -08:00
err = input_register_device ( pwr ) ;
if ( err ) {
dev_err ( & pdev - > dev , " Can't register power key: %d \n " , err ) ;
return err ;
2011-05-13 15:17:51 +05:30
}
2013-12-15 03:28:26 -08:00
platform_set_drvdata ( pdev , pwrkey ) ;
2014-03-29 12:39:38 -07:00
device_init_wakeup ( & pdev - > dev , 1 ) ;
2011-05-13 15:17:51 +05:30
return 0 ;
}
2012-11-23 21:50:47 -08:00
static int pmic8xxx_pwrkey_remove ( struct platform_device * pdev )
2011-05-13 15:17:51 +05:30
{
device_init_wakeup ( & pdev - > dev , 0 ) ;
return 0 ;
}
2014-03-29 12:39:38 -07:00
static const struct of_device_id pm8xxx_pwr_key_id_table [ ] = {
{ . compatible = " qcom,pm8058-pwrkey " } ,
{ . compatible = " qcom,pm8921-pwrkey " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , pm8xxx_pwr_key_id_table ) ;
2011-05-13 15:17:51 +05:30
static struct platform_driver pmic8xxx_pwrkey_driver = {
. probe = pmic8xxx_pwrkey_probe ,
2012-11-23 21:27:39 -08:00
. remove = pmic8xxx_pwrkey_remove ,
2011-05-13 15:17:51 +05:30
. driver = {
2014-03-29 12:39:38 -07:00
. name = " pm8xxx-pwrkey " ,
2011-05-13 15:17:51 +05:30
. owner = THIS_MODULE ,
. pm = & pm8xxx_pwr_key_pm_ops ,
2014-03-29 12:39:38 -07:00
. of_match_table = pm8xxx_pwr_key_id_table ,
2011-05-13 15:17:51 +05:30
} ,
} ;
2011-11-29 11:08:40 -08:00
module_platform_driver ( pmic8xxx_pwrkey_driver ) ;
2011-05-13 15:17:51 +05:30
MODULE_ALIAS ( " platform:pmic8xxx_pwrkey " ) ;
MODULE_DESCRIPTION ( " PMIC8XXX Power Key driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_AUTHOR ( " Trilok Soni <tsoni@codeaurora.org> " ) ;